@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
@@ -46,7 +46,7 @@ function parsePackageManifest(value) {
46
46
  packageSpec: requiredString(marivo.packageSpec, 'package compatibility marivo.packageSpec'),
47
47
  }),
48
48
  contracts: Object.freeze({
49
- runtimeInstallation: requiredLiteral(contracts.runtimeInstallation, 'dsh-data-analysis-runtime/v2', 'package compatibility contracts.runtimeInstallation'),
49
+ runtimeInstallation: requiredLiteral(contracts.runtimeInstallation, 'dsh-data-analysis-runtime/v3', 'package compatibility contracts.runtimeInstallation'),
50
50
  subprocessPolicy: requiredLiteral(contracts.subprocessPolicy, 'direct-argv-inherited-env-snapshot-overlay-v2', 'package compatibility contracts.subprocessPolicy'),
51
51
  }),
52
52
  }),
@@ -56,7 +56,7 @@ const packageManifest = parsePackageManifest(createRequire(import.meta.url)('../
56
56
  /** The package semver and the compatibility contract shipped in this exact installation. */
57
57
  export const PLUGIN_VERSION = packageManifest.version;
58
58
  export const DSH_DATA_ANALYSIS_COMPATIBILITY = packageManifest.dshDataAnalysisCompatibility;
59
- /** Every DSH peer is required and must use this one verified release. */
59
+ /** Every DSH peer is required and must satisfy this declared compatibility range. */
60
60
  export const DSH_PEER_RANGE = DSH_DATA_ANALYSIS_COMPATIBILITY.dsh.peerRange;
61
61
  export const DSH_DISTRIBUTION = DSH_DATA_ANALYSIS_COMPATIBILITY.dsh.distribution;
62
62
  export const DSH_PEER_DEPENDENCIES = Object.freeze(Object.fromEntries(Object.entries(packageManifest.peerDependencies).filter(([name]) => name.startsWith('@deepseek-ai/dsh-'))));
@@ -0,0 +1,109 @@
1
+ import { z } from 'zod';
2
+ export const datasourceAuthoringSchema = z.object({
3
+ fingerprint: z.string(),
4
+ backends: z.array(z.object({
5
+ name: z.string(),
6
+ fields: z.array(z.object({
7
+ name: z.string(),
8
+ type: z.enum(['string', 'number', 'boolean', 'json']),
9
+ required: z.boolean(),
10
+ description: z.string(),
11
+ default: z.unknown().optional(),
12
+ })),
13
+ })),
14
+ });
15
+ // The public DatasourceSpec union and dataclass fields are read from the checked Runtime.
16
+ // No backend registry or field schema is duplicated in the plugin.
17
+ export const DATASOURCE_AUTHORING_PROGRAM = String.raw `
18
+ import contextlib
19
+ import dataclasses
20
+ import hashlib
21
+ import json
22
+ import sys
23
+ import typing
24
+ import marivo.datasource as md
25
+
26
+ class Discard:
27
+ def write(self, value): return len(value)
28
+ def flush(self): pass
29
+
30
+ def spec_fields(cls):
31
+ hints = typing.get_type_hints(cls)
32
+ result = []
33
+ for field in dataclasses.fields(cls):
34
+ if not field.init or field.name == "ai_context":
35
+ continue
36
+ annotation = hints[field.name]
37
+ types = set(typing.get_args(annotation) or [annotation]) - {type(None)}
38
+ kind = "string" if types == {str} else "boolean" if types == {bool} else "number" if types <= {int, float} else "json"
39
+ item = {"name": field.name, "type": kind,
40
+ "required": field.default is dataclasses.MISSING and field.default_factory is dataclasses.MISSING,
41
+ "description": field.metadata.get("description", "")}
42
+ if field.default is not dataclasses.MISSING:
43
+ item["default"] = field.default
44
+ result.append(item)
45
+ return result
46
+
47
+ def configuration(name, cls):
48
+ import marivo.semantic as ms
49
+ description = md.describe(name)
50
+ details = ms.load().datasources.get(name).details()
51
+ if details.backend_type != description.backend_type or dict(details.fields) != description.literal_fields or dict(details.env_refs) != description.env_refs:
52
+ raise ValueError("context-changed")
53
+ context = {"business_definition": details.context.business_definition,
54
+ "guardrails": list(details.context.guardrails)}
55
+ allowed = {field["name"] for field in spec_fields(cls)}
56
+ fields = {"name": name}
57
+ extra = {}
58
+ for key, value in description.literal_fields.items():
59
+ if key in allowed and key != "extra": fields[key] = value
60
+ else: extra[key] = value
61
+ if extra: fields["extra"] = extra
62
+ for key, value in description.env_refs.items():
63
+ if key.startswith("http_header:"):
64
+ fields.setdefault("http_headers_env", {})[key.removeprefix("http_header:")] = value
65
+ elif key + "_env" in allowed: fields[key + "_env"] = value
66
+ else: raise ValueError("datasource-definition-invalid")
67
+ version = hashlib.sha256(json.dumps([description.backend_type, fields, context], sort_keys=True).encode()).hexdigest()
68
+ return {"name": name, "backend": description.backend_type, "fields": fields, "version": version}, context
69
+
70
+ try:
71
+ payload = json.load(sys.stdin)
72
+ with contextlib.redirect_stdout(Discard()), contextlib.redirect_stderr(Discard()):
73
+ specs = {cls.backend_type: cls for cls in typing.get_args(md.DatasourceSpec)}
74
+ if payload["action"] == "schema":
75
+ result = {"backends": [{"name": key, "fields": spec_fields(cls)} for key, cls in specs.items()]}
76
+ elif payload["action"] == "read":
77
+ description = md.describe(payload["name"])
78
+ result, _ = configuration(payload["name"], specs[description.backend_type])
79
+ else:
80
+ cls = specs[payload["backend"]]
81
+ fields = payload["fields"]
82
+ allowed = {field["name"] for field in spec_fields(cls)}
83
+ context = None
84
+ if payload["action"] == "update":
85
+ current = md.describe(payload["name"])
86
+ if fields.get("name") != payload["name"] or current.backend_type != payload["backend"]:
87
+ raise ValueError("datasource-identity-fixed")
88
+ original, context = configuration(payload["name"], cls)
89
+ if original["version"] != payload["version"]:
90
+ raise ValueError("datasource-config-changed")
91
+ if set(fields) - allowed:
92
+ raise ValueError("unknown-field")
93
+ spec = cls(**fields)
94
+ if payload["action"] == "create" and any(item.name == spec.name for item in md.list()):
95
+ result = {"error": "datasource-already-exists"}
96
+ else:
97
+ if sorted(spec.env_refs.values()) != sorted(payload["refs"]):
98
+ raise ValueError("credential-reference-mismatch")
99
+ if context is not None:
100
+ import marivo.semantic as ms
101
+ spec = cls(**fields, ai_context=ms.ai_context(**context))
102
+ md.register(spec)
103
+ result = {"name": spec.name}
104
+
105
+ except Exception as exc:
106
+ code = str(exc)
107
+ result = {"error": code if code in ("datasource-config-changed", "datasource-identity-fixed", "context-changed") else "datasource-definition-invalid"}
108
+ print(json.dumps(result))
109
+ `.trim();
@@ -1,31 +1,48 @@
1
- export const MARIVO_DATASOURCE_DESCRIBE_PROGRAM = String.raw `
1
+ import { RESOLVER_PROGRAM } from "./resolver-program.js";
2
+ const DATASOURCE_DESCRIPTION_PROGRAM = String.raw `
2
3
  import json
3
4
  import os
4
5
  import sys
5
6
  import marivo
6
7
  import marivo.datasource as md
7
8
 
8
- description = md.describe(sys.argv[1])
9
- print(json.dumps({
10
- "name": description.name,
11
- "refs": list(description.env_refs.values()),
12
- }, sort_keys=True))
13
- `.trim();
14
- export const MARIVO_DATASOURCE_INVENTORY_PROGRAM = String.raw `
15
- import json
16
- import os
17
- import sys
18
- import marivo
19
- import marivo.datasource as md
9
+ import hashlib
10
+
11
+ def project_description(description):
12
+ payload = {"name": description.name, "backend": description.backend_type,
13
+ "fields": description.literal_fields, "refs": description.env_refs}
14
+ return {"name": description.name, "backend": description.backend_type,
15
+ "properties": description.literal_fields, "refs": list(description.env_refs.values()),
16
+ "fields": description.env_refs,
17
+ "definition": hashlib.sha256(json.dumps(payload, sort_keys=True, default=str).encode()).hexdigest()}
20
18
 
19
+ `.trim();
20
+ export const MARIVO_DATASOURCE_DESCRIBE_PROGRAM = `${DATASOURCE_DESCRIPTION_PROGRAM}
21
+ print(json.dumps(project_description(md.describe(sys.argv[1])), sort_keys=True))`;
22
+ export const MARIVO_DATASOURCE_REMOVE_PROGRAM = `${DATASOURCE_DESCRIPTION_PROGRAM}
23
+ import contextlib
24
+ class Discard:
25
+ def write(self, value): return len(value)
26
+ def flush(self): pass
27
+ try:
28
+ payload = json.load(sys.stdin)
29
+ with contextlib.redirect_stdout(Discard()), contextlib.redirect_stderr(Discard()):
30
+ current = project_description(md.describe(payload["name"]))
31
+ if current["definition"] != payload["definition"]:
32
+ result = {"error": "context-changed"}
33
+ elif md.remove(payload["name"]):
34
+ result = {"name": payload["name"]}
35
+ else:
36
+ result = {"error": "datasource-not-removable"}
37
+ except Exception:
38
+ result = {"error": "datasource-remove-failed"}
39
+ print(json.dumps(result))`;
40
+ export const MARIVO_DATASOURCE_INVENTORY_PROGRAM = `${DATASOURCE_DESCRIPTION_PROGRAM}
21
41
  print(json.dumps({
22
- "datasources": [{
23
- "name": description.name,
24
- "refs": list(description.env_refs.values()),
25
- } for description in (md.describe(item.name) for item in md.list())],
42
+ "datasources": [project_description(md.describe(item.name)) for item in md.list()],
26
43
  }, sort_keys=True))
27
- `.trim();
28
- export const MARIVO_DATASOURCE_TEST_PROGRAM = String.raw `
44
+ `;
45
+ export const MARIVO_DATASOURCE_TEST_PROGRAM = `${RESOLVER_PROGRAM}\n${String.raw `
29
46
  import contextlib
30
47
  import io
31
48
  import json
@@ -34,17 +51,15 @@ import sys
34
51
  import marivo
35
52
  import marivo.datasource as md
36
53
 
54
+ payload = json.load(sys.stdin)
55
+ resolver = SnapshotResolver(payload)
56
+ secret_values = list(payload["values"].values())
37
57
  captured_stdout = io.StringIO()
38
58
  captured_stderr = io.StringIO()
39
59
  try:
40
60
  with contextlib.redirect_stdout(captured_stdout), contextlib.redirect_stderr(captured_stderr):
41
- description = md.describe(sys.argv[1])
42
- secret_values = [
43
- value
44
- for ref in description.env_refs.values()
45
- if (value := os.environ.get(ref))
46
- ]
47
- result = md.test(sys.argv[1])
61
+ with md.credential_scope(resolver=resolver):
62
+ result = md.test(sys.argv[1])
48
63
  except Exception as exc:
49
64
  print(json.dumps({"kind": "datasource-test-failed", "exception_type": type(exc).__name__}), file=sys.stderr)
50
65
  raise SystemExit(70)
@@ -77,4 +92,4 @@ print(json.dumps(redact({
77
92
  "failure": failure,
78
93
  "repair": repair,
79
94
  }), sort_keys=True))
80
- `.trim();
95
+ `.trim()}`;
@@ -1,7 +1,9 @@
1
1
  import { isCredentialRefName } from '@deepseek-ai/dsh-credentials';
2
2
  import { MarivoEnvironmentError } from "../environment/errors.js";
3
3
  import { resolveMarivoBridgeSource } from "../environment/source.js";
4
- import { MARIVO_DATASOURCE_DESCRIBE_PROGRAM, MARIVO_DATASOURCE_INVENTORY_PROGRAM, MARIVO_DATASOURCE_TEST_PROGRAM, } from "./bridge-programs.js";
4
+ import { DATASOURCE_AUTHORING_PROGRAM, datasourceAuthoringSchema, } from "./authoring.js";
5
+ import { MARIVO_DATASOURCE_DESCRIBE_PROGRAM, MARIVO_DATASOURCE_INVENTORY_PROGRAM, MARIVO_DATASOURCE_REMOVE_PROGRAM, MARIVO_DATASOURCE_TEST_PROGRAM, } from "./bridge-programs.js";
6
+ import { marivoCredentialStorageRef } from "./shell-env.js";
5
7
  const DATASOURCE_LIMITS = Object.freeze({
6
8
  timeoutMs: 30_000,
7
9
  stdoutMaxBytes: 262_144,
@@ -24,6 +26,16 @@ function string(value) {
24
26
  function nullableString(value) {
25
27
  return value === null || typeof value === 'string';
26
28
  }
29
+ function jsonValue(value) {
30
+ if (value === null || typeof value === 'string' || typeof value === 'boolean')
31
+ return true;
32
+ if (typeof value === 'number')
33
+ return Number.isFinite(value);
34
+ if (Array.isArray(value))
35
+ return value.every(jsonValue);
36
+ const fields = object(value);
37
+ return fields !== undefined && Object.values(fields).every(jsonValue);
38
+ }
27
39
  function parseJsonObject(stdout, phase) {
28
40
  try {
29
41
  const parsed = JSON.parse(stdout.toString('utf8'));
@@ -41,15 +53,24 @@ function parseDescription(value, phase) {
41
53
  if (source === undefined) {
42
54
  throw new MarivoEnvironmentError('subprocess-output-invalid', `Marivo datasource ${phase} returned invalid fields`, { phase });
43
55
  }
44
- exactKeys(source, ['name', 'refs'], phase);
56
+ exactKeys(source, ['name', 'backend', 'properties', 'refs', 'fields', 'definition'], phase);
45
57
  const name = string(source.name);
46
- if (name === undefined || !Array.isArray(source.refs)) {
58
+ const backend = string(source.backend);
59
+ const properties = object(source.properties);
60
+ if (name === undefined ||
61
+ name.length > 256 ||
62
+ backend === undefined ||
63
+ backend.length > 256 ||
64
+ properties === undefined ||
65
+ !jsonValue(properties) ||
66
+ !Array.isArray(source.refs) ||
67
+ source.refs.length > 128) {
47
68
  throw new MarivoEnvironmentError('subprocess-output-invalid', `Marivo datasource ${phase} returned invalid fields`, { phase });
48
69
  }
49
70
  const refs = [];
50
71
  const seen = new Set();
51
72
  for (const ref of source.refs) {
52
- if (typeof ref !== 'string' || !isCredentialRefName(ref)) {
73
+ if (typeof ref !== 'string' || ref.length > 256 || !isCredentialRefName(ref)) {
53
74
  throw new MarivoEnvironmentError('subprocess-output-invalid', `Marivo datasource ${phase} returned an invalid credential reference`, { phase });
54
75
  }
55
76
  if (!seen.has(ref)) {
@@ -57,7 +78,21 @@ function parseDescription(value, phase) {
57
78
  refs.push(ref);
58
79
  }
59
80
  }
60
- return { name, refs };
81
+ const fields = object(source.fields);
82
+ if (!fields ||
83
+ typeof source.definition !== 'string' ||
84
+ !/^[a-f0-9]{64}$/.test(source.definition) ||
85
+ Object.values(fields).some((ref) => typeof ref !== 'string' || !refs.includes(ref)) ||
86
+ refs.some((ref) => !Object.values(fields).includes(ref)))
87
+ throw new Error('Invalid datasource definition projection');
88
+ return {
89
+ name,
90
+ backend,
91
+ properties: properties,
92
+ refs,
93
+ fields: fields,
94
+ definition: source.definition,
95
+ };
61
96
  }
62
97
  function parseInventory(stdout) {
63
98
  const value = parseJsonObject(stdout, 'inventory');
@@ -87,7 +122,9 @@ function parseRepair(value) {
87
122
  throw new TypeError('repair must be an object');
88
123
  exactKeys(source, ['kind', 'help_target', 'action', 'snippet', 'candidates', 'preserves_evidence'], 'test');
89
124
  if (string(source.kind) === undefined ||
90
- string(source.help_target) === undefined ||
125
+ object(source.help_target) === undefined ||
126
+ string(object(source.help_target)?.surface) === undefined ||
127
+ !nullableString(object(source.help_target)?.canonical_id) ||
91
128
  typeof source.action !== 'string' ||
92
129
  !nullableString(source.snippet) ||
93
130
  !Array.isArray(source.candidates) ||
@@ -160,6 +197,81 @@ export class MarivoDatasourceBridge {
160
197
  this.#runner = runner;
161
198
  this.binding = runner.binding;
162
199
  }
200
+ async authoring(signal) {
201
+ const result = await this.#runner.runChecked({
202
+ program: DATASOURCE_AUTHORING_PROGRAM,
203
+ stdin: JSON.stringify({ action: 'schema' }),
204
+ limits: DATASOURCE_LIMITS,
205
+ signal,
206
+ });
207
+ this.#assertSuccess(result, 'inventory');
208
+ return datasourceAuthoringSchema.parse({
209
+ ...parseJsonObject(result.stdout, 'inventory'),
210
+ fingerprint: this.binding.fingerprint,
211
+ });
212
+ }
213
+ async configuration(name, signal) {
214
+ const result = await this.#runner.runChecked({
215
+ program: DATASOURCE_AUTHORING_PROGRAM,
216
+ stdin: JSON.stringify({ action: 'read', name }),
217
+ limits: DATASOURCE_LIMITS,
218
+ signal,
219
+ });
220
+ this.#assertSuccess(result, 'inventory');
221
+ const value = parseJsonObject(result.stdout, 'inventory');
222
+ if (value.name !== name ||
223
+ typeof value.backend !== 'string' ||
224
+ typeof value.version !== 'string' ||
225
+ !object(value.fields) ||
226
+ !jsonValue(value.fields))
227
+ throw new Error('Datasource configuration cannot be read completely');
228
+ return value;
229
+ }
230
+ async update(input, signal) {
231
+ return this.#write(input, 'update', signal);
232
+ }
233
+ async create(input, signal) {
234
+ return this.#write(input, 'create', signal);
235
+ }
236
+ async #write(input, action, signal) {
237
+ const refs = Object.entries(input.fields)
238
+ .filter(([key]) => key.endsWith('_env'))
239
+ .flatMap(([, value]) => typeof value === 'string'
240
+ ? [value]
241
+ : value && typeof value === 'object' && !Array.isArray(value)
242
+ ? Object.values(value)
243
+ : []);
244
+ for (const ref of refs) {
245
+ if (typeof ref !== 'string')
246
+ return { error: 'datasource-credential-ref-invalid' };
247
+ try {
248
+ marivoCredentialStorageRef(ref);
249
+ }
250
+ catch (error) {
251
+ if (error instanceof MarivoEnvironmentError &&
252
+ error.code === 'datasource-credential-ref-invalid')
253
+ return { error: 'datasource-credential-ref-invalid' };
254
+ throw error;
255
+ }
256
+ }
257
+ const result = await this.#runner.runChecked({
258
+ program: DATASOURCE_AUTHORING_PROGRAM,
259
+ stdin: JSON.stringify({ action, ...input, refs }),
260
+ limits: DATASOURCE_LIMITS,
261
+ signal,
262
+ });
263
+ this.#assertSuccess(result, 'inventory');
264
+ const value = parseJsonObject(result.stdout, 'inventory');
265
+ if (value.error === 'datasource-already-exists' ||
266
+ value.error === 'datasource-definition-invalid' ||
267
+ value.error === 'datasource-config-changed' ||
268
+ value.error === 'datasource-identity-fixed' ||
269
+ value.error === 'context-changed')
270
+ return { error: value.error };
271
+ if (typeof value.name !== 'string')
272
+ throw new Error('Invalid datasource creation result');
273
+ return { name: value.name };
274
+ }
163
275
  async describe(name, signal) {
164
276
  const result = await this.#runner.runChecked({
165
277
  program: MARIVO_DATASOURCE_DESCRIBE_PROGRAM,
@@ -170,6 +282,23 @@ export class MarivoDatasourceBridge {
170
282
  this.#assertSuccess(result, 'describe');
171
283
  return parseDescription(parseJsonObject(result.stdout, 'describe'), 'describe');
172
284
  }
285
+ async remove(description, signal) {
286
+ const result = await this.#runner.runChecked({
287
+ program: MARIVO_DATASOURCE_REMOVE_PROGRAM,
288
+ stdin: JSON.stringify({ name: description.name, definition: description.definition }),
289
+ limits: DATASOURCE_LIMITS,
290
+ signal,
291
+ });
292
+ this.#assertSuccess(result, 'inventory');
293
+ const value = parseJsonObject(result.stdout, 'inventory');
294
+ if (value.error === 'context-changed' ||
295
+ value.error === 'datasource-not-removable' ||
296
+ value.error === 'datasource-remove-failed')
297
+ return { error: value.error };
298
+ if (value.name !== description.name)
299
+ throw new Error('Invalid datasource removal result');
300
+ return { name: description.name };
301
+ }
173
302
  async inventory(signal) {
174
303
  const result = await this.#runner.runChecked({
175
304
  program: MARIVO_DATASOURCE_INVENTORY_PROGRAM,
@@ -179,16 +308,22 @@ export class MarivoDatasourceBridge {
179
308
  this.#assertSuccess(result, 'inventory');
180
309
  return parseInventory(result.stdout);
181
310
  }
182
- async test(name, environmentOverlay, signal) {
311
+ async test(description, values, signal) {
312
+ const { name } = description;
183
313
  const result = await this.#runner.runChecked({
184
314
  program: MARIVO_DATASOURCE_TEST_PROGRAM,
185
315
  args: [name],
186
- environmentOverlay,
316
+ stdin: JSON.stringify({
317
+ project_root: this.binding.projectRoot,
318
+ grants: { [name]: description },
319
+ values,
320
+ }),
321
+ secretValues: Object.values(values),
187
322
  limits: DATASOURCE_LIMITS,
188
323
  signal,
189
324
  });
190
325
  this.#assertSuccess(result, 'test');
191
- return parseTest(result.stdout, Object.values(environmentOverlay).filter((value) => Boolean(value)));
326
+ return parseTest(result.stdout, Object.values(values));
192
327
  }
193
328
  #assertSuccess(result, phase) {
194
329
  if (result.exitCode === 0)
@@ -0,0 +1,41 @@
1
+ import { z } from 'zod';
2
+ export const CREDENTIAL_CHANGES_SERVICE = 'dshDataAnalysisCredentials';
3
+ const owner = '@chengxianglibra/dsh-data-analysis';
4
+ const sessionId = z.string().min(1).max(256);
5
+ export const credentialRevisionSchema = z
6
+ .object({ generation: z.string().uuid(), cursor: z.string().min(1).max(256) })
7
+ .strict();
8
+ /** Plugin-owned strict boundary shared by both faces; no Host registry is duplicated. */
9
+ const descriptor = {
10
+ id: `${owner}#${CREDENTIAL_CHANGES_SERVICE}/changes`,
11
+ service: CREDENTIAL_CHANGES_SERVICE,
12
+ namespace: CREDENTIAL_CHANGES_SERVICE,
13
+ method: 'changes',
14
+ mode: 'stream',
15
+ invocation: { kind: 'direct' },
16
+ parameters: [
17
+ {
18
+ name: 'sessionId',
19
+ wire: 'sessionId',
20
+ source: 'json',
21
+ codec: { mode: 'strict', typeSymbol: `${owner}#CredentialSessionId`, schema: sessionId },
22
+ },
23
+ ],
24
+ cancellation: { parameter: 'signal' },
25
+ result: {
26
+ mode: 'strict',
27
+ typeSymbol: `${owner}#CredentialRevision`,
28
+ schema: credentialRevisionSchema,
29
+ },
30
+ };
31
+ export const credentialChangesHost = {
32
+ package: owner,
33
+ face: 'host',
34
+ schemas: [],
35
+ model: { services: [], events: [], objects: [] },
36
+ invocations: [descriptor],
37
+ };
38
+ export const credentialChangesRemote = {
39
+ package: owner,
40
+ descriptors: [descriptor],
41
+ };
@@ -0,0 +1,49 @@
1
+ import { setTimeout as delay } from 'node:timers/promises';
2
+ import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
3
+ import { PendingTasks } from "../lifecycle.js";
4
+ import { CREDENTIAL_CHANGES_SERVICE } from "./changes-contract.js";
5
+ import { CredentialServiceError } from "./service.js";
6
+ /** Reuses the Host's logical stream carrier; credentials and waiting calls keep their owner. */
7
+ export class CredentialChangesService extends TypertRemoteService {
8
+ credentials;
9
+ lifetime = new AbortController();
10
+ tasks = new PendingTasks();
11
+ closing;
12
+ constructor(ctx, credentials) {
13
+ super(ctx, CREDENTIAL_CHANGES_SERVICE);
14
+ this.credentials = credentials;
15
+ }
16
+ async *changes(sessionId, caller) {
17
+ const signal = AbortSignal.any([caller, this.lifetime.signal]);
18
+ let cursor;
19
+ while (!signal.aborted) {
20
+ const snapshot = await this.tasks.track(this.snapshot(sessionId, cursor, signal));
21
+ signal.throwIfAborted();
22
+ if (snapshot.cursor !== cursor) {
23
+ cursor = snapshot.cursor;
24
+ yield { generation: snapshot.generation, cursor };
25
+ }
26
+ }
27
+ }
28
+ async snapshot(sessionId, cursor, signal) {
29
+ let attempt = 0;
30
+ for (;;) {
31
+ signal.throwIfAborted();
32
+ try {
33
+ return await this.credentials.waitWatch(sessionId, cursor, signal);
34
+ }
35
+ catch (error) {
36
+ signal.throwIfAborted();
37
+ if (!(error instanceof CredentialServiceError) ||
38
+ error.code !== 'credential-state-unavailable')
39
+ throw new Error('credential-notification-unavailable');
40
+ await delay([1000, 2000, 5000][Math.min(attempt++, 2)], undefined, { signal });
41
+ }
42
+ }
43
+ }
44
+ close() {
45
+ this.lifetime.abort();
46
+ this.closing ??= this.tasks.drain();
47
+ return this.closing;
48
+ }
49
+ }
@@ -0,0 +1,39 @@
1
+ import { defineTool } from '@deepseek-ai/dsh-tools';
2
+ import { registerMarivoTool } from "../tool-lifecycle.js";
3
+ import { resolveMarivoDatasourceBridge } from "./bridge.js";
4
+ export const MARIVO_DATASOURCE_CONFIGURE_TOOL_NAME = 'marivo_datasource_configure';
5
+ export function createMarivoDatasourceConfigureTool(source, service) {
6
+ return defineTool({
7
+ name: MARIVO_DATASOURCE_CONFIGURE_TOOL_NAME,
8
+ description: 'Ask the user to create/select or edit a datasource in the owning Web session right tab. Waits until the user saves and tests successfully, cancels, or hands off a failure. Never pass credentials or configuration values. After success, rediscover the datasource and verify the requested table before analysis.',
9
+ parameters: {
10
+ mode: {
11
+ type: 'string',
12
+ required: true,
13
+ description: 'create (also permits choosing an existing datasource) or edit (fixed name and engine).',
14
+ },
15
+ name: { type: 'string', description: 'Existing datasource name, required only for edit.' },
16
+ reason: {
17
+ type: 'string',
18
+ required: true,
19
+ description: 'Brief reason for configuration, at most 1000 characters. No secrets.',
20
+ },
21
+ },
22
+ output: {
23
+ schema: { type: 'json' },
24
+ render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
25
+ },
26
+ async execute(args, exec) {
27
+ if (args.mode !== 'create' && args.mode !== 'edit')
28
+ throw new Error('Invalid configuration mode');
29
+ return JSON.parse(JSON.stringify(await service.track(service.configure(exec, () => resolveMarivoDatasourceBridge(source), {
30
+ mode: args.mode,
31
+ name: args.name,
32
+ reason: args.reason,
33
+ }))));
34
+ },
35
+ });
36
+ }
37
+ export function registerMarivoDatasourceConfigureTool(ctx, source, service) {
38
+ return registerMarivoTool(ctx, createMarivoDatasourceConfigureTool(source, service));
39
+ }
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ import { CredentialServiceError } from "./service.js";
3
+ /** Validate against this Workspace's live Runtime; never include config values in errors. */
4
+ export function withDatasourceDefaults(schema, configured) {
5
+ if (configured === undefined)
6
+ return schema;
7
+ const parsed = z.record(z.string(), z.record(z.string(), z.json())).safeParse(configured);
8
+ if (!parsed.success)
9
+ throw new CredentialServiceError('datasource-defaults-invalid');
10
+ for (const [backend, values] of Object.entries(parsed.data)) {
11
+ const definition = schema.backends.find((item) => item.name === backend);
12
+ if (!definition)
13
+ throw new CredentialServiceError('datasource-defaults-backend-invalid');
14
+ for (const [name, value] of Object.entries(values)) {
15
+ const field = definition.fields.find((item) => item.name === name);
16
+ if (name.endsWith('_env'))
17
+ throw new CredentialServiceError('datasource-defaults-credential-forbidden');
18
+ if (!field)
19
+ throw new CredentialServiceError('datasource-defaults-field-invalid');
20
+ if (field.type !== 'json' && typeof value !== field.type)
21
+ throw new CredentialServiceError('datasource-defaults-type-invalid');
22
+ }
23
+ }
24
+ return { ...schema, creationDefaults: parsed.data };
25
+ }
@@ -1,4 +1,6 @@
1
- export { createMarivoDatasourceAccessTool, MARIVO_DATASOURCE_ACCESS_TOOL_NAME, registerMarivoDatasourceAccessTool, } from "./access.js";
2
1
  export { MarivoDatasourceBridge, resolveMarivoDatasourceBridge, resolveMarivoDatasourceInventoryBridge, } from "./bridge.js";
3
- export { assertMarivoCredentialReferences, DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES, DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS, MARIVO_CREDENTIAL_LEASE_PREFIX, MARIVO_CREDENTIAL_STORAGE_PREFIX, MarivoShellCredentialLeases, marivoCredentialStorageRef, } from "./shell-env.js";
2
+ export { createMarivoDatasourceConfigureTool, MARIVO_DATASOURCE_CONFIGURE_TOOL_NAME, registerMarivoDatasourceConfigureTool, } from "./configure.js";
3
+ export { MarivoPythonExecutionError, registerMarivoPythonTool } from "./python.js";
4
+ export { MarivoCredentialService } from "./service.js";
5
+ export { assertMarivoCredentialReferences, MARIVO_CREDENTIAL_STORAGE_PREFIX, marivoCredentialStorageRef, } from "./shell-env.js";
4
6
  export { createMarivoDatasourceTestTool, MARIVO_DATASOURCE_TEST_TOOL_NAME, registerMarivoDatasourceTestTool, } from "./test.js";
@@ -0,0 +1,21 @@
1
+ export const DEFAULT_PYTHON_TIMEOUT_MS = 120_000;
2
+ export const DEFAULT_PYTHON_MAX_TIMEOUT_MS = 600_000;
3
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
4
+ /** Do not echo rejected inputs: tool arguments and configuration are not trusted diagnostics. */
5
+ export function pythonTimeout(value, name) {
6
+ if (typeof value !== 'number' ||
7
+ !Number.isInteger(value) ||
8
+ value <= 0 ||
9
+ value > MAX_TIMER_DELAY_MS)
10
+ throw new Error(`${name} must be a positive integer no greater than ${MAX_TIMER_DELAY_MS}`);
11
+ return value;
12
+ }
13
+ export function resolvePythonOptions(options) {
14
+ const pythonTimeoutMs = pythonTimeout(options.pythonTimeoutMs === undefined ? DEFAULT_PYTHON_TIMEOUT_MS : options.pythonTimeoutMs, 'pythonTimeoutMs');
15
+ const pythonMaxTimeoutMs = pythonTimeout(options.pythonMaxTimeoutMs === undefined
16
+ ? DEFAULT_PYTHON_MAX_TIMEOUT_MS
17
+ : options.pythonMaxTimeoutMs, 'pythonMaxTimeoutMs');
18
+ if (pythonTimeoutMs > pythonMaxTimeoutMs)
19
+ throw new Error('pythonTimeoutMs must not exceed pythonMaxTimeoutMs');
20
+ return { pythonTimeoutMs, pythonMaxTimeoutMs };
21
+ }