@lengmoxxl/sdk 0.1.4 → 0.1.5
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 +136 -38
- package/dist/connection.d.ts +6 -1
- package/dist/index.cjs +101 -2
- package/dist/index.d.ts +2 -1
- package/dist/process.d.ts +19 -0
- package/dist/types.d.ts +21 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,54 +1,152 @@
|
|
|
1
|
-
# @lengmoxxl/sdk
|
|
2
|
-
The client SDK for [WangCai](https://github.com/lengmoXXL/WangCai), the terminal app. It reaches a machine, the one
|
|
3
|
-
the app runs on or one over SSH, and drives its terminals, files and subprocesses through a single connection.
|
|
1
|
+
# @lengmoxxl/sdk:接口参考
|
|
4
2
|
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
本参考对应 SDK `0.1.5`:连接本机或 SSH 机器上的旺财后台进程,提供终端、文件与子进程
|
|
4
|
+
操作。插件入口与生命周期见 [插件开发手册](../docs/plugins.md),进程关系见
|
|
5
|
+
[架构说明](../docs/architecture.md)。
|
|
7
6
|
|
|
8
|
-
##
|
|
7
|
+
## 插件上下文
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
| 成员 | 说明 |
|
|
10
|
+
| --- | --- |
|
|
11
|
+
| `global` | 公共 `Bus`,跨插件与前后端通信 |
|
|
12
|
+
| `ui.handle<T>(method, handler)` | 后端注册方法,返回取消注册的 `Dispose` |
|
|
13
|
+
| `ui.publish(topic, data)` | 后端向本插件 UI 发事件,对应前端 `ui.subscribe` |
|
|
14
|
+
| `ui.subscribe<T>(topic, callback)` | 前端订阅本插件后端事件,返回取消订阅的函数 |
|
|
15
|
+
| `ui.request<T>(method, params?)` | 前端调用本插件后端方法,返回 `Promise<T>` |
|
|
16
|
+
| `connect(machine, signal?)` | 后端借用机器连接:`{ host?: string }`,可带 `AbortSignal` |
|
|
17
|
+
| `host.dataDirectory` / `host.config` | 插件私有数据目录 / 本插件设置 |
|
|
18
|
+
| `host.tabs(options)` / `host.open(url)` | 前端开侧栏标签页 / 用系统浏览器打开 http、https |
|
|
19
|
+
| `host.preview` | `{ url, message }`,HTML 预览 frame 的地址与握手消息 |
|
|
13
20
|
|
|
14
|
-
|
|
21
|
+
请求与事件传可结构化克隆的数据,不传函数、DOM、Pty 或连接实例。
|
|
15
22
|
|
|
16
|
-
|
|
23
|
+
`ui.handle` 注册的方法名不能含 `:`;`ui.publish` 与 `ui.subscribe` 的主题由宿主加插件 id 前缀,
|
|
24
|
+
`global` 用原始主题,自定义主题要自己加命名空间(如 `directory-demo:opened`)。
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
import { openMachine } from '@lengmoxxl/sdk';
|
|
26
|
+
`connect({})` 连接本机,`connect({ host })` 连接 SSH。
|
|
20
27
|
|
|
21
|
-
|
|
28
|
+
宿主为相同机器复用连接并计数,每次取得连接对应一次 `disconnect()`。
|
|
22
29
|
|
|
23
|
-
|
|
24
|
-
const session = await connection.pty.create({ rows: 24, cols: 80 }, '/repo');
|
|
25
|
-
const terminal = await connection.pty.attach(session.id);
|
|
26
|
-
terminal.onData(({ data }) => process.stdout.write(data));
|
|
27
|
-
await terminal.write('git status\n');
|
|
30
|
+
`Dispose = () => void | Promise<void>`,卸载时清理资源,不要关闭用户仍要保留的 PTY。
|
|
28
31
|
|
|
29
|
-
|
|
30
|
-
connection.disconnect();
|
|
31
|
-
```
|
|
32
|
+
`Bus`:`publish(topic, data): Promise<void>`、`subscribe<T>(topic, callback): () => void`。
|
|
32
33
|
|
|
33
|
-
`
|
|
34
|
-
connection is reference counted, so callers that hand the same one around keep it alive until the last
|
|
35
|
-
`disconnect()`. `openMachine({ type: 'ssh', host, agent })` calls `agent(host)` before it connects: putting
|
|
36
|
-
the carrier agent on the host belongs to the app that embeds the SDK.
|
|
34
|
+
`host.dataDirectory` 默认 `~/.local/share/wangcai/data/<plugin-id>`。
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
- `connection.subprocess` - `exec`, which returns `{ stdout, stderr, code }` as `Uint8Array`
|
|
41
|
-
- `connection.onState` - connection status and the sessions it can see
|
|
36
|
+
`host.config` 是浅合并,避免把自定义字段命名为 `theme`,配色键见
|
|
37
|
+
[README 主题参数](../README.md#主题参数)。
|
|
42
38
|
|
|
43
|
-
|
|
44
|
-
types) lives in the `@lengmoxxl/sdk/channel` entry. That entry is types only.
|
|
39
|
+
## 标签页
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
`host.tabs({ id, title, tooltip?, workspaceId?, onClose?, mount })`:`id`、`title` 必填,
|
|
42
|
+
`tooltip?` 是标签上的悬停提示(缺省用 `title`),`workspaceId` 缺省用当前工作区;
|
|
43
|
+
`mount(container)` 返回的 `TabContent.dispose` 必填,`onSelect?` 在选中、工作区刷新或侧栏重新显示时
|
|
44
|
+
都可能调用,`onClose?` 在移除标签页时先于 `dispose` 调用。
|
|
47
45
|
|
|
46
|
+
相同插件 id、工作区 id、标签 id 再次调用 `host.tabs` 会复用并选中;尚无工作区时打开的标签页
|
|
47
|
+
会在第一个工作区激活时绑定到它。
|
|
48
|
+
|
|
49
|
+
`TabRecord = { plugin, id, workspaceId? }` 只保存身份,插件的 `open(context, record)` 据此恢复
|
|
50
|
+
内容,资源必须仍在。
|
|
51
|
+
|
|
52
|
+
## 预览
|
|
53
|
+
|
|
54
|
+
用 `host.preview.url` 建 iframe,设 `sandbox="allow-scripts"`,不要加
|
|
55
|
+
`allow-same-origin`;收到来源为该 frame、`event.data === host.preview.message` 的消息后
|
|
56
|
+
`postMessage(html, '*')`。预览会执行其中脚本。
|
|
57
|
+
|
|
58
|
+
## 插件配置
|
|
59
|
+
|
|
60
|
+
插件 `config` 是嵌套对象,叶子声明 `type` 与可选 `default`;宿主只保留 schema 声明的键,
|
|
61
|
+
缺失或类型不符时取 default,`array` 内部条目由插件自己验证。
|
|
62
|
+
|
|
63
|
+
## 工作区协议
|
|
64
|
+
|
|
65
|
+
放入 init.ts `workspaces` 的插件用 `ui.handle` 提供:`workspaces()`(`WorkspaceRow[]`,
|
|
66
|
+
按显示顺序)、`workspace-menu()`(`WorkspaceMenuItem[]`)、`workspace-create({ key })`
|
|
67
|
+
(返回 `{ id }`)、`workspace-select({ id })`、`workspace-close({ id })`、
|
|
68
|
+
`workspace-move({ id, before? })`(`before` 是目标工作区 id,不是索引;省略为末尾,且只
|
|
69
|
+
允许同一 provider 内拖动)。
|
|
70
|
+
|
|
71
|
+
provider 要保证工作区 id 在所有 provider 的行中唯一识别;`error` 由 provider 给出并在重试成功后清除,
|
|
72
|
+
宿主不禁用带 `error` 的菜单项。
|
|
73
|
+
|
|
74
|
+
约定的公共主题:`workspaces`(行变化,App 重新请求列表)、`workspace:active`
|
|
75
|
+
(`WorkspaceActive | null`,provider 发布)、`workspace:query`(视图请求 provider 再发布
|
|
76
|
+
当前工作区)、`onclick`(`FileClick`)。
|
|
77
|
+
|
|
78
|
+
字段类型见 SDK 导出的 `WorkspaceRow`、`WorkspaceMenuItem`、`Machine`、`WorkspaceActive`
|
|
79
|
+
与 `FileClick`,定义在 `sdk/types.ts`。
|
|
80
|
+
|
|
81
|
+
## 机器、终端与子进程
|
|
82
|
+
|
|
83
|
+
`MachineConnection`:`ready`(首次连接就绪)、`state`、`port`(本地 loopback 端口,SSH 时
|
|
84
|
+
为转发端口)、`onState(callback)`、`retain()`(连接层使用)、`disconnect()`,以及 `pty`、`fs`、
|
|
85
|
+
`subprocess`。
|
|
86
|
+
|
|
87
|
+
`MachineState` 含 `status`、`error?`、`sessions`、`generation`。
|
|
88
|
+
|
|
89
|
+
断线会自动重连并重新 attach 已挂载的终端与进程,失败的请求与输入不自动重放,后台进程重启时旧 Pty 与进程失效。
|
|
90
|
+
|
|
91
|
+
`connection.pty`:`list()`、`cwd(id)`、`create(size?, cwd?)`(默认 24 行 80 列;cwd 用绝对目录,
|
|
92
|
+
省略时在后台进程的 HOME 下启动)、`attach(id)`(复用句柄)、`close(id)`(结束 shell 与后台子孙进程)。
|
|
93
|
+
|
|
94
|
+
`Size` 用整数:rows 2–300,cols 2–500。
|
|
95
|
+
|
|
96
|
+
`Session` 有 `id, title`、`pid, rows, cols`、`exit_code: number | null`。
|
|
97
|
+
|
|
98
|
+
`Pty`:`id`、`onSnapshot(callback)`、`onData(callback)`、`write(data)`、`resize(size)`、
|
|
99
|
+
`detach()`(停止挂载,不关闭 shell)。
|
|
100
|
+
|
|
101
|
+
`Output = { data: Uint8Array; seq: number }`,`Snapshot` 额外有 `rows, cols`。
|
|
102
|
+
|
|
103
|
+
字节含终端控制序列,交给终端模拟器,不要把快照当键盘输入重发。
|
|
104
|
+
|
|
105
|
+
`receive` 与 `release(error?)` 由连接层使用。
|
|
106
|
+
|
|
107
|
+
`connection.fs`:`stat(path)` 返回 `{ isDirectory: boolean } | null`、`readDirectory(path)` 返回
|
|
108
|
+
`DirectoryEntry[]`(`{ name, isDirectory }`)、`readFile(path)` 返回原始字节。
|
|
109
|
+
|
|
110
|
+
路径为绝对路径,`readFile` 上限 16 MiB;SDK 不提供写入、删除或上传接口。
|
|
111
|
+
|
|
112
|
+
`connection.subprocess.exec(program, args, options)`:`args` 是独立 argv(不经 shell 展开),
|
|
113
|
+
`options.cwd` 是绝对工作目录,`options.env` 覆盖继承的环境变量;返回
|
|
114
|
+
`{ stdout: Uint8Array; stderr: Uint8Array; code: number }`。非零退出码仍返回结果,无法启动、
|
|
115
|
+
超时、输出超限或被信号终止时 reject;stdin 关闭,长时间或交互用 `subprocess.spawn` 或 PTY。
|
|
116
|
+
|
|
117
|
+
执行上限 15 秒,SDK 等待响应上限 20 秒,stdout 与 stderr 各最多 8 MiB,断线取消未完成的 exec。
|
|
118
|
+
|
|
119
|
+
`connection.subprocess.spawn(program, args, options)`(`options` 同 `exec`)启动一个
|
|
120
|
+
长期存活的进程,stdin、stdout、stderr 都是管道,返回 `ProcessInfo = { id, pid, exit_code }`;
|
|
121
|
+
`attach(id)` 复用句柄(未 attach 前不接收输出),`close(id)` 结束进程及其进程组;进程归后台进程所有,不 `close` 就一直留着。
|
|
122
|
+
|
|
123
|
+
`Process`:`id`、`onData(callback)`、`onExit(callback)`、`write(data)`。
|
|
124
|
+
|
|
125
|
+
`ProcessOutput = { data: Uint8Array; seq: number; stream: 'stdout' | 'stderr' }`,两路输出各自按序到达,
|
|
126
|
+
`seq` 单调递增,跨两路的先后以 `seq` 为准。
|
|
127
|
+
|
|
128
|
+
`onExit` 给出退出码(被信号结束时按 shell 的 128+signal),节点来不及投递时给出 `undefined`,此时
|
|
129
|
+
输出流已不可信,进程可能仍在运行,请重新 `spawn`。
|
|
130
|
+
|
|
131
|
+
`write` 按 32 KiB 分片、按序应用,进程退出后 reject;先 `attach` 再写。
|
|
132
|
+
|
|
133
|
+
`receive` 与 `release(error?)` 由连接层使用。
|
|
134
|
+
|
|
135
|
+
## 独立 Node 客户端
|
|
136
|
+
|
|
137
|
+
App 插件用 `context.connect`;插件之外才调用 `openMachine`:
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { openMachine } from '@lengmoxxl/sdk';
|
|
141
|
+
|
|
142
|
+
const connection = await openMachine({ type: 'local', binary: '/path/to/wangcai' });
|
|
143
|
+
await connection.fs.readDirectory('/');
|
|
144
|
+
connection.disconnect();
|
|
48
145
|
```
|
|
49
|
-
npm run build
|
|
50
|
-
```
|
|
51
146
|
|
|
52
|
-
|
|
53
|
-
`
|
|
54
|
-
`
|
|
147
|
+
`openMachine` 返回 `Promise<MachineConnection>`。`type` 为 `'local'` 或 `'ssh'`;local 可给
|
|
148
|
+
`binary`,ssh 必须给 `host`(系统 ssh 目标或别名)与可选 `agent`(`InstallAgent`),两者
|
|
149
|
+
都可给 `signal`。不传 `agent` 时 SDK 不下载二进制;连接用 BatchMode SSH。
|
|
150
|
+
|
|
151
|
+
`agentCommand(command)` 返回拼接后的 shell 命令:在 PATH 前加入远端 `$HOME/.local/bin`,
|
|
152
|
+
并携带当前进程的 WANGCAI_HOME(会转义;command 本身不转义,只传可信命令)。
|
package/dist/connection.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { ConnectionOptions, MachineState, Session, Size, DirectoryEntry, ExecOptions, ExecResult } from './types';
|
|
1
|
+
import type { ConnectionOptions, MachineState, Session, Size, DirectoryEntry, ExecOptions, ExecResult, ProcessInfo } from './types';
|
|
2
|
+
import { Process } from './process';
|
|
2
3
|
import { Pty } from './pty';
|
|
3
4
|
export declare class MachineConnection {
|
|
4
5
|
private options;
|
|
5
6
|
private listeners;
|
|
6
7
|
private terminals;
|
|
8
|
+
private processes;
|
|
7
9
|
port?: number;
|
|
8
10
|
private instance?;
|
|
9
11
|
readonly ready: Promise<void>;
|
|
@@ -42,6 +44,9 @@ export declare class MachineConnection {
|
|
|
42
44
|
};
|
|
43
45
|
subprocess: {
|
|
44
46
|
exec: (program: string, args: string[], options: ExecOptions) => Promise<ExecResult>;
|
|
47
|
+
spawn: (program: string, args: string[], options: ExecOptions) => Promise<ProcessInfo>;
|
|
48
|
+
attach: (id: string) => Promise<Process>;
|
|
49
|
+
close: (id: string) => Promise<void>;
|
|
45
50
|
};
|
|
46
51
|
private cleanup;
|
|
47
52
|
private fail;
|
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,68 @@ var import_node_net = require("node:net");
|
|
|
41
41
|
var import_node_util = require("node:util");
|
|
42
42
|
var import_ws = __toESM(require("ws"));
|
|
43
43
|
|
|
44
|
+
// process.ts
|
|
45
|
+
var CHUNK = 32 * 1024;
|
|
46
|
+
var Process = class {
|
|
47
|
+
constructor(id, request) {
|
|
48
|
+
this.id = id;
|
|
49
|
+
this.request = request;
|
|
50
|
+
}
|
|
51
|
+
sequence = -1;
|
|
52
|
+
error;
|
|
53
|
+
ended = false;
|
|
54
|
+
code;
|
|
55
|
+
buffered = [];
|
|
56
|
+
outputs = /* @__PURE__ */ new Set();
|
|
57
|
+
exits = /* @__PURE__ */ new Set();
|
|
58
|
+
receive(event) {
|
|
59
|
+
if (this.error) return;
|
|
60
|
+
if (event.event === "exit" || event.event === "lost") {
|
|
61
|
+
this.ended = true;
|
|
62
|
+
this.code = event.event === "exit" ? event.code : void 0;
|
|
63
|
+
for (const callback of this.exits) callback(this.code);
|
|
64
|
+
this.exits.clear();
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (event.seq <= this.sequence) return;
|
|
68
|
+
this.sequence = event.seq;
|
|
69
|
+
const output = { data: event.data, seq: event.seq, stream: event.event };
|
|
70
|
+
if (!this.outputs.size) this.buffered.push(output);
|
|
71
|
+
for (const callback of this.outputs) callback(output);
|
|
72
|
+
}
|
|
73
|
+
onData(callback) {
|
|
74
|
+
this.outputs.add(callback);
|
|
75
|
+
for (const event of this.buffered) callback(event);
|
|
76
|
+
this.buffered = [];
|
|
77
|
+
return () => {
|
|
78
|
+
this.outputs.delete(callback);
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
/** Its exit code, or nothing when the node lost the stream and the process may still be running. */
|
|
82
|
+
onExit(callback) {
|
|
83
|
+
this.exits.add(callback);
|
|
84
|
+
if (this.ended) callback(this.code);
|
|
85
|
+
return () => {
|
|
86
|
+
this.exits.delete(callback);
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async write(data) {
|
|
90
|
+
if (this.error) throw this.error;
|
|
91
|
+
const requests = [];
|
|
92
|
+
for (let offset = 0; offset < data.length; offset += CHUNK) {
|
|
93
|
+
const chunk = Buffer.from(data.subarray(offset, offset + CHUNK)).toString("base64");
|
|
94
|
+
requests.push(this.request("process_write", { session_id: this.id, data: chunk }));
|
|
95
|
+
}
|
|
96
|
+
await Promise.all(requests);
|
|
97
|
+
}
|
|
98
|
+
release(error = new Error("Process detached")) {
|
|
99
|
+
this.error = error;
|
|
100
|
+
this.outputs.clear();
|
|
101
|
+
this.exits.clear();
|
|
102
|
+
this.buffered = [];
|
|
103
|
+
}
|
|
104
|
+
};
|
|
105
|
+
|
|
44
106
|
// pty.ts
|
|
45
107
|
var Pty = class {
|
|
46
108
|
constructor(id, request, remove) {
|
|
@@ -132,6 +194,7 @@ var MachineConnection = class {
|
|
|
132
194
|
}
|
|
133
195
|
listeners = /* @__PURE__ */ new Set();
|
|
134
196
|
terminals = /* @__PURE__ */ new Map();
|
|
197
|
+
processes = /* @__PURE__ */ new Map();
|
|
135
198
|
// Where the daemon listens: the window reaches it with this.
|
|
136
199
|
port;
|
|
137
200
|
instance;
|
|
@@ -174,6 +237,8 @@ var MachineConnection = class {
|
|
|
174
237
|
this.rejectReady(new Error("Connection cancelled"));
|
|
175
238
|
for (const terminal of this.terminals.values()) terminal.release();
|
|
176
239
|
this.terminals.clear();
|
|
240
|
+
for (const process2 of this.processes.values()) process2.release();
|
|
241
|
+
this.processes.clear();
|
|
177
242
|
this.state.status = "disconnected";
|
|
178
243
|
this.state.error = void 0;
|
|
179
244
|
this.publish();
|
|
@@ -217,6 +282,26 @@ var MachineConnection = class {
|
|
|
217
282
|
exec: async (program, args, options) => {
|
|
218
283
|
const result = await this.request("exec", { program, args, ...options }, 2e4);
|
|
219
284
|
return { stdout: Buffer.from(result.stdout, "base64"), stderr: Buffer.from(result.stderr, "base64"), code: result.code };
|
|
285
|
+
},
|
|
286
|
+
spawn: (program, args, options) => this.request("spawn", { program, args, ...options }),
|
|
287
|
+
attach: async (id) => {
|
|
288
|
+
const existing = this.processes.get(id);
|
|
289
|
+
if (existing) return existing;
|
|
290
|
+
const process2 = new Process(id, (op, params) => this.request(op, params));
|
|
291
|
+
this.processes.set(id, process2);
|
|
292
|
+
try {
|
|
293
|
+
await this.request("process_attach", { session_id: id });
|
|
294
|
+
} catch (error) {
|
|
295
|
+
this.processes.delete(id);
|
|
296
|
+
process2.release();
|
|
297
|
+
throw error;
|
|
298
|
+
}
|
|
299
|
+
return process2;
|
|
300
|
+
},
|
|
301
|
+
close: async (id) => {
|
|
302
|
+
await this.request("process_close", { session_id: id });
|
|
303
|
+
this.processes.get(id)?.release();
|
|
304
|
+
this.processes.delete(id);
|
|
220
305
|
}
|
|
221
306
|
};
|
|
222
307
|
cleanup() {
|
|
@@ -285,9 +370,12 @@ var MachineConnection = class {
|
|
|
285
370
|
if (binary) {
|
|
286
371
|
const bytes = data;
|
|
287
372
|
const length = bytes.readUInt32BE(0);
|
|
288
|
-
if (length > bytes.length - 4) throw new Error("Invalid
|
|
373
|
+
if (length > bytes.length - 4) throw new Error("Invalid packet");
|
|
289
374
|
const header = JSON.parse(bytes.subarray(4, 4 + length).toString());
|
|
290
|
-
|
|
375
|
+
const event = { ...header, data: bytes.subarray(4 + length) };
|
|
376
|
+
const terminal = this.terminals.get(header.session_id);
|
|
377
|
+
if (terminal) terminal.receive(event);
|
|
378
|
+
else this.processes.get(header.session_id)?.receive(event);
|
|
291
379
|
} else {
|
|
292
380
|
const message = JSON.parse(data.toString());
|
|
293
381
|
if (message.id) {
|
|
@@ -326,6 +414,8 @@ var MachineConnection = class {
|
|
|
326
414
|
if (this.instance && this.instance !== actual.instance_id) {
|
|
327
415
|
for (const terminal of this.terminals.values()) terminal.release(new Error("Node restarted; terminal no longer exists"));
|
|
328
416
|
this.terminals.clear();
|
|
417
|
+
for (const process2 of this.processes.values()) process2.release(new Error("Node restarted; process no longer exists"));
|
|
418
|
+
this.processes.clear();
|
|
329
419
|
}
|
|
330
420
|
this.instance = actual.instance_id;
|
|
331
421
|
for (const [id, terminal] of this.terminals) {
|
|
@@ -337,6 +427,15 @@ var MachineConnection = class {
|
|
|
337
427
|
this.terminals.delete(id);
|
|
338
428
|
}
|
|
339
429
|
}
|
|
430
|
+
for (const [id, process2] of this.processes) {
|
|
431
|
+
try {
|
|
432
|
+
await this.request("process_attach", { session_id: id });
|
|
433
|
+
} catch (error) {
|
|
434
|
+
if (!current()) return;
|
|
435
|
+
process2.release(error);
|
|
436
|
+
this.processes.delete(id);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
340
439
|
if (!current()) return;
|
|
341
440
|
this.state.status = "connected";
|
|
342
441
|
this.state.error = void 0;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { MachineConnection } from './connection';
|
|
2
2
|
import type { ConnectionOptions } from './types';
|
|
3
3
|
export { agentCommand } from './agent';
|
|
4
|
-
export type { ConnectionOptions, FileClick, Machine, MachineState, Session, Size, Output, Snapshot, TerminalEvent, DirectoryEntry, ExecOptions, ExecResult, InstallAgent, Profile, Theme, WorkspaceActive, WorkspaceRow, WorkspaceMenuItem } from './types';
|
|
4
|
+
export type { ConnectionOptions, FileClick, Machine, MachineState, Session, Size, Output, Snapshot, TerminalEvent, DirectoryEntry, ExecOptions, ExecResult, ProcessEvent, ProcessInfo, ProcessOutput, InstallAgent, Profile, Theme, WorkspaceActive, WorkspaceRow, WorkspaceMenuItem } from './types';
|
|
5
5
|
export type { MachineConnection } from './connection';
|
|
6
|
+
export type { Process } from './process';
|
|
6
7
|
export type { Pty } from './pty';
|
|
7
8
|
/** Opens one connection to a machine and waits for it to be ready. */
|
|
8
9
|
export declare function openMachine(options: ConnectionOptions): Promise<MachineConnection>;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ProcessEvent, ProcessOutput } from './types';
|
|
2
|
+
export declare class Process {
|
|
3
|
+
readonly id: string;
|
|
4
|
+
private request;
|
|
5
|
+
private sequence;
|
|
6
|
+
private error?;
|
|
7
|
+
private ended;
|
|
8
|
+
private code?;
|
|
9
|
+
private buffered;
|
|
10
|
+
private outputs;
|
|
11
|
+
private exits;
|
|
12
|
+
constructor(id: string, request: (op: string, params: Record<string, unknown>) => Promise<unknown>);
|
|
13
|
+
receive(event: ProcessEvent): void;
|
|
14
|
+
onData(callback: (event: ProcessOutput) => void): () => void;
|
|
15
|
+
/** Its exit code, or nothing when the node lost the stream and the process may still be running. */
|
|
16
|
+
onExit(callback: (code?: number) => void): () => void;
|
|
17
|
+
write(data: Uint8Array): Promise<void>;
|
|
18
|
+
release(error?: Error): void;
|
|
19
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type InstallAgent = (host: string, signal?: AbortSignal) => Promise<void>
|
|
|
6
6
|
*
|
|
7
7
|
* workspaces() -> WorkspaceRow[] one flat row per workspace, in draw order
|
|
8
8
|
* workspace-menu() -> WorkspaceMenuItem[] what the menu offers to open; an entry that carries
|
|
9
|
-
* an error stays listed
|
|
9
|
+
* an error stays listed with its reason, and a click retries it
|
|
10
10
|
* workspace-create({ key }) -> { id: string } the id of the workspace it made
|
|
11
11
|
* workspace-select({ id })
|
|
12
12
|
* workspace-close({ id })
|
|
@@ -88,6 +88,26 @@ export type TerminalEvent = ({
|
|
|
88
88
|
} & Output) & {
|
|
89
89
|
session_id: string;
|
|
90
90
|
};
|
|
91
|
+
export interface ProcessInfo {
|
|
92
|
+
id: string;
|
|
93
|
+
pid: number;
|
|
94
|
+
exit_code: number | null;
|
|
95
|
+
}
|
|
96
|
+
/** `stream` names the pipe the bytes came from. */
|
|
97
|
+
export interface ProcessOutput extends Output {
|
|
98
|
+
stream: 'stdout' | 'stderr';
|
|
99
|
+
}
|
|
100
|
+
export type ProcessEvent = ({
|
|
101
|
+
event: 'stdout' | 'stderr';
|
|
102
|
+
} & Output | {
|
|
103
|
+
event: 'exit';
|
|
104
|
+
seq: number;
|
|
105
|
+
code: number;
|
|
106
|
+
} | {
|
|
107
|
+
event: 'lost';
|
|
108
|
+
}) & {
|
|
109
|
+
session_id: string;
|
|
110
|
+
};
|
|
91
111
|
export interface DirectoryEntry {
|
|
92
112
|
name: string;
|
|
93
113
|
isDirectory: boolean;
|