feat: add gateway restart tool
This commit is contained in:
36
src/agents/clawdis-gateway-tool.test.ts
Normal file
36
src/agents/clawdis-gateway-tool.test.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createClawdisTools } from "./clawdis-tools.js";
|
||||
|
||||
describe("clawdis_gateway tool", () => {
|
||||
it("schedules SIGUSR1 restart", async () => {
|
||||
vi.useFakeTimers();
|
||||
const kill = vi.spyOn(process, "kill").mockImplementation(() => true);
|
||||
|
||||
try {
|
||||
const tool = createClawdisTools().find(
|
||||
(candidate) => candidate.name === "clawdis_gateway",
|
||||
);
|
||||
expect(tool).toBeDefined();
|
||||
if (!tool) throw new Error("missing clawdis_gateway tool");
|
||||
|
||||
const result = await tool.execute("call1", {
|
||||
action: "restart",
|
||||
delayMs: 0,
|
||||
});
|
||||
expect(result.details).toMatchObject({
|
||||
ok: true,
|
||||
pid: process.pid,
|
||||
signal: "SIGUSR1",
|
||||
delayMs: 0,
|
||||
});
|
||||
|
||||
expect(kill).not.toHaveBeenCalled();
|
||||
await vi.runAllTimersAsync();
|
||||
expect(kill).toHaveBeenCalledWith(process.pid, "SIGUSR1");
|
||||
} finally {
|
||||
kill.mockRestore();
|
||||
vi.useRealTimers();
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -1370,11 +1370,62 @@ function createCronTool(): AnyAgentTool {
|
||||
};
|
||||
}
|
||||
|
||||
const GatewayToolSchema = Type.Union([
|
||||
Type.Object({
|
||||
action: Type.Literal("restart"),
|
||||
delayMs: Type.Optional(Type.Number()),
|
||||
reason: Type.Optional(Type.String()),
|
||||
}),
|
||||
]);
|
||||
|
||||
function createGatewayTool(): AnyAgentTool {
|
||||
return {
|
||||
label: "Clawdis Gateway",
|
||||
name: "clawdis_gateway",
|
||||
description:
|
||||
"Restart the running gateway process in-place (SIGUSR1) without needing an external supervisor. Use delayMs to avoid interrupting an in-flight reply.",
|
||||
parameters: GatewayToolSchema,
|
||||
execute: async (_toolCallId, args) => {
|
||||
const params = args as Record<string, unknown>;
|
||||
const action = readStringParam(params, "action", { required: true });
|
||||
if (action !== "restart") throw new Error(`Unknown action: ${action}`);
|
||||
|
||||
const delayMsRaw =
|
||||
typeof params.delayMs === "number" && Number.isFinite(params.delayMs)
|
||||
? Math.floor(params.delayMs)
|
||||
: 2000;
|
||||
const delayMs = Math.min(Math.max(delayMsRaw, 0), 60_000);
|
||||
const reason =
|
||||
typeof params.reason === "string" && params.reason.trim()
|
||||
? params.reason.trim().slice(0, 200)
|
||||
: undefined;
|
||||
|
||||
const pid = process.pid;
|
||||
setTimeout(() => {
|
||||
try {
|
||||
process.kill(pid, "SIGUSR1");
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
}, delayMs);
|
||||
|
||||
return jsonResult({
|
||||
ok: true,
|
||||
pid,
|
||||
signal: "SIGUSR1",
|
||||
delayMs,
|
||||
reason: reason ?? null,
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createClawdisTools(): AnyAgentTool[] {
|
||||
return [
|
||||
createBrowserTool(),
|
||||
createCanvasTool(),
|
||||
createNodesTool(),
|
||||
createCronTool(),
|
||||
createGatewayTool(),
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user