dataiku-sdk 3.4.0 → 3.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +4 -4
  2. package/bin/dss.js +28 -2
  3. package/dist/build-metadata.json +1 -1
  4. package/dist/src/cli/coerce.d.ts +0 -1
  5. package/dist/src/cli/coerce.js +0 -5
  6. package/dist/src/cli/command-syntax.json +14524 -0
  7. package/dist/src/cli/commands/analysis.js +6 -9
  8. package/dist/src/cli/commands/api-deployer.js +15 -33
  9. package/dist/src/cli/commands/api-service.js +17 -27
  10. package/dist/src/cli/commands/app.js +25 -41
  11. package/dist/src/cli/commands/bundle.js +15 -24
  12. package/dist/src/cli/commands/business-app.js +10 -17
  13. package/dist/src/cli/commands/code-env.js +23 -36
  14. package/dist/src/cli/commands/code.js +4 -4
  15. package/dist/src/cli/commands/connection.js +12 -22
  16. package/dist/src/cli/commands/continuous-activity.js +6 -9
  17. package/dist/src/cli/commands/dashboard.js +8 -13
  18. package/dist/src/cli/commands/data-collection.js +9 -16
  19. package/dist/src/cli/commands/data-quality.js +14 -26
  20. package/dist/src/cli/commands/dataset.js +29 -54
  21. package/dist/src/cli/commands/discussion.js +7 -10
  22. package/dist/src/cli/commands/doctor.js +3 -3
  23. package/dist/src/cli/commands/flow-zone.js +8 -17
  24. package/dist/src/cli/commands/folder.js +12 -20
  25. package/dist/src/cli/commands/future.js +7 -10
  26. package/dist/src/cli/commands/group.js +7 -11
  27. package/dist/src/cli/commands/insight.js +7 -11
  28. package/dist/src/cli/commands/job.js +17 -26
  29. package/dist/src/cli/commands/knowledge-bank.js +5 -6
  30. package/dist/src/cli/commands/llm.js +5 -8
  31. package/dist/src/cli/commands/macro.js +9 -16
  32. package/dist/src/cli/commands/meaning.js +7 -11
  33. package/dist/src/cli/commands/metrics.js +7 -10
  34. package/dist/src/cli/commands/ml-task.js +12 -20
  35. package/dist/src/cli/commands/model-evaluation-store.js +7 -11
  36. package/dist/src/cli/commands/notebook.js +15 -27
  37. package/dist/src/cli/commands/plugin.js +35 -68
  38. package/dist/src/cli/commands/project-deployer.js +10 -22
  39. package/dist/src/cli/commands/project-folder.js +10 -17
  40. package/dist/src/cli/commands/project-git.js +38 -70
  41. package/dist/src/cli/commands/project-library.js +15 -23
  42. package/dist/src/cli/commands/project.js +9 -26
  43. package/dist/src/cli/commands/recipe.d.ts +16 -0
  44. package/dist/src/cli/commands/recipe.js +83 -94
  45. package/dist/src/cli/commands/saved-model.js +23 -39
  46. package/dist/src/cli/commands/scenario.js +20 -34
  47. package/dist/src/cli/commands/sql.js +4 -4
  48. package/dist/src/cli/commands/statistics.js +11 -18
  49. package/dist/src/cli/commands/streaming-endpoint.js +7 -11
  50. package/dist/src/cli/commands/user.js +11 -22
  51. package/dist/src/cli/commands/variable.js +3 -4
  52. package/dist/src/cli/commands/webapp.js +10 -16
  53. package/dist/src/cli/commands/wiki.js +9 -13
  54. package/dist/src/cli/commands/workspace.js +8 -14
  55. package/dist/src/cli/contract.d.ts +5 -15
  56. package/dist/src/cli/contract.js +157 -254
  57. package/dist/src/cli/doctor.js +1 -0
  58. package/dist/src/cli/env.d.ts +2 -0
  59. package/dist/src/cli/env.js +16 -1
  60. package/dist/src/cli/flags.d.ts +1 -0
  61. package/dist/src/cli/flags.js +4 -9
  62. package/dist/src/cli/helpers/app-permissions.js +7 -5
  63. package/dist/src/cli/helpers/flow-zone.js +8 -6
  64. package/dist/src/cli/helpers/recipe.js +2 -1
  65. package/dist/src/cli/syntax.d.ts +83 -0
  66. package/dist/src/cli/syntax.js +217 -0
  67. package/dist/src/cli/types.d.ts +3 -0
  68. package/dist/src/cli/usage.d.ts +1 -1
  69. package/dist/src/cli/usage.js +4 -2
  70. package/dist/src/cli/version.d.ts +0 -2
  71. package/dist/src/cli/version.js +6 -7
  72. package/dist/src/cli.js +132 -274
  73. package/dist/src/client.d.ts +2 -1
  74. package/dist/src/client.js +32 -17
  75. package/dist/src/errors.d.ts +12 -1
  76. package/dist/src/errors.js +28 -3
  77. package/dist/src/index.d.ts +1 -3
  78. package/dist/src/index.js +2 -2
  79. package/dist/src/resources/api-services.js +2 -3
  80. package/dist/src/resources/applications.js +2 -4
  81. package/dist/src/resources/base.d.ts +2 -0
  82. package/dist/src/resources/base.js +14 -2
  83. package/dist/src/resources/code-envs.js +3 -5
  84. package/dist/src/resources/data-quality.js +14 -13
  85. package/dist/src/resources/datasets.d.ts +13 -0
  86. package/dist/src/resources/datasets.js +5 -4
  87. package/dist/src/resources/flow-zones.js +4 -2
  88. package/dist/src/resources/folders.js +4 -2
  89. package/dist/src/resources/futures.js +1 -3
  90. package/dist/src/resources/insights.js +3 -2
  91. package/dist/src/resources/jobs.js +4 -6
  92. package/dist/src/resources/macros.d.ts +1 -1
  93. package/dist/src/resources/macros.js +3 -7
  94. package/dist/src/resources/notebooks.js +2 -3
  95. package/dist/src/resources/project-git.js +9 -11
  96. package/dist/src/resources/project-library.d.ts +0 -2
  97. package/dist/src/resources/project-library.js +2 -7
  98. package/dist/src/resources/projects.js +4 -3
  99. package/dist/src/resources/recipes.d.ts +28 -0
  100. package/dist/src/resources/recipes.js +117 -89
  101. package/dist/src/resources/saved-models.js +1 -13
  102. package/dist/src/resources/scenarios.js +1 -3
  103. package/dist/src/resources/sql.js +9 -8
  104. package/dist/src/resources/statistics.js +3 -4
  105. package/dist/src/resources/variables.js +2 -1
  106. package/dist/src/resources/webapps.js +2 -3
  107. package/dist/src/schemas.d.ts +1 -2
  108. package/dist/src/schemas.js +1 -1
  109. package/dist/src/skill.js +6 -7
  110. package/dist/src/utils/cleanup-ledger.js +9 -8
  111. package/dist/src/utils/deep-merge.js +2 -4
  112. package/dist/src/utils/flow-map.js +1 -6
  113. package/dist/src/utils/polling.d.ts +3 -0
  114. package/dist/src/utils/polling.js +3 -0
  115. package/dist/src/utils/project-archive.js +10 -14
  116. package/dist/src/utils/project-incarnation.js +3 -7
  117. package/dist/src/utils/records.d.ts +4 -0
  118. package/dist/src/utils/records.js +8 -0
  119. package/dist/src/utils/stable-hash.d.ts +3 -1
  120. package/dist/src/utils/stable-hash.js +2 -0
  121. package/package.json +10 -14
  122. package/plugin.json +1 -1
  123. package/skills/dataiku-dss/SKILL.md +3 -4
  124. package/skills/dataiku-dss/references/authentication.md +3 -3
  125. package/skills/dataiku-dss/references/coding.md +1 -1
  126. package/skills/dataiku-dss/references/discovery.md +1 -1
  127. package/skills/dataiku-dss/references/mutations.md +0 -1
  128. package/dist/src/utils/pagination.d.ts +0 -8
  129. package/dist/src/utils/pagination.js +0 -23
  130. package/packages/types/dist/index.d.ts +0 -1062
  131. package/packages/types/dist/index.js +0 -871
package/dist/src/cli.js CHANGED
@@ -4,11 +4,12 @@ import { homedir, } from "node:os";
4
4
  import { resolve, } from "node:path";
5
5
  import { num, unknownJsonInput, } from "./cli/coerce.js";
6
6
  import { commands, } from "./cli/commands/index.js";
7
- import { dataikuEnvironmentEnabled, loadEnvFile, } from "./cli/env.js";
7
+ import { ambientProjectKey, loadEnvFile, } from "./cli/env.js";
8
8
  import { executionMode, FLAG_ALIASES, isNegativeNumberToken, parseArgs, SHORT_FLAGS, VALUE_FLAGS, } from "./cli/flags.js";
9
9
  import { cleanupLedgerEntry, } from "./cli/helpers/cleanup.js";
10
10
  import { commandFailureExitCode, CommandResultFailure, flushCliWarnings, isFailedWaitResult, planResult, projectResultFields, setOutputFieldProjection, writeCommandResult, } from "./cli/output.js";
11
11
  import { currentCommandContext, resolveCredentials, resolveLoginCredentials, } from "./cli/runtime.js";
12
+ import { commandUsage, } from "./cli/syntax.js";
12
13
  import { COMMANDS_RUN_HINT, inferRequiresProject, missingActionError, noCommandError, requireArgs, RESOURCE_NAMES, unknownActionError, unknownResourceError, UsageError, } from "./cli/usage.js";
13
14
  import { cliVersionResult, } from "./cli/version.js";
14
15
  import { getCredentialsPath, } from "./config.js";
@@ -171,17 +172,6 @@ async function runCleanup(flags) {
171
172
  || !commands[resource]?.[action]) {
172
173
  throw new UsageError(`Invalid cleanup argv: ${entry.cleanup.argv.join(" ")}`);
173
174
  }
174
- if (resource === "project"
175
- && action === "delete"
176
- && typeof parsed.flags["expect-project-incarnation"] !== "string") {
177
- throw new UsageError("Project cleanup entry is not bound to a project incarnation. Refusing deletion after possible project-key reuse.", "validation_failed", "Capture a new cleanup entry from the current CLI. Legacy entries cannot safely delete projects.");
178
- }
179
- if (resource === "app"
180
- && action === "delete-instance"
181
- && parsed.flags["unconfirmed-creation"] !== true
182
- && typeof parsed.flags["expect-project-incarnation"] !== "string") {
183
- throw new UsageError("App cleanup entry is not bound to a project incarnation. Refusing deletion after possible project-key reuse.", "validation_failed", "Capture a new cleanup entry from the current CLI. Legacy entries cannot safely delete app projects.");
184
- }
185
175
  const result = await commands[resource][action].handler(client, args, parsed.flags);
186
176
  if (isFailedWaitResult(result)) {
187
177
  const failure = {
@@ -334,32 +324,11 @@ function validateSupportedCommandFlags(resource, action, flags) {
334
324
  ? "Remove --dry-run; use --plan to preview the mutation instead."
335
325
  : `Remove --dry-run; ${resource} ${action} has no dry-run mode.`);
336
326
  }
337
- }
338
- function stripOptionalUsageGroupsForValidation(usage) {
339
- let stripped = "";
340
- let optionalDepth = 0;
341
- for (const char of usage) {
342
- if (char === "[") {
343
- optionalDepth++;
344
- if (optionalDepth === 1)
345
- stripped += " ";
346
- continue;
347
- }
348
- if (char === "]" && optionalDepth > 0) {
349
- optionalDepth--;
350
- if (optionalDepth === 0)
351
- stripped += " ";
352
- continue;
353
- }
354
- if (optionalDepth === 0)
355
- stripped += char;
327
+ // Registry commands reject --plan in buildMutationPlan; meta commands never reach it.
328
+ if (META_COMMAND_RESOURCES[resource] === true && executionMode(flags).plan
329
+ && !entry.flags.some((flag) => flag.name === "plan")) {
330
+ throw new UsageError(`--plan is only supported for mutating commands; ${resource} ${action} has no plan mode.`, "usage_error", `Remove --plan; ${resource} ${action} has no plan mode.`);
356
331
  }
357
- return stripped;
358
- }
359
- function requiredPositionalCount(usage) {
360
- const requiredUsage = stripOptionalUsageGroupsForValidation(usage);
361
- const positionalTokens = [...requiredUsage.matchAll(/<[^>]+>/g),];
362
- return positionalTokens.length;
363
332
  }
364
333
  function flagIsProvided(flags, name, allowEmpty = false) {
365
334
  const value = flags[name];
@@ -370,7 +339,7 @@ function flagIsProvided(flags, name, allowEmpty = false) {
370
339
  return allowEmpty || value.length > 0;
371
340
  }
372
341
  function validateRequiredCommandInputs(resource, action, args, flags, entry) {
373
- const argCount = requiredPositionalCount(entry.usage);
342
+ const argCount = entry.positionalArguments.filter((positional) => positional.required).length;
374
343
  const allowEmptyFlags = {};
375
344
  for (const flag of entry.flags) {
376
345
  if (flag.allowEmptyValue === true)
@@ -439,81 +408,60 @@ async function validateDryRunHandlerPreconditions(handler, args, flags) {
439
408
  throw error;
440
409
  }
441
410
  }
411
+ /**
412
+ * The one validator for meta commands, shared by direct runs and batch
413
+ * --dry-run: action, supported flags (incl. --plan/--dry-run), flag conflicts,
414
+ * and required inputs. Returns the canonical action, or undefined for a
415
+ * non-meta resource.
416
+ */
442
417
  async function validateMetaCommandInputs(resource, action, flags) {
443
- if (resource === "doctor") {
444
- if (action !== undefined && action !== "run") {
445
- throw unknownActionError("doctor", action, ["run",]);
446
- }
447
- return "run";
448
- }
418
+ if (META_COMMAND_RESOURCES[resource] !== true)
419
+ return undefined;
420
+ await loadCommandRuntime();
421
+ let resolved;
449
422
  if (resource === "auth") {
450
423
  const validActions = Object.keys(contract.AUTH_ACTIONS);
451
- if (!action) {
452
- throw missingActionError("auth", validActions, "dss auth login --url URL --api-key KEY");
453
- }
424
+ if (!action)
425
+ throw missingActionError("auth", validActions, commandUsage("auth", "login"));
454
426
  if (!contract.AUTH_ACTIONS[action]) {
455
427
  throw unknownActionError("auth", action, validActions, "auth only supports 'login'. To check credentials/connectivity, run 'dss doctor'.");
456
428
  }
457
- return action;
429
+ resolved = action;
458
430
  }
459
- if (resource === "install-skill") {
460
- if (action !== undefined && action !== "run") {
461
- throw unknownActionError("install-skill", action, ["run",]);
462
- }
463
- const { AGENTS, } = await import("./skill.js");
464
- const agentFilter = typeof flags["agent"] === "string" ? flags["agent"] : undefined;
465
- if (agentFilter && !AGENTS[agentFilter]) {
466
- throw new UsageError(`Unknown agent: ${agentFilter}.`, "usage_error", COMMANDS_RUN_HINT, { agent: agentFilter, validAgents: Object.keys(AGENTS), });
467
- }
468
- return "run";
469
- }
470
- if (resource === "agent") {
431
+ else if (resource === "agent" || resource === "commands") {
432
+ const only = resource === "agent" ? "contract" : "run";
433
+ const usage = resource === "agent" ? contract.AGENT_CONTRACT_USAGE : contract.COMMANDS_USAGE;
471
434
  if (!action)
472
- throw missingActionError("agent", ["contract",], contract.AGENT_CONTRACT_USAGE);
473
- if (action !== "contract")
474
- throw unknownActionError("agent", action, ["contract",]);
475
- return action;
435
+ throw missingActionError(resource, [only,], usage);
436
+ if (action !== only)
437
+ throw unknownActionError(resource, action, [only,]);
438
+ resolved = action;
476
439
  }
477
- if (resource === "commands") {
478
- if (!action)
479
- throw missingActionError("commands", ["run",], contract.COMMANDS_USAGE);
480
- if (action !== "run")
481
- throw unknownActionError("commands", action, ["run",]);
482
- validateSupportedCommandFlags("commands", "run", flags);
483
- commandRegistrySelectors(flags);
484
- return action;
485
- }
486
- if (resource === "version") {
440
+ else {
487
441
  if (action !== undefined && action !== "run") {
488
- throw unknownActionError("version", action, ["run",]);
442
+ throw unknownActionError(resource, action, ["run",]);
489
443
  }
490
- return "run";
444
+ resolved = "run";
491
445
  }
492
- if (resource === "cleanup") {
493
- if (action !== undefined && action !== "run") {
494
- throw unknownActionError("cleanup", action, ["run",]);
495
- }
496
- const entry = cachedCommandRegistry("cleanup")?.run;
446
+ validateSupportedCommandFlags(resource, resolved, flags);
447
+ if (resource === "commands")
448
+ commandRegistrySelectors(flags);
449
+ if (resource === "cleanup" || resource === "batch") {
450
+ const entry = cachedCommandRegistry(resource)?.run;
497
451
  if (entry)
498
- validateRequiredCommandInputs("cleanup", "run", [], flags, entry);
499
- return "run";
452
+ validateRequiredCommandInputs(resource, "run", [], flags, entry);
500
453
  }
501
- if (resource === "fixtures") {
502
- if (action !== undefined && action !== "run") {
503
- throw unknownActionError("fixtures", action, ["run",]);
504
- }
505
- return "run";
454
+ if (resource === "cleanup" && flags["apply"] === true && executionMode(flags).dryRun) {
455
+ throw new UsageError("--dry-run and --apply are mutually exclusive.", "usage_error");
506
456
  }
507
- if (resource === "batch") {
508
- if (action !== undefined && action !== "run") {
509
- throw unknownActionError("batch", action, ["run",]);
457
+ if (resource === "install-skill") {
458
+ const { AGENTS, } = await import("./skill.js");
459
+ const agentFilter = typeof flags["agent"] === "string" ? flags["agent"] : undefined;
460
+ if (agentFilter && !AGENTS[agentFilter]) {
461
+ throw new UsageError(`Unknown agent: ${agentFilter}.`, "usage_error", COMMANDS_RUN_HINT, { agent: agentFilter, validAgents: Object.keys(AGENTS), });
510
462
  }
511
- const entry = cachedCommandRegistry("batch")?.run;
512
- if (entry)
513
- validateRequiredCommandInputs("batch", "run", [], flags, entry);
514
- return "run";
515
463
  }
516
- return undefined;
464
+ return resolved;
517
465
  }
518
466
  async function validateBatchStep(argv) {
519
467
  const { positional, flags, } = parseArgs(argv);
@@ -553,29 +501,8 @@ function batchStepNeedsClient(argv) {
553
501
  }
554
502
  }
555
503
  function batchStepCommandContext(argv) {
556
- const positionals = [];
557
- for (let index = 0; index < argv.length; index++) {
558
- const arg = argv[index];
559
- if (arg === "--") {
560
- positionals.push(...argv.slice(index + 1));
561
- break;
562
- }
563
- if (arg.startsWith("--")) {
564
- const name = arg.slice(2).split("=")[0] ?? "";
565
- const canonical = FLAG_ALIASES[name] ?? name;
566
- if (!arg.includes("=") && VALUE_FLAGS.has(canonical))
567
- index++;
568
- continue;
569
- }
570
- if (arg.length === 2 && arg[0] === "-" && arg[1] !== "-") {
571
- const long = SHORT_FLAGS[arg[1]];
572
- if (long && VALUE_FLAGS.has(long))
573
- index++;
574
- continue;
575
- }
576
- positionals.push(arg);
577
- }
578
- return { resource: positionals[0], action: positionals[1], };
504
+ const [resource, action,] = rawPositionals(argv);
505
+ return { resource, action, };
579
506
  }
580
507
  const SENSITIVE_ARGV_VALUE_FLAGS = {
581
508
  "api-key": true,
@@ -769,133 +696,78 @@ function batchPlan(flags) {
769
696
  plannedAndDryRun: executionMode(flags).dryRun,
770
697
  });
771
698
  }
772
- async function runMetaCommand(resource, action, flags) {
773
- if (resource === "doctor") {
774
- await loadCommandRuntime();
775
- if (action !== undefined && action !== "run") {
776
- throw unknownActionError("doctor", action, ["run",]);
777
- }
778
- currentCommandContext.action = action ?? "run";
779
- const { runDoctor, } = await import("./cli/doctor.js");
780
- const { result, exitCode, } = await runDoctor(flags);
781
- return { action: "run", result, exitCode, };
782
- }
783
- if (resource === "auth") {
784
- await loadCommandRuntime();
785
- const validActions = Object.keys(contract.AUTH_ACTIONS);
786
- if (!action) {
787
- throw missingActionError("auth", validActions, "dss auth login --url URL --api-key KEY");
788
- }
789
- currentCommandContext.action = action;
790
- const authMeta = contract.AUTH_ACTIONS[action];
791
- if (!authMeta) {
792
- throw unknownActionError("auth", action, validActions, "auth only supports 'login'. To check credentials/connectivity, run 'dss doctor'.");
793
- }
794
- if (executionMode(flags).plan) {
795
- return { action, result: authLoginPlan(flags), exitCode: 0, };
796
- }
797
- return { action, result: await authMeta.handler(flags), exitCode: 0, };
798
- }
799
- if (resource === "install-skill") {
800
- await loadCommandRuntime();
801
- if (action !== undefined && action !== "run") {
802
- throw unknownActionError("install-skill", action, ["run",]);
803
- }
804
- currentCommandContext.action = action ?? "run";
805
- return { action: "run", result: await runInstallSkill(flags), exitCode: 0, };
806
- }
807
- if (resource === "agent") {
808
- await loadCommandRuntime();
809
- if (!action)
810
- throw missingActionError("agent", ["contract",], contract.AGENT_CONTRACT_USAGE);
811
- currentCommandContext.action = action;
812
- if (action !== "contract")
813
- throw unknownActionError("agent", action, ["contract",]);
814
- return { action, result: contract.buildAgentContract(), exitCode: 0, };
815
- }
816
- if (resource === "commands") {
817
- await loadCommandRuntime();
818
- if (!action)
819
- throw missingActionError("commands", ["run",], contract.COMMANDS_USAGE);
820
- currentCommandContext.action = action;
821
- if (action !== "run")
822
- throw unknownActionError("commands", action, ["run",]);
823
- validateSupportedCommandFlags("commands", "run", flags);
824
- const selectors = commandRegistrySelectors(flags);
825
- const outputPath = flags["output"];
826
- if (selectors.length === 0 && typeof outputPath !== "string") {
827
- return { action, result: contract.commandActionSummary(), exitCode: 0, };
699
+ async function runMetaCommand(resource, rawAction, flags) {
700
+ const action = await validateMetaCommandInputs(resource, rawAction, flags);
701
+ if (action === undefined)
702
+ return undefined;
703
+ currentCommandContext.action = action;
704
+ const plan = executionMode(flags).plan;
705
+ switch (resource) {
706
+ case "doctor": {
707
+ const { runDoctor, } = await import("./cli/doctor.js");
708
+ const { result, exitCode, } = await runDoctor(flags);
709
+ return { action, result, exitCode, };
828
710
  }
829
- const registry = selectors.length === 0 ? contract.buildCommandRegistry() : {};
830
- for (const selector of selectors) {
831
- const selectorParts = selector.split(".");
832
- if (selectorParts.some((part) => part.length === 0)) {
833
- throw new UsageError(`Invalid --fields selector: ${selector}. Expected RESOURCE or RESOURCE.ACTION[.FIELD...].`, "usage_error", contract.COMMANDS_USAGE, { selector, });
834
- }
835
- const [selectedResource, selectedAction,] = selectorParts;
836
- const resourceActions = cachedCommandRegistry(selectedResource);
837
- if (!resourceActions)
838
- throw unknownResourceError(selectedResource);
839
- registry[selectedResource] = resourceActions;
840
- if (selectedAction && !resourceActions[selectedAction]) {
841
- throw unknownActionError(selectedResource, selectedAction, Object.keys(resourceActions));
842
- }
711
+ case "auth":
712
+ if (plan)
713
+ return { action, result: authLoginPlan(flags), exitCode: 0, };
714
+ return { action, result: await contract.AUTH_ACTIONS[action].handler(flags), exitCode: 0, };
715
+ case "install-skill":
716
+ return { action, result: await runInstallSkill(flags), exitCode: 0, };
717
+ case "agent":
718
+ return { action, result: contract.buildAgentContract(), exitCode: 0, };
719
+ case "version":
720
+ return { action, result: cliVersionResult(), exitCode: 0, };
721
+ case "fixtures": {
722
+ const { runFixtures, } = await import("./cli/doctor.js");
723
+ return { action, result: await runFixtures(flags), exitCode: 0, };
843
724
  }
844
- const selectedRegistry = selectors.length === 0
845
- ? registry
846
- : projectResultFields(registry, selectors, () => Object.keys(contract.commandActionSummary()).sort());
847
- if (typeof outputPath === "string") {
848
- const exported = selectedRegistry;
849
- await fs.writeFile(outputPath, `${JSON.stringify(exported)}\n`, "utf-8");
850
- return { action, result: { path: outputPath, }, exitCode: 0, };
725
+ case "cleanup": {
726
+ if (plan)
727
+ return { action, result: cleanupPlan(flags), exitCode: 0, };
728
+ const { result, exitCode, } = await runCleanup(flags);
729
+ return { action, result, exitCode, };
851
730
  }
852
- return {
853
- action,
854
- result: selectedRegistry,
855
- exitCode: 0,
856
- };
857
- }
858
- if (resource === "version") {
859
- if (action !== undefined && action !== "run") {
860
- throw unknownActionError("version", action, ["run",]);
731
+ case "batch": {
732
+ if (plan)
733
+ return { action, result: batchPlan(flags), exitCode: 0, };
734
+ const { result, exitCode, } = await runBatch(flags);
735
+ return { action, result, exitCode, };
861
736
  }
862
- currentCommandContext.action = action ?? "run";
863
- return { action: "run", result: cliVersionResult(), exitCode: 0, };
737
+ default:
738
+ return { action, result: await commandsRunResult(flags), exitCode: 0, };
864
739
  }
865
- if (resource === "cleanup") {
866
- await loadCommandRuntime();
867
- if (action !== undefined && action !== "run") {
868
- throw unknownActionError("cleanup", action, ["run",]);
869
- }
870
- currentCommandContext.action = action ?? "run";
871
- if (executionMode(flags).plan) {
872
- return { action: "run", result: cleanupPlan(flags), exitCode: 0, };
740
+ }
741
+ async function commandsRunResult(flags) {
742
+ const selectors = commandRegistrySelectors(flags);
743
+ const outputPath = flags["output"];
744
+ if (selectors.length === 0 && typeof outputPath !== "string") {
745
+ return contract.commandActionSummary();
746
+ }
747
+ const registry = selectors.length === 0 ? contract.buildCommandRegistry() : {};
748
+ for (const selector of selectors) {
749
+ const selectorParts = selector.split(".");
750
+ if (selectorParts.some((part) => part.length === 0)) {
751
+ throw new UsageError(`Invalid --fields selector: ${selector}. Expected RESOURCE or RESOURCE.ACTION[.FIELD...].`, "usage_error", contract.COMMANDS_USAGE, { selector, });
873
752
  }
874
- const { result, exitCode, } = await runCleanup(flags);
875
- return { action: "run", result, exitCode, };
876
- }
877
- if (resource === "fixtures") {
878
- await loadCommandRuntime();
879
- if (action !== undefined && action !== "run") {
880
- throw unknownActionError("fixtures", action, ["run",]);
753
+ const [selectedResource, selectedAction,] = selectorParts;
754
+ const resourceActions = cachedCommandRegistry(selectedResource);
755
+ if (!resourceActions)
756
+ throw unknownResourceError(selectedResource);
757
+ registry[selectedResource] = resourceActions;
758
+ if (selectedAction && !resourceActions[selectedAction]) {
759
+ throw unknownActionError(selectedResource, selectedAction, Object.keys(resourceActions));
881
760
  }
882
- currentCommandContext.action = action ?? "run";
883
- const { runFixtures, } = await import("./cli/doctor.js");
884
- return { action: "run", result: await runFixtures(flags), exitCode: 0, };
885
761
  }
886
- if (resource === "batch") {
887
- await loadCommandRuntime();
888
- if (action !== undefined && action !== "run") {
889
- throw unknownActionError("batch", action, ["run",]);
890
- }
891
- currentCommandContext.action = action ?? "run";
892
- if (executionMode(flags).plan) {
893
- return { action: "run", result: batchPlan(flags), exitCode: 0, };
894
- }
895
- const { result, exitCode, } = await runBatch(flags);
896
- return { action: "run", result, exitCode, };
762
+ const selectedRegistry = selectors.length === 0
763
+ ? registry
764
+ : projectResultFields(registry, selectors, () => Object.keys(contract.commandActionSummary()).sort());
765
+ if (typeof outputPath === "string") {
766
+ const exported = selectedRegistry;
767
+ await fs.writeFile(outputPath, `${JSON.stringify(exported)}\n`, "utf-8");
768
+ return { path: outputPath, };
897
769
  }
898
- return undefined;
770
+ return selectedRegistry;
899
771
  }
900
772
  /**
901
773
  * Cleanup-ledger support is checked before a handler runs so an unsupported
@@ -993,9 +865,7 @@ async function runBatch(flags) {
993
865
  if (executionMode(flags).dryRun) {
994
866
  const batchProjectKey = typeof flags["project-key"] === "string"
995
867
  ? flags["project-key"]
996
- : dataikuEnvironmentEnabled()
997
- ? process.env.DATAIKU_PROJECT_KEY
998
- : undefined;
868
+ : ambientProjectKey();
999
869
  const planned = await Promise.all(steps.map(async (argv, index) => {
1000
870
  const context = batchStepCommandContext(argv);
1001
871
  Object.assign(currentCommandContext, { ...context, projectKey: batchProjectKey, });
@@ -1018,7 +888,8 @@ async function runBatch(flags) {
1018
888
  };
1019
889
  }
1020
890
  catch (error) {
1021
- const envelope = buildErrorReport(error);
891
+ // Steps validate concurrently; the shared context may belong to a sibling.
892
+ const envelope = buildErrorReport(error, { ...context, projectKey: batchProjectKey, });
1022
893
  return {
1023
894
  index,
1024
895
  args: redactArgv(argv),
@@ -1037,9 +908,7 @@ async function runBatch(flags) {
1037
908
  validateBatchStepModes(steps);
1038
909
  let projectKey = typeof flags["project-key"] === "string"
1039
910
  ? flags["project-key"]
1040
- : dataikuEnvironmentEnabled()
1041
- ? process.env.DATAIKU_PROJECT_KEY
1042
- : undefined;
911
+ : ambientProjectKey();
1043
912
  let client;
1044
913
  const needsClient = steps.some((argv) => batchStepNeedsClient(argv));
1045
914
  if (needsClient) {
@@ -1247,8 +1116,7 @@ function rawFlagValue(argv, flagName) {
1247
1116
  function commandIsProjectScoped(resource, action) {
1248
1117
  if (!resource)
1249
1118
  return false;
1250
- const usage = commands[resource]?.[action ?? ""]?.usage ?? "";
1251
- return inferRequiresProject(resource, action ?? "", usage);
1119
+ return inferRequiresProject(resource, action ?? "");
1252
1120
  }
1253
1121
  function rawCommandContext() {
1254
1122
  const argv = process.argv.slice(2);
@@ -1256,15 +1124,13 @@ function rawCommandContext() {
1256
1124
  const resource = currentCommandContext.resource ?? positionals[0];
1257
1125
  const action = currentCommandContext.action ?? positionals[1];
1258
1126
  const explicitProjectKey = rawFlagValue(argv, "project-key") ?? rawFlagValue(argv, "project");
1259
- const ambientProjectKey = dataikuEnvironmentEnabled()
1260
- ? process.env.DATAIKU_PROJECT_KEY
1261
- : undefined;
1127
+ const environmentProjectKey = ambientProjectKey();
1262
1128
  return {
1263
1129
  resource,
1264
1130
  action,
1265
1131
  projectKey: explicitProjectKey
1266
1132
  ?? (commandIsProjectScoped(resource, action)
1267
- ? currentCommandContext.projectKey ?? ambientProjectKey
1133
+ ? currentCommandContext.projectKey ?? environmentProjectKey
1268
1134
  : undefined),
1269
1135
  };
1270
1136
  }
@@ -1364,7 +1230,6 @@ function dssDiagnosticDetails(err) {
1364
1230
  return {};
1365
1231
  }
1366
1232
  }
1367
- const MISSING_PROJECT_KEY_ERROR_PREFIX = "projectKey is required";
1368
1233
  function isAmbiguousMutationFailure(err, context) {
1369
1234
  if (!(err instanceof DataikuError))
1370
1235
  return false;
@@ -1399,9 +1264,15 @@ function boundedFailureResult(result) {
1399
1264
  function errorExitCode(err) {
1400
1265
  if (err instanceof CommandResultFailure)
1401
1266
  return err.exitCode;
1402
- if (err instanceof ClientValidationError && err.code === "assertion_failed")
1267
+ if (err instanceof ClientValidationError
1268
+ && (err.code === "assertion_failed" || err.code === "long_running_failure"))
1403
1269
  return 4;
1404
- if (err instanceof ClientValidationError && err.code === "ambiguous_outcome")
1270
+ if (err instanceof ClientValidationError
1271
+ && (err.code === "ambiguous_outcome" || err.code === "internal_error"))
1272
+ return 2;
1273
+ // A permission/environment refusal is not a usage error (exit 1 is usage only).
1274
+ if ((err instanceof UsageError || err instanceof ClientValidationError)
1275
+ && err.code === "target_absence_unverifiable")
1405
1276
  return 2;
1406
1277
  if (err instanceof UsageError || err instanceof ClientValidationError)
1407
1278
  return 1;
@@ -1409,15 +1280,14 @@ function errorExitCode(err) {
1409
1280
  return 2;
1410
1281
  if (err instanceof DataikuError)
1411
1282
  return err.category === "transient" ? 3 : 2;
1412
- if (err instanceof Error && err.message.startsWith(MISSING_PROJECT_KEY_ERROR_PREFIX))
1413
- return 1;
1414
1283
  return 2;
1415
1284
  }
1416
- function buildErrorReport(err) {
1417
- const context = rawCommandContext();
1285
+ function buildErrorReport(err, contextOverride) {
1286
+ const context = contextOverride ?? rawCommandContext();
1418
1287
  const exitCode = errorExitCode(err);
1419
1288
  if (err instanceof ClientValidationError
1420
- && (err.code === "ambiguous_outcome" || err.code === "assertion_failed")) {
1289
+ && (err.code === "ambiguous_outcome" || err.code === "assertion_failed"
1290
+ || err.code === "long_running_failure")) {
1421
1291
  return {
1422
1292
  type: "error",
1423
1293
  ok: false,
@@ -1439,7 +1309,9 @@ function buildErrorReport(err) {
1439
1309
  code: err.code,
1440
1310
  category: err.code === "target_absence_unverifiable"
1441
1311
  ? "permission_or_environment"
1442
- : "usage",
1312
+ : err.code === "internal_error"
1313
+ ? "internal"
1314
+ : "usage",
1443
1315
  exitCode,
1444
1316
  ...(err.code === "target_absence_unverifiable" ? { retryable: false, } : {}),
1445
1317
  ...(err.hint ? { hint: err.hint, } : {}),
@@ -1517,18 +1389,6 @@ function buildErrorReport(err) {
1517
1389
  ...context,
1518
1390
  };
1519
1391
  }
1520
- if (err instanceof Error && err.message.startsWith(MISSING_PROJECT_KEY_ERROR_PREFIX)) {
1521
- return {
1522
- type: "error",
1523
- ok: false,
1524
- error: err.message,
1525
- code: "missing_required_flag",
1526
- category: "usage",
1527
- exitCode,
1528
- hint: "Pass --project-key or set DATAIKU_PROJECT_KEY.",
1529
- ...context,
1530
- };
1531
- }
1532
1392
  const message = err instanceof Error ? err.message : String(err);
1533
1393
  return {
1534
1394
  type: "error",
@@ -1579,9 +1439,7 @@ async function main() {
1579
1439
  action: positional[1],
1580
1440
  projectKey: typeof flags["project-key"] === "string"
1581
1441
  ? flags["project-key"]
1582
- : dataikuEnvironmentEnabled()
1583
- ? process.env.DATAIKU_PROJECT_KEY
1584
- : undefined,
1442
+ : ambientProjectKey(),
1585
1443
  });
1586
1444
  const metaCommand = await runMetaCommand(resource, positional[1], flags);
1587
1445
  if (metaCommand) {
@@ -118,7 +118,7 @@ export interface DataikuClientConfig {
118
118
  projectKey?: string;
119
119
  /** Request/header timeout and buffered-body budget; idle timeout per raw stream read (default 30 000 ms). */
120
120
  requestTimeoutMs?: number;
121
- /** Max retry attempts for idempotent requests (default 4, capped at 10) */
121
+ /** Maximum total attempts, initial request included, for idempotent requests (default 4, capped at 10; 1 = no retry) */
122
122
  retryMaxAttempts?: number;
123
123
  /**
124
124
  * Maximum bytes retained from a single response body by text/JSON consumers
@@ -336,6 +336,7 @@ export declare class DataikuClient {
336
336
  stream(path: string): Promise<Response>;
337
337
  /** Bound each pending read, not total transfer time; preserve large downloads and backpressure. */
338
338
  private withBodyDeadline;
339
+ /** JSON request headers; Content-Type only when a body is sent. */
339
340
  private getHeaders;
340
341
  private getAnyHeaders;
341
342
  private responseMeta;