terminal-bridge-setup 3.3.3 → 3.3.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/bin/setup.mjs
CHANGED
|
@@ -128,8 +128,13 @@ function installProxyDeps() {
|
|
|
128
128
|
const registryArg = process.argv.find(a => a.startsWith("--registry="));
|
|
129
129
|
const baseArgs = ["install", "--no-audit", "--no-fund"];
|
|
130
130
|
const runNpm = (args) => {
|
|
131
|
-
|
|
132
|
-
|
|
131
|
+
// Windows 的 npm 是 npm.cmd 批处理,spawn 不带 shell 无法启动
|
|
132
|
+
// (表现为"运行 npm install ..."后无任何输出直接失败,实测踩过)。
|
|
133
|
+
// shell:true 下走 cmd 解析;registry 参数先过白名单校验防 shell 注入。
|
|
134
|
+
const safeArgs = args.filter(a =>
|
|
135
|
+
!a.startsWith("--registry=") || /^--registry=https?:\/\/[\w.\-/:]+$/.test(a));
|
|
136
|
+
console.log(c.dim(` 运行 npm ${safeArgs.join(" ")} ...`));
|
|
137
|
+
return spawnSync("npm", safeArgs, { cwd: proxyDir, stdio: "inherit", shell: true });
|
|
133
138
|
};
|
|
134
139
|
|
|
135
140
|
let result;
|
|
@@ -147,9 +152,12 @@ function installProxyDeps() {
|
|
|
147
152
|
}
|
|
148
153
|
}
|
|
149
154
|
|
|
150
|
-
if (result.status !== 0) {
|
|
151
|
-
|
|
152
|
-
|
|
155
|
+
if (result.status !== 0 || result.error) {
|
|
156
|
+
if (result.error) {
|
|
157
|
+
console.error(c.red(" ✗ 无法启动 npm:" + result.error.message));
|
|
158
|
+
console.error(c.yellow(` 手动修复:cd "${join(INSTALL_DIR, "proxy")}" && npm install`));
|
|
159
|
+
fail("npm 无法启动");
|
|
160
|
+
}
|
|
153
161
|
console.error(c.red(" ✗ npm install 失败"));
|
|
154
162
|
console.error(c.yellow(" 常见原因和解决方法:"));
|
|
155
163
|
console.error("");
|
|
@@ -777,9 +777,22 @@ function clickDropdownOption(target) {
|
|
|
777
777
|
});
|
|
778
778
|
// 用与编排一致的定位逻辑回看"到底选中了哪个节点"
|
|
779
779
|
const trigEl = findDbTrigger();
|
|
780
|
+
// 编辑器现场取证:焦点在哪、每个 monaco 的可见性与内容长度
|
|
781
|
+
const ae = document.activeElement;
|
|
782
|
+
const editors = [...document.querySelectorAll(".monaco-editor")].map(m => {
|
|
783
|
+
const r = m.getBoundingClientRect();
|
|
784
|
+
return {
|
|
785
|
+
visible: m.offsetParent !== null && r.width > 0 && r.height > 0,
|
|
786
|
+
rect: { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width), h: Math.round(r.height) },
|
|
787
|
+
textLen: (m.querySelector(".view-lines")?.textContent || "").length,
|
|
788
|
+
inputareaFocused: document.activeElement === m.querySelector("textarea.inputarea"),
|
|
789
|
+
};
|
|
790
|
+
});
|
|
780
791
|
sendResponse({
|
|
781
792
|
ok: true,
|
|
782
793
|
hash: location.hash,
|
|
794
|
+
activeElement: ae ? { cls: String(ae.className || "").slice(0, 80), id: ae.id || "", tag: ae.tagName } : null,
|
|
795
|
+
editors,
|
|
783
796
|
selects: [...document.querySelectorAll(".ant-select")].map(selectDump),
|
|
784
797
|
triggerLocated: trigEl ? selectDump(trigEl) : null,
|
|
785
798
|
metaNow: readYearningMeta(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "JumpServer 终端桥接",
|
|
4
|
-
"version": "3.3.
|
|
4
|
+
"version": "3.3.5",
|
|
5
5
|
"description": "终端桥接:JumpServer / Arthas / Yearning,让 Agent 能通过浏览器执行命令、收结果与查数据",
|
|
6
6
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtsnqR6PcFUueZwYria79tVbstvjk+tM7PpvIXILm5xbd6bAdjDIhzg3lsnKioVfvxjfvT+s6vJsiOYa9ojVZyJMFc5m/05TYqr770ovYwQmz0e88fmiy6dUoSulbtKvBCSLbN6OOL7u+ul8ixLZ/HautxSmou/eNgAFPmhE+4UueE7wfCqcgMYvjLvEzlqTVumMW+5LKw9YsRk6WhHPghY1a3MVUn3eQOWXBtQTEUy3wBM3v4wHxLwDeinVOR4f/P87IlUNo84C5DeimoFit0qCj3K04hS8MIYCLCYZc3v9ftRJDJBkAoah6Eaqj7JajbS3KvLR1ctOYfDhubgmURQIDAQAB",
|
|
7
7
|
"permissions": ["debugger", "tabs", "alarms", "webNavigation", "nativeMessaging", "scripting", "downloads", "storage"],
|
package/files/proxy/server.js
CHANGED
|
@@ -166,7 +166,12 @@ function feedYearningWaiters(payload) {
|
|
|
166
166
|
try {
|
|
167
167
|
obj = msgpackDecode(Buffer.from(data, "base64"));
|
|
168
168
|
} catch { return; }
|
|
169
|
-
if (!obj
|
|
169
|
+
if (!obj) return;
|
|
170
|
+
// 两类有效帧:正常结果(results 非空)/ SQL 执行报错(error 非空,results 为空)。
|
|
171
|
+
// 错误帧不接住的话 waiter 只能干等超时,Agent 无从得知 SQL 写错了
|
|
172
|
+
const hasResults = obj.results != null;
|
|
173
|
+
const hasError = typeof obj.error === "string" && obj.error.trim().length > 0;
|
|
174
|
+
if (!hasResults && !hasError) return; // 心跳等噪音帧
|
|
170
175
|
|
|
171
176
|
let consumed = false;
|
|
172
177
|
if (yrRunWaiters.size > 0) {
|
|
@@ -181,8 +186,8 @@ function feedYearningWaiters(payload) {
|
|
|
181
186
|
}
|
|
182
187
|
|
|
183
188
|
// 未被 yr-run 消费的结果帧 = 手动查询(或 yr-run 已完成后的重复帧),
|
|
184
|
-
// 也生成 CSV
|
|
185
|
-
if (!consumed) {
|
|
189
|
+
// 也生成 CSV 记录。错误帧没有数据,跳过导出。
|
|
190
|
+
if (!consumed && hasResults) {
|
|
186
191
|
const rows = Array.isArray(obj.results)
|
|
187
192
|
? obj.results.map(t => (t && t.data ? t.data.length : 0)).reduce((a, b) => a + b, 0)
|
|
188
193
|
: 0;
|
|
@@ -251,6 +256,18 @@ async function handleYrRun(ws, msg) {
|
|
|
251
256
|
settled = true;
|
|
252
257
|
yrRunWaiters.delete(runEntry);
|
|
253
258
|
clearTimeout(timer);
|
|
259
|
+
// SQL 执行报错帧:把数据库错误原文带回给 Agent(不导 CSV,无数据可导)
|
|
260
|
+
const sqlError = typeof obj.error === "string" ? obj.error.trim() : "";
|
|
261
|
+
if (sqlError) {
|
|
262
|
+
console.log(TAG, `[yr-run ${reqId}] SQL 执行报错: ${sqlError.slice(0, 200)}`);
|
|
263
|
+
ws.send(JSON.stringify({
|
|
264
|
+
type: "result", reqId, ok: false, error: "sql-error",
|
|
265
|
+
message: "SQL 被数据库拒绝:" + sqlError,
|
|
266
|
+
output: JSON.stringify(obj),
|
|
267
|
+
elapsedMs: Date.now() - sentAt,
|
|
268
|
+
}));
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
254
271
|
console.log(TAG, `[yr-run ${reqId}] 结果帧到达(query_time=${obj.query_time ?? "?"})`);
|
|
255
272
|
// 同步发给插件:浏览器侧生成 CSV 落下载(popup 可见、可重新下载)
|
|
256
273
|
sendToExtension({
|
package/files/skill/SKILL.md
CHANGED
|
@@ -334,6 +334,8 @@ Agent → 代理:
|
|
|
334
334
|
| 现象 | 原因 | 处理 |
|
|
335
335
|
|------|------|------|
|
|
336
336
|
| `error: extension not connected` | 代理没收到插件 hello | 检查插件是否启用、service worker 日志、代理是否在跑 |
|
|
337
|
+
| `error: write-forbidden` | Yearning 只读白名单拦截(非 SELECT/SHOW/DESC/EXPLAIN) | 修正为只读语句重试;不要试图混淆绕过 |
|
|
338
|
+
| `error: sql-error` | **SQL 被数据库拒绝**(message 带原文,如 `Table 'xxx' doesn't exist`、权限不足) | 按 message 修正 SQL 后重试;秒回不是超时 |
|
|
337
339
|
| `error: sudo-required` | 命令触发 sudo 密码提示(alias 劫持,仅 JumpServer) | 按"sudo 自动重试"流程问用户是否切 root,确认后切 root 重发原命令 |
|
|
338
340
|
| `error: arthas-forbidden` | Arthas 高风险命令(retransform/profiler/stop/reset)被禁用 | 告知用户去浏览器 Arthas 终端手动执行,不要重试或绕过 |
|
|
339
341
|
| `error: arthas-needs-limit` | 中风险命令(trace/watch/stack/monitor)缺 -n/#cost(严格模式) | 按 suggest 补参数重发,或加 `-n 1` |
|
|
@@ -50,6 +50,10 @@ WebSocket 结果帧。Yearning 结果帧是 opcode=2 二进制帧,解码后通
|
|
|
50
50
|
SELECT/SHOW/DESC/EXPLAIN;命中 INTO OUTFILE/DUMPFILE/FOR UPDATE 等副作用子句
|
|
51
51
|
同样拒绝。失败返回 `ok:false, error:"write-forbidden"`。yr-set 路径同受约束。
|
|
52
52
|
|
|
53
|
+
**SQL 执行报错回传**:数据库拒绝 SQL 时(表不存在/权限不足等),Yearning 的
|
|
54
|
+
错误帧会被捕获并立即返回 `ok:false, error:"sql-error", message:"SQL 被数据库拒绝:<原文>"`,
|
|
55
|
+
不会等到超时。Agent 应按 message 修正 SQL 重试。
|
|
56
|
+
|
|
53
57
|
prepare 模式示例:
|
|
54
58
|
|
|
55
59
|
```json
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "terminal-bridge-setup",
|
|
3
|
-
"version": "3.3.
|
|
3
|
+
"version": "3.3.5",
|
|
4
4
|
"description": "一次性安装器:释放终端桥接(JumpServer / Arthas / Yearning)的本地代理 + Chrome 插件,并注册 native messaging host。让 Agent 能通过浏览器 xterm 终端执行命令并拿回输出。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "encorearon",
|