@hcmai/cli 0.3.10 → 0.3.12
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 +5 -16
- package/dist/index.js +539 -1328
- package/dist/index.js.map +1 -1
- package/dist/skills/manifest.json +9 -12
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -177,12 +177,12 @@ function wrapIndex(index, length) {
|
|
|
177
177
|
if (length <= 0) return 0;
|
|
178
178
|
return (index % length + length) % length;
|
|
179
179
|
}
|
|
180
|
-
function resolveSlashKey(
|
|
181
|
-
if (
|
|
182
|
-
if (
|
|
180
|
+
function resolveSlashKey(key2, selected) {
|
|
181
|
+
if (key2.upArrow) return { type: "move", delta: -1 };
|
|
182
|
+
if (key2.downArrow) return { type: "move", delta: 1 };
|
|
183
183
|
if (!selected) return { type: "none" };
|
|
184
|
-
if (
|
|
185
|
-
if (
|
|
184
|
+
if (key2.tab) return { type: "complete", text: selected.cmd + (selected.takesArgs ? " " : "") };
|
|
185
|
+
if (key2.return) {
|
|
186
186
|
return selected.takesArgs ? { type: "complete", text: selected.cmd + " " } : { type: "execute", text: selected.cmd };
|
|
187
187
|
}
|
|
188
188
|
return { type: "none" };
|
|
@@ -523,8 +523,8 @@ var init_TerminalRenderer = __esm({
|
|
|
523
523
|
}
|
|
524
524
|
}
|
|
525
525
|
/** 来源行(仅子代理触发,agentPath ≥2 段时显示 `↑ 来自 a › b`,对齐 Web)。 */
|
|
526
|
-
originLine(
|
|
527
|
-
const segs =
|
|
526
|
+
originLine(path9) {
|
|
527
|
+
const segs = path9.split(".").filter(Boolean);
|
|
528
528
|
if (segs.length < 2) return "";
|
|
529
529
|
const names = segs.map((k) => agentDisplayName(k));
|
|
530
530
|
return chalk.gray(` \u2191 \u6765\u81EA ${names.join(" \u203A ")}`) + "\n";
|
|
@@ -1181,41 +1181,41 @@ function MultilineInput(props) {
|
|
|
1181
1181
|
setCursor(nextCursor);
|
|
1182
1182
|
onChange(next);
|
|
1183
1183
|
};
|
|
1184
|
-
useInput((input,
|
|
1185
|
-
if (captureKey?.(input,
|
|
1186
|
-
if (
|
|
1184
|
+
useInput((input, key2) => {
|
|
1185
|
+
if (captureKey?.(input, key2)) return;
|
|
1186
|
+
if (key2.escape) {
|
|
1187
1187
|
if (value) edit("", 0);
|
|
1188
1188
|
onEscapeClear?.();
|
|
1189
1189
|
return;
|
|
1190
1190
|
}
|
|
1191
|
-
const modifiedEnter = classifyModifiedEnter(input,
|
|
1191
|
+
const modifiedEnter = classifyModifiedEnter(input, key2);
|
|
1192
1192
|
if (modifiedEnter === "newline" || input === "\n") {
|
|
1193
1193
|
edit(value.slice(0, cursor) + "\n" + value.slice(cursor), cursor + 1);
|
|
1194
1194
|
return;
|
|
1195
1195
|
}
|
|
1196
|
-
if (
|
|
1196
|
+
if (key2.return) {
|
|
1197
1197
|
const sendDraft = draftFromSendCommandLine(value, cursor);
|
|
1198
1198
|
const toSend = sendDraft !== null ? sendDraft : value;
|
|
1199
1199
|
if (toSend.trim()) onSubmit(toSend);
|
|
1200
1200
|
return;
|
|
1201
1201
|
}
|
|
1202
1202
|
if (/^(?:\x1b)?\[\d+(?:;\d+)*[~u]$/.test(input)) return;
|
|
1203
|
-
if (
|
|
1203
|
+
if (key2.backspace || key2.delete) {
|
|
1204
1204
|
if (cursor > 0) edit(value.slice(0, cursor - 1) + value.slice(cursor), cursor - 1);
|
|
1205
1205
|
return;
|
|
1206
1206
|
}
|
|
1207
|
-
if (
|
|
1207
|
+
if (key2.leftArrow) {
|
|
1208
1208
|
setCursor((c) => Math.max(0, c - 1));
|
|
1209
1209
|
return;
|
|
1210
1210
|
}
|
|
1211
|
-
if (
|
|
1211
|
+
if (key2.rightArrow) {
|
|
1212
1212
|
setCursor((c) => Math.min(value.length, c + 1));
|
|
1213
1213
|
return;
|
|
1214
1214
|
}
|
|
1215
|
-
if (
|
|
1215
|
+
if (key2.upArrow || key2.downArrow) {
|
|
1216
1216
|
const lines2 = value.split("\n");
|
|
1217
1217
|
const { row: row2, col: col2 } = locate(lines2, cursor);
|
|
1218
|
-
if (
|
|
1218
|
+
if (key2.upArrow) {
|
|
1219
1219
|
if (row2 === 0) {
|
|
1220
1220
|
onHistoryUp?.();
|
|
1221
1221
|
return;
|
|
@@ -1230,7 +1230,7 @@ function MultilineInput(props) {
|
|
|
1230
1230
|
}
|
|
1231
1231
|
return;
|
|
1232
1232
|
}
|
|
1233
|
-
if (
|
|
1233
|
+
if (key2.ctrl || key2.meta || key2.tab) return;
|
|
1234
1234
|
if (input) {
|
|
1235
1235
|
const text = input.replace(/\r\n?/g, "\n");
|
|
1236
1236
|
edit(value.slice(0, cursor) + text + value.slice(cursor), cursor + text.length);
|
|
@@ -1255,8 +1255,8 @@ function MultilineInput(props) {
|
|
|
1255
1255
|
] }) : /* @__PURE__ */ jsx2(Text2, { children: line.length ? line : " " })
|
|
1256
1256
|
] }, r)) });
|
|
1257
1257
|
}
|
|
1258
|
-
function classifyModifiedEnter(input,
|
|
1259
|
-
if (
|
|
1258
|
+
function classifyModifiedEnter(input, key2) {
|
|
1259
|
+
if (key2.return && (key2.ctrl || key2.meta || key2.shift)) return "newline";
|
|
1260
1260
|
const match = /^(?:\x1b)?\[27;(\d+);13~$/.exec(input);
|
|
1261
1261
|
if (!match) return null;
|
|
1262
1262
|
return "newline";
|
|
@@ -1368,8 +1368,8 @@ function ReplApp(props) {
|
|
|
1368
1368
|
(patch) => setStreaming((prev) => ({ ...prev, ...patch })),
|
|
1369
1369
|
[]
|
|
1370
1370
|
);
|
|
1371
|
-
useInput2((_input,
|
|
1372
|
-
if (
|
|
1371
|
+
useInput2((_input, key2) => {
|
|
1372
|
+
if (key2.escape && cancelRef.current && !interaction) {
|
|
1373
1373
|
cancelRef.current("user-esc");
|
|
1374
1374
|
return;
|
|
1375
1375
|
}
|
|
@@ -1458,10 +1458,10 @@ function ReplApp(props) {
|
|
|
1458
1458
|
});
|
|
1459
1459
|
const t0 = Date.now();
|
|
1460
1460
|
try {
|
|
1461
|
-
const
|
|
1461
|
+
const result = await presenter.consume(handle.events);
|
|
1462
1462
|
const stats = presenter.completionStats;
|
|
1463
|
-
if (
|
|
1464
|
-
bridge.showCompleted(Date.now() - t0,
|
|
1463
|
+
if (result.status === "completed") {
|
|
1464
|
+
bridge.showCompleted(Date.now() - t0, result.traceId, convRef.current ?? "", presenter.tokenCount || void 0, stats);
|
|
1465
1465
|
}
|
|
1466
1466
|
if (presenter.tokenCount) setSessionTokens((prev) => prev + presenter.tokenCount);
|
|
1467
1467
|
if (convRef.current) {
|
|
@@ -1568,9 +1568,9 @@ function ReplApp(props) {
|
|
|
1568
1568
|
const slashOpen = slashItems.length > 0;
|
|
1569
1569
|
const slashSelected = slashOpen ? wrapIndex(slashIndex, slashItems.length) : 0;
|
|
1570
1570
|
const slashColWidth = slashOpen ? Math.max(...slashItems.map((s) => s.cmd.length)) + 2 : 0;
|
|
1571
|
-
const handleSlashKey = (_input,
|
|
1571
|
+
const handleSlashKey = (_input, key2) => {
|
|
1572
1572
|
if (!slashOpen) return false;
|
|
1573
|
-
const action = resolveSlashKey(
|
|
1573
|
+
const action = resolveSlashKey(key2, slashItems[slashSelected]);
|
|
1574
1574
|
switch (action.type) {
|
|
1575
1575
|
case "move":
|
|
1576
1576
|
setSlashIndex(wrapIndex(slashSelected + action.delta, slashItems.length));
|
|
@@ -1706,8 +1706,8 @@ function agentLabel(agentId) {
|
|
|
1706
1706
|
if (/^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(agentId)) return `agent \u2026${agentId.slice(-4)}`;
|
|
1707
1707
|
return agentDisplayName(agentId);
|
|
1708
1708
|
}
|
|
1709
|
-
function originLine(
|
|
1710
|
-
const segs = (
|
|
1709
|
+
function originLine(path9) {
|
|
1710
|
+
const segs = (path9 ?? "").split(".").filter(Boolean);
|
|
1711
1711
|
if (segs.length < 2) return null;
|
|
1712
1712
|
return `\u6765\u81EA ${segs.map((k) => agentDisplayName(k)).join(" \u203A ")}`;
|
|
1713
1713
|
}
|
|
@@ -1723,9 +1723,9 @@ function InteractionView({ interaction }) {
|
|
|
1723
1723
|
}));
|
|
1724
1724
|
const askFreeText = req.kind === "ASK" && (choices.length === 0 || freeMode);
|
|
1725
1725
|
const textInputActive = rejectMode || askFreeText;
|
|
1726
|
-
useInput2((input,
|
|
1726
|
+
useInput2((input, key2) => {
|
|
1727
1727
|
if (textInputActive) return;
|
|
1728
|
-
if (
|
|
1728
|
+
if (key2.escape) {
|
|
1729
1729
|
respond({ kind: "reject" });
|
|
1730
1730
|
return;
|
|
1731
1731
|
}
|
|
@@ -2286,9 +2286,9 @@ async function loginCommand(args) {
|
|
|
2286
2286
|
} catch (e) {
|
|
2287
2287
|
const err = e;
|
|
2288
2288
|
if (err.isAxiosError) {
|
|
2289
|
-
const { fromAxiosError:
|
|
2289
|
+
const { fromAxiosError: fromAxiosError21 } = await import("@hcmai/sdk");
|
|
2290
2290
|
exitWithError(
|
|
2291
|
-
|
|
2291
|
+
fromAxiosError21(
|
|
2292
2292
|
e,
|
|
2293
2293
|
envName
|
|
2294
2294
|
)
|
|
@@ -2595,15 +2595,15 @@ async function configDeleteDeprecated(name) {
|
|
|
2595
2595
|
warnDeprecated("hcm config delete", "hcm env remove --force");
|
|
2596
2596
|
await envRemove(name, { force: true });
|
|
2597
2597
|
}
|
|
2598
|
-
async function configSet(
|
|
2598
|
+
async function configSet(key2, value) {
|
|
2599
2599
|
try {
|
|
2600
|
-
if (!
|
|
2600
|
+
if (!key2.startsWith("defaults.")) {
|
|
2601
2601
|
throw new CliError4({
|
|
2602
2602
|
code: CliErrorCode3.INVALID_ARGUMENT,
|
|
2603
2603
|
message: "hcm config set only accepts defaults.*. For env fields use 'hcm env edit <field> <value>'."
|
|
2604
2604
|
});
|
|
2605
2605
|
}
|
|
2606
|
-
const sub =
|
|
2606
|
+
const sub = key2.slice("defaults.".length);
|
|
2607
2607
|
const g = await loadGlobalConfig3();
|
|
2608
2608
|
g.defaults = g.defaults ?? {};
|
|
2609
2609
|
g.defaults[sub] = value;
|
|
@@ -2613,15 +2613,15 @@ async function configSet(key, value) {
|
|
|
2613
2613
|
exitWithError(e);
|
|
2614
2614
|
}
|
|
2615
2615
|
}
|
|
2616
|
-
async function configGet(
|
|
2616
|
+
async function configGet(key2) {
|
|
2617
2617
|
try {
|
|
2618
|
-
if (!
|
|
2618
|
+
if (!key2.startsWith("defaults.")) {
|
|
2619
2619
|
throw new CliError4({
|
|
2620
2620
|
code: CliErrorCode3.INVALID_ARGUMENT,
|
|
2621
2621
|
message: "hcm config get only accepts defaults.*. For env fields use 'hcm env show'."
|
|
2622
2622
|
});
|
|
2623
2623
|
}
|
|
2624
|
-
const sub =
|
|
2624
|
+
const sub = key2.slice("defaults.".length);
|
|
2625
2625
|
const g = await loadGlobalConfig3();
|
|
2626
2626
|
exitOk(String(g.defaults?.[sub] ?? ""));
|
|
2627
2627
|
} catch (e) {
|
|
@@ -2650,12 +2650,12 @@ async function queryCommand(model, args) {
|
|
|
2650
2650
|
if (args.offset) dsl.offset = Number(args.offset);
|
|
2651
2651
|
if (!dsl.limit) dsl.limit = 20;
|
|
2652
2652
|
const client3 = HcmClient.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2653
|
-
const
|
|
2653
|
+
const result = await client3.query(model, dsl);
|
|
2654
2654
|
const fmt = args.output ?? "table";
|
|
2655
|
-
const out = formatRows(
|
|
2655
|
+
const out = formatRows(result.rows, { format: fmt });
|
|
2656
2656
|
process.stdout.write(out + "\n");
|
|
2657
|
-
if (fmt === "table" &&
|
|
2658
|
-
process.stdout.write(`Showing ${
|
|
2657
|
+
if (fmt === "table" && result.total !== void 0) {
|
|
2658
|
+
process.stdout.write(`Showing ${result.rows.length} of ${result.total} results.
|
|
2659
2659
|
`);
|
|
2660
2660
|
}
|
|
2661
2661
|
process.exit(0);
|
|
@@ -2772,17 +2772,17 @@ async function actionCommand(spec, args) {
|
|
|
2772
2772
|
});
|
|
2773
2773
|
if (params) input.params = params;
|
|
2774
2774
|
const client3 = HcmClient2.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2775
|
-
const
|
|
2775
|
+
const result = await client3.action(model, action, input);
|
|
2776
2776
|
const fmt = args.output ?? "json";
|
|
2777
|
-
if (fmt !== "json" &&
|
|
2777
|
+
if (fmt !== "json" && result.taskId) {
|
|
2778
2778
|
process.stdout.write(
|
|
2779
|
-
`\u23F3 task created: ${
|
|
2779
|
+
`\u23F3 task created: ${result.taskId} (use \`hcm task ${result.taskId}\` to follow up \u2014 coming Phase 1)
|
|
2780
2780
|
`
|
|
2781
2781
|
);
|
|
2782
2782
|
} else if (fmt !== "json") {
|
|
2783
2783
|
process.stdout.write("\u2705 action succeeded\n");
|
|
2784
2784
|
}
|
|
2785
|
-
process.stdout.write(formatObject(
|
|
2785
|
+
process.stdout.write(formatObject(result, { format: fmt }) + "\n");
|
|
2786
2786
|
process.exit(0);
|
|
2787
2787
|
} catch (e) {
|
|
2788
2788
|
const err = e;
|
|
@@ -2856,11 +2856,11 @@ async function updateCommand(model, args) {
|
|
|
2856
2856
|
fileFlag: "--data-file"
|
|
2857
2857
|
});
|
|
2858
2858
|
const client3 = HcmClient4.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2859
|
-
const
|
|
2859
|
+
const result = await client3.update(model, args.id, data);
|
|
2860
2860
|
const fmt = args.output ?? "json";
|
|
2861
2861
|
if (fmt !== "json") process.stdout.write(`\u2705 ${model}#${args.id} \u5DF2\u66F4\u65B0
|
|
2862
2862
|
`);
|
|
2863
|
-
process.stdout.write(formatObject3(
|
|
2863
|
+
process.stdout.write(formatObject3(result, { format: fmt }) + "\n");
|
|
2864
2864
|
process.exit(0);
|
|
2865
2865
|
} catch (e) {
|
|
2866
2866
|
const err = e;
|
|
@@ -2941,20 +2941,20 @@ async function settingSetCommand(domain, args) {
|
|
|
2941
2941
|
process.exit(0);
|
|
2942
2942
|
}
|
|
2943
2943
|
const c = await client(args);
|
|
2944
|
-
const
|
|
2944
|
+
const result = await patchSettingDomain(c.raw(), domain, items);
|
|
2945
2945
|
const fmt = args.output ?? "json";
|
|
2946
2946
|
if (fmt !== "json") process.stdout.write(`\u2705 ${domain} \u5DF2\u66F4\u65B0 ${items.length} \u9879
|
|
2947
2947
|
`);
|
|
2948
|
-
process.stdout.write(formatObject4(
|
|
2948
|
+
process.stdout.write(formatObject4(result, { format: fmt }) + "\n");
|
|
2949
2949
|
process.exit(0);
|
|
2950
2950
|
} catch (e) {
|
|
2951
2951
|
fail(e, args);
|
|
2952
2952
|
}
|
|
2953
2953
|
}
|
|
2954
|
-
async function settingResetCommand(domain, namespace,
|
|
2954
|
+
async function settingResetCommand(domain, namespace, key2, args) {
|
|
2955
2955
|
try {
|
|
2956
2956
|
if (!args.yes) {
|
|
2957
|
-
const ok = await promptConfirm(`\u5C06\u6E05\u9664 ${domain} / ${namespace}.${
|
|
2957
|
+
const ok = await promptConfirm(`\u5C06\u6E05\u9664 ${domain} / ${namespace}.${key2} \u7684\u79DF\u6237\u8986\u76D6\uFF0C\u56DE\u843D\u5230\u9ED8\u8BA4\u503C\u3002\u7EE7\u7EED\uFF1F`);
|
|
2958
2958
|
if (!ok) {
|
|
2959
2959
|
process.stderr.write("\u5DF2\u53D6\u6D88\n");
|
|
2960
2960
|
process.exit(1);
|
|
@@ -2962,11 +2962,11 @@ async function settingResetCommand(domain, namespace, key, args) {
|
|
|
2962
2962
|
}
|
|
2963
2963
|
const c = await client(args);
|
|
2964
2964
|
const revision = args.revision === void 0 ? void 0 : Number(args.revision);
|
|
2965
|
-
const
|
|
2965
|
+
const result = await resetSettingItem(c.raw(), domain, namespace, key2, revision);
|
|
2966
2966
|
const fmt = args.output ?? "json";
|
|
2967
|
-
if (fmt !== "json") process.stdout.write(`\u2705 ${namespace}.${
|
|
2967
|
+
if (fmt !== "json") process.stdout.write(`\u2705 ${namespace}.${key2} \u5DF2\u56DE\u843D\u5230\u9ED8\u8BA4\u503C
|
|
2968
2968
|
`);
|
|
2969
|
-
process.stdout.write(formatObject4(
|
|
2969
|
+
process.stdout.write(formatObject4(result, { format: fmt }) + "\n");
|
|
2970
2970
|
process.exit(0);
|
|
2971
2971
|
} catch (e) {
|
|
2972
2972
|
fail(e, args);
|
|
@@ -3059,8 +3059,8 @@ import {
|
|
|
3059
3059
|
formatObject as formatObject7,
|
|
3060
3060
|
fromAxiosError as fromAxiosError8
|
|
3061
3061
|
} from "@hcmai/sdk";
|
|
3062
|
-
function collectYmlFiles(
|
|
3063
|
-
if (statSync(
|
|
3062
|
+
function collectYmlFiles(path9) {
|
|
3063
|
+
if (statSync(path9).isFile()) return [path9];
|
|
3064
3064
|
const out = [];
|
|
3065
3065
|
const walk = (dir) => {
|
|
3066
3066
|
for (const name of readdirSync(dir).sort()) {
|
|
@@ -3069,7 +3069,7 @@ function collectYmlFiles(path11) {
|
|
|
3069
3069
|
else if (full.endsWith(".yml") || full.endsWith(".yaml")) out.push(full);
|
|
3070
3070
|
}
|
|
3071
3071
|
};
|
|
3072
|
-
walk(
|
|
3072
|
+
walk(path9);
|
|
3073
3073
|
return out.sort();
|
|
3074
3074
|
}
|
|
3075
3075
|
var DRY_RUN_CLIENT = {
|
|
@@ -3077,9 +3077,9 @@ var DRY_RUN_CLIENT = {
|
|
|
3077
3077
|
create: async () => ({ id: "" }),
|
|
3078
3078
|
update: async () => ({})
|
|
3079
3079
|
};
|
|
3080
|
-
async function importCommand(
|
|
3080
|
+
async function importCommand(path9, args) {
|
|
3081
3081
|
try {
|
|
3082
|
-
const files = collectYmlFiles(
|
|
3082
|
+
const files = collectYmlFiles(path9);
|
|
3083
3083
|
const fixtures = files.map(
|
|
3084
3084
|
(f) => parseFixture(readFileSync2(f, "utf8"), relative(process.cwd(), f))
|
|
3085
3085
|
);
|
|
@@ -3089,16 +3089,16 @@ async function importCommand(path11, args) {
|
|
|
3089
3089
|
onError: args.onError === "stop" ? "stop" : "continue"
|
|
3090
3090
|
};
|
|
3091
3091
|
const client3 = opts.dryRun ? DRY_RUN_CLIENT : HcmClient7.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
3092
|
-
const
|
|
3093
|
-
if (args.report) writeFileSync(args.report, JSON.stringify(
|
|
3092
|
+
const result = await runImport(client3, fixtures, opts);
|
|
3093
|
+
if (args.report) writeFileSync(args.report, JSON.stringify(result, null, 2));
|
|
3094
3094
|
const fmt = args.output ?? "json";
|
|
3095
3095
|
const tag = opts.dryRun ? "\u{1F50D} dry-run" : "\u{1F4E6} import";
|
|
3096
3096
|
process.stdout.write(
|
|
3097
|
-
`${tag}: created=${
|
|
3097
|
+
`${tag}: created=${result.created} updated=${result.updated} skipped=${result.skipped} failed=${result.failed}
|
|
3098
3098
|
`
|
|
3099
3099
|
);
|
|
3100
|
-
process.stdout.write(formatObject7(
|
|
3101
|
-
process.exit(
|
|
3100
|
+
process.stdout.write(formatObject7(result, { format: fmt }) + "\n");
|
|
3101
|
+
process.exit(result.failed > 0 ? 3 : 0);
|
|
3102
3102
|
} catch (e) {
|
|
3103
3103
|
const err = e;
|
|
3104
3104
|
if (err?.isAxiosError) exitWithError(fromAxiosError8(err, args.profile));
|
|
@@ -3314,8 +3314,8 @@ function stableJson(value) {
|
|
|
3314
3314
|
}
|
|
3315
3315
|
}
|
|
3316
3316
|
function eventPreview(value) {
|
|
3317
|
-
for (const
|
|
3318
|
-
if (typeof value[
|
|
3317
|
+
for (const key2 of ["text", "content", "summary", "question", "detail", "message"]) {
|
|
3318
|
+
if (typeof value[key2] === "string" && value[key2].trim()) return oneLine(value[key2], 86);
|
|
3319
3319
|
}
|
|
3320
3320
|
const tools = value.tools;
|
|
3321
3321
|
if (Array.isArray(tools) && tools.length) {
|
|
@@ -3389,13 +3389,13 @@ async function printConversationList(args) {
|
|
|
3389
3389
|
const ctx = await getActiveAuthContextFromCli(args);
|
|
3390
3390
|
const client3 = HcmClient10.fromAuthContext(ctx);
|
|
3391
3391
|
const limit = parsePositiveInt(args.limit, 10, "--limit");
|
|
3392
|
-
const
|
|
3392
|
+
const result = await fetchRecentConversations(client3.raw(), limit);
|
|
3393
3393
|
const fmt = args.output ?? "text";
|
|
3394
3394
|
if (fmt === "json") {
|
|
3395
|
-
process.stdout.write(formatObject9(
|
|
3395
|
+
process.stdout.write(formatObject9(result, { format: "json" }) + "\n");
|
|
3396
3396
|
return;
|
|
3397
3397
|
}
|
|
3398
|
-
const rows =
|
|
3398
|
+
const rows = result.rows.map((row) => ({
|
|
3399
3399
|
UPDATED: formatShortDate(row.lastActiveAt ?? row.updateTime ?? row.createTime),
|
|
3400
3400
|
MSGS: row.messageCount ?? 0,
|
|
3401
3401
|
TOKENS: row.totalTokens ?? 0,
|
|
@@ -3403,8 +3403,8 @@ async function printConversationList(args) {
|
|
|
3403
3403
|
CONVERSATION: row.id ?? "-"
|
|
3404
3404
|
}));
|
|
3405
3405
|
process.stdout.write(formatRows3(rows, { format: "table" }) + "\n");
|
|
3406
|
-
if (
|
|
3407
|
-
process.stdout.write(`Showing ${
|
|
3406
|
+
if (result.total !== void 0) {
|
|
3407
|
+
process.stdout.write(`Showing ${result.rows.length} of ${result.total} conversations.
|
|
3408
3408
|
`);
|
|
3409
3409
|
}
|
|
3410
3410
|
}
|
|
@@ -3676,9 +3676,9 @@ async function chatCommand(prompt, args) {
|
|
|
3676
3676
|
process.once("SIGINT", sigintHandler);
|
|
3677
3677
|
const t0 = Date.now();
|
|
3678
3678
|
try {
|
|
3679
|
-
const
|
|
3679
|
+
const result = await presenter.consume(handle.events);
|
|
3680
3680
|
const stats = presenter.completionStats;
|
|
3681
|
-
renderer.showCompleted(Date.now() - t0,
|
|
3681
|
+
renderer.showCompleted(Date.now() - t0, result.traceId, handle.conversationId, presenter.tokenCount || void 0, {
|
|
3682
3682
|
inputTokens: stats.inputTokens,
|
|
3683
3683
|
outputTokens: stats.outputTokens,
|
|
3684
3684
|
toolCount: stats.toolCount,
|
|
@@ -3687,8 +3687,8 @@ async function chatCommand(prompt, args) {
|
|
|
3687
3687
|
await ws.close();
|
|
3688
3688
|
await saveConversationState2(stateKey, {
|
|
3689
3689
|
lastConversationId: handle.conversationId,
|
|
3690
|
-
lastStreamId:
|
|
3691
|
-
lastTraceId:
|
|
3690
|
+
lastStreamId: result.streamId,
|
|
3691
|
+
lastTraceId: result.traceId ?? void 0,
|
|
3692
3692
|
lastAgentKey: args.agent,
|
|
3693
3693
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3694
3694
|
});
|
|
@@ -3700,7 +3700,7 @@ async function chatCommand(prompt, args) {
|
|
|
3700
3700
|
resolve4();
|
|
3701
3701
|
}
|
|
3702
3702
|
});
|
|
3703
|
-
process.exit(
|
|
3703
|
+
process.exit(result.status === "interrupted" ? 130 : 0);
|
|
3704
3704
|
} catch (e) {
|
|
3705
3705
|
process.removeListener("SIGINT", sigintHandler);
|
|
3706
3706
|
await ws.close();
|
|
@@ -4192,10 +4192,10 @@ async function withClient(args, fn) {
|
|
|
4192
4192
|
const client3 = HcmClient13.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
4193
4193
|
return fn(client3);
|
|
4194
4194
|
}
|
|
4195
|
-
function emit(
|
|
4195
|
+
function emit(result, args, human) {
|
|
4196
4196
|
const fmt = args.output ?? "json";
|
|
4197
4197
|
if (fmt !== "json") process.stdout.write(human + "\n");
|
|
4198
|
-
process.stdout.write(formatObject11(
|
|
4198
|
+
process.stdout.write(formatObject11(result, { format: fmt }) + "\n");
|
|
4199
4199
|
process.exit(0);
|
|
4200
4200
|
}
|
|
4201
4201
|
function fail2(e, args) {
|
|
@@ -4214,20 +4214,20 @@ async function cacheStatsCommand(model, args) {
|
|
|
4214
4214
|
}
|
|
4215
4215
|
async function cacheClearCommand(model, args) {
|
|
4216
4216
|
try {
|
|
4217
|
-
const
|
|
4218
|
-
emit(
|
|
4217
|
+
const result = await withClient(args, (c) => clearModelCache(c.raw(), model, args.id));
|
|
4218
|
+
emit(result, args, args.id ? `\u{1F9F9} \u5DF2\u6E05 ${model}#${args.id} \u7F13\u5B58` : `\u{1F9F9} \u5DF2\u6E05 ${model} \u5168\u90E8\u5B9E\u4F53\u7F13\u5B58`);
|
|
4219
4219
|
} catch (e) {
|
|
4220
4220
|
fail2(e, args);
|
|
4221
4221
|
}
|
|
4222
4222
|
}
|
|
4223
4223
|
async function cacheClearMetaCommand(model, args) {
|
|
4224
4224
|
try {
|
|
4225
|
-
const
|
|
4225
|
+
const result = await withClient(
|
|
4226
4226
|
args,
|
|
4227
4227
|
(c) => clearMetaCache(c.raw(), model, { type: args.type, state: args.state })
|
|
4228
4228
|
);
|
|
4229
4229
|
const scope = args.type ? `${args.type}${args.state ? `.${args.state}` : ""}` : "\u5168\u90E8";
|
|
4230
|
-
emit(
|
|
4230
|
+
emit(result, args, `\u{1F9F9} \u5DF2\u6E05 ${model} \u5143\u6570\u636E\u7F13\u5B58\uFF08${scope}\uFF09`);
|
|
4231
4231
|
} catch (e) {
|
|
4232
4232
|
fail2(e, args);
|
|
4233
4233
|
}
|
|
@@ -4280,21 +4280,20 @@ async function uploadCommand(filePath, args) {
|
|
|
4280
4280
|
init_exit();
|
|
4281
4281
|
import { promises as fsp2 } from "fs";
|
|
4282
4282
|
import os2 from "os";
|
|
4283
|
-
import
|
|
4284
|
-
import { createHash as
|
|
4283
|
+
import path4 from "path";
|
|
4284
|
+
import { createHash as createHash2 } from "crypto";
|
|
4285
4285
|
import {
|
|
4286
4286
|
createHttpClient as createHttpClient2,
|
|
4287
4287
|
fetchSkillCatalog,
|
|
4288
|
-
fetchSkillFile,
|
|
4289
4288
|
fetchSkillMarkdown,
|
|
4290
4289
|
fetchSkillReference,
|
|
4291
4290
|
resolveChironBase,
|
|
4292
|
-
assertSafeSkillId
|
|
4291
|
+
assertSafeSkillId,
|
|
4293
4292
|
assertSafeReferencePath as assertSafeReferencePath2,
|
|
4294
|
-
normalizeReferences
|
|
4293
|
+
normalizeReferences,
|
|
4295
4294
|
parseSkillRequirements as parseSkillRequirements2,
|
|
4296
4295
|
parseSkillFrontmatter as parseSkillFrontmatter2,
|
|
4297
|
-
resolveSkillInstallOrder
|
|
4296
|
+
resolveSkillInstallOrder,
|
|
4298
4297
|
resolveAllSkillsInstallOrder,
|
|
4299
4298
|
matchSkills,
|
|
4300
4299
|
filterByStage,
|
|
@@ -4303,8 +4302,8 @@ import {
|
|
|
4303
4302
|
loadEnv as loadEnv5,
|
|
4304
4303
|
loadGlobalConfig as loadGlobalConfig6,
|
|
4305
4304
|
resolveActiveEnv as resolveActiveEnv3,
|
|
4306
|
-
CliError as
|
|
4307
|
-
CliErrorCode as
|
|
4305
|
+
CliError as CliError16,
|
|
4306
|
+
CliErrorCode as CliErrorCode15
|
|
4308
4307
|
} from "@hcmai/sdk";
|
|
4309
4308
|
|
|
4310
4309
|
// src/commands/skills-catalog.ts
|
|
@@ -4593,810 +4592,20 @@ async function resolveBundledClosure(targetId, dir = bundledSkillsDir()) {
|
|
|
4593
4592
|
return { documents, external: [...external].sort() };
|
|
4594
4593
|
}
|
|
4595
4594
|
|
|
4596
|
-
// src/commands/skills
|
|
4597
|
-
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
import {
|
|
4601
|
-
CliError as CliError16,
|
|
4602
|
-
CliErrorCode as CliErrorCode15,
|
|
4603
|
-
assertSafeSkillFilePath,
|
|
4604
|
-
assertSafeSkillId,
|
|
4605
|
-
normalizeReferences,
|
|
4606
|
-
resolveSkillInstallOrder
|
|
4607
|
-
} from "@hcmai/sdk";
|
|
4608
|
-
async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fetchFile, fetchReference) {
|
|
4609
|
-
const order = resolveSkillInstallOrder(catalog, requestedSkill);
|
|
4610
|
-
for (const skill of order) {
|
|
4611
|
-
if (skill.actionSurface !== "cli") {
|
|
4612
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u5C1A\u672A\u6536\u655B\u5230 hcm CLI\uFF0C\u4E0D\u80FD\u540C\u6B65\u4E3A WorkBuddy \u6B63\u5F0F\u6267\u884C Skill`);
|
|
4613
|
-
}
|
|
4614
|
-
}
|
|
4615
|
-
return Promise.all(
|
|
4616
|
-
order.map(async (skill) => {
|
|
4617
|
-
let files;
|
|
4618
|
-
if (skill.files === void 0) {
|
|
4619
|
-
const markdown2 = await fetchMarkdown(skill.id);
|
|
4620
|
-
const content = Buffer.from(markdown2, "utf8");
|
|
4621
|
-
const references = normalizeReferences(skill.references, skill.id);
|
|
4622
|
-
if (references.length > 0 && !fetchReference) {
|
|
4623
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u58F0\u660E\u4E86 references\uFF0C\u4F46\u8C03\u7528\u65B9\u6CA1\u6709\u53C2\u8003\u6587\u4EF6\u4E0B\u8F7D\u80FD\u529B`);
|
|
4624
|
-
}
|
|
4625
|
-
const referenceFiles = await Promise.all(
|
|
4626
|
-
references.map(async (relativePath) => {
|
|
4627
|
-
const targetPath = `references/${relativePath}`;
|
|
4628
|
-
assertSafeSkillFilePath(targetPath);
|
|
4629
|
-
const fetched = await fetchReference(skill.id, relativePath);
|
|
4630
|
-
const referenceContent = Buffer.isBuffer(fetched) ? Buffer.from(fetched) : Buffer.from(fetched, "utf8");
|
|
4631
|
-
return {
|
|
4632
|
-
path: targetPath,
|
|
4633
|
-
content: referenceContent,
|
|
4634
|
-
bytes: referenceContent.length,
|
|
4635
|
-
sha256: sha2562(referenceContent)
|
|
4636
|
-
};
|
|
4637
|
-
})
|
|
4638
|
-
);
|
|
4639
|
-
files = [
|
|
4640
|
-
{
|
|
4641
|
-
path: "SKILL.md",
|
|
4642
|
-
content,
|
|
4643
|
-
bytes: content.length,
|
|
4644
|
-
sha256: sha2562(content)
|
|
4645
|
-
},
|
|
4646
|
-
...referenceFiles
|
|
4647
|
-
];
|
|
4648
|
-
} else {
|
|
4649
|
-
if (!Array.isArray(skill.files) || skill.files.length === 0) {
|
|
4650
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u76EE\u5F55\u4E3A\u7A7A`);
|
|
4651
|
-
}
|
|
4652
|
-
if (!fetchFile) throw invalid(`\u6280\u80FD ${skill.id} \u63D0\u4F9B\u5B8C\u6574\u76EE\u5F55\uFF0C\u4F46\u8C03\u7528\u65B9\u6CA1\u6709\u6587\u4EF6\u4E0B\u8F7D\u80FD\u529B`);
|
|
4653
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4654
|
-
for (const file of skill.files) {
|
|
4655
|
-
assertSafeSkillFilePath(file.path);
|
|
4656
|
-
if (seen.has(file.path))
|
|
4657
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u5305\u542B\u91CD\u590D\u8DEF\u5F84: ${file.path}`);
|
|
4658
|
-
seen.add(file.path);
|
|
4659
|
-
if (!Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4660
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u7684\u6587\u4EF6\u76EE\u5F55\u5143\u6570\u636E\u975E\u6CD5: ${file.path}`);
|
|
4661
|
-
}
|
|
4662
|
-
}
|
|
4663
|
-
if (!seen.has("SKILL.md")) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4664
|
-
files = await Promise.all(
|
|
4665
|
-
skill.files.map(async (file) => {
|
|
4666
|
-
const content = Buffer.from(await fetchFile(skill.id, file.path));
|
|
4667
|
-
if (content.length !== file.bytes) {
|
|
4668
|
-
throw invalid(
|
|
4669
|
-
`\u6280\u80FD ${skill.id}/${file.path} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${file.bytes}\uFF0C\u5B9E\u9645=${content.length}`
|
|
4670
|
-
);
|
|
4671
|
-
}
|
|
4672
|
-
const digest = sha2562(content);
|
|
4673
|
-
if (digest !== file.sha256) {
|
|
4674
|
-
throw invalid(`\u6280\u80FD ${skill.id}/${file.path} \u6458\u8981\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4`);
|
|
4675
|
-
}
|
|
4676
|
-
return { path: file.path, content, bytes: content.length, sha256: digest };
|
|
4677
|
-
})
|
|
4678
|
-
);
|
|
4679
|
-
}
|
|
4680
|
-
const root = files.find((file) => file.path === "SKILL.md");
|
|
4681
|
-
if (!root) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4682
|
-
const markdown = root.content.toString("utf8");
|
|
4683
|
-
if (!markdown.trim()) throw invalid(`\u6280\u80FD ${skill.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4684
|
-
validateWorkBuddyFrontmatter(markdown, skill.id);
|
|
4685
|
-
const bytes = root.bytes;
|
|
4686
|
-
if (Number.isFinite(skill.bytes) && skill.bytes > 0 && skill.bytes !== bytes) {
|
|
4687
|
-
throw invalid(`\u6280\u80FD ${skill.id} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${skill.bytes}\uFF0C\u5B9E\u9645=${bytes}`);
|
|
4688
|
-
}
|
|
4689
|
-
return { id: skill.id, markdown, bytes, sha256: root.sha256, files };
|
|
4690
|
-
})
|
|
4691
|
-
);
|
|
4692
|
-
}
|
|
4693
|
-
async function syncWorkBuddyProject(input) {
|
|
4694
|
-
const project = path4.resolve(input.project);
|
|
4695
|
-
const metaDir = path4.join(project, ".hcm-delivery");
|
|
4696
|
-
const skillsDir = path4.join(project, ".codebuddy", "skills");
|
|
4697
|
-
const lockPath = path4.join(metaDir, "chiron.lock.json");
|
|
4698
|
-
assertSafeSkillId(input.requestedSkill);
|
|
4699
|
-
validatePreparedDocuments(input.documents, input.requestedSkill);
|
|
4700
|
-
validatePreparedRuntime(input.runtime);
|
|
4701
|
-
await recoverInterruptedSyncs(project, metaDir, lockPath);
|
|
4702
|
-
const oldLock = await readLock(lockPath);
|
|
4703
|
-
const skillEntries = input.documents.flatMap(
|
|
4704
|
-
(document) => document.files.map((file) => [ownedFile(document.id, file.path), file])
|
|
4705
|
-
);
|
|
4706
|
-
const runtimeEntries = input.runtime.files.map((file) => [file.path, file]);
|
|
4707
|
-
const desiredEntries = [
|
|
4708
|
-
...skillEntries,
|
|
4709
|
-
...runtimeEntries
|
|
4710
|
-
];
|
|
4711
|
-
const desiredFiles = desiredEntries.map(([file]) => file);
|
|
4712
|
-
const desiredByFile = new Map(desiredEntries);
|
|
4713
|
-
const oldOwned = new Set(oldLock?.ownedFiles ?? []);
|
|
4714
|
-
const oldDigestByFile = lockDigests(oldLock);
|
|
4715
|
-
for (const file of oldOwned) validateOwnedFile(file);
|
|
4716
|
-
for (const file of desiredFiles) {
|
|
4717
|
-
const absolute = path4.join(project, file);
|
|
4718
|
-
const current = await readOptionalBytes(absolute);
|
|
4719
|
-
if (current !== void 0 && !oldOwned.has(file)) {
|
|
4720
|
-
throw invalid(`\u76EE\u6807\u6587\u4EF6\u4E0D\u5C5E\u4E8E\u4E0A\u4E00\u4EFD Chiron \u9501\uFF0C\u62D2\u7EDD\u8986\u76D6\u987E\u95EE\u6587\u4EF6\uFF1A${file}`);
|
|
4721
|
-
}
|
|
4722
|
-
}
|
|
4723
|
-
for (const file of oldOwned) {
|
|
4724
|
-
const absolute = path4.join(project, file);
|
|
4725
|
-
const current = await readOptionalBytes(absolute);
|
|
4726
|
-
if (current === void 0) continue;
|
|
4727
|
-
const expected = oldDigestByFile.get(file);
|
|
4728
|
-
if (!expected || sha2562(current) !== expected.sha256 || !await fileModeMatches(absolute, expected.mode)) {
|
|
4729
|
-
throw invalid(`\u4E0A\u4E00\u4EFD\u9501\u62E5\u6709\u7684\u751F\u6210\u6587\u4EF6\u5DF2\u88AB\u4EBA\u5DE5\u4FEE\u6539\uFF0C\u62D2\u7EDD\u8986\u76D6\u6216\u6E05\u7406\uFF1A${file}`);
|
|
4730
|
-
}
|
|
4731
|
-
}
|
|
4732
|
-
const newLock = {
|
|
4733
|
-
schemaVersion: 1,
|
|
4734
|
-
source: {
|
|
4735
|
-
endpointOriginSha256: sha2562(normalizeEndpoint(input.endpoint)),
|
|
4736
|
-
catalogPath: "/skills.json",
|
|
4737
|
-
catalogSha256: sha2562(stableJson2(input.catalog))
|
|
4738
|
-
},
|
|
4739
|
-
selection: { requestedSkill: input.requestedSkill },
|
|
4740
|
-
resolvedSkills: input.documents.map(({ id, bytes, sha256: digest, files: skillFiles }) => ({
|
|
4741
|
-
id,
|
|
4742
|
-
bytes,
|
|
4743
|
-
sha256: digest,
|
|
4744
|
-
files: skillFiles.map(({ path: filePath, bytes: fileBytes, sha256: fileSha256 }) => ({
|
|
4745
|
-
path: filePath,
|
|
4746
|
-
bytes: fileBytes,
|
|
4747
|
-
sha256: fileSha256
|
|
4748
|
-
}))
|
|
4749
|
-
})),
|
|
4750
|
-
runtime: {
|
|
4751
|
-
capabilities: input.runtime.capabilities,
|
|
4752
|
-
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4753
|
-
launch: input.runtime.launch,
|
|
4754
|
-
files: input.runtime.files.map(({ path: filePath, bytes, sha256: digest, mode }) => ({
|
|
4755
|
-
path: filePath,
|
|
4756
|
-
bytes,
|
|
4757
|
-
sha256: digest,
|
|
4758
|
-
mode
|
|
4759
|
-
}))
|
|
4760
|
-
},
|
|
4761
|
-
ownedFiles: desiredFiles,
|
|
4762
|
-
syncedAt: input.now ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4763
|
-
};
|
|
4764
|
-
if (oldLock && sameLockContent(oldLock, newLock) && await desiredFilesMatch(project, desiredByFile)) {
|
|
4765
|
-
return result(false, input, project, skillsDir, lockPath, [], []);
|
|
4766
|
-
}
|
|
4767
|
-
const obsolete = [...oldOwned].filter((file) => !desiredByFile.has(file));
|
|
4768
|
-
await fs5.mkdir(metaDir, { recursive: true });
|
|
4769
|
-
const staging = await fs5.mkdtemp(path4.join(metaDir, ".staging-"));
|
|
4770
|
-
const stagedFiles = path4.join(staging, "new");
|
|
4771
|
-
const backups = path4.join(staging, "backup");
|
|
4772
|
-
const stagedLock = path4.join(staging, "chiron.lock.json");
|
|
4773
|
-
const journalPath = path4.join(staging, "journal.json");
|
|
4774
|
-
let journalReady = false;
|
|
4775
|
-
try {
|
|
4776
|
-
for (const [file, preparedFile2] of desiredByFile) {
|
|
4777
|
-
const staged = path4.join(stagedFiles, file);
|
|
4778
|
-
await fs5.mkdir(path4.dirname(staged), { recursive: true });
|
|
4779
|
-
await fs5.writeFile(
|
|
4780
|
-
staged,
|
|
4781
|
-
preparedFile2.content,
|
|
4782
|
-
preparedFile2.mode === void 0 ? void 0 : { mode: preparedFile2.mode }
|
|
4783
|
-
);
|
|
4784
|
-
}
|
|
4785
|
-
await fs5.writeFile(stagedLock, `${JSON.stringify(newLock, null, 2)}
|
|
4786
|
-
`, "utf8");
|
|
4787
|
-
const affected = [.../* @__PURE__ */ new Set([...desiredFiles, ...obsolete])];
|
|
4788
|
-
for (const file of affected) {
|
|
4789
|
-
const target = path4.join(project, file);
|
|
4790
|
-
if (!await exists(target)) continue;
|
|
4791
|
-
const backup = path4.join(backups, file);
|
|
4792
|
-
await fs5.mkdir(path4.dirname(backup), { recursive: true });
|
|
4793
|
-
await fs5.copyFile(target, backup);
|
|
4794
|
-
}
|
|
4795
|
-
const oldLockExisted = await exists(lockPath);
|
|
4796
|
-
if (oldLockExisted) {
|
|
4797
|
-
const backupLock = path4.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4798
|
-
await fs5.mkdir(path4.dirname(backupLock), { recursive: true });
|
|
4799
|
-
await fs5.copyFile(lockPath, backupLock);
|
|
4800
|
-
}
|
|
4801
|
-
const journal = {
|
|
4802
|
-
schemaVersion: 1,
|
|
4803
|
-
project,
|
|
4804
|
-
affectedFiles: affected,
|
|
4805
|
-
oldLockExisted
|
|
4806
|
-
};
|
|
4807
|
-
await fs5.writeFile(journalPath, `${JSON.stringify(journal, null, 2)}
|
|
4808
|
-
`, "utf8");
|
|
4809
|
-
journalReady = true;
|
|
4810
|
-
for (const file of desiredFiles) {
|
|
4811
|
-
const target = path4.join(project, file);
|
|
4812
|
-
await fs5.mkdir(path4.dirname(target), { recursive: true });
|
|
4813
|
-
await fs5.rm(target, { force: true });
|
|
4814
|
-
await fs5.rename(path4.join(stagedFiles, file), target);
|
|
4815
|
-
}
|
|
4816
|
-
for (const file of obsolete) await fs5.unlink(path4.join(project, file)).catch(ignoreMissing);
|
|
4817
|
-
await fs5.rm(lockPath, { force: true });
|
|
4818
|
-
await fs5.rename(stagedLock, lockPath);
|
|
4819
|
-
await fs5.unlink(journalPath);
|
|
4820
|
-
journalReady = false;
|
|
4821
|
-
} catch (cause) {
|
|
4822
|
-
if (journalReady) await recoverStaging(project, staging, lockPath);
|
|
4823
|
-
throw cause;
|
|
4824
|
-
} finally {
|
|
4825
|
-
await fs5.rm(staging, { recursive: true, force: true });
|
|
4826
|
-
}
|
|
4827
|
-
return result(
|
|
4828
|
-
true,
|
|
4829
|
-
input,
|
|
4830
|
-
project,
|
|
4831
|
-
skillsDir,
|
|
4832
|
-
lockPath,
|
|
4833
|
-
input.documents.map((document) => document.id),
|
|
4834
|
-
[...new Set(obsolete.map(idFromOwnedFile).filter((id) => id !== void 0))]
|
|
4835
|
-
);
|
|
4836
|
-
}
|
|
4837
|
-
async function recoverInterruptedSyncs(project, metaDir, lockPath) {
|
|
4838
|
-
let entries;
|
|
4839
|
-
try {
|
|
4840
|
-
entries = await fs5.readdir(metaDir, { withFileTypes: true });
|
|
4841
|
-
} catch (cause) {
|
|
4842
|
-
if (cause.code === "ENOENT") return;
|
|
4843
|
-
throw cause;
|
|
4844
|
-
}
|
|
4845
|
-
for (const entry of entries) {
|
|
4846
|
-
if (!entry.isDirectory() || !entry.name.startsWith(".staging-")) continue;
|
|
4847
|
-
const staging = path4.join(metaDir, entry.name);
|
|
4848
|
-
if (await exists(path4.join(staging, "journal.json"))) {
|
|
4849
|
-
await recoverStaging(project, staging, lockPath);
|
|
4850
|
-
}
|
|
4851
|
-
await fs5.rm(staging, { recursive: true, force: true });
|
|
4852
|
-
}
|
|
4853
|
-
}
|
|
4854
|
-
async function recoverStaging(project, staging, lockPath) {
|
|
4855
|
-
const journalPath = path4.join(staging, "journal.json");
|
|
4856
|
-
const journal = parseJournal(await fs5.readFile(journalPath, "utf8"), project);
|
|
4857
|
-
const backups = path4.join(staging, "backup");
|
|
4858
|
-
for (const file of journal.affectedFiles) {
|
|
4859
|
-
const target = path4.join(project, file);
|
|
4860
|
-
const backup = path4.join(backups, file);
|
|
4861
|
-
if (await exists(backup)) {
|
|
4862
|
-
await fs5.mkdir(path4.dirname(target), { recursive: true });
|
|
4863
|
-
await fs5.rm(target, { force: true });
|
|
4864
|
-
await fs5.rename(backup, target);
|
|
4865
|
-
} else {
|
|
4866
|
-
await fs5.rm(target, { force: true });
|
|
4867
|
-
}
|
|
4868
|
-
}
|
|
4869
|
-
const backupLock = path4.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4870
|
-
if (journal.oldLockExisted) {
|
|
4871
|
-
if (!await exists(backupLock)) throw invalid(`\u540C\u6B65\u6062\u590D\u7F3A\u5C11\u65E7\u9501\u5907\u4EFD\uFF1A${backupLock}`);
|
|
4872
|
-
await fs5.mkdir(path4.dirname(lockPath), { recursive: true });
|
|
4873
|
-
await fs5.rm(lockPath, { force: true });
|
|
4874
|
-
await fs5.rename(backupLock, lockPath);
|
|
4875
|
-
} else {
|
|
4876
|
-
await fs5.rm(lockPath, { force: true });
|
|
4877
|
-
}
|
|
4878
|
-
}
|
|
4879
|
-
function parseJournal(content, project) {
|
|
4880
|
-
let journal;
|
|
4881
|
-
try {
|
|
4882
|
-
journal = JSON.parse(content);
|
|
4883
|
-
} catch (cause) {
|
|
4884
|
-
throw new CliError16({
|
|
4885
|
-
code: CliErrorCode15.CONFIG_PARSE_ERROR,
|
|
4886
|
-
message: "Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u635F\u574F",
|
|
4887
|
-
cause
|
|
4888
|
-
});
|
|
4889
|
-
}
|
|
4890
|
-
if (journal.schemaVersion !== 1 || journal.project !== project || !Array.isArray(journal.affectedFiles) || typeof journal.oldLockExisted !== "boolean") {
|
|
4891
|
-
throw invalid("Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u5408\u540C\u4E0D\u5339\u914D\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6062\u590D\u8303\u56F4");
|
|
4892
|
-
}
|
|
4893
|
-
for (const file of journal.affectedFiles) validateOwnedFile(file);
|
|
4894
|
-
return journal;
|
|
4895
|
-
}
|
|
4896
|
-
function validatePreparedDocuments(documents, requestedSkill) {
|
|
4897
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4898
|
-
for (const document of documents) {
|
|
4899
|
-
assertSafeSkillId(document.id);
|
|
4900
|
-
if (seen.has(document.id)) throw invalid(`\u540C\u6B65\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
4901
|
-
seen.add(document.id);
|
|
4902
|
-
if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4903
|
-
const paths = /* @__PURE__ */ new Set();
|
|
4904
|
-
for (const file of document.files) {
|
|
4905
|
-
assertSafeSkillFilePath(file.path);
|
|
4906
|
-
if (paths.has(file.path)) throw invalid(`\u6280\u80FD ${document.id} \u5305\u542B\u91CD\u590D\u6587\u4EF6: ${file.path}`);
|
|
4907
|
-
paths.add(file.path);
|
|
4908
|
-
if (file.content.length !== file.bytes || sha2562(file.content) !== file.sha256) {
|
|
4909
|
-
throw invalid(`\u6280\u80FD ${document.id}/${file.path} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4910
|
-
}
|
|
4911
|
-
}
|
|
4912
|
-
const root = document.files.find((file) => file.path === "SKILL.md");
|
|
4913
|
-
if (!root || !root.content.equals(Buffer.from(document.markdown, "utf8")) || root.bytes !== document.bytes || root.sha256 !== document.sha256) {
|
|
4914
|
-
throw invalid(`\u6280\u80FD ${document.id} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4915
|
-
}
|
|
4916
|
-
}
|
|
4917
|
-
if (!seen.has(requestedSkill)) throw invalid(`\u540C\u6B65\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${requestedSkill}`);
|
|
4918
|
-
}
|
|
4919
|
-
function validatePreparedRuntime(runtime) {
|
|
4920
|
-
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0) {
|
|
4921
|
-
throw invalid("WorkBuddy CLI \u80FD\u529B\u96C6\u5408\u4E3A\u7A7A");
|
|
4922
|
-
}
|
|
4923
|
-
const capabilities = /* @__PURE__ */ new Set();
|
|
4924
|
-
for (const capability of runtime.capabilities) {
|
|
4925
|
-
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
4926
|
-
throw invalid(`WorkBuddy CLI \u80FD\u529B\u6807\u8BC6\u975E\u6CD5\u6216\u91CD\u590D\uFF1A${capability}`);
|
|
4927
|
-
}
|
|
4928
|
-
capabilities.add(capability);
|
|
4929
|
-
}
|
|
4930
|
-
if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha2562(`${runtime.capabilities.join("\n")}
|
|
4931
|
-
`)) {
|
|
4932
|
-
throw invalid("WorkBuddy CLI \u80FD\u529B\u6458\u8981\u4E0D\u4E00\u81F4");
|
|
4933
|
-
}
|
|
4934
|
-
if (!validDigest(runtime.launch.executableSha256)) {
|
|
4935
|
-
throw invalid("WorkBuddy CLI \u53EF\u6267\u884C\u6587\u4EF6\u6458\u8981\u975E\u6CD5");
|
|
4936
|
-
}
|
|
4937
|
-
if (runtime.launch.kind === "node-entry" && !validDigest(runtime.launch.entrySha256)) {
|
|
4938
|
-
throw invalid("WorkBuddy CLI \u5165\u53E3\u6458\u8981\u975E\u6CD5");
|
|
4939
|
-
}
|
|
4940
|
-
if (!Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4941
|
-
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5FC5\u987B\u540C\u65F6\u5305\u542B Unix \u4E0E Windows \u6587\u4EF6");
|
|
4942
|
-
}
|
|
4943
|
-
const files = /* @__PURE__ */ new Set();
|
|
4944
|
-
for (const file of runtime.files) {
|
|
4945
|
-
validateRuntimeFile(file);
|
|
4946
|
-
if (files.has(file.path)) throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u91CD\u590D\uFF1A${file.path}`);
|
|
4947
|
-
files.add(file.path);
|
|
4948
|
-
}
|
|
4949
|
-
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
4950
|
-
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5408\u540C\u4E0D\u5B8C\u6574");
|
|
4951
|
-
}
|
|
4952
|
-
}
|
|
4953
|
-
function validateRuntimeFile(file) {
|
|
4954
|
-
validateOwnedFile(file.path);
|
|
4955
|
-
if (file.content.length !== file.bytes || sha2562(file.content) !== file.sha256 || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
|
|
4956
|
-
throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u6458\u8981\u6216\u6743\u9650\u4E0D\u4E00\u81F4\uFF1A${file.path}`);
|
|
4957
|
-
}
|
|
4958
|
-
}
|
|
4959
|
-
function validateWorkBuddyFrontmatter(markdown, skillId) {
|
|
4960
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(markdown);
|
|
4961
|
-
if (!match) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 YAML frontmatter`);
|
|
4962
|
-
const frontmatter = match[1] ?? "";
|
|
4963
|
-
const name = /^name:\s*['"]?([^'"\s]+)['"]?\s*$/m.exec(frontmatter)?.[1];
|
|
4964
|
-
const description = /^description:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim();
|
|
4965
|
-
if (name !== skillId) throw invalid(`\u6280\u80FD ${skillId} \u7684 frontmatter name \u4E0D\u4E00\u81F4`);
|
|
4966
|
-
if (!description) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 description\uFF0CWorkBuddy \u65E0\u6CD5\u53D1\u73B0`);
|
|
4967
|
-
}
|
|
4968
|
-
async function readLock(lockPath) {
|
|
4969
|
-
const content = await readOptional(lockPath);
|
|
4970
|
-
if (content === void 0) return void 0;
|
|
4971
|
-
try {
|
|
4972
|
-
const lock = JSON.parse(content);
|
|
4973
|
-
if (lock.schemaVersion !== 1 || !lock.source || !lock.selection?.requestedSkill || !Array.isArray(lock.resolvedSkills) || !Array.isArray(lock.ownedFiles)) {
|
|
4974
|
-
throw new Error("schema mismatch");
|
|
4975
|
-
}
|
|
4976
|
-
assertSafeSkillId(lock.selection.requestedSkill);
|
|
4977
|
-
for (const skill of lock.resolvedSkills) {
|
|
4978
|
-
assertSafeSkillId(skill.id);
|
|
4979
|
-
if (!Number.isInteger(skill.bytes) || skill.bytes < 0 || !/^[a-f0-9]{64}$/.test(skill.sha256)) {
|
|
4980
|
-
throw new Error("invalid skill digest");
|
|
4981
|
-
}
|
|
4982
|
-
if (skill.files !== void 0) {
|
|
4983
|
-
if (!Array.isArray(skill.files) || skill.files.length === 0)
|
|
4984
|
-
throw new Error("invalid files");
|
|
4985
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4986
|
-
for (const file of skill.files) {
|
|
4987
|
-
assertSafeSkillFilePath(file.path);
|
|
4988
|
-
if (seen.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4989
|
-
throw new Error("invalid file digest");
|
|
4990
|
-
}
|
|
4991
|
-
seen.add(file.path);
|
|
4992
|
-
}
|
|
4993
|
-
if (!seen.has("SKILL.md")) throw new Error("missing SKILL.md");
|
|
4994
|
-
}
|
|
4995
|
-
}
|
|
4996
|
-
if (lock.runtime !== void 0) validateLockedRuntime(lock.runtime);
|
|
4997
|
-
for (const file of lock.ownedFiles) validateOwnedFile(file);
|
|
4998
|
-
return lock;
|
|
4999
|
-
} catch (cause) {
|
|
5000
|
-
throw new CliError16({
|
|
5001
|
-
code: CliErrorCode15.CONFIG_PARSE_ERROR,
|
|
5002
|
-
message: `Chiron \u9501\u6587\u4EF6\u635F\u574F\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6587\u4EF6\u6240\u6709\u6743\uFF1A${lockPath}`,
|
|
5003
|
-
cause
|
|
5004
|
-
});
|
|
5005
|
-
}
|
|
5006
|
-
}
|
|
5007
|
-
function validateLockedRuntime(runtime) {
|
|
5008
|
-
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha2562(`${runtime.capabilities.join("\n")}
|
|
5009
|
-
`) || !runtime.launch || !validDigest(runtime.launch.executableSha256) || !Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
5010
|
-
throw new Error("invalid runtime");
|
|
5011
|
-
}
|
|
5012
|
-
if (runtime.launch.kind === "node-entry") {
|
|
5013
|
-
if (!validDigest(runtime.launch.entrySha256)) throw new Error("invalid runtime entry");
|
|
5014
|
-
} else if (runtime.launch.kind !== "executable") {
|
|
5015
|
-
throw new Error("invalid runtime launch kind");
|
|
5016
|
-
}
|
|
5017
|
-
const capabilities = /* @__PURE__ */ new Set();
|
|
5018
|
-
for (const capability of runtime.capabilities) {
|
|
5019
|
-
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
5020
|
-
throw new Error("invalid runtime capability");
|
|
5021
|
-
}
|
|
5022
|
-
capabilities.add(capability);
|
|
5023
|
-
}
|
|
5024
|
-
const files = /* @__PURE__ */ new Set();
|
|
5025
|
-
for (const file of runtime.files) {
|
|
5026
|
-
validateOwnedFile(file.path);
|
|
5027
|
-
if (files.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !validDigest(file.sha256) || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
|
|
5028
|
-
throw new Error("invalid runtime file");
|
|
5029
|
-
}
|
|
5030
|
-
files.add(file.path);
|
|
5031
|
-
}
|
|
5032
|
-
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
5033
|
-
throw new Error("missing runtime file");
|
|
5034
|
-
}
|
|
5035
|
-
}
|
|
5036
|
-
function sameLockContent(left, right) {
|
|
5037
|
-
const withoutTime = (lock) => ({ ...lock, syncedAt: void 0 });
|
|
5038
|
-
return stableJson2(withoutTime(left)) === stableJson2(withoutTime(right));
|
|
5039
|
-
}
|
|
5040
|
-
async function desiredFilesMatch(project, desired) {
|
|
5041
|
-
for (const [file, preparedFile2] of desired) {
|
|
5042
|
-
const absolute = path4.join(project, file);
|
|
5043
|
-
const current = await readOptionalBytes(absolute);
|
|
5044
|
-
if (current === void 0 || sha2562(current) !== preparedFile2.sha256 || !await fileModeMatches(absolute, preparedFile2.mode)) {
|
|
5045
|
-
return false;
|
|
5046
|
-
}
|
|
5047
|
-
}
|
|
5048
|
-
return true;
|
|
4595
|
+
// src/commands/skills.ts
|
|
4596
|
+
var SKILLS_STATE_FILE = ".hcm-skills.json";
|
|
4597
|
+
async function sha256File(file) {
|
|
4598
|
+
return createHash2("sha256").update(await fsp2.readFile(file)).digest("hex");
|
|
5049
4599
|
}
|
|
5050
|
-
function
|
|
4600
|
+
async function currentSkillsRuntimeLaunch() {
|
|
4601
|
+
const entry = process.argv[1];
|
|
4602
|
+
if (!entry) throw invalid("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
5051
4603
|
return {
|
|
5052
|
-
changed,
|
|
5053
|
-
requestedSkill: input.requestedSkill,
|
|
5054
|
-
resolvedSkills: input.documents.map((document) => document.id),
|
|
5055
|
-
project,
|
|
5056
|
-
skillsDir,
|
|
5057
|
-
lockPath,
|
|
5058
|
-
runnerPath: path4.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
5059
|
-
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
5060
|
-
installed,
|
|
5061
|
-
removed
|
|
5062
|
-
};
|
|
5063
|
-
}
|
|
5064
|
-
function ownedFile(id, relativePath = "SKILL.md") {
|
|
5065
|
-
assertSafeSkillId(id);
|
|
5066
|
-
assertSafeSkillFilePath(relativePath);
|
|
5067
|
-
return `.codebuddy/skills/${id}/${relativePath}`;
|
|
5068
|
-
}
|
|
5069
|
-
function idFromOwnedFile(file) {
|
|
5070
|
-
validateOwnedFile(file);
|
|
5071
|
-
if (!file.startsWith(".codebuddy/skills/")) return void 0;
|
|
5072
|
-
return file.split("/")[2] ?? "";
|
|
5073
|
-
}
|
|
5074
|
-
function validateOwnedFile(file) {
|
|
5075
|
-
if (file === ".hcm-delivery/bin/hcm" || file === ".hcm-delivery/bin/hcm.cmd") return;
|
|
5076
|
-
const match = /^\.codebuddy\/skills\/([a-z0-9][a-z0-9-]{0,63})\/(.+)$/.exec(file);
|
|
5077
|
-
if (!match) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
5078
|
-
const id = match[1];
|
|
5079
|
-
const relativePath = match[2];
|
|
5080
|
-
if (!id || !relativePath) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
5081
|
-
assertSafeSkillId(id);
|
|
5082
|
-
try {
|
|
5083
|
-
assertSafeSkillFilePath(relativePath);
|
|
5084
|
-
} catch {
|
|
5085
|
-
throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
5086
|
-
}
|
|
5087
|
-
}
|
|
5088
|
-
function normalizeEndpoint(endpoint) {
|
|
5089
|
-
let url;
|
|
5090
|
-
try {
|
|
5091
|
-
url = new URL(endpoint);
|
|
5092
|
-
} catch (cause) {
|
|
5093
|
-
throw new CliError16({
|
|
5094
|
-
code: CliErrorCode15.INVALID_ARGUMENT,
|
|
5095
|
-
message: `\u975E\u6CD5 endpoint\uFF1A${endpoint}`,
|
|
5096
|
-
cause
|
|
5097
|
-
});
|
|
5098
|
-
}
|
|
5099
|
-
url.username = "";
|
|
5100
|
-
url.password = "";
|
|
5101
|
-
url.search = "";
|
|
5102
|
-
url.hash = "";
|
|
5103
|
-
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
5104
|
-
return url.toString().replace(/\/$/, "");
|
|
5105
|
-
}
|
|
5106
|
-
function sha2562(value) {
|
|
5107
|
-
return createHash2("sha256").update(value).digest("hex");
|
|
5108
|
-
}
|
|
5109
|
-
function stableJson2(value) {
|
|
5110
|
-
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
5111
|
-
if (value && typeof value === "object") {
|
|
5112
|
-
const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
5113
|
-
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
|
|
5114
|
-
}
|
|
5115
|
-
return JSON.stringify(value);
|
|
5116
|
-
}
|
|
5117
|
-
async function readOptional(file) {
|
|
5118
|
-
try {
|
|
5119
|
-
return await fs5.readFile(file, "utf8");
|
|
5120
|
-
} catch (cause) {
|
|
5121
|
-
if (cause.code === "ENOENT") return void 0;
|
|
5122
|
-
throw cause;
|
|
5123
|
-
}
|
|
5124
|
-
}
|
|
5125
|
-
async function readOptionalBytes(file) {
|
|
5126
|
-
try {
|
|
5127
|
-
return await fs5.readFile(file);
|
|
5128
|
-
} catch (cause) {
|
|
5129
|
-
if (cause.code === "ENOENT") return void 0;
|
|
5130
|
-
throw cause;
|
|
5131
|
-
}
|
|
5132
|
-
}
|
|
5133
|
-
async function exists(file) {
|
|
5134
|
-
try {
|
|
5135
|
-
await fs5.access(file);
|
|
5136
|
-
return true;
|
|
5137
|
-
} catch (cause) {
|
|
5138
|
-
if (cause.code === "ENOENT") return false;
|
|
5139
|
-
throw cause;
|
|
5140
|
-
}
|
|
5141
|
-
}
|
|
5142
|
-
function lockDigests(lock) {
|
|
5143
|
-
const digests = /* @__PURE__ */ new Map();
|
|
5144
|
-
for (const skill of lock?.resolvedSkills ?? []) {
|
|
5145
|
-
if (skill.files === void 0) {
|
|
5146
|
-
digests.set(ownedFile(skill.id), { sha256: skill.sha256 });
|
|
5147
|
-
continue;
|
|
5148
|
-
}
|
|
5149
|
-
for (const file of skill.files) {
|
|
5150
|
-
digests.set(ownedFile(skill.id, file.path), { sha256: file.sha256 });
|
|
5151
|
-
}
|
|
5152
|
-
}
|
|
5153
|
-
for (const file of lock?.runtime?.files ?? []) {
|
|
5154
|
-
digests.set(file.path, { sha256: file.sha256, mode: file.mode });
|
|
5155
|
-
}
|
|
5156
|
-
return digests;
|
|
5157
|
-
}
|
|
5158
|
-
async function fileModeMatches(file, expected) {
|
|
5159
|
-
if (expected === void 0 || process.platform === "win32") return true;
|
|
5160
|
-
const stat = await fs5.stat(file);
|
|
5161
|
-
return (stat.mode & 511) === expected;
|
|
5162
|
-
}
|
|
5163
|
-
function validDigest(value) {
|
|
5164
|
-
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
5165
|
-
}
|
|
5166
|
-
function ignoreMissing(cause) {
|
|
5167
|
-
if (cause.code !== "ENOENT") throw cause;
|
|
5168
|
-
}
|
|
5169
|
-
function invalid(message) {
|
|
5170
|
-
return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
|
|
5171
|
-
}
|
|
5172
|
-
|
|
5173
|
-
// src/commands/skills-runtime.ts
|
|
5174
|
-
import { execFile } from "child_process";
|
|
5175
|
-
import { createHash as createHash3 } from "crypto";
|
|
5176
|
-
import { createReadStream, promises as fs6 } from "fs";
|
|
5177
|
-
import path5 from "path";
|
|
5178
|
-
import { promisify } from "util";
|
|
5179
|
-
import { CliError as CliError17, CliErrorCode as CliErrorCode16 } from "@hcmai/sdk";
|
|
5180
|
-
var execFileAsync = promisify(execFile);
|
|
5181
|
-
var REQUIRED_CAPABILITIES = [
|
|
5182
|
-
"hcm.help.skills",
|
|
5183
|
-
"hcm.skills.help.sync",
|
|
5184
|
-
"hcm.skills.sync.option.project",
|
|
5185
|
-
"hcm.skills.sync.option.target"
|
|
5186
|
-
];
|
|
5187
|
-
async function prepareWorkBuddyRuntime(input = {}) {
|
|
5188
|
-
const candidate = input.candidate?.trim() || input.currentEntry || process.argv[1];
|
|
5189
|
-
if (!candidate) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D HCM CLI \u5165\u53E3\uFF1B\u8BF7\u4F20 --cli <path>");
|
|
5190
|
-
const launch = await resolveLaunch(candidate, input.nodeExecutable ?? process.execPath);
|
|
5191
|
-
const launchTarget = launch.kind === "node-entry" ? launch.entry : launch.executable;
|
|
5192
|
-
for (const forbidden2 of input.forbiddenCandidatePaths ?? []) {
|
|
5193
|
-
if (await comparablePath(forbidden2) === launchTarget) {
|
|
5194
|
-
throw invalid2("\u5019\u9009 HCM CLI \u4E0D\u80FD\u6307\u5411\u5F85\u751F\u6210\u7684\u9879\u76EE\u7A33\u5B9A\u5165\u53E3");
|
|
5195
|
-
}
|
|
5196
|
-
}
|
|
5197
|
-
if (!(input.currentEntryHasRequiredCapabilities && !input.candidate?.trim())) {
|
|
5198
|
-
const beforeProbe = await launchIdentity(launch);
|
|
5199
|
-
const syncHelp = await runProbe(launch, ["skills", "sync", "--help"], input.timeoutMs).catch(
|
|
5200
|
-
() => ""
|
|
5201
|
-
);
|
|
5202
|
-
const missing = [];
|
|
5203
|
-
if (!/\bhcm\s+skills\s+sync\b/.test(syncHelp)) {
|
|
5204
|
-
missing.push(REQUIRED_CAPABILITIES[0], REQUIRED_CAPABILITIES[1]);
|
|
5205
|
-
}
|
|
5206
|
-
if (!/--project(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[2]);
|
|
5207
|
-
if (!/--target(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[3]);
|
|
5208
|
-
if (missing.length > 0) {
|
|
5209
|
-
throw invalid2(`\u5019\u9009 HCM CLI \u7F3A\u5C11 WorkBuddy \u6240\u9700\u80FD\u529B\uFF1A${missing.join(", ")}`);
|
|
5210
|
-
}
|
|
5211
|
-
if (await launchIdentity(launch) !== beforeProbe) {
|
|
5212
|
-
throw invalid2("\u5019\u9009 HCM CLI \u5728\u80FD\u529B\u63A2\u9488\u671F\u95F4\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u8BD5\u540C\u6B65");
|
|
5213
|
-
}
|
|
5214
|
-
}
|
|
5215
|
-
const capabilities = [...REQUIRED_CAPABILITIES];
|
|
5216
|
-
const capabilitiesSha256 = sha2563(`${capabilities.join("\n")}
|
|
5217
|
-
`);
|
|
5218
|
-
const executableSha256 = await sha256File(launch.executable);
|
|
5219
|
-
const launchRecord = launch.kind === "node-entry" ? {
|
|
5220
4604
|
kind: "node-entry",
|
|
5221
|
-
executableSha256,
|
|
5222
|
-
entrySha256: await sha256File(
|
|
5223
|
-
} : { kind: "executable", executableSha256 };
|
|
5224
|
-
const unixContent = Buffer.from(unixLauncher(launch), "utf8");
|
|
5225
|
-
const windowsContent = Buffer.from(windowsLauncher(launch), "utf8");
|
|
5226
|
-
return {
|
|
5227
|
-
capabilities,
|
|
5228
|
-
capabilitiesSha256,
|
|
5229
|
-
launch: launchRecord,
|
|
5230
|
-
files: [
|
|
5231
|
-
preparedFile(".hcm-delivery/bin/hcm", unixContent, 493),
|
|
5232
|
-
preparedFile(".hcm-delivery/bin/hcm.cmd", windowsContent, 420)
|
|
5233
|
-
]
|
|
5234
|
-
};
|
|
5235
|
-
}
|
|
5236
|
-
async function resolveLaunch(candidate, nodeExecutable) {
|
|
5237
|
-
const requested = path5.resolve(candidate);
|
|
5238
|
-
const resolvedCandidate = await realFile(requested, "\u5019\u9009 HCM CLI");
|
|
5239
|
-
const header = (await readPrefix(resolvedCandidate, 256)).toString("utf8");
|
|
5240
|
-
const nodeEntry = /^(?:#![^\r\n]*\bnode\b)|\.(?:c|m)?js$/i.test(
|
|
5241
|
-
header.startsWith("#!") ? header.split(/\r?\n/, 1)[0] ?? "" : resolvedCandidate
|
|
5242
|
-
);
|
|
5243
|
-
if (nodeEntry) {
|
|
5244
|
-
return {
|
|
5245
|
-
kind: "node-entry",
|
|
5246
|
-
executable: await realFile(path5.resolve(nodeExecutable), "Node \u53EF\u6267\u884C\u6587\u4EF6"),
|
|
5247
|
-
entry: resolvedCandidate
|
|
5248
|
-
};
|
|
5249
|
-
}
|
|
5250
|
-
return { kind: "executable", executable: resolvedCandidate };
|
|
5251
|
-
}
|
|
5252
|
-
async function realFile(file, label) {
|
|
5253
|
-
try {
|
|
5254
|
-
const resolved = await fs6.realpath(file);
|
|
5255
|
-
const stat = await fs6.stat(resolved);
|
|
5256
|
-
if (!stat.isFile()) throw new Error("not a regular file");
|
|
5257
|
-
return resolved;
|
|
5258
|
-
} catch (cause) {
|
|
5259
|
-
throw new CliError17({
|
|
5260
|
-
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
5261
|
-
message: `${label}\u4E0D\u53EF\u7528\uFF1A${file}`,
|
|
5262
|
-
cause
|
|
5263
|
-
});
|
|
5264
|
-
}
|
|
5265
|
-
}
|
|
5266
|
-
async function comparablePath(file) {
|
|
5267
|
-
const absolute = path5.resolve(file);
|
|
5268
|
-
return fs6.realpath(absolute).catch(() => absolute);
|
|
5269
|
-
}
|
|
5270
|
-
async function launchIdentity(launch) {
|
|
5271
|
-
const files = launch.kind === "node-entry" ? [launch.executable, launch.entry] : [launch.executable];
|
|
5272
|
-
const identities = await Promise.all(
|
|
5273
|
-
files.map(async (file) => {
|
|
5274
|
-
const stat = await fs6.stat(file, { bigint: true });
|
|
5275
|
-
return [file, stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].map(String).join("\0");
|
|
5276
|
-
})
|
|
5277
|
-
);
|
|
5278
|
-
return identities.join("\n");
|
|
5279
|
-
}
|
|
5280
|
-
async function readPrefix(file, bytes) {
|
|
5281
|
-
const handle = await fs6.open(file, "r");
|
|
5282
|
-
try {
|
|
5283
|
-
const buffer = Buffer.alloc(bytes);
|
|
5284
|
-
const result2 = await handle.read(buffer, 0, bytes, 0);
|
|
5285
|
-
return buffer.subarray(0, result2.bytesRead);
|
|
5286
|
-
} finally {
|
|
5287
|
-
await handle.close();
|
|
5288
|
-
}
|
|
5289
|
-
}
|
|
5290
|
-
async function runProbe(launch, args, timeoutMs = 5e3) {
|
|
5291
|
-
const command = launch.executable;
|
|
5292
|
-
const commandArgs = launch.kind === "node-entry" ? [launch.entry, ...args] : args;
|
|
5293
|
-
const env2 = {
|
|
5294
|
-
...process.env,
|
|
5295
|
-
CI: "1",
|
|
5296
|
-
NO_COLOR: "1",
|
|
5297
|
-
HCM_RUNTIME_PROBE: "1"
|
|
4605
|
+
executableSha256: await sha256File(process.execPath),
|
|
4606
|
+
entrySha256: await sha256File(entry)
|
|
5298
4607
|
};
|
|
5299
|
-
const nodeOptions = normalizeWorkBuddyNodeOptions(env2.NODE_OPTIONS);
|
|
5300
|
-
if (nodeOptions === void 0) delete env2.NODE_OPTIONS;
|
|
5301
|
-
else env2.NODE_OPTIONS = nodeOptions;
|
|
5302
|
-
try {
|
|
5303
|
-
const { stdout, stderr } = await execFileAsync(command, commandArgs, {
|
|
5304
|
-
encoding: "utf8",
|
|
5305
|
-
timeout: timeoutMs,
|
|
5306
|
-
maxBuffer: 256 * 1024,
|
|
5307
|
-
windowsHide: true,
|
|
5308
|
-
env: env2
|
|
5309
|
-
});
|
|
5310
|
-
return `${stdout}
|
|
5311
|
-
${stderr}`;
|
|
5312
|
-
} catch (cause) {
|
|
5313
|
-
throw new CliError17({
|
|
5314
|
-
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
5315
|
-
message: `\u5019\u9009 HCM CLI \u80FD\u529B\u63A2\u9488\u5931\u8D25\uFF1Ahcm ${args.join(" ")}`,
|
|
5316
|
-
cause
|
|
5317
|
-
});
|
|
5318
|
-
}
|
|
5319
4608
|
}
|
|
5320
|
-
function unixLauncher(launch) {
|
|
5321
|
-
const command = shellQuote(launch.executable);
|
|
5322
|
-
const entry = launch.kind === "node-entry" ? ` ${shellQuote(launch.entry)}` : "";
|
|
5323
|
-
return `#!/bin/sh
|
|
5324
|
-
set -eu
|
|
5325
|
-
|
|
5326
|
-
# WorkBuddy on macOS may inject --use-system-ca. Node can then spend minutes
|
|
5327
|
-
# evaluating the Keychain when a short-lived CLI process exits. Preserve every
|
|
5328
|
-
# other host option (including safety --require hooks) and change only this CA mode.
|
|
5329
|
-
wb_node_options="\${NODE_OPTIONS-}"
|
|
5330
|
-
if [ "$(uname -s 2>/dev/null || printf '%s' unknown)" = 'Darwin' ] && printf '%s
|
|
5331
|
-
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; then
|
|
5332
|
-
while printf '%s
|
|
5333
|
-
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; do
|
|
5334
|
-
wb_next_options="$(printf '%s
|
|
5335
|
-
' "$wb_node_options" | sed -E 's/(^|[[:space:]])--use-system-ca([[:space:]]|$)/\\1\\2/g')"
|
|
5336
|
-
[ "$wb_next_options" != "$wb_node_options" ] || break
|
|
5337
|
-
wb_node_options="$wb_next_options"
|
|
5338
|
-
done
|
|
5339
|
-
if ! printf '%s
|
|
5340
|
-
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-bundled-ca([[:space:]]|$)'; then
|
|
5341
|
-
wb_node_options="\${wb_node_options:+$wb_node_options }--use-bundled-ca"
|
|
5342
|
-
fi
|
|
5343
|
-
NODE_OPTIONS="$wb_node_options"
|
|
5344
|
-
export NODE_OPTIONS
|
|
5345
|
-
fi
|
|
5346
|
-
|
|
5347
|
-
exec ${command}${entry} "$@"
|
|
5348
|
-
`;
|
|
5349
|
-
}
|
|
5350
|
-
function normalizeWorkBuddyNodeOptions(value, platform = process.platform) {
|
|
5351
|
-
if (platform !== "darwin" || value === void 0) return value;
|
|
5352
|
-
const systemCa = /(^|[\t\n\r ])--use-system-ca(?=$|[\t\n\r ])/;
|
|
5353
|
-
if (!systemCa.test(value)) return value;
|
|
5354
|
-
let normalized = value;
|
|
5355
|
-
while (systemCa.test(normalized)) normalized = normalized.replace(systemCa, "$1");
|
|
5356
|
-
normalized = normalized.trim();
|
|
5357
|
-
const bundledCa = /(^|[\t\n\r ])--use-bundled-ca(?=$|[\t\n\r ])/;
|
|
5358
|
-
if (!bundledCa.test(normalized)) {
|
|
5359
|
-
normalized = `${normalized}${normalized ? " " : ""}--use-bundled-ca`;
|
|
5360
|
-
}
|
|
5361
|
-
return normalized;
|
|
5362
|
-
}
|
|
5363
|
-
function windowsLauncher(launch) {
|
|
5364
|
-
const command = cmdQuote(launch.executable);
|
|
5365
|
-
const entry = launch.kind === "node-entry" ? ` ${cmdQuote(launch.entry)}` : "";
|
|
5366
|
-
return `@echo off\r
|
|
5367
|
-
${command}${entry} %*\r
|
|
5368
|
-
`;
|
|
5369
|
-
}
|
|
5370
|
-
function shellQuote(value) {
|
|
5371
|
-
if (/[\r\n\0]/.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5372
|
-
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
5373
|
-
}
|
|
5374
|
-
function cmdQuote(value) {
|
|
5375
|
-
if (/[\r\n\0"]/g.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5376
|
-
return `"${value.replace(/%/g, "%%")}"`;
|
|
5377
|
-
}
|
|
5378
|
-
function preparedFile(filePath, content, mode) {
|
|
5379
|
-
return { path: filePath, content, bytes: content.length, sha256: sha2563(content), mode };
|
|
5380
|
-
}
|
|
5381
|
-
async function sha256File(file) {
|
|
5382
|
-
const hash = createHash3("sha256");
|
|
5383
|
-
await new Promise((resolve4, reject) => {
|
|
5384
|
-
const stream = createReadStream(file);
|
|
5385
|
-
stream.on("data", (chunk) => hash.update(chunk));
|
|
5386
|
-
stream.on("error", reject);
|
|
5387
|
-
stream.on("end", resolve4);
|
|
5388
|
-
});
|
|
5389
|
-
return hash.digest("hex");
|
|
5390
|
-
}
|
|
5391
|
-
function sha2563(value) {
|
|
5392
|
-
return createHash3("sha256").update(value).digest("hex");
|
|
5393
|
-
}
|
|
5394
|
-
function invalid2(message) {
|
|
5395
|
-
return new CliError17({ code: CliErrorCode16.INVALID_ARGUMENT, message });
|
|
5396
|
-
}
|
|
5397
|
-
|
|
5398
|
-
// src/commands/skills.ts
|
|
5399
|
-
var SKILLS_STATE_FILE = ".hcm-skills.json";
|
|
5400
4609
|
function migrateSkillsState(state) {
|
|
5401
4610
|
const skills2 = {};
|
|
5402
4611
|
for (const [id, record] of Object.entries(state.skills ?? {})) {
|
|
@@ -5409,8 +4618,8 @@ function migrateSkillsState(state) {
|
|
|
5409
4618
|
}
|
|
5410
4619
|
return { ...state, skills: skills2 };
|
|
5411
4620
|
}
|
|
5412
|
-
function
|
|
5413
|
-
return
|
|
4621
|
+
function sha2562(text) {
|
|
4622
|
+
return createHash2("sha256").update(text, "utf8").digest("hex");
|
|
5414
4623
|
}
|
|
5415
4624
|
async function resolveEndpoint(args) {
|
|
5416
4625
|
if (args.endpoint) return args.endpoint.replace(/\/+$/, "");
|
|
@@ -5418,35 +4627,35 @@ async function resolveEndpoint(args) {
|
|
|
5418
4627
|
const envName = resolveActiveEnv3({ envFlag: args.env }, process.env, g);
|
|
5419
4628
|
const env2 = await loadEnv5(envName).catch(() => null);
|
|
5420
4629
|
if (!env2?.endpoint) {
|
|
5421
|
-
throw new
|
|
5422
|
-
code:
|
|
4630
|
+
throw new CliError16({
|
|
4631
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
5423
4632
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`\u3002\uFF08\u6280\u80FD\u5E93\u514D\u8BA4\u8BC1\uFF0C\u4E0D\u9700\u8981\u5148 login\uFF09"
|
|
5424
4633
|
});
|
|
5425
4634
|
}
|
|
5426
4635
|
return env2.endpoint.replace(/\/+$/, "");
|
|
5427
4636
|
}
|
|
5428
|
-
function
|
|
4637
|
+
function normalizeEndpoint(endpoint) {
|
|
5429
4638
|
return endpoint.replace(/\/+$/, "");
|
|
5430
4639
|
}
|
|
5431
4640
|
function decideTrackedEndpoint(options) {
|
|
5432
4641
|
const { explicit } = options;
|
|
5433
|
-
const current = options.current ?
|
|
5434
|
-
const recorded = options.recorded ?
|
|
4642
|
+
const current = options.current ? normalizeEndpoint(options.current) : void 0;
|
|
4643
|
+
const recorded = options.recorded ? normalizeEndpoint(options.recorded) : void 0;
|
|
5435
4644
|
if (explicit) {
|
|
5436
|
-
if (!current) throw
|
|
4645
|
+
if (!current) throw invalid("\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u7528 --env <name> \u6307\u4E00\u4E2A\u914D\u597D\u7684\u73AF\u5883");
|
|
5437
4646
|
return current;
|
|
5438
4647
|
}
|
|
5439
4648
|
if (!recorded) {
|
|
5440
4649
|
if (!current) {
|
|
5441
|
-
throw new
|
|
5442
|
-
code:
|
|
4650
|
+
throw new CliError16({
|
|
4651
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
5443
4652
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`\u3002\uFF08\u6280\u80FD\u5E93\u514D\u8BA4\u8BC1\uFF0C\u4E0D\u9700\u8981\u5148 login\uFF09"
|
|
5444
4653
|
});
|
|
5445
4654
|
}
|
|
5446
4655
|
return current;
|
|
5447
4656
|
}
|
|
5448
4657
|
if (!current || current === recorded) return recorded;
|
|
5449
|
-
throw
|
|
4658
|
+
throw invalid(
|
|
5450
4659
|
`\u8FD9\u4E2A\u76EE\u5F55\u7684\u6280\u80FD\u88C5\u81EA ${recorded}\uFF0C\u5F53\u524D env \u6307\u5411 ${current} \u2014\u2014 \u4E0D\u662F\u540C\u4E00\u4E2A\u73AF\u5883\u3002
|
|
5451
4660
|
\u6280\u80FD\u968F\u4EA7\u54C1\u7248\u672C\u8D70\uFF0C\u8DE8\u73AF\u5883\u5347\u7EA7\u4F1A\u62FF\u53E6\u4E00\u4E2A\u73AF\u5883\u7684\u7248\u672C\u76D6\u6389\u73B0\u5728\u8FD9\u4EFD\uFF0C\u4E14\u4E0D\u4F1A\u6709\u4EFB\u4F55\u63D0\u793A\u3002
|
|
5452
4661
|
\u660E\u8BF4\u8981\u54EA\u4E2A\uFF1A
|
|
@@ -5454,7 +4663,7 @@ function decideTrackedEndpoint(options) {
|
|
|
5454
4663
|
\xB7 \u6539\u8DDF\u5F53\u524D\u8FD9\u4E2A \u2192 \u52A0 --endpoint ${current}\uFF08\u6765\u6E90\u4F1A\u6539\u6210\u5B83\uFF0C\u4E4B\u540E\u4E0D\u7528\u518D\u5E26\uFF09`
|
|
5455
4664
|
);
|
|
5456
4665
|
}
|
|
5457
|
-
async function
|
|
4666
|
+
async function exists(target) {
|
|
5458
4667
|
try {
|
|
5459
4668
|
await fsp2.stat(target);
|
|
5460
4669
|
return true;
|
|
@@ -5463,25 +4672,25 @@ async function exists2(target) {
|
|
|
5463
4672
|
}
|
|
5464
4673
|
}
|
|
5465
4674
|
async function detectTarget(cwd) {
|
|
5466
|
-
if (await
|
|
5467
|
-
if (await
|
|
5468
|
-
if (await
|
|
4675
|
+
if (await exists(path4.join(cwd, ".claude"))) return "claude";
|
|
4676
|
+
if (await exists(path4.join(cwd, "AGENTS.md"))) return "codex";
|
|
4677
|
+
if (await exists(path4.join(cwd, ".codex"))) return "codex";
|
|
5469
4678
|
return void 0;
|
|
5470
4679
|
}
|
|
5471
4680
|
async function resolveInstallLayout(args, cwd = process.cwd()) {
|
|
5472
4681
|
const requested = args.target && args.target !== "auto" ? args.target : void 0;
|
|
5473
4682
|
const target = requested ?? await detectTarget(cwd) ?? "claude";
|
|
5474
4683
|
if (target === "codex") {
|
|
5475
|
-
const root = args.global ?
|
|
4684
|
+
const root = args.global ? path4.join(os2.homedir(), ".codex") : cwd;
|
|
5476
4685
|
return {
|
|
5477
4686
|
target,
|
|
5478
|
-
baseDir: args.dir ?
|
|
5479
|
-
agentsFile:
|
|
4687
|
+
baseDir: args.dir ? path4.resolve(args.dir) : path4.join(root, "hcm-skills"),
|
|
4688
|
+
agentsFile: path4.join(root, "AGENTS.md")
|
|
5480
4689
|
};
|
|
5481
4690
|
}
|
|
5482
4691
|
return {
|
|
5483
4692
|
target,
|
|
5484
|
-
baseDir: args.dir ?
|
|
4693
|
+
baseDir: args.dir ? path4.resolve(args.dir) : args.global ? path4.join(os2.homedir(), ".claude", "skills") : path4.join(cwd, ".claude", "skills")
|
|
5485
4694
|
};
|
|
5486
4695
|
}
|
|
5487
4696
|
async function chironClient(args) {
|
|
@@ -5518,8 +4727,8 @@ async function skillsListCommand(keyword, args) {
|
|
|
5518
4727
|
const catalog = buildUnifiedCatalog({ source, chironManifest: manifest2, bundled, cliVersion: CLI_VERSION });
|
|
5519
4728
|
const conflicts = detectIdConflicts(catalog);
|
|
5520
4729
|
if (conflicts.ids.length > 0) {
|
|
5521
|
-
throw new
|
|
5522
|
-
code:
|
|
4730
|
+
throw new CliError16({
|
|
4731
|
+
code: CliErrorCode15.SKILL_ID_CONFLICT,
|
|
5523
4732
|
message: conflictMessage(conflicts.ids, endpoint ?? "(\u672A\u77E5\u73AF\u5883)")
|
|
5524
4733
|
});
|
|
5525
4734
|
}
|
|
@@ -5577,9 +4786,9 @@ async function skillsListCommand(keyword, args) {
|
|
|
5577
4786
|
`);
|
|
5578
4787
|
if (args.byStage) {
|
|
5579
4788
|
const all = cats.flatMap((c) => c.skills);
|
|
5580
|
-
const stageList = manifest2?.stages ?? [...new Set(all.flatMap((skill) => skill.stages ?? []))].sort().map((
|
|
5581
|
-
key,
|
|
5582
|
-
name:
|
|
4789
|
+
const stageList = manifest2?.stages ?? [...new Set(all.flatMap((skill) => skill.stages ?? []))].sort().map((key2) => ({
|
|
4790
|
+
key: key2,
|
|
4791
|
+
name: key2,
|
|
5583
4792
|
description: "",
|
|
5584
4793
|
skillCount: 0
|
|
5585
4794
|
}));
|
|
@@ -5635,7 +4844,7 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5635
4844
|
process.stdout.write(formatObject14({ baseDir: layout.baseDir, target: layout.target, all: false, skills: [] }, { format: fmt }) + "\n");
|
|
5636
4845
|
process.exit(0);
|
|
5637
4846
|
}
|
|
5638
|
-
process.stdout.write(`${
|
|
4847
|
+
process.stdout.write(`${path4.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002
|
|
5639
4848
|
`);
|
|
5640
4849
|
process.stdout.write(" \u88C5\u5168\u90E8\uFF1Ahcm skills install --all\n");
|
|
5641
4850
|
process.exit(0);
|
|
@@ -5657,7 +4866,7 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5657
4866
|
direct: record.direct,
|
|
5658
4867
|
installedAt: record.installedAt,
|
|
5659
4868
|
// 三种状态互斥:文件没了 / 本地改过 / 和装的时候一致
|
|
5660
|
-
status: markdown === void 0 ? "missing" :
|
|
4869
|
+
status: markdown === void 0 ? "missing" : sha2562(markdown) !== record.sha256 ? "locally-modified" : "installed"
|
|
5661
4870
|
});
|
|
5662
4871
|
}
|
|
5663
4872
|
const byKeyword = keyword ? rows.filter((row) => row.id.includes(keyword) || row.title.includes(keyword)) : rows;
|
|
@@ -5719,7 +4928,7 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5719
4928
|
}
|
|
5720
4929
|
async function skillsShowCommand(id, args) {
|
|
5721
4930
|
try {
|
|
5722
|
-
|
|
4931
|
+
assertSafeSkillId(id);
|
|
5723
4932
|
const source = parseSourceFilter(args.source);
|
|
5724
4933
|
if (source !== "chiron" && listBundledSkills().some((s) => s.id === id)) {
|
|
5725
4934
|
const document = await readBundledSkillDocument(id);
|
|
@@ -5727,7 +4936,7 @@ async function skillsShowCommand(id, args) {
|
|
|
5727
4936
|
process.exit(0);
|
|
5728
4937
|
}
|
|
5729
4938
|
if (source === "cli") {
|
|
5730
|
-
throw
|
|
4939
|
+
throw invalid(`\u8FD9\u4E00\u7248 CLI \u6CA1\u6709\u81EA\u5E26 ${id} \u8FD9\u4E2A\u6280\u80FD\uFF08--source cli \u4E0D\u770B\u777F\u620E\u76EE\u5F55\uFF09`);
|
|
5731
4940
|
}
|
|
5732
4941
|
const { http, base } = await chironClient(args);
|
|
5733
4942
|
const md = await fetchSkillMarkdown(http, id, base);
|
|
@@ -5737,80 +4946,12 @@ async function skillsShowCommand(id, args) {
|
|
|
5737
4946
|
fail3(e, args);
|
|
5738
4947
|
}
|
|
5739
4948
|
}
|
|
5740
|
-
async function skillsSyncCommand(id, args) {
|
|
5741
|
-
try {
|
|
5742
|
-
assertSafeSkillId2(id);
|
|
5743
|
-
if (args.target !== "workbuddy") {
|
|
5744
|
-
throw invalid3(`sync --target \u5F53\u524D\u53EA\u652F\u6301 workbuddy\uFF0C\u6536\u5230 "${args.target ?? ""}"`);
|
|
5745
|
-
}
|
|
5746
|
-
if (!args.project?.trim()) throw invalid3("sync \u5FC5\u987B\u4F20 --project <path>");
|
|
5747
|
-
if (listBundledSkills().some((one) => one.id === id)) {
|
|
5748
|
-
throw new CliError18({
|
|
5749
|
-
code: CliErrorCode17.SYNC_SOURCE_UNSUPPORTED,
|
|
5750
|
-
message: `\`${id}\` \u662F CLI \u81EA\u5E26\u6280\u80FD\uFF1Bskills sync \u7684\u9501\u6587\u4EF6\uFF08chiron.lock.json\uFF09\u6B64\u523B\u53EA\u80FD\u8BB0\u5F55\u777F\u620E\u6765\u6E90\u7684\u51FA\u5904\u3002\u88C5\u5230\u672C\u5730\u7528 \`hcm skills install ` + id + "`\u3002"
|
|
5751
|
-
});
|
|
5752
|
-
}
|
|
5753
|
-
const project = path6.resolve(args.project);
|
|
5754
|
-
const runtime = await prepareWorkBuddyRuntime({
|
|
5755
|
-
candidate: args.cli,
|
|
5756
|
-
currentEntryHasRequiredCapabilities: !args.cli?.trim(),
|
|
5757
|
-
forbiddenCandidatePaths: [
|
|
5758
|
-
path6.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
5759
|
-
path6.join(project, ".hcm-delivery", "bin", "hcm.cmd")
|
|
5760
|
-
]
|
|
5761
|
-
});
|
|
5762
|
-
const connectionArgs = { endpoint: args.endpoint, env: args.env };
|
|
5763
|
-
const { http, endpoint, base } = await chironClient(connectionArgs);
|
|
5764
|
-
const catalog = await fetchSkillCatalog(http, base);
|
|
5765
|
-
const documents = await prepareSkillDocuments(
|
|
5766
|
-
catalog,
|
|
5767
|
-
id,
|
|
5768
|
-
async (skillId) => fetchSkillMarkdown(http, skillId, base),
|
|
5769
|
-
async (skillId, relativePath) => fetchSkillFile(http, skillId, relativePath, base),
|
|
5770
|
-
async (skillId, relativePath) => fetchSkillReference(http, skillId, relativePath, base)
|
|
5771
|
-
);
|
|
5772
|
-
const result2 = await syncWorkBuddyProject({
|
|
5773
|
-
project,
|
|
5774
|
-
endpoint,
|
|
5775
|
-
catalog,
|
|
5776
|
-
requestedSkill: id,
|
|
5777
|
-
documents,
|
|
5778
|
-
runtime
|
|
5779
|
-
});
|
|
5780
|
-
const fmt = args.output ?? "table";
|
|
5781
|
-
if (fmt === "json" || fmt === "yaml") {
|
|
5782
|
-
process.stdout.write(`${formatObject14(result2, { format: fmt })}
|
|
5783
|
-
`);
|
|
5784
|
-
} else if (result2.changed) {
|
|
5785
|
-
process.stdout.write(`\u2705 ${id} \u2192 ${result2.skillsDir}
|
|
5786
|
-
`);
|
|
5787
|
-
process.stdout.write(` \u95ED\u5305 ${result2.resolvedSkills.join(" \u2192 ")}
|
|
5788
|
-
`);
|
|
5789
|
-
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5790
|
-
`);
|
|
5791
|
-
process.stdout.write(` CLI \u80FD\u529B ${result2.capabilitiesSha256.slice(0, 12)}
|
|
5792
|
-
`);
|
|
5793
|
-
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5794
|
-
`);
|
|
5795
|
-
} else {
|
|
5796
|
-
process.stdout.write(`\u2713 ${id} \u5DF2\u662F\u5F53\u524D\u76EE\u6807\u5B9E\u4F8B\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0
|
|
5797
|
-
`);
|
|
5798
|
-
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5799
|
-
`);
|
|
5800
|
-
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5801
|
-
`);
|
|
5802
|
-
}
|
|
5803
|
-
process.exit(0);
|
|
5804
|
-
} catch (e) {
|
|
5805
|
-
fail3(e, { endpoint: args.endpoint, env: args.env });
|
|
5806
|
-
}
|
|
5807
|
-
}
|
|
5808
4949
|
async function skillsInstallCommand(id, args) {
|
|
5809
4950
|
try {
|
|
5810
4951
|
const all = Boolean(args.all);
|
|
5811
|
-
if (all && id) throw
|
|
5812
|
-
if (!all && !id) throw
|
|
5813
|
-
if (id)
|
|
4952
|
+
if (all && id) throw invalid("`--all` \u662F\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u4E0D\u8981\u518D\u5E26\u6280\u80FD id\uFF1B\u53EA\u88C5\u4E00\u4E2A\u5C31\u53BB\u6389 --all");
|
|
4953
|
+
if (!all && !id) throw invalid("\u8981\u88C5\u54EA\u4E2A\u6280\u80FD\uFF1F\u7ED9\u4E2A id\uFF08`hcm skills list` \u770B\u76EE\u5F55\uFF09\uFF0C\u6216\u7528 `--all` \u88C5\u5168\u90E8");
|
|
4954
|
+
if (id) assertSafeSkillId(id);
|
|
5814
4955
|
const layout = await resolveInstallLayout(args);
|
|
5815
4956
|
let documents;
|
|
5816
4957
|
let requestedIds;
|
|
@@ -5819,7 +4960,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
5819
4960
|
let manifest2;
|
|
5820
4961
|
let conflictNote;
|
|
5821
4962
|
if (args.from) {
|
|
5822
|
-
const root =
|
|
4963
|
+
const root = path4.resolve(args.from);
|
|
5823
4964
|
requestedIds = all ? await listLocalSkillIds(root) : [id];
|
|
5824
4965
|
const seen = /* @__PURE__ */ new Set();
|
|
5825
4966
|
documents = [];
|
|
@@ -5849,14 +4990,14 @@ async function skillsInstallCommand(id, args) {
|
|
|
5849
4990
|
const catalog = buildUnifiedCatalog({ source, chironManifest: manifest2, bundled, cliVersion: CLI_VERSION });
|
|
5850
4991
|
const conflicts = detectIdConflicts(catalog);
|
|
5851
4992
|
if (conflicts.ids.length > 0) {
|
|
5852
|
-
throw new
|
|
5853
|
-
code:
|
|
4993
|
+
throw new CliError16({
|
|
4994
|
+
code: CliErrorCode15.SKILL_ID_CONFLICT,
|
|
5854
4995
|
message: conflictMessage(conflicts.ids, endpoint ?? "(\u672A\u77E5\u73AF\u5883)")
|
|
5855
4996
|
});
|
|
5856
4997
|
}
|
|
5857
4998
|
conflictNote = conflicts.checked ? void 0 : unconfirmedConflictNote(source);
|
|
5858
4999
|
for (const v of checkCrossSourceRequires(catalog)) {
|
|
5859
|
-
throw
|
|
5000
|
+
throw invalid(`${v.from} \u58F0\u660E\u4F9D\u8D56 ${v.to}\uFF0C\u4F46 ${v.to} \u662F CLI \u81EA\u5E26\u6280\u80FD\u3002${v.reason}`);
|
|
5860
5001
|
}
|
|
5861
5002
|
const wantBundled = (one) => catalog.cli.entries.has(one);
|
|
5862
5003
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -5872,16 +5013,16 @@ async function skillsInstallCommand(id, args) {
|
|
|
5872
5013
|
const { documents: local, external } = await resolveBundledClosure(one);
|
|
5873
5014
|
for (const need of external) {
|
|
5874
5015
|
if (catalog.chiron.status !== "fetched") {
|
|
5875
|
-
throw
|
|
5016
|
+
throw invalid(
|
|
5876
5017
|
`\u81EA\u5E26\u6280\u80FD ${one} \u4F9D\u8D56\u777F\u620E\u6280\u80FD ${need}\uFF0C\u4F46\u8FD9\u4E00\u8D9F\u6CA1\u8FDE\u7F51\u53D6\u777F\u620E\u76EE\u5F55\uFF08--source ${source}\uFF09\u3002\u8054\u7F51\u540E\u91CD\u8DD1\uFF0C\u6216\u5148\u5355\u72EC\u88C5\u4E0A\u5B83\u3002`
|
|
5877
5018
|
);
|
|
5878
5019
|
}
|
|
5879
5020
|
if (!catalog.chiron.entries.has(need)) {
|
|
5880
|
-
throw
|
|
5021
|
+
throw invalid(`\u81EA\u5E26\u6280\u80FD ${one} \u4F9D\u8D56 ${need}\uFF0C\u4F46 ${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u91CC\u6CA1\u6709\u5B83`);
|
|
5881
5022
|
}
|
|
5882
5023
|
pushAll(
|
|
5883
5024
|
await mapWithConcurrency(
|
|
5884
|
-
|
|
5025
|
+
resolveSkillInstallOrder(manifest2, need),
|
|
5885
5026
|
FETCH_CONCURRENCY,
|
|
5886
5027
|
(skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5887
5028
|
)
|
|
@@ -5893,7 +5034,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
5893
5034
|
const order = manifest2 ? resolveAllSkillsInstallOrder(manifest2) : [];
|
|
5894
5035
|
const bundledIds = [...catalog.cli.entries.keys()].sort();
|
|
5895
5036
|
if (order.length === 0 && bundledIds.length === 0) {
|
|
5896
|
-
throw
|
|
5037
|
+
throw invalid(`${endpoint ?? "\u8FD9\u4E00\u8D9F\u770B\u7684\u6765\u6E90"} \u91CC\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
|
|
5897
5038
|
}
|
|
5898
5039
|
requestedIds = [...order.map((skill) => skill.id), ...bundledIds];
|
|
5899
5040
|
if (order.length > 0) {
|
|
@@ -5911,19 +5052,19 @@ async function skillsInstallCommand(id, args) {
|
|
|
5911
5052
|
await installBundled(id);
|
|
5912
5053
|
} else {
|
|
5913
5054
|
if (source === "cli") {
|
|
5914
|
-
throw
|
|
5055
|
+
throw invalid(`\u8FD9\u4E00\u7248 CLI \u6CA1\u6709\u81EA\u5E26 ${id} \u8FD9\u4E2A\u6280\u80FD\uFF08--source cli \u4E0D\u770B\u777F\u620E\u76EE\u5F55\uFF09`);
|
|
5915
5056
|
}
|
|
5916
5057
|
requestedIds = [id];
|
|
5917
5058
|
pushAll(
|
|
5918
5059
|
await mapWithConcurrency(
|
|
5919
|
-
|
|
5060
|
+
resolveSkillInstallOrder(manifest2, id),
|
|
5920
5061
|
FETCH_CONCURRENCY,
|
|
5921
5062
|
(skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5922
5063
|
)
|
|
5923
5064
|
);
|
|
5924
5065
|
}
|
|
5925
5066
|
}
|
|
5926
|
-
const
|
|
5067
|
+
const result = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
|
|
5927
5068
|
endpoint,
|
|
5928
5069
|
base,
|
|
5929
5070
|
target: layout.target,
|
|
@@ -5932,19 +5073,19 @@ async function skillsInstallCommand(id, args) {
|
|
|
5932
5073
|
const agents = layout.agentsFile ? await syncAgentsIndex(layout, manifest2) : void 0;
|
|
5933
5074
|
const fmt = args.output ?? "table";
|
|
5934
5075
|
if (fmt === "json" || fmt === "yaml") {
|
|
5935
|
-
process.stdout.write(formatObject14({ ...
|
|
5076
|
+
process.stdout.write(formatObject14({ ...result, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents, conflictCheck: conflictNote ? { checked: false, note: conflictNote } : { checked: true } }, { format: fmt }) + "\n");
|
|
5936
5077
|
} else {
|
|
5937
|
-
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${
|
|
5078
|
+
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result.baseDir}` : `\u2705 ${id} \u2192 ${path4.join(result.baseDir, id, "SKILL.md")}`;
|
|
5938
5079
|
process.stdout.write(`${headline}
|
|
5939
5080
|
`);
|
|
5940
|
-
if (
|
|
5941
|
-
process.stdout.write(all ? ` \u65B0\u5199\u5165 ${
|
|
5942
|
-
` : ` \u5DF2\u5B89\u88C5 ${
|
|
5081
|
+
if (result.installed.length > 0) {
|
|
5082
|
+
process.stdout.write(all ? ` \u65B0\u5199\u5165 ${result.installed.length} \u4E2A
|
|
5083
|
+
` : ` \u5DF2\u5B89\u88C5 ${result.installed.join(" \u2192 ")}
|
|
5943
5084
|
`);
|
|
5944
5085
|
}
|
|
5945
|
-
if (
|
|
5946
|
-
process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${
|
|
5947
|
-
` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${
|
|
5086
|
+
if (result.unchanged.length > 0) {
|
|
5087
|
+
process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${result.unchanged.length} \u4E2A
|
|
5088
|
+
` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${result.unchanged.join(", ")}
|
|
5948
5089
|
`);
|
|
5949
5090
|
}
|
|
5950
5091
|
const referenceCount = documents.reduce((n, d) => n + (d.references?.length ?? 0), 0);
|
|
@@ -5956,7 +5097,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
5956
5097
|
process.stdout.write(` \u5DF2${agents === "created" ? "\u521B\u5EFA" : "\u66F4\u65B0"} ${layout.agentsFile} \u91CC\u7684\u6280\u80FD\u7D22\u5F15\uFF08Codex \u8BFB\u8FD9\u91CC\uFF09
|
|
5957
5098
|
`);
|
|
5958
5099
|
}
|
|
5959
|
-
process.stdout.write(` \u6765\u6E90 ${endpoint ?? (args.from ?
|
|
5100
|
+
process.stdout.write(` \u6765\u6E90 ${endpoint ?? (args.from ? path4.resolve(args.from) : `@hcmai/cli@${CLI_VERSION}`)}
|
|
5960
5101
|
`);
|
|
5961
5102
|
if (conflictNote) process.stdout.write(` \u26A0\uFE0F ${conflictNote}
|
|
5962
5103
|
`);
|
|
@@ -5986,18 +5127,18 @@ async function listLocalSkillIds(root) {
|
|
|
5986
5127
|
try {
|
|
5987
5128
|
entries = await fsp2.readdir(root, { withFileTypes: true });
|
|
5988
5129
|
} catch {
|
|
5989
|
-
throw
|
|
5130
|
+
throw invalid(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
|
|
5990
5131
|
}
|
|
5991
5132
|
const ids = [];
|
|
5992
5133
|
for (const entry of entries) {
|
|
5993
5134
|
if (!entry.isDirectory()) continue;
|
|
5994
|
-
if (await
|
|
5135
|
+
if (await exists(path4.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
|
|
5995
5136
|
}
|
|
5996
|
-
if (ids.length === 0) throw
|
|
5137
|
+
if (ids.length === 0) throw invalid(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
|
|
5997
5138
|
return ids.sort();
|
|
5998
5139
|
}
|
|
5999
5140
|
async function fetchSkillDocument(http, skill, endpoint, base) {
|
|
6000
|
-
const relatives =
|
|
5141
|
+
const relatives = normalizeReferences(skill.references, skill.id);
|
|
6001
5142
|
const references = await Promise.all(relatives.map(async (relative2) => ({
|
|
6002
5143
|
path: relative2,
|
|
6003
5144
|
content: await fetchSkillReference(http, skill.id, relative2, base)
|
|
@@ -6018,23 +5159,23 @@ async function syncAgentsIndex(layout, manifest2) {
|
|
|
6018
5159
|
for (const category of manifest2?.categories ?? []) {
|
|
6019
5160
|
for (const skill of category.skills ?? []) catalog.set(skill.id, skill);
|
|
6020
5161
|
}
|
|
6021
|
-
const agentsDir =
|
|
5162
|
+
const agentsDir = path4.dirname(layout.agentsFile);
|
|
6022
5163
|
const entries = Object.keys(state.skills).sort().map((id) => {
|
|
6023
5164
|
const known = catalog.get(id);
|
|
6024
|
-
const absolute =
|
|
6025
|
-
let relative2 =
|
|
5165
|
+
const absolute = path4.join(layout.baseDir, id, "SKILL.md");
|
|
5166
|
+
let relative2 = path4.relative(agentsDir, absolute);
|
|
6026
5167
|
if (!relative2.startsWith(".")) relative2 = `./${relative2}`;
|
|
6027
5168
|
return {
|
|
6028
5169
|
id,
|
|
6029
5170
|
title: known?.title ?? id,
|
|
6030
5171
|
description: known?.description ?? "",
|
|
6031
|
-
relativePath: relative2.split(
|
|
5172
|
+
relativePath: relative2.split(path4.sep).join("/")
|
|
6032
5173
|
};
|
|
6033
5174
|
});
|
|
6034
5175
|
return upsertAgentsBlock(layout.agentsFile, renderAgentsBlock(entries));
|
|
6035
5176
|
}
|
|
6036
5177
|
async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
6037
|
-
|
|
5178
|
+
assertSafeSkillId(targetId);
|
|
6038
5179
|
const state = /* @__PURE__ */ new Map();
|
|
6039
5180
|
const pathStack = [];
|
|
6040
5181
|
const order = [];
|
|
@@ -6043,20 +5184,20 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
6043
5184
|
if (current === "done") return;
|
|
6044
5185
|
if (current === "visiting") {
|
|
6045
5186
|
const start = pathStack.indexOf(id);
|
|
6046
|
-
throw
|
|
5187
|
+
throw invalid(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
|
|
6047
5188
|
}
|
|
6048
|
-
|
|
6049
|
-
const local =
|
|
5189
|
+
assertSafeSkillId(id);
|
|
5190
|
+
const local = path4.join(sourceRoot, id, "SKILL.md");
|
|
6050
5191
|
let markdown;
|
|
6051
5192
|
try {
|
|
6052
5193
|
markdown = await fsp2.readFile(local, "utf8");
|
|
6053
5194
|
} catch (cause) {
|
|
6054
5195
|
if (cause?.code === "ENOENT") {
|
|
6055
|
-
throw
|
|
5196
|
+
throw invalid(requiredBy ? `\u672C\u5730\u6280\u80FD ${requiredBy} \u4F9D\u8D56\u4E0D\u5B58\u5728: ${id}\uFF08${local}\uFF09` : `\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709 ${id}/SKILL.md\uFF1A${path4.join(sourceRoot, id)}`);
|
|
6056
5197
|
}
|
|
6057
5198
|
throw cause;
|
|
6058
5199
|
}
|
|
6059
|
-
if (!markdown.trim()) throw
|
|
5200
|
+
if (!markdown.trim()) throw invalid(`\u6280\u80FD ${id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
6060
5201
|
state.set(id, "visiting");
|
|
6061
5202
|
pathStack.push(id);
|
|
6062
5203
|
const document = {
|
|
@@ -6064,7 +5205,7 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
6064
5205
|
markdown,
|
|
6065
5206
|
source: local,
|
|
6066
5207
|
sourceKind: "local",
|
|
6067
|
-
references: await readLocalReferences(
|
|
5208
|
+
references: await readLocalReferences(path4.join(sourceRoot, id))
|
|
6068
5209
|
};
|
|
6069
5210
|
for (const required of parseSkillRequirements2(markdown, id)) {
|
|
6070
5211
|
await visit(required, id);
|
|
@@ -6078,7 +5219,7 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
6078
5219
|
}
|
|
6079
5220
|
async function readSkillsState(baseDir) {
|
|
6080
5221
|
try {
|
|
6081
|
-
const raw = await fsp2.readFile(
|
|
5222
|
+
const raw = await fsp2.readFile(path4.join(baseDir, SKILLS_STATE_FILE), "utf8");
|
|
6082
5223
|
const parsed = JSON.parse(raw);
|
|
6083
5224
|
if (!parsed || typeof parsed !== "object" || !parsed.skills) return void 0;
|
|
6084
5225
|
return migrateSkillsState(parsed);
|
|
@@ -6088,7 +5229,7 @@ async function readSkillsState(baseDir) {
|
|
|
6088
5229
|
}
|
|
6089
5230
|
async function writeSkillsState(baseDir, state) {
|
|
6090
5231
|
await fsp2.mkdir(baseDir, { recursive: true });
|
|
6091
|
-
const file =
|
|
5232
|
+
const file = path4.join(baseDir, SKILLS_STATE_FILE);
|
|
6092
5233
|
const temp = `${file}.${process.pid}.tmp`;
|
|
6093
5234
|
await fsp2.writeFile(temp, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
6094
5235
|
await fsp2.rename(temp, file);
|
|
@@ -6117,7 +5258,7 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
6117
5258
|
existing = await fsp2.readFile(agentsFile, "utf8");
|
|
6118
5259
|
} catch (cause) {
|
|
6119
5260
|
if (cause?.code !== "ENOENT") throw cause;
|
|
6120
|
-
await fsp2.mkdir(
|
|
5261
|
+
await fsp2.mkdir(path4.dirname(agentsFile), { recursive: true });
|
|
6121
5262
|
await fsp2.writeFile(agentsFile, block + "\n", "utf8");
|
|
6122
5263
|
return "created";
|
|
6123
5264
|
}
|
|
@@ -6133,22 +5274,22 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
6133
5274
|
return "appended";
|
|
6134
5275
|
}
|
|
6135
5276
|
async function installSkillDocuments(documents, requestedIds, baseDir, force, meta2) {
|
|
6136
|
-
if (requestedIds.length === 0) throw
|
|
6137
|
-
for (const id of requestedIds)
|
|
5277
|
+
if (requestedIds.length === 0) throw invalid("\u6CA1\u6709\u6307\u5B9A\u8981\u88C5\u7684\u6280\u80FD");
|
|
5278
|
+
for (const id of requestedIds) assertSafeSkillId(id);
|
|
6138
5279
|
const seen = /* @__PURE__ */ new Set();
|
|
6139
5280
|
const candidates = documents.map((document) => {
|
|
6140
|
-
|
|
6141
|
-
if (seen.has(document.id)) throw
|
|
5281
|
+
assertSafeSkillId(document.id);
|
|
5282
|
+
if (seen.has(document.id)) throw invalid(`\u5B89\u88C5\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
6142
5283
|
seen.add(document.id);
|
|
6143
|
-
if (!document.markdown.trim()) throw
|
|
5284
|
+
if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
6144
5285
|
return {
|
|
6145
5286
|
...document,
|
|
6146
|
-
target:
|
|
5287
|
+
target: path4.join(baseDir, document.id, "SKILL.md"),
|
|
6147
5288
|
bytes: Buffer.byteLength(document.markdown)
|
|
6148
5289
|
};
|
|
6149
5290
|
});
|
|
6150
5291
|
for (const id of requestedIds) {
|
|
6151
|
-
if (!seen.has(id)) throw
|
|
5292
|
+
if (!seen.has(id)) throw invalid(`\u5B89\u88C5\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${id}`);
|
|
6152
5293
|
}
|
|
6153
5294
|
const unchanged = /* @__PURE__ */ new Set();
|
|
6154
5295
|
for (const candidate of candidates) {
|
|
@@ -6159,7 +5300,7 @@ async function installSkillDocuments(documents, requestedIds, baseDir, force, me
|
|
|
6159
5300
|
unchanged.add(candidate.id);
|
|
6160
5301
|
continue;
|
|
6161
5302
|
}
|
|
6162
|
-
throw
|
|
5303
|
+
throw invalid(
|
|
6163
5304
|
`${candidate.target} \u5DF2\u5B58\u5728\uFF0C\u4E14\u548C\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\u4E0D\u4E00\u81F4\u3002\u7528 --force \u8986\u76D6\uFF08\u4F1A\u4E22\u6389\u672C\u5730\u6539\u52A8\uFF09\uFF0C\u6216 --dir <path> \u88C5\u5230\u522B\u5904\uFF1B\u53EA\u60F3\u8DDF\u4E0A\u4EA7\u54C1\u7248\u672C\u7528 hcm skills update`
|
|
6164
5305
|
);
|
|
6165
5306
|
}
|
|
@@ -6194,7 +5335,7 @@ async function sameOnDisk(baseDir, candidate) {
|
|
|
6194
5335
|
return true;
|
|
6195
5336
|
}
|
|
6196
5337
|
async function readLocalReferences(skillDir) {
|
|
6197
|
-
const root =
|
|
5338
|
+
const root = path4.join(skillDir, "references");
|
|
6198
5339
|
const out = [];
|
|
6199
5340
|
const walk = async (dir, prefix) => {
|
|
6200
5341
|
let items;
|
|
@@ -6207,12 +5348,12 @@ async function readLocalReferences(skillDir) {
|
|
|
6207
5348
|
for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
6208
5349
|
const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
6209
5350
|
if (item.isDirectory()) {
|
|
6210
|
-
await walk(
|
|
5351
|
+
await walk(path4.join(dir, item.name), relative2);
|
|
6211
5352
|
continue;
|
|
6212
5353
|
}
|
|
6213
5354
|
if (!item.isFile()) continue;
|
|
6214
5355
|
assertSafeReferencePath2(relative2);
|
|
6215
|
-
out.push({ path: relative2, content: await fsp2.readFile(
|
|
5356
|
+
out.push({ path: relative2, content: await fsp2.readFile(path4.join(dir, item.name), "utf8") });
|
|
6216
5357
|
}
|
|
6217
5358
|
};
|
|
6218
5359
|
await walk(root, "");
|
|
@@ -6228,7 +5369,7 @@ async function planStaleReferences(state, documents, baseDir) {
|
|
|
6228
5369
|
if (fresh.has(relative2)) continue;
|
|
6229
5370
|
const onDisk = await readInstalledReference(baseDir, document.id, relative2);
|
|
6230
5371
|
if (onDisk === void 0) continue;
|
|
6231
|
-
if (
|
|
5372
|
+
if (sha2562(onDisk) !== recordedSha) {
|
|
6232
5373
|
keptLocallyModified.push({ id: document.id, path: relative2 });
|
|
6233
5374
|
continue;
|
|
6234
5375
|
}
|
|
@@ -6243,15 +5384,15 @@ async function writeSkillDocuments(documents, baseDir, removals = []) {
|
|
|
6243
5384
|
const removed = [];
|
|
6244
5385
|
try {
|
|
6245
5386
|
for (const document of documents) {
|
|
6246
|
-
const target =
|
|
6247
|
-
await fsp2.mkdir(
|
|
6248
|
-
const temp =
|
|
5387
|
+
const target = path4.join(baseDir, document.id, "SKILL.md");
|
|
5388
|
+
await fsp2.mkdir(path4.dirname(target), { recursive: true });
|
|
5389
|
+
const temp = path4.join(path4.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
|
|
6249
5390
|
await fsp2.writeFile(temp, document.markdown, "utf8");
|
|
6250
5391
|
staged.push({ temp, target });
|
|
6251
5392
|
for (const reference of document.references ?? []) {
|
|
6252
5393
|
assertSafeReferencePath2(reference.path);
|
|
6253
|
-
const referenceTarget =
|
|
6254
|
-
await fsp2.mkdir(
|
|
5394
|
+
const referenceTarget = path4.join(baseDir, document.id, "references", reference.path);
|
|
5395
|
+
await fsp2.mkdir(path4.dirname(referenceTarget), { recursive: true });
|
|
6255
5396
|
const referenceTemp = `${referenceTarget}.${process.pid}.${Date.now()}.tmp`;
|
|
6256
5397
|
await fsp2.writeFile(referenceTemp, reference.content, "utf8");
|
|
6257
5398
|
staged.push({ temp: referenceTemp, target: referenceTarget });
|
|
@@ -6259,7 +5400,7 @@ async function writeSkillDocuments(documents, baseDir, removals = []) {
|
|
|
6259
5400
|
}
|
|
6260
5401
|
for (const stale of removals) {
|
|
6261
5402
|
assertSafeReferencePath2(stale.path);
|
|
6262
|
-
const original =
|
|
5403
|
+
const original = path4.join(baseDir, stale.id, "references", stale.path);
|
|
6263
5404
|
const parked = `${original}.${process.pid}.${Date.now()}.removed`;
|
|
6264
5405
|
await fsp2.rename(original, parked);
|
|
6265
5406
|
removed.push({ parked, original });
|
|
@@ -6285,6 +5426,7 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
6285
5426
|
if (meta2.base !== void 0) state.base = meta2.base;
|
|
6286
5427
|
if (meta2.all) state.all = true;
|
|
6287
5428
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
5429
|
+
state.runtime = { launch: await currentSkillsRuntimeLaunch() };
|
|
6288
5430
|
for (const document of documents) {
|
|
6289
5431
|
const previous = state.skills[document.id];
|
|
6290
5432
|
state.skills[document.id] = {
|
|
@@ -6292,24 +5434,24 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
6292
5434
|
// 🔴 两个键同值:`source` 留给旧 CLI 读(降级不炸),新代码只读 `sourceLocator`。
|
|
6293
5435
|
sourceKind: document.sourceKind ?? previous?.sourceKind ?? "chiron",
|
|
6294
5436
|
sourceLocator: document.source,
|
|
6295
|
-
sha256:
|
|
5437
|
+
sha256: sha2562(document.markdown),
|
|
6296
5438
|
installedAt: meta2.keepInstalledAt?.has(document.id) && previous ? previous.installedAt : state.updatedAt,
|
|
6297
5439
|
// 已经是直接安装的,不因为这次作为依赖被带进来就降级
|
|
6298
5440
|
direct: meta2.directIds.has(document.id) || Boolean(previous?.direct),
|
|
6299
5441
|
references: Object.fromEntries(
|
|
6300
|
-
(document.references ?? []).map((reference) => [reference.path,
|
|
5442
|
+
(document.references ?? []).map((reference) => [reference.path, sha2562(reference.content)])
|
|
6301
5443
|
)
|
|
6302
5444
|
};
|
|
6303
5445
|
}
|
|
6304
5446
|
await writeSkillsState(baseDir, state);
|
|
6305
5447
|
return state;
|
|
6306
5448
|
}
|
|
6307
|
-
function
|
|
6308
|
-
return new
|
|
5449
|
+
function invalid(message) {
|
|
5450
|
+
return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
|
|
6309
5451
|
}
|
|
6310
5452
|
async function readInstalled(baseDir, id) {
|
|
6311
5453
|
try {
|
|
6312
|
-
return await fsp2.readFile(
|
|
5454
|
+
return await fsp2.readFile(path4.join(baseDir, id, "SKILL.md"), "utf8");
|
|
6313
5455
|
} catch (cause) {
|
|
6314
5456
|
if (cause?.code === "ENOENT") return void 0;
|
|
6315
5457
|
throw cause;
|
|
@@ -6317,7 +5459,7 @@ async function readInstalled(baseDir, id) {
|
|
|
6317
5459
|
}
|
|
6318
5460
|
async function readInstalledReference(baseDir, id, relative2) {
|
|
6319
5461
|
try {
|
|
6320
|
-
return await fsp2.readFile(
|
|
5462
|
+
return await fsp2.readFile(path4.join(baseDir, id, "references", relative2), "utf8");
|
|
6321
5463
|
} catch (cause) {
|
|
6322
5464
|
if (cause?.code === "ENOENT") return void 0;
|
|
6323
5465
|
throw cause;
|
|
@@ -6377,7 +5519,7 @@ async function planSkillUpdate(options) {
|
|
|
6377
5519
|
for (const id of chironNeeded) {
|
|
6378
5520
|
if (!manifest2) continue;
|
|
6379
5521
|
try {
|
|
6380
|
-
for (const skill of
|
|
5522
|
+
for (const skill of resolveSkillInstallOrder(manifest2, id)) {
|
|
6381
5523
|
if (!closure.has(skill.id)) closure.set(skill.id, { kind: "chiron", skill });
|
|
6382
5524
|
}
|
|
6383
5525
|
} catch {
|
|
@@ -6403,12 +5545,12 @@ async function planSkillUpdate(options) {
|
|
|
6403
5545
|
const remote = await fetch();
|
|
6404
5546
|
const remoteReferences = remote.references ?? [];
|
|
6405
5547
|
let differsFromRemote = remote.markdown !== onDisk;
|
|
6406
|
-
let editedLocally = record !== void 0 &&
|
|
5548
|
+
let editedLocally = record !== void 0 && sha2562(onDisk) !== record.sha256;
|
|
6407
5549
|
for (const reference of remoteReferences) {
|
|
6408
5550
|
const installed = await readInstalledReference(baseDir, id, reference.path);
|
|
6409
5551
|
if (installed !== reference.content) differsFromRemote = true;
|
|
6410
5552
|
const recordedSha = record?.references?.[reference.path];
|
|
6411
|
-
if (installed !== void 0 && recordedSha &&
|
|
5553
|
+
if (installed !== void 0 && recordedSha && sha2562(installed) !== recordedSha) editedLocally = true;
|
|
6412
5554
|
}
|
|
6413
5555
|
if (!differsFromRemote) {
|
|
6414
5556
|
entries.push({ id, status: "unchanged" });
|
|
@@ -6449,9 +5591,9 @@ async function skillsUpdateCommand(args) {
|
|
|
6449
5591
|
const layout = await resolveInstallLayout(args);
|
|
6450
5592
|
const state = await readSkillsState(layout.baseDir);
|
|
6451
5593
|
if (!state || Object.keys(state.skills).length === 0) {
|
|
6452
|
-
throw new
|
|
6453
|
-
code:
|
|
6454
|
-
message: `${
|
|
5594
|
+
throw new CliError16({
|
|
5595
|
+
code: CliErrorCode15.INVALID_ARGUMENT,
|
|
5596
|
+
message: `${path4.join(layout.baseDir, SKILLS_STATE_FILE)} \u4E0D\u5B58\u5728\u6216\u4E3A\u7A7A \u2014\u2014 \u8FD9\u4E2A\u76EE\u5F55\u4E0B\u8FD8\u6CA1\u6709\u7528\u672C CLI \u88C5\u8FC7\u6280\u80FD\u3002\u5148 \`hcm skills install <id>\`\uFF1B\u5982\u679C\u6280\u80FD\u662F\u624B\u5DE5\u62F7\u8FDB\u6765\u7684\uFF0C\u91CD\u88C5\u4E00\u6B21\u5373\u53EF\u63A5\u7BA1\u5347\u7EA7\u3002`
|
|
6455
5597
|
});
|
|
6456
5598
|
}
|
|
6457
5599
|
const source = parseSourceFilter(args.source);
|
|
@@ -6581,8 +5723,8 @@ import {
|
|
|
6581
5723
|
loadEnv as loadEnv6,
|
|
6582
5724
|
loadGlobalConfig as loadGlobalConfig7,
|
|
6583
5725
|
resolveActiveEnv as resolveActiveEnv4,
|
|
6584
|
-
CliError as
|
|
6585
|
-
CliErrorCode as
|
|
5726
|
+
CliError as CliError17,
|
|
5727
|
+
CliErrorCode as CliErrorCode16
|
|
6586
5728
|
} from "@hcmai/sdk";
|
|
6587
5729
|
init_exit();
|
|
6588
5730
|
async function resolveBootstrapKey(args, stdinTaken) {
|
|
@@ -6590,8 +5732,8 @@ async function resolveBootstrapKey(args, stdinTaken) {
|
|
|
6590
5732
|
if (fromEnv) return fromEnv;
|
|
6591
5733
|
if (args.bootstrapKeyStdin) {
|
|
6592
5734
|
if (stdinTaken) {
|
|
6593
|
-
throw new
|
|
6594
|
-
code:
|
|
5735
|
+
throw new CliError17({
|
|
5736
|
+
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
6595
5737
|
message: "--bootstrap-key-stdin \u4E0E --admin-password-stdin \u4E0D\u80FD\u540C\u65F6\u7528\uFF08stdin \u53EA\u6709\u4E00\u8DEF\uFF09\uFF1B\u628A\u5176\u4E2D\u4E00\u4E2A\u6539\u8D70 HCM_BOOTSTRAP_KEY \u73AF\u5883\u53D8\u91CF\u6216\u4EA4\u4E92\u8F93\u5165"
|
|
6596
5738
|
});
|
|
6597
5739
|
}
|
|
@@ -6621,8 +5763,8 @@ async function resolveEndpoint2(args) {
|
|
|
6621
5763
|
const envName = resolveActiveEnv4({ envFlag: args.env }, process.env, g);
|
|
6622
5764
|
const env2 = await loadEnv6(envName).catch(() => null);
|
|
6623
5765
|
if (!env2?.endpoint) {
|
|
6624
|
-
throw new
|
|
6625
|
-
code:
|
|
5766
|
+
throw new CliError17({
|
|
5767
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
6626
5768
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`"
|
|
6627
5769
|
});
|
|
6628
5770
|
}
|
|
@@ -6631,14 +5773,14 @@ async function resolveEndpoint2(args) {
|
|
|
6631
5773
|
async function tenantBootstrapCommand(args) {
|
|
6632
5774
|
try {
|
|
6633
5775
|
if (!args.tenantId || !args.adminUsername) {
|
|
6634
|
-
throw new
|
|
6635
|
-
code:
|
|
5776
|
+
throw new CliError17({
|
|
5777
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
6636
5778
|
message: "--tenant-id \u4E0E --admin-username \u5FC5\u586B"
|
|
6637
5779
|
});
|
|
6638
5780
|
}
|
|
6639
5781
|
const endpoint = await resolveEndpoint2(args);
|
|
6640
5782
|
const { pwd, stdinTaken } = await resolveAdminPassword(args);
|
|
6641
|
-
const
|
|
5783
|
+
const key2 = await resolveBootstrapKey(args, stdinTaken);
|
|
6642
5784
|
if (!args.yes) {
|
|
6643
5785
|
const ok = await promptConfirm(
|
|
6644
5786
|
`\u5C06\u5728 ${endpoint} \u521B\u5EFA\u79DF\u6237 "${args.tenantId}"\uFF08admin: ${args.adminUsername}\uFF09\u3002\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\uFF0C\u7EE7\u7EED\uFF1F`
|
|
@@ -6654,7 +5796,7 @@ async function tenantBootstrapCommand(args) {
|
|
|
6654
5796
|
onRefresh: async () => {
|
|
6655
5797
|
}
|
|
6656
5798
|
});
|
|
6657
|
-
const
|
|
5799
|
+
const result = await bootstrapTenant(http, key2, {
|
|
6658
5800
|
tenantId: args.tenantId,
|
|
6659
5801
|
adminUsername: args.adminUsername,
|
|
6660
5802
|
adminPassword: pwd,
|
|
@@ -6663,12 +5805,12 @@ async function tenantBootstrapCommand(args) {
|
|
|
6663
5805
|
const fmt = args.output ?? "json";
|
|
6664
5806
|
if (fmt !== "json") {
|
|
6665
5807
|
process.stdout.write(
|
|
6666
|
-
`\u2705 \u79DF\u6237 ${
|
|
5808
|
+
`\u2705 \u79DF\u6237 ${result.tenantId} \u5DF2\u5F00\u901A\uFF08adminUserId ${result.adminUserId ?? "-"}\uFF09
|
|
6667
5809
|
\u4E0B\u4E00\u6B65\uFF1Ahcm env add <name> --endpoint ${endpoint} && hcm login --env <name>
|
|
6668
5810
|
`
|
|
6669
5811
|
);
|
|
6670
5812
|
}
|
|
6671
|
-
process.stdout.write(formatObject15(
|
|
5813
|
+
process.stdout.write(formatObject15(result, { format: fmt }) + "\n");
|
|
6672
5814
|
process.exit(0);
|
|
6673
5815
|
} catch (e) {
|
|
6674
5816
|
const err = e;
|
|
@@ -6692,8 +5834,8 @@ import {
|
|
|
6692
5834
|
getTenantMeta,
|
|
6693
5835
|
saveTenantMeta,
|
|
6694
5836
|
deleteTenantMeta,
|
|
6695
|
-
CliError as
|
|
6696
|
-
CliErrorCode as
|
|
5837
|
+
CliError as CliError18,
|
|
5838
|
+
CliErrorCode as CliErrorCode17
|
|
6697
5839
|
} from "@hcmai/sdk";
|
|
6698
5840
|
init_exit();
|
|
6699
5841
|
init_auth_guard();
|
|
@@ -6729,21 +5871,21 @@ async function metaListCommand(args) {
|
|
|
6729
5871
|
fail4(e, args);
|
|
6730
5872
|
}
|
|
6731
5873
|
}
|
|
6732
|
-
async function metaGetCommand(
|
|
5874
|
+
async function metaGetCommand(path9, args) {
|
|
6733
5875
|
try {
|
|
6734
5876
|
const c = await client2(args);
|
|
6735
|
-
const content = await getTenantMeta(c.raw(),
|
|
5877
|
+
const content = await getTenantMeta(c.raw(), path9);
|
|
6736
5878
|
process.stdout.write(content.endsWith("\n") ? content : content + "\n");
|
|
6737
5879
|
process.exit(0);
|
|
6738
5880
|
} catch (e) {
|
|
6739
5881
|
fail4(e, args);
|
|
6740
5882
|
}
|
|
6741
5883
|
}
|
|
6742
|
-
async function metaSetCommand(
|
|
5884
|
+
async function metaSetCommand(path9, args) {
|
|
6743
5885
|
try {
|
|
6744
5886
|
if (!args.file) {
|
|
6745
|
-
throw new
|
|
6746
|
-
code:
|
|
5887
|
+
throw new CliError18({
|
|
5888
|
+
code: CliErrorCode17.MISSING_FLAG,
|
|
6747
5889
|
message: "--file <path> \u5FC5\u586B\uFF08\u4ECE\u6587\u4EF6\u8BFB\u53D6 meta \u539F\u6587\uFF1B\u7528 - \u8868\u793A stdin\uFF09"
|
|
6748
5890
|
});
|
|
6749
5891
|
}
|
|
@@ -6756,7 +5898,7 @@ async function metaSetCommand(path11, args) {
|
|
|
6756
5898
|
}) : await fsp3.readFile(args.file, "utf8");
|
|
6757
5899
|
if (args.dryRun) {
|
|
6758
5900
|
process.stdout.write(
|
|
6759
|
-
`[dry-run] \u5C06\u5199\u5165 ${
|
|
5901
|
+
`[dry-run] \u5C06\u5199\u5165 ${path9}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
|
|
6760
5902
|
---
|
|
6761
5903
|
${content}
|
|
6762
5904
|
`
|
|
@@ -6764,35 +5906,35 @@ ${content}
|
|
|
6764
5906
|
process.exit(0);
|
|
6765
5907
|
}
|
|
6766
5908
|
const c = await client2(args);
|
|
6767
|
-
const
|
|
5909
|
+
const result = await saveTenantMeta(c.raw(), path9, content);
|
|
6768
5910
|
const fmt = args.output ?? "json";
|
|
6769
5911
|
if (fmt !== "json") {
|
|
6770
|
-
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${
|
|
5912
|
+
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path9}
|
|
6771
5913
|
\u8BB0\u5F97\u6E05\u7F13\u5B58\uFF1Ahcm cache clear-meta <Model>
|
|
6772
5914
|
`);
|
|
6773
5915
|
}
|
|
6774
|
-
process.stdout.write(formatObject16(
|
|
5916
|
+
process.stdout.write(formatObject16(result, { format: fmt }) + "\n");
|
|
6775
5917
|
process.exit(0);
|
|
6776
5918
|
} catch (e) {
|
|
6777
5919
|
if (e?.code === "ENOENT") exitWithError(new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${args.file}`));
|
|
6778
5920
|
fail4(e, args);
|
|
6779
5921
|
}
|
|
6780
5922
|
}
|
|
6781
|
-
async function metaDeleteCommand(
|
|
5923
|
+
async function metaDeleteCommand(path9, args) {
|
|
6782
5924
|
try {
|
|
6783
5925
|
if (!args.yes) {
|
|
6784
|
-
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${
|
|
5926
|
+
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path9}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
|
|
6785
5927
|
if (!ok) {
|
|
6786
5928
|
process.stderr.write("\u5DF2\u53D6\u6D88\n");
|
|
6787
5929
|
process.exit(1);
|
|
6788
5930
|
}
|
|
6789
5931
|
}
|
|
6790
5932
|
const c = await client2(args);
|
|
6791
|
-
const
|
|
5933
|
+
const result = await deleteTenantMeta(c.raw(), path9);
|
|
6792
5934
|
const fmt = args.output ?? "json";
|
|
6793
|
-
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${
|
|
5935
|
+
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path9}
|
|
6794
5936
|
`);
|
|
6795
|
-
process.stdout.write(formatObject16(
|
|
5937
|
+
process.stdout.write(formatObject16(result, { format: fmt }) + "\n");
|
|
6796
5938
|
process.exit(0);
|
|
6797
5939
|
} catch (e) {
|
|
6798
5940
|
fail4(e, args);
|
|
@@ -6803,8 +5945,8 @@ async function metaDeleteCommand(path11, args) {
|
|
|
6803
5945
|
init_exit();
|
|
6804
5946
|
init_auth_guard();
|
|
6805
5947
|
import * as crypto from "crypto";
|
|
6806
|
-
import * as
|
|
6807
|
-
import * as
|
|
5948
|
+
import * as fs5 from "fs/promises";
|
|
5949
|
+
import * as path5 from "path";
|
|
6808
5950
|
import {
|
|
6809
5951
|
buildMiniPreviewTargets,
|
|
6810
5952
|
buildMiniVerifyAgentReport,
|
|
@@ -6873,7 +6015,7 @@ async function workspaceListCommand(args) {
|
|
|
6873
6015
|
try {
|
|
6874
6016
|
const client3 = HcmClient16.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
6875
6017
|
const limit = parsePositiveInt3(args.limit, 50, "--limit");
|
|
6876
|
-
const
|
|
6018
|
+
const result = await client3.query("TenantExtensionWorkspace", {
|
|
6877
6019
|
keyword: args.keyword,
|
|
6878
6020
|
fields: [
|
|
6879
6021
|
"workspaceKey",
|
|
@@ -6888,7 +6030,7 @@ async function workspaceListCommand(args) {
|
|
|
6888
6030
|
sort: [{ field: "devModeEnabled", desc: true }, { field: "updateTime", desc: true }],
|
|
6889
6031
|
limit
|
|
6890
6032
|
});
|
|
6891
|
-
writeWorkspaceListResult(
|
|
6033
|
+
writeWorkspaceListResult(result.rows, args.output ?? "table", result.total);
|
|
6892
6034
|
process.exit(0);
|
|
6893
6035
|
} catch (e) {
|
|
6894
6036
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -6902,12 +6044,12 @@ async function workspaceActiveCommand(args) {
|
|
|
6902
6044
|
fallbackEnv: localContext?.profile
|
|
6903
6045
|
});
|
|
6904
6046
|
const client3 = HcmClient16.fromAuthContext(ctx);
|
|
6905
|
-
const
|
|
6047
|
+
const result = await client3.query("TenantExtensionWorkspace", {
|
|
6906
6048
|
filter: { devModeEnabled: true },
|
|
6907
6049
|
fields: ["workspaceKey", "devModeEnabled"],
|
|
6908
6050
|
limit: 1
|
|
6909
6051
|
});
|
|
6910
|
-
const activeWorkspaceKey = optionalText(
|
|
6052
|
+
const activeWorkspaceKey = optionalText(result.rows[0]?.workspaceKey) ?? null;
|
|
6911
6053
|
const view = {
|
|
6912
6054
|
activeWorkspaceKey,
|
|
6913
6055
|
localWorkspaceKey: localContext?.workspaceKey ?? null,
|
|
@@ -6939,7 +6081,7 @@ async function workspaceBindCommand(workspaceKey, args) {
|
|
|
6939
6081
|
tenantId: args.tenant ?? client3?.tenantId,
|
|
6940
6082
|
endpoint: args.endpoint ?? client3?.endpoint
|
|
6941
6083
|
});
|
|
6942
|
-
await
|
|
6084
|
+
await fs5.mkdir(rootDir, { recursive: true });
|
|
6943
6085
|
await writeWorkspaceContext(rootDir, context);
|
|
6944
6086
|
writeWorkspaceBindResult(context, rootDir, args.output ?? "table");
|
|
6945
6087
|
process.exit(0);
|
|
@@ -6974,7 +6116,7 @@ async function workspaceCreateCommand(workspaceKey, args) {
|
|
|
6974
6116
|
tenantId: client3.tenantId,
|
|
6975
6117
|
endpoint: client3.endpoint
|
|
6976
6118
|
});
|
|
6977
|
-
await
|
|
6119
|
+
await fs5.mkdir(rootDir, { recursive: true });
|
|
6978
6120
|
await writeWorkspaceContext(rootDir, context);
|
|
6979
6121
|
writeWorkspaceCreateResult(
|
|
6980
6122
|
{
|
|
@@ -7011,23 +6153,23 @@ async function workspacePullCommand(args) {
|
|
|
7011
6153
|
tenantId: client3.tenantId ?? existing?.tenantId,
|
|
7012
6154
|
endpoint: client3.endpoint ?? existing?.endpoint
|
|
7013
6155
|
});
|
|
7014
|
-
await
|
|
6156
|
+
await fs5.mkdir(rootDir, { recursive: true });
|
|
7015
6157
|
await writeWorkspaceContext(rootDir, context);
|
|
7016
6158
|
const files = await listWorkspaceFiles(client3.raw(), workspaceKey, { pageSize: 5e3 });
|
|
7017
6159
|
const miniApps = detectRemoteMiniApps(files.data);
|
|
7018
6160
|
const pulledMiniApps = [];
|
|
7019
6161
|
for (const appCode of miniApps) {
|
|
7020
|
-
const
|
|
6162
|
+
const result = await pullMiniAppProject({
|
|
7021
6163
|
http: client3.raw(),
|
|
7022
6164
|
appCode,
|
|
7023
6165
|
workspaceKey,
|
|
7024
|
-
targetDir:
|
|
6166
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
7025
6167
|
tenantId: client3.tenantId,
|
|
7026
6168
|
endpoint: client3.endpoint,
|
|
7027
6169
|
profile: args.profile ?? client3.profile,
|
|
7028
6170
|
force: !!args.force
|
|
7029
6171
|
});
|
|
7030
|
-
pulledMiniApps.push(
|
|
6172
|
+
pulledMiniApps.push(result);
|
|
7031
6173
|
}
|
|
7032
6174
|
const rawFiles = files.data.filter(
|
|
7033
6175
|
(file) => !isDirectoryMarker(file.relativePath) && !isMiniAppPath(file.relativePath)
|
|
@@ -7036,7 +6178,7 @@ async function workspacePullCommand(args) {
|
|
|
7036
6178
|
const rawRemoteByLocalPath = /* @__PURE__ */ new Map();
|
|
7037
6179
|
for (const file of rawFiles) {
|
|
7038
6180
|
const content = await loadWorkspaceFileContent(client3.raw(), file.id);
|
|
7039
|
-
const target =
|
|
6181
|
+
const target = path5.join(rootDir, content.relativePath);
|
|
7040
6182
|
await writeTextFile(target, content.content, !!args.force);
|
|
7041
6183
|
pulledRawFiles.push(content.relativePath);
|
|
7042
6184
|
rawRemoteByLocalPath.set(content.relativePath, file);
|
|
@@ -7083,10 +6225,10 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
7083
6225
|
fields: args.fields,
|
|
7084
6226
|
defaults: args.defaults
|
|
7085
6227
|
});
|
|
7086
|
-
const
|
|
6228
|
+
const result = await initMiniAppProject({
|
|
7087
6229
|
appCode,
|
|
7088
6230
|
workspaceKey: context.workspaceKey,
|
|
7089
|
-
targetDir:
|
|
6231
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
7090
6232
|
name: args.name,
|
|
7091
6233
|
defaultSurface: parseSurface(args.surface),
|
|
7092
6234
|
templateKind,
|
|
@@ -7100,7 +6242,7 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
7100
6242
|
profile: args.env ?? args.profile ?? client3?.profile ?? context.profile,
|
|
7101
6243
|
force: !!args.force
|
|
7102
6244
|
});
|
|
7103
|
-
writeWorkspaceAddMiniResult(
|
|
6245
|
+
writeWorkspaceAddMiniResult(result, rootDir, args.output ?? "table");
|
|
7104
6246
|
process.exit(0);
|
|
7105
6247
|
} catch (e) {
|
|
7106
6248
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -7180,12 +6322,12 @@ async function workspacePushCommand(args) {
|
|
|
7180
6322
|
});
|
|
7181
6323
|
const results = [];
|
|
7182
6324
|
for (const app of miniApps) {
|
|
7183
|
-
const
|
|
6325
|
+
const result = await pushMiniAppProject({
|
|
7184
6326
|
http: client3.raw(),
|
|
7185
6327
|
rootDir: app.dir,
|
|
7186
6328
|
dryRun: !!args.dryRun
|
|
7187
6329
|
});
|
|
7188
|
-
results.push({ app, result
|
|
6330
|
+
results.push({ app, result });
|
|
7189
6331
|
}
|
|
7190
6332
|
writeWorkspacePushResult(rawResult, results, args.output ?? "table");
|
|
7191
6333
|
process.exit(
|
|
@@ -7327,7 +6469,7 @@ function removedMiniCommand() {
|
|
|
7327
6469
|
);
|
|
7328
6470
|
}
|
|
7329
6471
|
function resolveRootDir(dir) {
|
|
7330
|
-
return
|
|
6472
|
+
return path5.resolve(dir ?? ".");
|
|
7331
6473
|
}
|
|
7332
6474
|
function buildWorkspaceContext(args) {
|
|
7333
6475
|
return {
|
|
@@ -7343,7 +6485,7 @@ function buildWorkspaceContext(args) {
|
|
|
7343
6485
|
async function readWorkspaceContext(rootDir) {
|
|
7344
6486
|
try {
|
|
7345
6487
|
const parsed = JSON.parse(
|
|
7346
|
-
await
|
|
6488
|
+
await fs5.readFile(path5.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
|
|
7347
6489
|
);
|
|
7348
6490
|
if (parsed.kind !== WORKSPACE_CONTEXT_KIND || parsed.version !== WORKSPACE_CONTEXT_VERSION) {
|
|
7349
6491
|
throw new Error(`${WORKSPACE_CONTEXT_FILE} is not an HCM workspace context`);
|
|
@@ -7372,25 +6514,25 @@ async function requireWorkspaceContext(rootDir) {
|
|
|
7372
6514
|
return context;
|
|
7373
6515
|
}
|
|
7374
6516
|
async function writeWorkspaceContext(rootDir, context) {
|
|
7375
|
-
await
|
|
7376
|
-
|
|
6517
|
+
await fs5.writeFile(
|
|
6518
|
+
path5.join(rootDir, WORKSPACE_CONTEXT_FILE),
|
|
7377
6519
|
JSON.stringify(context, null, 2) + "\n"
|
|
7378
6520
|
);
|
|
7379
6521
|
}
|
|
7380
6522
|
async function writeTextFile(file, content, force) {
|
|
7381
6523
|
try {
|
|
7382
|
-
await
|
|
6524
|
+
await fs5.access(file);
|
|
7383
6525
|
if (!force) throw new Error(`${file} already exists; use --force to overwrite`);
|
|
7384
6526
|
} catch (e) {
|
|
7385
6527
|
if (e.code !== "ENOENT") throw e;
|
|
7386
6528
|
}
|
|
7387
|
-
await
|
|
7388
|
-
await
|
|
6529
|
+
await fs5.mkdir(path5.dirname(file), { recursive: true });
|
|
6530
|
+
await fs5.writeFile(file, content);
|
|
7389
6531
|
}
|
|
7390
6532
|
async function readWorkspaceRawManifest(rootDir, context) {
|
|
7391
6533
|
try {
|
|
7392
6534
|
const parsed = JSON.parse(
|
|
7393
|
-
await
|
|
6535
|
+
await fs5.readFile(path5.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
|
|
7394
6536
|
);
|
|
7395
6537
|
if (parsed.version !== WORKSPACE_CONTEXT_VERSION || parsed.workspaceKey !== context.workspaceKey) {
|
|
7396
6538
|
throw new Error(`${WORKSPACE_PULL_MANIFEST_FILE} does not match workspace ${context.workspaceKey}`);
|
|
@@ -7408,8 +6550,8 @@ async function readWorkspaceRawManifest(rootDir, context) {
|
|
|
7408
6550
|
}
|
|
7409
6551
|
}
|
|
7410
6552
|
async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath) {
|
|
7411
|
-
const stateDir =
|
|
7412
|
-
await
|
|
6553
|
+
const stateDir = path5.join(rootDir, WORKSPACE_STATE_DIR);
|
|
6554
|
+
await fs5.mkdir(stateDir, { recursive: true });
|
|
7413
6555
|
const snapshots = files.map((file) => ({
|
|
7414
6556
|
localPath: file.localPath,
|
|
7415
6557
|
remotePath: file.localPath,
|
|
@@ -7418,8 +6560,8 @@ async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath
|
|
|
7418
6560
|
size: file.size,
|
|
7419
6561
|
lastModified: remoteByLocalPath.get(file.localPath)?.lastModified
|
|
7420
6562
|
}));
|
|
7421
|
-
await
|
|
7422
|
-
|
|
6563
|
+
await fs5.writeFile(
|
|
6564
|
+
path5.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
|
|
7423
6565
|
JSON.stringify(
|
|
7424
6566
|
{
|
|
7425
6567
|
version: WORKSPACE_CONTEXT_VERSION,
|
|
@@ -7441,10 +6583,10 @@ async function readWorkspaceRawLocalFiles(rootDir) {
|
|
|
7441
6583
|
return files;
|
|
7442
6584
|
}
|
|
7443
6585
|
async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
7444
|
-
const absoluteDir =
|
|
6586
|
+
const absoluteDir = path5.join(rootDir, relativeDir);
|
|
7445
6587
|
let entries;
|
|
7446
6588
|
try {
|
|
7447
|
-
entries = await
|
|
6589
|
+
entries = await fs5.readdir(absoluteDir, { withFileTypes: true });
|
|
7448
6590
|
} catch (e) {
|
|
7449
6591
|
if (e.code === "ENOENT") return;
|
|
7450
6592
|
throw e;
|
|
@@ -7452,19 +6594,19 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
|
7452
6594
|
for (const entry of entries) {
|
|
7453
6595
|
if (entry.isDirectory() && RAW_WORKSPACE_EXCLUDED_DIRS.has(entry.name)) continue;
|
|
7454
6596
|
if (entry.isFile() && RAW_WORKSPACE_EXCLUDED_FILES.has(entry.name)) continue;
|
|
7455
|
-
const localPath = normalizeRelativePath(
|
|
7456
|
-
const absolutePath =
|
|
6597
|
+
const localPath = normalizeRelativePath(path5.posix.join(toPosix(relativeDir), entry.name));
|
|
6598
|
+
const absolutePath = path5.join(rootDir, localPath);
|
|
7457
6599
|
if (entry.isDirectory()) {
|
|
7458
6600
|
await walkWorkspaceRawFiles(rootDir, localPath, files);
|
|
7459
6601
|
continue;
|
|
7460
6602
|
}
|
|
7461
6603
|
if (!entry.isFile()) continue;
|
|
7462
|
-
const content = await
|
|
6604
|
+
const content = await fs5.readFile(absolutePath, "utf-8");
|
|
7463
6605
|
files.push({
|
|
7464
6606
|
localPath,
|
|
7465
6607
|
absolutePath,
|
|
7466
6608
|
content,
|
|
7467
|
-
sha256:
|
|
6609
|
+
sha256: sha2563(content),
|
|
7468
6610
|
size: Buffer.byteLength(content, "utf-8")
|
|
7469
6611
|
});
|
|
7470
6612
|
}
|
|
@@ -7478,13 +6620,13 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
7478
6620
|
const localFiles = await readWorkspaceRawLocalFiles(args.rootDir);
|
|
7479
6621
|
const remoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
7480
6622
|
const plan = buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles);
|
|
7481
|
-
const
|
|
6623
|
+
const result = {
|
|
7482
6624
|
workspaceKey: args.context.workspaceKey,
|
|
7483
6625
|
dryRun: args.dryRun,
|
|
7484
6626
|
operations: plan.operations,
|
|
7485
6627
|
conflicts: plan.conflicts
|
|
7486
6628
|
};
|
|
7487
|
-
if (args.dryRun || plan.conflicts.length) return
|
|
6629
|
+
if (args.dryRun || plan.conflicts.length) return result;
|
|
7488
6630
|
const localByPath = new Map(localFiles.map((file) => [file.localPath, file]));
|
|
7489
6631
|
for (const operation of plan.operations) {
|
|
7490
6632
|
if (operation.operation === "skip") continue;
|
|
@@ -7510,17 +6652,17 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
7510
6652
|
const latestRemoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
7511
6653
|
const latestRemoteByLocalPath = new Map(latestRemoteFiles.map((file) => [file.item.relativePath, file.item]));
|
|
7512
6654
|
await writeWorkspaceRawState(args.rootDir, args.context, localFiles, latestRemoteByLocalPath);
|
|
7513
|
-
return
|
|
6655
|
+
return result;
|
|
7514
6656
|
}
|
|
7515
6657
|
async function loadWorkspaceRawRemoteFiles(http, workspaceKey) {
|
|
7516
6658
|
const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5e3 });
|
|
7517
|
-
const
|
|
6659
|
+
const result = [];
|
|
7518
6660
|
for (const item of list.data) {
|
|
7519
6661
|
if (isDirectoryMarker(item.relativePath) || isMiniAppPath(item.relativePath)) continue;
|
|
7520
6662
|
const content = await loadWorkspaceFileContent(http, item.id);
|
|
7521
|
-
|
|
6663
|
+
result.push({ item, content: content.content });
|
|
7522
6664
|
}
|
|
7523
|
-
return
|
|
6665
|
+
return result;
|
|
7524
6666
|
}
|
|
7525
6667
|
function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
7526
6668
|
const status = computeWorkspaceRawStatus(manifest2, localFiles);
|
|
@@ -7531,7 +6673,7 @@ function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
|
7531
6673
|
for (const entry of status) {
|
|
7532
6674
|
const remote = remoteByLocal.get(entry.localPath);
|
|
7533
6675
|
const base = baseByLocal.get(entry.localPath);
|
|
7534
|
-
const remoteSha = remote ?
|
|
6676
|
+
const remoteSha = remote ? sha2563(remote.content) : void 0;
|
|
7535
6677
|
if (entry.status === "added") {
|
|
7536
6678
|
if (remote) {
|
|
7537
6679
|
conflicts.push(workspaceRawConflict(entry, "remote file already exists but was not in local snapshot"));
|
|
@@ -7633,15 +6775,15 @@ function workspaceRawConflict(entry, reason) {
|
|
|
7633
6775
|
};
|
|
7634
6776
|
}
|
|
7635
6777
|
async function discoverMiniApps(rootDir) {
|
|
7636
|
-
const appsRoot =
|
|
6778
|
+
const appsRoot = path5.join(rootDir, MINI_APPS_DIR);
|
|
7637
6779
|
let entries;
|
|
7638
6780
|
try {
|
|
7639
|
-
entries = await
|
|
6781
|
+
entries = await fs5.readdir(appsRoot, { withFileTypes: true });
|
|
7640
6782
|
} catch (e) {
|
|
7641
6783
|
if (e.code === "ENOENT") return [];
|
|
7642
6784
|
throw e;
|
|
7643
6785
|
}
|
|
7644
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir:
|
|
6786
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir: path5.join(appsRoot, entry.name) })).sort((a, b) => a.appCode.localeCompare(b.appCode));
|
|
7645
6787
|
}
|
|
7646
6788
|
function detectRemoteMiniApps(files) {
|
|
7647
6789
|
const appCodes = /* @__PURE__ */ new Set();
|
|
@@ -7777,9 +6919,9 @@ function filterPreviewTargets(targets, surface) {
|
|
|
7777
6919
|
throw new Error("--surface must be all, bare, standalone or embedded");
|
|
7778
6920
|
}
|
|
7779
6921
|
function normalizeWorkspaceKey(value) {
|
|
7780
|
-
const
|
|
7781
|
-
if (!
|
|
7782
|
-
return
|
|
6922
|
+
const key2 = value.trim();
|
|
6923
|
+
if (!key2) throw new Error("workspaceKey is required");
|
|
6924
|
+
return key2;
|
|
7783
6925
|
}
|
|
7784
6926
|
function normalizeRelativePath(value) {
|
|
7785
6927
|
const normalized = toPosix(value).replace(/^\/+/, "").replace(/\/+/g, "/");
|
|
@@ -7794,9 +6936,9 @@ function normalizeRelativePath(value) {
|
|
|
7794
6936
|
return normalized;
|
|
7795
6937
|
}
|
|
7796
6938
|
function toPosix(value) {
|
|
7797
|
-
return value.split(
|
|
6939
|
+
return value.split(path5.sep).join("/");
|
|
7798
6940
|
}
|
|
7799
|
-
function
|
|
6941
|
+
function sha2563(content) {
|
|
7800
6942
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
7801
6943
|
}
|
|
7802
6944
|
function stringRequired(value, field) {
|
|
@@ -7863,52 +7005,52 @@ function writeWorkspaceBindResult(context, rootDir, format) {
|
|
|
7863
7005
|
].join("\n") + "\n"
|
|
7864
7006
|
);
|
|
7865
7007
|
}
|
|
7866
|
-
function writeWorkspaceCreateResult(
|
|
7008
|
+
function writeWorkspaceCreateResult(result, format) {
|
|
7867
7009
|
if (format !== "table") {
|
|
7868
|
-
process.stdout.write(formatObject17(
|
|
7010
|
+
process.stdout.write(formatObject17(result, { format }) + "\n");
|
|
7869
7011
|
return;
|
|
7870
7012
|
}
|
|
7871
7013
|
process.stdout.write(
|
|
7872
7014
|
[
|
|
7873
|
-
`workspace created: ${
|
|
7874
|
-
`id: ${
|
|
7875
|
-
`directory: ${
|
|
7876
|
-
`profile: ${
|
|
7877
|
-
`tenant: ${
|
|
7878
|
-
`activated: ${
|
|
7015
|
+
`workspace created: ${result.workspaceKey}`,
|
|
7016
|
+
`id: ${result.id}`,
|
|
7017
|
+
`directory: ${result.rootDir}`,
|
|
7018
|
+
`profile: ${result.profile}`,
|
|
7019
|
+
`tenant: ${result.tenantId}`,
|
|
7020
|
+
`activated: ${result.activated ? "yes" : "no"}`
|
|
7879
7021
|
].join("\n") + "\n"
|
|
7880
7022
|
);
|
|
7881
7023
|
}
|
|
7882
|
-
function writeWorkspacePullResult(
|
|
7024
|
+
function writeWorkspacePullResult(result, format) {
|
|
7883
7025
|
if (format !== "table") {
|
|
7884
|
-
process.stdout.write(formatObject17(
|
|
7026
|
+
process.stdout.write(formatObject17(result, { format }) + "\n");
|
|
7885
7027
|
return;
|
|
7886
7028
|
}
|
|
7887
7029
|
const lines = [
|
|
7888
|
-
`workspace pulled: ${
|
|
7889
|
-
`directory: ${
|
|
7890
|
-
`remote files: ${
|
|
7891
|
-
`mini apps: ${
|
|
7892
|
-
`raw files: ${
|
|
7030
|
+
`workspace pulled: ${result.workspaceKey}`,
|
|
7031
|
+
`directory: ${result.rootDir}`,
|
|
7032
|
+
`remote files: ${result.totalRemoteFiles}`,
|
|
7033
|
+
`mini apps: ${result.miniApps.length}`,
|
|
7034
|
+
`raw files: ${result.rawFiles.length}`
|
|
7893
7035
|
];
|
|
7894
|
-
if (
|
|
7036
|
+
if (result.miniApps.length) {
|
|
7895
7037
|
lines.push("mini artifacts:");
|
|
7896
|
-
for (const app of
|
|
7038
|
+
for (const app of result.miniApps) lines.push(` - ${app.appCode}: ${app.files} files`);
|
|
7897
7039
|
}
|
|
7898
7040
|
process.stdout.write(lines.join("\n") + "\n");
|
|
7899
7041
|
}
|
|
7900
|
-
function writeWorkspaceAddMiniResult(
|
|
7042
|
+
function writeWorkspaceAddMiniResult(result, rootDir, format) {
|
|
7901
7043
|
if (format !== "table") {
|
|
7902
|
-
process.stdout.write(formatObject17({ ...
|
|
7044
|
+
process.stdout.write(formatObject17({ ...result, workspaceRoot: rootDir }, { format }) + "\n");
|
|
7903
7045
|
return;
|
|
7904
7046
|
}
|
|
7905
7047
|
process.stdout.write(
|
|
7906
7048
|
[
|
|
7907
|
-
`workspace artifact added: mini:${
|
|
7908
|
-
`workspace: ${
|
|
7049
|
+
`workspace artifact added: mini:${result.appCode}`,
|
|
7050
|
+
`workspace: ${result.workspaceKey}`,
|
|
7909
7051
|
`workspace root: ${rootDir}`,
|
|
7910
|
-
`directory: ${
|
|
7911
|
-
`files: ${
|
|
7052
|
+
`directory: ${result.targetDir}`,
|
|
7053
|
+
`files: ${result.files.length}`
|
|
7912
7054
|
].join("\n") + "\n"
|
|
7913
7055
|
);
|
|
7914
7056
|
}
|
|
@@ -8093,8 +7235,8 @@ import {
|
|
|
8093
7235
|
deleteIdentity as deleteIdentity2,
|
|
8094
7236
|
loadGlobalConfig as loadGlobalConfig8,
|
|
8095
7237
|
TokenStore as TokenStore3,
|
|
8096
|
-
CliError as
|
|
8097
|
-
CliErrorCode as
|
|
7238
|
+
CliError as CliError19,
|
|
7239
|
+
CliErrorCode as CliErrorCode18
|
|
8098
7240
|
} from "@hcmai/sdk";
|
|
8099
7241
|
async function identityList(args) {
|
|
8100
7242
|
try {
|
|
@@ -8122,8 +7264,8 @@ async function identityUse(name) {
|
|
|
8122
7264
|
try {
|
|
8123
7265
|
const meta2 = await loadIdentity4(name);
|
|
8124
7266
|
const env2 = await loadEnv7(meta2.env).catch(() => {
|
|
8125
|
-
throw new
|
|
8126
|
-
code:
|
|
7267
|
+
throw new CliError19({
|
|
7268
|
+
code: CliErrorCode18.IDENTITY_NOT_IN_ENV,
|
|
8127
7269
|
message: `identity '${name}' references env '${meta2.env}' which no longer exists. Re-create the env, or remove this orphan: hcm identity remove ${name}`
|
|
8128
7270
|
});
|
|
8129
7271
|
});
|
|
@@ -8165,7 +7307,7 @@ import {
|
|
|
8165
7307
|
} from "@hcmai/sdk";
|
|
8166
7308
|
|
|
8167
7309
|
// src/commands/delivery.ts
|
|
8168
|
-
import { createHash as
|
|
7310
|
+
import { createHash as createHash4 } from "crypto";
|
|
8169
7311
|
var APPROVAL_BYPASS_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--force", "--auto-approve"]);
|
|
8170
7312
|
var CREDENTIAL_FLAGS = /* @__PURE__ */ new Set([
|
|
8171
7313
|
"--password",
|
|
@@ -8187,7 +7329,7 @@ var GUARDED_EXECUTOR_REASON_CODES = /* @__PURE__ */ new Set([
|
|
|
8187
7329
|
]);
|
|
8188
7330
|
function assessDeliveryCommand(inputArgv, options = {}) {
|
|
8189
7331
|
const argv = inputArgv[0] === "--" ? inputArgv.slice(1) : [...inputArgv];
|
|
8190
|
-
const commandSha256 =
|
|
7332
|
+
const commandSha256 = sha2564(JSON.stringify(argv));
|
|
8191
7333
|
const name = argv[0] ?? "";
|
|
8192
7334
|
const subcommand = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
|
|
8193
7335
|
const envAlias = flagValue(argv, "--env");
|
|
@@ -8218,7 +7360,7 @@ function assessDeliveryCommand(inputArgv, options = {}) {
|
|
|
8218
7360
|
return {
|
|
8219
7361
|
schemaVersion: 1,
|
|
8220
7362
|
contractVersion: "2026-08-29",
|
|
8221
|
-
assessmentId:
|
|
7363
|
+
assessmentId: sha2564(`2026-08-29
|
|
8222
7364
|
${commandSha256}`).slice(0, 24),
|
|
8223
7365
|
commandSha256,
|
|
8224
7366
|
command: { name: name || "(empty)", ...subcommand ? { subcommand } : {} },
|
|
@@ -8385,22 +7527,22 @@ function summarizeTarget(argv, actionResolution) {
|
|
|
8385
7527
|
}
|
|
8386
7528
|
if (name === "setting" && argv[2] && !argv[2].startsWith("-")) {
|
|
8387
7529
|
target.domain = argv[2];
|
|
8388
|
-
const settingKeys = valuesAfterFlag(argv, "--set").map((assignment) => assignment.split("=", 1)[0]).filter((
|
|
7530
|
+
const settingKeys = valuesAfterFlag(argv, "--set").map((assignment) => assignment.split("=", 1)[0]).filter((key2) => Boolean(key2)).sort();
|
|
8389
7531
|
if (settingKeys.length > 0) target.settingKeys = settingKeys;
|
|
8390
7532
|
}
|
|
8391
7533
|
const id = flagValue(argv, "--id");
|
|
8392
7534
|
if (name === "user" && subcommand === "reset-password") {
|
|
8393
|
-
if (id) target.userIdSha256 =
|
|
7535
|
+
if (id) target.userIdSha256 = sha2564(id);
|
|
8394
7536
|
const username = flagValue(argv, "--username");
|
|
8395
|
-
if (username) target.usernameSha256 =
|
|
7537
|
+
if (username) target.usernameSha256 = sha2564(username);
|
|
8396
7538
|
target.forceChangeOnNextLogin = !hasFlag(argv, "--no-force-change");
|
|
8397
7539
|
} else if (id) {
|
|
8398
|
-
target.recordIdSha256 =
|
|
7540
|
+
target.recordIdSha256 = sha2564(id);
|
|
8399
7541
|
}
|
|
8400
7542
|
for (const flag of PAYLOAD_FLAGS) {
|
|
8401
7543
|
const raw = flagValue(argv, flag);
|
|
8402
7544
|
if (!raw) continue;
|
|
8403
|
-
target.payloadSha256 =
|
|
7545
|
+
target.payloadSha256 = sha2564(raw);
|
|
8404
7546
|
try {
|
|
8405
7547
|
const parsed = JSON.parse(raw);
|
|
8406
7548
|
if (isPlainObject(parsed)) target.fieldNames = Object.keys(parsed).sort();
|
|
@@ -8497,8 +7639,8 @@ function valuesAfterFlag(argv, flag) {
|
|
|
8497
7639
|
}
|
|
8498
7640
|
return values;
|
|
8499
7641
|
}
|
|
8500
|
-
function
|
|
8501
|
-
return
|
|
7642
|
+
function sha2564(value) {
|
|
7643
|
+
return createHash4("sha256").update(value).digest("hex");
|
|
8502
7644
|
}
|
|
8503
7645
|
function isPlainObject(value) {
|
|
8504
7646
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -8683,11 +7825,11 @@ async function deliveryReconCommand(args) {
|
|
|
8683
7825
|
async function deliveryReconRoundCommand(args) {
|
|
8684
7826
|
let code;
|
|
8685
7827
|
try {
|
|
8686
|
-
const
|
|
7828
|
+
const result = inspectRound(args.dir ?? ".");
|
|
8687
7829
|
const fmt = args.output ?? "json";
|
|
8688
|
-
if (fmt !== "json") process.stdout.write(renderRound(
|
|
8689
|
-
process.stdout.write(formatObject20(
|
|
8690
|
-
code = ROUND_EXIT_CODES[
|
|
7830
|
+
if (fmt !== "json") process.stdout.write(renderRound(result));
|
|
7831
|
+
process.stdout.write(formatObject20(result, { format: fmt }) + "\n");
|
|
7832
|
+
code = ROUND_EXIT_CODES[result.state];
|
|
8691
7833
|
} catch (e) {
|
|
8692
7834
|
exitWithError(e);
|
|
8693
7835
|
code = 1;
|
|
@@ -8708,20 +7850,20 @@ import {
|
|
|
8708
7850
|
loadProtocolFacts,
|
|
8709
7851
|
scan
|
|
8710
7852
|
} from "@hcmai/sdk";
|
|
8711
|
-
import * as
|
|
8712
|
-
function renderGate0(
|
|
8713
|
-
const where = skillRoot ? `\uFF08${
|
|
7853
|
+
import * as path6 from "path";
|
|
7854
|
+
function renderGate0(result, skillRoot) {
|
|
7855
|
+
const where = skillRoot ? `\uFF08${path6.resolve(skillRoot)}\uFF09` : "";
|
|
8714
7856
|
const lines = [
|
|
8715
|
-
`\u626B\u63CF\u9762${where}\uFF1A${
|
|
7857
|
+
`\u626B\u63CF\u9762${where}\uFF1A${result.scanned.length} \u4E2A\u6587\u4EF6\uFF1B\u5224\u7EA2 ${result.red.length} \u6761\uFF1B\u7F3A\u53E3\u653E\u884C ${result.gaps.length} \u6761\u3002`
|
|
8716
7858
|
];
|
|
8717
7859
|
for (const channel of CHANNELS) {
|
|
8718
|
-
const rows =
|
|
7860
|
+
const rows = result.channels[channel];
|
|
8719
7861
|
lines.push(` [${channel}] ${rows.length} \u6761`);
|
|
8720
7862
|
for (const red of rows) lines.push(` - (${red.reason}) ${red.message}`);
|
|
8721
7863
|
}
|
|
8722
|
-
if (
|
|
8723
|
-
lines.push(` [gaps] ${
|
|
8724
|
-
for (const gap of
|
|
7864
|
+
if (result.gaps.length > 0) {
|
|
7865
|
+
lines.push(` [gaps] ${result.gaps.length} \u6761\uFF08\u4E0D\u9876 rc\uFF0C\u8BF7\u81EA\u5DF1\u770B\u4E00\u773C\uFF09`);
|
|
7866
|
+
for (const gap of result.gaps) lines.push(` - (${gap.reason}) ${gap.why}`);
|
|
8725
7867
|
}
|
|
8726
7868
|
return `${lines.join("\n")}
|
|
8727
7869
|
`;
|
|
@@ -8733,12 +7875,12 @@ async function deliveryGate0Command(args) {
|
|
|
8733
7875
|
const reconDir = args.recon ?? ".";
|
|
8734
7876
|
const { aclass, problems: aclassProblems } = deriveAclass(reconDir, args.mapping ?? null);
|
|
8735
7877
|
const { facts, problems: factsProblems } = loadProtocolFacts();
|
|
8736
|
-
const
|
|
7878
|
+
const result = scan(skillRoot, aclass, facts, { aclassProblems, factsProblems });
|
|
8737
7879
|
const premise2 = checkPremise([
|
|
8738
|
-
...
|
|
7880
|
+
...result.premise,
|
|
8739
7881
|
...baselineRoundPremise(inspectRound2(reconDir))
|
|
8740
7882
|
]);
|
|
8741
|
-
const merged = { ...
|
|
7883
|
+
const merged = { ...result, premise: premise2 };
|
|
8742
7884
|
const fmt = args.output ?? "json";
|
|
8743
7885
|
if (fmt !== "json") process.stdout.write(renderGate0(merged, skillRoot));
|
|
8744
7886
|
process.stdout.write(`${formatObject21(merged, { format: fmt })}
|
|
@@ -8766,7 +7908,7 @@ import {
|
|
|
8766
7908
|
runTriageRound
|
|
8767
7909
|
} from "@hcmai/sdk";
|
|
8768
7910
|
import { mkdirSync, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
8769
|
-
import * as
|
|
7911
|
+
import * as path7 from "path";
|
|
8770
7912
|
var DEFAULT_CAPABILITIES3 = "default";
|
|
8771
7913
|
function premise(message, hint) {
|
|
8772
7914
|
process.stderr.write(`\u{1F534} \u524D\u63D0\u4E0D\u6210\u7ACB\uFF1A${message}
|
|
@@ -8802,8 +7944,8 @@ function renderProblems(rows) {
|
|
|
8802
7944
|
`;
|
|
8803
7945
|
}
|
|
8804
7946
|
function writeTriageArtifact(outDir, body) {
|
|
8805
|
-
const out =
|
|
8806
|
-
mkdirSync(
|
|
7947
|
+
const out = path7.join(outDir, "work", "triage.md");
|
|
7948
|
+
mkdirSync(path7.dirname(out), { recursive: true });
|
|
8807
7949
|
writeFileSync2(out, body, "utf8");
|
|
8808
7950
|
return out;
|
|
8809
7951
|
}
|
|
@@ -8819,13 +7961,13 @@ async function runTriage(args) {
|
|
|
8819
7961
|
const client3 = HcmClient20.fromAuthContext(ctx);
|
|
8820
7962
|
const probe = httpProbe3(client3.raw(), args.capabilities ?? DEFAULT_CAPABILITIES3);
|
|
8821
7963
|
const artifactsDir = args.recon ?? new ReconLayout2(args.out ?? ".", ctx.tenantId, await computeReconSourceKey2(probe)).dir;
|
|
8822
|
-
const
|
|
8823
|
-
if (
|
|
8824
|
-
process.stdout.write(renderProblems(
|
|
7964
|
+
const result = await runTriageRound(probe, artifactsDir, entriesIn);
|
|
7965
|
+
if (result.body === null) {
|
|
7966
|
+
process.stdout.write(renderProblems(result.problems));
|
|
8825
7967
|
return 1;
|
|
8826
7968
|
}
|
|
8827
|
-
const out = writeTriageArtifact(args.out ?? ".",
|
|
8828
|
-
process.stdout.write(`\u5206\u8BCA\u81EA\u6D3D\uFF0C\u5DF2\u5199 ${out}\uFF08${
|
|
7969
|
+
const out = writeTriageArtifact(args.out ?? ".", result.body);
|
|
7970
|
+
process.stdout.write(`\u5206\u8BCA\u81EA\u6D3D\uFF0C\u5DF2\u5199 ${out}\uFF08${result.entries.length} \u6761\uFF09
|
|
8829
7971
|
`);
|
|
8830
7972
|
return 0;
|
|
8831
7973
|
}
|
|
@@ -8841,13 +7983,13 @@ async function deliveryTriageCommand(args) {
|
|
|
8841
7983
|
}
|
|
8842
7984
|
|
|
8843
7985
|
// src/commands/delivery-execution.ts
|
|
8844
|
-
import { createHash as
|
|
7986
|
+
import { createHash as createHash5, randomBytes as secureRandomBytes } from "crypto";
|
|
8845
7987
|
import { spawn } from "child_process";
|
|
8846
|
-
import { createReadStream
|
|
8847
|
-
import
|
|
8848
|
-
import { CliError as
|
|
7988
|
+
import { createReadStream, promises as fs6 } from "fs";
|
|
7989
|
+
import path8 from "path";
|
|
7990
|
+
import { CliError as CliError20, CliErrorCode as CliErrorCode19, formatObject as formatObject22 } from "@hcmai/sdk";
|
|
8849
7991
|
init_exit();
|
|
8850
|
-
var EXECUTION_CONTRACT_VERSION = "2026-08-29.controlled-
|
|
7992
|
+
var EXECUTION_CONTRACT_VERSION = "2026-08-29.controlled-v3";
|
|
8851
7993
|
var DEFAULT_TTL_SECONDS = 15 * 60;
|
|
8852
7994
|
var MIN_TTL_SECONDS = 60;
|
|
8853
7995
|
var MAX_TTL_SECONDS = 60 * 60;
|
|
@@ -8859,102 +8001,102 @@ async function prepareDeliveryExecution(input) {
|
|
|
8859
8001
|
assertGuardedAssessment(assessment, input.argv);
|
|
8860
8002
|
const ttlSeconds = input.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
8861
8003
|
if (!Number.isInteger(ttlSeconds) || ttlSeconds < MIN_TTL_SECONDS || ttlSeconds > MAX_TTL_SECONDS) {
|
|
8862
|
-
throw
|
|
8004
|
+
throw invalid2(`--ttl-seconds \u5FC5\u987B\u662F ${MIN_TTL_SECONDS} \u5230 ${MAX_TTL_SECONDS} \u7684\u6574\u6570`);
|
|
8863
8005
|
}
|
|
8864
8006
|
const change = await bindDocument(project, input.changeRef, "\u53D8\u66F4\u8BF4\u660E");
|
|
8865
8007
|
const rollback = await bindDocument(project, input.rollbackRef, "\u56DE\u6EDA\u8BF4\u660E");
|
|
8866
8008
|
const payloads = await bindPayloadFiles(project, input.argv);
|
|
8867
8009
|
const evidence = await bindEvidenceDirectory(project, input.evidenceDir);
|
|
8868
|
-
const { bytes: lockBytes, runtime: lockedRuntime } = await
|
|
8010
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readSkillsLock(project);
|
|
8869
8011
|
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
8870
8012
|
assertRuntimeMatches(runtime, lockedRuntime);
|
|
8871
8013
|
const created = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
8872
|
-
if (Number.isNaN(created.getTime())) throw
|
|
8014
|
+
if (Number.isNaN(created.getTime())) throw invalid2("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
8873
8015
|
const core = {
|
|
8874
8016
|
schemaVersion: 1,
|
|
8875
8017
|
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
8876
|
-
nonceSha256:
|
|
8018
|
+
nonceSha256: sha2565((input.randomBytes ?? secureRandomBytes)(12)),
|
|
8877
8019
|
createdAt: created.toISOString(),
|
|
8878
8020
|
expiresAt: new Date(created.getTime() + ttlSeconds * 1e3).toISOString(),
|
|
8879
|
-
|
|
8021
|
+
skillsLockSha256: sha2565(lockBytes),
|
|
8880
8022
|
runtime,
|
|
8881
8023
|
assessment,
|
|
8882
8024
|
documents: { change, rollback },
|
|
8883
8025
|
payloads,
|
|
8884
8026
|
evidenceDir: evidence.relative
|
|
8885
8027
|
};
|
|
8886
|
-
const planId =
|
|
8028
|
+
const planId = sha2565(canonicalJson(core)).slice(0, 24);
|
|
8887
8029
|
const plan = { ...core, planId };
|
|
8888
|
-
const plansDir =
|
|
8030
|
+
const plansDir = path8.join(project, ".hcm-delivery", "execution-plans");
|
|
8889
8031
|
await safeManagedDirectory(project, ".hcm-delivery/execution-plans", plansDir);
|
|
8890
|
-
const planFile =
|
|
8032
|
+
const planFile = path8.join(plansDir, `${planId}.json`);
|
|
8891
8033
|
try {
|
|
8892
|
-
await
|
|
8034
|
+
await fs6.writeFile(planFile, `${JSON.stringify(plan, null, 2)}
|
|
8893
8035
|
`, {
|
|
8894
8036
|
encoding: "utf8",
|
|
8895
8037
|
mode: 384,
|
|
8896
8038
|
flag: "wx"
|
|
8897
8039
|
});
|
|
8898
8040
|
} catch (cause) {
|
|
8899
|
-
throw
|
|
8041
|
+
throw invalid2(`\u6267\u884C\u8BA1\u5212\u5199\u5165\u5931\u8D25\u6216 planId \u5DF2\u5B58\u5728\uFF1A${planId}`, cause);
|
|
8900
8042
|
}
|
|
8901
8043
|
return plan;
|
|
8902
8044
|
}
|
|
8903
8045
|
async function executeDeliveryPlan(input) {
|
|
8904
|
-
if (!PLAN_ID_PATTERN.test(input.planId)) throw
|
|
8046
|
+
if (!PLAN_ID_PATTERN.test(input.planId)) throw invalid2("planId \u5FC5\u987B\u662F 24 \u4F4D\u5C0F\u5199\u5341\u516D\u8FDB\u5236");
|
|
8905
8047
|
const project = await realProject(input.project);
|
|
8906
8048
|
if (await readProjectMode(project) !== "controlled") {
|
|
8907
|
-
throw
|
|
8049
|
+
throw invalid2("\u9879\u76EE\u4E0D\u662F controlled\uFF0C\u4E0D\u80FD\u6267\u884C R2 \u8BA1\u5212");
|
|
8908
8050
|
}
|
|
8909
|
-
const planFile =
|
|
8051
|
+
const planFile = path8.join(project, ".hcm-delivery", "execution-plans", `${input.planId}.json`);
|
|
8910
8052
|
const plan = await readPlan(project, planFile);
|
|
8911
8053
|
if (plan.planId !== input.planId || computePlanId(plan) !== input.planId) {
|
|
8912
|
-
throw
|
|
8054
|
+
throw invalid2("\u6267\u884C\u8BA1\u5212\u5185\u5BB9\u4E0E planId \u6458\u8981\u4E0D\u4E00\u81F4");
|
|
8913
8055
|
}
|
|
8914
8056
|
const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
8915
|
-
if (Number.isNaN(now.getTime())) throw
|
|
8057
|
+
if (Number.isNaN(now.getTime())) throw invalid2("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
8916
8058
|
const expiresAt = Date.parse(plan.expiresAt);
|
|
8917
8059
|
const createdAt = Date.parse(plan.createdAt);
|
|
8918
8060
|
if (!Number.isFinite(createdAt) || !Number.isFinite(expiresAt) || expiresAt - createdAt < MIN_TTL_SECONDS * 1e3 || expiresAt - createdAt > MAX_TTL_SECONDS * 1e3) {
|
|
8919
|
-
throw
|
|
8061
|
+
throw invalid2("\u6267\u884C\u8BA1\u5212\u65F6\u95F4\u7A97\u53E3\u65E0\u6548");
|
|
8920
8062
|
}
|
|
8921
|
-
if (now.getTime() < createdAt - 5e3) throw
|
|
8922
|
-
if (now.getTime() > expiresAt) throw
|
|
8063
|
+
if (now.getTime() < createdAt - 5e3) throw invalid2("\u5F53\u524D\u65F6\u95F4\u65E9\u4E8E\u8BA1\u5212\u521B\u5EFA\u65F6\u95F4");
|
|
8064
|
+
if (now.getTime() > expiresAt) throw invalid2("\u6267\u884C\u8BA1\u5212\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0 prepare");
|
|
8923
8065
|
const assessment = await (input.assessCommand ?? assessEffectiveDeliveryCommand)(input.argv);
|
|
8924
8066
|
assertGuardedAssessment(assessment, input.argv);
|
|
8925
8067
|
if (canonicalJson(assessment) !== canonicalJson(plan.assessment)) {
|
|
8926
|
-
throw
|
|
8068
|
+
throw invalid2("\u5B9E\u9645 argv \u4E0E\u6267\u884C\u8BA1\u5212\u7684\u547D\u4EE4\u6458\u8981\u6216\u98CE\u9669\u4E8B\u5B9E\u4E0D\u4E00\u81F4");
|
|
8927
8069
|
}
|
|
8928
|
-
const { bytes: lockBytes, runtime: lockedRuntime } = await
|
|
8929
|
-
if (
|
|
8930
|
-
throw
|
|
8070
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readSkillsLock(project);
|
|
8071
|
+
if (sha2565(lockBytes) !== plan.skillsLockSha256) {
|
|
8072
|
+
throw invalid2("\u6280\u80FD\u9501\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0\u8BC4\u4F30\u5E76 prepare");
|
|
8931
8073
|
}
|
|
8932
8074
|
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
8933
8075
|
assertRuntimeMatches(runtime, lockedRuntime);
|
|
8934
8076
|
if (canonicalJson(runtime) !== canonicalJson(plan.runtime)) {
|
|
8935
|
-
throw
|
|
8077
|
+
throw invalid2("CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E\u6267\u884C\u8BA1\u5212\u4E0D\u4E00\u81F4");
|
|
8936
8078
|
}
|
|
8937
8079
|
await assertDocumentUnchanged(project, plan.documents.change, "\u53D8\u66F4\u8BF4\u660E");
|
|
8938
8080
|
await assertDocumentUnchanged(project, plan.documents.rollback, "\u56DE\u6EDA\u8BF4\u660E");
|
|
8939
8081
|
await assertPayloadsUnchanged(project, plan.payloads);
|
|
8940
8082
|
const evidenceDir = await bindEvidenceDirectory(project, plan.evidenceDir);
|
|
8941
|
-
const receiptFile =
|
|
8942
|
-
if (await pathExists(receiptFile)) throw
|
|
8943
|
-
const claimsDir =
|
|
8083
|
+
const receiptFile = path8.join(evidenceDir.absolute, `${plan.planId}.receipt.json`);
|
|
8084
|
+
if (await pathExists(receiptFile)) throw invalid2(`\u6267\u884C\u6536\u636E\u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u518D\u6B21\u6267\u884C\uFF1A${plan.planId}`);
|
|
8085
|
+
const claimsDir = path8.join(project, ".hcm-delivery", "execution-claims");
|
|
8944
8086
|
await safeManagedDirectory(project, ".hcm-delivery/execution-claims", claimsDir);
|
|
8945
|
-
const claimFile =
|
|
8087
|
+
const claimFile = path8.join(claimsDir, `${plan.planId}.json`);
|
|
8946
8088
|
try {
|
|
8947
|
-
await
|
|
8089
|
+
await fs6.writeFile(
|
|
8948
8090
|
claimFile,
|
|
8949
8091
|
`${JSON.stringify({ schemaVersion: 1, planId: plan.planId, claimedAt: now.toISOString() }, null, 2)}
|
|
8950
8092
|
`,
|
|
8951
8093
|
{ encoding: "utf8", mode: 384, flag: "wx" }
|
|
8952
8094
|
);
|
|
8953
8095
|
} catch (cause) {
|
|
8954
|
-
throw
|
|
8096
|
+
throw invalid2(`\u6267\u884C\u8BA1\u5212\u5DF2\u6D88\u8D39\uFF0C\u62D2\u7EDD\u91CD\u590D\u6267\u884C\uFF1A${plan.planId}`, cause);
|
|
8955
8097
|
}
|
|
8956
|
-
const stdoutHash =
|
|
8957
|
-
const stderrHash =
|
|
8098
|
+
const stdoutHash = createHash5("sha256");
|
|
8099
|
+
const stderrHash = createHash5("sha256");
|
|
8958
8100
|
const run = input.runCommand ?? runCurrentCliCommand;
|
|
8959
8101
|
let exitCode;
|
|
8960
8102
|
try {
|
|
@@ -8970,7 +8112,7 @@ async function executeDeliveryPlan(input) {
|
|
|
8970
8112
|
exitCode = 1;
|
|
8971
8113
|
}
|
|
8972
8114
|
const completed = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
8973
|
-
if (Number.isNaN(completed.getTime())) throw
|
|
8115
|
+
if (Number.isNaN(completed.getTime())) throw invalid2("\u5B8C\u6210\u65F6\u95F4\u65E0\u6548");
|
|
8974
8116
|
if (!Number.isInteger(exitCode) || exitCode < 0) exitCode = 1;
|
|
8975
8117
|
const receipt = {
|
|
8976
8118
|
schemaVersion: 1,
|
|
@@ -8989,9 +8131,9 @@ async function executeDeliveryPlan(input) {
|
|
|
8989
8131
|
}
|
|
8990
8132
|
async function deliveryPrepareCommand(argv, args) {
|
|
8991
8133
|
try {
|
|
8992
|
-
if (!args.changeRef) throw
|
|
8993
|
-
if (!args.rollbackRef) throw
|
|
8994
|
-
if (!args.evidenceDir) throw
|
|
8134
|
+
if (!args.changeRef) throw invalid2("--change-ref <file> \u5FC5\u586B");
|
|
8135
|
+
if (!args.rollbackRef) throw invalid2("--rollback-ref <file> \u5FC5\u586B");
|
|
8136
|
+
if (!args.evidenceDir) throw invalid2("--evidence-dir <dir> \u5FC5\u586B");
|
|
8995
8137
|
const ttlSeconds = parseTtl(args.ttlSeconds);
|
|
8996
8138
|
const plan = await prepareDeliveryExecution({
|
|
8997
8139
|
project: args.project ?? process.cwd(),
|
|
@@ -9020,54 +8162,75 @@ async function deliveryExecuteCommand(planId, argv, args) {
|
|
|
9020
8162
|
}
|
|
9021
8163
|
}
|
|
9022
8164
|
async function readProjectMode(project) {
|
|
9023
|
-
const file =
|
|
8165
|
+
const file = path8.join(project, ".hcm-delivery", "project.yml");
|
|
9024
8166
|
const content = await readSafeFile(project, file, "\u9879\u76EE\u914D\u7F6E");
|
|
9025
8167
|
const match = content.toString("utf8").match(
|
|
9026
8168
|
/^deliveryMode:\s*(?:"(plan-only|controlled)"|'(plan-only|controlled)'|(plan-only|controlled))\s*$/m
|
|
9027
8169
|
);
|
|
9028
8170
|
const mode = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
9029
8171
|
if (mode !== "plan-only" && mode !== "controlled") {
|
|
9030
|
-
throw
|
|
8172
|
+
throw invalid2("project.yml \u7F3A\u5C11\u5408\u6CD5 deliveryMode");
|
|
9031
8173
|
}
|
|
9032
8174
|
return mode;
|
|
9033
8175
|
}
|
|
9034
|
-
|
|
9035
|
-
|
|
9036
|
-
|
|
8176
|
+
var SKILLS_LOCK_CANDIDATES = [
|
|
8177
|
+
path8.join("hcm-skills", ".hcm-skills.json"),
|
|
8178
|
+
path8.join(".claude", "skills", ".hcm-skills.json")
|
|
8179
|
+
];
|
|
8180
|
+
async function readSkillsLock(project) {
|
|
8181
|
+
const found = [];
|
|
8182
|
+
for (const candidate of SKILLS_LOCK_CANDIDATES) {
|
|
8183
|
+
if (await pathExists(path8.join(project, candidate))) found.push(candidate);
|
|
8184
|
+
}
|
|
8185
|
+
if (found.length === 0) {
|
|
8186
|
+
throw invalid2(
|
|
8187
|
+
"\u627E\u4E0D\u5230\u6280\u80FD\u9501\uFF08hcm-skills/.hcm-skills.json \u6216 .claude/skills/.hcm-skills.json\uFF09\u3002next_step=\u5148\u5728\u9879\u76EE\u91CC\u8DD1\u4E00\u6B21 `hcm skills install <id>`\uFF0C\u518D\u91CD\u65B0 prepare"
|
|
8188
|
+
);
|
|
8189
|
+
}
|
|
8190
|
+
if (found.length > 1) {
|
|
8191
|
+
throw invalid2(
|
|
8192
|
+
`\u9879\u76EE\u91CC\u6709\u591A\u4EFD\u6280\u80FD\u9501\uFF08${found.join("\u3001")}\uFF09\uFF0C\u65E0\u6CD5\u552F\u4E00\u5224\u5B9A\u53D7\u63A7\u6267\u884C\u7ED1\u5B9A\u54EA\u4E00\u4EFD\u3002next_step=\u53EA\u4FDD\u7559\u5B9E\u9645\u5728\u7528\u7684\u90A3\u4E2A\u6280\u80FD\u76EE\u5F55`
|
|
8193
|
+
);
|
|
8194
|
+
}
|
|
8195
|
+
const bytes = await readSafeFile(project, path8.join(project, found[0]), "\u6280\u80FD\u9501");
|
|
9037
8196
|
let parsed;
|
|
9038
8197
|
try {
|
|
9039
8198
|
parsed = JSON.parse(bytes.toString("utf8"));
|
|
9040
8199
|
} catch (cause) {
|
|
9041
|
-
throw
|
|
8200
|
+
throw invalid2("\u6280\u80FD\u9501\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
9042
8201
|
}
|
|
9043
8202
|
const launch = parsed?.runtime?.launch;
|
|
9044
|
-
if (!isRuntimeIdentity(launch))
|
|
8203
|
+
if (!isRuntimeIdentity(launch)) {
|
|
8204
|
+
throw invalid2(
|
|
8205
|
+
"\u6280\u80FD\u9501\u6CA1\u6709\u8BB0\u5F55 runtime.launch\uFF0C\u65E0\u6CD5\u786E\u8BA4\u300C\u73B0\u5728\u8FD9\u4EFD CLI \u5C31\u662F\u5F53\u521D\u88C5\u6280\u80FD\u7684\u90A3\u4EFD\u300D\u3002next_step=\u7528\u5F53\u524D CLI \u91CD\u8DD1\u4E00\u6B21 `hcm skills install`\uFF08\u6216 `hcm skills update`\uFF09\u5237\u65B0\u6280\u80FD\u9501"
|
|
8206
|
+
);
|
|
8207
|
+
}
|
|
9045
8208
|
return { bytes, runtime: launch };
|
|
9046
8209
|
}
|
|
9047
8210
|
async function bindDocument(project, reference, label) {
|
|
9048
8211
|
const relative2 = safeRelative(reference, label);
|
|
9049
8212
|
if (!(relative2.startsWith("work/") || relative2.startsWith("decisions/"))) {
|
|
9050
|
-
throw
|
|
8213
|
+
throw invalid2(`${label}\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE work/ \u6216 decisions/ \u4E0B`);
|
|
9051
8214
|
}
|
|
9052
|
-
const file =
|
|
8215
|
+
const file = path8.join(project, relative2);
|
|
9053
8216
|
const bytes = await readSafeFile(project, file, label);
|
|
9054
|
-
return { path: relative2, sha256:
|
|
8217
|
+
return { path: relative2, sha256: sha2565(bytes) };
|
|
9055
8218
|
}
|
|
9056
8219
|
async function assertDocumentUnchanged(project, document, label) {
|
|
9057
8220
|
const current = await bindDocument(project, document.path, label);
|
|
9058
|
-
if (current.sha256 !== document.sha256) throw
|
|
8221
|
+
if (current.sha256 !== document.sha256) throw invalid2(`${label}\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0 prepare`);
|
|
9059
8222
|
}
|
|
9060
8223
|
async function bindEvidenceDirectory(project, reference) {
|
|
9061
8224
|
const relative2 = safeRelative(reference, "evidence \u76EE\u5F55");
|
|
9062
8225
|
if (!(relative2 === "evidence" || relative2.startsWith("evidence/"))) {
|
|
9063
|
-
throw
|
|
8226
|
+
throw invalid2("evidence \u76EE\u5F55\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE evidence/ \u4E0B");
|
|
9064
8227
|
}
|
|
9065
|
-
const absolute =
|
|
8228
|
+
const absolute = path8.join(project, relative2);
|
|
9066
8229
|
await assertNoSymlinkComponents(project, relative2);
|
|
9067
|
-
const stat = await
|
|
9068
|
-
throw
|
|
8230
|
+
const stat = await fs6.stat(absolute).catch((cause) => {
|
|
8231
|
+
throw invalid2(`evidence \u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
9069
8232
|
});
|
|
9070
|
-
if (!stat.isDirectory()) throw
|
|
8233
|
+
if (!stat.isDirectory()) throw invalid2(`evidence \u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
9071
8234
|
return { relative: relative2, absolute };
|
|
9072
8235
|
}
|
|
9073
8236
|
async function readPlan(project, file) {
|
|
@@ -9076,39 +8239,39 @@ async function readPlan(project, file) {
|
|
|
9076
8239
|
try {
|
|
9077
8240
|
plan = JSON.parse(bytes.toString("utf8"));
|
|
9078
8241
|
} catch (cause) {
|
|
9079
|
-
throw
|
|
8242
|
+
throw invalid2("\u6267\u884C\u8BA1\u5212\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
9080
8243
|
}
|
|
9081
|
-
if (!isExecutionPlan(plan)) throw
|
|
8244
|
+
if (!isExecutionPlan(plan)) throw invalid2("\u6267\u884C\u8BA1\u5212 schema \u65E0\u6548");
|
|
9082
8245
|
return plan;
|
|
9083
8246
|
}
|
|
9084
8247
|
function computePlanId(plan) {
|
|
9085
8248
|
const core = { ...plan };
|
|
9086
8249
|
delete core.planId;
|
|
9087
|
-
return
|
|
8250
|
+
return sha2565(canonicalJson(core)).slice(0, 24);
|
|
9088
8251
|
}
|
|
9089
8252
|
function assertGuardedAssessment(assessment, argv) {
|
|
9090
|
-
if (assessment.risk !== "R2") throw
|
|
8253
|
+
if (assessment.risk !== "R2") throw invalid2("\u53EA\u6709 R2 \u547D\u4EE4\u53EF\u4EE5\u8FDB\u5165\u53D7\u63A7\u6267\u884C\u8BA1\u5212");
|
|
9091
8254
|
if (!assessment.guardedExecutionAvailable) {
|
|
9092
|
-
throw
|
|
8255
|
+
throw invalid2("\u8BE5 R2 \u547D\u4EE4\u4E0D\u5728\u9996\u6279\u53D7\u63A7\u6267\u884C\u5668\u652F\u6301\u9762");
|
|
9093
8256
|
}
|
|
9094
|
-
if (!assessment.environment.envAlias) throw
|
|
8257
|
+
if (!assessment.environment.envAlias) throw invalid2("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --env \u73AF\u5883\u522B\u540D");
|
|
9095
8258
|
if (!assessment.environment.identityAlias) {
|
|
9096
|
-
throw
|
|
8259
|
+
throw invalid2("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --as \u8EAB\u4EFD\u522B\u540D");
|
|
9097
8260
|
}
|
|
9098
8261
|
validateSupportedCommandShape(assessment.reasonCode, argv);
|
|
9099
8262
|
}
|
|
9100
8263
|
function validateSupportedCommandShape(reasonCode, argv) {
|
|
9101
8264
|
if (reasonCode === "credential-reset") {
|
|
9102
8265
|
if (argv[0] !== "user" || argv[1] !== "reset-password") {
|
|
9103
|
-
throw
|
|
8266
|
+
throw invalid2("credential-reset \u53EA\u652F\u6301 user reset-password");
|
|
9104
8267
|
}
|
|
9105
8268
|
const userId = optionalFlagValue(argv, "--id");
|
|
9106
8269
|
const username = optionalFlagValue(argv, "--username");
|
|
9107
8270
|
if (Boolean(userId) === Boolean(username)) {
|
|
9108
|
-
throw
|
|
8271
|
+
throw invalid2("user reset-password \u5FC5\u987B\u4E14\u53EA\u80FD\u5305\u542B --id \u6216 --username");
|
|
9109
8272
|
}
|
|
9110
8273
|
if (!argv.includes("--password-stdin")) {
|
|
9111
|
-
throw
|
|
8274
|
+
throw invalid2("user reset-password \u5FC5\u987B\u5305\u542B\u72EC\u7ACB\u7684 --password-stdin");
|
|
9112
8275
|
}
|
|
9113
8276
|
if (argv.some(
|
|
9114
8277
|
(value) => [
|
|
@@ -9120,7 +8283,7 @@ function validateSupportedCommandShape(reasonCode, argv) {
|
|
|
9120
8283
|
"--pat-stdin"
|
|
9121
8284
|
].includes(value.split("=", 1)[0] ?? "")
|
|
9122
8285
|
) || argv.some((value) => value.startsWith("--password-stdin="))) {
|
|
9123
|
-
throw
|
|
8286
|
+
throw invalid2("user reset-password \u4E0D\u5141\u8BB8\u5728 argv \u4E2D\u643A\u5E26\u660E\u6587\u51ED\u636E");
|
|
9124
8287
|
}
|
|
9125
8288
|
return;
|
|
9126
8289
|
}
|
|
@@ -9134,17 +8297,17 @@ function validateSupportedCommandShape(reasonCode, argv) {
|
|
|
9134
8297
|
return;
|
|
9135
8298
|
}
|
|
9136
8299
|
if (reasonCode === "cache-mutation") {
|
|
9137
|
-
if (!argv[2] || argv[2].startsWith("-")) throw
|
|
8300
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid2("cache clear \u5FC5\u987B\u5305\u542B Model");
|
|
9138
8301
|
return;
|
|
9139
8302
|
}
|
|
9140
8303
|
if (reasonCode === "setting-write") {
|
|
9141
|
-
if (!argv[2] || argv[2].startsWith("-")) throw
|
|
8304
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid2("setting \u5199\u547D\u4EE4\u5FC5\u987B\u5305\u542B domain");
|
|
9142
8305
|
if (argv[1] === "set") {
|
|
9143
8306
|
requireFlagValue(argv, "--set", "setting set \u5FC5\u987B\u5305\u542B --set <assignment>");
|
|
9144
8307
|
return;
|
|
9145
8308
|
}
|
|
9146
8309
|
if (argv[1] === "reset" && (!argv[3] || !argv[4])) {
|
|
9147
|
-
throw
|
|
8310
|
+
throw invalid2("setting reset \u5FC5\u987B\u5305\u542B domain\u3001namespace \u4E0E key");
|
|
9148
8311
|
}
|
|
9149
8312
|
}
|
|
9150
8313
|
}
|
|
@@ -9165,10 +8328,10 @@ function requireExactlyOnePayload(argv, command) {
|
|
|
9165
8328
|
const inline = optionalFlagValue(argv, "--data") !== void 0;
|
|
9166
8329
|
const fromFile = optionalFlagValue(argv, "--data-file") !== void 0;
|
|
9167
8330
|
if (inline && fromFile) {
|
|
9168
|
-
throw
|
|
8331
|
+
throw invalid2(`${command} \u7684 --data \u4E0E --data-file \u4E92\u65A5\uFF0C\u53EA\u80FD\u7ED9\u4E00\u4E2A`);
|
|
9169
8332
|
}
|
|
9170
8333
|
if (!inline && !fromFile) {
|
|
9171
|
-
throw
|
|
8334
|
+
throw invalid2(`${command} \u5FC5\u987B\u5305\u542B --data <JSON> \u6216 --data-file <\u6587\u4EF6>`);
|
|
9172
8335
|
}
|
|
9173
8336
|
}
|
|
9174
8337
|
var PAYLOAD_FILE_FLAGS = ["--data-file", "--params-file"];
|
|
@@ -9183,15 +8346,15 @@ async function bindPayloadFiles(project, argv) {
|
|
|
9183
8346
|
}
|
|
9184
8347
|
async function bindPayloadFile(project, reference, label) {
|
|
9185
8348
|
const relative2 = safeRelative(reference, label);
|
|
9186
|
-
const file =
|
|
8349
|
+
const file = path8.join(project, relative2);
|
|
9187
8350
|
const bytes = await readSafeFile(project, file, label);
|
|
9188
|
-
return { path: relative2, sha256:
|
|
8351
|
+
return { path: relative2, sha256: sha2565(bytes) };
|
|
9189
8352
|
}
|
|
9190
8353
|
async function assertPayloadsUnchanged(project, payloads) {
|
|
9191
8354
|
for (const payload of payloads) {
|
|
9192
8355
|
const current = await bindPayloadFile(project, payload.path, `\u8F7D\u8377 ${payload.path}`);
|
|
9193
8356
|
if (current.sha256 !== payload.sha256) {
|
|
9194
|
-
throw
|
|
8357
|
+
throw invalid2(`\u8F7D\u8377\u6587\u4EF6 ${payload.path} \u7684\u5185\u5BB9\u5DF2\u53D8\u66F4\uFF0C\u8BF7\u91CD\u65B0 prepare`);
|
|
9195
8358
|
}
|
|
9196
8359
|
}
|
|
9197
8360
|
}
|
|
@@ -9201,22 +8364,22 @@ function requireFlagValue(argv, flag, message) {
|
|
|
9201
8364
|
if (value === flag) {
|
|
9202
8365
|
const next = argv[index + 1];
|
|
9203
8366
|
if (next && !next.startsWith("-")) return next;
|
|
9204
|
-
throw
|
|
8367
|
+
throw invalid2(message);
|
|
9205
8368
|
}
|
|
9206
8369
|
if (value?.startsWith(`${flag}=`) && value.length > flag.length + 1) {
|
|
9207
8370
|
return value.slice(flag.length + 1);
|
|
9208
8371
|
}
|
|
9209
8372
|
}
|
|
9210
|
-
throw
|
|
8373
|
+
throw invalid2(message);
|
|
9211
8374
|
}
|
|
9212
8375
|
function assertRuntimeMatches(actual, expected) {
|
|
9213
8376
|
if (canonicalJson(actual) !== canonicalJson(expected)) {
|
|
9214
|
-
throw
|
|
8377
|
+
throw invalid2("\u5F53\u524D CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E\u6280\u80FD\u9501\u4E0D\u4E00\u81F4\u3002next_step=\u7528\u5F53\u524D CLI \u91CD\u8DD1\u4E00\u6B21 `hcm skills install` \u5237\u65B0\u6280\u80FD\u9501\uFF0C\u518D prepare");
|
|
9215
8378
|
}
|
|
9216
8379
|
}
|
|
9217
8380
|
async function currentRuntimeIdentity() {
|
|
9218
8381
|
const entry = process.argv[1];
|
|
9219
|
-
if (!entry) throw
|
|
8382
|
+
if (!entry) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
9220
8383
|
return {
|
|
9221
8384
|
kind: "node-entry",
|
|
9222
8385
|
executableSha256: await sha256File2(process.execPath),
|
|
@@ -9225,7 +8388,7 @@ async function currentRuntimeIdentity() {
|
|
|
9225
8388
|
}
|
|
9226
8389
|
async function runCurrentCliCommand(argv, cwd, streams) {
|
|
9227
8390
|
const entry = process.argv[1];
|
|
9228
|
-
if (!entry) throw
|
|
8391
|
+
if (!entry) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
9229
8392
|
return new Promise((resolve4, reject) => {
|
|
9230
8393
|
const child = spawn(process.execPath, [entry, ...argv], {
|
|
9231
8394
|
cwd,
|
|
@@ -9249,29 +8412,29 @@ async function runCurrentCliCommand(argv, cwd, streams) {
|
|
|
9249
8412
|
async function writeReceiptExclusive(target, receipt) {
|
|
9250
8413
|
const temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
9251
8414
|
try {
|
|
9252
|
-
await
|
|
8415
|
+
await fs6.writeFile(temp, `${JSON.stringify(receipt, null, 2)}
|
|
9253
8416
|
`, {
|
|
9254
8417
|
encoding: "utf8",
|
|
9255
8418
|
mode: 384,
|
|
9256
8419
|
flag: "wx"
|
|
9257
8420
|
});
|
|
9258
|
-
await
|
|
8421
|
+
await fs6.link(temp, target);
|
|
9259
8422
|
} catch (cause) {
|
|
9260
|
-
throw
|
|
8423
|
+
throw invalid2(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${path8.basename(target)}`, cause);
|
|
9261
8424
|
} finally {
|
|
9262
|
-
await
|
|
8425
|
+
await fs6.unlink(temp).catch(() => void 0);
|
|
9263
8426
|
}
|
|
9264
8427
|
}
|
|
9265
8428
|
async function safeManagedDirectory(project, relative2, absolute) {
|
|
9266
|
-
await assertNoSymlinkComponents(project,
|
|
9267
|
-
const existing = await
|
|
9268
|
-
if (existing?.isSymbolicLink()) throw
|
|
9269
|
-
if (existing && !existing.isDirectory()) throw
|
|
9270
|
-
if (!existing) await
|
|
9271
|
-
await
|
|
8429
|
+
await assertNoSymlinkComponents(project, path8.dirname(relative2));
|
|
8430
|
+
const existing = await fs6.lstat(absolute).catch(() => void 0);
|
|
8431
|
+
if (existing?.isSymbolicLink()) throw invalid2(`\u53D7\u7BA1\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8432
|
+
if (existing && !existing.isDirectory()) throw invalid2(`\u53D7\u7BA1\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
8433
|
+
if (!existing) await fs6.mkdir(absolute, { mode: 448 });
|
|
8434
|
+
await fs6.chmod(absolute, 448);
|
|
9272
8435
|
}
|
|
9273
8436
|
async function pathExists(file) {
|
|
9274
|
-
return
|
|
8437
|
+
return fs6.lstat(file).then(
|
|
9275
8438
|
() => true,
|
|
9276
8439
|
(cause) => {
|
|
9277
8440
|
if (cause.code === "ENOENT") return false;
|
|
@@ -9280,49 +8443,49 @@ async function pathExists(file) {
|
|
|
9280
8443
|
);
|
|
9281
8444
|
}
|
|
9282
8445
|
async function readSafeFile(project, file, label) {
|
|
9283
|
-
const relative2 =
|
|
9284
|
-
if (relative2.startsWith("../") ||
|
|
9285
|
-
throw
|
|
8446
|
+
const relative2 = path8.relative(project, file).split(path8.sep).join("/");
|
|
8447
|
+
if (relative2.startsWith("../") || path8.isAbsolute(relative2))
|
|
8448
|
+
throw invalid2(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
9286
8449
|
await assertNoSymlinkComponents(project, relative2);
|
|
9287
8450
|
let stat;
|
|
9288
8451
|
try {
|
|
9289
|
-
stat = await
|
|
8452
|
+
stat = await fs6.lstat(file);
|
|
9290
8453
|
} catch (cause) {
|
|
9291
|
-
throw
|
|
8454
|
+
throw invalid2(`${label}\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
9292
8455
|
}
|
|
9293
|
-
if (stat.isSymbolicLink()) throw
|
|
9294
|
-
if (!stat.isFile()) throw
|
|
9295
|
-
return
|
|
8456
|
+
if (stat.isSymbolicLink()) throw invalid2(`${label}\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8457
|
+
if (!stat.isFile()) throw invalid2(`${label}\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${relative2}`);
|
|
8458
|
+
return fs6.readFile(file);
|
|
9296
8459
|
}
|
|
9297
8460
|
async function assertNoSymlinkComponents(project, relative2) {
|
|
9298
8461
|
const parts = relative2.split("/").filter(Boolean);
|
|
9299
8462
|
let current = project;
|
|
9300
8463
|
for (const part of parts) {
|
|
9301
|
-
current =
|
|
9302
|
-
const stat = await
|
|
9303
|
-
if (stat?.isSymbolicLink()) throw
|
|
8464
|
+
current = path8.join(current, part);
|
|
8465
|
+
const stat = await fs6.lstat(current).catch(() => void 0);
|
|
8466
|
+
if (stat?.isSymbolicLink()) throw invalid2(`\u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
9304
8467
|
if (!stat) break;
|
|
9305
8468
|
}
|
|
9306
8469
|
}
|
|
9307
8470
|
async function realProject(requested) {
|
|
9308
|
-
const absolute =
|
|
8471
|
+
const absolute = path8.resolve(requested);
|
|
9309
8472
|
let project;
|
|
9310
8473
|
try {
|
|
9311
|
-
project = await
|
|
8474
|
+
project = await fs6.realpath(absolute);
|
|
9312
8475
|
} catch (cause) {
|
|
9313
|
-
throw
|
|
8476
|
+
throw invalid2(`\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${absolute}`, cause);
|
|
9314
8477
|
}
|
|
9315
|
-
const stat = await
|
|
9316
|
-
if (!stat.isDirectory()) throw
|
|
8478
|
+
const stat = await fs6.stat(project);
|
|
8479
|
+
if (!stat.isDirectory()) throw invalid2(`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${absolute}`);
|
|
9317
8480
|
return project;
|
|
9318
8481
|
}
|
|
9319
8482
|
function safeRelative(reference, label) {
|
|
9320
|
-
if (!reference ||
|
|
9321
|
-
throw
|
|
8483
|
+
if (!reference || path8.isAbsolute(reference) || /[\r\n\0]/.test(reference)) {
|
|
8484
|
+
throw invalid2(`${label}\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84`);
|
|
9322
8485
|
}
|
|
9323
|
-
const normalized =
|
|
8486
|
+
const normalized = path8.normalize(reference).split(path8.sep).join("/");
|
|
9324
8487
|
if (normalized === ".." || normalized.startsWith("../") || normalized === ".") {
|
|
9325
|
-
throw
|
|
8488
|
+
throw invalid2(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
9326
8489
|
}
|
|
9327
8490
|
return normalized.replace(/^\.\//, "");
|
|
9328
8491
|
}
|
|
@@ -9333,7 +8496,7 @@ function isRuntimeIdentity(value) {
|
|
|
9333
8496
|
}
|
|
9334
8497
|
function isExecutionPlan(value) {
|
|
9335
8498
|
if (!isRecord(value)) return false;
|
|
9336
|
-
return value.schemaVersion === 1 && value.contractVersion === EXECUTION_CONTRACT_VERSION && typeof value.planId === "string" && PLAN_ID_PATTERN.test(value.planId) && typeof value.createdAt === "string" && typeof value.expiresAt === "string" && isSha(value.nonceSha256) && isSha(value.
|
|
8499
|
+
return value.schemaVersion === 1 && value.contractVersion === EXECUTION_CONTRACT_VERSION && typeof value.planId === "string" && PLAN_ID_PATTERN.test(value.planId) && typeof value.createdAt === "string" && typeof value.expiresAt === "string" && isSha(value.nonceSha256) && isSha(value.skillsLockSha256) && isRuntimeIdentity(value.runtime) && isRecord(value.assessment) && isRecord(value.documents) && isBoundDocument(value.documents.change) && isBoundDocument(value.documents.rollback) && Array.isArray(value.payloads) && value.payloads.every(isBoundDocument) && typeof value.evidenceDir === "string";
|
|
9337
8500
|
}
|
|
9338
8501
|
function isBoundDocument(value) {
|
|
9339
8502
|
return isRecord(value) && typeof value.path === "string" && isSha(value.sha256);
|
|
@@ -9346,31 +8509,31 @@ function isSha(value) {
|
|
|
9346
8509
|
}
|
|
9347
8510
|
function parseTtl(value) {
|
|
9348
8511
|
if (value === void 0) return void 0;
|
|
9349
|
-
if (!/^\d+$/.test(value)) throw
|
|
8512
|
+
if (!/^\d+$/.test(value)) throw invalid2("--ttl-seconds \u5FC5\u987B\u662F\u6574\u6570");
|
|
9350
8513
|
return Number(value);
|
|
9351
8514
|
}
|
|
9352
8515
|
function canonicalJson(value) {
|
|
9353
8516
|
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
9354
8517
|
if (isRecord(value)) {
|
|
9355
|
-
return `{${Object.keys(value).sort().map((
|
|
8518
|
+
return `{${Object.keys(value).sort().map((key2) => `${JSON.stringify(key2)}:${canonicalJson(value[key2])}`).join(",")}}`;
|
|
9356
8519
|
}
|
|
9357
8520
|
return JSON.stringify(value) ?? "null";
|
|
9358
8521
|
}
|
|
9359
|
-
function
|
|
9360
|
-
return
|
|
8522
|
+
function sha2565(value) {
|
|
8523
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
9361
8524
|
}
|
|
9362
8525
|
async function sha256File2(file) {
|
|
9363
|
-
const hash =
|
|
8526
|
+
const hash = createHash5("sha256");
|
|
9364
8527
|
await new Promise((resolve4, reject) => {
|
|
9365
|
-
const stream =
|
|
8528
|
+
const stream = createReadStream(file);
|
|
9366
8529
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
9367
8530
|
stream.on("error", reject);
|
|
9368
8531
|
stream.on("end", resolve4);
|
|
9369
8532
|
});
|
|
9370
8533
|
return hash.digest("hex");
|
|
9371
8534
|
}
|
|
9372
|
-
function
|
|
9373
|
-
return new
|
|
8535
|
+
function invalid2(message, cause) {
|
|
8536
|
+
return new CliError20({ code: CliErrorCode19.INVALID_ARGUMENT, message, cause });
|
|
9374
8537
|
}
|
|
9375
8538
|
|
|
9376
8539
|
// src/commands/user.ts
|
|
@@ -9381,21 +8544,21 @@ import {
|
|
|
9381
8544
|
adminResetPassword,
|
|
9382
8545
|
formatObject as formatObject23,
|
|
9383
8546
|
fromAxiosError as fromAxiosError19,
|
|
9384
|
-
CliError as
|
|
9385
|
-
CliErrorCode as
|
|
8547
|
+
CliError as CliError21,
|
|
8548
|
+
CliErrorCode as CliErrorCode20
|
|
9386
8549
|
} from "@hcmai/sdk";
|
|
9387
8550
|
function buildAdminResetPasswordRequest(args, password) {
|
|
9388
8551
|
const hasId = Boolean(args.id);
|
|
9389
8552
|
const hasUsername = Boolean(args.username);
|
|
9390
8553
|
if (hasId === hasUsername) {
|
|
9391
|
-
throw new
|
|
9392
|
-
code:
|
|
8554
|
+
throw new CliError21({
|
|
8555
|
+
code: CliErrorCode20.INVALID_ARGUMENT,
|
|
9393
8556
|
message: "--id <uuid> \u4E0E --username <user> \u5FC5\u987B\u4E14\u53EA\u80FD\u63D0\u4F9B\u4E00\u4E2A"
|
|
9394
8557
|
});
|
|
9395
8558
|
}
|
|
9396
8559
|
if (!password) {
|
|
9397
|
-
throw new
|
|
9398
|
-
code:
|
|
8560
|
+
throw new CliError21({
|
|
8561
|
+
code: CliErrorCode20.INVALID_ARGUMENT,
|
|
9399
8562
|
message: "\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A"
|
|
9400
8563
|
});
|
|
9401
8564
|
}
|
|
@@ -9412,8 +8575,8 @@ async function userResetPasswordCommand(args) {
|
|
|
9412
8575
|
const password = args.passwordStdin ? await readSecretFromStdin() : await promptPasswordTwice();
|
|
9413
8576
|
const request = buildAdminResetPasswordRequest(args, password);
|
|
9414
8577
|
const client3 = HcmClient21.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
9415
|
-
const
|
|
9416
|
-
process.stdout.write(formatObject23(
|
|
8578
|
+
const result = await adminResetPassword(client3.raw(), request);
|
|
8579
|
+
process.stdout.write(formatObject23(result, { format: args.output ?? "json" }) + "\n");
|
|
9417
8580
|
} catch (cause) {
|
|
9418
8581
|
const err = cause;
|
|
9419
8582
|
if (err?.isAxiosError) exitWithError(fromAxiosError19(err, args.env));
|
|
@@ -9424,14 +8587,62 @@ async function promptPasswordTwice() {
|
|
|
9424
8587
|
const password = await promptSecret("\u65B0\u5BC6\u7801: ");
|
|
9425
8588
|
const confirmation = await promptSecret("\u518D\u6B21\u8F93\u5165\u65B0\u5BC6\u7801: ");
|
|
9426
8589
|
if (password !== confirmation) {
|
|
9427
|
-
throw new
|
|
9428
|
-
code:
|
|
8590
|
+
throw new CliError21({
|
|
8591
|
+
code: CliErrorCode20.INVALID_ARGUMENT,
|
|
9429
8592
|
message: "\u4E24\u6B21\u8F93\u5165\u7684\u65B0\u5BC6\u7801\u4E0D\u4E00\u81F4"
|
|
9430
8593
|
});
|
|
9431
8594
|
}
|
|
9432
8595
|
return password;
|
|
9433
8596
|
}
|
|
9434
8597
|
|
|
8598
|
+
// src/commands/models.ts
|
|
8599
|
+
init_exit();
|
|
8600
|
+
init_auth_guard();
|
|
8601
|
+
import {
|
|
8602
|
+
HcmClient as HcmClient22,
|
|
8603
|
+
formatObject as formatObject24,
|
|
8604
|
+
fromAxiosError as fromAxiosError20
|
|
8605
|
+
} from "@hcmai/sdk";
|
|
8606
|
+
async function modelsCommand(keyword, args) {
|
|
8607
|
+
try {
|
|
8608
|
+
const client3 = HcmClient22.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
8609
|
+
const all = await client3.listModels();
|
|
8610
|
+
const needle = keyword?.trim().toLowerCase();
|
|
8611
|
+
const rows = (needle ? all.filter((m) => JSON.stringify(m).toLowerCase().includes(needle)) : all).sort((a, b) => key(a).localeCompare(key(b)));
|
|
8612
|
+
const fmt = args.output ?? "table";
|
|
8613
|
+
if (fmt !== "table") {
|
|
8614
|
+
process.stdout.write(formatObject24({ total: rows.length, models: rows }, { format: fmt }) + "\n");
|
|
8615
|
+
process.exit(0);
|
|
8616
|
+
}
|
|
8617
|
+
if (rows.length === 0) {
|
|
8618
|
+
process.stdout.write(
|
|
8619
|
+
needle ? `\u6CA1\u6709\u5339\u914D "${keyword}" \u7684 Model\uFF08\u5171 ${all.length} \u4E2A\u53EF\u89C1\uFF09\u3002
|
|
8620
|
+
` : "\u5F53\u524D\u8EAB\u4EFD\u5728\u8FD9\u4E2A\u73AF\u5883\u770B\u4E0D\u5230\u4EFB\u4F55 Model\u3002\n"
|
|
8621
|
+
);
|
|
8622
|
+
process.exit(0);
|
|
8623
|
+
}
|
|
8624
|
+
const width = Math.max(...rows.map((m) => key(m).length), 8);
|
|
8625
|
+
process.stdout.write(`${"MODEL".padEnd(width)} TYPE
|
|
8626
|
+
`);
|
|
8627
|
+
for (const m of rows) {
|
|
8628
|
+
process.stdout.write(`${key(m).padEnd(width)} ${typeof m.type === "string" ? m.type : "-"}
|
|
8629
|
+
`);
|
|
8630
|
+
}
|
|
8631
|
+
process.stdout.write(`
|
|
8632
|
+
\u5171 ${rows.length} \u4E2A${needle ? `\uFF08\u5168\u90E8 ${all.length} \u4E2A\u4E2D\u5339\u914D\uFF09` : ""}\u3002
|
|
8633
|
+
`);
|
|
8634
|
+
process.stdout.write("\u4E0B\u4E00\u6B65\uFF1Ahcm describe <Model> \u770B\u5B57\u6BB5\u3001\u5173\u7CFB\u4E0E Action\u3002\n");
|
|
8635
|
+
process.exit(0);
|
|
8636
|
+
} catch (e) {
|
|
8637
|
+
const err = e;
|
|
8638
|
+
if (err?.isAxiosError) exitWithError(fromAxiosError20(err, args.profile));
|
|
8639
|
+
exitWithError(e);
|
|
8640
|
+
}
|
|
8641
|
+
}
|
|
8642
|
+
function key(m) {
|
|
8643
|
+
return typeof m.modelKey === "string" && m.modelKey ? m.modelKey : "(\u672A\u547D\u540D)";
|
|
8644
|
+
}
|
|
8645
|
+
|
|
9435
8646
|
// src/startup.ts
|
|
9436
8647
|
function needsLegacyProfileMigration(argv) {
|
|
9437
8648
|
const args = argv.slice(2);
|
|
@@ -9506,6 +8717,7 @@ program.command("action <Model.Action>").description("\u8C03\u7528 Model \u4E0A\
|
|
|
9506
8717
|
program.command("create <Model>").description("\u521B\u5EFA\u4E00\u6761 Model \u8BB0\u5F55\uFF08\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF09").option("--data <JSON>", `\u8BB0\u5F55\u5B57\u6BB5 JSON\uFF0C\u4F8B\u5982 '{"code":"ORG_SALES","name":"\u9500\u552E\u90E8"}'`).option("--data-file <path>", "\u4ECE UTF-8 JSON \u6587\u4EF6\u8BFB\u53D6\u8BB0\u5F55\u5B57\u6BB5\uFF08\u4E0E --data \u4E92\u65A5\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--profile <name>", "[deprecated] \u7528 --env \u66FF\u4EE3").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((m, opts) => createCommand(m, opts));
|
|
9507
8718
|
program.command("delete <Model>").description("\u5220\u9664\u4E00\u6761 Model \u8BB0\u5F55\uFF08\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF1B\u90E8\u5206 Model \u4F1A\u6267\u884C\u4E1A\u52A1\u64A4\u9500\uFF09").option("--id <id>", "\u76EE\u6807\u5B9E\u4F53 ID\uFF08\u5FC5\u586B\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--profile <name>", "[deprecated] \u7528 --env \u66FF\u4EE3").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((m, opts) => deleteCommand(m, opts));
|
|
9508
8719
|
program.command("import <path>").description("\u6CE8\u5165 DataPackage YAML \u6570\u636E\u96C6\uFF08\u76EE\u5F55\u6216\u5355\u6587\u4EF6\uFF0C\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF09").option("--dry-run", "\u53EA\u8DD1\u5BA2\u6237\u7AEF\u9759\u6001\u6821\u9A8C\uFF0C\u4E0D\u843D\u5E93\uFF08\u89C1 spec \xA78 \u9650\u5236\uFF09").option("--update-only", "\u5DF2\u5B58\u5728\uFF08\u6309 key_field\uFF09\u5219 PUT \u66F4\u65B0\uFF0C\u5426\u5219 create").option("--on-error <mode>", "stop | continue\uFF08\u9ED8\u8BA4 continue\uFF09", "continue").option("--report <path>", "\u5199 JSON \u6CE8\u5165\u62A5\u544A\uFF08\u9010\u884C\u7ED3\u679C + key\u2192id \u6620\u5C04\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--profile <name>", "[deprecated] \u7528 --env \u66FF\u4EE3").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((p, opts) => importCommand(p, opts));
|
|
8720
|
+
program.command("models [keyword]").description("\u5217\u51FA\u76EE\u6807\u73AF\u5883\u6709\u54EA\u4E9B Model\uFF08describe \u7684\u4E0A\u4E00\u6B65\uFF09\uFF1B\u53EF\u5E26\u5173\u952E\u5B57\u8FC7\u6EE4").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((keyword, opts) => modelsCommand(keyword, opts));
|
|
9509
8721
|
program.command("describe <Model>").description("\u67E5\u770B Model \u5143\u6570\u636E\uFF08\u5B57\u6BB5\u3001\u5173\u7CFB\u3001Action \u5217\u8868\uFF09").option("-v, --verbose", "\u663E\u793A\u5B57\u6BB5\u63CF\u8FF0\u4E0E enum \u53D6\u503C").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--profile <name>", "[deprecated] \u7528 --env \u66FF\u4EE3").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((m, opts) => describeCommand(m, opts));
|
|
9510
8722
|
program.command("chat [prompt]").description("\u4E0E\u6570\u5B57\u5458\u5DE5 Moirai \u5BF9\u8BDD\uFF1B\u6709 prompt \u5355\u53D1\u540E\u9000\u51FA\uFF0C\u4E0D\u5E26 prompt \u8FDB\u5165 REPL").option("--agent <key>", "\u6307\u5B9A agent UUID\uFF08\u9ED8\u8BA4\u4F7F\u7528 analyst \u5185\u7F6E agent\uFF09").option("--resume <id>", "\u7EED\u63A5\u6307\u5B9A conversation\uFF1B\u4E0D\u4F20\u5219\u9ED8\u8BA4\u5F00\u65B0\u4F1A\u8BDD").option("--conversation <id>", "[deprecated] \u7528 --resume \u66FF\u4EE3\uFF1B\u4F20 NEW \u7B49\u4EF7\u9ED8\u8BA4\u65B0\u4F1A\u8BDD").option("--list", "\u5217\u51FA\u6700\u8FD1\u4F1A\u8BDD\uFF0C\u4E0D\u8FDB\u5165\u804A\u5929").option("--events [conversationId]", "\u53EA\u8BFB\u67E5\u770B\u6307\u5B9A conversation \u7684 timeline/event \u6D41\uFF1B\u4E5F\u53EF\u914D --resume <id>").option("--limit <N>", "\u914D\u5408 --list\uFF1A\u663E\u793A\u6700\u8FD1 N \u6761\u4F1A\u8BDD", "10").option("--stream", "\u6D41\u5F0F\u8F93\u51FA\uFF08\u9ED8\u8BA4\u5F00\u542F\uFF0C\u9010 token \u6253\u5370 + thinking/tools \u5B9E\u65F6\u663E\u793A\uFF09", true).option("--no-stream", "\u5173\u95ED\u6D41\u5F0F\uFF0C\u7B49 LLM \u5168\u6587\u8FD4\u56DE\u540E\u4E00\u6B21\u6027\u663E\u793A\uFF08CI / \u811A\u672C\u7BA1\u9053\u573A\u666F\uFF09").option("--show-tool-calls", "\u5C55\u793A\u5DE5\u5177\u8C03\u7528\u8BE6\u7EC6\u53C2\u6570\uFF08\u9ED8\u8BA4\u663E\u793A\u5DE5\u5177\u540D + \u72B6\u6001\uFF0Cverbose \u65F6\u663E\u793A\u5B8C\u6574\u53C2\u6570\uFF09", true).option("--no-show-tool-calls", "\u5B8C\u5168\u9690\u85CF\u5DE5\u5177\u8C03\u7528").option("--prompt-file <path>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6 prompt \u5185\u5BB9").option("--timeout <sec>", "\u8BF7\u6C42\u8D85\u65F6\u79D2\u6570", "60").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--profile <name>", "[deprecated] \u7528 --env \u66FF\u4EE3").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atext | json", "text").option("--no-markdown", "\u5173\u95ED\u6D41\u5F0F\u672B\u6001 markdown \u6E32\u67D3\uFF08CI / pipe \u53CB\u597D\uFF09").option("--no-thinking", "\u5173\u95ED thinking \u6BB5\u663E\u793A").option("--verbose-thinking", "\u5C55\u5F00 thinking \u5B8C\u6574\u5185\u5BB9\uFF08\u9ED8\u8BA4\u4EC5\u5355\u884C\u603B\u7ED3\uFF09").option("--verbose-tools", "\u5C55\u793A\u5DE5\u5177\u8C03\u7528\u5B8C\u6574\u53C2\u6570\uFF08\u9ED8\u8BA4\u4EC5\u5DE5\u5177\u540D + \u72B6\u6001\uFF09").option("--no-spinner", "\u5173\u95ED spinner\uFF08CI \u53CB\u597D\uFF09").option("--no-color", "\u5173\u95ED\u989C\u8272\uFF08CI / accessibility\uFF09").option("--raw", "\u9010\u884C\u6253\u5370\u539F\u59CB v4/control envelope\uFF08\u534F\u8BAE\u793A\u6CE2\u5668\uFF0C\u4E0D\u6E32\u67D3\uFF09").option("--auto-approve", "\u81EA\u52A8\u6279\u51C6\u9AD8\u98CE\u9669 confirm\uFF08\u4ECD\u6253\u5370\u5BA1\u8BA1\u884C\uFF1B\u6279\u91CF / \u975E\u4EA4\u4E92\u573A\u666F\uFF09").option("--on-confirm <decision>", "\u975E\u4EA4\u4E92 confirm \u51B3\u7B56\uFF1Aapprove | reject | reject:<reason>\uFF08\u4F18\u5148\u4E8E --auto-approve\uFF09").option("--on-ask <answer>", "\u975E\u4EA4\u4E92 ASK \u5E94\u7B54\u6587\u672C\uFF08\u547D\u4E2D choices \u65F6\u81EA\u52A8\u56DE\u586B choiceIndex\uFF09").option(
|
|
9511
8723
|
"--inject-on <spec>",
|
|
@@ -9562,7 +8774,6 @@ skills.command("list [keyword]").description("\u5217\u51FA\u76EE\u6807\u73AF\u58
|
|
|
9562
8774
|
skills.command("manifest").description("\u6253\u5370\u672C CLI \u7684\u547D\u4EE4\u9762\u4E0E\u81EA\u5E26\u6280\u80FD\u6E05\u5355\uFF08\u4E0D\u8FDE\u7F51\uFF0C\u4F9B\u5B88\u536B\u4E0E\u5916\u90E8\u5DE5\u5177\u6BD4\u5BF9\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => skillsManifestCommand(program, opts));
|
|
9563
8775
|
skills.command("show <id>").description("\u6253\u5370\u6280\u80FD\u539F\u6587\uFF08\u7BA1\u9053\u53CB\u597D\uFF09\uFF1B\u81EA\u5E26\u6280\u80FD\u76F4\u63A5\u8BFB\u672C\u5730\uFF0C\u4E0D\u8FDE\u7F51").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").action((id, opts) => skillsShowCommand(id, opts));
|
|
9564
8776
|
skills.command("install [id]").description("\u8FDE\u540C requires \u524D\u7F6E\u6280\u80FD\u4E0E\u53C2\u8003\u6587\u4EF6\u88C5\u5230 agent \u6280\u80FD\u76EE\u5F55\uFF1B--all \u88C5\u6574\u4E2A\u6280\u80FD\u5E93").option("--all", "\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D\uFF08\u5F80\u540E update \u4F1A\u81EA\u52A8\u8865\u4E0A\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\uFF09").option("--target <agent>", "\u88C5\u7ED9\u54EA\u4E2A agent\uFF1Aclaude\uFF08.claude/skills/\uFF09| codex\uFF08hcm-skills/ + AGENTS.md\uFF09| auto", "auto").option("--global", "\u88C5\u5230\u7528\u6237\u7EA7\u76EE\u5F55\u800C\u975E\u5F53\u524D\u9879\u76EE").option("--dir <path>", "\u88C5\u5230\u6307\u5B9A\u76EE\u5F55").option("--from <dir>", "\u4ECE\u672C\u5730\u76EE\u5F55\u88C5\uFF08\u73B0\u573A\u6539\u8FC7\u7684\u7248\u672C\uFF09\uFF0C\u4E0D\u8D70\u7F51\u7EDC").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u540C\u540D\u6280\u80FD").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((id, opts) => skillsInstallCommand(id, opts));
|
|
9565
|
-
skills.command("sync <id>").description("\u4ECE\u76EE\u6807\u5B9E\u4F8B\u540C\u6B65 Skill \u95ED\u5305\u5230 WorkBuddy \u9879\u76EE\u5E76\u5199 chiron.lock").requiredOption("--target <agent>", "\u76EE\u6807 Agent \u5BBF\u4E3B\uFF1B\u5F53\u524D\u652F\u6301 workbuddy").requiredOption("--project <path>", "WorkBuddy \u9879\u76EE\u6839\u76EE\u5F55").option("--cli <path>", "\u663E\u5F0F\u6307\u5B9A\u8981\u9501\u5B9A\u5230\u9879\u76EE\u7684 HCM CLI\uFF1B\u9ED8\u8BA4\u4F7F\u7528\u5F53\u524D\u8FD9\u4EFD CLI").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((id, opts) => skillsSyncCommand(id, opts));
|
|
9566
8777
|
skills.command("update").description("\u628A\u88C5\u8FC7\u7684\u6280\u80FD\u5347\u5230\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\uFF08\u4EA7\u54C1\u5347\u7EA7\u540E\u8DD1\u5B83\uFF09").option("--all", "\u8FDE\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4E00\u8D77\u88C5\u4E0A\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D").option("--check", "\u53EA\u770B\u4F1A\u53D8\u4EC0\u4E48\uFF0C\u4E0D\u5199\u76D8").option("--target <agent>", "claude | codex | auto\uFF08\u9ED8\u8BA4\u6309\u5DF2\u5B89\u88C5\u76EE\u5F55\u63A2\uFF09", "auto").option("--global", "\u5347\u7EA7\u7528\u6237\u7EA7\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--dir <path>", "\u5347\u7EA7\u6307\u5B9A\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--force", "\u8FDE\u672C\u5730\u6539\u8FC7\u7684\u6280\u80FD\u4E00\u8D77\u8986\u76D6\u6210\u4EA7\u54C1\u8FD9\u4E00\u7248").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6\u5B89\u88C5\u65F6\u8BB0\u5F55\u7684\u5730\u5740\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((opts) => skillsUpdateCommand(opts));
|
|
9567
8778
|
program.command("update <Model>").description("\u66F4\u65B0\u4E00\u6761 Model \u8BB0\u5F55\uFF08\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF09").requiredOption("--id <id>", "\u8BB0\u5F55 ID").option("--data <JSON>", `\u8981\u66F4\u65B0\u7684\u5B57\u6BB5 JSON\uFF0C\u4F8B\u5982 '{"name":"\u65B0\u540D\u79F0"}'`).option("--data-file <path>", "\u4ECE UTF-8 JSON \u6587\u4EF6\u8BFB\u53D6\u66F4\u65B0\u5B57\u6BB5\uFF08\u4E0E --data \u4E92\u65A5\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((model, opts) => updateCommand(model, opts));
|
|
9568
8779
|
program.command("version").description("\u67E5\u540E\u7AEF\u7248\u672C\uFF08commit / tag / \u6784\u5EFA\u65F6\u95F4\uFF09\u2014\u2014\u514D\u8BA4\u8BC1\uFF0C\u65E0\u9700\u5148 login").option("--endpoint <url>", "\u540E\u7AEF\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => versionCommand(opts));
|