@chengxianglibra/dsh-data-analysis 0.1.1 → 0.1.2
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 +83 -34
- package/lib/bin/environment.js +1 -1
- package/lib/bin/presentation-lint.js +21 -0
- package/lib/bridges.js +2 -2
- package/lib/client/credentials/create-datasource.js +67 -0
- package/lib/client/credentials/install.js +191 -0
- package/lib/client/credentials/model.js +494 -0
- package/lib/client/credentials/styles.js +17 -0
- package/lib/client/workspace-header-action.js +11 -0
- package/lib/client.js +1133 -736
- package/lib/compatibility.js +1 -1
- package/lib/datasource/authoring.js +70 -0
- package/lib/datasource/bridge-programs.js +30 -18
- package/lib/datasource/bridge.js +100 -8
- package/lib/datasource/index.js +3 -2
- package/lib/datasource/python.js +115 -0
- package/lib/datasource/resolver-program.js +112 -0
- package/lib/datasource/rpc.js +88 -0
- package/lib/datasource/service.js +687 -0
- package/lib/datasource/shell-env.js +2 -333
- package/lib/datasource/test.js +24 -51
- package/lib/environment/binding.js +54 -9
- package/lib/environment/runtime.js +89 -78
- package/lib/environment/subprocess.js +6 -1
- package/lib/environment/summary.js +3 -3
- package/lib/environment/workspace.js +5 -0
- package/lib/index.js +1 -1
- package/lib/plugin.js +168 -51
- package/lib/presentation/assets/portable.js +606 -0
- package/lib/presentation/assets/static.js +945 -0
- package/lib/presentation/build/index.js +71 -0
- package/lib/presentation/commit.js +156 -0
- package/lib/presentation/contracts/charts.js +476 -0
- package/lib/presentation/contracts/editing.js +58 -0
- package/lib/presentation/contracts/index.js +651 -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 +56 -0
- package/lib/presentation/files.js +83 -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 +195 -0
- package/lib/presentation/projection/program.js +144 -0
- package/lib/presentation/projection/sql-code.js +79 -0
- package/lib/presentation/receipt.js +34 -0
- package/lib/presentation/reports.js +122 -0
- package/lib/presentation/rpc.js +148 -0
- package/lib/presentation/tool.js +122 -0
- package/lib/python-execution-contracts.js +1 -0
- package/lib/python-execution.js +232 -0
- package/lib/semantic-browser/contracts.js +80 -0
- package/lib/semantic-browser/definition.js +64 -0
- package/lib/semantic-browser/program.js +119 -0
- package/lib/semantic-browser/service.js +72 -0
- package/lib/semantic-reference/bridge.js +118 -0
- package/lib/semantic-reference/contracts.js +113 -0
- package/lib/semantic-reference/index.js +3 -0
- package/lib/semantic-reference/rpc.js +122 -0
- package/lib/semantic-reference/search.js +94 -0
- package/lib/semantic-reference/usage.js +143 -0
- package/lib/types/bridges.d.ts +2 -2
- package/lib/types/client/credentials/create-datasource.d.ts +5 -0
- package/lib/types/client/credentials/install.d.ts +1 -0
- package/lib/types/client/credentials/model.d.ts +59 -0
- package/lib/types/client/credentials/styles.d.ts +1 -0
- package/lib/types/client/presentation/ask-dsh.d.ts +9 -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/copy-context.d.ts +5 -0
- package/lib/types/client/presentation/delivery-model.d.ts +54 -0
- package/lib/types/client/presentation/delivery.d.ts +77 -0
- package/lib/types/client/presentation/editor-controls.d.ts +12 -0
- package/lib/types/client/presentation/global-filters.d.ts +6 -0
- package/lib/types/client/presentation/host-entry.d.ts +12 -0
- package/lib/types/client/presentation/icons.d.ts +4 -0
- package/lib/types/client/presentation/install.d.ts +15 -0
- package/lib/types/client/presentation/markdown.d.ts +5 -0
- package/lib/types/client/presentation/model.d.ts +62 -0
- package/lib/types/client/presentation/reader.d.ts +11 -0
- package/lib/types/client/presentation/source-code-model.d.ts +16 -0
- package/lib/types/client/presentation/source-code.d.ts +11 -0
- package/lib/types/client/presentation/source-dialog.d.ts +13 -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 +12 -0
- package/lib/types/client/presentation/use-element-width.d.ts +5 -0
- package/lib/types/client/workspace-header-action.d.ts +7 -0
- package/lib/types/client.d.ts +4 -122
- package/lib/types/compatibility.d.ts +2 -2
- package/lib/types/datasource/authoring.d.ts +25 -0
- package/lib/types/datasource/bridge.d.ts +21 -3
- package/lib/types/datasource/index.d.ts +3 -5
- package/lib/types/datasource/python.d.ts +4 -0
- package/lib/types/datasource/resolver-program.d.ts +6 -0
- package/lib/types/datasource/rpc.d.ts +4 -0
- package/lib/types/datasource/service.d.ts +112 -0
- package/lib/types/datasource/shell-env.d.ts +0 -29
- package/lib/types/datasource/test.d.ts +6 -29
- 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 +13 -2
- package/lib/types/index.d.ts +1 -1
- package/lib/types/plugin.d.ts +11 -5
- package/lib/types/presentation/build/index.d.ts +11 -0
- package/lib/types/presentation/commit.d.ts +4 -0
- package/lib/types/presentation/contracts/charts.d.ts +13 -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 +252 -0
- package/lib/types/presentation/contracts/values.d.ts +9 -0
- package/lib/types/presentation/delivery.d.ts +3 -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 +5 -0
- package/lib/types/presentation/rpc.d.ts +24 -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/semantic-browser/contracts.d.ts +40 -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 +20 -0
- package/lib/types/semantic-reference/bridge.d.ts +11 -0
- package/lib/types/semantic-reference/contracts.d.ts +51 -0
- package/lib/types/semantic-reference/index.d.ts +3 -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/package.json +43 -19
- package/python/presentation-kit/dist/dsh_data_analysis_presentation_kit-1.1.0-py3-none-any.whl +0 -0
- package/skills/dsh-data-analysis-presentation/SKILL.md +24 -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 +27 -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 +392 -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 +79 -0
- package/skills/dsh-data-analysis-presentation/references/examples/computed-sources.draft.json +35 -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 +57 -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 +278 -0
- package/skills/dsh-data-analysis-presentation/references/examples/source-only.draft.json +19 -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 +42 -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/schema.md +138 -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
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { credentialRef, isCredentialRefName, } from '@deepseek-ai/dsh-credentials';
|
|
1
|
+
import { isCredentialRefName } from '@deepseek-ai/dsh-credentials';
|
|
3
2
|
import { MarivoEnvironmentError } from "../environment/index.js";
|
|
4
|
-
import { resolveMarivoDatasourceBridge, } from "./bridge.js";
|
|
5
3
|
export const MARIVO_CREDENTIAL_STORAGE_PREFIX = 'DSH_DATA_ANALYSIS_CREDENTIAL_';
|
|
6
|
-
export const MARIVO_CREDENTIAL_LEASE_PREFIX = '# dsh-marivo-credential-lease:';
|
|
7
|
-
export const DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS = 30 * 60_000;
|
|
8
|
-
export const DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES = 64;
|
|
9
|
-
const TOKEN_PATTERN = /^[A-Za-z0-9_-]{43}$/;
|
|
10
|
-
const SHELL_TOOL_NAMES = new Set(['bash', 'pwsh']);
|
|
11
4
|
const HOST_SHELL_ENVIRONMENT_NAMES = new Set([
|
|
12
5
|
'DSH_HOME',
|
|
13
6
|
'DSH_SESSION_ID',
|
|
@@ -56,331 +49,7 @@ export function marivoCredentialStorageRef(ref) {
|
|
|
56
49
|
.join('');
|
|
57
50
|
return `${MARIVO_CREDENTIAL_STORAGE_PREFIX}${encoded}`;
|
|
58
51
|
}
|
|
59
|
-
function credentialBindings(refs) {
|
|
60
|
-
return Object.freeze([...new Set(refs)].map((environmentRef) => ({
|
|
61
|
-
environmentRef,
|
|
62
|
-
storageRef: marivoCredentialStorageRef(environmentRef),
|
|
63
|
-
})));
|
|
64
|
-
}
|
|
65
|
-
function bashPrelude(token, bindings) {
|
|
66
|
-
return [
|
|
67
|
-
`${MARIVO_CREDENTIAL_LEASE_PREFIX}${token}`,
|
|
68
|
-
...bindings.flatMap(({ environmentRef, storageRef }) => [
|
|
69
|
-
`export ${environmentRef}="\${${storageRef}}"`,
|
|
70
|
-
`unset ${storageRef}`,
|
|
71
|
-
]),
|
|
72
|
-
'export MARIVO_PERSIST_CREDENTIALS=0',
|
|
73
|
-
].join('\n');
|
|
74
|
-
}
|
|
75
|
-
function pwshPrelude(token, bindings) {
|
|
76
|
-
return [
|
|
77
|
-
`${MARIVO_CREDENTIAL_LEASE_PREFIX}${token}`,
|
|
78
|
-
...bindings.flatMap(({ environmentRef, storageRef }) => [
|
|
79
|
-
`$env:${environmentRef} = $env:${storageRef}`,
|
|
80
|
-
`Remove-Item Env:${storageRef}`,
|
|
81
|
-
]),
|
|
82
|
-
"$env:MARIVO_PERSIST_CREDENTIALS = '0'",
|
|
83
|
-
].join('\n');
|
|
84
|
-
}
|
|
85
|
-
function leaseFailure(code, message, details = {}) {
|
|
86
|
-
return new MarivoEnvironmentError(code, message, details);
|
|
87
|
-
}
|
|
88
|
-
function commandArguments(execution) {
|
|
89
|
-
return typeof execution.arguments === 'object' &&
|
|
90
|
-
execution.arguments !== null &&
|
|
91
|
-
!Array.isArray(execution.arguments)
|
|
92
|
-
? execution.arguments
|
|
93
|
-
: undefined;
|
|
94
|
-
}
|
|
95
|
-
function hasCredentialStorageRefAt(command, offset, caseInsensitive) {
|
|
96
|
-
const candidate = caseInsensitive ? command.slice(offset).toUpperCase() : command.slice(offset);
|
|
97
|
-
if (!candidate.startsWith(MARIVO_CREDENTIAL_STORAGE_PREFIX))
|
|
98
|
-
return false;
|
|
99
|
-
let cursor = MARIVO_CREDENTIAL_STORAGE_PREFIX.length;
|
|
100
|
-
const firstHex = cursor;
|
|
101
|
-
while (cursor < candidate.length && /[A-F0-9]/.test(candidate[cursor] ?? ''))
|
|
102
|
-
cursor++;
|
|
103
|
-
return (cursor > firstHex &&
|
|
104
|
-
(cursor === candidate.length || !/[A-Z0-9_]/i.test(candidate[cursor] ?? '')));
|
|
105
|
-
}
|
|
106
|
-
function isBashCommentStart(command, offset) {
|
|
107
|
-
if (offset === 0)
|
|
108
|
-
return true;
|
|
109
|
-
return /[\s;&|()]/.test(command[offset - 1] ?? '');
|
|
110
|
-
}
|
|
111
|
-
function containsCredentialDereference(execution) {
|
|
112
|
-
const command = commandArguments(execution)?.command;
|
|
113
|
-
if (typeof command !== 'string')
|
|
114
|
-
return false;
|
|
115
|
-
const pwsh = execution.name === 'pwsh';
|
|
116
|
-
let quote;
|
|
117
|
-
let comment = false;
|
|
118
|
-
for (let index = 0; index < command.length; index++) {
|
|
119
|
-
const character = command[index];
|
|
120
|
-
if (comment) {
|
|
121
|
-
if (character === '\n' || character === '\r')
|
|
122
|
-
comment = false;
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
if (quote === "'") {
|
|
126
|
-
if (character === "'") {
|
|
127
|
-
if (pwsh && command[index + 1] === "'")
|
|
128
|
-
index++;
|
|
129
|
-
else
|
|
130
|
-
quote = undefined;
|
|
131
|
-
}
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
if (character === (pwsh ? '`' : '\\')) {
|
|
135
|
-
index++;
|
|
136
|
-
continue;
|
|
137
|
-
}
|
|
138
|
-
if (character === '#' && quote === undefined && (pwsh || isBashCommentStart(command, index))) {
|
|
139
|
-
comment = true;
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
if (character === "'" && quote === undefined) {
|
|
143
|
-
quote = "'";
|
|
144
|
-
continue;
|
|
145
|
-
}
|
|
146
|
-
if (character === '"') {
|
|
147
|
-
quote = quote === '"' ? undefined : '"';
|
|
148
|
-
continue;
|
|
149
|
-
}
|
|
150
|
-
if (character !== '$')
|
|
151
|
-
continue;
|
|
152
|
-
if (pwsh) {
|
|
153
|
-
if (command.slice(index + 1, index + 5).toLowerCase() === 'env:' &&
|
|
154
|
-
hasCredentialStorageRefAt(command, index + 5, true)) {
|
|
155
|
-
return true;
|
|
156
|
-
}
|
|
157
|
-
if (command.slice(index + 1, index + 6).toLowerCase() === '{env:' &&
|
|
158
|
-
hasCredentialStorageRefAt(command, index + 6, true)) {
|
|
159
|
-
return true;
|
|
160
|
-
}
|
|
161
|
-
continue;
|
|
162
|
-
}
|
|
163
|
-
const refOffset = command[index + 1] === '{' ? index + 2 : index + 1;
|
|
164
|
-
if (hasCredentialStorageRefAt(command, refOffset, false))
|
|
165
|
-
return true;
|
|
166
|
-
}
|
|
167
|
-
return false;
|
|
168
|
-
}
|
|
169
|
-
function leaseToken(execution) {
|
|
170
|
-
const command = commandArguments(execution)?.command;
|
|
171
|
-
if (typeof command !== 'string')
|
|
172
|
-
return undefined;
|
|
173
|
-
const firstLine = command.split(/\r?\n/, 1)[0] ?? '';
|
|
174
|
-
const candidate = firstLine.startsWith(MARIVO_CREDENTIAL_LEASE_PREFIX)
|
|
175
|
-
? firstLine.slice(MARIVO_CREDENTIAL_LEASE_PREFIX.length)
|
|
176
|
-
: undefined;
|
|
177
|
-
if (candidate !== undefined && TOKEN_PATTERN.test(candidate))
|
|
178
|
-
return candidate;
|
|
179
|
-
if (firstLine.includes('dsh-marivo-credential-')) {
|
|
180
|
-
throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease marker is malformed or unsupported');
|
|
181
|
-
}
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
function isHarnessPersistentShell(agent, name) {
|
|
185
|
-
const definition = agent.ctx.tools.get(name, agent);
|
|
186
|
-
return definition?.output.schema.type === 'string';
|
|
187
|
-
}
|
|
188
52
|
export function assertMarivoCredentialReferences(refs) {
|
|
189
|
-
for (const ref of refs)
|
|
53
|
+
for (const ref of refs)
|
|
190
54
|
marivoCredentialStorageRef(ref);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
/** Own bounded leases and expose credential values only to each admitted Shell execution. */
|
|
194
|
-
export class MarivoShellCredentialLeases {
|
|
195
|
-
#ctx;
|
|
196
|
-
#credentials;
|
|
197
|
-
#ttlMs;
|
|
198
|
-
#maxUses;
|
|
199
|
-
#leases = new Map();
|
|
200
|
-
#executionValues = new Map();
|
|
201
|
-
#contributors = new Map();
|
|
202
|
-
#disposed = false;
|
|
203
|
-
constructor(ctx, credentials, options = {}) {
|
|
204
|
-
const ttlMs = options.ttlMs ?? DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS;
|
|
205
|
-
const maxUses = options.maxUses ?? DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES;
|
|
206
|
-
if (!Number.isSafeInteger(ttlMs) ||
|
|
207
|
-
ttlMs < 1 ||
|
|
208
|
-
ttlMs > DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS) {
|
|
209
|
-
throw new TypeError(`Marivo shell credential lease ttlMs must be an integer from 1 to ${DEFAULT_MARIVO_CREDENTIAL_LEASE_TTL_MS}`);
|
|
210
|
-
}
|
|
211
|
-
if (!Number.isSafeInteger(maxUses) ||
|
|
212
|
-
maxUses < 1 ||
|
|
213
|
-
maxUses > DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES) {
|
|
214
|
-
throw new TypeError(`Marivo shell credential lease maxUses must be an integer from 1 to ${DEFAULT_MARIVO_CREDENTIAL_LEASE_MAX_USES}`);
|
|
215
|
-
}
|
|
216
|
-
this.#ctx = ctx;
|
|
217
|
-
this.#credentials = credentials;
|
|
218
|
-
this.#ttlMs = ttlMs;
|
|
219
|
-
this.#maxUses = maxUses;
|
|
220
|
-
}
|
|
221
|
-
#ensureContributor(ref) {
|
|
222
|
-
if (this.#disposed)
|
|
223
|
-
throw new Error('Marivo shell credential leases are disposed');
|
|
224
|
-
if (this.#contributors.has(ref))
|
|
225
|
-
return;
|
|
226
|
-
const dispose = shellEnvironmentRegistry(this.#ctx).register({
|
|
227
|
-
name: `dsh-data-analysis:credential:${ref}`,
|
|
228
|
-
variables: {
|
|
229
|
-
[ref]: {
|
|
230
|
-
description: 'Marivo datasource credential authorized for this Shell execution.',
|
|
231
|
-
},
|
|
232
|
-
},
|
|
233
|
-
resolve: (execution) => {
|
|
234
|
-
const value = this.#executionValues.get(execution)?.values.get(ref);
|
|
235
|
-
return value === undefined ? {} : { [ref]: value };
|
|
236
|
-
},
|
|
237
|
-
});
|
|
238
|
-
this.#contributors.set(ref, dispose);
|
|
239
|
-
}
|
|
240
|
-
#purgeExpired(now) {
|
|
241
|
-
for (const [token, lease] of this.#leases) {
|
|
242
|
-
if (lease.expiresAt <= now)
|
|
243
|
-
this.#leases.delete(token);
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
revokeLease(agent, bridge, datasourceName) {
|
|
247
|
-
for (const [token, lease] of this.#leases) {
|
|
248
|
-
if (lease.agent === agent &&
|
|
249
|
-
lease.workspaceFingerprint === bridge.binding.fingerprint &&
|
|
250
|
-
lease.datasourceName === datasourceName) {
|
|
251
|
-
this.#leases.delete(token);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
issueLease(agent, bridge, datasourceName, refs) {
|
|
256
|
-
if (this.#disposed)
|
|
257
|
-
throw new Error('Marivo shell credential leases are disposed');
|
|
258
|
-
const now = Date.now();
|
|
259
|
-
this.#purgeExpired(now);
|
|
260
|
-
this.revokeLease(agent, bridge, datasourceName);
|
|
261
|
-
assertMarivoCredentialReferences(refs);
|
|
262
|
-
const bindings = credentialBindings(refs);
|
|
263
|
-
for (const binding of bindings)
|
|
264
|
-
this.#ensureContributor(binding.storageRef);
|
|
265
|
-
const token = randomBytes(32).toString('base64url');
|
|
266
|
-
this.#leases.set(token, {
|
|
267
|
-
agent,
|
|
268
|
-
workspaceFingerprint: bridge.binding.fingerprint,
|
|
269
|
-
datasourceName,
|
|
270
|
-
bindings,
|
|
271
|
-
expiresAt: now + this.#ttlMs,
|
|
272
|
-
remainingUses: this.#maxUses,
|
|
273
|
-
});
|
|
274
|
-
return {
|
|
275
|
-
token,
|
|
276
|
-
expires_in_ms: this.#ttlMs,
|
|
277
|
-
max_uses: this.#maxUses,
|
|
278
|
-
usage: 'bounded-foreground-shell-lease',
|
|
279
|
-
bash_prelude: bashPrelude(token, bindings),
|
|
280
|
-
pwsh_prelude: pwshPrelude(token, bindings),
|
|
281
|
-
};
|
|
282
|
-
}
|
|
283
|
-
async #claim(agent, bridgeSource, execution, token) {
|
|
284
|
-
const lease = this.#leases.get(token);
|
|
285
|
-
if (lease === undefined) {
|
|
286
|
-
throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease is unknown; call marivo_datasource_access again');
|
|
287
|
-
}
|
|
288
|
-
if (lease.expiresAt <= Date.now()) {
|
|
289
|
-
this.#leases.delete(token);
|
|
290
|
-
throw leaseFailure('shell-credential-lease-expired', 'Marivo shell credential lease has expired; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
|
|
291
|
-
}
|
|
292
|
-
if (execution.agent !== agent || lease.agent !== agent) {
|
|
293
|
-
throw leaseFailure('shell-credential-lease-scope-mismatch', 'Marivo shell credential lease belongs to another Agent');
|
|
294
|
-
}
|
|
295
|
-
const bridge = await resolveMarivoDatasourceBridge(bridgeSource);
|
|
296
|
-
if (this.#leases.get(token) !== lease) {
|
|
297
|
-
throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential lease was revoked; call marivo_datasource_access again');
|
|
298
|
-
}
|
|
299
|
-
if (lease.workspaceFingerprint !== bridge.binding.fingerprint) {
|
|
300
|
-
throw leaseFailure('shell-credential-lease-scope-mismatch', 'Marivo shell credential lease belongs to another Workspace binding');
|
|
301
|
-
}
|
|
302
|
-
if (lease.expiresAt <= Date.now()) {
|
|
303
|
-
this.#leases.delete(token);
|
|
304
|
-
throw leaseFailure('shell-credential-lease-expired', 'Marivo shell credential lease has expired; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
|
|
305
|
-
}
|
|
306
|
-
const args = commandArguments(execution);
|
|
307
|
-
if (args?.run_in_background === true) {
|
|
308
|
-
throw leaseFailure('shell-credential-injection-unsupported', 'Marivo shell credential leases cannot be used by background Shell executions', { tool: execution.name });
|
|
309
|
-
}
|
|
310
|
-
if (isHarnessPersistentShell(agent, execution.name)) {
|
|
311
|
-
throw leaseFailure('shell-credential-injection-unsupported', `Marivo shell credential leases cannot be used by persistent ${execution.name}`, { tool: execution.name });
|
|
312
|
-
}
|
|
313
|
-
if (lease.remainingUses < 1) {
|
|
314
|
-
this.#leases.delete(token);
|
|
315
|
-
throw leaseFailure('shell-credential-lease-exhausted', 'Marivo shell credential lease is exhausted; call marivo_datasource_access again', { datasourceName: lease.datasourceName });
|
|
316
|
-
}
|
|
317
|
-
lease.remainingUses--;
|
|
318
|
-
const values = new Map();
|
|
319
|
-
for (const binding of lease.bindings) {
|
|
320
|
-
execution.signal.throwIfAborted();
|
|
321
|
-
let resolved;
|
|
322
|
-
try {
|
|
323
|
-
resolved = await this.#credentials.resolve(credentialRef(binding.storageRef));
|
|
324
|
-
}
|
|
325
|
-
catch {
|
|
326
|
-
throw leaseFailure('shell-credential-lease-resolve-failed', 'A credential authorized by the Marivo shell lease could not be resolved', { datasourceName: lease.datasourceName, ref: binding.environmentRef });
|
|
327
|
-
}
|
|
328
|
-
if (resolved === undefined) {
|
|
329
|
-
throw leaseFailure('shell-credential-lease-resolve-failed', 'A credential authorized by the Marivo shell lease is no longer configured', { datasourceName: lease.datasourceName, ref: binding.environmentRef });
|
|
330
|
-
}
|
|
331
|
-
values.set(binding.storageRef, resolved.value);
|
|
332
|
-
}
|
|
333
|
-
this.#executionValues.set(execution, { agent, values });
|
|
334
|
-
}
|
|
335
|
-
async prepareExecution(agent, bridgeSource, execution) {
|
|
336
|
-
if (this.#disposed)
|
|
337
|
-
throw new Error('Marivo shell credential leases are disposed');
|
|
338
|
-
const token = leaseToken(execution);
|
|
339
|
-
if (token === undefined) {
|
|
340
|
-
if (containsCredentialDereference(execution)) {
|
|
341
|
-
throw leaseFailure('shell-credential-lease-invalid', 'Marivo shell credential reference is not authorized because the exact prelude lease marker is missing. Call marivo_datasource_access again, copy the exact prelude, and use a foreground Shell execution', { tool: execution.name });
|
|
342
|
-
}
|
|
343
|
-
return false;
|
|
344
|
-
}
|
|
345
|
-
await this.#claim(agent, bridgeSource, execution, token);
|
|
346
|
-
return true;
|
|
347
|
-
}
|
|
348
|
-
installAgent(agent, bridgeSource) {
|
|
349
|
-
const stopPreExecute = agent.ctx.on('tools/pre-execute', async (execution, next) => {
|
|
350
|
-
if (execution.agent === agent && SHELL_TOOL_NAMES.has(execution.name)) {
|
|
351
|
-
await this.prepareExecution(agent, bridgeSource, execution);
|
|
352
|
-
}
|
|
353
|
-
return next();
|
|
354
|
-
});
|
|
355
|
-
const stopResult = agent.ctx.on('tools/result', (execution) => {
|
|
356
|
-
if (execution.agent === agent)
|
|
357
|
-
this.#executionValues.delete(execution);
|
|
358
|
-
});
|
|
359
|
-
let active = true;
|
|
360
|
-
return () => {
|
|
361
|
-
if (!active)
|
|
362
|
-
return;
|
|
363
|
-
active = false;
|
|
364
|
-
stopPreExecute();
|
|
365
|
-
stopResult();
|
|
366
|
-
for (const [token, lease] of this.#leases) {
|
|
367
|
-
if (lease.agent === agent)
|
|
368
|
-
this.#leases.delete(token);
|
|
369
|
-
}
|
|
370
|
-
for (const [execution, snapshot] of this.#executionValues) {
|
|
371
|
-
if (snapshot.agent === agent)
|
|
372
|
-
this.#executionValues.delete(execution);
|
|
373
|
-
}
|
|
374
|
-
};
|
|
375
|
-
}
|
|
376
|
-
dispose() {
|
|
377
|
-
if (this.#disposed)
|
|
378
|
-
return;
|
|
379
|
-
this.#disposed = true;
|
|
380
|
-
this.#leases.clear();
|
|
381
|
-
this.#executionValues.clear();
|
|
382
|
-
for (const dispose of this.#contributors.values())
|
|
383
|
-
dispose();
|
|
384
|
-
this.#contributors.clear();
|
|
385
|
-
}
|
|
386
55
|
}
|
package/lib/datasource/test.js
CHANGED
|
@@ -1,65 +1,38 @@
|
|
|
1
1
|
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
-
import { resolveMarivoDatasourceBridge
|
|
3
|
-
import { inspectMarivoDatasourceCredentials } from "./credentials.js";
|
|
2
|
+
import { resolveMarivoDatasourceBridge } from "./bridge.js";
|
|
4
3
|
export const MARIVO_DATASOURCE_TEST_TOOL_NAME = 'marivo_datasource_test';
|
|
5
|
-
function
|
|
6
|
-
if (typeof value !== 'string' || value.trim() === '' || value.length > 256) {
|
|
7
|
-
throw new TypeError('marivo_datasource_test name must be a non-empty string of at most 256 characters');
|
|
8
|
-
}
|
|
9
|
-
return value;
|
|
10
|
-
}
|
|
11
|
-
function renderValue(value) {
|
|
12
|
-
if (value.status === 'needs-credentials')
|
|
13
|
-
return JSON.stringify(value);
|
|
14
|
-
if (value.status === 'ok') {
|
|
15
|
-
return `Marivo datasource ${value.name} connection test succeeded${value.latency_ms === null ? '' : ` in ${value.latency_ms} ms`}.`;
|
|
16
|
-
}
|
|
17
|
-
return JSON.stringify(value);
|
|
18
|
-
}
|
|
19
|
-
/** Build the scoped datasource connection-test Tool. */
|
|
20
|
-
export function createMarivoDatasourceTestTool(bridgeSource, credentials, options) {
|
|
4
|
+
export function createMarivoDatasourceTestTool(source, service) {
|
|
21
5
|
return defineTool({
|
|
22
6
|
name: MARIVO_DATASOURCE_TEST_TOOL_NAME,
|
|
23
|
-
description: 'Test
|
|
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.',
|
|
24
8
|
parameters: {
|
|
25
|
-
name: {
|
|
26
|
-
type: 'string',
|
|
27
|
-
required: true,
|
|
28
|
-
description: 'Configured Marivo datasource name.',
|
|
29
|
-
},
|
|
9
|
+
name: { type: 'string', required: true, description: 'Configured datasource name.' },
|
|
30
10
|
},
|
|
31
11
|
output: {
|
|
32
12
|
schema: { type: 'json' },
|
|
33
|
-
render: (_args, value) => [
|
|
34
|
-
{ type: 'text', text: renderValue(value) },
|
|
35
|
-
],
|
|
13
|
+
render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
|
|
36
14
|
},
|
|
37
|
-
timeoutMs: 65_000,
|
|
38
15
|
async execute(args, exec) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const described = await bridge.describe(name, exec.signal);
|
|
43
|
-
const overlay = {};
|
|
44
|
-
const missing = await inspectMarivoDatasourceCredentials(described.refs, credentials, exec.signal, (ref, value) => {
|
|
45
|
-
overlay[ref] = value;
|
|
46
|
-
});
|
|
47
|
-
if (missing.length > 0)
|
|
48
|
-
return { status: 'needs-credentials', name: described.name, refs: missing };
|
|
49
|
-
const tested = await bridge.test(name, overlay, exec.signal);
|
|
50
|
-
if (tested.ok) {
|
|
51
|
-
return { status: 'ok', name: tested.name, latency_ms: tested.latency_ms };
|
|
52
|
-
}
|
|
53
|
-
return {
|
|
54
|
-
status: 'failed',
|
|
55
|
-
name: tested.name,
|
|
56
|
-
latency_ms: tested.latency_ms,
|
|
57
|
-
failure: tested.failure,
|
|
58
|
-
repair: tested.repair,
|
|
59
|
-
};
|
|
16
|
+
if (!args.name.trim() || args.name.length > 256)
|
|
17
|
+
throw new Error('Invalid datasource name');
|
|
18
|
+
return datasourceToolValue(await service.track(service.prepare('test', exec, () => resolveMarivoDatasourceBridge(source), args.name)));
|
|
60
19
|
},
|
|
61
20
|
});
|
|
62
21
|
}
|
|
63
|
-
export function registerMarivoDatasourceTestTool(ctx,
|
|
64
|
-
return ctx.tools.register(createMarivoDatasourceTestTool(
|
|
22
|
+
export function registerMarivoDatasourceTestTool(ctx, source, service) {
|
|
23
|
+
return ctx.tools.register(createMarivoDatasourceTestTool(source, service));
|
|
24
|
+
}
|
|
25
|
+
/** Preserve the datasource tools' public status family; raw Marivo results stay inside the service. */
|
|
26
|
+
export function datasourceToolValue(result) {
|
|
27
|
+
if (!('ok' in result))
|
|
28
|
+
return JSON.parse(JSON.stringify(result));
|
|
29
|
+
return JSON.parse(JSON.stringify(result.ok
|
|
30
|
+
? { status: 'ok', name: result.name, latency_ms: result.latency_ms }
|
|
31
|
+
: {
|
|
32
|
+
status: 'failed',
|
|
33
|
+
name: result.name,
|
|
34
|
+
latency_ms: result.latency_ms,
|
|
35
|
+
failure: result.failure,
|
|
36
|
+
repair: result.repair,
|
|
37
|
+
}));
|
|
65
38
|
}
|
|
@@ -39,10 +39,36 @@ _dsh_sys.argv = [_dsh_sys.argv[0], *_dsh_sys.argv[4:]]
|
|
|
39
39
|
const IMPORT_IDENTITY_PROGRAM = String.raw `
|
|
40
40
|
print(_dsh_json.dumps(_dsh_actual, sort_keys=True))
|
|
41
41
|
`.trim();
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
const PRESENTATION_IDENTITY_PRELUDE = String.raw `
|
|
43
|
+
try:
|
|
44
|
+
import dsh_data_analysis_presentation as _dsh_presentation
|
|
45
|
+
from importlib.metadata import distribution as _dsh_distribution
|
|
46
|
+
_dsh_presentation_distribution = _dsh_distribution("dsh-data-analysis-presentation-kit")
|
|
47
|
+
_dsh_presentation_path = _dsh_os.path.realpath(_dsh_presentation.__file__ or "")
|
|
48
|
+
if (
|
|
49
|
+
_dsh_presentation.__version__ != _dsh_expected_presentation["version"]
|
|
50
|
+
or _dsh_presentation_distribution.version != _dsh_expected_presentation["version"]
|
|
51
|
+
or _dsh_presentation_path != _dsh_os.path.realpath(_dsh_expected_presentation["packagePath"])
|
|
52
|
+
or _dsh_presentation_path != _dsh_os.path.realpath(_dsh_presentation_distribution.locate_file("dsh_data_analysis_presentation/__init__.py"))
|
|
53
|
+
or not callable(_dsh_presentation.write_dataset)
|
|
54
|
+
):
|
|
55
|
+
raise ValueError("presentation helper identity mismatch")
|
|
56
|
+
except Exception:
|
|
57
|
+
print(_dsh_json.dumps({"kind": "presentation-identity-mismatch"}), file=_dsh_sys.stderr)
|
|
58
|
+
raise SystemExit(78)
|
|
59
|
+
`.trim();
|
|
60
|
+
function redactSubprocessOutput(result, environmentOverlay, secretValues = []) {
|
|
61
|
+
if (environmentOverlay === undefined && secretValues.length === 0)
|
|
44
62
|
return result;
|
|
45
|
-
|
|
63
|
+
// These exact process controls carry no credential material. Redacting '1' would
|
|
64
|
+
// corrupt public schema IDs such as marivo.semantic_ref/v1.
|
|
65
|
+
const secrets = Object.entries(environmentOverlay ?? {})
|
|
66
|
+
.filter(([key, value]) => !((key === 'MARIVO_TELEMETRY' && value === 'off') ||
|
|
67
|
+
(key === 'PYTHONDONTWRITEBYTECODE' && value === '1')))
|
|
68
|
+
.map(([, value]) => value)
|
|
69
|
+
.filter((value) => value !== undefined && value !== '');
|
|
70
|
+
secrets.push(...secretValues.filter(Boolean));
|
|
71
|
+
secrets.sort((a, b) => b.length - a.length);
|
|
46
72
|
const redactText = (source) => {
|
|
47
73
|
let text = source;
|
|
48
74
|
for (const secret of secrets)
|
|
@@ -116,6 +142,9 @@ function fingerprint(binding) {
|
|
|
116
142
|
binding.marivoVersion,
|
|
117
143
|
binding.packagePath,
|
|
118
144
|
binding.subprocessPolicyId,
|
|
145
|
+
...(binding.presentationKit === undefined
|
|
146
|
+
? []
|
|
147
|
+
: [binding.presentationKit.version, binding.presentationKit.packagePath]),
|
|
119
148
|
];
|
|
120
149
|
return createHash('sha256').update(JSON.stringify(payload)).digest('hex');
|
|
121
150
|
}
|
|
@@ -142,7 +171,14 @@ export class MarivoEnvironment {
|
|
|
142
171
|
subprocessPolicy;
|
|
143
172
|
#failed = false;
|
|
144
173
|
constructor(binding, subprocessPolicy) {
|
|
145
|
-
this.binding = Object.freeze({
|
|
174
|
+
this.binding = Object.freeze({
|
|
175
|
+
...binding,
|
|
176
|
+
...(binding.presentationKit === undefined
|
|
177
|
+
? {}
|
|
178
|
+
: {
|
|
179
|
+
presentationKit: Object.freeze({ ...binding.presentationKit }),
|
|
180
|
+
}),
|
|
181
|
+
});
|
|
146
182
|
this.subprocessPolicy = subprocessPolicy;
|
|
147
183
|
}
|
|
148
184
|
get status() {
|
|
@@ -158,11 +194,14 @@ export class MarivoEnvironment {
|
|
|
158
194
|
}
|
|
159
195
|
if (request.program.trim() === '')
|
|
160
196
|
throw new TypeError('checked Python program must not be empty');
|
|
197
|
+
const presentationPrelude = this.binding.presentationKit === undefined
|
|
198
|
+
? ''
|
|
199
|
+
: `_dsh_expected_presentation = ${JSON.stringify(this.binding.presentationKit)}\n${PRESENTATION_IDENTITY_PRELUDE}\n`;
|
|
161
200
|
const rawResult = await this.subprocessPolicy.run({
|
|
162
201
|
executable: this.binding.pythonExecutable,
|
|
163
202
|
args: [
|
|
164
203
|
'-c',
|
|
165
|
-
`${CHECKED_IDENTITY_PRELUDE}\n${request.program}`,
|
|
204
|
+
`${CHECKED_IDENTITY_PRELUDE}\n${presentationPrelude}${request.program}`,
|
|
166
205
|
this.binding.pythonExecutable,
|
|
167
206
|
this.binding.marivoVersion,
|
|
168
207
|
this.binding.packagePath,
|
|
@@ -171,13 +210,14 @@ export class MarivoEnvironment {
|
|
|
171
210
|
...(request.environmentOverlay === undefined
|
|
172
211
|
? {}
|
|
173
212
|
: { environmentOverlay: request.environmentOverlay }),
|
|
213
|
+
...(request.stdin === undefined ? {} : { stdin: request.stdin }),
|
|
174
214
|
...(request.limits === undefined ? {} : { limits: request.limits }),
|
|
175
215
|
...(request.signal === undefined ? {} : { signal: request.signal }),
|
|
176
216
|
});
|
|
177
|
-
const result = redactSubprocessOutput(rawResult, request.environmentOverlay);
|
|
217
|
+
const result = redactSubprocessOutput(rawResult, request.environmentOverlay, request.secretValues);
|
|
178
218
|
if (result.exitCode === 78) {
|
|
179
219
|
this.#failed = true;
|
|
180
|
-
throw new MarivoEnvironmentError('binding-identity-mismatch', '
|
|
220
|
+
throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', {
|
|
181
221
|
fingerprint: this.binding.fingerprint,
|
|
182
222
|
exitCode: result.exitCode,
|
|
183
223
|
stderr: result.stderr.toString('utf8').slice(0, 2_000),
|
|
@@ -194,7 +234,7 @@ export class MarivoEnvironment {
|
|
|
194
234
|
});
|
|
195
235
|
if (result.exitCode !== 0) {
|
|
196
236
|
this.#failed = true;
|
|
197
|
-
throw new MarivoEnvironmentError('binding-identity-mismatch', '
|
|
237
|
+
throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', {
|
|
198
238
|
fingerprint: this.binding.fingerprint,
|
|
199
239
|
exitCode: result.exitCode,
|
|
200
240
|
stderr: result.stderr.toString('utf8').slice(0, 2_000),
|
|
@@ -213,7 +253,7 @@ export class MarivoEnvironment {
|
|
|
213
253
|
this.#failed = true;
|
|
214
254
|
if (cause instanceof MarivoEnvironmentError)
|
|
215
255
|
throw cause;
|
|
216
|
-
throw new MarivoEnvironmentError('binding-identity-mismatch', '
|
|
256
|
+
throw new MarivoEnvironmentError('binding-identity-mismatch', 'Bound Runtime import identity changed; explicit rebind is required', { fingerprint: this.binding.fingerprint }, { cause });
|
|
217
257
|
}
|
|
218
258
|
}
|
|
219
259
|
}
|
|
@@ -225,6 +265,10 @@ export function createSharedMarivoRuntimeRunner(runtime, options = {}) {
|
|
|
225
265
|
pythonExecutable: runtime.pythonExecutable,
|
|
226
266
|
marivoVersion: runtime.marivoVersion,
|
|
227
267
|
packagePath: runtime.packagePath,
|
|
268
|
+
presentationKit: {
|
|
269
|
+
version: runtime.presentationKitVersion,
|
|
270
|
+
packagePath: runtime.presentationKitPackagePath,
|
|
271
|
+
},
|
|
228
272
|
subprocessPolicyId: subprocessPolicy.id,
|
|
229
273
|
};
|
|
230
274
|
return new MarivoEnvironment({ ...partialBinding, fingerprint: fingerprint(partialBinding) }, subprocessPolicy);
|
|
@@ -263,6 +307,7 @@ export async function bindMarivoEnvironment(config, options = {}) {
|
|
|
263
307
|
pythonExecutable,
|
|
264
308
|
marivoVersion: report.marivo.version,
|
|
265
309
|
packagePath: normalizeAbsolute(report.marivo.package_path),
|
|
310
|
+
...(options.presentationKit === undefined ? {} : { presentationKit: options.presentationKit }),
|
|
266
311
|
subprocessPolicyId: subprocessPolicy.id,
|
|
267
312
|
};
|
|
268
313
|
const binding = {
|