@volter/world-runtime 3.0.163 → 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.
- package/dist/src/ancestry.js +2 -2
- package/dist/src/app-url.d.ts +5 -1
- package/dist/src/app-url.js +14 -1
- package/dist/src/branch.js +28 -8
- package/dist/src/browser-files.d.ts +5 -0
- package/dist/src/browser-files.js +14 -0
- package/dist/src/browsers.d.ts +2 -3
- package/dist/src/browsers.js +4 -4
- package/dist/src/catalog.d.ts +2 -1
- package/dist/src/catalog.js +3 -2
- package/dist/src/changeset.js +1 -1
- package/dist/src/cli-commands.js +71 -18
- package/dist/src/cli-load-clock.d.ts +1 -0
- package/dist/src/cli-load-clock.js +2 -0
- package/dist/src/cli.js +5 -1
- package/dist/src/clickhouse-backing.d.ts +2 -0
- package/dist/src/clickhouse-backing.js +145 -0
- package/dist/src/command-lifetime.d.mts +1 -1
- package/dist/src/command-lifetime.mjs +5 -2
- package/dist/src/configs.js +1 -1
- package/dist/src/consumers.js +1 -1
- package/dist/src/covers.d.ts +4 -2
- package/dist/src/covers.js +8 -4
- package/dist/src/go-mysql-binding.d.ts +19 -0
- package/dist/src/go-mysql-binding.js +51 -0
- package/dist/src/guest-processes.d.mts +1 -1
- package/dist/src/guest-processes.mjs +22 -14
- package/dist/src/host-cli.js +8 -1
- package/dist/src/host-ready.d.ts +16 -2
- package/dist/src/host-ready.js +66 -39
- package/dist/src/host-worker.js +7 -1
- package/dist/src/host.d.ts +26 -0
- package/dist/src/host.js +93 -3
- package/dist/src/infra-definition.d.ts +35 -0
- package/dist/src/infra-definition.js +146 -0
- package/dist/src/infra.d.ts +3 -0
- package/dist/src/infra.js +375 -105
- package/dist/src/init.d.ts +46 -0
- package/dist/src/init.js +445 -18
- package/dist/src/lifecycle-record.d.ts +5 -1
- package/dist/src/lifecycle-record.js +27 -4
- package/dist/src/local-branches.js +1 -1
- package/dist/src/mariadb-bootstrap.d.ts +32 -0
- package/dist/src/mariadb-bootstrap.js +90 -0
- package/dist/src/mariadb-supplier.d.ts +23 -0
- package/dist/src/mariadb-supplier.js +215 -0
- package/dist/src/mongodb-backing.d.ts +4 -0
- package/dist/src/mongodb-backing.js +71 -1
- package/dist/src/native-mariadb-backing.d.ts +13 -0
- package/dist/src/native-mariadb-backing.js +512 -0
- package/dist/src/native-postgres-backing.d.ts +4 -10
- package/dist/src/native-postgres-backing.js +428 -43
- package/dist/src/native-server-inputs.d.ts +16 -0
- package/dist/src/native-server-inputs.js +204 -0
- package/dist/src/origin.js +3 -3
- package/dist/src/original-inputs.d.ts +14 -0
- package/dist/src/original-inputs.js +84 -0
- package/dist/src/pack-facts.d.ts +9 -1
- package/dist/src/pglite-backing.d.ts +41 -17
- package/dist/src/pglite-backing.js +213 -79
- package/dist/src/pglite-host.mjs +482 -10
- package/dist/src/process-groups.d.mts +6 -1
- package/dist/src/process-groups.d.ts +2 -1
- package/dist/src/process-groups.js +1 -1
- package/dist/src/process-groups.mjs +82 -15
- package/dist/src/process-signal.d.mts +2 -2
- package/dist/src/process-signal.mjs +29 -9
- package/dist/src/project-inspect.d.ts +12 -0
- package/dist/src/project-inspect.js +41 -4
- package/dist/src/proxy-env.d.ts +34 -0
- package/dist/src/proxy-env.js +114 -0
- package/dist/src/redirect-proxy.d.ts +1 -31
- package/dist/src/redirect-proxy.js +3 -112
- package/dist/src/redis-backing.d.ts +6 -4
- package/dist/src/redis-backing.js +20 -168
- package/dist/src/runtime.d.ts +1 -0
- package/dist/src/runtime.js +503 -218
- package/dist/src/schema.d.ts +1 -0
- package/dist/src/schema.js +5 -3
- package/dist/src/served-world.js +3 -3
- package/dist/src/twin-backing.d.ts +47 -0
- package/dist/src/twin-backing.js +343 -0
- package/dist/src/up-cli.js +56 -3
- package/package.json +3 -3
- package/src/ancestry.ts +2 -2
- package/src/app-url.ts +9 -2
- package/src/branch.ts +26 -8
- package/src/browser-files.ts +15 -0
- package/src/browsers.ts +4 -4
- package/src/catalog.ts +4 -3
- package/src/changeset.ts +1 -1
- package/src/cli-commands.ts +85 -18
- package/src/cli-load-clock.ts +2 -0
- package/src/cli.ts +5 -1
- package/src/clickhouse-backing.ts +70 -0
- package/src/command-lifetime.d.mts +1 -1
- package/src/command-lifetime.mjs +5 -2
- package/src/configs.ts +1 -1
- package/src/consumers.ts +1 -1
- package/src/covers.ts +9 -4
- package/src/go-mysql-binding.ts +49 -0
- package/src/guest-processes.d.mts +1 -1
- package/src/guest-processes.mjs +22 -14
- package/src/host-cli.ts +7 -1
- package/src/host-ready.ts +62 -31
- package/src/host-worker.ts +6 -2
- package/src/host.ts +88 -6
- package/src/infra-definition.ts +135 -0
- package/src/infra.ts +250 -45
- package/src/init.ts +416 -17
- package/src/lifecycle-record.ts +18 -4
- package/src/local-branches.ts +1 -1
- package/src/mariadb-bootstrap.ts +107 -0
- package/src/mariadb-supplier.ts +199 -0
- package/src/mongodb-backing.ts +59 -1
- package/src/native-mariadb-backing.ts +418 -0
- package/src/native-postgres-backing.ts +353 -41
- package/src/native-server-inputs.ts +169 -0
- package/src/origin.ts +3 -3
- package/src/original-inputs.ts +72 -0
- package/src/pack-facts.ts +1 -1
- package/src/pglite-backing.ts +192 -80
- package/src/pglite-host.mjs +482 -10
- package/src/process-groups.d.mts +6 -1
- package/src/process-groups.mjs +82 -15
- package/src/process-groups.ts +2 -1
- package/src/process-signal.d.mts +2 -2
- package/src/process-signal.mjs +29 -9
- package/src/project-inspect.ts +37 -4
- package/src/proxy-env.ts +128 -0
- package/src/redirect-proxy.ts +3 -125
- package/src/redis-backing.ts +19 -136
- package/src/runtime.ts +342 -107
- package/src/schema.ts +5 -4
- package/src/served-world.ts +3 -3
- package/src/twin-backing.ts +300 -0
- package/src/up-cli.ts +52 -3
package/dist/src/cli-commands.js
CHANGED
|
@@ -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
|
-
|
|
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;
|
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 }) =>
|
|
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,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
|
-
|
|
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() +
|
|
68
|
+
if (!deadline) deadline = Date.now() + graceMs;
|
|
66
69
|
timer ??= setInterval(check, 20);
|
|
67
70
|
check();
|
|
68
71
|
};
|
package/dist/src/configs.js
CHANGED
|
@@ -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')
|
package/dist/src/consumers.js
CHANGED
|
@@ -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) {
|
package/dist/src/covers.d.ts
CHANGED
|
@@ -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
|
|
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`
|