mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-02 06:51:35 +00:00
* feat(gateway): stream node.invoke progress with idle timeouts and cancel * feat(node-host): opt-in approval-gated claude cli agent run command * feat(agents): run node-placed claude-cli turns through streaming node invoke * feat(anthropic): continue claude catalog sessions on advertising nodes * docs(nodes): document paired-node claude agent runs and continuation * chore(plugin-sdk): admit spawn-invocation helper into surface budget * fix(nodes): harden streaming invoke and node run policy from review findings * test(node-host): prove multi-token tool-policy argv fails closed * fix(nodes): allowlist cancel event for apps and break node-host type cycle * chore(protocol): regenerate Swift gateway models for node invoke progress * chore(node-host): drop test-only export surface flagged by deadcode ratchet * style(node-host): merge invoke-types imports * refactor(nodes): split node agent run modules to satisfy the LOC ratchet * chore(protocol): regenerate android gateway models and sync export baseline * chore(plugin-sdk): admit spawn-invocation helper and inherited deprecated drift into surface budget * chore(plugin-sdk): tolerate inherited agent-core deprecated export * fix(anthropic): drop node param parser orphaned by upstream catalog split * ci: sync unused-export baseline with healed main * test(agents): adopt renamed cli backend prepare helper * style(node-host): format claude run spawn invocation * ci: retrigger checks * chore(plugin-sdk): pin surface budgets to healed-base counts * fix(protocol): restore untyped lazy validators after upstream style change
82 lines
2.8 KiB
TypeScript
82 lines
2.8 KiB
TypeScript
/** In-process Gateway seam for streaming a Claude CLI turn from a paired node. */
|
|
import { randomUUID } from "node:crypto";
|
|
import type { SystemRunApprovalPlan } from "../infra/exec-approvals.js";
|
|
import { NODE_AGENT_CLI_CLAUDE_RUN_COMMAND } from "../infra/node-commands.js";
|
|
import { isNodeCommandAllowed, resolveNodeCommandAllowlist } from "./node-command-policy.js";
|
|
import type { NodeInvokeResult } from "./node-registry.js";
|
|
import { getFallbackGatewayContext } from "./server-plugin-fallback-context.js";
|
|
|
|
export async function invokeNodeClaudeCliRun(params: {
|
|
nodeId: string;
|
|
argv: string[];
|
|
stdin: string;
|
|
cwd?: string;
|
|
systemPrompt?: string;
|
|
agentId?: string;
|
|
sessionKey?: string;
|
|
approvalDecision?: "allow-once" | "allow-always";
|
|
systemRunPlan?: SystemRunApprovalPlan;
|
|
timeoutMs: number;
|
|
idleTimeoutMs: number;
|
|
onProgress: (chunk: string) => void;
|
|
signal?: AbortSignal;
|
|
}): Promise<NodeInvokeResult> {
|
|
const context = getFallbackGatewayContext();
|
|
if (!context) {
|
|
return {
|
|
ok: false,
|
|
error: { code: "UNAVAILABLE", message: "Gateway node runtime unavailable" },
|
|
};
|
|
}
|
|
const node = context.nodeRegistry.get(params.nodeId);
|
|
if (!node || !node.commands.includes(NODE_AGENT_CLI_CLAUDE_RUN_COMMAND)) {
|
|
return {
|
|
ok: false,
|
|
error: {
|
|
code: "UNAVAILABLE",
|
|
message: "paired node does not advertise Claude CLI agent runs",
|
|
},
|
|
};
|
|
}
|
|
const allowlist = resolveNodeCommandAllowlist(context.getRuntimeConfig(), {
|
|
...node,
|
|
approvedCommands: node.commands,
|
|
});
|
|
const allowed = isNodeCommandAllowed({
|
|
command: NODE_AGENT_CLI_CLAUDE_RUN_COMMAND,
|
|
declaredCommands: node.commands,
|
|
allowlist,
|
|
});
|
|
if (!allowed.ok) {
|
|
return {
|
|
ok: false,
|
|
error: {
|
|
code: "PERMISSION_DENIED",
|
|
message: `paired-node Claude CLI agent runs are blocked by node command policy (${allowed.reason})`,
|
|
},
|
|
};
|
|
}
|
|
return await context.nodeRegistry.invoke({
|
|
nodeId: params.nodeId,
|
|
expectedConnId: node.connId,
|
|
command: NODE_AGENT_CLI_CLAUDE_RUN_COMMAND,
|
|
params: {
|
|
argv: params.argv,
|
|
stdin: params.stdin,
|
|
...(params.cwd ? { cwd: params.cwd } : {}),
|
|
...(params.systemPrompt !== undefined ? { systemPrompt: params.systemPrompt } : {}),
|
|
...(params.agentId ? { agentId: params.agentId } : {}),
|
|
...(params.sessionKey ? { sessionKey: params.sessionKey } : {}),
|
|
...(params.approvalDecision ? { approvalDecision: params.approvalDecision } : {}),
|
|
...(params.systemRunPlan ? { systemRunPlan: params.systemRunPlan } : {}),
|
|
idleTimeoutMs: params.idleTimeoutMs,
|
|
timeoutMs: params.timeoutMs,
|
|
},
|
|
timeoutMs: params.timeoutMs,
|
|
idleTimeoutMs: params.idleTimeoutMs,
|
|
idempotencyKey: randomUUID(),
|
|
onProgress: params.onProgress,
|
|
...(params.signal ? { signal: params.signal } : {}),
|
|
});
|
|
}
|