dsh-perm-gate 2.1.0 → 2.1.2

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/INSTALL.ja.md +57 -1
  3. package/INSTALL.ko.md +54 -1
  4. package/INSTALL.md +53 -1
  5. package/INSTALL.zh.md +48 -1
  6. package/README.ja.md +84 -10
  7. package/README.ko.md +81 -10
  8. package/README.md +118 -22
  9. package/README.zh.md +86 -8
  10. package/cordis.patch.yml +34 -3
  11. package/dsh.plugin.json +22 -0
  12. package/lib/agent-identity.d.ts +43 -0
  13. package/lib/agent-identity.js +95 -0
  14. package/lib/client.js +114 -0
  15. package/lib/client.js.map +1 -1
  16. package/lib/compiler.d.ts +39 -0
  17. package/lib/compiler.js +106 -0
  18. package/lib/config.d.ts +95 -2
  19. package/lib/config.js +28 -3
  20. package/lib/engine.js +9 -2
  21. package/lib/evaluate.d.ts +9 -0
  22. package/lib/evaluate.js +189 -0
  23. package/lib/events.d.ts +13 -0
  24. package/lib/events.js +31 -0
  25. package/lib/index.d.ts +1 -1
  26. package/lib/index.js +235 -6
  27. package/lib/network-lifecycle.d.ts +107 -0
  28. package/lib/network-lifecycle.js +180 -0
  29. package/lib/network.d.ts +109 -0
  30. package/lib/network.js +196 -0
  31. package/lib/proxy.d.ts +160 -0
  32. package/lib/proxy.js +529 -0
  33. package/lib/risk.js +2 -3
  34. package/lib/rule-chain.d.ts +39 -0
  35. package/lib/rule-chain.js +222 -0
  36. package/lib/rule-dims.d.ts +139 -0
  37. package/lib/rule-dims.js +194 -0
  38. package/lib/rule.d.ts +25 -0
  39. package/lib/rule.js +15 -1
  40. package/lib/runtime.d.ts +87 -0
  41. package/lib/runtime.js +229 -2
  42. package/lib/session-sweep.d.ts +45 -0
  43. package/lib/session-sweep.js +153 -0
  44. package/lib/shadow.d.ts +34 -0
  45. package/lib/shadow.js +143 -0
  46. package/lib/watch.d.ts +42 -0
  47. package/lib/watch.js +170 -0
  48. package/package.json +8 -2
  49. package/scripts/patch-permission-glyph.mjs +209 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Agent identity extraction for the `agents` rule dimension.
3
+ *
4
+ * Given a tool execution context, produces zero or more identity candidates
5
+ * that the `agents` dimension can match against. Candidates are produced
6
+ * in priority order: most-specific first.
7
+ *
8
+ * Identity model:
9
+ * - `main` — the primary (top-level) agent session
10
+ * - `subagent` — a delegated child agent (parentAuthorized present)
11
+ * - `preset:<name>` — running under a named permission preset
12
+ *
13
+ * When no identity can be determined, an empty array is returned and the
14
+ * `agents` dimension in rules fail-closes (no match → rule skipped).
15
+ */
16
+ /**
17
+ * Extract agent identity candidates from a tool execution context.
18
+ *
19
+ * Returns a deduplicated list of identity strings. The `agents` rule
20
+ * dimension matches if ANY candidate matches ANY entry in the rule's
21
+ * `agents` list.
22
+ *
23
+ * @returns candidates in priority order, e.g. `['preset:permissive', 'subagent']`
24
+ * or `['main']`. Empty array = undecidable → agents rules fail-close.
25
+ */
26
+ export function extractAgentCandidates(exec) {
27
+ const candidates = [];
28
+ // 1. Preset name — most specific identity.
29
+ const preset = presetFromEvents(exec);
30
+ if (preset !== undefined) {
31
+ candidates.push(`preset:${preset}`);
32
+ }
33
+ // 2. Main vs subagent — determined by parentAuthorized presence.
34
+ if (exec.parentAuthorized !== undefined) {
35
+ // parentAuthorized is present → this is a child/subagent.
36
+ candidates.push('subagent');
37
+ }
38
+ else {
39
+ // parentAuthorized absent → assume main agent.
40
+ candidates.push('main');
41
+ }
42
+ return candidates;
43
+ }
44
+ /**
45
+ * Derive the current permission preset name from the session event log.
46
+ *
47
+ * The event log is append-only; the last `preset-switch` or `tier-select`
48
+ * event carries the current preset. Returns undefined when the log is
49
+ * unavailable or no preset event exists.
50
+ */
51
+ function presetFromEvents(exec) {
52
+ const events = eventsOf(exec);
53
+ if (events === undefined)
54
+ return undefined;
55
+ // Walk backwards to find the most recent preset/tier event.
56
+ for (let i = events.length - 1; i >= 0; i--) {
57
+ const ev = events[i];
58
+ if (typeof ev !== 'object' || ev === null)
59
+ continue;
60
+ const rec = ev;
61
+ const type = rec.type ?? rec.kind;
62
+ if (typeof type !== 'string')
63
+ continue;
64
+ if (type === 'preset-switch' || type === 'tier-select') {
65
+ const value = rec.preset ?? rec.tier ?? rec.value;
66
+ if (typeof value === 'string' && value !== '')
67
+ return value;
68
+ }
69
+ }
70
+ return undefined;
71
+ }
72
+ /**
73
+ * Read the session event log from a ToolExecutionLike, handling both
74
+ * DSH 0.1.1 (plain array) and 0.1.2+ (function accessors) shapes.
75
+ */
76
+ function eventsOf(exec) {
77
+ const session = exec.agent?.session;
78
+ if (session === undefined)
79
+ return undefined;
80
+ if (typeof session.snapshotEvents === 'function') {
81
+ try {
82
+ return session.snapshotEvents();
83
+ }
84
+ catch { /* ignore */ }
85
+ }
86
+ if (typeof session.ownEvents === 'function') {
87
+ try {
88
+ return session.ownEvents();
89
+ }
90
+ catch { /* ignore */ }
91
+ }
92
+ if (Array.isArray(session.events))
93
+ return session.events;
94
+ return undefined;
95
+ }
package/lib/client.js CHANGED
@@ -86,6 +86,13 @@ window.__ModuleLoader__.load({
86
86
  "card.llmPresetChoose": "选择预置…",
87
87
  "card.llmPresetPublic": "公共 API 预置",
88
88
  "card.llmPresetHost": "DSH 模型组(选择后自动切换为会话模式)",
89
+ "card.networkEnabled": "网络拦截(networkEnabled)",
90
+ "card.networkEnabledHint": "开启后启动本地代理拦截子进程出站连接,按规则放行/阻断。关闭则零行为变更。",
91
+ "card.watch": "规则热重载(watch)",
92
+ "card.watchHint": "开启后自动监听规则文件变更并即时生效,无需手动 reload。",
93
+ "card.networkRebindNote": "切换后立即生效(自动重绑,无需重载插件)。",
94
+ "card.networkInjectEnv": "注入代理环境变量(networkInjectEnv)",
95
+ "card.networkInjectEnvHint": "开启后为子进程写入 HTTP(S)_PROXY / ALL_PROXY(并清空 NO_PROXY)。关闭则只启动监听、不改变任何进程环境。",
89
96
  "card.healthStale": "宿主路由不可用(404)——node 半区仍是旧版本,请完全重启 dsh(而非仅刷新页面)后重试。",
90
97
  "card.healthTest": "健康测试",
91
98
  "card.healthRunning": "测试中…",
@@ -195,6 +202,13 @@ window.__ModuleLoader__.load({
195
202
  "card.llmPresetChoose": "Pick a preset…",
196
203
  "card.llmPresetPublic": "Public API presets",
197
204
  "card.llmPresetHost": "DSH model groups (selecting one switches to the session receiver)",
205
+ "card.networkEnabled": "Network interception (networkEnabled)",
206
+ "card.networkEnabledHint": "When on, starts a local proxy that intercepts outbound subprocess connections and applies allow/deny rules. Off = zero behavior change.",
207
+ "card.watch": "Rule hot-reload (watch)",
208
+ "card.watchHint": "When on, automatically watches rule files for changes and applies them instantly without manual reload.",
209
+ "card.networkRebindNote": "Takes effect immediately — the proxy rebinds automatically, no plugin reload needed.",
210
+ "card.networkInjectEnv": "Inject proxy environment (networkInjectEnv)",
211
+ "card.networkInjectEnvHint": "Writes HTTP(S)_PROXY / ALL_PROXY for subprocesses (and clears NO_PROXY). Off = the listener runs but no process environment is changed.",
198
212
  "card.healthStale": "Host route unavailable (404) — the node half is still an old build; fully restart dsh (not just the browser page) and retry.",
199
213
  "card.healthTest": "Health test",
200
214
  "card.healthRunning": "Testing…",
@@ -1404,6 +1418,106 @@ window.__ModuleLoader__.load({
1404
1418
  }) : null
1405
1419
  ]
1406
1420
  }),
1421
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1422
+ style: sectionStyle,
1423
+ children: [
1424
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1425
+ style: labelStyle,
1426
+ children: t("card.networkEnabled")
1427
+ }),
1428
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1429
+ style: hintStyle,
1430
+ children: t("card.networkEnabledHint")
1431
+ }),
1432
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1433
+ style: {
1434
+ display: "flex",
1435
+ alignItems: "center",
1436
+ gap: "8px",
1437
+ marginTop: "6px"
1438
+ },
1439
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1440
+ type: "checkbox",
1441
+ checked: value.networkEnabled === true,
1442
+ disabled: readonly,
1443
+ onChange: (event) => {
1444
+ scope.set("networkEnabled", event.currentTarget.checked);
1445
+ }
1446
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1447
+ style: { fontSize: "13px" },
1448
+ children: value.networkEnabled === true ? "ON" : "OFF"
1449
+ })]
1450
+ }),
1451
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1452
+ style: hintStyle,
1453
+ children: t("card.networkRebindNote")
1454
+ })
1455
+ ]
1456
+ }),
1457
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1458
+ style: sectionStyle,
1459
+ children: [
1460
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1461
+ style: labelStyle,
1462
+ children: t("card.networkInjectEnv")
1463
+ }),
1464
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1465
+ style: hintStyle,
1466
+ children: t("card.networkInjectEnvHint")
1467
+ }),
1468
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1469
+ style: {
1470
+ display: "flex",
1471
+ alignItems: "center",
1472
+ gap: "8px",
1473
+ marginTop: "6px"
1474
+ },
1475
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1476
+ type: "checkbox",
1477
+ checked: value.networkInjectEnv !== false,
1478
+ disabled: readonly,
1479
+ onChange: (event) => {
1480
+ scope.set("networkInjectEnv", event.currentTarget.checked);
1481
+ }
1482
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1483
+ style: { fontSize: "13px" },
1484
+ children: value.networkInjectEnv !== false ? "ON" : "OFF"
1485
+ })]
1486
+ })
1487
+ ]
1488
+ }),
1489
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
1490
+ style: sectionStyle,
1491
+ children: [
1492
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1493
+ style: labelStyle,
1494
+ children: t("card.watch")
1495
+ }),
1496
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1497
+ style: hintStyle,
1498
+ children: t("card.watchHint")
1499
+ }),
1500
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1501
+ style: {
1502
+ display: "flex",
1503
+ alignItems: "center",
1504
+ gap: "8px",
1505
+ marginTop: "6px"
1506
+ },
1507
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1508
+ type: "checkbox",
1509
+ checked: value.watch !== false,
1510
+ disabled: readonly,
1511
+ onChange: (event) => {
1512
+ scope.set("watch", event.currentTarget.checked);
1513
+ }
1514
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1515
+ style: { fontSize: "13px" },
1516
+ children: value.watch !== false ? "ON" : "OFF"
1517
+ })]
1518
+ })
1519
+ ]
1520
+ }),
1407
1521
  !snapshot.writable && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1408
1522
  style: {
1409
1523
  margin: "8px 0 0",