dataiku-sdk 0.12.7 → 1.1.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 +93 -15
- package/bin/dss.js +21 -2
- package/dist/build-metadata.json +1 -0
- package/dist/packages/types/src/index.d.ts +128 -3
- package/dist/packages/types/src/index.js +106 -3
- package/dist/src/cli/coerce.d.ts +2 -1
- package/dist/src/cli/coerce.js +84 -26
- package/dist/src/cli/commands/app.js +6 -6
- package/dist/src/cli/commands/code.js +2 -2
- package/dist/src/cli/commands/data-quality.js +50 -8
- package/dist/src/cli/commands/dataset.d.ts +23 -0
- package/dist/src/cli/commands/dataset.js +197 -12
- package/dist/src/cli/commands/folder.js +5 -4
- package/dist/src/cli/commands/future.js +2 -2
- package/dist/src/cli/commands/index.js +2 -0
- package/dist/src/cli/commands/job.js +26 -13
- package/dist/src/cli/commands/notebook.js +1 -1
- package/dist/src/cli/commands/project-git.d.ts +2 -0
- package/dist/src/cli/commands/project-git.js +563 -0
- package/dist/src/cli/commands/project.js +118 -14
- package/dist/src/cli/commands/recipe.js +14 -17
- package/dist/src/cli/commands/scenario.js +4 -4
- package/dist/src/cli/contract.d.ts +6 -4
- package/dist/src/cli/contract.js +595 -68
- package/dist/src/cli/doctor.d.ts +12 -2
- package/dist/src/cli/doctor.js +50 -19
- package/dist/src/cli/flags.js +23 -2
- package/dist/src/cli/helpers/cleanup.js +30 -1
- package/dist/src/cli/helpers/dataset.js +1 -1
- package/dist/src/cli/output.d.ts +5 -2
- package/dist/src/cli/output.js +13 -12
- package/dist/src/cli/types.d.ts +2 -0
- package/dist/src/cli/usage.d.ts +1 -1
- package/dist/src/cli/usage.js +2 -1
- package/dist/src/cli/version.d.ts +48 -6
- package/dist/src/cli/version.js +177 -11
- package/dist/src/cli.js +94 -39
- package/dist/src/client.d.ts +31 -1
- package/dist/src/client.js +39 -7
- package/dist/src/errors.d.ts +1 -1
- package/dist/src/index.d.ts +6 -4
- package/dist/src/index.js +3 -1
- package/dist/src/resources/connections.d.ts +3 -1
- package/dist/src/resources/connections.js +82 -21
- package/dist/src/resources/datasets.d.ts +43 -0
- package/dist/src/resources/datasets.js +241 -30
- package/dist/src/resources/folders.js +7 -3
- package/dist/src/resources/project-git.d.ts +154 -0
- package/dist/src/resources/project-git.js +427 -0
- package/dist/src/resources/projects.d.ts +36 -3
- package/dist/src/resources/projects.js +127 -16
- package/dist/src/resources/recipes.js +4 -4
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/utils/project-archive.d.ts +32 -0
- package/dist/src/utils/project-archive.js +624 -0
- package/package.json +6 -5
- package/packages/types/dist/index.d.ts +128 -3
- package/packages/types/dist/index.js +106 -3
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +26 -19
package/dist/src/cli/contract.js
CHANGED
|
@@ -4,7 +4,7 @@ import { validateCredentials, } from "../auth.js";
|
|
|
4
4
|
import { getCredentialsPath, saveCredentials, } from "../config.js";
|
|
5
5
|
import { DataikuError, } from "../errors.js";
|
|
6
6
|
import { APP_MANIFEST_CONCURRENCY_CONTROL, } from "../resources/applications.js";
|
|
7
|
-
import { jobBuildTargetTypeFromFlags, json, jsonInput, num, parseBooleanOption, parseJsonObject, requiredJsonInput, rewritePairsFromFlags, schemaColumnsInput, stringField, textInput, } from "./coerce.js";
|
|
7
|
+
import { jobBuildTargetTypeFromFlags, json, jsonInput, num, parseBooleanOption, parseJsonObject, recipeInputDatasetsFromFlags, requiredJsonInput, rewritePairsFromFlags, schemaColumnsInput, stringField, textInput, } from "./coerce.js";
|
|
8
8
|
import { commands, } from "./commands/index.js";
|
|
9
9
|
import { dataikuEnvironmentEnabled, } from "./env.js";
|
|
10
10
|
import { BOOLEAN_FLAGS, FLAG_ALIASES, KNOWN_LONG_FLAGS, SHORT_FLAGS, } from "./flags.js";
|
|
@@ -151,7 +151,64 @@ const PROJECT_SCOPED_RESOURCES = new Set([
|
|
|
151
151
|
"saved-model",
|
|
152
152
|
"wiki",
|
|
153
153
|
]);
|
|
154
|
-
|
|
154
|
+
/**
|
|
155
|
+
* Project Git actions that only observe repository state. Everything else in the
|
|
156
|
+
* `project-git` resource is a mutation: the verb-shaped names (`fetch`, `pull`,
|
|
157
|
+
* `switch`, `commit`, `reset-*`, `revert-*`, `drop-and-rebuild`, `future-abort`)
|
|
158
|
+
* match none of the mutating-verb patterns and would otherwise fall through to
|
|
159
|
+
* the read default, advertising network and history writes as safe reads.
|
|
160
|
+
*/
|
|
161
|
+
const PROJECT_GIT_READ_ACTIONS = {
|
|
162
|
+
branches: true,
|
|
163
|
+
"current-branch": true,
|
|
164
|
+
diff: true,
|
|
165
|
+
"future-status": true,
|
|
166
|
+
"future-wait": true,
|
|
167
|
+
"get-remote": true,
|
|
168
|
+
"list-libraries": true,
|
|
169
|
+
log: true,
|
|
170
|
+
status: true,
|
|
171
|
+
tags: true,
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* Git mutations that destroy work rather than add to it: `drop-and-rebuild`
|
|
175
|
+
* discards the repository and rebuilds it from DSS, the resets and reverts throw
|
|
176
|
+
* away local commits or working state, `remove-library` can delete the checked
|
|
177
|
+
* out directory, and the branch/tag deletions can remove remote refs.
|
|
178
|
+
*/
|
|
179
|
+
const PROJECT_GIT_DESTRUCTIVE_ACTIONS = {
|
|
180
|
+
"delete-branch": true,
|
|
181
|
+
"delete-tag": true,
|
|
182
|
+
"drop-and-rebuild": true,
|
|
183
|
+
"future-abort": true,
|
|
184
|
+
"remove-library": true,
|
|
185
|
+
"reset-all-libraries": true,
|
|
186
|
+
"reset-library": true,
|
|
187
|
+
"reset-to-head": true,
|
|
188
|
+
"reset-to-upstream": true,
|
|
189
|
+
"revert-commit": true,
|
|
190
|
+
"revert-to-revision": true,
|
|
191
|
+
};
|
|
192
|
+
/** Project Git actions that hand back a DSS future (`{jobId}`) instead of a result. */
|
|
193
|
+
const PROJECT_GIT_FUTURE_ACTIONS = {
|
|
194
|
+
"add-library": true,
|
|
195
|
+
"future-abort": true,
|
|
196
|
+
"future-status": true,
|
|
197
|
+
"future-wait": true,
|
|
198
|
+
"push-all-libraries": true,
|
|
199
|
+
"push-library": true,
|
|
200
|
+
"reset-all-libraries": true,
|
|
201
|
+
"reset-library": true,
|
|
202
|
+
};
|
|
203
|
+
/** Git mutations that converge: replaying them lands on the same repository state. */
|
|
204
|
+
const PROJECT_GIT_CONVERGENT_ACTIONS = {
|
|
205
|
+
fetch: true,
|
|
206
|
+
"reset-all-libraries": true,
|
|
207
|
+
"reset-library": true,
|
|
208
|
+
"reset-to-head": true,
|
|
209
|
+
"reset-to-upstream": true,
|
|
210
|
+
};
|
|
211
|
+
const GLOBAL_AGENT_FLAGS = ["verbose", "fields",];
|
|
155
212
|
const AUTHENTICATED_AGENT_FLAGS = [
|
|
156
213
|
"url",
|
|
157
214
|
"api-key",
|
|
@@ -160,22 +217,24 @@ const AUTHENTICATED_AGENT_FLAGS = [
|
|
|
160
217
|
"insecure",
|
|
161
218
|
"ca-cert",
|
|
162
219
|
];
|
|
163
|
-
export const COMMANDS_USAGE = "dss commands run [--fields PATHS] [--
|
|
164
|
-
const COMMANDS_DESCRIPTION = "Print
|
|
220
|
+
export const COMMANDS_USAGE = "dss commands run [--fields PATHS] [--output PATH]";
|
|
221
|
+
const COMMANDS_DESCRIPTION = "Print a compact resource/action summary by default; use --fields for scoped command metadata or --output PATH to export the full registry without sending it through stdout.";
|
|
165
222
|
const COMMANDS_EXAMPLES = [
|
|
166
|
-
"dss commands run
|
|
167
|
-
"dss commands run --fields dataset
|
|
168
|
-
"dss commands run --fields dataset.create
|
|
223
|
+
"dss commands run",
|
|
224
|
+
"dss commands run --fields dataset",
|
|
225
|
+
"dss commands run --fields dataset.create",
|
|
226
|
+
"dss commands run --fields dataset.create.usage,dataset.create.description,dataset.create.flags,dataset.create.examples",
|
|
227
|
+
"dss commands run --output commands.json",
|
|
169
228
|
];
|
|
170
229
|
export const AGENT_CONTRACT_COMMAND = "dss agent contract";
|
|
171
|
-
export const AGENT_CONTRACT_USAGE = "dss agent contract [--fields PATHS]
|
|
230
|
+
export const AGENT_CONTRACT_USAGE = "dss agent contract [--fields PATHS]";
|
|
172
231
|
const AGENT_CONTRACT_DESCRIPTION = "Print the versioned JSON agent contract; scope bootstrap fields, use commands.actions to enumerate the surface, and read schemas only when needed.";
|
|
173
232
|
const AGENT_CONTRACT_EXAMPLES = [
|
|
174
|
-
"dss agent contract --fields protocol,agentContractVersion,cli,stdio,planning,compatibility
|
|
175
|
-
"dss agent contract --fields commands.actions
|
|
233
|
+
"dss agent contract --fields protocol,agentContractVersion,cli,stdio,planning,compatibility",
|
|
234
|
+
"dss agent contract --fields commands.actions",
|
|
176
235
|
];
|
|
177
236
|
const VERSION_USAGE = "dss version";
|
|
178
|
-
const VERSION_DESCRIPTION = "Print the CLI version and
|
|
237
|
+
const VERSION_DESCRIPTION = "Print the CLI version, checkout/build revisions, load source, runtime, and stale-build status as JSON.";
|
|
179
238
|
const VERSION_EXAMPLES = ["dss version", "dss --version",];
|
|
180
239
|
const INSTALL_SKILL_USAGE = "dss install-skill [--global] [--agent NAME] [--target PATH] [--list-agents] [--dry-run] [--plan]";
|
|
181
240
|
const INSTALL_SKILL_DESCRIPTION = "Install the dataiku-dss agent skill for detected coding agents.";
|
|
@@ -189,11 +248,11 @@ const CLEANUP_EXAMPLES = [
|
|
|
189
248
|
"dss cleanup --file cleanup.jsonl",
|
|
190
249
|
"dss cleanup --file cleanup.jsonl --apply",
|
|
191
250
|
];
|
|
192
|
-
const FIXTURES_USAGE = "dss fixtures [--
|
|
251
|
+
const FIXTURES_USAGE = "dss fixtures [--project-key KEY] [--allow-types CSV]";
|
|
193
252
|
const FIXTURES_DESCRIPTION = "Discover safe live-test fixtures for agent workflows.";
|
|
194
253
|
const FIXTURES_EXAMPLES = [
|
|
195
|
-
"dss fixtures
|
|
196
|
-
"dss fixtures --
|
|
254
|
+
"dss fixtures",
|
|
255
|
+
"dss fixtures --allow-types Filesystem,Inline",
|
|
197
256
|
];
|
|
198
257
|
const ALLOWED_CLEANUP_ACTIONS = new Set([
|
|
199
258
|
// Must mirror every cleanup.argv shape emitted by cleanupLedgerEntry().
|
|
@@ -211,6 +270,7 @@ const ALLOWED_CLEANUP_ACTIONS = new Set([
|
|
|
211
270
|
"code-env delete",
|
|
212
271
|
"folder delete",
|
|
213
272
|
"folder delete-file",
|
|
273
|
+
"project delete",
|
|
214
274
|
"app delete-instance",
|
|
215
275
|
]);
|
|
216
276
|
export function isAllowedCleanupAction(resource, action) {
|
|
@@ -395,7 +455,7 @@ function shortFlagPattern(flags) {
|
|
|
395
455
|
* between `dss` and the first positional. Synthetic single-action commands
|
|
396
456
|
* whose usage omits their action token (e.g. `dss batch ...`, `dss version`)
|
|
397
457
|
* use an actionless prefix, while real command paths that spell `run` in their
|
|
398
|
-
* usage (e.g. `dss commands run
|
|
458
|
+
* usage (e.g. `dss commands run`) keep the action token.
|
|
399
459
|
*/
|
|
400
460
|
function argvPrefix(resource, action, usage) {
|
|
401
461
|
const usageTokens = usage.split(/\s+/).filter((token) => token.length > 0);
|
|
@@ -481,18 +541,8 @@ function argvJsonSchema(resource, action, flags, usage, requiredFlags, requiredO
|
|
|
481
541
|
},
|
|
482
542
|
};
|
|
483
543
|
}
|
|
484
|
-
function unsafeOutputs(resource, action,
|
|
544
|
+
function unsafeOutputs(resource, action, producesLocalFile) {
|
|
485
545
|
const outputs = [];
|
|
486
|
-
if (flags.some((flag) => flag.name === "raw")) {
|
|
487
|
-
outputs.push({
|
|
488
|
-
condition: "--raw without --output",
|
|
489
|
-
kind: "raw-stdout",
|
|
490
|
-
detail: `${resource} ${action} can intentionally write raw payload bytes/text instead of JSON.`,
|
|
491
|
-
safeAlternative: flags.some((flag) => flag.name === "output")
|
|
492
|
-
? "Pass --output PATH so stdout remains a JSON string containing the path."
|
|
493
|
-
: "Omit --raw when a JSON stdout value is required.",
|
|
494
|
-
});
|
|
495
|
-
}
|
|
496
546
|
if (producesLocalFile) {
|
|
497
547
|
const sensitivePermissions = resource === "app" && action === "permissions-snapshot";
|
|
498
548
|
outputs.push({
|
|
@@ -589,6 +639,13 @@ function inferSideEffect(resource, action) {
|
|
|
589
639
|
return "read";
|
|
590
640
|
if (resource === "data-quality" && action === "compute")
|
|
591
641
|
return "write";
|
|
642
|
+
// Project Git is classified from the explicit read table, not by verb shape:
|
|
643
|
+
// `switch`, `fetch`, `pull`, `push`, `commit`, the `reset-*`/`revert-*`
|
|
644
|
+
// families, `drop-and-rebuild`, and `future-abort` all mutate repository or
|
|
645
|
+
// future state yet match none of the mutating-verb patterns.
|
|
646
|
+
if (resource === "project-git") {
|
|
647
|
+
return PROJECT_GIT_READ_ACTIONS[action] === true ? "read" : "write";
|
|
648
|
+
}
|
|
592
649
|
if (READ_ACTIONS.has(action))
|
|
593
650
|
return "read";
|
|
594
651
|
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)/
|
|
@@ -606,18 +663,21 @@ function inferRequiresAuth(resource) {
|
|
|
606
663
|
&& resource !== "install-skill"
|
|
607
664
|
&& resource !== "version";
|
|
608
665
|
}
|
|
609
|
-
export function inferRequiresProject(resource,
|
|
666
|
+
export function inferRequiresProject(resource, action, usage) {
|
|
610
667
|
if (resource === "agent" || resource === "auth" || resource === "doctor" || resource === "commands"
|
|
611
668
|
|| resource === "install-skill" || resource === "version") {
|
|
612
669
|
return false;
|
|
613
670
|
}
|
|
614
671
|
if (PROJECT_SCOPED_RESOURCES.has(resource))
|
|
615
672
|
return true;
|
|
673
|
+
if (resource === "project-git")
|
|
674
|
+
return !action.startsWith("future-");
|
|
616
675
|
return usage.includes("--project-key");
|
|
617
676
|
}
|
|
618
677
|
const ARRAY_OUTPUT_ACTIONS = new Set([
|
|
619
678
|
"history",
|
|
620
679
|
"find",
|
|
680
|
+
"files",
|
|
621
681
|
"infer",
|
|
622
682
|
"last-results",
|
|
623
683
|
"list",
|
|
@@ -639,6 +699,13 @@ const STRING_OUTPUT_ACTIONS = new Set([
|
|
|
639
699
|
"log-url",
|
|
640
700
|
]);
|
|
641
701
|
function inferOutputShape(resource, action) {
|
|
702
|
+
if (resource === "project-git") {
|
|
703
|
+
if (action === "branches" || action === "tags" || action === "list-libraries")
|
|
704
|
+
return "array";
|
|
705
|
+
if (action === "set-library")
|
|
706
|
+
return "string";
|
|
707
|
+
return "object";
|
|
708
|
+
}
|
|
642
709
|
if (resource === "agent" || resource === "auth" || resource === "commands"
|
|
643
710
|
|| resource === "install-skill"
|
|
644
711
|
|| resource === "version") {
|
|
@@ -796,6 +863,9 @@ function inferPayloadSchema(inputContract) {
|
|
|
796
863
|
function inferExitCodes(resource, action, asyncKind) {
|
|
797
864
|
const longRunning = asyncKind !== "none" || `${resource}.${action}` === "batch.run";
|
|
798
865
|
const assertion = `${resource}.${action}` === "recipe.assert-unchanged"
|
|
866
|
+
|| `${resource}.${action}` === "dataset.assert-count"
|
|
867
|
+
|| `${resource}.${action}` === "dataset.assert-schema"
|
|
868
|
+
|| `${resource}.${action}` === "data-quality.assert-results"
|
|
799
869
|
|| `${resource}.${action}` === "batch.run";
|
|
800
870
|
return {
|
|
801
871
|
ok: 0,
|
|
@@ -807,6 +877,9 @@ function inferExitCodes(resource, action, asyncKind) {
|
|
|
807
877
|
};
|
|
808
878
|
}
|
|
809
879
|
function cleanupCommandFromDeleteUsage(resource, action) {
|
|
880
|
+
if (`${resource}.${action}` === "project.import") {
|
|
881
|
+
return "dss project delete <used-project-key> --if-exists --expect-project-incarnation <hash>";
|
|
882
|
+
}
|
|
810
883
|
if (resource === "project" && (action === "create" || action === "duplicate")) {
|
|
811
884
|
return undefined;
|
|
812
885
|
}
|
|
@@ -830,9 +903,23 @@ export function supportsCleanupLedger(resource, action) {
|
|
|
830
903
|
return cleanupCommandFromDeleteUsage(resource, action) !== undefined
|
|
831
904
|
|| `${resource}.${action}` === "folder.upload";
|
|
832
905
|
}
|
|
833
|
-
|
|
906
|
+
/**
|
|
907
|
+
* Write actions that no public DSS route can undo. `dataset upload-file` adds a
|
|
908
|
+
* file to the dataset's uploaded-files set, and DSS exposes no endpoint to
|
|
909
|
+
* replace or delete an individual uploaded file, so the write is permanent and
|
|
910
|
+
* no cleanup-ledger entry can recover the previous file set.
|
|
911
|
+
*/
|
|
912
|
+
const EXPLICIT_DESTRUCTIVE_KEYS = {
|
|
913
|
+
"dataset.upload-file": true,
|
|
914
|
+
};
|
|
915
|
+
function inferDestructiveLevel(resource, sideEffect, action) {
|
|
834
916
|
if (sideEffect !== "write")
|
|
835
917
|
return "none";
|
|
918
|
+
if (EXPLICIT_DESTRUCTIVE_KEYS[`${resource}.${action}`] === true)
|
|
919
|
+
return "destructive";
|
|
920
|
+
if (resource === "project-git") {
|
|
921
|
+
return PROJECT_GIT_DESTRUCTIVE_ACTIONS[action] === true ? "destructive" : "reversible";
|
|
922
|
+
}
|
|
836
923
|
if (/^(delete|abort|clear|unload|logout)/.test(action))
|
|
837
924
|
return "destructive";
|
|
838
925
|
return "reversible";
|
|
@@ -850,6 +937,10 @@ function inferAsyncKind(resource, action) {
|
|
|
850
937
|
}
|
|
851
938
|
if (resource === "data-quality" && action === "compute")
|
|
852
939
|
return "future";
|
|
940
|
+
// Project Git mutation results carry a future job id only for the library
|
|
941
|
+
// calls and the future lifecycle itself; plain Git actions settle inline.
|
|
942
|
+
if (resource === "project-git" && PROJECT_GIT_FUTURE_ACTIONS[action] === true)
|
|
943
|
+
return "future";
|
|
853
944
|
if (resource === "code" && action === "run")
|
|
854
945
|
return "future";
|
|
855
946
|
if (resource === "app"
|
|
@@ -872,6 +963,11 @@ function inferIdempotency(resource, sideEffect, action, usage) {
|
|
|
872
963
|
// error, so replaying the delete cannot fail on absence alone.
|
|
873
964
|
if (`${resource}.${action}` === "app.delete-instance")
|
|
874
965
|
return "convergent";
|
|
966
|
+
// Git fetch and the repository resets converge: replaying them lands on the
|
|
967
|
+
// same repository state instead of failing on an already-applied change.
|
|
968
|
+
if (resource === "project-git" && PROJECT_GIT_CONVERGENT_ACTIONS[action] === true) {
|
|
969
|
+
return "convergent";
|
|
970
|
+
}
|
|
875
971
|
if (/^(clear|refresh|set|save)/.test(action))
|
|
876
972
|
return "convergent";
|
|
877
973
|
return "none";
|
|
@@ -890,6 +986,10 @@ const LEDGER_ONLY_CLEANUP_KEYS = {
|
|
|
890
986
|
};
|
|
891
987
|
const LEDGER_ONLY_CLEANUP_HINT = "Pass `--record-cleanup PATH` when creating, then recover with `dss cleanup --file PATH --apply`. Do not issue direct instance deletion as cleanup: it bypasses the creation-future gate and can delete the project while DSS is still creating it.";
|
|
892
988
|
export function inferCleanupHint(resource, action) {
|
|
989
|
+
// Git mutations are not cleanup-ledger operations: no project-git branch or
|
|
990
|
+
// tag create has a generic `delete` command to close it out.
|
|
991
|
+
if (resource === "project-git")
|
|
992
|
+
return undefined;
|
|
893
993
|
if (!(action.startsWith("create") || action === "clone"))
|
|
894
994
|
return undefined;
|
|
895
995
|
if (LEDGER_ONLY_CLEANUP_KEYS[`${resource}.${action}`] === true) {
|
|
@@ -911,10 +1011,10 @@ export function inferCleanupHint(resource, action) {
|
|
|
911
1011
|
return `Delete with \`dss ${resource} delete <id>${ifExists}\` when the created object is disposable.`;
|
|
912
1012
|
}
|
|
913
1013
|
function buildRegistryEntry(resource, action, meta) {
|
|
914
|
-
const requiresAuth = inferRequiresAuth(resource);
|
|
1014
|
+
const requiresAuth = meta.localHandler === undefined && inferRequiresAuth(resource);
|
|
915
1015
|
const requiresProject = inferRequiresProject(resource, action, meta.usage);
|
|
916
1016
|
const sideEffect = inferSideEffect(resource, action);
|
|
917
|
-
const destructive = inferDestructiveLevel(sideEffect, action);
|
|
1017
|
+
const destructive = inferDestructiveLevel(resource, sideEffect, action);
|
|
918
1018
|
const asyncKind = inferAsyncKind(resource, action);
|
|
919
1019
|
const mutatesDss = sideEffect === "write" && resource !== "auth" && resource !== "install-skill";
|
|
920
1020
|
const supportsPlan = mutatesDss || sideEffect === "write";
|
|
@@ -980,7 +1080,7 @@ function buildRegistryEntry(resource, action, meta) {
|
|
|
980
1080
|
|| meta.usage.includes("--output-file PATH");
|
|
981
1081
|
const uniqueRequiredFlags = uniqueStrings(requiredFlags);
|
|
982
1082
|
const uniqueOptionalFlags = uniqueStrings(optionalFlags);
|
|
983
|
-
const unsafe = unsafeOutputs(resource, action,
|
|
1083
|
+
const unsafe = unsafeOutputs(resource, action, producesLocalFile);
|
|
984
1084
|
return {
|
|
985
1085
|
resource,
|
|
986
1086
|
action,
|
|
@@ -1229,7 +1329,7 @@ export function agentContractJsonSchema() {
|
|
|
1229
1329
|
},
|
|
1230
1330
|
};
|
|
1231
1331
|
}
|
|
1232
|
-
function commandActionSummary(registry) {
|
|
1332
|
+
export function commandActionSummary(registry) {
|
|
1233
1333
|
const summary = {};
|
|
1234
1334
|
for (const [resource, actions,] of Object.entries(registry)) {
|
|
1235
1335
|
summary[resource] = Object.keys(actions).sort();
|
|
@@ -1244,15 +1344,14 @@ export function buildAgentContract() {
|
|
|
1244
1344
|
cli: cliVersionResult(),
|
|
1245
1345
|
commands: {
|
|
1246
1346
|
discoveryCommand: "dss commands run",
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
compactOutputHint: "Pass --json to emit compact single-line JSON and reduce agent context usage.",
|
|
1347
|
+
fullRegistryExportCommand: "dss commands run --output PATH",
|
|
1348
|
+
scopedDiscoveryCommand: "dss commands run --fields RESOURCE[.ACTION[.FIELD...]]",
|
|
1349
|
+
actionIndexCommand: "dss agent contract --fields commands.actions",
|
|
1251
1350
|
scopedDiscoveryExamples: [
|
|
1252
|
-
"dss commands run --fields dataset
|
|
1253
|
-
"dss commands run --fields dataset.create
|
|
1351
|
+
"dss commands run --fields dataset",
|
|
1352
|
+
"dss commands run --fields dataset.create",
|
|
1254
1353
|
],
|
|
1255
|
-
scopedDiscoveryHint: "--fields RESOURCE returns every action of one resource; --fields RESOURCE.ACTION returns a single registry entry keyed by the dotted path; append .FIELD paths to project nested metadata. Comma-separate paths to select several.",
|
|
1354
|
+
scopedDiscoveryHint: "`dss commands run` returns the resource/action summary. --fields RESOURCE returns every action of one resource; --fields RESOURCE.ACTION returns a single registry entry keyed by the dotted path; append .FIELD paths to project nested metadata. Comma-separate paths to select several. Use --output PATH to export the full registry.",
|
|
1256
1355
|
actions: commandActionSummary(registry),
|
|
1257
1356
|
},
|
|
1258
1357
|
schemas: {
|
|
@@ -1265,28 +1364,22 @@ export function buildAgentContract() {
|
|
|
1265
1364
|
},
|
|
1266
1365
|
stdio: {
|
|
1267
1366
|
stdout: {
|
|
1367
|
+
format: "compact-json",
|
|
1268
1368
|
success: "single-json-value",
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
"recipe cat --raw without --output",
|
|
1272
|
-
],
|
|
1369
|
+
failure: "structured-error-object",
|
|
1370
|
+
richFailureResults: "doctor/batch/cleanup nonzero outcomes are their own compact result on stdout ({ok:false,...}) with the command's exit code; not re-wrapped.",
|
|
1273
1371
|
},
|
|
1274
1372
|
stderr: {
|
|
1275
1373
|
format: "jsonl",
|
|
1276
|
-
events: ["warning", "trace",
|
|
1277
|
-
error: "single-final-error-event-on-nonzero-exit",
|
|
1278
|
-
failureRouting: {
|
|
1279
|
-
commandFailure: "doctor/batch/cleanup failures: single JSON stdout; empty stderr",
|
|
1280
|
-
dispatchFailure: "usage/unknown/transport/internal failures: empty stdout; single JSON error stderr",
|
|
1281
|
-
},
|
|
1374
|
+
events: ["warning", "trace",],
|
|
1282
1375
|
},
|
|
1283
1376
|
},
|
|
1284
1377
|
planning: {
|
|
1285
1378
|
discoveryCommand: "dss commands run",
|
|
1286
1379
|
contractCommand: AGENT_CONTRACT_COMMAND,
|
|
1287
|
-
bootstrapCommand: "dss agent contract --fields protocol,agentContractVersion,cli,stdio,planning,compatibility
|
|
1288
|
-
preferredDiscoveryCommand: "dss commands run --fields RESOURCE.ACTION
|
|
1289
|
-
actionIndexCommand: "dss agent contract --fields commands.actions
|
|
1380
|
+
bootstrapCommand: "dss agent contract --fields protocol,agentContractVersion,cli,stdio,planning,compatibility",
|
|
1381
|
+
preferredDiscoveryCommand: "dss commands run --fields RESOURCE.ACTION",
|
|
1382
|
+
actionIndexCommand: "dss agent contract --fields commands.actions",
|
|
1290
1383
|
mutatingCommandsAdvertisePlan: true,
|
|
1291
1384
|
},
|
|
1292
1385
|
compatibility: {
|
|
@@ -1375,6 +1468,67 @@ function querySuffix(params) {
|
|
|
1375
1468
|
const raw = search.toString();
|
|
1376
1469
|
return raw ? `?${raw}` : "";
|
|
1377
1470
|
}
|
|
1471
|
+
/**
|
|
1472
|
+
* Git and future API mount. The official Python client mounts these routes on
|
|
1473
|
+
* `/dip/publicapi`, which is also what the project-git resource uses; do not
|
|
1474
|
+
* normalize them to the repo-wide `/public/api`.
|
|
1475
|
+
*/
|
|
1476
|
+
const PROJECT_GIT_API_ROOT = "/dip/publicapi";
|
|
1477
|
+
function projectGitEndpoint(projectKey, suffix) {
|
|
1478
|
+
if (!projectKey)
|
|
1479
|
+
throw new UsageError("--project-key is required for project-git mutations.");
|
|
1480
|
+
return `${PROJECT_GIT_API_ROOT}/projects/${encodeURIComponent(projectKey)}/git${suffix}`;
|
|
1481
|
+
}
|
|
1482
|
+
function projectGitFutureEndpoint(jobId) {
|
|
1483
|
+
return `${PROJECT_GIT_API_ROOT}/futures/${encodeURIComponent(jobId)}`;
|
|
1484
|
+
}
|
|
1485
|
+
/** Encode a library reference path per segment, mirroring the resource helper. */
|
|
1486
|
+
function encodeGitReferencePath(pathValue) {
|
|
1487
|
+
const normalized = pathValue.replace(/^\/+/, "").replace(/\/+$/, "");
|
|
1488
|
+
return normalized.split("/").map((segment) => encodeURIComponent(segment)).join("/");
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* `--plan` bypasses the SDK boundary guard, so HTTP(S) URLs with embedded
|
|
1492
|
+
* userinfo are rejected here before any plan is printed. SSH/scp-style URLs
|
|
1493
|
+
* (`git@host:org/repo.git`, `ssh://...`) remain valid.
|
|
1494
|
+
*/
|
|
1495
|
+
function validatedPlanRepositoryUrl(url, flag, usage) {
|
|
1496
|
+
const candidate = url.trim();
|
|
1497
|
+
if (/[\u0000-\u001f\u007f]/u.test(candidate)) {
|
|
1498
|
+
throw new UsageError(`--${flag} must not contain control characters. Usage: ${usage}`);
|
|
1499
|
+
}
|
|
1500
|
+
if (/^https?:/i.test(candidate)) {
|
|
1501
|
+
if (candidate.includes("\\")) {
|
|
1502
|
+
throw new UsageError(`--${flag} must be a valid HTTP(S) URL. Usage: ${usage}`);
|
|
1503
|
+
}
|
|
1504
|
+
if (!/^https?:\/\//i.test(candidate)) {
|
|
1505
|
+
throw new UsageError(`--${flag} must be a valid HTTP(S) URL. Usage: ${usage}`);
|
|
1506
|
+
}
|
|
1507
|
+
let parsed;
|
|
1508
|
+
try {
|
|
1509
|
+
parsed = new URL(candidate);
|
|
1510
|
+
}
|
|
1511
|
+
catch {
|
|
1512
|
+
throw new UsageError(`--${flag} must be a valid HTTP(S) URL. Usage: ${usage}`);
|
|
1513
|
+
}
|
|
1514
|
+
if (parsed.username !== "" || parsed.password !== "") {
|
|
1515
|
+
throw new UsageError(`--${flag} must not contain embedded credentials (userinfo). Usage: ${usage}`);
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
return candidate;
|
|
1519
|
+
}
|
|
1520
|
+
function requiredPlanRepositoryUrl(flags, flag, usage) {
|
|
1521
|
+
const url = requiredPlanFlag(flags, flag, usage);
|
|
1522
|
+
return validatedPlanRepositoryUrl(url, flag, usage);
|
|
1523
|
+
}
|
|
1524
|
+
/** Wait procedure advertised for the library calls that return a job id. */
|
|
1525
|
+
function projectGitFutureWait() {
|
|
1526
|
+
return {
|
|
1527
|
+
when: "after-dispatch",
|
|
1528
|
+
endpoint: `${PROJECT_GIT_API_ROOT}/futures/{jobId}?peek=false`,
|
|
1529
|
+
description: "Poll the returned job id until the future reports a result; never abort it.",
|
|
1530
|
+
};
|
|
1531
|
+
}
|
|
1378
1532
|
function jobBuildPayload(target, projectKey, flags) {
|
|
1379
1533
|
const targetType = jobBuildTargetTypeFromFlags(flags);
|
|
1380
1534
|
const partition = flags["partition"];
|
|
@@ -1609,8 +1763,11 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
1609
1763
|
};
|
|
1610
1764
|
case "dataset.create": {
|
|
1611
1765
|
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
1612
|
-
const connection =
|
|
1766
|
+
const connection = flags["connection"];
|
|
1613
1767
|
const dsType = requiredPlanFlag(flags, "type", entry.usage);
|
|
1768
|
+
if (!connection && dsType.toLowerCase() !== "uploadedfiles") {
|
|
1769
|
+
throw new UsageError("--connection is required unless --type is UploadedFiles.");
|
|
1770
|
+
}
|
|
1614
1771
|
return {
|
|
1615
1772
|
method: "POST",
|
|
1616
1773
|
endpoint: projectEndpoint("/datasets/"),
|
|
@@ -1643,6 +1800,38 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
1643
1800
|
identifiers: { oldName: args[0], newName: args[1], },
|
|
1644
1801
|
payload: { oldName: args[0], newName: args[1], },
|
|
1645
1802
|
};
|
|
1803
|
+
case "dataset.upload-file": {
|
|
1804
|
+
const fileName = requiredPlanFlag(flags, "file-name", entry.usage);
|
|
1805
|
+
const datasetEndpoint = projectEndpoint(`/datasets/${encodeURIComponent(args[0])}`);
|
|
1806
|
+
const filesEndpoint = `${datasetEndpoint}/uploaded/files`;
|
|
1807
|
+
const upload = {
|
|
1808
|
+
method: "POST",
|
|
1809
|
+
endpoint: filesEndpoint,
|
|
1810
|
+
payload: { ...uploadPayload(args[1]), fileName, },
|
|
1811
|
+
};
|
|
1812
|
+
return {
|
|
1813
|
+
...upload,
|
|
1814
|
+
identifiers: {
|
|
1815
|
+
datasetName: args[0],
|
|
1816
|
+
localPath: args[1],
|
|
1817
|
+
fileName,
|
|
1818
|
+
},
|
|
1819
|
+
requests: [
|
|
1820
|
+
{ sequence: 1, method: "GET", endpoint: datasetEndpoint, },
|
|
1821
|
+
{ sequence: 2, method: "GET", endpoint: filesEndpoint, },
|
|
1822
|
+
{ sequence: 3, ...upload, },
|
|
1823
|
+
{
|
|
1824
|
+
sequence: 4,
|
|
1825
|
+
method: "GET",
|
|
1826
|
+
endpoint: filesEndpoint,
|
|
1827
|
+
assert: {
|
|
1828
|
+
filename: fileName,
|
|
1829
|
+
length: "local file byte length",
|
|
1830
|
+
},
|
|
1831
|
+
},
|
|
1832
|
+
],
|
|
1833
|
+
};
|
|
1834
|
+
}
|
|
1646
1835
|
case "dataset.delete":
|
|
1647
1836
|
return {
|
|
1648
1837
|
method: "DELETE",
|
|
@@ -1740,7 +1929,7 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
1740
1929
|
payload: {
|
|
1741
1930
|
type,
|
|
1742
1931
|
name: flags["name"],
|
|
1743
|
-
inputDatasets: flags
|
|
1932
|
+
inputDatasets: recipeInputDatasetsFromFlags(flags),
|
|
1744
1933
|
outputDataset,
|
|
1745
1934
|
outputFolder,
|
|
1746
1935
|
outputConnection: flags["output-connection"],
|
|
@@ -1840,8 +2029,8 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
1840
2029
|
};
|
|
1841
2030
|
case "folder.create": {
|
|
1842
2031
|
const name = requiredPlanFlag(flags, "name", entry.usage);
|
|
1843
|
-
const type =
|
|
1844
|
-
const connection =
|
|
2032
|
+
const type = flags["type"];
|
|
2033
|
+
const connection = flags["connection"];
|
|
1845
2034
|
return {
|
|
1846
2035
|
method: "POST",
|
|
1847
2036
|
endpoint: projectEndpoint("/managedfolders/"),
|
|
@@ -2655,7 +2844,7 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2655
2844
|
method: "PUT",
|
|
2656
2845
|
endpoint: projectEndpoint(`/sql-notebooks/${encodeURIComponent(id)}/history`),
|
|
2657
2846
|
identifiers: { id, cellId: flags["cell-id"], },
|
|
2658
|
-
payload: { retain: num(flags["retain"]), },
|
|
2847
|
+
payload: { retain: num(flags["retain"], "--retain"), },
|
|
2659
2848
|
};
|
|
2660
2849
|
case "project.create": {
|
|
2661
2850
|
const settings = jsonInput(flags) ?? null;
|
|
@@ -2674,17 +2863,74 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2674
2863
|
},
|
|
2675
2864
|
};
|
|
2676
2865
|
}
|
|
2677
|
-
case "project.delete":
|
|
2866
|
+
case "project.delete": {
|
|
2867
|
+
const expectedProjectIncarnation = flags["expect-project-incarnation"] === undefined
|
|
2868
|
+
? undefined
|
|
2869
|
+
: requiredPlanFlag(flags, "expect-project-incarnation", entry.usage);
|
|
2870
|
+
if (expectedProjectIncarnation !== undefined
|
|
2871
|
+
&& !/^[0-9a-f]{64}$/.test(expectedProjectIncarnation)) {
|
|
2872
|
+
throw new UsageError("--expect-project-incarnation must be a 64-character lowercase SHA-256 hash.", "validation_failed");
|
|
2873
|
+
}
|
|
2874
|
+
const endpoint = `/public/api/projects/${encodeURIComponent(id)}${querySuffix({
|
|
2875
|
+
clearManagedDatasets: flags["drop-data"] === true,
|
|
2876
|
+
clearOutputManagedFolders: false,
|
|
2877
|
+
clearJobAndScenarioLogs: true,
|
|
2878
|
+
wait: true,
|
|
2879
|
+
})}`;
|
|
2880
|
+
const guarded = flags["if-exists"] === true
|
|
2881
|
+
|| flags["dry-run"] === true
|
|
2882
|
+
|| expectedProjectIncarnation !== undefined;
|
|
2883
|
+
if (!guarded) {
|
|
2884
|
+
return {
|
|
2885
|
+
method: "DELETE",
|
|
2886
|
+
endpoint,
|
|
2887
|
+
identifiers: { projectKey: id, },
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
const projectProbe = `/public/api/projects/${encodeURIComponent(id)}/`;
|
|
2891
|
+
const incarnationGate = expectedProjectIncarnation === undefined
|
|
2892
|
+
? { required: false, provided: false, }
|
|
2893
|
+
: {
|
|
2894
|
+
required: true,
|
|
2895
|
+
provided: true,
|
|
2896
|
+
expectedHash: expectedProjectIncarnation,
|
|
2897
|
+
};
|
|
2898
|
+
if (flags["dry-run"] === true) {
|
|
2899
|
+
return {
|
|
2900
|
+
method: "GET",
|
|
2901
|
+
endpoint: projectProbe,
|
|
2902
|
+
identifiers: {
|
|
2903
|
+
projectKey: id,
|
|
2904
|
+
dryRun: true,
|
|
2905
|
+
ifExists: flags["if-exists"] === true,
|
|
2906
|
+
projectIncarnationGate: incarnationGate,
|
|
2907
|
+
note: "Read-only guarded-delete preflight. No DELETE is issued; a supplied creationTag hash must match.",
|
|
2908
|
+
},
|
|
2909
|
+
};
|
|
2910
|
+
}
|
|
2678
2911
|
return {
|
|
2679
2912
|
method: "DELETE",
|
|
2680
|
-
endpoint
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2913
|
+
endpoint,
|
|
2914
|
+
identifiers: {
|
|
2915
|
+
projectKey: id,
|
|
2916
|
+
ifExists: flags["if-exists"] === true,
|
|
2917
|
+
projectIncarnationGate: incarnationGate,
|
|
2918
|
+
incarnationControl: expectedProjectIncarnation === undefined
|
|
2919
|
+
? "none"
|
|
2920
|
+
: "client-side-non-atomic-stale-identity-check",
|
|
2921
|
+
preflightRequests: [{
|
|
2922
|
+
method: "GET",
|
|
2923
|
+
endpoint: projectProbe,
|
|
2924
|
+
intent: expectedProjectIncarnation === undefined
|
|
2925
|
+
? "Confirm existence before a convergent delete."
|
|
2926
|
+
: "Recompute the current project-incarnation hash from creationTag before DELETE.",
|
|
2927
|
+
},],
|
|
2928
|
+
note: expectedProjectIncarnation === undefined
|
|
2929
|
+
? "Convergent delete: a 404 preflight is an already-absent success only with --if-exists."
|
|
2930
|
+
: "Incarnation-bound delete: the GET must match the expected creationTag hash before an unconditional DELETE. DSS exposes no conditional project DELETE, so the client-side gate cannot serialize against key reuse after the GET.",
|
|
2931
|
+
},
|
|
2687
2932
|
};
|
|
2933
|
+
}
|
|
2688
2934
|
case "project.duplicate": {
|
|
2689
2935
|
const options = jsonInput(flags);
|
|
2690
2936
|
return {
|
|
@@ -2706,11 +2952,32 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2706
2952
|
},
|
|
2707
2953
|
};
|
|
2708
2954
|
}
|
|
2709
|
-
case "project.import":
|
|
2710
|
-
|
|
2955
|
+
case "project.import": {
|
|
2956
|
+
const settings = jsonInput(flags) ?? {};
|
|
2957
|
+
const rawTarget = flags["target-project-key"];
|
|
2958
|
+
const targetProjectKey = rawTarget?.trim();
|
|
2959
|
+
if (rawTarget !== undefined && targetProjectKey === "") {
|
|
2960
|
+
throw new UsageError(`--target-project-key must not be empty. Usage: ${entry.usage}`);
|
|
2961
|
+
}
|
|
2962
|
+
const settingsTarget = settings.targetProjectKey;
|
|
2963
|
+
if (settingsTarget !== undefined
|
|
2964
|
+
&& (typeof settingsTarget !== "string" || settingsTarget.trim() === "")) {
|
|
2965
|
+
throw new UsageError(`targetProjectKey in import settings must be a non-empty string. Usage: ${entry.usage}`);
|
|
2966
|
+
}
|
|
2967
|
+
if (targetProjectKey !== undefined
|
|
2968
|
+
&& settingsTarget !== undefined
|
|
2969
|
+
&& targetProjectKey !== settingsTarget.trim()) {
|
|
2970
|
+
throw new UsageError(`--target-project-key conflicts with targetProjectKey in import settings. Usage: ${entry.usage}`);
|
|
2971
|
+
}
|
|
2972
|
+
const processPayload = targetProjectKey === undefined
|
|
2973
|
+
? settings
|
|
2974
|
+
: { ...settings, targetProjectKey, };
|
|
2975
|
+
const finalizedPayload = Object.keys(processPayload).length === 0
|
|
2976
|
+
? { _: "_", }
|
|
2977
|
+
: processPayload;
|
|
2978
|
+
const upload = {
|
|
2711
2979
|
method: "POST",
|
|
2712
2980
|
endpoint: "/public/api/projects/import/upload",
|
|
2713
|
-
identifiers: { filePath: id, },
|
|
2714
2981
|
payload: {
|
|
2715
2982
|
contentType: "multipart/form-data",
|
|
2716
2983
|
fileField: "file",
|
|
@@ -2718,6 +2985,46 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2718
2985
|
fileName: "tmp-import.zip",
|
|
2719
2986
|
},
|
|
2720
2987
|
};
|
|
2988
|
+
return {
|
|
2989
|
+
...upload,
|
|
2990
|
+
identifiers: {
|
|
2991
|
+
filePath: id,
|
|
2992
|
+
...(targetProjectKey ? { targetProjectKey, } : {}),
|
|
2993
|
+
archivePreflight: {
|
|
2994
|
+
local: true,
|
|
2995
|
+
required: true,
|
|
2996
|
+
checks: [
|
|
2997
|
+
"zip-integrity",
|
|
2998
|
+
"safe-unique-members",
|
|
2999
|
+
"manifest",
|
|
3000
|
+
"flow-references",
|
|
3001
|
+
"orphan-members",
|
|
3002
|
+
],
|
|
3003
|
+
},
|
|
3004
|
+
successVerification: {
|
|
3005
|
+
usedProjectKeyRequired: true,
|
|
3006
|
+
projectReadRequired: true,
|
|
3007
|
+
projectIncarnationRequired: true,
|
|
3008
|
+
remappingReported: true,
|
|
3009
|
+
},
|
|
3010
|
+
...(flags["record-cleanup"] === undefined
|
|
3011
|
+
? {}
|
|
3012
|
+
: {
|
|
3013
|
+
cleanupBinding: "actual used project key plus verified creationTag incarnation hash",
|
|
3014
|
+
}),
|
|
3015
|
+
},
|
|
3016
|
+
requests: [
|
|
3017
|
+
{ sequence: 1, ...upload, },
|
|
3018
|
+
{
|
|
3019
|
+
sequence: 2,
|
|
3020
|
+
method: "POST",
|
|
3021
|
+
endpoint: "/public/api/projects/import/{importId}/process",
|
|
3022
|
+
pathBindings: { importId: "requests[0].response.id", },
|
|
3023
|
+
payload: finalizedPayload,
|
|
3024
|
+
},
|
|
3025
|
+
],
|
|
3026
|
+
};
|
|
3027
|
+
}
|
|
2721
3028
|
case "project.settings-set":
|
|
2722
3029
|
return {
|
|
2723
3030
|
method: "PUT",
|
|
@@ -2755,6 +3062,226 @@ export function commandPlanShape(resource, action, args, flags, entry, projectKe
|
|
|
2755
3062
|
endpoint: projectEndpoint("/flow/zones"),
|
|
2756
3063
|
payload: jsonInput(flags),
|
|
2757
3064
|
};
|
|
3065
|
+
/* ---- project-git: mutations only; reads are never planned ---- */
|
|
3066
|
+
case "project-git.set-remote": {
|
|
3067
|
+
const remote = flags["name"] ?? "origin";
|
|
3068
|
+
return {
|
|
3069
|
+
method: "POST",
|
|
3070
|
+
endpoint: projectGitEndpoint(projectKey, `/remotes/${encodeURIComponent(remote)}`),
|
|
3071
|
+
identifiers: { remote, },
|
|
3072
|
+
payload: { url: requiredPlanRepositoryUrl(flags, "repository", entry.usage), },
|
|
3073
|
+
};
|
|
3074
|
+
}
|
|
3075
|
+
case "project-git.remove-remote": {
|
|
3076
|
+
const remote = flags["name"] ?? "origin";
|
|
3077
|
+
return {
|
|
3078
|
+
method: "DELETE",
|
|
3079
|
+
endpoint: projectGitEndpoint(projectKey, `/remotes/${encodeURIComponent(remote)}`),
|
|
3080
|
+
identifiers: { remote, },
|
|
3081
|
+
};
|
|
3082
|
+
}
|
|
3083
|
+
case "project-git.create-branch":
|
|
3084
|
+
return {
|
|
3085
|
+
method: "POST",
|
|
3086
|
+
endpoint: projectGitEndpoint(projectKey, "/branches/"),
|
|
3087
|
+
identifiers: { name: id, },
|
|
3088
|
+
payload: {
|
|
3089
|
+
name: id,
|
|
3090
|
+
commit: flags["commit"] ?? null,
|
|
3091
|
+
duplicateProject: parseBooleanOption(flags["duplicate-project"], "--duplicate-project") ?? false,
|
|
3092
|
+
targetProjectKey: flags["target-project-key"] ?? null,
|
|
3093
|
+
targetProjectFolderId: flags["target-project-folder-id"] ?? null,
|
|
3094
|
+
},
|
|
3095
|
+
};
|
|
3096
|
+
case "project-git.delete-branch": {
|
|
3097
|
+
const remote = parseBooleanOption(flags["remote"], "--remote") ?? false;
|
|
3098
|
+
return {
|
|
3099
|
+
method: "POST",
|
|
3100
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/deleteBranch"),
|
|
3101
|
+
identifiers: { name: id, remote, },
|
|
3102
|
+
payload: {
|
|
3103
|
+
name: id,
|
|
3104
|
+
remote,
|
|
3105
|
+
deleteRemotely: parseBooleanOption(flags["delete-remotely"], "--delete-remotely") ?? false,
|
|
3106
|
+
forceDelete: parseBooleanOption(flags["force-delete"], "--force-delete") ?? false,
|
|
3107
|
+
},
|
|
3108
|
+
};
|
|
3109
|
+
}
|
|
3110
|
+
case "project-git.switch":
|
|
3111
|
+
return {
|
|
3112
|
+
method: "POST",
|
|
3113
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/switchBranch${querySuffix({ branchName: id, })}`),
|
|
3114
|
+
identifiers: { branch: id, },
|
|
3115
|
+
};
|
|
3116
|
+
case "project-git.create-tag":
|
|
3117
|
+
return {
|
|
3118
|
+
method: "POST",
|
|
3119
|
+
endpoint: projectGitEndpoint(projectKey, "/tags/"),
|
|
3120
|
+
identifiers: { name: id, },
|
|
3121
|
+
payload: {
|
|
3122
|
+
name: id,
|
|
3123
|
+
reference: flags["reference"] ?? "HEAD",
|
|
3124
|
+
message: flags["message"] ?? "",
|
|
3125
|
+
},
|
|
3126
|
+
};
|
|
3127
|
+
case "project-git.delete-tag":
|
|
3128
|
+
return {
|
|
3129
|
+
method: "POST",
|
|
3130
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/deleteTag"),
|
|
3131
|
+
identifiers: { name: id, },
|
|
3132
|
+
payload: { name: id, },
|
|
3133
|
+
};
|
|
3134
|
+
case "project-git.fetch":
|
|
3135
|
+
return {
|
|
3136
|
+
method: "POST",
|
|
3137
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/fetch"),
|
|
3138
|
+
};
|
|
3139
|
+
case "project-git.pull":
|
|
3140
|
+
return {
|
|
3141
|
+
method: "POST",
|
|
3142
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/pullRebase${querySuffix({
|
|
3143
|
+
branchName: flags["branch"],
|
|
3144
|
+
})}`),
|
|
3145
|
+
...(flags["branch"] !== undefined
|
|
3146
|
+
? { identifiers: { branch: flags["branch"], }, }
|
|
3147
|
+
: {}),
|
|
3148
|
+
};
|
|
3149
|
+
case "project-git.push":
|
|
3150
|
+
return {
|
|
3151
|
+
method: "POST",
|
|
3152
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/push${querySuffix({
|
|
3153
|
+
branchName: flags["branch"],
|
|
3154
|
+
})}`),
|
|
3155
|
+
...(flags["branch"] !== undefined
|
|
3156
|
+
? { identifiers: { branch: flags["branch"], }, }
|
|
3157
|
+
: {}),
|
|
3158
|
+
};
|
|
3159
|
+
case "project-git.commit": {
|
|
3160
|
+
const message = requiredPlanFlag(flags, "message", entry.usage);
|
|
3161
|
+
return {
|
|
3162
|
+
method: "POST",
|
|
3163
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/commit"),
|
|
3164
|
+
identifiers: { message, },
|
|
3165
|
+
payload: { message, },
|
|
3166
|
+
};
|
|
3167
|
+
}
|
|
3168
|
+
case "project-git.revert-to-revision":
|
|
3169
|
+
return {
|
|
3170
|
+
method: "POST",
|
|
3171
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/revertToRevision${querySuffix({ commit: id, })}`),
|
|
3172
|
+
identifiers: { commit: id, },
|
|
3173
|
+
};
|
|
3174
|
+
case "project-git.revert-commit":
|
|
3175
|
+
return {
|
|
3176
|
+
method: "POST",
|
|
3177
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/revertCommit${querySuffix({ commit: id, })}`),
|
|
3178
|
+
identifiers: { commit: id, },
|
|
3179
|
+
};
|
|
3180
|
+
case "project-git.reset-to-head":
|
|
3181
|
+
return {
|
|
3182
|
+
method: "POST",
|
|
3183
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/resetToLocalHeadState"),
|
|
3184
|
+
};
|
|
3185
|
+
case "project-git.reset-to-upstream":
|
|
3186
|
+
return {
|
|
3187
|
+
method: "POST",
|
|
3188
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/resetToRemoteHeadState"),
|
|
3189
|
+
};
|
|
3190
|
+
case "project-git.drop-and-rebuild": {
|
|
3191
|
+
if (parseBooleanOption(flags["i-know-what-i-am-doing"], "--i-know-what-i-am-doing") !== true) {
|
|
3192
|
+
throw new UsageError(`--i-know-what-i-am-doing is required to acknowledge the irreversible Git history loss. Usage: ${entry.usage}`);
|
|
3193
|
+
}
|
|
3194
|
+
return {
|
|
3195
|
+
method: "POST",
|
|
3196
|
+
endpoint: projectGitEndpoint(projectKey, `/actions/dropAndRebuild${querySuffix({ iKnowWhatIAmDoing: true, })}`),
|
|
3197
|
+
};
|
|
3198
|
+
}
|
|
3199
|
+
case "project-git.add-library": {
|
|
3200
|
+
const targetPath = id;
|
|
3201
|
+
return {
|
|
3202
|
+
method: "POST",
|
|
3203
|
+
endpoint: projectGitEndpoint(projectKey, "/lib-git-refs/"),
|
|
3204
|
+
identifiers: { localTargetPath: targetPath, },
|
|
3205
|
+
payload: {
|
|
3206
|
+
repository: requiredPlanRepositoryUrl(flags, "repository", entry.usage),
|
|
3207
|
+
login: flags["login"] ?? null,
|
|
3208
|
+
password: flags["password-env"] !== undefined ? "***" : null,
|
|
3209
|
+
pathInGitRepository: flags["path-in-repository"] ?? "",
|
|
3210
|
+
localTargetPath: targetPath,
|
|
3211
|
+
checkout: requiredPlanFlag(flags, "checkout", entry.usage),
|
|
3212
|
+
addToPythonPath: parseBooleanOption(flags["no-add-to-python-path"], "--no-add-to-python-path") !== true,
|
|
3213
|
+
},
|
|
3214
|
+
wait: projectGitFutureWait(),
|
|
3215
|
+
};
|
|
3216
|
+
}
|
|
3217
|
+
case "project-git.set-library": {
|
|
3218
|
+
const targetPath = id;
|
|
3219
|
+
return {
|
|
3220
|
+
method: "PUT",
|
|
3221
|
+
endpoint: projectGitEndpoint(projectKey, `/lib-git-refs/${encodeGitReferencePath(targetPath)}`),
|
|
3222
|
+
identifiers: { library: targetPath, },
|
|
3223
|
+
payload: {
|
|
3224
|
+
repository: requiredPlanRepositoryUrl(flags, "repository", entry.usage),
|
|
3225
|
+
login: flags["login"] ?? null,
|
|
3226
|
+
password: flags["password-env"] !== undefined ? "***" : null,
|
|
3227
|
+
pathInGitRepository: flags["path-in-repository"] ?? "",
|
|
3228
|
+
checkout: requiredPlanFlag(flags, "checkout", entry.usage),
|
|
3229
|
+
},
|
|
3230
|
+
};
|
|
3231
|
+
}
|
|
3232
|
+
case "project-git.remove-library": {
|
|
3233
|
+
const targetPath = id;
|
|
3234
|
+
return {
|
|
3235
|
+
method: "DELETE",
|
|
3236
|
+
endpoint: projectGitEndpoint(projectKey, `/lib-git-refs/${encodeGitReferencePath(targetPath)}${querySuffix({
|
|
3237
|
+
deleteDirectory: parseBooleanOption(flags["delete-directory"], "--delete-directory") ?? false,
|
|
3238
|
+
})}`),
|
|
3239
|
+
identifiers: { library: targetPath, },
|
|
3240
|
+
};
|
|
3241
|
+
}
|
|
3242
|
+
case "project-git.reset-library": {
|
|
3243
|
+
const targetPath = id;
|
|
3244
|
+
return {
|
|
3245
|
+
method: "POST",
|
|
3246
|
+
endpoint: projectGitEndpoint(projectKey, "/lib-git-refs/action/reset"),
|
|
3247
|
+
identifiers: { library: targetPath, },
|
|
3248
|
+
payload: { gitRef: targetPath, },
|
|
3249
|
+
wait: projectGitFutureWait(),
|
|
3250
|
+
};
|
|
3251
|
+
}
|
|
3252
|
+
case "project-git.push-library": {
|
|
3253
|
+
const targetPath = id;
|
|
3254
|
+
const message = requiredPlanFlag(flags, "message", entry.usage);
|
|
3255
|
+
return {
|
|
3256
|
+
method: "POST",
|
|
3257
|
+
endpoint: projectGitEndpoint(projectKey, "/lib-git-refs/action/push"),
|
|
3258
|
+
identifiers: { library: targetPath, message, },
|
|
3259
|
+
payload: { gitRef: targetPath, commitMessage: message, },
|
|
3260
|
+
wait: projectGitFutureWait(),
|
|
3261
|
+
};
|
|
3262
|
+
}
|
|
3263
|
+
case "project-git.push-all-libraries": {
|
|
3264
|
+
const message = requiredPlanFlag(flags, "message", entry.usage);
|
|
3265
|
+
return {
|
|
3266
|
+
method: "POST",
|
|
3267
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/git-refs/push-all"),
|
|
3268
|
+
identifiers: { message, },
|
|
3269
|
+
payload: { commitMessage: message, },
|
|
3270
|
+
wait: projectGitFutureWait(),
|
|
3271
|
+
};
|
|
3272
|
+
}
|
|
3273
|
+
case "project-git.reset-all-libraries":
|
|
3274
|
+
return {
|
|
3275
|
+
method: "POST",
|
|
3276
|
+
endpoint: projectGitEndpoint(projectKey, "/actions/git-refs/reset-all"),
|
|
3277
|
+
wait: projectGitFutureWait(),
|
|
3278
|
+
};
|
|
3279
|
+
case "project-git.future-abort":
|
|
3280
|
+
return {
|
|
3281
|
+
method: "DELETE",
|
|
3282
|
+
endpoint: projectGitFutureEndpoint(id),
|
|
3283
|
+
identifiers: { jobId: id, },
|
|
3284
|
+
};
|
|
2758
3285
|
default:
|
|
2759
3286
|
return {
|
|
2760
3287
|
method: action.startsWith("delete") || action === "abort" ? "DELETE" : "POST",
|