sneakoscope 0.9.12 → 0.9.14
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 +31 -34
- package/crates/sks-core/Cargo.lock +99 -1
- package/crates/sks-core/Cargo.toml +2 -1
- package/crates/sks-core/src/main.rs +176 -2
- package/package.json +8 -3
- package/src/cli/command-registry.mjs +75 -116
- package/src/cli/feature-commands.mjs +81 -11
- package/src/cli/install-helpers.mjs +15 -8
- package/src/cli/router.mjs +2 -3
- package/src/commands/aliases.mjs +2 -0
- package/src/commands/auto-review.mjs +5 -0
- package/src/commands/autoresearch.mjs +5 -0
- package/src/commands/bootstrap.mjs +2 -0
- package/src/commands/code-structure.mjs +5 -0
- package/src/commands/codex-app.mjs +30 -0
- package/src/commands/codex-lb.mjs +78 -4
- package/src/commands/commands.mjs +5 -0
- package/src/commands/commit-and-push.mjs +5 -0
- package/src/commands/commit.mjs +5 -0
- package/src/commands/computer-use.mjs +31 -0
- package/src/commands/conflicts.mjs +13 -0
- package/src/commands/context7.mjs +5 -0
- package/src/commands/db.mjs +6 -0
- package/src/commands/deps.mjs +5 -0
- package/src/commands/dfix.mjs +2 -0
- package/src/commands/doctor.mjs +46 -0
- package/src/commands/dollar-commands.mjs +2 -0
- package/src/commands/eval.mjs +5 -0
- package/src/commands/fix-path.mjs +2 -0
- package/src/commands/gc.mjs +2 -0
- package/src/commands/goal.mjs +5 -0
- package/src/commands/guard.mjs +10 -0
- package/src/commands/gx.mjs +5 -0
- package/src/commands/harness.mjs +5 -0
- package/src/commands/help.mjs +3 -74
- package/src/commands/hook.mjs +8 -0
- package/src/commands/hproof.mjs +5 -0
- package/src/commands/image-ux-review.mjs +38 -0
- package/src/commands/init.mjs +2 -0
- package/src/commands/mad-sks.mjs +14 -0
- package/src/commands/memory.mjs +5 -0
- package/src/commands/openclaw.mjs +2 -0
- package/src/commands/perf.mjs +2 -2
- package/src/commands/pipeline.mjs +25 -0
- package/src/commands/postinstall.mjs +2 -0
- package/src/commands/ppt.mjs +44 -0
- package/src/commands/profile.mjs +5 -0
- package/src/commands/proof-field.mjs +5 -0
- package/src/commands/proof.mjs +59 -2
- package/src/commands/qa-loop.mjs +5 -0
- package/src/commands/quickstart.mjs +2 -0
- package/src/commands/reasoning.mjs +2 -0
- package/src/commands/recallpulse.mjs +5 -0
- package/src/commands/research.mjs +5 -0
- package/src/commands/selftest.mjs +2 -0
- package/src/commands/setup.mjs +2 -0
- package/src/commands/skill-dream.mjs +5 -0
- package/src/commands/stats.mjs +2 -0
- package/src/commands/team.mjs +2 -0
- package/src/commands/tmux.mjs +5 -0
- package/src/commands/update-check.mjs +2 -0
- package/src/commands/usage.mjs +2 -0
- package/src/commands/validate-artifacts.mjs +2 -0
- package/src/commands/versioning.mjs +16 -0
- package/src/commands/wiki.mjs +55 -4
- package/src/core/codex-lb-circuit.mjs +63 -1
- package/src/core/commands/basic-cli.mjs +315 -0
- package/src/{cli/maintenance-commands.mjs → core/commands/route-cli.mjs} +37 -37
- package/src/core/db-safety.mjs +17 -2
- package/src/core/feature-fixture-runner.mjs +109 -0
- package/src/core/feature-fixtures.mjs +40 -2
- package/src/core/feature-registry.mjs +103 -8
- package/src/core/fsx.mjs +1 -1
- package/src/core/git-simple.mjs +118 -0
- package/src/core/hooks-runtime.mjs +12 -3
- package/src/core/pipeline.mjs +19 -1
- package/src/core/proof/evidence-collector.mjs +7 -0
- package/src/core/proof/proof-reader.mjs +11 -0
- package/src/core/proof/proof-redaction.test-helper.mjs +9 -0
- package/src/core/proof/route-adapter.mjs +74 -0
- package/src/core/proof/route-finalizer-fixtures.mjs +21 -0
- package/src/core/proof/route-finalizer-policy.mjs +13 -0
- package/src/core/proof/route-finalizer.mjs +82 -0
- package/src/core/proof/route-proof-gate.mjs +33 -0
- package/src/core/proof/route-proof-policy.mjs +96 -0
- package/src/core/proof/selftest-proof-fixtures.mjs +54 -0
- package/src/core/proof/validation.mjs +1 -0
- package/src/core/proof-field.mjs +2 -2
- package/src/core/routes.mjs +31 -1
- package/src/core/rust-accelerator.mjs +35 -8
- package/src/core/version.mjs +1 -1
- package/src/core/wiki-image/before-after-relation.mjs +1 -0
- package/src/core/wiki-image/callout-parser.mjs +16 -0
- package/src/core/wiki-image/computer-use-evidence.mjs +1 -0
- package/src/core/wiki-image/computer-use-ledger.mjs +38 -0
- package/src/core/wiki-image/generated-review-parser.mjs +1 -0
- package/src/core/wiki-image/image-relation.mjs +2 -0
- package/src/core/wiki-image/image-ux-evidence.mjs +1 -0
- package/src/core/wiki-image/image-voxel-ledger.mjs +51 -1
- package/src/core/wiki-image/ppt-image-evidence.mjs +1 -0
- package/src/core/wiki-image/proof-linker.mjs +12 -0
- package/src/core/wiki-image/route-image-evidence.mjs +107 -0
- package/src/core/wiki-image/validation.mjs +16 -5
- package/src/core/wiki-image/visual-anchor.mjs +14 -1
- package/src/cli/legacy-main.mjs +0 -4146
package/README.md
CHANGED
|
@@ -1,8 +1,27 @@
|
|
|
1
1
|
# Sneakoscope Codex
|
|
2
2
|
|
|
3
|
-
Fast proof-first Codex trust layer with image-based Voxel TriWiki.
|
|
3
|
+
Fast legacy-free proof-first Codex trust layer with image-based Voxel TriWiki.
|
|
4
4
|
|
|
5
|
-
Sneakoscope Codex (`sks`) is a Codex CLI/App harness
|
|
5
|
+
Sneakoscope Codex (`sks`) is a Codex CLI/App harness that makes repeatable Codex work auditable. `0.9.14` runs through split command modules, automatically seals serious routes with Completion Proof, binds visual/UI claims to Image Voxel TriWiki anchors and relations, and release-gates hooks, codex-lb, executable fixtures, Rust parity, and DB safety evidence.
|
|
6
|
+
|
|
7
|
+
## 0.9.14 Current Release
|
|
8
|
+
|
|
9
|
+
`0.9.14` turns SKS into a legacy-free proof-first trust layer for Codex work:
|
|
10
|
+
|
|
11
|
+
- Command registry entries load independent command modules without runtime fallback to archived 0.9.13 CLI bundles.
|
|
12
|
+
- Serious routes write Completion Proof through the central route finalizer before completion is claimed.
|
|
13
|
+
- Visual/UI routes require Image Voxel TriWiki anchors, with before/after relations for visual fix claims.
|
|
14
|
+
- Executable feature fixtures validate expected artifact existence and schema contracts.
|
|
15
|
+
- Hook replay strictly matches decision, reason, gate, issues, continuation, and redaction policy.
|
|
16
|
+
- codex-lb launch failures feed both global and active-project circuit health; stateless `previous_response_not_found` stays a warning.
|
|
17
|
+
- Rust `image-hash` and semantic `voxel-validate` commands match JS fallback behavior.
|
|
18
|
+
- `$Commit` and `$Commit-And-Push` provide a simple git-only route for staging, committing, and optionally pushing without the full SKS pipeline.
|
|
19
|
+
|
|
20
|
+
Learn more:
|
|
21
|
+
- Completion Proof: [docs/completion-proof.md](docs/completion-proof.md)
|
|
22
|
+
- Image Voxel TriWiki: [docs/image-voxel-ledger.md](docs/image-voxel-ledger.md)
|
|
23
|
+
- Codex App Hooks/PAT: [docs/hooks-pat.md](docs/hooks-pat.md)
|
|
24
|
+
- codex-lb: [docs/codex-lb.md](docs/codex-lb.md)
|
|
6
25
|
|
|
7
26
|
## 60-second start
|
|
8
27
|
|
|
@@ -16,22 +35,20 @@ sks selftest --mock
|
|
|
16
35
|
|
|
17
36
|
## Three core promises
|
|
18
37
|
|
|
19
|
-
1.
|
|
20
|
-
2.
|
|
21
|
-
3.
|
|
38
|
+
1. Completion Proof for every serious route
|
|
39
|
+
2. Image Voxel TriWiki anchors and relations for every visual route
|
|
40
|
+
3. Codex App, codex-lb, hooks, Rust, DB, and fixtures verified by release gates
|
|
22
41
|
|
|
23
|
-
##
|
|
42
|
+
## Install Options
|
|
24
43
|
|
|
25
44
|
Install globally, then run `sks` from either a project or any global shell location:
|
|
26
45
|
|
|
27
46
|
```sh
|
|
28
47
|
npm i -g sneakoscope
|
|
29
48
|
sks root
|
|
30
|
-
sks
|
|
49
|
+
sks doctor
|
|
31
50
|
```
|
|
32
51
|
|
|
33
|
-
`0.9.12` adds the lazy CLI architecture foundation, `sks proof`, image voxel ledger commands, cold-start perf checks, hook trust reports, codex-lb circuit metrics, and feature fixture contracts. Rust accelerator source is included in the npm package; until prebuilt binaries ship, SKS uses JS fallbacks unless `SKS_RS_BIN` or a source-checkout `sks-rs` binary is available.
|
|
34
|
-
|
|
35
52
|
`npm i -g sneakoscope` automatically refreshes the `sks` command shim, global Codex App `$` skills, and SKS bootstrap surface. When the install is run from a project, postinstall bootstraps that project. When it is run outside a repo/project marker, postinstall bootstraps the per-user global runtime root instead of writing `.sneakoscope` into a random current directory. `sks root` tells you which root SKS will use.
|
|
36
53
|
|
|
37
54
|
If you only want a one-shot run without keeping `sks` installed globally:
|
|
@@ -53,6 +70,7 @@ Check that the install is usable:
|
|
|
53
70
|
sks deps check
|
|
54
71
|
sks codex-app check
|
|
55
72
|
sks dollar-commands
|
|
73
|
+
sks commit --json
|
|
56
74
|
sks selftest --mock
|
|
57
75
|
```
|
|
58
76
|
|
|
@@ -60,32 +78,11 @@ sks selftest --mock
|
|
|
60
78
|
|
|
61
79
|
`sks` adds a tmux Codex CLI runtime, Codex App `$` commands, Team/QA/PPT/Research/DB/GX/Wiki routes, OpenClaw skill generation, Context7-gated current docs, TriWiki context packs, DB safety, design SSOT policy, skill dreaming, release checks, and Honest Mode.
|
|
62
80
|
|
|
63
|
-
##
|
|
64
|
-
|
|
65
|
-
Sneakoscope 0.8.0 introduces the RecallPulse planning spine: a report-only active-recall layer that records what the pipeline should remember before a stage proceeds. RecallPulse maps TriWiki into L1/L2/L3 cache behavior, writes durable status ledgers instead of relying on ephemeral hook text, suppresses duplicate reminder loops, and emits RouteProofCapsule plus EvidenceEnvelope artifacts for later gate comparison. These artifacts are evidence surfaces first; speed or accuracy gains remain benchmark-gated until scored evals prove them.
|
|
66
|
-
|
|
67
|
-
Inspect the new report-only artifacts with:
|
|
68
|
-
|
|
69
|
-
```sh
|
|
70
|
-
sks recallpulse run latest
|
|
71
|
-
sks recallpulse status latest --json
|
|
72
|
-
sks recallpulse eval latest --json
|
|
73
|
-
sks recallpulse governance latest --json
|
|
74
|
-
sks recallpulse checklist latest --json
|
|
75
|
-
sks recallpulse checklist latest --task T001 --apply --evidence src/core/recallpulse.mjs
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
Research scouts now use named persona-inspired cognitive lenses: Einstein Scout, Feynman Scout, Turing Scout, von Neumann Scout, and Skeptic Scout. They are not impersonations; each scout ledger row must carry `display_name`, `persona`, `persona_boundary`, `reasoning_effort=xhigh`, a literal `Eureka!` idea, falsifiers, cheap probes, and debate participation evidence.
|
|
79
|
-
|
|
80
|
-
For existing 0.7.x users, the visible change is new report-only evidence, not a route personality rewrite. Team still feels like Team, DFix stays ultralight, DB remains conservative, QA-LOOP still dogfoods, PPT stays information-first, imagegen still requires real raster evidence, and Honest Mode remains the final truth pass. The original strong reminder idea became neutral RecallPulse so user-facing prompts stay short, professional, and non-repetitive; hook messages can point at status, but `mission-status-ledger.json` is the durable source when app-visible text disappears. The planning source is `docs/RECALLPULSE_0_8_0_TASKS.md`, and implementation is designed to land in safe task-sized slices before any enforcement promotion.
|
|
81
|
-
|
|
82
|
-
## 0.9.0 Report-Only Decision Lattice
|
|
83
|
-
|
|
84
|
-
Sneakoscope 0.9.0 adds a report-only Decision Lattice planner that uses A* over proof-debt signals to explain which route or verification path the pipeline would prefer. It is an evidence and planning surface, not a runtime shortcut: SKS must not claim speedup, fast-lane accuracy, or reduced verification cost from the lattice until replay or scored eval evidence demonstrates those outcomes.
|
|
81
|
+
## Report-Only Planning Surfaces
|
|
85
82
|
|
|
86
|
-
|
|
83
|
+
Decision Lattice and RecallPulse remain report-only planning and evidence surfaces. They can explain route choices and proof-debt signals, but SKS does not claim speedup, fast-lane accuracy, or reduced verification cost from them until scored evals prove those outcomes.
|
|
87
84
|
|
|
88
|
-
|
|
85
|
+
Useful checks:
|
|
89
86
|
|
|
90
87
|
```bash
|
|
91
88
|
sks proof-field scan --json --intent "small CLI change"
|
|
@@ -207,7 +204,7 @@ sks codex-lb repair
|
|
|
207
204
|
sks
|
|
208
205
|
```
|
|
209
206
|
|
|
210
|
-
Bare `sks` can also prompt for codex-lb auth; SKS stores the base URL/key in `~/.codex/sks-codex-lb.env`, writes the upstream codex-lb Codex CLI / IDE Extension provider block into `~/.codex/config.toml` for Codex App routing, loads the provider env key for tmux launches, and syncs the macOS user launch environment so the Codex App can see `CODEX_LB_API_KEY` after restart. If the provider block disappears but the stored env file is still recoverable, bare `sks`, npm postinstall upgrades, `sks doctor --fix`, and `sks codex-lb repair` restore it with `env_key = "CODEX_LB_API_KEY"`, `supports_websockets = true`, and `requires_openai_auth = true` as documented by codex-lb. If an older SKS release left the codex-lb dashboard key only in the shared Codex `auth.json` login cache, SKS migrates that key back into `~/.codex/sks-codex-lb.env` when a codex-lb provider or env base URL is already recoverable. It does not rewrite the shared Codex `auth.json` login cache by default; set `SKS_CODEX_LB_SYNC_CODEX_LOGIN=1` only if you intentionally want the old API-key login-cache behavior. When codex-lb is active, SKS opens a fresh `sks-codex-lb-*` tmux session and sweeps older detached codex-lb sessions for the same repo before launch so stale Responses API chains are not reused. Configured launch paths
|
|
207
|
+
Bare `sks` can also prompt for codex-lb auth; SKS stores the base URL/key in `~/.codex/sks-codex-lb.env`, writes the upstream codex-lb Codex CLI / IDE Extension provider block into `~/.codex/config.toml` for Codex App routing, loads the provider env key for tmux launches, and syncs the macOS user launch environment so the Codex App can see `CODEX_LB_API_KEY` after restart. If the provider block disappears but the stored env file is still recoverable, bare `sks`, npm postinstall upgrades, `sks doctor --fix`, and `sks codex-lb repair` restore it with `env_key = "CODEX_LB_API_KEY"`, `supports_websockets = true`, and `requires_openai_auth = true` as documented by codex-lb. If an older SKS release left the codex-lb dashboard key only in the shared Codex `auth.json` login cache, SKS migrates that key back into `~/.codex/sks-codex-lb.env` when a codex-lb provider or env base URL is already recoverable. It does not rewrite the shared Codex `auth.json` login cache by default; set `SKS_CODEX_LB_SYNC_CODEX_LOGIN=1` only if you intentionally want the old API-key login-cache behavior. When codex-lb is active, SKS opens a fresh `sks-codex-lb-*` tmux session and sweeps older detached codex-lb sessions for the same repo before launch so stale Responses API chains are not reused. Configured launch paths run a response-chain health check. `previous_response_not_found` is treated as a stateless-LB warning and keeps codex-lb active. Hard failures are surfaced to the user; SKS only bypasses codex-lb when the user chooses OAuth fallback or `SKS_CODEX_LB_AUTOBYPASS=1` is set.
|
|
211
208
|
|
|
212
209
|
If codex-lb provider auth drifts after launch/reinstall, run `sks doctor --fix` or `sks codex-lb repair`; to replace it, run `sks codex-lb reconfigure --host <domain> --api-key <key>`.
|
|
213
210
|
|
|
@@ -2,6 +2,104 @@
|
|
|
2
2
|
# It is not intended for manual editing.
|
|
3
3
|
version = 4
|
|
4
4
|
|
|
5
|
+
[[package]]
|
|
6
|
+
name = "itoa"
|
|
7
|
+
version = "1.0.18"
|
|
8
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
9
|
+
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
|
|
10
|
+
|
|
11
|
+
[[package]]
|
|
12
|
+
name = "memchr"
|
|
13
|
+
version = "2.8.0"
|
|
14
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
15
|
+
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
|
|
16
|
+
|
|
17
|
+
[[package]]
|
|
18
|
+
name = "proc-macro2"
|
|
19
|
+
version = "1.0.106"
|
|
20
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
21
|
+
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
|
|
22
|
+
dependencies = [
|
|
23
|
+
"unicode-ident",
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[[package]]
|
|
27
|
+
name = "quote"
|
|
28
|
+
version = "1.0.45"
|
|
29
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
30
|
+
checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
|
|
31
|
+
dependencies = [
|
|
32
|
+
"proc-macro2",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[[package]]
|
|
36
|
+
name = "serde"
|
|
37
|
+
version = "1.0.228"
|
|
38
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
39
|
+
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
|
40
|
+
dependencies = [
|
|
41
|
+
"serde_core",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[[package]]
|
|
45
|
+
name = "serde_core"
|
|
46
|
+
version = "1.0.228"
|
|
47
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
48
|
+
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
|
49
|
+
dependencies = [
|
|
50
|
+
"serde_derive",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[[package]]
|
|
54
|
+
name = "serde_derive"
|
|
55
|
+
version = "1.0.228"
|
|
56
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
57
|
+
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
|
58
|
+
dependencies = [
|
|
59
|
+
"proc-macro2",
|
|
60
|
+
"quote",
|
|
61
|
+
"syn",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
[[package]]
|
|
65
|
+
name = "serde_json"
|
|
66
|
+
version = "1.0.149"
|
|
67
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
68
|
+
checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
|
|
69
|
+
dependencies = [
|
|
70
|
+
"itoa",
|
|
71
|
+
"memchr",
|
|
72
|
+
"serde",
|
|
73
|
+
"serde_core",
|
|
74
|
+
"zmij",
|
|
75
|
+
]
|
|
76
|
+
|
|
5
77
|
[[package]]
|
|
6
78
|
name = "sks-core"
|
|
7
|
-
version = "0.9.
|
|
79
|
+
version = "0.9.14"
|
|
80
|
+
dependencies = [
|
|
81
|
+
"serde_json",
|
|
82
|
+
]
|
|
83
|
+
|
|
84
|
+
[[package]]
|
|
85
|
+
name = "syn"
|
|
86
|
+
version = "2.0.117"
|
|
87
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
88
|
+
checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
|
|
89
|
+
dependencies = [
|
|
90
|
+
"proc-macro2",
|
|
91
|
+
"quote",
|
|
92
|
+
"unicode-ident",
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
[[package]]
|
|
96
|
+
name = "unicode-ident"
|
|
97
|
+
version = "1.0.24"
|
|
98
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
99
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
100
|
+
|
|
101
|
+
[[package]]
|
|
102
|
+
name = "zmij"
|
|
103
|
+
version = "1.0.21"
|
|
104
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
105
|
+
checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"
|
|
@@ -4,7 +4,7 @@ use std::io::{self, Read, Seek, SeekFrom};
|
|
|
4
4
|
fn main() {
|
|
5
5
|
let mut args = std::env::args().skip(1);
|
|
6
6
|
match args.next().as_deref() {
|
|
7
|
-
Some("--version") => println!("sks-rs 0.9.
|
|
7
|
+
Some("--version") => println!("sks-rs 0.9.14"),
|
|
8
8
|
Some("compact-info") => {
|
|
9
9
|
let mut input = String::new();
|
|
10
10
|
let _ = io::stdin().read_to_string(&mut input);
|
|
@@ -28,6 +28,36 @@ fn main() {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
|
+
Some("image-hash") => {
|
|
32
|
+
let path = args.next().unwrap_or_default();
|
|
33
|
+
match image_hash(&path) {
|
|
34
|
+
Ok((sha, bytes)) => println!("{{\"ok\":true,\"engine\":\"rust\",\"path\":\"{}\",\"sha256\":\"{}\",\"bytes\":{}}}", json_escape(&path), sha, bytes),
|
|
35
|
+
Err(err) => {
|
|
36
|
+
println!("{{\"ok\":false,\"engine\":\"rust\",\"path\":\"{}\",\"error\":\"{}\"}}", json_escape(&path), json_escape(&err.to_string()));
|
|
37
|
+
std::process::exit(1);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
Some("voxel-validate") => {
|
|
42
|
+
let path = args.next().unwrap_or_default();
|
|
43
|
+
let mut require_anchors = false;
|
|
44
|
+
let mut require_relations = false;
|
|
45
|
+
while let Some(arg) = args.next() {
|
|
46
|
+
if arg == "--require-anchors" { require_anchors = true; }
|
|
47
|
+
if arg == "--require-relations" { require_relations = true; }
|
|
48
|
+
}
|
|
49
|
+
match std::fs::read_to_string(&path) {
|
|
50
|
+
Ok(text) => {
|
|
51
|
+
let report = voxel_validate(&text, require_anchors, require_relations);
|
|
52
|
+
println!("{}", report);
|
|
53
|
+
if report.contains("\"ok\":false") { std::process::exit(1); }
|
|
54
|
+
}
|
|
55
|
+
Err(err) => {
|
|
56
|
+
println!("{{\"ok\":false,\"engine\":\"rust\",\"issues\":[\"read_error\"],\"error\":\"{}\"}}", json_escape(&err.to_string()));
|
|
57
|
+
std::process::exit(1);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
31
61
|
Some("secret-scan") => {
|
|
32
62
|
let path = args.next().unwrap_or_default();
|
|
33
63
|
match std::fs::read_to_string(&path) {
|
|
@@ -45,12 +75,156 @@ fn main() {
|
|
|
45
75
|
}
|
|
46
76
|
}
|
|
47
77
|
_ => {
|
|
48
|
-
eprintln!("sks-rs optional accelerator. Commands: --version, compact-info, jsonl-tail, secret-scan");
|
|
78
|
+
eprintln!("sks-rs optional accelerator. Commands: --version, compact-info, jsonl-tail, secret-scan, image-hash, voxel-validate");
|
|
49
79
|
std::process::exit(2);
|
|
50
80
|
}
|
|
51
81
|
}
|
|
52
82
|
}
|
|
53
83
|
|
|
84
|
+
fn image_hash(path: &str) -> io::Result<(String, u64)> {
|
|
85
|
+
let mut file = File::open(path)?;
|
|
86
|
+
let mut data = Vec::new();
|
|
87
|
+
file.read_to_end(&mut data)?;
|
|
88
|
+
let bytes = data.len() as u64;
|
|
89
|
+
Ok((sha256_hex(&data), bytes))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
fn sha256_hex(data: &[u8]) -> String {
|
|
93
|
+
const K: [u32; 64] = [
|
|
94
|
+
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
|
|
95
|
+
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
|
|
96
|
+
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
|
|
97
|
+
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
|
|
98
|
+
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
|
|
99
|
+
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
|
|
100
|
+
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
|
|
101
|
+
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
|
|
102
|
+
];
|
|
103
|
+
let mut h: [u32; 8] = [0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19];
|
|
104
|
+
let bit_len = (data.len() as u64) * 8;
|
|
105
|
+
let mut msg = data.to_vec();
|
|
106
|
+
msg.push(0x80);
|
|
107
|
+
while (msg.len() % 64) != 56 { msg.push(0); }
|
|
108
|
+
msg.extend_from_slice(&bit_len.to_be_bytes());
|
|
109
|
+
for chunk in msg.chunks(64) {
|
|
110
|
+
let mut w = [0u32; 64];
|
|
111
|
+
for i in 0..16 {
|
|
112
|
+
w[i] = u32::from_be_bytes([chunk[i * 4], chunk[i * 4 + 1], chunk[i * 4 + 2], chunk[i * 4 + 3]]);
|
|
113
|
+
}
|
|
114
|
+
for i in 16..64 {
|
|
115
|
+
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
|
|
116
|
+
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
|
|
117
|
+
w[i] = w[i - 16].wrapping_add(s0).wrapping_add(w[i - 7]).wrapping_add(s1);
|
|
118
|
+
}
|
|
119
|
+
let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh) = (h[0], h[1], h[2], h[3], h[4], h[5], h[6], h[7]);
|
|
120
|
+
for i in 0..64 {
|
|
121
|
+
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
|
|
122
|
+
let ch = (e & f) ^ ((!e) & g);
|
|
123
|
+
let temp1 = hh.wrapping_add(s1).wrapping_add(ch).wrapping_add(K[i]).wrapping_add(w[i]);
|
|
124
|
+
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
|
|
125
|
+
let maj = (a & b) ^ (a & c) ^ (b & c);
|
|
126
|
+
let temp2 = s0.wrapping_add(maj);
|
|
127
|
+
hh = g;
|
|
128
|
+
g = f;
|
|
129
|
+
f = e;
|
|
130
|
+
e = d.wrapping_add(temp1);
|
|
131
|
+
d = c;
|
|
132
|
+
c = b;
|
|
133
|
+
b = a;
|
|
134
|
+
a = temp1.wrapping_add(temp2);
|
|
135
|
+
}
|
|
136
|
+
h[0] = h[0].wrapping_add(a);
|
|
137
|
+
h[1] = h[1].wrapping_add(b);
|
|
138
|
+
h[2] = h[2].wrapping_add(c);
|
|
139
|
+
h[3] = h[3].wrapping_add(d);
|
|
140
|
+
h[4] = h[4].wrapping_add(e);
|
|
141
|
+
h[5] = h[5].wrapping_add(f);
|
|
142
|
+
h[6] = h[6].wrapping_add(g);
|
|
143
|
+
h[7] = h[7].wrapping_add(hh);
|
|
144
|
+
}
|
|
145
|
+
h.iter().map(|x| format!("{:08x}", x)).collect::<String>()
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn voxel_validate(text: &str, require_anchors: bool, require_relations: bool) -> String {
|
|
149
|
+
let parsed: serde_json::Value = match serde_json::from_str(text) {
|
|
150
|
+
Ok(value) => value,
|
|
151
|
+
Err(err) => {
|
|
152
|
+
return format!("{{\"ok\":false,\"engine\":\"rust\",\"schema\":\"sks.image-voxel-ledger.v1\",\"images\":0,\"anchors\":0,\"relations\":0,\"issues\":[\"json_parse\"],\"error\":\"{}\"}}", json_escape(&err.to_string()));
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
let mut issues: Vec<String> = Vec::new();
|
|
156
|
+
if parsed.get("schema").and_then(|v| v.as_str()) != Some("sks.image-voxel-ledger.v1") { issues.push("schema".to_string()); }
|
|
157
|
+
let images = parsed.get("images").and_then(|v| v.as_array()).cloned().unwrap_or_default();
|
|
158
|
+
let anchors = parsed.get("anchors").and_then(|v| v.as_array()).cloned().unwrap_or_default();
|
|
159
|
+
let relations = parsed.get("relations").and_then(|v| v.as_array()).cloned().unwrap_or_default();
|
|
160
|
+
if !parsed.get("images").map(|v| v.is_array()).unwrap_or(false) { issues.push("missing_images".to_string()); }
|
|
161
|
+
if !parsed.get("anchors").map(|v| v.is_array()).unwrap_or(false) { issues.push("missing_anchors".to_string()); }
|
|
162
|
+
if require_anchors && anchors.is_empty() { issues.push("missing_anchors:visual-route".to_string()); }
|
|
163
|
+
if require_relations && relations.is_empty() { issues.push("missing_relations:visual-route".to_string()); }
|
|
164
|
+
let mut image_ids: Vec<String> = Vec::new();
|
|
165
|
+
let mut anchor_ids: Vec<String> = Vec::new();
|
|
166
|
+
for image in &images {
|
|
167
|
+
let id = image.get("id").and_then(|v| v.as_str()).unwrap_or("");
|
|
168
|
+
if id.is_empty() { issues.push("image_id".to_string()); }
|
|
169
|
+
if !id.is_empty() && image_ids.iter().any(|x| x == id) { issues.push(format!("duplicate_image:{}", id)); }
|
|
170
|
+
if !id.is_empty() { image_ids.push(id.to_string()); }
|
|
171
|
+
if image.get("path").and_then(|v| v.as_str()).unwrap_or("").is_empty() { issues.push(format!("image_path:{}", if id.is_empty() { "unknown" } else { id })); }
|
|
172
|
+
if image.get("sha256").and_then(|v| v.as_str()).unwrap_or("").is_empty() { issues.push(format!("image_sha256:{}", if id.is_empty() { "unknown" } else { id })); }
|
|
173
|
+
let w = image.get("width").and_then(|v| v.as_f64());
|
|
174
|
+
let h = image.get("height").and_then(|v| v.as_f64());
|
|
175
|
+
if !w.map(|n| n.is_finite()).unwrap_or(false) || !h.map(|n| n.is_finite()).unwrap_or(false) {
|
|
176
|
+
issues.push(format!("image_dimensions:{}", if id.is_empty() { "unknown" } else { id }));
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
for anchor in &anchors {
|
|
180
|
+
let id = anchor.get("id").and_then(|v| v.as_str()).unwrap_or("");
|
|
181
|
+
if id.is_empty() { issues.push("anchor_id".to_string()); }
|
|
182
|
+
if !id.is_empty() && anchor_ids.iter().any(|x| x == id) { issues.push(format!("duplicate_anchor:{}", id)); }
|
|
183
|
+
if !id.is_empty() { anchor_ids.push(id.to_string()); }
|
|
184
|
+
let image_id = anchor.get("image_id").and_then(|v| v.as_str()).unwrap_or("");
|
|
185
|
+
if image_id.is_empty() || !image_ids.iter().any(|x| x == image_id) { issues.push(format!("anchor_image_ref:{}", if id.is_empty() { "unknown" } else { id })); }
|
|
186
|
+
let image = images.iter().find(|img| img.get("id").and_then(|v| v.as_str()) == Some(image_id));
|
|
187
|
+
let w = image.and_then(|img| img.get("width")).and_then(|v| v.as_f64()).unwrap_or(f64::NAN);
|
|
188
|
+
let h = image.and_then(|img| img.get("height")).and_then(|v| v.as_f64()).unwrap_or(f64::NAN);
|
|
189
|
+
match anchor.get("bbox").and_then(|v| v.as_array()) {
|
|
190
|
+
Some(bbox) if bbox.len() == 4 => {
|
|
191
|
+
let vals: Vec<f64> = bbox.iter().map(|v| v.as_f64().unwrap_or(f64::NAN)).collect();
|
|
192
|
+
if vals.iter().any(|n| !n.is_finite()) { issues.push(format!("bbox_number:{}", if id.is_empty() { "unknown" } else { id })); }
|
|
193
|
+
if vals[2] <= 0.0 || vals[3] <= 0.0 { issues.push(format!("bbox_positive:{}", if id.is_empty() { "unknown" } else { id })); }
|
|
194
|
+
if w.is_finite() && vals[0] + vals[2] > w {
|
|
195
|
+
issues.push(format!("bbox_width_out_of_bounds:{}", if id.is_empty() { "unknown" } else { id }));
|
|
196
|
+
}
|
|
197
|
+
if h.is_finite() && vals[1] + vals[3] > h {
|
|
198
|
+
issues.push(format!("bbox_height_out_of_bounds:{}", if id.is_empty() { "unknown" } else { id }));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
Some(_) => issues.push(format!("bbox_shape:{}", if id.is_empty() { "unknown" } else { id })),
|
|
202
|
+
None => issues.push(format!("anchor_bbox:{}", if id.is_empty() { "unknown" } else { id })),
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
for relation in &relations {
|
|
206
|
+
if let Some(before) = relation.get("before_image_id").and_then(|v| v.as_str()) {
|
|
207
|
+
if !image_ids.iter().any(|x| x == before) { issues.push(format!("relation_before:{}", before)); }
|
|
208
|
+
}
|
|
209
|
+
if let Some(after) = relation.get("after_image_id").and_then(|v| v.as_str()) {
|
|
210
|
+
if !image_ids.iter().any(|x| x == after) { issues.push(format!("relation_after:{}", after)); }
|
|
211
|
+
}
|
|
212
|
+
let changed = relation.get("changed_anchor_ids").or_else(|| relation.get("anchors")).and_then(|v| v.as_array()).cloned().unwrap_or_default();
|
|
213
|
+
for anchor_id in changed.iter().filter_map(|v| v.as_str()) {
|
|
214
|
+
if !anchor_ids.iter().any(|x| x == anchor_id) { issues.push(format!("relation_anchor:{}", anchor_id)); }
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
issues.sort();
|
|
218
|
+
issues.dedup();
|
|
219
|
+
let ok = issues.is_empty();
|
|
220
|
+
let issue_json = issues.iter().map(|x| format!("\"{}\"", json_escape(x))).collect::<Vec<_>>().join(",");
|
|
221
|
+
format!("{{\"ok\":{},\"engine\":\"rust\",\"schema\":\"sks.image-voxel-ledger.v1\",\"images\":{},\"anchors\":{},\"relations\":{},\"issues\":[{}]}}", if ok { "true" } else { "false" }, images.len(), anchors.len(), relations.len(), issue_json)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
fn json_escape(value: &str) -> String {
|
|
225
|
+
value.replace('\\', "\\\\").replace('"', "\\\"").replace('\n', "\\n").replace('\r', "\\r")
|
|
226
|
+
}
|
|
227
|
+
|
|
54
228
|
fn tail_file(path: &str, bytes: u64) -> io::Result<String> {
|
|
55
229
|
let mut file = File::open(path)?;
|
|
56
230
|
let len = file.metadata()?.len();
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sneakoscope",
|
|
3
3
|
"displayName": "ㅅㅋㅅ",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.14",
|
|
5
5
|
"description": "Sneakoscope Codex: fast proof-first Codex trust layer with image-based Voxel TriWiki.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"homepage": "https://github.com/mandarange/Sneakoscope-Codex#readme",
|
|
@@ -37,20 +37,25 @@
|
|
|
37
37
|
"postinstall": "node ./bin/sks.mjs postinstall",
|
|
38
38
|
"selftest": "node ./bin/sks.mjs selftest --mock",
|
|
39
39
|
"doctor": "node ./bin/sks.mjs doctor",
|
|
40
|
-
"packcheck": "find bin src scripts -name '*.mjs' -print0 | xargs -0 -n1 node --check",
|
|
40
|
+
"packcheck": "find bin src scripts test -name '*.mjs' -print0 | xargs -0 -n1 node --check",
|
|
41
41
|
"changelog:check": "node ./scripts/changelog-check.mjs",
|
|
42
42
|
"cli-entrypoint:check": "node ./scripts/check-cli-entrypoint.mjs",
|
|
43
|
+
"legacy-free:check": "node ./scripts/check-legacy-free.mjs",
|
|
43
44
|
"sizecheck": "node ./scripts/sizecheck.mjs",
|
|
44
45
|
"registry:check": "node ./scripts/release-registry-check.mjs",
|
|
45
46
|
"feature:check": "node ./bin/sks.mjs features check --json",
|
|
46
47
|
"all-features:selftest": "node ./bin/sks.mjs all-features selftest --mock --json",
|
|
48
|
+
"all-features:execute-fixtures": "node ./bin/sks.mjs all-features selftest --mock --execute-fixtures --strict-artifacts --json",
|
|
47
49
|
"perf:cold-start": "node ./bin/sks.mjs perf cold-start --json",
|
|
48
50
|
"perf:gate": "node ./scripts/perf-gate.mjs",
|
|
49
51
|
"test": "node --test \"test/**/*.test.mjs\"",
|
|
50
52
|
"test:unit": "node --test \"test/unit/**/*.test.mjs\"",
|
|
51
53
|
"test:integration:mock": "node --test \"test/integration/**/*.test.mjs\"",
|
|
54
|
+
"test:e2e:mock": "node --test \"test/e2e/**/*.test.mjs\"",
|
|
55
|
+
"rust:check": "cargo check --manifest-path crates/sks-core/Cargo.toml",
|
|
56
|
+
"rust:smoke": "node ./scripts/rust-smoke.mjs",
|
|
52
57
|
"coverage": "node --experimental-test-coverage --test \"test/**/*.test.mjs\"",
|
|
53
|
-
"release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run packcheck && npm run feature:check && npm run all-features:selftest && npm run selftest && npm run test:unit && npm run test:integration:mock && npm run perf:gate && npm run sizecheck && npm run registry:check",
|
|
58
|
+
"release:check": "npm run repo-audit && npm run changelog:check && npm run cli-entrypoint:check && npm run legacy-free:check && npm run packcheck && npm run feature:check && npm run all-features:selftest && npm run all-features:execute-fixtures && npm run selftest && npm run test:unit && npm run test:integration:mock && npm run test:e2e:mock && npm run rust:check && npm run rust:smoke && npm run perf:gate && npm run sizecheck && npm run registry:check",
|
|
54
59
|
"publish:dry": "npm run release:check && npm --cache /tmp/sks-npm-cache publish --dry-run --registry https://registry.npmjs.org/ --access public",
|
|
55
60
|
"publish:npm": "npm --cache /tmp/sks-npm-cache publish --registry https://registry.npmjs.org/ --access public",
|
|
56
61
|
"prepublishOnly": "npm run release:check && node ./scripts/release-registry-check.mjs --require-unpublished"
|