@phpsandbox/sdk 0.1.1 → 0.1.4
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 +59 -22
- package/dist/browser/phpsandbox-sdk.esm.js +562 -265
- package/dist/browser/phpsandbox-sdk.esm.min.js +24 -24
- package/dist/browser/phpsandbox-sdk.iife.js +562 -265
- package/dist/browser/phpsandbox-sdk.iife.min.js +24 -24
- 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 +74 -18
- package/dist/filesystem.js +219 -62
- package/dist/index.d.ts +18 -9
- package/dist/index.js +21 -8
- 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/runtime/rest.js +1 -4
- package/dist/runtime-telemetry.d.ts +15 -0
- package/dist/runtime-telemetry.js +111 -0
- package/dist/schemas/publications.d.ts +10 -10
- package/dist/services.d.ts +2 -9
- package/dist/services.js +3 -10
- package/dist/shell.d.ts +9 -0
- package/dist/shell.js +4 -4
- package/dist/terminal.d.ts +17 -11
- package/dist/terminal.js +27 -44
- package/docs/api-summary.md +28 -23
- package/docs/cdn-usage.md +2 -2
- package/docs/getting-started.md +13 -13
- package/examples/basic.ts +1 -1
- package/examples/git-merge-smoke.ts +1 -4
- package/examples/laravel-load-test.ts +67 -80
- package/examples/notebook-readonly-smoke.ts +30 -33
- package/package.json +1 -1
package/dist/filesystem.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { UnsupportedRuntimeTransportError } from './errors/index.js';
|
|
1
2
|
import { ErrorEvent } from './types.js';
|
|
2
3
|
import { nanoid } from 'nanoid';
|
|
3
4
|
export var FileType;
|
|
@@ -84,28 +85,195 @@ export class Filesystem {
|
|
|
84
85
|
}
|
|
85
86
|
});
|
|
86
87
|
}
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
async write(path, contents, options = {}) {
|
|
89
|
+
const resolvedOptions = {
|
|
90
|
+
overwrite: true,
|
|
91
|
+
create: true,
|
|
92
|
+
unlock: false,
|
|
93
|
+
atomic: false,
|
|
94
|
+
...options,
|
|
95
|
+
};
|
|
96
|
+
const source = typeof contents === 'string' ? new TextEncoder().encode(contents) : contents;
|
|
97
|
+
if (this.okra.runtimeTransport === 'rest') {
|
|
98
|
+
const bytes = source instanceof Uint8Array ? source : await collectBytes(source);
|
|
99
|
+
await this.okra.invoke('fs.writeFile', {
|
|
100
|
+
path,
|
|
101
|
+
contents: bytes,
|
|
102
|
+
options: resolvedOptions,
|
|
103
|
+
}).catch((error) => this.handleError(error));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const id = nanoid();
|
|
107
|
+
const completion = this.okra.invoke('fs.writeStream', { id, path, options: resolvedOptions });
|
|
108
|
+
void completion.catch(() => { });
|
|
109
|
+
try {
|
|
110
|
+
for await (const chunk of toAsyncIterable(source)) {
|
|
111
|
+
const written = await this.okra.invoke('fs.writeChunk', { id, contents: chunk });
|
|
112
|
+
if (!written) {
|
|
113
|
+
throw new Error(`File write stream ${id} is not available`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const ended = await this.okra.invoke('fs.endWrite', { id });
|
|
117
|
+
if (!ended) {
|
|
118
|
+
throw new Error(`File write stream ${id} could not be completed`);
|
|
119
|
+
}
|
|
120
|
+
await completion;
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
await this.okra.invoke('fs.abortWrite', { id }).catch(() => false);
|
|
124
|
+
await completion.catch(() => false);
|
|
125
|
+
throw error;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
async read(path) {
|
|
129
|
+
try {
|
|
130
|
+
const content = await this.okra.invoke('fs.readFile', { path });
|
|
131
|
+
return content instanceof Uint8Array ? content : new TextEncoder().encode(content);
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
this.handleError(error);
|
|
135
|
+
}
|
|
89
136
|
}
|
|
90
|
-
async
|
|
91
|
-
|
|
92
|
-
|
|
137
|
+
async readText(path) {
|
|
138
|
+
return new TextDecoder().decode(await this.read(path));
|
|
139
|
+
}
|
|
140
|
+
async readLines(path, range) {
|
|
141
|
+
try {
|
|
142
|
+
const result = await this.okra.invoke('fs.readFile', {
|
|
143
|
+
path,
|
|
144
|
+
lineRange: {
|
|
145
|
+
lineStart: range.start,
|
|
146
|
+
lineEnd: range.end,
|
|
147
|
+
},
|
|
148
|
+
});
|
|
149
|
+
if (typeof result === 'string' || result instanceof Uint8Array) {
|
|
150
|
+
throw new Error(`Unexpected ranged file response for ${path}`);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
start: result.lineStart,
|
|
154
|
+
end: result.lineEnd,
|
|
155
|
+
content: result.content,
|
|
156
|
+
...(result.error === null ? {} : { warning: result.error }),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
this.handleError(error);
|
|
93
161
|
}
|
|
94
|
-
const bytes = contents instanceof Uint8Array ? contents : await collectBytes(contents);
|
|
95
|
-
await this.writeFile(path, bytes, options);
|
|
96
|
-
return true;
|
|
97
162
|
}
|
|
98
|
-
|
|
163
|
+
readStream(path) {
|
|
164
|
+
if (this.okra.runtimeTransport === 'rest') {
|
|
165
|
+
return new ReadableStream({
|
|
166
|
+
start: async (controller) => {
|
|
167
|
+
try {
|
|
168
|
+
controller.enqueue(await this.read(path));
|
|
169
|
+
controller.close();
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
controller.error(error);
|
|
173
|
+
}
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
const id = nanoid();
|
|
178
|
+
let disposable = null;
|
|
179
|
+
let closed = false;
|
|
99
180
|
return new ReadableStream({
|
|
100
|
-
start:
|
|
101
|
-
|
|
102
|
-
controller.enqueue(
|
|
103
|
-
|
|
181
|
+
start: (controller) => {
|
|
182
|
+
disposable = this.okra.listen(`fs.read.${id}`, (event) => {
|
|
183
|
+
controller.enqueue(event.data);
|
|
184
|
+
});
|
|
185
|
+
void this.okra.invoke('fs.readStream', { id, path }).then(() => {
|
|
186
|
+
if (!closed) {
|
|
187
|
+
closed = true;
|
|
188
|
+
disposable?.dispose();
|
|
189
|
+
controller.close();
|
|
190
|
+
}
|
|
191
|
+
}).catch((error) => {
|
|
192
|
+
if (!closed) {
|
|
193
|
+
closed = true;
|
|
194
|
+
disposable?.dispose();
|
|
195
|
+
controller.error(error);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
cancel: async () => {
|
|
200
|
+
if (!closed) {
|
|
201
|
+
closed = true;
|
|
202
|
+
disposable?.dispose();
|
|
203
|
+
await this.okra.invoke('fs.cancelRead', { id }).catch(() => false);
|
|
104
204
|
}
|
|
105
|
-
|
|
106
|
-
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
follow(path, options = {}) {
|
|
209
|
+
if (this.okra.runtimeTransport === 'rest') {
|
|
210
|
+
return new ReadableStream({
|
|
211
|
+
start(controller) {
|
|
212
|
+
controller.error(new UnsupportedRuntimeTransportError('Following files requires the realtime transport.'));
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
const id = nanoid();
|
|
217
|
+
let disposable = null;
|
|
218
|
+
let abort = null;
|
|
219
|
+
let started = false;
|
|
220
|
+
let closed = false;
|
|
221
|
+
const stop = async () => {
|
|
222
|
+
if (closed) {
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
closed = true;
|
|
226
|
+
disposable?.dispose();
|
|
227
|
+
disposable = null;
|
|
228
|
+
if (abort && options.signal) {
|
|
229
|
+
options.signal.removeEventListener('abort', abort);
|
|
230
|
+
}
|
|
231
|
+
abort = null;
|
|
232
|
+
if (started) {
|
|
233
|
+
await this.okra.invoke('fs.cancelFollow', { id }).catch(() => false);
|
|
234
|
+
}
|
|
235
|
+
};
|
|
236
|
+
return new ReadableStream({
|
|
237
|
+
start: (controller) => {
|
|
238
|
+
disposable = this.okra.listen(`fs.follow.${id}`, (event) => {
|
|
239
|
+
if (!closed) {
|
|
240
|
+
controller.enqueue(event.data);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
if (options.signal) {
|
|
244
|
+
abort = () => {
|
|
245
|
+
void stop().finally(() => controller.error(options.signal?.reason ?? new Error('Aborted')));
|
|
246
|
+
};
|
|
247
|
+
if (options.signal.aborted) {
|
|
248
|
+
abort();
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
options.signal.addEventListener('abort', abort, { once: true });
|
|
107
252
|
}
|
|
253
|
+
started = true;
|
|
254
|
+
void this.okra.invoke('fs.follow', { id, path, lines: options.lines ?? 10 }).then(() => {
|
|
255
|
+
if (!closed) {
|
|
256
|
+
closed = true;
|
|
257
|
+
disposable?.dispose();
|
|
258
|
+
if (abort && options.signal) {
|
|
259
|
+
options.signal.removeEventListener('abort', abort);
|
|
260
|
+
}
|
|
261
|
+
abort = null;
|
|
262
|
+
controller.close();
|
|
263
|
+
}
|
|
264
|
+
}).catch((error) => {
|
|
265
|
+
if (!closed) {
|
|
266
|
+
closed = true;
|
|
267
|
+
disposable?.dispose();
|
|
268
|
+
if (abort && options.signal) {
|
|
269
|
+
options.signal.removeEventListener('abort', abort);
|
|
270
|
+
}
|
|
271
|
+
abort = null;
|
|
272
|
+
controller.error(error);
|
|
273
|
+
}
|
|
274
|
+
});
|
|
108
275
|
},
|
|
276
|
+
cancel: stop,
|
|
109
277
|
});
|
|
110
278
|
}
|
|
111
279
|
find(query, options = {
|
|
@@ -187,56 +355,38 @@ export class Filesystem {
|
|
|
187
355
|
.invoke('fs.textSearch', { query, options: { ...defaultOptions, ...options } })
|
|
188
356
|
.finally(() => disposable.dispose());
|
|
189
357
|
}
|
|
190
|
-
|
|
191
|
-
return this.okra.invoke('fs.
|
|
192
|
-
}
|
|
193
|
-
move(from, to) {
|
|
194
|
-
return this.okra.invoke('fs.move', { from, to });
|
|
195
|
-
}
|
|
196
|
-
remove(path, type) {
|
|
197
|
-
return this.okra.invoke('fs.remove', { path, type });
|
|
198
|
-
}
|
|
199
|
-
async readFile(path, lineRange) {
|
|
200
|
-
try {
|
|
201
|
-
const content = await this.okra.invoke('fs.readFile', { path, lineRange });
|
|
202
|
-
if (content instanceof Uint8Array) {
|
|
203
|
-
return content;
|
|
204
|
-
}
|
|
205
|
-
if (typeof content === 'string') {
|
|
206
|
-
return new TextEncoder().encode(content);
|
|
207
|
-
}
|
|
208
|
-
return content;
|
|
209
|
-
}
|
|
210
|
-
catch (e) {
|
|
211
|
-
this.handleError(e);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
tail(path, lines = 10) {
|
|
215
|
-
return this.okra.invoke('fs.tail', { path, lines }).catch((e) => this.handleError(e));
|
|
216
|
-
}
|
|
217
|
-
writeFile(path, contents, options) {
|
|
218
|
-
return this.okra
|
|
219
|
-
.invoke('fs.writeFile', {
|
|
220
|
-
path,
|
|
221
|
-
contents,
|
|
222
|
-
options,
|
|
223
|
-
})
|
|
224
|
-
.catch((e) => this.handleError(e));
|
|
358
|
+
tail(path, options = {}) {
|
|
359
|
+
return this.okra.invoke('fs.tail', { path, lines: options.lines ?? 10 }).catch((e) => this.handleError(e));
|
|
225
360
|
}
|
|
226
361
|
stat(path) {
|
|
227
362
|
return this.okra.invoke('fs.stat', { path }).catch((e) => this.handleError(e));
|
|
228
363
|
}
|
|
229
|
-
|
|
230
|
-
return this.okra.invoke('fs.rename', {
|
|
364
|
+
move(from, to, options = {}) {
|
|
365
|
+
return this.okra.invoke('fs.rename', {
|
|
366
|
+
from,
|
|
367
|
+
to,
|
|
368
|
+
options: { overwrite: false, ...options },
|
|
369
|
+
}).catch((e) => this.handleError(e));
|
|
231
370
|
}
|
|
232
|
-
|
|
233
|
-
return this.okra.invoke('fs.delete', {
|
|
371
|
+
remove(path, options = {}) {
|
|
372
|
+
return this.okra.invoke('fs.delete', {
|
|
373
|
+
path,
|
|
374
|
+
options: { recursive: false, useTrash: false, atomic: false, ...options },
|
|
375
|
+
}).catch((e) => this.handleError(e));
|
|
234
376
|
}
|
|
235
|
-
copy(source, destination, options) {
|
|
236
|
-
return this.okra.invoke('fs.copy', {
|
|
377
|
+
copy(source, destination, options = {}) {
|
|
378
|
+
return this.okra.invoke('fs.copy', {
|
|
379
|
+
source,
|
|
380
|
+
destination,
|
|
381
|
+
options: { overwrite: false, ...options },
|
|
382
|
+
}).catch((e) => this.handleError(e));
|
|
237
383
|
}
|
|
238
|
-
|
|
239
|
-
return this.okra.invoke('fs.readDirectory', {
|
|
384
|
+
list(path, options = {}) {
|
|
385
|
+
return this.okra.invoke('fs.readDirectory', {
|
|
386
|
+
path,
|
|
387
|
+
include: options.include ?? [],
|
|
388
|
+
exclude: options.exclude ?? [],
|
|
389
|
+
}).catch((e) => this.handleError(e));
|
|
240
390
|
}
|
|
241
391
|
createDirectory(path) {
|
|
242
392
|
return this.okra.invoke('fs.createDirectory', { path }).catch((e) => this.handleError(e));
|
|
@@ -269,17 +419,17 @@ export class Filesystem {
|
|
|
269
419
|
.then(() => true)
|
|
270
420
|
.catch(() => false);
|
|
271
421
|
}
|
|
272
|
-
async download(
|
|
422
|
+
async download(options = {}) {
|
|
273
423
|
const id = nanoid();
|
|
274
|
-
const stream = !!
|
|
424
|
+
const stream = !!options.onChunk;
|
|
275
425
|
const chunks = [];
|
|
276
426
|
const disposable = this.okra.listen(`fs.download.${id}`, stream
|
|
277
|
-
?
|
|
427
|
+
? options.onChunk
|
|
278
428
|
: (data) => {
|
|
279
429
|
chunks.push(data);
|
|
280
430
|
});
|
|
281
431
|
return this.okra
|
|
282
|
-
.invoke('fs.download', { id, exclude, include })
|
|
432
|
+
.invoke('fs.download', { id, exclude: options.exclude, include: options.include })
|
|
283
433
|
.then(() => new Blob(chunks, { type: 'application/octet-stream' }))
|
|
284
434
|
.finally(() => disposable.dispose());
|
|
285
435
|
}
|
|
@@ -293,6 +443,13 @@ export class Filesystem {
|
|
|
293
443
|
throw e;
|
|
294
444
|
}
|
|
295
445
|
}
|
|
446
|
+
async function* toAsyncIterable(contents) {
|
|
447
|
+
if (contents instanceof Uint8Array) {
|
|
448
|
+
yield contents;
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
yield* contents;
|
|
452
|
+
}
|
|
296
453
|
async function collectBytes(stream) {
|
|
297
454
|
const chunks = [];
|
|
298
455
|
let length = 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { Filesystem, FilesystemActions, FilesystemEvents } from './filesystem.js';
|
|
2
|
-
import
|
|
3
|
-
import Container, { ContainerActions, ContainerEvents, PortInfo
|
|
2
|
+
import Terminals, { TerminalEvents, TerminalActions, type SpawnOptions } from './terminal.js';
|
|
3
|
+
import Container, { ContainerActions, ContainerEvents, PortInfo } from './container.js';
|
|
4
4
|
import Auth, { AuthActions } from './auth.js';
|
|
5
5
|
import Lsp, { LspActions, LspEvents } from './lsp.js';
|
|
6
6
|
import Composer, { ComposerActions, ComposerEvents } from './composer.js';
|
|
7
|
-
import
|
|
7
|
+
import Logs, { LogActions, LogEvents } from './log.js';
|
|
8
|
+
import Metrics from './metrics.js';
|
|
9
|
+
import Ports from './ports.js';
|
|
8
10
|
import Repl, { ReplActions, ReplEvents } from './repl.js';
|
|
9
|
-
import
|
|
11
|
+
import { ShellEvents, ShellActions, type ProcessResult, type ShellProcess } from './shell.js';
|
|
10
12
|
import Services, { ServiceActions, ServiceEvents } from './services.js';
|
|
11
13
|
import Config, { ConfigActions } from './config.js';
|
|
12
14
|
import { EventDispatcher } from './events/index.js';
|
|
@@ -19,6 +21,10 @@ export * from './types.js';
|
|
|
19
21
|
export * from './lsp.js';
|
|
20
22
|
export * from './filesystem.js';
|
|
21
23
|
export * from './container.js';
|
|
24
|
+
export * from './log.js';
|
|
25
|
+
export * from './metrics.js';
|
|
26
|
+
export * from './ports.js';
|
|
27
|
+
export * from './runtime-telemetry.js';
|
|
22
28
|
export * from './shell.js';
|
|
23
29
|
export * from './terminal.js';
|
|
24
30
|
export * from './git.js';
|
|
@@ -299,7 +305,6 @@ export declare class NotebookApi {
|
|
|
299
305
|
export interface PHPSandboxClientOptions {
|
|
300
306
|
debug?: boolean;
|
|
301
307
|
startClosed?: boolean;
|
|
302
|
-
telemetry?: Set<TelemetryFeature>;
|
|
303
308
|
fetch?: typeof globalThis.fetch;
|
|
304
309
|
webSocket?: unknown;
|
|
305
310
|
runtimeUrlProvider?: (notebookId: string) => Promise<string>;
|
|
@@ -384,15 +389,17 @@ export declare class NotebookInstance {
|
|
|
384
389
|
#private;
|
|
385
390
|
readonly data: NotebookData;
|
|
386
391
|
protected client: Client;
|
|
387
|
-
readonly
|
|
388
|
-
readonly
|
|
392
|
+
readonly files: Filesystem;
|
|
393
|
+
readonly terminals: Terminals;
|
|
389
394
|
readonly auth: Auth;
|
|
390
395
|
readonly lsp: Lsp;
|
|
391
396
|
readonly composer: Composer;
|
|
392
|
-
readonly
|
|
397
|
+
readonly logs: Logs;
|
|
398
|
+
readonly metrics: Metrics;
|
|
399
|
+
readonly ports: Ports;
|
|
393
400
|
readonly repl: Repl;
|
|
394
401
|
readonly container: Container;
|
|
395
|
-
readonly shell
|
|
402
|
+
private readonly shell;
|
|
396
403
|
readonly git: Git;
|
|
397
404
|
readonly services: Services;
|
|
398
405
|
readonly config: Config;
|
|
@@ -412,6 +419,8 @@ export declare class NotebookInstance {
|
|
|
412
419
|
publish(input: PublishInput): Promise<PublicationRun>;
|
|
413
420
|
run(command: string | string[], opts?: SpawnOptions): ShellProcess;
|
|
414
421
|
run(command: string, args: string[], opts?: SpawnOptions): ShellProcess;
|
|
422
|
+
exec(command: string | string[], opts?: SpawnOptions): Promise<ProcessResult>;
|
|
423
|
+
exec(command: string, args: string[], opts?: SpawnOptions): Promise<ProcessResult>;
|
|
415
424
|
gitCredentials(): Promise<GitCredentials>;
|
|
416
425
|
destroy(): Promise<void>;
|
|
417
426
|
stop(): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -11,12 +11,15 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
|
|
|
11
11
|
};
|
|
12
12
|
var _NotebookInstance_instances, _NotebookInstance_initPromise, _NotebookInstance_init;
|
|
13
13
|
import { Filesystem } from './filesystem.js';
|
|
14
|
-
import
|
|
14
|
+
import Terminals from './terminal.js';
|
|
15
15
|
import Container from './container.js';
|
|
16
16
|
import Auth from './auth.js';
|
|
17
17
|
import Lsp from './lsp.js';
|
|
18
18
|
import Composer from './composer.js';
|
|
19
|
-
import
|
|
19
|
+
import Logs from './log.js';
|
|
20
|
+
import Metrics from './metrics.js';
|
|
21
|
+
import Ports from './ports.js';
|
|
22
|
+
import { RuntimeTelemetry } from './runtime-telemetry.js';
|
|
20
23
|
import Repl from './repl.js';
|
|
21
24
|
import Shell from './shell.js';
|
|
22
25
|
import Services from './services.js';
|
|
@@ -32,6 +35,10 @@ export * from './types.js';
|
|
|
32
35
|
export * from './lsp.js';
|
|
33
36
|
export * from './filesystem.js';
|
|
34
37
|
export * from './container.js';
|
|
38
|
+
export * from './log.js';
|
|
39
|
+
export * from './metrics.js';
|
|
40
|
+
export * from './ports.js';
|
|
41
|
+
export * from './runtime-telemetry.js';
|
|
35
42
|
export * from './shell.js';
|
|
36
43
|
export * from './terminal.js';
|
|
37
44
|
export * from './git.js';
|
|
@@ -367,12 +374,15 @@ export class NotebookInstance {
|
|
|
367
374
|
this.watchConnection();
|
|
368
375
|
__classPrivateFieldSet(this, _NotebookInstance_initPromise, __classPrivateFieldGet(this, _NotebookInstance_instances, "m", _NotebookInstance_init).call(this), "f");
|
|
369
376
|
}
|
|
370
|
-
this.
|
|
371
|
-
this.
|
|
377
|
+
this.files = new Filesystem(this, this.socket === null ? null : (handler) => this.onDidConnect(handler));
|
|
378
|
+
this.terminals = new Terminals(this);
|
|
372
379
|
this.auth = new Auth(this);
|
|
373
380
|
this.lsp = new Lsp(this);
|
|
374
381
|
this.composer = new Composer(this, client);
|
|
375
|
-
|
|
382
|
+
const telemetry = new RuntimeTelemetry(this);
|
|
383
|
+
this.logs = new Logs(this, telemetry);
|
|
384
|
+
this.metrics = new Metrics(this, telemetry);
|
|
385
|
+
this.ports = new Ports(this, telemetry);
|
|
376
386
|
this.repl = new Repl(this);
|
|
377
387
|
this.container = new Container(this);
|
|
378
388
|
this.shell = new Shell(this);
|
|
@@ -421,6 +431,12 @@ export class NotebookInstance {
|
|
|
421
431
|
}
|
|
422
432
|
return this.shell.run(command, argsOrOpts);
|
|
423
433
|
}
|
|
434
|
+
exec(command, argsOrOpts = [], opts) {
|
|
435
|
+
if (Array.isArray(argsOrOpts)) {
|
|
436
|
+
return this.run(command, argsOrOpts, opts).wait();
|
|
437
|
+
}
|
|
438
|
+
return this.run(command, argsOrOpts).wait();
|
|
439
|
+
}
|
|
424
440
|
gitCredentials() {
|
|
425
441
|
return this.client.notebook.gitCredentials(this.data.id);
|
|
426
442
|
}
|
|
@@ -512,9 +528,6 @@ export class NotebookInstance {
|
|
|
512
528
|
const socket = this.realtimeSocket();
|
|
513
529
|
socket.onDidConnect(() => {
|
|
514
530
|
socket.emit('okra.connected');
|
|
515
|
-
if (this.client.options.telemetry) {
|
|
516
|
-
this.container.enableTelemetry(this.client.options.telemetry);
|
|
517
|
-
}
|
|
518
531
|
});
|
|
519
532
|
socket.onDidClose(() => {
|
|
520
533
|
socket.emit('okra.disconnected');
|
package/dist/log.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { NotebookInstance } from './index.js';
|
|
2
|
+
import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.js';
|
|
2
3
|
export interface LogEvents {
|
|
3
4
|
'notebook.log': string;
|
|
4
5
|
}
|
|
5
6
|
export interface LogActions {
|
|
6
7
|
}
|
|
7
|
-
export
|
|
8
|
+
export interface RuntimeLogEntry {
|
|
9
|
+
timestamp: string;
|
|
10
|
+
source: 'runtime';
|
|
11
|
+
message: string;
|
|
12
|
+
}
|
|
13
|
+
export default class Logs {
|
|
8
14
|
protected okra: NotebookInstance;
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
15
|
+
private readonly telemetry;
|
|
16
|
+
constructor(okra: NotebookInstance, telemetry: RuntimeTelemetry);
|
|
17
|
+
follow(options?: RuntimeWatchOptions): ReadableStream<RuntimeLogEntry>;
|
|
12
18
|
}
|
package/dist/log.js
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { watchRuntimeTelemetry } from './runtime-telemetry.js';
|
|
2
|
+
export default class Logs {
|
|
3
|
+
constructor(okra, telemetry) {
|
|
3
4
|
this.okra = okra;
|
|
5
|
+
this.telemetry = telemetry;
|
|
4
6
|
}
|
|
5
|
-
|
|
6
|
-
return this.okra.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
follow(options = {}) {
|
|
8
|
+
return watchRuntimeTelemetry(this.okra, this.telemetry, 'logs', 'notebook.log', (message) => ({
|
|
9
|
+
timestamp: new Date().toISOString(),
|
|
10
|
+
source: 'runtime',
|
|
11
|
+
message,
|
|
12
|
+
}), options);
|
|
10
13
|
}
|
|
11
14
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NotebookInstance } from './index.js';
|
|
2
|
+
import type { ContainerStats } from './container.js';
|
|
3
|
+
import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.js';
|
|
4
|
+
export default class Metrics {
|
|
5
|
+
private readonly okra;
|
|
6
|
+
private readonly telemetry;
|
|
7
|
+
constructor(okra: NotebookInstance, telemetry: RuntimeTelemetry);
|
|
8
|
+
current(): Promise<ContainerStats | null>;
|
|
9
|
+
watch(options?: RuntimeWatchOptions): ReadableStream<ContainerStats>;
|
|
10
|
+
}
|
package/dist/metrics.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { watchRuntimeTelemetry } from './runtime-telemetry.js';
|
|
2
|
+
export default class Metrics {
|
|
3
|
+
constructor(okra, telemetry) {
|
|
4
|
+
this.okra = okra;
|
|
5
|
+
this.telemetry = telemetry;
|
|
6
|
+
}
|
|
7
|
+
current() {
|
|
8
|
+
return this.okra.invoke('container.stats');
|
|
9
|
+
}
|
|
10
|
+
watch(options = {}) {
|
|
11
|
+
return watchRuntimeTelemetry(this.okra, this.telemetry, 'stats', 'container.stats', (stats) => stats, options);
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/ports.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { NotebookInstance } from './index.js';
|
|
2
|
+
import type { PortInfo } from './container.js';
|
|
3
|
+
import { RuntimeTelemetry, type RuntimeWatchOptions } from './runtime-telemetry.js';
|
|
4
|
+
export default class Ports {
|
|
5
|
+
private readonly okra;
|
|
6
|
+
private readonly telemetry;
|
|
7
|
+
constructor(okra: NotebookInstance, telemetry: RuntimeTelemetry);
|
|
8
|
+
list(): Promise<PortInfo[]>;
|
|
9
|
+
watch(options?: RuntimeWatchOptions): ReadableStream<PortInfo[]>;
|
|
10
|
+
}
|
package/dist/ports.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { watchRuntimeTelemetry } from './runtime-telemetry.js';
|
|
2
|
+
export default class Ports {
|
|
3
|
+
constructor(okra, telemetry) {
|
|
4
|
+
this.okra = okra;
|
|
5
|
+
this.telemetry = telemetry;
|
|
6
|
+
}
|
|
7
|
+
list() {
|
|
8
|
+
return this.okra.invoke('container.opened-ports');
|
|
9
|
+
}
|
|
10
|
+
watch(options = {}) {
|
|
11
|
+
return watchRuntimeTelemetry(this.okra, this.telemetry, 'ports', 'container.ports', (ports) => ports, options);
|
|
12
|
+
}
|
|
13
|
+
}
|
package/dist/runtime/rest.js
CHANGED
|
@@ -12,6 +12,7 @@ const restActionRoutes = {
|
|
|
12
12
|
'container.stop': { method: 'POST', path: '/runtime/container/stop' },
|
|
13
13
|
'container.set-php': { method: 'PUT', path: '/runtime/container/php' },
|
|
14
14
|
'container.opened-ports': { method: 'GET', path: '/runtime/container/ports' },
|
|
15
|
+
'container.stats': { method: 'GET', path: '/runtime/container/stats' },
|
|
15
16
|
'terminal.list': { method: 'GET', path: '/runtime/terminals' },
|
|
16
17
|
'shell.exec': { method: 'POST', path: '/runtime/executions' },
|
|
17
18
|
'fs.info': { method: 'POST', path: '/runtime/files/info' },
|
|
@@ -37,10 +38,6 @@ const restActionRoutes = {
|
|
|
37
38
|
'service.logs': { method: 'POST', path: '/runtime/services/logs' },
|
|
38
39
|
'composer.invoke': { method: 'POST', path: '/runtime/composer/executions' },
|
|
39
40
|
'composer.packages': { method: 'GET', path: '/runtime/composer/packages' },
|
|
40
|
-
'composer.dump-autoload': {
|
|
41
|
-
method: 'POST',
|
|
42
|
-
path: '/runtime/composer/autoload',
|
|
43
|
-
},
|
|
44
41
|
'git.checkpoint': { method: 'POST', path: '/runtime/git/checkpoints' },
|
|
45
42
|
'git.checkout': { method: 'POST', path: '/runtime/git/checkouts' },
|
|
46
43
|
'git.log': { method: 'POST', path: '/runtime/git/log' },
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NotebookInstance } from './index.js';
|
|
2
|
+
export type RuntimeTelemetryFeature = 'stats' | 'logs' | 'ports';
|
|
3
|
+
export interface RuntimeWatchOptions {
|
|
4
|
+
signal?: AbortSignal;
|
|
5
|
+
}
|
|
6
|
+
export declare class RuntimeTelemetry {
|
|
7
|
+
private readonly okra;
|
|
8
|
+
private readonly references;
|
|
9
|
+
private synchronization;
|
|
10
|
+
constructor(okra: NotebookInstance);
|
|
11
|
+
acquire(feature: RuntimeTelemetryFeature): Promise<() => Promise<void>>;
|
|
12
|
+
private removeReference;
|
|
13
|
+
private synchronize;
|
|
14
|
+
}
|
|
15
|
+
export declare function watchRuntimeTelemetry<T>(okra: NotebookInstance, telemetry: RuntimeTelemetry, feature: RuntimeTelemetryFeature, event: 'container.stats' | 'container.ports' | 'notebook.log', transform: (data: any) => T, options?: RuntimeWatchOptions): ReadableStream<T>;
|