dataiku-sdk 3.8.0 → 4.0.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 +7 -5
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +4 -0
- package/dist/packages/types/src/index.js +4 -0
- package/dist/src/cli/coerce.d.ts +6 -0
- package/dist/src/cli/coerce.js +9 -0
- package/dist/src/cli/command-syntax.json +36 -0
- package/dist/src/cli/commands/app.js +2 -2
- package/dist/src/cli/commands/code-env.js +5 -6
- package/dist/src/cli/commands/code.js +16 -2
- package/dist/src/cli/commands/dashboard.js +1 -2
- package/dist/src/cli/commands/dataset.js +2 -2
- package/dist/src/cli/commands/flow-zone.js +1 -1
- package/dist/src/cli/commands/job.js +101 -22
- package/dist/src/cli/commands/plugin.js +7 -7
- package/dist/src/cli/commands/project-library.js +2 -2
- package/dist/src/cli/commands/project.js +25 -7
- package/dist/src/cli/commands/recipe.js +11 -19
- package/dist/src/cli/commands/scenario.js +2 -4
- package/dist/src/cli/contract.js +29 -10
- package/dist/src/cli/env.js +2 -3
- package/dist/src/cli/helpers/app-permissions.d.ts +1 -1
- package/dist/src/cli/helpers/app-permissions.js +2 -2
- package/dist/src/cli/helpers/cleanup.d.ts +2 -0
- package/dist/src/cli/helpers/cleanup.js +122 -8
- package/dist/src/cli/helpers/flow-zone.d.ts +1 -1
- package/dist/src/cli/helpers/flow-zone.js +3 -4
- package/dist/src/cli/list-output.d.ts +5 -0
- package/dist/src/cli/list-output.js +11 -2
- package/dist/src/cli/list-shapes.d.ts +6 -0
- package/dist/src/cli/list-shapes.js +7 -1
- package/dist/src/cli/output-schemas.d.ts +1 -1
- package/dist/src/cli/output-schemas.js +28 -4
- package/dist/src/cli/output.js +7 -0
- package/dist/src/cli/project-map-output.d.ts +24 -0
- package/dist/src/cli/project-map-output.js +21 -0
- package/dist/src/cli/read-shapes.d.ts +14 -0
- package/dist/src/cli/read-shapes.js +114 -0
- package/dist/src/cli/result-shapes.d.ts +6 -0
- package/dist/src/cli/result-shapes.js +235 -0
- package/dist/src/cli/version.d.ts +2 -2
- package/dist/src/cli/version.js +4 -5
- package/dist/src/cli.js +77 -33
- package/dist/src/client.d.ts +14 -2
- package/dist/src/client.js +18 -10
- package/dist/src/generated/action-output-schemas.json +242 -35
- package/dist/src/index.d.ts +1 -1
- package/dist/src/resources/datasets.d.ts +11 -1
- package/dist/src/resources/datasets.js +91 -30
- package/dist/src/resources/jobs.d.ts +37 -6
- package/dist/src/resources/jobs.js +110 -44
- package/dist/src/resources/projects.js +2 -2
- package/dist/src/resources/recipes.d.ts +12 -1
- package/dist/src/resources/recipes.js +39 -12
- package/dist/src/resources/scenarios.d.ts +8 -1
- package/dist/src/resources/scenarios.js +51 -12
- package/dist/src/resources/wiki.js +3 -7
- package/dist/src/skill.js +1 -2
- package/dist/src/utils/project-archive.d.ts +25 -0
- package/dist/src/utils/project-archive.js +216 -5
- package/dist/src/utils/response-file.js +15 -8
- package/dist/src/utils/secret-sanitize.d.ts +11 -2
- package/dist/src/utils/secret-sanitize.js +23 -6
- package/package.json +13 -14
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +1 -1
- package/skills/dataiku-dss/references/discovery.md +2 -2
- package/skills/dataiku-dss/references/troubleshooting.md +2 -2
|
@@ -4,10 +4,13 @@ import { inspectProjectArchive, } from "../../utils/project-archive.js";
|
|
|
4
4
|
import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
|
|
5
5
|
import { jsonInput, num, requiredJsonInput, requiredStringFlag, } from "../coerce.js";
|
|
6
6
|
import { executionMode, } from "../flags.js";
|
|
7
|
-
import { CommandResultFailure, } from "../output.js";
|
|
7
|
+
import { CommandResultFailure, enqueueCliWarning, } from "../output.js";
|
|
8
|
+
import { compactProjectMapOutput, } from "../project-map-output.js";
|
|
8
9
|
import { commandUsage, withUsage, } from "../syntax.js";
|
|
9
10
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
10
11
|
const INSPECT_ARCHIVE_USAGE = commandUsage("project", "inspect-archive");
|
|
12
|
+
/** Maximum credential inventory entries echoed in the export warning. */
|
|
13
|
+
const EXPORT_CREDENTIAL_INVENTORY_LIMIT = 100;
|
|
11
14
|
function flowRenderFormat(value) {
|
|
12
15
|
if (value === undefined)
|
|
13
16
|
return undefined;
|
|
@@ -28,7 +31,7 @@ export const projectCommands = withUsage("project", {
|
|
|
28
31
|
},
|
|
29
32
|
get: {
|
|
30
33
|
handler: (c, _a, f) => c.projects.get(f["project-key"]),
|
|
31
|
-
description: "Get project settings and metadata.",
|
|
34
|
+
description: "Get project settings and metadata (compact by default: empty values, metrics/version/image metadata dropped, permission flags collapsed to deniedPermissions; --full returns the raw DSS object).",
|
|
32
35
|
examples: ["dss project get", "dss project get --project-key MYPROJ",],
|
|
33
36
|
},
|
|
34
37
|
metadata: {
|
|
@@ -42,13 +45,13 @@ export const projectCommands = withUsage("project", {
|
|
|
42
45
|
examples: ["dss project flow", "dss project flow --project-key MYPROJ",],
|
|
43
46
|
},
|
|
44
47
|
map: {
|
|
45
|
-
handler: (c, _a, f) => c.projects.map({
|
|
48
|
+
handler: async (c, _a, f) => compactProjectMapOutput(await c.projects.map({
|
|
46
49
|
projectKey: f["project-key"],
|
|
47
50
|
maxNodes: num(f["max-nodes"], "--max-nodes"),
|
|
48
51
|
maxEdges: num(f["max-edges"], "--max-edges"),
|
|
49
52
|
includeRaw: f["include-raw"] === true,
|
|
50
53
|
render: flowRenderFormat(f["render"]),
|
|
51
|
-
}),
|
|
54
|
+
})),
|
|
52
55
|
validate: (_a, f) => {
|
|
53
56
|
flowRenderFormat(f["render"]);
|
|
54
57
|
num(f["max-nodes"], "--max-nodes");
|
|
@@ -161,9 +164,24 @@ export const projectCommands = withUsage("project", {
|
|
|
161
164
|
if (!out)
|
|
162
165
|
throw new UsageError("--output PATH is required.", "missing_required_flag");
|
|
163
166
|
await c.projects.exportArchive(a[0], out, jsonInput(f));
|
|
164
|
-
|
|
167
|
+
const inspection = await inspectProjectArchive(out);
|
|
168
|
+
const findings = inspection.credentialFindings;
|
|
169
|
+
if (findings.length > 0) {
|
|
170
|
+
enqueueCliWarning({
|
|
171
|
+
code: "export_contains_credentials",
|
|
172
|
+
message: `The exported archive '${out}' contains ${findings.length} credential-bearing structure(s) `
|
|
173
|
+
+ "(member paths and key names only; values are never printed). "
|
|
174
|
+
+ "DSS exports credentials, API keys and secret-named variables verbatim.",
|
|
175
|
+
hint: "Sanitize the archive before sharing or uploading it. Overwriting an object in versioned storage does not erase its retained versions: "
|
|
176
|
+
+ "once an unsanitized archive is uploaded, rotate the exposed credentials.",
|
|
177
|
+
total: findings.length,
|
|
178
|
+
shown: Math.min(findings.length, EXPORT_CREDENTIAL_INVENTORY_LIMIT),
|
|
179
|
+
findings: findings.slice(0, EXPORT_CREDENTIAL_INVENTORY_LIMIT),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
return { path: out, credentialFindings: findings.length, };
|
|
165
183
|
},
|
|
166
|
-
description: "Export a project to a local archive (.zip).",
|
|
184
|
+
description: "Export a project to a local archive (.zip). The written archive is inspected locally; credential-bearing structures (apikeys.json, secret-named variables, apiKey/token/password fields) raise an export_contains_credentials warning listing member paths and key names only, and the result reports credentialFindings. The archive is not redacted.",
|
|
167
185
|
examples: ["dss project export MY_PROJ --output ./my_proj.zip",],
|
|
168
186
|
},
|
|
169
187
|
import: {
|
|
@@ -201,7 +219,7 @@ export const projectCommands = withUsage("project", {
|
|
|
201
219
|
"inspect-archive": {
|
|
202
220
|
handler: async (_c, a) => inspectArchiveCommand(a),
|
|
203
221
|
localHandler: inspectArchiveCommand,
|
|
204
|
-
description: "Read-only local inspection of a project export archive: member names, sizes, manifest validity, archive issues, and the source project key. Never contacts DSS.",
|
|
222
|
+
description: "Read-only local inspection of a project export archive: member names, sizes, manifest validity, archive issues (including credential_structure and residue_scan_skipped warnings), a credentialFindings inventory of credential-bearing member paths and key names (values are never reported), and the source project key. Never contacts DSS.",
|
|
205
223
|
examples: ["dss project inspect-archive ./my_proj.zip",],
|
|
206
224
|
},
|
|
207
225
|
"permissions-set": {
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { readFileSync, } from "node:fs";
|
|
2
|
-
import { writeFile, } from "node:fs/promises";
|
|
3
1
|
import { join, } from "node:path";
|
|
4
2
|
import { ClientValidationError, DataikuError, } from "../../errors.js";
|
|
5
3
|
import { recipeCreateWarnings, recipeOutputSchemaIsComputable, schemaUpdateComputableId, schemaUpdateIsPending, } from "../../resources/recipe-create.js";
|
|
6
|
-
import {
|
|
7
|
-
import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredJsonInput, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
|
|
4
|
+
import { buildRecipeUpdate, } from "../../resources/recipes.js";
|
|
5
|
+
import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, readTextFile, recipeInputDatasetsFromFlags, requiredJsonInput, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
|
|
8
6
|
import { executionMode, } from "../flags.js";
|
|
9
7
|
import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
|
|
10
8
|
import { ensureRecipeBackupDir, readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, writeRecipeBackup, } from "../helpers/recipe.js";
|
|
@@ -12,9 +10,9 @@ import { encodedProjectEndpoint, enqueueCliWarning, readIfExists, skipResult, }
|
|
|
12
10
|
import { commandUsage, withUsage, } from "../syntax.js";
|
|
13
11
|
import { requireArgs, UsageError, } from "../usage.js";
|
|
14
12
|
/** Read a local recipe file before any DSS request, mapping read failures to usage validation. */
|
|
15
|
-
function readRecipeFile(filePath, flagName) {
|
|
13
|
+
async function readRecipeFile(filePath, flagName) {
|
|
16
14
|
try {
|
|
17
|
-
return
|
|
15
|
+
return await readTextFile(filePath);
|
|
18
16
|
}
|
|
19
17
|
catch (error) {
|
|
20
18
|
const reason = error instanceof Error ? error.message : String(error);
|
|
@@ -507,7 +505,7 @@ export const recipeCommands = withUsage("recipe", {
|
|
|
507
505
|
throw new UsageError(`--file is required. Usage: ${commandUsage("recipe", "diff")}`);
|
|
508
506
|
}
|
|
509
507
|
// Read and validate the local file before any DSS request.
|
|
510
|
-
const localContent = readRecipeFile(filePath, "--file");
|
|
508
|
+
const localContent = await readRecipeFile(filePath, "--file");
|
|
511
509
|
const result = await c.recipes.get(a[0], {
|
|
512
510
|
includePayload: true,
|
|
513
511
|
projectKey: f["project-key"],
|
|
@@ -530,23 +528,17 @@ export const recipeCommands = withUsage("recipe", {
|
|
|
530
528
|
const pk = f["project-key"];
|
|
531
529
|
if (executionMode(f).dryRun) {
|
|
532
530
|
const current = await c.recipes.get(a[0], { projectKey: pk, includePayload: true, });
|
|
533
|
-
const
|
|
534
|
-
const next = {
|
|
535
|
-
...current,
|
|
536
|
-
...data,
|
|
537
|
-
recipe: deepMerge(currentRecipe, (data.recipe && typeof data.recipe === "object" && !Array.isArray(data.recipe))
|
|
538
|
-
? data.recipe
|
|
539
|
-
: {}),
|
|
540
|
-
};
|
|
531
|
+
const next = buildRecipeUpdate(current, data);
|
|
541
532
|
return { dryRun: true, action: "update", resource: "recipe", name: a[0], current, next, };
|
|
542
533
|
}
|
|
543
534
|
await c.recipes.update(a[0], data, pk);
|
|
544
535
|
return { updated: a[0], resource: "recipe", };
|
|
545
536
|
},
|
|
546
|
-
description: "Update recipe settings via JSON merge. Recipe definition fields must be nested under a top-level recipe key.",
|
|
537
|
+
description: "Update recipe settings via JSON merge. Recipe definition fields must be nested under a top-level recipe key. recipe.params.envSelection, when present, replaces the existing envSelection object (include envMode; no stale envName is kept) and is only accepted under recipe.params.",
|
|
547
538
|
examples: [
|
|
548
539
|
"dss recipe update compute_orders --data-file settings.json --dry-run",
|
|
549
|
-
'dss recipe update compute_orders --data \'{"recipe":{"params":{"envSelection":{"envMode":"EXPLICIT_ENV","envName":"
|
|
540
|
+
'dss recipe update compute_orders --data \'{"recipe":{"params":{"envSelection":{"envMode":"EXPLICIT_ENV","envName":"py311_env"}}}}\'',
|
|
541
|
+
'dss recipe update compute_orders --data \'{"recipe":{"params":{"envSelection":{"envMode":"INHERIT"}}}}\'',
|
|
550
542
|
"cat settings.json | dss recipe update compute_orders --stdin",
|
|
551
543
|
],
|
|
552
544
|
},
|
|
@@ -636,7 +628,7 @@ export const recipeCommands = withUsage("recipe", {
|
|
|
636
628
|
projectKey: f["project-key"],
|
|
637
629
|
});
|
|
638
630
|
if (typeof f["output"] === "string") {
|
|
639
|
-
await
|
|
631
|
+
await Bun.write(f["output"], payload);
|
|
640
632
|
return f["output"];
|
|
641
633
|
}
|
|
642
634
|
return payload;
|
|
@@ -663,7 +655,7 @@ export const recipeCommands = withUsage("recipe", {
|
|
|
663
655
|
const filePath = f["file"];
|
|
664
656
|
if (!filePath)
|
|
665
657
|
throw new UsageError("--file is required.");
|
|
666
|
-
const content = readRecipeFile(filePath, "--file");
|
|
658
|
+
const content = await readRecipeFile(filePath, "--file");
|
|
667
659
|
const pk = f["project-key"];
|
|
668
660
|
const shouldBackup = f["no-backup"] !== true;
|
|
669
661
|
const backupDir = shouldBackup
|
|
@@ -233,11 +233,9 @@ export const scenarioCommands = withUsage("scenario", {
|
|
|
233
233
|
const outputFile = f["output"]
|
|
234
234
|
?? f["output-file"];
|
|
235
235
|
if (outputFile) {
|
|
236
|
-
const {
|
|
237
|
-
const { dirname, resolve, } = await import("node:path");
|
|
236
|
+
const { resolve, } = await import("node:path");
|
|
238
237
|
const outputPath = resolve(outputFile);
|
|
239
|
-
await
|
|
240
|
-
await writeFile(outputPath, result.text, "utf-8");
|
|
238
|
+
await Bun.write(outputPath, result.text);
|
|
241
239
|
return { path: outputPath, truncated: result.truncated, };
|
|
242
240
|
}
|
|
243
241
|
return result.text;
|
package/dist/src/cli/contract.js
CHANGED
|
@@ -248,6 +248,13 @@ const ALLOWED_CLEANUP_ACTIONS = new Set([
|
|
|
248
248
|
"project-library delete",
|
|
249
249
|
"streaming-endpoint delete",
|
|
250
250
|
"workspace delete",
|
|
251
|
+
"saved-model delete",
|
|
252
|
+
"project-folder delete",
|
|
253
|
+
"data-collection delete",
|
|
254
|
+
"plugin delete",
|
|
255
|
+
"connection delete",
|
|
256
|
+
"user delete",
|
|
257
|
+
"group delete",
|
|
251
258
|
]);
|
|
252
259
|
export function isAllowedCleanupAction(resource, action) {
|
|
253
260
|
return ALLOWED_CLEANUP_ACTIONS.has(`${resource} ${action}`);
|
|
@@ -391,7 +398,7 @@ function structuredExamples(examples, examplePayload) {
|
|
|
391
398
|
* (`dss commands run`, `dss agent contract`) never loads the TypeBox graph.
|
|
392
399
|
* The content matches `typeBoxCommandOutputSchemas()` exactly; parity and
|
|
393
400
|
* freshness are enforced by tests/cli/discovery-output-schemas.test.ts and
|
|
394
|
-
* `bun run
|
|
401
|
+
* `bun run verify`.
|
|
395
402
|
*/
|
|
396
403
|
const actionOutputSchemas = actionOutputSchemasMetadata
|
|
397
404
|
.schemas;
|
|
@@ -764,6 +771,20 @@ const STRING_OUTPUT_ACTIONS = new Set([
|
|
|
764
771
|
"log",
|
|
765
772
|
"log-url",
|
|
766
773
|
]);
|
|
774
|
+
/**
|
|
775
|
+
* `resource.action` shapes that differ from the bare-action-name sets above:
|
|
776
|
+
* `contents`/`usages`/`project-timeline` are array-named elsewhere, and
|
|
777
|
+
* `download`/`diff` are string-named elsewhere, but these handlers return the
|
|
778
|
+
* opposite shape. Pinned by tests/cli/output-shapes.test.ts.
|
|
779
|
+
*/
|
|
780
|
+
const SCOPED_OUTPUT_SHAPES = {
|
|
781
|
+
"folder.contents": "array",
|
|
782
|
+
"plugin.usages": "object",
|
|
783
|
+
"plugin.download": "object",
|
|
784
|
+
"dataset.download": "object",
|
|
785
|
+
"project-library.get": "string",
|
|
786
|
+
"project-library.diff": "object",
|
|
787
|
+
};
|
|
767
788
|
function inferOutputShape(resource, action) {
|
|
768
789
|
if (resource === "project-git") {
|
|
769
790
|
if (action === "branches" || action === "tags" || action === "list-libraries")
|
|
@@ -777,14 +798,15 @@ function inferOutputShape(resource, action) {
|
|
|
777
798
|
|| resource === "version") {
|
|
778
799
|
return "object";
|
|
779
800
|
}
|
|
801
|
+
// Resource-scoped rules come first: the bare-name sets below classify by
|
|
802
|
+
// action alone and would otherwise misfile same-named actions elsewhere.
|
|
803
|
+
const scoped = SCOPED_OUTPUT_SHAPES[`${resource}.${action}`];
|
|
804
|
+
if (scoped !== undefined)
|
|
805
|
+
return scoped;
|
|
780
806
|
if (/^list(-|$)/.test(action))
|
|
781
807
|
return "array";
|
|
782
808
|
if (ARRAY_OUTPUT_ACTIONS.has(action))
|
|
783
809
|
return "array";
|
|
784
|
-
if (resource === "dataset" && action === "download")
|
|
785
|
-
return "object";
|
|
786
|
-
if (resource === "project-library" && action === "get")
|
|
787
|
-
return "string";
|
|
788
810
|
if (STRING_OUTPUT_ACTIONS.has(action))
|
|
789
811
|
return "string";
|
|
790
812
|
return "object";
|
|
@@ -1400,11 +1422,10 @@ function commandRegistryJsonSchema() {
|
|
|
1400
1422
|
function errorEnvelopeJsonSchema() {
|
|
1401
1423
|
return {
|
|
1402
1424
|
type: "object",
|
|
1403
|
-
required: ["type", "
|
|
1425
|
+
required: ["type", "error", "code", "category", "exitCode",],
|
|
1404
1426
|
additionalProperties: true,
|
|
1405
1427
|
properties: {
|
|
1406
1428
|
type: { const: "error", },
|
|
1407
|
-
ok: { const: false, },
|
|
1408
1429
|
error: { type: "string", },
|
|
1409
1430
|
code: { type: "string", },
|
|
1410
1431
|
category: { enum: ["usage", "permission_or_environment", "dss", "internal",], },
|
|
@@ -1414,8 +1435,6 @@ function errorEnvelopeJsonSchema() {
|
|
|
1414
1435
|
retryable: { type: "boolean", },
|
|
1415
1436
|
requestId: { type: "string", },
|
|
1416
1437
|
details: { type: "object", additionalProperties: true, },
|
|
1417
|
-
resource: { type: "string", },
|
|
1418
|
-
action: { type: "string", },
|
|
1419
1438
|
projectKey: { type: "string", },
|
|
1420
1439
|
},
|
|
1421
1440
|
};
|
|
@@ -1526,7 +1545,7 @@ export function buildAgentContract() {
|
|
|
1526
1545
|
failure: "structured-error-object",
|
|
1527
1546
|
failureResultDetailLimitBytes: 65_536,
|
|
1528
1547
|
fieldProjection: "Missing --fields paths: null on stdout; field_projection_missing on stderr.",
|
|
1529
|
-
|
|
1548
|
+
compactResults: "*.list, job get, dataset get/info, project get: compact; --full/--fields use DSS objects; lists take --contains/--limit (list_truncated warning).",
|
|
1530
1549
|
richFailureResults: "doctor/batch/cleanup failures: own compact {ok:false,...} result on stdout and command exit code; no wrapper.",
|
|
1531
1550
|
},
|
|
1532
1551
|
stderr: {
|
package/dist/src/cli/env.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { readFileSync, } from "node:fs";
|
|
2
|
-
import {
|
|
3
|
-
import { fileURLToPath, } from "node:url";
|
|
2
|
+
import { resolve, } from "node:path";
|
|
4
3
|
const projectEnvVars = new Set();
|
|
5
4
|
const installEnvVars = new Set();
|
|
6
5
|
/**
|
|
@@ -42,7 +41,7 @@ export function loadEnvFile() {
|
|
|
42
41
|
// CLI. First writer wins below, so cwd must be listed first.
|
|
43
42
|
const dirs = [
|
|
44
43
|
process.cwd(),
|
|
45
|
-
resolve(
|
|
44
|
+
resolve(import.meta.dir, "..", ".."),
|
|
46
45
|
];
|
|
47
46
|
for (const [index, dir,] of dirs.entries()) {
|
|
48
47
|
try {
|
|
@@ -43,7 +43,7 @@ export declare function buildAppPermissionsSnapshot(projectKey: string, dssUrl:
|
|
|
43
43
|
export declare function writeAppPermissionsSnapshot(filePath: string, snapshot: AppPermissionsSnapshot): void;
|
|
44
44
|
/** Validate envelope shape, version, and hash integrity. Never echoes permission payloads. */
|
|
45
45
|
export declare function parseAppPermissionsSnapshot(text: string, source: string): AppPermissionsSnapshot;
|
|
46
|
-
export declare function readAppPermissionsSnapshot(filePath: string): AppPermissionsSnapshot
|
|
46
|
+
export declare function readAppPermissionsSnapshot(filePath: string): Promise<AppPermissionsSnapshot>;
|
|
47
47
|
/**
|
|
48
48
|
* Refuse to use a snapshot outside the exact project *and* DSS server it was captured
|
|
49
49
|
* from. Access-control rules are server-scoped: the same project key, users, and groups
|
|
@@ -153,10 +153,10 @@ export function parseAppPermissionsSnapshot(text, source) {
|
|
|
153
153
|
permissions,
|
|
154
154
|
};
|
|
155
155
|
}
|
|
156
|
-
export function readAppPermissionsSnapshot(filePath) {
|
|
156
|
+
export async function readAppPermissionsSnapshot(filePath) {
|
|
157
157
|
let text;
|
|
158
158
|
try {
|
|
159
|
-
text = fs.
|
|
159
|
+
text = await fs.promises.readFile(filePath, "utf-8");
|
|
160
160
|
}
|
|
161
161
|
catch (error) {
|
|
162
162
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { CleanupLedgerEntry } from "../../utils/cleanup-ledger.js";
|
|
2
2
|
export declare function projectArg(projectKey: string | undefined): string[];
|
|
3
|
+
/** Input field that identifies the created object, when the result does not. */
|
|
4
|
+
export declare function cleanupInputIdentifierField(resource: string, action: string): string | undefined;
|
|
3
5
|
export declare function cleanupLedgerEntry(resource: string, action: string, args: string[], flags: Record<string, string | boolean>, result: unknown, projectKey: string | undefined): CleanupLedgerEntry | undefined;
|
|
@@ -35,6 +35,30 @@ function createInputFromFlags(flags) {
|
|
|
35
35
|
return undefined;
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Creates whose DSS response carries no identifier: the cleanup ledger pins the
|
|
40
|
+
* created object from this field of the `--data`/`--data-file` input instead.
|
|
41
|
+
* `--stdin` cannot be replayed, so the CLI rejects it with `--record-cleanup`
|
|
42
|
+
* before mutating.
|
|
43
|
+
*/
|
|
44
|
+
const CLEANUP_INPUT_IDENTIFIER_FIELDS = {
|
|
45
|
+
"workspace.create": "workspaceKey",
|
|
46
|
+
"connection.create": "name",
|
|
47
|
+
"user.create": "login",
|
|
48
|
+
"group.create": "name",
|
|
49
|
+
};
|
|
50
|
+
/** Input field that identifies the created object, when the result does not. */
|
|
51
|
+
export function cleanupInputIdentifierField(resource, action) {
|
|
52
|
+
return CLEANUP_INPUT_IDENTIFIER_FIELDS[`${resource}.${action}`];
|
|
53
|
+
}
|
|
54
|
+
function inputIdentifier(resource, action, flags) {
|
|
55
|
+
const field = cleanupInputIdentifierField(resource, action);
|
|
56
|
+
if (!field)
|
|
57
|
+
return undefined;
|
|
58
|
+
const input = resultRecord(createInputFromFlags(flags));
|
|
59
|
+
const identifier = stringField(input, [field,]);
|
|
60
|
+
return identifier ? { identifier, input, } : undefined;
|
|
61
|
+
}
|
|
38
62
|
export function cleanupLedgerEntry(resource, action, args, flags, result, projectKey) {
|
|
39
63
|
const record = resultRecord(result);
|
|
40
64
|
// A notebook save is ledger-eligible only when it created the notebook:
|
|
@@ -77,6 +101,22 @@ export function cleanupLedgerEntry(resource, action, args, flags, result, projec
|
|
|
77
101
|
cleanup: { argv: ["dataset", "delete", name, "--if-exists", ...withProject,], },
|
|
78
102
|
};
|
|
79
103
|
}
|
|
104
|
+
case "dataset.create-managed": {
|
|
105
|
+
// The handler returns `created` as the dataset name and the project
|
|
106
|
+
// DSS created it in, which wins over the requested project key.
|
|
107
|
+
const name = stringField(record, ["created", "name",]) ?? flags["name"];
|
|
108
|
+
if (!name)
|
|
109
|
+
return undefined;
|
|
110
|
+
const landedProject = stringField(record, ["projectKey",]) ?? project;
|
|
111
|
+
return {
|
|
112
|
+
...base,
|
|
113
|
+
...(landedProject ? { projectKey: landedProject, } : {}),
|
|
114
|
+
name,
|
|
115
|
+
cleanup: {
|
|
116
|
+
argv: ["dataset", "delete", name, "--if-exists", ...projectArg(landedProject),],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
80
120
|
case "recipe.create": {
|
|
81
121
|
const name = stringField(record, ["created", "recipeName", "name",])
|
|
82
122
|
?? flags["name"];
|
|
@@ -199,6 +239,78 @@ export function cleanupLedgerEntry(resource, action, args, flags, result, projec
|
|
|
199
239
|
cleanup: { argv: ["ml-task", "delete", analysisId, taskId, ...withProject,], },
|
|
200
240
|
};
|
|
201
241
|
}
|
|
242
|
+
case "ml-task.create-for-dataset": {
|
|
243
|
+
// This create opens a new visual analysis around the task, so cleanup
|
|
244
|
+
// removes the whole analysis rather than leaving an empty one behind.
|
|
245
|
+
const analysisId = stringField(record, ["analysisId",]);
|
|
246
|
+
if (!analysisId)
|
|
247
|
+
return undefined;
|
|
248
|
+
return {
|
|
249
|
+
...base,
|
|
250
|
+
id: analysisId,
|
|
251
|
+
cleanup: { argv: ["analysis", "delete", analysisId, "--if-exists", ...withProject,], },
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
case "saved-model.create-external": {
|
|
255
|
+
const id = stringField(record, ["id",]);
|
|
256
|
+
if (!id)
|
|
257
|
+
return undefined;
|
|
258
|
+
const landedProject = stringField(record, ["projectKey",]) ?? project;
|
|
259
|
+
return {
|
|
260
|
+
...base,
|
|
261
|
+
...(landedProject ? { projectKey: landedProject, } : {}),
|
|
262
|
+
id,
|
|
263
|
+
name: args[0],
|
|
264
|
+
cleanup: {
|
|
265
|
+
argv: ["saved-model", "delete", id, "--if-exists", ...projectArg(landedProject),],
|
|
266
|
+
},
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
case "project-folder.create-child": {
|
|
270
|
+
// `created` falls back to the requested name when DSS omits the id,
|
|
271
|
+
// and a name cannot address a folder delete: only `id` pins it.
|
|
272
|
+
const id = stringField(record, ["id",]);
|
|
273
|
+
if (!id)
|
|
274
|
+
return undefined;
|
|
275
|
+
return {
|
|
276
|
+
...base,
|
|
277
|
+
id,
|
|
278
|
+
name: flags["name"],
|
|
279
|
+
cleanup: { argv: ["project-folder", "delete", id, "--if-exists",], },
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
case "data-collection.create": {
|
|
283
|
+
const id = stringField(record, ["id",]);
|
|
284
|
+
if (!id)
|
|
285
|
+
return undefined;
|
|
286
|
+
return {
|
|
287
|
+
...base,
|
|
288
|
+
id,
|
|
289
|
+
cleanup: { argv: ["data-collection", "delete", id,], },
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
case "plugin.create-dev": {
|
|
293
|
+
const id = stringField(record, ["created",]) ?? args[0];
|
|
294
|
+
if (!id)
|
|
295
|
+
return undefined;
|
|
296
|
+
return {
|
|
297
|
+
...base,
|
|
298
|
+
id,
|
|
299
|
+
cleanup: { argv: ["plugin", "delete", id,], },
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
case "connection.create":
|
|
303
|
+
case "user.create":
|
|
304
|
+
case "group.create": {
|
|
305
|
+
const pinned = inputIdentifier(resource, action, flags);
|
|
306
|
+
if (!pinned)
|
|
307
|
+
return undefined;
|
|
308
|
+
return {
|
|
309
|
+
...base,
|
|
310
|
+
name: pinned.identifier,
|
|
311
|
+
cleanup: { argv: [resource, "delete", pinned.identifier, "--if-exists",], },
|
|
312
|
+
};
|
|
313
|
+
}
|
|
202
314
|
case "data-quality.create-rule": {
|
|
203
315
|
const ruleId = stringField(record, ["id", "created",]);
|
|
204
316
|
if (!ruleId)
|
|
@@ -222,8 +334,11 @@ export function cleanupLedgerEntry(resource, action, args, flags, result, projec
|
|
|
222
334
|
cleanup: { argv: ["code-env", "delete", lang, name, "--if-exists",], },
|
|
223
335
|
};
|
|
224
336
|
}
|
|
225
|
-
case "streaming-endpoint.create":
|
|
226
|
-
|
|
337
|
+
case "streaming-endpoint.create":
|
|
338
|
+
case "streaming-endpoint.create-managed": {
|
|
339
|
+
const id = stringField(record, ["created",]) ?? args[0];
|
|
340
|
+
if (!id)
|
|
341
|
+
return undefined;
|
|
227
342
|
return {
|
|
228
343
|
...base,
|
|
229
344
|
id,
|
|
@@ -240,15 +355,14 @@ export function cleanupLedgerEntry(resource, action, args, flags, result, projec
|
|
|
240
355
|
};
|
|
241
356
|
}
|
|
242
357
|
case "workspace.create": {
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
if (!workspaceKey)
|
|
358
|
+
const pinned = inputIdentifier(resource, action, flags);
|
|
359
|
+
if (!pinned)
|
|
246
360
|
return undefined;
|
|
247
361
|
return {
|
|
248
362
|
...base,
|
|
249
|
-
id:
|
|
250
|
-
name: stringField(input, ["displayName",]),
|
|
251
|
-
cleanup: { argv: ["workspace", "delete",
|
|
363
|
+
id: pinned.identifier,
|
|
364
|
+
name: stringField(pinned.input, ["displayName",]),
|
|
365
|
+
cleanup: { argv: ["workspace", "delete", pinned.identifier,], },
|
|
252
366
|
};
|
|
253
367
|
}
|
|
254
368
|
case "project-library.create-file":
|
|
@@ -56,7 +56,7 @@ export declare function dedupeFlowZonePlanItems(items: FlowZoneItemInput[]): Flo
|
|
|
56
56
|
export declare function flowZonePlanItemKeys(plan: FlowZoneOrganizePlan): Set<string>;
|
|
57
57
|
export declare function validateUniqueFlowZoneAssignments(plan: FlowZoneOrganizePlan): void;
|
|
58
58
|
export declare function parseFlowZoneOrganizePlan(input: Record<string, unknown>): FlowZoneOrganizePlan;
|
|
59
|
-
export declare function readFlowZoneOrganizePlan(flags: Record<string, string | boolean>, usage: string): FlowZoneOrganizePlan
|
|
59
|
+
export declare function readFlowZoneOrganizePlan(flags: Record<string, string | boolean>, usage: string): Promise<FlowZoneOrganizePlan>;
|
|
60
60
|
export declare function findFlowZoneForPlan(zones: FlowZone[], plan: FlowZoneOrganizeZonePlan): FlowZone | undefined;
|
|
61
61
|
export declare function ensureFlowZonePlanTarget(plan: FlowZoneOrganizeZonePlan, existing: FlowZone | undefined): void;
|
|
62
62
|
export declare function flowZoneExplicitItems(zone: FlowZone): FlowZoneItemInput[];
|
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { readFileSync, } from "node:fs";
|
|
2
1
|
import { ClientValidationError, } from "../../errors.js";
|
|
3
2
|
import { asRecord, } from "../../utils/records.js";
|
|
4
3
|
import { compareStrings, } from "../../utils/stable-hash.js";
|
|
5
|
-
import { finiteNumberField, jsonInput, optionalStringField, parseJsonObject, requiredStringArray, splitCsvFlag, } from "../coerce.js";
|
|
4
|
+
import { finiteNumberField, jsonInput, optionalStringField, parseJsonObject, readTextFile, requiredStringArray, splitCsvFlag, } from "../coerce.js";
|
|
6
5
|
import { commandUsage, } from "../syntax.js";
|
|
7
6
|
import { UsageError, } from "../usage.js";
|
|
8
7
|
export function flowZoneId(value) {
|
|
@@ -257,9 +256,9 @@ export function parseFlowZoneOrganizePlan(input) {
|
|
|
257
256
|
validateUniqueFlowZoneAssignments(plan);
|
|
258
257
|
return plan;
|
|
259
258
|
}
|
|
260
|
-
export function readFlowZoneOrganizePlan(flags, usage) {
|
|
259
|
+
export async function readFlowZoneOrganizePlan(flags, usage) {
|
|
261
260
|
const data = typeof flags["file"] === "string"
|
|
262
|
-
? parseJsonObject(
|
|
261
|
+
? parseJsonObject(await readTextFile(flags["file"]), flags["file"])
|
|
263
262
|
: jsonInput(flags);
|
|
264
263
|
if (!data) {
|
|
265
264
|
throw new UsageError(`--data, --data-file, --file, or --stdin is required. Usage: ${usage}`, "missing_required_flag");
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { type CompactListField } from "./list-shapes.js";
|
|
2
|
+
type Item = Record<string, unknown>;
|
|
3
|
+
/** One compact field of a DSS list item, as `RESOURCE.list` prints it. */
|
|
4
|
+
export declare function compactValue(item: Item, spec: CompactListField): unknown;
|
|
1
5
|
/**
|
|
2
6
|
* Shape a `*.list` result for agents: `--contains` filters on identifier
|
|
3
7
|
* fields, `--limit` caps the count (a `list_truncated` warning reports the
|
|
@@ -5,3 +9,4 @@
|
|
|
5
9
|
* `--full` or `--fields` asks for the DSS objects.
|
|
6
10
|
*/
|
|
7
11
|
export declare function shapeListResult(resource: string, result: unknown, flags: Record<string, string | boolean>): unknown;
|
|
12
|
+
export {};
|
|
@@ -4,10 +4,19 @@ import { enqueueCliWarning, } from "./output.js";
|
|
|
4
4
|
function record(value) {
|
|
5
5
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
6
6
|
}
|
|
7
|
-
function
|
|
7
|
+
function valueAt(item, path) {
|
|
8
8
|
let value = item;
|
|
9
|
-
for (const key of
|
|
9
|
+
for (const key of path.split("."))
|
|
10
10
|
value = record(value)[key];
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
/** One compact field of a DSS list item, as `RESOURCE.list` prints it. */
|
|
14
|
+
export function compactValue(item, spec) {
|
|
15
|
+
const value = valueAt(item, spec.path);
|
|
16
|
+
if (value === undefined && spec.fallback
|
|
17
|
+
&& valueAt(item, spec.fallback.when) === spec.fallback.equals) {
|
|
18
|
+
return spec.fallback.value;
|
|
19
|
+
}
|
|
11
20
|
if (spec.roleRefs !== true)
|
|
12
21
|
return value;
|
|
13
22
|
// Dataset refs across every role of recipe inputs/outputs (`{main:{items:[{ref}]}}`).
|
|
@@ -9,6 +9,12 @@ export interface CompactListField {
|
|
|
9
9
|
path: string;
|
|
10
10
|
/** Flatten recipe roles (`{main:{items:[{ref}]}}`) to the dataset refs. */
|
|
11
11
|
roleRefs?: true;
|
|
12
|
+
/** Value when `path` is absent and the item's `when` path equals `equals`. */
|
|
13
|
+
fallback?: {
|
|
14
|
+
when: string;
|
|
15
|
+
equals: unknown;
|
|
16
|
+
value: unknown;
|
|
17
|
+
};
|
|
12
18
|
schema: Record<string, unknown>;
|
|
13
19
|
}
|
|
14
20
|
/**
|
|
@@ -51,7 +51,12 @@ export const COMPACT_LIST_FIELDS = {
|
|
|
51
51
|
id: field("def.id", STRING),
|
|
52
52
|
name: field("def.name", STRING),
|
|
53
53
|
type: field("def.type", STRING),
|
|
54
|
-
|
|
54
|
+
// DSS lists in-progress jobs with `stableState: false` and no `state`
|
|
55
|
+
// (COMPUTING_DEPS or RUNNING); `job get` reports the exact phase.
|
|
56
|
+
state: {
|
|
57
|
+
...field("state", STRING),
|
|
58
|
+
fallback: { when: "stableState", equals: false, value: "RUNNING", },
|
|
59
|
+
},
|
|
55
60
|
startTime: field("startTime", NUMBER),
|
|
56
61
|
endTime: field("endTime", NUMBER),
|
|
57
62
|
},
|
|
@@ -83,6 +88,7 @@ export const COMPACT_LIST_FIELDS = {
|
|
|
83
88
|
id: field("id", STRING),
|
|
84
89
|
version: field("version", STRING),
|
|
85
90
|
label: field("meta.label", STRING),
|
|
91
|
+
isDev: field("isDev", BOOLEAN),
|
|
86
92
|
},
|
|
87
93
|
macro: {
|
|
88
94
|
runnableType: field("runnableType", STRING),
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* the single source of truth for `src/generated/action-output-schemas.json`,
|
|
4
4
|
* which discovery reads as plain data so no TypeBox schema graph loads on the
|
|
5
5
|
* discovery path. Regenerate with
|
|
6
|
-
* `bun scripts/generate-action-output-schemas.mjs`; `bun run
|
|
6
|
+
* `bun scripts/generate-action-output-schemas.mjs`; `bun run verify` fails when
|
|
7
7
|
* the generated file is stale. SDK validation keeps using the TypeBox schema
|
|
8
8
|
* objects from `packages/types` directly and never depends on the generated file.
|
|
9
9
|
*/
|