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.
- package/README.md +96 -58
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/dist/bin/fast-inline.js +5 -51
- package/dist/cli/command-manifest-lite.js +2 -1
- package/dist/cli/command-registry.js +2 -1
- package/dist/cli/global-mode-router.js +1 -1
- package/dist/cli/install-helpers-codex-lb-config.js +112 -914
- package/dist/cli/install-helpers-codex-lb-shared.js +3 -44
- package/dist/cli/install-helpers-install-support.js +4 -20
- package/dist/cli/install-helpers.js +36 -2716
- package/dist/cli/router.js +9 -9
- package/dist/commands/bridge-command-output.js +111 -0
- package/dist/commands/bridge.js +320 -0
- package/dist/commands/codex-app.js +39 -123
- package/dist/commands/doctor-helpers.js +380 -0
- package/dist/commands/doctor-profile.js +3 -0
- package/dist/commands/doctor.js +114 -873
- package/dist/commands/mad-sks.js +0 -4
- package/dist/config/skills-manifest.json +1 -1
- package/dist/core/agents/agent-effort-policy.js +11 -21
- package/dist/core/agents/agent-recursion-guard.js +26 -0
- package/dist/core/agents/agent-runner-codex-exec.js +7 -9
- package/dist/core/architecture-hardening/contracts/contracts.js +225 -161
- package/dist/core/architecture-hardening/state/state-service.js +49 -13
- package/dist/core/auto-review.js +15 -9
- package/dist/core/codex/codex-config-guard.js +8 -0
- package/dist/core/codex-adapter.js +8 -10
- package/dist/core/codex-app/codex-app-fast-ui-repair.js +7 -29
- package/dist/core/codex-app/codex-app-launcher.js +10 -12
- package/dist/core/codex-app/codex-app-restart.js +10 -12
- package/dist/core/codex-app/menubar/constants.js +2 -5
- package/dist/core/codex-app/menubar/global-install.js +3 -2
- package/dist/core/codex-app/menubar/installer/result.js +23 -0
- package/dist/core/codex-app/menubar/installer/runtime.js +28 -0
- package/dist/core/codex-app/menubar/installer.js +5 -96
- package/dist/core/codex-app/menubar/migration.js +1 -42
- package/dist/core/codex-app/openrouter-provider.js +0 -78
- package/dist/core/codex-app.js +97 -360
- package/dist/core/codex-control/codex-app-server-runtime-env.js +8 -28
- package/dist/core/codex-control/codex-current-image-path-real-probe.js +2 -3
- package/dist/core/codex-control/codex-current-web-search-probe.js +2 -3
- package/dist/core/codex-control/codex-sdk-adapter.js +5 -48
- package/dist/core/codex-control/codex-task-runner.js +16 -60
- package/dist/core/codex-control/desktop-bridge-launch-guard.js +227 -0
- package/dist/core/codex-exec-output-schema.js +11 -13
- package/dist/core/codex-lb/bridge-contracts.js +2 -0
- package/dist/core/codex-lb/bridge-runtime-validation/capability.js +104 -0
- package/dist/core/codex-lb/bridge-runtime-validation/catalog.js +52 -0
- package/dist/core/codex-lb/bridge-runtime-validation/shared.js +109 -0
- package/dist/core/codex-lb/bridge-runtime-validation/status.js +250 -0
- package/dist/core/codex-lb/bridge-runtime-validation.js +106 -0
- package/dist/core/codex-lb/capability-runner.js +505 -201
- package/dist/core/codex-lb/codex-lb-env.js +179 -12
- package/dist/core/codex-lb/combined-catalog/builder.js +86 -0
- package/dist/core/codex-lb/combined-catalog/contracts.js +5 -0
- package/dist/core/codex-lb/combined-catalog/normalize.js +170 -0
- package/dist/core/codex-lb/combined-catalog/persistence.js +207 -0
- package/dist/core/codex-lb/combined-catalog/shared.js +12 -0
- package/dist/core/codex-lb/combined-catalog/storage.js +171 -0
- package/dist/core/codex-lb/combined-catalog.js +12 -0
- package/dist/core/codex-lb/desktop-bridge/client.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/header-policy.js +48 -63
- package/dist/core/codex-lb/desktop-bridge/http-forward.js +115 -83
- package/dist/core/codex-lb/desktop-bridge/launchd.js +20 -13
- package/dist/core/codex-lb/desktop-bridge/location-rewrite.js +4 -1
- package/dist/core/codex-lb/desktop-bridge/security.js +381 -267
- package/dist/core/codex-lb/desktop-bridge/server.js +148 -19
- package/dist/core/codex-lb/desktop-bridge/state.js +56 -70
- package/dist/core/codex-lb/desktop-bridge/types.js +6 -1
- package/dist/core/codex-lb/desktop-bridge/websocket-forward.js +281 -106
- package/dist/core/codex-lb/desktop-bridge-migration/historical-config.js +245 -0
- package/dist/core/codex-lb/desktop-bridge-migration/historical-toml.js +56 -0
- package/dist/core/codex-lb/desktop-bridge-migration/metadata.js +73 -0
- package/dist/core/codex-lb/desktop-bridge-migration/receipt.js +52 -0
- package/dist/core/codex-lb/desktop-bridge-migration/retired-runtime-cleanup.js +106 -0
- package/dist/core/codex-lb/desktop-bridge-migration/types.js +1 -0
- package/dist/core/codex-lb/desktop-bridge-migration.js +238 -0
- package/dist/core/codex-lb/desktop-controller-v3/catalog.js +257 -0
- package/dist/core/codex-lb/desktop-controller-v3/diagnostics.js +62 -0
- package/dist/core/codex-lb/desktop-controller-v3/lifecycle-commands.js +203 -0
- package/dist/core/codex-lb/desktop-controller-v3/live-probes.js +293 -0
- package/dist/core/codex-lb/desktop-controller-v3/preflight-probes.js +144 -0
- package/dist/core/codex-lb/desktop-controller-v3/provider-commands.js +182 -0
- package/dist/core/codex-lb/desktop-controller-v3/shared.js +279 -0
- package/dist/core/codex-lb/desktop-controller-v3/status.js +269 -0
- package/dist/core/codex-lb/desktop-controller-v3/types.js +1 -0
- package/dist/core/codex-lb/desktop-controller-v3/verification.js +89 -0
- package/dist/core/codex-lb/desktop-controller-v3.js +67 -0
- package/dist/core/codex-lb/desktop-controller.js +2 -1730
- package/dist/core/codex-lb/desktop-service.js +462 -627
- package/dist/core/codex-lb/migration-receipt.js +560 -118
- package/dist/core/codex-lb/probes/auxiliary-surfaces-probe.js +37 -53
- package/dist/core/codex-lb/probes/bridge-probe.js +125 -64
- package/dist/core/codex-lb/probes/catalog-probe.js +36 -136
- package/dist/core/codex-lb/probes/computer-use-probe.js +63 -39
- package/dist/core/codex-lb/probes/image-generation-probe.js +98 -41
- package/dist/core/codex-lb/probes/probe-evidence.js +32 -33
- package/dist/core/codex-lb/probes/voice-realtime-probe.js +69 -43
- package/dist/core/codex-lb/provider-credentials/configuration.js +127 -0
- package/dist/core/codex-lb/provider-credentials/locks.js +11 -0
- package/dist/core/codex-lb/provider-credentials/private-snapshot.js +28 -0
- package/dist/core/codex-lb/provider-credentials/removal.js +33 -0
- package/dist/core/codex-lb/provider-credentials/resolution.js +59 -0
- package/dist/core/codex-lb/provider-credentials/runtime.js +55 -0
- package/dist/core/codex-lb/provider-credentials/types.js +1 -0
- package/dist/core/codex-lb/provider-credentials/validation-store.js +137 -0
- package/dist/core/codex-lb/provider-credentials.js +5 -0
- package/dist/core/codex-lb/provider-registry.js +323 -0
- package/dist/core/codex-lb/provider-route-policy.js +118 -0
- package/dist/core/codex-lb/request-route-resolver.js +172 -0
- package/dist/core/codex-lb/route-index.js +148 -0
- package/dist/core/codex-lb/trusted-deep-evidence.js +158 -286
- package/dist/core/commands/basic-cli.js +1 -1
- package/dist/core/commands/check-command.js +3 -10
- package/dist/core/commands/gate-result-contract.js +10 -14
- package/dist/core/commands/intent-normalization/normalizer.js +1 -1
- package/dist/core/commands/mad-sks-command.js +2 -6
- package/dist/core/commands/release-command.js +11 -1
- package/dist/core/commands/telegram-command.js +46 -6
- package/dist/core/doctor/doctor-readiness-matrix.js +9 -7
- package/dist/core/doctor/skill-legacy-surface.js +5 -5
- package/dist/core/evidence/v2/evidence-key.js +30 -3
- package/dist/core/feature-fixtures.js +8 -1
- package/dist/core/feature-registry.js +3 -2
- package/dist/core/hooks-runtime/stop-finalization.js +11 -1
- package/dist/core/hooks-runtime/subagent-context.js +15 -1
- package/dist/core/hooks-runtime/subagent-skill-availability-contract.js +1 -1
- package/dist/core/hooks-runtime/tool-output-quarantine.js +1 -1
- package/dist/core/hooks-runtime.js +73 -87
- package/dist/core/image-ux-review/imagegen-adapter.js +108 -94
- package/dist/core/image-ux-review/real-callout-extractor.js +144 -14
- package/dist/core/imagegen/desktop-bridge-imagegen-target.js +77 -0
- package/dist/core/imagegen/imagegen-capability.js +43 -151
- package/dist/core/init/skills/filesystem.js +89 -0
- package/dist/core/init/skills/inventory.js +97 -0
- package/dist/core/init/skills/manifest.js +151 -0
- package/dist/core/init/skills.js +5 -325
- package/dist/core/init.js +0 -63
- package/dist/core/install/installed-package-smoke.js +1 -1
- package/dist/core/managed-assets/managed-assets-manifest.js +0 -1
- package/dist/core/policy/latest-version-guidance.js +15 -2
- package/dist/core/preflight/parallel-preflight-engine.js +27 -51
- package/dist/core/provider/provider-badge.js +9 -4
- package/dist/core/provider/provider-context.js +57 -107
- package/dist/core/providers/openrouter/openrouter-account.js +3 -1
- package/dist/core/providers/openrouter/openrouter-secret-store.js +91 -22
- package/dist/core/release/desktop-bridge-release-evidence.js +457 -0
- package/dist/core/release/gate-manifest.js +0 -1
- package/dist/core/release/main-push-guard.js +13 -0
- package/dist/core/release/main-push-receipt.js +11 -0
- package/dist/core/release/physical-release-gates.js +664 -61
- package/dist/core/release/publish-preflight.js +3 -3
- package/dist/core/release/release-gate-batch-runner.js +19 -6
- package/dist/core/release/release-gate-dag.js +33 -6
- package/dist/core/release/release-gate-node.js +1 -3
- package/dist/core/release/release-gate-output-contract.js +55 -0
- package/dist/core/release/release-pack-contract.js +1 -1
- package/dist/core/release/release-pack-receipt.js +209 -8
- package/dist/core/release/release-real-contract.js +1 -1
- package/dist/core/release/stage-publish.js +106 -28
- package/dist/core/retention/temp-lease.js +204 -0
- package/dist/core/retention.js +18 -195
- package/dist/core/routes.js +1 -2
- package/dist/core/safety/intent-contract/intent-contract.js +3 -3
- package/dist/core/subagents/naruto-host-credentials.js +6 -1
- package/dist/core/subagents/official-subagent-config.js +27 -9
- package/dist/core/subagents/official-subagent-runner.js +14 -31
- package/dist/core/subagents/wave-lifecycle.js +36 -8
- package/dist/core/subagents/wave-parent-guidance.js +2 -1
- package/dist/core/telegram/doctor.js +2 -2
- package/dist/core/telegram/liveness.js +10 -4
- package/dist/core/triwiki/context-graph/compiler/index.js +18 -18
- package/dist/core/update/update-migration-state/fast-mode-config.js +172 -0
- package/dist/core/update/update-migration-state/session-state-split.js +247 -0
- package/dist/core/update/update-migration-state/simple-stages.js +46 -0
- package/dist/core/update/update-migration-state.js +4 -481
- package/dist/core/version.js +1 -1
- package/dist/native/sks-menubar/Sources/OperationCoordinator.swift +28 -4
- package/dist/native/sks-menubar/Sources/OperationModels.swift +112 -2
- package/dist/native/sks-menubar/Sources/ProviderRouteExplanation.swift +21 -0
- package/dist/native/sks-menubar/Sources/ProvidersBridgeCatalog.swift +140 -0
- package/dist/native/sks-menubar/Sources/ProvidersOpenRouter.swift +107 -470
- package/dist/native/sks-menubar/Sources/ProvidersReliability.swift +27 -197
- package/dist/native/sks-menubar/Sources/ProvidersRoutingTruth.swift +335 -269
- package/dist/native/sks-menubar/Sources/ProvidersViewController.swift +233 -437
- package/dist/native/sks-menubar/Sources/RemoteCodingViewController.swift +16 -7
- package/dist/native/sks-menubar/Sources/SecureProcessEnvelope.swift +39 -9
- package/dist/native/sks-menubar/Sources/TelegramSupport.swift +56 -0
- package/dist/scripts/canonical-test-runner.js +7 -1
- package/dist/scripts/check-runtime-schemas.js +7 -2
- package/dist/scripts/codex-sdk-real-smoke-check.js +3 -1
- package/dist/scripts/desktop-bridge-capabilities-check.js +123 -0
- package/dist/scripts/{codex-lb-desktop-bridge-latency-check.js → desktop-bridge-latency-check.js} +61 -16
- package/dist/scripts/desktop-bridge-real-evidence-check.js +64 -0
- package/dist/scripts/{codex-lb-desktop-bridge-check.js → desktop-bridge-transport-check.js} +92 -155
- package/dist/scripts/desktop-bridge-transport-client.js +116 -0
- package/dist/scripts/desktop-bridge-unification-check.js +276 -0
- package/dist/scripts/doctor-fixes-codex-app-fast-ui-check.js +12 -8
- package/dist/scripts/gate-policy-audit-check.js +52 -2
- package/dist/scripts/provider-badge-context-check.js +55 -12
- package/dist/scripts/publish-preflight.js +1 -2
- package/dist/scripts/release-affected-selector-check.js +1 -2
- package/dist/scripts/release-aggressive-resource-governor-check.js +1 -1
- package/dist/scripts/release-cache-glob-hashing-check.js +1 -0
- package/dist/scripts/release-cache-version-neutral-fixture-check.js +1 -0
- package/dist/scripts/release-full-parallelism-blackbox.js +1 -1
- package/dist/scripts/release-gate-batch-runner-check.js +9 -0
- package/dist/scripts/release-gate-dag-runner-check.js +6 -0
- package/dist/scripts/release-gate-dag-runner.js +4 -2
- package/dist/scripts/release-metadata-check.js +16 -3
- package/dist/scripts/release-pack-receipt.js +29 -6
- package/dist/scripts/release-physical-gates-check.js +39 -0
- package/dist/scripts/release-real-check.js +7 -5
- package/package.json +6 -8
- package/release-gates.v2.json +156 -157
- package/runtime-required-scripts.json +11 -11
- package/schemas/codex/desktop-bridge-status-v3.schema.json +286 -0
- package/schemas/codex/desktop-capabilities-v3.schema.json +198 -0
- package/schemas/release/release-gate-node.schema.json +2 -3
- package/dist/cli/codex-lb-setup-warning-output.js +0 -17
- package/dist/cli/install-helpers-codex-lb-chain.js +0 -529
- package/dist/commands/codex-lb.js +0 -1232
- package/dist/core/architecture-hardening/migration/migration.js +0 -111
- package/dist/core/codex-app/auxiliary-oauth-policy.js +0 -36
- package/dist/core/codex-app/catalog-compat/catalog-service.js +0 -104
- package/dist/core/codex-app/child-policy/child-policy.js +0 -35
- package/dist/core/codex-app/desktop-routing-snapshot.js +0 -233
- package/dist/core/codex-app/glm-model-profile.js +0 -1
- package/dist/core/codex-app/glm-profile-installer.js +0 -191
- package/dist/core/codex-app/glm-profile-schema.js +0 -42
- package/dist/core/codex-app/multi-provider-router.js +0 -391
- package/dist/core/codex-app/openrouter-activate.js +0 -405
- package/dist/core/codex-app/openrouter-model-catalog.js +0 -217
- package/dist/core/codex-app/provider-child-policy.js +0 -64
- package/dist/core/codex-app/provider-mode.js +0 -91
- package/dist/core/codex-app/provider-session-policy.js +0 -85
- package/dist/core/codex-app/session-policy/session-pinning.js +0 -68
- package/dist/core/codex-control/codex-lb-launch-recovery.js +0 -319
- package/dist/core/codex-lb/codex-lb-setup.js +0 -293
- package/dist/core/codex-lb/codex-lb-tool-catalog.js +0 -456
- package/dist/core/codex-lb/codex-lb-tool-output-recovery.js +0 -290
- package/dist/core/codex-lb/desktop-center-credentials.js +0 -1083
- package/dist/core/codex-lb/desktop-mode.js +0 -32
- package/dist/core/codex-lb/desktop-real-evidence.js +0 -168
- package/dist/core/codex-lb/legacy-migration.js +0 -576
- package/dist/core/codex-lb/native-openai-transport/transport.js +0 -68
- package/dist/core/codex-lb/provider-routing/provider-router.js +0 -58
- package/dist/core/codex-lb/routing-truth.js +0 -327
- package/dist/core/imagegen/codex-lb-imagegen-target.js +0 -92
- package/dist/native/sks-menubar/Sources/ProvidersConnectTest.swift +0 -118
- package/dist/native/sks-menubar/Sources/ProvidersFastMode.swift +0 -17
- package/dist/native/sks-menubar/Sources/ProvidersMultiProvider.swift +0 -331
- package/dist/native/sks-menubar/Sources/ProvidersRoleModels.swift +0 -260
- package/dist/scripts/codex-lb-catalog-passthrough-check.js +0 -120
- package/dist/scripts/codex-lb-desktop-auth-invariant-check.js +0 -169
- package/dist/scripts/codex-lb-desktop-capabilities-check.js +0 -287
- package/dist/scripts/codex-lb-desktop-real-evidence-check.js +0 -123
- package/dist/scripts/codex-lb-fast-mode-truth-check.js +0 -114
- package/dist/scripts/codex-lb-fast-ui-preservation-check.js +0 -152
- package/dist/scripts/codex-lb-gpt56-fast-profile-check.js +0 -231
|
@@ -2,7 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { spawnSync } from 'node:child_process';
|
|
4
4
|
import { normalizeReleaseOrigin, RELEASE_ORIGIN_IDENTITY } from './release-origin.js';
|
|
5
|
-
import {
|
|
5
|
+
import { validatePhysicalReleaseGateInspection } from './physical-release-gates.js';
|
|
6
6
|
export function inspectPublishPreflight(options) {
|
|
7
7
|
const root = path.resolve(options.root);
|
|
8
8
|
const run = options.run || runCommand;
|
|
@@ -54,10 +54,10 @@ export function inspectPublishPreflight(options) {
|
|
|
54
54
|
blockers.push(`remote_release_tag_not_head:${releaseTag}`);
|
|
55
55
|
}
|
|
56
56
|
const physicalReleaseGates = requirePhysicalReleaseGates
|
|
57
|
-
?
|
|
57
|
+
? validatePhysicalReleaseGateInspection({
|
|
58
58
|
root,
|
|
59
59
|
version,
|
|
60
|
-
sourceCommit: validSha(head) ? head :
|
|
60
|
+
sourceCommit: validSha(head) ? head : '',
|
|
61
61
|
})
|
|
62
62
|
: null;
|
|
63
63
|
if (physicalReleaseGates && !physicalReleaseGates.ok) {
|
|
@@ -5,6 +5,7 @@ import { cleanupReleaseGateHermeticEnv, createReleaseGateHermeticEnv } from './r
|
|
|
5
5
|
import { writeReleaseGateJson } from './release-gate-report.js';
|
|
6
6
|
import { guardedProcessKill, guardContextForRoute } from '../safety/mutation-guard.js';
|
|
7
7
|
import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
|
|
8
|
+
import { normalizeReleaseGateOutput } from './release-gate-output-contract.js';
|
|
8
9
|
const DISALLOWED_BATCH_RESOURCES = new Set(['git-worktree', 'local-llm-real', 'remote-model-real', 'publish', 'global-config', 'timing-sensitive']);
|
|
9
10
|
export function isReleaseGateBatchable(gate) {
|
|
10
11
|
if (gate.side_effect !== 'hermetic')
|
|
@@ -21,7 +22,7 @@ export async function runReleaseGateBatch(root, gates, input = {}) {
|
|
|
21
22
|
const nonBatchable = gates.filter((gate) => !isReleaseGateBatchable(gate));
|
|
22
23
|
if (nonBatchable.length) {
|
|
23
24
|
return {
|
|
24
|
-
schema: 'sks.release-gate-batch-result.
|
|
25
|
+
schema: 'sks.release-gate-batch-result.v2',
|
|
25
26
|
ok: false,
|
|
26
27
|
batch_size: gates.length,
|
|
27
28
|
completed: 0,
|
|
@@ -45,7 +46,7 @@ export async function runReleaseGateBatch(root, gates, input = {}) {
|
|
|
45
46
|
await Promise.all(workers);
|
|
46
47
|
const failed = results.filter((row) => !row.ok).length;
|
|
47
48
|
return {
|
|
48
|
-
schema: 'sks.release-gate-batch-result.
|
|
49
|
+
schema: 'sks.release-gate-batch-result.v2',
|
|
49
50
|
ok: failed === 0,
|
|
50
51
|
batch_size: gates.length,
|
|
51
52
|
completed: results.length - failed,
|
|
@@ -57,7 +58,8 @@ function runOne(root, runId, reportRoot, gate) {
|
|
|
57
58
|
const started = performance.now();
|
|
58
59
|
const hermetic = createReleaseGateHermeticEnv({ root, runId, gate, reportRoot });
|
|
59
60
|
return new Promise((resolve) => {
|
|
60
|
-
const child = spawn(gate.command, { cwd: root, shell: true, env: hermetic.env, stdio: ['ignore', '
|
|
61
|
+
const child = spawn(gate.command, { cwd: root, shell: true, env: hermetic.env, stdio: ['ignore', 'pipe', 'pipe'], detached: process.platform !== 'win32' });
|
|
62
|
+
let stdoutTail = '';
|
|
61
63
|
let timedOut = false;
|
|
62
64
|
let timeoutCleanup = null;
|
|
63
65
|
const timer = setTimeout(() => {
|
|
@@ -65,20 +67,27 @@ function runOne(root, runId, reportRoot, gate) {
|
|
|
65
67
|
timeoutCleanup = cleanupTimedOutGateProcessTree(root, child);
|
|
66
68
|
}, gate.timeout_ms);
|
|
67
69
|
timer.unref?.();
|
|
70
|
+
child.stdout.on('data', (chunk) => {
|
|
71
|
+
stdoutTail = appendTail(stdoutTail, String(chunk));
|
|
72
|
+
});
|
|
73
|
+
child.stderr.resume();
|
|
68
74
|
child.on('close', (code, signal) => {
|
|
69
75
|
void (async () => {
|
|
70
76
|
clearTimeout(timer);
|
|
71
77
|
if (timeoutCleanup)
|
|
72
78
|
await timeoutCleanup;
|
|
73
79
|
const exitCode = timedOut ? 124 : code;
|
|
80
|
+
const normalized = normalizeReleaseGateOutput({ gateId: gate.id, status: exitCode, signal, timedOut, stdout: stdoutTail });
|
|
74
81
|
const result = {
|
|
75
82
|
id: gate.id,
|
|
76
|
-
ok:
|
|
83
|
+
ok: normalized.evaluation.ok,
|
|
77
84
|
exit_code: exitCode,
|
|
78
85
|
signal,
|
|
79
86
|
timed_out: timedOut,
|
|
80
87
|
duration_ms: Math.max(1, Math.round(performance.now() - started)),
|
|
81
|
-
report_dir: hermetic.report_dir
|
|
88
|
+
report_dir: hermetic.report_dir,
|
|
89
|
+
output_contract: gate.output_contract,
|
|
90
|
+
gate_result: normalized.gate_result
|
|
82
91
|
};
|
|
83
92
|
cleanupReleaseGateHermeticEnv(hermetic);
|
|
84
93
|
resolve(result);
|
|
@@ -86,6 +95,10 @@ function runOne(root, runId, reportRoot, gate) {
|
|
|
86
95
|
});
|
|
87
96
|
});
|
|
88
97
|
}
|
|
98
|
+
function appendTail(previous, next, limit = 64 * 1024) {
|
|
99
|
+
const combined = `${previous}${next}`;
|
|
100
|
+
return combined.length > limit ? combined.slice(-limit) : combined;
|
|
101
|
+
}
|
|
89
102
|
async function cleanupTimedOutGateProcessTree(root, child) {
|
|
90
103
|
await killGateProcessTree(root, child, 'SIGTERM');
|
|
91
104
|
await sleep(1500);
|
|
@@ -118,7 +131,7 @@ function sleep(ms) {
|
|
|
118
131
|
function writeChildResult(reportRoot, result) {
|
|
119
132
|
const dir = result.report_dir || path.join(reportRoot, result.id.replace(/[^A-Za-z0-9_.:-]/g, '_'));
|
|
120
133
|
writeReleaseGateJson(path.join(dir, 'result.json'), {
|
|
121
|
-
schema: 'sks.release-gate-batch-child-result.
|
|
134
|
+
schema: 'sks.release-gate-batch-child-result.v2',
|
|
122
135
|
...result
|
|
123
136
|
});
|
|
124
137
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { spawn } from 'node:child_process';
|
|
4
|
+
import { finished } from 'node:stream/promises';
|
|
4
5
|
import { cleanupReleaseGateHermeticEnv, cleanupReleaseGateRunTemp, createReleaseGateHermeticEnv } from './release-gate-hermetic-env.js';
|
|
5
6
|
import { appendReleaseGateJsonl, writeReleaseGateJson } from './release-gate-report.js';
|
|
6
7
|
import { findReadyReleaseGateNodes, findReleaseGatesBlockedByFailedDeps, pickReadyLaunchableReleaseGates } from './release-gate-scheduler.js';
|
|
@@ -15,6 +16,7 @@ import { guardedProcessKill, guardContextForRoute } from '../safety/mutation-gua
|
|
|
15
16
|
import { createRequestedScopeContract } from '../safety/requested-scope-contract.js';
|
|
16
17
|
import { rmrf } from '../fsx.js';
|
|
17
18
|
import { sweepSksTempDirs } from '../retention.js';
|
|
19
|
+
import { normalizeReleaseGateOutput } from './release-gate-output-contract.js';
|
|
18
20
|
export function loadReleaseGateManifest(root, file = 'release-gates.v2.json') {
|
|
19
21
|
const manifestPath = path.join(root, file);
|
|
20
22
|
const parsed = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
@@ -398,6 +400,7 @@ function runGate(root, runId, reportRoot, gate) {
|
|
|
398
400
|
const err = fs.createWriteStream(stderrFile);
|
|
399
401
|
return new Promise((resolve) => {
|
|
400
402
|
const child = spawn(gate.command, { cwd: root, shell: true, env: hermetic.env, stdio: ['ignore', 'pipe', 'pipe'], detached: process.platform !== 'win32' });
|
|
403
|
+
let stdoutTail = '';
|
|
401
404
|
let timedOut = false;
|
|
402
405
|
let timeoutCleanup = null;
|
|
403
406
|
const timer = setTimeout(() => {
|
|
@@ -405,29 +408,53 @@ function runGate(root, runId, reportRoot, gate) {
|
|
|
405
408
|
timeoutCleanup = cleanupTimedOutGateProcessTree(root, child);
|
|
406
409
|
}, gate.timeout_ms);
|
|
407
410
|
timer.unref?.();
|
|
408
|
-
child.stdout.
|
|
409
|
-
|
|
411
|
+
child.stdout.on('data', (chunk) => {
|
|
412
|
+
stdoutTail = appendTail(stdoutTail, String(chunk));
|
|
413
|
+
});
|
|
414
|
+
child.stdout.pipe(out, { end: false });
|
|
415
|
+
child.stderr.pipe(err, { end: false });
|
|
410
416
|
child.on('close', (code, signal) => {
|
|
411
417
|
void (async () => {
|
|
412
418
|
clearTimeout(timer);
|
|
413
419
|
if (timeoutCleanup)
|
|
414
420
|
await timeoutCleanup;
|
|
421
|
+
const exitCode = timedOut ? 124 : code;
|
|
422
|
+
const normalized = normalizeReleaseGateOutput({
|
|
423
|
+
gateId: gate.id,
|
|
424
|
+
status: exitCode,
|
|
425
|
+
signal,
|
|
426
|
+
timedOut,
|
|
427
|
+
stdout: stdoutTail
|
|
428
|
+
});
|
|
429
|
+
out.write(`${stdoutTail.endsWith('\n') || stdoutTail.length === 0 ? '' : '\n'}${normalized.line}\n`);
|
|
415
430
|
out.end();
|
|
416
431
|
err.end();
|
|
432
|
+
await Promise.allSettled([finished(out), finished(err)]);
|
|
417
433
|
const durationMs = Date.now() - started;
|
|
418
434
|
const stderrText = fs.existsSync(stderrFile) ? fs.readFileSync(stderrFile, 'utf8') : '';
|
|
419
435
|
const timeoutTail = timedOut ? `release_gate_timeout:${gate.id}:${gate.timeout_ms}ms` : '';
|
|
420
436
|
const signalTail = !timedOut && signal ? `release_gate_signal:${gate.id}:${signal}` : '';
|
|
421
|
-
const
|
|
422
|
-
const
|
|
423
|
-
const result = { id: gate.id, ok:
|
|
424
|
-
writeReleaseGateJson(path.join(hermetic.report_dir, 'result.json'), {
|
|
437
|
+
const contractTail = normalized.evaluation.ok ? '' : normalized.gate_result.blockers.map(String).join('\n');
|
|
438
|
+
const stderrTail = tail([stderrText, timeoutTail, signalTail, contractTail].filter(Boolean).join('\n'));
|
|
439
|
+
const result = { id: gate.id, ok: normalized.evaluation.ok, exit_code: exitCode, signal, timed_out: timedOut, duration_ms: durationMs, cached: false, stderr_tail: stderrTail };
|
|
440
|
+
writeReleaseGateJson(path.join(hermetic.report_dir, 'result.json'), {
|
|
441
|
+
schema: 'sks.release-gate-result.v2',
|
|
442
|
+
...result,
|
|
443
|
+
output_contract: gate.output_contract,
|
|
444
|
+
gate_result: normalized.gate_result,
|
|
445
|
+
stdout_log: stdoutFile,
|
|
446
|
+
stderr_log: stderrFile
|
|
447
|
+
});
|
|
425
448
|
cleanupReleaseGateHermeticEnv(hermetic);
|
|
426
449
|
resolve(result);
|
|
427
450
|
})();
|
|
428
451
|
});
|
|
429
452
|
});
|
|
430
453
|
}
|
|
454
|
+
function appendTail(previous, next, limit = 64 * 1024) {
|
|
455
|
+
const combined = `${previous}${next}`;
|
|
456
|
+
return combined.length > limit ? combined.slice(-limit) : combined;
|
|
457
|
+
}
|
|
431
458
|
async function cleanupTimedOutGateProcessTree(root, child) {
|
|
432
459
|
await killGateProcessTree(root, child, 'SIGTERM');
|
|
433
460
|
await sleep(1500);
|
|
@@ -47,10 +47,8 @@ export function validateReleaseGateManifest(input) {
|
|
|
47
47
|
errors.push(`gate_side_effect_invalid:${gate?.id || 'unknown'}`);
|
|
48
48
|
if (!Number.isFinite(Number(gate?.timeout_ms)) || Number(gate.timeout_ms) <= 0)
|
|
49
49
|
errors.push(`gate_timeout_missing:${gate?.id || 'unknown'}`);
|
|
50
|
-
if (gate?.output_contract !==
|
|
50
|
+
if (gate?.output_contract !== 'sks.gate-result.v2')
|
|
51
51
|
errors.push(`gate_output_contract_invalid:${gate?.id || 'unknown'}`);
|
|
52
|
-
if (gate?.contract_note !== undefined && typeof gate.contract_note !== 'string')
|
|
53
|
-
errors.push(`gate_contract_note_invalid:${gate?.id || 'unknown'}`);
|
|
54
52
|
if (!gate?.cache || typeof gate.cache.enabled !== 'boolean' || !Array.isArray(gate.cache.inputs))
|
|
55
53
|
errors.push(`gate_cache_missing:${gate?.id || 'unknown'}`);
|
|
56
54
|
if (!gate?.isolation || gate.isolation.report_dir !== 'per-gate')
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { GATE_RESULT_CONTRACT, GATE_RESULT_CONTRACT_MODE, evaluateGateProcessOutput, parseGateResultFromStdout } from '../commands/gate-result-contract.js';
|
|
2
|
+
export function normalizeReleaseGateOutput(input) {
|
|
3
|
+
const childResult = parseGateResultFromStdout(input.stdout);
|
|
4
|
+
const processBlockers = [
|
|
5
|
+
...(input.timedOut ? [`release_gate_timeout:${input.gateId}`] : []),
|
|
6
|
+
...(!input.timedOut && input.signal ? [`release_gate_signal:${input.gateId}:${input.signal}`] : []),
|
|
7
|
+
...(!input.timedOut && !input.signal && input.status !== 0 ? [`release_gate_exit:${input.gateId}:${String(input.status)}`] : [])
|
|
8
|
+
];
|
|
9
|
+
const processOk = !input.timedOut && !input.signal && input.status === 0;
|
|
10
|
+
const gateResult = {
|
|
11
|
+
...(childResult || {}),
|
|
12
|
+
schema: GATE_RESULT_CONTRACT,
|
|
13
|
+
contract_mode: GATE_RESULT_CONTRACT_MODE,
|
|
14
|
+
ok: processOk && (childResult?.ok ?? true),
|
|
15
|
+
blockers: dedupe([
|
|
16
|
+
...(childResult?.blockers || []),
|
|
17
|
+
...processBlockers
|
|
18
|
+
]),
|
|
19
|
+
producer: 'release-gate-command-adapter',
|
|
20
|
+
execution: {
|
|
21
|
+
gate_id: input.gateId,
|
|
22
|
+
child_contract: childResult?.schema || null,
|
|
23
|
+
child_exit_code: input.status,
|
|
24
|
+
child_signal: input.signal,
|
|
25
|
+
timed_out: input.timedOut
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const line = JSON.stringify(gateResult);
|
|
29
|
+
return {
|
|
30
|
+
gate_result: gateResult,
|
|
31
|
+
evaluation: evaluateGateProcessOutput({ status: input.status, stdout: line }),
|
|
32
|
+
line
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
function dedupe(values) {
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const result = [];
|
|
38
|
+
for (const value of values) {
|
|
39
|
+
const key = typeof value === 'string' ? `string:${value}` : `json:${safeJson(value)}`;
|
|
40
|
+
if (seen.has(key))
|
|
41
|
+
continue;
|
|
42
|
+
seen.add(key);
|
|
43
|
+
result.push(value);
|
|
44
|
+
}
|
|
45
|
+
return result;
|
|
46
|
+
}
|
|
47
|
+
function safeJson(value) {
|
|
48
|
+
try {
|
|
49
|
+
const encoded = JSON.stringify(value);
|
|
50
|
+
return encoded === undefined ? String(value) : encoded;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return String(value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export const RELEASE_PACK_RECEIPT_SCHEMA = 'sks.release-pack-receipt.
|
|
1
|
+
export const RELEASE_PACK_RECEIPT_SCHEMA = 'sks.release-pack-receipt.v2';
|
|
2
2
|
export const RELEASE_PACK_COMPARE_SCHEMA = 'sks.release-pack-compare.v1';
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import fs from 'node:fs';
|
|
3
|
+
import os from 'node:os';
|
|
3
4
|
import path from 'node:path';
|
|
4
5
|
import { spawnSync } from 'node:child_process';
|
|
6
|
+
import { tmpdir } from '../fsx.js';
|
|
5
7
|
import { DEFAULT_MAX_PACK_BYTES, DEFAULT_MAX_UNPACKED_BYTES } from './package-size-budget.js';
|
|
6
8
|
import { readCurrentNpmPackGateArtifacts } from './npm-pack-proof.js';
|
|
7
9
|
import { scanTarballTextContents } from './release-pack-content-scanner.js';
|
|
@@ -42,6 +44,11 @@ export function inspectReleaseTarball(input) {
|
|
|
42
44
|
blockers.push('tarball_unpacked_size_unavailable');
|
|
43
45
|
const sha256 = bytes.length ? hash(bytes, 'sha256', 'hex') : '';
|
|
44
46
|
const sha512Base64 = bytes.length ? hash(bytes, 'sha512', 'base64') : '';
|
|
47
|
+
const sourceBinding = input.kind === 'local' && input.root && input.sourceCommit && bytes.length && inventory.blockers.length === 0
|
|
48
|
+
? inspectSourcePackageBinding(input.root, input.sourceCommit, tarball, files, sha256)
|
|
49
|
+
: null;
|
|
50
|
+
if (sourceBinding)
|
|
51
|
+
blockers.push(...sourceBinding.blockers);
|
|
45
52
|
const budgetBlockers = [
|
|
46
53
|
...(bytes.length > DEFAULT_MAX_PACK_BYTES ? ['packed_bytes_over_limit'] : []),
|
|
47
54
|
...(unpackedBytes > DEFAULT_MAX_UNPACKED_BYTES ? ['unpacked_bytes_over_limit'] : []),
|
|
@@ -55,6 +62,9 @@ export function inspectReleaseTarball(input) {
|
|
|
55
62
|
package_name: String(packageJson?.name || ''),
|
|
56
63
|
package_version: String(packageJson?.version || ''),
|
|
57
64
|
source_commit: input.sourceCommit || null,
|
|
65
|
+
source_tree_sha256: sourceBinding?.source_tree_sha256 || null,
|
|
66
|
+
source_package_sha256: sourceBinding?.source_package_sha256 || null,
|
|
67
|
+
source_package_binding_sha256: sourceBinding?.source_package_binding_sha256 || null,
|
|
58
68
|
tarball_name: path.basename(tarball),
|
|
59
69
|
tarball_path: input.root ? normalizePath(path.relative(input.root, tarball)) : normalizePath(tarball),
|
|
60
70
|
bytes: bytes.length,
|
|
@@ -115,7 +125,10 @@ export function compareReleasePacks(local, staged) {
|
|
|
115
125
|
export function validateReleasePackReceipt(value, expectedKind, options = {}) {
|
|
116
126
|
const receipt = value;
|
|
117
127
|
const blockers = [];
|
|
118
|
-
if (
|
|
128
|
+
if (receipt?.schema === 'sks.release-pack-receipt.v1') {
|
|
129
|
+
blockers.push('receipt_schema_v1_outdated_regenerate_with_npm_run_release:pack-receipt');
|
|
130
|
+
}
|
|
131
|
+
else if (!receipt || receipt.schema !== RELEASE_PACK_RECEIPT_SCHEMA)
|
|
119
132
|
blockers.push('schema_invalid');
|
|
120
133
|
if (receipt?.ok !== true)
|
|
121
134
|
blockers.push('not_ok');
|
|
@@ -192,8 +205,19 @@ export function validateReleasePackReceipt(value, expectedKind, options = {}) {
|
|
|
192
205
|
if (!/^[a-f0-9]{64}$/i.test(String(receipt?.npm_pack_proof?.file_list_sha256 || '')))
|
|
193
206
|
blockers.push('npm_pack_file_list_sha256_invalid');
|
|
194
207
|
}
|
|
195
|
-
if (receipt?.kind === 'local'
|
|
196
|
-
|
|
208
|
+
if (receipt?.kind === 'local') {
|
|
209
|
+
if (!/^[a-f0-9]{40}$/i.test(String(receipt?.source_commit || '')))
|
|
210
|
+
blockers.push('source_commit_invalid');
|
|
211
|
+
if (!/^[a-f0-9]{64}$/i.test(String(receipt?.source_tree_sha256 || '')))
|
|
212
|
+
blockers.push('source_tree_sha256_invalid');
|
|
213
|
+
if (!/^[a-f0-9]{64}$/i.test(String(receipt?.source_package_sha256 || '')))
|
|
214
|
+
blockers.push('source_package_sha256_invalid');
|
|
215
|
+
if (!/^[a-f0-9]{64}$/i.test(String(receipt?.source_package_binding_sha256 || '')))
|
|
216
|
+
blockers.push('source_package_binding_sha256_invalid');
|
|
217
|
+
const expectedBinding = sourcePackageBindingSha256(String(receipt?.source_commit || ''), String(receipt?.source_tree_sha256 || ''), String(receipt?.source_package_sha256 || ''), String(receipt?.sha256 || ''));
|
|
218
|
+
if (receipt?.source_package_binding_sha256 !== expectedBinding)
|
|
219
|
+
blockers.push('source_package_binding_sha256_mismatch');
|
|
220
|
+
}
|
|
197
221
|
return { ok: blockers.length === 0, receipt: receipt || null, blockers: unique(blockers) };
|
|
198
222
|
}
|
|
199
223
|
export function validateLocalReleasePackBinding(root, value) {
|
|
@@ -224,9 +248,12 @@ export function validateLocalReleasePackBinding(root, value) {
|
|
|
224
248
|
if (receipt?.sha512_integrity !== info.integrity)
|
|
225
249
|
blockers.push('npm_pack_integrity_mismatch');
|
|
226
250
|
}
|
|
227
|
-
const
|
|
228
|
-
if (!head || receipt?.source_commit !== head)
|
|
251
|
+
const sourceState = inspectLocalReleaseSourceState(root);
|
|
252
|
+
if (!sourceState.head || receipt?.source_commit !== sourceState.head)
|
|
229
253
|
blockers.push('npm_pack_source_commit_mismatch');
|
|
254
|
+
if (!sourceState.source_tree_sha256 || receipt?.source_tree_sha256 !== sourceState.source_tree_sha256)
|
|
255
|
+
blockers.push('npm_pack_source_tree_sha256_mismatch');
|
|
256
|
+
blockers.push(...sourceState.blockers);
|
|
230
257
|
const tarball = receipt?.tarball_path ? path.resolve(root, receipt.tarball_path) : '';
|
|
231
258
|
const managedRoot = path.resolve(root, '.sneakoscope', 'reports', 'release', String(receipt?.package_version || ''), 'artifacts');
|
|
232
259
|
const relative = tarball ? path.relative(managedRoot, tarball) : '..';
|
|
@@ -234,7 +261,7 @@ export function validateLocalReleasePackBinding(root, value) {
|
|
|
234
261
|
blockers.push('local_tarball_artifact_missing_or_unsafe');
|
|
235
262
|
else {
|
|
236
263
|
const actual = inspectReleaseTarball({ tarball, kind: 'local', sourceCommit: receipt?.source_commit || null, root });
|
|
237
|
-
for (const key of ['package_name', 'package_version', 'tarball_name', 'tarball_path', 'bytes', 'unpacked_bytes', 'sha256', 'sha512_integrity', 'file_count', 'file_list_sha256']) {
|
|
264
|
+
for (const key of ['package_name', 'package_version', 'source_tree_sha256', 'source_package_sha256', 'source_package_binding_sha256', 'tarball_name', 'tarball_path', 'bytes', 'unpacked_bytes', 'sha256', 'sha512_integrity', 'file_count', 'file_list_sha256']) {
|
|
238
265
|
if (actual[key] !== receipt?.[key])
|
|
239
266
|
blockers.push(`local_tarball_artifact_mismatch:${key}`);
|
|
240
267
|
}
|
|
@@ -245,7 +272,72 @@ export function validateLocalReleasePackBinding(root, value) {
|
|
|
245
272
|
if (!actual.ok)
|
|
246
273
|
blockers.push(...actual.blockers.map((blocker) => `local_tarball_artifact:${blocker}`));
|
|
247
274
|
}
|
|
248
|
-
return { ok: blockers.length === 0, receipt, gate, blockers: unique(blockers) };
|
|
275
|
+
return { ok: blockers.length === 0, receipt, gate, sourceState, blockers: unique(blockers) };
|
|
276
|
+
}
|
|
277
|
+
export function inspectLocalReleaseSourceState(root) {
|
|
278
|
+
const blockers = [];
|
|
279
|
+
const head = gitHead(root);
|
|
280
|
+
if (!head)
|
|
281
|
+
blockers.push('npm_pack_source_commit_unavailable');
|
|
282
|
+
const sourceTreeSha256 = head ? gitTreeSha256(root, head) : '';
|
|
283
|
+
if (!sourceTreeSha256)
|
|
284
|
+
blockers.push('npm_pack_source_tree_digest_unavailable');
|
|
285
|
+
const trackedStatus = spawnSync('git', ['status', '--porcelain=v1', '--untracked-files=no'], { cwd: root, encoding: 'utf8' });
|
|
286
|
+
const trackedChanges = trackedStatus.status === 0 && Boolean(String(trackedStatus.stdout || '').trim());
|
|
287
|
+
if (trackedStatus.status !== 0)
|
|
288
|
+
blockers.push('npm_pack_tracked_status_unavailable');
|
|
289
|
+
else if (trackedChanges)
|
|
290
|
+
blockers.push('npm_pack_tracked_changes_present');
|
|
291
|
+
const fullStatus = spawnSync('git', ['status', '--porcelain=v1', '--untracked-files=all'], {
|
|
292
|
+
cwd: root,
|
|
293
|
+
encoding: 'utf8',
|
|
294
|
+
maxBuffer: 32 * 1024 * 1024
|
|
295
|
+
});
|
|
296
|
+
if (fullStatus.status !== 0)
|
|
297
|
+
blockers.push('npm_pack_untracked_status_unavailable');
|
|
298
|
+
else if (String(fullStatus.stdout || '').split(/\r?\n/).some((line) => line.startsWith('?? '))) {
|
|
299
|
+
blockers.push('npm_pack_untracked_files_present');
|
|
300
|
+
}
|
|
301
|
+
const trackedFilesResult = spawnSync('git', ['ls-files', '-z'], { cwd: root, encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
302
|
+
const trackedFiles = trackedFilesResult.status === 0
|
|
303
|
+
? new Set(String(trackedFilesResult.stdout || '').split('\0').filter(Boolean).map(normalizePath))
|
|
304
|
+
: null;
|
|
305
|
+
if (!trackedFiles)
|
|
306
|
+
blockers.push('npm_pack_tracked_file_inventory_unavailable');
|
|
307
|
+
const dryRun = spawnSync('npm', ['pack', '--dry-run', '--ignore-scripts', '--json'], {
|
|
308
|
+
cwd: root,
|
|
309
|
+
encoding: 'utf8',
|
|
310
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
311
|
+
env: { ...process.env, npm_config_cache: process.env.SKS_RELEASE_NPM_CACHE || path.join(os.tmpdir(), 'sneakoscope-npm-cache') }
|
|
312
|
+
});
|
|
313
|
+
let packedFiles = null;
|
|
314
|
+
if (dryRun.status === 0) {
|
|
315
|
+
try {
|
|
316
|
+
const parsed = JSON.parse(String(dryRun.stdout || '[]'));
|
|
317
|
+
const info = Array.isArray(parsed) ? parsed[0] : parsed;
|
|
318
|
+
packedFiles = Array.isArray(info?.files)
|
|
319
|
+
? info.files.map((file) => normalizePath(String(file?.path || ''))).filter(Boolean)
|
|
320
|
+
: null;
|
|
321
|
+
}
|
|
322
|
+
catch {
|
|
323
|
+
packedFiles = null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (!packedFiles)
|
|
327
|
+
blockers.push('npm_pack_dry_run_file_inventory_unavailable');
|
|
328
|
+
const packEligibleUntracked = trackedFiles && packedFiles
|
|
329
|
+
? unique(packedFiles.filter((relative) => !trackedFiles.has(relative) && !isGeneratedDistPackagePath(relative))).sort()
|
|
330
|
+
: [];
|
|
331
|
+
if (packEligibleUntracked.length > 0)
|
|
332
|
+
blockers.push('npm_pack_eligible_untracked_files_present');
|
|
333
|
+
return {
|
|
334
|
+
ok: blockers.length === 0,
|
|
335
|
+
head,
|
|
336
|
+
source_tree_sha256: sourceTreeSha256,
|
|
337
|
+
tracked_changes: trackedChanges,
|
|
338
|
+
pack_eligible_untracked: packEligibleUntracked,
|
|
339
|
+
blockers: unique(blockers)
|
|
340
|
+
};
|
|
249
341
|
}
|
|
250
342
|
export function writeReleaseJson(file, value) {
|
|
251
343
|
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
@@ -284,6 +376,7 @@ const SECRET_PATTERNS = [
|
|
|
284
376
|
{ kind: 'npm_token', regex: /\bnpm_[A-Za-z0-9]{30,}\b/g },
|
|
285
377
|
{ kind: 'slack_token', regex: /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/g },
|
|
286
378
|
{ kind: 'openai_token', regex: /\bsk-(?:proj-|svcacct-)?[A-Za-z0-9_-]{20,}\b/g },
|
|
379
|
+
{ kind: 'supabase_secret_key', regex: /\bsb_secret_[A-Za-z0-9_-]{20,}\b/g },
|
|
287
380
|
{ kind: 'google_api_key', regex: /\bAIza[0-9A-Za-z_-]{30,}\b/g },
|
|
288
381
|
{ kind: 'aws_access_key', regex: /\b(?:AKIA|ASIA)[A-Z0-9]{16}\b/g },
|
|
289
382
|
{ kind: 'jwt', regex: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g },
|
|
@@ -323,7 +416,7 @@ const RETIRED_SURFACE_ALLOWLIST = [
|
|
|
323
416
|
{ path: /^dist\/core\/doctor\/skill-legacy-surface\.js$/, kinds: new Set(RETIRED_SURFACE_PATTERNS.map((pattern) => pattern.kind)) },
|
|
324
417
|
{ path: /^dist\/core\/doctor\/retired-managed-projection-residue\.js$/, kinds: new Set(['retired_team_runtime_identity']) },
|
|
325
418
|
{ path: /^dist\/core\/doctor\/retired-managed-residue(?:-artifact-helpers|-artifacts|-missions|-private|-runtime|-state)?\.js$/, kinds: new Set(['retired_cli_command', 'retired_dollar_command', 'retired_ralph_identity', 'retired_team_current_wording', 'retired_team_profile', 'retired_team_runtime_identity', 'retired_team_workdir']) },
|
|
326
|
-
{ path: /^dist\/core\/init\/skills\.js$/, kinds: new Set(['retired_ralph_identity']) },
|
|
419
|
+
{ path: /^dist\/core\/init\/(?:skills\.js|skills\/inventory\.js)$/, kinds: new Set(['retired_ralph_identity']) },
|
|
327
420
|
{ path: /^dist\/core\/install\/installed-package-smoke\.js$/, kinds: new Set(['retired_agent_option', 'retired_clones_option', 'retired_dollar_command', 'retired_menubar_mcp_identity', 'retired_naruto_option', 'retired_naruto_workers_command', 'retired_ralph_identity']) },
|
|
328
421
|
{ path: /^dist\/core\/ops\/upgrade-migration-fixtures\.js$/, kinds: new Set(['retired_dollar_command']) },
|
|
329
422
|
{ path: /^dist\/core\/release\/release-pack-receipt\.js$/, kinds: new Set(RETIRED_SURFACE_PATTERNS.map((pattern) => pattern.kind)) },
|
|
@@ -339,6 +432,103 @@ function retiredSurfaceFindingAllowed(finding) {
|
|
|
339
432
|
function hash(value, algorithm, encoding) {
|
|
340
433
|
return crypto.createHash(algorithm).update(value).digest(encoding);
|
|
341
434
|
}
|
|
435
|
+
function inspectSourcePackageBinding(root, sourceCommit, tarball, files, tarballSha256) {
|
|
436
|
+
const blockers = [];
|
|
437
|
+
const sourceTreeSha256 = gitTreeSha256(root, sourceCommit);
|
|
438
|
+
if (!sourceTreeSha256)
|
|
439
|
+
blockers.push('source_tree_digest_unavailable');
|
|
440
|
+
const temp = tmpdir('release-source-package-binding-');
|
|
441
|
+
const sourceRoot = path.join(temp, 'source');
|
|
442
|
+
const packageRoot = path.join(temp, 'packed');
|
|
443
|
+
const archive = path.join(temp, 'source.tar');
|
|
444
|
+
const packageHash = crypto.createHash('sha256');
|
|
445
|
+
try {
|
|
446
|
+
fs.mkdirSync(sourceRoot);
|
|
447
|
+
fs.mkdirSync(packageRoot);
|
|
448
|
+
const archived = spawnSync('git', ['archive', '--format=tar', `--output=${archive}`, sourceCommit], {
|
|
449
|
+
cwd: root,
|
|
450
|
+
encoding: 'utf8',
|
|
451
|
+
maxBuffer: 32 * 1024 * 1024
|
|
452
|
+
});
|
|
453
|
+
if (archived.status !== 0)
|
|
454
|
+
blockers.push('source_commit_archive_unavailable');
|
|
455
|
+
const extractedSource = archived.status === 0
|
|
456
|
+
? spawnSync('tar', ['-xf', archive, '-C', sourceRoot], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 })
|
|
457
|
+
: null;
|
|
458
|
+
if (!extractedSource || extractedSource.status !== 0)
|
|
459
|
+
blockers.push('source_commit_extract_unavailable');
|
|
460
|
+
const extractedPackage = spawnSync('tar', ['-xzf', tarball, '-C', packageRoot], { encoding: 'utf8', maxBuffer: 32 * 1024 * 1024 });
|
|
461
|
+
if (extractedPackage.status !== 0)
|
|
462
|
+
blockers.push('tarball_source_binding_extract_unavailable');
|
|
463
|
+
if (extractedSource?.status === 0 && extractedPackage.status === 0) {
|
|
464
|
+
for (const entry of [...files].sort()) {
|
|
465
|
+
const relative = normalizePath(entry).replace(/^package\//, '');
|
|
466
|
+
const packedFile = path.join(packageRoot, 'package', relative);
|
|
467
|
+
const sourceFile = path.join(sourceRoot, relative);
|
|
468
|
+
if (!fs.existsSync(packedFile) || !fs.statSync(packedFile).isFile()) {
|
|
469
|
+
blockers.push(`packed_file_unavailable_for_source_binding:${relative}`);
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const packedBytes = fs.readFileSync(packedFile);
|
|
473
|
+
const packedSha256 = hash(packedBytes, 'sha256', 'hex');
|
|
474
|
+
packageHash.update(relative);
|
|
475
|
+
packageHash.update('\0');
|
|
476
|
+
packageHash.update(String(packedBytes.length));
|
|
477
|
+
packageHash.update('\0');
|
|
478
|
+
packageHash.update(packedSha256);
|
|
479
|
+
packageHash.update('\0');
|
|
480
|
+
if (!fs.existsSync(sourceFile) || !fs.statSync(sourceFile).isFile()) {
|
|
481
|
+
if (isGeneratedDistPackagePath(relative)) {
|
|
482
|
+
const builtFile = path.join(root, relative);
|
|
483
|
+
let builtStat = null;
|
|
484
|
+
try {
|
|
485
|
+
builtStat = fs.lstatSync(builtFile);
|
|
486
|
+
}
|
|
487
|
+
catch {
|
|
488
|
+
builtStat = null;
|
|
489
|
+
}
|
|
490
|
+
if (!builtStat?.isFile() || builtStat.isSymbolicLink()) {
|
|
491
|
+
blockers.push(`packed_generated_dist_missing_from_current_build:${relative}`);
|
|
492
|
+
}
|
|
493
|
+
else if (!fs.readFileSync(builtFile).equals(packedBytes)) {
|
|
494
|
+
blockers.push(`packed_generated_dist_differs_from_current_build:${relative}`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
blockers.push(`packed_file_missing_from_source_commit:${relative}`);
|
|
499
|
+
}
|
|
500
|
+
continue;
|
|
501
|
+
}
|
|
502
|
+
const sourceBytes = fs.readFileSync(sourceFile);
|
|
503
|
+
if (!sourceBytes.equals(packedBytes))
|
|
504
|
+
blockers.push(`packed_file_differs_from_source_commit:${relative}`);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
finally {
|
|
509
|
+
fs.rmSync(temp, { recursive: true, force: true });
|
|
510
|
+
}
|
|
511
|
+
const sourcePackageSha256 = packageHash.digest('hex');
|
|
512
|
+
return {
|
|
513
|
+
source_tree_sha256: sourceTreeSha256,
|
|
514
|
+
source_package_sha256: sourcePackageSha256,
|
|
515
|
+
source_package_binding_sha256: sourcePackageBindingSha256(sourceCommit, sourceTreeSha256, sourcePackageSha256, tarballSha256),
|
|
516
|
+
blockers: unique(blockers)
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
function sourcePackageBindingSha256(sourceCommit, sourceTreeSha256, sourcePackageSha256, tarballSha256) {
|
|
520
|
+
return hash(Buffer.from([
|
|
521
|
+
RELEASE_PACK_RECEIPT_SCHEMA,
|
|
522
|
+
sourceCommit,
|
|
523
|
+
sourceTreeSha256,
|
|
524
|
+
sourcePackageSha256,
|
|
525
|
+
tarballSha256
|
|
526
|
+
].join('\n')), 'sha256', 'hex');
|
|
527
|
+
}
|
|
528
|
+
function isGeneratedDistPackagePath(relative) {
|
|
529
|
+
const normalized = normalizePath(relative);
|
|
530
|
+
return normalized.startsWith('dist/') && !normalized.includes('/../');
|
|
531
|
+
}
|
|
342
532
|
function unique(values) {
|
|
343
533
|
return [...new Set(values.filter(Boolean))];
|
|
344
534
|
}
|
|
@@ -346,6 +536,17 @@ function gitHead(root) {
|
|
|
346
536
|
const result = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: root, encoding: 'utf8' });
|
|
347
537
|
return result.status === 0 ? String(result.stdout || '').trim() : '';
|
|
348
538
|
}
|
|
539
|
+
function gitTreeSha256(root, commit) {
|
|
540
|
+
if (!/^[a-f0-9]{40}$/i.test(commit))
|
|
541
|
+
return '';
|
|
542
|
+
const verified = spawnSync('git', ['rev-parse', '--verify', `${commit}^{commit}`], { cwd: root, encoding: 'utf8' });
|
|
543
|
+
if (verified.status !== 0 || String(verified.stdout || '').trim().toLowerCase() !== commit.toLowerCase())
|
|
544
|
+
return '';
|
|
545
|
+
const tree = spawnSync('git', ['ls-tree', '-r', '-z', '--full-tree', commit], { cwd: root, maxBuffer: 32 * 1024 * 1024 });
|
|
546
|
+
if (tree.status !== 0 || !Buffer.isBuffer(tree.stdout))
|
|
547
|
+
return '';
|
|
548
|
+
return hash(tree.stdout, 'sha256', 'hex');
|
|
549
|
+
}
|
|
349
550
|
function normalizePath(value) {
|
|
350
551
|
return value.split(path.sep).join('/');
|
|
351
552
|
}
|
|
@@ -15,7 +15,7 @@ export const RELEASE_REAL_REQUIRED_CHECK_IDS = Object.freeze([
|
|
|
15
15
|
'codex-sdk:real-smoke'
|
|
16
16
|
]);
|
|
17
17
|
export const RELEASE_REAL_OPTIONAL_CHECK_IDS = Object.freeze([
|
|
18
|
-
'
|
|
18
|
+
'desktop-bridge:real-evidence',
|
|
19
19
|
'imagegen:real-smoke',
|
|
20
20
|
'ux-review:real-imagegen-smoke',
|
|
21
21
|
'ppt:real-imagegen-smoke'
|