@downcity/plugins 1.0.71 → 1.0.73

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@downcity/plugins",
3
- "version": "1.0.71",
3
+ "version": "1.0.73",
4
4
  "type": "module",
5
5
  "description": "Downcity 内建 plugin 集合包",
6
6
  "main": "./bin/index.js",
@@ -12,8 +12,8 @@
12
12
  }
13
13
  },
14
14
  "dependencies": {
15
- "@downcity/agent": "^1.1.118",
16
- "@downcity/shell": "^0.1.4",
15
+ "@downcity/agent": "^1.1.120",
16
+ "@downcity/shell": "^0.1.8",
17
17
  "@larksuiteoapi/node-sdk": "^1.66.0",
18
18
  "ai": "^6.0.193",
19
19
  "better-sqlite3": "^12.10.0",
@@ -19,6 +19,7 @@ import {
19
19
  denyShellApproval,
20
20
  execShellCommand,
21
21
  readShellSession,
22
+ setShellApprovalModeView,
22
23
  startShellSession,
23
24
  writeShellSession,
24
25
  } from "@downcity/shell/session/ShellActionRuntime.js";
@@ -26,6 +27,7 @@ import {
26
27
  async function create_fixture() {
27
28
  const root_path = await fs.mkdtemp(path.join(os.tmpdir(), "downcity-unrestricted-"));
28
29
  const events = [];
30
+ const run_context = {};
29
31
  const context = {
30
32
  rootPath: root_path,
31
33
  env: {},
@@ -40,10 +42,14 @@ async function create_fixture() {
40
42
  },
41
43
  }),
42
44
  },
45
+ shellIntegration: {
46
+ getRunContext: () => run_context,
47
+ },
43
48
  };
44
49
  return {
45
50
  root_path,
46
51
  events,
52
+ run_context,
47
53
  context,
48
54
  };
49
55
  }
@@ -333,3 +339,98 @@ test("shell_write safe writes without approval", async () => {
333
339
  await fs.rm(fixture.root_path, { recursive: true, force: true });
334
340
  }
335
341
  });
342
+
343
+ test("shell_exec unrestricted always-allow mode skips pending approval", async () => {
344
+ const fixture = await create_fixture();
345
+ const state = createShellRuntimeState({
346
+ defaultApprovalTimeoutMs: 2000,
347
+ defaultInlineWaitMs: 20,
348
+ defaultExecTimeoutMs: 2000,
349
+ });
350
+ try {
351
+ assert.equal(
352
+ setShellApprovalModeView(state, "session_auto", "always-allow"),
353
+ "always-allow",
354
+ );
355
+ fixture.run_context.sessionId = "session_auto";
356
+
357
+ const result = await execShellCommand(state, fixture.context, {
358
+ cmd: "printf auto-approved",
359
+ cwd: fixture.root_path,
360
+ shell: "/bin/sh",
361
+ login: false,
362
+ sandbox: "unrestricted",
363
+ reason: "测试当前 session 自动允许 unrestricted shell_exec。",
364
+ timeoutMs: 2000,
365
+ });
366
+
367
+ assert.equal(state.approvals.size, 0);
368
+ assert.equal(fixture.events.length, 0);
369
+ assert.equal(result.shell.sandboxMode, "unrestricted");
370
+ assert.equal(result.shell.approvalStatus, "approved");
371
+ assert.equal(result.chunk.output, "auto-approved");
372
+
373
+ const audit_path = path.join(
374
+ fixture.root_path,
375
+ ".downcity",
376
+ "logs",
377
+ "unrestricted-sandbox-audit.jsonl",
378
+ );
379
+ const audit = await fs.readFile(audit_path, "utf-8");
380
+ assert.match(audit, /approval_auto_approved/);
381
+ } finally {
382
+ await closeAllShellSessions(state, true);
383
+ await fs.rm(fixture.root_path, { recursive: true, force: true });
384
+ }
385
+ });
386
+
387
+ test("shell_write unrestricted always-allow mode is scoped by session", async () => {
388
+ const fixture = await create_fixture();
389
+ const state = createShellRuntimeState({
390
+ defaultApprovalTimeoutMs: 2000,
391
+ defaultInlineWaitMs: 20,
392
+ });
393
+ try {
394
+ setShellApprovalModeView(state, "session_auto", "always-allow");
395
+
396
+ const auto_started = await startShellSession(state, fixture.context, {
397
+ cmd: "cat",
398
+ cwd: fixture.root_path,
399
+ shell: "/bin/sh",
400
+ login: false,
401
+ sandbox: "unrestricted",
402
+ reason: "测试当前 session 自动允许 unrestricted shell_start。",
403
+ ownerContextId: "session_auto",
404
+ inlineWaitMs: 20,
405
+ });
406
+ assert.equal(auto_started.shell.approvalStatus, "approved");
407
+ assert.equal(state.approvals.size, 0);
408
+
409
+ const written = await writeShellSession(state, fixture.context, {
410
+ shellId: auto_started.shell.shellId,
411
+ chars: "auto-write\n",
412
+ reason: "测试当前 session 自动允许 unrestricted shell_write。",
413
+ });
414
+ assert.equal(written.shell.approvalStatus, "approved");
415
+ assert.equal(state.approvals.size, 0);
416
+
417
+ const ask_pending = startShellSession(state, fixture.context, {
418
+ cmd: "printf ask-session",
419
+ cwd: fixture.root_path,
420
+ shell: "/bin/sh",
421
+ login: false,
422
+ sandbox: "unrestricted",
423
+ reason: "测试其它 session 仍然需要审批。",
424
+ ownerContextId: "session_ask",
425
+ inlineWaitMs: 20,
426
+ });
427
+ const approval = await wait_for_approval(state);
428
+ assert.equal(approval.ownerContextId, "session_ask");
429
+ await approveShellApproval(state, fixture.context, approval.approvalId);
430
+ const ask_result = await ask_pending;
431
+ assert.equal(ask_result.shell.approvalStatus, "approved");
432
+ } finally {
433
+ await closeAllShellSessions(state, true);
434
+ await fs.rm(fixture.root_path, { recursive: true, force: true });
435
+ }
436
+ });