@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.
- package/README.md +139 -53
- package/cordis.patch.yml +1 -1
- package/lib/bin/environment.js +9 -7
- package/lib/bin/presentation-lint.js +21 -0
- package/lib/bridges.js +2 -2
- package/lib/client/credentials/changes.js +74 -0
- package/lib/client/credentials/create-datasource.js +171 -0
- package/lib/client/credentials/credential-draft.js +42 -0
- package/lib/client/credentials/defaults.js +11 -0
- package/lib/client/credentials/descriptions.js +46 -0
- package/lib/client/credentials/install.js +319 -0
- package/lib/client/credentials/model.js +766 -0
- package/lib/client/credentials/report-publishing.js +96 -0
- package/lib/client/credentials/styles.js +21 -0
- package/lib/client/i18n/context.js +22 -0
- package/lib/client/i18n/copy.js +57 -0
- package/lib/client/i18n/credentials.js +475 -0
- package/lib/client/i18n/host.js +27 -0
- package/lib/client/i18n/navigation.js +41 -0
- package/lib/client/i18n/presentation.js +577 -0
- package/lib/client/i18n/python.js +36 -0
- package/lib/client/i18n/semantic.js +441 -0
- package/lib/client/input-context.js +21 -0
- package/lib/client/python-tool/card.js +35 -0
- package/lib/client/python-tool/execution-error.js +35 -0
- package/lib/client/python-tool/install.js +9 -0
- package/lib/client/python-tool/model.js +101 -0
- package/lib/client/right-tabs/install.js +535 -0
- package/lib/client/right-tabs/live-delivery.js +58 -0
- package/lib/client/right-tabs/navigation.js +47 -0
- package/lib/client/right-tabs/page.js +178 -0
- package/lib/client/right-tabs/styles.js +11 -0
- package/lib/client/rpc.js +5 -0
- package/lib/client/semantic-reference.js +10 -0
- package/lib/client/workspace-header-action.js +24 -0
- package/lib/client/workspace-shortcuts.js +26 -0
- package/lib/client.js +1250 -730
- package/lib/compatibility.js +2 -2
- package/lib/datasource/authoring.js +109 -0
- package/lib/datasource/bridge-programs.js +42 -27
- package/lib/datasource/bridge.js +144 -9
- package/lib/datasource/changes-contract.js +41 -0
- package/lib/datasource/changes.js +49 -0
- package/lib/datasource/configure.js +39 -0
- package/lib/datasource/defaults.js +25 -0
- package/lib/datasource/index.js +4 -2
- package/lib/datasource/python-options.js +21 -0
- package/lib/datasource/python.js +206 -0
- package/lib/datasource/resolver-program.js +112 -0
- package/lib/datasource/rpc.js +150 -0
- package/lib/datasource/service.js +962 -0
- package/lib/datasource/shell-env.js +2 -333
- package/lib/datasource/test.js +25 -51
- package/lib/disclosure/activation.js +19 -7
- package/lib/disclosure/execution-guidance.js +52 -0
- package/lib/disclosure/help.js +4 -1
- package/lib/environment/binding.js +54 -9
- package/lib/environment/runtime.js +120 -108
- package/lib/environment/subprocess.js +6 -1
- package/lib/environment/summary.js +3 -3
- package/lib/environment/workspace.js +23 -1
- package/lib/index.js +1 -1
- package/lib/lifecycle.js +33 -0
- package/lib/plugin-agents.js +154 -0
- package/lib/plugin.js +172 -133
- package/lib/presentation/asset.js +34 -0
- package/lib/presentation/assets/portable.js +606 -0
- package/lib/presentation/assets/static.js +1216 -0
- package/lib/presentation/build/index.js +72 -0
- package/lib/presentation/commit.js +163 -0
- package/lib/presentation/contracts/asset-path.js +9 -0
- package/lib/presentation/contracts/catalog.js +62 -0
- package/lib/presentation/contracts/charts.js +453 -0
- package/lib/presentation/contracts/editing.js +58 -0
- package/lib/presentation/contracts/index.js +663 -0
- package/lib/presentation/contracts/interaction.js +62 -0
- package/lib/presentation/contracts/types.js +15 -0
- package/lib/presentation/contracts/values.js +28 -0
- package/lib/presentation/delivery.js +71 -0
- package/lib/presentation/export-html.js +214 -0
- package/lib/presentation/files.js +82 -0
- package/lib/presentation/index.js +6 -0
- package/lib/presentation/lint.js +46 -0
- package/lib/presentation/projection/files.js +85 -0
- package/lib/presentation/projection/index.js +196 -0
- package/lib/presentation/projection/program.js +137 -0
- package/lib/presentation/projection/sql-code.js +79 -0
- package/lib/presentation/receipt.js +34 -0
- package/lib/presentation/reports.js +219 -0
- package/lib/presentation/rpc.js +186 -0
- package/lib/presentation/tool.js +118 -0
- package/lib/python-execution-contracts.js +1 -0
- package/lib/python-execution.js +232 -0
- package/lib/report-publishing/adapters.js +111 -0
- package/lib/report-publishing/config.js +69 -0
- package/lib/report-publishing/service.js +180 -0
- package/lib/rpc.js +49 -0
- package/lib/semantic-browser/contracts.js +97 -0
- package/lib/semantic-browser/definition.js +64 -0
- package/lib/semantic-browser/program.js +129 -0
- package/lib/semantic-browser/service.js +106 -0
- package/lib/semantic-reference/bridge.js +127 -0
- package/lib/semantic-reference/contracts.js +127 -0
- package/lib/semantic-reference/environment.js +56 -0
- package/lib/semantic-reference/index.js +3 -0
- package/lib/semantic-reference/labels.js +16 -0
- package/lib/semantic-reference/rpc.js +143 -0
- package/lib/semantic-reference/search.js +95 -0
- package/lib/semantic-reference/usage.js +152 -0
- package/lib/tool-lifecycle.js +22 -0
- package/lib/types/bridges.d.ts +2 -2
- package/lib/types/client/credentials/changes.d.ts +9 -0
- package/lib/types/client/credentials/create-datasource.d.ts +7 -0
- package/lib/types/client/credentials/credential-draft.d.ts +14 -0
- package/lib/types/client/credentials/defaults.d.ts +2 -0
- package/lib/types/client/credentials/descriptions.d.ts +1 -0
- package/lib/types/client/credentials/install.d.ts +19 -0
- package/lib/types/client/credentials/model.d.ts +79 -0
- package/lib/types/client/credentials/report-publishing.d.ts +5 -0
- package/lib/types/client/credentials/styles.d.ts +1 -0
- package/lib/types/client/i18n/context.d.ts +17 -0
- package/lib/types/client/i18n/copy.d.ts +806 -0
- package/lib/types/client/i18n/credentials.d.ts +475 -0
- package/lib/types/client/i18n/host.d.ts +16 -0
- package/lib/types/client/i18n/navigation.d.ts +41 -0
- package/lib/types/client/i18n/presentation.d.ts +577 -0
- package/lib/types/client/i18n/python.d.ts +19 -0
- package/lib/types/client/i18n/semantic.d.ts +441 -0
- package/lib/types/client/input-context.d.ts +19 -0
- package/lib/types/client/presentation/ask-dsh.d.ts +5 -0
- package/lib/types/client/presentation/catalog-model.d.ts +24 -0
- package/lib/types/client/presentation/catalog.d.ts +20 -0
- package/lib/types/client/presentation/chart-explorer.d.ts +12 -0
- package/lib/types/client/presentation/chart-geometry.d.ts +30 -0
- package/lib/types/client/presentation/chart-renderer.d.ts +10 -0
- package/lib/types/client/presentation/chart-specials.d.ts +10 -0
- package/lib/types/client/presentation/chart-styles.d.ts +1 -0
- package/lib/types/client/presentation/chart-tooltip.d.ts +13 -0
- package/lib/types/client/presentation/chart-view.d.ts +23 -0
- package/lib/types/client/presentation/context-reference.d.ts +12 -0
- package/lib/types/client/presentation/copy-context.d.ts +5 -0
- package/lib/types/client/presentation/delivery-model.d.ts +75 -0
- package/lib/types/client/presentation/delivery.d.ts +78 -0
- package/lib/types/client/presentation/download.d.ts +2 -0
- package/lib/types/client/presentation/editor-controls.d.ts +12 -0
- package/lib/types/client/presentation/export-menu.d.ts +26 -0
- package/lib/types/client/presentation/export-view.d.ts +63 -0
- package/lib/types/client/presentation/global-filters.d.ts +6 -0
- package/lib/types/client/presentation/host-entry.d.ts +18 -0
- package/lib/types/client/presentation/icons.d.ts +4 -0
- package/lib/types/client/presentation/markdown.d.ts +5 -0
- package/lib/types/client/presentation/model.d.ts +61 -0
- package/lib/types/client/presentation/reader.d.ts +24 -0
- package/lib/types/client/presentation/reference-source.d.ts +9 -0
- package/lib/types/client/presentation/source-code-model.d.ts +17 -0
- package/lib/types/client/presentation/source-code.d.ts +6 -0
- package/lib/types/client/presentation/source-dialog.d.ts +12 -0
- package/lib/types/client/presentation/source-facts.d.ts +20 -0
- package/lib/types/client/presentation/source-format.d.ts +5 -0
- package/lib/types/client/presentation/source-highlight.d.ts +7 -0
- package/lib/types/client/presentation/sources.d.ts +13 -0
- package/lib/types/client/presentation/styles.d.ts +1 -0
- package/lib/types/client/presentation/table.d.ts +16 -0
- package/lib/types/client/presentation/use-element-width.d.ts +5 -0
- package/lib/types/client/python-tool/card.d.ts +2 -0
- package/lib/types/client/python-tool/execution-error.d.ts +3 -0
- package/lib/types/client/python-tool/install.d.ts +2 -0
- package/lib/types/client/python-tool/model.d.ts +20 -0
- package/lib/types/client/right-tabs/install.d.ts +18 -0
- package/lib/types/client/right-tabs/live-delivery.d.ts +8 -0
- package/lib/types/client/right-tabs/navigation.d.ts +17 -0
- package/lib/types/client/right-tabs/page.d.ts +36 -0
- package/lib/types/client/right-tabs/styles.d.ts +2 -0
- package/lib/types/client/rpc.d.ts +3 -0
- package/lib/types/client/semantic-reference.d.ts +4 -0
- package/lib/types/client/workspace-header-action.d.ts +7 -0
- package/lib/types/client/workspace-shortcuts.d.ts +15 -0
- package/lib/types/client.d.ts +9 -124
- package/lib/types/compatibility.d.ts +3 -3
- package/lib/types/datasource/authoring.d.ts +35 -0
- package/lib/types/datasource/bridge.d.ts +43 -4
- package/lib/types/datasource/changes-contract.d.ts +18 -0
- package/lib/types/datasource/changes.d.ts +15 -0
- package/lib/types/datasource/configure.d.ts +7 -0
- package/lib/types/datasource/defaults.d.ts +7 -0
- package/lib/types/datasource/index.d.ts +8 -5
- package/lib/types/datasource/python-options.d.ts +9 -0
- package/lib/types/datasource/python.d.ts +19 -0
- package/lib/types/datasource/resolver-program.d.ts +6 -0
- package/lib/types/datasource/rpc.d.ts +5 -0
- package/lib/types/datasource/service.d.ts +155 -0
- package/lib/types/datasource/shell-env.d.ts +0 -29
- package/lib/types/datasource/test.d.ts +6 -29
- package/lib/types/disclosure/activation.d.ts +2 -1
- package/lib/types/disclosure/execution-guidance.d.ts +6 -0
- package/lib/types/disclosure/help.d.ts +1 -1
- package/lib/types/environment/binding.d.ts +5 -1
- package/lib/types/environment/errors.d.ts +1 -1
- package/lib/types/environment/runtime.d.ts +5 -5
- package/lib/types/environment/types.d.ts +15 -3
- package/lib/types/environment/workspace.d.ts +1 -0
- package/lib/types/index.d.ts +1 -1
- package/lib/types/lifecycle.d.ts +8 -0
- package/lib/types/plugin-agents.d.ts +27 -0
- package/lib/types/plugin.d.ts +14 -24
- package/lib/types/presentation/asset.d.ts +6 -0
- package/lib/types/presentation/build/index.d.ts +16 -0
- package/lib/types/presentation/commit.d.ts +4 -0
- package/lib/types/presentation/contracts/asset-path.d.ts +2 -0
- package/lib/types/presentation/contracts/catalog.d.ts +27 -0
- package/lib/types/presentation/contracts/charts.d.ts +11 -0
- package/lib/types/presentation/contracts/editing.d.ts +8 -0
- package/lib/types/presentation/contracts/index.d.ts +11 -0
- package/lib/types/presentation/contracts/interaction.d.ts +8 -0
- package/lib/types/presentation/contracts/types.d.ts +255 -0
- package/lib/types/presentation/contracts/values.d.ts +9 -0
- package/lib/types/presentation/delivery.d.ts +3 -0
- package/lib/types/presentation/export-html.d.ts +13 -0
- package/lib/types/presentation/files.d.ts +7 -0
- package/lib/types/presentation/index.d.ts +6 -0
- package/lib/types/presentation/lint.d.ts +13 -0
- package/lib/types/presentation/projection/files.d.ts +2 -0
- package/lib/types/presentation/projection/index.d.ts +20 -0
- package/lib/types/presentation/projection/program.d.ts +2 -0
- package/lib/types/presentation/projection/sql-code.d.ts +2 -0
- package/lib/types/presentation/receipt.d.ts +14 -0
- package/lib/types/presentation/reports.d.ts +10 -0
- package/lib/types/presentation/rpc.d.ts +25 -0
- package/lib/types/presentation/tool.d.ts +12 -0
- package/lib/types/python-execution-contracts.d.ts +10 -0
- package/lib/types/python-execution.d.ts +10 -0
- package/lib/types/report-publishing/adapters.d.ts +6 -0
- package/lib/types/report-publishing/config.d.ts +22 -0
- package/lib/types/report-publishing/service.d.ts +68 -0
- package/lib/types/rpc.d.ts +3 -0
- package/lib/types/semantic-browser/contracts.d.ts +47 -0
- package/lib/types/semantic-browser/definition.d.ts +20 -0
- package/lib/types/semantic-browser/program.d.ts +2 -0
- package/lib/types/semantic-browser/service.d.ts +26 -0
- package/lib/types/semantic-reference/bridge.d.ts +12 -0
- package/lib/types/semantic-reference/contracts.d.ts +54 -0
- package/lib/types/semantic-reference/environment.d.ts +22 -0
- package/lib/types/semantic-reference/index.d.ts +3 -0
- package/lib/types/semantic-reference/labels.d.ts +16 -0
- package/lib/types/semantic-reference/rpc.d.ts +15 -0
- package/lib/types/semantic-reference/search.d.ts +10 -0
- package/lib/types/semantic-reference/usage.d.ts +43 -0
- package/lib/types/tool-lifecycle.d.ts +4 -0
- package/lib/types/workspace-identity.d.ts +6 -0
- package/lib/workspace-identity.js +9 -0
- package/package.json +99 -53
- package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
- package/skills/dsh-data-analysis-files/SKILL.md +32 -0
- package/skills/dsh-data-analysis-files/references/examples.md +93 -0
- package/skills/dsh-data-analysis-presentation/SKILL.md +28 -0
- package/skills/dsh-data-analysis-presentation/references/chart-examples.md +18 -0
- package/skills/dsh-data-analysis-presentation/references/charts.md +55 -0
- package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +36 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-bins.dataset.json +33 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-boxes.dataset.json +45 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-composition.dataset.json +33 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-funnel.dataset.json +27 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-precision.dataset.json +21 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-series.dataset.json +58 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-shares.dataset.json +33 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts-waterfall.dataset.json +44 -0
- package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +393 -0
- package/skills/dsh-data-analysis-presentation/references/examples/comparison-selected.dataset.json +24 -0
- package/skills/dsh-data-analysis-presentation/references/examples/comparison-summary.dataset.json +22 -0
- package/skills/dsh-data-analysis-presentation/references/examples/comparison.dataset.json +18 -0
- package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +80 -0
- package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +42 -0
- package/skills/dsh-data-analysis-presentation/references/examples/computed.dataset.json +12 -0
- package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +58 -0
- package/skills/dsh-data-analysis-presentation/references/examples/interaction-detail.dataset.json +30 -0
- package/skills/dsh-data-analysis-presentation/references/examples/interaction-summary.dataset.json +22 -0
- package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +279 -0
- package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +27 -0
- package/skills/dsh-data-analysis-presentation/references/examples/write-charts.py +73 -0
- package/skills/dsh-data-analysis-presentation/references/examples/write-comparison.py +42 -0
- package/skills/dsh-data-analysis-presentation/references/examples/write-computed.py +21 -0
- package/skills/dsh-data-analysis-presentation/references/examples/write-interaction.py +33 -0
- package/skills/dsh-data-analysis-presentation/references/examples.md +44 -0
- package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
- package/skills/dsh-data-analysis-presentation/references/interaction.md +56 -0
- package/skills/dsh-data-analysis-presentation/references/narrative.md +37 -0
- package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
- package/skills/dsh-data-analysis-presentation/references/schema.md +162 -0
- package/lib/datasource/access.js +0 -60
- package/lib/datasource/credentials.js +0 -24
- package/lib/evidence/bridge-program.js +0 -153
- package/lib/evidence/bridge.js +0 -166
- package/lib/evidence/index.js +0 -2
- package/lib/evidence/sources.js +0 -263
- package/lib/types/datasource/access.d.ts +0 -27
- package/lib/types/evidence/bridge.d.ts +0 -40
- package/lib/types/evidence/index.d.ts +0 -4
- package/lib/types/evidence/sources.d.ts +0 -57
- package/python/report-kit/dist/dsh_data_analysis_report_kit-3.0.0-py3-none-any.whl +0 -0
- package/skills/dsh-data-analysis-report/SKILL.md +0 -57
- package/skills/dsh-data-analysis-report/assets/marivo-artifact.js +0 -113
- package/skills/dsh-data-analysis-report/assets/marivo-session-dag.js +0 -1201
- package/skills/dsh-data-analysis-report/assets/report-data.js +0 -449
package/lib/compatibility.js
CHANGED
|
@@ -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/
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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()}`;
|
package/lib/datasource/bridge.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
+
}
|
package/lib/datasource/index.js
CHANGED
|
@@ -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 {
|
|
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
|
+
}
|