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,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 6: pkg-runner
|
|
3
|
+
*
|
|
4
|
+
* Spawn @yao-pkg/pkg with the synthesized config and capture output.
|
|
5
|
+
*
|
|
6
|
+
* Phase 1 implementation: prefer the locally-installed binary at
|
|
7
|
+
* node_modules/.bin/pkg (or @yao-pkg/pkg/lib-es5/bin.js). If pkg is not
|
|
8
|
+
* installed, throw a clear error pointing at `npm install -D @yao-pkg/pkg`.
|
|
9
|
+
*
|
|
10
|
+
* We deliberately avoid bundling pkg as a runtime dep — it's a dev tool and
|
|
11
|
+
* users who never run `cc pack` should not pay its install cost.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import fs from "node:fs";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { spawnSync } from "node:child_process";
|
|
18
|
+
import { PackError, EXIT } from "./errors.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param {object} ctx
|
|
22
|
+
* @param {string} ctx.cliRoot
|
|
23
|
+
* @param {string} ctx.pkgConfigFile synthesized package.json path
|
|
24
|
+
* @param {string} ctx.outputPath absolute final output path (no ext)
|
|
25
|
+
* @param {string[]} ctx.targets
|
|
26
|
+
* @param {object} [ctx.logger]
|
|
27
|
+
* @returns {{ outputs: string[] }}
|
|
28
|
+
*/
|
|
29
|
+
export function runPkg(ctx) {
|
|
30
|
+
const { cliRoot, pkgConfigFile, outputPath, targets, logger } = ctx;
|
|
31
|
+
const log = logger?.log || (() => {});
|
|
32
|
+
|
|
33
|
+
const pkgBin = locatePkgBinary(cliRoot);
|
|
34
|
+
if (!pkgBin) {
|
|
35
|
+
throw new PackError(buildPkgMissingMessage(cliRoot), EXIT.PKG);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Use --output to control the produced filename; pkg auto-appends platform
|
|
39
|
+
// suffix only when multiple targets are passed.
|
|
40
|
+
const args = [
|
|
41
|
+
pkgBin.script,
|
|
42
|
+
pkgConfigFile,
|
|
43
|
+
"--targets",
|
|
44
|
+
targets.join(","),
|
|
45
|
+
"--output",
|
|
46
|
+
outputPath,
|
|
47
|
+
];
|
|
48
|
+
|
|
49
|
+
log(` [pkg] Running: ${pkgBin.runtime} ${args.join(" ")}`);
|
|
50
|
+
const res = spawnSync(pkgBin.runtime, args, {
|
|
51
|
+
cwd: path.dirname(pkgConfigFile),
|
|
52
|
+
stdio: "inherit",
|
|
53
|
+
windowsHide: true,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
if (res.status !== 0) {
|
|
57
|
+
throw new PackError(
|
|
58
|
+
`pkg exited with code ${res.status}. See output above for details.`,
|
|
59
|
+
EXIT.PKG,
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const outputs = collectOutputs(outputPath, targets);
|
|
64
|
+
if (outputs.length === 0) {
|
|
65
|
+
throw new PackError(
|
|
66
|
+
`pkg reported success but no output file found at ${outputPath}*`,
|
|
67
|
+
EXIT.PKG,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
// Pair each produced artifact with the pkg target it came from. For a
|
|
71
|
+
// single target build, pkg writes to the bare --output path; for multi
|
|
72
|
+
// target it suffixes with "-<platformKey>". We match on the suffix
|
|
73
|
+
// when possible and fall back to positional pairing.
|
|
74
|
+
const rich = outputs.map((p) => {
|
|
75
|
+
const base = path.basename(outputPath);
|
|
76
|
+
const name = path.basename(p, path.extname(p));
|
|
77
|
+
const suffix = name.startsWith(base + "-")
|
|
78
|
+
? name.slice(base.length + 1)
|
|
79
|
+
: null;
|
|
80
|
+
let target = null;
|
|
81
|
+
if (suffix && targets.length > 1) {
|
|
82
|
+
target = targets.find((t) => t.endsWith(suffix)) || null;
|
|
83
|
+
}
|
|
84
|
+
if (!target && targets.length === 1) target = targets[0];
|
|
85
|
+
return { path: p, target };
|
|
86
|
+
});
|
|
87
|
+
return { outputs: rich };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Build an install-context-aware "pkg not found" message. The CLI ships in
|
|
92
|
+
* three shapes (monorepo workspace, global npm install, plain local install),
|
|
93
|
+
* each of which needs a different `npm install` invocation.
|
|
94
|
+
*/
|
|
95
|
+
export function buildPkgMissingMessage(cliRoot) {
|
|
96
|
+
const ctx = detectInstallContext(cliRoot);
|
|
97
|
+
const header =
|
|
98
|
+
"@yao-pkg/pkg not found in node_modules. Install it where the CLI lives:";
|
|
99
|
+
if (ctx.kind === "monorepo") {
|
|
100
|
+
return `${header}\n npm install -D @yao-pkg/pkg --workspace packages/cli`;
|
|
101
|
+
}
|
|
102
|
+
if (ctx.kind === "global") {
|
|
103
|
+
return (
|
|
104
|
+
`${header}\n` +
|
|
105
|
+
` cd "${ctx.installDir}" && npm install @yao-pkg/pkg\n` +
|
|
106
|
+
`(The CLI is installed globally at the path above; pkg must be added to that node_modules.)`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return (
|
|
110
|
+
`${header}\n` + ` cd "${ctx.installDir}" && npm install -D @yao-pkg/pkg`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Classify how the CLI is installed by looking at cliRoot's parent chain.
|
|
116
|
+
* - "monorepo": cliRoot is a workspace package inside a repo whose root
|
|
117
|
+
* package.json declares `workspaces`.
|
|
118
|
+
* - "global": cliRoot's immediate parent is `node_modules` (the npm-i-g
|
|
119
|
+
* and `npx` install layouts both produce this).
|
|
120
|
+
* - "standalone": neither of the above — fall back to a generic
|
|
121
|
+
* `cd <cliRoot> && npm install -D` hint.
|
|
122
|
+
*/
|
|
123
|
+
function detectInstallContext(cliRoot) {
|
|
124
|
+
const parent = path.dirname(cliRoot);
|
|
125
|
+
if (path.basename(parent) === "node_modules") {
|
|
126
|
+
return { kind: "global", installDir: cliRoot };
|
|
127
|
+
}
|
|
128
|
+
let cur = parent;
|
|
129
|
+
for (let i = 0; i < 5; i++) {
|
|
130
|
+
const pj = path.join(cur, "package.json");
|
|
131
|
+
if (fs.existsSync(pj)) {
|
|
132
|
+
try {
|
|
133
|
+
const meta = JSON.parse(fs.readFileSync(pj, "utf-8"));
|
|
134
|
+
if (meta.workspaces) {
|
|
135
|
+
return { kind: "monorepo", installDir: cur };
|
|
136
|
+
}
|
|
137
|
+
} catch {
|
|
138
|
+
/* malformed package.json — keep walking */
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
const next = path.dirname(cur);
|
|
142
|
+
if (next === cur) break;
|
|
143
|
+
cur = next;
|
|
144
|
+
}
|
|
145
|
+
return { kind: "standalone", installDir: cliRoot };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Locate a runnable pkg binary using node's resolution algorithm so we
|
|
150
|
+
* find both local and monorepo-hoisted installs of @yao-pkg/pkg (or
|
|
151
|
+
* legacy vercel/pkg) without hard-coding paths.
|
|
152
|
+
*/
|
|
153
|
+
function locatePkgBinary(cliRoot) {
|
|
154
|
+
const req = createRequire(path.join(cliRoot, "package.json"));
|
|
155
|
+
for (const pkgName of ["@yao-pkg/pkg", "pkg"]) {
|
|
156
|
+
try {
|
|
157
|
+
const pkgJson = req.resolve(`${pkgName}/package.json`);
|
|
158
|
+
const moduleDir = path.dirname(pkgJson);
|
|
159
|
+
const meta = JSON.parse(fs.readFileSync(pkgJson, "utf-8"));
|
|
160
|
+
const binEntry = typeof meta.bin === "string" ? meta.bin : meta.bin?.pkg;
|
|
161
|
+
if (binEntry) {
|
|
162
|
+
const script = path.join(moduleDir, binEntry);
|
|
163
|
+
if (fs.existsSync(script)) {
|
|
164
|
+
return { runtime: process.execPath, script };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
} catch {
|
|
168
|
+
/* not installed via this name */
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return null;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* For a single target, pkg writes to the exact --output path (with .exe on
|
|
176
|
+
* Windows). For multiple targets it suffixes -<platform>-<arch>.
|
|
177
|
+
*/
|
|
178
|
+
function collectOutputs(outputPath, targets) {
|
|
179
|
+
const dir = path.dirname(outputPath);
|
|
180
|
+
if (!fs.existsSync(dir)) return [];
|
|
181
|
+
const base = path.basename(outputPath);
|
|
182
|
+
const all = fs.readdirSync(dir);
|
|
183
|
+
return all
|
|
184
|
+
.filter(
|
|
185
|
+
(name) =>
|
|
186
|
+
name === base ||
|
|
187
|
+
name.startsWith(base + "-") ||
|
|
188
|
+
name === base + ".exe" ||
|
|
189
|
+
name.startsWith(base + "-"),
|
|
190
|
+
)
|
|
191
|
+
.map((name) => path.join(dir, name))
|
|
192
|
+
.filter((p) => fs.statSync(p).isFile());
|
|
193
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Phase 1: precheck — confirm the project root, npm install, and git state
|
|
3
|
+
* are in shape before we attempt to bundle.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { execSync } from "node:child_process";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { PackError, EXIT } from "./errors.js";
|
|
11
|
+
|
|
12
|
+
const WINDOWS_RESERVED = new Set([
|
|
13
|
+
"CON",
|
|
14
|
+
"PRN",
|
|
15
|
+
"AUX",
|
|
16
|
+
"NUL",
|
|
17
|
+
"COM1",
|
|
18
|
+
"COM2",
|
|
19
|
+
"COM3",
|
|
20
|
+
"COM4",
|
|
21
|
+
"COM5",
|
|
22
|
+
"COM6",
|
|
23
|
+
"COM7",
|
|
24
|
+
"COM8",
|
|
25
|
+
"COM9",
|
|
26
|
+
"LPT1",
|
|
27
|
+
"LPT2",
|
|
28
|
+
"LPT3",
|
|
29
|
+
"LPT4",
|
|
30
|
+
"LPT5",
|
|
31
|
+
"LPT6",
|
|
32
|
+
"LPT7",
|
|
33
|
+
"LPT8",
|
|
34
|
+
"LPT9",
|
|
35
|
+
]);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sanitize a project name for safe use as a filesystem directory segment.
|
|
39
|
+
*
|
|
40
|
+
* Rules:
|
|
41
|
+
* - Lowercase; replace any char outside [a-z0-9_-] with '-'
|
|
42
|
+
* - Collapse consecutive dashes; strip leading/trailing dashes
|
|
43
|
+
* - Windows reserved names get '-proj' appended
|
|
44
|
+
* - Truncate to 64 characters
|
|
45
|
+
* - Empty result after sanitization → PackError
|
|
46
|
+
*
|
|
47
|
+
* @param {string} rawName raw value from config.json "name" field
|
|
48
|
+
* @returns {string} safe directory name
|
|
49
|
+
*/
|
|
50
|
+
export function sanitizeProjectName(rawName) {
|
|
51
|
+
let name = String(rawName || "")
|
|
52
|
+
.toLowerCase()
|
|
53
|
+
.replace(/[^a-z0-9_-]/g, "-")
|
|
54
|
+
.replace(/-+/g, "-")
|
|
55
|
+
.replace(/^-+|-+$/g, "");
|
|
56
|
+
|
|
57
|
+
if (WINDOWS_RESERVED.has(name.toUpperCase())) {
|
|
58
|
+
name = name + "-proj";
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
name = name.slice(0, 64);
|
|
62
|
+
|
|
63
|
+
if (!name) {
|
|
64
|
+
throw new PackError(
|
|
65
|
+
`Project name "${rawName}" cannot be sanitized to a valid filesystem segment. ` +
|
|
66
|
+
'Use only ASCII letters, digits, hyphens, and underscores in the "name" field.',
|
|
67
|
+
EXIT.PRECHECK,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return name;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @param {object} ctx
|
|
76
|
+
* @param {string} ctx.projectRoot absolute path
|
|
77
|
+
* @param {boolean} ctx.allowDirty allow uncommitted changes
|
|
78
|
+
* @param {boolean|undefined} [ctx.projectMode] tri-state: true = force
|
|
79
|
+
* project-mode (fail if no
|
|
80
|
+
* config), false = force
|
|
81
|
+
* CLI-only, undefined =
|
|
82
|
+
* auto-detect from the
|
|
83
|
+
* presence of
|
|
84
|
+
* projectRoot/.chainlesschain/config.json
|
|
85
|
+
* @param {string|null} [ctx.projectConfigOverride] absolute or projectRoot-
|
|
86
|
+
* relative path to an
|
|
87
|
+
* alternate config.json
|
|
88
|
+
* (takes precedence over
|
|
89
|
+
* the default location)
|
|
90
|
+
* @returns {{
|
|
91
|
+
* cliRoot:string,
|
|
92
|
+
* repoRoot:string|null,
|
|
93
|
+
* gitCommit:string|null,
|
|
94
|
+
* dirty:boolean,
|
|
95
|
+
* projectMode:boolean,
|
|
96
|
+
* projectConfigPath:string|null,
|
|
97
|
+
* }}
|
|
98
|
+
*/
|
|
99
|
+
export function precheck(ctx) {
|
|
100
|
+
const { projectRoot, allowDirty } = ctx;
|
|
101
|
+
|
|
102
|
+
// Locate this CLI package root (packages/cli) — relative to this file.
|
|
103
|
+
// fileURLToPath handles Windows (C:/...) and POSIX absolute paths uniformly;
|
|
104
|
+
// the old `new URL(import.meta.url).pathname.replace(/^\//, "")` dropped the
|
|
105
|
+
// leading "/" on POSIX, making the path relative, which then caused
|
|
106
|
+
// path.resolve to prepend cwd → doubled prefix on Linux CI.
|
|
107
|
+
const cliRoot = path.resolve(
|
|
108
|
+
path.dirname(fileURLToPath(import.meta.url)),
|
|
109
|
+
"..",
|
|
110
|
+
"..",
|
|
111
|
+
"..",
|
|
112
|
+
);
|
|
113
|
+
const cliPkgPath = path.join(cliRoot, "package.json");
|
|
114
|
+
if (!fs.existsSync(cliPkgPath)) {
|
|
115
|
+
throw new PackError(
|
|
116
|
+
`CLI package.json not found at ${cliPkgPath}`,
|
|
117
|
+
EXIT.PRECHECK,
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const nodeModulesPath = path.join(cliRoot, "node_modules");
|
|
122
|
+
if (!fs.existsSync(nodeModulesPath)) {
|
|
123
|
+
throw new PackError(
|
|
124
|
+
`node_modules missing in ${cliRoot}. Run 'npm install' first.`,
|
|
125
|
+
EXIT.PRECHECK,
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// The "project root" the user is bundling. Often === cliRoot during dev,
|
|
130
|
+
// but a downstream consumer running `cc pack` from their own project
|
|
131
|
+
// produces a bundle named after their project.
|
|
132
|
+
if (!projectRoot || !fs.existsSync(projectRoot)) {
|
|
133
|
+
throw new PackError(`Invalid projectRoot: ${projectRoot}`, EXIT.PRECHECK);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Walk up from projectRoot to find the git repo root (if any). Pack does
|
|
137
|
+
// not require git, but if the dir IS a git repo we want the commit/dirty
|
|
138
|
+
// flag for the manifest.
|
|
139
|
+
let gitCommit = null;
|
|
140
|
+
let dirty = false;
|
|
141
|
+
let repoRoot = null;
|
|
142
|
+
try {
|
|
143
|
+
repoRoot = execSync("git rev-parse --show-toplevel", {
|
|
144
|
+
cwd: projectRoot,
|
|
145
|
+
encoding: "utf-8",
|
|
146
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
147
|
+
}).trim();
|
|
148
|
+
gitCommit = execSync("git rev-parse --short HEAD", {
|
|
149
|
+
cwd: repoRoot,
|
|
150
|
+
encoding: "utf-8",
|
|
151
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
152
|
+
}).trim();
|
|
153
|
+
const status = execSync("git status --porcelain", {
|
|
154
|
+
cwd: repoRoot,
|
|
155
|
+
encoding: "utf-8",
|
|
156
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
157
|
+
});
|
|
158
|
+
dirty = status.trim().length > 0;
|
|
159
|
+
} catch (_e) {
|
|
160
|
+
// Not a git repo — fine, leave the metadata null
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (dirty && !allowDirty) {
|
|
164
|
+
throw new PackError(
|
|
165
|
+
"Working tree is dirty. Commit/stash your changes or pass --allow-dirty.",
|
|
166
|
+
EXIT.PRECHECK,
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const { projectMode, projectConfigPath, projectName } = resolveProjectMode({
|
|
171
|
+
projectRoot,
|
|
172
|
+
projectMode: ctx.projectMode,
|
|
173
|
+
projectConfigOverride: ctx.projectConfigOverride || null,
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
return {
|
|
177
|
+
cliRoot,
|
|
178
|
+
repoRoot,
|
|
179
|
+
gitCommit,
|
|
180
|
+
dirty,
|
|
181
|
+
projectMode,
|
|
182
|
+
projectConfigPath,
|
|
183
|
+
projectName,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Decide whether this pack invocation runs in project mode and where the
|
|
189
|
+
* bundled config.json lives. Separated out for unit-testability.
|
|
190
|
+
*
|
|
191
|
+
* Matrix:
|
|
192
|
+
* projectMode === true + config exists → project mode, use config
|
|
193
|
+
* projectMode === true + config missing → PackError
|
|
194
|
+
* projectMode === false → CLI-only, ignore any config
|
|
195
|
+
* projectMode === undefined + exists → project mode (auto-detect)
|
|
196
|
+
* projectMode === undefined + missing → CLI-only
|
|
197
|
+
*
|
|
198
|
+
* @param {object} ctx
|
|
199
|
+
* @param {string} ctx.projectRoot
|
|
200
|
+
* @param {boolean|undefined} ctx.projectMode
|
|
201
|
+
* @param {string|null} ctx.projectConfigOverride
|
|
202
|
+
* @returns {{projectMode:boolean, projectConfigPath:string|null}}
|
|
203
|
+
*/
|
|
204
|
+
export function resolveProjectMode(ctx) {
|
|
205
|
+
const { projectRoot, projectMode, projectConfigOverride } = ctx;
|
|
206
|
+
|
|
207
|
+
if (projectMode === false) {
|
|
208
|
+
return { projectMode: false, projectConfigPath: null, projectName: null };
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const candidatePath = projectConfigOverride
|
|
212
|
+
? path.isAbsolute(projectConfigOverride)
|
|
213
|
+
? projectConfigOverride
|
|
214
|
+
: path.resolve(projectRoot, projectConfigOverride)
|
|
215
|
+
: path.join(projectRoot, ".chainlesschain", "config.json");
|
|
216
|
+
|
|
217
|
+
const exists = fs.existsSync(candidatePath);
|
|
218
|
+
|
|
219
|
+
if (projectMode === true) {
|
|
220
|
+
if (!exists) {
|
|
221
|
+
throw new PackError(
|
|
222
|
+
`--project mode requires a config.json at ${candidatePath}. ` +
|
|
223
|
+
"Run 'cc init' first, or pass --project-config-override <path>.",
|
|
224
|
+
EXIT.PRECHECK,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
const { name: projectName } = validateProjectConfig(candidatePath);
|
|
228
|
+
return { projectMode: true, projectConfigPath: candidatePath, projectName };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// Auto-detect
|
|
232
|
+
if (exists) {
|
|
233
|
+
const { name: projectName } = validateProjectConfig(candidatePath);
|
|
234
|
+
return { projectMode: true, projectConfigPath: candidatePath, projectName };
|
|
235
|
+
}
|
|
236
|
+
return { projectMode: false, projectConfigPath: null, projectName: null };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Minimal Phase-0 schema check. Confirms the file parses as JSON and has a
|
|
241
|
+
* non-empty `name` field (required by the §4.1 naming rule). Returns the
|
|
242
|
+
* sanitized project name so callers can propagate it without re-reading.
|
|
243
|
+
*
|
|
244
|
+
* @returns {{ name: string }}
|
|
245
|
+
*/
|
|
246
|
+
function validateProjectConfig(configPath) {
|
|
247
|
+
let raw;
|
|
248
|
+
try {
|
|
249
|
+
raw = fs.readFileSync(configPath, "utf-8");
|
|
250
|
+
} catch (e) {
|
|
251
|
+
throw new PackError(
|
|
252
|
+
`Cannot read project config ${configPath}: ${e.message}`,
|
|
253
|
+
EXIT.PRECHECK,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
let parsed;
|
|
257
|
+
try {
|
|
258
|
+
parsed = JSON.parse(raw);
|
|
259
|
+
} catch (e) {
|
|
260
|
+
throw new PackError(
|
|
261
|
+
`Project config ${configPath} is not valid JSON: ${e.message}`,
|
|
262
|
+
EXIT.PRECHECK,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
if (!parsed || typeof parsed.name !== "string" || !parsed.name.trim()) {
|
|
266
|
+
throw new PackError(
|
|
267
|
+
`Project config ${configPath} is missing required field "name".`,
|
|
268
|
+
EXIT.PRECHECK,
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
const name = sanitizeProjectName(parsed.name.trim());
|
|
272
|
+
return { name };
|
|
273
|
+
}
|
|
Binary file
|