napbox 0.0.0-stage → 0.1.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/LICENSE +202 -0
- package/NOTICE +13 -0
- package/README.md +82 -2
- package/README.zh-CN.md +83 -0
- package/THIRD_PARTY_NOTICES.txt +7674 -0
- package/dist/agent/cache.d.ts +1 -0
- package/dist/agent/cache.js +17 -0
- package/dist/agent/hooks.d.ts +11 -0
- package/dist/agent/hooks.js +139 -0
- package/dist/agent/locations.d.ts +6 -0
- package/dist/agent/locations.js +11 -0
- package/dist/agent/mcp.d.ts +1 -0
- package/dist/agent/mcp.js +132 -0
- package/dist/agent/setup.d.ts +20 -0
- package/dist/agent/setup.js +255 -0
- package/dist/agent/shell-command.d.ts +13 -0
- package/dist/agent/shell-command.js +208 -0
- package/dist/agent/tools.d.ts +43 -0
- package/dist/agent/tools.js +295 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +59 -0
- package/dist/client.d.ts +84 -0
- package/dist/client.js +261 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/protocol.d.ts +508 -0
- package/dist/protocol.js +210 -0
- package/dist/server-launcher.mjs +79 -0
- package/native/napbox +0 -0
- package/native/napbox-guard +0 -0
- package/native/napboxd +4 -0
- package/package.json +46 -4
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { CreateParams, DownResult, EnvironmentInfo, type EgressMode, type EnvironmentEvent, type EnvironmentTarget, type ErrorCode, ExecResult, ImageImportResult, LogsResult, type OutputStream, RunsResult, StatusResult, type SubscribeParams, type RunParams, type UpParams } from "./protocol.ts";
|
|
2
|
+
export declare class NapboxError extends Error {
|
|
3
|
+
name: string;
|
|
4
|
+
readonly code: ErrorCode | (string & {});
|
|
5
|
+
constructor(code: string, message: string);
|
|
6
|
+
}
|
|
7
|
+
/** 和 napboxd 的 Paths.supportDirectory 一样:设了 NAPBOX_SUPPORT_DIR 时用它 */
|
|
8
|
+
export declare function supportDirectory(): string;
|
|
9
|
+
export declare function defaultSocketPath(): string;
|
|
10
|
+
export interface Output {
|
|
11
|
+
stream: OutputStream;
|
|
12
|
+
/** 原样的字节,可能在多字节字符或一行的中间切开 */
|
|
13
|
+
data: Uint8Array;
|
|
14
|
+
}
|
|
15
|
+
type OnOutput = (output: Output) => void | Promise<void>;
|
|
16
|
+
/** create 里 files、profiles、environment 不写或传 undefined 时是空的。compose 变量要显式给,不会读本进程的环境变量 */
|
|
17
|
+
export type CreateOptions = Pick<CreateParams, "name" | "owner" | "directory" | "egress"> & Partial<Pick<CreateParams, "files" | "profiles" | "environment">>;
|
|
18
|
+
/** up 的 environment 必须给:compose 变量替换用哪些变量由调用方决定(命令行给的是它自己的环境变量) */
|
|
19
|
+
export type UpOptions = Pick<UpParams, "cwd" | "environment"> & Partial<Pick<UpParams, "files" | "profiles" | "build" | "temp">>;
|
|
20
|
+
/** 起一个临时环境,在前台跑其中一个服务,跑完销毁。command 不写就用 compose 里写的 */
|
|
21
|
+
export type RunOptions = Pick<RunParams, "cwd" | "environment" | "service"> & Partial<Pick<RunParams, "files" | "profiles" | "build" | "egress" | "command" | "noDeps">> & {
|
|
22
|
+
/** 给服务的标准输入,不写时它一读就是结束 */
|
|
23
|
+
stdin?: ExecOptions["stdin"];
|
|
24
|
+
onOutput: OnOutput;
|
|
25
|
+
/** 取消时服务停掉,临时环境照样销毁 */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
};
|
|
28
|
+
export interface LogsOptions {
|
|
29
|
+
target: EnvironmentTarget;
|
|
30
|
+
service: string;
|
|
31
|
+
/** 最后几行,不写是全部(命令行的默认 200 行是命令行自己的) */
|
|
32
|
+
tail?: number;
|
|
33
|
+
/** 输出完继续跟着。环境回收时结果带 ended;napboxd 退出时抛 daemon_unavailable */
|
|
34
|
+
follow?: boolean;
|
|
35
|
+
onOutput: OnOutput;
|
|
36
|
+
signal?: AbortSignal;
|
|
37
|
+
}
|
|
38
|
+
export interface ExecOptions {
|
|
39
|
+
target: EnvironmentTarget;
|
|
40
|
+
service: string;
|
|
41
|
+
command: string[];
|
|
42
|
+
/** 给命令的标准输入,不写时命令一读就是结束。内容给完才告诉命令输入结束 */
|
|
43
|
+
stdin?: string | Uint8Array | AsyncIterable<Uint8Array | string>;
|
|
44
|
+
onOutput: OnOutput;
|
|
45
|
+
/** 取消时结束容器里的这条命令和它的子进程;自己用 setsid 脱离的进程等环境停止时才结束 */
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
}
|
|
48
|
+
export interface SubscribeOptions extends SubscribeParams {
|
|
49
|
+
/** 订阅生效了,从这以后的事件都会送到。在这里查 status、runs,中间不会漏(DESIGN.md 第 9 节) */
|
|
50
|
+
onSubscribed?: () => void | Promise<void>;
|
|
51
|
+
onEvent: (event: EnvironmentEvent) => void | Promise<void>;
|
|
52
|
+
/** 不再订阅时用它取消 */
|
|
53
|
+
signal?: AbortSignal;
|
|
54
|
+
}
|
|
55
|
+
export declare class NapboxClient {
|
|
56
|
+
readonly socketPath: string;
|
|
57
|
+
constructor(options?: {
|
|
58
|
+
socketPath?: string;
|
|
59
|
+
});
|
|
60
|
+
status(): Promise<StatusResult>;
|
|
61
|
+
up(options: UpOptions): Promise<EnvironmentInfo>;
|
|
62
|
+
/** 结果里的 exitCode 是前台那个服务的退出码;它失败不算出错,不抛。环境起不来才抛 */
|
|
63
|
+
run(options: RunOptions): Promise<ExecResult>;
|
|
64
|
+
create(options: CreateOptions): Promise<EnvironmentInfo>;
|
|
65
|
+
/** 环境当前的信息,不唤醒 */
|
|
66
|
+
environment(target: EnvironmentTarget): Promise<EnvironmentInfo>;
|
|
67
|
+
wake(target: EnvironmentTarget): Promise<EnvironmentInfo>;
|
|
68
|
+
sleep(target: EnvironmentTarget): Promise<EnvironmentInfo>;
|
|
69
|
+
down(target: EnvironmentTarget): Promise<DownResult>;
|
|
70
|
+
runs(target: EnvironmentTarget): Promise<RunsResult>;
|
|
71
|
+
egress(target: EnvironmentTarget, mode: EgressMode): Promise<EnvironmentInfo>;
|
|
72
|
+
reset(target: EnvironmentTarget, volume: string): Promise<EnvironmentInfo>;
|
|
73
|
+
importImage(reference: string): Promise<ImageImportResult>;
|
|
74
|
+
logs(options: LogsOptions): Promise<LogsResult>;
|
|
75
|
+
/** 结果里的 exitCode 和命令的退出码一样;命令失败不算出错,不抛 */
|
|
76
|
+
exec(options: ExecOptions): Promise<ExecResult>;
|
|
77
|
+
/**
|
|
78
|
+
* 一直收事件,直到 signal 取消(以 signal.reason 拒绝)或 napboxd 退出(抛 daemon_unavailable)。
|
|
79
|
+
* 不存盘、不补发:断开后重新订阅,在 onSubscribed 里用 status 和 runs 补齐。
|
|
80
|
+
*/
|
|
81
|
+
subscribe(options: SubscribeOptions): Promise<void>;
|
|
82
|
+
private exchange;
|
|
83
|
+
}
|
|
84
|
+
export {};
|
package/dist/client.js
ADDED
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
// 调用 napboxd 本地 socket 的客户端,协议见 DESIGN.md 第 4、9 节。
|
|
2
|
+
//
|
|
3
|
+
// 约定(测试在 test/client.test.ts、test/vm.test.ts):
|
|
4
|
+
// - 每次调用一个连接。一个连接上的请求是排队处理的,up 可能要很久,不能共用。
|
|
5
|
+
// - 失败一律抛 NapboxError,code 是 napboxd 给的原样错误码。客户端自己只产生两种:
|
|
6
|
+
// - daemon_unavailable:连不上,或者还没拿到最后结果连接就断了。napboxd 没运行时不会替它启动,它由登录项负责。
|
|
7
|
+
// 中途断开时这次操作不一定没做,要用 status 或 environment 查当前状态。
|
|
8
|
+
// - protocol_error:回应看不懂(不是 JSON、字段或取值对不上),一般是客户端和 napboxd 版本差太多。
|
|
9
|
+
// 不比较版本号:多出来的字段不管,旧 napboxd 没有的方法它自己报 unknown_method。
|
|
10
|
+
// - 不设超时:up 可能在拉镜像,napboxd 自己有 healthcheck 的等待上限。logs、exec、run、subscribe 可以用 signal 取消,
|
|
11
|
+
// 取消后 Promise 以 signal.reason 拒绝。其余方法不收 signal:断开连接并不会停下 napboxd 里正在做的事。
|
|
12
|
+
// - 回调返回 Promise 时等它完成才读下一条;回调抛错时结束这次调用,Promise 以那个错误拒绝。
|
|
13
|
+
import { homedir } from "node:os";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { createConnection } from "node:net";
|
|
16
|
+
import { CreateParams, DownResult, EmptyResult, EnvironmentInfo, ExecResult, ImageImportResult, LogsResult, methods, Response, RunsResult, StatusResult, } from "./protocol.js";
|
|
17
|
+
export class NapboxError extends Error {
|
|
18
|
+
name = "NapboxError";
|
|
19
|
+
code;
|
|
20
|
+
constructor(code, message) {
|
|
21
|
+
super(message);
|
|
22
|
+
this.code = code;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/** 和 napboxd 的 Paths.supportDirectory 一样:设了 NAPBOX_SUPPORT_DIR 时用它 */
|
|
26
|
+
export function supportDirectory() {
|
|
27
|
+
return (process.env.NAPBOX_SUPPORT_DIR || path.join(homedir(), "Library/Application Support/napbox"));
|
|
28
|
+
}
|
|
29
|
+
export function defaultSocketPath() {
|
|
30
|
+
return path.join(supportDirectory(), "napboxd.sock");
|
|
31
|
+
}
|
|
32
|
+
const REQUEST_ID = 1;
|
|
33
|
+
export class NapboxClient {
|
|
34
|
+
socketPath;
|
|
35
|
+
constructor(options = {}) {
|
|
36
|
+
this.socketPath = options.socketPath ?? defaultSocketPath();
|
|
37
|
+
}
|
|
38
|
+
status() {
|
|
39
|
+
return this.exchange({ method: methods.status, result: StatusResult });
|
|
40
|
+
}
|
|
41
|
+
up(options) {
|
|
42
|
+
const { cwd, environment, files = [], profiles = [], build = false, temp } = options;
|
|
43
|
+
const params = { cwd, files, profiles, environment, build, temp };
|
|
44
|
+
return this.exchange({ method: methods.up, params, result: EnvironmentInfo });
|
|
45
|
+
}
|
|
46
|
+
/** 结果里的 exitCode 是前台那个服务的退出码;它失败不算出错,不抛。环境起不来才抛 */
|
|
47
|
+
run(options) {
|
|
48
|
+
const { cwd, environment, service, files = [], profiles = [], build = false, egress } = options;
|
|
49
|
+
const { command = [], noDeps = false, stdin, onOutput, signal } = options;
|
|
50
|
+
const params = { cwd, files, profiles, environment, build, egress, service, command, noDeps };
|
|
51
|
+
return this.exchange({
|
|
52
|
+
method: methods.run,
|
|
53
|
+
params,
|
|
54
|
+
result: ExecResult,
|
|
55
|
+
onOutput,
|
|
56
|
+
afterRequest: (write) => sendStdin(stdin, write),
|
|
57
|
+
signal,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
create(options) {
|
|
61
|
+
const { name, owner, directory, files = [], profiles = [], environment = {}, egress } = options;
|
|
62
|
+
const params = { name, owner, directory, files, profiles, environment, egress };
|
|
63
|
+
return this.exchange({ method: methods.create, params, result: EnvironmentInfo });
|
|
64
|
+
}
|
|
65
|
+
/** 环境当前的信息,不唤醒 */
|
|
66
|
+
environment(target) {
|
|
67
|
+
return this.exchange({
|
|
68
|
+
method: methods.environment,
|
|
69
|
+
params: { target },
|
|
70
|
+
result: EnvironmentInfo,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
wake(target) {
|
|
74
|
+
return this.exchange({ method: methods.wake, params: { target }, result: EnvironmentInfo });
|
|
75
|
+
}
|
|
76
|
+
sleep(target) {
|
|
77
|
+
return this.exchange({ method: methods.sleep, params: { target }, result: EnvironmentInfo });
|
|
78
|
+
}
|
|
79
|
+
down(target) {
|
|
80
|
+
return this.exchange({ method: methods.down, params: { target }, result: DownResult });
|
|
81
|
+
}
|
|
82
|
+
runs(target) {
|
|
83
|
+
return this.exchange({ method: methods.runs, params: { target }, result: RunsResult });
|
|
84
|
+
}
|
|
85
|
+
egress(target, mode) {
|
|
86
|
+
return this.exchange({
|
|
87
|
+
method: methods.egress,
|
|
88
|
+
params: { target, mode },
|
|
89
|
+
result: EnvironmentInfo,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
reset(target, volume) {
|
|
93
|
+
return this.exchange({
|
|
94
|
+
method: methods.reset,
|
|
95
|
+
params: { target, volume },
|
|
96
|
+
result: EnvironmentInfo,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
importImage(reference) {
|
|
100
|
+
return this.exchange({
|
|
101
|
+
method: methods.imageImport,
|
|
102
|
+
params: { reference },
|
|
103
|
+
result: ImageImportResult,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
logs(options) {
|
|
107
|
+
const { target, service, tail, follow = false, onOutput, signal } = options;
|
|
108
|
+
return this.exchange({
|
|
109
|
+
method: methods.logs,
|
|
110
|
+
params: { target, service, tail, follow },
|
|
111
|
+
result: LogsResult,
|
|
112
|
+
onOutput,
|
|
113
|
+
signal,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** 结果里的 exitCode 和命令的退出码一样;命令失败不算出错,不抛 */
|
|
117
|
+
exec(options) {
|
|
118
|
+
const { target, service, command, stdin, onOutput, signal } = options;
|
|
119
|
+
return this.exchange({
|
|
120
|
+
method: methods.exec,
|
|
121
|
+
params: { target, service, command },
|
|
122
|
+
result: ExecResult,
|
|
123
|
+
onOutput,
|
|
124
|
+
afterRequest: (write) => sendStdin(stdin, write),
|
|
125
|
+
signal,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 一直收事件,直到 signal 取消(以 signal.reason 拒绝)或 napboxd 退出(抛 daemon_unavailable)。
|
|
130
|
+
* 不存盘、不补发:断开后重新订阅,在 onSubscribed 里用 status 和 runs 补齐。
|
|
131
|
+
*/
|
|
132
|
+
async subscribe(options) {
|
|
133
|
+
const { owner, names, onSubscribed, onEvent, signal } = options;
|
|
134
|
+
await this.exchange({
|
|
135
|
+
method: methods.subscribe,
|
|
136
|
+
params: { owner, names },
|
|
137
|
+
result: EmptyResult,
|
|
138
|
+
onSubscribed,
|
|
139
|
+
onEvent,
|
|
140
|
+
signal,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
async exchange(call) {
|
|
144
|
+
const { signal } = call;
|
|
145
|
+
signal?.throwIfAborted();
|
|
146
|
+
const socket = await connect(this.socketPath);
|
|
147
|
+
const abort = () => socket.destroy();
|
|
148
|
+
signal?.addEventListener("abort", abort);
|
|
149
|
+
let inputFailure;
|
|
150
|
+
try {
|
|
151
|
+
signal?.throwIfAborted();
|
|
152
|
+
const replies = readLines(socket, this.socketPath);
|
|
153
|
+
const write = (message) => new Promise((resolve) => {
|
|
154
|
+
socket.write(`${JSON.stringify(message)}\n`, (error) => resolve(!error));
|
|
155
|
+
});
|
|
156
|
+
void write({ id: REQUEST_ID, method: call.method, params: call.params });
|
|
157
|
+
void call.afterRequest?.(write).catch((error) => {
|
|
158
|
+
inputFailure = { error };
|
|
159
|
+
socket.destroy();
|
|
160
|
+
});
|
|
161
|
+
for await (const line of replies) {
|
|
162
|
+
const reply = parseReply(line);
|
|
163
|
+
if (reply.error)
|
|
164
|
+
throw new NapboxError(reply.error.code, reply.error.message);
|
|
165
|
+
if (reply.result !== undefined)
|
|
166
|
+
return checked(call.result, reply.result);
|
|
167
|
+
if (reply.output && call.onOutput) {
|
|
168
|
+
const data = Buffer.from(reply.output.data, "base64");
|
|
169
|
+
await call.onOutput({ stream: reply.output.stream, data });
|
|
170
|
+
}
|
|
171
|
+
else if (reply.event && call.onEvent) {
|
|
172
|
+
await call.onEvent(reply.event);
|
|
173
|
+
}
|
|
174
|
+
else if (reply.subscribed && call.onEvent) {
|
|
175
|
+
await call.onSubscribed?.();
|
|
176
|
+
}
|
|
177
|
+
else {
|
|
178
|
+
throw new NapboxError("protocol_error", `${call.method} 收到看不懂的回应:${line}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
throw new NapboxError("daemon_unavailable", `napboxd 没给最后的结果就断开了(${this.socketPath}),这次操作不一定没做,用 status 查当前状态`);
|
|
182
|
+
}
|
|
183
|
+
catch (error) {
|
|
184
|
+
// abort 是自己断开的,读到的断开不算 napboxd 的问题;标准输入出错同理
|
|
185
|
+
signal?.throwIfAborted();
|
|
186
|
+
if (inputFailure)
|
|
187
|
+
throw inputFailure.error;
|
|
188
|
+
throw error;
|
|
189
|
+
}
|
|
190
|
+
finally {
|
|
191
|
+
signal?.removeEventListener("abort", abort);
|
|
192
|
+
socket.destroy();
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function connect(socketPath) {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const socket = createConnection(socketPath);
|
|
199
|
+
socket.once("error", (error) => {
|
|
200
|
+
reject(new NapboxError("daemon_unavailable", `连不上 napboxd(${socketPath}:${error.message})。客户端不会启动它,它由 napbox setup 注册的登录项运行。`));
|
|
201
|
+
});
|
|
202
|
+
socket.once("connect", () => {
|
|
203
|
+
socket.removeAllListeners("error");
|
|
204
|
+
// 之后的错误由 readLines 读到;留一个监听,免得没有人在读的时候未处理的 error 事件带崩进程
|
|
205
|
+
socket.on("error", () => { });
|
|
206
|
+
resolve(socket);
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
/** 按行读。napboxd 突然断开(比如 ECONNRESET)和读到结尾一样处理:没有下一行了,由调用的地方报 */
|
|
211
|
+
async function* readLines(socket, socketPath) {
|
|
212
|
+
socket.setEncoding("utf8"); // 多字节字符切在两块数据之间时由它拼好
|
|
213
|
+
let buffer = "";
|
|
214
|
+
try {
|
|
215
|
+
for await (const chunk of socket) {
|
|
216
|
+
buffer += chunk;
|
|
217
|
+
let end = buffer.indexOf("\n");
|
|
218
|
+
while (end >= 0) {
|
|
219
|
+
yield buffer.slice(0, end);
|
|
220
|
+
buffer = buffer.slice(end + 1);
|
|
221
|
+
end = buffer.indexOf("\n");
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
catch (error) {
|
|
226
|
+
throw new NapboxError("daemon_unavailable", `和 napboxd 的连接断了(${socketPath}:${error.message}),这次操作不一定没做,用 status 查当前状态`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
function parseReply(line) {
|
|
230
|
+
let value;
|
|
231
|
+
try {
|
|
232
|
+
value = JSON.parse(line);
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
throw new NapboxError("protocol_error", `napboxd 的回应不是 JSON:${line}`);
|
|
236
|
+
}
|
|
237
|
+
const reply = checked(Response, value);
|
|
238
|
+
// id 0 是 napboxd 解析不了请求时的回应(Daemon.respond)
|
|
239
|
+
if (reply.id !== REQUEST_ID && !(reply.id === 0 && reply.error)) {
|
|
240
|
+
throw new NapboxError("protocol_error", `napboxd 回应的 id 是 ${reply.id},请求的是 ${REQUEST_ID}`);
|
|
241
|
+
}
|
|
242
|
+
return reply;
|
|
243
|
+
}
|
|
244
|
+
function checked(schema, value) {
|
|
245
|
+
const parsed = schema.safeParse(value);
|
|
246
|
+
if (parsed.success)
|
|
247
|
+
return parsed.data;
|
|
248
|
+
throw new NapboxError("protocol_error", `napboxd 的回应对不上,可能是客户端和 napboxd 版本差太多:${parsed.error.message}`);
|
|
249
|
+
}
|
|
250
|
+
async function sendStdin(stdin, write) {
|
|
251
|
+
const chunks = stdin === undefined ? [] : isSingleChunk(stdin) ? [stdin] : stdin;
|
|
252
|
+
for await (const chunk of chunks) {
|
|
253
|
+
const data = Buffer.from(chunk).toString("base64");
|
|
254
|
+
if (!(await write({ data })))
|
|
255
|
+
return; // 命令已经结束了
|
|
256
|
+
}
|
|
257
|
+
await write({ end: true });
|
|
258
|
+
}
|
|
259
|
+
function isSingleChunk(stdin) {
|
|
260
|
+
return typeof stdin === "string" || stdin instanceof Uint8Array;
|
|
261
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { type CreateOptions, defaultSocketPath, type ExecOptions, type LogsOptions, NapboxClient, NapboxError, type Output, type RunOptions, type SubscribeOptions, type UpOptions, } from "./client.ts";
|
|
2
|
+
export { type DownResult, type EgressMode, type EnvironmentEvent, type EnvironmentInfo, type EnvironmentState, type EnvironmentTarget, type ErrorCode, errorCodes, type EventKind, type ExecResult, type ImageImportResult, type LogsResult, type OutputStream, type PortInfo, type Readiness, type RunPurpose, type RunRecord, type RunsResult, type ServiceExit, type ServiceExitRestart, type ServiceRun, type SleepReason, type StatusResult, } from "./protocol.ts";
|
package/dist/index.js
ADDED