@phpsandbox/sdk 0.0.68 → 0.1.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/README.md +66 -32
- package/dist/auth.d.ts +0 -1
- package/dist/auth.js +0 -3
- package/dist/beacon/index.d.ts +2 -0
- package/dist/beacon/index.js +5 -0
- package/dist/browser/phpsandbox-sdk.esm.js +842 -1320
- package/dist/browser/phpsandbox-sdk.esm.min.js +24 -24
- package/dist/browser/phpsandbox-sdk.iife.js +842 -1320
- package/dist/browser/phpsandbox-sdk.iife.min.js +23 -23
- package/dist/composer.d.ts +36 -23
- package/dist/composer.js +61 -11
- package/dist/container.d.ts +2 -6
- package/dist/container.js +0 -28
- package/dist/filesystem.d.ts +58 -17
- package/dist/filesystem.js +169 -51
- package/dist/index.d.ts +30 -26
- package/dist/index.js +37 -32
- package/dist/log.d.ts +11 -5
- package/dist/log.js +10 -7
- package/dist/metrics.d.ts +10 -0
- package/dist/metrics.js +13 -0
- package/dist/ports.d.ts +10 -0
- package/dist/ports.js +13 -0
- package/dist/publications.d.ts +222 -0
- package/dist/{deployments.js → publications.js} +64 -69
- package/dist/repl.d.ts +0 -2
- package/dist/repl.js +0 -3
- package/dist/runtime/rest.js +1 -4
- package/dist/runtime-telemetry.d.ts +15 -0
- package/dist/runtime-telemetry.js +111 -0
- package/dist/schemas/{deployments.d.ts → publications.d.ts} +76 -76
- package/dist/schemas/publications.js +133 -0
- package/dist/services.d.ts +2 -9
- package/dist/services.js +3 -10
- package/dist/shell.d.ts +52 -22
- package/dist/shell.js +103 -75
- package/dist/terminal.d.ts +18 -14
- package/dist/terminal.js +29 -49
- package/docs/api-summary.md +37 -29
- package/docs/cdn-usage.md +4 -4
- package/docs/getting-started.md +17 -17
- package/examples/README.md +8 -9
- package/examples/basic.ts +2 -2
- package/examples/git-merge-smoke.ts +3 -6
- package/examples/laravel-load-test.ts +80 -98
- package/examples/notebook-readonly-smoke.ts +38 -41
- package/examples/publication-provider-label.ts +11 -0
- package/examples/{deploy-cloudflare-container.ts → publish-cloudflare-container.ts} +11 -11
- package/examples/{deploy-laravel-cloud.ts → publish-laravel-cloud.ts} +11 -11
- package/examples/{deploy-ssh-agent.ts → publish-ssh-agent.ts} +15 -16
- package/package.json +7 -2
- package/dist/deployments.d.ts +0 -251
- package/dist/laravel.d.ts +0 -22
- package/dist/laravel.js +0 -11
- package/dist/schemas/deployments.js +0 -133
- package/examples/deployment-provider-label.ts +0 -11
package/dist/shell.js
CHANGED
|
@@ -1,108 +1,136 @@
|
|
|
1
1
|
import { nanoid } from 'nanoid';
|
|
2
|
-
function
|
|
2
|
+
function resolveRunInput(command, argsOrOpts = [], opts) {
|
|
3
3
|
const args = Array.isArray(argsOrOpts) ? argsOrOpts : [];
|
|
4
4
|
const resolvedOpts = Array.isArray(argsOrOpts) ? opts : argsOrOpts;
|
|
5
5
|
return {
|
|
6
6
|
command: Array.isArray(command) ? command : args.length > 0 ? [command, ...args] : command,
|
|
7
|
-
opts: resolvedOpts,
|
|
7
|
+
opts: { tty: false, ...resolvedOpts },
|
|
8
8
|
};
|
|
9
9
|
}
|
|
10
10
|
export class CommandError extends Error {
|
|
11
|
-
constructor(
|
|
12
|
-
super(output);
|
|
13
|
-
this.
|
|
14
|
-
this.exitCode = exitCode;
|
|
11
|
+
constructor(result) {
|
|
12
|
+
super(result.output);
|
|
13
|
+
this.result = result;
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
class
|
|
18
|
-
constructor(output, exitCode) {
|
|
16
|
+
export class ProcessResult {
|
|
17
|
+
constructor(output, stdout, stderr, exitCode) {
|
|
19
18
|
this.output = output;
|
|
19
|
+
this.stdout = stdout;
|
|
20
|
+
this.stderr = stderr;
|
|
20
21
|
this.exitCode = exitCode;
|
|
21
22
|
}
|
|
22
23
|
throw() {
|
|
23
24
|
if (this.exitCode !== 0) {
|
|
24
|
-
throw new CommandError(this
|
|
25
|
+
throw new CommandError(this);
|
|
25
26
|
}
|
|
26
27
|
return this;
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
|
-
export
|
|
30
|
-
constructor(
|
|
31
|
-
this.
|
|
30
|
+
export class ProcessReadable {
|
|
31
|
+
constructor() {
|
|
32
|
+
this.controller = null;
|
|
33
|
+
this.closed = false;
|
|
34
|
+
this.stream = new ReadableStream({
|
|
35
|
+
start: (controller) => {
|
|
36
|
+
this.controller = controller;
|
|
37
|
+
},
|
|
38
|
+
cancel: () => {
|
|
39
|
+
this.closed = true;
|
|
40
|
+
this.controller = null;
|
|
41
|
+
},
|
|
42
|
+
});
|
|
32
43
|
}
|
|
33
|
-
|
|
34
|
-
|
|
44
|
+
enqueue(value) {
|
|
45
|
+
if (!this.closed) {
|
|
46
|
+
this.controller?.enqueue(value);
|
|
47
|
+
}
|
|
35
48
|
}
|
|
36
|
-
|
|
37
|
-
|
|
49
|
+
close() {
|
|
50
|
+
if (this.closed) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
this.closed = true;
|
|
54
|
+
this.controller?.close();
|
|
55
|
+
this.controller = null;
|
|
38
56
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
return new Result(result.output, result.exitCode);
|
|
57
|
+
fail(error) {
|
|
58
|
+
if (this.closed) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
this.closed = true;
|
|
62
|
+
this.controller?.error(error);
|
|
63
|
+
this.controller = null;
|
|
47
64
|
}
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
dispose();
|
|
66
|
-
if (controller) {
|
|
67
|
-
try {
|
|
68
|
-
controller.close();
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
// Ignore close errors after cancellation.
|
|
65
|
+
}
|
|
66
|
+
export class ShellProcess {
|
|
67
|
+
constructor(id, okra, command, opts) {
|
|
68
|
+
this.id = id;
|
|
69
|
+
this.okra = okra;
|
|
70
|
+
const output = new ProcessReadable();
|
|
71
|
+
const stdout = new ProcessReadable();
|
|
72
|
+
const stderr = new ProcessReadable();
|
|
73
|
+
this.output = output.stream;
|
|
74
|
+
this.stdout = stdout.stream;
|
|
75
|
+
this.stderr = stderr.stream;
|
|
76
|
+
this.stdin = new WritableStream({
|
|
77
|
+
write: async (chunk) => {
|
|
78
|
+
const input = typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk);
|
|
79
|
+
const written = await this.okra.invoke('shell.input', { id: this.id, input });
|
|
80
|
+
if (!written) {
|
|
81
|
+
throw new Error(`Process ${this.id} is not accepting input`);
|
|
72
82
|
}
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
const encoder = new TextEncoder();
|
|
86
|
+
let subscription = this.okra.listen(`shell.output.${this.id}`, (event) => {
|
|
87
|
+
const data = encoder.encode(event.output);
|
|
88
|
+
const source = event.source ?? 'output';
|
|
89
|
+
output.enqueue({ source, data });
|
|
90
|
+
if (source === 'stderr') {
|
|
91
|
+
stderr.enqueue(data);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
stdout.enqueue(data);
|
|
73
95
|
}
|
|
96
|
+
});
|
|
97
|
+
const finish = () => {
|
|
98
|
+
subscription?.dispose();
|
|
99
|
+
subscription = null;
|
|
100
|
+
output.close();
|
|
101
|
+
stdout.close();
|
|
102
|
+
stderr.close();
|
|
74
103
|
};
|
|
75
104
|
const fail = (error) => {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
if (controller) {
|
|
82
|
-
controller.error(error);
|
|
83
|
-
}
|
|
105
|
+
subscription?.dispose();
|
|
106
|
+
subscription = null;
|
|
107
|
+
output.fail(error);
|
|
108
|
+
stdout.fail(error);
|
|
109
|
+
stderr.fail(error);
|
|
84
110
|
};
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}));
|
|
91
|
-
},
|
|
92
|
-
cancel: () => {
|
|
93
|
-
closed = true;
|
|
94
|
-
dispose();
|
|
95
|
-
},
|
|
96
|
-
});
|
|
97
|
-
const result = this.exec(input.command, { ...input.opts, id })
|
|
98
|
-
.then((response) => {
|
|
99
|
-
close();
|
|
100
|
-
return response;
|
|
101
|
-
})
|
|
102
|
-
.catch((error) => {
|
|
111
|
+
const { abortSignal, ...payloadOpts } = opts;
|
|
112
|
+
this.completion = this.okra.invoke('shell.exec', { command, opts: { ...payloadOpts, id: this.id } }, { abortSignal }).then((result) => {
|
|
113
|
+
finish();
|
|
114
|
+
return new ProcessResult(result.output, result.stdout, result.stderr, result.exitCode);
|
|
115
|
+
}).catch((error) => {
|
|
103
116
|
fail(error);
|
|
104
117
|
throw error;
|
|
105
118
|
});
|
|
106
|
-
|
|
119
|
+
}
|
|
120
|
+
wait() {
|
|
121
|
+
return this.completion;
|
|
122
|
+
}
|
|
123
|
+
async kill() {
|
|
124
|
+
await this.okra.invoke('shell.kill', { id: this.id });
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export default class Shell {
|
|
128
|
+
constructor(okra) {
|
|
129
|
+
this.okra = okra;
|
|
130
|
+
}
|
|
131
|
+
run(command, argsOrOpts = [], opts) {
|
|
132
|
+
const input = resolveRunInput(command, argsOrOpts, opts);
|
|
133
|
+
const id = input.opts?.id ?? nanoid();
|
|
134
|
+
return new ShellProcess(id, this.okra, input.command, { ...input.opts, id });
|
|
107
135
|
}
|
|
108
136
|
}
|
package/dist/terminal.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Action,
|
|
1
|
+
import { Action, NotebookInstance } from './index.js';
|
|
2
2
|
export interface Task {
|
|
3
3
|
id: string;
|
|
4
4
|
command: string | string[];
|
|
@@ -14,7 +14,7 @@ export interface TerminalCreateInput {
|
|
|
14
14
|
kind: string;
|
|
15
15
|
size: [number, number];
|
|
16
16
|
command?: string | string[];
|
|
17
|
-
env?: Record<string, string>;
|
|
17
|
+
env?: Record<string, string | number | boolean>;
|
|
18
18
|
cwd?: string;
|
|
19
19
|
}
|
|
20
20
|
export interface TerminalSpawnInput {
|
|
@@ -49,7 +49,6 @@ export interface TerminalActions {
|
|
|
49
49
|
input: string;
|
|
50
50
|
}>;
|
|
51
51
|
'terminal.list': Action<{}, Task[]>;
|
|
52
|
-
'terminal.start': Action;
|
|
53
52
|
'terminal.create': Action<TerminalCreateInput, Task>;
|
|
54
53
|
'terminal.spawn': Action<TerminalSpawnInput, Task>;
|
|
55
54
|
'terminal.attach': Action<{
|
|
@@ -66,8 +65,8 @@ export interface TerminalActions {
|
|
|
66
65
|
}, boolean>;
|
|
67
66
|
}
|
|
68
67
|
export interface SandboxProcess {
|
|
69
|
-
|
|
70
|
-
input: WritableStream<string>;
|
|
68
|
+
wait(): Promise<number>;
|
|
69
|
+
input: WritableStream<string | Uint8Array>;
|
|
71
70
|
output: ReadableStream<string>;
|
|
72
71
|
kill(): Promise<boolean>;
|
|
73
72
|
resize(dimensions: {
|
|
@@ -88,22 +87,27 @@ export interface SpawnOptions {
|
|
|
88
87
|
tty?: boolean;
|
|
89
88
|
abortSignal?: AbortSignal;
|
|
90
89
|
}
|
|
91
|
-
export
|
|
90
|
+
export interface TerminalOptions {
|
|
91
|
+
command?: string | string[];
|
|
92
|
+
args?: string[];
|
|
93
|
+
kind?: string;
|
|
94
|
+
size?: [number, number];
|
|
95
|
+
cwd?: string;
|
|
96
|
+
env?: Record<string, string | number | boolean>;
|
|
97
|
+
id?: string;
|
|
98
|
+
osc?: boolean;
|
|
99
|
+
tty?: boolean;
|
|
100
|
+
abortSignal?: AbortSignal;
|
|
101
|
+
}
|
|
102
|
+
export default class Terminals {
|
|
92
103
|
protected okra: NotebookInstance;
|
|
93
104
|
constructor(okra: NotebookInstance);
|
|
94
105
|
list(): Promise<Task[]>;
|
|
95
|
-
|
|
96
|
-
create(input: TerminalCreateInput): Promise<Task>;
|
|
97
|
-
onStarted(handler: (t: Task) => void): Disposable;
|
|
98
|
-
resize(id: string, size: [number, number]): Promise<boolean>;
|
|
99
|
-
onOutput(id: string, handler: (data: TerminalEvents['terminal.output']) => void): Disposable;
|
|
100
|
-
input(id: string, input: string): Promise<boolean>;
|
|
101
|
-
listen<T extends keyof TerminalEvents>(event: T, handler: (data: TerminalEvents[T]) => void): Disposable;
|
|
106
|
+
create(options?: TerminalOptions): Promise<SandboxProcess & Task>;
|
|
102
107
|
attach(id: string, opts?: {
|
|
103
108
|
replayHistory?: boolean;
|
|
104
109
|
abortSignal?: AbortSignal;
|
|
105
110
|
}): Promise<(SandboxProcess & Task) | false>;
|
|
106
|
-
spawn(command: string, args: string[], opts?: SpawnOptions): Promise<SandboxProcess & Task>;
|
|
107
111
|
private processHandle;
|
|
108
112
|
}
|
|
109
113
|
export {};
|
package/dist/terminal.js
CHANGED
|
@@ -2,33 +2,34 @@ import { nanoid } from 'nanoid';
|
|
|
2
2
|
import { once } from './utils/promise.js';
|
|
3
3
|
const TERMINAL_INPUT_FLUSH_DELAY_MS = 8;
|
|
4
4
|
const terminalInputFlushPattern = /[\x00-\x1f\x7f]/;
|
|
5
|
-
export default class
|
|
5
|
+
export default class Terminals {
|
|
6
6
|
constructor(okra) {
|
|
7
7
|
this.okra = okra;
|
|
8
8
|
}
|
|
9
9
|
list() {
|
|
10
10
|
return this.okra.invoke('terminal.list');
|
|
11
11
|
}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
12
|
+
async create(options = {}) {
|
|
13
|
+
const id = options.id ?? nanoid();
|
|
14
|
+
const handle = this.processHandle(id, options.abortSignal);
|
|
15
|
+
const command = typeof options.command === 'string' && options.args?.length
|
|
16
|
+
? [options.command, ...options.args]
|
|
17
|
+
: options.command;
|
|
18
|
+
const { abortSignal, args: _args, ...input } = options;
|
|
19
|
+
try {
|
|
20
|
+
const task = await this.okra.invoke('terminal.create', {
|
|
21
|
+
...input,
|
|
22
|
+
id,
|
|
23
|
+
kind: options.kind ?? 'bash',
|
|
24
|
+
size: options.size ?? [80, 24],
|
|
25
|
+
command,
|
|
26
|
+
}, { abortSignal });
|
|
27
|
+
return { ...handle.process, ...task };
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
handle.dispose();
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
32
33
|
}
|
|
33
34
|
async attach(id, opts) {
|
|
34
35
|
const handle = this.processHandle(id, opts?.abortSignal);
|
|
@@ -51,28 +52,6 @@ export default class Terminal {
|
|
|
51
52
|
throw error;
|
|
52
53
|
}
|
|
53
54
|
}
|
|
54
|
-
async spawn(command, args, opts) {
|
|
55
|
-
const id = opts?.id || nanoid();
|
|
56
|
-
const handle = this.processHandle(id, opts?.abortSignal);
|
|
57
|
-
const { abortSignal, ...otherOpts } = opts ?? {};
|
|
58
|
-
try {
|
|
59
|
-
const result = await this.okra.invoke('terminal.spawn', {
|
|
60
|
-
command: [command, ...args],
|
|
61
|
-
opts: {
|
|
62
|
-
id,
|
|
63
|
-
...otherOpts,
|
|
64
|
-
},
|
|
65
|
-
}, { abortSignal });
|
|
66
|
-
return {
|
|
67
|
-
...handle.process,
|
|
68
|
-
...result,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
catch (error) {
|
|
72
|
-
handle.dispose();
|
|
73
|
-
throw error;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
55
|
processHandle(id, abortSignal) {
|
|
77
56
|
const disposables = new Set();
|
|
78
57
|
let abortHandler;
|
|
@@ -98,7 +77,7 @@ export default class Terminal {
|
|
|
98
77
|
}
|
|
99
78
|
const input = pendingInput;
|
|
100
79
|
pendingInput = '';
|
|
101
|
-
void this.input
|
|
80
|
+
void this.okra.send('terminal.input', { id, input });
|
|
102
81
|
};
|
|
103
82
|
const scheduleInputFlush = () => {
|
|
104
83
|
if (inputFlushTimer) {
|
|
@@ -106,7 +85,8 @@ export default class Terminal {
|
|
|
106
85
|
}
|
|
107
86
|
inputFlushTimer = setTimeout(flushInput, TERMINAL_INPUT_FLUSH_DELAY_MS);
|
|
108
87
|
};
|
|
109
|
-
const queueInput = (
|
|
88
|
+
const queueInput = (value) => {
|
|
89
|
+
const chunk = typeof value === 'string' ? value : new TextDecoder().decode(value);
|
|
110
90
|
pendingInput += chunk;
|
|
111
91
|
if (terminalInputFlushPattern.test(chunk)) {
|
|
112
92
|
flushInput();
|
|
@@ -127,7 +107,7 @@ export default class Terminal {
|
|
|
127
107
|
const output = new ReadableStream({
|
|
128
108
|
start: (_controller) => {
|
|
129
109
|
controller = _controller;
|
|
130
|
-
disposables.add(this.listen(`terminal.output.${id}`, (data) => {
|
|
110
|
+
disposables.add(this.okra.listen(`terminal.output.${id}`, (data) => {
|
|
131
111
|
controller?.enqueue(data.output);
|
|
132
112
|
}));
|
|
133
113
|
},
|
|
@@ -136,8 +116,8 @@ export default class Terminal {
|
|
|
136
116
|
disposeInput();
|
|
137
117
|
},
|
|
138
118
|
});
|
|
139
|
-
const
|
|
140
|
-
disposables.add(this.listen(`terminal.close.${id}`, (data) => {
|
|
119
|
+
const completion = new Promise((resolve) => {
|
|
120
|
+
disposables.add(this.okra.listen(`terminal.close.${id}`, (data) => {
|
|
141
121
|
if (controller) {
|
|
142
122
|
try {
|
|
143
123
|
controller.close();
|
|
@@ -165,7 +145,7 @@ export default class Terminal {
|
|
|
165
145
|
}
|
|
166
146
|
return {
|
|
167
147
|
process: {
|
|
168
|
-
|
|
148
|
+
wait: () => completion,
|
|
169
149
|
input,
|
|
170
150
|
output,
|
|
171
151
|
kill,
|
package/docs/api-summary.md
CHANGED
|
@@ -23,45 +23,55 @@ Connection lifecycle methods, streaming actions, and event subscriptions require
|
|
|
23
23
|
|
|
24
24
|
- `create(template, input?, init = true)`
|
|
25
25
|
- `get(id)`
|
|
26
|
-
- `open(id)`
|
|
27
26
|
- `fork(id, input?)`
|
|
28
|
-
- `
|
|
27
|
+
- `destroy(id)`
|
|
29
28
|
- `openFromData(data)`
|
|
30
29
|
- `mail.status(id)`, `mail.enable(id)`, `mail.disable(id)`, `mail.list(id, options?)`, `mail.get(id, hash)`, `mail.delete(id, hash)`
|
|
31
30
|
|
|
32
31
|
## `NotebookInstance`
|
|
33
32
|
|
|
34
33
|
- Lifecycle: `ready()`, `connected()`, `whenConnected()`, `reconnect()`, `dispose()`
|
|
35
|
-
- Notebook actions: `fork(input?)`, `
|
|
34
|
+
- Notebook actions: `fork(input?)`, `destroy()`, `stop()`, `restart()`, `ping()`, `update()`
|
|
35
|
+
- Processes: `exec(command, args?, options?)`, or `run(command, args?, options?)` returning native `stdin`, `stdout`, `stderr`, ordered `output`, `wait()`, and `kill()`
|
|
36
36
|
- Events: `listen(event, handler)`, `onDidConnect(handler)`, `onDidDisconnect(handler)`, `onDidInitialize(handler)`
|
|
37
37
|
- Raw call: `invoke(action, data?, options?)`
|
|
38
38
|
|
|
39
39
|
## Services
|
|
40
40
|
|
|
41
|
-
### Filesystem (`notebook.
|
|
41
|
+
### Filesystem (`notebook.files`)
|
|
42
42
|
|
|
43
|
-
- File data: `
|
|
44
|
-
- File management: `move()`, `
|
|
45
|
-
- Directory: `
|
|
43
|
+
- File data: `read()`, `readText()`, `readStream()`, `write()`, `tail()`, `stat()`, `exists()`
|
|
44
|
+
- File management: `move()`, `copy()`, `remove()`
|
|
45
|
+
- Directory: `createDirectory()`, `list()`, `tree()`
|
|
46
46
|
- Search: `find()`, `search()`
|
|
47
|
-
- Streaming/monitoring: `watch()`, `download()`
|
|
47
|
+
- Streaming/monitoring: `readStream()`, `watch()`, `download()`
|
|
48
|
+
- `tail()` is an eager last-lines snapshot; it does not follow future appends.
|
|
48
49
|
|
|
49
|
-
### Terminal (`notebook.
|
|
50
|
+
### Terminal (`notebook.terminals`)
|
|
50
51
|
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
52
|
+
- `list()` returns terminal metadata
|
|
53
|
+
- `create(options?)` returns a terminal handle
|
|
54
|
+
- `attach(id, options?)` returns a terminal handle
|
|
55
|
+
- Handles expose `input`, `output`, `wait()`, `resize()`, and `kill()`
|
|
54
56
|
|
|
55
57
|
### Container (`notebook.container`)
|
|
56
58
|
|
|
57
59
|
- Lifecycle: `start()`, `stop()`, `state()`
|
|
58
|
-
- Ports: `openedPorts()`, `onPort(handler)`
|
|
59
60
|
- Runtime config: `setPhp(version)`
|
|
60
|
-
- Telemetry: `enableTelemetry(features)`, `stopTelemetry()`, `listen(event, handler)`
|
|
61
61
|
|
|
62
|
-
###
|
|
62
|
+
### Runtime observability
|
|
63
63
|
|
|
64
|
-
- `
|
|
64
|
+
- `notebook.logs.follow({ signal? })` returns `ReadableStream<RuntimeLogEntry>`
|
|
65
|
+
- `notebook.ports.list()` returns the current preview ports
|
|
66
|
+
- `notebook.ports.watch({ signal? })` returns `ReadableStream<PortInfo[]>`
|
|
67
|
+
- `notebook.metrics.current()` returns the current CPU, memory, and disk sample
|
|
68
|
+
- `notebook.metrics.watch({ signal? })` returns `ReadableStream<ContainerStats>`
|
|
69
|
+
|
|
70
|
+
### Processes
|
|
71
|
+
|
|
72
|
+
- `notebook.run(command, args?, options?)` returns a streaming process handle
|
|
73
|
+
- `notebook.exec(command, args?, options?)` returns a buffered `ProcessResult` for one-shot commands
|
|
74
|
+
- Process handles expose `output`, `stdout`, `stderr`, `stdin`, `wait()`, and `kill()`
|
|
65
75
|
|
|
66
76
|
### Services (`notebook.services`)
|
|
67
77
|
|
|
@@ -70,13 +80,13 @@ Connection lifecycle methods, streaming actions, and event subscriptions require
|
|
|
70
80
|
- `run(name, command?)`
|
|
71
81
|
- `stop(name)`
|
|
72
82
|
- `logs(name, { tail? })`
|
|
73
|
-
- `logs(name, { follow: true, tail?, id? })` returning `
|
|
74
|
-
- Events: `listen()`, `onLog(id, handler)`
|
|
83
|
+
- `logs(name, { follow: true, tail?, id? })` returning `ReadableStream<string>`; cancel the stream to stop following
|
|
75
84
|
|
|
76
85
|
### Composer (`notebook.composer`)
|
|
77
86
|
|
|
78
|
-
- Core: `
|
|
79
|
-
- Helpers: `install()`, `update()`, `require()`, `remove()`, `dumpAutoload()`, `packages()
|
|
87
|
+
- Core: `run(command, args?, options?)`
|
|
88
|
+
- Helpers: `install()`, `update()`, `require()`, `remove()`, `dumpAutoload()`, `packages()`
|
|
89
|
+
- Command handles expose `output`, `stdout`, `stderr`, `wait()`, and `kill()`
|
|
80
90
|
|
|
81
91
|
### Git (`notebook.git`)
|
|
82
92
|
|
|
@@ -114,19 +124,17 @@ Connection lifecycle methods, streaming actions, and event subscriptions require
|
|
|
114
124
|
- `stop()`, `eval(code, args?, replOpts?)`, `write(input)`, `resize(cols, rows)`
|
|
115
125
|
- Events: `listen()`, `onOutput()`
|
|
116
126
|
|
|
117
|
-
### Laravel (`notebook.laravel`)
|
|
118
|
-
|
|
119
|
-
- Wrapper exists in the SDK, but the current runtime action set does not expose Laravel maintenance actions.
|
|
120
|
-
|
|
121
127
|
### Auth (`notebook.auth`)
|
|
122
128
|
|
|
123
129
|
- `login(runtimeUrl)` using a fresh ticket-bearing runtime URL
|
|
124
|
-
- `logout()`
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
## Publications
|
|
127
132
|
|
|
128
|
-
- `
|
|
129
|
-
- `
|
|
133
|
+
- `notebook.publish({ slug, provider, protection?, feedback?, metadata? })`
|
|
134
|
+
- `notebook.publication()` returns the current publication or `null`
|
|
135
|
+
- `client.publications.get(id)`, `destroy(id)`, `events(id)`, `logs(id)`, and `buildLogs(id, buildId)`
|
|
136
|
+
- A publish run exposes `events()` as an `AsyncIterable`, `result()`, and `publication()`.
|
|
137
|
+
- Built-in provider names are `cloudflare-containers`, `ssh-server`, and `laravel-cloud`. Provider maps support TypeScript declaration merging.
|
|
130
138
|
|
|
131
139
|
## Common Errors
|
|
132
140
|
|
|
@@ -137,6 +145,6 @@ Connection lifecycle methods, streaming actions, and event subscriptions require
|
|
|
137
145
|
|
|
138
146
|
## Notes for Consumers
|
|
139
147
|
|
|
140
|
-
- `
|
|
148
|
+
- `get()` does not auto-initialize; call `await notebook.ready()`.
|
|
141
149
|
- `create()` and `fork()` initialize by default.
|
|
142
150
|
- Always call `notebook.dispose()` when finished.
|
package/docs/cdn-usage.md
CHANGED
|
@@ -15,10 +15,10 @@ Do not hardcode long-lived private API tokens in public frontend code. Use short
|
|
|
15
15
|
import { PHPSandbox } from 'https://unpkg.com/@phpsandbox/sdk@0.0.51/dist/browser/phpsandbox-sdk.esm.min.js';
|
|
16
16
|
|
|
17
17
|
const client = new PHPSandbox('your-short-lived-token');
|
|
18
|
-
const notebook = await client.notebook.
|
|
18
|
+
const notebook = await client.notebook.get('your-notebook-id');
|
|
19
19
|
await notebook.ready();
|
|
20
20
|
|
|
21
|
-
const result = await notebook.
|
|
21
|
+
const result = await notebook.exec('php -v', { tty: false });
|
|
22
22
|
result.throw();
|
|
23
23
|
console.log(result.output);
|
|
24
24
|
</script>
|
|
@@ -43,9 +43,9 @@ Do not hardcode long-lived private API tokens in public frontend code. Use short
|
|
|
43
43
|
<script>
|
|
44
44
|
const client = new PHPSandbox.PHPSandbox('your-short-lived-token');
|
|
45
45
|
|
|
46
|
-
client.notebook.
|
|
46
|
+
client.notebook.get('your-notebook-id').then(async (notebook) => {
|
|
47
47
|
await notebook.ready();
|
|
48
|
-
const result = await notebook.
|
|
48
|
+
const result = await notebook.exec('php -v', { tty: false });
|
|
49
49
|
console.log(result.output);
|
|
50
50
|
notebook.dispose();
|
|
51
51
|
});
|
package/docs/getting-started.md
CHANGED
|
@@ -35,8 +35,8 @@ const persistent = await client.notebook.create('laravel', {
|
|
|
35
35
|
persistent: true,
|
|
36
36
|
});
|
|
37
37
|
|
|
38
|
-
//
|
|
39
|
-
const existing = await client.notebook.
|
|
38
|
+
// get() needs ready()
|
|
39
|
+
const existing = await client.notebook.get('notebook-id');
|
|
40
40
|
await existing.ready();
|
|
41
41
|
|
|
42
42
|
const fork = await existing.fork();
|
|
@@ -48,18 +48,18 @@ API notebook creates and forks are ephemeral by default. Use `persistent: true`
|
|
|
48
48
|
## 3. Write and Read Files
|
|
49
49
|
|
|
50
50
|
```ts
|
|
51
|
-
await notebook.
|
|
51
|
+
await notebook.files.write('hello.php', '<?php echo "Hello";');
|
|
52
52
|
|
|
53
|
-
const data = await notebook.
|
|
53
|
+
const data = await notebook.files.read('hello.php');
|
|
54
54
|
const text = new TextDecoder().decode(data);
|
|
55
55
|
|
|
56
56
|
console.log(text);
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
The same `write()` method accepts text, bytes, readable streams, and async iterables:
|
|
60
60
|
|
|
61
61
|
```ts
|
|
62
|
-
await notebook.
|
|
62
|
+
await notebook.files.write('binary.dat', new Uint8Array([1, 2, 3]), {
|
|
63
63
|
create: true,
|
|
64
64
|
overwrite: true,
|
|
65
65
|
unlock: false,
|
|
@@ -72,7 +72,7 @@ await notebook.file.writeFile('binary.dat', new Uint8Array([1, 2, 3]), {
|
|
|
72
72
|
### Terminal (stream output)
|
|
73
73
|
|
|
74
74
|
```ts
|
|
75
|
-
const task = await notebook.
|
|
75
|
+
const task = await notebook.terminals.create({ command: 'php', args: ['hello.php'] });
|
|
76
76
|
const reader = task.output.getReader();
|
|
77
77
|
|
|
78
78
|
try {
|
|
@@ -85,29 +85,29 @@ try {
|
|
|
85
85
|
reader.releaseLock();
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
-
const exitCode = await task.
|
|
88
|
+
const exitCode = await task.wait();
|
|
89
89
|
console.log(`\nExit code: ${exitCode}`);
|
|
90
90
|
```
|
|
91
91
|
|
|
92
92
|
### Shell (single command result)
|
|
93
93
|
|
|
94
94
|
```ts
|
|
95
|
-
const result = await notebook.
|
|
95
|
+
const result = await notebook.exec('php -v', { tty: false });
|
|
96
96
|
result.throw();
|
|
97
|
-
console.log(result.
|
|
97
|
+
console.log(result.stdout);
|
|
98
98
|
```
|
|
99
99
|
|
|
100
100
|
## 5. Search and Watch Files
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
|
-
const matches = await notebook.
|
|
103
|
+
const matches = await notebook.files.find('*.php', {
|
|
104
104
|
includes: ['app/**'],
|
|
105
105
|
excludes: ['vendor/**'],
|
|
106
106
|
});
|
|
107
107
|
|
|
108
108
|
console.log(matches.map((m) => m.path));
|
|
109
109
|
|
|
110
|
-
const watcher = notebook.
|
|
110
|
+
const watcher = notebook.files.watch(
|
|
111
111
|
'/app',
|
|
112
112
|
{
|
|
113
113
|
recursive: true,
|
|
@@ -125,8 +125,8 @@ watcher.dispose();
|
|
|
125
125
|
## 6. Use Composer and Git
|
|
126
126
|
|
|
127
127
|
```ts
|
|
128
|
-
await notebook.composer.install(
|
|
129
|
-
await notebook.composer.require({ packages: ['monolog/monolog'] });
|
|
128
|
+
(await notebook.composer.install().wait()).throw();
|
|
129
|
+
(await notebook.composer.require({ packages: ['monolog/monolog'] }).wait()).throw();
|
|
130
130
|
|
|
131
131
|
await notebook.git.checkpoint('Jane Doe <jane@example.com>', 'Initial checkpoint');
|
|
132
132
|
```
|
|
@@ -141,9 +141,9 @@ notebook.dispose();
|
|
|
141
141
|
|
|
142
142
|
## Common Pitfalls
|
|
143
143
|
|
|
144
|
-
- `
|
|
145
|
-
- `
|
|
146
|
-
- `
|
|
144
|
+
- `get()` notebooks must call `await notebook.ready()` before tool calls.
|
|
145
|
+
- `files.watch()` requires an `excludes` array.
|
|
146
|
+
- Use `notebook.exec()` for one-shot commands; for streaming shell handles, await `process.wait()` and call `result.throw()` to fail on non-zero exit codes.
|
|
147
147
|
|
|
148
148
|
## Next Reading
|
|
149
149
|
|