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.
Files changed (61) hide show
  1. package/README.md +93 -15
  2. package/bin/dss.js +21 -2
  3. package/dist/build-metadata.json +1 -0
  4. package/dist/packages/types/src/index.d.ts +128 -3
  5. package/dist/packages/types/src/index.js +106 -3
  6. package/dist/src/cli/coerce.d.ts +2 -1
  7. package/dist/src/cli/coerce.js +84 -26
  8. package/dist/src/cli/commands/app.js +6 -6
  9. package/dist/src/cli/commands/code.js +2 -2
  10. package/dist/src/cli/commands/data-quality.js +50 -8
  11. package/dist/src/cli/commands/dataset.d.ts +23 -0
  12. package/dist/src/cli/commands/dataset.js +197 -12
  13. package/dist/src/cli/commands/folder.js +5 -4
  14. package/dist/src/cli/commands/future.js +2 -2
  15. package/dist/src/cli/commands/index.js +2 -0
  16. package/dist/src/cli/commands/job.js +26 -13
  17. package/dist/src/cli/commands/notebook.js +1 -1
  18. package/dist/src/cli/commands/project-git.d.ts +2 -0
  19. package/dist/src/cli/commands/project-git.js +563 -0
  20. package/dist/src/cli/commands/project.js +118 -14
  21. package/dist/src/cli/commands/recipe.js +14 -17
  22. package/dist/src/cli/commands/scenario.js +4 -4
  23. package/dist/src/cli/contract.d.ts +6 -4
  24. package/dist/src/cli/contract.js +595 -68
  25. package/dist/src/cli/doctor.d.ts +12 -2
  26. package/dist/src/cli/doctor.js +50 -19
  27. package/dist/src/cli/flags.js +23 -2
  28. package/dist/src/cli/helpers/cleanup.js +30 -1
  29. package/dist/src/cli/helpers/dataset.js +1 -1
  30. package/dist/src/cli/output.d.ts +5 -2
  31. package/dist/src/cli/output.js +13 -12
  32. package/dist/src/cli/types.d.ts +2 -0
  33. package/dist/src/cli/usage.d.ts +1 -1
  34. package/dist/src/cli/usage.js +2 -1
  35. package/dist/src/cli/version.d.ts +48 -6
  36. package/dist/src/cli/version.js +177 -11
  37. package/dist/src/cli.js +94 -39
  38. package/dist/src/client.d.ts +31 -1
  39. package/dist/src/client.js +39 -7
  40. package/dist/src/errors.d.ts +1 -1
  41. package/dist/src/index.d.ts +6 -4
  42. package/dist/src/index.js +3 -1
  43. package/dist/src/resources/connections.d.ts +3 -1
  44. package/dist/src/resources/connections.js +82 -21
  45. package/dist/src/resources/datasets.d.ts +43 -0
  46. package/dist/src/resources/datasets.js +241 -30
  47. package/dist/src/resources/folders.js +7 -3
  48. package/dist/src/resources/project-git.d.ts +154 -0
  49. package/dist/src/resources/project-git.js +427 -0
  50. package/dist/src/resources/projects.d.ts +36 -3
  51. package/dist/src/resources/projects.js +127 -16
  52. package/dist/src/resources/recipes.js +4 -4
  53. package/dist/src/schemas.d.ts +2 -2
  54. package/dist/src/schemas.js +1 -1
  55. package/dist/src/utils/project-archive.d.ts +32 -0
  56. package/dist/src/utils/project-archive.js +624 -0
  57. package/package.json +6 -5
  58. package/packages/types/dist/index.d.ts +128 -3
  59. package/packages/types/dist/index.js +106 -3
  60. package/plugin.json +1 -1
  61. package/skills/dataiku-dss/SKILL.md +26 -19
@@ -1,6 +1,16 @@
1
+ import { ClientValidationError, DataikuError, } from "../../errors.js";
1
2
  import { deepMerge, } from "../../utils/deep-merge.js";
2
- import { jsonInput, num, requiredJsonInput, } from "../coerce.js";
3
+ import { inspectProjectArchive, } from "../../utils/project-archive.js";
4
+ import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
5
+ import { jsonInput, num, requiredJsonInput, requiredStringFlag, } from "../coerce.js";
6
+ import { CommandResultFailure, } from "../output.js";
3
7
  import { requireArgs, UsageError, } from "../usage.js";
8
+ const INSPECT_ARCHIVE_USAGE = "dss project inspect-archive <file>";
9
+ /** Local, DSS-free archive inspection shared by the local handler and the command handler. */
10
+ function inspectArchiveCommand(args) {
11
+ requireArgs(args, 1, INSPECT_ARCHIVE_USAGE);
12
+ return inspectProjectArchive(args[0]);
13
+ }
4
14
  export const projectCommands = {
5
15
  list: {
6
16
  handler: (c) => c.projects.list(),
@@ -29,8 +39,8 @@ export const projectCommands = {
29
39
  map: {
30
40
  handler: (c, _a, f) => c.projects.map({
31
41
  projectKey: f["project-key"],
32
- maxNodes: num(f["max-nodes"]),
33
- maxEdges: num(f["max-edges"]),
42
+ maxNodes: num(f["max-nodes"], "--max-nodes"),
43
+ maxEdges: num(f["max-edges"], "--max-edges"),
34
44
  includeRaw: f["include-raw"] === true,
35
45
  }),
36
46
  usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw] [--project-key KEY]",
@@ -56,12 +66,75 @@ export const projectCommands = {
56
66
  },
57
67
  delete: {
58
68
  handler: async (c, a, f) => {
59
- requireArgs(a, 1, "dss project delete <projectKey> [--drop-data]");
60
- await c.projects.deleteProject(a[0], f["drop-data"] === true);
61
- return { deleted: a[0], };
69
+ const usage = "dss project delete <projectKey> [--drop-data] [--if-exists] [--dry-run] [--expect-project-incarnation HASH]";
70
+ requireArgs(a, 1, usage);
71
+ const projectKey = a[0];
72
+ const expectedIncarnation = f["expect-project-incarnation"] === undefined
73
+ ? undefined
74
+ : requiredStringFlag(f, "expect-project-incarnation", usage);
75
+ if (expectedIncarnation !== undefined
76
+ && !/^[0-9a-f]{64}$/.test(expectedIncarnation)) {
77
+ throw new UsageError("--expect-project-incarnation must be a 64-character lowercase SHA-256 hash.", "validation_failed");
78
+ }
79
+ const guarded = f["if-exists"] === true
80
+ || f["dry-run"] === true
81
+ || expectedIncarnation !== undefined;
82
+ if (!guarded) {
83
+ // Purely user-requested delete without any guard flag: keep the
84
+ // historical direct DELETE, with no preflight probe.
85
+ await c.projects.deleteProject(projectKey, f["drop-data"] === true);
86
+ return { deleted: projectKey, };
87
+ }
88
+ // Guarded path: existence and identity must be verified before any
89
+ // DELETE. A missing creationTag or an incarnation mismatch refuses
90
+ // before the DELETE under possible project-key reuse.
91
+ let currentIncarnation;
92
+ try {
93
+ const details = await c.projects.get(projectKey);
94
+ if (expectedIncarnation !== undefined) {
95
+ currentIncarnation = projectIncarnationHash(projectKey, details);
96
+ if (currentIncarnation === undefined) {
97
+ throw new ClientValidationError(`DSS did not return creationTag identity for project ${projectKey}. Refusing to delete without project-incarnation verification.`, "validation_failed", "Project-key reuse cannot be excluded. Refusing the DELETE; re-read the project and retry with a hash captured from that incarnation.", { projectKey, });
98
+ }
99
+ if (currentIncarnation !== expectedIncarnation) {
100
+ throw new ClientValidationError(`Project ${projectKey} is not the same project incarnation recorded for this operation. Refusing to delete after project-key reuse.`, "validation_failed", "Re-read the current project and retry only with an artifact captured from that incarnation.", {
101
+ projectKey,
102
+ expectedProjectIncarnationHash: expectedIncarnation,
103
+ currentProjectIncarnationHash: currentIncarnation,
104
+ });
105
+ }
106
+ }
107
+ }
108
+ catch (error) {
109
+ if (error instanceof DataikuError && error.status === 404) {
110
+ // Already absent: converge only when the caller asked for it.
111
+ if (f["if-exists"] !== true)
112
+ throw error;
113
+ return { deleted: false, alreadyAbsent: true, projectKey, };
114
+ }
115
+ throw error;
116
+ }
117
+ if (f["dry-run"] === true) {
118
+ return {
119
+ deleted: false,
120
+ dryRun: true,
121
+ projectKey,
122
+ ...(currentIncarnation !== undefined
123
+ ? { projectIncarnationHash: currentIncarnation, }
124
+ : {}),
125
+ };
126
+ }
127
+ await c.projects.deleteProject(projectKey, f["drop-data"] === true);
128
+ return {
129
+ deleted: true,
130
+ projectKey,
131
+ ...(currentIncarnation !== undefined
132
+ ? { projectIncarnationHash: currentIncarnation, }
133
+ : {}),
134
+ };
62
135
  },
63
- usage: "dss project delete <projectKey> [--drop-data]",
64
- description: "Delete a project (destructive). --drop-data also clears managed datasets.",
136
+ usage: "dss project delete <projectKey> [--drop-data] [--if-exists] [--dry-run] [--expect-project-incarnation HASH]",
137
+ description: "Delete a project (destructive). --drop-data also clears managed datasets; --if-exists treats an absent project as already deleted; --dry-run verifies without deleting; --expect-project-incarnation HASH refuses deletion unless the project still matches the recorded incarnation.",
65
138
  examples: ["dss project delete MY_PROJ",],
66
139
  },
67
140
  duplicate: {
@@ -87,13 +160,44 @@ export const projectCommands = {
87
160
  examples: ["dss project export MY_PROJ --output ./my_proj.zip",],
88
161
  },
89
162
  import: {
90
- handler: async (c, a) => {
91
- requireArgs(a, 1, "dss project import <filePath>");
92
- return c.projects.importProjectFromArchive(a[0]);
163
+ handler: async (c, a, f) => {
164
+ const usage = "dss project import <filePath> [--target-project-key KEY] [--data JSON|--data-file PATH|--stdin]";
165
+ requireArgs(a, 1, usage);
166
+ const settings = jsonInput(f) ?? {};
167
+ const rawTarget = f["target-project-key"];
168
+ const targetProjectKey = rawTarget?.trim();
169
+ if (rawTarget !== undefined && targetProjectKey === "") {
170
+ throw new UsageError(`--target-project-key must not be empty. Usage: ${usage}`);
171
+ }
172
+ const settingsTarget = settings.targetProjectKey;
173
+ if (settingsTarget !== undefined
174
+ && (typeof settingsTarget !== "string" || settingsTarget.trim() === "")) {
175
+ throw new UsageError(`targetProjectKey in import settings must be a non-empty string. Usage: ${usage}`);
176
+ }
177
+ if (targetProjectKey !== undefined
178
+ && settingsTarget !== undefined
179
+ && targetProjectKey !== settingsTarget.trim()) {
180
+ throw new UsageError(`--target-project-key conflicts with targetProjectKey in import settings. Usage: ${usage}`);
181
+ }
182
+ const result = await c.projects.importProjectFromArchive(a[0], targetProjectKey === undefined ? settings : { ...settings, targetProjectKey, });
183
+ if (result.success !== true) {
184
+ throw new CommandResultFailure({ ...result, resource: "project", action: "import", stage: "process", }, 2);
185
+ }
186
+ return result;
93
187
  },
94
- usage: "dss project import <filePath>",
95
- description: "Upload a project archive to prepare an import (returns a temporary import id).",
96
- examples: ["dss project import ./my_proj.zip",],
188
+ usage: "dss project import <filePath> [--target-project-key KEY] [--data JSON|--data-file PATH|--stdin]",
189
+ description: "Upload and process a project archive, optionally overriding its project key. The archive is validated locally before upload; verified success reports the used project key, explicit request/actual remapping, and the landed incarnation hash.",
190
+ examples: [
191
+ "dss project import ./my_proj.zip",
192
+ "dss project import ./my_proj.zip --target-project-key MY_PROJ",
193
+ ],
194
+ },
195
+ "inspect-archive": {
196
+ handler: async (_c, a) => inspectArchiveCommand(a),
197
+ localHandler: inspectArchiveCommand,
198
+ usage: INSPECT_ARCHIVE_USAGE,
199
+ 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.",
200
+ examples: ["dss project inspect-archive ./my_proj.zip",],
97
201
  },
98
202
  "permissions-get": {
99
203
  handler: (c, _a, f) => c.projects.getPermissions(f["project-key"]),
@@ -79,9 +79,9 @@ export const recipeCommands = {
79
79
  logFilter: jobLogFilterFromFlag(f["log-filter"]),
80
80
  maxLogLines: maxLogLinesFromFlags(f),
81
81
  partition: f["partition"],
82
- pollIntervalMs: num(f["poll-interval"]),
82
+ pollIntervalMs: num(f["poll-interval"], "--poll-interval"),
83
83
  projectKey: pk,
84
- timeoutMs: num(f["timeout"]),
84
+ timeoutMs: num(f["timeout"], "--timeout"),
85
85
  summary: f["summary"] === true,
86
86
  wait,
87
87
  };
@@ -164,7 +164,7 @@ export const recipeCommands = {
164
164
  handler: async (c, _a, f) => {
165
165
  const type = f["type"];
166
166
  if (!type) {
167
- throw new UsageError("--type is required. Usage: dss recipe create --type TYPE --input DS (--output DS | --output-folder FOLDER_ID)");
167
+ throw new UsageError("--type is required. Usage: dss recipe create --type TYPE [--input DS] (--output DS | --output-folder FOLDER_ID)");
168
168
  }
169
169
  const outputDataset = f["output"];
170
170
  const outputFolder = f["output-folder"];
@@ -172,7 +172,7 @@ export const recipeCommands = {
172
172
  throw new UsageError("--output and --output-folder are mutually exclusive.");
173
173
  }
174
174
  if (!outputDataset && !outputFolder) {
175
- throw new UsageError("--output or --output-folder is required. Usage: dss recipe create --type TYPE --input DS (--output DS | --output-folder FOLDER_ID)");
175
+ throw new UsageError("--output or --output-folder is required. Usage: dss recipe create --type TYPE [--input DS] (--output DS | --output-folder FOLDER_ID)");
176
176
  }
177
177
  if (outputFolder && !f["output-connection"]) {
178
178
  throw new UsageError("--output-connection is required when using --output-folder.");
@@ -197,10 +197,7 @@ export const recipeCommands = {
197
197
  }
198
198
  fuzzyDistance = normalized;
199
199
  }
200
- const fuzzyThreshold = num(f["fuzzy-threshold"]);
201
- if (typeof f["fuzzy-threshold"] === "string" && fuzzyThreshold === undefined) {
202
- throw new UsageError("--fuzzy-threshold must be a finite number.", "validation_failed");
203
- }
200
+ const fuzzyThreshold = num(f["fuzzy-threshold"], "--fuzzy-threshold");
204
201
  const payload = {
205
202
  type,
206
203
  name,
@@ -255,8 +252,8 @@ export const recipeCommands = {
255
252
  },], pk);
256
253
  return { created: createdName, resource: "recipe", ...created, ...moved, };
257
254
  },
258
- usage: "dss recipe create --type TYPE --input DS[,DS2] (--output DS | --output-folder FOLDER_ID) [--name NAME] [--output-connection CONN] [--zone ZONE|--zone-id ID] [--if-not-exists] [--dry-run] [--join-on COL|LEFT=RIGHT[,...]] [--join-type LEFT|INNER|RIGHT|FULL] [--fuzzy-on COL|LEFT=RIGHT[,...]] [--fuzzy-distance DAMERAU_LEVENSHTEIN|HAMMING|JACCARD|COSINE|EUCLIDEAN] [--fuzzy-threshold N] [--normalize] [--project-key KEY]",
259
- description: "Create a recipe with one or more inputs and a dataset or managed-folder output.",
255
+ usage: "dss recipe create --type TYPE [--input DS[,DS2]] (--output DS | --output-folder FOLDER_ID) [--name NAME] [--output-connection CONN] [--zone ZONE|--zone-id ID] [--if-not-exists] [--dry-run] [--join-on COL|LEFT=RIGHT[,...]] [--join-type LEFT|INNER|RIGHT|FULL] [--fuzzy-on COL|LEFT=RIGHT[,...]] [--fuzzy-distance DAMERAU_LEVENSHTEIN|HAMMING|JACCARD|COSINE|EUCLIDEAN] [--fuzzy-threshold N] [--normalize] [--project-key KEY]",
256
+ description: "Create a recipe with optional inputs and a dataset or managed-folder output.",
260
257
  examples: [
261
258
  "dss recipe create --type python --input raw_orders,lookup --output orders_clean",
262
259
  "dss recipe create --type python --input orders --input customers --output orders_clean --zone Experiments",
@@ -485,23 +482,23 @@ export const recipeCommands = {
485
482
  }
486
483
  return payload;
487
484
  },
488
- usage: "dss recipe get-payload <name> [--raw] [--output PATH] [--project-key KEY]",
489
- description: "Print the recipe code payload as JSON; use --raw for raw bytes, not JSON.",
485
+ usage: "dss recipe get-payload <name> [--output PATH] [--project-key KEY]",
486
+ description: "Print the recipe code payload as a JSON string, or write it to a file with --output.",
490
487
  examples: [
491
- "dss recipe get-payload compute_orders --raw",
488
+ "dss recipe get-payload compute_orders",
492
489
  "dss recipe get-payload compute_orders -o code.py",
493
490
  ],
494
491
  },
495
492
  cat: {
496
493
  handler: (c, a, f) => {
497
- requireArgs(a, 1, "dss recipe cat <name> [--raw]");
494
+ requireArgs(a, 1, "dss recipe cat <name>");
498
495
  return c.recipes.getPayload(a[0], {
499
496
  projectKey: f["project-key"],
500
497
  });
501
498
  },
502
- usage: "dss recipe cat <name> [--raw] [--project-key KEY]",
503
- description: "Print a recipe code payload as JSON; use --raw for raw bytes, not JSON.",
504
- examples: ["dss recipe cat compute_orders --raw",],
499
+ usage: "dss recipe cat <name> [--project-key KEY]",
500
+ description: "Print the recipe code payload as a JSON string.",
501
+ examples: ["dss recipe cat compute_orders",],
505
502
  },
506
503
  "set-payload": {
507
504
  handler: async (c, a, f) => {
@@ -23,8 +23,8 @@ export const scenarioCommands = {
23
23
  requireArgs(a, 1, "dss scenario run <id>");
24
24
  const pk = f["project-key"];
25
25
  const options = {
26
- pollIntervalMs: num(f["poll-interval"]),
27
- timeoutMs: num(f["timeout"]),
26
+ pollIntervalMs: num(f["poll-interval"], "--poll-interval"),
27
+ timeoutMs: num(f["timeout"], "--timeout"),
28
28
  };
29
29
  if (f["dry-run"] === true) {
30
30
  return {
@@ -51,8 +51,8 @@ export const scenarioCommands = {
51
51
  requireArgs(a, 1, "dss scenario run-and-wait <id>");
52
52
  const pk = f["project-key"];
53
53
  const options = {
54
- pollIntervalMs: num(f["poll-interval"]),
55
- timeoutMs: num(f["timeout"]),
54
+ pollIntervalMs: num(f["poll-interval"], "--poll-interval"),
55
+ timeoutMs: num(f["timeout"], "--timeout"),
56
56
  };
57
57
  if (f["dry-run"] === true) {
58
58
  return {
@@ -39,7 +39,7 @@ export interface CommandStructuredExample {
39
39
  }
40
40
  export interface CommandUnsafeOutput {
41
41
  condition: string;
42
- kind: "raw-stdout" | "local-file";
42
+ kind: "local-file";
43
43
  detail: string;
44
44
  safeAlternative?: string;
45
45
  }
@@ -80,18 +80,19 @@ export interface CommandRegistryEntry {
80
80
  cleanupHint?: string;
81
81
  agentContractVersion: number;
82
82
  }
83
- export declare const COMMANDS_USAGE = "dss commands run [--fields PATHS] [--json]";
83
+ export declare const COMMANDS_USAGE = "dss commands run [--fields PATHS] [--output PATH]";
84
84
  export declare const AGENT_CONTRACT_COMMAND = "dss agent contract";
85
- export declare const AGENT_CONTRACT_USAGE = "dss agent contract [--fields PATHS] [--json]";
85
+ export declare const AGENT_CONTRACT_USAGE = "dss agent contract [--fields PATHS]";
86
86
  export declare const CLEANUP_USAGE = "dss cleanup --file PATH [--dry-run|--apply] [--continue-on-error]";
87
87
  export declare function isAllowedCleanupAction(resource: string, action: string): boolean;
88
88
  export declare function buildCommandSchemas(resource: string, action: string, flags: CommandFlagMetadata[], requiredFlags: string[], requiredOneOf: CommandFlagChoice[], payloadSchema: CommandPayloadSchema | undefined, outputShape: CommandOutputShape, usage: string): CommandAgentSchemas;
89
- export declare function inferRequiresProject(resource: string, _action: string, usage: string): boolean;
89
+ export declare function inferRequiresProject(resource: string, action: string, usage: string): boolean;
90
90
  export declare function inferInputContract(usage: string): CommandInputContract;
91
91
  export declare function supportsCleanupLedger(resource: string, action: string): boolean;
92
92
  export declare function inferCleanupHint(resource: string, action: string): string | undefined;
93
93
  export declare function buildCommandRegistry(): Record<string, Record<string, CommandRegistryEntry>>;
94
94
  export declare function agentContractJsonSchema(): Record<string, unknown>;
95
+ export declare function commandActionSummary(registry: Record<string, Record<string, CommandRegistryEntry>>): Record<string, string[]>;
95
96
  export declare function buildAgentContract(): Record<string, unknown>;
96
97
  export declare function commandPlanShape(resource: string, action: string, args: string[], flags: Record<string, string | boolean>, entry: CommandRegistryEntry, projectKey: string | undefined): {
97
98
  endpoint?: string;
@@ -100,6 +101,7 @@ export declare function commandPlanShape(resource: string, action: string, args:
100
101
  payload?: unknown;
101
102
  localWrites?: unknown;
102
103
  wait?: unknown;
104
+ requests?: unknown;
103
105
  };
104
106
  export declare function buildMutationPlan(resource: string, action: string, meta: CommandMeta, args: string[], flags: Record<string, string | boolean>): Record<string, unknown>;
105
107
  /**