
























@@ -174,6 +174,20 @@ describe("createNodesTool screen_record duration guardrails", () => {
174174expect(schema.properties?.screenIndex).toMatchObject({ type: "integer", minimum: 0 });
175175});
176176177+it("advertises node command timeout constraints in the tool schema", () => {
178+const tool = createNodesTool();
179+const schema = tool.parameters as {
180+properties?: {
181+maxAgeMs?: { minimum?: number; type?: string };
182+locationTimeoutMs?: { minimum?: number; type?: string };
183+invokeTimeoutMs?: { minimum?: number; type?: string };
184+};
185+};
186+expect(schema.properties?.maxAgeMs).toMatchObject({ type: "integer", minimum: 0 });
187+expect(schema.properties?.locationTimeoutMs).toMatchObject({ type: "integer", minimum: 1 });
188+expect(schema.properties?.invokeTimeoutMs).toMatchObject({ type: "integer", minimum: 1 });
189+});
190+177191it("clamps screen_record durationMs argument to 300000 before gateway invoke", async () => {
178192gatewayMocks.callGatewayTool.mockResolvedValue({ payload: { ok: true } });
179193const tool = createNodesTool();
@@ -404,6 +418,48 @@ describe("createNodesTool screen_record duration guardrails", () => {
404418});
405419});
406420421+it.each([
422+["location_get", { maxAgeMs: -1 }, "maxAgeMs must be a non-negative integer"],
423+["location_get", { locationTimeoutMs: 0 }, "locationTimeoutMs must be a positive integer"],
424+[
425+"invoke",
426+{ invokeCommand: "device.status", invokeTimeoutMs: "15s" },
427+"invokeTimeoutMs must be a positive integer",
428+],
429+])("rejects invalid %s command numeric params %s", async (action, params, message) => {
430+const tool = createNodesTool();
431+432+await expect(
433+tool.execute("call-invalid-command-number", {
434+ action,
435+node: "macbook",
436+ ...params,
437+}),
438+).rejects.toThrow(message);
439+expect(gatewayMocks.callGatewayTool).not.toHaveBeenCalled();
440+});
441+442+it("forwards validated location_get numeric params to gateway invoke", async () => {
443+gatewayMocks.callGatewayTool.mockResolvedValue({ payload: { lat: 1, lon: 2 } });
444+const tool = createNodesTool();
445+446+await tool.execute("call-location-numbers", {
447+action: "location_get",
448+node: "macbook",
449+maxAgeMs: "5000",
450+locationTimeoutMs: "10000",
451+});
452+453+const call = gatewayMocks.callGatewayTool.mock.calls[0] as
454+| [string, unknown, { params?: { maxAgeMs?: unknown; timeoutMs?: unknown } }]
455+| undefined;
456+expect(call?.[0]).toBe("node.invoke");
457+expect(call?.[2].params).toMatchObject({
458+maxAgeMs: 5000,
459+timeoutMs: 10000,
460+});
461+});
462+407463it("uses operator.pairing plus operator.admin to approve exec-capable node pair requests", async () => {
408464mockNodePairApproveFlow({
409465requiredApproveScopes: ["operator.pairing", "operator.admin"],
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。