@nimbus-sh/worker 0.2.0 → 0.2.2
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/facets/manager.d.ts +47 -2
- package/dist/facets/manager.d.ts.map +1 -1
- package/dist/facets/manager.js +139 -15
- package/dist/loaders/generated-workers.d.ts +2 -2
- package/dist/loaders/generated-workers.d.ts.map +1 -1
- package/dist/loaders/generated-workers.js +3 -3
- package/dist/runtime/bash-repl.d.ts +1 -1
- package/dist/runtime/bash-repl.d.ts.map +1 -1
- package/dist/runtime/bash-repl.js +3 -2
- package/dist/runtime/cpython-resident.d.ts +27 -0
- package/dist/runtime/cpython-resident.d.ts.map +1 -0
- package/dist/runtime/cpython-resident.js +133 -0
- package/dist/runtime/facet-loader-host.d.ts +16 -1
- package/dist/runtime/facet-loader-host.d.ts.map +1 -1
- package/dist/runtime/facet-loader-host.js +42 -0
- package/dist/runtime/package-manager.d.ts +3 -58
- package/dist/runtime/package-manager.d.ts.map +1 -1
- package/dist/runtime/package-manager.js +6 -143
- package/dist/runtime/python-repl.d.ts +1 -1
- package/dist/runtime/python-repl.d.ts.map +1 -1
- package/dist/runtime/python-repl.js +2 -2
- package/dist/runtime/ruby-repl.js +1 -1
- package/dist/runtime/ruby-resident.d.ts +20 -0
- package/dist/runtime/ruby-resident.d.ts.map +1 -0
- package/dist/runtime/ruby-resident.js +278 -0
- package/dist/runtime/runtime-catalog.d.ts +4 -80
- package/dist/runtime/runtime-catalog.d.ts.map +1 -1
- package/dist/runtime/runtime-catalog.js +4 -67
- package/dist/session/init.d.ts +16 -7
- package/dist/session/init.d.ts.map +1 -1
- package/dist/session/init.js +210 -249
- package/dist/session/keys.d.ts +13 -0
- package/dist/session/keys.d.ts.map +1 -1
- package/dist/session/keys.js +13 -0
- package/dist/session/nimbus-session.d.ts +30 -2
- package/dist/session/nimbus-session.d.ts.map +1 -1
- package/dist/session/nimbus-session.js +49 -58
- package/dist/session/programmatic.d.ts +3 -3
- package/dist/session/programmatic.d.ts.map +1 -1
- package/dist/session/programmatic.js +3 -2
- package/dist/session/routes.js +1 -1
- package/dist/session/supervisor-rpc.d.ts +6 -13
- package/dist/session/supervisor-rpc.d.ts.map +1 -1
- package/dist/session/supervisor-rpc.js +7 -17
- package/package.json +2 -2
- package/scripts/bundle-facet-workers.mjs +9 -8
- package/scripts/bundle-runtime.mjs +276 -44
- package/dist/npm/tarball-stream.d.ts +0 -93
- package/dist/npm/tarball-stream.d.ts.map +0 -1
- package/dist/npm/tarball-stream.js +0 -235
- package/dist/npm/tarball.d.ts +0 -34
- package/dist/npm/tarball.d.ts.map +0 -1
- package/dist/npm/tarball.js +0 -97
- package/dist/runtime/bash-runner.d.ts +0 -63
- package/dist/runtime/bash-runner.d.ts.map +0 -1
- package/dist/runtime/bash-runner.generated.d.ts +0 -14
- package/dist/runtime/bash-runner.generated.d.ts.map +0 -1
- package/dist/runtime/bash-runner.generated.js +0 -13
- package/dist/runtime/bash-runner.js +0 -291
- package/dist/runtime/clang-runner.d.ts +0 -37
- package/dist/runtime/clang-runner.d.ts.map +0 -1
- package/dist/runtime/clang-runner.js +0 -865
- package/dist/runtime/cpython-runner.d.ts +0 -71
- package/dist/runtime/cpython-runner.d.ts.map +0 -1
- package/dist/runtime/cpython-runner.js +0 -535
- package/dist/runtime/python-pip.d.ts +0 -38
- package/dist/runtime/python-pip.d.ts.map +0 -1
- package/dist/runtime/python-pip.js +0 -1063
- package/dist/runtime/ruby-gems.d.ts +0 -30
- package/dist/runtime/ruby-gems.d.ts.map +0 -1
- package/dist/runtime/ruby-gems.js +0 -636
- package/dist/runtime/ruby-runner.d.ts +0 -75
- package/dist/runtime/ruby-runner.d.ts.map +0 -1
- package/dist/runtime/ruby-runner.js +0 -1577
- package/dist/runtime/runtime-registry.d.ts +0 -162
- package/dist/runtime/runtime-registry.d.ts.map +0 -1
- package/dist/runtime/runtime-registry.js +0 -364
- package/dist/runtime/virtual-socket-kernel.generated.d.ts +0 -14
- package/dist/runtime/virtual-socket-kernel.generated.d.ts.map +0 -1
- package/dist/runtime/virtual-socket-kernel.generated.js +0 -13
- package/dist/runtime/wasm-runner.d.ts +0 -70
- package/dist/runtime/wasm-runner.d.ts.map +0 -1
- package/dist/runtime/wasm-runner.js +0 -664
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cpython-runner.ts — `python` / `python3` / `pip`, on CPython 3.13 built for
|
|
3
|
-
* wasm32-wasi.
|
|
4
|
-
*
|
|
5
|
-
* This replaces the Pyodide runner, and the reason is not the interpreter: it
|
|
6
|
-
* is the filesystem. Pyodide is CPython built with Emscripten, so it brings its
|
|
7
|
-
* own MEMFS, and every invocation had to copy the session's files in and diff
|
|
8
|
-
* them back out through vfs-snapshot.ts. That made Python the last runtime with
|
|
9
|
-
* a private, parallel filesystem. This build talks to runtime/wasi/preamble.ts
|
|
10
|
-
* like clang, bash and ruby do — `open()` in Python is the same syscall as
|
|
11
|
-
* `open()` in C — so there is nothing to copy and nothing to diff.
|
|
12
|
-
*
|
|
13
|
-
* What follows from that:
|
|
14
|
-
* - manifestVfs, not snapshotVfs: the facet is given sizes and modes and
|
|
15
|
-
* demand-loads the handful of files the program actually opens.
|
|
16
|
-
* - supervisorPid, not omitSupervisor: a pool without a supervisor can read
|
|
17
|
-
* the seeded manifest and can never write anything back. It looks like it
|
|
18
|
-
* works.
|
|
19
|
-
* - No Python-level socket shim. CPython's _socket is real here, over
|
|
20
|
-
* nimbus-net.c and the host's synthetic paths, so loopback is ordinary
|
|
21
|
-
* socket code rather than a monkey-patch.
|
|
22
|
-
*
|
|
23
|
-
* The interpreter is a WASI reactor (see packages/worker/wasm/python), because
|
|
24
|
-
* a command module's _start runs once and that only covers `python script.py`.
|
|
25
|
-
*
|
|
26
|
-
* FOUR THINGS THIS RUNTIME REDISCOVERED THE HARD WAY, ALL OF WHICH ruby-runner
|
|
27
|
-
* ALREADY KNEW. Read the list rather than finding a fifth:
|
|
28
|
-
* 1. Every entry into the VM goes through WebAssembly.promising, not only the
|
|
29
|
-
* calls known to park — a Suspending import traps on an unpromised stack
|
|
30
|
-
* even when it returns a plain integer.
|
|
31
|
-
* 2. The supervisor is adopted AFTER __wasiInitFS, which clears it on purpose,
|
|
32
|
-
* and the facet drains queued writes in a `finally`.
|
|
33
|
-
* 3. modes are seeded `{ '': 7, tmp: 7, home: 7 }` ahead of the manifest,
|
|
34
|
-
* because manifestVfs's walk skips the empty root — without it the preopen
|
|
35
|
-
* at '/' is mode 0 and every traversal under it is EACCES.
|
|
36
|
-
* 4. The loader pool is built per invocation, never cached: supervisorPid is
|
|
37
|
-
* baked into the SUPERVISOR binding at construction, so a held pool hands
|
|
38
|
-
* every later caller the first caller's write credential.
|
|
39
|
-
*
|
|
40
|
-
* One ordering in cpythonRunFacetFn is load-bearing and looks redundant: the
|
|
41
|
-
* supervisor stub is PUBLISHED on globalThis and only then adopted, because
|
|
42
|
-
* __wasiInitFS clears the adoption on purpose and the boot re-adopts it from
|
|
43
|
-
* there afterwards. Adopting once at the entry and deleting the Reflect.set
|
|
44
|
-
* leaves a guest that reads the seeded filesystem and silently writes nowhere —
|
|
45
|
-
* every write queued, none landed, no error anywhere. Ruby carries the same
|
|
46
|
-
* pair for the same reason. The drain in the `finally` is the other half: a
|
|
47
|
-
* program that wrote a file and then raised still wrote the file.
|
|
48
|
-
*/
|
|
49
|
-
import type { FacetManager } from '../facets/manager.js';
|
|
50
|
-
import type { CommandContext } from '@nimbus-sh/core/substrate/lifo/commands/types.js';
|
|
51
|
-
import type { SqliteVFS } from '@nimbus-sh/core/vfs/sqlite-vfs.js';
|
|
52
|
-
import type { RuntimeManifest } from './runtime-catalog.js';
|
|
53
|
-
/**
|
|
54
|
-
* The one canonical facet preamble. Composed in exactly one place: a
|
|
55
|
-
* hand-rolled second copy is how ruby-repl once drifted into booting a VM whose
|
|
56
|
-
* language prelude was missing.
|
|
57
|
-
*/
|
|
58
|
-
export declare function buildCPythonPreamble(): string;
|
|
59
|
-
/**
|
|
60
|
-
* The worker source for a resident Python process. Same shape as
|
|
61
|
-
* buildRubySocketProcessWorker: the socket kernel and the interpreter live in a
|
|
62
|
-
* DurableObject, startProcess runs the program until it stops, and inbound
|
|
63
|
-
* requests arrive on handleHttpRequest and are dispatched into the server the
|
|
64
|
-
* program registered before exiting.
|
|
65
|
-
*/
|
|
66
|
-
export declare function buildCPythonSocketProcessWorker(preamble: string): string;
|
|
67
|
-
export declare function makeCPythonRunnerFactory(deps: {
|
|
68
|
-
facetMgr: FacetManager;
|
|
69
|
-
vfs: SqliteVFS;
|
|
70
|
-
}): (manifest: RuntimeManifest, installRoot: string, binName: string, binKind: string | undefined) => (ctx: CommandContext) => Promise<number>;
|
|
71
|
-
//# sourceMappingURL=cpython-runner.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cpython-runner.d.ts","sourceRoot":"","sources":["../../src/runtime/cpython-runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+CG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kDAAkD,CAAC;AAEvF,OAAO,KAAK,EAAmB,SAAS,EAAE,MAAM,mCAAmC,CAAC;AAcpF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAuB5D;;;;GAIG;AACH,wBAAgB,oBAAoB,IAAI,MAAM,CAM7C;AAkFD;;;;;;GAMG;AACH,wBAAgB,+BAA+B,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAwDxE;AA+GD,wBAAgB,wBAAwB,CAAC,IAAI,EAAE;IAC7C,QAAQ,EAAE,YAAY,CAAC;IACvB,GAAG,EAAE,SAAS,CAAC;CAChB,GAAG,CAAC,QAAQ,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,SAAS,KAC7F,CAAC,GAAG,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAuP3C"}
|
|
@@ -1,535 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* cpython-runner.ts — `python` / `python3` / `pip`, on CPython 3.13 built for
|
|
3
|
-
* wasm32-wasi.
|
|
4
|
-
*
|
|
5
|
-
* This replaces the Pyodide runner, and the reason is not the interpreter: it
|
|
6
|
-
* is the filesystem. Pyodide is CPython built with Emscripten, so it brings its
|
|
7
|
-
* own MEMFS, and every invocation had to copy the session's files in and diff
|
|
8
|
-
* them back out through vfs-snapshot.ts. That made Python the last runtime with
|
|
9
|
-
* a private, parallel filesystem. This build talks to runtime/wasi/preamble.ts
|
|
10
|
-
* like clang, bash and ruby do — `open()` in Python is the same syscall as
|
|
11
|
-
* `open()` in C — so there is nothing to copy and nothing to diff.
|
|
12
|
-
*
|
|
13
|
-
* What follows from that:
|
|
14
|
-
* - manifestVfs, not snapshotVfs: the facet is given sizes and modes and
|
|
15
|
-
* demand-loads the handful of files the program actually opens.
|
|
16
|
-
* - supervisorPid, not omitSupervisor: a pool without a supervisor can read
|
|
17
|
-
* the seeded manifest and can never write anything back. It looks like it
|
|
18
|
-
* works.
|
|
19
|
-
* - No Python-level socket shim. CPython's _socket is real here, over
|
|
20
|
-
* nimbus-net.c and the host's synthetic paths, so loopback is ordinary
|
|
21
|
-
* socket code rather than a monkey-patch.
|
|
22
|
-
*
|
|
23
|
-
* The interpreter is a WASI reactor (see packages/worker/wasm/python), because
|
|
24
|
-
* a command module's _start runs once and that only covers `python script.py`.
|
|
25
|
-
*
|
|
26
|
-
* FOUR THINGS THIS RUNTIME REDISCOVERED THE HARD WAY, ALL OF WHICH ruby-runner
|
|
27
|
-
* ALREADY KNEW. Read the list rather than finding a fifth:
|
|
28
|
-
* 1. Every entry into the VM goes through WebAssembly.promising, not only the
|
|
29
|
-
* calls known to park — a Suspending import traps on an unpromised stack
|
|
30
|
-
* even when it returns a plain integer.
|
|
31
|
-
* 2. The supervisor is adopted AFTER __wasiInitFS, which clears it on purpose,
|
|
32
|
-
* and the facet drains queued writes in a `finally`.
|
|
33
|
-
* 3. modes are seeded `{ '': 7, tmp: 7, home: 7 }` ahead of the manifest,
|
|
34
|
-
* because manifestVfs's walk skips the empty root — without it the preopen
|
|
35
|
-
* at '/' is mode 0 and every traversal under it is EACCES.
|
|
36
|
-
* 4. The loader pool is built per invocation, never cached: supervisorPid is
|
|
37
|
-
* baked into the SUPERVISOR binding at construction, so a held pool hands
|
|
38
|
-
* every later caller the first caller's write credential.
|
|
39
|
-
*
|
|
40
|
-
* One ordering in cpythonRunFacetFn is load-bearing and looks redundant: the
|
|
41
|
-
* supervisor stub is PUBLISHED on globalThis and only then adopted, because
|
|
42
|
-
* __wasiInitFS clears the adoption on purpose and the boot re-adopts it from
|
|
43
|
-
* there afterwards. Adopting once at the entry and deleting the Reflect.set
|
|
44
|
-
* leaves a guest that reads the seeded filesystem and silently writes nowhere —
|
|
45
|
-
* every write queued, none landed, no error anywhere. Ruby carries the same
|
|
46
|
-
* pair for the same reason. The drain in the `finally` is the other half: a
|
|
47
|
-
* program that wrote a file and then raised still wrote the file.
|
|
48
|
-
*/
|
|
49
|
-
import { resolveVfsPath } from '@nimbus-sh/core/vfs/path.js';
|
|
50
|
-
import { z } from 'zod/v4';
|
|
51
|
-
import { hasLeadingCliFlag } from '@nimbus-sh/core/runtime/cli-flags.js';
|
|
52
|
-
import { CPYTHON_PREAMBLE_TAIL } from '@nimbus-sh/core/runtime/cpython-preamble.js';
|
|
53
|
-
import { PYTHON_SERVER_ADAPTER } from '@nimbus-sh/core/runtime/python-server-adapter.js';
|
|
54
|
-
import { getFacetManagerLoaderHost } from './facet-loader-host.js';
|
|
55
|
-
import { requireVfsCred } from '@nimbus-sh/core/runtime/os-contracts.js';
|
|
56
|
-
import { buildPipInvocation, PYTHON_SITE_PACKAGES_ROOT, sessionUsesSciVariant, } from './python-pip.js';
|
|
57
|
-
import { manifestVfs } from '@nimbus-sh/core/runtime/vfs-manifest.js';
|
|
58
|
-
import { VIRTUAL_SOCKET_KERNEL_SRC } from './virtual-socket-kernel.generated.js';
|
|
59
|
-
import { WASI_INSTANCE_PREAMBLE_SRC } from '@nimbus-sh/core/runtime/wasi-instance.js';
|
|
60
|
-
const PYTHON_VERSION_FLAGS = new Set(['--version', '-V']);
|
|
61
|
-
const PYTHON_HELP_FLAGS = new Set(['--help', '-h']);
|
|
62
|
-
/** Where `nimbus install python` stages the interpreter inside the session. */
|
|
63
|
-
const CPYTHON_WASM_REL = 'share/cpython/python.wasm';
|
|
64
|
-
/**
|
|
65
|
-
* The same interpreter with numpy and markupsafe's C speedups linked in, and
|
|
66
|
-
* their Python half. wasm32-wasi has no dlopen, so a compiled package is either
|
|
67
|
-
* in the binary or unavailable; EXTENSIONS.md has why that beat a runtime
|
|
68
|
-
* linker. Chosen per invocation from what the session has installed, so a
|
|
69
|
-
* session that installed none of it pays none of the 7.8 MiB.
|
|
70
|
-
*/
|
|
71
|
-
const CPYTHON_SCI_WASM_REL = 'share/cpython/python-sci.wasm';
|
|
72
|
-
const CPYTHON_SCI_PACKAGES_REL = 'lib/sci-packages.zip';
|
|
73
|
-
const CPYTHON_STDLIB_REL = 'lib/python313.zip';
|
|
74
|
-
const CPYTHON_CACERT_REL = 'etc/ssl/cert.pem';
|
|
75
|
-
/**
|
|
76
|
-
* The one canonical facet preamble. Composed in exactly one place: a
|
|
77
|
-
* hand-rolled second copy is how ruby-repl once drifted into booting a VM whose
|
|
78
|
-
* language prelude was missing.
|
|
79
|
-
*/
|
|
80
|
-
export function buildCPythonPreamble() {
|
|
81
|
-
return [
|
|
82
|
-
VIRTUAL_SOCKET_KERNEL_SRC,
|
|
83
|
-
WASI_INSTANCE_PREAMBLE_SRC,
|
|
84
|
-
CPYTHON_PREAMBLE_TAIL,
|
|
85
|
-
].join('\n');
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Python's CLI, as far as a sandbox needs it. Flags that only affect an
|
|
89
|
-
* interactive tty or bytecode caching are accepted and ignored; anything else
|
|
90
|
-
* is refused by name rather than silently doing something different.
|
|
91
|
-
*/
|
|
92
|
-
function parsePythonArgv(argv) {
|
|
93
|
-
const fail = (error) => ({ mode: 'inline', inlineCode: '', scriptPath: '', scriptArgs: [], exitCode: 2, error });
|
|
94
|
-
let i = 0;
|
|
95
|
-
while (i < argv.length) {
|
|
96
|
-
const a = argv[i];
|
|
97
|
-
if (a === '-c') {
|
|
98
|
-
const code = argv[i + 1];
|
|
99
|
-
if (code === undefined)
|
|
100
|
-
return fail('Argument expected for the -c option');
|
|
101
|
-
return { mode: 'inline', inlineCode: code, scriptPath: '', scriptArgs: argv.slice(i + 2), exitCode: 0 };
|
|
102
|
-
}
|
|
103
|
-
if (a === '-m') {
|
|
104
|
-
const mod = argv[i + 1];
|
|
105
|
-
if (mod === undefined)
|
|
106
|
-
return fail('Argument expected for the -m option');
|
|
107
|
-
const rest = argv.slice(i + 2);
|
|
108
|
-
const inlineCode = [
|
|
109
|
-
'import runpy, sys',
|
|
110
|
-
`sys.argv = ${JSON.stringify([mod, ...rest])}`,
|
|
111
|
-
`runpy.run_module(${JSON.stringify(mod)}, run_name='__main__', alter_sys=True)`,
|
|
112
|
-
].join('\n');
|
|
113
|
-
return { mode: 'inline', inlineCode, scriptPath: '', scriptArgs: rest, exitCode: 0 };
|
|
114
|
-
}
|
|
115
|
-
if (a === '-') {
|
|
116
|
-
return { mode: 'stdin', inlineCode: '', scriptPath: '-', scriptArgs: argv.slice(i + 1), exitCode: 0 };
|
|
117
|
-
}
|
|
118
|
-
if (!a.startsWith('-')) {
|
|
119
|
-
return { mode: 'script', inlineCode: '', scriptPath: a, scriptArgs: argv.slice(i + 1), exitCode: 0 };
|
|
120
|
-
}
|
|
121
|
-
if (/^-[OBuEItcsx]+$/.test(a)) {
|
|
122
|
-
i++;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
return fail(`unknown option: ${a}`);
|
|
126
|
-
}
|
|
127
|
-
// No mode argument. init.ts routes a bare `python` to the REPL before it gets
|
|
128
|
-
// here, so reaching this point means flags without a program.
|
|
129
|
-
return fail("no program given. Use 'python -c \"code\"', 'python -m <module>' or 'python script.py'.");
|
|
130
|
-
}
|
|
131
|
-
/**
|
|
132
|
-
* Whether this invocation should get a resident process rather than a one-shot.
|
|
133
|
-
* A script or `-m` can bind a port and keep serving; pip never does.
|
|
134
|
-
*/
|
|
135
|
-
function shouldRunAsResidentProcess(argv, parsed, pipMode) {
|
|
136
|
-
if (pipMode)
|
|
137
|
-
return false;
|
|
138
|
-
if (parsed.mode === 'script')
|
|
139
|
-
return true;
|
|
140
|
-
if (argv[0] === '-m' && argv[1] && argv[1] !== 'pip')
|
|
141
|
-
return true;
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
144
|
-
/** `python -m pip ...` is pip, reached the long way round. */
|
|
145
|
-
async function buildPythonModulePipInvocation(argv, cwd, vfs, runtimeContext) {
|
|
146
|
-
if (argv[0] !== '-m' || argv[1] !== 'pip')
|
|
147
|
-
return { mode: 'none', code: '', exitCode: 0 };
|
|
148
|
-
return await buildPipInvocation(argv.slice(2), 'pip', cwd, vfs, runtimeContext);
|
|
149
|
-
}
|
|
150
|
-
function toArrayBuffer(bytes) {
|
|
151
|
-
return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
|
|
152
|
-
}
|
|
153
|
-
function errorMessage(error) {
|
|
154
|
-
return error instanceof Error ? error.message : String(error);
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* The worker source for a resident Python process. Same shape as
|
|
158
|
-
* buildRubySocketProcessWorker: the socket kernel and the interpreter live in a
|
|
159
|
-
* DurableObject, startProcess runs the program until it stops, and inbound
|
|
160
|
-
* requests arrive on handleHttpRequest and are dispatched into the server the
|
|
161
|
-
* program registered before exiting.
|
|
162
|
-
*/
|
|
163
|
-
export function buildCPythonSocketProcessWorker(preamble) {
|
|
164
|
-
return [
|
|
165
|
-
'import { DurableObject } from "cloudflare:workers";',
|
|
166
|
-
// The module arrives by path (vfsWasmModules) and has to be published where
|
|
167
|
-
// the preamble looks for it. Without this the facet boots and the first
|
|
168
|
-
// thing it says is "python.wasm was not supplied to this facet".
|
|
169
|
-
"import __NIMBUS_WASM_python from './python.wasm';",
|
|
170
|
-
'globalThis.__NIMBUS_WASM = globalThis.__NIMBUS_WASM || {};',
|
|
171
|
-
"globalThis.__NIMBUS_WASM['python.wasm'] = __NIMBUS_WASM_python;",
|
|
172
|
-
'',
|
|
173
|
-
preamble,
|
|
174
|
-
'',
|
|
175
|
-
// Only adopt a real binding: routed fetch hops resolve the entrypoint
|
|
176
|
-
// without a supervisor, and overwriting with undefined would drop the live
|
|
177
|
-
// stub the process needs for its whole lifetime.
|
|
178
|
-
'function __nimbusAdoptPySupervisor(env) {',
|
|
179
|
-
' const supervisor = env && env.SUPERVISOR;',
|
|
180
|
-
' if (supervisor) globalThis.__nimbusPySupervisor = supervisor;',
|
|
181
|
-
' __wasiAdoptSupervisor(supervisor);',
|
|
182
|
-
'}',
|
|
183
|
-
// A resident process answers between requests, and that is the only moment
|
|
184
|
-
// "durable while running" can be made true: by the time the caller holds a
|
|
185
|
-
// response, everything the request wrote has reached the VFS.
|
|
186
|
-
'async function __nimbusParkPy(value) {',
|
|
187
|
-
' await __wasiDrainPersist();',
|
|
188
|
-
' await __wasiRevalidateFS();',
|
|
189
|
-
' return value;',
|
|
190
|
-
'}',
|
|
191
|
-
'async function __nimbusStartPyProcess(args) {',
|
|
192
|
-
' const result = await globalThis.__cpythonStartProcess(args || {});',
|
|
193
|
-
' const ports = await globalThis.__cpythonListeningPorts();',
|
|
194
|
-
' const registrations = globalThis.__nimbusVirtualPortRegistrationPromises || [];',
|
|
195
|
-
' if (registrations.length > 0) await Promise.allSettled(registrations.splice(0));',
|
|
196
|
-
' if (ports.length > 0) {',
|
|
197
|
-
' return { state: "listening", port: ports[0], stdout: result.stdout, stderr: result.stderr };',
|
|
198
|
-
' }',
|
|
199
|
-
' return { state: "exited", result, stdout: result.stdout, stderr: result.stderr };',
|
|
200
|
-
'}',
|
|
201
|
-
'export class NimbusProcess extends DurableObject {',
|
|
202
|
-
' async startProcess(args) {',
|
|
203
|
-
' __nimbusAdoptPySupervisor(this.env);',
|
|
204
|
-
' return __nimbusParkPy(await __nimbusStartPyProcess(args || {}));',
|
|
205
|
-
' }',
|
|
206
|
-
' async fetch(request) {',
|
|
207
|
-
' __nimbusAdoptPySupervisor(this.env);',
|
|
208
|
-
' return this.handleHttpRequest(request);',
|
|
209
|
-
' }',
|
|
210
|
-
' async handleHttpRequest(request) {',
|
|
211
|
-
' __nimbusAdoptPySupervisor(this.env);',
|
|
212
|
-
' const hinted = Number(request.headers.get("X-Nimbus-Port") || 0);',
|
|
213
|
-
' const port = hinted || Array.from(globalThis.__nimbusVirtualSockets.listeners.keys())[0];',
|
|
214
|
-
' if (!port) return new Response("Nimbus Python process has no listening virtual socket", { status: 502 });',
|
|
215
|
-
' return __nimbusParkPy(await globalThis.__nimbusVirtualSockets.handleHttpRequest(port, request));',
|
|
216
|
-
' }',
|
|
217
|
-
'}',
|
|
218
|
-
].join('\n');
|
|
219
|
-
}
|
|
220
|
-
const CPythonBootSchema = z.object({
|
|
221
|
-
state: z.string().optional(),
|
|
222
|
-
port: z.number().optional(),
|
|
223
|
-
stdout: z.string().optional(),
|
|
224
|
-
stderr: z.string().optional(),
|
|
225
|
-
result: z.object({ exitCode: z.number().optional() }).passthrough().optional(),
|
|
226
|
-
}).passthrough();
|
|
227
|
-
/**
|
|
228
|
-
* Facet-side entry. Serialized with fn.toString(), so it captures nothing and
|
|
229
|
-
* names no import: everything it needs is on globalThis, put there by the
|
|
230
|
-
* preamble.
|
|
231
|
-
*/
|
|
232
|
-
async function cpythonRunFacetFn(args, facetEnv) {
|
|
233
|
-
const run = Reflect.get(globalThis, '__cpythonRun');
|
|
234
|
-
if (typeof run !== 'function') {
|
|
235
|
-
return {
|
|
236
|
-
stdout: '', stderr: '', exitCode: 127,
|
|
237
|
-
error: 'cpython preamble missing: __cpythonRun not in scope',
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
const adopt = Reflect.get(globalThis, '__wasiAdoptSupervisor');
|
|
241
|
-
const drain = Reflect.get(globalThis, '__wasiDrainPersist');
|
|
242
|
-
const supervisor = facetEnv && facetEnv.SUPERVISOR;
|
|
243
|
-
// Published where the boot re-adopts it after the mount: adopting only here
|
|
244
|
-
// would be undone by __wasiInitFS, which clears it on purpose.
|
|
245
|
-
if (supervisor)
|
|
246
|
-
Reflect.set(globalThis, '__nimbusPySupervisor', supervisor);
|
|
247
|
-
adopt?.(supervisor);
|
|
248
|
-
try {
|
|
249
|
-
return await run(args);
|
|
250
|
-
}
|
|
251
|
-
finally {
|
|
252
|
-
// In `finally`, not on the success path: a program that wrote a file and
|
|
253
|
-
// then raised still wrote the file, and those bytes are the user's.
|
|
254
|
-
await drain?.();
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
/**
|
|
258
|
-
* Start a program that binds a port as its own process, so it outlives the
|
|
259
|
-
* invocation that started it. Mirrors spawnRubySocketProcess.
|
|
260
|
-
*/
|
|
261
|
-
async function spawnCPythonSocketProcess(facetMgr, args, command) {
|
|
262
|
-
const workerCode = buildCPythonSocketProcessWorker(buildCPythonPreamble());
|
|
263
|
-
const spawned = await facetMgr.spawnWorker(workerCode, command, args.cwd, {
|
|
264
|
-
compatibilityFlags: ['nodejs_compat'],
|
|
265
|
-
// By path, not by value: the interpreter is 10.6 MiB, more than a single
|
|
266
|
-
// RPC value may carry, so whichever host runs this process reads it itself.
|
|
267
|
-
vfsWasmModules: { 'python.wasm': args.wasmVfsPath },
|
|
268
|
-
startArgs: args.startArgs,
|
|
269
|
-
}).catch(() => null);
|
|
270
|
-
if (!spawned)
|
|
271
|
-
return { exitCode: 1, stdout: '', stderr: 'python process boot failed\n' };
|
|
272
|
-
const boot = CPythonBootSchema.safeParse(spawned.boot);
|
|
273
|
-
if (!boot.success) {
|
|
274
|
-
facetMgr.finishProcess(spawned.pid, 1, 'python process boot failed');
|
|
275
|
-
return { exitCode: 1, stdout: '', stderr: 'python process boot failed\n' };
|
|
276
|
-
}
|
|
277
|
-
const data = boot.data;
|
|
278
|
-
if (data.state === 'listening' && typeof data.port === 'number' && data.port > 0) {
|
|
279
|
-
facetMgr.registerPort(spawned.pid, data.port);
|
|
280
|
-
const routeable = await facetMgr.waitForRouteablePorts(spawned.pid);
|
|
281
|
-
const port = routeable.includes(data.port) ? data.port : routeable[0];
|
|
282
|
-
if (!port) {
|
|
283
|
-
facetMgr.kill(spawned.pid);
|
|
284
|
-
return {
|
|
285
|
-
exitCode: 1,
|
|
286
|
-
stdout: data.stdout || '',
|
|
287
|
-
stderr: `${data.stderr || ''}python: virtual socket port ${data.port} failed to attach a route handler\n`,
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
exitCode: 0,
|
|
292
|
-
stdout: `${data.stdout || ''}\x1b[2m[started (long-running): pid=${spawned.pid} cmd="${command}" port=${port}]\x1b[0m\n`,
|
|
293
|
-
stderr: data.stderr || '',
|
|
294
|
-
spawnedPid: spawned.pid,
|
|
295
|
-
port,
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
const result = data.result;
|
|
299
|
-
const exitCode = typeof result?.exitCode === 'number' ? result.exitCode : 0;
|
|
300
|
-
facetMgr.finishProcess(spawned.pid, exitCode, data.stderr || 'python process exited');
|
|
301
|
-
return { exitCode, stdout: data.stdout || '', stderr: data.stderr || '' };
|
|
302
|
-
}
|
|
303
|
-
export function makeCPythonRunnerFactory(deps) {
|
|
304
|
-
const { facetMgr } = deps;
|
|
305
|
-
return function cpythonRunnerFactory(manifest, installRoot, binName, _binKind) {
|
|
306
|
-
const findFile = (rel) => {
|
|
307
|
-
const entry = manifest.files.find((f) => f.path === rel);
|
|
308
|
-
return entry ? `${installRoot}/${entry.path}` : null;
|
|
309
|
-
};
|
|
310
|
-
const baseWasmVfs = findFile(CPYTHON_WASM_REL);
|
|
311
|
-
const sciWasmVfs = findFile(CPYTHON_SCI_WASM_REL);
|
|
312
|
-
const sciPackagesVfs = findFile(CPYTHON_SCI_PACKAGES_REL);
|
|
313
|
-
const stdlibVfs = findFile(CPYTHON_STDLIB_REL);
|
|
314
|
-
let manifestCache = null;
|
|
315
|
-
return async function cpythonBinHandler(ctx) {
|
|
316
|
-
const cred = requireVfsCred(ctx.cred, binName);
|
|
317
|
-
const credKey = `${cred.uid}:${cred.gid}:${cred.groups.join(',')}`;
|
|
318
|
-
const vfs = deps.vfs.as(cred);
|
|
319
|
-
const argv = ctx.args || [];
|
|
320
|
-
const cwd = ctx.cwd || '/home/user';
|
|
321
|
-
const pipRuntimeContext = {
|
|
322
|
-
// No Pyodide lockfile: there is no curated wheel index behind this
|
|
323
|
-
// interpreter, so pip resolves against PyPI like anywhere else.
|
|
324
|
-
pyodideLockfileText: null,
|
|
325
|
-
runtimeArtifacts: manifest.runtime_artifacts || [],
|
|
326
|
-
};
|
|
327
|
-
const isPipBin = _binKind === 'pip' || binName === 'pip' || binName === 'pip3';
|
|
328
|
-
const pipInvocation = isPipBin
|
|
329
|
-
? await buildPipInvocation(argv, binName, cwd, vfs, pipRuntimeContext)
|
|
330
|
-
: await buildPythonModulePipInvocation(argv, cwd, vfs, pipRuntimeContext);
|
|
331
|
-
if (pipInvocation.error) {
|
|
332
|
-
ctx.stderr.write(`${binName}: ${pipInvocation.error}\n`);
|
|
333
|
-
return pipInvocation.exitCode;
|
|
334
|
-
}
|
|
335
|
-
if (pipInvocation.mode !== 'pip' && hasLeadingCliFlag(argv, PYTHON_VERSION_FLAGS)) {
|
|
336
|
-
ctx.stdout.write('Python 3.13.14 (CPython, wasm32-wasi, Nimbus runtime)\n');
|
|
337
|
-
return 0;
|
|
338
|
-
}
|
|
339
|
-
if (pipInvocation.mode !== 'pip' && hasLeadingCliFlag(argv, PYTHON_HELP_FLAGS)) {
|
|
340
|
-
ctx.stdout.write(`usage: ${binName} [option] ... [-c cmd | -m mod | file | -] [arg] ...\n`);
|
|
341
|
-
ctx.stdout.write('Nimbus CPython 3.13 runtime (wasm32-wasi).\n');
|
|
342
|
-
ctx.stdout.write('Supported: -c <code>, -m <module>, <file.py>, stdin via -, and the session filesystem directly.\n');
|
|
343
|
-
ctx.stdout.write('zlib, lzma, bz2, hashlib, ssl, sqlite3 and sockets are built in; pure-Python wheels install with pip.\n');
|
|
344
|
-
return 0;
|
|
345
|
-
}
|
|
346
|
-
// Which interpreter this invocation gets. The sci variant is chosen from
|
|
347
|
-
// what the session has installed, never from a guess about what this
|
|
348
|
-
// program will import — the whole point of keying on state is that it is
|
|
349
|
-
// also right for `python -c` naming a module in a variable. Falling back
|
|
350
|
-
// when the variant is absent keeps a session installed before the variant
|
|
351
|
-
// shipped working, on the base interpreter, rather than failing to start.
|
|
352
|
-
const wantsSci = sessionUsesSciVariant(vfs)
|
|
353
|
-
&& sciWasmVfs !== null && vfs.exists(sciWasmVfs);
|
|
354
|
-
const wasmVfs = wantsSci ? sciWasmVfs : baseWasmVfs;
|
|
355
|
-
const sciPackagesPath = wantsSci && sciPackagesVfs && vfs.exists(sciPackagesVfs)
|
|
356
|
-
? sciPackagesVfs
|
|
357
|
-
: null;
|
|
358
|
-
if (!wasmVfs || !vfs.exists(wasmVfs)) {
|
|
359
|
-
ctx.stderr.write(`${binName}: python.wasm missing (re-run 'nimbus install python')\n`);
|
|
360
|
-
return 127;
|
|
361
|
-
}
|
|
362
|
-
if (!stdlibVfs || !vfs.exists(stdlibVfs)) {
|
|
363
|
-
ctx.stderr.write(`${binName}: python313.zip missing (re-run 'nimbus install python')\n`);
|
|
364
|
-
return 127;
|
|
365
|
-
}
|
|
366
|
-
const parsed = pipInvocation.mode === 'pip'
|
|
367
|
-
? { mode: 'inline', inlineCode: pipInvocation.code, scriptPath: '', scriptArgs: [], exitCode: 0 }
|
|
368
|
-
: parsePythonArgv(argv);
|
|
369
|
-
if (parsed.error) {
|
|
370
|
-
ctx.stderr.write(`${binName}: ${parsed.error}\n`);
|
|
371
|
-
return parsed.exitCode;
|
|
372
|
-
}
|
|
373
|
-
let userCode = '';
|
|
374
|
-
let progName = binName;
|
|
375
|
-
let pyArgv = [binName];
|
|
376
|
-
if (parsed.mode === 'inline') {
|
|
377
|
-
userCode = parsed.inlineCode;
|
|
378
|
-
pyArgv = ['-c', ...parsed.scriptArgs];
|
|
379
|
-
}
|
|
380
|
-
else if (parsed.mode === 'script') {
|
|
381
|
-
const absPath = resolveVfsPath(parsed.scriptPath, cwd);
|
|
382
|
-
try {
|
|
383
|
-
if (!vfs.exists(absPath)) {
|
|
384
|
-
ctx.stderr.write(`${binName}: can't open file '${parsed.scriptPath}': [Errno 2] No such file or directory\n`);
|
|
385
|
-
return 2;
|
|
386
|
-
}
|
|
387
|
-
userCode = new TextDecoder('utf-8').decode(vfs.readFile(absPath));
|
|
388
|
-
}
|
|
389
|
-
catch (e) {
|
|
390
|
-
ctx.stderr.write(`${binName}: ${parsed.scriptPath}: ${errorMessage(e)}\n`);
|
|
391
|
-
return 1;
|
|
392
|
-
}
|
|
393
|
-
progName = parsed.scriptPath;
|
|
394
|
-
pyArgv = [parsed.scriptPath, ...parsed.scriptArgs];
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
const stdinReader = ctx.stdin;
|
|
398
|
-
userCode = (stdinReader && typeof stdinReader.read === 'function' ? await stdinReader.read() : '') ?? '';
|
|
399
|
-
pyArgv = ['-', ...parsed.scriptArgs];
|
|
400
|
-
}
|
|
401
|
-
// sys.argv is set from Python rather than from WASI argv: the reactor has
|
|
402
|
-
// no argv of its own, and this keeps the one place that decides what the
|
|
403
|
-
// program sees in TypeScript.
|
|
404
|
-
const prelude = [
|
|
405
|
-
PYTHON_SERVER_ADAPTER,
|
|
406
|
-
'import sys',
|
|
407
|
-
`sys.argv = ${JSON.stringify(pyArgv)}`,
|
|
408
|
-
// The variant's own packages. zipimport reads them straight out of the
|
|
409
|
-
// session filesystem, so they need no unpacking and no manifest entry
|
|
410
|
-
// beyond the archive itself.
|
|
411
|
-
...(sciPackagesPath
|
|
412
|
-
? [`sys.path.insert(0, ${JSON.stringify(`/${sciPackagesPath.replace(/^\/+/, '')}`)})`]
|
|
413
|
-
: []),
|
|
414
|
-
`sys.path.insert(0, ${JSON.stringify(`/${PYTHON_SITE_PACKAGES_ROOT}`)})`,
|
|
415
|
-
`sys.path.insert(0, ${JSON.stringify(cwd)})`,
|
|
416
|
-
// WASI has no process cwd, so wasi-libc starts every guest at '/'.
|
|
417
|
-
// Leaving it there silently reroutes every relative path a program
|
|
418
|
-
// opens — the shell says the user is in /home/user and Python resolves
|
|
419
|
-
// against the root.
|
|
420
|
-
'import os',
|
|
421
|
-
'try:',
|
|
422
|
-
` os.chdir(${JSON.stringify(cwd)})`,
|
|
423
|
-
'except OSError:',
|
|
424
|
-
' pass',
|
|
425
|
-
].join('\n');
|
|
426
|
-
const cacertVfs = findFile(CPYTHON_CACERT_REL);
|
|
427
|
-
const userEnv = { ...(ctx.env || {}) };
|
|
428
|
-
if (!userEnv.HOME)
|
|
429
|
-
userEnv.HOME = '/home/user';
|
|
430
|
-
if (!userEnv.PYTHONUNBUFFERED)
|
|
431
|
-
userEnv.PYTHONUNBUFFERED = '1';
|
|
432
|
-
// Without this OpenSSL has no trust anchors at all — there is no
|
|
433
|
-
// /etc/ssl on a Nimbus session — and every HTTPS request fails
|
|
434
|
-
// verification with a message about a missing local issuer rather than
|
|
435
|
-
// about a missing bundle.
|
|
436
|
-
if (!userEnv.SSL_CERT_FILE && cacertVfs)
|
|
437
|
-
userEnv.SSL_CERT_FILE = `/${cacertVfs.replace(/^\/+/, '')}`;
|
|
438
|
-
// A manifest, not a copy: sizes and modes only, with the facet demand-
|
|
439
|
-
// loading whatever the program opens. The stdlib zip is covered by it
|
|
440
|
-
// like any other file, which is the whole point of not having a private
|
|
441
|
-
// filesystem any more.
|
|
442
|
-
const stdlibDir = stdlibVfs.replace(/\/[^/]+$/, '');
|
|
443
|
-
// Every runtime file the interpreter is told about has to be a root of
|
|
444
|
-
// its own. The trust store was reachable only while the cwd happened to
|
|
445
|
-
// be an ancestor of the install — from ~ the walk swept the whole runtime
|
|
446
|
-
// tree in — so `cd` into any subdirectory and SSL_CERT_FILE pointed at a
|
|
447
|
-
// path the facet could not see, and every pip install failed
|
|
448
|
-
// CERTIFICATE_VERIFY_FAILED with the bundle sitting right there.
|
|
449
|
-
const cacertDir = cacertVfs ? cacertVfs.replace(/\/[^/]+$/, '') : null;
|
|
450
|
-
const revision = Math.max(vfs.revision(cwd), vfs.revision(PYTHON_SITE_PACKAGES_ROOT), vfs.revision(stdlibVfs));
|
|
451
|
-
let fsManifest = manifestCache && manifestCache.cred === credKey
|
|
452
|
-
&& manifestCache.cwd === cwd && manifestCache.revision === revision
|
|
453
|
-
? manifestCache.result
|
|
454
|
-
: null;
|
|
455
|
-
if (!fsManifest) {
|
|
456
|
-
fsManifest = manifestVfs(vfs, cwd, {
|
|
457
|
-
extraRoots: [PYTHON_SITE_PACKAGES_ROOT, stdlibDir, ...(cacertDir ? [cacertDir] : [])],
|
|
458
|
-
revision,
|
|
459
|
-
});
|
|
460
|
-
manifestCache = { cred: credKey, cwd, revision, result: fsManifest };
|
|
461
|
-
}
|
|
462
|
-
if ('error' in fsManifest) {
|
|
463
|
-
ctx.stderr.write(`${binName}: ${fsManifest.error}\n`);
|
|
464
|
-
return 1;
|
|
465
|
-
}
|
|
466
|
-
const snapshot = fsManifest.snapshot;
|
|
467
|
-
// Built per invocation, not cached: supervisorPid is baked into the
|
|
468
|
-
// SUPERVISOR binding at construction, so a pool held across calls would
|
|
469
|
-
// hand every later caller the first caller's write credential.
|
|
470
|
-
const { NimbusLoaderPool } = await import('../loaders/loader-pool.js');
|
|
471
|
-
const host = getFacetManagerLoaderHost(facetMgr);
|
|
472
|
-
const pool = new NimbusLoaderPool(host.env, host.ctx, {
|
|
473
|
-
// The variant is in the tag because the pool's constructor-time wasm
|
|
474
|
-
// fingerprint is name:length:first-byte:last-byte, not a content hash.
|
|
475
|
-
// Two variants differ by megabytes so they would not collide today, but
|
|
476
|
-
// a warm slot serving the wrong interpreter is not a failure worth
|
|
477
|
-
// leaving to a size coincidence.
|
|
478
|
-
tag: wantsSci ? 'cpython-runner:sci' : 'cpython-runner',
|
|
479
|
-
concurrency: 1,
|
|
480
|
-
// NOT omitSupervisor. Without it the facet reads the seeded manifest
|
|
481
|
-
// and can never write anything back — the program appears to run and
|
|
482
|
-
// its output never reaches the session.
|
|
483
|
-
supervisorPid: ctx.pid,
|
|
484
|
-
preamble: buildCPythonPreamble(),
|
|
485
|
-
wasmModules: { 'python.wasm': toArrayBuffer(vfs.readFile(wasmVfs)) },
|
|
486
|
-
});
|
|
487
|
-
const facetArgs = {
|
|
488
|
-
userCode: `${prelude}\n${userCode}`,
|
|
489
|
-
pyArgv,
|
|
490
|
-
userEnv,
|
|
491
|
-
progName,
|
|
492
|
-
cwd,
|
|
493
|
-
pythonHome: `/${installRoot.replace(/^\/+/, '')}`,
|
|
494
|
-
supervisorPid: ctx.pid,
|
|
495
|
-
fsSnapshot: snapshot,
|
|
496
|
-
};
|
|
497
|
-
// A script or `-m` can bind a port and keep serving, and such a program
|
|
498
|
-
// is not finished when it stops producing output — it is finished when it
|
|
499
|
-
// stops running. Pyodide gave those a dedicated socket process; that
|
|
500
|
-
// spawn is not ported yet, so for now they get a one-shot facet with a
|
|
501
|
-
// budget long enough not to cut a server off mid-request. Porting the
|
|
502
|
-
// resident process is the last piece, and until it lands a server holds
|
|
503
|
-
// its facet rather than being driven by inbound requests.
|
|
504
|
-
const resident = shouldRunAsResidentProcess(argv, parsed, pipInvocation.mode === 'pip');
|
|
505
|
-
if (resident) {
|
|
506
|
-
const command = [binName, ...argv].map((part) => (/^[A-Za-z0-9_./:=@+-]+$/.test(part) ? part : JSON.stringify(part))).join(' ');
|
|
507
|
-
const spawnResult = await spawnCPythonSocketProcess(facetMgr, { wasmVfsPath: wasmVfs, startArgs: facetArgs, cwd }, command);
|
|
508
|
-
if (spawnResult.stdout)
|
|
509
|
-
ctx.stdout.write(spawnResult.stdout);
|
|
510
|
-
if (spawnResult.stderr)
|
|
511
|
-
ctx.stderr.write(spawnResult.stderr);
|
|
512
|
-
return spawnResult.exitCode;
|
|
513
|
-
}
|
|
514
|
-
let result;
|
|
515
|
-
try {
|
|
516
|
-
result = await pool.submit(cpythonRunFacetFn, facetArgs, {
|
|
517
|
-
timeoutMs: 120_000,
|
|
518
|
-
});
|
|
519
|
-
}
|
|
520
|
-
catch (e) {
|
|
521
|
-
ctx.stderr.write(`${binName}: ${errorMessage(e)}\n`);
|
|
522
|
-
return 1;
|
|
523
|
-
}
|
|
524
|
-
if (result.stdout)
|
|
525
|
-
ctx.stdout.write(result.stdout);
|
|
526
|
-
if (result.stderr)
|
|
527
|
-
ctx.stderr.write(result.stderr);
|
|
528
|
-
if (result.error) {
|
|
529
|
-
ctx.stderr.write(`${binName}: ${result.error}\n`);
|
|
530
|
-
return result.exitCode || 1;
|
|
531
|
-
}
|
|
532
|
-
return result.exitCode;
|
|
533
|
-
};
|
|
534
|
-
};
|
|
535
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod/v4';
|
|
2
|
-
import { type RuntimeArtifactMetadata, type RuntimePythonPackageArtifactMetadata } from './runtime-catalog.js';
|
|
3
|
-
export declare const PYTHON_SITE_PACKAGES_ROOT = "home/user/.nimbus-python/site-packages";
|
|
4
|
-
export declare const PYTHON_PYODIDE_PACKAGE_MANIFEST = "home/user/.nimbus-python/site-packages/.nimbus-pyodide-packages.json";
|
|
5
|
-
interface PythonPipVfs {
|
|
6
|
-
exists(path: string): boolean;
|
|
7
|
-
readFile(path: string): Uint8Array;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Whether this session has installed anything the sci interpreter variant
|
|
11
|
-
* carries compiled code for.
|
|
12
|
-
*
|
|
13
|
-
* This reads installed state; it does not predict what a program might import.
|
|
14
|
-
* The dist-info directory pip writes is the record, and a variant chosen from it
|
|
15
|
-
* is right for `python -c` reading a module name out of a variable, which a
|
|
16
|
-
* per-program classifier cannot be.
|
|
17
|
-
*/
|
|
18
|
-
export declare function sessionUsesSciVariant(vfs: PythonPipVfs): boolean;
|
|
19
|
-
export interface PythonPipRuntimeContext {
|
|
20
|
-
pyodideLockfileText?: string | null;
|
|
21
|
-
runtimeArtifacts?: RuntimeArtifactMetadata[];
|
|
22
|
-
}
|
|
23
|
-
export interface InstalledPyodidePackageManifest {
|
|
24
|
-
version: 1;
|
|
25
|
-
packages: RuntimePythonPackageArtifactMetadata[];
|
|
26
|
-
}
|
|
27
|
-
export interface PipInvocation {
|
|
28
|
-
mode: 'pip' | 'none';
|
|
29
|
-
code: string;
|
|
30
|
-
error?: string;
|
|
31
|
-
exitCode: number;
|
|
32
|
-
pyodidePackages?: RuntimePythonPackageArtifactMetadata[];
|
|
33
|
-
}
|
|
34
|
-
export declare const InstalledPyodidePackageManifestSchema: z.ZodType<InstalledPyodidePackageManifest>;
|
|
35
|
-
export declare function parseInstalledPyodidePackageManifest(text: string): InstalledPyodidePackageManifest;
|
|
36
|
-
export declare function buildPipInvocation(argv: string[], binName: string, cwd: string, vfs: PythonPipVfs, runtimeContext?: PythonPipRuntimeContext): Promise<PipInvocation>;
|
|
37
|
-
export {};
|
|
38
|
-
//# sourceMappingURL=python-pip.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"python-pip.d.ts","sourceRoot":"","sources":["../../src/runtime/python-pip.ts"],"names":[],"mappings":"AAmBA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAG3B,OAAO,EAGL,KAAK,uBAAuB,EAC5B,KAAK,oCAAoC,EAC1C,MAAM,sBAAsB,CAAC;AAE9B,eAAO,MAAM,yBAAyB,2CAA2C,CAAC;AAClF,eAAO,MAAM,+BAA+B,yEAA+D,CAAC;AAI5G,UAAU,YAAY;IACpB,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC9B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;CACpC;AAuFD;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,OAAO,CAGhE;AAkDD,MAAM,WAAW,uBAAuB;IACtC,mBAAmB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,gBAAgB,CAAC,EAAE,uBAAuB,EAAE,CAAC;CAC9C;AAuDD,MAAM,WAAW,+BAA+B;IAC9C,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,oCAAoC,EAAE,CAAC;CAClD;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,KAAK,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,eAAe,CAAC,EAAE,oCAAoC,EAAE,CAAC;CAC1D;AAQD,eAAO,MAAM,qCAAqC,EAAE,CAAC,CAAC,OAAO,CAAC,+BAA+B,CAG3F,CAAC;AAEH,wBAAgB,oCAAoC,CAAC,IAAI,EAAE,MAAM,GAAG,+BAA+B,CAElG;AAED,wBAAsB,kBAAkB,CACtC,IAAI,EAAE,MAAM,EAAE,EACd,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,YAAY,EACjB,cAAc,GAAE,uBAA4B,GAC3C,OAAO,CAAC,aAAa,CAAC,CA0CxB"}
|