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.
Files changed (68) hide show
  1. package/README.md +7 -5
  2. package/dist/build-metadata.json +1 -1
  3. package/dist/packages/types/src/index.d.ts +4 -0
  4. package/dist/packages/types/src/index.js +4 -0
  5. package/dist/src/cli/coerce.d.ts +6 -0
  6. package/dist/src/cli/coerce.js +9 -0
  7. package/dist/src/cli/command-syntax.json +36 -0
  8. package/dist/src/cli/commands/app.js +2 -2
  9. package/dist/src/cli/commands/code-env.js +5 -6
  10. package/dist/src/cli/commands/code.js +16 -2
  11. package/dist/src/cli/commands/dashboard.js +1 -2
  12. package/dist/src/cli/commands/dataset.js +2 -2
  13. package/dist/src/cli/commands/flow-zone.js +1 -1
  14. package/dist/src/cli/commands/job.js +101 -22
  15. package/dist/src/cli/commands/plugin.js +7 -7
  16. package/dist/src/cli/commands/project-library.js +2 -2
  17. package/dist/src/cli/commands/project.js +25 -7
  18. package/dist/src/cli/commands/recipe.js +11 -19
  19. package/dist/src/cli/commands/scenario.js +2 -4
  20. package/dist/src/cli/contract.js +29 -10
  21. package/dist/src/cli/env.js +2 -3
  22. package/dist/src/cli/helpers/app-permissions.d.ts +1 -1
  23. package/dist/src/cli/helpers/app-permissions.js +2 -2
  24. package/dist/src/cli/helpers/cleanup.d.ts +2 -0
  25. package/dist/src/cli/helpers/cleanup.js +122 -8
  26. package/dist/src/cli/helpers/flow-zone.d.ts +1 -1
  27. package/dist/src/cli/helpers/flow-zone.js +3 -4
  28. package/dist/src/cli/list-output.d.ts +5 -0
  29. package/dist/src/cli/list-output.js +11 -2
  30. package/dist/src/cli/list-shapes.d.ts +6 -0
  31. package/dist/src/cli/list-shapes.js +7 -1
  32. package/dist/src/cli/output-schemas.d.ts +1 -1
  33. package/dist/src/cli/output-schemas.js +28 -4
  34. package/dist/src/cli/output.js +7 -0
  35. package/dist/src/cli/project-map-output.d.ts +24 -0
  36. package/dist/src/cli/project-map-output.js +21 -0
  37. package/dist/src/cli/read-shapes.d.ts +14 -0
  38. package/dist/src/cli/read-shapes.js +114 -0
  39. package/dist/src/cli/result-shapes.d.ts +6 -0
  40. package/dist/src/cli/result-shapes.js +235 -0
  41. package/dist/src/cli/version.d.ts +2 -2
  42. package/dist/src/cli/version.js +4 -5
  43. package/dist/src/cli.js +77 -33
  44. package/dist/src/client.d.ts +14 -2
  45. package/dist/src/client.js +18 -10
  46. package/dist/src/generated/action-output-schemas.json +242 -35
  47. package/dist/src/index.d.ts +1 -1
  48. package/dist/src/resources/datasets.d.ts +11 -1
  49. package/dist/src/resources/datasets.js +91 -30
  50. package/dist/src/resources/jobs.d.ts +37 -6
  51. package/dist/src/resources/jobs.js +110 -44
  52. package/dist/src/resources/projects.js +2 -2
  53. package/dist/src/resources/recipes.d.ts +12 -1
  54. package/dist/src/resources/recipes.js +39 -12
  55. package/dist/src/resources/scenarios.d.ts +8 -1
  56. package/dist/src/resources/scenarios.js +51 -12
  57. package/dist/src/resources/wiki.js +3 -7
  58. package/dist/src/skill.js +1 -2
  59. package/dist/src/utils/project-archive.d.ts +25 -0
  60. package/dist/src/utils/project-archive.js +216 -5
  61. package/dist/src/utils/response-file.js +15 -8
  62. package/dist/src/utils/secret-sanitize.d.ts +11 -2
  63. package/dist/src/utils/secret-sanitize.js +23 -6
  64. package/package.json +13 -14
  65. package/plugin.json +1 -1
  66. package/skills/dataiku-dss/SKILL.md +1 -1
  67. package/skills/dataiku-dss/references/discovery.md +2 -2
  68. package/skills/dataiku-dss/references/troubleshooting.md +2 -2
@@ -13,6 +13,7 @@
13
13
  * Imported by the generator and the discovery parity test only.
14
14
  */
15
15
  import { compactListOutputSchemas, } from "./list-shapes.js";
16
+ import { compactReadOutputSchemas, DATASET_GET_DROPPED_KEYS, PROJECT_GET_DROPPED_KEYS, } from "./read-shapes.js";
16
17
  const PROJECT_LIBRARY_ITEM_OUTPUT_SCHEMA = {
17
18
  type: "object",
18
19
  additionalProperties: false,
@@ -50,13 +51,28 @@ const NOTEBOOK_SAVE_SQL_OUTPUT_SCHEMA = {
50
51
  requested: { type: "string", },
51
52
  },
52
53
  };
54
+ /**
55
+ * Discovery schema of a compact settings object (`dataset get`, `project get`):
56
+ * the SDK TypeBox schema minus the keys the CLI drops, with only the
57
+ * identifiers that survive pruning required. SDK validation keeps the
58
+ * unmodified TypeBox schema.
59
+ */
60
+ function settingsOutputSchema(schema, droppedKeys, extraProperties = {}) {
61
+ const properties = Object.fromEntries(Object.entries(schema["properties"]).filter(([key,]) => !droppedKeys.includes(key)));
62
+ const required = (schema["required"] ?? []).filter((key) => !droppedKeys.includes(key));
63
+ return {
64
+ ...schema,
65
+ properties: { ...properties, ...extraProperties, },
66
+ required: [...required, ...Object.keys(extraProperties),],
67
+ };
68
+ }
53
69
  let commandOutputSchemas;
54
70
  /**
55
71
  * Canonical TypeBox-derived output schemas, keyed by `resource.action`. This is
56
72
  * the single source of truth for `src/generated/action-output-schemas.json`,
57
73
  * which discovery reads as plain data so no TypeBox schema graph loads on the
58
74
  * discovery path. Regenerate with
59
- * `bun scripts/generate-action-output-schemas.mjs`; `bun run check` fails when
75
+ * `bun scripts/generate-action-output-schemas.mjs`; `bun run verify` fails when
60
76
  * the generated file is stale. SDK validation keeps using the TypeBox schema
61
77
  * objects from `packages/types` directly and never depends on the generated file.
62
78
  */
@@ -196,9 +212,15 @@ export function typeBoxCommandOutputSchemas() {
196
212
  maxLines: { type: "integer", minimum: 0, },
197
213
  },
198
214
  },
199
- "project.get": ProjectDetailsSchema,
215
+ "project.get": settingsOutputSchema(ProjectDetailsSchema, PROJECT_GET_DROPPED_KEYS, {
216
+ deniedPermissions: {
217
+ type: "array",
218
+ items: { type: "string", },
219
+ description: "Permission flags DSS reports as false; absent flags are granted.",
220
+ },
221
+ }),
200
222
  "project.metadata": ProjectMetadataSchema,
201
- "dataset.get": DatasetDetailsSchema,
223
+ "dataset.get": settingsOutputSchema(DatasetDetailsSchema, DATASET_GET_DROPPED_KEYS),
202
224
  "dataset.schema": DatasetSchemaSchema,
203
225
  "job.wait": JobWaitResultSchema,
204
226
  "job.monitor": JobWaitResultSchema,
@@ -224,7 +246,9 @@ export function typeBoxCommandOutputSchemas() {
224
246
  },
225
247
  ],
226
248
  },
227
- // `*.list` defaults to compact items; `--full` returns the DSS objects.
249
+ // `*.list` and the four large reads default to compact shapes; `--full`
250
+ // returns the DSS objects.
228
251
  ...compactListOutputSchemas(),
252
+ ...compactReadOutputSchemas(),
229
253
  };
230
254
  }
@@ -193,6 +193,13 @@ export function commandFailureMessage(result) {
193
193
  if (typeof message === "string" && message.length > 0)
194
194
  return message;
195
195
  }
196
+ if (record.timedOut === true && typeof record.jobId === "string"
197
+ && typeof record.progress === "object" && typeof record.state === "string") {
198
+ // A job wait result: the timeout only stopped polling, the DSS job is
199
+ // still running (and was not cancelled).
200
+ const resume = typeof record.resume === "string" ? ` Resume with: ${record.resume}` : "";
201
+ return `Timed out waiting for job ${record.jobId} after ${record.elapsedMs}ms (last known state: ${record.state}); the job is still running in DSS and was not cancelled.${resume}`;
202
+ }
196
203
  const state = typeof record.state === "string"
197
204
  ? record.state
198
205
  : typeof record.outcome === "string"
@@ -0,0 +1,24 @@
1
+ import type { FlowMapResult } from "../resources/projects.js";
2
+ import type { AnalyzedFlowMap, AnalyzedFlowNode, FlowMapComponent, FlowMapZone } from "../utils/flow-analysis.js";
3
+ /**
4
+ * `dss project map` output: the SDK `FlowMapResult` with derivable duplicates
5
+ * removed at the CLI boundary. Lossless — every dropped field can be rebuilt:
6
+ * node `name` defaults to `id`, `zoneName` comes from `zones[]`, zone/component
7
+ * membership comes from each node's `zoneId`/`componentId`, an absent
8
+ * `warnings` means none, and an absent `truncation` means nothing was cut.
9
+ * `topologyFingerprint` is passed through untouched.
10
+ */
11
+ export type CompactFlowMapNode = Omit<AnalyzedFlowNode, "zoneName">;
12
+ export type CompactFlowMapZone = Omit<FlowMapZone, "nodeIds">;
13
+ export type CompactFlowMapComponent = Omit<FlowMapComponent, "nodeIds">;
14
+ export interface CompactFlowMap extends Omit<AnalyzedFlowMap, "nodes" | "zones" | "components" | "warnings"> {
15
+ nodes: CompactFlowMapNode[];
16
+ zones: CompactFlowMapZone[];
17
+ components: CompactFlowMapComponent[];
18
+ warnings?: string[];
19
+ }
20
+ export interface CompactFlowMapResult extends Omit<FlowMapResult, "map" | "truncation"> {
21
+ map: CompactFlowMap;
22
+ truncation?: FlowMapResult["truncation"];
23
+ }
24
+ export declare function compactProjectMapOutput(result: FlowMapResult): CompactFlowMapResult;
@@ -0,0 +1,21 @@
1
+ function compactNode(node) {
2
+ const { zoneName: _zoneName, name, ...rest } = node;
3
+ return name === undefined || name === node.id ? rest : { ...rest, name, };
4
+ }
5
+ export function compactProjectMapOutput(result) {
6
+ const { nodes, zones, components, warnings, ...mapRest } = result.map;
7
+ const map = {
8
+ ...mapRest,
9
+ nodes: nodes.map(compactNode),
10
+ zones: zones.map(({ nodeIds: _nodeIds, ...zone }) => zone),
11
+ components: components.map(({ nodeIds: _nodeIds, ...component }) => component),
12
+ };
13
+ if (warnings.length > 0)
14
+ map.warnings = warnings;
15
+ const { map: _map, truncation, ...resultRest } = result;
16
+ return {
17
+ map,
18
+ ...(truncation.truncated ? { truncation, } : {}),
19
+ ...resultRest,
20
+ };
21
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Plain data shared by the compact read shapers (src/cli/result-shapes.ts) and
3
+ * the discovery output schemas (src/cli/output-schemas.ts), so the advertised
4
+ * schema is the emitted shape. Imports nothing: the schema generator loads it
5
+ * without the CLI runtime.
6
+ */
7
+ /**
8
+ * Settings-object noise `dataset get` / `project get` drop: metrics and check
9
+ * configuration, version stamps and checklists that agents never edit.
10
+ */
11
+ export declare const DATASET_GET_DROPPED_KEYS: readonly ["metrics", "metricsChecks", "versionTag", "creationTag", "checklists"];
12
+ export declare const PROJECT_GET_DROPPED_KEYS: readonly ["metrics", "metricsChecks", "versionTag", "creationTag", "checklists", "objectImgHash", "isProjectImg", "defaultImgColor", "imgPattern"];
13
+ /** Discovery output schemas of the `job get` and `dataset info` compact defaults. */
14
+ export declare function compactReadOutputSchemas(): Record<string, Record<string, unknown>>;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Plain data shared by the compact read shapers (src/cli/result-shapes.ts) and
3
+ * the discovery output schemas (src/cli/output-schemas.ts), so the advertised
4
+ * schema is the emitted shape. Imports nothing: the schema generator loads it
5
+ * without the CLI runtime.
6
+ */
7
+ /**
8
+ * Settings-object noise `dataset get` / `project get` drop: metrics and check
9
+ * configuration, version stamps and checklists that agents never edit.
10
+ */
11
+ export const DATASET_GET_DROPPED_KEYS = [
12
+ "metrics",
13
+ "metricsChecks",
14
+ "versionTag",
15
+ "creationTag",
16
+ "checklists",
17
+ ];
18
+ export const PROJECT_GET_DROPPED_KEYS = [
19
+ ...DATASET_GET_DROPPED_KEYS,
20
+ "objectImgHash",
21
+ "isProjectImg",
22
+ "defaultImgColor",
23
+ "imgPattern",
24
+ ];
25
+ const STRING = { type: "string", };
26
+ const NUMBER = { type: "number", };
27
+ const BOOLEAN = { type: "boolean", };
28
+ /** Discovery output schemas of the `job get` and `dataset info` compact defaults. */
29
+ export function compactReadOutputSchemas() {
30
+ const errorSchema = {
31
+ type: "object",
32
+ additionalProperties: false,
33
+ properties: { type: STRING, message: STRING, },
34
+ };
35
+ return {
36
+ "job.get": {
37
+ type: "object",
38
+ additionalProperties: false,
39
+ required: ["id", "state",],
40
+ properties: {
41
+ id: STRING,
42
+ name: STRING,
43
+ type: STRING,
44
+ state: STRING,
45
+ initiator: STRING,
46
+ triggeredFrom: STRING,
47
+ startTime: NUMBER,
48
+ endTime: NUMBER,
49
+ outputs: {
50
+ type: "array",
51
+ items: {
52
+ type: "object",
53
+ additionalProperties: false,
54
+ properties: { type: STRING, id: STRING, projectKey: STRING, partition: STRING, },
55
+ },
56
+ },
57
+ progress: { type: "object", additionalProperties: NUMBER, },
58
+ error: errorSchema,
59
+ activities: {
60
+ type: "array",
61
+ items: {
62
+ type: "object",
63
+ additionalProperties: false,
64
+ properties: {
65
+ id: STRING,
66
+ recipe: STRING,
67
+ recipeType: STRING,
68
+ state: STRING,
69
+ totalTime: NUMBER,
70
+ warnings: NUMBER,
71
+ error: errorSchema,
72
+ message: STRING,
73
+ },
74
+ },
75
+ },
76
+ },
77
+ },
78
+ "dataset.info": {
79
+ type: "object",
80
+ additionalProperties: false,
81
+ required: ["name",],
82
+ properties: {
83
+ name: STRING,
84
+ type: STRING,
85
+ managed: BOOLEAN,
86
+ connection: STRING,
87
+ formatType: STRING,
88
+ partitioned: BOOLEAN,
89
+ schema: {
90
+ type: "array",
91
+ items: {
92
+ type: "object",
93
+ additionalProperties: false,
94
+ required: ["name", "type",],
95
+ properties: { name: STRING, type: STRING, meaning: STRING, comment: STRING, },
96
+ },
97
+ },
98
+ recipes: { type: "array", items: STRING, },
99
+ buildable: BOOLEAN,
100
+ upstreamBuildable: BOOLEAN,
101
+ downstreamBuildable: BOOLEAN,
102
+ tags: { type: "array", items: STRING, },
103
+ dataQuality: { type: "object", additionalProperties: true, },
104
+ lastBuild: {
105
+ type: "object",
106
+ additionalProperties: false,
107
+ properties: { jobId: STRING, success: BOOLEAN, startTime: NUMBER, endTime: NUMBER, },
108
+ },
109
+ lastModifiedOn: NUMBER,
110
+ lastModifiedBy: STRING,
111
+ },
112
+ },
113
+ };
114
+ }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Compact a command result for agents: `*.list` items (filter, cap, compact
3
+ * fields) and the four large read commands. `--full` and `--fields` bypass
4
+ * the read shapers so the DSS object (or a projection of it) is returned.
5
+ */
6
+ export declare function shapeCommandResult(resource: string, action: string, result: unknown, flags: Record<string, string | boolean>): unknown;
@@ -0,0 +1,235 @@
1
+ /**
2
+ * Compact default results of the four large read commands (`job get`,
3
+ * `dataset info`, `dataset get`, `project get`), plus the single dispatch
4
+ * point for compacting command results (`shapeCommandResult`).
5
+ *
6
+ * The SDK resources keep returning the raw DSS objects; only the CLI compacts.
7
+ * `--full` returns the DSS object unchanged, and `--fields` projects DSS paths
8
+ * out of it (exactly like the compact `*.list` shapes in list-output.ts).
9
+ *
10
+ * Imports no schema graph: the discovery schemas below are plain data.
11
+ */
12
+ import { isRecord, } from "../utils/records.js";
13
+ import { shapeListResult, } from "./list-output.js";
14
+ import { DATASET_GET_DROPPED_KEYS, PROJECT_GET_DROPPED_KEYS, } from "./read-shapes.js";
15
+ function record(value) {
16
+ return isRecord(value) ? value : {};
17
+ }
18
+ function nonEmptyString(value) {
19
+ return typeof value === "string" && value.length > 0 ? value : undefined;
20
+ }
21
+ /** Drop `undefined` members so absent optional fields are omitted, never null. */
22
+ function defined(fields) {
23
+ return Object.fromEntries(Object.entries(fields).filter(([, value,]) => value !== undefined));
24
+ }
25
+ /** `{ type, message }` of a DSS error object (`error`, `firstFailure`); undefined when empty. */
26
+ function errorSummary(value) {
27
+ if (!isRecord(value))
28
+ return undefined;
29
+ const shaped = defined({
30
+ type: nonEmptyString(value["errorType"]),
31
+ message: nonEmptyString(value["detailedMessage"]) ?? nonEmptyString(value["message"]),
32
+ });
33
+ return Object.keys(shaped).length > 0 ? shaped : undefined;
34
+ }
35
+ /**
36
+ * Output id/project of a job output across DSS shapes: `targetDataset` +
37
+ * `targetDatasetProjectKey`, `targetManagedFolder`, `targetSavedModel`, ...
38
+ * and the plain `{ id, projectKey }` form.
39
+ */
40
+ function outputTarget(output) {
41
+ const targetKey = Object.keys(output).find((key) => /^target[A-Z]/.test(key) && !key.endsWith("ProjectKey") && key !== "targetPartition"
42
+ && typeof output[key] === "string");
43
+ return {
44
+ id: targetKey === undefined ? output["id"] : output[targetKey],
45
+ projectKey: targetKey === undefined ? output["projectKey"] : output[`${targetKey}ProjectKey`],
46
+ };
47
+ }
48
+ function shapeJobOutput(output, jobProjectKey) {
49
+ const fields = record(output);
50
+ const { id, projectKey, } = outputTarget(fields);
51
+ const partition = fields["targetPartition"] ?? fields["partition"];
52
+ return defined({
53
+ type: fields["type"],
54
+ id,
55
+ projectKey: typeof projectKey === "string" && projectKey !== jobProjectKey
56
+ ? projectKey
57
+ : undefined,
58
+ partition: typeof partition === "string" && partition !== "NP" ? partition : undefined,
59
+ });
60
+ }
61
+ function shapeJobActivity(activity) {
62
+ const fields = record(activity);
63
+ return defined({
64
+ id: fields["activityId"],
65
+ recipe: fields["recipeName"],
66
+ recipeType: fields["recipeType"],
67
+ state: fields["state"],
68
+ totalTime: fields["totalTime"],
69
+ warnings: record(fields["warnings"])["totalCount"],
70
+ error: errorSummary(fields["firstFailure"]),
71
+ message: nonEmptyString(fields["message"]),
72
+ });
73
+ }
74
+ /** `job get`: state, outputs, progress, and the failure; never the log tail. */
75
+ function shapeJobGet(result) {
76
+ if (!isRecord(result))
77
+ return result;
78
+ const base = record(result["baseStatus"]);
79
+ const def = record(base["def"]);
80
+ const activities = record(base["activities"]);
81
+ return defined({
82
+ id: def["id"],
83
+ name: def["name"],
84
+ type: def["type"],
85
+ state: base["state"],
86
+ initiator: nonEmptyString(def["initiator"])
87
+ ?? nonEmptyString(record(result["initiator"])["login"]),
88
+ triggeredFrom: def["triggeredFrom"],
89
+ startTime: base["jobStartTime"],
90
+ endTime: base["jobEndTime"],
91
+ outputs: Array.isArray(def["outputs"])
92
+ ? def["outputs"].map((output) => shapeJobOutput(output, def["projectKey"]))
93
+ : undefined,
94
+ progress: result["globalState"],
95
+ error: errorSummary(result["error"]),
96
+ activities: Object.values(activities).map(shapeJobActivity),
97
+ });
98
+ }
99
+ /** Extra column keys worth keeping when set (DSS sends most as ""/defaults). */
100
+ const COLUMN_EXTRA_KEYS = ["meaning", "comment",];
101
+ function shapeColumn(column) {
102
+ const fields = record(column);
103
+ const shaped = { name: fields["name"], type: fields["type"], };
104
+ for (const key of COLUMN_EXTRA_KEYS) {
105
+ const value = nonEmptyString(fields[key]);
106
+ if (value !== undefined)
107
+ shaped[key] = value;
108
+ }
109
+ return shaped;
110
+ }
111
+ function shapeLastBuild(value) {
112
+ if (!isRecord(value))
113
+ return undefined;
114
+ const shaped = defined({
115
+ jobId: value["jobId"],
116
+ success: value["buildSuccess"],
117
+ startTime: value["buildStartTime"],
118
+ endTime: value["buildEndTime"],
119
+ });
120
+ return Object.keys(shaped).length > 0 ? shaped : undefined;
121
+ }
122
+ /** `dataset info`: what the dataset is, its schema, flow neighbours, and freshness. */
123
+ function shapeDatasetInfo(result) {
124
+ if (!isRecord(result))
125
+ return result;
126
+ const dataset = record(result["dataset"]);
127
+ const params = record(dataset["params"]);
128
+ const columns = record(dataset["schema"])["columns"];
129
+ const tags = dataset["tags"];
130
+ const quality = record(result["dataQualityStatus"]);
131
+ const versioning = record(result["versioning"]);
132
+ const timeline = record(result["timeline"]);
133
+ return defined({
134
+ name: result["name"] ?? dataset["name"],
135
+ type: result["type"] ?? dataset["type"],
136
+ managed: dataset["managed"],
137
+ connection: params["connection"] ?? params["uploadConnection"],
138
+ formatType: dataset["formatType"],
139
+ partitioned: result["partitioned"],
140
+ schema: Array.isArray(columns) ? columns.map(shapeColumn) : undefined,
141
+ recipes: Array.isArray(result["recipes"])
142
+ ? result["recipes"].map((recipe) => record(recipe)["name"])
143
+ : undefined,
144
+ buildable: result["buildable"],
145
+ upstreamBuildable: result["upstreamBuildable"],
146
+ downstreamBuildable: result["downstreamBuildable"],
147
+ tags: Array.isArray(tags) && tags.length > 0 ? tags : undefined,
148
+ dataQuality: Object.keys(quality).length > 0 && quality["numberItems"] !== 0
149
+ ? result["dataQualityStatus"]
150
+ : undefined,
151
+ lastBuild: shapeLastBuild(result["lastBuild"]),
152
+ lastModifiedOn: versioning["lastModifiedOn"] ?? timeline["lastModifiedOn"],
153
+ lastModifiedBy: record(versioning["lastModifiedBy"] ?? timeline["lastModifiedBy"])["login"],
154
+ });
155
+ }
156
+ /**
157
+ * Recursively remove empty strings, arrays and objects (including objects that
158
+ * become empty). `false`, `0` and `null` are kept. Array positions are
159
+ * preserved: an element that empties out stays as its empty value.
160
+ */
161
+ function pruneEmpty(value) {
162
+ if (typeof value === "string")
163
+ return value.length === 0 ? undefined : value;
164
+ if (Array.isArray(value)) {
165
+ if (value.length === 0)
166
+ return undefined;
167
+ return value.map((item) => pruneEmpty(item) ?? (typeof item === "string" ? "" : Array.isArray(item) ? [] : {}));
168
+ }
169
+ if (isRecord(value)) {
170
+ const pruned = {};
171
+ for (const [key, member,] of Object.entries(value)) {
172
+ const kept = pruneEmpty(member);
173
+ if (kept !== undefined)
174
+ pruned[key] = kept;
175
+ }
176
+ return Object.keys(pruned).length === 0 ? undefined : pruned;
177
+ }
178
+ return value;
179
+ }
180
+ function withoutKeys(source, keys) {
181
+ return Object.fromEntries(Object.entries(source).filter(([key,]) => !keys.includes(key)));
182
+ }
183
+ /** `dataset get`: the settings object minus metadata and empty/default noise. */
184
+ function shapeDatasetGet(result) {
185
+ if (!isRecord(result))
186
+ return result;
187
+ const settings = withoutKeys(result, DATASET_GET_DROPPED_KEYS);
188
+ const formatParams = settings["formatParams"];
189
+ if (isRecord(formatParams)) {
190
+ settings["formatParams"] = withoutKeys(formatParams, ["hiveSeparators",]);
191
+ }
192
+ return pruneEmpty(settings) ?? {};
193
+ }
194
+ const PERMISSION_KEY = /^can[A-Z]/;
195
+ /**
196
+ * `project get`: as `dataset get`, with the boolean permission flags
197
+ * (`can*`, `isProjectAdmin`) collapsed to the denied ones. Absent from the
198
+ * list means granted, so nothing is lost.
199
+ */
200
+ function shapeProjectGet(result) {
201
+ if (!isRecord(result))
202
+ return result;
203
+ const settings = withoutKeys(result, PROJECT_GET_DROPPED_KEYS);
204
+ const deniedPermissions = [];
205
+ for (const [key, value,] of Object.entries(settings)) {
206
+ if (typeof value !== "boolean" || !(PERMISSION_KEY.test(key) || key === "isProjectAdmin")) {
207
+ continue;
208
+ }
209
+ if (!value)
210
+ deniedPermissions.push(key);
211
+ delete settings[key];
212
+ }
213
+ const pruned = record(pruneEmpty(settings));
214
+ return { ...pruned, deniedPermissions, };
215
+ }
216
+ /** Compact read shapers by `resource.action`. */
217
+ const READ_SHAPERS = {
218
+ "job.get": shapeJobGet,
219
+ "dataset.info": shapeDatasetInfo,
220
+ "dataset.get": shapeDatasetGet,
221
+ "project.get": shapeProjectGet,
222
+ };
223
+ /**
224
+ * Compact a command result for agents: `*.list` items (filter, cap, compact
225
+ * fields) and the four large read commands. `--full` and `--fields` bypass
226
+ * the read shapers so the DSS object (or a projection of it) is returned.
227
+ */
228
+ export function shapeCommandResult(resource, action, result, flags) {
229
+ if (action === "list")
230
+ return shapeListResult(resource, result, flags);
231
+ const shaper = READ_SHAPERS[`${resource}.${action}`];
232
+ if (!shaper || flags["full"] === true || typeof flags["fields"] === "string")
233
+ return result;
234
+ return shaper(result);
235
+ }
@@ -49,7 +49,7 @@ export declare function buildVersionPayload(input: {
49
49
  export declare const PACKAGE_ROOT: string | undefined;
50
50
  export declare const CLI_VERSION: string;
51
51
  export declare const CLI_GIT_REVISION: string | undefined;
52
- export declare const AGENT_CONTRACT_VERSION = 3;
52
+ export declare const AGENT_CONTRACT_VERSION = 5;
53
53
  export declare const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
54
- export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
54
+ export declare const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v5.json";
55
55
  export declare function cliVersionResult(): CliVersionPayload;
@@ -1,8 +1,7 @@
1
1
  import { readdirSync, readFileSync, statSync, } from "node:fs";
2
2
  import { dirname, resolve, } from "node:path";
3
- import { fileURLToPath, } from "node:url";
4
3
  export function findPackageRoot() {
5
- let dir = dirname(fileURLToPath(import.meta.url));
4
+ let dir = import.meta.dir;
6
5
  for (let i = 0; i < 5; i++) {
7
6
  try {
8
7
  readFileSync(resolve(dir, "package.json"), "utf-8");
@@ -133,7 +132,7 @@ export function gitRevision(packageRoot) {
133
132
  const full = gitFullRevision(packageRoot);
134
133
  return full ? full.slice(0, 7) : undefined;
135
134
  }
136
- export function detectLoadSource(modulePath = fileURLToPath(import.meta.url)) {
135
+ export function detectLoadSource(modulePath = import.meta.path) {
137
136
  return modulePath.replaceAll("\\", "/").includes("/dist/") ? "dist" : "source";
138
137
  }
139
138
  export function buildMetadataRevision(packageRoot) {
@@ -204,9 +203,9 @@ export function buildVersionPayload(input) {
204
203
  export const PACKAGE_ROOT = findPackageRoot();
205
204
  export const CLI_VERSION = packageVersion(PACKAGE_ROOT);
206
205
  export const CLI_GIT_REVISION = gitRevision(PACKAGE_ROOT);
207
- export const AGENT_CONTRACT_VERSION = 3;
206
+ export const AGENT_CONTRACT_VERSION = 5;
208
207
  export const JSON_SCHEMA_DRAFT = "https://json-schema.org/draft/2020-12/schema";
209
- export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v3.json";
208
+ export const AGENT_CONTRACT_SCHEMA_ID = "https://clssck.github.io/dataiku-sdk/schemas/agent-contract-v5.json";
210
209
  export function cliVersionResult() {
211
210
  // Only code actually loaded from dist/ may report dist provenance; an
212
211
  // inherited DSS_LOAD_SOURCE on a source run is ignored.