@wenathlan/saddle 2.0.167 → 2.0.168
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/Dockerfile +22 -16
- package/README.md +1 -1
- package/docs/architecture.md +4 -4
- package/docs/e2ughengine.md +4 -4
- package/modalities.ts +99 -22
- package/package.json +43 -46
- package/workloadlaw.ts +337 -38
package/Dockerfile
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# saddle 2.0.
|
|
1
|
+
# saddle 2.0.168 — THE ENGINE CONTAINER FILE
|
|
2
2
|
#
|
|
3
3
|
# THE FAMILY CONTAINER DOCTRINE, THE SADDLE SHAPE: one versioned container
|
|
4
4
|
# per engine, built like the site homes (argan, cadria) build theirs — a
|
|
@@ -158,7 +158,7 @@ ARG NODE_BASE_DIGEST=5adad64f9a1fca2d51972caa65983634c8a774eb300d72b20f7f2a0f603
|
|
|
158
158
|
# ===== stage: solbuild — the locked npm tree and the console spa =====
|
|
159
159
|
FROM --platform=$BUILDPLATFORM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS solbuild
|
|
160
160
|
|
|
161
|
-
ARG SADDLE_VERSION=2.0.
|
|
161
|
+
ARG SADDLE_VERSION=2.0.168
|
|
162
162
|
|
|
163
163
|
WORKDIR /app
|
|
164
164
|
|
|
@@ -172,6 +172,12 @@ COPY saddle/ .
|
|
|
172
172
|
# so the vault sources ride the context beside the saddle and land at
|
|
173
173
|
# /vault where the import resolves (the family context fix).
|
|
174
174
|
COPY vault/ /vault
|
|
175
|
+
# the routerbase sibling of the family law: saddle/routerbase.ts
|
|
176
|
+
# re-exports the family routerBase from ../devthink/routerbase.ts, so
|
|
177
|
+
# the devthink sources land at /devthink where the sol build resolves
|
|
178
|
+
# the re-export (sources only - the same never-installed law the vault
|
|
179
|
+
# sibling above rides, the family context fix the lanes carry).
|
|
180
|
+
COPY devthink/ /devthink
|
|
175
181
|
RUN npm run web:build:pages \
|
|
176
182
|
# the theme build emits the hashed SPA bundles straight at the app
|
|
177
183
|
# root (the family static-first vite contract): stage them into a
|
|
@@ -186,7 +192,7 @@ RUN npm run web:build:pages \
|
|
|
186
192
|
# ===== stage: base — the hardened runtime floor every profile shares =====
|
|
187
193
|
FROM ${NODE_BASE_TAG}@sha256:${NODE_BASE_DIGEST} AS base
|
|
188
194
|
|
|
189
|
-
ARG SADDLE_VERSION=2.0.
|
|
195
|
+
ARG SADDLE_VERSION=2.0.168
|
|
190
196
|
# the revision stamp the devthink container carries (DEVTHINK_REVISION
|
|
191
197
|
# there): the publish legs pass the release sha at build time and the
|
|
192
198
|
# revision label below declares the default-unknown local shape.
|
|
@@ -489,7 +495,7 @@ LABEL org.opencontainers.image.title="saddle (the android os-distribution lane)"
|
|
|
489
495
|
# docker build --target osv -t saddle:2.0.165-v .
|
|
490
496
|
# docker build --target oswindows -t saddle:2.0.165-windows .
|
|
491
497
|
FROM scratch AS osgrapheneos
|
|
492
|
-
ARG SADDLE_VERSION=2.0.
|
|
498
|
+
ARG SADDLE_VERSION=2.0.168
|
|
493
499
|
ARG SADDLE_OS_PROFILE="grapheneos"
|
|
494
500
|
ENV SADDLE_OS_PROFILE=grapheneos
|
|
495
501
|
LABEL org.opencontainers.image.title="saddle (the grapheneos os-distribution catalog lane)" \
|
|
@@ -500,7 +506,7 @@ LABEL org.opencontainers.image.title="saddle (the grapheneos os-distribution cat
|
|
|
500
506
|
COPY osprofiles/grapheneos.json /saddle/osprofile.json
|
|
501
507
|
|
|
502
508
|
FROM scratch AS osmaruos
|
|
503
|
-
ARG SADDLE_VERSION=2.0.
|
|
509
|
+
ARG SADDLE_VERSION=2.0.168
|
|
504
510
|
ARG SADDLE_OS_PROFILE="maruos"
|
|
505
511
|
ENV SADDLE_OS_PROFILE=maruos
|
|
506
512
|
LABEL org.opencontainers.image.title="saddle (the maru os os-distribution catalog lane)" \
|
|
@@ -511,7 +517,7 @@ LABEL org.opencontainers.image.title="saddle (the maru os os-distribution catalo
|
|
|
511
517
|
COPY osprofiles/maruos.json /saddle/osprofile.json
|
|
512
518
|
|
|
513
519
|
FROM scratch AS osv
|
|
514
|
-
ARG SADDLE_VERSION=2.0.
|
|
520
|
+
ARG SADDLE_VERSION=2.0.168
|
|
515
521
|
ARG SADDLE_OS_PROFILE="v"
|
|
516
522
|
ENV SADDLE_OS_PROFILE=v
|
|
517
523
|
LABEL org.opencontainers.image.title="saddle (the v os-distribution catalog lane)" \
|
|
@@ -522,7 +528,7 @@ LABEL org.opencontainers.image.title="saddle (the v os-distribution catalog lane
|
|
|
522
528
|
COPY osprofiles/v.json /saddle/osprofile.json
|
|
523
529
|
|
|
524
530
|
FROM scratch AS oswindows
|
|
525
|
-
ARG SADDLE_VERSION=2.0.
|
|
531
|
+
ARG SADDLE_VERSION=2.0.168
|
|
526
532
|
ARG SADDLE_OS_PROFILE="windows"
|
|
527
533
|
ENV SADDLE_OS_PROFILE=windows
|
|
528
534
|
LABEL org.opencontainers.image.title="saddle (the windows os-distribution catalog lane)" \
|
|
@@ -533,7 +539,7 @@ LABEL org.opencontainers.image.title="saddle (the windows os-distribution catalo
|
|
|
533
539
|
COPY osprofiles/windows.json /saddle/osprofile.json
|
|
534
540
|
|
|
535
541
|
FROM scratch AS ospostmarketos
|
|
536
|
-
ARG SADDLE_VERSION=2.0.
|
|
542
|
+
ARG SADDLE_VERSION=2.0.168
|
|
537
543
|
ARG SADDLE_OS_PROFILE="postmarketos"
|
|
538
544
|
ENV SADDLE_OS_PROFILE=postmarketos
|
|
539
545
|
LABEL org.opencontainers.image.title="saddle (the postmarketos os-distribution catalog lane)" \
|
|
@@ -544,7 +550,7 @@ LABEL org.opencontainers.image.title="saddle (the postmarketos os-distribution c
|
|
|
544
550
|
COPY osprofiles/postmarketos.json /saddle/osprofile.json
|
|
545
551
|
|
|
546
552
|
FROM scratch AS oslineageos
|
|
547
|
-
ARG SADDLE_VERSION=2.0.
|
|
553
|
+
ARG SADDLE_VERSION=2.0.168
|
|
548
554
|
ARG SADDLE_OS_PROFILE="lineageos"
|
|
549
555
|
ENV SADDLE_OS_PROFILE=lineageos
|
|
550
556
|
LABEL org.opencontainers.image.title="saddle (the lineageos os-distribution catalog lane)" \
|
|
@@ -555,7 +561,7 @@ LABEL org.opencontainers.image.title="saddle (the lineageos os-distribution cata
|
|
|
555
561
|
COPY osprofiles/lineageos.json /saddle/osprofile.json
|
|
556
562
|
|
|
557
563
|
FROM scratch AS oscalyxos
|
|
558
|
-
ARG SADDLE_VERSION=2.0.
|
|
564
|
+
ARG SADDLE_VERSION=2.0.168
|
|
559
565
|
ARG SADDLE_OS_PROFILE="calyxos"
|
|
560
566
|
ENV SADDLE_OS_PROFILE=calyxos
|
|
561
567
|
LABEL org.opencontainers.image.title="saddle (the calyxos os-distribution catalog lane)" \
|
|
@@ -566,7 +572,7 @@ LABEL org.opencontainers.image.title="saddle (the calyxos os-distribution catalo
|
|
|
566
572
|
COPY osprofiles/calyxos.json /saddle/osprofile.json
|
|
567
573
|
|
|
568
574
|
FROM scratch AS osarchlinux
|
|
569
|
-
ARG SADDLE_VERSION=2.0.
|
|
575
|
+
ARG SADDLE_VERSION=2.0.168
|
|
570
576
|
ARG SADDLE_OS_PROFILE="archlinux"
|
|
571
577
|
ENV SADDLE_OS_PROFILE=archlinux
|
|
572
578
|
LABEL org.opencontainers.image.title="saddle (the archlinux os-distribution catalog lane)" \
|
|
@@ -577,7 +583,7 @@ LABEL org.opencontainers.image.title="saddle (the archlinux os-distribution cata
|
|
|
577
583
|
COPY osprofiles/archlinux.json /saddle/osprofile.json
|
|
578
584
|
|
|
579
585
|
FROM scratch AS osalpine
|
|
580
|
-
ARG SADDLE_VERSION=2.0.
|
|
586
|
+
ARG SADDLE_VERSION=2.0.168
|
|
581
587
|
ARG SADDLE_OS_PROFILE="alpine"
|
|
582
588
|
ENV SADDLE_OS_PROFILE=alpine
|
|
583
589
|
LABEL org.opencontainers.image.title="saddle (the alpine os-distribution catalog lane)" \
|
|
@@ -588,7 +594,7 @@ LABEL org.opencontainers.image.title="saddle (the alpine os-distribution catalog
|
|
|
588
594
|
COPY osprofiles/alpine.json /saddle/osprofile.json
|
|
589
595
|
|
|
590
596
|
FROM scratch AS osdebian
|
|
591
|
-
ARG SADDLE_VERSION=2.0.
|
|
597
|
+
ARG SADDLE_VERSION=2.0.168
|
|
592
598
|
ARG SADDLE_OS_PROFILE="debian"
|
|
593
599
|
ENV SADDLE_OS_PROFILE=debian
|
|
594
600
|
LABEL org.opencontainers.image.title="saddle (the debian os-distribution catalog lane)" \
|
|
@@ -599,7 +605,7 @@ LABEL org.opencontainers.image.title="saddle (the debian os-distribution catalog
|
|
|
599
605
|
COPY osprofiles/debian.json /saddle/osprofile.json
|
|
600
606
|
|
|
601
607
|
FROM scratch AS osfedora
|
|
602
|
-
ARG SADDLE_VERSION=2.0.
|
|
608
|
+
ARG SADDLE_VERSION=2.0.168
|
|
603
609
|
ARG SADDLE_OS_PROFILE="fedora"
|
|
604
610
|
ENV SADDLE_OS_PROFILE=fedora
|
|
605
611
|
LABEL org.opencontainers.image.title="saddle (the fedora os-distribution catalog lane)" \
|
|
@@ -610,7 +616,7 @@ LABEL org.opencontainers.image.title="saddle (the fedora os-distribution catalog
|
|
|
610
616
|
COPY osprofiles/fedora.json /saddle/osprofile.json
|
|
611
617
|
|
|
612
618
|
FROM scratch AS osnixos
|
|
613
|
-
ARG SADDLE_VERSION=2.0.
|
|
619
|
+
ARG SADDLE_VERSION=2.0.168
|
|
614
620
|
ARG SADDLE_OS_PROFILE="nixos"
|
|
615
621
|
ENV SADDLE_OS_PROFILE=nixos
|
|
616
622
|
LABEL org.opencontainers.image.title="saddle (the nixos os-distribution catalog lane)" \
|
|
@@ -621,7 +627,7 @@ LABEL org.opencontainers.image.title="saddle (the nixos os-distribution catalog
|
|
|
621
627
|
COPY osprofiles/nixos.json /saddle/osprofile.json
|
|
622
628
|
|
|
623
629
|
FROM scratch AS osvoid
|
|
624
|
-
ARG SADDLE_VERSION=2.0.
|
|
630
|
+
ARG SADDLE_VERSION=2.0.168
|
|
625
631
|
ARG SADDLE_OS_PROFILE="void"
|
|
626
632
|
ENV SADDLE_OS_PROFILE=void
|
|
627
633
|
LABEL org.opencontainers.image.title="saddle (the void os-distribution catalog lane)" \
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ domain modules land beside the saddle domains:
|
|
|
31
31
|
| `media.ts`, `render.ts`, `quantum.ts`, `tiers.ts` | media pipeline, GPU registry, quantum simulation, tier policy |
|
|
32
32
|
| `security.ts`, `alternatives.ts` | hardening gates and the alternative-stack catalog |
|
|
33
33
|
| `index.html`, `App.tsx`, `sol/` | the single SPA shell and the Sol theme (one tsx app with one stylesheet per theme) |
|
|
34
|
-
| `server.ts`, `sandbox.ts` | the zero-dependency self-hosted API of the 2.0.
|
|
34
|
+
| `server.ts`, `sandbox.ts` | the zero-dependency self-hosted API of the 2.0.168 grand-merge envelope (`node server.ts`; the api answers its version at `/api/v1/health`) and the browser-pure engine port |
|
|
35
35
|
| `vercel.json`, `netlify.toml` | the platform deploy adapters beside the surface they publish (the caddy ghost stays out of the tree) |
|
|
36
36
|
| `specs/*` package exports | processors, gpus, cores, boards and the qemu/mttg/passage/docker envelopes |
|
|
37
37
|
|
package/docs/architecture.md
CHANGED
|
@@ -121,9 +121,9 @@ The remaining categories of the old file were already domain-owned: kernel featu
|
|
|
121
121
|
|
|
122
122
|
## 3b. Test patterns
|
|
123
123
|
|
|
124
|
-
Tests live in the `test/` directory (one of the five allowed directories) and run
|
|
124
|
+
Tests live in the `test/` directory (one of the five allowed directories) and run on vitest: `vitest run` reads `test/*.test.ts` (153 tests across eleven files - engine, features, media, render, simulation, specs, virtualcpu, virtualgpu, virtualmemory, web, workflow), the same runner the ci.yml family suite executes through the app's `npm test`. Patterns:
|
|
125
125
|
|
|
126
|
-
-
|
|
126
|
+
- vitest (`describe`/`it`) plus `node:assert/strict`; no Jest, no node:test (vitest is the only CI runner), no install step beyond the dev tooling (the engine has zero runtime dependencies).
|
|
127
127
|
- One test file per area under test (planner and lint rules, cpuinfo/meminfo generators, MIG density validator, QEMU argv builder); assertions target public exports, never module internals.
|
|
128
128
|
- Doc-tests remain `@example` blocks in JSDoc (the planner asserts in `index.ts`), and every test stays deterministic: ports use the 30000-59999 random band or explicit ephemeral allocation, no network, no timing-dependent assertions.
|
|
129
129
|
|
|
@@ -266,7 +266,7 @@ Supporting subsystems: warm pool (prewarm/acquire/release/refill/drain, MAP_PRIV
|
|
|
266
266
|
| TypeScript | 7.0.2 (Go-native port); `strict`, `verbatimModuleSyntax`, `isolatedModules`, `erasableSyntaxOnly`, `exactOptionalPropertyTypes`, `noUncheckedIndexedAccess`; ES2025 target with `lib ES2025+ESNext.Disposable` |
|
|
267
267
|
| Erasable modern syntax | `using`/`await using` (modellease, MetricsStore, sandboxhandle), `satisfies` (`engineversions as const satisfies Record<string,string>`), const type parameters (`validateSpec<const T>`), `#private` fields and accessors, `import.meta.filename` for CLI detection |
|
|
268
268
|
| ESM | `"type": "module"`; relative imports carry the `.ts` extension; no CommonJS `require` |
|
|
269
|
-
| Tests |
|
|
269
|
+
| Tests | vitest + `node:assert/strict` executed with `vitest run` over `test/*.test.ts`; the dedicated `test/` directory holds eleven test files (148 cases: 86 planner/generator originals, 24 workflow/simulation gates, 18 feature-audit builders, 4 media pipeline, 1 snapshot regression, 9 web edition), ci.yml runs them through the app's `npm test`; no Jest; no node:test; doc-tests as `@example` blocks (the planner asserts live in `index.ts` planner docs) |
|
|
270
270
|
| C++26 | `import std;` permitted, `std::expected` as the error channel, `std::mdspan` with the p2662r3 fallback guard, `std::print`/`std::format`, `enum class`, `[[nodiscard]]` on validate/argv/topology, RAII for every fd/socket (no exceptions in happy paths) |
|
|
271
271
|
| C | C11 with `_GNU_SOURCE`, `dlsym(RTLD_NEXT)` resolution, xorshift32 deterministic drift, constructor priority 101 |
|
|
272
272
|
| Python | 3.14 floor; stdlib only (socket, json, logging); try/except logging on every operation |
|
|
@@ -318,7 +318,7 @@ The old pins survive only inside `legacyCatalog` provenance blocks in `virtualha
|
|
|
318
318
|
|
|
319
319
|
## 15. CI graph (summary)
|
|
320
320
|
|
|
321
|
-
Five workflows gate the tree: ci.yml (lint, typecheck matrix Node 24/26, the
|
|
321
|
+
Five workflows gate the tree: ci.yml (lint, typecheck matrix Node 24/26, the vitest suite in `test/`, build, flat-structure md5 guard, JSON schema validation of the ten data files), publishghcr.yml (the combined container pipeline: per-arch validation legs on push - clinfo/vulkaninfo/glxinfo probes inside the image - and the four-arch GHCR publication with provenance+SBOM on release), release.yml (GHCR + npm provenance + level-9 zip with SHA-256), pages.yml (the merged web pipeline: module syntax gate, boot smoke on port 37810, html lint, the webapi auth/mesh suite with a throwaway `E2UGH_DB`, the zip -9 static bundle and the GitHub Pages deployment of the web tree) and security.yml (CodeQL C/C++/TS, dependency review, Dockle/Trivy SARIF, gitleaks, Scorecard, Biome security group, license allowlist). Full descriptions and pins are in `readme.md`; the workflows themselves are the executable contract.
|
|
322
322
|
|
|
323
323
|
## 16. Compliance checklist
|
|
324
324
|
|
package/docs/e2ughengine.md
CHANGED
|
@@ -220,7 +220,7 @@ The CI pipeline enforces the flat contract: one concern per file with 20-25 rela
|
|
|
220
220
|
| 42 | `docs/viability.md` | the proof, research methodology, 145-entry bibliography and roadmap appendices A-C |
|
|
221
221
|
| 43 | `docs/alternatives.md` | ten alternative sandbox families compared against the engine |
|
|
222
222
|
| 43b | `docs/optimization.md` | the build-pipeline optimization roadmap (cross-compilation, prebuilt QEMU/Mesa layers, runner strategy) for the slow multi-arch grid |
|
|
223
|
-
| 44 | `test/` |
|
|
223
|
+
| 44 | `test/` | vitest suite executed by ci.yml (`npm test`, `vitest run` over `test/*.test.ts`; 153 cases, including CI-gate replay and end-to-end simulation suites) |
|
|
224
224
|
| 45 | `web/` | the web node (v7): `index.html` terminal sandbox, `login.html`/`register.html`/`dashboard.html` auth pages, `sandbox.js` browser-pure engine port (20 commands), `localauth.js` static-edge browser accounts (login/register/dashboard keep working on github pages with no api; accounts are mirrored to IndexedDB so partial clears never lose them, the dashboard carries backup/restore keyfile buttons, and the CODEOWNERS admins iakadion + akadion are seeded automatically with the shared bootstrap password - surviving any storage reset), `server.js` self-hosted node:http API (`/api/v1` sandboxes and exec), `db.js` sqlite persistence layer, `auth.js` scrypt password hashing with cookie sessions, `mesh.js` HMAC-signed node-to-node calls, `package.json`/`Dockerfile` (image `ghcr.io/wenathlan/e2ugh`; node pinned by the root `.nvmrc`), `schema.prisma`/`init.sql` database definition kept flat per the two-level contract, `mime.types`, `vercel.json`/`netlify.toml` static deploy without functions, `caddyfile` self-host proxy for devthink.pro, `readme.md` web docs |
|
|
225
225
|
| 46 | `LICENSE` | full MIT text (Copyright (c) 2026 wenathlan) |
|
|
226
226
|
|
|
@@ -327,7 +327,7 @@ docker run --rm -p 37810:37810 -e E2UGH_DB=/data/e2ugh.db -v e2ughdata:/data \
|
|
|
327
327
|
|
|
328
328
|
Five workflows gate the repository, all pinned to action releases verified on 2026-08-22:
|
|
329
329
|
|
|
330
|
-
- **ci.yml** (every push and pull request) - six gates: Biome 2.5.11 lint and format; `tsc --noEmit` with TypeScript 7.0.2 on a Node 24/26 matrix; the
|
|
330
|
+
- **ci.yml** (every push and pull request) - six gates: Biome 2.5.11 lint and format; `tsc --noEmit` with TypeScript 7.0.2 on a Node 24/26 matrix; the vitest runner over the dedicated `test/` suite (153 test cases); a build gate; the flat-structure contract (flat tree, no deep nesting, no duplicated content via md5); strict JSON schema validation of every tracked data file.
|
|
331
331
|
- **hardware variants** (publishghcr.yml matrix) - one dockerfile, three curated profiles baked at build time so operators pull exactly the flavor they need: `ghcr.io/wenathlan/e2ugh:latest` (max: EPYC 9965 192c/384t, 1 TiB plan, 8x B200), `:balanced` (Threadripper PRO 9995WX 96c/192t, 512 GiB, 2x B200) and `:lite` (Ryzen 9 9950X3D 16c/32t, 128 GiB, 1x RTX 5090); every profile tag also carries semver and sha suffixes, and the runtime overlay (`VHE_TOTALRAM_GB`, `VHE_CPUS`, `VHE_GPU_PROFILE` environment variables) still lets any variant report any catalog identity up to 18 TiB at run time - the baked profile is the default identity, never a ceiling.
|
|
332
332
|
- **publishghcr.yml** (the combined container pipeline; main, tags and releases) - per-architecture validation legs (`linux/amd64` native; `linux/arm64`, `linux/ppc64le` and `linux/s390x` under QEMU user emulation) that validate the live stack inside the built image: `clinfo` must report Rusticl OpenCL 3.1 on llvmpipe, `vulkaninfo --summary` must find lavapipe Vulkan 1.4, and Xvfb + `glxinfo` must report llvmpipe OpenGL 4.6, executed with `--memory-swap=-1 --shm-size=2g`. On releases, production builds push per-arch images by digest (the max profile ships the four-arch registry surface: amd64, arm64, ppc64le, s390x) with provenance `mode=max` and SBOM attestations before `docker buildx imagetools` assembles the per-profile manifest indexes.
|
|
333
333
|
- **release.yml** (version tags and releases) - three publications: the multi-arch GHCR image, the npm package with `--provenance --access public` (zero runtime dependencies), and a maximum-compression (level 9) source zip with SHA-256 checksums.
|
|
@@ -384,7 +384,7 @@ The test contract is the Node built-in runner: no Jest, no third-party harness,
|
|
|
384
384
|
# from a checkout (Node 24+)
|
|
385
385
|
npm run typecheck # tsc --noEmit, the TypeScript 7.0.2 gate
|
|
386
386
|
npm run lint # Biome 2.5.11
|
|
387
|
-
|
|
387
|
+
npm test # the vitest suite (153 tests) - vitest run over test/*.test.ts, the form ci.yml uses
|
|
388
388
|
|
|
389
389
|
# from the published image (GHCR, linux/amd64 and linux/arm64)
|
|
390
390
|
docker run --rm --memory-swap=-1 --shm-size=2g \
|
|
@@ -424,7 +424,7 @@ The `test/` suite (Node 26.7.0 in CI, Node 24+ locally, `node:assert/strict`, 15
|
|
|
424
424
|
| SADDLE v5 / v6 (Meta TS) | 2026-08-22 wave | `src_core_orchestrator.ts` (2080 lines) and `coreorchestrator.ts` (1283 lines): RBAC, NUMA scheduler, balloon, SR-IOV, passage protocols, quotas, preemption, leader election, OTel exporter; plus `pipelinememorypassage.ts` host-aware memory planning | Unique subsystems absorbed into `orchestrator.ts` (5238 lines) and `virtualmemory.ts`; duplicated buses/state machines/metrics collapsed into one of each |
|
|
425
425
|
| Virtual Hardware Engine v5 (e2ugh) | 2026-08-22, English | The 44-file public repo generation: 9 TS modules, 11 JSONs, 4 workflows, 10 English docs with ~120 cited sources | Baseline of the v2 code tree; modules renamed and extended (hardware -> virtualcpu, memory -> virtualmemory) |
|
|
426
426
|
| e2ugh v2 | 2026-08-22+ | The merge: 57 CPUs, 19 GPUs + APUs, one event bus (51 typed topics), one 15-state lifecycle, one metrics store | Baseline of the v3 redistribution |
|
|
427
|
-
| e2ugh v3-v9 (this repo) | 2026-08-23+ | The rebrand and redistribution: github.com/wenathlan/e2ugh, package 1.2.1, `future.ts` decomposed into `scheduler.ts` and `compute.ts` (checkpoint plans into `virtualization.ts`, post-quantum into `security.ts`), a `test/`
|
|
427
|
+
| e2ugh v3-v9 (this repo) | 2026-08-23+ | The rebrand and redistribution: github.com/wenathlan/e2ugh, package 1.2.1, `future.ts` decomposed into `scheduler.ts` and `compute.ts` (checkpoint plans into `virtualization.ts`, post-quantum into `security.ts`), a `test/` vitest suite, and the corrected version ledger (kernel 7.1.9/6.18.45, edk2-stable202605, wgpu 30.x, Spin 3.6.0, containerd 2.3.4, WireGuard via kernel mainline) | Current |
|
|
428
428
|
|
|
429
429
|
Full per-generation detail, including the verification reports for each wave, is preserved in `docs/viability.md`.
|
|
430
430
|
|
package/modalities.ts
CHANGED
|
@@ -2492,7 +2492,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
2492
2492
|
slug: "runnerdeno",
|
|
2493
2493
|
lane: "runner",
|
|
2494
2494
|
isolation: "process",
|
|
2495
|
-
state: "
|
|
2495
|
+
state: "live",
|
|
2496
2496
|
drivers: ["deno"],
|
|
2497
2497
|
hardening: ["permission-map"],
|
|
2498
2498
|
boot: "hot",
|
|
@@ -2508,10 +2508,18 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
2508
2508
|
num: 19,
|
|
2509
2509
|
lane: "runner",
|
|
2510
2510
|
binary: "deno",
|
|
2511
|
-
// the payload rides the staged program file —
|
|
2512
|
-
// the sandbox alone, the
|
|
2511
|
+
// the real deno runner: the payload rides the staged program file —
|
|
2512
|
+
// the read permission maps the sandbox alone, the module cache rides
|
|
2513
|
+
// the sandbox tree (DENO_DIR never touches the host home — the same
|
|
2514
|
+
// seat the edge lane carries), the stdin dash never rides (no pipe
|
|
2515
|
+
// is written). the runtime detection rides the spawner's own absence
|
|
2516
|
+
// grammar — the same probehostbinary seat the edge lane table
|
|
2517
|
+
// carries — and a host without the deno binary answers the honest
|
|
2518
|
+
// coded absence, never a faked run (the seat never fakes a runtime).
|
|
2513
2519
|
argv: ({ file }) => ["deno", "run", "--allow-read=/sandbox", file],
|
|
2520
|
+
env: { DENO_DIR: "/sandbox/denodir" },
|
|
2514
2521
|
hardening: ["permission-map"],
|
|
2522
|
+
probe: "the host must carry the deno runtime (the runner-deno seat the owner's law 268 charges)",
|
|
2515
2523
|
}),
|
|
2516
2524
|
},
|
|
2517
2525
|
{
|
|
@@ -4191,7 +4199,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
4191
4199
|
slug: "storagesqlite",
|
|
4192
4200
|
lane: "sandbox",
|
|
4193
4201
|
isolation: "process",
|
|
4194
|
-
state: "
|
|
4202
|
+
state: "live",
|
|
4195
4203
|
drivers: ["sqlite"],
|
|
4196
4204
|
hardening: ["file-quota"],
|
|
4197
4205
|
boot: "hot",
|
|
@@ -4207,27 +4215,60 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
4207
4215
|
num: 72,
|
|
4208
4216
|
lane: "sandbox",
|
|
4209
4217
|
operate: async (request) => {
|
|
4218
|
+
// the real vault roundtrip: the node:sqlite runtime the engine host
|
|
4219
|
+
// carries — without it the seat answers the honest coded absence, and
|
|
4220
|
+
// with it one full roundtrip (create, insert, select, drop) on a
|
|
4221
|
+
// private in-memory table, the readback compared against the payload
|
|
4222
|
+
// character for character (the seat never fakes a database and never
|
|
4223
|
+
// opens a file — the store rides :memory: alone, the caller values
|
|
4224
|
+
// ride parameterized statements, only the seat's own generated table
|
|
4225
|
+
// name rides the statement text).
|
|
4226
|
+
let sqlite: typeof import("node:sqlite");
|
|
4210
4227
|
try {
|
|
4211
|
-
|
|
4212
|
-
const database = new sqlite.DatabaseSync(":memory:");
|
|
4213
|
-
try {
|
|
4214
|
-
database.exec("CREATE TABLE sandbox_artifacts (id INTEGER PRIMARY KEY, payload TEXT)");
|
|
4215
|
-
const statement = database.prepare("INSERT INTO sandbox_artifacts (payload) VALUES (?)");
|
|
4216
|
-
statement.run(request.code.slice(0, 1024));
|
|
4217
|
-
const rows = database.prepare("SELECT id, payload FROM sandbox_artifacts").all();
|
|
4218
|
-
return {
|
|
4219
|
-
stdout: `sqlite vault: ${rows.length} row(s) answered by the in-memory store (node:sqlite, :memory:)`,
|
|
4220
|
-
artifact: { rows: rows.length, engine: "node:sqlite" },
|
|
4221
|
-
};
|
|
4222
|
-
} finally {
|
|
4223
|
-
database.close();
|
|
4224
|
-
}
|
|
4228
|
+
sqlite = (await import("node:sqlite")) as typeof import("node:sqlite");
|
|
4225
4229
|
} catch (error) {
|
|
4226
4230
|
throw Object.assign(
|
|
4227
4231
|
new Error(`the sqlite runtime is absent from this host: ${error instanceof Error ? error.message : String(error)}`),
|
|
4228
4232
|
{ code: "HOST_RUNTIME_ABSENT" },
|
|
4229
4233
|
);
|
|
4230
4234
|
}
|
|
4235
|
+
const table = `saddle_sqlite_${Date.now().toString(36)}${Math.floor(Math.random() * 1679616).toString(36)}`;
|
|
4236
|
+
const payload = request.code;
|
|
4237
|
+
let readback = "";
|
|
4238
|
+
const database = new sqlite.DatabaseSync(":memory:");
|
|
4239
|
+
try {
|
|
4240
|
+
database.exec(`CREATE TABLE ${table} (k TEXT PRIMARY KEY, v TEXT)`);
|
|
4241
|
+
database.prepare(`INSERT INTO ${table} (k, v) VALUES (?, ?)`).run("saddle-payload", payload);
|
|
4242
|
+
readback = String(
|
|
4243
|
+
(database.prepare(`SELECT v FROM ${table} WHERE k = ?`).get("saddle-payload") as { v?: unknown } | undefined)?.v ?? "",
|
|
4244
|
+
);
|
|
4245
|
+
} catch (error) {
|
|
4246
|
+
throw Object.assign(
|
|
4247
|
+
new Error(`the sqlite vault refused the roundtrip: ${error instanceof Error ? error.message : String(error)}`),
|
|
4248
|
+
{ code: "SQLITE_ROUNDTRIP_FAILED" },
|
|
4249
|
+
);
|
|
4250
|
+
} finally {
|
|
4251
|
+
try {
|
|
4252
|
+
database.exec(`DROP TABLE IF EXISTS ${table}`);
|
|
4253
|
+
} catch {
|
|
4254
|
+
/* the roundtrip already answered; the drop is best effort */
|
|
4255
|
+
}
|
|
4256
|
+
try {
|
|
4257
|
+
database.close();
|
|
4258
|
+
} catch {
|
|
4259
|
+
/* the close is best effort */
|
|
4260
|
+
}
|
|
4261
|
+
}
|
|
4262
|
+
if (readback !== payload) {
|
|
4263
|
+
throw Object.assign(
|
|
4264
|
+
new Error(`the sqlite roundtrip answered ${readback.length} characters for ${payload.length} written`),
|
|
4265
|
+
{ code: "SQLITE_ROUNDTRIP_FAILED" },
|
|
4266
|
+
);
|
|
4267
|
+
}
|
|
4268
|
+
return {
|
|
4269
|
+
stdout: `sqlite vault: ${payload.length} characters roundtripped through the node:sqlite in-memory store`,
|
|
4270
|
+
artifact: { rows: 1, engine: "node:sqlite", roundtrip: true, store: ":memory:" },
|
|
4271
|
+
};
|
|
4231
4272
|
},
|
|
4232
4273
|
}),
|
|
4233
4274
|
},
|
|
@@ -4427,7 +4468,7 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
4427
4468
|
slug: "networkdns",
|
|
4428
4469
|
lane: "sandbox",
|
|
4429
4470
|
isolation: "process",
|
|
4430
|
-
state: "
|
|
4471
|
+
state: "live",
|
|
4431
4472
|
drivers: ["dns"],
|
|
4432
4473
|
hardening: ["resolver-allowlist"],
|
|
4433
4474
|
boot: "hot",
|
|
@@ -4443,11 +4484,47 @@ export const MODALITYENGINE: readonly ModalitySpec[] = [
|
|
|
4443
4484
|
num: 77,
|
|
4444
4485
|
lane: "sandbox",
|
|
4445
4486
|
operate: async (request) => {
|
|
4487
|
+
// the real resolution roundtrip: the node:dns resolver the engine
|
|
4488
|
+
// host carries — one lookup of the pinned well-known name
|
|
4489
|
+
// ("localhost", the name every host resolver answers; the caller
|
|
4490
|
+
// may name another, the roundtrip rides the same resolver call)
|
|
4491
|
+
// with the answered records verified as the honest result (the
|
|
4492
|
+
// non-empty answer, the address, the address family). the seat
|
|
4493
|
+
// never fakes a response and never opens a socket beyond the
|
|
4494
|
+
// resolver call — a host without a working resolver path answers
|
|
4495
|
+
// the honest coded refusal, never a canned answer (the wire
|
|
4496
|
+
// question the builddnsquery builder frames rides the artifact
|
|
4497
|
+
// beside the records the resolver actually answered — the same
|
|
4498
|
+
// grammar the sqlite vault roundtrip carries).
|
|
4499
|
+
const dns = await import("node:dns");
|
|
4446
4500
|
const inputs = (request.inputs ?? {}) as { domain?: string; qtype?: string };
|
|
4447
|
-
const
|
|
4501
|
+
const domain = inputs.domain ?? "localhost";
|
|
4502
|
+
const qtype = (inputs.qtype ?? "a").toLowerCase();
|
|
4503
|
+
const packet = builddnsquery(domain, qtype);
|
|
4504
|
+
const family = qtype === "a" ? 4 : qtype === "aaaa" ? 6 : 0;
|
|
4505
|
+
let records: import("node:dns").LookupAddress[];
|
|
4506
|
+
try {
|
|
4507
|
+
records = await dns.promises.lookup(domain, { all: true, family });
|
|
4508
|
+
} catch (error) {
|
|
4509
|
+
throw Object.assign(
|
|
4510
|
+
new Error(
|
|
4511
|
+
`the host resolver refused the ${qtype} roundtrip for ${domain}: ${error instanceof Error ? error.message : String(error)}`,
|
|
4512
|
+
),
|
|
4513
|
+
{ code: "DNS_RESOLUTION_REFUSED" },
|
|
4514
|
+
);
|
|
4515
|
+
}
|
|
4516
|
+
if (
|
|
4517
|
+
records.length === 0 ||
|
|
4518
|
+
records.some((record) => record.address.length === 0 || (record.family !== 4 && record.family !== 6))
|
|
4519
|
+
) {
|
|
4520
|
+
throw Object.assign(
|
|
4521
|
+
new Error(`the dns roundtrip answered ${records.length} records for ${domain} — the honest result never rides a malformed answer`),
|
|
4522
|
+
{ code: "DNS_ROUNDTRIP_FAILED" },
|
|
4523
|
+
);
|
|
4524
|
+
}
|
|
4448
4525
|
return {
|
|
4449
|
-
stdout: `dns:
|
|
4450
|
-
artifact: { packet, domain
|
|
4526
|
+
stdout: `dns resolver: ${domain} resolved ${records.length} records through the node:dns host resolver (the ${qtype} question, the honest roundtrip)`,
|
|
4527
|
+
artifact: { packet, domain, qtype, records, resolved: true, resolver: "node:dns" },
|
|
4451
4528
|
};
|
|
4452
4529
|
},
|
|
4453
4530
|
}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenathlan/saddle",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.168",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "binary computing engine that turns distributed storage into a publishable working set carrying the merged virtual-hardware engine (the saddle + e2ugh grand merge)",
|
|
6
6
|
"license": "GPL-3.0-only",
|
|
@@ -48,9 +48,9 @@
|
|
|
48
48
|
"vm:create": "node --import tsx ./cli.ts vm",
|
|
49
49
|
"vm:validate": "node --import tsx ./cli.ts vmlint",
|
|
50
50
|
"gpu:list": "node --import tsx ./cli.ts gpus",
|
|
51
|
-
"docker:build": "docker build -f Dockerfile -t saddle:2.0.
|
|
52
|
-
"docker:run": "docker run --rm --memory-swap=-1 --shm-size=2g saddle:2.0.
|
|
53
|
-
"docker:health": "docker run --rm -d --name saddle-health -p 8080:8080 saddle:2.0.
|
|
51
|
+
"docker:build": "docker build -f Dockerfile -t saddle:2.0.168 ..",
|
|
52
|
+
"docker:run": "docker run --rm --memory-swap=-1 --shm-size=2g saddle:2.0.168",
|
|
53
|
+
"docker:health": "docker run --rm -d --name saddle-health -p 8080:8080 saddle:2.0.168 >/dev/null && sleep 5; curl -fsS http://localhost:8080/api/v1/health || true; docker rm -f saddle-health >/dev/null 2>&1 || true",
|
|
54
54
|
"firecracker:boot": "firecracker --api-sock /tmp/fc-saddle.sock --config-file vmconfig.json 2>/dev/null || echo 'firecracker 1.16.1 warm-pool boot 125ms target'",
|
|
55
55
|
"py:bridge": "python3 qemubridge.py",
|
|
56
56
|
"py:test": "python3 -m pytest -q || python3 qemubridge.py",
|
|
@@ -59,9 +59,9 @@
|
|
|
59
59
|
"start:engine": "node --import tsx ./cli.ts plan"
|
|
60
60
|
},
|
|
61
61
|
"dependencies": {
|
|
62
|
-
"@ai-sdk/openai": "^4.0.
|
|
63
|
-
"@ai-sdk/openai-compatible": "^3.0.
|
|
64
|
-
"@ai-sdk/provider-utils": "^5.0.
|
|
62
|
+
"@ai-sdk/openai": "^4.0.111",
|
|
63
|
+
"@ai-sdk/openai-compatible": "^3.0.87",
|
|
64
|
+
"@ai-sdk/provider-utils": "^5.0.76",
|
|
65
65
|
"@anthropic-ai/sdk": "^1.6.0",
|
|
66
66
|
"@biomejs/biome": "^2.6.6",
|
|
67
67
|
"@capacitor/android": "^8.6.0",
|
|
@@ -131,15 +131,15 @@
|
|
|
131
131
|
"@types/react-dom": "^19.3.0",
|
|
132
132
|
"@types/three": "^0.187.0",
|
|
133
133
|
"@vitejs/plugin-react": "^6.2.1",
|
|
134
|
-
"@wenathlan/argan": "~2.0.
|
|
135
|
-
"@wenathlan/cadria": "~2.0.
|
|
136
|
-
"@wenathlan/debonair": "~2.0.
|
|
137
|
-
"@wenathlan/devthink": "~2.0.
|
|
138
|
-
"@wenathlan/getry": "~2.0.
|
|
139
|
-
"@wenathlan/saddle": "~2.0.
|
|
140
|
-
"@wenathlan/vault": "~2.0.
|
|
134
|
+
"@wenathlan/argan": "~2.0.167",
|
|
135
|
+
"@wenathlan/cadria": "~2.0.167",
|
|
136
|
+
"@wenathlan/debonair": "~2.0.167",
|
|
137
|
+
"@wenathlan/devthink": "~2.0.167",
|
|
138
|
+
"@wenathlan/getry": "~2.0.167",
|
|
139
|
+
"@wenathlan/saddle": "~2.0.167",
|
|
140
|
+
"@wenathlan/vault": "~2.0.167",
|
|
141
141
|
"add": "^2.0.9",
|
|
142
|
-
"ai": "^7.0.
|
|
142
|
+
"ai": "^7.0.165",
|
|
143
143
|
"autoprefixer": "^10.6.2",
|
|
144
144
|
"axios": "^1.21.0",
|
|
145
145
|
"baseline-browser-mapping": "^2.11.35",
|
|
@@ -150,7 +150,7 @@
|
|
|
150
150
|
"class-variance-authority": "^0.7.1",
|
|
151
151
|
"clsx": "^2.1.1",
|
|
152
152
|
"cmdk": "^1.1.1",
|
|
153
|
-
"commander": "^15.
|
|
153
|
+
"commander": "^15.1.0",
|
|
154
154
|
"date-fns": "^4.4.0",
|
|
155
155
|
"deepmerge-ts": "^8.0.2",
|
|
156
156
|
"dockerode": "^5.0.1",
|
|
@@ -168,13 +168,12 @@
|
|
|
168
168
|
"framer-motion": "^14.10.1",
|
|
169
169
|
"glob": "^14.0.4",
|
|
170
170
|
"gsap": "^3.15.1",
|
|
171
|
-
"hono": "^4.13.
|
|
171
|
+
"hono": "^4.13.17",
|
|
172
172
|
"ink": "^8.0.0",
|
|
173
173
|
"input-otp": "^1.5.0",
|
|
174
174
|
"js-yaml": "^5.5.0",
|
|
175
175
|
"jsdom": "^31.0.0",
|
|
176
176
|
"katex": "^0.19.1",
|
|
177
|
-
"lucide-react": "^1.70.0",
|
|
178
177
|
"minimatch": "^11.0.5",
|
|
179
178
|
"motion": "^14.10.1",
|
|
180
179
|
"mysql2": "^3.24.6",
|
|
@@ -233,9 +232,9 @@
|
|
|
233
232
|
"zustand": "^5.1.0"
|
|
234
233
|
},
|
|
235
234
|
"devDependencies": {
|
|
236
|
-
"@ai-sdk/openai": "^4.0.
|
|
237
|
-
"@ai-sdk/openai-compatible": "^3.0.
|
|
238
|
-
"@ai-sdk/provider-utils": "^5.0.
|
|
235
|
+
"@ai-sdk/openai": "^4.0.111",
|
|
236
|
+
"@ai-sdk/openai-compatible": "^3.0.87",
|
|
237
|
+
"@ai-sdk/provider-utils": "^5.0.76",
|
|
239
238
|
"@anthropic-ai/sdk": "^1.6.0",
|
|
240
239
|
"@biomejs/biome": "^2.6.6",
|
|
241
240
|
"@capacitor/android": "^8.6.0",
|
|
@@ -305,15 +304,15 @@
|
|
|
305
304
|
"@types/react-dom": "^19.3.0",
|
|
306
305
|
"@types/three": "^0.187.0",
|
|
307
306
|
"@vitejs/plugin-react": "^6.2.1",
|
|
308
|
-
"@wenathlan/argan": "~2.0.
|
|
309
|
-
"@wenathlan/cadria": "~2.0.
|
|
310
|
-
"@wenathlan/debonair": "~2.0.
|
|
311
|
-
"@wenathlan/devthink": "~2.0.
|
|
312
|
-
"@wenathlan/getry": "~2.0.
|
|
313
|
-
"@wenathlan/saddle": "~2.0.
|
|
314
|
-
"@wenathlan/vault": "~2.0.
|
|
307
|
+
"@wenathlan/argan": "~2.0.167",
|
|
308
|
+
"@wenathlan/cadria": "~2.0.167",
|
|
309
|
+
"@wenathlan/debonair": "~2.0.167",
|
|
310
|
+
"@wenathlan/devthink": "~2.0.167",
|
|
311
|
+
"@wenathlan/getry": "~2.0.167",
|
|
312
|
+
"@wenathlan/saddle": "~2.0.167",
|
|
313
|
+
"@wenathlan/vault": "~2.0.167",
|
|
315
314
|
"add": "^2.0.9",
|
|
316
|
-
"ai": "^7.0.
|
|
315
|
+
"ai": "^7.0.165",
|
|
317
316
|
"autoprefixer": "^10.6.2",
|
|
318
317
|
"axios": "^1.21.0",
|
|
319
318
|
"baseline-browser-mapping": "^2.11.35",
|
|
@@ -324,7 +323,7 @@
|
|
|
324
323
|
"class-variance-authority": "^0.7.1",
|
|
325
324
|
"clsx": "^2.1.1",
|
|
326
325
|
"cmdk": "^1.1.1",
|
|
327
|
-
"commander": "^15.
|
|
326
|
+
"commander": "^15.1.0",
|
|
328
327
|
"date-fns": "^4.4.0",
|
|
329
328
|
"deepmerge-ts": "^8.0.2",
|
|
330
329
|
"dockerode": "^5.0.1",
|
|
@@ -342,13 +341,12 @@
|
|
|
342
341
|
"framer-motion": "^14.10.1",
|
|
343
342
|
"glob": "^14.0.4",
|
|
344
343
|
"gsap": "^3.15.1",
|
|
345
|
-
"hono": "^4.13.
|
|
344
|
+
"hono": "^4.13.17",
|
|
346
345
|
"ink": "^8.0.0",
|
|
347
346
|
"input-otp": "^1.5.0",
|
|
348
347
|
"js-yaml": "^5.5.0",
|
|
349
348
|
"jsdom": "^31.0.0",
|
|
350
349
|
"katex": "^0.19.1",
|
|
351
|
-
"lucide-react": "^1.70.0",
|
|
352
350
|
"minimatch": "^11.0.5",
|
|
353
351
|
"motion": "^14.10.1",
|
|
354
352
|
"mysql2": "^3.24.6",
|
|
@@ -407,9 +405,9 @@
|
|
|
407
405
|
"zustand": "^5.1.0"
|
|
408
406
|
},
|
|
409
407
|
"overrides": {
|
|
410
|
-
"@ai-sdk/openai": "^4.0.
|
|
411
|
-
"@ai-sdk/openai-compatible": "^3.0.
|
|
412
|
-
"@ai-sdk/provider-utils": "^5.0.
|
|
408
|
+
"@ai-sdk/openai": "^4.0.111",
|
|
409
|
+
"@ai-sdk/openai-compatible": "^3.0.87",
|
|
410
|
+
"@ai-sdk/provider-utils": "^5.0.76",
|
|
413
411
|
"@anthropic-ai/sdk": "^1.6.0",
|
|
414
412
|
"@biomejs/biome": "^2.6.6",
|
|
415
413
|
"@capacitor/android": "^8.6.0",
|
|
@@ -479,15 +477,15 @@
|
|
|
479
477
|
"@types/react-dom": "^19.3.0",
|
|
480
478
|
"@types/three": "^0.187.0",
|
|
481
479
|
"@vitejs/plugin-react": "^6.2.1",
|
|
482
|
-
"@wenathlan/argan": "~2.0.
|
|
483
|
-
"@wenathlan/cadria": "~2.0.
|
|
484
|
-
"@wenathlan/debonair": "~2.0.
|
|
485
|
-
"@wenathlan/devthink": "~2.0.
|
|
486
|
-
"@wenathlan/getry": "~2.0.
|
|
487
|
-
"@wenathlan/saddle": "~2.0.
|
|
488
|
-
"@wenathlan/vault": "~2.0.
|
|
480
|
+
"@wenathlan/argan": "~2.0.167",
|
|
481
|
+
"@wenathlan/cadria": "~2.0.167",
|
|
482
|
+
"@wenathlan/debonair": "~2.0.167",
|
|
483
|
+
"@wenathlan/devthink": "~2.0.167",
|
|
484
|
+
"@wenathlan/getry": "~2.0.167",
|
|
485
|
+
"@wenathlan/saddle": "~2.0.167",
|
|
486
|
+
"@wenathlan/vault": "~2.0.167",
|
|
489
487
|
"add": "^2.0.9",
|
|
490
|
-
"ai": "^7.0.
|
|
488
|
+
"ai": "^7.0.165",
|
|
491
489
|
"autoprefixer": "^10.6.2",
|
|
492
490
|
"axios": "^1.21.0",
|
|
493
491
|
"baseline-browser-mapping": "^2.11.35",
|
|
@@ -498,7 +496,7 @@
|
|
|
498
496
|
"class-variance-authority": "^0.7.1",
|
|
499
497
|
"clsx": "^2.1.1",
|
|
500
498
|
"cmdk": "^1.1.1",
|
|
501
|
-
"commander": "^15.
|
|
499
|
+
"commander": "^15.1.0",
|
|
502
500
|
"date-fns": "^4.4.0",
|
|
503
501
|
"deepmerge-ts": "^8.0.2",
|
|
504
502
|
"dockerode": "^5.0.1",
|
|
@@ -516,13 +514,12 @@
|
|
|
516
514
|
"framer-motion": "^14.10.1",
|
|
517
515
|
"glob": "^14.0.4",
|
|
518
516
|
"gsap": "^3.15.1",
|
|
519
|
-
"hono": "^4.13.
|
|
517
|
+
"hono": "^4.13.17",
|
|
520
518
|
"ink": "^8.0.0",
|
|
521
519
|
"input-otp": "^1.5.0",
|
|
522
520
|
"js-yaml": "^5.5.0",
|
|
523
521
|
"jsdom": "^31.0.0",
|
|
524
522
|
"katex": "^0.19.1",
|
|
525
|
-
"lucide-react": "^1.70.0",
|
|
526
523
|
"minimatch": "^11.0.5",
|
|
527
524
|
"motion": "^14.10.1",
|
|
528
525
|
"mysql2": "^3.24.6",
|
package/workloadlaw.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
// # workloadlaw — the family workload lane catalog
|
|
2
|
-
// engine
|
|
1
|
+
// # workloadlaw — the family workload lane catalog, the join law of the
|
|
2
|
+
// engine and the lane seat factory of the app seats (root layer, waves
|
|
3
|
+
// WL1 + W5). One row per heavy workload lane of every
|
|
3
4
|
// official app of the family: the app that owns the lane, the lane's name,
|
|
4
5
|
// the demand the lane puts on the sandbox (gpu, network, isolation depth,
|
|
5
6
|
// boot budget) and where the lane runs today. The law this module answers
|
|
@@ -89,52 +90,268 @@ export type LaneJoin = {
|
|
|
89
90
|
* the join computes the answer from the registry of record. */
|
|
90
91
|
export const FAMILYLANES: readonly WorkloadLane[] = Object.freeze([
|
|
91
92
|
// the argan lanes: the dns engine's heavy passes
|
|
92
|
-
{
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
{
|
|
94
|
+
app: "argan",
|
|
95
|
+
lane: "dnssec-validate",
|
|
96
|
+
role: "the dnssec chain validation walk: rrsig parsing, key tag checksums, validity windows and ds match rules over untrusted input",
|
|
97
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
98
|
+
today: "app-lane",
|
|
99
|
+
},
|
|
100
|
+
{
|
|
101
|
+
app: "argan",
|
|
102
|
+
lane: "zonefile-parse-diff",
|
|
103
|
+
role: "the master file parse and the external-dns diff: tokenizer, normalization, creates/deletes/updates by group",
|
|
104
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
105
|
+
today: "app-lane",
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
app: "argan",
|
|
109
|
+
lane: "catalog-registry-serve",
|
|
110
|
+
role: "the registry tables the catalog serves: mime types, protocols and seed rows over https and db, never the browser fallback",
|
|
111
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
112
|
+
today: "app-lane",
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
app: "argan",
|
|
116
|
+
lane: "label-store-seed",
|
|
117
|
+
role: "the label manifest seed into the store: the upsert walk that protects translated rows, mirrored server-side",
|
|
118
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
119
|
+
today: "app-lane",
|
|
120
|
+
},
|
|
96
121
|
// the cadria lanes: the versawaze engine's generation passes
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
122
|
+
{
|
|
123
|
+
app: "cadria",
|
|
124
|
+
lane: "analyze-wav",
|
|
125
|
+
role: "the audio analysis pipeline: decode, downmix, resample, stft and the spectrum, rhythm, tonality, timbre and structure fusion",
|
|
126
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 800 },
|
|
127
|
+
today: "engine-lane",
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
app: "cadria",
|
|
131
|
+
lane: "generate-image",
|
|
132
|
+
role: "the image generation walk: palette, composition blocks, rhythm scatter, texture and motion into the deterministic project",
|
|
133
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
134
|
+
today: "engine-lane",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
app: "cadria",
|
|
138
|
+
lane: "render-svg",
|
|
139
|
+
role: "the svg render of the image project: draw command compilation and the byte-deterministic serialization",
|
|
140
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
141
|
+
today: "engine-lane",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
app: "cadria",
|
|
145
|
+
lane: "decode-video",
|
|
146
|
+
role: "the frame-accurate video decode lane: the container-namespace pass the codec walk rides, never the visitor decoder",
|
|
147
|
+
demand: { gpu: false, network: false, isolation: "namespace", bootbudget: 800 },
|
|
148
|
+
today: "app-lane",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
app: "cadria",
|
|
152
|
+
lane: "compile-shader",
|
|
153
|
+
role: "the shader program assembly: the fragment library, the deterministic manifest and the sha digest, no visitor gpu ever compiles",
|
|
154
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
155
|
+
today: "engine-lane",
|
|
156
|
+
},
|
|
102
157
|
// the cobaia lanes: the monetization surface's light walks
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
158
|
+
{
|
|
159
|
+
app: "cobaia",
|
|
160
|
+
lane: "plan-checkout",
|
|
161
|
+
role: "the plan checkout surface: mrr plan rows, link validation and the billing webhook walk when it lands, secrets server-side",
|
|
162
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
163
|
+
today: "app-lane",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
app: "cobaia",
|
|
167
|
+
lane: "ads-slot",
|
|
168
|
+
role: "the honest ad slot lane: the reserved slots and the rotation decision the deploy answers before the render",
|
|
169
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
170
|
+
today: "app-lane",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
app: "cobaia",
|
|
174
|
+
lane: "label-seed",
|
|
175
|
+
role: "the label manifest seed of the monetization face: the store walk the sqlite vault carries",
|
|
176
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
177
|
+
today: "app-lane",
|
|
178
|
+
},
|
|
106
179
|
// the debonair lanes: the kathesis engine's audio walks
|
|
107
|
-
{
|
|
108
|
-
|
|
180
|
+
{
|
|
181
|
+
app: "debonair",
|
|
182
|
+
lane: "ingest-audio",
|
|
183
|
+
role: "the ingest worker: the stream separation, the pcm downmix and the payload json the reconstruction reads",
|
|
184
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 800 },
|
|
185
|
+
today: "engine-lane",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
app: "debonair",
|
|
189
|
+
lane: "render-track",
|
|
190
|
+
role: "the render worker: the composed plan render, the mastering chain and the wav encode into the take store",
|
|
191
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 800 },
|
|
192
|
+
today: "engine-lane",
|
|
193
|
+
},
|
|
109
194
|
// the forge lanes: the execution clone's run walks
|
|
110
|
-
{
|
|
111
|
-
|
|
195
|
+
{
|
|
196
|
+
app: "forge",
|
|
197
|
+
lane: "binary-run",
|
|
198
|
+
role: "the binary run lane: the runner the saddle engine answers for the fleet, the mode gate resolving through the join",
|
|
199
|
+
demand: { gpu: false, network: true, isolation: "namespace", bootbudget: 800 },
|
|
200
|
+
today: "app-lane",
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
app: "forge",
|
|
204
|
+
lane: "queue-priority",
|
|
205
|
+
role: " the run queue walk: express, normal and batch ordering over the queue the lane serves",
|
|
206
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
207
|
+
today: "app-lane",
|
|
208
|
+
},
|
|
112
209
|
// the foundry lanes: the pipeline clone's graph walks
|
|
113
|
-
{
|
|
114
|
-
|
|
210
|
+
{
|
|
211
|
+
app: "foundry",
|
|
212
|
+
lane: "pipeline-graph",
|
|
213
|
+
role: "the pipeline graph lane: provision, pull, run, collect and store — the plan walk the herd drives",
|
|
214
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
215
|
+
today: "app-lane",
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
app: "foundry",
|
|
219
|
+
lane: "image-artifact",
|
|
220
|
+
role: "the image artifact lane: the format plans, the checksum seals and the shelf records the vault stores",
|
|
221
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
222
|
+
today: "app-lane",
|
|
223
|
+
},
|
|
115
224
|
// the getry lanes: the ai gateway's inference walks
|
|
116
|
-
{
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
225
|
+
{
|
|
226
|
+
app: "getry",
|
|
227
|
+
lane: "inference-route",
|
|
228
|
+
role: "the inference route lane: the provider pool, the sse parse and the model masking over the upstream calls",
|
|
229
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
230
|
+
today: "engine-lane",
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
app: "getry",
|
|
234
|
+
lane: "stream-fallback",
|
|
235
|
+
role: "the resilience runner: the verdict ladder, the backoff walk and the trail the retry lane keeps",
|
|
236
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
237
|
+
today: "engine-lane",
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
app: "getry",
|
|
241
|
+
lane: "thinking-ladder",
|
|
242
|
+
role: "the seven-level reasoning ladder: the budget resolution the request injects before the upstream call",
|
|
243
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
244
|
+
today: "engine-lane",
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
app: "getry",
|
|
248
|
+
lane: "router-rotation",
|
|
249
|
+
role: "the rotation lane: the weighted draw, the cooldown ledger and the session rows the store keeps",
|
|
250
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
251
|
+
today: "engine-lane",
|
|
252
|
+
},
|
|
120
253
|
// the saddle lanes: the engine's own dispatch surface
|
|
121
|
-
{
|
|
254
|
+
{
|
|
255
|
+
app: "saddle",
|
|
256
|
+
lane: "edge-dispatch",
|
|
257
|
+
role: "the edge plane dispatch itself: the visitor guard, the lane resolution and the artifact commit through the vault seam",
|
|
258
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 1500 },
|
|
259
|
+
today: "engine-lane",
|
|
260
|
+
},
|
|
122
261
|
// the stealthhead lanes: the fps platform's simulation walks
|
|
123
|
-
{
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
262
|
+
{
|
|
263
|
+
app: "stealthhead",
|
|
264
|
+
lane: "match-simulate",
|
|
265
|
+
role: "the authoritative match simulation: the fixed-step physics, the reconcile and rewind twin and the seeded spawns",
|
|
266
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
267
|
+
today: "app-lane",
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
app: "stealthhead",
|
|
271
|
+
lane: "rank-compute",
|
|
272
|
+
role: "the ranking walk: the elo ladder, the season batch and the position assignment over the standing rows",
|
|
273
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
274
|
+
today: "app-lane",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
app: "stealthhead",
|
|
278
|
+
lane: "world-bake",
|
|
279
|
+
role: "the world pre-compilation: the map bake through the render engine — the gpu pass the planned modalities answer",
|
|
280
|
+
demand: { gpu: true, network: true, isolation: "vm", bootbudget: 1500 },
|
|
281
|
+
today: "app-lane",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
app: "stealthhead",
|
|
285
|
+
lane: "ballistics-balance",
|
|
286
|
+
role: "the armory balance sweep: falloff, shots-to-kill and ttk validation, offline and deterministic",
|
|
287
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
288
|
+
today: "app-lane",
|
|
289
|
+
},
|
|
127
290
|
// the vault lanes: the storage engine's object walks
|
|
128
|
-
{
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
291
|
+
{
|
|
292
|
+
app: "vault",
|
|
293
|
+
lane: "blob-ingest",
|
|
294
|
+
role: "the object deposit lane: path and mime validation, the quota gate inside the write transaction and the detach walk",
|
|
295
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
296
|
+
today: "engine-lane",
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
app: "vault",
|
|
300
|
+
lane: "chunk-window",
|
|
301
|
+
role: "the chunk window lane: the fixed slicing, the per-window digest and the brotli decision over the byte stream",
|
|
302
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 800 },
|
|
303
|
+
today: "engine-lane",
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
app: "vault",
|
|
307
|
+
lane: "digest-sha256",
|
|
308
|
+
role: "the digest engine: the sha-256 master and chunk walks and the full re-verification the read path answers",
|
|
309
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
310
|
+
today: "engine-lane",
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
app: "vault",
|
|
314
|
+
lane: "ref-gc",
|
|
315
|
+
role: "the reference gc lane: the soft-delete tombstones, the ref detach and the orphan sweep the store drives",
|
|
316
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
317
|
+
today: "engine-lane",
|
|
318
|
+
},
|
|
132
319
|
// the devthink lanes: the os envelope's command walks
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
320
|
+
{
|
|
321
|
+
app: "devthink",
|
|
322
|
+
lane: "chat-sandbox",
|
|
323
|
+
role: "the chat sandbox lane: the session jail, the tool surface and the exec walks the engine dispatches server-side",
|
|
324
|
+
demand: { gpu: false, network: true, isolation: "namespace", bootbudget: 1500 },
|
|
325
|
+
today: "engine-lane",
|
|
326
|
+
},
|
|
327
|
+
{
|
|
328
|
+
app: "devthink",
|
|
329
|
+
lane: "llm-gateway",
|
|
330
|
+
role: "the universal gateway pipeline: model resolution, auth, body building, retry and the streaming byte passthrough",
|
|
331
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
332
|
+
today: "engine-lane",
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
app: "devthink",
|
|
336
|
+
lane: "stream-parse",
|
|
337
|
+
role: "the streaming parser: the tolerant sse walk, the payload normalization and the delta tracking over the live stream",
|
|
338
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 25 },
|
|
339
|
+
today: "engine-lane",
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
app: "devthink",
|
|
343
|
+
lane: "graph-run",
|
|
344
|
+
role: "the graph runner: the deterministic stage walk, the retry ladder and the progress views the console draws",
|
|
345
|
+
demand: { gpu: false, network: false, isolation: "process", bootbudget: 25 },
|
|
346
|
+
today: "engine-lane",
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
app: "devthink",
|
|
350
|
+
lane: "mcp-serve",
|
|
351
|
+
role: "the mcp registry lane: the tool catalog, the approval gates and the transports the server answers",
|
|
352
|
+
demand: { gpu: false, network: true, isolation: "process", bootbudget: 800 },
|
|
353
|
+
today: "engine-lane",
|
|
354
|
+
},
|
|
138
355
|
]);
|
|
139
356
|
|
|
140
357
|
/** The official app slugs the catalog covers — the lockstep vocabulary the
|
|
@@ -291,3 +508,85 @@ export function workloadreport(): FamilyWorkloadReport {
|
|
|
291
508
|
perapp,
|
|
292
509
|
};
|
|
293
510
|
}
|
|
511
|
+
|
|
512
|
+
// The lane seat engine (wave W5, the dedup law): every app file of the
|
|
513
|
+
// family once carried its own copy of the seat resolver (the deploy's
|
|
514
|
+
// opt-in walk — local, join or a pinned modality id), the env reader and
|
|
515
|
+
// the lane lookup beside its own lane table. The engine lives here once
|
|
516
|
+
// now: the factory below binds the law — ported verbatim from the app
|
|
517
|
+
// seats the wave folded, zero behavior change — to the app's own slug,
|
|
518
|
+
// env var name and lane table, and the app seats re-export the bound
|
|
519
|
+
// functions under the historical names their batteries pin, so the
|
|
520
|
+
// engine is never duplicated between apps (the family law: the owner
|
|
521
|
+
// exports, the apps import). The seat stays as pure as the catalog: no
|
|
522
|
+
// new imports, no storage, the browser-pure bundle keeps carrying it.
|
|
523
|
+
|
|
524
|
+
/** One workload lane row as the app seats speak it — the historical
|
|
525
|
+
* surface name of the engine's own row shape (WorkloadLane above): the
|
|
526
|
+
* alias keeps every app importer stable while the engine owns the shape
|
|
527
|
+
* (the dedup law — one row shape, one owner). */
|
|
528
|
+
export type AppWorkloadLane = WorkloadLane;
|
|
529
|
+
|
|
530
|
+
/** The typed answer of the lane seat: the declaration the deploy reads —
|
|
531
|
+
* local (the lanes run in the deploy's own process), join (the platform
|
|
532
|
+
* resolves every lane through the engine's join resolver), or the pinned
|
|
533
|
+
* modality id the deploy named (validated by the platform's join at
|
|
534
|
+
* dispatch, never judged here). every answer is honest: an absent or
|
|
535
|
+
* blank choice answers the declared default, never a silent switch. */
|
|
536
|
+
export type LaneSeatResult =
|
|
537
|
+
| { ok: true; seat: "local"; sandboxed: false }
|
|
538
|
+
| { ok: true; seat: "join"; sandboxed: true; pinned: false }
|
|
539
|
+
| { ok: true; seat: string; sandboxed: true; pinned: true };
|
|
540
|
+
|
|
541
|
+
/** One app's lane seating — the bound engine the app seats re-export:
|
|
542
|
+
* the app's own slug, env var name and lane table beside the seat
|
|
543
|
+
* functions the app surface speaks (the names the batteries pin). */
|
|
544
|
+
export type LaneSeating = {
|
|
545
|
+
/** The official app slug the seating carries. */
|
|
546
|
+
APPSLUG: string;
|
|
547
|
+
/** The environment name the deploy pins the seat through. */
|
|
548
|
+
SANDBOXLANEENV: string;
|
|
549
|
+
/** The lane table the seating answers lookups from. */
|
|
550
|
+
WORKLOADLANES: readonly AppWorkloadLane[];
|
|
551
|
+
/** Resolves the sandbox lane seat — the deploy's opt-in choice walk. */
|
|
552
|
+
lanesandbox: (choice: string | undefined) => LaneSeatResult;
|
|
553
|
+
/** Reads the seat off one environment lookup — the injected env carrier. */
|
|
554
|
+
lanesandboxfromenv: (lookup: (key: string) => string | undefined) => LaneSeatResult;
|
|
555
|
+
/** Answers one lane row by lane id — the lookup the app surfaces read. */
|
|
556
|
+
laneof: (lane: string) => AppWorkloadLane | undefined;
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
/** Builds one app's lane seating — the lane seat engine of the family
|
|
560
|
+
* bound to the app's own data: the resolver, the env reader and the lane
|
|
561
|
+
* lookup ported verbatim from the app files the wave folded, closed over
|
|
562
|
+
* the env var name and the lane table the app hands in. the app seat
|
|
563
|
+
* keeps only its data — the slug, the env var name, the lane table — and
|
|
564
|
+
* re-exports the bound functions under the names its surface always
|
|
565
|
+
* spoke, so the engine is never duplicated between apps (the family law:
|
|
566
|
+
* the owner exports, the apps import).
|
|
567
|
+
*
|
|
568
|
+
* @param options the app's own seating data: the slug, the env var name
|
|
569
|
+
* the deploy pins the seat through and the lane table the lookups read.
|
|
570
|
+
* @returns the bound seating.
|
|
571
|
+
*/
|
|
572
|
+
export function laneseating(options: { slug: string; envvar: string; lanes: readonly AppWorkloadLane[] }): LaneSeating {
|
|
573
|
+
const SANDBOXLANEENV = options.envvar;
|
|
574
|
+
const WORKLOADLANES = options.lanes;
|
|
575
|
+
function lanesandbox(choice: string | undefined): LaneSeatResult {
|
|
576
|
+
const name = choice?.trim() ?? "";
|
|
577
|
+
if (name.length === 0 || name === "local") {
|
|
578
|
+
return { ok: true, seat: "local", sandboxed: false };
|
|
579
|
+
}
|
|
580
|
+
if (name === "join") {
|
|
581
|
+
return { ok: true, seat: "join", sandboxed: true, pinned: false };
|
|
582
|
+
}
|
|
583
|
+
return { ok: true, seat: name, sandboxed: true, pinned: true };
|
|
584
|
+
}
|
|
585
|
+
function lanesandboxfromenv(lookup: (key: string) => string | undefined): LaneSeatResult {
|
|
586
|
+
return lanesandbox(lookup(SANDBOXLANEENV));
|
|
587
|
+
}
|
|
588
|
+
function laneof(lane: string): AppWorkloadLane | undefined {
|
|
589
|
+
return WORKLOADLANES.find((row) => row.lane === lane);
|
|
590
|
+
}
|
|
591
|
+
return { APPSLUG: options.slug, SANDBOXLANEENV, WORKLOADLANES, lanesandbox, lanesandboxfromenv, laneof };
|
|
592
|
+
}
|