@team-work/sandbox-client 0.1.1-beta.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.
Files changed (99) hide show
  1. package/README.md +69 -0
  2. package/dist/cli/config-cmd.d.ts +3 -0
  3. package/dist/cli/config-cmd.js +60 -0
  4. package/dist/cli/config-cmd.js.map +1 -0
  5. package/dist/cli/index.d.ts +2 -0
  6. package/dist/cli/index.js +30 -0
  7. package/dist/cli/index.js.map +1 -0
  8. package/dist/cli/list-cmd.d.ts +3 -0
  9. package/dist/cli/list-cmd.js +36 -0
  10. package/dist/cli/list-cmd.js.map +1 -0
  11. package/dist/cli/log-cmd.d.ts +3 -0
  12. package/dist/cli/log-cmd.js +118 -0
  13. package/dist/cli/log-cmd.js.map +1 -0
  14. package/dist/cli/start-cmd.d.ts +3 -0
  15. package/dist/cli/start-cmd.js +69 -0
  16. package/dist/cli/start-cmd.js.map +1 -0
  17. package/dist/cli/status-cmd.d.ts +3 -0
  18. package/dist/cli/status-cmd.js +46 -0
  19. package/dist/cli/status-cmd.js.map +1 -0
  20. package/dist/cli/stop-cmd.d.ts +3 -0
  21. package/dist/cli/stop-cmd.js +115 -0
  22. package/dist/cli/stop-cmd.js.map +1 -0
  23. package/dist/cli/update-cmd.d.ts +3 -0
  24. package/dist/cli/update-cmd.js +16 -0
  25. package/dist/cli/update-cmd.js.map +1 -0
  26. package/dist/client/frames.d.ts +111 -0
  27. package/dist/client/frames.js +22 -0
  28. package/dist/client/frames.js.map +1 -0
  29. package/dist/client/handshake.d.ts +7 -0
  30. package/dist/client/handshake.js +83 -0
  31. package/dist/client/handshake.js.map +1 -0
  32. package/dist/client/protocol.d.ts +36 -0
  33. package/dist/client/protocol.js +39 -0
  34. package/dist/client/protocol.js.map +1 -0
  35. package/dist/client/ws-client.d.ts +48 -0
  36. package/dist/client/ws-client.js +272 -0
  37. package/dist/client/ws-client.js.map +1 -0
  38. package/dist/config/index.d.ts +6 -0
  39. package/dist/config/index.js +68 -0
  40. package/dist/config/index.js.map +1 -0
  41. package/dist/config/paths.d.ts +14 -0
  42. package/dist/config/paths.js +41 -0
  43. package/dist/config/paths.js.map +1 -0
  44. package/dist/config/schema.d.ts +17 -0
  45. package/dist/config/schema.js +52 -0
  46. package/dist/config/schema.js.map +1 -0
  47. package/dist/daemon/detach.d.ts +17 -0
  48. package/dist/daemon/detach.js +30 -0
  49. package/dist/daemon/detach.js.map +1 -0
  50. package/dist/daemon/launch.d.ts +15 -0
  51. package/dist/daemon/launch.js +70 -0
  52. package/dist/daemon/launch.js.map +1 -0
  53. package/dist/daemon/signal.d.ts +7 -0
  54. package/dist/daemon/signal.js +22 -0
  55. package/dist/daemon/signal.js.map +1 -0
  56. package/dist/handlers/rpc/exec-shell.d.ts +13 -0
  57. package/dist/handlers/rpc/exec-shell.js +52 -0
  58. package/dist/handlers/rpc/exec-shell.js.map +1 -0
  59. package/dist/handlers/rpc/file-ops.d.ts +17 -0
  60. package/dist/handlers/rpc/file-ops.js +163 -0
  61. package/dist/handlers/rpc/file-ops.js.map +1 -0
  62. package/dist/handlers/rpc/router.d.ts +9 -0
  63. package/dist/handlers/rpc/router.js +98 -0
  64. package/dist/handlers/rpc/router.js.map +1 -0
  65. package/dist/handlers/terminal/manager.d.ts +20 -0
  66. package/dist/handlers/terminal/manager.js +75 -0
  67. package/dist/handlers/terminal/manager.js.map +1 -0
  68. package/dist/handlers/terminal/pty-session.d.ts +17 -0
  69. package/dist/handlers/terminal/pty-session.js +76 -0
  70. package/dist/handlers/terminal/pty-session.js.map +1 -0
  71. package/dist/handlers/terminal/router.d.ts +6 -0
  72. package/dist/handlers/terminal/router.js +25 -0
  73. package/dist/handlers/terminal/router.js.map +1 -0
  74. package/dist/instance/id.d.ts +5 -0
  75. package/dist/instance/id.js +17 -0
  76. package/dist/instance/id.js.map +1 -0
  77. package/dist/instance/store.d.ts +33 -0
  78. package/dist/instance/store.js +155 -0
  79. package/dist/instance/store.js.map +1 -0
  80. package/dist/instance/types.d.ts +28 -0
  81. package/dist/instance/types.js +7 -0
  82. package/dist/instance/types.js.map +1 -0
  83. package/dist/logging/logger.d.ts +11 -0
  84. package/dist/logging/logger.js +42 -0
  85. package/dist/logging/logger.js.map +1 -0
  86. package/dist/system/report.d.ts +2 -0
  87. package/dist/system/report.js +61 -0
  88. package/dist/system/report.js.map +1 -0
  89. package/dist/update/self-update.d.ts +8 -0
  90. package/dist/update/self-update.js +41 -0
  91. package/dist/update/self-update.js.map +1 -0
  92. package/dist/utils/info.d.ts +11 -0
  93. package/dist/utils/info.js +21 -0
  94. package/dist/utils/info.js.map +1 -0
  95. package/dist/utils/pem.d.ts +15 -0
  96. package/dist/utils/pem.js +31 -0
  97. package/dist/utils/pem.js.map +1 -0
  98. package/package.json +44 -0
  99. package/scripts/fix-pty-perms.js +30 -0
package/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # @team-work/sandbox-client
2
+
3
+ team-work 平台的 sandbox CLI 客户端。全局命令 `tw`,在用户机器后台运行,通过 WebSocket 接入 sandbox 服务端,接受远程调度执行 **伪终端会话 / shell 执行 / 文件操作**。
4
+
5
+ ## 安装
6
+
7
+ ```bash
8
+ npm i -g @team-work/sandbox-client
9
+ ```
10
+
11
+ ## 快速开始
12
+
13
+ ```bash
14
+ # 1. 配置 sandbox 服务地址(ws://host:18081/ws/sandbox/cli)
15
+ tw config set serverUrl ws://10.0.0.1:18081/ws/sandbox/cli
16
+ # 可选:自定义日志目录(默认 ~/.tw/logs)
17
+ tw config set logDir /var/log/tw
18
+
19
+ # 2. 后台启动(sandboxId + privateKey 均为位置参数)
20
+ tw start <sandboxId> <privateKey>
21
+ # 输出:inst-20260805-1a2b3c started (pid 12345)
22
+
23
+ # 3. 查询 / 列举 / 停止
24
+ tw status # 取最近一个实例
25
+ tw list # 列出所有激活态实例
26
+ tw stop <id>
27
+ tw log <id> [--follow] # 跟踪日志
28
+
29
+ # 4. 自更新
30
+ tw update
31
+ ```
32
+
33
+ ## 命令一览
34
+
35
+ | 命令 | 说明 |
36
+ |------|------|
37
+ | `tw config set <key> <value>` | 设置配置(serverUrl / logDir) |
38
+ | `tw config get <key>` | 读取配置 |
39
+ | `tw config list` | 列出全部配置 |
40
+ | `tw config unset <key>` | 删除配置 |
41
+ | `tw start <sandboxId> <privateKey> [--foreground] [--id <instId>]` | 启动守护进程 |
42
+ | `tw status [id]` | 查询实例状态 |
43
+ | `tw stop [id] [--yes]` | 停止实例 |
44
+ | `tw list` | 列出所有实例 |
45
+ | `tw log [id] [--follow]` | 查看实例日志 |
46
+ | `tw update` | 自更新 CLI |
47
+
48
+ ## 协议
49
+
50
+ CLI 严格对齐 sandbox 服务端 `/ws/cli` 协议:
51
+
52
+ - raw `ws`,UTF-8 JSON 文本帧,按 `type` 字段区分。
53
+ - 握手:首帧 `{type:'hello', sandboxId, privateKey}` → `{type:'hello-ack', ok}`。
54
+ - 上报:`{type:'report', config:{ip,os,cpu,memory,storage,cliVersion}}`。
55
+ - 心跳:每 25s `{type:'ping'}` → `{type:'pong'}`。
56
+ - RPC:服务端 `{type:'rpc', reqId, action, params}`;CLI 按 action 回 `rpc-result`(文件操作)或 `rpc-stream`+`rpc-end`(exec_shell)。
57
+ - 伪终端:`terminal.open/input/resize/close`(server→cli),`terminal.data/exit`(cli→server),按 `sessionId` 多会话复用。
58
+
59
+ 详见 sandbox 服务端 `src/gateway/cli.gateway.ts`。
60
+
61
+ ## 配置文件位置
62
+
63
+ - 配置:`~/.tw/config.json`(权限 0600)
64
+ - 实例状态:`~/.tw/instances/<id>.json`(权限 0600)
65
+ - 日志:`~/.tw/logs/<id>.log`(或自定义 logDir)
66
+
67
+ ## License
68
+
69
+ MIT
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw config <get|set|list|unset>` */
3
+ export declare function buildConfigCommand(): Command;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildConfigCommand = buildConfigCommand;
4
+ const commander_1 = require("commander");
5
+ const index_js_1 = require("../config/index.js");
6
+ const schema_js_1 = require("../config/schema.js");
7
+ /** `tw config <get|set|list|unset>` */
8
+ function buildConfigCommand() {
9
+ const cmd = new commander_1.Command('config');
10
+ cmd
11
+ .command('set <key> <value>')
12
+ .description('设置配置项(serverUrl / logDir)')
13
+ .action((key, value) => {
14
+ if (!(0, schema_js_1.isValidKey)(key)) {
15
+ console.error(`未知配置项: ${key}(合法: ${schema_js_1.CONFIG_KEYS.join(', ')})`);
16
+ process.exit(1);
17
+ }
18
+ try {
19
+ (0, index_js_1.setConfig)(key, value);
20
+ console.log(`已设置 ${key} = ${value}`);
21
+ }
22
+ catch (err) {
23
+ console.error(err instanceof Error ? err.message : String(err));
24
+ process.exit(1);
25
+ }
26
+ });
27
+ cmd
28
+ .command('get <key>')
29
+ .description('读取配置项')
30
+ .action((key) => {
31
+ if (!(0, schema_js_1.isValidKey)(key)) {
32
+ console.error(`未知配置项: ${key}`);
33
+ process.exit(1);
34
+ }
35
+ const config = (0, index_js_1.loadConfig)();
36
+ console.log(config[key]);
37
+ });
38
+ cmd
39
+ .command('list')
40
+ .description('列出全部配置')
41
+ .action(() => {
42
+ const config = (0, index_js_1.loadConfig)();
43
+ for (const k of schema_js_1.CONFIG_KEYS) {
44
+ console.log(`${k} = ${config[k]}`);
45
+ }
46
+ });
47
+ cmd
48
+ .command('unset <key>')
49
+ .description('删除配置项(恢复默认)')
50
+ .action((key) => {
51
+ if (!(0, schema_js_1.isValidKey)(key)) {
52
+ console.error(`未知配置项: ${key}`);
53
+ process.exit(1);
54
+ }
55
+ (0, index_js_1.unsetConfig)(key);
56
+ console.log(`已删除 ${key}`);
57
+ });
58
+ return cmd;
59
+ }
60
+ //# sourceMappingURL=config-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-cmd.js","sourceRoot":"","sources":["../../src/cli/config-cmd.ts"],"names":[],"mappings":";;AAMA,gDAuDC;AA7DD,yCAAoC;AAEpC,iDAAwE;AACxE,mDAA8D;AAE9D,uCAAuC;AACvC,SAAgB,kBAAkB;IAChC,MAAM,GAAG,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC,CAAC;IAElC,GAAG;SACA,OAAO,CAAC,mBAAmB,CAAC;SAC5B,WAAW,CAAC,2BAA2B,CAAC;SACxC,MAAM,CAAC,CAAC,GAAW,EAAE,KAAa,EAAE,EAAE;QACrC,IAAI,CAAC,IAAA,sBAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,QAAQ,uBAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAI,CAAC;YACH,IAAA,oBAAS,EAAC,GAAG,EAAE,KAAK,CAAC,CAAC;YACtB,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,MAAM,KAAK,EAAE,CAAC,CAAC;QACvC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,GAAG;SACA,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,OAAO,CAAC;SACpB,MAAM,CAAC,CAAC,GAAW,EAAE,EAAE;QACtB,IAAI,CAAC,IAAA,sBAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,MAAM,GAAG,IAAA,qBAAU,GAAE,CAAC;QAC5B,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAC3B,CAAC,CAAC,CAAC;IAEL,GAAG;SACA,OAAO,CAAC,MAAM,CAAC;SACf,WAAW,CAAC,QAAQ,CAAC;SACrB,MAAM,CAAC,GAAG,EAAE;QACX,MAAM,MAAM,GAAG,IAAA,qBAAU,GAAE,CAAC;QAC5B,KAAK,MAAM,CAAC,IAAI,uBAAW,EAAE,CAAC;YAC5B,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEL,GAAG;SACA,OAAO,CAAC,aAAa,CAAC;SACtB,WAAW,CAAC,aAAa,CAAC;SAC1B,MAAM,CAAC,CAAC,GAAW,EAAE,EAAE;QACtB,IAAI,CAAC,IAAA,sBAAU,EAAC,GAAG,CAAC,EAAE,CAAC;YACrB,OAAO,CAAC,KAAK,CAAC,UAAU,GAAG,EAAE,CAAC,CAAC;YAC/B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,IAAA,sBAAW,EAAC,GAAG,CAAC,CAAC;QACjB,OAAO,CAAC,GAAG,CAAC,OAAO,GAAG,EAAE,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEL,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const commander_1 = require("commander");
5
+ const info_js_1 = require("../utils/info.js");
6
+ const config_cmd_js_1 = require("./config-cmd.js");
7
+ const start_cmd_js_1 = require("./start-cmd.js");
8
+ const status_cmd_js_1 = require("./status-cmd.js");
9
+ const stop_cmd_js_1 = require("./stop-cmd.js");
10
+ const list_cmd_js_1 = require("./list-cmd.js");
11
+ const log_cmd_js_1 = require("./log-cmd.js");
12
+ const update_cmd_js_1 = require("./update-cmd.js");
13
+ const program = new commander_1.Command('tw');
14
+ program
15
+ .name('tw')
16
+ .description('team-work sandbox CLI client')
17
+ .version((0, info_js_1.getPkgInfo)().version);
18
+ program.addCommand((0, config_cmd_js_1.buildConfigCommand)());
19
+ program.addCommand((0, start_cmd_js_1.buildStartCommand)());
20
+ program.addCommand((0, status_cmd_js_1.buildStatusCommand)());
21
+ program.addCommand((0, stop_cmd_js_1.buildStopCommand)());
22
+ program.addCommand((0, list_cmd_js_1.buildListCommand)());
23
+ program.addCommand((0, log_cmd_js_1.buildLogCommand)());
24
+ program.addCommand((0, update_cmd_js_1.buildUpdateCommand)());
25
+ // 缺省显示 help
26
+ program.parseAsync(process.argv).catch((err) => {
27
+ console.error(err instanceof Error ? err.message : String(err));
28
+ process.exit(1);
29
+ });
30
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":";;;AAEA,yCAAoC;AAEpC,8CAA8C;AAC9C,mDAAqD;AACrD,iDAAmD;AACnD,mDAAqD;AACrD,+CAAiD;AACjD,+CAAiD;AACjD,6CAA+C;AAC/C,mDAAqD;AAErD,MAAM,OAAO,GAAG,IAAI,mBAAO,CAAC,IAAI,CAAC,CAAC;AAElC,OAAO;KACJ,IAAI,CAAC,IAAI,CAAC;KACV,WAAW,CAAC,8BAA8B,CAAC;KAC3C,OAAO,CAAC,IAAA,oBAAU,GAAE,CAAC,OAAO,CAAC,CAAC;AAEjC,OAAO,CAAC,UAAU,CAAC,IAAA,kCAAkB,GAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,IAAA,gCAAiB,GAAE,CAAC,CAAC;AACxC,OAAO,CAAC,UAAU,CAAC,IAAA,kCAAkB,GAAE,CAAC,CAAC;AACzC,OAAO,CAAC,UAAU,CAAC,IAAA,8BAAgB,GAAE,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,IAAA,8BAAgB,GAAE,CAAC,CAAC;AACvC,OAAO,CAAC,UAAU,CAAC,IAAA,4BAAe,GAAE,CAAC,CAAC;AACtC,OAAO,CAAC,UAAU,CAAC,IAAA,kCAAkB,GAAE,CAAC,CAAC;AAEzC,YAAY;AACZ,OAAO,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IAC7C,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw list`:列出本机所有激活态实例(PID 已死的残留自动清理) */
3
+ export declare function buildListCommand(): Command;
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildListCommand = buildListCommand;
4
+ const commander_1 = require("commander");
5
+ const store_js_1 = require("../instance/store.js");
6
+ /** `tw list`:列出本机所有激活态实例(PID 已死的残留自动清理) */
7
+ function buildListCommand() {
8
+ const cmd = new commander_1.Command('list');
9
+ cmd.description('列出所有实例').action(() => {
10
+ (0, store_js_1.purgeDeadInstances)(); // 自愈:清掉崩溃未自删的 stale 记录
11
+ const all = (0, store_js_1.listInstances)();
12
+ if (!all.length) {
13
+ console.log('暂无实例');
14
+ return;
15
+ }
16
+ // 按启动时间降序
17
+ all.sort((a, b) => (a.startedAt < b.startedAt ? 1 : -1));
18
+ const header = ['ID', 'PID', 'STATUS', 'WS', 'SANDBOX', 'STARTED'];
19
+ const rows = all.map((r) => [
20
+ r.id,
21
+ String(r.pid),
22
+ r.status,
23
+ r.wsState,
24
+ r.sandboxId,
25
+ r.startedAt.replace('T', ' ').slice(0, 19),
26
+ ]);
27
+ const widths = header.map((_, i) => Math.max(header[i].length, ...rows.map((r) => r[i].length)));
28
+ const fmt = (cells) => cells.map((c, i) => c.padEnd(widths[i])).join(' ');
29
+ console.log(fmt(header));
30
+ console.log(widths.map((w) => '-'.repeat(w)).join(' '));
31
+ for (const r of rows)
32
+ console.log(fmt(r));
33
+ });
34
+ return cmd;
35
+ }
36
+ //# sourceMappingURL=list-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list-cmd.js","sourceRoot":"","sources":["../../src/cli/list-cmd.ts"],"names":[],"mappings":";;AAKA,4CA8BC;AAnCD,yCAAoC;AAEpC,mDAAyE;AAEzE,2CAA2C;AAC3C,SAAgB,gBAAgB;IAC9B,MAAM,GAAG,GAAG,IAAI,mBAAO,CAAC,MAAM,CAAC,CAAC;IAChC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,EAAE;QACpC,IAAA,6BAAkB,GAAE,CAAC,CAAC,uBAAuB;QAC7C,MAAM,GAAG,GAAG,IAAA,wBAAa,GAAE,CAAC;QAC5B,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACpB,OAAO;QACT,CAAC;QACD,UAAU;QACV,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAEzD,MAAM,MAAM,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;QACnE,MAAM,IAAI,GAAG,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;YAC1B,CAAC,CAAC,EAAE;YACJ,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;YACb,CAAC,CAAC,MAAM;YACR,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SAC3C,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CACjC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAC5D,CAAC;QACF,MAAM,GAAG,GAAG,CAAC,KAAe,EAAE,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;QACzB,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QACzD,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw log [id] [--follow] [--changes]` */
3
+ export declare function buildLogCommand(): Command;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.buildLogCommand = buildLogCommand;
7
+ const commander_1 = require("commander");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_readline_1 = __importDefault(require("node:readline"));
10
+ const paths_js_1 = require("../config/paths.js");
11
+ const store_js_1 = require("../instance/store.js");
12
+ function resolveLogFile(id) {
13
+ if (id) {
14
+ const rec = (0, store_js_1.loadInstance)(id);
15
+ if (rec)
16
+ return rec.logFile;
17
+ return null;
18
+ }
19
+ // 取最近实例
20
+ const all = (0, store_js_1.listInstances)();
21
+ if (!all.length)
22
+ return null;
23
+ all.sort((a, b) => (a.lastSeenAt < b.lastSeenAt ? 1 : -1));
24
+ return all[0].logFile;
25
+ }
26
+ /** 解析 pino JSON 行,提取 msg / level / time */
27
+ function parsePinoLine(line) {
28
+ try {
29
+ const j = JSON.parse(line);
30
+ return { time: j.time, level: j.level, msg: j.msg, ...j };
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ }
36
+ /** `tw log [id] [--follow] [--changes]` */
37
+ function buildLogCommand() {
38
+ const cmd = new commander_1.Command('log');
39
+ cmd
40
+ .description('查看实例日志(缺省取最近一个)')
41
+ .argument('[id]', '实例 id')
42
+ .option('-f, --follow', '持续跟踪(类似 tail -f)', false)
43
+ .option('-c, --changes', '仅输出变更行(含 auth/fail/error/warn 等关键词)', false)
44
+ .option('--clean', '清理超期日志文件(同时输出)', false)
45
+ .option('--clean-days <days>', '超期天数,默认 7', '7')
46
+ .action(async (id, opts) => {
47
+ // --clean:清理超期日志并报告
48
+ if (opts.clean) {
49
+ const days = parseInt(opts.cleanDays, 10);
50
+ if (Number.isNaN(days) || days <= 0) {
51
+ console.error('--clean-days 必须是正整数');
52
+ process.exit(1);
53
+ }
54
+ const maxAgeMs = days * 24 * 60 * 60 * 1000;
55
+ const n = (0, paths_js_1.purgeOldLogs)(paths_js_1.DEFAULT_LOG_DIR, maxAgeMs);
56
+ console.log(`已清理 ${n} 个超过 ${days} 天的日志文件 (${paths_js_1.DEFAULT_LOG_DIR})`);
57
+ return;
58
+ }
59
+ const file = resolveLogFile(id);
60
+ if (!file || !(0, node_fs_1.existsSync)(file)) {
61
+ console.error(id ? `实例或日志不存在: ${id}` : '暂无实例日志');
62
+ process.exit(1);
63
+ }
64
+ const content = (0, node_fs_1.readFileSync)(file, 'utf8');
65
+ const lines = content.split('\n').filter(Boolean);
66
+ if (opts.changes) {
67
+ // 变更模式:只输出含 "changed" / "update" / "auth" / "fail" / "error" / "warn" 关键词的行
68
+ const keywords = ['changed', 'update', 'auth', 'fail', 'error', 'warn', 'removed', 'added', 'purge', 'exit'];
69
+ const filtered = lines.filter((l) => {
70
+ const p = parsePinoLine(l);
71
+ if (!p)
72
+ return false;
73
+ const msg = String(p.msg ?? '').toLowerCase();
74
+ return keywords.some((k) => msg.includes(k));
75
+ });
76
+ if (!filtered.length) {
77
+ console.log('(无变更相关日志)');
78
+ }
79
+ else {
80
+ for (const l of filtered)
81
+ console.log(l);
82
+ }
83
+ return;
84
+ }
85
+ const tail = async () => {
86
+ const stream = (0, node_fs_1.createReadStream)(file, { encoding: 'utf8' });
87
+ const rl = node_readline_1.default.createInterface({ input: stream, crlfDelay: Infinity });
88
+ for await (const line of rl) {
89
+ console.log(line);
90
+ }
91
+ };
92
+ await tail();
93
+ if (!opts.follow)
94
+ return;
95
+ // follow:轮询新内容(用顶部导入的 statSync)
96
+ let size = (0, node_fs_1.statSync)(file).size;
97
+ // eslint-disable-next-line no-constant-condition
98
+ while (true) {
99
+ await new Promise((r) => setTimeout(r, 500));
100
+ let st;
101
+ try {
102
+ st = (0, node_fs_1.statSync)(file);
103
+ }
104
+ catch {
105
+ continue;
106
+ }
107
+ if (st.size > size) {
108
+ const stream = (0, node_fs_1.createReadStream)(file, { encoding: 'utf8', start: size });
109
+ const rl = node_readline_1.default.createInterface({ input: stream, crlfDelay: Infinity });
110
+ for await (const line of rl)
111
+ console.log(line);
112
+ size = st.size;
113
+ }
114
+ }
115
+ });
116
+ return cmd;
117
+ }
118
+ //# sourceMappingURL=log-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log-cmd.js","sourceRoot":"","sources":["../../src/cli/log-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw start <sandboxId> <privateKey> [--foreground] [--id <instId>]` */
3
+ export declare function buildStartCommand(): Command;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildStartCommand = buildStartCommand;
4
+ const commander_1 = require("commander");
5
+ const index_js_1 = require("../config/index.js");
6
+ const launch_js_1 = require("../daemon/launch.js");
7
+ const detach_js_1 = require("../daemon/detach.js");
8
+ const store_js_1 = require("../instance/store.js");
9
+ /** `tw start <sandboxId> <privateKey> [--foreground] [--id <instId>]` */
10
+ function buildStartCommand() {
11
+ const cmd = new commander_1.Command('start');
12
+ cmd
13
+ .description('后台启动守护进程,连接 sandbox')
14
+ .argument('<sandboxId>', '沙箱 ID(oauth_sandbox 表主键)')
15
+ .argument('<privateKey>', '私钥(hello 握手凭证)')
16
+ .option('--foreground', '前台运行(调试用,不 detach)', false)
17
+ .option('--daemon', '(内部) 作为被拉起的守护进程直接运行,写文件日志', false)
18
+ .option('--id <instId>', '指定实例 id(默认自动生成)')
19
+ .option('--replace', '强制替换同 sandboxId 旧实例(默认 false)', false)
20
+ .action(async (sandboxId, privateKey, opts) => {
21
+ const config = (0, index_js_1.loadConfig)();
22
+ if (!config.serverUrl) {
23
+ console.error('未配置 serverUrl,请先执行: tw config set serverUrl ws://host:port/ws/sandbox/cli');
24
+ process.exit(1);
25
+ }
26
+ // 内部 daemon 子进程直接跑;此时父进程已完成替换逻辑
27
+ if (opts.daemon) {
28
+ const code = await (0, launch_js_1.runDaemon)({ sandboxId, privateKey, config, instanceId: opts.id, foreground: false });
29
+ process.exit(code);
30
+ }
31
+ // 同 sandboxId 只允许一个运行进程:发现旧的 running 实例则杀掉再启。
32
+ const existing = (0, store_js_1.findRunningBySandboxId)(sandboxId);
33
+ if (existing && existing.id !== opts.id) {
34
+ if (!opts.replace) {
35
+ console.error(`sandbox ${sandboxId} 已有运行实例 ${existing.id} (pid ${existing.pid})。\n` +
36
+ `若确认替换,请加 --replace 参数:tw start ${sandboxId} <key> --replace`);
37
+ process.exit(1);
38
+ }
39
+ const res = await (0, store_js_1.killInstance)(existing.id);
40
+ console.log(res.note);
41
+ if (res.rec && !res.ok) {
42
+ console.error('旧实例终止失败,中止启动');
43
+ process.exit(1);
44
+ }
45
+ }
46
+ if (opts.foreground) {
47
+ const code = await (0, launch_js_1.runDaemon)({ sandboxId, privateKey, config, instanceId: opts.id, foreground: true });
48
+ process.exit(code);
49
+ }
50
+ // 后台:spawn detached 子进程
51
+ const entryPath = process.argv[1];
52
+ const { instanceId, child } = (0, detach_js_1.detachStart)({ sandboxId, privateKey, entryPath, instanceId: opts.id });
53
+ // 子进程异常退出(极少)直接报错
54
+ child.on('error', (err) => {
55
+ console.error('启动失败:', err.message);
56
+ process.exit(1);
57
+ });
58
+ const rec = await (0, detach_js_1.waitForInstance)(instanceId, 3000);
59
+ if (!rec) {
60
+ console.error('实例启动超时,请检查日志:', instanceId);
61
+ process.exit(1);
62
+ }
63
+ console.log(`${rec.id} started (pid ${rec.pid})`);
64
+ console.log(`日志: ${rec.logFile}`);
65
+ process.exit(0);
66
+ });
67
+ return cmd;
68
+ }
69
+ //# sourceMappingURL=start-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"start-cmd.js","sourceRoot":"","sources":["../../src/cli/start-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw status [id]` */
3
+ export declare function buildStatusCommand(): Command;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildStatusCommand = buildStatusCommand;
4
+ const commander_1 = require("commander");
5
+ const store_js_1 = require("../instance/store.js");
6
+ /** 取最近一个实例(按 lastSeenAt 降序) */
7
+ function latestInstance() {
8
+ const all = (0, store_js_1.listInstances)();
9
+ if (!all.length)
10
+ return null;
11
+ all.sort((a, b) => (a.lastSeenAt < b.lastSeenAt ? 1 : -1));
12
+ return all[0];
13
+ }
14
+ /** `tw status [id]` */
15
+ function buildStatusCommand() {
16
+ const cmd = new commander_1.Command('status');
17
+ cmd
18
+ .description('查询实例状态(缺省取最近一个)')
19
+ .argument('[id]', '实例 id')
20
+ .action((id) => {
21
+ (0, store_js_1.purgeDeadInstances)(); // 自愈
22
+ const rec = id ? (0, store_js_1.loadInstance)(id) : latestInstance();
23
+ if (!rec) {
24
+ console.error(id ? `实例不存在: ${id}` : '暂无实例');
25
+ process.exit(1);
26
+ }
27
+ const alive = (0, store_js_1.isPidAlive)(rec.pid);
28
+ if (!alive) {
29
+ // 残留记录(理论上 purgeDeadInstances 已清,兜底)
30
+ (0, store_js_1.removeInstance)(rec.id);
31
+ console.error(id ? `实例 ${id} 已退出(记录已清理)` : '暂无实例');
32
+ process.exit(1);
33
+ }
34
+ console.log(`ID ${rec.id}`);
35
+ console.log(`PID ${rec.pid} (alive)`);
36
+ console.log(`Sandbox ${rec.sandboxId}`);
37
+ console.log(`Server ${rec.serverUrl}`);
38
+ console.log(`WS ${rec.wsState}`);
39
+ console.log(`Status ${rec.status}`);
40
+ console.log(`Started ${rec.startedAt}`);
41
+ console.log(`Last seen ${rec.lastSeenAt}`);
42
+ console.log(`Log ${rec.logFile}`);
43
+ });
44
+ return cmd;
45
+ }
46
+ //# sourceMappingURL=status-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"status-cmd.js","sourceRoot":"","sources":["../../src/cli/status-cmd.ts"],"names":[],"mappings":";;AAcA,gDA8BC;AA5CD,yCAAoC;AAEpC,mDAAmH;AAGnH,+BAA+B;AAC/B,SAAS,cAAc;IACrB,MAAM,GAAG,GAAG,IAAA,wBAAa,GAAE,CAAC;IAC5B,IAAI,CAAC,GAAG,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC;IAC7B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3D,OAAO,GAAG,CAAC,CAAC,CAAC,CAAC;AAChB,CAAC;AAED,uBAAuB;AACvB,SAAgB,kBAAkB;IAChC,MAAM,GAAG,GAAG,IAAI,mBAAO,CAAC,QAAQ,CAAC,CAAC;IAClC,GAAG;SACA,WAAW,CAAC,iBAAiB,CAAC;SAC9B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;SACzB,MAAM,CAAC,CAAC,EAAW,EAAE,EAAE;QACtB,IAAA,6BAAkB,GAAE,CAAC,CAAC,KAAK;QAC3B,MAAM,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,IAAA,uBAAY,EAAC,EAAE,CAAC,CAAC,CAAC,CAAC,cAAc,EAAE,CAAC;QACrD,IAAI,CAAC,GAAG,EAAE,CAAC;YACT,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,MAAM,KAAK,GAAG,IAAA,qBAAU,EAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,qCAAqC;YACrC,IAAA,yBAAc,EAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACvB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACnD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC;QACtC,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,GAAG,UAAU,CAAC,CAAC;QAC/C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QAC3C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;QAC1C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;QAC7C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;QAC9C,OAAO,CAAC,GAAG,CAAC,gBAAgB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IACL,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw stop [id] [--yes]` */
3
+ export declare function buildStopCommand(): Command;
@@ -0,0 +1,115 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.buildStopCommand = buildStopCommand;
37
+ const commander_1 = require("commander");
38
+ const readline = __importStar(require("node:readline"));
39
+ const store_js_1 = require("../instance/store.js");
40
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
41
+ function latestInstance() {
42
+ const all = (0, store_js_1.listInstances)();
43
+ if (!all.length)
44
+ return null;
45
+ all.sort((a, b) => (a.lastSeenAt < b.lastSeenAt ? 1 : -1));
46
+ return all[0];
47
+ }
48
+ async function killPid(pid) {
49
+ if (!(0, store_js_1.isPidAlive)(pid))
50
+ return true;
51
+ try {
52
+ process.kill(pid, 'SIGTERM');
53
+ }
54
+ catch {
55
+ return false;
56
+ }
57
+ // 等 5s,仍存活则 SIGKILL
58
+ for (let i = 0; i < 50; i++) {
59
+ await sleep(100);
60
+ if (!(0, store_js_1.isPidAlive)(pid))
61
+ return true;
62
+ }
63
+ if ((0, store_js_1.isPidAlive)(pid)) {
64
+ try {
65
+ process.kill(pid, 'SIGKILL');
66
+ }
67
+ catch {
68
+ // ignore
69
+ }
70
+ await sleep(200);
71
+ }
72
+ return !(0, store_js_1.isPidAlive)(pid);
73
+ }
74
+ function confirm(question) {
75
+ return new Promise((resolve) => {
76
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
77
+ rl.question(question, (answer) => {
78
+ rl.close();
79
+ resolve(/^y(es)?$/i.test(answer.trim()));
80
+ });
81
+ });
82
+ }
83
+ /** `tw stop [id] [--yes]` */
84
+ function buildStopCommand() {
85
+ const cmd = new commander_1.Command('stop');
86
+ cmd
87
+ .description('停止实例(缺省取最近一个)')
88
+ .argument('[id]', '实例 id')
89
+ .option('--yes', '跳过二次确认', false)
90
+ .action(async (id, opts) => {
91
+ const rec = id ? (0, store_js_1.loadInstance)(id) : latestInstance();
92
+ if (!rec) {
93
+ console.error(id ? `实例不存在: ${id}` : '暂无实例');
94
+ process.exit(1);
95
+ }
96
+ if (!opts.yes) {
97
+ const ok = await confirm(`确认停止 ${rec.id} (pid ${rec.pid})? [y/N] `);
98
+ if (!ok) {
99
+ console.log('已取消');
100
+ process.exit(0);
101
+ }
102
+ }
103
+ const success = await killPid(rec.pid);
104
+ // daemon 收到 SIGTERM 会自删记录;兜底:若文件仍在则删(SIGKILL 未自删场景)
105
+ (0, store_js_1.removeInstance)(rec.id);
106
+ if (success)
107
+ console.log(`${rec.id} stopped`);
108
+ else {
109
+ console.error(`${rec.id} 停止失败(pid ${rec.pid})`);
110
+ process.exit(1);
111
+ }
112
+ });
113
+ return cmd;
114
+ }
115
+ //# sourceMappingURL=stop-cmd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stop-cmd.js","sourceRoot":"","sources":["../../src/cli/stop-cmd.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { Command } from 'commander';
2
+ /** `tw update`:自更新 CLI 本身 */
3
+ export declare function buildUpdateCommand(): Command;