@hcmai/cli 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/index.js +1915 -244
- 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,20 +4224,21 @@ 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
|
-
normalizeReferences,
|
|
4238
|
+
normalizeReferences as normalizeReferences2,
|
|
4238
4239
|
parseSkillRequirements,
|
|
4239
4240
|
parseSkillFrontmatter,
|
|
4240
|
-
resolveSkillInstallOrder,
|
|
4241
|
+
resolveSkillInstallOrder as resolveSkillInstallOrder2,
|
|
4241
4242
|
resolveAllSkillsInstallOrder,
|
|
4242
4243
|
flattenSkills,
|
|
4243
4244
|
matchSkills,
|
|
@@ -4247,12 +4248,816 @@ import {
|
|
|
4247
4248
|
loadEnv as loadEnv5,
|
|
4248
4249
|
loadGlobalConfig as loadGlobalConfig6,
|
|
4249
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 {
|
|
4250
4260
|
CliError as CliError14,
|
|
4251
|
-
CliErrorCode as CliErrorCode13
|
|
4261
|
+
CliErrorCode as CliErrorCode13,
|
|
4262
|
+
assertSafeSkillFilePath,
|
|
4263
|
+
assertSafeSkillId,
|
|
4264
|
+
normalizeReferences,
|
|
4265
|
+
resolveSkillInstallOrder
|
|
4252
4266
|
} from "@hcmai/sdk";
|
|
4267
|
+
async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fetchFile, fetchReference) {
|
|
4268
|
+
const order = resolveSkillInstallOrder(catalog, requestedSkill);
|
|
4269
|
+
for (const skill of order) {
|
|
4270
|
+
if (skill.actionSurface !== "cli") {
|
|
4271
|
+
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`);
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
return Promise.all(
|
|
4275
|
+
order.map(async (skill) => {
|
|
4276
|
+
let files;
|
|
4277
|
+
if (skill.files === void 0) {
|
|
4278
|
+
const markdown2 = await fetchMarkdown(skill.id);
|
|
4279
|
+
const content = Buffer.from(markdown2, "utf8");
|
|
4280
|
+
const references = normalizeReferences(skill.references, skill.id);
|
|
4281
|
+
if (references.length > 0 && !fetchReference) {
|
|
4282
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u58F0\u660E\u4E86 references\uFF0C\u4F46\u8C03\u7528\u65B9\u6CA1\u6709\u53C2\u8003\u6587\u4EF6\u4E0B\u8F7D\u80FD\u529B`);
|
|
4283
|
+
}
|
|
4284
|
+
const referenceFiles = await Promise.all(
|
|
4285
|
+
references.map(async (relativePath) => {
|
|
4286
|
+
const targetPath = `references/${relativePath}`;
|
|
4287
|
+
assertSafeSkillFilePath(targetPath);
|
|
4288
|
+
const fetched = await fetchReference(skill.id, relativePath);
|
|
4289
|
+
const referenceContent = Buffer.isBuffer(fetched) ? Buffer.from(fetched) : Buffer.from(fetched, "utf8");
|
|
4290
|
+
return {
|
|
4291
|
+
path: targetPath,
|
|
4292
|
+
content: referenceContent,
|
|
4293
|
+
bytes: referenceContent.length,
|
|
4294
|
+
sha256: sha256(referenceContent)
|
|
4295
|
+
};
|
|
4296
|
+
})
|
|
4297
|
+
);
|
|
4298
|
+
files = [
|
|
4299
|
+
{
|
|
4300
|
+
path: "SKILL.md",
|
|
4301
|
+
content,
|
|
4302
|
+
bytes: content.length,
|
|
4303
|
+
sha256: sha256(content)
|
|
4304
|
+
},
|
|
4305
|
+
...referenceFiles
|
|
4306
|
+
];
|
|
4307
|
+
} else {
|
|
4308
|
+
if (!Array.isArray(skill.files) || skill.files.length === 0) {
|
|
4309
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u76EE\u5F55\u4E3A\u7A7A`);
|
|
4310
|
+
}
|
|
4311
|
+
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`);
|
|
4312
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4313
|
+
for (const file of skill.files) {
|
|
4314
|
+
assertSafeSkillFilePath(file.path);
|
|
4315
|
+
if (seen.has(file.path))
|
|
4316
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684 files \u5305\u542B\u91CD\u590D\u8DEF\u5F84: ${file.path}`);
|
|
4317
|
+
seen.add(file.path);
|
|
4318
|
+
if (!Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4319
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u7684\u6587\u4EF6\u76EE\u5F55\u5143\u6570\u636E\u975E\u6CD5: ${file.path}`);
|
|
4320
|
+
}
|
|
4321
|
+
}
|
|
4322
|
+
if (!seen.has("SKILL.md")) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4323
|
+
files = await Promise.all(
|
|
4324
|
+
skill.files.map(async (file) => {
|
|
4325
|
+
const content = Buffer.from(await fetchFile(skill.id, file.path));
|
|
4326
|
+
if (content.length !== file.bytes) {
|
|
4327
|
+
throw invalid(
|
|
4328
|
+
`\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}`
|
|
4329
|
+
);
|
|
4330
|
+
}
|
|
4331
|
+
const digest = sha256(content);
|
|
4332
|
+
if (digest !== file.sha256) {
|
|
4333
|
+
throw invalid(`\u6280\u80FD ${skill.id}/${file.path} \u6458\u8981\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4`);
|
|
4334
|
+
}
|
|
4335
|
+
return { path: file.path, content, bytes: content.length, sha256: digest };
|
|
4336
|
+
})
|
|
4337
|
+
);
|
|
4338
|
+
}
|
|
4339
|
+
const root = files.find((file) => file.path === "SKILL.md");
|
|
4340
|
+
if (!root) throw invalid(`\u6280\u80FD ${skill.id} \u7684\u5B8C\u6574\u76EE\u5F55\u7F3A\u5C11 SKILL.md`);
|
|
4341
|
+
const markdown = root.content.toString("utf8");
|
|
4342
|
+
if (!markdown.trim()) throw invalid(`\u6280\u80FD ${skill.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4343
|
+
validateWorkBuddyFrontmatter(markdown, skill.id);
|
|
4344
|
+
const bytes = root.bytes;
|
|
4345
|
+
if (Number.isFinite(skill.bytes) && skill.bytes > 0 && skill.bytes !== bytes) {
|
|
4346
|
+
throw invalid(`\u6280\u80FD ${skill.id} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${skill.bytes}\uFF0C\u5B9E\u9645=${bytes}`);
|
|
4347
|
+
}
|
|
4348
|
+
return { id: skill.id, markdown, bytes, sha256: root.sha256, files };
|
|
4349
|
+
})
|
|
4350
|
+
);
|
|
4351
|
+
}
|
|
4352
|
+
async function syncWorkBuddyProject(input) {
|
|
4353
|
+
const project = path2.resolve(input.project);
|
|
4354
|
+
const metaDir = path2.join(project, ".hcm-delivery");
|
|
4355
|
+
const skillsDir = path2.join(project, ".codebuddy", "skills");
|
|
4356
|
+
const lockPath = path2.join(metaDir, "chiron.lock.json");
|
|
4357
|
+
assertSafeSkillId(input.requestedSkill);
|
|
4358
|
+
validatePreparedDocuments(input.documents, input.requestedSkill);
|
|
4359
|
+
validatePreparedRuntime(input.runtime);
|
|
4360
|
+
await recoverInterruptedSyncs(project, metaDir, lockPath);
|
|
4361
|
+
const oldLock = await readLock(lockPath);
|
|
4362
|
+
const skillEntries = input.documents.flatMap(
|
|
4363
|
+
(document) => document.files.map((file) => [ownedFile(document.id, file.path), file])
|
|
4364
|
+
);
|
|
4365
|
+
const runtimeEntries = input.runtime.files.map((file) => [file.path, file]);
|
|
4366
|
+
const desiredEntries = [
|
|
4367
|
+
...skillEntries,
|
|
4368
|
+
...runtimeEntries
|
|
4369
|
+
];
|
|
4370
|
+
const desiredFiles = desiredEntries.map(([file]) => file);
|
|
4371
|
+
const desiredByFile = new Map(desiredEntries);
|
|
4372
|
+
const oldOwned = new Set(oldLock?.ownedFiles ?? []);
|
|
4373
|
+
const oldDigestByFile = lockDigests(oldLock);
|
|
4374
|
+
for (const file of oldOwned) validateOwnedFile(file);
|
|
4375
|
+
for (const file of desiredFiles) {
|
|
4376
|
+
const absolute = path2.join(project, file);
|
|
4377
|
+
const current = await readOptionalBytes(absolute);
|
|
4378
|
+
if (current !== void 0 && !oldOwned.has(file)) {
|
|
4379
|
+
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}`);
|
|
4380
|
+
}
|
|
4381
|
+
}
|
|
4382
|
+
for (const file of oldOwned) {
|
|
4383
|
+
const absolute = path2.join(project, file);
|
|
4384
|
+
const current = await readOptionalBytes(absolute);
|
|
4385
|
+
if (current === void 0) continue;
|
|
4386
|
+
const expected = oldDigestByFile.get(file);
|
|
4387
|
+
if (!expected || sha256(current) !== expected.sha256 || !await fileModeMatches(absolute, expected.mode)) {
|
|
4388
|
+
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}`);
|
|
4389
|
+
}
|
|
4390
|
+
}
|
|
4391
|
+
const newLock = {
|
|
4392
|
+
schemaVersion: 1,
|
|
4393
|
+
source: {
|
|
4394
|
+
endpointOriginSha256: sha256(normalizeEndpoint(input.endpoint)),
|
|
4395
|
+
catalogPath: "/skills.json",
|
|
4396
|
+
catalogSha256: sha256(stableJson2(input.catalog))
|
|
4397
|
+
},
|
|
4398
|
+
selection: { requestedSkill: input.requestedSkill },
|
|
4399
|
+
resolvedSkills: input.documents.map(({ id, bytes, sha256: digest, files: skillFiles }) => ({
|
|
4400
|
+
id,
|
|
4401
|
+
bytes,
|
|
4402
|
+
sha256: digest,
|
|
4403
|
+
files: skillFiles.map(({ path: filePath, bytes: fileBytes, sha256: fileSha256 }) => ({
|
|
4404
|
+
path: filePath,
|
|
4405
|
+
bytes: fileBytes,
|
|
4406
|
+
sha256: fileSha256
|
|
4407
|
+
}))
|
|
4408
|
+
})),
|
|
4409
|
+
runtime: {
|
|
4410
|
+
capabilities: input.runtime.capabilities,
|
|
4411
|
+
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4412
|
+
launch: input.runtime.launch,
|
|
4413
|
+
files: input.runtime.files.map(({ path: filePath, bytes, sha256: digest, mode }) => ({
|
|
4414
|
+
path: filePath,
|
|
4415
|
+
bytes,
|
|
4416
|
+
sha256: digest,
|
|
4417
|
+
mode
|
|
4418
|
+
}))
|
|
4419
|
+
},
|
|
4420
|
+
ownedFiles: desiredFiles,
|
|
4421
|
+
syncedAt: input.now ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
4422
|
+
};
|
|
4423
|
+
if (oldLock && sameLockContent(oldLock, newLock) && await desiredFilesMatch(project, desiredByFile)) {
|
|
4424
|
+
return result(false, input, project, skillsDir, lockPath, [], []);
|
|
4425
|
+
}
|
|
4426
|
+
const obsolete = [...oldOwned].filter((file) => !desiredByFile.has(file));
|
|
4427
|
+
await fs4.mkdir(metaDir, { recursive: true });
|
|
4428
|
+
const staging = await fs4.mkdtemp(path2.join(metaDir, ".staging-"));
|
|
4429
|
+
const stagedFiles = path2.join(staging, "new");
|
|
4430
|
+
const backups = path2.join(staging, "backup");
|
|
4431
|
+
const stagedLock = path2.join(staging, "chiron.lock.json");
|
|
4432
|
+
const journalPath = path2.join(staging, "journal.json");
|
|
4433
|
+
let journalReady = false;
|
|
4434
|
+
try {
|
|
4435
|
+
for (const [file, preparedFile2] of desiredByFile) {
|
|
4436
|
+
const staged = path2.join(stagedFiles, file);
|
|
4437
|
+
await fs4.mkdir(path2.dirname(staged), { recursive: true });
|
|
4438
|
+
await fs4.writeFile(
|
|
4439
|
+
staged,
|
|
4440
|
+
preparedFile2.content,
|
|
4441
|
+
preparedFile2.mode === void 0 ? void 0 : { mode: preparedFile2.mode }
|
|
4442
|
+
);
|
|
4443
|
+
}
|
|
4444
|
+
await fs4.writeFile(stagedLock, `${JSON.stringify(newLock, null, 2)}
|
|
4445
|
+
`, "utf8");
|
|
4446
|
+
const affected = [.../* @__PURE__ */ new Set([...desiredFiles, ...obsolete])];
|
|
4447
|
+
for (const file of affected) {
|
|
4448
|
+
const target = path2.join(project, file);
|
|
4449
|
+
if (!await exists(target)) continue;
|
|
4450
|
+
const backup = path2.join(backups, file);
|
|
4451
|
+
await fs4.mkdir(path2.dirname(backup), { recursive: true });
|
|
4452
|
+
await fs4.copyFile(target, backup);
|
|
4453
|
+
}
|
|
4454
|
+
const oldLockExisted = await exists(lockPath);
|
|
4455
|
+
if (oldLockExisted) {
|
|
4456
|
+
const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4457
|
+
await fs4.mkdir(path2.dirname(backupLock), { recursive: true });
|
|
4458
|
+
await fs4.copyFile(lockPath, backupLock);
|
|
4459
|
+
}
|
|
4460
|
+
const journal = {
|
|
4461
|
+
schemaVersion: 1,
|
|
4462
|
+
project,
|
|
4463
|
+
affectedFiles: affected,
|
|
4464
|
+
oldLockExisted
|
|
4465
|
+
};
|
|
4466
|
+
await fs4.writeFile(journalPath, `${JSON.stringify(journal, null, 2)}
|
|
4467
|
+
`, "utf8");
|
|
4468
|
+
journalReady = true;
|
|
4469
|
+
for (const file of desiredFiles) {
|
|
4470
|
+
const target = path2.join(project, file);
|
|
4471
|
+
await fs4.mkdir(path2.dirname(target), { recursive: true });
|
|
4472
|
+
await fs4.rm(target, { force: true });
|
|
4473
|
+
await fs4.rename(path2.join(stagedFiles, file), target);
|
|
4474
|
+
}
|
|
4475
|
+
for (const file of obsolete) await fs4.unlink(path2.join(project, file)).catch(ignoreMissing);
|
|
4476
|
+
await fs4.rm(lockPath, { force: true });
|
|
4477
|
+
await fs4.rename(stagedLock, lockPath);
|
|
4478
|
+
await fs4.unlink(journalPath);
|
|
4479
|
+
journalReady = false;
|
|
4480
|
+
} catch (cause) {
|
|
4481
|
+
if (journalReady) await recoverStaging(project, staging, lockPath);
|
|
4482
|
+
throw cause;
|
|
4483
|
+
} finally {
|
|
4484
|
+
await fs4.rm(staging, { recursive: true, force: true });
|
|
4485
|
+
}
|
|
4486
|
+
return result(
|
|
4487
|
+
true,
|
|
4488
|
+
input,
|
|
4489
|
+
project,
|
|
4490
|
+
skillsDir,
|
|
4491
|
+
lockPath,
|
|
4492
|
+
input.documents.map((document) => document.id),
|
|
4493
|
+
[...new Set(obsolete.map(idFromOwnedFile).filter((id) => id !== void 0))]
|
|
4494
|
+
);
|
|
4495
|
+
}
|
|
4496
|
+
async function recoverInterruptedSyncs(project, metaDir, lockPath) {
|
|
4497
|
+
let entries;
|
|
4498
|
+
try {
|
|
4499
|
+
entries = await fs4.readdir(metaDir, { withFileTypes: true });
|
|
4500
|
+
} catch (cause) {
|
|
4501
|
+
if (cause.code === "ENOENT") return;
|
|
4502
|
+
throw cause;
|
|
4503
|
+
}
|
|
4504
|
+
for (const entry of entries) {
|
|
4505
|
+
if (!entry.isDirectory() || !entry.name.startsWith(".staging-")) continue;
|
|
4506
|
+
const staging = path2.join(metaDir, entry.name);
|
|
4507
|
+
if (await exists(path2.join(staging, "journal.json"))) {
|
|
4508
|
+
await recoverStaging(project, staging, lockPath);
|
|
4509
|
+
}
|
|
4510
|
+
await fs4.rm(staging, { recursive: true, force: true });
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
async function recoverStaging(project, staging, lockPath) {
|
|
4514
|
+
const journalPath = path2.join(staging, "journal.json");
|
|
4515
|
+
const journal = parseJournal(await fs4.readFile(journalPath, "utf8"), project);
|
|
4516
|
+
const backups = path2.join(staging, "backup");
|
|
4517
|
+
for (const file of journal.affectedFiles) {
|
|
4518
|
+
const target = path2.join(project, file);
|
|
4519
|
+
const backup = path2.join(backups, file);
|
|
4520
|
+
if (await exists(backup)) {
|
|
4521
|
+
await fs4.mkdir(path2.dirname(target), { recursive: true });
|
|
4522
|
+
await fs4.rm(target, { force: true });
|
|
4523
|
+
await fs4.rename(backup, target);
|
|
4524
|
+
} else {
|
|
4525
|
+
await fs4.rm(target, { force: true });
|
|
4526
|
+
}
|
|
4527
|
+
}
|
|
4528
|
+
const backupLock = path2.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4529
|
+
if (journal.oldLockExisted) {
|
|
4530
|
+
if (!await exists(backupLock)) throw invalid(`\u540C\u6B65\u6062\u590D\u7F3A\u5C11\u65E7\u9501\u5907\u4EFD\uFF1A${backupLock}`);
|
|
4531
|
+
await fs4.mkdir(path2.dirname(lockPath), { recursive: true });
|
|
4532
|
+
await fs4.rm(lockPath, { force: true });
|
|
4533
|
+
await fs4.rename(backupLock, lockPath);
|
|
4534
|
+
} else {
|
|
4535
|
+
await fs4.rm(lockPath, { force: true });
|
|
4536
|
+
}
|
|
4537
|
+
}
|
|
4538
|
+
function parseJournal(content, project) {
|
|
4539
|
+
let journal;
|
|
4540
|
+
try {
|
|
4541
|
+
journal = JSON.parse(content);
|
|
4542
|
+
} catch (cause) {
|
|
4543
|
+
throw new CliError14({
|
|
4544
|
+
code: CliErrorCode13.CONFIG_PARSE_ERROR,
|
|
4545
|
+
message: "Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u635F\u574F",
|
|
4546
|
+
cause
|
|
4547
|
+
});
|
|
4548
|
+
}
|
|
4549
|
+
if (journal.schemaVersion !== 1 || journal.project !== project || !Array.isArray(journal.affectedFiles) || typeof journal.oldLockExisted !== "boolean") {
|
|
4550
|
+
throw invalid("Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u5408\u540C\u4E0D\u5339\u914D\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6062\u590D\u8303\u56F4");
|
|
4551
|
+
}
|
|
4552
|
+
for (const file of journal.affectedFiles) validateOwnedFile(file);
|
|
4553
|
+
return journal;
|
|
4554
|
+
}
|
|
4555
|
+
function validatePreparedDocuments(documents, requestedSkill) {
|
|
4556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4557
|
+
for (const document of documents) {
|
|
4558
|
+
assertSafeSkillId(document.id);
|
|
4559
|
+
if (seen.has(document.id)) throw invalid(`\u540C\u6B65\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
4560
|
+
seen.add(document.id);
|
|
4561
|
+
if (!document.markdown.trim()) throw invalid(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4562
|
+
const paths = /* @__PURE__ */ new Set();
|
|
4563
|
+
for (const file of document.files) {
|
|
4564
|
+
assertSafeSkillFilePath(file.path);
|
|
4565
|
+
if (paths.has(file.path)) throw invalid(`\u6280\u80FD ${document.id} \u5305\u542B\u91CD\u590D\u6587\u4EF6: ${file.path}`);
|
|
4566
|
+
paths.add(file.path);
|
|
4567
|
+
if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256) {
|
|
4568
|
+
throw invalid(`\u6280\u80FD ${document.id}/${file.path} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4569
|
+
}
|
|
4570
|
+
}
|
|
4571
|
+
const root = document.files.find((file) => file.path === "SKILL.md");
|
|
4572
|
+
if (!root || !root.content.equals(Buffer.from(document.markdown, "utf8")) || root.bytes !== document.bytes || root.sha256 !== document.sha256) {
|
|
4573
|
+
throw invalid(`\u6280\u80FD ${document.id} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4574
|
+
}
|
|
4575
|
+
}
|
|
4576
|
+
if (!seen.has(requestedSkill)) throw invalid(`\u540C\u6B65\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${requestedSkill}`);
|
|
4577
|
+
}
|
|
4578
|
+
function validatePreparedRuntime(runtime) {
|
|
4579
|
+
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0) {
|
|
4580
|
+
throw invalid("WorkBuddy CLI \u80FD\u529B\u96C6\u5408\u4E3A\u7A7A");
|
|
4581
|
+
}
|
|
4582
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
4583
|
+
for (const capability of runtime.capabilities) {
|
|
4584
|
+
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
4585
|
+
throw invalid(`WorkBuddy CLI \u80FD\u529B\u6807\u8BC6\u975E\u6CD5\u6216\u91CD\u590D\uFF1A${capability}`);
|
|
4586
|
+
}
|
|
4587
|
+
capabilities.add(capability);
|
|
4588
|
+
}
|
|
4589
|
+
if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
|
|
4590
|
+
`)) {
|
|
4591
|
+
throw invalid("WorkBuddy CLI \u80FD\u529B\u6458\u8981\u4E0D\u4E00\u81F4");
|
|
4592
|
+
}
|
|
4593
|
+
if (!validDigest(runtime.launch.executableSha256)) {
|
|
4594
|
+
throw invalid("WorkBuddy CLI \u53EF\u6267\u884C\u6587\u4EF6\u6458\u8981\u975E\u6CD5");
|
|
4595
|
+
}
|
|
4596
|
+
if (runtime.launch.kind === "node-entry" && !validDigest(runtime.launch.entrySha256)) {
|
|
4597
|
+
throw invalid("WorkBuddy CLI \u5165\u53E3\u6458\u8981\u975E\u6CD5");
|
|
4598
|
+
}
|
|
4599
|
+
if (!Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4600
|
+
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5FC5\u987B\u540C\u65F6\u5305\u542B Unix \u4E0E Windows \u6587\u4EF6");
|
|
4601
|
+
}
|
|
4602
|
+
const files = /* @__PURE__ */ new Set();
|
|
4603
|
+
for (const file of runtime.files) {
|
|
4604
|
+
validateRuntimeFile(file);
|
|
4605
|
+
if (files.has(file.path)) throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u91CD\u590D\uFF1A${file.path}`);
|
|
4606
|
+
files.add(file.path);
|
|
4607
|
+
}
|
|
4608
|
+
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
4609
|
+
throw invalid("WorkBuddy \u8FD0\u884C\u5165\u53E3\u5408\u540C\u4E0D\u5B8C\u6574");
|
|
4610
|
+
}
|
|
4611
|
+
}
|
|
4612
|
+
function validateRuntimeFile(file) {
|
|
4613
|
+
validateOwnedFile(file.path);
|
|
4614
|
+
if (file.content.length !== file.bytes || sha256(file.content) !== file.sha256 || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
|
|
4615
|
+
throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u6458\u8981\u6216\u6743\u9650\u4E0D\u4E00\u81F4\uFF1A${file.path}`);
|
|
4616
|
+
}
|
|
4617
|
+
}
|
|
4618
|
+
function validateWorkBuddyFrontmatter(markdown, skillId) {
|
|
4619
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/.exec(markdown);
|
|
4620
|
+
if (!match) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 YAML frontmatter`);
|
|
4621
|
+
const frontmatter = match[1] ?? "";
|
|
4622
|
+
const name = /^name:\s*['"]?([^'"\s]+)['"]?\s*$/m.exec(frontmatter)?.[1];
|
|
4623
|
+
const description = /^description:\s*(.+)$/m.exec(frontmatter)?.[1]?.trim();
|
|
4624
|
+
if (name !== skillId) throw invalid(`\u6280\u80FD ${skillId} \u7684 frontmatter name \u4E0D\u4E00\u81F4`);
|
|
4625
|
+
if (!description) throw invalid(`\u6280\u80FD ${skillId} \u7F3A\u5C11 description\uFF0CWorkBuddy \u65E0\u6CD5\u53D1\u73B0`);
|
|
4626
|
+
}
|
|
4627
|
+
async function readLock(lockPath) {
|
|
4628
|
+
const content = await readOptional(lockPath);
|
|
4629
|
+
if (content === void 0) return void 0;
|
|
4630
|
+
try {
|
|
4631
|
+
const lock = JSON.parse(content);
|
|
4632
|
+
if (lock.schemaVersion !== 1 || !lock.source || !lock.selection?.requestedSkill || !Array.isArray(lock.resolvedSkills) || !Array.isArray(lock.ownedFiles)) {
|
|
4633
|
+
throw new Error("schema mismatch");
|
|
4634
|
+
}
|
|
4635
|
+
assertSafeSkillId(lock.selection.requestedSkill);
|
|
4636
|
+
for (const skill of lock.resolvedSkills) {
|
|
4637
|
+
assertSafeSkillId(skill.id);
|
|
4638
|
+
if (!Number.isInteger(skill.bytes) || skill.bytes < 0 || !/^[a-f0-9]{64}$/.test(skill.sha256)) {
|
|
4639
|
+
throw new Error("invalid skill digest");
|
|
4640
|
+
}
|
|
4641
|
+
if (skill.files !== void 0) {
|
|
4642
|
+
if (!Array.isArray(skill.files) || skill.files.length === 0)
|
|
4643
|
+
throw new Error("invalid files");
|
|
4644
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4645
|
+
for (const file of skill.files) {
|
|
4646
|
+
assertSafeSkillFilePath(file.path);
|
|
4647
|
+
if (seen.has(file.path) || !Number.isInteger(file.bytes) || file.bytes < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) {
|
|
4648
|
+
throw new Error("invalid file digest");
|
|
4649
|
+
}
|
|
4650
|
+
seen.add(file.path);
|
|
4651
|
+
}
|
|
4652
|
+
if (!seen.has("SKILL.md")) throw new Error("missing SKILL.md");
|
|
4653
|
+
}
|
|
4654
|
+
}
|
|
4655
|
+
if (lock.runtime !== void 0) validateLockedRuntime(lock.runtime);
|
|
4656
|
+
for (const file of lock.ownedFiles) validateOwnedFile(file);
|
|
4657
|
+
return lock;
|
|
4658
|
+
} catch (cause) {
|
|
4659
|
+
throw new CliError14({
|
|
4660
|
+
code: CliErrorCode13.CONFIG_PARSE_ERROR,
|
|
4661
|
+
message: `Chiron \u9501\u6587\u4EF6\u635F\u574F\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6587\u4EF6\u6240\u6709\u6743\uFF1A${lockPath}`,
|
|
4662
|
+
cause
|
|
4663
|
+
});
|
|
4664
|
+
}
|
|
4665
|
+
}
|
|
4666
|
+
function validateLockedRuntime(runtime) {
|
|
4667
|
+
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha256(`${runtime.capabilities.join("\n")}
|
|
4668
|
+
`) || !runtime.launch || !validDigest(runtime.launch.executableSha256) || !Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4669
|
+
throw new Error("invalid runtime");
|
|
4670
|
+
}
|
|
4671
|
+
if (runtime.launch.kind === "node-entry") {
|
|
4672
|
+
if (!validDigest(runtime.launch.entrySha256)) throw new Error("invalid runtime entry");
|
|
4673
|
+
} else if (runtime.launch.kind !== "executable") {
|
|
4674
|
+
throw new Error("invalid runtime launch kind");
|
|
4675
|
+
}
|
|
4676
|
+
const capabilities = /* @__PURE__ */ new Set();
|
|
4677
|
+
for (const capability of runtime.capabilities) {
|
|
4678
|
+
if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/.test(capability) || capabilities.has(capability)) {
|
|
4679
|
+
throw new Error("invalid runtime capability");
|
|
4680
|
+
}
|
|
4681
|
+
capabilities.add(capability);
|
|
4682
|
+
}
|
|
4683
|
+
const files = /* @__PURE__ */ new Set();
|
|
4684
|
+
for (const file of runtime.files) {
|
|
4685
|
+
validateOwnedFile(file.path);
|
|
4686
|
+
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) {
|
|
4687
|
+
throw new Error("invalid runtime file");
|
|
4688
|
+
}
|
|
4689
|
+
files.add(file.path);
|
|
4690
|
+
}
|
|
4691
|
+
if (!files.has(".hcm-delivery/bin/hcm") || !files.has(".hcm-delivery/bin/hcm.cmd")) {
|
|
4692
|
+
throw new Error("missing runtime file");
|
|
4693
|
+
}
|
|
4694
|
+
}
|
|
4695
|
+
function sameLockContent(left, right) {
|
|
4696
|
+
const withoutTime = (lock) => ({ ...lock, syncedAt: void 0 });
|
|
4697
|
+
return stableJson2(withoutTime(left)) === stableJson2(withoutTime(right));
|
|
4698
|
+
}
|
|
4699
|
+
async function desiredFilesMatch(project, desired) {
|
|
4700
|
+
for (const [file, preparedFile2] of desired) {
|
|
4701
|
+
const absolute = path2.join(project, file);
|
|
4702
|
+
const current = await readOptionalBytes(absolute);
|
|
4703
|
+
if (current === void 0 || sha256(current) !== preparedFile2.sha256 || !await fileModeMatches(absolute, preparedFile2.mode)) {
|
|
4704
|
+
return false;
|
|
4705
|
+
}
|
|
4706
|
+
}
|
|
4707
|
+
return true;
|
|
4708
|
+
}
|
|
4709
|
+
function result(changed, input, project, skillsDir, lockPath, installed, removed) {
|
|
4710
|
+
return {
|
|
4711
|
+
changed,
|
|
4712
|
+
requestedSkill: input.requestedSkill,
|
|
4713
|
+
resolvedSkills: input.documents.map((document) => document.id),
|
|
4714
|
+
project,
|
|
4715
|
+
skillsDir,
|
|
4716
|
+
lockPath,
|
|
4717
|
+
runnerPath: path2.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
4718
|
+
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4719
|
+
installed,
|
|
4720
|
+
removed
|
|
4721
|
+
};
|
|
4722
|
+
}
|
|
4723
|
+
function ownedFile(id, relativePath = "SKILL.md") {
|
|
4724
|
+
assertSafeSkillId(id);
|
|
4725
|
+
assertSafeSkillFilePath(relativePath);
|
|
4726
|
+
return `.codebuddy/skills/${id}/${relativePath}`;
|
|
4727
|
+
}
|
|
4728
|
+
function idFromOwnedFile(file) {
|
|
4729
|
+
validateOwnedFile(file);
|
|
4730
|
+
if (!file.startsWith(".codebuddy/skills/")) return void 0;
|
|
4731
|
+
return file.split("/")[2] ?? "";
|
|
4732
|
+
}
|
|
4733
|
+
function validateOwnedFile(file) {
|
|
4734
|
+
if (file === ".hcm-delivery/bin/hcm" || file === ".hcm-delivery/bin/hcm.cmd") return;
|
|
4735
|
+
const match = /^\.codebuddy\/skills\/([a-z0-9][a-z0-9-]{0,63})\/(.+)$/.exec(file);
|
|
4736
|
+
if (!match) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4737
|
+
const id = match[1];
|
|
4738
|
+
const relativePath = match[2];
|
|
4739
|
+
if (!id || !relativePath) throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4740
|
+
assertSafeSkillId(id);
|
|
4741
|
+
try {
|
|
4742
|
+
assertSafeSkillFilePath(relativePath);
|
|
4743
|
+
} catch {
|
|
4744
|
+
throw invalid(`\u9501\u6587\u4EF6\u5305\u542B\u975E\u6CD5\u6240\u6709\u6743\u8DEF\u5F84\uFF1A${file}`);
|
|
4745
|
+
}
|
|
4746
|
+
}
|
|
4747
|
+
function normalizeEndpoint(endpoint) {
|
|
4748
|
+
let url;
|
|
4749
|
+
try {
|
|
4750
|
+
url = new URL(endpoint);
|
|
4751
|
+
} catch (cause) {
|
|
4752
|
+
throw new CliError14({
|
|
4753
|
+
code: CliErrorCode13.INVALID_ARGUMENT,
|
|
4754
|
+
message: `\u975E\u6CD5 endpoint\uFF1A${endpoint}`,
|
|
4755
|
+
cause
|
|
4756
|
+
});
|
|
4757
|
+
}
|
|
4758
|
+
url.username = "";
|
|
4759
|
+
url.password = "";
|
|
4760
|
+
url.search = "";
|
|
4761
|
+
url.hash = "";
|
|
4762
|
+
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
4763
|
+
return url.toString().replace(/\/$/, "");
|
|
4764
|
+
}
|
|
4765
|
+
function sha256(value) {
|
|
4766
|
+
return createHash("sha256").update(value).digest("hex");
|
|
4767
|
+
}
|
|
4768
|
+
function stableJson2(value) {
|
|
4769
|
+
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
4770
|
+
if (value && typeof value === "object") {
|
|
4771
|
+
const entries = Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right));
|
|
4772
|
+
return `{${entries.map(([key, item]) => `${JSON.stringify(key)}:${stableJson2(item)}`).join(",")}}`;
|
|
4773
|
+
}
|
|
4774
|
+
return JSON.stringify(value);
|
|
4775
|
+
}
|
|
4776
|
+
async function readOptional(file) {
|
|
4777
|
+
try {
|
|
4778
|
+
return await fs4.readFile(file, "utf8");
|
|
4779
|
+
} catch (cause) {
|
|
4780
|
+
if (cause.code === "ENOENT") return void 0;
|
|
4781
|
+
throw cause;
|
|
4782
|
+
}
|
|
4783
|
+
}
|
|
4784
|
+
async function readOptionalBytes(file) {
|
|
4785
|
+
try {
|
|
4786
|
+
return await fs4.readFile(file);
|
|
4787
|
+
} catch (cause) {
|
|
4788
|
+
if (cause.code === "ENOENT") return void 0;
|
|
4789
|
+
throw cause;
|
|
4790
|
+
}
|
|
4791
|
+
}
|
|
4792
|
+
async function exists(file) {
|
|
4793
|
+
try {
|
|
4794
|
+
await fs4.access(file);
|
|
4795
|
+
return true;
|
|
4796
|
+
} catch (cause) {
|
|
4797
|
+
if (cause.code === "ENOENT") return false;
|
|
4798
|
+
throw cause;
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
function lockDigests(lock) {
|
|
4802
|
+
const digests = /* @__PURE__ */ new Map();
|
|
4803
|
+
for (const skill of lock?.resolvedSkills ?? []) {
|
|
4804
|
+
if (skill.files === void 0) {
|
|
4805
|
+
digests.set(ownedFile(skill.id), { sha256: skill.sha256 });
|
|
4806
|
+
continue;
|
|
4807
|
+
}
|
|
4808
|
+
for (const file of skill.files) {
|
|
4809
|
+
digests.set(ownedFile(skill.id, file.path), { sha256: file.sha256 });
|
|
4810
|
+
}
|
|
4811
|
+
}
|
|
4812
|
+
for (const file of lock?.runtime?.files ?? []) {
|
|
4813
|
+
digests.set(file.path, { sha256: file.sha256, mode: file.mode });
|
|
4814
|
+
}
|
|
4815
|
+
return digests;
|
|
4816
|
+
}
|
|
4817
|
+
async function fileModeMatches(file, expected) {
|
|
4818
|
+
if (expected === void 0 || process.platform === "win32") return true;
|
|
4819
|
+
const stat = await fs4.stat(file);
|
|
4820
|
+
return (stat.mode & 511) === expected;
|
|
4821
|
+
}
|
|
4822
|
+
function validDigest(value) {
|
|
4823
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
4824
|
+
}
|
|
4825
|
+
function ignoreMissing(cause) {
|
|
4826
|
+
if (cause.code !== "ENOENT") throw cause;
|
|
4827
|
+
}
|
|
4828
|
+
function invalid(message) {
|
|
4829
|
+
return new CliError14({ code: CliErrorCode13.INVALID_ARGUMENT, message });
|
|
4830
|
+
}
|
|
4831
|
+
|
|
4832
|
+
// src/commands/skills-runtime.ts
|
|
4833
|
+
import { execFile } from "child_process";
|
|
4834
|
+
import { createHash as createHash2 } from "crypto";
|
|
4835
|
+
import { createReadStream, promises as fs5 } from "fs";
|
|
4836
|
+
import path3 from "path";
|
|
4837
|
+
import { promisify } from "util";
|
|
4838
|
+
import { CliError as CliError15, CliErrorCode as CliErrorCode14 } from "@hcmai/sdk";
|
|
4839
|
+
var execFileAsync = promisify(execFile);
|
|
4840
|
+
var REQUIRED_CAPABILITIES = [
|
|
4841
|
+
"hcm.help.skills",
|
|
4842
|
+
"hcm.skills.help.sync",
|
|
4843
|
+
"hcm.skills.sync.option.project",
|
|
4844
|
+
"hcm.skills.sync.option.target"
|
|
4845
|
+
];
|
|
4846
|
+
async function prepareWorkBuddyRuntime(input = {}) {
|
|
4847
|
+
const candidate = input.candidate?.trim() || input.currentEntry || process.argv[1];
|
|
4848
|
+
if (!candidate) throw invalid2("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D HCM CLI \u5165\u53E3\uFF1B\u8BF7\u4F20 --cli <path>");
|
|
4849
|
+
const launch = await resolveLaunch(candidate, input.nodeExecutable ?? process.execPath);
|
|
4850
|
+
const launchTarget = launch.kind === "node-entry" ? launch.entry : launch.executable;
|
|
4851
|
+
for (const forbidden2 of input.forbiddenCandidatePaths ?? []) {
|
|
4852
|
+
if (await comparablePath(forbidden2) === launchTarget) {
|
|
4853
|
+
throw invalid2("\u5019\u9009 HCM CLI \u4E0D\u80FD\u6307\u5411\u5F85\u751F\u6210\u7684\u9879\u76EE\u7A33\u5B9A\u5165\u53E3");
|
|
4854
|
+
}
|
|
4855
|
+
}
|
|
4856
|
+
if (!(input.currentEntryHasRequiredCapabilities && !input.candidate?.trim())) {
|
|
4857
|
+
const beforeProbe = await launchIdentity(launch);
|
|
4858
|
+
const syncHelp = await runProbe(launch, ["skills", "sync", "--help"], input.timeoutMs).catch(
|
|
4859
|
+
() => ""
|
|
4860
|
+
);
|
|
4861
|
+
const missing = [];
|
|
4862
|
+
if (!/\bhcm\s+skills\s+sync\b/.test(syncHelp)) {
|
|
4863
|
+
missing.push(REQUIRED_CAPABILITIES[0], REQUIRED_CAPABILITIES[1]);
|
|
4864
|
+
}
|
|
4865
|
+
if (!/--project(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[2]);
|
|
4866
|
+
if (!/--target(?:\s|=|<|\[|$)/.test(syncHelp)) missing.push(REQUIRED_CAPABILITIES[3]);
|
|
4867
|
+
if (missing.length > 0) {
|
|
4868
|
+
throw invalid2(`\u5019\u9009 HCM CLI \u7F3A\u5C11 WorkBuddy \u6240\u9700\u80FD\u529B\uFF1A${missing.join(", ")}`);
|
|
4869
|
+
}
|
|
4870
|
+
if (await launchIdentity(launch) !== beforeProbe) {
|
|
4871
|
+
throw invalid2("\u5019\u9009 HCM CLI \u5728\u80FD\u529B\u63A2\u9488\u671F\u95F4\u53D1\u751F\u53D8\u5316\uFF0C\u8BF7\u91CD\u8BD5\u540C\u6B65");
|
|
4872
|
+
}
|
|
4873
|
+
}
|
|
4874
|
+
const capabilities = [...REQUIRED_CAPABILITIES];
|
|
4875
|
+
const capabilitiesSha256 = sha2562(`${capabilities.join("\n")}
|
|
4876
|
+
`);
|
|
4877
|
+
const executableSha256 = await sha256File(launch.executable);
|
|
4878
|
+
const launchRecord = launch.kind === "node-entry" ? {
|
|
4879
|
+
kind: "node-entry",
|
|
4880
|
+
executableSha256,
|
|
4881
|
+
entrySha256: await sha256File(launch.entry)
|
|
4882
|
+
} : { kind: "executable", executableSha256 };
|
|
4883
|
+
const unixContent = Buffer.from(unixLauncher(launch), "utf8");
|
|
4884
|
+
const windowsContent = Buffer.from(windowsLauncher(launch), "utf8");
|
|
4885
|
+
return {
|
|
4886
|
+
capabilities,
|
|
4887
|
+
capabilitiesSha256,
|
|
4888
|
+
launch: launchRecord,
|
|
4889
|
+
files: [
|
|
4890
|
+
preparedFile(".hcm-delivery/bin/hcm", unixContent, 493),
|
|
4891
|
+
preparedFile(".hcm-delivery/bin/hcm.cmd", windowsContent, 420)
|
|
4892
|
+
]
|
|
4893
|
+
};
|
|
4894
|
+
}
|
|
4895
|
+
async function resolveLaunch(candidate, nodeExecutable) {
|
|
4896
|
+
const requested = path3.resolve(candidate);
|
|
4897
|
+
const resolvedCandidate = await realFile(requested, "\u5019\u9009 HCM CLI");
|
|
4898
|
+
const header = (await readPrefix(resolvedCandidate, 256)).toString("utf8");
|
|
4899
|
+
const nodeEntry = /^(?:#![^\r\n]*\bnode\b)|\.(?:c|m)?js$/i.test(
|
|
4900
|
+
header.startsWith("#!") ? header.split(/\r?\n/, 1)[0] ?? "" : resolvedCandidate
|
|
4901
|
+
);
|
|
4902
|
+
if (nodeEntry) {
|
|
4903
|
+
return {
|
|
4904
|
+
kind: "node-entry",
|
|
4905
|
+
executable: await realFile(path3.resolve(nodeExecutable), "Node \u53EF\u6267\u884C\u6587\u4EF6"),
|
|
4906
|
+
entry: resolvedCandidate
|
|
4907
|
+
};
|
|
4908
|
+
}
|
|
4909
|
+
return { kind: "executable", executable: resolvedCandidate };
|
|
4910
|
+
}
|
|
4911
|
+
async function realFile(file, label) {
|
|
4912
|
+
try {
|
|
4913
|
+
const resolved = await fs5.realpath(file);
|
|
4914
|
+
const stat = await fs5.stat(resolved);
|
|
4915
|
+
if (!stat.isFile()) throw new Error("not a regular file");
|
|
4916
|
+
return resolved;
|
|
4917
|
+
} catch (cause) {
|
|
4918
|
+
throw new CliError15({
|
|
4919
|
+
code: CliErrorCode14.INVALID_ARGUMENT,
|
|
4920
|
+
message: `${label}\u4E0D\u53EF\u7528\uFF1A${file}`,
|
|
4921
|
+
cause
|
|
4922
|
+
});
|
|
4923
|
+
}
|
|
4924
|
+
}
|
|
4925
|
+
async function comparablePath(file) {
|
|
4926
|
+
const absolute = path3.resolve(file);
|
|
4927
|
+
return fs5.realpath(absolute).catch(() => absolute);
|
|
4928
|
+
}
|
|
4929
|
+
async function launchIdentity(launch) {
|
|
4930
|
+
const files = launch.kind === "node-entry" ? [launch.executable, launch.entry] : [launch.executable];
|
|
4931
|
+
const identities = await Promise.all(
|
|
4932
|
+
files.map(async (file) => {
|
|
4933
|
+
const stat = await fs5.stat(file, { bigint: true });
|
|
4934
|
+
return [file, stat.dev, stat.ino, stat.size, stat.mtimeNs, stat.ctimeNs].map(String).join("\0");
|
|
4935
|
+
})
|
|
4936
|
+
);
|
|
4937
|
+
return identities.join("\n");
|
|
4938
|
+
}
|
|
4939
|
+
async function readPrefix(file, bytes) {
|
|
4940
|
+
const handle = await fs5.open(file, "r");
|
|
4941
|
+
try {
|
|
4942
|
+
const buffer = Buffer.alloc(bytes);
|
|
4943
|
+
const result2 = await handle.read(buffer, 0, bytes, 0);
|
|
4944
|
+
return buffer.subarray(0, result2.bytesRead);
|
|
4945
|
+
} finally {
|
|
4946
|
+
await handle.close();
|
|
4947
|
+
}
|
|
4948
|
+
}
|
|
4949
|
+
async function runProbe(launch, args, timeoutMs = 5e3) {
|
|
4950
|
+
const command = launch.executable;
|
|
4951
|
+
const commandArgs = launch.kind === "node-entry" ? [launch.entry, ...args] : args;
|
|
4952
|
+
const env2 = {
|
|
4953
|
+
...process.env,
|
|
4954
|
+
CI: "1",
|
|
4955
|
+
NO_COLOR: "1",
|
|
4956
|
+
HCM_RUNTIME_PROBE: "1"
|
|
4957
|
+
};
|
|
4958
|
+
const nodeOptions = normalizeWorkBuddyNodeOptions(env2.NODE_OPTIONS);
|
|
4959
|
+
if (nodeOptions === void 0) delete env2.NODE_OPTIONS;
|
|
4960
|
+
else env2.NODE_OPTIONS = nodeOptions;
|
|
4961
|
+
try {
|
|
4962
|
+
const { stdout, stderr } = await execFileAsync(command, commandArgs, {
|
|
4963
|
+
encoding: "utf8",
|
|
4964
|
+
timeout: timeoutMs,
|
|
4965
|
+
maxBuffer: 256 * 1024,
|
|
4966
|
+
windowsHide: true,
|
|
4967
|
+
env: env2
|
|
4968
|
+
});
|
|
4969
|
+
return `${stdout}
|
|
4970
|
+
${stderr}`;
|
|
4971
|
+
} catch (cause) {
|
|
4972
|
+
throw new CliError15({
|
|
4973
|
+
code: CliErrorCode14.INVALID_ARGUMENT,
|
|
4974
|
+
message: `\u5019\u9009 HCM CLI \u80FD\u529B\u63A2\u9488\u5931\u8D25\uFF1Ahcm ${args.join(" ")}`,
|
|
4975
|
+
cause
|
|
4976
|
+
});
|
|
4977
|
+
}
|
|
4978
|
+
}
|
|
4979
|
+
function unixLauncher(launch) {
|
|
4980
|
+
const command = shellQuote(launch.executable);
|
|
4981
|
+
const entry = launch.kind === "node-entry" ? ` ${shellQuote(launch.entry)}` : "";
|
|
4982
|
+
return `#!/bin/sh
|
|
4983
|
+
set -eu
|
|
4984
|
+
|
|
4985
|
+
# WorkBuddy on macOS may inject --use-system-ca. Node can then spend minutes
|
|
4986
|
+
# evaluating the Keychain when a short-lived CLI process exits. Preserve every
|
|
4987
|
+
# other host option (including safety --require hooks) and change only this CA mode.
|
|
4988
|
+
wb_node_options="\${NODE_OPTIONS-}"
|
|
4989
|
+
if [ "$(uname -s 2>/dev/null || printf '%s' unknown)" = 'Darwin' ] && printf '%s
|
|
4990
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; then
|
|
4991
|
+
while printf '%s
|
|
4992
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-system-ca([[:space:]]|$)'; do
|
|
4993
|
+
wb_next_options="$(printf '%s
|
|
4994
|
+
' "$wb_node_options" | sed -E 's/(^|[[:space:]])--use-system-ca([[:space:]]|$)/\\1\\2/g')"
|
|
4995
|
+
[ "$wb_next_options" != "$wb_node_options" ] || break
|
|
4996
|
+
wb_node_options="$wb_next_options"
|
|
4997
|
+
done
|
|
4998
|
+
if ! printf '%s
|
|
4999
|
+
' "$wb_node_options" | grep -Eq '(^|[[:space:]])--use-bundled-ca([[:space:]]|$)'; then
|
|
5000
|
+
wb_node_options="\${wb_node_options:+$wb_node_options }--use-bundled-ca"
|
|
5001
|
+
fi
|
|
5002
|
+
NODE_OPTIONS="$wb_node_options"
|
|
5003
|
+
export NODE_OPTIONS
|
|
5004
|
+
fi
|
|
5005
|
+
|
|
5006
|
+
exec ${command}${entry} "$@"
|
|
5007
|
+
`;
|
|
5008
|
+
}
|
|
5009
|
+
function normalizeWorkBuddyNodeOptions(value, platform = process.platform) {
|
|
5010
|
+
if (platform !== "darwin" || value === void 0) return value;
|
|
5011
|
+
const systemCa = /(^|[\t\n\r ])--use-system-ca(?=$|[\t\n\r ])/;
|
|
5012
|
+
if (!systemCa.test(value)) return value;
|
|
5013
|
+
let normalized = value;
|
|
5014
|
+
while (systemCa.test(normalized)) normalized = normalized.replace(systemCa, "$1");
|
|
5015
|
+
normalized = normalized.trim();
|
|
5016
|
+
const bundledCa = /(^|[\t\n\r ])--use-bundled-ca(?=$|[\t\n\r ])/;
|
|
5017
|
+
if (!bundledCa.test(normalized)) {
|
|
5018
|
+
normalized = `${normalized}${normalized ? " " : ""}--use-bundled-ca`;
|
|
5019
|
+
}
|
|
5020
|
+
return normalized;
|
|
5021
|
+
}
|
|
5022
|
+
function windowsLauncher(launch) {
|
|
5023
|
+
const command = cmdQuote(launch.executable);
|
|
5024
|
+
const entry = launch.kind === "node-entry" ? ` ${cmdQuote(launch.entry)}` : "";
|
|
5025
|
+
return `@echo off\r
|
|
5026
|
+
${command}${entry} %*\r
|
|
5027
|
+
`;
|
|
5028
|
+
}
|
|
5029
|
+
function shellQuote(value) {
|
|
5030
|
+
if (/[\r\n\0]/.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5031
|
+
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
5032
|
+
}
|
|
5033
|
+
function cmdQuote(value) {
|
|
5034
|
+
if (/[\r\n\0"]/g.test(value)) throw invalid2("CLI \u542F\u52A8\u8DEF\u5F84\u5305\u542B\u975E\u6CD5\u63A7\u5236\u5B57\u7B26");
|
|
5035
|
+
return `"${value.replace(/%/g, "%%")}"`;
|
|
5036
|
+
}
|
|
5037
|
+
function preparedFile(filePath, content, mode) {
|
|
5038
|
+
return { path: filePath, content, bytes: content.length, sha256: sha2562(content), mode };
|
|
5039
|
+
}
|
|
5040
|
+
async function sha256File(file) {
|
|
5041
|
+
const hash = createHash2("sha256");
|
|
5042
|
+
await new Promise((resolve3, reject) => {
|
|
5043
|
+
const stream = createReadStream(file);
|
|
5044
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
5045
|
+
stream.on("error", reject);
|
|
5046
|
+
stream.on("end", resolve3);
|
|
5047
|
+
});
|
|
5048
|
+
return hash.digest("hex");
|
|
5049
|
+
}
|
|
5050
|
+
function sha2562(value) {
|
|
5051
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
5052
|
+
}
|
|
5053
|
+
function invalid2(message) {
|
|
5054
|
+
return new CliError15({ code: CliErrorCode14.INVALID_ARGUMENT, message });
|
|
5055
|
+
}
|
|
5056
|
+
|
|
5057
|
+
// src/commands/skills.ts
|
|
4253
5058
|
var SKILLS_STATE_FILE = ".hcm-skills.json";
|
|
4254
|
-
function
|
|
4255
|
-
return
|
|
5059
|
+
function sha2563(text) {
|
|
5060
|
+
return createHash3("sha256").update(text, "utf8").digest("hex");
|
|
4256
5061
|
}
|
|
4257
5062
|
async function resolveEndpoint(args) {
|
|
4258
5063
|
if (args.endpoint) return args.endpoint.replace(/\/+$/, "");
|
|
@@ -4260,14 +5065,43 @@ async function resolveEndpoint(args) {
|
|
|
4260
5065
|
const envName = resolveActiveEnv3({ envFlag: args.env }, process.env, g);
|
|
4261
5066
|
const env2 = await loadEnv5(envName).catch(() => null);
|
|
4262
5067
|
if (!env2?.endpoint) {
|
|
4263
|
-
throw new
|
|
4264
|
-
code:
|
|
5068
|
+
throw new CliError16({
|
|
5069
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
4265
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"
|
|
4266
5071
|
});
|
|
4267
5072
|
}
|
|
4268
5073
|
return env2.endpoint.replace(/\/+$/, "");
|
|
4269
5074
|
}
|
|
4270
|
-
|
|
5075
|
+
function normalizeEndpoint2(endpoint) {
|
|
5076
|
+
return endpoint.replace(/\/+$/, "");
|
|
5077
|
+
}
|
|
5078
|
+
function decideTrackedEndpoint(options) {
|
|
5079
|
+
const { explicit } = options;
|
|
5080
|
+
const current = options.current ? normalizeEndpoint2(options.current) : void 0;
|
|
5081
|
+
const recorded = options.recorded ? normalizeEndpoint2(options.recorded) : void 0;
|
|
5082
|
+
if (explicit) {
|
|
5083
|
+
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");
|
|
5084
|
+
return current;
|
|
5085
|
+
}
|
|
5086
|
+
if (!recorded) {
|
|
5087
|
+
if (!current) {
|
|
5088
|
+
throw new CliError16({
|
|
5089
|
+
code: CliErrorCode15.MISSING_FLAG,
|
|
5090
|
+
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"
|
|
5091
|
+
});
|
|
5092
|
+
}
|
|
5093
|
+
return current;
|
|
5094
|
+
}
|
|
5095
|
+
if (!current || current === recorded) return recorded;
|
|
5096
|
+
throw invalid3(
|
|
5097
|
+
`\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
|
|
5098
|
+
\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
|
|
5099
|
+
\u660E\u8BF4\u8981\u54EA\u4E2A\uFF1A
|
|
5100
|
+
\xB7 \u7EE7\u7EED\u8DDF ${recorded} \u2192 \u52A0 --endpoint ${recorded}
|
|
5101
|
+
\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`
|
|
5102
|
+
);
|
|
5103
|
+
}
|
|
5104
|
+
async function exists2(target) {
|
|
4271
5105
|
try {
|
|
4272
5106
|
await fsp.stat(target);
|
|
4273
5107
|
return true;
|
|
@@ -4276,25 +5110,25 @@ async function exists(target) {
|
|
|
4276
5110
|
}
|
|
4277
5111
|
}
|
|
4278
5112
|
async function detectTarget(cwd) {
|
|
4279
|
-
if (await
|
|
4280
|
-
if (await
|
|
4281
|
-
if (await
|
|
5113
|
+
if (await exists2(path4.join(cwd, ".claude"))) return "claude";
|
|
5114
|
+
if (await exists2(path4.join(cwd, "AGENTS.md"))) return "codex";
|
|
5115
|
+
if (await exists2(path4.join(cwd, ".codex"))) return "codex";
|
|
4282
5116
|
return void 0;
|
|
4283
5117
|
}
|
|
4284
5118
|
async function resolveInstallLayout(args, cwd = process.cwd()) {
|
|
4285
5119
|
const requested = args.target && args.target !== "auto" ? args.target : void 0;
|
|
4286
5120
|
const target = requested ?? await detectTarget(cwd) ?? "claude";
|
|
4287
5121
|
if (target === "codex") {
|
|
4288
|
-
const root = args.global ?
|
|
5122
|
+
const root = args.global ? path4.join(os2.homedir(), ".codex") : cwd;
|
|
4289
5123
|
return {
|
|
4290
5124
|
target,
|
|
4291
|
-
baseDir: args.dir ?
|
|
4292
|
-
agentsFile:
|
|
5125
|
+
baseDir: args.dir ? path4.resolve(args.dir) : path4.join(root, "hcm-skills"),
|
|
5126
|
+
agentsFile: path4.join(root, "AGENTS.md")
|
|
4293
5127
|
};
|
|
4294
5128
|
}
|
|
4295
5129
|
return {
|
|
4296
5130
|
target,
|
|
4297
|
-
baseDir: args.dir ?
|
|
5131
|
+
baseDir: args.dir ? path4.resolve(args.dir) : args.global ? path4.join(os2.homedir(), ".claude", "skills") : path4.join(cwd, ".claude", "skills")
|
|
4298
5132
|
};
|
|
4299
5133
|
}
|
|
4300
5134
|
async function chironClient(args) {
|
|
@@ -4397,7 +5231,7 @@ async function listInstalledSkills(keyword, args) {
|
|
|
4397
5231
|
process.stdout.write(formatObject13({ baseDir: layout.baseDir, target: layout.target, all: false, skills: [] }, { format: fmt }) + "\n");
|
|
4398
5232
|
process.exit(0);
|
|
4399
5233
|
}
|
|
4400
|
-
process.stdout.write(`${
|
|
5234
|
+
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
|
|
4401
5235
|
`);
|
|
4402
5236
|
process.stdout.write(" \u88C5\u5168\u90E8\uFF1Ahcm skills install --all\n");
|
|
4403
5237
|
process.exit(0);
|
|
@@ -4416,7 +5250,7 @@ async function listInstalledSkills(keyword, args) {
|
|
|
4416
5250
|
direct: record.direct,
|
|
4417
5251
|
installedAt: record.installedAt,
|
|
4418
5252
|
// 三种状态互斥:文件没了 / 本地改过 / 和装的时候一致
|
|
4419
|
-
status: markdown === void 0 ? "missing" :
|
|
5253
|
+
status: markdown === void 0 ? "missing" : sha2563(markdown) !== record.sha256 ? "locally-modified" : "installed"
|
|
4420
5254
|
});
|
|
4421
5255
|
}
|
|
4422
5256
|
const shown = keyword ? rows.filter((row) => row.id.includes(keyword) || row.title.includes(keyword)) : rows;
|
|
@@ -4470,12 +5304,74 @@ async function skillsShowCommand(id, args) {
|
|
|
4470
5304
|
fail3(e, args);
|
|
4471
5305
|
}
|
|
4472
5306
|
}
|
|
5307
|
+
async function skillsSyncCommand(id, args) {
|
|
5308
|
+
try {
|
|
5309
|
+
assertSafeSkillId2(id);
|
|
5310
|
+
if (args.target !== "workbuddy") {
|
|
5311
|
+
throw invalid3(`sync --target \u5F53\u524D\u53EA\u652F\u6301 workbuddy\uFF0C\u6536\u5230 "${args.target ?? ""}"`);
|
|
5312
|
+
}
|
|
5313
|
+
if (!args.project?.trim()) throw invalid3("sync \u5FC5\u987B\u4F20 --project <path>");
|
|
5314
|
+
const project = path4.resolve(args.project);
|
|
5315
|
+
const runtime = await prepareWorkBuddyRuntime({
|
|
5316
|
+
candidate: args.cli,
|
|
5317
|
+
currentEntryHasRequiredCapabilities: !args.cli?.trim(),
|
|
5318
|
+
forbiddenCandidatePaths: [
|
|
5319
|
+
path4.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
5320
|
+
path4.join(project, ".hcm-delivery", "bin", "hcm.cmd")
|
|
5321
|
+
]
|
|
5322
|
+
});
|
|
5323
|
+
const connectionArgs = { endpoint: args.endpoint, env: args.env };
|
|
5324
|
+
const { http, endpoint, base } = await chironClient(connectionArgs);
|
|
5325
|
+
const catalog = await fetchSkillCatalog(http, base);
|
|
5326
|
+
const documents = await prepareSkillDocuments(
|
|
5327
|
+
catalog,
|
|
5328
|
+
id,
|
|
5329
|
+
async (skillId) => fetchSkillMarkdown(http, skillId, base),
|
|
5330
|
+
async (skillId, relativePath) => fetchSkillFile(http, skillId, relativePath, base),
|
|
5331
|
+
async (skillId, relativePath) => fetchSkillReference(http, skillId, relativePath, base)
|
|
5332
|
+
);
|
|
5333
|
+
const result2 = await syncWorkBuddyProject({
|
|
5334
|
+
project,
|
|
5335
|
+
endpoint,
|
|
5336
|
+
catalog,
|
|
5337
|
+
requestedSkill: id,
|
|
5338
|
+
documents,
|
|
5339
|
+
runtime
|
|
5340
|
+
});
|
|
5341
|
+
const fmt = args.output ?? "table";
|
|
5342
|
+
if (fmt === "json" || fmt === "yaml") {
|
|
5343
|
+
process.stdout.write(`${formatObject13(result2, { format: fmt })}
|
|
5344
|
+
`);
|
|
5345
|
+
} else if (result2.changed) {
|
|
5346
|
+
process.stdout.write(`\u2705 ${id} \u2192 ${result2.skillsDir}
|
|
5347
|
+
`);
|
|
5348
|
+
process.stdout.write(` \u95ED\u5305 ${result2.resolvedSkills.join(" \u2192 ")}
|
|
5349
|
+
`);
|
|
5350
|
+
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5351
|
+
`);
|
|
5352
|
+
process.stdout.write(` CLI \u80FD\u529B ${result2.capabilitiesSha256.slice(0, 12)}
|
|
5353
|
+
`);
|
|
5354
|
+
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5355
|
+
`);
|
|
5356
|
+
} else {
|
|
5357
|
+
process.stdout.write(`\u2713 ${id} \u5DF2\u662F\u5F53\u524D\u76EE\u6807\u5B9E\u4F8B\u7248\u672C\uFF0C\u65E0\u9700\u66F4\u65B0
|
|
5358
|
+
`);
|
|
5359
|
+
process.stdout.write(` \u5165\u53E3 ${result2.runnerPath}
|
|
5360
|
+
`);
|
|
5361
|
+
process.stdout.write(` \u9501 ${result2.lockPath}
|
|
5362
|
+
`);
|
|
5363
|
+
}
|
|
5364
|
+
process.exit(0);
|
|
5365
|
+
} catch (e) {
|
|
5366
|
+
fail3(e, { endpoint: args.endpoint, env: args.env });
|
|
5367
|
+
}
|
|
5368
|
+
}
|
|
4473
5369
|
async function skillsInstallCommand(id, args) {
|
|
4474
5370
|
try {
|
|
4475
5371
|
const all = Boolean(args.all);
|
|
4476
|
-
if (all && id) throw
|
|
4477
|
-
if (!all && !id) throw
|
|
4478
|
-
if (id)
|
|
5372
|
+
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");
|
|
5373
|
+
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");
|
|
5374
|
+
if (id) assertSafeSkillId2(id);
|
|
4479
5375
|
const layout = await resolveInstallLayout(args);
|
|
4480
5376
|
let documents;
|
|
4481
5377
|
let requestedIds;
|
|
@@ -4483,7 +5379,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
4483
5379
|
let base;
|
|
4484
5380
|
let manifest2;
|
|
4485
5381
|
if (args.from) {
|
|
4486
|
-
const root =
|
|
5382
|
+
const root = path4.resolve(args.from);
|
|
4487
5383
|
requestedIds = all ? await listLocalSkillIds(root) : [id];
|
|
4488
5384
|
const seen = /* @__PURE__ */ new Set();
|
|
4489
5385
|
documents = [];
|
|
@@ -4495,12 +5391,17 @@ async function skillsInstallCommand(id, args) {
|
|
|
4495
5391
|
}
|
|
4496
5392
|
}
|
|
4497
5393
|
} else {
|
|
4498
|
-
const
|
|
5394
|
+
const tracked = decideTrackedEndpoint({
|
|
5395
|
+
explicit: Boolean(args.endpoint || args.env),
|
|
5396
|
+
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5397
|
+
recorded: (await readSkillsState(layout.baseDir))?.endpoint
|
|
5398
|
+
});
|
|
5399
|
+
const client3 = await chironClient({ ...args, endpoint: tracked });
|
|
4499
5400
|
endpoint = client3.endpoint;
|
|
4500
5401
|
base = client3.base;
|
|
4501
5402
|
manifest2 = await fetchSkillCatalog(client3.http, base);
|
|
4502
|
-
const order = all ? resolveAllSkillsInstallOrder(manifest2) :
|
|
4503
|
-
if (all && order.length === 0) throw
|
|
5403
|
+
const order = all ? resolveAllSkillsInstallOrder(manifest2) : resolveSkillInstallOrder2(manifest2, id);
|
|
5404
|
+
if (all && order.length === 0) throw invalid3(`${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u662F\u7A7A\u7684\uFF0C\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
|
|
4504
5405
|
requestedIds = all ? order.map((skill) => skill.id) : [id];
|
|
4505
5406
|
documents = await mapWithConcurrency(
|
|
4506
5407
|
order,
|
|
@@ -4508,7 +5409,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
4508
5409
|
(skill) => fetchSkillDocument(client3.http, skill, endpoint, base)
|
|
4509
5410
|
);
|
|
4510
5411
|
}
|
|
4511
|
-
const
|
|
5412
|
+
const result2 = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
|
|
4512
5413
|
endpoint,
|
|
4513
5414
|
base,
|
|
4514
5415
|
target: layout.target,
|
|
@@ -4517,19 +5418,19 @@ async function skillsInstallCommand(id, args) {
|
|
|
4517
5418
|
const agents = layout.agentsFile ? await syncAgentsIndex(layout, manifest2) : void 0;
|
|
4518
5419
|
const fmt = args.output ?? "table";
|
|
4519
5420
|
if (fmt === "json" || fmt === "yaml") {
|
|
4520
|
-
process.stdout.write(formatObject13({ ...
|
|
5421
|
+
process.stdout.write(formatObject13({ ...result2, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents }, { format: fmt }) + "\n");
|
|
4521
5422
|
} else {
|
|
4522
|
-
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${
|
|
5423
|
+
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result2.baseDir}` : `\u2705 ${id} \u2192 ${path4.join(result2.baseDir, id, "SKILL.md")}`;
|
|
4523
5424
|
process.stdout.write(`${headline}
|
|
4524
5425
|
`);
|
|
4525
|
-
if (
|
|
4526
|
-
process.stdout.write(all ? ` \u65B0\u5199\u5165 ${
|
|
4527
|
-
` : ` \u5DF2\u5B89\u88C5 ${
|
|
5426
|
+
if (result2.installed.length > 0) {
|
|
5427
|
+
process.stdout.write(all ? ` \u65B0\u5199\u5165 ${result2.installed.length} \u4E2A
|
|
5428
|
+
` : ` \u5DF2\u5B89\u88C5 ${result2.installed.join(" \u2192 ")}
|
|
4528
5429
|
`);
|
|
4529
5430
|
}
|
|
4530
|
-
if (
|
|
4531
|
-
process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${
|
|
4532
|
-
` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${
|
|
5431
|
+
if (result2.unchanged.length > 0) {
|
|
5432
|
+
process.stdout.write(all ? ` \u5DF2\u662F\u8FD9\u4E00\u7248\u3001\u672A\u6539\u52A8 ${result2.unchanged.length} \u4E2A
|
|
5433
|
+
` : ` \u5DF2\u5B58\u5728\u4E14\u7248\u672C\u4E00\u81F4 ${result2.unchanged.join(", ")}
|
|
4533
5434
|
`);
|
|
4534
5435
|
}
|
|
4535
5436
|
const referenceCount = documents.reduce((n, d) => n + (d.references?.length ?? 0), 0);
|
|
@@ -4541,7 +5442,7 @@ async function skillsInstallCommand(id, args) {
|
|
|
4541
5442
|
process.stdout.write(` \u5DF2${agents === "created" ? "\u521B\u5EFA" : "\u66F4\u65B0"} ${layout.agentsFile} \u91CC\u7684\u6280\u80FD\u7D22\u5F15\uFF08Codex \u8BFB\u8FD9\u91CC\uFF09
|
|
4542
5443
|
`);
|
|
4543
5444
|
}
|
|
4544
|
-
process.stdout.write(` \u6765\u6E90 ${endpoint ??
|
|
5445
|
+
process.stdout.write(` \u6765\u6E90 ${endpoint ?? path4.resolve(args.from)}
|
|
4545
5446
|
`);
|
|
4546
5447
|
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");
|
|
4547
5448
|
}
|
|
@@ -4569,18 +5470,18 @@ async function listLocalSkillIds(root) {
|
|
|
4569
5470
|
try {
|
|
4570
5471
|
entries = await fsp.readdir(root, { withFileTypes: true });
|
|
4571
5472
|
} catch {
|
|
4572
|
-
throw
|
|
5473
|
+
throw invalid3(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
|
|
4573
5474
|
}
|
|
4574
5475
|
const ids = [];
|
|
4575
5476
|
for (const entry of entries) {
|
|
4576
5477
|
if (!entry.isDirectory()) continue;
|
|
4577
|
-
if (await
|
|
5478
|
+
if (await exists2(path4.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
|
|
4578
5479
|
}
|
|
4579
|
-
if (ids.length === 0) throw
|
|
5480
|
+
if (ids.length === 0) throw invalid3(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
|
|
4580
5481
|
return ids.sort();
|
|
4581
5482
|
}
|
|
4582
5483
|
async function fetchSkillDocument(http, skill, endpoint, base) {
|
|
4583
|
-
const relatives =
|
|
5484
|
+
const relatives = normalizeReferences2(skill.references, skill.id);
|
|
4584
5485
|
const references = await Promise.all(relatives.map(async (relative2) => ({
|
|
4585
5486
|
path: relative2,
|
|
4586
5487
|
content: await fetchSkillReference(http, skill.id, relative2, base)
|
|
@@ -4600,23 +5501,23 @@ async function syncAgentsIndex(layout, manifest2) {
|
|
|
4600
5501
|
for (const category of manifest2?.categories ?? []) {
|
|
4601
5502
|
for (const skill of category.skills ?? []) catalog.set(skill.id, skill);
|
|
4602
5503
|
}
|
|
4603
|
-
const agentsDir =
|
|
5504
|
+
const agentsDir = path4.dirname(layout.agentsFile);
|
|
4604
5505
|
const entries = Object.keys(state.skills).sort().map((id) => {
|
|
4605
5506
|
const known = catalog.get(id);
|
|
4606
|
-
const absolute =
|
|
4607
|
-
let relative2 =
|
|
5507
|
+
const absolute = path4.join(layout.baseDir, id, "SKILL.md");
|
|
5508
|
+
let relative2 = path4.relative(agentsDir, absolute);
|
|
4608
5509
|
if (!relative2.startsWith(".")) relative2 = `./${relative2}`;
|
|
4609
5510
|
return {
|
|
4610
5511
|
id,
|
|
4611
5512
|
title: known?.title ?? id,
|
|
4612
5513
|
description: known?.description ?? "",
|
|
4613
|
-
relativePath: relative2.split(
|
|
5514
|
+
relativePath: relative2.split(path4.sep).join("/")
|
|
4614
5515
|
};
|
|
4615
5516
|
});
|
|
4616
5517
|
return upsertAgentsBlock(layout.agentsFile, renderAgentsBlock(entries));
|
|
4617
5518
|
}
|
|
4618
5519
|
async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
4619
|
-
|
|
5520
|
+
assertSafeSkillId2(targetId);
|
|
4620
5521
|
const state = /* @__PURE__ */ new Map();
|
|
4621
5522
|
const pathStack = [];
|
|
4622
5523
|
const order = [];
|
|
@@ -4625,27 +5526,27 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
4625
5526
|
if (current === "done") return;
|
|
4626
5527
|
if (current === "visiting") {
|
|
4627
5528
|
const start = pathStack.indexOf(id);
|
|
4628
|
-
throw
|
|
5529
|
+
throw invalid3(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
|
|
4629
5530
|
}
|
|
4630
|
-
|
|
4631
|
-
const local =
|
|
5531
|
+
assertSafeSkillId2(id);
|
|
5532
|
+
const local = path4.join(sourceRoot, id, "SKILL.md");
|
|
4632
5533
|
let markdown;
|
|
4633
5534
|
try {
|
|
4634
5535
|
markdown = await fsp.readFile(local, "utf8");
|
|
4635
5536
|
} catch (cause) {
|
|
4636
5537
|
if (cause?.code === "ENOENT") {
|
|
4637
|
-
throw
|
|
5538
|
+
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)}`);
|
|
4638
5539
|
}
|
|
4639
5540
|
throw cause;
|
|
4640
5541
|
}
|
|
4641
|
-
if (!markdown.trim()) throw
|
|
5542
|
+
if (!markdown.trim()) throw invalid3(`\u6280\u80FD ${id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4642
5543
|
state.set(id, "visiting");
|
|
4643
5544
|
pathStack.push(id);
|
|
4644
5545
|
const document = {
|
|
4645
5546
|
id,
|
|
4646
5547
|
markdown,
|
|
4647
5548
|
source: local,
|
|
4648
|
-
references: await readLocalReferences(
|
|
5549
|
+
references: await readLocalReferences(path4.join(sourceRoot, id))
|
|
4649
5550
|
};
|
|
4650
5551
|
for (const required of parseSkillRequirements(markdown, id)) {
|
|
4651
5552
|
await visit(required, id);
|
|
@@ -4659,7 +5560,7 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
4659
5560
|
}
|
|
4660
5561
|
async function readSkillsState(baseDir) {
|
|
4661
5562
|
try {
|
|
4662
|
-
const raw = await fsp.readFile(
|
|
5563
|
+
const raw = await fsp.readFile(path4.join(baseDir, SKILLS_STATE_FILE), "utf8");
|
|
4663
5564
|
const parsed = JSON.parse(raw);
|
|
4664
5565
|
if (!parsed || typeof parsed !== "object" || !parsed.skills) return void 0;
|
|
4665
5566
|
return parsed;
|
|
@@ -4669,7 +5570,7 @@ async function readSkillsState(baseDir) {
|
|
|
4669
5570
|
}
|
|
4670
5571
|
async function writeSkillsState(baseDir, state) {
|
|
4671
5572
|
await fsp.mkdir(baseDir, { recursive: true });
|
|
4672
|
-
const file =
|
|
5573
|
+
const file = path4.join(baseDir, SKILLS_STATE_FILE);
|
|
4673
5574
|
const temp = `${file}.${process.pid}.tmp`;
|
|
4674
5575
|
await fsp.writeFile(temp, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
4675
5576
|
await fsp.rename(temp, file);
|
|
@@ -4698,7 +5599,7 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
4698
5599
|
existing = await fsp.readFile(agentsFile, "utf8");
|
|
4699
5600
|
} catch (cause) {
|
|
4700
5601
|
if (cause?.code !== "ENOENT") throw cause;
|
|
4701
|
-
await fsp.mkdir(
|
|
5602
|
+
await fsp.mkdir(path4.dirname(agentsFile), { recursive: true });
|
|
4702
5603
|
await fsp.writeFile(agentsFile, block + "\n", "utf8");
|
|
4703
5604
|
return "created";
|
|
4704
5605
|
}
|
|
@@ -4714,22 +5615,22 @@ async function upsertAgentsBlock(agentsFile, block) {
|
|
|
4714
5615
|
return "appended";
|
|
4715
5616
|
}
|
|
4716
5617
|
async function installSkillDocuments(documents, requestedIds, baseDir, force, meta2) {
|
|
4717
|
-
if (requestedIds.length === 0) throw
|
|
4718
|
-
for (const id of requestedIds)
|
|
5618
|
+
if (requestedIds.length === 0) throw invalid3("\u6CA1\u6709\u6307\u5B9A\u8981\u88C5\u7684\u6280\u80FD");
|
|
5619
|
+
for (const id of requestedIds) assertSafeSkillId2(id);
|
|
4719
5620
|
const seen = /* @__PURE__ */ new Set();
|
|
4720
5621
|
const candidates = documents.map((document) => {
|
|
4721
|
-
|
|
4722
|
-
if (seen.has(document.id)) throw
|
|
5622
|
+
assertSafeSkillId2(document.id);
|
|
5623
|
+
if (seen.has(document.id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u5305\u542B\u91CD\u590D\u6280\u80FD: ${document.id}`);
|
|
4723
5624
|
seen.add(document.id);
|
|
4724
|
-
if (!document.markdown.trim()) throw
|
|
5625
|
+
if (!document.markdown.trim()) throw invalid3(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
4725
5626
|
return {
|
|
4726
5627
|
...document,
|
|
4727
|
-
target:
|
|
5628
|
+
target: path4.join(baseDir, document.id, "SKILL.md"),
|
|
4728
5629
|
bytes: Buffer.byteLength(document.markdown)
|
|
4729
5630
|
};
|
|
4730
5631
|
});
|
|
4731
5632
|
for (const id of requestedIds) {
|
|
4732
|
-
if (!seen.has(id)) throw
|
|
5633
|
+
if (!seen.has(id)) throw invalid3(`\u5B89\u88C5\u95ED\u5305\u4E0D\u5305\u542B\u76EE\u6807\u6280\u80FD: ${id}`);
|
|
4733
5634
|
}
|
|
4734
5635
|
const unchanged = /* @__PURE__ */ new Set();
|
|
4735
5636
|
for (const candidate of candidates) {
|
|
@@ -4740,7 +5641,7 @@ async function installSkillDocuments(documents, requestedIds, baseDir, force, me
|
|
|
4740
5641
|
unchanged.add(candidate.id);
|
|
4741
5642
|
continue;
|
|
4742
5643
|
}
|
|
4743
|
-
throw
|
|
5644
|
+
throw invalid3(
|
|
4744
5645
|
`${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`
|
|
4745
5646
|
);
|
|
4746
5647
|
}
|
|
@@ -4775,7 +5676,7 @@ async function sameOnDisk(baseDir, candidate) {
|
|
|
4775
5676
|
return true;
|
|
4776
5677
|
}
|
|
4777
5678
|
async function readLocalReferences(skillDir) {
|
|
4778
|
-
const root =
|
|
5679
|
+
const root = path4.join(skillDir, "references");
|
|
4779
5680
|
const out = [];
|
|
4780
5681
|
const walk = async (dir, prefix) => {
|
|
4781
5682
|
let items;
|
|
@@ -4788,12 +5689,12 @@ async function readLocalReferences(skillDir) {
|
|
|
4788
5689
|
for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4789
5690
|
const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
4790
5691
|
if (item.isDirectory()) {
|
|
4791
|
-
await walk(
|
|
5692
|
+
await walk(path4.join(dir, item.name), relative2);
|
|
4792
5693
|
continue;
|
|
4793
5694
|
}
|
|
4794
5695
|
if (!item.isFile()) continue;
|
|
4795
5696
|
assertSafeReferencePath(relative2);
|
|
4796
|
-
out.push({ path: relative2, content: await fsp.readFile(
|
|
5697
|
+
out.push({ path: relative2, content: await fsp.readFile(path4.join(dir, item.name), "utf8") });
|
|
4797
5698
|
}
|
|
4798
5699
|
};
|
|
4799
5700
|
await walk(root, "");
|
|
@@ -4803,15 +5704,15 @@ async function writeSkillDocuments(documents, baseDir) {
|
|
|
4803
5704
|
const staged = [];
|
|
4804
5705
|
try {
|
|
4805
5706
|
for (const document of documents) {
|
|
4806
|
-
const target =
|
|
4807
|
-
await fsp.mkdir(
|
|
4808
|
-
const temp =
|
|
5707
|
+
const target = path4.join(baseDir, document.id, "SKILL.md");
|
|
5708
|
+
await fsp.mkdir(path4.dirname(target), { recursive: true });
|
|
5709
|
+
const temp = path4.join(path4.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
|
|
4809
5710
|
await fsp.writeFile(temp, document.markdown, "utf8");
|
|
4810
5711
|
staged.push({ temp, target });
|
|
4811
5712
|
for (const reference of document.references ?? []) {
|
|
4812
5713
|
assertSafeReferencePath(reference.path);
|
|
4813
|
-
const referenceTarget =
|
|
4814
|
-
await fsp.mkdir(
|
|
5714
|
+
const referenceTarget = path4.join(baseDir, document.id, "references", reference.path);
|
|
5715
|
+
await fsp.mkdir(path4.dirname(referenceTarget), { recursive: true });
|
|
4815
5716
|
const referenceTemp = `${referenceTarget}.${process.pid}.${Date.now()}.tmp`;
|
|
4816
5717
|
await fsp.writeFile(referenceTemp, reference.content, "utf8");
|
|
4817
5718
|
staged.push({ temp: referenceTemp, target: referenceTarget });
|
|
@@ -4840,24 +5741,24 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
4840
5741
|
const previous = state.skills[document.id];
|
|
4841
5742
|
state.skills[document.id] = {
|
|
4842
5743
|
source: document.source,
|
|
4843
|
-
sha256:
|
|
5744
|
+
sha256: sha2563(document.markdown),
|
|
4844
5745
|
installedAt: meta2.keepInstalledAt?.has(document.id) && previous ? previous.installedAt : state.updatedAt,
|
|
4845
5746
|
// 已经是直接安装的,不因为这次作为依赖被带进来就降级
|
|
4846
5747
|
direct: meta2.directIds.has(document.id) || Boolean(previous?.direct),
|
|
4847
5748
|
references: Object.fromEntries(
|
|
4848
|
-
(document.references ?? []).map((reference) => [reference.path,
|
|
5749
|
+
(document.references ?? []).map((reference) => [reference.path, sha2563(reference.content)])
|
|
4849
5750
|
)
|
|
4850
5751
|
};
|
|
4851
5752
|
}
|
|
4852
5753
|
await writeSkillsState(baseDir, state);
|
|
4853
5754
|
return state;
|
|
4854
5755
|
}
|
|
4855
|
-
function
|
|
4856
|
-
return new
|
|
5756
|
+
function invalid3(message) {
|
|
5757
|
+
return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
|
|
4857
5758
|
}
|
|
4858
5759
|
async function readInstalled(baseDir, id) {
|
|
4859
5760
|
try {
|
|
4860
|
-
return await fsp.readFile(
|
|
5761
|
+
return await fsp.readFile(path4.join(baseDir, id, "SKILL.md"), "utf8");
|
|
4861
5762
|
} catch (cause) {
|
|
4862
5763
|
if (cause?.code === "ENOENT") return void 0;
|
|
4863
5764
|
throw cause;
|
|
@@ -4865,7 +5766,7 @@ async function readInstalled(baseDir, id) {
|
|
|
4865
5766
|
}
|
|
4866
5767
|
async function readInstalledReference(baseDir, id, relative2) {
|
|
4867
5768
|
try {
|
|
4868
|
-
return await fsp.readFile(
|
|
5769
|
+
return await fsp.readFile(path4.join(baseDir, id, "references", relative2), "utf8");
|
|
4869
5770
|
} catch (cause) {
|
|
4870
5771
|
if (cause?.code === "ENOENT") return void 0;
|
|
4871
5772
|
throw cause;
|
|
@@ -4881,7 +5782,7 @@ async function planSkillUpdate(options) {
|
|
|
4881
5782
|
const closure = /* @__PURE__ */ new Map();
|
|
4882
5783
|
for (const id of wanted.sort()) {
|
|
4883
5784
|
try {
|
|
4884
|
-
for (const skill of
|
|
5785
|
+
for (const skill of resolveSkillInstallOrder2(manifest2, id)) {
|
|
4885
5786
|
if (!closure.has(skill.id)) closure.set(skill.id, skill);
|
|
4886
5787
|
}
|
|
4887
5788
|
} catch {
|
|
@@ -4908,12 +5809,12 @@ async function planSkillUpdate(options) {
|
|
|
4908
5809
|
const remote = await fetchDocument(skill);
|
|
4909
5810
|
const remoteReferences = remote.references ?? [];
|
|
4910
5811
|
let differsFromRemote = remote.markdown !== onDisk;
|
|
4911
|
-
let editedLocally = record !== void 0 &&
|
|
5812
|
+
let editedLocally = record !== void 0 && sha2563(onDisk) !== record.sha256;
|
|
4912
5813
|
for (const reference of remoteReferences) {
|
|
4913
5814
|
const installed = await readInstalledReference(baseDir, skill.id, reference.path);
|
|
4914
5815
|
if (installed !== reference.content) differsFromRemote = true;
|
|
4915
5816
|
const recordedSha = record?.references?.[reference.path];
|
|
4916
|
-
if (installed !== void 0 && recordedSha &&
|
|
5817
|
+
if (installed !== void 0 && recordedSha && sha2563(installed) !== recordedSha) editedLocally = true;
|
|
4917
5818
|
}
|
|
4918
5819
|
if (!differsFromRemote) {
|
|
4919
5820
|
entries.push({ id: skill.id, status: "unchanged" });
|
|
@@ -4949,14 +5850,15 @@ async function skillsUpdateCommand(args) {
|
|
|
4949
5850
|
const layout = await resolveInstallLayout(args);
|
|
4950
5851
|
const state = await readSkillsState(layout.baseDir);
|
|
4951
5852
|
if (!state || Object.keys(state.skills).length === 0) {
|
|
4952
|
-
throw new
|
|
4953
|
-
code:
|
|
4954
|
-
message: `${
|
|
5853
|
+
throw new CliError16({
|
|
5854
|
+
code: CliErrorCode15.INVALID_ARGUMENT,
|
|
5855
|
+
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`
|
|
4955
5856
|
});
|
|
4956
5857
|
}
|
|
4957
|
-
const endpoint =
|
|
4958
|
-
|
|
4959
|
-
|
|
5858
|
+
const endpoint = decideTrackedEndpoint({
|
|
5859
|
+
explicit: Boolean(args.endpoint || args.env),
|
|
5860
|
+
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5861
|
+
recorded: state.endpoint
|
|
4960
5862
|
});
|
|
4961
5863
|
const { http, base } = await chironClient({ ...args, endpoint });
|
|
4962
5864
|
const manifest2 = await fetchSkillCatalog(http, base);
|
|
@@ -5034,8 +5936,8 @@ import {
|
|
|
5034
5936
|
loadEnv as loadEnv6,
|
|
5035
5937
|
loadGlobalConfig as loadGlobalConfig7,
|
|
5036
5938
|
resolveActiveEnv as resolveActiveEnv4,
|
|
5037
|
-
CliError as
|
|
5038
|
-
CliErrorCode as
|
|
5939
|
+
CliError as CliError17,
|
|
5940
|
+
CliErrorCode as CliErrorCode16
|
|
5039
5941
|
} from "@hcmai/sdk";
|
|
5040
5942
|
init_exit();
|
|
5041
5943
|
async function resolveBootstrapKey(args, stdinTaken) {
|
|
@@ -5043,8 +5945,8 @@ async function resolveBootstrapKey(args, stdinTaken) {
|
|
|
5043
5945
|
if (fromEnv) return fromEnv;
|
|
5044
5946
|
if (args.bootstrapKeyStdin) {
|
|
5045
5947
|
if (stdinTaken) {
|
|
5046
|
-
throw new
|
|
5047
|
-
code:
|
|
5948
|
+
throw new CliError17({
|
|
5949
|
+
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
5048
5950
|
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"
|
|
5049
5951
|
});
|
|
5050
5952
|
}
|
|
@@ -5074,8 +5976,8 @@ async function resolveEndpoint2(args) {
|
|
|
5074
5976
|
const envName = resolveActiveEnv4({ envFlag: args.env }, process.env, g);
|
|
5075
5977
|
const env2 = await loadEnv6(envName).catch(() => null);
|
|
5076
5978
|
if (!env2?.endpoint) {
|
|
5077
|
-
throw new
|
|
5078
|
-
code:
|
|
5979
|
+
throw new CliError17({
|
|
5980
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
5079
5981
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`"
|
|
5080
5982
|
});
|
|
5081
5983
|
}
|
|
@@ -5084,8 +5986,8 @@ async function resolveEndpoint2(args) {
|
|
|
5084
5986
|
async function tenantBootstrapCommand(args) {
|
|
5085
5987
|
try {
|
|
5086
5988
|
if (!args.tenantId || !args.adminUsername) {
|
|
5087
|
-
throw new
|
|
5088
|
-
code:
|
|
5989
|
+
throw new CliError17({
|
|
5990
|
+
code: CliErrorCode16.MISSING_FLAG,
|
|
5089
5991
|
message: "--tenant-id \u4E0E --admin-username \u5FC5\u586B"
|
|
5090
5992
|
});
|
|
5091
5993
|
}
|
|
@@ -5107,7 +6009,7 @@ async function tenantBootstrapCommand(args) {
|
|
|
5107
6009
|
onRefresh: async () => {
|
|
5108
6010
|
}
|
|
5109
6011
|
});
|
|
5110
|
-
const
|
|
6012
|
+
const result2 = await bootstrapTenant(http, key, {
|
|
5111
6013
|
tenantId: args.tenantId,
|
|
5112
6014
|
adminUsername: args.adminUsername,
|
|
5113
6015
|
adminPassword: pwd,
|
|
@@ -5116,12 +6018,12 @@ async function tenantBootstrapCommand(args) {
|
|
|
5116
6018
|
const fmt = args.output ?? "json";
|
|
5117
6019
|
if (fmt !== "json") {
|
|
5118
6020
|
process.stdout.write(
|
|
5119
|
-
`\u2705 \u79DF\u6237 ${
|
|
6021
|
+
`\u2705 \u79DF\u6237 ${result2.tenantId} \u5DF2\u5F00\u901A\uFF08adminUserId ${result2.adminUserId ?? "-"}\uFF09
|
|
5120
6022
|
\u4E0B\u4E00\u6B65\uFF1Ahcm env add <name> --endpoint ${endpoint} && hcm login --env <name>
|
|
5121
6023
|
`
|
|
5122
6024
|
);
|
|
5123
6025
|
}
|
|
5124
|
-
process.stdout.write(formatObject14(
|
|
6026
|
+
process.stdout.write(formatObject14(result2, { format: fmt }) + "\n");
|
|
5125
6027
|
process.exit(0);
|
|
5126
6028
|
} catch (e) {
|
|
5127
6029
|
const err = e;
|
|
@@ -5145,8 +6047,8 @@ import {
|
|
|
5145
6047
|
getTenantMeta,
|
|
5146
6048
|
saveTenantMeta,
|
|
5147
6049
|
deleteTenantMeta,
|
|
5148
|
-
CliError as
|
|
5149
|
-
CliErrorCode as
|
|
6050
|
+
CliError as CliError18,
|
|
6051
|
+
CliErrorCode as CliErrorCode17
|
|
5150
6052
|
} from "@hcmai/sdk";
|
|
5151
6053
|
init_exit();
|
|
5152
6054
|
init_auth_guard();
|
|
@@ -5182,21 +6084,21 @@ async function metaListCommand(args) {
|
|
|
5182
6084
|
fail4(e, args);
|
|
5183
6085
|
}
|
|
5184
6086
|
}
|
|
5185
|
-
async function metaGetCommand(
|
|
6087
|
+
async function metaGetCommand(path7, args) {
|
|
5186
6088
|
try {
|
|
5187
6089
|
const c = await client2(args);
|
|
5188
|
-
const content = await getTenantMeta(c.raw(),
|
|
6090
|
+
const content = await getTenantMeta(c.raw(), path7);
|
|
5189
6091
|
process.stdout.write(content.endsWith("\n") ? content : content + "\n");
|
|
5190
6092
|
process.exit(0);
|
|
5191
6093
|
} catch (e) {
|
|
5192
6094
|
fail4(e, args);
|
|
5193
6095
|
}
|
|
5194
6096
|
}
|
|
5195
|
-
async function metaSetCommand(
|
|
6097
|
+
async function metaSetCommand(path7, args) {
|
|
5196
6098
|
try {
|
|
5197
6099
|
if (!args.file) {
|
|
5198
|
-
throw new
|
|
5199
|
-
code:
|
|
6100
|
+
throw new CliError18({
|
|
6101
|
+
code: CliErrorCode17.MISSING_FLAG,
|
|
5200
6102
|
message: "--file <path> \u5FC5\u586B\uFF08\u4ECE\u6587\u4EF6\u8BFB\u53D6 meta \u539F\u6587\uFF1B\u7528 - \u8868\u793A stdin\uFF09"
|
|
5201
6103
|
});
|
|
5202
6104
|
}
|
|
@@ -5209,7 +6111,7 @@ async function metaSetCommand(path4, args) {
|
|
|
5209
6111
|
}) : await fsp2.readFile(args.file, "utf8");
|
|
5210
6112
|
if (args.dryRun) {
|
|
5211
6113
|
process.stdout.write(
|
|
5212
|
-
`[dry-run] \u5C06\u5199\u5165 ${
|
|
6114
|
+
`[dry-run] \u5C06\u5199\u5165 ${path7}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
|
|
5213
6115
|
---
|
|
5214
6116
|
${content}
|
|
5215
6117
|
`
|
|
@@ -5217,35 +6119,35 @@ ${content}
|
|
|
5217
6119
|
process.exit(0);
|
|
5218
6120
|
}
|
|
5219
6121
|
const c = await client2(args);
|
|
5220
|
-
const
|
|
6122
|
+
const result2 = await saveTenantMeta(c.raw(), path7, content);
|
|
5221
6123
|
const fmt = args.output ?? "json";
|
|
5222
6124
|
if (fmt !== "json") {
|
|
5223
|
-
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${
|
|
6125
|
+
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path7}
|
|
5224
6126
|
\u8BB0\u5F97\u6E05\u7F13\u5B58\uFF1Ahcm cache clear-meta <Model>
|
|
5225
6127
|
`);
|
|
5226
6128
|
}
|
|
5227
|
-
process.stdout.write(formatObject15(
|
|
6129
|
+
process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
|
|
5228
6130
|
process.exit(0);
|
|
5229
6131
|
} catch (e) {
|
|
5230
6132
|
if (e?.code === "ENOENT") exitWithError(new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${args.file}`));
|
|
5231
6133
|
fail4(e, args);
|
|
5232
6134
|
}
|
|
5233
6135
|
}
|
|
5234
|
-
async function metaDeleteCommand(
|
|
6136
|
+
async function metaDeleteCommand(path7, args) {
|
|
5235
6137
|
try {
|
|
5236
6138
|
if (!args.yes) {
|
|
5237
|
-
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${
|
|
6139
|
+
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path7}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
|
|
5238
6140
|
if (!ok) {
|
|
5239
6141
|
process.stderr.write("\u5DF2\u53D6\u6D88\n");
|
|
5240
6142
|
process.exit(1);
|
|
5241
6143
|
}
|
|
5242
6144
|
}
|
|
5243
6145
|
const c = await client2(args);
|
|
5244
|
-
const
|
|
6146
|
+
const result2 = await deleteTenantMeta(c.raw(), path7);
|
|
5245
6147
|
const fmt = args.output ?? "json";
|
|
5246
|
-
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${
|
|
6148
|
+
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path7}
|
|
5247
6149
|
`);
|
|
5248
|
-
process.stdout.write(formatObject15(
|
|
6150
|
+
process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
|
|
5249
6151
|
process.exit(0);
|
|
5250
6152
|
} catch (e) {
|
|
5251
6153
|
fail4(e, args);
|
|
@@ -5256,8 +6158,8 @@ async function metaDeleteCommand(path4, args) {
|
|
|
5256
6158
|
init_exit();
|
|
5257
6159
|
init_auth_guard();
|
|
5258
6160
|
import * as crypto from "crypto";
|
|
5259
|
-
import * as
|
|
5260
|
-
import * as
|
|
6161
|
+
import * as fs6 from "fs/promises";
|
|
6162
|
+
import * as path5 from "path";
|
|
5261
6163
|
import {
|
|
5262
6164
|
buildMiniPreviewTargets,
|
|
5263
6165
|
buildMiniVerifyAgentReport,
|
|
@@ -5326,7 +6228,7 @@ async function workspaceListCommand(args) {
|
|
|
5326
6228
|
try {
|
|
5327
6229
|
const client3 = HcmClient16.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
5328
6230
|
const limit = parsePositiveInt3(args.limit, 50, "--limit");
|
|
5329
|
-
const
|
|
6231
|
+
const result2 = await client3.query("TenantExtensionWorkspace", {
|
|
5330
6232
|
keyword: args.keyword,
|
|
5331
6233
|
fields: [
|
|
5332
6234
|
"workspaceKey",
|
|
@@ -5341,7 +6243,7 @@ async function workspaceListCommand(args) {
|
|
|
5341
6243
|
sort: [{ field: "devModeEnabled", desc: true }, { field: "updateTime", desc: true }],
|
|
5342
6244
|
limit
|
|
5343
6245
|
});
|
|
5344
|
-
writeWorkspaceListResult(
|
|
6246
|
+
writeWorkspaceListResult(result2.rows, args.output ?? "table", result2.total);
|
|
5345
6247
|
process.exit(0);
|
|
5346
6248
|
} catch (e) {
|
|
5347
6249
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -5355,12 +6257,12 @@ async function workspaceActiveCommand(args) {
|
|
|
5355
6257
|
fallbackEnv: localContext?.profile
|
|
5356
6258
|
});
|
|
5357
6259
|
const client3 = HcmClient16.fromAuthContext(ctx);
|
|
5358
|
-
const
|
|
6260
|
+
const result2 = await client3.query("TenantExtensionWorkspace", {
|
|
5359
6261
|
filter: { devModeEnabled: true },
|
|
5360
6262
|
fields: ["workspaceKey", "devModeEnabled"],
|
|
5361
6263
|
limit: 1
|
|
5362
6264
|
});
|
|
5363
|
-
const activeWorkspaceKey = optionalText(
|
|
6265
|
+
const activeWorkspaceKey = optionalText(result2.rows[0]?.workspaceKey) ?? null;
|
|
5364
6266
|
const view = {
|
|
5365
6267
|
activeWorkspaceKey,
|
|
5366
6268
|
localWorkspaceKey: localContext?.workspaceKey ?? null,
|
|
@@ -5392,7 +6294,7 @@ async function workspaceBindCommand(workspaceKey, args) {
|
|
|
5392
6294
|
tenantId: args.tenant ?? client3?.tenantId,
|
|
5393
6295
|
endpoint: args.endpoint ?? client3?.endpoint
|
|
5394
6296
|
});
|
|
5395
|
-
await
|
|
6297
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5396
6298
|
await writeWorkspaceContext(rootDir, context);
|
|
5397
6299
|
writeWorkspaceBindResult(context, rootDir, args.output ?? "table");
|
|
5398
6300
|
process.exit(0);
|
|
@@ -5427,7 +6329,7 @@ async function workspaceCreateCommand(workspaceKey, args) {
|
|
|
5427
6329
|
tenantId: client3.tenantId,
|
|
5428
6330
|
endpoint: client3.endpoint
|
|
5429
6331
|
});
|
|
5430
|
-
await
|
|
6332
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5431
6333
|
await writeWorkspaceContext(rootDir, context);
|
|
5432
6334
|
writeWorkspaceCreateResult(
|
|
5433
6335
|
{
|
|
@@ -5464,23 +6366,23 @@ async function workspacePullCommand(args) {
|
|
|
5464
6366
|
tenantId: client3.tenantId ?? existing?.tenantId,
|
|
5465
6367
|
endpoint: client3.endpoint ?? existing?.endpoint
|
|
5466
6368
|
});
|
|
5467
|
-
await
|
|
6369
|
+
await fs6.mkdir(rootDir, { recursive: true });
|
|
5468
6370
|
await writeWorkspaceContext(rootDir, context);
|
|
5469
6371
|
const files = await listWorkspaceFiles(client3.raw(), workspaceKey, { pageSize: 5e3 });
|
|
5470
6372
|
const miniApps = detectRemoteMiniApps(files.data);
|
|
5471
6373
|
const pulledMiniApps = [];
|
|
5472
6374
|
for (const appCode of miniApps) {
|
|
5473
|
-
const
|
|
6375
|
+
const result2 = await pullMiniAppProject({
|
|
5474
6376
|
http: client3.raw(),
|
|
5475
6377
|
appCode,
|
|
5476
6378
|
workspaceKey,
|
|
5477
|
-
targetDir:
|
|
6379
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
5478
6380
|
tenantId: client3.tenantId,
|
|
5479
6381
|
endpoint: client3.endpoint,
|
|
5480
6382
|
profile: args.profile ?? client3.profile,
|
|
5481
6383
|
force: !!args.force
|
|
5482
6384
|
});
|
|
5483
|
-
pulledMiniApps.push(
|
|
6385
|
+
pulledMiniApps.push(result2);
|
|
5484
6386
|
}
|
|
5485
6387
|
const rawFiles = files.data.filter(
|
|
5486
6388
|
(file) => !isDirectoryMarker(file.relativePath) && !isMiniAppPath(file.relativePath)
|
|
@@ -5489,7 +6391,7 @@ async function workspacePullCommand(args) {
|
|
|
5489
6391
|
const rawRemoteByLocalPath = /* @__PURE__ */ new Map();
|
|
5490
6392
|
for (const file of rawFiles) {
|
|
5491
6393
|
const content = await loadWorkspaceFileContent(client3.raw(), file.id);
|
|
5492
|
-
const target =
|
|
6394
|
+
const target = path5.join(rootDir, content.relativePath);
|
|
5493
6395
|
await writeTextFile(target, content.content, !!args.force);
|
|
5494
6396
|
pulledRawFiles.push(content.relativePath);
|
|
5495
6397
|
rawRemoteByLocalPath.set(content.relativePath, file);
|
|
@@ -5536,10 +6438,10 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
5536
6438
|
fields: args.fields,
|
|
5537
6439
|
defaults: args.defaults
|
|
5538
6440
|
});
|
|
5539
|
-
const
|
|
6441
|
+
const result2 = await initMiniAppProject({
|
|
5540
6442
|
appCode,
|
|
5541
6443
|
workspaceKey: context.workspaceKey,
|
|
5542
|
-
targetDir:
|
|
6444
|
+
targetDir: path5.join(rootDir, MINI_APPS_DIR, appCode),
|
|
5543
6445
|
name: args.name,
|
|
5544
6446
|
defaultSurface: parseSurface(args.surface),
|
|
5545
6447
|
templateKind,
|
|
@@ -5553,7 +6455,7 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
5553
6455
|
profile: args.env ?? args.profile ?? client3?.profile ?? context.profile,
|
|
5554
6456
|
force: !!args.force
|
|
5555
6457
|
});
|
|
5556
|
-
writeWorkspaceAddMiniResult(
|
|
6458
|
+
writeWorkspaceAddMiniResult(result2, rootDir, args.output ?? "table");
|
|
5557
6459
|
process.exit(0);
|
|
5558
6460
|
} catch (e) {
|
|
5559
6461
|
exitWorkspaceError(e, args.env ?? args.profile);
|
|
@@ -5633,12 +6535,12 @@ async function workspacePushCommand(args) {
|
|
|
5633
6535
|
});
|
|
5634
6536
|
const results = [];
|
|
5635
6537
|
for (const app of miniApps) {
|
|
5636
|
-
const
|
|
6538
|
+
const result2 = await pushMiniAppProject({
|
|
5637
6539
|
http: client3.raw(),
|
|
5638
6540
|
rootDir: app.dir,
|
|
5639
6541
|
dryRun: !!args.dryRun
|
|
5640
6542
|
});
|
|
5641
|
-
results.push({ app, result });
|
|
6543
|
+
results.push({ app, result: result2 });
|
|
5642
6544
|
}
|
|
5643
6545
|
writeWorkspacePushResult(rawResult, results, args.output ?? "table");
|
|
5644
6546
|
process.exit(
|
|
@@ -5780,7 +6682,7 @@ function removedMiniCommand() {
|
|
|
5780
6682
|
);
|
|
5781
6683
|
}
|
|
5782
6684
|
function resolveRootDir(dir) {
|
|
5783
|
-
return
|
|
6685
|
+
return path5.resolve(dir ?? ".");
|
|
5784
6686
|
}
|
|
5785
6687
|
function buildWorkspaceContext(args) {
|
|
5786
6688
|
return {
|
|
@@ -5796,7 +6698,7 @@ function buildWorkspaceContext(args) {
|
|
|
5796
6698
|
async function readWorkspaceContext(rootDir) {
|
|
5797
6699
|
try {
|
|
5798
6700
|
const parsed = JSON.parse(
|
|
5799
|
-
await
|
|
6701
|
+
await fs6.readFile(path5.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
|
|
5800
6702
|
);
|
|
5801
6703
|
if (parsed.kind !== WORKSPACE_CONTEXT_KIND || parsed.version !== WORKSPACE_CONTEXT_VERSION) {
|
|
5802
6704
|
throw new Error(`${WORKSPACE_CONTEXT_FILE} is not an HCM workspace context`);
|
|
@@ -5825,25 +6727,25 @@ async function requireWorkspaceContext(rootDir) {
|
|
|
5825
6727
|
return context;
|
|
5826
6728
|
}
|
|
5827
6729
|
async function writeWorkspaceContext(rootDir, context) {
|
|
5828
|
-
await
|
|
5829
|
-
|
|
6730
|
+
await fs6.writeFile(
|
|
6731
|
+
path5.join(rootDir, WORKSPACE_CONTEXT_FILE),
|
|
5830
6732
|
JSON.stringify(context, null, 2) + "\n"
|
|
5831
6733
|
);
|
|
5832
6734
|
}
|
|
5833
6735
|
async function writeTextFile(file, content, force) {
|
|
5834
6736
|
try {
|
|
5835
|
-
await
|
|
6737
|
+
await fs6.access(file);
|
|
5836
6738
|
if (!force) throw new Error(`${file} already exists; use --force to overwrite`);
|
|
5837
6739
|
} catch (e) {
|
|
5838
6740
|
if (e.code !== "ENOENT") throw e;
|
|
5839
6741
|
}
|
|
5840
|
-
await
|
|
5841
|
-
await
|
|
6742
|
+
await fs6.mkdir(path5.dirname(file), { recursive: true });
|
|
6743
|
+
await fs6.writeFile(file, content);
|
|
5842
6744
|
}
|
|
5843
6745
|
async function readWorkspaceRawManifest(rootDir, context) {
|
|
5844
6746
|
try {
|
|
5845
6747
|
const parsed = JSON.parse(
|
|
5846
|
-
await
|
|
6748
|
+
await fs6.readFile(path5.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
|
|
5847
6749
|
);
|
|
5848
6750
|
if (parsed.version !== WORKSPACE_CONTEXT_VERSION || parsed.workspaceKey !== context.workspaceKey) {
|
|
5849
6751
|
throw new Error(`${WORKSPACE_PULL_MANIFEST_FILE} does not match workspace ${context.workspaceKey}`);
|
|
@@ -5861,8 +6763,8 @@ async function readWorkspaceRawManifest(rootDir, context) {
|
|
|
5861
6763
|
}
|
|
5862
6764
|
}
|
|
5863
6765
|
async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath) {
|
|
5864
|
-
const stateDir =
|
|
5865
|
-
await
|
|
6766
|
+
const stateDir = path5.join(rootDir, WORKSPACE_STATE_DIR);
|
|
6767
|
+
await fs6.mkdir(stateDir, { recursive: true });
|
|
5866
6768
|
const snapshots = files.map((file) => ({
|
|
5867
6769
|
localPath: file.localPath,
|
|
5868
6770
|
remotePath: file.localPath,
|
|
@@ -5871,8 +6773,8 @@ async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath
|
|
|
5871
6773
|
size: file.size,
|
|
5872
6774
|
lastModified: remoteByLocalPath.get(file.localPath)?.lastModified
|
|
5873
6775
|
}));
|
|
5874
|
-
await
|
|
5875
|
-
|
|
6776
|
+
await fs6.writeFile(
|
|
6777
|
+
path5.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
|
|
5876
6778
|
JSON.stringify(
|
|
5877
6779
|
{
|
|
5878
6780
|
version: WORKSPACE_CONTEXT_VERSION,
|
|
@@ -5894,10 +6796,10 @@ async function readWorkspaceRawLocalFiles(rootDir) {
|
|
|
5894
6796
|
return files;
|
|
5895
6797
|
}
|
|
5896
6798
|
async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
5897
|
-
const absoluteDir =
|
|
6799
|
+
const absoluteDir = path5.join(rootDir, relativeDir);
|
|
5898
6800
|
let entries;
|
|
5899
6801
|
try {
|
|
5900
|
-
entries = await
|
|
6802
|
+
entries = await fs6.readdir(absoluteDir, { withFileTypes: true });
|
|
5901
6803
|
} catch (e) {
|
|
5902
6804
|
if (e.code === "ENOENT") return;
|
|
5903
6805
|
throw e;
|
|
@@ -5905,19 +6807,19 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
|
5905
6807
|
for (const entry of entries) {
|
|
5906
6808
|
if (entry.isDirectory() && RAW_WORKSPACE_EXCLUDED_DIRS.has(entry.name)) continue;
|
|
5907
6809
|
if (entry.isFile() && RAW_WORKSPACE_EXCLUDED_FILES.has(entry.name)) continue;
|
|
5908
|
-
const localPath = normalizeRelativePath(
|
|
5909
|
-
const absolutePath =
|
|
6810
|
+
const localPath = normalizeRelativePath(path5.posix.join(toPosix(relativeDir), entry.name));
|
|
6811
|
+
const absolutePath = path5.join(rootDir, localPath);
|
|
5910
6812
|
if (entry.isDirectory()) {
|
|
5911
6813
|
await walkWorkspaceRawFiles(rootDir, localPath, files);
|
|
5912
6814
|
continue;
|
|
5913
6815
|
}
|
|
5914
6816
|
if (!entry.isFile()) continue;
|
|
5915
|
-
const content = await
|
|
6817
|
+
const content = await fs6.readFile(absolutePath, "utf-8");
|
|
5916
6818
|
files.push({
|
|
5917
6819
|
localPath,
|
|
5918
6820
|
absolutePath,
|
|
5919
6821
|
content,
|
|
5920
|
-
sha256:
|
|
6822
|
+
sha256: sha2564(content),
|
|
5921
6823
|
size: Buffer.byteLength(content, "utf-8")
|
|
5922
6824
|
});
|
|
5923
6825
|
}
|
|
@@ -5931,13 +6833,13 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
5931
6833
|
const localFiles = await readWorkspaceRawLocalFiles(args.rootDir);
|
|
5932
6834
|
const remoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
5933
6835
|
const plan = buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles);
|
|
5934
|
-
const
|
|
6836
|
+
const result2 = {
|
|
5935
6837
|
workspaceKey: args.context.workspaceKey,
|
|
5936
6838
|
dryRun: args.dryRun,
|
|
5937
6839
|
operations: plan.operations,
|
|
5938
6840
|
conflicts: plan.conflicts
|
|
5939
6841
|
};
|
|
5940
|
-
if (args.dryRun || plan.conflicts.length) return
|
|
6842
|
+
if (args.dryRun || plan.conflicts.length) return result2;
|
|
5941
6843
|
const localByPath = new Map(localFiles.map((file) => [file.localPath, file]));
|
|
5942
6844
|
for (const operation of plan.operations) {
|
|
5943
6845
|
if (operation.operation === "skip") continue;
|
|
@@ -5963,17 +6865,17 @@ async function pushWorkspaceRawFiles(args) {
|
|
|
5963
6865
|
const latestRemoteFiles = await loadWorkspaceRawRemoteFiles(args.http, args.context.workspaceKey);
|
|
5964
6866
|
const latestRemoteByLocalPath = new Map(latestRemoteFiles.map((file) => [file.item.relativePath, file.item]));
|
|
5965
6867
|
await writeWorkspaceRawState(args.rootDir, args.context, localFiles, latestRemoteByLocalPath);
|
|
5966
|
-
return
|
|
6868
|
+
return result2;
|
|
5967
6869
|
}
|
|
5968
6870
|
async function loadWorkspaceRawRemoteFiles(http, workspaceKey) {
|
|
5969
6871
|
const list = await listWorkspaceFiles(http, workspaceKey, { pageSize: 5e3 });
|
|
5970
|
-
const
|
|
6872
|
+
const result2 = [];
|
|
5971
6873
|
for (const item of list.data) {
|
|
5972
6874
|
if (isDirectoryMarker(item.relativePath) || isMiniAppPath(item.relativePath)) continue;
|
|
5973
6875
|
const content = await loadWorkspaceFileContent(http, item.id);
|
|
5974
|
-
|
|
6876
|
+
result2.push({ item, content: content.content });
|
|
5975
6877
|
}
|
|
5976
|
-
return
|
|
6878
|
+
return result2;
|
|
5977
6879
|
}
|
|
5978
6880
|
function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
5979
6881
|
const status = computeWorkspaceRawStatus(manifest2, localFiles);
|
|
@@ -5984,7 +6886,7 @@ function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
|
5984
6886
|
for (const entry of status) {
|
|
5985
6887
|
const remote = remoteByLocal.get(entry.localPath);
|
|
5986
6888
|
const base = baseByLocal.get(entry.localPath);
|
|
5987
|
-
const remoteSha = remote ?
|
|
6889
|
+
const remoteSha = remote ? sha2564(remote.content) : void 0;
|
|
5988
6890
|
if (entry.status === "added") {
|
|
5989
6891
|
if (remote) {
|
|
5990
6892
|
conflicts.push(workspaceRawConflict(entry, "remote file already exists but was not in local snapshot"));
|
|
@@ -6086,15 +6988,15 @@ function workspaceRawConflict(entry, reason) {
|
|
|
6086
6988
|
};
|
|
6087
6989
|
}
|
|
6088
6990
|
async function discoverMiniApps(rootDir) {
|
|
6089
|
-
const appsRoot =
|
|
6991
|
+
const appsRoot = path5.join(rootDir, MINI_APPS_DIR);
|
|
6090
6992
|
let entries;
|
|
6091
6993
|
try {
|
|
6092
|
-
entries = await
|
|
6994
|
+
entries = await fs6.readdir(appsRoot, { withFileTypes: true });
|
|
6093
6995
|
} catch (e) {
|
|
6094
6996
|
if (e.code === "ENOENT") return [];
|
|
6095
6997
|
throw e;
|
|
6096
6998
|
}
|
|
6097
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir:
|
|
6999
|
+
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));
|
|
6098
7000
|
}
|
|
6099
7001
|
function detectRemoteMiniApps(files) {
|
|
6100
7002
|
const appCodes = /* @__PURE__ */ new Set();
|
|
@@ -6183,17 +7085,17 @@ function parseModelKey(value) {
|
|
|
6183
7085
|
}
|
|
6184
7086
|
return model;
|
|
6185
7087
|
}
|
|
6186
|
-
function parseJsonObject(value,
|
|
7088
|
+
function parseJsonObject(value, flagName2) {
|
|
6187
7089
|
if (!value) return void 0;
|
|
6188
7090
|
if (isAuto(value)) return void 0;
|
|
6189
7091
|
let parsed;
|
|
6190
7092
|
try {
|
|
6191
7093
|
parsed = JSON.parse(value);
|
|
6192
7094
|
} catch {
|
|
6193
|
-
throw new Error(`${
|
|
7095
|
+
throw new Error(`${flagName2} must be valid JSON object`);
|
|
6194
7096
|
}
|
|
6195
7097
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
6196
|
-
throw new Error(`${
|
|
7098
|
+
throw new Error(`${flagName2} must be valid JSON object`);
|
|
6197
7099
|
}
|
|
6198
7100
|
return parsed;
|
|
6199
7101
|
}
|
|
@@ -6247,9 +7149,9 @@ function normalizeRelativePath(value) {
|
|
|
6247
7149
|
return normalized;
|
|
6248
7150
|
}
|
|
6249
7151
|
function toPosix(value) {
|
|
6250
|
-
return value.split(
|
|
7152
|
+
return value.split(path5.sep).join("/");
|
|
6251
7153
|
}
|
|
6252
|
-
function
|
|
7154
|
+
function sha2564(content) {
|
|
6253
7155
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
6254
7156
|
}
|
|
6255
7157
|
function stringRequired(value, field) {
|
|
@@ -6316,52 +7218,52 @@ function writeWorkspaceBindResult(context, rootDir, format) {
|
|
|
6316
7218
|
].join("\n") + "\n"
|
|
6317
7219
|
);
|
|
6318
7220
|
}
|
|
6319
|
-
function writeWorkspaceCreateResult(
|
|
7221
|
+
function writeWorkspaceCreateResult(result2, format) {
|
|
6320
7222
|
if (format !== "table") {
|
|
6321
|
-
process.stdout.write(formatObject16(
|
|
7223
|
+
process.stdout.write(formatObject16(result2, { format }) + "\n");
|
|
6322
7224
|
return;
|
|
6323
7225
|
}
|
|
6324
7226
|
process.stdout.write(
|
|
6325
7227
|
[
|
|
6326
|
-
`workspace created: ${
|
|
6327
|
-
`id: ${
|
|
6328
|
-
`directory: ${
|
|
6329
|
-
`profile: ${
|
|
6330
|
-
`tenant: ${
|
|
6331
|
-
`activated: ${
|
|
7228
|
+
`workspace created: ${result2.workspaceKey}`,
|
|
7229
|
+
`id: ${result2.id}`,
|
|
7230
|
+
`directory: ${result2.rootDir}`,
|
|
7231
|
+
`profile: ${result2.profile}`,
|
|
7232
|
+
`tenant: ${result2.tenantId}`,
|
|
7233
|
+
`activated: ${result2.activated ? "yes" : "no"}`
|
|
6332
7234
|
].join("\n") + "\n"
|
|
6333
7235
|
);
|
|
6334
7236
|
}
|
|
6335
|
-
function writeWorkspacePullResult(
|
|
7237
|
+
function writeWorkspacePullResult(result2, format) {
|
|
6336
7238
|
if (format !== "table") {
|
|
6337
|
-
process.stdout.write(formatObject16(
|
|
7239
|
+
process.stdout.write(formatObject16(result2, { format }) + "\n");
|
|
6338
7240
|
return;
|
|
6339
7241
|
}
|
|
6340
7242
|
const lines = [
|
|
6341
|
-
`workspace pulled: ${
|
|
6342
|
-
`directory: ${
|
|
6343
|
-
`remote files: ${
|
|
6344
|
-
`mini apps: ${
|
|
6345
|
-
`raw files: ${
|
|
7243
|
+
`workspace pulled: ${result2.workspaceKey}`,
|
|
7244
|
+
`directory: ${result2.rootDir}`,
|
|
7245
|
+
`remote files: ${result2.totalRemoteFiles}`,
|
|
7246
|
+
`mini apps: ${result2.miniApps.length}`,
|
|
7247
|
+
`raw files: ${result2.rawFiles.length}`
|
|
6346
7248
|
];
|
|
6347
|
-
if (
|
|
7249
|
+
if (result2.miniApps.length) {
|
|
6348
7250
|
lines.push("mini artifacts:");
|
|
6349
|
-
for (const app of
|
|
7251
|
+
for (const app of result2.miniApps) lines.push(` - ${app.appCode}: ${app.files} files`);
|
|
6350
7252
|
}
|
|
6351
7253
|
process.stdout.write(lines.join("\n") + "\n");
|
|
6352
7254
|
}
|
|
6353
|
-
function writeWorkspaceAddMiniResult(
|
|
7255
|
+
function writeWorkspaceAddMiniResult(result2, rootDir, format) {
|
|
6354
7256
|
if (format !== "table") {
|
|
6355
|
-
process.stdout.write(formatObject16({ ...
|
|
7257
|
+
process.stdout.write(formatObject16({ ...result2, workspaceRoot: rootDir }, { format }) + "\n");
|
|
6356
7258
|
return;
|
|
6357
7259
|
}
|
|
6358
7260
|
process.stdout.write(
|
|
6359
7261
|
[
|
|
6360
|
-
`workspace artifact added: mini:${
|
|
6361
|
-
`workspace: ${
|
|
7262
|
+
`workspace artifact added: mini:${result2.appCode}`,
|
|
7263
|
+
`workspace: ${result2.workspaceKey}`,
|
|
6362
7264
|
`workspace root: ${rootDir}`,
|
|
6363
|
-
`directory: ${
|
|
6364
|
-
`files: ${
|
|
7265
|
+
`directory: ${result2.targetDir}`,
|
|
7266
|
+
`files: ${result2.files.length}`
|
|
6365
7267
|
].join("\n") + "\n"
|
|
6366
7268
|
);
|
|
6367
7269
|
}
|
|
@@ -6546,8 +7448,8 @@ import {
|
|
|
6546
7448
|
deleteIdentity as deleteIdentity2,
|
|
6547
7449
|
loadGlobalConfig as loadGlobalConfig8,
|
|
6548
7450
|
TokenStore as TokenStore3,
|
|
6549
|
-
CliError as
|
|
6550
|
-
CliErrorCode as
|
|
7451
|
+
CliError as CliError19,
|
|
7452
|
+
CliErrorCode as CliErrorCode18
|
|
6551
7453
|
} from "@hcmai/sdk";
|
|
6552
7454
|
async function identityList(args) {
|
|
6553
7455
|
try {
|
|
@@ -6575,8 +7477,8 @@ async function identityUse(name) {
|
|
|
6575
7477
|
try {
|
|
6576
7478
|
const meta2 = await loadIdentity4(name);
|
|
6577
7479
|
const env2 = await loadEnv7(meta2.env).catch(() => {
|
|
6578
|
-
throw new
|
|
6579
|
-
code:
|
|
7480
|
+
throw new CliError19({
|
|
7481
|
+
code: CliErrorCode18.IDENTITY_NOT_IN_ENV,
|
|
6580
7482
|
message: `identity '${name}' references env '${meta2.env}' which no longer exists. Re-create the env, or remove this orphan: hcm identity remove ${name}`
|
|
6581
7483
|
});
|
|
6582
7484
|
});
|
|
@@ -6610,6 +7512,768 @@ async function identityRemove(name) {
|
|
|
6610
7512
|
}
|
|
6611
7513
|
}
|
|
6612
7514
|
|
|
7515
|
+
// src/commands/delivery.ts
|
|
7516
|
+
import { createHash as createHash5 } from "crypto";
|
|
7517
|
+
import { formatObject as formatObject17 } from "@hcmai/sdk";
|
|
7518
|
+
var APPROVAL_BYPASS_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--force", "--auto-approve"]);
|
|
7519
|
+
var CREDENTIAL_FLAGS = /* @__PURE__ */ new Set([
|
|
7520
|
+
"--password",
|
|
7521
|
+
"--password-stdin",
|
|
7522
|
+
"--client-secret",
|
|
7523
|
+
"--pat",
|
|
7524
|
+
"--pat-stdin",
|
|
7525
|
+
"--new-password",
|
|
7526
|
+
"--new-password-stdin"
|
|
7527
|
+
]);
|
|
7528
|
+
var PAYLOAD_FLAGS = ["--data", "--params"];
|
|
7529
|
+
var GUARDED_EXECUTOR_REASON_CODES = /* @__PURE__ */ new Set([
|
|
7530
|
+
"record-create",
|
|
7531
|
+
"record-update",
|
|
7532
|
+
"cache-mutation",
|
|
7533
|
+
"setting-write"
|
|
7534
|
+
]);
|
|
7535
|
+
function assessDeliveryCommand(inputArgv) {
|
|
7536
|
+
const argv = inputArgv[0] === "--" ? inputArgv.slice(1) : [...inputArgv];
|
|
7537
|
+
const commandSha256 = sha2565(JSON.stringify(argv));
|
|
7538
|
+
const name = argv[0] ?? "";
|
|
7539
|
+
const subcommand = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
|
|
7540
|
+
const envAlias = flagValue(argv, "--env");
|
|
7541
|
+
const identityAlias = flagValue(argv, "--as");
|
|
7542
|
+
const sensitiveFlags = [...new Set(PAYLOAD_FLAGS.filter((flag) => hasFlag(argv, flag)))];
|
|
7543
|
+
const target = summarizeTarget(argv);
|
|
7544
|
+
let classification = classify(argv);
|
|
7545
|
+
const invalidPayload = payloadError(argv);
|
|
7546
|
+
if (invalidPayload) {
|
|
7547
|
+
classification = forbidden("invalid-input", "JSON payload \u65E0\u6548\uFF0C\u4E0D\u80FD\u8FDB\u5165\u4EFB\u4F55\u6267\u884C\u8DEF\u5F84\u3002");
|
|
7548
|
+
}
|
|
7549
|
+
const requirements = [];
|
|
7550
|
+
const guardedExecutionAvailable = classification.risk === "R2" && GUARDED_EXECUTOR_REASON_CODES.has(classification.reasonCode);
|
|
7551
|
+
if (classification.risk === "R2") {
|
|
7552
|
+
if (!envAlias) requirements.push("explicit-env");
|
|
7553
|
+
if (!identityAlias) requirements.push("explicit-identity");
|
|
7554
|
+
if (guardedExecutionAvailable) {
|
|
7555
|
+
requirements.push("change-plan", "rollback-plan", "human-approval", "single-use-plan");
|
|
7556
|
+
} else {
|
|
7557
|
+
requirements.push("guarded-executor");
|
|
7558
|
+
}
|
|
7559
|
+
} else if (classification.risk === "R3") {
|
|
7560
|
+
requirements.push("governance-escalation");
|
|
7561
|
+
}
|
|
7562
|
+
const allowed = classification.risk === "R0" || classification.risk === "R1";
|
|
7563
|
+
return {
|
|
7564
|
+
schemaVersion: 1,
|
|
7565
|
+
contractVersion: "2026-08-28",
|
|
7566
|
+
assessmentId: sha2565(`2026-08-28
|
|
7567
|
+
${commandSha256}`).slice(0, 24),
|
|
7568
|
+
commandSha256,
|
|
7569
|
+
command: { name: name || "(empty)", ...subcommand ? { subcommand } : {} },
|
|
7570
|
+
...classification,
|
|
7571
|
+
guardedExecutionAvailable,
|
|
7572
|
+
environment: {
|
|
7573
|
+
...envAlias ? { envAlias } : {},
|
|
7574
|
+
...identityAlias ? { identityAlias } : {}
|
|
7575
|
+
},
|
|
7576
|
+
target,
|
|
7577
|
+
requirements,
|
|
7578
|
+
evidenceRequirements: evidenceFor(classification.risk),
|
|
7579
|
+
sensitiveFlags,
|
|
7580
|
+
modePolicy: {
|
|
7581
|
+
planOnly: allowed ? "allow" : "deny",
|
|
7582
|
+
controlled: allowed || guardedExecutionAvailable ? "allow" : "deny"
|
|
7583
|
+
}
|
|
7584
|
+
};
|
|
7585
|
+
}
|
|
7586
|
+
function deliveryAssessCommand(argv, args) {
|
|
7587
|
+
const assessment = assessDeliveryCommand(argv);
|
|
7588
|
+
process.stdout.write(formatObject17(assessment, { format: args.output ?? "json" }) + "\n");
|
|
7589
|
+
}
|
|
7590
|
+
function classify(argv) {
|
|
7591
|
+
const name = argv[0] ?? "";
|
|
7592
|
+
const subcommand = argv[1] ?? "";
|
|
7593
|
+
if (argv.some((arg) => APPROVAL_BYPASS_FLAGS.has(flagName(arg)))) {
|
|
7594
|
+
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");
|
|
7595
|
+
}
|
|
7596
|
+
if (argv.some((arg) => CREDENTIAL_FLAGS.has(flagName(arg)))) {
|
|
7597
|
+
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");
|
|
7598
|
+
}
|
|
7599
|
+
if (name === "version") return direct("R0", "product-discovery", "product-discovery");
|
|
7600
|
+
if (name === "delivery" && subcommand === "assess") {
|
|
7601
|
+
return direct("R0", "local-discovery", "risk-assessment");
|
|
7602
|
+
}
|
|
7603
|
+
if (name === "skills") {
|
|
7604
|
+
if (subcommand === "list" || subcommand === "show") {
|
|
7605
|
+
return direct("R0", "product-discovery", "skill-discovery");
|
|
7606
|
+
}
|
|
7607
|
+
if (subcommand === "sync" || subcommand === "install") {
|
|
7608
|
+
return direct("R1", "managed-local-write", "skill-managed-write");
|
|
7609
|
+
}
|
|
7610
|
+
return forbidden("unknown-command", "\u672A\u77E5 skills \u5B50\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
|
|
7611
|
+
}
|
|
7612
|
+
if (name === "env") {
|
|
7613
|
+
if (subcommand === "list" || subcommand === "current" || subcommand === "show") {
|
|
7614
|
+
return direct("R0", "local-discovery", "environment-read");
|
|
7615
|
+
}
|
|
7616
|
+
return forbidden("identity-governance", "\u73AF\u5883\u65B0\u589E\u3001\u5207\u6362\u3001\u7F16\u8F91\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7617
|
+
}
|
|
7618
|
+
if (name === "identity") {
|
|
7619
|
+
if (subcommand === "list") return direct("R0", "local-discovery", "identity-read");
|
|
7620
|
+
return forbidden("identity-governance", "\u8EAB\u4EFD\u5207\u6362\u6216\u5220\u9664\u5C5E\u4E8E\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7621
|
+
}
|
|
7622
|
+
if (name === "query" || name === "describe" || name === "trace") {
|
|
7623
|
+
return direct("R1", "remote-read", "business-read");
|
|
7624
|
+
}
|
|
7625
|
+
if (name === "cache") {
|
|
7626
|
+
if (subcommand === "stats") return direct("R1", "remote-read", "cache-read");
|
|
7627
|
+
if (subcommand === "clear" || subcommand === "clear-meta") {
|
|
7628
|
+
return guarded("operational-write", "cache-mutation");
|
|
7629
|
+
}
|
|
7630
|
+
}
|
|
7631
|
+
if (name === "meta") {
|
|
7632
|
+
if (subcommand === "list" || subcommand === "get") {
|
|
7633
|
+
return direct("R1", "remote-read", "metadata-read");
|
|
7634
|
+
}
|
|
7635
|
+
if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
|
|
7636
|
+
return direct("R1", "local-validation", "metadata-dry-run");
|
|
7637
|
+
}
|
|
7638
|
+
if (subcommand === "set") return guarded("tenant-config-write", "metadata-write");
|
|
7639
|
+
if (subcommand === "delete") return forbidden("destructive", "\u5143\u6570\u636E\u5220\u9664\u4E0D\u53EF\u7531\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u3002");
|
|
7640
|
+
}
|
|
7641
|
+
if (name === "setting") {
|
|
7642
|
+
if (subcommand === "get") return direct("R1", "remote-read", "setting-read");
|
|
7643
|
+
if (subcommand === "set" && hasFlag(argv, "--dry-run")) {
|
|
7644
|
+
return direct("R1", "local-validation", "setting-dry-run");
|
|
7645
|
+
}
|
|
7646
|
+
if (subcommand === "set" || subcommand === "reset") {
|
|
7647
|
+
return guarded("tenant-config-write", "setting-write");
|
|
7648
|
+
}
|
|
7649
|
+
}
|
|
7650
|
+
if (name === "workspace") {
|
|
7651
|
+
if (subcommand === "push" && hasFlag(argv, "--dry-run")) {
|
|
7652
|
+
return direct("R1", "remote-read", "workspace-push-dry-run");
|
|
7653
|
+
}
|
|
7654
|
+
if (["list", "active", "status", "validate", "verify", "preview"].includes(subcommand)) {
|
|
7655
|
+
return direct("R1", "remote-read", "workspace-read");
|
|
7656
|
+
}
|
|
7657
|
+
if (subcommand === "pull") {
|
|
7658
|
+
return direct("R1", "managed-local-write", "workspace-pull");
|
|
7659
|
+
}
|
|
7660
|
+
if (subcommand === "bind" || subcommand === "add" && argv[2] === "mini") {
|
|
7661
|
+
return guarded("managed-local-write", "workspace-local-change");
|
|
7662
|
+
}
|
|
7663
|
+
if (subcommand === "create") return guarded("tenant-write", "workspace-create");
|
|
7664
|
+
if (subcommand === "push") return forbidden("publish", "\u5F00\u53D1\u7A7A\u95F4\u53D1\u5E03\u5C5E\u4E8E R3\u3002");
|
|
7665
|
+
}
|
|
7666
|
+
if (name === "import") {
|
|
7667
|
+
if (hasFlag(argv, "--dry-run")) {
|
|
7668
|
+
return direct("R1", "local-validation", "import-dry-run");
|
|
7669
|
+
}
|
|
7670
|
+
return guarded("bulk-tenant-write", "import-write");
|
|
7671
|
+
}
|
|
7672
|
+
if (name === "create" || name === "update") {
|
|
7673
|
+
return guarded("tenant-write", name === "create" ? "record-create" : "record-update");
|
|
7674
|
+
}
|
|
7675
|
+
if (name === "upload") return guarded("tenant-write", "document-upload");
|
|
7676
|
+
if (name === "download") return direct("R1", "managed-local-write", "document-download");
|
|
7677
|
+
if (name === "action") {
|
|
7678
|
+
return forbidden(
|
|
7679
|
+
"action-effect-unclassified",
|
|
7680
|
+
"Action \u5C1A\u65E0\u7EDF\u4E00 destructive/publish \u8BED\u4E49\uFF0C\u9996\u7248\u4E0D\u80FD\u6309\u540D\u79F0\u731C\u6D4B\u540E\u653E\u884C\u3002"
|
|
7681
|
+
);
|
|
7682
|
+
}
|
|
7683
|
+
if (name === "delete" || name === "meta" && subcommand === "delete") {
|
|
7684
|
+
return forbidden("destructive", "\u5220\u9664\u64CD\u4F5C\u4E0D\u8FDB\u5165\u987E\u95EE\u63D2\u4EF6\u6267\u884C\u9762\u3002");
|
|
7685
|
+
}
|
|
7686
|
+
if (name === "tenant") {
|
|
7687
|
+
return forbidden("tenant-governance", "\u79DF\u6237\u5F00\u901A\u6216\u6CBB\u7406\u5FC5\u987B\u5347\u7EA7\u6CBB\u7406\u4EBA\u5458\u3002");
|
|
7688
|
+
}
|
|
7689
|
+
if (name === "login" || name === "logout") {
|
|
7690
|
+
return forbidden("identity-governance", "\u767B\u5F55\u4E0E\u767B\u51FA\u5C5E\u4E8E\u8EAB\u4EFD\u6CBB\u7406\u64CD\u4F5C\u3002");
|
|
7691
|
+
}
|
|
7692
|
+
if (name === "chat") {
|
|
7693
|
+
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");
|
|
7694
|
+
}
|
|
7695
|
+
return forbidden("unknown-command", "\u672A\u77E5\u547D\u4EE4\u6309\u6700\u9AD8\u98CE\u9669\u5173\u95ED\u3002");
|
|
7696
|
+
}
|
|
7697
|
+
function summarizeTarget(argv) {
|
|
7698
|
+
const name = argv[0] ?? "";
|
|
7699
|
+
const subcommand = argv[1] ?? "";
|
|
7700
|
+
const target = {};
|
|
7701
|
+
if (["query", "describe", "create", "update", "delete"].includes(name) && subcommand) {
|
|
7702
|
+
target.model = subcommand;
|
|
7703
|
+
}
|
|
7704
|
+
if (name === "action" && subcommand) target.action = subcommand;
|
|
7705
|
+
if (name === "skills" && argv[2] && !argv[2].startsWith("-")) target.skill = argv[2];
|
|
7706
|
+
if (name === "meta" && argv[2] && !argv[2].startsWith("-")) target.path = argv[2];
|
|
7707
|
+
if (name === "cache" && argv[2] && !argv[2].startsWith("-")) {
|
|
7708
|
+
target.cacheOperation = subcommand;
|
|
7709
|
+
target.model = argv[2];
|
|
7710
|
+
}
|
|
7711
|
+
if (name === "setting" && argv[2] && !argv[2].startsWith("-")) {
|
|
7712
|
+
target.domain = argv[2];
|
|
7713
|
+
const settingKeys = valuesAfterFlag(argv, "--set").map((assignment) => assignment.split("=", 1)[0]).filter((key) => Boolean(key)).sort();
|
|
7714
|
+
if (settingKeys.length > 0) target.settingKeys = settingKeys;
|
|
7715
|
+
}
|
|
7716
|
+
const id = flagValue(argv, "--id");
|
|
7717
|
+
if (id) target.recordIdSha256 = sha2565(id);
|
|
7718
|
+
for (const flag of PAYLOAD_FLAGS) {
|
|
7719
|
+
const raw = flagValue(argv, flag);
|
|
7720
|
+
if (!raw) continue;
|
|
7721
|
+
target.payloadSha256 = sha2565(raw);
|
|
7722
|
+
try {
|
|
7723
|
+
const parsed = JSON.parse(raw);
|
|
7724
|
+
if (isPlainObject(parsed)) target.fieldNames = Object.keys(parsed).sort();
|
|
7725
|
+
} catch {
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
return target;
|
|
7729
|
+
}
|
|
7730
|
+
function payloadError(argv) {
|
|
7731
|
+
for (const flag of PAYLOAD_FLAGS) {
|
|
7732
|
+
const raw = flagValue(argv, flag);
|
|
7733
|
+
if (raw === void 0) continue;
|
|
7734
|
+
try {
|
|
7735
|
+
const parsed = JSON.parse(raw);
|
|
7736
|
+
if (!isPlainObject(parsed)) return true;
|
|
7737
|
+
} catch {
|
|
7738
|
+
return true;
|
|
7739
|
+
}
|
|
7740
|
+
}
|
|
7741
|
+
return false;
|
|
7742
|
+
}
|
|
7743
|
+
function direct(risk, effect, reasonCode) {
|
|
7744
|
+
return {
|
|
7745
|
+
risk,
|
|
7746
|
+
execution: "direct",
|
|
7747
|
+
effect,
|
|
7748
|
+
reasonCode,
|
|
7749
|
+
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"
|
|
7750
|
+
};
|
|
7751
|
+
}
|
|
7752
|
+
function guarded(effect, reasonCode) {
|
|
7753
|
+
return {
|
|
7754
|
+
risk: "R2",
|
|
7755
|
+
execution: "guarded",
|
|
7756
|
+
effect,
|
|
7757
|
+
reasonCode,
|
|
7758
|
+
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"
|
|
7759
|
+
};
|
|
7760
|
+
}
|
|
7761
|
+
function forbidden(reasonCode, reason) {
|
|
7762
|
+
return { risk: "R3", execution: "forbidden", effect: reasonCode, reasonCode, reason };
|
|
7763
|
+
}
|
|
7764
|
+
function evidenceFor(risk) {
|
|
7765
|
+
if (risk === "R0") return ["command-digest", "discovery-result"];
|
|
7766
|
+
if (risk === "R1") return ["command-digest", "target-aliases", "result-summary"];
|
|
7767
|
+
if (risk === "R2") {
|
|
7768
|
+
return [
|
|
7769
|
+
"command-digest",
|
|
7770
|
+
"explicit-target-aliases",
|
|
7771
|
+
"pre-state-or-dry-run",
|
|
7772
|
+
"rollback-plan",
|
|
7773
|
+
"execution-receipt",
|
|
7774
|
+
"post-verification"
|
|
7775
|
+
];
|
|
7776
|
+
}
|
|
7777
|
+
return ["governance-decision"];
|
|
7778
|
+
}
|
|
7779
|
+
function flagValue(argv, flag) {
|
|
7780
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
7781
|
+
const arg = argv[index];
|
|
7782
|
+
if (arg === void 0) continue;
|
|
7783
|
+
if (arg === flag) return argv[index + 1];
|
|
7784
|
+
if (arg.startsWith(`${flag}=`)) return arg.slice(flag.length + 1);
|
|
7785
|
+
}
|
|
7786
|
+
return void 0;
|
|
7787
|
+
}
|
|
7788
|
+
function hasFlag(argv, flag) {
|
|
7789
|
+
return argv.some((arg) => arg === flag || arg.startsWith(`${flag}=`));
|
|
7790
|
+
}
|
|
7791
|
+
function flagName(arg) {
|
|
7792
|
+
const equals = arg.indexOf("=");
|
|
7793
|
+
return equals < 0 ? arg : arg.slice(0, equals);
|
|
7794
|
+
}
|
|
7795
|
+
function valuesAfterFlag(argv, flag) {
|
|
7796
|
+
const index = argv.indexOf(flag);
|
|
7797
|
+
if (index < 0) return [];
|
|
7798
|
+
const values = [];
|
|
7799
|
+
for (let cursor = index + 1; cursor < argv.length; cursor += 1) {
|
|
7800
|
+
const value = argv[cursor];
|
|
7801
|
+
if (value === void 0 || value.startsWith("-")) break;
|
|
7802
|
+
values.push(value);
|
|
7803
|
+
}
|
|
7804
|
+
return values;
|
|
7805
|
+
}
|
|
7806
|
+
function sha2565(value) {
|
|
7807
|
+
return createHash5("sha256").update(value).digest("hex");
|
|
7808
|
+
}
|
|
7809
|
+
function isPlainObject(value) {
|
|
7810
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7811
|
+
}
|
|
7812
|
+
|
|
7813
|
+
// src/commands/delivery-execution.ts
|
|
7814
|
+
import { createHash as createHash6, randomBytes as secureRandomBytes } from "crypto";
|
|
7815
|
+
import { spawn } from "child_process";
|
|
7816
|
+
import { createReadStream as createReadStream2, promises as fs7 } from "fs";
|
|
7817
|
+
import path6 from "path";
|
|
7818
|
+
import { CliError as CliError20, CliErrorCode as CliErrorCode19, formatObject as formatObject18 } from "@hcmai/sdk";
|
|
7819
|
+
init_exit();
|
|
7820
|
+
var EXECUTION_CONTRACT_VERSION = "2026-08-28.controlled-v1";
|
|
7821
|
+
var DEFAULT_TTL_SECONDS = 15 * 60;
|
|
7822
|
+
var MIN_TTL_SECONDS = 60;
|
|
7823
|
+
var MAX_TTL_SECONDS = 60 * 60;
|
|
7824
|
+
var PLAN_ID_PATTERN = /^[a-f0-9]{24}$/;
|
|
7825
|
+
async function prepareDeliveryExecution(input) {
|
|
7826
|
+
const project = await realProject(input.project);
|
|
7827
|
+
await readProjectMode(project);
|
|
7828
|
+
const assessment = assessDeliveryCommand(input.argv);
|
|
7829
|
+
assertGuardedAssessment(assessment, input.argv);
|
|
7830
|
+
const ttlSeconds = input.ttlSeconds ?? DEFAULT_TTL_SECONDS;
|
|
7831
|
+
if (!Number.isInteger(ttlSeconds) || ttlSeconds < MIN_TTL_SECONDS || ttlSeconds > MAX_TTL_SECONDS) {
|
|
7832
|
+
throw invalid4(`--ttl-seconds \u5FC5\u987B\u662F ${MIN_TTL_SECONDS} \u5230 ${MAX_TTL_SECONDS} \u7684\u6574\u6570`);
|
|
7833
|
+
}
|
|
7834
|
+
const change = await bindDocument(project, input.changeRef, "\u53D8\u66F4\u8BF4\u660E");
|
|
7835
|
+
const rollback = await bindDocument(project, input.rollbackRef, "\u56DE\u6EDA\u8BF4\u660E");
|
|
7836
|
+
const evidence = await bindEvidenceDirectory(project, input.evidenceDir);
|
|
7837
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
|
|
7838
|
+
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
7839
|
+
assertRuntimeMatches(runtime, lockedRuntime);
|
|
7840
|
+
const created = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7841
|
+
if (Number.isNaN(created.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
7842
|
+
const core = {
|
|
7843
|
+
schemaVersion: 1,
|
|
7844
|
+
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
7845
|
+
nonceSha256: sha2566((input.randomBytes ?? secureRandomBytes)(12)),
|
|
7846
|
+
createdAt: created.toISOString(),
|
|
7847
|
+
expiresAt: new Date(created.getTime() + ttlSeconds * 1e3).toISOString(),
|
|
7848
|
+
chironLockSha256: sha2566(lockBytes),
|
|
7849
|
+
runtime,
|
|
7850
|
+
assessment,
|
|
7851
|
+
documents: { change, rollback },
|
|
7852
|
+
evidenceDir: evidence.relative
|
|
7853
|
+
};
|
|
7854
|
+
const planId = sha2566(canonicalJson(core)).slice(0, 24);
|
|
7855
|
+
const plan = { ...core, planId };
|
|
7856
|
+
const plansDir = path6.join(project, ".hcm-delivery", "execution-plans");
|
|
7857
|
+
await safeManagedDirectory(project, ".hcm-delivery/execution-plans", plansDir);
|
|
7858
|
+
const planFile = path6.join(plansDir, `${planId}.json`);
|
|
7859
|
+
try {
|
|
7860
|
+
await fs7.writeFile(planFile, `${JSON.stringify(plan, null, 2)}
|
|
7861
|
+
`, {
|
|
7862
|
+
encoding: "utf8",
|
|
7863
|
+
mode: 384,
|
|
7864
|
+
flag: "wx"
|
|
7865
|
+
});
|
|
7866
|
+
} catch (cause) {
|
|
7867
|
+
throw invalid4(`\u6267\u884C\u8BA1\u5212\u5199\u5165\u5931\u8D25\u6216 planId \u5DF2\u5B58\u5728\uFF1A${planId}`, cause);
|
|
7868
|
+
}
|
|
7869
|
+
return plan;
|
|
7870
|
+
}
|
|
7871
|
+
async function executeDeliveryPlan(input) {
|
|
7872
|
+
if (!PLAN_ID_PATTERN.test(input.planId)) throw invalid4("planId \u5FC5\u987B\u662F 24 \u4F4D\u5C0F\u5199\u5341\u516D\u8FDB\u5236");
|
|
7873
|
+
const project = await realProject(input.project);
|
|
7874
|
+
if (await readProjectMode(project) !== "controlled") {
|
|
7875
|
+
throw invalid4("\u9879\u76EE\u4E0D\u662F controlled\uFF0C\u4E0D\u80FD\u6267\u884C R2 \u8BA1\u5212");
|
|
7876
|
+
}
|
|
7877
|
+
const planFile = path6.join(project, ".hcm-delivery", "execution-plans", `${input.planId}.json`);
|
|
7878
|
+
const plan = await readPlan(project, planFile);
|
|
7879
|
+
if (plan.planId !== input.planId || computePlanId(plan) !== input.planId) {
|
|
7880
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u5185\u5BB9\u4E0E planId \u6458\u8981\u4E0D\u4E00\u81F4");
|
|
7881
|
+
}
|
|
7882
|
+
const now = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7883
|
+
if (Number.isNaN(now.getTime())) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E0\u6548");
|
|
7884
|
+
const expiresAt = Date.parse(plan.expiresAt);
|
|
7885
|
+
const createdAt = Date.parse(plan.createdAt);
|
|
7886
|
+
if (!Number.isFinite(createdAt) || !Number.isFinite(expiresAt) || expiresAt - createdAt < MIN_TTL_SECONDS * 1e3 || expiresAt - createdAt > MAX_TTL_SECONDS * 1e3) {
|
|
7887
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u65F6\u95F4\u7A97\u53E3\u65E0\u6548");
|
|
7888
|
+
}
|
|
7889
|
+
if (now.getTime() < createdAt - 5e3) throw invalid4("\u5F53\u524D\u65F6\u95F4\u65E9\u4E8E\u8BA1\u5212\u521B\u5EFA\u65F6\u95F4");
|
|
7890
|
+
if (now.getTime() > expiresAt) throw invalid4("\u6267\u884C\u8BA1\u5212\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0 prepare");
|
|
7891
|
+
const assessment = assessDeliveryCommand(input.argv);
|
|
7892
|
+
assertGuardedAssessment(assessment, input.argv);
|
|
7893
|
+
if (canonicalJson(assessment) !== canonicalJson(plan.assessment)) {
|
|
7894
|
+
throw invalid4("\u5B9E\u9645 argv \u4E0E\u6267\u884C\u8BA1\u5212\u7684\u547D\u4EE4\u6458\u8981\u6216\u98CE\u9669\u4E8B\u5B9E\u4E0D\u4E00\u81F4");
|
|
7895
|
+
}
|
|
7896
|
+
const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
|
|
7897
|
+
if (sha2566(lockBytes) !== plan.chironLockSha256) {
|
|
7898
|
+
throw invalid4("Chiron \u9501\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0\u8BC4\u4F30\u5E76 prepare");
|
|
7899
|
+
}
|
|
7900
|
+
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
7901
|
+
assertRuntimeMatches(runtime, lockedRuntime);
|
|
7902
|
+
if (canonicalJson(runtime) !== canonicalJson(plan.runtime)) {
|
|
7903
|
+
throw invalid4("CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E\u6267\u884C\u8BA1\u5212\u4E0D\u4E00\u81F4");
|
|
7904
|
+
}
|
|
7905
|
+
await assertDocumentUnchanged(project, plan.documents.change, "\u53D8\u66F4\u8BF4\u660E");
|
|
7906
|
+
await assertDocumentUnchanged(project, plan.documents.rollback, "\u56DE\u6EDA\u8BF4\u660E");
|
|
7907
|
+
const evidenceDir = await bindEvidenceDirectory(project, plan.evidenceDir);
|
|
7908
|
+
const receiptFile = path6.join(evidenceDir.absolute, `${plan.planId}.receipt.json`);
|
|
7909
|
+
if (await pathExists(receiptFile)) throw invalid4(`\u6267\u884C\u6536\u636E\u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u518D\u6B21\u6267\u884C\uFF1A${plan.planId}`);
|
|
7910
|
+
const claimsDir = path6.join(project, ".hcm-delivery", "execution-claims");
|
|
7911
|
+
await safeManagedDirectory(project, ".hcm-delivery/execution-claims", claimsDir);
|
|
7912
|
+
const claimFile = path6.join(claimsDir, `${plan.planId}.json`);
|
|
7913
|
+
try {
|
|
7914
|
+
await fs7.writeFile(
|
|
7915
|
+
claimFile,
|
|
7916
|
+
`${JSON.stringify({ schemaVersion: 1, planId: plan.planId, claimedAt: now.toISOString() }, null, 2)}
|
|
7917
|
+
`,
|
|
7918
|
+
{ encoding: "utf8", mode: 384, flag: "wx" }
|
|
7919
|
+
);
|
|
7920
|
+
} catch (cause) {
|
|
7921
|
+
throw invalid4(`\u6267\u884C\u8BA1\u5212\u5DF2\u6D88\u8D39\uFF0C\u62D2\u7EDD\u91CD\u590D\u6267\u884C\uFF1A${plan.planId}`, cause);
|
|
7922
|
+
}
|
|
7923
|
+
const stdoutHash = createHash6("sha256");
|
|
7924
|
+
const stderrHash = createHash6("sha256");
|
|
7925
|
+
const run = input.runCommand ?? runCurrentCliCommand;
|
|
7926
|
+
let exitCode;
|
|
7927
|
+
try {
|
|
7928
|
+
({ exitCode } = await run(input.argv, project, {
|
|
7929
|
+
stdout: (chunk) => stdoutHash.update(chunk),
|
|
7930
|
+
stderr: (chunk) => stderrHash.update(chunk)
|
|
7931
|
+
}));
|
|
7932
|
+
} catch (cause) {
|
|
7933
|
+
stderrHash.update(
|
|
7934
|
+
Buffer.from(`delivery executor failed: ${String(cause.message)}
|
|
7935
|
+
`)
|
|
7936
|
+
);
|
|
7937
|
+
exitCode = 1;
|
|
7938
|
+
}
|
|
7939
|
+
const completed = (input.now ?? (() => /* @__PURE__ */ new Date()))();
|
|
7940
|
+
if (Number.isNaN(completed.getTime())) throw invalid4("\u5B8C\u6210\u65F6\u95F4\u65E0\u6548");
|
|
7941
|
+
if (!Number.isInteger(exitCode) || exitCode < 0) exitCode = 1;
|
|
7942
|
+
const receipt = {
|
|
7943
|
+
schemaVersion: 1,
|
|
7944
|
+
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
7945
|
+
planId: plan.planId,
|
|
7946
|
+
assessmentId: plan.assessment.assessmentId,
|
|
7947
|
+
commandSha256: plan.assessment.commandSha256,
|
|
7948
|
+
startedAt: now.toISOString(),
|
|
7949
|
+
completedAt: completed.toISOString(),
|
|
7950
|
+
exitCode,
|
|
7951
|
+
stdoutSha256: stdoutHash.digest("hex"),
|
|
7952
|
+
stderrSha256: stderrHash.digest("hex")
|
|
7953
|
+
};
|
|
7954
|
+
await writeReceiptExclusive(receiptFile, receipt);
|
|
7955
|
+
return receipt;
|
|
7956
|
+
}
|
|
7957
|
+
async function deliveryPrepareCommand(argv, args) {
|
|
7958
|
+
try {
|
|
7959
|
+
if (!args.changeRef) throw invalid4("--change-ref <file> \u5FC5\u586B");
|
|
7960
|
+
if (!args.rollbackRef) throw invalid4("--rollback-ref <file> \u5FC5\u586B");
|
|
7961
|
+
if (!args.evidenceDir) throw invalid4("--evidence-dir <dir> \u5FC5\u586B");
|
|
7962
|
+
const ttlSeconds = parseTtl(args.ttlSeconds);
|
|
7963
|
+
const plan = await prepareDeliveryExecution({
|
|
7964
|
+
project: args.project ?? process.cwd(),
|
|
7965
|
+
argv,
|
|
7966
|
+
changeRef: args.changeRef,
|
|
7967
|
+
rollbackRef: args.rollbackRef,
|
|
7968
|
+
evidenceDir: args.evidenceDir,
|
|
7969
|
+
...ttlSeconds === void 0 ? {} : { ttlSeconds }
|
|
7970
|
+
});
|
|
7971
|
+
process.stdout.write(formatObject18(plan, { format: args.output ?? "json" }) + "\n");
|
|
7972
|
+
} catch (cause) {
|
|
7973
|
+
exitWithError(cause);
|
|
7974
|
+
}
|
|
7975
|
+
}
|
|
7976
|
+
async function deliveryExecuteCommand(planId, argv, args) {
|
|
7977
|
+
try {
|
|
7978
|
+
const receipt = await executeDeliveryPlan({
|
|
7979
|
+
project: args.project ?? process.cwd(),
|
|
7980
|
+
planId,
|
|
7981
|
+
argv
|
|
7982
|
+
});
|
|
7983
|
+
process.stdout.write(formatObject18(receipt, { format: args.output ?? "json" }) + "\n");
|
|
7984
|
+
process.exitCode = receipt.exitCode;
|
|
7985
|
+
} catch (cause) {
|
|
7986
|
+
exitWithError(cause);
|
|
7987
|
+
}
|
|
7988
|
+
}
|
|
7989
|
+
async function readProjectMode(project) {
|
|
7990
|
+
const file = path6.join(project, ".hcm-delivery", "project.yml");
|
|
7991
|
+
const content = await readSafeFile(project, file, "\u9879\u76EE\u914D\u7F6E");
|
|
7992
|
+
const match = content.toString("utf8").match(
|
|
7993
|
+
/^deliveryMode:\s*(?:"(plan-only|controlled)"|'(plan-only|controlled)'|(plan-only|controlled))\s*$/m
|
|
7994
|
+
);
|
|
7995
|
+
const mode = match?.[1] ?? match?.[2] ?? match?.[3];
|
|
7996
|
+
if (mode !== "plan-only" && mode !== "controlled") {
|
|
7997
|
+
throw invalid4("project.yml \u7F3A\u5C11\u5408\u6CD5 deliveryMode");
|
|
7998
|
+
}
|
|
7999
|
+
return mode;
|
|
8000
|
+
}
|
|
8001
|
+
async function readChironLock(project) {
|
|
8002
|
+
const file = path6.join(project, ".hcm-delivery", "chiron.lock.json");
|
|
8003
|
+
const bytes = await readSafeFile(project, file, "Chiron \u9501");
|
|
8004
|
+
let parsed;
|
|
8005
|
+
try {
|
|
8006
|
+
parsed = JSON.parse(bytes.toString("utf8"));
|
|
8007
|
+
} catch (cause) {
|
|
8008
|
+
throw invalid4("Chiron \u9501\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
8009
|
+
}
|
|
8010
|
+
const launch = parsed?.runtime?.launch;
|
|
8011
|
+
if (!isRuntimeIdentity(launch)) throw invalid4("Chiron \u9501\u7F3A\u5C11\u5408\u6CD5 runtime.launch \u6458\u8981");
|
|
8012
|
+
return { bytes, runtime: launch };
|
|
8013
|
+
}
|
|
8014
|
+
async function bindDocument(project, reference, label) {
|
|
8015
|
+
const relative2 = safeRelative(reference, label);
|
|
8016
|
+
if (!(relative2.startsWith("work/") || relative2.startsWith("decisions/"))) {
|
|
8017
|
+
throw invalid4(`${label}\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE work/ \u6216 decisions/ \u4E0B`);
|
|
8018
|
+
}
|
|
8019
|
+
const file = path6.join(project, relative2);
|
|
8020
|
+
const bytes = await readSafeFile(project, file, label);
|
|
8021
|
+
return { path: relative2, sha256: sha2566(bytes) };
|
|
8022
|
+
}
|
|
8023
|
+
async function assertDocumentUnchanged(project, document, label) {
|
|
8024
|
+
const current = await bindDocument(project, document.path, label);
|
|
8025
|
+
if (current.sha256 !== document.sha256) throw invalid4(`${label}\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0 prepare`);
|
|
8026
|
+
}
|
|
8027
|
+
async function bindEvidenceDirectory(project, reference) {
|
|
8028
|
+
const relative2 = safeRelative(reference, "evidence \u76EE\u5F55");
|
|
8029
|
+
if (!(relative2 === "evidence" || relative2.startsWith("evidence/"))) {
|
|
8030
|
+
throw invalid4("evidence \u76EE\u5F55\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE evidence/ \u4E0B");
|
|
8031
|
+
}
|
|
8032
|
+
const absolute = path6.join(project, relative2);
|
|
8033
|
+
await assertNoSymlinkComponents(project, relative2);
|
|
8034
|
+
const stat = await fs7.stat(absolute).catch((cause) => {
|
|
8035
|
+
throw invalid4(`evidence \u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
8036
|
+
});
|
|
8037
|
+
if (!stat.isDirectory()) throw invalid4(`evidence \u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
8038
|
+
return { relative: relative2, absolute };
|
|
8039
|
+
}
|
|
8040
|
+
async function readPlan(project, file) {
|
|
8041
|
+
const bytes = await readSafeFile(project, file, "\u6267\u884C\u8BA1\u5212");
|
|
8042
|
+
let plan;
|
|
8043
|
+
try {
|
|
8044
|
+
plan = JSON.parse(bytes.toString("utf8"));
|
|
8045
|
+
} catch (cause) {
|
|
8046
|
+
throw invalid4("\u6267\u884C\u8BA1\u5212\u4E0D\u662F\u5408\u6CD5 JSON", cause);
|
|
8047
|
+
}
|
|
8048
|
+
if (!isExecutionPlan(plan)) throw invalid4("\u6267\u884C\u8BA1\u5212 schema \u65E0\u6548");
|
|
8049
|
+
return plan;
|
|
8050
|
+
}
|
|
8051
|
+
function computePlanId(plan) {
|
|
8052
|
+
const core = { ...plan };
|
|
8053
|
+
delete core.planId;
|
|
8054
|
+
return sha2566(canonicalJson(core)).slice(0, 24);
|
|
8055
|
+
}
|
|
8056
|
+
function assertGuardedAssessment(assessment, argv) {
|
|
8057
|
+
if (assessment.risk !== "R2") throw invalid4("\u53EA\u6709 R2 \u547D\u4EE4\u53EF\u4EE5\u8FDB\u5165\u53D7\u63A7\u6267\u884C\u8BA1\u5212");
|
|
8058
|
+
if (!assessment.guardedExecutionAvailable) {
|
|
8059
|
+
throw invalid4("\u8BE5 R2 \u547D\u4EE4\u4E0D\u5728\u9996\u6279\u53D7\u63A7\u6267\u884C\u5668\u652F\u6301\u9762");
|
|
8060
|
+
}
|
|
8061
|
+
if (!assessment.environment.envAlias) throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --env \u73AF\u5883\u522B\u540D");
|
|
8062
|
+
if (!assessment.environment.identityAlias) {
|
|
8063
|
+
throw invalid4("R2 argv \u5FC5\u987B\u663E\u5F0F\u5305\u542B --as \u8EAB\u4EFD\u522B\u540D");
|
|
8064
|
+
}
|
|
8065
|
+
validateSupportedCommandShape(assessment.reasonCode, argv);
|
|
8066
|
+
}
|
|
8067
|
+
function validateSupportedCommandShape(reasonCode, argv) {
|
|
8068
|
+
if (reasonCode === "record-create") {
|
|
8069
|
+
requireFlagValue(argv, "--data", "create \u5FC5\u987B\u5305\u542B --data <JSON>");
|
|
8070
|
+
return;
|
|
8071
|
+
}
|
|
8072
|
+
if (reasonCode === "record-update") {
|
|
8073
|
+
requireFlagValue(argv, "--id", "update \u5FC5\u987B\u5305\u542B --id <id>");
|
|
8074
|
+
requireFlagValue(argv, "--data", "update \u5FC5\u987B\u5305\u542B --data <JSON>");
|
|
8075
|
+
return;
|
|
8076
|
+
}
|
|
8077
|
+
if (reasonCode === "cache-mutation") {
|
|
8078
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid4("cache clear \u5FC5\u987B\u5305\u542B Model");
|
|
8079
|
+
return;
|
|
8080
|
+
}
|
|
8081
|
+
if (reasonCode === "setting-write") {
|
|
8082
|
+
if (!argv[2] || argv[2].startsWith("-")) throw invalid4("setting \u5199\u547D\u4EE4\u5FC5\u987B\u5305\u542B domain");
|
|
8083
|
+
if (argv[1] === "set") {
|
|
8084
|
+
requireFlagValue(argv, "--set", "setting set \u5FC5\u987B\u5305\u542B --set <assignment>");
|
|
8085
|
+
return;
|
|
8086
|
+
}
|
|
8087
|
+
if (argv[1] === "reset" && (!argv[3] || !argv[4])) {
|
|
8088
|
+
throw invalid4("setting reset \u5FC5\u987B\u5305\u542B domain\u3001namespace \u4E0E key");
|
|
8089
|
+
}
|
|
8090
|
+
}
|
|
8091
|
+
}
|
|
8092
|
+
function requireFlagValue(argv, flag, message) {
|
|
8093
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
8094
|
+
const value = argv[index];
|
|
8095
|
+
if (value === flag) {
|
|
8096
|
+
const next = argv[index + 1];
|
|
8097
|
+
if (next && !next.startsWith("-")) return next;
|
|
8098
|
+
throw invalid4(message);
|
|
8099
|
+
}
|
|
8100
|
+
if (value?.startsWith(`${flag}=`) && value.length > flag.length + 1) {
|
|
8101
|
+
return value.slice(flag.length + 1);
|
|
8102
|
+
}
|
|
8103
|
+
}
|
|
8104
|
+
throw invalid4(message);
|
|
8105
|
+
}
|
|
8106
|
+
function assertRuntimeMatches(actual, expected) {
|
|
8107
|
+
if (canonicalJson(actual) !== canonicalJson(expected)) {
|
|
8108
|
+
throw invalid4("\u5F53\u524D CLI \u8FD0\u884C\u65F6\u6458\u8981\u4E0E Chiron \u9501\u4E0D\u4E00\u81F4\uFF0C\u8BF7\u91CD\u65B0 skills sync");
|
|
8109
|
+
}
|
|
8110
|
+
}
|
|
8111
|
+
async function currentRuntimeIdentity() {
|
|
8112
|
+
const entry = process.argv[1];
|
|
8113
|
+
if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
8114
|
+
return {
|
|
8115
|
+
kind: "node-entry",
|
|
8116
|
+
executableSha256: await sha256File2(process.execPath),
|
|
8117
|
+
entrySha256: await sha256File2(entry)
|
|
8118
|
+
};
|
|
8119
|
+
}
|
|
8120
|
+
async function runCurrentCliCommand(argv, cwd, streams) {
|
|
8121
|
+
const entry = process.argv[1];
|
|
8122
|
+
if (!entry) throw invalid4("\u65E0\u6CD5\u89E3\u6790\u5F53\u524D CLI entry");
|
|
8123
|
+
return new Promise((resolve3, reject) => {
|
|
8124
|
+
const child = spawn(process.execPath, [entry, ...argv], {
|
|
8125
|
+
cwd,
|
|
8126
|
+
env: process.env,
|
|
8127
|
+
shell: false,
|
|
8128
|
+
stdio: ["inherit", "pipe", "pipe"],
|
|
8129
|
+
windowsHide: true
|
|
8130
|
+
});
|
|
8131
|
+
child.stdout.on("data", (chunk) => {
|
|
8132
|
+
streams.stdout(chunk);
|
|
8133
|
+
process.stdout.write(chunk);
|
|
8134
|
+
});
|
|
8135
|
+
child.stderr.on("data", (chunk) => {
|
|
8136
|
+
streams.stderr(chunk);
|
|
8137
|
+
process.stderr.write(chunk);
|
|
8138
|
+
});
|
|
8139
|
+
child.once("error", reject);
|
|
8140
|
+
child.once("close", (code, signal) => resolve3({ exitCode: code ?? (signal ? 1 : 0) }));
|
|
8141
|
+
});
|
|
8142
|
+
}
|
|
8143
|
+
async function writeReceiptExclusive(target, receipt) {
|
|
8144
|
+
const temp = `${target}.${process.pid}.${Date.now()}.tmp`;
|
|
8145
|
+
try {
|
|
8146
|
+
await fs7.writeFile(temp, `${JSON.stringify(receipt, null, 2)}
|
|
8147
|
+
`, {
|
|
8148
|
+
encoding: "utf8",
|
|
8149
|
+
mode: 384,
|
|
8150
|
+
flag: "wx"
|
|
8151
|
+
});
|
|
8152
|
+
await fs7.link(temp, target);
|
|
8153
|
+
} catch (cause) {
|
|
8154
|
+
throw invalid4(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${path6.basename(target)}`, cause);
|
|
8155
|
+
} finally {
|
|
8156
|
+
await fs7.unlink(temp).catch(() => void 0);
|
|
8157
|
+
}
|
|
8158
|
+
}
|
|
8159
|
+
async function safeManagedDirectory(project, relative2, absolute) {
|
|
8160
|
+
await assertNoSymlinkComponents(project, path6.dirname(relative2));
|
|
8161
|
+
const existing = await fs7.lstat(absolute).catch(() => void 0);
|
|
8162
|
+
if (existing?.isSymbolicLink()) throw invalid4(`\u53D7\u7BA1\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8163
|
+
if (existing && !existing.isDirectory()) throw invalid4(`\u53D7\u7BA1\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
8164
|
+
if (!existing) await fs7.mkdir(absolute, { mode: 448 });
|
|
8165
|
+
await fs7.chmod(absolute, 448);
|
|
8166
|
+
}
|
|
8167
|
+
async function pathExists(file) {
|
|
8168
|
+
return fs7.lstat(file).then(
|
|
8169
|
+
() => true,
|
|
8170
|
+
(cause) => {
|
|
8171
|
+
if (cause.code === "ENOENT") return false;
|
|
8172
|
+
throw cause;
|
|
8173
|
+
}
|
|
8174
|
+
);
|
|
8175
|
+
}
|
|
8176
|
+
async function readSafeFile(project, file, label) {
|
|
8177
|
+
const relative2 = path6.relative(project, file).split(path6.sep).join("/");
|
|
8178
|
+
if (relative2.startsWith("../") || path6.isAbsolute(relative2))
|
|
8179
|
+
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8180
|
+
await assertNoSymlinkComponents(project, relative2);
|
|
8181
|
+
let stat;
|
|
8182
|
+
try {
|
|
8183
|
+
stat = await fs7.lstat(file);
|
|
8184
|
+
} catch (cause) {
|
|
8185
|
+
throw invalid4(`${label}\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
8186
|
+
}
|
|
8187
|
+
if (stat.isSymbolicLink()) throw invalid4(`${label}\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8188
|
+
if (!stat.isFile()) throw invalid4(`${label}\u4E0D\u662F\u666E\u901A\u6587\u4EF6\uFF1A${relative2}`);
|
|
8189
|
+
return fs7.readFile(file);
|
|
8190
|
+
}
|
|
8191
|
+
async function assertNoSymlinkComponents(project, relative2) {
|
|
8192
|
+
const parts = relative2.split("/").filter(Boolean);
|
|
8193
|
+
let current = project;
|
|
8194
|
+
for (const part of parts) {
|
|
8195
|
+
current = path6.join(current, part);
|
|
8196
|
+
const stat = await fs7.lstat(current).catch(() => void 0);
|
|
8197
|
+
if (stat?.isSymbolicLink()) throw invalid4(`\u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8198
|
+
if (!stat) break;
|
|
8199
|
+
}
|
|
8200
|
+
}
|
|
8201
|
+
async function realProject(requested) {
|
|
8202
|
+
const absolute = path6.resolve(requested);
|
|
8203
|
+
let project;
|
|
8204
|
+
try {
|
|
8205
|
+
project = await fs7.realpath(absolute);
|
|
8206
|
+
} catch (cause) {
|
|
8207
|
+
throw invalid4(`\u9879\u76EE\u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${absolute}`, cause);
|
|
8208
|
+
}
|
|
8209
|
+
const stat = await fs7.stat(project);
|
|
8210
|
+
if (!stat.isDirectory()) throw invalid4(`\u9879\u76EE\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${absolute}`);
|
|
8211
|
+
return project;
|
|
8212
|
+
}
|
|
8213
|
+
function safeRelative(reference, label) {
|
|
8214
|
+
if (!reference || path6.isAbsolute(reference) || /[\r\n\0]/.test(reference)) {
|
|
8215
|
+
throw invalid4(`${label}\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84`);
|
|
8216
|
+
}
|
|
8217
|
+
const normalized = path6.normalize(reference).split(path6.sep).join("/");
|
|
8218
|
+
if (normalized === ".." || normalized.startsWith("../") || normalized === ".") {
|
|
8219
|
+
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8220
|
+
}
|
|
8221
|
+
return normalized.replace(/^\.\//, "");
|
|
8222
|
+
}
|
|
8223
|
+
function isRuntimeIdentity(value) {
|
|
8224
|
+
if (!isRecord(value) || !isSha(value.executableSha256)) return false;
|
|
8225
|
+
if (value.kind === "executable") return true;
|
|
8226
|
+
return value.kind === "node-entry" && isSha(value.entrySha256);
|
|
8227
|
+
}
|
|
8228
|
+
function isExecutionPlan(value) {
|
|
8229
|
+
if (!isRecord(value)) return false;
|
|
8230
|
+
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";
|
|
8231
|
+
}
|
|
8232
|
+
function isBoundDocument(value) {
|
|
8233
|
+
return isRecord(value) && typeof value.path === "string" && isSha(value.sha256);
|
|
8234
|
+
}
|
|
8235
|
+
function isRecord(value) {
|
|
8236
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
8237
|
+
}
|
|
8238
|
+
function isSha(value) {
|
|
8239
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
8240
|
+
}
|
|
8241
|
+
function parseTtl(value) {
|
|
8242
|
+
if (value === void 0) return void 0;
|
|
8243
|
+
if (!/^\d+$/.test(value)) throw invalid4("--ttl-seconds \u5FC5\u987B\u662F\u6574\u6570");
|
|
8244
|
+
return Number(value);
|
|
8245
|
+
}
|
|
8246
|
+
function canonicalJson(value) {
|
|
8247
|
+
if (Array.isArray(value)) return `[${value.map(canonicalJson).join(",")}]`;
|
|
8248
|
+
if (isRecord(value)) {
|
|
8249
|
+
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(value[key])}`).join(",")}}`;
|
|
8250
|
+
}
|
|
8251
|
+
return JSON.stringify(value) ?? "null";
|
|
8252
|
+
}
|
|
8253
|
+
function sha2566(value) {
|
|
8254
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
8255
|
+
}
|
|
8256
|
+
async function sha256File2(file) {
|
|
8257
|
+
const hash = createHash6("sha256");
|
|
8258
|
+
await new Promise((resolve3, reject) => {
|
|
8259
|
+
const stream = createReadStream2(file);
|
|
8260
|
+
stream.on("data", (chunk) => hash.update(chunk));
|
|
8261
|
+
stream.on("error", reject);
|
|
8262
|
+
stream.on("end", resolve3);
|
|
8263
|
+
});
|
|
8264
|
+
return hash.digest("hex");
|
|
8265
|
+
}
|
|
8266
|
+
function invalid4(message, cause) {
|
|
8267
|
+
return new CliError20({ code: CliErrorCode19.INVALID_ARGUMENT, message, cause });
|
|
8268
|
+
}
|
|
8269
|
+
|
|
8270
|
+
// src/startup.ts
|
|
8271
|
+
function needsLegacyProfileMigration(argv) {
|
|
8272
|
+
const args = argv.slice(2);
|
|
8273
|
+
if (args[0] === "delivery") return false;
|
|
8274
|
+
return !args.some((arg) => ["--help", "-h", "--version", "-V"].includes(arg));
|
|
8275
|
+
}
|
|
8276
|
+
|
|
6613
8277
|
// src/index.ts
|
|
6614
8278
|
var program = new Command();
|
|
6615
8279
|
var exitOnBrokenPipe = (err) => {
|
|
@@ -6640,6 +8304,10 @@ var identity = program.command("identity").description("\u7BA1\u7406 HCM \u8EAB\
|
|
|
6640
8304
|
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));
|
|
6641
8305
|
identity.command("use <name>").description("\u628A identity \u8BBE\u4E3A\u5176 env \u7684 default identity").action((name) => identityUse(name));
|
|
6642
8306
|
identity.command("remove <name>").description("\u5220\u9664 identity\uFF08\u6E05 token + \u82E5\u662F env default \u5219\u6E05\u7A7A\uFF09").action((name) => identityRemove(name));
|
|
8307
|
+
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");
|
|
8308
|
+
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));
|
|
8309
|
+
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));
|
|
8310
|
+
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));
|
|
6643
8311
|
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(
|
|
6644
8312
|
"--filter <JSON>",
|
|
6645
8313
|
`\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`
|
|
@@ -6703,6 +8371,7 @@ var skills = program.command("skills").description("\u777F\u620E Chiron \u4EA4\u
|
|
|
6703
8371
|
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));
|
|
6704
8372
|
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));
|
|
6705
8373
|
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));
|
|
8374
|
+
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));
|
|
6706
8375
|
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));
|
|
6707
8376
|
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));
|
|
6708
8377
|
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));
|
|
@@ -6711,13 +8380,15 @@ setting.command("get <domain>").description("\u8BFB\u67D0\u57DF\u53C2\u6570\uFF0
|
|
|
6711
8380
|
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));
|
|
6712
8381
|
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));
|
|
6713
8382
|
async function main() {
|
|
6714
|
-
|
|
6715
|
-
|
|
6716
|
-
|
|
6717
|
-
|
|
6718
|
-
|
|
8383
|
+
if (needsLegacyProfileMigration(process.argv)) {
|
|
8384
|
+
try {
|
|
8385
|
+
await migrateLegacyProfiles();
|
|
8386
|
+
} catch (e) {
|
|
8387
|
+
process.stderr.write(
|
|
8388
|
+
`[hcm] event=migration_error reason=${JSON.stringify(String(e.message))}
|
|
6719
8389
|
`
|
|
6720
|
-
|
|
8390
|
+
);
|
|
8391
|
+
}
|
|
6721
8392
|
}
|
|
6722
8393
|
await program.parseAsync(process.argv);
|
|
6723
8394
|
}
|