@nimbus-sh/core 0.2.0 → 0.4.0

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 (49) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +112 -0
  3. package/dist/_shared/tarball-stream.d.ts +93 -0
  4. package/dist/_shared/tarball-stream.d.ts.map +1 -0
  5. package/dist/_shared/tarball-stream.js +235 -0
  6. package/dist/_shared/tarball.d.ts +17 -0
  7. package/dist/_shared/tarball.d.ts.map +1 -0
  8. package/dist/_shared/tarball.js +39 -0
  9. package/dist/index.d.ts +3 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +1 -0
  12. package/dist/runtime/clang-runner.d.ts +38 -0
  13. package/dist/runtime/clang-runner.d.ts.map +1 -0
  14. package/dist/runtime/clang-runner.js +866 -0
  15. package/dist/runtime/facet-host.d.ts +12 -0
  16. package/dist/runtime/facet-host.d.ts.map +1 -1
  17. package/dist/runtime/local-facet-host.d.ts.map +1 -1
  18. package/dist/runtime/local-facet-host.js +29 -9
  19. package/dist/runtime/ruby-gems.d.ts +30 -0
  20. package/dist/runtime/ruby-gems.d.ts.map +1 -0
  21. package/dist/runtime/ruby-gems.js +636 -0
  22. package/dist/runtime/ruby-runner.d.ts +127 -0
  23. package/dist/runtime/ruby-runner.d.ts.map +1 -0
  24. package/dist/runtime/ruby-runner.js +1357 -0
  25. package/dist/runtime/runtime-package.d.ts +63 -0
  26. package/dist/runtime/runtime-package.d.ts.map +1 -0
  27. package/dist/runtime/runtime-package.js +66 -0
  28. package/dist/runtime/runtime-registry.d.ts +3 -2
  29. package/dist/runtime/runtime-registry.d.ts.map +1 -1
  30. package/dist/runtime/runtime-registry.js +1 -1
  31. package/dist/runtime/session-process-supervisor.d.ts +15 -0
  32. package/dist/runtime/session-process-supervisor.d.ts.map +1 -1
  33. package/dist/runtime/session-process-supervisor.js +30 -0
  34. package/dist/workspace/nimbus-workspace.d.ts +102 -22
  35. package/dist/workspace/nimbus-workspace.d.ts.map +1 -1
  36. package/dist/workspace/nimbus-workspace.js +197 -52
  37. package/package.json +4 -2
  38. package/src/_shared/tarball-stream.ts +263 -0
  39. package/src/_shared/tarball.ts +46 -0
  40. package/src/index.ts +7 -0
  41. package/src/runtime/clang-runner.ts +924 -0
  42. package/src/runtime/facet-host.ts +12 -0
  43. package/src/runtime/local-facet-host.ts +28 -8
  44. package/src/runtime/ruby-gems.ts +682 -0
  45. package/src/runtime/ruby-runner.ts +1484 -0
  46. package/src/runtime/runtime-package.ts +114 -0
  47. package/src/runtime/runtime-registry.ts +4 -3
  48. package/src/runtime/session-process-supervisor.ts +27 -0
  49. package/src/workspace/nimbus-workspace.ts +268 -63
@@ -0,0 +1,63 @@
1
+ /**
2
+ * runtime-package.ts — a runtime that arrived with the code, rather than over
3
+ * the network.
4
+ *
5
+ * `nimbus install <name>` reads a catalog out of R2 and writes the result into
6
+ * `~/.nimbus/runtimes/<name>/<version>/`. That is the Cloudflare deployment's
7
+ * answer to "where do the bytes come from", and it needs a bucket, a binding
8
+ * and a colo cache. An embedder who ran `npm i @nimbus-sh/runtime-bash` has
9
+ * already answered the same question: the bytes are on disk beside their
10
+ * manifest, fetched and integrity-checked by npm before any of this ran.
11
+ *
12
+ * So this is the second publisher, not the second package manager. It writes
13
+ * the SAME tree at the SAME path from the SAME manifest — `installRoot()`,
14
+ * `parseRuntimeManifest()`, one file per `manifest.files` entry — so
15
+ * `listInstalledManifests` and `rehydrateInstalledRuntimes` cannot tell which
16
+ * one ran, and a workspace behaves identically either way.
17
+ *
18
+ * Trust: R2 is verified against a digest chain rooted in a build-time pin
19
+ * because `caches.default` is shared across tenants and R2 keys are not
20
+ * content-addressed. A runtime package's root of trust is npm's own tarball
21
+ * integrity, which the install already checked; from there the manifest's
22
+ * per-file digests are re-verified here for exactly the reason the R2 path
23
+ * verifies them — these blobs are interpreters, so bytes that reach the
24
+ * filesystem are bytes that execute.
25
+ */
26
+ import type { CredentialedVfs } from '../vfs/sqlite-vfs.js';
27
+ import { type ManifestFile, type RuntimeManifest } from './runtime-manifest.js';
28
+ /**
29
+ * An installed npm package holding one runtime.
30
+ *
31
+ * The published packages (`@nimbus-sh/runtime-bash`,
32
+ * `@nimbus-sh/runtime-cpython`) are the implementations; each is a manifest,
33
+ * the content-addressed blobs it names, and the eight lines of `node:fs` that
34
+ * read them. The port is here rather than in those packages because the
35
+ * FILESYSTEM is what a runtime is, and this is the half that knows it.
36
+ *
37
+ * `readBlob` takes the whole manifest entry rather than a bare key, mirroring
38
+ * `fetchBlob` in the Cloudflare catalog: a key and the digest that vouches for
39
+ * it never travel as separate arguments, so there is no call in which they can
40
+ * disagree.
41
+ */
42
+ export interface RuntimePackage {
43
+ readonly manifest: RuntimeManifest;
44
+ readBlob(file: ManifestFile): Uint8Array | Promise<Uint8Array>;
45
+ }
46
+ export interface SeededRuntime {
47
+ readonly name: string;
48
+ readonly version: string;
49
+ /** VFS path of the install root, e.g. `home/user/.nimbus/runtimes/bash/5.2.37`. */
50
+ readonly root: string;
51
+ /** False when the runtime was already installed at `root` and nothing was written. */
52
+ readonly written: boolean;
53
+ }
54
+ /**
55
+ * Write a runtime package into the filesystem as an install.
56
+ *
57
+ * Idempotent on the same rule the package manager uses: a `manifest.json`
58
+ * already at the install root means the install completed, and the root
59
+ * carries the version, so a package upgrade lands beside its predecessor
60
+ * rather than half over it.
61
+ */
62
+ export declare function seedRuntimePackage(vfs: CredentialedVfs, homeDir: string, runtimePackage: RuntimePackage): Promise<SeededRuntime>;
63
+ //# sourceMappingURL=runtime-package.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime-package.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-package.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D,OAAO,EAEL,KAAK,YAAY,EACjB,KAAK,eAAe,EACrB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE,YAAY,GAAG,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC;CAChE;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;GAOG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,eAAe,EACpB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,cAAc,GAC7B,OAAO,CAAC,aAAa,CAAC,CAsBxB"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * runtime-package.ts — a runtime that arrived with the code, rather than over
3
+ * the network.
4
+ *
5
+ * `nimbus install <name>` reads a catalog out of R2 and writes the result into
6
+ * `~/.nimbus/runtimes/<name>/<version>/`. That is the Cloudflare deployment's
7
+ * answer to "where do the bytes come from", and it needs a bucket, a binding
8
+ * and a colo cache. An embedder who ran `npm i @nimbus-sh/runtime-bash` has
9
+ * already answered the same question: the bytes are on disk beside their
10
+ * manifest, fetched and integrity-checked by npm before any of this ran.
11
+ *
12
+ * So this is the second publisher, not the second package manager. It writes
13
+ * the SAME tree at the SAME path from the SAME manifest — `installRoot()`,
14
+ * `parseRuntimeManifest()`, one file per `manifest.files` entry — so
15
+ * `listInstalledManifests` and `rehydrateInstalledRuntimes` cannot tell which
16
+ * one ran, and a workspace behaves identically either way.
17
+ *
18
+ * Trust: R2 is verified against a digest chain rooted in a build-time pin
19
+ * because `caches.default` is shared across tenants and R2 keys are not
20
+ * content-addressed. A runtime package's root of trust is npm's own tarball
21
+ * integrity, which the install already checked; from there the manifest's
22
+ * per-file digests are re-verified here for exactly the reason the R2 path
23
+ * verifies them — these blobs are interpreters, so bytes that reach the
24
+ * filesystem are bytes that execute.
25
+ */
26
+ import { sha256Hex } from '../_shared/crypto.js';
27
+ import { installRoot } from './installed-runtimes.js';
28
+ import { parseRuntimeManifest, } from './runtime-manifest.js';
29
+ /**
30
+ * Write a runtime package into the filesystem as an install.
31
+ *
32
+ * Idempotent on the same rule the package manager uses: a `manifest.json`
33
+ * already at the install root means the install completed, and the root
34
+ * carries the version, so a package upgrade lands beside its predecessor
35
+ * rather than half over it.
36
+ */
37
+ export async function seedRuntimePackage(vfs, homeDir, runtimePackage) {
38
+ const manifest = parseRuntimeManifest(runtimePackage.manifest);
39
+ const root = installRoot(homeDir, manifest.name, manifest.version);
40
+ if (vfs.exists(`${root}/manifest.json`)) {
41
+ return { name: manifest.name, version: manifest.version, root, written: false };
42
+ }
43
+ vfs.mkdir(root, { recursive: true });
44
+ for (const file of manifest.files) {
45
+ const target = `${root}/${file.path}`;
46
+ const parent = target.slice(0, target.lastIndexOf('/'));
47
+ if (!vfs.exists(parent))
48
+ vfs.mkdir(parent, { recursive: true });
49
+ vfs.writeFile(target, await verifiedBlob(manifest, runtimePackage, file));
50
+ }
51
+ // Last, where the R2 installer writes it first: that one has `--reinstall`
52
+ // to force a redo, and this one has no verb at all, so a manifest sitting
53
+ // beside a half-written tree would report a broken runtime as installed for
54
+ // the life of the filesystem.
55
+ vfs.writeFile(`${root}/manifest.json`, JSON.stringify(manifest, null, 2));
56
+ return { name: manifest.name, version: manifest.version, root, written: true };
57
+ }
58
+ async function verifiedBlob(manifest, runtimePackage, file) {
59
+ const bytes = await runtimePackage.readBlob(file);
60
+ const actual = await sha256Hex(bytes);
61
+ if (actual !== file.sha256) {
62
+ throw new Error(`${manifest.name}@${manifest.version}: sha256 mismatch for ${file.path} — manifest expects `
63
+ + `${file.sha256}, ${file.content} holds ${actual}`);
64
+ }
65
+ return bytes;
66
+ }
@@ -154,8 +154,9 @@ export interface ShellRegistry {
154
154
  export declare function buildRuntimeHandler(spec: RuntimeSpec, ctx0: {
155
155
  vfs: SqliteVFS;
156
156
  /** Lazy esbuild initialiser. Called once per first .ts/.tsx/.jsx
157
- * invocation — the host owns the init lifecycle. */
158
- getEsbuild(): EsbuildService;
157
+ * invocation — the host owns the init lifecycle, including whether
158
+ * the module is loaded eagerly or on this call. */
159
+ getEsbuild(): EsbuildService | Promise<EsbuildService>;
159
160
  registry: ShellRegistry;
160
161
  }): (ctx: any) => Promise<number>;
161
162
  //# sourceMappingURL=runtime-registry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"runtime-registry.d.ts","sourceRoot":"","sources":["../../src/runtime/runtime-registry.ts"],"names":[],"mappings":"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"}
@@ -283,7 +283,7 @@ export function buildRuntimeHandler(spec, ctx0) {
283
283
  scriptExt === '.jsx' ||
284
284
  needsEsmTransform) {
285
285
  try {
286
- const eb = getEsbuild();
286
+ const eb = await getEsbuild();
287
287
  const loader = scriptExt === '.tsx' ? 'tsx' :
288
288
  scriptExt === '.jsx' ? 'jsx' :
289
289
  scriptExt === '.ts' ? 'ts' :
@@ -55,6 +55,8 @@ export declare class SessionProcessSupervisor {
55
55
  private terminators;
56
56
  /** Fires after every appendOutput/markExit once log persistence is wired. */
57
57
  private logActivity;
58
+ /** Fires once per pid on its first terminal transition; see setOnTerminal. */
59
+ private onTerminalCb;
58
60
  /** Allocate a PID and register a new process. */
59
61
  spawn(command: string, argv: string[], cwd: string, opts?: ProcessSpawnOptions): ProcessEntry;
60
62
  /** Mark an existing entry as long-running. Idempotent. */
@@ -76,6 +78,19 @@ export declare class SessionProcessSupervisor {
76
78
  private terminate;
77
79
  cred(pid: number): VfsCred;
78
80
  setUmask(pid: number, umask: number): number;
81
+ /**
82
+ * Observe every pid's FIRST transition out of `running`, whichever door it
83
+ * leaves by — exit(), kill(), a facet's self-reported exit, a timeout abort:
84
+ * all of them end here, which is what makes this one callback a complete
85
+ * seam for per-pid durable state (the resident-launch journal) that must be
86
+ * released exactly when the process ends and never before.
87
+ *
88
+ * One slot, owned by the FacetManager. A second subscriber would mean two
89
+ * owners of process-end policy; grow this into a list only when a second
90
+ * genuine owner exists.
91
+ */
92
+ setOnTerminal(cb: (pid: number) => void): void;
93
+ private fireTerminal;
79
94
  /** Mark a process as exited. First terminal state wins. */
80
95
  exit(pid: number, exitCode: number): void;
81
96
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"session-process-supervisor.d.ts","sourceRoot":"","sources":["../../src/runtime/session-process-supervisor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"session-process-supervisor.d.ts","sourceRoot":"","sources":["../../src/runtime/session-process-supervisor.ts"],"names":[],"mappings":"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"}
@@ -30,6 +30,8 @@ export class SessionProcessSupervisor {
30
30
  terminators = new Map();
31
31
  /** Fires after every appendOutput/markExit once log persistence is wired. */
32
32
  logActivity = null;
33
+ /** Fires once per pid on its first terminal transition; see setOnTerminal. */
34
+ onTerminalCb = null;
33
35
  // ── Lifecycle / PID authority ─────────────────────────────────────────
34
36
  /** Allocate a PID and register a new process. */
35
37
  spawn(command, argv, cwd, opts = {}) {
@@ -86,19 +88,47 @@ export class SessionProcessSupervisor {
86
88
  setUmask(pid, umask) {
87
89
  return this.table.setUmask(pid, umask);
88
90
  }
91
+ /**
92
+ * Observe every pid's FIRST transition out of `running`, whichever door it
93
+ * leaves by — exit(), kill(), a facet's self-reported exit, a timeout abort:
94
+ * all of them end here, which is what makes this one callback a complete
95
+ * seam for per-pid durable state (the resident-launch journal) that must be
96
+ * released exactly when the process ends and never before.
97
+ *
98
+ * One slot, owned by the FacetManager. A second subscriber would mean two
99
+ * owners of process-end policy; grow this into a list only when a second
100
+ * genuine owner exists.
101
+ */
102
+ setOnTerminal(cb) {
103
+ this.onTerminalCb = cb;
104
+ }
105
+ fireTerminal(pid, wasRunning) {
106
+ if (!wasRunning || !this.onTerminalCb)
107
+ return;
108
+ if (this.table.get(pid)?.state === 'running')
109
+ return;
110
+ try {
111
+ this.onTerminalCb(pid);
112
+ }
113
+ catch { /* the process is gone regardless */ }
114
+ }
89
115
  /** Mark a process as exited. First terminal state wins. */
90
116
  exit(pid, exitCode) {
117
+ const wasRunning = this.table.get(pid)?.state === 'running';
91
118
  this.table.exit(pid, exitCode);
92
119
  this.terminators.delete(pid);
120
+ this.fireTerminal(pid, wasRunning);
93
121
  }
94
122
  /**
95
123
  * Mark a process as killed and tear down its input channel so queued
96
124
  * stdin can't outlive the process.
97
125
  */
98
126
  kill(pid) {
127
+ const wasRunning = this.table.get(pid)?.state === 'running';
99
128
  const killed = this.table.kill(pid);
100
129
  this.terminate(pid);
101
130
  this.input.close(pid);
131
+ this.fireTerminal(pid, wasRunning);
102
132
  return killed;
103
133
  }
104
134
  /** Clean up exited processes older than maxAge ms. */
@@ -3,24 +3,32 @@
3
3
  *
4
4
  * A workspace is a durable filesystem plus a shell over it. It owns no
5
5
  * transport, no session, no socket and no Durable Object: the host supplies
6
- * SQLite and gets back `.fs` and `.exec`. That is what makes it embeddable in
7
- * a Durable Object that is already busy powering something else, and what
8
- * makes it runnable in a plain bun process over `bun:sqlite`.
6
+ * the filesystem and gets back `.fs`, `.exec`, and a command registry to add
7
+ * to. That is what makes it embeddable in a Durable Object that is already
8
+ * busy powering something else, and what makes it runnable in a plain bun
9
+ * process over `bun:sqlite`.
9
10
  *
10
- * The composition here is not new. It is the one `session/init.ts` performs,
11
- * lifted out of the session so there is one recipe rather than one per caller
12
- * — five unit tests were already hand-rolling it, one of them reaching a
13
- * private field to do so. The boot itself still belongs to `Sandbox.create`;
14
- * this adds only what a durable, credentialed filesystem needs on top and
15
- * nothing the sandbox already knows how to do.
11
+ * The composition here is not new. It is the one `session/init.ts` performed
12
+ * inline, lifted out of the session so there is one recipe rather than one per
13
+ * caller the session now reads its kernel, shell and registry off a
14
+ * workspace, and five unit tests were already hand-rolling the same steps.
15
+ *
16
+ * Deliberately not `Sandbox.create`, which is the lifo demo sandbox's boot
17
+ * rather than this one: it registers `systemctl`, `tunnel` and the network
18
+ * command set, boots enabled service units out of `/etc/systemd`, and starts
19
+ * the shell before the host can register a command of its own. A session
20
+ * routed through it would silently acquire all of that.
16
21
  */
22
+ import { Kernel } from '../substrate/lifo/kernel/index.js';
23
+ import { Shell } from '../substrate/lifo/shell/Shell.js';
24
+ import type { ShellCommandIdentity } from '../substrate/lifo/shell/Shell.js';
25
+ import type { CommandRegistry } from '../substrate/lifo/commands/registry.js';
17
26
  import type { CommandResult, RunOptions, SandboxFs } from '../substrate/lifo/sandbox/types.js';
18
- import type { Kernel } from '../substrate/lifo/kernel/index.js';
19
- import type { Shell } from '../substrate/lifo/shell/Shell.js';
20
27
  import type { ITerminal } from '../substrate/lifo/terminal/ITerminal.js';
21
28
  import { SqliteVFS } from '../vfs/sqlite-vfs.js';
22
29
  import type { SqlDatabase, TransactionHost } from '../runtime/os-contracts.js';
23
30
  import type { FacetHost } from '../runtime/facet-host.js';
31
+ import { type RuntimePackage } from '../runtime/runtime-package.js';
24
32
  export interface NimbusWorkspaceOptions {
25
33
  /** The host's SQLite. In a Durable Object: `ctx.storage.sql`. */
26
34
  readonly sql: SqlDatabase;
@@ -33,18 +41,55 @@ export interface NimbusWorkspaceOptions {
33
41
  */
34
42
  readonly transactions?: TransactionHost;
35
43
  /**
36
- * Process-id generation. The workspace revokes every append capability at
37
- * or below `generation * 1_000_000` before serving anything, so a value
38
- * that repeats across restarts hands a dead process live write authority.
39
- * Hosts that persist must supply a counter that never repeats.
44
+ * The filesystem already open over `sql`, for a host that has one.
45
+ *
46
+ * A Durable Object does: its installer, its git commands and its RPC
47
+ * surfaces read those rows without a shell in sight, and they hold a
48
+ * SqliteVFS from the first request that needed one. It must hand over THAT
49
+ * one — a second SqliteVFS over the same database is a second cache, and
50
+ * one of the two will serve a stale read. Such a host has also already
51
+ * revoked the previous generation's append writers, which is why that only
52
+ * happens below when the workspace is the one opening the filesystem.
53
+ */
54
+ readonly vfs?: SqliteVFS;
55
+ /**
56
+ * Process-id generation. Two things rest on it: the workspace revokes every
57
+ * append capability at or below `generation * 1_000_000` before serving
58
+ * anything, and the wasm runner allocates pids above it. A value that
59
+ * repeats across restarts hands a dead process live write authority, so
60
+ * hosts that persist must supply a counter that never repeats.
40
61
  */
41
62
  readonly generation?: number;
42
63
  /** Top-level directories backed by `sql`. Defaults to DEFAULT_MOUNT_POINTS. */
43
64
  readonly mounts?: readonly string[];
65
+ /** Overlaid on the Nimbus default environment. */
44
66
  readonly env?: Record<string, string>;
45
67
  readonly cwd?: string;
46
68
  /** Absent means headless: `.exec` captures output and nothing is drawn. */
47
69
  readonly terminal?: ITerminal;
70
+ /**
71
+ * Who the shell acts as, when the host keeps a process table that can answer
72
+ * for it. Absent, commands run as uid 1000 with a umask of 022 and no
73
+ * process behind them, which is the Shell's own default.
74
+ */
75
+ readonly identity?: ShellCommandIdentity;
76
+ /**
77
+ * Language runtimes to install before the shell is served, as npm packages
78
+ * the embedder imported (`@nimbus-sh/runtime-bash`,
79
+ * `@nimbus-sh/runtime-cpython`).
80
+ *
81
+ * A Durable Object gets these from R2 through `nimbus install`; an embedder
82
+ * off Cloudflare has no bucket and needs none, because npm already fetched
83
+ * and integrity-checked the same bytes. Both write the same tree at the same
84
+ * path, so what is installed here is indistinguishable from what is
85
+ * installed there — see runtime/runtime-package.ts.
86
+ *
87
+ * Independent of `facets`: this decides what the filesystem HOLDS, and
88
+ * `facets` decides whether anything can run it. A workspace given runtimes
89
+ * and no facet host installs them and still answers "command not found",
90
+ * because it still has nothing that could compile a module.
91
+ */
92
+ readonly runtimes?: readonly RuntimePackage[];
48
93
  /**
49
94
  * Where WebAssembly runs.
50
95
  *
@@ -56,21 +101,24 @@ export interface NimbusWorkspaceOptions {
56
101
  * and `wasm-runner` joins them, which is what makes a `\0asm` file on the
57
102
  * PATH executable (see shell/exec-dispatch.ts).
58
103
  *
59
- * A Durable Object passes the workerd host (`@nimbus-sh/worker`'s
60
- * `loaderFacetHost`); a plain process passes `localFacetHost()`.
104
+ * A plain process passes `localFacetHost()`. A Durable Object passes nothing
105
+ * here and registers its own runners instead, because the ones it needs
106
+ * carry REPLs and a resident-process substrate this cannot reach.
61
107
  */
62
108
  readonly facets?: FacetHost;
63
109
  }
64
110
  /**
65
111
  * A durable filesystem and a shell over it.
66
112
  *
67
- * Created with {@link NimbusWorkspace.create} rather than `new` because the
68
- * boot it delegates to sources `/etc/profile`, which is genuinely async. A
69
- * Durable Object constructor cannot await, so a host constructs the workspace
70
- * in its first request rather than in its constructor.
113
+ * The composition itself is synchronous. {@link create} awaits only the
114
+ * optional work installing runtime packages, loading the wasm runner modules
115
+ * so a host that asks for neither is never suspended between mounting the
116
+ * filesystem and registering the commands. A Durable Object needs that: it
117
+ * must not take delivery of an event with a half-built shell. The remaining
118
+ * async step, running the user's login files, is {@link start}, which the host
119
+ * calls once its own commands are in place.
71
120
  */
72
121
  export declare class NimbusWorkspace {
73
- private readonly sandbox;
74
122
  private readonly sql;
75
123
  /**
76
124
  * Credentialed and mount-aware. Acts as the session user, never as the
@@ -82,9 +130,26 @@ export declare class NimbusWorkspace {
82
130
  readonly vfs: SqliteVFS;
83
131
  readonly kernel: Kernel;
84
132
  readonly shell: Shell;
133
+ /** What the shell resolves a command name against. A host adds its own. */
134
+ readonly registry: CommandRegistry;
135
+ /**
136
+ * The environment the shell was composed with. The shell's own copy drifts
137
+ * from this one the moment the user exports anything; this is what a host
138
+ * hands to a subordinate shell it starts itself.
139
+ */
140
+ readonly env: Record<string, string>;
141
+ private readonly commands;
85
142
  private constructor();
86
143
  static create(options: NimbusWorkspaceOptions): Promise<NimbusWorkspace>;
87
144
  exec(command: string, options?: RunOptions): Promise<CommandResult>;
145
+ /**
146
+ * Apply the login files, and begin reading the terminal when there is one.
147
+ *
148
+ * Separate from {@link create} because a host with commands of its own must
149
+ * register them first: `/etc/profile` and `~/.nimbusrc` are the user's
150
+ * files, and either may name a command the host has yet to supply.
151
+ */
152
+ start(): Promise<void>;
88
153
  /**
89
154
  * Files, directories and bytes this workspace occupies.
90
155
  *
@@ -107,4 +172,19 @@ export declare class NimbusWorkspace {
107
172
  */
108
173
  destroy(): void;
109
174
  }
175
+ /**
176
+ * The directories and account files the shell cannot start without.
177
+ *
178
+ * Idempotent by construction: every write is guarded by an existence check, so
179
+ * a workspace reopened over a populated database keeps whatever the user did
180
+ * to these files. `/etc/passwd` and `/etc/group` are load-bearing rather than
181
+ * decorative — `id`, `chown` and `su` resolve names through them.
182
+ *
183
+ * What a PRODUCT puts in a fresh filesystem — a banner, a welcome file, a
184
+ * starter app — is not here. This is the base an OS needs in order to boot,
185
+ * and it is exported because a host may need the filesystem before it needs a
186
+ * shell: the Nimbus session seeds its starter project for a browser that hits
187
+ * `/preview` without ever opening a terminal.
188
+ */
189
+ export declare function seedBaseFilesystem(vfs: SqliteVFS, mounts: readonly string[]): void;
110
190
  //# sourceMappingURL=nimbus-workspace.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/F,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mCAAmC,CAAC;AAChE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACzE,OAAO,EAAE,SAAS,EAAqB,MAAM,sBAAsB,CAAC;AAGpE,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG/E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAU1D,MAAM,WAAW,sBAAsB;IACrC,iEAAiE;IACjE,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,eAAe,CAAC;IACxC;;;;;OAKG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACpC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAC;IAC9B;;;;;;;;;;;;;OAaG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAaxB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAExB,OAAO,CAAC,QAAQ,CAAC,GAAG;IAdtB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,4EAA4E;IAC5E,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IAEtB,OAAO;WAiBM,MAAM,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,eAAe,CAAC;IAmC9E,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC;IAInE;;;;;;OAMG;IACH,KAAK,IAAI;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE;IAK3D;;;;;;;OAOG;IACH,OAAO,IAAI,IAAI;CAKhB"}
1
+ {"version":3,"file":"nimbus-workspace.d.ts","sourceRoot":"","sources":["../../src/workspace/nimbus-workspace.ts"],"names":[],"mappings":"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"}