@yejiming/dsh-data-agent 0.0.10 → 0.0.12
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.en.md +19 -16
- package/README.md +19 -16
- package/lib/client.js +137 -93
- package/lib/client.js.map +1 -1
- package/lib/{command-LFgLb6el.js → command-DuCpwVbl.js} +2 -2
- package/lib/command.js +1 -1
- package/lib/{connections-WmjuUrDj.js → connections-5sfdEDsG.js} +44 -23
- package/lib/index.js +18 -14
- package/lib/routes.js +6 -2
- package/lib/{tool-Dka6RyEp.js → tool-DgL0fBfj.js} +157 -11
- package/lib/tool.js +1 -1
- package/lib/types/analysis-html.d.ts +27 -0
- package/lib/types/analysis.d.ts +13 -1
- package/lib/types/client/locales.d.ts +6 -0
- package/lib/types/client/persistence.d.ts +1 -1
- package/lib/types/connections.d.ts +10 -0
- package/lib/types/index.d.ts +5 -6
- package/lib/types/presentation-text.d.ts +3 -0
- package/lib/types/storage.d.ts +5 -0
- package/package.json +40 -40
- package/preset/data-agent/agent.cordis.yml +5 -3
package/README.en.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[中文](README.md) | **English**
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="assets/dsh-data-agent-banner.
|
|
6
|
+
<img src="assets/dsh-data-agent-banner.webp" alt="dsh-data-agent hero banner" width="100%">
|
|
7
7
|
</p>
|
|
8
8
|
<p align="center">
|
|
9
9
|
<img src="https://img.shields.io/github/v/release/omdsh-dev/dsh-data-agent?style=flat-square" alt="Version">
|
|
@@ -34,45 +34,41 @@
|
|
|
34
34
|
|
|
35
35
|
dsh-data-agent is a data analysis plugin for DeepSeek Harness (DSH). Connect a database and ask a business question; DSH inspects schemas, writes and runs SQL, continues the analysis from real results, and returns clear conclusions and business insights. The plugin supports both the Web UI and dsh-tui without modifying the DSH source code.
|
|
36
36
|
|
|
37
|
-

|
|
38
38
|
|
|
39
39
|
## Features
|
|
40
40
|
|
|
41
41
|
- **Analyze data through conversation**: Describe your goal in natural language. DSH understands the question, breaks it into analysis steps, queries real data, and organizes the conclusions. You can keep asking follow-up questions to explore the same context in greater depth.
|
|
42
42
|
- **Discover business insights automatically**: Data Agent goes beyond returning query results. It helps compare trends, locate anomalies, identify valuable customers or products, and turn the data into explanations that support decisions.
|
|
43
|
-
- **
|
|
43
|
+
- **Cross-surface HTML reports (render-analysis)**: In an ordinary tool call, the agent can choose to produce a single chart or a Dashboard-style report (metric/line/bar/pie/scatter/table views). Every successful call saves an offline HTML file under `analysis-reports/` in the current workspace. Web also shows an inline preview and a “View analysis” Modal; dsh-tui returns the file path. Whether to chart remains the agent's decision — schema exploration, single scalars, and queries without visual value are never forced into charts.
|
|
44
44
|
- **Works with both Web UI and dsh-tui**: For a visual workflow, we recommend [zhu1090093659/dsh-web-ui](https://github.com/zhu1090093659/dsh-web-ui), where you can connect databases, browse schemas, and inspect results in the browser. For a keyboard-first workflow, we recommend [ccch1mneyyy/dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI), where you can use the same Data Mode, connect through `/database`, and move directly into conversational analysis. Both interfaces provide the core Data Agent experience.
|
|
45
45
|
- **Connect common business databases**: Supports MySQL, PostgreSQL, SQLite, Oracle, Hive, and Impala across application databases, analytics systems, local data files, and data warehouses.
|
|
46
46
|
- **Let DSH complete the analysis loop**: DSH inspects table structures, writes SQL, runs the query, and adjusts its approach based on errors or returned data instead of stopping at an unverified SQL draft.
|
|
47
|
-
- **Stay focused with Data Mode**: The session uses DSH's native `str_replace_editor` for files and keeps `sql-query`, `sql-write`,
|
|
47
|
+
- **Stay focused with Data Mode**: The session uses DSH's native `str_replace_editor` for files and keeps `sql-query`, `sql-write`, `sql-cmd`, and `render-analysis`; Web, Desktop, dsh-tui, and headless profiles use the same tool protocol. Host or community tools such as `describe_image` and `ssh_*` do not leak into Data Mode.
|
|
48
48
|
- **Work safely with real data**: Use read-only mode and a read-only database account when appropriate. TUI passwords are masked and are never restored as part of a form draft. You decide whether the session may modify data.
|
|
49
49
|
|
|
50
50
|
The Web UI also includes an on-demand database workbench. Click the database button in the top-right of the composer to configure the connection, browse schemas, inspect columns, or run SQL in one Modal. Before and after the conversation starts, it no longer occupies the area above the composer or a left sidebar.
|
|
51
51
|
|
|
52
|
-

|
|
53
53
|
|
|
54
54
|
Choose “Data Mode” when creating a session, and DSH will use the data-analysis workflow for everything that follows.
|
|
55
55
|
|
|
56
|
-

|
|
57
57
|
|
|
58
58
|
## Quick Install
|
|
59
59
|
|
|
60
|
-
The
|
|
60
|
+
The commands below install the plugin into the Web profile.
|
|
61
61
|
|
|
62
62
|
### Method 1: npm (recommended)
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
65
|
dsh plugin --profile web add @yejiming/dsh-data-agent
|
|
66
|
-
dsh plugin --profile desktop add @yejiming/dsh-data-agent
|
|
67
|
-
dsh plugin --profile dsh-tui add @yejiming/dsh-data-agent
|
|
68
66
|
```
|
|
69
67
|
|
|
70
68
|
### Method 2: GitHub
|
|
71
69
|
|
|
72
70
|
```sh
|
|
73
71
|
dsh plugin --profile web add github:omdsh-dev/dsh-data-agent
|
|
74
|
-
dsh plugin --profile desktop add github:omdsh-dev/dsh-data-agent
|
|
75
|
-
dsh plugin --profile dsh-tui add github:omdsh-dev/dsh-data-agent
|
|
76
72
|
```
|
|
77
73
|
|
|
78
74
|
The plugin installs the Data Mode preset automatically and preloads its database tools and command when the profile starts. Selecting the preset no longer performs dynamic package-subpath imports. No local build is required.
|
|
@@ -94,18 +90,25 @@ Then:
|
|
|
94
90
|
|
|
95
91
|
For example, ask: “Analyze order changes over the last 30 days, identify the regions and products with the largest revenue decline, and explain the main causes.” DSH will inspect the relevant tables, generate and run the queries, and complete the analysis from real results.
|
|
96
92
|
|
|
97
|
-
###
|
|
93
|
+
### Analysis reports and HTML artifacts
|
|
98
94
|
|
|
99
|
-
|
|
95
|
+
Data Mode provides the render-analysis tool on every surface. The agent first explores and verifies facts with sql-query, then decides for itself whether a visualization helps. When it does, one tool call produces one versioned analysis report:
|
|
100
96
|
|
|
101
97
|
- A report holds 1-6 read-only datasets and 1-8 views (metric, line, bar, pie, scatter, table); multiple views may reuse one dataset, and aggregation or Top N is written in the SQL itself;
|
|
102
98
|
- Simple questions produce a single main chart (inline preview in the result row); complex questions produce a compact summary plus a “View analysis” button;
|
|
103
99
|
- “View analysis” opens a large Modal with every view of that report: a compact metric band, a full-width main chart, a two-column secondary grid, and a detail table — responsive across light/dark themes and narrow screens;
|
|
100
|
+
- Regardless of the active UI, the complete Dashboard is written atomically to `analysis-reports/*.html` under the session workspace and appears in DSH's Produced row where supported. The filename defaults to the report title or a semantic `outputName` basename, without a long UUID. Data, styles, and SVG rendering code are inline, so the file opens without a network connection;
|
|
104
101
|
- The complete report snapshot is persisted with the session log: refreshing or replaying history never re-queries the database and creates no extra browser storage;
|
|
105
|
-
-
|
|
102
|
+
- Web still renders its preview from the same report meta; the Node HTML generator loads neither ECharts nor Web client code.
|
|
106
103
|
|
|
107
104
|
## Using Data Agent in dsh-tui
|
|
108
105
|
|
|
106
|
+
Install Data Agent into the dsh-tui profile. `render-analysis` does not require a particular dsh-TUI version or scene capability:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
dsh plugin --profile dsh-tui add @yejiming/dsh-data-agent
|
|
110
|
+
```
|
|
111
|
+
|
|
109
112
|
Start the terminal interface:
|
|
110
113
|
|
|
111
114
|
```sh
|
|
@@ -129,9 +132,9 @@ After connecting, return to the chat input and ask a business question. Other us
|
|
|
129
132
|
/database disconnect Disconnect the current database
|
|
130
133
|
```
|
|
131
134
|
|
|
132
|
-
|
|
135
|
+
After the agent generates a report, the tool card shows dataset, view, empty-data facts, and the absolute HTML path. TUI does not print a character Dashboard and does not register `/analysis`; open the HTML in a local browser to inspect all six view types and raw data. The file belongs to that tool call, and `/resume` does not re-query the database.
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
When you reopen the connection form in the same session, it restores the latest database type, host, port, user, database, and read-only mode. The password always remains masked and is never restored.
|
|
135
138
|
|
|
136
139
|
## How to Ask Better Analysis Questions
|
|
137
140
|
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
**中文** | [English](README.en.md)
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<img src="assets/dsh-data-agent-banner.
|
|
6
|
+
<img src="assets/dsh-data-agent-banner.webp" alt="dsh-data-agent HERO图" width="100%">
|
|
7
7
|
</p>
|
|
8
8
|
<p align="center">
|
|
9
9
|
<img src="https://img.shields.io/github/v/release/omdsh-dev/dsh-data-agent?style=flat-square" alt="Version">
|
|
@@ -34,45 +34,41 @@
|
|
|
34
34
|
|
|
35
35
|
dsh-data-agent是DeepSeek Harness(DSH)的数据分析插件。连接数据库后,直接提出业务问题,DSH会自动查看库表、编写并执行SQL、根据真实结果继续分析,最终给出清晰的数据结论和商业洞察。插件同时支持Web UI与dsh-tui,无需修改DSH源码。
|
|
36
36
|
|
|
37
|
-

|
|
38
38
|
|
|
39
39
|
## 主要功能
|
|
40
40
|
|
|
41
41
|
- **通过对话完成数据分析**:直接用自然语言描述目标,DSH会理解问题、拆解分析步骤、查询真实数据并整理结论。你可以继续追问,分析会沿着当前上下文逐步深入。
|
|
42
42
|
- **自动寻找商业洞察**:不仅返回查询结果,还能帮助比较趋势、定位异常、识别高价值客户或商品,并把数据转化为便于业务决策的说明。
|
|
43
|
-
-
|
|
43
|
+
- **跨界面HTML分析报告(render-analysis)**:Agent可在普通工具调用里自主生成单图或Dashboard式综合分析报告(metric/line/bar/pie/scatter/table视图)。每次成功调用都会在当前工作目录的`analysis-reports/`中保存一份离线可打开的HTML;Web同时提供内联预览与“查看分析”Modal,dsh-tui直接返回文件路径。是否画图由Agent按问题判断,schema探查、单标量等查询不会被强制生成图表。
|
|
44
44
|
- **完整兼容Web UI与dsh-tui**:喜欢可视化操作时,可以在Web界面连接数据库、浏览库表和查看结果,推荐使用[zhu1090093659/dsh-web-ui](https://github.com/zhu1090093659/dsh-web-ui);习惯键盘工作流时,可以在终端中使用同一“数据模式”,通过`/database`完成连接,然后直接开始对话分析,推荐使用[ccch1mneyyy/dsh-TUI](https://github.com/ccch1mneyyy/dsh-TUI)。两种界面都能使用数据Agent的核心能力。
|
|
45
45
|
- **连接常见业务数据库**:支持MySQL、PostgreSQL、SQLite、Oracle、Hive和Impala,可用于业务系统、分析库、本地数据文件及数仓场景。
|
|
46
46
|
- **DSH自动完成分析闭环**:DSH会根据当前问题查看表结构、编写SQL、执行查询,并结合报错或返回结果继续调整,而不是只生成一段未经验证的SQL。
|
|
47
|
-
- **专注数据任务的数据模式**:会话使用DSH原生`str_replace_editor`处理文件,并保留`sql-query`、`sql-write`、`sql-cmd`;Web
|
|
47
|
+
- **专注数据任务的数据模式**:会话使用DSH原生`str_replace_editor`处理文件,并保留`sql-query`、`sql-write`、`sql-cmd`与`render-analysis`;Web、Desktop、dsh-tui和headless profile使用同一工具协议。`describe_image`、`ssh_*`等宿主或社区插件工具不会进入数据模式。
|
|
48
48
|
- **安全地使用真实数据**:支持只读模式和数据库只读账号;TUI密码会被隐藏,且不会作为表单草稿恢复。是否允许修改数据由你决定。
|
|
49
49
|
|
|
50
50
|
Web UI还提供按需数据库工作台:点击输入框右上角的数据库按钮,即可在同一个Modal中配置连接、浏览库表、查看字段结构或临时运行SQL。开始对话前后都不占用输入框上方或左侧的对话空间。
|
|
51
51
|
|
|
52
|
-

|
|
53
53
|
|
|
54
54
|
创建会话时选择“数据模式”,DSH就会以数据分析工作流处理后续问题。
|
|
55
55
|
|
|
56
|
-

|
|
57
57
|
|
|
58
58
|
## 快速安装
|
|
59
59
|
|
|
60
|
-
Web
|
|
60
|
+
以下命令将插件安装到Web profile。
|
|
61
61
|
|
|
62
62
|
### 方式一:npm安装(推荐)
|
|
63
63
|
|
|
64
64
|
```sh
|
|
65
65
|
dsh plugin --profile web add @yejiming/dsh-data-agent
|
|
66
|
-
dsh plugin --profile desktop add @yejiming/dsh-data-agent
|
|
67
|
-
dsh plugin --profile dsh-tui add @yejiming/dsh-data-agent
|
|
68
66
|
```
|
|
69
67
|
|
|
70
68
|
### 方式二:从GitHub安装
|
|
71
69
|
|
|
72
70
|
```sh
|
|
73
71
|
dsh plugin --profile web add github:omdsh-dev/dsh-data-agent
|
|
74
|
-
dsh plugin --profile desktop add github:omdsh-dev/dsh-data-agent
|
|
75
|
-
dsh plugin --profile dsh-tui add github:omdsh-dev/dsh-data-agent
|
|
76
72
|
```
|
|
77
73
|
|
|
78
74
|
插件会自动安装“数据模式”预设,并在profile启动时预加载该预设的数据库工具与命令;选择预设时不再动态导入插件子路径,无需本地构建。
|
|
@@ -94,18 +90,25 @@ dsh --profile web
|
|
|
94
90
|
|
|
95
91
|
例如,输入“分析最近30天订单变化,找出销售额下降最明显的地区和商品,并解释主要原因”,DSH会自行查看相关表、生成并执行查询,再根据真实结果完成分析。
|
|
96
92
|
|
|
97
|
-
###
|
|
93
|
+
### 分析报告与HTML文件
|
|
98
94
|
|
|
99
|
-
|
|
95
|
+
数据模式提供render-analysis工具:Agent会先用sql-query探查并核对事实,再自行判断可视化是否有帮助。判断需要画图时,一次工具调用会生成一份版本化分析报告:
|
|
100
96
|
|
|
101
97
|
- 报告包含 1-6 个只读数据集与 1-8 个视图(metric、line、bar、pie、scatter、table),同一数据集可被多个视图复用,聚合与 Top N 都写在 SQL 中;
|
|
102
98
|
- 简单问题生成单个主图(结果行内联预览),复杂问题生成紧凑摘要 + 「查看分析」按钮;
|
|
103
99
|
- 「查看分析」在大型 Modal 中展示本次报告的全部视图:紧凑指标带、全宽主图、双列辅助图与明细表,浅色/深色主题与窄屏单列自适应;
|
|
100
|
+
- 无论当前使用哪种UI,完整Dashboard都会原子保存到会话工作目录的`analysis-reports/*.html`,并在支持的DSH界面进入“产物”栏;文件名默认使用报告标题,也可用语义化`outputName`指定basename,不追加长UUID;文件内联数据、样式和SVG渲染代码,断网时也能直接打开;
|
|
104
101
|
- 完整报告快照随会话日志持久化:刷新或历史回放不重新查询数据库,也不产生新的浏览器存储;
|
|
105
|
-
-
|
|
102
|
+
- Web仍从同一份报告meta渲染预览;Node侧HTML生成器不加载ECharts或Web client代码。
|
|
106
103
|
|
|
107
104
|
## 在dsh-tui中使用
|
|
108
105
|
|
|
106
|
+
把Data Agent安装到dsh-tui profile即可;`render-analysis`不要求特定dsh-TUI版本或scene能力:
|
|
107
|
+
|
|
108
|
+
```sh
|
|
109
|
+
dsh plugin --profile dsh-tui add @yejiming/dsh-data-agent
|
|
110
|
+
```
|
|
111
|
+
|
|
109
112
|
启动终端界面:
|
|
110
113
|
|
|
111
114
|
```sh
|
|
@@ -129,9 +132,9 @@ dsh --profile dsh-tui
|
|
|
129
132
|
/database disconnect 断开当前连接
|
|
130
133
|
```
|
|
131
134
|
|
|
132
|
-
|
|
135
|
+
Agent生成分析报告后,工具卡会显示数据集、视图、空数据摘要与HTML绝对路径。TUI不会输出字符Dashboard,也没有`/analysis`命令;直接在本机浏览器中打开该HTML即可查看六类视图和原始数据。文件来自本次工具调用,`/resume`不会重新查询数据库。
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
同一会话再次打开连接表单时,会恢复最近填写的数据库类型、地址、端口、用户、数据库和只读模式。密码始终隐藏且不会恢复。
|
|
135
138
|
|
|
136
139
|
## 推荐的提问方式
|
|
137
140
|
|
package/lib/client.js
CHANGED
|
@@ -55,7 +55,7 @@ window.__ModuleLoader__.load({
|
|
|
55
55
|
saved.credentialMode = "reference";
|
|
56
56
|
return saved;
|
|
57
57
|
}
|
|
58
|
-
if (candidate.credentialMode === "password") saved.credentialMode =
|
|
58
|
+
if (candidate.credentialMode === "password" || candidate.credentialMode === "none") saved.credentialMode = candidate.credentialMode;
|
|
59
59
|
const persistPassword = candidate.persistPassword === true;
|
|
60
60
|
if (persistPassword) saved.persistPassword = true;
|
|
61
61
|
if (persistPassword && typeof candidate.password === "string") saved.password = candidate.password;
|
|
@@ -72,7 +72,10 @@ window.__ModuleLoader__.load({
|
|
|
72
72
|
toWrite.credentialMode = "reference";
|
|
73
73
|
} else {
|
|
74
74
|
delete toWrite.passwordRef;
|
|
75
|
-
if (toWrite.
|
|
75
|
+
if (toWrite.credentialMode === "none") {
|
|
76
|
+
delete toWrite.password;
|
|
77
|
+
delete toWrite.persistPassword;
|
|
78
|
+
} else if (toWrite.persistPassword !== true) delete toWrite.password;
|
|
76
79
|
}
|
|
77
80
|
storage.setItem(CONNECTION_STORAGE_KEY, JSON.stringify(toWrite));
|
|
78
81
|
} catch {}
|
|
@@ -100,64 +103,64 @@ window.__ModuleLoader__.load({
|
|
|
100
103
|
document.head.appendChild(tag);
|
|
101
104
|
}
|
|
102
105
|
var DataAgentWorkbench_module_css_default = {
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"input": "Sv3uVW_input",
|
|
109
|
-
"treeNode": "Sv3uVW_treeNode",
|
|
106
|
+
"workbench": "Sv3uVW_workbench",
|
|
107
|
+
"summaryType": "Sv3uVW_summaryType",
|
|
108
|
+
"field": "Sv3uVW_field",
|
|
109
|
+
"treeChildren": "Sv3uVW_treeChildren",
|
|
110
|
+
"active": "Sv3uVW_active",
|
|
110
111
|
"hint": "Sv3uVW_hint",
|
|
111
|
-
"
|
|
112
|
-
"
|
|
112
|
+
"triggerActive": "Sv3uVW_triggerActive",
|
|
113
|
+
"sqlResult": "Sv3uVW_sqlResult",
|
|
114
|
+
"errorIcon": "Sv3uVW_errorIcon",
|
|
113
115
|
"nullCell": "Sv3uVW_nullCell",
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
116
|
+
"tabPanel": "Sv3uVW_tabPanel",
|
|
117
|
+
"emptyStateText": "Sv3uVW_emptyStateText",
|
|
118
|
+
"modalColTitle": "Sv3uVW_modalColTitle",
|
|
117
119
|
"label": "Sv3uVW_label",
|
|
118
|
-
"
|
|
119
|
-
"treeChildren": "Sv3uVW_treeChildren",
|
|
120
|
-
"sqlInput": "Sv3uVW_sqlInput",
|
|
121
|
-
"tabActive": "Sv3uVW_tabActive",
|
|
122
|
-
"primary": "Sv3uVW_primary",
|
|
123
|
-
"emptyStateIcon": "Sv3uVW_emptyStateIcon",
|
|
124
|
-
"treeIcon": "Sv3uVW_treeIcon",
|
|
120
|
+
"errorHead": "Sv3uVW_errorHead",
|
|
125
121
|
"open": "Sv3uVW_open",
|
|
126
|
-
"fieldRow2": "Sv3uVW_fieldRow2",
|
|
127
|
-
"errorIcon": "Sv3uVW_errorIcon",
|
|
128
122
|
"modalHint": "Sv3uVW_modalHint",
|
|
123
|
+
"rememberRow": "Sv3uVW_rememberRow",
|
|
124
|
+
"ghost": "Sv3uVW_ghost",
|
|
125
|
+
"tab": "Sv3uVW_tab",
|
|
129
126
|
"connectionState": "Sv3uVW_connectionState",
|
|
130
|
-
"
|
|
131
|
-
"active": "Sv3uVW_active",
|
|
132
|
-
"colCount": "Sv3uVW_colCount",
|
|
133
|
-
"sqlResult": "Sv3uVW_sqlResult",
|
|
134
|
-
"treeCount": "Sv3uVW_treeCount",
|
|
127
|
+
"fieldGrid": "Sv3uVW_fieldGrid",
|
|
135
128
|
"sqlPanel": "Sv3uVW_sqlPanel",
|
|
136
|
-
"
|
|
129
|
+
"treeItem": "Sv3uVW_treeItem",
|
|
130
|
+
"errorBar": "Sv3uVW_errorBar",
|
|
131
|
+
"colScroll": "Sv3uVW_colScroll",
|
|
137
132
|
"connectionSummary": "Sv3uVW_connectionSummary",
|
|
138
133
|
"typeCell": "Sv3uVW_typeCell",
|
|
139
|
-
"
|
|
140
|
-
"
|
|
141
|
-
"workbench": "Sv3uVW_workbench",
|
|
142
|
-
"errorBar": "Sv3uVW_errorBar",
|
|
143
|
-
"treeItem": "Sv3uVW_treeItem",
|
|
144
|
-
"actions": "Sv3uVW_actions",
|
|
134
|
+
"schemaPanel": "Sv3uVW_schemaPanel",
|
|
135
|
+
"treeNode": "Sv3uVW_treeNode",
|
|
145
136
|
"rememberHint": "Sv3uVW_rememberHint",
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
"
|
|
149
|
-
"emptyState": "Sv3uVW_emptyState",
|
|
150
|
-
"shortcutHint": "Sv3uVW_shortcutHint",
|
|
137
|
+
"tabs": "Sv3uVW_tabs",
|
|
138
|
+
"treeName": "Sv3uVW_treeName",
|
|
139
|
+
"actions": "Sv3uVW_actions",
|
|
151
140
|
"treeChevron": "Sv3uVW_treeChevron",
|
|
152
|
-
"
|
|
153
|
-
"triggerSlot": "Sv3uVW_triggerSlot",
|
|
154
|
-
"ghost": "Sv3uVW_ghost",
|
|
155
|
-
"trigger": "Sv3uVW_trigger",
|
|
141
|
+
"triggerDot": "Sv3uVW_triggerDot",
|
|
156
142
|
"workbenchModalContent": "Sv3uVW_workbenchModalContent",
|
|
157
|
-
"
|
|
143
|
+
"fieldRow2": "Sv3uVW_fieldRow2",
|
|
144
|
+
"tabActive": "Sv3uVW_tabActive",
|
|
145
|
+
"modalCol": "Sv3uVW_modalCol",
|
|
146
|
+
"emptyState": "Sv3uVW_emptyState",
|
|
147
|
+
"input": "Sv3uVW_input",
|
|
148
|
+
"treeCount": "Sv3uVW_treeCount",
|
|
149
|
+
"sqlActions": "Sv3uVW_sqlActions",
|
|
150
|
+
"colCount": "Sv3uVW_colCount",
|
|
151
|
+
"columnsTable": "Sv3uVW_columnsTable",
|
|
158
152
|
"summaryDb": "Sv3uVW_summaryDb",
|
|
159
|
-
"
|
|
160
|
-
"
|
|
153
|
+
"trigger": "Sv3uVW_trigger",
|
|
154
|
+
"treeScroll": "Sv3uVW_treeScroll",
|
|
155
|
+
"triggerSlot": "Sv3uVW_triggerSlot",
|
|
156
|
+
"treeIcon": "Sv3uVW_treeIcon",
|
|
157
|
+
"sqlInput": "Sv3uVW_sqlInput",
|
|
158
|
+
"tree": "Sv3uVW_tree",
|
|
159
|
+
"primary": "Sv3uVW_primary",
|
|
160
|
+
"shortcutHint": "Sv3uVW_shortcutHint",
|
|
161
|
+
"errorText": "Sv3uVW_errorText",
|
|
162
|
+
"emptyStateIcon": "Sv3uVW_emptyStateIcon",
|
|
163
|
+
"workbenchModal": "Sv3uVW_workbenchModal"
|
|
161
164
|
};
|
|
162
165
|
//#endregion
|
|
163
166
|
//#region src/client/DataAgentWorkbench.tsx
|
|
@@ -391,6 +394,18 @@ window.__ModuleLoader__.load({
|
|
|
391
394
|
else if (saved.password !== void 0 && saved.password !== "") body.password = saved.password;
|
|
392
395
|
return body;
|
|
393
396
|
}
|
|
397
|
+
/** Whether localStorage contains enough information for a safe automatic retry. */
|
|
398
|
+
function canAutoReconnect(saved) {
|
|
399
|
+
if (saved.type === "sqlite" || saved.credentialMode === "none") return true;
|
|
400
|
+
if (saved.passwordRef !== void 0 && saved.passwordRef !== "") return true;
|
|
401
|
+
return saved.persistPassword === true && saved.password !== void 0 && saved.password !== "";
|
|
402
|
+
}
|
|
403
|
+
/** Avoid replaying the last browser credential into a different server-side profile. */
|
|
404
|
+
function savedMatchesSummary(saved, summary) {
|
|
405
|
+
if (saved.type !== summary.type || saved.database !== summary.database) return false;
|
|
406
|
+
if (saved.type === "sqlite") return true;
|
|
407
|
+
return (saved.host ?? "") === (summary.host ?? "") && (saved.port ?? void 0) === (summary.port ?? void 0) && (saved.user ?? "") === (summary.user ?? "");
|
|
408
|
+
}
|
|
394
409
|
/** The database workbench body. */
|
|
395
410
|
function DataAgentWorkbench({ sessionId, useSessions, t }) {
|
|
396
411
|
const isDataAgent = useSessions((snapshot) => snapshot).byId[sessionId]?.agentPreset === DATA_AGENT_PRESET;
|
|
@@ -410,6 +425,7 @@ window.__ModuleLoader__.load({
|
|
|
410
425
|
const [busy, setBusy] = (0, react.useState)(false);
|
|
411
426
|
const [error, setError] = (0, react.useState)(null);
|
|
412
427
|
const [connected, setConnected] = (0, react.useState)(false);
|
|
428
|
+
const [reconnectRequired, setReconnectRequired] = (0, react.useState)(false);
|
|
413
429
|
const [workbenchOpen, setWorkbenchOpen] = (0, react.useState)(false);
|
|
414
430
|
const [activeTab, setActiveTab] = (0, react.useState)("connection");
|
|
415
431
|
const [restoring, setRestoring] = (0, react.useState)(false);
|
|
@@ -467,28 +483,35 @@ window.__ModuleLoader__.load({
|
|
|
467
483
|
setBusy(true);
|
|
468
484
|
fetch(`/plugins/data-agent/status?sessionId=${encodeURIComponent(sessionId)}`, { signal: controller.signal }).then((response) => parseJsonResponse(response)).then(async (body) => {
|
|
469
485
|
if (cancelled) return;
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
486
|
+
const summary = body.summary;
|
|
487
|
+
const matchesSaved = saved !== null && summary !== void 0 && savedMatchesSummary(saved, summary);
|
|
488
|
+
if (summary !== void 0) {
|
|
489
|
+
setType(summary.type);
|
|
490
|
+
setHost(summary.host ?? "");
|
|
491
|
+
setPort(summary.port !== void 0 ? String(summary.port) : "");
|
|
492
|
+
setUser(summary.user ?? "");
|
|
493
|
+
setDatabase(summary.database);
|
|
494
|
+
setReadonly(summary.readonly === true);
|
|
495
|
+
setPasswordRef(summary.passwordRef ?? "");
|
|
496
|
+
setCredentialMode(summary.credentialMode === "reference" || summary.passwordRef !== void 0 ? "reference" : "password");
|
|
497
|
+
setCredentialStatus(summary.credential);
|
|
498
|
+
if (!matchesSaved) {
|
|
499
|
+
setPassword("");
|
|
500
|
+
setRememberPassword(false);
|
|
482
501
|
}
|
|
483
|
-
return;
|
|
484
502
|
}
|
|
485
|
-
|
|
503
|
+
setConnected(body.connected);
|
|
504
|
+
setReconnectRequired(body.reconnectRequired === true);
|
|
505
|
+
if (body.connected) return;
|
|
506
|
+
const referenceUnavailable = summary?.credentialMode === "reference" && summary.credential?.configured === false;
|
|
507
|
+
if (saved !== null && saved.database !== "" && canAutoReconnect(saved) && (summary === void 0 || matchesSaved) && !referenceUnavailable) {
|
|
486
508
|
setRestoring(true);
|
|
487
509
|
try {
|
|
488
510
|
const result = await performConnect(sessionId, payloadFromSaved(saved), controller.signal);
|
|
489
511
|
if (cancelled) return;
|
|
490
512
|
if (result.ok) {
|
|
491
513
|
setConnected(true);
|
|
514
|
+
setReconnectRequired(false);
|
|
492
515
|
setCredentialStatus(result.summary?.credential);
|
|
493
516
|
} else setError(`连接恢复失败:${result.error ?? "unknown error"}`);
|
|
494
517
|
} catch (cause) {
|
|
@@ -549,7 +572,18 @@ window.__ModuleLoader__.load({
|
|
|
549
572
|
const result = await performConnect(sessionId, body);
|
|
550
573
|
if (result.ok) {
|
|
551
574
|
setConnected(true);
|
|
575
|
+
setReconnectRequired(false);
|
|
552
576
|
setCredentialStatus(result.summary?.credential);
|
|
577
|
+
if (result.summary?.credentialMode === "none" && !sqlite) saveConnection({
|
|
578
|
+
type,
|
|
579
|
+
host,
|
|
580
|
+
...port !== "" ? { port: Number(port) } : {},
|
|
581
|
+
user,
|
|
582
|
+
database,
|
|
583
|
+
readonly,
|
|
584
|
+
credentialMode: "none",
|
|
585
|
+
savedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
586
|
+
});
|
|
553
587
|
setActiveTab("schema");
|
|
554
588
|
schemasLoaded.current = false;
|
|
555
589
|
await loadSchemas(true);
|
|
@@ -570,6 +604,7 @@ window.__ModuleLoader__.load({
|
|
|
570
604
|
body: JSON.stringify({ sessionId })
|
|
571
605
|
}));
|
|
572
606
|
setConnected(false);
|
|
607
|
+
setReconnectRequired(false);
|
|
573
608
|
setCredentialStatus(void 0);
|
|
574
609
|
setReadonly(false);
|
|
575
610
|
setActiveTab("connection");
|
|
@@ -650,7 +685,7 @@ window.__ModuleLoader__.load({
|
|
|
650
685
|
setSqlBusy(false);
|
|
651
686
|
}
|
|
652
687
|
};
|
|
653
|
-
const composerPlaceholder = connected ? t("composer.placeholder.connected") : t("composer.placeholder.disconnected");
|
|
688
|
+
const composerPlaceholder = connected ? t("composer.placeholder.connected") : reconnectRequired ? t("composer.placeholder.reconnectRequired") : t("composer.placeholder.disconnected");
|
|
654
689
|
(0, react.useLayoutEffect)(() => {
|
|
655
690
|
if (!isDataAgent) return;
|
|
656
691
|
const textarea = (triggerSlotRef.current?.closest("[data-composer-card]"))?.querySelector("textarea");
|
|
@@ -667,7 +702,7 @@ window.__ModuleLoader__.load({
|
|
|
667
702
|
const databaseLabel = sqlite ? t("form.database.sqlite") : type === "oracle" ? t("form.database.oracle") : type === "hive" || type === "impala" ? t("form.database.hive") : t("form.database");
|
|
668
703
|
const formDisabled = busy || connected;
|
|
669
704
|
const triggerState = error !== null ? "error" : busy || restoring ? "ongoing" : connected ? "done" : "warning";
|
|
670
|
-
const triggerLabel = error !== null ? t("workbench.open.error") : busy || restoring ? t("workbench.open.checking") : connected ? t("workbench.open.connected") : t("workbench.open.disconnected");
|
|
705
|
+
const triggerLabel = error !== null ? t("workbench.open.error") : busy || restoring ? t("workbench.open.checking") : connected ? t("workbench.open.connected") : reconnectRequired ? t("workbench.open.reconnectRequired") : t("workbench.open.disconnected");
|
|
671
706
|
const tabs = [
|
|
672
707
|
{
|
|
673
708
|
id: "connection",
|
|
@@ -727,9 +762,9 @@ window.__ModuleLoader__.load({
|
|
|
727
762
|
}),
|
|
728
763
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
729
764
|
className: DataAgentWorkbench_module_css_default.connectionState,
|
|
730
|
-
children: error !== null ? t("error.title") : restoring ? t("state.reconnecting") : busy ? t("state.checking") : connected ? t("state.connected") : t("state.disconnected")
|
|
765
|
+
children: error !== null ? t("error.title") : restoring ? t("state.reconnecting") : busy ? t("state.checking") : connected ? t("state.connected") : reconnectRequired ? t("state.reconnectRequired") : t("state.disconnected")
|
|
731
766
|
}),
|
|
732
|
-
connected && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
767
|
+
(connected || reconnectRequired) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
733
768
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
734
769
|
className: DataAgentWorkbench_module_css_default.summaryType,
|
|
735
770
|
children: t(`type.${type}`)
|
|
@@ -1359,12 +1394,14 @@ window.__ModuleLoader__.load({
|
|
|
1359
1394
|
"version",
|
|
1360
1395
|
"title",
|
|
1361
1396
|
"summary",
|
|
1397
|
+
"htmlPath",
|
|
1362
1398
|
"datasets",
|
|
1363
1399
|
"views"
|
|
1364
1400
|
], prefix);
|
|
1365
1401
|
if (input["version"] !== 1) fail(prefix + ": 不支持的报告版本(期望 version 1)");
|
|
1366
1402
|
const title = requireNonEmptyString(input["title"], prefix + ".title");
|
|
1367
1403
|
const summary = optionalString(input, "summary", prefix);
|
|
1404
|
+
const htmlPath = optionalString(input, "htmlPath", prefix);
|
|
1368
1405
|
const datasets = input["datasets"];
|
|
1369
1406
|
if (!Array.isArray(datasets) || datasets.length < 1 || datasets.length > 6) fail(prefix + ": datasets 必须是 1-6 个");
|
|
1370
1407
|
const parsedDatasets = [];
|
|
@@ -1417,6 +1454,7 @@ window.__ModuleLoader__.load({
|
|
|
1417
1454
|
views: parsedViews
|
|
1418
1455
|
};
|
|
1419
1456
|
if (summary !== void 0) report.summary = summary;
|
|
1457
|
+
if (htmlPath !== void 0) report.htmlPath = htmlPath;
|
|
1420
1458
|
return report;
|
|
1421
1459
|
}
|
|
1422
1460
|
const BASE_VIEW_PROPERTIES = {
|
|
@@ -42499,40 +42537,40 @@ window.__ModuleLoader__.load({
|
|
|
42499
42537
|
document.head.appendChild(tag);
|
|
42500
42538
|
}
|
|
42501
42539
|
var AnalysisDashboard_module_css_default = {
|
|
42502
|
-
"
|
|
42503
|
-
"
|
|
42540
|
+
"viewHalf": "_1tppBG_viewHalf",
|
|
42541
|
+
"table": "_1tppBG_table",
|
|
42542
|
+
"modalHeader": "_1tppBG_modalHeader",
|
|
42543
|
+
"view": "_1tppBG_view",
|
|
42544
|
+
"rowInterrupted": "_1tppBG_rowInterrupted",
|
|
42545
|
+
"viewTitle": "_1tppBG_viewTitle",
|
|
42504
42546
|
"modalSummary": "_1tppBG_modalSummary",
|
|
42505
|
-
"
|
|
42547
|
+
"rowError": "_1tppBG_rowError",
|
|
42548
|
+
"metricLabel": "_1tppBG_metricLabel",
|
|
42549
|
+
"modalClose": "_1tppBG_modalClose",
|
|
42550
|
+
"grid": "_1tppBG_grid",
|
|
42506
42551
|
"empty": "_1tppBG_empty",
|
|
42507
|
-
"
|
|
42508
|
-
"
|
|
42509
|
-
"modalTitle": "_1tppBG_modalTitle",
|
|
42510
|
-
"rowActions": "_1tppBG_rowActions",
|
|
42511
|
-
"rowButton": "_1tppBG_rowButton",
|
|
42552
|
+
"rowSummary": "_1tppBG_rowSummary",
|
|
42553
|
+
"row": "_1tppBG_row",
|
|
42512
42554
|
"modalShell": "_1tppBG_modalShell",
|
|
42513
|
-
"
|
|
42514
|
-
"
|
|
42555
|
+
"metricBand": "_1tppBG_metricBand",
|
|
42556
|
+
"rowTitle": "_1tppBG_rowTitle",
|
|
42557
|
+
"metricValue": "_1tppBG_metricValue",
|
|
42558
|
+
"modalTitleWrap": "_1tppBG_modalTitleWrap",
|
|
42559
|
+
"chart": "_1tppBG_chart",
|
|
42560
|
+
"tableWrap": "_1tppBG_tableWrap",
|
|
42561
|
+
"viewFull": "_1tppBG_viewFull",
|
|
42562
|
+
"modal": "_1tppBG_modal",
|
|
42515
42563
|
"visuallyHidden": "_1tppBG_visuallyHidden",
|
|
42516
|
-
"
|
|
42564
|
+
"modalTitle": "_1tppBG_modalTitle",
|
|
42565
|
+
"modalBody": "_1tppBG_modalBody",
|
|
42517
42566
|
"chartCanvas": "_1tppBG_chartCanvas",
|
|
42518
|
-
"
|
|
42519
|
-
"tableWrap": "_1tppBG_tableWrap",
|
|
42520
|
-
"viewHalf": "_1tppBG_viewHalf",
|
|
42567
|
+
"metric": "_1tppBG_metric",
|
|
42521
42568
|
"modalCount": "_1tppBG_modalCount",
|
|
42522
|
-
"
|
|
42523
|
-
"
|
|
42524
|
-
"
|
|
42525
|
-
"
|
|
42526
|
-
"
|
|
42527
|
-
"row": "_1tppBG_row",
|
|
42528
|
-
"view": "_1tppBG_view",
|
|
42529
|
-
"viewTitle": "_1tppBG_viewTitle",
|
|
42530
|
-
"rowSummary": "_1tppBG_rowSummary",
|
|
42531
|
-
"chart": "_1tppBG_chart",
|
|
42532
|
-
"grid": "_1tppBG_grid",
|
|
42533
|
-
"preview": "_1tppBG_preview",
|
|
42534
|
-
"rowError": "_1tppBG_rowError",
|
|
42535
|
-
"modalHeader": "_1tppBG_modalHeader"
|
|
42569
|
+
"rowHead": "_1tppBG_rowHead",
|
|
42570
|
+
"rowActions": "_1tppBG_rowActions",
|
|
42571
|
+
"emptyText": "_1tppBG_emptyText",
|
|
42572
|
+
"rowButton": "_1tppBG_rowButton",
|
|
42573
|
+
"preview": "_1tppBG_preview"
|
|
42536
42574
|
};
|
|
42537
42575
|
//#endregion
|
|
42538
42576
|
//#region src/client/AnalysisChart.tsx
|
|
@@ -42984,12 +43022,15 @@ window.__ModuleLoader__.load({
|
|
|
42984
43022
|
"state.disconnected": "未连接",
|
|
42985
43023
|
"state.checking": "正在检查连接…",
|
|
42986
43024
|
"state.reconnecting": "正在恢复连接…",
|
|
43025
|
+
"state.reconnectRequired": "需要重新认证",
|
|
42987
43026
|
"workbench.open.connected": "数据库工作台:已连接",
|
|
42988
43027
|
"workbench.open.disconnected": "数据库工作台:未连接",
|
|
42989
43028
|
"workbench.open.checking": "数据库工作台:正在检查连接",
|
|
42990
43029
|
"workbench.open.error": "数据库工作台:连接异常",
|
|
43030
|
+
"workbench.open.reconnectRequired": "数据库工作台:需要重新认证",
|
|
42991
43031
|
"composer.placeholder.disconnected": "数据库未连接,请点击输入框右上角的配置按钮",
|
|
42992
43032
|
"composer.placeholder.connected": "数据库连接成功,请描述分析内容",
|
|
43033
|
+
"composer.placeholder.reconnectRequired": "数据库需要重新认证,请点击输入框右上角的配置按钮",
|
|
42993
43034
|
"wb.workbench.title": "数据库工作台",
|
|
42994
43035
|
"wb.workbench.description": "管理连接、浏览库表并运行 SQL",
|
|
42995
43036
|
"wb.workbench.tabs": "数据库工作台功能",
|
|
@@ -43067,12 +43108,15 @@ window.__ModuleLoader__.load({
|
|
|
43067
43108
|
"state.disconnected": "Not connected",
|
|
43068
43109
|
"state.checking": "Checking connection…",
|
|
43069
43110
|
"state.reconnecting": "Restoring connection…",
|
|
43111
|
+
"state.reconnectRequired": "Authentication required",
|
|
43070
43112
|
"workbench.open.connected": "Database workbench: connected",
|
|
43071
43113
|
"workbench.open.disconnected": "Database workbench: not connected",
|
|
43072
43114
|
"workbench.open.checking": "Database workbench: checking connection",
|
|
43073
43115
|
"workbench.open.error": "Database workbench: connection error",
|
|
43116
|
+
"workbench.open.reconnectRequired": "Database workbench: authentication required",
|
|
43074
43117
|
"composer.placeholder.disconnected": "Database not connected. Click the configuration button at the top right of the input box.",
|
|
43075
43118
|
"composer.placeholder.connected": "Database connected. Describe what you want to analyze.",
|
|
43119
|
+
"composer.placeholder.reconnectRequired": "Database authentication required. Click the configuration button at the top right of the input box.",
|
|
43076
43120
|
"wb.workbench.title": "Database workbench",
|
|
43077
43121
|
"wb.workbench.description": "Manage connections, browse schemas, and run SQL",
|
|
43078
43122
|
"wb.workbench.tabs": "Database workbench sections",
|