dataiku-sdk 3.1.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.
Files changed (58) hide show
  1. package/README.md +124 -12
  2. package/dist/build-metadata.json +1 -1
  3. package/dist/src/auth.js +1 -1
  4. package/dist/src/cli/coerce.js +11 -2
  5. package/dist/src/cli/commands/analysis.js +4 -3
  6. package/dist/src/cli/commands/api-service.js +5 -3
  7. package/dist/src/cli/commands/app.js +4 -3
  8. package/dist/src/cli/commands/bundle.js +2 -1
  9. package/dist/src/cli/commands/code-env.js +10 -9
  10. package/dist/src/cli/commands/dashboard.js +7 -6
  11. package/dist/src/cli/commands/data-quality.js +9 -8
  12. package/dist/src/cli/commands/dataset.js +9 -8
  13. package/dist/src/cli/commands/doctor.js +4 -2
  14. package/dist/src/cli/commands/flow-zone.js +9 -8
  15. package/dist/src/cli/commands/folder.js +9 -8
  16. package/dist/src/cli/commands/future.js +2 -1
  17. package/dist/src/cli/commands/insight.js +8 -7
  18. package/dist/src/cli/commands/job.js +4 -3
  19. package/dist/src/cli/commands/meaning.js +5 -4
  20. package/dist/src/cli/commands/ml-task.js +3 -2
  21. package/dist/src/cli/commands/model-evaluation-store.js +4 -3
  22. package/dist/src/cli/commands/notebook.js +11 -10
  23. package/dist/src/cli/commands/project-library.js +7 -6
  24. package/dist/src/cli/commands/project.js +3 -2
  25. package/dist/src/cli/commands/recipe.js +14 -13
  26. package/dist/src/cli/commands/saved-model.js +4 -3
  27. package/dist/src/cli/commands/scenario.js +9 -8
  28. package/dist/src/cli/commands/sql.js +3 -2
  29. package/dist/src/cli/commands/statistics.js +7 -6
  30. package/dist/src/cli/commands/variable.js +2 -1
  31. package/dist/src/cli/commands/webapp.js +2 -1
  32. package/dist/src/cli/commands/wiki.js +7 -6
  33. package/dist/src/cli/contract.d.ts +1 -1
  34. package/dist/src/cli/contract.js +100 -82
  35. package/dist/src/cli/flags.d.ts +4 -0
  36. package/dist/src/cli/flags.js +20 -1
  37. package/dist/src/cli.js +92 -68
  38. package/dist/src/client.d.ts +3 -1
  39. package/dist/src/client.js +78 -21
  40. package/dist/src/errors.js +2 -0
  41. package/dist/src/resources/folders.js +2 -1
  42. package/dist/src/resources/projects.d.ts +0 -2
  43. package/dist/src/resources/projects.js +3 -17
  44. package/dist/src/resources/sql.js +1 -1
  45. package/dist/src/skill.d.ts +13 -4
  46. package/dist/src/skill.js +75 -24
  47. package/dist/src/utils/response-file.d.ts +2 -0
  48. package/dist/src/utils/response-file.js +34 -0
  49. package/package.json +6 -4
  50. package/plugin.json +1 -1
  51. package/skills/dataiku-dss/SKILL.md +44 -224
  52. package/skills/dataiku-dss/references/app-releases.md +27 -0
  53. package/skills/dataiku-dss/references/authentication.md +39 -0
  54. package/skills/dataiku-dss/references/coding.md +8 -0
  55. package/skills/dataiku-dss/references/discovery.md +30 -0
  56. package/skills/dataiku-dss/references/flow-maps.md +12 -0
  57. package/skills/dataiku-dss/references/mutations.md +33 -0
  58. package/skills/dataiku-dss/references/troubleshooting.md +29 -0
@@ -1,5 +1,6 @@
1
1
  import { ClientValidationError, } from "../../errors.js";
2
2
  import { deepMerge, } from "../../utils/deep-merge.js";
3
+ import { executionMode, } from "../flags.js";
3
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";
4
5
  import { readIfExists, skipResult, } from "../output.js";
5
6
  import { requireArgs, UsageError, } from "../usage.js";
@@ -63,13 +64,13 @@ export const flowZoneCommands = {
63
64
  color: flowZoneColor(f["color"]),
64
65
  projectKey: pk,
65
66
  };
66
- if (f["if-not-exists"] === true || f["dry-run"] === true) {
67
+ if (f["if-not-exists"] === true || executionMode(f).dryRun) {
67
68
  const list = await c.flowZones.list(pk);
68
69
  const existing = list.find((zone) => zone.name === name);
69
- if (existing && f["if-not-exists"] === true && f["dry-run"] !== true) {
70
+ if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
70
71
  return skipResult("flow-zone", existing.id, "exists", { current: existing, });
71
72
  }
72
- if (f["dry-run"] === true) {
73
+ if (executionMode(f).dryRun) {
73
74
  return {
74
75
  dryRun: true,
75
76
  action: "create",
@@ -103,7 +104,7 @@ export const flowZoneCommands = {
103
104
  color: flowZoneColor(f["color"]),
104
105
  projectKey: pk,
105
106
  };
106
- if (f["dry-run"] === true) {
107
+ if (executionMode(f).dryRun) {
107
108
  const current = await c.flowZones.get(zoneId, pk);
108
109
  const next = deepMerge(current, patch);
109
110
  return { dryRun: true, action: "update", resource: "flow-zone", id: zoneId, current, next, };
@@ -118,11 +119,11 @@ export const flowZoneCommands = {
118
119
  handler: async (c, a, f) => {
119
120
  requireArgs(a, 1, "dss flow-zone delete <id>");
120
121
  const zoneId = flowZoneId(a[0]);
121
- if (f["dry-run"] === true || f["if-exists"] === true) {
122
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
122
123
  const current = await readIfExists(() => c.flowZones.get(zoneId, f["project-key"]));
123
124
  if (!current)
124
125
  return skipResult("flow-zone", zoneId, "missing");
125
- if (f["dry-run"] === true) {
126
+ if (executionMode(f).dryRun) {
126
127
  return { dryRun: true, action: "delete", resource: "flow-zone", id: zoneId, current, };
127
128
  }
128
129
  }
@@ -147,7 +148,7 @@ export const flowZoneCommands = {
147
148
  if (items.length === 0) {
148
149
  throw new UsageError("At least one object is required. Use --dataset, --recipe, --folder, or --object TYPE:ID.");
149
150
  }
150
- if (f["dry-run"] === true) {
151
+ if (executionMode(f).dryRun) {
151
152
  return {
152
153
  dryRun: true,
153
154
  action: "move",
@@ -206,7 +207,7 @@ export const flowZoneCommands = {
206
207
  const pruneItems = Array.isArray(step.pruneItems) ? step.pruneItems : [];
207
208
  return count + pruneItems.length;
208
209
  }, 0);
209
- if (f["dry-run"] === true) {
210
+ if (executionMode(f).dryRun) {
210
211
  return {
211
212
  dryRun: true,
212
213
  action: "organize",
@@ -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["dry-run"] === true) {
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["dry-run"] !== true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true || f["if-exists"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] !== true) {
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["dry-run"] !== true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true || f["if-exists"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true || f["if-exists"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true) {
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["dry-run"] === true || f["if-exists"] === true) {
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["dry-run"] === true) {
64
+ if (executionMode(f).dryRun) {
64
65
  return {
65
66
  dryRun: true,
66
67
  action: "delete",
@@ -1,4 +1,5 @@
1
1
  import { jsonInput, num, parseBooleanOption, } from "../coerce.js";
2
+ import { executionMode, } from "../flags.js";
2
3
  import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
3
4
  import { requireArgs, requireNoArgs, UsageError, } from "../usage.js";
4
5
  export const notebookCommands = {
@@ -26,11 +27,11 @@ export const notebookCommands = {
26
27
  handler: async (c, a, f) => {
27
28
  requireArgs(a, 1, "dss notebook delete-jupyter <name>");
28
29
  const pk = f["project-key"];
29
- if (f["dry-run"] === true || f["if-exists"] === true) {
30
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
30
31
  const current = await readIfExists(() => c.notebooks.getJupyter(a[0], pk));
31
32
  if (!current)
32
33
  return skipResult("jupyter-notebook", a[0], "missing");
33
- if (f["dry-run"] === true) {
34
+ if (executionMode(f).dryRun) {
34
35
  return { dryRun: true, action: "delete", resource: "jupyter-notebook", name: a[0], current, };
35
36
  }
36
37
  }
@@ -45,7 +46,7 @@ export const notebookCommands = {
45
46
  handler: async (c, a, f) => {
46
47
  requireArgs(a, 1, "dss notebook clear-jupyter-outputs <name>");
47
48
  const pk = f["project-key"];
48
- if (f["dry-run"] === true) {
49
+ if (executionMode(f).dryRun) {
49
50
  // The real mutation is a single server-side DELETE of the
50
51
  // outputs endpoint, so the dry run can only report the current
51
52
  // content (read-only) and the exact request that would run; the
@@ -82,7 +83,7 @@ export const notebookCommands = {
82
83
  const pk = f["project-key"];
83
84
  if (f["all"] === true) {
84
85
  requireNoArgs(a, "dss notebook unload-jupyter --all [--dry-run] [--project-key KEY]");
85
- if (f["dry-run"] === true) {
86
+ if (executionMode(f).dryRun) {
86
87
  const planned = [];
87
88
  for (const notebook of await c.notebooks.listJupyter(pk, { active: true, })) {
88
89
  for (const session of await c.notebooks.listJupyterSessions(notebook.name, pk)) {
@@ -106,7 +107,7 @@ export const notebookCommands = {
106
107
  return { unloaded, resource: "jupyter-notebook", all: true, };
107
108
  }
108
109
  requireArgs(a, 2, "dss notebook unload-jupyter <name> <sessionId>");
109
- if (f["dry-run"] === true) {
110
+ if (executionMode(f).dryRun) {
110
111
  const sessions = await c.notebooks.listJupyterSessions(a[0], pk);
111
112
  const current = sessions.find((session) => session.sessionId === a[1]);
112
113
  return {
@@ -149,11 +150,11 @@ export const notebookCommands = {
149
150
  handler: async (c, a, f) => {
150
151
  requireArgs(a, 1, "dss notebook delete-sql <id>");
151
152
  const pk = f["project-key"];
152
- if (f["dry-run"] === true || f["if-exists"] === true) {
153
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
153
154
  const current = await readIfExists(() => c.notebooks.getSql(a[0], pk));
154
155
  if (!current)
155
156
  return skipResult("sql-notebook", a[0], "missing");
156
- if (f["dry-run"] === true) {
157
+ if (executionMode(f).dryRun) {
157
158
  return { dryRun: true, action: "delete", resource: "sql-notebook", id: a[0], current, };
158
159
  }
159
160
  }
@@ -181,7 +182,7 @@ export const notebookCommands = {
181
182
  throw new UsageError("--data, --data-file, or --stdin is required (notebook JSON content).");
182
183
  }
183
184
  const pk = f["project-key"];
184
- if (f["dry-run"] === true) {
185
+ if (executionMode(f).dryRun) {
185
186
  const current = await readIfExists(() => c.notebooks.getJupyter(a[0], pk));
186
187
  return {
187
188
  dryRun: true,
@@ -217,7 +218,7 @@ export const notebookCommands = {
217
218
  throw new UsageError("--data, --data-file, or --stdin is required (SQL notebook content JSON).");
218
219
  }
219
220
  const pk = f["project-key"];
220
- if (f["dry-run"] === true) {
221
+ if (executionMode(f).dryRun) {
221
222
  const current = await readIfExists(() => c.notebooks.getSql(a[0], pk));
222
223
  return {
223
224
  dryRun: true,
@@ -251,7 +252,7 @@ export const notebookCommands = {
251
252
  numRunsToRetain: num(f["retain"], "--retain"),
252
253
  projectKey: pk,
253
254
  };
254
- if (f["dry-run"] === true) {
255
+ if (executionMode(f).dryRun) {
255
256
  const current = await c.notebooks.getSqlHistory(a[0], pk);
256
257
  return {
257
258
  dryRun: true,
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import { encodeLibraryPath, EXPECT_SHA256_PATTERN, validateLibraryDestinationPath, validateLibraryName, validateLibraryPath, } from "../../resources/project-library.js";
3
3
  import { numFlag, readStdinText, sha256Hex, } from "../coerce.js";
4
+ import { executionMode, } from "../flags.js";
4
5
  import { encodedProjectEndpoint, planResult, skipResult, } from "../output.js";
5
6
  import { requireArgs, UsageError, } from "../usage.js";
6
7
  const PROJECT_LIBRARY_EXIT_CODES = { usage: 1, error: 2, transient: 3, };
@@ -100,7 +101,7 @@ export const projectLibraryCommands = {
100
101
  requireArgs(a, 1, "dss project-library create-file <path> [--if-not-exists] [--dry-run] [--project-key KEY]");
101
102
  const pk = f["project-key"];
102
103
  validateLibraryPath(a[0]);
103
- if (f["dry-run"] === true) {
104
+ if (executionMode(f).dryRun) {
104
105
  return projectLibraryPlan("create-file", {
105
106
  method: "POST",
106
107
  endpoint: projectLibraryContentsEndpoint(c, pk, a[0]),
@@ -126,7 +127,7 @@ export const projectLibraryCommands = {
126
127
  requireArgs(a, 1, "dss project-library create-folder <path> [--if-not-exists] [--dry-run] [--project-key KEY]");
127
128
  const pk = f["project-key"];
128
129
  validateLibraryPath(a[0]);
129
- if (f["dry-run"] === true) {
130
+ if (executionMode(f).dryRun) {
130
131
  return projectLibraryPlan("create-folder", {
131
132
  method: "POST",
132
133
  endpoint: projectLibraryFolderEndpoint(c, pk, a[0]),
@@ -153,7 +154,7 @@ export const projectLibraryCommands = {
153
154
  const pk = f["project-key"];
154
155
  const expectSha256 = expectSha256FromFlags(f);
155
156
  validateLibraryPath(a[0]);
156
- if (f["dry-run"] === true) {
157
+ if (executionMode(f).dryRun) {
157
158
  return projectLibraryPlan("put", {
158
159
  method: "POST",
159
160
  endpoint: projectLibraryContentsEndpoint(c, pk, a[0]),
@@ -206,7 +207,7 @@ export const projectLibraryCommands = {
206
207
  requireArgs(a, 1, "dss project-library delete <path> [--dry-run] [--project-key KEY]");
207
208
  const pk = f["project-key"];
208
209
  validateLibraryPath(a[0]);
209
- if (f["dry-run"] === true) {
210
+ if (executionMode(f).dryRun) {
210
211
  return projectLibraryPlan("delete", {
211
212
  method: "DELETE",
212
213
  endpoint: projectLibraryContentsEndpoint(c, pk, a[0]),
@@ -226,7 +227,7 @@ export const projectLibraryCommands = {
226
227
  const pk = f["project-key"];
227
228
  const validPath = validateLibraryPath(a[0]);
228
229
  const validName = validateLibraryName(a[1]);
229
- if (f["dry-run"] === true) {
230
+ if (executionMode(f).dryRun) {
230
231
  return projectLibraryPlan("rename", {
231
232
  method: "POST",
232
233
  endpoint: projectLibraryActionEndpoint(c, pk, "rename/"),
@@ -247,7 +248,7 @@ export const projectLibraryCommands = {
247
248
  const pk = f["project-key"];
248
249
  const validPath = validateLibraryPath(a[0]);
249
250
  const destination = validateLibraryDestinationPath(a[1]);
250
- if (f["dry-run"] === true) {
251
+ if (executionMode(f).dryRun) {
251
252
  return projectLibraryPlan("move", {
252
253
  method: "POST",
253
254
  endpoint: projectLibraryActionEndpoint(c, pk, "move"),
@@ -3,6 +3,7 @@ import { deepMerge, } from "../../utils/deep-merge.js";
3
3
  import { inspectProjectArchive, } from "../../utils/project-archive.js";
4
4
  import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
5
5
  import { jsonInput, num, requiredJsonInput, requiredStringFlag, } from "../coerce.js";
6
+ import { executionMode, } from "../flags.js";
6
7
  import { CommandResultFailure, } from "../output.js";
7
8
  import { requireArgs, UsageError, } from "../usage.js";
8
9
  const INSPECT_ARCHIVE_USAGE = "dss project inspect-archive <file>";
@@ -91,7 +92,7 @@ export const projectCommands = {
91
92
  throw new UsageError("--expect-project-incarnation must be a 64-character lowercase SHA-256 hash.", "validation_failed");
92
93
  }
93
94
  const guarded = f["if-exists"] === true
94
- || f["dry-run"] === true
95
+ || executionMode(f).dryRun
95
96
  || expectedIncarnation !== undefined;
96
97
  if (!guarded) {
97
98
  // Purely user-requested delete without any guard flag: keep the
@@ -128,7 +129,7 @@ export const projectCommands = {
128
129
  }
129
130
  throw error;
130
131
  }
131
- if (f["dry-run"] === true) {
132
+ if (executionMode(f).dryRun) {
132
133
  return {
133
134
  deleted: false,
134
135
  dryRun: true,
@@ -3,6 +3,7 @@ import { writeFile, } from "node:fs/promises";
3
3
  import { join, } from "node:path";
4
4
  import { deepMerge, } from "../../utils/deep-merge.js";
5
5
  import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
6
+ import { executionMode, } from "../flags.js";
6
7
  import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
7
8
  import { ensureRecipeBackupDir, readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, writeRecipeBackup, } from "../helpers/recipe.js";
8
9
  import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
@@ -109,7 +110,7 @@ export const recipeCommands = {
109
110
  summary: f["summary"] === true,
110
111
  wait,
111
112
  };
112
- if (f["dry-run"] === true) {
113
+ if (executionMode(f).dryRun) {
113
114
  const outputs = await c.recipes.resolveRunOutputs(a[0], {
114
115
  partition: options.partition,
115
116
  projectKey: pk,
@@ -139,11 +140,11 @@ export const recipeCommands = {
139
140
  handler: async (c, a, f) => {
140
141
  requireArgs(a, 1, "dss recipe delete <name>");
141
142
  const pk = f["project-key"];
142
- if (f["dry-run"] === true || f["if-exists"] === true) {
143
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
143
144
  const current = await readIfExists(() => c.recipes.get(a[0], { projectKey: pk, includePayload: true, }));
144
145
  if (!current)
145
146
  return skipResult("recipe", a[0], "missing");
146
- if (f["dry-run"] === true) {
147
+ if (executionMode(f).dryRun) {
147
148
  return { dryRun: true, action: "delete", resource: "recipe", name: a[0], current, };
148
149
  }
149
150
  }
@@ -241,13 +242,13 @@ export const recipeCommands = {
241
242
  const zoneMove = zoneId && name
242
243
  ? [{ objectId: name, objectType: "RECIPE", },]
243
244
  : undefined;
244
- if ((f["if-not-exists"] === true || f["dry-run"] === true) && name) {
245
+ if ((f["if-not-exists"] === true || executionMode(f).dryRun) && name) {
245
246
  const list = await c.recipes.list(pk);
246
247
  const existing = list.find((r) => r.name === name);
247
- if (existing && f["if-not-exists"] === true && f["dry-run"] !== true) {
248
+ if (existing && f["if-not-exists"] === true && !executionMode(f).dryRun) {
248
249
  return skipResult("recipe", name, "exists", { current: existing, });
249
250
  }
250
- if (f["dry-run"] === true) {
251
+ if (executionMode(f).dryRun) {
251
252
  return {
252
253
  dryRun: true,
253
254
  action: "create",
@@ -259,7 +260,7 @@ export const recipeCommands = {
259
260
  };
260
261
  }
261
262
  }
262
- if (f["dry-run"] === true) {
263
+ if (executionMode(f).dryRun) {
263
264
  return {
264
265
  dryRun: true,
265
266
  action: "create",
@@ -328,7 +329,7 @@ export const recipeCommands = {
328
329
  throw new UsageError("Cannot reuse --path or --metastore-table for multiple cloned output datasets.", "invalid_enum");
329
330
  }
330
331
  const zoneId = await resolveFlowZoneIdFromFlags(c, f, pk);
331
- if (f["dry-run"] === true) {
332
+ if (executionMode(f).dryRun) {
332
333
  return {
333
334
  dryRun: true,
334
335
  action: "clone",
@@ -391,7 +392,7 @@ export const recipeCommands = {
391
392
  throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin]");
392
393
  }
393
394
  const pk = f["project-key"];
394
- if (f["dry-run"] === true) {
395
+ if (executionMode(f).dryRun) {
395
396
  const current = await c.recipes.get(a[0], { projectKey: pk, includePayload: true, });
396
397
  const currentRecipe = current.recipe;
397
398
  const next = {
@@ -430,7 +431,7 @@ export const recipeCommands = {
430
431
  }
431
432
  const nextItems = [...items, { ref: a[1], deps: [], },];
432
433
  const inputs = nextItems.map(recipeInputItemRef).filter((ref) => Boolean(ref));
433
- if (f["dry-run"] === true) {
434
+ if (executionMode(f).dryRun) {
434
435
  return {
435
436
  dryRun: true,
436
437
  action: "add-input",
@@ -467,7 +468,7 @@ export const recipeCommands = {
467
468
  }
468
469
  const nextItems = items.filter((item) => recipeInputItemRef(item) !== a[1]);
469
470
  const inputs = nextItems.map(recipeInputItemRef).filter((ref) => Boolean(ref));
470
- if (f["dry-run"] === true) {
471
+ if (executionMode(f).dryRun) {
471
472
  return {
472
473
  dryRun: true,
473
474
  action: "remove-input",
@@ -542,7 +543,7 @@ export const recipeCommands = {
542
543
  projectKey: pk,
543
544
  includePayload: true,
544
545
  });
545
- if (f["dry-run"] === true) {
546
+ if (executionMode(f).dryRun) {
546
547
  return {
547
548
  dryRun: true,
548
549
  action: "set-payload",
@@ -593,7 +594,7 @@ export const recipeCommands = {
593
594
  const next = f["payload-only"] === true || !restoredRecipe
594
595
  ? { ...current, payload, }
595
596
  : { ...current, recipe: restoredRecipe, payload, };
596
- if (f["dry-run"] === true) {
597
+ if (executionMode(f).dryRun) {
597
598
  return {
598
599
  dryRun: true,
599
600
  action: "restore",
@@ -1,3 +1,4 @@
1
+ import { executionMode, } from "../flags.js";
1
2
  import { readIfExists, skipResult, } from "../output.js";
2
3
  import { requireArgs, } from "../usage.js";
3
4
  export const savedModelCommands = {
@@ -40,7 +41,7 @@ export const savedModelCommands = {
40
41
  handler: async (c, a, f) => {
41
42
  requireArgs(a, 2, "dss saved-model set-active <modelId> <versionId> [--dry-run] [--project-key KEY]");
42
43
  const projectKey = f["project-key"];
43
- if (f["dry-run"] === true) {
44
+ if (executionMode(f).dryRun) {
44
45
  return {
45
46
  dryRun: true,
46
47
  action: "set-active",
@@ -64,11 +65,11 @@ export const savedModelCommands = {
64
65
  handler: async (c, a, f) => {
65
66
  requireArgs(a, 1, "dss saved-model delete <modelId> [--if-exists] [--dry-run] [--project-key KEY]");
66
67
  const projectKey = f["project-key"];
67
- if (f["dry-run"] === true || f["if-exists"] === true) {
68
+ if (executionMode(f).dryRun || f["if-exists"] === true) {
68
69
  const current = await readIfExists(() => c.savedModels.get(a[0], projectKey));
69
70
  if (!current)
70
71
  return skipResult("saved-model", a[0], "missing");
71
- if (f["dry-run"] === true) {
72
+ if (executionMode(f).dryRun) {
72
73
  return { dryRun: true, action: "delete", resource: "saved-model", id: a[0], current, };
73
74
  }
74
75
  }