terminal-bridge-setup 3.0.0 → 3.1.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.
|
@@ -135,6 +135,11 @@ chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
|
|
|
135
135
|
});
|
|
136
136
|
return true; // 异步
|
|
137
137
|
}
|
|
138
|
+
if (msg.type === "CSV_CLEAR") {
|
|
139
|
+
chrome.storage.session.remove(CSV_STORAGE_KEY).catch(() => {});
|
|
140
|
+
sendResponse({ ok: true });
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
138
143
|
if (msg.type === "CSV_DOWNLOAD") {
|
|
139
144
|
getCsvExports().then(list => {
|
|
140
145
|
const record = list.find(e => e.id === msg.id);
|
|
@@ -688,15 +693,27 @@ function resultJsonToCsv(jsonText) {
|
|
|
688
693
|
return "\uFEFF" + [headers.join(","), ...rows].join("\r\n") + "\r\n";
|
|
689
694
|
}
|
|
690
695
|
|
|
696
|
+
// 文件名:表名_库_数据源_日期。表名从 SQL 提取(from/into/update 后的词),
|
|
697
|
+
// 库和数据源从目标 tab 的 meta(tapTabMeta)取。
|
|
698
|
+
function buildCsvFileName(sql, tabId) {
|
|
699
|
+
const sqlText = sql || "";
|
|
700
|
+
const tableMatch = sqlText.match(/\b(?:from|into|update|join)\s+[`"]?(\w+)[`"]?/i);
|
|
701
|
+
const table = (tableMatch ? tableMatch[1] : "query").slice(0, 40);
|
|
702
|
+
const meta = tapTabMeta.get(tabId) || {};
|
|
703
|
+
const database = (meta.database || "").replace(/[^\w.-]+/g, "") || "nodb";
|
|
704
|
+
const source = (meta.dataSource || "").split(" · ")[0].replace(/[^\w.-]+/g, "") || "nosrc";
|
|
705
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
706
|
+
const time = new Date().toTimeString().slice(0, 5).replace(":", "");
|
|
707
|
+
return `${table}_${database}_${source}_${date}_${time}.csv`;
|
|
708
|
+
}
|
|
709
|
+
|
|
691
710
|
async function handleYrExportCsv(frame) {
|
|
692
711
|
const csv = resultJsonToCsv(frame.payload || "");
|
|
693
712
|
if (!csv) {
|
|
694
713
|
console.warn("[bg] yr-export-csv: 结果 JSON 解析失败或无表结构");
|
|
695
714
|
return;
|
|
696
715
|
}
|
|
697
|
-
const
|
|
698
|
-
const sqlHead = (frame.sql || "query").slice(0, 30).replace(/[^\w-]+/g, "_");
|
|
699
|
-
const name = `yearning-${sqlHead}-${stamp}.csv`;
|
|
716
|
+
const name = buildCsvFileName(frame.sql, frame.tabId);
|
|
700
717
|
const dataUrl = "data:text/csv;charset=utf-8," + encodeURIComponent(csv);
|
|
701
718
|
await addCsvExport({
|
|
702
719
|
id: Date.now(),
|
|
@@ -200,10 +200,14 @@
|
|
|
200
200
|
<div class="hint" id="wsTapStatus">未监听 Yearning 页面</div>
|
|
201
201
|
<div id="yearningTabList" style="margin-top:6px;"></div>
|
|
202
202
|
<div id="csvSection" style="margin-top:8px;display:none;">
|
|
203
|
-
<div
|
|
203
|
+
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:4px;">
|
|
204
|
+
<span class="section-title" style="margin:0;">CSV 导出记录</span>
|
|
205
|
+
<button class="copy-btn" id="btnCsvClear" style="background:#e53935;">清空</button>
|
|
206
|
+
</div>
|
|
204
207
|
<div id="csvList"></div>
|
|
205
|
-
<div class="hint"
|
|
208
|
+
<div class="hint">点击记录下载 · 表名_库_数据源_日期.csv</div>
|
|
206
209
|
</div>
|
|
210
|
+
<button class="scan" id="btnCopyPrompt" style="margin-top:8px;background:#7c4dff;">📋 复制 Agent 使用 Prompt</button>
|
|
207
211
|
</div>
|
|
208
212
|
|
|
209
213
|
<div class="section">
|
package/files/extension/popup.js
CHANGED
|
@@ -144,8 +144,10 @@ const csvList = document.getElementById('csvList');
|
|
|
144
144
|
function refreshCsvList() {
|
|
145
145
|
chrome.runtime.sendMessage({ type: 'CSV_LIST' }, (res) => {
|
|
146
146
|
if (chrome.runtime.lastError || !res || !res.ok) return;
|
|
147
|
-
const
|
|
148
|
-
csvSection.style.display =
|
|
147
|
+
const all = res.exports || [];
|
|
148
|
+
csvSection.style.display = all.length ? 'block' : 'none';
|
|
149
|
+
// 只显示最近 5 条,popup 不超长(存储仍保留 20 条)
|
|
150
|
+
const exports = all.slice(0, 5);
|
|
149
151
|
csvList.innerHTML = exports.map(e => {
|
|
150
152
|
const time = new Date(e.time).toLocaleTimeString();
|
|
151
153
|
return `<div class="csv-item" data-id="${e.id}" title="${escapeHtml(e.sql || e.name)}">
|
|
@@ -166,6 +168,41 @@ function refreshCsvList() {
|
|
|
166
168
|
}
|
|
167
169
|
refreshCsvList();
|
|
168
170
|
|
|
171
|
+
// 清空 CSV 导出记录
|
|
172
|
+
document.getElementById('btnCsvClear').onclick = () => {
|
|
173
|
+
chrome.runtime.sendMessage({ type: 'CSV_CLEAR' }, () => refreshCsvList());
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
// 复制 Agent 使用 Prompt(粘给任意 AI 助手即可用桥接查 Yearning)
|
|
177
|
+
const AGENT_PROMPT = `你的机器上已装好 Terminal Bridge(Yearning SQL 桥接)。请按以下方式查询数据库:
|
|
178
|
+
|
|
179
|
+
## 查询(Yearning)
|
|
180
|
+
cd ~/.terminal-bridge/proxy && node yr-example.mjs "SELECT ...;" [超时ms]
|
|
181
|
+
|
|
182
|
+
返回 JSON:{ ok, output(结果 JSON: field 列定义 + data 数据行), error }。
|
|
183
|
+
|
|
184
|
+
## 说明
|
|
185
|
+
- SQL 会自动注入到用户浏览器里选中的 Yearning 页面并点「查询」,结果自动返回
|
|
186
|
+
- 前置:用户已在 Yearning 页面选择数据库,且插件 popup 已「监听当前 Yearning 页」
|
|
187
|
+
- 多个 Yearning 页面时:用户在 popup 列表点选目标页面(显示 数据源 · 数据库)
|
|
188
|
+
- 报 database-not-selected = 页面未选数据库,请让用户先选
|
|
189
|
+
- 报 timeout = 查询超时或页面未监听,让用户确认 popup 状态
|
|
190
|
+
- 只读查询即可;每次查询的结果会自动出现在插件 popup 的「CSV 导出记录」里供用户下载
|
|
191
|
+
|
|
192
|
+
## 终端命令(JumpServer/Arthas,同一代理)
|
|
193
|
+
cd ~/.terminal-bridge/proxy && node client-example.mjs "linux 命令" [超时ms]
|
|
194
|
+
多层引号命令加 --b64 参数下发。`;
|
|
195
|
+
|
|
196
|
+
document.getElementById('btnCopyPrompt').onclick = () => {
|
|
197
|
+
navigator.clipboard.writeText(AGENT_PROMPT).then(() => {
|
|
198
|
+
const btn = document.getElementById('btnCopyPrompt');
|
|
199
|
+
btn.textContent = '✓ 已复制,粘贴给 Agent 即可';
|
|
200
|
+
setTimeout(() => { btn.textContent = '📋 复制 Agent 使用 Prompt'; }, 2000);
|
|
201
|
+
}).catch(() => {
|
|
202
|
+
console.warn('复制失败');
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
|
|
169
206
|
// ============== 代理控制 ==============
|
|
170
207
|
const proxyDot = document.getElementById('proxyDot');
|
|
171
208
|
const proxyText = document.getElementById('proxyStatusText');
|
package/package.json
CHANGED