@lengmoxxl/sdk 0.1.6 → 0.1.7

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 CHANGED
@@ -1,8 +1,7 @@
1
1
  # @lengmoxxl/sdk:接口参考
2
2
 
3
- 本参考对应 SDK `0.1.5`:连接本机或 SSH 机器上的旺财后台进程,提供终端、文件与子进程
4
- 操作。插件入口与生命周期见 [插件开发手册](../docs/plugins.md),进程关系见
5
- [架构说明](../docs/architecture.md)。
3
+ SDK 连接本机或 SSH 机器上的旺财后台进程,提供终端、文件与子进程操作。插件入口与生命周期见
4
+ [插件开发手册](../docs/plugins.md),进程关系见 [架构说明](../docs/architecture.md)。
6
5
 
7
6
  ## 插件上下文
8
7
 
@@ -87,7 +86,8 @@ provider 要保证工作区 id 在所有 provider 的行中唯一识别;`error
87
86
 
88
87
  `MachineState` 含 `status`、`error?`、`sessions`、`generation`。
89
88
 
90
- 断线会自动重连并重新 attach 已挂载的终端与进程,失败的请求与输入不自动重放,后台进程重启时旧 Pty 与进程失效。
89
+ 断线会自动重连并重新 attach 已挂载的终端、进程与文件订阅,失败的请求与输入不自动重放,后台进程重启时
90
+ 旧 Pty 与进程失效。
91
91
 
92
92
  `connection.pty`:`list()`、`cwd(id)`、`create(size?, cwd?)`(默认 24 行 80 列;cwd 用绝对目录,
93
93
  省略时在后台进程的 HOME 下启动)、`attach(id)`(复用句柄)、`close(id)`(结束 shell 与后台子孙进程)。
@@ -110,6 +110,12 @@ provider 要保证工作区 id 在所有 provider 的行中唯一识别;`error
110
110
 
111
111
  路径为绝对路径,`readFile` 上限 16 MiB;SDK 不提供写入、删除或上传接口。
112
112
 
113
+ `connection.fs.watch(path, options, callback)` 订阅该路径的变化,`options.recursive` 决定是否含子目录,
114
+ 返回的 `() => void` 关闭订阅。回调收到的是 `FileChanges = { created, modified, removed }`,一个安静窗口
115
+ (约 200ms 没新事件,最长 2 秒)一批,路径按订阅时给的根路径书写;连接彻底关闭(最后一个持有者
116
+ `disconnect()`)时一并结束。macOS 走 FSEvents,创建与删除多数时候报成 `modified`;Linux 走 inotify,
117
+ 三类齐全。
118
+
113
119
  `connection.subprocess.exec(program, args, options)`:`args` 是独立 argv(不经 shell 展开),
114
120
  `options.cwd` 是绝对工作目录,`options.env` 覆盖继承的环境变量;返回
115
121
  `{ stdout: Uint8Array; stderr: Uint8Array; code: number }`。非零退出码仍返回结果,无法启动、
@@ -119,7 +125,7 @@ provider 要保证工作区 id 在所有 provider 的行中唯一识别;`error
119
125
 
120
126
  `connection.subprocess.spawn(program, args, options)`(`options` 同 `exec`)启动一个
121
127
  长期存活的进程,stdin、stdout、stderr 都是管道,返回 `ProcessInfo = { id, pid, exit_code }`;
122
- `attach(id)` 复用句柄(未 attach 前不接收输出),`close(id)` 结束进程及其进程组;进程归后台进程所有,不 `close` 就一直留着。
128
+ `attach(id)` 复用句柄(未 attach 前不接收输出),`close(id)` 结束进程及其进程组;进程归后台进程所有,除 `close` 外,只有起它或最后 `attach` 它的那条连接断开后 60 秒内没有连接再 `attach`,节点才会自己收回。
123
129
 
124
130
  `Process`:`id`、`onData(callback)`、`onExit(callback)`、`write(data)`。
125
131
 
@@ -1,4 +1,4 @@
1
- import type { ConnectionOptions, MachineState, Session, Size, DirectoryEntry, ExecOptions, ExecResult, ProcessInfo } from './types';
1
+ import type { ConnectionOptions, MachineState, Session, Size, DirectoryEntry, ExecOptions, ExecResult, ProcessInfo, FileChanges, WatchOptions } from './types';
2
2
  import { Process } from './process';
3
3
  import { Pty } from './pty';
4
4
  export declare class MachineConnection {
@@ -6,6 +6,7 @@ export declare class MachineConnection {
6
6
  private listeners;
7
7
  private terminals;
8
8
  private processes;
9
+ private watchers;
9
10
  port?: number;
10
11
  private instance?;
11
12
  readonly ready: Promise<void>;
@@ -41,6 +42,7 @@ export declare class MachineConnection {
41
42
  } | null>;
42
43
  readDirectory: (path: string) => Promise<DirectoryEntry[]>;
43
44
  readFile: (path: string) => Promise<Uint8Array>;
45
+ watch: (path: string, options: WatchOptions, callback: (changes: FileChanges) => void) => Promise<() => void>;
44
46
  };
45
47
  subprocess: {
46
48
  exec: (program: string, args: string[], options: ExecOptions) => Promise<ExecResult>;
package/dist/index.cjs CHANGED
@@ -206,6 +206,7 @@ var MachineConnection = class {
206
206
  listeners = /* @__PURE__ */ new Set();
207
207
  terminals = /* @__PURE__ */ new Map();
208
208
  processes = /* @__PURE__ */ new Map();
209
+ watchers = /* @__PURE__ */ new Map();
209
210
  // Where the daemon listens: the window reaches it with this.
210
211
  port;
211
212
  instance;
@@ -250,6 +251,7 @@ var MachineConnection = class {
250
251
  this.terminals.clear();
251
252
  for (const process2 of this.processes.values()) process2.release();
252
253
  this.processes.clear();
254
+ this.watchers.clear();
253
255
  this.state.status = "disconnected";
254
256
  this.state.error = void 0;
255
257
  this.publish();
@@ -287,6 +289,22 @@ var MachineConnection = class {
287
289
  readFile: async (path) => {
288
290
  const result = await this.request("read_file", { path });
289
291
  return Buffer.from(result.data, "base64");
292
+ },
293
+ watch: async (path, options, callback) => {
294
+ const watchId = `watch-${++this.serial}`;
295
+ const recursive = options.recursive ?? false;
296
+ this.watchers.set(watchId, { recursive, path, callback });
297
+ try {
298
+ await this.request("watch", { watch_id: watchId, path, recursive });
299
+ } catch (error) {
300
+ this.watchers.delete(watchId);
301
+ throw error;
302
+ }
303
+ return () => {
304
+ this.watchers.delete(watchId);
305
+ void this.request("unwatch", { watch_id: watchId }).catch(() => {
306
+ });
307
+ };
290
308
  }
291
309
  };
292
310
  subprocess = {
@@ -403,6 +421,9 @@ var MachineConnection = class {
403
421
  } else if (message.event === "sessions_changed") {
404
422
  void this.refresh().catch(() => {
405
423
  });
424
+ } else if (message.event === "fs_changed") {
425
+ const watcher = this.watchers.get(message.watch_id);
426
+ if (watcher) watcher.callback({ created: message.created, modified: message.modified, removed: message.removed });
406
427
  }
407
428
  }
408
429
  } catch (error) {
@@ -451,6 +472,13 @@ var MachineConnection = class {
451
472
  this.processes.delete(id);
452
473
  }
453
474
  }
475
+ for (const [watchId, watcher] of this.watchers) {
476
+ try {
477
+ await this.request("watch", { watch_id: watchId, path: watcher.path, recursive: watcher.recursive });
478
+ } catch {
479
+ if (!current()) return;
480
+ }
481
+ }
454
482
  if (!current()) return;
455
483
  this.state.status = "connected";
456
484
  this.state.error = void 0;
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
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, ProcessEvent, ProcessInfo, ProcessOutput, InstallAgent, Profile, Theme, WorkspaceActive, WorkspaceRow, WorkspaceMenuItem } from './types';
4
+ export type { ConnectionOptions, FileClick, Machine, MachineState, Session, Size, Output, Snapshot, TerminalEvent, DirectoryEntry, FileChanges, WatchOptions, ExecOptions, ExecResult, ProcessEvent, ProcessInfo, ProcessOutput, InstallAgent, Profile, Theme, WorkspaceActive, WorkspaceRow, WorkspaceMenuItem } from './types';
5
5
  export type { MachineConnection } from './connection';
6
6
  export type { Process } from './process';
7
7
  export type { Pty } from './pty';
package/dist/types.d.ts CHANGED
@@ -116,6 +116,14 @@ export interface DirectoryEntry {
116
116
  name: string;
117
117
  isDirectory: boolean;
118
118
  }
119
+ export interface FileChanges {
120
+ created: string[];
121
+ modified: string[];
122
+ removed: string[];
123
+ }
124
+ export interface WatchOptions {
125
+ recursive?: boolean;
126
+ }
119
127
  export interface ExecOptions {
120
128
  cwd: string;
121
129
  env?: Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lengmoxxl/sdk",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "Client SDK for WangCai: connect to a local or remote machine and drive its pty, files and git",
5
5
  "license": "MIT",
6
6
  "repository": {