sneakoscope 8.1.2 → 8.2.0

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.
Files changed (261) hide show
  1. package/README.md +96 -58
  2. package/crates/sks-core/Cargo.lock +1 -1
  3. package/crates/sks-core/Cargo.toml +1 -1
  4. package/dist/bin/fast-inline.js +5 -51
  5. package/dist/cli/command-manifest-lite.js +2 -1
  6. package/dist/cli/command-registry.js +2 -1
  7. package/dist/cli/global-mode-router.js +1 -1
  8. package/dist/cli/install-helpers-codex-lb-config.js +112 -914
  9. package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
  10. package/dist/cli/install-helpers-install-support.js +4 -20
  11. package/dist/cli/install-helpers.js +36 -2716
  12. package/dist/cli/router.js +9 -9
  13. package/dist/commands/bridge-command-output.js +111 -0
  14. package/dist/commands/bridge.js +320 -0
  15. package/dist/commands/codex-app.js +39 -123
  16. package/dist/commands/doctor-helpers.js +380 -0
  17. package/dist/commands/doctor-profile.js +3 -0
  18. package/dist/commands/doctor.js +114 -873
  19. package/dist/commands/mad-sks.js +0 -4
  20. package/dist/config/skills-manifest.json +1 -1
  21. package/dist/core/agents/agent-effort-policy.js +11 -21
  22. package/dist/core/agents/agent-recursion-guard.js +26 -0
  23. package/dist/core/agents/agent-runner-codex-exec.js +7 -9
  24. package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
  25. package/dist/core/architecture-hardening/state/state-service.js +49 -13
  26. package/dist/core/auto-review.js +15 -9
  27. package/dist/core/codex/codex-config-guard.js +8 -0
  28. package/dist/core/codex-adapter.js +8 -10
  29. package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
  30. package/dist/core/codex-app/codex-app-launcher.js +10 -12
  31. package/dist/core/codex-app/codex-app-restart.js +10 -12
  32. package/dist/core/codex-app/menubar/constants.js +2 -5
  33. package/dist/core/codex-app/menubar/global-install.js +3 -2
  34. package/dist/core/codex-app/menubar/installer/result.js +23 -0
  35. package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
  36. package/dist/core/codex-app/menubar/installer.js +5 -96
  37. package/dist/core/codex-app/menubar/migration.js +1 -42
  38. package/dist/core/codex-app/openrouter-provider.js +0 -78
  39. package/dist/core/codex-app.js +97 -360
  40. package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
  41. package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
  42. package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
  43. package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
  44. package/dist/core/codex-control/codex-task-runner.js +16 -60
  45. package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
  46. package/dist/core/codex-exec-output-schema.js +11 -13
  47. package/dist/core/codex-lb/bridge-contracts.js +2 -0
  48. package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
  49. package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
  50. package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
  51. package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
  52. package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
  53. package/dist/core/codex-lb/capability-runner.js +505 -201
  54. package/dist/core/codex-lb/codex-lb-env.js +179 -12
  55. package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
  56. package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
  57. package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
  58. package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
  59. package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
  60. package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
  61. package/dist/core/codex-lb/combined-catalog.js +12 -0
  62. package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
  63. package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
  64. package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
  65. package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
  66. package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
  67. package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
  68. package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
  69. package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
  70. package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
  71. package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
  72. package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
  73. package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
  74. package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
  75. package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
  76. package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
  77. package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
  78. package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
  79. package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
  80. package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
  81. package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
  82. package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
  83. package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
  84. package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
  85. package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
  86. package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
  87. package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
  88. package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
  89. package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
  90. package/dist/core/codex-lb/desktop-controller.js +2 -1730
  91. package/dist/core/codex-lb/desktop-service.js +462 -627
  92. package/dist/core/codex-lb/migration-receipt.js +560 -118
  93. package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
  94. package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
  95. package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
  96. package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
  97. package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
  98. package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
  99. package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
  100. package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
  101. package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
  102. package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
  103. package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
  104. package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
  105. package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
  106. package/dist/core/codex-lb/provider-credentials/types.js +1 -0
  107. package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
  108. package/dist/core/codex-lb/provider-credentials.js +5 -0
  109. package/dist/core/codex-lb/provider-registry.js +323 -0
  110. package/dist/core/codex-lb/provider-route-policy.js +118 -0
  111. package/dist/core/codex-lb/request-route-resolver.js +172 -0
  112. package/dist/core/codex-lb/route-index.js +148 -0
  113. package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
  114. package/dist/core/commands/basic-cli.js +1 -1
  115. package/dist/core/commands/check-command.js +3 -10
  116. package/dist/core/commands/gate-result-contract.js +10 -14
  117. package/dist/core/commands/intent-normalization/normalizer.js +1 -1
  118. package/dist/core/commands/mad-sks-command.js +2 -6
  119. package/dist/core/commands/release-command.js +11 -1
  120. package/dist/core/commands/telegram-command.js +46 -6
  121. package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
  122. package/dist/core/doctor/skill-legacy-surface.js +5 -5
  123. package/dist/core/evidence/v2/evidence-key.js +30 -3
  124. package/dist/core/feature-fixtures.js +8 -1
  125. package/dist/core/feature-registry.js +3 -2
  126. package/dist/core/hooks-runtime/stop-finalization.js +11 -1
  127. package/dist/core/hooks-runtime/subagent-context.js +15 -1
  128. package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
  129. package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
  130. package/dist/core/hooks-runtime.js +73 -87
  131. package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
  132. package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
  133. package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
  134. package/dist/core/imagegen/imagegen-capability.js +43 -151
  135. package/dist/core/init/skills/filesystem.js +89 -0
  136. package/dist/core/init/skills/inventory.js +97 -0
  137. package/dist/core/init/skills/manifest.js +151 -0
  138. package/dist/core/init/skills.js +5 -325
  139. package/dist/core/init.js +0 -63
  140. package/dist/core/install/installed-package-smoke.js +1 -1
  141. package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
  142. package/dist/core/policy/latest-version-guidance.js +15 -2
  143. package/dist/core/preflight/parallel-preflight-engine.js +27 -51
  144. package/dist/core/provider/provider-badge.js +9 -4
  145. package/dist/core/provider/provider-context.js +57 -107
  146. package/dist/core/providers/openrouter/openrouter-account.js +3 -1
  147. package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
  148. package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
  149. package/dist/core/release/gate-manifest.js +0 -1
  150. package/dist/core/release/main-push-guard.js +13 -0
  151. package/dist/core/release/main-push-receipt.js +11 -0
  152. package/dist/core/release/physical-release-gates.js +664 -61
  153. package/dist/core/release/publish-preflight.js +3 -3
  154. package/dist/core/release/release-gate-batch-runner.js +19 -6
  155. package/dist/core/release/release-gate-dag.js +33 -6
  156. package/dist/core/release/release-gate-node.js +1 -3
  157. package/dist/core/release/release-gate-output-contract.js +55 -0
  158. package/dist/core/release/release-pack-contract.js +1 -1
  159. package/dist/core/release/release-pack-receipt.js +209 -8
  160. package/dist/core/release/release-real-contract.js +1 -1
  161. package/dist/core/release/stage-publish.js +106 -28
  162. package/dist/core/retention/temp-lease.js +204 -0
  163. package/dist/core/retention.js +18 -195
  164. package/dist/core/routes.js +1 -2
  165. package/dist/core/safety/intent-contract/intent-contract.js +3 -3
  166. package/dist/core/subagents/naruto-host-credentials.js +6 -1
  167. package/dist/core/subagents/official-subagent-config.js +27 -9
  168. package/dist/core/subagents/official-subagent-runner.js +14 -31
  169. package/dist/core/subagents/wave-lifecycle.js +36 -8
  170. package/dist/core/subagents/wave-parent-guidance.js +2 -1
  171. package/dist/core/telegram/doctor.js +2 -2
  172. package/dist/core/telegram/liveness.js +10 -4
  173. package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
  174. package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
  175. package/dist/core/update/update-migration-state/session-state-split.js +247 -0
  176. package/dist/core/update/update-migration-state/simple-stages.js +46 -0
  177. package/dist/core/update/update-migration-state.js +4 -481
  178. package/dist/core/version.js +1 -1
  179. package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
  180. package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
  181. package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
  182. package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
  183. package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
  184. package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
  185. package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
  186. package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
  187. package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
  188. package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
  189. package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
  190. package/dist/scripts/canonical-test-runner.js +7 -1
  191. package/dist/scripts/check-runtime-schemas.js +7 -2
  192. package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
  193. package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
  194. package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
  195. package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
  196. package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
  197. package/dist/scripts/desktop-bridge-transport-client.js +116 -0
  198. package/dist/scripts/desktop-bridge-unification-check.js +276 -0
  199. package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
  200. package/dist/scripts/gate-policy-audit-check.js +52 -2
  201. package/dist/scripts/provider-badge-context-check.js +55 -12
  202. package/dist/scripts/publish-preflight.js +1 -2
  203. package/dist/scripts/release-affected-selector-check.js +1 -2
  204. package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
  205. package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
  206. package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
  207. package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
  208. package/dist/scripts/release-gate-batch-runner-check.js +9 -0
  209. package/dist/scripts/release-gate-dag-runner-check.js +6 -0
  210. package/dist/scripts/release-gate-dag-runner.js +4 -2
  211. package/dist/scripts/release-metadata-check.js +16 -3
  212. package/dist/scripts/release-pack-receipt.js +29 -6
  213. package/dist/scripts/release-physical-gates-check.js +39 -0
  214. package/dist/scripts/release-real-check.js +7 -5
  215. package/package.json +6 -8
  216. package/release-gates.v2.json +156 -157
  217. package/runtime-required-scripts.json +11 -11
  218. package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
  219. package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
  220. package/schemas/release/release-gate-node.schema.json +2 -3
  221. package/dist/cli/codex-lb-setup-warning-output.js +0 -17
  222. package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
  223. package/dist/commands/codex-lb.js +0 -1232
  224. package/dist/core/architecture-hardening/migration/migration.js +0 -111
  225. package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
  226. package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
  227. package/dist/core/codex-app/child-policy/child-policy.js +0 -35
  228. package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
  229. package/dist/core/codex-app/glm-model-profile.js +0 -1
  230. package/dist/core/codex-app/glm-profile-installer.js +0 -191
  231. package/dist/core/codex-app/glm-profile-schema.js +0 -42
  232. package/dist/core/codex-app/multi-provider-router.js +0 -391
  233. package/dist/core/codex-app/openrouter-activate.js +0 -405
  234. package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
  235. package/dist/core/codex-app/provider-child-policy.js +0 -64
  236. package/dist/core/codex-app/provider-mode.js +0 -91
  237. package/dist/core/codex-app/provider-session-policy.js +0 -85
  238. package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
  239. package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
  240. package/dist/core/codex-lb/codex-lb-setup.js +0 -293
  241. package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
  242. package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
  243. package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
  244. package/dist/core/codex-lb/desktop-mode.js +0 -32
  245. package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
  246. package/dist/core/codex-lb/legacy-migration.js +0 -576
  247. package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
  248. package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
  249. package/dist/core/codex-lb/routing-truth.js +0 -327
  250. package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
  251. package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
  252. package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
  253. package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
  254. package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
  255. package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
  256. package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
  257. package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
  258. package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
  259. package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
  260. package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
  261. package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
@@ -1,10 +1,14 @@
1
+ import crypto from 'node:crypto';
1
2
  import path from 'node:path';
2
3
  import fs from 'node:fs';
3
4
  import { RELEASE_ORIGIN_IDENTITY, releaseOriginIdentity } from './release-origin.js';
4
5
  import { exactNpmStageCliInvocation, localNpmStageReviewEnvironmentBlocker, REQUIRED_NPM_STAGE_CLI_VERSION } from './npm-stage-contract.js';
6
+ import { PHYSICAL_RELEASE_EVIDENCE_REPOSITORY } from './physical-release-gates.js';
7
+ import { NPM_STAGE_PUBLISH_RECEIPT_SCHEMA, STAGE_DISPATCH_NONCE_PATTERN } from './npm-stage-tarball-verifier-support.js';
5
8
  export const STAGE_PUBLISH_SCHEMA = 'sks.release-stage-publish.v1';
6
9
  export const STAGE_WORKFLOW_FILE = 'publish-npm.yml';
7
10
  export const RELEASE_BRANCH = 'main';
11
+ export const STAGE_RUN_NAME_PREFIX = 'npm-stage';
8
12
  const NPM_REGISTRY = 'https://registry.npmjs.org/';
9
13
  const STAGE_ID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
10
14
  export function stagePublish(opts) {
@@ -12,20 +16,27 @@ export function stagePublish(opts) {
12
16
  const steps = [];
13
17
  const packageIdentity = readPackageIdentity(opts.root, readJsonFile);
14
18
  const version = String(opts.version || packageIdentity.version || '').trim();
19
+ const physicalEvidenceRunId = String(opts.physicalEvidenceRunId || '').trim();
15
20
  const confirmed = opts.confirm === true;
16
21
  let commit = null;
17
22
  let runId = null;
18
23
  let stageId = null;
24
+ let dispatchNonce = null;
19
25
  const preflight = runPreflight(opts, packageIdentity.name, version);
20
26
  steps.push(...preflight.steps);
21
27
  commit = preflight.commit;
22
28
  if (!preflight.ok)
23
29
  return finish();
24
30
  if (!confirmed) {
25
- steps.push(skipped('push', 'confirm_required'), skipped('run_snapshot', 'confirm_required'), skipped('dispatch', 'confirm_required'));
31
+ steps.push(skipped('dispatch_nonce', 'confirm_required'), skipped('push', 'confirm_required'), skipped('run_snapshot', 'confirm_required'), skipped('dispatch', 'confirm_required'));
26
32
  steps.push(skipped('watch', 'confirm_required'), skipped('download', 'confirm_required'), skipped('verify', 'confirm_required'));
27
33
  return finish();
28
34
  }
35
+ dispatchNonce = String((opts.generateDispatchNonce || defaultDispatchNonce)()).trim();
36
+ const dispatchNonceOk = STAGE_DISPATCH_NONCE_PATTERN.test(dispatchNonce);
37
+ steps.push(step('dispatch_nonce', dispatchNonceOk, dispatchNonceOk ? dispatchNonce : null, dispatchNonceOk ? null : 'stage_dispatch_nonce_invalid'));
38
+ if (!dispatchNonceOk)
39
+ return finish();
29
40
  const push = opts.run('git', ['push', 'origin', RELEASE_BRANCH]);
30
41
  steps.push(step('push', push.status === 0, `git push origin ${RELEASE_BRANCH}`, push.status === 0 ? null : 'stage_push_failed'));
31
42
  if (push.status !== 0)
@@ -38,13 +49,16 @@ export function stagePublish(opts) {
38
49
  'workflow', 'run', STAGE_WORKFLOW_FILE,
39
50
  '--ref', RELEASE_BRANCH,
40
51
  '-f', `version=${version}`,
52
+ '-f', `dispatch_nonce=${dispatchNonce}`,
53
+ '-f', `physical_evidence_run_id=${physicalEvidenceRunId}`,
41
54
  '-f', 'confirm_stage=true'
42
55
  ]);
43
- steps.push(step('dispatch', dispatch.status === 0, `gh workflow run ${STAGE_WORKFLOW_FILE} version=${version} confirm_stage=true`, dispatch.status === 0 ? null : 'stage_dispatch_failed'));
56
+ steps.push(step('dispatch', dispatch.status === 0, `gh workflow run ${STAGE_WORKFLOW_FILE} version=${version} dispatch_nonce=${dispatchNonce} physical_evidence_run_id=${physicalEvidenceRunId} confirm_stage=true`, dispatch.status === 0 ? null : 'stage_dispatch_failed'));
44
57
  if (dispatch.status !== 0)
45
58
  return finish();
46
- runId = resolveRunId(opts, commit, priorRuns.ids);
47
- steps.push(step('resolve_run', Boolean(runId), runId ? `run ${runId}` : null, runId ? null : 'stage_run_not_found'));
59
+ const resolvedRun = resolveRunId(opts, commit, priorRuns.ids, stageRunDisplayTitle(version, dispatchNonce, physicalEvidenceRunId));
60
+ runId = resolvedRun.runId;
61
+ steps.push(step('resolve_run', Boolean(runId), runId ? `run ${runId}` : null, runId ? null : resolvedRun.blocker));
48
62
  if (!runId)
49
63
  return finish();
50
64
  const watch = opts.run('gh', ['run', 'watch', runId, '--exit-status'], { timeoutMs: opts.watchTimeoutMs ?? 3 * 60 * 60 * 1000 });
@@ -52,11 +66,11 @@ export function stagePublish(opts) {
52
66
  if (watch.status !== 0)
53
67
  return finish();
54
68
  const artifactDir = opts.artifactDir || path.join(opts.root, '.sneakoscope', 'reports', 'release', version, 'stage');
55
- const download = downloadArtifacts(opts, runId, commit, artifactDir);
69
+ const download = downloadArtifacts(opts, runId, commit, dispatchNonce, artifactDir);
56
70
  steps.push(download.step);
57
71
  if (!download.step.ok)
58
72
  return finish();
59
- const receipt = readStageReceipt(artifactDir, commit, readJsonFile);
73
+ const receipt = readStageReceipt(artifactDir, commit, dispatchNonce, physicalEvidenceRunId, runId, readJsonFile);
60
74
  stageId = receipt.stageId;
61
75
  steps.push(step('stage_receipt', Boolean(stageId), receipt.path, stageId ? null : receipt.blocker));
62
76
  if (!stageId || !receipt.path)
@@ -65,7 +79,10 @@ export function stagePublish(opts) {
65
79
  handoffDir: receipt.handoffDir,
66
80
  stageReceiptPath: receipt.path,
67
81
  version,
68
- stageId
82
+ stageId,
83
+ dispatchNonce,
84
+ physicalEvidenceRunId,
85
+ workflowRunId: runId
69
86
  });
70
87
  steps.push(verify);
71
88
  return finish();
@@ -77,6 +94,9 @@ export function stagePublish(opts) {
77
94
  ok: blockers.length === 0 && (confirmed ? reachedApproval : true),
78
95
  confirmed,
79
96
  version,
97
+ release_tag: `v${version}`,
98
+ physical_evidence_run_id: /^\d+$/.test(physicalEvidenceRunId) ? physicalEvidenceRunId : null,
99
+ dispatch_nonce: dispatchNonce,
80
100
  commit,
81
101
  run_id: runId,
82
102
  stage_id: stageId,
@@ -104,11 +124,24 @@ function runPreflight(opts, packageName, version) {
104
124
  const steps = [];
105
125
  const branch = text(opts.run('git', ['rev-parse', '--abbrev-ref', 'HEAD']));
106
126
  steps.push(step('branch', branch === RELEASE_BRANCH, branch, branch === RELEASE_BRANCH ? null : 'stage_requires_main_branch'));
127
+ const commit = text(opts.run('git', ['rev-parse', 'HEAD'])) || null;
107
128
  const status = opts.run('git', ['status', '--porcelain']);
108
129
  const clean = status.status === 0 && text(status) === '';
109
130
  steps.push(step('clean_tree', clean, clean ? 'clean' : 'dirty', clean ? null : 'stage_requires_clean_tree'));
110
131
  const versionOk = /^\d+\.\d+\.\d+$/.test(version);
111
132
  steps.push(step('version', versionOk, version || null, versionOk ? null : 'stage_version_invalid'));
133
+ const releaseTag = versionOk ? `v${version}` : '';
134
+ const localTag = releaseTag
135
+ ? text(opts.run('git', ['rev-parse', `refs/tags/${releaseTag}^{commit}`]))
136
+ : '';
137
+ const localTagOk = Boolean(commit) && localTag === commit;
138
+ steps.push(step('local_release_tag', localTagOk, localTag || releaseTag || null, localTagOk ? null : 'stage_local_release_tag_mismatch'));
139
+ const remoteTagResult = releaseTag
140
+ ? opts.run('git', ['ls-remote', '--exit-code', 'origin', `refs/tags/${releaseTag}`, `refs/tags/${releaseTag}^{}`])
141
+ : { status: 1, stdout: '', stderr: '' };
142
+ const remoteTag = remoteTagResult.status === 0 ? remoteReleaseTagCommit(remoteTagResult.stdout, releaseTag) : '';
143
+ const remoteTagOk = Boolean(commit) && remoteTag === commit;
144
+ steps.push(step('remote_release_tag', remoteTagOk, remoteTag || releaseTag || null, remoteTagOk ? null : 'stage_remote_release_tag_mismatch'));
112
145
  const packageNameOk = packageName.length > 0;
113
146
  steps.push(step('package_name', packageNameOk, packageName || null, packageNameOk ? null : 'stage_package_name_invalid'));
114
147
  const origin = releaseOriginIdentity(opts.root);
@@ -122,11 +155,27 @@ function runPreflight(opts, packageName, version) {
122
155
  const verifierReady = fs.existsSync(verifier);
123
156
  steps.push(step('local_review_verifier', verifierReady, verifierReady ? verifier : null, verifierReady ? null : 'stage_verifier_unavailable_outside_checkout'));
124
157
  const physicalVerifier = path.join(opts.root, 'dist', 'scripts', 'release-physical-gates-check.js');
125
- const physical = opts.run(process.execPath, [physicalVerifier]);
158
+ const physicalEvidenceRunId = String(opts.physicalEvidenceRunId || '').trim();
159
+ const physicalEvidenceRunIdReady = /^\d+$/.test(physicalEvidenceRunId);
160
+ steps.push(step('physical_evidence_run_id', physicalEvidenceRunIdReady, physicalEvidenceRunId || null, physicalEvidenceRunIdReady ? null : 'stage_physical_evidence_run_id_invalid'));
161
+ const physicalArgs = [
162
+ physicalVerifier,
163
+ ...(opts.physicalEvidenceArchive ? ['--archive', path.resolve(opts.root, opts.physicalEvidenceArchive)] : []),
164
+ '--evidence-run-id', physicalEvidenceRunId,
165
+ '--repository', PHYSICAL_RELEASE_EVIDENCE_REPOSITORY
166
+ ];
167
+ const physical = physicalEvidenceRunIdReady
168
+ ? opts.run(process.execPath, physicalArgs)
169
+ : { status: 1, stdout: '', stderr: 'physical evidence run id missing or invalid' };
126
170
  const physicalReport = safeJsonObject(physical.stdout);
127
- const physicalReady = physical.status === 0 && physicalReport?.ok === true;
171
+ const physicalReady = physical.status === 0
172
+ && physicalReport?.schema === 'sks.release-physical-gates-inspection.v2'
173
+ && physicalReport?.ok === true
174
+ && physicalReport?.status === 'passed'
175
+ && physicalReport?.release_authorizing === true
176
+ && physicalReport?.inspector_platform === 'darwin';
128
177
  steps.push(step('physical_release_gates', physicalReady, physicalReady
129
- ? 'all four source-bound physical release receipts verified'
178
+ ? 'all five source-bound physical release receipts and live Desktop Bridge artifacts verified on macOS'
130
179
  : compactPhysicalGateFailure(physical, physicalReport), physicalReady ? null : 'stage_physical_release_gates_invalid'));
131
180
  const reviewEnvironmentBlocker = localNpmStageReviewEnvironmentBlocker(opts.env || process.env);
132
181
  steps.push(step('local_review_environment', reviewEnvironmentBlocker === null, reviewEnvironmentBlocker === null ? 'maintainer-local' : reviewEnvironmentBlocker, reviewEnvironmentBlocker === null ? null : `stage_${reviewEnvironmentBlocker}`));
@@ -184,30 +233,44 @@ function runPreflight(opts, packageName, version) {
184
233
  }
185
234
  const gh = opts.run('gh', ['auth', 'status']);
186
235
  steps.push(step('gh_auth', gh.status === 0, gh.status === 0 ? 'authenticated' : null, gh.status === 0 ? null : 'stage_gh_not_authenticated'));
187
- const commit = text(opts.run('git', ['rev-parse', 'HEAD'])) || null;
188
236
  return { ok: steps.every((entry) => entry.ok), commit, steps };
189
237
  }
190
- function resolveRunId(opts, commit, priorRunIds) {
238
+ function remoteReleaseTagCommit(output, tag) {
239
+ const rows = String(output || '').trim().split(/\r?\n/).filter(Boolean);
240
+ const peeled = rows.find((row) => row.endsWith(`refs/tags/${tag}^{}`));
241
+ return text({ status: 0, stdout: peeled || rows[0] || '', stderr: '' }).split(/\s+/, 1)[0] || '';
242
+ }
243
+ export function stageRunDisplayTitle(version, dispatchNonce, physicalEvidenceRunId) {
244
+ return `${STAGE_RUN_NAME_PREFIX}-${version}-${dispatchNonce}-physical-${physicalEvidenceRunId}`;
245
+ }
246
+ function defaultDispatchNonce() {
247
+ return crypto.randomBytes(16).toString('hex');
248
+ }
249
+ function resolveRunId(opts, commit, priorRunIds, expectedDisplayTitle) {
191
250
  if (!commit)
192
- return null;
251
+ return { runId: null, blocker: 'stage_run_not_found' };
193
252
  for (let attempt = 0; attempt < 30; attempt += 1) {
194
253
  const listed = listWorkflowRuns(opts);
195
254
  if (listed.status === 0) {
196
255
  const rows = safeJsonArray(listed.stdout);
197
- const match = rows.find((row) => {
256
+ const matches = rows.filter((row) => {
198
257
  const id = String(row?.databaseId ?? '');
199
258
  const event = String(row?.event || '');
200
259
  return String(row?.headSha || '') === commit
260
+ && String(row?.displayTitle || '') === expectedDisplayTitle
201
261
  && id.length > 0
202
262
  && !priorRunIds.has(id)
203
- && (!event || event === 'workflow_dispatch');
263
+ && event === 'workflow_dispatch';
204
264
  });
205
- if (match?.databaseId != null)
206
- return String(match.databaseId);
265
+ if (matches.length > 1)
266
+ return { runId: null, blocker: 'stage_run_ambiguous' };
267
+ if (matches[0]?.databaseId != null) {
268
+ return { runId: String(matches[0].databaseId), blocker: 'stage_run_not_found' };
269
+ }
207
270
  }
208
271
  opts.run('sleep', ['2']);
209
272
  }
210
- return null;
273
+ return { runId: null, blocker: 'stage_run_not_found' };
211
274
  }
212
275
  function snapshotWorkflowRunIds(opts, commit) {
213
276
  if (!commit)
@@ -226,12 +289,12 @@ function listWorkflowRuns(opts) {
226
289
  'run', 'list',
227
290
  '--workflow', STAGE_WORKFLOW_FILE,
228
291
  '--branch', RELEASE_BRANCH,
229
- '--limit', '20',
230
- '--json', 'databaseId,headSha,status,event'
292
+ '--limit', '100',
293
+ '--json', 'databaseId,headSha,status,event,displayTitle'
231
294
  ]);
232
295
  }
233
- function downloadArtifacts(opts, runId, commit, dir) {
234
- const names = stageArtifactNames(commit);
296
+ function downloadArtifacts(opts, runId, commit, dispatchNonce, dir) {
297
+ const names = stageArtifactNames(commit, dispatchNonce);
235
298
  if (!names)
236
299
  return { step: step('download', false, null, 'stage_commit_unknown') };
237
300
  const args = ['run', 'download', runId, '--dir', dir];
@@ -240,16 +303,16 @@ function downloadArtifacts(opts, runId, commit, dir) {
240
303
  const result = opts.run('gh', args);
241
304
  return { step: step('download', result.status === 0, dir, result.status === 0 ? null : 'stage_artifact_download_failed') };
242
305
  }
243
- function stageArtifactNames(commit) {
244
- if (!commit)
306
+ function stageArtifactNames(commit, dispatchNonce) {
307
+ if (!commit || !STAGE_DISPATCH_NONCE_PATTERN.test(dispatchNonce))
245
308
  return null;
246
309
  return {
247
- handoff: `stage-input-${commit}`,
248
- receipt: `npm-stage-receipt-${commit}`
310
+ handoff: `stage-input-${commit}-${dispatchNonce}`,
311
+ receipt: `npm-stage-receipt-${commit}-${dispatchNonce}`
249
312
  };
250
313
  }
251
- function readStageReceipt(dir, commit, readJsonFile) {
252
- const names = stageArtifactNames(commit);
314
+ function readStageReceipt(dir, commit, dispatchNonce, physicalEvidenceRunId, workflowRunId, readJsonFile) {
315
+ const names = stageArtifactNames(commit, dispatchNonce);
253
316
  const layouts = names
254
317
  ? [
255
318
  {
@@ -266,9 +329,21 @@ function readStageReceipt(dir, commit, readJsonFile) {
266
329
  const stageId = String(payload?.stage_id || '').trim();
267
330
  if (!stageId)
268
331
  continue;
332
+ if (payload?.schema !== NPM_STAGE_PUBLISH_RECEIPT_SCHEMA) {
333
+ return { stageId: null, path: file, handoffDir: layout.handoffDir, blocker: 'stage_receipt_schema_invalid' };
334
+ }
269
335
  if (!STAGE_ID_RE.test(stageId)) {
270
336
  return { stageId: null, path: file, handoffDir: layout.handoffDir, blocker: 'stage_id_uuid_invalid' };
271
337
  }
338
+ if (String(payload?.dispatch_nonce || '') !== dispatchNonce) {
339
+ return { stageId: null, path: file, handoffDir: layout.handoffDir, blocker: 'stage_receipt_dispatch_nonce_mismatch' };
340
+ }
341
+ if (String(payload?.physical_evidence_run_id || '') !== physicalEvidenceRunId) {
342
+ return { stageId: null, path: file, handoffDir: layout.handoffDir, blocker: 'stage_receipt_physical_evidence_run_id_mismatch' };
343
+ }
344
+ if (String(payload?.workflow_run_id || '') !== workflowRunId) {
345
+ return { stageId: null, path: file, handoffDir: layout.handoffDir, blocker: 'stage_receipt_workflow_run_id_mismatch' };
346
+ }
272
347
  return { stageId, path: file, handoffDir: layout.handoffDir, blocker: null };
273
348
  }
274
349
  }
@@ -281,6 +356,9 @@ function runLocalVerify(opts, input) {
281
356
  const result = opts.run(process.execPath, [
282
357
  verifier,
283
358
  '--stage-id', input.stageId,
359
+ '--dispatch-nonce', input.dispatchNonce,
360
+ '--physical-evidence-run-id', input.physicalEvidenceRunId,
361
+ '--workflow-run-id', input.workflowRunId,
284
362
  '--local-receipt', path.join(input.handoffDir, 'pack-receipt.json'),
285
363
  '--local-tarball', path.join(input.handoffDir, `sneakoscope-${input.version}.tgz`),
286
364
  '--stage-receipt', input.stageReceiptPath
@@ -0,0 +1,204 @@
1
+ import { randomUUID } from 'node:crypto';
2
+ import fs from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { exists, readJson, rmrf, SKS_TEMP_LEASE_FILE } from '../fsx.js';
5
+ const TEMP_RETENTION_SCAN_MAX_ENTRIES = 100_000;
6
+ const TEMP_RETENTION_SCAN_MAX_DEPTH = 20;
7
+ export async function inspectTempPath(target) {
8
+ const rootStat = await fs.lstat(target).catch(() => null);
9
+ if (!rootStat || rootStat.isSymbolicLink()) {
10
+ return {
11
+ complete: false,
12
+ latestMtimeMs: 0,
13
+ bytes: 0,
14
+ blockers: [rootStat ? 'temp_path_is_symlink' : 'temp_path_missing_or_unreadable']
15
+ };
16
+ }
17
+ let latestMtimeMs = rootStat.mtimeMs;
18
+ let bytes = rootStat.isFile() ? rootStat.size : 0;
19
+ let entryCount = 0;
20
+ const blockers = [];
21
+ const stack = rootStat.isDirectory()
22
+ ? [{ dir: target, depth: 0 }]
23
+ : [];
24
+ while (stack.length) {
25
+ const current = stack.pop();
26
+ let entries;
27
+ try {
28
+ entries = await fs.readdir(current.dir, { withFileTypes: true });
29
+ }
30
+ catch {
31
+ blockers.push('temp_path_readdir_failed');
32
+ continue;
33
+ }
34
+ for (const entry of entries) {
35
+ entryCount += 1;
36
+ if (entryCount > TEMP_RETENTION_SCAN_MAX_ENTRIES) {
37
+ blockers.push(`temp_path_max_entries_exceeded:${TEMP_RETENTION_SCAN_MAX_ENTRIES}`);
38
+ return { complete: false, latestMtimeMs, bytes, blockers: [...new Set(blockers)] };
39
+ }
40
+ const child = path.join(current.dir, entry.name);
41
+ const stat = await fs.lstat(child).catch(() => null);
42
+ if (!stat) {
43
+ blockers.push('temp_path_stat_failed');
44
+ continue;
45
+ }
46
+ latestMtimeMs = Math.max(latestMtimeMs, stat.mtimeMs);
47
+ if (stat.isSymbolicLink())
48
+ continue;
49
+ if (stat.isDirectory()) {
50
+ if (current.depth >= TEMP_RETENTION_SCAN_MAX_DEPTH) {
51
+ blockers.push(`temp_path_max_depth_exceeded:${TEMP_RETENTION_SCAN_MAX_DEPTH}`);
52
+ }
53
+ else {
54
+ stack.push({ dir: child, depth: current.depth + 1 });
55
+ }
56
+ continue;
57
+ }
58
+ if (stat.isFile())
59
+ bytes += stat.size;
60
+ }
61
+ }
62
+ return {
63
+ complete: blockers.length === 0,
64
+ latestMtimeMs,
65
+ bytes,
66
+ blockers: [...new Set(blockers)]
67
+ };
68
+ }
69
+ export function currentProcessOwns(stat) {
70
+ if (typeof process.getuid !== 'function')
71
+ return true;
72
+ return stat.uid === process.getuid();
73
+ }
74
+ export function sharedTempEntryMatchesProject(entryName, projectHash) {
75
+ return entryName === `sks-${projectHash}` || entryName.startsWith(`sks-${projectHash}-`);
76
+ }
77
+ export function activeTempEnvironmentKey(target) {
78
+ const resolvedTarget = path.resolve(target);
79
+ for (const key of ['SKS_TMP_DIR', 'TMPDIR', 'TMP', 'TEMP']) {
80
+ const raw = process.env[key];
81
+ if (!raw)
82
+ continue;
83
+ const activePath = path.resolve(raw);
84
+ if (isWithin(resolvedTarget, activePath))
85
+ return key;
86
+ }
87
+ return null;
88
+ }
89
+ export async function liveTempLease(target) {
90
+ const leasePath = path.join(target, SKS_TEMP_LEASE_FILE);
91
+ const stat = await fs.lstat(leasePath).catch(() => null);
92
+ if (!stat?.isFile() || stat.isSymbolicLink() || !currentProcessOwns(stat))
93
+ return null;
94
+ const lease = await readJson(leasePath, null).catch(() => null);
95
+ const pid = Number(lease?.pid);
96
+ if (lease?.schema !== 'sks.temp-lease.v1' || !processIdAlive(pid))
97
+ return null;
98
+ return {
99
+ path: leasePath,
100
+ pid,
101
+ kind: lease?.kind ? String(lease.kind) : null
102
+ };
103
+ }
104
+ export async function removeDeadCanonicalTestLease(base, target, targetStat, dryRun, actions) {
105
+ if (!targetStat.isDirectory() || targetStat.isSymbolicLink() || !currentProcessOwns(targetStat))
106
+ return false;
107
+ const [realBase, realTarget] = await Promise.all([
108
+ fs.realpath(base).catch(() => null),
109
+ fs.realpath(target).catch(() => null)
110
+ ]);
111
+ if (!realBase || !realTarget || path.dirname(realTarget) !== realBase || !isWithin(realBase, realTarget))
112
+ return false;
113
+ const leasePath = path.join(target, SKS_TEMP_LEASE_FILE);
114
+ const leaseLstat = await fs.lstat(leasePath).catch(() => null);
115
+ if (!leaseLstat?.isFile()
116
+ || leaseLstat.isSymbolicLink()
117
+ || !currentProcessOwns(leaseLstat)
118
+ || leaseLstat.size > 4096)
119
+ return false;
120
+ const leaseHandle = await fs.open(leasePath, 'r').catch(() => null);
121
+ if (!leaseHandle)
122
+ return false;
123
+ let lease = null;
124
+ try {
125
+ const leaseStat = await leaseHandle.stat();
126
+ if (!leaseStat.isFile()
127
+ || !currentProcessOwns(leaseStat)
128
+ || leaseStat.dev !== leaseLstat.dev
129
+ || leaseStat.ino !== leaseLstat.ino)
130
+ return false;
131
+ lease = JSON.parse(await leaseHandle.readFile({ encoding: 'utf8' }));
132
+ }
133
+ catch {
134
+ return false;
135
+ }
136
+ finally {
137
+ await leaseHandle.close().catch(() => undefined);
138
+ }
139
+ const leaseKeys = lease && typeof lease === 'object' && !Array.isArray(lease)
140
+ ? Object.keys(lease).sort()
141
+ : [];
142
+ const pid = Number(lease?.pid);
143
+ if (lease?.schema !== 'sks.temp-lease.v1'
144
+ || lease?.kind !== 'canonical-test-runner'
145
+ || !Number.isSafeInteger(pid)
146
+ || pid <= 0
147
+ || typeof lease?.created_at !== 'string'
148
+ || !Number.isFinite(Date.parse(lease.created_at))
149
+ || leaseKeys.join(',') !== 'created_at,kind,pid,schema'
150
+ || processIdAlive(pid))
151
+ return false;
152
+ const currentTargetStat = await fs.lstat(target).catch(() => null);
153
+ if (!currentTargetStat?.isDirectory()
154
+ || currentTargetStat.isSymbolicLink()
155
+ || !currentProcessOwns(currentTargetStat)
156
+ || currentTargetStat.dev !== targetStat.dev
157
+ || currentTargetStat.ino !== targetStat.ino)
158
+ return false;
159
+ const action = {
160
+ action: 'remove_sks_temp',
161
+ path: target,
162
+ reason: 'dead_canonical_test_lease',
163
+ lease_path: leasePath,
164
+ owner_pid: pid,
165
+ lease_kind: lease.kind
166
+ };
167
+ if (dryRun) {
168
+ actions.push(action);
169
+ return true;
170
+ }
171
+ const quarantine = path.join(realBase, `.sks-retention-quarantine-${process.pid}-${randomUUID()}`);
172
+ try {
173
+ await fs.rename(target, quarantine);
174
+ }
175
+ catch {
176
+ return false;
177
+ }
178
+ const quarantinedStat = await fs.lstat(quarantine).catch(() => null);
179
+ if (!quarantinedStat
180
+ || quarantinedStat.dev !== targetStat.dev
181
+ || quarantinedStat.ino !== targetStat.ino) {
182
+ if (!(await exists(target)))
183
+ await fs.rename(quarantine, target).catch(() => undefined);
184
+ return false;
185
+ }
186
+ await rmrf(quarantine);
187
+ actions.push(action);
188
+ return true;
189
+ }
190
+ function processIdAlive(pid) {
191
+ if (!Number.isSafeInteger(pid) || pid <= 0)
192
+ return false;
193
+ try {
194
+ process.kill(pid, 0);
195
+ return true;
196
+ }
197
+ catch (error) {
198
+ return error?.code === 'EPERM';
199
+ }
200
+ }
201
+ function isWithin(parent, candidate) {
202
+ const rel = path.relative(path.resolve(parent), path.resolve(candidate));
203
+ return rel === '' || (!rel.startsWith('..') && !path.isAbsolute(rel));
204
+ }