@xiaoqiong0v0/opencode-ssh-tool 0.5.1 → 0.6.1
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 +27 -21
- package/dist/agent.d.ts +52 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +244 -0
- package/dist/agent.js.map +1 -0
- package/dist/base-session.d.ts +95 -13
- package/dist/base-session.d.ts.map +1 -1
- package/dist/base-session.js +271 -102
- package/dist/base-session.js.map +1 -1
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +16 -7
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +14 -15
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +14 -15
- package/dist/constants.js.map +1 -1
- package/dist/i18n.d.ts +24 -14
- package/dist/i18n.d.ts.map +1 -1
- package/dist/i18n.js +37 -12
- package/dist/i18n.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +90 -73
- package/dist/index.js.map +1 -1
- package/dist/last-match.d.ts +11 -0
- package/dist/last-match.d.ts.map +1 -1
- package/dist/last-match.js +24 -0
- package/dist/last-match.js.map +1 -1
- package/dist/local-session.d.ts +2 -1
- package/dist/local-session.d.ts.map +1 -1
- package/dist/local-session.js +14 -8
- package/dist/local-session.js.map +1 -1
- package/dist/server-entry.d.ts +2 -0
- package/dist/server-entry.d.ts.map +1 -0
- package/dist/server-entry.js +61 -0
- package/dist/server-entry.js.map +1 -0
- package/dist/server-manager.d.ts +9 -9
- package/dist/server-manager.d.ts.map +1 -1
- package/dist/server-manager.js +208 -27
- package/dist/server-manager.js.map +1 -1
- package/dist/server.d.ts +3 -28
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +429 -275
- package/dist/server.js.map +1 -1
- package/dist/session.d.ts +0 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +2 -9
- package/dist/session.js.map +1 -1
- package/dist/shell-adapter.d.ts +38 -2
- package/dist/shell-adapter.d.ts.map +1 -1
- package/dist/shell-adapter.js +372 -24
- package/dist/shell-adapter.js.map +1 -1
- package/dist/utils.d.ts +3 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +29 -10
- package/dist/utils.js.map +1 -1
- package/dist/web/app.css +12 -7
- package/dist/web/app.js +11 -2
- package/dist/web/index.html +6 -3
- package/dist/web/xterm.css +218 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -7,10 +7,11 @@ opencode 插件:让 opencode 像人一样操作**长驻交互式终端会话**
|
|
|
7
7
|
- **长驻会话**:`term_cli connect` 建立连接后,`term_cli exec` 在同一会话执行命令,保留 cwd/环境/后台进程/sudo 缓存
|
|
8
8
|
- **PTY 交互**:分配伪终端,可处理 sudo 密码、vi、top 等交互程序
|
|
9
9
|
- **权限管控**:只读白名单直接放行、危险命令黑名单硬拒、其余走 `context.ask()` 用户审批
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
10
|
+
- **命令完成判定**:完成标记法(`<SSH_DONE:seq:退出码>`)+ 静默窗口 + 超时三重兜底,动画输出(进度条等)自动识别;未闭合引号/反引号等会触发 shell 续行的命令提交前拦截
|
|
11
|
+
- **双视图**:transcript(命令+输出消息对)与 raw(xterm.js 实时画面,支持 vi/top 等全屏程序);终端尺寸前后端一致固定 `120×40`
|
|
12
|
+
- **历史消息对**:命令+输出 全部存文件(`~/.opencode/plugins-cache/opencode-ssh-tool/<会话>/`),按对数保留(默认 100 对),重启不丢、会话关闭清理
|
|
13
|
+
- **HTTP 终端查看**:默认开启本地服务,浏览器打开可查看终端记录(WebSocket 实时推送,无轮询)
|
|
14
|
+
- **多语言**:`toolLang`(工具描述/CLI/session 文案)与 `webLang`(页面 UI)独立配置,默认英文;环境变量 `SSH_TOOL_LANG` / `SSH_WEB_LANG` 分别覆盖
|
|
14
15
|
|
|
15
16
|
## 安装
|
|
16
17
|
|
|
@@ -59,8 +60,10 @@ term_cli help # 完整用法
|
|
|
59
60
|
// 保留的消息对数上限(默认 100,最小 1)。超出时移除最旧的一对
|
|
60
61
|
"maxMessages": 100
|
|
61
62
|
},
|
|
62
|
-
//
|
|
63
|
-
"toolLang": "en",
|
|
63
|
+
// 工具语言(工具描述 / CLI / agent·session 文案):"en" | "zh"(默认 "en",可用环境变量 SSH_TOOL_LANG 覆盖)
|
|
64
|
+
"toolLang": "en",
|
|
65
|
+
// Web 界面语言(仅浏览器页面 UI 文案):"en" | "zh"(默认 "en",可用环境变量 SSH_WEB_LANG 覆盖)
|
|
66
|
+
"webLang": "en",
|
|
64
67
|
// 权限自定义正则(追加到内置默认,控制"拒绝"与"需审批"命令)
|
|
65
68
|
"permission": {
|
|
66
69
|
// 内置默认危险命令黑名单(命中直接拒绝):
|
|
@@ -79,7 +82,7 @@ term_cli help # 完整用法
|
|
|
79
82
|
}
|
|
80
83
|
```
|
|
81
84
|
|
|
82
|
-
|
|
85
|
+
多语言:`toolLang` 控制工具侧全部文案(工具描述、CLI 输出、agent/session 回传消息),`webLang` 仅控制浏览器页面 UI。优先级均为「环境变量 > 配置文件 > 默认 `en`」:工具用 `SSH_TOOL_LANG`,Web 用 `SSH_WEB_LANG`。
|
|
83
86
|
|
|
84
87
|
权限自定义正则**追加**到内置默认(`DENY` 黑名单 + `ALLOW_READONLY` 白名单),`deny`/`allow` 为空数组时仅用内置默认规则,可自行补充扩展。
|
|
85
88
|
|
|
@@ -115,20 +118,21 @@ term_cli exec(command)
|
|
|
115
118
|
|
|
116
119
|
服务默认开启(配置 `server.enabled`)。`term_cli status` / `term_cli read`(history 模式)会返回实际地址(端口 0 时自动分配,避免冲突):
|
|
117
120
|
|
|
118
|
-
```
|
|
119
|
-
浏览器访问 http://127.0.0.1:<port>
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
121
|
+
```
|
|
122
|
+
浏览器访问 http://127.0.0.1:<port> 查看终端记录(WebSocket 实时推送)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
页面按会话/终端切换,transcript 展示「命令 + 输出」历史对,raw 展示 xterm.js 实时画面。
|
|
123
126
|
|
|
124
127
|
## 架构
|
|
125
128
|
|
|
126
|
-
```
|
|
127
|
-
opencode(插件进程)
|
|
128
|
-
├─
|
|
129
|
-
│
|
|
130
|
-
|
|
131
|
-
|
|
129
|
+
```
|
|
130
|
+
opencode(插件进程)
|
|
131
|
+
├─ agent:每会话多个命名终端,PTY 固定 120×40
|
|
132
|
+
│ ├─ SSH(ssh2 PTY shell)/ 本地·容器(Bun.Terminal)
|
|
133
|
+
│ └─ 完成标记法执行命令(`command ;markerCmd` → `<SSH_DONE:seq:退出码>`)
|
|
134
|
+
├─ server:独立 HTTP 子进程(WS 统一事件流,proto 版本校验)
|
|
135
|
+
└─ web:transcript(消息对)/ raw(xterm.js)双视图,浏览器实时查看
|
|
132
136
|
```
|
|
133
137
|
|
|
134
138
|
## 开发
|
|
@@ -168,8 +172,10 @@ npm publish # 只发布 dist/
|
|
|
168
172
|
|
|
169
173
|
## 文档
|
|
170
174
|
|
|
171
|
-
- `docs/requirements/需求说明.md` — 原始需求
|
|
172
|
-
- `docs/design/方案分析.md` — 技术分析、决策、风险
|
|
173
|
-
- `docs/design/结构设计.md` — 编码依据
|
|
175
|
+
- `docs/requirements/需求说明.md` — 原始需求
|
|
176
|
+
- `docs/design/方案分析.md` — 技术分析、决策、风险
|
|
177
|
+
- `docs/design/结构设计.md` — 编码依据
|
|
178
|
+
- `docs/design/消息协议重构设计.md` — WS 统一事件流协议
|
|
179
|
+
- `docs/design/终端渲染与尺寸固定说明.md` — 终端尺寸固定、续行检测、渲染修复
|
|
174
180
|
|
|
175
181
|
|
package/dist/agent.d.ts
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/** 代理可操作的会话接口(SshSession / LocalSession 子集) */
|
|
2
|
+
export interface AgentSession {
|
|
3
|
+
exec(command: string): Promise<unknown>;
|
|
4
|
+
send(text: string): {
|
|
5
|
+
ok: boolean;
|
|
6
|
+
error?: string;
|
|
7
|
+
};
|
|
8
|
+
close(): void;
|
|
9
|
+
hasRunningStream(): boolean;
|
|
10
|
+
getRunningStream(): {
|
|
11
|
+
data: string;
|
|
12
|
+
done: boolean;
|
|
13
|
+
};
|
|
14
|
+
getRunningCommand(): string;
|
|
15
|
+
/** 设置是否保持 busy(排队序列期间为 true,最后一条完成才清) */
|
|
16
|
+
setHoldBusy(hold: boolean): void;
|
|
17
|
+
/** 设置命令生命周期监听器(cmdStart/cmdDone 事件源) */
|
|
18
|
+
setLifecycle(listener: ((ev: {
|
|
19
|
+
type: "start";
|
|
20
|
+
command: string;
|
|
21
|
+
ts: number;
|
|
22
|
+
} | {
|
|
23
|
+
type: "done";
|
|
24
|
+
exitCode: number | null;
|
|
25
|
+
endTs: number;
|
|
26
|
+
output?: string;
|
|
27
|
+
}) => void) | null): void;
|
|
28
|
+
/** 读取原始字节流增量 */
|
|
29
|
+
readRawStream(pos: number): {
|
|
30
|
+
data: string;
|
|
31
|
+
pos: number;
|
|
32
|
+
reset?: boolean;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** 代理状态 */
|
|
36
|
+
export interface AgentHandle {
|
|
37
|
+
close(): void;
|
|
38
|
+
/** 重新注册当前会话列表(增删会话后调用) */
|
|
39
|
+
reRegister(): void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* 启动代理客户端:连接独立服务并注册会话,处理转发命令
|
|
43
|
+
* @param url 服务 WS 地址(如 ws://127.0.0.1:PORT/ws)
|
|
44
|
+
* @param resolveSession 按 sessionID/name 取会话句柄
|
|
45
|
+
* @param listSessions 当前本进程持有的全部会话(返回 {sessionID, name}[])
|
|
46
|
+
* @returns 代理句柄
|
|
47
|
+
*/
|
|
48
|
+
export declare function startAgent(url: string, resolveSession: (sessionID: string, name: string) => AgentSession | undefined, listSessions: () => Array<{
|
|
49
|
+
sessionID: string;
|
|
50
|
+
name: string;
|
|
51
|
+
}>): AgentHandle;
|
|
52
|
+
//# sourceMappingURL=agent.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAMA,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;IACvC,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,EAAE,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACnD,KAAK,IAAI,IAAI,CAAA;IACb,gBAAgB,IAAI,OAAO,CAAA;IAC3B,gBAAgB,IAAI;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,OAAO,CAAA;KAAE,CAAA;IACnD,iBAAiB,IAAI,MAAM,CAAA;IAC3B,yCAAyC;IACzC,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;IAChC,wCAAwC;IACxC,YAAY,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAC,GAAG,IAAI,GAAG,IAAI,CAAA;IAC/K,gBAAgB;IAChB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAA;CAC3E;AAED,WAAW;AACX,MAAM,WAAW,WAAW;IAC1B,KAAK,IAAI,IAAI,CAAA;IACb,0BAA0B;IAC1B,UAAU,IAAI,IAAI,CAAA;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,YAAY,GAAG,SAAS,EAC7E,YAAY,EAAE,MAAM,KAAK,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7D,WAAW,CA2Mb"}
|
package/dist/agent.js
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
// 代理客户端:插件进程连上独立服务(WS),注册本进程持有的会话,
|
|
2
|
+
// 处理服务转发来的交互命令(run-exec / run-send / run-delete),并把实时流推给服务。
|
|
3
|
+
import log from "./log.js";
|
|
4
|
+
import { INTERACTIVE_BUSY_MS } from "./constants.js";
|
|
5
|
+
/**
|
|
6
|
+
* 启动代理客户端:连接独立服务并注册会话,处理转发命令
|
|
7
|
+
* @param url 服务 WS 地址(如 ws://127.0.0.1:PORT/ws)
|
|
8
|
+
* @param resolveSession 按 sessionID/name 取会话句柄
|
|
9
|
+
* @param listSessions 当前本进程持有的全部会话(返回 {sessionID, name}[])
|
|
10
|
+
* @returns 代理句柄
|
|
11
|
+
*/
|
|
12
|
+
export function startAgent(url, resolveSession, listSessions) {
|
|
13
|
+
let ws = null;
|
|
14
|
+
let closed = false;
|
|
15
|
+
let timer = null;
|
|
16
|
+
const wsUrl = url.startsWith("ws://") ? url : `ws://${url}`;
|
|
17
|
+
const send = (msg) => {
|
|
18
|
+
if (ws && ws.readyState === WebSocket.OPEN)
|
|
19
|
+
ws.send(JSON.stringify(msg));
|
|
20
|
+
};
|
|
21
|
+
const register = () => {
|
|
22
|
+
send({ type: "register", sessions: listSessions() });
|
|
23
|
+
};
|
|
24
|
+
const rawPosMap = new Map();
|
|
25
|
+
/** 已挂接生命周期监听的会话对象(按对象去重:同名会话断开重建是新对象,需重新挂接) */
|
|
26
|
+
const lifeHooked = new WeakSet();
|
|
27
|
+
/** 会话执行队列:命令 busy 时 web 提交的命令排队,当前命令完成后依次执行(key=sessionID:name) */
|
|
28
|
+
const execQueue = new Map();
|
|
29
|
+
/** 各终端最近一次命令提交时间戳(busy 分流:<阈值视为连续命令排队,≥阈值视为交互输入 send) */
|
|
30
|
+
const lastSubmitTs = new Map();
|
|
31
|
+
/** 为某个会话挂接生命周期监听:cmdStart/cmdDone 由 BaseSession 精确发出(web exec 与插件 exec 统一来源) */
|
|
32
|
+
const hookLifecycle = (key, session) => {
|
|
33
|
+
if (lifeHooked.has(session))
|
|
34
|
+
return;
|
|
35
|
+
lifeHooked.add(session);
|
|
36
|
+
const sep = key.indexOf(":");
|
|
37
|
+
const sid = key.slice(0, sep), name = key.slice(sep + 1);
|
|
38
|
+
session.setLifecycle((ev) => {
|
|
39
|
+
if (ev.type === "start") {
|
|
40
|
+
send({ type: "cmdStart", sessionID: sid, name, command: ev.command, ts: ev.ts });
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// 快速命令(生命周期落在单个 tick 内)可能没被 pushStreams 推过 out:
|
|
44
|
+
// done 携带完整输出时补推一次 out(final=true,server 替换而非追加,
|
|
45
|
+
// 避免与已推的增量重复),保证 web 能看到命令输出(不仅 cmdline)
|
|
46
|
+
if (ev.output) {
|
|
47
|
+
send({ type: "out", sessionID: sid, name, data: ev.output, final: true });
|
|
48
|
+
}
|
|
49
|
+
// 排队序列:队列里还有后续命令 → more=true,server 据此保持 busy=true(中间不闪 false)
|
|
50
|
+
const more = (execQueue.get(key)?.length ?? 0) > 0;
|
|
51
|
+
send({ type: "cmdDone", sessionID: sid, name, exitCode: ev.exitCode, endTs: ev.endTs, more });
|
|
52
|
+
// 出队下一条命令由 execQueued 在 promise resolve 后处理(见上),此处不再重复出队
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
/** 执行单条命令;完成后从队列取下一条继续(在 promise resolve 后才出队,
|
|
57
|
+
* 保证上一条的 running context 已清理,出队的下一条不会因 busy 误判被跳过) */
|
|
58
|
+
const execQueued = (sid, name, command) => {
|
|
59
|
+
const session = resolveSession(sid, name);
|
|
60
|
+
if (!session) {
|
|
61
|
+
send({ type: "cmdDone", sessionID: sid, name, exitCode: null, endTs: Date.now() });
|
|
62
|
+
return Promise.resolve();
|
|
63
|
+
}
|
|
64
|
+
const key = `${sid}:${name}`;
|
|
65
|
+
/** 队列是否还有后续命令(排队命令)→ 决定 server 是否保持 busy */
|
|
66
|
+
const hasMore = () => (execQueue.get(key)?.length ?? 0) > 0;
|
|
67
|
+
return session.exec(command).then((result) => {
|
|
68
|
+
// exec 同步等待:正常完成/动画等待均会触发生命周期 done(cmdDone)。
|
|
69
|
+
// 仅 quick-fail(busy/未连接,返回 {ok:false})不会发 done —— 补一条兜底防 server busy 卡死
|
|
70
|
+
if (result && typeof result === "object" && "ok" in result && !result.ok) {
|
|
71
|
+
// 失败(busy/未连接/引号不闭合等):把错误文本作为输出回传,web 端可见失败原因
|
|
72
|
+
const err = result.error;
|
|
73
|
+
if (err)
|
|
74
|
+
send({ type: "out", sessionID: sid, name, data: err, final: true });
|
|
75
|
+
send({ type: "cmdDone", sessionID: sid, name, exitCode: null, endTs: Date.now(), more: hasMore() });
|
|
76
|
+
}
|
|
77
|
+
// 出队下一条:此刻上一条已完成,running context 已清理(resolve 在 lifecycle done 之后)
|
|
78
|
+
const q = execQueue.get(key);
|
|
79
|
+
const next = q?.shift();
|
|
80
|
+
if (q && q.length === 0)
|
|
81
|
+
execQueue.delete(key);
|
|
82
|
+
if (next) {
|
|
83
|
+
log.info(`agent 队列出队执行 ${sid}/${name}: ${next}`);
|
|
84
|
+
return execQueued(sid, name, next);
|
|
85
|
+
}
|
|
86
|
+
// 队列已空:排队序列结束,放开 busy 保持
|
|
87
|
+
resolveSession(sid, name)?.setHoldBusy(false);
|
|
88
|
+
return;
|
|
89
|
+
}).catch((e) => {
|
|
90
|
+
log.error(`代理执行失败 ${sid}/${name}`, e instanceof Error ? e.message : String(e));
|
|
91
|
+
// 命令未运行(异常):补充 cmdDone 兜底(正常完成由生命周期上报,server 幂等)
|
|
92
|
+
send({ type: "cmdDone", sessionID: sid, name, exitCode: null, endTs: Date.now(), more: hasMore() });
|
|
93
|
+
// 异常也应继续出队下一条,避免队列卡死
|
|
94
|
+
const q = execQueue.get(key);
|
|
95
|
+
const next = q?.shift();
|
|
96
|
+
if (q && q.length === 0)
|
|
97
|
+
execQueue.delete(key);
|
|
98
|
+
if (next) {
|
|
99
|
+
log.info(`agent 队列异常后出队 ${sid}/${name}: ${next}`);
|
|
100
|
+
return execQueued(sid, name, next);
|
|
101
|
+
}
|
|
102
|
+
resolveSession(sid, name)?.setHoldBusy(false);
|
|
103
|
+
return;
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const pushStreams = () => {
|
|
107
|
+
for (const { sessionID, name } of listSessions()) {
|
|
108
|
+
const session = resolveSession(sessionID, name);
|
|
109
|
+
const key = `${sessionID}:${name}`;
|
|
110
|
+
if (!session)
|
|
111
|
+
continue;
|
|
112
|
+
hookLifecycle(key, session);
|
|
113
|
+
const raw = session.readRawStream(rawPosMap.get(key) ?? 0);
|
|
114
|
+
if (raw.data) {
|
|
115
|
+
rawPosMap.set(key, raw.pos);
|
|
116
|
+
send({ type: "raw", sessionID, name, data: raw.data, pos: raw.pos, reset: !!raw.reset });
|
|
117
|
+
}
|
|
118
|
+
if (!session.hasRunningStream())
|
|
119
|
+
continue;
|
|
120
|
+
const st = session.getRunningStream();
|
|
121
|
+
if (st.data)
|
|
122
|
+
send({ type: "out", sessionID, name, data: st.data });
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
const connect = () => {
|
|
126
|
+
if (closed)
|
|
127
|
+
return;
|
|
128
|
+
try {
|
|
129
|
+
ws = new WebSocket(wsUrl);
|
|
130
|
+
}
|
|
131
|
+
catch (e) {
|
|
132
|
+
log.error("代理连接服务失败", e instanceof Error ? e.message : String(e));
|
|
133
|
+
setTimeout(connect, 2000);
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
ws.onopen = () => {
|
|
137
|
+
log.info(`代理已连接 ${wsUrl}`);
|
|
138
|
+
register();
|
|
139
|
+
timer = setInterval(pushStreams, 100);
|
|
140
|
+
};
|
|
141
|
+
ws.onmessage = (ev) => {
|
|
142
|
+
let msg;
|
|
143
|
+
try {
|
|
144
|
+
msg = JSON.parse(String(ev.data));
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
const t = msg.type;
|
|
150
|
+
if (!t)
|
|
151
|
+
return;
|
|
152
|
+
if (t === "run-exec") {
|
|
153
|
+
const sid = typeof msg.sessionID === "string" ? msg.sessionID : "";
|
|
154
|
+
const name = typeof msg.name === "string" ? msg.name : "";
|
|
155
|
+
const command = typeof msg.command === "string" ? msg.command : "";
|
|
156
|
+
if (!sid || !command)
|
|
157
|
+
return;
|
|
158
|
+
const session = resolveSession(sid, name);
|
|
159
|
+
if (!session) {
|
|
160
|
+
send({ type: "cmdDone", sessionID: sid, name, exitCode: null, endTs: Date.now() });
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
// web 输入框回车 = 提交命令:空闲立即执行;忙时按距上次提交间隔分流——
|
|
164
|
+
// 间隔 < INTERACTIVE_BUSY_MS(连续快速命令,模型连续多行提交)→ 排队依次执行;
|
|
165
|
+
// 间隔 ≥ 阈值仍 busy(用户给运行中的交互程序输入,如 sudo 密码/REPL)→ 原样 send 交程序。
|
|
166
|
+
// 不再用 hasRunningStream() 单点猜测——快速命令窗口内到达的后续命令会被误判为交互输入而裸执行。
|
|
167
|
+
const key = `${sid}:${name}`;
|
|
168
|
+
const now = Date.now();
|
|
169
|
+
const last = lastSubmitTs.get(key) ?? 0;
|
|
170
|
+
lastSubmitTs.set(key, now);
|
|
171
|
+
// 排队序列执行中(队列非空):新命令排队,等序列结束再执行;
|
|
172
|
+
// 不能走 send(那是给交互程序的输入),也不能直接 exec(hold busy 会 quick-fail)
|
|
173
|
+
if ((execQueue.get(key)?.length ?? 0) > 0) {
|
|
174
|
+
execQueue.get(key).push(command);
|
|
175
|
+
log.info(`agent run-exec 序列中排队 ${sid}/${name}: ${command} (队列 ${execQueue.get(key).length})`);
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (session.hasRunningStream()) {
|
|
179
|
+
if (now - last < INTERACTIVE_BUSY_MS) {
|
|
180
|
+
const q = execQueue.get(key);
|
|
181
|
+
if (q)
|
|
182
|
+
q.push(command);
|
|
183
|
+
else
|
|
184
|
+
execQueue.set(key, [command]);
|
|
185
|
+
log.info(`agent run-exec 排队 ${sid}/${name}: ${command} (队列 ${(execQueue.get(key) ?? []).length})`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
log.info(`agent run-exec busy 超阈值按交互输入 ${sid}/${name}: ${command}`);
|
|
189
|
+
session.send(command + "\r");
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
log.info(`agent run-exec 直接执行 ${sid}/${name}: ${command}`);
|
|
193
|
+
void execQueued(sid, name, command);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (t === "run-send") {
|
|
197
|
+
const sid = typeof msg.sessionID === "string" ? msg.sessionID : "";
|
|
198
|
+
const name = typeof msg.name === "string" ? msg.name : "";
|
|
199
|
+
const text = typeof msg.text === "string" ? msg.text : "";
|
|
200
|
+
const session = resolveSession(sid, name);
|
|
201
|
+
if (session) {
|
|
202
|
+
lastSubmitTs.set(`${sid}:${name}`, Date.now());
|
|
203
|
+
session.send(text);
|
|
204
|
+
}
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (t === "run-delete") {
|
|
208
|
+
const sid = typeof msg.sessionID === "string" ? msg.sessionID : "";
|
|
209
|
+
const name = typeof msg.name === "string" ? msg.name : "";
|
|
210
|
+
const session = resolveSession(sid, name);
|
|
211
|
+
if (session)
|
|
212
|
+
session.close();
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
ws.onclose = () => {
|
|
217
|
+
if (timer) {
|
|
218
|
+
clearInterval(timer);
|
|
219
|
+
timer = null;
|
|
220
|
+
}
|
|
221
|
+
if (closed)
|
|
222
|
+
return;
|
|
223
|
+
log.info("代理连接断开,2s 后重连");
|
|
224
|
+
setTimeout(connect, 2000);
|
|
225
|
+
};
|
|
226
|
+
ws.onerror = () => { };
|
|
227
|
+
};
|
|
228
|
+
connect();
|
|
229
|
+
return {
|
|
230
|
+
close: () => {
|
|
231
|
+
closed = true;
|
|
232
|
+
if (timer) {
|
|
233
|
+
clearInterval(timer);
|
|
234
|
+
timer = null;
|
|
235
|
+
}
|
|
236
|
+
try {
|
|
237
|
+
ws?.close();
|
|
238
|
+
}
|
|
239
|
+
catch { /* ignore */ }
|
|
240
|
+
},
|
|
241
|
+
reRegister: register,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
//# sourceMappingURL=agent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/base-session.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { SessionHistory } from "./history.js";
|
|
2
2
|
import { type ShellAdapter } from "./shell-adapter.js";
|
|
3
|
+
import { type Lang } from "./i18n.js";
|
|
3
4
|
/** 命令执行结果 */
|
|
4
5
|
export interface ExecResult {
|
|
5
6
|
ok: boolean;
|
|
@@ -12,24 +13,47 @@ export interface ExecResult {
|
|
|
12
13
|
command?: string;
|
|
13
14
|
duration?: number;
|
|
14
15
|
}
|
|
16
|
+
/** 生命周期事件(命令开始/完成),供 agent 转报 server(web 与插件自身 exec 统一来源) */
|
|
17
|
+
export type LifecycleEvent = {
|
|
18
|
+
type: "start";
|
|
19
|
+
command: string;
|
|
20
|
+
ts: number;
|
|
21
|
+
} | {
|
|
22
|
+
type: "done";
|
|
23
|
+
exitCode: number | null;
|
|
24
|
+
endTs: number;
|
|
25
|
+
output?: string;
|
|
26
|
+
};
|
|
15
27
|
/** 会话命令执行与读取的公共实现 */
|
|
16
28
|
export declare abstract class BaseSession {
|
|
17
29
|
protected readonly sessionID: string;
|
|
18
30
|
protected readonly name: string;
|
|
31
|
+
protected readonly _lang: Lang;
|
|
19
32
|
protected _connected: boolean;
|
|
20
33
|
protected _remoteBusy: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* 是否保持 busy:排队命令顺序执行时,中间某条完成也不清 busy,
|
|
36
|
+
* 直到序列最后一条完成(否则 web/status 会在中间命令间隙闪 busy=false)。
|
|
37
|
+
*/
|
|
38
|
+
private _holdBusy;
|
|
21
39
|
protected _buffer: string;
|
|
22
40
|
protected _connectedAt: number;
|
|
23
41
|
protected _lastActive: number;
|
|
24
42
|
protected _runningStartPos: number | null;
|
|
25
43
|
protected _runningCommand: string;
|
|
26
44
|
protected _watchTimer: ReturnType<typeof setInterval> | null;
|
|
45
|
+
/** 命令序号(完成标记 <SSH_DONE:seq:code> 标识本次命令,防残留误判) */
|
|
46
|
+
private _cmdSeq;
|
|
47
|
+
/** 当前命令序号(_beginCapture 递增,供检测/拼接用) */
|
|
48
|
+
private _runningSeq;
|
|
27
49
|
/** 下次捕获窗口起点(相对当前 buffer):前一条命令结束后,标记/残留从此处开始 */
|
|
28
50
|
protected _cursor: number;
|
|
29
51
|
/** WS 实时流已发送位置(相对 buffer 偏移);null = 尚未定位命令回显结束点 */
|
|
30
52
|
protected _streamPos: number | null;
|
|
31
53
|
/** Shell 适配器(探测后确定) */
|
|
32
54
|
protected _adapter: ShellAdapter | null;
|
|
55
|
+
/** 多行命令写入 PTY 的行分隔符:Unix PTY 用 \n;Windows ConPTY(本地会话)用 \r(裸 \n 不提交行) */
|
|
56
|
+
protected _lineSep: string;
|
|
33
57
|
protected _closed: boolean;
|
|
34
58
|
/** 当前运行命令的输入时刻(history 记录展示命令发起时间用) */
|
|
35
59
|
private _runningStartTs;
|
|
@@ -40,15 +64,22 @@ export declare abstract class BaseSession {
|
|
|
40
64
|
/** 累积原始字节总数(单调递增,前端增量游标) */
|
|
41
65
|
private _rawTotal;
|
|
42
66
|
protected readonly _history: SessionHistory;
|
|
43
|
-
|
|
67
|
+
/** 生命周期监听器(命令开始/完成事件,agent 转报 server) */
|
|
68
|
+
private _lifecycle;
|
|
69
|
+
/**
|
|
70
|
+
* 本次命令期间是否真实发送过中断(Ctrl-C)。
|
|
71
|
+
* 中断判定不依赖"命令回显定位":超宽命令被 readline 折行重绘时回显会多出字符,
|
|
72
|
+
* 精确匹配必然失败(echoEnd=0),若再以 echoEnd 为前置条件则 ^C 永远检测不到 → busy 卡死。
|
|
73
|
+
* 仅当确实发过 ^C 才去缓冲区找 ^C 回显,天然排除历史残留 ^C 的误判。
|
|
74
|
+
*/
|
|
75
|
+
private _interruptSent;
|
|
76
|
+
constructor(sessionID: string, history: SessionHistory, name?: string, _lang?: Lang);
|
|
44
77
|
/** 向传输层写入字节(子类实现:SSH 写 stream / 本地写 Bun.Terminal) */
|
|
45
78
|
protected abstract _write(data: string): void;
|
|
46
79
|
/** 关闭传输层资源(子类实现:SSH 关 client+stream / 本地关 term+proc) */
|
|
47
80
|
protected abstract _closeTransport(): void;
|
|
48
81
|
/** 结果中的类型专属字段(SSH 附加 host,本地为空) */
|
|
49
82
|
protected get _extraResult(): Partial<ExecResult>;
|
|
50
|
-
/** 忙碌提示中的状态命令名(ssh_status / local_status) */
|
|
51
|
-
protected abstract _statusCmd: string;
|
|
52
83
|
/**
|
|
53
84
|
* 异步提交命令:立即返回,命令后台执行,输出由后台监听收集进 history
|
|
54
85
|
* @param command 命令
|
|
@@ -83,6 +114,21 @@ export declare abstract class BaseSession {
|
|
|
83
114
|
getHistory(): SessionHistory;
|
|
84
115
|
getRunningOutput(): string;
|
|
85
116
|
getRunningCommand(): string;
|
|
117
|
+
/**
|
|
118
|
+
* 设置命令生命周期监听器(命令开始/完成时回调)
|
|
119
|
+
* @param listener 回调(start 携带命令,done 携带退出码与结束时刻;null 表示清除)
|
|
120
|
+
*/
|
|
121
|
+
setLifecycle(listener: ((ev: LifecycleEvent) => void) | null): void;
|
|
122
|
+
/**
|
|
123
|
+
* 设置是否保持 busy(排队命令序列期间为 true):
|
|
124
|
+
* true 时中间命令完成不清 busy;置 false 时立即清 busy。
|
|
125
|
+
* @param hold 是否保持
|
|
126
|
+
*/
|
|
127
|
+
setHoldBusy(hold: boolean): void;
|
|
128
|
+
/** 触发命令开始事件 */
|
|
129
|
+
private _emitStart;
|
|
130
|
+
/** 触发命令完成事件 */
|
|
131
|
+
private _emitDone;
|
|
86
132
|
hasRunningStream(): boolean;
|
|
87
133
|
/**
|
|
88
134
|
* 增量取运行中命令的实时输出(WS 推流用):
|
|
@@ -103,24 +149,60 @@ export declare abstract class BaseSession {
|
|
|
103
149
|
};
|
|
104
150
|
/** 是否连接且传输层就绪(子类检查自身传输对象) */
|
|
105
151
|
protected abstract _ready(): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* 本会话当前命令的完成标记命令(adapter 负责各自的标记语法,无 adapter 时用 POSIX 默认)
|
|
154
|
+
* @param seq 命令序号
|
|
155
|
+
* @returns 标记命令文本
|
|
156
|
+
*/
|
|
157
|
+
private _markerCmd;
|
|
158
|
+
/**
|
|
159
|
+
* 组合实际写入 PTY 的命令:在规范形式(`_composeEchoText`)基础上把换行改写为会话行分隔符并补尾部回车。
|
|
160
|
+
* 行分隔符按会话类型:Unix PTY 用 `\n`(正常提交行),Windows ConPTY 用 `\r`(实测裸 `\n` 不提交行,
|
|
161
|
+
* pwsh 会停在续行 `>>`、行序错乱)。
|
|
162
|
+
* @param command 原始命令(history 存干净版本)
|
|
163
|
+
* @returns 实际写入 PTY 的文本(尾部带 \r,行分隔已按会话类型规范化)
|
|
164
|
+
*/
|
|
165
|
+
private _composeCommand;
|
|
166
|
+
/**
|
|
167
|
+
* 组合命令文本(规范/匹配用形式,始终以 `\n` 分隔行、无尾部回车):命令 + 完成标记。
|
|
168
|
+
* 该形式供 `extractOutputStart` 按 `\n` 切行并做段间宽松匹配(跳过 `>>` 续行提示),
|
|
169
|
+
* 故不可改写为 `_lineSep`——否则本地会话(`\r`)下整段回显变一行、段间匹配失效、回显剥离失败。
|
|
170
|
+
* 完成标记用当前 shell 的语句分隔符同行拼接(POSIX/pwsh 为 `;`,cmd 为 `&`),免疫 prompt 框架覆盖:
|
|
171
|
+
* `python; printf ...` 中 printf 由 shell 在 python 退出后执行,不会被 REPL 当 stdin 消费
|
|
172
|
+
* (实测 top/read/python 均正常出 marker)。
|
|
173
|
+
* 多行命令用 shell 组语法(groupWrap)包裹成**一条**命令:一次输入 = 一条命令 = 一段输出,
|
|
174
|
+
* 避免 shell 逐行执行导致回显与输出交错、中间输出被 extractOutputStart 丢掉
|
|
175
|
+
* (如 `cd /tmp` 之后的 `echo AAA`/`echo BBB` 输出丢失);heredoc 分隔符行必须独占一行,
|
|
176
|
+
* 若标记直接拼上去(`EOF ;printf ...`)会让分隔符失效 → heredoc 永不结束 → 卡死,
|
|
177
|
+
* 故含 heredoc 的多行命令也必须包组。
|
|
178
|
+
* cmd 无法安全包组(组内 `%errorlevel%` 为解析期展开、退出码失真)或无 adapter 时回退原样拼接。
|
|
179
|
+
* 交互程序(top/vi/read/python 等)期间保留 running 上下文与 busy,marker 出现即判定完成。
|
|
180
|
+
* @param command 原始命令
|
|
181
|
+
* @returns 命令 + 标记文本(规范 `\n` 形式,无尾部回车)
|
|
182
|
+
*/
|
|
183
|
+
private _composeEchoText;
|
|
184
|
+
/**
|
|
185
|
+
* 提取命令纯输出:从命令回显后切到完成标记(无标记则切到中断回显 ^C 处),
|
|
186
|
+
* 顺带去掉追加的标记命令回显行。
|
|
187
|
+
* 回显定位使用组合命令文本(command + 标记段)而非裸命令:裸命令可能出现在程序错误输出中
|
|
188
|
+
* (如 `pw` 的错误行 `bash: pw: command not found`),而组合命令整行回显是唯一的,
|
|
189
|
+
* 可避免 last-match 误命中错误行导致输出被裁空。
|
|
190
|
+
* @param raw 本次执行窗口原始字节流
|
|
191
|
+
* @param command 原始命令(历史用)
|
|
192
|
+
* @returns 纯程序输出原始流
|
|
193
|
+
*/
|
|
194
|
+
private _extractOutput;
|
|
106
195
|
/** 命令执行/提交前准备:裁剪已消费缓冲 + 丢弃上一条命令的迟到残留标记 */
|
|
107
196
|
private _beginCapture;
|
|
108
197
|
/** 追加输出字节:维护业务缓冲 + 连续原始流 ring */
|
|
109
198
|
protected _appendBuffer(text: string): void;
|
|
110
|
-
/** 探测 shell
|
|
199
|
+
/** 探测 shell 类型:按各适配器 probeCommand 逐个探测(POSIX 系优先,pwsh/cmd 用各自独有命令),
|
|
200
|
+
* login shell 的 $0 带 - 前缀(-bash/-zsh)由对应 parseProbe 容忍 */
|
|
111
201
|
protected _probeShell(deadline: number): Promise<ShellAdapter | null>;
|
|
112
202
|
/** 等待探测输出稳定:buffer 有内容且 500ms 不再变化则返回,超时返回 null */
|
|
113
203
|
private _waitProbeOutput;
|
|
114
|
-
/**
|
|
115
|
-
* deadline 内循环注入+等待确认,成功返回 true,超时返回 false
|
|
116
|
-
* 逐行发送避免因 shell 未就绪导致的整块丢失;每轮等待最大 3s
|
|
117
|
-
* 注入命令原样回显(不抑制),确认以 echo 唯一标识为准,避免与后续命令时序混淆
|
|
118
|
-
*/
|
|
119
|
-
protected _injectAndSettle(deadline: number): Promise<boolean>;
|
|
120
|
-
/** 等待缓冲中出现指定文本(最多 maxMs) */
|
|
121
|
-
private _waitForBufferToken;
|
|
122
204
|
private _waitCompletion;
|
|
123
|
-
/** 后台监听:轮询 done 标记 → 收集输出进 history + busy=false */
|
|
205
|
+
/** 后台监听:轮询 done 标记 / ^C 中断 → 收集输出进 history + busy=false */
|
|
124
206
|
private _startBackgroundWatch;
|
|
125
207
|
protected _clearRunningContext(): void;
|
|
126
208
|
private _truncate;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base-session.d.ts","sourceRoot":"","sources":["../src/base-session.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"base-session.d.ts","sourceRoot":"","sources":["../src/base-session.ts"],"names":[],"mappings":"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"}
|