dataiku-sdk 3.6.0 → 3.7.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 (127) hide show
  1. package/README.md +14 -6
  2. package/bin/bun-version.js +14 -0
  3. package/bin/dss.js +23 -12
  4. package/dist/build-metadata.json +1 -1
  5. package/dist/packages/types/src/index.d.ts +74 -1
  6. package/dist/packages/types/src/index.js +53 -1
  7. package/dist/src/cli/coerce.d.ts +1 -0
  8. package/dist/src/cli/coerce.js +19 -15
  9. package/dist/src/cli/command-syntax.json +3521 -416
  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 +35 -4
  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.d.ts +5 -0
  18. package/dist/src/cli/commands/flow.js +57 -0
  19. package/dist/src/cli/commands/future.js +8 -0
  20. package/dist/src/cli/commands/index.js +6 -0
  21. package/dist/src/cli/commands/job.js +29 -6
  22. package/dist/src/cli/commands/metrics.js +21 -1
  23. package/dist/src/cli/commands/ml-task.d.ts +5 -0
  24. package/dist/src/cli/commands/ml-task.js +86 -16
  25. package/dist/src/cli/commands/mlflow.d.ts +11 -0
  26. package/dist/src/cli/commands/mlflow.js +121 -0
  27. package/dist/src/cli/commands/plugin.js +5 -11
  28. package/dist/src/cli/commands/project-deployer.js +70 -0
  29. package/dist/src/cli/commands/project-library.js +2 -5
  30. package/dist/src/cli/commands/project.js +8 -0
  31. package/dist/src/cli/commands/recipe.d.ts +9 -0
  32. package/dist/src/cli/commands/recipe.js +93 -7
  33. package/dist/src/cli/commands/saved-model.d.ts +1 -1
  34. package/dist/src/cli/commands/saved-model.js +9 -76
  35. package/dist/src/cli/commands/streaming-endpoint.js +30 -1
  36. package/dist/src/cli/commands/webapp.js +8 -0
  37. package/dist/src/cli/commands/wiki.js +22 -0
  38. package/dist/src/cli/commands/workspace.js +8 -0
  39. package/dist/src/cli/contract.d.ts +16 -15
  40. package/dist/src/cli/contract.js +139 -3404
  41. package/dist/src/cli/doctor.d.ts +13 -0
  42. package/dist/src/cli/doctor.js +41 -1
  43. package/dist/src/cli/flags.d.ts +7 -1
  44. package/dist/src/cli/flags.js +78 -6
  45. package/dist/src/cli/helpers/app-successor.js +1 -2
  46. package/dist/src/cli/helpers/documentation.d.ts +21 -0
  47. package/dist/src/cli/helpers/documentation.js +53 -0
  48. package/dist/src/cli/helpers/trained-model.d.ts +26 -0
  49. package/dist/src/cli/helpers/trained-model.js +144 -0
  50. package/dist/src/cli/list-output.d.ts +7 -0
  51. package/dist/src/cli/list-output.js +77 -0
  52. package/dist/src/cli/list-shapes.d.ts +21 -0
  53. package/dist/src/cli/list-shapes.js +111 -0
  54. package/dist/src/cli/output-schemas.d.ts +0 -14
  55. package/dist/src/cli/output-schemas.js +4 -7
  56. package/dist/src/cli/output.d.ts +0 -1
  57. package/dist/src/cli/output.js +27 -19
  58. package/dist/src/cli/plans.d.ts +14 -0
  59. package/dist/src/cli/plans.js +3684 -0
  60. package/dist/src/cli/usage.d.ts +7 -0
  61. package/dist/src/cli/usage.js +20 -1
  62. package/dist/src/cli/version.d.ts +2 -2
  63. package/dist/src/cli/version.js +2 -2
  64. package/dist/src/cli.js +127 -113
  65. package/dist/src/client.d.ts +52 -43
  66. package/dist/src/client.js +267 -96
  67. package/dist/src/errors.d.ts +11 -1
  68. package/dist/src/errors.js +31 -47
  69. package/dist/src/generated/action-output-schemas.json +345 -89
  70. package/dist/src/index.d.ts +5 -1
  71. package/dist/src/index.js +3 -0
  72. package/dist/src/resources/analyses.d.ts +8 -0
  73. package/dist/src/resources/analyses.js +13 -1
  74. package/dist/src/resources/bundles.d.ts +34 -0
  75. package/dist/src/resources/bundles.js +67 -0
  76. package/dist/src/resources/connections.d.ts +9 -0
  77. package/dist/src/resources/connections.js +15 -0
  78. package/dist/src/resources/data-quality.d.ts +20 -0
  79. package/dist/src/resources/data-quality.js +30 -0
  80. package/dist/src/resources/dataset-create.d.ts +19 -0
  81. package/dist/src/resources/dataset-create.js +66 -0
  82. package/dist/src/resources/datasets.d.ts +25 -15
  83. package/dist/src/resources/datasets.js +59 -111
  84. package/dist/src/resources/discussions.d.ts +5 -0
  85. package/dist/src/resources/discussions.js +7 -0
  86. package/dist/src/resources/documentation.d.ts +25 -0
  87. package/dist/src/resources/documentation.js +16 -0
  88. package/dist/src/resources/flow.d.ts +48 -0
  89. package/dist/src/resources/flow.js +65 -0
  90. package/dist/src/resources/futures.d.ts +23 -0
  91. package/dist/src/resources/futures.js +32 -0
  92. package/dist/src/resources/jobs.js +102 -2
  93. package/dist/src/resources/metrics.d.ts +10 -0
  94. package/dist/src/resources/metrics.js +10 -0
  95. package/dist/src/resources/ml-tasks.d.ts +65 -2
  96. package/dist/src/resources/ml-tasks.js +102 -28
  97. package/dist/src/resources/mlflow-extension.d.ts +73 -0
  98. package/dist/src/resources/mlflow-extension.js +105 -0
  99. package/dist/src/resources/projects.d.ts +6 -0
  100. package/dist/src/resources/projects.js +8 -0
  101. package/dist/src/resources/recipe-create.d.ts +65 -0
  102. package/dist/src/resources/recipe-create.js +338 -0
  103. package/dist/src/resources/recipes.d.ts +28 -29
  104. package/dist/src/resources/recipes.js +126 -245
  105. package/dist/src/resources/saved-models.d.ts +10 -6
  106. package/dist/src/resources/saved-models.js +16 -14
  107. package/dist/src/resources/scenarios.js +8 -14
  108. package/dist/src/resources/streaming-endpoints.d.ts +12 -0
  109. package/dist/src/resources/streaming-endpoints.js +15 -0
  110. package/dist/src/resources/trained-model.d.ts +60 -0
  111. package/dist/src/resources/trained-model.js +98 -0
  112. package/dist/src/resources/webapps.d.ts +8 -0
  113. package/dist/src/resources/webapps.js +10 -0
  114. package/dist/src/resources/wiki.d.ts +5 -0
  115. package/dist/src/resources/wiki.js +7 -0
  116. package/dist/src/resources/workspaces.d.ts +2 -0
  117. package/dist/src/resources/workspaces.js +4 -0
  118. package/dist/src/utils/response-file.js +20 -9
  119. package/dist/src/utils/secret-sanitize.d.ts +7 -0
  120. package/dist/src/utils/secret-sanitize.js +46 -0
  121. package/package.json +25 -26
  122. package/plugin.json +1 -1
  123. package/skills/dataiku-dss/SKILL.md +1 -1
  124. package/skills/dataiku-dss/references/authentication.md +3 -1
  125. package/skills/dataiku-dss/references/coding.md +1 -0
  126. package/skills/dataiku-dss/references/discovery.md +4 -6
  127. package/skills/dataiku-dss/references/troubleshooting.md +3 -2
@@ -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, isNegativeNumberToken, 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.");
@@ -738,7 +744,24 @@ async function runMetaCommand(resource, rawAction, flags) {
738
744
  return { action, result: await commandsRunResult(flags), exitCode: 0, };
739
745
  }
740
746
  }
747
+ const COMMAND_SEARCH_LIMIT = 5;
741
748
  async function commandsRunResult(flags) {
749
+ const query = flags["contains"];
750
+ if (typeof query === "string") {
751
+ if (flags["fields"] !== undefined || flags["output"] !== undefined) {
752
+ throw new UsageError("--contains cannot be combined with --fields or --output.", "usage_error", contract.COMMANDS_USAGE);
753
+ }
754
+ const matches = contract.searchCommands(query);
755
+ if (matches.length > COMMAND_SEARCH_LIMIT) {
756
+ enqueueCliWarning({
757
+ code: "results_truncated",
758
+ shown: COMMAND_SEARCH_LIMIT,
759
+ total: matches.length,
760
+ hint: "Add words to --contains to narrow the search.",
761
+ });
762
+ }
763
+ return matches.slice(0, COMMAND_SEARCH_LIMIT);
764
+ }
742
765
  const selectors = commandRegistrySelectors(flags);
743
766
  const outputPath = flags["output"];
744
767
  if (selectors.length === 0 && typeof outputPath !== "string") {
@@ -990,7 +1013,7 @@ async function runBatch(flags) {
990
1013
  throw unknownActionError(resource, action, Object.keys(resourceActions));
991
1014
  validateSupportedCommandFlags(resource, action, stepFlags);
992
1015
  if (executionMode(stepFlags).plan) {
993
- result = contract.buildMutationPlan(resource, action, meta, positional.slice(2), stepFlags);
1016
+ result = (await loadPlanBuilders()).buildMutationPlan(resource, action, meta, positional.slice(2), stepFlags);
994
1017
  }
995
1018
  else {
996
1019
  if (!client) {
@@ -1000,6 +1023,8 @@ async function runBatch(flags) {
1000
1023
  await preflightCleanupLedgerForFlags(client, stepFlags);
1001
1024
  const stepArgs = positional.slice(2);
1002
1025
  result = await meta.handler(client, stepArgs, stepFlags);
1026
+ if (action === "list")
1027
+ result = shapeListResult(resource, result, stepFlags);
1003
1028
  await recordCleanupLedgerEntry(client, resource, action, stepArgs, stepFlags, result, projectKey);
1004
1029
  }
1005
1030
  const failureExitCode = commandFailureExitCode(result);
@@ -1070,36 +1095,6 @@ function summarizeCleanupFailures(failures) {
1070
1095
  : [];
1071
1096
  }));
1072
1097
  }
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
1098
  function rawFlagValue(argv, flagName) {
1104
1099
  const longFlag = `--${flagName}`;
1105
1100
  for (let index = 0; index < argv.length; index++) {
@@ -1173,62 +1168,81 @@ function requestIdFromBody(body) {
1173
1168
  const DSS_DIAGNOSTIC_MESSAGE_FIELDS = ["detailedMessage", "message", "errorMessage",];
1174
1169
  const MAX_DSS_DIAGNOSTIC_LENGTH = 300;
1175
1170
  /**
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.
1171
+ * Credential material for redacting a server echo in an error report: the API
1172
+ * key, resolved through the same resolver every command uses (flag → env →
1173
+ * saved), and credential values from the command's JSON input, which DSS
1174
+ * error text can echo back.
1179
1175
  */
1180
1176
  function cliSecretValues() {
1177
+ const inputCredentials = [...seenJsonInputCredentials(),];
1181
1178
  try {
1182
1179
  const { apiKey, } = resolveCredentials(parseArgs(process.argv.slice(2)).flags);
1183
- return apiKey ? [apiKey,] : [];
1180
+ return apiKey ? [apiKey, ...inputCredentials,] : inputCredentials;
1184
1181
  }
1185
1182
  catch {
1186
- return [];
1183
+ return inputCredentials;
1187
1184
  }
1188
1185
  }
1189
1186
  /**
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.
1187
+ * Envelope details a DSS failure carries beyond the top-level fields: safe
1188
+ * response metadata (target, elapsed time), a truncated-body marker, and the
1189
+ * retry record only when a retry or timeout actually happened.
1190
+ */
1191
+ function dssErrorDetails(err) {
1192
+ const { requestId: _requestId, request_id: _requestIdAlt, errorId: _errorId, ...metadata } = safeResponseMetadata(err.body);
1193
+ return {
1194
+ ...metadata,
1195
+ ...(err.trustedTarget ? { target: err.trustedTarget, } : {}),
1196
+ ...(err.trustedElapsedMs !== undefined ? { elapsedMs: err.trustedElapsedMs, } : {}),
1197
+ ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1198
+ ...(err.retry && (err.retry.retries > 0 || err.retry.timedOut) ? { retry: err.retry, } : {}),
1199
+ };
1200
+ }
1201
+ /**
1202
+ * Bounded, sanitized server diagnostic: the DSS error text (`dssMessage`) and
1203
+ * Java error type (`dssErrorType`) the response body carried. Whitespace
1204
+ * collapses to one line, credential material is redacted, long text
1205
+ * truncates, and the raw body/stack never pass through.
1196
1206
  */
1197
1207
  function dssDiagnosticDetails(err) {
1208
+ let secrets;
1209
+ const diagnostic = (value) => {
1210
+ if (typeof value !== "string" || value.trim() === "")
1211
+ return undefined;
1212
+ secrets ??= cliSecretValues();
1213
+ const sanitized = sanitizeSecrets(value.replace(/\s+/g, " ").trim(), {
1214
+ sensitiveKeys: {},
1215
+ secrets,
1216
+ redactUrlUserinfo: true,
1217
+ });
1218
+ return sanitized.length > MAX_DSS_DIAGNOSTIC_LENGTH
1219
+ ? `${sanitized.slice(0, MAX_DSS_DIAGNOSTIC_LENGTH)}…`
1220
+ : sanitized;
1221
+ };
1222
+ let parsed;
1198
1223
  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
- };
1224
+ parsed = JSON.parse(err.body);
1228
1225
  }
1229
1226
  catch {
1227
+ // Plain-text or HTML bodies, and the client's own transport detail for
1228
+ // status 0, surface through the bounded summary.
1229
+ const dssMessage = err.body.trim() === "" ? undefined : diagnostic(err.summary);
1230
+ return dssMessage === undefined ? {} : { dssMessage, };
1231
+ }
1232
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1230
1233
  return {};
1234
+ const record = parsed;
1235
+ let message;
1236
+ for (const field of DSS_DIAGNOSTIC_MESSAGE_FIELDS) {
1237
+ message = diagnostic(record[field]);
1238
+ if (message !== undefined)
1239
+ break;
1231
1240
  }
1241
+ const errorType = diagnostic(record["errorType"]);
1242
+ return {
1243
+ ...(message !== undefined ? { dssMessage: message, } : {}),
1244
+ ...(errorType !== undefined ? { dssErrorType: errorType, } : {}),
1245
+ };
1232
1246
  }
1233
1247
  function isAmbiguousMutationFailure(err, context) {
1234
1248
  if (!(err instanceof DataikuError))
@@ -1344,48 +1358,41 @@ function buildErrorReport(err, contextOverride) {
1344
1358
  status: err.status,
1345
1359
  retryable: false,
1346
1360
  requestId: err.requestId ?? requestIdFromBody(err.body),
1361
+ // The retry record proves how many times the mutation was dispatched.
1347
1362
  details: {
1348
- dssCategory: err.category,
1349
- ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1350
- ...dssDiagnosticDetails(err),
1351
- statusText: canonicalStatusText(err.status),
1352
1363
  idempotency: "none",
1364
+ ...dssErrorDetails(err),
1365
+ ...dssDiagnosticDetails(err),
1353
1366
  ...(err.retry ? { retry: err.retry, } : {}),
1354
1367
  },
1355
1368
  ...context,
1356
1369
  };
1357
1370
  }
1358
1371
  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);
1372
+ const { dssMessage, dssErrorType, } = dssDiagnosticDetails(err);
1373
+ const details = { ...dssErrorDetails(err), ...(dssErrorType ? { dssErrorType, } : {}), };
1374
+ // Generic hints name what the command targeted. DSS answers 403 (not
1375
+ // 404) for a project that does not exist: "Failed to read project
1376
+ // permissions". A 403 about something else keeps the generic hint.
1377
+ const inProject = context.projectKey ? ` in project ${context.projectKey}` : "";
1378
+ const projectLevel = dssMessage === undefined || /\bproject\b/i.test(dssMessage);
1379
+ const hint = err.retryHint === FORBIDDEN_HINT && context.projectKey && projectLevel
1380
+ ? `Check that project ${context.projectKey} exists (\`dss project list\`) and that this API key may access it.`
1381
+ : err.retryHint === NOT_FOUND_HINT && context.resource
1382
+ ? `Check the ${context.resource} identifier${inProject}${commandSyntaxTree(context.resource, "list") ? ` (\`dss ${context.resource} list\`)` : ""}.`
1383
+ : err.retryHint;
1371
1384
  return {
1372
1385
  type: "error",
1373
1386
  ok: false,
1374
- error: errorMessage,
1387
+ error: dssMessage ? `${err.safeMessage}: ${dssMessage}` : err.safeMessage,
1375
1388
  code: dataikuErrorCode(err.category),
1376
1389
  category: "dss",
1377
1390
  exitCode,
1378
- hint: err.retryHint,
1391
+ hint,
1379
1392
  status: err.status,
1380
1393
  retryable: err.retryable,
1381
1394
  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
- },
1395
+ ...(Object.keys(details).length > 0 ? { details, } : {}),
1389
1396
  ...context,
1390
1397
  };
1391
1398
  }
@@ -1463,11 +1470,17 @@ async function main() {
1463
1470
  await loadCommandRuntime();
1464
1471
  validateSupportedCommandFlags(resource, action, flags);
1465
1472
  const args = positional.slice(2);
1473
+ // A missing required positional is a usage error with the usage line, never
1474
+ // a handler crash. Required flags stay with the handlers' own validation,
1475
+ // which names the specific alternatives.
1476
+ const registryEntry = cachedCommandRegistry(resource)?.[action];
1477
+ if (registryEntry)
1478
+ requireEntryPositionals(registryEntry, args);
1466
1479
  // Command-local validation runs for live, --plan, and --dry-run alike, so a
1467
1480
  // bad identifier is a usage error before any plan is built or request made.
1468
1481
  actionMeta.validate?.(args, flags);
1469
1482
  if (executionMode(flags).plan) {
1470
- const plan = contract.buildMutationPlan(resource, action, actionMeta, args, flags);
1483
+ const plan = (await loadPlanBuilders()).buildMutationPlan(resource, action, actionMeta, args, flags);
1471
1484
  writeCommandResult(plan);
1472
1485
  return;
1473
1486
  }
@@ -1502,7 +1515,8 @@ async function main() {
1502
1515
  });
1503
1516
  assertCleanupLedgerSupported(resource, action, flags);
1504
1517
  await preflightCleanupLedgerForFlags(client, flags);
1505
- const result = await actionMeta.handler(client, args, flags);
1518
+ const handled = await actionMeta.handler(client, args, flags);
1519
+ const result = action === "list" ? shapeListResult(resource, handled, flags) : handled;
1506
1520
  await recordCleanupLedgerEntry(client, resource, action, args, flags, result, projectKey);
1507
1521
  const failureExitCode = commandFailureExitCode(result);
1508
1522
  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