@twinsearth/fang-dsh-plugin 0.0.0-stage → 3.3.4
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/CLA.md +79 -0
- package/LICENSE-COMMERCIAL +78 -0
- package/LICENSE-CORE +661 -0
- package/LICENSE-SDK +21 -0
- package/LICENSES/cordis-MIT.txt +39 -0
- package/NOTICE +95 -0
- package/README.md +158 -2
- package/client.js +7287 -0
- package/cordis.patch.yml +163 -0
- package/defaults/umbrella-rules.yaml +113 -0
- package/docs/CONTRACT-INTEROP.md +77 -0
- package/docs/CRASH-SAFETY.md +69 -0
- package/docs/DECISION-RELAY.md +46 -0
- package/docs/DSH-ARCHITECTURE-MAP.md +73 -0
- package/docs/FORK-SEED.md +64 -0
- package/docs/MAIN-CITY-GATE.md +58 -0
- package/docs/OVERLAY-MASTER-SWITCH.md +35 -0
- package/docs/PACKAGE-RENAME-v3.3.3.md +40 -0
- package/docs/PM-DECISION-POLICY.md +61 -0
- package/docs/WORKFLOW-UI.md +86 -0
- package/icon.svg +8 -0
- package/locale/en.json +6 -0
- package/locale/zh.json +6 -0
- package/package.json +71 -4
- package/sdk/README.md +65 -0
- package/sdk/examples/status-badge/README.md +16 -0
- package/sdk/examples/status-badge/client.js +60 -0
- package/sdk/examples/status-badge/cordis.patch.yml +7 -0
- package/sdk/examples/status-badge/index.js +12 -0
- package/sdk/examples/status-badge/package.json +24 -0
- package/sdk/templates/plugin/README.md +36 -0
- package/sdk/templates/plugin/client.js +35 -0
- package/sdk/templates/plugin/cordis.patch.yml +8 -0
- package/sdk/templates/plugin/icon.svg +5 -0
- package/sdk/templates/plugin/index.js +23 -0
- package/sdk/templates/plugin/locale/en.json +6 -0
- package/sdk/templates/plugin/package.json +36 -0
- package/src/compose.mjs +67 -0
- package/src/contract/VENDOR.json +16 -0
- package/src/contract/client.js +93 -0
- package/src/contract/constants.js +94 -0
- package/src/contract/envelope.js +83 -0
- package/src/contract/index.js +20 -0
- package/src/contract/negotiate.js +106 -0
- package/src/contract/relay.js +223 -0
- package/src/contract/server.js +77 -0
- package/src/contract/transports.js +319 -0
- package/src/index.mjs +11 -0
- package/src/kernel/core.mjs +100 -0
- package/src/kernel/extensions.mjs +61 -0
- package/src/kernel/fsx.mjs +349 -0
- package/src/kernel/index.mjs +21 -0
- package/src/kernel/ledger.mjs +94 -0
- package/src/kernel/paths.mjs +182 -0
- package/src/kernel/role.mjs +68 -0
- package/src/kernel/rounds.mjs +172 -0
- package/src/kernel/schema.mjs +5 -0
- package/src/kernel/types.mjs +266 -0
- package/src/kernel/vendor/cosmokit.mjs +421 -0
- package/src/kernel/vendor/schemastery.mjs +638 -0
- package/src/lib/adapter-client.mjs +383 -0
- package/src/lib/adapter-protocol.mjs +243 -0
- package/src/lib/ai-secret-store.mjs +202 -0
- package/src/lib/boot-guard.mjs +186 -0
- package/src/lib/branch-store.mjs +84 -0
- package/src/lib/branch.mjs +74 -0
- package/src/lib/bridge-client.mjs +133 -0
- package/src/lib/broker-adapter.mjs +67 -0
- package/src/lib/broker.mjs +45 -0
- package/src/lib/business-producers.mjs +109 -0
- package/src/lib/command-cache.mjs +209 -0
- package/src/lib/compat.mjs +170 -0
- package/src/lib/composer-journal.mjs +128 -0
- package/src/lib/container/index.mjs +806 -0
- package/src/lib/decision-events.mjs +129 -0
- package/src/lib/decision-ingest-client.mjs +41 -0
- package/src/lib/decision-outbox.mjs +129 -0
- package/src/lib/decision-policy.mjs +52 -0
- package/src/lib/default-essence.mjs +61 -0
- package/src/lib/desktop-launch.mjs +138 -0
- package/src/lib/essence.mjs +1211 -0
- package/src/lib/fork-client.mjs +410 -0
- package/src/lib/fork-config-lock.mjs +166 -0
- package/src/lib/fork-seed.mjs +304 -0
- package/src/lib/fork-sessions.mjs +183 -0
- package/src/lib/fork-supervision-settings.mjs +58 -0
- package/src/lib/fork-supervisor.mjs +122 -0
- package/src/lib/fs-guard.mjs +28 -0
- package/src/lib/http-auth.mjs +162 -0
- package/src/lib/idle-audit.mjs +178 -0
- package/src/lib/interaction-poke.mjs +61 -0
- package/src/lib/main-gate.mjs +579 -0
- package/src/lib/mirror-etag.mjs +92 -0
- package/src/lib/mirror-official-source.mjs +93 -0
- package/src/lib/mirror.mjs +478 -0
- package/src/lib/mother-link.mjs +156 -0
- package/src/lib/mutex.mjs +31 -0
- package/src/lib/permissions.mjs +115 -0
- package/src/lib/pnpm-store.mjs +189 -0
- package/src/lib/preferences.mjs +212 -0
- package/src/lib/producer-contract.mjs +162 -0
- package/src/lib/reflow.mjs +41 -0
- package/src/lib/remote.mjs +408 -0
- package/src/lib/restart-contract.mjs +752 -0
- package/src/lib/roots.mjs +30 -0
- package/src/lib/rule-registry.mjs +1127 -0
- package/src/lib/runtime-capture.mjs +307 -0
- package/src/lib/semaphore.mjs +34 -0
- package/src/lib/session-bundle.mjs +138 -0
- package/src/lib/session-format.mjs +672 -0
- package/src/lib/sse-parser.mjs +102 -0
- package/src/lib/ssrf-guard.mjs +170 -0
- package/src/lib/status-store.mjs +257 -0
- package/src/lib/task-handle.mjs +622 -0
- package/src/lib/ui-actions.mjs +55 -0
- package/src/lib/umbrella.mjs +1661 -0
- package/src/lib/upgrade-coordinator.mjs +107 -0
- package/src/lib/upgrade-status.mjs +43 -0
- package/src/plugins/adapter.mjs +1468 -0
- package/src/plugins/approver.mjs +1210 -0
- package/src/plugins/auditor.mjs +330 -0
- package/src/plugins/buffer-keeper.mjs +522 -0
- package/src/plugins/decision-center.mjs +2581 -0
- package/src/plugins/forker.mjs +1610 -0
- package/src/plugins/healer.mjs +702 -0
- package/src/plugins/hot-reloader.mjs +274 -0
- package/src/plugins/imports.mjs +247 -0
- package/src/plugins/orchestrator.mjs +3938 -0
- package/src/plugins/restart-guard.mjs +1022 -0
- package/src/plugins/router.mjs +714 -0
- package/src/plugins/scorer.mjs +661 -0
- package/src/plugins/session-bridge.mjs +166 -0
- package/src/plugins/session-catalog.mjs +771 -0
- package/src/plugins/session-router.mjs +125 -0
- package/src/plugins/session-sync.mjs +171 -0
- package/src/plugins/umbrella.mjs +419 -0
- package/tools/boot-guard.mjs +95 -0
- package/tools/decision-ingest.mjs +53 -0
- package/tools/desktop-launch.mjs +71 -0
- package/tools/fangctl.mjs +408 -0
- package/tools/gate/main-city.mjs +204 -0
- package/tools/mcp/server.mjs +121 -0
- package/tools/mcp/tools.mjs +65 -0
- package/tools/recover-queue.mjs +133 -0
package/cordis.patch.yml
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
2
|
+
# Copyright (c) 2026 dsh-Fang contributors — see LICENSE-CORE.
|
|
3
|
+
# dsh-Fang v1.0.0 — bundle layer.
|
|
4
|
+
# One installable bundle; one loader row per plugin. The row order is a dependency
|
|
5
|
+
# order: auditor/forker/buffer feed the middle tier, orchestrator comes last.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: fang-auditor
|
|
8
|
+
name: '@twinsearth/fang-dsh-plugin/auditor'
|
|
9
|
+
config:
|
|
10
|
+
dataRoot: ''
|
|
11
|
+
bootSelfCheck: true
|
|
12
|
+
|
|
13
|
+
- id: fang-forker
|
|
14
|
+
name: '@twinsearth/fang-dsh-plugin/forker'
|
|
15
|
+
config:
|
|
16
|
+
cliPath: ''
|
|
17
|
+
profileTemplate: ''
|
|
18
|
+
startTimeoutMs: 60000
|
|
19
|
+
maxForks: 8
|
|
20
|
+
supervise: false
|
|
21
|
+
maxRestarts: 3
|
|
22
|
+
backoffBaseMs: 1000
|
|
23
|
+
backoffMaxMs: 30000
|
|
24
|
+
backoffJitter: 0.2
|
|
25
|
+
healthPath: /
|
|
26
|
+
|
|
27
|
+
- id: fang-buffer-keeper
|
|
28
|
+
name: '@twinsearth/fang-dsh-plugin/buffer-keeper'
|
|
29
|
+
config:
|
|
30
|
+
root: ''
|
|
31
|
+
settleMs: 750
|
|
32
|
+
pollMs: 250
|
|
33
|
+
|
|
34
|
+
- id: fang-scorer
|
|
35
|
+
name: '@twinsearth/fang-dsh-plugin/scorer'
|
|
36
|
+
config:
|
|
37
|
+
allowlist: []
|
|
38
|
+
|
|
39
|
+
- id: fang-umbrella
|
|
40
|
+
name: '@twinsearth/fang-dsh-plugin/umbrella'
|
|
41
|
+
config:
|
|
42
|
+
bufferRoot: ''
|
|
43
|
+
productionRoot: ''
|
|
44
|
+
allowPaths: []
|
|
45
|
+
networkAllowlist: []
|
|
46
|
+
credentialPatterns: []
|
|
47
|
+
destructivePatterns: []
|
|
48
|
+
lifecyclePatterns: []
|
|
49
|
+
lifecycleTools: []
|
|
50
|
+
gradeFloorTable: umbrella
|
|
51
|
+
cacheSize: 200
|
|
52
|
+
auditDecisions: true
|
|
53
|
+
|
|
54
|
+
- id: fang-healer
|
|
55
|
+
name: '@twinsearth/fang-dsh-plugin/healer'
|
|
56
|
+
config:
|
|
57
|
+
blockingOnUnknown: true
|
|
58
|
+
reservedIds: []
|
|
59
|
+
|
|
60
|
+
- id: fang-router
|
|
61
|
+
name: '@twinsearth/fang-dsh-plugin/router'
|
|
62
|
+
config:
|
|
63
|
+
containerAvailable: false
|
|
64
|
+
trustedPlugins: []
|
|
65
|
+
|
|
66
|
+
- id: fang-approver
|
|
67
|
+
name: '@twinsearth/fang-dsh-plugin/approver'
|
|
68
|
+
config:
|
|
69
|
+
defaultTtlMs: 300000
|
|
70
|
+
remindAt: 0.5
|
|
71
|
+
authMode: manual
|
|
72
|
+
strictRedLine: true
|
|
73
|
+
backupRoot: ''
|
|
74
|
+
|
|
75
|
+
- id: fang-hot-reloader
|
|
76
|
+
name: '@twinsearth/fang-dsh-plugin/hot-reloader'
|
|
77
|
+
config:
|
|
78
|
+
healthWindowMs: 300
|
|
79
|
+
healthMaxNewHandles: 2
|
|
80
|
+
requireApprovalForSwap: true
|
|
81
|
+
|
|
82
|
+
- id: fang-restart-guard
|
|
83
|
+
name: '@twinsearth/fang-dsh-plugin/restart-guard'
|
|
84
|
+
config:
|
|
85
|
+
mode: observe
|
|
86
|
+
authMode: manual
|
|
87
|
+
strictRedLine: true
|
|
88
|
+
allowProcessRestart: false
|
|
89
|
+
autoPromoteAfterHours: 24
|
|
90
|
+
|
|
91
|
+
- id: fang-adapter
|
|
92
|
+
name: '@twinsearth/fang-dsh-plugin/adapter'
|
|
93
|
+
config:
|
|
94
|
+
prefix: /fang
|
|
95
|
+
taskId: ''
|
|
96
|
+
auditLimit: 500
|
|
97
|
+
commandTimeoutMs: 10000
|
|
98
|
+
|
|
99
|
+
- id: fang-session-catalog
|
|
100
|
+
name: '@twinsearth/fang-dsh-plugin/session-catalog'
|
|
101
|
+
config:
|
|
102
|
+
limit: 50
|
|
103
|
+
allowArchived: false
|
|
104
|
+
previewMaxChars: 2000
|
|
105
|
+
|
|
106
|
+
- id: fang-session-bridge
|
|
107
|
+
name: '@twinsearth/fang-dsh-plugin/session-bridge'
|
|
108
|
+
config:
|
|
109
|
+
dshHome: ""
|
|
110
|
+
prefix: /fang
|
|
111
|
+
timeoutMs: 15000
|
|
112
|
+
|
|
113
|
+
- id: fang-session-sync
|
|
114
|
+
name: '@twinsearth/fang-dsh-plugin/session-sync'
|
|
115
|
+
config:
|
|
116
|
+
sessionsRoot: ""
|
|
117
|
+
bundleRoot: ""
|
|
118
|
+
maxBundleBytes: 536870912
|
|
119
|
+
|
|
120
|
+
- id: fang-session-router
|
|
121
|
+
name: '@twinsearth/fang-dsh-plugin/session-router'
|
|
122
|
+
config:
|
|
123
|
+
sameMachine: true
|
|
124
|
+
bridgeEnabled: true
|
|
125
|
+
needsRealtime: false
|
|
126
|
+
|
|
127
|
+
- id: fang-decision-center
|
|
128
|
+
name: '@twinsearth/fang-dsh-plugin/decision-center'
|
|
129
|
+
config:
|
|
130
|
+
maxTickets: 500
|
|
131
|
+
maxPendingRed: 30
|
|
132
|
+
defaultTimeoutMinutes: 360
|
|
133
|
+
tickMs: 5000
|
|
134
|
+
registerTool: true
|
|
135
|
+
|
|
136
|
+
- id: fang-orchestrator
|
|
137
|
+
name: '@twinsearth/fang-dsh-plugin'
|
|
138
|
+
config:
|
|
139
|
+
role: mother
|
|
140
|
+
maxForks: 8
|
|
141
|
+
autoReapIdleMinutes: 0
|
|
142
|
+
routes:
|
|
143
|
+
enabled: true
|
|
144
|
+
prefix: /fang
|
|
145
|
+
contract:
|
|
146
|
+
enabled: true
|
|
147
|
+
token: ''
|
|
148
|
+
fileDir: ''
|
|
149
|
+
pathPrefix: /fang/api/contract
|
|
150
|
+
|
|
151
|
+
# Child plugin of the fang bundle: composer draft/queue journal (client half).
|
|
152
|
+
# The main Web panel is the fang client half itself (package.json dsh.client),
|
|
153
|
+
# so the plugin is fully usable without the PC Electron client.
|
|
154
|
+
- id: fang-web-entry
|
|
155
|
+
name: '@twinsearth/dsh-fang-web-entry'
|
|
156
|
+
|
|
157
|
+
# v3.3.3: default OFF. Draft/queue recovery is optional; the host half
|
|
158
|
+
# provides fangQueueJournal only when this row is enabled, and without it
|
|
159
|
+
# the orchestrator returns 503 queue-journal-disabled for journal/queue
|
|
160
|
+
# routes. The client half is not loaded, so its composer UI stays hidden.
|
|
161
|
+
- id: fang-queue-journal
|
|
162
|
+
name: '@twinsearth/dsh-fang-queue-journal'
|
|
163
|
+
disabled: true
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
# dsh-Fang v2.8.1 — Umbrella 推荐基线配置(随包分发,项目不可替换,只能叠加)
|
|
2
|
+
#
|
|
3
|
+
# 加载顺序(后加载的层按 id 覆盖先加载的层;未列出的规则保持内置默认):
|
|
4
|
+
# 1) 本文件(包内基线) ← defaults/umbrella-rules.yaml
|
|
5
|
+
# 2) 项目级 <project>/.dsh/umbrella-rules.yaml(插件配置 rulesFile)
|
|
6
|
+
# 3) 环境级 $UMBRELLA_RULES_PATH(CI/部署注入,可选)
|
|
7
|
+
#
|
|
8
|
+
# 锁定语义:带 non-negotiable / hard-deny 标签的规则不可被**关闭**、不可被**降低灯色**;
|
|
9
|
+
# 允许项目追加 pattern(扩大覆盖面)或提升灯色(更严格)。
|
|
10
|
+
# 项目尝试关闭硬规则 → 该层错误;若该层 failOnInvalidPattern: true → 整层拒绝,回退到基线。
|
|
11
|
+
#
|
|
12
|
+
# 说明:本文件刻意**只显式写硬规则与兜底**。可调规则留空 = 使用内置默认值,
|
|
13
|
+
# 项目按需在自己的叠加层里覆盖(示例见文件末尾注释与 docs/RULES-BASELINE.md)。
|
|
14
|
+
|
|
15
|
+
version: 1
|
|
16
|
+
|
|
17
|
+
settings:
|
|
18
|
+
failOnUnknownRule: false # 未知 id 只警告,不阻断(基线可能领先于代码)
|
|
19
|
+
failOnInvalidPattern: true # 正则/声明式规则编译失败视为配置错误,整层拒绝(宁可不启动)
|
|
20
|
+
rejectUnsafeRegex: true # 拒绝明显灾难回溯的正则形状
|
|
21
|
+
|
|
22
|
+
rules:
|
|
23
|
+
# ── 硬规则:显式锁定(不可关闭、不可降级)──────────────────────────────
|
|
24
|
+
- id: dsh-lifecycle
|
|
25
|
+
enabled: true
|
|
26
|
+
light: red
|
|
27
|
+
priority: 10
|
|
28
|
+
reason: "DSH 宿主的生命周期动作(重启/终止/进程级),必须转交 restart-guard 审批"
|
|
29
|
+
tags: [hard-deny, non-negotiable, delegate-to-restart-guard]
|
|
30
|
+
|
|
31
|
+
- id: destructive-command
|
|
32
|
+
enabled: true
|
|
33
|
+
light: red
|
|
34
|
+
priority: 20
|
|
35
|
+
reason: "破坏性 shell 命令(rm -rf / format / shutdown / 强制推送等),一律红色"
|
|
36
|
+
tags: [hard-deny, non-negotiable]
|
|
37
|
+
|
|
38
|
+
- id: credential-access
|
|
39
|
+
enabled: true
|
|
40
|
+
light: red
|
|
41
|
+
priority: 30
|
|
42
|
+
reason: "读取凭据路径(.ssh/.aws/.gnupg/.git-credentials 等),唯一硬拒绝出口"
|
|
43
|
+
tags: [hard-deny, non-negotiable]
|
|
44
|
+
|
|
45
|
+
# ── 兜底:安全网(不可关闭)──────────────────────────────────────────────
|
|
46
|
+
# 未登记的工具/没有可识别目标时落到这里;优先级 9999 保证它永远最后。
|
|
47
|
+
- id: unknown-tool
|
|
48
|
+
enabled: true
|
|
49
|
+
light: yellow
|
|
50
|
+
priority: 9999
|
|
51
|
+
reason: "未知工具或目标:行为不可预测,保守判黄(需审批)"
|
|
52
|
+
tags: [conservative, non-negotiable]
|
|
53
|
+
|
|
54
|
+
# ── 可调规则:留空 = 使用内置默认。下面是**推荐值**(注释形式),项目按需覆盖 ──
|
|
55
|
+
#
|
|
56
|
+
# 用于参考的默认(与 DEFAULT_RULE_PRIORITIES 一致):
|
|
57
|
+
# network-allowlisted blue 40 外连命中允许列表 → 放行 + 标记
|
|
58
|
+
# network-egress yellow 50 外连未在允许列表 → 拆审批
|
|
59
|
+
# dynamic-import yellow 60 动态加载未评分模块 → 先扫描
|
|
60
|
+
# write-in-buffer blue 70 写沙箱缓冲区 → 放行 + 标记
|
|
61
|
+
# write-in-allowlist blue 80 写允许列表路径 → 放行 + 标记
|
|
62
|
+
# write-production yellow 90 写生产区 → 需审批
|
|
63
|
+
# write-outside-buffer yellow 100 写越界 → 需审批
|
|
64
|
+
# exec-read-only blue 110 只读 shell(无重定向)→ 放行 + 标记
|
|
65
|
+
# exec-side-effect yellow 120 shell 有副作用 → 需审批
|
|
66
|
+
# read-in-buffer green 130 读沙箱缓冲区 → 自动放行
|
|
67
|
+
# read-outside-buffer blue 140 读缓冲区外 → 放行 + 标记
|
|
68
|
+
#
|
|
69
|
+
# 项目级覆盖示例(写在 <project>/.dsh/umbrella-rules.yaml):
|
|
70
|
+
#
|
|
71
|
+
# version: 1
|
|
72
|
+
# settings:
|
|
73
|
+
# failOnInvalidPattern: true
|
|
74
|
+
# rules:
|
|
75
|
+
# # 更严格:越界写从黄升红
|
|
76
|
+
# - id: write-outside-buffer
|
|
77
|
+
# light: red
|
|
78
|
+
# reason: "本项目:任何越界写都直接拒绝"
|
|
79
|
+
#
|
|
80
|
+
# # 更严格:生产区写也需要人工审批(升红)
|
|
81
|
+
# - id: write-production
|
|
82
|
+
# light: red
|
|
83
|
+
#
|
|
84
|
+
# # 追加凭据 pattern(硬规则允许"扩大覆盖面",不允许关闭)
|
|
85
|
+
# - id: credential-access
|
|
86
|
+
# pathPatterns:
|
|
87
|
+
# - re: "\\.npmrc$"
|
|
88
|
+
# label: "npm registry token"
|
|
89
|
+
# - re: "secrets[\\\\/]"
|
|
90
|
+
# label: "project secrets directory"
|
|
91
|
+
#
|
|
92
|
+
# # 项目专属声明式规则
|
|
93
|
+
# - id: project-build-guard
|
|
94
|
+
# category: destructive
|
|
95
|
+
# light: red
|
|
96
|
+
# reason: "禁止删除构建目录"
|
|
97
|
+
# priority: 25
|
|
98
|
+
# match:
|
|
99
|
+
# kind: command-pattern
|
|
100
|
+
# patterns:
|
|
101
|
+
# - re: "\\brm\\s+-rf\\s+(?:dist|build|out)\\b"
|
|
102
|
+
# label: "delete build directory"
|
|
103
|
+
# evidence:
|
|
104
|
+
# kind: pattern-match
|
|
105
|
+
#
|
|
106
|
+
# # 以下写法会被拒绝(硬规则锁定):
|
|
107
|
+
# # - id: destructive-command
|
|
108
|
+
# # enabled: false # → "cannot be disabled"
|
|
109
|
+
# # - id: credential-access
|
|
110
|
+
# # light: yellow # → "light cannot be lowered from red to yellow"
|
|
111
|
+
#
|
|
112
|
+
# 注意:`light: blue` 不能把 network-egress 变成"内网放行"——它的谓词看 allowHosts;
|
|
113
|
+
# 要放行内网域请把它们加入 policy.networkAllowlist,而不是只改灯色。
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# 房主 ↔ 瓮城 互联契约(v3.2.4)
|
|
2
|
+
|
|
3
|
+
> 目标:**双方独立升级/发布,仍能互联互通**;接口架构可通过更新契约包整体替换,
|
|
4
|
+
> 同时保留多级保底通讯。
|
|
5
|
+
> 实现:`@twinsearth/dsh-fang-contract` 1.0.x(源码独立仓,另见 `TwinsEarth/dsh-fang-contract`),
|
|
6
|
+
> 两侧各自内嵌**同一份** `src/contract/` 源码(无运行时 npm 依赖),版本号一致。
|
|
7
|
+
|
|
8
|
+
## 1. 契约内容
|
|
9
|
+
|
|
10
|
+
- 方法表:`contract.hello/health`、`fang.fork.spawn/stop/list`、`fang.decision.create/resolve`、
|
|
11
|
+
`fang.session.list`、`fang.orchestrator.status`;
|
|
12
|
+
- 信封:`protocol=fang-master, protocolVersion=1, id, method, params, meta`;
|
|
13
|
+
- 版本协商:`hello` 返回 `contractVersion / role / methods / capabilities / transports`,
|
|
14
|
+
`negotiate()` 裁剪双方方法交集;MAJOR 不兼容时自动降级;
|
|
15
|
+
- 错误码:`unsupported-method / unsupported-version / transport-* / all-transports-failed` 等。
|
|
16
|
+
|
|
17
|
+
## 2. 四级保底传输
|
|
18
|
+
|
|
19
|
+
```text
|
|
20
|
+
local 同 Host DSH 服务 fangContract(零网络,首选)
|
|
21
|
+
↓ 不可用/超时
|
|
22
|
+
http POST /fang/api/contract/{hello,invoke}(Bearer token)
|
|
23
|
+
↓ 不可用/超时
|
|
24
|
+
file 同一文件系统 outbox/inbox(requests/*.json → responses/*.json)
|
|
25
|
+
↓ 不可用/超时
|
|
26
|
+
legacy 直接调用 3.1.x 旧服务键(fangForker / fangDecisionCenter 等)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
业务错误(如 `ticket-invalid`)**不降级**,避免重复副作用;传输/协议/未知方法才降级。
|
|
30
|
+
每次尝试记录在 `client.attempts()`,状态可观测。
|
|
31
|
+
|
|
32
|
+
## 2.5 零配置(v3.1.16 / 房主 3.2.5)
|
|
33
|
+
|
|
34
|
+
瓮城原身运行态的 spawn 环境新增 `DSH_FANG_MOTHER=<母体 dataDir>`;房主 Router 检测到该变量后
|
|
35
|
+
自动使用 `<母体 dataDir>/contract-outbox` 作为 `dispatch.contractFileDir`,无需手工配置:
|
|
36
|
+
|
|
37
|
+
```text
|
|
38
|
+
原身 Host(essence Router)
|
|
39
|
+
env: DSH_FANG_ROLE=fork / DSH_FANG_ESSENCE=1 / DSH_FANG_MOTHER=<母体 dataDir>
|
|
40
|
+
↓ dispatch.contract=true(自动 file 桥)
|
|
41
|
+
<母体 dataDir>/contract-outbox/requests → responses
|
|
42
|
+
↓
|
|
43
|
+
母体 Host(Fang contract service)→ 真实 fork
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
HTTP 桥需要两个凭据:契约 Bearer token + 安装级 `x-fang-auth`;房主通过
|
|
47
|
+
`dispatch.motherToken` 与 `dispatch.motherAuthToken` 提供(SDK 侧为可选 `headers`)。
|
|
48
|
+
|
|
49
|
+
## 3. 两侧实现位置
|
|
50
|
+
|
|
51
|
+
| 侧 | 文件 | 内容 |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| 瓮城 dsh-Fang v3.1.15 | `src/contract/` + `src/plugins/orchestrator.mjs` | 内嵌契约 SDK;mother 提供 `fangContract` 服务;HTTP `/fang/api/contract/*`;可选 file outbox server |
|
|
54
|
+
| 房主 dsh-Master v3.2.4 | `src/contract/` + `src/superagent/dispatch.mjs` | 内嵌同一 SDK;派发优先 contract(local/http/file/legacy),失败再回退本地 forker/母体 bridge |
|
|
55
|
+
|
|
56
|
+
两侧都不 import 对方内部源码,只依赖契约包稳定接口。
|
|
57
|
+
|
|
58
|
+
## 4. 独立升级 / 完全替换流程
|
|
59
|
+
|
|
60
|
+
1. 修改并发布契约包(源码仓 `dsh-fang-contract`,版本 `x.y.z`);
|
|
61
|
+
2. 将 `src/contract/` 同步到两侧(同源文件,`CONTRACT_VERSION` 一致);
|
|
62
|
+
3. MINOR:双方可不同时升级;`hello` 协商后使用方法交集,旧端自动降级;
|
|
63
|
+
4. MAJOR:新方法同时保留 `method@old` 一个 MINOR 周期(双写),两侧逐侧升级;
|
|
64
|
+
5. 观察 `client.attempts()` / `describe()` 的 transport 命中与降级;
|
|
65
|
+
6. 旧 MAJOR 下线前至少保留一个 MINOR 周期的 legacy 传输。
|
|
66
|
+
|
|
67
|
+
> npm 上 `@twinsearth/dsh-fang-contract@1.0.0` 首次发布被 npm **staged publishing**
|
|
68
|
+
> 置为 `0.0.0-stage` 占位(需 2FA/stage 审批后才能成为正式版本);
|
|
69
|
+
> 本次两个主包采用“同源内嵌 + 独立源码仓”的方式先行发布,待 npm 正式可用后
|
|
70
|
+
> 可改为依赖 npm 包,无需改协议/代码。
|
|
71
|
+
|
|
72
|
+
## 5. 安全
|
|
73
|
+
|
|
74
|
+
- HTTP 契约必须 Bearer token(两侧各自配置 `contract.token` / `dispatch.motherToken`);
|
|
75
|
+
- env 只透传 `DSH_MASTER_*`;契约响应/状态不含 key/token/私钥;
|
|
76
|
+
- file outbox 目录须在受控本机路径,由 OS 账户隔离;
|
|
77
|
+
- legacy 传输只调用既有服务键,不扩大权限。
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# CRASH-SAFETY — 瓮城防崩溃方案(防线、强制点、运行手册)
|
|
2
|
+
|
|
3
|
+
> 状态:生效中(2026-11-10 起固化)。与 `docs/WORKFLOW-UI.md` 一起受测试/打包/发布门禁保护。
|
|
4
|
+
> 发布动作遵循 `docs/WORKFLOW-UI.md` §0:**发布必须由人类显式授权**,禁止自动 release/npm/GitHub Release。
|
|
5
|
+
> 目标:**插件树坏一行,不拖垮宿主;分身崩溃,不扩散;配置变更,可回滚。**
|
|
6
|
+
|
|
7
|
+
## 1. 威胁模型
|
|
8
|
+
|
|
9
|
+
| 威胁 | 后果 | 现有防线 |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| 插件 bundle 某行解析失败 / 模块缺失 / `apply()` 抛错 | 宿主启动崩溃或半启动 | boot-guard 启动前自检 + 失败行 `disabled` + 安全模式 |
|
|
12
|
+
| 插件崩溃把宿主 fiber 带走 / 反复崩 | 母体不可用 / 崩溃循环 | Cordis fiber 隔离 + RestartGuard 唯一重启闸门 |
|
|
13
|
+
| 分身进程崩溃 / 事件循环耗尽 / `process.exit()` | 任务失败、端口/资源泄漏 | 独立进程 + 独立 `DSH_HOME` + 退避重启 + 孤儿回收 |
|
|
14
|
+
| 角色/权限边界被绕过 | 越权写母体 | role fail-closed(`roleGate`/`roleAllows`) |
|
|
15
|
+
| 崩溃后状态丢失/重复执行 | 重复任务、丢事件 | fork meta(crashCount/restartCount/lastError)+ 待处理缓冲 + 未清洁启动检测 |
|
|
16
|
+
| 升级把运行中的 profile 改坏 | 客户端崩溃、用户配置丢失 | 升级安全规程(停 host、备份、候选包冒烟、装后 dry-run、回滚) |
|
|
17
|
+
|
|
18
|
+
## 2. 防线清单(代码事实)
|
|
19
|
+
|
|
20
|
+
| 防线 | 入口 | 行为 |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| boot-guard | `src/lib/boot-guard.mjs`、`tools/boot-guard.mjs` | 解析 fang bundle 行;重复 id / 模块解析 / 隔离子进程 `import` 且导出 `apply()`;失败行可 `--apply` 写成 `disabled:true`(写前备份);`--safe-mode` 全禁 fang 行;`--candidate-dir` 用于更新候选包冒烟 |
|
|
23
|
+
| RestartGuard | `src/plugins/restart-guard.mjs` | `classifyRisk` / `decide` / `findGrant`;母体重启唯一入口,默认只到审批/审计,不直接退出进程 |
|
|
24
|
+
| 分身隔离与监督 | `src/plugins/forker.mjs`、`src/lib/fork-supervisor.mjs` | 每任务独立进程 + 独立 `DSH_HOME`;`planRestart` 指数退避 + 最大次数;`reconcileOrphans` 只回收死亡孤儿、不 kill 活实例;`writeForkMeta` 原子写 meta |
|
|
25
|
+
| 角色 fail-closed | `src/kernel/role.mjs` | `roleGate` / `roleAllows`:未知角色默认拒绝,不默认放行 |
|
|
26
|
+
| 待处理缓冲 | `src/plugins/buffer-keeper.mjs` | pending 观察 + digest 兜底 + 静默窗口;时间戳缺失时补观测时间,避免误判 |
|
|
27
|
+
| 未清洁启动检测 | `src/lib/desktop-launch.mjs` | `readUncleanBoot` / `runBootGuard` / `resolveDesktopLaunch`:桌面启动前检测上次崩溃并跑 guard |
|
|
28
|
+
|
|
29
|
+
## 3. 强制点矩阵(改坏就红)
|
|
30
|
+
|
|
31
|
+
| 环节 | 命令/文件 | 覆盖 |
|
|
32
|
+
|---|---|---|
|
|
33
|
+
| 单测 | `test/boot-guard.test.mjs`、`test/desktop-launch.test.mjs`、`test/fork-supervisor.test.mjs`、**`test/crash-safety.test.mjs`(新增)** | 行解析/隔离冒烟/禁用写回;未清洁启动;退避/孤儿;门禁工具自身 |
|
|
34
|
+
| 发布前源码门禁 | `node tools/release/preflight.mjs` → `crash-safety --json` | 防线文件存在、纯函数导出、插件闸门 token、client 半浏览器安全、文档与打包清单 |
|
|
35
|
+
| 打包后门禁 | `node tools/release/build.mjs` → `crash-safety --package dist/npm/package --tarball …` | **最终产物**内必需防线文件、版本/client 声明、候选包 `apply()` 冒烟、manifest sha256、tarball 内约束文档 |
|
|
36
|
+
| 发布 | release workflow build job(内置 `build.mjs` → preflight + crash-safety) | 未过 crash-safety 的产物不会进入 GitHub Release / npm |
|
|
37
|
+
|
|
38
|
+
## 4. 运行手册
|
|
39
|
+
|
|
40
|
+
### 4.1 启动前自检(profile 有 Fang)
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
node tools/boot-guard.mjs --profile-dir <profile> --json # dry-run,只读
|
|
44
|
+
node tools/boot-guard.mjs --profile-dir <profile> --apply # 禁用失败行(备份后写 profile patch)
|
|
45
|
+
node tools/boot-guard.mjs --profile-dir <profile> --safe-mode # 全禁 fang 行(救急)
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### 4.2 升级候选包(不碰运行中的 profile)
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
node tools/boot-guard.mjs --candidate-dir <解包后的候选包目录> --json # 入口存在 + apply() 冒烟
|
|
52
|
+
# 停 host → 备份 profile 用户层 → 安装 → 装后 dry-run → 启动 → 失败则恢复备份/--apply
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### 4.3 崩溃后处置顺序
|
|
56
|
+
|
|
57
|
+
1. 先看 `fang-meta.json` 的 `crashCount/lastError` 与宿主日志,确认是分身还是母体;
|
|
58
|
+
2. 分身:等监督重启(opt-in)或手动重启;孤儿按 `reconcileOrphans` 规则处理,不 kill 活实例;
|
|
59
|
+
3. 母体:走 RestartGuard 审批;启动前跑 boot-guard dry-run,确认不是插件树坏行;
|
|
60
|
+
4. 若确认是某行插件:`--apply` 禁用该行并记录,恢复后单独排查该插件;
|
|
61
|
+
5. 复盘:把新发现的崩溃模式补进本文件和 `test/crash-safety.test.mjs`。
|
|
62
|
+
|
|
63
|
+
## 5. 边界(诚实)
|
|
64
|
+
|
|
65
|
+
- 不承诺宿主内核/Node 自身崩溃恢复;Cordis fiber 隔离只覆盖插件生命周期错误。
|
|
66
|
+
- 分身与母体是**同用户、同 ACL**,不是权限隔离;`DSH_HOME` 隔离只防数据串写。
|
|
67
|
+
- 桌面 Electron 壳(`dsh-fang-desktop/panel`)是另一套 UI,不在 web 控制台门禁覆盖内。
|
|
68
|
+
- `crash-safety` 是**静态+冒烟**门禁,不是形式化证明;它保证“已知防线还在、产物能加载”,
|
|
69
|
+
不保证任意新插件逻辑无崩溃。
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# DECISION-RELAY — 会话内决策中继(v3.1.13)
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
决策中心不再只在母体「收件箱」里处理:**每个会话自己**在右侧显示待决策入口,
|
|
6
|
+
用户在该会话里直接选 A/B/C/D 或输入决定,Agent 收到结果后继续任务。
|
|
7
|
+
总体界面仍在母体:收件箱(侧栏入口 + 右侧抽屉)保持原样,汇总所有会话/分身的待决策。
|
|
8
|
+
|
|
9
|
+
## 行为
|
|
10
|
+
|
|
11
|
+
1. **悬浮入口**:当前会话存在 `pending` 决策时,右侧边缘出现红色「◈ 待选 N」悬浮按钮;
|
|
12
|
+
没有待决策时不显示(避免干扰)。入口只在**母体 Web** 注册——分身/元宝运行态不显示
|
|
13
|
+
(决策由母体的收件箱统一处理)。
|
|
14
|
+
2. **从右向左滑出**:点击按钮,决策栏从右侧滑入(`translateX` 过渡 + 遮罩),`Esc` 或点遮罩关闭。
|
|
15
|
+
3. **按会话过滤**:只显示 `source.sessionId === 当前会话` 的 ticket;其他会话/分身的票留在收件箱。
|
|
16
|
+
4. **选择或输入**:
|
|
17
|
+
- 预置选项按 **A/B/C/D** 标号显示(超过 4 个继续 E、F…),点击即提交 `option.value`;
|
|
18
|
+
- 直接在输入框写决定(如“按 B 方案,先灰度 10%”),提交后作为 **custom decision**;
|
|
19
|
+
- 无预置选项的 green/ack 票显示「确认收到(ACK)」。
|
|
20
|
+
5. 提交后按钮计数实时更新;没有待决策时按钮消失,面板提示为空。
|
|
21
|
+
|
|
22
|
+
## 服务端语义
|
|
23
|
+
|
|
24
|
+
`POST /fang/api/decisions/resolve` 新增可选字段 `custom`:
|
|
25
|
+
|
|
26
|
+
- 仅在**显式**传 `custom` 时生效(不是“任意未知 option 都放过”);
|
|
27
|
+
- 去空白、上限 2000 字符;`resolvedOption = custom`、`resolvedPayload = {custom}`、
|
|
28
|
+
`resolutionNote = custom`、ticket 标记 `customInput: true`;
|
|
29
|
+
- 未传 custom 的未知 option 仍按原逻辑拒绝(`unknown-option`,fail-closed);
|
|
30
|
+
- Agent 的 `request_human_decision` 返回值 `selectedValue` 即用户输入/选项值,据此继续。
|
|
31
|
+
|
|
32
|
+
## 代码位置
|
|
33
|
+
|
|
34
|
+
| 文件 | 作用 |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `client.js` | `mountDecisionRelay()`:悬浮按钮 + 右滑面板 + A/B/C/D + 自定义输入;slot `conversation.session.header.utilities`;store 新增 `resolveCustom()` |
|
|
37
|
+
| `src/plugins/decision-center.mjs` | `resolve()` 接受 `custom`;`publicTicket` 暴露 `customInput` |
|
|
38
|
+
| `src/plugins/orchestrator.mjs` | `/fang/api/decisions/resolve` 透传 `custom` |
|
|
39
|
+
| `test/decision-relay-dom.test.mjs` | 入口/面板/选项/自定义输入/会话过滤/fork 不挂载 |
|
|
40
|
+
| `test/decision-center.test.mjs` | custom 解析 + 未知 option 仍 fail-closed |
|
|
41
|
+
|
|
42
|
+
## 边界
|
|
43
|
+
|
|
44
|
+
- 只在母体 Web 的会话里出现;分身 Web 的决策仍在母体收件箱处理。
|
|
45
|
+
- 悬浮按钮依赖 DSH 会话 header utilities 槽与 sessionId 标准属性;DSH 大版本升级需回归对应 DOM 测试。
|
|
46
|
+
- 自定义输入会作为决定原文写入 ticket/审计,注意不要输入密钥等敏感信息。
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# DSH 系统架构 × 瓮城安全边界(v3.3.0)
|
|
2
|
+
|
|
3
|
+
> 依据:DeepSeek Harness 0.2.2 架构文档与仓库内实际代码(`dsh --profile <name> --dump-config` 是最终事实)。
|
|
4
|
+
> 本文只记录「哪些层会影响主城安全、瓮城在哪一层设闸」。逐文件行号会随 DSH 版本变化,结论以能力边界为准。
|
|
5
|
+
|
|
6
|
+
## 1. 分层总图
|
|
7
|
+
|
|
8
|
+
```text
|
|
9
|
+
入口:dsh CLI / Web / Electron / SDK / ACP
|
|
10
|
+
└─ Profile($DSH_HOME/profiles/<name>)
|
|
11
|
+
├─ 有序 bundles(dsh-base / dsh-web-app / 树外插件)
|
|
12
|
+
├─ profile cordis.patch.yml
|
|
13
|
+
└─ home 级 cordis.patch.yml + CLI --patch
|
|
14
|
+
└─ Cordis 插件树(一切皆是插件)
|
|
15
|
+
├─ Host Plane:llm / sessions / tools / sandbox / approval / subagents / presets
|
|
16
|
+
├─ Agent Plane:每个 Agent 的 tools / prompt sections / delegation
|
|
17
|
+
└─ Session:append-only SessionEvent 日志(模型可见 ⟺ 已记录)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## 2. 与主城安全直接相关的十层
|
|
21
|
+
|
|
22
|
+
| 层 | DSH 机制 | 失败后果 | 瓮城闸门 |
|
|
23
|
+
|---|---|---|---|
|
|
24
|
+
| Profile 组合 | bundles 有序 + patch 逐层覆盖 | 缺 bundle/重复/坏 patch → 起不来或半残 | `profile` gate:路径、名称、bundles 去重、patch 解析、磁盘 |
|
|
25
|
+
| 插件生命周期 | Cordis Fiber;PENDING/LOADING/ACTIVE/FAILED;effect 可回收 | 注册泄漏/依赖缺失 → 静默无能力 | boot-guard + 关键防线文件 + 插件冒烟 |
|
|
26
|
+
| Host/Agent 平面 | 注册表与工具作用域隔离 | 把 host 注册表塞进 isolate → 名称解析失败 | 不在门禁里改 profile 层,只做只读校验 |
|
|
27
|
+
| Agent Loop | turn/step/工具批次;错误恢复 append-only | 半途崩溃留下不一致历史 | restart safe-mode + 审计链 |
|
|
28
|
+
| Session 日志 | 追加式、版本化、可重建 | 篡改/删行会让重放失真 | `audit` 链 + Session 版本校验(DSH 自身) |
|
|
29
|
+
| 工具执行 | tools/pre-execute → guard → execute → post-execute | 权限绕过/危险命令 | DSH guard/approval/sandbox 原生;瓮城不削弱默认值 |
|
|
30
|
+
| Guard/Approval/Sandbox | 三层安全,fail-closed | 静默放行不可回滚动作 | restart 默认 observe;自动重启默认 false;崩溃循环直接 disarm |
|
|
31
|
+
| Preset/HMR | 修订保留;活 Agent 组合稳定 | 热替换失败拖垮进程 | 门禁不热改 profile;变更必须 restart 路径 |
|
|
32
|
+
| KV Cache | 前缀稳定是硬约束 | 无关安全,但错误重试造成成本/时序异常 | 门禁报告只读,不改请求前缀 |
|
|
33
|
+
| 打包/发布 | bundle 分发即代码执行 | 坏产物/错标签/重复发布污染 registry | `package` / `publish` gate;sha256/manifest/tag 校验 |
|
|
34
|
+
|
|
35
|
+
## 3. 瓮城 18 个子插件的职责与主城关系
|
|
36
|
+
|
|
37
|
+
| 子插件 | 关键风险 | 现有保护 | v3.3.0 增量 |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| orchestrator | 路由/派生/状态机 | 鉴权、fail-closed、配额 | 新 profile 建前过 gate |
|
|
40
|
+
| forker | 子进程/端口/配置锁 | 端口池、退避、孤儿回收、只读修复 | `provision` 新建 profile 过 gate |
|
|
41
|
+
| restart-guard | 交接执行权 = 进程退出/重启 | 三重门禁 + 审批 + supervisor 契约 | **崩溃循环 15m≥3 → safe-mode,人工清除前永不 handoff** |
|
|
42
|
+
| restart-supervisor/watchdog | 外部拉起 | 授权清单 + 回执 + boot-guard | 增加 deploy gate(含备份/审计链/崩溃循环) |
|
|
43
|
+
| approver | 快照/回滚会覆盖目录 | digest 校验、代际、TTL | 门禁写 profile 前自动 `.gate-backups` |
|
|
44
|
+
| auditor | 审计链是最后证据 | 全链重算 + 篡改检测 | 部署/安装/发布前验链;门禁结论入链 |
|
|
45
|
+
| scorer | 静态评分 | A–E 分档、C3 宽限、veto | **scoreGateFindings:0 warning=A,1–2=B,3+=C,blocker=E** |
|
|
46
|
+
| healer | 修复建议不得放行 | 只读、UNKNOWN fail-closed | **gateRepairPlan:blocker→可执行修复建议** |
|
|
47
|
+
| decision-center | 通知/审批/阻塞 | 红阻塞、黄蓝绿状态机 | 不作为主城放行依据;不绕过 restart/approval |
|
|
48
|
+
| session-* / imports | 会话复制/恢复 | 有界读取、摘要大小限制 | profile gate 校验路径与磁盘 |
|
|
49
|
+
| essence/mirror/compat | 元主/镜像升级 | 指纹、备份、兼容矩阵 | install/deploy gate 校验 profile |
|
|
50
|
+
| hot-reloader | 热替换 | 只做可回收注册 | 主城变更不允许热替换 profile |
|
|
51
|
+
| buffer/queue-journal | 持久化队列 | journal/outbox | 门禁不删除运行数据 |
|
|
52
|
+
| adapter/approver 客户端 | 跨进程通信 | Bearer/HMAC | 门禁不联网 |
|
|
53
|
+
|
|
54
|
+
## 4. 为什么必须 fail-closed
|
|
55
|
+
|
|
56
|
+
1. **Cordis 的注册是副作用**:一个坏插件可以在 ACTIVE 后注册工具/服务,进程看似正常但能力错误。
|
|
57
|
+
2. **Profile 是分层覆盖**:任何时候都可能出现「上一层覆盖导致另一层失效」,只看源码无法证明最终组合。
|
|
58
|
+
3. **Session 是唯一真相源**:日志损坏后,resume/fork/transcript 全部失真,重装后仍可能复现。
|
|
59
|
+
4. **重启是唯一把代码变成新事实的动作**:在崩溃循环/审计链断裂时重启,很可能只是把故障带入下一次启动。
|
|
60
|
+
5. **DSH 明确是 Developer Preview**:沙箱/审批降低风险但不保证隔离;对不可信输入应使用一次性环境。
|
|
61
|
+
|
|
62
|
+
## 5. 门禁的最小信任链
|
|
63
|
+
|
|
64
|
+
```text
|
|
65
|
+
源码(版本/文档/契约/防线)
|
|
66
|
+
→ 产物(sha256/sidecar/manifest/必需文件)
|
|
67
|
+
→ 发布(tag/版本/registry 幂等)
|
|
68
|
+
→ 安装(profile 路径/无活宿主/磁盘)
|
|
69
|
+
→ 部署(备份/审计链/崩溃循环/supervisor)
|
|
70
|
+
→ 运行(restart-guard 三重门禁 + safe-mode)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
任何一环缺少证据 = blocker;不允许「先跑起来再说」。
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# FORK-SEED — 派生分身的默认第三方插件
|
|
2
|
+
|
|
3
|
+
> v3.1.12 起:新机器 / 新用户的干净分身不再只有 `base + web-app + fang`,而是按
|
|
4
|
+
> “安装不激活 / 安装并激活”两组清单带上默认第三方插件。
|
|
5
|
+
|
|
6
|
+
## 默认清单(2026-11-11)
|
|
7
|
+
|
|
8
|
+
安装不激活(只进 `dependencies` + `node_modules`,不加入 profile bundles):
|
|
9
|
+
|
|
10
|
+
```text
|
|
11
|
+
@twinsearth/dsh-2xray
|
|
12
|
+
@michengai/dsh-agency-agents
|
|
13
|
+
dsh-better-sidebar
|
|
14
|
+
dsh-context
|
|
15
|
+
@liustack/modlens
|
|
16
|
+
@liustack/modsearch
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
安装并激活(额外写入 `dsh.profile.bundles`,分身启动即加载):
|
|
20
|
+
|
|
21
|
+
```text
|
|
22
|
+
dshmarket
|
|
23
|
+
@twinsearth/dsh-im2a-plugin
|
|
24
|
+
@openviking/dsh-memory-plugin
|
|
25
|
+
@twinsearth/w2m-dsh-plugin
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 行为
|
|
29
|
+
|
|
30
|
+
- 位置:`fang-forker` 插件 Config 的 `forkSeed`:
|
|
31
|
+
- `enabled`(默认 true);
|
|
32
|
+
- `install[]`、`activate[]`(默认值即上面的清单)。
|
|
33
|
+
- 时机:**每次 spawn**(首次派生与崩溃重启都会走)在启动 DSH 子进程前执行;
|
|
34
|
+
已按指纹缓存,首次安装后重启只重新打 profile patch,不重复安装。
|
|
35
|
+
- 安装:独立 seed home(`<forksRoot>/.plugin-seed/seed-<fingerprint>`)里用 DSH 自带 pnpm
|
|
36
|
+
执行 `dsh plugin --profile seed add ...`;模块树以 hardlink 合并进分身的 `node_modules`,
|
|
37
|
+
不替换 mother 镜像里的运行时包。
|
|
38
|
+
- 两种派生入口(开发者推荐 / 绿色“派生新分身”)都走同一个 `Forker.spawn`,因此清单一致。
|
|
39
|
+
- 失败不阻塞分身启动:结果在 spawn 返回值 `forkSeed`(`ok/error/detail/cached`)与审计中可见。
|
|
40
|
+
- 环境变量 `DSH_FANG_FORK_SEED=0` 可全局关闭(e2e/CI 使用,避免每次联网装包)。
|
|
41
|
+
|
|
42
|
+
## 自定义
|
|
43
|
+
|
|
44
|
+
在母亲 profile 的 Cordis 覆盖层里改 `fang-forker` 的 config,例如:
|
|
45
|
+
|
|
46
|
+
```yaml
|
|
47
|
+
- id: fang-forker
|
|
48
|
+
config:
|
|
49
|
+
forkSeed:
|
|
50
|
+
enabled: true
|
|
51
|
+
install:
|
|
52
|
+
- '@twinsearth/dsh-2xray'
|
|
53
|
+
activate:
|
|
54
|
+
- dshmarket
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
`install` 只装不激活;`activate` 会同时进依赖与 profile bundles。包名会做格式校验,
|
|
58
|
+
非法名(路径、参数注入)会被忽略。
|
|
59
|
+
|
|
60
|
+
## 边界
|
|
61
|
+
|
|
62
|
+
- 首次派生需要联网从 npm 安装;离线时分会话仍可启动,只是 `forkSeed.ok=false`。
|
|
63
|
+
- 仅作用于**任务分身**;元宝运行态(`essence-runtime`)继续继承元宝自己的模块树。
|
|
64
|
+
- 已在运行的旧分身在下次重启时补装(配置回放后重新打 patch)。
|