dataiku-sdk 3.6.1 → 3.8.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 (139) hide show
  1. package/README.md +14 -6
  2. package/bin/bun-version.js +14 -0
  3. package/bin/dss.js +11 -15
  4. package/dist/build-metadata.json +1 -1
  5. package/dist/packages/types/src/index.d.ts +114 -5
  6. package/dist/packages/types/src/index.js +75 -2
  7. package/dist/src/cli/coerce.d.ts +2 -0
  8. package/dist/src/cli/coerce.js +20 -16
  9. package/dist/src/cli/command-syntax.json +3369 -144
  10. package/dist/src/cli/commands/analysis.js +19 -1
  11. package/dist/src/cli/commands/bundle.js +22 -0
  12. package/dist/src/cli/commands/connection.js +26 -2
  13. package/dist/src/cli/commands/data-quality.js +33 -1
  14. package/dist/src/cli/commands/dataset.js +76 -9
  15. package/dist/src/cli/commands/discussion.js +15 -0
  16. package/dist/src/cli/commands/doctor.js +1 -1
  17. package/dist/src/cli/commands/flow-zone.js +241 -46
  18. package/dist/src/cli/commands/flow.d.ts +5 -0
  19. package/dist/src/cli/commands/flow.js +57 -0
  20. package/dist/src/cli/commands/future.js +8 -0
  21. package/dist/src/cli/commands/index.js +6 -0
  22. package/dist/src/cli/commands/job.js +44 -6
  23. package/dist/src/cli/commands/metrics.js +21 -1
  24. package/dist/src/cli/commands/ml-task.d.ts +5 -0
  25. package/dist/src/cli/commands/ml-task.js +86 -16
  26. package/dist/src/cli/commands/mlflow.d.ts +11 -0
  27. package/dist/src/cli/commands/mlflow.js +121 -0
  28. package/dist/src/cli/commands/plugin.js +5 -11
  29. package/dist/src/cli/commands/project-deployer.js +70 -0
  30. package/dist/src/cli/commands/project-library.js +2 -5
  31. package/dist/src/cli/commands/project.js +8 -0
  32. package/dist/src/cli/commands/recipe.d.ts +13 -0
  33. package/dist/src/cli/commands/recipe.js +130 -7
  34. package/dist/src/cli/commands/saved-model.d.ts +1 -1
  35. package/dist/src/cli/commands/saved-model.js +9 -76
  36. package/dist/src/cli/commands/streaming-endpoint.js +30 -1
  37. package/dist/src/cli/commands/webapp.js +8 -0
  38. package/dist/src/cli/commands/wiki.js +78 -0
  39. package/dist/src/cli/commands/workspace.js +8 -0
  40. package/dist/src/cli/contract.d.ts +16 -15
  41. package/dist/src/cli/contract.js +150 -3407
  42. package/dist/src/cli/doctor.d.ts +13 -0
  43. package/dist/src/cli/doctor.js +43 -1
  44. package/dist/src/cli/flags.d.ts +14 -1
  45. package/dist/src/cli/flags.js +106 -8
  46. package/dist/src/cli/helpers/app-successor.js +1 -2
  47. package/dist/src/cli/helpers/dataset.d.ts +12 -0
  48. package/dist/src/cli/helpers/dataset.js +36 -0
  49. package/dist/src/cli/helpers/documentation.d.ts +21 -0
  50. package/dist/src/cli/helpers/documentation.js +53 -0
  51. package/dist/src/cli/helpers/flow-zone.d.ts +21 -0
  52. package/dist/src/cli/helpers/flow-zone.js +144 -4
  53. package/dist/src/cli/helpers/trained-model.d.ts +26 -0
  54. package/dist/src/cli/helpers/trained-model.js +144 -0
  55. package/dist/src/cli/list-output.d.ts +7 -0
  56. package/dist/src/cli/list-output.js +77 -0
  57. package/dist/src/cli/list-shapes.d.ts +21 -0
  58. package/dist/src/cli/list-shapes.js +111 -0
  59. package/dist/src/cli/output-schemas.d.ts +0 -14
  60. package/dist/src/cli/output-schemas.js +4 -7
  61. package/dist/src/cli/output.d.ts +0 -1
  62. package/dist/src/cli/output.js +33 -19
  63. package/dist/src/cli/plans.d.ts +14 -0
  64. package/dist/src/cli/plans.js +3740 -0
  65. package/dist/src/cli/types.d.ts +2 -0
  66. package/dist/src/cli/usage.d.ts +7 -0
  67. package/dist/src/cli/usage.js +20 -1
  68. package/dist/src/cli/version.d.ts +2 -2
  69. package/dist/src/cli/version.js +2 -2
  70. package/dist/src/cli.js +137 -115
  71. package/dist/src/client.d.ts +52 -43
  72. package/dist/src/client.js +267 -96
  73. package/dist/src/errors.d.ts +22 -2
  74. package/dist/src/errors.js +49 -50
  75. package/dist/src/generated/action-output-schemas.json +345 -89
  76. package/dist/src/index.d.ts +5 -1
  77. package/dist/src/index.js +3 -0
  78. package/dist/src/resources/analyses.d.ts +8 -0
  79. package/dist/src/resources/analyses.js +13 -1
  80. package/dist/src/resources/bundles.d.ts +34 -0
  81. package/dist/src/resources/bundles.js +67 -0
  82. package/dist/src/resources/connections.d.ts +9 -0
  83. package/dist/src/resources/connections.js +15 -0
  84. package/dist/src/resources/data-quality.d.ts +20 -0
  85. package/dist/src/resources/data-quality.js +30 -0
  86. package/dist/src/resources/dataset-create.d.ts +38 -0
  87. package/dist/src/resources/dataset-create.js +135 -0
  88. package/dist/src/resources/datasets.d.ts +46 -15
  89. package/dist/src/resources/datasets.js +120 -112
  90. package/dist/src/resources/discussions.d.ts +5 -0
  91. package/dist/src/resources/discussions.js +7 -0
  92. package/dist/src/resources/documentation.d.ts +25 -0
  93. package/dist/src/resources/documentation.js +16 -0
  94. package/dist/src/resources/flow-zones.d.ts +29 -3
  95. package/dist/src/resources/flow-zones.js +53 -3
  96. package/dist/src/resources/flow.d.ts +48 -0
  97. package/dist/src/resources/flow.js +65 -0
  98. package/dist/src/resources/futures.d.ts +23 -0
  99. package/dist/src/resources/futures.js +32 -0
  100. package/dist/src/resources/jobs.d.ts +34 -0
  101. package/dist/src/resources/jobs.js +170 -3
  102. package/dist/src/resources/metrics.d.ts +10 -0
  103. package/dist/src/resources/metrics.js +10 -0
  104. package/dist/src/resources/ml-tasks.d.ts +65 -2
  105. package/dist/src/resources/ml-tasks.js +102 -28
  106. package/dist/src/resources/mlflow-extension.d.ts +73 -0
  107. package/dist/src/resources/mlflow-extension.js +105 -0
  108. package/dist/src/resources/projects.d.ts +15 -0
  109. package/dist/src/resources/projects.js +18 -3
  110. package/dist/src/resources/recipe-create.d.ts +79 -0
  111. package/dist/src/resources/recipe-create.js +454 -0
  112. package/dist/src/resources/recipes.d.ts +28 -29
  113. package/dist/src/resources/recipes.js +201 -267
  114. package/dist/src/resources/saved-models.d.ts +10 -6
  115. package/dist/src/resources/saved-models.js +16 -14
  116. package/dist/src/resources/scenarios.js +8 -14
  117. package/dist/src/resources/sql.js +52 -13
  118. package/dist/src/resources/streaming-endpoints.d.ts +12 -0
  119. package/dist/src/resources/streaming-endpoints.js +15 -0
  120. package/dist/src/resources/trained-model.d.ts +60 -0
  121. package/dist/src/resources/trained-model.js +98 -0
  122. package/dist/src/resources/webapps.d.ts +8 -0
  123. package/dist/src/resources/webapps.js +10 -0
  124. package/dist/src/resources/wiki.d.ts +52 -1
  125. package/dist/src/resources/wiki.js +96 -0
  126. package/dist/src/resources/workspaces.d.ts +2 -0
  127. package/dist/src/resources/workspaces.js +4 -0
  128. package/dist/src/utils/flow-analysis.js +4 -0
  129. package/dist/src/utils/response-file.js +20 -9
  130. package/dist/src/utils/secret-sanitize.d.ts +7 -0
  131. package/dist/src/utils/secret-sanitize.js +46 -0
  132. package/package.json +25 -26
  133. package/plugin.json +1 -1
  134. package/skills/dataiku-dss/SKILL.md +1 -1
  135. package/skills/dataiku-dss/references/authentication.md +3 -1
  136. package/skills/dataiku-dss/references/coding.md +1 -0
  137. package/skills/dataiku-dss/references/discovery.md +4 -6
  138. package/skills/dataiku-dss/references/flow-maps.md +2 -2
  139. package/skills/dataiku-dss/references/troubleshooting.md +3 -2
@@ -7,6 +7,8 @@ export interface CommandPayloadSchema {
7
7
  dataFileFlag?: boolean;
8
8
  contentType?: "application/json" | "text/plain";
9
9
  jsonShape?: "object" | "array";
10
+ /** Precise JSON Schema of the payload, published as the contract's `schemas.input`. */
11
+ jsonSchema?: Record<string, unknown>;
10
12
  }
11
13
  export interface CommandFlagChoice {
12
14
  oneOf: string[][];
@@ -12,6 +12,13 @@ export declare function unsupportedHelpFlag(): UsageError;
12
12
  export declare function noCommandError(): UsageError;
13
13
  export declare function missingActionError(resource: string, validActions: string[], usage?: string): UsageError;
14
14
  export declare function unknownResourceError(resource: string): UsageError;
15
+ /**
16
+ * Agents rarely mistype; they reuse plausible flags from other commands or
17
+ * CLIs. When the command is known, the hint is its usage line, which lists
18
+ * every flag it takes, so one error carries the fix without a discovery round
19
+ * trip. `--json` gets its own hint: stdout is always JSON.
20
+ */
21
+ export declare function unknownFlagError(flagLabel: string, resource?: string, action?: string): UsageError;
15
22
  export declare function unknownActionError(resource: string, action: string | undefined, validActions: string[], hint?: string): UsageError;
16
23
  export declare function requireArgs(args: string[], count: number, usage: string): void;
17
24
  export declare function requireNoArgs(args: string[], usage: string): void;
@@ -1,4 +1,4 @@
1
- import { commandSyntaxTree, syntaxHasFlag, } from "./syntax.js";
1
+ import { commandSyntaxTree, renderNodes, syntaxHasFlag, } from "./syntax.js";
2
2
  export const RESOURCE_NAMES = [
3
3
  "agent",
4
4
  "app",
@@ -35,8 +35,10 @@ export const RESOURCE_NAMES = [
35
35
  "analysis",
36
36
  "ml-task",
37
37
  "saved-model",
38
+ "mlflow",
38
39
  "model-evaluation-store",
39
40
  "future",
41
+ "flow",
40
42
  "flow-zone",
41
43
  "dataset",
42
44
  "recipe",
@@ -64,6 +66,7 @@ const PROJECT_SCOPED_RESOURCES = new Set([
64
66
  "knowledge-bank",
65
67
  "dataset",
66
68
  "llm",
69
+ "flow",
67
70
  "flow-zone",
68
71
  "insight",
69
72
  "folder",
@@ -72,6 +75,7 @@ const PROJECT_SCOPED_RESOURCES = new Set([
72
75
  "notebook",
73
76
  "macro",
74
77
  "ml-task",
78
+ "mlflow",
75
79
  "model-evaluation-store",
76
80
  "recipe",
77
81
  "scenario",
@@ -121,6 +125,21 @@ export function missingActionError(resource, validActions, usage) {
121
125
  export function unknownResourceError(resource) {
122
126
  return new UsageError(`Unknown resource: ${resource}.`, "usage_error", COMMANDS_RUN_HINT, { resource, validResources: RESOURCE_NAMES, });
123
127
  }
128
+ /**
129
+ * Agents rarely mistype; they reuse plausible flags from other commands or
130
+ * CLIs. When the command is known, the hint is its usage line, which lists
131
+ * every flag it takes, so one error carries the fix without a discovery round
132
+ * trip. `--json` gets its own hint: stdout is always JSON.
133
+ */
134
+ export function unknownFlagError(flagLabel, resource, action) {
135
+ const command = resource && action ? commandSyntaxTree(resource, action) : undefined;
136
+ // The envelope already names resource and action; the message stays the flag.
137
+ const message = `Unknown flag: ${flagLabel}`;
138
+ if (flagLabel === "--json") {
139
+ return new UsageError(message, "unknown_flag", "Drop --json: stdout is always one JSON value.");
140
+ }
141
+ return new UsageError(message, "unknown_flag", command ? `Usage: ${renderNodes(command)}` : undefined);
142
+ }
124
143
  export function unknownActionError(resource, action, validActions, hint) {
125
144
  return new UsageError(`Unknown action: ${resource} ${action ?? ""}`.trim(), "usage_error", hint ?? COMMANDS_RUN_HINT, { resource, action, validActions, });
126
145
  }
@@ -49,7 +49,7 @@ export declare function buildVersionPayload(input: {
49
49
  export declare const PACKAGE_ROOT: string | undefined;
50
50
  export declare const CLI_VERSION: string;
51
51
  export declare const CLI_GIT_REVISION: string | undefined;
52
- export declare const AGENT_CONTRACT_VERSION = 2;
52
+ export declare const AGENT_CONTRACT_VERSION = 3;
53
53
  export declare const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
54
- export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v2.json";
54
+ export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
55
55
  export declare function cliVersionResult(): CliVersionPayload;
@@ -204,9 +204,9 @@ export function buildVersionPayload(input) {
204
204
  export const PACKAGE_ROOT = findPackageRoot();
205
205
  export const CLI_VERSION = packageVersion(PACKAGE_ROOT);
206
206
  export const CLI_GIT_REVISION = gitRevision(PACKAGE_ROOT);
207
- export const AGENT_CONTRACT_VERSION = 2;
207
+ export const AGENT_CONTRACT_VERSION = 3;
208
208
  export const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
209
- export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v2.json";
209
+ export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
210
210
  export function cliVersionResult() {
211
211
  // Only code actually loaded from dist/ may report dist provenance; an
212
212
  // inherited DSS_LOAD_SOURCE on a source run is ignored.
package/dist/src/cli.js CHANGED
@@ -2,18 +2,19 @@
2
2
  import * as fs from "node:fs/promises";
3
3
  import { homedir, } from "node:os";
4
4
  import { resolve, } from "node:path";
5
- import { num, unknownJsonInput, } from "./cli/coerce.js";
5
+ import { num, seenJsonInputCredentials, unknownJsonInput, } from "./cli/coerce.js";
6
6
  import { commands, } from "./cli/commands/index.js";
7
7
  import { ambientProjectKey, loadEnvFile, } from "./cli/env.js";
8
- import { executionMode, FLAG_ALIASES, isNegativeNumberToken, parseArgs, SHORT_FLAGS, VALUE_FLAGS, } from "./cli/flags.js";
8
+ import { executionMode, FLAG_ALIASES, isFlagValueToken, parseArgs, rawPositionals, } from "./cli/flags.js";
9
9
  import { cleanupLedgerEntry, } from "./cli/helpers/cleanup.js";
10
- import { commandFailureExitCode, CommandResultFailure, flushCliWarnings, isFailedWaitResult, planResult, projectResultFields, setOutputFieldProjection, writeCommandResult, } from "./cli/output.js";
10
+ import { shapeListResult, } from "./cli/list-output.js";
11
+ import { commandFailureExitCode, CommandResultFailure, enqueueCliWarning, flushCliWarnings, isFailedWaitResult, planResult, projectResultFields, setOutputFieldProjection, writeCommandResult, } from "./cli/output.js";
11
12
  import { currentCommandContext, resolveCredentials, resolveLoginCredentials, } from "./cli/runtime.js";
12
- import { commandUsage, } from "./cli/syntax.js";
13
- import { COMMANDS_RUN_HINT, inferRequiresProject, missingActionError, noCommandError, requireArgs, RESOURCE_NAMES, unknownActionError, unknownResourceError, UsageError, } from "./cli/usage.js";
13
+ import { commandSyntaxTree, commandUsage, } from "./cli/syntax.js";
14
+ import { COMMANDS_RUN_HINT, inferRequiresProject, missingActionError, noCommandError, requireArgs, RESOURCE_NAMES, unknownActionError, unknownFlagError, unknownResourceError, UsageError, } from "./cli/usage.js";
14
15
  import { cliVersionResult, } from "./cli/version.js";
15
16
  import { getCredentialsPath, } from "./config.js";
16
- import { canonicalStatusText, ClientValidationError, DataikuError, dataikuErrorCode, } from "./errors.js";
17
+ import { ClientValidationError, DataikuError, dataikuErrorCode, FORBIDDEN_HINT, NOT_FOUND_HINT, } from "./errors.js";
17
18
  import { appendCleanupLedgerEntry, findCleanupLedgerBindingViolation, preflightCleanupLedgerPath, readCleanupLedger, reserveCleanupLedgerDssUrl, } from "./utils/cleanup-ledger.js";
18
19
  import { canonicalDssUrl, } from "./utils/dss-url.js";
19
20
  import { sanitizeSecrets, } from "./utils/secret-sanitize.js";
@@ -26,6 +27,12 @@ function loadCommandRuntime() {
26
27
  contract = module;
27
28
  });
28
29
  }
30
+ // Plan builders (and the command helpers they share with live handlers) load
31
+ // only for --plan; flag validation needs just the registry.
32
+ let planBuildersLoad;
33
+ function loadPlanBuilders() {
34
+ return planBuildersLoad ??= import("./cli/plans.js");
35
+ }
29
36
  // ---------------------------------------------------------------------------
30
37
  // Arg parsing
31
38
  // ---------------------------------------------------------------------------
@@ -290,9 +297,6 @@ function supportedCommandFlags(entry) {
290
297
  }
291
298
  return supported;
292
299
  }
293
- function scopedCommandDiscoveryHint(resource, action) {
294
- return `Use \`dss commands run --fields ${resource}.${action}\` to list the flags this command supports.`;
295
- }
296
300
  /**
297
301
  * Resolve the registry field selectors for `commands run --fields`. Omitting the flag
298
302
  * selects the action summary; `--output` exports the full registry, but an explicitly supplied value carrying no selector is a
@@ -315,9 +319,8 @@ function validateSupportedCommandFlags(resource, action, flags) {
315
319
  return;
316
320
  const supported = supportedCommandFlags(entry);
317
321
  for (const flagName of Object.keys(flags)) {
318
- if (supported[flagName] !== true) {
319
- throw new UsageError(`Unknown flag --${flagName} for ${resource} ${action}`, "unknown_flag", scopedCommandDiscoveryHint(resource, action));
320
- }
322
+ if (supported[flagName] !== true)
323
+ throw unknownFlagError(`--${flagName}`, resource, action);
321
324
  }
322
325
  if (executionMode(flags).dryRun && !entry.dryRun) {
323
326
  throw new UsageError(`--dry-run is not supported for ${resource} ${action}.`, "unknown_flag", entry.flags.some((flag) => flag.name === "plan")
@@ -339,14 +342,12 @@ function flagIsProvided(flags, name, allowEmpty = false) {
339
342
  return allowEmpty || value.length > 0;
340
343
  }
341
344
  function validateRequiredCommandInputs(resource, action, args, flags, entry) {
342
- const argCount = entry.positionalArguments.filter((positional) => positional.required).length;
343
345
  const allowEmptyFlags = {};
344
346
  for (const flag of entry.flags) {
345
347
  if (flag.allowEmptyValue === true)
346
348
  allowEmptyFlags[flag.name] = true;
347
349
  }
348
- if (args.length < argCount)
349
- requireArgs(args, argCount, entry.usage);
350
+ requireEntryPositionals(entry, args);
350
351
  for (const flagName of entry.requiredFlags) {
351
352
  if (!flagIsProvided(flags, flagName, allowEmptyFlags[flagName] === true)) {
352
353
  throw new UsageError(`--${flagName} is required. Usage: ${entry.usage}`, "missing_required_flag");
@@ -367,6 +368,11 @@ function validateRegistryCommandInputs(resource, action, args, flags) {
367
368
  validateSupportedCommandFlags(resource, action, flags);
368
369
  validateRequiredCommandInputs(resource, action, args, flags, entry);
369
370
  }
371
+ function requireEntryPositionals(entry, args) {
372
+ const argCount = entry.positionalArguments.filter((positional) => positional.required).length;
373
+ if (args.length < argCount)
374
+ requireArgs(args, argCount, entry.usage);
375
+ }
370
376
  class BatchDryRunValidationComplete extends Error {
371
377
  constructor() {
372
378
  super("Batch dry-run validation reached client execution.");
@@ -527,8 +533,7 @@ function redactArgv(argv) {
527
533
  else {
528
534
  redacted.push(arg);
529
535
  const next = argv[index + 1];
530
- if (next !== undefined
531
- && (next === "-" || !next.startsWith("-") || isNegativeNumberToken(next))) {
536
+ if (next !== undefined && isFlagValueToken(next)) {
532
537
  redacted.push("***");
533
538
  index++;
534
539
  }
@@ -738,7 +743,24 @@ async function runMetaCommand(resource, rawAction, flags) {
738
743
  return { action, result: await commandsRunResult(flags), exitCode: 0, };
739
744
  }
740
745
  }
746
+ const COMMAND_SEARCH_LIMIT = 5;
741
747
  async function commandsRunResult(flags) {
748
+ const query = flags["contains"];
749
+ if (typeof query === "string") {
750
+ if (flags["fields"] !== undefined || flags["output"] !== undefined) {
751
+ throw new UsageError("--contains cannot be combined with --fields or --output.", "usage_error", contract.COMMANDS_USAGE);
752
+ }
753
+ const matches = contract.searchCommands(query);
754
+ if (matches.length > COMMAND_SEARCH_LIMIT) {
755
+ enqueueCliWarning({
756
+ code: "results_truncated",
757
+ shown: COMMAND_SEARCH_LIMIT,
758
+ total: matches.length,
759
+ hint: "Add words to --contains to narrow the search.",
760
+ });
761
+ }
762
+ return matches.slice(0, COMMAND_SEARCH_LIMIT);
763
+ }
742
764
  const selectors = commandRegistrySelectors(flags);
743
765
  const outputPath = flags["output"];
744
766
  if (selectors.length === 0 && typeof outputPath !== "string") {
@@ -990,7 +1012,7 @@ async function runBatch(flags) {
990
1012
  throw unknownActionError(resource, action, Object.keys(resourceActions));
991
1013
  validateSupportedCommandFlags(resource, action, stepFlags);
992
1014
  if (executionMode(stepFlags).plan) {
993
- result = contract.buildMutationPlan(resource, action, meta, positional.slice(2), stepFlags);
1015
+ result = (await loadPlanBuilders()).buildMutationPlan(resource, action, meta, positional.slice(2), stepFlags);
994
1016
  }
995
1017
  else {
996
1018
  if (!client) {
@@ -1000,6 +1022,8 @@ async function runBatch(flags) {
1000
1022
  await preflightCleanupLedgerForFlags(client, stepFlags);
1001
1023
  const stepArgs = positional.slice(2);
1002
1024
  result = await meta.handler(client, stepArgs, stepFlags);
1025
+ if (action === "list")
1026
+ result = shapeListResult(resource, result, stepFlags);
1003
1027
  await recordCleanupLedgerEntry(client, resource, action, stepArgs, stepFlags, result, projectKey);
1004
1028
  }
1005
1029
  const failureExitCode = commandFailureExitCode(result);
@@ -1070,36 +1094,6 @@ function summarizeCleanupFailures(failures) {
1070
1094
  : [];
1071
1095
  }));
1072
1096
  }
1073
- /**
1074
- * Positional arguments scanned with the same flag grammar as `parseArgs`, so
1075
- * error reporting can recover the invoked resource/action without depending
1076
- * on the command registry being loaded.
1077
- */
1078
- function rawPositionals(argv) {
1079
- const positionals = [];
1080
- for (let index = 0; index < argv.length; index++) {
1081
- const arg = argv[index];
1082
- if (arg === "--") {
1083
- positionals.push(...argv.slice(index + 1));
1084
- break;
1085
- }
1086
- if (arg.startsWith("--")) {
1087
- const name = arg.slice(2).split("=")[0] ?? "";
1088
- const canonical = FLAG_ALIASES[name] ?? name;
1089
- if (!arg.includes("=") && VALUE_FLAGS.has(canonical))
1090
- index++;
1091
- continue;
1092
- }
1093
- if (arg.length === 2 && arg[0] === "-" && arg[1] !== "-") {
1094
- const long = SHORT_FLAGS[arg[1]];
1095
- if (long && VALUE_FLAGS.has(long))
1096
- index++;
1097
- continue;
1098
- }
1099
- positionals.push(arg);
1100
- }
1101
- return positionals;
1102
- }
1103
1097
  function rawFlagValue(argv, flagName) {
1104
1098
  const longFlag = `--${flagName}`;
1105
1099
  for (let index = 0; index < argv.length; index++) {
@@ -1173,66 +1167,89 @@ function requestIdFromBody(body) {
1173
1167
  const DSS_DIAGNOSTIC_MESSAGE_FIELDS = ["detailedMessage", "message", "errorMessage",];
1174
1168
  const MAX_DSS_DIAGNOSTIC_LENGTH = 300;
1175
1169
  /**
1176
- * Credential material for redacting a server echo in a receipt: resolved
1177
- * through the same resolver every command uses (flag → env → saved), so the
1178
- * report path never grows a second credential-precedence convention.
1170
+ * Credential material for redacting a server echo in an error report: the API
1171
+ * key, resolved through the same resolver every command uses (flag → env →
1172
+ * saved), and credential values from the command's JSON input, which DSS
1173
+ * error text can echo back.
1179
1174
  */
1180
1175
  function cliSecretValues() {
1176
+ const inputCredentials = [...seenJsonInputCredentials(),];
1181
1177
  try {
1182
1178
  const { apiKey, } = resolveCredentials(parseArgs(process.argv.slice(2)).flags);
1183
- return apiKey ? [apiKey,] : [];
1179
+ return apiKey ? [apiKey, ...inputCredentials,] : inputCredentials;
1184
1180
  }
1185
1181
  catch {
1186
- return [];
1182
+ return inputCredentials;
1187
1183
  }
1188
1184
  }
1189
1185
  /**
1190
- * Bounded, sanitized server diagnostic preserved on an ambiguous mutation
1191
- * envelope (`dssMessage`/`dssErrorType`, next to the existing `dssCategory`):
1192
- * the DSS error text and Java error type the response body carried, so the
1193
- * receipt explains why the server failed while the mutation outcome stays
1194
- * unknown. Whitespace collapses to one line, credential material is redacted,
1195
- * long text truncates, and the raw body/stack never pass through.
1186
+ * Envelope details a DSS failure carries beyond the top-level fields: safe
1187
+ * response metadata (target, elapsed time), a truncated-body marker, and the
1188
+ * retry record only when a retry or timeout actually happened.
1189
+ */
1190
+ function dssErrorDetails(err) {
1191
+ const { requestId: _requestId, request_id: _requestIdAlt, errorId: _errorId, ...metadata } = safeResponseMetadata(err.body);
1192
+ return {
1193
+ ...metadata,
1194
+ ...(err.trustedTarget ? { target: err.trustedTarget, } : {}),
1195
+ ...(err.trustedElapsedMs !== undefined ? { elapsedMs: err.trustedElapsedMs, } : {}),
1196
+ ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1197
+ ...(err.retry && (err.retry.retries > 0 || err.retry.timedOut) ? { retry: err.retry, } : {}),
1198
+ };
1199
+ }
1200
+ /**
1201
+ * Bounded, sanitized server diagnostic: the DSS error text (`dssMessage`) and
1202
+ * Java error type (`dssErrorType`) the response body carried. Whitespace
1203
+ * collapses to one line, credential material is redacted, long text
1204
+ * truncates, and the raw body/stack never pass through.
1196
1205
  */
1197
1206
  function dssDiagnosticDetails(err) {
1207
+ let secrets;
1208
+ const diagnostic = (value) => {
1209
+ if (typeof value !== "string" || value.trim() === "")
1210
+ return undefined;
1211
+ secrets ??= cliSecretValues();
1212
+ const sanitized = sanitizeSecrets(value.replace(/\s+/g, " ").trim(), {
1213
+ sensitiveKeys: {},
1214
+ secrets,
1215
+ redactUrlUserinfo: true,
1216
+ });
1217
+ return sanitized.length > MAX_DSS_DIAGNOSTIC_LENGTH
1218
+ ? `${sanitized.slice(0, MAX_DSS_DIAGNOSTIC_LENGTH)}…`
1219
+ : sanitized;
1220
+ };
1221
+ let parsed;
1198
1222
  try {
1199
- const parsed = JSON.parse(err.body);
1200
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1201
- return {};
1202
- const record = parsed;
1203
- let secrets;
1204
- const diagnostic = (value) => {
1205
- if (typeof value !== "string" || value.trim() === "")
1206
- return undefined;
1207
- secrets ??= cliSecretValues();
1208
- const sanitized = sanitizeSecrets(value.replace(/\s+/g, " ").trim(), {
1209
- sensitiveKeys: {},
1210
- secrets,
1211
- redactUrlUserinfo: true,
1212
- });
1213
- return sanitized.length > MAX_DSS_DIAGNOSTIC_LENGTH
1214
- ? `${sanitized.slice(0, MAX_DSS_DIAGNOSTIC_LENGTH)}…`
1215
- : sanitized;
1216
- };
1217
- let message;
1218
- for (const field of DSS_DIAGNOSTIC_MESSAGE_FIELDS) {
1219
- message = diagnostic(record[field]);
1220
- if (message !== undefined)
1221
- break;
1222
- }
1223
- const errorType = diagnostic(record["errorType"]);
1224
- return {
1225
- ...(message !== undefined ? { dssMessage: message, } : {}),
1226
- ...(errorType !== undefined ? { dssErrorType: errorType, } : {}),
1227
- };
1223
+ parsed = JSON.parse(err.body);
1228
1224
  }
1229
1225
  catch {
1226
+ // Plain-text or HTML bodies, and the client's own transport detail for
1227
+ // status 0, surface through the bounded summary.
1228
+ const dssMessage = err.body.trim() === "" ? undefined : diagnostic(err.summary);
1229
+ return dssMessage === undefined ? {} : { dssMessage, };
1230
+ }
1231
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1230
1232
  return {};
1233
+ const record = parsed;
1234
+ let message;
1235
+ for (const field of DSS_DIAGNOSTIC_MESSAGE_FIELDS) {
1236
+ message = diagnostic(record[field]);
1237
+ if (message !== undefined)
1238
+ break;
1231
1239
  }
1240
+ const errorType = diagnostic(record["errorType"]);
1241
+ return {
1242
+ ...(message !== undefined ? { dssMessage: message, } : {}),
1243
+ ...(errorType !== undefined ? { dssErrorType: errorType, } : {}),
1244
+ };
1232
1245
  }
1233
1246
  function isAmbiguousMutationFailure(err, context) {
1234
1247
  if (!(err instanceof DataikuError))
1235
1248
  return false;
1249
+ // DSS definitively refused the operation (e.g. the database rejected the SQL):
1250
+ // nothing took effect, so there is no outcome to verify.
1251
+ if (err.definiteFailure)
1252
+ return false;
1236
1253
  const method = err.retry?.method.toUpperCase();
1237
1254
  if (!method || method === "GET" || method === "HEAD")
1238
1255
  return false;
@@ -1344,48 +1361,46 @@ function buildErrorReport(err, contextOverride) {
1344
1361
  status: err.status,
1345
1362
  retryable: false,
1346
1363
  requestId: err.requestId ?? requestIdFromBody(err.body),
1364
+ // The retry record proves how many times the mutation was dispatched.
1347
1365
  details: {
1348
- dssCategory: err.category,
1349
- ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1350
- ...dssDiagnosticDetails(err),
1351
- statusText: canonicalStatusText(err.status),
1352
1366
  idempotency: "none",
1367
+ ...dssErrorDetails(err),
1368
+ ...dssDiagnosticDetails(err),
1369
+ ...err.details,
1353
1370
  ...(err.retry ? { retry: err.retry, } : {}),
1354
1371
  },
1355
1372
  ...context,
1356
1373
  };
1357
1374
  }
1358
1375
  if (err instanceof DataikuError) {
1359
- const errorMessage = err.category === "not_found" && context.resource
1360
- ? `Not found: ${context.resource}${context.action ? ` ${context.action}` : ""}`
1361
- + `${context.projectKey ? ` in project ${context.projectKey}` : ""}`
1362
- + " — verify the object identifier and project key."
1363
- + `\nHint: ${err.retryHint}`
1364
- : err.safeMessage;
1365
- const safeMetadata = safeResponseMetadata(err.body);
1366
- if (err.trustedTarget)
1367
- safeMetadata.target = err.trustedTarget;
1368
- if (err.trustedElapsedMs !== undefined)
1369
- safeMetadata.elapsedMs = err.trustedElapsedMs;
1370
- const safeBody = JSON.stringify(safeMetadata);
1376
+ const { dssMessage, dssErrorType, } = dssDiagnosticDetails(err);
1377
+ const details = {
1378
+ ...dssErrorDetails(err),
1379
+ ...(dssErrorType ? { dssErrorType, } : {}),
1380
+ ...err.details,
1381
+ };
1382
+ // Generic hints name what the command targeted. DSS answers 403 (not
1383
+ // 404) for a project that does not exist: "Failed to read project
1384
+ // permissions". A 403 about something else keeps the generic hint.
1385
+ const inProject = context.projectKey ? ` in project ${context.projectKey}` : "";
1386
+ const projectLevel = dssMessage === undefined || /\bproject\b/i.test(dssMessage);
1387
+ const hint = err.retryHint === FORBIDDEN_HINT && context.projectKey && projectLevel
1388
+ ? `Check that project ${context.projectKey} exists (\`dss project list\`) and that this API key may access it.`
1389
+ : err.retryHint === NOT_FOUND_HINT && context.resource
1390
+ ? `Check the ${context.resource} identifier${inProject}${commandSyntaxTree(context.resource, "list") ? ` (\`dss ${context.resource} list\`)` : ""}.`
1391
+ : err.retryHint;
1371
1392
  return {
1372
1393
  type: "error",
1373
1394
  ok: false,
1374
- error: errorMessage,
1395
+ error: dssMessage ? `${err.safeMessage}: ${dssMessage}` : err.safeMessage,
1375
1396
  code: dataikuErrorCode(err.category),
1376
1397
  category: "dss",
1377
1398
  exitCode,
1378
- hint: err.retryHint,
1399
+ hint,
1379
1400
  status: err.status,
1380
1401
  retryable: err.retryable,
1381
1402
  requestId: err.requestId ?? requestIdFromBody(err.body),
1382
- details: {
1383
- dssCategory: err.category,
1384
- ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1385
- statusText: canonicalStatusText(err.status),
1386
- body: safeBody,
1387
- ...(err.retry ? { retry: err.retry, } : {}),
1388
- },
1403
+ ...(Object.keys(details).length > 0 ? { details, } : {}),
1389
1404
  ...context,
1390
1405
  };
1391
1406
  }
@@ -1463,11 +1478,17 @@ async function main() {
1463
1478
  await loadCommandRuntime();
1464
1479
  validateSupportedCommandFlags(resource, action, flags);
1465
1480
  const args = positional.slice(2);
1481
+ // A missing required positional is a usage error with the usage line, never
1482
+ // a handler crash. Required flags stay with the handlers' own validation,
1483
+ // which names the specific alternatives.
1484
+ const registryEntry = cachedCommandRegistry(resource)?.[action];
1485
+ if (registryEntry)
1486
+ requireEntryPositionals(registryEntry, args);
1466
1487
  // Command-local validation runs for live, --plan, and --dry-run alike, so a
1467
1488
  // bad identifier is a usage error before any plan is built or request made.
1468
1489
  actionMeta.validate?.(args, flags);
1469
1490
  if (executionMode(flags).plan) {
1470
- const plan = contract.buildMutationPlan(resource, action, actionMeta, args, flags);
1491
+ const plan = (await loadPlanBuilders()).buildMutationPlan(resource, action, actionMeta, args, flags);
1471
1492
  writeCommandResult(plan);
1472
1493
  return;
1473
1494
  }
@@ -1502,7 +1523,8 @@ async function main() {
1502
1523
  });
1503
1524
  assertCleanupLedgerSupported(resource, action, flags);
1504
1525
  await preflightCleanupLedgerForFlags(client, flags);
1505
- const result = await actionMeta.handler(client, args, flags);
1526
+ const handled = await actionMeta.handler(client, args, flags);
1527
+ const result = action === "list" ? shapeListResult(resource, handled, flags) : handled;
1506
1528
  await recordCleanupLedgerEntry(client, resource, action, args, flags, result, projectKey);
1507
1529
  const failureExitCode = commandFailureExitCode(result);
1508
1530
  if (failureExitCode !== undefined)
@@ -1,47 +1,49 @@
1
1
  import { type Static, type TSchema } from "@sinclair/typebox";
2
- import { AnalysesResource } from "./resources/analyses.js";
3
- import { ApiDeployerResource } from "./resources/api-deployer.js";
4
- import { ApiServicesResource } from "./resources/api-services.js";
5
- import { ApplicationsResource } from "./resources/applications.js";
6
- import { BundlesResource, ProjectDeployerResource } from "./resources/bundles.js";
7
- import { CodeEnvsResource } from "./resources/code-envs.js";
8
- import { ConnectionsResource } from "./resources/connections.js";
9
- import { ContinuousActivitiesResource } from "./resources/continuous-activities.js";
10
- import { DashboardsResource } from "./resources/dashboards.js";
11
- import { DataCollectionsResource } from "./resources/data-collections.js";
12
- import { DataQualityResource } from "./resources/data-quality.js";
13
- import { DatasetsResource } from "./resources/datasets.js";
14
- import { DiscussionsResource } from "./resources/discussions.js";
15
- import { FlowZonesResource } from "./resources/flow-zones.js";
16
- import { FoldersResource } from "./resources/folders.js";
17
- import { FuturesResource } from "./resources/futures.js";
18
- import { GroupsResource } from "./resources/groups.js";
19
- import { InsightsResource } from "./resources/insights.js";
20
- import { JobsResource } from "./resources/jobs.js";
21
- import { KnowledgeBanksResource } from "./resources/knowledge-banks.js";
22
- import { LlmsResource } from "./resources/llms.js";
23
- import { MacrosResource } from "./resources/macros.js";
24
- import { MeaningsResource } from "./resources/meanings.js";
25
- import { MetricsResource } from "./resources/metrics.js";
26
- import { MlTasksResource } from "./resources/ml-tasks.js";
27
- import { ModelEvaluationStoresResource } from "./resources/model-evaluation-stores.js";
28
- import { NotebooksResource } from "./resources/notebooks.js";
29
- import { PluginsResource } from "./resources/plugins.js";
30
- import { ProjectFoldersResource } from "./resources/project-folders.js";
31
- import { ProjectGitResource } from "./resources/project-git.js";
32
- import { ProjectLibraryResource } from "./resources/project-library.js";
33
- import { ProjectsResource } from "./resources/projects.js";
34
- import { RecipesResource } from "./resources/recipes.js";
35
- import { SavedModelsResource } from "./resources/saved-models.js";
36
- import { ScenariosResource } from "./resources/scenarios.js";
37
- import { SqlResource } from "./resources/sql.js";
38
- import { StatisticsResource } from "./resources/statistics.js";
39
- import { StreamingEndpointsResource } from "./resources/streaming-endpoints.js";
40
- import { UsersResource } from "./resources/users.js";
41
- import { VariablesResource } from "./resources/variables.js";
42
- import { WebappsResource } from "./resources/webapps.js";
43
- import { WikiResource } from "./resources/wiki.js";
44
- import { WorkspacesResource } from "./resources/workspaces.js";
2
+ import type { AnalysesResource } from "./resources/analyses.js";
3
+ import type { ApiDeployerResource } from "./resources/api-deployer.js";
4
+ import type { ApiServicesResource } from "./resources/api-services.js";
5
+ import type { ApplicationsResource } from "./resources/applications.js";
6
+ import type { BundlesResource, ProjectDeployerResource } from "./resources/bundles.js";
7
+ import type { CodeEnvsResource } from "./resources/code-envs.js";
8
+ import type { ConnectionsResource } from "./resources/connections.js";
9
+ import type { ContinuousActivitiesResource } from "./resources/continuous-activities.js";
10
+ import type { DashboardsResource } from "./resources/dashboards.js";
11
+ import type { DataCollectionsResource } from "./resources/data-collections.js";
12
+ import type { DataQualityResource } from "./resources/data-quality.js";
13
+ import type { DatasetsResource } from "./resources/datasets.js";
14
+ import type { DiscussionsResource } from "./resources/discussions.js";
15
+ import type { FlowZonesResource } from "./resources/flow-zones.js";
16
+ import type { FlowResource } from "./resources/flow.js";
17
+ import type { FoldersResource } from "./resources/folders.js";
18
+ import type { FuturesResource } from "./resources/futures.js";
19
+ import type { GroupsResource } from "./resources/groups.js";
20
+ import type { InsightsResource } from "./resources/insights.js";
21
+ import type { JobsResource } from "./resources/jobs.js";
22
+ import type { KnowledgeBanksResource } from "./resources/knowledge-banks.js";
23
+ import type { LlmsResource } from "./resources/llms.js";
24
+ import type { MacrosResource } from "./resources/macros.js";
25
+ import type { MeaningsResource } from "./resources/meanings.js";
26
+ import type { MetricsResource } from "./resources/metrics.js";
27
+ import type { MlTasksResource } from "./resources/ml-tasks.js";
28
+ import type { MlflowExtensionResource } from "./resources/mlflow-extension.js";
29
+ import type { ModelEvaluationStoresResource } from "./resources/model-evaluation-stores.js";
30
+ import type { NotebooksResource } from "./resources/notebooks.js";
31
+ import type { PluginsResource } from "./resources/plugins.js";
32
+ import type { ProjectFoldersResource } from "./resources/project-folders.js";
33
+ import type { ProjectGitResource } from "./resources/project-git.js";
34
+ import type { ProjectLibraryResource } from "./resources/project-library.js";
35
+ import type { ProjectsResource } from "./resources/projects.js";
36
+ import type { RecipesResource } from "./resources/recipes.js";
37
+ import type { SavedModelsResource } from "./resources/saved-models.js";
38
+ import type { ScenariosResource } from "./resources/scenarios.js";
39
+ import type { SqlResource } from "./resources/sql.js";
40
+ import type { StatisticsResource } from "./resources/statistics.js";
41
+ import type { StreamingEndpointsResource } from "./resources/streaming-endpoints.js";
42
+ import type { UsersResource } from "./resources/users.js";
43
+ import type { VariablesResource } from "./resources/variables.js";
44
+ import type { WebappsResource } from "./resources/webapps.js";
45
+ import type { WikiResource } from "./resources/wiki.js";
46
+ import type { WorkspacesResource } from "./resources/workspaces.js";
45
47
  /** One part of a {@link DataikuClient.uploadForm} multipart/form-data body. */
46
48
  export interface UploadFormPart {
47
49
  /** Form field name. */
@@ -82,6 +84,8 @@ export interface DataikuGetOptions {
82
84
  * server once instead of failing before any transport attempt.
83
85
  */
84
86
  noRetry?: boolean;
87
+ /** Extra request headers, e.g. `x-dku-mlflow-project-key` for the MLflow API. */
88
+ headers?: Record<string, string>;
85
89
  }
86
90
  export interface DataikuClientTraceEvent {
87
91
  type: "trace";
@@ -162,6 +166,8 @@ export declare class DataikuClient {
162
166
  private scenariosResource?;
163
167
  private foldersResource?;
164
168
  private flowZonesResource?;
169
+ private flowResource?;
170
+ private mlflowExtensionResource?;
165
171
  private variablesResource?;
166
172
  private connectionsResource?;
167
173
  private codeEnvsResource?;
@@ -206,6 +212,8 @@ export declare class DataikuClient {
206
212
  get scenarios(): ScenariosResource;
207
213
  get folders(): FoldersResource;
208
214
  get flowZones(): FlowZonesResource;
215
+ get flow(): FlowResource;
216
+ get mlflowExtension(): MlflowExtensionResource;
209
217
  get variables(): VariablesResource;
210
218
  get connections(): ConnectionsResource;
211
219
  get codeEnvs(): CodeEnvsResource;
@@ -298,6 +306,7 @@ export declare class DataikuClient {
298
306
  */
299
307
  post<T = unknown>(path: string, body?: unknown, options?: {
300
308
  retryMaxAttempts?: number;
309
+ headers?: Record<string, string>;
301
310
  }): Promise<T>;
302
311
  /**
303
312
  * POST returning the text body, under the same bounded buffering rules as