@meshagent/meshagent 0.29.2 → 0.30.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/CHANGELOG.md +12 -0
- package/dist/browser/agent-client.js +3 -28
- package/dist/browser/agent.js +6 -6
- package/dist/browser/containers-client.d.ts +125 -0
- package/dist/browser/containers-client.js +458 -0
- package/dist/browser/database-client.d.ts +42 -6
- package/dist/browser/database-client.js +610 -77
- package/dist/browser/developer-client.d.ts +2 -2
- package/dist/browser/developer-client.js +60 -15
- package/dist/browser/helpers.js +4 -3
- package/dist/browser/index.d.ts +1 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/lk-client.js +12 -3
- package/dist/browser/meshagent-client.d.ts +5 -0
- package/dist/browser/messaging-client.d.ts +1 -0
- package/dist/browser/messaging-client.js +52 -8
- package/dist/browser/queues-client.d.ts +2 -0
- package/dist/browser/queues-client.js +34 -7
- package/dist/browser/response.d.ts +28 -0
- package/dist/browser/response.js +76 -1
- package/dist/browser/room-client.d.ts +43 -1
- package/dist/browser/room-client.js +204 -0
- package/dist/browser/secrets-client.d.ts +1 -0
- package/dist/browser/secrets-client.js +32 -27
- package/dist/browser/storage-client.d.ts +22 -7
- package/dist/browser/storage-client.js +353 -15
- package/dist/browser/sync-client.d.ts +12 -13
- package/dist/browser/sync-client.js +263 -65
- package/dist/esm/agent-client.js +3 -28
- package/dist/esm/agent.js +6 -6
- package/dist/esm/containers-client.d.ts +125 -0
- package/dist/esm/containers-client.js +453 -0
- package/dist/esm/database-client.d.ts +42 -6
- package/dist/esm/database-client.js +611 -78
- package/dist/esm/developer-client.d.ts +2 -2
- package/dist/esm/developer-client.js +61 -16
- package/dist/esm/helpers.js +4 -3
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/lk-client.js +12 -3
- package/dist/esm/meshagent-client.d.ts +5 -0
- package/dist/esm/messaging-client.d.ts +1 -0
- package/dist/esm/messaging-client.js +52 -8
- package/dist/esm/queues-client.d.ts +2 -0
- package/dist/esm/queues-client.js +35 -8
- package/dist/esm/response.d.ts +28 -0
- package/dist/esm/response.js +73 -0
- package/dist/esm/room-client.d.ts +43 -1
- package/dist/esm/room-client.js +207 -3
- package/dist/esm/secrets-client.d.ts +1 -0
- package/dist/esm/secrets-client.js +33 -28
- package/dist/esm/storage-client.d.ts +22 -7
- package/dist/esm/storage-client.js +353 -15
- package/dist/esm/sync-client.d.ts +12 -13
- package/dist/esm/sync-client.js +263 -64
- package/dist/node/agent-client.js +3 -28
- package/dist/node/agent.js +6 -6
- package/dist/node/containers-client.d.ts +125 -0
- package/dist/node/containers-client.js +458 -0
- package/dist/node/database-client.d.ts +42 -6
- package/dist/node/database-client.js +610 -77
- package/dist/node/developer-client.d.ts +2 -2
- package/dist/node/developer-client.js +60 -15
- package/dist/node/helpers.js +4 -3
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.js +1 -0
- package/dist/node/lk-client.js +12 -3
- package/dist/node/meshagent-client.d.ts +5 -0
- package/dist/node/messaging-client.d.ts +1 -0
- package/dist/node/messaging-client.js +52 -8
- package/dist/node/queues-client.d.ts +2 -0
- package/dist/node/queues-client.js +34 -7
- package/dist/node/response.d.ts +28 -0
- package/dist/node/response.js +76 -1
- package/dist/node/room-client.d.ts +43 -1
- package/dist/node/room-client.js +204 -0
- package/dist/node/secrets-client.d.ts +1 -0
- package/dist/node/secrets-client.js +32 -27
- package/dist/node/storage-client.d.ts +22 -7
- package/dist/node/storage-client.js +353 -15
- package/dist/node/sync-client.d.ts +12 -13
- package/dist/node/sync-client.js +263 -65
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [0.30.0]
|
|
2
|
+
- Breaking: tool invocation now uses toolkit-based `room.invoke`/`room.*` events with streaming tool-call chunks, and `RemoteToolkit` registration follows the new room-scoped protocol.
|
|
3
|
+
- Added a Containers client with image listing/pulling, container lifecycle operations, and exec/log streaming.
|
|
4
|
+
- Storage client replaced handle-based writes with streaming upload/download, download URLs, and size metadata.
|
|
5
|
+
- Database and Sync clients now stream inserts/queries/search and sync updates with typed value handling; messaging/queues/developer clients updated to toolkit invocation.
|
|
6
|
+
|
|
7
|
+
## [0.29.4]
|
|
8
|
+
- Stability
|
|
9
|
+
|
|
10
|
+
## [0.29.3]
|
|
11
|
+
- Stability
|
|
12
|
+
|
|
1
13
|
## [0.29.2]
|
|
2
14
|
- Stability
|
|
3
15
|
|
|
@@ -173,38 +173,13 @@ class AgentsClient {
|
|
|
173
173
|
this.client = room;
|
|
174
174
|
}
|
|
175
175
|
async call(params) {
|
|
176
|
-
await this.client.
|
|
176
|
+
await this.client.call(params);
|
|
177
177
|
}
|
|
178
178
|
async listToolkits(params) {
|
|
179
|
-
|
|
180
|
-
if (params?.participantId != null) {
|
|
181
|
-
request["participant_id"] = params.participantId;
|
|
182
|
-
}
|
|
183
|
-
if (params?.participantName != null) {
|
|
184
|
-
request["participant_name"] = params.participantName;
|
|
185
|
-
}
|
|
186
|
-
if (params?.timeout !== undefined) {
|
|
187
|
-
request["timeout"] = params.timeout;
|
|
188
|
-
}
|
|
189
|
-
const result = (await this.client.sendRequest("agent.list_toolkits", request));
|
|
190
|
-
const tools = result.json["tools"];
|
|
191
|
-
const toolkits = [];
|
|
192
|
-
for (const name of Object.keys(tools)) {
|
|
193
|
-
const data = tools[name];
|
|
194
|
-
toolkits.push(ToolkitDescription.fromJson(data, { name }));
|
|
195
|
-
}
|
|
196
|
-
return toolkits;
|
|
179
|
+
return await this.client.listToolkits(params);
|
|
197
180
|
}
|
|
198
181
|
async invokeTool(params) {
|
|
199
|
-
|
|
200
|
-
toolkit: params.toolkit,
|
|
201
|
-
tool: params.tool,
|
|
202
|
-
arguments: {
|
|
203
|
-
type: "json",
|
|
204
|
-
json: params.arguments,
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
return await this.client.sendRequest("agent.invoke_tool", request);
|
|
182
|
+
return await this.client.invoke(params);
|
|
208
183
|
}
|
|
209
184
|
}
|
|
210
185
|
exports.AgentsClient = AgentsClient;
|
package/dist/browser/agent.js
CHANGED
|
@@ -93,15 +93,15 @@ class RemoteToolkit extends Toolkit {
|
|
|
93
93
|
}
|
|
94
94
|
async start({ public_: isPublic = false } = {}) {
|
|
95
95
|
const handler = this._toolCall.bind(this);
|
|
96
|
-
this.client.protocol.addHandler(`
|
|
96
|
+
this.client.protocol.addHandler(`room.tool_call.${this.name}`, handler);
|
|
97
97
|
await this._register(isPublic);
|
|
98
98
|
}
|
|
99
99
|
async stop() {
|
|
100
100
|
await this._unregister();
|
|
101
|
-
this.client.protocol.removeHandler(`
|
|
101
|
+
this.client.protocol.removeHandler(`room.tool_call.${this.name}`);
|
|
102
102
|
}
|
|
103
103
|
async _register(public_) {
|
|
104
|
-
const response = await this.client.sendRequest("
|
|
104
|
+
const response = await this.client.sendRequest("room.register_toolkit", {
|
|
105
105
|
name: this.name,
|
|
106
106
|
title: this.title,
|
|
107
107
|
description: this.description,
|
|
@@ -114,7 +114,7 @@ class RemoteToolkit extends Toolkit {
|
|
|
114
114
|
async _unregister() {
|
|
115
115
|
if (!this._registrationId)
|
|
116
116
|
return;
|
|
117
|
-
await this.client.sendRequest("
|
|
117
|
+
await this.client.sendRequest("room.unregister_toolkit", {
|
|
118
118
|
id: this._registrationId,
|
|
119
119
|
});
|
|
120
120
|
}
|
|
@@ -144,11 +144,11 @@ class RemoteToolkit extends Toolkit {
|
|
|
144
144
|
args = rawArguments ?? {};
|
|
145
145
|
}
|
|
146
146
|
const response = await this.execute(toolName, args);
|
|
147
|
-
await this.client.protocol.send("
|
|
147
|
+
await this.client.protocol.send("room.tool_call_response", response.pack(), messageId);
|
|
148
148
|
}
|
|
149
149
|
catch (e) {
|
|
150
150
|
const err = new response_1.ErrorContent({ text: String(e) });
|
|
151
|
-
await this.client.protocol.send("
|
|
151
|
+
await this.client.protocol.send("room.tool_call_response", err.pack(), messageId);
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { ContainerMountSpec } from "./meshagent-client";
|
|
2
|
+
import { Content } from "./response";
|
|
3
|
+
import { RoomClient } from "./room-client";
|
|
4
|
+
export interface DockerSecret {
|
|
5
|
+
username: string;
|
|
6
|
+
password: string;
|
|
7
|
+
registry: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
}
|
|
10
|
+
export interface ContainerImage {
|
|
11
|
+
id: string;
|
|
12
|
+
tags: string[];
|
|
13
|
+
size?: number;
|
|
14
|
+
labels: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
export interface ContainerParticipantInfo {
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
}
|
|
20
|
+
export interface RoomContainer {
|
|
21
|
+
id: string;
|
|
22
|
+
image: string;
|
|
23
|
+
name?: string;
|
|
24
|
+
startedBy: ContainerParticipantInfo;
|
|
25
|
+
state: string;
|
|
26
|
+
private: boolean;
|
|
27
|
+
serviceId?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface ContainerLogsSession {
|
|
30
|
+
stream: AsyncIterable<string>;
|
|
31
|
+
result: Promise<void>;
|
|
32
|
+
cancel(): Promise<void>;
|
|
33
|
+
}
|
|
34
|
+
export declare class ExecSession {
|
|
35
|
+
readonly command: string;
|
|
36
|
+
readonly result: Promise<number>;
|
|
37
|
+
readonly previousOutput: Uint8Array[];
|
|
38
|
+
readonly output: AsyncIterable<Uint8Array>;
|
|
39
|
+
private readonly requestId;
|
|
40
|
+
private readonly containerId;
|
|
41
|
+
private readonly tty?;
|
|
42
|
+
private readonly resultCompleter;
|
|
43
|
+
private readonly outputController;
|
|
44
|
+
private readonly queuedInput;
|
|
45
|
+
private inputClosed;
|
|
46
|
+
private closed;
|
|
47
|
+
private inputWaiter;
|
|
48
|
+
private lastResizeWidth?;
|
|
49
|
+
private lastResizeHeight?;
|
|
50
|
+
constructor(params: {
|
|
51
|
+
requestId: string;
|
|
52
|
+
command: string;
|
|
53
|
+
containerId: string;
|
|
54
|
+
tty?: boolean;
|
|
55
|
+
});
|
|
56
|
+
inputStream(): AsyncIterable<Content>;
|
|
57
|
+
write(data: Uint8Array): Promise<void>;
|
|
58
|
+
resize(params: {
|
|
59
|
+
width: number;
|
|
60
|
+
height: number;
|
|
61
|
+
}): Promise<void>;
|
|
62
|
+
stop(): Promise<void>;
|
|
63
|
+
kill(): Promise<void>;
|
|
64
|
+
close(status: number): void;
|
|
65
|
+
closeError(error: unknown): void;
|
|
66
|
+
addOutput(data: Uint8Array): void;
|
|
67
|
+
get isClosed(): boolean;
|
|
68
|
+
private queueInput;
|
|
69
|
+
private closeInputStream;
|
|
70
|
+
}
|
|
71
|
+
export declare class ContainersClient {
|
|
72
|
+
private readonly room;
|
|
73
|
+
constructor({ room }: {
|
|
74
|
+
room: RoomClient;
|
|
75
|
+
});
|
|
76
|
+
private unexpectedResponseError;
|
|
77
|
+
private invoke;
|
|
78
|
+
listImages(): Promise<ContainerImage[]>;
|
|
79
|
+
pullImage(params: {
|
|
80
|
+
tag: string;
|
|
81
|
+
credentials?: DockerSecret[];
|
|
82
|
+
}): Promise<void>;
|
|
83
|
+
run(params: {
|
|
84
|
+
image: string;
|
|
85
|
+
command?: string;
|
|
86
|
+
workingDir?: string;
|
|
87
|
+
env?: Record<string, string>;
|
|
88
|
+
mountPath?: string;
|
|
89
|
+
mountSubpath?: string;
|
|
90
|
+
role?: string;
|
|
91
|
+
participantName?: string;
|
|
92
|
+
ports?: Record<number, number> | Record<string, number>;
|
|
93
|
+
credentials?: DockerSecret[];
|
|
94
|
+
name?: string;
|
|
95
|
+
mounts?: ContainerMountSpec;
|
|
96
|
+
writableRootFs?: boolean;
|
|
97
|
+
private?: boolean;
|
|
98
|
+
}): Promise<string>;
|
|
99
|
+
runService(params: {
|
|
100
|
+
serviceId: string;
|
|
101
|
+
env?: Record<string, string>;
|
|
102
|
+
}): Promise<string>;
|
|
103
|
+
exec(params: {
|
|
104
|
+
containerId: string;
|
|
105
|
+
command: string;
|
|
106
|
+
tty?: boolean;
|
|
107
|
+
}): ExecSession;
|
|
108
|
+
stop(params: {
|
|
109
|
+
containerId: string;
|
|
110
|
+
force?: boolean;
|
|
111
|
+
}): Promise<void>;
|
|
112
|
+
waitForExit(params: {
|
|
113
|
+
containerId: string;
|
|
114
|
+
}): Promise<number>;
|
|
115
|
+
deleteContainer(params: {
|
|
116
|
+
containerId: string;
|
|
117
|
+
}): Promise<void>;
|
|
118
|
+
logs(params: {
|
|
119
|
+
containerId: string;
|
|
120
|
+
follow?: boolean;
|
|
121
|
+
}): ContainerLogsSession;
|
|
122
|
+
list(params?: {
|
|
123
|
+
all?: boolean;
|
|
124
|
+
}): Promise<RoomContainer[]>;
|
|
125
|
+
}
|
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ContainersClient = exports.ExecSession = void 0;
|
|
4
|
+
const uuid_1 = require("uuid");
|
|
5
|
+
const completer_1 = require("./completer");
|
|
6
|
+
const response_1 = require("./response");
|
|
7
|
+
const room_server_client_1 = require("./room-server-client");
|
|
8
|
+
const stream_controller_1 = require("./stream-controller");
|
|
9
|
+
function isRecord(value) {
|
|
10
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
|
+
}
|
|
12
|
+
function toStringMapList(values) {
|
|
13
|
+
return Object.entries(values).map(([key, value]) => ({ key, value }));
|
|
14
|
+
}
|
|
15
|
+
function toPortPairs(values) {
|
|
16
|
+
return Object.entries(values).map(([containerPort, hostPort]) => ({
|
|
17
|
+
container_port: Number(containerPort),
|
|
18
|
+
host_port: hostPort,
|
|
19
|
+
}));
|
|
20
|
+
}
|
|
21
|
+
function toCredentials(values) {
|
|
22
|
+
return values.map((entry) => ({
|
|
23
|
+
registry: entry.registry,
|
|
24
|
+
username: entry.username,
|
|
25
|
+
password: entry.password,
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
function readStringField(data, field, operation) {
|
|
29
|
+
const value = data[field];
|
|
30
|
+
if (typeof value !== "string") {
|
|
31
|
+
throw new room_server_client_1.RoomServerException(`unexpected return type from containers.${operation}`);
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function decodeJsonStatus(data) {
|
|
36
|
+
const text = new TextDecoder().decode(data);
|
|
37
|
+
let parsed;
|
|
38
|
+
try {
|
|
39
|
+
parsed = JSON.parse(text);
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
throw new room_server_client_1.RoomServerException("containers.exec returned an invalid status payload");
|
|
43
|
+
}
|
|
44
|
+
if (!isRecord(parsed)) {
|
|
45
|
+
throw new room_server_client_1.RoomServerException("containers.exec returned an invalid status payload");
|
|
46
|
+
}
|
|
47
|
+
const status = parsed["status"];
|
|
48
|
+
if (typeof status !== "number" || !Number.isInteger(status)) {
|
|
49
|
+
throw new room_server_client_1.RoomServerException("containers.exec returned an invalid status payload");
|
|
50
|
+
}
|
|
51
|
+
return status;
|
|
52
|
+
}
|
|
53
|
+
class ExecSession {
|
|
54
|
+
constructor(params) {
|
|
55
|
+
this.previousOutput = [];
|
|
56
|
+
this.resultCompleter = new completer_1.Completer();
|
|
57
|
+
this.outputController = new stream_controller_1.StreamController();
|
|
58
|
+
this.queuedInput = [];
|
|
59
|
+
this.inputClosed = false;
|
|
60
|
+
this.closed = false;
|
|
61
|
+
this.inputWaiter = null;
|
|
62
|
+
this.requestId = params.requestId;
|
|
63
|
+
this.command = params.command;
|
|
64
|
+
this.containerId = params.containerId;
|
|
65
|
+
this.tty = params.tty;
|
|
66
|
+
this.result = this.resultCompleter.fut;
|
|
67
|
+
this.output = this.outputController.stream;
|
|
68
|
+
}
|
|
69
|
+
async *inputStream() {
|
|
70
|
+
yield new response_1.BinaryContent({
|
|
71
|
+
data: new Uint8Array(0),
|
|
72
|
+
headers: {
|
|
73
|
+
kind: "start",
|
|
74
|
+
request_id: this.requestId,
|
|
75
|
+
container_id: this.containerId,
|
|
76
|
+
command: this.command,
|
|
77
|
+
tty: this.tty,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
while (true) {
|
|
81
|
+
while (this.queuedInput.length > 0) {
|
|
82
|
+
const chunk = this.queuedInput.shift();
|
|
83
|
+
if (chunk) {
|
|
84
|
+
yield chunk;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
if (this.inputClosed) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
this.inputWaiter = new completer_1.Completer();
|
|
91
|
+
await this.inputWaiter.fut;
|
|
92
|
+
this.inputWaiter = null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async write(data) {
|
|
96
|
+
this.queueInput({ channel: 1, data });
|
|
97
|
+
}
|
|
98
|
+
async resize(params) {
|
|
99
|
+
if (this.lastResizeWidth === params.width && this.lastResizeHeight === params.height) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
this.lastResizeWidth = params.width;
|
|
103
|
+
this.lastResizeHeight = params.height;
|
|
104
|
+
this.queueInput({ channel: 4, width: params.width, height: params.height });
|
|
105
|
+
}
|
|
106
|
+
async stop() {
|
|
107
|
+
this.closeInputStream();
|
|
108
|
+
}
|
|
109
|
+
async kill() {
|
|
110
|
+
if (this.inputClosed) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
this.queueInput({ channel: 5 });
|
|
114
|
+
}
|
|
115
|
+
close(status) {
|
|
116
|
+
if (!this.resultCompleter.completed) {
|
|
117
|
+
this.resultCompleter.complete(status);
|
|
118
|
+
}
|
|
119
|
+
this.closed = true;
|
|
120
|
+
this.closeInputStream();
|
|
121
|
+
this.outputController.close();
|
|
122
|
+
}
|
|
123
|
+
closeError(error) {
|
|
124
|
+
if (!this.resultCompleter.completed) {
|
|
125
|
+
this.resultCompleter.completeError(error);
|
|
126
|
+
}
|
|
127
|
+
this.closed = true;
|
|
128
|
+
this.closeInputStream();
|
|
129
|
+
this.outputController.close();
|
|
130
|
+
}
|
|
131
|
+
addOutput(data) {
|
|
132
|
+
this.previousOutput.push(data);
|
|
133
|
+
this.outputController.add(data);
|
|
134
|
+
}
|
|
135
|
+
get isClosed() {
|
|
136
|
+
return this.closed;
|
|
137
|
+
}
|
|
138
|
+
queueInput(params) {
|
|
139
|
+
if (this.inputClosed) {
|
|
140
|
+
throw new room_server_client_1.RoomServerException("container exec session is already closed");
|
|
141
|
+
}
|
|
142
|
+
this.queuedInput.push(new response_1.BinaryContent({
|
|
143
|
+
data: params.data ?? new Uint8Array(0),
|
|
144
|
+
headers: {
|
|
145
|
+
kind: "input",
|
|
146
|
+
channel: params.channel,
|
|
147
|
+
width: params.width,
|
|
148
|
+
height: params.height,
|
|
149
|
+
},
|
|
150
|
+
}));
|
|
151
|
+
this.inputWaiter?.complete();
|
|
152
|
+
}
|
|
153
|
+
closeInputStream() {
|
|
154
|
+
if (this.inputClosed) {
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
this.inputClosed = true;
|
|
158
|
+
this.inputWaiter?.complete();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
exports.ExecSession = ExecSession;
|
|
162
|
+
class ContainersClient {
|
|
163
|
+
constructor({ room }) {
|
|
164
|
+
this.room = room;
|
|
165
|
+
}
|
|
166
|
+
unexpectedResponseError(operation) {
|
|
167
|
+
return new room_server_client_1.RoomServerException(`unexpected return type from containers.${operation}`);
|
|
168
|
+
}
|
|
169
|
+
async invoke(operation, input) {
|
|
170
|
+
return await this.room.invoke({
|
|
171
|
+
toolkit: "containers",
|
|
172
|
+
tool: operation,
|
|
173
|
+
input,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
async listImages() {
|
|
177
|
+
const output = await this.invoke("list_images", {});
|
|
178
|
+
if (!(output instanceof response_1.JsonContent)) {
|
|
179
|
+
throw this.unexpectedResponseError("list_images");
|
|
180
|
+
}
|
|
181
|
+
const imagesRaw = output.json["images"];
|
|
182
|
+
if (!Array.isArray(imagesRaw)) {
|
|
183
|
+
throw this.unexpectedResponseError("list_images");
|
|
184
|
+
}
|
|
185
|
+
const images = [];
|
|
186
|
+
for (const entry of imagesRaw) {
|
|
187
|
+
if (!isRecord(entry)) {
|
|
188
|
+
throw this.unexpectedResponseError("list_images");
|
|
189
|
+
}
|
|
190
|
+
const id = entry["id"];
|
|
191
|
+
const tags = entry["tags"];
|
|
192
|
+
const labelsRaw = entry["labels"];
|
|
193
|
+
if (typeof id !== "string" || !Array.isArray(tags)) {
|
|
194
|
+
throw this.unexpectedResponseError("list_images");
|
|
195
|
+
}
|
|
196
|
+
const normalizedTags = tags.filter((tag) => typeof tag === "string");
|
|
197
|
+
const size = entry["size"];
|
|
198
|
+
images.push({
|
|
199
|
+
id,
|
|
200
|
+
tags: normalizedTags,
|
|
201
|
+
size: typeof size === "number" ? size : undefined,
|
|
202
|
+
labels: isRecord(labelsRaw) ? labelsRaw : {},
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
return images;
|
|
206
|
+
}
|
|
207
|
+
async pullImage(params) {
|
|
208
|
+
await this.invoke("pull_image", {
|
|
209
|
+
tag: params.tag,
|
|
210
|
+
credentials: toCredentials(params.credentials ?? []),
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
async run(params) {
|
|
214
|
+
const output = await this.invoke("run", {
|
|
215
|
+
image: params.image,
|
|
216
|
+
command: params.command,
|
|
217
|
+
working_dir: params.workingDir,
|
|
218
|
+
env: toStringMapList(params.env ?? {}),
|
|
219
|
+
mount_path: params.mountPath,
|
|
220
|
+
mount_subpath: params.mountSubpath,
|
|
221
|
+
role: params.role,
|
|
222
|
+
participant_name: params.participantName,
|
|
223
|
+
ports: toPortPairs(params.ports ?? {}),
|
|
224
|
+
credentials: toCredentials(params.credentials ?? []),
|
|
225
|
+
name: params.name,
|
|
226
|
+
mounts: params.mounts,
|
|
227
|
+
writable_root_fs: params.writableRootFs,
|
|
228
|
+
private: params.private,
|
|
229
|
+
});
|
|
230
|
+
if (!(output instanceof response_1.JsonContent) || !isRecord(output.json)) {
|
|
231
|
+
throw this.unexpectedResponseError("run");
|
|
232
|
+
}
|
|
233
|
+
return readStringField(output.json, "container_id", "run");
|
|
234
|
+
}
|
|
235
|
+
async runService(params) {
|
|
236
|
+
const output = await this.invoke("run_service", {
|
|
237
|
+
service_id: params.serviceId,
|
|
238
|
+
env: toStringMapList(params.env ?? {}),
|
|
239
|
+
});
|
|
240
|
+
if (!(output instanceof response_1.JsonContent) || !isRecord(output.json)) {
|
|
241
|
+
throw this.unexpectedResponseError("run_service");
|
|
242
|
+
}
|
|
243
|
+
return readStringField(output.json, "container_id", "run_service");
|
|
244
|
+
}
|
|
245
|
+
exec(params) {
|
|
246
|
+
const requestId = (0, uuid_1.v4)();
|
|
247
|
+
const session = new ExecSession({
|
|
248
|
+
requestId,
|
|
249
|
+
command: params.command,
|
|
250
|
+
containerId: params.containerId,
|
|
251
|
+
tty: params.tty,
|
|
252
|
+
});
|
|
253
|
+
this.room
|
|
254
|
+
.invokeStream({
|
|
255
|
+
toolkit: "containers",
|
|
256
|
+
tool: "exec",
|
|
257
|
+
input: session.inputStream(),
|
|
258
|
+
})
|
|
259
|
+
.then(async (stream) => {
|
|
260
|
+
for await (const chunk of stream) {
|
|
261
|
+
if (chunk instanceof response_1.ErrorContent) {
|
|
262
|
+
throw new room_server_client_1.RoomServerException(chunk.text, chunk.code);
|
|
263
|
+
}
|
|
264
|
+
if (!(chunk instanceof response_1.BinaryContent)) {
|
|
265
|
+
throw this.unexpectedResponseError("exec");
|
|
266
|
+
}
|
|
267
|
+
const channel = chunk.headers["channel"];
|
|
268
|
+
if (typeof channel !== "number") {
|
|
269
|
+
throw new room_server_client_1.RoomServerException("containers.exec returned a chunk without a valid channel");
|
|
270
|
+
}
|
|
271
|
+
if (channel === 1) {
|
|
272
|
+
session.addOutput(chunk.data);
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
if (channel === 3) {
|
|
276
|
+
session.close(decodeJsonStatus(chunk.data));
|
|
277
|
+
return;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
throw new room_server_client_1.RoomServerException("containers.exec stream closed before a status was returned");
|
|
281
|
+
})
|
|
282
|
+
.catch((error) => {
|
|
283
|
+
session.closeError(error);
|
|
284
|
+
});
|
|
285
|
+
return session;
|
|
286
|
+
}
|
|
287
|
+
async stop(params) {
|
|
288
|
+
await this.invoke("stop_container", {
|
|
289
|
+
container_id: params.containerId,
|
|
290
|
+
force: params.force ?? true,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
async waitForExit(params) {
|
|
294
|
+
const output = await this.invoke("wait_for_exit", {
|
|
295
|
+
container_id: params.containerId,
|
|
296
|
+
});
|
|
297
|
+
if (!(output instanceof response_1.JsonContent) || !isRecord(output.json)) {
|
|
298
|
+
throw this.unexpectedResponseError("wait_for_exit");
|
|
299
|
+
}
|
|
300
|
+
const exitCode = output.json["exit_code"];
|
|
301
|
+
if (typeof exitCode !== "number" || !Number.isInteger(exitCode)) {
|
|
302
|
+
throw this.unexpectedResponseError("wait_for_exit");
|
|
303
|
+
}
|
|
304
|
+
return exitCode;
|
|
305
|
+
}
|
|
306
|
+
async deleteContainer(params) {
|
|
307
|
+
await this.invoke("delete_container", {
|
|
308
|
+
container_id: params.containerId,
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
logs(params) {
|
|
312
|
+
const requestId = (0, uuid_1.v4)();
|
|
313
|
+
const closeInput = new completer_1.Completer();
|
|
314
|
+
const streamController = new stream_controller_1.StreamController();
|
|
315
|
+
const result = new completer_1.Completer();
|
|
316
|
+
let inputClosed = false;
|
|
317
|
+
const closeInputStream = () => {
|
|
318
|
+
if (inputClosed) {
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
inputClosed = true;
|
|
322
|
+
if (!closeInput.completed) {
|
|
323
|
+
closeInput.complete();
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
const inputStream = async function* () {
|
|
327
|
+
yield new response_1.BinaryContent({
|
|
328
|
+
data: new Uint8Array(0),
|
|
329
|
+
headers: {
|
|
330
|
+
kind: "start",
|
|
331
|
+
request_id: requestId,
|
|
332
|
+
container_id: params.containerId,
|
|
333
|
+
follow: params.follow ?? false,
|
|
334
|
+
},
|
|
335
|
+
});
|
|
336
|
+
await closeInput.fut;
|
|
337
|
+
};
|
|
338
|
+
this.room
|
|
339
|
+
.invokeStream({
|
|
340
|
+
toolkit: "containers",
|
|
341
|
+
tool: "logs",
|
|
342
|
+
input: inputStream(),
|
|
343
|
+
})
|
|
344
|
+
.then(async (stream) => {
|
|
345
|
+
const decoder = new TextDecoder();
|
|
346
|
+
for await (const chunk of stream) {
|
|
347
|
+
if (chunk instanceof response_1.ErrorContent) {
|
|
348
|
+
throw new room_server_client_1.RoomServerException(chunk.text, chunk.code);
|
|
349
|
+
}
|
|
350
|
+
if (chunk instanceof response_1.ControlContent) {
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (!(chunk instanceof response_1.BinaryContent)) {
|
|
354
|
+
throw this.unexpectedResponseError("logs");
|
|
355
|
+
}
|
|
356
|
+
const channel = chunk.headers["channel"];
|
|
357
|
+
if (typeof channel !== "number") {
|
|
358
|
+
throw new room_server_client_1.RoomServerException("containers.logs returned a chunk without a valid channel");
|
|
359
|
+
}
|
|
360
|
+
if (channel !== 1) {
|
|
361
|
+
continue;
|
|
362
|
+
}
|
|
363
|
+
streamController.add(decoder.decode(chunk.data));
|
|
364
|
+
}
|
|
365
|
+
closeInputStream();
|
|
366
|
+
streamController.close();
|
|
367
|
+
result.complete();
|
|
368
|
+
})
|
|
369
|
+
.catch((error) => {
|
|
370
|
+
closeInputStream();
|
|
371
|
+
streamController.close();
|
|
372
|
+
if (!result.completed) {
|
|
373
|
+
result.completeError(error);
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
const outputStream = {
|
|
377
|
+
[Symbol.asyncIterator]() {
|
|
378
|
+
const it = streamController.stream[Symbol.asyncIterator]();
|
|
379
|
+
return {
|
|
380
|
+
async next() {
|
|
381
|
+
return await it.next();
|
|
382
|
+
},
|
|
383
|
+
async return(value) {
|
|
384
|
+
closeInputStream();
|
|
385
|
+
return await it.return?.(value) ?? { done: true, value };
|
|
386
|
+
},
|
|
387
|
+
async throw(e) {
|
|
388
|
+
closeInputStream();
|
|
389
|
+
if (it.throw) {
|
|
390
|
+
return await it.throw(e);
|
|
391
|
+
}
|
|
392
|
+
throw e;
|
|
393
|
+
},
|
|
394
|
+
};
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
return {
|
|
398
|
+
stream: outputStream,
|
|
399
|
+
result: result.fut,
|
|
400
|
+
cancel: async () => {
|
|
401
|
+
closeInputStream();
|
|
402
|
+
await result.fut.catch(() => undefined);
|
|
403
|
+
},
|
|
404
|
+
};
|
|
405
|
+
}
|
|
406
|
+
async list(params) {
|
|
407
|
+
const output = await this.invoke("list_containers", {
|
|
408
|
+
all: params?.all,
|
|
409
|
+
});
|
|
410
|
+
if (!(output instanceof response_1.JsonContent) || !isRecord(output.json)) {
|
|
411
|
+
throw this.unexpectedResponseError("list");
|
|
412
|
+
}
|
|
413
|
+
const containersRaw = output.json["containers"];
|
|
414
|
+
if (!Array.isArray(containersRaw)) {
|
|
415
|
+
throw this.unexpectedResponseError("list");
|
|
416
|
+
}
|
|
417
|
+
const items = [];
|
|
418
|
+
for (const entry of containersRaw) {
|
|
419
|
+
if (!isRecord(entry)) {
|
|
420
|
+
throw this.unexpectedResponseError("list");
|
|
421
|
+
}
|
|
422
|
+
const startedByRaw = entry["started_by"];
|
|
423
|
+
if (!isRecord(startedByRaw)) {
|
|
424
|
+
throw this.unexpectedResponseError("list");
|
|
425
|
+
}
|
|
426
|
+
const id = entry["id"];
|
|
427
|
+
const image = entry["image"];
|
|
428
|
+
const state = entry["state"];
|
|
429
|
+
const privateFlag = entry["private"];
|
|
430
|
+
const startedById = startedByRaw["id"];
|
|
431
|
+
const startedByName = startedByRaw["name"];
|
|
432
|
+
if (typeof id !== "string" ||
|
|
433
|
+
typeof image !== "string" ||
|
|
434
|
+
typeof state !== "string" ||
|
|
435
|
+
typeof privateFlag !== "boolean" ||
|
|
436
|
+
typeof startedById !== "string" ||
|
|
437
|
+
typeof startedByName !== "string") {
|
|
438
|
+
throw this.unexpectedResponseError("list");
|
|
439
|
+
}
|
|
440
|
+
const nameRaw = entry["name"];
|
|
441
|
+
const serviceIdRaw = entry["service_id"];
|
|
442
|
+
items.push({
|
|
443
|
+
id,
|
|
444
|
+
image,
|
|
445
|
+
name: typeof nameRaw === "string" ? nameRaw : undefined,
|
|
446
|
+
startedBy: {
|
|
447
|
+
id: startedById,
|
|
448
|
+
name: startedByName,
|
|
449
|
+
},
|
|
450
|
+
state,
|
|
451
|
+
private: privateFlag,
|
|
452
|
+
serviceId: typeof serviceIdRaw === "string" ? serviceIdRaw : undefined,
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
return items;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
exports.ContainersClient = ContainersClient;
|