dataiku-sdk 3.5.0 → 3.6.1
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.
- package/README.md +1 -1
- package/bin/dss.js +50 -9
- package/dist/build-metadata.json +1 -1
- package/dist/src/cli/coerce.d.ts +0 -1
- package/dist/src/cli/coerce.js +0 -5
- package/dist/src/cli/commands/app.js +3 -2
- package/dist/src/cli/commands/dataset.js +2 -4
- package/dist/src/cli/commands/job.js +4 -3
- package/dist/src/cli/contract.js +1 -1
- package/dist/src/cli/env.js +2 -1
- package/dist/src/cli/flags.d.ts +1 -0
- package/dist/src/cli/flags.js +4 -9
- package/dist/src/cli/helpers/app-permissions.js +5 -4
- package/dist/src/cli/helpers/flow-zone.js +6 -5
- package/dist/src/cli/version.d.ts +0 -2
- package/dist/src/cli/version.js +6 -9
- package/dist/src/cli.js +107 -222
- package/dist/src/client.d.ts +2 -1
- package/dist/src/client.js +25 -11
- package/dist/src/resources/base.d.ts +2 -0
- package/dist/src/resources/base.js +14 -2
- package/dist/src/resources/code-envs.js +1 -3
- package/dist/src/resources/data-quality.js +10 -10
- package/dist/src/resources/macros.d.ts +1 -1
- package/dist/src/resources/macros.js +1 -3
- package/dist/src/resources/recipes.js +1 -5
- package/dist/src/resources/saved-models.js +1 -13
- package/dist/src/resources/scenarios.js +1 -3
- package/dist/src/resources/sql.js +1 -5
- package/dist/src/resources/statistics.js +1 -3
- package/dist/src/utils/deep-merge.js +2 -4
- package/dist/src/utils/flow-map.js +1 -6
- package/dist/src/utils/project-archive.js +8 -12
- package/dist/src/utils/project-incarnation.js +3 -7
- package/dist/src/utils/records.d.ts +4 -0
- package/dist/src/utils/records.js +8 -0
- package/package.json +7 -11
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +1 -1
- package/skills/dataiku-dss/references/authentication.md +2 -2
- package/skills/dataiku-dss/references/discovery.md +1 -1
- package/skills/dataiku-dss/references/mutations.md +0 -1
- package/packages/types/dist/index.d.ts +0 -1062
- package/packages/types/dist/index.js +0 -871
package/dist/src/cli.js
CHANGED
|
@@ -9,6 +9,7 @@ import { executionMode, FLAG_ALIASES, isNegativeNumberToken, parseArgs, SHORT_FL
|
|
|
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 = {
|
|
@@ -418,81 +408,60 @@ async function validateDryRunHandlerPreconditions(handler, args, flags) {
|
|
|
418
408
|
throw error;
|
|
419
409
|
}
|
|
420
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
|
+
*/
|
|
421
417
|
async function validateMetaCommandInputs(resource, action, flags) {
|
|
422
|
-
if (resource
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
return "run";
|
|
427
|
-
}
|
|
418
|
+
if (META_COMMAND_RESOURCES[resource] !== true)
|
|
419
|
+
return undefined;
|
|
420
|
+
await loadCommandRuntime();
|
|
421
|
+
let resolved;
|
|
428
422
|
if (resource === "auth") {
|
|
429
423
|
const validActions = Object.keys(contract.AUTH_ACTIONS);
|
|
430
|
-
if (!action)
|
|
431
|
-
throw missingActionError("auth", validActions, "
|
|
432
|
-
}
|
|
424
|
+
if (!action)
|
|
425
|
+
throw missingActionError("auth", validActions, commandUsage("auth", "login"));
|
|
433
426
|
if (!contract.AUTH_ACTIONS[action]) {
|
|
434
427
|
throw unknownActionError("auth", action, validActions, "auth only supports 'login'. To check credentials/connectivity, run 'dss doctor'.");
|
|
435
428
|
}
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
if (resource === "install-skill") {
|
|
439
|
-
if (action !== undefined && action !== "run") {
|
|
440
|
-
throw unknownActionError("install-skill", action, ["run",]);
|
|
441
|
-
}
|
|
442
|
-
const { AGENTS, } = await import("./skill.js");
|
|
443
|
-
const agentFilter = typeof flags["agent"] === "string" ? flags["agent"] : undefined;
|
|
444
|
-
if (agentFilter && !AGENTS[agentFilter]) {
|
|
445
|
-
throw new UsageError(`Unknown agent: ${agentFilter}.`, "usage_error", COMMANDS_RUN_HINT, { agent: agentFilter, validAgents: Object.keys(AGENTS), });
|
|
446
|
-
}
|
|
447
|
-
return "run";
|
|
429
|
+
resolved = action;
|
|
448
430
|
}
|
|
449
|
-
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;
|
|
450
434
|
if (!action)
|
|
451
|
-
throw missingActionError(
|
|
452
|
-
if (action !==
|
|
453
|
-
throw unknownActionError(
|
|
454
|
-
|
|
435
|
+
throw missingActionError(resource, [only,], usage);
|
|
436
|
+
if (action !== only)
|
|
437
|
+
throw unknownActionError(resource, action, [only,]);
|
|
438
|
+
resolved = action;
|
|
455
439
|
}
|
|
456
|
-
|
|
457
|
-
if (!action)
|
|
458
|
-
throw missingActionError("commands", ["run",], contract.COMMANDS_USAGE);
|
|
459
|
-
if (action !== "run")
|
|
460
|
-
throw unknownActionError("commands", action, ["run",]);
|
|
461
|
-
validateSupportedCommandFlags("commands", "run", flags);
|
|
462
|
-
commandRegistrySelectors(flags);
|
|
463
|
-
return action;
|
|
464
|
-
}
|
|
465
|
-
if (resource === "version") {
|
|
440
|
+
else {
|
|
466
441
|
if (action !== undefined && action !== "run") {
|
|
467
|
-
throw unknownActionError(
|
|
442
|
+
throw unknownActionError(resource, action, ["run",]);
|
|
468
443
|
}
|
|
469
|
-
|
|
444
|
+
resolved = "run";
|
|
470
445
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
const entry = cachedCommandRegistry(
|
|
446
|
+
validateSupportedCommandFlags(resource, resolved, flags);
|
|
447
|
+
if (resource === "commands")
|
|
448
|
+
commandRegistrySelectors(flags);
|
|
449
|
+
if (resource === "cleanup" || resource === "batch") {
|
|
450
|
+
const entry = cachedCommandRegistry(resource)?.run;
|
|
476
451
|
if (entry)
|
|
477
|
-
validateRequiredCommandInputs(
|
|
478
|
-
return "run";
|
|
452
|
+
validateRequiredCommandInputs(resource, "run", [], flags, entry);
|
|
479
453
|
}
|
|
480
|
-
if (resource === "
|
|
481
|
-
|
|
482
|
-
throw unknownActionError("fixtures", action, ["run",]);
|
|
483
|
-
}
|
|
484
|
-
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");
|
|
485
456
|
}
|
|
486
|
-
if (resource === "
|
|
487
|
-
|
|
488
|
-
|
|
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), });
|
|
489
462
|
}
|
|
490
|
-
const entry = cachedCommandRegistry("batch")?.run;
|
|
491
|
-
if (entry)
|
|
492
|
-
validateRequiredCommandInputs("batch", "run", [], flags, entry);
|
|
493
|
-
return "run";
|
|
494
463
|
}
|
|
495
|
-
return
|
|
464
|
+
return resolved;
|
|
496
465
|
}
|
|
497
466
|
async function validateBatchStep(argv) {
|
|
498
467
|
const { positional, flags, } = parseArgs(argv);
|
|
@@ -532,29 +501,8 @@ function batchStepNeedsClient(argv) {
|
|
|
532
501
|
}
|
|
533
502
|
}
|
|
534
503
|
function batchStepCommandContext(argv) {
|
|
535
|
-
const
|
|
536
|
-
|
|
537
|
-
const arg = argv[index];
|
|
538
|
-
if (arg === "--") {
|
|
539
|
-
positionals.push(...argv.slice(index + 1));
|
|
540
|
-
break;
|
|
541
|
-
}
|
|
542
|
-
if (arg.startsWith("--")) {
|
|
543
|
-
const name = arg.slice(2).split("=")[0] ?? "";
|
|
544
|
-
const canonical = FLAG_ALIASES[name] ?? name;
|
|
545
|
-
if (!arg.includes("=") && VALUE_FLAGS.has(canonical))
|
|
546
|
-
index++;
|
|
547
|
-
continue;
|
|
548
|
-
}
|
|
549
|
-
if (arg.length === 2 && arg[0] === "-" && arg[1] !== "-") {
|
|
550
|
-
const long = SHORT_FLAGS[arg[1]];
|
|
551
|
-
if (long && VALUE_FLAGS.has(long))
|
|
552
|
-
index++;
|
|
553
|
-
continue;
|
|
554
|
-
}
|
|
555
|
-
positionals.push(arg);
|
|
556
|
-
}
|
|
557
|
-
return { resource: positionals[0], action: positionals[1], };
|
|
504
|
+
const [resource, action,] = rawPositionals(argv);
|
|
505
|
+
return { resource, action, };
|
|
558
506
|
}
|
|
559
507
|
const SENSITIVE_ARGV_VALUE_FLAGS = {
|
|
560
508
|
"api-key": true,
|
|
@@ -748,145 +696,78 @@ function batchPlan(flags) {
|
|
|
748
696
|
plannedAndDryRun: executionMode(flags).dryRun,
|
|
749
697
|
});
|
|
750
698
|
}
|
|
751
|
-
async function runMetaCommand(resource,
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
}
|
|
763
|
-
if (resource === "auth") {
|
|
764
|
-
await loadCommandRuntime();
|
|
765
|
-
const validActions = Object.keys(contract.AUTH_ACTIONS);
|
|
766
|
-
if (!action) {
|
|
767
|
-
throw missingActionError("auth", validActions, "dss auth login --url URL --api-key KEY");
|
|
768
|
-
}
|
|
769
|
-
currentCommandContext.action = action;
|
|
770
|
-
const authMeta = contract.AUTH_ACTIONS[action];
|
|
771
|
-
if (!authMeta) {
|
|
772
|
-
throw unknownActionError("auth", action, validActions, "auth only supports 'login'. To check credentials/connectivity, run 'dss doctor'.");
|
|
773
|
-
}
|
|
774
|
-
validateSupportedCommandFlags("auth", action, flags);
|
|
775
|
-
if (executionMode(flags).plan) {
|
|
776
|
-
return { action, result: authLoginPlan(flags), exitCode: 0, };
|
|
777
|
-
}
|
|
778
|
-
return { action, result: await authMeta.handler(flags), exitCode: 0, };
|
|
779
|
-
}
|
|
780
|
-
if (resource === "install-skill") {
|
|
781
|
-
await loadCommandRuntime();
|
|
782
|
-
if (action !== undefined && action !== "run") {
|
|
783
|
-
throw unknownActionError("install-skill", action, ["run",]);
|
|
784
|
-
}
|
|
785
|
-
currentCommandContext.action = action ?? "run";
|
|
786
|
-
validateSupportedCommandFlags("install-skill", "run", flags);
|
|
787
|
-
return { action: "run", result: await runInstallSkill(flags), exitCode: 0, };
|
|
788
|
-
}
|
|
789
|
-
if (resource === "agent") {
|
|
790
|
-
await loadCommandRuntime();
|
|
791
|
-
if (!action)
|
|
792
|
-
throw missingActionError("agent", ["contract",], contract.AGENT_CONTRACT_USAGE);
|
|
793
|
-
currentCommandContext.action = action;
|
|
794
|
-
if (action !== "contract")
|
|
795
|
-
throw unknownActionError("agent", action, ["contract",]);
|
|
796
|
-
validateSupportedCommandFlags("agent", "contract", flags);
|
|
797
|
-
return { action, result: contract.buildAgentContract(), exitCode: 0, };
|
|
798
|
-
}
|
|
799
|
-
if (resource === "commands") {
|
|
800
|
-
await loadCommandRuntime();
|
|
801
|
-
if (!action)
|
|
802
|
-
throw missingActionError("commands", ["run",], contract.COMMANDS_USAGE);
|
|
803
|
-
currentCommandContext.action = action;
|
|
804
|
-
if (action !== "run")
|
|
805
|
-
throw unknownActionError("commands", action, ["run",]);
|
|
806
|
-
validateSupportedCommandFlags("commands", "run", flags);
|
|
807
|
-
const selectors = commandRegistrySelectors(flags);
|
|
808
|
-
const outputPath = flags["output"];
|
|
809
|
-
if (selectors.length === 0 && typeof outputPath !== "string") {
|
|
810
|
-
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, };
|
|
811
710
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
}
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
}
|
|
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, };
|
|
826
724
|
}
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
await fs.writeFile(outputPath, `${JSON.stringify(exported)}\n`, "utf-8");
|
|
833
|
-
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, };
|
|
834
730
|
}
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
exitCode
|
|
839
|
-
|
|
840
|
-
}
|
|
841
|
-
if (resource === "version") {
|
|
842
|
-
await loadCommandRuntime();
|
|
843
|
-
if (action !== undefined && action !== "run") {
|
|
844
|
-
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, };
|
|
845
736
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
return { action: "run", result: cliVersionResult(), exitCode: 0, };
|
|
737
|
+
default:
|
|
738
|
+
return { action, result: await commandsRunResult(flags), exitCode: 0, };
|
|
849
739
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
if (
|
|
861
|
-
|
|
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, });
|
|
862
752
|
}
|
|
863
|
-
const
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
if (
|
|
869
|
-
throw unknownActionError(
|
|
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));
|
|
870
760
|
}
|
|
871
|
-
currentCommandContext.action = action ?? "run";
|
|
872
|
-
validateSupportedCommandFlags("fixtures", "run", flags);
|
|
873
|
-
const { runFixtures, } = await import("./cli/doctor.js");
|
|
874
|
-
return { action: "run", result: await runFixtures(flags), exitCode: 0, };
|
|
875
761
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
if (executionMode(flags).plan) {
|
|
884
|
-
return { action: "run", result: batchPlan(flags), exitCode: 0, };
|
|
885
|
-
}
|
|
886
|
-
const { result, exitCode, } = await runBatch(flags);
|
|
887
|
-
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, };
|
|
888
769
|
}
|
|
889
|
-
return
|
|
770
|
+
return selectedRegistry;
|
|
890
771
|
}
|
|
891
772
|
/**
|
|
892
773
|
* Cleanup-ledger support is checked before a handler runs so an unsupported
|
|
@@ -1389,6 +1270,10 @@ function errorExitCode(err) {
|
|
|
1389
1270
|
if (err instanceof ClientValidationError
|
|
1390
1271
|
&& (err.code === "ambiguous_outcome" || err.code === "internal_error"))
|
|
1391
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")
|
|
1276
|
+
return 2;
|
|
1392
1277
|
if (err instanceof UsageError || err instanceof ClientValidationError)
|
|
1393
1278
|
return 1;
|
|
1394
1279
|
if (isAmbiguousMutationFailure(err, rawCommandContext()))
|
package/dist/src/client.d.ts
CHANGED
|
@@ -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
|
-
/**
|
|
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;
|
package/dist/src/client.js
CHANGED
|
@@ -470,7 +470,7 @@ export class DataikuClient {
|
|
|
470
470
|
/* ---- public: HTTP verbs ---- */
|
|
471
471
|
async get(path, options) {
|
|
472
472
|
const deadlineAt = resolveGetDeadlineAt(options);
|
|
473
|
-
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, { method: "GET", headers: this.getHeaders(), }, options?.noRetry === true ? 1 : undefined, deadlineAt);
|
|
473
|
+
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, { method: "GET", headers: this.getHeaders(false), }, options?.noRetry === true ? 1 : undefined, deadlineAt);
|
|
474
474
|
return this.parseJsonResponse(res, deadlineAt);
|
|
475
475
|
}
|
|
476
476
|
/**
|
|
@@ -481,7 +481,7 @@ export class DataikuClient {
|
|
|
481
481
|
async getWithMetadata(path) {
|
|
482
482
|
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
483
483
|
method: "GET",
|
|
484
|
-
headers: this.getHeaders(),
|
|
484
|
+
headers: this.getHeaders(false),
|
|
485
485
|
});
|
|
486
486
|
const data = await this.parseJsonResponse(res);
|
|
487
487
|
return { data, meta: this.responseMeta(res.headers), };
|
|
@@ -543,7 +543,7 @@ export class DataikuClient {
|
|
|
543
543
|
}
|
|
544
544
|
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
545
545
|
method: "POST",
|
|
546
|
-
headers: this.getHeaders(),
|
|
546
|
+
headers: this.getHeaders(body !== undefined),
|
|
547
547
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
548
548
|
}, retryMaxAttempts);
|
|
549
549
|
return this.parseJsonResponse(res);
|
|
@@ -555,7 +555,7 @@ export class DataikuClient {
|
|
|
555
555
|
async postText(path, body) {
|
|
556
556
|
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
557
557
|
method: "POST",
|
|
558
|
-
headers: this.getHeaders(),
|
|
558
|
+
headers: this.getHeaders(body !== undefined),
|
|
559
559
|
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
560
560
|
});
|
|
561
561
|
const { text, truncated, } = await this.readBoundedBodyText(res, this.maxResponseBodyBytes);
|
|
@@ -600,7 +600,7 @@ export class DataikuClient {
|
|
|
600
600
|
async put(path, body) {
|
|
601
601
|
const res = await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
602
602
|
method: "PUT",
|
|
603
|
-
headers: this.getHeaders(),
|
|
603
|
+
headers: this.getHeaders(true),
|
|
604
604
|
body: JSON.stringify(body),
|
|
605
605
|
});
|
|
606
606
|
return this.parseJsonResponse(res);
|
|
@@ -608,13 +608,13 @@ export class DataikuClient {
|
|
|
608
608
|
async del(path) {
|
|
609
609
|
await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
610
610
|
method: "DELETE",
|
|
611
|
-
headers: this.getHeaders(),
|
|
611
|
+
headers: this.getHeaders(false),
|
|
612
612
|
});
|
|
613
613
|
}
|
|
614
614
|
async putVoid(path, body) {
|
|
615
615
|
await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
616
616
|
method: "PUT",
|
|
617
|
-
headers: this.getHeaders(),
|
|
617
|
+
headers: this.getHeaders(true),
|
|
618
618
|
body: JSON.stringify(body),
|
|
619
619
|
});
|
|
620
620
|
}
|
|
@@ -626,7 +626,7 @@ export class DataikuClient {
|
|
|
626
626
|
async putVoidNoBody(path) {
|
|
627
627
|
await this.fetchWithRetry(`${this.baseUrl}${path}`, {
|
|
628
628
|
method: "PUT",
|
|
629
|
-
headers: this.getHeaders(),
|
|
629
|
+
headers: this.getHeaders(false),
|
|
630
630
|
});
|
|
631
631
|
}
|
|
632
632
|
/**
|
|
@@ -731,11 +731,12 @@ export class DataikuClient {
|
|
|
731
731
|
return response;
|
|
732
732
|
}
|
|
733
733
|
/* ---- private: headers ---- */
|
|
734
|
-
|
|
734
|
+
/** JSON request headers; Content-Type only when a body is sent. */
|
|
735
|
+
getHeaders(withBody) {
|
|
735
736
|
return {
|
|
736
737
|
Authorization: `Bearer ${this.apiKey}`,
|
|
737
738
|
Accept: "application/json",
|
|
738
|
-
"Content-Type": "application/json",
|
|
739
|
+
...(withBody ? { "Content-Type": "application/json", } : {}),
|
|
739
740
|
};
|
|
740
741
|
}
|
|
741
742
|
getAnyHeaders() {
|
|
@@ -1030,7 +1031,20 @@ export class DataikuClient {
|
|
|
1030
1031
|
elapsedMs: Date.now() - startedAt,
|
|
1031
1032
|
});
|
|
1032
1033
|
if (!res.ok) {
|
|
1033
|
-
|
|
1034
|
+
// The status is already known; a body that misses the deadline must not
|
|
1035
|
+
// turn a real 5xx/4xx into a bare transport timeout.
|
|
1036
|
+
let text = "";
|
|
1037
|
+
let truncated = false;
|
|
1038
|
+
try {
|
|
1039
|
+
({ text, truncated, } = await this.readBoundedBodyText(res, this.maxResponseBodyBytes, startedAt + attemptTimeoutMs));
|
|
1040
|
+
}
|
|
1041
|
+
catch (bodyError) {
|
|
1042
|
+
// Oversized bodies keep their own classified error; any other read
|
|
1043
|
+
// failure (deadline abort, dropped connection) keeps the known status.
|
|
1044
|
+
if (bodyError instanceof DataikuError && bodyError.status !== 0)
|
|
1045
|
+
throw bodyError;
|
|
1046
|
+
text = `(response body not received within ${attemptTimeoutMs}ms)`;
|
|
1047
|
+
}
|
|
1034
1048
|
const canRetry = retryEnabled && attempt < maxAttempts && isTransientError(res.status, text);
|
|
1035
1049
|
const serverDelayMs = canRetry ? retryAfterDelayMs(res.headers.get("retry-after")) : 0;
|
|
1036
1050
|
// A longer server wait declines automatic retry; never clamp it into an early request.
|
|
@@ -3,6 +3,8 @@ import type { DataikuClient } from "../client.js";
|
|
|
3
3
|
export declare function requireArrayResponse<T>(value: unknown, operation: string): T[];
|
|
4
4
|
/** Validate identifiers without changing significant whitespace or encoding. */
|
|
5
5
|
export declare function requireNonEmpty(value: unknown, name: string): string;
|
|
6
|
+
export declare function requireObject(value: unknown, name: string): Record<string, unknown>;
|
|
7
|
+
export declare function requireNonEmptyArray(value: string[], name: string): string[];
|
|
6
8
|
export declare abstract class BaseResource {
|
|
7
9
|
protected readonly client: DataikuClient;
|
|
8
10
|
constructor(client: DataikuClient);
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { ClientValidationError,
|
|
1
|
+
import { ClientValidationError, unexpectedResponseError, } from "../errors.js";
|
|
2
2
|
/** Never disguise an unexpected successful HTTP payload as an empty listing. */
|
|
3
3
|
export function requireArrayResponse(value, operation) {
|
|
4
4
|
if (!Array.isArray(value)) {
|
|
5
|
-
throw
|
|
5
|
+
throw unexpectedResponseError(`${operation}: DSS did not return an array.`);
|
|
6
6
|
}
|
|
7
7
|
return value;
|
|
8
8
|
}
|
|
@@ -13,6 +13,18 @@ export function requireNonEmpty(value, name) {
|
|
|
13
13
|
}
|
|
14
14
|
return value;
|
|
15
15
|
}
|
|
16
|
+
export function requireObject(value, name) {
|
|
17
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
18
|
+
throw new ClientValidationError(`${name} must be a JSON object.`, "validation_failed");
|
|
19
|
+
}
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
export function requireNonEmptyArray(value, name) {
|
|
23
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
24
|
+
throw new ClientValidationError(`${name} must be a non-empty array.`, "validation_failed");
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
16
28
|
export class BaseResource {
|
|
17
29
|
client;
|
|
18
30
|
constructor(client) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ClientValidationError, } from "../errors.js";
|
|
2
2
|
import { CodeEnvLogSummaryArraySchema, CodeEnvSummaryArraySchema, CodeEnvUsageArraySchema, CodeEnvVersionForProjectSchema, } from "../schemas.js";
|
|
3
|
+
import { isRecord, } from "../utils/records.js";
|
|
3
4
|
import { stableHash, } from "../utils/stable-hash.js";
|
|
4
5
|
import { BaseResource, } from "./base.js";
|
|
5
6
|
/** Byte cap applied to fetched code-env logs so stdout/JSON stays bounded. */
|
|
@@ -212,6 +213,3 @@ function normalizePackageList(packages) {
|
|
|
212
213
|
const lines = Array.isArray(packages) ? packages : packages.split(/\r?\n/);
|
|
213
214
|
return lines.map((line) => line.trim()).filter((line) => line.length > 0).join("\n");
|
|
214
215
|
}
|
|
215
|
-
function isRecord(value) {
|
|
216
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
217
|
-
}
|