release-skill 0.9.17 → 0.9.18
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/.cursor-plugin/plugin.json +10 -0
- package/.kimi-plugin/plugin.json +1 -1
- package/.qoder-plugin/plugin.json +1 -1
- package/CHANGELOG.md +36 -2
- package/INSTALL.md +56 -9
- package/INSTALL.zh-CN.md +41 -7
- package/README.md +31 -20
- package/README.zh-CN.md +29 -19
- package/adapters/claude/.claude-plugin/plugin.json +1 -1
- package/adapters/claude/bin/consumer-contract-vectors.json +7 -7
- package/adapters/claude/bin/error-codes.json +1 -1
- package/adapters/claude/bin/foundation-resource-binding.json +1 -1
- package/adapters/claude/bin/prebuild-manifest.json +8 -4
- package/adapters/claude/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/claude/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/claude/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/claude/bin/registry.json +1 -1
- package/adapters/claude/bin/release-skill.bundle.mjs +3560 -1200
- package/adapters/claude/bin/rules.json +1 -1
- package/adapters/claude/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/claude/data/hosts/registry.json +1 -0
- package/adapters/claude/schemas/release-plan.schema.json +255 -25
- package/adapters/claude/schemas/release-project.schema.json +228 -26
- package/adapters/claude/skills/release-finish/SKILL.md +17 -4
- package/adapters/claude/skills/release-help/SKILL.md +40 -39
- package/adapters/claude/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/codex/.codex-plugin/plugin.json +2 -2
- package/adapters/codex/bin/consumer-contract-vectors.json +7 -7
- package/adapters/codex/bin/error-codes.json +1 -1
- package/adapters/codex/bin/foundation-resource-binding.json +1 -1
- package/adapters/codex/bin/prebuild-manifest.json +8 -4
- package/adapters/codex/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/codex/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/codex/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/codex/bin/registry.json +1 -1
- package/adapters/codex/bin/release-skill.bundle.mjs +3560 -1200
- package/adapters/codex/bin/rules.json +1 -1
- package/adapters/codex/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/codex/data/hosts/registry.json +1 -0
- package/adapters/codex/schemas/release-plan.schema.json +255 -25
- package/adapters/codex/schemas/release-project.schema.json +228 -26
- package/adapters/codex/skills/release-finish/SKILL.md +17 -4
- package/adapters/codex/skills/release-help/SKILL.md +40 -39
- package/adapters/codex/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/cursor/.cursor-plugin/plugin.json +10 -0
- package/adapters/cursor/bin/consumer-contract-vectors.json +110 -0
- package/adapters/cursor/bin/error-codes.json +161 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-contracts/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-contracts/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-engineering-kit/THIRD_PARTY_NOTICES +142 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/LICENSE +202 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/NOTICE +10 -0
- package/adapters/cursor/bin/foundation-legal/skill-family-harness-node/native-NOTICE +2 -0
- package/adapters/cursor/bin/foundation-resource-binding.json +1 -0
- package/adapters/cursor/bin/kernel-protocol.json +60 -0
- package/adapters/cursor/bin/license-texts/Apache-2.0.txt +201 -0
- package/adapters/cursor/bin/license-texts/MIT.txt +21 -0
- package/adapters/cursor/bin/prebuild-manifest.json +83 -0
- package/adapters/cursor/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/cursor/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/cursor/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/cursor/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/cursor/bin/registry.json +347 -0
- package/adapters/cursor/bin/release-skill-local-finish.mjs +4 -0
- package/adapters/cursor/bin/release-skill.bundle.mjs +151806 -0
- package/adapters/cursor/bin/release-skill.mjs +54 -0
- package/adapters/cursor/bin/rules.json +104 -0
- package/adapters/cursor/bin/schemas/consumer-contract-vector.schema.json +149 -0
- package/adapters/cursor/data/hosts/claude/host-descriptor.json +45 -0
- package/adapters/cursor/data/hosts/codebuddy/host-descriptor.json +26 -0
- package/adapters/cursor/data/hosts/codex/host-descriptor.json +45 -0
- package/adapters/cursor/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/cursor/data/hosts/deepseek-harness/host-descriptor.json +23 -0
- package/adapters/cursor/data/hosts/kimi-code/host-descriptor.json +36 -0
- package/adapters/cursor/data/hosts/qoder/host-descriptor.json +26 -0
- package/adapters/cursor/data/hosts/registry.json +14 -0
- package/adapters/cursor/data/hosts/workbuddy/host-descriptor.json +34 -0
- package/adapters/cursor/data/licensing/registry.json +211 -0
- package/adapters/cursor/data/licensing/schema.json +207 -0
- package/adapters/cursor/native/safe-write/binding.gyp +41 -0
- package/adapters/cursor/native/safe-write/prebuilds/darwin-arm64/safe_write.node +0 -0
- package/adapters/cursor/native/safe-write/prebuilds.json +24 -0
- package/adapters/cursor/native/safe-write/src/safe_write.cc +2032 -0
- package/adapters/cursor/schemas/.render-manifest.json +41 -0
- package/adapters/cursor/schemas/approval-record.schema.json +115 -0
- package/adapters/cursor/schemas/artifact-lock.schema.json +111 -0
- package/adapters/cursor/schemas/artifact-plan.schema.json +52 -0
- package/adapters/cursor/schemas/artifact-policy.schema.json +76 -0
- package/adapters/cursor/schemas/evidence-event-v2.schema.json +164 -0
- package/adapters/cursor/schemas/evidence-event.schema.json +89 -0
- package/adapters/cursor/schemas/postpublish-approval-record.schema.json +47 -0
- package/adapters/cursor/schemas/release-plan.schema.json +2226 -0
- package/adapters/cursor/schemas/release-project.schema.json +1815 -0
- package/adapters/cursor/schemas/release-run.schema.json +777 -0
- package/adapters/cursor/skills/release-assess/SKILL.md +61 -0
- package/adapters/cursor/skills/release-config/SKILL.md +151 -0
- package/adapters/cursor/skills/release-docs/SKILL.md +120 -0
- package/adapters/cursor/skills/release-finish/SKILL.md +155 -0
- package/adapters/cursor/skills/release-help/SKILL.md +155 -0
- package/adapters/cursor/skills/release-marketplace/SKILL.md +157 -0
- package/adapters/cursor/skills/release-prepare/SKILL.md +136 -0
- package/adapters/cursor/skills/release-publish/SKILL.md +92 -0
- package/adapters/cursor/skills/release-reconcile/SKILL.md +81 -0
- package/adapters/cursor/skills/release-setup/SKILL.md +132 -0
- package/adapters/cursor/skills/release-verify/SKILL.md +110 -0
- package/adapters/cursor/src/schemas/adapter-build-manifest.schema.json +41 -0
- package/adapters/cursor/src/schemas/adapter-peer-verification-request.schema.json +54 -0
- package/adapters/cursor/src/schemas/adapter-peer-verification-result.schema.json +54 -0
- package/adapters/cursor/src/schemas/adapter-source.schema.json +47 -0
- package/adapters/cursor/src/schemas/audit-baseline-pin.schema.json +163 -0
- package/adapters/cursor/src/schemas/declared-read-surface-result.schema.json +61 -0
- package/adapters/cursor/src/schemas/engineering-baseline.schema.json +77 -0
- package/adapters/cursor/src/schemas/executable-identity-observation.schema.json +162 -0
- package/adapters/cursor/src/schemas/filesystem-root-binding.schema.json +14 -0
- package/adapters/cursor/src/schemas/filesystem-tree-observation.schema.json +71 -0
- package/adapters/cursor/src/schemas/fixed-set-publication-manifest.schema.json +108 -0
- package/adapters/cursor/src/schemas/fixed-set-publication-receipt.schema.json +148 -0
- package/adapters/cursor/src/schemas/host-capability-fact.schema.json +31 -0
- package/adapters/cursor/src/schemas/host-descriptor.schema.json +144 -0
- package/adapters/cursor/src/schemas/host-operation-plan.schema.json +57 -0
- package/adapters/cursor/src/schemas/host-operation-receipt.schema.json +81 -0
- package/adapters/cursor/src/schemas/host-probe-result.schema.json +30 -0
- package/adapters/cursor/src/schemas/host-registry.schema.json +18 -0
- package/adapters/cursor/src/schemas/host-verification-request.schema.json +67 -0
- package/adapters/cursor/src/schemas/host-verification-result.schema.json +149 -0
- package/adapters/cursor/src/schemas/managed-file-lock.schema.json +77 -0
- package/adapters/cursor/src/schemas/migration-manifest.schema.json +412 -0
- package/adapters/cursor/src/schemas/observation-scope.schema.json +91 -0
- package/adapters/cursor/src/schemas/operation-request.schema.json +58 -0
- package/adapters/cursor/src/schemas/operation-result.schema.json +130 -0
- package/adapters/cursor/src/schemas/platform-difference-registry.schema.json +110 -0
- package/adapters/cursor/src/schemas/plugin-verification-request.schema.json +499 -0
- package/adapters/cursor/src/schemas/plugin-verification-result.schema.json +1217 -0
- package/adapters/cursor/src/schemas/profile-adoption-declaration.schema.json +111 -0
- package/adapters/cursor/src/schemas/profile-descriptor.schema.json +99 -0
- package/adapters/cursor/src/schemas/project-manifest.schema.json +67 -0
- package/adapters/cursor/src/schemas/project-profile.schema.json +25 -0
- package/adapters/cursor/src/schemas/public-boundary-declaration.schema.json +73 -0
- package/adapters/cursor/src/schemas/report-binding.schema.json +42 -0
- package/adapters/cursor/src/schemas/report-model.schema.json +308 -0
- package/adapters/cursor/src/schemas/skill-family-directory-verification-request.schema.json +100 -0
- package/adapters/cursor/src/schemas/skill-family-directory-verification-result.schema.json +193 -0
- package/adapters/cursor/src/schemas/source-authority-receipt.schema.json +31 -0
- package/adapters/cursor/src/schemas/state-event-envelope.schema.json +38 -0
- package/adapters/cursor/src/schemas/state-snapshot-metadata.schema.json +25 -0
- package/adapters/cursor/src/schemas/structured-scan-policy.schema.json +97 -0
- package/adapters/cursor/src/schemas/surface-scan-policy.schema.json +42 -0
- package/adapters/cursor/src/schemas/timeout-policy.schema.json +24 -0
- package/adapters/cursor/src/schemas/token-estimate-record.schema.json +75 -0
- package/adapters/cursor/src/schemas/token-estimate-result.schema.json +43 -0
- package/adapters/cursor/src/schemas/watchdog-termination-envelope.schema.json +156 -0
- package/adapters/kimi/.kimi-plugin/plugin.json +1 -1
- package/adapters/kimi/bin/consumer-contract-vectors.json +7 -7
- package/adapters/kimi/bin/error-codes.json +1 -1
- package/adapters/kimi/bin/foundation-resource-binding.json +1 -1
- package/adapters/kimi/bin/prebuild-manifest.json +8 -4
- package/adapters/kimi/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/kimi/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/kimi/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/kimi/bin/registry.json +1 -1
- package/adapters/kimi/bin/release-skill.bundle.mjs +3560 -1200
- package/adapters/kimi/bin/rules.json +1 -1
- package/adapters/kimi/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/kimi/data/hosts/registry.json +1 -0
- package/adapters/kimi/schemas/release-plan.schema.json +255 -25
- package/adapters/kimi/schemas/release-project.schema.json +228 -26
- package/adapters/kimi/skills/release-finish/SKILL.md +17 -4
- package/adapters/kimi/skills/release-help/SKILL.md +40 -39
- package/adapters/kimi/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/qoder/.qoder-plugin/plugin.json +1 -1
- package/adapters/qoder/bin/consumer-contract-vectors.json +7 -7
- package/adapters/qoder/bin/error-codes.json +1 -1
- package/adapters/qoder/bin/foundation-resource-binding.json +1 -1
- package/adapters/qoder/bin/prebuild-manifest.json +8 -4
- 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 +1 -1
- package/adapters/qoder/bin/release-skill.bundle.mjs +3560 -1200
- package/adapters/qoder/bin/rules.json +1 -1
- package/adapters/qoder/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/qoder/data/hosts/registry.json +1 -0
- package/adapters/qoder/schemas/release-plan.schema.json +255 -25
- package/adapters/qoder/schemas/release-project.schema.json +228 -26
- package/adapters/qoder/skills/release-finish/SKILL.md +17 -4
- package/adapters/qoder/skills/release-help/SKILL.md +40 -39
- package/adapters/qoder/src/schemas/host-descriptor.schema.json +9 -2
- package/adapters/workbuddy/.codebuddy-plugin/plugin.json +1 -1
- package/adapters/workbuddy/bin/consumer-contract-vectors.json +7 -7
- package/adapters/workbuddy/bin/error-codes.json +1 -1
- package/adapters/workbuddy/bin/foundation-resource-binding.json +1 -1
- package/adapters/workbuddy/bin/prebuild-manifest.json +8 -4
- package/adapters/workbuddy/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/adapters/workbuddy/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/adapters/workbuddy/bin/registry.json +1 -1
- package/adapters/workbuddy/bin/release-skill.bundle.mjs +3560 -1200
- package/adapters/workbuddy/bin/rules.json +1 -1
- package/adapters/workbuddy/data/hosts/cursor/host-descriptor.json +34 -0
- package/adapters/workbuddy/data/hosts/registry.json +1 -0
- package/adapters/workbuddy/schemas/release-plan.schema.json +255 -25
- package/adapters/workbuddy/schemas/release-project.schema.json +228 -26
- package/adapters/workbuddy/skills/release-finish/SKILL.md +17 -4
- package/adapters/workbuddy/skills/release-help/SKILL.md +40 -39
- package/adapters/workbuddy/src/schemas/host-descriptor.schema.json +9 -2
- package/bin/consumer-contract-vectors.json +7 -7
- package/bin/error-codes.json +1 -1
- package/bin/foundation-resource-binding.json +1 -1
- package/bin/prebuild-manifest.json +8 -4
- package/bin/prebuilds/darwin-arm64/bound_read.darwin-arm64.node +0 -0
- package/bin/prebuilds/darwin-x64/bound_read.darwin-x64.node +0 -0
- package/bin/prebuilds/linux-arm64-gnu/bound_read.linux-arm64-gnu.node +0 -0
- package/bin/prebuilds/linux-x64-gnu/bound_read.linux-x64-gnu.node +0 -0
- package/bin/registry.json +1 -1
- package/bin/release-skill-cli.mjs +25 -1
- package/bin/release-skill.bundle.mjs +3560 -1200
- package/bin/rules.json +1 -1
- package/data/hosts/cursor/host-descriptor.json +34 -0
- package/data/hosts/registry.json +1 -0
- package/package.json +7 -5
- package/platform-manifest.json +75 -9
- package/schemas/release-plan.schema.json +255 -25
- package/schemas/release-project.schema.json +228 -26
- package/scripts/sync-public-files.mjs +3 -22
- package/skills/release-finish/SKILL.md +17 -4
- package/skills/release-help/SKILL.md +40 -39
- package/skills-src/release-finish/SKILL.md +17 -4
- package/skills-src/release-help/SKILL.md +40 -39
- package/src/adapters/contract.mjs +1 -0
- package/src/adapters/plugin-marketplace.mjs +201 -9
- package/src/commands/post-release-local.mjs +290 -34
- package/src/commands/prepare.mjs +74 -9
- package/src/commands/publish.mjs +4 -1
- package/src/commands/ship.mjs +2 -0
- package/src/commands/verify.mjs +73 -0
- package/src/core/checkpoints.mjs +5 -2
- package/src/core/config.mjs +1 -1
- package/src/core/foundation-host-verification.mjs +168 -0
- package/src/core/plan.mjs +19 -1
- package/src/core/postpublish.mjs +9 -1
- package/src/core/run-retention.mjs +1 -1
- package/src/core/surface-host-bindings.mjs +1 -0
- package/src/core/verification-gates.mjs +1 -1
- package/src/platforms/registry.mjs +225 -16
- package/src/producers/build-adapters.mjs +21 -26
- package/src/producers/foundation-resource-projection.mjs +3 -3
- package/src/schemas/host-descriptor.schema.json +9 -2
|
@@ -1,12 +1,19 @@
|
|
|
1
|
-
import { access, readFile } from 'node:fs/promises';
|
|
1
|
+
import { access, readFile, mkdir, mkdtemp, cp, rename, rm, lstat, realpath, chmod } from 'node:fs/promises';
|
|
2
2
|
import { homedir } from 'node:os';
|
|
3
|
-
import { join, relative } from 'node:path';
|
|
3
|
+
import { join, relative, isAbsolute, normalize } from 'node:path';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
readFileContained,
|
|
7
|
+
readFileStrict,
|
|
7
8
|
resolveContained,
|
|
8
9
|
superviseProcess,
|
|
9
10
|
withTemporaryWorkspace,
|
|
11
|
+
createTemporaryWorkspace,
|
|
12
|
+
createFilesystemRootBinding,
|
|
13
|
+
observeFilesystemTree,
|
|
14
|
+
createFixedSetPublicationManifest,
|
|
15
|
+
publishFixedSet,
|
|
16
|
+
replaceFixedSetAtomic,
|
|
10
17
|
} from 'skill-family-harness-node';
|
|
11
18
|
|
|
12
19
|
import { getPlatform } from '../platforms/registry.mjs';
|
|
@@ -177,6 +184,17 @@ function hubTargets(plan) {
|
|
|
177
184
|
workbuddy: 'Handle manually; WorkBuddy cannot pin a Hub ref in this flow (it follows the CodeBuddy manual boundary).',
|
|
178
185
|
};
|
|
179
186
|
return local.hosts.map((host) => {
|
|
187
|
+
if (host === 'cursor') {
|
|
188
|
+
return {
|
|
189
|
+
targetKind: 'cursor-local', executionMode: 'executable',
|
|
190
|
+
unitId: declaration.unitId, host, plugin: local.plugin,
|
|
191
|
+
version: unit?.targetVersion,
|
|
192
|
+
snapshotPath: unit?.frozenSnapshot?.path,
|
|
193
|
+
manifestDigest: unit?.frozenSnapshot?.manifestDigest,
|
|
194
|
+
cursor: local.cursor, timeoutMs: 300_000,
|
|
195
|
+
message: `Install or replace the complete frozen ${local.plugin} Cursor Local plugin; quit Cursor first and restart it afterwards.`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
180
198
|
if (host === 'qoder') {
|
|
181
199
|
return {
|
|
182
200
|
targetKind: 'hub-backed',
|
|
@@ -1388,49 +1406,120 @@ proc unexpectedDirectoryTrust {} {
|
|
|
1388
1406
|
exit 147
|
|
1389
1407
|
}
|
|
1390
1408
|
|
|
1391
|
-
proc
|
|
1392
|
-
global expect_out
|
|
1393
|
-
set dialogBuffer $
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1409
|
+
proc readDirectoryTrustDialog {prefix state timeoutCode unknownCode eofCode} {
|
|
1410
|
+
global expect_out
|
|
1411
|
+
set dialogBuffer $prefix
|
|
1412
|
+
set cleanedPrefix [cleanScreen $dialogBuffer]
|
|
1413
|
+
if {![regexp -nocase {Trust this folder\\?} $cleanedPrefix]} {
|
|
1414
|
+
expect {
|
|
1415
|
+
-nocase -re {Trust this folder\\?} {
|
|
1416
|
+
append dialogBuffer $expect_out(buffer)
|
|
1417
|
+
}
|
|
1418
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1419
|
+
eof { failEof $state $eofCode }
|
|
1397
1420
|
}
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1421
|
+
set cleanedPrefix [cleanScreen $dialogBuffer]
|
|
1422
|
+
}
|
|
1423
|
+
set framed [regexp -nocase {(^|\\n)[ \\t]*─{8,}[ \\t]*\\n[ \\t]*Trust this folder\\?} $cleanedPrefix]
|
|
1424
|
+
if {$framed} {
|
|
1425
|
+
expect {
|
|
1426
|
+
-re {(^|\\r|\\n)[ \\t]*─{8,}[ \\t]*\\r*\\n} {
|
|
1427
|
+
append dialogBuffer $expect_out(buffer)
|
|
1428
|
+
}
|
|
1429
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1430
|
+
eof { failEof $state $eofCode }
|
|
1401
1431
|
}
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1432
|
+
} else {
|
|
1433
|
+
expect {
|
|
1434
|
+
-nocase -re {↑↓[^\\r\\n]*navigate[^\\r\\n]*(?:Esc[^\\r\\n]*)?\\r*\\n} {
|
|
1435
|
+
append dialogBuffer $expect_out(buffer)
|
|
1436
|
+
}
|
|
1437
|
+
timeout { failTimeout $state $timeoutCode $unknownCode }
|
|
1438
|
+
eof { failEof $state $eofCode }
|
|
1405
1439
|
}
|
|
1406
|
-
eof { failEof directory-trust-selected-row 141 }
|
|
1407
1440
|
}
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1441
|
+
return [cleanScreen $dialogBuffer]
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
proc directoryTrustAction {dialog state unknownCode} {
|
|
1445
|
+
set inDialog 0
|
|
1446
|
+
set labels {}
|
|
1447
|
+
set selectedCount 0
|
|
1448
|
+
set selectedIndex -1
|
|
1449
|
+
set selectedLabel ""
|
|
1450
|
+
set trustCount 0
|
|
1451
|
+
set trustIndex -1
|
|
1452
|
+
foreach rawLine [split $dialog "\\n"] {
|
|
1453
|
+
set line [string trim $rawLine]
|
|
1454
|
+
if {[regexp -nocase {^Trust this folder\\?$} $line]} {
|
|
1455
|
+
set inDialog 1
|
|
1456
|
+
continue
|
|
1457
|
+
}
|
|
1458
|
+
if {!$inDialog} { continue }
|
|
1459
|
+
if {$line eq ""} { continue }
|
|
1460
|
+
set selected [regexp {^❯[ \\t]*} $line]
|
|
1461
|
+
if {$selected} {
|
|
1462
|
+
regsub {^❯[ \\t]*} $line {} label
|
|
1463
|
+
set label [string trim $label]
|
|
1464
|
+
} else {
|
|
1465
|
+
set label $line
|
|
1411
1466
|
}
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
exit
|
|
1467
|
+
set knownAction [expr {
|
|
1468
|
+
[string equal -nocase $label "Trust this folder"]
|
|
1469
|
+
|| [string equal -nocase $label "No, exit"]
|
|
1470
|
+
|| [string equal -nocase $label "Don't trust"]
|
|
1471
|
+
}]
|
|
1472
|
+
if {!$knownAction && $selected} {
|
|
1473
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1474
|
+
exit $unknownCode
|
|
1475
|
+
}
|
|
1476
|
+
if {!$knownAction} { continue }
|
|
1477
|
+
set index [llength $labels]
|
|
1478
|
+
lappend labels $label
|
|
1479
|
+
if {[string equal -nocase $label "Trust this folder"]} {
|
|
1480
|
+
incr trustCount
|
|
1481
|
+
set trustIndex $index
|
|
1482
|
+
}
|
|
1483
|
+
if {$selected} {
|
|
1484
|
+
incr selectedCount
|
|
1485
|
+
set selectedIndex $index
|
|
1486
|
+
set selectedLabel $label
|
|
1415
1487
|
}
|
|
1416
|
-
timeout { failTimeout directory-trust-target-row 148 150 }
|
|
1417
|
-
eof { failEof directory-trust-target-row 149 }
|
|
1418
1488
|
}
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
puts stderr "KIMI_TUI_STATE:directory-trust:selection-unknown"
|
|
1423
|
-
exit 143
|
|
1489
|
+
if {[llength $labels] != 2 || $trustCount != 1 || $selectedCount != 1} {
|
|
1490
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1491
|
+
exit $unknownCode
|
|
1424
1492
|
}
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1493
|
+
if {[string equal -nocase $selectedLabel "Trust this folder"]} {
|
|
1494
|
+
return selected-trust
|
|
1495
|
+
}
|
|
1496
|
+
if {![string equal -nocase $selectedLabel "No, exit"]
|
|
1497
|
+
&& ![string equal -nocase $selectedLabel "Don't trust"]} {
|
|
1498
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1499
|
+
exit $unknownCode
|
|
1500
|
+
}
|
|
1501
|
+
if {$selectedIndex + 1 == $trustIndex} { return move-down }
|
|
1502
|
+
if {$selectedIndex - 1 == $trustIndex} { return move-up }
|
|
1503
|
+
puts stderr "KIMI_TUI_STATE:$state:selection-unknown"
|
|
1504
|
+
exit $unknownCode
|
|
1505
|
+
}
|
|
1506
|
+
|
|
1507
|
+
proc confirmInitialDirectoryTrust {} {
|
|
1508
|
+
global expect_out promptPattern
|
|
1509
|
+
set dialog [readDirectoryTrustDialog $expect_out(buffer) directory-trust-selected-row 140 143 141]
|
|
1510
|
+
set action [directoryTrustAction $dialog directory-trust 143]
|
|
1511
|
+
if {$action eq "move-down"} {
|
|
1512
|
+
send -- "\\033\\[B"
|
|
1513
|
+
} elseif {$action eq "move-up"} {
|
|
1514
|
+
send -- "\\033\\[A"
|
|
1515
|
+
}
|
|
1516
|
+
if {$action ne "selected-trust"} {
|
|
1517
|
+
set confirmedDialog [readDirectoryTrustDialog "" directory-trust-confirm-selection 144 146 145]
|
|
1518
|
+
set confirmedAction [directoryTrustAction $confirmedDialog directory-trust-confirm-selection 146]
|
|
1519
|
+
if {$confirmedAction ne "selected-trust"} {
|
|
1429
1520
|
puts stderr "KIMI_TUI_STATE:directory-trust-confirm-selection:unknown"
|
|
1430
1521
|
exit 146
|
|
1431
1522
|
}
|
|
1432
|
-
timeout { failTimeout directory-trust-confirm-selection 144 146 }
|
|
1433
|
-
eof { failEof directory-trust-confirm-selection 145 }
|
|
1434
1523
|
}
|
|
1435
1524
|
send -- "\\033\\[13u"
|
|
1436
1525
|
expect {
|
|
@@ -1679,6 +1768,160 @@ async function runKimiUpdate(target, detected, run, kimiHome, {
|
|
|
1679
1768
|
return { status: 'UPDATED', version: target.version, restartRequired: true };
|
|
1680
1769
|
}
|
|
1681
1770
|
|
|
1771
|
+
async function observeCursorClosure(root) {
|
|
1772
|
+
return observeFilesystemTree({ root, rootBinding: await createFilesystemRootBinding(root) });
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
async function cursorMainProcessRunning(run) {
|
|
1776
|
+
const result = await run('/bin/ps', ['-axo', 'comm='], { timeout: 30_000 });
|
|
1777
|
+
if (typeof result?.stdout !== 'string' || result.stdout.trim().length === 0) throw new Error('Cursor process observation is unavailable');
|
|
1778
|
+
return result.stdout.split('\n').some((line) => /(?:^|\/)Cursor$/u.test(line.trim()));
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
async function runCursorLocalUpdate(target, {
|
|
1782
|
+
root, run, cursorPluginsRoot, cursorPlatform = process.platform,
|
|
1783
|
+
cursorIsRunning = () => cursorMainProcessRunning(run),
|
|
1784
|
+
cursorPublication = { publishFixedSet, replaceFixedSetAtomic },
|
|
1785
|
+
cursorMoveBackup = rename,
|
|
1786
|
+
}) {
|
|
1787
|
+
const frozen = await verifyFrozenSnapshot({ root, snapshotPath: target.snapshotPath, expectedDigest: target.manifestDigest });
|
|
1788
|
+
if (!target.cursor?.sourcePath) throw new Error('Cursor local update requires a frozen cursor.sourcePath');
|
|
1789
|
+
if (!/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(target.plugin)) throw new Error('Cursor plugin identity must use a valid Cursor plugin name');
|
|
1790
|
+
const source = await resolveContained(frozen.snapshotDir, target.cursor.sourcePath);
|
|
1791
|
+
const identity = JSON.parse(await readFileContained(source, '.cursor-plugin/plugin.json', { encoding: 'utf8' }));
|
|
1792
|
+
if (typeof identity.name !== 'string' || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(identity.name)
|
|
1793
|
+
|| identity.name !== target.plugin || identity.version !== target.version) throw new Error('Cursor frozen plugin manifest identity conflicts with the release unit');
|
|
1794
|
+
if (typeof cursorPluginsRoot !== 'string' || !isAbsolute(cursorPluginsRoot) || normalize(cursorPluginsRoot) !== cursorPluginsRoot) {
|
|
1795
|
+
throw new Error('Cursor local update requires --cursor-plugins-root <normalized-absolute-directory>');
|
|
1796
|
+
}
|
|
1797
|
+
if (cursorPlatform !== 'darwin') return { status: 'MANUAL_REQUIRED', reason: 'Cursor Local automatic installation currently supports macOS only' };
|
|
1798
|
+
try {
|
|
1799
|
+
if (await cursorIsRunning() !== false) return { status: 'MANUAL_REQUIRED', reason: 'Quit the Cursor main process before updating Local plugins' };
|
|
1800
|
+
} catch (cause) {
|
|
1801
|
+
return { status: 'MANUAL_REQUIRED', reason: `Cannot confirm Cursor has exited: ${cause.message}` };
|
|
1802
|
+
}
|
|
1803
|
+
// Bind the caller-selected root before creating any staging directory.
|
|
1804
|
+
await createFilesystemRootBinding(cursorPluginsRoot);
|
|
1805
|
+
const pluginsRoot = await realpath(cursorPluginsRoot);
|
|
1806
|
+
const sourceObservation = await observeCursorClosure(source);
|
|
1807
|
+
const workspace = await createTemporaryWorkspace({ prefix: 'release-skill-cursor-local-' });
|
|
1808
|
+
let sibling;
|
|
1809
|
+
let preserve = false;
|
|
1810
|
+
try {
|
|
1811
|
+
const candidate = join(workspace.root, 'plugin');
|
|
1812
|
+
await cp(source, candidate, { recursive: true, errorOnExist: true, force: false });
|
|
1813
|
+
if ((await observeCursorClosure(candidate)).membersDigest !== sourceObservation.membersDigest) throw new Error('Cursor candidate differs from the frozen source closure');
|
|
1814
|
+
await chmod(candidate, 0o700);
|
|
1815
|
+
if (target.cursor.dependencyInstall !== undefined) {
|
|
1816
|
+
if (target.cursor.dependencyInstall !== 'npm-ci-ignore-scripts') throw new Error('Unsupported Cursor dependency installation policy');
|
|
1817
|
+
await run('npm', ['ci', '--ignore-scripts', '--prefix', candidate], {
|
|
1818
|
+
cwd: candidate, timeout: target.timeoutMs, env: hostEnvironment('cursor'),
|
|
1819
|
+
});
|
|
1820
|
+
// npm may add dependencies, but must not mutate the frozen plugin inputs.
|
|
1821
|
+
for (const member of sourceObservation.members.filter((entry) => entry.type === 'file')) {
|
|
1822
|
+
await readFileStrict(candidate, member.path, { expectedSha256: member.sha256 });
|
|
1823
|
+
}
|
|
1824
|
+
}
|
|
1825
|
+
const prepared = await observeCursorClosure(candidate);
|
|
1826
|
+
await verifyFrozenSnapshot({ root, snapshotPath: target.snapshotPath, expectedDigest: target.manifestDigest });
|
|
1827
|
+
try {
|
|
1828
|
+
if (await cursorIsRunning() !== false) return { status: 'MANUAL_REQUIRED', reason: 'Cursor started while preparing the plugin; quit Cursor and rerun the update' };
|
|
1829
|
+
} catch (cause) {
|
|
1830
|
+
return { status: 'MANUAL_REQUIRED', reason: `Cannot confirm Cursor has exited before publication: ${cause.message}` };
|
|
1831
|
+
}
|
|
1832
|
+
const local = await resolveContained(pluginsRoot, 'local');
|
|
1833
|
+
await mkdir(local, { recursive: true });
|
|
1834
|
+
await createFilesystemRootBinding(local);
|
|
1835
|
+
const targetPath = await resolveContained(local, target.plugin);
|
|
1836
|
+
let installed = false;
|
|
1837
|
+
let previousClosure;
|
|
1838
|
+
try {
|
|
1839
|
+
await lstat(targetPath);
|
|
1840
|
+
installed = true;
|
|
1841
|
+
} catch (cause) { if (cause.code !== 'ENOENT') throw cause; }
|
|
1842
|
+
if (installed) {
|
|
1843
|
+
const current = JSON.parse(await readFileContained(targetPath, '.cursor-plugin/plugin.json', { encoding: 'utf8' }));
|
|
1844
|
+
if (current.name !== target.plugin || typeof current.version !== 'string') throw new Error('Cursor installed plugin manifest identity conflicts with the frozen plugin');
|
|
1845
|
+
const observed = await observeCursorClosure(targetPath);
|
|
1846
|
+
previousClosure = observed.membersDigest;
|
|
1847
|
+
if (current.version === target.version && observed.membersDigest === prepared.membersDigest) return { status: 'ALREADY_CURRENT', version: target.version, installPath: targetPath };
|
|
1848
|
+
}
|
|
1849
|
+
// Cursor is closed, so this same-parent candidate cannot be discovered by
|
|
1850
|
+
// a live scanner. Only this uniquely-created directory may be cleaned.
|
|
1851
|
+
sibling = await mkdtemp(join(local, `.${target.plugin}-stage-`));
|
|
1852
|
+
await cp(candidate, sibling, { recursive: true, errorOnExist: false, force: false });
|
|
1853
|
+
if ((await observeCursorClosure(sibling)).membersDigest !== prepared.membersDigest) throw new Error('Cursor publication candidate closure drift');
|
|
1854
|
+
const publication = { sourceRoot: sibling, targetParent: local, targetSegment: target.plugin };
|
|
1855
|
+
let backupPath;
|
|
1856
|
+
if (installed) {
|
|
1857
|
+
try {
|
|
1858
|
+
await cursorPublication.replaceFixedSetAtomic(publication);
|
|
1859
|
+
} catch (cause) {
|
|
1860
|
+
preserve = cause.details?.phase === 'post-commit' || cause.details?.publicationState === 'indeterminate' || cause.details?.commitState === 'indeterminate';
|
|
1861
|
+
throw cause;
|
|
1862
|
+
}
|
|
1863
|
+
// Foundation has proved the exchanged mapping. The old complete plugin
|
|
1864
|
+
// is now owned by the user and must leave the Local scanner namespace.
|
|
1865
|
+
preserve = true;
|
|
1866
|
+
try {
|
|
1867
|
+
const backups = await resolveContained(pluginsRoot, `backups/${target.plugin}`);
|
|
1868
|
+
await mkdir(backups, { recursive: true });
|
|
1869
|
+
await createFilesystemRootBinding(backups);
|
|
1870
|
+
const backupDirectory = await mkdtemp(join(backups, 'release-'));
|
|
1871
|
+
backupPath = join(backupDirectory, 'plugin');
|
|
1872
|
+
await cursorMoveBackup(sibling, backupPath);
|
|
1873
|
+
sibling = undefined;
|
|
1874
|
+
preserve = false;
|
|
1875
|
+
} catch (cause) {
|
|
1876
|
+
// One explicit reverse exchange is justified only by the verified
|
|
1877
|
+
// success above and an old directory still at the displaced path.
|
|
1878
|
+
if ((await observeCursorClosure(sibling)).membersDigest !== previousClosure
|
|
1879
|
+
|| (await observeCursorClosure(targetPath)).membersDigest !== prepared.membersDigest) throw cause;
|
|
1880
|
+
await cursorPublication.replaceFixedSetAtomic(publication);
|
|
1881
|
+
preserve = false;
|
|
1882
|
+
throw new Error(`Cursor backup move failed; original plugin restored: ${cause.message}`);
|
|
1883
|
+
}
|
|
1884
|
+
} else {
|
|
1885
|
+
const manifest = await createFixedSetPublicationManifest(publication);
|
|
1886
|
+
const receipt = await cursorPublication.publishFixedSet({ ...publication, manifest });
|
|
1887
|
+
if (receipt.status !== 'succeeded') {
|
|
1888
|
+
preserve = receipt.status === 'indeterminate' || receipt.commitState !== 'not-committed';
|
|
1889
|
+
const error = new Error(`Cursor publication failed: ${receipt.error?.message ?? receipt.status}`);
|
|
1890
|
+
error.details = receipt;
|
|
1891
|
+
throw error;
|
|
1892
|
+
}
|
|
1893
|
+
sibling = undefined;
|
|
1894
|
+
}
|
|
1895
|
+
if ((await observeCursorClosure(targetPath)).membersDigest !== prepared.membersDigest) throw new Error('Cursor installed plugin closure drift after publication');
|
|
1896
|
+
return {
|
|
1897
|
+
status: 'UPDATED', version: target.version, installPath: targetPath,
|
|
1898
|
+
...(backupPath ? { backupPath } : {}), restartRequired: true,
|
|
1899
|
+
reloadInstruction: 'Restart Cursor or run Reload Window, then verify the Local source, version and Skill invocation.',
|
|
1900
|
+
};
|
|
1901
|
+
} catch (error) {
|
|
1902
|
+
if (preserve) error.details = { ...error.details, candidatePath: sibling, workspacePath: workspace.root, manualAction: 'Keep both directories intact; inspect the Cursor Local publication mapping before retrying.' };
|
|
1903
|
+
throw error;
|
|
1904
|
+
} finally {
|
|
1905
|
+
if (!preserve) {
|
|
1906
|
+
// Sealed snapshots carry read-only directories. Restore owner access
|
|
1907
|
+
// only in this invocation's disposable copies before Foundation cleanup.
|
|
1908
|
+
for (const cleanupRoot of [sibling, workspace.root].filter(Boolean)) {
|
|
1909
|
+
const closure = await observeFilesystemTree({
|
|
1910
|
+
root: cleanupRoot,
|
|
1911
|
+
rootBinding: await createFilesystemRootBinding(cleanupRoot),
|
|
1912
|
+
symlinkPolicy: { mode: 'record' },
|
|
1913
|
+
});
|
|
1914
|
+
await chmod(cleanupRoot, 0o700);
|
|
1915
|
+
for (const member of closure.members.filter((entry) => entry.type === 'directory')) {
|
|
1916
|
+
await chmod(await resolveContained(cleanupRoot, member.path), member.statMode | 0o700);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
if (sibling) await rm(sibling, { recursive: true, force: false });
|
|
1920
|
+
await workspace.dispose();
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
}
|
|
1924
|
+
|
|
1682
1925
|
function aggregateStatus(results) {
|
|
1683
1926
|
const statuses = new Set(results.map((entry) => entry.status));
|
|
1684
1927
|
const completed = results.some((entry) => ['UPDATED', 'ALREADY_CURRENT'].includes(entry.status));
|
|
@@ -1718,6 +1961,11 @@ async function updateLocalHostPluginsInternal({
|
|
|
1718
1961
|
detect = (host) => defaultDetect(host, defaultRun),
|
|
1719
1962
|
run = defaultRun,
|
|
1720
1963
|
kimiHome,
|
|
1964
|
+
cursorPluginsRoot,
|
|
1965
|
+
cursorPlatform,
|
|
1966
|
+
cursorIsRunning,
|
|
1967
|
+
cursorPublication,
|
|
1968
|
+
cursorMoveBackup,
|
|
1721
1969
|
verifyInstalledPayload = verifyInstalledMarketplacePayload,
|
|
1722
1970
|
} = {}) {
|
|
1723
1971
|
const effectiveKimiHome = kimiHome ?? process.env.KIMI_CODE_HOME ?? join(homedir(), '.kimi-code');
|
|
@@ -1732,6 +1980,7 @@ async function updateLocalHostPluginsInternal({
|
|
|
1732
1980
|
item.executionMode !== 'manual' && selected.has(item.host)
|
|
1733
1981
|
));
|
|
1734
1982
|
for (const target of targets) {
|
|
1983
|
+
if (target.host === 'cursor') continue;
|
|
1735
1984
|
if (target.host === 'qoder') assertQoderExecutableTarget(target);
|
|
1736
1985
|
else assertExecutableTarget(target);
|
|
1737
1986
|
}
|
|
@@ -1739,6 +1988,13 @@ async function updateLocalHostPluginsInternal({
|
|
|
1739
1988
|
const results = [];
|
|
1740
1989
|
for (const target of targets) {
|
|
1741
1990
|
try {
|
|
1991
|
+
if (target.host === 'cursor') {
|
|
1992
|
+
const outcome = await runCursorLocalUpdate(target, {
|
|
1993
|
+
root, run, cursorPluginsRoot, cursorPlatform, cursorIsRunning, cursorPublication, cursorMoveBackup,
|
|
1994
|
+
});
|
|
1995
|
+
results.push({ host: target.host, unitId: target.unitId, ...outcome });
|
|
1996
|
+
continue;
|
|
1997
|
+
}
|
|
1742
1998
|
const detected = await detect(target.host);
|
|
1743
1999
|
if (!detected?.available) {
|
|
1744
2000
|
results.push({
|
package/src/commands/prepare.mjs
CHANGED
|
@@ -25,7 +25,7 @@ import { promisify } from 'node:util';
|
|
|
25
25
|
|
|
26
26
|
const execFile = promisify(execFileCb);
|
|
27
27
|
|
|
28
|
-
import { classifyPathInput, writeFileAtomic, withTemporaryWorkspace } from 'skill-family-harness-node';
|
|
28
|
+
import { classifyPathInput, writeFileAtomic, withTemporaryWorkspace, resolveContained, readFileStrict } from 'skill-family-harness-node';
|
|
29
29
|
import { loadProjectConfig } from '../core/config.mjs';
|
|
30
30
|
import { captureBaseline } from '../core/baseline.mjs';
|
|
31
31
|
import { runHook } from '../core/hooks.mjs';
|
|
@@ -63,6 +63,7 @@ import {
|
|
|
63
63
|
assertSelfBootstrapFacts,
|
|
64
64
|
} from '../core/derived-artifact-gates.mjs';
|
|
65
65
|
import { isFoundationPluginVerificationEligible } from '../core/foundation-plugin-verification.mjs';
|
|
66
|
+
import { assertCursorHostScenario } from '../core/foundation-host-verification.mjs';
|
|
66
67
|
import { PKG_ROOT } from '../core/pkg-root.mjs';
|
|
67
68
|
import { writeFrozenMarker, FROZEN_MARKER_FILENAME } from '../core/frozen-marker.mjs';
|
|
68
69
|
import {
|
|
@@ -897,12 +898,21 @@ async function processSnapshots(config, root, evidence, runDir, production = fal
|
|
|
897
898
|
}
|
|
898
899
|
|
|
899
900
|
/** Validate Hub-backed plugin identities against the already frozen snapshot. */
|
|
900
|
-
async function validateFrozenLocalHostUpdatePlugins(unitResults) {
|
|
901
|
-
for (const { unit, manifest } of unitResults) {
|
|
901
|
+
async function validateFrozenLocalHostUpdatePlugins(unitResults, resolvedVersions) {
|
|
902
|
+
for (const [index, { unit, manifest }] of unitResults.entries()) {
|
|
902
903
|
const declaration = unit.postPublish?.localHostUpdate;
|
|
903
904
|
if (!declaration) continue;
|
|
905
|
+
if (declaration.hosts.includes('cursor')) {
|
|
906
|
+
const source = await resolveContained(manifest.outputDir, declaration.cursor.sourcePath);
|
|
907
|
+
const receipt = await readFileStrict(source, '.cursor-plugin/plugin.json', { encoding: 'utf8' });
|
|
908
|
+
const cursor = JSON.parse(receipt.content);
|
|
909
|
+
if (typeof cursor.name !== 'string' || !/^[a-z0-9](?:[a-z0-9.-]*[a-z0-9])?$/u.test(cursor.name)
|
|
910
|
+
|| cursor.name !== declaration.plugin || cursor.version !== resolvedVersions[index]) {
|
|
911
|
+
throw new ReleaseError(GATE_FAILED, `unit "${unit.id}" Cursor local plugin manifest must match the frozen plugin name and target version`);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
904
914
|
const manifestEntries = (manifest.entries ?? [])
|
|
905
|
-
.filter((entry) => /(?:^|\/)\.(?:claude|codex|kimi|codebuddy)-plugin\/plugin\.json$/u.test(entry.path));
|
|
915
|
+
.filter((entry) => /(?:^|\/)\.(?:claude|codex|kimi|codebuddy|qoder|cursor)-plugin\/plugin\.json$/u.test(entry.path));
|
|
906
916
|
const identities = new Set();
|
|
907
917
|
for (const entry of manifestEntries) {
|
|
908
918
|
try {
|
|
@@ -1884,6 +1894,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
1884
1894
|
// Undeclared stays absent so legacy frozen plans remain byte-identical.
|
|
1885
1895
|
const frozenUnitDists = frozenDistributions?.get(unit.id) ?? null;
|
|
1886
1896
|
for (const platform of PLATFORMS) {
|
|
1897
|
+
if (platform.actionType === null) continue;
|
|
1887
1898
|
const dist = frozenUnitDists
|
|
1888
1899
|
? frozenUnitDists.find((d) => d.type === platform.distributionType)
|
|
1889
1900
|
: (unit.distributions ?? []).find((d) => d.type === platform.distributionType);
|
|
@@ -1896,6 +1907,10 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
1896
1907
|
// but no frozen ref/marketplaceCommitSha (production-only bindings),
|
|
1897
1908
|
// keeping the two loops' shapes aligned for plan completeness.
|
|
1898
1909
|
const externalMarketplace = dist.marketplaceRepo !== undefined && dist.marketplaceRepo !== null;
|
|
1910
|
+
const marketplaceSource = dist.marketplaceSource
|
|
1911
|
+
?? (platform.id === 'qoder' && externalMarketplace
|
|
1912
|
+
? `https://github.com/${dist.marketplaceRepo}.git`
|
|
1913
|
+
: undefined);
|
|
1899
1914
|
// Normalized marketplace form: explicit mutually exclusive declaration.
|
|
1900
1915
|
// bundled-family: marketplace and plugin live in the same repo.
|
|
1901
1916
|
// standalone-index: external marketplace repo indexes a separate plugin repo.
|
|
@@ -1937,7 +1952,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
1937
1952
|
consumer: platform.id,
|
|
1938
1953
|
plugin: dist.plugin,
|
|
1939
1954
|
...(dist.marketplace !== undefined ? { marketplace: dist.marketplace } : {}),
|
|
1940
|
-
...(
|
|
1955
|
+
...(marketplaceSource !== undefined ? { marketplaceSource } : {}),
|
|
1941
1956
|
repo: externalMarketplace ? dist.marketplaceRepo : unit.publicRepo,
|
|
1942
1957
|
version,
|
|
1943
1958
|
entrySkill: dist.entrySkill,
|
|
@@ -1965,7 +1980,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
1965
1980
|
installed: true,
|
|
1966
1981
|
plugin: dist.plugin,
|
|
1967
1982
|
...(dist.marketplace !== undefined ? { marketplace: dist.marketplace } : {}),
|
|
1968
|
-
...(
|
|
1983
|
+
...(marketplaceSource !== undefined ? { marketplaceSource } : {}),
|
|
1969
1984
|
version,
|
|
1970
1985
|
entrySkill: dist.entrySkill,
|
|
1971
1986
|
...(externalMarketplace ? { marketplaceLocation: 'external', repo: dist.marketplaceRepo } : {}),
|
|
@@ -2127,6 +2142,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
2127
2142
|
// entrySkillFound/manifestDigest expected). Marketplace identity follows
|
|
2128
2143
|
// the distribution declaration, same as the non-production loop.
|
|
2129
2144
|
for (const platform of PLATFORMS) {
|
|
2145
|
+
if (platform.actionType === null) continue;
|
|
2130
2146
|
const dist = frozenUnitDists
|
|
2131
2147
|
? frozenUnitDists.find((d) => d.type === platform.distributionType)
|
|
2132
2148
|
: (unit.distributions ?? []).find((d) => d.type === platform.distributionType);
|
|
@@ -2143,6 +2159,10 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
2143
2159
|
// bind this unit's own frozen snapshot — the payload authority is the unit
|
|
2144
2160
|
// snapshot, unchanged. Inline form (no marketplaceRepo) is byte-identical.
|
|
2145
2161
|
const externalMarketplace = dist.marketplaceRepo !== undefined && dist.marketplaceRepo !== null;
|
|
2162
|
+
const marketplaceSource = dist.marketplaceSource
|
|
2163
|
+
?? (platform.id === 'qoder' && externalMarketplace
|
|
2164
|
+
? `https://github.com/${dist.marketplaceRepo}.git`
|
|
2165
|
+
: undefined);
|
|
2146
2166
|
const freeze = externalMarketplace ? externalFreezes.get(`${unit.id} ${dist.type}`) : null;
|
|
2147
2167
|
// Normalized marketplace form: explicit mutually exclusive declaration.
|
|
2148
2168
|
// All four platforms carry marketplaceForm and sourceDescriptor.
|
|
@@ -2181,7 +2201,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
2181
2201
|
consumer: platform.id,
|
|
2182
2202
|
plugin: dist.plugin,
|
|
2183
2203
|
...(dist.marketplace !== undefined ? { marketplace: dist.marketplace } : {}),
|
|
2184
|
-
...(
|
|
2204
|
+
...(marketplaceSource !== undefined ? { marketplaceSource } : {}),
|
|
2185
2205
|
repo: externalMarketplace ? dist.marketplaceRepo : unit.publicRepo,
|
|
2186
2206
|
ref: externalMarketplace ? freeze.ref : resolvedTag,
|
|
2187
2207
|
version: unitVersion,
|
|
@@ -2225,7 +2245,7 @@ export function buildExternalActions(unitResults, resolvedVersions, productionAs
|
|
|
2225
2245
|
consumer: platform.id,
|
|
2226
2246
|
plugin: dist.plugin,
|
|
2227
2247
|
...(dist.marketplace !== undefined ? { marketplace: dist.marketplace } : {}),
|
|
2228
|
-
...(
|
|
2248
|
+
...(marketplaceSource !== undefined ? { marketplaceSource } : {}),
|
|
2229
2249
|
repo: externalMarketplace ? dist.marketplaceRepo : unit.publicRepo,
|
|
2230
2250
|
version: unitVersion,
|
|
2231
2251
|
ref: externalMarketplace ? freeze.ref : resolvedTag,
|
|
@@ -2613,6 +2633,35 @@ async function resolveDistributionManifestFacts(unitResults, resolvedVersions) {
|
|
|
2613
2633
|
if (!platform || dist.type === 'npm') continue;
|
|
2614
2634
|
const key = `${unit.id} ${dist.type}`;
|
|
2615
2635
|
|
|
2636
|
+
if (platform.installMethod === 'foundation-host-verification') {
|
|
2637
|
+
assertCursorHostScenario(dist.hostVerification);
|
|
2638
|
+
const entrySkillPath = resolve(snapshotDir, 'skills', dist.entrySkill, 'SKILL.md');
|
|
2639
|
+
let entrySkillStat;
|
|
2640
|
+
try {
|
|
2641
|
+
entrySkillStat = await lstat(entrySkillPath);
|
|
2642
|
+
} catch (err) {
|
|
2643
|
+
throw new ReleaseError(
|
|
2644
|
+
GATE_FAILED,
|
|
2645
|
+
`unit "${unit.id}" ${dist.type} entry skill "${dist.entrySkill}" is absent from the frozen payload`,
|
|
2646
|
+
{ unitId: unit.id, distributionType: dist.type, cause: err.code },
|
|
2647
|
+
);
|
|
2648
|
+
}
|
|
2649
|
+
if (!entrySkillStat.isFile() || entrySkillStat.isSymbolicLink()) {
|
|
2650
|
+
throw new ReleaseError(
|
|
2651
|
+
GATE_FAILED,
|
|
2652
|
+
`unit "${unit.id}" ${dist.type} entry skill must be a regular non-symlink SKILL.md`,
|
|
2653
|
+
{ unitId: unit.id, distributionType: dist.type },
|
|
2654
|
+
);
|
|
2655
|
+
}
|
|
2656
|
+
distributionFacts.set(key, {
|
|
2657
|
+
kind: 'host-verification',
|
|
2658
|
+
platform,
|
|
2659
|
+
entrySkill: dist.entrySkill,
|
|
2660
|
+
entrySkillRelativePath: `skills/${dist.entrySkill}/SKILL.md`,
|
|
2661
|
+
});
|
|
2662
|
+
continue;
|
|
2663
|
+
}
|
|
2664
|
+
|
|
2616
2665
|
// 校验 marketplaceSourceType:从配置读取,不允许硬编码默认值
|
|
2617
2666
|
const sourceTypeResult = validateMarketplaceSourceSelection(platform.id, dist, dist);
|
|
2618
2667
|
if (!sourceTypeResult.valid) {
|
|
@@ -2829,6 +2878,7 @@ async function runPrepareSkillResourceClosureGate({
|
|
|
2829
2878
|
(p) => p.distributionType === distribution.type,
|
|
2830
2879
|
);
|
|
2831
2880
|
if (!platform) continue;
|
|
2881
|
+
if (platform.installMethod === 'foundation-host-verification') continue;
|
|
2832
2882
|
const frozenManifest = frozenManifestByDist.get(
|
|
2833
2883
|
`${unit.id} ${distribution.type}`,
|
|
2834
2884
|
);
|
|
@@ -2903,6 +2953,7 @@ async function runPrepareSkillResourceClosureGate({
|
|
|
2903
2953
|
(p) => p.distributionType === distribution.type,
|
|
2904
2954
|
);
|
|
2905
2955
|
if (!platform) continue;
|
|
2956
|
+
if (platform.installMethod === 'foundation-host-verification') continue;
|
|
2906
2957
|
expectedHosts.push(await normalizeHostId(platform.buildAdapter.name));
|
|
2907
2958
|
}
|
|
2908
2959
|
const hostCoverage = evaluateDeclaredHostSurfaceCoverage(
|
|
@@ -4267,7 +4318,7 @@ export async function prepareRelease(options) {
|
|
|
4267
4318
|
const { unitResults, snapshotDigests } = await processSnapshots(
|
|
4268
4319
|
selectedConfig, realRoot, evidence, runDir, production,
|
|
4269
4320
|
);
|
|
4270
|
-
await validateFrozenLocalHostUpdatePlugins(unitResults);
|
|
4321
|
+
await validateFrozenLocalHostUpdatePlugins(unitResults, resolvedVersions);
|
|
4271
4322
|
|
|
4272
4323
|
// Snapshot gates always run on disposable writable copies. The public
|
|
4273
4324
|
// snapshot authority is re-digested after every gate and is never exposed
|
|
@@ -4488,6 +4539,20 @@ export async function prepareRelease(options) {
|
|
|
4488
4539
|
if (!platform || dist.type === 'npm') return dist;
|
|
4489
4540
|
const key = `${unit.id} ${dist.type}`;
|
|
4490
4541
|
const facts = distributionFacts.get(key);
|
|
4542
|
+
if (facts?.kind === 'host-verification') {
|
|
4543
|
+
const { hostVerification, ...frozenDistribution } = dist;
|
|
4544
|
+
return {
|
|
4545
|
+
...frozenDistribution,
|
|
4546
|
+
hostVerificationContract: {
|
|
4547
|
+
contractVersion: 1,
|
|
4548
|
+
scenario: structuredClone(hostVerification),
|
|
4549
|
+
hostId: facts.platform.id,
|
|
4550
|
+
payloadDigest: productionAssets?.[idx]?.manifestDigest ?? manifest.snapshotDigest,
|
|
4551
|
+
manifestRelativePath: facts.entrySkillRelativePath,
|
|
4552
|
+
entrySkill: facts.entrySkill,
|
|
4553
|
+
},
|
|
4554
|
+
};
|
|
4555
|
+
}
|
|
4491
4556
|
|
|
4492
4557
|
// includeMarketplaceEntry 代表"契约实际包含一条市场条目",
|
|
4493
4558
|
// 不能只代表平台理论上支持市场条目。
|
package/src/commands/publish.mjs
CHANGED
|
@@ -662,6 +662,7 @@ export async function publishRelease(options) {
|
|
|
662
662
|
for (const dist of frozenUnit?.distributions ?? []) {
|
|
663
663
|
const platform = PLATFORMS.find((p) => p.distributionType === dist.type);
|
|
664
664
|
if (!platform) continue;
|
|
665
|
+
if (platform.installMethod === 'foundation-host-verification') continue;
|
|
665
666
|
const contract = dist.installationContract;
|
|
666
667
|
if (!contract?.normalizedManifest) continue;
|
|
667
668
|
const pluginRoot = pluginRootFromManifestRelativePath(contract.manifestRelativePath);
|
|
@@ -696,7 +697,9 @@ export async function publishRelease(options) {
|
|
|
696
697
|
const expectedHosts = [];
|
|
697
698
|
for (const distribution of frozenUnit?.distributions ?? []) {
|
|
698
699
|
const platform = PLATFORMS.find((item) => item.distributionType === distribution.type);
|
|
699
|
-
if (platform
|
|
700
|
+
if (platform && platform.installMethod !== 'foundation-host-verification') {
|
|
701
|
+
expectedHosts.push(await normalizeHostId(platform.buildAdapter.name));
|
|
702
|
+
}
|
|
700
703
|
}
|
|
701
704
|
const hostCoverage = evaluateDeclaredHostSurfaceCoverage(
|
|
702
705
|
expectedHosts,
|
package/src/commands/ship.mjs
CHANGED
|
@@ -220,6 +220,7 @@ const ACTION_TARGET_FIELDS = Object.freeze({
|
|
|
220
220
|
'codex-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
|
|
221
221
|
'kimi-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
|
|
222
222
|
'codebuddy-marketplace-install': Object.freeze(['consumer', 'plugin', 'repo', 'ref', 'version', 'entrySkill']),
|
|
223
|
+
'qoder-marketplace-install': Object.freeze(['consumer', 'plugin', 'marketplace', 'repo', 'ref', 'version', 'entrySkill']),
|
|
223
224
|
'set-default-branch': Object.freeze(['repo', 'oldBranch', 'newBranch', 'expectedNewBranchCommit']),
|
|
224
225
|
});
|
|
225
226
|
|
|
@@ -705,6 +706,7 @@ export async function advanceShip(options = {}, injected = {}) {
|
|
|
705
706
|
adapterRegistry: options.adapterRegistry,
|
|
706
707
|
root,
|
|
707
708
|
verificationGatesAuthorized: state.verificationGatesAuthorized === true,
|
|
709
|
+
cursorHostRuntime: options.cursorHostRuntime,
|
|
708
710
|
});
|
|
709
711
|
state = {
|
|
710
712
|
...state,
|