dataiku-sdk 3.4.0 → 3.6.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 (131) hide show
  1. package/README.md +4 -4
  2. package/bin/dss.js +28 -2
  3. package/dist/build-metadata.json +1 -1
  4. package/dist/src/cli/coerce.d.ts +0 -1
  5. package/dist/src/cli/coerce.js +0 -5
  6. package/dist/src/cli/command-syntax.json +14524 -0
  7. package/dist/src/cli/commands/analysis.js +6 -9
  8. package/dist/src/cli/commands/api-deployer.js +15 -33
  9. package/dist/src/cli/commands/api-service.js +17 -27
  10. package/dist/src/cli/commands/app.js +25 -41
  11. package/dist/src/cli/commands/bundle.js +15 -24
  12. package/dist/src/cli/commands/business-app.js +10 -17
  13. package/dist/src/cli/commands/code-env.js +23 -36
  14. package/dist/src/cli/commands/code.js +4 -4
  15. package/dist/src/cli/commands/connection.js +12 -22
  16. package/dist/src/cli/commands/continuous-activity.js +6 -9
  17. package/dist/src/cli/commands/dashboard.js +8 -13
  18. package/dist/src/cli/commands/data-collection.js +9 -16
  19. package/dist/src/cli/commands/data-quality.js +14 -26
  20. package/dist/src/cli/commands/dataset.js +29 -54
  21. package/dist/src/cli/commands/discussion.js +7 -10
  22. package/dist/src/cli/commands/doctor.js +3 -3
  23. package/dist/src/cli/commands/flow-zone.js +8 -17
  24. package/dist/src/cli/commands/folder.js +12 -20
  25. package/dist/src/cli/commands/future.js +7 -10
  26. package/dist/src/cli/commands/group.js +7 -11
  27. package/dist/src/cli/commands/insight.js +7 -11
  28. package/dist/src/cli/commands/job.js +17 -26
  29. package/dist/src/cli/commands/knowledge-bank.js +5 -6
  30. package/dist/src/cli/commands/llm.js +5 -8
  31. package/dist/src/cli/commands/macro.js +9 -16
  32. package/dist/src/cli/commands/meaning.js +7 -11
  33. package/dist/src/cli/commands/metrics.js +7 -10
  34. package/dist/src/cli/commands/ml-task.js +12 -20
  35. package/dist/src/cli/commands/model-evaluation-store.js +7 -11
  36. package/dist/src/cli/commands/notebook.js +15 -27
  37. package/dist/src/cli/commands/plugin.js +35 -68
  38. package/dist/src/cli/commands/project-deployer.js +10 -22
  39. package/dist/src/cli/commands/project-folder.js +10 -17
  40. package/dist/src/cli/commands/project-git.js +38 -70
  41. package/dist/src/cli/commands/project-library.js +15 -23
  42. package/dist/src/cli/commands/project.js +9 -26
  43. package/dist/src/cli/commands/recipe.d.ts +16 -0
  44. package/dist/src/cli/commands/recipe.js +83 -94
  45. package/dist/src/cli/commands/saved-model.js +23 -39
  46. package/dist/src/cli/commands/scenario.js +20 -34
  47. package/dist/src/cli/commands/sql.js +4 -4
  48. package/dist/src/cli/commands/statistics.js +11 -18
  49. package/dist/src/cli/commands/streaming-endpoint.js +7 -11
  50. package/dist/src/cli/commands/user.js +11 -22
  51. package/dist/src/cli/commands/variable.js +3 -4
  52. package/dist/src/cli/commands/webapp.js +10 -16
  53. package/dist/src/cli/commands/wiki.js +9 -13
  54. package/dist/src/cli/commands/workspace.js +8 -14
  55. package/dist/src/cli/contract.d.ts +5 -15
  56. package/dist/src/cli/contract.js +157 -254
  57. package/dist/src/cli/doctor.js +1 -0
  58. package/dist/src/cli/env.d.ts +2 -0
  59. package/dist/src/cli/env.js +16 -1
  60. package/dist/src/cli/flags.d.ts +1 -0
  61. package/dist/src/cli/flags.js +4 -9
  62. package/dist/src/cli/helpers/app-permissions.js +7 -5
  63. package/dist/src/cli/helpers/flow-zone.js +8 -6
  64. package/dist/src/cli/helpers/recipe.js +2 -1
  65. package/dist/src/cli/syntax.d.ts +83 -0
  66. package/dist/src/cli/syntax.js +217 -0
  67. package/dist/src/cli/types.d.ts +3 -0
  68. package/dist/src/cli/usage.d.ts +1 -1
  69. package/dist/src/cli/usage.js +4 -2
  70. package/dist/src/cli/version.d.ts +0 -2
  71. package/dist/src/cli/version.js +6 -7
  72. package/dist/src/cli.js +132 -274
  73. package/dist/src/client.d.ts +2 -1
  74. package/dist/src/client.js +32 -17
  75. package/dist/src/errors.d.ts +12 -1
  76. package/dist/src/errors.js +28 -3
  77. package/dist/src/index.d.ts +1 -3
  78. package/dist/src/index.js +2 -2
  79. package/dist/src/resources/api-services.js +2 -3
  80. package/dist/src/resources/applications.js +2 -4
  81. package/dist/src/resources/base.d.ts +2 -0
  82. package/dist/src/resources/base.js +14 -2
  83. package/dist/src/resources/code-envs.js +3 -5
  84. package/dist/src/resources/data-quality.js +14 -13
  85. package/dist/src/resources/datasets.d.ts +13 -0
  86. package/dist/src/resources/datasets.js +5 -4
  87. package/dist/src/resources/flow-zones.js +4 -2
  88. package/dist/src/resources/folders.js +4 -2
  89. package/dist/src/resources/futures.js +1 -3
  90. package/dist/src/resources/insights.js +3 -2
  91. package/dist/src/resources/jobs.js +4 -6
  92. package/dist/src/resources/macros.d.ts +1 -1
  93. package/dist/src/resources/macros.js +3 -7
  94. package/dist/src/resources/notebooks.js +2 -3
  95. package/dist/src/resources/project-git.js +9 -11
  96. package/dist/src/resources/project-library.d.ts +0 -2
  97. package/dist/src/resources/project-library.js +2 -7
  98. package/dist/src/resources/projects.js +4 -3
  99. package/dist/src/resources/recipes.d.ts +28 -0
  100. package/dist/src/resources/recipes.js +117 -89
  101. package/dist/src/resources/saved-models.js +1 -13
  102. package/dist/src/resources/scenarios.js +1 -3
  103. package/dist/src/resources/sql.js +9 -8
  104. package/dist/src/resources/statistics.js +3 -4
  105. package/dist/src/resources/variables.js +2 -1
  106. package/dist/src/resources/webapps.js +2 -3
  107. package/dist/src/schemas.d.ts +1 -2
  108. package/dist/src/schemas.js +1 -1
  109. package/dist/src/skill.js +6 -7
  110. package/dist/src/utils/cleanup-ledger.js +9 -8
  111. package/dist/src/utils/deep-merge.js +2 -4
  112. package/dist/src/utils/flow-map.js +1 -6
  113. package/dist/src/utils/polling.d.ts +3 -0
  114. package/dist/src/utils/polling.js +3 -0
  115. package/dist/src/utils/project-archive.js +10 -14
  116. package/dist/src/utils/project-incarnation.js +3 -7
  117. package/dist/src/utils/records.d.ts +4 -0
  118. package/dist/src/utils/records.js +8 -0
  119. package/dist/src/utils/stable-hash.d.ts +3 -1
  120. package/dist/src/utils/stable-hash.js +2 -0
  121. package/package.json +10 -14
  122. package/plugin.json +1 -1
  123. package/skills/dataiku-dss/SKILL.md +3 -4
  124. package/skills/dataiku-dss/references/authentication.md +3 -3
  125. package/skills/dataiku-dss/references/coding.md +1 -1
  126. package/skills/dataiku-dss/references/discovery.md +1 -1
  127. package/skills/dataiku-dss/references/mutations.md +0 -1
  128. package/dist/src/utils/pagination.d.ts +0 -8
  129. package/dist/src/utils/pagination.js +0 -23
  130. package/packages/types/dist/index.d.ts +0 -1062
  131. package/packages/types/dist/index.js +0 -871
@@ -5,8 +5,9 @@ import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
5
5
  import { jsonInput, num, requiredJsonInput, requiredStringFlag, } from "../coerce.js";
6
6
  import { executionMode, } from "../flags.js";
7
7
  import { CommandResultFailure, } from "../output.js";
8
+ import { commandUsage, withUsage, } from "../syntax.js";
8
9
  import { requireArgs, UsageError, } from "../usage.js";
9
- const INSPECT_ARCHIVE_USAGE = "dss project inspect-archive <file>";
10
+ const INSPECT_ARCHIVE_USAGE = commandUsage("project", "inspect-archive");
10
11
  function flowRenderFormat(value) {
11
12
  if (value === undefined)
12
13
  return undefined;
@@ -19,28 +20,24 @@ function inspectArchiveCommand(args) {
19
20
  requireArgs(args, 1, INSPECT_ARCHIVE_USAGE);
20
21
  return inspectProjectArchive(args[0]);
21
22
  }
22
- export const projectCommands = {
23
+ export const projectCommands = withUsage("project", {
23
24
  list: {
24
25
  handler: (c) => c.projects.list(),
25
- usage: "dss project list",
26
26
  description: "List all accessible projects.",
27
27
  examples: ["dss project list",],
28
28
  },
29
29
  get: {
30
30
  handler: (c, _a, f) => c.projects.get(f["project-key"]),
31
- usage: "dss project get [--project-key KEY]",
32
31
  description: "Get project settings and metadata.",
33
32
  examples: ["dss project get", "dss project get --project-key MYPROJ",],
34
33
  },
35
34
  metadata: {
36
35
  handler: (c, _a, f) => c.projects.metadata(f["project-key"]),
37
- usage: "dss project metadata [--project-key KEY]",
38
36
  description: "Get project-level metadata (tags, labels, custom fields).",
39
37
  examples: ["dss project metadata", "dss project metadata --project-key MYPROJ",],
40
38
  },
41
39
  flow: {
42
40
  handler: (c, _a, f) => c.projects.flow(f["project-key"]),
43
- usage: "dss project flow [--project-key KEY]",
44
41
  description: "Get the raw flow graph (all datasets, recipes, and edges).",
45
42
  examples: ["dss project flow", "dss project flow --project-key MYPROJ",],
46
43
  },
@@ -57,7 +54,6 @@ export const projectCommands = {
57
54
  num(f["max-nodes"], "--max-nodes");
58
55
  num(f["max-edges"], "--max-edges");
59
56
  },
60
- usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw] [--render ascii|mermaid] [--project-key KEY]",
61
57
  description: "Get an analyzed flow map with zones, layers, components, and diagnostics.",
62
58
  examples: [
63
59
  "dss project map",
@@ -68,20 +64,19 @@ export const projectCommands = {
68
64
  },
69
65
  create: {
70
66
  handler: (c, a, f) => {
71
- const usage = "dss project create <projectKey> <name> --owner LOGIN [--data JSON|--data-file PATH|--stdin]";
67
+ const usage = commandUsage("project", "create");
72
68
  requireArgs(a, 2, usage);
73
69
  const owner = f["owner"];
74
70
  if (!owner)
75
71
  throw new UsageError(`--owner is required. Usage: ${usage}`);
76
72
  return c.projects.createProject(a[0], a[1], owner, jsonInput(f));
77
73
  },
78
- usage: "dss project create <projectKey> <name> --owner LOGIN [--data JSON|--data-file PATH|--stdin]",
79
74
  description: "Create a new project with owner login and optional settings.",
80
75
  examples: ["dss project create MY_PROJ MyProject --owner alice",],
81
76
  },
82
77
  delete: {
83
78
  handler: async (c, a, f) => {
84
- const usage = "dss project delete <projectKey> [--drop-data] [--if-exists] [--dry-run] [--expect-project-incarnation HASH]";
79
+ const usage = commandUsage("project", "delete");
85
80
  requireArgs(a, 1, usage);
86
81
  const projectKey = a[0];
87
82
  const expectedIncarnation = f["expect-project-incarnation"] === undefined
@@ -148,35 +143,32 @@ export const projectCommands = {
148
143
  : {}),
149
144
  };
150
145
  },
151
- usage: "dss project delete <projectKey> [--drop-data] [--if-exists] [--dry-run] [--expect-project-incarnation HASH]",
152
146
  description: "Delete a project (destructive). --drop-data also clears managed datasets; --if-exists treats an absent project as already deleted; --dry-run verifies without deleting; --expect-project-incarnation HASH refuses deletion unless the project still matches the recorded incarnation.",
153
147
  examples: ["dss project delete MY_PROJ",],
154
148
  },
155
149
  duplicate: {
156
150
  handler: (c, a, f) => {
157
- requireArgs(a, 3, "dss project duplicate <sourceKey> <targetKey> <targetName> [--data JSON|--data-file PATH|--stdin]");
151
+ requireArgs(a, 3, commandUsage("project", "duplicate"));
158
152
  return c.projects.duplicate(a[0], a[1], a[2], jsonInput(f));
159
153
  },
160
- usage: "dss project duplicate <sourceKey> <targetKey> <targetName> [--data JSON|--data-file PATH|--stdin]",
161
154
  description: "Duplicate a project into a new project key.",
162
155
  examples: ["dss project duplicate SRC NEW NewProject",],
163
156
  },
164
157
  export: {
165
158
  handler: async (c, a, f) => {
166
- requireArgs(a, 1, "dss project export <projectKey> --output PATH [--data JSON|--data-file PATH|--stdin]");
159
+ requireArgs(a, 1, commandUsage("project", "export"));
167
160
  const out = f["output"];
168
161
  if (!out)
169
162
  throw new UsageError("--output PATH is required.", "missing_required_flag");
170
163
  await c.projects.exportArchive(a[0], out, jsonInput(f));
171
164
  return { path: out, };
172
165
  },
173
- usage: "dss project export <projectKey> --output PATH [--data JSON|--data-file PATH|--stdin]",
174
166
  description: "Export a project to a local archive (.zip).",
175
167
  examples: ["dss project export MY_PROJ --output ./my_proj.zip",],
176
168
  },
177
169
  import: {
178
170
  handler: async (c, a, f) => {
179
- const usage = "dss project import <filePath> [--target-project-key KEY] [--data JSON|--data-file PATH|--stdin]";
171
+ const usage = commandUsage("project", "import");
180
172
  requireArgs(a, 1, usage);
181
173
  const settings = jsonInput(f) ?? {};
182
174
  const rawTarget = f["target-project-key"];
@@ -200,7 +192,6 @@ export const projectCommands = {
200
192
  }
201
193
  return result;
202
194
  },
203
- usage: "dss project import <filePath> [--target-project-key KEY] [--data JSON|--data-file PATH|--stdin]",
204
195
  description: "Upload and process a project archive, optionally overriding its project key. The archive is validated locally before upload; verified success reports the used project key, explicit request/actual remapping, and the landed incarnation hash.",
205
196
  examples: [
206
197
  "dss project import ./my_proj.zip",
@@ -210,7 +201,6 @@ export const projectCommands = {
210
201
  "inspect-archive": {
211
202
  handler: async (_c, a) => inspectArchiveCommand(a),
212
203
  localHandler: inspectArchiveCommand,
213
- usage: INSPECT_ARCHIVE_USAGE,
214
204
  description: "Read-only local inspection of a project export archive: member names, sizes, manifest validity, archive issues, and the source project key. Never contacts DSS.",
215
205
  examples: ["dss project inspect-archive ./my_proj.zip",],
216
206
  },
@@ -220,13 +210,11 @@ export const projectCommands = {
220
210
  await c.projects.setPermissions(f["project-key"], body);
221
211
  return { updated: true, };
222
212
  },
223
- usage: "dss project permissions-set (--data JSON|--data-file PATH|--stdin) [--project-key KEY]",
224
213
  description: "Replace a project's permissions.",
225
214
  examples: ["dss project permissions-set --data-file perms.json --project-key MY_PROJ",],
226
215
  },
227
216
  "permissions-get": {
228
217
  handler: (c, _a, f) => c.projects.getPermissions(f["project-key"]),
229
- usage: "dss project permissions-get [--project-key KEY]",
230
218
  description: "Get a project's permissions.",
231
219
  examples: ["dss project permissions-get --project-key MY_PROJ",],
232
220
  },
@@ -244,7 +232,6 @@ export const projectCommands = {
244
232
  await c.projects.setMetadata(f["project-key"], metadata);
245
233
  return { updated: true, };
246
234
  },
247
- usage: "dss project metadata-set (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
248
235
  description: "Replace project metadata with a full object obtained from a previous metadata GET. Fields absent from the payload are removed.",
249
236
  examples: [
250
237
  "dss project metadata-set --data-file metadata.json --project-key MY_PROJ --dry-run",
@@ -252,7 +239,6 @@ export const projectCommands = {
252
239
  },
253
240
  "tags-get": {
254
241
  handler: (c, _a, f) => c.projects.tags(f["project-key"]),
255
- usage: "dss project tags-get [--project-key KEY]",
256
242
  description: "Get project-level tags as a dictionary of tag name to display settings.",
257
243
  examples: ["dss project tags-get", "dss project tags-get --project-key MY_PROJ",],
258
244
  },
@@ -270,7 +256,6 @@ export const projectCommands = {
270
256
  await c.projects.setTags(f["project-key"], tags);
271
257
  return { updated: true, };
272
258
  },
273
- usage: "dss project tags-set (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
274
259
  description: "Replace project tags with a full object obtained from a previous tags GET. Tags absent from the payload are removed.",
275
260
  examples: [
276
261
  'dss project tags-set --data \'{"tags":{"production":{"color":"#28aadd"}}}\' --project-key MY_PROJ --dry-run',
@@ -278,7 +263,6 @@ export const projectCommands = {
278
263
  },
279
264
  "settings-get": {
280
265
  handler: (c, _a, f) => c.projects.getSettings(f["project-key"]),
281
- usage: "dss project settings-get [--project-key KEY]",
282
266
  description: "Get a project's settings.",
283
267
  examples: ["dss project settings-get --project-key MY_PROJ",],
284
268
  },
@@ -291,8 +275,7 @@ export const projectCommands = {
291
275
  await c.projects.setSettings(projectKey, next);
292
276
  return { updated: true, };
293
277
  },
294
- usage: "dss project settings-set (--data JSON|--data-file PATH|--stdin) [--project-key KEY]",
295
278
  description: "Update a project's settings via JSON merge.",
296
279
  examples: ["dss project settings-set --data-file settings.json --project-key MY_PROJ",],
297
280
  },
298
- };
281
+ });
@@ -1,2 +1,18 @@
1
1
  import type { CommandMeta } from "../types.js";
2
+ /** Maps `recipe create` flags to SDK options; shared by the handler and `--plan`. */
3
+ export declare function recipeCreateOptionsFromFlags(f: Record<string, string | boolean>): {
4
+ type: string;
5
+ name: string | undefined;
6
+ inputDatasets: string[] | undefined;
7
+ outputDataset: string | undefined;
8
+ outputFolder: string | undefined;
9
+ outputConnection: string | undefined;
10
+ joinOn?: string[] | undefined;
11
+ joinType?: string | undefined;
12
+ fuzzyOn?: string[] | undefined;
13
+ fuzzyDistance?: string | undefined;
14
+ fuzzyThreshold?: number | undefined;
15
+ fuzzyNormalize: boolean;
16
+ projectKey: string | undefined;
17
+ };
2
18
  export declare const recipeCommands: Record<string, CommandMeta>;
@@ -1,12 +1,14 @@
1
1
  import { readFileSync, } from "node:fs";
2
2
  import { writeFile, } from "node:fs/promises";
3
3
  import { join, } from "node:path";
4
+ import { ClientValidationError, } from "../../errors.js";
4
5
  import { deepMerge, } from "../../utils/deep-merge.js";
5
6
  import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredJsonInput, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
6
7
  import { executionMode, } from "../flags.js";
7
8
  import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
8
9
  import { ensureRecipeBackupDir, readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, writeRecipeBackup, } from "../helpers/recipe.js";
9
10
  import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
11
+ import { commandUsage, withUsage, } from "../syntax.js";
10
12
  import { requireArgs, UsageError, } from "../usage.js";
11
13
  /** Read a local recipe file before any DSS request, mapping read failures to usage validation. */
12
14
  function readRecipeFile(filePath, flagName) {
@@ -59,27 +61,80 @@ function formatLineDiff(remoteName, localPath, remoteContent, localContent) {
59
61
  }
60
62
  return lines.join("\n");
61
63
  }
62
- export const recipeCommands = {
64
+ /** Maps `recipe create` flags to SDK options; shared by the handler and `--plan`. */
65
+ export function recipeCreateOptionsFromFlags(f) {
66
+ const type = f["type"];
67
+ if (!type) {
68
+ throw new UsageError(`--type is required. Usage: ${commandUsage("recipe", "create")}`);
69
+ }
70
+ const outputDataset = f["output"];
71
+ const outputFolder = f["output-folder"];
72
+ if (outputDataset && outputFolder) {
73
+ throw new UsageError("--output and --output-folder are mutually exclusive.");
74
+ }
75
+ if (!outputDataset && !outputFolder) {
76
+ throw new UsageError(`--output or --output-folder is required. Usage: ${commandUsage("recipe", "create")}`);
77
+ }
78
+ if (outputFolder && !f["output-connection"]) {
79
+ throw new UsageError("--output-connection is required when using --output-folder.");
80
+ }
81
+ const name = f["name"];
82
+ const pk = f["project-key"];
83
+ const inputDatasets = recipeInputDatasetsFromFlags(f);
84
+ const joinColumns = splitCsvFlag(f["join-on"]);
85
+ const fuzzyColumns = splitCsvFlag(f["fuzzy-on"]);
86
+ const rawFuzzyDistance = f["fuzzy-distance"];
87
+ let fuzzyDistance;
88
+ if (typeof rawFuzzyDistance === "string") {
89
+ const normalized = rawFuzzyDistance.trim().toUpperCase();
90
+ if (![
91
+ "DAMERAU_LEVENSHTEIN",
92
+ "HAMMING",
93
+ "JACCARD",
94
+ "COSINE",
95
+ "EUCLIDEAN",
96
+ ].includes(normalized)) {
97
+ throw new UsageError("--fuzzy-distance must be one of DAMERAU_LEVENSHTEIN, HAMMING, JACCARD, COSINE, or EUCLIDEAN.", "invalid_enum");
98
+ }
99
+ fuzzyDistance = normalized;
100
+ }
101
+ const fuzzyThreshold = num(f["fuzzy-threshold"], "--fuzzy-threshold");
102
+ const payload = {
103
+ type,
104
+ name,
105
+ inputDatasets,
106
+ outputDataset,
107
+ outputFolder,
108
+ outputConnection: f["output-connection"],
109
+ ...(joinColumns.length > 0 ? { joinOn: joinColumns, } : {}),
110
+ ...(typeof f["join-type"] === "string" ? { joinType: f["join-type"], } : {}),
111
+ ...(fuzzyColumns.length > 0 ? { fuzzyOn: fuzzyColumns, } : {}),
112
+ ...(fuzzyDistance ? { fuzzyDistance, } : {}),
113
+ ...(fuzzyThreshold !== undefined ? { fuzzyThreshold, } : {}),
114
+ fuzzyNormalize: f["normalize"] === true,
115
+ projectKey: pk,
116
+ };
117
+ return payload;
118
+ }
119
+ export const recipeCommands = withUsage("recipe", {
63
120
  list: {
64
121
  handler: (c, _a, f) => c.recipes.list(f["project-key"]),
65
- usage: "dss recipe list [--project-key KEY]",
66
122
  description: "List all recipes in a project.",
67
123
  examples: ["dss recipe list",],
68
124
  },
69
125
  metadata: {
70
126
  handler: (c, a, f) => {
71
- requireArgs(a, 1, "dss recipe metadata <name>");
127
+ requireArgs(a, 1, commandUsage("recipe", "metadata"));
72
128
  return c.recipes.metadata(a[0], {
73
129
  projectKey: f["project-key"],
74
130
  });
75
131
  },
76
- usage: "dss recipe metadata <name> [--project-key KEY]",
77
132
  description: "Get recipe metadata (label, description, tags, custom fields).",
78
133
  examples: ["dss recipe metadata compute_orders",],
79
134
  },
80
135
  "metadata-set": {
81
136
  handler: async (c, a, f) => {
82
- const usage = "dss recipe metadata-set <name> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]";
137
+ const usage = commandUsage("recipe", "metadata-set");
83
138
  requireArgs(a, 1, usage);
84
139
  const metadata = requiredJsonInput(f, "--data, --data-file, or --stdin is required (full metadata object).");
85
140
  const pk = f["project-key"];
@@ -95,7 +150,6 @@ export const recipeCommands = {
95
150
  await c.recipes.setMetadata(a[0], metadata, { projectKey: pk, });
96
151
  return { updated: a[0], resource: "recipe", };
97
152
  },
98
- usage: "dss recipe metadata-set <name> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
99
153
  description: "Replace recipe metadata with a full object obtained from a previous metadata GET. Fields absent from the payload are removed.",
100
154
  examples: [
101
155
  'dss recipe metadata-set compute_orders --data \'{"label":"Orders"}\' --dry-run',
@@ -103,13 +157,12 @@ export const recipeCommands = {
103
157
  },
104
158
  get: {
105
159
  handler: (c, a, f) => {
106
- requireArgs(a, 1, "dss recipe get <name>");
160
+ requireArgs(a, 1, commandUsage("recipe", "get"));
107
161
  return c.recipes.get(a[0], {
108
162
  includePayload: f["include-payload"] === true && f["no-payload"] !== true,
109
163
  projectKey: f["project-key"],
110
164
  });
111
165
  },
112
- usage: "dss recipe get <name> [--include-payload|--no-payload] [--project-key KEY]",
113
166
  description: "Get compact recipe settings unless --include-payload is set.",
114
167
  examples: [
115
168
  "dss recipe get compute_orders",
@@ -119,18 +172,17 @@ export const recipeCommands = {
119
172
  },
120
173
  "validate-graph": {
121
174
  handler: (c, a, f) => {
122
- requireArgs(a, 1, "dss recipe validate-graph <name>");
175
+ requireArgs(a, 1, commandUsage("recipe", "validate-graph"));
123
176
  return c.recipes.validateGraph(a[0], {
124
177
  projectKey: f["project-key"],
125
178
  });
126
179
  },
127
- usage: "dss recipe validate-graph <name> [--project-key KEY]",
128
180
  description: "Validate declared recipe input/output graph references before building.",
129
181
  examples: ["dss recipe validate-graph compute_orders",],
130
182
  },
131
183
  run: {
132
184
  handler: async (c, a, f) => {
133
- requireArgs(a, 1, "dss recipe run <name>");
185
+ requireArgs(a, 1, commandUsage("recipe", "run"));
134
186
  const pk = f["project-key"];
135
187
  const wait = recipeRunShouldWait(f);
136
188
  const options = {
@@ -163,7 +215,6 @@ export const recipeCommands = {
163
215
  }
164
216
  return c.recipes.run(a[0], options);
165
217
  },
166
- usage: "dss recipe run <name> [--wait|--no-wait] [--build-mode MODE] [--include-logs] [--log-filter stdout|stderr|user|errors] [--summary] [--max-log-lines N] [--timeout MS] [--poll-interval MS] [--partition PARTITION] [--dry-run] [--project-key KEY]",
167
218
  description: "Run a recipe by resolving its outputs and submitting the correct dataset or managed-folder build job. --timeout defaults to 120000 ms and --poll-interval to 2000 ms.",
168
219
  examples: [
169
220
  "dss recipe run compute_orders --wait",
@@ -173,7 +224,7 @@ export const recipeCommands = {
173
224
  },
174
225
  delete: {
175
226
  handler: async (c, a, f) => {
176
- requireArgs(a, 1, "dss recipe delete <name>");
227
+ requireArgs(a, 1, commandUsage("recipe", "delete"));
177
228
  const pk = f["project-key"];
178
229
  if (executionMode(f).dryRun || f["if-exists"] === true) {
179
230
  const current = await readIfExists(() => c.recipes.get(a[0], { projectKey: pk, includePayload: true, }));
@@ -186,19 +237,17 @@ export const recipeCommands = {
186
237
  await c.recipes.delete(a[0], pk);
187
238
  return { deleted: a[0], resource: "recipe", };
188
239
  },
189
- usage: "dss recipe delete <name> [--if-exists] [--dry-run] [--project-key KEY]",
190
240
  description: "Delete a recipe.",
191
241
  examples: ["dss recipe delete compute_orders", "dss recipe delete compute_orders --if-exists",],
192
242
  },
193
243
  download: {
194
244
  handler: (c, a, f) => {
195
- requireArgs(a, 1, "dss recipe download <name>");
245
+ requireArgs(a, 1, commandUsage("recipe", "download"));
196
246
  return c.recipes.download(a[0], {
197
247
  outputPath: f["output"],
198
248
  projectKey: f["project-key"],
199
249
  });
200
250
  },
201
- usage: "dss recipe download <name> [--output PATH] [--project-key KEY]",
202
251
  description: "Download recipe definition as JSON.",
203
252
  examples: [
204
253
  "dss recipe download compute_orders",
@@ -207,13 +256,12 @@ export const recipeCommands = {
207
256
  },
208
257
  "download-code": {
209
258
  handler: (c, a, f) => {
210
- requireArgs(a, 1, "dss recipe download-code <name>");
259
+ requireArgs(a, 1, commandUsage("recipe", "download-code"));
211
260
  return c.recipes.downloadCode(a[0], {
212
261
  outputPath: f["output"],
213
262
  projectKey: f["project-key"],
214
263
  });
215
264
  },
216
- usage: "dss recipe download-code <name> [--output PATH] [--project-key KEY]",
217
265
  description: "Download the code payload of a recipe.",
218
266
  examples: [
219
267
  "dss recipe download-code compute_orders",
@@ -222,57 +270,9 @@ export const recipeCommands = {
222
270
  },
223
271
  create: {
224
272
  handler: async (c, _a, f) => {
225
- const type = f["type"];
226
- if (!type) {
227
- throw new UsageError("--type is required. Usage: dss recipe create --type TYPE [--input DS] (--output DS | --output-folder FOLDER_ID)");
228
- }
229
- const outputDataset = f["output"];
230
- const outputFolder = f["output-folder"];
231
- if (outputDataset && outputFolder) {
232
- throw new UsageError("--output and --output-folder are mutually exclusive.");
233
- }
234
- if (!outputDataset && !outputFolder) {
235
- throw new UsageError("--output or --output-folder is required. Usage: dss recipe create --type TYPE [--input DS] (--output DS | --output-folder FOLDER_ID)");
236
- }
237
- if (outputFolder && !f["output-connection"]) {
238
- throw new UsageError("--output-connection is required when using --output-folder.");
239
- }
240
- const name = f["name"];
241
- const pk = f["project-key"];
242
- const inputDatasets = recipeInputDatasetsFromFlags(f);
243
- const joinColumns = splitCsvFlag(f["join-on"]);
244
- const fuzzyColumns = splitCsvFlag(f["fuzzy-on"]);
245
- const rawFuzzyDistance = f["fuzzy-distance"];
246
- let fuzzyDistance;
247
- if (typeof rawFuzzyDistance === "string") {
248
- const normalized = rawFuzzyDistance.trim().toUpperCase();
249
- if (![
250
- "DAMERAU_LEVENSHTEIN",
251
- "HAMMING",
252
- "JACCARD",
253
- "COSINE",
254
- "EUCLIDEAN",
255
- ].includes(normalized)) {
256
- throw new UsageError("--fuzzy-distance must be one of DAMERAU_LEVENSHTEIN, HAMMING, JACCARD, COSINE, or EUCLIDEAN.", "invalid_enum");
257
- }
258
- fuzzyDistance = normalized;
259
- }
260
- const fuzzyThreshold = num(f["fuzzy-threshold"], "--fuzzy-threshold");
261
- const payload = {
262
- type,
263
- name,
264
- inputDatasets,
265
- outputDataset,
266
- outputFolder,
267
- outputConnection: f["output-connection"],
268
- ...(joinColumns.length > 0 ? { joinOn: joinColumns, } : {}),
269
- ...(typeof f["join-type"] === "string" ? { joinType: f["join-type"], } : {}),
270
- ...(fuzzyColumns.length > 0 ? { fuzzyOn: fuzzyColumns, } : {}),
271
- ...(fuzzyDistance ? { fuzzyDistance, } : {}),
272
- ...(fuzzyThreshold !== undefined ? { fuzzyThreshold, } : {}),
273
- fuzzyNormalize: f["normalize"] === true,
274
- projectKey: pk,
275
- };
273
+ const payload = recipeCreateOptionsFromFlags(f);
274
+ const name = payload.name;
275
+ const pk = payload.projectKey;
276
276
  const zoneId = await resolveFlowZoneIdFromFlags(c, f, pk);
277
277
  const zoneMove = zoneId && name
278
278
  ? [{ objectId: name, objectType: "RECIPE", },]
@@ -312,7 +312,6 @@ export const recipeCommands = {
312
312
  },], pk);
313
313
  return { created: createdName, resource: "recipe", ...created, ...moved, };
314
314
  },
315
- usage: "dss recipe create --type TYPE [--input DS[,DS2]] (--output DS | --output-folder FOLDER_ID) [--name NAME] [--output-connection CONN] [--zone ZONE|--zone-id ID] [--if-not-exists] [--dry-run] [--join-on COL|LEFT=RIGHT[,...]] [--join-type LEFT|INNER|RIGHT|FULL] [--fuzzy-on COL|LEFT=RIGHT[,...]] [--fuzzy-distance DAMERAU_LEVENSHTEIN|HAMMING|JACCARD|COSINE|EUCLIDEAN] [--fuzzy-threshold N] [--normalize] [--project-key KEY]",
316
315
  description: "Create a recipe with optional inputs and a dataset or managed-folder output.",
317
316
  examples: [
318
317
  "dss recipe create --type python --input raw_orders,lookup --output orders_clean",
@@ -322,7 +321,7 @@ export const recipeCommands = {
322
321
  },
323
322
  clone: {
324
323
  handler: async (c, a, f) => {
325
- const usage = "dss recipe clone (source|--from SOURCE) (--name NAME|--to NAME) [--replace-input FROM=TO] [--replace-output FROM=TO] [--replace-payload-text FROM=TO] [--output DATASET] [--copy-output-settings] [--path PATH] [--metastore-table TABLE] [--zone ZONE|--zone-id ID] [--dry-run] [--project-key KEY]";
324
+ const usage = commandUsage("recipe", "clone");
326
325
  const fromFlag = typeof f["from"] === "string" ? f["from"].trim() : "";
327
326
  const sourceName = a[0] ?? fromFlag;
328
327
  if (!sourceName) {
@@ -390,7 +389,6 @@ export const recipeCommands = {
390
389
  const moved = await moveCreatedItemsToZone(c, f, [{ objectId: name, objectType: "RECIPE", },], pk);
391
390
  return { ...cloned, resource: "recipe", ...moved, };
392
391
  },
393
- usage: "dss recipe clone (source|--from SOURCE) (--name NAME|--to NAME) [--replace-input FROM=TO] [--replace-output FROM=TO] [--replace-payload-text FROM=TO] [--output DATASET] [--copy-output-settings] [--path PATH] [--metastore-table TABLE] [--zone ZONE|--zone-id ID] [--dry-run] [--project-key KEY]",
394
392
  description: "Clone a recipe graph/settings/payload into a separate experiment recipe.",
395
393
  examples: [
396
394
  "dss recipe clone compute_orders --name compute_orders_opt --output orders_opt --copy-output-settings --dry-run",
@@ -399,10 +397,10 @@ export const recipeCommands = {
399
397
  },
400
398
  diff: {
401
399
  handler: async (c, a, f) => {
402
- requireArgs(a, 1, "dss recipe diff <name> --file PATH");
400
+ requireArgs(a, 1, commandUsage("recipe", "diff"));
403
401
  const filePath = f["file"];
404
402
  if (!filePath) {
405
- throw new UsageError("--file is required. Usage: dss recipe diff <name> --file PATH");
403
+ throw new UsageError(`--file is required. Usage: ${commandUsage("recipe", "diff")}`);
406
404
  }
407
405
  // Read and validate the local file before any DSS request.
408
406
  const localContent = readRecipeFile(filePath, "--file");
@@ -411,20 +409,19 @@ export const recipeCommands = {
411
409
  projectKey: f["project-key"],
412
410
  });
413
411
  if (!result.payload) {
414
- throw new Error(`Recipe "${a[0]}" has no code payload to diff.`);
412
+ throw new ClientValidationError(`Recipe "${a[0]}" has no code payload to diff.`, "validation_failed");
415
413
  }
416
414
  return formatLineDiff(a[0], filePath, result.payload, localContent);
417
415
  },
418
- usage: "dss recipe diff <name> --file PATH [--project-key KEY]",
419
416
  description: "Show differences between local file and remote recipe code.",
420
417
  examples: ["dss recipe diff compute_orders --file code.py",],
421
418
  },
422
419
  update: {
423
420
  handler: async (c, a, f) => {
424
- requireArgs(a, 1, "dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin]");
421
+ requireArgs(a, 1, commandUsage("recipe", "update"));
425
422
  const data = jsonInput(f);
426
423
  if (!data) {
427
- throw new UsageError("--data, --data-file, or --stdin is required. Usage: dss recipe update <name> [--data '{...}' | --data-file PATH | --stdin]");
424
+ throw new UsageError(`--data, --data-file, or --stdin is required. Usage: ${commandUsage("recipe", "update")}`);
428
425
  }
429
426
  const pk = f["project-key"];
430
427
  if (executionMode(f).dryRun) {
@@ -442,7 +439,6 @@ export const recipeCommands = {
442
439
  await c.recipes.update(a[0], data, pk);
443
440
  return { updated: a[0], resource: "recipe", };
444
441
  },
445
- usage: "dss recipe update <name> (--data '{...}' | --data-file PATH | --stdin) [--dry-run] [--project-key KEY]",
446
442
  description: "Update recipe settings via JSON merge. Recipe definition fields must be nested under a top-level recipe key.",
447
443
  examples: [
448
444
  "dss recipe update compute_orders --data-file settings.json --dry-run",
@@ -452,7 +448,7 @@ export const recipeCommands = {
452
448
  },
453
449
  "add-input": {
454
450
  handler: async (c, a, f) => {
455
- requireArgs(a, 2, "dss recipe add-input <recipe> <dataset> [--role ROLE] [--if-not-exists] [--dry-run] [--project-key KEY]");
451
+ requireArgs(a, 2, commandUsage("recipe", "add-input"));
456
452
  const role = f["role"] ?? "main";
457
453
  const pk = f["project-key"];
458
454
  const { recipe, } = await c.recipes.get(a[0], { projectKey: pk, });
@@ -480,7 +476,6 @@ export const recipeCommands = {
480
476
  await c.recipes.update(a[0], { recipe: { inputs: { [role]: { items: nextItems, }, }, }, }, pk);
481
477
  return { updated: a[0], resource: "recipe", action: "add-input", role, dataset: a[1], inputs, };
482
478
  },
483
- usage: "dss recipe add-input <recipe> <dataset> [--role ROLE] [--if-not-exists] [--dry-run] [--project-key KEY]",
484
479
  description: "Add a dataset as a recipe input by appending one item to the current inputs (no need to resend the whole list).",
485
480
  examples: [
486
481
  "dss recipe add-input compute_orders extra_lookup",
@@ -489,7 +484,7 @@ export const recipeCommands = {
489
484
  },
490
485
  "remove-input": {
491
486
  handler: async (c, a, f) => {
492
- requireArgs(a, 2, "dss recipe remove-input <recipe> <dataset> [--role ROLE] [--if-exists] [--dry-run] [--project-key KEY]");
487
+ requireArgs(a, 2, commandUsage("recipe", "remove-input"));
493
488
  const role = f["role"] ?? "main";
494
489
  const pk = f["project-key"];
495
490
  const { recipe, } = await c.recipes.get(a[0], { projectKey: pk, });
@@ -524,7 +519,6 @@ export const recipeCommands = {
524
519
  inputs,
525
520
  };
526
521
  },
527
- usage: "dss recipe remove-input <recipe> <dataset> [--role ROLE] [--if-exists] [--dry-run] [--project-key KEY]",
528
522
  description: "Remove a dataset from a recipe's inputs by dropping one item from the current inputs.",
529
523
  examples: [
530
524
  "dss recipe remove-input compute_orders stale_lookup",
@@ -533,7 +527,7 @@ export const recipeCommands = {
533
527
  },
534
528
  "get-payload": {
535
529
  handler: async (c, a, f) => {
536
- requireArgs(a, 1, "dss recipe get-payload <name>");
530
+ requireArgs(a, 1, commandUsage("recipe", "get-payload"));
537
531
  const payload = await c.recipes.getPayload(a[0], {
538
532
  projectKey: f["project-key"],
539
533
  });
@@ -543,7 +537,6 @@ export const recipeCommands = {
543
537
  }
544
538
  return payload;
545
539
  },
546
- usage: "dss recipe get-payload <name> [--output PATH] [--project-key KEY]",
547
540
  description: "Print the recipe code payload as a JSON string, or write it to a file with --output.",
548
541
  examples: [
549
542
  "dss recipe get-payload compute_orders",
@@ -552,18 +545,17 @@ export const recipeCommands = {
552
545
  },
553
546
  cat: {
554
547
  handler: (c, a, f) => {
555
- requireArgs(a, 1, "dss recipe cat <name>");
548
+ requireArgs(a, 1, commandUsage("recipe", "cat"));
556
549
  return c.recipes.getPayload(a[0], {
557
550
  projectKey: f["project-key"],
558
551
  });
559
552
  },
560
- usage: "dss recipe cat <name> [--project-key KEY]",
561
553
  description: "Print the recipe code payload as a JSON string.",
562
554
  examples: ["dss recipe cat compute_orders",],
563
555
  },
564
556
  "set-payload": {
565
557
  handler: async (c, a, f) => {
566
- requireArgs(a, 1, "dss recipe set-payload <name> --file PATH");
558
+ requireArgs(a, 1, commandUsage("recipe", "set-payload"));
567
559
  const filePath = f["file"];
568
560
  if (!filePath)
569
561
  throw new UsageError("--file is required.");
@@ -603,7 +595,6 @@ export const recipeCommands = {
603
595
  ...(backupPath ? { backupPath, } : {}),
604
596
  };
605
597
  },
606
- usage: "dss recipe set-payload <name> --file PATH [--backup-dir DIR|--no-backup] [--dry-run] [--project-key KEY]",
607
598
  description: "Upload recipe code from a local file, backing up payload, graph, settings, and version metadata by default.",
608
599
  examples: [
609
600
  "dss recipe set-payload compute_orders --file code.py --dry-run",
@@ -613,7 +604,7 @@ export const recipeCommands = {
613
604
  },
614
605
  restore: {
615
606
  handler: async (c, a, f) => {
616
- const usage = "dss recipe restore <name> --backup FILE [--payload-only] [--dry-run] [--project-key KEY]";
607
+ const usage = commandUsage("recipe", "restore");
617
608
  requireArgs(a, 1, usage);
618
609
  const backupPath = requiredStringFlag(f, "backup", usage);
619
610
  const backup = readRecipeBackupFile(backupPath);
@@ -648,7 +639,6 @@ export const recipeCommands = {
648
639
  payloadOnly: f["payload-only"] === true,
649
640
  };
650
641
  },
651
- usage: "dss recipe restore <name> --backup FILE [--payload-only] [--dry-run] [--project-key KEY]",
652
642
  description: "Restore a recipe from a set-payload backup.",
653
643
  examples: [
654
644
  "dss recipe restore compute_orders --backup .dss-backups/recipes/backup.recipe-backup.json --dry-run",
@@ -656,7 +646,7 @@ export const recipeCommands = {
656
646
  },
657
647
  "assert-unchanged": {
658
648
  handler: async (c, a, f) => {
659
- const usage = "dss recipe assert-unchanged <name> --since BACKUP [--project-key KEY]";
649
+ const usage = commandUsage("recipe", "assert-unchanged");
660
650
  requireArgs(a, 1, usage);
661
651
  const backupPath = requiredStringFlag(f, "since", usage);
662
652
  const backup = readRecipeBackupFile(backupPath);
@@ -708,10 +698,9 @@ export const recipeCommands = {
708
698
  failures,
709
699
  };
710
700
  },
711
- usage: "dss recipe assert-unchanged <name> --since BACKUP [--project-key KEY]",
712
701
  description: "Compare current recipe payload, graph, and code env against a backup.",
713
702
  examples: [
714
703
  "dss recipe assert-unchanged compute_orders --since .dss-backups/recipes/backup.recipe-backup.json",
715
704
  ],
716
705
  },
717
- };
706
+ });