dataiku-sdk 3.2.0 → 3.4.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.
- package/README.md +151 -46
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +102 -0
- package/dist/packages/types/src/index.js +76 -0
- package/dist/src/cli/commands/app.js +57 -15
- package/dist/src/cli/commands/bundle.js +1 -1
- package/dist/src/cli/commands/code-env.js +13 -7
- package/dist/src/cli/commands/code.js +1 -1
- package/dist/src/cli/commands/connection.d.ts +9 -0
- package/dist/src/cli/commands/connection.js +267 -1
- package/dist/src/cli/commands/data-collection.d.ts +2 -0
- package/dist/src/cli/commands/data-collection.js +164 -0
- package/dist/src/cli/commands/dataset.js +162 -33
- package/dist/src/cli/commands/future.js +2 -2
- package/dist/src/cli/commands/group.d.ts +2 -0
- package/dist/src/cli/commands/group.js +109 -0
- package/dist/src/cli/commands/index.js +151 -78
- package/dist/src/cli/commands/job.js +4 -2
- package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
- package/dist/src/cli/commands/knowledge-bank.js +80 -0
- package/dist/src/cli/commands/llm.d.ts +2 -0
- package/dist/src/cli/commands/llm.js +93 -0
- package/dist/src/cli/commands/macro.d.ts +2 -0
- package/dist/src/cli/commands/macro.js +221 -0
- package/dist/src/cli/commands/ml-task.js +18 -6
- package/dist/src/cli/commands/notebook.js +6 -2
- package/dist/src/cli/commands/plugin.d.ts +2 -0
- package/dist/src/cli/commands/plugin.js +799 -0
- package/dist/src/cli/commands/project-folder.d.ts +2 -0
- package/dist/src/cli/commands/project-folder.js +197 -0
- package/dist/src/cli/commands/project-git.js +15 -55
- package/dist/src/cli/commands/project.js +52 -6
- package/dist/src/cli/commands/recipe.js +36 -1
- package/dist/src/cli/commands/saved-model.d.ts +5 -0
- package/dist/src/cli/commands/saved-model.js +388 -1
- package/dist/src/cli/commands/scenario.js +139 -2
- package/dist/src/cli/commands/user.d.ts +8 -0
- package/dist/src/cli/commands/user.js +296 -0
- package/dist/src/cli/contract.d.ts +1 -2
- package/dist/src/cli/contract.js +1098 -297
- package/dist/src/cli/flags.js +30 -1
- package/dist/src/cli/helpers/app-successor.d.ts +19 -4
- package/dist/src/cli/helpers/app-successor.js +90 -30
- package/dist/src/cli/helpers/cleanup.js +3 -1
- package/dist/src/cli/helpers/flow-zone.js +1 -2
- package/dist/src/cli/output-schemas.d.ts +24 -0
- package/dist/src/cli/output-schemas.js +233 -0
- package/dist/src/cli/output.d.ts +1 -1
- package/dist/src/cli/output.js +23 -10
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +47 -1
- package/dist/src/cli.js +147 -41
- package/dist/src/client.d.ts +92 -5
- package/dist/src/client.js +202 -31
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +8 -4
- package/dist/src/generated/action-output-schemas.json +1735 -0
- package/dist/src/index.d.ts +16 -7
- package/dist/src/index.js +11 -2
- package/dist/src/resources/analyses.js +1 -8
- package/dist/src/resources/applications.d.ts +8 -6
- package/dist/src/resources/applications.js +21 -21
- package/dist/src/resources/base.d.ts +4 -0
- package/dist/src/resources/base.js +15 -0
- package/dist/src/resources/bundles.d.ts +9 -4
- package/dist/src/resources/bundles.js +10 -5
- package/dist/src/resources/connections.d.ts +78 -1
- package/dist/src/resources/connections.js +132 -1
- package/dist/src/resources/data-collections.d.ts +118 -0
- package/dist/src/resources/data-collections.js +49 -0
- package/dist/src/resources/datasets.d.ts +69 -0
- package/dist/src/resources/datasets.js +136 -5
- package/dist/src/resources/flow-zones.d.ts +6 -2
- package/dist/src/resources/flow-zones.js +6 -3
- package/dist/src/resources/futures.d.ts +6 -1
- package/dist/src/resources/futures.js +68 -22
- package/dist/src/resources/groups.d.ts +47 -0
- package/dist/src/resources/groups.js +56 -0
- package/dist/src/resources/jobs.d.ts +8 -13
- package/dist/src/resources/jobs.js +71 -34
- package/dist/src/resources/knowledge-banks.d.ts +58 -0
- package/dist/src/resources/knowledge-banks.js +61 -0
- package/dist/src/resources/llms.d.ts +92 -0
- package/dist/src/resources/llms.js +62 -0
- package/dist/src/resources/macros.d.ts +121 -0
- package/dist/src/resources/macros.js +231 -0
- package/dist/src/resources/ml-tasks.d.ts +37 -2
- package/dist/src/resources/ml-tasks.js +109 -20
- package/dist/src/resources/model-evaluation-stores.js +1 -8
- package/dist/src/resources/notebooks.d.ts +43 -7
- package/dist/src/resources/notebooks.js +53 -13
- package/dist/src/resources/plugins.d.ts +263 -0
- package/dist/src/resources/plugins.js +455 -0
- package/dist/src/resources/project-folders.d.ts +71 -0
- package/dist/src/resources/project-folders.js +57 -0
- package/dist/src/resources/project-git.d.ts +12 -12
- package/dist/src/resources/project-git.js +84 -82
- package/dist/src/resources/project-library.d.ts +0 -1
- package/dist/src/resources/project-library.js +1 -17
- package/dist/src/resources/projects.d.ts +23 -3
- package/dist/src/resources/projects.js +60 -34
- package/dist/src/resources/recipes.d.ts +15 -1
- package/dist/src/resources/recipes.js +73 -6
- package/dist/src/resources/saved-models.d.ts +134 -1
- package/dist/src/resources/saved-models.js +214 -6
- package/dist/src/resources/scenarios.d.ts +105 -0
- package/dist/src/resources/scenarios.js +232 -106
- package/dist/src/resources/sql.js +5 -1
- package/dist/src/resources/users.d.ts +129 -0
- package/dist/src/resources/users.js +128 -0
- package/dist/src/resources/webapps.d.ts +15 -2
- package/dist/src/resources/webapps.js +18 -4
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/utils/deep-merge.d.ts +1 -1
- package/dist/src/utils/git-reference.d.ts +12 -0
- package/dist/src/utils/git-reference.js +38 -0
- package/dist/src/utils/polling.d.ts +20 -0
- package/dist/src/utils/polling.js +27 -0
- package/dist/src/utils/response-file.js +1 -1
- package/dist/src/utils/secret-sanitize.d.ts +54 -0
- package/dist/src/utils/secret-sanitize.js +101 -0
- package/package.json +9 -8
- package/packages/types/dist/index.d.ts +102 -0
- package/packages/types/dist/index.js +76 -0
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +11 -7
- package/skills/dataiku-dss/references/app-releases.md +8 -8
- package/skills/dataiku-dss/references/authentication.md +6 -6
- package/skills/dataiku-dss/references/discovery.md +4 -4
- package/skills/dataiku-dss/references/mutations.md +1 -1
- package/skills/dataiku-dss/references/troubleshooting.md +2 -2
package/dist/src/cli/output.js
CHANGED
|
@@ -47,14 +47,15 @@ function projectionRecords(result) {
|
|
|
47
47
|
const items = Array.isArray(result) ? result : [result,];
|
|
48
48
|
return items.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
49
49
|
}
|
|
50
|
-
function warnUnknownProjectionFields(result, fields) {
|
|
50
|
+
function warnUnknownProjectionFields(result, fields, availableFieldNames) {
|
|
51
51
|
const records = projectionRecords(result);
|
|
52
52
|
if (records.length === 0)
|
|
53
53
|
return;
|
|
54
54
|
const unknownFields = fields.filter((field) => !records.some((record) => resolveFieldPath(record, field).found));
|
|
55
55
|
if (unknownFields.length === 0)
|
|
56
56
|
return;
|
|
57
|
-
const availableFields =
|
|
57
|
+
const availableFields = availableFieldNames?.()
|
|
58
|
+
?? [...new Set(records.flatMap((record) => Object.keys(record))),].sort();
|
|
58
59
|
enqueueCliWarning({
|
|
59
60
|
code: "field_projection_missing",
|
|
60
61
|
fields: unknownFields,
|
|
@@ -67,8 +68,8 @@ function warnUnknownProjectionFields(result, fields) {
|
|
|
67
68
|
* element-wise; scalars and string results pass through untouched. Requested keys
|
|
68
69
|
* that are absent become null so every row keeps a stable, predictable shape.
|
|
69
70
|
*/
|
|
70
|
-
export function projectResultFields(result, fields) {
|
|
71
|
-
warnUnknownProjectionFields(result, fields);
|
|
71
|
+
export function projectResultFields(result, fields, availableFieldNames) {
|
|
72
|
+
warnUnknownProjectionFields(result, fields, availableFieldNames);
|
|
72
73
|
if (Array.isArray(result))
|
|
73
74
|
return result.map((item) => pickResultFields(item, fields));
|
|
74
75
|
return pickResultFields(result, fields);
|
|
@@ -98,7 +99,7 @@ export function transientBodyWithTargetContext(body, target, elapsedMs) {
|
|
|
98
99
|
}
|
|
99
100
|
export function addTransientTargetContext(error, target, elapsedMs) {
|
|
100
101
|
if (error instanceof DataikuError && error.category === "transient") {
|
|
101
|
-
throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId, { target, elapsedMs, });
|
|
102
|
+
throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId, { target, elapsedMs, bodyTruncated: error.bodyTruncated, });
|
|
102
103
|
}
|
|
103
104
|
throw error;
|
|
104
105
|
}
|
|
@@ -106,10 +107,16 @@ export function isFailedWaitResult(result) {
|
|
|
106
107
|
if (result === null || typeof result !== "object" || Array.isArray(result))
|
|
107
108
|
return false;
|
|
108
109
|
const record = result;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
if (record.success !== false)
|
|
111
|
+
return false;
|
|
112
|
+
if (typeof record.elapsedMs !== "number" || typeof record.pollCount !== "number")
|
|
113
|
+
return false;
|
|
114
|
+
// Job/future wait results name the terminal state as a string; macro wait
|
|
115
|
+
// results identify themselves by their runnable identity fields with the
|
|
116
|
+
// poll state attached as an object (or omitted entirely).
|
|
117
|
+
if (typeof record.state === "string" || typeof record.outcome === "string")
|
|
118
|
+
return true;
|
|
119
|
+
return typeof record.runnableType === "string" && typeof record.runId === "string";
|
|
113
120
|
}
|
|
114
121
|
export function isAssertionFailureResult(result) {
|
|
115
122
|
if (result === null || typeof result !== "object" || Array.isArray(result))
|
|
@@ -172,7 +179,13 @@ export class CommandResultFailure extends Error {
|
|
|
172
179
|
export function commandFailureMessage(result) {
|
|
173
180
|
if (isFailedWaitResult(result)) {
|
|
174
181
|
const record = result;
|
|
175
|
-
const state = typeof record.state === "string"
|
|
182
|
+
const state = typeof record.state === "string"
|
|
183
|
+
? record.state
|
|
184
|
+
: typeof record.outcome === "string"
|
|
185
|
+
? record.outcome
|
|
186
|
+
: typeof record.timedOut === "boolean"
|
|
187
|
+
? "timed out"
|
|
188
|
+
: undefined;
|
|
176
189
|
return `Command completed with failed long-running result${state ? `: ${state}` : ""}.`;
|
|
177
190
|
}
|
|
178
191
|
if (isAssertionFailureResult(result)) {
|
package/dist/src/cli/usage.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StableErrorCode } from "../errors.js";
|
|
2
2
|
export declare const RESOURCE_NAMES: string[];
|
|
3
|
+
export declare function inferRequiresProject(resource: string, action: string, usage: string): boolean;
|
|
3
4
|
export declare class UsageError extends Error {
|
|
4
5
|
readonly code: StableErrorCode;
|
|
5
6
|
readonly hint?: string;
|
package/dist/src/cli/usage.js
CHANGED
|
@@ -9,6 +9,15 @@ export const RESOURCE_NAMES = [
|
|
|
9
9
|
"bundle",
|
|
10
10
|
"project-deployer",
|
|
11
11
|
"project-git",
|
|
12
|
+
"project-folder",
|
|
13
|
+
"data-collection",
|
|
14
|
+
"llm",
|
|
15
|
+
"knowledge-bank",
|
|
16
|
+
"macro",
|
|
17
|
+
"plugin",
|
|
18
|
+
"user",
|
|
19
|
+
"group",
|
|
20
|
+
"project",
|
|
12
21
|
"project-library",
|
|
13
22
|
"streaming-endpoint",
|
|
14
23
|
"continuous-activity",
|
|
@@ -44,10 +53,47 @@ export const RESOURCE_NAMES = [
|
|
|
44
53
|
"commands",
|
|
45
54
|
"fixtures",
|
|
46
55
|
"install-skill",
|
|
47
|
-
"project",
|
|
48
56
|
"version",
|
|
49
57
|
]
|
|
50
58
|
.sort();
|
|
59
|
+
const PROJECT_SCOPED_RESOURCES = new Set([
|
|
60
|
+
"analysis",
|
|
61
|
+
"data-quality",
|
|
62
|
+
"dashboard",
|
|
63
|
+
"knowledge-bank",
|
|
64
|
+
"dataset",
|
|
65
|
+
"llm",
|
|
66
|
+
"flow-zone",
|
|
67
|
+
"insight",
|
|
68
|
+
"folder",
|
|
69
|
+
"fixtures",
|
|
70
|
+
"job",
|
|
71
|
+
"notebook",
|
|
72
|
+
"macro",
|
|
73
|
+
"ml-task",
|
|
74
|
+
"model-evaluation-store",
|
|
75
|
+
"recipe",
|
|
76
|
+
"scenario",
|
|
77
|
+
"sql",
|
|
78
|
+
"variable",
|
|
79
|
+
"saved-model",
|
|
80
|
+
"wiki",
|
|
81
|
+
]);
|
|
82
|
+
export function inferRequiresProject(resource, action, usage) {
|
|
83
|
+
if (resource === "agent" || resource === "auth" || resource === "doctor" || resource === "commands"
|
|
84
|
+
|| resource === "install-skill" || resource === "version")
|
|
85
|
+
return false;
|
|
86
|
+
if (resource === "sql" && action === "query")
|
|
87
|
+
return false;
|
|
88
|
+
if (PROJECT_SCOPED_RESOURCES.has(resource))
|
|
89
|
+
return true;
|
|
90
|
+
// Plugin project scope is optional, not a requirement.
|
|
91
|
+
if (resource === "plugin")
|
|
92
|
+
return false;
|
|
93
|
+
if (resource === "project-git")
|
|
94
|
+
return !action.startsWith("future-");
|
|
95
|
+
return usage.includes("--project-key");
|
|
96
|
+
}
|
|
51
97
|
export class UsageError extends Error {
|
|
52
98
|
code;
|
|
53
99
|
hint;
|
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
|
-
|
|
18
|
+
import { sanitizeSecrets, } from "./utils/secret-sanitize.js";
|
|
18
19
|
let contract;
|
|
19
20
|
let commandRuntimeLoad;
|
|
20
|
-
//
|
|
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 ??=
|
|
23
|
-
|
|
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
|
|
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
|
|
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 =
|
|
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:
|
|
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
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1655
|
+
if (errorReportNeedsCommandRuntime(err))
|
|
1656
|
+
await loadCommandRuntime();
|
|
1551
1657
|
writeErrorReport(err);
|
|
1552
1658
|
process.exitCode = errorExitCode(err);
|
|
1553
1659
|
});
|