@nimbus-sh/sdk 0.1.4 → 0.2.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.
- package/README.md +45 -3
- package/dist/flue.d.ts +93 -0
- package/dist/flue.d.ts.map +1 -0
- package/dist/flue.js +112 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/sandbox.d.ts +258 -11
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +433 -61
- package/dist/worker.d.ts +2 -2
- package/dist/worker.d.ts.map +1 -1
- package/dist/worker.js +1 -1
- package/package.json +11 -3
package/README.md
CHANGED
|
@@ -72,6 +72,15 @@ export default createNimbusHandler({
|
|
|
72
72
|
});
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
+
The bundled session UI includes an Agent surface in the editor workspace.
|
|
76
|
+
Configure it with Worker vars
|
|
77
|
+
`NIMBUS_CF_OAUTH_CLIENT_ID`, `NIMBUS_CF_OAUTH_SCOPES`,
|
|
78
|
+
`NIMBUS_AGENT_MODEL`, `NIMBUS_AGENT_GATEWAY_ID`, and the secrets
|
|
79
|
+
`NIMBUS_AGENT_COOKIE_SECRET` or `NIMBUS_CLOUDFLARE_API_TOKEN` when you want
|
|
80
|
+
Cloudflare OAuth or owner-token Workers AI access. User OAuth uses
|
|
81
|
+
Authorization Code + PKCE and encrypted browser cookies; Nimbus does not store
|
|
82
|
+
user OAuth tokens in Durable Object storage.
|
|
83
|
+
|
|
75
84
|
## Programmatic sandbox quickstart
|
|
76
85
|
|
|
77
86
|
The same sandbox handle API works in two modes:
|
|
@@ -152,6 +161,32 @@ Worker verifies the JWT, enforces `sid` pins and `sandbox:use` scope, applies
|
|
|
152
161
|
the configured runtime policy, and delegates to the same `NimbusSession` RPC
|
|
153
162
|
methods used by `Nimbus.fromEnv()`.
|
|
154
163
|
|
|
164
|
+
Sandbox destruction is a separate lifecycle operation. Tokens that call
|
|
165
|
+
`box.destroy()` must include `session:destroy` or `session:admin` in addition
|
|
166
|
+
to the normal sandbox scope.
|
|
167
|
+
|
|
168
|
+
## Flue connector
|
|
169
|
+
|
|
170
|
+
Use `@nimbus-sh/sdk/flue` when an agent runtime expects Flue's sandbox
|
|
171
|
+
provider contract:
|
|
172
|
+
|
|
173
|
+
```ts
|
|
174
|
+
import { Nimbus } from '@nimbus-sh/sdk/sandbox';
|
|
175
|
+
import { nimbusFlue } from '@nimbus-sh/sdk/flue';
|
|
176
|
+
|
|
177
|
+
const box = Nimbus.fromEnv(env, nimbusConfig).sandbox('job-123');
|
|
178
|
+
await box.ready();
|
|
179
|
+
|
|
180
|
+
const factory = nimbusFlue(box);
|
|
181
|
+
const sessionEnv = await factory.createSessionEnv({
|
|
182
|
+
id: 'job-123',
|
|
183
|
+
cwd: '/home/user',
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
await sessionEnv.writeFile('/home/user/main.py', 'print(2 + 2)\n');
|
|
187
|
+
const result = await sessionEnv.exec('python /home/user/main.py');
|
|
188
|
+
```
|
|
189
|
+
|
|
155
190
|
## Sandbox handle API
|
|
156
191
|
|
|
157
192
|
```ts
|
|
@@ -168,13 +203,20 @@ const box = nimbus.sandbox('session-or-job-id', {
|
|
|
168
203
|
|
|
169
204
|
await box.ready();
|
|
170
205
|
await box.exec('node -e "console.log(2 + 2)"');
|
|
171
|
-
await box.startProcess('node --watch /home/user/app/server.js');
|
|
206
|
+
const proc = await box.startProcess('node --watch /home/user/app/server.js');
|
|
207
|
+
// returns immediately with proc.pid; poll box.processes.logs(proc.pid) for
|
|
208
|
+
// output and the exit record, or box.processes.kill(proc.pid) to stop it
|
|
172
209
|
await box.runCode('print(2 + 2)', { language: 'python', install: 'ifMissing' });
|
|
173
210
|
|
|
174
211
|
await box.files.write('/home/user/app/a.txt', 'hello');
|
|
175
212
|
await box.files.read('/home/user/app/a.txt');
|
|
176
213
|
await box.files.list('/home/user/app');
|
|
177
|
-
await box.files.
|
|
214
|
+
await box.files.stat('/home/user/app/a.txt');
|
|
215
|
+
await box.files.lstat('/home/user/app/link');
|
|
216
|
+
await box.files.rename('/home/user/app/a.txt', '/home/user/app/b.txt');
|
|
217
|
+
await box.files.chmod('/home/user/app/b.txt', 0o755);
|
|
218
|
+
await box.files.readRange('/home/user/app/b.txt', 0, 64);
|
|
219
|
+
await box.files.delete('/home/user/app/b.txt');
|
|
178
220
|
|
|
179
221
|
await box.runtimes.available();
|
|
180
222
|
await box.runtimes.installed();
|
|
@@ -208,7 +250,7 @@ object with `tools.exec.execute`, `runCode`, `readFile`, `writeFile`,
|
|
|
208
250
|
`provider.capabilities` is intentionally honest: Nimbus claims shell,
|
|
209
251
|
JavaScript/TypeScript, npm, git, owned filesystem, outbound fetch, inbound
|
|
210
252
|
HTTP-like port routing, process spawn/long-running processes, Python/Ruby
|
|
211
|
-
when allowed, and clang
|
|
253
|
+
when allowed, and clang-backed WASI/WebAssembly execution. It does not claim Docker, apt, GPU,
|
|
212
254
|
custom Linux images, native Linux ELF execution, or raw TCP listeners.
|
|
213
255
|
|
|
214
256
|
## Quickstart — mint a session token
|
package/dist/flue.d.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nimbus-sh/sdk/flue - Flue sandbox connector for Nimbus sandboxes.
|
|
3
|
+
*
|
|
4
|
+
* Flue owns the agent harness. Nimbus owns the sandbox. This adapter maps a
|
|
5
|
+
* `NimbusSandbox` handle to Flue's `SandboxFactory`/`SandboxApi` contract
|
|
6
|
+
* without making `@flue/runtime` a hard dependency of the core SDK.
|
|
7
|
+
*/
|
|
8
|
+
import type { NimbusSandbox } from './sandbox.js';
|
|
9
|
+
export interface NimbusFlueFileStat {
|
|
10
|
+
isFile: boolean;
|
|
11
|
+
isDirectory: boolean;
|
|
12
|
+
isSymbolicLink: boolean;
|
|
13
|
+
size: number;
|
|
14
|
+
mtime: Date;
|
|
15
|
+
}
|
|
16
|
+
export interface NimbusFlueSessionEnv {
|
|
17
|
+
exec(command: string, options?: NimbusFlueExecOptions): Promise<NimbusFlueShellResult>;
|
|
18
|
+
readFile(path: string): Promise<string>;
|
|
19
|
+
readFileBuffer(path: string): Promise<Uint8Array>;
|
|
20
|
+
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
21
|
+
stat(path: string): Promise<NimbusFlueFileStat>;
|
|
22
|
+
readdir(path: string): Promise<string[]>;
|
|
23
|
+
exists(path: string): Promise<boolean>;
|
|
24
|
+
mkdir(path: string, options?: {
|
|
25
|
+
recursive?: boolean;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
rm(path: string, options?: {
|
|
28
|
+
recursive?: boolean;
|
|
29
|
+
force?: boolean;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
cwd: string;
|
|
32
|
+
resolvePath(path: string): string;
|
|
33
|
+
}
|
|
34
|
+
export interface NimbusFlueShellResult {
|
|
35
|
+
stdout: string;
|
|
36
|
+
stderr: string;
|
|
37
|
+
exitCode: number;
|
|
38
|
+
}
|
|
39
|
+
export interface NimbusFlueExecOptions {
|
|
40
|
+
cwd?: string;
|
|
41
|
+
env?: Record<string, string>;
|
|
42
|
+
timeout?: number;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
}
|
|
45
|
+
export interface NimbusFlueSandboxApi {
|
|
46
|
+
readFile(path: string): Promise<string>;
|
|
47
|
+
readFileBuffer(path: string): Promise<Uint8Array>;
|
|
48
|
+
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
49
|
+
stat(path: string): Promise<NimbusFlueFileStat>;
|
|
50
|
+
readdir(path: string): Promise<string[]>;
|
|
51
|
+
exists(path: string): Promise<boolean>;
|
|
52
|
+
mkdir(path: string, options?: {
|
|
53
|
+
recursive?: boolean;
|
|
54
|
+
}): Promise<void>;
|
|
55
|
+
rm(path: string, options?: {
|
|
56
|
+
recursive?: boolean;
|
|
57
|
+
force?: boolean;
|
|
58
|
+
}): Promise<void>;
|
|
59
|
+
exec(command: string, options?: NimbusFlueExecOptions): Promise<NimbusFlueShellResult>;
|
|
60
|
+
}
|
|
61
|
+
export interface NimbusFlueRuntime<SessionEnv = NimbusFlueSessionEnv> {
|
|
62
|
+
createSandboxSessionEnv(api: NimbusFlueSandboxApi, cwd: string): SessionEnv;
|
|
63
|
+
}
|
|
64
|
+
export interface NimbusFlueFactory<SessionEnv = NimbusFlueSessionEnv> {
|
|
65
|
+
createSessionEnv(options: {
|
|
66
|
+
id: string;
|
|
67
|
+
cwd?: string;
|
|
68
|
+
}): Promise<SessionEnv>;
|
|
69
|
+
}
|
|
70
|
+
export interface NimbusFlueOptions<SessionEnv = NimbusFlueSessionEnv> {
|
|
71
|
+
cwd?: string;
|
|
72
|
+
runtime?: NimbusFlueRuntime<SessionEnv>;
|
|
73
|
+
}
|
|
74
|
+
export declare function nimbusFlue<SessionEnv = NimbusFlueSessionEnv>(sandbox: NimbusSandbox, options?: NimbusFlueOptions<SessionEnv>): NimbusFlueFactory<SessionEnv>;
|
|
75
|
+
export declare class NimbusFlueApi implements NimbusFlueSandboxApi {
|
|
76
|
+
private readonly sandbox;
|
|
77
|
+
constructor(sandbox: NimbusSandbox);
|
|
78
|
+
readFile(path: string): Promise<string>;
|
|
79
|
+
readFileBuffer(path: string): Promise<Uint8Array>;
|
|
80
|
+
writeFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
81
|
+
stat(path: string): Promise<NimbusFlueFileStat>;
|
|
82
|
+
readdir(path: string): Promise<string[]>;
|
|
83
|
+
exists(path: string): Promise<boolean>;
|
|
84
|
+
mkdir(path: string, _options?: {
|
|
85
|
+
recursive?: boolean;
|
|
86
|
+
}): Promise<void>;
|
|
87
|
+
rm(path: string, options?: {
|
|
88
|
+
recursive?: boolean;
|
|
89
|
+
force?: boolean;
|
|
90
|
+
}): Promise<void>;
|
|
91
|
+
exec(command: string, options?: NimbusFlueExecOptions): Promise<NimbusFlueShellResult>;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=flue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flue.d.ts","sourceRoot":"","sources":["../src/flue.ts"],"names":[],"mappings":"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"}
|
package/dist/flue.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @nimbus-sh/sdk/flue - Flue sandbox connector for Nimbus sandboxes.
|
|
3
|
+
*
|
|
4
|
+
* Flue owns the agent harness. Nimbus owns the sandbox. This adapter maps a
|
|
5
|
+
* `NimbusSandbox` handle to Flue's `SandboxFactory`/`SandboxApi` contract
|
|
6
|
+
* without making `@flue/runtime` a hard dependency of the core SDK.
|
|
7
|
+
*/
|
|
8
|
+
export function nimbusFlue(sandbox, options = {}) {
|
|
9
|
+
return {
|
|
10
|
+
async createSessionEnv({ cwd }) {
|
|
11
|
+
const runtime = options.runtime ?? await loadFlueRuntime();
|
|
12
|
+
return runtime.createSandboxSessionEnv(new NimbusFlueApi(sandbox), cwd ?? options.cwd ?? '/home/user');
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export class NimbusFlueApi {
|
|
17
|
+
sandbox;
|
|
18
|
+
constructor(sandbox) {
|
|
19
|
+
this.sandbox = sandbox;
|
|
20
|
+
}
|
|
21
|
+
async readFile(path) {
|
|
22
|
+
const content = await this.sandbox.files.read(path);
|
|
23
|
+
if (content == null)
|
|
24
|
+
throw enoent(path);
|
|
25
|
+
return content;
|
|
26
|
+
}
|
|
27
|
+
async readFileBuffer(path) {
|
|
28
|
+
const content = await this.sandbox.files.readBytes(path);
|
|
29
|
+
if (content == null)
|
|
30
|
+
throw enoent(path);
|
|
31
|
+
return content;
|
|
32
|
+
}
|
|
33
|
+
async writeFile(path, content) {
|
|
34
|
+
await this.sandbox.files.write(path, content);
|
|
35
|
+
}
|
|
36
|
+
async stat(path) {
|
|
37
|
+
const stat = await this.sandbox.files.stat(path);
|
|
38
|
+
if (!stat)
|
|
39
|
+
throw enoent(path);
|
|
40
|
+
return toFlueStat(stat);
|
|
41
|
+
}
|
|
42
|
+
async readdir(path) {
|
|
43
|
+
const entries = await this.sandbox.files.list(path);
|
|
44
|
+
return entries.map((entry) => entry.name);
|
|
45
|
+
}
|
|
46
|
+
async exists(path) {
|
|
47
|
+
return this.sandbox.files.exists(path);
|
|
48
|
+
}
|
|
49
|
+
async mkdir(path, _options = {}) {
|
|
50
|
+
await this.sandbox.files.mkdir(path);
|
|
51
|
+
}
|
|
52
|
+
async rm(path, options = {}) {
|
|
53
|
+
if (options.force && !(await this.sandbox.files.exists(path)))
|
|
54
|
+
return;
|
|
55
|
+
await this.sandbox.files.delete(path, { recursive: options.recursive });
|
|
56
|
+
}
|
|
57
|
+
async exec(command, options = {}) {
|
|
58
|
+
const result = await this.sandbox.exec(command, {
|
|
59
|
+
cwd: options.cwd,
|
|
60
|
+
env: options.env,
|
|
61
|
+
timeoutMs: secondsToMilliseconds(options.timeout),
|
|
62
|
+
});
|
|
63
|
+
if (options.signal?.aborted)
|
|
64
|
+
throw abortError(options.signal);
|
|
65
|
+
return toShellResult(result);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async function loadFlueRuntime() {
|
|
69
|
+
try {
|
|
70
|
+
const specifier = '@flue/runtime';
|
|
71
|
+
const runtime = await import(specifier);
|
|
72
|
+
if (typeof runtime.createSandboxSessionEnv === 'function') {
|
|
73
|
+
return runtime;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Error below gives callers the exact integration action.
|
|
78
|
+
}
|
|
79
|
+
throw new Error('nimbusFlue requires @flue/runtime. Install Flue or pass { runtime: { createSandboxSessionEnv } }.');
|
|
80
|
+
}
|
|
81
|
+
function toFlueStat(stat) {
|
|
82
|
+
const type = String(stat.type);
|
|
83
|
+
return {
|
|
84
|
+
isFile: type === 'file',
|
|
85
|
+
isDirectory: type === 'directory',
|
|
86
|
+
isSymbolicLink: false,
|
|
87
|
+
size: Number(stat.size) || 0,
|
|
88
|
+
mtime: new Date(Number(stat.mtime) || Date.now()),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
function toShellResult(result) {
|
|
92
|
+
return {
|
|
93
|
+
stdout: result.stdout,
|
|
94
|
+
stderr: result.stderr,
|
|
95
|
+
exitCode: result.exitCode,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
function secondsToMilliseconds(timeout) {
|
|
99
|
+
if (typeof timeout !== 'number' || !Number.isFinite(timeout) || timeout <= 0)
|
|
100
|
+
return undefined;
|
|
101
|
+
return Math.max(1, Math.round(timeout * 1000));
|
|
102
|
+
}
|
|
103
|
+
function enoent(path) {
|
|
104
|
+
const error = new Error(`ENOENT: no such file or directory, '${path}'`);
|
|
105
|
+
error.code = 'ENOENT';
|
|
106
|
+
return error;
|
|
107
|
+
}
|
|
108
|
+
function abortError(signal) {
|
|
109
|
+
return signal.reason instanceof Error
|
|
110
|
+
? signal.reason
|
|
111
|
+
: new DOMException('The operation was aborted', 'AbortError');
|
|
112
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/sandbox.d.ts
CHANGED
|
@@ -22,6 +22,14 @@ export interface NimbusSandboxProfile {
|
|
|
22
22
|
}
|
|
23
23
|
export interface NimbusConfig {
|
|
24
24
|
endpoint?: string;
|
|
25
|
+
/**
|
|
26
|
+
* The deployment's `NIMBUS_PREVIEW_HOST_SUFFIX`, enabling the
|
|
27
|
+
* `<port>--<sid>.<suffix>` preview origin. `Nimbus.fromEnv` reads it off
|
|
28
|
+
* the bindings, so in-Worker callers never restate it; remote clients
|
|
29
|
+
* (`Nimbus.connect`) have no bindings and must supply it to get host-form
|
|
30
|
+
* preview URLs.
|
|
31
|
+
*/
|
|
32
|
+
previewHostSuffix?: string;
|
|
25
33
|
sandboxes?: Record<string, NimbusSandboxProfile>;
|
|
26
34
|
}
|
|
27
35
|
export interface NimbusFromEnvOptions {
|
|
@@ -64,10 +72,31 @@ export interface NimbusExecResult {
|
|
|
64
72
|
duration: number;
|
|
65
73
|
timestamp: number;
|
|
66
74
|
}
|
|
67
|
-
export interface
|
|
68
|
-
|
|
69
|
-
|
|
75
|
+
export interface NimbusTerminalSize {
|
|
76
|
+
columns: number;
|
|
77
|
+
rows: number;
|
|
78
|
+
}
|
|
79
|
+
export interface NimbusDestroyOptions {
|
|
80
|
+
reason?: string;
|
|
81
|
+
}
|
|
82
|
+
export interface NimbusDestroyResult {
|
|
83
|
+
ok: true;
|
|
84
|
+
killed: number;
|
|
85
|
+
destroyedAt: number;
|
|
86
|
+
reason: string | null;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* A started background process. It is still running when `startProcess`
|
|
90
|
+
* returns, so there is no exit code or captured output here — poll
|
|
91
|
+
* `processes.logs(pid)` (which carries the exit record once it lands),
|
|
92
|
+
* `processes.list()`, or `processes.attach(pid)`.
|
|
93
|
+
*/
|
|
94
|
+
export interface NimbusStartResult {
|
|
95
|
+
command: string;
|
|
96
|
+
pid: number;
|
|
97
|
+
process: NimbusProcess;
|
|
70
98
|
ports: NimbusPort[];
|
|
99
|
+
startedAt: number;
|
|
71
100
|
}
|
|
72
101
|
export interface NimbusProcess {
|
|
73
102
|
pid: number;
|
|
@@ -79,22 +108,63 @@ export interface NimbusProcess {
|
|
|
79
108
|
startTime: number;
|
|
80
109
|
endTime: number | null;
|
|
81
110
|
longRunning: boolean;
|
|
111
|
+
attachedTty: boolean;
|
|
112
|
+
}
|
|
113
|
+
export interface NimbusProcessLogChunk {
|
|
114
|
+
seq: number;
|
|
115
|
+
ts: number;
|
|
116
|
+
stream: 'stdout' | 'stderr';
|
|
117
|
+
data: string;
|
|
118
|
+
binary?: boolean;
|
|
119
|
+
}
|
|
120
|
+
export interface NimbusProcessExitInfo {
|
|
121
|
+
code: number;
|
|
122
|
+
at: number;
|
|
123
|
+
reason?: string;
|
|
124
|
+
}
|
|
125
|
+
export interface NimbusProcessLogsOptions {
|
|
126
|
+
cursor?: number;
|
|
127
|
+
lines?: number;
|
|
128
|
+
bytes?: number;
|
|
129
|
+
}
|
|
130
|
+
export interface NimbusProcessLogsResult {
|
|
131
|
+
pid: number;
|
|
132
|
+
chunks: NimbusProcessLogChunk[];
|
|
133
|
+
text: string;
|
|
134
|
+
cursor: number;
|
|
135
|
+
truncated: boolean;
|
|
136
|
+
exit: NimbusProcessExitInfo | null;
|
|
137
|
+
}
|
|
138
|
+
export interface NimbusProcessAttachOptions {
|
|
139
|
+
pollIntervalMs?: number;
|
|
140
|
+
lines?: number;
|
|
141
|
+
bytes?: number;
|
|
142
|
+
signal?: AbortSignal;
|
|
82
143
|
}
|
|
83
144
|
export interface NimbusPort {
|
|
84
145
|
port: number;
|
|
85
146
|
pid: number;
|
|
86
147
|
registeredAt: number;
|
|
87
148
|
}
|
|
149
|
+
export interface NimbusFileStat {
|
|
150
|
+
type: 'file' | 'directory' | string;
|
|
151
|
+
size: number;
|
|
152
|
+
ctime?: number;
|
|
153
|
+
mtime: number;
|
|
154
|
+
mode: number;
|
|
155
|
+
}
|
|
88
156
|
export interface NimbusRuntimeSummary {
|
|
89
157
|
name: string;
|
|
90
158
|
version: string;
|
|
91
159
|
root: string;
|
|
160
|
+
abi: string;
|
|
92
161
|
bins: string[];
|
|
93
162
|
sizeBytes: number;
|
|
94
163
|
license: string;
|
|
95
164
|
}
|
|
96
165
|
export interface NimbusAvailableRuntime {
|
|
97
166
|
name: string;
|
|
167
|
+
abi: string;
|
|
98
168
|
defaultVersion: string;
|
|
99
169
|
versions: Array<{
|
|
100
170
|
version: string;
|
|
@@ -102,7 +172,82 @@ export interface NimbusAvailableRuntime {
|
|
|
102
172
|
license: string;
|
|
103
173
|
}>;
|
|
104
174
|
}
|
|
105
|
-
|
|
175
|
+
interface NimbusSessionStub {
|
|
176
|
+
_rpcReady(options?: {
|
|
177
|
+
preinstall?: string[];
|
|
178
|
+
}): Promise<{
|
|
179
|
+
ok: true;
|
|
180
|
+
preinstalled: string[];
|
|
181
|
+
}>;
|
|
182
|
+
_rpcExec(command: string, options?: Record<string, unknown>): Promise<NimbusExecResult>;
|
|
183
|
+
_rpcStartProcess(command: string, options?: Record<string, unknown>): Promise<NimbusStartResult>;
|
|
184
|
+
_rpcRunCode(code: string, options?: Record<string, unknown>): Promise<NimbusExecResult>;
|
|
185
|
+
_rpcReadFile(path: string): Promise<string | null>;
|
|
186
|
+
_rpcReadFileBytes(path: string): Promise<Uint8Array | null>;
|
|
187
|
+
_rpcWriteFile(path: string, content: string | Uint8Array): Promise<void>;
|
|
188
|
+
_rpcStat(path: string): Promise<NimbusFileStat | null>;
|
|
189
|
+
_rpcLstat(path: string): Promise<NimbusFileStat | null>;
|
|
190
|
+
_rpcReaddir(path: string): Promise<{
|
|
191
|
+
name: string;
|
|
192
|
+
type: string;
|
|
193
|
+
}[]>;
|
|
194
|
+
_rpcRename(from: string, to: string): Promise<void>;
|
|
195
|
+
_rpcChmod(path: string, mode: number): Promise<void>;
|
|
196
|
+
_rpcFsReadRange(path: string, offset: number, length: number): Promise<Uint8Array | null>;
|
|
197
|
+
_rpcExists(path: string): Promise<boolean>;
|
|
198
|
+
_rpcMkdir(path: string): Promise<void>;
|
|
199
|
+
_rpcDeleteFile(path: string, options?: {
|
|
200
|
+
recursive?: boolean;
|
|
201
|
+
}): Promise<void>;
|
|
202
|
+
_rpcInstallRuntime(spec: string, options?: {
|
|
203
|
+
force?: boolean;
|
|
204
|
+
}): Promise<unknown>;
|
|
205
|
+
_rpcEnsureRuntimes(specs: string[], options?: {
|
|
206
|
+
force?: boolean;
|
|
207
|
+
}): Promise<unknown>;
|
|
208
|
+
_rpcListRuntimes(): Promise<{
|
|
209
|
+
installed: NimbusRuntimeSummary[];
|
|
210
|
+
available: NimbusAvailableRuntime[];
|
|
211
|
+
}>;
|
|
212
|
+
_rpcListProcesses(): Promise<NimbusProcess[]>;
|
|
213
|
+
_rpcKillProcess(pid: number): Promise<{
|
|
214
|
+
ok: boolean;
|
|
215
|
+
pid: number;
|
|
216
|
+
}>;
|
|
217
|
+
_rpcWriteProcessInput(pid: number, data: string): Promise<{
|
|
218
|
+
ok: boolean;
|
|
219
|
+
pid: number;
|
|
220
|
+
}>;
|
|
221
|
+
_rpcEndProcessInput(pid: number): Promise<{
|
|
222
|
+
ok: boolean;
|
|
223
|
+
pid: number;
|
|
224
|
+
}>;
|
|
225
|
+
_rpcResizeProcess(pid: number, size: NimbusTerminalSize): Promise<{
|
|
226
|
+
ok: boolean;
|
|
227
|
+
pid: number;
|
|
228
|
+
}>;
|
|
229
|
+
_rpcSignalProcess(pid: number, signal: string): Promise<{
|
|
230
|
+
ok: boolean;
|
|
231
|
+
pid: number;
|
|
232
|
+
}>;
|
|
233
|
+
_rpcProcessLogs(pid: number, options?: NimbusProcessLogsOptions): Promise<NimbusProcessLogsResult>;
|
|
234
|
+
_rpcListPorts(): Promise<NimbusPort[]>;
|
|
235
|
+
_rpcExposePort(port: number): Promise<{
|
|
236
|
+
port: number;
|
|
237
|
+
listening: boolean;
|
|
238
|
+
pid: number | null;
|
|
239
|
+
registeredAt: number | null;
|
|
240
|
+
}>;
|
|
241
|
+
_rpcUnexposePort(port: number): Promise<{
|
|
242
|
+
port: number;
|
|
243
|
+
ok: boolean;
|
|
244
|
+
}>;
|
|
245
|
+
_rpcDestroy(options?: NimbusDestroyOptions): Promise<NimbusDestroyResult>;
|
|
246
|
+
}
|
|
247
|
+
interface NimbusSessionNamespace {
|
|
248
|
+
idFromName(name: string): DurableObjectId;
|
|
249
|
+
get(id: DurableObjectId): NimbusSessionStub;
|
|
250
|
+
}
|
|
106
251
|
type NimbusTarget = {
|
|
107
252
|
kind: 'binding';
|
|
108
253
|
namespace: NimbusSessionNamespace;
|
|
@@ -149,15 +294,27 @@ export declare class NimbusSandbox {
|
|
|
149
294
|
private remoteRpc;
|
|
150
295
|
ready(): Promise<void>;
|
|
151
296
|
exec(command: string, options?: NimbusExecOptions): Promise<NimbusExecResult>;
|
|
297
|
+
/**
|
|
298
|
+
* Start a command in the background. Returns as soon as the process has a
|
|
299
|
+
* pid — it does not wait for the command to finish.
|
|
300
|
+
*/
|
|
152
301
|
startProcess(command: string, options?: NimbusExecOptions): Promise<NimbusStartResult>;
|
|
153
302
|
runCode(code: string, options?: NimbusExecOptions & {
|
|
154
303
|
language?: 'javascript' | 'typescript' | 'python' | 'ruby' | 'shell';
|
|
155
304
|
install?: 'never' | 'ifMissing';
|
|
156
305
|
}): Promise<NimbusExecResult>;
|
|
306
|
+
destroy(options?: NimbusDestroyOptions): Promise<NimbusDestroyResult>;
|
|
157
307
|
files: {
|
|
158
308
|
read: (path: string) => Promise<string | null>;
|
|
159
309
|
readBytes: (path: string) => Promise<Uint8Array | null>;
|
|
160
310
|
write: (path: string, content: string | Uint8Array) => Promise<void>;
|
|
311
|
+
stat: (path: string) => Promise<NimbusFileStat | null>;
|
|
312
|
+
/** stat without following a symlink leaf. */
|
|
313
|
+
lstat: (path: string) => Promise<NimbusFileStat | null>;
|
|
314
|
+
rename: (from: string, to: string) => Promise<void>;
|
|
315
|
+
chmod: (path: string, mode: number) => Promise<void>;
|
|
316
|
+
/** Read `length` bytes at `offset` without materializing the whole file. */
|
|
317
|
+
readRange: (path: string, offset: number, length: number) => Promise<Uint8Array | null>;
|
|
161
318
|
list: (path?: string) => Promise<{
|
|
162
319
|
name: string;
|
|
163
320
|
type: string;
|
|
@@ -188,10 +345,24 @@ export declare class NimbusSandbox {
|
|
|
188
345
|
ok: boolean;
|
|
189
346
|
pid: number;
|
|
190
347
|
}>;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}
|
|
348
|
+
write: (pid: number, data: string) => Promise<{
|
|
349
|
+
ok: boolean;
|
|
350
|
+
pid: number;
|
|
351
|
+
}>;
|
|
352
|
+
endInput: (pid: number) => Promise<{
|
|
353
|
+
ok: boolean;
|
|
354
|
+
pid: number;
|
|
355
|
+
}>;
|
|
356
|
+
resize: (pid: number, size: NimbusTerminalSize) => Promise<{
|
|
357
|
+
ok: boolean;
|
|
358
|
+
pid: number;
|
|
359
|
+
}>;
|
|
360
|
+
signal: (pid: number, signal: string) => Promise<{
|
|
361
|
+
ok: boolean;
|
|
362
|
+
pid: number;
|
|
363
|
+
}>;
|
|
364
|
+
logs: (pid: number, options?: NimbusProcessLogsOptions) => Promise<NimbusProcessLogsResult>;
|
|
365
|
+
attach: (pid: number, options?: NimbusProcessAttachOptions) => NimbusProcessAttachment;
|
|
195
366
|
};
|
|
196
367
|
ports: {
|
|
197
368
|
list: () => Promise<NimbusPort[]>;
|
|
@@ -230,7 +401,7 @@ export declare class NimbusSandbox {
|
|
|
230
401
|
execute: (input: unknown) => Promise<string | null>;
|
|
231
402
|
};
|
|
232
403
|
writeFile: {
|
|
233
|
-
execute: (input:
|
|
404
|
+
execute: (input: unknown) => Promise<void>;
|
|
234
405
|
};
|
|
235
406
|
listFiles: {
|
|
236
407
|
execute: (input?: unknown) => Promise<{
|
|
@@ -245,7 +416,7 @@ export declare class NimbusSandbox {
|
|
|
245
416
|
}[]>;
|
|
246
417
|
};
|
|
247
418
|
deleteFile: {
|
|
248
|
-
execute: (input:
|
|
419
|
+
execute: (input: unknown) => Promise<void>;
|
|
249
420
|
};
|
|
250
421
|
exists: {
|
|
251
422
|
execute: (input: unknown) => Promise<boolean>;
|
|
@@ -261,12 +432,48 @@ export declare class NimbusSandbox {
|
|
|
261
432
|
pid: number;
|
|
262
433
|
}>;
|
|
263
434
|
};
|
|
435
|
+
writeProcessInput: {
|
|
436
|
+
execute: (input: {
|
|
437
|
+
pid: number;
|
|
438
|
+
data: string;
|
|
439
|
+
}) => Promise<{
|
|
440
|
+
ok: boolean;
|
|
441
|
+
pid: number;
|
|
442
|
+
}>;
|
|
443
|
+
};
|
|
444
|
+
endProcessInput: {
|
|
445
|
+
execute: (input: number | {
|
|
446
|
+
pid: number;
|
|
447
|
+
}) => Promise<{
|
|
448
|
+
ok: boolean;
|
|
449
|
+
pid: number;
|
|
450
|
+
}>;
|
|
451
|
+
};
|
|
452
|
+
resizeProcess: {
|
|
453
|
+
execute: (input: {
|
|
454
|
+
pid: number;
|
|
455
|
+
columns: number;
|
|
456
|
+
rows: number;
|
|
457
|
+
}) => Promise<{
|
|
458
|
+
ok: boolean;
|
|
459
|
+
pid: number;
|
|
460
|
+
}>;
|
|
461
|
+
};
|
|
462
|
+
signalProcess: {
|
|
463
|
+
execute: (input: {
|
|
464
|
+
pid: number;
|
|
465
|
+
signal: string;
|
|
466
|
+
}) => Promise<{
|
|
467
|
+
ok: boolean;
|
|
468
|
+
pid: number;
|
|
469
|
+
}>;
|
|
470
|
+
};
|
|
264
471
|
logs: {
|
|
265
472
|
execute: (input: number | {
|
|
266
473
|
pid: number;
|
|
267
474
|
lines?: number;
|
|
268
475
|
bytes?: number;
|
|
269
|
-
}) => Promise<
|
|
476
|
+
}) => Promise<NimbusProcessLogsResult>;
|
|
270
477
|
};
|
|
271
478
|
exposePort: {
|
|
272
479
|
execute: (input: number | {
|
|
@@ -304,7 +511,47 @@ export declare class NimbusSandbox {
|
|
|
304
511
|
capabilities(): string[];
|
|
305
512
|
private execOptions;
|
|
306
513
|
private assertRuntimeAllowed;
|
|
514
|
+
/**
|
|
515
|
+
* Browser-facing URL for an exposed port, or undefined when the deployment
|
|
516
|
+
* is not addressable (no `endpoint`, no configured preview base).
|
|
517
|
+
*
|
|
518
|
+
* The URL carries NO credential. On a deployment with auth enforced it is
|
|
519
|
+
* the destination, not the ticket: the session mints a single-use attach
|
|
520
|
+
* token for it at `GET /s/<id>/api/preview-url?port=<n>`, which is what the
|
|
521
|
+
* session shell opens and what an embedder should hand to a browser.
|
|
522
|
+
*/
|
|
307
523
|
private portUrl;
|
|
524
|
+
private rpc;
|
|
525
|
+
}
|
|
526
|
+
export declare class NimbusProcessAttachment implements AsyncIterable<NimbusProcessLogChunk> {
|
|
527
|
+
private readonly sandbox;
|
|
528
|
+
readonly pid: number;
|
|
529
|
+
private readonly options;
|
|
530
|
+
private cursor;
|
|
531
|
+
constructor(sandbox: NimbusSandbox, pid: number, options?: NimbusProcessAttachOptions);
|
|
532
|
+
write(data: string): Promise<{
|
|
533
|
+
ok: boolean;
|
|
534
|
+
pid: number;
|
|
535
|
+
}>;
|
|
536
|
+
endInput(): Promise<{
|
|
537
|
+
ok: boolean;
|
|
538
|
+
pid: number;
|
|
539
|
+
}>;
|
|
540
|
+
resize(size: NimbusTerminalSize): Promise<{
|
|
541
|
+
ok: boolean;
|
|
542
|
+
pid: number;
|
|
543
|
+
}>;
|
|
544
|
+
signal(signal: string): Promise<{
|
|
545
|
+
ok: boolean;
|
|
546
|
+
pid: number;
|
|
547
|
+
}>;
|
|
548
|
+
kill(): Promise<{
|
|
549
|
+
ok: boolean;
|
|
550
|
+
pid: number;
|
|
551
|
+
}>;
|
|
552
|
+
logs(options?: NimbusProcessLogsOptions): Promise<NimbusProcessLogsResult>;
|
|
553
|
+
stream(options?: NimbusProcessAttachOptions): AsyncIterable<NimbusProcessLogChunk>;
|
|
554
|
+
[Symbol.asyncIterator](): AsyncIterator<NimbusProcessLogChunk>;
|
|
308
555
|
}
|
|
309
556
|
export {};
|
|
310
557
|
//# sourceMappingURL=sandbox.d.ts.map
|