sneakoscope 4.8.7 → 5.3.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 +51 -726
- package/config/bench-baseline.json +7 -0
- package/crates/sks-core/Cargo.lock +1 -1
- package/crates/sks-core/Cargo.toml +1 -1
- package/crates/sks-core/src/main.rs +1 -1
- package/dist/bin/install.js +35 -0
- package/dist/bin/sks.js +1 -1
- package/dist/cli/cli-theme.js +51 -0
- package/dist/cli/command-registry.js +15 -4
- package/dist/cli/help-fast.js +18 -8
- package/dist/cli/insane-search-command.js +36 -8
- package/dist/cli/install-helpers.js +32 -0
- package/dist/commands/codex-lb.js +2 -0
- package/dist/commands/doctor.js +94 -9
- package/dist/commands/proof.js +6 -3
- package/dist/config/skills-manifest.json +82 -54
- package/dist/core/agents/agent-conflict-graph.js +5 -0
- package/dist/core/agents/agent-lease.js +5 -0
- package/dist/core/agents/agent-ledger-schemas.js +1 -0
- package/dist/core/agents/agent-lifecycle.js +5 -3
- package/dist/core/agents/agent-orchestrator.js +268 -62
- package/dist/core/agents/agent-output-validator.js +14 -3
- package/dist/core/agents/agent-patch-queue-store.js +95 -2
- package/dist/core/agents/agent-patch-queue.js +6 -3
- package/dist/core/agents/agent-patch-schema.js +13 -0
- package/dist/core/agents/agent-proof-evidence.js +6 -1
- package/dist/core/agents/agent-runner-fake.js +40 -0
- package/dist/core/agents/agent-worker-pipeline.js +36 -2
- package/dist/core/agents/codex-exec-worker-adapter.js +2 -0
- package/dist/core/agents/native-cli-worker.js +0 -1
- package/dist/core/agents/native-worker-backend-router.js +58 -1
- package/dist/core/codex-app/sks-menubar.js +477 -132
- package/dist/core/codex-app.js +1 -0
- package/dist/core/codex-control/codex-sdk-config-policy.js +2 -1
- package/dist/core/codex-control/codex-sdk-env-policy.js +1 -1
- package/dist/core/codex-control/schemas/agent-worker-result.schema.js +12 -3
- package/dist/core/codex-hooks/codex-hook-managed-install.js +5 -0
- package/dist/core/codex-hooks/codex-hook-state-writer.js +70 -11
- package/dist/core/codex-hooks/codex-hook-trust-doctor.js +2 -2
- package/dist/core/codex-lb/codex-lb-env.js +20 -1
- package/dist/core/commands/basic-cli.js +18 -1
- package/dist/core/commands/check-command.js +37 -5
- package/dist/core/commands/computer-use-command.js +62 -7
- package/dist/core/commands/db-command.js +31 -5
- package/dist/core/commands/fast-mode-command.js +3 -0
- package/dist/core/commands/gate-result-contract.js +43 -0
- package/dist/core/commands/gates-command.js +6 -1
- package/dist/core/commands/gc-command.js +29 -2
- package/dist/core/commands/goal-command.js +9 -2
- package/dist/core/commands/gx-command.js +79 -7
- package/dist/core/commands/image-ux-review-command.js +188 -13
- package/dist/core/commands/mad-db-command.js +85 -176
- package/dist/core/commands/mad-sks-command.js +230 -29
- package/dist/core/commands/menubar-command.js +24 -2
- package/dist/core/commands/naruto-command.js +64 -10
- package/dist/core/commands/plan-command.js +76 -0
- package/dist/core/commands/ppt-command.js +159 -24
- package/dist/core/commands/qa-loop-command.js +6 -2
- package/dist/core/commands/release-command.js +55 -2
- package/dist/core/commands/research-command.js +1 -1
- package/dist/core/commands/review-command.js +217 -0
- package/dist/core/commands/route-success-helpers.js +59 -0
- package/dist/core/commands/run-command.js +19 -7
- package/dist/core/commands/seo-command.js +49 -1
- package/dist/core/commands/ui-command.js +161 -0
- package/dist/core/commands/uninstall-command.js +15 -1
- package/dist/core/db-safety.js +2 -2
- package/dist/core/doctor/doctor-dirty-planner.js +51 -7
- package/dist/core/doctor/doctor-transaction.js +8 -0
- package/dist/core/doctor/doctor-zellij-repair.js +1 -0
- package/dist/core/doctor/imagegen-repair.js +161 -0
- package/dist/core/feature-fixture-runner.js +2 -2
- package/dist/core/feature-fixtures.js +58 -37
- package/dist/core/feature-registry.js +102 -2
- package/dist/core/fsx.js +39 -6
- package/dist/core/image-ux-review/imagegen-adapter.js +48 -11
- package/dist/core/image-ux-review.js +16 -0
- package/dist/core/imagegen/imagegen-capability.js +11 -5
- package/dist/core/imagegen/require-imagegen.js +57 -0
- package/dist/core/init/skills.js +10 -5
- package/dist/core/mad-db/mad-db-coordinator.js +94 -18
- package/dist/core/mad-db/mad-db-policy.js +12 -10
- package/dist/core/mad-sks/executors/executor-base.js +8 -2
- package/dist/core/mad-sks/executors/index.js +4 -0
- package/dist/core/mad-sks/executors/sql-plane-executor.js +194 -0
- package/dist/core/naruto/naruto-active-pool.js +15 -4
- package/dist/core/naruto/naruto-real-worker-child.js +11 -1
- package/dist/core/naruto/naruto-role-policy.js +3 -0
- package/dist/core/naruto/naruto-task-hints.js +10 -0
- package/dist/core/naruto/naruto-work-graph.js +8 -2
- package/dist/core/naruto/naruto-work-item.js +6 -0
- package/dist/core/naruto/solution-tournament.js +101 -0
- package/dist/core/permission-gates.js +30 -0
- package/dist/core/pipeline-internals/runtime-core.js +11 -3
- package/dist/core/pipeline-internals/runtime-gates.js +27 -0
- package/dist/core/ppt-review/index.js +6 -1
- package/dist/core/ppt-review/slide-imagegen-review.js +75 -10
- package/dist/core/ppt.js +70 -2
- package/dist/core/proof/auto-finalize.js +70 -11
- package/dist/core/proof/proof-schema.js +2 -0
- package/dist/core/proof/route-adapter.js +5 -1
- package/dist/core/proof/route-finalizer.js +8 -5
- package/dist/core/proof/selftest-proof-fixtures.js +18 -5
- package/dist/core/proof/validation.js +2 -0
- package/dist/core/provider/model-router.js +53 -0
- package/dist/core/release/release-gate-node.js +3 -0
- package/dist/core/release/release-gate-resource-governor.js +1 -0
- package/dist/core/routes/constants.js +1 -1
- package/dist/core/routes.js +65 -18
- package/dist/core/safety/mutation-guard.js +5 -1
- package/dist/core/stop-gate/gate-evaluator.js +102 -0
- package/dist/core/stop-gate/stop-gate-check.js +16 -1
- package/dist/core/stop-gate/stop-gate-writer.js +4 -0
- package/dist/core/triwiki/agents-md-projector.js +184 -0
- package/dist/core/triwiki-wrongness/wrongness-schema.js +3 -1
- package/dist/core/trust-kernel/trust-kernel-schema.js +1 -0
- package/dist/core/trust-kernel/trust-report.js +1 -1
- package/dist/core/trust-kernel/trust-status.js +2 -0
- package/dist/core/ui/dashboard-html.js +111 -0
- package/dist/core/update/update-migration-state.js +386 -15
- package/dist/core/update-check.js +178 -81
- package/dist/core/verification/diff-quality.js +100 -0
- package/dist/core/verification/impact-scan.js +164 -0
- package/dist/core/verification/machine-feedback.js +146 -0
- package/dist/core/verification/mistake-rule-compiler.js +195 -0
- package/dist/core/version.js +1 -1
- package/dist/core/zellij/zellij-self-heal.js +9 -6
- package/dist/core/zellij/zellij-slot-telemetry.js +1 -1
- package/dist/scripts/check-feature-quality.js +2 -2
- package/dist/scripts/cli-output-consistency-check.js +57 -0
- package/dist/scripts/coding-bench-check.js +136 -0
- package/dist/scripts/doctor-imagegen-repair-check.js +62 -0
- package/dist/scripts/ensure-bin-executable.js +11 -3
- package/dist/scripts/harness-benchmark-check.js +104 -0
- package/dist/scripts/legacy-update-e2e-check.js +228 -0
- package/dist/scripts/mad-db-command-check.js +11 -8
- package/dist/scripts/mad-db-route-identity-check.js +8 -6
- package/dist/scripts/mad-db-safety-conflict-matrix-check.js +31 -8
- package/dist/scripts/mad-db-skill-policy-snapshot-check.js +4 -5
- package/dist/scripts/naruto-real-active-pool-runtime-check.js +26 -1
- package/dist/scripts/ppt-real-imagegen-wiring-check.js +5 -1
- package/dist/scripts/proof-root-cause-policy-check.js +2 -0
- package/dist/scripts/release-dag-full-coverage-check.js +13 -4
- package/dist/scripts/release-gate-dag-runner.js +14 -0
- package/dist/scripts/release-gate-existence-audit.js +2 -1
- package/dist/scripts/release-gate-planner.js +2 -1
- package/dist/scripts/release-gate-script-parity-check.js +45 -17
- package/dist/scripts/release-metadata-1-19-check.js +2 -1
- package/dist/scripts/seo-geo-feature-fixture-quality-check.js +1 -1
- package/dist/scripts/sks-1-11-gate-lib.js +8 -6
- package/dist/scripts/sks-menubar-install-check.js +57 -20
- package/dist/scripts/ux-review-real-loop-fixture-check.js +9 -4
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
- package/dist/scripts/zellij-slot-telemetry-runtime-check.js +1 -0
- package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
- package/docs/demo.tape +28 -0
- package/package.json +31 -24
- package/schemas/codex/completion-proof.schema.json +3 -2
- package/schemas/release/release-gate-node.schema.json +2 -1
- package/dist/scripts/agent-backfill-route-blackbox.js +0 -5
- package/dist/scripts/agent-dynamic-pool-route-blackbox.js +0 -5
- package/dist/scripts/agent-parallel-write-blackbox.js +0 -56
- package/dist/scripts/agent-patch-swarm-route-blackbox.js +0 -10
- package/dist/scripts/agent-route-blackbox-lib.js +0 -132
- package/dist/scripts/blackbox-command-import-smoke.js +0 -143
- package/dist/scripts/blackbox-global-shim.js +0 -77
- package/dist/scripts/blackbox-matrix.js +0 -70
- package/dist/scripts/blackbox-npx-one-shot.js +0 -69
- package/dist/scripts/blackbox-pack-install.js +0 -174
- package/dist/scripts/brand-neutrality-zero-leakage-blackbox.js +0 -4
- package/dist/scripts/build-once-runner-blackbox.js +0 -34
- package/dist/scripts/codex-0140-integration-blackbox.js +0 -13
- package/dist/scripts/codex-agent-type-blackbox.js +0 -4
- package/dist/scripts/codex-app-harness-blackbox.js +0 -4
- package/dist/scripts/codex-app-skill-agent-blackbox.js +0 -4
- package/dist/scripts/codex-hook-approval-blackbox.js +0 -4
- package/dist/scripts/codex-init-deep-directory-local-blackbox.js +0 -4
- package/dist/scripts/codex-native-feature-broker-blackbox.js +0 -4
- package/dist/scripts/codex-native-pattern-analysis-blackbox.js +0 -4
- package/dist/scripts/codex-native-read-repair-split-blackbox.js +0 -55
- package/dist/scripts/codex-native-reference-cache-blackbox.js +0 -41
- package/dist/scripts/core-skill-integrity-blackbox.js +0 -32
- package/dist/scripts/dfix-fast-blackbox-check.js +0 -37
- package/dist/scripts/dfix-parallel-write-blackbox.js +0 -48
- package/dist/scripts/dfix-patch-swarm-route-blackbox.js +0 -10
- package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
- package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
- package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
- package/dist/scripts/doctor-transaction-engine-blackbox.js +0 -28
- package/dist/scripts/doctor-zellij-fix-blackbox.js +0 -4
- package/dist/scripts/doctor-zellij-no-homebrew-blackbox.js +0 -4
- package/dist/scripts/doctor-zellij-upgrade-blackbox.js +0 -4
- package/dist/scripts/gate-pack-runner-blackbox.js +0 -27
- package/dist/scripts/gate-pack-v2-blackbox.js +0 -18
- package/dist/scripts/geo-cli-blackbox-check.js +0 -18
- package/dist/scripts/loop-collision-blackbox.js +0 -3
- package/dist/scripts/loop-concurrency-oversubscription-blackbox.js +0 -3
- package/dist/scripts/loop-fixture-production-misuse-blackbox.js +0 -3
- package/dist/scripts/loop-kill-interrupt-real-blackbox.js +0 -3
- package/dist/scripts/loop-merge-strategy-blackbox.js +0 -3
- package/dist/scripts/loop-mesh-production-e2e-blackbox.js +0 -3
- package/dist/scripts/loop-side-effect-blackbox.js +0 -3
- package/dist/scripts/mad-db-operation-lifecycle-blackbox.js +0 -29
- package/dist/scripts/mad-sks-actual-executor-blackbox.js +0 -5
- package/dist/scripts/mad-zellij-headless-fallback-blackbox.js +0 -4
- package/dist/scripts/mad-zellij-self-heal-blackbox.js +0 -4
- package/dist/scripts/naruto-loop-mesh-blackbox.js +0 -3
- package/dist/scripts/naruto-real-parallelism-blackbox.js +0 -307
- package/dist/scripts/naruto-worktree-coding-blackbox.js +0 -29
- package/dist/scripts/parallel-runtime-real-blackbox.js +0 -44
- package/dist/scripts/pipeline-codex-native-doctor-mad-routing-real-blackbox.js +0 -77
- package/dist/scripts/pipeline-codex-native-e2e-blackbox.js +0 -13
- package/dist/scripts/pipeline-codex-native-image-routing-real-blackbox.js +0 -50
- package/dist/scripts/pipeline-codex-native-loop-routing-real-blackbox.js +0 -74
- package/dist/scripts/pipeline-codex-native-qa-routing-real-blackbox.js +0 -51
- package/dist/scripts/pipeline-codex-native-research-routing-real-blackbox.js +0 -45
- package/dist/scripts/pipeline-execution-profile-routing-blackbox.js +0 -4
- package/dist/scripts/postinstall-global-doctor-blackbox.js +0 -12
- package/dist/scripts/ppt-full-e2e-blackbox-check.js +0 -109
- package/dist/scripts/ppt-imagegen-blackbox-check.js +0 -46
- package/dist/scripts/qa-backfill-route-blackbox.js +0 -5
- package/dist/scripts/qa-patch-swarm-route-blackbox.js +0 -10
- package/dist/scripts/release-full-parallelism-blackbox.js +0 -41
- package/dist/scripts/release-triwiki-first-runner-blackbox.js +0 -48
- package/dist/scripts/release-wiring-3110-blackbox.js +0 -27
- package/dist/scripts/release-wiring-3112-blackbox.js +0 -17
- package/dist/scripts/release-wiring-3113-blackbox.js +0 -17
- package/dist/scripts/research-backfill-route-blackbox.js +0 -5
- package/dist/scripts/research-final-reviewer-blackbox.js +0 -70
- package/dist/scripts/research-stage-cycle-runtime-blackbox.js +0 -40
- package/dist/scripts/research-synthesis-writer-blackbox.js +0 -24
- package/dist/scripts/route-blackbox-realism-check.js +0 -21
- package/dist/scripts/scheduler-resource-claim-blackbox.js +0 -24
- package/dist/scripts/seo-cli-blackbox-check.js +0 -18
- package/dist/scripts/sks-3110-all-feature-regression-blackbox.js +0 -116
- package/dist/scripts/sks-3112-all-feature-regression-blackbox.js +0 -29
- package/dist/scripts/sks-3113-all-feature-regression-blackbox.js +0 -17
- package/dist/scripts/sks-400-all-feature-regression-blackbox.js +0 -21
- package/dist/scripts/sks-400-extreme-parallel-blackbox.js +0 -8
- package/dist/scripts/sks-400-five-minute-blackbox.js +0 -9
- package/dist/scripts/sks-400-legacy-purge-blackbox.js +0 -8
- package/dist/scripts/sks-401-all-feature-regression-blackbox.js +0 -46
- package/dist/scripts/sks-401-five-minute-actual-blackbox.js +0 -23
- package/dist/scripts/sks-402-all-feature-regression-blackbox.js +0 -9
- package/dist/scripts/sks-402-five-minute-real-blackbox.js +0 -22
- package/dist/scripts/sksd-warm-cache-blackbox.js +0 -12
- package/dist/scripts/team-backfill-route-blackbox.js +0 -5
- package/dist/scripts/team-parallel-write-blackbox.js +0 -55
- package/dist/scripts/team-patch-swarm-route-blackbox.js +0 -10
- package/dist/scripts/triwiki-affected-graph-blackbox.js +0 -28
- package/dist/scripts/triwiki-proof-bank-blackbox.js +0 -30
- package/dist/scripts/triwiki-proof-bank-lock-blackbox.js +0 -7
- package/dist/scripts/ux-patch-swarm-route-blackbox.js +0 -10
- package/dist/scripts/ux-review-imagegen-blackbox-check.js +0 -67
- package/dist/scripts/zellij-initial-main-only-blackbox.js +0 -28
- package/dist/scripts/zellij-pane-lock-concurrency-blackbox.js +0 -80
- package/dist/scripts/zellij-pane-lock-open-worker-integration-blackbox.js +0 -137
- package/dist/scripts/zellij-self-heal-typed-blackbox.js +0 -4
- package/dist/scripts/zellij-slot-telemetry-real-blackbox.js +0 -20
- package/dist/scripts/zellij-stacked-fallback-integration-blackbox.js +0 -81
- package/dist/scripts/zellij-worker-pane-real-ui-blackbox.js +0 -202
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
export const DASHBOARD_HTML = `<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>SKS Dashboard</title>
|
|
7
|
+
<style>
|
|
8
|
+
:root {
|
|
9
|
+
--bg:#0d1117; --card:#161b22; --line:#30363d; --text:#e6edf3; --muted:#8b949e;
|
|
10
|
+
--run:#3fb950; --fail:#f85149; --queue:#8b949e; --verify:#58a6ff; --warn:#d29922;
|
|
11
|
+
}
|
|
12
|
+
* { box-sizing: border-box; }
|
|
13
|
+
body { margin:0; background:var(--bg); color:var(--text); font:14px/1.45 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace; }
|
|
14
|
+
header { display:grid; grid-template-columns: 1fr auto; gap:16px; align-items:center; padding:18px 22px; border-bottom:1px solid var(--line); background:#0b1016; position:sticky; top:0; z-index:2; }
|
|
15
|
+
h1 { margin:0; font-size:16px; font-weight:700; letter-spacing:0; }
|
|
16
|
+
.meta { color:var(--muted); margin-top:4px; display:flex; gap:12px; flex-wrap:wrap; }
|
|
17
|
+
.badge { display:inline-flex; align-items:center; min-height:28px; padding:0 10px; border:1px solid var(--line); background:var(--card); color:var(--muted); }
|
|
18
|
+
.badge.pass { color:var(--run); border-color:rgba(63,185,80,.55); }
|
|
19
|
+
.badge.fail { color:var(--fail); border-color:rgba(248,81,73,.55); }
|
|
20
|
+
main { display:grid; grid-template-columns:minmax(0,1fr) 360px; gap:18px; padding:18px; }
|
|
21
|
+
.strip { display:grid; grid-template-columns:repeat(6,minmax(92px,1fr)); gap:10px; margin-bottom:14px; }
|
|
22
|
+
.metric { border:1px solid var(--line); background:var(--card); padding:10px; min-height:64px; }
|
|
23
|
+
.metric b { display:block; font-size:20px; color:var(--text); }
|
|
24
|
+
.metric span { color:var(--muted); font-size:12px; }
|
|
25
|
+
.grid { display:grid; grid-template-columns:repeat(auto-fill,minmax(220px,1fr)); gap:10px; }
|
|
26
|
+
.slot { border:1px solid var(--line); border-left:4px solid var(--queue); background:var(--card); min-height:136px; padding:12px; overflow:hidden; }
|
|
27
|
+
.slot.running { border-left-color:var(--run); }
|
|
28
|
+
.slot.verifying { border-left-color:var(--verify); }
|
|
29
|
+
.slot.completed { border-left-color:var(--run); opacity:.86; }
|
|
30
|
+
.slot.failed { border-left-color:var(--fail); }
|
|
31
|
+
.slot h2 { margin:0 0 8px; font-size:13px; font-weight:700; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
|
|
32
|
+
.row { display:flex; justify-content:space-between; gap:8px; color:var(--muted); font-size:12px; min-width:0; }
|
|
33
|
+
.model { color:var(--verify); overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:110px; }
|
|
34
|
+
.task { margin-top:10px; min-height:38px; color:var(--text); overflow:hidden; display:-webkit-box; -webkit-line-clamp:2; -webkit-box-orient:vertical; }
|
|
35
|
+
.bar { height:6px; background:#0b1016; border:1px solid var(--line); margin-top:12px; overflow:hidden; }
|
|
36
|
+
.bar i { display:block; height:100%; width:0; background:var(--verify); transition:width .25s ease; }
|
|
37
|
+
aside { display:grid; gap:14px; align-content:start; }
|
|
38
|
+
section { border-top:1px solid var(--line); padding-top:12px; }
|
|
39
|
+
section h2 { margin:0 0 10px; font-size:13px; }
|
|
40
|
+
.feed, .gates { display:grid; gap:8px; }
|
|
41
|
+
.event, .gate { background:var(--card); border:1px solid var(--line); padding:9px; color:var(--muted); min-height:34px; overflow-wrap:anywhere; }
|
|
42
|
+
.gate.pass { color:var(--run); }
|
|
43
|
+
.gate.fail { color:var(--fail); }
|
|
44
|
+
.pulse { display:inline-block; width:8px; height:8px; border-radius:50%; background:var(--run); margin-right:7px; animation:pulse 1.6s infinite; }
|
|
45
|
+
@keyframes pulse { 0%,100%{opacity:.35} 50%{opacity:1} }
|
|
46
|
+
@media (max-width: 900px) { main { grid-template-columns:1fr; } .strip { grid-template-columns:repeat(2,minmax(0,1fr)); } header { grid-template-columns:1fr; } }
|
|
47
|
+
</style>
|
|
48
|
+
</head>
|
|
49
|
+
<body>
|
|
50
|
+
<header>
|
|
51
|
+
<div>
|
|
52
|
+
<h1><span class="pulse"></span><span id="mission">SKS mission</span></h1>
|
|
53
|
+
<div class="meta"><span id="route">route</span><span id="elapsed">00:00</span><span id="updated">waiting</span></div>
|
|
54
|
+
</div>
|
|
55
|
+
<div id="gateBadge" class="badge">● proof pending</div>
|
|
56
|
+
</header>
|
|
57
|
+
<main>
|
|
58
|
+
<div>
|
|
59
|
+
<div class="strip" id="strip"></div>
|
|
60
|
+
<div class="grid" id="slots"></div>
|
|
61
|
+
</div>
|
|
62
|
+
<aside>
|
|
63
|
+
<section><h2>Events</h2><div class="feed" id="events"></div></section>
|
|
64
|
+
<section><h2>Gates</h2><div class="gates" id="gates"></div></section>
|
|
65
|
+
</aside>
|
|
66
|
+
</main>
|
|
67
|
+
<script>
|
|
68
|
+
const state = { started: Date.now() };
|
|
69
|
+
function render(payload) {
|
|
70
|
+
const data = payload && payload.snapshot ? payload : { snapshot: payload };
|
|
71
|
+
const snapshot = data.snapshot || {};
|
|
72
|
+
const gates = data.gates || [];
|
|
73
|
+
const events = data.events || [];
|
|
74
|
+
const slots = Object.values(snapshot.slots || {});
|
|
75
|
+
document.getElementById('mission').textContent = snapshot.mission_id || data.mission_id || 'latest';
|
|
76
|
+
document.getElementById('route').textContent = data.route || snapshot.route || 'SKS';
|
|
77
|
+
document.getElementById('elapsed').textContent = elapsed(state.started);
|
|
78
|
+
document.getElementById('updated').textContent = snapshot.updated_at || data.ts || 'waiting';
|
|
79
|
+
const blocked = gates.some(g => g.ok === false);
|
|
80
|
+
const passed = gates.length > 0 && gates.every(g => g.ok !== false);
|
|
81
|
+
const badge = document.getElementById('gateBadge');
|
|
82
|
+
badge.className = 'badge ' + (blocked ? 'fail' : passed ? 'pass' : '');
|
|
83
|
+
badge.textContent = blocked ? '✖ blocked' : passed ? '✔ gate passed' : '● proof pending';
|
|
84
|
+
const counts = snapshot.counts || {};
|
|
85
|
+
const spawned = slots.length;
|
|
86
|
+
document.getElementById('strip').innerHTML = [
|
|
87
|
+
['run', counts.running || 0], ['verify', counts.verifying || 0], ['queue', counts.queued || 0],
|
|
88
|
+
['done', counts.completed || 0], ['fail', counts.failed || 0], ['spawned', spawned]
|
|
89
|
+
].map(([k,v]) => '<div class="metric"><b>'+v+'</b><span>'+k+'</span></div>').join('');
|
|
90
|
+
document.getElementById('slots').innerHTML = slots.map(slot => {
|
|
91
|
+
const status = String(slot.status || 'queued');
|
|
92
|
+
const progress = slot.progress && slot.progress.total ? Math.max(0, Math.min(100, Math.round(slot.progress.done / slot.progress.total * 100))) : (status === 'completed' ? 100 : 0);
|
|
93
|
+
return '<article class="slot '+status+'"><h2>'+esc(slot.slot_id || slot.worker_id || 'slot')+'</h2>'
|
|
94
|
+
+ '<div class="row"><span>'+esc(slot.role || 'worker')+'</span><span class="model">'+esc(slot.model || slot.provider || slot.backend || slot.service_tier || '')+'</span></div>'
|
|
95
|
+
+ '<div class="row"><span>'+esc(status)+'</span><span>'+esc(slot.latest_event_type || '')+'</span></div>'
|
|
96
|
+
+ '<div class="task">'+esc(slot.task_title || slot.current_file || slot.log_tail || '')+'</div>'
|
|
97
|
+
+ '<div class="bar"><i style="width:'+progress+'%"></i></div></article>';
|
|
98
|
+
}).join('') || '<div class="event">No live slots yet.</div>';
|
|
99
|
+
document.getElementById('events').innerHTML = events.slice(-30).reverse().map(e => '<div class="event">'+esc((e.ts || '')+' '+(e.type || e.event_type || e.event || 'event'))+'</div>').join('') || '<div class="event">No events yet.</div>';
|
|
100
|
+
document.getElementById('gates').innerHTML = gates.map(g => '<div class="gate '+(g.ok===false?'fail':'pass')+'">'+esc((g.ok===false?'✖ ':'✔ ')+(g.id || g.file || 'gate')+(g.missing&&g.missing.length?' missing '+g.missing.join(', '):''))+'</div>').join('') || '<div class="gate">No gate snapshot yet.</div>';
|
|
101
|
+
}
|
|
102
|
+
function esc(v) { return String(v == null ? '' : v).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
|
103
|
+
function elapsed(ms) { const s = Math.max(0, Math.floor((Date.now()-ms)/1000)); return String(Math.floor(s/60)).padStart(2,'0')+':'+String(s%60).padStart(2,'0'); }
|
|
104
|
+
fetch('/api/state').then(r => r.json()).then(render).catch(()=>{});
|
|
105
|
+
const source = new EventSource('/events' + location.search);
|
|
106
|
+
source.onmessage = (event) => render(JSON.parse(event.data));
|
|
107
|
+
setInterval(() => { document.getElementById('elapsed').textContent = elapsed(state.started); }, 1000);
|
|
108
|
+
</script>
|
|
109
|
+
</body>
|
|
110
|
+
</html>`;
|
|
111
|
+
//# sourceMappingURL=dashboard-html.js.map
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import fsp from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
2
3
|
import path from 'node:path';
|
|
3
|
-
import { ensureDir, exists, globalSksRoot, nowIso, packageRoot, PACKAGE_VERSION, projectRoot, readJson, runProcess, sha256, which, writeJsonAtomic, writeReceiptRotated } from '../fsx.js';
|
|
4
|
+
import { ensureDir, exists, globalSksRoot, nowIso, packageRoot, PACKAGE_VERSION, projectRoot, readJson, readText, runProcess, sha256, which, writeJsonAtomic, writeReceiptRotated, writeTextAtomic } from '../fsx.js';
|
|
4
5
|
import { MANAGED_ASSET_VERSION } from '../managed-assets/managed-assets-manifest.js';
|
|
5
6
|
import { enforceRetention } from '../retention.js';
|
|
6
7
|
import { COMMANDS } from '../../cli/command-registry.js';
|
|
8
|
+
import { reconcileSkills } from '../init/skills.js';
|
|
9
|
+
import { codexHookTrustDoctor } from '../codex-hooks/codex-hook-trust-doctor.js';
|
|
10
|
+
import { writeCodexConfigGuarded } from '../codex/codex-config-guard.js';
|
|
7
11
|
export const UPDATE_MIGRATION_SCHEMA = 'sks.project-migration-receipt.v2';
|
|
8
12
|
export const INSTALLATION_EPOCH_SCHEMA = 'sks.installation-epoch.v1';
|
|
9
13
|
export function installationEpochPath() {
|
|
@@ -85,15 +89,19 @@ export async function writeProjectUpdateMigrationReceipt(input) {
|
|
|
85
89
|
const receiptPath = projectUpdateMigrationReceiptPath(input.root);
|
|
86
90
|
const epoch = await ensureInstallationEpoch(input.source);
|
|
87
91
|
const retentionCleanup = await runUpdateRetentionCleanup(input.root, input.source);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
92
|
+
const legacyStages = await runLegacyMigrationStages(input.root, { fromVersion: input.fromVersion || null });
|
|
93
|
+
const stageBlockers = legacyStages.flatMap((stage) => stage.blockers.map((blocker) => `${stage.id}:${blocker}`));
|
|
94
|
+
const stageWarnings = legacyStages.flatMap((stage) => stage.warnings.map((warning) => `${stage.id}:${warning}`));
|
|
95
|
+
const requiredBlockers = [...(input.blockers || []), ...stageBlockers];
|
|
96
|
+
const optionalWarnings = [...(input.warnings || []), ...stageWarnings];
|
|
90
97
|
const receipt = {
|
|
91
98
|
schema: UPDATE_MIGRATION_SCHEMA,
|
|
92
|
-
status: input.status || 'current',
|
|
99
|
+
status: input.status || (requiredBlockers.length ? 'blocked' : 'current'),
|
|
93
100
|
sks_version: PACKAGE_VERSION,
|
|
94
101
|
root: input.root,
|
|
95
102
|
source: input.source,
|
|
96
103
|
generated_at: nowIso(),
|
|
104
|
+
from_version: input.fromVersion || null,
|
|
97
105
|
project_root_hash: projectRootHash(input.root),
|
|
98
106
|
installation_epoch_sha256: installationEpochSha256(epoch),
|
|
99
107
|
project_semantic_hash: await projectSemanticHash(input.root),
|
|
@@ -101,7 +109,8 @@ export async function writeProjectUpdateMigrationReceipt(input) {
|
|
|
101
109
|
installation_epoch_path: installationEpochPath(),
|
|
102
110
|
doctor: input.doctor || null,
|
|
103
111
|
retention_cleanup: retentionCleanup,
|
|
104
|
-
update_stages: input.updateStages || [],
|
|
112
|
+
update_stages: [...(input.updateStages || []), ...legacyStages],
|
|
113
|
+
legacy_migration_stages: legacyStages,
|
|
105
114
|
required_blockers: requiredBlockers,
|
|
106
115
|
optional_warnings: optionalWarnings,
|
|
107
116
|
blockers: requiredBlockers,
|
|
@@ -175,6 +184,278 @@ export async function runUpdateRetentionCleanup(root, source = 'update-migration
|
|
|
175
184
|
};
|
|
176
185
|
}
|
|
177
186
|
}
|
|
187
|
+
const LEGACY_MIGRATION_STAGES = [
|
|
188
|
+
{
|
|
189
|
+
id: 'legacy-team-artifacts',
|
|
190
|
+
min_from_version: '0.0.0',
|
|
191
|
+
run: runLegacyTeamArtifactsStage
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
id: 'session-state-split',
|
|
195
|
+
min_from_version: '0.0.0',
|
|
196
|
+
run: runSessionStateSplitStage
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'skills-reconcile',
|
|
200
|
+
min_from_version: '0.0.0',
|
|
201
|
+
run: runSkillsReconcileStage
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'menubar-retarget',
|
|
205
|
+
min_from_version: '0.0.0',
|
|
206
|
+
run: runMenubarRetargetStage
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: 'config-fastmode-normalize',
|
|
210
|
+
min_from_version: '0.0.0',
|
|
211
|
+
run: runConfigFastModeNormalizeStage
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
id: 'hook-trust-refresh',
|
|
215
|
+
min_from_version: '0.0.0',
|
|
216
|
+
run: runHookTrustRefreshStage
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
id: 'receipt-rotation',
|
|
220
|
+
min_from_version: '0.0.0',
|
|
221
|
+
run: runReceiptRotationStage
|
|
222
|
+
}
|
|
223
|
+
];
|
|
224
|
+
async function runLegacyMigrationStages(root, opts = {}) {
|
|
225
|
+
const fromVersion = opts.fromVersion || null;
|
|
226
|
+
const runs = [];
|
|
227
|
+
for (const stage of LEGACY_MIGRATION_STAGES) {
|
|
228
|
+
if (!legacyStageApplies(fromVersion, stage.min_from_version)) {
|
|
229
|
+
runs.push({
|
|
230
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
231
|
+
id: stage.id,
|
|
232
|
+
ok: true,
|
|
233
|
+
status: 'skipped',
|
|
234
|
+
min_from_version: stage.min_from_version,
|
|
235
|
+
from_version: fromVersion,
|
|
236
|
+
actions: ['skipped_by_from_version'],
|
|
237
|
+
blockers: [],
|
|
238
|
+
warnings: []
|
|
239
|
+
});
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
try {
|
|
243
|
+
const result = await stage.run(root, fromVersion);
|
|
244
|
+
runs.push({
|
|
245
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
246
|
+
id: stage.id,
|
|
247
|
+
min_from_version: stage.min_from_version,
|
|
248
|
+
from_version: fromVersion,
|
|
249
|
+
...result
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
catch (err) {
|
|
253
|
+
runs.push({
|
|
254
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
255
|
+
id: stage.id,
|
|
256
|
+
ok: false,
|
|
257
|
+
status: 'failed',
|
|
258
|
+
min_from_version: stage.min_from_version,
|
|
259
|
+
from_version: fromVersion,
|
|
260
|
+
actions: [],
|
|
261
|
+
blockers: [err?.message || String(err)],
|
|
262
|
+
warnings: []
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
return runs;
|
|
267
|
+
}
|
|
268
|
+
async function runLegacyTeamArtifactsStage(root) {
|
|
269
|
+
const candidates = [
|
|
270
|
+
path.join(root, '.sneakoscope', 'team'),
|
|
271
|
+
path.join(root, '.sneakoscope', 'team-dashboard-state.json'),
|
|
272
|
+
path.join(root, '.sneakoscope', 'work-order-ledger.json')
|
|
273
|
+
];
|
|
274
|
+
const present = [];
|
|
275
|
+
for (const candidate of candidates) {
|
|
276
|
+
if (await exists(candidate))
|
|
277
|
+
present.push(path.relative(root, candidate));
|
|
278
|
+
}
|
|
279
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'update', 'legacy-team-artifacts.json'), {
|
|
280
|
+
schema: 'sks.legacy-team-artifacts-migration.v1',
|
|
281
|
+
generated_at: nowIso(),
|
|
282
|
+
present
|
|
283
|
+
});
|
|
284
|
+
return {
|
|
285
|
+
ok: true,
|
|
286
|
+
status: 'ok',
|
|
287
|
+
actions: present.length ? ['recorded_legacy_team_artifacts'] : ['no_legacy_team_artifacts'],
|
|
288
|
+
blockers: [],
|
|
289
|
+
warnings: [],
|
|
290
|
+
detail: { present }
|
|
291
|
+
};
|
|
292
|
+
}
|
|
293
|
+
async function runSessionStateSplitStage(root) {
|
|
294
|
+
const legacyCurrent = path.join(root, '.sneakoscope', 'current.json');
|
|
295
|
+
const stateCurrent = path.join(root, '.sneakoscope', 'state', 'current.json');
|
|
296
|
+
const sessionsDir = path.join(root, '.sneakoscope', 'state', 'sessions');
|
|
297
|
+
await ensureDir(sessionsDir);
|
|
298
|
+
const actions = [];
|
|
299
|
+
let current = await readJson(stateCurrent, null).catch(() => null);
|
|
300
|
+
const legacy = await readJson(legacyCurrent, null).catch(() => null);
|
|
301
|
+
if (!current && legacy) {
|
|
302
|
+
current = legacy;
|
|
303
|
+
await writeJsonAtomic(stateCurrent, current);
|
|
304
|
+
actions.push('copied_legacy_current_json_to_state_current');
|
|
305
|
+
}
|
|
306
|
+
const missionId = typeof current?.mission_id === 'string' ? current.mission_id : typeof current?.mission === 'string' ? current.mission : null;
|
|
307
|
+
if (missionId) {
|
|
308
|
+
const sessionPath = path.join(sessionsDir, `${safeFileName(missionId)}.json`);
|
|
309
|
+
if (!(await exists(sessionPath))) {
|
|
310
|
+
await writeJsonAtomic(sessionPath, { ...current, migrated_from: path.relative(root, stateCurrent), migrated_at: nowIso() });
|
|
311
|
+
actions.push('wrote_state_session_alias');
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (!actions.length)
|
|
315
|
+
actions.push('session_state_current');
|
|
316
|
+
return { ok: true, status: 'ok', actions, blockers: [], warnings: [], detail: { legacy_present: Boolean(legacy), mission_id: missionId } };
|
|
317
|
+
}
|
|
318
|
+
async function runSkillsReconcileStage(root) {
|
|
319
|
+
const global = await reconcileSkills({ targetDir: path.join(os.homedir(), '.agents', 'skills'), scope: 'global', fix: true })
|
|
320
|
+
.catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
321
|
+
const project = await reconcileSkills({ targetDir: path.join(root, '.agents', 'skills'), scope: 'project', fix: true })
|
|
322
|
+
.catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
323
|
+
const blockers = [
|
|
324
|
+
...(global.ok === false || global.error ? [`global:${global.error || 'failed'}`] : []),
|
|
325
|
+
...(project.ok === false || project.error ? [`project:${project.error || 'failed'}`] : [])
|
|
326
|
+
];
|
|
327
|
+
return {
|
|
328
|
+
ok: blockers.length === 0,
|
|
329
|
+
status: blockers.length ? 'failed' : 'ok',
|
|
330
|
+
actions: ['reconciled_global_skills', 'reconciled_project_skills'],
|
|
331
|
+
blockers,
|
|
332
|
+
warnings: [],
|
|
333
|
+
detail: {
|
|
334
|
+
global_installed: Array.isArray(global.installed) ? global.installed.length : null,
|
|
335
|
+
project_removed: Array.isArray(project.removed) ? project.removed.length : null
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
async function runMenubarRetargetStage(root) {
|
|
340
|
+
const actionScript = path.join(os.homedir(), '.codex', 'sks-menubar', 'sks-menubar-action.sh');
|
|
341
|
+
const text = await readText(actionScript, null);
|
|
342
|
+
if (typeof text !== 'string')
|
|
343
|
+
return { ok: true, status: 'ok', actions: ['menubar_action_script_absent'], blockers: [], warnings: [] };
|
|
344
|
+
const desired = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
345
|
+
const line = `SKS_ENTRY=${JSON.stringify(desired)}`;
|
|
346
|
+
const next = /^\s*SKS_ENTRY\s*=.*$/m.test(text)
|
|
347
|
+
? text.replace(/^\s*SKS_ENTRY\s*=.*$/m, line)
|
|
348
|
+
: `${line}\n${text}`;
|
|
349
|
+
if (next !== text) {
|
|
350
|
+
await writeTextAtomic(actionScript, next);
|
|
351
|
+
return { ok: true, status: 'ok', actions: ['retargeted_menubar_action_script'], blockers: [], warnings: [], detail: { action_script: actionScript } };
|
|
352
|
+
}
|
|
353
|
+
return { ok: true, status: 'ok', actions: ['menubar_action_script_current'], blockers: [], warnings: [], detail: { action_script: actionScript } };
|
|
354
|
+
}
|
|
355
|
+
async function runConfigFastModeNormalizeStage() {
|
|
356
|
+
const configPath = path.join(os.homedir(), '.codex', 'config.toml');
|
|
357
|
+
const text = await readText(configPath, null);
|
|
358
|
+
if (typeof text !== 'string')
|
|
359
|
+
return { ok: true, status: 'ok', actions: ['codex_config_absent'], blockers: [], warnings: [] };
|
|
360
|
+
const table = text.match(/(^|\n)\[user\.fast_mode\][\s\S]*?(?=\n\[|$)/);
|
|
361
|
+
const misplaced = table?.[0]?.match(/^\s*default_profile\s*=\s*"([^"]+)"\s*$/m)?.[1] || null;
|
|
362
|
+
const hasTopLevel = /^\s*default_profile\s*=\s*"[^"]+"\s*$/m.test(text.slice(0, Math.max(0, text.search(/^\s*\[/m))));
|
|
363
|
+
if (!misplaced)
|
|
364
|
+
return { ok: true, status: 'ok', actions: ['fastmode_default_profile_current'], blockers: [], warnings: [] };
|
|
365
|
+
let next = text.replace(/(^|\n)(\[user\.fast_mode\][\s\S]*?)(?=\n\[|$)/, (_match, prefix, block) => {
|
|
366
|
+
const cleaned = String(block).split(/\r?\n/).filter((line) => !/^\s*default_profile\s*=/.test(line)).join('\n').trimEnd();
|
|
367
|
+
return `${prefix}${cleaned}`;
|
|
368
|
+
});
|
|
369
|
+
if (!hasTopLevel)
|
|
370
|
+
next = insertTopLevelTomlKey(next, `default_profile = ${JSON.stringify(misplaced)}`);
|
|
371
|
+
let guardResult = null;
|
|
372
|
+
if (next !== text) {
|
|
373
|
+
guardResult = await writeCodexConfigGuarded({
|
|
374
|
+
configPath,
|
|
375
|
+
before: text,
|
|
376
|
+
mutate: () => `${next.trim()}\n`,
|
|
377
|
+
cause: 'project-update-fastmode-default-profile',
|
|
378
|
+
backupTag: 'project-update-fastmode-default-profile',
|
|
379
|
+
preserveFastUiKeys: true
|
|
380
|
+
});
|
|
381
|
+
if (!guardResult.ok) {
|
|
382
|
+
return {
|
|
383
|
+
ok: false,
|
|
384
|
+
status: 'failed',
|
|
385
|
+
actions: ['normalize_fastmode_default_profile_blocked'],
|
|
386
|
+
blockers: [`codex_config_guard:${guardResult.status}`],
|
|
387
|
+
warnings: [],
|
|
388
|
+
detail: { config_path: configPath, default_profile: misplaced, guard: guardResult }
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return { ok: true, status: 'ok', actions: ['normalized_fastmode_default_profile'], blockers: [], warnings: [], detail: { config_path: configPath, default_profile: misplaced, guard: guardResult } };
|
|
393
|
+
}
|
|
394
|
+
async function runHookTrustRefreshStage(root) {
|
|
395
|
+
const result = await codexHookTrustDoctor(root, { fix: true, managed: true, actual: true });
|
|
396
|
+
const blockers = result.ok === false ? (result.blockers || ['hook_trust_refresh_failed']) : [];
|
|
397
|
+
return {
|
|
398
|
+
ok: blockers.length === 0,
|
|
399
|
+
status: blockers.length ? 'failed' : 'ok',
|
|
400
|
+
actions: ['refreshed_hook_trust'],
|
|
401
|
+
blockers,
|
|
402
|
+
warnings: result.warnings || [],
|
|
403
|
+
detail: { entries: result.current_hash_count ?? null }
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async function runReceiptRotationStage(root) {
|
|
407
|
+
const receiptPath = projectUpdateMigrationReceiptPath(root);
|
|
408
|
+
const dir = path.dirname(receiptPath);
|
|
409
|
+
const base = path.basename(receiptPath);
|
|
410
|
+
const rows = await fsp.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
411
|
+
const rotated = await Promise.all(rows
|
|
412
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith(`${base}.`) && entry.name.endsWith('.json'))
|
|
413
|
+
.map(async (entry) => {
|
|
414
|
+
const file = path.join(dir, entry.name);
|
|
415
|
+
return { file, stat: await fsp.stat(file).catch(() => null) };
|
|
416
|
+
}));
|
|
417
|
+
const removable = rotated
|
|
418
|
+
.filter((row) => Boolean(row.stat))
|
|
419
|
+
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
420
|
+
.slice(5);
|
|
421
|
+
await Promise.all(removable.map((row) => fsp.rm(row.file, { force: true }).catch(() => undefined)));
|
|
422
|
+
return {
|
|
423
|
+
ok: true,
|
|
424
|
+
status: 'ok',
|
|
425
|
+
actions: removable.length ? ['rotated_old_update_receipts'] : ['receipt_rotation_current'],
|
|
426
|
+
blockers: [],
|
|
427
|
+
warnings: [],
|
|
428
|
+
detail: { removed: removable.length }
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function legacyStageApplies(fromVersion, minFromVersion) {
|
|
432
|
+
if (!fromVersion)
|
|
433
|
+
return true;
|
|
434
|
+
if (compareVersionLike(fromVersion, PACKAGE_VERSION) > 0)
|
|
435
|
+
return false;
|
|
436
|
+
return compareVersionLike(fromVersion, minFromVersion) >= 0;
|
|
437
|
+
}
|
|
438
|
+
function compareVersionLike(a, b) {
|
|
439
|
+
const pa = String(a || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
|
|
440
|
+
const pb = String(b || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
|
|
441
|
+
for (let index = 0; index < Math.max(pa.length, pb.length, 3); index += 1) {
|
|
442
|
+
if ((pa[index] || 0) > (pb[index] || 0))
|
|
443
|
+
return 1;
|
|
444
|
+
if ((pa[index] || 0) < (pb[index] || 0))
|
|
445
|
+
return -1;
|
|
446
|
+
}
|
|
447
|
+
return 0;
|
|
448
|
+
}
|
|
449
|
+
function safeFileName(value) {
|
|
450
|
+
return String(value || 'session').replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '') || 'session';
|
|
451
|
+
}
|
|
452
|
+
function insertTopLevelTomlKey(text, line) {
|
|
453
|
+
const raw = String(text || '').trimEnd();
|
|
454
|
+
const firstTable = raw.search(/^\s*\[/m);
|
|
455
|
+
if (firstTable < 0)
|
|
456
|
+
return `${line}\n${raw}`.trim() + '\n';
|
|
457
|
+
return `${raw.slice(0, firstTable).trimEnd()}\n${line}\n\n${raw.slice(firstTable).trimStart()}`.trim() + '\n';
|
|
458
|
+
}
|
|
178
459
|
export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
179
460
|
const env = input.env || process.env;
|
|
180
461
|
const command = input.command;
|
|
@@ -215,7 +496,8 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
|
215
496
|
};
|
|
216
497
|
return withUpdateMigrationLock(root, empty, async () => {
|
|
217
498
|
const reportFile = path.join(root, '.sneakoscope', 'update', 'doctor-migration.json');
|
|
218
|
-
const
|
|
499
|
+
const baseTimeoutMs = migrationDoctorTimeoutMs(env);
|
|
500
|
+
let doctor = await runPackageLocalDoctor({
|
|
219
501
|
root,
|
|
220
502
|
args: ['doctor', '--fix', '--yes', '--profile', 'migration', '--machine-only', '--report-file', reportFile],
|
|
221
503
|
env: {
|
|
@@ -223,29 +505,51 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
|
223
505
|
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
224
506
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
225
507
|
},
|
|
226
|
-
timeoutMs:
|
|
508
|
+
timeoutMs: baseTimeoutMs,
|
|
227
509
|
maxOutputBytes: 32 * 1024
|
|
228
510
|
});
|
|
511
|
+
const timeoutWarnings = [];
|
|
512
|
+
if (!doctor.ok && doctor.timedOut) {
|
|
513
|
+
timeoutWarnings.push(`doctor_migration_timeout_retry:timeout_ms=${baseTimeoutMs}`);
|
|
514
|
+
doctor = await runPackageLocalDoctor({
|
|
515
|
+
root,
|
|
516
|
+
args: ['doctor', '--fix', '--yes', '--profile', 'migration', '--machine-only', '--report-file', reportFile],
|
|
517
|
+
env: {
|
|
518
|
+
...env,
|
|
519
|
+
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
520
|
+
SKS_DISABLE_UPDATE_CHECK: '1',
|
|
521
|
+
SKS_MIGRATION_DOCTOR_RETRY: '1'
|
|
522
|
+
},
|
|
523
|
+
timeoutMs: baseTimeoutMs * 2,
|
|
524
|
+
maxOutputBytes: 32 * 1024
|
|
525
|
+
});
|
|
526
|
+
}
|
|
229
527
|
if (!doctor.ok) {
|
|
230
|
-
const
|
|
528
|
+
const blocker = doctor.timedOut ? 'doctor_migration_timeout' : 'doctor_migration_failed';
|
|
529
|
+
const requiredBlockers = [blocker, ...(doctor.required_blockers.length ? doctor.required_blockers : [])];
|
|
530
|
+
const warnings = [
|
|
531
|
+
...timeoutWarnings,
|
|
532
|
+
...doctor.optional_warnings,
|
|
533
|
+
...(doctor.timedOut ? ['doctor_migration_timeout_may_be_network_or_first_compile_slow_run_sks_doctor_fix_yes_for_live_progress'] : [])
|
|
534
|
+
];
|
|
231
535
|
const blocked = await writeProjectUpdateMigrationReceipt({
|
|
232
536
|
root,
|
|
233
537
|
source: 'first-command-gate',
|
|
234
538
|
status: 'blocked',
|
|
235
539
|
doctor,
|
|
236
540
|
blockers: requiredBlockers,
|
|
237
|
-
warnings
|
|
541
|
+
warnings
|
|
238
542
|
});
|
|
239
|
-
return { ...empty, ok: false, status: 'blocked', receipt: blocked, doctor, failed_stage_id: 'doctor:migration-profile', blockers: requiredBlockers, warnings
|
|
543
|
+
return { ...empty, ok: false, status: 'blocked', receipt: blocked, doctor, failed_stage_id: 'doctor:migration-profile', blockers: requiredBlockers, warnings };
|
|
240
544
|
}
|
|
241
545
|
const current = await writeProjectUpdateMigrationReceipt({
|
|
242
546
|
root,
|
|
243
547
|
source: 'first-command-gate',
|
|
244
548
|
doctor,
|
|
245
549
|
blockers: [],
|
|
246
|
-
warnings: doctor.optional_warnings
|
|
550
|
+
warnings: [...timeoutWarnings, ...doctor.optional_warnings]
|
|
247
551
|
});
|
|
248
|
-
return { ...empty, ok: true, status: 'repaired', receipt: current, doctor, failed_stage_id: null, blockers: [], warnings: [] };
|
|
552
|
+
return { ...empty, ok: true, status: 'repaired', receipt: current, doctor, failed_stage_id: null, blockers: [], warnings: [...timeoutWarnings, ...doctor.optional_warnings] };
|
|
249
553
|
}, recheck ? { recheck } : {});
|
|
250
554
|
}
|
|
251
555
|
export async function runPostinstallGlobalDoctorAndMarkPending(input = {}) {
|
|
@@ -267,7 +571,7 @@ export async function runPostinstallGlobalDoctorAndMarkPending(input = {}) {
|
|
|
267
571
|
SKS_DISABLE_UPDATE_CHECK: '1',
|
|
268
572
|
SKS_POSTINSTALL_NO_BOOTSTRAP: '1'
|
|
269
573
|
},
|
|
270
|
-
timeoutMs:
|
|
574
|
+
timeoutMs: migrationDoctorTimeoutMs(env),
|
|
271
575
|
maxOutputBytes: 32 * 1024
|
|
272
576
|
});
|
|
273
577
|
const pending = await writePendingUpdateMigration({
|
|
@@ -288,10 +592,18 @@ function commandSkipsMigrationGate(command) {
|
|
|
288
592
|
const entry = COMMANDS[command];
|
|
289
593
|
return entry?.skipMigrationGate === true || entry?.readonly === true;
|
|
290
594
|
}
|
|
595
|
+
function migrationDoctorTimeoutMs(env) {
|
|
596
|
+
const override = Number.parseInt(env.SKS_MIGRATION_DOCTOR_TIMEOUT_MS || '', 10);
|
|
597
|
+
return Number.isFinite(override) && override > 0 ? override : 180_000;
|
|
598
|
+
}
|
|
291
599
|
export async function runPackageLocalDoctor(input = {}) {
|
|
292
600
|
const entrypoint = input.entrypoint || path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
293
601
|
const cwd = input.root || globalSksRoot();
|
|
294
602
|
const args = input.args || ['doctor', '--json'];
|
|
603
|
+
const env = input.env || process.env;
|
|
604
|
+
const testRun = testPackageLocalDoctorRun({ entrypoint, cwd, args, env });
|
|
605
|
+
if (testRun)
|
|
606
|
+
return testRun;
|
|
295
607
|
if (!(await exists(entrypoint))) {
|
|
296
608
|
return {
|
|
297
609
|
schema: 'sks.package-local-doctor-run.v1',
|
|
@@ -306,6 +618,8 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
306
618
|
optional_warnings: [],
|
|
307
619
|
stdout_tail: '',
|
|
308
620
|
stderr_tail: '',
|
|
621
|
+
timedOut: false,
|
|
622
|
+
timed_out: false,
|
|
309
623
|
error: `missing package-local sks entrypoint: ${entrypoint}`
|
|
310
624
|
};
|
|
311
625
|
}
|
|
@@ -313,7 +627,7 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
313
627
|
cwd,
|
|
314
628
|
env: {
|
|
315
629
|
...process.env,
|
|
316
|
-
...
|
|
630
|
+
...env,
|
|
317
631
|
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
318
632
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
319
633
|
},
|
|
@@ -322,7 +636,8 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
322
636
|
}).catch((err) => ({
|
|
323
637
|
code: 1,
|
|
324
638
|
stdout: '',
|
|
325
|
-
stderr: err?.message || String(err)
|
|
639
|
+
stderr: err?.message || String(err),
|
|
640
|
+
timedOut: false
|
|
326
641
|
}));
|
|
327
642
|
const reportFile = reportFileFromArgs(args);
|
|
328
643
|
const parsed = reportFile
|
|
@@ -345,9 +660,65 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
345
660
|
optional_warnings: optionalWarnings,
|
|
346
661
|
stdout_tail: tail(result.stdout || ''),
|
|
347
662
|
stderr_tail: tail(result.stderr || ''),
|
|
663
|
+
timedOut: result.timedOut === true,
|
|
664
|
+
timed_out: result.timedOut === true,
|
|
348
665
|
error: ok ? null : tail(result.stderr || result.stdout || requiredBlockers.join(', ') || 'doctor failed')
|
|
349
666
|
};
|
|
350
667
|
}
|
|
668
|
+
function testPackageLocalDoctorRun(input) {
|
|
669
|
+
if (input.env.SKS_TEST_DOCTOR_TIMEOUT_ONCE === '1' && input.env.SKS_MIGRATION_DOCTOR_RETRY !== '1') {
|
|
670
|
+
return mockPackageLocalDoctorRun(input, {
|
|
671
|
+
ok: false,
|
|
672
|
+
timedOut: true,
|
|
673
|
+
exitCode: 124,
|
|
674
|
+
blockers: ['test_doctor_timeout_once']
|
|
675
|
+
});
|
|
676
|
+
}
|
|
677
|
+
if (input.env.SKS_TEST_DOCTOR_TIMEOUT_ONCE === '1' && input.env.SKS_MIGRATION_DOCTOR_RETRY === '1') {
|
|
678
|
+
return mockPackageLocalDoctorRun(input, {
|
|
679
|
+
ok: true,
|
|
680
|
+
timedOut: false,
|
|
681
|
+
exitCode: 0,
|
|
682
|
+
warnings: ['test_doctor_retry_succeeded']
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
if (input.env.SKS_TEST_DOCTOR_FAIL === '1') {
|
|
686
|
+
return mockPackageLocalDoctorRun(input, {
|
|
687
|
+
ok: false,
|
|
688
|
+
timedOut: false,
|
|
689
|
+
exitCode: 1,
|
|
690
|
+
blockers: ['test_doctor_failed']
|
|
691
|
+
});
|
|
692
|
+
}
|
|
693
|
+
if (input.env.SKS_TEST_DOCTOR_OK === '1') {
|
|
694
|
+
return mockPackageLocalDoctorRun(input, {
|
|
695
|
+
ok: true,
|
|
696
|
+
timedOut: false,
|
|
697
|
+
exitCode: 0,
|
|
698
|
+
warnings: ['test_doctor_ok']
|
|
699
|
+
});
|
|
700
|
+
}
|
|
701
|
+
return null;
|
|
702
|
+
}
|
|
703
|
+
function mockPackageLocalDoctorRun(input, result) {
|
|
704
|
+
return {
|
|
705
|
+
schema: 'sks.package-local-doctor-run.v1',
|
|
706
|
+
ok: result.ok,
|
|
707
|
+
status: result.ok ? 'ok' : 'failed',
|
|
708
|
+
entrypoint: input.entrypoint,
|
|
709
|
+
cwd: input.cwd,
|
|
710
|
+
args: input.args,
|
|
711
|
+
exit_code: result.exitCode,
|
|
712
|
+
parsed_ok: result.ok,
|
|
713
|
+
required_blockers: result.ok ? [] : result.blockers || [],
|
|
714
|
+
optional_warnings: result.warnings || [],
|
|
715
|
+
stdout_tail: result.ok ? '{"ok":true}' : '',
|
|
716
|
+
stderr_tail: result.ok ? '' : (result.blockers || ['doctor failed']).join(', '),
|
|
717
|
+
timedOut: result.timedOut,
|
|
718
|
+
timed_out: result.timedOut,
|
|
719
|
+
error: result.ok ? null : (result.blockers || ['doctor failed']).join(', ')
|
|
720
|
+
};
|
|
721
|
+
}
|
|
351
722
|
export async function resolveInstalledSksEntrypoint(input = {}) {
|
|
352
723
|
const packageName = input.packageName || 'sneakoscope';
|
|
353
724
|
const candidates = [
|