dataiku-sdk 3.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -19
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +39 -4
- package/dist/packages/types/src/index.js +31 -2
- 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 +66 -18
- package/dist/src/cli/commands/app.js +4 -3
- package/dist/src/cli/commands/bundle.js +51 -16
- package/dist/src/cli/commands/code-env.js +219 -57
- package/dist/src/cli/commands/code.d.ts +19 -0
- package/dist/src/cli/commands/code.js +87 -15
- package/dist/src/cli/commands/dashboard.js +7 -6
- package/dist/src/cli/commands/data-quality.js +9 -8
- package/dist/src/cli/commands/dataset.js +17 -11
- package/dist/src/cli/commands/doctor.js +4 -2
- package/dist/src/cli/commands/flow-zone.js +42 -13
- package/dist/src/cli/commands/folder.js +9 -8
- package/dist/src/cli/commands/future.js +2 -1
- package/dist/src/cli/commands/insight.js +8 -7
- package/dist/src/cli/commands/job.js +4 -3
- package/dist/src/cli/commands/meaning.js +5 -4
- package/dist/src/cli/commands/ml-task.js +3 -2
- package/dist/src/cli/commands/model-evaluation-store.js +4 -3
- package/dist/src/cli/commands/notebook.js +82 -50
- package/dist/src/cli/commands/project-git.js +8 -5
- package/dist/src/cli/commands/project-library.d.ts +21 -0
- package/dist/src/cli/commands/project-library.js +128 -56
- package/dist/src/cli/commands/project.js +19 -4
- package/dist/src/cli/commands/recipe.js +51 -25
- package/dist/src/cli/commands/saved-model.js +4 -3
- package/dist/src/cli/commands/scenario.js +9 -8
- package/dist/src/cli/commands/sql.d.ts +10 -0
- package/dist/src/cli/commands/sql.js +42 -19
- package/dist/src/cli/commands/statistics.js +7 -6
- package/dist/src/cli/commands/variable.js +2 -1
- package/dist/src/cli/commands/webapp.js +68 -13
- package/dist/src/cli/commands/wiki.js +7 -6
- package/dist/src/cli/contract.d.ts +16 -1
- package/dist/src/cli/contract.js +967 -186
- package/dist/src/cli/env.d.ts +9 -0
- package/dist/src/cli/env.js +20 -2
- package/dist/src/cli/flags.d.ts +4 -0
- package/dist/src/cli/flags.js +29 -1
- package/dist/src/cli/helpers/cleanup.js +104 -3
- package/dist/src/cli/helpers/flow-zone.d.ts +3 -0
- package/dist/src/cli/helpers/flow-zone.js +33 -3
- package/dist/src/cli/helpers/recipe.d.ts +20 -0
- package/dist/src/cli/helpers/recipe.js +158 -3
- package/dist/src/cli/output.d.ts +2 -0
- package/dist/src/cli/output.js +33 -6
- package/dist/src/cli/runtime.d.ts +35 -1
- package/dist/src/cli/runtime.js +125 -18
- package/dist/src/cli/types.d.ts +7 -0
- package/dist/src/cli.js +226 -95
- package/dist/src/client.d.ts +63 -1
- package/dist/src/client.js +305 -53
- package/dist/src/errors.d.ts +8 -2
- package/dist/src/errors.js +42 -0
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.js +2 -1
- package/dist/src/resources/api-services.d.ts +48 -6
- package/dist/src/resources/api-services.js +54 -8
- package/dist/src/resources/bundles.d.ts +51 -6
- package/dist/src/resources/bundles.js +39 -9
- package/dist/src/resources/code-envs.d.ts +25 -3
- package/dist/src/resources/code-envs.js +104 -2
- package/dist/src/resources/datasets.d.ts +20 -0
- package/dist/src/resources/datasets.js +232 -52
- package/dist/src/resources/folders.js +2 -1
- package/dist/src/resources/jobs.d.ts +35 -10
- package/dist/src/resources/jobs.js +92 -17
- package/dist/src/resources/notebooks.d.ts +77 -7
- package/dist/src/resources/notebooks.js +143 -19
- package/dist/src/resources/project-git.d.ts +12 -0
- package/dist/src/resources/project-git.js +39 -8
- package/dist/src/resources/project-library.d.ts +122 -7
- package/dist/src/resources/project-library.js +494 -25
- package/dist/src/resources/projects.d.ts +5 -4
- package/dist/src/resources/projects.js +21 -19
- package/dist/src/resources/scenarios.d.ts +30 -0
- package/dist/src/resources/scenarios.js +73 -27
- package/dist/src/resources/sql.d.ts +6 -0
- package/dist/src/resources/sql.js +23 -3
- package/dist/src/resources/webapps.d.ts +35 -4
- package/dist/src/resources/webapps.js +54 -5
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/skill.d.ts +18 -0
- package/dist/src/skill.js +93 -9
- package/dist/src/utils/flow-analysis.d.ts +53 -0
- package/dist/src/utils/flow-analysis.js +382 -0
- package/dist/src/utils/project-archive.d.ts +41 -2
- package/dist/src/utils/project-archive.js +159 -16
- package/dist/src/utils/response-file.d.ts +2 -0
- package/dist/src/utils/response-file.js +34 -0
- package/package.json +9 -7
- package/packages/types/dist/index.d.ts +39 -4
- package/packages/types/dist/index.js +31 -2
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +44 -197
- package/skills/dataiku-dss/references/app-releases.md +27 -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
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
2
2
|
import { num, parseBooleanOption, requiredJsonInput, } from "../coerce.js";
|
|
3
|
+
import { executionMode, } from "../flags.js";
|
|
3
4
|
import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
|
|
4
5
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
6
|
/**
|
|
@@ -52,20 +53,20 @@ export const dataQualityCommands = {
|
|
|
52
53
|
: typeof config.displayName === "string"
|
|
53
54
|
? config.displayName
|
|
54
55
|
: undefined;
|
|
55
|
-
if (f["if-not-exists"] === true || f
|
|
56
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
56
57
|
const existing = identity
|
|
57
58
|
? (await c.dataQuality.listRules(a[0], pk)).find((rule) => rule.id === identity || rule.displayName === identity)
|
|
58
59
|
: undefined;
|
|
59
|
-
if (existing && f["if-not-exists"] === true && f
|
|
60
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
60
61
|
return skipResult("data-quality", identity ?? existing.id, "exists", {
|
|
61
62
|
dataset: a[0],
|
|
62
63
|
current: existing,
|
|
63
64
|
});
|
|
64
65
|
}
|
|
65
|
-
if (f["if-not-exists"] === true && !identity && f
|
|
66
|
+
if (f["if-not-exists"] === true && !identity && !executionMode(f).dryRun) {
|
|
66
67
|
throw new UsageError("--if-not-exists requires rule id or displayName in the rule JSON.");
|
|
67
68
|
}
|
|
68
|
-
if (f
|
|
69
|
+
if (executionMode(f).dryRun) {
|
|
69
70
|
return {
|
|
70
71
|
dryRun: true,
|
|
71
72
|
action: "create-rule",
|
|
@@ -97,7 +98,7 @@ export const dataQualityCommands = {
|
|
|
97
98
|
handler: async (c, a, f) => {
|
|
98
99
|
requireArgs(a, 2, "dss data-quality update-rule <dataset> <rule-id> --data JSON");
|
|
99
100
|
const data = requiredJsonInput(f, "--data, --data-file, or --stdin is required.");
|
|
100
|
-
if (f
|
|
101
|
+
if (executionMode(f).dryRun) {
|
|
101
102
|
const current = await c.dataQuality.getRule(a[0], a[1], f["project-key"]);
|
|
102
103
|
const next = deepMerge(current, data);
|
|
103
104
|
return {
|
|
@@ -125,11 +126,11 @@ export const dataQualityCommands = {
|
|
|
125
126
|
handler: async (c, a, f) => {
|
|
126
127
|
requireArgs(a, 2, "dss data-quality delete-rule <dataset> <rule-id>");
|
|
127
128
|
const pk = f["project-key"];
|
|
128
|
-
if (f
|
|
129
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
129
130
|
const current = await readIfExists(() => c.dataQuality.getRule(a[0], a[1], pk));
|
|
130
131
|
if (!current)
|
|
131
132
|
return skipResult("data-quality", a[1], "missing", { dataset: a[0], });
|
|
132
|
-
if (f
|
|
133
|
+
if (executionMode(f).dryRun) {
|
|
133
134
|
return {
|
|
134
135
|
dryRun: true,
|
|
135
136
|
action: "delete-rule",
|
|
@@ -248,7 +249,7 @@ export const dataQualityCommands = {
|
|
|
248
249
|
projectKey: pk,
|
|
249
250
|
timeoutMs: num(f["timeout"], "--timeout"),
|
|
250
251
|
};
|
|
251
|
-
if (f
|
|
252
|
+
if (executionMode(f).dryRun) {
|
|
252
253
|
const params = new URLSearchParams();
|
|
253
254
|
params.set("partition", options.partition?.trim() ? options.partition : "NP");
|
|
254
255
|
if (options.ruleId !== undefined)
|
|
@@ -2,6 +2,7 @@ import { buildDatasetCloneSettings, } from "../../resources/datasets.js";
|
|
|
2
2
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
3
3
|
import { compareStrings, stableHash, } from "../../utils/stable-hash.js";
|
|
4
4
|
import { jsonInput, num, schemaColumnsInput, unknownJsonInput, } from "../coerce.js";
|
|
5
|
+
import { executionMode, } from "../flags.js";
|
|
5
6
|
import { datasetSourceSummary, } from "../helpers/dataset.js";
|
|
6
7
|
import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
|
|
7
8
|
import { enqueueCliWarning, readIfExists, skipResult, } from "../output.js";
|
|
@@ -195,7 +196,7 @@ export const datasetCommands = {
|
|
|
195
196
|
requireArgs(a, 1, usage);
|
|
196
197
|
const columns = schemaColumnsInput(f, usage);
|
|
197
198
|
const pk = f["project-key"];
|
|
198
|
-
if (f
|
|
199
|
+
if (executionMode(f).dryRun) {
|
|
199
200
|
const current = await c.datasets.schema(a[0], pk);
|
|
200
201
|
return {
|
|
201
202
|
dryRun: true,
|
|
@@ -314,6 +315,7 @@ export const datasetCommands = {
|
|
|
314
315
|
outputPath: f["output"],
|
|
315
316
|
projectKey: f["project-key"],
|
|
316
317
|
limit: num(f["limit"], "--limit"),
|
|
318
|
+
rawData: f["raw-data"] === true,
|
|
317
319
|
});
|
|
318
320
|
if (result.truncated) {
|
|
319
321
|
enqueueCliWarning({
|
|
@@ -337,9 +339,13 @@ export const datasetCommands = {
|
|
|
337
339
|
}
|
|
338
340
|
return result;
|
|
339
341
|
},
|
|
340
|
-
usage: "dss dataset download <name> [--output PATH] [--limit N] [--project-key KEY]",
|
|
341
|
-
description: "Download up to --limit rows (default 100k) as CSV
|
|
342
|
-
examples: [
|
|
342
|
+
usage: "dss dataset download <name> [--output PATH] [--limit N] [--raw-data] [--project-key KEY]",
|
|
343
|
+
description: "Download up to --limit rows (default 100k) as CSV and return { path, rows, truncated, limit }. Formula-like cells are neutralized for spreadsheets; --raw-data preserves exact bytes. Warnings report truncation and the default output path.",
|
|
344
|
+
examples: [
|
|
345
|
+
"dss dataset download orders",
|
|
346
|
+
"dss dataset download orders --output ./data/",
|
|
347
|
+
"dss dataset download orders --raw-data",
|
|
348
|
+
],
|
|
343
349
|
},
|
|
344
350
|
create: {
|
|
345
351
|
handler: async (c, _a, f) => {
|
|
@@ -360,13 +366,13 @@ export const datasetCommands = {
|
|
|
360
366
|
projectKey: pk,
|
|
361
367
|
};
|
|
362
368
|
const zoneId = await resolveFlowZoneIdFromFlags(c, f, pk);
|
|
363
|
-
if (f["if-not-exists"] === true || f
|
|
369
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
364
370
|
const list = await c.datasets.list(pk);
|
|
365
371
|
const existing = list.find((d) => d.name === name);
|
|
366
|
-
if (existing && f["if-not-exists"] === true && f
|
|
372
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
367
373
|
return skipResult("dataset", name, "exists", { current: existing, });
|
|
368
374
|
}
|
|
369
|
-
if (f
|
|
375
|
+
if (executionMode(f).dryRun) {
|
|
370
376
|
return {
|
|
371
377
|
dryRun: true,
|
|
372
378
|
action: "create",
|
|
@@ -405,7 +411,7 @@ export const datasetCommands = {
|
|
|
405
411
|
const current = await c.datasets.get(a[0], pk);
|
|
406
412
|
const next = buildDatasetCloneSettings(current, a[1], pk ?? c.resolveProjectKey(pk), opts);
|
|
407
413
|
const zoneId = await resolveFlowZoneIdFromFlags(c, f, pk);
|
|
408
|
-
if (f
|
|
414
|
+
if (executionMode(f).dryRun) {
|
|
409
415
|
return {
|
|
410
416
|
dryRun: true,
|
|
411
417
|
action: "clone",
|
|
@@ -432,11 +438,11 @@ export const datasetCommands = {
|
|
|
432
438
|
handler: async (c, a, f) => {
|
|
433
439
|
requireArgs(a, 1, "dss dataset delete <name>");
|
|
434
440
|
const pk = f["project-key"];
|
|
435
|
-
if (f
|
|
441
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
436
442
|
const current = await readIfExists(() => c.datasets.get(a[0], pk));
|
|
437
443
|
if (!current)
|
|
438
444
|
return skipResult("dataset", a[0], "missing");
|
|
439
|
-
if (f
|
|
445
|
+
if (executionMode(f).dryRun) {
|
|
440
446
|
return { dryRun: true, action: "delete", resource: "dataset", name: a[0], current, };
|
|
441
447
|
}
|
|
442
448
|
}
|
|
@@ -455,7 +461,7 @@ export const datasetCommands = {
|
|
|
455
461
|
throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss dataset update <name> [--data '{...}' | --data-file PATH | --stdin]");
|
|
456
462
|
}
|
|
457
463
|
const pk = f["project-key"];
|
|
458
|
-
if (f
|
|
464
|
+
if (executionMode(f).dryRun) {
|
|
459
465
|
const current = await c.datasets.get(a[0], pk);
|
|
460
466
|
const next = deepMerge(current, data);
|
|
461
467
|
return { dryRun: true, action: "update", resource: "dataset", name: a[0], current, next, };
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { runDoctor, } from "../doctor.js";
|
|
2
1
|
export const doctorCommands = {
|
|
3
2
|
run: {
|
|
4
|
-
handler: async (_c, _a, f) =>
|
|
3
|
+
handler: async (_c, _a, f) => {
|
|
4
|
+
const { runDoctor, } = await import("../doctor.js");
|
|
5
|
+
return (await runDoctor(f)).result;
|
|
6
|
+
},
|
|
5
7
|
usage: "dss doctor [--project-key KEY] [--capabilities] [--fast]",
|
|
6
8
|
description: "Run JSON diagnostics for DSS credentials, connectivity, and project access.",
|
|
7
9
|
examples: ["dss doctor", "dss doctor --project-key MYPROJ", "dss doctor --capabilities --fast",],
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { ClientValidationError, } from "../../errors.js";
|
|
1
2
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
2
|
-
import {
|
|
3
|
+
import { executionMode, } from "../flags.js";
|
|
4
|
+
import { ensureFlowZonePlanTarget, findFlowZoneForPlan, flowZoneColor, flowZoneContains, flowZoneCurrentPosition, flowZoneDetailSummary, flowZoneId, flowZoneMoveDelta, flowZoneMoveItems, flowZoneName, flowZoneOrganizeStep, flowZonePlanFromZones, flowZonePlanItemKeys, flowZonePruneItems, flowZoneSamePosition, flowZoneSummary, readFlowZoneOrganizePlan, resolveFlowZoneIdFromFlags, throwFlowZoneValidationError, validateFlowZoneOrganizeObjects, } from "../helpers/flow-zone.js";
|
|
3
5
|
import { readIfExists, skipResult, } from "../output.js";
|
|
4
6
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
7
|
export const flowZoneCommands = {
|
|
@@ -62,13 +64,13 @@ export const flowZoneCommands = {
|
|
|
62
64
|
color: flowZoneColor(f["color"]),
|
|
63
65
|
projectKey: pk,
|
|
64
66
|
};
|
|
65
|
-
if (f["if-not-exists"] === true || f
|
|
67
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
66
68
|
const list = await c.flowZones.list(pk);
|
|
67
69
|
const existing = list.find((zone) => zone.name === name);
|
|
68
|
-
if (existing && f["if-not-exists"] === true && f
|
|
70
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
69
71
|
return skipResult("flow-zone", existing.id, "exists", { current: existing, });
|
|
70
72
|
}
|
|
71
|
-
if (f
|
|
73
|
+
if (executionMode(f).dryRun) {
|
|
72
74
|
return {
|
|
73
75
|
dryRun: true,
|
|
74
76
|
action: "create",
|
|
@@ -102,7 +104,7 @@ export const flowZoneCommands = {
|
|
|
102
104
|
color: flowZoneColor(f["color"]),
|
|
103
105
|
projectKey: pk,
|
|
104
106
|
};
|
|
105
|
-
if (f
|
|
107
|
+
if (executionMode(f).dryRun) {
|
|
106
108
|
const current = await c.flowZones.get(zoneId, pk);
|
|
107
109
|
const next = deepMerge(current, patch);
|
|
108
110
|
return { dryRun: true, action: "update", resource: "flow-zone", id: zoneId, current, next, };
|
|
@@ -117,11 +119,11 @@ export const flowZoneCommands = {
|
|
|
117
119
|
handler: async (c, a, f) => {
|
|
118
120
|
requireArgs(a, 1, "dss flow-zone delete <id>");
|
|
119
121
|
const zoneId = flowZoneId(a[0]);
|
|
120
|
-
if (f
|
|
122
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
121
123
|
const current = await readIfExists(() => c.flowZones.get(zoneId, f["project-key"]));
|
|
122
124
|
if (!current)
|
|
123
125
|
return skipResult("flow-zone", zoneId, "missing");
|
|
124
|
-
if (f
|
|
126
|
+
if (executionMode(f).dryRun) {
|
|
125
127
|
return { dryRun: true, action: "delete", resource: "flow-zone", id: zoneId, current, };
|
|
126
128
|
}
|
|
127
129
|
}
|
|
@@ -146,7 +148,7 @@ export const flowZoneCommands = {
|
|
|
146
148
|
if (items.length === 0) {
|
|
147
149
|
throw new UsageError("At least one object is required. Use --dataset, --recipe, --folder, or --object TYPE:ID.");
|
|
148
150
|
}
|
|
149
|
-
if (f
|
|
151
|
+
if (executionMode(f).dryRun) {
|
|
150
152
|
return {
|
|
151
153
|
dryRun: true,
|
|
152
154
|
action: "move",
|
|
@@ -166,6 +168,19 @@ export const flowZoneCommands = {
|
|
|
166
168
|
"dss flow-zone move ZONE_ID --object SAVED_MODEL:model_id",
|
|
167
169
|
],
|
|
168
170
|
},
|
|
171
|
+
plan: {
|
|
172
|
+
handler: async (c, _a, f) => {
|
|
173
|
+
const pk = f["project-key"];
|
|
174
|
+
const [zones, topologyFingerprint,] = await Promise.all([
|
|
175
|
+
c.flowZones.list(pk),
|
|
176
|
+
c.projects.topologyFingerprint(pk),
|
|
177
|
+
]);
|
|
178
|
+
return flowZonePlanFromZones(zones, topologyFingerprint);
|
|
179
|
+
},
|
|
180
|
+
usage: "dss flow-zone plan [--project-key KEY]",
|
|
181
|
+
description: "Export current visual flow-zone organization as an organize-compatible plan.",
|
|
182
|
+
examples: ["dss flow-zone plan --project-key MYPROJ",],
|
|
183
|
+
},
|
|
169
184
|
organize: {
|
|
170
185
|
handler: async (c, _a, f) => {
|
|
171
186
|
const usage = "dss flow-zone organize (--data JSON|--data-file PATH|--file PATH|--stdin) [--sync] [--validate-objects] [--dry-run] [--project-key KEY]";
|
|
@@ -173,7 +188,13 @@ export const flowZoneCommands = {
|
|
|
173
188
|
const plan = readFlowZoneOrganizePlan(f, usage);
|
|
174
189
|
const sync = f["sync"] === true;
|
|
175
190
|
const validateObjects = f["validate-objects"] === true;
|
|
176
|
-
const zones = await
|
|
191
|
+
const [zones, topologyFingerprint,] = await Promise.all([
|
|
192
|
+
c.flowZones.list(pk),
|
|
193
|
+
c.projects.topologyFingerprint(pk),
|
|
194
|
+
]);
|
|
195
|
+
if (plan.topologyFingerprint && plan.topologyFingerprint !== topologyFingerprint) {
|
|
196
|
+
throw new ClientValidationError("Flow topology changed after this visual organization plan was generated.", "assertion_failed", "Regenerate the plan with dss flow-zone plan, then review it before applying.", { expected: plan.topologyFingerprint, actual: topologyFingerprint, });
|
|
197
|
+
}
|
|
177
198
|
const plannedItemKeys = flowZonePlanItemKeys(plan);
|
|
178
199
|
const planned = plan.zones.map((zonePlan) => flowZoneOrganizeStep(zonePlan, findFlowZoneForPlan(zones, zonePlan), sync, plannedItemKeys));
|
|
179
200
|
const validation = validateObjects
|
|
@@ -186,7 +207,7 @@ export const flowZoneCommands = {
|
|
|
186
207
|
const pruneItems = Array.isArray(step.pruneItems) ? step.pruneItems : [];
|
|
187
208
|
return count + pruneItems.length;
|
|
188
209
|
}, 0);
|
|
189
|
-
if (f
|
|
210
|
+
if (executionMode(f).dryRun) {
|
|
190
211
|
return {
|
|
191
212
|
dryRun: true,
|
|
192
213
|
action: "organize",
|
|
@@ -194,6 +215,7 @@ export const flowZoneCommands = {
|
|
|
194
215
|
projectKey: pk,
|
|
195
216
|
sync,
|
|
196
217
|
validateObjects,
|
|
218
|
+
topologyFingerprint,
|
|
197
219
|
zoneCount: plan.zones.length,
|
|
198
220
|
itemCount,
|
|
199
221
|
pruneItemCount,
|
|
@@ -209,6 +231,7 @@ export const flowZoneCommands = {
|
|
|
209
231
|
for (const zonePlan of plan.zones) {
|
|
210
232
|
let zone = findFlowZoneForPlan(currentZones, zonePlan);
|
|
211
233
|
ensureFlowZonePlanTarget(zonePlan, zone);
|
|
234
|
+
const moveItems = flowZoneMoveDelta(zone, zonePlan.items);
|
|
212
235
|
const pruneItems = sync ? flowZonePruneItems(zone, plannedItemKeys) : [];
|
|
213
236
|
if (!zone) {
|
|
214
237
|
zone = await c.flowZones.create({
|
|
@@ -238,15 +261,19 @@ export const flowZoneCommands = {
|
|
|
238
261
|
updated.push(zone);
|
|
239
262
|
}
|
|
240
263
|
}
|
|
241
|
-
if (
|
|
242
|
-
await c.flowZones.moveItems(zone.id,
|
|
243
|
-
moved.push({ zoneId: zone.id, name: zone.name, items:
|
|
264
|
+
if (moveItems.length > 0) {
|
|
265
|
+
await c.flowZones.moveItems(zone.id, moveItems, pk);
|
|
266
|
+
moved.push({ zoneId: zone.id, name: zone.name, items: moveItems, });
|
|
244
267
|
}
|
|
245
268
|
if (pruneItems.length > 0) {
|
|
246
269
|
await c.flowZones.moveItems("default", pruneItems, pk);
|
|
247
270
|
pruned.push({ zoneId: "default", fromZoneId: zone.id, name: zone.name, items: pruneItems, });
|
|
248
271
|
}
|
|
249
272
|
}
|
|
273
|
+
const topologyFingerprintAfter = await c.projects.topologyFingerprint(pk);
|
|
274
|
+
if (topologyFingerprintAfter !== topologyFingerprint) {
|
|
275
|
+
throw new ClientValidationError("Visual flow-zone changes modified flow topology.", "assertion_failed", "Inspect the flow before applying any further organization changes.", { before: topologyFingerprint, after: topologyFingerprintAfter, });
|
|
276
|
+
}
|
|
250
277
|
return {
|
|
251
278
|
organized: true,
|
|
252
279
|
action: "organize",
|
|
@@ -254,6 +281,8 @@ export const flowZoneCommands = {
|
|
|
254
281
|
projectKey: pk,
|
|
255
282
|
sync,
|
|
256
283
|
validateObjects,
|
|
284
|
+
topologyFingerprint,
|
|
285
|
+
topologyUnchanged: true,
|
|
257
286
|
zoneCount: plan.zones.length,
|
|
258
287
|
itemCount,
|
|
259
288
|
pruneItemCount,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
2
2
|
import { jsonInput, } from "../coerce.js";
|
|
3
|
+
import { executionMode, } from "../flags.js";
|
|
3
4
|
import { addTransientTargetContext, encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
|
|
4
5
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
6
|
async function resolveFolderId(client, nameOrId, flags) {
|
|
@@ -28,13 +29,13 @@ export const folderCommands = {
|
|
|
28
29
|
path: f["path"],
|
|
29
30
|
projectKey: pk,
|
|
30
31
|
};
|
|
31
|
-
if (f["if-not-exists"] === true || f
|
|
32
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
32
33
|
const list = await c.folders.list(pk);
|
|
33
34
|
const existing = list.find((folder) => folder.name === name);
|
|
34
|
-
if (existing && f["if-not-exists"] === true && f
|
|
35
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
35
36
|
return skipResult("folder", existing.id ?? name, "exists", { current: existing, });
|
|
36
37
|
}
|
|
37
|
-
if (f
|
|
38
|
+
if (executionMode(f).dryRun) {
|
|
38
39
|
return {
|
|
39
40
|
dryRun: true,
|
|
40
41
|
action: "create",
|
|
@@ -74,7 +75,7 @@ export const folderCommands = {
|
|
|
74
75
|
}
|
|
75
76
|
const pk = f["project-key"];
|
|
76
77
|
const folderId = await resolveFolderId(c, a[0], f);
|
|
77
|
-
if (f
|
|
78
|
+
if (executionMode(f).dryRun) {
|
|
78
79
|
const current = await c.folders.get(folderId, pk);
|
|
79
80
|
const next = deepMerge(current, data);
|
|
80
81
|
return {
|
|
@@ -102,11 +103,11 @@ export const folderCommands = {
|
|
|
102
103
|
requireArgs(a, 1, "dss folder delete <name-or-id>");
|
|
103
104
|
const pk = f["project-key"];
|
|
104
105
|
const folderId = await resolveFolderId(c, a[0], f);
|
|
105
|
-
if (f
|
|
106
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
106
107
|
const current = await readIfExists(() => c.folders.get(folderId, pk));
|
|
107
108
|
if (!current)
|
|
108
109
|
return skipResult("folder", folderId, "missing");
|
|
109
|
-
if (f
|
|
110
|
+
if (executionMode(f).dryRun) {
|
|
110
111
|
return {
|
|
111
112
|
dryRun: true,
|
|
112
113
|
action: "delete",
|
|
@@ -167,7 +168,7 @@ export const folderCommands = {
|
|
|
167
168
|
requireArgs(a, 3, "dss folder upload <name-or-id> <path> <localPath>");
|
|
168
169
|
const pk = f["project-key"];
|
|
169
170
|
const folderId = await resolveFolderId(c, a[0], f);
|
|
170
|
-
if (f
|
|
171
|
+
if (executionMode(f).dryRun) {
|
|
171
172
|
return {
|
|
172
173
|
dryRun: true,
|
|
173
174
|
action: "upload",
|
|
@@ -190,7 +191,7 @@ export const folderCommands = {
|
|
|
190
191
|
"delete-file": {
|
|
191
192
|
handler: async (c, a, f) => {
|
|
192
193
|
requireArgs(a, 2, "dss folder delete-file <name-or-id> <path>");
|
|
193
|
-
if (f
|
|
194
|
+
if (executionMode(f).dryRun) {
|
|
194
195
|
return { dryRun: true, action: "delete-file", resource: "folder", folder: a[0], path: a[1], };
|
|
195
196
|
}
|
|
196
197
|
await c.folders.deleteFile(await resolveFolderId(c, a[0], f), a[1], f["project-key"]);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { num, } from "../coerce.js";
|
|
2
|
+
import { executionMode, } from "../flags.js";
|
|
2
3
|
import { planResult, } from "../output.js";
|
|
3
4
|
import { requireArgs, } from "../usage.js";
|
|
4
5
|
export const futureCommands = {
|
|
@@ -36,7 +37,7 @@ export const futureCommands = {
|
|
|
36
37
|
handler: async (c, a, f) => {
|
|
37
38
|
requireArgs(a, 1, "dss future abort <id>");
|
|
38
39
|
const id = a[0];
|
|
39
|
-
if (f
|
|
40
|
+
if (executionMode(f).dryRun) {
|
|
40
41
|
return planResult("future", "abort", {
|
|
41
42
|
method: "POST",
|
|
42
43
|
endpoint: `/public/api/futures/${encodeURIComponent(id)}/abort`,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
2
2
|
import { json, jsonInput, parseBooleanOption, textInput, } from "../coerce.js";
|
|
3
|
+
import { executionMode, } from "../flags.js";
|
|
3
4
|
import { readIfExists, skipResult, } from "../output.js";
|
|
4
5
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
6
|
export const insightCommands = {
|
|
@@ -40,17 +41,17 @@ export const insightCommands = {
|
|
|
40
41
|
if (params !== undefined)
|
|
41
42
|
prototype.params = params;
|
|
42
43
|
const pk = f["project-key"];
|
|
43
|
-
if (f["if-not-exists"] === true || f
|
|
44
|
+
if (f["if-not-exists"] === true || executionMode(f).dryRun) {
|
|
44
45
|
const existing = name
|
|
45
46
|
? (await c.insights.list(pk)).find((insight) => insight.name === name)
|
|
46
47
|
: undefined;
|
|
47
|
-
if (existing && f["if-not-exists"] === true && f
|
|
48
|
+
if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
|
|
48
49
|
return skipResult("insight", existing.id, "exists", { current: existing, });
|
|
49
50
|
}
|
|
50
|
-
if (f["if-not-exists"] === true && !name && f
|
|
51
|
+
if (f["if-not-exists"] === true && !name && !executionMode(f).dryRun) {
|
|
51
52
|
throw new UsageError("--if-not-exists requires --name for insight create.");
|
|
52
53
|
}
|
|
53
|
-
if (f
|
|
54
|
+
if (executionMode(f).dryRun) {
|
|
54
55
|
return {
|
|
55
56
|
dryRun: true,
|
|
56
57
|
action: "create",
|
|
@@ -95,7 +96,7 @@ export const insightCommands = {
|
|
|
95
96
|
&& contentType === undefined && payload === undefined) {
|
|
96
97
|
throw new UsageError("--name, --listed, --params, --content, --file, --data, --data-file, or --stdin is required.");
|
|
97
98
|
}
|
|
98
|
-
if (f
|
|
99
|
+
if (executionMode(f).dryRun) {
|
|
99
100
|
const current = await c.insights.get(a[0], f["project-key"]);
|
|
100
101
|
const next = deepMerge(current, data ?? {});
|
|
101
102
|
if (name !== undefined)
|
|
@@ -138,11 +139,11 @@ export const insightCommands = {
|
|
|
138
139
|
handler: async (c, a, f) => {
|
|
139
140
|
requireArgs(a, 1, "dss insight delete <id>");
|
|
140
141
|
const pk = f["project-key"];
|
|
141
|
-
if (f
|
|
142
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
142
143
|
const current = await readIfExists(() => c.insights.get(a[0], pk));
|
|
143
144
|
if (!current)
|
|
144
145
|
return skipResult("insight", a[0], "missing");
|
|
145
|
-
if (f
|
|
146
|
+
if (executionMode(f).dryRun) {
|
|
146
147
|
return { dryRun: true, action: "delete", resource: "insight", id: a[0], current, };
|
|
147
148
|
}
|
|
148
149
|
}
|
|
@@ -2,6 +2,7 @@ import { mkdir, writeFile, } from "node:fs/promises";
|
|
|
2
2
|
import { dirname, resolve, } from "node:path";
|
|
3
3
|
import { parseJobLogProgress, } from "../../resources/jobs.js";
|
|
4
4
|
import { jobBuildTargetTypeFromFlags, jobLogFilterFromFlag, maxLogLinesFromFlags, num, plainRecord, } from "../coerce.js";
|
|
5
|
+
import { executionMode, } from "../flags.js";
|
|
5
6
|
import { encodedProjectEndpoint, } from "../output.js";
|
|
6
7
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
@@ -276,7 +277,7 @@ export const jobCommands = {
|
|
|
276
277
|
targetType: jobBuildTargetTypeFromFlags(f),
|
|
277
278
|
timeoutMs: num(f["timeout"], "--timeout"),
|
|
278
279
|
};
|
|
279
|
-
if (f
|
|
280
|
+
if (executionMode(f).dryRun) {
|
|
280
281
|
return {
|
|
281
282
|
dryRun: true,
|
|
282
283
|
action: "build",
|
|
@@ -315,7 +316,7 @@ export const jobCommands = {
|
|
|
315
316
|
summary: f["summary"] === true,
|
|
316
317
|
targetType: jobBuildTargetTypeFromFlags(f),
|
|
317
318
|
};
|
|
318
|
-
if (f
|
|
319
|
+
if (executionMode(f).dryRun) {
|
|
319
320
|
return {
|
|
320
321
|
dryRun: true,
|
|
321
322
|
action: "build-and-wait",
|
|
@@ -399,7 +400,7 @@ export const jobCommands = {
|
|
|
399
400
|
handler: async (c, a, f) => {
|
|
400
401
|
requireArgs(a, 1, "dss job abort <id>");
|
|
401
402
|
const pk = f["project-key"];
|
|
402
|
-
if (f
|
|
403
|
+
if (executionMode(f).dryRun) {
|
|
403
404
|
return {
|
|
404
405
|
dryRun: true,
|
|
405
406
|
action: "abort",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsonInput, requiredJsonInput, } from "../coerce.js";
|
|
2
|
+
import { executionMode, } from "../flags.js";
|
|
2
3
|
import { isNotFoundError, planResult, skipResult, } from "../output.js";
|
|
3
4
|
import { requireArgs, } from "../usage.js";
|
|
4
5
|
const MEANING_PLAN_FAILURE_CODES = { usage: 1, error: 2, transient: 3, };
|
|
@@ -40,7 +41,7 @@ export const meaningCommands = {
|
|
|
40
41
|
requireArgs(a, 3, "dss meaning create <id> <label> <type> [--data JSON|--data-file PATH|--stdin] [--dry-run]");
|
|
41
42
|
const body = jsonInput(f) ?? {};
|
|
42
43
|
const payload = meaningCreatePayload(a[0], a[1], a[2], body);
|
|
43
|
-
if (f
|
|
44
|
+
if (executionMode(f).dryRun) {
|
|
44
45
|
return planResult("meaning", "create", {
|
|
45
46
|
asyncKind: "none",
|
|
46
47
|
method: "POST",
|
|
@@ -62,7 +63,7 @@ export const meaningCommands = {
|
|
|
62
63
|
handler: async (c, a, f) => {
|
|
63
64
|
requireArgs(a, 1, "dss meaning update <meaningId> (--data JSON|--data-file PATH|--stdin) [--dry-run]");
|
|
64
65
|
const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (meaning definition).");
|
|
65
|
-
if (f
|
|
66
|
+
if (executionMode(f).dryRun) {
|
|
66
67
|
return planResult("meaning", "update", {
|
|
67
68
|
asyncKind: "none",
|
|
68
69
|
method: "PUT",
|
|
@@ -83,7 +84,7 @@ export const meaningCommands = {
|
|
|
83
84
|
delete: {
|
|
84
85
|
handler: async (c, a, f) => {
|
|
85
86
|
requireArgs(a, 1, "dss meaning delete <meaningId>");
|
|
86
|
-
if (f
|
|
87
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
87
88
|
const current = await c.meanings.get(a[0]).catch((error) => {
|
|
88
89
|
if (isNotFoundError(error)
|
|
89
90
|
|| (error instanceof Error && /unknown meaning/i.test(error.message))) {
|
|
@@ -93,7 +94,7 @@ export const meaningCommands = {
|
|
|
93
94
|
});
|
|
94
95
|
if (!current)
|
|
95
96
|
return skipResult("meaning", a[0], "missing");
|
|
96
|
-
if (f
|
|
97
|
+
if (executionMode(f).dryRun) {
|
|
97
98
|
return planResult("meaning", "delete", {
|
|
98
99
|
asyncKind: "none",
|
|
99
100
|
method: "DELETE",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { requiredJsonInput, requiredStringFlag, } from "../coerce.js";
|
|
2
|
+
import { executionMode, } from "../flags.js";
|
|
2
3
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
3
4
|
function optionalStringFlag(flags, name) {
|
|
4
5
|
const value = flags[name];
|
|
@@ -127,7 +128,7 @@ export const mlTaskCommands = {
|
|
|
127
128
|
testDatasetRef: optionalStringFlag(f, "test-dataset"),
|
|
128
129
|
projectKey: f["project-key"],
|
|
129
130
|
};
|
|
130
|
-
if (f
|
|
131
|
+
if (executionMode(f).dryRun) {
|
|
131
132
|
return {
|
|
132
133
|
dryRun: true,
|
|
133
134
|
action: "deploy",
|
|
@@ -148,7 +149,7 @@ export const mlTaskCommands = {
|
|
|
148
149
|
handler: async (c, a, f) => {
|
|
149
150
|
requireArgs(a, 2, "dss ml-task delete <analysisId> <mlTaskId> [--dry-run] [--project-key KEY]");
|
|
150
151
|
const projectKey = f["project-key"];
|
|
151
|
-
if (f
|
|
152
|
+
if (executionMode(f).dryRun) {
|
|
152
153
|
return {
|
|
153
154
|
dryRun: true,
|
|
154
155
|
action: "delete",
|
|
@@ -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 modelEvaluationStoreCommands = {
|
|
@@ -23,7 +24,7 @@ export const modelEvaluationStoreCommands = {
|
|
|
23
24
|
const name = requiredStringFlag(f, "name", usage);
|
|
24
25
|
const projectKey = f["project-key"];
|
|
25
26
|
const options = { name, projectKey, };
|
|
26
|
-
if (f
|
|
27
|
+
if (executionMode(f).dryRun) {
|
|
27
28
|
return {
|
|
28
29
|
dryRun: true,
|
|
29
30
|
action: "create",
|
|
@@ -56,11 +57,11 @@ export const modelEvaluationStoreCommands = {
|
|
|
56
57
|
handler: async (c, a, f) => {
|
|
57
58
|
requireArgs(a, 1, "dss model-evaluation-store delete <storeId> [--if-exists] [--dry-run] [--project-key KEY]");
|
|
58
59
|
const projectKey = f["project-key"];
|
|
59
|
-
if (f
|
|
60
|
+
if (executionMode(f).dryRun || f["if-exists"] === true) {
|
|
60
61
|
const current = await readIfExists(() => c.modelEvaluationStores.get(a[0], projectKey));
|
|
61
62
|
if (!current)
|
|
62
63
|
return skipResult("model-evaluation-store", a[0], "missing");
|
|
63
|
-
if (f
|
|
64
|
+
if (executionMode(f).dryRun) {
|
|
64
65
|
return {
|
|
65
66
|
dryRun: true,
|
|
66
67
|
action: "delete",
|