@hcmai/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/index.js +2050 -262
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -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(path7) {
|
|
527
|
+
const segs = path7.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";
|
|
@@ -1458,10 +1458,10 @@ function ReplApp(props) {
|
|
|
1458
1458
|
});
|
|
1459
1459
|
const t0 = Date.now();
|
|
1460
1460
|
try {
|
|
1461
|
-
const
|
|
1461
|
+
const result2 = await presenter.consume(handle.events);
|
|
1462
1462
|
const stats = presenter.completionStats;
|
|
1463
|
-
if (
|
|
1464
|
-
bridge.showCompleted(Date.now() - t0,
|
|
1463
|
+
if (result2.status === "completed") {
|
|
1464
|
+
bridge.showCompleted(Date.now() - t0, result2.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) {
|
|
@@ -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(path7) {
|
|
1710
|
+
const segs = (path7 ?? "").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
|
}
|
|
@@ -2639,12 +2639,12 @@ async function queryCommand(model, args) {
|
|
|
2639
2639
|
if (args.offset) dsl.offset = Number(args.offset);
|
|
2640
2640
|
if (!dsl.limit) dsl.limit = 20;
|
|
2641
2641
|
const client3 = HcmClient.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2642
|
-
const
|
|
2642
|
+
const result2 = await client3.query(model, dsl);
|
|
2643
2643
|
const fmt = args.output ?? "table";
|
|
2644
|
-
const out = formatRows(
|
|
2644
|
+
const out = formatRows(result2.rows, { format: fmt });
|
|
2645
2645
|
process.stdout.write(out + "\n");
|
|
2646
|
-
if (fmt === "table" &&
|
|
2647
|
-
process.stdout.write(`Showing ${
|
|
2646
|
+
if (fmt === "table" && result2.total !== void 0) {
|
|
2647
|
+
process.stdout.write(`Showing ${result2.rows.length} of ${result2.total} results.
|
|
2648
2648
|
`);
|
|
2649
2649
|
}
|
|
2650
2650
|
process.exit(0);
|
|
@@ -2709,17 +2709,17 @@ async function actionCommand(spec, args) {
|
|
|
2709
2709
|
}
|
|
2710
2710
|
}
|
|
2711
2711
|
const client3 = HcmClient2.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2712
|
-
const
|
|
2712
|
+
const result2 = await client3.action(model, action, input);
|
|
2713
2713
|
const fmt = args.output ?? "json";
|
|
2714
|
-
if (fmt !== "json" &&
|
|
2714
|
+
if (fmt !== "json" && result2.taskId) {
|
|
2715
2715
|
process.stdout.write(
|
|
2716
|
-
`\u23F3 task created: ${
|
|
2716
|
+
`\u23F3 task created: ${result2.taskId} (use \`hcm task ${result2.taskId}\` to follow up \u2014 coming Phase 1)
|
|
2717
2717
|
`
|
|
2718
2718
|
);
|
|
2719
2719
|
} else if (fmt !== "json") {
|
|
2720
2720
|
process.stdout.write("\u2705 action succeeded\n");
|
|
2721
2721
|
}
|
|
2722
|
-
process.stdout.write(formatObject(
|
|
2722
|
+
process.stdout.write(formatObject(result2, { format: fmt }) + "\n");
|
|
2723
2723
|
process.exit(0);
|
|
2724
2724
|
} catch (e) {
|
|
2725
2725
|
const err = e;
|
|
@@ -2808,11 +2808,11 @@ async function updateCommand(model, args) {
|
|
|
2808
2808
|
});
|
|
2809
2809
|
}
|
|
2810
2810
|
const client3 = HcmClient4.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
2811
|
-
const
|
|
2811
|
+
const result2 = await client3.update(model, args.id, data);
|
|
2812
2812
|
const fmt = args.output ?? "json";
|
|
2813
2813
|
if (fmt !== "json") process.stdout.write(`\u2705 ${model}#${args.id} \u5DF2\u66F4\u65B0
|
|
2814
2814
|
`);
|
|
2815
|
-
process.stdout.write(formatObject3(
|
|
2815
|
+
process.stdout.write(formatObject3(result2, { format: fmt }) + "\n");
|
|
2816
2816
|
process.exit(0);
|
|
2817
2817
|
} catch (e) {
|
|
2818
2818
|
const err = e;
|
|
@@ -2893,11 +2893,11 @@ async function settingSetCommand(domain, args) {
|
|
|
2893
2893
|
process.exit(0);
|
|
2894
2894
|
}
|
|
2895
2895
|
const c = await client(args);
|
|
2896
|
-
const
|
|
2896
|
+
const result2 = await patchSettingDomain(c.raw(), domain, items);
|
|
2897
2897
|
const fmt = args.output ?? "json";
|
|
2898
2898
|
if (fmt !== "json") process.stdout.write(`\u2705 ${domain} \u5DF2\u66F4\u65B0 ${items.length} \u9879
|
|
2899
2899
|
`);
|
|
2900
|
-
process.stdout.write(formatObject4(
|
|
2900
|
+
process.stdout.write(formatObject4(result2, { format: fmt }) + "\n");
|
|
2901
2901
|
process.exit(0);
|
|
2902
2902
|
} catch (e) {
|
|
2903
2903
|
fail(e, args);
|
|
@@ -2914,11 +2914,11 @@ async function settingResetCommand(domain, namespace, key, args) {
|
|
|
2914
2914
|
}
|
|
2915
2915
|
const c = await client(args);
|
|
2916
2916
|
const revision = args.revision === void 0 ? void 0 : Number(args.revision);
|
|
2917
|
-
const
|
|
2917
|
+
const result2 = await resetSettingItem(c.raw(), domain, namespace, key, revision);
|
|
2918
2918
|
const fmt = args.output ?? "json";
|
|
2919
2919
|
if (fmt !== "json") process.stdout.write(`\u2705 ${namespace}.${key} \u5DF2\u56DE\u843D\u5230\u9ED8\u8BA4\u503C
|
|
2920
2920
|
`);
|
|
2921
|
-
process.stdout.write(formatObject4(
|
|
2921
|
+
process.stdout.write(formatObject4(result2, { format: fmt }) + "\n");
|
|
2922
2922
|
process.exit(0);
|
|
2923
2923
|
} catch (e) {
|
|
2924
2924
|
fail(e, args);
|
|
@@ -3011,8 +3011,8 @@ import {
|
|
|
3011
3011
|
formatObject as formatObject7,
|
|
3012
3012
|
fromAxiosError as fromAxiosError8
|
|
3013
3013
|
} from "@hcmai/sdk";
|
|
3014
|
-
function collectYmlFiles(
|
|
3015
|
-
if (statSync(
|
|
3014
|
+
function collectYmlFiles(path7) {
|
|
3015
|
+
if (statSync(path7).isFile()) return [path7];
|
|
3016
3016
|
const out = [];
|
|
3017
3017
|
const walk = (dir) => {
|
|
3018
3018
|
for (const name of readdirSync(dir).sort()) {
|
|
@@ -3021,7 +3021,7 @@ function collectYmlFiles(path4) {
|
|
|
3021
3021
|
else if (full.endsWith(".yml") || full.endsWith(".yaml")) out.push(full);
|
|
3022
3022
|
}
|
|
3023
3023
|
};
|
|
3024
|
-
walk(
|
|
3024
|
+
walk(path7);
|
|
3025
3025
|
return out.sort();
|
|
3026
3026
|
}
|
|
3027
3027
|
var DRY_RUN_CLIENT = {
|
|
@@ -3029,9 +3029,9 @@ var DRY_RUN_CLIENT = {
|
|
|
3029
3029
|
create: async () => ({ id: "" }),
|
|
3030
3030
|
update: async () => ({})
|
|
3031
3031
|
};
|
|
3032
|
-
async function importCommand(
|
|
3032
|
+
async function importCommand(path7, args) {
|
|
3033
3033
|
try {
|
|
3034
|
-
const files = collectYmlFiles(
|
|
3034
|
+
const files = collectYmlFiles(path7);
|
|
3035
3035
|
const fixtures = files.map(
|
|
3036
3036
|
(f) => parseFixture(readFileSync2(f, "utf8"), relative(process.cwd(), f))
|
|
3037
3037
|
);
|
|
@@ -3041,16 +3041,16 @@ async function importCommand(path4, args) {
|
|
|
3041
3041
|
onError: args.onError === "stop" ? "stop" : "continue"
|
|
3042
3042
|
};
|
|
3043
3043
|
const client3 = opts.dryRun ? DRY_RUN_CLIENT : HcmClient7.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
3044
|
-
const
|
|
3045
|
-
if (args.report) writeFileSync(args.report, JSON.stringify(
|
|
3044
|
+
const result2 = await runImport(client3, fixtures, opts);
|
|
3045
|
+
if (args.report) writeFileSync(args.report, JSON.stringify(result2, null, 2));
|
|
3046
3046
|
const fmt = args.output ?? "json";
|
|
3047
3047
|
const tag = opts.dryRun ? "\u{1F50D} dry-run" : "\u{1F4E6} import";
|
|
3048
3048
|
process.stdout.write(
|
|
3049
|
-
`${tag}: created=${
|
|
3049
|
+
`${tag}: created=${result2.created} updated=${result2.updated} skipped=${result2.skipped} failed=${result2.failed}
|
|
3050
3050
|
`
|
|
3051
3051
|
);
|
|
3052
|
-
process.stdout.write(formatObject7(
|
|
3053
|
-
process.exit(
|
|
3052
|
+
process.stdout.write(formatObject7(result2, { format: fmt }) + "\n");
|
|
3053
|
+
process.exit(result2.failed > 0 ? 3 : 0);
|
|
3054
3054
|
} catch (e) {
|
|
3055
3055
|
const err = e;
|
|
3056
3056
|
if (err?.isAxiosError) exitWithError(fromAxiosError8(err, args.profile));
|
|
@@ -3333,13 +3333,13 @@ async function printConversationList(args) {
|
|
|
3333
3333
|
const ctx = await getActiveAuthContextFromCli(args);
|
|
3334
3334
|
const client3 = HcmClient10.fromAuthContext(ctx);
|
|
3335
3335
|
const limit = parsePositiveInt(args.limit, 10, "--limit");
|
|
3336
|
-
const
|
|
3336
|
+
const result2 = await fetchRecentConversations(client3.raw(), limit);
|
|
3337
3337
|
const fmt = args.output ?? "text";
|
|
3338
3338
|
if (fmt === "json") {
|
|
3339
|
-
process.stdout.write(formatObject9(
|
|
3339
|
+
process.stdout.write(formatObject9(result2, { format: "json" }) + "\n");
|
|
3340
3340
|
return;
|
|
3341
3341
|
}
|
|
3342
|
-
const rows =
|
|
3342
|
+
const rows = result2.rows.map((row) => ({
|
|
3343
3343
|
UPDATED: formatShortDate(row.lastActiveAt ?? row.updateTime ?? row.createTime),
|
|
3344
3344
|
MSGS: row.messageCount ?? 0,
|
|
3345
3345
|
TOKENS: row.totalTokens ?? 0,
|
|
@@ -3347,8 +3347,8 @@ async function printConversationList(args) {
|
|
|
3347
3347
|
CONVERSATION: row.id ?? "-"
|
|
3348
3348
|
}));
|
|
3349
3349
|
process.stdout.write(formatRows3(rows, { format: "table" }) + "\n");
|
|
3350
|
-
if (
|
|
3351
|
-
process.stdout.write(`Showing ${
|
|
3350
|
+
if (result2.total !== void 0) {
|
|
3351
|
+
process.stdout.write(`Showing ${result2.rows.length} of ${result2.total} conversations.
|
|
3352
3352
|
`);
|
|
3353
3353
|
}
|
|
3354
3354
|
}
|
|
@@ -3620,9 +3620,9 @@ async function chatCommand(prompt, args) {
|
|
|
3620
3620
|
process.once("SIGINT", sigintHandler);
|
|
3621
3621
|
const t0 = Date.now();
|
|
3622
3622
|
try {
|
|
3623
|
-
const
|
|
3623
|
+
const result2 = await presenter.consume(handle.events);
|
|
3624
3624
|
const stats = presenter.completionStats;
|
|
3625
|
-
renderer.showCompleted(Date.now() - t0,
|
|
3625
|
+
renderer.showCompleted(Date.now() - t0, result2.traceId, handle.conversationId, presenter.tokenCount || void 0, {
|
|
3626
3626
|
inputTokens: stats.inputTokens,
|
|
3627
3627
|
outputTokens: stats.outputTokens,
|
|
3628
3628
|
toolCount: stats.toolCount,
|
|
@@ -3631,8 +3631,8 @@ async function chatCommand(prompt, args) {
|
|
|
3631
3631
|
await ws.close();
|
|
3632
3632
|
await saveConversationState2(stateKey, {
|
|
3633
3633
|
lastConversationId: handle.conversationId,
|
|
3634
|
-
lastStreamId:
|
|
3635
|
-
lastTraceId:
|
|
3634
|
+
lastStreamId: result2.streamId,
|
|
3635
|
+
lastTraceId: result2.traceId ?? void 0,
|
|
3636
3636
|
lastAgentKey: args.agent,
|
|
3637
3637
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
3638
3638
|
});
|
|
@@ -3644,7 +3644,7 @@ async function chatCommand(prompt, args) {
|
|
|
3644
3644
|
resolve3();
|
|
3645
3645
|
}
|
|
3646
3646
|
});
|
|
3647
|
-
process.exit(
|
|
3647
|
+
process.exit(result2.status === "interrupted" ? 130 : 0);
|
|
3648
3648
|
} catch (e) {
|
|
3649
3649
|
process.removeListener("SIGINT", sigintHandler);
|
|
3650
3650
|
await ws.close();
|
|
@@ -4136,10 +4136,10 @@ async function withClient(args, fn) {
|
|
|
4136
4136
|
const client3 = HcmClient13.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
4137
4137
|
return fn(client3);
|
|
4138
4138
|
}
|
|
4139
|
-
function emit(
|
|
4139
|
+
function emit(result2, args, human) {
|
|
4140
4140
|
const fmt = args.output ?? "json";
|
|
4141
4141
|
if (fmt !== "json") process.stdout.write(human + "\n");
|
|
4142
|
-
process.stdout.write(formatObject11(
|
|
4142
|
+
process.stdout.write(formatObject11(result2, { format: fmt }) + "\n");
|
|
4143
4143
|
process.exit(0);
|
|
4144
4144
|
}
|
|
4145
4145
|
function fail2(e, args) {
|
|
@@ -4158,20 +4158,20 @@ async function cacheStatsCommand(model, args) {
|
|
|
4158
4158
|
}
|
|
4159
4159
|
async function cacheClearCommand(model, args) {
|
|
4160
4160
|
try {
|
|
4161
|
-
const
|
|
4162
|
-
emit(
|
|
4161
|
+
const result2 = await withClient(args, (c) => clearModelCache(c.raw(), model, args.id));
|
|
4162
|
+
emit(result2, args, args.id ? `\u{1F9F9} \u5DF2\u6E05 ${model}#${args.id} \u7F13\u5B58` : `\u{1F9F9} \u5DF2\u6E05 ${model} \u5168\u90E8\u5B9E\u4F53\u7F13\u5B58`);
|
|
4163
4163
|
} catch (e) {
|
|
4164
4164
|
fail2(e, args);
|
|
4165
4165
|
}
|
|
4166
4166
|
}
|
|
4167
4167
|
async function cacheClearMetaCommand(model, args) {
|
|
4168
4168
|
try {
|
|
4169
|
-
const
|
|
4169
|
+
const result2 = await withClient(
|
|
4170
4170
|
args,
|
|
4171
4171
|
(c) => clearMetaCache(c.raw(), model, { type: args.type, state: args.state })
|
|
4172
4172
|
);
|
|
4173
4173
|
const scope = args.type ? `${args.type}${args.state ? `.${args.state}` : ""}` : "\u5168\u90E8";
|
|
4174
|
-
emit(
|
|
4174
|
+
emit(result2, args, `\u{1F9F9} \u5DF2\u6E05 ${model} \u5143\u6570\u636E\u7F13\u5B58\uFF08${scope}\uFF09`);
|
|
4175
4175
|
} catch (e) {
|
|
4176
4176
|
fail2(e, args);
|
|
4177
4177
|
}
|
|
@@ -4224,19 +4224,23 @@ async function uploadCommand(filePath, args) {
|
|
|
4224
4224
|
init_exit();
|
|
4225
4225
|
import { promises as fsp } from "fs";
|
|
4226
4226
|
import os2 from "os";
|
|
4227
|
-
import
|
|
4228
|
-
import { createHash } from "crypto";
|
|
4227
|
+
import path4 from "path";
|
|
4228
|
+
import { createHash as createHash3 } from "crypto";
|
|
4229
4229
|
import {
|
|
4230
4230
|
createHttpClient as createHttpClient2,
|
|
4231
4231
|
fetchSkillCatalog,
|
|
4232
|
+
fetchSkillFile,
|
|
4232
4233
|
fetchSkillMarkdown,
|
|
4233
4234
|
fetchSkillReference,
|
|
4234
4235
|
resolveChironBase,
|
|
4235
|
-
assertSafeSkillId,
|
|
4236
|
+
assertSafeSkillId as assertSafeSkillId2,
|
|
4236
4237
|
assertSafeReferencePath,
|
|
4237
4238
|
normalizeReferences,
|
|
4238
4239
|
parseSkillRequirements,
|
|
4239
|
-
|
|
4240
|
+
parseSkillFrontmatter,
|
|
4241
|
+
resolveSkillInstallOrder as resolveSkillInstallOrder2,
|
|
4242
|
+
resolveAllSkillsInstallOrder,
|
|
4243
|
+
flattenSkills,
|
|
4240
4244
|
matchSkills,
|
|
4241
4245
|
filterByStage,
|
|
4242
4246
|
formatObject as formatObject13,
|
|
@@ -4244,12 +4248,796 @@ import {
|
|
|
4244
4248
|
loadEnv as loadEnv5,
|
|
4245
4249
|
loadGlobalConfig as loadGlobalConfig6,
|
|
4246
4250
|
resolveActiveEnv as resolveActiveEnv3,
|
|
4251
|
+
CliError as CliError16,
|
|
4252
|
+
CliErrorCode as CliErrorCode15
|
|
4253
|
+
} from "@hcmai/sdk";
|
|
4254
|
+
|
|
4255
|
+
// src/commands/skills-sync.ts
|
|
4256
|
+
import { createHash } from "crypto";
|
|
4257
|
+
import { promises as fs4 } from "fs";
|
|
4258
|
+
import path2 from "path";
|
|
4259
|
+
import {
|
|
4247
4260
|
CliError as CliError14,
|
|
4248
|
-
CliErrorCode as CliErrorCode13
|
|
4261
|
+
CliErrorCode as CliErrorCode13,
|
|
4262
|
+
assertSafeSkillFilePath,
|
|
4263
|
+
assertSafeSkillId,
|
|
4264
|
+
resolveSkillInstallOrder
|
|
4249
4265
|
} from "@hcmai/sdk";
|
|
4266
|
+
async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fetchFile) {
|
|
4267
|
+
const order = resolveSkillInstallOrder(catalog, requestedSkill);
|
|
4268
|
+
for (const skill of order) {
|
|
4269
|
+
if (skill.actionSurface !== "cli") {
|
|
4270
|
+
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`);
|
|
4271
|
+
}
|
|
4272
|
+
}
|
|
4273
|
+
return Promise.all(
|
|
4274
|
+
order.map(async (skill) => {
|
|
4275
|
+
let files;
|
|
4276
|
+
if (skill.files === void 0) {
|
|
4277
|
+
const markdown2 = await fetchMarkdown(skill.id);
|
|
4278
|
+
const content = Buffer.from(markdown2, "utf8");
|
|
4279
|
+
files = [
|
|
4280
|
+
{
|
|
4281
|
+
path: "SKILL.md",
|
|
4282
|
+
content,
|
|
4283
|
+
bytes: content.length,
|
|
4284
|
+
sha256: sha256(content)
|
|
4285
|
+
}
|
|
4286
|
+
];
|
|
4287
|
+
} else {
|
|
4288
|
+
if (!Array.isArray(skill.files) || skill.files.length === 0) {
|
|
4289
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u76EE\u5F55\u4E3A\u7A7A`);
|
|
4290
|
+
}
|
|
4291
|
+
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`);
|
|
4292
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4293
|
+
for (const file of skill.files) {
|
|
4294
|
+
assertSafeSkillFilePath(file.path);
|
|
4295
|
+
if (seen.has(file.path))
|
|
4296
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u5305\u542B\u91CD\u590D\u8DEF\u5F84: ${file.path}`);
|
|
4297
|
+
seen.add(file.path);
|
|
4298
|
+
if (!Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4299
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684\u6587\u4EF6\u76EE\u5F55\u5143\u6570\u636E\u975E\u6CD5: ${file.path}`);
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
if (!seen.has("SKILL.md")) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4303
|
+
files = await Promise.all(
|
|
4304
|
+
skill.files.map(async (file) => {
|
|
4305
|
+
const content = Buffer.from(await fetchFile(skill.id, file.path));
|
|
4306
|
+
if (content.length !== file.bytes) {
|
|
4307
|
+
throw invalid(
|
|
4308
|
+
`\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}`
|
|
4309
|
+
);
|
|
4310
|
+
}
|
|
4311
|
+
const digest = sha256(content);
|
|
4312
|
+
if (digest !== file.sha256) {
|
|
4313
|
+
throw invalid(`\u6280\u80FD ${skill.id}/${file.path} \u6458\u8981\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4`);
|
|
4314
|
+
}
|
|
4315
|
+
return { path: file.path, content, bytes: content.length, sha256: digest };
|
|
4316
|
+
})
|
|
4317
|
+
);
|
|
4318
|
+
}
|
|
4319
|
+
const root = files.find((file) => file.path === "SKILL.md");
|
|
4320
|
+
if (!root) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4321
|
+
const markdown = root.content.toString("utf8");
|
|
4322
|
+
if (!markdown.trim()) throw invalid(`\u6280\u80FD ${skill.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4323
|
+
validateWorkBuddyFrontmatter(markdown, skill.id);
|
|
4324
|
+
const bytes = root.bytes;
|
|
4325
|
+
if (Number.isFinite(skill.bytes) && skill.bytes > 0 && skill.bytes !== bytes) {
|
|
4326
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${skill.bytes}\uFF0C\u5B9E\u9645=${bytes}`);
|
|
4327
|
+
}
|
|
4328
|
+
return { id: skill.id, markdown, bytes, sha256: root.sha256, files };
|
|
4329
|
+
})
|
|
4330
|
+
);
|
|
4331
|
+
}
|
|
4332
|
+
async function syncWorkBuddyProject(input) {
|
|
4333
|
+
const project = path2.resolve(input.project);
|
|
4334
|
+
const metaDir = path2.join(project, ".hcm-delivery");
|
|
4335
|
+
const skillsDir = path2.join(project, ".codebuddy", "skills");
|
|
4336
|
+
const lockPath = path2.join(metaDir, "chiron.lock.json");
|
|
4337
|
+
assertSafeSkillId(input.requestedSkill);
|
|
4338
|
+
validatePreparedDocuments(input.documents, input.requestedSkill);
|
|
4339
|
+
validatePreparedRuntime(input.runtime);
|
|
4340
|
+
await recoverInterruptedSyncs(project, metaDir, lockPath);
|
|
4341
|
+
const oldLock = await readLock(lockPath);
|
|
4342
|
+
const skillEntries = input.documents.flatMap(
|
|
4343
|
+
(document) => document.files.map((file) => [ownedFile(document.id, file.path), file])
|
|
4344
|
+
);
|
|
4345
|
+
const runtimeEntries = input.runtime.files.map((file) => [file.path, file]);
|
|
4346
|
+
const desiredEntries = [
|
|
4347
|
+
...skillEntries,
|
|
4348
|
+
...runtimeEntries
|
|
4349
|
+
];
|
|
4350
|
+
const desiredFiles = desiredEntries.map(([file]) => file);
|
|
4351
|
+
const desiredByFile = new Map(desiredEntries);
|
|
4352
|
+
const oldOwned = new Set(oldLock?.ownedFiles ?? []);
|
|
4353
|
+
const oldDigestByFile = lockDigests(oldLock);
|
|
4354
|
+
for (const file of oldOwned) validateOwnedFile(file);
|
|
4355
|
+
for (const file of desiredFiles) {
|
|
4356
|
+
const absolute = path2.join(project, file);
|
|
4357
|
+
const current = await readOptionalBytes(absolute);
|
|
4358
|
+
if (current !== void 0 && !oldOwned.has(file)) {
|
|
4359
|
+
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}`);
|
|
4360
|
+
}
|
|
4361
|
+
}
|
|
4362
|
+
for (const file of oldOwned) {
|
|
4363
|
+
const absolute = path2.join(project, file);
|
|
4364
|
+
const current = await readOptionalBytes(absolute);
|
|
4365
|
+
if (current === void 0) continue;
|
|
4366
|
+
const expected = oldDigestByFile.get(file);
|
|
4367
|
+
if (!expected || sha256(current) !== expected.sha256 || !await fileModeMatches(absolute, expected.mode)) {
|
|
4368
|
+
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}`);
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4371
|
+
const newLock = {
|
|
4372
|
+
schemaVersion: 1,
|
|
4373
|
+
source: {
|
|
4374
|
+
endpointOriginSha256: sha256(normalizeEndpoint(input.endpoint)),
|
|
4375
|
+
catalogPath: "/skills.json",
|
|
4376
|
+
catalogSha256: sha256(stableJson2(input.catalog))
|
|
4377
|
+
},
|
|
4378
|
+
selection: { requestedSkill: input.requestedSkill },
|
|
4379
|
+
resolvedSkills: input.documents.map(({ id, bytes, sha256: digest, files: skillFiles }) => ({
|
|
4380
|
+
id,
|
|
4381
|
+
bytes,
|
|
4382
|
+
sha256: digest,
|
|
4383
|
+
files: skillFiles.map(({ path: filePath, bytes: fileBytes, sha256: fileSha256 }) => ({
|
|
4384
|
+
path: filePath,
|
|
4385
|
+
bytes: fileBytes,
|
|
4386
|
+
sha256: fileSha256
|
|
4387
|
+
}))
|
|
4388
|
+
})),
|
|
4389
|
+
runtime: {
|
|
4390
|
+
capabilities: input.runtime.capabilities,
|
|
4391
|
+
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4392
|
+
launch: input.runtime.launch,
|
|
4393
|
+
files: input.runtime.files.map(({ path: filePath, bytes, sha256: digest, mode }) => ({
|
|
4394
|
+
path: filePath,
|
|
4395
|
+
bytes,
|
|
4396
|
+
sha256: digest,
|
|
4397
|
+
mode
|
|
4398
|
+
}))
|
|
4399
|
+
},
|
|
4400
|
+
ownedFiles: desiredFiles,
|
|
4401
|
+
syncedAt: input.now ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4402
|
+
};
|
|
4403
|
+
if (oldLock && sameLockContent(oldLock, newLock) && await desiredFilesMatch(project, desiredByFile)) {
|
|
4404
|
+
return result(false, input, project, skillsDir, lockPath, [], []);
|
|
4405
|
+
}
|
|
4406
|
+
const obsolete = [...oldOwned].filter((file) => !desiredByFile.has(file));
|
|
4407
|
+
await fs4.mkdir(metaDir, { recursive: true });
|
|
4408
|
+
const staging = await fs4.mkdtemp(path2.join(metaDir, ".staging-"));
|
|
4409
|
+
const stagedFiles = path2.join(staging, "new");
|
|
4410
|
+
const backups = path2.join(staging, "backup");
|
|
4411
|
+
const stagedLock = path2.join(staging, "chiron.lock.json");
|
|
4412
|
+
const journalPath = path2.join(staging, "journal.json");
|
|
4413
|
+
let journalReady = false;
|
|
4414
|
+
try {
|
|
4415
|
+
for (const [file, preparedFile2] of desiredByFile) {
|
|
4416
|
+
const staged = path2.join(stagedFiles, file);
|
|
4417
|
+
await fs4.mkdir(path2.dirname(staged), { recursive: true });
|
|
4418
|
+
await fs4.writeFile(
|
|
4419
|
+
staged,
|
|
4420
|
+
preparedFile2.content,
|
|
4421
|
+
preparedFile2.mode === void 0 ? void 0 : { mode: preparedFile2.mode }
|
|
4422
|
+
);
|
|
4423
|
+
}
|
|
4424
|
+
await fs4.writeFile(stagedLock, `${JSON.stringify(newLock, null, 2)}
|
|
4425
|
+
`, "utf8");
|
|
4426
|
+
const affected = [.../* @__PURE__ */ new Set([...desiredFiles, ...obsolete])];
|
|
4427
|
+
for (const file of affected) {
|
|
4428
|
+
const target = path2.join(project, file);
|
|
4429
|
+
if (!await exists(target)) continue;
|
|
4430
|
+
const backup = path2.join(backups, file);
|
|
4431
|
+
await fs4.mkdir(path2.dirname(backup), { recursive: true });
|
|
4432
|
+
await fs4.copyFile(target, backup);
|
|
4433
|
+
}
|
|
4434
|
+
const oldLockExisted = await exists(lockPath);
|
|
4435
|
+
if (oldLockExisted) {
|
|
4436
|
+
const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4437
|
+
await fs4.mkdir(path2.dirname(backupLock), { recursive: true });
|
|
4438
|
+
await fs4.copyFile(lockPath, backupLock);
|
|
4439
|
+
}
|
|
4440
|
+
const journal = {
|
|
4441
|
+
schemaVersion: 1,
|
|
4442
|
+
project,
|
|
4443
|
+
affectedFiles: affected,
|
|
4444
|
+
oldLockExisted
|
|
4445
|
+
};
|
|
4446
|
+
await fs4.writeFile(journalPath, `${JSON.stringify(journal, null, 2)}
|
|
4447
|
+
`, "utf8");
|
|
4448
|
+
journalReady = true;
|
|
4449
|
+
for (const file of desiredFiles) {
|
|
4450
|
+
const target = path2.join(project, file);
|
|
4451
|
+
await fs4.mkdir(path2.dirname(target), { recursive: true });
|
|
4452
|
+
await fs4.rm(target, { force: true });
|
|
4453
|
+
await fs4.rename(path2.join(stagedFiles, file), target);
|
|
4454
|
+
}
|
|
4455
|
+
for (const file of obsolete) await fs4.unlink(path2.join(project, file)).catch(ignoreMissing);
|
|
4456
|
+
await fs4.rm(lockPath, { force: true });
|
|
4457
|
+
await fs4.rename(stagedLock, lockPath);
|
|
4458
|
+
await fs4.unlink(journalPath);
|
|
4459
|
+
journalReady = false;
|
|
4460
|
+
} catch (cause) {
|
|
4461
|
+
if (journalReady) await recoverStaging(project, staging, lockPath);
|
|
4462
|
+
throw cause;
|
|
4463
|
+
} finally {
|
|
4464
|
+
await fs4.rm(staging, { recursive: true, force: true });
|
|
4465
|
+
}
|
|
4466
|
+
return result(
|
|
4467
|
+
true,
|
|
4468
|
+
input,
|
|
4469
|
+
project,
|
|
4470
|
+
skillsDir,
|
|
4471
|
+
lockPath,
|
|
4472
|
+
input.documents.map((document) => document.id),
|
|
4473
|
+
[...new Set(obsolete.map(idFromOwnedFile).filter((id) => id !== void 0))]
|
|
4474
|
+
);
|
|
4475
|
+
}
|
|
4476
|
+
async function recoverInterruptedSyncs(project, metaDir, lockPath) {
|
|
4477
|
+
let entries;
|
|
4478
|
+
try {
|
|
4479
|
+
entries = await fs4.readdir(metaDir, { withFileTypes: true });
|
|
4480
|
+
} catch (cause) {
|
|
4481
|
+
if (cause.code === "ENOENT") return;
|
|
4482
|
+
throw cause;
|
|
4483
|
+
}
|
|
4484
|
+
for (const entry of entries) {
|
|
4485
|
+
if (!entry.isDirectory() || !entry.name.startsWith(".staging-")) continue;
|
|
4486
|
+
const staging = path2.join(metaDir, entry.name);
|
|
4487
|
+
if (await exists(path2.join(staging, "journal.json"))) {
|
|
4488
|
+
await recoverStaging(project, staging, lockPath);
|
|
4489
|
+
}
|
|
4490
|
+
await fs4.rm(staging, { recursive: true, force: true });
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
async function recoverStaging(project, staging, lockPath) {
|
|
4494
|
+
const journalPath = path2.join(staging, "journal.json");
|
|
4495
|
+
const journal = parseJournal(await fs4.readFile(journalPath, "utf8"), project);
|
|
4496
|
+
const backups = path2.join(staging, "backup");
|
|
4497
|
+
for (const file of journal.affectedFiles) {
|
|
4498
|
+
const target = path2.join(project, file);
|
|
4499
|
+
const backup = path2.join(backups, file);
|
|
4500
|
+
if (await exists(backup)) {
|
|
4501
|
+
await fs4.mkdir(path2.dirname(target), { recursive: true });
|
|
4502
|
+
await fs4.rm(target, { force: true });
|
|
4503
|
+
await fs4.rename(backup, target);
|
|
4504
|
+
} else {
|
|
4505
|
+
await fs4.rm(target, { force: true });
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4509
|
+
if (journal.oldLockExisted) {
|
|
4510
|
+
if (!await exists(backupLock)) throw invalid(`\u540C\u6B65\u6062\u590D\u7F3A\u5C11\u65E7\u9501\u5907\u4EFD\uFF1A${backupLock}`);
|
|
4511
|
+
await fs4.mkdir(path2.dirname(lockPath), { recursive: true });
|
|
4512
|
+
await fs4.rm(lockPath, { force: true });
|
|
4513
|
+
await fs4.rename(backupLock, lockPath);
|
|
4514
|
+
} else {
|
|
4515
|
+
await fs4.rm(lockPath, { force: true });
|
|
4516
|
+
}
|
|
4517
|
+
}
|
|
4518
|
+
function parseJournal(content, project) {
|
|
4519
|
+
let journal;
|
|
4520
|
+
try {
|
|
4521
|
+
journal = JSON.parse(content);
|
|
4522
|
+
} catch (cause) {
|
|
4523
|
+
throw new CliError14({
|
|
4524
|
+
code: CliErrorCode13.CONFIG_PARSE_ERROR,
|
|
4525
|
+
message: "Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u635F\u574F",
|
|
4526
|
+
cause
|
|
4527
|
+
});
|
|
4528
|
+
}
|
|
4529
|
+
if (journal.schemaVersion !== 1 || journal.project !== project || !Array.isArray(journal.affectedFiles) || typeof journal.oldLockExisted !== "boolean") {
|
|
4530
|
+
throw invalid("Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u5408\u540C\u4E0D\u5339\u914D\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6062\u590D\u8303\u56F4");
|
|
4531
|
+
}
|
|
4532
|
+
for (const file of journal.affectedFiles) validateOwnedFile(file);
|
|
4533
|
+
return journal;
|
|
4534
|
+
}
|
|
4535
|
+
function validatePreparedDocuments(documents, requestedSkill) {
|
|
4536
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4537
|
+
for (const document of documents) {
|
|
4538
|
+
assertSafeSkillId(document.id);
|
|
4539
|
+
if (seen.has(document.id)) throw invalid(`\u540C\u6B65\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
4540
|
+
seen.add(document.id);
|
|
4541
|
+
if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4542
|
+
const paths = /* @__PURE__ */ new Set();
|
|
4543
|
+
for (const file of document.files) {
|
|
4544
|
+
assertSafeSkillFilePath(file.path);
|
|
4545
|
+
if (paths.has(file.path)) throw invalid(`\u6280\u80FD ${document.id} \u5305\u542B\u91CD\u590D\u6587\u4EF6: ${file.path}`);
|
|
4546
|
+
paths.add(file.path);
|
|
4547
|
+
if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256) {
|
|
4548
|
+
throw invalid(`\u6280\u80FD ${document.id}/${file.path} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4549
|
+
}
|
|
4550
|
+
}
|
|
4551
|
+
const root = document.files.find((file) => file.path === "SKILL.md");
|
|
4552
|
+
if (!root || !root.content.equals(Buffer.from(document.markdown, "utf8")) || root.bytes !== document.bytes || root.sha256 !== document.sha256) {
|
|
4553
|
+
throw invalid(`\u6280\u80FD ${document.id} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4554
|
+
}
|
|
4555
|
+
}
|
|
4556
|
+
if (!seen.has(requestedSkill)) throw invalid(`\u540C\u6B65\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${requestedSkill}`);
|
|
4557
|
+
}
|
|
4558
|
+
function validatePreparedRuntime(runtime) {
|
|
4559
|
+
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0) {
|
|
4560
|
+
throw invalid("WorkBuddy CLI \u80FD\u529B\u96C6\u5408\u4E3A\u7A7A");
|
|
4561
|
+
}
|
|
4562
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
4563
|
+
for (const capability of runtime.capabilities) {
|
|
4564
|
+
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
4565
|
+
throw invalid(`WorkBuddy CLI \u80FD\u529B\u6807\u8BC6\u975E\u6CD5\u6216\u91CD\u590D\uFF1A${capability}`);
|
|
4566
|
+
}
|
|
4567
|
+
capabilities.add(capability);
|
|
4568
|
+
}
|
|
4569
|
+
if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
|
|
4570
|
+
`)) {
|
|
4571
|
+
throw invalid("WorkBuddy CLI \u80FD\u529B\u6458\u8981\u4E0D\u4E00\u81F4");
|
|
4572
|
+
}
|
|
4573
|
+
if (!validDigest(runtime.launch.executableSha256)) {
|
|
4574
|
+
throw invalid("WorkBuddy CLI \u53EF\u6267\u884C\u6587\u4EF6\u6458\u8981\u975E\u6CD5");
|
|
4575
|
+
}
|
|
4576
|
+
if (runtime.launch.kind === "node-entry" && !validDigest(runtime.launch.entrySha256)) {
|
|
4577
|
+
throw invalid("WorkBuddy CLI \u5165\u53E3\u6458\u8981\u975E\u6CD5");
|
|
4578
|
+
}
|
|
4579
|
+
if (!Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4580
|
+
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5FC5\u987B\u540C\u65F6\u5305\u542B Unix \u4E0E Windows \u6587\u4EF6");
|
|
4581
|
+
}
|
|
4582
|
+
const files = /* @__PURE__ */ new Set();
|
|
4583
|
+
for (const file of runtime.files) {
|
|
4584
|
+
validateRuntimeFile(file);
|
|
4585
|
+
if (files.has(file.path)) throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u91CD\u590D\uFF1A${file.path}`);
|
|
4586
|
+
files.add(file.path);
|
|
4587
|
+
}
|
|
4588
|
+
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
4589
|
+
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5408\u540C\u4E0D\u5B8C\u6574");
|
|
4590
|
+
}
|
|
4591
|
+
}
|
|
4592
|
+
function validateRuntimeFile(file) {
|
|
4593
|
+
validateOwnedFile(file.path);
|
|
4594
|
+
if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256 || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
|
|
4595
|
+
throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u6458\u8981\u6216\u6743\u9650\u4E0D\u4E00\u81F4\uFF1A${file.path}`);
|
|
4596
|
+
}
|
|
4597
|
+
}
|
|
4598
|
+
function validateWorkBuddyFrontmatter(markdown, skillId) {
|
|
4599
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(markdown);
|
|
4600
|
+
if (!match) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 YAML frontmatter`);
|
|
4601
|
+
const frontmatter = match[1] ?? "";
|
|
4602
|
+
const name = /^name:\s*['"]?([^'"\s]+)['"]?\s*$/m.exec(frontmatter)?.[1];
|
|
4603
|
+
const description = /^description:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim();
|
|
4604
|
+
if (name !== skillId) throw invalid(`\u6280\u80FD ${skillId} \u7684 frontmatter name \u4E0D\u4E00\u81F4`);
|
|
4605
|
+
if (!description) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 description\uFF0CWorkBuddy \u65E0\u6CD5\u53D1\u73B0`);
|
|
4606
|
+
}
|
|
4607
|
+
async function readLock(lockPath) {
|
|
4608
|
+
const content = await readOptional(lockPath);
|
|
4609
|
+
if (content === void 0) return void 0;
|
|
4610
|
+
try {
|
|
4611
|
+
const lock = JSON.parse(content);
|
|
4612
|
+
if (lock.schemaVersion !== 1 || !lock.source || !lock.selection?.requestedSkill || !Array.isArray(lock.resolvedSkills) || !Array.isArray(lock.ownedFiles)) {
|
|
4613
|
+
throw new Error("schema mismatch");
|
|
4614
|
+
}
|
|
4615
|
+
assertSafeSkillId(lock.selection.requestedSkill);
|
|
4616
|
+
for (const skill of lock.resolvedSkills) {
|
|
4617
|
+
assertSafeSkillId(skill.id);
|
|
4618
|
+
if (!Number.isInteger(skill.bytes) || skill.bytes < 0 || !/^[a-f0-9]{64}$/.test(skill.sha256)) {
|
|
4619
|
+
throw new Error("invalid skill digest");
|
|
4620
|
+
}
|
|
4621
|
+
if (skill.files !== void 0) {
|
|
4622
|
+
if (!Array.isArray(skill.files) || skill.files.length === 0)
|
|
4623
|
+
throw new Error("invalid files");
|
|
4624
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4625
|
+
for (const file of skill.files) {
|
|
4626
|
+
assertSafeSkillFilePath(file.path);
|
|
4627
|
+
if (seen.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4628
|
+
throw new Error("invalid file digest");
|
|
4629
|
+
}
|
|
4630
|
+
seen.add(file.path);
|
|
4631
|
+
}
|
|
4632
|
+
if (!seen.has("SKILL.md")) throw new Error("missing SKILL.md");
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
if (lock.runtime !== void 0) validateLockedRuntime(lock.runtime);
|
|
4636
|
+
for (const file of lock.ownedFiles) validateOwnedFile(file);
|
|
4637
|
+
return lock;
|
|
4638
|
+
} catch (cause) {
|
|
4639
|
+
throw new CliError14({
|
|
4640
|
+
code: CliErrorCode13.CONFIG_PARSE_ERROR,
|
|
4641
|
+
message: `Chiron \u9501\u6587\u4EF6\u635F\u574F\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6587\u4EF6\u6240\u6709\u6743\uFF1A${lockPath}`,
|
|
4642
|
+
cause
|
|
4643
|
+
});
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
function validateLockedRuntime(runtime) {
|
|
4647
|
+
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
|
|
4648
|
+
`) || !runtime.launch || !validDigest(runtime.launch.executableSha256) || !Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4649
|
+
throw new Error("invalid runtime");
|
|
4650
|
+
}
|
|
4651
|
+
if (runtime.launch.kind === "node-entry") {
|
|
4652
|
+
if (!validDigest(runtime.launch.entrySha256)) throw new Error("invalid runtime entry");
|
|
4653
|
+
} else if (runtime.launch.kind !== "executable") {
|
|
4654
|
+
throw new Error("invalid runtime launch kind");
|
|
4655
|
+
}
|
|
4656
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
4657
|
+
for (const capability of runtime.capabilities) {
|
|
4658
|
+
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
4659
|
+
throw new Error("invalid runtime capability");
|
|
4660
|
+
}
|
|
4661
|
+
capabilities.add(capability);
|
|
4662
|
+
}
|
|
4663
|
+
const files = /* @__PURE__ */ new Set();
|
|
4664
|
+
for (const file of runtime.files) {
|
|
4665
|
+
validateOwnedFile(file.path);
|
|
4666
|
+
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) {
|
|
4667
|
+
throw new Error("invalid runtime file");
|
|
4668
|
+
}
|
|
4669
|
+
files.add(file.path);
|
|
4670
|
+
}
|
|
4671
|
+
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
4672
|
+
throw new Error("missing runtime file");
|
|
4673
|
+
}
|
|
4674
|
+
}
|
|
4675
|
+
function sameLockContent(left, right) {
|
|
4676
|
+
const withoutTime = (lock) => ({ ...lock, syncedAt: void 0 });
|
|
4677
|
+
return stableJson2(withoutTime(left)) === stableJson2(withoutTime(right));
|
|
4678
|
+
}
|
|
4679
|
+
async function desiredFilesMatch(project, desired) {
|
|
4680
|
+
for (const [file, preparedFile2] of desired) {
|
|
4681
|
+
const absolute = path2.join(project, file);
|
|
4682
|
+
const current = await readOptionalBytes(absolute);
|
|
4683
|
+
if (current === void 0 || sha256(current) !== preparedFile2.sha256 || !await fileModeMatches(absolute, preparedFile2.mode)) {
|
|
4684
|
+
return false;
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
return true;
|
|
4688
|
+
}
|
|
4689
|
+
function result(changed, input, project, skillsDir, lockPath, installed, removed) {
|
|
4690
|
+
return {
|
|
4691
|
+
changed,
|
|
4692
|
+
requestedSkill: input.requestedSkill,
|
|
4693
|
+
resolvedSkills: input.documents.map((document) => document.id),
|
|
4694
|
+
project,
|
|
4695
|
+
skillsDir,
|
|
4696
|
+
lockPath,
|
|
4697
|
+
runnerPath: path2.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
4698
|
+
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4699
|
+
installed,
|
|
4700
|
+
removed
|
|
4701
|
+
};
|
|
4702
|
+
}
|
|
4703
|
+
function ownedFile(id, relativePath = "SKILL.md") {
|
|
4704
|
+
assertSafeSkillId(id);
|
|
4705
|
+
assertSafeSkillFilePath(relativePath);
|
|
4706
|
+
return `.codebuddy/skills/${id}/${relativePath}`;
|
|
4707
|
+
}
|
|
4708
|
+
function idFromOwnedFile(file) {
|
|
4709
|
+
validateOwnedFile(file);
|
|
4710
|
+
if (!file.startsWith(".codebuddy/skills/")) return void 0;
|
|
4711
|
+
return file.split("/")[2] ?? "";
|
|
4712
|
+
}
|
|
4713
|
+
function validateOwnedFile(file) {
|
|
4714
|
+
if (file === ".hcm-delivery/bin/hcm" || file === ".hcm-delivery/bin/hcm.cmd") return;
|
|
4715
|
+
const match = /^\.codebuddy\/skills\/([a-z0-9][a-z0-9-]{0,63})\/(.+)$/.exec(file);
|
|
4716
|
+
if (!match) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4717
|
+
const id = match[1];
|
|
4718
|
+
const relativePath = match[2];
|
|
4719
|
+
if (!id || !relativePath) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4720
|
+
assertSafeSkillId(id);
|
|
4721
|
+
try {
|
|
4722
|
+
assertSafeSkillFilePath(relativePath);
|
|
4723
|
+
} catch {
|
|
4724
|
+
throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4727
|
+
function normalizeEndpoint(endpoint) {
|
|
4728
|
+
let url;
|
|
4729
|
+
try {
|
|
4730
|
+
url = new URL(endpoint);
|
|
4731
|
+
} catch (cause) {
|
|
4732
|
+
throw new CliError14({
|
|
4733
|
+
code: CliErrorCode13.INVALID_ARGUMENT,
|
|
4734
|
+
message: `\u975E\u6CD5 endpoint\uFF1A${endpoint}`,
|
|
4735
|
+
cause
|
|
4736
|
+
});
|
|
4737
|
+
}
|
|
4738
|
+
url.username = "";
|
|
4739
|
+
url.password = "";
|
|
4740
|
+
url.search = "";
|
|
4741
|
+
url.hash = "";
|
|
4742
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
4743
|
+
return url.toString().replace(/\/$/, "");
|
|
4744
|
+
}
|
|
4745
|
+
function sha256(value) {
|
|
4746
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4747
|
+
}
|
|
4748
|
+
function stableJson2(value) {
|
|
4749
|
+
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
4750
|
+
if (value && typeof value === "object") {
|
|
4751
|
+
const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
4752
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
|
|
4753
|
+
}
|
|
4754
|
+
return JSON.stringify(value);
|
|
4755
|
+
}
|
|
4756
|
+
async function readOptional(file) {
|
|
4757
|
+
try {
|
|
4758
|
+
return await fs4.readFile(file, "utf8");
|
|
4759
|
+
} catch (cause) {
|
|
4760
|
+
if (cause.code === "ENOENT") return void 0;
|
|
4761
|
+
throw cause;
|
|
4762
|
+
}
|
|
4763
|
+
}
|
|
4764
|
+
async function readOptionalBytes(file) {
|
|
4765
|
+
try {
|
|
4766
|
+
return await fs4.readFile(file);
|
|
4767
|
+
} catch (cause) {
|
|
4768
|
+
if (cause.code === "ENOENT") return void 0;
|
|
4769
|
+
throw cause;
|
|
4770
|
+
}
|
|
4771
|
+
}
|
|
4772
|
+
async function exists(file) {
|
|
4773
|
+
try {
|
|
4774
|
+
await fs4.access(file);
|
|
4775
|
+
return true;
|
|
4776
|
+
} catch (cause) {
|
|
4777
|
+
if (cause.code === "ENOENT") return false;
|
|
4778
|
+
throw cause;
|
|
4779
|
+
}
|
|
4780
|
+
}
|
|
4781
|
+
function lockDigests(lock) {
|
|
4782
|
+
const digests = /* @__PURE__ */ new Map();
|
|
4783
|
+
for (const skill of lock?.resolvedSkills ?? []) {
|
|
4784
|
+
if (skill.files === void 0) {
|
|
4785
|
+
digests.set(ownedFile(skill.id), { sha256: skill.sha256 });
|
|
4786
|
+
continue;
|
|
4787
|
+
}
|
|
4788
|
+
for (const file of skill.files) {
|
|
4789
|
+
digests.set(ownedFile(skill.id, file.path), { sha256: file.sha256 });
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
for (const file of lock?.runtime?.files ?? []) {
|
|
4793
|
+
digests.set(file.path, { sha256: file.sha256, mode: file.mode });
|
|
4794
|
+
}
|
|
4795
|
+
return digests;
|
|
4796
|
+
}
|
|
4797
|
+
async function fileModeMatches(file, expected) {
|
|
4798
|
+
if (expected === void 0 || process.platform === "win32") return true;
|
|
4799
|
+
const stat = await fs4.stat(file);
|
|
4800
|
+
return (stat.mode & 511) === expected;
|
|
4801
|
+
}
|
|
4802
|
+
function validDigest(value) {
|
|
4803
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
4804
|
+
}
|
|
4805
|
+
function ignoreMissing(cause) {
|
|
4806
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
4807
|
+
}
|
|
4808
|
+
function invalid(message) {
|
|
4809
|
+
return new CliError14({ code: CliErrorCode13.INVALID_ARGUMENT, message });
|
|
4810
|
+
}
|
|
4811
|
+
|
|
4812
|
+
// src/commands/skills-runtime.ts
|
|
4813
|
+
import { execFile } from "child_process";
|
|
4814
|
+
import { createHash as createHash2 } from "crypto";
|
|
4815
|
+
import { createReadStream, promises as fs5 } from "fs";
|
|
4816
|
+
import path3 from "path";
|
|
4817
|
+
import { promisify } from "util";
|
|
4818
|
+
import { CliError as CliError15, CliErrorCode as CliErrorCode14 } from "@hcmai/sdk";
|
|
4819
|
+
var execFileAsync = promisify(execFile);
|
|
4820
|
+
var REQUIRED_CAPABILITIES = [
|
|
4821
|
+
"hcm.help.skills",
|
|
4822
|
+
"hcm.skills.help.sync",
|
|
4823
|
+
"hcm.skills.sync.option.project",
|
|
4824
|
+
"hcm.skills.sync.option.target"
|
|
4825
|
+
];
|
|
4826
|
+
async function prepareWorkBuddyRuntime(input = {}) {
|
|
4827
|
+
const candidate = input.candidate?.trim() || input.currentEntry || process.argv[1];
|
|
4828
|
+
if (!candidate) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D HCM CLI \u5165\u53E3\uFF1B\u8BF7\u4F20 --cli <path>");
|
|
4829
|
+
const launch = await resolveLaunch(candidate, input.nodeExecutable ?? process.execPath);
|
|
4830
|
+
const launchTarget = launch.kind === "node-entry" ? launch.entry : launch.executable;
|
|
4831
|
+
for (const forbidden2 of input.forbiddenCandidatePaths ?? []) {
|
|
4832
|
+
if (await comparablePath(forbidden2) === launchTarget) {
|
|
4833
|
+
throw invalid2("\u5019\u9009 HCM CLI \u4E0D\u80FD\u6307\u5411\u5F85\u751F\u6210\u7684\u9879\u76EE\u7A33\u5B9A\u5165\u53E3");
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
if (!(input.currentEntryHasRequiredCapabilities && !input.candidate?.trim())) {
|
|
4837
|
+
const beforeProbe = await launchIdentity(launch);
|
|
4838
|
+
const syncHelp = await runProbe(launch, ["skills", "sync", "--help"], input.timeoutMs).catch(
|
|
4839
|
+
() => ""
|
|
4840
|
+
);
|
|
4841
|
+
const missing = [];
|
|
4842
|
+
if (!/\bhcm\s+skills\s+sync\b/.test(syncHelp)) {
|
|
4843
|
+
missing.push(REQUIRED_CAPABILITIES[0], REQUIRED_CAPABILITIES[1]);
|
|
4844
|
+
}
|
|
4845
|
+
if (!/--project(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[2]);
|
|
4846
|
+
if (!/--target(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[3]);
|
|
4847
|
+
if (missing.length > 0) {
|
|
4848
|
+
throw invalid2(`\u5019\u9009 HCM CLI \u7F3A\u5C11 WorkBuddy \u6240\u9700\u80FD\u529B\uFF1A${missing.join(", ")}`);
|
|
4849
|
+
}
|
|
4850
|
+
if (await launchIdentity(launch) !== beforeProbe) {
|
|
4851
|
+
throw invalid2("\u5019\u9009 HCM CLI \u5728\u80FD\u529B\u63A2\u9488\u671F\u95F4\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u8BD5\u540C\u6B65");
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
const capabilities = [...REQUIRED_CAPABILITIES];
|
|
4855
|
+
const capabilitiesSha256 = sha2562(`${capabilities.join("\n")}
|
|
4856
|
+
`);
|
|
4857
|
+
const executableSha256 = await sha256File(launch.executable);
|
|
4858
|
+
const launchRecord = launch.kind === "node-entry" ? {
|
|
4859
|
+
kind: "node-entry",
|
|
4860
|
+
executableSha256,
|
|
4861
|
+
entrySha256: await sha256File(launch.entry)
|
|
4862
|
+
} : { kind: "executable", executableSha256 };
|
|
4863
|
+
const unixContent = Buffer.from(unixLauncher(launch), "utf8");
|
|
4864
|
+
const windowsContent = Buffer.from(windowsLauncher(launch), "utf8");
|
|
4865
|
+
return {
|
|
4866
|
+
capabilities,
|
|
4867
|
+
capabilitiesSha256,
|
|
4868
|
+
launch: launchRecord,
|
|
4869
|
+
files: [
|
|
4870
|
+
preparedFile(".hcm-delivery/bin/hcm", unixContent, 493),
|
|
4871
|
+
preparedFile(".hcm-delivery/bin/hcm.cmd", windowsContent, 420)
|
|
4872
|
+
]
|
|
4873
|
+
};
|
|
4874
|
+
}
|
|
4875
|
+
async function resolveLaunch(candidate, nodeExecutable) {
|
|
4876
|
+
const requested = path3.resolve(candidate);
|
|
4877
|
+
const resolvedCandidate = await realFile(requested, "\u5019\u9009 HCM CLI");
|
|
4878
|
+
const header = (await readPrefix(resolvedCandidate, 256)).toString("utf8");
|
|
4879
|
+
const nodeEntry = /^(?:#![^\r\n]*\bnode\b)|\.(?:c|m)?js$/i.test(
|
|
4880
|
+
header.startsWith("#!") ? header.split(/\r?\n/, 1)[0] ?? "" : resolvedCandidate
|
|
4881
|
+
);
|
|
4882
|
+
if (nodeEntry) {
|
|
4883
|
+
return {
|
|
4884
|
+
kind: "node-entry",
|
|
4885
|
+
executable: await realFile(path3.resolve(nodeExecutable), "Node \u53EF\u6267\u884C\u6587\u4EF6"),
|
|
4886
|
+
entry: resolvedCandidate
|
|
4887
|
+
};
|
|
4888
|
+
}
|
|
4889
|
+
return { kind: "executable", executable: resolvedCandidate };
|
|
4890
|
+
}
|
|
4891
|
+
async function realFile(file, label) {
|
|
4892
|
+
try {
|
|
4893
|
+
const resolved = await fs5.realpath(file);
|
|
4894
|
+
const stat = await fs5.stat(resolved);
|
|
4895
|
+
if (!stat.isFile()) throw new Error("not a regular file");
|
|
4896
|
+
return resolved;
|
|
4897
|
+
} catch (cause) {
|
|
4898
|
+
throw new CliError15({
|
|
4899
|
+
code: CliErrorCode14.INVALID_ARGUMENT,
|
|
4900
|
+
message: `${label}\u4E0D\u53EF\u7528\uFF1A${file}`,
|
|
4901
|
+
cause
|
|
4902
|
+
});
|
|
4903
|
+
}
|
|
4904
|
+
}
|
|
4905
|
+
async function comparablePath(file) {
|
|
4906
|
+
const absolute = path3.resolve(file);
|
|
4907
|
+
return fs5.realpath(absolute).catch(() => absolute);
|
|
4908
|
+
}
|
|
4909
|
+
async function launchIdentity(launch) {
|
|
4910
|
+
const files = launch.kind === "node-entry" ? [launch.executable, launch.entry] : [launch.executable];
|
|
4911
|
+
const identities = await Promise.all(
|
|
4912
|
+
files.map(async (file) => {
|
|
4913
|
+
const stat = await fs5.stat(file, { bigint: true });
|
|
4914
|
+
return [file, stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].map(String).join("\0");
|
|
4915
|
+
})
|
|
4916
|
+
);
|
|
4917
|
+
return identities.join("\n");
|
|
4918
|
+
}
|
|
4919
|
+
async function readPrefix(file, bytes) {
|
|
4920
|
+
const handle = await fs5.open(file, "r");
|
|
4921
|
+
try {
|
|
4922
|
+
const buffer = Buffer.alloc(bytes);
|
|
4923
|
+
const result2 = await handle.read(buffer, 0, bytes, 0);
|
|
4924
|
+
return buffer.subarray(0, result2.bytesRead);
|
|
4925
|
+
} finally {
|
|
4926
|
+
await handle.close();
|
|
4927
|
+
}
|
|
4928
|
+
}
|
|
4929
|
+
async function runProbe(launch, args, timeoutMs = 5e3) {
|
|
4930
|
+
const command = launch.executable;
|
|
4931
|
+
const commandArgs = launch.kind === "node-entry" ? [launch.entry, ...args] : args;
|
|
4932
|
+
const env2 = {
|
|
4933
|
+
...process.env,
|
|
4934
|
+
CI: "1",
|
|
4935
|
+
NO_COLOR: "1",
|
|
4936
|
+
HCM_RUNTIME_PROBE: "1"
|
|
4937
|
+
};
|
|
4938
|
+
const nodeOptions = normalizeWorkBuddyNodeOptions(env2.NODE_OPTIONS);
|
|
4939
|
+
if (nodeOptions === void 0) delete env2.NODE_OPTIONS;
|
|
4940
|
+
else env2.NODE_OPTIONS = nodeOptions;
|
|
4941
|
+
try {
|
|
4942
|
+
const { stdout, stderr } = await execFileAsync(command, commandArgs, {
|
|
4943
|
+
encoding: "utf8",
|
|
4944
|
+
timeout: timeoutMs,
|
|
4945
|
+
maxBuffer: 256 * 1024,
|
|
4946
|
+
windowsHide: true,
|
|
4947
|
+
env: env2
|
|
4948
|
+
});
|
|
4949
|
+
return `${stdout}
|
|
4950
|
+
${stderr}`;
|
|
4951
|
+
} catch (cause) {
|
|
4952
|
+
throw new CliError15({
|
|
4953
|
+
code: CliErrorCode14.INVALID_ARGUMENT,
|
|
4954
|
+
message: `\u5019\u9009 HCM CLI \u80FD\u529B\u63A2\u9488\u5931\u8D25\uFF1Ahcm ${args.join(" ")}`,
|
|
4955
|
+
cause
|
|
4956
|
+
});
|
|
4957
|
+
}
|
|
4958
|
+
}
|
|
4959
|
+
function unixLauncher(launch) {
|
|
4960
|
+
const command = shellQuote(launch.executable);
|
|
4961
|
+
const entry = launch.kind === "node-entry" ? ` ${shellQuote(launch.entry)}` : "";
|
|
4962
|
+
return `#!/bin/sh
|
|
4963
|
+
set -eu
|
|
4964
|
+
|
|
4965
|
+
# WorkBuddy on macOS may inject --use-system-ca. Node can then spend minutes
|
|
4966
|
+
# evaluating the Keychain when a short-lived CLI process exits. Preserve every
|
|
4967
|
+
# other host option (including safety --require hooks) and change only this CA mode.
|
|
4968
|
+
wb_node_options="\${NODE_OPTIONS-}"
|
|
4969
|
+
if [ "$(uname -s 2>/dev/null || printf '%s' unknown)" = 'Darwin' ] && printf '%s
|
|
4970
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; then
|
|
4971
|
+
while printf '%s
|
|
4972
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; do
|
|
4973
|
+
wb_next_options="$(printf '%s
|
|
4974
|
+
' "$wb_node_options" | sed -E 's/(^|[[:space:]])--use-system-ca([[:space:]]|$)/\\1\\2/g')"
|
|
4975
|
+
[ "$wb_next_options" != "$wb_node_options" ] || break
|
|
4976
|
+
wb_node_options="$wb_next_options"
|
|
4977
|
+
done
|
|
4978
|
+
if ! printf '%s
|
|
4979
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-bundled-ca([[:space:]]|$)'; then
|
|
4980
|
+
wb_node_options="\${wb_node_options:+$wb_node_options }--use-bundled-ca"
|
|
4981
|
+
fi
|
|
4982
|
+
NODE_OPTIONS="$wb_node_options"
|
|
4983
|
+
export NODE_OPTIONS
|
|
4984
|
+
fi
|
|
4985
|
+
|
|
4986
|
+
exec ${command}${entry} "$@"
|
|
4987
|
+
`;
|
|
4988
|
+
}
|
|
4989
|
+
function normalizeWorkBuddyNodeOptions(value, platform = process.platform) {
|
|
4990
|
+
if (platform !== "darwin" || value === void 0) return value;
|
|
4991
|
+
const systemCa = /(^|[\t\n\r ])--use-system-ca(?=$|[\t\n\r ])/;
|
|
4992
|
+
if (!systemCa.test(value)) return value;
|
|
4993
|
+
let normalized = value;
|
|
4994
|
+
while (systemCa.test(normalized)) normalized = normalized.replace(systemCa, "$1");
|
|
4995
|
+
normalized = normalized.trim();
|
|
4996
|
+
const bundledCa = /(^|[\t\n\r ])--use-bundled-ca(?=$|[\t\n\r ])/;
|
|
4997
|
+
if (!bundledCa.test(normalized)) {
|
|
4998
|
+
normalized = `${normalized}${normalized ? " " : ""}--use-bundled-ca`;
|
|
4999
|
+
}
|
|
5000
|
+
return normalized;
|
|
5001
|
+
}
|
|
5002
|
+
function windowsLauncher(launch) {
|
|
5003
|
+
const command = cmdQuote(launch.executable);
|
|
5004
|
+
const entry = launch.kind === "node-entry" ? ` ${cmdQuote(launch.entry)}` : "";
|
|
5005
|
+
return `@echo off\r
|
|
5006
|
+
${command}${entry} %*\r
|
|
5007
|
+
`;
|
|
5008
|
+
}
|
|
5009
|
+
function shellQuote(value) {
|
|
5010
|
+
if (/[\r\n\0]/.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5011
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
5012
|
+
}
|
|
5013
|
+
function cmdQuote(value) {
|
|
5014
|
+
if (/[\r\n\0"]/g.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5015
|
+
return `"${value.replace(/%/g, "%%")}"`;
|
|
5016
|
+
}
|
|
5017
|
+
function preparedFile(filePath, content, mode) {
|
|
5018
|
+
return { path: filePath, content, bytes: content.length, sha256: sha2562(content), mode };
|
|
5019
|
+
}
|
|
5020
|
+
async function sha256File(file) {
|
|
5021
|
+
const hash = createHash2("sha256");
|
|
5022
|
+
await new Promise((resolve3, reject) => {
|
|
5023
|
+
const stream = createReadStream(file);
|
|
5024
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
5025
|
+
stream.on("error", reject);
|
|
5026
|
+
stream.on("end", resolve3);
|
|
5027
|
+
});
|
|
5028
|
+
return hash.digest("hex");
|
|
5029
|
+
}
|
|
5030
|
+
function sha2562(value) {
|
|
5031
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
5032
|
+
}
|
|
5033
|
+
function invalid2(message) {
|
|
5034
|
+
return new CliError15({ code: CliErrorCode14.INVALID_ARGUMENT, message });
|
|
5035
|
+
}
|
|
5036
|
+
|
|
5037
|
+
// src/commands/skills.ts
|
|
4250
5038
|
var SKILLS_STATE_FILE = ".hcm-skills.json";
|
|
4251
|
-
function
|
|
4252
|
-
return
|
|
5039
|
+
function sha2563(text) {
|
|
5040
|
+
return createHash3("sha256").update(text, "utf8").digest("hex");
|
|
4253
5041
|
}
|
|
4254
5042
|
async function resolveEndpoint(args) {
|
|
4255
5043
|
if (args.endpoint) return args.endpoint.replace(/\/+$/, "");
|
|
@@ -4257,14 +5045,43 @@ async function resolveEndpoint(args) {
|
|
|
4257
5045
|
const envName = resolveActiveEnv3({ envFlag: args.env }, process.env, g);
|
|
4258
5046
|
const env2 = await loadEnv5(envName).catch(() => null);
|
|
4259
5047
|
if (!env2?.endpoint) {
|
|
4260
|
-
throw new
|
|
4261
|
-
code:
|
|
5048
|
+
throw new CliError16({
|
|
5049
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
4262
5050
|
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"
|
|
4263
5051
|
});
|
|
4264
5052
|
}
|
|
4265
5053
|
return env2.endpoint.replace(/\/+$/, "");
|
|
4266
5054
|
}
|
|
4267
|
-
|
|
5055
|
+
function normalizeEndpoint2(endpoint) {
|
|
5056
|
+
return endpoint.replace(/\/+$/, "");
|
|
5057
|
+
}
|
|
5058
|
+
function decideTrackedEndpoint(options) {
|
|
5059
|
+
const { explicit } = options;
|
|
5060
|
+
const current = options.current ? normalizeEndpoint2(options.current) : void 0;
|
|
5061
|
+
const recorded = options.recorded ? normalizeEndpoint2(options.recorded) : void 0;
|
|
5062
|
+
if (explicit) {
|
|
5063
|
+
if (!current) throw invalid3("\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u7528 --env <name> \u6307\u4E00\u4E2A\u914D\u597D\u7684\u73AF\u5883");
|
|
5064
|
+
return current;
|
|
5065
|
+
}
|
|
5066
|
+
if (!recorded) {
|
|
5067
|
+
if (!current) {
|
|
5068
|
+
throw new CliError16({
|
|
5069
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
5070
|
+
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"
|
|
5071
|
+
});
|
|
5072
|
+
}
|
|
5073
|
+
return current;
|
|
5074
|
+
}
|
|
5075
|
+
if (!current || current === recorded) return recorded;
|
|
5076
|
+
throw invalid3(
|
|
5077
|
+
`\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
|
|
5078
|
+
\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
|
|
5079
|
+
\u660E\u8BF4\u8981\u54EA\u4E2A\uFF1A
|
|
5080
|
+
\xB7 \u7EE7\u7EED\u8DDF ${recorded} \u2192 \u52A0 --endpoint ${recorded}
|
|
5081
|
+
\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`
|
|
5082
|
+
);
|
|
5083
|
+
}
|
|
5084
|
+
async function exists2(target) {
|
|
4268
5085
|
try {
|
|
4269
5086
|
await fsp.stat(target);
|
|
4270
5087
|
return true;
|
|
@@ -4273,25 +5090,25 @@ async function exists(target) {
|
|
|
4273
5090
|
}
|
|
4274
5091
|
}
|
|
4275
5092
|
async function detectTarget(cwd) {
|
|
4276
|
-
if (await
|
|
4277
|
-
if (await
|
|
4278
|
-
if (await
|
|
5093
|
+
if (await exists2(path4.join(cwd, ".claude"))) return "claude";
|
|
5094
|
+
if (await exists2(path4.join(cwd, "AGENTS.md"))) return "codex";
|
|
5095
|
+
if (await exists2(path4.join(cwd, ".codex"))) return "codex";
|
|
4279
5096
|
return void 0;
|
|
4280
5097
|
}
|
|
4281
5098
|
async function resolveInstallLayout(args, cwd = process.cwd()) {
|
|
4282
5099
|
const requested = args.target && args.target !== "auto" ? args.target : void 0;
|
|
4283
5100
|
const target = requested ?? await detectTarget(cwd) ?? "claude";
|
|
4284
5101
|
if (target === "codex") {
|
|
4285
|
-
const root = args.global ?
|
|
5102
|
+
const root = args.global ? path4.join(os2.homedir(), ".codex") : cwd;
|
|
4286
5103
|
return {
|
|
4287
5104
|
target,
|
|
4288
|
-
baseDir: args.dir ?
|
|
4289
|
-
agentsFile:
|
|
5105
|
+
baseDir: args.dir ? path4.resolve(args.dir) : path4.join(root, "hcm-skills"),
|
|
5106
|
+
agentsFile: path4.join(root, "AGENTS.md")
|
|
4290
5107
|
};
|
|
4291
5108
|
}
|
|
4292
5109
|
return {
|
|
4293
5110
|
target,
|
|
4294
|
-
baseDir: args.dir ?
|
|
5111
|
+
baseDir: args.dir ? path4.resolve(args.dir) : args.global ? path4.join(os2.homedir(), ".claude", "skills") : path4.join(cwd, ".claude", "skills")
|
|
4295
5112
|
};
|
|
4296
5113
|
}
|
|
4297
5114
|
async function chironClient(args) {
|
|
@@ -4315,6 +5132,7 @@ function fail3(e, args) {
|
|
|
4315
5132
|
}
|
|
4316
5133
|
async function skillsListCommand(keyword, args) {
|
|
4317
5134
|
try {
|
|
5135
|
+
if (args.installed) return await listInstalledSkills(keyword, args);
|
|
4318
5136
|
const { http, endpoint, base } = await chironClient(args);
|
|
4319
5137
|
const manifest2 = await fetchSkillCatalog(http, base);
|
|
4320
5138
|
const fmt = args.output ?? "table";
|
|
@@ -4384,6 +5202,78 @@ async function skillsListCommand(keyword, args) {
|
|
|
4384
5202
|
fail3(e, args);
|
|
4385
5203
|
}
|
|
4386
5204
|
}
|
|
5205
|
+
async function listInstalledSkills(keyword, args) {
|
|
5206
|
+
const layout = await resolveInstallLayout(args);
|
|
5207
|
+
const state = await readSkillsState(layout.baseDir);
|
|
5208
|
+
const fmt = args.output ?? "table";
|
|
5209
|
+
if (!state || Object.keys(state.skills).length === 0) {
|
|
5210
|
+
if (fmt === "json" || fmt === "yaml") {
|
|
5211
|
+
process.stdout.write(formatObject13({ baseDir: layout.baseDir, target: layout.target, all: false, skills: [] }, { format: fmt }) + "\n");
|
|
5212
|
+
process.exit(0);
|
|
5213
|
+
}
|
|
5214
|
+
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
|
|
5215
|
+
`);
|
|
5216
|
+
process.stdout.write(" \u88C5\u5168\u90E8\uFF1Ahcm skills install --all\n");
|
|
5217
|
+
process.exit(0);
|
|
5218
|
+
}
|
|
5219
|
+
const rows = [];
|
|
5220
|
+
for (const [id, record] of Object.entries(state.skills).sort(([a], [b]) => a.localeCompare(b))) {
|
|
5221
|
+
const markdown = await readInstalled(layout.baseDir, id);
|
|
5222
|
+
let title = id;
|
|
5223
|
+
if (markdown !== void 0) {
|
|
5224
|
+
const raw = parseSkillFrontmatter(markdown, id).title;
|
|
5225
|
+
if (typeof raw === "string" && raw.trim()) title = raw.trim();
|
|
5226
|
+
}
|
|
5227
|
+
rows.push({
|
|
5228
|
+
id,
|
|
5229
|
+
title,
|
|
5230
|
+
direct: record.direct,
|
|
5231
|
+
installedAt: record.installedAt,
|
|
5232
|
+
// 三种状态互斥:文件没了 / 本地改过 / 和装的时候一致
|
|
5233
|
+
status: markdown === void 0 ? "missing" : sha2563(markdown) !== record.sha256 ? "locally-modified" : "installed"
|
|
5234
|
+
});
|
|
5235
|
+
}
|
|
5236
|
+
const shown = keyword ? rows.filter((row) => row.id.includes(keyword) || row.title.includes(keyword)) : rows;
|
|
5237
|
+
if (fmt === "json" || fmt === "yaml") {
|
|
5238
|
+
process.stdout.write(formatObject13({
|
|
5239
|
+
baseDir: layout.baseDir,
|
|
5240
|
+
target: layout.target,
|
|
5241
|
+
endpoint: state.endpoint,
|
|
5242
|
+
all: Boolean(state.all),
|
|
5243
|
+
updatedAt: state.updatedAt,
|
|
5244
|
+
skills: shown
|
|
5245
|
+
}, { format: fmt }) + "\n");
|
|
5246
|
+
process.exit(0);
|
|
5247
|
+
}
|
|
5248
|
+
process.stdout.write(`\u5DF2\u5B89\u88C5\u7684\u777F\u620E\u6280\u80FD \u2014 ${layout.baseDir}\uFF08${layout.target}\uFF09
|
|
5249
|
+
`);
|
|
5250
|
+
process.stdout.write(`\u6765\u6E90 ${state.endpoint ?? "\uFF08\u672A\u8BB0\u5F55\uFF09"}` + (state.all ? " \u8DDF\u968F\u5168\u96C6\uFF1A\u662F\n\n" : "\n\n"));
|
|
5251
|
+
if (shown.length === 0) {
|
|
5252
|
+
process.stdout.write(` \u6CA1\u6709\u5339\u914D\u300C${keyword}\u300D\u7684\u6280\u80FD\uFF08\u5171\u88C5\u4E86 ${rows.length} \u4E2A\uFF09
|
|
5253
|
+
`);
|
|
5254
|
+
process.exit(0);
|
|
5255
|
+
}
|
|
5256
|
+
for (const row of shown) {
|
|
5257
|
+
const mark = row.status === "missing" ? "?" : row.status === "locally-modified" ? "!" : row.direct ? "\u25CF" : "\u25CB";
|
|
5258
|
+
const note = row.status === "missing" ? "\u6587\u4EF6\u4E0D\u89C1\u4E86\uFF08update \u4F1A\u88C5\u56DE\uFF09" : row.status === "locally-modified" ? "\u672C\u5730\u6539\u8FC7" : row.direct ? "" : "\u4F9D\u8D56\u5E26\u5165";
|
|
5259
|
+
process.stdout.write(` ${mark} ${row.id.padEnd(30)} ${row.title.padEnd(24)} ${note}
|
|
5260
|
+
`);
|
|
5261
|
+
}
|
|
5262
|
+
const modified = shown.filter((row) => row.status === "locally-modified").length;
|
|
5263
|
+
const missing = shown.filter((row) => row.status === "missing").length;
|
|
5264
|
+
const indirect = shown.filter((row) => row.direct === false).length;
|
|
5265
|
+
process.stdout.write("\n");
|
|
5266
|
+
process.stdout.write(` \u25CF \u76F4\u63A5\u5B89\u88C5 \u25CB \u4F9D\u8D56\u5E26\u5165 ! \u672C\u5730\u6539\u8FC7 ? \u6587\u4EF6\u7F3A\u5931
|
|
5267
|
+
`);
|
|
5268
|
+
process.stdout.write(` \u5171 ${shown.length} \u4E2A` + [
|
|
5269
|
+
indirect > 0 ? `${indirect} \u4E2A\u662F\u4F9D\u8D56\u5E26\u5165` : "",
|
|
5270
|
+
modified > 0 ? `${modified} \u4E2A\u672C\u5730\u6539\u8FC7` : "",
|
|
5271
|
+
missing > 0 ? `${missing} \u4E2A\u6587\u4EF6\u7F3A\u5931` : ""
|
|
5272
|
+
].filter(Boolean).map((t) => `\uFF0C${t}`).join("") + "\n");
|
|
5273
|
+
process.stdout.write(` \u5347\u7EA7\uFF1Ahcm skills update${state.all ? "" : "\uFF08\u60F3\u8DDF\u968F\u5168\u96C6\u52A0 --all\uFF09"}
|
|
5274
|
+
`);
|
|
5275
|
+
process.exit(0);
|
|
5276
|
+
}
|
|
4387
5277
|
async function skillsShowCommand(id, args) {
|
|
4388
5278
|
try {
|
|
4389
5279
|
const { http, base } = await chironClient(args);
|
|
@@ -4394,44 +5284,132 @@ async function skillsShowCommand(id, args) {
|
|
|
4394
5284
|
fail3(e, args);
|
|
4395
5285
|
}
|
|
4396
5286
|
}
|
|
5287
|
+
async function skillsSyncCommand(id, args) {
|
|
5288
|
+
try {
|
|
5289
|
+
assertSafeSkillId2(id);
|
|
5290
|
+
if (args.target !== "workbuddy") {
|
|
5291
|
+
throw invalid3(`sync --target \u5F53\u524D\u53EA\u652F\u6301 workbuddy\uFF0C\u6536\u5230 "${args.target ?? ""}"`);
|
|
5292
|
+
}
|
|
5293
|
+
if (!args.project?.trim()) throw invalid3("sync \u5FC5\u987B\u4F20 --project <path>");
|
|
5294
|
+
const project = path4.resolve(args.project);
|
|
5295
|
+
const runtime = await prepareWorkBuddyRuntime({
|
|
5296
|
+
candidate: args.cli,
|
|
5297
|
+
currentEntryHasRequiredCapabilities: !args.cli?.trim(),
|
|
5298
|
+
forbiddenCandidatePaths: [
|
|
5299
|
+
path4.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
5300
|
+
path4.join(project, ".hcm-delivery", "bin", "hcm.cmd")
|
|
5301
|
+
]
|
|
5302
|
+
});
|
|
5303
|
+
const connectionArgs = { endpoint: args.endpoint, env: args.env };
|
|
5304
|
+
const { http, endpoint, base } = await chironClient(connectionArgs);
|
|
5305
|
+
const catalog = await fetchSkillCatalog(http, base);
|
|
5306
|
+
const documents = await prepareSkillDocuments(
|
|
5307
|
+
catalog,
|
|
5308
|
+
id,
|
|
5309
|
+
async (skillId) => fetchSkillMarkdown(http, skillId, base),
|
|
5310
|
+
async (skillId, relativePath) => fetchSkillFile(http, skillId, relativePath, base)
|
|
5311
|
+
);
|
|
5312
|
+
const result2 = await syncWorkBuddyProject({
|
|
5313
|
+
project,
|
|
5314
|
+
endpoint,
|
|
5315
|
+
catalog,
|
|
5316
|
+
requestedSkill: id,
|
|
5317
|
+
documents,
|
|
5318
|
+
runtime
|
|
5319
|
+
});
|
|
5320
|
+
const fmt = args.output ?? "table";
|
|
5321
|
+
if (fmt === "json" || fmt === "yaml") {
|
|
5322
|
+
process.stdout.write(`${formatObject13(result2, { format: fmt })}
|
|
5323
|
+
`);
|
|
5324
|
+
} else if (result2.changed) {
|
|
5325
|
+
process.stdout.write(`\u2705 ${id} \u2192 ${result2.skillsDir}
|
|
5326
|
+
`);
|
|
5327
|
+
process.stdout.write(` \u95ED\u5305 ${result2.resolvedSkills.join(" \u2192 ")}
|
|
5328
|
+
`);
|
|
5329
|
+
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5330
|
+
`);
|
|
5331
|
+
process.stdout.write(` CLI \u80FD\u529B ${result2.capabilitiesSha256.slice(0, 12)}
|
|
5332
|
+
`);
|
|
5333
|
+
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5334
|
+
`);
|
|
5335
|
+
} else {
|
|
5336
|
+
process.stdout.write(`\u2713 ${id} \u5DF2\u662F\u5F53\u524D\u76EE\u6807\u5B9E\u4F8B\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0
|
|
5337
|
+
`);
|
|
5338
|
+
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5339
|
+
`);
|
|
5340
|
+
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5341
|
+
`);
|
|
5342
|
+
}
|
|
5343
|
+
process.exit(0);
|
|
5344
|
+
} catch (e) {
|
|
5345
|
+
fail3(e, { endpoint: args.endpoint, env: args.env });
|
|
5346
|
+
}
|
|
5347
|
+
}
|
|
4397
5348
|
async function skillsInstallCommand(id, args) {
|
|
4398
5349
|
try {
|
|
4399
|
-
|
|
5350
|
+
const all = Boolean(args.all);
|
|
5351
|
+
if (all && id) throw invalid3("`--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");
|
|
5352
|
+
if (!all && !id) throw invalid3("\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");
|
|
5353
|
+
if (id) assertSafeSkillId2(id);
|
|
4400
5354
|
const layout = await resolveInstallLayout(args);
|
|
4401
5355
|
let documents;
|
|
5356
|
+
let requestedIds;
|
|
4402
5357
|
let endpoint;
|
|
4403
5358
|
let base;
|
|
4404
5359
|
let manifest2;
|
|
4405
5360
|
if (args.from) {
|
|
4406
|
-
|
|
5361
|
+
const root = path4.resolve(args.from);
|
|
5362
|
+
requestedIds = all ? await listLocalSkillIds(root) : [id];
|
|
5363
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5364
|
+
documents = [];
|
|
5365
|
+
for (const one of requestedIds) {
|
|
5366
|
+
for (const document of await resolveLocalSkillDocuments(root, one)) {
|
|
5367
|
+
if (seen.has(document.id)) continue;
|
|
5368
|
+
seen.add(document.id);
|
|
5369
|
+
documents.push(document);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
4407
5372
|
} else {
|
|
4408
|
-
const
|
|
5373
|
+
const tracked = decideTrackedEndpoint({
|
|
5374
|
+
explicit: Boolean(args.endpoint || args.env),
|
|
5375
|
+
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5376
|
+
recorded: (await readSkillsState(layout.baseDir))?.endpoint
|
|
5377
|
+
});
|
|
5378
|
+
const client3 = await chironClient({ ...args, endpoint: tracked });
|
|
4409
5379
|
endpoint = client3.endpoint;
|
|
4410
5380
|
base = client3.base;
|
|
4411
5381
|
manifest2 = await fetchSkillCatalog(client3.http, base);
|
|
4412
|
-
const order =
|
|
4413
|
-
|
|
4414
|
-
|
|
5382
|
+
const order = all ? resolveAllSkillsInstallOrder(manifest2) : resolveSkillInstallOrder2(manifest2, id);
|
|
5383
|
+
if (all && order.length === 0) throw invalid3(`${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u662F\u7A7A\u7684\uFF0C\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
|
|
5384
|
+
requestedIds = all ? order.map((skill) => skill.id) : [id];
|
|
5385
|
+
documents = await mapWithConcurrency(
|
|
5386
|
+
order,
|
|
5387
|
+
FETCH_CONCURRENCY,
|
|
5388
|
+
(skill) => fetchSkillDocument(client3.http, skill, endpoint, base)
|
|
4415
5389
|
);
|
|
4416
5390
|
}
|
|
4417
|
-
const
|
|
5391
|
+
const result2 = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
|
|
4418
5392
|
endpoint,
|
|
4419
5393
|
base,
|
|
4420
|
-
target: layout.target
|
|
5394
|
+
target: layout.target,
|
|
5395
|
+
all
|
|
4421
5396
|
});
|
|
4422
5397
|
const agents = layout.agentsFile ? await syncAgentsIndex(layout, manifest2) : void 0;
|
|
4423
5398
|
const fmt = args.output ?? "table";
|
|
4424
5399
|
if (fmt === "json" || fmt === "yaml") {
|
|
4425
|
-
process.stdout.write(formatObject13({ ...
|
|
5400
|
+
process.stdout.write(formatObject13({ ...result2, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents }, { format: fmt }) + "\n");
|
|
4426
5401
|
} else {
|
|
4427
|
-
|
|
5402
|
+
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result2.baseDir}` : `\u2705 ${id} \u2192 ${path4.join(result2.baseDir, id, "SKILL.md")}`;
|
|
5403
|
+
process.stdout.write(`${headline}
|
|
4428
5404
|
`);
|
|
4429
|
-
if (
|
|
4430
|
-
process.stdout.write(` \
|
|
5405
|
+
if (result2.installed.length > 0) {
|
|
5406
|
+
process.stdout.write(all ? ` \u65B0\u5199\u5165 ${result2.installed.length} \u4E2A
|
|
5407
|
+
` : ` \u5DF2\u5B89\u88C5 ${result2.installed.join(" \u2192 ")}
|
|
4431
5408
|
`);
|
|
4432
5409
|
}
|
|
4433
|
-
if (
|
|
4434
|
-
process.stdout.write(` \u5DF2\
|
|
5410
|
+
if (result2.unchanged.length > 0) {
|
|
5411
|
+
process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${result2.unchanged.length} \u4E2A
|
|
5412
|
+
` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${result2.unchanged.join(", ")}
|
|
4435
5413
|
`);
|
|
4436
5414
|
}
|
|
4437
5415
|
const referenceCount = documents.reduce((n, d) => n + (d.references?.length ?? 0), 0);
|
|
@@ -4443,16 +5421,44 @@ async function skillsInstallCommand(id, args) {
|
|
|
4443
5421
|
process.stdout.write(` \u5DF2${agents === "created" ? "\u521B\u5EFA" : "\u66F4\u65B0"} ${layout.agentsFile} \u91CC\u7684\u6280\u80FD\u7D22\u5F15\uFF08Codex \u8BFB\u8FD9\u91CC\uFF09
|
|
4444
5422
|
`);
|
|
4445
5423
|
}
|
|
4446
|
-
process.stdout.write(` \u6765\u6E90 ${
|
|
4447
|
-
`);
|
|
4448
|
-
process.stdout.write(` \u4EA7\u54C1\u66F4\u65B0\u540E\u8DD1 hcm skills update \u5347\u7EA7
|
|
5424
|
+
process.stdout.write(` \u6765\u6E90 ${endpoint ?? path4.resolve(args.from)}
|
|
4449
5425
|
`);
|
|
5426
|
+
process.stdout.write(all ? " \u5DF2\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D\uFF1A\u4EE5\u540E\u8DD1 hcm skills update\uFF0C\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4F1A\u81EA\u52A8\u8865\u4E0A\n" : " \u4EA7\u54C1\u66F4\u65B0\u540E\u8DD1 hcm skills update \u5347\u7EA7\n");
|
|
4450
5427
|
}
|
|
4451
5428
|
process.exit(0);
|
|
4452
5429
|
} catch (e) {
|
|
4453
5430
|
fail3(e, args);
|
|
4454
5431
|
}
|
|
4455
5432
|
}
|
|
5433
|
+
var FETCH_CONCURRENCY = 6;
|
|
5434
|
+
async function mapWithConcurrency(items, limit, fn) {
|
|
5435
|
+
const results = new Array(items.length);
|
|
5436
|
+
let cursor = 0;
|
|
5437
|
+
const worker = async () => {
|
|
5438
|
+
for (; ; ) {
|
|
5439
|
+
const index = cursor++;
|
|
5440
|
+
if (index >= items.length) return;
|
|
5441
|
+
results[index] = await fn(items[index]);
|
|
5442
|
+
}
|
|
5443
|
+
};
|
|
5444
|
+
await Promise.all(Array.from({ length: Math.min(limit, items.length) }, worker));
|
|
5445
|
+
return results;
|
|
5446
|
+
}
|
|
5447
|
+
async function listLocalSkillIds(root) {
|
|
5448
|
+
let entries;
|
|
5449
|
+
try {
|
|
5450
|
+
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
5451
|
+
} catch {
|
|
5452
|
+
throw invalid3(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
|
|
5453
|
+
}
|
|
5454
|
+
const ids = [];
|
|
5455
|
+
for (const entry of entries) {
|
|
5456
|
+
if (!entry.isDirectory()) continue;
|
|
5457
|
+
if (await exists2(path4.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
|
|
5458
|
+
}
|
|
5459
|
+
if (ids.length === 0) throw invalid3(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
|
|
5460
|
+
return ids.sort();
|
|
5461
|
+
}
|
|
4456
5462
|
async function fetchSkillDocument(http, skill, endpoint, base) {
|
|
4457
5463
|
const relatives = normalizeReferences(skill.references, skill.id);
|
|
4458
5464
|
const references = await Promise.all(relatives.map(async (relative2) => ({
|
|
@@ -4474,23 +5480,23 @@ async function syncAgentsIndex(layout, manifest2) {
|
|
|
4474
5480
|
for (const category of manifest2?.categories ?? []) {
|
|
4475
5481
|
for (const skill of category.skills ?? []) catalog.set(skill.id, skill);
|
|
4476
5482
|
}
|
|
4477
|
-
const agentsDir =
|
|
5483
|
+
const agentsDir = path4.dirname(layout.agentsFile);
|
|
4478
5484
|
const entries = Object.keys(state.skills).sort().map((id) => {
|
|
4479
5485
|
const known = catalog.get(id);
|
|
4480
|
-
const absolute =
|
|
4481
|
-
let relative2 =
|
|
5486
|
+
const absolute = path4.join(layout.baseDir, id, "SKILL.md");
|
|
5487
|
+
let relative2 = path4.relative(agentsDir, absolute);
|
|
4482
5488
|
if (!relative2.startsWith(".")) relative2 = `./${relative2}`;
|
|
4483
5489
|
return {
|
|
4484
5490
|
id,
|
|
4485
5491
|
title: known?.title ?? id,
|
|
4486
5492
|
description: known?.description ?? "",
|
|
4487
|
-
relativePath: relative2.split(
|
|
5493
|
+
relativePath: relative2.split(path4.sep).join("/")
|
|
4488
5494
|
};
|
|
4489
5495
|
});
|
|
4490
5496
|
return upsertAgentsBlock(layout.agentsFile, renderAgentsBlock(entries));
|
|
4491
5497
|
}
|
|
4492
5498
|
async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
4493
|
-
|
|
5499
|
+
assertSafeSkillId2(targetId);
|
|
4494
5500
|
const state = /* @__PURE__ */ new Map();
|
|
4495
5501
|
const pathStack = [];
|
|
4496
5502
|
const order = [];
|
|
@@ -4499,27 +5505,27 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
4499
5505
|
if (current === "done") return;
|
|
4500
5506
|
if (current === "visiting") {
|
|
4501
5507
|
const start = pathStack.indexOf(id);
|
|
4502
|
-
throw
|
|
5508
|
+
throw invalid3(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
|
|
4503
5509
|
}
|
|
4504
|
-
|
|
4505
|
-
const local =
|
|
5510
|
+
assertSafeSkillId2(id);
|
|
5511
|
+
const local = path4.join(sourceRoot, id, "SKILL.md");
|
|
4506
5512
|
let markdown;
|
|
4507
5513
|
try {
|
|
4508
5514
|
markdown = await fsp.readFile(local, "utf8");
|
|
4509
5515
|
} catch (cause) {
|
|
4510
5516
|
if (cause?.code === "ENOENT") {
|
|
4511
|
-
throw
|
|
5517
|
+
throw invalid3(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)}`);
|
|
4512
5518
|
}
|
|
4513
5519
|
throw cause;
|
|
4514
5520
|
}
|
|
4515
|
-
if (!markdown.trim()) throw
|
|
5521
|
+
if (!markdown.trim()) throw invalid3(`\u6280\u80FD ${id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4516
5522
|
state.set(id, "visiting");
|
|
4517
5523
|
pathStack.push(id);
|
|
4518
5524
|
const document = {
|
|
4519
5525
|
id,
|
|
4520
5526
|
markdown,
|
|
4521
5527
|
source: local,
|
|
4522
|
-
references: await readLocalReferences(
|
|
5528
|
+
references: await readLocalReferences(path4.join(sourceRoot, id))
|
|
4523
5529
|
};
|
|
4524
5530
|
for (const required of parseSkillRequirements(markdown, id)) {
|
|
4525
5531
|
await visit(required, id);
|
|
@@ -4533,7 +5539,7 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
4533
5539
|
}
|
|
4534
5540
|
async function readSkillsState(baseDir) {
|
|
4535
5541
|
try {
|
|
4536
|
-
const raw = await fsp.readFile(
|
|
5542
|
+
const raw = await fsp.readFile(path4.join(baseDir, SKILLS_STATE_FILE), "utf8");
|
|
4537
5543
|
const parsed = JSON.parse(raw);
|
|
4538
5544
|
if (!parsed || typeof parsed !== "object" || !parsed.skills) return void 0;
|
|
4539
5545
|
return parsed;
|
|
@@ -4543,7 +5549,7 @@ async function readSkillsState(baseDir) {
|
|
|
4543
5549
|
}
|
|
4544
5550
|
async function writeSkillsState(baseDir, state) {
|
|
4545
5551
|
await fsp.mkdir(baseDir, { recursive: true });
|
|
4546
|
-
const file =
|
|
5552
|
+
const file = path4.join(baseDir, SKILLS_STATE_FILE);
|
|
4547
5553
|
const temp = `${file}.${process.pid}.tmp`;
|
|
4548
5554
|
await fsp.writeFile(temp, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
4549
5555
|
await fsp.rename(temp, file);
|
|
@@ -4572,7 +5578,7 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
4572
5578
|
existing = await fsp.readFile(agentsFile, "utf8");
|
|
4573
5579
|
} catch (cause) {
|
|
4574
5580
|
if (cause?.code !== "ENOENT") throw cause;
|
|
4575
|
-
await fsp.mkdir(
|
|
5581
|
+
await fsp.mkdir(path4.dirname(agentsFile), { recursive: true });
|
|
4576
5582
|
await fsp.writeFile(agentsFile, block + "\n", "utf8");
|
|
4577
5583
|
return "created";
|
|
4578
5584
|
}
|
|
@@ -4587,50 +5593,47 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
4587
5593
|
await fsp.writeFile(agentsFile, existing + separator + block + "\n", "utf8");
|
|
4588
5594
|
return "appended";
|
|
4589
5595
|
}
|
|
4590
|
-
async function installSkillDocuments(documents,
|
|
4591
|
-
|
|
5596
|
+
async function installSkillDocuments(documents, requestedIds, baseDir, force, meta2) {
|
|
5597
|
+
if (requestedIds.length === 0) throw invalid3("\u6CA1\u6709\u6307\u5B9A\u8981\u88C5\u7684\u6280\u80FD");
|
|
5598
|
+
for (const id of requestedIds) assertSafeSkillId2(id);
|
|
4592
5599
|
const seen = /* @__PURE__ */ new Set();
|
|
4593
5600
|
const candidates = documents.map((document) => {
|
|
4594
|
-
|
|
4595
|
-
if (seen.has(document.id)) throw
|
|
5601
|
+
assertSafeSkillId2(document.id);
|
|
5602
|
+
if (seen.has(document.id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
4596
5603
|
seen.add(document.id);
|
|
4597
|
-
if (!document.markdown.trim()) throw
|
|
5604
|
+
if (!document.markdown.trim()) throw invalid3(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4598
5605
|
return {
|
|
4599
5606
|
...document,
|
|
4600
|
-
target:
|
|
5607
|
+
target: path4.join(baseDir, document.id, "SKILL.md"),
|
|
4601
5608
|
bytes: Buffer.byteLength(document.markdown)
|
|
4602
5609
|
};
|
|
4603
5610
|
});
|
|
4604
|
-
|
|
4605
|
-
|
|
5611
|
+
for (const id of requestedIds) {
|
|
5612
|
+
if (!seen.has(id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${id}`);
|
|
5613
|
+
}
|
|
4606
5614
|
const unchanged = /* @__PURE__ */ new Set();
|
|
4607
5615
|
for (const candidate of candidates) {
|
|
4608
|
-
|
|
4609
|
-
|
|
4610
|
-
|
|
4611
|
-
|
|
4612
|
-
if (cause?.code !== "ENOENT") throw cause;
|
|
4613
|
-
}
|
|
4614
|
-
if (existing === void 0 || force) continue;
|
|
4615
|
-
if (candidate.id !== requestedId && existing === candidate.markdown) {
|
|
5616
|
+
if (force) continue;
|
|
5617
|
+
const same = await sameOnDisk(baseDir, candidate);
|
|
5618
|
+
if (same === void 0) continue;
|
|
5619
|
+
if (same) {
|
|
4616
5620
|
unchanged.add(candidate.id);
|
|
4617
5621
|
continue;
|
|
4618
5622
|
}
|
|
4619
|
-
|
|
4620
|
-
|
|
5623
|
+
throw invalid3(
|
|
5624
|
+
`${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`
|
|
5625
|
+
);
|
|
4621
5626
|
}
|
|
4622
5627
|
const pending = candidates.filter((candidate) => !unchanged.has(candidate.id));
|
|
4623
5628
|
await writeSkillDocuments(pending, baseDir);
|
|
4624
5629
|
await recordInstalled(baseDir, candidates, {
|
|
4625
5630
|
...meta2,
|
|
4626
|
-
directIds:
|
|
5631
|
+
directIds: new Set(requestedIds),
|
|
4627
5632
|
keepInstalledAt: unchanged
|
|
4628
5633
|
});
|
|
4629
5634
|
return {
|
|
4630
|
-
|
|
4631
|
-
|
|
4632
|
-
source: requested.source,
|
|
4633
|
-
bytes: requested.bytes,
|
|
5635
|
+
ids: [...requestedIds],
|
|
5636
|
+
baseDir,
|
|
4634
5637
|
installed: pending.map((candidate) => candidate.id),
|
|
4635
5638
|
unchanged: candidates.filter((candidate) => unchanged.has(candidate.id)).map((candidate) => candidate.id),
|
|
4636
5639
|
files: candidates.map((candidate) => ({
|
|
@@ -4642,8 +5645,17 @@ async function installSkillDocuments(documents, requestedId, baseDir, force, met
|
|
|
4642
5645
|
}))
|
|
4643
5646
|
};
|
|
4644
5647
|
}
|
|
5648
|
+
async function sameOnDisk(baseDir, candidate) {
|
|
5649
|
+
const existing = await readInstalled(baseDir, candidate.id);
|
|
5650
|
+
if (existing === void 0) return void 0;
|
|
5651
|
+
if (existing !== candidate.markdown) return false;
|
|
5652
|
+
for (const reference of candidate.references ?? []) {
|
|
5653
|
+
if (await readInstalledReference(baseDir, candidate.id, reference.path) !== reference.content) return false;
|
|
5654
|
+
}
|
|
5655
|
+
return true;
|
|
5656
|
+
}
|
|
4645
5657
|
async function readLocalReferences(skillDir) {
|
|
4646
|
-
const root =
|
|
5658
|
+
const root = path4.join(skillDir, "references");
|
|
4647
5659
|
const out = [];
|
|
4648
5660
|
const walk = async (dir, prefix) => {
|
|
4649
5661
|
let items;
|
|
@@ -4656,12 +5668,12 @@ async function readLocalReferences(skillDir) {
|
|
|
4656
5668
|
for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4657
5669
|
const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
4658
5670
|
if (item.isDirectory()) {
|
|
4659
|
-
await walk(
|
|
5671
|
+
await walk(path4.join(dir, item.name), relative2);
|
|
4660
5672
|
continue;
|
|
4661
5673
|
}
|
|
4662
5674
|
if (!item.isFile()) continue;
|
|
4663
5675
|
assertSafeReferencePath(relative2);
|
|
4664
|
-
out.push({ path: relative2, content: await fsp.readFile(
|
|
5676
|
+
out.push({ path: relative2, content: await fsp.readFile(path4.join(dir, item.name), "utf8") });
|
|
4665
5677
|
}
|
|
4666
5678
|
};
|
|
4667
5679
|
await walk(root, "");
|
|
@@ -4671,15 +5683,15 @@ async function writeSkillDocuments(documents, baseDir) {
|
|
|
4671
5683
|
const staged = [];
|
|
4672
5684
|
try {
|
|
4673
5685
|
for (const document of documents) {
|
|
4674
|
-
const target =
|
|
4675
|
-
await fsp.mkdir(
|
|
4676
|
-
const temp =
|
|
5686
|
+
const target = path4.join(baseDir, document.id, "SKILL.md");
|
|
5687
|
+
await fsp.mkdir(path4.dirname(target), { recursive: true });
|
|
5688
|
+
const temp = path4.join(path4.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
|
|
4677
5689
|
await fsp.writeFile(temp, document.markdown, "utf8");
|
|
4678
5690
|
staged.push({ temp, target });
|
|
4679
5691
|
for (const reference of document.references ?? []) {
|
|
4680
5692
|
assertSafeReferencePath(reference.path);
|
|
4681
|
-
const referenceTarget =
|
|
4682
|
-
await fsp.mkdir(
|
|
5693
|
+
const referenceTarget = path4.join(baseDir, document.id, "references", reference.path);
|
|
5694
|
+
await fsp.mkdir(path4.dirname(referenceTarget), { recursive: true });
|
|
4683
5695
|
const referenceTemp = `${referenceTarget}.${process.pid}.${Date.now()}.tmp`;
|
|
4684
5696
|
await fsp.writeFile(referenceTemp, reference.content, "utf8");
|
|
4685
5697
|
staged.push({ temp: referenceTemp, target: referenceTarget });
|
|
@@ -4702,29 +5714,30 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
4702
5714
|
if (meta2.target) state.target = meta2.target;
|
|
4703
5715
|
if (meta2.endpoint) state.endpoint = meta2.endpoint;
|
|
4704
5716
|
if (meta2.base !== void 0) state.base = meta2.base;
|
|
5717
|
+
if (meta2.all) state.all = true;
|
|
4705
5718
|
state.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4706
5719
|
for (const document of documents) {
|
|
4707
5720
|
const previous = state.skills[document.id];
|
|
4708
5721
|
state.skills[document.id] = {
|
|
4709
5722
|
source: document.source,
|
|
4710
|
-
sha256:
|
|
5723
|
+
sha256: sha2563(document.markdown),
|
|
4711
5724
|
installedAt: meta2.keepInstalledAt?.has(document.id) && previous ? previous.installedAt : state.updatedAt,
|
|
4712
5725
|
// 已经是直接安装的,不因为这次作为依赖被带进来就降级
|
|
4713
5726
|
direct: meta2.directIds.has(document.id) || Boolean(previous?.direct),
|
|
4714
5727
|
references: Object.fromEntries(
|
|
4715
|
-
(document.references ?? []).map((reference) => [reference.path,
|
|
5728
|
+
(document.references ?? []).map((reference) => [reference.path, sha2563(reference.content)])
|
|
4716
5729
|
)
|
|
4717
5730
|
};
|
|
4718
5731
|
}
|
|
4719
5732
|
await writeSkillsState(baseDir, state);
|
|
4720
5733
|
return state;
|
|
4721
5734
|
}
|
|
4722
|
-
function
|
|
4723
|
-
return new
|
|
5735
|
+
function invalid3(message) {
|
|
5736
|
+
return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
|
|
4724
5737
|
}
|
|
4725
5738
|
async function readInstalled(baseDir, id) {
|
|
4726
5739
|
try {
|
|
4727
|
-
return await fsp.readFile(
|
|
5740
|
+
return await fsp.readFile(path4.join(baseDir, id, "SKILL.md"), "utf8");
|
|
4728
5741
|
} catch (cause) {
|
|
4729
5742
|
if (cause?.code === "ENOENT") return void 0;
|
|
4730
5743
|
throw cause;
|
|
@@ -4732,7 +5745,7 @@ async function readInstalled(baseDir, id) {
|
|
|
4732
5745
|
}
|
|
4733
5746
|
async function readInstalledReference(baseDir, id, relative2) {
|
|
4734
5747
|
try {
|
|
4735
|
-
return await fsp.readFile(
|
|
5748
|
+
return await fsp.readFile(path4.join(baseDir, id, "references", relative2), "utf8");
|
|
4736
5749
|
} catch (cause) {
|
|
4737
5750
|
if (cause?.code === "ENOENT") return void 0;
|
|
4738
5751
|
throw cause;
|
|
@@ -4740,13 +5753,15 @@ async function readInstalledReference(baseDir, id, relative2) {
|
|
|
4740
5753
|
}
|
|
4741
5754
|
async function planSkillUpdate(options) {
|
|
4742
5755
|
const { state, manifest: manifest2, baseDir, force, fetchDocument } = options;
|
|
5756
|
+
const all = options.all ?? Boolean(state.all);
|
|
4743
5757
|
const recorded = Object.entries(state.skills);
|
|
4744
5758
|
const directIds = recorded.some(([, r]) => r.direct) ? recorded.filter(([, r]) => r.direct).map(([id]) => id) : recorded.map(([id]) => id);
|
|
5759
|
+
const wanted = all ? [.../* @__PURE__ */ new Set([...directIds, ...flattenSkills(manifest2).map((skill) => skill.id)])] : directIds;
|
|
4745
5760
|
const entries = [];
|
|
4746
5761
|
const closure = /* @__PURE__ */ new Map();
|
|
4747
|
-
for (const id of
|
|
5762
|
+
for (const id of wanted.sort()) {
|
|
4748
5763
|
try {
|
|
4749
|
-
for (const skill of
|
|
5764
|
+
for (const skill of resolveSkillInstallOrder2(manifest2, id)) {
|
|
4750
5765
|
if (!closure.has(skill.id)) closure.set(skill.id, skill);
|
|
4751
5766
|
}
|
|
4752
5767
|
} catch {
|
|
@@ -4766,19 +5781,19 @@ async function planSkillUpdate(options) {
|
|
|
4766
5781
|
entries.push({
|
|
4767
5782
|
id: skill.id,
|
|
4768
5783
|
status: "added",
|
|
4769
|
-
note: record ? "\u672C\u5730\u6587\u4EF6\u7F3A\u5931\uFF0C\u91CD\u65B0\u88C5\u56DE" : "\u4EA7\u54C1\u65B0\u589E\u7684\u524D\u7F6E\u6280\u80FD"
|
|
5784
|
+
note: record ? "\u672C\u5730\u6587\u4EF6\u7F3A\u5931\uFF0C\u91CD\u65B0\u88C5\u56DE" : all ? "\u4EA7\u54C1\u8FD9\u4E00\u7248\u65B0\u589E\u7684\u6280\u80FD" : "\u4EA7\u54C1\u65B0\u589E\u7684\u524D\u7F6E\u6280\u80FD"
|
|
4770
5785
|
});
|
|
4771
5786
|
continue;
|
|
4772
5787
|
}
|
|
4773
5788
|
const remote = await fetchDocument(skill);
|
|
4774
5789
|
const remoteReferences = remote.references ?? [];
|
|
4775
5790
|
let differsFromRemote = remote.markdown !== onDisk;
|
|
4776
|
-
let editedLocally = record !== void 0 &&
|
|
5791
|
+
let editedLocally = record !== void 0 && sha2563(onDisk) !== record.sha256;
|
|
4777
5792
|
for (const reference of remoteReferences) {
|
|
4778
5793
|
const installed = await readInstalledReference(baseDir, skill.id, reference.path);
|
|
4779
5794
|
if (installed !== reference.content) differsFromRemote = true;
|
|
4780
5795
|
const recordedSha = record?.references?.[reference.path];
|
|
4781
|
-
if (installed !== void 0 && recordedSha &&
|
|
5796
|
+
if (installed !== void 0 && recordedSha && sha2563(installed) !== recordedSha) editedLocally = true;
|
|
4782
5797
|
}
|
|
4783
5798
|
if (!differsFromRemote) {
|
|
4784
5799
|
entries.push({ id: skill.id, status: "unchanged" });
|
|
@@ -4814,31 +5829,36 @@ async function skillsUpdateCommand(args) {
|
|
|
4814
5829
|
const layout = await resolveInstallLayout(args);
|
|
4815
5830
|
const state = await readSkillsState(layout.baseDir);
|
|
4816
5831
|
if (!state || Object.keys(state.skills).length === 0) {
|
|
4817
|
-
throw new
|
|
4818
|
-
code:
|
|
4819
|
-
message: `${
|
|
5832
|
+
throw new CliError16({
|
|
5833
|
+
code: CliErrorCode15.INVALID_ARGUMENT,
|
|
5834
|
+
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`
|
|
4820
5835
|
});
|
|
4821
5836
|
}
|
|
4822
|
-
const endpoint =
|
|
4823
|
-
|
|
4824
|
-
|
|
5837
|
+
const endpoint = decideTrackedEndpoint({
|
|
5838
|
+
explicit: Boolean(args.endpoint || args.env),
|
|
5839
|
+
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5840
|
+
recorded: state.endpoint
|
|
4825
5841
|
});
|
|
4826
5842
|
const { http, base } = await chironClient({ ...args, endpoint });
|
|
4827
5843
|
const manifest2 = await fetchSkillCatalog(http, base);
|
|
5844
|
+
const all = Boolean(args.all) || Boolean(state.all);
|
|
4828
5845
|
const plan = await planSkillUpdate({
|
|
4829
5846
|
state,
|
|
4830
5847
|
manifest: manifest2,
|
|
4831
5848
|
baseDir: layout.baseDir,
|
|
4832
5849
|
force: Boolean(args.force),
|
|
5850
|
+
all,
|
|
4833
5851
|
fetchDocument: (skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
4834
5852
|
});
|
|
4835
|
-
|
|
5853
|
+
const subscriptionChanged = all && !state.all;
|
|
5854
|
+
if (!args.check && (plan.documents.length > 0 || subscriptionChanged)) {
|
|
4836
5855
|
await writeSkillDocuments(plan.documents, layout.baseDir);
|
|
4837
5856
|
await recordInstalled(layout.baseDir, plan.documents, {
|
|
4838
5857
|
endpoint,
|
|
4839
5858
|
base,
|
|
4840
5859
|
target: layout.target,
|
|
4841
|
-
|
|
5860
|
+
all,
|
|
5861
|
+
directIds: all ? new Set(plan.documents.map((document) => document.id)) : /* @__PURE__ */ new Set()
|
|
4842
5862
|
});
|
|
4843
5863
|
if (layout.agentsFile) await syncAgentsIndex(layout, manifest2);
|
|
4844
5864
|
}
|
|
@@ -4850,6 +5870,7 @@ async function skillsUpdateCommand(args) {
|
|
|
4850
5870
|
baseDir: layout.baseDir,
|
|
4851
5871
|
target: layout.target,
|
|
4852
5872
|
dryRun: Boolean(args.check),
|
|
5873
|
+
all,
|
|
4853
5874
|
changed: changed.length,
|
|
4854
5875
|
entries: plan.entries
|
|
4855
5876
|
}, { format: fmt }) + "\n");
|
|
@@ -4857,9 +5878,7 @@ async function skillsUpdateCommand(args) {
|
|
|
4857
5878
|
}
|
|
4858
5879
|
process.stdout.write(`\u777F\u620E\u6280\u80FD\u5347\u7EA7 \u2014 ${endpoint}
|
|
4859
5880
|
`);
|
|
4860
|
-
process.stdout.write(`\u843D\u70B9 ${layout.baseDir}\uFF08${layout.target}\uFF09
|
|
4861
|
-
|
|
4862
|
-
`);
|
|
5881
|
+
process.stdout.write(`\u843D\u70B9 ${layout.baseDir}\uFF08${layout.target}\uFF09` + (all ? " \u8DDF\u968F\u5168\u96C6\uFF1A\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4F1A\u81EA\u52A8\u8865\u4E0A\n\n" : "\n\n"));
|
|
4863
5882
|
for (const entry of plan.entries) {
|
|
4864
5883
|
const mark = entry.status === "unchanged" ? " " : entry.status === "locally-modified" ? "!" : "\xB7";
|
|
4865
5884
|
process.stdout.write(` ${mark} ${entry.id.padEnd(30)} ${UPDATE_STATUS_LABEL[entry.status]}
|
|
@@ -4896,8 +5915,8 @@ import {
|
|
|
4896
5915
|
loadEnv as loadEnv6,
|
|
4897
5916
|
loadGlobalConfig as loadGlobalConfig7,
|
|
4898
5917
|
resolveActiveEnv as resolveActiveEnv4,
|
|
4899
|
-
CliError as
|
|
4900
|
-
CliErrorCode as
|
|
5918
|
+
CliError as CliError17,
|
|
5919
|
+
CliErrorCode as CliErrorCode16
|
|
4901
5920
|
} from "@hcmai/sdk";
|
|
4902
5921
|
init_exit();
|
|
4903
5922
|
async function resolveBootstrapKey(args, stdinTaken) {
|
|
@@ -4905,8 +5924,8 @@ async function resolveBootstrapKey(args, stdinTaken) {
|
|
|
4905
5924
|
if (fromEnv) return fromEnv;
|
|
4906
5925
|
if (args.bootstrapKeyStdin) {
|
|
4907
5926
|
if (stdinTaken) {
|
|
4908
|
-
throw new
|
|
4909
|
-
code:
|
|
5927
|
+
throw new CliError17({
|
|
5928
|
+
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
4910
5929
|
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"
|
|
4911
5930
|
});
|
|
4912
5931
|
}
|
|
@@ -4936,8 +5955,8 @@ async function resolveEndpoint2(args) {
|
|
|
4936
5955
|
const envName = resolveActiveEnv4({ envFlag: args.env }, process.env, g);
|
|
4937
5956
|
const env2 = await loadEnv6(envName).catch(() => null);
|
|
4938
5957
|
if (!env2?.endpoint) {
|
|
4939
|
-
throw new
|
|
4940
|
-
code:
|
|
5958
|
+
throw new CliError17({
|
|
5959
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
4941
5960
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`"
|
|
4942
5961
|
});
|
|
4943
5962
|
}
|
|
@@ -4946,8 +5965,8 @@ async function resolveEndpoint2(args) {
|
|
|
4946
5965
|
async function tenantBootstrapCommand(args) {
|
|
4947
5966
|
try {
|
|
4948
5967
|
if (!args.tenantId || !args.adminUsername) {
|
|
4949
|
-
throw new
|
|
4950
|
-
code:
|
|
5968
|
+
throw new CliError17({
|
|
5969
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
4951
5970
|
message: "--tenant-id \u4E0E --admin-username \u5FC5\u586B"
|
|
4952
5971
|
});
|
|
4953
5972
|
}
|
|
@@ -4969,7 +5988,7 @@ async function tenantBootstrapCommand(args) {
|
|
|
4969
5988
|
onRefresh: async () => {
|
|
4970
5989
|
}
|
|
4971
5990
|
});
|
|
4972
|
-
const
|
|
5991
|
+
const result2 = await bootstrapTenant(http, key, {
|
|
4973
5992
|
tenantId: args.tenantId,
|
|
4974
5993
|
adminUsername: args.adminUsername,
|
|
4975
5994
|
adminPassword: pwd,
|
|
@@ -4978,12 +5997,12 @@ async function tenantBootstrapCommand(args) {
|
|
|
4978
5997
|
const fmt = args.output ?? "json";
|
|
4979
5998
|
if (fmt !== "json") {
|
|
4980
5999
|
process.stdout.write(
|
|
4981
|
-
`\u2705 \u79DF\u6237 ${
|
|
6000
|
+
`\u2705 \u79DF\u6237 ${result2.tenantId} \u5DF2\u5F00\u901A\uFF08adminUserId ${result2.adminUserId ?? "-"}\uFF09
|
|
4982
6001
|
\u4E0B\u4E00\u6B65\uFF1Ahcm env add <name> --endpoint ${endpoint} && hcm login --env <name>
|
|
4983
6002
|
`
|
|
4984
6003
|
);
|
|
4985
6004
|
}
|
|
4986
|
-
process.stdout.write(formatObject14(
|
|
6005
|
+
process.stdout.write(formatObject14(result2, { format: fmt }) + "\n");
|
|
4987
6006
|
process.exit(0);
|
|
4988
6007
|
} catch (e) {
|
|
4989
6008
|
const err = e;
|
|
@@ -5007,8 +6026,8 @@ import {
|
|
|
5007
6026
|
getTenantMeta,
|
|
5008
6027
|
saveTenantMeta,
|
|
5009
6028
|
deleteTenantMeta,
|
|
5010
|
-
CliError as
|
|
5011
|
-
CliErrorCode as
|
|
6029
|
+
CliError as CliError18,
|
|
6030
|
+
CliErrorCode as CliErrorCode17
|
|
5012
6031
|
} from "@hcmai/sdk";
|
|
5013
6032
|
init_exit();
|
|
5014
6033
|
init_auth_guard();
|
|
@@ -5044,21 +6063,21 @@ async function metaListCommand(args) {
|
|
|
5044
6063
|
fail4(e, args);
|
|
5045
6064
|
}
|
|
5046
6065
|
}
|
|
5047
|
-
async function metaGetCommand(
|
|
6066
|
+
async function metaGetCommand(path7, args) {
|
|
5048
6067
|
try {
|
|
5049
6068
|
const c = await client2(args);
|
|
5050
|
-
const content = await getTenantMeta(c.raw(),
|
|
6069
|
+
const content = await getTenantMeta(c.raw(), path7);
|
|
5051
6070
|
process.stdout.write(content.endsWith("\n") ? content : content + "\n");
|
|
5052
6071
|
process.exit(0);
|
|
5053
6072
|
} catch (e) {
|
|
5054
6073
|
fail4(e, args);
|
|
5055
6074
|
}
|
|
5056
6075
|
}
|
|
5057
|
-
async function metaSetCommand(
|
|
6076
|
+
async function metaSetCommand(path7, args) {
|
|
5058
6077
|
try {
|
|
5059
6078
|
if (!args.file) {
|
|
5060
|
-
throw new
|
|
5061
|
-
code:
|
|
6079
|
+
throw new CliError18({
|
|
6080
|
+
code: CliErrorCode17.MISSING_FLAG,
|
|
5062
6081
|
message: "--file <path> \u5FC5\u586B\uFF08\u4ECE\u6587\u4EF6\u8BFB\u53D6 meta \u539F\u6587\uFF1B\u7528 - \u8868\u793A stdin\uFF09"
|
|
5063
6082
|
});
|
|
5064
6083
|
}
|
|
@@ -5071,7 +6090,7 @@ async function metaSetCommand(path4, args) {
|
|
|
5071
6090
|
}) : await fsp2.readFile(args.file, "utf8");
|
|
5072
6091
|
if (args.dryRun) {
|
|
5073
6092
|
process.stdout.write(
|
|
5074
|
-
`[dry-run] \u5C06\u5199\u5165 ${
|
|
6093
|
+
`[dry-run] \u5C06\u5199\u5165 ${path7}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
|
|
5075
6094
|
---
|
|
5076
6095
|
${content}
|
|
5077
6096
|
`
|
|
@@ -5079,35 +6098,35 @@ ${content}
|
|
|
5079
6098
|
process.exit(0);
|
|
5080
6099
|
}
|
|
5081
6100
|
const c = await client2(args);
|
|
5082
|
-
const
|
|
6101
|
+
const result2 = await saveTenantMeta(c.raw(), path7, content);
|
|
5083
6102
|
const fmt = args.output ?? "json";
|
|
5084
6103
|
if (fmt !== "json") {
|
|
5085
|
-
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${
|
|
6104
|
+
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path7}
|
|
5086
6105
|
\u8BB0\u5F97\u6E05\u7F13\u5B58\uFF1Ahcm cache clear-meta <Model>
|
|
5087
6106
|
`);
|
|
5088
6107
|
}
|
|
5089
|
-
process.stdout.write(formatObject15(
|
|
6108
|
+
process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
|
|
5090
6109
|
process.exit(0);
|
|
5091
6110
|
} catch (e) {
|
|
5092
6111
|
if (e?.code === "ENOENT") exitWithError(new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${args.file}`));
|
|
5093
6112
|
fail4(e, args);
|
|
5094
6113
|
}
|
|
5095
6114
|
}
|
|
5096
|
-
async function metaDeleteCommand(
|
|
6115
|
+
async function metaDeleteCommand(path7, args) {
|
|
5097
6116
|
try {
|
|
5098
6117
|
if (!args.yes) {
|
|
5099
|
-
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${
|
|
6118
|
+
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path7}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
|
|
5100
6119
|
if (!ok) {
|
|
5101
6120
|
process.stderr.write("\u5DF2\u53D6\u6D88\n");
|
|
5102
6121
|
process.exit(1);
|
|
5103
6122
|
}
|
|
5104
6123
|
}
|
|
5105
6124
|
const c = await client2(args);
|
|
5106
|
-
const
|
|
6125
|
+
const result2 = await deleteTenantMeta(c.raw(), path7);
|
|
5107
6126
|
const fmt = args.output ?? "json";
|
|
5108
|
-
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${
|
|
6127
|
+
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path7}
|
|
5109
6128
|
`);
|
|
5110
|
-
process.stdout.write(formatObject15(
|
|
6129
|
+
process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
|
|
5111
6130
|
process.exit(0);
|
|
5112
6131
|
} catch (e) {
|
|
5113
6132
|
fail4(e, args);
|
|
@@ -5118,8 +6137,8 @@ async function metaDeleteCommand(path4, args) {
|
|
|
5118
6137
|
init_exit();
|
|
5119
6138
|
init_auth_guard();
|
|
5120
6139
|
import * as crypto from "crypto";
|
|
5121
|
-
import * as
|
|
5122
|
-
import * as
|
|
6140
|
+
import * as fs6 from "fs/promises";
|
|
6141
|
+
import * as path5 from "path";
|
|
5123
6142
|
import {
|
|
5124
6143
|
buildMiniPreviewTargets,
|
|
5125
6144
|
buildMiniVerifyAgentReport,
|
|
@@ -5188,7 +6207,7 @@ async function workspaceListCommand(args) {
|
|
|
5188
6207
|
try {
|
|
5189
6208
|
const client3 = HcmClient16.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
5190
6209
|
const limit = parsePositiveInt3(args.limit, 50, "--limit");
|
|
5191
|
-
const
|
|
6210
|
+
const result2 = await client3.query("TenantExtensionWorkspace", {
|
|
5192
6211
|
keyword: args.keyword,
|
|
5193
6212
|
fields: [
|
|
5194
6213
|
"workspaceKey",
|
|
@@ -5203,7 +6222,7 @@ async function workspaceListCommand(args) {
|
|
|
5203
6222
|
sort: [{ field: "devModeEnabled", desc: true }, { field: "updateTime", desc: true }],
|
|
5204
6223
|
limit
|
|
5205
6224
|
});
|
|
5206
|
-
writeWorkspaceListResult(
|
|
6225
|
+
writeWorkspaceListResult(result2.rows, args.output ?? "table", result2.total);
|
|
5207
6226
|
process.exit(0);
|
|
5208
6227
|
} catch (e) {
|
|
5209
6228
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -5217,12 +6236,12 @@ async function workspaceActiveCommand(args) {
|
|
|
5217
6236
|
fallbackEnv: localContext?.profile
|
|
5218
6237
|
});
|
|
5219
6238
|
const client3 = HcmClient16.fromAuthContext(ctx);
|
|
5220
|
-
const
|
|
6239
|
+
const result2 = await client3.query("TenantExtensionWorkspace", {
|
|
5221
6240
|
filter: { devModeEnabled: true },
|
|
5222
6241
|
fields: ["workspaceKey", "devModeEnabled"],
|
|
5223
6242
|
limit: 1
|
|
5224
6243
|
});
|
|
5225
|
-
const activeWorkspaceKey = optionalText(
|
|
6244
|
+
const activeWorkspaceKey = optionalText(result2.rows[0]?.workspaceKey) ?? null;
|
|
5226
6245
|
const view = {
|
|
5227
6246
|
activeWorkspaceKey,
|
|
5228
6247
|
localWorkspaceKey: localContext?.workspaceKey ?? null,
|
|
@@ -5254,7 +6273,7 @@ async function workspaceBindCommand(workspaceKey, args) {
|
|
|
5254
6273
|
tenantId: args.tenant ?? client3?.tenantId,
|
|
5255
6274
|
endpoint: args.endpoint ?? client3?.endpoint
|
|
5256
6275
|
});
|
|
5257
|
-
await
|
|
6276
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5258
6277
|
await writeWorkspaceContext(rootDir, context);
|
|
5259
6278
|
writeWorkspaceBindResult(context, rootDir, args.output ?? "table");
|
|
5260
6279
|
process.exit(0);
|
|
@@ -5289,7 +6308,7 @@ async function workspaceCreateCommand(workspaceKey, args) {
|
|
|
5289
6308
|
tenantId: client3.tenantId,
|
|
5290
6309
|
endpoint: client3.endpoint
|
|
5291
6310
|
});
|
|
5292
|
-
await
|
|
6311
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5293
6312
|
await writeWorkspaceContext(rootDir, context);
|
|
5294
6313
|
writeWorkspaceCreateResult(
|
|
5295
6314
|
{
|
|
@@ -5326,23 +6345,23 @@ async function workspacePullCommand(args) {
|
|
|
5326
6345
|
tenantId: client3.tenantId ?? existing?.tenantId,
|
|
5327
6346
|
endpoint: client3.endpoint ?? existing?.endpoint
|
|
5328
6347
|
});
|
|
5329
|
-
await
|
|
6348
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5330
6349
|
await writeWorkspaceContext(rootDir, context);
|
|
5331
6350
|
const files = await listWorkspaceFiles(client3.raw(), workspaceKey, { pageSize: 5e3 });
|
|
5332
6351
|
const miniApps = detectRemoteMiniApps(files.data);
|
|
5333
6352
|
const pulledMiniApps = [];
|
|
5334
6353
|
for (const appCode of miniApps) {
|
|
5335
|
-
const
|
|
6354
|
+
const result2 = await pullMiniAppProject({
|
|
5336
6355
|
http: client3.raw(),
|
|
5337
6356
|
appCode,
|
|
5338
6357
|
workspaceKey,
|
|
5339
|
-
targetDir:
|
|
6358
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
5340
6359
|
tenantId: client3.tenantId,
|
|
5341
6360
|
endpoint: client3.endpoint,
|
|
5342
6361
|
profile: args.profile ?? client3.profile,
|
|
5343
6362
|
force: !!args.force
|
|
5344
6363
|
});
|
|
5345
|
-
pulledMiniApps.push(
|
|
6364
|
+
pulledMiniApps.push(result2);
|
|
5346
6365
|
}
|
|
5347
6366
|
const rawFiles = files.data.filter(
|
|
5348
6367
|
(file) => !isDirectoryMarker(file.relativePath) && !isMiniAppPath(file.relativePath)
|
|
@@ -5351,7 +6370,7 @@ async function workspacePullCommand(args) {
|
|
|
5351
6370
|
const rawRemoteByLocalPath = /* @__PURE__ */ new Map();
|
|
5352
6371
|
for (const file of rawFiles) {
|
|
5353
6372
|
const content = await loadWorkspaceFileContent(client3.raw(), file.id);
|
|
5354
|
-
const target =
|
|
6373
|
+
const target = path5.join(rootDir, content.relativePath);
|
|
5355
6374
|
await writeTextFile(target, content.content, !!args.force);
|
|
5356
6375
|
pulledRawFiles.push(content.relativePath);
|
|
5357
6376
|
rawRemoteByLocalPath.set(content.relativePath, file);
|
|
@@ -5398,10 +6417,10 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
5398
6417
|
fields: args.fields,
|
|
5399
6418
|
defaults: args.defaults
|
|
5400
6419
|
});
|
|
5401
|
-
const
|
|
6420
|
+
const result2 = await initMiniAppProject({
|
|
5402
6421
|
appCode,
|
|
5403
6422
|
workspaceKey: context.workspaceKey,
|
|
5404
|
-
targetDir:
|
|
6423
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
5405
6424
|
name: args.name,
|
|
5406
6425
|
defaultSurface: parseSurface(args.surface),
|
|
5407
6426
|
templateKind,
|
|
@@ -5415,7 +6434,7 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
5415
6434
|
profile: args.env ?? args.profile ?? client3?.profile ?? context.profile,
|
|
5416
6435
|
force: !!args.force
|
|
5417
6436
|
});
|
|
5418
|
-
writeWorkspaceAddMiniResult(
|
|
6437
|
+
writeWorkspaceAddMiniResult(result2, rootDir, args.output ?? "table");
|
|
5419
6438
|
process.exit(0);
|
|
5420
6439
|
} catch (e) {
|
|
5421
6440
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -5495,12 +6514,12 @@ async function workspacePushCommand(args) {
|
|
|
5495
6514
|
});
|
|
5496
6515
|
const results = [];
|
|
5497
6516
|
for (const app of miniApps) {
|
|
5498
|
-
const
|
|
6517
|
+
const result2 = await pushMiniAppProject({
|
|
5499
6518
|
http: client3.raw(),
|
|
5500
6519
|
rootDir: app.dir,
|
|
5501
6520
|
dryRun: !!args.dryRun
|
|
5502
6521
|
});
|
|
5503
|
-
results.push({ app, result });
|
|
6522
|
+
results.push({ app, result: result2 });
|
|
5504
6523
|
}
|
|
5505
6524
|
writeWorkspacePushResult(rawResult, results, args.output ?? "table");
|
|
5506
6525
|
process.exit(
|
|
@@ -5642,7 +6661,7 @@ function removedMiniCommand() {
|
|
|
5642
6661
|
);
|
|
5643
6662
|
}
|
|
5644
6663
|
function resolveRootDir(dir) {
|
|
5645
|
-
return
|
|
6664
|
+
return path5.resolve(dir ?? ".");
|
|
5646
6665
|
}
|
|
5647
6666
|
function buildWorkspaceContext(args) {
|
|
5648
6667
|
return {
|
|
@@ -5658,7 +6677,7 @@ function buildWorkspaceContext(args) {
|
|
|
5658
6677
|
async function readWorkspaceContext(rootDir) {
|
|
5659
6678
|
try {
|
|
5660
6679
|
const parsed = JSON.parse(
|
|
5661
|
-
await
|
|
6680
|
+
await fs6.readFile(path5.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
|
|
5662
6681
|
);
|
|
5663
6682
|
if (parsed.kind !== WORKSPACE_CONTEXT_KIND || parsed.version !== WORKSPACE_CONTEXT_VERSION) {
|
|
5664
6683
|
throw new Error(`${WORKSPACE_CONTEXT_FILE} is not an HCM workspace context`);
|
|
@@ -5687,25 +6706,25 @@ async function requireWorkspaceContext(rootDir) {
|
|
|
5687
6706
|
return context;
|
|
5688
6707
|
}
|
|
5689
6708
|
async function writeWorkspaceContext(rootDir, context) {
|
|
5690
|
-
await
|
|
5691
|
-
|
|
6709
|
+
await fs6.writeFile(
|
|
6710
|
+
path5.join(rootDir, WORKSPACE_CONTEXT_FILE),
|
|
5692
6711
|
JSON.stringify(context, null, 2) + "\n"
|
|
5693
6712
|
);
|
|
5694
6713
|
}
|
|
5695
6714
|
async function writeTextFile(file, content, force) {
|
|
5696
6715
|
try {
|
|
5697
|
-
await
|
|
6716
|
+
await fs6.access(file);
|
|
5698
6717
|
if (!force) throw new Error(`${file} already exists; use --force to overwrite`);
|
|
5699
6718
|
} catch (e) {
|
|
5700
6719
|
if (e.code !== "ENOENT") throw e;
|
|
5701
6720
|
}
|
|
5702
|
-
await
|
|
5703
|
-
await
|
|
6721
|
+
await fs6.mkdir(path5.dirname(file), { recursive: true });
|
|
6722
|
+
await fs6.writeFile(file, content);
|
|
5704
6723
|
}
|
|
5705
6724
|
async function readWorkspaceRawManifest(rootDir, context) {
|
|
5706
6725
|
try {
|
|
5707
6726
|
const parsed = JSON.parse(
|
|
5708
|
-
await
|
|
6727
|
+
await fs6.readFile(path5.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
|
|
5709
6728
|
);
|
|
5710
6729
|
if (parsed.version !== WORKSPACE_CONTEXT_VERSION || parsed.workspaceKey !== context.workspaceKey) {
|
|
5711
6730
|
throw new Error(`${WORKSPACE_PULL_MANIFEST_FILE} does not match workspace ${context.workspaceKey}`);
|
|
@@ -5723,8 +6742,8 @@ async function readWorkspaceRawManifest(rootDir, context) {
|
|
|
5723
6742
|
}
|
|
5724
6743
|
}
|
|
5725
6744
|
async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath) {
|
|
5726
|
-
const stateDir =
|
|
5727
|
-
await
|
|
6745
|
+
const stateDir = path5.join(rootDir, WORKSPACE_STATE_DIR);
|
|
6746
|
+
await fs6.mkdir(stateDir, { recursive: true });
|
|
5728
6747
|
const snapshots = files.map((file) => ({
|
|
5729
6748
|
localPath: file.localPath,
|
|
5730
6749
|
remotePath: file.localPath,
|
|
@@ -5733,8 +6752,8 @@ async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath
|
|
|
5733
6752
|
size: file.size,
|
|
5734
6753
|
lastModified: remoteByLocalPath.get(file.localPath)?.lastModified
|
|
5735
6754
|
}));
|
|
5736
|
-
await
|
|
5737
|
-
|
|
6755
|
+
await fs6.writeFile(
|
|
6756
|
+
path5.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
|
|
5738
6757
|
JSON.stringify(
|
|
5739
6758
|
{
|
|
5740
6759
|
version: WORKSPACE_CONTEXT_VERSION,
|
|
@@ -5756,10 +6775,10 @@ async function readWorkspaceRawLocalFiles(rootDir) {
|
|
|
5756
6775
|
return files;
|
|
5757
6776
|
}
|
|
5758
6777
|
async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
5759
|
-
const absoluteDir =
|
|
6778
|
+
const absoluteDir = path5.join(rootDir, relativeDir);
|
|
5760
6779
|
let entries;
|
|
5761
6780
|
try {
|
|
5762
|
-
entries = await
|
|
6781
|
+
entries = await fs6.readdir(absoluteDir, { withFileTypes: true });
|
|
5763
6782
|
} catch (e) {
|
|
5764
6783
|
if (e.code === "ENOENT") return;
|
|
5765
6784
|
throw e;
|
|
@@ -5767,19 +6786,19 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
|
5767
6786
|
for (const entry of entries) {
|
|
5768
6787
|
if (entry.isDirectory() && RAW_WORKSPACE_EXCLUDED_DIRS.has(entry.name)) continue;
|
|
5769
6788
|
if (entry.isFile() && RAW_WORKSPACE_EXCLUDED_FILES.has(entry.name)) continue;
|
|
5770
|
-
const localPath = normalizeRelativePath(
|
|
5771
|
-
const absolutePath =
|
|
6789
|
+
const localPath = normalizeRelativePath(path5.posix.join(toPosix(relativeDir), entry.name));
|
|
6790
|
+
const absolutePath = path5.join(rootDir, localPath);
|
|
5772
6791
|
if (entry.isDirectory()) {
|
|
5773
6792
|
await walkWorkspaceRawFiles(rootDir, localPath, files);
|
|
5774
6793
|
continue;
|
|
5775
6794
|
}
|
|
5776
6795
|
if (!entry.isFile()) continue;
|
|
5777
|
-
const content = await
|
|
6796
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
5778
6797
|
files.push({
|
|
5779
6798
|
localPath,
|
|
5780
6799
|
absolutePath,
|
|
5781
6800
|
content,
|
|
5782
|
-
sha256:
|
|
6801
|
+
sha256: sha2564(content),
|
|
5783
6802
|
size: Buffer.byteLength(content, "utf-8")
|
|
5784
6803
|
});
|
|
5785
6804
|
}
|
|
@@ -5793,13 +6812,13 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
5793
6812
|
const localFiles = await readWorkspaceRawLocalFiles(args.rootDir);
|
|
5794
6813
|
const remoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
5795
6814
|
const plan = buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles);
|
|
5796
|
-
const
|
|
6815
|
+
const result2 = {
|
|
5797
6816
|
workspaceKey: args.context.workspaceKey,
|
|
5798
6817
|
dryRun: args.dryRun,
|
|
5799
6818
|
operations: plan.operations,
|
|
5800
6819
|
conflicts: plan.conflicts
|
|
5801
6820
|
};
|
|
5802
|
-
if (args.dryRun || plan.conflicts.length) return
|
|
6821
|
+
if (args.dryRun || plan.conflicts.length) return result2;
|
|
5803
6822
|
const localByPath = new Map(localFiles.map((file) => [file.localPath, file]));
|
|
5804
6823
|
for (const operation of plan.operations) {
|
|
5805
6824
|
if (operation.operation === "skip") continue;
|
|
@@ -5825,17 +6844,17 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
5825
6844
|
const latestRemoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
5826
6845
|
const latestRemoteByLocalPath = new Map(latestRemoteFiles.map((file) => [file.item.relativePath, file.item]));
|
|
5827
6846
|
await writeWorkspaceRawState(args.rootDir, args.context, localFiles, latestRemoteByLocalPath);
|
|
5828
|
-
return
|
|
6847
|
+
return result2;
|
|
5829
6848
|
}
|
|
5830
6849
|
async function loadWorkspaceRawRemoteFiles(http, workspaceKey) {
|
|
5831
6850
|
const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5e3 });
|
|
5832
|
-
const
|
|
6851
|
+
const result2 = [];
|
|
5833
6852
|
for (const item of list.data) {
|
|
5834
6853
|
if (isDirectoryMarker(item.relativePath) || isMiniAppPath(item.relativePath)) continue;
|
|
5835
6854
|
const content = await loadWorkspaceFileContent(http, item.id);
|
|
5836
|
-
|
|
6855
|
+
result2.push({ item, content: content.content });
|
|
5837
6856
|
}
|
|
5838
|
-
return
|
|
6857
|
+
return result2;
|
|
5839
6858
|
}
|
|
5840
6859
|
function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
5841
6860
|
const status = computeWorkspaceRawStatus(manifest2, localFiles);
|
|
@@ -5846,7 +6865,7 @@ function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
|
5846
6865
|
for (const entry of status) {
|
|
5847
6866
|
const remote = remoteByLocal.get(entry.localPath);
|
|
5848
6867
|
const base = baseByLocal.get(entry.localPath);
|
|
5849
|
-
const remoteSha = remote ?
|
|
6868
|
+
const remoteSha = remote ? sha2564(remote.content) : void 0;
|
|
5850
6869
|
if (entry.status === "added") {
|
|
5851
6870
|
if (remote) {
|
|
5852
6871
|
conflicts.push(workspaceRawConflict(entry, "remote file already exists but was not in local snapshot"));
|
|
@@ -5948,15 +6967,15 @@ function workspaceRawConflict(entry, reason) {
|
|
|
5948
6967
|
};
|
|
5949
6968
|
}
|
|
5950
6969
|
async function discoverMiniApps(rootDir) {
|
|
5951
|
-
const appsRoot =
|
|
6970
|
+
const appsRoot = path5.join(rootDir, MINI_APPS_DIR);
|
|
5952
6971
|
let entries;
|
|
5953
6972
|
try {
|
|
5954
|
-
entries = await
|
|
6973
|
+
entries = await fs6.readdir(appsRoot, { withFileTypes: true });
|
|
5955
6974
|
} catch (e) {
|
|
5956
6975
|
if (e.code === "ENOENT") return [];
|
|
5957
6976
|
throw e;
|
|
5958
6977
|
}
|
|
5959
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir:
|
|
6978
|
+
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));
|
|
5960
6979
|
}
|
|
5961
6980
|
function detectRemoteMiniApps(files) {
|
|
5962
6981
|
const appCodes = /* @__PURE__ */ new Set();
|
|
@@ -6045,17 +7064,17 @@ function parseModelKey(value) {
|
|
|
6045
7064
|
}
|
|
6046
7065
|
return model;
|
|
6047
7066
|
}
|
|
6048
|
-
function parseJsonObject(value,
|
|
7067
|
+
function parseJsonObject(value, flagName2) {
|
|
6049
7068
|
if (!value) return void 0;
|
|
6050
7069
|
if (isAuto(value)) return void 0;
|
|
6051
7070
|
let parsed;
|
|
6052
7071
|
try {
|
|
6053
7072
|
parsed = JSON.parse(value);
|
|
6054
7073
|
} catch {
|
|
6055
|
-
throw new Error(`${
|
|
7074
|
+
throw new Error(`${flagName2} must be valid JSON object`);
|
|
6056
7075
|
}
|
|
6057
7076
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6058
|
-
throw new Error(`${
|
|
7077
|
+
throw new Error(`${flagName2} must be valid JSON object`);
|
|
6059
7078
|
}
|
|
6060
7079
|
return parsed;
|
|
6061
7080
|
}
|
|
@@ -6109,9 +7128,9 @@ function normalizeRelativePath(value) {
|
|
|
6109
7128
|
return normalized;
|
|
6110
7129
|
}
|
|
6111
7130
|
function toPosix(value) {
|
|
6112
|
-
return value.split(
|
|
7131
|
+
return value.split(path5.sep).join("/");
|
|
6113
7132
|
}
|
|
6114
|
-
function
|
|
7133
|
+
function sha2564(content) {
|
|
6115
7134
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
6116
7135
|
}
|
|
6117
7136
|
function stringRequired(value, field) {
|
|
@@ -6178,52 +7197,52 @@ function writeWorkspaceBindResult(context, rootDir, format) {
|
|
|
6178
7197
|
].join("\n") + "\n"
|
|
6179
7198
|
);
|
|
6180
7199
|
}
|
|
6181
|
-
function writeWorkspaceCreateResult(
|
|
7200
|
+
function writeWorkspaceCreateResult(result2, format) {
|
|
6182
7201
|
if (format !== "table") {
|
|
6183
|
-
process.stdout.write(formatObject16(
|
|
7202
|
+
process.stdout.write(formatObject16(result2, { format }) + "\n");
|
|
6184
7203
|
return;
|
|
6185
7204
|
}
|
|
6186
7205
|
process.stdout.write(
|
|
6187
7206
|
[
|
|
6188
|
-
`workspace created: ${
|
|
6189
|
-
`id: ${
|
|
6190
|
-
`directory: ${
|
|
6191
|
-
`profile: ${
|
|
6192
|
-
`tenant: ${
|
|
6193
|
-
`activated: ${
|
|
7207
|
+
`workspace created: ${result2.workspaceKey}`,
|
|
7208
|
+
`id: ${result2.id}`,
|
|
7209
|
+
`directory: ${result2.rootDir}`,
|
|
7210
|
+
`profile: ${result2.profile}`,
|
|
7211
|
+
`tenant: ${result2.tenantId}`,
|
|
7212
|
+
`activated: ${result2.activated ? "yes" : "no"}`
|
|
6194
7213
|
].join("\n") + "\n"
|
|
6195
7214
|
);
|
|
6196
7215
|
}
|
|
6197
|
-
function writeWorkspacePullResult(
|
|
7216
|
+
function writeWorkspacePullResult(result2, format) {
|
|
6198
7217
|
if (format !== "table") {
|
|
6199
|
-
process.stdout.write(formatObject16(
|
|
7218
|
+
process.stdout.write(formatObject16(result2, { format }) + "\n");
|
|
6200
7219
|
return;
|
|
6201
7220
|
}
|
|
6202
7221
|
const lines = [
|
|
6203
|
-
`workspace pulled: ${
|
|
6204
|
-
`directory: ${
|
|
6205
|
-
`remote files: ${
|
|
6206
|
-
`mini apps: ${
|
|
6207
|
-
`raw files: ${
|
|
7222
|
+
`workspace pulled: ${result2.workspaceKey}`,
|
|
7223
|
+
`directory: ${result2.rootDir}`,
|
|
7224
|
+
`remote files: ${result2.totalRemoteFiles}`,
|
|
7225
|
+
`mini apps: ${result2.miniApps.length}`,
|
|
7226
|
+
`raw files: ${result2.rawFiles.length}`
|
|
6208
7227
|
];
|
|
6209
|
-
if (
|
|
7228
|
+
if (result2.miniApps.length) {
|
|
6210
7229
|
lines.push("mini artifacts:");
|
|
6211
|
-
for (const app of
|
|
7230
|
+
for (const app of result2.miniApps) lines.push(` - ${app.appCode}: ${app.files} files`);
|
|
6212
7231
|
}
|
|
6213
7232
|
process.stdout.write(lines.join("\n") + "\n");
|
|
6214
7233
|
}
|
|
6215
|
-
function writeWorkspaceAddMiniResult(
|
|
7234
|
+
function writeWorkspaceAddMiniResult(result2, rootDir, format) {
|
|
6216
7235
|
if (format !== "table") {
|
|
6217
|
-
process.stdout.write(formatObject16({ ...
|
|
7236
|
+
process.stdout.write(formatObject16({ ...result2, workspaceRoot: rootDir }, { format }) + "\n");
|
|
6218
7237
|
return;
|
|
6219
7238
|
}
|
|
6220
7239
|
process.stdout.write(
|
|
6221
7240
|
[
|
|
6222
|
-
`workspace artifact added: mini:${
|
|
6223
|
-
`workspace: ${
|
|
7241
|
+
`workspace artifact added: mini:${result2.appCode}`,
|
|
7242
|
+
`workspace: ${result2.workspaceKey}`,
|
|
6224
7243
|
`workspace root: ${rootDir}`,
|
|
6225
|
-
`directory: ${
|
|
6226
|
-
`files: ${
|
|
7244
|
+
`directory: ${result2.targetDir}`,
|
|
7245
|
+
`files: ${result2.files.length}`
|
|
6227
7246
|
].join("\n") + "\n"
|
|
6228
7247
|
);
|
|
6229
7248
|
}
|
|
@@ -6408,8 +7427,8 @@ import {
|
|
|
6408
7427
|
deleteIdentity as deleteIdentity2,
|
|
6409
7428
|
loadGlobalConfig as loadGlobalConfig8,
|
|
6410
7429
|
TokenStore as TokenStore3,
|
|
6411
|
-
CliError as
|
|
6412
|
-
CliErrorCode as
|
|
7430
|
+
CliError as CliError19,
|
|
7431
|
+
CliErrorCode as CliErrorCode18
|
|
6413
7432
|
} from "@hcmai/sdk";
|
|
6414
7433
|
async function identityList(args) {
|
|
6415
7434
|
try {
|
|
@@ -6437,8 +7456,8 @@ async function identityUse(name) {
|
|
|
6437
7456
|
try {
|
|
6438
7457
|
const meta2 = await loadIdentity4(name);
|
|
6439
7458
|
const env2 = await loadEnv7(meta2.env).catch(() => {
|
|
6440
|
-
throw new
|
|
6441
|
-
code:
|
|
7459
|
+
throw new CliError19({
|
|
7460
|
+
code: CliErrorCode18.IDENTITY_NOT_IN_ENV,
|
|
6442
7461
|
message: `identity '${name}' references env '${meta2.env}' which no longer exists. Re-create the env, or remove this orphan: hcm identity remove ${name}`
|
|
6443
7462
|
});
|
|
6444
7463
|
});
|
|
@@ -6472,6 +7491,768 @@ async function identityRemove(name) {
|
|
|
6472
7491
|
}
|
|
6473
7492
|
}
|
|
6474
7493
|
|
|
7494
|
+
// src/commands/delivery.ts
|
|
7495
|
+
import { createHash as createHash5 } from "crypto";
|
|
7496
|
+
import { formatObject as formatObject17 } from "@hcmai/sdk";
|
|
7497
|
+
var APPROVAL_BYPASS_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--force", "--auto-approve"]);
|
|
7498
|
+
var CREDENTIAL_FLAGS = /* @__PURE__ */ new Set([
|
|
7499
|
+
"--password",
|
|
7500
|
+
"--password-stdin",
|
|
7501
|
+
"--client-secret",
|
|
7502
|
+
"--pat",
|
|
7503
|
+
"--pat-stdin",
|
|
7504
|
+
"--new-password",
|
|
7505
|
+
"--new-password-stdin"
|
|
7506
|
+
]);
|
|
7507
|
+
var PAYLOAD_FLAGS = ["--data", "--params"];
|
|
7508
|
+
var GUARDED_EXECUTOR_REASON_CODES = /* @__PURE__ */ new Set([
|
|
7509
|
+
"record-create",
|
|
7510
|
+
"record-update",
|
|
7511
|
+
"cache-mutation",
|
|
7512
|
+
"setting-write"
|
|
7513
|
+
]);
|
|
7514
|
+
function assessDeliveryCommand(inputArgv) {
|
|
7515
|
+
const argv = inputArgv[0] === "--" ? inputArgv.slice(1) : [...inputArgv];
|
|
7516
|
+
const commandSha256 = sha2565(JSON.stringify(argv));
|
|
7517
|
+
const name = argv[0] ?? "";
|
|
7518
|
+
const subcommand = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
|
|
7519
|
+
const envAlias = flagValue(argv, "--env");
|
|
7520
|
+
const identityAlias = flagValue(argv, "--as");
|
|
7521
|
+
const sensitiveFlags = [...new Set(PAYLOAD_FLAGS.filter((flag) => hasFlag(argv, flag)))];
|
|
7522
|
+
const target = summarizeTarget(argv);
|
|
7523
|
+
let classification = classify(argv);
|
|
7524
|
+
const invalidPayload = payloadError(argv);
|
|
7525
|
+
if (invalidPayload) {
|
|
7526
|
+
classification = forbidden("invalid-input", "JSON payload \u65E0\u6548\uFF0C\u4E0D\u80FD\u8FDB\u5165\u4EFB\u4F55\u6267\u884C\u8DEF\u5F84\u3002");
|
|
7527
|
+
}
|
|
7528
|
+
const requirements = [];
|
|
7529
|
+
const guardedExecutionAvailable = classification.risk === "R2" && GUARDED_EXECUTOR_REASON_CODES.has(classification.reasonCode);
|
|
7530
|
+
if (classification.risk === "R2") {
|
|
7531
|
+
if (!envAlias) requirements.push("explicit-env");
|
|
7532
|
+
if (!identityAlias) requirements.push("explicit-identity");
|
|
7533
|
+
if (guardedExecutionAvailable) {
|
|
7534
|
+
requirements.push("change-plan", "rollback-plan", "human-approval", "single-use-plan");
|
|
7535
|
+
} else {
|
|
7536
|
+
requirements.push("guarded-executor");
|
|
7537
|
+
}
|
|
7538
|
+
} else if (classification.risk === "R3") {
|
|
7539
|
+
requirements.push("governance-escalation");
|
|
7540
|
+
}
|
|
7541
|
+
const allowed = classification.risk === "R0" || classification.risk === "R1";
|
|
7542
|
+
return {
|
|
7543
|
+
schemaVersion: 1,
|
|
7544
|
+
contractVersion: "2026-08-28",
|
|
7545
|
+
assessmentId: sha2565(`2026-08-28
|
|
7546
|
+
${commandSha256}`).slice(0, 24),
|
|
7547
|
+
commandSha256,
|
|
7548
|
+
command: { name: name || "(empty)", ...subcommand ? { subcommand } : {} },
|
|
7549
|
+
...classification,
|
|
7550
|
+
guardedExecutionAvailable,
|
|
7551
|
+
environment: {
|
|
7552
|
+
...envAlias ? { envAlias } : {},
|
|
7553
|
+
...identityAlias ? { identityAlias } : {}
|
|
7554
|
+
},
|
|
7555
|
+
target,
|
|
7556
|
+
requirements,
|
|
7557
|
+
evidenceRequirements: evidenceFor(classification.risk),
|
|
7558
|
+
sensitiveFlags,
|
|
7559
|
+
modePolicy: {
|
|
7560
|
+
planOnly: allowed ? "allow" : "deny",
|
|
7561
|
+
controlled: allowed || guardedExecutionAvailable ? "allow" : "deny"
|
|
7562
|
+
}
|
|
7563
|
+
};
|
|
7564
|
+
}
|
|
7565
|
+
function deliveryAssessCommand(argv, args) {
|
|
7566
|
+
const assessment = assessDeliveryCommand(argv);
|
|
7567
|
+
process.stdout.write(formatObject17(assessment, { format: args.output ?? "json" }) + "\n");
|
|
7568
|
+
}
|
|
7569
|
+
function classify(argv) {
|
|
7570
|
+
const name = argv[0] ?? "";
|
|
7571
|
+
const subcommand = argv[1] ?? "";
|
|
7572
|
+
if (argv.some((arg) => APPROVAL_BYPASS_FLAGS.has(flagName(arg)))) {
|
|
7573
|
+
return forbidden("approval-bypass", "\u547D\u4EE4\u5305\u542B\u8DF3\u8FC7\u786E\u8BA4\u7684\u65D7\u6807\uFF0C\u987E\u95EE\u4EA4\u4ED8\u9762\u4E0D\u6267\u884C\u3002");
|
|
7574
|
+
}
|
|
7575
|
+
if (argv.some((arg) => CREDENTIAL_FLAGS.has(flagName(arg)))) {
|
|
7576
|
+
return forbidden("credential-governance", "\u547D\u4EE4\u643A\u5E26\u8EAB\u4EFD\u51ED\u636E\u53C2\u6570\uFF0C\u5FC5\u987B\u8F6C\u4EA4\u8EAB\u4EFD\u6CBB\u7406\u6D41\u7A0B\u3002");
|
|
7577
|
+
}
|
|
7578
|
+
if (name === "version") return direct("R0", "product-discovery", "product-discovery");
|
|
7579
|
+
if (name === "delivery" && subcommand === "assess") {
|
|
7580
|
+
return direct("R0", "local-discovery", "risk-assessment");
|
|
7581
|
+
}
|
|
7582
|
+
if (name === "skills") {
|
|
7583
|
+
if (subcommand === "list" || subcommand === "show") {
|
|
7584
|
+
return direct("R0", "product-discovery", "skill-discovery");
|
|
7585
|
+
}
|
|
7586
|
+
if (subcommand === "sync" || subcommand === "install") {
|
|
7587
|
+
return direct("R1", "managed-local-write", "skill-managed-write");
|
|
7588
|
+
}
|
|
7589
|
+
return forbidden("unknown-command", "\u672A\u77E5 skills \u5B50\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
|
|
7590
|
+
}
|
|
7591
|
+
if (name === "env") {
|
|
7592
|
+
if (subcommand === "list" || subcommand === "current" || subcommand === "show") {
|
|
7593
|
+
return direct("R0", "local-discovery", "environment-read");
|
|
7594
|
+
}
|
|
7595
|
+
return forbidden("identity-governance", "\u73AF\u5883\u65B0\u589E\u3001\u5207\u6362\u3001\u7F16\u8F91\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7596
|
+
}
|
|
7597
|
+
if (name === "identity") {
|
|
7598
|
+
if (subcommand === "list") return direct("R0", "local-discovery", "identity-read");
|
|
7599
|
+
return forbidden("identity-governance", "\u8EAB\u4EFD\u5207\u6362\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7600
|
+
}
|
|
7601
|
+
if (name === "query" || name === "describe" || name === "trace") {
|
|
7602
|
+
return direct("R1", "remote-read", "business-read");
|
|
7603
|
+
}
|
|
7604
|
+
if (name === "cache") {
|
|
7605
|
+
if (subcommand === "stats") return direct("R1", "remote-read", "cache-read");
|
|
7606
|
+
if (subcommand === "clear" || subcommand === "clear-meta") {
|
|
7607
|
+
return guarded("operational-write", "cache-mutation");
|
|
7608
|
+
}
|
|
7609
|
+
}
|
|
7610
|
+
if (name === "meta") {
|
|
7611
|
+
if (subcommand === "list" || subcommand === "get") {
|
|
7612
|
+
return direct("R1", "remote-read", "metadata-read");
|
|
7613
|
+
}
|
|
7614
|
+
if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
|
|
7615
|
+
return direct("R1", "local-validation", "metadata-dry-run");
|
|
7616
|
+
}
|
|
7617
|
+
if (subcommand === "set") return guarded("tenant-config-write", "metadata-write");
|
|
7618
|
+
if (subcommand === "delete") return forbidden("destructive", "\u5143\u6570\u636E\u5220\u9664\u4E0D\u53EF\u7531\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u3002");
|
|
7619
|
+
}
|
|
7620
|
+
if (name === "setting") {
|
|
7621
|
+
if (subcommand === "get") return direct("R1", "remote-read", "setting-read");
|
|
7622
|
+
if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
|
|
7623
|
+
return direct("R1", "local-validation", "setting-dry-run");
|
|
7624
|
+
}
|
|
7625
|
+
if (subcommand === "set" || subcommand === "reset") {
|
|
7626
|
+
return guarded("tenant-config-write", "setting-write");
|
|
7627
|
+
}
|
|
7628
|
+
}
|
|
7629
|
+
if (name === "workspace") {
|
|
7630
|
+
if (subcommand === "push" && hasFlag(argv, "--dry-run")) {
|
|
7631
|
+
return direct("R1", "remote-read", "workspace-push-dry-run");
|
|
7632
|
+
}
|
|
7633
|
+
if (["list", "active", "status", "validate", "verify", "preview"].includes(subcommand)) {
|
|
7634
|
+
return direct("R1", "remote-read", "workspace-read");
|
|
7635
|
+
}
|
|
7636
|
+
if (subcommand === "pull") {
|
|
7637
|
+
return direct("R1", "managed-local-write", "workspace-pull");
|
|
7638
|
+
}
|
|
7639
|
+
if (subcommand === "bind" || subcommand === "add" && argv[2] === "mini") {
|
|
7640
|
+
return guarded("managed-local-write", "workspace-local-change");
|
|
7641
|
+
}
|
|
7642
|
+
if (subcommand === "create") return guarded("tenant-write", "workspace-create");
|
|
7643
|
+
if (subcommand === "push") return forbidden("publish", "\u5F00\u53D1\u7A7A\u95F4\u53D1\u5E03\u5C5E\u4E8E R3\u3002");
|
|
7644
|
+
}
|
|
7645
|
+
if (name === "import") {
|
|
7646
|
+
if (hasFlag(argv, "--dry-run")) {
|
|
7647
|
+
return direct("R1", "local-validation", "import-dry-run");
|
|
7648
|
+
}
|
|
7649
|
+
return guarded("bulk-tenant-write", "import-write");
|
|
7650
|
+
}
|
|
7651
|
+
if (name === "create" || name === "update") {
|
|
7652
|
+
return guarded("tenant-write", name === "create" ? "record-create" : "record-update");
|
|
7653
|
+
}
|
|
7654
|
+
if (name === "upload") return guarded("tenant-write", "document-upload");
|
|
7655
|
+
if (name === "download") return direct("R1", "managed-local-write", "document-download");
|
|
7656
|
+
if (name === "action") {
|
|
7657
|
+
return forbidden(
|
|
7658
|
+
"action-effect-unclassified",
|
|
7659
|
+
"Action \u5C1A\u65E0\u7EDF\u4E00 destructive/publish \u8BED\u4E49\uFF0C\u9996\u7248\u4E0D\u80FD\u6309\u540D\u79F0\u731C\u6D4B\u540E\u653E\u884C\u3002"
|
|
7660
|
+
);
|
|
7661
|
+
}
|
|
7662
|
+
if (name === "delete" || name === "meta" && subcommand === "delete") {
|
|
7663
|
+
return forbidden("destructive", "\u5220\u9664\u64CD\u4F5C\u4E0D\u8FDB\u5165\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u9762\u3002");
|
|
7664
|
+
}
|
|
7665
|
+
if (name === "tenant") {
|
|
7666
|
+
return forbidden("tenant-governance", "\u79DF\u6237\u5F00\u901A\u6216\u6CBB\u7406\u5FC5\u987B\u5347\u7EA7\u6CBB\u7406\u4EBA\u5458\u3002");
|
|
7667
|
+
}
|
|
7668
|
+
if (name === "login" || name === "logout") {
|
|
7669
|
+
return forbidden("identity-governance", "\u767B\u5F55\u4E0E\u767B\u51FA\u5C5E\u4E8E\u8EAB\u4EFD\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7670
|
+
}
|
|
7671
|
+
if (name === "chat") {
|
|
7672
|
+
return forbidden("agent-autonomy", "\u987E\u95EE\u63D2\u4EF6\u4E0D\u901A\u8FC7\u5D4C\u5957\u6570\u5B57\u5458\u5DE5\u5BF9\u8BDD\u6269\u5927\u81EA\u6CBB\u6267\u884C\u9762\u3002");
|
|
7673
|
+
}
|
|
7674
|
+
return forbidden("unknown-command", "\u672A\u77E5\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
|
|
7675
|
+
}
|
|
7676
|
+
function summarizeTarget(argv) {
|
|
7677
|
+
const name = argv[0] ?? "";
|
|
7678
|
+
const subcommand = argv[1] ?? "";
|
|
7679
|
+
const target = {};
|
|
7680
|
+
if (["query", "describe", "create", "update", "delete"].includes(name) && subcommand) {
|
|
7681
|
+
target.model = subcommand;
|
|
7682
|
+
}
|
|
7683
|
+
if (name === "action" && subcommand) target.action = subcommand;
|
|
7684
|
+
if (name === "skills" && argv[2] && !argv[2].startsWith("-")) target.skill = argv[2];
|
|
7685
|
+
if (name === "meta" && argv[2] && !argv[2].startsWith("-")) target.path = argv[2];
|
|
7686
|
+
if (name === "cache" && argv[2] && !argv[2].startsWith("-")) {
|
|
7687
|
+
target.cacheOperation = subcommand;
|
|
7688
|
+
target.model = argv[2];
|
|
7689
|
+
}
|
|
7690
|
+
if (name === "setting" && argv[2] && !argv[2].startsWith("-")) {
|
|
7691
|
+
target.domain = argv[2];
|
|
7692
|
+
const settingKeys = valuesAfterFlag(argv, "--set").map((assignment) => assignment.split("=", 1)[0]).filter((key) => Boolean(key)).sort();
|
|
7693
|
+
if (settingKeys.length > 0) target.settingKeys = settingKeys;
|
|
7694
|
+
}
|
|
7695
|
+
const id = flagValue(argv, "--id");
|
|
7696
|
+
if (id) target.recordIdSha256 = sha2565(id);
|
|
7697
|
+
for (const flag of PAYLOAD_FLAGS) {
|
|
7698
|
+
const raw = flagValue(argv, flag);
|
|
7699
|
+
if (!raw) continue;
|
|
7700
|
+
target.payloadSha256 = sha2565(raw);
|
|
7701
|
+
try {
|
|
7702
|
+
const parsed = JSON.parse(raw);
|
|
7703
|
+
if (isPlainObject(parsed)) target.fieldNames = Object.keys(parsed).sort();
|
|
7704
|
+
} catch {
|
|
7705
|
+
}
|
|
7706
|
+
}
|
|
7707
|
+
return target;
|
|
7708
|
+
}
|
|
7709
|
+
function payloadError(argv) {
|
|
7710
|
+
for (const flag of PAYLOAD_FLAGS) {
|
|
7711
|
+
const raw = flagValue(argv, flag);
|
|
7712
|
+
if (raw === void 0) continue;
|
|
7713
|
+
try {
|
|
7714
|
+
const parsed = JSON.parse(raw);
|
|
7715
|
+
if (!isPlainObject(parsed)) return true;
|
|
7716
|
+
} catch {
|
|
7717
|
+
return true;
|
|
7718
|
+
}
|
|
7719
|
+
}
|
|
7720
|
+
return false;
|
|
7721
|
+
}
|
|
7722
|
+
function direct(risk, effect, reasonCode) {
|
|
7723
|
+
return {
|
|
7724
|
+
risk,
|
|
7725
|
+
execution: "direct",
|
|
7726
|
+
effect,
|
|
7727
|
+
reasonCode,
|
|
7728
|
+
reason: risk === "R0" ? "\u7EAF\u53D1\u73B0\u64CD\u4F5C\uFF0C\u53EF\u76F4\u63A5\u6267\u884C\u3002" : "\u53EA\u8BFB\u6216\u53D7\u7BA1\u672C\u5730\u64CD\u4F5C\uFF0C\u53EF\u76F4\u63A5\u6267\u884C\u5E76\u4FDD\u7559\u7ED3\u679C\u3002"
|
|
7729
|
+
};
|
|
7730
|
+
}
|
|
7731
|
+
function guarded(effect, reasonCode) {
|
|
7732
|
+
return {
|
|
7733
|
+
risk: "R2",
|
|
7734
|
+
execution: "guarded",
|
|
7735
|
+
effect,
|
|
7736
|
+
reasonCode,
|
|
7737
|
+
reason: "\u666E\u901A\u526F\u4F5C\u7528\u64CD\u4F5C\uFF1B\u53EA\u5141\u8BB8\u5728 controlled \u9879\u76EE\u4E2D\u901A\u8FC7 delivery prepare + delivery execute \u5355\u6B21\u53D7\u63A7\u6267\u884C\u3002"
|
|
7738
|
+
};
|
|
7739
|
+
}
|
|
7740
|
+
function forbidden(reasonCode, reason) {
|
|
7741
|
+
return { risk: "R3", execution: "forbidden", effect: reasonCode, reasonCode, reason };
|
|
7742
|
+
}
|
|
7743
|
+
function evidenceFor(risk) {
|
|
7744
|
+
if (risk === "R0") return ["command-digest", "discovery-result"];
|
|
7745
|
+
if (risk === "R1") return ["command-digest", "target-aliases", "result-summary"];
|
|
7746
|
+
if (risk === "R2") {
|
|
7747
|
+
return [
|
|
7748
|
+
"command-digest",
|
|
7749
|
+
"explicit-target-aliases",
|
|
7750
|
+
"pre-state-or-dry-run",
|
|
7751
|
+
"rollback-plan",
|
|
7752
|
+
"execution-receipt",
|
|
7753
|
+
"post-verification"
|
|
7754
|
+
];
|
|
7755
|
+
}
|
|
7756
|
+
return ["governance-decision"];
|
|
7757
|
+
}
|
|
7758
|
+
function flagValue(argv, flag) {
|
|
7759
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
7760
|
+
const arg = argv[index];
|
|
7761
|
+
if (arg === void 0) continue;
|
|
7762
|
+
if (arg === flag) return argv[index + 1];
|
|
7763
|
+
if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1);
|
|
7764
|
+
}
|
|
7765
|
+
return void 0;
|
|
7766
|
+
}
|
|
7767
|
+
function hasFlag(argv, flag) {
|
|
7768
|
+
return argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
7769
|
+
}
|
|
7770
|
+
function flagName(arg) {
|
|
7771
|
+
const equals = arg.indexOf("=");
|
|
7772
|
+
return equals < 0 ? arg : arg.slice(0, equals);
|
|
7773
|
+
}
|
|
7774
|
+
function valuesAfterFlag(argv, flag) {
|
|
7775
|
+
const index = argv.indexOf(flag);
|
|
7776
|
+
if (index < 0) return [];
|
|
7777
|
+
const values = [];
|
|
7778
|
+
for (let cursor = index + 1; cursor < argv.length; cursor += 1) {
|
|
7779
|
+
const value = argv[cursor];
|
|
7780
|
+
if (value === void 0 || value.startsWith("-")) break;
|
|
7781
|
+
values.push(value);
|
|
7782
|
+
}
|
|
7783
|
+
return values;
|
|
7784
|
+
}
|
|
7785
|
+
function sha2565(value) {
|
|
7786
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
7787
|
+
}
|
|
7788
|
+
function isPlainObject(value) {
|
|
7789
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7790
|
+
}
|
|
7791
|
+
|
|
7792
|
+
// src/commands/delivery-execution.ts
|
|
7793
|
+
import { createHash as createHash6, randomBytes as secureRandomBytes } from "crypto";
|
|
7794
|
+
import { spawn } from "child_process";
|
|
7795
|
+
import { createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
7796
|
+
import path6 from "path";
|
|
7797
|
+
import { CliError as CliError20, CliErrorCode as CliErrorCode19, formatObject as formatObject18 } from "@hcmai/sdk";
|
|
7798
|
+
init_exit();
|
|
7799
|
+
var EXECUTION_CONTRACT_VERSION = "2026-08-28.controlled-v1";
|
|
7800
|
+
var DEFAULT_TTL_SECONDS = 15 * 60;
|
|
7801
|
+
var MIN_TTL_SECONDS = 60;
|
|
7802
|
+
var MAX_TTL_SECONDS = 60 * 60;
|
|
7803
|
+
var PLAN_ID_PATTERN = /^[a-f0-9]{24}$/;
|
|
7804
|
+
async function prepareDeliveryExecution(input) {
|
|
7805
|
+
const project = await realProject(input.project);
|
|
7806
|
+
await readProjectMode(project);
|
|
7807
|
+
const assessment = assessDeliveryCommand(input.argv);
|
|
7808
|
+
assertGuardedAssessment(assessment, input.argv);
|
|
7809
|
+
const ttlSeconds = input.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
7810
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds < MIN_TTL_SECONDS || ttlSeconds > MAX_TTL_SECONDS) {
|
|
7811
|
+
throw invalid4(`--ttl-seconds \u5FC5\u987B\u662F ${MIN_TTL_SECONDS} \u5230 ${MAX_TTL_SECONDS} \u7684\u6574\u6570`);
|
|
7812
|
+
}
|
|
7813
|
+
const change = await bindDocument(project, input.changeRef, "\u53D8\u66F4\u8BF4\u660E");
|
|
7814
|
+
const rollback = await bindDocument(project, input.rollbackRef, "\u56DE\u6EDA\u8BF4\u660E");
|
|
7815
|
+
const evidence = await bindEvidenceDirectory(project, input.evidenceDir);
|
|
7816
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
|
|
7817
|
+
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
7818
|
+
assertRuntimeMatches(runtime, lockedRuntime);
|
|
7819
|
+
const created = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7820
|
+
if (Number.isNaN(created.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
7821
|
+
const core = {
|
|
7822
|
+
schemaVersion: 1,
|
|
7823
|
+
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
7824
|
+
nonceSha256: sha2566((input.randomBytes ?? secureRandomBytes)(12)),
|
|
7825
|
+
createdAt: created.toISOString(),
|
|
7826
|
+
expiresAt: new Date(created.getTime() + ttlSeconds * 1e3).toISOString(),
|
|
7827
|
+
chironLockSha256: sha2566(lockBytes),
|
|
7828
|
+
runtime,
|
|
7829
|
+
assessment,
|
|
7830
|
+
documents: { change, rollback },
|
|
7831
|
+
evidenceDir: evidence.relative
|
|
7832
|
+
};
|
|
7833
|
+
const planId = sha2566(canonicalJson(core)).slice(0, 24);
|
|
7834
|
+
const plan = { ...core, planId };
|
|
7835
|
+
const plansDir = path6.join(project, ".hcm-delivery", "execution-plans");
|
|
7836
|
+
await safeManagedDirectory(project, ".hcm-delivery/execution-plans", plansDir);
|
|
7837
|
+
const planFile = path6.join(plansDir, `${planId}.json`);
|
|
7838
|
+
try {
|
|
7839
|
+
await fs7.writeFile(planFile, `${JSON.stringify(plan, null, 2)}
|
|
7840
|
+
`, {
|
|
7841
|
+
encoding: "utf8",
|
|
7842
|
+
mode: 384,
|
|
7843
|
+
flag: "wx"
|
|
7844
|
+
});
|
|
7845
|
+
} catch (cause) {
|
|
7846
|
+
throw invalid4(`\u6267\u884C\u8BA1\u5212\u5199\u5165\u5931\u8D25\u6216 planId \u5DF2\u5B58\u5728\uFF1A${planId}`, cause);
|
|
7847
|
+
}
|
|
7848
|
+
return plan;
|
|
7849
|
+
}
|
|
7850
|
+
async function executeDeliveryPlan(input) {
|
|
7851
|
+
if (!PLAN_ID_PATTERN.test(input.planId)) throw invalid4("planId \u5FC5\u987B\u662F 24 \u4F4D\u5C0F\u5199\u5341\u516D\u8FDB\u5236");
|
|
7852
|
+
const project = await realProject(input.project);
|
|
7853
|
+
if (await readProjectMode(project) !== "controlled") {
|
|
7854
|
+
throw invalid4("\u9879\u76EE\u4E0D\u662F controlled\uFF0C\u4E0D\u80FD\u6267\u884C R2 \u8BA1\u5212");
|
|
7855
|
+
}
|
|
7856
|
+
const planFile = path6.join(project, ".hcm-delivery", "execution-plans", `${input.planId}.json`);
|
|
7857
|
+
const plan = await readPlan(project, planFile);
|
|
7858
|
+
if (plan.planId !== input.planId || computePlanId(plan) !== input.planId) {
|
|
7859
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u5185\u5BB9\u4E0E planId \u6458\u8981\u4E0D\u4E00\u81F4");
|
|
7860
|
+
}
|
|
7861
|
+
const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7862
|
+
if (Number.isNaN(now.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
7863
|
+
const expiresAt = Date.parse(plan.expiresAt);
|
|
7864
|
+
const createdAt = Date.parse(plan.createdAt);
|
|
7865
|
+
if (!Number.isFinite(createdAt) || !Number.isFinite(expiresAt) || expiresAt - createdAt < MIN_TTL_SECONDS * 1e3 || expiresAt - createdAt > MAX_TTL_SECONDS * 1e3) {
|
|
7866
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u65F6\u95F4\u7A97\u53E3\u65E0\u6548");
|
|
7867
|
+
}
|
|
7868
|
+
if (now.getTime() < createdAt - 5e3) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E9\u4E8E\u8BA1\u5212\u521B\u5EFA\u65F6\u95F4");
|
|
7869
|
+
if (now.getTime() > expiresAt) throw invalid4("\u6267\u884C\u8BA1\u5212\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0 prepare");
|
|
7870
|
+
const assessment = assessDeliveryCommand(input.argv);
|
|
7871
|
+
assertGuardedAssessment(assessment, input.argv);
|
|
7872
|
+
if (canonicalJson(assessment) !== canonicalJson(plan.assessment)) {
|
|
7873
|
+
throw invalid4("\u5B9E\u9645 argv \u4E0E\u6267\u884C\u8BA1\u5212\u7684\u547D\u4EE4\u6458\u8981\u6216\u98CE\u9669\u4E8B\u5B9E\u4E0D\u4E00\u81F4");
|
|
7874
|
+
}
|
|
7875
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
|
|
7876
|
+
if (sha2566(lockBytes) !== plan.chironLockSha256) {
|
|
7877
|
+
throw invalid4("Chiron \u9501\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0\u8BC4\u4F30\u5E76 prepare");
|
|
7878
|
+
}
|
|
7879
|
+
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
7880
|
+
assertRuntimeMatches(runtime, lockedRuntime);
|
|
7881
|
+
if (canonicalJson(runtime) !== canonicalJson(plan.runtime)) {
|
|
7882
|
+
throw invalid4("CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E\u6267\u884C\u8BA1\u5212\u4E0D\u4E00\u81F4");
|
|
7883
|
+
}
|
|
7884
|
+
await assertDocumentUnchanged(project, plan.documents.change, "\u53D8\u66F4\u8BF4\u660E");
|
|
7885
|
+
await assertDocumentUnchanged(project, plan.documents.rollback, "\u56DE\u6EDA\u8BF4\u660E");
|
|
7886
|
+
const evidenceDir = await bindEvidenceDirectory(project, plan.evidenceDir);
|
|
7887
|
+
const receiptFile = path6.join(evidenceDir.absolute, `${plan.planId}.receipt.json`);
|
|
7888
|
+
if (await pathExists(receiptFile)) throw invalid4(`\u6267\u884C\u6536\u636E\u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u518D\u6B21\u6267\u884C\uFF1A${plan.planId}`);
|
|
7889
|
+
const claimsDir = path6.join(project, ".hcm-delivery", "execution-claims");
|
|
7890
|
+
await safeManagedDirectory(project, ".hcm-delivery/execution-claims", claimsDir);
|
|
7891
|
+
const claimFile = path6.join(claimsDir, `${plan.planId}.json`);
|
|
7892
|
+
try {
|
|
7893
|
+
await fs7.writeFile(
|
|
7894
|
+
claimFile,
|
|
7895
|
+
`${JSON.stringify({ schemaVersion: 1, planId: plan.planId, claimedAt: now.toISOString() }, null, 2)}
|
|
7896
|
+
`,
|
|
7897
|
+
{ encoding: "utf8", mode: 384, flag: "wx" }
|
|
7898
|
+
);
|
|
7899
|
+
} catch (cause) {
|
|
7900
|
+
throw invalid4(`\u6267\u884C\u8BA1\u5212\u5DF2\u6D88\u8D39\uFF0C\u62D2\u7EDD\u91CD\u590D\u6267\u884C\uFF1A${plan.planId}`, cause);
|
|
7901
|
+
}
|
|
7902
|
+
const stdoutHash = createHash6("sha256");
|
|
7903
|
+
const stderrHash = createHash6("sha256");
|
|
7904
|
+
const run = input.runCommand ?? runCurrentCliCommand;
|
|
7905
|
+
let exitCode;
|
|
7906
|
+
try {
|
|
7907
|
+
({ exitCode } = await run(input.argv, project, {
|
|
7908
|
+
stdout: (chunk) => stdoutHash.update(chunk),
|
|
7909
|
+
stderr: (chunk) => stderrHash.update(chunk)
|
|
7910
|
+
}));
|
|
7911
|
+
} catch (cause) {
|
|
7912
|
+
stderrHash.update(
|
|
7913
|
+
Buffer.from(`delivery executor failed: ${String(cause.message)}
|
|
7914
|
+
`)
|
|
7915
|
+
);
|
|
7916
|
+
exitCode = 1;
|
|
7917
|
+
}
|
|
7918
|
+
const completed = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7919
|
+
if (Number.isNaN(completed.getTime())) throw invalid4("\u5B8C\u6210\u65F6\u95F4\u65E0\u6548");
|
|
7920
|
+
if (!Number.isInteger(exitCode) || exitCode < 0) exitCode = 1;
|
|
7921
|
+
const receipt = {
|
|
7922
|
+
schemaVersion: 1,
|
|
7923
|
+
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
7924
|
+
planId: plan.planId,
|
|
7925
|
+
assessmentId: plan.assessment.assessmentId,
|
|
7926
|
+
commandSha256: plan.assessment.commandSha256,
|
|
7927
|
+
startedAt: now.toISOString(),
|
|
7928
|
+
completedAt: completed.toISOString(),
|
|
7929
|
+
exitCode,
|
|
7930
|
+
stdoutSha256: stdoutHash.digest("hex"),
|
|
7931
|
+
stderrSha256: stderrHash.digest("hex")
|
|
7932
|
+
};
|
|
7933
|
+
await writeReceiptExclusive(receiptFile, receipt);
|
|
7934
|
+
return receipt;
|
|
7935
|
+
}
|
|
7936
|
+
async function deliveryPrepareCommand(argv, args) {
|
|
7937
|
+
try {
|
|
7938
|
+
if (!args.changeRef) throw invalid4("--change-ref <file> \u5FC5\u586B");
|
|
7939
|
+
if (!args.rollbackRef) throw invalid4("--rollback-ref <file> \u5FC5\u586B");
|
|
7940
|
+
if (!args.evidenceDir) throw invalid4("--evidence-dir <dir> \u5FC5\u586B");
|
|
7941
|
+
const ttlSeconds = parseTtl(args.ttlSeconds);
|
|
7942
|
+
const plan = await prepareDeliveryExecution({
|
|
7943
|
+
project: args.project ?? process.cwd(),
|
|
7944
|
+
argv,
|
|
7945
|
+
changeRef: args.changeRef,
|
|
7946
|
+
rollbackRef: args.rollbackRef,
|
|
7947
|
+
evidenceDir: args.evidenceDir,
|
|
7948
|
+
...ttlSeconds === void 0 ? {} : { ttlSeconds }
|
|
7949
|
+
});
|
|
7950
|
+
process.stdout.write(formatObject18(plan, { format: args.output ?? "json" }) + "\n");
|
|
7951
|
+
} catch (cause) {
|
|
7952
|
+
exitWithError(cause);
|
|
7953
|
+
}
|
|
7954
|
+
}
|
|
7955
|
+
async function deliveryExecuteCommand(planId, argv, args) {
|
|
7956
|
+
try {
|
|
7957
|
+
const receipt = await executeDeliveryPlan({
|
|
7958
|
+
project: args.project ?? process.cwd(),
|
|
7959
|
+
planId,
|
|
7960
|
+
argv
|
|
7961
|
+
});
|
|
7962
|
+
process.stdout.write(formatObject18(receipt, { format: args.output ?? "json" }) + "\n");
|
|
7963
|
+
process.exitCode = receipt.exitCode;
|
|
7964
|
+
} catch (cause) {
|
|
7965
|
+
exitWithError(cause);
|
|
7966
|
+
}
|
|
7967
|
+
}
|
|
7968
|
+
async function readProjectMode(project) {
|
|
7969
|
+
const file = path6.join(project, ".hcm-delivery", "project.yml");
|
|
7970
|
+
const content = await readSafeFile(project, file, "\u9879\u76EE\u914D\u7F6E");
|
|
7971
|
+
const match = content.toString("utf8").match(
|
|
7972
|
+
/^deliveryMode:\s*(?:"(plan-only|controlled)"|'(plan-only|controlled)'|(plan-only|controlled))\s*$/m
|
|
7973
|
+
);
|
|
7974
|
+
const mode = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
7975
|
+
if (mode !== "plan-only" && mode !== "controlled") {
|
|
7976
|
+
throw invalid4("project.yml \u7F3A\u5C11\u5408\u6CD5 deliveryMode");
|
|
7977
|
+
}
|
|
7978
|
+
return mode;
|
|
7979
|
+
}
|
|
7980
|
+
async function readChironLock(project) {
|
|
7981
|
+
const file = path6.join(project, ".hcm-delivery", "chiron.lock.json");
|
|
7982
|
+
const bytes = await readSafeFile(project, file, "Chiron \u9501");
|
|
7983
|
+
let parsed;
|
|
7984
|
+
try {
|
|
7985
|
+
parsed = JSON.parse(bytes.toString("utf8"));
|
|
7986
|
+
} catch (cause) {
|
|
7987
|
+
throw invalid4("Chiron \u9501\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
7988
|
+
}
|
|
7989
|
+
const launch = parsed?.runtime?.launch;
|
|
7990
|
+
if (!isRuntimeIdentity(launch)) throw invalid4("Chiron \u9501\u7F3A\u5C11\u5408\u6CD5 runtime.launch \u6458\u8981");
|
|
7991
|
+
return { bytes, runtime: launch };
|
|
7992
|
+
}
|
|
7993
|
+
async function bindDocument(project, reference, label) {
|
|
7994
|
+
const relative2 = safeRelative(reference, label);
|
|
7995
|
+
if (!(relative2.startsWith("work/") || relative2.startsWith("decisions/"))) {
|
|
7996
|
+
throw invalid4(`${label}\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE work/ \u6216 decisions/ \u4E0B`);
|
|
7997
|
+
}
|
|
7998
|
+
const file = path6.join(project, relative2);
|
|
7999
|
+
const bytes = await readSafeFile(project, file, label);
|
|
8000
|
+
return { path: relative2, sha256: sha2566(bytes) };
|
|
8001
|
+
}
|
|
8002
|
+
async function assertDocumentUnchanged(project, document, label) {
|
|
8003
|
+
const current = await bindDocument(project, document.path, label);
|
|
8004
|
+
if (current.sha256 !== document.sha256) throw invalid4(`${label}\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0 prepare`);
|
|
8005
|
+
}
|
|
8006
|
+
async function bindEvidenceDirectory(project, reference) {
|
|
8007
|
+
const relative2 = safeRelative(reference, "evidence \u76EE\u5F55");
|
|
8008
|
+
if (!(relative2 === "evidence" || relative2.startsWith("evidence/"))) {
|
|
8009
|
+
throw invalid4("evidence \u76EE\u5F55\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE evidence/ \u4E0B");
|
|
8010
|
+
}
|
|
8011
|
+
const absolute = path6.join(project, relative2);
|
|
8012
|
+
await assertNoSymlinkComponents(project, relative2);
|
|
8013
|
+
const stat = await fs7.stat(absolute).catch((cause) => {
|
|
8014
|
+
throw invalid4(`evidence \u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
8015
|
+
});
|
|
8016
|
+
if (!stat.isDirectory()) throw invalid4(`evidence \u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
8017
|
+
return { relative: relative2, absolute };
|
|
8018
|
+
}
|
|
8019
|
+
async function readPlan(project, file) {
|
|
8020
|
+
const bytes = await readSafeFile(project, file, "\u6267\u884C\u8BA1\u5212");
|
|
8021
|
+
let plan;
|
|
8022
|
+
try {
|
|
8023
|
+
plan = JSON.parse(bytes.toString("utf8"));
|
|
8024
|
+
} catch (cause) {
|
|
8025
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
8026
|
+
}
|
|
8027
|
+
if (!isExecutionPlan(plan)) throw invalid4("\u6267\u884C\u8BA1\u5212 schema \u65E0\u6548");
|
|
8028
|
+
return plan;
|
|
8029
|
+
}
|
|
8030
|
+
function computePlanId(plan) {
|
|
8031
|
+
const core = { ...plan };
|
|
8032
|
+
delete core.planId;
|
|
8033
|
+
return sha2566(canonicalJson(core)).slice(0, 24);
|
|
8034
|
+
}
|
|
8035
|
+
function assertGuardedAssessment(assessment, argv) {
|
|
8036
|
+
if (assessment.risk !== "R2") throw invalid4("\u53EA\u6709 R2 \u547D\u4EE4\u53EF\u4EE5\u8FDB\u5165\u53D7\u63A7\u6267\u884C\u8BA1\u5212");
|
|
8037
|
+
if (!assessment.guardedExecutionAvailable) {
|
|
8038
|
+
throw invalid4("\u8BE5 R2 \u547D\u4EE4\u4E0D\u5728\u9996\u6279\u53D7\u63A7\u6267\u884C\u5668\u652F\u6301\u9762");
|
|
8039
|
+
}
|
|
8040
|
+
if (!assessment.environment.envAlias) throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --env \u73AF\u5883\u522B\u540D");
|
|
8041
|
+
if (!assessment.environment.identityAlias) {
|
|
8042
|
+
throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --as \u8EAB\u4EFD\u522B\u540D");
|
|
8043
|
+
}
|
|
8044
|
+
validateSupportedCommandShape(assessment.reasonCode, argv);
|
|
8045
|
+
}
|
|
8046
|
+
function validateSupportedCommandShape(reasonCode, argv) {
|
|
8047
|
+
if (reasonCode === "record-create") {
|
|
8048
|
+
requireFlagValue(argv, "--data", "create \u5FC5\u987B\u5305\u542B --data <JSON>");
|
|
8049
|
+
return;
|
|
8050
|
+
}
|
|
8051
|
+
if (reasonCode === "record-update") {
|
|
8052
|
+
requireFlagValue(argv, "--id", "update \u5FC5\u987B\u5305\u542B --id <id>");
|
|
8053
|
+
requireFlagValue(argv, "--data", "update \u5FC5\u987B\u5305\u542B --data <JSON>");
|
|
8054
|
+
return;
|
|
8055
|
+
}
|
|
8056
|
+
if (reasonCode === "cache-mutation") {
|
|
8057
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid4("cache clear \u5FC5\u987B\u5305\u542B Model");
|
|
8058
|
+
return;
|
|
8059
|
+
}
|
|
8060
|
+
if (reasonCode === "setting-write") {
|
|
8061
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid4("setting \u5199\u547D\u4EE4\u5FC5\u987B\u5305\u542B domain");
|
|
8062
|
+
if (argv[1] === "set") {
|
|
8063
|
+
requireFlagValue(argv, "--set", "setting set \u5FC5\u987B\u5305\u542B --set <assignment>");
|
|
8064
|
+
return;
|
|
8065
|
+
}
|
|
8066
|
+
if (argv[1] === "reset" && (!argv[3] || !argv[4])) {
|
|
8067
|
+
throw invalid4("setting reset \u5FC5\u987B\u5305\u542B domain\u3001namespace \u4E0E key");
|
|
8068
|
+
}
|
|
8069
|
+
}
|
|
8070
|
+
}
|
|
8071
|
+
function requireFlagValue(argv, flag, message) {
|
|
8072
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
8073
|
+
const value = argv[index];
|
|
8074
|
+
if (value === flag) {
|
|
8075
|
+
const next = argv[index + 1];
|
|
8076
|
+
if (next && !next.startsWith("-")) return next;
|
|
8077
|
+
throw invalid4(message);
|
|
8078
|
+
}
|
|
8079
|
+
if (value?.startsWith(`${flag}=`) && value.length > flag.length + 1) {
|
|
8080
|
+
return value.slice(flag.length + 1);
|
|
8081
|
+
}
|
|
8082
|
+
}
|
|
8083
|
+
throw invalid4(message);
|
|
8084
|
+
}
|
|
8085
|
+
function assertRuntimeMatches(actual, expected) {
|
|
8086
|
+
if (canonicalJson(actual) !== canonicalJson(expected)) {
|
|
8087
|
+
throw invalid4("\u5F53\u524D CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E Chiron \u9501\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 skills sync");
|
|
8088
|
+
}
|
|
8089
|
+
}
|
|
8090
|
+
async function currentRuntimeIdentity() {
|
|
8091
|
+
const entry = process.argv[1];
|
|
8092
|
+
if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
8093
|
+
return {
|
|
8094
|
+
kind: "node-entry",
|
|
8095
|
+
executableSha256: await sha256File2(process.execPath),
|
|
8096
|
+
entrySha256: await sha256File2(entry)
|
|
8097
|
+
};
|
|
8098
|
+
}
|
|
8099
|
+
async function runCurrentCliCommand(argv, cwd, streams) {
|
|
8100
|
+
const entry = process.argv[1];
|
|
8101
|
+
if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
8102
|
+
return new Promise((resolve3, reject) => {
|
|
8103
|
+
const child = spawn(process.execPath, [entry, ...argv], {
|
|
8104
|
+
cwd,
|
|
8105
|
+
env: process.env,
|
|
8106
|
+
shell: false,
|
|
8107
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
8108
|
+
windowsHide: true
|
|
8109
|
+
});
|
|
8110
|
+
child.stdout.on("data", (chunk) => {
|
|
8111
|
+
streams.stdout(chunk);
|
|
8112
|
+
process.stdout.write(chunk);
|
|
8113
|
+
});
|
|
8114
|
+
child.stderr.on("data", (chunk) => {
|
|
8115
|
+
streams.stderr(chunk);
|
|
8116
|
+
process.stderr.write(chunk);
|
|
8117
|
+
});
|
|
8118
|
+
child.once("error", reject);
|
|
8119
|
+
child.once("close", (code, signal) => resolve3({ exitCode: code ?? (signal ? 1 : 0) }));
|
|
8120
|
+
});
|
|
8121
|
+
}
|
|
8122
|
+
async function writeReceiptExclusive(target, receipt) {
|
|
8123
|
+
const temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
8124
|
+
try {
|
|
8125
|
+
await fs7.writeFile(temp, `${JSON.stringify(receipt, null, 2)}
|
|
8126
|
+
`, {
|
|
8127
|
+
encoding: "utf8",
|
|
8128
|
+
mode: 384,
|
|
8129
|
+
flag: "wx"
|
|
8130
|
+
});
|
|
8131
|
+
await fs7.link(temp, target);
|
|
8132
|
+
} catch (cause) {
|
|
8133
|
+
throw invalid4(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${path6.basename(target)}`, cause);
|
|
8134
|
+
} finally {
|
|
8135
|
+
await fs7.unlink(temp).catch(() => void 0);
|
|
8136
|
+
}
|
|
8137
|
+
}
|
|
8138
|
+
async function safeManagedDirectory(project, relative2, absolute) {
|
|
8139
|
+
await assertNoSymlinkComponents(project, path6.dirname(relative2));
|
|
8140
|
+
const existing = await fs7.lstat(absolute).catch(() => void 0);
|
|
8141
|
+
if (existing?.isSymbolicLink()) throw invalid4(`\u53D7\u7BA1\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8142
|
+
if (existing && !existing.isDirectory()) throw invalid4(`\u53D7\u7BA1\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
8143
|
+
if (!existing) await fs7.mkdir(absolute, { mode: 448 });
|
|
8144
|
+
await fs7.chmod(absolute, 448);
|
|
8145
|
+
}
|
|
8146
|
+
async function pathExists(file) {
|
|
8147
|
+
return fs7.lstat(file).then(
|
|
8148
|
+
() => true,
|
|
8149
|
+
(cause) => {
|
|
8150
|
+
if (cause.code === "ENOENT") return false;
|
|
8151
|
+
throw cause;
|
|
8152
|
+
}
|
|
8153
|
+
);
|
|
8154
|
+
}
|
|
8155
|
+
async function readSafeFile(project, file, label) {
|
|
8156
|
+
const relative2 = path6.relative(project, file).split(path6.sep).join("/");
|
|
8157
|
+
if (relative2.startsWith("../") || path6.isAbsolute(relative2))
|
|
8158
|
+
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8159
|
+
await assertNoSymlinkComponents(project, relative2);
|
|
8160
|
+
let stat;
|
|
8161
|
+
try {
|
|
8162
|
+
stat = await fs7.lstat(file);
|
|
8163
|
+
} catch (cause) {
|
|
8164
|
+
throw invalid4(`${label}\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
8165
|
+
}
|
|
8166
|
+
if (stat.isSymbolicLink()) throw invalid4(`${label}\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8167
|
+
if (!stat.isFile()) throw invalid4(`${label}\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${relative2}`);
|
|
8168
|
+
return fs7.readFile(file);
|
|
8169
|
+
}
|
|
8170
|
+
async function assertNoSymlinkComponents(project, relative2) {
|
|
8171
|
+
const parts = relative2.split("/").filter(Boolean);
|
|
8172
|
+
let current = project;
|
|
8173
|
+
for (const part of parts) {
|
|
8174
|
+
current = path6.join(current, part);
|
|
8175
|
+
const stat = await fs7.lstat(current).catch(() => void 0);
|
|
8176
|
+
if (stat?.isSymbolicLink()) throw invalid4(`\u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8177
|
+
if (!stat) break;
|
|
8178
|
+
}
|
|
8179
|
+
}
|
|
8180
|
+
async function realProject(requested) {
|
|
8181
|
+
const absolute = path6.resolve(requested);
|
|
8182
|
+
let project;
|
|
8183
|
+
try {
|
|
8184
|
+
project = await fs7.realpath(absolute);
|
|
8185
|
+
} catch (cause) {
|
|
8186
|
+
throw invalid4(`\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${absolute}`, cause);
|
|
8187
|
+
}
|
|
8188
|
+
const stat = await fs7.stat(project);
|
|
8189
|
+
if (!stat.isDirectory()) throw invalid4(`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${absolute}`);
|
|
8190
|
+
return project;
|
|
8191
|
+
}
|
|
8192
|
+
function safeRelative(reference, label) {
|
|
8193
|
+
if (!reference || path6.isAbsolute(reference) || /[\r\n\0]/.test(reference)) {
|
|
8194
|
+
throw invalid4(`${label}\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84`);
|
|
8195
|
+
}
|
|
8196
|
+
const normalized = path6.normalize(reference).split(path6.sep).join("/");
|
|
8197
|
+
if (normalized === ".." || normalized.startsWith("../") || normalized === ".") {
|
|
8198
|
+
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8199
|
+
}
|
|
8200
|
+
return normalized.replace(/^\.\//, "");
|
|
8201
|
+
}
|
|
8202
|
+
function isRuntimeIdentity(value) {
|
|
8203
|
+
if (!isRecord(value) || !isSha(value.executableSha256)) return false;
|
|
8204
|
+
if (value.kind === "executable") return true;
|
|
8205
|
+
return value.kind === "node-entry" && isSha(value.entrySha256);
|
|
8206
|
+
}
|
|
8207
|
+
function isExecutionPlan(value) {
|
|
8208
|
+
if (!isRecord(value)) return false;
|
|
8209
|
+
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.chironLockSha256) && isRuntimeIdentity(value.runtime) && isRecord(value.assessment) && isRecord(value.documents) && isBoundDocument(value.documents.change) && isBoundDocument(value.documents.rollback) && typeof value.evidenceDir === "string";
|
|
8210
|
+
}
|
|
8211
|
+
function isBoundDocument(value) {
|
|
8212
|
+
return isRecord(value) && typeof value.path === "string" && isSha(value.sha256);
|
|
8213
|
+
}
|
|
8214
|
+
function isRecord(value) {
|
|
8215
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8216
|
+
}
|
|
8217
|
+
function isSha(value) {
|
|
8218
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
8219
|
+
}
|
|
8220
|
+
function parseTtl(value) {
|
|
8221
|
+
if (value === void 0) return void 0;
|
|
8222
|
+
if (!/^\d+$/.test(value)) throw invalid4("--ttl-seconds \u5FC5\u987B\u662F\u6574\u6570");
|
|
8223
|
+
return Number(value);
|
|
8224
|
+
}
|
|
8225
|
+
function canonicalJson(value) {
|
|
8226
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
8227
|
+
if (isRecord(value)) {
|
|
8228
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
8229
|
+
}
|
|
8230
|
+
return JSON.stringify(value) ?? "null";
|
|
8231
|
+
}
|
|
8232
|
+
function sha2566(value) {
|
|
8233
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
8234
|
+
}
|
|
8235
|
+
async function sha256File2(file) {
|
|
8236
|
+
const hash = createHash6("sha256");
|
|
8237
|
+
await new Promise((resolve3, reject) => {
|
|
8238
|
+
const stream = createReadStream2(file);
|
|
8239
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
8240
|
+
stream.on("error", reject);
|
|
8241
|
+
stream.on("end", resolve3);
|
|
8242
|
+
});
|
|
8243
|
+
return hash.digest("hex");
|
|
8244
|
+
}
|
|
8245
|
+
function invalid4(message, cause) {
|
|
8246
|
+
return new CliError20({ code: CliErrorCode19.INVALID_ARGUMENT, message, cause });
|
|
8247
|
+
}
|
|
8248
|
+
|
|
8249
|
+
// src/startup.ts
|
|
8250
|
+
function needsLegacyProfileMigration(argv) {
|
|
8251
|
+
const args = argv.slice(2);
|
|
8252
|
+
if (args[0] === "delivery") return false;
|
|
8253
|
+
return !args.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg));
|
|
8254
|
+
}
|
|
8255
|
+
|
|
6475
8256
|
// src/index.ts
|
|
6476
8257
|
var program = new Command();
|
|
6477
8258
|
var exitOnBrokenPipe = (err) => {
|
|
@@ -6502,6 +8283,10 @@ var identity = program.command("identity").description("\u7BA1\u7406 HCM \u8EAB\
|
|
|
6502
8283
|
identity.command("list").description("\u5217\u51FA identity\uFF08\u9ED8\u8BA4\u5217\u5F53\u524D env\uFF0C--env all \u5217\u5168\u90E8\uFF09").option("--env <name>", '\u8FC7\u6EE4 env\uFF1B\u4F20 "all" \u5217\u5168\u90E8', void 0).action((opts) => identityList(opts));
|
|
6503
8284
|
identity.command("use <name>").description("\u628A identity \u8BBE\u4E3A\u5176 env \u7684 default identity").action((name) => identityUse(name));
|
|
6504
8285
|
identity.command("remove <name>").description("\u5220\u9664 identity\uFF08\u6E05 token + \u82E5\u662F env default \u5219\u6E05\u7A7A\uFF09").action((name) => identityRemove(name));
|
|
8286
|
+
var delivery = program.command("delivery").description("\u987E\u95EE\u4EA4\u4ED8\u547D\u4EE4\u7684\u98CE\u9669\u8BC4\u4F30\u4E0E\u4E00\u6B21\u6027\u53D7\u63A7\u6267\u884C\u5408\u540C");
|
|
8287
|
+
delivery.command("assess").description("\u7EAF\u672C\u5730\u8BC4\u4F30\u4E00\u6B21 HCM CLI argv\uFF1B\u7528 -- \u5206\u9694\u88AB\u8BC4\u4F30\u547D\u4EE4").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u88AB\u8BC4\u4F30\u7684 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570\uFF0C\u4E0D\u5305\u542B hcm \u672C\u8EAB").action((command, opts) => deliveryAssessCommand(command, opts));
|
|
8288
|
+
delivery.command("prepare").description("\u628A\u53D7\u652F\u6301\u7684 R2 argv \u4E0E\u9501\u3001\u8FD0\u884C\u65F6\u548C\u53D8\u66F4/\u56DE\u6EDA\u8BF4\u660E\u7ED1\u5B9A\u4E3A\u77ED\u65F6\u6267\u884C\u8BA1\u5212").requiredOption("--change-ref <file>", "\u9879\u76EE work/ \u6216 decisions/ \u4E0B\u7684\u53D8\u66F4\u8BF4\u660E").requiredOption("--rollback-ref <file>", "\u9879\u76EE work/ \u6216 decisions/ \u4E0B\u7684\u56DE\u6EDA\u8BF4\u660E").requiredOption("--evidence-dir <dir>", "\u9879\u76EE evidence/ \u4E0B\u7684\u6536\u636E\u76EE\u5F55").option("--project <dir>", "HCM \u987E\u95EE\u9879\u76EE\u6839\u76EE\u5F55", ".").option("--ttl-seconds <n>", "\u6709\u6548\u671F\u79D2\u6570\uFF0860-3600\uFF09", "900").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u88AB\u7ED1\u5B9A\u7684 R2 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570\uFF0C\u4E0D\u5305\u542B hcm \u672C\u8EAB").action((command, opts) => deliveryPrepareCommand(command, opts));
|
|
8289
|
+
delivery.command("execute <planId>").description("\u9010\u6B21\u6279\u51C6\u540E\u6D88\u8D39\u77ED\u65F6\u8BA1\u5212\uFF0C\u5E76\u6267\u884C\u4E00\u6B21\u4E0E\u8BA1\u5212\u5B8C\u5168\u76F8\u540C\u7684 R2 argv").option("--project <dir>", "HCM \u987E\u95EE\u9879\u76EE\u6839\u76EE\u5F55", ".").option("--output <fmt>", "\u6536\u636E\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").allowUnknownOption(true).argument("<command...>", "\u4E0E prepare \u5B8C\u5168\u76F8\u540C\u7684 R2 HCM \u5B50\u547D\u4EE4\u4E0E\u53C2\u6570").action((planId, command, opts) => deliveryExecuteCommand(planId, command, opts));
|
|
6505
8290
|
program.command("query <Model>").description("\u67E5\u8BE2 Model \u5217\u8868\u6570\u636E\uFF08\u901A\u8FC7 ModelQueryDsl JSON \u8868\u8FBE\u8FC7\u6EE4/\u6392\u5E8F/\u5206\u9875\uFF09").option(
|
|
6506
8291
|
"--filter <JSON>",
|
|
6507
8292
|
`\u8FC7\u6EE4 AST\uFF08ModelQueryDsl\uFF09\uFF1A\u53F6\u5B50 '{"field":"status","op":"eq","value":"ACTIVE"}'\uFF0C\u591A\u6761\u4EF6\u7528 '{"and":[\u53F6\u5B50,\u2026]}'\u3002\u26A0\uFE0F \u4E0D\u63A5\u53D7 {"\u5B57\u6BB5":"\u503C"} \u7B80\u5199\uFF0Cschema \u5C42\u4F1A\u62D2`
|
|
@@ -6562,10 +8347,11 @@ meta.command("get <path>").description("\u6253\u5370 meta \u539F\u6587\uFF08\u59
|
|
|
6562
8347
|
meta.command("set <path>").description("\u5199\u5165 meta \u539F\u6587\uFF1B\u5199\u5B8C\u987B hcm cache clear-meta <Model> \u624D\u751F\u6548").requiredOption("--file <file>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6\u539F\u6587\uFF08- \u8868\u793A stdin\uFF09").option("--dry-run", "\u53EA\u663E\u793A\u5C06\u5199\u5165\u7684\u5185\u5BB9\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").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").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((p, opts) => metaSetCommand(p, opts));
|
|
6563
8348
|
meta.command("delete <path>").description("\u5220\u9664 meta\uFF08\u4E0D\u53EF\u9006\uFF0C\u9ED8\u8BA4\u8FC7\u786E\u8BA4\u95F8\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").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").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((p, opts) => metaDeleteCommand(p, opts));
|
|
6564
8349
|
var skills = program.command("skills").description("\u777F\u620E Chiron \u4EA4\u4ED8\u6280\u80FD\u5E93\uFF1A\u5217\u51FA / \u67E5\u770B / \u5B89\u88C5\u4EA4\u4ED8\u6280\u80FD\uFF08\u514D\u8BA4\u8BC1\uFF0C\u65E0\u9700\u5148 login\uFF09");
|
|
6565
|
-
skills.command("list [keyword]").description("\u5217\u51FA\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\u7684\u4EA4\u4ED8\u6280\u80FD").option("--category <key>", "\u53EA\u770B\u67D0\u4E2A\u5206\u7C7B").option("--stage <key>", "\u53EA\u770B\u67D0\u4E2A\u4EA4\u4ED8\u9636\u6BB5\uFF1Ahandover | baseline | configure | migrate | integrate | golive | operate").option("--by-stage", "\u6309\u4EA4\u4ED8\u751F\u547D\u5468\u671F\u5206\u7EC4\u5C55\u793A\uFF08\u9ED8\u8BA4\u6309\u529F\u80FD\u5206\u7C7B\uFF09").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((kw, opts) => skillsListCommand(kw, opts));
|
|
8350
|
+
skills.command("list [keyword]").description("\u5217\u51FA\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\u7684\u4EA4\u4ED8\u6280\u80FD\uFF1B--installed \u6539\u770B\u672C\u5730\u88C5\u4E86\u54EA\u4E9B\uFF08\u4E0D\u8FDE\u7F51\uFF09").option("--installed", "\u53EA\u770B\u672C\u5730\u5DF2\u88C5\u7684\u6280\u80FD\uFF08\u8BFB .hcm-skills.json\uFF0C\u4E0D\u8FDE\u7F51\uFF09").option("--target <agent>", "\u914D\u5408 --installed\uFF1Aclaude | codex | auto", "auto").option("--global", "\u914D\u5408 --installed\uFF1A\u770B\u7528\u6237\u7EA7\u76EE\u5F55").option("--dir <path>", "\u914D\u5408 --installed\uFF1A\u770B\u6307\u5B9A\u76EE\u5F55").option("--category <key>", "\u53EA\u770B\u67D0\u4E2A\u5206\u7C7B").option("--stage <key>", "\u53EA\u770B\u67D0\u4E2A\u4EA4\u4ED8\u9636\u6BB5\uFF1Ahandover | baseline | configure | migrate | integrate | golive | operate").option("--by-stage", "\u6309\u4EA4\u4ED8\u751F\u547D\u5468\u671F\u5206\u7EC4\u5C55\u793A\uFF08\u9ED8\u8BA4\u6309\u529F\u80FD\u5206\u7C7B\uFF09").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((kw, opts) => skillsListCommand(kw, opts));
|
|
6566
8351
|
skills.command("show <id>").description("\u6253\u5370\u6280\u80FD\u539F\u6587\uFF08\u7BA1\u9053\u53CB\u597D\uFF09").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));
|
|
6567
|
-
skills.command("install
|
|
6568
|
-
skills.command("
|
|
8352
|
+
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("--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));
|
|
8353
|
+
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));
|
|
8354
|
+
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("--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));
|
|
6569
8355
|
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").requiredOption("--data <JSON>", `\u8981\u66F4\u65B0\u7684\u5B57\u6BB5 JSON\uFF0C\u4F8B\u5982 '{"name":"\u65B0\u540D\u79F0"}'`).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));
|
|
6570
8356
|
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));
|
|
6571
8357
|
var setting = program.command("setting").description("\u79DF\u6237\u7CFB\u7EDF\u53C2\u6570\uFF08\u767B\u5F55\u7B56\u7565 / \u5BC6\u7801\u7B56\u7565 / \u529F\u80FD\u5F00\u5173 / \u96C6\u6210\u53C2\u6570\uFF09");
|
|
@@ -6573,13 +8359,15 @@ setting.command("get <domain>").description("\u8BFB\u67D0\u57DF\u53C2\u6570\uFF0
|
|
|
6573
8359
|
setting.command("set <domain>").description("\u6539\u53C2\u6570\uFF0C\u53EF\u91CD\u590D --set\uFF1B\u8D70\u53D7\u63A7\u6821\u9A8C\u8DEF\u5F84\u800C\u975E\u6807\u51C6 CRUD").option("--set <assignment...>", "namespace.key=value\uFF0C\u4F8B\u5982 security.password.passwordPolicy=complex").option("--dry-run", "\u53EA\u663E\u793A\u5C06\u5199\u5165\u4EC0\u4E48\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").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").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((d, opts) => settingSetCommand(d, opts));
|
|
6574
8360
|
setting.command("reset <domain> <namespace> <key>").description("\u6E05\u9664\u79DF\u6237\u8986\u76D6\uFF0C\u56DE\u843D\u5230\u9ED8\u8BA4\u503C").option("--revision <n>", "\u4E50\u89C2\u9501\u7248\u672C\u53F7").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").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").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((d, ns, k, opts) => settingResetCommand(d, ns, k, opts));
|
|
6575
8361
|
async function main() {
|
|
6576
|
-
|
|
6577
|
-
|
|
6578
|
-
|
|
6579
|
-
|
|
6580
|
-
|
|
8362
|
+
if (needsLegacyProfileMigration(process.argv)) {
|
|
8363
|
+
try {
|
|
8364
|
+
await migrateLegacyProfiles();
|
|
8365
|
+
} catch (e) {
|
|
8366
|
+
process.stderr.write(
|
|
8367
|
+
`[hcm] event=migration_error reason=${JSON.stringify(String(e.message))}
|
|
6581
8368
|
`
|
|
6582
|
-
|
|
8369
|
+
);
|
|
8370
|
+
}
|
|
6583
8371
|
}
|
|
6584
8372
|
await program.parseAsync(process.argv);
|
|
6585
8373
|
}
|