sneakoscope 4.8.7 → 5.1.2
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/bench/tasks/t01-off-by-one/repo/package.json +9 -0
- package/bench/tasks/t01-off-by-one/repo/src/pagination.js +5 -0
- package/bench/tasks/t01-off-by-one/repo/test.js +5 -0
- package/bench/tasks/t01-off-by-one/task.json +13 -0
- package/bench/tasks/t02-signature-cochange/repo/package.json +9 -0
- package/bench/tasks/t02-signature-cochange/repo/src/admin.js +5 -0
- package/bench/tasks/t02-signature-cochange/repo/src/audit.js +5 -0
- package/bench/tasks/t02-signature-cochange/repo/src/labels.js +3 -0
- package/bench/tasks/t02-signature-cochange/repo/src/profile.js +5 -0
- package/bench/tasks/t02-signature-cochange/repo/test.js +9 -0
- package/bench/tasks/t02-signature-cochange/task.json +12 -0
- package/bench/tasks/t03-type-puzzle/repo/package.json +9 -0
- package/bench/tasks/t03-type-puzzle/repo/src/config.js +4 -0
- package/bench/tasks/t03-type-puzzle/repo/test.js +6 -0
- package/bench/tasks/t03-type-puzzle/task.json +13 -0
- package/bench/tasks/t04-refactor-preserve/repo/package.json +9 -0
- package/bench/tasks/t04-refactor-preserve/repo/src/cart.js +8 -0
- package/bench/tasks/t04-refactor-preserve/repo/test.js +4 -0
- package/bench/tasks/t04-refactor-preserve/task.json +12 -0
- package/bench/tasks/t05-performance/repo/package.json +9 -0
- package/bench/tasks/t05-performance/repo/src/pairs.js +9 -0
- package/bench/tasks/t05-performance/repo/test.js +11 -0
- package/bench/tasks/t05-performance/task.json +12 -0
- package/bench/tasks/t06-mistake-rule/repo/package.json +9 -0
- package/bench/tasks/t06-mistake-rule/repo/src/loader.js +6 -0
- package/bench/tasks/t06-mistake-rule/repo/test.js +7 -0
- package/bench/tasks/t06-mistake-rule/task.json +12 -0
- 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 +62 -6
- 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-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-worker-backend-router.js +58 -1
- package/dist/core/codex-app/sks-menubar.js +18 -5
- 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 +21 -2
- 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 +22 -1
- 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-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 +10 -0
- 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 +366 -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/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/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 +1 -1
- 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/ux-review-real-loop-fixture-check.js +9 -4
- package/dist/scripts/ux-review-run-wires-imagegen-check.js +4 -0
- package/docs/assets/sneakoscope-architecture-pipeline.jpg +0 -0
- package/docs/demo.tape +28 -0
- package/package.json +67 -3
- 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-context7-mcp-repair-blackbox.js +0 -16
- package/dist/scripts/doctor-dirty-repair-blackbox.js +0 -22
- package/dist/scripts/doctor-dirty-semantic-blackbox.js +0 -8
- package/dist/scripts/doctor-fix-production-blackbox.js +0 -26
- package/dist/scripts/doctor-native-capability-repair-blackbox.js +0 -39
- package/dist/scripts/doctor-startup-config-repair-blackbox.js +0 -13
- package/dist/scripts/doctor-supabase-mcp-repair-blackbox.js +0 -14
- 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/project-skill-dedupe-blackbox.js +0 -36
- 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/supabase-secret-preservation-blackbox.js +0 -29
- 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/update-preserves-supabase-keys-blackbox.js +0 -27
- 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,12 @@
|
|
|
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';
|
|
7
10
|
export const UPDATE_MIGRATION_SCHEMA = 'sks.project-migration-receipt.v2';
|
|
8
11
|
export const INSTALLATION_EPOCH_SCHEMA = 'sks.installation-epoch.v1';
|
|
9
12
|
export function installationEpochPath() {
|
|
@@ -85,15 +88,19 @@ export async function writeProjectUpdateMigrationReceipt(input) {
|
|
|
85
88
|
const receiptPath = projectUpdateMigrationReceiptPath(input.root);
|
|
86
89
|
const epoch = await ensureInstallationEpoch(input.source);
|
|
87
90
|
const retentionCleanup = await runUpdateRetentionCleanup(input.root, input.source);
|
|
88
|
-
const
|
|
89
|
-
const
|
|
91
|
+
const legacyStages = await runLegacyMigrationStages(input.root, { fromVersion: input.fromVersion || null });
|
|
92
|
+
const stageBlockers = legacyStages.flatMap((stage) => stage.blockers.map((blocker) => `${stage.id}:${blocker}`));
|
|
93
|
+
const stageWarnings = legacyStages.flatMap((stage) => stage.warnings.map((warning) => `${stage.id}:${warning}`));
|
|
94
|
+
const requiredBlockers = [...(input.blockers || []), ...stageBlockers];
|
|
95
|
+
const optionalWarnings = [...(input.warnings || []), ...stageWarnings];
|
|
90
96
|
const receipt = {
|
|
91
97
|
schema: UPDATE_MIGRATION_SCHEMA,
|
|
92
|
-
status: input.status || 'current',
|
|
98
|
+
status: input.status || (requiredBlockers.length ? 'blocked' : 'current'),
|
|
93
99
|
sks_version: PACKAGE_VERSION,
|
|
94
100
|
root: input.root,
|
|
95
101
|
source: input.source,
|
|
96
102
|
generated_at: nowIso(),
|
|
103
|
+
from_version: input.fromVersion || null,
|
|
97
104
|
project_root_hash: projectRootHash(input.root),
|
|
98
105
|
installation_epoch_sha256: installationEpochSha256(epoch),
|
|
99
106
|
project_semantic_hash: await projectSemanticHash(input.root),
|
|
@@ -101,7 +108,8 @@ export async function writeProjectUpdateMigrationReceipt(input) {
|
|
|
101
108
|
installation_epoch_path: installationEpochPath(),
|
|
102
109
|
doctor: input.doctor || null,
|
|
103
110
|
retention_cleanup: retentionCleanup,
|
|
104
|
-
update_stages: input.updateStages || [],
|
|
111
|
+
update_stages: [...(input.updateStages || []), ...legacyStages],
|
|
112
|
+
legacy_migration_stages: legacyStages,
|
|
105
113
|
required_blockers: requiredBlockers,
|
|
106
114
|
optional_warnings: optionalWarnings,
|
|
107
115
|
blockers: requiredBlockers,
|
|
@@ -175,6 +183,259 @@ export async function runUpdateRetentionCleanup(root, source = 'update-migration
|
|
|
175
183
|
};
|
|
176
184
|
}
|
|
177
185
|
}
|
|
186
|
+
const LEGACY_MIGRATION_STAGES = [
|
|
187
|
+
{
|
|
188
|
+
id: 'legacy-team-artifacts',
|
|
189
|
+
min_from_version: '0.0.0',
|
|
190
|
+
run: runLegacyTeamArtifactsStage
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
id: 'session-state-split',
|
|
194
|
+
min_from_version: '0.0.0',
|
|
195
|
+
run: runSessionStateSplitStage
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'skills-reconcile',
|
|
199
|
+
min_from_version: '0.0.0',
|
|
200
|
+
run: runSkillsReconcileStage
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
id: 'menubar-retarget',
|
|
204
|
+
min_from_version: '0.0.0',
|
|
205
|
+
run: runMenubarRetargetStage
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
id: 'config-fastmode-normalize',
|
|
209
|
+
min_from_version: '0.0.0',
|
|
210
|
+
run: runConfigFastModeNormalizeStage
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'hook-trust-refresh',
|
|
214
|
+
min_from_version: '0.0.0',
|
|
215
|
+
run: runHookTrustRefreshStage
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
id: 'receipt-rotation',
|
|
219
|
+
min_from_version: '0.0.0',
|
|
220
|
+
run: runReceiptRotationStage
|
|
221
|
+
}
|
|
222
|
+
];
|
|
223
|
+
async function runLegacyMigrationStages(root, opts = {}) {
|
|
224
|
+
const fromVersion = opts.fromVersion || null;
|
|
225
|
+
const runs = [];
|
|
226
|
+
for (const stage of LEGACY_MIGRATION_STAGES) {
|
|
227
|
+
if (!legacyStageApplies(fromVersion, stage.min_from_version)) {
|
|
228
|
+
runs.push({
|
|
229
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
230
|
+
id: stage.id,
|
|
231
|
+
ok: true,
|
|
232
|
+
status: 'skipped',
|
|
233
|
+
min_from_version: stage.min_from_version,
|
|
234
|
+
from_version: fromVersion,
|
|
235
|
+
actions: ['skipped_by_from_version'],
|
|
236
|
+
blockers: [],
|
|
237
|
+
warnings: []
|
|
238
|
+
});
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
try {
|
|
242
|
+
const result = await stage.run(root, fromVersion);
|
|
243
|
+
runs.push({
|
|
244
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
245
|
+
id: stage.id,
|
|
246
|
+
min_from_version: stage.min_from_version,
|
|
247
|
+
from_version: fromVersion,
|
|
248
|
+
...result
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
catch (err) {
|
|
252
|
+
runs.push({
|
|
253
|
+
schema: 'sks.legacy-update-migration-stage.v1',
|
|
254
|
+
id: stage.id,
|
|
255
|
+
ok: false,
|
|
256
|
+
status: 'failed',
|
|
257
|
+
min_from_version: stage.min_from_version,
|
|
258
|
+
from_version: fromVersion,
|
|
259
|
+
actions: [],
|
|
260
|
+
blockers: [err?.message || String(err)],
|
|
261
|
+
warnings: []
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
return runs;
|
|
266
|
+
}
|
|
267
|
+
async function runLegacyTeamArtifactsStage(root) {
|
|
268
|
+
const candidates = [
|
|
269
|
+
path.join(root, '.sneakoscope', 'team'),
|
|
270
|
+
path.join(root, '.sneakoscope', 'team-dashboard-state.json'),
|
|
271
|
+
path.join(root, '.sneakoscope', 'work-order-ledger.json')
|
|
272
|
+
];
|
|
273
|
+
const present = [];
|
|
274
|
+
for (const candidate of candidates) {
|
|
275
|
+
if (await exists(candidate))
|
|
276
|
+
present.push(path.relative(root, candidate));
|
|
277
|
+
}
|
|
278
|
+
await writeJsonAtomic(path.join(root, '.sneakoscope', 'update', 'legacy-team-artifacts.json'), {
|
|
279
|
+
schema: 'sks.legacy-team-artifacts-migration.v1',
|
|
280
|
+
generated_at: nowIso(),
|
|
281
|
+
present
|
|
282
|
+
});
|
|
283
|
+
return {
|
|
284
|
+
ok: true,
|
|
285
|
+
status: 'ok',
|
|
286
|
+
actions: present.length ? ['recorded_legacy_team_artifacts'] : ['no_legacy_team_artifacts'],
|
|
287
|
+
blockers: [],
|
|
288
|
+
warnings: [],
|
|
289
|
+
detail: { present }
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
async function runSessionStateSplitStage(root) {
|
|
293
|
+
const legacyCurrent = path.join(root, '.sneakoscope', 'current.json');
|
|
294
|
+
const stateCurrent = path.join(root, '.sneakoscope', 'state', 'current.json');
|
|
295
|
+
const sessionsDir = path.join(root, '.sneakoscope', 'state', 'sessions');
|
|
296
|
+
await ensureDir(sessionsDir);
|
|
297
|
+
const actions = [];
|
|
298
|
+
let current = await readJson(stateCurrent, null).catch(() => null);
|
|
299
|
+
const legacy = await readJson(legacyCurrent, null).catch(() => null);
|
|
300
|
+
if (!current && legacy) {
|
|
301
|
+
current = legacy;
|
|
302
|
+
await writeJsonAtomic(stateCurrent, current);
|
|
303
|
+
actions.push('copied_legacy_current_json_to_state_current');
|
|
304
|
+
}
|
|
305
|
+
const missionId = typeof current?.mission_id === 'string' ? current.mission_id : typeof current?.mission === 'string' ? current.mission : null;
|
|
306
|
+
if (missionId) {
|
|
307
|
+
const sessionPath = path.join(sessionsDir, `${safeFileName(missionId)}.json`);
|
|
308
|
+
if (!(await exists(sessionPath))) {
|
|
309
|
+
await writeJsonAtomic(sessionPath, { ...current, migrated_from: path.relative(root, stateCurrent), migrated_at: nowIso() });
|
|
310
|
+
actions.push('wrote_state_session_alias');
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!actions.length)
|
|
314
|
+
actions.push('session_state_current');
|
|
315
|
+
return { ok: true, status: 'ok', actions, blockers: [], warnings: [], detail: { legacy_present: Boolean(legacy), mission_id: missionId } };
|
|
316
|
+
}
|
|
317
|
+
async function runSkillsReconcileStage(root) {
|
|
318
|
+
const global = await reconcileSkills({ targetDir: path.join(os.homedir(), '.agents', 'skills'), scope: 'global', fix: true })
|
|
319
|
+
.catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
320
|
+
const project = await reconcileSkills({ targetDir: path.join(root, '.agents', 'skills'), scope: 'project', fix: true })
|
|
321
|
+
.catch((err) => ({ ok: false, error: err?.message || String(err) }));
|
|
322
|
+
const blockers = [
|
|
323
|
+
...(global.ok === false || global.error ? [`global:${global.error || 'failed'}`] : []),
|
|
324
|
+
...(project.ok === false || project.error ? [`project:${project.error || 'failed'}`] : [])
|
|
325
|
+
];
|
|
326
|
+
return {
|
|
327
|
+
ok: blockers.length === 0,
|
|
328
|
+
status: blockers.length ? 'failed' : 'ok',
|
|
329
|
+
actions: ['reconciled_global_skills', 'reconciled_project_skills'],
|
|
330
|
+
blockers,
|
|
331
|
+
warnings: [],
|
|
332
|
+
detail: {
|
|
333
|
+
global_installed: Array.isArray(global.installed) ? global.installed.length : null,
|
|
334
|
+
project_removed: Array.isArray(project.removed) ? project.removed.length : null
|
|
335
|
+
}
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async function runMenubarRetargetStage(root) {
|
|
339
|
+
const actionScript = path.join(os.homedir(), '.codex', 'sks-menubar', 'sks-menubar-action.sh');
|
|
340
|
+
const text = await readText(actionScript, null);
|
|
341
|
+
if (typeof text !== 'string')
|
|
342
|
+
return { ok: true, status: 'ok', actions: ['menubar_action_script_absent'], blockers: [], warnings: [] };
|
|
343
|
+
const desired = path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
344
|
+
const line = `SKS_ENTRY=${JSON.stringify(desired)}`;
|
|
345
|
+
const next = /^\s*SKS_ENTRY\s*=.*$/m.test(text)
|
|
346
|
+
? text.replace(/^\s*SKS_ENTRY\s*=.*$/m, line)
|
|
347
|
+
: `${line}\n${text}`;
|
|
348
|
+
if (next !== text) {
|
|
349
|
+
await writeTextAtomic(actionScript, next);
|
|
350
|
+
return { ok: true, status: 'ok', actions: ['retargeted_menubar_action_script'], blockers: [], warnings: [], detail: { action_script: actionScript } };
|
|
351
|
+
}
|
|
352
|
+
return { ok: true, status: 'ok', actions: ['menubar_action_script_current'], blockers: [], warnings: [], detail: { action_script: actionScript } };
|
|
353
|
+
}
|
|
354
|
+
async function runConfigFastModeNormalizeStage() {
|
|
355
|
+
const configPath = path.join(os.homedir(), '.codex', 'config.toml');
|
|
356
|
+
const text = await readText(configPath, null);
|
|
357
|
+
if (typeof text !== 'string')
|
|
358
|
+
return { ok: true, status: 'ok', actions: ['codex_config_absent'], blockers: [], warnings: [] };
|
|
359
|
+
const table = text.match(/(^|\n)\[user\.fast_mode\][\s\S]*?(?=\n\[|$)/);
|
|
360
|
+
const misplaced = table?.[0]?.match(/^\s*default_profile\s*=\s*"([^"]+)"\s*$/m)?.[1] || null;
|
|
361
|
+
const hasTopLevel = /^\s*default_profile\s*=\s*"[^"]+"\s*$/m.test(text.slice(0, Math.max(0, text.search(/^\s*\[/m))));
|
|
362
|
+
if (!misplaced)
|
|
363
|
+
return { ok: true, status: 'ok', actions: ['fastmode_default_profile_current'], blockers: [], warnings: [] };
|
|
364
|
+
let next = text.replace(/(^|\n)(\[user\.fast_mode\][\s\S]*?)(?=\n\[|$)/, (_match, prefix, block) => {
|
|
365
|
+
const cleaned = String(block).split(/\r?\n/).filter((line) => !/^\s*default_profile\s*=/.test(line)).join('\n').trimEnd();
|
|
366
|
+
return `${prefix}${cleaned}`;
|
|
367
|
+
});
|
|
368
|
+
if (!hasTopLevel)
|
|
369
|
+
next = insertTopLevelTomlKey(next, `default_profile = ${JSON.stringify(misplaced)}`);
|
|
370
|
+
if (next !== text)
|
|
371
|
+
await writeTextAtomic(configPath, `${next.trim()}\n`);
|
|
372
|
+
return { ok: true, status: 'ok', actions: ['normalized_fastmode_default_profile'], blockers: [], warnings: [], detail: { config_path: configPath, default_profile: misplaced } };
|
|
373
|
+
}
|
|
374
|
+
async function runHookTrustRefreshStage(root) {
|
|
375
|
+
const result = await codexHookTrustDoctor(root, { fix: true, managed: true, actual: true });
|
|
376
|
+
const blockers = result.ok === false ? (result.blockers || ['hook_trust_refresh_failed']) : [];
|
|
377
|
+
return {
|
|
378
|
+
ok: blockers.length === 0,
|
|
379
|
+
status: blockers.length ? 'failed' : 'ok',
|
|
380
|
+
actions: ['refreshed_hook_trust'],
|
|
381
|
+
blockers,
|
|
382
|
+
warnings: result.warnings || [],
|
|
383
|
+
detail: { entries: result.current_hash_count ?? null }
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
async function runReceiptRotationStage(root) {
|
|
387
|
+
const receiptPath = projectUpdateMigrationReceiptPath(root);
|
|
388
|
+
const dir = path.dirname(receiptPath);
|
|
389
|
+
const base = path.basename(receiptPath);
|
|
390
|
+
const rows = await fsp.readdir(dir, { withFileTypes: true }).catch(() => []);
|
|
391
|
+
const rotated = await Promise.all(rows
|
|
392
|
+
.filter((entry) => entry.isFile() && entry.name.startsWith(`${base}.`) && entry.name.endsWith('.json'))
|
|
393
|
+
.map(async (entry) => {
|
|
394
|
+
const file = path.join(dir, entry.name);
|
|
395
|
+
return { file, stat: await fsp.stat(file).catch(() => null) };
|
|
396
|
+
}));
|
|
397
|
+
const removable = rotated
|
|
398
|
+
.filter((row) => Boolean(row.stat))
|
|
399
|
+
.sort((a, b) => b.stat.mtimeMs - a.stat.mtimeMs)
|
|
400
|
+
.slice(5);
|
|
401
|
+
await Promise.all(removable.map((row) => fsp.rm(row.file, { force: true }).catch(() => undefined)));
|
|
402
|
+
return {
|
|
403
|
+
ok: true,
|
|
404
|
+
status: 'ok',
|
|
405
|
+
actions: removable.length ? ['rotated_old_update_receipts'] : ['receipt_rotation_current'],
|
|
406
|
+
blockers: [],
|
|
407
|
+
warnings: [],
|
|
408
|
+
detail: { removed: removable.length }
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
function legacyStageApplies(fromVersion, minFromVersion) {
|
|
412
|
+
if (!fromVersion)
|
|
413
|
+
return true;
|
|
414
|
+
if (compareVersionLike(fromVersion, PACKAGE_VERSION) > 0)
|
|
415
|
+
return false;
|
|
416
|
+
return compareVersionLike(fromVersion, minFromVersion) >= 0;
|
|
417
|
+
}
|
|
418
|
+
function compareVersionLike(a, b) {
|
|
419
|
+
const pa = String(a || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
|
|
420
|
+
const pb = String(b || '').split(/[.-]/).map((value) => Number.parseInt(value, 10) || 0);
|
|
421
|
+
for (let index = 0; index < Math.max(pa.length, pb.length, 3); index += 1) {
|
|
422
|
+
if ((pa[index] || 0) > (pb[index] || 0))
|
|
423
|
+
return 1;
|
|
424
|
+
if ((pa[index] || 0) < (pb[index] || 0))
|
|
425
|
+
return -1;
|
|
426
|
+
}
|
|
427
|
+
return 0;
|
|
428
|
+
}
|
|
429
|
+
function safeFileName(value) {
|
|
430
|
+
return String(value || 'session').replace(/[^A-Za-z0-9_.-]+/g, '-').replace(/^-+|-+$/g, '') || 'session';
|
|
431
|
+
}
|
|
432
|
+
function insertTopLevelTomlKey(text, line) {
|
|
433
|
+
const raw = String(text || '').trimEnd();
|
|
434
|
+
const firstTable = raw.search(/^\s*\[/m);
|
|
435
|
+
if (firstTable < 0)
|
|
436
|
+
return `${line}\n${raw}`.trim() + '\n';
|
|
437
|
+
return `${raw.slice(0, firstTable).trimEnd()}\n${line}\n\n${raw.slice(firstTable).trimStart()}`.trim() + '\n';
|
|
438
|
+
}
|
|
178
439
|
export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
179
440
|
const env = input.env || process.env;
|
|
180
441
|
const command = input.command;
|
|
@@ -215,7 +476,8 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
|
215
476
|
};
|
|
216
477
|
return withUpdateMigrationLock(root, empty, async () => {
|
|
217
478
|
const reportFile = path.join(root, '.sneakoscope', 'update', 'doctor-migration.json');
|
|
218
|
-
const
|
|
479
|
+
const baseTimeoutMs = migrationDoctorTimeoutMs(env);
|
|
480
|
+
let doctor = await runPackageLocalDoctor({
|
|
219
481
|
root,
|
|
220
482
|
args: ['doctor', '--fix', '--yes', '--profile', 'migration', '--machine-only', '--report-file', reportFile],
|
|
221
483
|
env: {
|
|
@@ -223,29 +485,51 @@ export async function ensureCurrentMigrationBeforeCommand(input) {
|
|
|
223
485
|
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
224
486
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
225
487
|
},
|
|
226
|
-
timeoutMs:
|
|
488
|
+
timeoutMs: baseTimeoutMs,
|
|
227
489
|
maxOutputBytes: 32 * 1024
|
|
228
490
|
});
|
|
491
|
+
const timeoutWarnings = [];
|
|
492
|
+
if (!doctor.ok && doctor.timedOut) {
|
|
493
|
+
timeoutWarnings.push(`doctor_migration_timeout_retry:timeout_ms=${baseTimeoutMs}`);
|
|
494
|
+
doctor = await runPackageLocalDoctor({
|
|
495
|
+
root,
|
|
496
|
+
args: ['doctor', '--fix', '--yes', '--profile', 'migration', '--machine-only', '--report-file', reportFile],
|
|
497
|
+
env: {
|
|
498
|
+
...env,
|
|
499
|
+
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
500
|
+
SKS_DISABLE_UPDATE_CHECK: '1',
|
|
501
|
+
SKS_MIGRATION_DOCTOR_RETRY: '1'
|
|
502
|
+
},
|
|
503
|
+
timeoutMs: baseTimeoutMs * 2,
|
|
504
|
+
maxOutputBytes: 32 * 1024
|
|
505
|
+
});
|
|
506
|
+
}
|
|
229
507
|
if (!doctor.ok) {
|
|
230
|
-
const
|
|
508
|
+
const blocker = doctor.timedOut ? 'doctor_migration_timeout' : 'doctor_migration_failed';
|
|
509
|
+
const requiredBlockers = [blocker, ...(doctor.required_blockers.length ? doctor.required_blockers : [])];
|
|
510
|
+
const warnings = [
|
|
511
|
+
...timeoutWarnings,
|
|
512
|
+
...doctor.optional_warnings,
|
|
513
|
+
...(doctor.timedOut ? ['doctor_migration_timeout_may_be_network_or_first_compile_slow_run_sks_doctor_fix_yes_for_live_progress'] : [])
|
|
514
|
+
];
|
|
231
515
|
const blocked = await writeProjectUpdateMigrationReceipt({
|
|
232
516
|
root,
|
|
233
517
|
source: 'first-command-gate',
|
|
234
518
|
status: 'blocked',
|
|
235
519
|
doctor,
|
|
236
520
|
blockers: requiredBlockers,
|
|
237
|
-
warnings
|
|
521
|
+
warnings
|
|
238
522
|
});
|
|
239
|
-
return { ...empty, ok: false, status: 'blocked', receipt: blocked, doctor, failed_stage_id: 'doctor:migration-profile', blockers: requiredBlockers, warnings
|
|
523
|
+
return { ...empty, ok: false, status: 'blocked', receipt: blocked, doctor, failed_stage_id: 'doctor:migration-profile', blockers: requiredBlockers, warnings };
|
|
240
524
|
}
|
|
241
525
|
const current = await writeProjectUpdateMigrationReceipt({
|
|
242
526
|
root,
|
|
243
527
|
source: 'first-command-gate',
|
|
244
528
|
doctor,
|
|
245
529
|
blockers: [],
|
|
246
|
-
warnings: doctor.optional_warnings
|
|
530
|
+
warnings: [...timeoutWarnings, ...doctor.optional_warnings]
|
|
247
531
|
});
|
|
248
|
-
return { ...empty, ok: true, status: 'repaired', receipt: current, doctor, failed_stage_id: null, blockers: [], warnings: [] };
|
|
532
|
+
return { ...empty, ok: true, status: 'repaired', receipt: current, doctor, failed_stage_id: null, blockers: [], warnings: [...timeoutWarnings, ...doctor.optional_warnings] };
|
|
249
533
|
}, recheck ? { recheck } : {});
|
|
250
534
|
}
|
|
251
535
|
export async function runPostinstallGlobalDoctorAndMarkPending(input = {}) {
|
|
@@ -267,7 +551,7 @@ export async function runPostinstallGlobalDoctorAndMarkPending(input = {}) {
|
|
|
267
551
|
SKS_DISABLE_UPDATE_CHECK: '1',
|
|
268
552
|
SKS_POSTINSTALL_NO_BOOTSTRAP: '1'
|
|
269
553
|
},
|
|
270
|
-
timeoutMs:
|
|
554
|
+
timeoutMs: migrationDoctorTimeoutMs(env),
|
|
271
555
|
maxOutputBytes: 32 * 1024
|
|
272
556
|
});
|
|
273
557
|
const pending = await writePendingUpdateMigration({
|
|
@@ -288,10 +572,18 @@ function commandSkipsMigrationGate(command) {
|
|
|
288
572
|
const entry = COMMANDS[command];
|
|
289
573
|
return entry?.skipMigrationGate === true || entry?.readonly === true;
|
|
290
574
|
}
|
|
575
|
+
function migrationDoctorTimeoutMs(env) {
|
|
576
|
+
const override = Number.parseInt(env.SKS_MIGRATION_DOCTOR_TIMEOUT_MS || '', 10);
|
|
577
|
+
return Number.isFinite(override) && override > 0 ? override : 180_000;
|
|
578
|
+
}
|
|
291
579
|
export async function runPackageLocalDoctor(input = {}) {
|
|
292
580
|
const entrypoint = input.entrypoint || path.join(packageRoot(), 'dist', 'bin', 'sks.js');
|
|
293
581
|
const cwd = input.root || globalSksRoot();
|
|
294
582
|
const args = input.args || ['doctor', '--json'];
|
|
583
|
+
const env = input.env || process.env;
|
|
584
|
+
const testRun = testPackageLocalDoctorRun({ entrypoint, cwd, args, env });
|
|
585
|
+
if (testRun)
|
|
586
|
+
return testRun;
|
|
295
587
|
if (!(await exists(entrypoint))) {
|
|
296
588
|
return {
|
|
297
589
|
schema: 'sks.package-local-doctor-run.v1',
|
|
@@ -306,6 +598,8 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
306
598
|
optional_warnings: [],
|
|
307
599
|
stdout_tail: '',
|
|
308
600
|
stderr_tail: '',
|
|
601
|
+
timedOut: false,
|
|
602
|
+
timed_out: false,
|
|
309
603
|
error: `missing package-local sks entrypoint: ${entrypoint}`
|
|
310
604
|
};
|
|
311
605
|
}
|
|
@@ -313,7 +607,7 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
313
607
|
cwd,
|
|
314
608
|
env: {
|
|
315
609
|
...process.env,
|
|
316
|
-
...
|
|
610
|
+
...env,
|
|
317
611
|
SKS_UPDATE_MIGRATION_GATE_DISABLED: '1',
|
|
318
612
|
SKS_DISABLE_UPDATE_CHECK: '1'
|
|
319
613
|
},
|
|
@@ -322,7 +616,8 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
322
616
|
}).catch((err) => ({
|
|
323
617
|
code: 1,
|
|
324
618
|
stdout: '',
|
|
325
|
-
stderr: err?.message || String(err)
|
|
619
|
+
stderr: err?.message || String(err),
|
|
620
|
+
timedOut: false
|
|
326
621
|
}));
|
|
327
622
|
const reportFile = reportFileFromArgs(args);
|
|
328
623
|
const parsed = reportFile
|
|
@@ -345,9 +640,65 @@ export async function runPackageLocalDoctor(input = {}) {
|
|
|
345
640
|
optional_warnings: optionalWarnings,
|
|
346
641
|
stdout_tail: tail(result.stdout || ''),
|
|
347
642
|
stderr_tail: tail(result.stderr || ''),
|
|
643
|
+
timedOut: result.timedOut === true,
|
|
644
|
+
timed_out: result.timedOut === true,
|
|
348
645
|
error: ok ? null : tail(result.stderr || result.stdout || requiredBlockers.join(', ') || 'doctor failed')
|
|
349
646
|
};
|
|
350
647
|
}
|
|
648
|
+
function testPackageLocalDoctorRun(input) {
|
|
649
|
+
if (input.env.SKS_TEST_DOCTOR_TIMEOUT_ONCE === '1' && input.env.SKS_MIGRATION_DOCTOR_RETRY !== '1') {
|
|
650
|
+
return mockPackageLocalDoctorRun(input, {
|
|
651
|
+
ok: false,
|
|
652
|
+
timedOut: true,
|
|
653
|
+
exitCode: 124,
|
|
654
|
+
blockers: ['test_doctor_timeout_once']
|
|
655
|
+
});
|
|
656
|
+
}
|
|
657
|
+
if (input.env.SKS_TEST_DOCTOR_TIMEOUT_ONCE === '1' && input.env.SKS_MIGRATION_DOCTOR_RETRY === '1') {
|
|
658
|
+
return mockPackageLocalDoctorRun(input, {
|
|
659
|
+
ok: true,
|
|
660
|
+
timedOut: false,
|
|
661
|
+
exitCode: 0,
|
|
662
|
+
warnings: ['test_doctor_retry_succeeded']
|
|
663
|
+
});
|
|
664
|
+
}
|
|
665
|
+
if (input.env.SKS_TEST_DOCTOR_FAIL === '1') {
|
|
666
|
+
return mockPackageLocalDoctorRun(input, {
|
|
667
|
+
ok: false,
|
|
668
|
+
timedOut: false,
|
|
669
|
+
exitCode: 1,
|
|
670
|
+
blockers: ['test_doctor_failed']
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
if (input.env.SKS_TEST_DOCTOR_OK === '1') {
|
|
674
|
+
return mockPackageLocalDoctorRun(input, {
|
|
675
|
+
ok: true,
|
|
676
|
+
timedOut: false,
|
|
677
|
+
exitCode: 0,
|
|
678
|
+
warnings: ['test_doctor_ok']
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
return null;
|
|
682
|
+
}
|
|
683
|
+
function mockPackageLocalDoctorRun(input, result) {
|
|
684
|
+
return {
|
|
685
|
+
schema: 'sks.package-local-doctor-run.v1',
|
|
686
|
+
ok: result.ok,
|
|
687
|
+
status: result.ok ? 'ok' : 'failed',
|
|
688
|
+
entrypoint: input.entrypoint,
|
|
689
|
+
cwd: input.cwd,
|
|
690
|
+
args: input.args,
|
|
691
|
+
exit_code: result.exitCode,
|
|
692
|
+
parsed_ok: result.ok,
|
|
693
|
+
required_blockers: result.ok ? [] : result.blockers || [],
|
|
694
|
+
optional_warnings: result.warnings || [],
|
|
695
|
+
stdout_tail: result.ok ? '{"ok":true}' : '',
|
|
696
|
+
stderr_tail: result.ok ? '' : (result.blockers || ['doctor failed']).join(', '),
|
|
697
|
+
timedOut: result.timedOut,
|
|
698
|
+
timed_out: result.timedOut,
|
|
699
|
+
error: result.ok ? null : (result.blockers || ['doctor failed']).join(', ')
|
|
700
|
+
};
|
|
701
|
+
}
|
|
351
702
|
export async function resolveInstalledSksEntrypoint(input = {}) {
|
|
352
703
|
const packageName = input.packageName || 'sneakoscope';
|
|
353
704
|
const candidates = [
|