@hedeqiang/dsh-nl2sql 0.14.4 → 0.14.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/CHANGELOG.md +7 -1
- package/lib/client.js +53 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
5
5
|
|
|
6
6
|
## [Unreleased]
|
|
7
7
|
|
|
8
|
+
## [0.14.5] - 2026-11-13
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Data health settings** are laid out like the Settings page: two groups (Data freshness, Data quality patrol) with switches and one explanation per row; a group's fields are hidden while it is off. With freshness off, its table and events are no longer shown on the Data health page either — just a note and the switch.
|
|
12
|
+
|
|
8
13
|
## [0.14.4] - 2026-11-13
|
|
9
14
|
|
|
10
15
|
### Added
|
|
@@ -164,7 +169,8 @@ These features are tested with local databases and fake IM / Lark servers; real
|
|
|
164
169
|
|
|
165
170
|
- Redesigned UI; Lark push (Markdown and native charts); access control, feedback loop, dashboard global filters; PostgreSQL / Elasticsearch, SSH tunnels, privacy settings, automatic overviews, scheduled reports; semantic layer, ER graph, scan guard, history and audit, dashboards; first version.
|
|
166
171
|
|
|
167
|
-
[Unreleased]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.
|
|
172
|
+
[Unreleased]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.5...HEAD
|
|
173
|
+
[0.14.5]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.4...v0.14.5
|
|
168
174
|
[0.14.4]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.3...v0.14.4
|
|
169
175
|
[0.14.3]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.2...v0.14.3
|
|
170
176
|
[0.14.2]: https://github.com/hedeqiang/dsh-nl2sql/compare/v0.14.1...v0.14.2
|
package/lib/client.js
CHANGED
|
@@ -17,7 +17,20 @@ window.__ModuleLoader__.load({
|
|
|
17
17
|
// ------------------------------------------------------------ locale
|
|
18
18
|
|
|
19
19
|
const zh = {
|
|
20
|
-
'fq.
|
|
20
|
+
'fq.admin.fOffNote': "数据新鲜度已关闭:不检查、不推送,页面上也不显示。需要时在下方「设置」中开启。",
|
|
21
|
+
'fq.set.gFresh': "数据新鲜度",
|
|
22
|
+
'fq.set.gQuality': "数据质量巡检",
|
|
23
|
+
'fq.set.fEnabledShort': "启用",
|
|
24
|
+
'fq.set.fEnabledHint': "定时检查各表最新数据时间,滞后时推送通知;Agent 回答「今天 / 昨天」等近期数据前会先检查。关闭后以上全部停止,页面上也不再显示。",
|
|
25
|
+
'fq.set.showShort': "在页面上显示",
|
|
26
|
+
'fq.set.showHint': "查询结果下方的「数据滞后」提示和表结构页的新鲜度面板。隐藏后仍会定时检查和推送通知。",
|
|
27
|
+
'fq.set.staleHint': "超过这个时长没有新数据即视为滞后;每张表可在上方表格单独设置,0 表示不监控。",
|
|
28
|
+
'fq.set.fChannelHint': "每次滞后只推送一次,恢复时再推送一次。",
|
|
29
|
+
'fq.set.qEnabledHint': "每天定时检查空值、重复键、异常值、未来时间与负值,并给出健康分。",
|
|
30
|
+
'fq.set.on': "已开启",
|
|
31
|
+
'fq.set.off': "已关闭",
|
|
32
|
+
'fq.set.shown': "显示",
|
|
33
|
+
'fq.set.hidden': "已隐藏",
|
|
21
34
|
'mcp.client.claudeCode': 'Claude Code', 'mcp.client.cline': 'Cline', 'mcp.client.windsurf': 'Windsurf', 'mcp.client.codex': 'Codex CLI', 'mcp.client.gemini': 'Gemini CLI', 'mcp.client.zed': 'Zed', 'mcp.client.continue': 'Continue',
|
|
22
35
|
'mcp.where.claudeCode': '在终端运行(HTTP 直连;下方为 stdio 桥接写法)。', 'mcp.where.cline': '写入 cline_mcp_settings.json(Cline 面板 → MCP Servers → Configure)。', 'mcp.where.windsurf': '写入 Windsurf 的 mcp_config.json(Cascade → MCP → 打开配置文件)。', 'mcp.where.codex': '写入 ~/.codex/config.toml(或项目的 .codex/config.toml)。', 'mcp.where.gemini': '写入 ~/.gemini/settings.json 或项目的 .gemini/settings.json。', 'mcp.where.zed': '写入 Zed 的 settings.json。', 'mcp.where.continue': '保存为工作区 .continue/mcpServers/nl2sql.yaml。',
|
|
23
36
|
'mcp.test': '测试连接', 'mcp.testOk': '连接正常:发现 {n} 个工具。', 'mcp.testFail': '连接失败:{error}', 'mcp.testFailSteps': '连接测试未通过:',
|
|
@@ -81,7 +94,6 @@ window.__ModuleLoader__.load({
|
|
|
81
94
|
"fq.admin.qOff": "每日质量巡检已关闭",
|
|
82
95
|
"fq.admin.noDs": "请先添加数据源",
|
|
83
96
|
"fq.set.title": "设置",
|
|
84
|
-
"fq.set.fEnabled": "启用数据新鲜度(定时检查、滞后通知、Agent 回答近期数据时的提醒);关闭后页面上也不再显示",
|
|
85
97
|
"fq.set.qEnabled": "每日数据质量巡检",
|
|
86
98
|
"fq.set.interval": "新鲜度检查间隔(分钟)",
|
|
87
99
|
"fq.set.time": "质量巡检时间",
|
|
@@ -90,7 +102,6 @@ window.__ModuleLoader__.load({
|
|
|
90
102
|
"fq.set.fChannel": "数据延迟通知渠道",
|
|
91
103
|
"fq.set.qChannel": "健康分下降通知渠道",
|
|
92
104
|
"fq.set.noPush": "不推送",
|
|
93
|
-
"fq.set.hint": "每张表的阈值可在上方表格中单独设置(0 表示不监控);延迟通知每个滞后周期只推送一次,恢复时再推送一次。",
|
|
94
105
|
"fq.saved": "已保存",
|
|
95
106
|
'pro.close': '关闭', 'pro.sendNow': '立即推送', 'pro.sent': '已推送', 'pro.deleteConfirm': '删除「{name}」?', 'pro.weekday': '星期',
|
|
96
107
|
'pro.wd.0': '周日', 'pro.wd.1': '周一', 'pro.wd.2': '周二', 'pro.wd.3': '周三', 'pro.wd.4': '周四', 'pro.wd.5': '周五', 'pro.wd.6': '周六',
|
|
@@ -1179,7 +1190,20 @@ window.__ModuleLoader__.load({
|
|
|
1179
1190
|
'policy.tablesLabel': '可访问的表',
|
|
1180
1191
|
}
|
|
1181
1192
|
const en = {
|
|
1182
|
-
'fq.
|
|
1193
|
+
'fq.admin.fOffNote': "Data freshness is off: no checks, no notifications, nothing shown on the page. Turn it on in Settings below.",
|
|
1194
|
+
'fq.set.gFresh': "Data freshness",
|
|
1195
|
+
'fq.set.gQuality': "Data quality patrol",
|
|
1196
|
+
'fq.set.fEnabledShort': "Enabled",
|
|
1197
|
+
'fq.set.fEnabledHint': "Checks the latest timestamp of every table on a schedule and notifies when data stops updating; the agent checks it before answering about recent periods. When off, all of this stops and nothing is shown on the page.",
|
|
1198
|
+
'fq.set.showShort': "Show on the page",
|
|
1199
|
+
'fq.set.showHint': "Stale-data bars under results and the freshness panel in Schema. Checks and notifications keep running when hidden.",
|
|
1200
|
+
'fq.set.staleHint': "Data older than this counts as stale; set per-table thresholds in the table above (0 = not monitored).",
|
|
1201
|
+
'fq.set.fChannelHint': "One notification per stale episode, and one when it recovers.",
|
|
1202
|
+
'fq.set.qEnabledHint': "Daily check of null rates, duplicate keys, outliers, future timestamps and negative amounts, with a health score.",
|
|
1203
|
+
'fq.set.on': "On",
|
|
1204
|
+
'fq.set.off': "Off",
|
|
1205
|
+
'fq.set.shown': "Shown",
|
|
1206
|
+
'fq.set.hidden': "Hidden",
|
|
1183
1207
|
'mcp.client.claudeCode': 'Claude Code', 'mcp.client.cline': 'Cline', 'mcp.client.windsurf': 'Windsurf', 'mcp.client.codex': 'Codex CLI', 'mcp.client.gemini': 'Gemini CLI', 'mcp.client.zed': 'Zed', 'mcp.client.continue': 'Continue',
|
|
1184
1208
|
'mcp.where.claudeCode': 'Run in a terminal (HTTP; the stdio bridge variant is below).', 'mcp.where.cline': 'Add to cline_mcp_settings.json (Cline panel → MCP Servers → Configure).', 'mcp.where.windsurf': 'Add to Windsurf\'s mcp_config.json (Cascade → MCP → open config file).', 'mcp.where.codex': 'Add to ~/.codex/config.toml (or the project\'s .codex/config.toml).', 'mcp.where.gemini': 'Add to ~/.gemini/settings.json or the project\'s .gemini/settings.json.', 'mcp.where.zed': 'Add to Zed\'s settings.json.', 'mcp.where.continue': 'Save as .continue/mcpServers/nl2sql.yaml in the workspace.',
|
|
1185
1209
|
'mcp.test': 'Test connection', 'mcp.testOk': 'Connected: {n} tools discovered.', 'mcp.testFail': 'Connection failed: {error}', 'mcp.testFailSteps': 'Connection test failed:',
|
|
@@ -1243,7 +1267,6 @@ window.__ModuleLoader__.load({
|
|
|
1243
1267
|
"fq.admin.qOff": "Daily quality patrol is off",
|
|
1244
1268
|
"fq.admin.noDs": "Add a data source first",
|
|
1245
1269
|
"fq.set.title": "Settings",
|
|
1246
|
-
"fq.set.fEnabled": "Enable data freshness (scheduled checks, stale-data notifications, warnings in answers about recent data); when off, nothing is shown on the page either",
|
|
1247
1270
|
"fq.set.qEnabled": "Daily data quality patrol",
|
|
1248
1271
|
"fq.set.interval": "Freshness check interval (min)",
|
|
1249
1272
|
"fq.set.time": "Quality patrol time",
|
|
@@ -1252,7 +1275,6 @@ window.__ModuleLoader__.load({
|
|
|
1252
1275
|
"fq.set.fChannel": "Delay notification channel",
|
|
1253
1276
|
"fq.set.qChannel": "Score drop notification channel",
|
|
1254
1277
|
"fq.set.noPush": "Do not push",
|
|
1255
|
-
"fq.set.hint": "Per-table thresholds can be set in the table above (0 = not monitored). A delay is pushed once per stale episode, and once more when it recovers.",
|
|
1256
1278
|
"fq.saved": "Saved",
|
|
1257
1279
|
'pro.close': 'Close', 'pro.sendNow': 'Send now', 'pro.sent': 'Sent', 'pro.deleteConfirm': 'Delete "{name}"?', 'pro.weekday': 'Weekday',
|
|
1258
1280
|
'pro.wd.0': 'Sunday', 'pro.wd.1': 'Monday', 'pro.wd.2': 'Tuesday', 'pro.wd.3': 'Wednesday', 'pro.wd.4': 'Thursday', 'pro.wd.5': 'Friday', 'pro.wd.6': 'Saturday',
|
|
@@ -2369,6 +2391,9 @@ window.__ModuleLoader__.load({
|
|
|
2369
2391
|
.n2s-mcp-steps{margin:6px 0 0;padding-left:18px;font-size:12.5px}
|
|
2370
2392
|
.n2s-mcp-log .n2s-select{max-width:200px}
|
|
2371
2393
|
.n2s-mcp-logtable td{vertical-align:middle}
|
|
2394
|
+
.n2s-fq-settings{padding-top:6px}
|
|
2395
|
+
.n2s-fq-setgroup{margin:14px 0 2px;font-size:12.5px;font-weight:600;color:var(--n2s-muted);letter-spacing:.02em}
|
|
2396
|
+
.n2s-fq-setgroup:first-child{margin-top:4px}
|
|
2372
2397
|
/* ---- dashboard v2 ---- */
|
|
2373
2398
|
.n2s-srcbadge{display:inline-flex;align-items:center;gap:3px;max-width:160px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:none}
|
|
2374
2399
|
.n2s-seg.small button{height:26px;padding:0 10px;font-size:12px}
|
|
@@ -6796,19 +6821,24 @@ window.__ModuleLoader__.load({
|
|
|
6796
6821
|
onSaved?.()
|
|
6797
6822
|
} catch (e) { notifyError(e) }
|
|
6798
6823
|
})
|
|
6799
|
-
|
|
6800
|
-
|
|
6801
|
-
|
|
6802
|
-
|
|
6803
|
-
|
|
6804
|
-
|
|
6805
|
-
|
|
6806
|
-
|
|
6807
|
-
|
|
6808
|
-
|
|
6809
|
-
|
|
6810
|
-
h(
|
|
6811
|
-
h('div', { className: 'n2s-
|
|
6824
|
+
const fOn = Boolean(form.freshness.enabled)
|
|
6825
|
+
const qOn = Boolean(form.quality.enabled)
|
|
6826
|
+
const sw = (checked, onChange, label, disabled) => h('label', { className: 'n2s-switch' }, h('input', { type: 'checkbox', role: 'switch', checked, disabled, 'aria-label': label, onChange: e => onChange(e.target.checked) }), h('span', null, label))
|
|
6827
|
+
const num = (id, value, onChange, attrs) => h('input', { id, className: 'n2s-input', type: 'number', style: { maxWidth: 160 }, value, onChange: e => onChange(e.target.value), ...attrs })
|
|
6828
|
+
const picker = (id, value, onChange) => h('div', { style: { maxWidth: 360 } }, h(ChannelPicker, { t, id, value: value ?? '', noneLabel: t('fq.set.noPush'), onChange }))
|
|
6829
|
+
return h('div', { className: 'n2s-card n2s-fq-settings' },
|
|
6830
|
+
h('div', { className: 'n2s-fq-setgroup' }, t('fq.set.gFresh')),
|
|
6831
|
+
h(SettingRow, { label: t('fq.set.fEnabledShort'), hint: t('fq.set.fEnabledHint') }, sw(fOn, v => setF('enabled', v), fOn ? t('fq.set.on') : t('fq.set.off'))),
|
|
6832
|
+
fOn && h(SettingRow, { label: t('fq.set.showShort'), hint: t('fq.set.showHint') }, sw(form.freshness.showInUi !== false, v => setF('showInUi', v), form.freshness.showInUi !== false ? t('fq.set.shown') : t('fq.set.hidden'))),
|
|
6833
|
+
fOn && h(SettingRow, { label: t('fq.set.interval'), htmlFor: 'n2s-fq-int' }, num('n2s-fq-int', form.freshness.intervalMin, v => setF('intervalMin', v), { min: 5, max: 1440 })),
|
|
6834
|
+
fOn && h(SettingRow, { label: t('fq.set.staleHours'), htmlFor: 'n2s-fq-stale', hint: t('fq.set.staleHint') }, num('n2s-fq-stale', form.freshness.staleHours, v => setF('staleHours', v), { min: 0.25, step: 0.25 })),
|
|
6835
|
+
fOn && h(SettingRow, { label: t('fq.set.fChannel'), htmlFor: 'n2s-fq-fch', hint: t('fq.set.fChannelHint') }, picker('n2s-fq-fch', form.freshness.channelId, v => setF('channelId', v))),
|
|
6836
|
+
h('div', { className: 'n2s-fq-setgroup' }, t('fq.set.gQuality')),
|
|
6837
|
+
h(SettingRow, { label: t('fq.set.qEnabled'), hint: t('fq.set.qEnabledHint') }, sw(qOn, v => setQ('enabled', v), qOn ? t('fq.set.on') : t('fq.set.off'))),
|
|
6838
|
+
qOn && h(SettingRow, { label: t('fq.set.time'), htmlFor: 'n2s-fq-time' }, h('input', { id: 'n2s-fq-time', className: 'n2s-input', type: 'time', style: { maxWidth: 160 }, value: form.quality.time, onChange: e => setQ('time', e.target.value) })),
|
|
6839
|
+
qOn && h(SettingRow, { label: t('fq.set.qChannel'), htmlFor: 'n2s-fq-qch' }, picker('n2s-fq-qch', form.quality.channelId, v => setQ('channelId', v))),
|
|
6840
|
+
qOn && form.quality.channelId && h(SettingRow, { label: t('fq.set.drop'), htmlFor: 'n2s-fq-drop' }, num('n2s-fq-drop', form.quality.dropPoints, v => setQ('dropPoints', v), { min: 1, max: 100 })),
|
|
6841
|
+
h('div', { className: 'n2s-row', style: { marginTop: 14, justifyContent: 'flex-end' } },
|
|
6812
6842
|
h('button', { type: 'button', className: 'n2s-btn primary', disabled: busy, onClick: save }, busy ? h('span', { className: 'n2s-spin' }) : null, t('ds.save'))))
|
|
6813
6843
|
}
|
|
6814
6844
|
|
|
@@ -6905,7 +6935,10 @@ window.__ModuleLoader__.load({
|
|
|
6905
6935
|
' · ',
|
|
6906
6936
|
status.value.quality.enabled ? fmt(t, 'fq.admin.nextQ', { time: status.value.quality.time }) : t('fq.admin.qOff')),
|
|
6907
6937
|
!dsId ? h('div', { className: 'n2s-empty' }, h('h3', null, t('fq.admin.noDs'))) : h(React.Fragment, null,
|
|
6908
|
-
|
|
6938
|
+
// When freshness is turned off, its table and events are not shown (and not queried).
|
|
6939
|
+
status.value && !status.value.freshness.enabled
|
|
6940
|
+
? h('div', { className: 'n2s-notice', style: { marginBottom: 6 } }, t('fq.admin.fOffNote'))
|
|
6941
|
+
: h(HealthFreshnessTable, { key: `f-${dsId}`, t, dsId }),
|
|
6909
6942
|
h(HealthQuality, { key: `q-${dsId}`, t, dsId })),
|
|
6910
6943
|
h('div', { className: 'n2s-setsub' }, t('fq.set.title')),
|
|
6911
6944
|
h(HealthSettings, { t, onSaved: () => status.reload() }))
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hedeqiang/dsh-nl2sql",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.5",
|
|
4
4
|
"description": "Ask MySQL, PostgreSQL, Elasticsearch and CSV/Excel in plain language inside DeepSeek Harness: charts, dashboards, alerts, and IM push and bots for Lark/Feishu, Telegram, WeCom and DingTalk.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|