dataiku-sdk 3.2.0 → 3.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +151 -46
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +102 -0
- package/dist/packages/types/src/index.js +76 -0
- package/dist/src/cli/commands/app.js +57 -15
- package/dist/src/cli/commands/bundle.js +1 -1
- package/dist/src/cli/commands/code-env.js +13 -7
- package/dist/src/cli/commands/code.js +1 -1
- package/dist/src/cli/commands/connection.d.ts +9 -0
- package/dist/src/cli/commands/connection.js +267 -1
- package/dist/src/cli/commands/data-collection.d.ts +2 -0
- package/dist/src/cli/commands/data-collection.js +164 -0
- package/dist/src/cli/commands/dataset.js +162 -33
- package/dist/src/cli/commands/future.js +2 -2
- package/dist/src/cli/commands/group.d.ts +2 -0
- package/dist/src/cli/commands/group.js +109 -0
- package/dist/src/cli/commands/index.js +151 -78
- package/dist/src/cli/commands/job.js +4 -2
- package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
- package/dist/src/cli/commands/knowledge-bank.js +80 -0
- package/dist/src/cli/commands/llm.d.ts +2 -0
- package/dist/src/cli/commands/llm.js +93 -0
- package/dist/src/cli/commands/macro.d.ts +2 -0
- package/dist/src/cli/commands/macro.js +221 -0
- package/dist/src/cli/commands/ml-task.js +18 -6
- package/dist/src/cli/commands/notebook.js +6 -2
- package/dist/src/cli/commands/plugin.d.ts +2 -0
- package/dist/src/cli/commands/plugin.js +799 -0
- package/dist/src/cli/commands/project-folder.d.ts +2 -0
- package/dist/src/cli/commands/project-folder.js +197 -0
- package/dist/src/cli/commands/project-git.js +15 -55
- package/dist/src/cli/commands/project.js +52 -6
- package/dist/src/cli/commands/recipe.js +36 -1
- package/dist/src/cli/commands/saved-model.d.ts +5 -0
- package/dist/src/cli/commands/saved-model.js +388 -1
- package/dist/src/cli/commands/scenario.js +139 -2
- package/dist/src/cli/commands/user.d.ts +8 -0
- package/dist/src/cli/commands/user.js +296 -0
- package/dist/src/cli/contract.d.ts +1 -2
- package/dist/src/cli/contract.js +1098 -297
- package/dist/src/cli/flags.js +30 -1
- package/dist/src/cli/helpers/app-successor.d.ts +19 -4
- package/dist/src/cli/helpers/app-successor.js +90 -30
- package/dist/src/cli/helpers/cleanup.js +3 -1
- package/dist/src/cli/helpers/flow-zone.js +1 -2
- package/dist/src/cli/output-schemas.d.ts +24 -0
- package/dist/src/cli/output-schemas.js +233 -0
- package/dist/src/cli/output.d.ts +1 -1
- package/dist/src/cli/output.js +23 -10
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +47 -1
- package/dist/src/cli.js +147 -41
- package/dist/src/client.d.ts +92 -5
- package/dist/src/client.js +202 -31
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +8 -4
- package/dist/src/generated/action-output-schemas.json +1735 -0
- package/dist/src/index.d.ts +16 -7
- package/dist/src/index.js +11 -2
- package/dist/src/resources/analyses.js +1 -8
- package/dist/src/resources/applications.d.ts +8 -6
- package/dist/src/resources/applications.js +21 -21
- package/dist/src/resources/base.d.ts +4 -0
- package/dist/src/resources/base.js +15 -0
- package/dist/src/resources/bundles.d.ts +9 -4
- package/dist/src/resources/bundles.js +10 -5
- package/dist/src/resources/connections.d.ts +78 -1
- package/dist/src/resources/connections.js +132 -1
- package/dist/src/resources/data-collections.d.ts +118 -0
- package/dist/src/resources/data-collections.js +49 -0
- package/dist/src/resources/datasets.d.ts +69 -0
- package/dist/src/resources/datasets.js +136 -5
- package/dist/src/resources/flow-zones.d.ts +6 -2
- package/dist/src/resources/flow-zones.js +6 -3
- package/dist/src/resources/futures.d.ts +6 -1
- package/dist/src/resources/futures.js +68 -22
- package/dist/src/resources/groups.d.ts +47 -0
- package/dist/src/resources/groups.js +56 -0
- package/dist/src/resources/jobs.d.ts +8 -13
- package/dist/src/resources/jobs.js +71 -34
- package/dist/src/resources/knowledge-banks.d.ts +58 -0
- package/dist/src/resources/knowledge-banks.js +61 -0
- package/dist/src/resources/llms.d.ts +92 -0
- package/dist/src/resources/llms.js +62 -0
- package/dist/src/resources/macros.d.ts +121 -0
- package/dist/src/resources/macros.js +231 -0
- package/dist/src/resources/ml-tasks.d.ts +37 -2
- package/dist/src/resources/ml-tasks.js +109 -20
- package/dist/src/resources/model-evaluation-stores.js +1 -8
- package/dist/src/resources/notebooks.d.ts +43 -7
- package/dist/src/resources/notebooks.js +53 -13
- package/dist/src/resources/plugins.d.ts +263 -0
- package/dist/src/resources/plugins.js +455 -0
- package/dist/src/resources/project-folders.d.ts +71 -0
- package/dist/src/resources/project-folders.js +57 -0
- package/dist/src/resources/project-git.d.ts +12 -12
- package/dist/src/resources/project-git.js +84 -82
- package/dist/src/resources/project-library.d.ts +0 -1
- package/dist/src/resources/project-library.js +1 -17
- package/dist/src/resources/projects.d.ts +23 -3
- package/dist/src/resources/projects.js +60 -34
- package/dist/src/resources/recipes.d.ts +15 -1
- package/dist/src/resources/recipes.js +73 -6
- package/dist/src/resources/saved-models.d.ts +134 -1
- package/dist/src/resources/saved-models.js +214 -6
- package/dist/src/resources/scenarios.d.ts +105 -0
- package/dist/src/resources/scenarios.js +232 -106
- package/dist/src/resources/sql.js +5 -1
- package/dist/src/resources/users.d.ts +129 -0
- package/dist/src/resources/users.js +128 -0
- package/dist/src/resources/webapps.d.ts +15 -2
- package/dist/src/resources/webapps.js +18 -4
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/utils/deep-merge.d.ts +1 -1
- package/dist/src/utils/git-reference.d.ts +12 -0
- package/dist/src/utils/git-reference.js +38 -0
- package/dist/src/utils/polling.d.ts +20 -0
- package/dist/src/utils/polling.js +27 -0
- package/dist/src/utils/response-file.js +1 -1
- package/dist/src/utils/secret-sanitize.d.ts +54 -0
- package/dist/src/utils/secret-sanitize.js +101 -0
- package/package.json +9 -8
- package/packages/types/dist/index.d.ts +102 -0
- package/packages/types/dist/index.js +76 -0
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +11 -7
- package/skills/dataiku-dss/references/app-releases.md +8 -8
- package/skills/dataiku-dss/references/authentication.md +6 -6
- package/skills/dataiku-dss/references/discovery.md +4 -4
- package/skills/dataiku-dss/references/mutations.md +1 -1
- package/skills/dataiku-dss/references/troubleshooting.md +2 -2
package/dist/src/cli/contract.js
CHANGED
|
@@ -3,10 +3,11 @@ import * as path from "node:path";
|
|
|
3
3
|
import { validateCredentials, } from "../auth.js";
|
|
4
4
|
import { getCredentialsPath, saveCredentials, } from "../config.js";
|
|
5
5
|
import { DataikuError, } from "../errors.js";
|
|
6
|
+
import actionOutputSchemasMetadata from "../generated/action-output-schemas.json" with { type: "json"
|
|
7
|
+
};
|
|
6
8
|
import { APP_MANIFEST_CONCURRENCY_CONTROL, } from "../resources/applications.js";
|
|
7
|
-
import { encodeGitReferencePath, validateGitReferencePath, } from "../resources/project-git.js";
|
|
8
9
|
import { encodeLibraryPath, PROJECT_LIBRARY_CONCURRENCY_CONTROL, validateLibraryDestinationPath, validateLibraryName, validateLibraryPath, } from "../resources/project-library.js";
|
|
9
|
-
import {
|
|
10
|
+
import { encodeGitReferencePath, validateGitReferencePath, } from "../utils/git-reference.js";
|
|
10
11
|
import { jobBuildTargetTypeFromFlags, json, jsonInput, num, parseBooleanOption, parseJsonObject, recipeInputDatasetsFromFlags, requiredJsonInput, rewritePairsFromFlags, schemaColumnsInput, sha256Hex, stableHash, stringField, textInput, } from "./coerce.js";
|
|
11
12
|
import { parseCodeRunIntegerFlag, resolveCodeInputWithSource, } from "./commands/code.js";
|
|
12
13
|
import { commands, } from "./commands/index.js";
|
|
@@ -18,7 +19,7 @@ import { flowZoneColor, flowZoneMoveItems, flowZoneName, } from "./helpers/flow-
|
|
|
18
19
|
import { recipeBackupPath, recipeRunShouldWait, } from "./helpers/recipe.js";
|
|
19
20
|
import { encodedProjectEndpointForPlan, planResult, } from "./output.js";
|
|
20
21
|
import { resolveLoginCredentials, } from "./runtime.js";
|
|
21
|
-
import { requireArgs, UsageError, } from "./usage.js";
|
|
22
|
+
import { inferRequiresProject, requireArgs, UsageError, } from "./usage.js";
|
|
22
23
|
import { AGENT_CONTRACT_SCHEMA_ID, AGENT_CONTRACT_VERSION, cliVersionResult, JSON_SCHEMA_DRAFT, } from "./version.js";
|
|
23
24
|
function codeEnvWait(flags) {
|
|
24
25
|
return flags["no-wait"] !== true;
|
|
@@ -39,19 +40,27 @@ function codeEnvParams(flags) {
|
|
|
39
40
|
function splitPackageSpec(raw) {
|
|
40
41
|
return raw.split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0);
|
|
41
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* Resolve requested package specs from --file/--packages/--package. An
|
|
45
|
+
* explicit source that resolves to zero specs is a legitimate clear
|
|
46
|
+
* (set-packages replaces specPackageList wholesale); only a call with no
|
|
47
|
+
* package source flag at all is a usage error.
|
|
48
|
+
*/
|
|
42
49
|
function codeEnvPackageList(flags) {
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
50
|
+
const file = flags["file"];
|
|
51
|
+
const packages = typeof file === "string"
|
|
52
|
+
? splitPackageSpec(fs.readFileSync(file, "utf-8"))
|
|
53
|
+
: [];
|
|
47
54
|
if (typeof flags["packages"] === "string") {
|
|
48
55
|
packages.push(...splitPackageSpec(flags["packages"]));
|
|
49
56
|
}
|
|
50
57
|
if (typeof flags["package"] === "string") {
|
|
51
58
|
packages.push(...splitPackageSpec(flags["package"]));
|
|
52
59
|
}
|
|
53
|
-
if (
|
|
54
|
-
|
|
60
|
+
if (typeof file !== "string"
|
|
61
|
+
&& typeof flags["packages"] !== "string"
|
|
62
|
+
&& typeof flags["package"] !== "string") {
|
|
63
|
+
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
64
|
}
|
|
56
65
|
return packages;
|
|
57
66
|
}
|
|
@@ -127,26 +136,31 @@ const READ_ACTIONS = new Set([
|
|
|
127
136
|
"usages",
|
|
128
137
|
"verify-instance",
|
|
129
138
|
]);
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
"
|
|
139
|
-
"
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
"
|
|
147
|
-
"
|
|
148
|
-
|
|
149
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Security admin actions that hand back a DSS future instead of settling
|
|
141
|
+
* inline: resyncs mint sync jobs, external-user/group fetches mint a
|
|
142
|
+
* supplier-enumeration job, and provision runs a supplier import. Without
|
|
143
|
+
* this table they fall through to the read default and `--plan` rejects
|
|
144
|
+
* them as non-mutating.
|
|
145
|
+
*/
|
|
146
|
+
const ADMIN_SECURITY_FUTURE_ACTIONS = {
|
|
147
|
+
"external-groups": true,
|
|
148
|
+
"external-users": true,
|
|
149
|
+
provision: true,
|
|
150
|
+
resync: true,
|
|
151
|
+
"resync-multi": true,
|
|
152
|
+
};
|
|
153
|
+
/** Connection tables-import actions return a DSS future reference. */
|
|
154
|
+
const CONNECTION_FUTURE_ACTIONS = {
|
|
155
|
+
"execute-import": true,
|
|
156
|
+
"prepare-import": true,
|
|
157
|
+
};
|
|
158
|
+
/** Plugin actions that only observe state despite POST shape (documented). */
|
|
159
|
+
const PLUGIN_GIT_OBSERVER_ACTIONS = {
|
|
160
|
+
"git-branches": true,
|
|
161
|
+
list: true,
|
|
162
|
+
usages: true,
|
|
163
|
+
};
|
|
150
164
|
/**
|
|
151
165
|
* Project Git actions that only observe repository state. Everything else in the
|
|
152
166
|
* `project-git` resource is a mutation: the verb-shaped names (`fetch`, `pull`,
|
|
@@ -185,6 +199,17 @@ const PROJECT_GIT_DESTRUCTIVE_ACTIONS = {
|
|
|
185
199
|
"revert-commit": true,
|
|
186
200
|
"revert-to-revision": true,
|
|
187
201
|
};
|
|
202
|
+
/** Plugin actions that discard work or publish it beyond the instance. */
|
|
203
|
+
const PLUGIN_DESTRUCTIVE_ACTIONS = {
|
|
204
|
+
"contents-delete": true,
|
|
205
|
+
delete: true,
|
|
206
|
+
"delete-git-remote": true,
|
|
207
|
+
fetch: true,
|
|
208
|
+
pull: true,
|
|
209
|
+
push: true,
|
|
210
|
+
"reset-local": true,
|
|
211
|
+
"reset-remote": true,
|
|
212
|
+
};
|
|
188
213
|
/** Project Git actions that hand back a DSS future (`{jobId}`) instead of a result. */
|
|
189
214
|
const PROJECT_GIT_FUTURE_ACTIONS = {
|
|
190
215
|
"add-library": true,
|
|
@@ -409,200 +434,18 @@ function structuredExamples(examples, examplePayload) {
|
|
|
409
434
|
};
|
|
410
435
|
});
|
|
411
436
|
}
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
lastModified: { type: "number", },
|
|
423
|
-
children: { type: "array", items: { type: "object", additionalProperties: true, }, },
|
|
424
|
-
},
|
|
425
|
-
};
|
|
426
|
-
const NOTEBOOK_SAVE_OUTPUT_SCHEMA = {
|
|
427
|
-
type: "object",
|
|
428
|
-
additionalProperties: false,
|
|
429
|
-
required: ["saved", "resource", "created", "hash",],
|
|
430
|
-
properties: {
|
|
431
|
-
saved: { type: "string", },
|
|
432
|
-
resource: { enum: ["jupyter-notebook", "sql-notebook",], },
|
|
433
|
-
created: { type: "boolean", },
|
|
434
|
-
hash: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
435
|
-
},
|
|
436
|
-
};
|
|
437
|
-
const COMMAND_OUTPUT_SCHEMAS = {
|
|
438
|
-
"code-env.list": CodeEnvSummaryArraySchema,
|
|
439
|
-
"code-env.get": CodeEnvDetailsSchema,
|
|
440
|
-
"code-env.list-logs": CodeEnvLogSummaryArraySchema,
|
|
441
|
-
"code-env.get-log": {
|
|
442
|
-
oneOf: [
|
|
443
|
-
{
|
|
444
|
-
type: "object",
|
|
445
|
-
required: ["log", "bytes", "truncated", "tailed", "envLang", "envName", "logName",],
|
|
446
|
-
properties: {
|
|
447
|
-
log: { type: "string", },
|
|
448
|
-
bytes: { type: "integer", minimum: 0, },
|
|
449
|
-
truncated: { type: "boolean", },
|
|
450
|
-
tailed: { type: "boolean", },
|
|
451
|
-
envLang: { enum: ["PYTHON", "R",], },
|
|
452
|
-
envName: { type: "string", },
|
|
453
|
-
logName: { type: "string", },
|
|
454
|
-
},
|
|
455
|
-
},
|
|
456
|
-
{
|
|
457
|
-
type: "object",
|
|
458
|
-
required: ["path", "bytes", "truncated", "tailed", "envLang", "envName", "logName",],
|
|
459
|
-
properties: {
|
|
460
|
-
path: { type: "string", },
|
|
461
|
-
bytes: { type: "integer", minimum: 0, },
|
|
462
|
-
truncated: { type: "boolean", },
|
|
463
|
-
tailed: { type: "boolean", },
|
|
464
|
-
envLang: { enum: ["PYTHON", "R",], },
|
|
465
|
-
envName: { type: "string", },
|
|
466
|
-
logName: { type: "string", },
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
],
|
|
470
|
-
},
|
|
471
|
-
"code-env.version": CodeEnvVersionForProjectSchema,
|
|
472
|
-
"code-env.usages": CodeEnvUsageArraySchema,
|
|
473
|
-
"code.run": {
|
|
474
|
-
type: "object",
|
|
475
|
-
additionalProperties: false,
|
|
476
|
-
required: [
|
|
477
|
-
"outcome",
|
|
478
|
-
"success",
|
|
479
|
-
"runId",
|
|
480
|
-
"elapsedMs",
|
|
481
|
-
"pollCount",
|
|
482
|
-
"output",
|
|
483
|
-
"logTruncated",
|
|
484
|
-
"maxLogBytes",
|
|
485
|
-
"cleanup",
|
|
486
|
-
],
|
|
487
|
-
properties: {
|
|
488
|
-
outcome: { type: "string", },
|
|
489
|
-
success: { type: "boolean", },
|
|
490
|
-
runId: { type: "string", },
|
|
491
|
-
elapsedMs: { type: "number", minimum: 0, },
|
|
492
|
-
pollCount: { type: "integer", minimum: 0, },
|
|
493
|
-
output: { type: "string", },
|
|
494
|
-
log: { type: "string", },
|
|
495
|
-
logTruncated: { type: "boolean", },
|
|
496
|
-
maxLogBytes: { type: "integer", minimum: 0, },
|
|
497
|
-
timedOut: { const: true, },
|
|
498
|
-
timeoutMs: { type: "integer", minimum: 0, },
|
|
499
|
-
cleanup: {
|
|
500
|
-
type: "object",
|
|
501
|
-
additionalProperties: false,
|
|
502
|
-
required: ["status",],
|
|
503
|
-
properties: {
|
|
504
|
-
status: { enum: ["deleted", "kept", "failed",], },
|
|
505
|
-
error: { type: "string", },
|
|
506
|
-
},
|
|
507
|
-
},
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
"notebook.list-jupyter": JupyterNotebookSummaryArraySchema,
|
|
511
|
-
"notebook.get-jupyter": JupyterNotebookContentSchema,
|
|
512
|
-
"notebook.sessions-jupyter": NotebookSessionArraySchema,
|
|
513
|
-
"notebook.list-sql": SqlNotebookSummaryArraySchema,
|
|
514
|
-
"notebook.get-sql": SqlNotebookContentSchema,
|
|
515
|
-
"notebook.history-sql": SqlNotebookHistorySchema,
|
|
516
|
-
"notebook.save-jupyter": NOTEBOOK_SAVE_OUTPUT_SCHEMA,
|
|
517
|
-
"notebook.save-sql": NOTEBOOK_SAVE_OUTPUT_SCHEMA,
|
|
518
|
-
"project-library.list": { type: "array", items: PROJECT_LIBRARY_ITEM_OUTPUT_SCHEMA, },
|
|
519
|
-
"project-library.get-bytes": {
|
|
520
|
-
type: "object",
|
|
521
|
-
additionalProperties: false,
|
|
522
|
-
required: ["path", "bytes", "sha256",],
|
|
523
|
-
properties: {
|
|
524
|
-
path: { type: "string", },
|
|
525
|
-
bytes: { type: "integer", minimum: 0, },
|
|
526
|
-
sha256: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
527
|
-
},
|
|
528
|
-
},
|
|
529
|
-
"project-library.put": {
|
|
530
|
-
type: "object",
|
|
531
|
-
additionalProperties: false,
|
|
532
|
-
required: ["updated", "bytes", "sha256",],
|
|
533
|
-
properties: {
|
|
534
|
-
updated: { type: "string", },
|
|
535
|
-
bytes: { type: "integer", minimum: 0, },
|
|
536
|
-
sha256: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
537
|
-
beforeSha256: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
538
|
-
},
|
|
539
|
-
},
|
|
540
|
-
"project-library.diff": {
|
|
541
|
-
type: "object",
|
|
542
|
-
additionalProperties: false,
|
|
543
|
-
required: [
|
|
544
|
-
"path",
|
|
545
|
-
"unchanged",
|
|
546
|
-
"added",
|
|
547
|
-
"removed",
|
|
548
|
-
"diff",
|
|
549
|
-
"diffTruncated",
|
|
550
|
-
"localSha256",
|
|
551
|
-
"localBytes",
|
|
552
|
-
"maxLines",
|
|
553
|
-
],
|
|
554
|
-
properties: {
|
|
555
|
-
path: { type: "string", },
|
|
556
|
-
unchanged: { type: "boolean", },
|
|
557
|
-
added: { type: "integer", minimum: 0, },
|
|
558
|
-
removed: { type: "integer", minimum: 0, },
|
|
559
|
-
diff: { type: "string", },
|
|
560
|
-
diffTruncated: { type: "boolean", },
|
|
561
|
-
binary: { type: "boolean", },
|
|
562
|
-
remoteAbsent: { type: "boolean", },
|
|
563
|
-
remoteSha256: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
564
|
-
remoteBytes: { type: "integer", minimum: 0, },
|
|
565
|
-
localSha256: { type: "string", pattern: "^[a-f0-9]{64}$", },
|
|
566
|
-
localBytes: { type: "integer", minimum: 0, },
|
|
567
|
-
maxLines: { type: "integer", minimum: 0, },
|
|
568
|
-
},
|
|
569
|
-
},
|
|
570
|
-
"project.list": ProjectSummaryArraySchema,
|
|
571
|
-
"project.get": ProjectDetailsSchema,
|
|
572
|
-
"project.metadata": ProjectMetadataSchema,
|
|
573
|
-
"dataset.list": DatasetSummaryArraySchema,
|
|
574
|
-
"dataset.get": DatasetDetailsSchema,
|
|
575
|
-
"dataset.schema": DatasetSchemaSchema,
|
|
576
|
-
"recipe.list": RecipeSummaryArraySchema,
|
|
577
|
-
"job.list": JobSummaryArraySchema,
|
|
578
|
-
"job.wait": JobWaitResultSchema,
|
|
579
|
-
"job.monitor": JobWaitResultSchema,
|
|
580
|
-
"scenario.list": ScenarioSummaryArraySchema,
|
|
581
|
-
"scenario.get": ScenarioDetailsSchema,
|
|
582
|
-
"scenario.status": ScenarioStatusSchema,
|
|
583
|
-
"flow-zone.list": FlowZoneArraySchema,
|
|
584
|
-
"flow-zone.get": FlowZoneSchema,
|
|
585
|
-
"sql.query": {
|
|
586
|
-
anyOf: [
|
|
587
|
-
SqlQueryResponseSchema,
|
|
588
|
-
{
|
|
589
|
-
type: "object",
|
|
590
|
-
required: ["queryId", "rowCount", "preview",],
|
|
591
|
-
additionalProperties: true,
|
|
592
|
-
properties: {
|
|
593
|
-
queryId: { type: "string", },
|
|
594
|
-
rowCount: { type: "number", },
|
|
595
|
-
preview: { type: "array", items: true, },
|
|
596
|
-
truncated: { type: "boolean", },
|
|
597
|
-
outputPath: { type: "string", },
|
|
598
|
-
written: { type: "string", },
|
|
599
|
-
},
|
|
600
|
-
},
|
|
601
|
-
],
|
|
602
|
-
},
|
|
603
|
-
};
|
|
437
|
+
/**
|
|
438
|
+
* Discovery output schemas: the generated declarative metadata committed at
|
|
439
|
+
* `src/generated/action-output-schemas.json`. Plain data only, so discovery
|
|
440
|
+
* (`dss commands run`, `dss agent contract`) never loads the TypeBox graph.
|
|
441
|
+
* The content matches `typeBoxCommandOutputSchemas()` exactly; parity and
|
|
442
|
+
* freshness are enforced by tests/cli/discovery-output-schemas.test.ts and
|
|
443
|
+
* `bun run check`.
|
|
444
|
+
*/
|
|
445
|
+
const actionOutputSchemas = actionOutputSchemasMetadata
|
|
446
|
+
.schemas;
|
|
604
447
|
function outputJsonSchema(resource, action, shape) {
|
|
605
|
-
const precise =
|
|
448
|
+
const precise = actionOutputSchemas[registryKey(resource, action)];
|
|
606
449
|
if (precise)
|
|
607
450
|
return precise;
|
|
608
451
|
if (shape === "array")
|
|
@@ -916,10 +759,33 @@ function inferSideEffect(resource, action) {
|
|
|
916
759
|
return "read";
|
|
917
760
|
if (resource === "data-quality" && action === "compute")
|
|
918
761
|
return "write";
|
|
762
|
+
if (resource === "user" && ADMIN_SECURITY_FUTURE_ACTIONS[action] === true)
|
|
763
|
+
return "write";
|
|
764
|
+
if (resource === "connection" && CONNECTION_FUTURE_ACTIONS[action] === true)
|
|
765
|
+
return "write";
|
|
766
|
+
// Saved-model evaluation triggers scoring work on DSS side (cost-bearing).
|
|
767
|
+
if (resource === "saved-model" && action === "evaluate-version")
|
|
768
|
+
return "write";
|
|
769
|
+
// LLM Mesh completions/embeddings invoke the LLM provider (cost-bearing,
|
|
770
|
+
// arbitrary-prompt execution); the verb shapes match no mutating pattern
|
|
771
|
+
// and would fall through to read, wrongly rejecting --plan. Knowledge-bank
|
|
772
|
+
// search stays a READ per Main's preservation directive.
|
|
773
|
+
if (resource === "llm" && (action === "completions" || action === "embeddings")) {
|
|
774
|
+
return "write";
|
|
775
|
+
}
|
|
919
776
|
// Project Git is classified from the explicit read table, not by verb shape:
|
|
920
777
|
// `switch`, `fetch`, `pull`, `push`, `commit`, the `reset-*`/`revert-*`
|
|
921
778
|
// families, `drop-and-rebuild`, and `future-abort` all mutate repository or
|
|
922
779
|
// future state yet match none of the mutating-verb patterns.
|
|
780
|
+
// Plugin dev-Git actions mutate repository/plugin state like project-git:
|
|
781
|
+
// `fetch`/`pull`/`push`/`reset-*` never match the generic mutating-verb
|
|
782
|
+
// regex, so they are classified explicitly as write (destructive level
|
|
783
|
+
// from the explicit table below). `git-branches` is a state observer
|
|
784
|
+
// (observed GET on DSS 15; the upstream docs' POST is wrong) and stays
|
|
785
|
+
// read.
|
|
786
|
+
if (resource === "plugin") {
|
|
787
|
+
return PLUGIN_GIT_OBSERVER_ACTIONS[action] === true ? "read" : "write";
|
|
788
|
+
}
|
|
923
789
|
if (resource === "project-git") {
|
|
924
790
|
return PROJECT_GIT_READ_ACTIONS[action] === true ? "read" : "write";
|
|
925
791
|
}
|
|
@@ -927,8 +793,9 @@ function inferSideEffect(resource, action) {
|
|
|
927
793
|
return "read";
|
|
928
794
|
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
795
|
.test(action)
|
|
930
|
-
// Compound actions whose mutating verb is a suffix (e.g. permissions-set,
|
|
931
|
-
|
|
796
|
+
// Compound actions whose mutating verb is a suffix (e.g. permissions-set,
|
|
797
|
+
// dataset-compute, saved-model external-metadata-put).
|
|
798
|
+
|| /-(set|compute|restore|put)$/.test(action)) {
|
|
932
799
|
return "write";
|
|
933
800
|
}
|
|
934
801
|
return "read";
|
|
@@ -940,19 +807,6 @@ function inferRequiresAuth(resource) {
|
|
|
940
807
|
&& resource !== "install-skill"
|
|
941
808
|
&& resource !== "version";
|
|
942
809
|
}
|
|
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
810
|
const ARRAY_OUTPUT_ACTIONS = new Set([
|
|
957
811
|
"history",
|
|
958
812
|
"find",
|
|
@@ -1166,7 +1020,7 @@ function cleanupCommandFromDeleteUsage(resource, action) {
|
|
|
1166
1020
|
return "dss notebook delete-jupyter <name> --if-exists";
|
|
1167
1021
|
}
|
|
1168
1022
|
if (`${resource}.${action}` === "notebook.save-sql") {
|
|
1169
|
-
return "dss notebook delete-sql <id> --if-exists";
|
|
1023
|
+
return "dss notebook delete-sql <the returned `saved` id> --if-exists";
|
|
1170
1024
|
}
|
|
1171
1025
|
if (`${resource}.${action}` === "project.import") {
|
|
1172
1026
|
return "dss project delete <used-project-key> --if-exists --expect-project-incarnation <hash>";
|
|
@@ -1203,12 +1057,29 @@ export function supportsCleanupLedger(resource, action) {
|
|
|
1203
1057
|
const EXPLICIT_DESTRUCTIVE_KEYS = {
|
|
1204
1058
|
"dataset.upload-file": true,
|
|
1205
1059
|
"sql.query": true,
|
|
1060
|
+
// Plugin dev-Git and content deletions discard work with no undo route.
|
|
1061
|
+
"plugin.delete": true,
|
|
1062
|
+
"plugin.delete-git-remote": true,
|
|
1063
|
+
"plugin.push": true,
|
|
1064
|
+
"plugin.pull": true,
|
|
1065
|
+
"plugin.fetch": true,
|
|
1066
|
+
"plugin.reset-local": true,
|
|
1067
|
+
"plugin.reset-remote": true,
|
|
1068
|
+
"plugin.contents-delete": true,
|
|
1069
|
+
// Macro runs execute arbitrary plugin code.
|
|
1070
|
+
"macro.run": true,
|
|
1071
|
+
"macro.run-and-wait": true,
|
|
1072
|
+
// Saved-model version deletion removes model versions permanently.
|
|
1073
|
+
"saved-model.delete-versions": true,
|
|
1206
1074
|
};
|
|
1207
1075
|
function inferDestructiveLevel(resource, sideEffect, action) {
|
|
1208
1076
|
if (sideEffect !== "write")
|
|
1209
1077
|
return "none";
|
|
1210
1078
|
if (EXPLICIT_DESTRUCTIVE_KEYS[`${resource}.${action}`] === true)
|
|
1211
1079
|
return "destructive";
|
|
1080
|
+
if (resource === "plugin") {
|
|
1081
|
+
return PLUGIN_DESTRUCTIVE_ACTIONS[action] === true ? "destructive" : "reversible";
|
|
1082
|
+
}
|
|
1212
1083
|
if (resource === "project-git") {
|
|
1213
1084
|
return PROJECT_GIT_DESTRUCTIVE_ACTIONS[action] === true ? "destructive" : "reversible";
|
|
1214
1085
|
}
|
|
@@ -1229,6 +1100,10 @@ function inferAsyncKind(resource, action) {
|
|
|
1229
1100
|
if (resource === "scenario" && ["run", "run-and-wait", "status",].includes(action)) {
|
|
1230
1101
|
return "future";
|
|
1231
1102
|
}
|
|
1103
|
+
if (resource === "user" && ADMIN_SECURITY_FUTURE_ACTIONS[action] === true)
|
|
1104
|
+
return "future";
|
|
1105
|
+
if (resource === "connection" && CONNECTION_FUTURE_ACTIONS[action] === true)
|
|
1106
|
+
return "future";
|
|
1232
1107
|
if (resource === "data-quality" && action === "compute")
|
|
1233
1108
|
return "future";
|
|
1234
1109
|
// Project Git mutation results carry a future job id only for the library
|
|
@@ -1296,7 +1171,7 @@ export function inferCleanupHint(resource, action) {
|
|
|
1296
1171
|
return "If created:true, delete with `dss notebook delete-jupyter <name> --if-exists`.";
|
|
1297
1172
|
}
|
|
1298
1173
|
if (key === "notebook.save-sql") {
|
|
1299
|
-
return "If created:true, delete with `dss notebook delete-sql <
|
|
1174
|
+
return "If created:true, the persisted id is in `saved`; delete with `dss notebook delete-sql <saved> --if-exists`.";
|
|
1300
1175
|
}
|
|
1301
1176
|
if (!(action.startsWith("create") || action === "clone"))
|
|
1302
1177
|
return undefined;
|
|
@@ -1392,6 +1267,7 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
1392
1267
|
const uniqueRequiredFlags = uniqueStrings(requiredFlags);
|
|
1393
1268
|
const uniqueOptionalFlags = uniqueStrings(optionalFlags);
|
|
1394
1269
|
const unsafe = unsafeOutputs(resource, action, producesLocalFile);
|
|
1270
|
+
let schemas;
|
|
1395
1271
|
return {
|
|
1396
1272
|
resource,
|
|
1397
1273
|
action,
|
|
@@ -1418,7 +1294,9 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
1418
1294
|
...(requiredOneOf.length > 0 ? { requiredOneOf, } : {}),
|
|
1419
1295
|
...(inputGroups.length > 0 ? { requiredInputGroups: inputGroups, } : {}),
|
|
1420
1296
|
...(payloadSchema ? { payloadSchema, } : {}),
|
|
1421
|
-
schemas
|
|
1297
|
+
get schemas() {
|
|
1298
|
+
return schemas ??= buildCommandSchemas(resource, action, flagMetadata, uniqueRequiredFlags, requiredOneOf, payloadSchema, outputShape, meta.usage);
|
|
1299
|
+
},
|
|
1422
1300
|
...(unsafe ? { unsafeOutputs: unsafe, } : {}),
|
|
1423
1301
|
...(examplePayload !== undefined ? { examplePayload, } : {}),
|
|
1424
1302
|
...(cleanupCommand ? { cleanupCommand, } : {}),
|
|
@@ -1427,97 +1305,109 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
1427
1305
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1428
1306
|
};
|
|
1429
1307
|
}
|
|
1430
|
-
|
|
1308
|
+
function commandDefinitions(resourceFilter) {
|
|
1431
1309
|
const registry = {};
|
|
1432
|
-
for (const
|
|
1310
|
+
for (const resource of Object.keys(commands)) {
|
|
1433
1311
|
if (resourceFilter !== undefined && resource !== resourceFilter)
|
|
1434
1312
|
continue;
|
|
1435
|
-
registry[resource] =
|
|
1436
|
-
for (const [action, meta,] of Object.entries(actions)) {
|
|
1437
|
-
registry[resource][action] = buildRegistryEntry(resource, action, meta);
|
|
1438
|
-
}
|
|
1313
|
+
registry[resource] = commands[resource];
|
|
1439
1314
|
}
|
|
1440
1315
|
if (resourceFilter === undefined || resourceFilter === "commands") {
|
|
1441
1316
|
registry.commands = {
|
|
1442
|
-
run:
|
|
1317
|
+
run: {
|
|
1443
1318
|
handler: async () => undefined,
|
|
1444
1319
|
usage: COMMANDS_USAGE,
|
|
1445
1320
|
description: COMMANDS_DESCRIPTION,
|
|
1446
1321
|
examples: COMMANDS_EXAMPLES,
|
|
1447
|
-
}
|
|
1322
|
+
},
|
|
1448
1323
|
};
|
|
1449
1324
|
}
|
|
1450
1325
|
if (resourceFilter === undefined || resourceFilter === "agent") {
|
|
1451
1326
|
registry.agent = {
|
|
1452
|
-
contract:
|
|
1327
|
+
contract: {
|
|
1453
1328
|
handler: async () => undefined,
|
|
1454
1329
|
usage: AGENT_CONTRACT_USAGE,
|
|
1455
1330
|
description: AGENT_CONTRACT_DESCRIPTION,
|
|
1456
1331
|
examples: AGENT_CONTRACT_EXAMPLES,
|
|
1457
|
-
}
|
|
1332
|
+
},
|
|
1458
1333
|
};
|
|
1459
1334
|
}
|
|
1460
1335
|
if (resourceFilter === undefined || resourceFilter === "version") {
|
|
1461
1336
|
registry.version = {
|
|
1462
|
-
run:
|
|
1337
|
+
run: {
|
|
1463
1338
|
handler: async () => undefined,
|
|
1464
1339
|
usage: VERSION_USAGE,
|
|
1465
1340
|
description: VERSION_DESCRIPTION,
|
|
1466
1341
|
examples: VERSION_EXAMPLES,
|
|
1467
|
-
}
|
|
1342
|
+
},
|
|
1468
1343
|
};
|
|
1469
1344
|
}
|
|
1470
1345
|
if (resourceFilter === undefined || resourceFilter === "install-skill") {
|
|
1471
1346
|
registry["install-skill"] = {
|
|
1472
|
-
run:
|
|
1347
|
+
run: {
|
|
1473
1348
|
handler: async () => undefined,
|
|
1474
1349
|
usage: INSTALL_SKILL_USAGE,
|
|
1475
1350
|
description: INSTALL_SKILL_DESCRIPTION,
|
|
1476
1351
|
examples: INSTALL_SKILL_EXAMPLES,
|
|
1477
|
-
}
|
|
1352
|
+
},
|
|
1478
1353
|
};
|
|
1479
1354
|
}
|
|
1480
1355
|
if (resourceFilter === undefined || resourceFilter === "cleanup") {
|
|
1481
1356
|
registry.cleanup = {
|
|
1482
|
-
run:
|
|
1357
|
+
run: {
|
|
1483
1358
|
handler: async () => undefined,
|
|
1484
1359
|
usage: CLEANUP_USAGE,
|
|
1485
1360
|
description: CLEANUP_DESCRIPTION,
|
|
1486
1361
|
examples: CLEANUP_EXAMPLES,
|
|
1487
|
-
}
|
|
1362
|
+
},
|
|
1488
1363
|
};
|
|
1489
1364
|
}
|
|
1490
1365
|
if (resourceFilter === undefined || resourceFilter === "fixtures") {
|
|
1491
1366
|
registry.fixtures = {
|
|
1492
|
-
run:
|
|
1367
|
+
run: {
|
|
1493
1368
|
handler: async () => undefined,
|
|
1494
1369
|
usage: FIXTURES_USAGE,
|
|
1495
1370
|
description: FIXTURES_DESCRIPTION,
|
|
1496
1371
|
examples: FIXTURES_EXAMPLES,
|
|
1497
|
-
}
|
|
1372
|
+
},
|
|
1498
1373
|
};
|
|
1499
1374
|
}
|
|
1500
1375
|
if (resourceFilter === undefined || resourceFilter === "batch") {
|
|
1501
1376
|
registry.batch = {
|
|
1502
|
-
run:
|
|
1377
|
+
run: {
|
|
1503
1378
|
handler: async () => undefined,
|
|
1504
1379
|
usage: BATCH_USAGE,
|
|
1505
1380
|
description: BATCH_DESCRIPTION,
|
|
1506
1381
|
examples: BATCH_EXAMPLES,
|
|
1507
1382
|
examplePayload: BATCH_EXAMPLE_PAYLOAD,
|
|
1508
1383
|
payloadSchema: { stdin: true, dataFlag: true, dataFileFlag: true, jsonShape: "array", },
|
|
1509
|
-
}
|
|
1384
|
+
},
|
|
1510
1385
|
};
|
|
1511
1386
|
}
|
|
1512
1387
|
if (resourceFilter === undefined || resourceFilter === "auth") {
|
|
1513
1388
|
registry.auth = {};
|
|
1514
1389
|
for (const [action, meta,] of Object.entries(AUTH_ACTIONS)) {
|
|
1515
|
-
registry.auth[action] =
|
|
1390
|
+
registry.auth[action] = {
|
|
1516
1391
|
handler: async () => undefined,
|
|
1517
1392
|
usage: meta.usage,
|
|
1518
1393
|
description: meta.description,
|
|
1519
1394
|
examples: meta.examples,
|
|
1520
1395
|
requiredFlags: meta.requiredFlags,
|
|
1396
|
+
};
|
|
1397
|
+
}
|
|
1398
|
+
}
|
|
1399
|
+
return registry;
|
|
1400
|
+
}
|
|
1401
|
+
export function buildCommandRegistry(resourceFilter) {
|
|
1402
|
+
const registry = {};
|
|
1403
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions(resourceFilter))) {
|
|
1404
|
+
const entries = {};
|
|
1405
|
+
registry[resource] = entries;
|
|
1406
|
+
for (const [action, meta,] of Object.entries(actions)) {
|
|
1407
|
+
let entry;
|
|
1408
|
+
Object.defineProperty(entries, action, {
|
|
1409
|
+
enumerable: true,
|
|
1410
|
+
get: () => entry ??= buildRegistryEntry(resource, action, meta),
|
|
1521
1411
|
});
|
|
1522
1412
|
}
|
|
1523
1413
|
}
|
|
@@ -1676,38 +1566,43 @@ export function agentContractJsonSchema() {
|
|
|
1676
1566
|
},
|
|
1677
1567
|
};
|
|
1678
1568
|
}
|
|
1679
|
-
export function commandActionSummary(
|
|
1569
|
+
export function commandActionSummary() {
|
|
1680
1570
|
const summary = {};
|
|
1681
|
-
for (const [resource, actions,] of Object.entries(
|
|
1571
|
+
for (const [resource, actions,] of Object.entries(commandDefinitions())) {
|
|
1682
1572
|
summary[resource] = Object.keys(actions).sort();
|
|
1683
1573
|
}
|
|
1684
1574
|
return summary;
|
|
1685
1575
|
}
|
|
1686
1576
|
export function buildAgentContract() {
|
|
1687
|
-
|
|
1577
|
+
let commandSection;
|
|
1578
|
+
let schemaSection;
|
|
1688
1579
|
return {
|
|
1689
1580
|
protocol: "dataiku-sdk-agent",
|
|
1690
1581
|
agentContractVersion: AGENT_CONTRACT_VERSION,
|
|
1691
1582
|
cli: cliVersionResult(),
|
|
1692
|
-
commands
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1583
|
+
get commands() {
|
|
1584
|
+
return commandSection ??= {
|
|
1585
|
+
discoveryCommand: "dss commands run",
|
|
1586
|
+
fullRegistryExportCommand: "dss commands run --output PATH",
|
|
1587
|
+
scopedDiscoveryCommand: "dss commands run --fields RESOURCE[.ACTION[.FIELD...]]",
|
|
1588
|
+
actionIndexCommand: "dss agent contract --fields commands.actions",
|
|
1589
|
+
scopedDiscoveryExamples: [
|
|
1590
|
+
"dss commands run --fields dataset",
|
|
1591
|
+
"dss commands run --fields dataset.create",
|
|
1592
|
+
],
|
|
1593
|
+
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.",
|
|
1594
|
+
actions: commandActionSummary(),
|
|
1595
|
+
};
|
|
1703
1596
|
},
|
|
1704
|
-
schemas
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1597
|
+
get schemas() {
|
|
1598
|
+
return schemaSection ??= {
|
|
1599
|
+
agentContract: agentContractJsonSchema(),
|
|
1600
|
+
commandRegistry: commandRegistryJsonSchema(),
|
|
1601
|
+
commandRegistryEntry: commandRegistryEntryJsonSchema(),
|
|
1602
|
+
errorEnvelope: errorEnvelopeJsonSchema(),
|
|
1603
|
+
warningEvent: warningEventJsonSchema(),
|
|
1604
|
+
traceEvent: traceEventJsonSchema(),
|
|
1605
|
+
};
|
|
1711
1606
|
},
|
|
1712
1607
|
stdio: {
|
|
1713
1608
|
stdout: {
|
|
@@ -1795,6 +1690,19 @@ function requiredPlanFlag(flags, name, usage) {
|
|
|
1795
1690
|
return value.trim();
|
|
1796
1691
|
throw new UsageError(`--${name} is required. Usage: ${usage}`);
|
|
1797
1692
|
}
|
|
1693
|
+
/**
|
|
1694
|
+
* Optional caller-chosen identifier. Absent selects the command's generated
|
|
1695
|
+
* mode; a present-but-empty value is an input error, never a silent omission.
|
|
1696
|
+
*/
|
|
1697
|
+
function optionalPlanFlag(flags, name, usage) {
|
|
1698
|
+
const value = flags[name];
|
|
1699
|
+
if (value === undefined)
|
|
1700
|
+
return undefined;
|
|
1701
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
1702
|
+
throw new UsageError(`--${name} must not be empty when supplied; omit --${name} entirely to generate the successor key during apply. Usage: ${usage}`, "validation_failed");
|
|
1703
|
+
}
|
|
1704
|
+
return value.trim();
|
|
1705
|
+
}
|
|
1798
1706
|
function optionalJsonFlag(flags, name) {
|
|
1799
1707
|
const value = flags[name];
|
|
1800
1708
|
return typeof value === "string" ? parseJsonObject(value, `--${name}`) : undefined;
|
|
@@ -1819,6 +1727,9 @@ function querySuffix(params) {
|
|
|
1819
1727
|
const raw = search.toString();
|
|
1820
1728
|
return raw ? `?${raw}` : "";
|
|
1821
1729
|
}
|
|
1730
|
+
function projectFolderEndpoint(folderId) {
|
|
1731
|
+
return `/public/api/project-folders/${encodeURIComponent(folderId)}`;
|
|
1732
|
+
}
|
|
1822
1733
|
/**
|
|
1823
1734
|
* Git and future API mount. The official Python client mounts these routes on
|
|
1824
1735
|
* `/dip/publicapi`, which is also what the project-git resource uses; do not
|
|
@@ -1863,6 +1774,92 @@ function validatedPlanRepositoryUrl(url, flag, usage) {
|
|
|
1863
1774
|
}
|
|
1864
1775
|
return candidate;
|
|
1865
1776
|
}
|
|
1777
|
+
function pluginRootEndpoint(pluginId, suffix) {
|
|
1778
|
+
return `/public/api/plugins/${encodeURIComponent(pluginId)}${suffix}`;
|
|
1779
|
+
}
|
|
1780
|
+
function pluginActionEndpoint(pluginId, action) {
|
|
1781
|
+
return pluginRootEndpoint(pluginId, `/actions/${action}`);
|
|
1782
|
+
}
|
|
1783
|
+
function pluginContentsEndpoint(pluginId, contentPath) {
|
|
1784
|
+
return `${pluginRootEndpoint(pluginId, "/contents/")}${encodePluginSegmentsForPlan(contentPath)}`;
|
|
1785
|
+
}
|
|
1786
|
+
function pluginFoldersEndpoint(pluginId, contentPath) {
|
|
1787
|
+
return `${pluginRootEndpoint(pluginId, "/folders/")}${encodePluginSegmentsForPlan(contentPath)}`;
|
|
1788
|
+
}
|
|
1789
|
+
/** Encodes a plugin content path per segment for plan-time endpoints. */
|
|
1790
|
+
function encodePluginSegmentsForPlan(contentPath) {
|
|
1791
|
+
return contentPath.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
1792
|
+
}
|
|
1793
|
+
/**
|
|
1794
|
+
* Plan payload for plugin git install/update bodies. The repository URL is
|
|
1795
|
+
* validated (embedded userinfo rejected) and secrets are never echoed: the
|
|
1796
|
+
* plan carries the URL only, never embedded credentials.
|
|
1797
|
+
*/
|
|
1798
|
+
function pluginGitPlanPayload(flags) {
|
|
1799
|
+
const repository = requiredPlanFlag(flags, "repository", "dss plugin install-from-git --repository URL");
|
|
1800
|
+
const payload = {
|
|
1801
|
+
gitRepositoryUrl: validatedPlanRepositoryUrl(repository, "repository", "--repository URL"),
|
|
1802
|
+
};
|
|
1803
|
+
if (typeof flags["checkout"] === "string")
|
|
1804
|
+
payload.gitCheckout = flags["checkout"];
|
|
1805
|
+
if (typeof flags["path-in-repository"] === "string") {
|
|
1806
|
+
payload.gitSubpath = flags["path-in-repository"];
|
|
1807
|
+
}
|
|
1808
|
+
return payload;
|
|
1809
|
+
}
|
|
1810
|
+
function optionalPlanProjectScope(flags) {
|
|
1811
|
+
const projectKey = flags["project-key"];
|
|
1812
|
+
if (typeof projectKey !== "string" || projectKey.trim() === "")
|
|
1813
|
+
return "";
|
|
1814
|
+
return `?projectKey=${encodeURIComponent(projectKey.trim())}`;
|
|
1815
|
+
}
|
|
1816
|
+
function settingsConfigKeys(flags) {
|
|
1817
|
+
const content = flags["content"];
|
|
1818
|
+
if (typeof content !== "string")
|
|
1819
|
+
return [];
|
|
1820
|
+
try {
|
|
1821
|
+
const parsed = JSON.parse(content);
|
|
1822
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
|
|
1823
|
+
return [];
|
|
1824
|
+
return Object.keys(parsed);
|
|
1825
|
+
}
|
|
1826
|
+
catch {
|
|
1827
|
+
return [];
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
function contentSourceKind(flags) {
|
|
1831
|
+
if (flags["stdin"] === true)
|
|
1832
|
+
return "stdin";
|
|
1833
|
+
if (typeof flags["file"] === "string")
|
|
1834
|
+
return "file";
|
|
1835
|
+
if (typeof flags["content"] === "string")
|
|
1836
|
+
return "content";
|
|
1837
|
+
return "unknown";
|
|
1838
|
+
}
|
|
1839
|
+
/**
|
|
1840
|
+
* Plan-safe view of a user create/update body: password values are replaced by
|
|
1841
|
+
* a fixed marker so secrets never appear in --plan output.
|
|
1842
|
+
*/
|
|
1843
|
+
function redactedUserPayload(payload) {
|
|
1844
|
+
if (payload["password"] === undefined)
|
|
1845
|
+
return payload;
|
|
1846
|
+
return { ...payload, password: "<omitted>", };
|
|
1847
|
+
}
|
|
1848
|
+
/**
|
|
1849
|
+
* Plan-safe view of a connection create/update body: the connection-type
|
|
1850
|
+
* specific `params` object may carry credentials (passwords, keys, tokens), so
|
|
1851
|
+
* the plan carries only the marker, never the raw params.
|
|
1852
|
+
*/
|
|
1853
|
+
function redactedConnectionPayload(payload) {
|
|
1854
|
+
if (payload["params"] === undefined)
|
|
1855
|
+
return payload;
|
|
1856
|
+
return { ...payload, params: "<omitted; may include credentials>", };
|
|
1857
|
+
}
|
|
1858
|
+
/** Required CSV flag for plans (comma-separated logins list). */
|
|
1859
|
+
function requiredPlanCsv(flags, name, usage) {
|
|
1860
|
+
const raw = requiredPlanFlag(flags, name, usage);
|
|
1861
|
+
return raw.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
|
|
1862
|
+
}
|
|
1866
1863
|
function requiredPlanRepositoryUrl(flags, flag, usage) {
|
|
1867
1864
|
const url = requiredPlanFlag(flags, flag, usage);
|
|
1868
1865
|
return validatedPlanRepositoryUrl(url, flag, usage);
|
|
@@ -2092,8 +2089,8 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2092
2089
|
};
|
|
2093
2090
|
case "future.abort":
|
|
2094
2091
|
return {
|
|
2095
|
-
method: "
|
|
2096
|
-
endpoint: `/public/api/futures/${encodeURIComponent(id)}
|
|
2092
|
+
method: "DELETE",
|
|
2093
|
+
endpoint: `/public/api/futures/${encodeURIComponent(id)}`,
|
|
2097
2094
|
identifiers: { id, },
|
|
2098
2095
|
};
|
|
2099
2096
|
case "flow-zone.create": {
|
|
@@ -2221,8 +2218,38 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2221
2218
|
method: "PUT",
|
|
2222
2219
|
endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}`),
|
|
2223
2220
|
identifiers: { name: id, },
|
|
2221
|
+
};
|
|
2222
|
+
case "dataset.metadata-set":
|
|
2223
|
+
return {
|
|
2224
|
+
method: "PUT",
|
|
2225
|
+
endpoint: projectEndpoint(`/datasets/${encodeURIComponent(id)}/metadata`),
|
|
2226
|
+
identifiers: { name: id, },
|
|
2224
2227
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2225
2228
|
};
|
|
2229
|
+
case "dataset.create-managed": {
|
|
2230
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2231
|
+
const connection = requiredPlanFlag(flags, "connection", entry.usage);
|
|
2232
|
+
const creationSettings = { connectionId: connection, };
|
|
2233
|
+
const specificSettings = {};
|
|
2234
|
+
if (typeof flags["type-option-id"] === "string") {
|
|
2235
|
+
creationSettings.typeOptionId = flags["type-option-id"];
|
|
2236
|
+
}
|
|
2237
|
+
if (typeof flags["format-option-id"] === "string") {
|
|
2238
|
+
specificSettings.formatOptionId = flags["format-option-id"];
|
|
2239
|
+
}
|
|
2240
|
+
if (typeof flags["copy-partitioning-from"] === "string") {
|
|
2241
|
+
specificSettings.partitioningOptionId = `copy:${flags["partitioning-folder"] === true ? "folder" : "dataset"}:${flags["copy-partitioning-from"]}`;
|
|
2242
|
+
}
|
|
2243
|
+
if (Object.keys(specificSettings).length > 0) {
|
|
2244
|
+
creationSettings.specificSettings = specificSettings;
|
|
2245
|
+
}
|
|
2246
|
+
return {
|
|
2247
|
+
method: "POST",
|
|
2248
|
+
endpoint: projectEndpoint("/datasets/managed"),
|
|
2249
|
+
identifiers: { name, },
|
|
2250
|
+
payload: { name, creationSettings, },
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2226
2253
|
case "recipe.add-input":
|
|
2227
2254
|
case "recipe.remove-input": {
|
|
2228
2255
|
const role = flags["role"] ?? "main";
|
|
@@ -2411,6 +2438,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2411
2438
|
payload: jobBuildPayload(id, projectKey, flags),
|
|
2412
2439
|
wait: action === "build-and-wait" || flags["wait"] === true,
|
|
2413
2440
|
};
|
|
2441
|
+
case "scenario.abort":
|
|
2442
|
+
return {
|
|
2443
|
+
method: "POST",
|
|
2444
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/abort`),
|
|
2445
|
+
identifiers: { id, },
|
|
2446
|
+
};
|
|
2447
|
+
case "scenario.payload-set":
|
|
2448
|
+
return {
|
|
2449
|
+
method: "PUT",
|
|
2450
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/payload`),
|
|
2451
|
+
identifiers: { id, },
|
|
2452
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2453
|
+
};
|
|
2454
|
+
case "scenario.active-set": {
|
|
2455
|
+
const activeRaw = args[1];
|
|
2456
|
+
if (activeRaw !== "true" && activeRaw !== "false") {
|
|
2457
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2458
|
+
}
|
|
2459
|
+
return {
|
|
2460
|
+
method: "PUT",
|
|
2461
|
+
endpoint: projectEndpoint(`/scenarios/${encodeURIComponent(id)}/light`),
|
|
2462
|
+
identifiers: { id, },
|
|
2463
|
+
payload: { id, active: activeRaw === "true", },
|
|
2464
|
+
};
|
|
2465
|
+
}
|
|
2466
|
+
case "folder.create": {
|
|
2467
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
2468
|
+
const type = flags["type"];
|
|
2469
|
+
const connection = flags["connection"];
|
|
2470
|
+
return {
|
|
2471
|
+
method: "POST",
|
|
2472
|
+
endpoint: projectEndpoint("/managedfolders/"),
|
|
2473
|
+
identifiers: { name, },
|
|
2474
|
+
payload: { name, type, connection, path: flags["path"], projectKey, },
|
|
2475
|
+
};
|
|
2476
|
+
}
|
|
2414
2477
|
case "job.abort":
|
|
2415
2478
|
return {
|
|
2416
2479
|
method: "POST",
|
|
@@ -2451,17 +2514,6 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2451
2514
|
identifiers: { id, },
|
|
2452
2515
|
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2453
2516
|
};
|
|
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
2517
|
case "folder.update":
|
|
2466
2518
|
return {
|
|
2467
2519
|
method: "PUT",
|
|
@@ -2686,6 +2738,577 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2686
2738
|
endpoint: `/public/api/meanings/${encodeURIComponent(id)}`,
|
|
2687
2739
|
identifiers: { id, },
|
|
2688
2740
|
};
|
|
2741
|
+
case "llm.list": {
|
|
2742
|
+
const params = new URLSearchParams();
|
|
2743
|
+
const purpose = flags["purpose"];
|
|
2744
|
+
if (typeof purpose === "string" && purpose.trim() !== "") {
|
|
2745
|
+
params.set("purpose", purpose.trim());
|
|
2746
|
+
}
|
|
2747
|
+
const query = params.size > 0 ? `?${params.toString()}` : "";
|
|
2748
|
+
return {
|
|
2749
|
+
method: "GET",
|
|
2750
|
+
endpoint: projectEndpoint(`/llms/${query}`),
|
|
2751
|
+
};
|
|
2752
|
+
}
|
|
2753
|
+
case "llm.completions":
|
|
2754
|
+
case "llm.embeddings": {
|
|
2755
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
2756
|
+
const llmId = stringField(payload, ["llmId",]);
|
|
2757
|
+
if (llmId === undefined || llmId.trim() === "") {
|
|
2758
|
+
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");
|
|
2759
|
+
}
|
|
2760
|
+
const queries = payload["queries"];
|
|
2761
|
+
if (!Array.isArray(queries) || queries.length === 0) {
|
|
2762
|
+
throw new UsageError(`queries is required and must be a non-empty array of query objects.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2763
|
+
}
|
|
2764
|
+
return {
|
|
2765
|
+
method: "POST",
|
|
2766
|
+
endpoint: projectEndpoint(action === "completions" ? "/llms/completions" : "/llms/embeddings"),
|
|
2767
|
+
identifiers: { llmId, },
|
|
2768
|
+
payload,
|
|
2769
|
+
};
|
|
2770
|
+
}
|
|
2771
|
+
case "knowledge-bank.search": {
|
|
2772
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
2773
|
+
throw new UsageError(`knowledgeBankId must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2774
|
+
}
|
|
2775
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
2776
|
+
const query = stringField(payload, ["query",]);
|
|
2777
|
+
if (query === undefined || query.trim() === "") {
|
|
2778
|
+
throw new UsageError(`query is required and must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2779
|
+
}
|
|
2780
|
+
return {
|
|
2781
|
+
method: "POST",
|
|
2782
|
+
endpoint: projectEndpoint(`/knowledge-banks/${encodeURIComponent(id)}/search`),
|
|
2783
|
+
identifiers: { knowledgeBankId: id, },
|
|
2784
|
+
payload,
|
|
2785
|
+
};
|
|
2786
|
+
}
|
|
2787
|
+
case "knowledge-bank.clear":
|
|
2788
|
+
if (typeof id !== "string" || id.trim() === "") {
|
|
2789
|
+
throw new UsageError(`knowledgeBankId must be a non-empty string.\nUsage: ${entry.usage}`, "invalid_flag_value");
|
|
2790
|
+
}
|
|
2791
|
+
return {
|
|
2792
|
+
method: "POST",
|
|
2793
|
+
endpoint: projectEndpoint(`/knowledge-banks/${encodeURIComponent(id)}/clear`),
|
|
2794
|
+
identifiers: { knowledgeBankId: id, },
|
|
2795
|
+
};
|
|
2796
|
+
case "data-collection.create":
|
|
2797
|
+
return {
|
|
2798
|
+
method: "POST",
|
|
2799
|
+
endpoint: "/public/api/data-collections/",
|
|
2800
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2801
|
+
};
|
|
2802
|
+
case "data-collection.settings-set":
|
|
2803
|
+
return {
|
|
2804
|
+
method: "PUT",
|
|
2805
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}`,
|
|
2806
|
+
identifiers: { dataCollectionId: id, },
|
|
2807
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2808
|
+
};
|
|
2809
|
+
case "data-collection.delete":
|
|
2810
|
+
return {
|
|
2811
|
+
method: "DELETE",
|
|
2812
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}`,
|
|
2813
|
+
identifiers: { dataCollectionId: id, },
|
|
2814
|
+
};
|
|
2815
|
+
case "data-collection.add-object": {
|
|
2816
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
2817
|
+
const cid = id
|
|
2818
|
+
?? (typeof payload["dataCollectionId"] === "string"
|
|
2819
|
+
? payload["dataCollectionId"]
|
|
2820
|
+
: undefined);
|
|
2821
|
+
if (!cid)
|
|
2822
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2823
|
+
const reference = { ...payload, };
|
|
2824
|
+
delete reference["dataCollectionId"];
|
|
2825
|
+
return {
|
|
2826
|
+
method: "POST",
|
|
2827
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(cid)}/objects`,
|
|
2828
|
+
identifiers: { dataCollectionId: cid, },
|
|
2829
|
+
payload: reference,
|
|
2830
|
+
};
|
|
2831
|
+
}
|
|
2832
|
+
case "data-collection.remove-dataset": {
|
|
2833
|
+
const projectKeyArg = args[1];
|
|
2834
|
+
const datasetName = args[2];
|
|
2835
|
+
if (!projectKeyArg || !datasetName) {
|
|
2836
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2837
|
+
}
|
|
2838
|
+
return {
|
|
2839
|
+
method: "DELETE",
|
|
2840
|
+
endpoint: `/public/api/data-collections/${encodeURIComponent(id)}/objects/dataset/${encodeURIComponent(projectKeyArg)}/${encodeURIComponent(datasetName)}`,
|
|
2841
|
+
identifiers: { dataCollectionId: id, projectKey: projectKeyArg, datasetName, },
|
|
2842
|
+
};
|
|
2843
|
+
}
|
|
2844
|
+
case "project.tags-set":
|
|
2845
|
+
return {
|
|
2846
|
+
method: "PUT",
|
|
2847
|
+
endpoint: encodedProjectEndpointForPlan(projectKey ?? requiredPlanProjectKey(flags, entry.usage), "/tags"),
|
|
2848
|
+
identifiers: { projectKey: projectKey ?? requiredPlanProjectKey(flags, entry.usage), },
|
|
2849
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2850
|
+
};
|
|
2851
|
+
case "project.metadata-set":
|
|
2852
|
+
return {
|
|
2853
|
+
method: "PUT",
|
|
2854
|
+
endpoint: encodedProjectEndpointForPlan(projectKey ?? requiredPlanProjectKey(flags, entry.usage), "/metadata"),
|
|
2855
|
+
identifiers: { projectKey: projectKey ?? requiredPlanProjectKey(flags, entry.usage), },
|
|
2856
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2857
|
+
};
|
|
2858
|
+
case "macro.run":
|
|
2859
|
+
case "macro.run-and-wait": {
|
|
2860
|
+
// The plan payload mirrors the live POST body exactly: params and
|
|
2861
|
+
// adminParams when provided via --data, {} otherwise. Validation is
|
|
2862
|
+
// identical to the live path (JSON object shape), zero network.
|
|
2863
|
+
const data = optionalJsonFlag(flags, "data");
|
|
2864
|
+
const runPayload = {};
|
|
2865
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
2866
|
+
if (data["params"] !== undefined)
|
|
2867
|
+
runPayload["params"] = data["params"];
|
|
2868
|
+
if (data["adminParams"] !== undefined)
|
|
2869
|
+
runPayload["adminParams"] = data["adminParams"];
|
|
2870
|
+
}
|
|
2871
|
+
return {
|
|
2872
|
+
method: "POST",
|
|
2873
|
+
endpoint: projectEndpoint(`/runnables/${encodeURIComponent(id)}?wait=false`),
|
|
2874
|
+
identifiers: { id, },
|
|
2875
|
+
payload: runPayload,
|
|
2876
|
+
wait: action === "macro.run-and-wait" || flags["wait"] === true,
|
|
2877
|
+
};
|
|
2878
|
+
}
|
|
2879
|
+
case "macro.abort": {
|
|
2880
|
+
const runId = args[1];
|
|
2881
|
+
if (!runId)
|
|
2882
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2883
|
+
return {
|
|
2884
|
+
method: "POST",
|
|
2885
|
+
endpoint: projectEndpoint(`/runnables/${encodeURIComponent(id)}/abort/${encodeURIComponent(runId)}`),
|
|
2886
|
+
identifiers: { id, runId, },
|
|
2887
|
+
};
|
|
2888
|
+
}
|
|
2889
|
+
case "recipe.metadata-set":
|
|
2890
|
+
return {
|
|
2891
|
+
method: "PUT",
|
|
2892
|
+
endpoint: projectEndpoint(`/recipes/${encodeURIComponent(id)}/metadata`),
|
|
2893
|
+
identifiers: { recipeName: id, },
|
|
2894
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2895
|
+
};
|
|
2896
|
+
case "user.create":
|
|
2897
|
+
return {
|
|
2898
|
+
method: "POST",
|
|
2899
|
+
endpoint: "/public/api/admin/users",
|
|
2900
|
+
payload: redactedUserPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
2901
|
+
};
|
|
2902
|
+
case "group.create":
|
|
2903
|
+
return {
|
|
2904
|
+
method: "POST",
|
|
2905
|
+
endpoint: "/public/api/admin/groups",
|
|
2906
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2907
|
+
};
|
|
2908
|
+
case "saved-model.create-external": {
|
|
2909
|
+
const config = optionalJsonFlag(flags, "configuration") ?? optionalJsonFlag(flags, "data");
|
|
2910
|
+
return {
|
|
2911
|
+
method: "POST",
|
|
2912
|
+
endpoint: projectEndpoint("/savedmodels/"),
|
|
2913
|
+
identifiers: { name: id, },
|
|
2914
|
+
payload: {
|
|
2915
|
+
savedModelType: requiredPlanFlag(flags, "type", entry.usage),
|
|
2916
|
+
name: id,
|
|
2917
|
+
...(typeof flags["prediction-type"] === "string"
|
|
2918
|
+
? { predictionType: flags["prediction-type"], }
|
|
2919
|
+
: {}),
|
|
2920
|
+
...(config ? { proxyModelConfiguration: config, } : {}),
|
|
2921
|
+
},
|
|
2922
|
+
};
|
|
2923
|
+
}
|
|
2924
|
+
case "saved-model.update-settings":
|
|
2925
|
+
return {
|
|
2926
|
+
method: "PUT",
|
|
2927
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}`),
|
|
2928
|
+
identifiers: { savedModelId: id, },
|
|
2929
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
2930
|
+
};
|
|
2931
|
+
case "saved-model.delete-versions": {
|
|
2932
|
+
const versionsRaw = args[1];
|
|
2933
|
+
if (typeof versionsRaw !== "string" || versionsRaw.trim() === "") {
|
|
2934
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
2935
|
+
}
|
|
2936
|
+
return {
|
|
2937
|
+
method: "POST",
|
|
2938
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/actions/delete-versions`),
|
|
2939
|
+
identifiers: { savedModelId: id, },
|
|
2940
|
+
payload: {
|
|
2941
|
+
versions: versionsRaw.split(",").map((v) => v.trim()).filter((v) => v.length > 0),
|
|
2942
|
+
removeIntermediate: parseBooleanOption(flags["remove-intermediate"], "--remove-intermediate") ?? true,
|
|
2943
|
+
},
|
|
2944
|
+
};
|
|
2945
|
+
}
|
|
2946
|
+
case "saved-model.import-mlflow-version":
|
|
2947
|
+
return {
|
|
2948
|
+
method: "POST",
|
|
2949
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}`),
|
|
2950
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
2951
|
+
payload: {
|
|
2952
|
+
source: { kind: "local-archive", archive: requiredPlanFlag(flags, "archive", entry.usage), },
|
|
2953
|
+
...(typeof flags["code-env"] === "string"
|
|
2954
|
+
? { codeEnvName: flags["code-env"], }
|
|
2955
|
+
: {}),
|
|
2956
|
+
// Execute always sends the query parameter with the
|
|
2957
|
+
// external-caller default NONE; the plan mirrors it.
|
|
2958
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
2959
|
+
? flags["container-exec-config"]
|
|
2960
|
+
: "NONE",
|
|
2961
|
+
setActive: parseBooleanOption(flags["set-active"], "--set-active") ?? true,
|
|
2962
|
+
...(flags["binary-classification-threshold"] !== undefined
|
|
2963
|
+
? {
|
|
2964
|
+
binaryClassificationThreshold: num(flags["binary-classification-threshold"], "--binary-classification-threshold"),
|
|
2965
|
+
}
|
|
2966
|
+
: {}),
|
|
2967
|
+
},
|
|
2968
|
+
};
|
|
2969
|
+
case "saved-model.import-mlflow-version-from-folder":
|
|
2970
|
+
return {
|
|
2971
|
+
method: "POST",
|
|
2972
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}`),
|
|
2973
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
2974
|
+
payload: {
|
|
2975
|
+
source: {
|
|
2976
|
+
kind: "managed-folder",
|
|
2977
|
+
folderRef: requiredPlanFlag(flags, "folder", entry.usage),
|
|
2978
|
+
path: flags["path"],
|
|
2979
|
+
},
|
|
2980
|
+
...(typeof flags["code-env"] === "string" ? { codeEnvName: flags["code-env"], } : {}),
|
|
2981
|
+
// Execute always sends the query parameter with the
|
|
2982
|
+
// external-caller default NONE; the plan mirrors it.
|
|
2983
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
2984
|
+
? flags["container-exec-config"]
|
|
2985
|
+
: "NONE",
|
|
2986
|
+
setActive: parseBooleanOption(flags["set-active"], "--set-active") ?? true,
|
|
2987
|
+
},
|
|
2988
|
+
};
|
|
2989
|
+
case "saved-model.external-metadata-put": {
|
|
2990
|
+
// DSS requires containerExecConfigName on this endpoint; for an
|
|
2991
|
+
// external API caller it resolves as LOCAL-CONFIG -> NONE, so the
|
|
2992
|
+
// plan mirrors the NONE default unless --container-exec-config
|
|
2993
|
+
// overrides it. Same contract as execute.
|
|
2994
|
+
const containerExecConfigName = typeof flags["container-exec-config"] === "string"
|
|
2995
|
+
? flags["container-exec-config"]
|
|
2996
|
+
: "NONE";
|
|
2997
|
+
return {
|
|
2998
|
+
method: "PUT",
|
|
2999
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/external-ml/metadata?containerExecConfigName=${encodeURIComponent(containerExecConfigName)}`),
|
|
3000
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3001
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3002
|
+
};
|
|
3003
|
+
}
|
|
3004
|
+
case "saved-model.evaluate-version": {
|
|
3005
|
+
const payload = {
|
|
3006
|
+
datasetRef: requiredPlanFlag(flags, "dataset", entry.usage),
|
|
3007
|
+
// Execute always sends an explicit containerExecConfigName
|
|
3008
|
+
// (official external-caller semantics: LOCAL-CONFIG -> NONE);
|
|
3009
|
+
// the plan mirrors that default and any explicit override.
|
|
3010
|
+
containerExecConfigName: typeof flags["container-exec-config"] === "string"
|
|
3011
|
+
? flags["container-exec-config"]
|
|
3012
|
+
: "NONE",
|
|
3013
|
+
};
|
|
3014
|
+
// --sampling is a JSON flag value; the plan carries the parsed
|
|
3015
|
+
// object under the same key as the execute body (samplingParam).
|
|
3016
|
+
if (flags["sampling"] !== undefined) {
|
|
3017
|
+
payload.samplingParam = json(flags["sampling"], "--sampling");
|
|
3018
|
+
}
|
|
3019
|
+
return {
|
|
3020
|
+
method: "POST",
|
|
3021
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/external-ml/actions/evaluate`),
|
|
3022
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3023
|
+
payload,
|
|
3024
|
+
};
|
|
3025
|
+
}
|
|
3026
|
+
case "saved-model.set-user-meta":
|
|
3027
|
+
return {
|
|
3028
|
+
method: "PUT",
|
|
3029
|
+
endpoint: projectEndpoint(`/savedmodels/${encodeURIComponent(id)}/versions/${encodeURIComponent(args[1] ?? "")}/user-meta`),
|
|
3030
|
+
identifiers: { savedModelId: id, versionId: args[1], },
|
|
3031
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3032
|
+
};
|
|
3033
|
+
case "user.update":
|
|
3034
|
+
return {
|
|
3035
|
+
method: "PUT",
|
|
3036
|
+
endpoint: `/public/api/admin/users/${encodeURIComponent(id)}`,
|
|
3037
|
+
identifiers: { login: id, },
|
|
3038
|
+
payload: redactedUserPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3039
|
+
};
|
|
3040
|
+
case "user.delete":
|
|
3041
|
+
case "group.delete":
|
|
3042
|
+
return {
|
|
3043
|
+
method: "DELETE",
|
|
3044
|
+
endpoint: resource === "user"
|
|
3045
|
+
? `/public/api/admin/users/${encodeURIComponent(id)}`
|
|
3046
|
+
: `/public/api/admin/groups/${encodeURIComponent(id)}`,
|
|
3047
|
+
identifiers: resource === "user" ? { login: id, } : { name: id, },
|
|
3048
|
+
};
|
|
3049
|
+
case "user.resync":
|
|
3050
|
+
return {
|
|
3051
|
+
method: "POST",
|
|
3052
|
+
endpoint: `/public/api/admin/users/${encodeURIComponent(id)}/actions/resync`,
|
|
3053
|
+
identifiers: { login: id, },
|
|
3054
|
+
};
|
|
3055
|
+
case "user.resync-multi": {
|
|
3056
|
+
const logins = requiredPlanCsv(flags, "logins", entry.usage);
|
|
3057
|
+
return {
|
|
3058
|
+
method: "POST",
|
|
3059
|
+
endpoint: "/public/api/admin/users/actions/resync-multi",
|
|
3060
|
+
identifiers: { count: logins.length, },
|
|
3061
|
+
payload: logins,
|
|
3062
|
+
};
|
|
3063
|
+
}
|
|
3064
|
+
case "user.external-users":
|
|
3065
|
+
return {
|
|
3066
|
+
method: "GET",
|
|
3067
|
+
endpoint: "/public/api/admin/users/actions/external-users",
|
|
3068
|
+
};
|
|
3069
|
+
case "user.external-groups":
|
|
3070
|
+
return {
|
|
3071
|
+
method: "GET",
|
|
3072
|
+
endpoint: "/public/api/admin/users/actions/external-groups",
|
|
3073
|
+
};
|
|
3074
|
+
case "connection.test":
|
|
3075
|
+
return {
|
|
3076
|
+
method: "GET",
|
|
3077
|
+
endpoint: `/public/api/connections/${encodeURIComponent(id)}/test`,
|
|
3078
|
+
identifiers: { connectionName: id, },
|
|
3079
|
+
};
|
|
3080
|
+
case "user.provision":
|
|
3081
|
+
return {
|
|
3082
|
+
method: "POST",
|
|
3083
|
+
endpoint: "/public/api/admin/users/actions/provision",
|
|
3084
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3085
|
+
};
|
|
3086
|
+
case "group.update":
|
|
3087
|
+
return {
|
|
3088
|
+
method: "PUT",
|
|
3089
|
+
endpoint: `/public/api/admin/groups/${encodeURIComponent(id)}`,
|
|
3090
|
+
identifiers: { name: id, },
|
|
3091
|
+
payload: requiredPlanJsonInput(flags, entry.usage),
|
|
3092
|
+
};
|
|
3093
|
+
case "connection.create":
|
|
3094
|
+
return {
|
|
3095
|
+
method: "POST",
|
|
3096
|
+
endpoint: "/public/api/admin/connections",
|
|
3097
|
+
payload: redactedConnectionPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3098
|
+
};
|
|
3099
|
+
case "connection.update":
|
|
3100
|
+
return {
|
|
3101
|
+
method: "PUT",
|
|
3102
|
+
endpoint: `/public/api/admin/connections/${encodeURIComponent(id)}`,
|
|
3103
|
+
identifiers: { connectionName: id, },
|
|
3104
|
+
payload: redactedConnectionPayload(requiredPlanJsonInput(flags, entry.usage)),
|
|
3105
|
+
};
|
|
3106
|
+
case "connection.delete":
|
|
3107
|
+
return {
|
|
3108
|
+
method: "DELETE",
|
|
3109
|
+
endpoint: `/public/api/admin/connections/${encodeURIComponent(id)}`,
|
|
3110
|
+
identifiers: { connectionName: id, },
|
|
3111
|
+
};
|
|
3112
|
+
case "connection.prepare-import": {
|
|
3113
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3114
|
+
if (!projectKey)
|
|
3115
|
+
throw new UsageError(`Missing project key for ${resource} ${action}.`);
|
|
3116
|
+
return {
|
|
3117
|
+
method: "POST",
|
|
3118
|
+
endpoint: encodedProjectEndpointForPlan(projectKey, "/datasets/tables-import/actions/prepare-from-keys"),
|
|
3119
|
+
identifiers: { projectKey, },
|
|
3120
|
+
payload,
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
case "connection.execute-import": {
|
|
3124
|
+
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3125
|
+
if (!projectKey)
|
|
3126
|
+
throw new UsageError(`Missing project key for ${resource} ${action}.`);
|
|
3127
|
+
return {
|
|
3128
|
+
method: "POST",
|
|
3129
|
+
endpoint: encodedProjectEndpointForPlan(projectKey, "/datasets/tables-import/actions/execute-from-candidates"),
|
|
3130
|
+
identifiers: { projectKey, },
|
|
3131
|
+
payload,
|
|
3132
|
+
};
|
|
3133
|
+
}
|
|
3134
|
+
case "plugin.install-from-zip":
|
|
3135
|
+
return {
|
|
3136
|
+
method: "POST",
|
|
3137
|
+
endpoint: "/public/api/plugins/actions/installFromZip",
|
|
3138
|
+
payload: { file: requiredPlanFlag(flags, "file", entry.usage), upload: "multipart", },
|
|
3139
|
+
};
|
|
3140
|
+
case "plugin.install-from-store":
|
|
3141
|
+
return {
|
|
3142
|
+
method: "POST",
|
|
3143
|
+
endpoint: "/public/api/plugins/actions/installFromStore",
|
|
3144
|
+
identifiers: { pluginId: id, },
|
|
3145
|
+
payload: { pluginId: id, },
|
|
3146
|
+
};
|
|
3147
|
+
case "plugin.install-from-git": {
|
|
3148
|
+
const payload = pluginGitPlanPayload(flags);
|
|
3149
|
+
return {
|
|
3150
|
+
method: "POST",
|
|
3151
|
+
endpoint: "/public/api/plugins/actions/installFromGit",
|
|
3152
|
+
payload,
|
|
3153
|
+
};
|
|
3154
|
+
}
|
|
3155
|
+
case "plugin.update-from-zip":
|
|
3156
|
+
return {
|
|
3157
|
+
method: "POST",
|
|
3158
|
+
endpoint: pluginActionEndpoint(id, "updateFromZip"),
|
|
3159
|
+
identifiers: { pluginId: id, },
|
|
3160
|
+
payload: { file: requiredPlanFlag(flags, "file", entry.usage), upload: "multipart", },
|
|
3161
|
+
};
|
|
3162
|
+
case "plugin.update-from-store":
|
|
3163
|
+
return {
|
|
3164
|
+
method: "POST",
|
|
3165
|
+
endpoint: pluginActionEndpoint(id, "updateFromStore"),
|
|
3166
|
+
identifiers: { pluginId: id, },
|
|
3167
|
+
};
|
|
3168
|
+
case "plugin.update-from-git":
|
|
3169
|
+
return {
|
|
3170
|
+
method: "POST",
|
|
3171
|
+
endpoint: pluginActionEndpoint(id, "updateFromGit"),
|
|
3172
|
+
identifiers: { pluginId: id, },
|
|
3173
|
+
payload: pluginGitPlanPayload(flags),
|
|
3174
|
+
};
|
|
3175
|
+
case "plugin.settings-set": {
|
|
3176
|
+
const scope = optionalPlanProjectScope(flags);
|
|
3177
|
+
return {
|
|
3178
|
+
method: "POST",
|
|
3179
|
+
endpoint: `${pluginRootEndpoint(id, "/settings")}${scope}`,
|
|
3180
|
+
identifiers: { pluginId: id, },
|
|
3181
|
+
payload: { configKeys: settingsConfigKeys(flags), },
|
|
3182
|
+
};
|
|
3183
|
+
}
|
|
3184
|
+
case "plugin.code-env-create": {
|
|
3185
|
+
const conda = parseBooleanOption(flags["conda"], "--conda") ?? false;
|
|
3186
|
+
return {
|
|
3187
|
+
method: "POST",
|
|
3188
|
+
endpoint: `${pluginRootEndpoint(id, "/code-env/actions/create")}`,
|
|
3189
|
+
identifiers: { pluginId: id, },
|
|
3190
|
+
payload: {
|
|
3191
|
+
deploymentMode: "PLUGIN_MANAGED",
|
|
3192
|
+
conda,
|
|
3193
|
+
pythonInterpreter: flags["python-interpreter"] ?? null,
|
|
3194
|
+
},
|
|
3195
|
+
wait: flags["wait"] === true,
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
case "plugin.code-env-update":
|
|
3199
|
+
return {
|
|
3200
|
+
method: "POST",
|
|
3201
|
+
endpoint: `${pluginRootEndpoint(id, "/code-env/actions/update")}`,
|
|
3202
|
+
identifiers: { pluginId: id, },
|
|
3203
|
+
wait: flags["wait"] === true,
|
|
3204
|
+
};
|
|
3205
|
+
case "plugin.move-to-dev":
|
|
3206
|
+
return {
|
|
3207
|
+
method: "POST",
|
|
3208
|
+
endpoint: pluginActionEndpoint(id, "moveToDev"),
|
|
3209
|
+
identifiers: { pluginId: id, },
|
|
3210
|
+
};
|
|
3211
|
+
case "plugin.delete":
|
|
3212
|
+
return {
|
|
3213
|
+
method: "POST",
|
|
3214
|
+
endpoint: pluginActionEndpoint(id, "delete"),
|
|
3215
|
+
identifiers: { pluginId: id, },
|
|
3216
|
+
payload: { force: parseBooleanOption(flags["force"], "--force") ?? false, },
|
|
3217
|
+
};
|
|
3218
|
+
case "plugin.create-dev": {
|
|
3219
|
+
const creationMode = requiredPlanFlag(flags, "creation-mode", entry.usage);
|
|
3220
|
+
const payload = {
|
|
3221
|
+
pluginId: id,
|
|
3222
|
+
creationMode,
|
|
3223
|
+
};
|
|
3224
|
+
if (typeof flags["repository"] === "string")
|
|
3225
|
+
payload.gitRepository = flags["repository"];
|
|
3226
|
+
if (typeof flags["checkout"] === "string")
|
|
3227
|
+
payload.gitCheckout = flags["checkout"];
|
|
3228
|
+
if (typeof flags["path-in-repository"] === "string") {
|
|
3229
|
+
payload.gitSubpath = flags["path-in-repository"];
|
|
3230
|
+
}
|
|
3231
|
+
return {
|
|
3232
|
+
method: "POST",
|
|
3233
|
+
endpoint: "/public/api/plugins/actions/createDev",
|
|
3234
|
+
identifiers: { pluginId: id, creationMode, },
|
|
3235
|
+
payload,
|
|
3236
|
+
};
|
|
3237
|
+
}
|
|
3238
|
+
case "plugin.set-git-remote": {
|
|
3239
|
+
const repository = requiredPlanFlag(flags, "repository", entry.usage);
|
|
3240
|
+
return {
|
|
3241
|
+
method: "POST",
|
|
3242
|
+
endpoint: `${pluginRootEndpoint(id, "/gitRemote")}`,
|
|
3243
|
+
identifiers: { pluginId: id, },
|
|
3244
|
+
payload: { repositoryUrl: validatedPlanRepositoryUrl(repository, "repository", entry.usage), },
|
|
3245
|
+
};
|
|
3246
|
+
}
|
|
3247
|
+
case "plugin.delete-git-remote":
|
|
3248
|
+
return {
|
|
3249
|
+
method: "DELETE",
|
|
3250
|
+
endpoint: pluginRootEndpoint(id, "/gitRemote"),
|
|
3251
|
+
identifiers: { pluginId: id, },
|
|
3252
|
+
};
|
|
3253
|
+
case "plugin.push":
|
|
3254
|
+
case "plugin.pull":
|
|
3255
|
+
case "plugin.fetch":
|
|
3256
|
+
case "plugin.reset-local":
|
|
3257
|
+
case "plugin.reset-remote": {
|
|
3258
|
+
const actionMap = {
|
|
3259
|
+
"plugin.push": "push",
|
|
3260
|
+
"plugin.pull": "pullRebase",
|
|
3261
|
+
"plugin.fetch": "fetch",
|
|
3262
|
+
"plugin.reset-local": "resetToLocalHeadState",
|
|
3263
|
+
"plugin.reset-remote": "resetToRemoteHeadState",
|
|
3264
|
+
};
|
|
3265
|
+
return {
|
|
3266
|
+
method: "POST",
|
|
3267
|
+
endpoint: pluginActionEndpoint(id, actionMap[`${resource}.${action}`]),
|
|
3268
|
+
identifiers: { pluginId: id, },
|
|
3269
|
+
};
|
|
3270
|
+
}
|
|
3271
|
+
case "plugin.contents-put":
|
|
3272
|
+
return {
|
|
3273
|
+
method: "POST",
|
|
3274
|
+
endpoint: pluginContentsEndpoint(id, args[1] ?? ""),
|
|
3275
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3276
|
+
payload: { contentSource: contentSourceKind(flags), },
|
|
3277
|
+
};
|
|
3278
|
+
case "plugin.contents-delete":
|
|
3279
|
+
return {
|
|
3280
|
+
method: "DELETE",
|
|
3281
|
+
endpoint: pluginContentsEndpoint(id, args[1] ?? ""),
|
|
3282
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3283
|
+
};
|
|
3284
|
+
case "plugin.folder-add":
|
|
3285
|
+
return {
|
|
3286
|
+
method: "POST",
|
|
3287
|
+
endpoint: pluginFoldersEndpoint(id, args[1] ?? ""),
|
|
3288
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3289
|
+
};
|
|
3290
|
+
case "plugin.rename": {
|
|
3291
|
+
const newName = requiredPlanPositionals(entry.usage)[2];
|
|
3292
|
+
if (!newName)
|
|
3293
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3294
|
+
return {
|
|
3295
|
+
method: "POST",
|
|
3296
|
+
endpoint: `${pluginRootEndpoint(id, "/contents-actions/rename")}`,
|
|
3297
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3298
|
+
payload: { oldPath: args[1], newName: args[2], },
|
|
3299
|
+
};
|
|
3300
|
+
}
|
|
3301
|
+
case "plugin.move": {
|
|
3302
|
+
const destination = args[2];
|
|
3303
|
+
if (!destination)
|
|
3304
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
3305
|
+
return {
|
|
3306
|
+
method: "POST",
|
|
3307
|
+
endpoint: `${pluginRootEndpoint(id, "/contents-actions/move")}`,
|
|
3308
|
+
identifiers: { pluginId: id, path: args[1], },
|
|
3309
|
+
payload: { oldPath: args[1], newPath: destination, },
|
|
3310
|
+
};
|
|
3311
|
+
}
|
|
2689
3312
|
case "business-app.save-settings":
|
|
2690
3313
|
return {
|
|
2691
3314
|
method: "PUT",
|
|
@@ -2725,6 +3348,42 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2725
3348
|
}
|
|
2726
3349
|
case "app.create-instance": {
|
|
2727
3350
|
const payload = requiredPlanJsonInput(flags, entry.usage);
|
|
3351
|
+
if (payload["targetProjectKey"] === undefined) {
|
|
3352
|
+
// Generated-at-apply mode: --plan never allocates or reserves a
|
|
3353
|
+
// random key. The payload stays exactly as supplied, the runtime
|
|
3354
|
+
// generates the key (and defaults the display name to it) during
|
|
3355
|
+
// apply, and no absence probe runs for a generated key, so the
|
|
3356
|
+
// plan carries no preflight requests and no fake concrete GET
|
|
3357
|
+
// path: `{targetProjectKey}` resolves at apply time.
|
|
3358
|
+
return {
|
|
3359
|
+
method: "POST",
|
|
3360
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
3361
|
+
identifiers: {
|
|
3362
|
+
appId: id,
|
|
3363
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
3364
|
+
...(payload["targetProjectName"] === undefined
|
|
3365
|
+
? { targetProjectNameGeneratedDuringApply: true, }
|
|
3366
|
+
: {}),
|
|
3367
|
+
preflightExecuted: false,
|
|
3368
|
+
preflightWillRunDuringApply: false,
|
|
3369
|
+
preflightRequests: [],
|
|
3370
|
+
incarnationControl: "client-side-non-atomic-future-target-and-creation-tag-join",
|
|
3371
|
+
incarnationObservationRequests: [
|
|
3372
|
+
{
|
|
3373
|
+
method: "GET",
|
|
3374
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3375
|
+
when: flags["wait"] === true
|
|
3376
|
+
? "after-terminal-future-target"
|
|
3377
|
+
: "conditional-inline-hasResult-target",
|
|
3378
|
+
intent: "After DSS reports inline or terminal creation success for the generated key, observe creationTag for later cleanup binding.",
|
|
3379
|
+
},
|
|
3380
|
+
],
|
|
3381
|
+
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.",
|
|
3382
|
+
},
|
|
3383
|
+
payload,
|
|
3384
|
+
wait: flags["wait"] === true,
|
|
3385
|
+
};
|
|
3386
|
+
}
|
|
2728
3387
|
const rawTargetProjectKey = stringField(payload, ["targetProjectKey",]);
|
|
2729
3388
|
if (!rawTargetProjectKey || rawTargetProjectKey.trim() === "") {
|
|
2730
3389
|
throw new UsageError("Instance creation payload must include a non-empty targetProjectKey.", "validation_failed", `Usage: ${entry.usage}`);
|
|
@@ -2817,13 +3476,110 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2817
3476
|
}
|
|
2818
3477
|
case "app.create-successor-instance": {
|
|
2819
3478
|
const sourceProjectKey = requiredPlanFlag(flags, "from", entry.usage);
|
|
2820
|
-
const targetProjectKey =
|
|
2821
|
-
if (sourceProjectKey === targetProjectKey) {
|
|
3479
|
+
const targetProjectKey = optionalPlanFlag(flags, "to", entry.usage);
|
|
3480
|
+
if (targetProjectKey !== undefined && sourceProjectKey === targetProjectKey) {
|
|
2822
3481
|
throw new UsageError("--from and --to must be different project keys.", "validation_failed", `Usage: ${entry.usage}`);
|
|
2823
3482
|
}
|
|
2824
3483
|
const targetProjectName = flags["name"];
|
|
2825
3484
|
const copyPermissions = parseBooleanOption(flags["copy-permissions"], "--copy-permissions")
|
|
2826
3485
|
?? false;
|
|
3486
|
+
if (targetProjectKey === undefined) {
|
|
3487
|
+
// Generated-at-apply mode: --plan never allocates a key and no
|
|
3488
|
+
// target exists yet, so the plan makes no target-absence claim
|
|
3489
|
+
// and advertises no target probe. The key is generated once
|
|
3490
|
+
// during apply, immediately before the single instance POST;
|
|
3491
|
+
// `{targetProjectKey}` resolves only after the terminal future
|
|
3492
|
+
// names it. Source and template gates still run before the POST.
|
|
3493
|
+
return {
|
|
3494
|
+
method: "POST",
|
|
3495
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
3496
|
+
identifiers: {
|
|
3497
|
+
appId: id,
|
|
3498
|
+
sourceProjectKey,
|
|
3499
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
3500
|
+
targetPreflight: "not-applicable-generated-key",
|
|
3501
|
+
preflightExecuted: false,
|
|
3502
|
+
preflightWillRunDuringApply: true,
|
|
3503
|
+
...(targetProjectName !== undefined
|
|
3504
|
+
? { targetProjectName, }
|
|
3505
|
+
: { targetProjectNameGeneratedDuringApply: true, }),
|
|
3506
|
+
copyPermissions,
|
|
3507
|
+
incarnationControl: "client-side-non-atomic-future-target-and-creation-tag-join",
|
|
3508
|
+
incarnationObservationRequests: [
|
|
3509
|
+
{
|
|
3510
|
+
method: "GET",
|
|
3511
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3512
|
+
when: "after-terminal-future-target",
|
|
3513
|
+
intent: "After the terminal future names the generated successor key, observe creationTag and bind later target checks and cleanup to that hash.",
|
|
3514
|
+
},
|
|
3515
|
+
],
|
|
3516
|
+
preflightRequests: [
|
|
3517
|
+
{
|
|
3518
|
+
method: "GET",
|
|
3519
|
+
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances/`,
|
|
3520
|
+
when: "before-create",
|
|
3521
|
+
intent: "Verify the --from project is a registered instance of the app.",
|
|
3522
|
+
},
|
|
3523
|
+
{
|
|
3524
|
+
method: "GET",
|
|
3525
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/app-manifest`,
|
|
3526
|
+
when: "before-create",
|
|
3527
|
+
intent: "Verify the --from project is an APP_INSTANCE project.",
|
|
3528
|
+
},
|
|
3529
|
+
],
|
|
3530
|
+
...(copyPermissions
|
|
3531
|
+
? {
|
|
3532
|
+
permissionConcurrencyControl: "client-side-non-atomic-stale-identity-and-hash-checks",
|
|
3533
|
+
permissionRequests: [
|
|
3534
|
+
{
|
|
3535
|
+
method: "GET",
|
|
3536
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/permissions`,
|
|
3537
|
+
intent: "Snapshot the predecessor instance ACL before creation.",
|
|
3538
|
+
},
|
|
3539
|
+
{
|
|
3540
|
+
method: "GET",
|
|
3541
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3542
|
+
intent: "Read the generated successor ACL to decide whether the copy is a no-op.",
|
|
3543
|
+
},
|
|
3544
|
+
{
|
|
3545
|
+
method: "GET",
|
|
3546
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3547
|
+
intent: "Recheck generated successor creationTag after reading its ACL; stop if the project key was reused.",
|
|
3548
|
+
},
|
|
3549
|
+
{
|
|
3550
|
+
method: "GET",
|
|
3551
|
+
endpoint: `/public/api/projects/${encodeURIComponent(sourceProjectKey)}/permissions`,
|
|
3552
|
+
intent: "Recheck the predecessor ACL immediately before the write and stop if its hash drifted.",
|
|
3553
|
+
},
|
|
3554
|
+
{
|
|
3555
|
+
method: "GET",
|
|
3556
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3557
|
+
intent: "Recheck generated successor creationTag immediately before the unconditional permission PUT.",
|
|
3558
|
+
},
|
|
3559
|
+
{
|
|
3560
|
+
method: "PUT",
|
|
3561
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3562
|
+
intent: "Apply the predecessor ACL snapshot to the generated successor instance.",
|
|
3563
|
+
},
|
|
3564
|
+
{
|
|
3565
|
+
method: "GET",
|
|
3566
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/permissions",
|
|
3567
|
+
intent: "Verify the generated successor ACL hash equals the predecessor snapshot hash.",
|
|
3568
|
+
},
|
|
3569
|
+
{
|
|
3570
|
+
method: "GET",
|
|
3571
|
+
endpointTemplate: "/public/api/projects/{targetProjectKey}/",
|
|
3572
|
+
intent: "Detect generated successor project-key reuse across the permission write and verification read.",
|
|
3573
|
+
},
|
|
3574
|
+
],
|
|
3575
|
+
}
|
|
3576
|
+
: {}),
|
|
3577
|
+
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.",
|
|
3578
|
+
},
|
|
3579
|
+
payload: targetProjectName !== undefined ? { targetProjectName, } : {},
|
|
3580
|
+
wait: true,
|
|
3581
|
+
};
|
|
3582
|
+
}
|
|
2827
3583
|
return {
|
|
2828
3584
|
method: "POST",
|
|
2829
3585
|
endpoint: `/public/api/apps/${encodeURIComponent(id)}/instances`,
|
|
@@ -4076,6 +4832,51 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
4076
4832
|
endpoint: projectGitFutureEndpoint(id),
|
|
4077
4833
|
identifiers: { jobId: id, },
|
|
4078
4834
|
};
|
|
4835
|
+
case "project-folder.settings-set": {
|
|
4836
|
+
const payload = requiredPlanJsonInput(flags, `--data, --data-file, or --stdin is required. Usage: ${entry.usage}`);
|
|
4837
|
+
return {
|
|
4838
|
+
method: "PUT",
|
|
4839
|
+
endpoint: `${projectFolderEndpoint(id)}/settings`,
|
|
4840
|
+
identifiers: { folderId: id, },
|
|
4841
|
+
payload,
|
|
4842
|
+
};
|
|
4843
|
+
}
|
|
4844
|
+
case "project-folder.move": {
|
|
4845
|
+
const destination = args[1];
|
|
4846
|
+
if (!destination)
|
|
4847
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
4848
|
+
return {
|
|
4849
|
+
method: "POST",
|
|
4850
|
+
endpoint: `${projectFolderEndpoint(id)}/move${querySuffix({ destination, })}`,
|
|
4851
|
+
identifiers: { folderId: id, destination, },
|
|
4852
|
+
};
|
|
4853
|
+
}
|
|
4854
|
+
case "project-folder.delete":
|
|
4855
|
+
return {
|
|
4856
|
+
method: "DELETE",
|
|
4857
|
+
endpoint: projectFolderEndpoint(id),
|
|
4858
|
+
identifiers: { folderId: id, },
|
|
4859
|
+
};
|
|
4860
|
+
case "project-folder.create-child": {
|
|
4861
|
+
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
4862
|
+
return {
|
|
4863
|
+
method: "POST",
|
|
4864
|
+
endpoint: `${projectFolderEndpoint(id)}/children${querySuffix({ name, })}`,
|
|
4865
|
+
identifiers: { parentFolderId: id, name, },
|
|
4866
|
+
};
|
|
4867
|
+
}
|
|
4868
|
+
case "project-folder.move-project": {
|
|
4869
|
+
const projectKeyArg = args[1];
|
|
4870
|
+
const destination = args[2];
|
|
4871
|
+
if (!projectKeyArg || !destination) {
|
|
4872
|
+
throw new UsageError(`Usage: ${entry.usage}`);
|
|
4873
|
+
}
|
|
4874
|
+
return {
|
|
4875
|
+
method: "POST",
|
|
4876
|
+
endpoint: `${projectFolderEndpoint(id)}/projects/${encodeURIComponent(projectKeyArg)}/move${querySuffix({ destination, })}`,
|
|
4877
|
+
identifiers: { folderId: id, projectKey: projectKeyArg, destination, },
|
|
4878
|
+
};
|
|
4879
|
+
}
|
|
4079
4880
|
default:
|
|
4080
4881
|
return {
|
|
4081
4882
|
exact: false,
|