berry-agent 0.1.0-alpha.1
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/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/.api-emit.stamp +1 -0
- package/dist/.build-meta.json +6 -0
- package/dist/agent/events.js +1 -0
- package/dist/agent/index.js +2 -0
- package/dist/agent/loop.js +139 -0
- package/dist/agent/queue.js +119 -0
- package/dist/agent/stream.js +62 -0
- package/dist/agent/tools-batch.js +221 -0
- package/dist/agent/types.js +8 -0
- package/dist/api/berry-agent-llm.d.ts +18 -0
- package/dist/api/berry-agent.d.ts +9 -0
- package/dist/api/surface.json +1632 -0
- package/dist/api/tsconfig.paths.json +12 -0
- package/dist/api/typebox-compile.d.ts +2 -0
- package/dist/api/typebox-value.d.ts +2 -0
- package/dist/api/typebox.d.ts +2 -0
- package/dist/browser/cdp.js +256 -0
- package/dist/browser/codes.js +31 -0
- package/dist/browser/discover.js +107 -0
- package/dist/browser/engine.js +169 -0
- package/dist/browser/index.js +17 -0
- package/dist/browser/install.js +342 -0
- package/dist/browser/page.js +321 -0
- package/dist/browser/service.js +158 -0
- package/dist/browser/tools.js +198 -0
- package/dist/browser/types.js +81 -0
- package/dist/channels/ask-queue.js +82 -0
- package/dist/channels/commands.js +129 -0
- package/dist/channels/engine/cell.js +143 -0
- package/dist/channels/engine/diff.js +143 -0
- package/dist/channels/engine/engine.js +416 -0
- package/dist/channels/engine/index.js +8 -0
- package/dist/channels/engine/input-keys.js +86 -0
- package/dist/channels/engine/input.js +596 -0
- package/dist/channels/engine/memory-io.js +71 -0
- package/dist/channels/engine/process-io.js +38 -0
- package/dist/channels/engine/types.js +17 -0
- package/dist/channels/engine/width.js +122 -0
- package/dist/channels/index.js +15 -0
- package/dist/channels/registry.js +56 -0
- package/dist/channels/sdk/admit.js +29 -0
- package/dist/channels/sdk/backend.js +102 -0
- package/dist/channels/sdk/cursor.js +56 -0
- package/dist/channels/sdk/index.js +20 -0
- package/dist/channels/sdk/jsonl.js +208 -0
- package/dist/channels/sdk/protocol.js +24 -0
- package/dist/channels/sdk/schema.js +91 -0
- package/dist/channels/sdk/wire-core.js +446 -0
- package/dist/channels/service.js +169 -0
- package/dist/channels/tui/autocomplete/autocomplete.js +42 -0
- package/dist/channels/tui/autocomplete/file-mentions.js +106 -0
- package/dist/channels/tui/autocomplete/popup.js +148 -0
- package/dist/channels/tui/autocomplete/provider.js +7 -0
- package/dist/channels/tui/autocomplete/token.js +68 -0
- package/dist/channels/tui/backend/ansi-rows.js +211 -0
- package/dist/channels/tui/backend/main-screen.js +207 -0
- package/dist/channels/tui/backend/osc.js +91 -0
- package/dist/channels/tui/backend/transcript.js +230 -0
- package/dist/channels/tui/backend/tui-backend.js +915 -0
- package/dist/channels/tui/editor/editor-model.js +543 -0
- package/dist/channels/tui/editor/editor-view.js +140 -0
- package/dist/channels/tui/editor/editor.js +275 -0
- package/dist/channels/tui/editor/undo-stack.js +35 -0
- package/dist/channels/tui/editor/visual-lines.js +126 -0
- package/dist/channels/tui/editor/word-nav.js +106 -0
- package/dist/channels/tui/history/history-viewer.js +403 -0
- package/dist/channels/tui/index.js +3 -0
- package/dist/channels/tui/layout.js +167 -0
- package/dist/channels/tui/markdown/blocks.js +111 -0
- package/dist/channels/tui/markdown/inline.js +75 -0
- package/dist/channels/tui/markdown/markdown.js +182 -0
- package/dist/channels/tui/memory/memory-viewer.js +540 -0
- package/dist/channels/tui/overlay/alt-screen.js +105 -0
- package/dist/channels/tui/overlay/overlay.js +57 -0
- package/dist/channels/tui/overlay/select-confirm.js +156 -0
- package/dist/channels/tui/panels/todo-panel.js +54 -0
- package/dist/channels/tui/panels/tool-progress-panel.js +93 -0
- package/dist/channels/tui/scroll/scroll-view.js +254 -0
- package/dist/channels/tui/status/status-line.js +76 -0
- package/dist/channels/tui/text.js +38 -0
- package/dist/channels/tui/theme.js +41 -0
- package/dist/channels/types.js +11 -0
- package/dist/channels/ui-core.js +209 -0
- package/dist/checkpoint/capture.js +45 -0
- package/dist/checkpoint/codes.js +33 -0
- package/dist/checkpoint/command.js +82 -0
- package/dist/checkpoint/gate.js +48 -0
- package/dist/checkpoint/index.js +20 -0
- package/dist/checkpoint/restore.js +172 -0
- package/dist/checkpoint/store.js +247 -0
- package/dist/checkpoint/types.js +4 -0
- package/dist/checkpoint/walk.js +146 -0
- package/dist/compaction/ccr-tools.js +112 -0
- package/dist/compaction/ccr.js +84 -0
- package/dist/compaction/codes.js +21 -0
- package/dist/compaction/index.js +18 -0
- package/dist/compaction/policy.js +200 -0
- package/dist/compaction/service.js +422 -0
- package/dist/compaction/slots.js +121 -0
- package/dist/compaction/types.js +10 -0
- package/dist/context/codes.js +40 -0
- package/dist/context/events.js +160 -0
- package/dist/context/index.js +13 -0
- package/dist/context/logger.js +136 -0
- package/dist/context/scope.js +182 -0
- package/dist/context/workspace.js +104 -0
- package/dist/contracts/agent-events.d.ts +73 -0
- package/dist/contracts/agent-events.js +16 -0
- package/dist/contracts/api.d.ts +249 -0
- package/dist/contracts/api.js +299 -0
- package/dist/contracts/approval.d.ts +37 -0
- package/dist/contracts/approval.js +9 -0
- package/dist/contracts/env-ref.d.ts +41 -0
- package/dist/contracts/env-ref.js +32 -0
- package/dist/contracts/errors.d.ts +56 -0
- package/dist/contracts/errors.js +266 -0
- package/dist/contracts/events.d.ts +70 -0
- package/dist/contracts/events.js +329 -0
- package/dist/contracts/index.d.ts +32 -0
- package/dist/contracts/index.js +26 -0
- package/dist/contracts/llm.d.ts +275 -0
- package/dist/contracts/llm.js +12 -0
- package/dist/contracts/messages.d.ts +63 -0
- package/dist/contracts/messages.js +65 -0
- package/dist/contracts/redact.d.ts +46 -0
- package/dist/contracts/redact.js +223 -0
- package/dist/contracts/tools.d.ts +197 -0
- package/dist/contracts/tools.js +33 -0
- package/dist/contracts/types.d.ts +302 -0
- package/dist/contracts/types.js +50 -0
- package/dist/contracts/ui.d.ts +122 -0
- package/dist/contracts/ui.js +15 -0
- package/dist/conversation/agent-service.js +61 -0
- package/dist/conversation/approval-wiring.js +82 -0
- package/dist/conversation/backoff.js +38 -0
- package/dist/conversation/codes.js +47 -0
- package/dist/conversation/control-tools.js +102 -0
- package/dist/conversation/control.js +210 -0
- package/dist/conversation/driver.js +922 -0
- package/dist/conversation/index.js +12 -0
- package/dist/conversation/model-visible.js +169 -0
- package/dist/conversation/open-tools.js +126 -0
- package/dist/conversation/reseed.js +132 -0
- package/dist/conversation/sessions.js +215 -0
- package/dist/conversation/todo.js +203 -0
- package/dist/conversation/types.js +35 -0
- package/dist/conversation/wiring.js +204 -0
- package/dist/credentials/codes.js +59 -0
- package/dist/credentials/commands.js +186 -0
- package/dist/credentials/env-ref.js +34 -0
- package/dist/credentials/index.js +28 -0
- package/dist/credentials/migration.js +18 -0
- package/dist/credentials/oauth.js +266 -0
- package/dist/credentials/refresh.js +152 -0
- package/dist/credentials/secrets.js +118 -0
- package/dist/credentials/types.js +30 -0
- package/dist/exec/bash.js +279 -0
- package/dist/exec/codes.js +37 -0
- package/dist/exec/env.js +99 -0
- package/dist/exec/environment.js +39 -0
- package/dist/exec/git-guard.js +623 -0
- package/dist/exec/index.js +24 -0
- package/dist/exec/registry.js +120 -0
- package/dist/exec/spawn.js +281 -0
- package/dist/exec/tail.js +77 -0
- package/dist/exec/types.js +5 -0
- package/dist/goal/codes.js +33 -0
- package/dist/goal/command.js +175 -0
- package/dist/goal/fold.js +121 -0
- package/dist/goal/gates.js +122 -0
- package/dist/goal/index.js +22 -0
- package/dist/goal/migration.js +48 -0
- package/dist/goal/service.js +501 -0
- package/dist/goal/todo-tool.js +174 -0
- package/dist/goal/types.js +1 -0
- package/dist/goal/update-tool.js +46 -0
- package/dist/host/approval-cmd.js +276 -0
- package/dist/host/assembly.js +1146 -0
- package/dist/host/boot-failures.js +104 -0
- package/dist/host/budget-advisory.js +74 -0
- package/dist/host/budget-broadcast.js +50 -0
- package/dist/host/builtins.js +54 -0
- package/dist/host/cli.js +544 -0
- package/dist/host/codes.js +120 -0
- package/dist/host/config-schema.js +195 -0
- package/dist/host/conversation-stack.js +654 -0
- package/dist/host/core-plugins.js +1685 -0
- package/dist/host/credentials-cmd.js +42 -0
- package/dist/host/disclosure.js +30 -0
- package/dist/host/dispatch.js +105 -0
- package/dist/host/doors-cmd.js +154 -0
- package/dist/host/dump-config.js +44 -0
- package/dist/host/hook-dispatch-guard.js +27 -0
- package/dist/host/import-gate.js +153 -0
- package/dist/host/index.js +54 -0
- package/dist/host/issue-session.js +241 -0
- package/dist/host/loader.js +372 -0
- package/dist/host/main.js +161 -0
- package/dist/host/manifest.js +381 -0
- package/dist/host/mcp-entry.js +53 -0
- package/dist/host/plugin-boot.js +952 -0
- package/dist/host/plugin-context.js +672 -0
- package/dist/host/plugin-install.js +480 -0
- package/dist/host/plugin-reload.js +124 -0
- package/dist/host/plugin-store.js +462 -0
- package/dist/host/plugin-tools.js +359 -0
- package/dist/host/plugin-uninstall.js +257 -0
- package/dist/host/plugins-cmd.js +302 -0
- package/dist/host/plugins-command.js +130 -0
- package/dist/host/plugins-config.js +195 -0
- package/dist/host/prompt-sections.js +133 -0
- package/dist/host/run-entry.js +544 -0
- package/dist/host/runtime.js +209 -0
- package/dist/host/scheduler-tick.js +294 -0
- package/dist/host/serve-daemon.js +501 -0
- package/dist/host/serve-entry.js +279 -0
- package/dist/host/session-anchor.js +41 -0
- package/dist/host/sessions-cmd.js +219 -0
- package/dist/host/sessions-face.js +47 -0
- package/dist/host/settings-store.js +112 -0
- package/dist/host/signals.js +69 -0
- package/dist/host/single-instance.js +105 -0
- package/dist/host/subagent-factory.js +291 -0
- package/dist/host/testkit/harness.js +283 -0
- package/dist/host/testkit/index.js +14 -0
- package/dist/host/testkit/matrix.js +302 -0
- package/dist/host/tool-policy-store.js +177 -0
- package/dist/host/triggers.js +212 -0
- package/dist/host/tui-entry.js +216 -0
- package/dist/host/webui-bridge.js +165 -0
- package/dist/issue/codes.js +38 -0
- package/dist/issue/filter.js +146 -0
- package/dist/issue/github.js +116 -0
- package/dist/issue/index.js +17 -0
- package/dist/issue/mount.js +70 -0
- package/dist/issue/poll.js +58 -0
- package/dist/issue/service.js +341 -0
- package/dist/issue/tools.js +70 -0
- package/dist/issue/types.js +34 -0
- package/dist/issue/webhook.js +126 -0
- package/dist/llm/codes.js +77 -0
- package/dist/llm/complete.js +196 -0
- package/dist/llm/events.js +15 -0
- package/dist/llm/index.js +24 -0
- package/dist/llm/inflight.js +65 -0
- package/dist/llm/model-id.js +58 -0
- package/dist/llm/provider-face.d.ts +32 -0
- package/dist/llm/provider-face.js +34 -0
- package/dist/llm/recovery.js +146 -0
- package/dist/llm/runtime.js +46 -0
- package/dist/llm/stream-fn.js +147 -0
- package/dist/lsp/codes.js +31 -0
- package/dist/lsp/connection.js +147 -0
- package/dist/lsp/frame.js +110 -0
- package/dist/lsp/index.js +16 -0
- package/dist/lsp/inject.js +58 -0
- package/dist/lsp/instance.js +264 -0
- package/dist/lsp/service.js +522 -0
- package/dist/lsp/tools.js +53 -0
- package/dist/lsp/types.js +201 -0
- package/dist/mcp/bridge.js +222 -0
- package/dist/mcp/codes.js +21 -0
- package/dist/mcp/index.js +19 -0
- package/dist/mcp/jsonrpc.js +230 -0
- package/dist/mcp/service.js +125 -0
- package/dist/mcp/tools.js +145 -0
- package/dist/mcp/types.js +108 -0
- package/dist/memory/cite.js +131 -0
- package/dist/memory/codes.js +50 -0
- package/dist/memory/command.js +71 -0
- package/dist/memory/consolidate.js +219 -0
- package/dist/memory/cycle.js +109 -0
- package/dist/memory/dao.js +878 -0
- package/dist/memory/diff.js +238 -0
- package/dist/memory/extract.js +181 -0
- package/dist/memory/fts.js +58 -0
- package/dist/memory/index.js +42 -0
- package/dist/memory/inject.js +354 -0
- package/dist/memory/merge.js +178 -0
- package/dist/memory/migration.js +132 -0
- package/dist/memory/pollution.js +59 -0
- package/dist/memory/port.js +385 -0
- package/dist/memory/review.js +201 -0
- package/dist/memory/scan.js +89 -0
- package/dist/memory/tools.js +508 -0
- package/dist/memory/types.js +140 -0
- package/dist/obs/codes.js +38 -0
- package/dist/obs/db.js +115 -0
- package/dist/obs/index.js +20 -0
- package/dist/obs/rollup.js +61 -0
- package/dist/obs/service.js +363 -0
- package/dist/obs/session-tools.js +282 -0
- package/dist/obs/session-view.js +225 -0
- package/dist/obs/tool.js +97 -0
- package/dist/obs/types.js +1 -0
- package/dist/persist/audit.js +63 -0
- package/dist/persist/aux.js +39 -0
- package/dist/persist/codes.js +35 -0
- package/dist/persist/index.js +30 -0
- package/dist/persist/load-history.js +50 -0
- package/dist/persist/migrations.js +37 -0
- package/dist/persist/paths.js +108 -0
- package/dist/persist/persistence.js +229 -0
- package/dist/persist/schema.js +99 -0
- package/dist/persist/secret-box.js +95 -0
- package/dist/persist/store.js +706 -0
- package/dist/persist/write-behind.js +238 -0
- package/dist/safety/approval.js +171 -0
- package/dist/safety/bwrap.js +123 -0
- package/dist/safety/codes.js +57 -0
- package/dist/safety/danger.js +505 -0
- package/dist/safety/gate.js +226 -0
- package/dist/safety/index.js +24 -0
- package/dist/safety/presets.js +73 -0
- package/dist/safety/roots.js +139 -0
- package/dist/safety/sandbox.js +214 -0
- package/dist/safety/seatbelt.js +88 -0
- package/dist/safety/sensitive.js +34 -0
- package/dist/safety/tool-policy.js +203 -0
- package/dist/safety/types.js +13 -0
- package/dist/scheduler/codes.js +54 -0
- package/dist/scheduler/cron-backend.js +135 -0
- package/dist/scheduler/engine.js +280 -0
- package/dist/scheduler/gates.js +60 -0
- package/dist/scheduler/index.js +25 -0
- package/dist/scheduler/migration.js +22 -0
- package/dist/scheduler/runner.js +171 -0
- package/dist/scheduler/schedule.js +193 -0
- package/dist/scheduler/service.js +283 -0
- package/dist/scheduler/tick.js +139 -0
- package/dist/scheduler/types.js +1 -0
- package/dist/sdk/http.js +865 -0
- package/dist/sdk/index.js +22 -0
- package/dist/sdk/mcp.js +289 -0
- package/dist/sdk/plugin-route-registry.js +126 -0
- package/dist/sdk/plugin-routes.js +149 -0
- package/dist/sdk/security.js +116 -0
- package/dist/sdk/types.js +32 -0
- package/dist/session/budget.js +159 -0
- package/dist/session/codes.js +40 -0
- package/dist/session/derive.js +180 -0
- package/dist/session/event-data.js +1 -0
- package/dist/session/fork.js +72 -0
- package/dist/session/import-gates.js +148 -0
- package/dist/session/index.js +18 -0
- package/dist/session/recover.js +103 -0
- package/dist/session/session.js +279 -0
- package/dist/session/snapshot.js +107 -0
- package/dist/skills/agents.js +242 -0
- package/dist/skills/codes.js +51 -0
- package/dist/skills/discovery.js +266 -0
- package/dist/skills/frontmatter.js +215 -0
- package/dist/skills/index.js +28 -0
- package/dist/skills/load.js +118 -0
- package/dist/skills/manage.js +201 -0
- package/dist/skills/registry.js +138 -0
- package/dist/skills/render.js +124 -0
- package/dist/skills/sections.js +171 -0
- package/dist/skills/types.js +21 -0
- package/dist/subagent/codes.js +50 -0
- package/dist/subagent/declarative.js +55 -0
- package/dist/subagent/index.js +19 -0
- package/dist/subagent/notify.js +51 -0
- package/dist/subagent/provide.js +7 -0
- package/dist/subagent/registry.js +149 -0
- package/dist/subagent/service.js +338 -0
- package/dist/subagent/surface.js +44 -0
- package/dist/subagent/tool.js +153 -0
- package/dist/subagent/types.js +25 -0
- package/dist/tools/apply-patch.js +164 -0
- package/dist/tools/codes.js +109 -0
- package/dist/tools/fs.js +501 -0
- package/dist/tools/index.js +17 -0
- package/dist/tools/observed.js +99 -0
- package/dist/tools/pipeline.js +176 -0
- package/dist/tools/protected-read.js +54 -0
- package/dist/tools/registry.js +250 -0
- package/dist/tools/search.js +451 -0
- package/dist/tools/worktree.js +327 -0
- package/dist/web/codes.js +37 -0
- package/dist/web/gate.js +43 -0
- package/dist/web/hygiene.js +195 -0
- package/dist/web/index.js +16 -0
- package/dist/web/service.js +207 -0
- package/dist/web/ssrf-guard.js +44 -0
- package/dist/web/tool.js +76 -0
- package/dist/web/types.js +10 -0
- package/dist/webui/assets/index-B-t2O8gh.js +22 -0
- package/dist/webui/assets/index-DOlgSZ9Q.css +2 -0
- package/dist/webui/index.html +14 -0
- package/dist/webui/index.js +14 -0
- package/dist/webui/server.js +549 -0
- package/dist/webui/types.js +38 -0
- package/examples/README.md +26 -0
- package/examples/minimal-code-plugin/entry.js +43 -0
- package/examples/minimal-code-plugin/package.json +12 -0
- package/examples/pure-skill-pack/package.json +14 -0
- package/examples/pure-skill-pack/skills/markdown-table/SKILL.md +32 -0
- package/package.json +86 -0
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* host/main — bin 入口装配位(package.json bin = berry-agent → dist/host/main.js)。
|
|
4
|
+
*
|
|
5
|
+
* 薄壳三件事:装崩溃编舞(前置窗口直写 crash.log——运行时组装后切
|
|
6
|
+
* runtime.writeCrashLog)→ 装信号编舞(SIGINT②→130 / SIGTERM 优雅序——
|
|
7
|
+
* 04 §1)→ parseCli + dispatchCli 分派 + process.exit。
|
|
8
|
+
*
|
|
9
|
+
* 本批(12c)执行器族全空——除 help/version 外一切命令诚实退 1「执行面
|
|
10
|
+
* 尚未装配」(错误路径及时非零退出铁律);12d(装载器)/conversation
|
|
11
|
+
* 装配批/12e(TUI)逐席充实 handlers 并在组装运行时后挂 attachRuntime
|
|
12
|
+
* (信号/崩溃编舞切运行时本体)。
|
|
13
|
+
*
|
|
14
|
+
* 自动执行卫兵:仅作为主模块直跑时执行(import.meta.url 对 argv[1])——
|
|
15
|
+
* 被 import(测试/程序化装配)不触发副作用。tsc 直出保留首行 shebang,
|
|
16
|
+
* bin 执行位由 npm pack 装配。
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, realpathSync } from 'node:fs';
|
|
19
|
+
import { argv, exit as processExit, stderr, stdin, stdout } from 'node:process';
|
|
20
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
21
|
+
import { resolveDataDir } from '../persist/index.js';
|
|
22
|
+
import { parseCli } from './cli.js';
|
|
23
|
+
import { dispatchCli } from './dispatch.js';
|
|
24
|
+
import { appendCrashLog } from './runtime.js';
|
|
25
|
+
import { installCrashChoreography, installSignalChoreography } from './signals.js';
|
|
26
|
+
import { DAEMON_CHILD_ENV, runDaemonServe, runServeStatus, runServeStop, spawnDaemonServe } from './serve-daemon.js';
|
|
27
|
+
import { runServeEntry } from './serve-entry.js';
|
|
28
|
+
import { runMcpEntry } from './mcp-entry.js';
|
|
29
|
+
import { runTuiEntry } from './tui-entry.js';
|
|
30
|
+
import { runRunEntry } from './run-entry.js';
|
|
31
|
+
import { runDumpConfigEntry } from './dump-config.js';
|
|
32
|
+
import { runPluginsEntry } from './plugins-cmd.js';
|
|
33
|
+
import { runSessionsEntry } from './sessions-cmd.js';
|
|
34
|
+
import { runCredentialsEntry } from './credentials-cmd.js';
|
|
35
|
+
import { runDoorsEntry } from './doors-cmd.js';
|
|
36
|
+
/** 在飞运行时柄(组装后挂入——信号/崩溃编舞切运行时本体;前置窗口 null) */
|
|
37
|
+
let activeRuntime = null;
|
|
38
|
+
/** 装配序挂入运行时(12d/12e 与 conversation 装配批的执行器在组装后调用) */
|
|
39
|
+
export function attachRuntime(runtime) {
|
|
40
|
+
activeRuntime = runtime;
|
|
41
|
+
}
|
|
42
|
+
/** 读包版本(裸 semver——bin 与包根两级上溯;src 与 dist 同相对深度) */
|
|
43
|
+
function readVersion() {
|
|
44
|
+
const pkgUrl = new URL('../../package.json', import.meta.url);
|
|
45
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(pkgUrl), 'utf8'));
|
|
46
|
+
return pkg.version ?? '0.0.0-unknown';
|
|
47
|
+
}
|
|
48
|
+
/** serve 分派三态:daemon child(env 标记)→ spawner(--daemon)→ stdio 前台 */
|
|
49
|
+
function dispatchServe(flags) {
|
|
50
|
+
if (process.env[DAEMON_CHILD_ENV] === '1') {
|
|
51
|
+
// child 形态:HTTP 面常驻主体(spawner 已 detached + stderr 重定向 log)
|
|
52
|
+
return runDaemonServe({ flags, version: readVersion(), onRuntime: attachRuntime });
|
|
53
|
+
}
|
|
54
|
+
if (flags.daemon) {
|
|
55
|
+
// spawner 形态:spawn 自镜像后即退(起活确认窗内报结果;child 独立生命周期)
|
|
56
|
+
return spawnDaemonServe({ flags });
|
|
57
|
+
}
|
|
58
|
+
return runServeEntry({ flags, version: readVersion(), onRuntime: attachRuntime });
|
|
59
|
+
}
|
|
60
|
+
/** 执行器族(12c 空起——逐批充实;12e TUI / 13c serve stdio / 13e-3 daemon 编舞 + status/stop / 13f mcp 包装 / 12f-3 dump-config + plugins / 20b run / 20d sessions / g-2 doors 已接线,upgrade 诚实退 1) */
|
|
61
|
+
const handlers = {
|
|
62
|
+
tui: (flags) => runTuiEntry({
|
|
63
|
+
flags,
|
|
64
|
+
version: readVersion(), // OSC title 基线真值(批 12 挂账兑现)
|
|
65
|
+
onRuntime: attachRuntime, // 信号/崩溃编舞切运行时本体
|
|
66
|
+
}),
|
|
67
|
+
serve: (flags) => dispatchServe(flags),
|
|
68
|
+
// run 单次执行(20b 接线——07 §5 run 旗标族全量消费位;tick 形 message 为
|
|
69
|
+
// 空串占位——提示词在 jobs 行内,由 run-entry 经行读取)
|
|
70
|
+
run: (message, flags) => runRunEntry({
|
|
71
|
+
message,
|
|
72
|
+
flags,
|
|
73
|
+
version: readVersion(),
|
|
74
|
+
onRuntime: attachRuntime, // 信号/崩溃编舞切运行时本体
|
|
75
|
+
}),
|
|
76
|
+
serveStatus: () => runServeStatus({ dataDir: resolveDataDir() }),
|
|
77
|
+
serveStop: () => runServeStop({ dataDir: resolveDataDir() }),
|
|
78
|
+
// MCP server 包装形态(13f 接线——零旗标面;serverInfo 版本真值同 tui 路径)
|
|
79
|
+
mcp: () => runMcpEntry({
|
|
80
|
+
version: readVersion(),
|
|
81
|
+
onRuntime: attachRuntime, // 信号/崩溃编舞切运行时本体
|
|
82
|
+
}),
|
|
83
|
+
// dump-config :memory: 同构诊断(12f-3——07 §5 禁侧门纪律,assembly 公共段)
|
|
84
|
+
dumpConfig: (flags) => runDumpConfigEntry({
|
|
85
|
+
flags,
|
|
86
|
+
version: readVersion(),
|
|
87
|
+
onRuntime: attachRuntime,
|
|
88
|
+
}),
|
|
89
|
+
// plugins 子命令族 CLI 面(12f-3——list 同构装配 / check 纯只读骨架 / 写侧六动词诚实退 1)
|
|
90
|
+
plugins: (sub) => runPluginsEntry(sub, {
|
|
91
|
+
version: readVersion(),
|
|
92
|
+
onRuntime: attachRuntime,
|
|
93
|
+
}),
|
|
94
|
+
// sessions 子命令族 CLI 面(20d——list/search/reindex 读腿零装配、resume 进
|
|
95
|
+
// TUI〔resumeSessionId 载体〕、fork 全装配同 run --fork 机;07 §5 定名)
|
|
96
|
+
sessions: (sub) => runSessionsEntry(sub, {
|
|
97
|
+
version: readVersion(),
|
|
98
|
+
onRuntime: attachRuntime, // fork/resume 装配路径——信号/崩溃编舞切运行时本体
|
|
99
|
+
}),
|
|
100
|
+
// credentials 子命令族 CLI 面(c-5——03 §10.9 人面命令:零装配直开库,
|
|
101
|
+
// 动词语义单源 credentials/commands 与 TUI /credentials 同底座)
|
|
102
|
+
credentials: (sub) => runCredentialsEntry(sub, {}),
|
|
103
|
+
// doors 子命令族 CLI 面(g-2——03 §4.6 / 07 §5 定名:list 只读零装配零库
|
|
104
|
+
// 纯文件读;open/close 合法解析形、执行层语义拒退 1——写动词 TUI /doors 专属)
|
|
105
|
+
doors: (sub) => runDoorsEntry(sub, {}),
|
|
106
|
+
};
|
|
107
|
+
/** 主序:编舞装配 → 分派 → 终局 */
|
|
108
|
+
async function main() {
|
|
109
|
+
installCrashChoreography({
|
|
110
|
+
// 前置窗口(运行时未组装)直写缺省数据目录;组装后走运行时本体(memory 形跳过语义在其内)
|
|
111
|
+
writeCrashLog: (error) => {
|
|
112
|
+
if (activeRuntime !== null)
|
|
113
|
+
activeRuntime.writeCrashLog(error);
|
|
114
|
+
else
|
|
115
|
+
appendCrashLog(resolveDataDir(), error);
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
installSignalChoreography({
|
|
119
|
+
onGraceful: async () => {
|
|
120
|
+
await activeRuntime?.shutdown(); // 未组装 = 无优雅序可走,直退 0
|
|
121
|
+
return 0;
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
return dispatchCli(parseCli(argv.slice(2)), handlers, {
|
|
125
|
+
stdinIsTTY: stdin.isTTY === true,
|
|
126
|
+
stdoutIsTTY: stdout.isTTY === true,
|
|
127
|
+
writeOut: (text) => stdout.write(`${text}\n`),
|
|
128
|
+
writeErr: (text) => stderr.write(`${text}\n`),
|
|
129
|
+
version: readVersion(),
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// 自动执行卫兵:仅主模块直跑(进程入口)才走主序——被 import 零副作用。
|
|
133
|
+
// argv[1] 先 realpath 归一:入口模块 node 侧恒走真实路径解析,而 argv[1] 是
|
|
134
|
+
// 调用面字面量——npm bin 符号链接或路径含符号链接环节(/tmp → /private/tmp、
|
|
135
|
+
// nvm/volta shim 目录)两侧错配即静默 no-op 退 0(2026-09-08 发布面收口批
|
|
136
|
+
// 安装冒烟逮到——真实装机路径含符号链接即坏,发布阻塞件)
|
|
137
|
+
/**
|
|
138
|
+
* 主模块判定(bin 直跑卫兵)——argv[1] realpath 归一后对模块 URL 全等比对。
|
|
139
|
+
* @param argv1 进程 argv[1](调用面字面量——可能是符号链接或含链接环节的路径)
|
|
140
|
+
* @param moduleUrl 本模块 import.meta.url(node 入口解析恒为真实路径)
|
|
141
|
+
*/
|
|
142
|
+
export function isMainModule(argv1, moduleUrl) {
|
|
143
|
+
if (argv1 === undefined)
|
|
144
|
+
return false;
|
|
145
|
+
try {
|
|
146
|
+
return pathToFileURL(realpathSync(argv1)).href === moduleUrl;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false; // argv[1] 已不可解析(极端竞态)——按非主模块退
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const isMain = isMainModule(argv[1], import.meta.url);
|
|
153
|
+
if (isMain) {
|
|
154
|
+
void main()
|
|
155
|
+
.then((code) => processExit(code))
|
|
156
|
+
.catch((err) => {
|
|
157
|
+
// 主序自身抛错(编舞装配前的窄窗)——stderr 一行 + 执行失败档退
|
|
158
|
+
stderr.write(`启动失败:${err instanceof Error ? err.message : String(err)}\n`);
|
|
159
|
+
processExit(1);
|
|
160
|
+
});
|
|
161
|
+
}
|
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* host/manifest — 插件清单契约与拒绝式形状校验(03 篇 §1.2/§1.3/§5.3)。
|
|
3
|
+
*
|
|
4
|
+
* 契约先行笔(批 12a):形状类型 + 校验纯函数零副作用——jiti 装载、虚拟键
|
|
5
|
+
* 注入、时钟护栏等装载器机制归批 12d 实装笔消费本面。校验一律**拒绝式**
|
|
6
|
+
* (未知键拒载、坏形当场红——拼写错误不留静默通道),失败面统一 tagged
|
|
7
|
+
* union 不抛(调用方——装机腿/boot 合成腿——决定 fail-loud 还是行级隔离)。
|
|
8
|
+
*
|
|
9
|
+
* 复用件:清单 api 块形状与版本串判据单源在 contracts(ApiBlock /
|
|
10
|
+
* isValidApiVersion——§8.4 装载门同判据,不散拷)。
|
|
11
|
+
*/
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { parseConfigSchemaFields } from './config-schema.js';
|
|
14
|
+
import { isValidApiVersion } from '../contracts/index.js';
|
|
15
|
+
// internal 桶机制符号深导(opens 授予位值域单源——02 §4.3 #2 深挖面册纪律)
|
|
16
|
+
import { DOORS_SEGMENT_V1_DOMAIN, USER_GRANTABLE_CAPABILITIES } from '../contracts/api.js';
|
|
17
|
+
/**
|
|
18
|
+
* 清单键目录(API 治理真相源⑤——03 §8.2「清单键与类型」;抽取器 jiti 导入
|
|
19
|
+
* 收割进 surface.json 的 manifest-keys 域)。校验闭集自本目录派生——键表增删
|
|
20
|
+
* 两路(目录 / parseManifest 未知键执法)同笔共变,结构性不可漂移。tier 全
|
|
21
|
+
* stable:清单键是插件作者依赖面(§1.2 拍板定形;§8.3「现役全 stable」同律)。
|
|
22
|
+
*/
|
|
23
|
+
export const MANIFEST_KEY_CATALOG = [
|
|
24
|
+
{ key: 'id', tier: 'stable', desc: '插件 id;缺省取 package.json name,不合字符集须显式声明(无隐式映射变换)' },
|
|
25
|
+
{ key: 'label', tier: 'stable', desc: '展示名;缺省取 id' },
|
|
26
|
+
{ key: 'entry', tier: 'stable', desc: '入口文件(相对包根);缺席走入口解析序三步' },
|
|
27
|
+
{ key: 'grants', tier: 'stable', desc: '授权申请面;单维 writableRoots 字符串数组' },
|
|
28
|
+
{
|
|
29
|
+
key: 'config',
|
|
30
|
+
tier: 'stable',
|
|
31
|
+
desc: '宿主侧默认配置值(启用行 config 缺席时作 apply 的 config 实参;形状声明归 configSchema——2026-09-11 交互动词族批 schema/值分键)',
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
key: 'configSchema',
|
|
35
|
+
tier: 'stable',
|
|
36
|
+
desc: '配置字段声明面(字段描述数组——字段级校验与 /plugins config 表单渲染双消费源)',
|
|
37
|
+
},
|
|
38
|
+
{ key: 'api', tier: 'stable', desc: 'API 治理块(minApiVersion/targetApiVersion/experimental)' },
|
|
39
|
+
{ key: 'skills', tier: 'stable', desc: '技能目录清单;非空即在场的「声明载荷」之一(纯声明包零码装载)' },
|
|
40
|
+
{
|
|
41
|
+
key: 'agents',
|
|
42
|
+
tier: 'stable',
|
|
43
|
+
desc: '声明式子代理目录清单(06 §11.6 位 4;与 skills 同形——mount 即收集、物化消费腿挂 core:subagent 消费批)',
|
|
44
|
+
},
|
|
45
|
+
];
|
|
46
|
+
/** 清单块已知键闭集(未知键拒载——拒绝式而非忽略式;自目录派生单源) */
|
|
47
|
+
const MANIFEST_KEYS = new Set(MANIFEST_KEY_CATALOG.map((k) => k.key));
|
|
48
|
+
/** 插件 id 字符集:小写字母/数字/连字符,首字符非连字符(03 §1.2) */
|
|
49
|
+
const PLUGIN_ID_RE = /^[a-z0-9][a-z0-9-]*$/;
|
|
50
|
+
/** 官方前缀(core: 为官方插件保留——冒号仅许官方前缀位) */
|
|
51
|
+
const CORE_PREFIX = 'core:';
|
|
52
|
+
/**
|
|
53
|
+
* id 字符集校验(03 §1.2 前缀豁免句)。
|
|
54
|
+
*
|
|
55
|
+
* - 用户插件(official=false):整串过字符集且**不含冒号**——起头 `core:` 或
|
|
56
|
+
* 含 `:` 一律拒(官方前缀保留 + 冒号仅许官方前缀位);
|
|
57
|
+
* - 官方引用形(official=true,§1.4——core: 形状与磁盘插件同轨校验):id 形如
|
|
58
|
+
* `core:<name>` 时校验对象 = `<name>` 段过同一字符集。
|
|
59
|
+
*/
|
|
60
|
+
export function checkPluginId(id, opts) {
|
|
61
|
+
if (opts.official) {
|
|
62
|
+
if (!id.startsWith(CORE_PREFIX))
|
|
63
|
+
return PLUGIN_ID_RE.test(id) && !id.includes(':');
|
|
64
|
+
const name = id.slice(CORE_PREFIX.length);
|
|
65
|
+
return name.length > 0 && PLUGIN_ID_RE.test(name);
|
|
66
|
+
}
|
|
67
|
+
if (id.includes(':'))
|
|
68
|
+
return false;
|
|
69
|
+
return PLUGIN_ID_RE.test(id);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 解析并校验插件清单(纯函数)。
|
|
73
|
+
*
|
|
74
|
+
* 输入 = package.json 解析产物(JSON 对象——宿主侧 json/yaml 载体差异由调用方
|
|
75
|
+
* 抹平);`official` 旗区分用户磁盘插件与 core: 官方引用形(§1.4 对象直调形态
|
|
76
|
+
* 的清单面也经本校验——同轨纪律)。失败一律 `PLUGIN_SHAPE_INVALID`,message
|
|
77
|
+
* 自带指路(缺省 id 不合字符集时指路清单显式声明 `berryAgent.id`——无隐式
|
|
78
|
+
* 映射变换,§1.2 拍板题 10)。
|
|
79
|
+
*/
|
|
80
|
+
export function parseManifest(pkg, opts = {}) {
|
|
81
|
+
const official = opts.official === true;
|
|
82
|
+
if (typeof pkg !== 'object' || pkg === null || Array.isArray(pkg)) {
|
|
83
|
+
return shapeFail('package.json 顶层须为对象');
|
|
84
|
+
}
|
|
85
|
+
const record = pkg;
|
|
86
|
+
const name = record['name'];
|
|
87
|
+
if (typeof name !== 'string' || name.length === 0) {
|
|
88
|
+
return shapeFail('package.json name 缺席或非非空字符串——插件包身份不可判定');
|
|
89
|
+
}
|
|
90
|
+
const block = record['berryAgent'];
|
|
91
|
+
if (block === undefined) {
|
|
92
|
+
// berryAgent 块缺席的包不是插件——install 拒绝(03 §1.2)
|
|
93
|
+
return shapeFail(`包 ${name} 无 berryAgent 字段——不是插件包(清单载体 = package.json 加字段,禁双载体)`);
|
|
94
|
+
}
|
|
95
|
+
if (typeof block !== 'object' || block === null || Array.isArray(block)) {
|
|
96
|
+
return shapeFail(`berryAgent 块须为对象(包 ${name})`);
|
|
97
|
+
}
|
|
98
|
+
const manifest = block;
|
|
99
|
+
// 未知键拒载——拒绝式而非忽略式(拼写错误当场红)
|
|
100
|
+
for (const key of Object.keys(manifest)) {
|
|
101
|
+
if (!MANIFEST_KEYS.has(key)) {
|
|
102
|
+
return shapeFail(`berryAgent 未知键 "${key}"(包 ${name})——已知键闭集:${[...MANIFEST_KEYS].join('/')}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// id:在场须字符串;缺省 = name;不合字符集即拒(缺省不合时指路显式声明)
|
|
106
|
+
let id;
|
|
107
|
+
if (manifest['id'] !== undefined) {
|
|
108
|
+
if (typeof manifest['id'] !== 'string' || manifest['id'].length === 0) {
|
|
109
|
+
return shapeFail(`berryAgent.id 须为非空字符串(包 ${name})`);
|
|
110
|
+
}
|
|
111
|
+
id = manifest['id'];
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
id = name;
|
|
115
|
+
}
|
|
116
|
+
if (!checkPluginId(id, { official })) {
|
|
117
|
+
return shapeFail(manifest['id'] === undefined
|
|
118
|
+
? `缺省 id(= package.json name "${name}")不合插件 id 字符集${official ? '' : '(含冒号或 core: 前缀保留)'}——在清单 berryAgent.id 显式声明合法 id(小写字母/数字/连字符,首字符非连字符;无隐式映射变换)`
|
|
119
|
+
: `berryAgent.id "${id}" 不合插件 id 字符集(小写字母/数字/连字符,首字符非连字符${official ? ';core: 前缀后段同判据' : ';冒号仅许官方前缀位——core: 为官方插件保留'})`);
|
|
120
|
+
}
|
|
121
|
+
// label:缺省 = id;须字符串
|
|
122
|
+
let label = id;
|
|
123
|
+
if (manifest['label'] !== undefined) {
|
|
124
|
+
if (typeof manifest['label'] !== 'string' || manifest['label'].length === 0) {
|
|
125
|
+
return shapeFail(`berryAgent.label 须为非空字符串(插件 ${id})`);
|
|
126
|
+
}
|
|
127
|
+
label = manifest['label'];
|
|
128
|
+
}
|
|
129
|
+
// version(package.json 顶层透传——git 源可缺席)
|
|
130
|
+
const version = record['version'];
|
|
131
|
+
if (version !== undefined && typeof version !== 'string') {
|
|
132
|
+
return shapeFail(`package.json version 须为字符串(插件 ${id})`);
|
|
133
|
+
}
|
|
134
|
+
// entry:须字符串
|
|
135
|
+
if (manifest['entry'] !== undefined && (typeof manifest['entry'] !== 'string' || manifest['entry'].length === 0)) {
|
|
136
|
+
return shapeFail(`berryAgent.entry 须为非空字符串(插件 ${id})`);
|
|
137
|
+
}
|
|
138
|
+
// grants:须对象(深校验 writableRoots 形随装载器批——本笔浅校验)
|
|
139
|
+
if (manifest['grants'] !== undefined) {
|
|
140
|
+
const grants = manifest['grants'];
|
|
141
|
+
if (typeof grants !== 'object' || grants === null || Array.isArray(grants)) {
|
|
142
|
+
return shapeFail(`berryAgent.grants 须为对象(插件 ${id}——申请面 { writableRoots: string[] })`);
|
|
143
|
+
}
|
|
144
|
+
const roots = grants['writableRoots'];
|
|
145
|
+
if (roots !== undefined && (!Array.isArray(roots) || roots.some((r) => typeof r !== 'string'))) {
|
|
146
|
+
return shapeFail(`berryAgent.grants.writableRoots 须为字符串数组(插件 ${id})`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// config:须对象(宿主侧默认配置值——schema/值分键后值单源;secret 明文拒在 configSchema 解析后交叉判)
|
|
150
|
+
if (manifest['config'] !== undefined) {
|
|
151
|
+
const config = manifest['config'];
|
|
152
|
+
if (typeof config !== 'object' || config === null || Array.isArray(config)) {
|
|
153
|
+
return shapeFail(`berryAgent.config 须为对象(插件 ${id}——宿主侧默认配置值)`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// configSchema:字段描述数组深校验(ix-3——03 §1.2 configSchema 条款)+ 清单侧 secret 明文拒
|
|
157
|
+
let configSchema;
|
|
158
|
+
if (manifest['configSchema'] !== undefined) {
|
|
159
|
+
const parsed = parseConfigSchemaFields(manifest['configSchema'], { pluginId: id });
|
|
160
|
+
if (!parsed.ok) {
|
|
161
|
+
return shapeFail(parsed.message);
|
|
162
|
+
}
|
|
163
|
+
configSchema = parsed.fields;
|
|
164
|
+
// secret 明文拒(清单侧半边——宿主默认值位携 secret 键明文值拒;行 config 半边归装载器)
|
|
165
|
+
if (manifest['config'] !== undefined) {
|
|
166
|
+
const defaults = manifest['config'];
|
|
167
|
+
const leaked = configSchema.filter((f) => f.type === 'secret' && f.key in defaults).map((f) => f.key);
|
|
168
|
+
if (leaked.length > 0) {
|
|
169
|
+
return configFail(`清单 config 键携 secret 型键明文值(插件 ${id}:${leaked.join('、')})——从 package.json 删该键、走表单/凭证盒(package.json 随 npm 分发明文更烈)`);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
// api 块:形状 + 版本串判据(单源 contracts——isValidApiVersion 同判据)
|
|
174
|
+
let api;
|
|
175
|
+
if (manifest['api'] !== undefined) {
|
|
176
|
+
const rawApi = manifest['api'];
|
|
177
|
+
if (typeof rawApi !== 'object' || rawApi === null || Array.isArray(rawApi)) {
|
|
178
|
+
return shapeFail(`berryAgent.api 须为对象(插件 ${id})`);
|
|
179
|
+
}
|
|
180
|
+
const apiRecord = rawApi;
|
|
181
|
+
for (const key of Object.keys(apiRecord)) {
|
|
182
|
+
if (key !== 'minApiVersion' && key !== 'targetApiVersion' && key !== 'experimental') {
|
|
183
|
+
return shapeFail(`berryAgent.api 未知键 "${key}"(插件 ${id})——闭集:minApiVersion/targetApiVersion/experimental`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (typeof apiRecord['minApiVersion'] !== 'string' || !isValidApiVersion(apiRecord['minApiVersion'])) {
|
|
187
|
+
return shapeFail(`berryAgent.api.minApiVersion 缺席或坏形(插件 ${id}——应为 MAJOR.MINOR 整数点分两段,如 "1.0")`);
|
|
188
|
+
}
|
|
189
|
+
let targetApiVersion;
|
|
190
|
+
if (apiRecord['targetApiVersion'] !== undefined) {
|
|
191
|
+
if (typeof apiRecord['targetApiVersion'] !== 'string' || !isValidApiVersion(apiRecord['targetApiVersion'])) {
|
|
192
|
+
return shapeFail(`berryAgent.api.targetApiVersion 坏形(插件 ${id}——应为 MAJOR.MINOR 整数点分两段)`);
|
|
193
|
+
}
|
|
194
|
+
targetApiVersion = apiRecord['targetApiVersion'];
|
|
195
|
+
if (versionPairLt(targetApiVersion, apiRecord['minApiVersion'])) {
|
|
196
|
+
return shapeFail(`berryAgent.api targetApiVersion < minApiVersion(插件 ${id}——行为锚不得低于硬地板)`);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
let experimental;
|
|
200
|
+
if (apiRecord['experimental'] !== undefined) {
|
|
201
|
+
if (!Array.isArray(apiRecord['experimental']) ||
|
|
202
|
+
apiRecord['experimental'].some((k) => typeof k !== 'string' || k.length === 0)) {
|
|
203
|
+
return shapeFail(`berryAgent.api.experimental 须为非空字符串数组(插件 ${id})`);
|
|
204
|
+
}
|
|
205
|
+
experimental = apiRecord['experimental'];
|
|
206
|
+
}
|
|
207
|
+
api = { minApiVersion: apiRecord['minApiVersion'], targetApiVersion, experimental };
|
|
208
|
+
}
|
|
209
|
+
// skills:须字符串数组(非空数组即在场的「声明载荷」之一)
|
|
210
|
+
let skills;
|
|
211
|
+
if (manifest['skills'] !== undefined) {
|
|
212
|
+
if (!Array.isArray(manifest['skills']) || manifest['skills'].some((s) => typeof s !== 'string' || s.length === 0)) {
|
|
213
|
+
return shapeFail(`berryAgent.skills 须为非空字符串数组(插件 ${id}——技能目录清单)`);
|
|
214
|
+
}
|
|
215
|
+
skills = manifest['skills'];
|
|
216
|
+
}
|
|
217
|
+
// agents:声明式子代理目录清单(§6.3——与 skills 同形同判据;生态启动批
|
|
218
|
+
// eco-1 键收下:entryPlan 判定同认「声明载荷」,装载收集在 loader)
|
|
219
|
+
let agents;
|
|
220
|
+
if (manifest['agents'] !== undefined) {
|
|
221
|
+
if (!Array.isArray(manifest['agents']) || manifest['agents'].some((s) => typeof s !== 'string' || s.length === 0)) {
|
|
222
|
+
return shapeFail(`berryAgent.agents 须为非空字符串数组(插件 ${id}——声明式子代理目录清单)`);
|
|
223
|
+
}
|
|
224
|
+
agents = manifest['agents'];
|
|
225
|
+
}
|
|
226
|
+
// 入口解析序三步定死(03 §1.2——声明载荷 = skills/agents 任一非空即第②步)
|
|
227
|
+
const entry = manifest['entry'];
|
|
228
|
+
const declaredPayload = (skills !== undefined && skills.length > 0) || (agents !== undefined && agents.length > 0);
|
|
229
|
+
const entryPlan = entry !== undefined
|
|
230
|
+
? { kind: 'entry-file', entry }
|
|
231
|
+
: declaredPayload
|
|
232
|
+
? { kind: 'declared-payload' }
|
|
233
|
+
: { kind: 'default-export' };
|
|
234
|
+
return {
|
|
235
|
+
ok: true,
|
|
236
|
+
manifest: {
|
|
237
|
+
id,
|
|
238
|
+
label,
|
|
239
|
+
version,
|
|
240
|
+
entry,
|
|
241
|
+
grants: manifest['grants'],
|
|
242
|
+
config: manifest['config'],
|
|
243
|
+
configSchema,
|
|
244
|
+
api,
|
|
245
|
+
skills,
|
|
246
|
+
agents,
|
|
247
|
+
entryPlan,
|
|
248
|
+
},
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
/** 形状失败速记(同码分流——install 拒绝与形状违例共用 PLUGIN_SHAPE_INVALID) */
|
|
252
|
+
function shapeFail(message) {
|
|
253
|
+
return { ok: false, code: 'PLUGIN_SHAPE_INVALID', message };
|
|
254
|
+
}
|
|
255
|
+
/** 值违例失败(secret 明文拒专用——PLUGIN_CONFIG_INVALID 面) */
|
|
256
|
+
function configFail(message) {
|
|
257
|
+
return { ok: false, code: 'PLUGIN_CONFIG_INVALID', message };
|
|
258
|
+
}
|
|
259
|
+
/** [major, minor] 数值比较:a < b(判据调用方保证两串已过格式校验) */
|
|
260
|
+
function versionPairLt(a, b) {
|
|
261
|
+
const [amaj, amin] = a.split('.').map(Number);
|
|
262
|
+
const [bmaj, bmin] = b.split('.').map(Number);
|
|
263
|
+
return amaj !== bmaj ? amaj < bmaj : amin < bmin;
|
|
264
|
+
}
|
|
265
|
+
/* ---------------- 启用清单行(enabled.yaml——03 §5.3 单层 overlay) ---------------- */
|
|
266
|
+
/**
|
|
267
|
+
* 启用清单文件路径(数据目录下单文件——boot 读侧与 fire 复检活体读侧共用
|
|
268
|
+
* 单源;C 批 C-3:plugin-boot 与 triggers 活体开门读取都经本函数取路径)。
|
|
269
|
+
*/
|
|
270
|
+
export function enabledYamlPath(dataDir) {
|
|
271
|
+
return join(dataDir, 'enabled.yaml');
|
|
272
|
+
}
|
|
273
|
+
/** 行 schema 已知键闭集 */
|
|
274
|
+
const ROW_KEYS = new Set(['id', 'config', 'disabled', 'opens']);
|
|
275
|
+
/** 顶层文档已知键闭集(plugins + doors——未知顶层键拒绝式,与行键同律) */
|
|
276
|
+
const DOC_KEYS = new Set(['plugins', 'doors']);
|
|
277
|
+
/**
|
|
278
|
+
* 校验启用清单行集(纯函数——输入 = yaml.load 产物,载体解析在装配层)。
|
|
279
|
+
*
|
|
280
|
+
* 逐行拒绝式:未知键 / id 缺席或坏形 / disabled 非布尔 / config 非对象 /
|
|
281
|
+
* opens 坏形或值域外 / core: 行带 opens 即拒,message 点名行序与插件 id。
|
|
282
|
+
* **同 id 多行即拒**(§5.3「单行 per id——行 schema 无多行载体」;uninstall
|
|
283
|
+
* 段①防线同判据)。用户行 id 不校验 core: 保留——用户行覆盖 core: 同名行
|
|
284
|
+
* 是合法形态(字段级后写胜出),core: 行自身不进用户文件(内置全启);
|
|
285
|
+
* 覆盖合法性由装配层合成时裁决。
|
|
286
|
+
*
|
|
287
|
+
* 顶层两查(开门制扩展批 2026-09-09 增):未知顶层键拒(闭集 plugins/doors
|
|
288
|
+
* ——无闭集则 `door:` 之类的段名手误将被静默忽略 = 静默不授予,拒绝式不留
|
|
289
|
+
* 静默通道);`doors` 段坏形(非数组/空串/值域外)拒——值域 v1 =
|
|
290
|
+
* DOORS_SEGMENT_V1_DOMAIN 两门枚举(射程即值域,§8.2 名单系上界;六枚全宽
|
|
291
|
+
* 将致「清单呈现开、门检实效关」三读分叉),PLUGIN_ROW_INVALID 同门。
|
|
292
|
+
*/
|
|
293
|
+
export function parseEnabledRows(doc) {
|
|
294
|
+
if (typeof doc !== 'object' || doc === null || Array.isArray(doc)) {
|
|
295
|
+
return rowFail('启用清单顶层须为对象 { plugins: [行...], doors?: [...] }');
|
|
296
|
+
}
|
|
297
|
+
const record = doc;
|
|
298
|
+
for (const key of Object.keys(record)) {
|
|
299
|
+
if (!DOC_KEYS.has(key)) {
|
|
300
|
+
return rowFail(`启用清单顶层未知键 "${key}"——已知键闭集:plugins/doors`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
const plugins = record['plugins'];
|
|
304
|
+
if (!Array.isArray(plugins)) {
|
|
305
|
+
return rowFail('启用清单 plugins 须为数组(行 schema:{ id, config?, disabled?, opens? })');
|
|
306
|
+
}
|
|
307
|
+
// doors 段三查(03 §5.3 开门制扩展批增段——拒绝式不留静默通道)
|
|
308
|
+
let doors;
|
|
309
|
+
if (record['doors'] !== undefined) {
|
|
310
|
+
const rawDoors = record['doors'];
|
|
311
|
+
if (!Array.isArray(rawDoors) || rawDoors.some((d) => typeof d !== 'string' || d.length === 0)) {
|
|
312
|
+
return rowFail('启用清单顶层 doors 须为非空字符串数组(模型道高危面开门授予段——03 §4.6 双源并集律第二源)');
|
|
313
|
+
}
|
|
314
|
+
const values = rawDoors;
|
|
315
|
+
const illegal = values.find((d) => !DOORS_SEGMENT_V1_DOMAIN.includes(d));
|
|
316
|
+
if (illegal !== undefined) {
|
|
317
|
+
return rowFail(`启用清单顶层 doors 含非法能力位 "${illegal}"——值域 v1 = 两门枚举(现役:${DOORS_SEGMENT_V1_DOMAIN.join('、')};§8.2 高危面名单系上界,扩枚随后续模型道高危面立题放宽)`);
|
|
318
|
+
}
|
|
319
|
+
doors = values;
|
|
320
|
+
}
|
|
321
|
+
const rows = [];
|
|
322
|
+
const seen = new Set();
|
|
323
|
+
for (let i = 0; i < plugins.length; i++) {
|
|
324
|
+
const row = plugins[i];
|
|
325
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row)) {
|
|
326
|
+
return rowFail(`第 ${i + 1} 行须为对象(行 schema:{ id, config?, disabled?, opens? })`);
|
|
327
|
+
}
|
|
328
|
+
const record = row;
|
|
329
|
+
for (const key of Object.keys(record)) {
|
|
330
|
+
if (!ROW_KEYS.has(key)) {
|
|
331
|
+
return rowFail(`第 ${i + 1} 行未知键 "${key}"——已知键闭集:id/config/disabled/opens`);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (typeof record['id'] !== 'string' || record['id'].length === 0) {
|
|
335
|
+
return rowFail(`第 ${i + 1} 行 id 缺席或非非空字符串——id 即装机账本键`);
|
|
336
|
+
}
|
|
337
|
+
const id = record['id'];
|
|
338
|
+
// 用户行 id 过基础字符集(含 core: 前缀——覆盖官方行合法;校验 = 官方式前缀豁免)
|
|
339
|
+
if (!checkPluginId(id, { official: true })) {
|
|
340
|
+
return rowFail(`第 ${i + 1} 行 id "${id}" 坏形(小写字母/数字/连字符;官方前缀 core:<name> 后段同判据)`);
|
|
341
|
+
}
|
|
342
|
+
if (seen.has(id)) {
|
|
343
|
+
return rowFail(`插件 ${id} 多行——单行 per id(行 schema 无多行载体)`);
|
|
344
|
+
}
|
|
345
|
+
seen.add(id);
|
|
346
|
+
if (record['disabled'] !== undefined && typeof record['disabled'] !== 'boolean') {
|
|
347
|
+
return rowFail(`插件 ${id} 行 disabled 须为布尔`);
|
|
348
|
+
}
|
|
349
|
+
if (record['config'] !== undefined &&
|
|
350
|
+
(typeof record['config'] !== 'object' || record['config'] === null || Array.isArray(record['config']))) {
|
|
351
|
+
return rowFail(`插件 ${id} 行 config 须为对象(整值替换非合并)`);
|
|
352
|
+
}
|
|
353
|
+
// opens 三查(03 §4.6/§5.3 批 U1 增位——拒绝式不留静默通道)
|
|
354
|
+
let opens;
|
|
355
|
+
if (record['opens'] !== undefined) {
|
|
356
|
+
if (!Array.isArray(record['opens']) || record['opens'].some((o) => typeof o !== 'string' || o.length === 0)) {
|
|
357
|
+
return rowFail(`插件 ${id} 行 opens 须为非空字符串数组(高危面开门授予位——03 §4.6)`);
|
|
358
|
+
}
|
|
359
|
+
const values = record['opens'];
|
|
360
|
+
const illegal = values.find((o) => !USER_GRANTABLE_CAPABILITIES.includes(o));
|
|
361
|
+
if (illegal !== undefined) {
|
|
362
|
+
return rowFail(`插件 ${id} 行 opens 含非法能力位 "${illegal}"——值域 = 高危面名单单源(现役:${USER_GRANTABLE_CAPABILITIES.join('、')};常规能力位不走开门制)`);
|
|
363
|
+
}
|
|
364
|
+
if (id.startsWith('core:')) {
|
|
365
|
+
return rowFail(`官方件 ${id} 行不设 opens——core: 件高危面窄面注入走宿主装配裁决,不走用户开门位(03 §4.6 治理判据:开门不可被静默继承)`);
|
|
366
|
+
}
|
|
367
|
+
opens = values;
|
|
368
|
+
}
|
|
369
|
+
rows.push({
|
|
370
|
+
id,
|
|
371
|
+
config: record['config'],
|
|
372
|
+
disabled: record['disabled'] === undefined ? undefined : record['disabled'],
|
|
373
|
+
...(opens !== undefined ? { opens } : {}),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
return { ok: true, rows, doors: doors ?? [] };
|
|
377
|
+
}
|
|
378
|
+
/** 行失败速记 */
|
|
379
|
+
function rowFail(message) {
|
|
380
|
+
return { ok: false, code: 'PLUGIN_ROW_INVALID', message };
|
|
381
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { stdin, stdout, stderr } from 'node:process';
|
|
2
|
+
import { runMcpFace } from '../sdk/index.js';
|
|
3
|
+
import { assembleHostStack } from './assembly.js';
|
|
4
|
+
import { createServeBridge } from './serve-entry.js';
|
|
5
|
+
/**
|
|
6
|
+
* mcp 主入口。阻塞至调用方收线(stdin EOF = 对端 agent 退出)/ 信号(经
|
|
7
|
+
* main 编舞 → runtime.shutdown → closer → face.dispose)/ 传输面坏死;
|
|
8
|
+
* 返回进程退出码。
|
|
9
|
+
*/
|
|
10
|
+
export async function runMcpEntry(options) {
|
|
11
|
+
// —— 装配公共段(批 19a-3 迁 assembly 件——与 TUI 同一合成代码路径;
|
|
12
|
+
// debug 恒 false:零旗标律〔07 §5〕,日志级只走 env)——
|
|
13
|
+
const assembly = await assembleHostStack({
|
|
14
|
+
runtime: {
|
|
15
|
+
...(options.dataDir !== undefined ? { dataDir: options.dataDir } : {}),
|
|
16
|
+
...(options.memory === true ? { memory: true } : {}),
|
|
17
|
+
},
|
|
18
|
+
noPlugins: false,
|
|
19
|
+
debug: false,
|
|
20
|
+
version: options.version ?? '0.0.0-unknown',
|
|
21
|
+
...(options.providers !== undefined ? { providers: options.providers } : {}),
|
|
22
|
+
...(options.model !== undefined ? { model: options.model } : {}),
|
|
23
|
+
...(options.env !== undefined ? { env: options.env } : {}),
|
|
24
|
+
...(options.sandboxMode !== undefined ? { sandboxMode: options.sandboxMode } : {}),
|
|
25
|
+
...(options.onRuntime !== undefined ? { onRuntime: options.onRuntime } : {}),
|
|
26
|
+
});
|
|
27
|
+
if (!assembly.ok) {
|
|
28
|
+
stderr.write(`${assembly.crashed ? `mcp 运行失败:${assembly.message}` : assembly.message}\n`);
|
|
29
|
+
return assembly.exitCode;
|
|
30
|
+
}
|
|
31
|
+
const { runtime, stack, logger } = assembly;
|
|
32
|
+
let exitCode = 0;
|
|
33
|
+
try {
|
|
34
|
+
const bridge = createServeBridge(stack, runtime, { cwd: options.cwd ?? process.cwd() });
|
|
35
|
+
const face = runMcpFace({
|
|
36
|
+
io: options.io ?? { input: stdin, output: stdout },
|
|
37
|
+
bridge,
|
|
38
|
+
serverInfo: { name: 'berry-agent', version: options.version ?? '0.0.0-unknown' }, // 对外声明值位
|
|
39
|
+
log: (message) => logger.warn(message),
|
|
40
|
+
});
|
|
41
|
+
stack.channels.addBackend(face.backend); // 信封回流自动馈送(conversation-stack onEvent → emit)
|
|
42
|
+
// 信号路优雅档:六步退出序经 closer 收口 face(幂等);EOF 路径 face 自决
|
|
43
|
+
runtime.registerCloser({ label: 'mcp-face', fn: () => Promise.resolve(face.dispose()) });
|
|
44
|
+
exitCode = await face.done; // EOF 0 / 传输面坏死 1
|
|
45
|
+
await runtime.shutdown().catch(() => { }); // 六步退出序(closer 内已 dispose——幂等双保险)
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
runtime.writeCrashLog(err); // 崩溃取证先行(memory 形跳过——件内语义)
|
|
49
|
+
stderr.write(`mcp 运行失败:${err instanceof Error ? err.message : String(err)}\n`);
|
|
50
|
+
exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
return exitCode;
|
|
53
|
+
}
|