@hcmai/cli 0.3.4 → 0.3.5
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/dist/index.js +1003 -365
- package/dist/index.js.map +1 -1
- package/dist/skills/hcm-delivery-doctor/SKILL.md +49 -0
- package/dist/skills/llms.txt +11 -0
- package/dist/skills/manifest.json +692 -0
- package/package.json +3 -3
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(path9) {
|
|
527
|
+
const segs = path9.split(".").filter(Boolean);
|
|
528
528
|
if (segs.length < 2) return "";
|
|
529
529
|
const names = segs.map((k) => agentDisplayName(k));
|
|
530
530
|
return chalk.gray(` \u2191 \u6765\u81EA ${names.join(" \u203A ")}`) + "\n";
|
|
@@ -1706,8 +1706,8 @@ function agentLabel(agentId) {
|
|
|
1706
1706
|
if (/^[0-9a-f]{8}-[0-9a-f-]{27}$/i.test(agentId)) return `agent \u2026${agentId.slice(-4)}`;
|
|
1707
1707
|
return agentDisplayName(agentId);
|
|
1708
1708
|
}
|
|
1709
|
-
function originLine(
|
|
1710
|
-
const segs = (
|
|
1709
|
+
function originLine(path9) {
|
|
1710
|
+
const segs = (path9 ?? "").split(".").filter(Boolean);
|
|
1711
1711
|
if (segs.length < 2) return null;
|
|
1712
1712
|
return `\u6765\u81EA ${segs.map((k) => agentDisplayName(k)).join(" \u203A ")}`;
|
|
1713
1713
|
}
|
|
@@ -2286,9 +2286,9 @@ async function loginCommand(args) {
|
|
|
2286
2286
|
} catch (e) {
|
|
2287
2287
|
const err = e;
|
|
2288
2288
|
if (err.isAxiosError) {
|
|
2289
|
-
const { fromAxiosError:
|
|
2289
|
+
const { fromAxiosError: fromAxiosError19 } = await import("@hcmai/sdk");
|
|
2290
2290
|
exitWithError(
|
|
2291
|
-
|
|
2291
|
+
fromAxiosError19(
|
|
2292
2292
|
e,
|
|
2293
2293
|
envName
|
|
2294
2294
|
)
|
|
@@ -3059,8 +3059,8 @@ import {
|
|
|
3059
3059
|
formatObject as formatObject7,
|
|
3060
3060
|
fromAxiosError as fromAxiosError8
|
|
3061
3061
|
} from "@hcmai/sdk";
|
|
3062
|
-
function collectYmlFiles(
|
|
3063
|
-
if (statSync(
|
|
3062
|
+
function collectYmlFiles(path9) {
|
|
3063
|
+
if (statSync(path9).isFile()) return [path9];
|
|
3064
3064
|
const out = [];
|
|
3065
3065
|
const walk = (dir) => {
|
|
3066
3066
|
for (const name of readdirSync(dir).sort()) {
|
|
@@ -3069,7 +3069,7 @@ function collectYmlFiles(path7) {
|
|
|
3069
3069
|
else if (full.endsWith(".yml") || full.endsWith(".yaml")) out.push(full);
|
|
3070
3070
|
}
|
|
3071
3071
|
};
|
|
3072
|
-
walk(
|
|
3072
|
+
walk(path9);
|
|
3073
3073
|
return out.sort();
|
|
3074
3074
|
}
|
|
3075
3075
|
var DRY_RUN_CLIENT = {
|
|
@@ -3077,9 +3077,9 @@ var DRY_RUN_CLIENT = {
|
|
|
3077
3077
|
create: async () => ({ id: "" }),
|
|
3078
3078
|
update: async () => ({})
|
|
3079
3079
|
};
|
|
3080
|
-
async function importCommand(
|
|
3080
|
+
async function importCommand(path9, args) {
|
|
3081
3081
|
try {
|
|
3082
|
-
const files = collectYmlFiles(
|
|
3082
|
+
const files = collectYmlFiles(path9);
|
|
3083
3083
|
const fixtures = files.map(
|
|
3084
3084
|
(f) => parseFixture(readFileSync2(f, "utf8"), relative(process.cwd(), f))
|
|
3085
3085
|
);
|
|
@@ -4278,10 +4278,10 @@ async function uploadCommand(filePath, args) {
|
|
|
4278
4278
|
|
|
4279
4279
|
// src/commands/skills.ts
|
|
4280
4280
|
init_exit();
|
|
4281
|
-
import { promises as
|
|
4281
|
+
import { promises as fsp2 } from "fs";
|
|
4282
4282
|
import os2 from "os";
|
|
4283
|
-
import
|
|
4284
|
-
import { createHash as
|
|
4283
|
+
import path6 from "path";
|
|
4284
|
+
import { createHash as createHash4 } from "crypto";
|
|
4285
4285
|
import {
|
|
4286
4286
|
createHttpClient as createHttpClient2,
|
|
4287
4287
|
fetchSkillCatalog,
|
|
@@ -4290,31 +4290,316 @@ import {
|
|
|
4290
4290
|
fetchSkillReference,
|
|
4291
4291
|
resolveChironBase,
|
|
4292
4292
|
assertSafeSkillId as assertSafeSkillId2,
|
|
4293
|
-
assertSafeReferencePath,
|
|
4293
|
+
assertSafeReferencePath as assertSafeReferencePath2,
|
|
4294
4294
|
normalizeReferences as normalizeReferences2,
|
|
4295
|
-
parseSkillRequirements,
|
|
4296
|
-
parseSkillFrontmatter,
|
|
4295
|
+
parseSkillRequirements as parseSkillRequirements2,
|
|
4296
|
+
parseSkillFrontmatter as parseSkillFrontmatter2,
|
|
4297
4297
|
resolveSkillInstallOrder as resolveSkillInstallOrder2,
|
|
4298
4298
|
resolveAllSkillsInstallOrder,
|
|
4299
|
-
flattenSkills,
|
|
4300
4299
|
matchSkills,
|
|
4301
4300
|
filterByStage,
|
|
4302
|
-
formatObject as
|
|
4301
|
+
formatObject as formatObject14,
|
|
4303
4302
|
fromAxiosError as fromAxiosError13,
|
|
4304
4303
|
loadEnv as loadEnv5,
|
|
4305
4304
|
loadGlobalConfig as loadGlobalConfig6,
|
|
4306
4305
|
resolveActiveEnv as resolveActiveEnv3,
|
|
4307
|
-
CliError as
|
|
4308
|
-
CliErrorCode as
|
|
4306
|
+
CliError as CliError18,
|
|
4307
|
+
CliErrorCode as CliErrorCode17
|
|
4309
4308
|
} from "@hcmai/sdk";
|
|
4310
4309
|
|
|
4311
|
-
// src/commands/skills-
|
|
4310
|
+
// src/commands/skills-catalog.ts
|
|
4311
|
+
import { flattenSkills, CliError as CliError14, CliErrorCode as CliErrorCode13 } from "@hcmai/sdk";
|
|
4312
|
+
var CLI_CATEGORY_KEY = "cli-operations";
|
|
4313
|
+
var CLI_CATEGORY_NAME = "CLI \u64CD\u4F5C";
|
|
4314
|
+
function chironEntry(skill) {
|
|
4315
|
+
return {
|
|
4316
|
+
meta: {
|
|
4317
|
+
id: skill.id,
|
|
4318
|
+
title: skill.title,
|
|
4319
|
+
description: skill.description,
|
|
4320
|
+
category: skill.category,
|
|
4321
|
+
stages: skill.stages ?? [],
|
|
4322
|
+
actionSurface: skill.actionSurface,
|
|
4323
|
+
requires: skill.requires,
|
|
4324
|
+
bytes: skill.bytes,
|
|
4325
|
+
files: skill.files
|
|
4326
|
+
},
|
|
4327
|
+
locator: { kind: "chiron", markdownUrl: skill.markdownUrl }
|
|
4328
|
+
};
|
|
4329
|
+
}
|
|
4330
|
+
function bundledEntry(entry, cliVersion) {
|
|
4331
|
+
return {
|
|
4332
|
+
meta: {
|
|
4333
|
+
id: entry.id,
|
|
4334
|
+
title: entry.title ?? entry.id,
|
|
4335
|
+
description: entry.description ?? "",
|
|
4336
|
+
category: entry.category ?? CLI_CATEGORY_KEY,
|
|
4337
|
+
stages: entry.stages ?? [],
|
|
4338
|
+
// 自带技能讲的就是本 CLI 的命令,恒为 cli。
|
|
4339
|
+
actionSurface: "cli",
|
|
4340
|
+
requires: entry.requires,
|
|
4341
|
+
bytes: entry.bytes,
|
|
4342
|
+
files: entry.files
|
|
4343
|
+
},
|
|
4344
|
+
locator: { kind: "cli", packagePath: `skills/${entry.id}`, cliVersion }
|
|
4345
|
+
};
|
|
4346
|
+
}
|
|
4347
|
+
function cliLocatorString(id, cliVersion) {
|
|
4348
|
+
return `@hcmai/cli@${cliVersion}:skills/${id}`;
|
|
4349
|
+
}
|
|
4350
|
+
var SOURCE_FILTERS = ["cli", "chiron", "all"];
|
|
4351
|
+
function parseSourceFilter(raw) {
|
|
4352
|
+
if (raw === void 0) return "all";
|
|
4353
|
+
if (SOURCE_FILTERS.includes(raw)) return raw;
|
|
4354
|
+
throw new CliError14({
|
|
4355
|
+
code: CliErrorCode13.INVALID_ARGUMENT,
|
|
4356
|
+
message: `--source \u53EA\u8BA4 ${SOURCE_FILTERS.join(" / ")}\uFF0C\u6536\u5230 "${raw}"\u3002\uFF08\`local\` \u4E0D\u662F\u53D6\u503C\uFF1A\u6CA1\u6709\u54EA\u4E00\u8D9F\u4F1A\u53BB\u300C\u626B local \u6765\u6E90\u300D\uFF0Cinstall --from \u88C5\u7684\u90A3\u4E9B\u5728\u4EFB\u4F55 --source \u4E0B\u90FD\u5355\u5217\u3001\u90FD\u4E0D\u53C2\u4E0E\u5347\u7EA7\u3002\uFF09`
|
|
4357
|
+
});
|
|
4358
|
+
}
|
|
4359
|
+
function emptyHalf() {
|
|
4360
|
+
return { status: "skipped", entries: /* @__PURE__ */ new Map() };
|
|
4361
|
+
}
|
|
4362
|
+
function buildUnifiedCatalog(input) {
|
|
4363
|
+
const chiron = emptyHalf();
|
|
4364
|
+
const cli = emptyHalf();
|
|
4365
|
+
if (input.source !== "cli" && input.chironManifest) {
|
|
4366
|
+
chiron.status = "fetched";
|
|
4367
|
+
for (const skill of flattenSkills(input.chironManifest)) chiron.entries.set(skill.id, chironEntry(skill));
|
|
4368
|
+
}
|
|
4369
|
+
if (input.source !== "chiron" && input.bundled) {
|
|
4370
|
+
cli.status = "fetched";
|
|
4371
|
+
for (const entry of input.bundled) cli.entries.set(entry.id, bundledEntry(entry, input.cliVersion));
|
|
4372
|
+
}
|
|
4373
|
+
return { chiron, cli };
|
|
4374
|
+
}
|
|
4375
|
+
function detectIdConflicts(catalog) {
|
|
4376
|
+
if (catalog.chiron.status !== "fetched" || catalog.cli.status !== "fetched") {
|
|
4377
|
+
return { checked: false, ids: [] };
|
|
4378
|
+
}
|
|
4379
|
+
const ids = [...catalog.cli.entries.keys()].filter((id) => catalog.chiron.entries.has(id)).sort();
|
|
4380
|
+
return { checked: true, ids };
|
|
4381
|
+
}
|
|
4382
|
+
function conflictMessage(ids, endpoint) {
|
|
4383
|
+
return `\u6280\u80FD id \`${ids.join("`\u3001`")}\` \u5728 CLI \u81EA\u5E26\u4E0E ${endpoint} \u7684\u777F\u620E\u76EE\u5F55\u91CC\u540C\u65F6\u5B58\u5728\u3002
|
|
4384
|
+
\u4E24\u8FB9\u5404\u81EA\u4E3B\u5F20\u7684\u4E1C\u897F\u4E0D\u540C\uFF0C\u53D6\u4EFB\u4F55\u4E00\u4EFD\u90FD\u4F1A\u8BA9\u53E6\u4E00\u4EFD\u7684\u8BFB\u8005\u62FF\u5230\u9519\u7684\u5408\u540C\u3002
|
|
4385
|
+
\u51FA\u8DEF\u53EA\u6709\u4E24\u6761\uFF1A\u2460 \u5347\u7EA7 CLI\uFF08\u4EA7\u54C1\u628A\u5B83\u6536\u7F16\u4E86\uFF0C\u65B0\u7248 CLI \u4F1A\u64A4\u6389\u81EA\u5E26\u7684\u90A3\u4EFD\uFF09\uFF1B
|
|
4386
|
+
\u2461 \u8FD9\u662F\u4E00\u6B21\u5F52\u5C5E\u672A\u5B9A\uFF0C\u628A id \u4E0E\u4E24\u5904\u51FA\u5904\u62A5\u7ED9\u4EA7\u54C1\u4FA7\u88C1\u51B3\u3002`;
|
|
4387
|
+
}
|
|
4388
|
+
function unconfirmedConflictNote(source) {
|
|
4389
|
+
return `\u672C\u8D9F\u53EA\u770B\u4E86${source === "cli" ? "CLI \u81EA\u5E26" : "\u777F\u620E"}\u6765\u6E90\uFF0C\u672A\u505A\u8DE8\u6765\u6E90\u51B2\u7A81\u68C0\u67E5`;
|
|
4390
|
+
}
|
|
4391
|
+
function classifyInstalledSource(id, record, catalog) {
|
|
4392
|
+
const kind = record.sourceKind ?? "chiron";
|
|
4393
|
+
if (kind === "local") return "local";
|
|
4394
|
+
const half = kind === "cli" ? catalog.cli : catalog.chiron;
|
|
4395
|
+
if (half.status !== "fetched") return "not-checked";
|
|
4396
|
+
if (half.entries.has(id)) return "present";
|
|
4397
|
+
return kind === "cli" ? "removed-in-cli" : "removed-upstream";
|
|
4398
|
+
}
|
|
4399
|
+
var SOURCE_STATUS_NOTE = {
|
|
4400
|
+
"removed-upstream": "\u4EA7\u54C1\u8FD9\u4E00\u7248\u7684\u6280\u80FD\u76EE\u5F55\u91CC\u5DF2\u7ECF\u6CA1\u6709\u5B83\u4E86\uFF1B\u672C\u5730\u6587\u4EF6\u4FDD\u7559\u4E0D\u52A8",
|
|
4401
|
+
"removed-in-cli": "\u8FD9\u4E00\u7248 CLI \u4E0D\u518D\u81EA\u5E26\u5B83\uFF08\u591A\u534A\u662F\u88AB\u4EA7\u54C1\u6536\u7F16\u4E86\uFF0C\u8DD1 hcm skills list \u770B\u770B\uFF09",
|
|
4402
|
+
"not-checked": "\u672C\u8F6E\u6CA1\u8FDE\u7F51\u6838\u5BF9\u5B83\u7684\u6765\u6E90\uFF08--source cli\uFF09\uFF0C\u5B83\u662F\u4E0D\u662F\u8FD8\u5728\u4EA7\u54C1\u76EE\u5F55\u91CC\u8FD9\u4E00\u8D9F\u4E0D\u77E5\u9053",
|
|
4403
|
+
local: "\u672C\u5730\u6765\u6E90\uFF08install --from \u88C5\u7684\uFF09\uFF0Cupdate \u4E0D\u7BA1\u5B83\uFF1B\u8981\u66F4\u65B0\u5C31\u518D install --from \u4E00\u6B21"
|
|
4404
|
+
};
|
|
4405
|
+
function checkCrossSourceRequires(catalog) {
|
|
4406
|
+
if (catalog.chiron.status !== "fetched" || catalog.cli.status !== "fetched") return [];
|
|
4407
|
+
const out = [];
|
|
4408
|
+
for (const [id, entry] of catalog.chiron.entries) {
|
|
4409
|
+
for (const need of entry.meta.requires ?? []) {
|
|
4410
|
+
if (catalog.chiron.entries.has(need)) continue;
|
|
4411
|
+
if (catalog.cli.entries.has(need)) {
|
|
4412
|
+
out.push({
|
|
4413
|
+
from: id,
|
|
4414
|
+
to: need,
|
|
4415
|
+
reason: "\u777F\u620E\u6280\u80FD\u4E0D\u5F97\u4F9D\u8D56 CLI \u81EA\u5E26\u6280\u80FD\uFF1A\u4EA7\u54C1\u77E5\u8BC6\u7684\u53EF\u8BFB\u6027\u4E0D\u80FD\u53D6\u51B3\u4E8E\u987E\u95EE\u88C5\u4E86\u54EA\u7248 CLI"
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
}
|
|
4419
|
+
}
|
|
4420
|
+
return out;
|
|
4421
|
+
}
|
|
4422
|
+
|
|
4423
|
+
// src/commands/skills-bundled.ts
|
|
4424
|
+
import { promises as fsp } from "fs";
|
|
4425
|
+
import * as path3 from "path";
|
|
4426
|
+
import { CliError as CliError15, CliErrorCode as CliErrorCode14, assertSafeReferencePath, parseSkillRequirements } from "@hcmai/sdk";
|
|
4427
|
+
|
|
4428
|
+
// src/commands/skills-manifest.ts
|
|
4429
|
+
import { readFileSync as readFileSync3, readdirSync as readdirSync2, statSync as statSync3, existsSync } from "fs";
|
|
4312
4430
|
import { createHash } from "crypto";
|
|
4431
|
+
import * as path2 from "path";
|
|
4432
|
+
import { fileURLToPath } from "url";
|
|
4433
|
+
import { formatObject as formatObject13, parseSkillFrontmatter } from "@hcmai/sdk";
|
|
4434
|
+
function collectCommandSurface(root) {
|
|
4435
|
+
const out = [];
|
|
4436
|
+
const walk = (cmd, prefix) => {
|
|
4437
|
+
for (const sub of cmd.commands) {
|
|
4438
|
+
const segs = [...prefix, sub.name()];
|
|
4439
|
+
const options = sub.options.map((o) => /(--[a-zA-Z0-9][\w-]*)/.exec(o.flags)?.[1] ?? "").filter((s) => s.length > 0);
|
|
4440
|
+
out.push({ path: segs.join(" "), options: [...new Set(options)].sort() });
|
|
4441
|
+
walk(sub, segs);
|
|
4442
|
+
}
|
|
4443
|
+
};
|
|
4444
|
+
walk(root, []);
|
|
4445
|
+
return out.sort((a, b) => a.path.localeCompare(b.path));
|
|
4446
|
+
}
|
|
4447
|
+
function bundledSkillsDir() {
|
|
4448
|
+
const here = path2.dirname(fileURLToPath(import.meta.url));
|
|
4449
|
+
return path2.join(here, "skills");
|
|
4450
|
+
}
|
|
4451
|
+
function sha256(buf) {
|
|
4452
|
+
return createHash("sha256").update(buf).digest("hex");
|
|
4453
|
+
}
|
|
4454
|
+
function listFiles(dir, rel = "") {
|
|
4455
|
+
const out = [];
|
|
4456
|
+
for (const name of readdirSync2(dir)) {
|
|
4457
|
+
const p = path2.join(dir, name);
|
|
4458
|
+
const r = rel ? `${rel}/${name}` : name;
|
|
4459
|
+
if (statSync3(p).isDirectory()) out.push(...listFiles(p, r));
|
|
4460
|
+
else out.push(r);
|
|
4461
|
+
}
|
|
4462
|
+
return out;
|
|
4463
|
+
}
|
|
4464
|
+
function collectBundledSkills(dir = bundledSkillsDir()) {
|
|
4465
|
+
if (!existsSync(dir)) return [];
|
|
4466
|
+
const out = [];
|
|
4467
|
+
for (const id of readdirSync2(dir).sort()) {
|
|
4468
|
+
const skillDir = path2.join(dir, id);
|
|
4469
|
+
if (!statSync3(skillDir).isDirectory()) continue;
|
|
4470
|
+
const files = listFiles(skillDir).sort();
|
|
4471
|
+
if (!files.includes("SKILL.md")) continue;
|
|
4472
|
+
let bytes = 0;
|
|
4473
|
+
const fileRows = files.map((rel) => {
|
|
4474
|
+
const buf = readFileSync3(path2.join(skillDir, rel));
|
|
4475
|
+
bytes += buf.byteLength;
|
|
4476
|
+
return { path: rel, bytes: buf.byteLength, sha256: sha256(buf) };
|
|
4477
|
+
});
|
|
4478
|
+
const body = readFileSync3(path2.join(skillDir, "SKILL.md"), "utf8");
|
|
4479
|
+
const fm = parseSkillFrontmatter(body, id);
|
|
4480
|
+
out.push({
|
|
4481
|
+
id,
|
|
4482
|
+
title: str(fm.title),
|
|
4483
|
+
description: str(fm.description),
|
|
4484
|
+
category: str(fm.category),
|
|
4485
|
+
stages: strArray(fm.stages),
|
|
4486
|
+
actionSurface: str(fm.actionSurface),
|
|
4487
|
+
requires: strArray(fm.requires),
|
|
4488
|
+
bytes,
|
|
4489
|
+
files: fileRows
|
|
4490
|
+
});
|
|
4491
|
+
}
|
|
4492
|
+
return out;
|
|
4493
|
+
}
|
|
4494
|
+
function buildManifest(root, skillsDir = bundledSkillsDir()) {
|
|
4495
|
+
return {
|
|
4496
|
+
schemaVersion: 1,
|
|
4497
|
+
cliVersion: CLI_VERSION,
|
|
4498
|
+
commands: collectCommandSurface(root),
|
|
4499
|
+
skills: collectBundledSkills(skillsDir)
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
function skillsManifestCommand(root, args) {
|
|
4503
|
+
const manifest2 = buildManifest(root);
|
|
4504
|
+
process.stdout.write(formatObject13(manifest2, { format: args.output ?? "json" }) + "\n");
|
|
4505
|
+
}
|
|
4506
|
+
function str(v) {
|
|
4507
|
+
return typeof v === "string" && v ? v : void 0;
|
|
4508
|
+
}
|
|
4509
|
+
function strArray(v) {
|
|
4510
|
+
return Array.isArray(v) ? v.map(String) : void 0;
|
|
4511
|
+
}
|
|
4512
|
+
|
|
4513
|
+
// src/commands/skills-bundled.ts
|
|
4514
|
+
function listBundledSkills(dir = bundledSkillsDir()) {
|
|
4515
|
+
return collectBundledSkills(dir);
|
|
4516
|
+
}
|
|
4517
|
+
function unreachable(id, detail) {
|
|
4518
|
+
return new CliError15({
|
|
4519
|
+
code: CliErrorCode14.SKILL_SOURCE_UNREACHABLE,
|
|
4520
|
+
message: `\u88C5\u5728\u8FD9\u4EFD CLI \u91CC\u7684\u6280\u80FD\u8D44\u6E90\u8BFB\u4E0D\u5230\uFF08${detail}\uFF09\uFF0C\u91CD\u88C5 @hcmai/cli\u3002
|
|
4521
|
+
\u6280\u80FD id\uFF1A${id}\uFF1B\u627E\u7684\u4F4D\u7F6E\uFF1A${bundledSkillsDir()}`
|
|
4522
|
+
});
|
|
4523
|
+
}
|
|
4524
|
+
async function readBundledReferences(skillDir) {
|
|
4525
|
+
const root = path3.join(skillDir, "references");
|
|
4526
|
+
const out = [];
|
|
4527
|
+
const walk = async (dir, prefix) => {
|
|
4528
|
+
let items;
|
|
4529
|
+
try {
|
|
4530
|
+
items = await fsp.readdir(dir, { withFileTypes: true });
|
|
4531
|
+
} catch (cause) {
|
|
4532
|
+
if (cause?.code === "ENOENT") return;
|
|
4533
|
+
throw cause;
|
|
4534
|
+
}
|
|
4535
|
+
for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
4536
|
+
const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
4537
|
+
if (item.isDirectory()) {
|
|
4538
|
+
await walk(path3.join(dir, item.name), relative2);
|
|
4539
|
+
continue;
|
|
4540
|
+
}
|
|
4541
|
+
if (!item.isFile()) continue;
|
|
4542
|
+
assertSafeReferencePath(relative2);
|
|
4543
|
+
out.push({ path: relative2, content: await fsp.readFile(path3.join(dir, item.name), "utf8") });
|
|
4544
|
+
}
|
|
4545
|
+
};
|
|
4546
|
+
await walk(root, "");
|
|
4547
|
+
return out;
|
|
4548
|
+
}
|
|
4549
|
+
async function readBundledSkillDocument(id, dir = bundledSkillsDir()) {
|
|
4550
|
+
const skillDir = path3.join(dir, id);
|
|
4551
|
+
let markdown;
|
|
4552
|
+
try {
|
|
4553
|
+
markdown = await fsp.readFile(path3.join(skillDir, "SKILL.md"), "utf8");
|
|
4554
|
+
} catch (cause) {
|
|
4555
|
+
if (cause?.code === "ENOENT") throw unreachable(id, "SKILL.md \u4E0D\u5B58\u5728");
|
|
4556
|
+
throw cause;
|
|
4557
|
+
}
|
|
4558
|
+
if (!markdown.trim()) throw unreachable(id, "SKILL.md \u662F\u7A7A\u7684");
|
|
4559
|
+
return {
|
|
4560
|
+
id,
|
|
4561
|
+
markdown,
|
|
4562
|
+
source: cliLocatorString(id, CLI_VERSION),
|
|
4563
|
+
sourceKind: "cli",
|
|
4564
|
+
references: await readBundledReferences(skillDir)
|
|
4565
|
+
};
|
|
4566
|
+
}
|
|
4567
|
+
async function resolveBundledClosure(targetId, dir = bundledSkillsDir()) {
|
|
4568
|
+
const documents = [];
|
|
4569
|
+
const external = /* @__PURE__ */ new Set();
|
|
4570
|
+
const state = /* @__PURE__ */ new Map();
|
|
4571
|
+
const stack = [];
|
|
4572
|
+
const available = new Set(listBundledSkills(dir).map((s) => s.id));
|
|
4573
|
+
const visit = async (id) => {
|
|
4574
|
+
if (state.get(id) === "done") return;
|
|
4575
|
+
if (state.get(id) === "visiting") {
|
|
4576
|
+
throw new CliError15({
|
|
4577
|
+
code: CliErrorCode14.INVALID_ARGUMENT,
|
|
4578
|
+
message: `\u81EA\u5E26\u6280\u80FD\u4F9D\u8D56\u6210\u73AF\uFF1A${[...stack, id].join(" \u2192 ")}`
|
|
4579
|
+
});
|
|
4580
|
+
}
|
|
4581
|
+
state.set(id, "visiting");
|
|
4582
|
+
stack.push(id);
|
|
4583
|
+
const document = await readBundledSkillDocument(id, dir);
|
|
4584
|
+
for (const need of parseSkillRequirements(document.markdown, id)) {
|
|
4585
|
+
if (available.has(need)) await visit(need);
|
|
4586
|
+
else external.add(need);
|
|
4587
|
+
}
|
|
4588
|
+
stack.pop();
|
|
4589
|
+
state.set(id, "done");
|
|
4590
|
+
documents.push(document);
|
|
4591
|
+
};
|
|
4592
|
+
await visit(targetId);
|
|
4593
|
+
return { documents, external: [...external].sort() };
|
|
4594
|
+
}
|
|
4595
|
+
|
|
4596
|
+
// src/commands/skills-sync.ts
|
|
4597
|
+
import { createHash as createHash2 } from "crypto";
|
|
4313
4598
|
import { promises as fs5 } from "fs";
|
|
4314
|
-
import
|
|
4599
|
+
import path4 from "path";
|
|
4315
4600
|
import {
|
|
4316
|
-
CliError as
|
|
4317
|
-
CliErrorCode as
|
|
4601
|
+
CliError as CliError16,
|
|
4602
|
+
CliErrorCode as CliErrorCode15,
|
|
4318
4603
|
assertSafeSkillFilePath,
|
|
4319
4604
|
assertSafeSkillId,
|
|
4320
4605
|
normalizeReferences,
|
|
@@ -4347,7 +4632,7 @@ async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fet
|
|
|
4347
4632
|
path: targetPath,
|
|
4348
4633
|
content: referenceContent,
|
|
4349
4634
|
bytes: referenceContent.length,
|
|
4350
|
-
sha256:
|
|
4635
|
+
sha256: sha2562(referenceContent)
|
|
4351
4636
|
};
|
|
4352
4637
|
})
|
|
4353
4638
|
);
|
|
@@ -4356,7 +4641,7 @@ async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fet
|
|
|
4356
4641
|
path: "SKILL.md",
|
|
4357
4642
|
content,
|
|
4358
4643
|
bytes: content.length,
|
|
4359
|
-
sha256:
|
|
4644
|
+
sha256: sha2562(content)
|
|
4360
4645
|
},
|
|
4361
4646
|
...referenceFiles
|
|
4362
4647
|
];
|
|
@@ -4384,7 +4669,7 @@ async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fet
|
|
|
4384
4669
|
`\u6280\u80FD ${skill.id}/${file.path} \u5B57\u8282\u6570\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4\uFF1A\u76EE\u5F55=${file.bytes}\uFF0C\u5B9E\u9645=${content.length}`
|
|
4385
4670
|
);
|
|
4386
4671
|
}
|
|
4387
|
-
const digest =
|
|
4672
|
+
const digest = sha2562(content);
|
|
4388
4673
|
if (digest !== file.sha256) {
|
|
4389
4674
|
throw invalid(`\u6280\u80FD ${skill.id}/${file.path} \u6458\u8981\u4E0E\u76EE\u5F55\u4E0D\u4E00\u81F4`);
|
|
4390
4675
|
}
|
|
@@ -4406,10 +4691,10 @@ async function prepareSkillDocuments(catalog, requestedSkill, fetchMarkdown, fet
|
|
|
4406
4691
|
);
|
|
4407
4692
|
}
|
|
4408
4693
|
async function syncWorkBuddyProject(input) {
|
|
4409
|
-
const project =
|
|
4410
|
-
const metaDir =
|
|
4411
|
-
const skillsDir =
|
|
4412
|
-
const lockPath =
|
|
4694
|
+
const project = path4.resolve(input.project);
|
|
4695
|
+
const metaDir = path4.join(project, ".hcm-delivery");
|
|
4696
|
+
const skillsDir = path4.join(project, ".codebuddy", "skills");
|
|
4697
|
+
const lockPath = path4.join(metaDir, "chiron.lock.json");
|
|
4413
4698
|
assertSafeSkillId(input.requestedSkill);
|
|
4414
4699
|
validatePreparedDocuments(input.documents, input.requestedSkill);
|
|
4415
4700
|
validatePreparedRuntime(input.runtime);
|
|
@@ -4429,27 +4714,27 @@ async function syncWorkBuddyProject(input) {
|
|
|
4429
4714
|
const oldDigestByFile = lockDigests(oldLock);
|
|
4430
4715
|
for (const file of oldOwned) validateOwnedFile(file);
|
|
4431
4716
|
for (const file of desiredFiles) {
|
|
4432
|
-
const absolute =
|
|
4717
|
+
const absolute = path4.join(project, file);
|
|
4433
4718
|
const current = await readOptionalBytes(absolute);
|
|
4434
4719
|
if (current !== void 0 && !oldOwned.has(file)) {
|
|
4435
4720
|
throw invalid(`\u76EE\u6807\u6587\u4EF6\u4E0D\u5C5E\u4E8E\u4E0A\u4E00\u4EFD Chiron \u9501\uFF0C\u62D2\u7EDD\u8986\u76D6\u987E\u95EE\u6587\u4EF6\uFF1A${file}`);
|
|
4436
4721
|
}
|
|
4437
4722
|
}
|
|
4438
4723
|
for (const file of oldOwned) {
|
|
4439
|
-
const absolute =
|
|
4724
|
+
const absolute = path4.join(project, file);
|
|
4440
4725
|
const current = await readOptionalBytes(absolute);
|
|
4441
4726
|
if (current === void 0) continue;
|
|
4442
4727
|
const expected = oldDigestByFile.get(file);
|
|
4443
|
-
if (!expected ||
|
|
4728
|
+
if (!expected || sha2562(current) !== expected.sha256 || !await fileModeMatches(absolute, expected.mode)) {
|
|
4444
4729
|
throw invalid(`\u4E0A\u4E00\u4EFD\u9501\u62E5\u6709\u7684\u751F\u6210\u6587\u4EF6\u5DF2\u88AB\u4EBA\u5DE5\u4FEE\u6539\uFF0C\u62D2\u7EDD\u8986\u76D6\u6216\u6E05\u7406\uFF1A${file}`);
|
|
4445
4730
|
}
|
|
4446
4731
|
}
|
|
4447
4732
|
const newLock = {
|
|
4448
4733
|
schemaVersion: 1,
|
|
4449
4734
|
source: {
|
|
4450
|
-
endpointOriginSha256:
|
|
4735
|
+
endpointOriginSha256: sha2562(normalizeEndpoint(input.endpoint)),
|
|
4451
4736
|
catalogPath: "/skills.json",
|
|
4452
|
-
catalogSha256:
|
|
4737
|
+
catalogSha256: sha2562(stableJson2(input.catalog))
|
|
4453
4738
|
},
|
|
4454
4739
|
selection: { requestedSkill: input.requestedSkill },
|
|
4455
4740
|
resolvedSkills: input.documents.map(({ id, bytes, sha256: digest, files: skillFiles }) => ({
|
|
@@ -4481,16 +4766,16 @@ async function syncWorkBuddyProject(input) {
|
|
|
4481
4766
|
}
|
|
4482
4767
|
const obsolete = [...oldOwned].filter((file) => !desiredByFile.has(file));
|
|
4483
4768
|
await fs5.mkdir(metaDir, { recursive: true });
|
|
4484
|
-
const staging = await fs5.mkdtemp(
|
|
4485
|
-
const stagedFiles =
|
|
4486
|
-
const backups =
|
|
4487
|
-
const stagedLock =
|
|
4488
|
-
const journalPath =
|
|
4769
|
+
const staging = await fs5.mkdtemp(path4.join(metaDir, ".staging-"));
|
|
4770
|
+
const stagedFiles = path4.join(staging, "new");
|
|
4771
|
+
const backups = path4.join(staging, "backup");
|
|
4772
|
+
const stagedLock = path4.join(staging, "chiron.lock.json");
|
|
4773
|
+
const journalPath = path4.join(staging, "journal.json");
|
|
4489
4774
|
let journalReady = false;
|
|
4490
4775
|
try {
|
|
4491
4776
|
for (const [file, preparedFile2] of desiredByFile) {
|
|
4492
|
-
const staged =
|
|
4493
|
-
await fs5.mkdir(
|
|
4777
|
+
const staged = path4.join(stagedFiles, file);
|
|
4778
|
+
await fs5.mkdir(path4.dirname(staged), { recursive: true });
|
|
4494
4779
|
await fs5.writeFile(
|
|
4495
4780
|
staged,
|
|
4496
4781
|
preparedFile2.content,
|
|
@@ -4501,16 +4786,16 @@ async function syncWorkBuddyProject(input) {
|
|
|
4501
4786
|
`, "utf8");
|
|
4502
4787
|
const affected = [.../* @__PURE__ */ new Set([...desiredFiles, ...obsolete])];
|
|
4503
4788
|
for (const file of affected) {
|
|
4504
|
-
const target =
|
|
4789
|
+
const target = path4.join(project, file);
|
|
4505
4790
|
if (!await exists(target)) continue;
|
|
4506
|
-
const backup =
|
|
4507
|
-
await fs5.mkdir(
|
|
4791
|
+
const backup = path4.join(backups, file);
|
|
4792
|
+
await fs5.mkdir(path4.dirname(backup), { recursive: true });
|
|
4508
4793
|
await fs5.copyFile(target, backup);
|
|
4509
4794
|
}
|
|
4510
4795
|
const oldLockExisted = await exists(lockPath);
|
|
4511
4796
|
if (oldLockExisted) {
|
|
4512
|
-
const backupLock =
|
|
4513
|
-
await fs5.mkdir(
|
|
4797
|
+
const backupLock = path4.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4798
|
+
await fs5.mkdir(path4.dirname(backupLock), { recursive: true });
|
|
4514
4799
|
await fs5.copyFile(lockPath, backupLock);
|
|
4515
4800
|
}
|
|
4516
4801
|
const journal = {
|
|
@@ -4523,12 +4808,12 @@ async function syncWorkBuddyProject(input) {
|
|
|
4523
4808
|
`, "utf8");
|
|
4524
4809
|
journalReady = true;
|
|
4525
4810
|
for (const file of desiredFiles) {
|
|
4526
|
-
const target =
|
|
4527
|
-
await fs5.mkdir(
|
|
4811
|
+
const target = path4.join(project, file);
|
|
4812
|
+
await fs5.mkdir(path4.dirname(target), { recursive: true });
|
|
4528
4813
|
await fs5.rm(target, { force: true });
|
|
4529
|
-
await fs5.rename(
|
|
4814
|
+
await fs5.rename(path4.join(stagedFiles, file), target);
|
|
4530
4815
|
}
|
|
4531
|
-
for (const file of obsolete) await fs5.unlink(
|
|
4816
|
+
for (const file of obsolete) await fs5.unlink(path4.join(project, file)).catch(ignoreMissing);
|
|
4532
4817
|
await fs5.rm(lockPath, { force: true });
|
|
4533
4818
|
await fs5.rename(stagedLock, lockPath);
|
|
4534
4819
|
await fs5.unlink(journalPath);
|
|
@@ -4559,32 +4844,32 @@ async function recoverInterruptedSyncs(project, metaDir, lockPath) {
|
|
|
4559
4844
|
}
|
|
4560
4845
|
for (const entry of entries) {
|
|
4561
4846
|
if (!entry.isDirectory() || !entry.name.startsWith(".staging-")) continue;
|
|
4562
|
-
const staging =
|
|
4563
|
-
if (await exists(
|
|
4847
|
+
const staging = path4.join(metaDir, entry.name);
|
|
4848
|
+
if (await exists(path4.join(staging, "journal.json"))) {
|
|
4564
4849
|
await recoverStaging(project, staging, lockPath);
|
|
4565
4850
|
}
|
|
4566
4851
|
await fs5.rm(staging, { recursive: true, force: true });
|
|
4567
4852
|
}
|
|
4568
4853
|
}
|
|
4569
4854
|
async function recoverStaging(project, staging, lockPath) {
|
|
4570
|
-
const journalPath =
|
|
4855
|
+
const journalPath = path4.join(staging, "journal.json");
|
|
4571
4856
|
const journal = parseJournal(await fs5.readFile(journalPath, "utf8"), project);
|
|
4572
|
-
const backups =
|
|
4857
|
+
const backups = path4.join(staging, "backup");
|
|
4573
4858
|
for (const file of journal.affectedFiles) {
|
|
4574
|
-
const target =
|
|
4575
|
-
const backup =
|
|
4859
|
+
const target = path4.join(project, file);
|
|
4860
|
+
const backup = path4.join(backups, file);
|
|
4576
4861
|
if (await exists(backup)) {
|
|
4577
|
-
await fs5.mkdir(
|
|
4862
|
+
await fs5.mkdir(path4.dirname(target), { recursive: true });
|
|
4578
4863
|
await fs5.rm(target, { force: true });
|
|
4579
4864
|
await fs5.rename(backup, target);
|
|
4580
4865
|
} else {
|
|
4581
4866
|
await fs5.rm(target, { force: true });
|
|
4582
4867
|
}
|
|
4583
4868
|
}
|
|
4584
|
-
const backupLock =
|
|
4869
|
+
const backupLock = path4.join(backups, ".hcm-delivery", "chiron.lock.json");
|
|
4585
4870
|
if (journal.oldLockExisted) {
|
|
4586
4871
|
if (!await exists(backupLock)) throw invalid(`\u540C\u6B65\u6062\u590D\u7F3A\u5C11\u65E7\u9501\u5907\u4EFD\uFF1A${backupLock}`);
|
|
4587
|
-
await fs5.mkdir(
|
|
4872
|
+
await fs5.mkdir(path4.dirname(lockPath), { recursive: true });
|
|
4588
4873
|
await fs5.rm(lockPath, { force: true });
|
|
4589
4874
|
await fs5.rename(backupLock, lockPath);
|
|
4590
4875
|
} else {
|
|
@@ -4596,8 +4881,8 @@ function parseJournal(content, project) {
|
|
|
4596
4881
|
try {
|
|
4597
4882
|
journal = JSON.parse(content);
|
|
4598
4883
|
} catch (cause) {
|
|
4599
|
-
throw new
|
|
4600
|
-
code:
|
|
4884
|
+
throw new CliError16({
|
|
4885
|
+
code: CliErrorCode15.CONFIG_PARSE_ERROR,
|
|
4601
4886
|
message: "Chiron \u540C\u6B65\u6062\u590D\u65E5\u5FD7\u635F\u574F",
|
|
4602
4887
|
cause
|
|
4603
4888
|
});
|
|
@@ -4620,7 +4905,7 @@ function validatePreparedDocuments(documents, requestedSkill) {
|
|
|
4620
4905
|
assertSafeSkillFilePath(file.path);
|
|
4621
4906
|
if (paths.has(file.path)) throw invalid(`\u6280\u80FD ${document.id} \u5305\u542B\u91CD\u590D\u6587\u4EF6: ${file.path}`);
|
|
4622
4907
|
paths.add(file.path);
|
|
4623
|
-
if (file.content.length !== file.bytes ||
|
|
4908
|
+
if (file.content.length !== file.bytes || sha2562(file.content) !== file.sha256) {
|
|
4624
4909
|
throw invalid(`\u6280\u80FD ${document.id}/${file.path} \u7684\u5DF2\u51C6\u5907\u5185\u5BB9\u6458\u8981\u4E0D\u4E00\u81F4`);
|
|
4625
4910
|
}
|
|
4626
4911
|
}
|
|
@@ -4642,7 +4927,7 @@ function validatePreparedRuntime(runtime) {
|
|
|
4642
4927
|
}
|
|
4643
4928
|
capabilities.add(capability);
|
|
4644
4929
|
}
|
|
4645
|
-
if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !==
|
|
4930
|
+
if (!validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha2562(`${runtime.capabilities.join("\n")}
|
|
4646
4931
|
`)) {
|
|
4647
4932
|
throw invalid("WorkBuddy CLI \u80FD\u529B\u6458\u8981\u4E0D\u4E00\u81F4");
|
|
4648
4933
|
}
|
|
@@ -4667,7 +4952,7 @@ function validatePreparedRuntime(runtime) {
|
|
|
4667
4952
|
}
|
|
4668
4953
|
function validateRuntimeFile(file) {
|
|
4669
4954
|
validateOwnedFile(file.path);
|
|
4670
|
-
if (file.content.length !== file.bytes ||
|
|
4955
|
+
if (file.content.length !== file.bytes || sha2562(file.content) !== file.sha256 || !Number.isInteger(file.mode) || file.mode < 0 || file.mode > 511) {
|
|
4671
4956
|
throw invalid(`WorkBuddy \u8FD0\u884C\u5165\u53E3\u6458\u8981\u6216\u6743\u9650\u4E0D\u4E00\u81F4\uFF1A${file.path}`);
|
|
4672
4957
|
}
|
|
4673
4958
|
}
|
|
@@ -4712,15 +4997,15 @@ async function readLock(lockPath) {
|
|
|
4712
4997
|
for (const file of lock.ownedFiles) validateOwnedFile(file);
|
|
4713
4998
|
return lock;
|
|
4714
4999
|
} catch (cause) {
|
|
4715
|
-
throw new
|
|
4716
|
-
code:
|
|
5000
|
+
throw new CliError16({
|
|
5001
|
+
code: CliErrorCode15.CONFIG_PARSE_ERROR,
|
|
4717
5002
|
message: `Chiron \u9501\u6587\u4EF6\u635F\u574F\uFF0C\u62D2\u7EDD\u731C\u6D4B\u6587\u4EF6\u6240\u6709\u6743\uFF1A${lockPath}`,
|
|
4718
5003
|
cause
|
|
4719
5004
|
});
|
|
4720
5005
|
}
|
|
4721
5006
|
}
|
|
4722
5007
|
function validateLockedRuntime(runtime) {
|
|
4723
|
-
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !==
|
|
5008
|
+
if (!Array.isArray(runtime.capabilities) || runtime.capabilities.length === 0 || !validDigest(runtime.capabilitiesSha256) || runtime.capabilitiesSha256 !== sha2562(`${runtime.capabilities.join("\n")}
|
|
4724
5009
|
`) || !runtime.launch || !validDigest(runtime.launch.executableSha256) || !Array.isArray(runtime.files) || runtime.files.length !== 2) {
|
|
4725
5010
|
throw new Error("invalid runtime");
|
|
4726
5011
|
}
|
|
@@ -4754,9 +5039,9 @@ function sameLockContent(left, right) {
|
|
|
4754
5039
|
}
|
|
4755
5040
|
async function desiredFilesMatch(project, desired) {
|
|
4756
5041
|
for (const [file, preparedFile2] of desired) {
|
|
4757
|
-
const absolute =
|
|
5042
|
+
const absolute = path4.join(project, file);
|
|
4758
5043
|
const current = await readOptionalBytes(absolute);
|
|
4759
|
-
if (current === void 0 ||
|
|
5044
|
+
if (current === void 0 || sha2562(current) !== preparedFile2.sha256 || !await fileModeMatches(absolute, preparedFile2.mode)) {
|
|
4760
5045
|
return false;
|
|
4761
5046
|
}
|
|
4762
5047
|
}
|
|
@@ -4770,7 +5055,7 @@ function result(changed, input, project, skillsDir, lockPath, installed, removed
|
|
|
4770
5055
|
project,
|
|
4771
5056
|
skillsDir,
|
|
4772
5057
|
lockPath,
|
|
4773
|
-
runnerPath:
|
|
5058
|
+
runnerPath: path4.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
4774
5059
|
capabilitiesSha256: input.runtime.capabilitiesSha256,
|
|
4775
5060
|
installed,
|
|
4776
5061
|
removed
|
|
@@ -4805,8 +5090,8 @@ function normalizeEndpoint(endpoint) {
|
|
|
4805
5090
|
try {
|
|
4806
5091
|
url = new URL(endpoint);
|
|
4807
5092
|
} catch (cause) {
|
|
4808
|
-
throw new
|
|
4809
|
-
code:
|
|
5093
|
+
throw new CliError16({
|
|
5094
|
+
code: CliErrorCode15.INVALID_ARGUMENT,
|
|
4810
5095
|
message: `\u975E\u6CD5 endpoint\uFF1A${endpoint}`,
|
|
4811
5096
|
cause
|
|
4812
5097
|
});
|
|
@@ -4818,8 +5103,8 @@ function normalizeEndpoint(endpoint) {
|
|
|
4818
5103
|
url.pathname = url.pathname.replace(/\/+$/, "");
|
|
4819
5104
|
return url.toString().replace(/\/$/, "");
|
|
4820
5105
|
}
|
|
4821
|
-
function
|
|
4822
|
-
return
|
|
5106
|
+
function sha2562(value) {
|
|
5107
|
+
return createHash2("sha256").update(value).digest("hex");
|
|
4823
5108
|
}
|
|
4824
5109
|
function stableJson2(value) {
|
|
4825
5110
|
if (Array.isArray(value)) return `[${value.map(stableJson2).join(",")}]`;
|
|
@@ -4882,16 +5167,16 @@ function ignoreMissing(cause) {
|
|
|
4882
5167
|
if (cause.code !== "ENOENT") throw cause;
|
|
4883
5168
|
}
|
|
4884
5169
|
function invalid(message) {
|
|
4885
|
-
return new
|
|
5170
|
+
return new CliError16({ code: CliErrorCode15.INVALID_ARGUMENT, message });
|
|
4886
5171
|
}
|
|
4887
5172
|
|
|
4888
5173
|
// src/commands/skills-runtime.ts
|
|
4889
5174
|
import { execFile } from "child_process";
|
|
4890
|
-
import { createHash as
|
|
5175
|
+
import { createHash as createHash3 } from "crypto";
|
|
4891
5176
|
import { createReadStream, promises as fs6 } from "fs";
|
|
4892
|
-
import
|
|
5177
|
+
import path5 from "path";
|
|
4893
5178
|
import { promisify } from "util";
|
|
4894
|
-
import { CliError as
|
|
5179
|
+
import { CliError as CliError17, CliErrorCode as CliErrorCode16 } from "@hcmai/sdk";
|
|
4895
5180
|
var execFileAsync = promisify(execFile);
|
|
4896
5181
|
var REQUIRED_CAPABILITIES = [
|
|
4897
5182
|
"hcm.help.skills",
|
|
@@ -4928,7 +5213,7 @@ async function prepareWorkBuddyRuntime(input = {}) {
|
|
|
4928
5213
|
}
|
|
4929
5214
|
}
|
|
4930
5215
|
const capabilities = [...REQUIRED_CAPABILITIES];
|
|
4931
|
-
const capabilitiesSha256 =
|
|
5216
|
+
const capabilitiesSha256 = sha2563(`${capabilities.join("\n")}
|
|
4932
5217
|
`);
|
|
4933
5218
|
const executableSha256 = await sha256File(launch.executable);
|
|
4934
5219
|
const launchRecord = launch.kind === "node-entry" ? {
|
|
@@ -4949,7 +5234,7 @@ async function prepareWorkBuddyRuntime(input = {}) {
|
|
|
4949
5234
|
};
|
|
4950
5235
|
}
|
|
4951
5236
|
async function resolveLaunch(candidate, nodeExecutable) {
|
|
4952
|
-
const requested =
|
|
5237
|
+
const requested = path5.resolve(candidate);
|
|
4953
5238
|
const resolvedCandidate = await realFile(requested, "\u5019\u9009 HCM CLI");
|
|
4954
5239
|
const header = (await readPrefix(resolvedCandidate, 256)).toString("utf8");
|
|
4955
5240
|
const nodeEntry = /^(?:#![^\r\n]*\bnode\b)|\.(?:c|m)?js$/i.test(
|
|
@@ -4958,7 +5243,7 @@ async function resolveLaunch(candidate, nodeExecutable) {
|
|
|
4958
5243
|
if (nodeEntry) {
|
|
4959
5244
|
return {
|
|
4960
5245
|
kind: "node-entry",
|
|
4961
|
-
executable: await realFile(
|
|
5246
|
+
executable: await realFile(path5.resolve(nodeExecutable), "Node \u53EF\u6267\u884C\u6587\u4EF6"),
|
|
4962
5247
|
entry: resolvedCandidate
|
|
4963
5248
|
};
|
|
4964
5249
|
}
|
|
@@ -4971,15 +5256,15 @@ async function realFile(file, label) {
|
|
|
4971
5256
|
if (!stat.isFile()) throw new Error("not a regular file");
|
|
4972
5257
|
return resolved;
|
|
4973
5258
|
} catch (cause) {
|
|
4974
|
-
throw new
|
|
4975
|
-
code:
|
|
5259
|
+
throw new CliError17({
|
|
5260
|
+
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
4976
5261
|
message: `${label}\u4E0D\u53EF\u7528\uFF1A${file}`,
|
|
4977
5262
|
cause
|
|
4978
5263
|
});
|
|
4979
5264
|
}
|
|
4980
5265
|
}
|
|
4981
5266
|
async function comparablePath(file) {
|
|
4982
|
-
const absolute =
|
|
5267
|
+
const absolute = path5.resolve(file);
|
|
4983
5268
|
return fs6.realpath(absolute).catch(() => absolute);
|
|
4984
5269
|
}
|
|
4985
5270
|
async function launchIdentity(launch) {
|
|
@@ -5025,8 +5310,8 @@ async function runProbe(launch, args, timeoutMs = 5e3) {
|
|
|
5025
5310
|
return `${stdout}
|
|
5026
5311
|
${stderr}`;
|
|
5027
5312
|
} catch (cause) {
|
|
5028
|
-
throw new
|
|
5029
|
-
code:
|
|
5313
|
+
throw new CliError17({
|
|
5314
|
+
code: CliErrorCode16.INVALID_ARGUMENT,
|
|
5030
5315
|
message: `\u5019\u9009 HCM CLI \u80FD\u529B\u63A2\u9488\u5931\u8D25\uFF1Ahcm ${args.join(" ")}`,
|
|
5031
5316
|
cause
|
|
5032
5317
|
});
|
|
@@ -5091,10 +5376,10 @@ function cmdQuote(value) {
|
|
|
5091
5376
|
return `"${value.replace(/%/g, "%%")}"`;
|
|
5092
5377
|
}
|
|
5093
5378
|
function preparedFile(filePath, content, mode) {
|
|
5094
|
-
return { path: filePath, content, bytes: content.length, sha256:
|
|
5379
|
+
return { path: filePath, content, bytes: content.length, sha256: sha2563(content), mode };
|
|
5095
5380
|
}
|
|
5096
5381
|
async function sha256File(file) {
|
|
5097
|
-
const hash =
|
|
5382
|
+
const hash = createHash3("sha256");
|
|
5098
5383
|
await new Promise((resolve3, reject) => {
|
|
5099
5384
|
const stream = createReadStream(file);
|
|
5100
5385
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
@@ -5103,17 +5388,29 @@ async function sha256File(file) {
|
|
|
5103
5388
|
});
|
|
5104
5389
|
return hash.digest("hex");
|
|
5105
5390
|
}
|
|
5106
|
-
function
|
|
5107
|
-
return
|
|
5391
|
+
function sha2563(value) {
|
|
5392
|
+
return createHash3("sha256").update(value).digest("hex");
|
|
5108
5393
|
}
|
|
5109
5394
|
function invalid2(message) {
|
|
5110
|
-
return new
|
|
5395
|
+
return new CliError17({ code: CliErrorCode16.INVALID_ARGUMENT, message });
|
|
5111
5396
|
}
|
|
5112
5397
|
|
|
5113
5398
|
// src/commands/skills.ts
|
|
5114
5399
|
var SKILLS_STATE_FILE = ".hcm-skills.json";
|
|
5115
|
-
function
|
|
5116
|
-
|
|
5400
|
+
function migrateSkillsState(state) {
|
|
5401
|
+
const skills2 = {};
|
|
5402
|
+
for (const [id, record] of Object.entries(state.skills ?? {})) {
|
|
5403
|
+
if (record.sourceKind) {
|
|
5404
|
+
skills2[id] = record;
|
|
5405
|
+
continue;
|
|
5406
|
+
}
|
|
5407
|
+
const kind = /^https?:\/\//i.test(record.source ?? "") ? "chiron" : "local";
|
|
5408
|
+
skills2[id] = { ...record, sourceKind: kind, sourceLocator: record.source };
|
|
5409
|
+
}
|
|
5410
|
+
return { ...state, skills: skills2 };
|
|
5411
|
+
}
|
|
5412
|
+
function sha2564(text) {
|
|
5413
|
+
return createHash4("sha256").update(text, "utf8").digest("hex");
|
|
5117
5414
|
}
|
|
5118
5415
|
async function resolveEndpoint(args) {
|
|
5119
5416
|
if (args.endpoint) return args.endpoint.replace(/\/+$/, "");
|
|
@@ -5121,8 +5418,8 @@ async function resolveEndpoint(args) {
|
|
|
5121
5418
|
const envName = resolveActiveEnv3({ envFlag: args.env }, process.env, g);
|
|
5122
5419
|
const env2 = await loadEnv5(envName).catch(() => null);
|
|
5123
5420
|
if (!env2?.endpoint) {
|
|
5124
|
-
throw new
|
|
5125
|
-
code:
|
|
5421
|
+
throw new CliError18({
|
|
5422
|
+
code: CliErrorCode17.MISSING_FLAG,
|
|
5126
5423
|
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"
|
|
5127
5424
|
});
|
|
5128
5425
|
}
|
|
@@ -5141,8 +5438,8 @@ function decideTrackedEndpoint(options) {
|
|
|
5141
5438
|
}
|
|
5142
5439
|
if (!recorded) {
|
|
5143
5440
|
if (!current) {
|
|
5144
|
-
throw new
|
|
5145
|
-
code:
|
|
5441
|
+
throw new CliError18({
|
|
5442
|
+
code: CliErrorCode17.MISSING_FLAG,
|
|
5146
5443
|
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"
|
|
5147
5444
|
});
|
|
5148
5445
|
}
|
|
@@ -5159,32 +5456,32 @@ function decideTrackedEndpoint(options) {
|
|
|
5159
5456
|
}
|
|
5160
5457
|
async function exists2(target) {
|
|
5161
5458
|
try {
|
|
5162
|
-
await
|
|
5459
|
+
await fsp2.stat(target);
|
|
5163
5460
|
return true;
|
|
5164
5461
|
} catch {
|
|
5165
5462
|
return false;
|
|
5166
5463
|
}
|
|
5167
5464
|
}
|
|
5168
5465
|
async function detectTarget(cwd) {
|
|
5169
|
-
if (await exists2(
|
|
5170
|
-
if (await exists2(
|
|
5171
|
-
if (await exists2(
|
|
5466
|
+
if (await exists2(path6.join(cwd, ".claude"))) return "claude";
|
|
5467
|
+
if (await exists2(path6.join(cwd, "AGENTS.md"))) return "codex";
|
|
5468
|
+
if (await exists2(path6.join(cwd, ".codex"))) return "codex";
|
|
5172
5469
|
return void 0;
|
|
5173
5470
|
}
|
|
5174
5471
|
async function resolveInstallLayout(args, cwd = process.cwd()) {
|
|
5175
5472
|
const requested = args.target && args.target !== "auto" ? args.target : void 0;
|
|
5176
5473
|
const target = requested ?? await detectTarget(cwd) ?? "claude";
|
|
5177
5474
|
if (target === "codex") {
|
|
5178
|
-
const root = args.global ?
|
|
5475
|
+
const root = args.global ? path6.join(os2.homedir(), ".codex") : cwd;
|
|
5179
5476
|
return {
|
|
5180
5477
|
target,
|
|
5181
|
-
baseDir: args.dir ?
|
|
5182
|
-
agentsFile:
|
|
5478
|
+
baseDir: args.dir ? path6.resolve(args.dir) : path6.join(root, "hcm-skills"),
|
|
5479
|
+
agentsFile: path6.join(root, "AGENTS.md")
|
|
5183
5480
|
};
|
|
5184
5481
|
}
|
|
5185
5482
|
return {
|
|
5186
5483
|
target,
|
|
5187
|
-
baseDir: args.dir ?
|
|
5484
|
+
baseDir: args.dir ? path6.resolve(args.dir) : args.global ? path6.join(os2.homedir(), ".claude", "skills") : path6.join(cwd, ".claude", "skills")
|
|
5188
5485
|
};
|
|
5189
5486
|
}
|
|
5190
5487
|
async function chironClient(args) {
|
|
@@ -5209,10 +5506,41 @@ function fail3(e, args) {
|
|
|
5209
5506
|
async function skillsListCommand(keyword, args) {
|
|
5210
5507
|
try {
|
|
5211
5508
|
if (args.installed) return await listInstalledSkills(keyword, args);
|
|
5212
|
-
const
|
|
5213
|
-
|
|
5509
|
+
const source = parseSourceFilter(args.source);
|
|
5510
|
+
let manifest2;
|
|
5511
|
+
let endpoint;
|
|
5512
|
+
if (source !== "cli") {
|
|
5513
|
+
const client3 = await chironClient(args);
|
|
5514
|
+
endpoint = client3.endpoint;
|
|
5515
|
+
manifest2 = await fetchSkillCatalog(client3.http, client3.base);
|
|
5516
|
+
}
|
|
5517
|
+
const bundled = source === "chiron" ? void 0 : listBundledSkills();
|
|
5518
|
+
const catalog = buildUnifiedCatalog({ source, chironManifest: manifest2, bundled, cliVersion: CLI_VERSION });
|
|
5519
|
+
const conflicts = detectIdConflicts(catalog);
|
|
5520
|
+
if (conflicts.ids.length > 0) {
|
|
5521
|
+
throw new CliError18({
|
|
5522
|
+
code: CliErrorCode17.SKILL_ID_CONFLICT,
|
|
5523
|
+
message: conflictMessage(conflicts.ids, endpoint ?? "(\u672A\u77E5\u73AF\u5883)")
|
|
5524
|
+
});
|
|
5525
|
+
}
|
|
5214
5526
|
const fmt = args.output ?? "table";
|
|
5215
|
-
|
|
5527
|
+
const cliCategory = catalog.cli.entries.size > 0 ? {
|
|
5528
|
+
key: CLI_CATEGORY_KEY,
|
|
5529
|
+
name: CLI_CATEGORY_NAME,
|
|
5530
|
+
description: "\u968F\u8FD9\u4EFD CLI \u53D1\u5E03\u7684\u6280\u80FD\uFF0C\u8BB2\u7684\u662F\u672C CLI \u7684\u547D\u4EE4\uFF1B\u5347\u7EA7 CLI \u5C31\u662F\u5347\u7EA7\u5B83\u4EEC",
|
|
5531
|
+
skills: [...catalog.cli.entries.values()].map((entry) => ({
|
|
5532
|
+
id: entry.meta.id,
|
|
5533
|
+
title: entry.meta.title,
|
|
5534
|
+
category: CLI_CATEGORY_KEY,
|
|
5535
|
+
actionSurface: "cli",
|
|
5536
|
+
description: entry.meta.description,
|
|
5537
|
+
stages: entry.meta.stages,
|
|
5538
|
+
requires: entry.meta.requires,
|
|
5539
|
+
bytes: entry.meta.bytes,
|
|
5540
|
+
markdownUrl: ""
|
|
5541
|
+
}))
|
|
5542
|
+
} : void 0;
|
|
5543
|
+
let cats = [...manifest2?.categories ?? [], ...cliCategory ? [cliCategory] : []];
|
|
5216
5544
|
if (args.category) cats = cats.filter((c) => c.key === args.category);
|
|
5217
5545
|
if (keyword) {
|
|
5218
5546
|
cats = cats.map((c) => ({ ...c, skills: matchSkills(c.skills ?? [], keyword) })).filter((c) => c.skills.length > 0);
|
|
@@ -5221,21 +5549,41 @@ async function skillsListCommand(keyword, args) {
|
|
|
5221
5549
|
cats = cats.map((c) => ({ ...c, skills: filterByStage(c.skills ?? [], args.stage) })).filter((c) => c.skills.length > 0);
|
|
5222
5550
|
}
|
|
5223
5551
|
if (fmt === "json" || fmt === "yaml") {
|
|
5224
|
-
process.stdout.write(
|
|
5552
|
+
process.stdout.write(
|
|
5553
|
+
formatObject14(
|
|
5554
|
+
{
|
|
5555
|
+
...manifest2 ?? {},
|
|
5556
|
+
categories: cats,
|
|
5557
|
+
sources: {
|
|
5558
|
+
cli: { status: catalog.cli.status, count: catalog.cli.entries.size },
|
|
5559
|
+
chiron: { status: catalog.chiron.status, count: catalog.chiron.entries.size }
|
|
5560
|
+
},
|
|
5561
|
+
conflictCheck: conflicts.checked ? { checked: true } : { checked: false, note: unconfirmedConflictNote(source) }
|
|
5562
|
+
},
|
|
5563
|
+
{ format: fmt }
|
|
5564
|
+
) + "\n"
|
|
5565
|
+
);
|
|
5225
5566
|
process.exit(0);
|
|
5226
5567
|
}
|
|
5227
5568
|
const shown = cats.flatMap((c) => c.skills);
|
|
5569
|
+
const total = (manifest2?.skillCount ?? 0) + catalog.cli.entries.size;
|
|
5228
5570
|
if (shown.length === 0) {
|
|
5229
|
-
process.stdout.write(`\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\uFF08
|
|
5571
|
+
process.stdout.write(`\u6CA1\u6709\u5339\u914D\u7684\u6280\u80FD\uFF08\u8FD9\u4E00\u8D9F\u770B\u7684\u6765\u6E90\u5171 ${total} \u4E2A\uFF09
|
|
5230
5572
|
`);
|
|
5231
5573
|
process.exit(0);
|
|
5232
5574
|
}
|
|
5233
|
-
process.stdout.write(`${manifest2
|
|
5575
|
+
process.stdout.write(`${manifest2?.title ?? "HCM \u6280\u80FD"} \u2014 ${endpoint ?? `@hcmai/cli@${CLI_VERSION}`}
|
|
5234
5576
|
|
|
5235
5577
|
`);
|
|
5236
5578
|
if (args.byStage) {
|
|
5237
5579
|
const all = cats.flatMap((c) => c.skills);
|
|
5238
|
-
|
|
5580
|
+
const stageList = manifest2?.stages ?? [...new Set(all.flatMap((skill) => skill.stages ?? []))].sort().map((key) => ({
|
|
5581
|
+
key,
|
|
5582
|
+
name: key,
|
|
5583
|
+
description: "",
|
|
5584
|
+
skillCount: 0
|
|
5585
|
+
}));
|
|
5586
|
+
for (const st of stageList) {
|
|
5239
5587
|
const inStage = filterByStage(all, st.key);
|
|
5240
5588
|
if (inStage.length === 0 && !args.stage) {
|
|
5241
5589
|
process.stdout.write(` ${st.name} (\u65E0\u6280\u80FD\u8986\u76D6)
|
|
@@ -5284,10 +5632,10 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5284
5632
|
const fmt = args.output ?? "table";
|
|
5285
5633
|
if (!state || Object.keys(state.skills).length === 0) {
|
|
5286
5634
|
if (fmt === "json" || fmt === "yaml") {
|
|
5287
|
-
process.stdout.write(
|
|
5635
|
+
process.stdout.write(formatObject14({ baseDir: layout.baseDir, target: layout.target, all: false, skills: [] }, { format: fmt }) + "\n");
|
|
5288
5636
|
process.exit(0);
|
|
5289
5637
|
}
|
|
5290
|
-
process.stdout.write(`${
|
|
5638
|
+
process.stdout.write(`${path6.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
|
|
5291
5639
|
`);
|
|
5292
5640
|
process.stdout.write(" \u88C5\u5168\u90E8\uFF1Ahcm skills install --all\n");
|
|
5293
5641
|
process.exit(0);
|
|
@@ -5297,31 +5645,43 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5297
5645
|
const markdown = await readInstalled(layout.baseDir, id);
|
|
5298
5646
|
let title = id;
|
|
5299
5647
|
if (markdown !== void 0) {
|
|
5300
|
-
const raw =
|
|
5648
|
+
const raw = parseSkillFrontmatter2(markdown, id).title;
|
|
5301
5649
|
if (typeof raw === "string" && raw.trim()) title = raw.trim();
|
|
5302
5650
|
}
|
|
5303
5651
|
rows.push({
|
|
5304
5652
|
id,
|
|
5305
5653
|
title,
|
|
5654
|
+
// 迁移保证它必然有值;`?? 'chiron'` 只是让类型闭合,不是业务缺省。
|
|
5655
|
+
sourceKind: record.sourceKind ?? "chiron",
|
|
5656
|
+
sourceLocator: record.sourceLocator ?? record.source,
|
|
5306
5657
|
direct: record.direct,
|
|
5307
5658
|
installedAt: record.installedAt,
|
|
5308
5659
|
// 三种状态互斥:文件没了 / 本地改过 / 和装的时候一致
|
|
5309
|
-
status: markdown === void 0 ? "missing" :
|
|
5660
|
+
status: markdown === void 0 ? "missing" : sha2564(markdown) !== record.sha256 ? "locally-modified" : "installed"
|
|
5310
5661
|
});
|
|
5311
5662
|
}
|
|
5312
|
-
const
|
|
5663
|
+
const byKeyword = keyword ? rows.filter((row) => row.id.includes(keyword) || row.title.includes(keyword)) : rows;
|
|
5664
|
+
const sourceFilter = parseSourceFilter(args.source);
|
|
5665
|
+
const shown = sourceFilter === "all" ? byKeyword : byKeyword.filter((row) => row.sourceKind === sourceFilter);
|
|
5666
|
+
const groupOf = (kind) => kind === "cli" ? "CLI \u81EA\u5E26" : kind === "local" ? "\u672C\u5730\u76EE\u5F55\u88C5\u7684" : "\u777F\u620E\u6280\u80FD\u5E93";
|
|
5313
5667
|
if (fmt === "json" || fmt === "yaml") {
|
|
5314
|
-
process.stdout.write(
|
|
5668
|
+
process.stdout.write(formatObject14({
|
|
5315
5669
|
baseDir: layout.baseDir,
|
|
5316
5670
|
target: layout.target,
|
|
5317
5671
|
endpoint: state.endpoint,
|
|
5318
5672
|
all: Boolean(state.all),
|
|
5319
5673
|
updatedAt: state.updatedAt,
|
|
5674
|
+
// 按来源分组的小计:顾问要能一眼看出「这份是跟着 CLI 走的还是跟着后端走的」
|
|
5675
|
+
bySource: {
|
|
5676
|
+
cli: shown.filter((r) => r.sourceKind === "cli").length,
|
|
5677
|
+
chiron: shown.filter((r) => r.sourceKind === "chiron").length,
|
|
5678
|
+
local: shown.filter((r) => r.sourceKind === "local").length
|
|
5679
|
+
},
|
|
5320
5680
|
skills: shown
|
|
5321
5681
|
}, { format: fmt }) + "\n");
|
|
5322
5682
|
process.exit(0);
|
|
5323
5683
|
}
|
|
5324
|
-
process.stdout.write(`\u5DF2\u5B89\u88C5\u7684\
|
|
5684
|
+
process.stdout.write(`\u5DF2\u5B89\u88C5\u7684\u6280\u80FD \u2014 ${layout.baseDir}\uFF08${layout.target}\uFF09
|
|
5325
5685
|
`);
|
|
5326
5686
|
process.stdout.write(`\u6765\u6E90 ${state.endpoint ?? "\uFF08\u672A\u8BB0\u5F55\uFF09"}` + (state.all ? " \u8DDF\u968F\u5168\u96C6\uFF1A\u662F\n\n" : "\n\n"));
|
|
5327
5687
|
if (shown.length === 0) {
|
|
@@ -5329,11 +5689,18 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5329
5689
|
`);
|
|
5330
5690
|
process.exit(0);
|
|
5331
5691
|
}
|
|
5332
|
-
for (const
|
|
5333
|
-
const
|
|
5334
|
-
|
|
5335
|
-
process.stdout.write(` ${
|
|
5692
|
+
for (const kind of ["chiron", "cli", "local"]) {
|
|
5693
|
+
const group = shown.filter((row) => row.sourceKind === kind);
|
|
5694
|
+
if (group.length === 0) continue;
|
|
5695
|
+
process.stdout.write(` ${groupOf(kind)}\uFF08${group.length}\uFF09
|
|
5696
|
+
`);
|
|
5697
|
+
for (const row of group) {
|
|
5698
|
+
const mark = row.status === "missing" ? "?" : row.status === "locally-modified" ? "!" : row.direct ? "\u25CF" : "\u25CB";
|
|
5699
|
+
const note = row.status === "missing" ? "\u6587\u4EF6\u4E0D\u89C1\u4E86\uFF08update \u4F1A\u88C5\u56DE\uFF09" : row.status === "locally-modified" ? "\u672C\u5730\u6539\u8FC7" : row.direct ? "" : "\u4F9D\u8D56\u5E26\u5165";
|
|
5700
|
+
process.stdout.write(` ${mark} ${row.id.padEnd(30)} ${row.title.padEnd(24)} ${note}
|
|
5336
5701
|
`);
|
|
5702
|
+
}
|
|
5703
|
+
process.stdout.write("\n");
|
|
5337
5704
|
}
|
|
5338
5705
|
const modified = shown.filter((row) => row.status === "locally-modified").length;
|
|
5339
5706
|
const missing = shown.filter((row) => row.status === "missing").length;
|
|
@@ -5352,6 +5719,16 @@ async function listInstalledSkills(keyword, args) {
|
|
|
5352
5719
|
}
|
|
5353
5720
|
async function skillsShowCommand(id, args) {
|
|
5354
5721
|
try {
|
|
5722
|
+
assertSafeSkillId2(id);
|
|
5723
|
+
const source = parseSourceFilter(args.source);
|
|
5724
|
+
if (source !== "chiron" && listBundledSkills().some((s) => s.id === id)) {
|
|
5725
|
+
const document = await readBundledSkillDocument(id);
|
|
5726
|
+
process.stdout.write(document.markdown.endsWith("\n") ? document.markdown : document.markdown + "\n");
|
|
5727
|
+
process.exit(0);
|
|
5728
|
+
}
|
|
5729
|
+
if (source === "cli") {
|
|
5730
|
+
throw invalid3(`\u8FD9\u4E00\u7248 CLI \u6CA1\u6709\u81EA\u5E26 ${id} \u8FD9\u4E2A\u6280\u80FD\uFF08--source cli \u4E0D\u770B\u777F\u620E\u76EE\u5F55\uFF09`);
|
|
5731
|
+
}
|
|
5355
5732
|
const { http, base } = await chironClient(args);
|
|
5356
5733
|
const md = await fetchSkillMarkdown(http, id, base);
|
|
5357
5734
|
process.stdout.write(md.endsWith("\n") ? md : md + "\n");
|
|
@@ -5367,13 +5744,19 @@ async function skillsSyncCommand(id, args) {
|
|
|
5367
5744
|
throw invalid3(`sync --target \u5F53\u524D\u53EA\u652F\u6301 workbuddy\uFF0C\u6536\u5230 "${args.target ?? ""}"`);
|
|
5368
5745
|
}
|
|
5369
5746
|
if (!args.project?.trim()) throw invalid3("sync \u5FC5\u987B\u4F20 --project <path>");
|
|
5370
|
-
|
|
5747
|
+
if (listBundledSkills().some((one) => one.id === id)) {
|
|
5748
|
+
throw new CliError18({
|
|
5749
|
+
code: CliErrorCode17.SYNC_SOURCE_UNSUPPORTED,
|
|
5750
|
+
message: `\`${id}\` \u662F CLI \u81EA\u5E26\u6280\u80FD\uFF1Bskills sync \u7684\u9501\u6587\u4EF6\uFF08chiron.lock.json\uFF09\u6B64\u523B\u53EA\u80FD\u8BB0\u5F55\u777F\u620E\u6765\u6E90\u7684\u51FA\u5904\u3002\u88C5\u5230\u672C\u5730\u7528 \`hcm skills install ` + id + "`\u3002"
|
|
5751
|
+
});
|
|
5752
|
+
}
|
|
5753
|
+
const project = path6.resolve(args.project);
|
|
5371
5754
|
const runtime = await prepareWorkBuddyRuntime({
|
|
5372
5755
|
candidate: args.cli,
|
|
5373
5756
|
currentEntryHasRequiredCapabilities: !args.cli?.trim(),
|
|
5374
5757
|
forbiddenCandidatePaths: [
|
|
5375
|
-
|
|
5376
|
-
|
|
5758
|
+
path6.join(project, ".hcm-delivery", "bin", "hcm"),
|
|
5759
|
+
path6.join(project, ".hcm-delivery", "bin", "hcm.cmd")
|
|
5377
5760
|
]
|
|
5378
5761
|
});
|
|
5379
5762
|
const connectionArgs = { endpoint: args.endpoint, env: args.env };
|
|
@@ -5396,7 +5779,7 @@ async function skillsSyncCommand(id, args) {
|
|
|
5396
5779
|
});
|
|
5397
5780
|
const fmt = args.output ?? "table";
|
|
5398
5781
|
if (fmt === "json" || fmt === "yaml") {
|
|
5399
|
-
process.stdout.write(`${
|
|
5782
|
+
process.stdout.write(`${formatObject14(result2, { format: fmt })}
|
|
5400
5783
|
`);
|
|
5401
5784
|
} else if (result2.changed) {
|
|
5402
5785
|
process.stdout.write(`\u2705 ${id} \u2192 ${result2.skillsDir}
|
|
@@ -5434,8 +5817,9 @@ async function skillsInstallCommand(id, args) {
|
|
|
5434
5817
|
let endpoint;
|
|
5435
5818
|
let base;
|
|
5436
5819
|
let manifest2;
|
|
5820
|
+
let conflictNote;
|
|
5437
5821
|
if (args.from) {
|
|
5438
|
-
const root =
|
|
5822
|
+
const root = path6.resolve(args.from);
|
|
5439
5823
|
requestedIds = all ? await listLocalSkillIds(root) : [id];
|
|
5440
5824
|
const seen = /* @__PURE__ */ new Set();
|
|
5441
5825
|
documents = [];
|
|
@@ -5447,23 +5831,97 @@ async function skillsInstallCommand(id, args) {
|
|
|
5447
5831
|
}
|
|
5448
5832
|
}
|
|
5449
5833
|
} else {
|
|
5450
|
-
const
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
5466
|
-
);
|
|
5834
|
+
const source = parseSourceFilter(args.source);
|
|
5835
|
+
const bundled = source === "chiron" ? void 0 : listBundledSkills();
|
|
5836
|
+
let http;
|
|
5837
|
+
if (source !== "cli") {
|
|
5838
|
+
const tracked = decideTrackedEndpoint({
|
|
5839
|
+
explicit: Boolean(args.endpoint || args.env),
|
|
5840
|
+
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5841
|
+
recorded: (await readSkillsState(layout.baseDir))?.endpoint
|
|
5842
|
+
});
|
|
5843
|
+
const client3 = await chironClient({ ...args, endpoint: tracked });
|
|
5844
|
+
endpoint = client3.endpoint;
|
|
5845
|
+
base = client3.base;
|
|
5846
|
+
http = client3.http;
|
|
5847
|
+
manifest2 = await fetchSkillCatalog(http, base);
|
|
5848
|
+
}
|
|
5849
|
+
const catalog = buildUnifiedCatalog({ source, chironManifest: manifest2, bundled, cliVersion: CLI_VERSION });
|
|
5850
|
+
const conflicts = detectIdConflicts(catalog);
|
|
5851
|
+
if (conflicts.ids.length > 0) {
|
|
5852
|
+
throw new CliError18({
|
|
5853
|
+
code: CliErrorCode17.SKILL_ID_CONFLICT,
|
|
5854
|
+
message: conflictMessage(conflicts.ids, endpoint ?? "(\u672A\u77E5\u73AF\u5883)")
|
|
5855
|
+
});
|
|
5856
|
+
}
|
|
5857
|
+
conflictNote = conflicts.checked ? void 0 : unconfirmedConflictNote(source);
|
|
5858
|
+
for (const v of checkCrossSourceRequires(catalog)) {
|
|
5859
|
+
throw invalid3(`${v.from} \u58F0\u660E\u4F9D\u8D56 ${v.to}\uFF0C\u4F46 ${v.to} \u662F CLI \u81EA\u5E26\u6280\u80FD\u3002${v.reason}`);
|
|
5860
|
+
}
|
|
5861
|
+
const wantBundled = (one) => catalog.cli.entries.has(one);
|
|
5862
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5863
|
+
documents = [];
|
|
5864
|
+
const pushAll = (docs) => {
|
|
5865
|
+
for (const d of docs) {
|
|
5866
|
+
if (seen.has(d.id)) continue;
|
|
5867
|
+
seen.add(d.id);
|
|
5868
|
+
documents.push(d);
|
|
5869
|
+
}
|
|
5870
|
+
};
|
|
5871
|
+
const installBundled = async (one) => {
|
|
5872
|
+
const { documents: local, external } = await resolveBundledClosure(one);
|
|
5873
|
+
for (const need of external) {
|
|
5874
|
+
if (catalog.chiron.status !== "fetched") {
|
|
5875
|
+
throw invalid3(
|
|
5876
|
+
`\u81EA\u5E26\u6280\u80FD ${one} \u4F9D\u8D56\u777F\u620E\u6280\u80FD ${need}\uFF0C\u4F46\u8FD9\u4E00\u8D9F\u6CA1\u8FDE\u7F51\u53D6\u777F\u620E\u76EE\u5F55\uFF08--source ${source}\uFF09\u3002\u8054\u7F51\u540E\u91CD\u8DD1\uFF0C\u6216\u5148\u5355\u72EC\u88C5\u4E0A\u5B83\u3002`
|
|
5877
|
+
);
|
|
5878
|
+
}
|
|
5879
|
+
if (!catalog.chiron.entries.has(need)) {
|
|
5880
|
+
throw invalid3(`\u81EA\u5E26\u6280\u80FD ${one} \u4F9D\u8D56 ${need}\uFF0C\u4F46 ${endpoint} \u7684\u6280\u80FD\u76EE\u5F55\u91CC\u6CA1\u6709\u5B83`);
|
|
5881
|
+
}
|
|
5882
|
+
pushAll(
|
|
5883
|
+
await mapWithConcurrency(
|
|
5884
|
+
resolveSkillInstallOrder2(manifest2, need),
|
|
5885
|
+
FETCH_CONCURRENCY,
|
|
5886
|
+
(skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5887
|
+
)
|
|
5888
|
+
);
|
|
5889
|
+
}
|
|
5890
|
+
pushAll(local);
|
|
5891
|
+
};
|
|
5892
|
+
if (all) {
|
|
5893
|
+
const order = manifest2 ? resolveAllSkillsInstallOrder(manifest2) : [];
|
|
5894
|
+
const bundledIds = [...catalog.cli.entries.keys()].sort();
|
|
5895
|
+
if (order.length === 0 && bundledIds.length === 0) {
|
|
5896
|
+
throw invalid3(`${endpoint ?? "\u8FD9\u4E00\u8D9F\u770B\u7684\u6765\u6E90"} \u91CC\u6CA1\u6709\u53EF\u88C5\u7684\u6280\u80FD`);
|
|
5897
|
+
}
|
|
5898
|
+
requestedIds = [...order.map((skill) => skill.id), ...bundledIds];
|
|
5899
|
+
if (order.length > 0) {
|
|
5900
|
+
pushAll(
|
|
5901
|
+
await mapWithConcurrency(
|
|
5902
|
+
order,
|
|
5903
|
+
FETCH_CONCURRENCY,
|
|
5904
|
+
(skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5905
|
+
)
|
|
5906
|
+
);
|
|
5907
|
+
}
|
|
5908
|
+
for (const one of bundledIds) await installBundled(one);
|
|
5909
|
+
} else if (wantBundled(id)) {
|
|
5910
|
+
requestedIds = [id];
|
|
5911
|
+
await installBundled(id);
|
|
5912
|
+
} else {
|
|
5913
|
+
if (source === "cli") {
|
|
5914
|
+
throw invalid3(`\u8FD9\u4E00\u7248 CLI \u6CA1\u6709\u81EA\u5E26 ${id} \u8FD9\u4E2A\u6280\u80FD\uFF08--source cli \u4E0D\u770B\u777F\u620E\u76EE\u5F55\uFF09`);
|
|
5915
|
+
}
|
|
5916
|
+
requestedIds = [id];
|
|
5917
|
+
pushAll(
|
|
5918
|
+
await mapWithConcurrency(
|
|
5919
|
+
resolveSkillInstallOrder2(manifest2, id),
|
|
5920
|
+
FETCH_CONCURRENCY,
|
|
5921
|
+
(skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5922
|
+
)
|
|
5923
|
+
);
|
|
5924
|
+
}
|
|
5467
5925
|
}
|
|
5468
5926
|
const result2 = await installSkillDocuments(documents, requestedIds, layout.baseDir, Boolean(args.force), {
|
|
5469
5927
|
endpoint,
|
|
@@ -5474,9 +5932,9 @@ async function skillsInstallCommand(id, args) {
|
|
|
5474
5932
|
const agents = layout.agentsFile ? await syncAgentsIndex(layout, manifest2) : void 0;
|
|
5475
5933
|
const fmt = args.output ?? "table";
|
|
5476
5934
|
if (fmt === "json" || fmt === "yaml") {
|
|
5477
|
-
process.stdout.write(
|
|
5935
|
+
process.stdout.write(formatObject14({ ...result2, all, target_agent: layout.target, agentsFile: layout.agentsFile, agents, conflictCheck: conflictNote ? { checked: false, note: conflictNote } : { checked: true } }, { format: fmt }) + "\n");
|
|
5478
5936
|
} else {
|
|
5479
|
-
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result2.baseDir}` : `\u2705 ${id} \u2192 ${
|
|
5937
|
+
const headline = all ? `\u2705 \u5168\u90E8 ${requestedIds.length} \u4E2A\u6280\u80FD \u2192 ${result2.baseDir}` : `\u2705 ${id} \u2192 ${path6.join(result2.baseDir, id, "SKILL.md")}`;
|
|
5480
5938
|
process.stdout.write(`${headline}
|
|
5481
5939
|
`);
|
|
5482
5940
|
if (result2.installed.length > 0) {
|
|
@@ -5498,7 +5956,9 @@ async function skillsInstallCommand(id, args) {
|
|
|
5498
5956
|
process.stdout.write(` \u5DF2${agents === "created" ? "\u521B\u5EFA" : "\u66F4\u65B0"} ${layout.agentsFile} \u91CC\u7684\u6280\u80FD\u7D22\u5F15\uFF08Codex \u8BFB\u8FD9\u91CC\uFF09
|
|
5499
5957
|
`);
|
|
5500
5958
|
}
|
|
5501
|
-
process.stdout.write(` \u6765\u6E90 ${endpoint ??
|
|
5959
|
+
process.stdout.write(` \u6765\u6E90 ${endpoint ?? (args.from ? path6.resolve(args.from) : `@hcmai/cli@${CLI_VERSION}`)}
|
|
5960
|
+
`);
|
|
5961
|
+
if (conflictNote) process.stdout.write(` \u26A0\uFE0F ${conflictNote}
|
|
5502
5962
|
`);
|
|
5503
5963
|
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");
|
|
5504
5964
|
}
|
|
@@ -5524,14 +5984,14 @@ async function mapWithConcurrency(items, limit, fn) {
|
|
|
5524
5984
|
async function listLocalSkillIds(root) {
|
|
5525
5985
|
let entries;
|
|
5526
5986
|
try {
|
|
5527
|
-
entries = await
|
|
5987
|
+
entries = await fsp2.readdir(root, { withFileTypes: true });
|
|
5528
5988
|
} catch {
|
|
5529
5989
|
throw invalid3(`\u8BFB\u4E0D\u5230\u672C\u5730\u6280\u80FD\u76EE\u5F55\uFF1A${root}`);
|
|
5530
5990
|
}
|
|
5531
5991
|
const ids = [];
|
|
5532
5992
|
for (const entry of entries) {
|
|
5533
5993
|
if (!entry.isDirectory()) continue;
|
|
5534
|
-
if (await exists2(
|
|
5994
|
+
if (await exists2(path6.join(root, entry.name, "SKILL.md"))) ids.push(entry.name);
|
|
5535
5995
|
}
|
|
5536
5996
|
if (ids.length === 0) throw invalid3(`\u672C\u5730\u76EE\u5F55\u91CC\u6CA1\u6709\u4EFB\u4F55 <id>/SKILL.md\uFF1A${root}`);
|
|
5537
5997
|
return ids.sort();
|
|
@@ -5546,6 +6006,7 @@ async function fetchSkillDocument(http, skill, endpoint, base) {
|
|
|
5546
6006
|
id: skill.id,
|
|
5547
6007
|
markdown: await fetchSkillMarkdown(http, skill.id, base),
|
|
5548
6008
|
source: `${endpoint}${base}/skills/${skill.id}.md`,
|
|
6009
|
+
sourceKind: "chiron",
|
|
5549
6010
|
references
|
|
5550
6011
|
};
|
|
5551
6012
|
}
|
|
@@ -5557,17 +6018,17 @@ async function syncAgentsIndex(layout, manifest2) {
|
|
|
5557
6018
|
for (const category of manifest2?.categories ?? []) {
|
|
5558
6019
|
for (const skill of category.skills ?? []) catalog.set(skill.id, skill);
|
|
5559
6020
|
}
|
|
5560
|
-
const agentsDir =
|
|
6021
|
+
const agentsDir = path6.dirname(layout.agentsFile);
|
|
5561
6022
|
const entries = Object.keys(state.skills).sort().map((id) => {
|
|
5562
6023
|
const known = catalog.get(id);
|
|
5563
|
-
const absolute =
|
|
5564
|
-
let relative2 =
|
|
6024
|
+
const absolute = path6.join(layout.baseDir, id, "SKILL.md");
|
|
6025
|
+
let relative2 = path6.relative(agentsDir, absolute);
|
|
5565
6026
|
if (!relative2.startsWith(".")) relative2 = `./${relative2}`;
|
|
5566
6027
|
return {
|
|
5567
6028
|
id,
|
|
5568
6029
|
title: known?.title ?? id,
|
|
5569
6030
|
description: known?.description ?? "",
|
|
5570
|
-
relativePath: relative2.split(
|
|
6031
|
+
relativePath: relative2.split(path6.sep).join("/")
|
|
5571
6032
|
};
|
|
5572
6033
|
});
|
|
5573
6034
|
return upsertAgentsBlock(layout.agentsFile, renderAgentsBlock(entries));
|
|
@@ -5585,13 +6046,13 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
5585
6046
|
throw invalid3(`\u6280\u80FD\u4F9D\u8D56\u5B58\u5728\u73AF: ${[...pathStack.slice(Math.max(0, start)), id].join(" -> ")}`);
|
|
5586
6047
|
}
|
|
5587
6048
|
assertSafeSkillId2(id);
|
|
5588
|
-
const local =
|
|
6049
|
+
const local = path6.join(sourceRoot, id, "SKILL.md");
|
|
5589
6050
|
let markdown;
|
|
5590
6051
|
try {
|
|
5591
|
-
markdown = await
|
|
6052
|
+
markdown = await fsp2.readFile(local, "utf8");
|
|
5592
6053
|
} catch (cause) {
|
|
5593
6054
|
if (cause?.code === "ENOENT") {
|
|
5594
|
-
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${
|
|
6055
|
+
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${path6.join(sourceRoot, id)}`);
|
|
5595
6056
|
}
|
|
5596
6057
|
throw cause;
|
|
5597
6058
|
}
|
|
@@ -5602,9 +6063,10 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
5602
6063
|
id,
|
|
5603
6064
|
markdown,
|
|
5604
6065
|
source: local,
|
|
5605
|
-
|
|
6066
|
+
sourceKind: "local",
|
|
6067
|
+
references: await readLocalReferences(path6.join(sourceRoot, id))
|
|
5606
6068
|
};
|
|
5607
|
-
for (const required of
|
|
6069
|
+
for (const required of parseSkillRequirements2(markdown, id)) {
|
|
5608
6070
|
await visit(required, id);
|
|
5609
6071
|
}
|
|
5610
6072
|
pathStack.pop();
|
|
@@ -5616,20 +6078,20 @@ async function resolveLocalSkillDocuments(sourceRoot, targetId) {
|
|
|
5616
6078
|
}
|
|
5617
6079
|
async function readSkillsState(baseDir) {
|
|
5618
6080
|
try {
|
|
5619
|
-
const raw = await
|
|
6081
|
+
const raw = await fsp2.readFile(path6.join(baseDir, SKILLS_STATE_FILE), "utf8");
|
|
5620
6082
|
const parsed = JSON.parse(raw);
|
|
5621
6083
|
if (!parsed || typeof parsed !== "object" || !parsed.skills) return void 0;
|
|
5622
|
-
return parsed;
|
|
6084
|
+
return migrateSkillsState(parsed);
|
|
5623
6085
|
} catch {
|
|
5624
6086
|
return void 0;
|
|
5625
6087
|
}
|
|
5626
6088
|
}
|
|
5627
6089
|
async function writeSkillsState(baseDir, state) {
|
|
5628
|
-
await
|
|
5629
|
-
const file =
|
|
6090
|
+
await fsp2.mkdir(baseDir, { recursive: true });
|
|
6091
|
+
const file = path6.join(baseDir, SKILLS_STATE_FILE);
|
|
5630
6092
|
const temp = `${file}.${process.pid}.tmp`;
|
|
5631
|
-
await
|
|
5632
|
-
await
|
|
6093
|
+
await fsp2.writeFile(temp, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
6094
|
+
await fsp2.rename(temp, file);
|
|
5633
6095
|
}
|
|
5634
6096
|
var AGENTS_BLOCK_BEGIN = "<!-- hcm-skills:begin -->";
|
|
5635
6097
|
var AGENTS_BLOCK_END = "<!-- hcm-skills:end -->";
|
|
@@ -5652,22 +6114,22 @@ function renderAgentsBlock(entries) {
|
|
|
5652
6114
|
async function upsertAgentsBlock(agentsFile, block) {
|
|
5653
6115
|
let existing = "";
|
|
5654
6116
|
try {
|
|
5655
|
-
existing = await
|
|
6117
|
+
existing = await fsp2.readFile(agentsFile, "utf8");
|
|
5656
6118
|
} catch (cause) {
|
|
5657
6119
|
if (cause?.code !== "ENOENT") throw cause;
|
|
5658
|
-
await
|
|
5659
|
-
await
|
|
6120
|
+
await fsp2.mkdir(path6.dirname(agentsFile), { recursive: true });
|
|
6121
|
+
await fsp2.writeFile(agentsFile, block + "\n", "utf8");
|
|
5660
6122
|
return "created";
|
|
5661
6123
|
}
|
|
5662
6124
|
const begin = existing.indexOf(AGENTS_BLOCK_BEGIN);
|
|
5663
6125
|
const end = existing.indexOf(AGENTS_BLOCK_END);
|
|
5664
6126
|
if (begin >= 0 && end > begin) {
|
|
5665
6127
|
const next = existing.slice(0, begin) + block + existing.slice(end + AGENTS_BLOCK_END.length);
|
|
5666
|
-
await
|
|
6128
|
+
await fsp2.writeFile(agentsFile, next, "utf8");
|
|
5667
6129
|
return "replaced";
|
|
5668
6130
|
}
|
|
5669
6131
|
const separator = existing.endsWith("\n") ? "\n" : "\n\n";
|
|
5670
|
-
await
|
|
6132
|
+
await fsp2.writeFile(agentsFile, existing + separator + block + "\n", "utf8");
|
|
5671
6133
|
return "appended";
|
|
5672
6134
|
}
|
|
5673
6135
|
async function installSkillDocuments(documents, requestedIds, baseDir, force, meta2) {
|
|
@@ -5681,7 +6143,7 @@ async function installSkillDocuments(documents, requestedIds, baseDir, force, me
|
|
|
5681
6143
|
if (!document.markdown.trim()) throw invalid3(`\u6280\u80FD ${document.id} \u5185\u5BB9\u4E3A\u7A7A`);
|
|
5682
6144
|
return {
|
|
5683
6145
|
...document,
|
|
5684
|
-
target:
|
|
6146
|
+
target: path6.join(baseDir, document.id, "SKILL.md"),
|
|
5685
6147
|
bytes: Buffer.byteLength(document.markdown)
|
|
5686
6148
|
};
|
|
5687
6149
|
});
|
|
@@ -5732,12 +6194,12 @@ async function sameOnDisk(baseDir, candidate) {
|
|
|
5732
6194
|
return true;
|
|
5733
6195
|
}
|
|
5734
6196
|
async function readLocalReferences(skillDir) {
|
|
5735
|
-
const root =
|
|
6197
|
+
const root = path6.join(skillDir, "references");
|
|
5736
6198
|
const out = [];
|
|
5737
6199
|
const walk = async (dir, prefix) => {
|
|
5738
6200
|
let items;
|
|
5739
6201
|
try {
|
|
5740
|
-
items = await
|
|
6202
|
+
items = await fsp2.readdir(dir, { withFileTypes: true });
|
|
5741
6203
|
} catch (cause) {
|
|
5742
6204
|
if (cause?.code === "ENOENT") return;
|
|
5743
6205
|
throw cause;
|
|
@@ -5745,49 +6207,79 @@ async function readLocalReferences(skillDir) {
|
|
|
5745
6207
|
for (const item of items.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
5746
6208
|
const relative2 = prefix ? `${prefix}/${item.name}` : item.name;
|
|
5747
6209
|
if (item.isDirectory()) {
|
|
5748
|
-
await walk(
|
|
6210
|
+
await walk(path6.join(dir, item.name), relative2);
|
|
5749
6211
|
continue;
|
|
5750
6212
|
}
|
|
5751
6213
|
if (!item.isFile()) continue;
|
|
5752
|
-
|
|
5753
|
-
out.push({ path: relative2, content: await
|
|
6214
|
+
assertSafeReferencePath2(relative2);
|
|
6215
|
+
out.push({ path: relative2, content: await fsp2.readFile(path6.join(dir, item.name), "utf8") });
|
|
5754
6216
|
}
|
|
5755
6217
|
};
|
|
5756
6218
|
await walk(root, "");
|
|
5757
6219
|
return out;
|
|
5758
6220
|
}
|
|
5759
|
-
async function
|
|
6221
|
+
async function planStaleReferences(state, documents, baseDir) {
|
|
6222
|
+
const remove = [];
|
|
6223
|
+
const keptLocallyModified = [];
|
|
6224
|
+
for (const document of documents) {
|
|
6225
|
+
const recorded = state.skills[document.id]?.references ?? {};
|
|
6226
|
+
const fresh = new Set((document.references ?? []).map((r) => r.path));
|
|
6227
|
+
for (const [relative2, recordedSha] of Object.entries(recorded)) {
|
|
6228
|
+
if (fresh.has(relative2)) continue;
|
|
6229
|
+
const onDisk = await readInstalledReference(baseDir, document.id, relative2);
|
|
6230
|
+
if (onDisk === void 0) continue;
|
|
6231
|
+
if (sha2564(onDisk) !== recordedSha) {
|
|
6232
|
+
keptLocallyModified.push({ id: document.id, path: relative2 });
|
|
6233
|
+
continue;
|
|
6234
|
+
}
|
|
6235
|
+
remove.push({ id: document.id, path: relative2 });
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
const byKey = (a, b) => `${a.id}/${a.path}`.localeCompare(`${b.id}/${b.path}`);
|
|
6239
|
+
return { remove: remove.sort(byKey), keptLocallyModified: keptLocallyModified.sort(byKey) };
|
|
6240
|
+
}
|
|
6241
|
+
async function writeSkillDocuments(documents, baseDir, removals = []) {
|
|
5760
6242
|
const staged = [];
|
|
6243
|
+
const removed = [];
|
|
5761
6244
|
try {
|
|
5762
6245
|
for (const document of documents) {
|
|
5763
|
-
const target =
|
|
5764
|
-
await
|
|
5765
|
-
const temp =
|
|
5766
|
-
await
|
|
6246
|
+
const target = path6.join(baseDir, document.id, "SKILL.md");
|
|
6247
|
+
await fsp2.mkdir(path6.dirname(target), { recursive: true });
|
|
6248
|
+
const temp = path6.join(path6.dirname(target), `.SKILL.md.${process.pid}.${Date.now()}.tmp`);
|
|
6249
|
+
await fsp2.writeFile(temp, document.markdown, "utf8");
|
|
5767
6250
|
staged.push({ temp, target });
|
|
5768
6251
|
for (const reference of document.references ?? []) {
|
|
5769
|
-
|
|
5770
|
-
const referenceTarget =
|
|
5771
|
-
await
|
|
6252
|
+
assertSafeReferencePath2(reference.path);
|
|
6253
|
+
const referenceTarget = path6.join(baseDir, document.id, "references", reference.path);
|
|
6254
|
+
await fsp2.mkdir(path6.dirname(referenceTarget), { recursive: true });
|
|
5772
6255
|
const referenceTemp = `${referenceTarget}.${process.pid}.${Date.now()}.tmp`;
|
|
5773
|
-
await
|
|
6256
|
+
await fsp2.writeFile(referenceTemp, reference.content, "utf8");
|
|
5774
6257
|
staged.push({ temp: referenceTemp, target: referenceTarget });
|
|
5775
6258
|
}
|
|
5776
6259
|
}
|
|
5777
|
-
for (const
|
|
6260
|
+
for (const stale of removals) {
|
|
6261
|
+
assertSafeReferencePath2(stale.path);
|
|
6262
|
+
const original = path6.join(baseDir, stale.id, "references", stale.path);
|
|
6263
|
+
const parked = `${original}.${process.pid}.${Date.now()}.removed`;
|
|
6264
|
+
await fsp2.rename(original, parked);
|
|
6265
|
+
removed.push({ parked, original });
|
|
6266
|
+
}
|
|
6267
|
+
for (const file of staged) await fsp2.rename(file.temp, file.target);
|
|
6268
|
+
await Promise.all(removed.map((r) => fsp2.unlink(r.parked).catch(() => void 0)));
|
|
5778
6269
|
} catch (cause) {
|
|
5779
|
-
await Promise.all(staged.map((file) =>
|
|
6270
|
+
await Promise.all(staged.map((file) => fsp2.unlink(file.temp).catch(() => void 0)));
|
|
6271
|
+
await Promise.all(removed.map((r) => fsp2.rename(r.parked, r.original).catch(() => void 0)));
|
|
5780
6272
|
throw cause;
|
|
5781
6273
|
}
|
|
5782
6274
|
}
|
|
5783
6275
|
async function recordInstalled(baseDir, documents, meta2) {
|
|
5784
6276
|
const state = await readSkillsState(baseDir) ?? {
|
|
5785
|
-
version:
|
|
6277
|
+
version: 2,
|
|
5786
6278
|
target: meta2.target ?? "claude",
|
|
5787
6279
|
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
5788
6280
|
skills: {}
|
|
5789
6281
|
};
|
|
5790
|
-
state.version =
|
|
6282
|
+
state.version = 2;
|
|
5791
6283
|
if (meta2.target) state.target = meta2.target;
|
|
5792
6284
|
if (meta2.endpoint) state.endpoint = meta2.endpoint;
|
|
5793
6285
|
if (meta2.base !== void 0) state.base = meta2.base;
|
|
@@ -5797,12 +6289,15 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
5797
6289
|
const previous = state.skills[document.id];
|
|
5798
6290
|
state.skills[document.id] = {
|
|
5799
6291
|
source: document.source,
|
|
5800
|
-
|
|
6292
|
+
// 🔴 两个键同值:`source` 留给旧 CLI 读(降级不炸),新代码只读 `sourceLocator`。
|
|
6293
|
+
sourceKind: document.sourceKind ?? previous?.sourceKind ?? "chiron",
|
|
6294
|
+
sourceLocator: document.source,
|
|
6295
|
+
sha256: sha2564(document.markdown),
|
|
5801
6296
|
installedAt: meta2.keepInstalledAt?.has(document.id) && previous ? previous.installedAt : state.updatedAt,
|
|
5802
6297
|
// 已经是直接安装的,不因为这次作为依赖被带进来就降级
|
|
5803
6298
|
direct: meta2.directIds.has(document.id) || Boolean(previous?.direct),
|
|
5804
6299
|
references: Object.fromEntries(
|
|
5805
|
-
(document.references ?? []).map((reference) => [reference.path,
|
|
6300
|
+
(document.references ?? []).map((reference) => [reference.path, sha2564(reference.content)])
|
|
5806
6301
|
)
|
|
5807
6302
|
};
|
|
5808
6303
|
}
|
|
@@ -5810,11 +6305,11 @@ async function recordInstalled(baseDir, documents, meta2) {
|
|
|
5810
6305
|
return state;
|
|
5811
6306
|
}
|
|
5812
6307
|
function invalid3(message) {
|
|
5813
|
-
return new
|
|
6308
|
+
return new CliError18({ code: CliErrorCode17.INVALID_ARGUMENT, message });
|
|
5814
6309
|
}
|
|
5815
6310
|
async function readInstalled(baseDir, id) {
|
|
5816
6311
|
try {
|
|
5817
|
-
return await
|
|
6312
|
+
return await fsp2.readFile(path6.join(baseDir, id, "SKILL.md"), "utf8");
|
|
5818
6313
|
} catch (cause) {
|
|
5819
6314
|
if (cause?.code === "ENOENT") return void 0;
|
|
5820
6315
|
throw cause;
|
|
@@ -5822,63 +6317,106 @@ async function readInstalled(baseDir, id) {
|
|
|
5822
6317
|
}
|
|
5823
6318
|
async function readInstalledReference(baseDir, id, relative2) {
|
|
5824
6319
|
try {
|
|
5825
|
-
return await
|
|
6320
|
+
return await fsp2.readFile(path6.join(baseDir, id, "references", relative2), "utf8");
|
|
5826
6321
|
} catch (cause) {
|
|
5827
6322
|
if (cause?.code === "ENOENT") return void 0;
|
|
5828
6323
|
throw cause;
|
|
5829
6324
|
}
|
|
5830
6325
|
}
|
|
5831
6326
|
async function planSkillUpdate(options) {
|
|
5832
|
-
const { state,
|
|
6327
|
+
const { state, baseDir, force, fetchDocument } = options;
|
|
6328
|
+
const manifest2 = options.manifest;
|
|
6329
|
+
const catalog = options.catalog ?? buildUnifiedCatalog({ source: "chiron", chironManifest: manifest2, cliVersion: CLI_VERSION });
|
|
6330
|
+
const fetchBundled = options.fetchBundled ?? ((id) => readBundledSkillDocument(id));
|
|
5833
6331
|
const all = options.all ?? Boolean(state.all);
|
|
5834
6332
|
const recorded = Object.entries(state.skills);
|
|
5835
6333
|
const directIds = recorded.some(([, r]) => r.direct) ? recorded.filter(([, r]) => r.direct).map(([id]) => id) : recorded.map(([id]) => id);
|
|
5836
|
-
const wanted = all ? [.../* @__PURE__ */ new Set([...directIds, ...flattenSkills(manifest2).map((skill) => skill.id)])] : directIds;
|
|
5837
6334
|
const entries = [];
|
|
6335
|
+
const live = [];
|
|
6336
|
+
for (const id of directIds.sort()) {
|
|
6337
|
+
const record = state.skills[id];
|
|
6338
|
+
const status = classifyInstalledSource(id, record, catalog);
|
|
6339
|
+
if (status === "present") {
|
|
6340
|
+
live.push({ id, kind: (record.sourceKind ?? "chiron") === "cli" ? "cli" : "chiron" });
|
|
6341
|
+
continue;
|
|
6342
|
+
}
|
|
6343
|
+
entries.push({ id, status, note: SOURCE_STATUS_NOTE[status] });
|
|
6344
|
+
}
|
|
6345
|
+
if (all) {
|
|
6346
|
+
const known = new Set(live.map((x) => x.id));
|
|
6347
|
+
for (const kind of ["chiron", "cli"]) {
|
|
6348
|
+
const half = kind === "chiron" ? catalog.chiron : catalog.cli;
|
|
6349
|
+
if (half.status !== "fetched") continue;
|
|
6350
|
+
for (const id of half.entries.keys()) {
|
|
6351
|
+
if (known.has(id)) continue;
|
|
6352
|
+
known.add(id);
|
|
6353
|
+
live.push({ id, kind });
|
|
6354
|
+
}
|
|
6355
|
+
}
|
|
6356
|
+
}
|
|
5838
6357
|
const closure = /* @__PURE__ */ new Map();
|
|
5839
|
-
|
|
6358
|
+
const chironNeeded = /* @__PURE__ */ new Set();
|
|
6359
|
+
for (const item of live) {
|
|
6360
|
+
if (item.kind === "cli") {
|
|
6361
|
+
const queue = [item.id];
|
|
6362
|
+
while (queue.length > 0) {
|
|
6363
|
+
const id = queue.shift();
|
|
6364
|
+
if (closure.has(id)) continue;
|
|
6365
|
+
const entry = catalog.cli.entries.get(id);
|
|
6366
|
+
if (!entry) {
|
|
6367
|
+
chironNeeded.add(id);
|
|
6368
|
+
continue;
|
|
6369
|
+
}
|
|
6370
|
+
closure.set(id, { kind: "cli" });
|
|
6371
|
+
for (const need of entry.meta.requires ?? []) queue.push(need);
|
|
6372
|
+
}
|
|
6373
|
+
continue;
|
|
6374
|
+
}
|
|
6375
|
+
chironNeeded.add(item.id);
|
|
6376
|
+
}
|
|
6377
|
+
for (const id of chironNeeded) {
|
|
6378
|
+
if (!manifest2) continue;
|
|
5840
6379
|
try {
|
|
5841
6380
|
for (const skill of resolveSkillInstallOrder2(manifest2, id)) {
|
|
5842
|
-
if (!closure.has(skill.id)) closure.set(skill.id, skill);
|
|
6381
|
+
if (!closure.has(skill.id)) closure.set(skill.id, { kind: "chiron", skill });
|
|
5843
6382
|
}
|
|
5844
6383
|
} catch {
|
|
5845
|
-
entries.
|
|
5846
|
-
id,
|
|
5847
|
-
|
|
5848
|
-
note: "\u4EA7\u54C1\u8FD9\u4E00\u7248\u7684\u6280\u80FD\u76EE\u5F55\u91CC\u5DF2\u7ECF\u6CA1\u6709\u5B83\u4E86\uFF1B\u672C\u5730\u6587\u4EF6\u4FDD\u7559\u4E0D\u52A8"
|
|
5849
|
-
});
|
|
6384
|
+
if (!entries.some((e) => e.id === id)) {
|
|
6385
|
+
entries.push({ id, status: "removed-upstream", note: SOURCE_STATUS_NOTE["removed-upstream"] });
|
|
6386
|
+
}
|
|
5850
6387
|
}
|
|
5851
6388
|
}
|
|
5852
6389
|
const documents = [];
|
|
5853
|
-
for (const
|
|
5854
|
-
const record = state.skills[
|
|
5855
|
-
const onDisk = await readInstalled(baseDir,
|
|
6390
|
+
for (const [id, item] of closure) {
|
|
6391
|
+
const record = state.skills[id];
|
|
6392
|
+
const onDisk = await readInstalled(baseDir, id);
|
|
6393
|
+
const fetch = async () => item.kind === "cli" ? await fetchBundled(id) : await fetchDocument(item.skill);
|
|
5856
6394
|
if (onDisk === void 0) {
|
|
5857
|
-
documents.push(await
|
|
6395
|
+
documents.push(await fetch());
|
|
5858
6396
|
entries.push({
|
|
5859
|
-
id
|
|
6397
|
+
id,
|
|
5860
6398
|
status: "added",
|
|
5861
6399
|
note: record ? "\u672C\u5730\u6587\u4EF6\u7F3A\u5931\uFF0C\u91CD\u65B0\u88C5\u56DE" : all ? "\u4EA7\u54C1\u8FD9\u4E00\u7248\u65B0\u589E\u7684\u6280\u80FD" : "\u4EA7\u54C1\u65B0\u589E\u7684\u524D\u7F6E\u6280\u80FD"
|
|
5862
6400
|
});
|
|
5863
6401
|
continue;
|
|
5864
6402
|
}
|
|
5865
|
-
const remote = await
|
|
6403
|
+
const remote = await fetch();
|
|
5866
6404
|
const remoteReferences = remote.references ?? [];
|
|
5867
6405
|
let differsFromRemote = remote.markdown !== onDisk;
|
|
5868
|
-
let editedLocally = record !== void 0 &&
|
|
6406
|
+
let editedLocally = record !== void 0 && sha2564(onDisk) !== record.sha256;
|
|
5869
6407
|
for (const reference of remoteReferences) {
|
|
5870
|
-
const installed = await readInstalledReference(baseDir,
|
|
6408
|
+
const installed = await readInstalledReference(baseDir, id, reference.path);
|
|
5871
6409
|
if (installed !== reference.content) differsFromRemote = true;
|
|
5872
6410
|
const recordedSha = record?.references?.[reference.path];
|
|
5873
|
-
if (installed !== void 0 && recordedSha &&
|
|
6411
|
+
if (installed !== void 0 && recordedSha && sha2564(installed) !== recordedSha) editedLocally = true;
|
|
5874
6412
|
}
|
|
5875
6413
|
if (!differsFromRemote) {
|
|
5876
|
-
entries.push({ id
|
|
6414
|
+
entries.push({ id, status: "unchanged" });
|
|
5877
6415
|
continue;
|
|
5878
6416
|
}
|
|
5879
6417
|
if (editedLocally && !force) {
|
|
5880
6418
|
entries.push({
|
|
5881
|
-
id
|
|
6419
|
+
id,
|
|
5882
6420
|
status: "locally-modified",
|
|
5883
6421
|
note: "\u672C\u5730\u6539\u8FC7\uFF0C\u6CA1\u6709\u8986\u76D6\uFF1B\u786E\u5B9A\u8981\u7528\u4EA7\u54C1\u8FD9\u4E00\u7248\u5C31\u52A0 --force"
|
|
5884
6422
|
});
|
|
@@ -5886,7 +6424,7 @@ async function planSkillUpdate(options) {
|
|
|
5886
6424
|
}
|
|
5887
6425
|
documents.push(remote);
|
|
5888
6426
|
entries.push({
|
|
5889
|
-
id
|
|
6427
|
+
id,
|
|
5890
6428
|
status: "updated",
|
|
5891
6429
|
note: editedLocally ? "\u672C\u5730\u6539\u8FC7\uFF0C--force \u5DF2\u8986\u76D6" : void 0
|
|
5892
6430
|
});
|
|
@@ -5899,37 +6437,54 @@ var UPDATE_STATUS_LABEL = {
|
|
|
5899
6437
|
added: "\u5DF2\u8865\u88C5",
|
|
5900
6438
|
unchanged: "\u5DF2\u662F\u6700\u65B0",
|
|
5901
6439
|
"locally-modified": "\u672C\u5730\u6539\u8FC7\xB7\u8DF3\u8FC7",
|
|
5902
|
-
"removed-upstream": "\u4EA7\u54C1\u5DF2\u79FB\u9664"
|
|
6440
|
+
"removed-upstream": "\u4EA7\u54C1\u5DF2\u79FB\u9664",
|
|
6441
|
+
"removed-in-cli": "CLI \u4E0D\u518D\u81EA\u5E26",
|
|
6442
|
+
// 🔴 这一档不是结论,是「这一趟没核」。文案上必须看得出区别,
|
|
6443
|
+
// 否则顾问会把它读成「没问题」,而它可能正藏着一份真的被移除了的技能。
|
|
6444
|
+
"not-checked": "\u672C\u8F6E\u672A\u6838",
|
|
6445
|
+
local: "\u672C\u5730\u6765\u6E90\xB7\u4E0D\u7BA1"
|
|
5903
6446
|
};
|
|
5904
6447
|
async function skillsUpdateCommand(args) {
|
|
5905
6448
|
try {
|
|
5906
6449
|
const layout = await resolveInstallLayout(args);
|
|
5907
6450
|
const state = await readSkillsState(layout.baseDir);
|
|
5908
6451
|
if (!state || Object.keys(state.skills).length === 0) {
|
|
5909
|
-
throw new
|
|
5910
|
-
code:
|
|
5911
|
-
message: `${
|
|
6452
|
+
throw new CliError18({
|
|
6453
|
+
code: CliErrorCode17.INVALID_ARGUMENT,
|
|
6454
|
+
message: `${path6.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`
|
|
5912
6455
|
});
|
|
5913
6456
|
}
|
|
6457
|
+
const source = parseSourceFilter(args.source);
|
|
5914
6458
|
const endpoint = decideTrackedEndpoint({
|
|
5915
6459
|
explicit: Boolean(args.endpoint || args.env),
|
|
5916
6460
|
current: await resolveEndpoint(args).catch(() => void 0),
|
|
5917
6461
|
recorded: state.endpoint
|
|
5918
6462
|
});
|
|
5919
|
-
|
|
5920
|
-
|
|
6463
|
+
let manifest2;
|
|
6464
|
+
let base;
|
|
6465
|
+
let http;
|
|
6466
|
+
if (source !== "cli") {
|
|
6467
|
+
const client3 = await chironClient({ ...args, endpoint });
|
|
6468
|
+
http = client3.http;
|
|
6469
|
+
base = client3.base;
|
|
6470
|
+
manifest2 = await fetchSkillCatalog(http, base);
|
|
6471
|
+
}
|
|
6472
|
+
const bundled = source === "chiron" ? void 0 : listBundledSkills();
|
|
6473
|
+
const catalog = buildUnifiedCatalog({ source, chironManifest: manifest2, bundled, cliVersion: CLI_VERSION });
|
|
5921
6474
|
const all = Boolean(args.all) || Boolean(state.all);
|
|
5922
6475
|
const plan = await planSkillUpdate({
|
|
5923
6476
|
state,
|
|
5924
6477
|
manifest: manifest2,
|
|
6478
|
+
catalog,
|
|
5925
6479
|
baseDir: layout.baseDir,
|
|
5926
6480
|
force: Boolean(args.force),
|
|
5927
6481
|
all,
|
|
5928
6482
|
fetchDocument: (skill) => fetchSkillDocument(http, skill, endpoint, base)
|
|
5929
6483
|
});
|
|
6484
|
+
const stale = await planStaleReferences(state, plan.documents, layout.baseDir);
|
|
5930
6485
|
const subscriptionChanged = all && !state.all;
|
|
5931
6486
|
if (!args.check && (plan.documents.length > 0 || subscriptionChanged)) {
|
|
5932
|
-
await writeSkillDocuments(plan.documents, layout.baseDir);
|
|
6487
|
+
await writeSkillDocuments(plan.documents, layout.baseDir, stale.remove);
|
|
5933
6488
|
await recordInstalled(layout.baseDir, plan.documents, {
|
|
5934
6489
|
endpoint,
|
|
5935
6490
|
base,
|
|
@@ -5942,18 +6497,24 @@ async function skillsUpdateCommand(args) {
|
|
|
5942
6497
|
const changed = plan.entries.filter((e) => e.status === "updated" || e.status === "added");
|
|
5943
6498
|
const fmt = args.output ?? "table";
|
|
5944
6499
|
if (fmt === "json" || fmt === "yaml") {
|
|
5945
|
-
process.stdout.write(
|
|
6500
|
+
process.stdout.write(formatObject14({
|
|
5946
6501
|
endpoint,
|
|
5947
6502
|
baseDir: layout.baseDir,
|
|
5948
6503
|
target: layout.target,
|
|
5949
6504
|
dryRun: Boolean(args.check),
|
|
5950
6505
|
all,
|
|
5951
6506
|
changed: changed.length,
|
|
6507
|
+
sources: {
|
|
6508
|
+
cli: { status: catalog.cli.status, count: catalog.cli.entries.size },
|
|
6509
|
+
chiron: { status: catalog.chiron.status, count: catalog.chiron.entries.size }
|
|
6510
|
+
},
|
|
6511
|
+
cliVersion: CLI_VERSION,
|
|
6512
|
+
staleReferences: stale,
|
|
5952
6513
|
entries: plan.entries
|
|
5953
6514
|
}, { format: fmt }) + "\n");
|
|
5954
6515
|
process.exit(0);
|
|
5955
6516
|
}
|
|
5956
|
-
process.stdout.write(`\
|
|
6517
|
+
process.stdout.write(`\u6280\u80FD\u5347\u7EA7 \u2014 ${source === "cli" ? `@hcmai/cli@${CLI_VERSION}\uFF08\u672A\u8FDE\u7F51\uFF09` : endpoint}
|
|
5957
6518
|
`);
|
|
5958
6519
|
process.stdout.write(`\u843D\u70B9 ${layout.baseDir}\uFF08${layout.target}\uFF09` + (all ? " \u8DDF\u968F\u5168\u96C6\uFF1A\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4F1A\u81EA\u52A8\u8865\u4E0A\n\n" : "\n\n"));
|
|
5959
6520
|
for (const entry of plan.entries) {
|
|
@@ -5966,10 +6527,38 @@ async function skillsUpdateCommand(args) {
|
|
|
5966
6527
|
process.stdout.write("\n");
|
|
5967
6528
|
const skipped = plan.entries.filter((e) => e.status === "locally-modified").length;
|
|
5968
6529
|
const removed = plan.entries.filter((e) => e.status === "removed-upstream").length;
|
|
6530
|
+
const removedInCli = plan.entries.filter((e) => e.status === "removed-in-cli").length;
|
|
6531
|
+
const notChecked = plan.entries.filter((e) => e.status === "not-checked").length;
|
|
5969
6532
|
const tail = [
|
|
5970
6533
|
skipped > 0 ? `${skipped} \u4E2A\u672C\u5730\u6539\u8FC7\u88AB\u8DF3\u8FC7\uFF08--force \u8986\u76D6\uFF09` : "",
|
|
5971
|
-
removed > 0 ? `${removed} \u4E2A\u4EA7\u54C1\u5DF2\u79FB\u9664\uFF08\u672C\u5730\u6587\u4EF6\u4FDD\u7559\uFF09` : ""
|
|
6534
|
+
removed > 0 ? `${removed} \u4E2A\u4EA7\u54C1\u5DF2\u79FB\u9664\uFF08\u672C\u5730\u6587\u4EF6\u4FDD\u7559\uFF09` : "",
|
|
6535
|
+
removedInCli > 0 ? `${removedInCli} \u4E2A\u8FD9\u4E00\u7248 CLI \u4E0D\u518D\u81EA\u5E26\uFF08\u672C\u5730\u6587\u4EF6\u4FDD\u7559\uFF09` : "",
|
|
6536
|
+
notChecked > 0 ? `${notChecked} \u4E2A\u672C\u8F6E\u672A\u6838\uFF08\u90A3\u4E00\u534A\u8FD9\u8D9F\u6CA1\u770B\uFF09` : ""
|
|
5972
6537
|
].filter(Boolean).join("\uFF0C");
|
|
6538
|
+
const cliCount = plan.entries.filter(
|
|
6539
|
+
(e) => (state.skills[e.id]?.sourceKind ?? "chiron") === "cli"
|
|
6540
|
+
).length;
|
|
6541
|
+
if (cliCount > 0) {
|
|
6542
|
+
process.stdout.write(` \u81EA\u5E26\u6280\u80FD ${cliCount} \u4E2A\u5DF2\u8DDF\u968F\u5F53\u524D CLI ${CLI_VERSION}
|
|
6543
|
+
`);
|
|
6544
|
+
}
|
|
6545
|
+
if (source === "cli" && all) {
|
|
6546
|
+
process.stdout.write(
|
|
6547
|
+
" \u672C\u8D9F\u53EA\u770B\u4E86\u81EA\u5E26\u90A3\u4E00\u534A\uFF1B\u8FD9\u4E2A\u76EE\u5F55\u8BA2\u9605\u7684\u662F\u5168\u5E93\uFF0C\u8054\u7F51\u540E update \u4F1A\u8865\u9F50\u777F\u620E\u90A3\u4E00\u534A\n"
|
|
6548
|
+
);
|
|
6549
|
+
}
|
|
6550
|
+
if (stale.remove.length > 0) {
|
|
6551
|
+
process.stdout.write(
|
|
6552
|
+
` ${args.check ? "\u4F1A\u5220\u6389" : "\u5DF2\u5220\u6389"} ${stale.remove.length} \u4E2A\u4EA7\u54C1\u8FD9\u4E00\u7248\u64A4\u6389\u7684\u53C2\u8003\u6587\u4EF6\uFF08${stale.remove.map((r) => `${r.id}/references/${r.path}`).join("\u3001")}\uFF09
|
|
6553
|
+
`
|
|
6554
|
+
);
|
|
6555
|
+
}
|
|
6556
|
+
if (stale.keptLocallyModified.length > 0) {
|
|
6557
|
+
process.stdout.write(
|
|
6558
|
+
` ${stale.keptLocallyModified.length} \u4E2A\u4EA7\u54C1\u5DF2\u64A4\u6389\u4F46\u672C\u5730\u6539\u8FC7\u7684\u53C2\u8003\u6587\u4EF6\u4FDD\u7559\u4E86\uFF08${stale.keptLocallyModified.map((r) => `${r.id}/references/${r.path}`).join("\u3001")}\uFF09
|
|
6559
|
+
`
|
|
6560
|
+
);
|
|
6561
|
+
}
|
|
5973
6562
|
if (changed.length === 0 && skipped === 0 && removed === 0) {
|
|
5974
6563
|
process.stdout.write(args.check ? " \u5168\u90E8\u5DF2\u662F\u6700\u65B0\uFF08--check \u53EA\u770B\u4E0D\u52A8\uFF09\n" : " \u5168\u90E8\u5DF2\u662F\u6700\u65B0\uFF0C\u6CA1\u6709\u6539\u52A8\n");
|
|
5975
6564
|
} else {
|
|
@@ -5987,13 +6576,13 @@ async function skillsUpdateCommand(args) {
|
|
|
5987
6576
|
import {
|
|
5988
6577
|
bootstrapTenant,
|
|
5989
6578
|
createHttpClient as createHttpClient3,
|
|
5990
|
-
formatObject as
|
|
6579
|
+
formatObject as formatObject15,
|
|
5991
6580
|
fromAxiosError as fromAxiosError14,
|
|
5992
6581
|
loadEnv as loadEnv6,
|
|
5993
6582
|
loadGlobalConfig as loadGlobalConfig7,
|
|
5994
6583
|
resolveActiveEnv as resolveActiveEnv4,
|
|
5995
|
-
CliError as
|
|
5996
|
-
CliErrorCode as
|
|
6584
|
+
CliError as CliError19,
|
|
6585
|
+
CliErrorCode as CliErrorCode18
|
|
5997
6586
|
} from "@hcmai/sdk";
|
|
5998
6587
|
init_exit();
|
|
5999
6588
|
async function resolveBootstrapKey(args, stdinTaken) {
|
|
@@ -6001,8 +6590,8 @@ async function resolveBootstrapKey(args, stdinTaken) {
|
|
|
6001
6590
|
if (fromEnv) return fromEnv;
|
|
6002
6591
|
if (args.bootstrapKeyStdin) {
|
|
6003
6592
|
if (stdinTaken) {
|
|
6004
|
-
throw new
|
|
6005
|
-
code:
|
|
6593
|
+
throw new CliError19({
|
|
6594
|
+
code: CliErrorCode18.INVALID_ARGUMENT,
|
|
6006
6595
|
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"
|
|
6007
6596
|
});
|
|
6008
6597
|
}
|
|
@@ -6032,8 +6621,8 @@ async function resolveEndpoint2(args) {
|
|
|
6032
6621
|
const envName = resolveActiveEnv4({ envFlag: args.env }, process.env, g);
|
|
6033
6622
|
const env2 = await loadEnv6(envName).catch(() => null);
|
|
6034
6623
|
if (!env2?.endpoint) {
|
|
6035
|
-
throw new
|
|
6036
|
-
code:
|
|
6624
|
+
throw new CliError19({
|
|
6625
|
+
code: CliErrorCode18.MISSING_FLAG,
|
|
6037
6626
|
message: "\u672A\u89E3\u6790\u5230 endpoint \u2014\u2014 \u4F20 --endpoint <url>\uFF0C\u6216\u5148 `hcm env add <name> --endpoint <url>`"
|
|
6038
6627
|
});
|
|
6039
6628
|
}
|
|
@@ -6042,8 +6631,8 @@ async function resolveEndpoint2(args) {
|
|
|
6042
6631
|
async function tenantBootstrapCommand(args) {
|
|
6043
6632
|
try {
|
|
6044
6633
|
if (!args.tenantId || !args.adminUsername) {
|
|
6045
|
-
throw new
|
|
6046
|
-
code:
|
|
6634
|
+
throw new CliError19({
|
|
6635
|
+
code: CliErrorCode18.MISSING_FLAG,
|
|
6047
6636
|
message: "--tenant-id \u4E0E --admin-username \u5FC5\u586B"
|
|
6048
6637
|
});
|
|
6049
6638
|
}
|
|
@@ -6079,7 +6668,7 @@ async function tenantBootstrapCommand(args) {
|
|
|
6079
6668
|
`
|
|
6080
6669
|
);
|
|
6081
6670
|
}
|
|
6082
|
-
process.stdout.write(
|
|
6671
|
+
process.stdout.write(formatObject15(result2, { format: fmt }) + "\n");
|
|
6083
6672
|
process.exit(0);
|
|
6084
6673
|
} catch (e) {
|
|
6085
6674
|
const err = e;
|
|
@@ -6094,17 +6683,17 @@ async function tenantBootstrapCommand(args) {
|
|
|
6094
6683
|
}
|
|
6095
6684
|
|
|
6096
6685
|
// src/commands/meta.ts
|
|
6097
|
-
import { promises as
|
|
6686
|
+
import { promises as fsp3 } from "fs";
|
|
6098
6687
|
import {
|
|
6099
6688
|
HcmClient as HcmClient15,
|
|
6100
|
-
formatObject as
|
|
6689
|
+
formatObject as formatObject16,
|
|
6101
6690
|
fromAxiosError as fromAxiosError15,
|
|
6102
6691
|
listTenantMeta,
|
|
6103
6692
|
getTenantMeta,
|
|
6104
6693
|
saveTenantMeta,
|
|
6105
6694
|
deleteTenantMeta,
|
|
6106
|
-
CliError as
|
|
6107
|
-
CliErrorCode as
|
|
6695
|
+
CliError as CliError20,
|
|
6696
|
+
CliErrorCode as CliErrorCode19
|
|
6108
6697
|
} from "@hcmai/sdk";
|
|
6109
6698
|
init_exit();
|
|
6110
6699
|
init_auth_guard();
|
|
@@ -6134,27 +6723,27 @@ async function metaListCommand(args) {
|
|
|
6134
6723
|
const fmt = args.output ?? "json";
|
|
6135
6724
|
if (fmt !== "json") process.stdout.write(`\u{1F4C4} ${items.length} \u4EFD${total ? ` / \u5171 ${total}` : ""}
|
|
6136
6725
|
`);
|
|
6137
|
-
process.stdout.write(
|
|
6726
|
+
process.stdout.write(formatObject16(items, { format: fmt }) + "\n");
|
|
6138
6727
|
process.exit(0);
|
|
6139
6728
|
} catch (e) {
|
|
6140
6729
|
fail4(e, args);
|
|
6141
6730
|
}
|
|
6142
6731
|
}
|
|
6143
|
-
async function metaGetCommand(
|
|
6732
|
+
async function metaGetCommand(path9, args) {
|
|
6144
6733
|
try {
|
|
6145
6734
|
const c = await client2(args);
|
|
6146
|
-
const content = await getTenantMeta(c.raw(),
|
|
6735
|
+
const content = await getTenantMeta(c.raw(), path9);
|
|
6147
6736
|
process.stdout.write(content.endsWith("\n") ? content : content + "\n");
|
|
6148
6737
|
process.exit(0);
|
|
6149
6738
|
} catch (e) {
|
|
6150
6739
|
fail4(e, args);
|
|
6151
6740
|
}
|
|
6152
6741
|
}
|
|
6153
|
-
async function metaSetCommand(
|
|
6742
|
+
async function metaSetCommand(path9, args) {
|
|
6154
6743
|
try {
|
|
6155
6744
|
if (!args.file) {
|
|
6156
|
-
throw new
|
|
6157
|
-
code:
|
|
6745
|
+
throw new CliError20({
|
|
6746
|
+
code: CliErrorCode19.MISSING_FLAG,
|
|
6158
6747
|
message: "--file <path> \u5FC5\u586B\uFF08\u4ECE\u6587\u4EF6\u8BFB\u53D6 meta \u539F\u6587\uFF1B\u7528 - \u8868\u793A stdin\uFF09"
|
|
6159
6748
|
});
|
|
6160
6749
|
}
|
|
@@ -6164,10 +6753,10 @@ async function metaSetCommand(path7, args) {
|
|
|
6164
6753
|
process.stdin.on("data", (d) => buf += d);
|
|
6165
6754
|
process.stdin.on("end", () => resolve3(buf));
|
|
6166
6755
|
process.stdin.on("error", reject);
|
|
6167
|
-
}) : await
|
|
6756
|
+
}) : await fsp3.readFile(args.file, "utf8");
|
|
6168
6757
|
if (args.dryRun) {
|
|
6169
6758
|
process.stdout.write(
|
|
6170
|
-
`[dry-run] \u5C06\u5199\u5165 ${
|
|
6759
|
+
`[dry-run] \u5C06\u5199\u5165 ${path9}\uFF08${Buffer.byteLength(content)} B\uFF09\u2014\u2014\u672A\u5B9E\u9645\u5199\u5165
|
|
6171
6760
|
---
|
|
6172
6761
|
${content}
|
|
6173
6762
|
`
|
|
@@ -6175,35 +6764,35 @@ ${content}
|
|
|
6175
6764
|
process.exit(0);
|
|
6176
6765
|
}
|
|
6177
6766
|
const c = await client2(args);
|
|
6178
|
-
const result2 = await saveTenantMeta(c.raw(),
|
|
6767
|
+
const result2 = await saveTenantMeta(c.raw(), path9, content);
|
|
6179
6768
|
const fmt = args.output ?? "json";
|
|
6180
6769
|
if (fmt !== "json") {
|
|
6181
|
-
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${
|
|
6770
|
+
process.stdout.write(`\u2705 \u5DF2\u5199\u5165 ${path9}
|
|
6182
6771
|
\u8BB0\u5F97\u6E05\u7F13\u5B58\uFF1Ahcm cache clear-meta <Model>
|
|
6183
6772
|
`);
|
|
6184
6773
|
}
|
|
6185
|
-
process.stdout.write(
|
|
6774
|
+
process.stdout.write(formatObject16(result2, { format: fmt }) + "\n");
|
|
6186
6775
|
process.exit(0);
|
|
6187
6776
|
} catch (e) {
|
|
6188
6777
|
if (e?.code === "ENOENT") exitWithError(new Error(`\u6587\u4EF6\u4E0D\u5B58\u5728\uFF1A${args.file}`));
|
|
6189
6778
|
fail4(e, args);
|
|
6190
6779
|
}
|
|
6191
6780
|
}
|
|
6192
|
-
async function metaDeleteCommand(
|
|
6781
|
+
async function metaDeleteCommand(path9, args) {
|
|
6193
6782
|
try {
|
|
6194
6783
|
if (!args.yes) {
|
|
6195
|
-
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${
|
|
6784
|
+
const ok = await promptConfirm(`\u5C06\u5220\u9664\u5143\u6570\u636E ${path9}\uFF0C\u6B64\u64CD\u4F5C\u4E0D\u53EF\u9006\u3002\u7EE7\u7EED\uFF1F`);
|
|
6196
6785
|
if (!ok) {
|
|
6197
6786
|
process.stderr.write("\u5DF2\u53D6\u6D88\n");
|
|
6198
6787
|
process.exit(1);
|
|
6199
6788
|
}
|
|
6200
6789
|
}
|
|
6201
6790
|
const c = await client2(args);
|
|
6202
|
-
const result2 = await deleteTenantMeta(c.raw(),
|
|
6791
|
+
const result2 = await deleteTenantMeta(c.raw(), path9);
|
|
6203
6792
|
const fmt = args.output ?? "json";
|
|
6204
|
-
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${
|
|
6793
|
+
if (fmt !== "json") process.stdout.write(`\u{1F5D1}\uFE0F \u5DF2\u5220\u9664 ${path9}
|
|
6205
6794
|
`);
|
|
6206
|
-
process.stdout.write(
|
|
6795
|
+
process.stdout.write(formatObject16(result2, { format: fmt }) + "\n");
|
|
6207
6796
|
process.exit(0);
|
|
6208
6797
|
} catch (e) {
|
|
6209
6798
|
fail4(e, args);
|
|
@@ -6215,14 +6804,14 @@ init_exit();
|
|
|
6215
6804
|
init_auth_guard();
|
|
6216
6805
|
import * as crypto from "crypto";
|
|
6217
6806
|
import * as fs7 from "fs/promises";
|
|
6218
|
-
import * as
|
|
6807
|
+
import * as path7 from "path";
|
|
6219
6808
|
import {
|
|
6220
6809
|
buildMiniPreviewTargets,
|
|
6221
6810
|
buildMiniVerifyAgentReport,
|
|
6222
6811
|
createWorkspaceFile,
|
|
6223
6812
|
deleteWorkspaceFile,
|
|
6224
6813
|
formatMiniVerifyReport,
|
|
6225
|
-
formatObject as
|
|
6814
|
+
formatObject as formatObject17,
|
|
6226
6815
|
formatRows as formatRows5,
|
|
6227
6816
|
getMiniStatus,
|
|
6228
6817
|
HcmClient as HcmClient16,
|
|
@@ -6432,7 +7021,7 @@ async function workspacePullCommand(args) {
|
|
|
6432
7021
|
http: client3.raw(),
|
|
6433
7022
|
appCode,
|
|
6434
7023
|
workspaceKey,
|
|
6435
|
-
targetDir:
|
|
7024
|
+
targetDir: path7.join(rootDir, MINI_APPS_DIR, appCode),
|
|
6436
7025
|
tenantId: client3.tenantId,
|
|
6437
7026
|
endpoint: client3.endpoint,
|
|
6438
7027
|
profile: args.profile ?? client3.profile,
|
|
@@ -6447,7 +7036,7 @@ async function workspacePullCommand(args) {
|
|
|
6447
7036
|
const rawRemoteByLocalPath = /* @__PURE__ */ new Map();
|
|
6448
7037
|
for (const file of rawFiles) {
|
|
6449
7038
|
const content = await loadWorkspaceFileContent(client3.raw(), file.id);
|
|
6450
|
-
const target =
|
|
7039
|
+
const target = path7.join(rootDir, content.relativePath);
|
|
6451
7040
|
await writeTextFile(target, content.content, !!args.force);
|
|
6452
7041
|
pulledRawFiles.push(content.relativePath);
|
|
6453
7042
|
rawRemoteByLocalPath.set(content.relativePath, file);
|
|
@@ -6497,7 +7086,7 @@ async function workspaceAddMiniCommand(appCode, args) {
|
|
|
6497
7086
|
const result2 = await initMiniAppProject({
|
|
6498
7087
|
appCode,
|
|
6499
7088
|
workspaceKey: context.workspaceKey,
|
|
6500
|
-
targetDir:
|
|
7089
|
+
targetDir: path7.join(rootDir, MINI_APPS_DIR, appCode),
|
|
6501
7090
|
name: args.name,
|
|
6502
7091
|
defaultSurface: parseSurface(args.surface),
|
|
6503
7092
|
templateKind,
|
|
@@ -6551,7 +7140,7 @@ async function workspaceStatusCommand(args) {
|
|
|
6551
7140
|
}
|
|
6552
7141
|
if (args.output === "json" || args.output === "yaml") {
|
|
6553
7142
|
process.stdout.write(
|
|
6554
|
-
|
|
7143
|
+
formatObject17({ rootDir, miniApps: rows }, { format: args.output }) + "\n"
|
|
6555
7144
|
);
|
|
6556
7145
|
} else {
|
|
6557
7146
|
process.stdout.write(formatRows5(rows, { format: "table" }) + "\n");
|
|
@@ -6627,7 +7216,7 @@ async function workspacePreviewCommand(args) {
|
|
|
6627
7216
|
);
|
|
6628
7217
|
}
|
|
6629
7218
|
if (args.output === "json" || args.output === "yaml") {
|
|
6630
|
-
process.stdout.write(
|
|
7219
|
+
process.stdout.write(formatObject17(rows, { format: args.output }) + "\n");
|
|
6631
7220
|
} else {
|
|
6632
7221
|
process.stdout.write(formatRows5(rows, { format: "table" }) + "\n");
|
|
6633
7222
|
}
|
|
@@ -6738,7 +7327,7 @@ function removedMiniCommand() {
|
|
|
6738
7327
|
);
|
|
6739
7328
|
}
|
|
6740
7329
|
function resolveRootDir(dir) {
|
|
6741
|
-
return
|
|
7330
|
+
return path7.resolve(dir ?? ".");
|
|
6742
7331
|
}
|
|
6743
7332
|
function buildWorkspaceContext(args) {
|
|
6744
7333
|
return {
|
|
@@ -6754,7 +7343,7 @@ function buildWorkspaceContext(args) {
|
|
|
6754
7343
|
async function readWorkspaceContext(rootDir) {
|
|
6755
7344
|
try {
|
|
6756
7345
|
const parsed = JSON.parse(
|
|
6757
|
-
await fs7.readFile(
|
|
7346
|
+
await fs7.readFile(path7.join(rootDir, WORKSPACE_CONTEXT_FILE), "utf-8")
|
|
6758
7347
|
);
|
|
6759
7348
|
if (parsed.kind !== WORKSPACE_CONTEXT_KIND || parsed.version !== WORKSPACE_CONTEXT_VERSION) {
|
|
6760
7349
|
throw new Error(`${WORKSPACE_CONTEXT_FILE} is not an HCM workspace context`);
|
|
@@ -6784,7 +7373,7 @@ async function requireWorkspaceContext(rootDir) {
|
|
|
6784
7373
|
}
|
|
6785
7374
|
async function writeWorkspaceContext(rootDir, context) {
|
|
6786
7375
|
await fs7.writeFile(
|
|
6787
|
-
|
|
7376
|
+
path7.join(rootDir, WORKSPACE_CONTEXT_FILE),
|
|
6788
7377
|
JSON.stringify(context, null, 2) + "\n"
|
|
6789
7378
|
);
|
|
6790
7379
|
}
|
|
@@ -6795,13 +7384,13 @@ async function writeTextFile(file, content, force) {
|
|
|
6795
7384
|
} catch (e) {
|
|
6796
7385
|
if (e.code !== "ENOENT") throw e;
|
|
6797
7386
|
}
|
|
6798
|
-
await fs7.mkdir(
|
|
7387
|
+
await fs7.mkdir(path7.dirname(file), { recursive: true });
|
|
6799
7388
|
await fs7.writeFile(file, content);
|
|
6800
7389
|
}
|
|
6801
7390
|
async function readWorkspaceRawManifest(rootDir, context) {
|
|
6802
7391
|
try {
|
|
6803
7392
|
const parsed = JSON.parse(
|
|
6804
|
-
await fs7.readFile(
|
|
7393
|
+
await fs7.readFile(path7.join(rootDir, WORKSPACE_STATE_DIR, WORKSPACE_PULL_MANIFEST_FILE), "utf-8")
|
|
6805
7394
|
);
|
|
6806
7395
|
if (parsed.version !== WORKSPACE_CONTEXT_VERSION || parsed.workspaceKey !== context.workspaceKey) {
|
|
6807
7396
|
throw new Error(`${WORKSPACE_PULL_MANIFEST_FILE} does not match workspace ${context.workspaceKey}`);
|
|
@@ -6819,7 +7408,7 @@ async function readWorkspaceRawManifest(rootDir, context) {
|
|
|
6819
7408
|
}
|
|
6820
7409
|
}
|
|
6821
7410
|
async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath) {
|
|
6822
|
-
const stateDir =
|
|
7411
|
+
const stateDir = path7.join(rootDir, WORKSPACE_STATE_DIR);
|
|
6823
7412
|
await fs7.mkdir(stateDir, { recursive: true });
|
|
6824
7413
|
const snapshots = files.map((file) => ({
|
|
6825
7414
|
localPath: file.localPath,
|
|
@@ -6830,7 +7419,7 @@ async function writeWorkspaceRawState(rootDir, context, files, remoteByLocalPath
|
|
|
6830
7419
|
lastModified: remoteByLocalPath.get(file.localPath)?.lastModified
|
|
6831
7420
|
}));
|
|
6832
7421
|
await fs7.writeFile(
|
|
6833
|
-
|
|
7422
|
+
path7.join(stateDir, WORKSPACE_PULL_MANIFEST_FILE),
|
|
6834
7423
|
JSON.stringify(
|
|
6835
7424
|
{
|
|
6836
7425
|
version: WORKSPACE_CONTEXT_VERSION,
|
|
@@ -6852,7 +7441,7 @@ async function readWorkspaceRawLocalFiles(rootDir) {
|
|
|
6852
7441
|
return files;
|
|
6853
7442
|
}
|
|
6854
7443
|
async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
6855
|
-
const absoluteDir =
|
|
7444
|
+
const absoluteDir = path7.join(rootDir, relativeDir);
|
|
6856
7445
|
let entries;
|
|
6857
7446
|
try {
|
|
6858
7447
|
entries = await fs7.readdir(absoluteDir, { withFileTypes: true });
|
|
@@ -6863,8 +7452,8 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
|
6863
7452
|
for (const entry of entries) {
|
|
6864
7453
|
if (entry.isDirectory() && RAW_WORKSPACE_EXCLUDED_DIRS.has(entry.name)) continue;
|
|
6865
7454
|
if (entry.isFile() && RAW_WORKSPACE_EXCLUDED_FILES.has(entry.name)) continue;
|
|
6866
|
-
const localPath = normalizeRelativePath(
|
|
6867
|
-
const absolutePath =
|
|
7455
|
+
const localPath = normalizeRelativePath(path7.posix.join(toPosix(relativeDir), entry.name));
|
|
7456
|
+
const absolutePath = path7.join(rootDir, localPath);
|
|
6868
7457
|
if (entry.isDirectory()) {
|
|
6869
7458
|
await walkWorkspaceRawFiles(rootDir, localPath, files);
|
|
6870
7459
|
continue;
|
|
@@ -6875,7 +7464,7 @@ async function walkWorkspaceRawFiles(rootDir, relativeDir, files) {
|
|
|
6875
7464
|
localPath,
|
|
6876
7465
|
absolutePath,
|
|
6877
7466
|
content,
|
|
6878
|
-
sha256:
|
|
7467
|
+
sha256: sha2565(content),
|
|
6879
7468
|
size: Buffer.byteLength(content, "utf-8")
|
|
6880
7469
|
});
|
|
6881
7470
|
}
|
|
@@ -6942,7 +7531,7 @@ function buildWorkspaceRawPushPlan(manifest2, localFiles, remoteFiles) {
|
|
|
6942
7531
|
for (const entry of status) {
|
|
6943
7532
|
const remote = remoteByLocal.get(entry.localPath);
|
|
6944
7533
|
const base = baseByLocal.get(entry.localPath);
|
|
6945
|
-
const remoteSha = remote ?
|
|
7534
|
+
const remoteSha = remote ? sha2565(remote.content) : void 0;
|
|
6946
7535
|
if (entry.status === "added") {
|
|
6947
7536
|
if (remote) {
|
|
6948
7537
|
conflicts.push(workspaceRawConflict(entry, "remote file already exists but was not in local snapshot"));
|
|
@@ -7044,7 +7633,7 @@ function workspaceRawConflict(entry, reason) {
|
|
|
7044
7633
|
};
|
|
7045
7634
|
}
|
|
7046
7635
|
async function discoverMiniApps(rootDir) {
|
|
7047
|
-
const appsRoot =
|
|
7636
|
+
const appsRoot = path7.join(rootDir, MINI_APPS_DIR);
|
|
7048
7637
|
let entries;
|
|
7049
7638
|
try {
|
|
7050
7639
|
entries = await fs7.readdir(appsRoot, { withFileTypes: true });
|
|
@@ -7052,7 +7641,7 @@ async function discoverMiniApps(rootDir) {
|
|
|
7052
7641
|
if (e.code === "ENOENT") return [];
|
|
7053
7642
|
throw e;
|
|
7054
7643
|
}
|
|
7055
|
-
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir:
|
|
7644
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => ({ appCode: entry.name, dir: path7.join(appsRoot, entry.name) })).sort((a, b) => a.appCode.localeCompare(b.appCode));
|
|
7056
7645
|
}
|
|
7057
7646
|
function detectRemoteMiniApps(files) {
|
|
7058
7647
|
const appCodes = /* @__PURE__ */ new Set();
|
|
@@ -7205,9 +7794,9 @@ function normalizeRelativePath(value) {
|
|
|
7205
7794
|
return normalized;
|
|
7206
7795
|
}
|
|
7207
7796
|
function toPosix(value) {
|
|
7208
|
-
return value.split(
|
|
7797
|
+
return value.split(path7.sep).join("/");
|
|
7209
7798
|
}
|
|
7210
|
-
function
|
|
7799
|
+
function sha2565(content) {
|
|
7211
7800
|
return crypto.createHash("sha256").update(content, "utf-8").digest("hex");
|
|
7212
7801
|
}
|
|
7213
7802
|
function stringRequired(value, field) {
|
|
@@ -7233,7 +7822,7 @@ function optionalText(value) {
|
|
|
7233
7822
|
}
|
|
7234
7823
|
function writeWorkspaceListResult(rows, format, total) {
|
|
7235
7824
|
if (format !== "table") {
|
|
7236
|
-
process.stdout.write(
|
|
7825
|
+
process.stdout.write(formatObject17({ rows, total }, { format }) + "\n");
|
|
7237
7826
|
return;
|
|
7238
7827
|
}
|
|
7239
7828
|
process.stdout.write(formatRows5(toWorkspaceListRows(rows), { format: "table" }) + "\n");
|
|
@@ -7244,7 +7833,7 @@ function writeWorkspaceListResult(rows, format, total) {
|
|
|
7244
7833
|
}
|
|
7245
7834
|
function writeWorkspaceActiveResult(view, format) {
|
|
7246
7835
|
if (format !== "table") {
|
|
7247
|
-
process.stdout.write(
|
|
7836
|
+
process.stdout.write(formatObject17(view, { format }) + "\n");
|
|
7248
7837
|
return;
|
|
7249
7838
|
}
|
|
7250
7839
|
const rows = [
|
|
@@ -7262,7 +7851,7 @@ function writeWorkspaceActiveResult(view, format) {
|
|
|
7262
7851
|
}
|
|
7263
7852
|
function writeWorkspaceBindResult(context, rootDir, format) {
|
|
7264
7853
|
if (format !== "table") {
|
|
7265
|
-
process.stdout.write(
|
|
7854
|
+
process.stdout.write(formatObject17({ ...context, rootDir }, { format }) + "\n");
|
|
7266
7855
|
return;
|
|
7267
7856
|
}
|
|
7268
7857
|
process.stdout.write(
|
|
@@ -7276,7 +7865,7 @@ function writeWorkspaceBindResult(context, rootDir, format) {
|
|
|
7276
7865
|
}
|
|
7277
7866
|
function writeWorkspaceCreateResult(result2, format) {
|
|
7278
7867
|
if (format !== "table") {
|
|
7279
|
-
process.stdout.write(
|
|
7868
|
+
process.stdout.write(formatObject17(result2, { format }) + "\n");
|
|
7280
7869
|
return;
|
|
7281
7870
|
}
|
|
7282
7871
|
process.stdout.write(
|
|
@@ -7292,7 +7881,7 @@ function writeWorkspaceCreateResult(result2, format) {
|
|
|
7292
7881
|
}
|
|
7293
7882
|
function writeWorkspacePullResult(result2, format) {
|
|
7294
7883
|
if (format !== "table") {
|
|
7295
|
-
process.stdout.write(
|
|
7884
|
+
process.stdout.write(formatObject17(result2, { format }) + "\n");
|
|
7296
7885
|
return;
|
|
7297
7886
|
}
|
|
7298
7887
|
const lines = [
|
|
@@ -7310,7 +7899,7 @@ function writeWorkspacePullResult(result2, format) {
|
|
|
7310
7899
|
}
|
|
7311
7900
|
function writeWorkspaceAddMiniResult(result2, rootDir, format) {
|
|
7312
7901
|
if (format !== "table") {
|
|
7313
|
-
process.stdout.write(
|
|
7902
|
+
process.stdout.write(formatObject17({ ...result2, workspaceRoot: rootDir }, { format }) + "\n");
|
|
7314
7903
|
return;
|
|
7315
7904
|
}
|
|
7316
7905
|
process.stdout.write(
|
|
@@ -7326,7 +7915,7 @@ function writeWorkspaceAddMiniResult(result2, rootDir, format) {
|
|
|
7326
7915
|
function writeWorkspaceValidateResult(results, format) {
|
|
7327
7916
|
if (format !== "table") {
|
|
7328
7917
|
process.stdout.write(
|
|
7329
|
-
|
|
7918
|
+
formatObject17(
|
|
7330
7919
|
results.map((item) => ({ artifact: `mini:${item.app.appCode}`, ...item.result })),
|
|
7331
7920
|
{ format }
|
|
7332
7921
|
) + "\n"
|
|
@@ -7354,7 +7943,7 @@ mini:${item.app.appCode}
|
|
|
7354
7943
|
function writeWorkspacePushResult(rawResult, results, format) {
|
|
7355
7944
|
if (format !== "table") {
|
|
7356
7945
|
process.stdout.write(
|
|
7357
|
-
|
|
7946
|
+
formatObject17(
|
|
7358
7947
|
{
|
|
7359
7948
|
workspaceFiles: { artifact: "workspace", ...rawResult },
|
|
7360
7949
|
miniApps: results.map((item) => ({ artifact: `mini:${item.app.appCode}`, ...item.result }))
|
|
@@ -7410,7 +7999,7 @@ function writeWorkspaceVerifyResult(context, rootDir, raw, reports, format) {
|
|
|
7410
7999
|
const ok = rawOk && reports.every((item) => item.report.ok);
|
|
7411
8000
|
if (format === "json") {
|
|
7412
8001
|
process.stdout.write(
|
|
7413
|
-
|
|
8002
|
+
formatObject17(
|
|
7414
8003
|
{
|
|
7415
8004
|
ok,
|
|
7416
8005
|
workspaceKey: context.workspaceKey,
|
|
@@ -7437,7 +8026,7 @@ function writeWorkspaceVerifyResult(context, rootDir, raw, reports, format) {
|
|
|
7437
8026
|
}
|
|
7438
8027
|
if (format !== "table") {
|
|
7439
8028
|
process.stdout.write(
|
|
7440
|
-
|
|
8029
|
+
formatObject17(
|
|
7441
8030
|
{
|
|
7442
8031
|
ok,
|
|
7443
8032
|
workspaceKey: context.workspaceKey,
|
|
@@ -7504,8 +8093,8 @@ import {
|
|
|
7504
8093
|
deleteIdentity as deleteIdentity2,
|
|
7505
8094
|
loadGlobalConfig as loadGlobalConfig8,
|
|
7506
8095
|
TokenStore as TokenStore3,
|
|
7507
|
-
CliError as
|
|
7508
|
-
CliErrorCode as
|
|
8096
|
+
CliError as CliError21,
|
|
8097
|
+
CliErrorCode as CliErrorCode20
|
|
7509
8098
|
} from "@hcmai/sdk";
|
|
7510
8099
|
async function identityList(args) {
|
|
7511
8100
|
try {
|
|
@@ -7533,8 +8122,8 @@ async function identityUse(name) {
|
|
|
7533
8122
|
try {
|
|
7534
8123
|
const meta2 = await loadIdentity4(name);
|
|
7535
8124
|
const env2 = await loadEnv7(meta2.env).catch(() => {
|
|
7536
|
-
throw new
|
|
7537
|
-
code:
|
|
8125
|
+
throw new CliError21({
|
|
8126
|
+
code: CliErrorCode20.IDENTITY_NOT_IN_ENV,
|
|
7538
8127
|
message: `identity '${name}' references env '${meta2.env}' which no longer exists. Re-create the env, or remove this orphan: hcm identity remove ${name}`
|
|
7539
8128
|
});
|
|
7540
8129
|
});
|
|
@@ -7572,11 +8161,11 @@ async function identityRemove(name) {
|
|
|
7572
8161
|
init_auth_guard();
|
|
7573
8162
|
import {
|
|
7574
8163
|
HcmClient as HcmClient17,
|
|
7575
|
-
formatObject as
|
|
8164
|
+
formatObject as formatObject18
|
|
7576
8165
|
} from "@hcmai/sdk";
|
|
7577
8166
|
|
|
7578
8167
|
// src/commands/delivery.ts
|
|
7579
|
-
import { createHash as
|
|
8168
|
+
import { createHash as createHash6 } from "crypto";
|
|
7580
8169
|
var APPROVAL_BYPASS_FLAGS = /* @__PURE__ */ new Set(["-y", "--yes", "--force", "--auto-approve"]);
|
|
7581
8170
|
var CREDENTIAL_FLAGS = /* @__PURE__ */ new Set([
|
|
7582
8171
|
"--password",
|
|
@@ -7598,7 +8187,7 @@ var GUARDED_EXECUTOR_REASON_CODES = /* @__PURE__ */ new Set([
|
|
|
7598
8187
|
]);
|
|
7599
8188
|
function assessDeliveryCommand(inputArgv, options = {}) {
|
|
7600
8189
|
const argv = inputArgv[0] === "--" ? inputArgv.slice(1) : [...inputArgv];
|
|
7601
|
-
const commandSha256 =
|
|
8190
|
+
const commandSha256 = sha2566(JSON.stringify(argv));
|
|
7602
8191
|
const name = argv[0] ?? "";
|
|
7603
8192
|
const subcommand = argv[1] && !argv[1].startsWith("-") ? argv[1] : void 0;
|
|
7604
8193
|
const envAlias = flagValue(argv, "--env");
|
|
@@ -7629,7 +8218,7 @@ function assessDeliveryCommand(inputArgv, options = {}) {
|
|
|
7629
8218
|
return {
|
|
7630
8219
|
schemaVersion: 1,
|
|
7631
8220
|
contractVersion: "2026-08-29",
|
|
7632
|
-
assessmentId:
|
|
8221
|
+
assessmentId: sha2566(`2026-08-29
|
|
7633
8222
|
${commandSha256}`).slice(0, 24),
|
|
7634
8223
|
commandSha256,
|
|
7635
8224
|
command: { name: name || "(empty)", ...subcommand ? { subcommand } : {} },
|
|
@@ -7801,17 +8390,17 @@ function summarizeTarget(argv, actionResolution) {
|
|
|
7801
8390
|
}
|
|
7802
8391
|
const id = flagValue(argv, "--id");
|
|
7803
8392
|
if (name === "user" && subcommand === "reset-password") {
|
|
7804
|
-
if (id) target.userIdSha256 =
|
|
8393
|
+
if (id) target.userIdSha256 = sha2566(id);
|
|
7805
8394
|
const username = flagValue(argv, "--username");
|
|
7806
|
-
if (username) target.usernameSha256 =
|
|
8395
|
+
if (username) target.usernameSha256 = sha2566(username);
|
|
7807
8396
|
target.forceChangeOnNextLogin = !hasFlag(argv, "--no-force-change");
|
|
7808
8397
|
} else if (id) {
|
|
7809
|
-
target.recordIdSha256 =
|
|
8398
|
+
target.recordIdSha256 = sha2566(id);
|
|
7810
8399
|
}
|
|
7811
8400
|
for (const flag of PAYLOAD_FLAGS) {
|
|
7812
8401
|
const raw = flagValue(argv, flag);
|
|
7813
8402
|
if (!raw) continue;
|
|
7814
|
-
target.payloadSha256 =
|
|
8403
|
+
target.payloadSha256 = sha2566(raw);
|
|
7815
8404
|
try {
|
|
7816
8405
|
const parsed = JSON.parse(raw);
|
|
7817
8406
|
if (isPlainObject(parsed)) target.fieldNames = Object.keys(parsed).sort();
|
|
@@ -7908,8 +8497,8 @@ function valuesAfterFlag(argv, flag) {
|
|
|
7908
8497
|
}
|
|
7909
8498
|
return values;
|
|
7910
8499
|
}
|
|
7911
|
-
function
|
|
7912
|
-
return
|
|
8500
|
+
function sha2566(value) {
|
|
8501
|
+
return createHash6("sha256").update(value).digest("hex");
|
|
7913
8502
|
}
|
|
7914
8503
|
function isPlainObject(value) {
|
|
7915
8504
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -7928,7 +8517,7 @@ async function assessEffectiveDeliveryCommand(inputArgv, dependencies = {}) {
|
|
|
7928
8517
|
}
|
|
7929
8518
|
async function deliveryAssessCommand(argv, args) {
|
|
7930
8519
|
const assessment = await assessEffectiveDeliveryCommand(argv);
|
|
7931
|
-
process.stdout.write(
|
|
8520
|
+
process.stdout.write(formatObject18(assessment, { format: args.output ?? "json" }) + "\n");
|
|
7932
8521
|
}
|
|
7933
8522
|
async function resolveCoreAction(request) {
|
|
7934
8523
|
try {
|
|
@@ -8013,12 +8602,54 @@ function flagValue2(argv, flag) {
|
|
|
8013
8602
|
return index >= 0 ? argv[index + 1] : void 0;
|
|
8014
8603
|
}
|
|
8015
8604
|
|
|
8605
|
+
// src/commands/delivery-doctor.ts
|
|
8606
|
+
init_auth_guard();
|
|
8607
|
+
init_exit();
|
|
8608
|
+
import {
|
|
8609
|
+
HcmClient as HcmClient18,
|
|
8610
|
+
fetchReconIdentity,
|
|
8611
|
+
formatObject as formatObject19,
|
|
8612
|
+
fromAxiosError as fromAxiosError17,
|
|
8613
|
+
httpProbe,
|
|
8614
|
+
runDoctor
|
|
8615
|
+
} from "@hcmai/sdk";
|
|
8616
|
+
var DEFAULT_CAPABILITIES = "default";
|
|
8617
|
+
function render2(report) {
|
|
8618
|
+
const lines = [];
|
|
8619
|
+
for (const c of report.checks) {
|
|
8620
|
+
lines.push(`${c.ok ? "\u2705" : "\u274C"} ${c.anchor.padEnd(13)} ${c.detail}`);
|
|
8621
|
+
}
|
|
8622
|
+
lines.push("");
|
|
8623
|
+
lines.push(
|
|
8624
|
+
report.ok ? "\u56DB\u6761\u951A\u70B9\u5168\u7EFF\uFF1A\u53EF\u4EE5\u5F00\u59CB recon\u3002" : "\u6709\u951A\u70B9\u6CA1\u8FC7\u2014\u2014\u5148\u6309\u4E0A\u9762\u5404\u6761\u7684\u4E0B\u4E00\u6B65\u5904\u7406\uFF0C\u4E0D\u8981\u5728\u8FD9\u4E2A\u72B6\u6001\u4E0A\u722C\u57FA\u7EBF\u3002"
|
|
8625
|
+
);
|
|
8626
|
+
return lines.join("\n");
|
|
8627
|
+
}
|
|
8628
|
+
async function deliveryDoctorCommand(args) {
|
|
8629
|
+
try {
|
|
8630
|
+
const ctx = await getActiveAuthContextFromCli(args);
|
|
8631
|
+
const client3 = HcmClient18.fromAuthContext(ctx);
|
|
8632
|
+
const probe = httpProbe(client3.raw(), args.capabilities ?? DEFAULT_CAPABILITIES);
|
|
8633
|
+
const identity2 = await fetchReconIdentity(probe, ctx.tenantId);
|
|
8634
|
+
const report = await runDoctor(probe, identity2);
|
|
8635
|
+
const fmt = args.output ?? "json";
|
|
8636
|
+
if (fmt !== "json") process.stdout.write(render2(report) + "\n");
|
|
8637
|
+
process.stdout.write(formatObject19(report, { format: fmt }) + "\n");
|
|
8638
|
+
process.exit(report.ok ? 0 : 1);
|
|
8639
|
+
} catch (e) {
|
|
8640
|
+
const err = e;
|
|
8641
|
+
if (err?.isAxiosError) exitWithError(fromAxiosError17(err, args.env));
|
|
8642
|
+
exitWithError(e);
|
|
8643
|
+
process.exit(1);
|
|
8644
|
+
}
|
|
8645
|
+
}
|
|
8646
|
+
|
|
8016
8647
|
// src/commands/delivery-execution.ts
|
|
8017
|
-
import { createHash as
|
|
8648
|
+
import { createHash as createHash7, randomBytes as secureRandomBytes } from "crypto";
|
|
8018
8649
|
import { spawn } from "child_process";
|
|
8019
8650
|
import { createReadStream as createReadStream2, promises as fs8 } from "fs";
|
|
8020
|
-
import
|
|
8021
|
-
import { CliError as
|
|
8651
|
+
import path8 from "path";
|
|
8652
|
+
import { CliError as CliError22, CliErrorCode as CliErrorCode21, formatObject as formatObject20 } from "@hcmai/sdk";
|
|
8022
8653
|
init_exit();
|
|
8023
8654
|
var EXECUTION_CONTRACT_VERSION = "2026-08-28.controlled-v1";
|
|
8024
8655
|
var DEFAULT_TTL_SECONDS = 15 * 60;
|
|
@@ -8045,20 +8676,20 @@ async function prepareDeliveryExecution(input) {
|
|
|
8045
8676
|
const core = {
|
|
8046
8677
|
schemaVersion: 1,
|
|
8047
8678
|
contractVersion: EXECUTION_CONTRACT_VERSION,
|
|
8048
|
-
nonceSha256:
|
|
8679
|
+
nonceSha256: sha2567((input.randomBytes ?? secureRandomBytes)(12)),
|
|
8049
8680
|
createdAt: created.toISOString(),
|
|
8050
8681
|
expiresAt: new Date(created.getTime() + ttlSeconds * 1e3).toISOString(),
|
|
8051
|
-
chironLockSha256:
|
|
8682
|
+
chironLockSha256: sha2567(lockBytes),
|
|
8052
8683
|
runtime,
|
|
8053
8684
|
assessment,
|
|
8054
8685
|
documents: { change, rollback },
|
|
8055
8686
|
evidenceDir: evidence.relative
|
|
8056
8687
|
};
|
|
8057
|
-
const planId =
|
|
8688
|
+
const planId = sha2567(canonicalJson(core)).slice(0, 24);
|
|
8058
8689
|
const plan = { ...core, planId };
|
|
8059
|
-
const plansDir =
|
|
8690
|
+
const plansDir = path8.join(project, ".hcm-delivery", "execution-plans");
|
|
8060
8691
|
await safeManagedDirectory(project, ".hcm-delivery/execution-plans", plansDir);
|
|
8061
|
-
const planFile =
|
|
8692
|
+
const planFile = path8.join(plansDir, `${planId}.json`);
|
|
8062
8693
|
try {
|
|
8063
8694
|
await fs8.writeFile(planFile, `${JSON.stringify(plan, null, 2)}
|
|
8064
8695
|
`, {
|
|
@@ -8077,7 +8708,7 @@ async function executeDeliveryPlan(input) {
|
|
|
8077
8708
|
if (await readProjectMode(project) !== "controlled") {
|
|
8078
8709
|
throw invalid4("\u9879\u76EE\u4E0D\u662F controlled\uFF0C\u4E0D\u80FD\u6267\u884C R2 \u8BA1\u5212");
|
|
8079
8710
|
}
|
|
8080
|
-
const planFile =
|
|
8711
|
+
const planFile = path8.join(project, ".hcm-delivery", "execution-plans", `${input.planId}.json`);
|
|
8081
8712
|
const plan = await readPlan(project, planFile);
|
|
8082
8713
|
if (plan.planId !== input.planId || computePlanId(plan) !== input.planId) {
|
|
8083
8714
|
throw invalid4("\u6267\u884C\u8BA1\u5212\u5185\u5BB9\u4E0E planId \u6458\u8981\u4E0D\u4E00\u81F4");
|
|
@@ -8097,7 +8728,7 @@ async function executeDeliveryPlan(input) {
|
|
|
8097
8728
|
throw invalid4("\u5B9E\u9645 argv \u4E0E\u6267\u884C\u8BA1\u5212\u7684\u547D\u4EE4\u6458\u8981\u6216\u98CE\u9669\u4E8B\u5B9E\u4E0D\u4E00\u81F4");
|
|
8098
8729
|
}
|
|
8099
8730
|
const { bytes: lockBytes, runtime: lockedRuntime } = await readChironLock(project);
|
|
8100
|
-
if (
|
|
8731
|
+
if (sha2567(lockBytes) !== plan.chironLockSha256) {
|
|
8101
8732
|
throw invalid4("Chiron \u9501\u6458\u8981\u5DF2\u6F02\u79FB\uFF0C\u8BF7\u91CD\u65B0\u8BC4\u4F30\u5E76 prepare");
|
|
8102
8733
|
}
|
|
8103
8734
|
const runtime = await (input.runtimeIdentity ?? currentRuntimeIdentity)();
|
|
@@ -8108,11 +8739,11 @@ async function executeDeliveryPlan(input) {
|
|
|
8108
8739
|
await assertDocumentUnchanged(project, plan.documents.change, "\u53D8\u66F4\u8BF4\u660E");
|
|
8109
8740
|
await assertDocumentUnchanged(project, plan.documents.rollback, "\u56DE\u6EDA\u8BF4\u660E");
|
|
8110
8741
|
const evidenceDir = await bindEvidenceDirectory(project, plan.evidenceDir);
|
|
8111
|
-
const receiptFile =
|
|
8742
|
+
const receiptFile = path8.join(evidenceDir.absolute, `${plan.planId}.receipt.json`);
|
|
8112
8743
|
if (await pathExists(receiptFile)) throw invalid4(`\u6267\u884C\u6536\u636E\u5DF2\u5B58\u5728\uFF0C\u62D2\u7EDD\u518D\u6B21\u6267\u884C\uFF1A${plan.planId}`);
|
|
8113
|
-
const claimsDir =
|
|
8744
|
+
const claimsDir = path8.join(project, ".hcm-delivery", "execution-claims");
|
|
8114
8745
|
await safeManagedDirectory(project, ".hcm-delivery/execution-claims", claimsDir);
|
|
8115
|
-
const claimFile =
|
|
8746
|
+
const claimFile = path8.join(claimsDir, `${plan.planId}.json`);
|
|
8116
8747
|
try {
|
|
8117
8748
|
await fs8.writeFile(
|
|
8118
8749
|
claimFile,
|
|
@@ -8123,8 +8754,8 @@ async function executeDeliveryPlan(input) {
|
|
|
8123
8754
|
} catch (cause) {
|
|
8124
8755
|
throw invalid4(`\u6267\u884C\u8BA1\u5212\u5DF2\u6D88\u8D39\uFF0C\u62D2\u7EDD\u91CD\u590D\u6267\u884C\uFF1A${plan.planId}`, cause);
|
|
8125
8756
|
}
|
|
8126
|
-
const stdoutHash =
|
|
8127
|
-
const stderrHash =
|
|
8757
|
+
const stdoutHash = createHash7("sha256");
|
|
8758
|
+
const stderrHash = createHash7("sha256");
|
|
8128
8759
|
const run = input.runCommand ?? runCurrentCliCommand;
|
|
8129
8760
|
let exitCode;
|
|
8130
8761
|
try {
|
|
@@ -8171,7 +8802,7 @@ async function deliveryPrepareCommand(argv, args) {
|
|
|
8171
8802
|
evidenceDir: args.evidenceDir,
|
|
8172
8803
|
...ttlSeconds === void 0 ? {} : { ttlSeconds }
|
|
8173
8804
|
});
|
|
8174
|
-
process.stdout.write(
|
|
8805
|
+
process.stdout.write(formatObject20(plan, { format: args.output ?? "json" }) + "\n");
|
|
8175
8806
|
} catch (cause) {
|
|
8176
8807
|
exitWithError(cause);
|
|
8177
8808
|
}
|
|
@@ -8183,14 +8814,14 @@ async function deliveryExecuteCommand(planId, argv, args) {
|
|
|
8183
8814
|
planId,
|
|
8184
8815
|
argv
|
|
8185
8816
|
});
|
|
8186
|
-
process.stdout.write(
|
|
8817
|
+
process.stdout.write(formatObject20(receipt, { format: args.output ?? "json" }) + "\n");
|
|
8187
8818
|
process.exitCode = receipt.exitCode;
|
|
8188
8819
|
} catch (cause) {
|
|
8189
8820
|
exitWithError(cause);
|
|
8190
8821
|
}
|
|
8191
8822
|
}
|
|
8192
8823
|
async function readProjectMode(project) {
|
|
8193
|
-
const file =
|
|
8824
|
+
const file = path8.join(project, ".hcm-delivery", "project.yml");
|
|
8194
8825
|
const content = await readSafeFile(project, file, "\u9879\u76EE\u914D\u7F6E");
|
|
8195
8826
|
const match = content.toString("utf8").match(
|
|
8196
8827
|
/^deliveryMode:\s*(?:"(plan-only|controlled)"|'(plan-only|controlled)'|(plan-only|controlled))\s*$/m
|
|
@@ -8202,7 +8833,7 @@ async function readProjectMode(project) {
|
|
|
8202
8833
|
return mode;
|
|
8203
8834
|
}
|
|
8204
8835
|
async function readChironLock(project) {
|
|
8205
|
-
const file =
|
|
8836
|
+
const file = path8.join(project, ".hcm-delivery", "chiron.lock.json");
|
|
8206
8837
|
const bytes = await readSafeFile(project, file, "Chiron \u9501");
|
|
8207
8838
|
let parsed;
|
|
8208
8839
|
try {
|
|
@@ -8219,9 +8850,9 @@ async function bindDocument(project, reference, label) {
|
|
|
8219
8850
|
if (!(relative2.startsWith("work/") || relative2.startsWith("decisions/"))) {
|
|
8220
8851
|
throw invalid4(`${label}\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE work/ \u6216 decisions/ \u4E0B`);
|
|
8221
8852
|
}
|
|
8222
|
-
const file =
|
|
8853
|
+
const file = path8.join(project, relative2);
|
|
8223
8854
|
const bytes = await readSafeFile(project, file, label);
|
|
8224
|
-
return { path: relative2, sha256:
|
|
8855
|
+
return { path: relative2, sha256: sha2567(bytes) };
|
|
8225
8856
|
}
|
|
8226
8857
|
async function assertDocumentUnchanged(project, document, label) {
|
|
8227
8858
|
const current = await bindDocument(project, document.path, label);
|
|
@@ -8232,7 +8863,7 @@ async function bindEvidenceDirectory(project, reference) {
|
|
|
8232
8863
|
if (!(relative2 === "evidence" || relative2.startsWith("evidence/"))) {
|
|
8233
8864
|
throw invalid4("evidence \u76EE\u5F55\u5FC5\u987B\u4F4D\u4E8E\u9879\u76EE evidence/ \u4E0B");
|
|
8234
8865
|
}
|
|
8235
|
-
const absolute =
|
|
8866
|
+
const absolute = path8.join(project, relative2);
|
|
8236
8867
|
await assertNoSymlinkComponents(project, relative2);
|
|
8237
8868
|
const stat = await fs8.stat(absolute).catch((cause) => {
|
|
8238
8869
|
throw invalid4(`evidence \u76EE\u5F55\u4E0D\u53EF\u7528\uFF1A${relative2}`, cause);
|
|
@@ -8254,7 +8885,7 @@ async function readPlan(project, file) {
|
|
|
8254
8885
|
function computePlanId(plan) {
|
|
8255
8886
|
const core = { ...plan };
|
|
8256
8887
|
delete core.planId;
|
|
8257
|
-
return
|
|
8888
|
+
return sha2567(canonicalJson(core)).slice(0, 24);
|
|
8258
8889
|
}
|
|
8259
8890
|
function assertGuardedAssessment(assessment, argv) {
|
|
8260
8891
|
if (assessment.risk !== "R2") throw invalid4("\u53EA\u6709 R2 \u547D\u4EE4\u53EF\u4EE5\u8FDB\u5165\u53D7\u63A7\u6267\u884C\u8BA1\u5212");
|
|
@@ -8393,13 +9024,13 @@ async function writeReceiptExclusive(target, receipt) {
|
|
|
8393
9024
|
});
|
|
8394
9025
|
await fs8.link(temp, target);
|
|
8395
9026
|
} catch (cause) {
|
|
8396
|
-
throw invalid4(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${
|
|
9027
|
+
throw invalid4(`\u6267\u884C\u6536\u636E\u5199\u5165\u5931\u8D25\u6216\u5DF2\u5B58\u5728\uFF1A${path8.basename(target)}`, cause);
|
|
8397
9028
|
} finally {
|
|
8398
9029
|
await fs8.unlink(temp).catch(() => void 0);
|
|
8399
9030
|
}
|
|
8400
9031
|
}
|
|
8401
9032
|
async function safeManagedDirectory(project, relative2, absolute) {
|
|
8402
|
-
await assertNoSymlinkComponents(project,
|
|
9033
|
+
await assertNoSymlinkComponents(project, path8.dirname(relative2));
|
|
8403
9034
|
const existing = await fs8.lstat(absolute).catch(() => void 0);
|
|
8404
9035
|
if (existing?.isSymbolicLink()) throw invalid4(`\u53D7\u7BA1\u76EE\u5F55\u4E0D\u80FD\u662F\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8405
9036
|
if (existing && !existing.isDirectory()) throw invalid4(`\u53D7\u7BA1\u8DEF\u5F84\u4E0D\u662F\u76EE\u5F55\uFF1A${relative2}`);
|
|
@@ -8416,8 +9047,8 @@ async function pathExists(file) {
|
|
|
8416
9047
|
);
|
|
8417
9048
|
}
|
|
8418
9049
|
async function readSafeFile(project, file, label) {
|
|
8419
|
-
const relative2 =
|
|
8420
|
-
if (relative2.startsWith("../") ||
|
|
9050
|
+
const relative2 = path8.relative(project, file).split(path8.sep).join("/");
|
|
9051
|
+
if (relative2.startsWith("../") || path8.isAbsolute(relative2))
|
|
8421
9052
|
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8422
9053
|
await assertNoSymlinkComponents(project, relative2);
|
|
8423
9054
|
let stat;
|
|
@@ -8434,14 +9065,14 @@ async function assertNoSymlinkComponents(project, relative2) {
|
|
|
8434
9065
|
const parts = relative2.split("/").filter(Boolean);
|
|
8435
9066
|
let current = project;
|
|
8436
9067
|
for (const part of parts) {
|
|
8437
|
-
current =
|
|
9068
|
+
current = path8.join(current, part);
|
|
8438
9069
|
const stat = await fs8.lstat(current).catch(() => void 0);
|
|
8439
9070
|
if (stat?.isSymbolicLink()) throw invalid4(`\u8DEF\u5F84\u5305\u542B\u7B26\u53F7\u94FE\u63A5\uFF1A${relative2}`);
|
|
8440
9071
|
if (!stat) break;
|
|
8441
9072
|
}
|
|
8442
9073
|
}
|
|
8443
9074
|
async function realProject(requested) {
|
|
8444
|
-
const absolute =
|
|
9075
|
+
const absolute = path8.resolve(requested);
|
|
8445
9076
|
let project;
|
|
8446
9077
|
try {
|
|
8447
9078
|
project = await fs8.realpath(absolute);
|
|
@@ -8453,10 +9084,10 @@ async function realProject(requested) {
|
|
|
8453
9084
|
return project;
|
|
8454
9085
|
}
|
|
8455
9086
|
function safeRelative(reference, label) {
|
|
8456
|
-
if (!reference ||
|
|
9087
|
+
if (!reference || path8.isAbsolute(reference) || /[\r\n\0]/.test(reference)) {
|
|
8457
9088
|
throw invalid4(`${label}\u5FC5\u987B\u662F\u9879\u76EE\u5185\u76F8\u5BF9\u8DEF\u5F84`);
|
|
8458
9089
|
}
|
|
8459
|
-
const normalized =
|
|
9090
|
+
const normalized = path8.normalize(reference).split(path8.sep).join("/");
|
|
8460
9091
|
if (normalized === ".." || normalized.startsWith("../") || normalized === ".") {
|
|
8461
9092
|
throw invalid4(`${label}\u8DEF\u5F84\u9003\u9038\u9879\u76EE`);
|
|
8462
9093
|
}
|
|
@@ -8492,11 +9123,11 @@ function canonicalJson(value) {
|
|
|
8492
9123
|
}
|
|
8493
9124
|
return JSON.stringify(value) ?? "null";
|
|
8494
9125
|
}
|
|
8495
|
-
function
|
|
8496
|
-
return
|
|
9126
|
+
function sha2567(value) {
|
|
9127
|
+
return createHash7("sha256").update(value).digest("hex");
|
|
8497
9128
|
}
|
|
8498
9129
|
async function sha256File2(file) {
|
|
8499
|
-
const hash =
|
|
9130
|
+
const hash = createHash7("sha256");
|
|
8500
9131
|
await new Promise((resolve3, reject) => {
|
|
8501
9132
|
const stream = createReadStream2(file);
|
|
8502
9133
|
stream.on("data", (chunk) => hash.update(chunk));
|
|
@@ -8506,32 +9137,32 @@ async function sha256File2(file) {
|
|
|
8506
9137
|
return hash.digest("hex");
|
|
8507
9138
|
}
|
|
8508
9139
|
function invalid4(message, cause) {
|
|
8509
|
-
return new
|
|
9140
|
+
return new CliError22({ code: CliErrorCode21.INVALID_ARGUMENT, message, cause });
|
|
8510
9141
|
}
|
|
8511
9142
|
|
|
8512
9143
|
// src/commands/user.ts
|
|
8513
9144
|
init_auth_guard();
|
|
8514
9145
|
init_exit();
|
|
8515
9146
|
import {
|
|
8516
|
-
HcmClient as
|
|
9147
|
+
HcmClient as HcmClient19,
|
|
8517
9148
|
adminResetPassword,
|
|
8518
|
-
formatObject as
|
|
8519
|
-
fromAxiosError as
|
|
8520
|
-
CliError as
|
|
8521
|
-
CliErrorCode as
|
|
9149
|
+
formatObject as formatObject21,
|
|
9150
|
+
fromAxiosError as fromAxiosError18,
|
|
9151
|
+
CliError as CliError23,
|
|
9152
|
+
CliErrorCode as CliErrorCode22
|
|
8522
9153
|
} from "@hcmai/sdk";
|
|
8523
9154
|
function buildAdminResetPasswordRequest(args, password) {
|
|
8524
9155
|
const hasId = Boolean(args.id);
|
|
8525
9156
|
const hasUsername = Boolean(args.username);
|
|
8526
9157
|
if (hasId === hasUsername) {
|
|
8527
|
-
throw new
|
|
8528
|
-
code:
|
|
9158
|
+
throw new CliError23({
|
|
9159
|
+
code: CliErrorCode22.INVALID_ARGUMENT,
|
|
8529
9160
|
message: "--id <uuid> \u4E0E --username <user> \u5FC5\u987B\u4E14\u53EA\u80FD\u63D0\u4F9B\u4E00\u4E2A"
|
|
8530
9161
|
});
|
|
8531
9162
|
}
|
|
8532
9163
|
if (!password) {
|
|
8533
|
-
throw new
|
|
8534
|
-
code:
|
|
9164
|
+
throw new CliError23({
|
|
9165
|
+
code: CliErrorCode22.INVALID_ARGUMENT,
|
|
8535
9166
|
message: "\u65B0\u5BC6\u7801\u4E0D\u80FD\u4E3A\u7A7A"
|
|
8536
9167
|
});
|
|
8537
9168
|
}
|
|
@@ -8547,12 +9178,12 @@ async function userResetPasswordCommand(args) {
|
|
|
8547
9178
|
try {
|
|
8548
9179
|
const password = args.passwordStdin ? await readSecretFromStdin() : await promptPasswordTwice();
|
|
8549
9180
|
const request = buildAdminResetPasswordRequest(args, password);
|
|
8550
|
-
const client3 =
|
|
9181
|
+
const client3 = HcmClient19.fromAuthContext(await getActiveAuthContextFromCli(args));
|
|
8551
9182
|
const result2 = await adminResetPassword(client3.raw(), request);
|
|
8552
|
-
process.stdout.write(
|
|
9183
|
+
process.stdout.write(formatObject21(result2, { format: args.output ?? "json" }) + "\n");
|
|
8553
9184
|
} catch (cause) {
|
|
8554
9185
|
const err = cause;
|
|
8555
|
-
if (err?.isAxiosError) exitWithError(
|
|
9186
|
+
if (err?.isAxiosError) exitWithError(fromAxiosError18(err, args.env));
|
|
8556
9187
|
exitWithError(cause);
|
|
8557
9188
|
}
|
|
8558
9189
|
}
|
|
@@ -8560,8 +9191,8 @@ async function promptPasswordTwice() {
|
|
|
8560
9191
|
const password = await promptSecret("\u65B0\u5BC6\u7801: ");
|
|
8561
9192
|
const confirmation = await promptSecret("\u518D\u6B21\u8F93\u5165\u65B0\u5BC6\u7801: ");
|
|
8562
9193
|
if (password !== confirmation) {
|
|
8563
|
-
throw new
|
|
8564
|
-
code:
|
|
9194
|
+
throw new CliError23({
|
|
9195
|
+
code: CliErrorCode22.INVALID_ARGUMENT,
|
|
8565
9196
|
message: "\u4E24\u6B21\u8F93\u5165\u7684\u65B0\u5BC6\u7801\u4E0D\u4E00\u81F4"
|
|
8566
9197
|
});
|
|
8567
9198
|
}
|
|
@@ -8607,8 +9238,9 @@ identity.command("use <name>").description("\u628A identity \u8BBE\u4E3A\u5176 e
|
|
|
8607
9238
|
identity.command("remove <name>").description("\u5220\u9664 identity\uFF08\u6E05 token + \u82E5\u662F env default \u5219\u6E05\u7A7A\uFF09").action((name) => identityRemove(name));
|
|
8608
9239
|
var user = program.command("user").description("\u7BA1\u7406\u79DF\u6237\u7528\u6237\u7684\u4E13\u7528\u51ED\u636E\u6D41\u7A0B");
|
|
8609
9240
|
user.command("reset-password").description("\u7531 admin.system \u5B89\u5168\u91CD\u7F6E\u7528\u6237\u5BC6\u7801\uFF0C\u5E76\u4F7F\u65E2\u6709\u4F1A\u8BDD\u5931\u6548").option("--id <uuid>", "\u76EE\u6807\u7528\u6237 ID\uFF08\u4E0E --username \u4E8C\u9009\u4E00\uFF09").option("--username <user>", "\u76EE\u6807\u7528\u6237\u540D\uFF08\u4E0E --id \u4E8C\u9009\u4E00\uFF09").option("--password-stdin", "\u4ECE\u6807\u51C6\u8F93\u5165\u8BFB\u53D6\u65B0\u5BC6\u7801\uFF08Agent/\u53D7\u63A7\u4EA4\u4ED8\u5FC5\u987B\u4F7F\u7528\uFF09").option("--no-force-change", "\u4E0B\u6B21\u767B\u5F55\u4E0D\u5F3A\u5236\u6539\u5BC6\uFF08\u4EC5\u9650\u5DF2\u6279\u51C6\u7684\u9694\u79BB\u6D4B\u8BD5\u73AF\u5883\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u5FC5\u987B\u5177\u5907 admin.system\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => userResetPasswordCommand(opts));
|
|
8610
|
-
var delivery = program.command("delivery").description("\u987E\u95EE\u4EA4\u4ED8\
|
|
9241
|
+
var delivery = program.command("delivery").description("\u987E\u95EE\u4EA4\u4ED8\uFF1A\u73B0\u573A\u63A2\u6D3B\u3001\u98CE\u9669\u8BC4\u4F30\u4E0E\u4E00\u6B21\u6027\u53D7\u63A7\u6267\u884C\u5408\u540C");
|
|
8611
9242
|
delivery.command("assess").description("\u8BC4\u4F30\u4E00\u6B21 HCM CLI argv\uFF1BAction \u98CE\u63A7\u58F0\u660E\u8BFB\u53D6\u81EA\u76EE\u6807 Core\uFF0C\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));
|
|
9243
|
+
delivery.command("doctor").description("\u56DB\u6761\u951A\u70B9\u63A2\u6D3B\uFF1A\u8FDE\u901A / \u6211\u662F\u8C01 / configAdmin / \u7AD9\u5728\u54EA\u4E00\u5C42").option("--env <name>", "\u76EE\u6807\u73AF\u5883\u522B\u540D").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").option("--capabilities <s>", "\u722C\u53D6\u65F6\u58F0\u660E\u7684 capability \u4E32\uFF08\u4F1A\u968F\u56DB\u5143\u7EC4\u7559\u6863\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => deliveryDoctorCommand(opts));
|
|
8612
9244
|
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));
|
|
8613
9245
|
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));
|
|
8614
9246
|
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(
|
|
@@ -8670,12 +9302,13 @@ meta.command("list").description("\u5217\u51FA\u79DF\u6237\u5143\u6570\u636E\u65
|
|
|
8670
9302
|
meta.command("get <path>").description("\u6253\u5370 meta \u539F\u6587\uFF08\u5982 metas/Employee.list.yml\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").action((p, opts) => metaGetCommand(p, opts));
|
|
8671
9303
|
meta.command("set <path>").description("\u5199\u5165 meta \u539F\u6587\uFF1B\u5199\u5B8C\u987B hcm cache clear-meta <Model> \u624D\u751F\u6548").requiredOption("--file <file>", "\u4ECE\u6587\u4EF6\u8BFB\u53D6\u539F\u6587\uFF08- \u8868\u793A stdin\uFF09").option("--dry-run", "\u53EA\u663E\u793A\u5C06\u5199\u5165\u7684\u5185\u5BB9\uFF0C\u4E0D\u5B9E\u9645\u5199\u5165").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((p, opts) => metaSetCommand(p, opts));
|
|
8672
9304
|
meta.command("delete <path>").description("\u5220\u9664 meta\uFF08\u4E0D\u53EF\u9006\uFF0C\u9ED8\u8BA4\u8FC7\u786E\u8BA4\u95F8\uFF09").option("-y, --yes", "\u8DF3\u8FC7\u786E\u8BA4").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((p, opts) => metaDeleteCommand(p, opts));
|
|
8673
|
-
var skills = program.command("skills").description("\
|
|
8674
|
-
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));
|
|
8675
|
-
skills.command("
|
|
8676
|
-
skills.command("
|
|
9305
|
+
var skills = program.command("skills").description("\u4EA4\u4ED8\u6280\u80FD\uFF1A\u777F\u620E Chiron \u6280\u80FD\u5E93 + CLI \u81EA\u5E26\u6280\u80FD\uFF08\u514D\u8BA4\u8BC1\uFF0C\u65E0\u9700\u5148 login\uFF09");
|
|
9306
|
+
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("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((kw, opts) => skillsListCommand(kw, opts));
|
|
9307
|
+
skills.command("manifest").description("\u6253\u5370\u672C CLI \u7684\u547D\u4EE4\u9762\u4E0E\u81EA\u5E26\u6280\u80FD\u6E05\u5355\uFF08\u4E0D\u8FDE\u7F51\uFF0C\u4F9B\u5B88\u536B\u4E0E\u5916\u90E8\u5DE5\u5177\u6BD4\u5BF9\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((opts) => skillsManifestCommand(program, opts));
|
|
9308
|
+
skills.command("show <id>").description("\u6253\u5370\u6280\u80FD\u539F\u6587\uFF08\u7BA1\u9053\u53CB\u597D\uFF09\uFF1B\u81EA\u5E26\u6280\u80FD\u76F4\u63A5\u8BFB\u672C\u5730\uFF0C\u4E0D\u8FDE\u7F51").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").action((id, opts) => skillsShowCommand(id, opts));
|
|
9309
|
+
skills.command("install [id]").description("\u8FDE\u540C requires \u524D\u7F6E\u6280\u80FD\u4E0E\u53C2\u8003\u6587\u4EF6\u88C5\u5230 agent \u6280\u80FD\u76EE\u5F55\uFF1B--all \u88C5\u6574\u4E2A\u6280\u80FD\u5E93").option("--all", "\u88C5\u6574\u4E2A\u6280\u80FD\u5E93\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D\uFF08\u5F80\u540E update \u4F1A\u81EA\u52A8\u8865\u4E0A\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\uFF09").option("--target <agent>", "\u88C5\u7ED9\u54EA\u4E2A agent\uFF1Aclaude\uFF08.claude/skills/\uFF09| codex\uFF08hcm-skills/ + AGENTS.md\uFF09| auto", "auto").option("--global", "\u88C5\u5230\u7528\u6237\u7EA7\u76EE\u5F55\u800C\u975E\u5F53\u524D\u9879\u76EE").option("--dir <path>", "\u88C5\u5230\u6307\u5B9A\u76EE\u5F55").option("--from <dir>", "\u4ECE\u672C\u5730\u76EE\u5F55\u88C5\uFF08\u73B0\u573A\u6539\u8FC7\u7684\u7248\u672C\uFF09\uFF0C\u4E0D\u8D70\u7F51\u7EDC").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--force", "\u8986\u76D6\u5DF2\u5B58\u5728\u7684\u540C\u540D\u6280\u80FD").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6 activeEnv \u7684 endpoint\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((id, opts) => skillsInstallCommand(id, opts));
|
|
8677
9310
|
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));
|
|
8678
|
-
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));
|
|
9311
|
+
skills.command("update").description("\u628A\u88C5\u8FC7\u7684\u6280\u80FD\u5347\u5230\u76EE\u6807\u73AF\u5883\u8FD9\u4E00\u7248\uFF08\u4EA7\u54C1\u5347\u7EA7\u540E\u8DD1\u5B83\uFF09").option("--all", "\u8FDE\u4EA7\u54C1\u65B0\u589E\u7684\u6280\u80FD\u4E00\u8D77\u88C5\u4E0A\uFF0C\u5E76\u8BB0\u4E3A\u300C\u8DDF\u968F\u5168\u96C6\u300D").option("--check", "\u53EA\u770B\u4F1A\u53D8\u4EC0\u4E48\uFF0C\u4E0D\u5199\u76D8").option("--target <agent>", "claude | codex | auto\uFF08\u9ED8\u8BA4\u6309\u5DF2\u5B89\u88C5\u76EE\u5F55\u63A2\uFF09", "auto").option("--global", "\u5347\u7EA7\u7528\u6237\u7EA7\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--dir <path>", "\u5347\u7EA7\u6307\u5B9A\u76EE\u5F55\u91CC\u7684\u6280\u80FD").option("--force", "\u8FDE\u672C\u5730\u6539\u8FC7\u7684\u6280\u80FD\u4E00\u8D77\u8986\u76D6\u6210\u4EA7\u54C1\u8FD9\u4E00\u7248").option("--source <kind>", "\u8FD9\u4E00\u8D9F\u770B\u54EA\u4E9B\u6765\u6E90\uFF1Acli\uFF08\u53EA\u770B\u81EA\u5E26\uFF0C\u4E0D\u8FDE\u7F51\uFF09| chiron | all", "all").option("--endpoint <url>", "\u6280\u80FD\u5E93\u5730\u5740\uFF08\u9ED8\u8BA4\u53D6\u5B89\u88C5\u65F6\u8BB0\u5F55\u7684\u5730\u5740\uFF09").option("--env <name>", "\u4ECE\u8BE5 env \u89E3\u6790 endpoint").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "table").action((opts) => skillsUpdateCommand(opts));
|
|
8679
9312
|
program.command("update <Model>").description("\u66F4\u65B0\u4E00\u6761 Model \u8BB0\u5F55\uFF08\u8D70\u540E\u7AEF\u5B8C\u6574\u6821\u9A8C\uFF09").requiredOption("--id <id>", "\u8BB0\u5F55 ID").option("--data <JSON>", `\u8981\u66F4\u65B0\u7684\u5B57\u6BB5 JSON\uFF0C\u4F8B\u5982 '{"name":"\u65B0\u540D\u79F0"}'`).option("--data-file <path>", "\u4ECE UTF-8 JSON \u6587\u4EF6\u8BFB\u53D6\u66F4\u65B0\u5B57\u6BB5\uFF08\u4E0E --data \u4E92\u65A5\uFF09").option("--env <name>", "\u4E34\u65F6\u5207\u73AF\u5883\uFF08\u8986\u76D6 activeEnv\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--as <identity>", "\u4E34\u65F6\u6362\u8EAB\u4EFD\uFF08\u8986\u76D6 env defaultIdentity\uFF0C\u672C\u6B21\u547D\u4EE4\uFF09").option("--output <fmt>", "\u8F93\u51FA\u683C\u5F0F\uFF1Atable | json | yaml", "json").action((model, opts) => updateCommand(model, opts));
|
|
8680
9313
|
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));
|
|
8681
9314
|
var setting = program.command("setting").description("\u79DF\u6237\u7CFB\u7EDF\u53C2\u6570\uFF08\u767B\u5F55\u7B56\u7565 / \u5BC6\u7801\u7B56\u7565 / \u529F\u80FD\u5F00\u5173 / \u96C6\u6210\u53C2\u6570\uFF09");
|
|
@@ -8695,9 +9328,14 @@ async function main() {
|
|
|
8695
9328
|
}
|
|
8696
9329
|
await program.parseAsync(process.argv);
|
|
8697
9330
|
}
|
|
8698
|
-
|
|
8699
|
-
|
|
9331
|
+
if (process.env.HCM_CLI_IMPORT_ONLY !== "1") {
|
|
9332
|
+
main().catch((e) => {
|
|
9333
|
+
process.stderr.write(`[hcm] fatal: ${String(e.message)}
|
|
8700
9334
|
`);
|
|
8701
|
-
|
|
8702
|
-
});
|
|
9335
|
+
process.exit(1);
|
|
9336
|
+
});
|
|
9337
|
+
}
|
|
9338
|
+
export {
|
|
9339
|
+
program
|
|
9340
|
+
};
|
|
8703
9341
|
//# sourceMappingURL=index.js.map
|