chainlesschain 0.156.6 → 0.157.0
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/README.md +26 -2
- package/bin/chainlesschain.js +13 -0
- package/package.json +3 -1
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{ActionButton-Dme4LGax.js → ActionButton-CCj9oE5_.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-B3-5BjRm.js → Analytics-RMqXlyHE.js} +2 -2
- package/src/assets/web-panel/assets/AppLayout-CSmBboZB.css +1 -0
- package/src/assets/web-panel/assets/AppLayout-CV6gWn1r.js +1 -0
- package/src/assets/web-panel/assets/{Backup-Cih5dXcD.js → Backup-bes7wE_k.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-wLmjCc9u.js → BaseInput-BKgAovqI.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CRfTuSl8.js → Chat-Duckao6i.js} +2 -2
- package/src/assets/web-panel/assets/{Checkbox-BfbEUJDW.js → Checkbox-BaBBUZnH.js} +1 -1
- package/src/assets/web-panel/assets/{Col-HJI40OzO.js → Col-B6iQKzFs.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-ADVAwcbQ.js → Compact-Fwt0CbI5.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BLRUoSoO.js → Cowork-DNy50_Cp.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-DcNB5TYu.js → Cron-CBREJypB.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-p_Wuj0Un.js → Dashboard-DnJ1aZvj.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-CrXGzreQ.js → Dropdown-B3vdFzOi.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-B97Dibo2.js → FormItemContext-RYn2zMn5.js} +1 -1
- package/src/assets/web-panel/assets/{Git-90CPsOOr.js → Git-7TolKe3c.js} +2 -2
- package/src/assets/web-panel/assets/KnowledgeGraph-4b9v3wYV.js +19 -0
- package/src/assets/web-panel/assets/KnowledgeGraph-U8ps3aGJ.css +1 -0
- package/src/assets/web-panel/assets/{Logs-0SXs6Eyx.js → Logs-BZgM3Q4b.js} +2 -2
- package/src/assets/web-panel/assets/{McpTools-VVSCkpV2.js → McpTools-5Wrif1R_.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-B_3zNQNB.js → Memory-D8YRnt51.js} +2 -2
- package/src/assets/web-panel/assets/{Notes-DNQz9UXh.js → Notes-CWTUG8hk.js} +2 -2
- package/src/assets/web-panel/assets/{Organization-CgXUnp-W.js → Organization-DvJzrIES.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-BVsn6SM5.js → Overflow-tpvXbZkh.js} +1 -1
- package/src/assets/web-panel/assets/{OverrideContext-7M2Kv4Ru.js → OverrideContext-9ePmgwvW.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DGPIG-9j.js → P2P-C-AoKbTs.js} +2 -2
- package/src/assets/web-panel/assets/{Permissions-DIFqcnjU.js → Permissions-cIclKlQB.js} +3 -3
- package/src/assets/web-panel/assets/Portal-DXIqogG2.js +1 -0
- package/src/assets/web-panel/assets/{Projects-Bzn-dJ59.js → Projects-CDKdsdit.js} +2 -2
- package/src/assets/web-panel/assets/{Providers-mscN7CK5.js → Providers-C33u4Mka.js} +2 -2
- package/src/assets/web-panel/assets/{Row-BFUWxIkx.js → Row-DVscVTtp.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-Dpa4h-q_.js → RssFeed-CGC4w7VD.js} +3 -3
- package/src/assets/web-panel/assets/{Security-DR6HKo_S.js → Security-ymR0We-P.js} +3 -3
- package/src/assets/web-panel/assets/{Services-CDh7r75R.js → Services-Gydt4uVC.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-VNikEgM4.js → Skeleton-CfLdvEpu.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-Dk9Cp1NG.js → Skills-C_SrPIFZ.js} +1 -1
- package/src/assets/web-panel/assets/Tasks-BchvT4YD.js +1 -0
- package/src/assets/web-panel/assets/{Templates-Ny_4GO6a.js → Templates-Bj1wDexb.js} +1 -1
- package/src/assets/web-panel/assets/Trigger-CIW_GVYA.js +1 -0
- package/src/assets/web-panel/assets/{VideoEditing-BNRFHgJ9.js → VideoEditing-CQOwjQFu.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BUfg4IAx.js → Wallet-0kSZ-ENs.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-Cia89OyQ.js → WebAuthn-CrZlAr6l.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-C1OsMtqv.js → WorkflowEditor-DbdF8700.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B2uGA8wN.js → chat-BNXQJRrX.js} +1 -1
- package/src/assets/web-panel/assets/{collapseMotion-DnZigkzG.js → collapseMotion-CSS8MlIE.js} +1 -1
- package/src/assets/web-panel/assets/{colors-C_wDMX2Q.js → colors-BgoKrIXh.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C1ikzEN-.js → compact-item-COAuztwB.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-XExBTk9v.js → createContext-BTceykzK.js} +1 -1
- package/src/assets/web-panel/assets/{hasIn-mXvd_Kdq.js → hasIn-BNooGgXx.js} +1 -1
- package/src/assets/web-panel/assets/{icons-CpgFsfkd.js → icons-DzieZiVh.js} +4 -4
- package/src/assets/web-panel/assets/index-4C1c4hkZ.js +36 -0
- package/src/assets/web-panel/assets/{index-Dz6RDRcu.js → index-81NIDNcI.js} +2 -2
- package/src/assets/web-panel/assets/{index-a0qENb5U.js → index-B7gZm05C.js} +1 -1
- package/src/assets/web-panel/assets/{index-CTle6zcb.js → index-BAPulPv1.js} +2 -2
- package/src/assets/web-panel/assets/{index-qtDQSqTG.js → index-BEbmwv5T.js} +2 -2
- package/src/assets/web-panel/assets/{index-D1eekAaa.js → index-BLzkplpf.js} +3 -3
- package/src/assets/web-panel/assets/{index-BBOVB9YK.js → index-BMU9I-F6.js} +1 -1
- package/src/assets/web-panel/assets/{index-D6Hyy0Bc.js → index-BRBeeCRz.js} +2 -2
- package/src/assets/web-panel/assets/index-BbBjevdP.js +1 -0
- package/src/assets/web-panel/assets/{index-kLUQdSDJ.js → index-Bjf02LQY.js} +2 -2
- package/src/assets/web-panel/assets/index-Bn7dXbEh.js +1 -0
- package/src/assets/web-panel/assets/index-BqJS8Rje.js +1 -0
- package/src/assets/web-panel/assets/{index-LpE6Six-.js → index-C1BBkQIj.js} +4 -4
- package/src/assets/web-panel/assets/{index-CxwU-EjS.js → index-C4YPr8e8.js} +1 -1
- package/src/assets/web-panel/assets/{index-lPIeHtHE.js → index-C7uW3zj4.js} +1 -1
- package/src/assets/web-panel/assets/{index-DMcLOtIo.js → index-CZ3YetO8.js} +1 -1
- package/src/assets/web-panel/assets/{index-Du7KGlCP.js → index-Cf1n_3VC.js} +1 -1
- package/src/assets/web-panel/assets/{index-DwMlStra.js → index-CjOtB4wg.js} +3 -3
- package/src/assets/web-panel/assets/{index-DYLE4bnY.js → index-CpFrvhnj.js} +1 -1
- package/src/assets/web-panel/assets/{index-v4Oi0d0l.js → index-D1SCFE25.js} +1 -1
- package/src/assets/web-panel/assets/{index-BOqmUcij.js → index-D5wFryiJ.js} +2 -2
- package/src/assets/web-panel/assets/{index-CbpKJ2W0.js → index-D6w1kIHg.js} +1 -1
- package/src/assets/web-panel/assets/{index-CttcpCq_.js → index-DC-nDpH_.js} +2 -2
- package/src/assets/web-panel/assets/index-DDgwb3KP.js +1 -0
- package/src/assets/web-panel/assets/index-DYDvGZbQ.js +1 -0
- package/src/assets/web-panel/assets/index-DbxkO9Uy.js +1 -0
- package/src/assets/web-panel/assets/index-DeN7D3FZ.js +1 -0
- package/src/assets/web-panel/assets/{index-DZjQgmBq.js → index-DnoNK5Gb.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_oSE2Nk.js → index-KEQgDCwj.js} +5 -5
- package/src/assets/web-panel/assets/{index-C53dnYiq.js → index-O-dqdn3q.js} +2 -2
- package/src/assets/web-panel/assets/{index-DJkIheU6.js → index-Z5CuKqcS.js} +1 -1
- package/src/assets/web-panel/assets/{index-fLUJs2Sr.js → index-Z9wFemG0.js} +2 -2
- package/src/assets/web-panel/assets/{index-D9tzxSFs.js → index-cF6gfPY3.js} +1 -1
- package/src/assets/web-panel/assets/{index-BL27IhbN.js → index-eVLTVVvL.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMYADk0v.js → index-g7FAcG7B.js} +1 -1
- package/src/assets/web-panel/assets/{index-BirLVqrC.js → index-lrfnKtkl.js} +1 -1
- package/src/assets/web-panel/assets/{index-jg5cpQg9.js → index-nw5SqpgZ.js} +2 -2
- package/src/assets/web-panel/assets/{index-BFFb9yPd.js → index-vHj3UTBK.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-DOj2K4bh.js → initDefaultProps-_t-PG0bz.js} +1 -1
- package/src/assets/web-panel/assets/{motion-joGf7r-l.js → motion-DPnqtODq.js} +2 -2
- package/src/assets/web-panel/assets/{move-Cwb6tumJ.js → move-kO9NQRyb.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CPycjJ8C.js → omit-DD9MqVt0.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-CnibXC3T.js → pickAttrs-D4u5AYq1.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-DWcIO1y4.js → placementArrow-BUkUxlJc.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-C5giLhLf.js → responsiveObserve-DA_o8FHw.js} +1 -1
- package/src/assets/web-panel/assets/{slide-zwgmm7vM.js → slide-DSmAtCrK.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CK8tJSHq.js → statusUtils-Bn00xQ4D.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-BzLSEXyu.js → styleChecker-phGTLjuN.js} +1 -1
- package/src/assets/web-panel/assets/{transition-D4AbuDdO.js → transition-exl3w1iN.js} +1 -1
- package/src/assets/web-panel/assets/{useConfigInject-ImjEZhXr.js → useConfigInject-C2E3Qsop.js} +2 -2
- package/src/assets/web-panel/assets/useFlexGapSupport-tlovsMBV.js +1 -0
- package/src/assets/web-panel/assets/{useMergedState-CXfbNKuO.js → useMergedState-DUMpRiCy.js} +1 -1
- package/src/assets/web-panel/assets/{useRefs-DwsdQTxa.js → useRefs-C8A7zAB_.js} +1 -1
- package/src/assets/web-panel/assets/{useState-DRbnp348.js → useState-CSbzOa8O.js} +1 -1
- package/src/assets/web-panel/assets/{vendor-C5RM7MZO.js → vendor-aH7YaPZi.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-DAWimP6X.js → vnode-CIbqhcnZ.js} +1 -1
- package/src/assets/web-panel/assets/{ws-D-sl0vsW.js → ws-BTS8ehTm.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-u6SXbmzZ.js → zoom-Ck9WbYsZ.js} +1 -1
- package/src/assets/web-panel/index.html +3 -3
- package/src/commands/mtc.js +786 -0
- package/src/commands/pack.js +463 -0
- package/src/commands/ui.js +6 -0
- package/src/gateways/ws/ws-server.js +49 -5
- package/src/index.js +37 -1
- package/src/lib/governance-v2-helpers.js +109 -0
- package/src/lib/packer/config-template-builder.js +151 -0
- package/src/lib/packer/errors.js +30 -0
- package/src/lib/packer/index.js +383 -0
- package/src/lib/packer/manifest-writer.js +93 -0
- package/src/lib/packer/native-prebuild-collector.js +305 -0
- package/src/lib/packer/pack-update-applier.js +203 -0
- package/src/lib/packer/pack-update-checker.js +185 -0
- package/src/lib/packer/pack-update-downloader.js +162 -0
- package/src/lib/packer/pkg-config-generator.js +325 -0
- package/src/lib/packer/pkg-runner.js +193 -0
- package/src/lib/packer/precheck.js +273 -0
- package/src/lib/packer/project-assets-collector.js +0 -0
- package/src/lib/packer/smoke-runner.js +339 -0
- package/src/lib/packer/web-panel-builder.js +157 -0
- package/src/lib/web-ui-server.js +95 -2
- package/src/lib/ws-server.js +1 -0
- package/src/runtime/agent-runtime.js +1 -0
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/assets/web-panel/assets/AppLayout-DvVLRyPs.js +0 -1
- package/src/assets/web-panel/assets/AppLayout-nff99EgA.css +0 -1
- package/src/assets/web-panel/assets/Portal-CFB5Y97t.js +0 -1
- package/src/assets/web-panel/assets/Tasks-CfHL1NrP.js +0 -1
- package/src/assets/web-panel/assets/Trigger-C7MTh_xj.js +0 -1
- package/src/assets/web-panel/assets/index-1ZqkTPt2.js +0 -1
- package/src/assets/web-panel/assets/index-B-TI0cZ2.js +0 -1
- package/src/assets/web-panel/assets/index-B6P9mWuk.js +0 -1
- package/src/assets/web-panel/assets/index-BfncNR8d.js +0 -1
- package/src/assets/web-panel/assets/index-C5Zv4fBx.js +0 -1
- package/src/assets/web-panel/assets/index-DQgS_8Fd.js +0 -1
- package/src/assets/web-panel/assets/index-DaMG8ksh.js +0 -36
- package/src/assets/web-panel/assets/index-f4W8Sok0.js +0 -1
- package/src/assets/web-panel/assets/useFlexGapSupport-Cd-PoTMl.js +0 -1
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `cc pack` — Bundle the current project environment into a standalone executable.
|
|
3
|
+
*
|
|
4
|
+
* See docs/design/CC_PACK_打包指令设计文档.md for the full design.
|
|
5
|
+
*
|
|
6
|
+
* Phase 1 scope: Windows x64 single target, dry-run + minimal pkg invocation,
|
|
7
|
+
* no native module prebuild collection or smoke test yet.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import chalk from "chalk";
|
|
11
|
+
import { logger } from "../lib/logger.js";
|
|
12
|
+
import { runPack } from "../lib/packer/index.js";
|
|
13
|
+
import path from "node:path";
|
|
14
|
+
import {
|
|
15
|
+
checkPackUpdate,
|
|
16
|
+
PackUpdateError,
|
|
17
|
+
} from "../lib/packer/pack-update-checker.js";
|
|
18
|
+
import {
|
|
19
|
+
downloadAndVerify,
|
|
20
|
+
DownloadError,
|
|
21
|
+
} from "../lib/packer/pack-update-downloader.js";
|
|
22
|
+
import {
|
|
23
|
+
scheduleReplace,
|
|
24
|
+
ApplyError,
|
|
25
|
+
} from "../lib/packer/pack-update-applier.js";
|
|
26
|
+
import { VERSION } from "../constants.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Default pkg target string for the host platform+arch. Falls back to
|
|
30
|
+
* `node20-win-x64` for unknown combos so users on uncommon hosts still get
|
|
31
|
+
* a sensible string to edit with `--targets`.
|
|
32
|
+
*/
|
|
33
|
+
export function defaultPkgTarget() {
|
|
34
|
+
const osSlug =
|
|
35
|
+
process.platform === "win32"
|
|
36
|
+
? "win"
|
|
37
|
+
: process.platform === "darwin"
|
|
38
|
+
? "macos"
|
|
39
|
+
: process.platform === "linux"
|
|
40
|
+
? "linux"
|
|
41
|
+
: null;
|
|
42
|
+
const archSlug =
|
|
43
|
+
process.arch === "x64" ? "x64" : process.arch === "arm64" ? "arm64" : null;
|
|
44
|
+
if (!osSlug || !archSlug) return "node20-win-x64";
|
|
45
|
+
return `node20-${osSlug}-${archSlug}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function registerPackCommand(program) {
|
|
49
|
+
const packCmd = program
|
|
50
|
+
.command("pack")
|
|
51
|
+
.description(
|
|
52
|
+
"Bundle the current project environment into a standalone executable",
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
packCmd
|
|
56
|
+
.option(
|
|
57
|
+
"-o, --output <path>",
|
|
58
|
+
"Output path without extension. pkg appends .exe for win targets only.",
|
|
59
|
+
)
|
|
60
|
+
.option(
|
|
61
|
+
"-t, --targets <list>",
|
|
62
|
+
"Comma-separated pkg targets (defaults to current host platform)",
|
|
63
|
+
defaultPkgTarget(),
|
|
64
|
+
)
|
|
65
|
+
.option("--ws-port <n>", "Default WS port baked into the artifact", "18800")
|
|
66
|
+
.option(
|
|
67
|
+
"--ui-port <n>",
|
|
68
|
+
"Default Web UI port baked into the artifact",
|
|
69
|
+
"18810",
|
|
70
|
+
)
|
|
71
|
+
.option(
|
|
72
|
+
"--token <str>",
|
|
73
|
+
'Default access token; "auto" generates a fresh one at first run',
|
|
74
|
+
"auto",
|
|
75
|
+
)
|
|
76
|
+
.option(
|
|
77
|
+
"--preset-config <path>",
|
|
78
|
+
"Pre-bundled config.json template (will be scanned for secrets)",
|
|
79
|
+
)
|
|
80
|
+
.option(
|
|
81
|
+
"--allow-secrets",
|
|
82
|
+
"Skip secret scanning of preset config (DANGEROUS)",
|
|
83
|
+
false,
|
|
84
|
+
)
|
|
85
|
+
.option("--include-db", "Bundle the DB initialization template", true)
|
|
86
|
+
.option("--no-include-db", "Skip bundling the DB template")
|
|
87
|
+
.option(
|
|
88
|
+
"--include-models",
|
|
89
|
+
"Bundle local LLM models (HUGE artifact size warning)",
|
|
90
|
+
false,
|
|
91
|
+
)
|
|
92
|
+
.option("--compress", "Enable pkg GZip compression", true)
|
|
93
|
+
.option("--no-compress", "Disable pkg compression")
|
|
94
|
+
.option("--sign <cert>", "Code-signing certificate (Phase 2)")
|
|
95
|
+
.option(
|
|
96
|
+
"--sign-password <pass>",
|
|
97
|
+
"Code-signing certificate password; supports env:NAME",
|
|
98
|
+
)
|
|
99
|
+
.option("--smoke-test", "Run smoke test on the produced artifact", true)
|
|
100
|
+
.option("--no-smoke-test", "Skip smoke test")
|
|
101
|
+
.option("--dry-run", "Print the build plan without invoking pkg", false)
|
|
102
|
+
.option(
|
|
103
|
+
"--bind-host <host>",
|
|
104
|
+
"Default bind host baked into the artifact",
|
|
105
|
+
"127.0.0.1",
|
|
106
|
+
)
|
|
107
|
+
.option("--allow-remote", "Default to 0.0.0.0 binding at runtime", false)
|
|
108
|
+
.option("--enable-tls", "Enable TLS in the artifact runtime", false)
|
|
109
|
+
.option("--tls-cert <path>", "TLS certificate path (PEM)")
|
|
110
|
+
.option("--tls-key <path>", "TLS private key path (PEM)")
|
|
111
|
+
.option("--access-policy <path>", "Pre-bundled access policy JSON")
|
|
112
|
+
.option(
|
|
113
|
+
"--skip-web-panel-build",
|
|
114
|
+
"Reuse existing web-panel/dist without rebuilding",
|
|
115
|
+
false,
|
|
116
|
+
)
|
|
117
|
+
.option(
|
|
118
|
+
"--allow-dirty",
|
|
119
|
+
"Allow packing with uncommitted changes in the working tree",
|
|
120
|
+
false,
|
|
121
|
+
)
|
|
122
|
+
.option(
|
|
123
|
+
"--cwd <dir>",
|
|
124
|
+
"Override the project root (defaults to process.cwd())",
|
|
125
|
+
)
|
|
126
|
+
.option(
|
|
127
|
+
"--project",
|
|
128
|
+
"Project mode: bundle .chainlesschain/ from cwd into the artifact",
|
|
129
|
+
)
|
|
130
|
+
.option(
|
|
131
|
+
"--no-project",
|
|
132
|
+
"Disable project-mode auto-detection; pack CLI-only (legacy behavior)",
|
|
133
|
+
)
|
|
134
|
+
.option(
|
|
135
|
+
"--project-config-override <path>",
|
|
136
|
+
"Use an alternate config.json instead of cwd/.chainlesschain/config.json",
|
|
137
|
+
)
|
|
138
|
+
.option(
|
|
139
|
+
"--force-large-project",
|
|
140
|
+
"Bypass the 50MB .chainlesschain/ size cap (DANGEROUS — bloats the exe)",
|
|
141
|
+
false,
|
|
142
|
+
)
|
|
143
|
+
.option(
|
|
144
|
+
"--entry <subcommand>",
|
|
145
|
+
"Override the default subcommand from config.pack.entry (project mode only)",
|
|
146
|
+
)
|
|
147
|
+
.option(
|
|
148
|
+
"--force-refresh-on-launch",
|
|
149
|
+
"Re-materialize project assets on every launch (project mode only)",
|
|
150
|
+
false,
|
|
151
|
+
)
|
|
152
|
+
.option(
|
|
153
|
+
"--update-manifest-url <url>",
|
|
154
|
+
"OTA manifest URL to bake into the artifact (enables `cc pack check-update`)",
|
|
155
|
+
)
|
|
156
|
+
.action(async (opts) => {
|
|
157
|
+
try {
|
|
158
|
+
const result = await runPack(opts, { logger });
|
|
159
|
+
if (opts.dryRun) {
|
|
160
|
+
logger.log(
|
|
161
|
+
chalk.green(
|
|
162
|
+
`\n ✓ Dry-run complete. ${result.steps?.length || 0} step(s) planned.`,
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
} else if (result.outputPath) {
|
|
166
|
+
logger.log(chalk.green(`\n ✓ Pack succeeded: ${result.outputPath}`));
|
|
167
|
+
if (result.sha256) {
|
|
168
|
+
logger.log(chalk.dim(` SHA-256: ${result.sha256}`));
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
} catch (err) {
|
|
172
|
+
logger.error(`pack failed: ${err.message}`);
|
|
173
|
+
if (err.exitCode) process.exit(err.exitCode);
|
|
174
|
+
process.exit(1);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Phase 5a: check-only OTA probe. Packed exes can't use `cc update`
|
|
179
|
+
// (that runs `npm install -g`), so this subcommand fetches a manifest
|
|
180
|
+
// and reports whether a newer packed artifact exists. Download +
|
|
181
|
+
// self-replace are Phase 5b/5c. See design doc §17.5-17.7.
|
|
182
|
+
packCmd
|
|
183
|
+
.command("check-update")
|
|
184
|
+
.description(
|
|
185
|
+
"Check whether a newer packed artifact is published (no download)",
|
|
186
|
+
)
|
|
187
|
+
.option(
|
|
188
|
+
"--manifest-url <url>",
|
|
189
|
+
"Manifest URL (falls back to BAKED.updateManifestUrl or CC_PACK_UPDATE_MANIFEST env)",
|
|
190
|
+
)
|
|
191
|
+
.option(
|
|
192
|
+
"--target <slug>",
|
|
193
|
+
"pkg target to match against manifest.latest.artifacts (e.g. node20-win-x64); defaults to current host",
|
|
194
|
+
)
|
|
195
|
+
.option(
|
|
196
|
+
"--current <version>",
|
|
197
|
+
"Override the current version (defaults to CLI VERSION or BAKED.packedCliVersion)",
|
|
198
|
+
)
|
|
199
|
+
.option("--json", "Emit JSON instead of human-readable output", false)
|
|
200
|
+
.option(
|
|
201
|
+
"--download",
|
|
202
|
+
"Phase 5b: after the check, download + SHA-256 verify the artifact (no install)",
|
|
203
|
+
false,
|
|
204
|
+
)
|
|
205
|
+
.option(
|
|
206
|
+
"--dest <path>",
|
|
207
|
+
"Phase 5b: download destination. Defaults to `<currentExePath>.new` inside a packed exe, or `./<basename(artifactUrl)>` otherwise. (Named `--dest`, not `--output`, to avoid colliding with the parent `pack -o`.)",
|
|
208
|
+
)
|
|
209
|
+
.option(
|
|
210
|
+
"--apply",
|
|
211
|
+
"Phase 5c: after download, replace the current exe with the new one. Requires --download. On Windows a sidecar cmd runs the move after this process exits",
|
|
212
|
+
false,
|
|
213
|
+
)
|
|
214
|
+
.option(
|
|
215
|
+
"--target-exe <path>",
|
|
216
|
+
"Phase 5c: path of the exe to replace. Defaults to process.execPath (the currently-running binary)",
|
|
217
|
+
)
|
|
218
|
+
.option(
|
|
219
|
+
"--restart",
|
|
220
|
+
"Phase 5c: spawn the new exe after replacement (default: exit without restart)",
|
|
221
|
+
false,
|
|
222
|
+
)
|
|
223
|
+
.action(async (opts) => {
|
|
224
|
+
const manifestUrl =
|
|
225
|
+
opts.manifestUrl ||
|
|
226
|
+
process.env.CC_PACK_UPDATE_MANIFEST ||
|
|
227
|
+
(typeof globalThis.BAKED === "object" &&
|
|
228
|
+
globalThis.BAKED?.updateManifestUrl) ||
|
|
229
|
+
null;
|
|
230
|
+
|
|
231
|
+
if (!manifestUrl) {
|
|
232
|
+
const msg =
|
|
233
|
+
"No manifest URL. Provide --manifest-url, set CC_PACK_UPDATE_MANIFEST, or bake one via `cc pack --update-manifest-url`.";
|
|
234
|
+
if (opts.json) {
|
|
235
|
+
logger.log(JSON.stringify({ error: msg, code: "NO_MANIFEST_URL" }));
|
|
236
|
+
} else {
|
|
237
|
+
logger.error(msg);
|
|
238
|
+
}
|
|
239
|
+
process.exit(2);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const currentVersion =
|
|
243
|
+
opts.current ||
|
|
244
|
+
(typeof globalThis.BAKED === "object" &&
|
|
245
|
+
globalThis.BAKED?.packedCliVersion) ||
|
|
246
|
+
VERSION;
|
|
247
|
+
|
|
248
|
+
const target = opts.target || defaultPkgTarget();
|
|
249
|
+
|
|
250
|
+
let result;
|
|
251
|
+
try {
|
|
252
|
+
result = await checkPackUpdate({
|
|
253
|
+
manifestUrl,
|
|
254
|
+
currentVersion,
|
|
255
|
+
target,
|
|
256
|
+
});
|
|
257
|
+
} catch (err) {
|
|
258
|
+
const code = err instanceof PackUpdateError ? err.code : "UNKNOWN";
|
|
259
|
+
if (opts.json) {
|
|
260
|
+
logger.log(JSON.stringify({ error: err.message, code }));
|
|
261
|
+
} else {
|
|
262
|
+
logger.error(`check-update failed [${code}]: ${err.message}`);
|
|
263
|
+
}
|
|
264
|
+
// Network / schema issues are non-zero but distinct from "pack failed"
|
|
265
|
+
process.exit(3);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Human or JSON report for the check result.
|
|
269
|
+
if (!opts.download) {
|
|
270
|
+
if (opts.json) {
|
|
271
|
+
logger.log(JSON.stringify(result, null, 2));
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (result.updateAvailable) {
|
|
275
|
+
logger.log(
|
|
276
|
+
chalk.bold(
|
|
277
|
+
`\n Update available: ${result.currentVersion} → ${chalk.green(result.latestVersion)}\n`,
|
|
278
|
+
),
|
|
279
|
+
);
|
|
280
|
+
if (result.artifact) {
|
|
281
|
+
logger.log(` Artifact (${target}):`);
|
|
282
|
+
logger.log(` ${result.artifact.url}`);
|
|
283
|
+
logger.log(chalk.dim(` sha256: ${result.artifact.sha256}`));
|
|
284
|
+
} else {
|
|
285
|
+
logger.log(
|
|
286
|
+
chalk.yellow(
|
|
287
|
+
` No artifact for target "${target}" in this manifest.`,
|
|
288
|
+
),
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
if (result.releaseNotes) {
|
|
292
|
+
logger.log(chalk.dim(` Release notes: ${result.releaseNotes}`));
|
|
293
|
+
}
|
|
294
|
+
} else {
|
|
295
|
+
logger.log(
|
|
296
|
+
chalk.green(
|
|
297
|
+
` ✓ You are on the latest version (${result.currentVersion}).`,
|
|
298
|
+
),
|
|
299
|
+
);
|
|
300
|
+
}
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// ── --download branch (Phase 5b) ─────────────────────────────────────
|
|
305
|
+
if (!result.updateAvailable) {
|
|
306
|
+
const msg = `Already on the latest version (${result.currentVersion}); nothing to download.`;
|
|
307
|
+
if (opts.json) logger.log(JSON.stringify({ skipped: msg, ...result }));
|
|
308
|
+
else logger.log(chalk.green(` ✓ ${msg}`));
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!result.artifact) {
|
|
312
|
+
const msg = `No artifact for target "${target}" in this manifest.`;
|
|
313
|
+
if (opts.json)
|
|
314
|
+
logger.log(JSON.stringify({ error: msg, code: "NO_ARTIFACT" }));
|
|
315
|
+
else logger.error(msg);
|
|
316
|
+
process.exit(3);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// Default output path. Inside a pkg-packed exe, `process.execPath` is the
|
|
320
|
+
// exe itself — writing `<exe>.new` alongside is conventional for the
|
|
321
|
+
// future Phase 5c self-replacer. Outside pkg, fall back to a filename
|
|
322
|
+
// under cwd so `cc pack check-update --download` from a dev checkout
|
|
323
|
+
// drops the artifact where the user can find it.
|
|
324
|
+
const outputPath = opts.dest
|
|
325
|
+
? path.resolve(opts.dest)
|
|
326
|
+
: process.pkg
|
|
327
|
+
? process.execPath + ".new"
|
|
328
|
+
: path.resolve(
|
|
329
|
+
process.cwd(),
|
|
330
|
+
path.basename(new URL(result.artifact.url).pathname) ||
|
|
331
|
+
"pack-update-artifact",
|
|
332
|
+
);
|
|
333
|
+
|
|
334
|
+
if (!opts.json) {
|
|
335
|
+
logger.log(
|
|
336
|
+
chalk.bold(
|
|
337
|
+
`\n Downloading ${result.currentVersion} → ${chalk.green(result.latestVersion)}`,
|
|
338
|
+
),
|
|
339
|
+
);
|
|
340
|
+
logger.log(` ${result.artifact.url}`);
|
|
341
|
+
logger.log(chalk.dim(` → ${outputPath}`));
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let lastPercent = -1;
|
|
345
|
+
try {
|
|
346
|
+
const dl = await downloadAndVerify({
|
|
347
|
+
url: result.artifact.url,
|
|
348
|
+
sha256: result.artifact.sha256,
|
|
349
|
+
outputPath,
|
|
350
|
+
onProgress: opts.json
|
|
351
|
+
? undefined
|
|
352
|
+
: ({ bytes, total }) => {
|
|
353
|
+
if (!total) return;
|
|
354
|
+
const pct = Math.floor((bytes / total) * 100);
|
|
355
|
+
if (pct !== lastPercent && pct % 10 === 0) {
|
|
356
|
+
lastPercent = pct;
|
|
357
|
+
logger.log(
|
|
358
|
+
chalk.dim(
|
|
359
|
+
` ${pct}% (${formatMB(bytes)}/${formatMB(total)})`,
|
|
360
|
+
),
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
if (opts.json) {
|
|
366
|
+
logger.log(
|
|
367
|
+
JSON.stringify(
|
|
368
|
+
{
|
|
369
|
+
downloaded: true,
|
|
370
|
+
outputPath: dl.outputPath,
|
|
371
|
+
bytes: dl.bytes,
|
|
372
|
+
sha256: dl.sha256,
|
|
373
|
+
latestVersion: result.latestVersion,
|
|
374
|
+
},
|
|
375
|
+
null,
|
|
376
|
+
2,
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
} else {
|
|
380
|
+
logger.log(
|
|
381
|
+
chalk.green(
|
|
382
|
+
`\n ✓ Downloaded + verified ${formatMB(dl.bytes)} to ${dl.outputPath}`,
|
|
383
|
+
),
|
|
384
|
+
);
|
|
385
|
+
if (!opts.apply) {
|
|
386
|
+
logger.log(
|
|
387
|
+
chalk.dim(
|
|
388
|
+
` sha256 OK. Pass --apply to replace the running exe automatically.`,
|
|
389
|
+
),
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// ── --apply branch (Phase 5c) ──────────────────────────────────────
|
|
395
|
+
if (!opts.apply) return;
|
|
396
|
+
|
|
397
|
+
const targetExe = opts.targetExe || process.execPath;
|
|
398
|
+
if (!opts.json) {
|
|
399
|
+
logger.log(chalk.bold(`\n Applying update → ${targetExe}`));
|
|
400
|
+
if (process.platform === "win32") {
|
|
401
|
+
logger.log(
|
|
402
|
+
chalk.yellow(
|
|
403
|
+
` [Windows] This process will exit after scheduling a sidecar cmd; the replacement runs detached. ${opts.restart ? "The new exe will start automatically." : "Re-launch the exe yourself after exit."}`,
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
} else {
|
|
407
|
+
logger.log(
|
|
408
|
+
chalk.dim(
|
|
409
|
+
` [POSIX] Atomic rename; the currently-running inode survives until exit. ${opts.restart ? "A detached copy will be started now." : "Next launch picks up the new bytes."}`,
|
|
410
|
+
),
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
try {
|
|
416
|
+
const plan = await scheduleReplace({
|
|
417
|
+
newExePath: dl.outputPath,
|
|
418
|
+
targetExePath: targetExe,
|
|
419
|
+
restart: Boolean(opts.restart),
|
|
420
|
+
});
|
|
421
|
+
if (opts.json) {
|
|
422
|
+
logger.log(JSON.stringify({ applied: true, ...plan }, null, 2));
|
|
423
|
+
} else {
|
|
424
|
+
logger.log(
|
|
425
|
+
chalk.green(
|
|
426
|
+
` ✓ Apply scheduled: action=${plan.action}${plan.sidecarPath ? `, sidecar=${plan.sidecarPath}` : ""}`,
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
// On Windows the sidecar waits for us to exit. On POSIX the rename
|
|
431
|
+
// already happened; if we were asked to restart, the detached child
|
|
432
|
+
// is running. Either way the parent's job is done.
|
|
433
|
+
if (plan.action === "sidecar-cmd") {
|
|
434
|
+
// Give the sidecar a moment to start waiting on our PID before we
|
|
435
|
+
// actually exit. Without this, process.exit(0) can race ahead and
|
|
436
|
+
// the sidecar's tasklist check may spuriously see us as gone
|
|
437
|
+
// before it has even polled once (harmless, but nicer to avoid).
|
|
438
|
+
setTimeout(() => process.exit(0), 500).unref?.();
|
|
439
|
+
}
|
|
440
|
+
} catch (err) {
|
|
441
|
+
const code = err instanceof ApplyError ? err.code : "UNKNOWN";
|
|
442
|
+
if (opts.json) {
|
|
443
|
+
logger.log(JSON.stringify({ error: err.message, code }));
|
|
444
|
+
} else {
|
|
445
|
+
logger.error(`apply failed [${code}]: ${err.message}`);
|
|
446
|
+
}
|
|
447
|
+
process.exit(5);
|
|
448
|
+
}
|
|
449
|
+
} catch (err) {
|
|
450
|
+
const code = err instanceof DownloadError ? err.code : "UNKNOWN";
|
|
451
|
+
if (opts.json) {
|
|
452
|
+
logger.log(JSON.stringify({ error: err.message, code }));
|
|
453
|
+
} else {
|
|
454
|
+
logger.error(`download failed [${code}]: ${err.message}`);
|
|
455
|
+
}
|
|
456
|
+
process.exit(4);
|
|
457
|
+
}
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
function formatMB(bytes) {
|
|
462
|
+
return `${(bytes / 1024 / 1024).toFixed(1)}MB`;
|
|
463
|
+
}
|
package/src/commands/ui.js
CHANGED
|
@@ -17,6 +17,11 @@ export function registerUiCommand(program) {
|
|
|
17
17
|
"--web-panel-dir <dir>",
|
|
18
18
|
"Path to built web-panel dist/ directory (auto-detected by default)",
|
|
19
19
|
)
|
|
20
|
+
.option(
|
|
21
|
+
"--ui-mode <mode>",
|
|
22
|
+
'UI rendering mode: "auto" (default), "full" (require Vue panel), or "minimal" (embedded HTML only)',
|
|
23
|
+
"auto",
|
|
24
|
+
)
|
|
20
25
|
.action(async (opts) => {
|
|
21
26
|
try {
|
|
22
27
|
const runtime = createAgentRuntimeFactory().createUiRuntime({
|
|
@@ -26,6 +31,7 @@ export function registerUiCommand(program) {
|
|
|
26
31
|
open: opts.open,
|
|
27
32
|
token: opts.token || null,
|
|
28
33
|
webPanelDir: opts.webPanelDir || null,
|
|
34
|
+
uiMode: opts.uiMode || "auto",
|
|
29
35
|
});
|
|
30
36
|
await runtime.startUiServer();
|
|
31
37
|
} catch (err) {
|
|
@@ -81,8 +81,34 @@ const __dirname = dirname(__filename);
|
|
|
81
81
|
/** Absolute path to the CLI entry point */
|
|
82
82
|
const BIN_PATH = join(__dirname, "..", "..", "..", "bin", "chainlesschain.js");
|
|
83
83
|
|
|
84
|
-
/**
|
|
85
|
-
|
|
84
|
+
/**
|
|
85
|
+
* Commands always blocked over WebSocket (any mode):
|
|
86
|
+
* - serve: would recursively spawn another WS server
|
|
87
|
+
* - setup: needs interactive TTY
|
|
88
|
+
* - pack: meaningless self-bundling from inside running instance
|
|
89
|
+
*/
|
|
90
|
+
const ALWAYS_BLOCKED_COMMANDS = new Set(["serve", "setup", "pack"]);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Commands blocked by default but unlocked when running inside a pack
|
|
94
|
+
* artifact (CC_PACK_MODE=1). The Web UI may then expose these via a
|
|
95
|
+
* shell-like surface for advanced users.
|
|
96
|
+
*/
|
|
97
|
+
const PACK_UNLOCKABLE_COMMANDS = new Set(["chat", "agent"]);
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Decide if a command is currently blocked over WebSocket.
|
|
101
|
+
* Reads CC_PACK_MODE at call time so tests can flip it per-case.
|
|
102
|
+
* @param {string} baseCmd
|
|
103
|
+
* @param {object} [env=process.env]
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
export function isCommandBlocked(baseCmd, env = process.env) {
|
|
107
|
+
if (ALWAYS_BLOCKED_COMMANDS.has(baseCmd)) return true;
|
|
108
|
+
const packMode = env.CC_PACK_MODE === "1" || env.CC_PACK_MODE === "true";
|
|
109
|
+
if (PACK_UNLOCKABLE_COMMANDS.has(baseCmd) && !packMode) return true;
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
86
112
|
|
|
87
113
|
/** Heartbeat interval (ms) */
|
|
88
114
|
const HEARTBEAT_INTERVAL = 30_000;
|
|
@@ -180,6 +206,14 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
180
206
|
});
|
|
181
207
|
|
|
182
208
|
this.wss.on("listening", () => {
|
|
209
|
+
// When port 0 was requested the OS assigns an ephemeral port;
|
|
210
|
+
// read it back so callers + the listening event see the actual
|
|
211
|
+
// bound port instead of 0. Tests rely on this for collision-free
|
|
212
|
+
// parallel runs.
|
|
213
|
+
const addr = this.wss.address();
|
|
214
|
+
if (addr && typeof addr === "object" && addr.port) {
|
|
215
|
+
this.port = addr.port;
|
|
216
|
+
}
|
|
183
217
|
this._startHeartbeat();
|
|
184
218
|
this.emit("listening", { port: this.port, host: this.host });
|
|
185
219
|
resolve();
|
|
@@ -236,10 +270,20 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
236
270
|
}
|
|
237
271
|
this.clients.clear();
|
|
238
272
|
|
|
239
|
-
// Close the server
|
|
273
|
+
// Close the server. ws.WebSocketServer.close() waits for all underlying
|
|
274
|
+
// sockets to fully terminate before invoking its callback — on slow CI
|
|
275
|
+
// runners (especially GH Linux/macOS) sockets can linger in TIME_WAIT /
|
|
276
|
+
// CLOSE_WAIT longer than Vitest's task timeout, causing the callback to
|
|
277
|
+
// never fire and afterEach to hang forever. Hard 2s ceiling here lets
|
|
278
|
+
// the test suite reclaim the worker even if a lingering socket exists;
|
|
279
|
+
// the underlying handle is GC'd by node shortly after anyway.
|
|
240
280
|
if (this.wss) {
|
|
241
281
|
await new Promise((resolve) => {
|
|
242
|
-
|
|
282
|
+
const ceiling = setTimeout(() => resolve(), 5000);
|
|
283
|
+
this.wss.close(() => {
|
|
284
|
+
clearTimeout(ceiling);
|
|
285
|
+
resolve();
|
|
286
|
+
});
|
|
243
287
|
});
|
|
244
288
|
this.wss = null;
|
|
245
289
|
}
|
|
@@ -499,7 +543,7 @@ export class ChainlessChainWSServer extends EventEmitter {
|
|
|
499
543
|
|
|
500
544
|
// Block dangerous/interactive commands
|
|
501
545
|
const baseCmd = args[0];
|
|
502
|
-
if (
|
|
546
|
+
if (isCommandBlocked(baseCmd)) {
|
|
503
547
|
this._send(ws, {
|
|
504
548
|
id,
|
|
505
549
|
type: "error",
|
package/src/index.js
CHANGED
|
@@ -127,6 +127,7 @@ import { registerServeCommand } from "./commands/serve.js";
|
|
|
127
127
|
|
|
128
128
|
// Web UI
|
|
129
129
|
import { registerUiCommand } from "./commands/ui.js";
|
|
130
|
+
import { registerPackCommand } from "./commands/pack.js";
|
|
130
131
|
|
|
131
132
|
// Video Editing Agent (CutClaw-inspired)
|
|
132
133
|
import { registerVideoCommand } from "./commands/video.js";
|
|
@@ -192,6 +193,8 @@ import { registerQuantizationCommand } from "./commands/quantization.js";
|
|
|
192
193
|
import { registerRuntimeCommand } from "./commands/runtime.js";
|
|
193
194
|
// Phase 17: IPFS decentralized storage
|
|
194
195
|
import { registerIpfsCommand } from "./commands/ipfs.js";
|
|
196
|
+
// MTC: Merkle Tree Certificates (Phase 1 Week 3)
|
|
197
|
+
import { registerMtcCommand } from "./commands/mtc.js";
|
|
195
198
|
// Phase 27: Multimodal Collaboration
|
|
196
199
|
import { registerMultimodalCommand } from "./commands/multimodal.js";
|
|
197
200
|
// Phase 22: Performance auto-tuning
|
|
@@ -355,7 +358,14 @@ import { registerKgV2Commands } from "./commands/kg.js";
|
|
|
355
358
|
import { registerPmodeV2Commands } from "./commands/planmode.js";
|
|
356
359
|
import { registerPipoV2Commands } from "./commands/pipeline.js";
|
|
357
360
|
|
|
358
|
-
|
|
361
|
+
/**
|
|
362
|
+
* @param {object} [opts]
|
|
363
|
+
* @param {Set<string>} [opts.allowedCommands] When set, only these top-level
|
|
364
|
+
* command names survive. Env var CC_PROJECT_ALLOWED_SUBCOMMANDS provides the
|
|
365
|
+
* same filter at runtime (packed exe project mode — comma-separated list).
|
|
366
|
+
* opts.allowedCommands takes precedence over the env var.
|
|
367
|
+
*/
|
|
368
|
+
export function createProgram(opts = {}) {
|
|
359
369
|
const program = new Command();
|
|
360
370
|
|
|
361
371
|
program
|
|
@@ -510,6 +520,9 @@ export function createProgram() {
|
|
|
510
520
|
// Web UI
|
|
511
521
|
registerUiCommand(program);
|
|
512
522
|
|
|
523
|
+
// Standalone Executable Bundling
|
|
524
|
+
registerPackCommand(program);
|
|
525
|
+
|
|
513
526
|
// Orchestration Layer
|
|
514
527
|
registerOrchestrateCommand(program);
|
|
515
528
|
|
|
@@ -565,6 +578,7 @@ export function createProgram() {
|
|
|
565
578
|
registerQuantizationCommand(program);
|
|
566
579
|
registerRuntimeCommand(program);
|
|
567
580
|
registerIpfsCommand(program);
|
|
581
|
+
registerMtcCommand(program);
|
|
568
582
|
registerMultimodalCommand(program);
|
|
569
583
|
registerPerfCommand(program);
|
|
570
584
|
registerAgentNetworkCommand(program);
|
|
@@ -720,5 +734,27 @@ export function createProgram() {
|
|
|
720
734
|
registerPmodeV2Commands(program);
|
|
721
735
|
registerPipoV2Commands(program);
|
|
722
736
|
|
|
737
|
+
// Phase 3a: project-mode command whitelist.
|
|
738
|
+
// In a packed project-mode exe, CC_PROJECT_ALLOWED_SUBCOMMANDS (set by the
|
|
739
|
+
// entry script from BAKED.projectAllowedSubcommands) restricts which top-level
|
|
740
|
+
// commands are visible. opts.allowedCommands (a Set<string>) is preferred for
|
|
741
|
+
// programmatic/test use and takes precedence over the env var.
|
|
742
|
+
const allowedSet =
|
|
743
|
+
opts.allowedCommands instanceof Set
|
|
744
|
+
? opts.allowedCommands
|
|
745
|
+
: process.env.CC_PROJECT_ALLOWED_SUBCOMMANDS
|
|
746
|
+
? new Set(
|
|
747
|
+
process.env.CC_PROJECT_ALLOWED_SUBCOMMANDS.split(",")
|
|
748
|
+
.map((s) => s.trim())
|
|
749
|
+
.filter(Boolean),
|
|
750
|
+
)
|
|
751
|
+
: null;
|
|
752
|
+
|
|
753
|
+
if (allowedSet && allowedSet.size > 0) {
|
|
754
|
+
program.commands = program.commands.filter((cmd) =>
|
|
755
|
+
allowedSet.has(cmd.name()),
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
|
|
723
759
|
return program;
|
|
724
760
|
}
|