@volter/world-runtime 3.0.162 → 3.0.164

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.
Files changed (140) hide show
  1. package/dist/src/ancestry.js +2 -2
  2. package/dist/src/app-url.d.ts +5 -1
  3. package/dist/src/app-url.js +14 -1
  4. package/dist/src/branch.js +28 -8
  5. package/dist/src/browser-files.d.ts +5 -0
  6. package/dist/src/browser-files.js +14 -0
  7. package/dist/src/browsers.d.ts +2 -3
  8. package/dist/src/browsers.js +4 -4
  9. package/dist/src/catalog.d.ts +2 -1
  10. package/dist/src/catalog.js +3 -2
  11. package/dist/src/changeset.js +1 -1
  12. package/dist/src/cli-commands.js +71 -18
  13. package/dist/src/cli-load-clock.d.ts +1 -0
  14. package/dist/src/cli-load-clock.js +2 -0
  15. package/dist/src/cli.js +5 -1
  16. package/dist/src/clickhouse-backing.d.ts +2 -0
  17. package/dist/src/clickhouse-backing.js +145 -0
  18. package/dist/src/command-lifetime.d.mts +1 -1
  19. package/dist/src/command-lifetime.mjs +5 -2
  20. package/dist/src/configs.js +1 -1
  21. package/dist/src/consumers.js +1 -1
  22. package/dist/src/covers.d.ts +4 -2
  23. package/dist/src/covers.js +8 -4
  24. package/dist/src/go-mysql-binding.d.ts +19 -0
  25. package/dist/src/go-mysql-binding.js +51 -0
  26. package/dist/src/guest-processes.d.mts +1 -1
  27. package/dist/src/guest-processes.mjs +22 -14
  28. package/dist/src/host-cli.js +8 -1
  29. package/dist/src/host-ready.d.ts +16 -2
  30. package/dist/src/host-ready.js +66 -39
  31. package/dist/src/host-worker.js +7 -1
  32. package/dist/src/host.d.ts +26 -0
  33. package/dist/src/host.js +93 -3
  34. package/dist/src/infra-definition.d.ts +35 -0
  35. package/dist/src/infra-definition.js +146 -0
  36. package/dist/src/infra.d.ts +3 -0
  37. package/dist/src/infra.js +375 -105
  38. package/dist/src/init.d.ts +46 -0
  39. package/dist/src/init.js +445 -18
  40. package/dist/src/lifecycle-record.d.ts +5 -1
  41. package/dist/src/lifecycle-record.js +27 -4
  42. package/dist/src/local-branches.js +1 -1
  43. package/dist/src/mariadb-bootstrap.d.ts +32 -0
  44. package/dist/src/mariadb-bootstrap.js +90 -0
  45. package/dist/src/mariadb-supplier.d.ts +23 -0
  46. package/dist/src/mariadb-supplier.js +215 -0
  47. package/dist/src/mongodb-backing.d.ts +4 -0
  48. package/dist/src/mongodb-backing.js +71 -1
  49. package/dist/src/native-mariadb-backing.d.ts +13 -0
  50. package/dist/src/native-mariadb-backing.js +512 -0
  51. package/dist/src/native-postgres-backing.d.ts +4 -10
  52. package/dist/src/native-postgres-backing.js +428 -43
  53. package/dist/src/native-server-inputs.d.ts +16 -0
  54. package/dist/src/native-server-inputs.js +204 -0
  55. package/dist/src/origin.js +3 -3
  56. package/dist/src/original-inputs.d.ts +14 -0
  57. package/dist/src/original-inputs.js +84 -0
  58. package/dist/src/pack-facts.d.ts +9 -1
  59. package/dist/src/pglite-backing.d.ts +41 -17
  60. package/dist/src/pglite-backing.js +213 -79
  61. package/dist/src/pglite-host.mjs +482 -10
  62. package/dist/src/process-groups.d.mts +6 -1
  63. package/dist/src/process-groups.d.ts +2 -1
  64. package/dist/src/process-groups.js +1 -1
  65. package/dist/src/process-groups.mjs +82 -15
  66. package/dist/src/process-signal.d.mts +2 -2
  67. package/dist/src/process-signal.mjs +29 -9
  68. package/dist/src/project-inspect.d.ts +12 -0
  69. package/dist/src/project-inspect.js +41 -4
  70. package/dist/src/proxy-env.d.ts +34 -0
  71. package/dist/src/proxy-env.js +114 -0
  72. package/dist/src/redirect-proxy.d.ts +1 -31
  73. package/dist/src/redirect-proxy.js +3 -112
  74. package/dist/src/redis-backing.d.ts +6 -4
  75. package/dist/src/redis-backing.js +20 -168
  76. package/dist/src/runtime.d.ts +1 -0
  77. package/dist/src/runtime.js +503 -218
  78. package/dist/src/schema.d.ts +1 -0
  79. package/dist/src/schema.js +5 -3
  80. package/dist/src/served-world.js +15 -10
  81. package/dist/src/service-exit.d.ts +3 -2
  82. package/dist/src/service-exit.js +7 -5
  83. package/dist/src/twin-backing.d.ts +47 -0
  84. package/dist/src/twin-backing.js +343 -0
  85. package/dist/src/up-cli.js +56 -3
  86. package/package.json +4 -4
  87. package/src/ancestry.ts +2 -2
  88. package/src/app-url.ts +9 -2
  89. package/src/branch.ts +26 -8
  90. package/src/browser-files.ts +15 -0
  91. package/src/browsers.ts +4 -4
  92. package/src/catalog.ts +4 -3
  93. package/src/changeset.ts +1 -1
  94. package/src/cli-commands.ts +85 -18
  95. package/src/cli-load-clock.ts +2 -0
  96. package/src/cli.ts +5 -1
  97. package/src/clickhouse-backing.ts +70 -0
  98. package/src/command-lifetime.d.mts +1 -1
  99. package/src/command-lifetime.mjs +5 -2
  100. package/src/configs.ts +1 -1
  101. package/src/consumers.ts +1 -1
  102. package/src/covers.ts +9 -4
  103. package/src/go-mysql-binding.ts +49 -0
  104. package/src/guest-processes.d.mts +1 -1
  105. package/src/guest-processes.mjs +22 -14
  106. package/src/host-cli.ts +7 -1
  107. package/src/host-ready.ts +62 -31
  108. package/src/host-worker.ts +6 -2
  109. package/src/host.ts +88 -6
  110. package/src/infra-definition.ts +135 -0
  111. package/src/infra.ts +250 -45
  112. package/src/init.ts +416 -17
  113. package/src/lifecycle-record.ts +18 -4
  114. package/src/local-branches.ts +1 -1
  115. package/src/mariadb-bootstrap.ts +107 -0
  116. package/src/mariadb-supplier.ts +199 -0
  117. package/src/mongodb-backing.ts +59 -1
  118. package/src/native-mariadb-backing.ts +418 -0
  119. package/src/native-postgres-backing.ts +353 -41
  120. package/src/native-server-inputs.ts +169 -0
  121. package/src/origin.ts +3 -3
  122. package/src/original-inputs.ts +72 -0
  123. package/src/pack-facts.ts +1 -1
  124. package/src/pglite-backing.ts +192 -80
  125. package/src/pglite-host.mjs +482 -10
  126. package/src/process-groups.d.mts +6 -1
  127. package/src/process-groups.mjs +82 -15
  128. package/src/process-groups.ts +2 -1
  129. package/src/process-signal.d.mts +2 -2
  130. package/src/process-signal.mjs +29 -9
  131. package/src/project-inspect.ts +37 -4
  132. package/src/proxy-env.ts +128 -0
  133. package/src/redirect-proxy.ts +3 -125
  134. package/src/redis-backing.ts +19 -136
  135. package/src/runtime.ts +342 -107
  136. package/src/schema.ts +5 -4
  137. package/src/served-world.ts +14 -10
  138. package/src/service-exit.ts +5 -3
  139. package/src/twin-backing.ts +300 -0
  140. package/src/up-cli.ts +52 -3
@@ -1,24 +1,10 @@
1
1
  // Every volter-world command but `up` (./up-cli.ts), loaded by ./cli.ts only when one of them is asked for.
2
- import { sessionTrustEnv } from "./ca-trust.js";
3
2
  import { optionValue, ownerOption } from "./cli-args.js";
4
- import { printReady } from "./up-cli.js";
5
3
  import { spawn, spawnSync } from 'node:child_process';
6
4
  import { createRequire } from 'node:module';
7
5
  import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
8
6
  import { tmpdir } from 'node:os';
9
7
  import { join, resolve } from 'node:path';
10
- import { activateScript, branchWorld, checkoutWorld, fetchFromOrigin, pushWorldChangeset, rebaseWorldChangeset, resetWorld, seedWorld, worldOrigin, approveWorldChangeset, writeWorldClock, checkPrerequisites, coverWorld, createWorldChangeset, diffWorld, doctorWorld, downWorld, findWorldChangeset, formatCoverageReport, recordCoverage, formatInitReport, formatPrerequisiteChecks, formatProjectInspection, initWorld, inspectProject, isRemoteWorldRef, listWorldChangesets, listWorlds, markWorld, migrateWorldConfig, readReflectRoutes, reflectRoutesPath, replayWorldChangeset, resolveWorldRef, attachWorld, retireWorldConsumers, runWorld, shareWorldServices, shellWorld, startReflectFront, startReflectResolver, statusWorld, statusWorldChangeset, tailWorldActions, unshareWorld, urlsWorld, urlWorld, verifyWorldChangeset, worldManifest, writeReflectRoutes, readReflectManifest, writeReflectManifest, clearReflectManifest, composeOverrideForReflect, splitDockerComposeArgs, dockerComposeWithOverride, ATTACHED_CA_PATH } from "./index.js";
11
- import { formatApplication, formatChangeset, formatRebaseReport, formatChangesetStatus, formatLedgerDelta, formatReplayReport, formatVerification, parseVerifierExpression } from '@volter/world-core/runtime';
12
- import { advertiseWorldManifest, fetchRemoteManifest, MANIFEST_PATH, remoteAttachEnv, startManifestServer } from "./serve.js";
13
- import { clockFile, instanceDir, pruneWorlds, withoutVaultSignIn } from "./runtime.js";
14
- import { worldResourceReport } from "./resources.js";
15
- import { worldConsumers, worldUsage } from "./consumers.js";
16
- import { addAppHosts, appUrlUnsetMessage, detectAppUrl, readAppUrl, setAppUrl } from "./app-url.js";
17
- import { describeServiceEnd, describeWorldEvent, readServiceEnd, readWorldEvents } from "./service-exit.js";
18
- import { worldEventsPath } from "./runtime.js";
19
- import { resolveCatalog } from "./catalog.js";
20
- import { loadWorldConfig } from "./configs.js";
21
- import { vendorOfPackModule } from '@volter/world-core/runtime';
22
8
  /** The non-flag args, skipping each value-taking flag's value (so `--root <path>` never
23
9
  * masquerades as a positional service id). */
24
10
  function positionalArgs(args, valueFlags = ['--root']) {
@@ -50,7 +36,7 @@ function acknowledgeOptions(args, command) {
50
36
  }
51
37
  function printHelp() {
52
38
  process.stdout.write(`Usage:
53
- volter-world init <name> --repo <path> [--out <dir>] [--force] [--allow-unknown] [--acknowledge vendor=reason]... [--json] [--root <repo>]
39
+ volter-world init <name> --repo <path> [--out <dir>] [--needs <needs.json>] [--force] [--allow-unknown] [--acknowledge vendor=reason]... [--json] [--root <repo>]
54
40
  # the FRONT DOOR: detect the repo's vendors, EMIT a world config +
55
41
  # env file OUTSIDE the repo (default <repo>/../<name>-pilot/, the
56
42
  # repo is never written to), then run the coverage proof over the
@@ -58,6 +44,7 @@ function printHelp() {
58
44
  # Deterministic: same repo + same pack catalog => identical bytes.
59
45
  # init DIAGNOSES; it never boots — it prints the exact next commands
60
46
  volter-world up <config> --env-out=<path> [--owner <label>] [--name <name>] [--mode local|share|sealed] [--isolation process|colocated|worker] [--keep-state] [--app-url <url>] [--in-process] [--json] [--root <repo>]
47
+ volter-world up <config> --env-out=<path> --in-process --attach-after-line [--name <name>] [--keep-state] [--app-url <url>] [--root <repo>] -- <command...> # after its last boot line, waits for stdin "attach" before running the command
61
48
  volter-world outdated <world> [--json] — each mounted twin: pinned / mounted / catalog-current version and protocol standing (R17)
62
49
  volter-world audit <world> [--json] — each mounted twin's grade, as the catalog records it (R17)
63
50
  volter-world down <name> [--grace-ms <ms>] [--purge] [--if-instance <token>] [--root <repo>] # SIGTERM, then SIGKILL survivors after the grace (default 5000ms)
@@ -177,6 +164,7 @@ export async function main() {
177
164
  return;
178
165
  }
179
166
  if (cmd === 'consumers') {
167
+ const { retireWorldConsumers } = await import("./runtime.js");
180
168
  // `volter-world consumers retire <world> [--consumer <id>] [--root <repo>] [--json]`: the owner's
181
169
  // explicit resolution of attachments whose runner and command are provably gone.
182
170
  const args = rest.filter((arg) => arg !== undefined);
@@ -206,6 +194,9 @@ export async function main() {
206
194
  return;
207
195
  }
208
196
  if (cmd === 'resources' || cmd === 'prune') {
197
+ const { pruneWorlds, statusWorld } = await import("./runtime.js");
198
+ const { worldResourceReport } = await import("./resources.js");
199
+ const { worldConsumers, worldUsage } = await import("./consumers.js");
209
200
  const args = [subject, ...rest].filter((arg) => arg !== undefined);
210
201
  const reportRoot = optionValue(args, '--root', process.cwd());
211
202
  // Destructive commands must reject typos, not silently broaden a target to all Worlds.
@@ -277,6 +268,7 @@ export async function main() {
277
268
  return;
278
269
  }
279
270
  if (cmd === 'down') {
271
+ const { downWorld } = await import("./runtime.js");
280
272
  if (!subject)
281
273
  throw new Error('volter-world down: missing world name');
282
274
  const graceArg = optionValue(rest, '--grace-ms');
@@ -321,6 +313,8 @@ export async function main() {
321
313
  return;
322
314
  }
323
315
  if (cmd === 'events') {
316
+ const { statusWorld, worldEventsPath } = await import("./runtime.js");
317
+ const { readWorldEvents, describeWorldEvent } = await import("./service-exit.js");
324
318
  if (!subject)
325
319
  throw new Error('volter-world events: missing world name');
326
320
  const status = statusWorld(subject, root, { readOnly: true, light: true });
@@ -335,6 +329,9 @@ export async function main() {
335
329
  return;
336
330
  }
337
331
  if (cmd === 'status') {
332
+ const { statusWorld, worldEventsPath } = await import("./runtime.js");
333
+ const { worldUsage } = await import("./consumers.js");
334
+ const { readServiceEnd, describeServiceEnd, readWorldEvents, describeWorldEvent } = await import("./service-exit.js");
338
335
  if (!subject)
339
336
  throw new Error('volter-world status: missing world name');
340
337
  const status = statusWorld(subject, root);
@@ -397,6 +394,7 @@ export async function main() {
397
394
  }
398
395
  // `plan` and `review` retired 2026-09-05: the changeset verbs are the one review path (verify, approve, status, push).
399
396
  if (cmd === 'doctor') {
397
+ const { doctorWorld } = await import("./runtime.js");
400
398
  if (!subject)
401
399
  throw new Error('volter-world doctor: missing world name');
402
400
  const report = await doctorWorld(subject, { root, verifyPublic: rest.includes('--verify-public') });
@@ -413,6 +411,7 @@ export async function main() {
413
411
  return;
414
412
  }
415
413
  if (cmd === 'doctor-prereqs') {
414
+ const { checkPrerequisites, formatPrerequisiteChecks } = await import("./prerequisites.js");
416
415
  const required = rest.flatMap((arg, index) => arg === '--require' ? [rest[index + 1]] : [])
417
416
  .filter(Boolean);
418
417
  const checks = checkPrerequisites(required.length ? required : ['local-execution']);
@@ -429,6 +428,7 @@ export async function main() {
429
428
  return;
430
429
  }
431
430
  if (cmd === 'inspect-project') {
431
+ const { inspectProject, formatProjectInspection } = await import("./project-inspect.js");
432
432
  const args = [...(subject ? [subject] : []), ...rest];
433
433
  const path = args.find((arg) => !arg.startsWith('--')) || process.cwd();
434
434
  const report = inspectProject(path);
@@ -436,6 +436,7 @@ export async function main() {
436
436
  return;
437
437
  }
438
438
  if (cmd === 'migrate-config') {
439
+ const { migrateWorldConfig } = await import("./configs.js");
439
440
  if (!subject)
440
441
  throw new Error('volter-world migrate-config: missing config path or name');
441
442
  const migration = migrateWorldConfig(subject, root);
@@ -469,22 +470,29 @@ export async function main() {
469
470
  return;
470
471
  }
471
472
  if (cmd === 'init') {
473
+ const { initWorld, formatInitReport } = await import("./init.js");
472
474
  if (!subject)
473
475
  throw new Error('volter-world init: missing world name (usage: volter-world init <name> --repo <path>)');
474
476
  const repo = optionValue(rest, '--repo');
475
477
  if (!repo)
476
478
  throw new Error('volter-world init: --repo <path> is required (the application repo to build the world for)');
477
479
  const out = optionValue(rest, '--out');
480
+ const needs = optionValue(rest, '--needs');
478
481
  const result = initWorld(subject, repo, {
479
482
  root,
480
483
  ...(out ? { out } : {}),
484
+ ...(needs ? { needs } : {}),
481
485
  force: rest.includes('--force'),
482
486
  allowUnknown: rest.includes('--allow-unknown'),
483
487
  acknowledge: acknowledgeOptions(rest, 'init'),
484
488
  });
485
489
  process.stdout.write(rest.includes('--json') ? `${JSON.stringify(result, null, 2)}\n` : formatInitReport(result));
486
490
  // Vendor coverage IS the exit code. App boot risks remain a separate, prominent report field.
487
- if (!result.ok)
491
+ // A World made from a stack's needs is the exception, and says so: it is made for the services the stack
492
+ // names, a vendor with no twin is its report and not its failure, and its maker goes by this exit alone.
493
+ if (!result.ok && needs)
494
+ process.stdout.write('\nVENDOR COVERAGE: incomplete (the vendors above with no pack). With --needs that is reported and is not a failure: the World is written, exit 0.\n');
495
+ if (!result.ok && !needs)
488
496
  process.exit(1);
489
497
  return;
490
498
  }
@@ -492,10 +500,11 @@ export async function main() {
492
500
  // twins this world mounted; `audit` is each one's grade as the catalog records it. Both read
493
501
  // instance.json and the committed artifacts — nothing is fetched.
494
502
  if (cmd === 'outdated' || cmd === 'audit') {
503
+ const { statusWorld } = await import("./runtime.js");
495
504
  if (!subject)
496
505
  throw new Error(`volter-world ${cmd}: missing world name (usage: volter-world ${cmd} <world> [--json])`);
497
506
  const status = statusWorld(subject, root);
498
- const rows = worldUpkeep(status, cmd, root);
507
+ const rows = await worldUpkeep(status, cmd, root);
499
508
  if (rest.includes('--json'))
500
509
  process.stdout.write(`${JSON.stringify(rows, null, 2)}\n`);
501
510
  else {
@@ -509,6 +518,7 @@ export async function main() {
509
518
  return;
510
519
  }
511
520
  if (cmd === 'covers') {
521
+ const { coverWorld, formatCoverageReport, recordCoverage } = await import("./covers.js");
512
522
  if (!subject)
513
523
  throw new Error('volter-world covers: missing world name (usage: volter-world covers <world> --repo <path>)');
514
524
  const repo = optionValue(rest, '--repo');
@@ -532,6 +542,8 @@ export async function main() {
532
542
  return;
533
543
  }
534
544
  if (cmd === 'clock') {
545
+ const { clockFile } = await import("./runtime.js");
546
+ const { writeWorldClock } = await import("./clock.js");
535
547
  // THE operator door for world time (physics): show / set <iso> / advance <duration> / shift <duration> / clear.
536
548
  // The clock every twin reads per request (kernel worldNow()), and every application process through the injector
537
549
  // (inject.cjs, WORLD TIME); a change takes effect live, no restarts. Its two forms (world-clock.cjs): `set` freezes
@@ -604,6 +616,7 @@ export async function main() {
604
616
  return;
605
617
  }
606
618
  if (cmd === 'urls') {
619
+ const { urlsWorld } = await import("./runtime.js");
607
620
  if (!subject)
608
621
  throw new Error('volter-world urls: missing world name');
609
622
  const urls = urlsWorld(subject, root);
@@ -619,6 +632,7 @@ export async function main() {
619
632
  return;
620
633
  }
621
634
  if (cmd === 'url') {
635
+ const { urlWorld } = await import("./runtime.js");
622
636
  if (!subject)
623
637
  throw new Error('volter-world url: missing world name (usage: volter-world url <world> [service])');
624
638
  const [service] = positionalArgs(rest);
@@ -631,6 +645,7 @@ export async function main() {
631
645
  return;
632
646
  }
633
647
  if (cmd === 'app-url') {
648
+ const { setAppUrl, detectAppUrl, addAppHosts, readAppUrl, appUrlUnsetMessage } = await import("./app-url.js");
634
649
  if (!subject)
635
650
  throw new Error('volter-world app-url: missing world name (usage: volter-world app-url <world> [--set <url>|--detect <pid|port>])');
636
651
  const set = optionValue(rest, '--set');
@@ -667,6 +682,7 @@ export async function main() {
667
682
  return;
668
683
  }
669
684
  if (cmd === 'tail' || cmd === 'log') {
685
+ const { tailWorldActions } = await import("./tail.js");
670
686
  if (!subject)
671
687
  throw new Error(`volter-world ${cmd}: missing world name (usage: volter-world log <world> [service...])`);
672
688
  const services = positionalArgs(rest);
@@ -685,6 +701,7 @@ export async function main() {
685
701
  return;
686
702
  }
687
703
  if (cmd === 'seed' || cmd === 'reset') {
704
+ const { seedWorld, resetWorld } = await import("./placeholder.js");
688
705
  if (!subject)
689
706
  throw new Error(`volter-world ${cmd}: missing world name (usage: volter-world ${cmd} <world> [--entry <file>] [--cwd <dir>])`);
690
707
  if (rest.some(arg => arg === '--env-file' || arg.startsWith('--env-file=')))
@@ -702,6 +719,8 @@ export async function main() {
702
719
  return;
703
720
  }
704
721
  if (cmd === 'branch') {
722
+ const { branchWorld } = await import("./branch.js");
723
+ const { printReady } = await import("./up-cli.js");
705
724
  if (rest.some(arg => arg === '--env-file' || arg.startsWith('--env-file=')))
706
725
  throw new Error('--env-file is not an input for this command. Use --env-out=<new-output-path>; never pass credentials or custody as an output.');
707
726
  const [name] = positionalArgs(rest);
@@ -718,6 +737,8 @@ export async function main() {
718
737
  return;
719
738
  }
720
739
  if (cmd === 'checkout') {
740
+ const { checkoutWorld } = await import("./branch.js");
741
+ const { printReady } = await import("./up-cli.js");
721
742
  if (!subject)
722
743
  throw new Error('volter-world checkout: missing world name');
723
744
  const instance = await checkoutWorld(subject, { root });
@@ -728,6 +749,7 @@ export async function main() {
728
749
  return;
729
750
  }
730
751
  if (cmd === 'fetch') {
752
+ const { fetchFromOrigin } = await import("./origin.js");
731
753
  if (!subject)
732
754
  throw new Error('volter-world fetch: missing world name (usage: volter-world fetch <world> --key <key> [--origin <url> --namespace <org>/<world>] [--full])');
733
755
  const key = optionValue(rest, '--key') || process.env.VOLTER_TWINS_READ_KEY || process.env.VOLTER_TWINS_KEY || '';
@@ -749,6 +771,7 @@ export async function main() {
749
771
  return;
750
772
  }
751
773
  if (cmd === 'origin') {
774
+ const { worldOrigin } = await import("./origin.js");
752
775
  if (!subject)
753
776
  throw new Error('volter-world origin: missing world name');
754
777
  const origin = worldOrigin(subject, root);
@@ -759,6 +782,7 @@ export async function main() {
759
782
  return;
760
783
  }
761
784
  if (cmd === 'mark') {
785
+ const { markWorld } = await import("./changeset.js");
762
786
  if (!subject)
763
787
  throw new Error('volter-world mark: missing world name (usage: volter-world mark <world> [--id <marker>] [--note <text>])');
764
788
  const id = optionValue(rest, '--id');
@@ -771,6 +795,8 @@ export async function main() {
771
795
  return;
772
796
  }
773
797
  if (cmd === 'diff') {
798
+ const { diffWorld } = await import("./changeset.js");
799
+ const { formatLedgerDelta } = await import('@volter/world-core/runtime');
774
800
  if (!subject)
775
801
  throw new Error('volter-world diff: missing world name (usage: volter-world diff <world> [--base <marker>])');
776
802
  const base = optionValue(rest, '--base');
@@ -779,6 +805,8 @@ export async function main() {
779
805
  return;
780
806
  }
781
807
  if (cmd === 'changeset') {
808
+ const { parseVerifierExpression, formatChangeset, formatReplayReport, formatVerification, formatChangesetStatus, formatRebaseReport, formatApplication } = await import('@volter/world-core/runtime');
809
+ const { createWorldChangeset, findWorldChangeset, listWorldChangesets, replayWorldChangeset, verifyWorldChangeset, approveWorldChangeset, statusWorldChangeset, rebaseWorldChangeset, pushWorldChangeset } = await import("./changeset.js");
782
810
  const CHANGESET_VALUE_FLAGS = ['--root', '--base', '--into', '--world', '--verifier', '--as', '--note'];
783
811
  const args = positionalArgs(rest, CHANGESET_VALUE_FLAGS);
784
812
  const world = optionValue(rest, '--world');
@@ -893,6 +921,8 @@ export async function main() {
893
921
  throw new Error(`volter-world changeset: unknown subcommand ${JSON.stringify(subject ?? '')} (want create|show|list|replay|verify|approve|status|rebase|push)`);
894
922
  }
895
923
  if (cmd === 'list') {
924
+ const { listWorlds } = await import("./runtime.js");
925
+ const { worldUsage } = await import("./consumers.js");
896
926
  const listArgs = [subject, ...rest].filter((arg) => arg !== undefined);
897
927
  const listRoot = optionValue(listArgs, '--root', process.cwd());
898
928
  const worlds = listWorlds(listRoot).map(world => ({ ...world, usage: worldUsage(listRoot, world.name) }));
@@ -907,6 +937,7 @@ export async function main() {
907
937
  return;
908
938
  }
909
939
  if (cmd === 'env') {
940
+ const { attachWorld } = await import("./runtime.js");
910
941
  if (!subject)
911
942
  throw new Error('volter-world env: missing world name');
912
943
  const split = rest.indexOf('--');
@@ -916,6 +947,10 @@ export async function main() {
916
947
  process.exit(exitCode);
917
948
  }
918
949
  if (cmd === 'attach') {
950
+ // The local env attach, every app's CMD path, needs the World's ref and its runtime alone: the two load together,
951
+ // and the remote door (serve.ts) and the reflect front (reflect.ts, ca-trust.ts) load only on their own paths.
952
+ // All five were imported one after another first (S2 R16: Dub's attach, 225 bodies in 341 ms before its `next`).
953
+ const [{ resolveWorldRef, isRemoteWorldRef, worldManifest }, { withoutVaultSignIn, instanceDir, attachWorld }] = await Promise.all([import("./attach.js"), import("./runtime.js")]);
919
954
  // `subject` may be the ref, a flag, or the `--` separator — reassemble and split.
920
955
  const args = [subject, ...rest].filter((arg) => arg !== undefined);
921
956
  const split = args.indexOf('--');
@@ -930,6 +965,7 @@ export async function main() {
930
965
  throw new Error('--owner is supported for local attach --via env commands');
931
966
  }
932
967
  if (isRemoteWorldRef(resolved.ref)) {
968
+ const { fetchRemoteManifest, remoteAttachEnv } = await import("./serve.js");
933
969
  // remote world: fetch the manifest, synthesize the attach env, exec
934
970
  const attachToken = optionValue(head, '--token') || process.env.VOLTER_WORLD_TOKEN || undefined;
935
971
  const manifest = await fetchRemoteManifest(resolved.ref, attachToken === undefined ? {} : { token: attachToken });
@@ -975,6 +1011,7 @@ export async function main() {
975
1011
  // being the caller's to point (a container's --dns, a resolver on the host).
976
1012
  if (command.length === 0)
977
1013
  throw new Error('volter-world attach: pass the command after --');
1014
+ const [{ readReflectManifest, splitDockerComposeArgs, composeOverrideForReflect, dockerComposeWithOverride, ATTACHED_CA_PATH }, { sessionTrustEnv }] = await Promise.all([import("./reflect.js"), import("./ca-trust.js")]);
978
1015
  const manifest = readReflectManifest(attachRoot, resolved.ref);
979
1016
  if (!manifest)
980
1017
  throw new Error(`volter-world attach --via reflect: no reflect front is running for world ${resolved.ref} — start one: volter-world reflect ${resolved.ref} --target-ip <ip> --port 443 --resolver-port 53`);
@@ -1007,12 +1044,15 @@ export async function main() {
1007
1044
  process.exit(await attachWorld(resolved.ref, command, attachRoot, { cwd: process.cwd(), owner: ownerOption(head), verbose: head.includes('--verbose') }));
1008
1045
  }
1009
1046
  if (cmd === 'manifest') {
1047
+ const { worldManifest } = await import("./attach.js");
1010
1048
  if (!subject)
1011
1049
  throw new Error('volter-world manifest: missing world name');
1012
1050
  process.stdout.write(`${JSON.stringify(worldManifest(subject, root), null, 2)}\n`);
1013
1051
  return;
1014
1052
  }
1015
1053
  if (cmd === 'reflect') {
1054
+ const { reflectRoutesPath, startReflectFront, startReflectResolver, readReflectRoutes, writeReflectManifest, clearReflectManifest } = await import("./reflect.js");
1055
+ const { statusWorld, instanceDir } = await import("./runtime.js");
1016
1056
  if (!subject)
1017
1057
  throw new Error('volter-world reflect: missing world name');
1018
1058
  const targetIp = optionValue(rest, '--target-ip');
@@ -1047,6 +1087,9 @@ export async function main() {
1047
1087
  return;
1048
1088
  }
1049
1089
  if (cmd === 'serve') {
1090
+ const { startReflectFront } = await import("./reflect.js");
1091
+ const { statusWorld, instanceDir } = await import("./runtime.js");
1092
+ const { startManifestServer, advertiseWorldManifest, MANIFEST_PATH } = await import("./serve.js");
1050
1093
  if (!subject)
1051
1094
  throw new Error('volter-world serve: missing world name');
1052
1095
  const advertise = optionValue(rest, '--advertise');
@@ -1088,6 +1131,7 @@ export async function main() {
1088
1131
  return;
1089
1132
  }
1090
1133
  if (cmd === 'route') {
1134
+ const { reflectRoutesPath, readReflectRoutes, writeReflectRoutes } = await import("./reflect.js");
1091
1135
  if (!subject)
1092
1136
  throw new Error('volter-world route: missing world name');
1093
1137
  const [verb, host] = rest.filter((arg) => !arg.startsWith('--'));
@@ -1111,17 +1155,21 @@ export async function main() {
1111
1155
  return;
1112
1156
  }
1113
1157
  if (cmd === 'activate') {
1158
+ const { activateScript } = await import("./runtime.js");
1114
1159
  if (!subject)
1115
1160
  throw new Error('volter-world activate: missing world name');
1116
1161
  process.stdout.write(activateScript(subject, root));
1117
1162
  return;
1118
1163
  }
1119
1164
  if (cmd === 'shell') {
1165
+ const { shellWorld } = await import("./runtime.js");
1120
1166
  if (!subject)
1121
1167
  throw new Error('volter-world shell: missing world name');
1122
1168
  process.exit(await shellWorld(subject, root));
1123
1169
  }
1124
1170
  if (cmd === 'run') {
1171
+ const { runWorld } = await import("./runtime.js");
1172
+ const { readServiceEnd, describeServiceEnd } = await import("./service-exit.js");
1125
1173
  if (!subject)
1126
1174
  throw new Error('volter-world run: missing config');
1127
1175
  const split = rest.indexOf('--');
@@ -1155,6 +1203,7 @@ export async function main() {
1155
1203
  process.exit(result.exitCode);
1156
1204
  }
1157
1205
  if (cmd === 'share') {
1206
+ const { shareWorldServices } = await import("./runtime.js");
1158
1207
  if (!subject)
1159
1208
  throw new Error('volter-world share: missing world name');
1160
1209
  const service = optionValue(rest, '--service') || undefined;
@@ -1187,6 +1236,7 @@ export async function main() {
1187
1236
  return;
1188
1237
  }
1189
1238
  if (cmd === 'unshare') {
1239
+ const { unshareWorld } = await import("./runtime.js");
1190
1240
  if (!subject)
1191
1241
  throw new Error('volter-world unshare: missing world name');
1192
1242
  const service = optionValue(rest, '--service') || undefined;
@@ -1201,7 +1251,10 @@ export async function main() {
1201
1251
  /** The rows `outdated` / `audit` print. `outdated`: pinned vs mounted vs catalog-current version and the
1202
1252
  * protocol standing; `audit`: each mounted twin's grade, from the catalog's record. `attention` marks a row the
1203
1253
  * operator should act on (exit 1). */
1204
- function worldUpkeep(status, kind, root) {
1254
+ async function worldUpkeep(status, kind, root) {
1255
+ const { resolveCatalog } = await import("./catalog.js");
1256
+ const { loadWorldConfig } = await import("./configs.js");
1257
+ const { vendorOfPackModule } = await import('@volter/world-core/runtime');
1205
1258
  const catalog = resolveCatalog(root);
1206
1259
  const config = loadWorldConfig(status.name, root).config;
1207
1260
  const out = [];
@@ -0,0 +1 @@
1
+ export declare const cliLoadStarted: number;
@@ -0,0 +1,2 @@
1
+ // Imported first by the CLI: measures only its modules, after the engine starts this first body.
2
+ export const cliLoadStarted = performance.now();
package/dist/src/cli.js CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  // volter-world: each command loads only what it needs. `up` (./up-cli.ts) starts the World's owner process
3
3
  // before any other command's modules load; every other command is ./cli-commands.ts.
4
+ import { cliLoadStarted } from "./cli-load-clock.js";
4
5
  import { optionValue } from "./cli-args.js";
5
6
  import { errorMessage } from "./error-message.js";
6
7
  const [cmd, subject, ...rest] = process.argv.slice(2);
7
8
  const run = cmd === 'up' && subject !== '--help' && subject !== '-h'
8
- ? import("./up-cli.js").then(({ upCommand }) => upCommand(subject, rest, optionValue(rest, '--root', process.cwd())))
9
+ ? import("./up-cli.js").then(({ upCommand }) => {
10
+ process.stderr.write(`volter-world up: entry modules loaded in ${(performance.now() - cliLoadStarted).toFixed(1)} ms\n`);
11
+ return upCommand(subject, rest, optionValue(rest.includes('--attach-after-line') && rest.includes('--') ? rest.slice(0, rest.indexOf('--')) : rest, '--root', process.cwd()));
12
+ })
9
13
  : import("./cli-commands.js").then(({ main }) => main());
10
14
  run.catch((error) => {
11
15
  process.stderr.write(`${errorMessage(error)}\n`);
@@ -0,0 +1,2 @@
1
+ import type { ManagedProtocol } from './twin-backing.js';
2
+ export declare const CLICKHOUSE_PROTOCOL: ManagedProtocol;
@@ -0,0 +1,145 @@
1
+ // ClickHouse's managed ownership questions: HTTP ping, this child's kernel boot answer,
2
+ // and a native Hello/Ping. No SQL is executed by readiness.
3
+ // Packet fields: clickhouse-docs/docs/native-protocol/{client,server,basics}.md;
4
+ // revision54429 uses the reached clickhouse-driver0.2.10 layout retained with the pack.
5
+ import { connect } from 'node:net';
6
+ import { WORLD_BOOT_PATH } from '@volter/world-core/runtime';
7
+ async function answer(port, path) {
8
+ const controller = new AbortController(), timer = setTimeout(() => controller.abort(), 1000);
9
+ try {
10
+ const response = await fetch(`http://127.0.0.1:${port}${path}`, { signal: controller.signal, redirect: 'error' });
11
+ if (!response.ok || !response.body)
12
+ return undefined;
13
+ // This private proof contains a UUID boot nonce and a JavaScript-safe PID; ping is four bytes.
14
+ const reader = response.body.getReader();
15
+ let text = '', bytes = 0;
16
+ try {
17
+ while (true) {
18
+ const part = await reader.read();
19
+ if (part.done)
20
+ break;
21
+ bytes += part.value.length;
22
+ if (bytes > 256)
23
+ return undefined;
24
+ text += new TextDecoder().decode(part.value);
25
+ }
26
+ }
27
+ finally {
28
+ await reader.cancel().catch(() => undefined);
29
+ }
30
+ return text;
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ finally {
36
+ clearTimeout(timer);
37
+ }
38
+ }
39
+ function number(value) { const result = []; do {
40
+ const byte = value % 128;
41
+ value = Math.floor(value / 128);
42
+ result.push(byte | (value ? 128 : 0));
43
+ } while (value); return result; }
44
+ function string(value) { const bytes = new TextEncoder().encode(value); return [...number(bytes.length), ...bytes]; }
45
+ function nativeAnswers(service) {
46
+ const port = service.sides?.native?.hostPort;
47
+ if (!port)
48
+ return Promise.resolve(false);
49
+ const users = service.configuration?.users;
50
+ const [user, password] = users && Object.hasOwn(users, 'default') ? ['default', users.default] : Object.entries(users ?? { default: '' })[0] ?? [];
51
+ if (typeof user !== 'string' || typeof password !== 'string')
52
+ return Promise.resolve(false);
53
+ const hello = Uint8Array.from([0, ...string('World ownership'), 1, 0, ...number(54429), ...string('default'), ...string(user), ...string(password)]);
54
+ return new Promise(resolve => {
55
+ const socket = connect({ host: '127.0.0.1', port, timeout: 1000 });
56
+ let bytes = new Uint8Array(), phase = 'hello', settled = false;
57
+ const end = (valid) => { if (settled)
58
+ return; settled = true; clearTimeout(timer); socket.destroy(); resolve(valid); };
59
+ // One complete ownership question has one deadline; incoming fragments never extend it.
60
+ const timer = setTimeout(() => end(false), 1000);
61
+ const missing = Symbol('incomplete');
62
+ socket.once('connect', () => socket.write(hello));
63
+ socket.on('data', chunk => {
64
+ // The declaring pack fixes its Hello name, timezone and display name;64KiB is beyond that packet domain.
65
+ if (bytes.length + chunk.length > 65536) {
66
+ end(false);
67
+ return;
68
+ }
69
+ const joined = new Uint8Array(bytes.length + chunk.length);
70
+ joined.set(bytes);
71
+ joined.set(chunk, bytes.length);
72
+ bytes = joined;
73
+ let at = 0;
74
+ const read = () => {
75
+ let value = 0n;
76
+ for (let shift = 0; shift < 70; shift += 7) {
77
+ if (at >= bytes.length)
78
+ throw missing;
79
+ const byte = bytes[at++];
80
+ value |= BigInt(byte & 127) << BigInt(shift);
81
+ if (!(byte & 128)) {
82
+ if (value > BigInt(Number.MAX_SAFE_INTEGER))
83
+ throw new Error('Invalid native integer');
84
+ return Number(value);
85
+ }
86
+ }
87
+ throw new Error('Invalid native integer');
88
+ };
89
+ const text = () => { const length = read(); if (length > bytes.length - at)
90
+ throw missing; const result = new TextDecoder().decode(bytes.subarray(at, at + length)); at += length; return result; };
91
+ try {
92
+ if (phase === 'hello') {
93
+ if (read() !== 0 || text() !== 'ClickHouse') {
94
+ end(false);
95
+ return;
96
+ }
97
+ read();
98
+ read();
99
+ const revision = read();
100
+ if (revision !== 54429) {
101
+ end(false);
102
+ return;
103
+ }
104
+ text();
105
+ text();
106
+ read();
107
+ bytes = bytes.slice(at);
108
+ phase = 'pong';
109
+ socket.write(Uint8Array.of(4));
110
+ if (!bytes.length)
111
+ return;
112
+ at = 0;
113
+ }
114
+ end(read() === 4);
115
+ }
116
+ catch (error) {
117
+ if (error !== missing)
118
+ end(false);
119
+ }
120
+ });
121
+ socket.once('error', () => end(false));
122
+ socket.once('timeout', () => end(false));
123
+ socket.once('close', () => end(false));
124
+ });
125
+ }
126
+ export const CLICKHOUSE_PROTOCOL = {
127
+ name: 'ClickHouse', bootProof: true,
128
+ async answers(port) { return await answer(port, '/ping') === 'Ok.\n'; },
129
+ async serves(port, pid, service, boot) {
130
+ if (!service || !boot || await answer(port, '/ping') !== 'Ok.\n')
131
+ return false;
132
+ const body = await answer(port, WORLD_BOOT_PATH);
133
+ if (!body)
134
+ return false;
135
+ try {
136
+ const held = JSON.parse(body);
137
+ if (held.boot !== boot || held.pid !== pid)
138
+ return false;
139
+ }
140
+ catch {
141
+ return false;
142
+ }
143
+ return nativeAnswers(service);
144
+ },
145
+ };
@@ -4,7 +4,7 @@ export const COMMAND_GRACE_MS: number;
4
4
  export const COMMAND_CONFIRM_MS: number;
5
5
  export const commandProcessGroup: boolean;
6
6
  export class CommandRetirementError extends Error {}
7
- export function commandLifetime(child: ChildProcess, abort?: AbortSignal, preserveOnSuccess?: boolean): {
7
+ export function commandLifetime(child: ChildProcess, abort?: AbortSignal, preserveOnSuccess?: boolean, graceMs?: number): {
8
8
  identity: Promise<SpawnIdentity | undefined>;
9
9
  cancel(signal: NodeJS.Signals): void;
10
10
  readonly forwarded: NodeJS.Signals | undefined;
@@ -9,7 +9,10 @@ export class CommandRetirementError extends Error {}
9
9
 
10
10
  /** The child owns a private POSIX group. A bounded retirement requires both closed output and
11
11
  * a vanished group. Unknown retirement keeps the caller's ownership record and reservation. */
12
- export function commandLifetime(child, abort, preserveOnSuccess = false) {
12
+ /** `graceMs` is how long a cancelled child is awaited before SIGKILL. A command gets the default; a STORE (a
13
+ * database, a twin that holds state) is given its lifecycle's budget, since what it does between the signal and
14
+ * its exit is write its state down, and a SIGKILL in the middle of that leaves a store nothing may take. */
15
+ export function commandLifetime(child, abort, preserveOnSuccess = false, graceMs = COMMAND_GRACE_MS) {
13
16
  const identity = child.pid && commandProcessGroup ? childIdentity(child) : Promise.resolve(undefined);
14
17
  let signalFailure;
15
18
  let pendingSignals = 0;
@@ -62,7 +65,7 @@ export function commandLifetime(child, abort, preserveOnSuccess = false) {
62
65
  }
63
66
  };
64
67
  const watch = () => {
65
- if (!deadline) deadline = Date.now() + COMMAND_GRACE_MS;
68
+ if (!deadline) deadline = Date.now() + graceMs;
66
69
  timer ??= setInterval(check, 20);
67
70
  check();
68
71
  };
@@ -1,6 +1,6 @@
1
1
  import { copyFileSync, constants, existsSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
2
2
  import { basename, dirname, join, resolve } from 'node:path';
3
- import { getActiveWorldStore, stateDirName } from '@volter/world-core/runtime';
3
+ import { getActiveWorldStore, stateDirName } from '@volter/world-core/runtime-state';
4
4
  import { assertWorldConfig, LEGACY_WORLD_SELECTION, worldConfigDocument } from "./schema.js";
5
5
  export function resolveConfigPath(config, root = process.cwd()) {
6
6
  const candidates = config.includes('/') || config.endsWith('.json')
@@ -2,7 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
  import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { hostname } from 'node:os';
4
4
  import { join } from 'node:path';
5
- import { stateDirName, withFileLock } from '@volter/world-core/runtime';
5
+ import { stateDirName, withFileLock } from '@volter/world-core/runtime-state';
6
6
  export const CONSUMER_HEARTBEAT_MS = 5000;
7
7
  const STALE_MS = 30_000;
8
8
  export function checkedOwner(owner) {
@@ -142,6 +142,8 @@ export declare function worldTwinInventory(world: string, root: string): WorldTw
142
142
  export type CoverageOptions = {
143
143
  root?: string;
144
144
  allowUnknown?: boolean;
145
+ /** Application environment names already read from its declared stack. */
146
+ declaredEnvironmentNames?: readonly string[];
145
147
  /**
146
148
  * vendor → reason: vendors the operator ACKNOWLEDGES need no twin in this world — e.g. an SDK
147
149
  * used verifier-only (svix's Webhook verifies incoming signatures locally and never calls out).
@@ -179,11 +181,11 @@ export type RepoVendorSignals = {
179
181
  };
180
182
  /** Detect every vendor (and every unmapped external-service-shaped signal) an application repo
181
183
  * betrays: mapped npm dependencies across the root + all workspace members, vendor-shaped env
182
- * names in committed `.env*` files, the SMTP/raw-protocol signal, and literal vendor URLs passed
184
+ * names in committed `.env*` files or its declared stack, the SMTP/raw-protocol signal, and literal vendor URLs passed
183
185
  * directly to fetch in production source. URL attribution reads the injector's VENDOR_HOSTS table
184
186
  * rather than re-encoding host rules here. Insertion order follows the sorted dependency, env-name,
185
187
  * and source lists, so callers that preserve it are deterministic. */
186
- export declare function detectRepoVendors(repoPath: string, services?: Array<WorldServiceConfig | WorldServiceInstance>): RepoVendorSignals;
188
+ export declare function detectRepoVendors(repoPath: string, services?: Array<WorldServiceConfig | WorldServiceInstance>, declaredEnvironmentNames?: readonly string[]): RepoVendorSignals;
187
189
  export declare function coverWorld(world: string, repoPath: string, options?: CoverageOptions): CoverageReport;
188
190
  /**
189
191
  * The report as the data `init` keeps in the config (`discovery.coverage`): the same rows `formatCoverageReport`