@dsh-edge/dsh-edge-agent 0.0.0-stage → 0.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.
- package/CHANGELOG.md +158 -0
- package/LICENSE +21 -0
- package/README.md +181 -2
- package/config/edge.example.json +179 -0
- package/config/privacy-policy.json +202 -0
- package/cordis.desktop-safe.patch.yml +129 -0
- package/cordis.patch.yml +382 -0
- package/cordis.yml +374 -0
- package/lib/attestation.d.ts +2 -0
- package/lib/attestation.d.ts.map +1 -0
- package/lib/attestation.js +2 -0
- package/lib/audit-log.d.ts +2 -0
- package/lib/audit-log.d.ts.map +1 -0
- package/lib/audit-log.js +2 -0
- package/lib/client.js +87 -0
- package/lib/cloud-llm-adapter.d.ts +2 -0
- package/lib/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/cloud-llm-adapter.js +2 -0
- package/lib/dsh-adapter/attestation.d.ts +42 -0
- package/lib/dsh-adapter/attestation.d.ts.map +1 -0
- package/lib/dsh-adapter/attestation.js +40 -0
- package/lib/dsh-adapter/audit-log.d.ts +139 -0
- package/lib/dsh-adapter/audit-log.d.ts.map +1 -0
- package/lib/dsh-adapter/audit-log.js +376 -0
- package/lib/dsh-adapter/cloud-egress.d.ts +181 -0
- package/lib/dsh-adapter/cloud-egress.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-egress.js +257 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts +416 -0
- package/lib/dsh-adapter/cloud-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/cloud-llm-adapter.js +1472 -0
- package/lib/dsh-adapter/contract.d.ts +80 -0
- package/lib/dsh-adapter/contract.d.ts.map +1 -0
- package/lib/dsh-adapter/contract.js +194 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts +214 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.d.ts.map +1 -0
- package/lib/dsh-adapter/desktop-cookie-janitor.js +409 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts +48 -0
- package/lib/dsh-adapter/dsh-edge-agent.d.ts.map +1 -0
- package/lib/dsh-adapter/dsh-edge-agent.js +64 -0
- package/lib/dsh-adapter/edge-config.d.ts +484 -0
- package/lib/dsh-adapter/edge-config.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-config.js +898 -0
- package/lib/dsh-adapter/edge-context-view.d.ts +184 -0
- package/lib/dsh-adapter/edge-context-view.d.ts.map +1 -0
- package/lib/dsh-adapter/edge-context-view.js +483 -0
- package/lib/dsh-adapter/effective-model.d.ts +85 -0
- package/lib/dsh-adapter/effective-model.d.ts.map +1 -0
- package/lib/dsh-adapter/effective-model.js +171 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts +244 -0
- package/lib/dsh-adapter/etscli-shell-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-shell-guard.js +420 -0
- package/lib/dsh-adapter/etscli-tools.d.ts +112 -0
- package/lib/dsh-adapter/etscli-tools.d.ts.map +1 -0
- package/lib/dsh-adapter/etscli-tools.js +393 -0
- package/lib/dsh-adapter/file-logger.d.ts +294 -0
- package/lib/dsh-adapter/file-logger.d.ts.map +1 -0
- package/lib/dsh-adapter/file-logger.js +564 -0
- package/lib/dsh-adapter/function-call-external.d.ts +357 -0
- package/lib/dsh-adapter/function-call-external.d.ts.map +1 -0
- package/lib/dsh-adapter/function-call-external.js +1005 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts +161 -0
- package/lib/dsh-adapter/host-egress-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/host-egress-guard.js +213 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts +198 -0
- package/lib/dsh-adapter/host-llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/host-llm-trace.js +344 -0
- package/lib/dsh-adapter/http-api-routes.d.ts +61 -0
- package/lib/dsh-adapter/http-api-routes.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-routes.js +79 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts +105 -0
- package/lib/dsh-adapter/http-api-sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api-sessions.js +173 -0
- package/lib/dsh-adapter/http-api.d.ts +221 -0
- package/lib/dsh-adapter/http-api.d.ts.map +1 -0
- package/lib/dsh-adapter/http-api.js +1811 -0
- package/lib/dsh-adapter/index.d.ts +62 -0
- package/lib/dsh-adapter/index.d.ts.map +1 -0
- package/lib/dsh-adapter/index.js +97 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts +56 -0
- package/lib/dsh-adapter/input-guard-messages.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard-messages.js +136 -0
- package/lib/dsh-adapter/input-guard.d.ts +508 -0
- package/lib/dsh-adapter/input-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/input-guard.js +2174 -0
- package/lib/dsh-adapter/intent-prompts.d.ts +77 -0
- package/lib/dsh-adapter/intent-prompts.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-prompts.js +97 -0
- package/lib/dsh-adapter/intent-router.d.ts +710 -0
- package/lib/dsh-adapter/intent-router.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-router.js +2802 -0
- package/lib/dsh-adapter/intent-signals.d.ts +208 -0
- package/lib/dsh-adapter/intent-signals.d.ts.map +1 -0
- package/lib/dsh-adapter/intent-signals.js +391 -0
- package/lib/dsh-adapter/invariant-install.d.ts +16 -0
- package/lib/dsh-adapter/invariant-install.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant-install.js +15 -0
- package/lib/dsh-adapter/invariant.d.ts +16 -0
- package/lib/dsh-adapter/invariant.d.ts.map +1 -0
- package/lib/dsh-adapter/invariant.js +22 -0
- package/lib/dsh-adapter/llm-adapters.d.ts +19 -0
- package/lib/dsh-adapter/llm-adapters.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-adapters.js +16 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts +92 -0
- package/lib/dsh-adapter/llm-internal-caller.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-internal-caller.js +100 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts +55 -0
- package/lib/dsh-adapter/llm-max-turns.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-max-turns.js +127 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts +131 -0
- package/lib/dsh-adapter/llm-trace-store.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace-store.js +205 -0
- package/lib/dsh-adapter/llm-trace.d.ts +450 -0
- package/lib/dsh-adapter/llm-trace.d.ts.map +1 -0
- package/lib/dsh-adapter/llm-trace.js +463 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts +141 -0
- package/lib/dsh-adapter/local-llm-adapter.d.ts.map +1 -0
- package/lib/dsh-adapter/local-llm-adapter.js +787 -0
- package/lib/dsh-adapter/local-storage.d.ts +96 -0
- package/lib/dsh-adapter/local-storage.d.ts.map +1 -0
- package/lib/dsh-adapter/local-storage.js +198 -0
- package/lib/dsh-adapter/model-router.d.ts +79 -0
- package/lib/dsh-adapter/model-router.d.ts.map +1 -0
- package/lib/dsh-adapter/model-router.js +107 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts +49 -0
- package/lib/dsh-adapter/ohttp-gateway.d.ts.map +1 -0
- package/lib/dsh-adapter/ohttp-gateway.js +32 -0
- package/lib/dsh-adapter/openai-sse.d.ts +85 -0
- package/lib/dsh-adapter/openai-sse.d.ts.map +1 -0
- package/lib/dsh-adapter/openai-sse.js +462 -0
- package/lib/dsh-adapter/pii-utils.d.ts +96 -0
- package/lib/dsh-adapter/pii-utils.d.ts.map +1 -0
- package/lib/dsh-adapter/pii-utils.js +324 -0
- package/lib/dsh-adapter/plugin-host.d.ts +38 -0
- package/lib/dsh-adapter/plugin-host.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin-host.js +38 -0
- package/lib/dsh-adapter/plugin.d.ts +46 -0
- package/lib/dsh-adapter/plugin.d.ts.map +1 -0
- package/lib/dsh-adapter/plugin.js +100 -0
- package/lib/dsh-adapter/privacy-policy.d.ts +237 -0
- package/lib/dsh-adapter/privacy-policy.d.ts.map +1 -0
- package/lib/dsh-adapter/privacy-policy.js +391 -0
- package/lib/dsh-adapter/prompt-guard.d.ts +152 -0
- package/lib/dsh-adapter/prompt-guard.d.ts.map +1 -0
- package/lib/dsh-adapter/prompt-guard.js +872 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts +365 -0
- package/lib/dsh-adapter/realm-skill-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/realm-skill-bridge.js +1180 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts +140 -0
- package/lib/dsh-adapter/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/dsh-adapter/sanitizer-pipeline.js +431 -0
- package/lib/dsh-adapter/secure-bridge.d.ts +47 -0
- package/lib/dsh-adapter/secure-bridge.d.ts.map +1 -0
- package/lib/dsh-adapter/secure-bridge.js +35 -0
- package/lib/dsh-adapter/session-attribution.d.ts +70 -0
- package/lib/dsh-adapter/session-attribution.d.ts.map +1 -0
- package/lib/dsh-adapter/session-attribution.js +76 -0
- package/lib/dsh-adapter/sessions.d.ts +86 -0
- package/lib/dsh-adapter/sessions.d.ts.map +1 -0
- package/lib/dsh-adapter/sessions.js +179 -0
- package/lib/dsh-adapter/tcm.d.ts +55 -0
- package/lib/dsh-adapter/tcm.d.ts.map +1 -0
- package/lib/dsh-adapter/tcm.js +94 -0
- package/lib/dsh-adapter/td-bus.d.ts +87 -0
- package/lib/dsh-adapter/td-bus.d.ts.map +1 -0
- package/lib/dsh-adapter/td-bus.js +196 -0
- package/lib/dsh-adapter/tool-runner.d.ts +185 -0
- package/lib/dsh-adapter/tool-runner.d.ts.map +1 -0
- package/lib/dsh-adapter/tool-runner.js +271 -0
- package/lib/dsh-adapter/tools.d.ts +118 -0
- package/lib/dsh-adapter/tools.d.ts.map +1 -0
- package/lib/dsh-adapter/tools.js +292 -0
- package/lib/dsh-adapter/types.d.ts +23 -0
- package/lib/dsh-adapter/types.d.ts.map +1 -0
- package/lib/dsh-adapter/types.js +16 -0
- package/lib/dsh-adapter/voice-input.d.ts +30 -0
- package/lib/dsh-adapter/voice-input.d.ts.map +1 -0
- package/lib/dsh-adapter/voice-input.js +34 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts +152 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.d.ts.map +1 -0
- package/lib/dsh-adapter/web-search-tavily-bocha.js +289 -0
- package/lib/http-api.d.ts +2 -0
- package/lib/http-api.d.ts.map +1 -0
- package/lib/http-api.js +2 -0
- package/lib/index.d.ts +48 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +49 -0
- package/lib/intent-router.d.ts +2 -0
- package/lib/intent-router.d.ts.map +1 -0
- package/lib/intent-router.js +2 -0
- package/lib/llm-adapters.d.ts +2 -0
- package/lib/llm-adapters.d.ts.map +1 -0
- package/lib/llm-adapters.js +2 -0
- package/lib/local-llm-adapter.d.ts +2 -0
- package/lib/local-llm-adapter.d.ts.map +1 -0
- package/lib/local-llm-adapter.js +2 -0
- package/lib/local-storage.d.ts +2 -0
- package/lib/local-storage.d.ts.map +1 -0
- package/lib/local-storage.js +2 -0
- package/lib/model-router.d.ts +2 -0
- package/lib/model-router.d.ts.map +1 -0
- package/lib/model-router.js +2 -0
- package/lib/ohttp-gateway.d.ts +2 -0
- package/lib/ohttp-gateway.d.ts.map +1 -0
- package/lib/ohttp-gateway.js +2 -0
- package/lib/plugin-host.d.ts +2 -0
- package/lib/plugin-host.d.ts.map +1 -0
- package/lib/plugin-host.js +2 -0
- package/lib/privacy-policy.d.ts +2 -0
- package/lib/privacy-policy.d.ts.map +1 -0
- package/lib/privacy-policy.js +2 -0
- package/lib/prompt-guard.d.ts +2 -0
- package/lib/prompt-guard.d.ts.map +1 -0
- package/lib/prompt-guard.js +2 -0
- package/lib/sanitizer-pipeline.d.ts +2 -0
- package/lib/sanitizer-pipeline.d.ts.map +1 -0
- package/lib/sanitizer-pipeline.js +2 -0
- package/lib/secure-bridge.d.ts +2 -0
- package/lib/secure-bridge.d.ts.map +1 -0
- package/lib/secure-bridge.js +2 -0
- package/lib/sessions.d.ts +2 -0
- package/lib/sessions.d.ts.map +1 -0
- package/lib/sessions.js +2 -0
- package/lib/tcm.d.ts +2 -0
- package/lib/tcm.d.ts.map +1 -0
- package/lib/tcm.js +2 -0
- package/lib/td-bus.d.ts +2 -0
- package/lib/td-bus.d.ts.map +1 -0
- package/lib/td-bus.js +2 -0
- package/lib/tools.d.ts +2 -0
- package/lib/tools.d.ts.map +1 -0
- package/lib/tools.js +2 -0
- package/lib/voice-input.d.ts +2 -0
- package/lib/voice-input.d.ts.map +1 -0
- package/lib/voice-input.js +2 -0
- package/package.json +253 -3
- package/presets/agent-preset-edge.json +50 -0
- package/presets/edge.desktop-safe.patch.yml +503 -0
- package/presets/edge.patch.yml +557 -0
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* pii-utils — 共享的 PII 检测与脱敏工具
|
|
3
|
+
*
|
|
4
|
+
* 本模块是**全项目 PII 正则的唯一事实源**(`PII_PATTERNS`)。
|
|
5
|
+
* 消费方:
|
|
6
|
+
* - `privacy-policy.ts` 的 `DEFAULT_POLICY.sanitizationRules`(直接复用
|
|
7
|
+
* `PII_PATTERNS`,不再各写一份字面量)
|
|
8
|
+
* - `sanitizer-pipeline.ts` 经由 `privacyPolicy.getSanitizationRules()`
|
|
9
|
+
* 消费 pattern 字符串做**可逆**脱敏
|
|
10
|
+
* - `prompt-guard` / `audit-log` / `local-storage` 经由本模块的
|
|
11
|
+
* `redactString` / `redactObject` 做**不可逆**掩码
|
|
12
|
+
*
|
|
13
|
+
* 两条链路共用同一批 pattern,只有替换语义不同(占位符 vs 掩码),
|
|
14
|
+
* 因此 pattern 必须集中定义,禁止在别处重复书写。
|
|
15
|
+
*
|
|
16
|
+
* @module dsh-edge-agent/pii-utils
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* PII 正则字面量(唯一事实源)。
|
|
20
|
+
*
|
|
21
|
+
* 以 `RegExp` source 语法书写(不含分隔符与 flags),便于同时用于
|
|
22
|
+
* `new RegExp(pattern, 'gi')`(脱敏管线)与本模块的 `RegExp` 实例。
|
|
23
|
+
*
|
|
24
|
+
* 规则顺序见 `PII_ORDER` —— 顺序是语义的一部分,长规则必须先跑。
|
|
25
|
+
*/
|
|
26
|
+
export const PII_PATTERNS = {
|
|
27
|
+
// 严格身份证:省份码 + 19xx/20xx + 合法月日 + 顺序码 + 校验位。
|
|
28
|
+
// `\b` 边界防止在 16-19 位银行卡号内部命中 18 位子串。
|
|
29
|
+
'id-card': '\\b[1-9]\\d{5}(?:19|20)\\d{2}(?:0[1-9]|1[0-2])(?:0[1-9]|[12]\\d|3[01])\\d{3}[\\dXx]\\b',
|
|
30
|
+
'bank-card': '\\b\\d{16,19}\\b',
|
|
31
|
+
// `\b` 边界防止在更长数字串内部误命中 11 位子串
|
|
32
|
+
phone: '\\b1[3-9]\\d{9}\\b',
|
|
33
|
+
email: '[\\w.-]+@[\\w.-]+\\.\\w+',
|
|
34
|
+
'api-key': '(?:sk-(?:proj-|ant-|live-|test-|org-)?[a-zA-Z0-9]{20,}|key-[a-zA-Z0-9]{16,}|api[_-]?key[_-]?[a-zA-Z0-9]{16,})',
|
|
35
|
+
// §7.106 (2026-10-23) credential:标签式凭据(密码/口令/用户名/账号/token/
|
|
36
|
+
// secret… + 分隔符 + 值)。LOCAL 轮放行明文口令操作(设备登录,操作者裁决)
|
|
37
|
+
// 后,这些值会沉淀进会话历史——后续 CLOUD 轮整段历史回显出网,必须由可逆
|
|
38
|
+
// 脱敏兜住。用户名/账号同掩:与口令构成可利用的配对,单独出网没有功能价值。
|
|
39
|
+
// 值段排除空白与常见收尾符,长度 3-64;宁可多掩不可漏放。
|
|
40
|
+
'credential': '(?:密码|口令|用户名|账号|账户|passwd|password|pwd|username|account|secret|access[ _-]?token|token|api[ _-]?key)["\' ]{0,2}(?:[::=]|是|为|\\s+is\\s+)["\' ]?[^\\s"\',,;;<>()()]{3,64}',
|
|
41
|
+
// 合法八位组(0-255)+ 上下文排除:
|
|
42
|
+
// `(?<![vV\d.])` 前面不是 v/V 或数字/点 → 排除 `v1.2.3.4` 版本号,
|
|
43
|
+
// 以及 `1.2.3.4.5` 这类更长点分串的中段
|
|
44
|
+
// `(?!\.\d)` 后面不再接 `.数字` → 排除五段以上的版本号
|
|
45
|
+
// 注意:裸写的四段版本号(如 `1.0.0.1`)仍会被当作 IP——它本身就是
|
|
46
|
+
// 合法且在用的公网地址,宁可误伤也不漏报。
|
|
47
|
+
'ip-address': '(?<![vV\\d.])(?:(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)\\.){3}(?:25[0-5]|2[0-4]\\d|1\\d{2}|[1-9]?\\d)(?!\\.?\\d)',
|
|
48
|
+
'mac-address': '(?:[0-9A-Fa-f]{2}[:-]){5}[0-9A-Fa-f]{2}',
|
|
49
|
+
passport: '\\b(?:E[0-9A-Z]\\d{7}|G\\d{8})\\b',
|
|
50
|
+
'license-plate': '[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤川青藏琼][A-Z][A-HJ-NP-Z0-9]{5}',
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* 规则求值顺序。长规则优先(id-card → bank-card → phone),防止短规则
|
|
54
|
+
* 在更长的数字串内部误命中。
|
|
55
|
+
*
|
|
56
|
+
* 注意:顺序生效的前提是 `redactString` / `sanitize` 采用**累进匹配**
|
|
57
|
+
* (每条规则在上一条的产出上继续匹配),否则顺序没有意义。
|
|
58
|
+
*/
|
|
59
|
+
export const PII_ORDER = [
|
|
60
|
+
'id-card',
|
|
61
|
+
'bank-card',
|
|
62
|
+
'phone',
|
|
63
|
+
'email',
|
|
64
|
+
'api-key',
|
|
65
|
+
'credential',
|
|
66
|
+
'ip-address',
|
|
67
|
+
'mac-address',
|
|
68
|
+
'passport',
|
|
69
|
+
'license-plate',
|
|
70
|
+
];
|
|
71
|
+
/** 各 PII 类型的数据分级,与 privacy-policy 的 dataClassifications 对应。 */
|
|
72
|
+
export const PII_DATA_CLASS = {
|
|
73
|
+
'id-card': 'secret',
|
|
74
|
+
'bank-card': 'secret',
|
|
75
|
+
'api-key': 'secret',
|
|
76
|
+
'credential': 'secret',
|
|
77
|
+
passport: 'secret',
|
|
78
|
+
phone: 'confidential',
|
|
79
|
+
email: 'confidential',
|
|
80
|
+
'ip-address': 'confidential',
|
|
81
|
+
'mac-address': 'confidential',
|
|
82
|
+
'license-plate': 'confidential',
|
|
83
|
+
};
|
|
84
|
+
/** 不可逆掩码函数(仅用于日志/存储,不参与上云脱敏)。 */
|
|
85
|
+
export const PII_MASKS = {
|
|
86
|
+
'id-card': (m) => `${m.slice(0, 3)}***********${m.slice(14)}`,
|
|
87
|
+
'bank-card': (m) => `${m.slice(0, 4)}**********${m.slice(-4)}`,
|
|
88
|
+
phone: (m) => `${m.slice(0, 3)}****${m.slice(7)}`,
|
|
89
|
+
email: (m) => {
|
|
90
|
+
const [local, domain] = m.split('@');
|
|
91
|
+
return `${local[0]}***@${domain}`;
|
|
92
|
+
},
|
|
93
|
+
'api-key': () => 'sk-***',
|
|
94
|
+
// 掩码保留标签只糊值段:日志能看出"这里有个凭据",看不出凭据本身。
|
|
95
|
+
'credential': (m) => {
|
|
96
|
+
const i = m.search(/[::=是为]/u);
|
|
97
|
+
return i > 0 ? `${m.slice(0, i)}***` : '***';
|
|
98
|
+
},
|
|
99
|
+
'ip-address': (m) => {
|
|
100
|
+
const parts = m.split('.');
|
|
101
|
+
return `${parts[0]}.${parts[1]}.***.***`;
|
|
102
|
+
},
|
|
103
|
+
'mac-address': (m) => {
|
|
104
|
+
const parts = m.split(/[:-]/);
|
|
105
|
+
return `${parts[0]}:${parts[1]}:**:**:**:**`;
|
|
106
|
+
},
|
|
107
|
+
passport: (m) => `${m[0]}****${m.slice(-2)}`,
|
|
108
|
+
'license-plate': (m) => `${m[0]}*·*****`,
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* 10 种 PII 规则,由 `PII_PATTERNS` + `PII_MASKS` 按 `PII_ORDER` 组装。
|
|
112
|
+
* 与 privacy-policy 的 sanitizationRules 共用同一批 pattern 字面量。
|
|
113
|
+
*/
|
|
114
|
+
export const PII_RULES = PII_ORDER.map((name) => ({
|
|
115
|
+
name,
|
|
116
|
+
pattern: new RegExp(PII_PATTERNS[name], 'g'),
|
|
117
|
+
mask: PII_MASKS[name],
|
|
118
|
+
}));
|
|
119
|
+
/**
|
|
120
|
+
* 对单个字符串应用所有 PII 规则,返回脱敏后的字符串和脱敏明细。
|
|
121
|
+
*
|
|
122
|
+
* 累进匹配:每条规则在**上一条规则的产出**(`result`)上继续匹配,而不是
|
|
123
|
+
* 回到原始 `text`。否则后序规则会命中前序规则已经掩码掉的区间,导致
|
|
124
|
+
* `result.replace` 静默 no-op、而 `matches` 里却记了一笔不存在的脱敏
|
|
125
|
+
* (2026-08-30 修复)。
|
|
126
|
+
*
|
|
127
|
+
* 同一取值在文本中出现多次时一次性全部替换,且只计一笔,避免计数虚高。
|
|
128
|
+
*/
|
|
129
|
+
export function redactString(text, rules = PII_RULES) {
|
|
130
|
+
let result = text;
|
|
131
|
+
const matches = [];
|
|
132
|
+
for (const rule of rules) {
|
|
133
|
+
// 克隆正则,避免共享实例的 lastIndex 状态在并发调用间串扰
|
|
134
|
+
const pattern = new RegExp(rule.pattern.source, rule.pattern.flags);
|
|
135
|
+
const hits = result.match(pattern);
|
|
136
|
+
if (!hits)
|
|
137
|
+
continue;
|
|
138
|
+
// 去重:同一取值只处理一次(split/join 已经全量替换)
|
|
139
|
+
for (const hit of new Set(hits)) {
|
|
140
|
+
let masked;
|
|
141
|
+
try {
|
|
142
|
+
masked = rule.mask(hit);
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
continue; // 掩码函数对畸形输入抛错时跳过,绝不让原文漏出
|
|
146
|
+
}
|
|
147
|
+
if (masked === hit)
|
|
148
|
+
continue;
|
|
149
|
+
result = result.split(hit).join(masked);
|
|
150
|
+
matches.push({ type: rule.name, masked });
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return { redacted: result, matches };
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* 深度遍历对象,对所有字符串值应用 PII 脱敏。
|
|
157
|
+
* 返回新对象(不修改原对象)。
|
|
158
|
+
*/
|
|
159
|
+
export function redactObject(obj, rules = PII_RULES) {
|
|
160
|
+
let count = 0;
|
|
161
|
+
const walk = (value) => {
|
|
162
|
+
if (typeof value === 'string') {
|
|
163
|
+
const { redacted, matches } = redactString(value, rules);
|
|
164
|
+
count += matches.length;
|
|
165
|
+
return redacted;
|
|
166
|
+
}
|
|
167
|
+
if (Array.isArray(value)) {
|
|
168
|
+
return value.map((item) => walk(item));
|
|
169
|
+
}
|
|
170
|
+
if (value !== null && typeof value === 'object') {
|
|
171
|
+
const out = {};
|
|
172
|
+
for (const [k, v] of Object.entries(value)) {
|
|
173
|
+
out[k] = walk(v);
|
|
174
|
+
}
|
|
175
|
+
return out;
|
|
176
|
+
}
|
|
177
|
+
return value;
|
|
178
|
+
};
|
|
179
|
+
return { redacted: walk(obj), count };
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* §7.87.13 (2026-09-20) PII 预检测前的文本规范化。
|
|
183
|
+
*
|
|
184
|
+
* PII 正则(`\b1[3-9]\d{9}\b` / `\d{17}[\dXx]` 等)都是**字面量字符匹配**,
|
|
185
|
+
* 对以下两种用户写法直接漏抓:
|
|
186
|
+
*
|
|
187
|
+
* - **空格分隔**: `我的手机号是 1 3 8 0 0 1 3 8 0 0 0`
|
|
188
|
+
* → phone regex 11 位数字必须连续,空格断开会漏。
|
|
189
|
+
* - **中文数字编码**: `身份证号 一一零一零一一九九零零三零七八八一一`
|
|
190
|
+
* → id-card regex 匹配 `\d{17}` 不抓 `一`,完全漏。
|
|
191
|
+
*
|
|
192
|
+
* 实测(§7.87.12 4 模型 trace):4 个本地 LLM 在这两个 case 上的失败率
|
|
193
|
+
* 100%(27B / 4B / 9B 都漏),**唯一防线**是 sanitizer 预处理。本函数
|
|
194
|
+
* 是这条防线的核心。
|
|
195
|
+
*
|
|
196
|
+
* ## 字符级 1:1 映射
|
|
197
|
+
*
|
|
198
|
+
* 返回 `charMap[i] = text 中第 i 个规范化字符对应的源位置`,配合 sanitizer
|
|
199
|
+
* 把 regex 在 normalized 上的命中区间 `[start, end)` 反查到原始文本上:
|
|
200
|
+
*
|
|
201
|
+
* ```
|
|
202
|
+
* text = "手机号 1 3 8 0 0 1 3 8 0 0 0"
|
|
203
|
+
* normalized = "手机号13800138000" ← 仅 drop digit 之间的 10 个空格
|
|
204
|
+
* charMap = [0,1,2, 3, 6,8,10,12,14,16,18,20,22,24]
|
|
205
|
+
*
|
|
206
|
+
* regex 在 normalized 上命中 "13800138000" → [4, 15)
|
|
207
|
+
* charMap[4]=6, charMap[14]=24 → text.slice(6, 25) = "1 3 8 0 0 1 3 8 0 0 0"
|
|
208
|
+
* ```
|
|
209
|
+
*
|
|
210
|
+
* `byOriginal` 用"用户原始字符串"做 key,desanitize 后能精确还原用户
|
|
211
|
+
* 输入的字面形式(带空格 / 中文数字),而不是规范化后的形式。
|
|
212
|
+
*
|
|
213
|
+
* ## 字符级规范化规则
|
|
214
|
+
*
|
|
215
|
+
* - **空白字符**(`,` `\s`):**仅当两侧相邻字符都是 ASCII digit 时丢弃** —
|
|
216
|
+
* 不能简单全 drop:否则 `IP 10.0.0.1` → `IP10.0.0.1`,IP regex 的 `\b`
|
|
217
|
+
* 在 'P' 与 '1' 之间无 word boundary,漏检。同理 email regex 在
|
|
218
|
+
* `a @ b . com` 间没有 space 时也能匹配,但保留 space 不破坏既有
|
|
219
|
+
* 检测。**安全**:即使过度保留 space,正则仍按设计工作,最多漏掉
|
|
220
|
+
* "digit 间空格"的规范化收益。
|
|
221
|
+
* - **中文数字 → 阿拉伯数字**:1:1 映射,包括:
|
|
222
|
+
* - 大写(财务/合同):壹贰叁肆伍陆柒捌玖
|
|
223
|
+
* - 〇 (圆圈 0):见 ○ 形式零
|
|
224
|
+
* - 两 (口语化 2):常见于电话/身份证
|
|
225
|
+
* - **其他字符**:原样保留
|
|
226
|
+
*
|
|
227
|
+
* ## 已知限制(可接受)
|
|
228
|
+
*
|
|
229
|
+
* - **位值中文数字**(`百`/`千`/`万`/`亿`)不被处理 — 需要完整数字
|
|
230
|
+
* parsing,而我们的 fixture 都是单字数字("一一零...")。如果未来
|
|
231
|
+
* fixture 出现"一百三十八",需要扩展 parseIntChinese 之类。
|
|
232
|
+
* - **规范化是纯字符串变换**,不引入语义理解。"我买了一个东西"
|
|
233
|
+
* 不会因"一"被改而漏报 — 因为规范化后"我买了1个东西"仍不含
|
|
234
|
+
* 11/17-18 位连续数字,regex 不会误报。保守 over-mask 是安全的。
|
|
235
|
+
*/
|
|
236
|
+
const CN_DIGIT_MAP = {
|
|
237
|
+
零: '0', 〇: '0',
|
|
238
|
+
一: '1', 壹: '1',
|
|
239
|
+
二: '2', 贰: '2', 两: '2',
|
|
240
|
+
三: '3', 叁: '3',
|
|
241
|
+
四: '4', 肆: '4',
|
|
242
|
+
五: '5', 伍: '5',
|
|
243
|
+
六: '6', 陆: '6',
|
|
244
|
+
七: '7', 柒: '7',
|
|
245
|
+
八: '8', 捌: '8',
|
|
246
|
+
九: '9', 玖: '9',
|
|
247
|
+
};
|
|
248
|
+
export function normalizeForPII(text) {
|
|
249
|
+
// 第一遍:把 CN 数字转成 ASCII 数字(`converted` 数组存"该位置的字符
|
|
250
|
+
// 转换后形态"),后续的空格判断用 `converted` 而不是原始 `text` —
|
|
251
|
+
// 否则空格两侧是"非 ASCII digit 但 CN digit"的话判断会错。
|
|
252
|
+
const converted = new Array(text.length);
|
|
253
|
+
for (let i = 0; i < text.length; i++) {
|
|
254
|
+
const ch = text[i];
|
|
255
|
+
const mapped = CN_DIGIT_MAP[ch];
|
|
256
|
+
converted[i] = mapped !== undefined ? mapped : ch;
|
|
257
|
+
}
|
|
258
|
+
// 第二遍:遍历 converted,对空白决定是丢弃还是保留
|
|
259
|
+
let normalized = '';
|
|
260
|
+
const charMap = [];
|
|
261
|
+
let changed = false;
|
|
262
|
+
for (let i = 0; i < converted.length; i++) {
|
|
263
|
+
const ch = converted[i];
|
|
264
|
+
if (/\s/u.test(ch)) {
|
|
265
|
+
// 仅当两侧相邻(在 converted 数组里)非空白字符都是 ASCII digit 时丢弃
|
|
266
|
+
let prevIsDigit = false;
|
|
267
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
268
|
+
if (!/\s/u.test(converted[j])) {
|
|
269
|
+
prevIsDigit = /\d/u.test(converted[j]);
|
|
270
|
+
break;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
let nextIsDigit = false;
|
|
274
|
+
for (let j = i + 1; j < converted.length; j++) {
|
|
275
|
+
if (!/\s/u.test(converted[j])) {
|
|
276
|
+
nextIsDigit = /\d/u.test(converted[j]);
|
|
277
|
+
break;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
if (prevIsDigit && nextIsDigit) {
|
|
281
|
+
// digit 之间的空白,丢掉以让 PII 正则能连续匹配
|
|
282
|
+
changed = true;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
// 非 digit 之间的空白 → 保留(保护 IP/email 的 word boundary)
|
|
286
|
+
}
|
|
287
|
+
else if (CN_DIGIT_MAP[text[i]] !== undefined) {
|
|
288
|
+
// CN → ASCII 转换
|
|
289
|
+
changed = true;
|
|
290
|
+
}
|
|
291
|
+
normalized += ch;
|
|
292
|
+
charMap.push(i);
|
|
293
|
+
}
|
|
294
|
+
return { normalized, charMap, changed };
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* 把 `normalizeForPII()` 返回的 charMap + normalized 上的命中区间
|
|
298
|
+
* `[start, end)` 翻回 `text` 上的对应区间。
|
|
299
|
+
*
|
|
300
|
+
* 用法:`sliceOriginal(text, charMap, start, end)` → 用户原始子串
|
|
301
|
+
* (含被丢弃的空白、被转换的中文数字)。
|
|
302
|
+
*
|
|
303
|
+
* 注意:返回字符串的字符数可能 > (end - start),因为 whitespace
|
|
304
|
+
* 不在 charMap 里但仍在原文本里。
|
|
305
|
+
*/
|
|
306
|
+
export function sliceOriginal(text, charMap, start, end) {
|
|
307
|
+
if (start < 0 || end > charMap.length || start >= end) {
|
|
308
|
+
throw new Error(`sliceOriginal: invalid range [${start}, ${end}) for charMap.length=${charMap.length}`);
|
|
309
|
+
}
|
|
310
|
+
const fromIdx = charMap[start];
|
|
311
|
+
const toIdx = charMap[end - 1] + 1;
|
|
312
|
+
return text.slice(fromIdx, toIdx);
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* §7.106 (2026-10-23): does the text carry a labeled credential
|
|
316
|
+
* (`密码:X`, `token = X`, …)? input-guard's sticky-LOCAL rule uses it: a
|
|
317
|
+
* credential handoff continues a device-operation context locally — the
|
|
318
|
+
* cloud route is a functional dead end there (the model could only ever
|
|
319
|
+
* echo the placeholder back). Non-global RegExp ⇒ stateless `.test`.
|
|
320
|
+
*/
|
|
321
|
+
const CREDENTIAL_PROBE = new RegExp(PII_PATTERNS.credential, 'iu');
|
|
322
|
+
export function containsCredential(text) {
|
|
323
|
+
return CREDENTIAL_PROBE.test(text);
|
|
324
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin-host — coordination anchor for third-party plugins. Holds a
|
|
3
|
+
* registry of approved plugins, their grants, and the host descriptor
|
|
4
|
+
* that downstream plugins can self-introspect.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-edge-agent/plugin-host
|
|
7
|
+
*/
|
|
8
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
9
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
10
|
+
export declare const name = "plugin-host";
|
|
11
|
+
export declare const inject: readonly ["agents", "sessions"];
|
|
12
|
+
export interface Config {
|
|
13
|
+
enableThirdParty: boolean;
|
|
14
|
+
grantStorePath: string;
|
|
15
|
+
}
|
|
16
|
+
export declare const Config: Schema<Config>;
|
|
17
|
+
export interface PluginDescriptor {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
version: string;
|
|
21
|
+
grants: string[];
|
|
22
|
+
installedAt: number;
|
|
23
|
+
}
|
|
24
|
+
export interface PluginHostService {
|
|
25
|
+
registerDescriptor(desc: PluginDescriptor): void;
|
|
26
|
+
list(): PluginDescriptor[];
|
|
27
|
+
get(id: string): PluginDescriptor | null;
|
|
28
|
+
revoke(id: string): void;
|
|
29
|
+
}
|
|
30
|
+
export declare class PluginHostServiceImpl extends Service {
|
|
31
|
+
constructor(ctx: any);
|
|
32
|
+
registerDescriptor(desc: PluginDescriptor): void;
|
|
33
|
+
list(): PluginDescriptor[];
|
|
34
|
+
get(id: string): PluginDescriptor | null;
|
|
35
|
+
revoke(id: string): void;
|
|
36
|
+
}
|
|
37
|
+
export declare function apply(ctx: any, config: Config): Promise<() => Promise<void>>;
|
|
38
|
+
//# sourceMappingURL=plugin-host.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/plugin-host.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAE7C,eAAO,MAAM,IAAI,gBAAgB,CAAA;AAEjC,eAAO,MAAM,MAAM,iCAAkC,CAAA;AAErD,MAAM,WAAW,MAAM;IACrB,gBAAgB,EAAE,OAAO,CAAA;IACzB,cAAc,EAAE,MAAM,CAAA;CACvB;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CAGhC,CAAA;AAEF,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,MAAM,EAAE,MAAM,EAAE,CAAA;IAChB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI,CAAA;IAChD,IAAI,IAAI,gBAAgB,EAAE,CAAA;IAC1B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI,CAAA;IACxC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,qBAAa,qBAAsB,SAAQ,OAAO;gBACpC,GAAG,EAAE,GAAG;IAGpB,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,GAAG,IAAI;IAGhD,IAAI,IAAI,gBAAgB,EAAE;IAG1B,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,gBAAgB,GAAG,IAAI;IAIxC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;CAGzB;AAED,wBAAsB,KAAK,CAAC,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAIlF"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* plugin-host — coordination anchor for third-party plugins. Holds a
|
|
3
|
+
* registry of approved plugins, their grants, and the host descriptor
|
|
4
|
+
* that downstream plugins can self-introspect.
|
|
5
|
+
*
|
|
6
|
+
* @module dsh-edge-agent/plugin-host
|
|
7
|
+
*/
|
|
8
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
9
|
+
import Schema from '@deepseek-ai/schemastery';
|
|
10
|
+
export const name = 'plugin-host';
|
|
11
|
+
export const inject = ['agents', 'sessions'];
|
|
12
|
+
export const Config = Schema.object({
|
|
13
|
+
enableThirdParty: Schema.boolean().default(false),
|
|
14
|
+
grantStorePath: Schema.string().default('~/.dsh-edge-agent/grants.json'),
|
|
15
|
+
});
|
|
16
|
+
export class PluginHostServiceImpl extends Service {
|
|
17
|
+
constructor(ctx) {
|
|
18
|
+
super(ctx, 'pluginHost');
|
|
19
|
+
}
|
|
20
|
+
registerDescriptor(desc) {
|
|
21
|
+
void desc;
|
|
22
|
+
}
|
|
23
|
+
list() {
|
|
24
|
+
return [];
|
|
25
|
+
}
|
|
26
|
+
get(id) {
|
|
27
|
+
void id;
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
revoke(id) {
|
|
31
|
+
void id;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function apply(ctx, config) {
|
|
35
|
+
ctx.plugin(PluginHostServiceImpl);
|
|
36
|
+
void config;
|
|
37
|
+
return async () => { };
|
|
38
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main plugin runtime — orchestrates the privacy stack in dependency order.
|
|
3
|
+
*
|
|
4
|
+
* Boot order (topological):
|
|
5
|
+
* 1. tcm (no deps)
|
|
6
|
+
* 2. local-storage (depends on tcm)
|
|
7
|
+
* 3. audit-log (depends on tcm, local-storage)
|
|
8
|
+
* 4. sessions (depends on tcm, local-storage)
|
|
9
|
+
* 5. privacy-policy(depends on tcm, local-storage, audit-log)
|
|
10
|
+
* 6. tools (depends on audit-log)
|
|
11
|
+
* 7. prompt-guard (depends on tcm, privacy-policy, audit-log)
|
|
12
|
+
* 8. ohttp-gateway (depends on tcm, audit-log)
|
|
13
|
+
* 9. secure-bridge (depends on tcm, ohttp-gateway, privacy-policy, audit-log)
|
|
14
|
+
* 10. attestation (depends on tcm, audit-log)
|
|
15
|
+
* 11. local-llm-adapter (registers on ctx.llm)
|
|
16
|
+
* 12. cloud-llm-adapter (registers on ctx.llm, depends on secure-bridge)
|
|
17
|
+
* 13. model-router (depends on llm, privacy-policy, audit-log)
|
|
18
|
+
* 14. td-bus (depends on audit-log, privacy-policy)
|
|
19
|
+
* 15. intent-router (depends on llm, prompt-guard, privacy-policy, sessions)
|
|
20
|
+
* 16. sanitizer-pipeline (depends on local-storage, privacy-policy)
|
|
21
|
+
* 17. voice-input (depends on sessions, audit-log)
|
|
22
|
+
* 18. plugin-host (depends on agents, sessions)
|
|
23
|
+
* 19. http-api (depends on agents, sessions, audit-log)
|
|
24
|
+
*
|
|
25
|
+
* Note: local-tools and web-search have been removed. Use DSH native tools:
|
|
26
|
+
* - @deepseek-ai/dsh-tool-bash (bash execution)
|
|
27
|
+
* - @deepseek-ai/dsh-tool-fs (file read/write/edit)
|
|
28
|
+
* - @deepseek-ai/dsh-tool-web (web_search + web_fetch)
|
|
29
|
+
* - @deepseek-ai/dsh-web-search-deepseek (DeepSeek search provider)
|
|
30
|
+
* - @dsh-edge/dsh-edge-agent/web-search-tavily-bocha
|
|
31
|
+
* (§7.82 Phase 5: 3-tier Tavily → 博查 → DeepSeek fallback, replaces
|
|
32
|
+
* upstream DeepSeek provider as the default `searchProvider`)
|
|
33
|
+
*
|
|
34
|
+
* Each plugin's own `apply` declares its `inject` dependencies; the DSH
|
|
35
|
+
* Loader activates them in topological order automatically. This file
|
|
36
|
+
* exists only to host the wiring for the MAIN entry that orchestrates
|
|
37
|
+
* initial setup and registers cross-cutting listeners.
|
|
38
|
+
*
|
|
39
|
+
* In v0.1.0 (scaffold), this is a thin delegator: each sub-plugin's
|
|
40
|
+
* apply is invoked by the Loader directly via its own cordis.yml row.
|
|
41
|
+
* Future versions may add inter-plugin event listeners here.
|
|
42
|
+
*/
|
|
43
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
44
|
+
import type { Config } from './index.js';
|
|
45
|
+
export declare function apply(ctx: Context, config: Config): Promise<() => Promise<void>>;
|
|
46
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAExC,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAyDtF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main plugin runtime — orchestrates the privacy stack in dependency order.
|
|
3
|
+
*
|
|
4
|
+
* Boot order (topological):
|
|
5
|
+
* 1. tcm (no deps)
|
|
6
|
+
* 2. local-storage (depends on tcm)
|
|
7
|
+
* 3. audit-log (depends on tcm, local-storage)
|
|
8
|
+
* 4. sessions (depends on tcm, local-storage)
|
|
9
|
+
* 5. privacy-policy(depends on tcm, local-storage, audit-log)
|
|
10
|
+
* 6. tools (depends on audit-log)
|
|
11
|
+
* 7. prompt-guard (depends on tcm, privacy-policy, audit-log)
|
|
12
|
+
* 8. ohttp-gateway (depends on tcm, audit-log)
|
|
13
|
+
* 9. secure-bridge (depends on tcm, ohttp-gateway, privacy-policy, audit-log)
|
|
14
|
+
* 10. attestation (depends on tcm, audit-log)
|
|
15
|
+
* 11. local-llm-adapter (registers on ctx.llm)
|
|
16
|
+
* 12. cloud-llm-adapter (registers on ctx.llm, depends on secure-bridge)
|
|
17
|
+
* 13. model-router (depends on llm, privacy-policy, audit-log)
|
|
18
|
+
* 14. td-bus (depends on audit-log, privacy-policy)
|
|
19
|
+
* 15. intent-router (depends on llm, prompt-guard, privacy-policy, sessions)
|
|
20
|
+
* 16. sanitizer-pipeline (depends on local-storage, privacy-policy)
|
|
21
|
+
* 17. voice-input (depends on sessions, audit-log)
|
|
22
|
+
* 18. plugin-host (depends on agents, sessions)
|
|
23
|
+
* 19. http-api (depends on agents, sessions, audit-log)
|
|
24
|
+
*
|
|
25
|
+
* Note: local-tools and web-search have been removed. Use DSH native tools:
|
|
26
|
+
* - @deepseek-ai/dsh-tool-bash (bash execution)
|
|
27
|
+
* - @deepseek-ai/dsh-tool-fs (file read/write/edit)
|
|
28
|
+
* - @deepseek-ai/dsh-tool-web (web_search + web_fetch)
|
|
29
|
+
* - @deepseek-ai/dsh-web-search-deepseek (DeepSeek search provider)
|
|
30
|
+
* - @dsh-edge/dsh-edge-agent/web-search-tavily-bocha
|
|
31
|
+
* (§7.82 Phase 5: 3-tier Tavily → 博查 → DeepSeek fallback, replaces
|
|
32
|
+
* upstream DeepSeek provider as the default `searchProvider`)
|
|
33
|
+
*
|
|
34
|
+
* Each plugin's own `apply` declares its `inject` dependencies; the DSH
|
|
35
|
+
* Loader activates them in topological order automatically. This file
|
|
36
|
+
* exists only to host the wiring for the MAIN entry that orchestrates
|
|
37
|
+
* initial setup and registers cross-cutting listeners.
|
|
38
|
+
*
|
|
39
|
+
* In v0.1.0 (scaffold), this is a thin delegator: each sub-plugin's
|
|
40
|
+
* apply is invoked by the Loader directly via its own cordis.yml row.
|
|
41
|
+
* Future versions may add inter-plugin event listeners here.
|
|
42
|
+
*/
|
|
43
|
+
export async function apply(ctx, config) {
|
|
44
|
+
// The main plugin does not own state — it only:
|
|
45
|
+
// 1. emits a single 'edge-agent/boot' event so dependent plugins
|
|
46
|
+
// can finalize their setup without circular imports.
|
|
47
|
+
// 2. installs a soft shutdown hook that flushes pending audit logs
|
|
48
|
+
// and closes the encrypted storage handle.
|
|
49
|
+
//
|
|
50
|
+
// All actual work is performed by the sub-plugins; this main entry
|
|
51
|
+
// exists only to give the package a single named surface that
|
|
52
|
+
// `cordis.yml` can mount.
|
|
53
|
+
// 2026-10-20 desktop fix: the host-half plugin declares `inject: []`,
|
|
54
|
+
// so the cordis proxy throws `cannot get property "event" without
|
|
55
|
+
// inject` when we touch ctx.event. The event has no subscribers (it
|
|
56
|
+
// was scaffold-era signalling for cross-plugin setup that ended up
|
|
57
|
+
// living in each plugin's own apply). Best-effort only: if the host
|
|
58
|
+
// ever grows an `event` accessor the emit still fires; otherwise we
|
|
59
|
+
// log and move on instead of killing the plugin mount.
|
|
60
|
+
const ctxWithEvent = ctx;
|
|
61
|
+
try {
|
|
62
|
+
await ctxWithEvent.event?.emit('edge-agent/boot', { config });
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
// expected on cordis 0.2.1-rc.2 (undeclared `event` service)
|
|
66
|
+
}
|
|
67
|
+
// Track disposers for ordered teardown.
|
|
68
|
+
const disposers = [];
|
|
69
|
+
// Hook shutdown to flush audit logs. Best-effort: `cordis 0.2.1-rc.2`
|
|
70
|
+
// throws `cannot get property "x" without inject` when a service was not
|
|
71
|
+
// declared. The aggregator declares `auditLog` but NOT `localStorage`
|
|
72
|
+
// (the desktop-safe patch never inserts a `local-storage` row, and
|
|
73
|
+
// declaring it would make the loader wait forever with
|
|
74
|
+
// `pending (waiting for service: localStorage)`). Wrap both shutdown
|
|
75
|
+
// captures so the host-half mounts cleanly under any patch composition.
|
|
76
|
+
try {
|
|
77
|
+
const { auditLogShutdown } = await import('./audit-log.js');
|
|
78
|
+
disposers.push(await auditLogShutdown(ctx));
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// ctx.auditLog unavailable or threw on access; no-op disposer.
|
|
82
|
+
}
|
|
83
|
+
try {
|
|
84
|
+
const { storageShutdown } = await import('./local-storage.js');
|
|
85
|
+
disposers.push(await storageShutdown(ctx));
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// ctx.localStorage unavailable or threw on access; no-op disposer.
|
|
89
|
+
}
|
|
90
|
+
return async () => {
|
|
91
|
+
for (const dispose of disposers.reverse()) {
|
|
92
|
+
try {
|
|
93
|
+
await dispose();
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
// swallow: teardown must be idempotent and tolerant of partial state
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
}
|