@chengxianglibra/dsh-data-analysis 0.1.1 → 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 (303) hide show
  1. package/README.md +139 -53
  2. package/cordis.patch.yml +1 -1
  3. package/lib/bin/environment.js +9 -7
  4. package/lib/bin/presentation-lint.js +21 -0
  5. package/lib/bridges.js +2 -2
  6. package/lib/client/credentials/changes.js +74 -0
  7. package/lib/client/credentials/create-datasource.js +171 -0
  8. package/lib/client/credentials/credential-draft.js +42 -0
  9. package/lib/client/credentials/defaults.js +11 -0
  10. package/lib/client/credentials/descriptions.js +46 -0
  11. package/lib/client/credentials/install.js +319 -0
  12. package/lib/client/credentials/model.js +766 -0
  13. package/lib/client/credentials/report-publishing.js +96 -0
  14. package/lib/client/credentials/styles.js +21 -0
  15. package/lib/client/i18n/context.js +22 -0
  16. package/lib/client/i18n/copy.js +57 -0
  17. package/lib/client/i18n/credentials.js +475 -0
  18. package/lib/client/i18n/host.js +27 -0
  19. package/lib/client/i18n/navigation.js +41 -0
  20. package/lib/client/i18n/presentation.js +577 -0
  21. package/lib/client/i18n/python.js +36 -0
  22. package/lib/client/i18n/semantic.js +441 -0
  23. package/lib/client/input-context.js +21 -0
  24. package/lib/client/python-tool/card.js +35 -0
  25. package/lib/client/python-tool/execution-error.js +35 -0
  26. package/lib/client/python-tool/install.js +9 -0
  27. package/lib/client/python-tool/model.js +101 -0
  28. package/lib/client/right-tabs/install.js +535 -0
  29. package/lib/client/right-tabs/live-delivery.js +58 -0
  30. package/lib/client/right-tabs/navigation.js +47 -0
  31. package/lib/client/right-tabs/page.js +178 -0
  32. package/lib/client/right-tabs/styles.js +11 -0
  33. package/lib/client/rpc.js +5 -0
  34. package/lib/client/semantic-reference.js +10 -0
  35. package/lib/client/workspace-header-action.js +24 -0
  36. package/lib/client/workspace-shortcuts.js +26 -0
  37. package/lib/client.js +1250 -730
  38. package/lib/compatibility.js +2 -2
  39. package/lib/datasource/authoring.js +109 -0
  40. package/lib/datasource/bridge-programs.js +42 -27
  41. package/lib/datasource/bridge.js +144 -9
  42. package/lib/datasource/changes-contract.js +41 -0
  43. package/lib/datasource/changes.js +49 -0
  44. package/lib/datasource/configure.js +39 -0
  45. package/lib/datasource/defaults.js +25 -0
  46. package/lib/datasource/index.js +4 -2
  47. package/lib/datasource/python-options.js +21 -0
  48. package/lib/datasource/python.js +206 -0
  49. package/lib/datasource/resolver-program.js +112 -0
  50. package/lib/datasource/rpc.js +150 -0
  51. package/lib/datasource/service.js +962 -0
  52. package/lib/datasource/shell-env.js +2 -333
  53. package/lib/datasource/test.js +25 -51
  54. package/lib/disclosure/activation.js +19 -7
  55. package/lib/disclosure/execution-guidance.js +52 -0
  56. package/lib/disclosure/help.js +4 -1
  57. package/lib/environment/binding.js +54 -9
  58. package/lib/environment/runtime.js +120 -108
  59. package/lib/environment/subprocess.js +6 -1
  60. package/lib/environment/summary.js +3 -3
  61. package/lib/environment/workspace.js +23 -1
  62. package/lib/index.js +1 -1
  63. package/lib/lifecycle.js +33 -0
  64. package/lib/plugin-agents.js +154 -0
  65. package/lib/plugin.js +172 -133
  66. package/lib/presentation/asset.js +34 -0
  67. package/lib/presentation/assets/portable.js +606 -0
  68. package/lib/presentation/assets/static.js +1216 -0
  69. package/lib/presentation/build/index.js +72 -0
  70. package/lib/presentation/commit.js +163 -0
  71. package/lib/presentation/contracts/asset-path.js +9 -0
  72. package/lib/presentation/contracts/catalog.js +62 -0
  73. package/lib/presentation/contracts/charts.js +453 -0
  74. package/lib/presentation/contracts/editing.js +58 -0
  75. package/lib/presentation/contracts/index.js +663 -0
  76. package/lib/presentation/contracts/interaction.js +62 -0
  77. package/lib/presentation/contracts/types.js +15 -0
  78. package/lib/presentation/contracts/values.js +28 -0
  79. package/lib/presentation/delivery.js +71 -0
  80. package/lib/presentation/export-html.js +214 -0
  81. package/lib/presentation/files.js +82 -0
  82. package/lib/presentation/index.js +6 -0
  83. package/lib/presentation/lint.js +46 -0
  84. package/lib/presentation/projection/files.js +85 -0
  85. package/lib/presentation/projection/index.js +196 -0
  86. package/lib/presentation/projection/program.js +137 -0
  87. package/lib/presentation/projection/sql-code.js +79 -0
  88. package/lib/presentation/receipt.js +34 -0
  89. package/lib/presentation/reports.js +219 -0
  90. package/lib/presentation/rpc.js +186 -0
  91. package/lib/presentation/tool.js +118 -0
  92. package/lib/python-execution-contracts.js +1 -0
  93. package/lib/python-execution.js +232 -0
  94. package/lib/report-publishing/adapters.js +111 -0
  95. package/lib/report-publishing/config.js +69 -0
  96. package/lib/report-publishing/service.js +180 -0
  97. package/lib/rpc.js +49 -0
  98. package/lib/semantic-browser/contracts.js +97 -0
  99. package/lib/semantic-browser/definition.js +64 -0
  100. package/lib/semantic-browser/program.js +129 -0
  101. package/lib/semantic-browser/service.js +106 -0
  102. package/lib/semantic-reference/bridge.js +127 -0
  103. package/lib/semantic-reference/contracts.js +127 -0
  104. package/lib/semantic-reference/environment.js +56 -0
  105. package/lib/semantic-reference/index.js +3 -0
  106. package/lib/semantic-reference/labels.js +16 -0
  107. package/lib/semantic-reference/rpc.js +143 -0
  108. package/lib/semantic-reference/search.js +95 -0
  109. package/lib/semantic-reference/usage.js +152 -0
  110. package/lib/tool-lifecycle.js +22 -0
  111. package/lib/types/bridges.d.ts +2 -2
  112. package/lib/types/client/credentials/changes.d.ts +9 -0
  113. package/lib/types/client/credentials/create-datasource.d.ts +7 -0
  114. package/lib/types/client/credentials/credential-draft.d.ts +14 -0
  115. package/lib/types/client/credentials/defaults.d.ts +2 -0
  116. package/lib/types/client/credentials/descriptions.d.ts +1 -0
  117. package/lib/types/client/credentials/install.d.ts +19 -0
  118. package/lib/types/client/credentials/model.d.ts +79 -0
  119. package/lib/types/client/credentials/report-publishing.d.ts +5 -0
  120. package/lib/types/client/credentials/styles.d.ts +1 -0
  121. package/lib/types/client/i18n/context.d.ts +17 -0
  122. package/lib/types/client/i18n/copy.d.ts +806 -0
  123. package/lib/types/client/i18n/credentials.d.ts +475 -0
  124. package/lib/types/client/i18n/host.d.ts +16 -0
  125. package/lib/types/client/i18n/navigation.d.ts +41 -0
  126. package/lib/types/client/i18n/presentation.d.ts +577 -0
  127. package/lib/types/client/i18n/python.d.ts +19 -0
  128. package/lib/types/client/i18n/semantic.d.ts +441 -0
  129. package/lib/types/client/input-context.d.ts +19 -0
  130. package/lib/types/client/presentation/ask-dsh.d.ts +5 -0
  131. package/lib/types/client/presentation/catalog-model.d.ts +24 -0
  132. package/lib/types/client/presentation/catalog.d.ts +20 -0
  133. package/lib/types/client/presentation/chart-explorer.d.ts +12 -0
  134. package/lib/types/client/presentation/chart-geometry.d.ts +30 -0
  135. package/lib/types/client/presentation/chart-renderer.d.ts +10 -0
  136. package/lib/types/client/presentation/chart-specials.d.ts +10 -0
  137. package/lib/types/client/presentation/chart-styles.d.ts +1 -0
  138. package/lib/types/client/presentation/chart-tooltip.d.ts +13 -0
  139. package/lib/types/client/presentation/chart-view.d.ts +23 -0
  140. package/lib/types/client/presentation/context-reference.d.ts +12 -0
  141. package/lib/types/client/presentation/copy-context.d.ts +5 -0
  142. package/lib/types/client/presentation/delivery-model.d.ts +75 -0
  143. package/lib/types/client/presentation/delivery.d.ts +78 -0
  144. package/lib/types/client/presentation/download.d.ts +2 -0
  145. package/lib/types/client/presentation/editor-controls.d.ts +12 -0
  146. package/lib/types/client/presentation/export-menu.d.ts +26 -0
  147. package/lib/types/client/presentation/export-view.d.ts +63 -0
  148. package/lib/types/client/presentation/global-filters.d.ts +6 -0
  149. package/lib/types/client/presentation/host-entry.d.ts +18 -0
  150. package/lib/types/client/presentation/icons.d.ts +4 -0
  151. package/lib/types/client/presentation/markdown.d.ts +5 -0
  152. package/lib/types/client/presentation/model.d.ts +61 -0
  153. package/lib/types/client/presentation/reader.d.ts +24 -0
  154. package/lib/types/client/presentation/reference-source.d.ts +9 -0
  155. package/lib/types/client/presentation/source-code-model.d.ts +17 -0
  156. package/lib/types/client/presentation/source-code.d.ts +6 -0
  157. package/lib/types/client/presentation/source-dialog.d.ts +12 -0
  158. package/lib/types/client/presentation/source-facts.d.ts +20 -0
  159. package/lib/types/client/presentation/source-format.d.ts +5 -0
  160. package/lib/types/client/presentation/source-highlight.d.ts +7 -0
  161. package/lib/types/client/presentation/sources.d.ts +13 -0
  162. package/lib/types/client/presentation/styles.d.ts +1 -0
  163. package/lib/types/client/presentation/table.d.ts +16 -0
  164. package/lib/types/client/presentation/use-element-width.d.ts +5 -0
  165. package/lib/types/client/python-tool/card.d.ts +2 -0
  166. package/lib/types/client/python-tool/execution-error.d.ts +3 -0
  167. package/lib/types/client/python-tool/install.d.ts +2 -0
  168. package/lib/types/client/python-tool/model.d.ts +20 -0
  169. package/lib/types/client/right-tabs/install.d.ts +18 -0
  170. package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
  171. package/lib/types/client/right-tabs/navigation.d.ts +17 -0
  172. package/lib/types/client/right-tabs/page.d.ts +36 -0
  173. package/lib/types/client/right-tabs/styles.d.ts +2 -0
  174. package/lib/types/client/rpc.d.ts +3 -0
  175. package/lib/types/client/semantic-reference.d.ts +4 -0
  176. package/lib/types/client/workspace-header-action.d.ts +7 -0
  177. package/lib/types/client/workspace-shortcuts.d.ts +15 -0
  178. package/lib/types/client.d.ts +9 -124
  179. package/lib/types/compatibility.d.ts +3 -3
  180. package/lib/types/datasource/authoring.d.ts +35 -0
  181. package/lib/types/datasource/bridge.d.ts +43 -4
  182. package/lib/types/datasource/changes-contract.d.ts +18 -0
  183. package/lib/types/datasource/changes.d.ts +15 -0
  184. package/lib/types/datasource/configure.d.ts +7 -0
  185. package/lib/types/datasource/defaults.d.ts +7 -0
  186. package/lib/types/datasource/index.d.ts +8 -5
  187. package/lib/types/datasource/python-options.d.ts +9 -0
  188. package/lib/types/datasource/python.d.ts +19 -0
  189. package/lib/types/datasource/resolver-program.d.ts +6 -0
  190. package/lib/types/datasource/rpc.d.ts +5 -0
  191. package/lib/types/datasource/service.d.ts +155 -0
  192. package/lib/types/datasource/shell-env.d.ts +0 -29
  193. package/lib/types/datasource/test.d.ts +6 -29
  194. package/lib/types/disclosure/activation.d.ts +2 -1
  195. package/lib/types/disclosure/execution-guidance.d.ts +6 -0
  196. package/lib/types/disclosure/help.d.ts +1 -1
  197. package/lib/types/environment/binding.d.ts +5 -1
  198. package/lib/types/environment/errors.d.ts +1 -1
  199. package/lib/types/environment/runtime.d.ts +5 -5
  200. package/lib/types/environment/types.d.ts +15 -3
  201. package/lib/types/environment/workspace.d.ts +1 -0
  202. package/lib/types/index.d.ts +1 -1
  203. package/lib/types/lifecycle.d.ts +8 -0
  204. package/lib/types/plugin-agents.d.ts +27 -0
  205. package/lib/types/plugin.d.ts +14 -24
  206. package/lib/types/presentation/asset.d.ts +6 -0
  207. package/lib/types/presentation/build/index.d.ts +16 -0
  208. package/lib/types/presentation/commit.d.ts +4 -0
  209. package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
  210. package/lib/types/presentation/contracts/catalog.d.ts +27 -0
  211. package/lib/types/presentation/contracts/charts.d.ts +11 -0
  212. package/lib/types/presentation/contracts/editing.d.ts +8 -0
  213. package/lib/types/presentation/contracts/index.d.ts +11 -0
  214. package/lib/types/presentation/contracts/interaction.d.ts +8 -0
  215. package/lib/types/presentation/contracts/types.d.ts +255 -0
  216. package/lib/types/presentation/contracts/values.d.ts +9 -0
  217. package/lib/types/presentation/delivery.d.ts +3 -0
  218. package/lib/types/presentation/export-html.d.ts +13 -0
  219. package/lib/types/presentation/files.d.ts +7 -0
  220. package/lib/types/presentation/index.d.ts +6 -0
  221. package/lib/types/presentation/lint.d.ts +13 -0
  222. package/lib/types/presentation/projection/files.d.ts +2 -0
  223. package/lib/types/presentation/projection/index.d.ts +20 -0
  224. package/lib/types/presentation/projection/program.d.ts +2 -0
  225. package/lib/types/presentation/projection/sql-code.d.ts +2 -0
  226. package/lib/types/presentation/receipt.d.ts +14 -0
  227. package/lib/types/presentation/reports.d.ts +10 -0
  228. package/lib/types/presentation/rpc.d.ts +25 -0
  229. package/lib/types/presentation/tool.d.ts +12 -0
  230. package/lib/types/python-execution-contracts.d.ts +10 -0
  231. package/lib/types/python-execution.d.ts +10 -0
  232. package/lib/types/report-publishing/adapters.d.ts +6 -0
  233. package/lib/types/report-publishing/config.d.ts +22 -0
  234. package/lib/types/report-publishing/service.d.ts +68 -0
  235. package/lib/types/rpc.d.ts +3 -0
  236. package/lib/types/semantic-browser/contracts.d.ts +47 -0
  237. package/lib/types/semantic-browser/definition.d.ts +20 -0
  238. package/lib/types/semantic-browser/program.d.ts +2 -0
  239. package/lib/types/semantic-browser/service.d.ts +26 -0
  240. package/lib/types/semantic-reference/bridge.d.ts +12 -0
  241. package/lib/types/semantic-reference/contracts.d.ts +54 -0
  242. package/lib/types/semantic-reference/environment.d.ts +22 -0
  243. package/lib/types/semantic-reference/index.d.ts +3 -0
  244. package/lib/types/semantic-reference/labels.d.ts +16 -0
  245. package/lib/types/semantic-reference/rpc.d.ts +15 -0
  246. package/lib/types/semantic-reference/search.d.ts +10 -0
  247. package/lib/types/semantic-reference/usage.d.ts +43 -0
  248. package/lib/types/tool-lifecycle.d.ts +4 -0
  249. package/lib/types/workspace-identity.d.ts +6 -0
  250. package/lib/workspace-identity.js +9 -0
  251. package/package.json +99 -53
  252. package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
  253. package/skills/dsh-data-analysis-files/SKILL.md +32 -0
  254. package/skills/dsh-data-analysis-files/references/examples.md +93 -0
  255. package/skills/dsh-data-analysis-presentation/SKILL.md +28 -0
  256. package/skills/dsh-data-analysis-presentation/references/chart-examples.md +18 -0
  257. package/skills/dsh-data-analysis-presentation/references/charts.md +55 -0
  258. package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +36 -0
  259. package/skills/dsh-data-analysis-presentation/references/examples/charts-bins.dataset.json +33 -0
  260. package/skills/dsh-data-analysis-presentation/references/examples/charts-boxes.dataset.json +45 -0
  261. package/skills/dsh-data-analysis-presentation/references/examples/charts-composition.dataset.json +33 -0
  262. package/skills/dsh-data-analysis-presentation/references/examples/charts-funnel.dataset.json +27 -0
  263. package/skills/dsh-data-analysis-presentation/references/examples/charts-precision.dataset.json +21 -0
  264. package/skills/dsh-data-analysis-presentation/references/examples/charts-series.dataset.json +58 -0
  265. package/skills/dsh-data-analysis-presentation/references/examples/charts-shares.dataset.json +33 -0
  266. package/skills/dsh-data-analysis-presentation/references/examples/charts-waterfall.dataset.json +44 -0
  267. package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +393 -0
  268. package/skills/dsh-data-analysis-presentation/references/examples/comparison-selected.dataset.json +24 -0
  269. package/skills/dsh-data-analysis-presentation/references/examples/comparison-summary.dataset.json +22 -0
  270. package/skills/dsh-data-analysis-presentation/references/examples/comparison.dataset.json +18 -0
  271. package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +80 -0
  272. package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +42 -0
  273. package/skills/dsh-data-analysis-presentation/references/examples/computed.dataset.json +12 -0
  274. package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +58 -0
  275. package/skills/dsh-data-analysis-presentation/references/examples/interaction-detail.dataset.json +30 -0
  276. package/skills/dsh-data-analysis-presentation/references/examples/interaction-summary.dataset.json +22 -0
  277. package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +279 -0
  278. package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +27 -0
  279. package/skills/dsh-data-analysis-presentation/references/examples/write-charts.py +73 -0
  280. package/skills/dsh-data-analysis-presentation/references/examples/write-comparison.py +42 -0
  281. package/skills/dsh-data-analysis-presentation/references/examples/write-computed.py +21 -0
  282. package/skills/dsh-data-analysis-presentation/references/examples/write-interaction.py +33 -0
  283. package/skills/dsh-data-analysis-presentation/references/examples.md +44 -0
  284. package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
  285. package/skills/dsh-data-analysis-presentation/references/interaction.md +56 -0
  286. package/skills/dsh-data-analysis-presentation/references/narrative.md +37 -0
  287. package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
  288. package/skills/dsh-data-analysis-presentation/references/schema.md +162 -0
  289. package/lib/datasource/access.js +0 -60
  290. package/lib/datasource/credentials.js +0 -24
  291. package/lib/evidence/bridge-program.js +0 -153
  292. package/lib/evidence/bridge.js +0 -166
  293. package/lib/evidence/index.js +0 -2
  294. package/lib/evidence/sources.js +0 -263
  295. package/lib/types/datasource/access.d.ts +0 -27
  296. package/lib/types/evidence/bridge.d.ts +0 -40
  297. package/lib/types/evidence/index.d.ts +0 -4
  298. package/lib/types/evidence/sources.d.ts +0 -57
  299. package/python/report-kit/dist/dsh_data_analysis_report_kit-3.0.0-py3-none-any.whl +0 -0
  300. package/skills/dsh-data-analysis-report/SKILL.md +0 -57
  301. package/skills/dsh-data-analysis-report/assets/marivo-artifact.js +0 -113
  302. package/skills/dsh-data-analysis-report/assets/marivo-session-dag.js +0 -1201
  303. package/skills/dsh-data-analysis-report/assets/report-data.js +0 -449
package/README.md CHANGED
@@ -1,72 +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.3 共享 Runtime 与 zero-init per-Workspace binding;
8
- - `marivo_help` 实时公共 Help transport;
9
- - `marivo_datasource_test` 的 DSH Credentials 收集与显式 connection test;
10
- - `marivo_datasource_access` 的 30 分钟、最多 64 次 foreground Shell credential lease;
11
- - `marivo_evidence_sources({ session_id, sources })` 的可移植 Artifact-owned Finding 来源交付;
12
- - `emit_dataset(BaseFrame, ...)`、`emit_computed(DataFrame, ...)` 与
13
- `emit_session_trace(SessionGraph, ...)` 的有界 JavaScript 投影;
14
- - 随包分发并挂载的 `dsh-data-analysis-report` Skill,以及 `marivo-analysis` 激活后的按需路由。
9
+ ## 安装
15
10
 
16
- 旧 `marivo_test`、`marivo_report_render`、`ReportDocument`、报告 parser/renderer/publisher、专用报告 Web
17
- 卡片与 replay 入口已删除,不提供 alias。
11
+ ### 环境准备
18
12
 
19
- ## 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`。
20
17
 
21
- 包内 `dshDataAnalysisCompatibility` 是唯一运行时兼容声明:
18
+ 默认安装方式会在首次启动时使用本机 Python 创建独立的虚拟环境,并通过 pip 安装 Marivo,无需预装 Marivo 或 uv。
19
+ 首次启动需要联网下载依赖,请等待环境准备完成。
22
20
 
23
- - DSH distribution 与所有必需 peer 精确使用 `0.1.1-rc.2`;
24
- - Marivo package spec 精确为 `marivo[duckdb,trino,clickhouse]==0.5.3`;
25
- - 项目自有 Runtime marker 为 `dsh-data-analysis-runtime/v2`;
26
- - 子进程策略为 `direct-argv-inherited-env-snapshot-overlay-v2`。
21
+ ### 安装并启动
27
22
 
28
- 管理员解释器的 `marivo.__version__` 与 package identity 必须精确匹配;不使用 capability/version matrix。
23
+ 将插件安装到 Web Profile:
29
24
 
30
- ## Tool contracts
25
+ ```bash
26
+ dsh plugin --profile web add @chengxianglibra/dsh-data-analysis
27
+ ```
28
+
29
+ 安装后启动 Web Profile;如果已在运行,请先退出再重新启动:
31
30
 
32
- ```text
33
- marivo_help({ targets: string[] })
34
- marivo_datasource_test({ name: string })
35
- marivo_datasource_access({ name: string })
36
- marivo_evidence_sources({
37
- session_id: string,
38
- sources: Array<{ artifact_ref: string, finding_id: string }>
39
- })
31
+ ```bash
32
+ dsh --profile web
40
33
  ```
41
34
 
42
- `marivo_datasource_test` 只拥有缺失 Credentials 的 DSH/Web 闭环和显式连接测试;
43
- `marivo_datasource_access` 只签发有界 foreground Shell lease,不执行连接测试。`md.inspect(...)`、
44
- Session recovery、Artifact revalidation、Quality、Evidence 读取、Session Graph 与 `to_pandas()` 都直接使用
45
- Marivo 公共 API,不增加 convenience Tool。
35
+ 在 Web 界面选择分析使用的工作区(Workspace),新建会话并发送一条消息。
36
+ 会话标题旁按顺序显示“数据源”“语义层”“报告”三个入口,浏览页面在原生右侧标签页打开。本文以 Web 界面为例。
46
37
 
47
- ## 报告交付
38
+ ### 本地文件分析
48
39
 
49
- 仅当用户明确请求 HTML/Web 或耐久报告、接受生成提议,或修改已有 bundle 时,Agent 加载
50
- `dsh-data-analysis-report`;普通长回答或多图表/表格不会自动产出文件。已有分析恢复并 revalidate persisted
51
- Artifacts,不为报告展示重新执行 `observe`。
52
- 该 Skill 只提供内容组织、布局、样式和检查原则,不包含页面 Starter、通用 chart helper、可视化 DSL 或
53
- HTML Checker。配套 assets 提供 `ReportData` 读取、精简 Artifact 摘要和 Session DAG;Python report-kit
54
- 分别通过 `emit_dataset`、`emit_computed`、`emit_session_trace` 发射 Artifact、pandas DataFrame 与 Graph
55
- 快照;Artifact 与 Graph emitter 默认 `reader`,明确审计请求才使用 `audit`。多 Session 的独立 Graph 集中展示,
56
- Frame preview 只按精确 Session 与 Artifact identity 关联。Agent 自由生成
57
- `<workspace>/<new-report-directory>/index.html` 与可选相对资源。Native/both 的
58
- 最终入口使用顶层 DSH `write` / `edit`,并以精确 Markdown 行内路径交付,让 DSH Web 复用 Produced Files
59
- 提供点击;Code-only 在 `run_code` 内使用同一 Tool,但嵌套 mutation 只保证精确路径。插件不创建报告对象、
60
- digest、不可变发布、历史字节 replay 或 share link。
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。
61
46
 
62
- ## 验证
47
+ 纯文件分析无需经过 Marivo `md.raw_sql`。下面的数据源配置流程适用于数据库连接和需要复用的 Marivo 数据源。
63
48
 
64
- ```bash
65
- npm run check
66
- npm run build
67
- npm run verify:plugin-package
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
68
83
  ```
69
84
 
70
- 当前架构与验收边界见仓库根目录的[总体架构](../../docs/architecture.md)、
71
- [插件能力优化设计](../../docs/plan/plugin-capability-optimization-design.md)和
72
- [v2 验收记录](../../docs/acceptance/plugin-capability-optimization-v2.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
+ 报告右上角的导出菜单提供两种方式:
144
+
145
+ | 选项 | 适合的用途 |
146
+ | --- | --- |
147
+ | 下载完整报告 | 下载所查看版本的完整 HTML 报告,供离线阅读 |
148
+ | 导出当前视图 | 将当前筛选、图形和表格排序固定为 HTML,附带来源版本及导出时间,分享当前看到的内容 |
149
+
150
+ 编辑模式下,请先保存或取消编辑再导出。下载的文件可直接用浏览器打开。
151
+
152
+ ## 许可证
153
+
154
+ 本项目采用 [MIT License](LICENSE)。
155
+
156
+ ## 报告发布到对象存储
157
+
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,9 +5,9 @@ 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
- Ensures the shared pinned Marivo and report-kit Runtime, initializes the minimal
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
12
12
  admission used by the DSH plugin. The JSON output contains no credentials or raw
13
13
  doctor details.
@@ -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,21 @@
1
+ #!/usr/bin/env node
2
+ import path from 'node:path';
3
+ import process from 'node:process';
4
+ import { lintPresentationDraft } from "../presentation/lint.js";
5
+ const usage = 'Usage: dsh-data-analysis-presentation-lint DRAFT_PATH [--project-root PATH]\n';
6
+ const args = process.argv.slice(2);
7
+ if (args.length === 1 && (args[0] === '--help' || args[0] === '-h')) {
8
+ process.stdout.write(usage);
9
+ }
10
+ else if ((args.length !== 1 && args.length !== 3) ||
11
+ !args[0] ||
12
+ args[0].startsWith('-') ||
13
+ (args.length === 3 && (args[1] !== '--project-root' || !args[2]))) {
14
+ process.stderr.write(usage);
15
+ process.exitCode = 2;
16
+ }
17
+ else {
18
+ const result = await lintPresentationDraft(path.resolve(args[2] ?? process.cwd()), args[0]);
19
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
20
+ process.exitCode = result.ok ? 0 : 1;
21
+ }
package/lib/bridges.js CHANGED
@@ -1,11 +1,11 @@
1
1
  import { MarivoDatasourceBridge } from "./datasource/bridge.js";
2
2
  import { MarivoHelpBridge } from "./disclosure/bridge.js";
3
- import { MarivoEvidenceBridge } from "./evidence/bridge.js";
3
+ import { MarivoPresentationProjection } from "./presentation/projection/index.js";
4
4
  /** Compose domain adapters without adding domain methods back to the Environment. */
5
5
  export function createMarivoBridgeSet(environment) {
6
6
  return Object.freeze({
7
7
  help: new MarivoHelpBridge(environment),
8
8
  datasource: new MarivoDatasourceBridge(environment),
9
- evidence: new MarivoEvidenceBridge(environment),
9
+ presentation: new MarivoPresentationProjection(environment),
10
10
  });
11
11
  }
@@ -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
+ }
@@ -0,0 +1,171 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ // @ts-nocheck -- JSX is bundled by the plugin client build.
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);
34
+ const [schema, setSchema] = useState(null);
35
+ const [original, setOriginal] = useState(null);
36
+ const [backend, setBackend] = useState('');
37
+ const [values, setValues] = useState({});
38
+ const [credentials, setCredentials] = useState([]);
39
+ const [busy, setBusy] = useState(false);
40
+ const [error, setError] = useState('');
41
+ useEffect(() => {
42
+ const controller = new AbortController();
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]) => {
49
+ if (controller.signal.aborted)
50
+ return;
51
+ setSchema(value);
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
+ }
70
+ })
71
+ .catch((error) => {
72
+ if (!controller.signal.aborted)
73
+ setError(errorMessage(error));
74
+ });
75
+ return () => {
76
+ active.current = false;
77
+ controller.abort();
78
+ };
79
+ }, [model, workspaceId, name]);
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)));
83
+ async function submit(event) {
84
+ event.preventDefault();
85
+ if (busy)
86
+ return;
87
+ setError('');
88
+ const input = {};
89
+ let changes = {};
90
+ try {
91
+ for (const field of fields.filter((field) => !field.name.endsWith('_env'))) {
92
+ const value = values[field.name];
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] !== '')))
100
+ continue;
101
+ input[field.name] = field.type === 'string' ? value : JSON.parse(value);
102
+ }
103
+ const prepared = prepareCredentials(input.name ?? '', credentials);
104
+ Object.assign(input, prepared.fields);
105
+ changes = prepared.changes;
106
+ }
107
+ catch (error) {
108
+ setError(error instanceof SyntaxError
109
+ ? 'marivo.credentials.check-the-format-of-number-boolean-and-json-fields'
110
+ : errorMessage(error));
111
+ return;
112
+ }
113
+ setBusy(true);
114
+ // Remove secrets from mounted inputs as soon as the explicit submission begins.
115
+ setCredentials((rows) => rows.map((row) => ({ ...row, value: '' })));
116
+ try {
117
+ await model.saveConfiguration(workspaceId, schema, { backend, fields: input }, original, requestId, changes);
118
+ if (active.current)
119
+ close();
120
+ }
121
+ catch (error) {
122
+ if (active.current)
123
+ setError(message('marivo.credentials.value-credential-inputs-have-been-cleared-enter-them-again', {
124
+ p0: errorMessage(error),
125
+ }));
126
+ }
127
+ finally {
128
+ for (const ref of Object.keys(changes))
129
+ delete changes[ref];
130
+ if (active.current)
131
+ setBusy(false);
132
+ }
133
+ }
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) => {
137
+ setBackend(event.target.value);
138
+ setValues(creationFieldValues(schema, event.target.value));
139
+ setCredentials(credentialRows(schema, event.target.value));
140
+ setError('');
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
144
+ ? field.type === 'json'
145
+ ? 'JSON'
146
+ : ''
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') })] })] }));
171
+ }
@@ -0,0 +1,42 @@
1
+ export function credentialReference(name, draft) {
2
+ if (draft.reference !== undefined)
3
+ return draft.reference.trim();
4
+ if (!draft.value)
5
+ return draft.existing ?? '';
6
+ const part = (value) => value
7
+ .toUpperCase()
8
+ .replace(/[^A-Z0-9_]/g, '_')
9
+ .slice(0, 40);
10
+ return `DS_${part(name) || 'CONNECTION'}_${part(draft.key ?? draft.field.replace(/_env$/, ''))}_${draft.id.replace(/-/g, '').toUpperCase()}`;
11
+ }
12
+ /** Values are kept separate from the public configuration, then handed to the credential operation. */
13
+ export function prepareCredentials(name, drafts) {
14
+ const fields = {};
15
+ const changes = Object.create(null);
16
+ for (const draft of drafts) {
17
+ const ref = credentialReference(name, draft);
18
+ if (!ref && !draft.value)
19
+ continue;
20
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(ref) ||
21
+ ref.length > 256 ||
22
+ /^(MARIVO_|DSH_DATA_ANALYSIS_|DSH_HOME$|DSH_SESSION_ID$|DSH_SESSION_JSONL$|DSH_SHELL$)/i.test(ref))
23
+ throw new Error('marivo.credentials.credential-references-may-contain-only-letters-numbers-and-underscores');
24
+ if (draft.key !== undefined) {
25
+ if (!draft.key.trim())
26
+ throw new Error('marivo.credentials.enter-the-header-name-for-this-credential');
27
+ fields[draft.field] ??= Object.create(null);
28
+ const mapping = fields[draft.field];
29
+ if (Object.hasOwn(mapping, draft.key))
30
+ throw new Error('marivo.credentials.header-names-must-be-unique');
31
+ mapping[draft.key] = ref;
32
+ }
33
+ else
34
+ fields[draft.field] = ref;
35
+ if (draft.value) {
36
+ if (changes[ref] !== undefined && changes[ref] !== draft.value)
37
+ throw new Error('marivo.credentials.the-same-credential-reference-cannot-have-different-values');
38
+ changes[ref] = draft.value;
39
+ }
40
+ }
41
+ return { fields, changes };
42
+ }
@@ -0,0 +1,11 @@
1
+ export function creationFieldValues(schema, backend) {
2
+ const defaults = schema.creationDefaults?.[backend] ?? {};
3
+ return Object.fromEntries((schema.backends.find((item) => item.name === backend)?.fields ?? [])
4
+ .filter((field) => !field.name.endsWith('_env') && Object.hasOwn(defaults, field.name))
5
+ .map((field) => [
6
+ field.name,
7
+ field.type === 'string'
8
+ ? defaults[field.name]
9
+ : JSON.stringify(defaults[field.name]),
10
+ ]));
11
+ }