@meshagent/meshagent 0.29.4 → 0.30.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/browser/agent-client.js +3 -28
  3. package/dist/browser/agent.js +6 -6
  4. package/dist/browser/containers-client.d.ts +125 -0
  5. package/dist/browser/containers-client.js +458 -0
  6. package/dist/browser/database-client.d.ts +42 -6
  7. package/dist/browser/database-client.js +610 -77
  8. package/dist/browser/developer-client.d.ts +2 -2
  9. package/dist/browser/developer-client.js +60 -15
  10. package/dist/browser/helpers.js +4 -3
  11. package/dist/browser/index.d.ts +1 -0
  12. package/dist/browser/index.js +1 -0
  13. package/dist/browser/lk-client.js +12 -3
  14. package/dist/browser/meshagent-client.d.ts +5 -0
  15. package/dist/browser/messaging-client.d.ts +1 -0
  16. package/dist/browser/messaging-client.js +52 -8
  17. package/dist/browser/queues-client.d.ts +2 -0
  18. package/dist/browser/queues-client.js +34 -7
  19. package/dist/browser/response.d.ts +28 -0
  20. package/dist/browser/response.js +76 -1
  21. package/dist/browser/room-client.d.ts +43 -1
  22. package/dist/browser/room-client.js +204 -0
  23. package/dist/browser/secrets-client.d.ts +1 -0
  24. package/dist/browser/secrets-client.js +32 -27
  25. package/dist/browser/storage-client.d.ts +22 -7
  26. package/dist/browser/storage-client.js +353 -15
  27. package/dist/browser/sync-client.d.ts +12 -13
  28. package/dist/browser/sync-client.js +263 -65
  29. package/dist/esm/agent-client.js +3 -28
  30. package/dist/esm/agent.js +6 -6
  31. package/dist/esm/containers-client.d.ts +125 -0
  32. package/dist/esm/containers-client.js +453 -0
  33. package/dist/esm/database-client.d.ts +42 -6
  34. package/dist/esm/database-client.js +611 -78
  35. package/dist/esm/developer-client.d.ts +2 -2
  36. package/dist/esm/developer-client.js +61 -16
  37. package/dist/esm/helpers.js +4 -3
  38. package/dist/esm/index.d.ts +1 -0
  39. package/dist/esm/index.js +1 -0
  40. package/dist/esm/lk-client.js +12 -3
  41. package/dist/esm/meshagent-client.d.ts +5 -0
  42. package/dist/esm/messaging-client.d.ts +1 -0
  43. package/dist/esm/messaging-client.js +52 -8
  44. package/dist/esm/queues-client.d.ts +2 -0
  45. package/dist/esm/queues-client.js +35 -8
  46. package/dist/esm/response.d.ts +28 -0
  47. package/dist/esm/response.js +73 -0
  48. package/dist/esm/room-client.d.ts +43 -1
  49. package/dist/esm/room-client.js +207 -3
  50. package/dist/esm/secrets-client.d.ts +1 -0
  51. package/dist/esm/secrets-client.js +33 -28
  52. package/dist/esm/storage-client.d.ts +22 -7
  53. package/dist/esm/storage-client.js +353 -15
  54. package/dist/esm/sync-client.d.ts +12 -13
  55. package/dist/esm/sync-client.js +263 -64
  56. package/dist/node/agent-client.js +3 -28
  57. package/dist/node/agent.js +6 -6
  58. package/dist/node/containers-client.d.ts +125 -0
  59. package/dist/node/containers-client.js +458 -0
  60. package/dist/node/database-client.d.ts +42 -6
  61. package/dist/node/database-client.js +610 -77
  62. package/dist/node/developer-client.d.ts +2 -2
  63. package/dist/node/developer-client.js +60 -15
  64. package/dist/node/helpers.js +4 -3
  65. package/dist/node/index.d.ts +1 -0
  66. package/dist/node/index.js +1 -0
  67. package/dist/node/lk-client.js +12 -3
  68. package/dist/node/meshagent-client.d.ts +5 -0
  69. package/dist/node/messaging-client.d.ts +1 -0
  70. package/dist/node/messaging-client.js +52 -8
  71. package/dist/node/queues-client.d.ts +2 -0
  72. package/dist/node/queues-client.js +34 -7
  73. package/dist/node/response.d.ts +28 -0
  74. package/dist/node/response.js +76 -1
  75. package/dist/node/room-client.d.ts +43 -1
  76. package/dist/node/room-client.js +204 -0
  77. package/dist/node/secrets-client.d.ts +1 -0
  78. package/dist/node/secrets-client.js +32 -27
  79. package/dist/node/storage-client.d.ts +22 -7
  80. package/dist/node/storage-client.js +353 -15
  81. package/dist/node/sync-client.d.ts +12 -13
  82. package/dist/node/sync-client.js +263 -65
  83. package/package.json +1 -1
@@ -0,0 +1,453 @@
1
+ import { v4 as uuidv4 } from "uuid";
2
+ import { Completer } from "./completer";
3
+ import { BinaryContent, ControlContent, ErrorContent, JsonContent } from "./response";
4
+ import { RoomServerException } from "./room-server-client";
5
+ import { StreamController } from "./stream-controller";
6
+ function isRecord(value) {
7
+ return typeof value === "object" && value !== null && !Array.isArray(value);
8
+ }
9
+ function toStringMapList(values) {
10
+ return Object.entries(values).map(([key, value]) => ({ key, value }));
11
+ }
12
+ function toPortPairs(values) {
13
+ return Object.entries(values).map(([containerPort, hostPort]) => ({
14
+ container_port: Number(containerPort),
15
+ host_port: hostPort,
16
+ }));
17
+ }
18
+ function toCredentials(values) {
19
+ return values.map((entry) => ({
20
+ registry: entry.registry,
21
+ username: entry.username,
22
+ password: entry.password,
23
+ }));
24
+ }
25
+ function readStringField(data, field, operation) {
26
+ const value = data[field];
27
+ if (typeof value !== "string") {
28
+ throw new RoomServerException(`unexpected return type from containers.${operation}`);
29
+ }
30
+ return value;
31
+ }
32
+ function decodeJsonStatus(data) {
33
+ const text = new TextDecoder().decode(data);
34
+ let parsed;
35
+ try {
36
+ parsed = JSON.parse(text);
37
+ }
38
+ catch {
39
+ throw new RoomServerException("containers.exec returned an invalid status payload");
40
+ }
41
+ if (!isRecord(parsed)) {
42
+ throw new RoomServerException("containers.exec returned an invalid status payload");
43
+ }
44
+ const status = parsed["status"];
45
+ if (typeof status !== "number" || !Number.isInteger(status)) {
46
+ throw new RoomServerException("containers.exec returned an invalid status payload");
47
+ }
48
+ return status;
49
+ }
50
+ export class ExecSession {
51
+ constructor(params) {
52
+ this.previousOutput = [];
53
+ this.resultCompleter = new Completer();
54
+ this.outputController = new StreamController();
55
+ this.queuedInput = [];
56
+ this.inputClosed = false;
57
+ this.closed = false;
58
+ this.inputWaiter = null;
59
+ this.requestId = params.requestId;
60
+ this.command = params.command;
61
+ this.containerId = params.containerId;
62
+ this.tty = params.tty;
63
+ this.result = this.resultCompleter.fut;
64
+ this.output = this.outputController.stream;
65
+ }
66
+ async *inputStream() {
67
+ yield new BinaryContent({
68
+ data: new Uint8Array(0),
69
+ headers: {
70
+ kind: "start",
71
+ request_id: this.requestId,
72
+ container_id: this.containerId,
73
+ command: this.command,
74
+ tty: this.tty,
75
+ },
76
+ });
77
+ while (true) {
78
+ while (this.queuedInput.length > 0) {
79
+ const chunk = this.queuedInput.shift();
80
+ if (chunk) {
81
+ yield chunk;
82
+ }
83
+ }
84
+ if (this.inputClosed) {
85
+ return;
86
+ }
87
+ this.inputWaiter = new Completer();
88
+ await this.inputWaiter.fut;
89
+ this.inputWaiter = null;
90
+ }
91
+ }
92
+ async write(data) {
93
+ this.queueInput({ channel: 1, data });
94
+ }
95
+ async resize(params) {
96
+ if (this.lastResizeWidth === params.width && this.lastResizeHeight === params.height) {
97
+ return;
98
+ }
99
+ this.lastResizeWidth = params.width;
100
+ this.lastResizeHeight = params.height;
101
+ this.queueInput({ channel: 4, width: params.width, height: params.height });
102
+ }
103
+ async stop() {
104
+ this.closeInputStream();
105
+ }
106
+ async kill() {
107
+ if (this.inputClosed) {
108
+ return;
109
+ }
110
+ this.queueInput({ channel: 5 });
111
+ }
112
+ close(status) {
113
+ if (!this.resultCompleter.completed) {
114
+ this.resultCompleter.complete(status);
115
+ }
116
+ this.closed = true;
117
+ this.closeInputStream();
118
+ this.outputController.close();
119
+ }
120
+ closeError(error) {
121
+ if (!this.resultCompleter.completed) {
122
+ this.resultCompleter.completeError(error);
123
+ }
124
+ this.closed = true;
125
+ this.closeInputStream();
126
+ this.outputController.close();
127
+ }
128
+ addOutput(data) {
129
+ this.previousOutput.push(data);
130
+ this.outputController.add(data);
131
+ }
132
+ get isClosed() {
133
+ return this.closed;
134
+ }
135
+ queueInput(params) {
136
+ if (this.inputClosed) {
137
+ throw new RoomServerException("container exec session is already closed");
138
+ }
139
+ this.queuedInput.push(new BinaryContent({
140
+ data: params.data ?? new Uint8Array(0),
141
+ headers: {
142
+ kind: "input",
143
+ channel: params.channel,
144
+ width: params.width,
145
+ height: params.height,
146
+ },
147
+ }));
148
+ this.inputWaiter?.complete();
149
+ }
150
+ closeInputStream() {
151
+ if (this.inputClosed) {
152
+ return;
153
+ }
154
+ this.inputClosed = true;
155
+ this.inputWaiter?.complete();
156
+ }
157
+ }
158
+ export class ContainersClient {
159
+ constructor({ room }) {
160
+ this.room = room;
161
+ }
162
+ unexpectedResponseError(operation) {
163
+ return new RoomServerException(`unexpected return type from containers.${operation}`);
164
+ }
165
+ async invoke(operation, input) {
166
+ return await this.room.invoke({
167
+ toolkit: "containers",
168
+ tool: operation,
169
+ input,
170
+ });
171
+ }
172
+ async listImages() {
173
+ const output = await this.invoke("list_images", {});
174
+ if (!(output instanceof JsonContent)) {
175
+ throw this.unexpectedResponseError("list_images");
176
+ }
177
+ const imagesRaw = output.json["images"];
178
+ if (!Array.isArray(imagesRaw)) {
179
+ throw this.unexpectedResponseError("list_images");
180
+ }
181
+ const images = [];
182
+ for (const entry of imagesRaw) {
183
+ if (!isRecord(entry)) {
184
+ throw this.unexpectedResponseError("list_images");
185
+ }
186
+ const id = entry["id"];
187
+ const tags = entry["tags"];
188
+ const labelsRaw = entry["labels"];
189
+ if (typeof id !== "string" || !Array.isArray(tags)) {
190
+ throw this.unexpectedResponseError("list_images");
191
+ }
192
+ const normalizedTags = tags.filter((tag) => typeof tag === "string");
193
+ const size = entry["size"];
194
+ images.push({
195
+ id,
196
+ tags: normalizedTags,
197
+ size: typeof size === "number" ? size : undefined,
198
+ labels: isRecord(labelsRaw) ? labelsRaw : {},
199
+ });
200
+ }
201
+ return images;
202
+ }
203
+ async pullImage(params) {
204
+ await this.invoke("pull_image", {
205
+ tag: params.tag,
206
+ credentials: toCredentials(params.credentials ?? []),
207
+ });
208
+ }
209
+ async run(params) {
210
+ const output = await this.invoke("run", {
211
+ image: params.image,
212
+ command: params.command,
213
+ working_dir: params.workingDir,
214
+ env: toStringMapList(params.env ?? {}),
215
+ mount_path: params.mountPath,
216
+ mount_subpath: params.mountSubpath,
217
+ role: params.role,
218
+ participant_name: params.participantName,
219
+ ports: toPortPairs(params.ports ?? {}),
220
+ credentials: toCredentials(params.credentials ?? []),
221
+ name: params.name,
222
+ mounts: params.mounts,
223
+ writable_root_fs: params.writableRootFs,
224
+ private: params.private,
225
+ });
226
+ if (!(output instanceof JsonContent) || !isRecord(output.json)) {
227
+ throw this.unexpectedResponseError("run");
228
+ }
229
+ return readStringField(output.json, "container_id", "run");
230
+ }
231
+ async runService(params) {
232
+ const output = await this.invoke("run_service", {
233
+ service_id: params.serviceId,
234
+ env: toStringMapList(params.env ?? {}),
235
+ });
236
+ if (!(output instanceof JsonContent) || !isRecord(output.json)) {
237
+ throw this.unexpectedResponseError("run_service");
238
+ }
239
+ return readStringField(output.json, "container_id", "run_service");
240
+ }
241
+ exec(params) {
242
+ const requestId = uuidv4();
243
+ const session = new ExecSession({
244
+ requestId,
245
+ command: params.command,
246
+ containerId: params.containerId,
247
+ tty: params.tty,
248
+ });
249
+ this.room
250
+ .invokeStream({
251
+ toolkit: "containers",
252
+ tool: "exec",
253
+ input: session.inputStream(),
254
+ })
255
+ .then(async (stream) => {
256
+ for await (const chunk of stream) {
257
+ if (chunk instanceof ErrorContent) {
258
+ throw new RoomServerException(chunk.text, chunk.code);
259
+ }
260
+ if (!(chunk instanceof BinaryContent)) {
261
+ throw this.unexpectedResponseError("exec");
262
+ }
263
+ const channel = chunk.headers["channel"];
264
+ if (typeof channel !== "number") {
265
+ throw new RoomServerException("containers.exec returned a chunk without a valid channel");
266
+ }
267
+ if (channel === 1) {
268
+ session.addOutput(chunk.data);
269
+ continue;
270
+ }
271
+ if (channel === 3) {
272
+ session.close(decodeJsonStatus(chunk.data));
273
+ return;
274
+ }
275
+ }
276
+ throw new RoomServerException("containers.exec stream closed before a status was returned");
277
+ })
278
+ .catch((error) => {
279
+ session.closeError(error);
280
+ });
281
+ return session;
282
+ }
283
+ async stop(params) {
284
+ await this.invoke("stop_container", {
285
+ container_id: params.containerId,
286
+ force: params.force ?? true,
287
+ });
288
+ }
289
+ async waitForExit(params) {
290
+ const output = await this.invoke("wait_for_exit", {
291
+ container_id: params.containerId,
292
+ });
293
+ if (!(output instanceof JsonContent) || !isRecord(output.json)) {
294
+ throw this.unexpectedResponseError("wait_for_exit");
295
+ }
296
+ const exitCode = output.json["exit_code"];
297
+ if (typeof exitCode !== "number" || !Number.isInteger(exitCode)) {
298
+ throw this.unexpectedResponseError("wait_for_exit");
299
+ }
300
+ return exitCode;
301
+ }
302
+ async deleteContainer(params) {
303
+ await this.invoke("delete_container", {
304
+ container_id: params.containerId,
305
+ });
306
+ }
307
+ logs(params) {
308
+ const requestId = uuidv4();
309
+ const closeInput = new Completer();
310
+ const streamController = new StreamController();
311
+ const result = new Completer();
312
+ let inputClosed = false;
313
+ const closeInputStream = () => {
314
+ if (inputClosed) {
315
+ return;
316
+ }
317
+ inputClosed = true;
318
+ if (!closeInput.completed) {
319
+ closeInput.complete();
320
+ }
321
+ };
322
+ const inputStream = async function* () {
323
+ yield new BinaryContent({
324
+ data: new Uint8Array(0),
325
+ headers: {
326
+ kind: "start",
327
+ request_id: requestId,
328
+ container_id: params.containerId,
329
+ follow: params.follow ?? false,
330
+ },
331
+ });
332
+ await closeInput.fut;
333
+ };
334
+ this.room
335
+ .invokeStream({
336
+ toolkit: "containers",
337
+ tool: "logs",
338
+ input: inputStream(),
339
+ })
340
+ .then(async (stream) => {
341
+ const decoder = new TextDecoder();
342
+ for await (const chunk of stream) {
343
+ if (chunk instanceof ErrorContent) {
344
+ throw new RoomServerException(chunk.text, chunk.code);
345
+ }
346
+ if (chunk instanceof ControlContent) {
347
+ continue;
348
+ }
349
+ if (!(chunk instanceof BinaryContent)) {
350
+ throw this.unexpectedResponseError("logs");
351
+ }
352
+ const channel = chunk.headers["channel"];
353
+ if (typeof channel !== "number") {
354
+ throw new RoomServerException("containers.logs returned a chunk without a valid channel");
355
+ }
356
+ if (channel !== 1) {
357
+ continue;
358
+ }
359
+ streamController.add(decoder.decode(chunk.data));
360
+ }
361
+ closeInputStream();
362
+ streamController.close();
363
+ result.complete();
364
+ })
365
+ .catch((error) => {
366
+ closeInputStream();
367
+ streamController.close();
368
+ if (!result.completed) {
369
+ result.completeError(error);
370
+ }
371
+ });
372
+ const outputStream = {
373
+ [Symbol.asyncIterator]() {
374
+ const it = streamController.stream[Symbol.asyncIterator]();
375
+ return {
376
+ async next() {
377
+ return await it.next();
378
+ },
379
+ async return(value) {
380
+ closeInputStream();
381
+ return await it.return?.(value) ?? { done: true, value };
382
+ },
383
+ async throw(e) {
384
+ closeInputStream();
385
+ if (it.throw) {
386
+ return await it.throw(e);
387
+ }
388
+ throw e;
389
+ },
390
+ };
391
+ },
392
+ };
393
+ return {
394
+ stream: outputStream,
395
+ result: result.fut,
396
+ cancel: async () => {
397
+ closeInputStream();
398
+ await result.fut.catch(() => undefined);
399
+ },
400
+ };
401
+ }
402
+ async list(params) {
403
+ const output = await this.invoke("list_containers", {
404
+ all: params?.all,
405
+ });
406
+ if (!(output instanceof JsonContent) || !isRecord(output.json)) {
407
+ throw this.unexpectedResponseError("list");
408
+ }
409
+ const containersRaw = output.json["containers"];
410
+ if (!Array.isArray(containersRaw)) {
411
+ throw this.unexpectedResponseError("list");
412
+ }
413
+ const items = [];
414
+ for (const entry of containersRaw) {
415
+ if (!isRecord(entry)) {
416
+ throw this.unexpectedResponseError("list");
417
+ }
418
+ const startedByRaw = entry["started_by"];
419
+ if (!isRecord(startedByRaw)) {
420
+ throw this.unexpectedResponseError("list");
421
+ }
422
+ const id = entry["id"];
423
+ const image = entry["image"];
424
+ const state = entry["state"];
425
+ const privateFlag = entry["private"];
426
+ const startedById = startedByRaw["id"];
427
+ const startedByName = startedByRaw["name"];
428
+ if (typeof id !== "string" ||
429
+ typeof image !== "string" ||
430
+ typeof state !== "string" ||
431
+ typeof privateFlag !== "boolean" ||
432
+ typeof startedById !== "string" ||
433
+ typeof startedByName !== "string") {
434
+ throw this.unexpectedResponseError("list");
435
+ }
436
+ const nameRaw = entry["name"];
437
+ const serviceIdRaw = entry["service_id"];
438
+ items.push({
439
+ id,
440
+ image,
441
+ name: typeof nameRaw === "string" ? nameRaw : undefined,
442
+ startedBy: {
443
+ id: startedById,
444
+ name: startedByName,
445
+ },
446
+ state,
447
+ private: privateFlag,
448
+ serviceId: typeof serviceIdRaw === "string" ? serviceIdRaw : undefined,
449
+ });
450
+ }
451
+ return items;
452
+ }
453
+ }
@@ -1,5 +1,5 @@
1
- import { RoomClient } from "./room-client";
2
1
  import { DataType } from "./data-types";
2
+ import { RoomClient } from "./room-client";
3
3
  export type CreateMode = "create" | "overwrite" | "create_if_not_exists";
4
4
  export interface TableRef {
5
5
  name: string;
@@ -11,6 +11,11 @@ export declare class DatabaseClient {
11
11
  constructor({ room }: {
12
12
  room: RoomClient;
13
13
  });
14
+ private _unexpectedResponseError;
15
+ private invoke;
16
+ private invokeStream;
17
+ private drainWriteStream;
18
+ private streamRows;
14
19
  listTables(): Promise<string[]>;
15
20
  private createTable;
16
21
  createTableWithSchema({ name, schema, data, mode }: {
@@ -24,6 +29,12 @@ export declare class DatabaseClient {
24
29
  data?: Array<Record<string, any>>;
25
30
  mode?: CreateMode;
26
31
  }): Promise<void>;
32
+ createTableFromDataStream({ name, chunks, schema, mode }: {
33
+ name: string;
34
+ chunks: AsyncIterable<Array<Record<string, any>>> | Iterable<Array<Record<string, any>>>;
35
+ schema?: Record<string, DataType>;
36
+ mode?: CreateMode;
37
+ }): Promise<void>;
27
38
  dropTable({ name, ignoreMissing }: {
28
39
  name: string;
29
40
  ignoreMissing?: boolean;
@@ -40,6 +51,10 @@ export declare class DatabaseClient {
40
51
  table: string;
41
52
  records: Array<Record<string, any>>;
42
53
  }): Promise<void>;
54
+ insertStream({ table, chunks }: {
55
+ table: string;
56
+ chunks: AsyncIterable<Array<Record<string, any>>> | Iterable<Array<Record<string, any>>>;
57
+ }): Promise<void>;
43
58
  update({ table, where, values, valuesSql }: {
44
59
  table: string;
45
60
  where: string;
@@ -53,13 +68,23 @@ export declare class DatabaseClient {
53
68
  merge({ table, on, records }: {
54
69
  table: string;
55
70
  on: string;
56
- records: any;
71
+ records: Array<Record<string, any>>;
72
+ }): Promise<void>;
73
+ mergeStream({ table, on, chunks }: {
74
+ table: string;
75
+ on: string;
76
+ chunks: AsyncIterable<Array<Record<string, any>>> | Iterable<Array<Record<string, any>>>;
57
77
  }): Promise<void>;
58
78
  sql({ query, tables, params }: {
59
79
  query: string;
60
80
  tables: TableRef[];
61
81
  params?: Record<string, any>;
62
82
  }): Promise<Array<Record<string, any>>>;
83
+ sqlStream({ query, tables, params }: {
84
+ query: string;
85
+ tables: TableRef[];
86
+ params?: Record<string, any>;
87
+ }): AsyncIterable<Array<Record<string, any>>>;
63
88
  search({ table, text, vector, where, limit, select }: {
64
89
  table: string;
65
90
  text?: string;
@@ -68,20 +93,31 @@ export declare class DatabaseClient {
68
93
  limit?: number;
69
94
  select?: string[];
70
95
  }): Promise<Array<Record<string, any>>>;
96
+ searchStream({ table, text, vector, where, limit, select }: {
97
+ table: string;
98
+ text?: string;
99
+ vector?: number[];
100
+ where?: string | Record<string, any>;
101
+ limit?: number;
102
+ select?: string[];
103
+ }): AsyncIterable<Array<Record<string, any>>>;
71
104
  optimize(table: string): Promise<void>;
72
- createVectorIndex({ table, column }: {
105
+ createVectorIndex({ table, column, replace }: {
73
106
  table: string;
74
107
  column: string;
108
+ replace?: boolean;
75
109
  }): Promise<void>;
76
- createScalarIndex({ table, column }: {
110
+ createScalarIndex({ table, column, replace }: {
77
111
  table: string;
78
112
  column: string;
113
+ replace?: boolean;
79
114
  }): Promise<void>;
80
- createFullTextSearchIndex({ table, column }: {
115
+ createFullTextSearchIndex({ table, column, replace }: {
81
116
  table: string;
82
117
  column: string;
118
+ replace?: boolean;
83
119
  }): Promise<void>;
84
120
  listIndexes({ table }: {
85
121
  table: string;
86
- }): Promise<Record<string, any>>;
122
+ }): Promise<Array<Record<string, any>>>;
87
123
  }