dataiku-sdk 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -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/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 +161 -32
- 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/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 +14 -54
- 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 +138 -1
- 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 +1089 -103
- 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.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 +83 -5
- package/dist/src/client.js +185 -29
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +2 -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 +99 -1
- package/dist/src/resources/futures.d.ts +4 -1
- package/dist/src/resources/futures.js +38 -6
- 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 +41 -16
- 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 +100 -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 +265 -0
- package/dist/src/resources/plugins.js +458 -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 +6 -0
- package/dist/src/resources/project-git.js +69 -42
- 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 +19 -1
- package/dist/src/resources/projects.js +27 -1
- 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/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 +6 -5
- 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 +7 -5
- package/skills/dataiku-dss/references/app-releases.md +8 -7
- package/skills/dataiku-dss/references/discovery.md +4 -4
package/dist/src/cli/contract.js
CHANGED
|
@@ -18,7 +18,7 @@ import { flowZoneColor, flowZoneMoveItems, flowZoneName, } from "./helpers/flow-
|
|
|
18
18
|
import { recipeBackupPath, recipeRunShouldWait, } from "./helpers/recipe.js";
|
|
19
19
|
import { encodedProjectEndpointForPlan, planResult, } from "./output.js";
|
|
20
20
|
import { resolveLoginCredentials, } from "./runtime.js";
|
|
21
|
-
import { requireArgs, UsageError, } from "./usage.js";
|
|
21
|
+
import { inferRequiresProject, requireArgs, UsageError, } from "./usage.js";
|
|
22
22
|
import { AGENT_CONTRACT_SCHEMA_ID, AGENT_CONTRACT_VERSION, cliVersionResult, JSON_SCHEMA_DRAFT, } from "./version.js";
|
|
23
23
|
function codeEnvWait(flags) {
|
|
24
24
|
return flags["no-wait"] !== true;
|
|
@@ -39,19 +39,27 @@ function codeEnvParams(flags) {
|
|
|
39
39
|
function splitPackageSpec(raw) {
|
|
40
40
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
41
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve requested package specs from --file/--packages/--package. An
|
|
44
|
+
* explicit source that resolves to zero specs is a legitimate clear
|
|
45
|
+
* (set-packages replaces specPackageList wholesale); only a call with no
|
|
46
|
+
* package source flag at all is a usage error.
|
|
47
|
+
*/
|
|
42
48
|
function codeEnvPackageList(flags) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
49
|
+
const file = flags["file"];
|
|
50
|
+
const packages = typeof file === "string"
|
|
51
|
+
? splitPackageSpec(fs.readFileSync(file, "utf-8"))
|
|
52
|
+
: [];
|
|
47
53
|
if (typeof flags["packages"] === "string") {
|
|
48
54
|
packages.push(...splitPackageSpec(flags["packages"]));
|
|
49
55
|
}
|
|
50
56
|
if (typeof flags["package"] === "string") {
|
|
51
57
|
packages.push(...splitPackageSpec(flags["package"]));
|
|
52
58
|
}
|
|
53
|
-
if (
|
|
54
|
-
|
|
59
|
+
if (typeof file !== "string"
|
|
60
|
+
&& typeof flags["packages"] !== "string"
|
|
61
|
+
&& typeof flags["package"] !== "string") {
|
|
62
|
+
throw new UsageError("--packages, --package, or --file is required. Use newline-separated package specs for version constraints; an explicitly empty value (or empty file) clears the requested package list.");
|
|
55
63
|
}
|
|
56
64
|
return packages;
|
|
57
65
|
}
|
|
@@ -127,26 +135,31 @@ const READ_ACTIONS = new Set([
|
|
|
127
135
|
"usages",
|
|
128
136
|
"verify-instance",
|
|
129
137
|
]);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
138
|
+
/**
|
|
139
|
+
* Security admin actions that hand back a DSS future instead of settling
|
|
140
|
+
* inline: resyncs mint sync jobs, external-user/group fetches mint a
|
|
141
|
+
* supplier-enumeration job, and provision runs a supplier import. Without
|
|
142
|
+
* this table they fall through to the read default and `--plan` rejects
|
|
143
|
+
* them as non-mutating.
|
|
144
|
+
*/
|
|
145
|
+
const ADMIN_SECURITY_FUTURE_ACTIONS = {
|
|
146
|
+
"external-groups": true,
|
|
147
|
+
"external-users": true,
|
|
148
|
+
provision: true,
|
|
149
|
+
resync: true,
|
|
150
|
+
"resync-multi": true,
|
|
151
|
+
};
|
|
152
|
+
/** Connection tables-import actions return a DSS future reference. */
|
|
153
|
+
const CONNECTION_FUTURE_ACTIONS = {
|
|
154
|
+
"execute-import": true,
|
|
155
|
+
"prepare-import": true,
|
|
156
|
+
};
|
|
157
|
+
/** Plugin actions that only observe state despite POST shape (documented). */
|
|
158
|
+
const PLUGIN_GIT_OBSERVER_ACTIONS = {
|
|
159
|
+
"git-branches": true,
|
|
160
|
+
list: true,
|
|
161
|
+
usages: true,
|
|
162
|
+
};
|
|
150
163
|
/**
|
|
151
164
|
* Project Git actions that only observe repository state. Everything else in the
|
|
152
165
|
* `project-git` resource is a mutation: the verb-shaped names (`fetch`, `pull`,
|
|
@@ -185,6 +198,17 @@ const PROJECT_GIT_DESTRUCTIVE_ACTIONS = {
|
|
|
185
198
|
"revert-commit": true,
|
|
186
199
|
"revert-to-revision": true,
|
|
187
200
|
};
|
|
201
|
+
/** Plugin actions that discard work or publish it beyond the instance. */
|
|
202
|
+
const PLUGIN_DESTRUCTIVE_ACTIONS = {
|
|
203
|
+
"contents-delete": true,
|
|
204
|
+
delete: true,
|
|
205
|
+
"delete-git-remote": true,
|
|
206
|
+
fetch: true,
|
|
207
|
+
pull: true,
|
|
208
|
+
push: true,
|
|
209
|
+
"reset-local": true,
|
|
210
|
+
"reset-remote": true,
|
|
211
|
+
};
|
|
188
212
|
/** Project Git actions that hand back a DSS future (`{jobId}`) instead of a result. */
|
|
189
213
|
const PROJECT_GIT_FUTURE_ACTIONS = {
|
|
190
214
|
"add-library": true,
|
|
@@ -434,6 +458,18 @@ const NOTEBOOK_SAVE_OUTPUT_SCHEMA = {
|
|
|
434
458
|
hash: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
435
459
|
},
|
|
436
460
|
};
|
|
461
|
+
/**
|
|
462
|
+
* `notebook.save-sql` adds `requested`: on create DSS allocates the persisted
|
|
463
|
+
* id (`saved`), while the requested handle only becomes the display name.
|
|
464
|
+
*/
|
|
465
|
+
const NOTEBOOK_SAVE_SQL_OUTPUT_SCHEMA = {
|
|
466
|
+
...NOTEBOOK_SAVE_OUTPUT_SCHEMA,
|
|
467
|
+
required: ["saved", "requested", "resource", "created", "hash",],
|
|
468
|
+
properties: {
|
|
469
|
+
...NOTEBOOK_SAVE_OUTPUT_SCHEMA["properties"],
|
|
470
|
+
requested: { type: "string", },
|
|
471
|
+
},
|
|
472
|
+
};
|
|
437
473
|
const COMMAND_OUTPUT_SCHEMAS = {
|
|
438
474
|
"code-env.list": CodeEnvSummaryArraySchema,
|
|
439
475
|
"code-env.get": CodeEnvDetailsSchema,
|
|
@@ -514,7 +550,7 @@ const COMMAND_OUTPUT_SCHEMAS = {
|
|
|
514
550
|
"notebook.get-sql": SqlNotebookContentSchema,
|
|
515
551
|
"notebook.history-sql": SqlNotebookHistorySchema,
|
|
516
552
|
"notebook.save-jupyter": NOTEBOOK_SAVE_OUTPUT_SCHEMA,
|
|
517
|
-
"notebook.save-sql":
|
|
553
|
+
"notebook.save-sql": NOTEBOOK_SAVE_SQL_OUTPUT_SCHEMA,
|
|
518
554
|
"project-library.list": { type: "array", items: PROJECT_LIBRARY_ITEM_OUTPUT_SCHEMA, },
|
|
519
555
|
"project-library.get-bytes": {
|
|
520
556
|
type: "object",
|
|
@@ -916,10 +952,33 @@ function inferSideEffect(resource, action) {
|
|
|
916
952
|
return "read";
|
|
917
953
|
if (resource === "data-quality" && action === "compute")
|
|
918
954
|
return "write";
|
|
955
|
+
if (resource === "user" && ADMIN_SECURITY_FUTURE_ACTIONS[action] === true)
|
|
956
|
+
return "write";
|
|
957
|
+
if (resource === "connection" && CONNECTION_FUTURE_ACTIONS[action] === true)
|
|
958
|
+
return "write";
|
|
959
|
+
// Saved-model evaluation triggers scoring work on DSS side (cost-bearing).
|
|
960
|
+
if (resource === "saved-model" && action === "evaluate-version")
|
|
961
|
+
return "write";
|
|
962
|
+
// LLM Mesh completions/embeddings invoke the LLM provider (cost-bearing,
|
|
963
|
+
// arbitrary-prompt execution); the verb shapes match no mutating pattern
|
|
964
|
+
// and would fall through to read, wrongly rejecting --plan. Knowledge-bank
|
|
965
|
+
// search stays a READ per Main's preservation directive.
|
|
966
|
+
if (resource === "llm" && (action === "completions" || action === "embeddings")) {
|
|
967
|
+
return "write";
|
|
968
|
+
}
|
|
919
969
|
// Project Git is classified from the explicit read table, not by verb shape:
|
|
920
970
|
// `switch`, `fetch`, `pull`, `push`, `commit`, the `reset-*`/`revert-*`
|
|
921
971
|
// families, `drop-and-rebuild`, and `future-abort` all mutate repository or
|
|
922
972
|
// future state yet match none of the mutating-verb patterns.
|
|
973
|
+
// Plugin dev-Git actions mutate repository/plugin state like project-git:
|
|
974
|
+
// `fetch`/`pull`/`push`/`reset-*` never match the generic mutating-verb
|
|
975
|
+
// regex, so they are classified explicitly as write (destructive level
|
|
976
|
+
// from the explicit table below). `git-branches` is a state observer
|
|
977
|
+
// (observed GET on DSS 15; the upstream docs' POST is wrong) and stays
|
|
978
|
+
// read.
|
|
979
|
+
if (resource === "plugin") {
|
|
980
|
+
return PLUGIN_GIT_OBSERVER_ACTIONS[action] === true ? "read" : "write";
|
|
981
|
+
}
|
|
923
982
|
if (resource === "project-git") {
|
|
924
983
|
return PROJECT_GIT_READ_ACTIONS[action] === true ? "read" : "write";
|
|
925
984
|
}
|
|
@@ -927,8 +986,9 @@ function inferSideEffect(resource, action) {
|
|
|
927
986
|
return "read";
|
|
928
987
|
if (/^(create|clone|restore|update|delete|set|save|upload|run|build|abort|move|refresh|clear|unload|install|login|logout|add|remove|publish|activate|deploy|import|export|preload|upgrade|start|stop|restart|duplicate|put|rename|reply|compute|organize)/
|
|
929
988
|
.test(action)
|
|
930
|
-
// Compound actions whose mutating verb is a suffix (e.g. permissions-set,
|
|
931
|
-
|
|
989
|
+
// Compound actions whose mutating verb is a suffix (e.g. permissions-set,
|
|
990
|
+
// dataset-compute, saved-model external-metadata-put).
|
|
991
|
+
|| /-(set|compute|restore|put)$/.test(action)) {
|
|
932
992
|
return "write";
|
|
933
993
|
}
|
|
934
994
|
return "read";
|
|
@@ -940,19 +1000,6 @@ function inferRequiresAuth(resource) {
|
|
|
940
1000
|
&& resource !== "install-skill"
|
|
941
1001
|
&& resource !== "version";
|
|
942
1002
|
}
|
|
943
|
-
export function inferRequiresProject(resource, action, usage) {
|
|
944
|
-
if (resource === "agent" || resource === "auth" || resource === "doctor" || resource === "commands"
|
|
945
|
-
|| resource === "install-skill" || resource === "version") {
|
|
946
|
-
return false;
|
|
947
|
-
}
|
|
948
|
-
if (resource === "sql" && action === "query")
|
|
949
|
-
return false;
|
|
950
|
-
if (PROJECT_SCOPED_RESOURCES.has(resource))
|
|
951
|
-
return true;
|
|
952
|
-
if (resource === "project-git")
|
|
953
|
-
return !action.startsWith("future-");
|
|
954
|
-
return usage.includes("--project-key");
|
|
955
|
-
}
|
|
956
1003
|
const ARRAY_OUTPUT_ACTIONS = new Set([
|
|
957
1004
|
"history",
|
|
958
1005
|
"find",
|
|
@@ -1166,7 +1213,7 @@ function cleanupCommandFromDeleteUsage(resource, action) {
|
|
|
1166
1213
|
return "dss notebook delete-jupyter <name> --if-exists";
|
|
1167
1214
|
}
|
|
1168
1215
|
if (`${resource}.${action}` === "notebook.save-sql") {
|
|
1169
|
-
return "dss notebook delete-sql <id> --if-exists";
|
|
1216
|
+
return "dss notebook delete-sql <the returned `saved` id> --if-exists";
|
|
1170
1217
|
}
|
|
1171
1218
|
if (`${resource}.${action}` === "project.import") {
|
|
1172
1219
|
return "dss project delete <used-project-key> --if-exists --expect-project-incarnation <hash>";
|
|
@@ -1203,12 +1250,29 @@ export function supportsCleanupLedger(resource, action) {
|
|
|
1203
1250
|
const EXPLICIT_DESTRUCTIVE_KEYS = {
|
|
1204
1251
|
"dataset.upload-file": true,
|
|
1205
1252
|
"sql.query": true,
|
|
1253
|
+
// Plugin dev-Git and content deletions discard work with no undo route.
|
|
1254
|
+
"plugin.delete": true,
|
|
1255
|
+
"plugin.delete-git-remote": true,
|
|
1256
|
+
"plugin.push": true,
|
|
1257
|
+
"plugin.pull": true,
|
|
1258
|
+
"plugin.fetch": true,
|
|
1259
|
+
"plugin.reset-local": true,
|
|
1260
|
+
"plugin.reset-remote": true,
|
|
1261
|
+
"plugin.contents-delete": true,
|
|
1262
|
+
// Macro runs execute arbitrary plugin code.
|
|
1263
|
+
"macro.run": true,
|
|
1264
|
+
"macro.run-and-wait": true,
|
|
1265
|
+
// Saved-model version deletion removes model versions permanently.
|
|
1266
|
+
"saved-model.delete-versions": true,
|
|
1206
1267
|
};
|
|
1207
1268
|
function inferDestructiveLevel(resource, sideEffect, action) {
|
|
1208
1269
|
if (sideEffect !== "write")
|
|
1209
1270
|
return "none";
|
|
1210
1271
|
if (EXPLICIT_DESTRUCTIVE_KEYS[`${resource}.${action}`] === true)
|
|
1211
1272
|
return "destructive";
|
|
1273
|
+
if (resource === "plugin") {
|
|
1274
|
+
return PLUGIN_DESTRUCTIVE_ACTIONS[action] === true ? "destructive" : "reversible";
|
|
1275
|
+
}
|
|
1212
1276
|
if (resource === "project-git") {
|
|
1213
1277
|
return PROJECT_GIT_DESTRUCTIVE_ACTIONS[action] === true ? "destructive" : "reversible";
|
|
1214
1278
|
}
|
|
@@ -1229,6 +1293,10 @@ function inferAsyncKind(resource, action) {
|
|
|
1229
1293
|
if (resource === "scenario" && ["run", "run-and-wait", "status",].includes(action)) {
|
|
1230
1294
|
return "future";
|
|
1231
1295
|
}
|
|
1296
|
+
if (resource === "user" && ADMIN_SECURITY_FUTURE_ACTIONS[action] === true)
|
|
1297
|
+
return "future";
|
|
1298
|
+
if (resource === "connection" && CONNECTION_FUTURE_ACTIONS[action] === true)
|
|
1299
|
+
return "future";
|
|
1232
1300
|
if (resource === "data-quality" && action === "compute")
|
|
1233
1301
|
return "future";
|
|
1234
1302
|
// Project Git mutation results carry a future job id only for the library
|
|
@@ -1296,7 +1364,7 @@ export function inferCleanupHint(resource, action) {
|
|
|
1296
1364
|
return "If created:true, delete with `dss notebook delete-jupyter <name> --if-exists`.";
|
|
1297
1365
|
}
|
|
1298
1366
|
if (key === "notebook.save-sql") {
|
|
1299
|
-
return "If created:true, delete with `dss notebook delete-sql <
|
|
1367
|
+
return "If created:true, the persisted id is in `saved`; delete with `dss notebook delete-sql <saved> --if-exists`.";
|
|
1300
1368
|
}
|
|
1301
1369
|
if (!(action.startsWith("create") || action === "clone"))
|
|
1302
1370
|
return undefined;
|
|
@@ -1427,98 +1495,105 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
1427
1495
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1428
1496
|
};
|
|
1429
1497
|
}
|
|
1430
|
-
|
|
1498
|
+
function commandDefinitions(resourceFilter) {
|
|
1431
1499
|
const registry = {};
|
|
1432
|
-
for (const
|
|
1500
|
+
for (const resource of Object.keys(commands)) {
|
|
1433
1501
|
if (resourceFilter !== undefined && resource !== resourceFilter)
|
|
1434
1502
|
continue;
|
|
1435
|
-
registry[resource] =
|
|
1436
|
-
for (const [action, meta,] of Object.entries(actions)) {
|
|
1437
|
-
registry[resource][action] = buildRegistryEntry(resource, action, meta);
|
|
1438
|
-
}
|
|
1503
|
+
registry[resource] = commands[resource];
|
|
1439
1504
|
}
|
|
1440
1505
|
if (resourceFilter === undefined || resourceFilter === "commands") {
|
|
1441
1506
|
registry.commands = {
|
|
1442
|
-
run:
|
|
1507
|
+
run: {
|
|
1443
1508
|
handler: async () => undefined,
|
|
1444
1509
|
usage: COMMANDS_USAGE,
|
|
1445
1510
|
description: COMMANDS_DESCRIPTION,
|
|
1446
1511
|
examples: COMMANDS_EXAMPLES,
|
|
1447
|
-
}
|
|
1512
|
+
},
|
|
1448
1513
|
};
|
|
1449
1514
|
}
|
|
1450
1515
|
if (resourceFilter === undefined || resourceFilter === "agent") {
|
|
1451
1516
|
registry.agent = {
|
|
1452
|
-
contract:
|
|
1517
|
+
contract: {
|
|
1453
1518
|
handler: async () => undefined,
|
|
1454
1519
|
usage: AGENT_CONTRACT_USAGE,
|
|
1455
1520
|
description: AGENT_CONTRACT_DESCRIPTION,
|
|
1456
1521
|
examples: AGENT_CONTRACT_EXAMPLES,
|
|
1457
|
-
}
|
|
1522
|
+
},
|
|
1458
1523
|
};
|
|
1459
1524
|
}
|
|
1460
1525
|
if (resourceFilter === undefined || resourceFilter === "version") {
|
|
1461
1526
|
registry.version = {
|
|
1462
|
-
run:
|
|
1527
|
+
run: {
|
|
1463
1528
|
handler: async () => undefined,
|
|
1464
1529
|
usage: VERSION_USAGE,
|
|
1465
1530
|
description: VERSION_DESCRIPTION,
|
|
1466
1531
|
examples: VERSION_EXAMPLES,
|
|
1467
|
-
}
|
|
1532
|
+
},
|
|
1468
1533
|
};
|
|
1469
1534
|
}
|
|
1470
1535
|
if (resourceFilter === undefined || resourceFilter === "install-skill") {
|
|
1471
1536
|
registry["install-skill"] = {
|
|
1472
|
-
run:
|
|
1537
|
+
run: {
|
|
1473
1538
|
handler: async () => undefined,
|
|
1474
1539
|
usage: INSTALL_SKILL_USAGE,
|
|
1475
1540
|
description: INSTALL_SKILL_DESCRIPTION,
|
|
1476
1541
|
examples: INSTALL_SKILL_EXAMPLES,
|
|
1477
|
-
}
|
|
1542
|
+
},
|
|
1478
1543
|
};
|
|
1479
1544
|
}
|
|
1480
1545
|
if (resourceFilter === undefined || resourceFilter === "cleanup") {
|
|
1481
1546
|
registry.cleanup = {
|
|
1482
|
-
run:
|
|
1547
|
+
run: {
|
|
1483
1548
|
handler: async () => undefined,
|
|
1484
1549
|
usage: CLEANUP_USAGE,
|
|
1485
1550
|
description: CLEANUP_DESCRIPTION,
|
|
1486
1551
|
examples: CLEANUP_EXAMPLES,
|
|
1487
|
-
}
|
|
1552
|
+
},
|
|
1488
1553
|
};
|
|
1489
1554
|
}
|
|
1490
1555
|
if (resourceFilter === undefined || resourceFilter === "fixtures") {
|
|
1491
1556
|
registry.fixtures = {
|
|
1492
|
-
run:
|
|
1557
|
+
run: {
|
|
1493
1558
|
handler: async () => undefined,
|
|
1494
1559
|
usage: FIXTURES_USAGE,
|
|
1495
1560
|
description: FIXTURES_DESCRIPTION,
|
|
1496
1561
|
examples: FIXTURES_EXAMPLES,
|
|
1497
|
-
}
|
|
1562
|
+
},
|
|
1498
1563
|
};
|
|
1499
1564
|
}
|
|
1500
1565
|
if (resourceFilter === undefined || resourceFilter === "batch") {
|
|
1501
1566
|
registry.batch = {
|
|
1502
|
-
run:
|
|
1567
|
+
run: {
|
|
1503
1568
|
handler: async () => undefined,
|
|
1504
1569
|
usage: BATCH_USAGE,
|
|
1505
1570
|
description: BATCH_DESCRIPTION,
|
|
1506
1571
|
examples: BATCH_EXAMPLES,
|
|
1507
1572
|
examplePayload: BATCH_EXAMPLE_PAYLOAD,
|
|
1508
1573
|
payloadSchema: { stdin: true, dataFlag: true, dataFileFlag: true, jsonShape: "array", },
|
|
1509
|
-
}
|
|
1574
|
+
},
|
|
1510
1575
|
};
|
|
1511
1576
|
}
|
|
1512
1577
|
if (resourceFilter === undefined || resourceFilter === "auth") {
|
|
1513
1578
|
registry.auth = {};
|
|
1514
1579
|
for (const [action, meta,] of Object.entries(AUTH_ACTIONS)) {
|
|
1515
|
-
registry.auth[action] =
|
|
1580
|
+
registry.auth[action] = {
|
|
1516
1581
|
handler: async () => undefined,
|
|
1517
1582
|
usage: meta.usage,
|
|
1518
1583
|
description: meta.description,
|
|
1519
1584
|
examples: meta.examples,
|
|
1520
1585
|
requiredFlags: meta.requiredFlags,
|
|
1521
|
-
}
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return registry;
|
|
1590
|
+
}
|
|
1591
|
+
export function buildCommandRegistry(resourceFilter) {
|
|
1592
|
+
const registry = {};
|
|
1593
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions(resourceFilter))) {
|
|
1594
|
+
registry[resource] = {};
|
|
1595
|
+
for (const [action, meta,] of Object.entries(actions)) {
|
|
1596
|
+
registry[resource][action] = buildRegistryEntry(resource, action, meta);
|
|
1522
1597
|
}
|
|
1523
1598
|
}
|
|
1524
1599
|
return registry;
|
|
@@ -1676,38 +1751,43 @@ export function agentContractJsonSchema() {
|
|
|
1676
1751
|
},
|
|
1677
1752
|
};
|
|
1678
1753
|
}
|
|
1679
|
-
export function commandActionSummary(
|
|
1754
|
+
export function commandActionSummary() {
|
|
1680
1755
|
const summary = {};
|
|
1681
|
-
for (const [resource, actions,] of Object.entries(
|
|
1756
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions())) {
|
|
1682
1757
|
summary[resource] = Object.keys(actions).sort();
|
|
1683
1758
|
}
|
|
1684
1759
|
return summary;
|
|
1685
1760
|
}
|
|
1686
1761
|
export function buildAgentContract() {
|
|
1687
|
-
|
|
1762
|
+
let commandSection;
|
|
1763
|
+
let schemaSection;
|
|
1688
1764
|
return {
|
|
1689
1765
|
protocol: "dataiku-sdk-agent",
|
|
1690
1766
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1691
1767
|
cli: cliVersionResult(),
|
|
1692
|
-
commands
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1768
|
+
get commands() {
|
|
1769
|
+
return commandSection ??= {
|
|
1770
|
+
discoveryCommand: "dss commands run",
|
|
1771
|
+
fullRegistryExportCommand: "dss commands run --output PATH",
|
|
1772
|
+
scopedDiscoveryCommand: "dss commands run --fields RESOURCE[.ACTION[.FIELD...]]",
|
|
1773
|
+
actionIndexCommand: "dss agent contract --fields commands.actions",
|
|
1774
|
+
scopedDiscoveryExamples: [
|
|
1775
|
+
"dss commands run --fields dataset",
|
|
1776
|
+
"dss commands run --fields dataset.create",
|
|
1777
|
+
],
|
|
1778
|
+
scopedDiscoveryHint: "Default: resource/action summary. --fields RESOURCE: all resource entries; RESOURCE.ACTION: one entry keyed by that path; append .FIELD for nested metadata. Comma-separate paths. --output PATH exports the full registry.",
|
|
1779
|
+
actions: commandActionSummary(),
|
|
1780
|
+
};
|
|
1703
1781
|
},
|
|
1704
|
-
schemas
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1782
|
+
get schemas() {
|
|
1783
|
+
return schemaSection ??= {
|
|
1784
|
+
agentContract: agentContractJsonSchema(),
|
|
1785
|
+
commandRegistry: commandRegistryJsonSchema(),
|
|
1786
|
+
commandRegistryEntry: commandRegistryEntryJsonSchema(),
|
|
1787
|
+
errorEnvelope: errorEnvelopeJsonSchema(),
|
|
1788
|
+
warningEvent: warningEventJsonSchema(),
|
|
1789
|
+
traceEvent: traceEventJsonSchema(),
|
|
1790
|
+
};
|
|
1711
1791
|
},
|
|
1712
1792
|
stdio: {
|
|
1713
1793
|
stdout: {
|
|
@@ -1795,6 +1875,19 @@ function requiredPlanFlag(flags, name, usage) {
|
|
|
1795
1875
|
return value.trim();
|
|
1796
1876
|
throw new UsageError(`--${name} is required. Usage: ${usage}`);
|
|
1797
1877
|
}
|
|
1878
|
+
/**
|
|
1879
|
+
* Optional caller-chosen identifier. Absent selects the command's generated
|
|
1880
|
+
* mode; a present-but-empty value is an input error, never a silent omission.
|
|
1881
|
+
*/
|
|
1882
|
+
function optionalPlanFlag(flags, name, usage) {
|
|
1883
|
+
const value = flags[name];
|
|
1884
|
+
if (value === undefined)
|
|
1885
|
+
return undefined;
|
|
1886
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1887
|
+
throw new UsageError(`--${name} must not be empty when supplied; omit --${name} entirely to generate the successor key during apply. Usage: ${usage}`, "validation_failed");
|
|
1888
|
+
}
|
|
1889
|
+
return value.trim();
|
|
1890
|
+
}
|
|
1798
1891
|
function optionalJsonFlag(flags, name) {
|
|
1799
1892
|
const value = flags[name];
|
|
1800
1893
|
return typeof value === "string" ? parseJsonObject(value, `--${name}`) : undefined;
|
|
@@ -1819,6 +1912,9 @@ function querySuffix(params) {
|
|
|
1819
1912
|
const raw = search.toString();
|
|
1820
1913
|
return raw ? `?${raw}` : "";
|
|
1821
1914
|
}
|
|
1915
|
+
function projectFolderEndpoint(folderId) {
|
|
1916
|
+
return `/public/api/project-folders/${encodeURIComponent(folderId)}`;
|
|
1917
|
+
}
|
|
1822
1918
|
/**
|
|
1823
1919
|
* Git and future API mount. The official Python client mounts these routes on
|
|
1824
1920
|
* `/dip/publicapi`, which is also what the project-git resource uses; do not
|
|
@@ -1863,6 +1959,92 @@ function validatedPlanRepositoryUrl(url, flag, usage) {
|
|
|
1863
1959
|
}
|
|
1864
1960
|
return candidate;
|
|
1865
1961
|
}
|
|
1962
|
+
function pluginRootEndpoint(pluginId, suffix) {
|
|
1963
|
+
return `/public/api/plugins/${encodeURIComponent(pluginId)}${suffix}`;
|
|
1964
|
+
}
|
|
1965
|
+
function pluginActionEndpoint(pluginId, action) {
|
|
1966
|
+
return pluginRootEndpoint(pluginId, `/actions/${action}`);
|
|
1967
|
+
}
|
|
1968
|
+
function pluginContentsEndpoint(pluginId, contentPath) {
|
|
1969
|
+
return `${pluginRootEndpoint(pluginId, "/contents/")}${encodePluginSegmentsForPlan(contentPath)}`;
|
|
1970
|
+
}
|
|
1971
|
+
function pluginFoldersEndpoint(pluginId, contentPath) {
|
|
1972
|
+
return `${pluginRootEndpoint(pluginId, "/folders/")}${encodePluginSegmentsForPlan(contentPath)}`;
|
|
1973
|
+
}
|
|
1974
|
+
/** Encodes a plugin content path per segment for plan-time endpoints. */
|
|
1975
|
+
function encodePluginSegmentsForPlan(contentPath) {
|
|
1976
|
+
return contentPath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
1977
|
+
}
|
|
1978
|
+
/**
|
|
1979
|
+
* Plan payload for plugin git install/update bodies. The repository URL is
|
|
1980
|
+
* validated (embedded userinfo rejected) and secrets are never echoed: the
|
|
1981
|
+
* plan carries the URL only, never embedded credentials.
|
|
1982
|
+
*/
|
|
1983
|
+
function pluginGitPlanPayload(flags) {
|
|
1984
|
+
const repository = requiredPlanFlag(flags, "repository", "dss plugin install-from-git --repository URL");
|
|
1985
|
+
const payload = {
|
|
1986
|
+
gitRepositoryUrl: validatedPlanRepositoryUrl(repository, "repository", "--repository URL"),
|
|
1987
|
+
};
|
|
1988
|
+
if (typeof flags["checkout"] === "string")
|
|
1989
|
+
payload.gitCheckout = flags["checkout"];
|
|
1990
|
+
if (typeof flags["path-in-repository"] === "string") {
|
|
1991
|
+
payload.gitSubpath = flags["path-in-repository"];
|
|
1992
|
+
}
|
|
1993
|
+
return payload;
|
|
1994
|
+
}
|
|
1995
|
+
function optionalPlanProjectScope(flags) {
|
|
1996
|
+
const projectKey = flags["project-key"];
|
|
1997
|
+
if (typeof projectKey !== "string" || projectKey.trim() === "")
|
|
1998
|
+
return "";
|
|
1999
|
+
return `?projectKey=${encodeURIComponent(projectKey.trim())}`;
|
|
2000
|
+
}
|
|
2001
|
+
function settingsConfigKeys(flags) {
|
|
2002
|
+
const content = flags["content"];
|
|
2003
|
+
if (typeof content !== "string")
|
|
2004
|
+
return [];
|
|
2005
|
+
try {
|
|
2006
|
+
const parsed = JSON.parse(content);
|
|
2007
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
2008
|
+
return [];
|
|
2009
|
+
return Object.keys(parsed);
|
|
2010
|
+
}
|
|
2011
|
+
catch {
|
|
2012
|
+
return [];
|
|
2013
|
+
}
|
|
2014
|
+
}
|
|
2015
|
+
function contentSourceKind(flags) {
|
|
2016
|
+
if (flags["stdin"] === true)
|
|
2017
|
+
return "stdin";
|
|
2018
|
+
if (typeof flags["file"] === "string")
|
|
2019
|
+
return "file";
|
|
2020
|
+
if (typeof flags["content"] === "string")
|
|
2021
|
+
return "content";
|
|
2022
|
+
return "unknown";
|
|
2023
|
+
}
|
|
2024
|
+
/**
|
|
2025
|
+
* Plan-safe view of a user create/update body: password values are replaced by
|
|
2026
|
+
* a fixed marker so secrets never appear in --plan output.
|
|
2027
|
+
*/
|
|
2028
|
+
function redactedUserPayload(payload) {
|
|
2029
|
+
if (payload["password"] === undefined)
|
|
2030
|
+
return payload;
|
|
2031
|
+
return { ...payload, password: "<omitted>", };
|
|
2032
|
+
}
|
|
2033
|
+
/**
|
|
2034
|
+
* Plan-safe view of a connection create/update body: the connection-type
|
|
2035
|
+
* specific `params` object may carry credentials (passwords, keys, tokens), so
|
|
2036
|
+
* the plan carries only the marker, never the raw params.
|
|
2037
|
+
*/
|
|
2038
|
+
function redactedConnectionPayload(payload) {
|
|
2039
|
+
if (payload["params"] === undefined)
|
|
2040
|
+
return payload;
|
|
2041
|
+
return { ...payload, params: "<omitted; may include credentials>", };
|
|
2042
|
+
}
|
|
2043
|
+
/** Required CSV flag for plans (comma-separated logins list). */
|
|
2044
|
+
function requiredPlanCsv(flags, name, usage) {
|
|
2045
|
+
const raw = requiredPlanFlag(flags, name, usage);
|
|
2046
|
+
return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
2047
|
+
}
|
|
1866
2048
|
function requiredPlanRepositoryUrl(flags, flag, usage) {
|
|
1867
2049
|
const url = requiredPlanFlag(flags, flag, usage);
|
|
1868
2050
|
return validatedPlanRepositoryUrl(url, flag, usage);
|
|
@@ -2092,8 +2274,8 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2092
2274
|
};
|
|
2093
2275
|
case "future.abort":
|
|
2094
2276
|
return {
|
|
2095
|
-
method: "
|
|
2096
|
-
endpoint: `/public/api/futures/${encodeURIComponent(id)}
|
|
2277
|
+
method: "DELETE",
|
|
2278
|
+
endpoint: `/public/api/futures/${encodeURIComponent(id)}`,
|
|
2097
2279
|
identifiers: { id, },
|
|
2098
2280
|
};
|
|
2099
2281
|
case "flow-zone.create": {
|
|
@@ -2221,8 +2403,38 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2221
2403
|
method: "PUT",
|
|
2222
2404
|
endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}`),
|
|
2223
2405
|
identifiers: { name: id, },
|
|
2406
|
+
};
|
|
2407
|
+
case "dataset.metadata-set":
|
|
2408
|
+
return {
|
|
2409
|
+
method: "PUT",
|
|
2410
|
+
endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}/metadata`),
|
|
2411
|
+
identifiers: { name: id, },
|
|
2224
2412
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2225
2413
|
};
|
|
2414
|
+
case "dataset.create-managed": {
|
|
2415
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2416
|
+
const connection = requiredPlanFlag(flags, "connection", entry.usage);
|
|
2417
|
+
const creationSettings = { connectionId: connection, };
|
|
2418
|
+
const specificSettings = {};
|
|
2419
|
+
if (typeof flags["type-option-id"] === "string") {
|
|
2420
|
+
creationSettings.typeOptionId = flags["type-option-id"];
|
|
2421
|
+
}
|
|
2422
|
+
if (typeof flags["format-option-id"] === "string") {
|
|
2423
|
+
specificSettings.formatOptionId = flags["format-option-id"];
|
|
2424
|
+
}
|
|
2425
|
+
if (typeof flags["copy-partitioning-from"] === "string") {
|
|
2426
|
+
specificSettings.partitioningOptionId = `copy:${flags["partitioning-folder"] === true ? "folder" : "dataset"}:${flags["copy-partitioning-from"]}`;
|
|
2427
|
+
}
|
|
2428
|
+
if (Object.keys(specificSettings).length > 0) {
|
|
2429
|
+
creationSettings.specificSettings = specificSettings;
|
|
2430
|
+
}
|
|
2431
|
+
return {
|
|
2432
|
+
method: "POST",
|
|
2433
|
+
endpoint: projectEndpoint("/datasets/managed"),
|
|
2434
|
+
identifiers: { name, },
|
|
2435
|
+
payload: { name, creationSettings, },
|
|
2436
|
+
};
|
|
2437
|
+
}
|
|
2226
2438
|
case "recipe.add-input":
|
|
2227
2439
|
case "recipe.remove-input": {
|
|
2228
2440
|
const role = flags["role"] ?? "main";
|
|
@@ -2411,6 +2623,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2411
2623
|
payload: jobBuildPayload(id, projectKey, flags),
|
|
2412
2624
|
wait: action === "build-and-wait" || flags["wait"] === true,
|
|
2413
2625
|
};
|
|
2626
|
+
case "scenario.abort":
|
|
2627
|
+
return {
|
|
2628
|
+
method: "POST",
|
|
2629
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/abort`),
|
|
2630
|
+
identifiers: { id, },
|
|
2631
|
+
};
|
|
2632
|
+
case "scenario.payload-set":
|
|
2633
|
+
return {
|
|
2634
|
+
method: "PUT",
|
|
2635
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/payload`),
|
|
2636
|
+
identifiers: { id, },
|
|
2637
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2638
|
+
};
|
|
2639
|
+
case "scenario.active-set": {
|
|
2640
|
+
const activeRaw = args[1];
|
|
2641
|
+
if (activeRaw !== "true" && activeRaw !== "false") {
|
|
2642
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2643
|
+
}
|
|
2644
|
+
return {
|
|
2645
|
+
method: "PUT",
|
|
2646
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/light`),
|
|
2647
|
+
identifiers: { id, },
|
|
2648
|
+
payload: { id, active: activeRaw === "true", },
|
|
2649
|
+
};
|
|
2650
|
+
}
|
|
2651
|
+
case "folder.create": {
|
|
2652
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2653
|
+
const type = flags["type"];
|
|
2654
|
+
const connection = flags["connection"];
|
|
2655
|
+
return {
|
|
2656
|
+
method: "POST",
|
|
2657
|
+
endpoint: projectEndpoint("/managedfolders/"),
|
|
2658
|
+
identifiers: { name, },
|
|
2659
|
+
payload: { name, type, connection, path: flags["path"], projectKey, },
|
|
2660
|
+
};
|
|
2661
|
+
}
|
|
2414
2662
|
case "job.abort":
|
|
2415
2663
|
return {
|
|
2416
2664
|
method: "POST",
|
|
@@ -2451,17 +2699,6 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2451
2699
|
identifiers: { id, },
|
|
2452
2700
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2453
2701
|
};
|
|
2454
|
-
case "folder.create": {
|
|
2455
|
-
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2456
|
-
const type = flags["type"];
|
|
2457
|
-
const connection = flags["connection"];
|
|
2458
|
-
return {
|
|
2459
|
-
method: "POST",
|
|
2460
|
-
endpoint: projectEndpoint("/managedfolders/"),
|
|
2461
|
-
identifiers: { name, },
|
|
2462
|
-
payload: { name, type, connection, path: flags["path"], projectKey, },
|
|
2463
|
-
};
|
|
2464
|
-
}
|
|
2465
2702
|
case "folder.update":
|
|
2466
2703
|
return {
|
|
2467
2704
|
method: "PUT",
|
|
@@ -2686,6 +2923,577 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2686
2923
|
endpoint: `/public/api/meanings/${encodeURIComponent(id)}`,
|
|
2687
2924
|
identifiers: { id, },
|
|
2688
2925
|
};
|
|
2926
|
+
case "llm.list": {
|
|
2927
|
+
const params = new URLSearchParams();
|
|
2928
|
+
const purpose = flags["purpose"];
|
|
2929
|
+
if (typeof purpose === "string" && purpose.trim() !== "") {
|
|
2930
|
+
params.set("purpose", purpose.trim());
|
|
2931
|
+
}
|
|
2932
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2933
|
+
return {
|
|
2934
|
+
method: "GET",
|
|
2935
|
+
endpoint: projectEndpoint(`/llms/${query}`),
|
|
2936
|
+
};
|
|
2937
|
+
}
|
|
2938
|
+
case "llm.completions":
|
|
2939
|
+
case "llm.embeddings": {
|
|
2940
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
2941
|
+
const llmId = stringField(payload, ["llmId",]);
|
|
2942
|
+
if (llmId === undefined || llmId.trim() === "") {
|
|
2943
|
+
throw new UsageError(`llmId is required and must be a non-empty string (e.g. "openai:openai1:gpt-4").\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2944
|
+
}
|
|
2945
|
+
const queries = payload["queries"];
|
|
2946
|
+
if (!Array.isArray(queries) || queries.length === 0) {
|
|
2947
|
+
throw new UsageError(`queries is required and must be a non-empty array of query objects.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2948
|
+
}
|
|
2949
|
+
return {
|
|
2950
|
+
method: "POST",
|
|
2951
|
+
endpoint: projectEndpoint(action === "completions" ? "/llms/completions" : "/llms/embeddings"),
|
|
2952
|
+
identifiers: { llmId, },
|
|
2953
|
+
payload,
|
|
2954
|
+
};
|
|
2955
|
+
}
|
|
2956
|
+
case "knowledge-bank.search": {
|
|
2957
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
2958
|
+
throw new UsageError(`knowledgeBankId must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2959
|
+
}
|
|
2960
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
2961
|
+
const query = stringField(payload, ["query",]);
|
|
2962
|
+
if (query === undefined || query.trim() === "") {
|
|
2963
|
+
throw new UsageError(`query is required and must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2964
|
+
}
|
|
2965
|
+
return {
|
|
2966
|
+
method: "POST",
|
|
2967
|
+
endpoint: projectEndpoint(`/knowledge-banks/${encodeURIComponent(id)}/search`),
|
|
2968
|
+
identifiers: { knowledgeBankId: id, },
|
|
2969
|
+
payload,
|
|
2970
|
+
};
|
|
2971
|
+
}
|
|
2972
|
+
case "knowledge-bank.clear":
|
|
2973
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
2974
|
+
throw new UsageError(`knowledgeBankId must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2975
|
+
}
|
|
2976
|
+
return {
|
|
2977
|
+
method: "POST",
|
|
2978
|
+
endpoint: projectEndpoint(`/knowledge-banks/${encodeURIComponent(id)}/clear`),
|
|
2979
|
+
identifiers: { knowledgeBankId: id, },
|
|
2980
|
+
};
|
|
2981
|
+
case "data-collection.create":
|
|
2982
|
+
return {
|
|
2983
|
+
method: "POST",
|
|
2984
|
+
endpoint: "/public/api/data-collections/",
|
|
2985
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2986
|
+
};
|
|
2987
|
+
case "data-collection.settings-set":
|
|
2988
|
+
return {
|
|
2989
|
+
method: "PUT",
|
|
2990
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}`,
|
|
2991
|
+
identifiers: { dataCollectionId: id, },
|
|
2992
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2993
|
+
};
|
|
2994
|
+
case "data-collection.delete":
|
|
2995
|
+
return {
|
|
2996
|
+
method: "DELETE",
|
|
2997
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}`,
|
|
2998
|
+
identifiers: { dataCollectionId: id, },
|
|
2999
|
+
};
|
|
3000
|
+
case "data-collection.add-object": {
|
|
3001
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3002
|
+
const cid = id
|
|
3003
|
+
?? (typeof payload["dataCollectionId"] === "string"
|
|
3004
|
+
? payload["dataCollectionId"]
|
|
3005
|
+
: undefined);
|
|
3006
|
+
if (!cid)
|
|
3007
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3008
|
+
const reference = { ...payload, };
|
|
3009
|
+
delete reference["dataCollectionId"];
|
|
3010
|
+
return {
|
|
3011
|
+
method: "POST",
|
|
3012
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(cid)}/objects`,
|
|
3013
|
+
identifiers: { dataCollectionId: cid, },
|
|
3014
|
+
payload: reference,
|
|
3015
|
+
};
|
|
3016
|
+
}
|
|
3017
|
+
case "data-collection.remove-dataset": {
|
|
3018
|
+
const projectKeyArg = args[1];
|
|
3019
|
+
const datasetName = args[2];
|
|
3020
|
+
if (!projectKeyArg || !datasetName) {
|
|
3021
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3022
|
+
}
|
|
3023
|
+
return {
|
|
3024
|
+
method: "DELETE",
|
|
3025
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}/objects/dataset/${encodeURIComponent(projectKeyArg)}/${encodeURIComponent(datasetName)}`,
|
|
3026
|
+
identifiers: { dataCollectionId: id, projectKey: projectKeyArg, datasetName, },
|
|
3027
|
+
};
|
|
3028
|
+
}
|
|
3029
|
+
case "project.tags-set":
|
|
3030
|
+
return {
|
|
3031
|
+
method: "PUT",
|
|
3032
|
+
endpoint: encodedProjectEndpointForPlan(projectKey ?? requiredPlanProjectKey(flags, entry.usage), "/tags"),
|
|
3033
|
+
identifiers: { projectKey: projectKey ?? requiredPlanProjectKey(flags, entry.usage), },
|
|
3034
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3035
|
+
};
|
|
3036
|
+
case "project.metadata-set":
|
|
3037
|
+
return {
|
|
3038
|
+
method: "PUT",
|
|
3039
|
+
endpoint: encodedProjectEndpointForPlan(projectKey ?? requiredPlanProjectKey(flags, entry.usage), "/metadata"),
|
|
3040
|
+
identifiers: { projectKey: projectKey ?? requiredPlanProjectKey(flags, entry.usage), },
|
|
3041
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3042
|
+
};
|
|
3043
|
+
case "macro.run":
|
|
3044
|
+
case "macro.run-and-wait": {
|
|
3045
|
+
// The plan payload mirrors the live POST body exactly: params and
|
|
3046
|
+
// adminParams when provided via --data, {} otherwise. Validation is
|
|
3047
|
+
// identical to the live path (JSON object shape), zero network.
|
|
3048
|
+
const data = optionalJsonFlag(flags, "data");
|
|
3049
|
+
const runPayload = {};
|
|
3050
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
3051
|
+
if (data["params"] !== undefined)
|
|
3052
|
+
runPayload["params"] = data["params"];
|
|
3053
|
+
if (data["adminParams"] !== undefined)
|
|
3054
|
+
runPayload["adminParams"] = data["adminParams"];
|
|
3055
|
+
}
|
|
3056
|
+
return {
|
|
3057
|
+
method: "POST",
|
|
3058
|
+
endpoint: projectEndpoint(`/runnables/${encodeURIComponent(id)}?wait=false`),
|
|
3059
|
+
identifiers: { id, },
|
|
3060
|
+
payload: runPayload,
|
|
3061
|
+
wait: action === "macro.run-and-wait" || flags["wait"] === true,
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
case "macro.abort": {
|
|
3065
|
+
const runId = args[1];
|
|
3066
|
+
if (!runId)
|
|
3067
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3068
|
+
return {
|
|
3069
|
+
method: "POST",
|
|
3070
|
+
endpoint: projectEndpoint(`/runnables/${encodeURIComponent(id)}/abort/${encodeURIComponent(runId)}`),
|
|
3071
|
+
identifiers: { id, runId, },
|
|
3072
|
+
};
|
|
3073
|
+
}
|
|
3074
|
+
case "recipe.metadata-set":
|
|
3075
|
+
return {
|
|
3076
|
+
method: "PUT",
|
|
3077
|
+
endpoint: projectEndpoint(`/recipes/${encodeURIComponent(id)}/metadata`),
|
|
3078
|
+
identifiers: { recipeName: id, },
|
|
3079
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3080
|
+
};
|
|
3081
|
+
case "user.create":
|
|
3082
|
+
return {
|
|
3083
|
+
method: "POST",
|
|
3084
|
+
endpoint: "/public/api/admin/users",
|
|
3085
|
+
payload: redactedUserPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3086
|
+
};
|
|
3087
|
+
case "group.create":
|
|
3088
|
+
return {
|
|
3089
|
+
method: "POST",
|
|
3090
|
+
endpoint: "/public/api/admin/groups",
|
|
3091
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3092
|
+
};
|
|
3093
|
+
case "saved-model.create-external": {
|
|
3094
|
+
const config = optionalJsonFlag(flags, "configuration") ?? optionalJsonFlag(flags, "data");
|
|
3095
|
+
return {
|
|
3096
|
+
method: "POST",
|
|
3097
|
+
endpoint: projectEndpoint("/savedmodels/"),
|
|
3098
|
+
identifiers: { name: id, },
|
|
3099
|
+
payload: {
|
|
3100
|
+
savedModelType: requiredPlanFlag(flags, "type", entry.usage),
|
|
3101
|
+
name: id,
|
|
3102
|
+
...(typeof flags["prediction-type"] === "string"
|
|
3103
|
+
? { predictionType: flags["prediction-type"], }
|
|
3104
|
+
: {}),
|
|
3105
|
+
...(config ? { proxyModelConfiguration: config, } : {}),
|
|
3106
|
+
},
|
|
3107
|
+
};
|
|
3108
|
+
}
|
|
3109
|
+
case "saved-model.update-settings":
|
|
3110
|
+
return {
|
|
3111
|
+
method: "PUT",
|
|
3112
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}`),
|
|
3113
|
+
identifiers: { savedModelId: id, },
|
|
3114
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3115
|
+
};
|
|
3116
|
+
case "saved-model.delete-versions": {
|
|
3117
|
+
const versionsRaw = args[1];
|
|
3118
|
+
if (typeof versionsRaw !== "string" || versionsRaw.trim() === "") {
|
|
3119
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3120
|
+
}
|
|
3121
|
+
return {
|
|
3122
|
+
method: "POST",
|
|
3123
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/actions/delete-versions`),
|
|
3124
|
+
identifiers: { savedModelId: id, },
|
|
3125
|
+
payload: {
|
|
3126
|
+
versions: versionsRaw.split(",").map((v) => v.trim()).filter((v) => v.length > 0),
|
|
3127
|
+
removeIntermediate: parseBooleanOption(flags["remove-intermediate"], "--remove-intermediate") ?? true,
|
|
3128
|
+
},
|
|
3129
|
+
};
|
|
3130
|
+
}
|
|
3131
|
+
case "saved-model.import-mlflow-version":
|
|
3132
|
+
return {
|
|
3133
|
+
method: "POST",
|
|
3134
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}`),
|
|
3135
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3136
|
+
payload: {
|
|
3137
|
+
source: { kind: "local-archive", archive: requiredPlanFlag(flags, "archive", entry.usage), },
|
|
3138
|
+
...(typeof flags["code-env"] === "string"
|
|
3139
|
+
? { codeEnvName: flags["code-env"], }
|
|
3140
|
+
: {}),
|
|
3141
|
+
// Execute always sends the query parameter with the
|
|
3142
|
+
// external-caller default NONE; the plan mirrors it.
|
|
3143
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
3144
|
+
? flags["container-exec-config"]
|
|
3145
|
+
: "NONE",
|
|
3146
|
+
setActive: parseBooleanOption(flags["set-active"], "--set-active") ?? true,
|
|
3147
|
+
...(flags["binary-classification-threshold"] !== undefined
|
|
3148
|
+
? {
|
|
3149
|
+
binaryClassificationThreshold: num(flags["binary-classification-threshold"], "--binary-classification-threshold"),
|
|
3150
|
+
}
|
|
3151
|
+
: {}),
|
|
3152
|
+
},
|
|
3153
|
+
};
|
|
3154
|
+
case "saved-model.import-mlflow-version-from-folder":
|
|
3155
|
+
return {
|
|
3156
|
+
method: "POST",
|
|
3157
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}`),
|
|
3158
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3159
|
+
payload: {
|
|
3160
|
+
source: {
|
|
3161
|
+
kind: "managed-folder",
|
|
3162
|
+
folderRef: requiredPlanFlag(flags, "folder", entry.usage),
|
|
3163
|
+
path: flags["path"],
|
|
3164
|
+
},
|
|
3165
|
+
...(typeof flags["code-env"] === "string" ? { codeEnvName: flags["code-env"], } : {}),
|
|
3166
|
+
// Execute always sends the query parameter with the
|
|
3167
|
+
// external-caller default NONE; the plan mirrors it.
|
|
3168
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
3169
|
+
? flags["container-exec-config"]
|
|
3170
|
+
: "NONE",
|
|
3171
|
+
setActive: parseBooleanOption(flags["set-active"], "--set-active") ?? true,
|
|
3172
|
+
},
|
|
3173
|
+
};
|
|
3174
|
+
case "saved-model.external-metadata-put": {
|
|
3175
|
+
// DSS requires containerExecConfigName on this endpoint; for an
|
|
3176
|
+
// external API caller it resolves as LOCAL-CONFIG -> NONE, so the
|
|
3177
|
+
// plan mirrors the NONE default unless --container-exec-config
|
|
3178
|
+
// overrides it. Same contract as execute.
|
|
3179
|
+
const containerExecConfigName = typeof flags["container-exec-config"] === "string"
|
|
3180
|
+
? flags["container-exec-config"]
|
|
3181
|
+
: "NONE";
|
|
3182
|
+
return {
|
|
3183
|
+
method: "PUT",
|
|
3184
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/external-ml/metadata?containerExecConfigName=${encodeURIComponent(containerExecConfigName)}`),
|
|
3185
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3186
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3187
|
+
};
|
|
3188
|
+
}
|
|
3189
|
+
case "saved-model.evaluate-version": {
|
|
3190
|
+
const payload = {
|
|
3191
|
+
datasetRef: requiredPlanFlag(flags, "dataset", entry.usage),
|
|
3192
|
+
// Execute always sends an explicit containerExecConfigName
|
|
3193
|
+
// (official external-caller semantics: LOCAL-CONFIG -> NONE);
|
|
3194
|
+
// the plan mirrors that default and any explicit override.
|
|
3195
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
3196
|
+
? flags["container-exec-config"]
|
|
3197
|
+
: "NONE",
|
|
3198
|
+
};
|
|
3199
|
+
// --sampling is a JSON flag value; the plan carries the parsed
|
|
3200
|
+
// object under the same key as the execute body (samplingParam).
|
|
3201
|
+
if (flags["sampling"] !== undefined) {
|
|
3202
|
+
payload.samplingParam = json(flags["sampling"], "--sampling");
|
|
3203
|
+
}
|
|
3204
|
+
return {
|
|
3205
|
+
method: "POST",
|
|
3206
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/external-ml/actions/evaluate`),
|
|
3207
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3208
|
+
payload,
|
|
3209
|
+
};
|
|
3210
|
+
}
|
|
3211
|
+
case "saved-model.set-user-meta":
|
|
3212
|
+
return {
|
|
3213
|
+
method: "PUT",
|
|
3214
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/user-meta`),
|
|
3215
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3216
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3217
|
+
};
|
|
3218
|
+
case "user.update":
|
|
3219
|
+
return {
|
|
3220
|
+
method: "PUT",
|
|
3221
|
+
endpoint: `/public/api/admin/users/${encodeURIComponent(id)}`,
|
|
3222
|
+
identifiers: { login: id, },
|
|
3223
|
+
payload: redactedUserPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3224
|
+
};
|
|
3225
|
+
case "user.delete":
|
|
3226
|
+
case "group.delete":
|
|
3227
|
+
return {
|
|
3228
|
+
method: "DELETE",
|
|
3229
|
+
endpoint: resource === "user"
|
|
3230
|
+
? `/public/api/admin/users/${encodeURIComponent(id)}`
|
|
3231
|
+
: `/public/api/admin/groups/${encodeURIComponent(id)}`,
|
|
3232
|
+
identifiers: resource === "user" ? { login: id, } : { name: id, },
|
|
3233
|
+
};
|
|
3234
|
+
case "user.resync":
|
|
3235
|
+
return {
|
|
3236
|
+
method: "POST",
|
|
3237
|
+
endpoint: `/public/api/admin/users/${encodeURIComponent(id)}/actions/resync`,
|
|
3238
|
+
identifiers: { login: id, },
|
|
3239
|
+
};
|
|
3240
|
+
case "user.resync-multi": {
|
|
3241
|
+
const logins = requiredPlanCsv(flags, "logins", entry.usage);
|
|
3242
|
+
return {
|
|
3243
|
+
method: "POST",
|
|
3244
|
+
endpoint: "/public/api/admin/users/actions/resync-multi",
|
|
3245
|
+
identifiers: { count: logins.length, },
|
|
3246
|
+
payload: logins,
|
|
3247
|
+
};
|
|
3248
|
+
}
|
|
3249
|
+
case "user.external-users":
|
|
3250
|
+
return {
|
|
3251
|
+
method: "GET",
|
|
3252
|
+
endpoint: "/public/api/admin/users/actions/external-users",
|
|
3253
|
+
};
|
|
3254
|
+
case "user.external-groups":
|
|
3255
|
+
return {
|
|
3256
|
+
method: "GET",
|
|
3257
|
+
endpoint: "/public/api/admin/users/actions/external-groups",
|
|
3258
|
+
};
|
|
3259
|
+
case "connection.test":
|
|
3260
|
+
return {
|
|
3261
|
+
method: "GET",
|
|
3262
|
+
endpoint: `/public/api/connections/${encodeURIComponent(id)}/test`,
|
|
3263
|
+
identifiers: { connectionName: id, },
|
|
3264
|
+
};
|
|
3265
|
+
case "user.provision":
|
|
3266
|
+
return {
|
|
3267
|
+
method: "POST",
|
|
3268
|
+
endpoint: "/public/api/admin/users/actions/provision",
|
|
3269
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3270
|
+
};
|
|
3271
|
+
case "group.update":
|
|
3272
|
+
return {
|
|
3273
|
+
method: "PUT",
|
|
3274
|
+
endpoint: `/public/api/admin/groups/${encodeURIComponent(id)}`,
|
|
3275
|
+
identifiers: { name: id, },
|
|
3276
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3277
|
+
};
|
|
3278
|
+
case "connection.create":
|
|
3279
|
+
return {
|
|
3280
|
+
method: "POST",
|
|
3281
|
+
endpoint: "/public/api/admin/connections",
|
|
3282
|
+
payload: redactedConnectionPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3283
|
+
};
|
|
3284
|
+
case "connection.update":
|
|
3285
|
+
return {
|
|
3286
|
+
method: "PUT",
|
|
3287
|
+
endpoint: `/public/api/admin/connections/${encodeURIComponent(id)}`,
|
|
3288
|
+
identifiers: { connectionName: id, },
|
|
3289
|
+
payload: redactedConnectionPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3290
|
+
};
|
|
3291
|
+
case "connection.delete":
|
|
3292
|
+
return {
|
|
3293
|
+
method: "DELETE",
|
|
3294
|
+
endpoint: `/public/api/admin/connections/${encodeURIComponent(id)}`,
|
|
3295
|
+
identifiers: { connectionName: id, },
|
|
3296
|
+
};
|
|
3297
|
+
case "connection.prepare-import": {
|
|
3298
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3299
|
+
if (!projectKey)
|
|
3300
|
+
throw new UsageError(`Missing project key for ${resource} ${action}.`);
|
|
3301
|
+
return {
|
|
3302
|
+
method: "POST",
|
|
3303
|
+
endpoint: encodedProjectEndpointForPlan(projectKey, "/datasets/tables-import/actions/prepare-from-keys"),
|
|
3304
|
+
identifiers: { projectKey, },
|
|
3305
|
+
payload,
|
|
3306
|
+
};
|
|
3307
|
+
}
|
|
3308
|
+
case "connection.execute-import": {
|
|
3309
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3310
|
+
if (!projectKey)
|
|
3311
|
+
throw new UsageError(`Missing project key for ${resource} ${action}.`);
|
|
3312
|
+
return {
|
|
3313
|
+
method: "POST",
|
|
3314
|
+
endpoint: encodedProjectEndpointForPlan(projectKey, "/datasets/tables-import/actions/execute-from-candidates"),
|
|
3315
|
+
identifiers: { projectKey, },
|
|
3316
|
+
payload,
|
|
3317
|
+
};
|
|
3318
|
+
}
|
|
3319
|
+
case "plugin.install-from-zip":
|
|
3320
|
+
return {
|
|
3321
|
+
method: "POST",
|
|
3322
|
+
endpoint: "/public/api/plugins/actions/installFromZip",
|
|
3323
|
+
payload: { file: requiredPlanFlag(flags, "file", entry.usage), upload: "multipart", },
|
|
3324
|
+
};
|
|
3325
|
+
case "plugin.install-from-store":
|
|
3326
|
+
return {
|
|
3327
|
+
method: "POST",
|
|
3328
|
+
endpoint: "/public/api/plugins/actions/installFromStore",
|
|
3329
|
+
identifiers: { pluginId: id, },
|
|
3330
|
+
payload: { pluginId: id, },
|
|
3331
|
+
};
|
|
3332
|
+
case "plugin.install-from-git": {
|
|
3333
|
+
const payload = pluginGitPlanPayload(flags);
|
|
3334
|
+
return {
|
|
3335
|
+
method: "POST",
|
|
3336
|
+
endpoint: "/public/api/plugins/actions/installFromGit",
|
|
3337
|
+
payload,
|
|
3338
|
+
};
|
|
3339
|
+
}
|
|
3340
|
+
case "plugin.update-from-zip":
|
|
3341
|
+
return {
|
|
3342
|
+
method: "POST",
|
|
3343
|
+
endpoint: pluginActionEndpoint(id, "updateFromZip"),
|
|
3344
|
+
identifiers: { pluginId: id, },
|
|
3345
|
+
payload: { file: requiredPlanFlag(flags, "file", entry.usage), upload: "multipart", },
|
|
3346
|
+
};
|
|
3347
|
+
case "plugin.update-from-store":
|
|
3348
|
+
return {
|
|
3349
|
+
method: "POST",
|
|
3350
|
+
endpoint: pluginActionEndpoint(id, "updateFromStore"),
|
|
3351
|
+
identifiers: { pluginId: id, },
|
|
3352
|
+
};
|
|
3353
|
+
case "plugin.update-from-git":
|
|
3354
|
+
return {
|
|
3355
|
+
method: "POST",
|
|
3356
|
+
endpoint: pluginActionEndpoint(id, "updateFromGit"),
|
|
3357
|
+
identifiers: { pluginId: id, },
|
|
3358
|
+
payload: pluginGitPlanPayload(flags),
|
|
3359
|
+
};
|
|
3360
|
+
case "plugin.settings-set": {
|
|
3361
|
+
const scope = optionalPlanProjectScope(flags);
|
|
3362
|
+
return {
|
|
3363
|
+
method: "POST",
|
|
3364
|
+
endpoint: `${pluginRootEndpoint(id, "/settings")}${scope}`,
|
|
3365
|
+
identifiers: { pluginId: id, },
|
|
3366
|
+
payload: { configKeys: settingsConfigKeys(flags), },
|
|
3367
|
+
};
|
|
3368
|
+
}
|
|
3369
|
+
case "plugin.code-env-create": {
|
|
3370
|
+
const conda = parseBooleanOption(flags["conda"], "--conda") ?? false;
|
|
3371
|
+
return {
|
|
3372
|
+
method: "POST",
|
|
3373
|
+
endpoint: `${pluginRootEndpoint(id, "/code-env/actions/create")}`,
|
|
3374
|
+
identifiers: { pluginId: id, },
|
|
3375
|
+
payload: {
|
|
3376
|
+
deploymentMode: "PLUGIN_MANAGED",
|
|
3377
|
+
conda,
|
|
3378
|
+
pythonInterpreter: flags["python-interpreter"] ?? null,
|
|
3379
|
+
},
|
|
3380
|
+
wait: flags["wait"] === true,
|
|
3381
|
+
};
|
|
3382
|
+
}
|
|
3383
|
+
case "plugin.code-env-update":
|
|
3384
|
+
return {
|
|
3385
|
+
method: "POST",
|
|
3386
|
+
endpoint: `${pluginRootEndpoint(id, "/code-env/actions/update")}`,
|
|
3387
|
+
identifiers: { pluginId: id, },
|
|
3388
|
+
wait: flags["wait"] === true,
|
|
3389
|
+
};
|
|
3390
|
+
case "plugin.move-to-dev":
|
|
3391
|
+
return {
|
|
3392
|
+
method: "POST",
|
|
3393
|
+
endpoint: pluginActionEndpoint(id, "moveToDev"),
|
|
3394
|
+
identifiers: { pluginId: id, },
|
|
3395
|
+
};
|
|
3396
|
+
case "plugin.delete":
|
|
3397
|
+
return {
|
|
3398
|
+
method: "POST",
|
|
3399
|
+
endpoint: pluginActionEndpoint(id, "delete"),
|
|
3400
|
+
identifiers: { pluginId: id, },
|
|
3401
|
+
payload: { force: parseBooleanOption(flags["force"], "--force") ?? false, },
|
|
3402
|
+
};
|
|
3403
|
+
case "plugin.create-dev": {
|
|
3404
|
+
const creationMode = requiredPlanFlag(flags, "creation-mode", entry.usage);
|
|
3405
|
+
const payload = {
|
|
3406
|
+
pluginId: id,
|
|
3407
|
+
creationMode,
|
|
3408
|
+
};
|
|
3409
|
+
if (typeof flags["repository"] === "string")
|
|
3410
|
+
payload.gitRepository = flags["repository"];
|
|
3411
|
+
if (typeof flags["checkout"] === "string")
|
|
3412
|
+
payload.gitCheckout = flags["checkout"];
|
|
3413
|
+
if (typeof flags["path-in-repository"] === "string") {
|
|
3414
|
+
payload.gitSubpath = flags["path-in-repository"];
|
|
3415
|
+
}
|
|
3416
|
+
return {
|
|
3417
|
+
method: "POST",
|
|
3418
|
+
endpoint: "/public/api/plugins/actions/createDev",
|
|
3419
|
+
identifiers: { pluginId: id, creationMode, },
|
|
3420
|
+
payload,
|
|
3421
|
+
};
|
|
3422
|
+
}
|
|
3423
|
+
case "plugin.set-git-remote": {
|
|
3424
|
+
const repository = requiredPlanFlag(flags, "repository", entry.usage);
|
|
3425
|
+
return {
|
|
3426
|
+
method: "POST",
|
|
3427
|
+
endpoint: `${pluginRootEndpoint(id, "/gitRemote")}`,
|
|
3428
|
+
identifiers: { pluginId: id, },
|
|
3429
|
+
payload: { repositoryUrl: validatedPlanRepositoryUrl(repository, "repository", entry.usage), },
|
|
3430
|
+
};
|
|
3431
|
+
}
|
|
3432
|
+
case "plugin.delete-git-remote":
|
|
3433
|
+
return {
|
|
3434
|
+
method: "DELETE",
|
|
3435
|
+
endpoint: pluginRootEndpoint(id, "/gitRemote"),
|
|
3436
|
+
identifiers: { pluginId: id, },
|
|
3437
|
+
};
|
|
3438
|
+
case "plugin.push":
|
|
3439
|
+
case "plugin.pull":
|
|
3440
|
+
case "plugin.fetch":
|
|
3441
|
+
case "plugin.reset-local":
|
|
3442
|
+
case "plugin.reset-remote": {
|
|
3443
|
+
const actionMap = {
|
|
3444
|
+
"plugin.push": "push",
|
|
3445
|
+
"plugin.pull": "pullRebase",
|
|
3446
|
+
"plugin.fetch": "fetch",
|
|
3447
|
+
"plugin.reset-local": "resetToLocalHeadState",
|
|
3448
|
+
"plugin.reset-remote": "resetToRemoteHeadState",
|
|
3449
|
+
};
|
|
3450
|
+
return {
|
|
3451
|
+
method: "POST",
|
|
3452
|
+
endpoint: pluginActionEndpoint(id, actionMap[`${resource}.${action}`]),
|
|
3453
|
+
identifiers: { pluginId: id, },
|
|
3454
|
+
};
|
|
3455
|
+
}
|
|
3456
|
+
case "plugin.contents-put":
|
|
3457
|
+
return {
|
|
3458
|
+
method: "POST",
|
|
3459
|
+
endpoint: pluginContentsEndpoint(id, args[1] ?? ""),
|
|
3460
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3461
|
+
payload: { contentSource: contentSourceKind(flags), },
|
|
3462
|
+
};
|
|
3463
|
+
case "plugin.contents-delete":
|
|
3464
|
+
return {
|
|
3465
|
+
method: "DELETE",
|
|
3466
|
+
endpoint: pluginContentsEndpoint(id, args[1] ?? ""),
|
|
3467
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3468
|
+
};
|
|
3469
|
+
case "plugin.folder-add":
|
|
3470
|
+
return {
|
|
3471
|
+
method: "POST",
|
|
3472
|
+
endpoint: pluginFoldersEndpoint(id, args[1] ?? ""),
|
|
3473
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3474
|
+
};
|
|
3475
|
+
case "plugin.rename": {
|
|
3476
|
+
const newName = requiredPlanPositionals(entry.usage)[2];
|
|
3477
|
+
if (!newName)
|
|
3478
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3479
|
+
return {
|
|
3480
|
+
method: "POST",
|
|
3481
|
+
endpoint: `${pluginRootEndpoint(id, "/contents-actions/rename")}`,
|
|
3482
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3483
|
+
payload: { oldPath: args[1], newName: args[2], },
|
|
3484
|
+
};
|
|
3485
|
+
}
|
|
3486
|
+
case "plugin.move": {
|
|
3487
|
+
const destination = args[2];
|
|
3488
|
+
if (!destination)
|
|
3489
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3490
|
+
return {
|
|
3491
|
+
method: "POST",
|
|
3492
|
+
endpoint: `${pluginRootEndpoint(id, "/contents-actions/move")}`,
|
|
3493
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3494
|
+
payload: { oldPath: args[1], newPath: destination, },
|
|
3495
|
+
};
|
|
3496
|
+
}
|
|
2689
3497
|
case "business-app.save-settings":
|
|
2690
3498
|
return {
|
|
2691
3499
|
method: "PUT",
|
|
@@ -2725,6 +3533,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2725
3533
|
}
|
|
2726
3534
|
case "app.create-instance": {
|
|
2727
3535
|
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3536
|
+
if (payload["targetProjectKey"] === undefined) {
|
|
3537
|
+
// Generated-at-apply mode: --plan never allocates or reserves a
|
|
3538
|
+
// random key. The payload stays exactly as supplied, the runtime
|
|
3539
|
+
// generates the key (and defaults the display name to it) during
|
|
3540
|
+
// apply, and no absence probe runs for a generated key, so the
|
|
3541
|
+
// plan carries no preflight requests and no fake concrete GET
|
|
3542
|
+
// path: `{targetProjectKey}` resolves at apply time.
|
|
3543
|
+
return {
|
|
3544
|
+
method: "POST",
|
|
3545
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
3546
|
+
identifiers: {
|
|
3547
|
+
appId: id,
|
|
3548
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
3549
|
+
...(payload["targetProjectName"] === undefined
|
|
3550
|
+
? { targetProjectNameGeneratedDuringApply: true, }
|
|
3551
|
+
: {}),
|
|
3552
|
+
preflightExecuted: false,
|
|
3553
|
+
preflightWillRunDuringApply: false,
|
|
3554
|
+
preflightRequests: [],
|
|
3555
|
+
incarnationControl: "client-side-non-atomic-future-target-and-creation-tag-join",
|
|
3556
|
+
incarnationObservationRequests: [
|
|
3557
|
+
{
|
|
3558
|
+
method: "GET",
|
|
3559
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3560
|
+
when: flags["wait"] === true
|
|
3561
|
+
? "after-terminal-future-target"
|
|
3562
|
+
: "conditional-inline-hasResult-target",
|
|
3563
|
+
intent: "After DSS reports inline or terminal creation success for the generated key, observe creationTag for later cleanup binding.",
|
|
3564
|
+
},
|
|
3565
|
+
],
|
|
3566
|
+
note: "Generated-at-apply: a random APP_ targetProjectKey is generated client-side during apply (never at plan time); targetProjectName defaults to it when omitted. No absent-project preflight runs for a generated key.",
|
|
3567
|
+
},
|
|
3568
|
+
payload,
|
|
3569
|
+
wait: flags["wait"] === true,
|
|
3570
|
+
};
|
|
3571
|
+
}
|
|
2728
3572
|
const rawTargetProjectKey = stringField(payload, ["targetProjectKey",]);
|
|
2729
3573
|
if (!rawTargetProjectKey || rawTargetProjectKey.trim() === "") {
|
|
2730
3574
|
throw new UsageError("Instance creation payload must include a non-empty targetProjectKey.", "validation_failed", `Usage: ${entry.usage}`);
|
|
@@ -2817,13 +3661,110 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2817
3661
|
}
|
|
2818
3662
|
case "app.create-successor-instance": {
|
|
2819
3663
|
const sourceProjectKey = requiredPlanFlag(flags, "from", entry.usage);
|
|
2820
|
-
const targetProjectKey =
|
|
2821
|
-
if (sourceProjectKey === targetProjectKey) {
|
|
3664
|
+
const targetProjectKey = optionalPlanFlag(flags, "to", entry.usage);
|
|
3665
|
+
if (targetProjectKey !== undefined && sourceProjectKey === targetProjectKey) {
|
|
2822
3666
|
throw new UsageError("--from and --to must be different project keys.", "validation_failed", `Usage: ${entry.usage}`);
|
|
2823
3667
|
}
|
|
2824
3668
|
const targetProjectName = flags["name"];
|
|
2825
3669
|
const copyPermissions = parseBooleanOption(flags["copy-permissions"], "--copy-permissions")
|
|
2826
3670
|
?? false;
|
|
3671
|
+
if (targetProjectKey === undefined) {
|
|
3672
|
+
// Generated-at-apply mode: --plan never allocates a key and no
|
|
3673
|
+
// target exists yet, so the plan makes no target-absence claim
|
|
3674
|
+
// and advertises no target probe. The key is generated once
|
|
3675
|
+
// during apply, immediately before the single instance POST;
|
|
3676
|
+
// `{targetProjectKey}` resolves only after the terminal future
|
|
3677
|
+
// names it. Source and template gates still run before the POST.
|
|
3678
|
+
return {
|
|
3679
|
+
method: "POST",
|
|
3680
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
3681
|
+
identifiers: {
|
|
3682
|
+
appId: id,
|
|
3683
|
+
sourceProjectKey,
|
|
3684
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
3685
|
+
targetPreflight: "not-applicable-generated-key",
|
|
3686
|
+
preflightExecuted: false,
|
|
3687
|
+
preflightWillRunDuringApply: true,
|
|
3688
|
+
...(targetProjectName !== undefined
|
|
3689
|
+
? { targetProjectName, }
|
|
3690
|
+
: { targetProjectNameGeneratedDuringApply: true, }),
|
|
3691
|
+
copyPermissions,
|
|
3692
|
+
incarnationControl: "client-side-non-atomic-future-target-and-creation-tag-join",
|
|
3693
|
+
incarnationObservationRequests: [
|
|
3694
|
+
{
|
|
3695
|
+
method: "GET",
|
|
3696
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3697
|
+
when: "after-terminal-future-target",
|
|
3698
|
+
intent: "After the terminal future names the generated successor key, observe creationTag and bind later target checks and cleanup to that hash.",
|
|
3699
|
+
},
|
|
3700
|
+
],
|
|
3701
|
+
preflightRequests: [
|
|
3702
|
+
{
|
|
3703
|
+
method: "GET",
|
|
3704
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances/`,
|
|
3705
|
+
when: "before-create",
|
|
3706
|
+
intent: "Verify the --from project is a registered instance of the app.",
|
|
3707
|
+
},
|
|
3708
|
+
{
|
|
3709
|
+
method: "GET",
|
|
3710
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/app-manifest`,
|
|
3711
|
+
when: "before-create",
|
|
3712
|
+
intent: "Verify the --from project is an APP_INSTANCE project.",
|
|
3713
|
+
},
|
|
3714
|
+
],
|
|
3715
|
+
...(copyPermissions
|
|
3716
|
+
? {
|
|
3717
|
+
permissionConcurrencyControl: "client-side-non-atomic-stale-identity-and-hash-checks",
|
|
3718
|
+
permissionRequests: [
|
|
3719
|
+
{
|
|
3720
|
+
method: "GET",
|
|
3721
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/permissions`,
|
|
3722
|
+
intent: "Snapshot the predecessor instance ACL before creation.",
|
|
3723
|
+
},
|
|
3724
|
+
{
|
|
3725
|
+
method: "GET",
|
|
3726
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3727
|
+
intent: "Read the generated successor ACL to decide whether the copy is a no-op.",
|
|
3728
|
+
},
|
|
3729
|
+
{
|
|
3730
|
+
method: "GET",
|
|
3731
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3732
|
+
intent: "Recheck generated successor creationTag after reading its ACL; stop if the project key was reused.",
|
|
3733
|
+
},
|
|
3734
|
+
{
|
|
3735
|
+
method: "GET",
|
|
3736
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/permissions`,
|
|
3737
|
+
intent: "Recheck the predecessor ACL immediately before the write and stop if its hash drifted.",
|
|
3738
|
+
},
|
|
3739
|
+
{
|
|
3740
|
+
method: "GET",
|
|
3741
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3742
|
+
intent: "Recheck generated successor creationTag immediately before the unconditional permission PUT.",
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
method: "PUT",
|
|
3746
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3747
|
+
intent: "Apply the predecessor ACL snapshot to the generated successor instance.",
|
|
3748
|
+
},
|
|
3749
|
+
{
|
|
3750
|
+
method: "GET",
|
|
3751
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3752
|
+
intent: "Verify the generated successor ACL hash equals the predecessor snapshot hash.",
|
|
3753
|
+
},
|
|
3754
|
+
{
|
|
3755
|
+
method: "GET",
|
|
3756
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3757
|
+
intent: "Detect generated successor project-key reuse across the permission write and verification read.",
|
|
3758
|
+
},
|
|
3759
|
+
],
|
|
3760
|
+
}
|
|
3761
|
+
: {}),
|
|
3762
|
+
note: "Generated-at-apply: the successor key is generated once during apply (never at plan time) and no absent-project preflight runs for a generated key, so the plan claims no target absence. Source and template gates run before the single instance POST, then the terminal future's own result names the successor key. The predecessor is never modified or deleted; cleanup targets the generated successor key only.",
|
|
3763
|
+
},
|
|
3764
|
+
payload: targetProjectName !== undefined ? { targetProjectName, } : {},
|
|
3765
|
+
wait: true,
|
|
3766
|
+
};
|
|
3767
|
+
}
|
|
2827
3768
|
return {
|
|
2828
3769
|
method: "POST",
|
|
2829
3770
|
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
@@ -4076,6 +5017,51 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
4076
5017
|
endpoint: projectGitFutureEndpoint(id),
|
|
4077
5018
|
identifiers: { jobId: id, },
|
|
4078
5019
|
};
|
|
5020
|
+
case "project-folder.settings-set": {
|
|
5021
|
+
const payload = requiredPlanJsonInput(flags, `--data, --data-file, or --stdin is required. Usage: ${entry.usage}`);
|
|
5022
|
+
return {
|
|
5023
|
+
method: "PUT",
|
|
5024
|
+
endpoint: `${projectFolderEndpoint(id)}/settings`,
|
|
5025
|
+
identifiers: { folderId: id, },
|
|
5026
|
+
payload,
|
|
5027
|
+
};
|
|
5028
|
+
}
|
|
5029
|
+
case "project-folder.move": {
|
|
5030
|
+
const destination = args[1];
|
|
5031
|
+
if (!destination)
|
|
5032
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
5033
|
+
return {
|
|
5034
|
+
method: "POST",
|
|
5035
|
+
endpoint: `${projectFolderEndpoint(id)}/move${querySuffix({ destination, })}`,
|
|
5036
|
+
identifiers: { folderId: id, destination, },
|
|
5037
|
+
};
|
|
5038
|
+
}
|
|
5039
|
+
case "project-folder.delete":
|
|
5040
|
+
return {
|
|
5041
|
+
method: "DELETE",
|
|
5042
|
+
endpoint: projectFolderEndpoint(id),
|
|
5043
|
+
identifiers: { folderId: id, },
|
|
5044
|
+
};
|
|
5045
|
+
case "project-folder.create-child": {
|
|
5046
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
5047
|
+
return {
|
|
5048
|
+
method: "POST",
|
|
5049
|
+
endpoint: `${projectFolderEndpoint(id)}/children${querySuffix({ name, })}`,
|
|
5050
|
+
identifiers: { parentFolderId: id, name, },
|
|
5051
|
+
};
|
|
5052
|
+
}
|
|
5053
|
+
case "project-folder.move-project": {
|
|
5054
|
+
const projectKeyArg = args[1];
|
|
5055
|
+
const destination = args[2];
|
|
5056
|
+
if (!projectKeyArg || !destination) {
|
|
5057
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
5058
|
+
}
|
|
5059
|
+
return {
|
|
5060
|
+
method: "POST",
|
|
5061
|
+
endpoint: `${projectFolderEndpoint(id)}/projects/${encodeURIComponent(projectKeyArg)}/move${querySuffix({ destination, })}`,
|
|
5062
|
+
identifiers: { folderId: id, projectKey: projectKeyArg, destination, },
|
|
5063
|
+
};
|
|
5064
|
+
}
|
|
4079
5065
|
default:
|
|
4080
5066
|
return {
|
|
4081
5067
|
exact: false,
|