@chengxianglibra/dsh-data-analysis 0.1.2 → 0.2.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.
Files changed (212) hide show
  1. package/README.md +136 -99
  2. package/cordis.patch.yml +1 -1
  3. package/lib/bin/environment.js +8 -6
  4. package/lib/client/credentials/changes.js +74 -0
  5. package/lib/client/credentials/create-datasource.js +126 -22
  6. package/lib/client/credentials/credential-draft.js +42 -0
  7. package/lib/client/credentials/defaults.js +11 -0
  8. package/lib/client/credentials/descriptions.js +46 -0
  9. package/lib/client/credentials/install.js +209 -81
  10. package/lib/client/credentials/model.js +323 -51
  11. package/lib/client/credentials/report-publishing.js +96 -0
  12. package/lib/client/credentials/styles.js +13 -9
  13. package/lib/client/i18n/context.js +22 -0
  14. package/lib/client/i18n/copy.js +57 -0
  15. package/lib/client/i18n/credentials.js +475 -0
  16. package/lib/client/i18n/host.js +27 -0
  17. package/lib/client/i18n/navigation.js +41 -0
  18. package/lib/client/i18n/presentation.js +577 -0
  19. package/lib/client/i18n/python.js +36 -0
  20. package/lib/client/i18n/semantic.js +441 -0
  21. package/lib/client/input-context.js +21 -0
  22. package/lib/client/python-tool/card.js +35 -0
  23. package/lib/client/python-tool/execution-error.js +35 -0
  24. package/lib/client/python-tool/install.js +9 -0
  25. package/lib/client/python-tool/model.js +101 -0
  26. package/lib/client/right-tabs/install.js +535 -0
  27. package/lib/client/right-tabs/live-delivery.js +58 -0
  28. package/lib/client/right-tabs/navigation.js +47 -0
  29. package/lib/client/right-tabs/page.js +178 -0
  30. package/lib/client/right-tabs/styles.js +11 -0
  31. package/lib/client/rpc.js +5 -0
  32. package/lib/client/semantic-reference.js +10 -0
  33. package/lib/client/workspace-header-action.js +14 -1
  34. package/lib/client/workspace-shortcuts.js +26 -0
  35. package/lib/client.js +365 -242
  36. package/lib/compatibility.js +1 -1
  37. package/lib/datasource/authoring.js +43 -4
  38. package/lib/datasource/bridge-programs.js +23 -20
  39. package/lib/datasource/bridge.js +46 -3
  40. package/lib/datasource/changes-contract.js +41 -0
  41. package/lib/datasource/changes.js +49 -0
  42. package/lib/datasource/configure.js +39 -0
  43. package/lib/datasource/defaults.js +25 -0
  44. package/lib/datasource/index.js +2 -1
  45. package/lib/datasource/python-options.js +21 -0
  46. package/lib/datasource/python.js +116 -25
  47. package/lib/datasource/rpc.js +75 -13
  48. package/lib/datasource/service.js +286 -11
  49. package/lib/datasource/test.js +2 -1
  50. package/lib/disclosure/activation.js +19 -7
  51. package/lib/disclosure/execution-guidance.js +52 -0
  52. package/lib/disclosure/help.js +4 -1
  53. package/lib/environment/runtime.js +35 -34
  54. package/lib/environment/workspace.js +18 -1
  55. package/lib/lifecycle.js +33 -0
  56. package/lib/plugin-agents.js +154 -0
  57. package/lib/plugin.js +94 -172
  58. package/lib/presentation/asset.js +34 -0
  59. package/lib/presentation/assets/portable.js +14 -14
  60. package/lib/presentation/assets/static.js +344 -73
  61. package/lib/presentation/build/index.js +3 -2
  62. package/lib/presentation/commit.js +19 -12
  63. package/lib/presentation/contracts/asset-path.js +9 -0
  64. package/lib/presentation/contracts/catalog.js +62 -0
  65. package/lib/presentation/contracts/charts.js +0 -23
  66. package/lib/presentation/contracts/index.js +17 -5
  67. package/lib/presentation/contracts/types.js +1 -1
  68. package/lib/presentation/delivery.js +45 -30
  69. package/lib/presentation/export-html.js +214 -0
  70. package/lib/presentation/files.js +4 -5
  71. package/lib/presentation/projection/index.js +2 -1
  72. package/lib/presentation/projection/program.js +2 -9
  73. package/lib/presentation/projection/sql-code.js +13 -13
  74. package/lib/presentation/reports.js +114 -17
  75. package/lib/presentation/rpc.js +96 -58
  76. package/lib/presentation/tool.js +6 -10
  77. package/lib/report-publishing/adapters.js +111 -0
  78. package/lib/report-publishing/config.js +69 -0
  79. package/lib/report-publishing/service.js +180 -0
  80. package/lib/rpc.js +49 -0
  81. package/lib/semantic-browser/contracts.js +17 -0
  82. package/lib/semantic-browser/program.js +10 -0
  83. package/lib/semantic-browser/service.js +40 -6
  84. package/lib/semantic-reference/bridge.js +10 -1
  85. package/lib/semantic-reference/contracts.js +20 -6
  86. package/lib/semantic-reference/environment.js +56 -0
  87. package/lib/semantic-reference/labels.js +16 -0
  88. package/lib/semantic-reference/rpc.js +39 -18
  89. package/lib/semantic-reference/search.js +5 -4
  90. package/lib/semantic-reference/usage.js +12 -3
  91. package/lib/tool-lifecycle.js +22 -0
  92. package/lib/types/client/credentials/changes.d.ts +9 -0
  93. package/lib/types/client/credentials/create-datasource.d.ts +3 -1
  94. package/lib/types/client/credentials/credential-draft.d.ts +14 -0
  95. package/lib/types/client/credentials/defaults.d.ts +2 -0
  96. package/lib/types/client/credentials/descriptions.d.ts +1 -0
  97. package/lib/types/client/credentials/install.d.ts +19 -1
  98. package/lib/types/client/credentials/model.d.ts +31 -11
  99. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  100. package/lib/types/client/credentials/styles.d.ts +1 -1
  101. package/lib/types/client/i18n/context.d.ts +17 -0
  102. package/lib/types/client/i18n/copy.d.ts +806 -0
  103. package/lib/types/client/i18n/credentials.d.ts +475 -0
  104. package/lib/types/client/i18n/host.d.ts +16 -0
  105. package/lib/types/client/i18n/navigation.d.ts +41 -0
  106. package/lib/types/client/i18n/presentation.d.ts +577 -0
  107. package/lib/types/client/i18n/python.d.ts +19 -0
  108. package/lib/types/client/i18n/semantic.d.ts +441 -0
  109. package/lib/types/client/input-context.d.ts +19 -0
  110. package/lib/types/client/presentation/ask-dsh.d.ts +4 -8
  111. package/lib/types/client/presentation/catalog-model.d.ts +24 -0
  112. package/lib/types/client/presentation/catalog.d.ts +20 -0
  113. package/lib/types/client/presentation/chart-tooltip.d.ts +2 -2
  114. package/lib/types/client/presentation/context-reference.d.ts +12 -0
  115. package/lib/types/client/presentation/copy-context.d.ts +2 -2
  116. package/lib/types/client/presentation/delivery-model.d.ts +27 -6
  117. package/lib/types/client/presentation/delivery.d.ts +8 -7
  118. package/lib/types/client/presentation/download.d.ts +2 -0
  119. package/lib/types/client/presentation/export-menu.d.ts +26 -0
  120. package/lib/types/client/presentation/export-view.d.ts +63 -0
  121. package/lib/types/client/presentation/host-entry.d.ts +8 -2
  122. package/lib/types/client/presentation/icons.d.ts +1 -1
  123. package/lib/types/client/presentation/model.d.ts +14 -15
  124. package/lib/types/client/presentation/reader.d.ts +15 -2
  125. package/lib/types/client/presentation/reference-source.d.ts +9 -0
  126. package/lib/types/client/presentation/source-code-model.d.ts +2 -1
  127. package/lib/types/client/presentation/source-code.d.ts +0 -5
  128. package/lib/types/client/presentation/source-dialog.d.ts +1 -2
  129. package/lib/types/client/presentation/sources.d.ts +5 -5
  130. package/lib/types/client/presentation/styles.d.ts +1 -1
  131. package/lib/types/client/presentation/table.d.ts +5 -1
  132. package/lib/types/client/python-tool/card.d.ts +2 -0
  133. package/lib/types/client/python-tool/execution-error.d.ts +3 -0
  134. package/lib/types/client/python-tool/install.d.ts +2 -0
  135. package/lib/types/client/python-tool/model.d.ts +20 -0
  136. package/lib/types/client/right-tabs/install.d.ts +18 -0
  137. package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
  138. package/lib/types/client/right-tabs/navigation.d.ts +17 -0
  139. package/lib/types/client/right-tabs/page.d.ts +36 -0
  140. package/lib/types/client/right-tabs/styles.d.ts +2 -0
  141. package/lib/types/client/rpc.d.ts +3 -0
  142. package/lib/types/client/semantic-reference.d.ts +4 -0
  143. package/lib/types/client/workspace-header-action.d.ts +1 -1
  144. package/lib/types/client/workspace-shortcuts.d.ts +15 -0
  145. package/lib/types/client.d.ts +6 -3
  146. package/lib/types/compatibility.d.ts +1 -1
  147. package/lib/types/datasource/authoring.d.ts +10 -0
  148. package/lib/types/datasource/bridge.d.ts +23 -2
  149. package/lib/types/datasource/changes-contract.d.ts +18 -0
  150. package/lib/types/datasource/changes.d.ts +15 -0
  151. package/lib/types/datasource/configure.d.ts +7 -0
  152. package/lib/types/datasource/defaults.d.ts +7 -0
  153. package/lib/types/datasource/index.d.ts +6 -1
  154. package/lib/types/datasource/python-options.d.ts +9 -0
  155. package/lib/types/datasource/python.d.ts +17 -2
  156. package/lib/types/datasource/rpc.d.ts +2 -1
  157. package/lib/types/datasource/service.d.ts +48 -5
  158. package/lib/types/datasource/test.d.ts +1 -1
  159. package/lib/types/disclosure/activation.d.ts +2 -1
  160. package/lib/types/disclosure/execution-guidance.d.ts +6 -0
  161. package/lib/types/disclosure/help.d.ts +1 -1
  162. package/lib/types/environment/types.d.ts +2 -1
  163. package/lib/types/environment/workspace.d.ts +1 -0
  164. package/lib/types/lifecycle.d.ts +8 -0
  165. package/lib/types/plugin-agents.d.ts +27 -0
  166. package/lib/types/plugin.d.ts +12 -28
  167. package/lib/types/presentation/asset.d.ts +6 -0
  168. package/lib/types/presentation/build/index.d.ts +7 -2
  169. package/lib/types/presentation/commit.d.ts +1 -1
  170. package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
  171. package/lib/types/presentation/contracts/catalog.d.ts +27 -0
  172. package/lib/types/presentation/contracts/charts.d.ts +0 -2
  173. package/lib/types/presentation/contracts/types.d.ts +7 -4
  174. package/lib/types/presentation/delivery.d.ts +1 -1
  175. package/lib/types/presentation/export-html.d.ts +13 -0
  176. package/lib/types/presentation/reports.d.ts +6 -1
  177. package/lib/types/presentation/rpc.d.ts +3 -2
  178. package/lib/types/presentation/tool.d.ts +2 -2
  179. package/lib/types/report-publishing/adapters.d.ts +6 -0
  180. package/lib/types/report-publishing/config.d.ts +22 -0
  181. package/lib/types/report-publishing/service.d.ts +68 -0
  182. package/lib/types/rpc.d.ts +3 -0
  183. package/lib/types/semantic-browser/contracts.d.ts +7 -0
  184. package/lib/types/semantic-browser/service.d.ts +7 -1
  185. package/lib/types/semantic-reference/bridge.d.ts +1 -0
  186. package/lib/types/semantic-reference/contracts.d.ts +5 -2
  187. package/lib/types/semantic-reference/environment.d.ts +22 -0
  188. package/lib/types/semantic-reference/labels.d.ts +16 -0
  189. package/lib/types/semantic-reference/rpc.d.ts +1 -1
  190. package/lib/types/semantic-reference/search.d.ts +1 -1
  191. package/lib/types/tool-lifecycle.d.ts +4 -0
  192. package/lib/types/workspace-identity.d.ts +6 -0
  193. package/lib/workspace-identity.js +9 -0
  194. package/package.json +83 -61
  195. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  196. package/skills/dsh-data-analysis-files/SKILL.md +32 -0
  197. package/skills/dsh-data-analysis-files/references/examples.md +93 -0
  198. package/skills/dsh-data-analysis-presentation/SKILL.md +10 -6
  199. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +13 -4
  200. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +3 -2
  201. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +3 -2
  202. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +12 -5
  203. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +3 -2
  204. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +3 -2
  205. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +12 -4
  206. package/skills/dsh-data-analysis-presentation/references/examples.md +3 -1
  207. package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
  208. package/skills/dsh-data-analysis-presentation/references/interaction.md +1 -1
  209. package/skills/dsh-data-analysis-presentation/references/narrative.md +1 -1
  210. package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
  211. package/skills/dsh-data-analysis-presentation/references/schema.md +25 -1
  212. package/lib/types/client/presentation/install.d.ts +0 -15
package/README.md CHANGED
@@ -1,121 +1,158 @@
1
1
  # @chengxianglibra/dsh-data-analysis
2
2
 
3
- 由个人维护的 DeepSeek Harness 社区插件,并非 DeepSeek 官方发行或维护的软件包。
3
+ 在 [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 中,用自然语言完成数据分析,
4
+ 浏览和复用业务指标定义,并将结果整理为可交互、可离线阅读的图表、报告和看板。
5
+ 语义分析能力由 [Marivo](https://github.com/chengxianglibra/marivo) 提供;本地文件也可直接使用 pandas 或 DuckDB。
4
6
 
5
- DeepSeek Harness 的 Marivo 集成插件。当前包提供:
7
+ 本项目是个人维护的社区插件,并非 DeepSeek 官方发行或维护的软件。
6
8
 
7
- - 精确 Marivo 0.5.4 共享 Runtime 与 zero-init per-Workspace binding;
8
- - `marivo_help` 实时公共 Help transport;
9
- - 会话标题旁的“语义层”对象浏览器:Workspace 分类搜索、只读详情与局部关系图;
10
- - `marivo_datasource_test` 的 DSH Credentials 收集与显式 connection test;
11
- - `marivo_python` 的本次执行准入与单次 resolver 凭证注入;
12
- - 会话标题旁的“数据源与凭证”管理、测试与缺失输入提交后的原调用续接;
13
- - `marivo_present({ draft_path })` 的一次文件提交、结果卡片、打开与离线 HTML 下载;
14
- - 唯一插件 Skill `dsh-data-analysis-presentation`,组织图表、表格、报告、看板与可读来源展示;
15
- - `dsh_data_analysis_presentation.write_dataset(frame, path)` 的 computed typed JSON writer;
16
- - 内部 Artifact/computed/source-only 数据投影,保存精确来源及 unavailable 状态。
9
+ ## 安装
17
10
 
18
- 共享 reader、离线 builder、`marivo_present` 和展示 Skill 提供统一的展示交付;
19
- 两个 Marivo Runtime Skill `marivo-analysis`、`marivo-semantic` 继续从当前 Runtime 挂载。
20
- 旧 report-kit、report Skill、经典 JS 与 Evidence 卡协议不再分发。升级说明见
21
- [0.1.2 发布说明](../../docs/releases/0.1.2.md)。
11
+ ### 环境准备
22
12
 
23
- ## Compatibility
13
+ - Node.js `^22.19.0 || >=24.0.0`,与 DeepSeek Harness 一致:22.x 需至少 22.19.0,或使用 24.0.0 及以上版本;不支持 23.x。
14
+ - 已安装并配置好 DeepSeek Harness `>=0.1.5-rc.1`(rc.1 验收状态见验证指南;范围内未来版本不视为已验收)。
15
+ - `pnpm` 可在命令行中使用,供 Harness 安装插件。
16
+ - 已安装 Python 3.10+,命令行可运行 `python3`(Windows 为 `python`),且支持 `venv`/`ensurepip`;部分 Linux 发行版需额外安装 `python3-venv`。
24
17
 
25
- 包内 `dshDataAnalysisCompatibility` 是唯一运行时兼容声明:
18
+ 默认安装方式会在首次启动时使用本机 Python 创建独立的虚拟环境,并通过 pip 安装 Marivo,无需预装 Marivo 或 uv。
19
+ 首次启动需要联网下载依赖,请等待环境准备完成。
26
20
 
27
- - DSH distribution 与所有必需 peer 精确使用 `0.1.1-rc.2`;
28
- - Runtime 通过 pip 安装已发布的 Marivo 0.5.4;版本约束为 `marivo[duckdb,trino,clickhouse]==0.5.4`;
29
- - 项目自有 Runtime marker 为 `dsh-data-analysis-runtime/v3`;
30
- - 子进程策略为 `direct-argv-inherited-env-snapshot-overlay-v2`。
21
+ ### 安装并启动
31
22
 
32
- 管理员解释器的 `marivo.__version__` 与 package identity 必须精确匹配;不使用 capability/version matrix。
23
+ 将插件安装到 Web Profile:
33
24
 
34
- ## Tool contracts
35
-
36
- ```text
37
- marivo_help({ targets: string[] })
38
- marivo_datasource_test({ name: string })
39
- marivo_python({ code: string, datasources: string[] })
40
- marivo_present({ draft_path: string, report_id?: string, expected_build_id?: string })
25
+ ```bash
26
+ dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
41
27
  ```
42
28
 
43
- `marivo_datasource_test` 只拥有缺失 Credentials 的 DSH/Web 闭环和显式连接测试;
44
- `marivo_python` 在本次调用内等待全部数据源凭证就绪、核验身份并取得 fresh snapshot,再通过 DSH 前台
45
- 执行服务与 Marivo 公开 resolver 启动一次代码。配置齐全时不额外测试;普通 Shell 不获得凭证。
46
- 缺失输入时默认等待 Web 表单,提交测试成功后继续原调用;取消、轮换或 Workspace 变化终止旧准备,
47
- 已启动代码失败不重放。无 Web 场景配置 `credentialInteraction: 'none'`,subagent 同样不等待表单。
48
- 详见[凭证模块](../../docs/modules/datasource-credentials.md)。`md.inspect(...)`、
49
- Session recovery、Artifact revalidation、Quality、Evidence 读取、Session Graph 与 `to_pandas()` 都直接使用
50
- Marivo 公共 API,不增加 convenience Tool。
51
-
52
- ## 语义层对象浏览器
53
-
54
- 点击 DSH 会话标题旁的“语义层”,默认查看该会话所属 Workspace,也可在面板内显式切换 Workspace。
55
- 可按业务域和类型筛选,搜索名称、引用与业务定义,
56
- 复制对象引用、查看定义位置,并在对象关系中逐层浏览。桌面采用三栏布局,窄屏支持列表与详情切换。
57
-
58
- 页面不要求 live Agent,只读取 Marivo Catalog 元数据,不执行 `observe`、数据预览或连接测试。
59
- 打开和手动刷新重新加载;读取失败时明确标注上次成功内容。数据源连接配置、凭证值与原始源码不展示。
60
- 使用及验收边界见[模块说明](../../docs/modules/semantic-browser.md)。
61
-
62
- “数据源与凭证”位于同一会话标题旁,打开时同样使用该会话所属 Workspace,面板内保留 Workspace 选择。
63
- 两个入口随 Harness 的会话标题显示,无会话或空会话时不显示,侧栏底部不保留入口。
64
-
65
- ## 分析与展示
66
-
67
- 普通事实问答使用文字。图表、表格、报告、看板和可读来源展示加载 `dsh-data-analysis-presentation`,
68
- 按 Skill 编写 Workspace 相对 Draft,再调用 `marivo_present`。已有 Artifact 或 computed 数据无需先激活
69
- `marivo-analysis`;需要新分析或语义编写时,按需加载对应 Runtime Skill 与 live Help。
70
- Skill 只指导内容组织、声明来源和交付,不承担分析计算或来源有效性判断;布局由 reader 自适应。
71
- 完整流程见[展示 Skill](../../docs/modules/presentation-skill.md)。
72
-
73
- Python helper 的可选 `labels={列名: 展示名}` 设置中文表头、图例和 tooltip,字段绑定仍使用原列名。
74
- Python helper 只接受 pandas DataFrame,写入 `schemaVersion: 1` 的 typed JSON;int64/Decimal 保留精确字符串,
75
- null 保留缺失含义,datetime 必须带时区。它不保存来源或转换代码;来源在展示 Draft 中声明。
76
- 固定 projection 从同一 bound Workspace 恢复 persisted Artifact 和可选 Finding,不自动 observe、revalidate 或读取凭据。
77
- 直接 Artifact dataset 缺必要数据会失败,computed 和 source-only 可以保留 unavailable 来源。
78
-
79
- 共享 reader 展示 Markdown、metric、18 类 chart、table 和 source,保留精确值、单位、截断与 unavailable 来源。chart 支持 bar/line 变体与当前页面的字段、图形及过滤探索;统计量由分析阶段准备,探索不改写保存快照。
80
- `marivo_present` 读取 Workspace 相对 Draft 路径,生成不可变新 build 的 JSON/自包含 HTML。
81
- 默认创建新 Report;修改已有报告时成对传入 `report_id` 与本次修改所基于的 `expected_build_id`,以完整 Draft 更新同一 Report。
82
- 版本冲突时读取当前保存内容并合并需保留的编辑,不强行覆盖,也不回退新建。
83
- 原卡片通过 reportId 打开最近保存结果;阅读器可编辑标题、正文、指标标签、图表和表格配置,以及移动/删除 cell(允许删空),支持撤销/重做和保存/取消。
84
- 保存生成同报告的新 build,保留底层数据、来源和代码,不运行 Agent 或 Python。
85
- 全部 18 种 chart 与同 dataset 的表格和数据预览共享临时筛选;筛选不重算指标、不标记编辑修改、不进入保存、下载或打印。
86
- 文档、receipt 与 delivery 使用 schema v2,Draft 和 typed dataset 保持 v1;旧报告不读取、不迁移,旧文件保留。文本始终包含两份文件的位置、digest 和字节数,headless 也能取得交付物。
87
- 展开来源仅使用保存的快照;文件变化、缺失或 Workspace 归属变化会明确失败。
88
- 实现与验收见[展示数据投影](../../docs/modules/presentation-projection.md)、[展示 reader](../../docs/modules/presentation-reader.md)、
89
- [展示交付](../../docs/modules/presentation-delivery.md)。
90
-
91
- ## 验证
29
+ 安装后启动 Web Profile;如果已在运行,请先退出再重新启动:
92
30
 
93
31
  ```bash
94
- npm run check
95
- npm run build
96
- npm run verify:plugin-package
32
+ dsh --profile web
33
+ ```
34
+
35
+ 在 Web 界面选择分析使用的工作区(Workspace),新建会话并发送一条消息。
36
+ 会话标题旁按顺序显示“数据源”“语义层”“报告”三个入口,浏览页面在原生右侧标签页打开。本文以 Web 界面为例。
37
+
38
+ ### 本地文件分析
39
+
40
+ 上传文件后直接提问,或指定 Workspace 中已有文件。助手可在插件绑定的 Python 环境中使用 pandas
41
+ 或原生 DuckDB 分析 CSV、JSON、Parquet,无需先配置数据源或建立语义模型;需要时可继续生成报告。
42
+ 默认安装包含 Marivo 的 `duckdb` extra(同时保留 `trino`、`clickhouse`)。Excel `.xlsx` 通过 DuckDB 的
43
+ [excel 扩展](https://duckdb.org/docs/stable/core_extensions/excel.html)读取;首次使用需要联网下载扩展,
44
+ 并允许写入 DuckDB 扩展缓存。它不是额外的 Python 包,无需为此安装 `openpyxl`。
45
+ 旧版 `.xls` 不在该扩展支持范围内,需要先转换为 `.xlsx`、CSV 或 Parquet。
46
+
47
+ 纯文件分析无需经过 Marivo `md.raw_sql`。下面的数据源配置流程适用于数据库连接和需要复用的 Marivo 数据源。
48
+
49
+ ## 能力
50
+
51
+ | 你想做什么 | 插件提供的能力 |
52
+ | --- | --- |
53
+ | 回答业务问题 | 用自然语言提出问题,分析指标趋势、比较不同时间或业务分组、探索变化原因 |
54
+ | 分析本地文件 | 复用原生附件路径,以 pandas 或 DuckDB 直接计算,并按需交付图表或报告 |
55
+ | 连接数据 | 配置数据源、管理连接凭证并测试连接;默认包含 DuckDB、Trino、ClickHouse 支持 |
56
+ | 统一分析口径 | 与 Agent 一起整理和复用指标、维度等业务定义,浏览语义对象及其关系 |
57
+ | 明确分析对象 | 在输入框通过 `@` 搜索并引用已有语义对象 |
58
+ | 展示分析结果 | 生成包含文字、指标卡、图表、表格和来源说明的报告或看板 |
59
+ | 阅读与分享 | 交互筛选、调整呈现、保存版本,并下载 HTML 离线阅读 |
60
+
61
+ ## 如何使用
62
+
63
+ ### 1. 准备数据源
64
+
65
+ 在会话标题旁打开“数据源”,查看所属工作区的连接属性与配置状态:
66
+
67
+ 1. 点击“新增数据源”,选择引擎,按表单填写名称及连接信息。
68
+ 2. 如果需要身份认证,在同一表单的“连接凭证”中直接填写用户名、密码或令牌。
69
+ 3. 测试连接,根据结果检查地址、凭证或访问权限。
70
+
71
+ 凭证引用名自动生成,可展开“凭证引用名 · 确认或修改”查看和调整;也可填写已有引用,并将值留空以复用凭证。
72
+ 编辑时已保存的凭证值不回显,留空沿用;填写新值默认使用新的引用,不删除旧的共享凭证。
73
+ 手动指定已存在的引用并填写新值时,不会自动覆盖,需到凭证页确认更新。
74
+ 已有数据源可点击“编辑配置”修改连接信息,名称和引擎保持不变;保存后重新测试。
75
+
76
+ 管理员可在 Harness 当前 profile 的本插件配置项下设置 `datasourceDefaults`,按引擎类型为新建表单填入默认值:
77
+
78
+ ```yaml
79
+ datasourceDefaults:
80
+ trino:
81
+ host: trino.internal.example
82
+ port: 443
97
83
  ```
98
84
 
99
- 当前架构与验收边界见仓库根目录的[总体架构](../../docs/architecture.md)和
100
- [展示交付](../../docs/modules/presentation-delivery.md)。
85
+ 这是插件配置片段,随 Harness 现有配置加载机制生效;不是独立配置文件。未指定的字段保持原样,用户可修改或清空填充值;
86
+ 切换引擎时清空表单并应用目标引擎的默认值。编辑或复用已有数据源不应用这些默认值。
87
+ 只配置非敏感普通字段,不能设置 `*_env` 凭据引用或凭据值;内部地址只保存在部署环境的配置中,不必加入插件源码。
88
+ 打开新建表单时按当前 Runtime 校验引擎、字段和类型,错误会明确显示且不回显配置值;配置错误不阻止已有数据源编辑。
89
+
90
+ 分析需要新增或修复数据源时,助手可自动打开所属会话的右侧配置页。填写后点击“保存并测试,成功后继续”;
91
+ 缺少凭证时继续在凭证表单补齐,测试成功后原调用返回,助手再核验目标表并继续分析。
92
+ 请求页默认显示新增表单;切换“使用已有数据源”后,可选择已有连接并验证,无需重复创建。测试失败保留已保存的配置,允许修正后重试;取消请求不会作为成功继续。
93
+
94
+ 更新插件后,如果旧浏览器页面中工具正在等待、却没有打开配置 Tab,也没有“待配置”入口,
95
+ 先刷新浏览器页面以加载当前客户端。仍存活的原请求会重新显示;刷新页面不等于重启 Host。
96
+ 若原调用已取消或 Host 已重启,需要重新发起配置请求。
97
+ 请通过凭证表单提交密码或令牌,不要粘贴到聊天消息中。
98
+
99
+ ### 2. 用自然语言提出分析问题
100
+
101
+ 说明使用哪些数据、时间范围、指标口径和希望得到的结果。例如:
102
+
103
+ > 使用订单数据,分析 2026 年 8 月的销售额和订单量,与 7 月比较,按渠道拆分,并说明数据来源和口径。
104
+
105
+ > 检查最近七天的查询耗时,找出耗时最高的查询类型,给出趋势图和明细表。
106
+
107
+ 如果还没有整理业务定义,可以先让 Agent 帮助梳理:
108
+
109
+ > 先查看当前数据源中有哪些订单相关表,和我确认销售额、支付订单数的统计口径,再保存为可复用的业务定义。
110
+
111
+ 需要报告时,直接说明希望的内容:
112
+
113
+ > 将这次分析整理为一份报告,包含结论、关键指标、趋势图、渠道对比表和来源说明,并提供可下载的 HTML。
114
+
115
+ ### 3. 浏览和引用业务定义
116
+
117
+ 点击会话标题旁的“语义层”,按分类或关键词查找指标、维度等对象,查看业务定义和对象关系。
118
+ 这里展示已有定义;浏览页面不会执行分析查询。浏览页固定属于入口所在会话的工作区,支持把语义对象独立打开。
119
+
120
+ 在聊天输入框输入 `@` 可搜索并选择当前工作区的对象;多词搜索可输入 `@"monthly revenue"`。
121
+ 选中后将显示完整的 `kind:path` 引用,便于 Agent 使用你指定的对象开展分析。
122
+
123
+ ### 4. 阅读、调整和追问报告
124
+
125
+ 当前会话交付新报告后,右侧自动打开该次交付的固定版本;后台完成、历史回放和重连不会抢占阅读页面。
126
+ 同工作区的后台交付会更新已打开的报告目录,并在 current 页提示新版本,保留正在阅读的内容。
127
+ 报告生成后自动在右侧 tab 打开固定版本,主对话不附加报告 block。默认只保存 JSON,点击下载或显式调用导出工具时才生成 HTML。“报告”目录按标题查找并打开 current;可以并排阅读不同报告和版本。
128
+
129
+ 用户要求离线 HTML 文件时,DSH 可直接调用 `marivo_export_html({ report_id, build_id?, output_path })`,例如输出到 `reports/analysis.html`。省略 `build_id` 时固定调用开始时的 current;指定时导出该历史版本。输出是完整、自包含且可交互的报告,不含未保存编辑或临时筛选。路径必须位于当前 Workspace,已有文件不会覆盖;导出不需要 Python、数据源或发布配置。成功返回实际路径、Build ID、字节数和 SHA-256;DSH 随后通过原生 `present({ files: [{ path, description }] })` 交付该文件。
130
+ 发现新版本时 current 先提示,点击刷新或重新打开才切换,旧版本筛选随切换重置。
131
+ 没有活动会话时,侧栏的“报告”入口仍可阅读 Workspace 中的报告。
132
+
133
+ - **探索数据**:筛选图表和表格,调整支持的图形及展示字段。交互使用报告中已保存的数据;获取新数据或重新计算时,在会话中提出要求。
134
+ - **查看来源**:查看报告内容附带的来源信息,核对分析依据。
135
+ - **调整呈现**:从 current 页点击“编辑报告”,在原编辑窗口编辑标题、正文、指标标签和图表/表格配置,移动或删除内容,支持撤销、重做和保存。
136
+ - **继续追问**:在支持的报告内容菜单中选择“加入提问”,在输入框插入 `# <cell名称>` 引用标签,补充问题后发送;完整上下文会随引用一起提交。
137
+ - **查看历史**:保存编辑后可在历史侧栏查看和下载已保存的版本,历史版本只读。
138
+
139
+ 页面筛选不会重新计算指标卡,也不会写入保存的报告。希望保留当前筛选结果时,使用“导出当前视图”。
140
+
141
+ ### 5. 下载与分享
142
+
143
+ 报告右上角的导出菜单提供两种方式:
101
144
 
102
- ## 语义对象引用
145
+ | 选项 | 适合的用途 |
146
+ | --- | --- |
147
+ | 下载完整报告 | 下载所查看版本的完整 HTML 报告,供离线阅读 |
148
+ | 导出当前视图 | 将当前筛选、图形和表格排序固定为 HTML,附带来源版本及导出时间,分享当前看到的内容 |
103
149
 
104
- 在 Web 输入框输入 `@` 选择当前 Workspace 的 Marivo 对象;多词检索使用 `@"monthly revenue"`。
105
- 选中后显示完整 `kind:path`,提交只携带精确 ref。最近七个自然日热度保存在 DSH profile;候选缓存为 30 秒。
106
- 对象有效性由执行时 Marivo Catalog 验证,删除对象不会被相似对象自动替换。
150
+ 编辑模式下,请先保存或取消编辑再导出。下载的文件可直接用浏览器打开。
107
151
 
152
+ ## 许可证
108
153
 
109
- ## 草稿静态预检
154
+ 本项目采用 [MIT License](LICENSE)。
110
155
 
111
- 在 Workspace 根目录运行 `dsh-data-analysis-presentation-lint analysis/presentation.draft.json`,
112
- 或通过 `--project-root PATH` 显式指定 Workspace。CLI 复用 present 的草稿与 TypedDataset 校验及文件边界,
113
- 检查草稿结构和引用的 computed JSON;不启动 Runtime、不执行分析、不生成报告文件。
114
- 输出 JSON 的 `ok` 仅表示这些静态检查通过;`deferredChecks` 列出仍由 `marivo_present` 完成的
115
- Artifact 可用性与数据、codeRefs、投影后文档与渲染检查。退出码为 0(通过)、1(校验失败)、2(参数错误)。
116
- 草稿结构通过后逐份检查 computed 文件,每份保留首个错误,修正后可再次预检。
156
+ ## 报告发布到对象存储
117
157
 
118
- int64 单元格必须是精确整数字符串,例如 `"42"`;JSON number `42` 也不接受。
119
- 诊断保留 JSON pointer,并显示 `column query_count type=int64`、原因与修复建议。
120
- 超过 JS 安全整数范围的 number 可能已舍入,不能转成字符串来恢复精度;应从原始精确数据重写,
121
- 或使用绑定 Runtime 中的 `write_dataset`。预检通过不等于成功交付,仍需 present 的成功 receipt。
158
+ 通过 `reportPublishing.enabled` 开启 S3 兼容对象存储发布;在“数据源与凭证”管理独立 AK/SK 和可选 Token。开启后报告菜单的完整 HTML 与当前视图下载均替换为发布,成功返回链接。配置示例与边界见[报告 HTML 发布](../../docs/modules/report-publishing.md)。
package/cordis.patch.yml CHANGED
@@ -1,5 +1,5 @@
1
1
  # Web-profile Marivo disclosure layer. One profile-owned plugin installation
2
- # serves every Agent and session Workspace in native, code, or both mode.
2
+ # serves every Agent and session Workspace in native, ptc, or both mode.
3
3
 
4
4
  - insert:
5
5
  - id: dsh-data-analysis
@@ -5,7 +5,7 @@ import { MarivoEnvironmentError } from "../environment/errors.js";
5
5
  import { ensureSharedMarivoRuntime, MarivoWorkspaceEnvironmentManager, } from "../environment/index.js";
6
6
  import { environmentPayload } from "../environment/summary.js";
7
7
  function usage() {
8
- return `Usage: dsh-data-analysis-env [--project-root PATH] [--python PATH] [--runtime-root PATH] [--uv PATH]
8
+ return `Usage: dsh-data-analysis-env [--project-root PATH] [--python PATH] [--runtime-root PATH] [--bootstrap-python PATH]
9
9
 
10
10
  Ensures the shared pinned Marivo and presentation-kit Runtime, initializes the minimal
11
11
  Workspace layout, and checks the exact interpreter, import identities, and doctor
@@ -23,7 +23,7 @@ function parseArguments(argv) {
23
23
  let projectRoot = process.env.DSH_DATA_ANALYSIS_PROJECT_ROOT ?? process.env.DSH_CWD ?? process.cwd();
24
24
  let pythonExecutable = process.env.DSH_DATA_ANALYSIS_PYTHON;
25
25
  let runtimeRoot = process.env.DSH_DATA_ANALYSIS_RUNTIME_ROOT;
26
- let uvExecutable = process.env.DSH_DATA_ANALYSIS_UV;
26
+ let bootstrapPythonExecutable = process.env.DSH_DATA_ANALYSIS_BOOTSTRAP_PYTHON;
27
27
  for (let index = 0; index < argv.length; index++) {
28
28
  const option = argv[index];
29
29
  if (option === '--help' || option === '-h')
@@ -43,8 +43,8 @@ function parseArguments(argv) {
43
43
  index++;
44
44
  continue;
45
45
  }
46
- if (option === '--uv') {
47
- uvExecutable = requireValue(argv, index, option);
46
+ if (option === '--bootstrap-python') {
47
+ bootstrapPythonExecutable = requireValue(argv, index, option);
48
48
  index++;
49
49
  continue;
50
50
  }
@@ -54,7 +54,7 @@ function parseArguments(argv) {
54
54
  projectRoot,
55
55
  ...(pythonExecutable === undefined ? {} : { pythonExecutable }),
56
56
  ...(runtimeRoot === undefined ? {} : { runtimeRoot }),
57
- ...(uvExecutable === undefined ? {} : { uvExecutable }),
57
+ ...(bootstrapPythonExecutable === undefined ? {} : { bootstrapPythonExecutable }),
58
58
  };
59
59
  }
60
60
  async function main(argv) {
@@ -74,7 +74,9 @@ async function main(argv) {
74
74
  const runtime = await ensureSharedMarivoRuntime({
75
75
  ...(args.pythonExecutable === undefined ? {} : { pythonExecutable: args.pythonExecutable }),
76
76
  ...(args.runtimeRoot === undefined ? {} : { runtimeRoot: args.runtimeRoot }),
77
- ...(args.uvExecutable === undefined ? {} : { uvExecutable: args.uvExecutable }),
77
+ ...(args.bootstrapPythonExecutable === undefined
78
+ ? {}
79
+ : { bootstrapPythonExecutable: args.bootstrapPythonExecutable }),
78
80
  });
79
81
  const manager = new MarivoWorkspaceEnvironmentManager(runtime);
80
82
  const environment = await manager.resolve(args.projectRoot);
@@ -0,0 +1,74 @@
1
+ import { credentialChangesRemote, } from "../../datasource/changes-contract.js";
2
+ /** Mount before opening a logical stream, using the existing Gateway WebSocket. */
3
+ export function credentialChanges(ctx) {
4
+ const mounted = ctx.remote.$mount(credentialChangesRemote);
5
+ // Initialization may be withdrawn before a Session starts consuming the mount.
6
+ void mounted.catch(() => { });
7
+ ctx.effect(() => async () => (await mounted)());
8
+ // Host and browser Context augmentations share this build; use the browser contract here.
9
+ const connection = ctx.connection;
10
+ return (sessionId) => {
11
+ let snapshot;
12
+ const stopConnection = connection.generation.subscribe(() => {
13
+ if (!connection.generation.getSnapshot())
14
+ snapshot?.abort();
15
+ });
16
+ const stream = ctx.remote.$stream({
17
+ name: 'dsh-data-analysis credential changes',
18
+ async *open(signal) {
19
+ const current = new AbortController();
20
+ snapshot = current;
21
+ try {
22
+ await mounted;
23
+ signal.throwIfAborted();
24
+ // A dynamically mounted namespace needs its own declared Cordis consumer.
25
+ let namespace;
26
+ const consumer = ctx.inject(['remote.dshDataAnalysisCredentials'], (scoped) => {
27
+ namespace = scoped.remote.dshDataAnalysisCredentials;
28
+ });
29
+ const stop = () => {
30
+ void consumer.dispose();
31
+ };
32
+ signal.addEventListener('abort', stop, { once: true });
33
+ if (signal.aborted)
34
+ stop();
35
+ try {
36
+ await consumer;
37
+ signal.throwIfAborted();
38
+ if (!namespace)
39
+ throw new Error('credential-notification-unavailable');
40
+ yield* namespace.changes(sessionId, signal);
41
+ }
42
+ finally {
43
+ // Abort snapshots before RemoteStream starts waiting for reconnection.
44
+ current.abort();
45
+ signal.removeEventListener('abort', stop);
46
+ await consumer.dispose();
47
+ }
48
+ }
49
+ finally {
50
+ current.abort();
51
+ }
52
+ },
53
+ carrierFailed: () => snapshot?.abort(),
54
+ ended: () => new Error('credential-notification-ended'),
55
+ });
56
+ const dispose = async () => {
57
+ stopConnection();
58
+ snapshot?.abort();
59
+ await stream.dispose();
60
+ };
61
+ return {
62
+ async *[Symbol.asyncIterator]() {
63
+ try {
64
+ for await (const item of stream)
65
+ yield { ...item, signal: AbortSignal.any([item.signal, snapshot.signal]) };
66
+ }
67
+ finally {
68
+ await dispose();
69
+ }
70
+ },
71
+ dispose,
72
+ };
73
+ };
74
+ }
@@ -1,67 +1,171 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  // @ts-nocheck -- JSX is bundled by the plugin client build.
3
- import { useEffect, useState } from 'react';
4
- export function CreateDatasource({ model, workspaceId, close }) {
3
+ import { useEffect, useId, useRef, useState } from 'react';
4
+ import { useCopy } from "./../i18n/context.js";
5
+ import { errorMessage, message } from "./../i18n/copy.js";
6
+ import { credentialReference, prepareCredentials } from "./credential-draft.js";
7
+ import { creationFieldValues } from "./defaults.js";
8
+ import { datasourceDescription } from "./descriptions.js";
9
+ function credentialLabel(field) {
10
+ return ({
11
+ user_env: 'marivo.credentials.username',
12
+ password_env: 'marivo.credentials.password',
13
+ http_bearer_token_env: 'marivo.credentials.access-token',
14
+ http_headers_env: 'marivo.credentials.header-credential-value',
15
+ }[field] ?? field.replace(/_env$/, ''));
16
+ }
17
+ function credentialRows(schema, backend, values = {}) {
18
+ return (schema.backends.find((item) => item.name === backend)?.fields ?? [])
19
+ .filter((field) => field.name.endsWith('_env'))
20
+ .flatMap((field) => field.type === 'json'
21
+ ? Object.entries(values[field.name] ?? {}).map(([key, existing]) => ({
22
+ id: crypto.randomUUID(),
23
+ field: field.name,
24
+ key,
25
+ existing,
26
+ value: '',
27
+ }))
28
+ : [{ id: crypto.randomUUID(), field: field.name, existing: values[field.name], value: '' }]);
29
+ }
30
+ export function CreateDatasource({ model, workspaceId, close, name, requestId }) {
31
+ const t = useCopy();
32
+ const formId = useId();
33
+ const active = useRef(false);
5
34
  const [schema, setSchema] = useState(null);
35
+ const [original, setOriginal] = useState(null);
6
36
  const [backend, setBackend] = useState('');
7
37
  const [values, setValues] = useState({});
38
+ const [credentials, setCredentials] = useState([]);
8
39
  const [busy, setBusy] = useState(false);
9
40
  const [error, setError] = useState('');
10
41
  useEffect(() => {
11
42
  const controller = new AbortController();
12
- model
13
- .authoring(workspaceId, controller.signal)
14
- .then((value) => {
43
+ active.current = true;
44
+ Promise.all([
45
+ model.authoring(workspaceId, controller.signal, name ? 'edit' : 'create'),
46
+ name ? model.configuration(workspaceId, name, controller.signal) : Promise.resolve(null),
47
+ ])
48
+ .then(([value, configuration]) => {
15
49
  if (controller.signal.aborted)
16
50
  return;
17
51
  setSchema(value);
18
- setBackend(value.backends[0]?.name ?? '');
52
+ setOriginal(configuration);
53
+ setBackend(configuration?.backend ?? value.backends[0]?.name ?? '');
54
+ setCredentials(credentialRows(value, configuration?.backend ?? value.backends[0]?.name, configuration?.fields));
55
+ if (!configuration)
56
+ setValues(creationFieldValues(value, value.backends[0]?.name ?? ''));
57
+ if (configuration) {
58
+ const fields = value.backends.find((item) => item.name === configuration.backend)?.fields;
59
+ if (!fields)
60
+ throw new Error('marivo.credentials.this-runtime-cannot-fully-edit-this-datasource');
61
+ setValues(Object.fromEntries(fields
62
+ .filter((field) => !field.name.endsWith('_env') && configuration.fields[field.name] !== undefined)
63
+ .map((field) => [
64
+ field.name,
65
+ field.type === 'string'
66
+ ? configuration.fields[field.name]
67
+ : JSON.stringify(configuration.fields[field.name]),
68
+ ])));
69
+ }
19
70
  })
20
71
  .catch((error) => {
21
72
  if (!controller.signal.aborted)
22
- setError(error.message);
73
+ setError(errorMessage(error));
23
74
  });
24
- return () => controller.abort();
25
- }, [model, workspaceId]);
75
+ return () => {
76
+ active.current = false;
77
+ controller.abort();
78
+ };
79
+ }, [model, workspaceId, name]);
26
80
  const fields = schema?.backends.find((item) => item.name === backend)?.fields ?? [];
81
+ const credentialFields = fields.filter((field) => field.name.endsWith('_env'));
82
+ const updateCredential = (id, change) => setCredentials((rows) => rows.map((row) => (row.id === id ? { ...row, ...change } : row)));
27
83
  async function submit(event) {
28
84
  event.preventDefault();
29
85
  if (busy)
30
86
  return;
31
87
  setError('');
32
88
  const input = {};
89
+ let changes = {};
33
90
  try {
34
- for (const field of fields) {
91
+ for (const field of fields.filter((field) => !field.name.endsWith('_env'))) {
35
92
  const value = values[field.name];
36
- if (value === undefined || value === '')
93
+ if (value === undefined)
94
+ continue;
95
+ if (value === '' &&
96
+ (field.type !== 'string' ||
97
+ (name
98
+ ? original?.fields[field.name] !== ''
99
+ : schema.creationDefaults?.[backend]?.[field.name] !== '')))
37
100
  continue;
38
101
  input[field.name] = field.type === 'string' ? value : JSON.parse(value);
39
102
  }
103
+ const prepared = prepareCredentials(input.name ?? '', credentials);
104
+ Object.assign(input, prepared.fields);
105
+ changes = prepared.changes;
40
106
  }
41
- catch {
42
- setError('请检查数字、布尔值或 JSON 字段的格式。');
107
+ catch (error) {
108
+ setError(error instanceof SyntaxError
109
+ ? 'marivo.credentials.check-the-format-of-number-boolean-and-json-fields'
110
+ : errorMessage(error));
43
111
  return;
44
112
  }
45
113
  setBusy(true);
114
+ // Remove secrets from mounted inputs as soon as the explicit submission begins.
115
+ setCredentials((rows) => rows.map((row) => ({ ...row, value: '' })));
46
116
  try {
47
- await model.createDatasource(workspaceId, schema, { backend, fields: input });
48
- close();
117
+ await model.saveConfiguration(workspaceId, schema, { backend, fields: input }, original, requestId, changes);
118
+ if (active.current)
119
+ close();
49
120
  }
50
121
  catch (error) {
51
- setError(error.message);
122
+ if (active.current)
123
+ setError(message('marivo.credentials.value-credential-inputs-have-been-cleared-enter-them-again', {
124
+ p0: errorMessage(error),
125
+ }));
52
126
  }
53
127
  finally {
54
- setBusy(false);
128
+ for (const ref of Object.keys(changes))
129
+ delete changes[ref];
130
+ if (active.current)
131
+ setBusy(false);
55
132
  }
56
133
  }
57
- return (_jsxs("form", { className: "mc-form", "aria-label": "\u65B0\u589E\u6570\u636E\u6E90", onSubmit: submit, children: [_jsxs("div", { className: "mc-form-body", children: [_jsx("div", { className: "mc-detail-heading", children: _jsx("h3", { children: "\u65B0\u589E\u6570\u636E\u6E90" }) }), error && (_jsx("p", { role: "alert", className: "mc-result", "data-tone": "error", children: error })), !schema && !error && _jsx("p", { role: "status", children: "\u6B63\u5728\u8BFB\u53D6\u6570\u636E\u6E90\u914D\u7F6E\u5B57\u6BB5\u2026" }), schema && (_jsxs("fieldset", { className: "mc-field", disabled: busy, children: [_jsxs("label", { className: "mc-secret-input", children: ["\u5F15\u64CE", _jsx("select", { "aria-label": "\u5F15\u64CE", value: backend, onChange: (event) => {
134
+ return (_jsxs("form", { className: "mc-form", "aria-label": name ? t('marivo.credentials.edit-datasource') : t('marivo.credentials.add-datasource'), onSubmit: submit, children: [_jsxs("div", { className: "mc-form-body", children: [_jsx("div", { className: "mc-detail-heading", children: _jsx("h3", { children: name
135
+ ? t('marivo.credentials.edit-datasource')
136
+ : t('marivo.credentials.add-datasource') }) }), error && (_jsx("p", { role: "alert", className: "mc-result", "data-tone": "error", children: t(error) })), !schema && !error && (_jsx("p", { role: "status", children: t('marivo.credentials.loading-datasource-configuration-fields') })), schema && (_jsxs("fieldset", { className: "mc-field", disabled: busy, children: [_jsxs("label", { className: "mc-secret-input", children: [t('marivo.credentials.engine'), _jsx("select", { "aria-label": t('marivo.credentials.engine'), disabled: !!name, value: backend, onChange: (event) => {
58
137
  setBackend(event.target.value);
59
- setValues({});
138
+ setValues(creationFieldValues(schema, event.target.value));
139
+ setCredentials(credentialRows(schema, event.target.value));
60
140
  setError('');
61
- }, children: schema.backends.map((item) => (_jsx("option", { value: item.name, children: item.name }, item.name))) })] }), fields.map((field) => (_jsxs("label", { className: "mc-secret-input", htmlFor: `mc-create-${field.name}`, children: [_jsxs("span", { className: "mc-input-heading", children: [_jsxs("span", { className: "mc-input-name", children: [field.name, field.name.endsWith('_env') ? '(凭证引用名)' : '', field.required ? ' *' : ''] }), (field.description || field.name.endsWith('_env')) && (_jsxs("span", { className: "mc-input-description", children: [field.description, field.name.endsWith('_env') &&
62
- ' 填写引用名,如 MY_DB_PASSWORD;仅使用字母、数字和下划线,不能以数字开头。实际用户名或密码请在创建后的“新增凭证”中填写。'] }))] }), field.type === 'boolean' ? (_jsxs("select", { id: `mc-create-${field.name}`, "aria-label": field.name, value: values[field.name] ?? '', onChange: (event) => setValues({ ...values, [field.name]: event.target.value }), children: [_jsxs("option", { value: "", children: ["\u9ED8\u8BA4", field.default == null ? '' : ` (${field.default})`] }), _jsx("option", { value: "true", children: "true" }), _jsx("option", { value: "false", children: "false" })] })) : (_jsx("input", { id: `mc-create-${field.name}`, "aria-label": field.name, required: field.required, autoComplete: "off", type: field.type === 'number' ? 'number' : 'text', placeholder: field.default == null
141
+ }, children: schema.backends.map((item) => (_jsx("option", { value: item.name, children: item.name }, item.name))) })] }), fields
142
+ .filter((field) => !field.name.endsWith('_env'))
143
+ .map((field) => (_jsxs("label", { className: "mc-secret-input", htmlFor: `${formId}-${field.name}`, children: [_jsxs("span", { className: "mc-input-heading", children: [_jsxs("span", { className: "mc-input-name", children: [field.name, field.required ? ' *' : ''] }), field.description && (_jsx("span", { className: "mc-input-description", children: t(datasourceDescription(field.description)) }))] }), field.type === 'boolean' ? (_jsxs("select", { id: `${formId}-${field.name}`, "aria-label": field.name, value: values[field.name] ?? '', onChange: (event) => setValues({ ...values, [field.name]: event.target.value }), children: [_jsxs("option", { value: "", children: [t('marivo.credentials.default'), field.default == null ? '' : ` (${field.default})`] }), _jsx("option", { value: "true", children: "true" }), _jsx("option", { value: "false", children: "false" })] })) : (_jsx("input", { id: `${formId}-${field.name}`, "aria-label": field.name, required: field.required, disabled: !!name && field.name === 'name', autoComplete: "off", type: field.type === 'number' ? 'number' : 'text', placeholder: field.default == null
63
144
  ? field.type === 'json'
64
145
  ? 'JSON'
65
146
  : ''
66
- : String(field.default), value: values[field.name] ?? '', onChange: (event) => setValues({ ...values, [field.name]: event.target.value }) }))] }, field.name)))] }))] }), _jsxs("div", { className: "mc-form-footer", children: [_jsx("button", { className: "mc-primary", type: "submit", disabled: !backend || busy, children: busy ? '正在新增…' : '确认新增数据源' }), _jsx("button", { type: "button", onClick: close, disabled: busy, children: "\u53D6\u6D88" })] })] }));
147
+ : String(field.default), value: values[field.name] ?? '', onChange: (event) => setValues({ ...values, [field.name]: event.target.value }) }))] }, field.name))), credentialFields.length > 0 && (_jsxs("section", { className: "mc-inline-credentials", "aria-label": t('marivo.credentials.connection-credentials'), children: [_jsx("h4", { children: t('marivo.credentials.connection-credentials') }), _jsxs("p", { className: "mc-note", children: [t('marivo.credentials.enter-credential-values-directly-for-harness-to-save-references'), name ? t('marivo.credentials.leave-blank-to-keep-the-current-value-a-new') : ''] }), credentialFields.map((field) => (_jsxs("div", { className: "mc-credential-group", children: [credentials
148
+ .filter((row) => row.field === field.name)
149
+ .map((row) => {
150
+ const label = credentialLabel(field.name);
151
+ const reference = credentialReference(values.name ?? '', row);
152
+ return (_jsxs("div", { className: "mc-credential-entry", children: [row.key !== undefined && (_jsxs("label", { className: "mc-secret-input", children: [t('marivo.credentials.header-name'), _jsx("input", { "aria-label": t('marivo.credentials.header-name'), value: row.key, onChange: (event) => updateCredential(row.id, { key: event.target.value }) })] })), _jsxs("label", { className: "mc-secret-input", children: [_jsxs("span", { className: "mc-input-heading", children: [_jsxs("span", { className: "mc-input-name", children: [t(label), field.required ? ' *' : ''] }), _jsx("span", { className: "mc-input-description", children: row.existing
153
+ ? t('marivo.credentials.a-reference-is-configured-leave-blank-to-keep-it')
154
+ : t('marivo.credentials.enter-the-value-directly-no-need-to-create-a') })] }), _jsx("input", { "aria-label": t(label), type: "password", autoComplete: "new-password", maxLength: 65536, required: field.required && !reference, value: row.value, placeholder: row.existing
155
+ ? t('marivo.credentials.leave-blank-to-keep-enter-a-new-value-to')
156
+ : t('marivo.credentials.enter-credential-value'), onChange: (event) => updateCredential(row.id, { value: event.target.value }) })] }), _jsxs("details", { className: "mc-reference-options", children: [_jsx("summary", { children: t('marivo.credentials.credential-reference-review-or-change') }), _jsxs("label", { className: "mc-secret-input", children: [field.name, _jsx("input", { "aria-label": field.name, autoComplete: "off", value: reference, placeholder: t('marivo.credentials.generated-after-entering-a-credential-value'), onChange: (event) => updateCredential(row.id, { reference: event.target.value }) })] }), _jsx("p", { className: "mc-note", children: t('marivo.credentials.to-reuse-a-saved-credential-enter-its-reference-and') }), row.reference !== undefined && (_jsx("button", { type: "button", onClick: () => updateCredential(row.id, { reference: undefined }), children: t('marivo.credentials.restore-automatic-reference') }))] }), row.key !== undefined && (_jsx("button", { type: "button", className: "mc-quiet", onClick: () => setCredentials((rows) => rows.filter((item) => item.id !== row.id)), children: t('marivo.credentials.remove-header') }))] }, row.id));
157
+ }), field.type === 'json' && (_jsx("button", { type: "button", onClick: () => setCredentials((rows) => [
158
+ ...rows,
159
+ { id: crypto.randomUUID(), field: field.name, key: '', value: '' },
160
+ ]), children: t('marivo.credentials.add-header-credential') }))] }, field.name)))] }))] }))] }), _jsx("p", { className: "mc-note", children: requestId
161
+ ? t('marivo.credentials.save-configuration-and-credentials-then-test-the-connection-the')
162
+ : name
163
+ ? t('marivo.credentials.test-the-connection-again-after-saving-name-and-engine')
164
+ : '' }), _jsxs("div", { className: "mc-form-footer", children: [_jsx("button", { className: "mc-primary", type: "submit", disabled: !backend || busy, children: busy
165
+ ? t('marivo.credentials.saving-or-validating')
166
+ : requestId
167
+ ? t('marivo.credentials.save-and-test-then-continue')
168
+ : name
169
+ ? t('marivo.credentials.save-configuration')
170
+ : t('marivo.credentials.confirm-new-datasource') }), _jsx("button", { type: "button", onClick: close, disabled: busy, children: t('marivo.credentials.cancel') })] })] }));
67
171
  }