release-skill 0.9.6 → 0.9.8
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/.agents/plugins/marketplace.json +9 -0
- package/.claude-plugin/marketplace.json +11 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +2 -2
- package/.kimi-plugin/plugin.json +1 -1
- package/CHANGELOG.md +55 -0
- package/INSTALL.md +17 -9
- package/INSTALL.zh-CN.md +15 -9
- package/README.md +33 -28
- package/README.zh-CN.md +28 -26
- package/adapters/claude/.claude-plugin/marketplace.json +1 -1
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +357 -52
- package/adapters/claude/skills/release-finish/SKILL.md +12 -4
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +357 -52
- package/adapters/codex/skills/release-finish/SKILL.md +12 -4
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +357 -52
- package/adapters/kimi/skills/release-finish/SKILL.md +12 -4
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +357 -52
- package/adapters/workbuddy/skills/release-finish/SKILL.md +12 -4
- package/bin/release-skill.bundle.mjs +357 -52
- package/package.json +1 -1
- package/platform-manifest.json +4 -4
- package/skills/release-finish/SKILL.md +12 -4
- package/skills-src/release-finish/SKILL.md +12 -4
- package/src/commands/post-release-local.mjs +274 -39
- package/src/commands/prepare.mjs +5 -3
- package/src/commands/publish.mjs +30 -3
- package/src/commands/ship.mjs +4 -6
- package/src/commands/verify.mjs +22 -15
- package/src/core/postpublish.mjs +24 -0
- package/src/core/recovery.mjs +8 -6
- package/src/core/skill-resource-closure.mjs +19 -1
- package/src/core/surface-host-bindings.mjs +35 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "release-skill",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.8",
|
|
4
4
|
"description": "Safe preparation and frozen GitHub/npm production publishing with full happy end verification",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "广州市风荷科技有限公司"
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
],
|
|
20
20
|
"defaultPrompt": [
|
|
21
21
|
"Assess this project for release readiness.",
|
|
22
|
-
"Prepare a release plan for version 0.9.
|
|
22
|
+
"Prepare a release plan for version 0.9.8.",
|
|
23
23
|
"Help me understand the release workflow."
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -9,9 +9,9 @@ const __bundlePkgRoot = __bundleResolve(__bundleDirname(__bundleFileURLToPath(im
|
|
|
9
9
|
// Provide a real require() for CJS packages bundled into ESM (e.g. yaml, ajv).
|
|
10
10
|
const __bundleRealRequire = __bundleCreateRequire(import.meta.url);
|
|
11
11
|
// Package identity injected at build time — closure-independent --version probe.
|
|
12
|
-
const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.
|
|
12
|
+
const __bundlePkg = Object.freeze({"name":"release-skill","version":"0.9.8"});
|
|
13
13
|
// Build-time source digest for the BUNDLE_STALE freshness gate (see above).
|
|
14
|
-
const __bundleSourceDigest = "
|
|
14
|
+
const __bundleSourceDigest = "7fa9e4b55133e888e3f3ace288f0194eb1ac063c0616252d2dda8e58be7eee21";
|
|
15
15
|
|
|
16
16
|
var __create = Object.create;
|
|
17
17
|
var __defProp = Object.defineProperty;
|
|
@@ -45592,6 +45592,9 @@ function normalizePostPublishView(plan) {
|
|
|
45592
45592
|
{ planVersion: version }
|
|
45593
45593
|
);
|
|
45594
45594
|
}
|
|
45595
|
+
function requiresPostPublishDistribution(plan) {
|
|
45596
|
+
return normalizePostPublishView(plan).some((declaration) => (declaration.targets?.length ?? 0) > 0 || (declaration.hooks ?? []).some((hook) => hook.phase === void 0 || hook.phase === "distribute"));
|
|
45597
|
+
}
|
|
45595
45598
|
function validatePostPublishHookIdUniqueness(declarations) {
|
|
45596
45599
|
const explicitHookIdOwner = /* @__PURE__ */ new Map();
|
|
45597
45600
|
for (const declaration of declarations ?? []) {
|
|
@@ -45759,6 +45762,7 @@ var init_postpublish = __esm({
|
|
|
45759
45762
|
__name(validatePostPublishHookEntry, "validatePostPublishHookEntry");
|
|
45760
45763
|
__name(validatePostPublishDeclaration, "validatePostPublishDeclaration");
|
|
45761
45764
|
__name(normalizePostPublishView, "normalizePostPublishView");
|
|
45765
|
+
__name(requiresPostPublishDistribution, "requiresPostPublishDistribution");
|
|
45762
45766
|
__name(validatePostPublishHookIdUniqueness, "validatePostPublishHookIdUniqueness");
|
|
45763
45767
|
__name(postPublishActionId, "postPublishActionId");
|
|
45764
45768
|
__name(orderTargetsByDependency, "orderTargetsByDependency");
|
|
@@ -109520,9 +109524,27 @@ async function checkSkillResourceClosure({
|
|
|
109520
109524
|
result2.receiptDigest = digestDocument(receiptProjection(result2));
|
|
109521
109525
|
return result2;
|
|
109522
109526
|
}
|
|
109523
|
-
function evaluateDeclaredHostSurfaceCoverage(expectedHosts, surfaces) {
|
|
109527
|
+
function evaluateDeclaredHostSurfaceCoverage(expectedHosts, surfaces, coverageClaims = []) {
|
|
109524
109528
|
const missing = [];
|
|
109529
|
+
const observedById = new Map((surfaces ?? []).map((surface) => [surface.id, surface]));
|
|
109530
|
+
const claimsByHost = /* @__PURE__ */ new Map();
|
|
109531
|
+
for (const claim of coverageClaims ?? []) {
|
|
109532
|
+
const hostClaims = claimsByHost.get(claim.host) ?? [];
|
|
109533
|
+
hostClaims.push(claim);
|
|
109534
|
+
claimsByHost.set(claim.host, hostClaims);
|
|
109535
|
+
}
|
|
109525
109536
|
for (const expectedHost of [...new Set(expectedHosts ?? [])].sort((a, b) => a.localeCompare(b))) {
|
|
109537
|
+
const declaredClaims = claimsByHost.get(expectedHost) ?? [];
|
|
109538
|
+
if (declaredClaims.length > 0) {
|
|
109539
|
+
const missingClaim = declaredClaims.find((claim) => {
|
|
109540
|
+
const surface3 = observedById.get(claim.surfaceId);
|
|
109541
|
+
return !surface3 || !(surface3.skillCount >= 1);
|
|
109542
|
+
});
|
|
109543
|
+
if (!missingClaim) continue;
|
|
109544
|
+
const surface2 = observedById.get(missingClaim.surfaceId);
|
|
109545
|
+
missing.push({ host: expectedHost, skillCount: surface2?.skillCount ?? 0 });
|
|
109546
|
+
continue;
|
|
109547
|
+
}
|
|
109526
109548
|
const surface = (surfaces ?? []).find((item) => item.host === expectedHost);
|
|
109527
109549
|
if (!surface || !(surface.skillCount >= 1)) {
|
|
109528
109550
|
missing.push({ host: expectedHost, skillCount: surface?.skillCount ?? 0 });
|
|
@@ -112496,6 +112518,23 @@ async function deriveSurfaceHostBinding({ manifest, pluginRoot, platform, snapsh
|
|
|
112496
112518
|
const host = await normalizeHostId(platform.buildAdapter.name);
|
|
112497
112519
|
return { surfaceId, host };
|
|
112498
112520
|
}
|
|
112521
|
+
function groupSurfaceHostBindings(claims = []) {
|
|
112522
|
+
if (!Array.isArray(claims)) {
|
|
112523
|
+
throw new TypeError("surfaceHostBindings must be an array");
|
|
112524
|
+
}
|
|
112525
|
+
const bySurface = /* @__PURE__ */ new Map();
|
|
112526
|
+
for (const claim of claims) {
|
|
112527
|
+
if (!claim || typeof claim !== "object" || typeof claim.surfaceId !== "string" || typeof claim.host !== "string") {
|
|
112528
|
+
throw new TypeError("surfaceHostBindings entries must be objects with string surfaceId and host");
|
|
112529
|
+
}
|
|
112530
|
+
const existing = bySurface.get(claim.surfaceId) ?? [];
|
|
112531
|
+
existing.push({ surfaceId: claim.surfaceId, host: claim.host });
|
|
112532
|
+
bySurface.set(claim.surfaceId, existing);
|
|
112533
|
+
}
|
|
112534
|
+
const coverageClaims = [...bySurface.values()].flat().sort((left, right) => left.surfaceId.localeCompare(right.surfaceId) || left.host.localeCompare(right.host));
|
|
112535
|
+
const checkerBindings = [...bySurface.values()].filter((surfaceClaims) => surfaceClaims.length === 1).map(([claim]) => claim).sort((left, right) => left.surfaceId.localeCompare(right.surfaceId) || left.host.localeCompare(right.host));
|
|
112536
|
+
return { coverageClaims, checkerBindings };
|
|
112537
|
+
}
|
|
112499
112538
|
var SKILLS_NORMALIZERS;
|
|
112500
112539
|
var init_surface_host_bindings = __esm({
|
|
112501
112540
|
async "src/core/surface-host-bindings.mjs"() {
|
|
@@ -112513,6 +112552,7 @@ var init_surface_host_bindings = __esm({
|
|
|
112513
112552
|
__name(pluginRootFromManifestRelativePath, "pluginRootFromManifestRelativePath");
|
|
112514
112553
|
__name(assertDeclaredSkillsDirExists, "assertDeclaredSkillsDirExists");
|
|
112515
112554
|
__name(deriveSurfaceHostBinding, "deriveSurfaceHostBinding");
|
|
112555
|
+
__name(groupSurfaceHostBindings, "groupSurfaceHostBindings");
|
|
112516
112556
|
}
|
|
112517
112557
|
});
|
|
112518
112558
|
|
|
@@ -113321,8 +113361,7 @@ async function verifyRelease(options) {
|
|
|
113321
113361
|
{ sourceRunStatus: sourceRun.status }
|
|
113322
113362
|
);
|
|
113323
113363
|
}
|
|
113324
|
-
const
|
|
113325
|
-
const requiresDistribution = postPublishDeclarations.some((declaration) => (declaration.targets?.length ?? 0) > 0 || (declaration.hooks?.length ?? 0) > 0);
|
|
113364
|
+
const requiresDistribution = requiresPostPublishDistribution(plan);
|
|
113326
113365
|
if (requiresDistribution) {
|
|
113327
113366
|
await evidence.append({ phase: "verify", step: "distribute-run-discovery", status: "started" });
|
|
113328
113367
|
const distributeCandidates = await discoverDistributeRuns({ planPath, plan }) ?? [];
|
|
@@ -113943,11 +113982,16 @@ async function verifyRelease(options) {
|
|
|
113943
113982
|
const unit = (plan.units ?? []).find((u) => u.id === unitId);
|
|
113944
113983
|
const dist = unit?.distributions?.find((d) => d.type === platform.distributionType);
|
|
113945
113984
|
let binding = null;
|
|
113985
|
+
let frozenPluginRoot = null;
|
|
113946
113986
|
const contract2 = dist?.installationContract;
|
|
113947
113987
|
if (contract2?.normalizedManifest) {
|
|
113988
|
+
frozenPluginRoot = pluginRootFromManifestRelativePath(contract2.manifestRelativePath);
|
|
113948
113989
|
binding = await deriveSurfaceHostBinding({
|
|
113949
113990
|
manifest: contract2.normalizedManifest,
|
|
113950
|
-
|
|
113991
|
+
// Adapter observations are already rooted at the installed
|
|
113992
|
+
// plugin. The frozen manifest's skills path is therefore
|
|
113993
|
+
// relative to '.', not to the snapshot-root manifest path.
|
|
113994
|
+
pluginRoot: ".",
|
|
113951
113995
|
platform,
|
|
113952
113996
|
snapshotDir: check.observation.installPath
|
|
113953
113997
|
});
|
|
@@ -113960,6 +114004,7 @@ async function verifyRelease(options) {
|
|
|
113960
114004
|
unitId,
|
|
113961
114005
|
surfaceId: binding?.surfaceId ?? ".",
|
|
113962
114006
|
binding,
|
|
114007
|
+
frozenPluginRoot,
|
|
113963
114008
|
unit
|
|
113964
114009
|
});
|
|
113965
114010
|
}
|
|
@@ -114042,7 +114087,7 @@ async function verifyRelease(options) {
|
|
|
114042
114087
|
);
|
|
114043
114088
|
}
|
|
114044
114089
|
const pluginDistCount = (surface.unit?.distributions ?? []).filter((d) => d.type !== "npm").length;
|
|
114045
|
-
if (pluginDistCount === 1) {
|
|
114090
|
+
if (pluginDistCount === 1 && surface.frozenPluginRoot === ".") {
|
|
114046
114091
|
const observedReceipt = createSkillResourceClosureReceipt(closureResult, {
|
|
114047
114092
|
unitId: surface.unitId,
|
|
114048
114093
|
preparedAt: expectedUnitReceipt.preparedAt ?? null,
|
|
@@ -114505,7 +114550,7 @@ async function readRunRecovery(runPath, options = {}) {
|
|
|
114505
114550
|
}
|
|
114506
114551
|
} else if (["publish", "reconcile"].includes(command2)) {
|
|
114507
114552
|
if (run6.status === "PUBLISHED") {
|
|
114508
|
-
const needsDistribution =
|
|
114553
|
+
const needsDistribution = requiresPostPublishDistribution(plan);
|
|
114509
114554
|
code = needsDistribution ? "DISTRIBUTE" : "VERIFY";
|
|
114510
114555
|
} else if (run6.status === "PARTIAL") {
|
|
114511
114556
|
code = "RECONCILE";
|
|
@@ -129636,10 +129681,11 @@ async function runPrepareSkillResourceClosureGate({
|
|
|
129636
129681
|
});
|
|
129637
129682
|
if (binding) surfaceHostBindings.push(binding);
|
|
129638
129683
|
}
|
|
129684
|
+
const { checkerBindings, coverageClaims } = groupSurfaceHostBindings(surfaceHostBindings);
|
|
129639
129685
|
const closureResult = await checkSkillResourceClosure({
|
|
129640
129686
|
snapshotDir: manifest.outputDir,
|
|
129641
129687
|
host: "root",
|
|
129642
|
-
surfaceHostBindings
|
|
129688
|
+
surfaceHostBindings: checkerBindings
|
|
129643
129689
|
});
|
|
129644
129690
|
const receipt = createSkillResourceClosureReceipt(closureResult, {
|
|
129645
129691
|
unitId: unit.id,
|
|
@@ -129684,7 +129730,8 @@ async function runPrepareSkillResourceClosureGate({
|
|
|
129684
129730
|
}
|
|
129685
129731
|
const hostCoverage = evaluateDeclaredHostSurfaceCoverage(
|
|
129686
129732
|
expectedHosts,
|
|
129687
|
-
closureResult.surfaces
|
|
129733
|
+
closureResult.surfaces,
|
|
129734
|
+
coverageClaims
|
|
129688
129735
|
);
|
|
129689
129736
|
if (!hostCoverage.passed) {
|
|
129690
129737
|
await evidence.append({
|
|
@@ -129708,7 +129755,7 @@ async function runPrepareSkillResourceClosureGate({
|
|
|
129708
129755
|
}
|
|
129709
129756
|
);
|
|
129710
129757
|
}
|
|
129711
|
-
for (const binding of
|
|
129758
|
+
for (const binding of coverageClaims) {
|
|
129712
129759
|
const boundSurface = closureResult.surfaces.find(
|
|
129713
129760
|
(surface) => surface.id === binding.surfaceId
|
|
129714
129761
|
);
|
|
@@ -131770,7 +131817,7 @@ function hostEnvironment(host, { kimiHome } = {}) {
|
|
|
131770
131817
|
env.CODEBUDDY_CONFIG_DIR = join27(env.HOME, ".workbuddy");
|
|
131771
131818
|
env.WORKBUDDY_CONFIG_DIR = join27(env.HOME, ".workbuddy");
|
|
131772
131819
|
}
|
|
131773
|
-
if (host === "kimi" && kimiHome) env.
|
|
131820
|
+
if (host === "kimi" && kimiHome) env.KIMI_CODE_HOME = kimiHome;
|
|
131774
131821
|
return env;
|
|
131775
131822
|
}
|
|
131776
131823
|
async function defaultRun(command2, args2, options = {}) {
|
|
@@ -131947,6 +131994,64 @@ function exactPluginObservation(target, stdout) {
|
|
|
131947
131994
|
function normalizeGitSource(source) {
|
|
131948
131995
|
return String(source ?? "").replace(/^git\+/, "").replace(/^https:\/\/github\.com\//, "").replace(/^git@github\.com:/, "").replace(/\.git$/u, "").replace(/\/$/u, "");
|
|
131949
131996
|
}
|
|
131997
|
+
function parseFrozenRemoteRef(target, stdout) {
|
|
131998
|
+
const directRefs = /* @__PURE__ */ new Map();
|
|
131999
|
+
const peeledRefs = /* @__PURE__ */ new Map();
|
|
132000
|
+
const expected = target.marketplaceRef;
|
|
132001
|
+
const allowedDirect = expected.startsWith("refs/") ? /* @__PURE__ */ new Set([expected]) : /* @__PURE__ */ new Set([`refs/heads/${expected}`, `refs/tags/${expected}`]);
|
|
132002
|
+
for (const line of String(stdout ?? "").split(/\r?\n/u)) {
|
|
132003
|
+
if (line.length === 0) continue;
|
|
132004
|
+
const match = /^([a-f0-9]{40})\t([^\s]+)$/u.exec(line);
|
|
132005
|
+
if (!match) throw new Error("git ls-remote returned an invalid line");
|
|
132006
|
+
const [, commit, remoteRef] = match;
|
|
132007
|
+
const peeled = remoteRef.endsWith("^{}");
|
|
132008
|
+
const direct = peeled ? remoteRef.slice(0, -3) : remoteRef;
|
|
132009
|
+
if (!allowedDirect.has(direct)) {
|
|
132010
|
+
throw new Error(`git ls-remote returned an unexpected ref ${remoteRef}`);
|
|
132011
|
+
}
|
|
132012
|
+
const destination = peeled ? peeledRefs : directRefs;
|
|
132013
|
+
const previous = destination.get(direct);
|
|
132014
|
+
if (previous && previous !== commit) {
|
|
132015
|
+
throw new Error(`git ls-remote returned conflicting values for ${remoteRef}`);
|
|
132016
|
+
}
|
|
132017
|
+
destination.set(direct, commit);
|
|
132018
|
+
}
|
|
132019
|
+
const resolved = [...allowedDirect].filter((remoteRef) => directRefs.has(remoteRef) || peeledRefs.has(remoteRef)).map((remoteRef) => peeledRefs.get(remoteRef) ?? directRefs.get(remoteRef));
|
|
132020
|
+
return {
|
|
132021
|
+
found: resolved.length > 0,
|
|
132022
|
+
exact: resolved.length > 0 && resolved.every((commit) => commit === target.marketplaceCommit)
|
|
132023
|
+
};
|
|
132024
|
+
}
|
|
132025
|
+
async function preflightStructuredMarketplace(target, env, run6) {
|
|
132026
|
+
try {
|
|
132027
|
+
const observed = await run6("git", [
|
|
132028
|
+
"ls-remote",
|
|
132029
|
+
"--exit-code",
|
|
132030
|
+
`https://${target.githubHost}/${target.marketplaceRepo}.git`,
|
|
132031
|
+
target.marketplaceRef,
|
|
132032
|
+
`${target.marketplaceRef}^{}`
|
|
132033
|
+
], { env, timeout: 3e4 });
|
|
132034
|
+
const remote = parseFrozenRemoteRef(target, observed.stdout);
|
|
132035
|
+
if (!remote.found) {
|
|
132036
|
+
return {
|
|
132037
|
+
ok: false,
|
|
132038
|
+
reason: `${target.host} frozen marketplace ref ${target.marketplaceRef} is missing`
|
|
132039
|
+
};
|
|
132040
|
+
}
|
|
132041
|
+
if (!remote.exact) {
|
|
132042
|
+
return {
|
|
132043
|
+
ok: false,
|
|
132044
|
+
reason: `${target.host} frozen marketplace ref ${target.marketplaceRef} does not resolve to ${target.marketplaceCommit}`
|
|
132045
|
+
};
|
|
132046
|
+
}
|
|
132047
|
+
return { ok: true };
|
|
132048
|
+
} catch (error) {
|
|
132049
|
+
return {
|
|
132050
|
+
ok: false,
|
|
132051
|
+
reason: `${target.host} could not prove the frozen marketplace ref: ${error?.message ?? String(error)}`
|
|
132052
|
+
};
|
|
132053
|
+
}
|
|
132054
|
+
}
|
|
131950
132055
|
async function observeMarketplace(target, command2, env, run6) {
|
|
131951
132056
|
const listed = await run6(command2, ["plugin", "marketplace", "list", "--json"], { env });
|
|
131952
132057
|
const parsed = parseJson2(listed.stdout, `${target.host} marketplace list`);
|
|
@@ -132132,6 +132237,10 @@ async function runStructuredUpdate(target, detected, run6, {
|
|
|
132132
132237
|
});
|
|
132133
132238
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
132134
132239
|
}
|
|
132240
|
+
if (!before.marketplace.exact) {
|
|
132241
|
+
const preflight = await preflightStructuredMarketplace(target, env, run6);
|
|
132242
|
+
if (!preflight.ok) return { status: "MANUAL_REQUIRED", reason: preflight.reason };
|
|
132243
|
+
}
|
|
132135
132244
|
const marketplaceRebound = await bindStructuredMarketplace(target, command2, env, run6, before);
|
|
132136
132245
|
const current = target.host === "claude" && marketplaceRebound ? await observeStructuredTarget(target, command2, env, run6) : before;
|
|
132137
132246
|
const platform = getPlatform(target.host);
|
|
@@ -132185,48 +132294,204 @@ function parseCodeBuddyRemoteObservation(target, stdout) {
|
|
|
132185
132294
|
};
|
|
132186
132295
|
}
|
|
132187
132296
|
function kimiExpectProgram({ removePlugin } = {}) {
|
|
132188
|
-
const removeCommand = removePlugin ? `
|
|
132297
|
+
const removeCommand = removePlugin ? `submitCommand "/plugins remove $removePlugin"
|
|
132189
132298
|
expect {
|
|
132190
132299
|
-nocase -re {(remove|delete|uninstall).*(confirm|sure)|(confirm|sure).*(remove|delete|uninstall)} {
|
|
132191
132300
|
expect {
|
|
132192
|
-
-ex $removePlugin {
|
|
132193
|
-
|
|
132194
|
-
|
|
132301
|
+
-ex $removePlugin {
|
|
132302
|
+
send -- "\\033\\[B"
|
|
132303
|
+
send -- "\\033\\[13u"
|
|
132304
|
+
}
|
|
132305
|
+
timeout { failTimeout remove-confirmation 125 127 }
|
|
132306
|
+
eof { failEof remove-confirmation 126 }
|
|
132307
|
+
}
|
|
132308
|
+
expect {
|
|
132309
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132310
|
+
-re $promptPattern {}
|
|
132311
|
+
timeout { failTimeout remove-prompt 128 130 }
|
|
132312
|
+
eof { failEof remove-prompt 129 }
|
|
132195
132313
|
}
|
|
132196
|
-
expect -re $promptPattern
|
|
132197
132314
|
}
|
|
132198
132315
|
-re $promptPattern {}
|
|
132199
|
-
|
|
132200
|
-
|
|
132316
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132317
|
+
timeout { failTimeout remove-dialog 122 124 }
|
|
132318
|
+
eof { failEof remove-dialog 123 }
|
|
132201
132319
|
}
|
|
132202
132320
|
` : "";
|
|
132203
|
-
return `set timeout
|
|
132204
|
-
foreach variable {RELEASE_SKILL_KIMI_COMMAND RELEASE_SKILL_KIMI_INSTALL_URL RELEASE_SKILL_KIMI_REMOVE_PLUGIN} {
|
|
132321
|
+
return `set timeout 240
|
|
132322
|
+
foreach variable {RELEASE_SKILL_KIMI_COMMAND RELEASE_SKILL_KIMI_INSTALL_URL RELEASE_SKILL_KIMI_REMOVE_PLUGIN RELEASE_SKILL_KIMI_EXPECTED_REPO RELEASE_SKILL_KIMI_EXPECTED_TAG} {
|
|
132205
132323
|
if {![info exists env($variable)]} { exit 90 }
|
|
132206
132324
|
}
|
|
132207
132325
|
set kimiCommand $env(RELEASE_SKILL_KIMI_COMMAND)
|
|
132208
132326
|
set installUrl $env(RELEASE_SKILL_KIMI_INSTALL_URL)
|
|
132209
132327
|
set removePlugin $env(RELEASE_SKILL_KIMI_REMOVE_PLUGIN)
|
|
132210
|
-
set
|
|
132328
|
+
set expectedRepo $env(RELEASE_SKILL_KIMI_EXPECTED_REPO)
|
|
132329
|
+
set expectedTag $env(RELEASE_SKILL_KIMI_EXPECTED_TAG)
|
|
132330
|
+
set promptPattern {(?:(?:^|\\r|\\n)> (?:\\r*\\n|$)|(?:^|\\r|\\n)(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*\u2502[^\\r\\n]*>[^\\r\\n]*\u2502(?:(?:\\033\\[[0-9;?]*[ -/]*[@-~])|\\033\\][^\\x07]*\\x07|[ \\t])*(?:\\r*\\n|$))}
|
|
132331
|
+
|
|
132332
|
+
proc cleanScreen {value} {
|
|
132333
|
+
regsub -all {\\033\\[[0-9;?]*[ -/]*[@-~]} $value {} value
|
|
132334
|
+
regsub -all {\\033\\][^\\x07]*\\x07} $value {} value
|
|
132335
|
+
regsub -all {\\r} $value {} value
|
|
132336
|
+
return $value
|
|
132337
|
+
}
|
|
132338
|
+
|
|
132339
|
+
proc compactScreen {value} {
|
|
132340
|
+
set value [cleanScreen $value]
|
|
132341
|
+
regsub -all {[[:space:]]+} $value {} value
|
|
132342
|
+
return [string tolower $value]
|
|
132343
|
+
}
|
|
132344
|
+
|
|
132345
|
+
proc extractPluginTrustDialog {value} {
|
|
132346
|
+
set cleaned [cleanScreen $value]
|
|
132347
|
+
set lowered [string tolower $cleaned]
|
|
132348
|
+
set dialogStart -1
|
|
132349
|
+
foreach marker {"install third-party plugin " "trust and install from "} {
|
|
132350
|
+
set markerStart [string last $marker $lowered]
|
|
132351
|
+
if {$markerStart > $dialogStart} { set dialogStart $markerStart }
|
|
132352
|
+
}
|
|
132353
|
+
if {$dialogStart < 0} { return "" }
|
|
132354
|
+
return [string range $cleaned $dialogStart end]
|
|
132355
|
+
}
|
|
132356
|
+
|
|
132357
|
+
proc failTimeout {state timeoutCode unknownCode} {
|
|
132358
|
+
global expect_out
|
|
132359
|
+
set buffer ""
|
|
132360
|
+
if {[info exists expect_out(buffer)]} { set buffer [string trim [cleanScreen $expect_out(buffer)]] }
|
|
132361
|
+
if {$buffer ne ""} {
|
|
132362
|
+
puts stderr "KIMI_TUI_STATE:$state:unknown"
|
|
132363
|
+
exit $unknownCode
|
|
132364
|
+
}
|
|
132365
|
+
puts stderr "KIMI_TUI_STATE:$state:timeout"
|
|
132366
|
+
exit $timeoutCode
|
|
132367
|
+
}
|
|
132368
|
+
|
|
132369
|
+
proc failEof {state code} {
|
|
132370
|
+
puts stderr "KIMI_TUI_STATE:$state:eof"
|
|
132371
|
+
exit $code
|
|
132372
|
+
}
|
|
132373
|
+
|
|
132374
|
+
proc directoryTrust {} {
|
|
132375
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:manual-required"
|
|
132376
|
+
exit 80
|
|
132377
|
+
}
|
|
132378
|
+
|
|
132379
|
+
proc submitCommand {command} {
|
|
132380
|
+
send -- "\\033\\[200~"
|
|
132381
|
+
send -- $command
|
|
132382
|
+
send -- "\\033\\[201~"
|
|
132383
|
+
send -- "\\033\\[13u"
|
|
132384
|
+
}
|
|
132385
|
+
|
|
132211
132386
|
spawn $kimiCommand
|
|
132212
|
-
|
|
132213
|
-
|
|
132387
|
+
if {[catch {exec stty columns 240 rows 60 < $spawn_out(slave,name)} resizeError]} {
|
|
132388
|
+
puts stderr "KIMI_TUI_STATE:terminal-size:failed"
|
|
132389
|
+
exit 131
|
|
132390
|
+
}
|
|
132214
132391
|
expect {
|
|
132215
|
-
-nocase -re {
|
|
132216
|
-
|
|
132217
|
-
|
|
132218
|
-
|
|
132219
|
-
|
|
132392
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132393
|
+
-re $promptPattern {}
|
|
132394
|
+
timeout { failTimeout initial-prompt 101 103 }
|
|
132395
|
+
eof { failEof initial-prompt 102 }
|
|
132396
|
+
}
|
|
132397
|
+
${removeCommand}submitCommand "/plugins install $installUrl"
|
|
132398
|
+
set dialogBuffer ""
|
|
132399
|
+
expect {
|
|
132400
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132401
|
+
-nocase -re {(?:Install third-party plugin|Trust and install from)[ \\t]} {
|
|
132402
|
+
append dialogBuffer $expect_out(buffer)
|
|
132403
|
+
}
|
|
132404
|
+
timeout { failTimeout plugin-trust-anchor 104 106 }
|
|
132405
|
+
eof { failEof plugin-trust-anchor 105 }
|
|
132406
|
+
}
|
|
132407
|
+
expect {
|
|
132408
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132409
|
+
-nocase -re {\u276F[^\\r\\n]*(?:Exit|Cancel|Trust and install)} {
|
|
132410
|
+
append dialogBuffer $expect_out(buffer)
|
|
132411
|
+
}
|
|
132412
|
+
-re {\u276F[^\\r\\n]*\\r*\\n} {
|
|
132413
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust-selected-row:unknown"
|
|
132414
|
+
exit 134
|
|
132415
|
+
}
|
|
132416
|
+
timeout {
|
|
132417
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust-selected-row:timeout"
|
|
132418
|
+
exit 132
|
|
132419
|
+
}
|
|
132420
|
+
eof { failEof plugin-trust-selected-row 133 }
|
|
132421
|
+
}
|
|
132422
|
+
|
|
132423
|
+
set dialog [extractPluginTrustDialog $dialogBuffer]
|
|
132424
|
+
if {$dialog eq ""} {
|
|
132425
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust:dialog-unknown"
|
|
132426
|
+
exit 113
|
|
132427
|
+
}
|
|
132428
|
+
set compactDialog [compactScreen $dialog]
|
|
132429
|
+
if {[string first [string tolower [compactScreen $expectedRepo]] $compactDialog] < 0} {
|
|
132430
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust:repo-mismatch"
|
|
132431
|
+
exit 111
|
|
132432
|
+
}
|
|
132433
|
+
if {[string first [string tolower [compactScreen $expectedTag]] $compactDialog] < 0} {
|
|
132434
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust:tag-mismatch"
|
|
132435
|
+
exit 112
|
|
132436
|
+
}
|
|
132437
|
+
if {[regexp -nocase {(^|\\n)[^\\n]*\u276F[^\\n]*trust and install} $dialog]} {
|
|
132438
|
+
send -- "\\033\\[13u"
|
|
132439
|
+
} elseif {[regexp -nocase {(^|\\n)[^\\n]*\u276F[^\\n]*(cancel|exit)} $dialog]} {
|
|
132440
|
+
send -- "\\033\\[B"
|
|
132441
|
+
expect {
|
|
132442
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132443
|
+
-nocase -re {\u276F[^\\r\\n]*Trust and install} {}
|
|
132444
|
+
-re {\u276F[^\\r\\n]*\\r*\\n} {
|
|
132445
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust-confirm-selection:unknown"
|
|
132446
|
+
exit 109
|
|
132220
132447
|
}
|
|
132448
|
+
timeout { failTimeout plugin-trust-confirm-selection 107 109 }
|
|
132449
|
+
eof { failEof plugin-trust-confirm-selection 108 }
|
|
132450
|
+
}
|
|
132451
|
+
send -- "\\033\\[13u"
|
|
132452
|
+
} else {
|
|
132453
|
+
puts stderr "KIMI_TUI_STATE:plugin-trust:selection-unknown"
|
|
132454
|
+
exit 113
|
|
132455
|
+
}
|
|
132456
|
+
|
|
132457
|
+
expect {
|
|
132458
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132459
|
+
-nocase -re {Install finished[^\\r\\n]*see details below\\.} {}
|
|
132460
|
+
-nocase -re {Installing plugin from[^\\r\\n]*(?:\\r|\\n)} {
|
|
132461
|
+
exp_continue -continue_timer
|
|
132462
|
+
}
|
|
132463
|
+
-nocase -re {Install failed:[^\\r\\n]*} {
|
|
132464
|
+
puts stderr "KIMI_TUI_STATE:install-result:failed"
|
|
132465
|
+
exit 135
|
|
132466
|
+
}
|
|
132467
|
+
-nocase -re {(^|\\r|\\n)Install[^\\r\\n]*\\r*\\n} {
|
|
132468
|
+
puts stderr "KIMI_TUI_STATE:install-result:unknown"
|
|
132469
|
+
exit 138
|
|
132470
|
+
}
|
|
132471
|
+
timeout {
|
|
132472
|
+
puts stderr "KIMI_TUI_STATE:install-result:timeout"
|
|
132473
|
+
exit 136
|
|
132221
132474
|
}
|
|
132222
|
-
|
|
132223
|
-
|
|
132475
|
+
eof { failEof install-result 137 }
|
|
132476
|
+
}
|
|
132477
|
+
expect {
|
|
132478
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132479
|
+
-re $promptPattern {}
|
|
132480
|
+
timeout { failTimeout post-install-prompt 114 116 }
|
|
132481
|
+
eof { failEof post-install-prompt 115 }
|
|
132482
|
+
}
|
|
132483
|
+
submitCommand "/reload"
|
|
132484
|
+
expect {
|
|
132485
|
+
-nocase -re {Trust this folder\\?} { directoryTrust }
|
|
132486
|
+
-re $promptPattern {}
|
|
132487
|
+
timeout { failTimeout reload-prompt 117 119 }
|
|
132488
|
+
eof { failEof reload-prompt 118 }
|
|
132489
|
+
}
|
|
132490
|
+
submitCommand "/exit"
|
|
132491
|
+
expect {
|
|
132492
|
+
eof { puts stderr "KIMI_TUI_STATE:exit-eof:eof" }
|
|
132493
|
+
timeout { failTimeout exit-eof 120 121 }
|
|
132224
132494
|
}
|
|
132225
|
-
expect -re $promptPattern
|
|
132226
|
-
send -- "/reload\\r"
|
|
132227
|
-
expect -re $promptPattern
|
|
132228
|
-
send -- "/exit\\r"
|
|
132229
|
-
expect eof
|
|
132230
132495
|
`;
|
|
132231
132496
|
}
|
|
132232
132497
|
async function observeKimiTarget(target, kimiHome, run6) {
|
|
@@ -132294,7 +132559,7 @@ async function runKimiUpdate(target, detected, run6, kimiHome, {
|
|
|
132294
132559
|
});
|
|
132295
132560
|
return { status: "ALREADY_CURRENT", version: target.version };
|
|
132296
132561
|
}
|
|
132297
|
-
await withTemporaryWorkspace(async (workspace) => {
|
|
132562
|
+
const tuiOutcome = await withTemporaryWorkspace(async (workspace) => {
|
|
132298
132563
|
const checkout = join27(workspace.root, "plugin");
|
|
132299
132564
|
await run6("git", [
|
|
132300
132565
|
"clone",
|
|
@@ -132315,18 +132580,31 @@ async function runKimiUpdate(target, detected, run6, kimiHome, {
|
|
|
132315
132580
|
}
|
|
132316
132581
|
const installUrl = `https://github.com/${target.pluginRepo}/releases/tag/${target.pluginTag}`;
|
|
132317
132582
|
const removePlugin = before.source === "legacy" ? target.plugin : "";
|
|
132318
|
-
|
|
132319
|
-
|
|
132320
|
-
|
|
132321
|
-
|
|
132322
|
-
|
|
132323
|
-
|
|
132324
|
-
|
|
132325
|
-
|
|
132326
|
-
|
|
132583
|
+
try {
|
|
132584
|
+
await run6(detected.expectCommand, ["-c", kimiExpectProgram({
|
|
132585
|
+
...removePlugin ? { removePlugin } : {}
|
|
132586
|
+
})], {
|
|
132587
|
+
timeout: Math.max(3e5, target.timeoutMs),
|
|
132588
|
+
env: {
|
|
132589
|
+
...env,
|
|
132590
|
+
RELEASE_SKILL_KIMI_COMMAND: detected.command,
|
|
132591
|
+
RELEASE_SKILL_KIMI_INSTALL_URL: installUrl,
|
|
132592
|
+
RELEASE_SKILL_KIMI_REMOVE_PLUGIN: removePlugin,
|
|
132593
|
+
RELEASE_SKILL_KIMI_EXPECTED_REPO: `https://github.com/${target.pluginRepo}`,
|
|
132594
|
+
RELEASE_SKILL_KIMI_EXPECTED_TAG: target.pluginTag
|
|
132595
|
+
}
|
|
132596
|
+
});
|
|
132597
|
+
} catch (error) {
|
|
132598
|
+
if (error?.exitStatus === 80) {
|
|
132599
|
+
return {
|
|
132600
|
+
status: "MANUAL_REQUIRED",
|
|
132601
|
+
reason: "Kimi requires folder trust; release-finish did not confirm the folder or continue installation"
|
|
132602
|
+
};
|
|
132327
132603
|
}
|
|
132328
|
-
|
|
132604
|
+
throw error;
|
|
132605
|
+
}
|
|
132329
132606
|
}, { prefix: "release-skill-kimi-update-" });
|
|
132607
|
+
if (tuiOutcome) return tuiOutcome;
|
|
132330
132608
|
const after = await observeKimiTarget(target, kimiHome, run6);
|
|
132331
132609
|
if (!after.exact) throw new Error("Kimi did not report the frozen plugin identity after TUI installation");
|
|
132332
132610
|
await verifyStructuredInstalledPayload({
|
|
@@ -132373,7 +132651,7 @@ async function updateLocalHostPluginsInternal({
|
|
|
132373
132651
|
kimiHome,
|
|
132374
132652
|
verifyInstalledPayload = verifyInstalledMarketplacePayload
|
|
132375
132653
|
} = {}) {
|
|
132376
|
-
const effectiveKimiHome = kimiHome ?? process.env.
|
|
132654
|
+
const effectiveKimiHome = kimiHome ?? process.env.KIMI_CODE_HOME ?? join27(homedir(), ".kimi-code");
|
|
132377
132655
|
const checklist = derivePostReleaseChecklist(plan, { postVerifyComplete: true });
|
|
132378
132656
|
if (confirmPlanDigest !== plan.digest) {
|
|
132379
132657
|
throw new Error("plan digest confirmation does not match the frozen release plan");
|
|
@@ -132462,7 +132740,7 @@ var init_post_release_local = __esm({
|
|
|
132462
132740
|
"NODE_EXTRA_CA_CERTS",
|
|
132463
132741
|
"CLAUDE_CONFIG_DIR",
|
|
132464
132742
|
"CODEX_HOME",
|
|
132465
|
-
"
|
|
132743
|
+
"KIMI_CODE_HOME",
|
|
132466
132744
|
"CODEBUDDY_CONFIG_DIR",
|
|
132467
132745
|
"WORKBUDDY_CONFIG_DIR"
|
|
132468
132746
|
]);
|
|
@@ -132495,6 +132773,8 @@ var init_post_release_local = __esm({
|
|
|
132495
132773
|
__name(observeCodeBuddyMarketplaceAfterResidual, "observeCodeBuddyMarketplaceAfterResidual");
|
|
132496
132774
|
__name(exactPluginObservation, "exactPluginObservation");
|
|
132497
132775
|
__name(normalizeGitSource, "normalizeGitSource");
|
|
132776
|
+
__name(parseFrozenRemoteRef, "parseFrozenRemoteRef");
|
|
132777
|
+
__name(preflightStructuredMarketplace, "preflightStructuredMarketplace");
|
|
132498
132778
|
__name(observeMarketplace, "observeMarketplace");
|
|
132499
132779
|
__name(observeStructuredTarget, "observeStructuredTarget");
|
|
132500
132780
|
__name(bindStructuredMarketplace, "bindStructuredMarketplace");
|
|
@@ -133318,10 +133598,11 @@ async function publishRelease(options) {
|
|
|
133318
133598
|
});
|
|
133319
133599
|
if (binding) surfaceHostBindings.push(binding);
|
|
133320
133600
|
}
|
|
133601
|
+
const { checkerBindings, coverageClaims } = groupSurfaceHostBindings(surfaceHostBindings);
|
|
133321
133602
|
const closureResult = await checkSkillResourceClosure({
|
|
133322
133603
|
snapshotDir,
|
|
133323
133604
|
host: "root",
|
|
133324
|
-
surfaceHostBindings
|
|
133605
|
+
surfaceHostBindings: checkerBindings
|
|
133325
133606
|
});
|
|
133326
133607
|
if (closureResult.findings.length > 0) {
|
|
133327
133608
|
await evidence.append({
|
|
@@ -133337,6 +133618,31 @@ async function publishRelease(options) {
|
|
|
133337
133618
|
{ unitId, findings: closureResult.findings }
|
|
133338
133619
|
);
|
|
133339
133620
|
}
|
|
133621
|
+
const expectedHosts = [];
|
|
133622
|
+
for (const distribution of frozenUnit?.distributions ?? []) {
|
|
133623
|
+
const platform = PLATFORMS.find((item) => item.distributionType === distribution.type);
|
|
133624
|
+
if (platform) expectedHosts.push(await normalizeHostId(platform.buildAdapter.name));
|
|
133625
|
+
}
|
|
133626
|
+
const hostCoverage = evaluateDeclaredHostSurfaceCoverage(
|
|
133627
|
+
expectedHosts,
|
|
133628
|
+
closureResult.surfaces,
|
|
133629
|
+
coverageClaims
|
|
133630
|
+
);
|
|
133631
|
+
if (!hostCoverage.passed) {
|
|
133632
|
+
await evidence.append({
|
|
133633
|
+
phase: "safety-gate",
|
|
133634
|
+
gate: "skill-resource-closure",
|
|
133635
|
+
status: "failed",
|
|
133636
|
+
unitId,
|
|
133637
|
+
reason: "declared-host-surface-missing",
|
|
133638
|
+
missingHosts: hostCoverage.missing
|
|
133639
|
+
});
|
|
133640
|
+
throw new ReleaseError(
|
|
133641
|
+
GATE_FAILED,
|
|
133642
|
+
`skill resource closure recheck failed for unit "${unitId}": declared host surface(s) missing or empty: ${hostCoverage.missing.map((item) => item.host).join(", ")}`,
|
|
133643
|
+
{ unitId, missingHosts: hostCoverage.missing }
|
|
133644
|
+
);
|
|
133645
|
+
}
|
|
133340
133646
|
const observed = createSkillResourceClosureReceipt(closureResult, {
|
|
133341
133647
|
unitId,
|
|
133342
133648
|
preparedAt: expected.preparedAt ?? null,
|
|
@@ -139802,8 +140108,7 @@ async function advanceShip(options = {}, injected = {}) {
|
|
|
139802
140108
|
let postVerifyRanThisCall = false;
|
|
139803
140109
|
if (state.status === "PUBLISHED" || state.status === "NEEDS_MANUAL_ATTESTATIONS") {
|
|
139804
140110
|
const plan = JSON.parse(await readFile46(state.planPath, "utf8"));
|
|
139805
|
-
const
|
|
139806
|
-
const needsDistribution = hasDistributeWork;
|
|
140111
|
+
const needsDistribution = requiresPostPublishDistribution(plan);
|
|
139807
140112
|
if (needsDistribution && deps.distributeRelease) {
|
|
139808
140113
|
state.status = "DISTRIBUTING";
|
|
139809
140114
|
await writeJsonAtomic(statePath, state);
|