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
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { requiredJsonInput, } from "../coerce.js";
|
|
2
|
+
import { executionMode, } from "../flags.js";
|
|
3
|
+
import { isNotFoundError, planResult, skipResult, } from "../output.js";
|
|
4
|
+
import { requireArgs, UsageError, } from "../usage.js";
|
|
5
|
+
const PLAN_FAILURE_CODES = { usage: 1, error: 2, transient: 3, };
|
|
6
|
+
function folderEndpoint(folderId, suffix = "") {
|
|
7
|
+
return `/public/api/project-folders/${encodeURIComponent(folderId)}${suffix}`;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Positional ids are server keys: an empty string would silently target the
|
|
11
|
+
* parent collection (e.g. DELETE /public/api/project-folders/ = the root
|
|
12
|
+
* folder) instead of failing as a usage error. Runs for live, --plan, and
|
|
13
|
+
* --dry-run alike with zero network.
|
|
14
|
+
*/
|
|
15
|
+
function nonEmptyPositionals(args, names, usage) {
|
|
16
|
+
args.forEach((value, index) => {
|
|
17
|
+
if (value.trim().length === 0) {
|
|
18
|
+
throw new UsageError(`<${names[index] ?? `arg${String(index + 1)}`}> must be a non-empty identifier. Usage: ${usage}`);
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
const GET_USAGE = "dss project-folder get <folderId>";
|
|
23
|
+
const SETTINGS_GET_USAGE = "dss project-folder settings-get <folderId>";
|
|
24
|
+
const SETTINGS_SET_USAGE = "dss project-folder settings-set <folderId> (--data JSON|--data-file PATH|--stdin) [--dry-run]";
|
|
25
|
+
const MOVE_USAGE = "dss project-folder move <folderId> <destinationFolderId> [--dry-run]";
|
|
26
|
+
const DELETE_USAGE = "dss project-folder delete <folderId> [--if-exists] [--dry-run]";
|
|
27
|
+
const CREATE_CHILD_USAGE = "dss project-folder create-child <parentFolderId> --name NAME [--dry-run]";
|
|
28
|
+
const MOVE_PROJECT_USAGE = "dss project-folder move-project <folderId> <projectKey> <destinationFolderId> [--dry-run]";
|
|
29
|
+
export const projectFolderCommands = {
|
|
30
|
+
root: {
|
|
31
|
+
handler: (c) => c.projectFolders.root(),
|
|
32
|
+
usage: "dss project-folder root",
|
|
33
|
+
description: "Get the root project folder definition (visible children and projects).",
|
|
34
|
+
examples: ["dss project-folder root",],
|
|
35
|
+
},
|
|
36
|
+
get: {
|
|
37
|
+
validate: (a) => {
|
|
38
|
+
requireArgs(a, 1, GET_USAGE);
|
|
39
|
+
nonEmptyPositionals(a, ["folderId",], GET_USAGE);
|
|
40
|
+
},
|
|
41
|
+
handler: (c, a) => c.projectFolders.get(a[0]),
|
|
42
|
+
usage: GET_USAGE,
|
|
43
|
+
description: "Get a project folder definition (children and project keys).",
|
|
44
|
+
examples: ["dss project-folder get KdLmPU6",],
|
|
45
|
+
},
|
|
46
|
+
"settings-get": {
|
|
47
|
+
validate: (a) => {
|
|
48
|
+
requireArgs(a, 1, SETTINGS_GET_USAGE);
|
|
49
|
+
nonEmptyPositionals(a, ["folderId",], SETTINGS_GET_USAGE);
|
|
50
|
+
},
|
|
51
|
+
handler: (c, a) => c.projectFolders.getSettings(a[0]),
|
|
52
|
+
usage: SETTINGS_GET_USAGE,
|
|
53
|
+
description: "Get project folder settings (name, owner, permissions). Admin required.",
|
|
54
|
+
examples: ["dss project-folder settings-get KdLmPU6",],
|
|
55
|
+
},
|
|
56
|
+
"settings-set": {
|
|
57
|
+
validate: (a) => {
|
|
58
|
+
requireArgs(a, 1, SETTINGS_SET_USAGE);
|
|
59
|
+
nonEmptyPositionals(a, ["folderId",], SETTINGS_SET_USAGE);
|
|
60
|
+
},
|
|
61
|
+
handler: async (c, a, f) => {
|
|
62
|
+
const settings = requiredJsonInput(f, "--data, --data-file, or --stdin is required (project folder settings).");
|
|
63
|
+
if (executionMode(f).dryRun) {
|
|
64
|
+
return planResult("project-folder", "settings-set", {
|
|
65
|
+
asyncKind: "none",
|
|
66
|
+
method: "PUT",
|
|
67
|
+
endpoint: folderEndpoint(a[0], "/settings"),
|
|
68
|
+
identifiers: { folderId: a[0], },
|
|
69
|
+
payload: settings,
|
|
70
|
+
idempotency: "none",
|
|
71
|
+
exitCodesOnFailure: PLAN_FAILURE_CODES,
|
|
72
|
+
plannedAndDryRun: true,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
await c.projectFolders.updateSettings(a[0], settings);
|
|
76
|
+
return { updated: a[0], resource: "project-folder", };
|
|
77
|
+
},
|
|
78
|
+
usage: SETTINGS_SET_USAGE,
|
|
79
|
+
description: "Replace project folder settings (name, owner, permissions). Admin required.",
|
|
80
|
+
examples: [
|
|
81
|
+
"dss project-folder settings-set KdLmPU6 --data-file settings.json --dry-run",
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
move: {
|
|
85
|
+
validate: (a) => {
|
|
86
|
+
requireArgs(a, 2, MOVE_USAGE);
|
|
87
|
+
nonEmptyPositionals(a, ["folderId", "destinationFolderId",], MOVE_USAGE);
|
|
88
|
+
},
|
|
89
|
+
handler: async (c, a, f) => {
|
|
90
|
+
if (executionMode(f).dryRun) {
|
|
91
|
+
return planResult("project-folder", "move", {
|
|
92
|
+
asyncKind: "none",
|
|
93
|
+
method: "POST",
|
|
94
|
+
endpoint: `${folderEndpoint(a[0], "/move")}?destination=${encodeURIComponent(a[1])}`,
|
|
95
|
+
identifiers: { folderId: a[0], destination: a[1], },
|
|
96
|
+
idempotency: "none",
|
|
97
|
+
exitCodesOnFailure: PLAN_FAILURE_CODES,
|
|
98
|
+
plannedAndDryRun: true,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
await c.projectFolders.move(a[0], a[1]);
|
|
102
|
+
return { moved: a[0], destination: a[1], resource: "project-folder", };
|
|
103
|
+
},
|
|
104
|
+
usage: MOVE_USAGE,
|
|
105
|
+
description: "Move a project folder (and its content) into another project folder. Cannot move into a sub-folder of itself.",
|
|
106
|
+
examples: ["dss project-folder move KdLmPU6 dgKywsx",],
|
|
107
|
+
},
|
|
108
|
+
delete: {
|
|
109
|
+
validate: (a) => {
|
|
110
|
+
requireArgs(a, 1, DELETE_USAGE);
|
|
111
|
+
nonEmptyPositionals(a, ["folderId",], DELETE_USAGE);
|
|
112
|
+
},
|
|
113
|
+
handler: async (c, a, f) => {
|
|
114
|
+
// Dry-run must be a validated plan with zero HTTP: no existence GET.
|
|
115
|
+
if (executionMode(f).dryRun) {
|
|
116
|
+
return planResult("project-folder", "delete", {
|
|
117
|
+
asyncKind: "none",
|
|
118
|
+
method: "DELETE",
|
|
119
|
+
endpoint: folderEndpoint(a[0]),
|
|
120
|
+
identifiers: { folderId: a[0], },
|
|
121
|
+
idempotency: "if-exists",
|
|
122
|
+
exitCodesOnFailure: PLAN_FAILURE_CODES,
|
|
123
|
+
plannedAndDryRun: true,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
// --if-exists (live only) pays one existence GET; a 404 becomes a skip.
|
|
127
|
+
if (f["if-exists"] === true) {
|
|
128
|
+
const current = await c.projectFolders.get(a[0]).catch((error) => {
|
|
129
|
+
if (isNotFoundError(error))
|
|
130
|
+
return undefined;
|
|
131
|
+
throw error;
|
|
132
|
+
});
|
|
133
|
+
if (!current)
|
|
134
|
+
return skipResult("project-folder", a[0], "missing");
|
|
135
|
+
}
|
|
136
|
+
await c.projectFolders.delete(a[0]);
|
|
137
|
+
return { deleted: a[0], resource: "project-folder", };
|
|
138
|
+
},
|
|
139
|
+
usage: DELETE_USAGE,
|
|
140
|
+
description: "Delete an empty project folder (no sub-folders, no projects). Admin required.",
|
|
141
|
+
examples: ["dss project-folder delete KdLmPU6 --dry-run",],
|
|
142
|
+
},
|
|
143
|
+
"create-child": {
|
|
144
|
+
validate: (a, f) => {
|
|
145
|
+
const name = f["name"];
|
|
146
|
+
if (typeof name === "string" && name.trim().length === 0) {
|
|
147
|
+
throw new UsageError(`--name must be a non-empty folder name. Usage: ${CREATE_CHILD_USAGE}`);
|
|
148
|
+
}
|
|
149
|
+
if (!a[0] || typeof name !== "string" || name.length === 0) {
|
|
150
|
+
throw new UsageError(`Usage: ${CREATE_CHILD_USAGE}`);
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
handler: async (c, a, f) => {
|
|
154
|
+
const name = f["name"];
|
|
155
|
+
if (executionMode(f).dryRun) {
|
|
156
|
+
return planResult("project-folder", "create-child", {
|
|
157
|
+
asyncKind: "none",
|
|
158
|
+
method: "POST",
|
|
159
|
+
endpoint: `${folderEndpoint(a[0], "/children")}?name=${encodeURIComponent(name)}`,
|
|
160
|
+
identifiers: { parentFolderId: a[0], name, },
|
|
161
|
+
idempotency: "none",
|
|
162
|
+
exitCodesOnFailure: PLAN_FAILURE_CODES,
|
|
163
|
+
plannedAndDryRun: true,
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const created = await c.projectFolders.createChild(a[0], name);
|
|
167
|
+
return { created: created.id ?? name, resource: "project-folder", ...created, };
|
|
168
|
+
},
|
|
169
|
+
usage: CREATE_CHILD_USAGE,
|
|
170
|
+
description: "Create a sub-project folder. WRITE_CONTENTS required on the parent.",
|
|
171
|
+
examples: ["dss project-folder create-child KdLmPU6 --name my_sub_folder",],
|
|
172
|
+
},
|
|
173
|
+
"move-project": {
|
|
174
|
+
validate: (a) => {
|
|
175
|
+
requireArgs(a, 3, MOVE_PROJECT_USAGE);
|
|
176
|
+
nonEmptyPositionals(a, ["folderId", "projectKey", "destinationFolderId",], MOVE_PROJECT_USAGE);
|
|
177
|
+
},
|
|
178
|
+
handler: async (c, a, f) => {
|
|
179
|
+
if (executionMode(f).dryRun) {
|
|
180
|
+
return planResult("project-folder", "move-project", {
|
|
181
|
+
asyncKind: "none",
|
|
182
|
+
method: "POST",
|
|
183
|
+
endpoint: `${folderEndpoint(a[0], `/projects/${encodeURIComponent(a[1])}/move`)}?destination=${encodeURIComponent(a[2])}`,
|
|
184
|
+
identifiers: { folderId: a[0], projectKey: a[1], destination: a[2], },
|
|
185
|
+
idempotency: "none",
|
|
186
|
+
exitCodesOnFailure: PLAN_FAILURE_CODES,
|
|
187
|
+
plannedAndDryRun: true,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
await c.projectFolders.moveProject(a[0], a[1], a[2]);
|
|
191
|
+
return { moved: a[1], destination: a[2], resource: "project-folder", };
|
|
192
|
+
},
|
|
193
|
+
usage: MOVE_PROJECT_USAGE,
|
|
194
|
+
description: "Move a project from its project folder to another project folder. WRITE_CONTENTS on destination and project admin required.",
|
|
195
|
+
examples: ["dss project-folder move-project KdLmPU6 MYPROJECT dgKywsx",],
|
|
196
|
+
},
|
|
197
|
+
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { validateGitReferencePath, } from "../../
|
|
1
|
+
import { validateGitReferencePath, } from "../../utils/git-reference.js";
|
|
2
|
+
import { sanitizeErrorSecrets, sanitizeSecrets, } from "../../utils/secret-sanitize.js";
|
|
2
3
|
import { num, parseBooleanOption, requiredStringFlag, } from "../coerce.js";
|
|
3
4
|
import { CommandResultFailure, } from "../output.js";
|
|
4
5
|
import { requireArgs, requireNoArgs, UsageError, } from "../usage.js";
|
|
@@ -28,48 +29,17 @@ const SENSITIVE_GIT_OUTPUT_KEYS = {
|
|
|
28
29
|
password: true,
|
|
29
30
|
token: true,
|
|
30
31
|
};
|
|
32
|
+
const GIT_SANITIZE_OPTIONS = {
|
|
33
|
+
sensitiveKeys: SENSITIVE_GIT_OUTPUT_KEYS,
|
|
34
|
+
redactUrlUserinfo: true,
|
|
35
|
+
};
|
|
31
36
|
/**
|
|
32
37
|
* DSS may already contain legacy remotes with credentials in the URL, and Git
|
|
33
38
|
* error text can echo them. Sanitize every server-derived Project Git value
|
|
34
39
|
* before it reaches stdout or a structured command failure.
|
|
35
40
|
*/
|
|
36
|
-
function redactGitUrlUserinfo(text) {
|
|
37
|
-
return text.replace(/https?:[\\/]{1,2}[^\s"'<>]+/giu, (url) => {
|
|
38
|
-
const schemeEnd = url.indexOf(":") + 1;
|
|
39
|
-
let authorityStart = schemeEnd;
|
|
40
|
-
while (url[authorityStart] === "/" || url[authorityStart] === "\\")
|
|
41
|
-
authorityStart++;
|
|
42
|
-
const slash = url.slice(authorityStart).search(/[\\/]/u);
|
|
43
|
-
const authorityEnd = slash === -1 ? url.length : authorityStart + slash;
|
|
44
|
-
const at = url.lastIndexOf("@", authorityEnd - 1);
|
|
45
|
-
if (at < authorityStart)
|
|
46
|
-
return url;
|
|
47
|
-
return `${url.slice(0, authorityStart)}[redacted]@${url.slice(at + 1)}`;
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
41
|
function sanitizeProjectGitOutput(value, secrets = []) {
|
|
51
|
-
|
|
52
|
-
let sanitized = redactGitUrlUserinfo(value);
|
|
53
|
-
for (const secret of secrets) {
|
|
54
|
-
if (secret !== "")
|
|
55
|
-
sanitized = sanitized.split(secret).join("[redacted]");
|
|
56
|
-
}
|
|
57
|
-
return sanitized;
|
|
58
|
-
}
|
|
59
|
-
if (Array.isArray(value)) {
|
|
60
|
-
return value.map((item) => sanitizeProjectGitOutput(item, secrets));
|
|
61
|
-
}
|
|
62
|
-
if (value !== null && typeof value === "object") {
|
|
63
|
-
const sanitized = {};
|
|
64
|
-
for (const [key, item,] of Object.entries(value)) {
|
|
65
|
-
const normalizedKey = key.replace(/[-_]/g, "").toLowerCase();
|
|
66
|
-
sanitized[key] = SENSITIVE_GIT_OUTPUT_KEYS[normalizedKey] === true
|
|
67
|
-
? "[redacted]"
|
|
68
|
-
: sanitizeProjectGitOutput(item, secrets);
|
|
69
|
-
}
|
|
70
|
-
return sanitized;
|
|
71
|
-
}
|
|
72
|
-
return value;
|
|
42
|
+
return sanitizeSecrets(value, { ...GIT_SANITIZE_OPTIONS, secrets, });
|
|
73
43
|
}
|
|
74
44
|
/**
|
|
75
45
|
* Most Git actions answer HTTP 200 with `{success: false}` and the Git stderr
|
|
@@ -84,16 +54,7 @@ function assertGitActionSucceeded(result, action) {
|
|
|
84
54
|
return sanitized;
|
|
85
55
|
}
|
|
86
56
|
function sanitizeProjectGitError(error) {
|
|
87
|
-
|
|
88
|
-
return error;
|
|
89
|
-
error.message = sanitizeProjectGitOutput(error.message);
|
|
90
|
-
if (error.stack !== undefined)
|
|
91
|
-
error.stack = sanitizeProjectGitOutput(error.stack);
|
|
92
|
-
const errorWithBody = error;
|
|
93
|
-
if (typeof errorWithBody.body === "string") {
|
|
94
|
-
errorWithBody.body = sanitizeProjectGitOutput(errorWithBody.body);
|
|
95
|
-
}
|
|
96
|
-
return error;
|
|
57
|
+
return sanitizeErrorSecrets(error, { ...GIT_SANITIZE_OPTIONS, });
|
|
97
58
|
}
|
|
98
59
|
/**
|
|
99
60
|
* External library credentials are read only from the environment variable
|
|
@@ -533,17 +494,16 @@ export const projectGitCommands = {
|
|
|
533
494
|
handler: async (c, a, f) => {
|
|
534
495
|
requireArgs(a, 1, FUTURE_WAIT_USAGE);
|
|
535
496
|
try {
|
|
536
|
-
|
|
497
|
+
// The official Python client returns the future result verbatim
|
|
498
|
+
// (wait_for_result never inspects result fields): a completed
|
|
499
|
+
// future's payload is caller-interpreted data, and a
|
|
500
|
+
// {success:false} bag from DSS means "no diagnostics to show",
|
|
501
|
+
// not an operation failure. Only the git ACTION endpoints carry
|
|
502
|
+
// the {success:false} = failed convention.
|
|
503
|
+
return sanitizeProjectGitOutput(await c.projectGit.waitForFuture(a[0], {
|
|
537
504
|
pollIntervalMs: num(f["poll-interval"], "--poll-interval"),
|
|
538
505
|
timeoutMs: num(f["timeout"], "--timeout"),
|
|
539
506
|
}));
|
|
540
|
-
if (result !== null
|
|
541
|
-
&& typeof result === "object"
|
|
542
|
-
&& !Array.isArray(result)
|
|
543
|
-
&& result.success === false) {
|
|
544
|
-
return assertGitActionSucceeded(result, "future-wait");
|
|
545
|
-
}
|
|
546
|
-
return result;
|
|
547
507
|
}
|
|
548
508
|
catch (error) {
|
|
549
509
|
throw sanitizeProjectGitError(error);
|
|
@@ -214,12 +214,6 @@ export const projectCommands = {
|
|
|
214
214
|
description: "Read-only local inspection of a project export archive: member names, sizes, manifest validity, archive issues, and the source project key. Never contacts DSS.",
|
|
215
215
|
examples: ["dss project inspect-archive ./my_proj.zip",],
|
|
216
216
|
},
|
|
217
|
-
"permissions-get": {
|
|
218
|
-
handler: (c, _a, f) => c.projects.getPermissions(f["project-key"]),
|
|
219
|
-
usage: "dss project permissions-get [--project-key KEY]",
|
|
220
|
-
description: "Get a project's permissions.",
|
|
221
|
-
examples: ["dss project permissions-get --project-key MY_PROJ",],
|
|
222
|
-
},
|
|
223
217
|
"permissions-set": {
|
|
224
218
|
handler: async (c, _a, f) => {
|
|
225
219
|
const body = requiredJsonInput(f, "--data, --data-file, or --stdin is required (permissions object).");
|
|
@@ -230,6 +224,58 @@ export const projectCommands = {
|
|
|
230
224
|
description: "Replace a project's permissions.",
|
|
231
225
|
examples: ["dss project permissions-set --data-file perms.json --project-key MY_PROJ",],
|
|
232
226
|
},
|
|
227
|
+
"permissions-get": {
|
|
228
|
+
handler: (c, _a, f) => c.projects.getPermissions(f["project-key"]),
|
|
229
|
+
usage: "dss project permissions-get [--project-key KEY]",
|
|
230
|
+
description: "Get a project's permissions.",
|
|
231
|
+
examples: ["dss project permissions-get --project-key MY_PROJ",],
|
|
232
|
+
},
|
|
233
|
+
"metadata-set": {
|
|
234
|
+
handler: async (c, _a, f) => {
|
|
235
|
+
const metadata = requiredJsonInput(f, "--data, --data-file, or --stdin is required (full metadata object).");
|
|
236
|
+
if (executionMode(f).dryRun) {
|
|
237
|
+
return {
|
|
238
|
+
dryRun: true,
|
|
239
|
+
action: "metadata-set",
|
|
240
|
+
resource: "project",
|
|
241
|
+
next: metadata,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
await c.projects.setMetadata(f["project-key"], metadata);
|
|
245
|
+
return { updated: true, };
|
|
246
|
+
},
|
|
247
|
+
usage: "dss project metadata-set (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
|
|
248
|
+
description: "Replace project metadata with a full object obtained from a previous metadata GET. Fields absent from the payload are removed.",
|
|
249
|
+
examples: [
|
|
250
|
+
"dss project metadata-set --data-file metadata.json --project-key MY_PROJ --dry-run",
|
|
251
|
+
],
|
|
252
|
+
},
|
|
253
|
+
"tags-get": {
|
|
254
|
+
handler: (c, _a, f) => c.projects.tags(f["project-key"]),
|
|
255
|
+
usage: "dss project tags-get [--project-key KEY]",
|
|
256
|
+
description: "Get project-level tags as a dictionary of tag name to display settings.",
|
|
257
|
+
examples: ["dss project tags-get", "dss project tags-get --project-key MY_PROJ",],
|
|
258
|
+
},
|
|
259
|
+
"tags-set": {
|
|
260
|
+
handler: async (c, _a, f) => {
|
|
261
|
+
const tags = requiredJsonInput(f, "--data, --data-file, or --stdin is required (full tags object).");
|
|
262
|
+
if (executionMode(f).dryRun) {
|
|
263
|
+
return {
|
|
264
|
+
dryRun: true,
|
|
265
|
+
action: "tags-set",
|
|
266
|
+
resource: "project",
|
|
267
|
+
next: tags,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
await c.projects.setTags(f["project-key"], tags);
|
|
271
|
+
return { updated: true, };
|
|
272
|
+
},
|
|
273
|
+
usage: "dss project tags-set (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
|
|
274
|
+
description: "Replace project tags with a full object obtained from a previous tags GET. Tags absent from the payload are removed.",
|
|
275
|
+
examples: [
|
|
276
|
+
'dss project tags-set --data \'{"tags":{"production":{"color":"#28aadd"}}}\' --project-key MY_PROJ --dry-run',
|
|
277
|
+
],
|
|
278
|
+
},
|
|
233
279
|
"settings-get": {
|
|
234
280
|
handler: (c, _a, f) => c.projects.getSettings(f["project-key"]),
|
|
235
281
|
usage: "dss project settings-get [--project-key KEY]",
|
|
@@ -2,7 +2,7 @@ import { readFileSync, } from "node:fs";
|
|
|
2
2
|
import { writeFile, } from "node:fs/promises";
|
|
3
3
|
import { join, } from "node:path";
|
|
4
4
|
import { deepMerge, } from "../../utils/deep-merge.js";
|
|
5
|
-
import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
|
|
5
|
+
import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredJsonInput, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
|
|
6
6
|
import { executionMode, } from "../flags.js";
|
|
7
7
|
import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
|
|
8
8
|
import { ensureRecipeBackupDir, readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, writeRecipeBackup, } from "../helpers/recipe.js";
|
|
@@ -66,6 +66,41 @@ export const recipeCommands = {
|
|
|
66
66
|
description: "List all recipes in a project.",
|
|
67
67
|
examples: ["dss recipe list",],
|
|
68
68
|
},
|
|
69
|
+
metadata: {
|
|
70
|
+
handler: (c, a, f) => {
|
|
71
|
+
requireArgs(a, 1, "dss recipe metadata <name>");
|
|
72
|
+
return c.recipes.metadata(a[0], {
|
|
73
|
+
projectKey: f["project-key"],
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
usage: "dss recipe metadata <name> [--project-key KEY]",
|
|
77
|
+
description: "Get recipe metadata (label, description, tags, custom fields).",
|
|
78
|
+
examples: ["dss recipe metadata compute_orders",],
|
|
79
|
+
},
|
|
80
|
+
"metadata-set": {
|
|
81
|
+
handler: async (c, a, f) => {
|
|
82
|
+
const usage = "dss recipe metadata-set <name> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]";
|
|
83
|
+
requireArgs(a, 1, usage);
|
|
84
|
+
const metadata = requiredJsonInput(f, "--data, --data-file, or --stdin is required (full metadata object).");
|
|
85
|
+
const pk = f["project-key"];
|
|
86
|
+
if (executionMode(f).dryRun) {
|
|
87
|
+
return {
|
|
88
|
+
dryRun: true,
|
|
89
|
+
action: "metadata-set",
|
|
90
|
+
resource: "recipe",
|
|
91
|
+
name: a[0],
|
|
92
|
+
next: metadata,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
await c.recipes.setMetadata(a[0], metadata, { projectKey: pk, });
|
|
96
|
+
return { updated: a[0], resource: "recipe", };
|
|
97
|
+
},
|
|
98
|
+
usage: "dss recipe metadata-set <name> (--data JSON|--data-file PATH|--stdin) [--dry-run] [--project-key KEY]",
|
|
99
|
+
description: "Replace recipe metadata with a full object obtained from a previous metadata GET. Fields absent from the payload are removed.",
|
|
100
|
+
examples: [
|
|
101
|
+
'dss recipe metadata-set compute_orders --data \'{"label":"Orders"}\' --dry-run',
|
|
102
|
+
],
|
|
103
|
+
},
|
|
69
104
|
get: {
|
|
70
105
|
handler: (c, a, f) => {
|
|
71
106
|
requireArgs(a, 1, "dss recipe get <name>");
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
import type { CommandMeta } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Mutating actions accept --plan (buildMutationPlan in contract.ts owns the
|
|
4
|
+
* --plan path centrally); the handlers below keep --dry-run support with a
|
|
5
|
+
* zero-request planned payload, mirroring set-active/delete.
|
|
6
|
+
*/
|
|
2
7
|
export declare const savedModelCommands: Record<string, CommandMeta>;
|