dataiku-sdk 3.1.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 +253 -38
- 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/auth.js +1 -1
- package/dist/src/cli/coerce.js +11 -2
- package/dist/src/cli/commands/analysis.js +4 -3
- package/dist/src/cli/commands/api-service.js +5 -3
- package/dist/src/cli/commands/app.js +61 -18
- package/dist/src/cli/commands/bundle.js +3 -2
- package/dist/src/cli/commands/code-env.js +23 -16
- package/dist/src/cli/commands/connection.d.ts +9 -0
- package/dist/src/cli/commands/connection.js +267 -1
- package/dist/src/cli/commands/dashboard.js +7 -6
- 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/data-quality.js +9 -8
- package/dist/src/cli/commands/dataset.js +170 -40
- package/dist/src/cli/commands/doctor.js +4 -2
- package/dist/src/cli/commands/flow-zone.js +9 -8
- package/dist/src/cli/commands/folder.js +9 -8
- package/dist/src/cli/commands/future.js +4 -3
- 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/insight.js +8 -7
- package/dist/src/cli/commands/job.js +4 -3
- 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/meaning.js +5 -4
- package/dist/src/cli/commands/ml-task.js +21 -8
- package/dist/src/cli/commands/model-evaluation-store.js +4 -3
- package/dist/src/cli/commands/notebook.js +17 -12
- 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-library.js +7 -6
- package/dist/src/cli/commands/project.js +55 -8
- package/dist/src/cli/commands/recipe.js +50 -14
- package/dist/src/cli/commands/saved-model.d.ts +5 -0
- package/dist/src/cli/commands/saved-model.js +392 -4
- package/dist/src/cli/commands/scenario.js +147 -9
- package/dist/src/cli/commands/sql.js +3 -2
- package/dist/src/cli/commands/statistics.js +7 -6
- package/dist/src/cli/commands/user.d.ts +8 -0
- package/dist/src/cli/commands/user.js +296 -0
- package/dist/src/cli/commands/variable.js +2 -1
- package/dist/src/cli/commands/webapp.js +2 -1
- package/dist/src/cli/commands/wiki.js +7 -6
- package/dist/src/cli/contract.d.ts +2 -3
- package/dist/src/cli/contract.js +1167 -163
- package/dist/src/cli/flags.d.ts +4 -0
- package/dist/src/cli/flags.js +50 -2
- 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 +225 -95
- package/dist/src/client.d.ts +86 -6
- package/dist/src/client.js +263 -50
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +4 -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/folders.js +2 -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 -3
- package/dist/src/resources/projects.js +30 -18
- 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/skill.d.ts +13 -4
- package/dist/src/skill.js +75 -24
- 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.d.ts +2 -0
- package/dist/src/utils/response-file.js +34 -0
- package/dist/src/utils/secret-sanitize.d.ts +54 -0
- package/dist/src/utils/secret-sanitize.js +101 -0
- package/package.json +8 -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 +47 -225
- package/skills/dataiku-dss/references/app-releases.md +28 -0
- package/skills/dataiku-dss/references/authentication.md +39 -0
- package/skills/dataiku-dss/references/coding.md +8 -0
- package/skills/dataiku-dss/references/discovery.md +30 -0
- package/skills/dataiku-dss/references/flow-maps.md +12 -0
- package/skills/dataiku-dss/references/mutations.md +33 -0
- package/skills/dataiku-dss/references/troubleshooting.md +29 -0
package/dist/src/cli/contract.js
CHANGED
|
@@ -13,12 +13,12 @@ import { commands, } from "./commands/index.js";
|
|
|
13
13
|
import { projectLibraryPutPayload, } from "./commands/project-library.js";
|
|
14
14
|
import { resolveSqlQueryInvocation, } from "./commands/sql.js";
|
|
15
15
|
import { dataikuEnvironmentEnabled, } from "./env.js";
|
|
16
|
-
import { BOOLEAN_FLAGS, FLAG_ALIASES, KNOWN_LONG_FLAGS, SHORT_FLAGS, } from "./flags.js";
|
|
16
|
+
import { BOOLEAN_FLAGS, executionMode, FLAG_ALIASES, KNOWN_LONG_FLAGS, SHORT_FLAGS, } from "./flags.js";
|
|
17
17
|
import { flowZoneColor, flowZoneMoveItems, flowZoneName, } from "./helpers/flow-zone.js";
|
|
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",
|
|
@@ -749,11 +785,11 @@ function unsafeOutputs(resource, action, producesLocalFile) {
|
|
|
749
785
|
condition: "--output or --output-file",
|
|
750
786
|
kind: "local-file",
|
|
751
787
|
detail: sensitivePermissions
|
|
752
|
-
? "
|
|
753
|
-
: "
|
|
788
|
+
? "Writes access-control identities and permissions to an owner-only local file."
|
|
789
|
+
: "Writes a local file; stdout contains JSON metadata.",
|
|
754
790
|
...(sensitivePermissions
|
|
755
791
|
? {
|
|
756
|
-
safeAlternative: "Keep
|
|
792
|
+
safeAlternative: "Keep owner-only and outside version control unless repository policy explicitly permits committing access-control data.",
|
|
757
793
|
}
|
|
758
794
|
: {}),
|
|
759
795
|
});
|
|
@@ -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
|
|
@@ -1293,10 +1361,10 @@ export function inferCleanupHint(resource, action) {
|
|
|
1293
1361
|
return undefined;
|
|
1294
1362
|
const key = `${resource}.${action}`;
|
|
1295
1363
|
if (key === "notebook.save-jupyter") {
|
|
1296
|
-
return "
|
|
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 "
|
|
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;
|
|
@@ -1317,7 +1385,7 @@ export function inferCleanupHint(resource, action) {
|
|
|
1317
1385
|
if (resource === "data-quality") {
|
|
1318
1386
|
return `Delete with \`dss data-quality delete-rule <dataset> <rule-id>${ifExists}\`.`;
|
|
1319
1387
|
}
|
|
1320
|
-
return `
|
|
1388
|
+
return `For disposable creates: \`dss ${resource} delete <id>${ifExists}\`.`;
|
|
1321
1389
|
}
|
|
1322
1390
|
function buildRegistryEntry(resource, action, meta) {
|
|
1323
1391
|
const requiresAuth = meta.localHandler === undefined && inferRequiresAuth(resource);
|
|
@@ -1427,82 +1495,107 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
1427
1495
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1428
1496
|
};
|
|
1429
1497
|
}
|
|
1430
|
-
|
|
1498
|
+
function commandDefinitions(resourceFilter) {
|
|
1499
|
+
const registry = {};
|
|
1500
|
+
for (const resource of Object.keys(commands)) {
|
|
1501
|
+
if (resourceFilter !== undefined && resource !== resourceFilter)
|
|
1502
|
+
continue;
|
|
1503
|
+
registry[resource] = commands[resource];
|
|
1504
|
+
}
|
|
1505
|
+
if (resourceFilter === undefined || resourceFilter === "commands") {
|
|
1506
|
+
registry.commands = {
|
|
1507
|
+
run: {
|
|
1508
|
+
handler: async () => undefined,
|
|
1509
|
+
usage: COMMANDS_USAGE,
|
|
1510
|
+
description: COMMANDS_DESCRIPTION,
|
|
1511
|
+
examples: COMMANDS_EXAMPLES,
|
|
1512
|
+
},
|
|
1513
|
+
};
|
|
1514
|
+
}
|
|
1515
|
+
if (resourceFilter === undefined || resourceFilter === "agent") {
|
|
1516
|
+
registry.agent = {
|
|
1517
|
+
contract: {
|
|
1518
|
+
handler: async () => undefined,
|
|
1519
|
+
usage: AGENT_CONTRACT_USAGE,
|
|
1520
|
+
description: AGENT_CONTRACT_DESCRIPTION,
|
|
1521
|
+
examples: AGENT_CONTRACT_EXAMPLES,
|
|
1522
|
+
},
|
|
1523
|
+
};
|
|
1524
|
+
}
|
|
1525
|
+
if (resourceFilter === undefined || resourceFilter === "version") {
|
|
1526
|
+
registry.version = {
|
|
1527
|
+
run: {
|
|
1528
|
+
handler: async () => undefined,
|
|
1529
|
+
usage: VERSION_USAGE,
|
|
1530
|
+
description: VERSION_DESCRIPTION,
|
|
1531
|
+
examples: VERSION_EXAMPLES,
|
|
1532
|
+
},
|
|
1533
|
+
};
|
|
1534
|
+
}
|
|
1535
|
+
if (resourceFilter === undefined || resourceFilter === "install-skill") {
|
|
1536
|
+
registry["install-skill"] = {
|
|
1537
|
+
run: {
|
|
1538
|
+
handler: async () => undefined,
|
|
1539
|
+
usage: INSTALL_SKILL_USAGE,
|
|
1540
|
+
description: INSTALL_SKILL_DESCRIPTION,
|
|
1541
|
+
examples: INSTALL_SKILL_EXAMPLES,
|
|
1542
|
+
},
|
|
1543
|
+
};
|
|
1544
|
+
}
|
|
1545
|
+
if (resourceFilter === undefined || resourceFilter === "cleanup") {
|
|
1546
|
+
registry.cleanup = {
|
|
1547
|
+
run: {
|
|
1548
|
+
handler: async () => undefined,
|
|
1549
|
+
usage: CLEANUP_USAGE,
|
|
1550
|
+
description: CLEANUP_DESCRIPTION,
|
|
1551
|
+
examples: CLEANUP_EXAMPLES,
|
|
1552
|
+
},
|
|
1553
|
+
};
|
|
1554
|
+
}
|
|
1555
|
+
if (resourceFilter === undefined || resourceFilter === "fixtures") {
|
|
1556
|
+
registry.fixtures = {
|
|
1557
|
+
run: {
|
|
1558
|
+
handler: async () => undefined,
|
|
1559
|
+
usage: FIXTURES_USAGE,
|
|
1560
|
+
description: FIXTURES_DESCRIPTION,
|
|
1561
|
+
examples: FIXTURES_EXAMPLES,
|
|
1562
|
+
},
|
|
1563
|
+
};
|
|
1564
|
+
}
|
|
1565
|
+
if (resourceFilter === undefined || resourceFilter === "batch") {
|
|
1566
|
+
registry.batch = {
|
|
1567
|
+
run: {
|
|
1568
|
+
handler: async () => undefined,
|
|
1569
|
+
usage: BATCH_USAGE,
|
|
1570
|
+
description: BATCH_DESCRIPTION,
|
|
1571
|
+
examples: BATCH_EXAMPLES,
|
|
1572
|
+
examplePayload: BATCH_EXAMPLE_PAYLOAD,
|
|
1573
|
+
payloadSchema: { stdin: true, dataFlag: true, dataFileFlag: true, jsonShape: "array", },
|
|
1574
|
+
},
|
|
1575
|
+
};
|
|
1576
|
+
}
|
|
1577
|
+
if (resourceFilter === undefined || resourceFilter === "auth") {
|
|
1578
|
+
registry.auth = {};
|
|
1579
|
+
for (const [action, meta,] of Object.entries(AUTH_ACTIONS)) {
|
|
1580
|
+
registry.auth[action] = {
|
|
1581
|
+
handler: async () => undefined,
|
|
1582
|
+
usage: meta.usage,
|
|
1583
|
+
description: meta.description,
|
|
1584
|
+
examples: meta.examples,
|
|
1585
|
+
requiredFlags: meta.requiredFlags,
|
|
1586
|
+
};
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
return registry;
|
|
1590
|
+
}
|
|
1591
|
+
export function buildCommandRegistry(resourceFilter) {
|
|
1431
1592
|
const registry = {};
|
|
1432
|
-
for (const [resource, actions,] of Object.entries(
|
|
1593
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions(resourceFilter))) {
|
|
1433
1594
|
registry[resource] = {};
|
|
1434
1595
|
for (const [action, meta,] of Object.entries(actions)) {
|
|
1435
1596
|
registry[resource][action] = buildRegistryEntry(resource, action, meta);
|
|
1436
1597
|
}
|
|
1437
1598
|
}
|
|
1438
|
-
registry.commands = {
|
|
1439
|
-
run: buildRegistryEntry("commands", "run", {
|
|
1440
|
-
handler: async () => undefined,
|
|
1441
|
-
usage: COMMANDS_USAGE,
|
|
1442
|
-
description: COMMANDS_DESCRIPTION,
|
|
1443
|
-
examples: COMMANDS_EXAMPLES,
|
|
1444
|
-
}),
|
|
1445
|
-
};
|
|
1446
|
-
registry.agent = {
|
|
1447
|
-
contract: buildRegistryEntry("agent", "contract", {
|
|
1448
|
-
handler: async () => undefined,
|
|
1449
|
-
usage: AGENT_CONTRACT_USAGE,
|
|
1450
|
-
description: AGENT_CONTRACT_DESCRIPTION,
|
|
1451
|
-
examples: AGENT_CONTRACT_EXAMPLES,
|
|
1452
|
-
}),
|
|
1453
|
-
};
|
|
1454
|
-
registry.version = {
|
|
1455
|
-
run: buildRegistryEntry("version", "run", {
|
|
1456
|
-
handler: async () => undefined,
|
|
1457
|
-
usage: VERSION_USAGE,
|
|
1458
|
-
description: VERSION_DESCRIPTION,
|
|
1459
|
-
examples: VERSION_EXAMPLES,
|
|
1460
|
-
}),
|
|
1461
|
-
};
|
|
1462
|
-
registry["install-skill"] = {
|
|
1463
|
-
run: buildRegistryEntry("install-skill", "run", {
|
|
1464
|
-
handler: async () => undefined,
|
|
1465
|
-
usage: INSTALL_SKILL_USAGE,
|
|
1466
|
-
description: INSTALL_SKILL_DESCRIPTION,
|
|
1467
|
-
examples: INSTALL_SKILL_EXAMPLES,
|
|
1468
|
-
}),
|
|
1469
|
-
};
|
|
1470
|
-
registry.cleanup = {
|
|
1471
|
-
run: buildRegistryEntry("cleanup", "run", {
|
|
1472
|
-
handler: async () => undefined,
|
|
1473
|
-
usage: CLEANUP_USAGE,
|
|
1474
|
-
description: CLEANUP_DESCRIPTION,
|
|
1475
|
-
examples: CLEANUP_EXAMPLES,
|
|
1476
|
-
}),
|
|
1477
|
-
};
|
|
1478
|
-
registry.fixtures = {
|
|
1479
|
-
run: buildRegistryEntry("fixtures", "run", {
|
|
1480
|
-
handler: async () => undefined,
|
|
1481
|
-
usage: FIXTURES_USAGE,
|
|
1482
|
-
description: FIXTURES_DESCRIPTION,
|
|
1483
|
-
examples: FIXTURES_EXAMPLES,
|
|
1484
|
-
}),
|
|
1485
|
-
};
|
|
1486
|
-
registry.batch = {
|
|
1487
|
-
run: buildRegistryEntry("batch", "run", {
|
|
1488
|
-
handler: async () => undefined,
|
|
1489
|
-
usage: BATCH_USAGE,
|
|
1490
|
-
description: BATCH_DESCRIPTION,
|
|
1491
|
-
examples: BATCH_EXAMPLES,
|
|
1492
|
-
examplePayload: BATCH_EXAMPLE_PAYLOAD,
|
|
1493
|
-
payloadSchema: { stdin: true, dataFlag: true, dataFileFlag: true, jsonShape: "array", },
|
|
1494
|
-
}),
|
|
1495
|
-
};
|
|
1496
|
-
registry.auth = {};
|
|
1497
|
-
for (const [action, meta,] of Object.entries(AUTH_ACTIONS)) {
|
|
1498
|
-
registry.auth[action] = buildRegistryEntry("auth", action, {
|
|
1499
|
-
handler: async () => undefined,
|
|
1500
|
-
usage: meta.usage,
|
|
1501
|
-
description: meta.description,
|
|
1502
|
-
examples: meta.examples,
|
|
1503
|
-
requiredFlags: meta.requiredFlags,
|
|
1504
|
-
});
|
|
1505
|
-
}
|
|
1506
1599
|
return registry;
|
|
1507
1600
|
}
|
|
1508
1601
|
function commandFlagJsonSchema() {
|
|
@@ -1658,38 +1751,43 @@ export function agentContractJsonSchema() {
|
|
|
1658
1751
|
},
|
|
1659
1752
|
};
|
|
1660
1753
|
}
|
|
1661
|
-
export function commandActionSummary(
|
|
1754
|
+
export function commandActionSummary() {
|
|
1662
1755
|
const summary = {};
|
|
1663
|
-
for (const [resource, actions,] of Object.entries(
|
|
1756
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions())) {
|
|
1664
1757
|
summary[resource] = Object.keys(actions).sort();
|
|
1665
1758
|
}
|
|
1666
1759
|
return summary;
|
|
1667
1760
|
}
|
|
1668
1761
|
export function buildAgentContract() {
|
|
1669
|
-
|
|
1762
|
+
let commandSection;
|
|
1763
|
+
let schemaSection;
|
|
1670
1764
|
return {
|
|
1671
1765
|
protocol: "dataiku-sdk-agent",
|
|
1672
1766
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1673
1767
|
cli: cliVersionResult(),
|
|
1674
|
-
commands
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
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
|
+
};
|
|
1685
1781
|
},
|
|
1686
|
-
schemas
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
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
|
+
};
|
|
1693
1791
|
},
|
|
1694
1792
|
stdio: {
|
|
1695
1793
|
stdout: {
|
|
@@ -1697,8 +1795,8 @@ export function buildAgentContract() {
|
|
|
1697
1795
|
success: "single-json-value",
|
|
1698
1796
|
failure: "structured-error-object",
|
|
1699
1797
|
failureResultDetailLimitBytes: 65_536,
|
|
1700
|
-
fieldProjection: "Missing --fields paths
|
|
1701
|
-
richFailureResults: "doctor/batch/cleanup
|
|
1798
|
+
fieldProjection: "Missing --fields paths: null on stdout; field_projection_missing on stderr.",
|
|
1799
|
+
richFailureResults: "doctor/batch/cleanup failures: own compact {ok:false,...} result on stdout and command exit code; no wrapper.",
|
|
1702
1800
|
},
|
|
1703
1801
|
stderr: {
|
|
1704
1802
|
format: "jsonl",
|
|
@@ -1715,7 +1813,7 @@ export function buildAgentContract() {
|
|
|
1715
1813
|
},
|
|
1716
1814
|
compatibility: {
|
|
1717
1815
|
fieldsAreAdditiveWithinMajor: true,
|
|
1718
|
-
failFastWhenUnsupported: "Check agentContractVersion before planning
|
|
1816
|
+
failFastWhenUnsupported: "Check agentContractVersion before planning.",
|
|
1719
1817
|
},
|
|
1720
1818
|
};
|
|
1721
1819
|
}
|
|
@@ -1777,6 +1875,19 @@ function requiredPlanFlag(flags, name, usage) {
|
|
|
1777
1875
|
return value.trim();
|
|
1778
1876
|
throw new UsageError(`--${name} is required. Usage: ${usage}`);
|
|
1779
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
|
+
}
|
|
1780
1891
|
function optionalJsonFlag(flags, name) {
|
|
1781
1892
|
const value = flags[name];
|
|
1782
1893
|
return typeof value === "string" ? parseJsonObject(value, `--${name}`) : undefined;
|
|
@@ -1801,6 +1912,9 @@ function querySuffix(params) {
|
|
|
1801
1912
|
const raw = search.toString();
|
|
1802
1913
|
return raw ? `?${raw}` : "";
|
|
1803
1914
|
}
|
|
1915
|
+
function projectFolderEndpoint(folderId) {
|
|
1916
|
+
return `/public/api/project-folders/${encodeURIComponent(folderId)}`;
|
|
1917
|
+
}
|
|
1804
1918
|
/**
|
|
1805
1919
|
* Git and future API mount. The official Python client mounts these routes on
|
|
1806
1920
|
* `/dip/publicapi`, which is also what the project-git resource uses; do not
|
|
@@ -1845,6 +1959,92 @@ function validatedPlanRepositoryUrl(url, flag, usage) {
|
|
|
1845
1959
|
}
|
|
1846
1960
|
return candidate;
|
|
1847
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
|
+
}
|
|
1848
2048
|
function requiredPlanRepositoryUrl(flags, flag, usage) {
|
|
1849
2049
|
const url = requiredPlanFlag(flags, flag, usage);
|
|
1850
2050
|
return validatedPlanRepositoryUrl(url, flag, usage);
|
|
@@ -2074,8 +2274,8 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2074
2274
|
};
|
|
2075
2275
|
case "future.abort":
|
|
2076
2276
|
return {
|
|
2077
|
-
method: "
|
|
2078
|
-
endpoint: `/public/api/futures/${encodeURIComponent(id)}
|
|
2277
|
+
method: "DELETE",
|
|
2278
|
+
endpoint: `/public/api/futures/${encodeURIComponent(id)}`,
|
|
2079
2279
|
identifiers: { id, },
|
|
2080
2280
|
};
|
|
2081
2281
|
case "flow-zone.create": {
|
|
@@ -2203,8 +2403,38 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2203
2403
|
method: "PUT",
|
|
2204
2404
|
endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}`),
|
|
2205
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, },
|
|
2206
2412
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2207
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
|
+
}
|
|
2208
2438
|
case "recipe.add-input":
|
|
2209
2439
|
case "recipe.remove-input": {
|
|
2210
2440
|
const role = flags["role"] ?? "main";
|
|
@@ -2393,6 +2623,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2393
2623
|
payload: jobBuildPayload(id, projectKey, flags),
|
|
2394
2624
|
wait: action === "build-and-wait" || flags["wait"] === true,
|
|
2395
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
|
+
}
|
|
2396
2662
|
case "job.abort":
|
|
2397
2663
|
return {
|
|
2398
2664
|
method: "POST",
|
|
@@ -2433,17 +2699,6 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2433
2699
|
identifiers: { id, },
|
|
2434
2700
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2435
2701
|
};
|
|
2436
|
-
case "folder.create": {
|
|
2437
|
-
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2438
|
-
const type = flags["type"];
|
|
2439
|
-
const connection = flags["connection"];
|
|
2440
|
-
return {
|
|
2441
|
-
method: "POST",
|
|
2442
|
-
endpoint: projectEndpoint("/managedfolders/"),
|
|
2443
|
-
identifiers: { name, },
|
|
2444
|
-
payload: { name, type, connection, path: flags["path"], projectKey, },
|
|
2445
|
-
};
|
|
2446
|
-
}
|
|
2447
2702
|
case "folder.update":
|
|
2448
2703
|
return {
|
|
2449
2704
|
method: "PUT",
|
|
@@ -2668,6 +2923,577 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2668
2923
|
endpoint: `/public/api/meanings/${encodeURIComponent(id)}`,
|
|
2669
2924
|
identifiers: { id, },
|
|
2670
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
|
+
}
|
|
2671
3497
|
case "business-app.save-settings":
|
|
2672
3498
|
return {
|
|
2673
3499
|
method: "PUT",
|
|
@@ -2707,6 +3533,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2707
3533
|
}
|
|
2708
3534
|
case "app.create-instance": {
|
|
2709
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
|
+
}
|
|
2710
3572
|
const rawTargetProjectKey = stringField(payload, ["targetProjectKey",]);
|
|
2711
3573
|
if (!rawTargetProjectKey || rawTargetProjectKey.trim() === "") {
|
|
2712
3574
|
throw new UsageError("Instance creation payload must include a non-empty targetProjectKey.", "validation_failed", `Usage: ${entry.usage}`);
|
|
@@ -2799,13 +3661,110 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2799
3661
|
}
|
|
2800
3662
|
case "app.create-successor-instance": {
|
|
2801
3663
|
const sourceProjectKey = requiredPlanFlag(flags, "from", entry.usage);
|
|
2802
|
-
const targetProjectKey =
|
|
2803
|
-
if (sourceProjectKey === targetProjectKey) {
|
|
3664
|
+
const targetProjectKey = optionalPlanFlag(flags, "to", entry.usage);
|
|
3665
|
+
if (targetProjectKey !== undefined && sourceProjectKey === targetProjectKey) {
|
|
2804
3666
|
throw new UsageError("--from and --to must be different project keys.", "validation_failed", `Usage: ${entry.usage}`);
|
|
2805
3667
|
}
|
|
2806
3668
|
const targetProjectName = flags["name"];
|
|
2807
3669
|
const copyPermissions = parseBooleanOption(flags["copy-permissions"], "--copy-permissions")
|
|
2808
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
|
+
}
|
|
2809
3768
|
return {
|
|
2810
3769
|
method: "POST",
|
|
2811
3770
|
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
@@ -3654,7 +4613,7 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
3654
4613
|
wait: true,
|
|
3655
4614
|
})}`;
|
|
3656
4615
|
const guarded = flags["if-exists"] === true
|
|
3657
|
-
|| flags
|
|
4616
|
+
|| executionMode(flags).dryRun
|
|
3658
4617
|
|| expectedProjectIncarnation !== undefined;
|
|
3659
4618
|
if (!guarded) {
|
|
3660
4619
|
return {
|
|
@@ -3671,7 +4630,7 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
3671
4630
|
provided: true,
|
|
3672
4631
|
expectedHash: expectedProjectIncarnation,
|
|
3673
4632
|
};
|
|
3674
|
-
if (flags
|
|
4633
|
+
if (executionMode(flags).dryRun) {
|
|
3675
4634
|
return {
|
|
3676
4635
|
method: "GET",
|
|
3677
4636
|
endpoint: projectProbe,
|
|
@@ -4058,6 +5017,51 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
4058
5017
|
endpoint: projectGitFutureEndpoint(id),
|
|
4059
5018
|
identifiers: { jobId: id, },
|
|
4060
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
|
+
}
|
|
4061
5065
|
default:
|
|
4062
5066
|
return {
|
|
4063
5067
|
exact: false,
|
|
@@ -4080,7 +5084,7 @@ export function buildMutationPlan(resource, action, meta, args, flags) {
|
|
|
4080
5084
|
asyncKind: entry.async,
|
|
4081
5085
|
exitCodesOnFailure: exitCodesOnFailure(entry),
|
|
4082
5086
|
idempotency: entry.idempotency,
|
|
4083
|
-
plannedAndDryRun: flags
|
|
5087
|
+
plannedAndDryRun: executionMode(flags).dryRun,
|
|
4084
5088
|
});
|
|
4085
5089
|
}
|
|
4086
5090
|
/**
|