release-skill 0.9.16 → 0.9.17
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/.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/.qoder-plugin/plugin.json +10 -0
- package/CHANGELOG.md +30 -0
- package/INSTALL.md +57 -9
- package/INSTALL.zh-CN.md +44 -7
- package/README.md +75 -31
- package/README.zh-CN.md +60 -29
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +1443 -672
- package/adapters/claude/schemas/release-plan.schema.json +1 -1
- package/adapters/claude/schemas/release-project.schema.json +1 -1
- package/adapters/claude/skills/release-finish/SKILL.md +14 -13
- package/adapters/claude/skills/release-help/SKILL.md +3 -3
- package/adapters/claude/skills/release-verify/SKILL.md +2 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/release-skill.bundle.mjs +1443 -672
- package/adapters/codex/schemas/release-plan.schema.json +1 -1
- package/adapters/codex/schemas/release-project.schema.json +1 -1
- package/adapters/codex/skills/release-finish/SKILL.md +14 -13
- package/adapters/codex/skills/release-help/SKILL.md +3 -3
- package/adapters/codex/skills/release-verify/SKILL.md +2 -2
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +1443 -672
- package/adapters/kimi/schemas/release-plan.schema.json +1 -1
- package/adapters/kimi/schemas/release-project.schema.json +1 -1
- package/adapters/kimi/skills/release-finish/SKILL.md +14 -13
- package/adapters/kimi/skills/release-help/SKILL.md +3 -3
- package/adapters/kimi/skills/release-verify/SKILL.md +2 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +10 -0
- package/adapters/qoder/bin/consumer-contract-vectors.json +110 -0
- package/adapters/qoder/bin/error-codes.json +161 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-contracts/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-contracts/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-engineering-kit/THIRD_PARTY_NOTICES +142 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/LICENSE +202 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/NOTICE +10 -0
- package/adapters/qoder/bin/foundation-legal/skill-family-harness-node/native-NOTICE +2 -0
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -0
- package/adapters/qoder/bin/kernel-protocol.json +60 -0
- package/adapters/qoder/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/qoder/bin/license-texts/MIT.txt +21 -0
- package/adapters/qoder/bin/prebuild-manifest.json +79 -0
- package/adapters/qoder/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/qoder/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/qoder/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/qoder/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/qoder/bin/registry.json +347 -0
- package/adapters/qoder/bin/release-skill-local-finish.mjs +4 -0
- package/adapters/qoder/bin/release-skill.bundle.mjs +149446 -0
- package/adapters/qoder/bin/release-skill.mjs +54 -0
- package/adapters/qoder/bin/rules.json +104 -0
- package/adapters/qoder/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/qoder/data/hosts/claude/host-descriptor.json +45 -0
- package/adapters/qoder/data/hosts/codebuddy/host-descriptor.json +26 -0
- package/adapters/qoder/data/hosts/codex/host-descriptor.json +45 -0
- package/adapters/qoder/data/hosts/deepseek-harness/host-descriptor.json +23 -0
- package/adapters/qoder/data/hosts/kimi-code/host-descriptor.json +36 -0
- package/adapters/qoder/data/hosts/qoder/host-descriptor.json +26 -0
- package/adapters/qoder/data/hosts/registry.json +13 -0
- package/adapters/qoder/data/hosts/workbuddy/host-descriptor.json +34 -0
- package/adapters/qoder/data/licensing/registry.json +211 -0
- package/adapters/qoder/data/licensing/schema.json +207 -0
- package/adapters/qoder/native/safe-write/binding.gyp +41 -0
- package/adapters/qoder/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/qoder/native/safe-write/prebuilds.json +24 -0
- package/adapters/qoder/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/qoder/schemas/.render-manifest.json +41 -0
- package/adapters/qoder/schemas/approval-record.schema.json +115 -0
- package/adapters/qoder/schemas/artifact-lock.schema.json +111 -0
- package/adapters/qoder/schemas/artifact-plan.schema.json +52 -0
- package/adapters/qoder/schemas/artifact-policy.schema.json +76 -0
- package/adapters/qoder/schemas/evidence-event-v2.schema.json +164 -0
- package/adapters/qoder/schemas/evidence-event.schema.json +89 -0
- package/adapters/qoder/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/qoder/schemas/release-plan.schema.json +1996 -0
- package/adapters/qoder/schemas/release-project.schema.json +1613 -0
- package/adapters/qoder/schemas/release-run.schema.json +777 -0
- package/adapters/qoder/skills/release-assess/SKILL.md +60 -0
- package/adapters/qoder/skills/release-config/SKILL.md +150 -0
- package/adapters/qoder/skills/release-docs/SKILL.md +119 -0
- package/adapters/qoder/skills/release-finish/SKILL.md +141 -0
- package/adapters/qoder/skills/release-help/SKILL.md +153 -0
- package/adapters/qoder/skills/release-marketplace/SKILL.md +156 -0
- package/adapters/qoder/skills/release-prepare/SKILL.md +135 -0
- package/adapters/qoder/skills/release-publish/SKILL.md +91 -0
- package/adapters/qoder/skills/release-reconcile/SKILL.md +80 -0
- package/adapters/qoder/skills/release-setup/SKILL.md +131 -0
- package/adapters/qoder/skills/release-verify/SKILL.md +109 -0
- package/adapters/qoder/src/schemas/adapter-build-manifest.schema.json +41 -0
- package/adapters/qoder/src/schemas/adapter-peer-verification-request.schema.json +54 -0
- package/adapters/qoder/src/schemas/adapter-peer-verification-result.schema.json +54 -0
- package/adapters/qoder/src/schemas/adapter-source.schema.json +47 -0
- package/adapters/qoder/src/schemas/audit-baseline-pin.schema.json +163 -0
- package/adapters/qoder/src/schemas/declared-read-surface-result.schema.json +61 -0
- package/adapters/qoder/src/schemas/engineering-baseline.schema.json +77 -0
- package/adapters/qoder/src/schemas/executable-identity-observation.schema.json +162 -0
- package/adapters/qoder/src/schemas/filesystem-root-binding.schema.json +14 -0
- package/adapters/qoder/src/schemas/filesystem-tree-observation.schema.json +71 -0
- package/adapters/qoder/src/schemas/fixed-set-publication-manifest.schema.json +108 -0
- package/adapters/qoder/src/schemas/fixed-set-publication-receipt.schema.json +148 -0
- package/adapters/qoder/src/schemas/host-capability-fact.schema.json +31 -0
- package/adapters/qoder/src/schemas/host-descriptor.schema.json +137 -0
- package/adapters/qoder/src/schemas/host-operation-plan.schema.json +57 -0
- package/adapters/qoder/src/schemas/host-operation-receipt.schema.json +81 -0
- package/adapters/qoder/src/schemas/host-probe-result.schema.json +30 -0
- package/adapters/qoder/src/schemas/host-registry.schema.json +18 -0
- package/adapters/qoder/src/schemas/host-verification-request.schema.json +67 -0
- package/adapters/qoder/src/schemas/host-verification-result.schema.json +149 -0
- package/adapters/qoder/src/schemas/managed-file-lock.schema.json +77 -0
- package/adapters/qoder/src/schemas/migration-manifest.schema.json +412 -0
- package/adapters/qoder/src/schemas/observation-scope.schema.json +91 -0
- package/adapters/qoder/src/schemas/operation-request.schema.json +58 -0
- package/adapters/qoder/src/schemas/operation-result.schema.json +130 -0
- package/adapters/qoder/src/schemas/platform-difference-registry.schema.json +110 -0
- package/adapters/qoder/src/schemas/plugin-verification-request.schema.json +499 -0
- package/adapters/qoder/src/schemas/plugin-verification-result.schema.json +1217 -0
- package/adapters/qoder/src/schemas/profile-adoption-declaration.schema.json +111 -0
- package/adapters/qoder/src/schemas/profile-descriptor.schema.json +99 -0
- package/adapters/qoder/src/schemas/project-manifest.schema.json +67 -0
- package/adapters/qoder/src/schemas/project-profile.schema.json +25 -0
- package/adapters/qoder/src/schemas/public-boundary-declaration.schema.json +73 -0
- package/adapters/qoder/src/schemas/report-binding.schema.json +42 -0
- package/adapters/qoder/src/schemas/report-model.schema.json +308 -0
- package/adapters/qoder/src/schemas/skill-family-directory-verification-request.schema.json +100 -0
- package/adapters/qoder/src/schemas/skill-family-directory-verification-result.schema.json +193 -0
- package/adapters/qoder/src/schemas/source-authority-receipt.schema.json +31 -0
- package/adapters/qoder/src/schemas/state-event-envelope.schema.json +38 -0
- package/adapters/qoder/src/schemas/state-snapshot-metadata.schema.json +25 -0
- package/adapters/qoder/src/schemas/structured-scan-policy.schema.json +97 -0
- package/adapters/qoder/src/schemas/surface-scan-policy.schema.json +42 -0
- package/adapters/qoder/src/schemas/timeout-policy.schema.json +24 -0
- package/adapters/qoder/src/schemas/token-estimate-record.schema.json +75 -0
- package/adapters/qoder/src/schemas/token-estimate-result.schema.json +43 -0
- package/adapters/qoder/src/schemas/watchdog-termination-envelope.schema.json +156 -0
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +1443 -672
- package/adapters/workbuddy/schemas/release-plan.schema.json +1 -1
- package/adapters/workbuddy/schemas/release-project.schema.json +1 -1
- package/adapters/workbuddy/skills/release-finish/SKILL.md +14 -13
- package/adapters/workbuddy/skills/release-help/SKILL.md +3 -3
- package/adapters/workbuddy/skills/release-verify/SKILL.md +2 -2
- package/bin/release-skill-cli.mjs +124 -6
- package/bin/release-skill.bundle.mjs +1443 -672
- package/package.json +4 -2
- package/platform-manifest.json +70 -4
- package/references/02-project-config.md +9 -2
- package/schemas/release-plan.schema.json +1 -1
- package/schemas/release-project.schema.json +1 -1
- package/skills/release-finish/SKILL.md +14 -13
- package/skills/release-help/SKILL.md +3 -3
- package/skills/release-verify/SKILL.md +2 -2
- package/skills-src/release-finish/SKILL.md +14 -13
- package/skills-src/release-help/SKILL.md +3 -3
- package/skills-src/release-verify/SKILL.md +2 -2
- package/src/commands/post-release-local.mjs +360 -51
- package/src/commands/verify-records.mjs +467 -0
- package/src/core/postpublish.mjs +1 -1
- package/src/core/run.mjs +1 -1
- package/src/producers/build-adapters.mjs +36 -13
|
@@ -14,6 +14,7 @@ import { resolveCodeBuddyMarketplace } from '../platforms/codebuddy.mjs';
|
|
|
14
14
|
import { verifyInstalledMarketplacePayload } from '../adapters/plugin-marketplace.mjs';
|
|
15
15
|
import { normalizePostPublishView, postPublishActionId } from '../core/postpublish.mjs';
|
|
16
16
|
import { loadRun, validateRunLineage } from '../core/run.mjs';
|
|
17
|
+
import { verifyFrozenSnapshot } from '../snapshot/frozen.mjs';
|
|
17
18
|
|
|
18
19
|
const HOSTS_BY_ACTION = Object.freeze({
|
|
19
20
|
'claude-marketplace-install': ['claude'],
|
|
@@ -40,6 +41,9 @@ const SAFE_ENV_KEYS = Object.freeze([
|
|
|
40
41
|
]);
|
|
41
42
|
const CODEBUDDY_PLUGIN_LIST_ARGS = Object.freeze(['plugin', 'list', '--json']);
|
|
42
43
|
const CODEBUDDY_MARKETPLACE_LIST_ARGS = Object.freeze(['plugin', 'marketplace', 'list']);
|
|
44
|
+
const QODER_PLUGIN_LIST_ARGS = Object.freeze(['plugins', 'list', '--json']);
|
|
45
|
+
const QODER_MARKETPLACE_LIST_ARGS = Object.freeze(['plugins', 'marketplace', 'list', '--json']);
|
|
46
|
+
const QODER_PAYLOAD_CONTRACT = 'external-marketplace-v1';
|
|
43
47
|
|
|
44
48
|
function attachFoundationFailure(error, { envelope, stdout }) {
|
|
45
49
|
Object.defineProperties(error, {
|
|
@@ -172,17 +176,38 @@ function hubTargets(plan) {
|
|
|
172
176
|
codebuddy: 'Handle manually; CodeBuddy cannot pin a Hub ref in this flow.',
|
|
173
177
|
workbuddy: 'Handle manually; WorkBuddy cannot pin a Hub ref in this flow (it follows the CodeBuddy manual boundary).',
|
|
174
178
|
};
|
|
175
|
-
return local.hosts.map((host) =>
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
179
|
+
return local.hosts.map((host) => {
|
|
180
|
+
if (host === 'qoder') {
|
|
181
|
+
return {
|
|
182
|
+
targetKind: 'hub-backed',
|
|
183
|
+
executionMode: 'executable',
|
|
184
|
+
unitId: declaration.unitId,
|
|
185
|
+
host,
|
|
186
|
+
plugin: local.plugin,
|
|
187
|
+
marketplace: hub.name,
|
|
188
|
+
hub,
|
|
189
|
+
version: unit?.targetVersion,
|
|
190
|
+
pluginRepo: unit?.publicRepo,
|
|
191
|
+
pluginCommit: unit?.frozenSnapshot?.commit,
|
|
192
|
+
snapshotPath: unit?.frozenSnapshot?.path,
|
|
193
|
+
manifestDigest: unit?.frozenSnapshot?.manifestDigest,
|
|
194
|
+
timeoutMs: 300_000,
|
|
195
|
+
message: `Update ${local.plugin} from Qoder Hub ${hub.name}; a new session or /plugins reload is required before treating the updated plugin as loaded.`,
|
|
196
|
+
...(tag ? { frozenTag: tag } : {}),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return {
|
|
200
|
+
targetKind: 'hub-backed',
|
|
201
|
+
executionMode: 'manual',
|
|
202
|
+
unitId: declaration.unitId,
|
|
203
|
+
host,
|
|
204
|
+
plugin: local.plugin,
|
|
205
|
+
hub,
|
|
206
|
+
message: `${manualInstruction[host]} Install or upgrade ${local.plugin} from Hub ${hub.name}; release-skill does not execute or probe this action.`,
|
|
207
|
+
...(unit?.publicRepo ? { publicRepo: unit.publicRepo } : {}),
|
|
208
|
+
...(tag ? { frozenTag: tag } : {}),
|
|
209
|
+
};
|
|
210
|
+
});
|
|
186
211
|
});
|
|
187
212
|
}
|
|
188
213
|
|
|
@@ -240,6 +265,23 @@ function assertExecutableTarget(target) {
|
|
|
240
265
|
}
|
|
241
266
|
}
|
|
242
267
|
|
|
268
|
+
function assertQoderExecutableTarget(target) {
|
|
269
|
+
for (const field of [
|
|
270
|
+
'unitId', 'plugin', 'marketplace', 'version', 'pluginRepo',
|
|
271
|
+
'pluginCommit', 'snapshotPath', 'manifestDigest',
|
|
272
|
+
]) {
|
|
273
|
+
if (typeof target[field] !== 'string' || target[field].length === 0) {
|
|
274
|
+
throw new Error(`Qoder local host update target is missing ${field}`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
if (!/^[a-f0-9]{40}$/u.test(target.pluginCommit)) {
|
|
278
|
+
throw new Error(`Qoder local host update requires the frozen public commit for unit ${target.unitId}`);
|
|
279
|
+
}
|
|
280
|
+
if (!/^[a-f0-9]{64}$/u.test(target.manifestDigest)) {
|
|
281
|
+
throw new Error(`Qoder local host update requires the frozen snapshot digest for unit ${target.unitId}`);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
243
285
|
function buildShipNextStep({ root, statePath, unitIds }) {
|
|
244
286
|
const argv = ['release-skill', 'ship'];
|
|
245
287
|
if (typeof root === 'string' && root.length > 0) argv.push('--root', root);
|
|
@@ -268,8 +310,12 @@ export function derivePostReleaseChecklist(plan, {
|
|
|
268
310
|
const uncovered = units.filter((unit) => (
|
|
269
311
|
!BRANCH_ACTION_INCLUDED.has(unit.productionConfig?.branchStrategy)
|
|
270
312
|
));
|
|
271
|
-
const
|
|
272
|
-
const
|
|
313
|
+
const declaredHubTargets = hubTargets(plan);
|
|
314
|
+
const executableTargets = [
|
|
315
|
+
...pluginTargets(plan),
|
|
316
|
+
...declaredHubTargets.filter((target) => target.executionMode === 'executable'),
|
|
317
|
+
];
|
|
318
|
+
const manualTargets = declaredHubTargets.filter((target) => target.executionMode === 'manual');
|
|
273
319
|
const targets = mergePostReleaseTargets(executableTargets, manualTargets);
|
|
274
320
|
const hasPendingPostVerify = postVerifyHooks(plan).length > 0 && !postVerifyComplete && targets.length > 0;
|
|
275
321
|
const hasStatePath = typeof statePath === 'string' && statePath.length > 0;
|
|
@@ -717,6 +763,215 @@ function normalizeGitSource(source) {
|
|
|
717
763
|
.replace(/\/$/u, '');
|
|
718
764
|
}
|
|
719
765
|
|
|
766
|
+
function normalizeHubGitSource(source, githubHost) {
|
|
767
|
+
return String(source ?? '')
|
|
768
|
+
.replace(/^git\+/, '')
|
|
769
|
+
.replace(new RegExp(`^https://${String(githubHost).replaceAll('.', '\\.').replaceAll('-', '\\-')}/`), '')
|
|
770
|
+
.replace(new RegExp(`^git@${String(githubHost).replaceAll('.', '\\.').replaceAll('-', '\\-')}:`), '')
|
|
771
|
+
.replace(/\.git$/u, '')
|
|
772
|
+
.replace(/\/$/u, '');
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
async function observeQoderMarketplace(target, command, env, run) {
|
|
776
|
+
const listed = await run(command, [...QODER_MARKETPLACE_LIST_ARGS], {
|
|
777
|
+
env,
|
|
778
|
+
timeout: target.timeoutMs,
|
|
779
|
+
});
|
|
780
|
+
const parsed = parseJson(listed.stdout, 'qoder marketplace list');
|
|
781
|
+
if (!Array.isArray(parsed)) throw new Error('qoder marketplace list did not return an array');
|
|
782
|
+
const matches = parsed.filter((entry) => entry?.name === target.marketplace);
|
|
783
|
+
if (matches.length === 0) return { installed: false };
|
|
784
|
+
if (matches.length !== 1) {
|
|
785
|
+
throw new Error(`qoder marketplace list returned conflicting entries for ${target.marketplace}`);
|
|
786
|
+
}
|
|
787
|
+
const [found] = matches;
|
|
788
|
+
if (
|
|
789
|
+
found.source?.source !== 'git'
|
|
790
|
+
|| normalizeHubGitSource(found.source.url, target.hub.githubHost) !== target.hub.repo
|
|
791
|
+
) {
|
|
792
|
+
throw new Error(`qoder marketplace ${target.marketplace} does not point to ${target.hub.repo}`);
|
|
793
|
+
}
|
|
794
|
+
if (typeof found.installLocation !== 'string' || found.installLocation.length === 0) {
|
|
795
|
+
throw new Error(`qoder marketplace ${target.marketplace} has no observable checkout root`);
|
|
796
|
+
}
|
|
797
|
+
const [remote, branch] = await Promise.all([
|
|
798
|
+
run('git', ['-C', found.installLocation, 'remote', 'get-url', 'origin'], {
|
|
799
|
+
env,
|
|
800
|
+
timeout: 30_000,
|
|
801
|
+
}),
|
|
802
|
+
run('git', ['-C', found.installLocation, 'symbolic-ref', '-q', 'HEAD'], {
|
|
803
|
+
env,
|
|
804
|
+
timeout: 30_000,
|
|
805
|
+
}),
|
|
806
|
+
]);
|
|
807
|
+
if (
|
|
808
|
+
normalizeHubGitSource(remote.stdout.trim(), target.hub.githubHost) !== target.hub.repo
|
|
809
|
+
|| branch.stdout.trim() !== target.hub.ref
|
|
810
|
+
) {
|
|
811
|
+
throw new Error(`qoder marketplace ${target.marketplace} checkout does not match the frozen Hub source`);
|
|
812
|
+
}
|
|
813
|
+
return { installed: true, root: found.installLocation, found };
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
async function observeQoderPlugin(target, command, env, run) {
|
|
817
|
+
const listed = await run(command, [...QODER_PLUGIN_LIST_ARGS], {
|
|
818
|
+
env,
|
|
819
|
+
timeout: target.timeoutMs,
|
|
820
|
+
});
|
|
821
|
+
const parsed = parseJson(listed.stdout, 'qoder plugin list');
|
|
822
|
+
if (!Array.isArray(parsed)) throw new Error('qoder plugin list did not return an array');
|
|
823
|
+
const selector = `${target.plugin}@${target.marketplace}`;
|
|
824
|
+
const matches = parsed.filter((entry) => entry?.id === selector);
|
|
825
|
+
if (matches.length === 0) return { installed: false };
|
|
826
|
+
if (matches.length !== 1) {
|
|
827
|
+
throw new Error(`qoder plugin list returned conflicting entries for ${selector}`);
|
|
828
|
+
}
|
|
829
|
+
const [found] = matches;
|
|
830
|
+
if (
|
|
831
|
+
found.name !== target.plugin
|
|
832
|
+
|| found.source !== selector
|
|
833
|
+
|| found.scope !== 'user'
|
|
834
|
+
|| typeof found.installPath !== 'string'
|
|
835
|
+
|| found.installPath.length === 0
|
|
836
|
+
) {
|
|
837
|
+
throw new Error(`qoder plugin ${selector} does not match its frozen user-scope identity`);
|
|
838
|
+
}
|
|
839
|
+
return {
|
|
840
|
+
installed: true,
|
|
841
|
+
exact: found.version === target.version,
|
|
842
|
+
installPath: found.installPath,
|
|
843
|
+
found,
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
|
|
847
|
+
async function readQoderManifest(root, label) {
|
|
848
|
+
const manifest = parseJson(
|
|
849
|
+
await readFileContained(root, '.qoder-plugin/plugin.json', { encoding: 'utf8' }),
|
|
850
|
+
label,
|
|
851
|
+
);
|
|
852
|
+
if (!manifest || typeof manifest !== 'object' || Array.isArray(manifest)) {
|
|
853
|
+
throw new Error(`${label} did not return an object`);
|
|
854
|
+
}
|
|
855
|
+
return manifest;
|
|
856
|
+
}
|
|
857
|
+
|
|
858
|
+
function assertQoderPluginManifest(target, manifest, label) {
|
|
859
|
+
if (
|
|
860
|
+
manifest.name !== target.plugin
|
|
861
|
+
|| manifest.version !== target.version
|
|
862
|
+
|| manifest.skills !== './adapters/qoder/skills/'
|
|
863
|
+
) {
|
|
864
|
+
throw new Error(`${label} does not match the frozen Qoder plugin identity and projection path`);
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
async function assertFrozenQoderManifest(target, root) {
|
|
869
|
+
const { snapshotDir: snapshotRoot } = await verifyFrozenSnapshot({
|
|
870
|
+
root,
|
|
871
|
+
snapshotPath: target.snapshotPath,
|
|
872
|
+
expectedDigest: target.manifestDigest,
|
|
873
|
+
});
|
|
874
|
+
const manifest = await readQoderManifest(snapshotRoot, 'frozen Qoder plugin manifest');
|
|
875
|
+
assertQoderPluginManifest(target, manifest, 'frozen Qoder plugin manifest');
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
async function assertQoderHubEntry(target, marketplaceRoot) {
|
|
879
|
+
const marketplace = parseJson(
|
|
880
|
+
await readFileContained(marketplaceRoot, 'marketplace.json', { encoding: 'utf8' }),
|
|
881
|
+
'Qoder Hub marketplace manifest',
|
|
882
|
+
);
|
|
883
|
+
if (marketplace?.name !== target.marketplace || !Array.isArray(marketplace.plugins)) {
|
|
884
|
+
throw new Error('Qoder Hub marketplace manifest does not match the frozen marketplace');
|
|
885
|
+
}
|
|
886
|
+
const matches = marketplace.plugins.filter((entry) => entry?.name === target.plugin);
|
|
887
|
+
if (matches.length !== 1) {
|
|
888
|
+
throw new Error(`Qoder Hub marketplace manifest must contain exactly one ${target.plugin} entry`);
|
|
889
|
+
}
|
|
890
|
+
const source = matches[0]?.source;
|
|
891
|
+
if (
|
|
892
|
+
source?.source !== 'url'
|
|
893
|
+
|| normalizeHubGitSource(source.url, target.hub.githubHost) !== target.pluginRepo
|
|
894
|
+
|| source.sha !== target.pluginCommit
|
|
895
|
+
) {
|
|
896
|
+
throw new Error('Qoder Hub entry does not match the frozen public source identity');
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
async function verifyQoderInstalledPayload({
|
|
901
|
+
target,
|
|
902
|
+
root,
|
|
903
|
+
installPath,
|
|
904
|
+
verifyInstalledPayload,
|
|
905
|
+
}) {
|
|
906
|
+
const manifest = await readQoderManifest(installPath, 'installed Qoder plugin manifest');
|
|
907
|
+
assertQoderPluginManifest(target, manifest, 'installed Qoder plugin manifest');
|
|
908
|
+
await verifyInstalledPayload({
|
|
909
|
+
snapshotPath: target.snapshotPath,
|
|
910
|
+
manifestDigest: target.manifestDigest,
|
|
911
|
+
payloadContract: QODER_PAYLOAD_CONTRACT,
|
|
912
|
+
marketplaceLocation: 'external',
|
|
913
|
+
}, { root }, installPath, 'qoder');
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
async function runQoderUpdate(target, detected, run, {
|
|
917
|
+
root,
|
|
918
|
+
verifyInstalledPayload,
|
|
919
|
+
}) {
|
|
920
|
+
const env = hostEnvironment('qoder');
|
|
921
|
+
await assertFrozenQoderManifest(target, root);
|
|
922
|
+
const marketplace = await observeQoderMarketplace(target, detected.command, env, run);
|
|
923
|
+
if (!marketplace.installed) {
|
|
924
|
+
return {
|
|
925
|
+
status: 'MANUAL_REQUIRED',
|
|
926
|
+
reason: `qoder marketplace ${target.marketplace} is not installed; no marketplace was added`,
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
const before = await observeQoderPlugin(target, detected.command, env, run);
|
|
930
|
+
if (!before.installed) {
|
|
931
|
+
return {
|
|
932
|
+
status: 'MANUAL_REQUIRED',
|
|
933
|
+
reason: 'qoder target plugin is not installed; no initial installation was performed',
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
if (before.exact) {
|
|
937
|
+
await assertQoderHubEntry(target, marketplace.root);
|
|
938
|
+
await verifyQoderInstalledPayload({
|
|
939
|
+
target,
|
|
940
|
+
root,
|
|
941
|
+
installPath: before.installPath,
|
|
942
|
+
verifyInstalledPayload,
|
|
943
|
+
});
|
|
944
|
+
return { status: 'ALREADY_CURRENT', version: target.version };
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
await run(detected.command, ['plugins', 'marketplace', 'update', target.marketplace], {
|
|
948
|
+
env,
|
|
949
|
+
timeout: target.timeoutMs,
|
|
950
|
+
});
|
|
951
|
+
const refreshed = await observeQoderMarketplace(target, detected.command, env, run);
|
|
952
|
+
if (!refreshed.installed) {
|
|
953
|
+
throw new Error(`qoder marketplace ${target.marketplace} disappeared after refresh`);
|
|
954
|
+
}
|
|
955
|
+
await assertQoderHubEntry(target, refreshed.root);
|
|
956
|
+
await run(detected.command, [
|
|
957
|
+
'plugins', 'update', `${target.plugin}@${target.marketplace}`, '--scope', 'user',
|
|
958
|
+
], { env, timeout: target.timeoutMs });
|
|
959
|
+
const after = await observeQoderPlugin(target, detected.command, env, run);
|
|
960
|
+
if (!after.exact) throw new Error('qoder did not update to the frozen plugin version');
|
|
961
|
+
await verifyQoderInstalledPayload({
|
|
962
|
+
target,
|
|
963
|
+
root,
|
|
964
|
+
installPath: after.installPath,
|
|
965
|
+
verifyInstalledPayload,
|
|
966
|
+
});
|
|
967
|
+
return {
|
|
968
|
+
status: 'UPDATED',
|
|
969
|
+
version: target.version,
|
|
970
|
+
restartRequired: true,
|
|
971
|
+
reloadInstruction: 'Start a new Qoder session or run /plugins reload before checking the loaded version.',
|
|
972
|
+
};
|
|
973
|
+
}
|
|
974
|
+
|
|
720
975
|
function parseFrozenRemoteRef(target, stdout) {
|
|
721
976
|
const directRefs = new Map();
|
|
722
977
|
const peeledRefs = new Map();
|
|
@@ -1046,14 +1301,14 @@ expect {
|
|
|
1046
1301
|
eof { failEof remove-confirmation 126 }
|
|
1047
1302
|
}
|
|
1048
1303
|
expect {
|
|
1049
|
-
-nocase -re {Trust this folder\\?} {
|
|
1304
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1050
1305
|
-re $promptPattern {}
|
|
1051
1306
|
timeout { failTimeout remove-prompt 128 130 }
|
|
1052
1307
|
eof { failEof remove-prompt 129 }
|
|
1053
1308
|
}
|
|
1054
1309
|
}
|
|
1055
1310
|
-re $promptPattern {}
|
|
1056
|
-
-nocase -re {Trust this folder\\?} {
|
|
1311
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1057
1312
|
timeout { failTimeout remove-dialog 122 124 }
|
|
1058
1313
|
eof { failEof remove-dialog 123 }
|
|
1059
1314
|
}
|
|
@@ -1128,9 +1383,62 @@ proc failEof {state code} {
|
|
|
1128
1383
|
exit $code
|
|
1129
1384
|
}
|
|
1130
1385
|
|
|
1131
|
-
proc
|
|
1132
|
-
puts stderr "KIMI_TUI_STATE:directory-trust:
|
|
1133
|
-
exit
|
|
1386
|
+
proc unexpectedDirectoryTrust {} {
|
|
1387
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:unexpected"
|
|
1388
|
+
exit 147
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
proc confirmInitialDirectoryTrust {} {
|
|
1392
|
+
global expect_out promptPattern
|
|
1393
|
+
set dialogBuffer $expect_out(buffer)
|
|
1394
|
+
expect {
|
|
1395
|
+
-nocase -re {❯[^\\r\\n]*No,[ \\t]*exit} {
|
|
1396
|
+
append dialogBuffer $expect_out(buffer)
|
|
1397
|
+
}
|
|
1398
|
+
-re {❯[^\\r\\n]*\\r*\\n} {
|
|
1399
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:selection-unknown"
|
|
1400
|
+
exit 143
|
|
1401
|
+
}
|
|
1402
|
+
timeout {
|
|
1403
|
+
puts stderr "KIMI_TUI_STATE:directory-trust-selected-row:timeout"
|
|
1404
|
+
exit 140
|
|
1405
|
+
}
|
|
1406
|
+
eof { failEof directory-trust-selected-row 141 }
|
|
1407
|
+
}
|
|
1408
|
+
expect {
|
|
1409
|
+
-nocase -re {(?:^|\\r|\\n)[ \\t]+Trust this folder[ \\t]*\\r*\\n} {
|
|
1410
|
+
append dialogBuffer $expect_out(buffer)
|
|
1411
|
+
}
|
|
1412
|
+
-re {(?:^|\\r|\\n)[^\\r\\n]*\\r*\\n} {
|
|
1413
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:selection-unknown"
|
|
1414
|
+
exit 143
|
|
1415
|
+
}
|
|
1416
|
+
timeout { failTimeout directory-trust-target-row 148 150 }
|
|
1417
|
+
eof { failEof directory-trust-target-row 149 }
|
|
1418
|
+
}
|
|
1419
|
+
set dialog [cleanScreen $dialogBuffer]
|
|
1420
|
+
if {![regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*No,[ \\t]*exit} $dialog]
|
|
1421
|
+
|| ![regexp -nocase {(^|\\n)[ \\t]+Trust this folder[ \\t]*($|\\n)} $dialog]} {
|
|
1422
|
+
puts stderr "KIMI_TUI_STATE:directory-trust:selection-unknown"
|
|
1423
|
+
exit 143
|
|
1424
|
+
}
|
|
1425
|
+
send -- "\\033\\[B"
|
|
1426
|
+
expect {
|
|
1427
|
+
-nocase -re {❯[^\\r\\n]*Trust this folder} {}
|
|
1428
|
+
-re {❯[^\\r\\n]*\\r*\\n} {
|
|
1429
|
+
puts stderr "KIMI_TUI_STATE:directory-trust-confirm-selection:unknown"
|
|
1430
|
+
exit 146
|
|
1431
|
+
}
|
|
1432
|
+
timeout { failTimeout directory-trust-confirm-selection 144 146 }
|
|
1433
|
+
eof { failEof directory-trust-confirm-selection 145 }
|
|
1434
|
+
}
|
|
1435
|
+
send -- "\\033\\[13u"
|
|
1436
|
+
expect {
|
|
1437
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1438
|
+
-re $promptPattern {}
|
|
1439
|
+
timeout { failTimeout directory-trust-prompt 151 153 }
|
|
1440
|
+
eof { failEof directory-trust-prompt 152 }
|
|
1441
|
+
}
|
|
1134
1442
|
}
|
|
1135
1443
|
|
|
1136
1444
|
proc submitCommand {command} {
|
|
@@ -1146,7 +1454,7 @@ if {[catch {exec stty columns 240 rows 60 < $spawn_out(slave,name)} resizeError]
|
|
|
1146
1454
|
exit 131
|
|
1147
1455
|
}
|
|
1148
1456
|
expect {
|
|
1149
|
-
-nocase -re {Trust this folder\\?} {
|
|
1457
|
+
-nocase -re {Trust this folder\\?} { confirmInitialDirectoryTrust }
|
|
1150
1458
|
-re $promptPattern {}
|
|
1151
1459
|
timeout { failTimeout initial-prompt 101 103 }
|
|
1152
1460
|
eof { failEof initial-prompt 102 }
|
|
@@ -1154,7 +1462,7 @@ expect {
|
|
|
1154
1462
|
${removeCommand}submitCommand "/plugins install $installUrl"
|
|
1155
1463
|
set dialogBuffer ""
|
|
1156
1464
|
expect {
|
|
1157
|
-
-nocase -re {Trust this folder\\?} {
|
|
1465
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1158
1466
|
-nocase -re {(?:Install third-party plugin|Trust and install from)[ \\t]} {
|
|
1159
1467
|
append dialogBuffer $expect_out(buffer)
|
|
1160
1468
|
}
|
|
@@ -1162,7 +1470,7 @@ expect {
|
|
|
1162
1470
|
eof { failEof plugin-trust-anchor 105 }
|
|
1163
1471
|
}
|
|
1164
1472
|
expect {
|
|
1165
|
-
-nocase -re {Trust this folder\\?} {
|
|
1473
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1166
1474
|
-nocase -re {❯[^\\r\\n]*(?:Exit|Cancel|Trust and install)} {
|
|
1167
1475
|
append dialogBuffer $expect_out(buffer)
|
|
1168
1476
|
}
|
|
@@ -1198,7 +1506,7 @@ if {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*trust and install} $dialog]} {
|
|
|
1198
1506
|
} elseif {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*(cancel|exit)} $dialog]} {
|
|
1199
1507
|
send -- "\\033\\[B"
|
|
1200
1508
|
expect {
|
|
1201
|
-
-nocase -re {Trust this folder\\?} {
|
|
1509
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1202
1510
|
-nocase -re {❯[^\\r\\n]*Trust and install} {}
|
|
1203
1511
|
-re {❯[^\\r\\n]*\\r*\\n} {
|
|
1204
1512
|
puts stderr "KIMI_TUI_STATE:plugin-trust-confirm-selection:unknown"
|
|
@@ -1214,7 +1522,7 @@ if {[regexp -nocase {(^|\\n)[^\\n]*❯[^\\n]*trust and install} $dialog]} {
|
|
|
1214
1522
|
}
|
|
1215
1523
|
|
|
1216
1524
|
expect {
|
|
1217
|
-
-nocase -re {Trust this folder\\?} {
|
|
1525
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1218
1526
|
-nocase -re {Install finished[^\\r\\n]*see details below\\.} {}
|
|
1219
1527
|
-nocase -re {Installing plugin from[^\\r\\n]*(?:\\r|\\n)} {
|
|
1220
1528
|
exp_continue -continue_timer
|
|
@@ -1234,14 +1542,14 @@ expect {
|
|
|
1234
1542
|
eof { failEof install-result 137 }
|
|
1235
1543
|
}
|
|
1236
1544
|
expect {
|
|
1237
|
-
-nocase -re {Trust this folder\\?} {
|
|
1545
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1238
1546
|
-re $promptPattern {}
|
|
1239
1547
|
timeout { failTimeout post-install-prompt 114 116 }
|
|
1240
1548
|
eof { failEof post-install-prompt 115 }
|
|
1241
1549
|
}
|
|
1242
1550
|
submitCommand "/reload"
|
|
1243
1551
|
expect {
|
|
1244
|
-
-nocase -re {Trust this folder\\?} {
|
|
1552
|
+
-nocase -re {Trust this folder\\?} { unexpectedDirectoryTrust }
|
|
1245
1553
|
-re $promptPattern {}
|
|
1246
1554
|
timeout { failTimeout reload-prompt 117 119 }
|
|
1247
1555
|
eof { failEof reload-prompt 118 }
|
|
@@ -1343,29 +1651,20 @@ async function runKimiUpdate(target, detected, run, kimiHome, {
|
|
|
1343
1651
|
}
|
|
1344
1652
|
const installUrl = `https://github.com/${target.pluginRepo}/releases/tag/${target.pluginTag}`;
|
|
1345
1653
|
const removePlugin = before.source === 'legacy' ? target.plugin : '';
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
} catch (error) {
|
|
1361
|
-
if (error?.exitStatus === 80) {
|
|
1362
|
-
return {
|
|
1363
|
-
status: 'MANUAL_REQUIRED',
|
|
1364
|
-
reason: 'Kimi requires folder trust; release-finish did not confirm the folder or continue installation',
|
|
1365
|
-
};
|
|
1366
|
-
}
|
|
1367
|
-
throw error;
|
|
1368
|
-
}
|
|
1654
|
+
await run(detected.expectCommand, ['-c', kimiExpectProgram({
|
|
1655
|
+
...(removePlugin ? { removePlugin } : {}),
|
|
1656
|
+
})], {
|
|
1657
|
+
cwd: root,
|
|
1658
|
+
timeout: Math.max(300_000, target.timeoutMs),
|
|
1659
|
+
env: {
|
|
1660
|
+
...env,
|
|
1661
|
+
RELEASE_SKILL_KIMI_COMMAND: detected.command,
|
|
1662
|
+
RELEASE_SKILL_KIMI_INSTALL_URL: installUrl,
|
|
1663
|
+
RELEASE_SKILL_KIMI_REMOVE_PLUGIN: removePlugin,
|
|
1664
|
+
RELEASE_SKILL_KIMI_EXPECTED_REPO: `https://github.com/${target.pluginRepo}`,
|
|
1665
|
+
RELEASE_SKILL_KIMI_EXPECTED_TAG: target.pluginTag,
|
|
1666
|
+
},
|
|
1667
|
+
});
|
|
1369
1668
|
}, { prefix: 'release-skill-kimi-update-' });
|
|
1370
1669
|
if (tuiOutcome) return tuiOutcome;
|
|
1371
1670
|
const after = await observeKimiTarget(target, kimiHome, run);
|
|
@@ -1429,8 +1728,13 @@ async function updateLocalHostPluginsInternal({
|
|
|
1429
1728
|
const selected = new Set(Array.isArray(selectedHosts) ? selectedHosts : []);
|
|
1430
1729
|
const unknown = [...selected].filter((host) => !checklist.localHostUpdate.hosts.includes(host));
|
|
1431
1730
|
if (unknown.length > 0) throw new Error(`selected hosts are not declared by the plan: ${unknown.join(', ')}`);
|
|
1432
|
-
const targets =
|
|
1433
|
-
|
|
1731
|
+
const targets = checklist.localHostUpdate.targets.filter((item) => (
|
|
1732
|
+
item.executionMode !== 'manual' && selected.has(item.host)
|
|
1733
|
+
));
|
|
1734
|
+
for (const target of targets) {
|
|
1735
|
+
if (target.host === 'qoder') assertQoderExecutableTarget(target);
|
|
1736
|
+
else assertExecutableTarget(target);
|
|
1737
|
+
}
|
|
1434
1738
|
|
|
1435
1739
|
const results = [];
|
|
1436
1740
|
for (const target of targets) {
|
|
@@ -1451,7 +1755,12 @@ async function updateLocalHostPluginsInternal({
|
|
|
1451
1755
|
root,
|
|
1452
1756
|
verifyInstalledPayload,
|
|
1453
1757
|
})
|
|
1454
|
-
:
|
|
1758
|
+
: target.host === 'qoder'
|
|
1759
|
+
? await runQoderUpdate(target, detected, run, {
|
|
1760
|
+
root,
|
|
1761
|
+
verifyInstalledPayload,
|
|
1762
|
+
})
|
|
1763
|
+
: await runStructuredUpdate(target, detected, run, {
|
|
1455
1764
|
plan,
|
|
1456
1765
|
root,
|
|
1457
1766
|
verifyInstalledPayload,
|