dataiku-sdk 3.1.0 → 3.3.0

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