@downcity/plugins 1.0.61 → 1.0.64
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/shell/ShellPlugin.d.ts.map +1 -1
- package/bin/shell/ShellPlugin.js +38 -1
- package/bin/shell/ShellPlugin.js.map +1 -1
- package/bin/shell/ShellRuntimeTypes.d.ts +50 -1
- package/bin/shell/ShellRuntimeTypes.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntime.d.ts +21 -0
- package/bin/shell/runtime/ShellActionRuntime.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntime.js +136 -0
- package/bin/shell/runtime/ShellActionRuntime.js.map +1 -1
- package/bin/shell/runtime/ShellActionRuntimeSupport.d.ts.map +1 -1
- package/bin/shell/runtime/ShellActionRuntimeSupport.js +3 -0
- package/bin/shell/runtime/ShellActionRuntimeSupport.js.map +1 -1
- package/bin/shell/runtime/ShellApprovalRuntime.d.ts +57 -0
- package/bin/shell/runtime/ShellApprovalRuntime.d.ts.map +1 -0
- package/bin/shell/runtime/ShellApprovalRuntime.js +182 -0
- package/bin/shell/runtime/ShellApprovalRuntime.js.map +1 -0
- package/bin/shell/types/ShellPluginOptions.d.ts +8 -0
- package/bin/shell/types/ShellPluginOptions.d.ts.map +1 -1
- package/package.json +2 -2
- package/scripts/unrestricted-sandbox-approval.test.mjs +156 -0
- package/src/shell/ShellPlugin.ts +40 -0
- package/src/shell/ShellRuntimeTypes.ts +54 -1
- package/src/shell/runtime/ShellActionRuntime.ts +164 -0
- package/src/shell/runtime/ShellActionRuntimeSupport.ts +6 -0
- package/src/shell/runtime/ShellApprovalRuntime.ts +236 -0
- package/src/shell/types/ShellPluginOptions.ts +10 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell unrestricted sandbox 审批运行时。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - agent 只能通过 shell tool 请求 unrestricted sandbox;真正执行前必须等待用户确认。
|
|
6
|
+
* - 审批结果最终回到原 tool result;session event 只用于 UI/CLI/Console 展示和操作。
|
|
7
|
+
* - V1 授权粒度固定为单次命令或单次 shell_start 创建的命令会话。
|
|
8
|
+
*/
|
|
9
|
+
import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
|
|
10
|
+
import type { ShellApprovalStatus } from "@downcity/agent/internal/executor/tools/shell/types/ShellPlugin.js";
|
|
11
|
+
import type { ShellPluginState } from "../../shell/ShellRuntimeTypes.js";
|
|
12
|
+
/**
|
|
13
|
+
* 校验 unrestricted sandbox 请求。
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateUnrestrictedRequest(params: {
|
|
16
|
+
cmd: string;
|
|
17
|
+
reason?: string;
|
|
18
|
+
}): string | null;
|
|
19
|
+
/**
|
|
20
|
+
* 请求用户批准 unrestricted sandbox 执行。
|
|
21
|
+
*/
|
|
22
|
+
export declare function requestUnrestrictedApproval(params: {
|
|
23
|
+
state: ShellPluginState;
|
|
24
|
+
context: AgentContext;
|
|
25
|
+
shellId: string;
|
|
26
|
+
toolName: "shell_exec" | "shell_start";
|
|
27
|
+
cmd: string;
|
|
28
|
+
cwd: string;
|
|
29
|
+
reason: string;
|
|
30
|
+
ownerContextId?: string;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
approvalId: string;
|
|
33
|
+
status: ShellApprovalStatus;
|
|
34
|
+
}>;
|
|
35
|
+
/**
|
|
36
|
+
* 兑现 unrestricted sandbox 审批。
|
|
37
|
+
*/
|
|
38
|
+
export declare function resolveApproval(params: {
|
|
39
|
+
state: ShellPluginState;
|
|
40
|
+
context: AgentContext;
|
|
41
|
+
approvalId: string;
|
|
42
|
+
decision: ShellApprovalStatus;
|
|
43
|
+
}): Promise<boolean>;
|
|
44
|
+
/**
|
|
45
|
+
* 列出 pending unrestricted sandbox 审批。
|
|
46
|
+
*/
|
|
47
|
+
export declare function listPendingApprovals(state: ShellPluginState): Array<{
|
|
48
|
+
approvalId: string;
|
|
49
|
+
shellId: string;
|
|
50
|
+
ownerContextId?: string;
|
|
51
|
+
toolName: "shell_exec" | "shell_start";
|
|
52
|
+
cmd: string;
|
|
53
|
+
cwd: string;
|
|
54
|
+
reason: string;
|
|
55
|
+
createdAt: number;
|
|
56
|
+
}>;
|
|
57
|
+
//# sourceMappingURL=ShellApprovalRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellApprovalRuntime.d.ts","sourceRoot":"","sources":["../../../src/shell/runtime/ShellApprovalRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8DAA8D,CAAC;AACjG,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oEAAoE,CAAC;AAC9G,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAsDrE;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE;IAClD,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,GAAG,IAAI,CAShB;AAED;;GAEG;AACH,wBAAsB,2BAA2B,CAAC,MAAM,EAAE;IACxD,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,YAAY,GAAG,aAAa,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,GAAG,OAAO,CAAC;IACV,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,mBAAmB,CAAC;CAC7B,CAAC,CAkED;AAED;;GAEG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,KAAK,EAAE,gBAAgB,CAAC;IACxB,OAAO,EAAE,YAAY,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,mBAAmB,CAAC;CAC/B,GAAG,OAAO,CAAC,OAAO,CAAC,CAiCnB;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,KAAK,CAAC;IACnE,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,YAAY,GAAG,aAAa,CAAC;IACvC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC,CAWD"}
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell unrestricted sandbox 审批运行时。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - agent 只能通过 shell tool 请求 unrestricted sandbox;真正执行前必须等待用户确认。
|
|
6
|
+
* - 审批结果最终回到原 tool result;session event 只用于 UI/CLI/Console 展示和操作。
|
|
7
|
+
* - V1 授权粒度固定为单次命令或单次 shell_start 创建的命令会话。
|
|
8
|
+
*/
|
|
9
|
+
import fs from "fs-extra";
|
|
10
|
+
import path from "node:path";
|
|
11
|
+
import { generateId } from "@downcity/agent/internal/utils/Id.js";
|
|
12
|
+
import { getSessionRunContext } from "@downcity/agent/internal/executor/SessionRunScope.js";
|
|
13
|
+
import { nowMs } from "./ShellActionRuntimeSupport.js";
|
|
14
|
+
const DANGEROUS_COMMAND_PATTERNS = [
|
|
15
|
+
/\bsudo\b/,
|
|
16
|
+
/\brm\s+-[^&|;\n]*r[^&|;\n]*f\s+\/(?:\s|$)/,
|
|
17
|
+
/\bchmod\s+-R\s+777\s+\/(?:\s|$)/,
|
|
18
|
+
/\bssh-keygen\b/,
|
|
19
|
+
/\bsecurity\s+(?:add|delete|unlock|set|import|export)-/i,
|
|
20
|
+
/(?:^|[\s;&|])(?:nohup\s+)?[^;&|\n]*(?:&)\s*$/,
|
|
21
|
+
];
|
|
22
|
+
function isDangerousCommand(cmd) {
|
|
23
|
+
return DANGEROUS_COMMAND_PATTERNS.some((pattern) => pattern.test(cmd));
|
|
24
|
+
}
|
|
25
|
+
function resolveAuditPath(context) {
|
|
26
|
+
return path.join(context.rootPath, ".downcity", "logs", "unrestricted-sandbox-audit.jsonl");
|
|
27
|
+
}
|
|
28
|
+
async function appendAudit(params) {
|
|
29
|
+
const filePath = resolveAuditPath(params.context);
|
|
30
|
+
await fs.ensureDir(path.dirname(filePath));
|
|
31
|
+
await fs.appendFile(filePath, `${JSON.stringify(params.record)}\n`, "utf-8");
|
|
32
|
+
}
|
|
33
|
+
function publishApprovalResult(params) {
|
|
34
|
+
const sessionId = String(params.ownerContextId || "").trim();
|
|
35
|
+
if (!sessionId)
|
|
36
|
+
return;
|
|
37
|
+
const turnId = String(getSessionRunContext()?.turnId || sessionId).trim();
|
|
38
|
+
try {
|
|
39
|
+
params.context.session.get(sessionId).publishEvent({
|
|
40
|
+
type: "tool-approval-result",
|
|
41
|
+
turnId,
|
|
42
|
+
toolCallId: params.shellId,
|
|
43
|
+
toolName: params.toolName,
|
|
44
|
+
approvalId: params.approvalId,
|
|
45
|
+
decision: params.decision,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
// ignore event delivery failures
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* 校验 unrestricted sandbox 请求。
|
|
54
|
+
*/
|
|
55
|
+
export function validateUnrestrictedRequest(params) {
|
|
56
|
+
const reason = String(params.reason || "").trim();
|
|
57
|
+
if (!reason) {
|
|
58
|
+
return "unrestricted sandbox requires a non-empty reason";
|
|
59
|
+
}
|
|
60
|
+
if (isDangerousCommand(params.cmd)) {
|
|
61
|
+
return "unrestricted sandbox rejected a dangerous command";
|
|
62
|
+
}
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* 请求用户批准 unrestricted sandbox 执行。
|
|
67
|
+
*/
|
|
68
|
+
export async function requestUnrestrictedApproval(params) {
|
|
69
|
+
const approvalId = `ap_${generateId()}`;
|
|
70
|
+
const createdAt = nowMs();
|
|
71
|
+
const ownerContextId = String(params.ownerContextId || "").trim() || undefined;
|
|
72
|
+
const status = await new Promise((resolve) => {
|
|
73
|
+
const timer = setTimeout(() => {
|
|
74
|
+
resolveApproval({
|
|
75
|
+
state: params.state,
|
|
76
|
+
context: params.context,
|
|
77
|
+
approvalId,
|
|
78
|
+
decision: "expired",
|
|
79
|
+
}).catch(() => undefined);
|
|
80
|
+
}, params.state.options.defaultApprovalTimeoutMs);
|
|
81
|
+
if (typeof timer.unref === "function")
|
|
82
|
+
timer.unref();
|
|
83
|
+
params.state.approvals.set(approvalId, {
|
|
84
|
+
approvalId,
|
|
85
|
+
shellId: params.shellId,
|
|
86
|
+
...(ownerContextId ? { ownerContextId } : {}),
|
|
87
|
+
toolName: params.toolName,
|
|
88
|
+
cmd: params.cmd,
|
|
89
|
+
cwd: params.cwd,
|
|
90
|
+
reason: params.reason,
|
|
91
|
+
createdAt,
|
|
92
|
+
timer,
|
|
93
|
+
resolve,
|
|
94
|
+
});
|
|
95
|
+
if (ownerContextId) {
|
|
96
|
+
const turnId = String(getSessionRunContext()?.turnId || ownerContextId).trim();
|
|
97
|
+
try {
|
|
98
|
+
params.context.session.get(ownerContextId).publishEvent({
|
|
99
|
+
type: "tool-approval-request",
|
|
100
|
+
turnId,
|
|
101
|
+
toolCallId: params.shellId,
|
|
102
|
+
toolName: params.toolName,
|
|
103
|
+
approvalId,
|
|
104
|
+
sandbox: "unrestricted",
|
|
105
|
+
cmd: params.cmd,
|
|
106
|
+
cwd: params.cwd,
|
|
107
|
+
reason: params.reason,
|
|
108
|
+
status: "pending",
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// ignore event delivery failures
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
appendAudit({
|
|
116
|
+
context: params.context,
|
|
117
|
+
record: {
|
|
118
|
+
event: "approval_requested",
|
|
119
|
+
approval_id: approvalId,
|
|
120
|
+
session_id: ownerContextId || null,
|
|
121
|
+
tool_call_id: params.shellId,
|
|
122
|
+
agent_id: params.context.config?.id || null,
|
|
123
|
+
cmd: params.cmd,
|
|
124
|
+
cwd: params.cwd,
|
|
125
|
+
reason: params.reason,
|
|
126
|
+
created_at: new Date(createdAt).toISOString(),
|
|
127
|
+
},
|
|
128
|
+
}).catch(() => undefined);
|
|
129
|
+
});
|
|
130
|
+
return { approvalId, status };
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* 兑现 unrestricted sandbox 审批。
|
|
134
|
+
*/
|
|
135
|
+
export async function resolveApproval(params) {
|
|
136
|
+
const approval = params.state.approvals.get(params.approvalId);
|
|
137
|
+
if (!approval)
|
|
138
|
+
return false;
|
|
139
|
+
params.state.approvals.delete(params.approvalId);
|
|
140
|
+
clearTimeout(approval.timer);
|
|
141
|
+
approval.resolve(params.decision);
|
|
142
|
+
publishApprovalResult({
|
|
143
|
+
context: params.context,
|
|
144
|
+
ownerContextId: approval.ownerContextId,
|
|
145
|
+
approvalId: approval.approvalId,
|
|
146
|
+
shellId: approval.shellId,
|
|
147
|
+
toolName: approval.toolName,
|
|
148
|
+
decision: params.decision,
|
|
149
|
+
});
|
|
150
|
+
await appendAudit({
|
|
151
|
+
context: params.context,
|
|
152
|
+
record: {
|
|
153
|
+
event: "approval_resolved",
|
|
154
|
+
approval_id: approval.approvalId,
|
|
155
|
+
session_id: approval.ownerContextId || null,
|
|
156
|
+
tool_call_id: approval.shellId,
|
|
157
|
+
agent_id: params.context.config?.id || null,
|
|
158
|
+
cmd: approval.cmd,
|
|
159
|
+
cwd: approval.cwd,
|
|
160
|
+
reason: approval.reason,
|
|
161
|
+
decision: params.decision,
|
|
162
|
+
resolved_at: new Date(nowMs()).toISOString(),
|
|
163
|
+
},
|
|
164
|
+
}).catch(() => undefined);
|
|
165
|
+
return true;
|
|
166
|
+
}
|
|
167
|
+
/**
|
|
168
|
+
* 列出 pending unrestricted sandbox 审批。
|
|
169
|
+
*/
|
|
170
|
+
export function listPendingApprovals(state) {
|
|
171
|
+
return Array.from(state.approvals.values()).map((approval) => ({
|
|
172
|
+
approvalId: approval.approvalId,
|
|
173
|
+
shellId: approval.shellId,
|
|
174
|
+
...(approval.ownerContextId ? { ownerContextId: approval.ownerContextId } : {}),
|
|
175
|
+
toolName: approval.toolName,
|
|
176
|
+
cmd: approval.cmd,
|
|
177
|
+
cwd: approval.cwd,
|
|
178
|
+
reason: approval.reason,
|
|
179
|
+
createdAt: approval.createdAt,
|
|
180
|
+
}));
|
|
181
|
+
}
|
|
182
|
+
//# sourceMappingURL=ShellApprovalRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ShellApprovalRuntime.js","sourceRoot":"","sources":["../../../src/shell/runtime/ShellApprovalRuntime.ts"],"names":[],"mappings":"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"}
|
|
@@ -50,6 +50,10 @@ export interface ShellPluginOptions {
|
|
|
50
50
|
* `shell.exec` 默认总超时,单位毫秒。
|
|
51
51
|
*/
|
|
52
52
|
defaultExecTimeoutMs?: number;
|
|
53
|
+
/**
|
|
54
|
+
* unrestricted sandbox 审批默认超时时间,单位毫秒。
|
|
55
|
+
*/
|
|
56
|
+
defaultApprovalTimeoutMs?: number;
|
|
53
57
|
}
|
|
54
58
|
/**
|
|
55
59
|
* ShellPlugin 归一化后的运行参数。
|
|
@@ -91,5 +95,9 @@ export interface ResolvedShellPluginOptions {
|
|
|
91
95
|
* `shell.exec` 默认总超时,单位毫秒。
|
|
92
96
|
*/
|
|
93
97
|
defaultExecTimeoutMs: number;
|
|
98
|
+
/**
|
|
99
|
+
* unrestricted sandbox 审批默认超时时间,单位毫秒。
|
|
100
|
+
*/
|
|
101
|
+
defaultApprovalTimeoutMs: number;
|
|
94
102
|
}
|
|
95
103
|
//# sourceMappingURL=ShellPluginOptions.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShellPluginOptions.d.ts","sourceRoot":"","sources":["../../../src/shell/types/ShellPluginOptions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"ShellPluginOptions.d.ts","sourceRoot":"","sources":["../../../src/shell/types/ShellPluginOptions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;IAEzB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAEhC;;OAEG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAE9B;;OAEG;IACH,wBAAwB,CAAC,EAAE,MAAM,CAAC;CACnC;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACzC;;OAEG;IACH,eAAe,EAAE,MAAM,CAAC;IAExB;;OAEG;IACH,cAAc,EAAE,MAAM,CAAC;IAEvB;;OAEG;IACH,sBAAsB,EAAE,MAAM,CAAC;IAE/B;;OAEG;IACH,kBAAkB,EAAE,MAAM,CAAC;IAE3B;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,mBAAmB,EAAE,MAAM,CAAC;IAE5B;;OAEG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,oBAAoB,EAAE,MAAM,CAAC;IAE7B;;OAEG;IACH,wBAAwB,EAAE,MAAM,CAAC;CAClC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@downcity/plugins",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.64",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Downcity 内建 plugin 集合包",
|
|
6
6
|
"main": "./bin/index.js",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"node-cron": "^4.2.1",
|
|
23
23
|
"ws": "^8.21.0",
|
|
24
24
|
"zod": "^4.4.3",
|
|
25
|
-
"@downcity/agent": "^1.1.
|
|
25
|
+
"@downcity/agent": "^1.1.111"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"@types/better-sqlite3": "^7.6.13",
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 验证 shell unrestricted sandbox 审批运行时。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - 测试编译后的 bin 输出,避免测试文件进入 package 源码导出面。
|
|
6
|
+
* - 直接驱动 shell runtime state,模拟 UI/Console 收到 approval_id 后批准或拒绝。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import fs from "node:fs/promises";
|
|
12
|
+
import os from "node:os";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
approveShellApproval,
|
|
17
|
+
closeAllShellSessions,
|
|
18
|
+
createShellPluginState,
|
|
19
|
+
denyShellApproval,
|
|
20
|
+
execShellCommand,
|
|
21
|
+
startShellSession,
|
|
22
|
+
} from "../bin/shell/runtime/ShellActionRuntime.js";
|
|
23
|
+
|
|
24
|
+
async function create_fixture() {
|
|
25
|
+
const root_path = await fs.mkdtemp(path.join(os.tmpdir(), "downcity-unrestricted-"));
|
|
26
|
+
const events = [];
|
|
27
|
+
const context = {
|
|
28
|
+
rootPath: root_path,
|
|
29
|
+
env: {},
|
|
30
|
+
config: { id: "test-agent" },
|
|
31
|
+
paths: {
|
|
32
|
+
getDowncityChannelMetaPath: () => path.join(root_path, ".downcity", "channel", "meta.json"),
|
|
33
|
+
},
|
|
34
|
+
session: {
|
|
35
|
+
get: () => ({
|
|
36
|
+
publishEvent: (event) => {
|
|
37
|
+
events.push(event);
|
|
38
|
+
},
|
|
39
|
+
}),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
return {
|
|
43
|
+
root_path,
|
|
44
|
+
events,
|
|
45
|
+
context,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function wait_for_approval(state) {
|
|
50
|
+
const started_at = Date.now();
|
|
51
|
+
while (Date.now() - started_at < 2000) {
|
|
52
|
+
const approval = Array.from(state.approvals.values())[0];
|
|
53
|
+
if (approval) return approval;
|
|
54
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
55
|
+
}
|
|
56
|
+
throw new Error("approval request was not created");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
test("shell_start unrestricted requires reason", async () => {
|
|
60
|
+
const fixture = await create_fixture();
|
|
61
|
+
const state = createShellPluginState({ defaultApprovalTimeoutMs: 500 });
|
|
62
|
+
try {
|
|
63
|
+
await assert.rejects(
|
|
64
|
+
startShellSession(state, fixture.context, {
|
|
65
|
+
cmd: "printf should-not-run",
|
|
66
|
+
cwd: fixture.root_path,
|
|
67
|
+
shell: "/bin/sh",
|
|
68
|
+
login: false,
|
|
69
|
+
sandbox: "unrestricted",
|
|
70
|
+
}),
|
|
71
|
+
/requires a non-empty reason/,
|
|
72
|
+
);
|
|
73
|
+
} finally {
|
|
74
|
+
await closeAllShellSessions(state, true);
|
|
75
|
+
await fs.rm(fixture.root_path, { recursive: true, force: true });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test("shell_start unrestricted denied returns denied tool result without execution", async () => {
|
|
80
|
+
const fixture = await create_fixture();
|
|
81
|
+
const state = createShellPluginState({
|
|
82
|
+
defaultApprovalTimeoutMs: 2000,
|
|
83
|
+
defaultInlineWaitMs: 20,
|
|
84
|
+
});
|
|
85
|
+
try {
|
|
86
|
+
const marker_path = path.join(fixture.root_path, "should-not-exist.txt");
|
|
87
|
+
const pending_result = startShellSession(state, fixture.context, {
|
|
88
|
+
cmd: `printf denied > ${JSON.stringify(marker_path)}`,
|
|
89
|
+
cwd: fixture.root_path,
|
|
90
|
+
shell: "/bin/sh",
|
|
91
|
+
login: false,
|
|
92
|
+
sandbox: "unrestricted",
|
|
93
|
+
reason: "测试拒绝 unrestricted sandbox 不会执行命令。",
|
|
94
|
+
ownerContextId: "session_test",
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const approval = await wait_for_approval(state);
|
|
98
|
+
assert.equal(approval.toolName, "shell_start");
|
|
99
|
+
assert.equal(fixture.events[0]?.type, "tool-approval-request");
|
|
100
|
+
assert.equal(fixture.events[0]?.approvalId, approval.approvalId);
|
|
101
|
+
|
|
102
|
+
assert.equal(
|
|
103
|
+
await denyShellApproval(state, fixture.context, approval.approvalId),
|
|
104
|
+
true,
|
|
105
|
+
);
|
|
106
|
+
const result = await pending_result;
|
|
107
|
+
|
|
108
|
+
assert.equal(result.shell.sandboxMode, "unrestricted");
|
|
109
|
+
assert.equal(result.shell.approvalStatus, "denied");
|
|
110
|
+
assert.equal(result.shell.status, "failed");
|
|
111
|
+
assert.match(result.chunk.output, /User denied unrestricted sandbox execution/);
|
|
112
|
+
await assert.rejects(fs.stat(marker_path), /ENOENT/);
|
|
113
|
+
assert.equal(fixture.events.at(-1)?.type, "tool-approval-result");
|
|
114
|
+
assert.equal(fixture.events.at(-1)?.decision, "denied");
|
|
115
|
+
} finally {
|
|
116
|
+
await closeAllShellSessions(state, true);
|
|
117
|
+
await fs.rm(fixture.root_path, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("shell_exec unrestricted approved executes in unrestricted sandbox", async () => {
|
|
122
|
+
const fixture = await create_fixture();
|
|
123
|
+
const state = createShellPluginState({
|
|
124
|
+
defaultApprovalTimeoutMs: 2000,
|
|
125
|
+
defaultInlineWaitMs: 20,
|
|
126
|
+
defaultExecTimeoutMs: 2000,
|
|
127
|
+
});
|
|
128
|
+
try {
|
|
129
|
+
const pending_result = execShellCommand(state, fixture.context, {
|
|
130
|
+
cmd: "printf unrestricted-ok",
|
|
131
|
+
cwd: fixture.root_path,
|
|
132
|
+
shell: "/bin/sh",
|
|
133
|
+
login: false,
|
|
134
|
+
sandbox: "unrestricted",
|
|
135
|
+
reason: "测试批准后执行 unrestricted sandbox 命令。",
|
|
136
|
+
timeoutMs: 2000,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const approval = await wait_for_approval(state);
|
|
140
|
+
assert.equal(approval.toolName, "shell_exec");
|
|
141
|
+
assert.equal(
|
|
142
|
+
await approveShellApproval(state, fixture.context, approval.approvalId),
|
|
143
|
+
true,
|
|
144
|
+
);
|
|
145
|
+
const result = await pending_result;
|
|
146
|
+
|
|
147
|
+
assert.equal(result.shell.sandboxed, false);
|
|
148
|
+
assert.equal(result.shell.sandboxMode, "unrestricted");
|
|
149
|
+
assert.equal(result.shell.approvalStatus, "approved");
|
|
150
|
+
assert.equal(result.shell.exitCode, 0);
|
|
151
|
+
assert.equal(result.chunk.output, "unrestricted-ok");
|
|
152
|
+
} finally {
|
|
153
|
+
await closeAllShellSessions(state, true);
|
|
154
|
+
await fs.rm(fixture.root_path, { recursive: true, force: true });
|
|
155
|
+
}
|
|
156
|
+
});
|
package/src/shell/ShellPlugin.ts
CHANGED
|
@@ -29,8 +29,11 @@ import {
|
|
|
29
29
|
closeAllShellSessions,
|
|
30
30
|
closeShellSession,
|
|
31
31
|
createShellPluginState,
|
|
32
|
+
approveShellApproval,
|
|
33
|
+
denyShellApproval,
|
|
32
34
|
execShellCommand,
|
|
33
35
|
getShellSessionStatus,
|
|
36
|
+
listShellApprovals,
|
|
34
37
|
readShellSession,
|
|
35
38
|
startShellSession,
|
|
36
39
|
waitShellSession,
|
|
@@ -108,6 +111,42 @@ export class ShellPlugin extends BasePlugin {
|
|
|
108
111
|
data: await this.close(params.context, params.payload as ShellCloseRequest),
|
|
109
112
|
}),
|
|
110
113
|
},
|
|
114
|
+
approvals: {
|
|
115
|
+
execute: async () => ({
|
|
116
|
+
success: true,
|
|
117
|
+
data: { approvals: listShellApprovals(this.state) },
|
|
118
|
+
}),
|
|
119
|
+
},
|
|
120
|
+
approve: {
|
|
121
|
+
execute: async (params) => {
|
|
122
|
+
const payload = params.payload as { approvalId?: unknown; approval_id?: unknown };
|
|
123
|
+
const approvalId = String(payload?.approvalId || payload?.approval_id || "").trim();
|
|
124
|
+
if (!approvalId) {
|
|
125
|
+
return { success: false, error: "approvalId is required" };
|
|
126
|
+
}
|
|
127
|
+
const ok = await approveShellApproval(this.state, params.context, approvalId);
|
|
128
|
+
return {
|
|
129
|
+
success: ok,
|
|
130
|
+
data: { approvalId, approved: ok },
|
|
131
|
+
...(ok ? {} : { error: "approval request not found" }),
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
deny: {
|
|
136
|
+
execute: async (params) => {
|
|
137
|
+
const payload = params.payload as { approvalId?: unknown; approval_id?: unknown };
|
|
138
|
+
const approvalId = String(payload?.approvalId || payload?.approval_id || "").trim();
|
|
139
|
+
if (!approvalId) {
|
|
140
|
+
return { success: false, error: "approvalId is required" };
|
|
141
|
+
}
|
|
142
|
+
const ok = await denyShellApproval(this.state, params.context, approvalId);
|
|
143
|
+
return {
|
|
144
|
+
success: ok,
|
|
145
|
+
data: { approvalId, denied: ok },
|
|
146
|
+
...(ok ? {} : { error: "approval request not found" }),
|
|
147
|
+
};
|
|
148
|
+
},
|
|
149
|
+
},
|
|
111
150
|
};
|
|
112
151
|
|
|
113
152
|
this.lifecycle = {
|
|
@@ -122,6 +161,7 @@ export class ShellPlugin extends BasePlugin {
|
|
|
122
161
|
}
|
|
123
162
|
}
|
|
124
163
|
this.state.sessions.clear();
|
|
164
|
+
this.state.approvals.clear();
|
|
125
165
|
this.state.context = null;
|
|
126
166
|
},
|
|
127
167
|
};
|
|
@@ -9,9 +9,58 @@
|
|
|
9
9
|
|
|
10
10
|
import type { ChildProcessWithoutNullStreams } from "node:child_process";
|
|
11
11
|
import type { AgentContext } from "@downcity/agent/internal/types/runtime/agent/AgentContext.js";
|
|
12
|
-
import type {
|
|
12
|
+
import type {
|
|
13
|
+
ShellApprovalStatus,
|
|
14
|
+
ShellSessionSnapshot,
|
|
15
|
+
} from "@downcity/agent/internal/executor/tools/shell/types/ShellPlugin.js";
|
|
13
16
|
import type { ResolvedShellPluginOptions } from "@/shell/types/ShellPluginOptions.js";
|
|
14
17
|
|
|
18
|
+
/**
|
|
19
|
+
* unrestricted sandbox 审批运行态。
|
|
20
|
+
*/
|
|
21
|
+
export type ShellApprovalRuntimeState = {
|
|
22
|
+
/**
|
|
23
|
+
* 当前审批请求 ID。
|
|
24
|
+
*/
|
|
25
|
+
approvalId: string;
|
|
26
|
+
/**
|
|
27
|
+
* 关联的 shell_id。
|
|
28
|
+
*/
|
|
29
|
+
shellId: string;
|
|
30
|
+
/**
|
|
31
|
+
* 所属 session/聊天上下文。
|
|
32
|
+
*/
|
|
33
|
+
ownerContextId?: string;
|
|
34
|
+
/**
|
|
35
|
+
* 关联工具名。
|
|
36
|
+
*/
|
|
37
|
+
toolName: "shell_exec" | "shell_start";
|
|
38
|
+
/**
|
|
39
|
+
* 申请执行的命令。
|
|
40
|
+
*/
|
|
41
|
+
cmd: string;
|
|
42
|
+
/**
|
|
43
|
+
* 命令执行目录。
|
|
44
|
+
*/
|
|
45
|
+
cwd: string;
|
|
46
|
+
/**
|
|
47
|
+
* 申请原因。
|
|
48
|
+
*/
|
|
49
|
+
reason: string;
|
|
50
|
+
/**
|
|
51
|
+
* 当前审批创建时间。
|
|
52
|
+
*/
|
|
53
|
+
createdAt: number;
|
|
54
|
+
/**
|
|
55
|
+
* 审批超时定时器。
|
|
56
|
+
*/
|
|
57
|
+
timer: NodeJS.Timeout;
|
|
58
|
+
/**
|
|
59
|
+
* 兑现审批结果。
|
|
60
|
+
*/
|
|
61
|
+
resolve: (status: ShellApprovalStatus) => void;
|
|
62
|
+
};
|
|
63
|
+
|
|
15
64
|
/**
|
|
16
65
|
* 单个 shell wait 调用挂起时注册的 waiter。
|
|
17
66
|
*/
|
|
@@ -88,6 +137,10 @@ export type ShellPluginState = {
|
|
|
88
137
|
* 当前实例持有的全部 in-memory shell session。
|
|
89
138
|
*/
|
|
90
139
|
sessions: Map<string, ShellSessionRuntimeState>;
|
|
140
|
+
/**
|
|
141
|
+
* 当前实例持有的全部 pending unrestricted sandbox 审批。
|
|
142
|
+
*/
|
|
143
|
+
approvals: Map<string, ShellApprovalRuntimeState>;
|
|
91
144
|
/**
|
|
92
145
|
* 当前实例最近一次启动时绑定的 agent context。
|
|
93
146
|
*
|