dataiku-sdk 3.1.0 → 3.3.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 +253 -38
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +102 -0
- package/dist/packages/types/src/index.js +76 -0
- package/dist/src/auth.js +1 -1
- package/dist/src/cli/coerce.js +11 -2
- package/dist/src/cli/commands/analysis.js +4 -3
- package/dist/src/cli/commands/api-service.js +5 -3
- package/dist/src/cli/commands/app.js +61 -18
- package/dist/src/cli/commands/bundle.js +3 -2
- package/dist/src/cli/commands/code-env.js +23 -16
- package/dist/src/cli/commands/connection.d.ts +9 -0
- package/dist/src/cli/commands/connection.js +267 -1
- package/dist/src/cli/commands/dashboard.js +7 -6
- package/dist/src/cli/commands/data-collection.d.ts +2 -0
- package/dist/src/cli/commands/data-collection.js +164 -0
- package/dist/src/cli/commands/data-quality.js +9 -8
- package/dist/src/cli/commands/dataset.js +170 -40
- package/dist/src/cli/commands/doctor.js +4 -2
- package/dist/src/cli/commands/flow-zone.js +9 -8
- package/dist/src/cli/commands/folder.js +9 -8
- package/dist/src/cli/commands/future.js +4 -3
- package/dist/src/cli/commands/group.d.ts +2 -0
- package/dist/src/cli/commands/group.js +109 -0
- package/dist/src/cli/commands/index.js +151 -78
- package/dist/src/cli/commands/insight.js +8 -7
- package/dist/src/cli/commands/job.js +4 -3
- package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
- package/dist/src/cli/commands/knowledge-bank.js +80 -0
- package/dist/src/cli/commands/llm.d.ts +2 -0
- package/dist/src/cli/commands/llm.js +93 -0
- package/dist/src/cli/commands/macro.d.ts +2 -0
- package/dist/src/cli/commands/macro.js +221 -0
- package/dist/src/cli/commands/meaning.js +5 -4
- package/dist/src/cli/commands/ml-task.js +21 -8
- package/dist/src/cli/commands/model-evaluation-store.js +4 -3
- package/dist/src/cli/commands/notebook.js +17 -12
- package/dist/src/cli/commands/plugin.d.ts +2 -0
- package/dist/src/cli/commands/plugin.js +799 -0
- package/dist/src/cli/commands/project-folder.d.ts +2 -0
- package/dist/src/cli/commands/project-folder.js +197 -0
- package/dist/src/cli/commands/project-git.js +14 -54
- package/dist/src/cli/commands/project-library.js +7 -6
- package/dist/src/cli/commands/project.js +55 -8
- package/dist/src/cli/commands/recipe.js +50 -14
- package/dist/src/cli/commands/saved-model.d.ts +5 -0
- package/dist/src/cli/commands/saved-model.js +392 -4
- package/dist/src/cli/commands/scenario.js +147 -9
- package/dist/src/cli/commands/sql.js +3 -2
- package/dist/src/cli/commands/statistics.js +7 -6
- package/dist/src/cli/commands/user.d.ts +8 -0
- package/dist/src/cli/commands/user.js +296 -0
- package/dist/src/cli/commands/variable.js +2 -1
- package/dist/src/cli/commands/webapp.js +2 -1
- package/dist/src/cli/commands/wiki.js +7 -6
- package/dist/src/cli/contract.d.ts +2 -3
- package/dist/src/cli/contract.js +1167 -163
- package/dist/src/cli/flags.d.ts +4 -0
- package/dist/src/cli/flags.js +50 -2
- package/dist/src/cli/helpers/app-successor.d.ts +19 -4
- package/dist/src/cli/helpers/app-successor.js +90 -30
- package/dist/src/cli/helpers/cleanup.js +3 -1
- package/dist/src/cli/helpers/flow-zone.js +1 -2
- package/dist/src/cli/output.d.ts +1 -1
- package/dist/src/cli/output.js +23 -10
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +47 -1
- package/dist/src/cli.js +225 -95
- package/dist/src/client.d.ts +86 -6
- package/dist/src/client.js +263 -50
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +4 -0
- package/dist/src/index.d.ts +16 -7
- package/dist/src/index.js +11 -2
- package/dist/src/resources/analyses.js +1 -8
- package/dist/src/resources/applications.d.ts +8 -6
- package/dist/src/resources/applications.js +21 -21
- package/dist/src/resources/base.d.ts +4 -0
- package/dist/src/resources/base.js +15 -0
- package/dist/src/resources/bundles.d.ts +9 -4
- package/dist/src/resources/bundles.js +10 -5
- package/dist/src/resources/connections.d.ts +78 -1
- package/dist/src/resources/connections.js +132 -1
- package/dist/src/resources/data-collections.d.ts +118 -0
- package/dist/src/resources/data-collections.js +49 -0
- package/dist/src/resources/datasets.d.ts +69 -0
- package/dist/src/resources/datasets.js +99 -1
- package/dist/src/resources/folders.js +2 -1
- package/dist/src/resources/futures.d.ts +4 -1
- package/dist/src/resources/futures.js +38 -6
- package/dist/src/resources/groups.d.ts +47 -0
- package/dist/src/resources/groups.js +56 -0
- package/dist/src/resources/jobs.d.ts +8 -13
- package/dist/src/resources/jobs.js +41 -16
- package/dist/src/resources/knowledge-banks.d.ts +58 -0
- package/dist/src/resources/knowledge-banks.js +61 -0
- package/dist/src/resources/llms.d.ts +92 -0
- package/dist/src/resources/llms.js +62 -0
- package/dist/src/resources/macros.d.ts +121 -0
- package/dist/src/resources/macros.js +231 -0
- package/dist/src/resources/ml-tasks.d.ts +37 -2
- package/dist/src/resources/ml-tasks.js +100 -20
- package/dist/src/resources/model-evaluation-stores.js +1 -8
- package/dist/src/resources/notebooks.d.ts +43 -7
- package/dist/src/resources/notebooks.js +53 -13
- package/dist/src/resources/plugins.d.ts +265 -0
- package/dist/src/resources/plugins.js +458 -0
- package/dist/src/resources/project-folders.d.ts +71 -0
- package/dist/src/resources/project-folders.js +57 -0
- package/dist/src/resources/project-git.d.ts +6 -0
- package/dist/src/resources/project-git.js +69 -42
- package/dist/src/resources/project-library.d.ts +0 -1
- package/dist/src/resources/project-library.js +1 -17
- package/dist/src/resources/projects.d.ts +19 -3
- package/dist/src/resources/projects.js +30 -18
- package/dist/src/resources/recipes.d.ts +15 -1
- package/dist/src/resources/recipes.js +73 -6
- package/dist/src/resources/saved-models.d.ts +134 -1
- package/dist/src/resources/saved-models.js +214 -6
- package/dist/src/resources/scenarios.d.ts +105 -0
- package/dist/src/resources/scenarios.js +232 -106
- package/dist/src/resources/sql.js +5 -1
- package/dist/src/resources/users.d.ts +129 -0
- package/dist/src/resources/users.js +128 -0
- package/dist/src/resources/webapps.d.ts +15 -2
- package/dist/src/resources/webapps.js +18 -4
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/skill.d.ts +13 -4
- package/dist/src/skill.js +75 -24
- package/dist/src/utils/deep-merge.d.ts +1 -1
- package/dist/src/utils/polling.d.ts +20 -0
- package/dist/src/utils/polling.js +27 -0
- package/dist/src/utils/response-file.d.ts +2 -0
- package/dist/src/utils/response-file.js +34 -0
- package/dist/src/utils/secret-sanitize.d.ts +54 -0
- package/dist/src/utils/secret-sanitize.js +101 -0
- package/package.json +8 -5
- package/packages/types/dist/index.d.ts +102 -0
- package/packages/types/dist/index.js +76 -0
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +47 -225
- package/skills/dataiku-dss/references/app-releases.md +28 -0
- package/skills/dataiku-dss/references/authentication.md +39 -0
- package/skills/dataiku-dss/references/coding.md +8 -0
- package/skills/dataiku-dss/references/discovery.md +30 -0
- package/skills/dataiku-dss/references/flow-maps.md +12 -0
- package/skills/dataiku-dss/references/mutations.md +33 -0
- package/skills/dataiku-dss/references/troubleshooting.md +29 -0
|
@@ -113,6 +113,9 @@ export const ProjectMetadataSchema = Type.Object({
|
|
|
113
113
|
}, { additionalProperties: true, }))),
|
|
114
114
|
}, { additionalProperties: true, })),
|
|
115
115
|
}, { additionalProperties: true, });
|
|
116
|
+
export const ProjectTagsSchema = Type.Object({
|
|
117
|
+
tags: Type.Record(Type.String(), Type.Object({ color: Type.Optional(Type.String()), }, { additionalProperties: true, }), { additionalProperties: true, }),
|
|
118
|
+
}, { additionalProperties: true, });
|
|
116
119
|
// ---------------------------------------------------------------------------
|
|
117
120
|
// Datasets
|
|
118
121
|
// ---------------------------------------------------------------------------
|
|
@@ -347,6 +350,67 @@ export const ScenarioWaitResultSchema = Type.Object({
|
|
|
347
350
|
steps: Type.Optional(Type.Array(ScenarioStepRunSchema)),
|
|
348
351
|
});
|
|
349
352
|
// ---------------------------------------------------------------------------
|
|
353
|
+
// Macros (runnables)
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
/** A macro definition as returned by GET /projects/{pk}/runnables/{runnableType}. */
|
|
356
|
+
export const MacroDefinitionSchema = Type.Object({
|
|
357
|
+
runnableType: Type.String(),
|
|
358
|
+
ownerPluginId: Type.Optional(Type.String()),
|
|
359
|
+
meta: Type.Optional(Type.Object({
|
|
360
|
+
label: Type.Optional(Type.String()),
|
|
361
|
+
}, { additionalProperties: true, })),
|
|
362
|
+
longDescription: Type.Optional(Type.String()),
|
|
363
|
+
resultType: Type.Optional(Type.String()),
|
|
364
|
+
extension: Type.Optional(Type.String()),
|
|
365
|
+
mimeType: Type.Optional(Type.String()),
|
|
366
|
+
resultLabel: Type.Optional(Type.String()),
|
|
367
|
+
params: Type.Optional(Type.Array(Type.Object({
|
|
368
|
+
name: Type.String(),
|
|
369
|
+
type: Type.Optional(Type.String()),
|
|
370
|
+
}, { additionalProperties: true, }))),
|
|
371
|
+
adminParams: Type.Optional(Type.Array(Type.Unknown())),
|
|
372
|
+
}, { additionalProperties: true, });
|
|
373
|
+
/** One entry of GET /projects/{pk}/runnables (array[Macro]). */
|
|
374
|
+
export const MacroSummarySchema = Type.Object({
|
|
375
|
+
runnableType: Type.Optional(Type.String()),
|
|
376
|
+
/** Owning plugin id as the server returns it (live identifiers are server-authored). */
|
|
377
|
+
ownerPluginId: Type.Optional(Type.String()),
|
|
378
|
+
meta: Type.Optional(Type.Object({
|
|
379
|
+
label: Type.Optional(Type.String()),
|
|
380
|
+
}, { additionalProperties: true, })),
|
|
381
|
+
}, { additionalProperties: true, });
|
|
382
|
+
export const MacroSummaryArraySchema = Type.Array(MacroSummarySchema);
|
|
383
|
+
/**
|
|
384
|
+
* Poll-state response of GET /projects/{pk}/runnables/{runnableType}/state/{run}.
|
|
385
|
+
* While running: `running: true` and optionally a stack of `progress` entries.
|
|
386
|
+
* When finished: `running: false`; on failure `resultError`/`storedError` carry
|
|
387
|
+
* details, otherwise `type` is filled.
|
|
388
|
+
*/
|
|
389
|
+
export const MacroStateSchema = Type.Object({
|
|
390
|
+
exists: Type.Optional(Type.Boolean()),
|
|
391
|
+
running: Type.Optional(Type.Boolean()),
|
|
392
|
+
empty: Type.Optional(Type.Boolean()),
|
|
393
|
+
type: Type.Optional(Type.String()),
|
|
394
|
+
progress: Type.Optional(Type.Unknown()),
|
|
395
|
+
resultError: Type.Optional(Type.Unknown()),
|
|
396
|
+
storedError: Type.Optional(Type.Unknown()),
|
|
397
|
+
}, { additionalProperties: true, });
|
|
398
|
+
/** Run trigger response of POST /projects/{pk}/runnables/{runnableType}. */
|
|
399
|
+
export const MacroRunStartSchema = Type.Object({
|
|
400
|
+
runId: Type.String(),
|
|
401
|
+
}, { additionalProperties: true, });
|
|
402
|
+
/** Result of {@link MacrosResource.runAndWait}: bounded client-side polling. */
|
|
403
|
+
export const MacroWaitResultSchema = Type.Object({
|
|
404
|
+
runnableType: Type.String(),
|
|
405
|
+
runId: Type.String(),
|
|
406
|
+
running: Type.Boolean(),
|
|
407
|
+
success: Type.Boolean(),
|
|
408
|
+
elapsedMs: Type.Number(),
|
|
409
|
+
pollCount: Type.Number(),
|
|
410
|
+
timedOut: Type.Optional(Type.Boolean()),
|
|
411
|
+
state: Type.Optional(MacroStateSchema),
|
|
412
|
+
}, { additionalProperties: true, });
|
|
413
|
+
// ---------------------------------------------------------------------------
|
|
350
414
|
// Flow zones
|
|
351
415
|
// ---------------------------------------------------------------------------
|
|
352
416
|
export const FlowZoneObjectTypeSchema = Type.Union([
|
|
@@ -631,6 +695,11 @@ export const SqlNotebookCellSchema = Type.Object({
|
|
|
631
695
|
id: Type.String(),
|
|
632
696
|
type: Type.String(),
|
|
633
697
|
name: Type.Optional(Type.String()),
|
|
698
|
+
/**
|
|
699
|
+
* Cell source. DSS enforces a JSON string here: an array fails to parse
|
|
700
|
+
* server-side (`Expected a string but was BEGIN_ARRAY ... $.cells[0].code`),
|
|
701
|
+
* so the official-client example's line-array form does not apply to SQL.
|
|
702
|
+
*/
|
|
634
703
|
code: Type.String(),
|
|
635
704
|
}, { additionalProperties: true, });
|
|
636
705
|
export const SqlNotebookSummarySchema = Type.Object({
|
|
@@ -639,6 +708,13 @@ export const SqlNotebookSummarySchema = Type.Object({
|
|
|
639
708
|
language: Type.String(),
|
|
640
709
|
connection: Type.String(),
|
|
641
710
|
}, { additionalProperties: true, });
|
|
711
|
+
/**
|
|
712
|
+
* A SQL notebook's content as returned by GET. The notebook record carries
|
|
713
|
+
* the server-allocated `id` (a request-supplied `id` is rejected on create)
|
|
714
|
+
* plus `name`/`projectKey`/`isRunning` and similar receipt fields via
|
|
715
|
+
* `additionalProperties`; requests supply `connection` + `cells` (and
|
|
716
|
+
* optionally `name` as the display handle).
|
|
717
|
+
*/
|
|
642
718
|
export const SqlNotebookContentSchema = Type.Object({
|
|
643
719
|
connection: Type.String(),
|
|
644
720
|
cells: Type.Array(SqlNotebookCellSchema),
|
package/dist/src/auth.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { DataikuClient, } from "./client.js";
|
|
2
1
|
import { DataikuError, } from "./errors.js";
|
|
3
2
|
export async function validateCredentials(url, apiKey, options = {}) {
|
|
4
3
|
try {
|
|
4
|
+
const { DataikuClient, } = await import("./client.js");
|
|
5
5
|
const client = new DataikuClient({
|
|
6
6
|
url,
|
|
7
7
|
apiKey,
|
package/dist/src/cli/coerce.js
CHANGED
|
@@ -169,6 +169,15 @@ function selectSingleSource(flags, sourceFlags) {
|
|
|
169
169
|
}
|
|
170
170
|
return provided[0];
|
|
171
171
|
}
|
|
172
|
+
function readInputText(path, flag) {
|
|
173
|
+
try {
|
|
174
|
+
return readFileSync(path, "utf-8");
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
const cause = error.code;
|
|
178
|
+
throw new UsageError(`Could not read ${flag} file: ${path}`, "validation_failed", "Verify that the path names an existing, readable file.", { flag, path, ...(cause ? { cause, } : {}), });
|
|
179
|
+
}
|
|
180
|
+
}
|
|
172
181
|
function resolveJsonSourceText(flags, kind) {
|
|
173
182
|
const source = selectSingleSource(flags, JSON_SOURCE_FLAGS);
|
|
174
183
|
if (source === undefined)
|
|
@@ -182,7 +191,7 @@ function resolveJsonSourceText(flags, kind) {
|
|
|
182
191
|
if (source === "data-file") {
|
|
183
192
|
const fileValue = flags["data-file"];
|
|
184
193
|
if (typeof fileValue === "string") {
|
|
185
|
-
const jsonText =
|
|
194
|
+
const jsonText = readInputText(fileValue, "--data-file");
|
|
186
195
|
return kind === "object"
|
|
187
196
|
? parseJsonObject(jsonText, fileValue)
|
|
188
197
|
: parseJsonValue(jsonText, fileValue);
|
|
@@ -241,7 +250,7 @@ export function textInput(flags) {
|
|
|
241
250
|
if (source === "file") {
|
|
242
251
|
const fileValue = flags["file"];
|
|
243
252
|
if (typeof fileValue === "string")
|
|
244
|
-
return
|
|
253
|
+
return readInputText(fileValue, "--file");
|
|
245
254
|
}
|
|
246
255
|
const contentValue = flags["content"];
|
|
247
256
|
if (typeof contentValue === "string")
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { requiredStringFlag, } from "../coerce.js";
|
|
2
|
+
import { executionMode, } from "../flags.js";
|
|
2
3
|
import { readIfExists, skipResult, } from "../output.js";
|
|
3
4
|
import { requireArgs, } from "../usage.js";
|
|
4
5
|
export const analysisCommands = {
|
|
@@ -23,7 +24,7 @@ export const analysisCommands = {
|
|
|
23
24
|
const inputDataset = requiredStringFlag(f, "input-dataset", usage);
|
|
24
25
|
const projectKey = f["project-key"];
|
|
25
26
|
const options = { inputDataset, projectKey, };
|
|
26
|
-
if (f
|
|
27
|
+
if (executionMode(f).dryRun) {
|
|
27
28
|
return {
|
|
28
29
|
dryRun: true,
|
|
29
30
|
action: "create",
|
|
@@ -45,11 +46,11 @@ export const analysisCommands = {
|
|
|
45
46
|
handler: async (c, a, f) => {
|
|
46
47
|
requireArgs(a, 1, "dss analysis delete <analysisId> [--if-exists] [--dry-run] [--project-key KEY]");
|
|
47
48
|
const projectKey = f["project-key"];
|
|
48
|
-
if (f
|
|
49
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
49
50
|
const current = await readIfExists(() => c.analyses.get(a[0], projectKey));
|
|
50
51
|
if (!current)
|
|
51
52
|
return skipResult("analysis", a[0], "missing");
|
|
52
|
-
if (f
|
|
53
|
+
if (executionMode(f).dryRun) {
|
|
53
54
|
return { dryRun: true, action: "delete", resource: "analysis", id: a[0], current, };
|
|
54
55
|
}
|
|
55
56
|
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { ClientValidationError, } from "../../errors.js";
|
|
2
|
+
import { writeResponseToFile, } from "../../utils/response-file.js";
|
|
2
3
|
import { stableHash, } from "../../utils/stable-hash.js";
|
|
3
4
|
import { requiredJsonInput, } from "../coerce.js";
|
|
5
|
+
import { executionMode, } from "../flags.js";
|
|
4
6
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
7
|
function validateExpectHash(value) {
|
|
6
8
|
if (value === undefined || value === false)
|
|
@@ -42,7 +44,7 @@ export const apiServiceCommands = {
|
|
|
42
44
|
const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (service settings).");
|
|
43
45
|
const projectKey = f["project-key"];
|
|
44
46
|
const expectHash = validateExpectHash(f["expect-hash"]);
|
|
45
|
-
if (f
|
|
47
|
+
if (executionMode(f).dryRun) {
|
|
46
48
|
const current = await c.apiServices.getSettings(a[0], projectKey);
|
|
47
49
|
const currentHash = stableHash(current);
|
|
48
50
|
if (expectHash !== undefined && currentHash !== expectHash.toLowerCase()) {
|
|
@@ -81,7 +83,7 @@ export const apiServiceCommands = {
|
|
|
81
83
|
type: "STD_PREDICTION",
|
|
82
84
|
modelRef: a[2],
|
|
83
85
|
};
|
|
84
|
-
if (f
|
|
86
|
+
if (executionMode(f).dryRun)
|
|
85
87
|
return endpoint;
|
|
86
88
|
return c.apiServices.addPredictionEndpoint(a[0], a[1], a[2], f["project-key"]);
|
|
87
89
|
},
|
|
@@ -143,7 +145,7 @@ export const apiServiceCommands = {
|
|
|
143
145
|
if (!res.body) {
|
|
144
146
|
throw new Error("apiServices.downloadPackageArchive response did not include a body");
|
|
145
147
|
}
|
|
146
|
-
const bytes = await
|
|
148
|
+
const bytes = await writeResponseToFile(out, res);
|
|
147
149
|
return { path: out, bytes, };
|
|
148
150
|
},
|
|
149
151
|
usage: "dss api-service download-package <serviceId> <packageId> --output PATH [--project-key KEY]",
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { ClientValidationError, DataikuError, } from "../../errors.js";
|
|
2
2
|
import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
|
|
3
3
|
import { jsonInput, num, parseBooleanOption, plainRecord, requiredJsonInput, requiredStringFlag, stringField, } from "../coerce.js";
|
|
4
|
+
import { executionMode, } from "../flags.js";
|
|
4
5
|
import { appPermissionsHash, appPermissionsVerificationError, assertAppPermissionsSnapshotBinding, buildAppPermissionsSnapshot, diffAppPermissions, readAppPermissionsSnapshot, writeAppPermissionsSnapshot, } from "../helpers/app-permissions.js";
|
|
5
|
-
import { createSuccessorInstance, futureTargetIdentity, isDefinitiveRejection, preflightSuccessorInstance, requireAbsentProjectTarget, safeErrorSummary, verifyAppInstance, VISUAL_UI_GATE, } from "../helpers/app-successor.js";
|
|
6
|
+
import { createSuccessorInstance, futureTargetIdentity, generatedInstanceKey, isDefinitiveRejection, preflightSuccessorInstance, requireAbsentProjectTarget, safeErrorSummary, verifyAppInstance, VISUAL_UI_GATE, } from "../helpers/app-successor.js";
|
|
6
7
|
import { CommandResultFailure, } from "../output.js";
|
|
7
8
|
import { requireArgs, requireNoArgs, UsageError, } from "../usage.js";
|
|
8
9
|
const CREATE_INSTANCE_USAGE = "dss app create-instance <appId> (--data JSON|--data-file PATH|--stdin) [--wait] [--timeout MS] [--poll-interval MS]";
|
|
9
10
|
const MANIFEST_VERSION_USAGE = "dss app manifest-version [--project-key KEY]";
|
|
10
11
|
const SET_MANIFEST_VERSION_USAGE = "dss app set-manifest-version (--manifest-version V|--version-notes NOTES) [--expect-hash SHA256] [--dry-run] [--project-key KEY]";
|
|
11
|
-
const CREATE_SUCCESSOR_USAGE = "dss app create-successor-instance <appId> --from KEY --to KEY [--name NAME] [--copy-permissions] [--timeout MS] [--poll-interval MS] [--dry-run] [--record-cleanup PATH]";
|
|
12
|
-
const SUCCESSOR_PREFLIGHT_USAGE = "dss app successor-preflight <appId> --from KEY --to KEY [--name NAME] [--copy-permissions]";
|
|
12
|
+
const CREATE_SUCCESSOR_USAGE = "dss app create-successor-instance <appId> --from KEY [--to KEY] [--name NAME] [--copy-permissions] [--timeout MS] [--poll-interval MS] [--dry-run] [--record-cleanup PATH]";
|
|
13
|
+
const SUCCESSOR_PREFLIGHT_USAGE = "dss app successor-preflight <appId> --from KEY [--to KEY] [--name NAME] [--copy-permissions]";
|
|
13
14
|
const VERIFY_INSTANCE_USAGE = "dss app verify-instance <appId> --project-key KEY [--expect-version V]";
|
|
14
15
|
const DELETE_INSTANCE_USAGE = "dss app delete-instance --project-key KEY [--future-id ID] [--expect-project-incarnation SHA256] [--unconfirmed-creation] [--timeout MS] [--poll-interval MS]";
|
|
15
16
|
const CREATE_INSTANCE_INDETERMINATE_REMEDIATION = "DSS may have accepted the creation, but no future ID or project incarnation authorizes cleanup. Inspect `dss app instances <appId>` and DSS task history; delete only after creation is known terminal and the target identity is verified.";
|
|
16
|
-
const CREATE_INSTANCE_REJECTED_REMEDIATION = "DSS refused the creation request, so no instance project was created: fix the reported problem and retry.";
|
|
17
|
+
const CREATE_INSTANCE_REJECTED_REMEDIATION = "DSS refused the creation request, so no instance project was created: fix the reported problem and retry. If the target key itself is the problem, rerun create-instance with targetProjectKey omitted to generate a fresh random key (the generated key is a new key, never the rejected one).";
|
|
17
18
|
const DELETE_INSTANCE_FUTURE_REMEDIATION = "The supplied creation future could not authorize deletion. Inspect that future and the target project, then retry only with a future whose terminal result reports this project.";
|
|
18
19
|
const DELETE_INSTANCE_UNCONFIRMED_REMEDIATION = "Creation may still be running and DSS returned no future ID. Inspect the app's instances and wait until creation is known terminal before running delete-instance without --unconfirmed-creation.";
|
|
19
20
|
const DELETE_INSTANCE_DELETE_REMEDIATION = "Future safety checks passed, but the project DELETE outcome is unresolved. Inspect the target project; if it still exists, retry delete-instance.";
|
|
@@ -51,6 +52,21 @@ async function assertProjectIncarnationHash(client, projectKey, expected) {
|
|
|
51
52
|
return;
|
|
52
53
|
throw new ClientValidationError(`Project ${projectKey} is not the same project incarnation recorded for this operation.`, "validation_failed", "Refusing to continue after project-key reuse. Re-read the current project and retry only with an artifact captured from that incarnation.", { projectKey, expectedProjectIncarnationHash: expected, currentProjectIncarnationHash: current, });
|
|
53
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* Optional caller-chosen successor key. Absent selects generated-key mode: the
|
|
57
|
+
* key is generated exactly once during apply (never during plan, preflight or
|
|
58
|
+
* dry-run) and no absence probe runs. A present-but-empty value is an input
|
|
59
|
+
* error, never a silent fallback to a generated key.
|
|
60
|
+
*/
|
|
61
|
+
function optionalSuccessorTarget(flags, usage) {
|
|
62
|
+
const value = flags["to"];
|
|
63
|
+
if (value === undefined)
|
|
64
|
+
return undefined;
|
|
65
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
66
|
+
throw new UsageError("--to must not be empty when supplied; omit --to entirely to create the successor with a generated key.", "validation_failed", `Usage: ${usage}`);
|
|
67
|
+
}
|
|
68
|
+
return value.trim();
|
|
69
|
+
}
|
|
54
70
|
export const appCommands = {
|
|
55
71
|
list: {
|
|
56
72
|
handler: (c) => c.applications.listApps(),
|
|
@@ -112,14 +128,36 @@ export const appCommands = {
|
|
|
112
128
|
handler: async (c, a, f) => {
|
|
113
129
|
requireArgs(a, 1, CREATE_INSTANCE_USAGE);
|
|
114
130
|
const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (instance creation payload).");
|
|
115
|
-
|
|
116
|
-
if (
|
|
117
|
-
|
|
131
|
+
let requestedProjectKey;
|
|
132
|
+
if (body["targetProjectKey"] === undefined) {
|
|
133
|
+
// Designer-friendly generated mode, mirroring the official Python
|
|
134
|
+
// client and the successor command's generated mode: one shared
|
|
135
|
+
// generator produces a random APP_ key (122 random bits of
|
|
136
|
+
// UUIDv4 hex, collisions negligible) that is POSTed as the
|
|
137
|
+
// authoritative targetProjectKey. No absent-project probe runs
|
|
138
|
+
// for a generated key, and errors never retry or fall back to
|
|
139
|
+
// an alternate key.
|
|
140
|
+
requestedProjectKey = generatedInstanceKey();
|
|
141
|
+
// Official key-as-name convenience: the display name defaults to
|
|
142
|
+
// the generated key when no targetProjectName is supplied.
|
|
143
|
+
if (body["targetProjectName"] === undefined) {
|
|
144
|
+
body.targetProjectName = requestedProjectKey;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
// Explicit keys keep the strict caller-chosen-key contract: the
|
|
149
|
+
// payload value must be a non-empty string, and the target must
|
|
150
|
+
// be proven absent before the POST.
|
|
151
|
+
const explicitKey = stringField(body, ["targetProjectKey",])?.trim();
|
|
152
|
+
if (!explicitKey) {
|
|
153
|
+
throw new UsageError("Instance creation payload must include a non-empty targetProjectKey.", "validation_failed", `Usage: ${CREATE_INSTANCE_USAGE}`);
|
|
154
|
+
}
|
|
155
|
+
requestedProjectKey = explicitKey;
|
|
156
|
+
await requireAbsentProjectTarget(c, requestedProjectKey, "targetProjectKey", {
|
|
157
|
+
appId: a[0],
|
|
158
|
+
});
|
|
118
159
|
}
|
|
119
160
|
body.targetProjectKey = requestedProjectKey;
|
|
120
|
-
await requireAbsentProjectTarget(c, requestedProjectKey, "targetProjectKey", {
|
|
121
|
-
appId: a[0],
|
|
122
|
-
});
|
|
123
161
|
let created;
|
|
124
162
|
try {
|
|
125
163
|
created = await c.applications.createInstance(a[0], body);
|
|
@@ -351,50 +389,55 @@ export const appCommands = {
|
|
|
351
389
|
};
|
|
352
390
|
},
|
|
353
391
|
usage: CREATE_INSTANCE_USAGE,
|
|
354
|
-
description: "Create an app instance from a Dataiku App template, optionally waiting for the
|
|
392
|
+
description: "Create an app instance from a Dataiku App template, optionally waiting for the creation future. Omit targetProjectKey to generate a random APP_ key (the default display name); explicit keys are trimmed and must be absent.",
|
|
355
393
|
examples: [
|
|
356
394
|
'dss app create-instance my-app --data \'{"targetProjectKey":"NEWPROJ"}\'',
|
|
357
395
|
'dss app create-instance my-app --data \'{"targetProjectKey":"NEWPROJ"}\' --wait --timeout 120000 --poll-interval 2000',
|
|
396
|
+
"dss app create-instance my-app --data '{}' --wait",
|
|
358
397
|
],
|
|
359
398
|
},
|
|
360
399
|
"successor-preflight": {
|
|
361
400
|
handler: async (c, a, f) => {
|
|
362
401
|
requireArgs(a, 1, SUCCESSOR_PREFLIGHT_USAGE);
|
|
402
|
+
const to = optionalSuccessorTarget(f, SUCCESSOR_PREFLIGHT_USAGE);
|
|
363
403
|
return preflightSuccessorInstance(c, {
|
|
364
404
|
appId: a[0],
|
|
365
405
|
from: requiredStringFlag(f, "from", SUCCESSOR_PREFLIGHT_USAGE),
|
|
366
|
-
to
|
|
406
|
+
...(to !== undefined ? { to, } : {}),
|
|
367
407
|
...(f["name"] !== undefined ? { name: f["name"], } : {}),
|
|
368
408
|
copyPermissions: parseBooleanOption(f["copy-permissions"], "--copy-permissions") ?? false,
|
|
369
409
|
dryRun: true,
|
|
370
410
|
}, SUCCESSOR_PREFLIGHT_USAGE);
|
|
371
411
|
},
|
|
372
412
|
usage: SUCCESSOR_PREFLIGHT_USAGE,
|
|
373
|
-
description: "Run every read-only successor gate before changing the template version: validate the template, verify the predecessor, prove target
|
|
413
|
+
description: "Run every read-only successor gate before changing the template version: validate the template, verify the predecessor, prove an explicit --to target absent, and optionally snapshot the predecessor ACL. Omit --to to preflight generated-key mode, where no target exists yet and nothing is probed or allocated.",
|
|
374
414
|
examples: [
|
|
375
415
|
"dss app successor-preflight my-app --from OLD_INSTANCE --to NEW_INSTANCE",
|
|
376
416
|
"dss app successor-preflight my-app --from OLD_INSTANCE --to NEW_INSTANCE --copy-permissions",
|
|
417
|
+
"dss app successor-preflight my-app --from OLD_INSTANCE",
|
|
377
418
|
],
|
|
378
419
|
},
|
|
379
420
|
"create-successor-instance": {
|
|
380
421
|
handler: async (c, a, f) => {
|
|
381
422
|
requireArgs(a, 1, CREATE_SUCCESSOR_USAGE);
|
|
423
|
+
const to = optionalSuccessorTarget(f, CREATE_SUCCESSOR_USAGE);
|
|
382
424
|
return createSuccessorInstance(c, {
|
|
383
425
|
appId: a[0],
|
|
384
426
|
from: requiredStringFlag(f, "from", CREATE_SUCCESSOR_USAGE),
|
|
385
|
-
to
|
|
427
|
+
...(to !== undefined ? { to, } : {}),
|
|
386
428
|
...(f["name"] !== undefined ? { name: f["name"], } : {}),
|
|
387
429
|
copyPermissions: parseBooleanOption(f["copy-permissions"], "--copy-permissions") ?? false,
|
|
388
|
-
dryRun:
|
|
430
|
+
dryRun: executionMode(f).dryRun,
|
|
389
431
|
timeoutMs: num(f["timeout"], "--timeout"),
|
|
390
432
|
pollIntervalMs: num(f["poll-interval"], "--poll-interval"),
|
|
391
433
|
}, CREATE_SUCCESSOR_USAGE);
|
|
392
434
|
},
|
|
393
435
|
usage: CREATE_SUCCESSOR_USAGE,
|
|
394
|
-
description: "Create a new app instance from the current template version alongside an existing instance. The predecessor is never modified and is retired separately and deliberately; the creation future is always awaited.",
|
|
436
|
+
description: "Create a new app instance from the current template version alongside an existing instance. An explicit --to keeps the strict caller-chosen-key contract (proven absent before the single POST); omit --to to generate the successor key once during apply and let the terminal future name it. The predecessor is never modified and is retired separately and deliberately; the creation future is always awaited.",
|
|
395
437
|
examples: [
|
|
396
438
|
"dss app create-successor-instance my-app --from OLD_INSTANCE --to NEW_INSTANCE",
|
|
397
439
|
'dss app create-successor-instance my-app --from OLD_INSTANCE --to NEW_INSTANCE --name "Release 2" --copy-permissions',
|
|
440
|
+
"dss app create-successor-instance my-app --from OLD_INSTANCE",
|
|
398
441
|
],
|
|
399
442
|
},
|
|
400
443
|
"instance-manifest": {
|
|
@@ -445,7 +488,7 @@ export const appCommands = {
|
|
|
445
488
|
...(version !== undefined ? { version, } : {}),
|
|
446
489
|
...(versionNotes !== undefined ? { versionNotes, } : {}),
|
|
447
490
|
...(f["expect-hash"] !== undefined ? { expectHash: f["expect-hash"], } : {}),
|
|
448
|
-
dryRun:
|
|
491
|
+
dryRun: executionMode(f).dryRun,
|
|
449
492
|
}, f["project-key"]);
|
|
450
493
|
if (result.outcome === "indeterminate") {
|
|
451
494
|
// The PUT may or may not have landed. Fail nonzero without
|
|
@@ -756,7 +799,7 @@ export const appCommands = {
|
|
|
756
799
|
assertAppPermissionsSnapshotBinding(snapshot, projectKey, c.getBaseUrl());
|
|
757
800
|
const currentProjectIncarnationHash = await requireProjectIncarnationHash(c, projectKey);
|
|
758
801
|
assertAppPermissionsSnapshotBinding(snapshot, projectKey, c.getBaseUrl(), currentProjectIncarnationHash);
|
|
759
|
-
const dryRun =
|
|
802
|
+
const dryRun = executionMode(f).dryRun;
|
|
760
803
|
const before = await c.projects.getPermissions(projectKey);
|
|
761
804
|
await assertProjectIncarnationHash(c, projectKey, snapshot.projectIncarnationHash);
|
|
762
805
|
const beforeHash = appPermissionsHash(before);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { writeResponseToFile, } from "../../utils/response-file.js";
|
|
1
2
|
import { json, parseBooleanOption, } from "../coerce.js";
|
|
2
3
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
3
4
|
export const bundleCommands = {
|
|
@@ -22,7 +23,7 @@ export const bundleCommands = {
|
|
|
22
23
|
return { exported: a[0], };
|
|
23
24
|
},
|
|
24
25
|
usage: "dss bundle export <bundleId> [--release-notes TEXT] [--evaluate-standards-checks true|false] [--project-key KEY]",
|
|
25
|
-
description: "Create
|
|
26
|
+
description: "Create an exported Design-node bundle. --release-notes forwards the documented releaseNotes parameter; --evaluate-standards-checks toggles the documented Project Standards Checks evaluation (default true; this instance forbids disabling it). DSS refuses re-exporting an existing bundle id — delete-exported first to replace one.",
|
|
26
27
|
examples: [
|
|
27
28
|
"dss bundle export v1",
|
|
28
29
|
"dss bundle export v1 --release-notes='Adds churn model' --evaluate-standards-checks=false",
|
|
@@ -48,7 +49,7 @@ export const bundleCommands = {
|
|
|
48
49
|
if (!res.body) {
|
|
49
50
|
throw new Error("bundles.downloadExportedArchive response did not include a body");
|
|
50
51
|
}
|
|
51
|
-
const bytes = await
|
|
52
|
+
const bytes = await writeResponseToFile(out, res);
|
|
52
53
|
return { path: out, bytes, };
|
|
53
54
|
},
|
|
54
55
|
usage: "dss bundle download-exported <bundleId> --output PATH [--project-key KEY]",
|
|
@@ -2,6 +2,7 @@ import { readFileSync, writeFileSync, } from "node:fs";
|
|
|
2
2
|
import { ClientValidationError, } from "../../errors.js";
|
|
3
3
|
import { stableHash, } from "../../utils/stable-hash.js";
|
|
4
4
|
import { json, jsonInput, num, numFlag, parseBooleanOption, } from "../coerce.js";
|
|
5
|
+
import { executionMode, } from "../flags.js";
|
|
5
6
|
import { enqueueCliWarning, isNotFoundError, readIfExists, skipResult, } from "../output.js";
|
|
6
7
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
7
8
|
const CODE_ENV_LANGS = ["PYTHON", "R",];
|
|
@@ -41,19 +42,25 @@ function codeEnvParams(flags) {
|
|
|
41
42
|
function splitPackageSpec(raw) {
|
|
42
43
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
43
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve requested package specs from --file/--packages/--package. An
|
|
47
|
+
* explicit source that resolves to zero specs is a legitimate clear
|
|
48
|
+
* (set-packages replaces specPackageList wholesale); only a call with no
|
|
49
|
+
* package source flag at all is a usage error.
|
|
50
|
+
*/
|
|
44
51
|
function codeEnvPackageList(flags) {
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
packages.push(...splitPackageSpec(readFileSync(flags["file"], "utf-8")));
|
|
48
|
-
}
|
|
52
|
+
const file = flags["file"];
|
|
53
|
+
const packages = typeof file === "string" ? splitPackageSpec(readFileSync(file, "utf-8")) : [];
|
|
49
54
|
if (typeof flags["packages"] === "string") {
|
|
50
55
|
packages.push(...splitPackageSpec(flags["packages"]));
|
|
51
56
|
}
|
|
52
57
|
if (typeof flags["package"] === "string") {
|
|
53
58
|
packages.push(...splitPackageSpec(flags["package"]));
|
|
54
59
|
}
|
|
55
|
-
if (
|
|
56
|
-
|
|
60
|
+
if (typeof file !== "string"
|
|
61
|
+
&& typeof flags["packages"] !== "string"
|
|
62
|
+
&& typeof flags["package"] !== "string") {
|
|
63
|
+
throw new UsageError("--packages, --package, or --file is required. Use newline-separated package specs for version constraints; an explicitly empty value (or empty file) clears the requested package list.");
|
|
57
64
|
}
|
|
58
65
|
return packages;
|
|
59
66
|
}
|
|
@@ -175,13 +182,13 @@ export const codeEnvCommands = {
|
|
|
175
182
|
}
|
|
176
183
|
const params = codeEnvParams(f);
|
|
177
184
|
const wait = codeEnvWait(f);
|
|
178
|
-
if (f["if-not-exists"] === true || f
|
|
185
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
179
186
|
const existing = (await c.codeEnvs.list({ envLang, }))
|
|
180
187
|
.find((env) => env.envName === a[1]);
|
|
181
|
-
if (existing && f["if-not-exists"] === true && f
|
|
188
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
182
189
|
return skipResult("code-env", a[1], "exists", { envLang, current: existing, });
|
|
183
190
|
}
|
|
184
|
-
if (f
|
|
191
|
+
if (executionMode(f).dryRun) {
|
|
185
192
|
return {
|
|
186
193
|
dryRun: true,
|
|
187
194
|
action: "create",
|
|
@@ -224,7 +231,7 @@ export const codeEnvCommands = {
|
|
|
224
231
|
}
|
|
225
232
|
const expectHash = validateExpectHash(f["expect-hash"]);
|
|
226
233
|
const definitionHash = stableHash(definition);
|
|
227
|
-
if (f
|
|
234
|
+
if (executionMode(f).dryRun) {
|
|
228
235
|
const current = await readIfExists(() => c.codeEnvs.getDefinition(envLang, a[1]));
|
|
229
236
|
const currentHash = current ? stableHash(current) : undefined;
|
|
230
237
|
if (expectHash !== undefined && currentHash !== expectHash) {
|
|
@@ -260,7 +267,7 @@ export const codeEnvCommands = {
|
|
|
260
267
|
const packages = codeEnvPackageList(f);
|
|
261
268
|
const installCorePackages = parseBooleanOption(f["install-core-packages"], "--install-core-packages");
|
|
262
269
|
const expectHash = validateExpectHash(f["expect-hash"]);
|
|
263
|
-
if (f
|
|
270
|
+
if (executionMode(f).dryRun) {
|
|
264
271
|
return {
|
|
265
272
|
dryRun: true,
|
|
266
273
|
action: "set-packages",
|
|
@@ -275,7 +282,7 @@ export const codeEnvCommands = {
|
|
|
275
282
|
return c.codeEnvs.setPackages(envLang, a[1], packages, { installCorePackages, expectHash, });
|
|
276
283
|
},
|
|
277
284
|
usage: "dss code-env set-packages <lang> <name> (--packages PKGS|--package PKG|--file PATH) [--install-core-packages true|false] [--expect-hash SHA256] [--dry-run]",
|
|
278
|
-
description: "Fetch the current definition, merge the requested package specs (and --install-core-packages) into it, then PUT the merged definition, so fields outside the package list are never dropped; --expect-hash refuses the merge when DSS changed since the hash was captured.",
|
|
285
|
+
description: "Fetch the current definition, merge the requested package specs (and --install-core-packages) into it, then PUT the merged definition, so fields outside the package list are never dropped; --expect-hash refuses the merge when DSS changed since the hash was captured. The package list is replaced wholesale, so an explicitly empty --packages value or an empty --file clears the requested packages.",
|
|
279
286
|
examples: [
|
|
280
287
|
"dss code-env set-packages PYTHON my_env --packages 'tabulate\\nnameparser' --dry-run",
|
|
281
288
|
"dss code-env set-packages PYTHON my_env --file requirements.txt",
|
|
@@ -297,7 +304,7 @@ export const codeEnvCommands = {
|
|
|
297
304
|
versionToUpdate,
|
|
298
305
|
wait,
|
|
299
306
|
};
|
|
300
|
-
if (f
|
|
307
|
+
if (executionMode(f).dryRun) {
|
|
301
308
|
return {
|
|
302
309
|
dryRun: true,
|
|
303
310
|
action: "update-packages",
|
|
@@ -320,7 +327,7 @@ export const codeEnvCommands = {
|
|
|
320
327
|
const envLang = requireEnvLang(a[0], usage);
|
|
321
328
|
const wait = codeEnvWait(f);
|
|
322
329
|
const envVersion = typeof f["env-version"] === "string" ? f["env-version"] : undefined;
|
|
323
|
-
if (f
|
|
330
|
+
if (executionMode(f).dryRun) {
|
|
324
331
|
return {
|
|
325
332
|
dryRun: true,
|
|
326
333
|
action: "update-images",
|
|
@@ -350,7 +357,7 @@ export const codeEnvCommands = {
|
|
|
350
357
|
throw new UsageError("--active is required. Usage: dss code-env set-jupyter <lang> <name> --active true|false");
|
|
351
358
|
}
|
|
352
359
|
const wait = codeEnvWait(f);
|
|
353
|
-
if (f
|
|
360
|
+
if (executionMode(f).dryRun) {
|
|
354
361
|
return {
|
|
355
362
|
dryRun: true,
|
|
356
363
|
action: "set-jupyter",
|
|
@@ -373,7 +380,7 @@ export const codeEnvCommands = {
|
|
|
373
380
|
requireArgs(a, 2, usage);
|
|
374
381
|
const envLang = requireEnvLang(a[0], usage);
|
|
375
382
|
const wait = codeEnvWait(f);
|
|
376
|
-
if (f
|
|
383
|
+
if (executionMode(f).dryRun) {
|
|
377
384
|
const current = await readIfExists(() => c.codeEnvs.get(envLang, a[1]));
|
|
378
385
|
if (!current)
|
|
379
386
|
return skipResult("code-env", a[1], "missing", { envLang, });
|
|
@@ -1,2 +1,11 @@
|
|
|
1
1
|
import type { CommandMeta } from "../types.js";
|
|
2
|
+
export declare function sanitizeConnectionSecrets<T>(value: T, secrets?: string[]): T;
|
|
3
|
+
/** Extract exact secret values (e.g. `params.password`) for error scrubbing. */
|
|
4
|
+
export declare function connectionSecretsFromBody(body: unknown): string[];
|
|
5
|
+
/**
|
|
6
|
+
* DSS validation errors can echo the submitted connection body (including
|
|
7
|
+
* credentials). Scrub error text and body before it reaches stderr or a
|
|
8
|
+
* structured failure.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sanitizeConnectionError(error: unknown, secrets: string[]): unknown;
|
|
2
11
|
export declare const connectionCommands: Record<string, CommandMeta>;
|