dataiku-sdk 3.2.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 (120) hide show
  1. package/README.md +149 -46
  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/cli/commands/app.js +57 -15
  6. package/dist/src/cli/commands/bundle.js +1 -1
  7. package/dist/src/cli/commands/code-env.js +13 -7
  8. package/dist/src/cli/commands/connection.d.ts +9 -0
  9. package/dist/src/cli/commands/connection.js +267 -1
  10. package/dist/src/cli/commands/data-collection.d.ts +2 -0
  11. package/dist/src/cli/commands/data-collection.js +164 -0
  12. package/dist/src/cli/commands/dataset.js +161 -32
  13. package/dist/src/cli/commands/future.js +2 -2
  14. package/dist/src/cli/commands/group.d.ts +2 -0
  15. package/dist/src/cli/commands/group.js +109 -0
  16. package/dist/src/cli/commands/index.js +151 -78
  17. package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
  18. package/dist/src/cli/commands/knowledge-bank.js +80 -0
  19. package/dist/src/cli/commands/llm.d.ts +2 -0
  20. package/dist/src/cli/commands/llm.js +93 -0
  21. package/dist/src/cli/commands/macro.d.ts +2 -0
  22. package/dist/src/cli/commands/macro.js +221 -0
  23. package/dist/src/cli/commands/ml-task.js +18 -6
  24. package/dist/src/cli/commands/notebook.js +6 -2
  25. package/dist/src/cli/commands/plugin.d.ts +2 -0
  26. package/dist/src/cli/commands/plugin.js +799 -0
  27. package/dist/src/cli/commands/project-folder.d.ts +2 -0
  28. package/dist/src/cli/commands/project-folder.js +197 -0
  29. package/dist/src/cli/commands/project-git.js +14 -54
  30. package/dist/src/cli/commands/project.js +52 -6
  31. package/dist/src/cli/commands/recipe.js +36 -1
  32. package/dist/src/cli/commands/saved-model.d.ts +5 -0
  33. package/dist/src/cli/commands/saved-model.js +388 -1
  34. package/dist/src/cli/commands/scenario.js +138 -1
  35. package/dist/src/cli/commands/user.d.ts +8 -0
  36. package/dist/src/cli/commands/user.js +296 -0
  37. package/dist/src/cli/contract.d.ts +1 -2
  38. package/dist/src/cli/contract.js +1089 -103
  39. package/dist/src/cli/flags.js +30 -1
  40. package/dist/src/cli/helpers/app-successor.d.ts +19 -4
  41. package/dist/src/cli/helpers/app-successor.js +90 -30
  42. package/dist/src/cli/helpers/cleanup.js +3 -1
  43. package/dist/src/cli/helpers/flow-zone.js +1 -2
  44. package/dist/src/cli/output.d.ts +1 -1
  45. package/dist/src/cli/output.js +23 -10
  46. package/dist/src/cli/usage.d.ts +1 -0
  47. package/dist/src/cli/usage.js +47 -1
  48. package/dist/src/cli.js +147 -41
  49. package/dist/src/client.d.ts +83 -5
  50. package/dist/src/client.js +185 -29
  51. package/dist/src/errors.d.ts +3 -0
  52. package/dist/src/errors.js +2 -0
  53. package/dist/src/index.d.ts +16 -7
  54. package/dist/src/index.js +11 -2
  55. package/dist/src/resources/analyses.js +1 -8
  56. package/dist/src/resources/applications.d.ts +8 -6
  57. package/dist/src/resources/applications.js +21 -21
  58. package/dist/src/resources/base.d.ts +4 -0
  59. package/dist/src/resources/base.js +15 -0
  60. package/dist/src/resources/bundles.d.ts +9 -4
  61. package/dist/src/resources/bundles.js +10 -5
  62. package/dist/src/resources/connections.d.ts +78 -1
  63. package/dist/src/resources/connections.js +132 -1
  64. package/dist/src/resources/data-collections.d.ts +118 -0
  65. package/dist/src/resources/data-collections.js +49 -0
  66. package/dist/src/resources/datasets.d.ts +69 -0
  67. package/dist/src/resources/datasets.js +99 -1
  68. package/dist/src/resources/futures.d.ts +4 -1
  69. package/dist/src/resources/futures.js +38 -6
  70. package/dist/src/resources/groups.d.ts +47 -0
  71. package/dist/src/resources/groups.js +56 -0
  72. package/dist/src/resources/jobs.d.ts +8 -13
  73. package/dist/src/resources/jobs.js +41 -16
  74. package/dist/src/resources/knowledge-banks.d.ts +58 -0
  75. package/dist/src/resources/knowledge-banks.js +61 -0
  76. package/dist/src/resources/llms.d.ts +92 -0
  77. package/dist/src/resources/llms.js +62 -0
  78. package/dist/src/resources/macros.d.ts +121 -0
  79. package/dist/src/resources/macros.js +231 -0
  80. package/dist/src/resources/ml-tasks.d.ts +37 -2
  81. package/dist/src/resources/ml-tasks.js +100 -20
  82. package/dist/src/resources/model-evaluation-stores.js +1 -8
  83. package/dist/src/resources/notebooks.d.ts +43 -7
  84. package/dist/src/resources/notebooks.js +53 -13
  85. package/dist/src/resources/plugins.d.ts +265 -0
  86. package/dist/src/resources/plugins.js +458 -0
  87. package/dist/src/resources/project-folders.d.ts +71 -0
  88. package/dist/src/resources/project-folders.js +57 -0
  89. package/dist/src/resources/project-git.d.ts +6 -0
  90. package/dist/src/resources/project-git.js +69 -42
  91. package/dist/src/resources/project-library.d.ts +0 -1
  92. package/dist/src/resources/project-library.js +1 -17
  93. package/dist/src/resources/projects.d.ts +19 -1
  94. package/dist/src/resources/projects.js +27 -1
  95. package/dist/src/resources/recipes.d.ts +15 -1
  96. package/dist/src/resources/recipes.js +73 -6
  97. package/dist/src/resources/saved-models.d.ts +134 -1
  98. package/dist/src/resources/saved-models.js +214 -6
  99. package/dist/src/resources/scenarios.d.ts +105 -0
  100. package/dist/src/resources/scenarios.js +232 -106
  101. package/dist/src/resources/sql.js +5 -1
  102. package/dist/src/resources/users.d.ts +129 -0
  103. package/dist/src/resources/users.js +128 -0
  104. package/dist/src/resources/webapps.d.ts +15 -2
  105. package/dist/src/resources/webapps.js +18 -4
  106. package/dist/src/schemas.d.ts +2 -2
  107. package/dist/src/schemas.js +1 -1
  108. package/dist/src/utils/deep-merge.d.ts +1 -1
  109. package/dist/src/utils/polling.d.ts +20 -0
  110. package/dist/src/utils/polling.js +27 -0
  111. package/dist/src/utils/response-file.js +1 -1
  112. package/dist/src/utils/secret-sanitize.d.ts +54 -0
  113. package/dist/src/utils/secret-sanitize.js +101 -0
  114. package/package.json +6 -5
  115. package/packages/types/dist/index.d.ts +102 -0
  116. package/packages/types/dist/index.js +76 -0
  117. package/plugin.json +1 -1
  118. package/skills/dataiku-dss/SKILL.md +7 -5
  119. package/skills/dataiku-dss/references/app-releases.md +8 -7
  120. package/skills/dataiku-dss/references/discovery.md +4 -4
package/dist/src/cli.js CHANGED
@@ -3,28 +3,26 @@ import * as fs from "node:fs/promises";
3
3
  import { homedir, } from "node:os";
4
4
  import { resolve, } from "node:path";
5
5
  import { num, unknownJsonInput, } from "./cli/coerce.js";
6
+ import { commands, } from "./cli/commands/index.js";
6
7
  import { dataikuEnvironmentEnabled, loadEnvFile, } from "./cli/env.js";
7
8
  import { executionMode, FLAG_ALIASES, isNegativeNumberToken, parseArgs, SHORT_FLAGS, VALUE_FLAGS, } from "./cli/flags.js";
8
9
  import { cleanupLedgerEntry, } from "./cli/helpers/cleanup.js";
9
10
  import { commandFailureExitCode, CommandResultFailure, flushCliWarnings, isFailedWaitResult, planResult, projectResultFields, setOutputFieldProjection, writeCommandResult, } from "./cli/output.js";
10
11
  import { currentCommandContext, resolveCredentials, resolveLoginCredentials, } from "./cli/runtime.js";
11
- import { COMMANDS_RUN_HINT, missingActionError, noCommandError, requireArgs, unknownActionError, unknownResourceError, UsageError, } from "./cli/usage.js";
12
+ import { COMMANDS_RUN_HINT, inferRequiresProject, missingActionError, noCommandError, requireArgs, RESOURCE_NAMES, unknownActionError, unknownResourceError, UsageError, } from "./cli/usage.js";
12
13
  import { cliVersionResult, } from "./cli/version.js";
13
14
  import { getCredentialsPath, } from "./config.js";
14
15
  import { canonicalStatusText, ClientValidationError, DataikuError, dataikuErrorCode, } from "./errors.js";
15
16
  import { appendCleanupLedgerEntry, findCleanupLedgerBindingViolation, preflightCleanupLedgerPath, readCleanupLedger, reserveCleanupLedgerDssUrl, } from "./utils/cleanup-ledger.js";
16
17
  import { canonicalDssUrl, } from "./utils/dss-url.js";
17
- let commands;
18
+ import { sanitizeSecrets, } from "./utils/secret-sanitize.js";
18
19
  let contract;
19
20
  let commandRuntimeLoad;
20
- // Dispatch and error reporting initialize these bindings before synchronous validation.
21
+ // Command definitions are resource-lazy; full contract metadata loads only
22
+ // after a valid action is selected or explicit discovery is requested.
21
23
  function loadCommandRuntime() {
22
- return commandRuntimeLoad ??= Promise.all([
23
- import("./cli/commands/index.js"),
24
- import("./cli/contract.js"),
25
- ]).then(([commandModule, contractModule,]) => {
26
- commands = commandModule.commands;
27
- contract = contractModule;
24
+ return commandRuntimeLoad ??= import("./cli/contract.js").then((module) => {
25
+ contract = module;
28
26
  });
29
27
  }
30
28
  // ---------------------------------------------------------------------------
@@ -279,6 +277,8 @@ const META_COMMAND_RESOURCES = {
279
277
  };
280
278
  const commandRegistryCache = new Map();
281
279
  function cachedCommandRegistry(resource) {
280
+ if (!contract)
281
+ return undefined;
282
282
  if (!commandRegistryCache.has(resource)) {
283
283
  commandRegistryCache.set(resource, contract.buildCommandRegistry(resource)[resource]);
284
284
  }
@@ -415,11 +415,21 @@ const batchDryRunClientHandler = {
415
415
  throw new BatchDryRunValidationComplete();
416
416
  },
417
417
  };
418
- const batchDryRunClient = new Proxy(batchDryRunClientTarget, batchDryRunClientHandler);
418
+ const batchDryRunResource = new Proxy(batchDryRunClientTarget, batchDryRunClientHandler);
419
+ let batchDryRunClient;
419
420
  async function validateDryRunHandlerPreconditions(handler, args, flags) {
420
421
  const readsFromStdin = flags["stdin"] === true || flags["sql"] === "-";
421
422
  if (readsFromStdin)
422
423
  return;
424
+ if (!batchDryRunClient) {
425
+ const { DataikuClient, } = await import("./client.js");
426
+ batchDryRunClient = new Proxy(new DataikuClient({
427
+ url: "http://dry-run.invalid",
428
+ apiKey: "dry-run-validation",
429
+ }), {
430
+ get: (_target, property) => property === "then" ? undefined : batchDryRunResource,
431
+ });
432
+ }
423
433
  try {
424
434
  await handler(batchDryRunClient, args, flags);
425
435
  }
@@ -760,9 +770,8 @@ function batchPlan(flags) {
760
770
  });
761
771
  }
762
772
  async function runMetaCommand(resource, action, flags) {
763
- if (resource !== "version")
764
- await loadCommandRuntime();
765
773
  if (resource === "doctor") {
774
+ await loadCommandRuntime();
766
775
  if (action !== undefined && action !== "run") {
767
776
  throw unknownActionError("doctor", action, ["run",]);
768
777
  }
@@ -772,6 +781,7 @@ async function runMetaCommand(resource, action, flags) {
772
781
  return { action: "run", result, exitCode, };
773
782
  }
774
783
  if (resource === "auth") {
784
+ await loadCommandRuntime();
775
785
  const validActions = Object.keys(contract.AUTH_ACTIONS);
776
786
  if (!action) {
777
787
  throw missingActionError("auth", validActions, "dss auth login --url URL --api-key KEY");
@@ -787,6 +797,7 @@ async function runMetaCommand(resource, action, flags) {
787
797
  return { action, result: await authMeta.handler(flags), exitCode: 0, };
788
798
  }
789
799
  if (resource === "install-skill") {
800
+ await loadCommandRuntime();
790
801
  if (action !== undefined && action !== "run") {
791
802
  throw unknownActionError("install-skill", action, ["run",]);
792
803
  }
@@ -794,6 +805,7 @@ async function runMetaCommand(resource, action, flags) {
794
805
  return { action: "run", result: await runInstallSkill(flags), exitCode: 0, };
795
806
  }
796
807
  if (resource === "agent") {
808
+ await loadCommandRuntime();
797
809
  if (!action)
798
810
  throw missingActionError("agent", ["contract",], contract.AGENT_CONTRACT_USAGE);
799
811
  currentCommandContext.action = action;
@@ -802,6 +814,7 @@ async function runMetaCommand(resource, action, flags) {
802
814
  return { action, result: contract.buildAgentContract(), exitCode: 0, };
803
815
  }
804
816
  if (resource === "commands") {
817
+ await loadCommandRuntime();
805
818
  if (!action)
806
819
  throw missingActionError("commands", ["run",], contract.COMMANDS_USAGE);
807
820
  currentCommandContext.action = action;
@@ -809,24 +822,28 @@ async function runMetaCommand(resource, action, flags) {
809
822
  throw unknownActionError("commands", action, ["run",]);
810
823
  validateSupportedCommandFlags("commands", "run", flags);
811
824
  const selectors = commandRegistrySelectors(flags);
812
- const registry = contract.buildCommandRegistry();
825
+ const outputPath = flags["output"];
826
+ if (selectors.length === 0 && typeof outputPath !== "string") {
827
+ return { action, result: contract.commandActionSummary(), exitCode: 0, };
828
+ }
829
+ const registry = selectors.length === 0 ? contract.buildCommandRegistry() : {};
813
830
  for (const selector of selectors) {
814
831
  const selectorParts = selector.split(".");
815
832
  if (selectorParts.some((part) => part.length === 0)) {
816
833
  throw new UsageError(`Invalid --fields selector: ${selector}. Expected RESOURCE or RESOURCE.ACTION[.FIELD...].`, "usage_error", contract.COMMANDS_USAGE, { selector, });
817
834
  }
818
835
  const [selectedResource, selectedAction,] = selectorParts;
819
- const resourceActions = registry[selectedResource];
836
+ const resourceActions = cachedCommandRegistry(selectedResource);
820
837
  if (!resourceActions)
821
838
  throw unknownResourceError(selectedResource);
839
+ registry[selectedResource] = resourceActions;
822
840
  if (selectedAction && !resourceActions[selectedAction]) {
823
841
  throw unknownActionError(selectedResource, selectedAction, Object.keys(resourceActions));
824
842
  }
825
843
  }
826
- const outputPath = flags["output"];
827
844
  const selectedRegistry = selectors.length === 0
828
845
  ? registry
829
- : projectResultFields(registry, selectors);
846
+ : projectResultFields(registry, selectors, () => Object.keys(contract.commandActionSummary()).sort());
830
847
  if (typeof outputPath === "string") {
831
848
  const exported = selectedRegistry;
832
849
  await fs.writeFile(outputPath, `${JSON.stringify(exported)}\n`, "utf-8");
@@ -834,7 +851,7 @@ async function runMetaCommand(resource, action, flags) {
834
851
  }
835
852
  return {
836
853
  action,
837
- result: selectors.length === 0 ? contract.commandActionSummary(registry) : selectedRegistry,
854
+ result: selectedRegistry,
838
855
  exitCode: 0,
839
856
  };
840
857
  }
@@ -846,6 +863,7 @@ async function runMetaCommand(resource, action, flags) {
846
863
  return { action: "run", result: cliVersionResult(), exitCode: 0, };
847
864
  }
848
865
  if (resource === "cleanup") {
866
+ await loadCommandRuntime();
849
867
  if (action !== undefined && action !== "run") {
850
868
  throw unknownActionError("cleanup", action, ["run",]);
851
869
  }
@@ -857,6 +875,7 @@ async function runMetaCommand(resource, action, flags) {
857
875
  return { action: "run", result, exitCode, };
858
876
  }
859
877
  if (resource === "fixtures") {
878
+ await loadCommandRuntime();
860
879
  if (action !== undefined && action !== "run") {
861
880
  throw unknownActionError("fixtures", action, ["run",]);
862
881
  }
@@ -865,6 +884,7 @@ async function runMetaCommand(resource, action, flags) {
865
884
  return { action: "run", result: await runFixtures(flags), exitCode: 0, };
866
885
  }
867
886
  if (resource === "batch") {
887
+ await loadCommandRuntime();
868
888
  if (action !== undefined && action !== "run") {
869
889
  throw unknownActionError("batch", action, ["run",]);
870
890
  }
@@ -1181,27 +1201,12 @@ function summarizeCleanupFailures(failures) {
1181
1201
  : [];
1182
1202
  }));
1183
1203
  }
1184
- function rawFlagValue(argv, flagName) {
1185
- const longFlag = `--${flagName}`;
1186
- for (let index = 0; index < argv.length; index++) {
1187
- const arg = argv[index];
1188
- if (arg === longFlag) {
1189
- const next = argv[index + 1];
1190
- return next && !next.startsWith("-") ? next : undefined;
1191
- }
1192
- if (arg.startsWith(`${longFlag}=`))
1193
- return arg.slice(longFlag.length + 1);
1194
- }
1195
- return undefined;
1196
- }
1197
- function commandIsProjectScoped(resource, action) {
1198
- if (!resource)
1199
- return false;
1200
- const usage = commands[resource]?.[action ?? ""]?.usage ?? "";
1201
- return contract.inferRequiresProject(resource, action ?? "", usage);
1202
- }
1203
- function rawCommandContext() {
1204
- const argv = process.argv.slice(2);
1204
+ /**
1205
+ * Positional arguments scanned with the same flag grammar as `parseArgs`, so
1206
+ * error reporting can recover the invoked resource/action without depending
1207
+ * on the command registry being loaded.
1208
+ */
1209
+ function rawPositionals(argv) {
1205
1210
  const positionals = [];
1206
1211
  for (let index = 0; index < argv.length; index++) {
1207
1212
  const arg = argv[index];
@@ -1224,6 +1229,30 @@ function rawCommandContext() {
1224
1229
  }
1225
1230
  positionals.push(arg);
1226
1231
  }
1232
+ return positionals;
1233
+ }
1234
+ function rawFlagValue(argv, flagName) {
1235
+ const longFlag = `--${flagName}`;
1236
+ for (let index = 0; index < argv.length; index++) {
1237
+ const arg = argv[index];
1238
+ if (arg === longFlag) {
1239
+ const next = argv[index + 1];
1240
+ return next && !next.startsWith("-") ? next : undefined;
1241
+ }
1242
+ if (arg.startsWith(`${longFlag}=`))
1243
+ return arg.slice(longFlag.length + 1);
1244
+ }
1245
+ return undefined;
1246
+ }
1247
+ function commandIsProjectScoped(resource, action) {
1248
+ if (!resource)
1249
+ return false;
1250
+ const usage = commands[resource]?.[action ?? ""]?.usage ?? "";
1251
+ return inferRequiresProject(resource, action ?? "", usage);
1252
+ }
1253
+ function rawCommandContext() {
1254
+ const argv = process.argv.slice(2);
1255
+ const positionals = rawPositionals(argv);
1227
1256
  const resource = currentCommandContext.resource ?? positionals[0];
1228
1257
  const action = currentCommandContext.action ?? positionals[1];
1229
1258
  const explicitProjectKey = rawFlagValue(argv, "project-key") ?? rawFlagValue(argv, "project");
@@ -1274,6 +1303,67 @@ function requestIdFromBody(body) {
1274
1303
  }
1275
1304
  return undefined;
1276
1305
  }
1306
+ /** DSS diagnostic fields, in the resource layer's read order (detailedMessage → message → errorMessage). */
1307
+ const DSS_DIAGNOSTIC_MESSAGE_FIELDS = ["detailedMessage", "message", "errorMessage",];
1308
+ const MAX_DSS_DIAGNOSTIC_LENGTH = 300;
1309
+ /**
1310
+ * Credential material for redacting a server echo in a receipt: resolved
1311
+ * through the same resolver every command uses (flag → env → saved), so the
1312
+ * report path never grows a second credential-precedence convention.
1313
+ */
1314
+ function cliSecretValues() {
1315
+ try {
1316
+ const { apiKey, } = resolveCredentials(parseArgs(process.argv.slice(2)).flags);
1317
+ return apiKey ? [apiKey,] : [];
1318
+ }
1319
+ catch {
1320
+ return [];
1321
+ }
1322
+ }
1323
+ /**
1324
+ * Bounded, sanitized server diagnostic preserved on an ambiguous mutation
1325
+ * envelope (`dssMessage`/`dssErrorType`, next to the existing `dssCategory`):
1326
+ * the DSS error text and Java error type the response body carried, so the
1327
+ * receipt explains why the server failed while the mutation outcome stays
1328
+ * unknown. Whitespace collapses to one line, credential material is redacted,
1329
+ * long text truncates, and the raw body/stack never pass through.
1330
+ */
1331
+ function dssDiagnosticDetails(err) {
1332
+ try {
1333
+ const parsed = JSON.parse(err.body);
1334
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
1335
+ return {};
1336
+ const record = parsed;
1337
+ let secrets;
1338
+ const diagnostic = (value) => {
1339
+ if (typeof value !== "string" || value.trim() === "")
1340
+ return undefined;
1341
+ secrets ??= cliSecretValues();
1342
+ const sanitized = sanitizeSecrets(value.replace(/\s+/g, " ").trim(), {
1343
+ sensitiveKeys: {},
1344
+ secrets,
1345
+ redactUrlUserinfo: true,
1346
+ });
1347
+ return sanitized.length > MAX_DSS_DIAGNOSTIC_LENGTH
1348
+ ? `${sanitized.slice(0, MAX_DSS_DIAGNOSTIC_LENGTH)}…`
1349
+ : sanitized;
1350
+ };
1351
+ let message;
1352
+ for (const field of DSS_DIAGNOSTIC_MESSAGE_FIELDS) {
1353
+ message = diagnostic(record[field]);
1354
+ if (message !== undefined)
1355
+ break;
1356
+ }
1357
+ const errorType = diagnostic(record["errorType"]);
1358
+ return {
1359
+ ...(message !== undefined ? { dssMessage: message, } : {}),
1360
+ ...(errorType !== undefined ? { dssErrorType: errorType, } : {}),
1361
+ };
1362
+ }
1363
+ catch {
1364
+ return {};
1365
+ }
1366
+ }
1277
1367
  const MISSING_PROJECT_KEY_ERROR_PREFIX = "projectKey is required";
1278
1368
  function isAmbiguousMutationFailure(err, context) {
1279
1369
  if (!(err instanceof DataikuError))
@@ -1384,6 +1474,8 @@ function buildErrorReport(err) {
1384
1474
  requestId: err.requestId ?? requestIdFromBody(err.body),
1385
1475
  details: {
1386
1476
  dssCategory: err.category,
1477
+ ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1478
+ ...dssDiagnosticDetails(err),
1387
1479
  statusText: canonicalStatusText(err.status),
1388
1480
  idempotency: "none",
1389
1481
  ...(err.retry ? { retry: err.retry, } : {}),
@@ -1417,6 +1509,7 @@ function buildErrorReport(err) {
1417
1509
  requestId: err.requestId ?? requestIdFromBody(err.body),
1418
1510
  details: {
1419
1511
  dssCategory: err.category,
1512
+ ...(err.bodyTruncated ? { bodyTruncated: true, } : {}),
1420
1513
  statusText: canonicalStatusText(err.status),
1421
1514
  body: safeBody,
1422
1515
  ...(err.retry ? { retry: err.retry, } : {}),
@@ -1451,6 +1544,14 @@ function writeErrorReport(err) {
1451
1544
  flushCliWarnings();
1452
1545
  process.stdout.write(`${JSON.stringify(buildErrorReport(err))}\n`);
1453
1546
  }
1547
+ /** Only ambiguous mutation failures require full contract metadata. */
1548
+ function errorReportNeedsCommandRuntime(err) {
1549
+ if (contract || !(err instanceof DataikuError))
1550
+ return false;
1551
+ const method = err.retry?.method.toUpperCase();
1552
+ return !!method && method !== "GET" && method !== "HEAD"
1553
+ && (err.status === 0 || err.status >= 500);
1554
+ }
1454
1555
  // ---------------------------------------------------------------------------
1455
1556
  // Main
1456
1557
  // ---------------------------------------------------------------------------
@@ -1489,7 +1590,8 @@ async function main() {
1489
1590
  process.exitCode = metaCommand.exitCode;
1490
1591
  return;
1491
1592
  }
1492
- if (!commands[resource])
1593
+ // Resource and action recovery require only this resource's definitions.
1594
+ if (!RESOURCE_NAMES.includes(resource))
1493
1595
  throw unknownResourceError(resource);
1494
1596
  const resourceActions = commands[resource];
1495
1597
  if (positional.length === 1) {
@@ -1500,8 +1602,12 @@ async function main() {
1500
1602
  const actionMeta = resourceActions[action];
1501
1603
  if (!actionMeta)
1502
1604
  throw unknownActionError(resource, action, Object.keys(resourceActions));
1605
+ await loadCommandRuntime();
1503
1606
  validateSupportedCommandFlags(resource, action, flags);
1504
1607
  const args = positional.slice(2);
1608
+ // Command-local validation runs for live, --plan, and --dry-run alike, so a
1609
+ // bad identifier is a usage error before any plan is built or request made.
1610
+ actionMeta.validate?.(args, flags);
1505
1611
  if (executionMode(flags).plan) {
1506
1612
  const plan = contract.buildMutationPlan(resource, action, actionMeta, args, flags);
1507
1613
  writeCommandResult(plan);
@@ -1515,7 +1621,6 @@ async function main() {
1515
1621
  writeCommandResult(result);
1516
1622
  return;
1517
1623
  }
1518
- actionMeta.validate?.(args, flags);
1519
1624
  const { url, apiKey, projectKey, tlsRejectUnauthorized, caCertPath, } = resolveCredentials(flags);
1520
1625
  currentCommandContext.projectKey = projectKey;
1521
1626
  if (!url) {
@@ -1547,7 +1652,8 @@ async function main() {
1547
1652
  writeCommandResult(result);
1548
1653
  }
1549
1654
  main().catch(async (err) => {
1550
- await loadCommandRuntime();
1655
+ if (errorReportNeedsCommandRuntime(err))
1656
+ await loadCommandRuntime();
1551
1657
  writeErrorReport(err);
1552
1658
  process.exitCode = errorExitCode(err);
1553
1659
  });
@@ -8,19 +8,26 @@ import { CodeEnvsResource } from "./resources/code-envs.js";
8
8
  import { ConnectionsResource } from "./resources/connections.js";
9
9
  import { ContinuousActivitiesResource } from "./resources/continuous-activities.js";
10
10
  import { DashboardsResource } from "./resources/dashboards.js";
11
+ import { DataCollectionsResource } from "./resources/data-collections.js";
11
12
  import { DataQualityResource } from "./resources/data-quality.js";
12
13
  import { DatasetsResource } from "./resources/datasets.js";
13
14
  import { DiscussionsResource } from "./resources/discussions.js";
14
15
  import { FlowZonesResource } from "./resources/flow-zones.js";
15
16
  import { FoldersResource } from "./resources/folders.js";
16
17
  import { FuturesResource } from "./resources/futures.js";
18
+ import { GroupsResource } from "./resources/groups.js";
17
19
  import { InsightsResource } from "./resources/insights.js";
18
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";
19
24
  import { MeaningsResource } from "./resources/meanings.js";
20
25
  import { MetricsResource } from "./resources/metrics.js";
21
26
  import { MlTasksResource } from "./resources/ml-tasks.js";
22
27
  import { ModelEvaluationStoresResource } from "./resources/model-evaluation-stores.js";
23
28
  import { NotebooksResource } from "./resources/notebooks.js";
29
+ import { PluginsResource } from "./resources/plugins.js";
30
+ import { ProjectFoldersResource } from "./resources/project-folders.js";
24
31
  import { ProjectGitResource } from "./resources/project-git.js";
25
32
  import { ProjectLibraryResource } from "./resources/project-library.js";
26
33
  import { ProjectsResource } from "./resources/projects.js";
@@ -30,10 +37,43 @@ import { ScenariosResource } from "./resources/scenarios.js";
30
37
  import { SqlResource } from "./resources/sql.js";
31
38
  import { StatisticsResource } from "./resources/statistics.js";
32
39
  import { StreamingEndpointsResource } from "./resources/streaming-endpoints.js";
40
+ import { UsersResource } from "./resources/users.js";
33
41
  import { VariablesResource } from "./resources/variables.js";
34
42
  import { WebappsResource } from "./resources/webapps.js";
35
43
  import { WikiResource } from "./resources/wiki.js";
36
44
  import { WorkspacesResource } from "./resources/workspaces.js";
45
+ /** One part of a {@link DataikuClient.uploadForm} multipart/form-data body. */
46
+ export interface UploadFormPart {
47
+ /** Form field name. */
48
+ name: string;
49
+ /** Text field value. Mutually exclusive with `blob`/`fileName` file parts. */
50
+ value?: string;
51
+ /** File content. Omit (or null) together with `fileName` for a zero-byte file part. */
52
+ blob?: Blob | null;
53
+ /** Filename for file parts. */
54
+ fileName?: string;
55
+ }
56
+ /**
57
+ * Multipart part modes for {@link DataikuClient.uploadForm}:
58
+ * - `value`: a form text field;
59
+ * - neither `blob` nor `fileName`: a filename-less, zero-length field part —
60
+ * the official Python client's files={"file": (None, None)} shape required
61
+ * by the managed-folder MLflow import (a Blob would add `filename=`, even
62
+ * when empty);
63
+ * - `blob` (optionally with `fileName`): a file part.
64
+ */
65
+ /** Per-call overrides for {@link DataikuClient.get}. */
66
+ export interface DataikuGetOptions {
67
+ /**
68
+ * TOTAL duration budget in milliseconds for this call, measured from call
69
+ * start: it bounds every retry attempt's fetch, the backoff sleeps between
70
+ * attempts, and the response body read. When the budget is exhausted the
71
+ * request is aborted and a timeout DataikuError is thrown. The client-level
72
+ * requestTimeoutMs still caps each attempt and buffered body read; this
73
+ * budget may shorten those limits but never extends them.
74
+ */
75
+ timeoutMs?: number;
76
+ }
37
77
  export interface DataikuClientTraceEvent {
38
78
  type: "trace";
39
79
  phase: "request" | "response" | "error";
@@ -72,9 +112,9 @@ export interface DataikuClientConfig {
72
112
  /** Max retry attempts for idempotent requests (default 4, capped at 10) */
73
113
  retryMaxAttempts?: number;
74
114
  /**
75
- * Maximum bytes buffered from a single response body by the text/JSON
76
- * consumer methods (default 50 MiB). Responses larger than this are
77
- * rejected with a DataikuError after the stream is cancelled.
115
+ * Maximum bytes retained from a single response body by text/JSON consumers
116
+ * (default 50 MiB). Oversized successful responses are rejected. Error
117
+ * responses retain a bounded prefix with DataikuError.bodyTruncated=true.
78
118
  */
79
119
  maxResponseBodyBytes?: number;
80
120
  /** Emit HTTP request/response trace events. Defaults to JSONL on stderr when verbose is true. */
@@ -139,6 +179,14 @@ export declare class DataikuClient {
139
179
  private mlTasksResource?;
140
180
  private savedModelsResource?;
141
181
  private modelEvaluationStoresResource?;
182
+ private projectFoldersResource?;
183
+ private dataCollectionsResource?;
184
+ private llmsResource?;
185
+ private knowledgeBanksResource?;
186
+ private macrosResource?;
187
+ private pluginsResource?;
188
+ private usersResource?;
189
+ private groupsResource?;
142
190
  get projects(): ProjectsResource;
143
191
  get datasets(): DatasetsResource;
144
192
  get dashboards(): DashboardsResource;
@@ -175,6 +223,14 @@ export declare class DataikuClient {
175
223
  get mlTasks(): MlTasksResource;
176
224
  get savedModels(): SavedModelsResource;
177
225
  get modelEvaluationStores(): ModelEvaluationStoresResource;
226
+ get projectFolders(): ProjectFoldersResource;
227
+ get dataCollections(): DataCollectionsResource;
228
+ get llms(): LlmsResource;
229
+ get knowledgeBanks(): KnowledgeBanksResource;
230
+ get macros(): MacrosResource;
231
+ get plugins(): PluginsResource;
232
+ get users(): UsersResource;
233
+ get groups(): GroupsResource;
178
234
  constructor(config?: DataikuClientConfig);
179
235
  /**
180
236
  * Canonical request base URL: the exact prefix every HTTP verb concatenates
@@ -189,7 +245,7 @@ export declare class DataikuClient {
189
245
  */
190
246
  getMaxResponseBodyBytes(): number;
191
247
  resolveProjectKey(paramValue?: string): string;
192
- get<T = unknown>(path: string): Promise<T>;
248
+ get<T = unknown>(path: string, options?: DataikuGetOptions): Promise<T>;
193
249
  /**
194
250
  * GET returning the parsed JSON body plus selected response metadata
195
251
  * (documented DSS version headers, server date, request id). Authorization
@@ -239,10 +295,32 @@ export declare class DataikuClient {
239
295
  * {@link getText} (byte cap `maxResponseBodyBytes`, request deadline).
240
296
  */
241
297
  postText(path: string, body?: unknown): Promise<string>;
298
+ /** Send raw file contents through the shared authenticated transport. */
299
+ postRawBody(path: string, body: string | Uint8Array): Promise<void>;
300
+ /** Git endpoints require Basic API-key authentication, unlike the bearer API. */
301
+ requestGit(method: string, path: string, body?: unknown, deadlineAt?: number): Promise<Response>;
242
302
  postStream(path: string, body?: unknown): Promise<Response>;
243
303
  put<T = unknown>(path: string, body: unknown): Promise<T>;
244
304
  del(path: string): Promise<void>;
245
305
  putVoid(path: string, body: unknown): Promise<void>;
306
+ /**
307
+ * PUT without a request body. A few DSS endpoints (bundle export) reject
308
+ * an empty JSON object body with 400 validation, and the official Python
309
+ * client sends no body at all for them.
310
+ */
311
+ putVoidNoBody(path: string): Promise<void>;
312
+ /**
313
+ * POST a multipart/form-data body built from explicit parts, with optional
314
+ * query parameters appended to the path, returning the parsed JSON body.
315
+ *
316
+ * A part with `value` becomes a form text field; a part with `blob`
317
+ * (or a `fileName` with no blob) becomes a file part — a `fileName` without
318
+ * blob yields a zero-byte file part, which some DSS import endpoints
319
+ * (e.g. saved-model MLflow version import) require for metadata-only parts.
320
+ * The `Content-Type` header is left to the runtime so the multipart
321
+ * boundary is set correctly.
322
+ */
323
+ uploadForm<T = unknown>(path: string, parts: UploadFormPart[], query?: URLSearchParams): Promise<T>;
246
324
  private uploadResponse;
247
325
  upload(path: string, filePath: string, fileName?: string): Promise<void>;
248
326
  uploadJson<T>(path: string, filePath: string, fileName?: string): Promise<T>;
@@ -271,7 +349,7 @@ export declare class DataikuClient {
271
349
  private requestIdFromHeaders;
272
350
  private parseJsonResponse;
273
351
  /**
274
- * Read at most `maxBytes` of a 2xx response body as UTF-8 text.
352
+ * Read at most `maxBytes` of a response body as UTF-8 text.
275
353
  *
276
354
  * The body is consumed with the request deadline still active (measured
277
355
  * from the first read): a stalled body throws a DataikuError instead of