@chengxianglibra/dsh-data-analysis 0.1.2 → 0.2.1
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.en.md +135 -0
- package/README.md +105 -98
- package/cordis.patch.yml +1 -1
- package/lib/bin/environment.js +8 -6
- package/lib/client/credentials/changes.js +74 -0
- package/lib/client/credentials/create-datasource.js +126 -22
- 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 +209 -81
- package/lib/client/credentials/model.js +323 -51
- package/lib/client/credentials/report-publishing.js +96 -0
- package/lib/client/credentials/styles.js +13 -9
- package/lib/client/i18n/context.js +22 -0
- package/lib/client/i18n/copy.js +60 -0
- package/lib/client/i18n/credentials.js +475 -0
- package/lib/client/i18n/host.js +28 -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/i18n/settings.js +30 -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/settings/card.js +90 -0
- package/lib/client/settings/install.js +10 -0
- package/lib/client/workspace-header-action.js +14 -1
- package/lib/client/workspace-shortcuts.js +26 -0
- package/lib/client.js +388 -242
- package/lib/compatibility.js +1 -1
- package/lib/datasource/authoring.js +43 -4
- package/lib/datasource/bridge-programs.js +23 -20
- package/lib/datasource/bridge.js +46 -3
- 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 +2 -1
- package/lib/datasource/python-options.js +15 -0
- package/lib/datasource/python.js +119 -25
- package/lib/datasource/rpc.js +75 -13
- package/lib/datasource/service.js +286 -11
- package/lib/datasource/test.js +3 -2
- package/lib/disclosure/activation.js +19 -7
- package/lib/disclosure/execution-guidance.js +49 -0
- package/lib/disclosure/help.js +4 -1
- package/lib/environment/runtime.js +35 -34
- package/lib/environment/workspace.js +18 -1
- package/lib/lifecycle.js +33 -0
- package/lib/plugin-agents.js +154 -0
- package/lib/plugin.js +98 -172
- package/lib/presentation/asset.js +34 -0
- package/lib/presentation/assets/portable.js +14 -14
- package/lib/presentation/assets/static.js +344 -73
- package/lib/presentation/build/index.js +3 -2
- package/lib/presentation/commit.js +19 -12
- package/lib/presentation/contracts/asset-path.js +9 -0
- package/lib/presentation/contracts/catalog.js +62 -0
- package/lib/presentation/contracts/charts.js +0 -23
- package/lib/presentation/contracts/index.js +17 -5
- package/lib/presentation/contracts/types.js +1 -1
- package/lib/presentation/delivery.js +45 -30
- package/lib/presentation/export-html.js +214 -0
- package/lib/presentation/files.js +4 -5
- package/lib/presentation/projection/index.js +2 -1
- package/lib/presentation/projection/program.js +2 -9
- package/lib/presentation/projection/sql-code.js +13 -13
- package/lib/presentation/reports.js +114 -17
- package/lib/presentation/rpc.js +96 -58
- package/lib/presentation/tool.js +6 -10
- 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 +17 -0
- package/lib/semantic-browser/program.js +10 -0
- package/lib/semantic-browser/service.js +40 -6
- package/lib/semantic-reference/bridge.js +10 -1
- package/lib/semantic-reference/contracts.js +20 -6
- package/lib/semantic-reference/environment.js +56 -0
- package/lib/semantic-reference/labels.js +16 -0
- package/lib/semantic-reference/rpc.js +39 -18
- package/lib/semantic-reference/search.js +5 -4
- package/lib/semantic-reference/usage.js +12 -3
- package/lib/settings.js +21 -0
- package/lib/tool-lifecycle.js +22 -0
- package/lib/types/client/credentials/changes.d.ts +9 -0
- package/lib/types/client/credentials/create-datasource.d.ts +3 -1
- 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 -1
- package/lib/types/client/credentials/model.d.ts +31 -11
- package/lib/types/client/credentials/report-publishing.d.ts +5 -0
- package/lib/types/client/credentials/styles.d.ts +1 -1
- package/lib/types/client/i18n/context.d.ts +17 -0
- package/lib/types/client/i18n/copy.d.ts +821 -0
- package/lib/types/client/i18n/credentials.d.ts +475 -0
- package/lib/types/client/i18n/host.d.ts +17 -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/i18n/settings.d.ts +16 -0
- package/lib/types/client/input-context.d.ts +19 -0
- package/lib/types/client/presentation/ask-dsh.d.ts +4 -8
- 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-tooltip.d.ts +2 -2
- package/lib/types/client/presentation/context-reference.d.ts +12 -0
- package/lib/types/client/presentation/copy-context.d.ts +2 -2
- package/lib/types/client/presentation/delivery-model.d.ts +27 -6
- package/lib/types/client/presentation/delivery.d.ts +8 -7
- package/lib/types/client/presentation/download.d.ts +2 -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/host-entry.d.ts +8 -2
- package/lib/types/client/presentation/icons.d.ts +1 -1
- package/lib/types/client/presentation/model.d.ts +14 -15
- package/lib/types/client/presentation/reader.d.ts +15 -2
- package/lib/types/client/presentation/reference-source.d.ts +9 -0
- package/lib/types/client/presentation/source-code-model.d.ts +2 -1
- package/lib/types/client/presentation/source-code.d.ts +0 -5
- package/lib/types/client/presentation/source-dialog.d.ts +1 -2
- package/lib/types/client/presentation/sources.d.ts +5 -5
- package/lib/types/client/presentation/styles.d.ts +1 -1
- package/lib/types/client/presentation/table.d.ts +5 -1
- 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/settings/card.d.ts +9 -0
- package/lib/types/client/settings/install.d.ts +2 -0
- package/lib/types/client/workspace-header-action.d.ts +1 -1
- package/lib/types/client/workspace-shortcuts.d.ts +15 -0
- package/lib/types/client.d.ts +5 -2
- package/lib/types/compatibility.d.ts +1 -1
- package/lib/types/datasource/authoring.d.ts +10 -0
- package/lib/types/datasource/bridge.d.ts +23 -2
- 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 +6 -1
- package/lib/types/datasource/python-options.d.ts +8 -0
- package/lib/types/datasource/python.d.ts +17 -2
- package/lib/types/datasource/rpc.d.ts +2 -1
- package/lib/types/datasource/service.d.ts +48 -5
- package/lib/types/datasource/test.d.ts +1 -1
- 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/types.d.ts +2 -1
- package/lib/types/environment/workspace.d.ts +1 -0
- package/lib/types/lifecycle.d.ts +8 -0
- package/lib/types/plugin-agents.d.ts +29 -0
- package/lib/types/plugin.d.ts +12 -28
- package/lib/types/presentation/asset.d.ts +6 -0
- package/lib/types/presentation/build/index.d.ts +7 -2
- package/lib/types/presentation/commit.d.ts +1 -1
- 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 +0 -2
- package/lib/types/presentation/contracts/types.d.ts +7 -4
- package/lib/types/presentation/delivery.d.ts +1 -1
- package/lib/types/presentation/export-html.d.ts +13 -0
- package/lib/types/presentation/reports.d.ts +6 -1
- package/lib/types/presentation/rpc.d.ts +3 -2
- package/lib/types/presentation/tool.d.ts +2 -2
- 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 +7 -0
- package/lib/types/semantic-browser/service.d.ts +7 -1
- package/lib/types/semantic-reference/bridge.d.ts +1 -0
- package/lib/types/semantic-reference/contracts.d.ts +5 -2
- package/lib/types/semantic-reference/environment.d.ts +22 -0
- package/lib/types/semantic-reference/labels.d.ts +16 -0
- package/lib/types/semantic-reference/rpc.d.ts +1 -1
- package/lib/types/semantic-reference/search.d.ts +1 -1
- package/lib/types/settings.d.ts +14 -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 +93 -61
- 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 +44 -0
- package/skills/dsh-data-analysis-files/references/delivery.md +15 -0
- package/skills/dsh-data-analysis-files/references/examples.md +93 -0
- package/skills/dsh-data-analysis-presentation/SKILL.md +15 -15
- package/skills/dsh-data-analysis-presentation/references/examples/artifact.draft.json +13 -4
- package/skills/dsh-data-analysis-presentation/references/examples/charts.draft.json +3 -2
- package/skills/dsh-data-analysis-presentation/references/examples/comparison.draft.json +3 -2
- package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +12 -5
- package/skills/dsh-data-analysis-presentation/references/examples/computed.draft.json +3 -2
- package/skills/dsh-data-analysis-presentation/references/examples/interaction.draft.json +3 -2
- package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +12 -4
- package/skills/dsh-data-analysis-presentation/references/examples.md +3 -1
- package/skills/dsh-data-analysis-presentation/references/export-html.md +7 -0
- package/skills/dsh-data-analysis-presentation/references/interaction.md +1 -1
- package/skills/dsh-data-analysis-presentation/references/narrative.md +1 -1
- package/skills/dsh-data-analysis-presentation/references/publishing.md +3 -0
- package/skills/dsh-data-analysis-presentation/references/schema.md +29 -2
- package/lib/types/client/presentation/install.d.ts +0 -15
package/lib/datasource/rpc.js
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { registerPluginRpc } from "../rpc.js";
|
|
3
|
+
import { withDatasourceDefaults } from "./defaults.js";
|
|
2
4
|
import { CREDENTIAL_CHANNEL, credentialError } from "./service.js";
|
|
3
5
|
const text = z.string().min(1).max(256);
|
|
4
6
|
const handle = { generation: z.string().uuid(), id: z.string().uuid(), scope: z.string().uuid() };
|
|
5
7
|
const mutation = z
|
|
6
8
|
.object({
|
|
7
9
|
...handle,
|
|
8
|
-
action: z.enum(['save', 'update', 'delete', 'test', 'submit', 'diagnose']),
|
|
10
|
+
action: z.enum(['save', 'update', 'delete', 'delete-datasource', 'test', 'submit', 'diagnose']),
|
|
11
|
+
deleteCredentials: z.boolean().optional(),
|
|
9
12
|
version: z.string().max(65536),
|
|
10
13
|
requestId: z.string().uuid().optional(),
|
|
11
14
|
reference: text.optional(),
|
|
12
15
|
changes: z.record(text, z.string().min(1).max(65536)).optional(),
|
|
13
16
|
})
|
|
14
17
|
.strict();
|
|
15
|
-
export function registerCredentialRpc(connection, service, workspace) {
|
|
16
|
-
const unregister = connection
|
|
18
|
+
export function registerCredentialRpc(connection, service, workspace, datasourceDefaults) {
|
|
19
|
+
const unregister = registerPluginRpc(connection, CREDENTIAL_CHANNEL, [
|
|
20
|
+
'overview',
|
|
21
|
+
'authoring',
|
|
22
|
+
'create-datasource',
|
|
23
|
+
'configuration',
|
|
24
|
+
'update-datasource',
|
|
25
|
+
'select-configuration',
|
|
26
|
+
'watch',
|
|
27
|
+
'start',
|
|
28
|
+
'operation',
|
|
29
|
+
'cancel-operation',
|
|
30
|
+
'cancel-request',
|
|
31
|
+
], async (endpoint, payload, signal) => {
|
|
17
32
|
try {
|
|
18
33
|
if (Buffer.byteLength(JSON.stringify(payload) ?? '') > 1_048_576)
|
|
19
34
|
throw new Error('invalid-request');
|
|
@@ -26,24 +41,74 @@ export function registerCredentialRpc(connection, service, workspace) {
|
|
|
26
41
|
};
|
|
27
42
|
}
|
|
28
43
|
else if (endpoint === 'authoring') {
|
|
29
|
-
const input = z
|
|
44
|
+
const input = z
|
|
45
|
+
.object({ workspaceId: text, mode: z.enum(['create', 'edit']).default('create') })
|
|
46
|
+
.strict()
|
|
47
|
+
.parse(payload);
|
|
30
48
|
const bridge = await workspace(input.workspaceId);
|
|
31
49
|
if (!bridge.authoring)
|
|
32
50
|
throw new Error('authoring-unavailable');
|
|
33
|
-
|
|
51
|
+
const schema = await bridge.authoring(signal);
|
|
52
|
+
value = {
|
|
53
|
+
generation: service.generation,
|
|
54
|
+
...(input.mode === 'edit'
|
|
55
|
+
? schema
|
|
56
|
+
: withDatasourceDefaults(schema, datasourceDefaults)),
|
|
57
|
+
};
|
|
34
58
|
}
|
|
35
|
-
else if (endpoint === '
|
|
59
|
+
else if (endpoint === 'configuration') {
|
|
60
|
+
const input = z.object({ workspaceId: text, name: text }).strict().parse(payload);
|
|
61
|
+
const bridge = await workspace(input.workspaceId);
|
|
62
|
+
if (!bridge.configuration)
|
|
63
|
+
throw new Error('authoring-unavailable');
|
|
64
|
+
value = await bridge.configuration(input.name, signal);
|
|
65
|
+
}
|
|
66
|
+
else if (endpoint === 'select-configuration') {
|
|
67
|
+
const input = z
|
|
68
|
+
.object({ workspaceId: text, requestId: z.string().uuid(), name: text })
|
|
69
|
+
.strict()
|
|
70
|
+
.parse(payload);
|
|
71
|
+
value = await service.selectConfiguration(input.requestId, input.workspaceId, input.name, () => workspace(input.workspaceId), signal);
|
|
72
|
+
}
|
|
73
|
+
else if (endpoint === 'create-datasource' || endpoint === 'update-datasource') {
|
|
36
74
|
const input = z
|
|
37
75
|
.object({
|
|
38
76
|
workspaceId: text,
|
|
39
77
|
generation: z.string().uuid(),
|
|
40
78
|
fingerprint: z.string().min(1).max(256),
|
|
41
79
|
backend: text,
|
|
80
|
+
name: text.optional(),
|
|
81
|
+
version: text.optional(),
|
|
82
|
+
requestId: z.string().uuid().optional(),
|
|
42
83
|
fields: z.record(text, z.unknown()),
|
|
43
84
|
})
|
|
44
85
|
.strict()
|
|
45
86
|
.parse(payload);
|
|
46
|
-
|
|
87
|
+
const request = input.requestId
|
|
88
|
+
? await service.configurationRequest(input.requestId, input.workspaceId, () => workspace(input.workspaceId), signal)
|
|
89
|
+
: undefined;
|
|
90
|
+
const operationSignal = request ? AbortSignal.any([signal, request.signal]) : signal;
|
|
91
|
+
const resolve = async () => {
|
|
92
|
+
if (input.requestId)
|
|
93
|
+
await service.configurationRequest(input.requestId, input.workspaceId, () => workspace(input.workspaceId), operationSignal);
|
|
94
|
+
return workspace(input.workspaceId);
|
|
95
|
+
};
|
|
96
|
+
if (endpoint === 'update-datasource') {
|
|
97
|
+
const { name, version } = z.object({ name: text, version: text }).parse(input);
|
|
98
|
+
if (request?.view.configuration.mode === 'edit' &&
|
|
99
|
+
request.view.configuration.name !== name)
|
|
100
|
+
throw new Error('invalid-request');
|
|
101
|
+
value = await service.updateDatasource(input.generation, input.fingerprint, { backend: input.backend, fields: input.fields, name, version }, resolve, operationSignal);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
if (input.name || input.version || request?.view.configuration.mode === 'edit')
|
|
105
|
+
throw new Error('invalid-request');
|
|
106
|
+
value = await service.createDatasource(input.generation, input.fingerprint, { backend: input.backend, fields: input.fields }, resolve, operationSignal);
|
|
107
|
+
}
|
|
108
|
+
if (input.requestId) {
|
|
109
|
+
const configured = await service.selectConfiguration(input.requestId, input.workspaceId, value.name, () => workspace(input.workspaceId), operationSignal);
|
|
110
|
+
value = { ...value, request: configured };
|
|
111
|
+
}
|
|
47
112
|
}
|
|
48
113
|
else if (endpoint === 'watch') {
|
|
49
114
|
const input = z
|
|
@@ -79,10 +144,7 @@ export function registerCredentialRpc(connection, service, workspace) {
|
|
|
79
144
|
error: { code: 'internal', message: credentialError(error), details: {} },
|
|
80
145
|
};
|
|
81
146
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await service.close();
|
|
86
|
-
await draining;
|
|
87
|
-
};
|
|
147
|
+
});
|
|
148
|
+
// The profile owns this shared service; withdrawing its Web entry must not close it.
|
|
149
|
+
return unregister;
|
|
88
150
|
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { credentialRef } from '@deepseek-ai/dsh-credentials';
|
|
3
|
+
import { finishCleanup } from "../lifecycle.js";
|
|
3
4
|
import { marivoCredentialStorageRef } from "./shell-env.js";
|
|
4
5
|
export const CREDENTIAL_CHANNEL = '/dsh-data-analysis-credentials';
|
|
5
6
|
const RETENTION = 30 * 60_000;
|
|
6
7
|
const CAPACITY = 512;
|
|
8
|
+
function sessionIdentity(exec) {
|
|
9
|
+
const session = exec.agent.session;
|
|
10
|
+
return JSON.stringify([
|
|
11
|
+
session.id,
|
|
12
|
+
Reflect.get(session.header, 'workspaceId'),
|
|
13
|
+
session.header.cwd,
|
|
14
|
+
]);
|
|
15
|
+
}
|
|
7
16
|
export class CredentialServiceError extends Error {
|
|
8
17
|
code;
|
|
9
18
|
constructor(code) {
|
|
@@ -24,9 +33,12 @@ export class MarivoCredentialService {
|
|
|
24
33
|
generation = randomUUID();
|
|
25
34
|
#store;
|
|
26
35
|
#contexts = new Map();
|
|
36
|
+
#activeContexts = new Set();
|
|
37
|
+
#configurations = new Map();
|
|
27
38
|
#requests = new Map();
|
|
28
39
|
#operations = new Map();
|
|
29
40
|
#controllers = new Map();
|
|
41
|
+
#closing;
|
|
30
42
|
#tasks = new Set();
|
|
31
43
|
#versions = new Map();
|
|
32
44
|
#activeRefs = new Map();
|
|
@@ -68,6 +80,9 @@ export class MarivoCredentialService {
|
|
|
68
80
|
for (const [id, op] of this.#operations)
|
|
69
81
|
if (op.endedAt !== undefined && now - op.endedAt >= RETENTION)
|
|
70
82
|
this.#operations.delete(id);
|
|
83
|
+
for (const [id, request] of this.#configurations)
|
|
84
|
+
if (request.view.endedAt !== undefined && now - request.view.endedAt >= RETENTION)
|
|
85
|
+
this.#configurations.delete(id);
|
|
71
86
|
for (const [id, request] of this.#requests)
|
|
72
87
|
if (request.view.endedAt !== undefined && now - request.view.endedAt >= RETENTION)
|
|
73
88
|
this.#requests.delete(id);
|
|
@@ -128,11 +143,13 @@ export class MarivoCredentialService {
|
|
|
128
143
|
this.#changed();
|
|
129
144
|
}
|
|
130
145
|
async #current(context, signal) {
|
|
146
|
+
assert(!context.invalidated, 'context-changed');
|
|
131
147
|
signal.throwIfAborted();
|
|
132
148
|
const bridge = await context.resolve();
|
|
133
149
|
assert(bridge.binding.fingerprint === context.bridge.binding.fingerprint, 'context-changed');
|
|
134
150
|
const described = await bridge.describe(context.description.name, signal);
|
|
135
151
|
assert(described.definition === context.description.definition, 'context-changed');
|
|
152
|
+
assert(!context.invalidated, 'context-changed');
|
|
136
153
|
signal.throwIfAborted();
|
|
137
154
|
context.touched = this.now();
|
|
138
155
|
}
|
|
@@ -146,7 +163,12 @@ export class MarivoCredentialService {
|
|
|
146
163
|
throw new CredentialServiceError('credential-state-unavailable');
|
|
147
164
|
}
|
|
148
165
|
}
|
|
149
|
-
const
|
|
166
|
+
const historyKey = this.#historyKey(context);
|
|
167
|
+
const previous = [...this.#history.entries()].reverse().find(([key]) => {
|
|
168
|
+
const [fingerprint, name] = JSON.parse(key);
|
|
169
|
+
return fingerprint === context.bridge.binding.fingerprint && name === context.description.name;
|
|
170
|
+
});
|
|
171
|
+
const history = this.#history.get(historyKey) ?? previous?.[1];
|
|
150
172
|
return {
|
|
151
173
|
token: context.token,
|
|
152
174
|
workspaceId: context.workspaceId,
|
|
@@ -162,7 +184,7 @@ export class MarivoCredentialService {
|
|
|
162
184
|
lastTest: {
|
|
163
185
|
at: history.at,
|
|
164
186
|
result: history.result,
|
|
165
|
-
stale: history.version !== this.#version(context),
|
|
187
|
+
stale: !this.#history.has(historyKey) || history.version !== this.#version(context),
|
|
166
188
|
},
|
|
167
189
|
}
|
|
168
190
|
: {}),
|
|
@@ -207,7 +229,8 @@ export class MarivoCredentialService {
|
|
|
207
229
|
resolve,
|
|
208
230
|
touched: this.now(),
|
|
209
231
|
};
|
|
210
|
-
const old = [...this.#contexts.values()].find((c) => c.
|
|
232
|
+
const old = [...this.#contexts.values()].find((c) => !c.invalidated &&
|
|
233
|
+
c.workspaceId === workspaceId &&
|
|
211
234
|
c.description.name === context.description.name &&
|
|
212
235
|
c.description.definition === context.description.definition &&
|
|
213
236
|
c.bridge.binding.fingerprint === context.bridge.binding.fingerprint);
|
|
@@ -234,9 +257,147 @@ export class MarivoCredentialService {
|
|
|
234
257
|
signal.throwIfAborted();
|
|
235
258
|
const result = await bridge.create(input, signal);
|
|
236
259
|
assert(!result.error && result.name, result.error ?? 'datasource-definition-invalid');
|
|
260
|
+
this.#changed();
|
|
237
261
|
return { name: result.name };
|
|
238
262
|
}, signal);
|
|
239
263
|
}
|
|
264
|
+
async updateDatasource(generation, fingerprint, input, resolve, caller) {
|
|
265
|
+
const signal = AbortSignal.any([caller, this.#lifetime.signal]);
|
|
266
|
+
return this.#locked(async () => {
|
|
267
|
+
assert(generation === this.generation, 'context-changed');
|
|
268
|
+
const bridge = await resolve();
|
|
269
|
+
assert(bridge.binding.fingerprint === fingerprint, 'context-changed');
|
|
270
|
+
assert(bridge.update, 'datasource-authoring-unavailable');
|
|
271
|
+
assert(![...this.#operations.values()].some((op) => op.status === 'running' &&
|
|
272
|
+
this.#contexts.get(op.scope)?.bridge.binding.fingerprint === fingerprint &&
|
|
273
|
+
this.#contexts.get(op.scope)?.description.name === input.name), 'operation-busy');
|
|
274
|
+
const result = await bridge.update(input, signal);
|
|
275
|
+
assert(!result.error && result.name, result.error ?? 'datasource-definition-invalid');
|
|
276
|
+
this.#changed();
|
|
277
|
+
return { name: result.name };
|
|
278
|
+
}, signal);
|
|
279
|
+
}
|
|
280
|
+
async configure(exec, resolve, input) {
|
|
281
|
+
assert(exec.agent, 'agent-required');
|
|
282
|
+
assert((input.mode === 'create' && input.name === undefined) ||
|
|
283
|
+
(input.mode === 'edit' &&
|
|
284
|
+
typeof input.name === 'string' &&
|
|
285
|
+
!!input.name.trim() &&
|
|
286
|
+
input.name.length <= 256), 'invalid-request');
|
|
287
|
+
assert(typeof input.reason === 'string' && !!input.reason.trim() && input.reason.length <= 1000, 'invalid-request');
|
|
288
|
+
const agent = exec.agent;
|
|
289
|
+
if (this.interaction === 'none' ||
|
|
290
|
+
agent.session.header.origin === 'subagent' ||
|
|
291
|
+
(agent.session.header.delegationDepth ?? 0) > 0)
|
|
292
|
+
return { status: 'needs-configuration', ...(input.name ? { name: input.name } : {}) };
|
|
293
|
+
const controller = new AbortController();
|
|
294
|
+
const signal = AbortSignal.any([this.executionSignal(exec.signal, agent), controller.signal]);
|
|
295
|
+
const identity = sessionIdentity(exec);
|
|
296
|
+
const bridge = await resolve();
|
|
297
|
+
signal.throwIfAborted();
|
|
298
|
+
assert(identity === sessionIdentity(exec), 'context-changed');
|
|
299
|
+
this.#prune();
|
|
300
|
+
assert(this.#configurations.size < CAPACITY / 2, 'capacity-exceeded');
|
|
301
|
+
return new Promise((finish) => {
|
|
302
|
+
const id = randomUUID();
|
|
303
|
+
const request = {
|
|
304
|
+
exec,
|
|
305
|
+
settled: false,
|
|
306
|
+
controller,
|
|
307
|
+
resolve,
|
|
308
|
+
fingerprint: bridge.binding.fingerprint,
|
|
309
|
+
sessionIdentity: identity,
|
|
310
|
+
signal,
|
|
311
|
+
finish,
|
|
312
|
+
view: {
|
|
313
|
+
id,
|
|
314
|
+
sessionId: agent.session.id,
|
|
315
|
+
workspaceId: String(Reflect.get(agent.session.header, 'workspaceId') ?? ''),
|
|
316
|
+
configuration: { ...input },
|
|
317
|
+
status: 'awaiting-input',
|
|
318
|
+
},
|
|
319
|
+
stop: () => signal.removeEventListener('abort', abort),
|
|
320
|
+
};
|
|
321
|
+
const abort = () => this.#endConfiguration(request, { status: 'call-ended' });
|
|
322
|
+
this.#configurations.set(id, request);
|
|
323
|
+
signal.addEventListener('abort', abort, { once: true });
|
|
324
|
+
if (signal.aborted)
|
|
325
|
+
abort();
|
|
326
|
+
this.#changed();
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
#endConfiguration(request, result) {
|
|
330
|
+
if (request.settled)
|
|
331
|
+
return;
|
|
332
|
+
request.settled = true;
|
|
333
|
+
request.view.endedAt = this.now();
|
|
334
|
+
request.view.status =
|
|
335
|
+
result.status === 'ok'
|
|
336
|
+
? 'succeeded'
|
|
337
|
+
: result.status === 'failed'
|
|
338
|
+
? 'handed-off'
|
|
339
|
+
: result.status === 'context-changed'
|
|
340
|
+
? 'context-changed'
|
|
341
|
+
: result.status === 'cancelled'
|
|
342
|
+
? 'cancelled'
|
|
343
|
+
: 'call-ended';
|
|
344
|
+
request.stop();
|
|
345
|
+
request.finish(result);
|
|
346
|
+
this.#changed();
|
|
347
|
+
}
|
|
348
|
+
async #configurationCurrent(request, signal) {
|
|
349
|
+
assert(request.view.endedAt === undefined && !request.signal.aborted, 'call-ended');
|
|
350
|
+
signal.throwIfAborted();
|
|
351
|
+
const bridge = await request.resolve();
|
|
352
|
+
assert(bridge.binding.fingerprint === request.fingerprint &&
|
|
353
|
+
sessionIdentity(request.exec) === request.sessionIdentity, 'context-changed');
|
|
354
|
+
return bridge;
|
|
355
|
+
}
|
|
356
|
+
async configurationRequest(id, workspaceId, resolve, caller) {
|
|
357
|
+
const request = this.#configurations.get(id);
|
|
358
|
+
assert(request, 'call-ended');
|
|
359
|
+
const bridge = await this.#configurationCurrent(request, caller);
|
|
360
|
+
assert((!request.view.workspaceId || request.view.workspaceId === workspaceId) &&
|
|
361
|
+
(await resolve()).binding.fingerprint === bridge.binding.fingerprint, 'context-changed');
|
|
362
|
+
assert(request.view.status !== 'executing', 'operation-busy');
|
|
363
|
+
return request;
|
|
364
|
+
}
|
|
365
|
+
async selectConfiguration(id, workspaceId, name, resolve, caller) {
|
|
366
|
+
const request = await this.configurationRequest(id, workspaceId, resolve, caller);
|
|
367
|
+
assert(request.view.configuration.mode !== 'edit' || request.view.configuration.name === name, 'datasource-identity-fixed');
|
|
368
|
+
const signal = AbortSignal.any([caller, request.signal]);
|
|
369
|
+
const context = await this.#bind(workspaceId, request.resolve, name, signal);
|
|
370
|
+
const view = await this.#view(context);
|
|
371
|
+
await this.#configurationCurrent(request, signal);
|
|
372
|
+
assert(request.view.status !== 'executing', 'operation-busy');
|
|
373
|
+
assert(this.#contexts.size < CAPACITY, 'capacity-exceeded');
|
|
374
|
+
this.#contexts.set(context.token, context);
|
|
375
|
+
request.view.context = view;
|
|
376
|
+
request.view.status = 'awaiting-input';
|
|
377
|
+
delete request.view.failure;
|
|
378
|
+
// Once a datasource is chosen, use the existing credential/test operation lifecycle.
|
|
379
|
+
const attached = {
|
|
380
|
+
view: request.view,
|
|
381
|
+
context,
|
|
382
|
+
exec: request.exec,
|
|
383
|
+
signal: request.signal,
|
|
384
|
+
stop: () => { },
|
|
385
|
+
finish: ({ result }) => {
|
|
386
|
+
this.#endConfiguration(request, {
|
|
387
|
+
status: result.ok ? 'ok' : 'failed',
|
|
388
|
+
name: result.name,
|
|
389
|
+
latency_ms: result.latency_ms,
|
|
390
|
+
...(!result.ok ? { failure: result.failure, repair: result.repair } : {}),
|
|
391
|
+
});
|
|
392
|
+
},
|
|
393
|
+
reject: () => {
|
|
394
|
+
this.#endConfiguration(request, { status: 'context-changed' });
|
|
395
|
+
},
|
|
396
|
+
};
|
|
397
|
+
this.#requests.set(id, attached);
|
|
398
|
+
this.#changed();
|
|
399
|
+
return structuredClone(request.view);
|
|
400
|
+
}
|
|
240
401
|
#watchRefs(refs) {
|
|
241
402
|
const unique = new Set(refs);
|
|
242
403
|
for (const ref of unique)
|
|
@@ -359,6 +520,7 @@ export class MarivoCredentialService {
|
|
|
359
520
|
assert(exec.agent, 'agent-required');
|
|
360
521
|
const signal = this.executionSignal(exec.signal, exec.agent);
|
|
361
522
|
const context = await this.#bind('', resolve, name, signal);
|
|
523
|
+
this.#activeContexts.add(context);
|
|
362
524
|
const stop = this.#watchRefs(context.description.refs);
|
|
363
525
|
try {
|
|
364
526
|
const tested = await this.#awaitCredentials(exec, context, await this.#view(context), signal);
|
|
@@ -370,6 +532,7 @@ export class MarivoCredentialService {
|
|
|
370
532
|
}
|
|
371
533
|
finally {
|
|
372
534
|
stop();
|
|
535
|
+
this.#activeContexts.delete(context);
|
|
373
536
|
}
|
|
374
537
|
}
|
|
375
538
|
async prepareExecution(exec, resolve, names) {
|
|
@@ -394,6 +557,8 @@ export class MarivoCredentialService {
|
|
|
394
557
|
delete values[ref];
|
|
395
558
|
for (const stop of stops)
|
|
396
559
|
stop();
|
|
560
|
+
for (const context of contexts)
|
|
561
|
+
this.#activeContexts.delete(context);
|
|
397
562
|
};
|
|
398
563
|
try {
|
|
399
564
|
for (const name of names) {
|
|
@@ -401,6 +566,7 @@ export class MarivoCredentialService {
|
|
|
401
566
|
assert(!contexts.length ||
|
|
402
567
|
context.bridge.binding.fingerprint === contexts[0].bridge.binding.fingerprint, 'context-changed');
|
|
403
568
|
contexts.push(context);
|
|
569
|
+
this.#activeContexts.add(context);
|
|
404
570
|
stops.push(this.#watchRefs(context.description.refs));
|
|
405
571
|
const view = await this.#view(context);
|
|
406
572
|
if (view.refs.every((ref) => view.credentials[ref]?.configured))
|
|
@@ -425,8 +591,10 @@ export class MarivoCredentialService {
|
|
|
425
591
|
assert(!released, 'execution-ended');
|
|
426
592
|
signal.throwIfAborted();
|
|
427
593
|
assert(exec.agent?.session === session && currentWorkspace() === workspace, 'context-changed');
|
|
428
|
-
for (const context of contexts)
|
|
594
|
+
for (const context of contexts) {
|
|
595
|
+
assert(!context.invalidated, 'context-changed');
|
|
429
596
|
assert(versions.get(context) === this.#version(context), 'credentials-changed');
|
|
597
|
+
}
|
|
430
598
|
};
|
|
431
599
|
assertCurrent();
|
|
432
600
|
values = await this.#snapshot(contexts, signal);
|
|
@@ -459,9 +627,12 @@ export class MarivoCredentialService {
|
|
|
459
627
|
return {
|
|
460
628
|
generation: this.generation,
|
|
461
629
|
cursor: `${this.generation}:${this.#revision}`,
|
|
462
|
-
|
|
630
|
+
configurationRequests: [...this.#configurations.values()]
|
|
463
631
|
.filter((r) => r.view.sessionId === sessionId)
|
|
464
632
|
.map((r) => structuredClone(r.view)),
|
|
633
|
+
requests: [...this.#requests.values()]
|
|
634
|
+
.filter((r) => r.view.sessionId === sessionId && !this.#configurations.has(r.view.id))
|
|
635
|
+
.map((r) => structuredClone(r.view)),
|
|
465
636
|
};
|
|
466
637
|
}
|
|
467
638
|
async waitWatch(sessionId, cursor, signal) {
|
|
@@ -483,6 +654,17 @@ export class MarivoCredentialService {
|
|
|
483
654
|
});
|
|
484
655
|
}
|
|
485
656
|
signal.throwIfAborted();
|
|
657
|
+
for (const request of this.#configurations.values()) {
|
|
658
|
+
if (request.view.sessionId !== sessionId || request.view.endedAt !== undefined)
|
|
659
|
+
continue;
|
|
660
|
+
try {
|
|
661
|
+
await this.#configurationCurrent(request, signal);
|
|
662
|
+
}
|
|
663
|
+
catch {
|
|
664
|
+
if (!signal.aborted)
|
|
665
|
+
this.#endConfiguration(request, { status: 'context-changed' });
|
|
666
|
+
}
|
|
667
|
+
}
|
|
486
668
|
for (const request of this.#requests.values()) {
|
|
487
669
|
if (request.view.sessionId !== sessionId || request.view.endedAt !== undefined)
|
|
488
670
|
continue;
|
|
@@ -513,14 +695,20 @@ export class MarivoCredentialService {
|
|
|
513
695
|
assert(this.#operations.size < CAPACITY, 'capacity-exceeded');
|
|
514
696
|
const context = this.#contexts.get(input.scope);
|
|
515
697
|
assert(context, 'context-changed');
|
|
698
|
+
assert(!context.invalidated, 'context-changed');
|
|
699
|
+
assert(input.action === 'delete-datasource' || input.deleteCredentials === undefined, 'invalid-action');
|
|
516
700
|
const request = input.requestId ? this.#requests.get(input.requestId) : undefined;
|
|
517
701
|
assert(!input.requestId ||
|
|
518
702
|
(request &&
|
|
519
|
-
request.context === context &&
|
|
703
|
+
request.context.token === context.token &&
|
|
520
704
|
request.view.endedAt === undefined &&
|
|
521
705
|
!request.signal.aborted), 'call-ended');
|
|
522
706
|
assert((input.action === 'submit' || input.action === 'diagnose') === !!request, 'invalid-action');
|
|
523
|
-
assert(![...this.#operations.values()].some((op) => op.
|
|
707
|
+
assert(![...this.#operations.values()].some((op) => op.status === 'running' &&
|
|
708
|
+
(op.scope === input.scope ||
|
|
709
|
+
(this.#contexts.get(op.scope)?.bridge.binding.fingerprint ===
|
|
710
|
+
context.bridge.binding.fingerprint &&
|
|
711
|
+
this.#contexts.get(op.scope)?.description.name === context.description.name))), 'operation-busy');
|
|
524
712
|
if (request)
|
|
525
713
|
assert(request.view.status !== 'executing', 'operation-busy');
|
|
526
714
|
assert(input.version === this.#version(context), 'credentials-changed');
|
|
@@ -543,6 +731,9 @@ export class MarivoCredentialService {
|
|
|
543
731
|
phase: 'saving',
|
|
544
732
|
saved: [],
|
|
545
733
|
errors: [],
|
|
734
|
+
...(input.action === 'delete-datasource'
|
|
735
|
+
? { deleteCredentials: input.deleteCredentials === true }
|
|
736
|
+
: {}),
|
|
546
737
|
};
|
|
547
738
|
this.#operations.set(op.id, op);
|
|
548
739
|
const controller = new AbortController();
|
|
@@ -565,7 +756,70 @@ export class MarivoCredentialService {
|
|
|
565
756
|
async #run(context, request, op, changes, reference, version, signal) {
|
|
566
757
|
try {
|
|
567
758
|
await this.#current(context, signal);
|
|
568
|
-
|
|
759
|
+
const configuration = request && this.#configurations.get(request.view.id);
|
|
760
|
+
if (configuration)
|
|
761
|
+
await this.#configurationCurrent(configuration, signal);
|
|
762
|
+
if (op.action === 'delete-datasource') {
|
|
763
|
+
await this.#locked(async () => {
|
|
764
|
+
await this.#current(context, signal);
|
|
765
|
+
assert(version === this.#version(context), 'credentials-changed');
|
|
766
|
+
assert(context.bridge.remove, 'datasource-remove-unavailable');
|
|
767
|
+
op.phase = 'removing';
|
|
768
|
+
// Revoke pending uses even if the subprocess result is later lost.
|
|
769
|
+
for (const bound of new Set([
|
|
770
|
+
...this.#contexts.values(),
|
|
771
|
+
...this.#activeContexts,
|
|
772
|
+
...[...this.#requests.values()].map((pending) => pending.context),
|
|
773
|
+
])) {
|
|
774
|
+
if (bound.bridge.binding.fingerprint !== context.bridge.binding.fingerprint ||
|
|
775
|
+
bound.description.name !== context.description.name)
|
|
776
|
+
continue;
|
|
777
|
+
bound.invalidated = true;
|
|
778
|
+
}
|
|
779
|
+
for (const pending of this.#requests.values()) {
|
|
780
|
+
if (!pending.context.invalidated || pending.view.endedAt !== undefined)
|
|
781
|
+
continue;
|
|
782
|
+
pending.view.status = 'context-changed';
|
|
783
|
+
pending.view.endedAt = this.now();
|
|
784
|
+
pending.stop();
|
|
785
|
+
pending.reject(new CredentialServiceError('context-changed'));
|
|
786
|
+
}
|
|
787
|
+
for (const pending of this.#configurations.values()) {
|
|
788
|
+
if (pending.fingerprint === context.bridge.binding.fingerprint &&
|
|
789
|
+
(pending.view.context?.name === context.description.name ||
|
|
790
|
+
(pending.view.configuration.mode === 'edit' &&
|
|
791
|
+
pending.view.configuration.name === context.description.name)))
|
|
792
|
+
this.#endConfiguration(pending, { status: 'context-changed' });
|
|
793
|
+
}
|
|
794
|
+
this.#changed();
|
|
795
|
+
const result = await context.bridge.remove(context.description, signal);
|
|
796
|
+
assert(!result.error && result.name === context.description.name, result.error ?? 'datasource-remove-failed');
|
|
797
|
+
op.datasourceRemoved = true;
|
|
798
|
+
for (const key of this.#history.keys()) {
|
|
799
|
+
const [fingerprint, name] = JSON.parse(key);
|
|
800
|
+
if (fingerprint === context.bridge.binding.fingerprint &&
|
|
801
|
+
name === context.description.name)
|
|
802
|
+
this.#history.delete(key);
|
|
803
|
+
}
|
|
804
|
+
if (op.deleteCredentials) {
|
|
805
|
+
op.deletedCredentials = [];
|
|
806
|
+
op.credentialDeleteFailures = [];
|
|
807
|
+
for (const ref of new Set(context.description.refs)) {
|
|
808
|
+
signal.throwIfAborted();
|
|
809
|
+
this.invalidate([ref]);
|
|
810
|
+
try {
|
|
811
|
+
await this.#store.unset(credentialRef(marivoCredentialStorageRef(ref)));
|
|
812
|
+
op.deletedCredentials.push(ref);
|
|
813
|
+
}
|
|
814
|
+
catch {
|
|
815
|
+
op.credentialDeleteFailures.push(ref);
|
|
816
|
+
}
|
|
817
|
+
}
|
|
818
|
+
assert(op.credentialDeleteFailures.length === 0, 'credential-delete-failed');
|
|
819
|
+
}
|
|
820
|
+
}, signal);
|
|
821
|
+
}
|
|
822
|
+
else if (op.action === 'diagnose') {
|
|
569
823
|
assert(request?.view.failure, 'no-test-failure');
|
|
570
824
|
op.result = request.view.failure;
|
|
571
825
|
request.view.status = 'handed-off';
|
|
@@ -606,6 +860,9 @@ export class MarivoCredentialService {
|
|
|
606
860
|
if (request && request.view.endedAt === undefined) {
|
|
607
861
|
request.view.context = await this.#view(context);
|
|
608
862
|
signal.throwIfAborted();
|
|
863
|
+
await this.#current(context, signal);
|
|
864
|
+
if (configuration)
|
|
865
|
+
await this.#configurationCurrent(configuration, signal);
|
|
609
866
|
assert(testedVersion === this.#version(context), 'credentials-changed');
|
|
610
867
|
if (op.result.ok) {
|
|
611
868
|
request.view.status = 'succeeded';
|
|
@@ -657,6 +914,16 @@ export class MarivoCredentialService {
|
|
|
657
914
|
this.#controllers.get(id)?.abort();
|
|
658
915
|
}
|
|
659
916
|
cancelRequest(id) {
|
|
917
|
+
const configuration = this.#configurations.get(id);
|
|
918
|
+
if (configuration) {
|
|
919
|
+
assert(configuration.view.endedAt === undefined, 'call-ended');
|
|
920
|
+
for (const op of this.#operations.values())
|
|
921
|
+
if (op.scope === configuration.view.context?.token && op.status === 'running')
|
|
922
|
+
this.#controllers.get(op.id)?.abort();
|
|
923
|
+
this.#endConfiguration(configuration, { status: 'cancelled' });
|
|
924
|
+
configuration.controller.abort();
|
|
925
|
+
return;
|
|
926
|
+
}
|
|
660
927
|
const request = this.#requests.get(id);
|
|
661
928
|
assert(request && request.view.endedAt === undefined && !request.signal.aborted, 'call-ended');
|
|
662
929
|
request.exec.agent.cancel({ kind: 'user' }, { keepInbox: true });
|
|
@@ -679,9 +946,17 @@ export class MarivoCredentialService {
|
|
|
679
946
|
this.#tasks.add(tracked);
|
|
680
947
|
return task;
|
|
681
948
|
}
|
|
682
|
-
|
|
949
|
+
close() {
|
|
950
|
+
if (this.#closing)
|
|
951
|
+
return this.#closing;
|
|
683
952
|
this.#lifetime.abort();
|
|
684
|
-
this.#
|
|
685
|
-
|
|
953
|
+
this.#closing = finishCleanup([
|
|
954
|
+
() => this.#changed(),
|
|
955
|
+
async () => {
|
|
956
|
+
while (this.#tasks.size)
|
|
957
|
+
await Promise.allSettled([...this.#tasks]);
|
|
958
|
+
},
|
|
959
|
+
]);
|
|
960
|
+
return this.#closing;
|
|
686
961
|
}
|
|
687
962
|
}
|
package/lib/datasource/test.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
import { registerMarivoTool } from "../tool-lifecycle.js";
|
|
2
3
|
import { resolveMarivoDatasourceBridge } from "./bridge.js";
|
|
3
4
|
export const MARIVO_DATASOURCE_TEST_TOOL_NAME = 'marivo_datasource_test';
|
|
4
5
|
export function createMarivoDatasourceTestTool(source, service) {
|
|
5
6
|
return defineTool({
|
|
6
7
|
name: MARIVO_DATASOURCE_TEST_TOOL_NAME,
|
|
7
|
-
description: 'Test a Marivo datasource through Host credentials. Missing credentials wait for the Web form while this call remains alive; configured connection failures return directly.',
|
|
8
|
+
description: 'Test a Marivo datasource through Host credentials after datasource changes, credential rotation, connection failures, or explicit user requests. Normal analysis with configured credentials needs no preliminary test; successful marivo_datasource_configure already tested that configuration. Missing credentials wait for the Web form only while this call remains alive; configured connection failures return directly. Only status=ok proves this connection test succeeded; verify the requested table and read access separately.',
|
|
8
9
|
parameters: {
|
|
9
10
|
name: { type: 'string', required: true, description: 'Configured datasource name.' },
|
|
10
11
|
},
|
|
@@ -20,7 +21,7 @@ export function createMarivoDatasourceTestTool(source, service) {
|
|
|
20
21
|
});
|
|
21
22
|
}
|
|
22
23
|
export function registerMarivoDatasourceTestTool(ctx, source, service) {
|
|
23
|
-
return ctx
|
|
24
|
+
return registerMarivoTool(ctx, createMarivoDatasourceTestTool(source, service));
|
|
24
25
|
}
|
|
25
26
|
/** Preserve the datasource tools' public status family; raw Marivo results stay inside the service. */
|
|
26
27
|
export function datasourceToolValue(result) {
|