dataiku-sdk 3.6.0 → 3.7.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 (127) hide show
  1. package/README.md +14 -6
  2. package/bin/bun-version.js +14 -0
  3. package/bin/dss.js +23 -12
  4. package/dist/build-metadata.json +1 -1
  5. package/dist/packages/types/src/index.d.ts +74 -1
  6. package/dist/packages/types/src/index.js +53 -1
  7. package/dist/src/cli/coerce.d.ts +1 -0
  8. package/dist/src/cli/coerce.js +19 -15
  9. package/dist/src/cli/command-syntax.json +3521 -416
  10. package/dist/src/cli/commands/analysis.js +19 -1
  11. package/dist/src/cli/commands/bundle.js +22 -0
  12. package/dist/src/cli/commands/connection.js +26 -2
  13. package/dist/src/cli/commands/data-quality.js +33 -1
  14. package/dist/src/cli/commands/dataset.js +35 -4
  15. package/dist/src/cli/commands/discussion.js +15 -0
  16. package/dist/src/cli/commands/doctor.js +1 -1
  17. package/dist/src/cli/commands/flow.d.ts +5 -0
  18. package/dist/src/cli/commands/flow.js +57 -0
  19. package/dist/src/cli/commands/future.js +8 -0
  20. package/dist/src/cli/commands/index.js +6 -0
  21. package/dist/src/cli/commands/job.js +29 -6
  22. package/dist/src/cli/commands/metrics.js +21 -1
  23. package/dist/src/cli/commands/ml-task.d.ts +5 -0
  24. package/dist/src/cli/commands/ml-task.js +86 -16
  25. package/dist/src/cli/commands/mlflow.d.ts +11 -0
  26. package/dist/src/cli/commands/mlflow.js +121 -0
  27. package/dist/src/cli/commands/plugin.js +5 -11
  28. package/dist/src/cli/commands/project-deployer.js +70 -0
  29. package/dist/src/cli/commands/project-library.js +2 -5
  30. package/dist/src/cli/commands/project.js +8 -0
  31. package/dist/src/cli/commands/recipe.d.ts +9 -0
  32. package/dist/src/cli/commands/recipe.js +93 -7
  33. package/dist/src/cli/commands/saved-model.d.ts +1 -1
  34. package/dist/src/cli/commands/saved-model.js +9 -76
  35. package/dist/src/cli/commands/streaming-endpoint.js +30 -1
  36. package/dist/src/cli/commands/webapp.js +8 -0
  37. package/dist/src/cli/commands/wiki.js +22 -0
  38. package/dist/src/cli/commands/workspace.js +8 -0
  39. package/dist/src/cli/contract.d.ts +16 -15
  40. package/dist/src/cli/contract.js +139 -3404
  41. package/dist/src/cli/doctor.d.ts +13 -0
  42. package/dist/src/cli/doctor.js +41 -1
  43. package/dist/src/cli/flags.d.ts +7 -1
  44. package/dist/src/cli/flags.js +78 -6
  45. package/dist/src/cli/helpers/app-successor.js +1 -2
  46. package/dist/src/cli/helpers/documentation.d.ts +21 -0
  47. package/dist/src/cli/helpers/documentation.js +53 -0
  48. package/dist/src/cli/helpers/trained-model.d.ts +26 -0
  49. package/dist/src/cli/helpers/trained-model.js +144 -0
  50. package/dist/src/cli/list-output.d.ts +7 -0
  51. package/dist/src/cli/list-output.js +77 -0
  52. package/dist/src/cli/list-shapes.d.ts +21 -0
  53. package/dist/src/cli/list-shapes.js +111 -0
  54. package/dist/src/cli/output-schemas.d.ts +0 -14
  55. package/dist/src/cli/output-schemas.js +4 -7
  56. package/dist/src/cli/output.d.ts +0 -1
  57. package/dist/src/cli/output.js +27 -19
  58. package/dist/src/cli/plans.d.ts +14 -0
  59. package/dist/src/cli/plans.js +3684 -0
  60. package/dist/src/cli/usage.d.ts +7 -0
  61. package/dist/src/cli/usage.js +20 -1
  62. package/dist/src/cli/version.d.ts +2 -2
  63. package/dist/src/cli/version.js +2 -2
  64. package/dist/src/cli.js +127 -113
  65. package/dist/src/client.d.ts +52 -43
  66. package/dist/src/client.js +267 -96
  67. package/dist/src/errors.d.ts +11 -1
  68. package/dist/src/errors.js +31 -47
  69. package/dist/src/generated/action-output-schemas.json +345 -89
  70. package/dist/src/index.d.ts +5 -1
  71. package/dist/src/index.js +3 -0
  72. package/dist/src/resources/analyses.d.ts +8 -0
  73. package/dist/src/resources/analyses.js +13 -1
  74. package/dist/src/resources/bundles.d.ts +34 -0
  75. package/dist/src/resources/bundles.js +67 -0
  76. package/dist/src/resources/connections.d.ts +9 -0
  77. package/dist/src/resources/connections.js +15 -0
  78. package/dist/src/resources/data-quality.d.ts +20 -0
  79. package/dist/src/resources/data-quality.js +30 -0
  80. package/dist/src/resources/dataset-create.d.ts +19 -0
  81. package/dist/src/resources/dataset-create.js +66 -0
  82. package/dist/src/resources/datasets.d.ts +25 -15
  83. package/dist/src/resources/datasets.js +59 -111
  84. package/dist/src/resources/discussions.d.ts +5 -0
  85. package/dist/src/resources/discussions.js +7 -0
  86. package/dist/src/resources/documentation.d.ts +25 -0
  87. package/dist/src/resources/documentation.js +16 -0
  88. package/dist/src/resources/flow.d.ts +48 -0
  89. package/dist/src/resources/flow.js +65 -0
  90. package/dist/src/resources/futures.d.ts +23 -0
  91. package/dist/src/resources/futures.js +32 -0
  92. package/dist/src/resources/jobs.js +102 -2
  93. package/dist/src/resources/metrics.d.ts +10 -0
  94. package/dist/src/resources/metrics.js +10 -0
  95. package/dist/src/resources/ml-tasks.d.ts +65 -2
  96. package/dist/src/resources/ml-tasks.js +102 -28
  97. package/dist/src/resources/mlflow-extension.d.ts +73 -0
  98. package/dist/src/resources/mlflow-extension.js +105 -0
  99. package/dist/src/resources/projects.d.ts +6 -0
  100. package/dist/src/resources/projects.js +8 -0
  101. package/dist/src/resources/recipe-create.d.ts +65 -0
  102. package/dist/src/resources/recipe-create.js +338 -0
  103. package/dist/src/resources/recipes.d.ts +28 -29
  104. package/dist/src/resources/recipes.js +126 -245
  105. package/dist/src/resources/saved-models.d.ts +10 -6
  106. package/dist/src/resources/saved-models.js +16 -14
  107. package/dist/src/resources/scenarios.js +8 -14
  108. package/dist/src/resources/streaming-endpoints.d.ts +12 -0
  109. package/dist/src/resources/streaming-endpoints.js +15 -0
  110. package/dist/src/resources/trained-model.d.ts +60 -0
  111. package/dist/src/resources/trained-model.js +98 -0
  112. package/dist/src/resources/webapps.d.ts +8 -0
  113. package/dist/src/resources/webapps.js +10 -0
  114. package/dist/src/resources/wiki.d.ts +5 -0
  115. package/dist/src/resources/wiki.js +7 -0
  116. package/dist/src/resources/workspaces.d.ts +2 -0
  117. package/dist/src/resources/workspaces.js +4 -0
  118. package/dist/src/utils/response-file.js +20 -9
  119. package/dist/src/utils/secret-sanitize.d.ts +7 -0
  120. package/dist/src/utils/secret-sanitize.js +46 -0
  121. package/package.json +25 -26
  122. package/plugin.json +1 -1
  123. package/skills/dataiku-dss/SKILL.md +1 -1
  124. package/skills/dataiku-dss/references/authentication.md +3 -1
  125. package/skills/dataiku-dss/references/coding.md +1 -0
  126. package/skills/dataiku-dss/references/discovery.md +4 -6
  127. package/skills/dataiku-dss/references/troubleshooting.md +3 -2
@@ -0,0 +1,121 @@
1
+ import { num, parseBooleanOption, splitCsvFlag, } from "../coerce.js";
2
+ import { executionMode, } from "../flags.js";
3
+ import { withUsage, } from "../syntax.js";
4
+ import { UsageError, } from "../usage.js";
5
+ /** `dss mlflow set-inference-info` flags → SDK inference info (shared with --plan). */
6
+ export function inferenceInfoFromFlags(runId, f) {
7
+ const predictionType = f["prediction-type"];
8
+ if (typeof predictionType !== "string") {
9
+ throw new UsageError("--prediction-type is required.", "missing_required_flag");
10
+ }
11
+ const classes = splitCsvFlag(f["classes"]);
12
+ return {
13
+ runId,
14
+ predictionType: predictionType.toUpperCase(),
15
+ ...(classes.length > 0 ? { classes, } : {}),
16
+ ...(typeof f["code-env"] === "string" ? { codeEnvName: f["code-env"], } : {}),
17
+ ...(typeof f["target"] === "string" ? { target: f["target"], } : {}),
18
+ };
19
+ }
20
+ /** `dss mlflow deploy-run` flags → SDK deploy options (shared with --plan). */
21
+ export function deployRunOptionsFromFlags(runId, savedModelId, f) {
22
+ const classLabels = splitCsvFlag(f["classes"]);
23
+ const setActive = f["set-active"];
24
+ const threshold = f["binary-classification-threshold"];
25
+ const optimal = f["use-optimal-threshold"];
26
+ const skipReports = f["skip-expensive-reports"];
27
+ return {
28
+ runId,
29
+ savedModelId,
30
+ ...(typeof f["version-id"] === "string" ? { versionId: f["version-id"], } : {}),
31
+ ...(typeof f["model-name"] === "string" ? { modelName: f["model-name"], } : {}),
32
+ ...(typeof f["evaluation-dataset"] === "string"
33
+ ? { evaluationDataset: f["evaluation-dataset"], }
34
+ : {}),
35
+ ...(typeof f["target"] === "string" ? { targetColumnName: f["target"], } : {}),
36
+ ...(classLabels.length > 0 ? { classLabels, } : {}),
37
+ ...(typeof f["code-env"] === "string" ? { codeEnvName: f["code-env"], } : {}),
38
+ ...(setActive !== undefined ? { activate: parseBooleanOption(setActive, "--set-active"), } : {}),
39
+ ...(threshold !== undefined
40
+ ? { binaryClassificationThreshold: num(threshold, "--binary-classification-threshold"), }
41
+ : {}),
42
+ ...(optimal !== undefined
43
+ ? { useOptimalThreshold: parseBooleanOption(optimal, "--use-optimal-threshold"), }
44
+ : {}),
45
+ ...(skipReports !== undefined
46
+ ? { skipExpensiveReports: parseBooleanOption(skipReports, "--skip-expensive-reports"), }
47
+ : {}),
48
+ };
49
+ }
50
+ /** `dss mlflow create-experiments-dataset` flags → SDK options (shared with --plan). */
51
+ export function experimentsDatasetOptionsFromFlags(datasetName, f) {
52
+ const experimentIds = splitCsvFlag(f["experiments"]);
53
+ const viewType = f["view-type"];
54
+ const format = f["format"];
55
+ return {
56
+ datasetName,
57
+ ...(experimentIds.length > 0 ? { experimentIds, } : {}),
58
+ ...(typeof viewType === "string"
59
+ ? {
60
+ viewType: viewType.toUpperCase(),
61
+ }
62
+ : {}),
63
+ ...(typeof f["filter"] === "string" ? { filter: f["filter"], } : {}),
64
+ ...(typeof format === "string"
65
+ ? { format: format.toUpperCase(), }
66
+ : {}),
67
+ };
68
+ }
69
+ export const mlflowCommands = withUsage("mlflow", {
70
+ "list-models": {
71
+ handler: (c, a, f) => c.mlflowExtension.listModels(a[0], f["project-key"]),
72
+ description: "List the models an MLflow experiment-tracking run logged ({runId, artifactPath}).",
73
+ examples: ["dss mlflow list-models RUN_ID",],
74
+ },
75
+ "set-inference-info": {
76
+ handler: async (c, a, f) => {
77
+ await c.mlflowExtension.setRunInferenceInfo(inferenceInfoFromFlags(a[0], f), f["project-key"]);
78
+ return { updated: a[0], resource: "mlflow", };
79
+ },
80
+ description: "Record an MLflow run's prediction type, classes, code env, and target so deploy-run can use them.",
81
+ examples: [
82
+ "dss mlflow set-inference-info RUN_ID --prediction-type MULTICLASS --classes a,b,c --target species",
83
+ ],
84
+ },
85
+ "deploy-run": {
86
+ handler: (c, a, f) => c.mlflowExtension.deployRun(deployRunOptionsFromFlags(a[0], a[1], f), f["project-key"]),
87
+ description: "Deploy an MLflow run's model as a version of an existing saved model (default version id: timestamp); --evaluation-dataset evaluates it. Synchronous: DSS loads the model in its code env, often past 30 s, so raise --request-timeout.",
88
+ examples: [
89
+ "dss mlflow deploy-run RUN_ID SAVED_MODEL_ID --version-id v2 --code-env py311_mlflow --request-timeout 300000",
90
+ ],
91
+ },
92
+ "create-experiments-dataset": {
93
+ handler: async (c, a, f) => {
94
+ await c.mlflowExtension.createExperimentsDataset(experimentsDatasetOptionsFromFlags(a[0], f), f["project-key"]);
95
+ return { created: a[0], resource: "mlflow", };
96
+ },
97
+ description: "Create a virtual dataset exposing the project's MLflow experiment-tracking runs.",
98
+ examples: ["dss mlflow create-experiments-dataset mlflow_runs --experiments FGXlefMU",],
99
+ },
100
+ "garbage-collect": {
101
+ handler: async (c, _a, f) => {
102
+ if (executionMode(f).dryRun) {
103
+ return { dryRun: true, action: "garbage-collect", resource: "mlflow", };
104
+ }
105
+ await c.mlflowExtension.garbageCollect(f["project-key"]);
106
+ return { garbageCollected: true, resource: "mlflow", };
107
+ },
108
+ description: "Permanently delete the project's MLflow experiments and runs marked as deleted.",
109
+ examples: ["dss mlflow garbage-collect --dry-run",],
110
+ },
111
+ "clean-db": {
112
+ handler: async (c, _a, f) => {
113
+ if (executionMode(f).dryRun)
114
+ return { dryRun: true, action: "clean-db", resource: "mlflow", };
115
+ await c.mlflowExtension.cleanDb(f["project-key"]);
116
+ return { cleaned: true, resource: "mlflow", };
117
+ },
118
+ description: "Admin: delete ALL MLflow experiment-tracking data of the project (experiments, runs, params, metrics, tags). Irreversible.",
119
+ examples: ["dss mlflow clean-db --plan",],
120
+ },
121
+ });
@@ -1,6 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import { validatedPluginGitUrl, validatePluginDestinationPath, validatePluginId, validatePluginName, validatePluginPath, } from "../../resources/plugins.js";
3
- import { num, readStdinText, } from "../coerce.js";
3
+ import { writeResponseToFile, } from "../../utils/response-file.js";
4
+ import { num, readStdinText, sha256Hex, } from "../coerce.js";
4
5
  import { executionMode, } from "../flags.js";
5
6
  import { planResult, } from "../output.js";
6
7
  import { commandUsage, withUsage, } from "../syntax.js";
@@ -188,7 +189,7 @@ export const pluginCommands = withUsage("plugin", {
188
189
  }
189
190
  const pluginId = validatePluginId(a[0]);
190
191
  const res = await c.plugins.download(pluginId);
191
- const bytes = await writeResponseToFileForPlugin(out, res);
192
+ const bytes = await writeResponseToFile(out, res);
192
193
  return { path: out, bytes, };
193
194
  },
194
195
  description: "Download a development plugin as a zip archive to a local file.",
@@ -598,7 +599,7 @@ export const pluginCommands = withUsage("plugin", {
598
599
  const out = f["output"];
599
600
  if (typeof out === "string" && out.trim() !== "") {
600
601
  const res = await c.plugins.downloadFile(pluginId, path);
601
- const bytes = await writeResponseToFileForPlugin(out, res);
602
+ const bytes = await writeResponseToFile(out, res);
602
603
  return { path: out, bytes, };
603
604
  }
604
605
  return { data: await c.plugins.getFile(pluginId, path), };
@@ -634,10 +635,7 @@ export const pluginCommands = withUsage("plugin", {
634
635
  : await readStdinText();
635
636
  await c.plugins.putFile(pluginId, path, content);
636
637
  const bytes = typeof content === "string" ? Buffer.byteLength(content, "utf8") : content.length;
637
- const hasher = new Bun.CryptoHasher("sha256");
638
- hasher.update(typeof content === "string" ? content : new Uint8Array(content));
639
- const sha256 = hasher.digest("hex");
640
- return { written: path, pluginId, bytes, sha256, };
638
+ return { written: path, pluginId, bytes, sha256: sha256Hex(content), };
641
639
  },
642
640
  description: "Create or replace a development plugin file with text or binary content; reports the written byte count and sha256.",
643
641
  examples: [
@@ -760,7 +758,3 @@ function readSettingsInput(flags) {
760
758
  }
761
759
  return parsed;
762
760
  }
763
- async function writeResponseToFileForPlugin(path, res) {
764
- const { writeResponseToFile, } = await import("../../utils/response-file.js");
765
- return writeResponseToFile(path, res);
766
- }
@@ -103,4 +103,74 @@ export const projectDeployerCommands = withUsage("project-deployer", {
103
103
  description: "Create a Project Deployer infrastructure.",
104
104
  examples: ["dss project-deployer create-infra --data-file infra.json",],
105
105
  },
106
+ "list-stages": {
107
+ handler: (c) => c.projectDeployer.listStages(),
108
+ description: "List the Project Deployer stages (e.g. Development, Test, Production).",
109
+ examples: ["dss project-deployer list-stages",],
110
+ },
111
+ "get-infra": {
112
+ handler: (c, a) => c.projectDeployer.getInfra(a[0]),
113
+ description: "Get a Project Deployer infrastructure (stage, automation node, deployments).",
114
+ examples: ["dss project-deployer get-infra prod-infra",],
115
+ },
116
+ "infra-settings": {
117
+ handler: (c, a) => c.projectDeployer.getInfraSettings(a[0]),
118
+ description: "Get an infrastructure's settings (permissions, automation node URL, connection remappings).",
119
+ examples: ["dss project-deployer infra-settings prod-infra",],
120
+ },
121
+ "save-infra-settings": {
122
+ handler: async (c, a, f) => {
123
+ await c.projectDeployer.saveInfraSettings(a[0], requiredJsonInput(f, "--data, --data-file, or --stdin is required (settings from infra-settings)."));
124
+ return { updated: a[0], resource: "project-deployer", };
125
+ },
126
+ description: "Save infrastructure settings obtained from infra-settings, edited.",
127
+ examples: ["dss project-deployer save-infra-settings prod-infra --data-file infra.json",],
128
+ },
129
+ "delete-infra": {
130
+ handler: async (c, a) => {
131
+ await c.projectDeployer.deleteInfra(a[0]);
132
+ return { deleted: a[0], resource: "project-deployer", };
133
+ },
134
+ description: "Delete an infrastructure that no deployment uses.",
135
+ examples: ["dss project-deployer delete-infra old-infra --plan",],
136
+ },
137
+ "deployment-settings": {
138
+ handler: (c, a) => c.projectDeployer.getDeploymentSettings(a[0]),
139
+ description: "Get a deployment's settings (infra, bundle, remappings); edit and send to save-deployment-settings.",
140
+ examples: ["dss project-deployer deployment-settings v1-on-prod",],
141
+ },
142
+ "governance-status": {
143
+ handler: (c, a, f) => c.projectDeployer.getGovernanceStatus(a[0], f["bundle-id"]),
144
+ description: "Governance status of a deployment's bundle (or --bundle-id): messages on whether it may be deployed.",
145
+ examples: ["dss project-deployer governance-status v1-on-prod",],
146
+ },
147
+ "delete-project": {
148
+ handler: async (c, a) => {
149
+ await c.projectDeployer.deleteProject(a[0]);
150
+ return { deleted: a[0], resource: "project-deployer", };
151
+ },
152
+ description: "Delete a published project that no deployment uses.",
153
+ examples: ["dss project-deployer delete-project MYPROJECT --plan",],
154
+ },
155
+ "project-settings": {
156
+ handler: (c, a) => c.projectDeployer.getProjectSettings(a[0]),
157
+ description: "Get a published project's settings (owner, permissions).",
158
+ examples: ["dss project-deployer project-settings MYPROJECT",],
159
+ },
160
+ "save-project-settings": {
161
+ handler: async (c, a, f) => {
162
+ await c.projectDeployer.saveProjectSettings(a[0], requiredJsonInput(f, "--data, --data-file, or --stdin is required (settings from project-settings)."));
163
+ return { updated: a[0], resource: "project-deployer", };
164
+ },
165
+ description: "Save published project settings obtained from project-settings, edited.",
166
+ examples: ["dss project-deployer save-project-settings MYPROJECT --data-file published.json",],
167
+ },
168
+ "delete-bundle": {
169
+ handler: async (c, a) => {
170
+ await c.projectDeployer.deleteBundle(a[0], a[1]);
171
+ return { deleted: a[1], resource: "project-deployer", project: a[0], };
172
+ },
173
+ description: "Delete a bundle of a published project that no deployment uses.",
174
+ examples: ["dss project-deployer delete-bundle MYPROJECT v1 --plan",],
175
+ },
106
176
  });
@@ -8,9 +8,6 @@ import { commandUsage, withUsage, } from "../syntax.js";
8
8
  import { requireArgs, UsageError, } from "../usage.js";
9
9
  const PROJECT_LIBRARY_EXIT_CODES = { usage: 1, error: 2, transient: 3, };
10
10
  const PUT_FLAG_ERROR_HINT = "Pass a 64-character SHA-256 hex digest (lowercase or uppercase).";
11
- function sha256BytesHex(bytes) {
12
- return new Bun.CryptoHasher("sha256").update(bytes).digest("hex");
13
- }
14
11
  function projectLibraryContentsEndpoint(client, projectKey, path) {
15
12
  return encodedProjectEndpoint(client, projectKey, `/libraries/contents/${encodeLibraryPath(path)}`);
16
13
  }
@@ -43,7 +40,7 @@ export function projectLibraryPutPayload(flags) {
43
40
  contentSource: "file",
44
41
  file: flags["file"],
45
42
  bytes: bytes.length,
46
- sha256: sha256BytesHex(bytes),
43
+ sha256: sha256Hex(bytes),
47
44
  ...(expectSha256 ? { expectSha256, } : {}),
48
45
  };
49
46
  }
@@ -90,7 +87,7 @@ export const projectLibraryCommands = withUsage("project-library", {
90
87
  throw new UsageError("--output PATH is required.", "missing_required_flag");
91
88
  const bytes = await c.projectLibrary.getFileBytes(a[0], f["project-key"]);
92
89
  const written = await Bun.write(out, bytes, { createPath: false, });
93
- return { path: out, bytes: written, sha256: sha256BytesHex(bytes), };
90
+ return { path: out, bytes: written, sha256: sha256Hex(bytes), };
94
91
  },
95
92
  description: "Download a project library file's raw bytes to a local file.",
96
93
  examples: ["dss project-library get-bytes static/logo.png --output ./logo.png",],
@@ -278,4 +278,12 @@ export const projectCommands = withUsage("project", {
278
278
  description: "Update a project's settings via JSON merge.",
279
279
  examples: ["dss project settings-set --data-file settings.json --project-key MY_PROJ",],
280
280
  },
281
+ "push-to-git-remote": {
282
+ handler: async (c, a, f) => {
283
+ await c.projects.pushToGitRemote(a[0], f["project-key"]);
284
+ return { pushed: a[0], resource: "project", };
285
+ },
286
+ description: "Push the project to a Git remote already declared in DSS (per-project Git mode). Branches, commits, pulls, and remotes: dss project-git.",
287
+ examples: ["dss project push-to-git-remote origin --plan",],
288
+ },
281
289
  });
@@ -13,6 +13,15 @@ export declare function recipeCreateOptionsFromFlags(f: Record<string, string |
13
13
  fuzzyDistance?: string | undefined;
14
14
  fuzzyThreshold?: number | undefined;
15
15
  fuzzyNormalize: boolean;
16
+ orderBy?: string | undefined;
17
+ formula?: string | undefined;
18
+ filter?: string | undefined;
16
19
  projectKey: string | undefined;
20
+ aggregate?: string[] | undefined;
21
+ dropColumns?: string[] | undefined;
22
+ fillEmpty?: string[] | undefined;
23
+ groupBy?: string[] | undefined;
24
+ keepColumns?: string[] | undefined;
25
+ rename?: string[] | undefined;
17
26
  };
18
27
  export declare const recipeCommands: Record<string, CommandMeta>;
@@ -1,13 +1,14 @@
1
1
  import { readFileSync, } from "node:fs";
2
2
  import { writeFile, } from "node:fs/promises";
3
3
  import { join, } from "node:path";
4
- import { ClientValidationError, } from "../../errors.js";
4
+ import { ClientValidationError, DataikuError, } from "../../errors.js";
5
+ import { recipeOutputSchemaIsComputable, schemaUpdateComputableId, schemaUpdateIsPending, } from "../../resources/recipe-create.js";
5
6
  import { deepMerge, } from "../../utils/deep-merge.js";
6
7
  import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredJsonInput, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
7
8
  import { executionMode, } from "../flags.js";
8
9
  import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
9
10
  import { ensureRecipeBackupDir, readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, writeRecipeBackup, } from "../helpers/recipe.js";
10
- import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
11
+ import { encodedProjectEndpoint, enqueueCliWarning, readIfExists, skipResult, } from "../output.js";
11
12
  import { commandUsage, withUsage, } from "../syntax.js";
12
13
  import { requireArgs, UsageError, } from "../usage.js";
13
14
  /** Read a local recipe file before any DSS request, mapping read failures to usage validation. */
@@ -30,6 +31,51 @@ function readRecipeBackupFile(backupPath) {
30
31
  throw new UsageError(`Could not read --backup ${backupPath}: ${reason}.`, "not_found", "Verify the backup file path and that it is readable.");
31
32
  }
32
33
  }
34
+ /**
35
+ * DSS does not recompute visual-recipe output schemas when the recipe is
36
+ * saved, and a build against a stale or empty output schema "succeeds" with
37
+ * the wrong columns. Around a payload write: outputs whose schema matched the
38
+ * DSS-computed one before the write (DSS-derived) or is empty get the newly
39
+ * computed schema; outputs whose schema had been hand-edited are left alone
40
+ * and reported in a warning.
41
+ */
42
+ async function writeKeepingDerivedOutputSchemas(c, recipeName, recipeType, projectKey, write) {
43
+ if (typeof recipeType !== "string" || !recipeOutputSchemaIsComputable(recipeType)) {
44
+ await write();
45
+ return {};
46
+ }
47
+ const derivedOutputs = await c.recipes.computeSchemaUpdates(recipeName, projectKey).then((before) => before.computables.filter((computable) => !schemaUpdateIsPending(computable)).map(schemaUpdateComputableId),
48
+ // Unknown before-state: only empty schemas are safe to fill.
49
+ (error) => {
50
+ if (error instanceof DataikuError)
51
+ return [];
52
+ throw error;
53
+ });
54
+ await write();
55
+ try {
56
+ const result = await c.recipes.updateSchema(recipeName, { projectKey, derivedOutputs, });
57
+ if (result.pending.length > 0) {
58
+ enqueueCliWarning({
59
+ code: "recipe_output_schema_outdated",
60
+ recipe: recipeName,
61
+ outputs: result.pending,
62
+ hint: `These outputs have an edited schema that differs from the DSS-computed one. Run dss recipe update-schema ${recipeName} to replace it, or build with --auto-update-schema.`,
63
+ });
64
+ }
65
+ return result.updated.length > 0 ? { outputSchemaUpdated: result.updated, } : {};
66
+ }
67
+ catch (error) {
68
+ if (!(error instanceof DataikuError))
69
+ throw error;
70
+ enqueueCliWarning({
71
+ code: "recipe_output_schema_not_computed",
72
+ recipe: recipeName,
73
+ error: error.message.split("\n")[0],
74
+ hint: `Fix the recipe settings, then run dss recipe update-schema ${recipeName}.`,
75
+ });
76
+ return {};
77
+ }
78
+ }
33
79
  function formatLineDiff(remoteName, localPath, remoteContent, localContent) {
34
80
  // Line-ending differences are not content differences: compare and split on
35
81
  // CRLF-normalized text, mirroring assert-unchanged's normalized payload hash.
@@ -112,10 +158,23 @@ export function recipeCreateOptionsFromFlags(f) {
112
158
  ...(fuzzyDistance ? { fuzzyDistance, } : {}),
113
159
  ...(fuzzyThreshold !== undefined ? { fuzzyThreshold, } : {}),
114
160
  fuzzyNormalize: f["normalize"] === true,
161
+ ...csvOption("groupBy", f["group-by"]),
162
+ ...csvOption("aggregate", f["aggregate"]),
163
+ ...(typeof f["order-by"] === "string" ? { orderBy: f["order-by"], } : {}),
164
+ ...csvOption("rename", f["rename"]),
165
+ ...csvOption("fillEmpty", f["fill-empty"]),
166
+ ...(typeof f["formula"] === "string" ? { formula: f["formula"], } : {}),
167
+ ...(typeof f["filter"] === "string" ? { filter: f["filter"], } : {}),
168
+ ...csvOption("dropColumns", f["drop-columns"]),
169
+ ...csvOption("keepColumns", f["keep-columns"]),
115
170
  projectKey: pk,
116
171
  };
117
172
  return payload;
118
173
  }
174
+ function csvOption(key, value) {
175
+ const items = splitCsvFlag(value);
176
+ return items.length > 0 ? { [key]: items, } : {};
177
+ }
119
178
  export const recipeCommands = withUsage("recipe", {
120
179
  list: {
121
180
  handler: (c, _a, f) => c.recipes.list(f["project-key"]),
@@ -310,13 +369,21 @@ export const recipeCommands = withUsage("recipe", {
310
369
  objectId: createdName,
311
370
  objectType: "RECIPE",
312
371
  },], pk);
372
+ if (created.outputSchemaUpdateError) {
373
+ enqueueCliWarning({
374
+ code: "recipe_output_schema_not_computed",
375
+ recipe: createdName,
376
+ error: created.outputSchemaUpdateError,
377
+ hint: `Fix the recipe settings, then run dss recipe update-schema ${createdName}.`,
378
+ });
379
+ }
313
380
  return { created: createdName, resource: "recipe", ...created, ...moved, };
314
381
  },
315
- description: "Create a recipe with optional inputs and a dataset or managed-folder output.",
382
+ description: "Create a recipe with optional inputs and a dataset or managed-folder output. Missing outputs are created on --output-connection as DSS managed datasets. Join, grouping (--group-by/--aggregate) and prepare (--rename/--formula/--filter/...) payloads come from flags. Visual recipe outputs get the DSS-computed schema (outputSchemaUpdated).",
316
383
  examples: [
317
384
  "dss recipe create --type python --input raw_orders,lookup --output orders_clean",
318
- "dss recipe create --type python --input orders --input customers --output orders_clean --zone Experiments",
319
- "dss recipe create --type python --input orders --output-folder LT7TUHJ8 --output-connection filesystem --dry-run",
385
+ "dss recipe create --type grouping --input orders --output orders_by_cat --group-by category --aggregate amount:sum+avg,name:first --order-by day",
386
+ "dss recipe create --type prepare --input orders --output orders_prep --rename name=label --formula 'total=amount * 2' --filter 'amount > 0'",
320
387
  ],
321
388
  },
322
389
  clone: {
@@ -586,22 +653,41 @@ export const recipeCommands = withUsage("recipe", {
586
653
  ensureRecipeBackupDir(backupDir);
587
654
  writeRecipeBackup(backupPath, `${JSON.stringify(recipeBackupDocument(a[0], pk, current), null, 2)}\n`);
588
655
  }
589
- await c.recipes.replace(a[0], { ...current, payload: content, }, pk);
656
+ const schema = await writeKeepingDerivedOutputSchemas(c, a[0], current.recipe.type, pk, () => c.recipes.replace(a[0], { ...current, payload: content, }, pk));
590
657
  return {
591
658
  updated: a[0],
592
659
  resource: "recipe",
593
660
  file: filePath,
594
661
  backupCreated: backupPath !== undefined,
595
662
  ...(backupPath ? { backupPath, } : {}),
663
+ ...schema,
596
664
  };
597
665
  },
598
- description: "Upload recipe code from a local file, backing up payload, graph, settings, and version metadata by default.",
666
+ description: "Upload recipe code from a local file, backing up payload, graph, settings, and version metadata by default. For visual and SQL query recipes, outputs whose schema was empty or matched the DSS-computed one before the upload get the newly computed schema (outputSchemaUpdated); hand-edited output schemas are kept and reported in a recipe_output_schema_outdated warning.",
599
667
  examples: [
600
668
  "dss recipe set-payload compute_orders --file code.py --dry-run",
601
669
  "dss recipe set-payload compute_orders --file code.py --backup-dir ./backups",
602
670
  "dss recipe set-payload compute_orders --file code.py --no-backup",
603
671
  ],
604
672
  },
673
+ "update-schema": {
674
+ handler: async (c, a, f) => {
675
+ requireArgs(a, 1, commandUsage("recipe", "update-schema"));
676
+ const dryRun = executionMode(f).dryRun;
677
+ const result = await c.recipes.updateSchema(a[0], {
678
+ projectKey: f["project-key"],
679
+ dryRun,
680
+ });
681
+ return dryRun
682
+ ? { dryRun: true, action: "update-schema", resource: "recipe", name: a[0], ...result, }
683
+ : result;
684
+ },
685
+ description: "Propagate schema changes: write the output schemas DSS computes from a visual or SQL query recipe (the UI's Update schema; dataikuapi compute_schema_updates().apply()) to every output whose schema differs. --dry-run lists them under pending. Code recipes are rejected: their code sets the schema when it runs.",
686
+ examples: [
687
+ "dss recipe update-schema compute_orders_joined --dry-run",
688
+ "dss recipe update-schema compute_orders_joined",
689
+ ],
690
+ },
605
691
  restore: {
606
692
  handler: async (c, a, f) => {
607
693
  const usage = commandUsage("recipe", "restore");
@@ -1,6 +1,6 @@
1
1
  import type { CommandMeta } from "../types.js";
2
2
  /**
3
- * Mutating actions accept --plan (buildMutationPlan in contract.ts owns the
3
+ * Mutating actions accept --plan (buildMutationPlan in plans.ts owns the
4
4
  * --plan path centrally); the handlers below keep --dry-run support with a
5
5
  * zero-request planned payload, mirroring set-active/delete.
6
6
  */
@@ -1,12 +1,11 @@
1
- import { unexpectedResponseError, } from "../../errors.js";
2
- import { writeResponseToFile, } from "../../utils/response-file.js";
3
1
  import { json, jsonInput, parseBooleanOption, requiredJsonInput, } from "../coerce.js";
4
2
  import { executionMode, } from "../flags.js";
3
+ import { trainedModelCommands, } from "../helpers/trained-model.js";
5
4
  import { readIfExists, skipResult, } from "../output.js";
6
5
  import { commandUsage, withUsage, } from "../syntax.js";
7
6
  import { requireArgs, UsageError, } from "../usage.js";
8
7
  /**
9
- * Mutating actions accept --plan (buildMutationPlan in contract.ts owns the
8
+ * Mutating actions accept --plan (buildMutationPlan in plans.ts owns the
10
9
  * --plan path centrally); the handlers below keep --dry-run support with a
11
10
  * zero-request planned payload, mirroring set-active/delete.
12
11
  */
@@ -361,79 +360,13 @@ export const savedModelCommands = withUsage("saved-model", {
361
360
  "dss saved-model evaluate-version MODEL_ID VERSION_ID --dataset ds --skip-expensive-reports true --project-key PROJECT",
362
361
  ],
363
362
  },
364
- "download-scoring-jar": {
365
- handler: async (c, a, f) => {
366
- const usage = commandUsage("saved-model", "download-scoring-jar");
367
- requireArgs(a, 2, usage);
368
- const out = f["output"];
369
- if (!out)
370
- throw new UsageError("--output PATH is required.", "missing_required_flag");
371
- const fullClassName = f["full-class-name"];
372
- const jarOptions = {
373
- ...(fullClassName !== undefined ? { fullClassName, } : {}),
374
- ...(f["include-libs"] !== undefined
375
- ? { includeLibs: parseBooleanOption(f["include-libs"], "--include-libs"), }
376
- : {}),
377
- };
378
- const res = await c.savedModels.downloadScoringJar(jarOptions, a[0], a[1], f["project-key"]);
379
- if (!res.body) {
380
- throw unexpectedResponseError("savedModels.downloadScoringJar response did not include a body");
381
- }
382
- const bytes = await writeResponseToFile(out, res);
383
- return { path: out, bytes, };
384
- },
385
- description: "Download the optimized scoring JAR of a version (license-gated server side). --full-class-name forwards the documented fullClassName query parameter; --include-libs toggles including scoring libraries.",
386
- examples: [
387
- "dss saved-model download-scoring-jar MODEL_ID VERSION_ID --output ./scoring.jar --project-key PROJECT",
388
- "dss saved-model download-scoring-jar MODEL_ID VERSION_ID --output ./scoring.jar --full-class-name model.Model --include-libs false --project-key PROJECT",
389
- ],
390
- },
391
- "download-scoring-pmml": {
392
- handler: async (c, a, f) => {
393
- requireArgs(a, 2, commandUsage("saved-model", "download-scoring-pmml"));
394
- const out = f["output"];
395
- if (!out)
396
- throw new UsageError("--output PATH is required.", "missing_required_flag");
397
- const res = await c.savedModels.downloadScoringPmml(a[0], a[1], f["project-key"]);
398
- if (!res.body) {
399
- throw unexpectedResponseError("savedModels.downloadScoringPmml response did not include a body");
400
- }
401
- const bytes = await writeResponseToFile(out, res);
402
- return { path: out, bytes, };
403
- },
404
- description: "Download the PMML scoring file of a version (license-gated server side).",
405
- examples: [
406
- "dss saved-model download-scoring-pmml MODEL_ID VERSION_ID --output ./model.pmml --project-key PROJECT",
407
- ],
408
- },
409
- "set-user-meta": {
410
- handler: async (c, a, f) => {
411
- const usage = commandUsage("saved-model", "set-user-meta");
412
- requireArgs(a, 2, usage);
413
- const userMeta = requiredJsonInput(f, "User-meta JSON is required via --data, --data-file, or --stdin.");
414
- const projectKey = f["project-key"];
415
- if (executionMode(f).dryRun) {
416
- return {
417
- dryRun: true,
418
- action: "set-user-meta",
419
- resource: "saved-model",
420
- id: a[0],
421
- versionId: a[1],
422
- projectKey,
423
- payload: userMeta,
424
- };
425
- }
426
- return c.savedModels.setUserMeta(a[0], a[1], userMeta, projectKey).then(() => ({
427
- updated: a[0],
428
- versionId: a[1],
429
- resource: "saved-model",
430
- }));
431
- },
432
- description: 'Update the user metadata of a model version. Send only the "userMeta" field of a previously-retrieved version-details object.',
433
- examples: [
434
- 'dss saved-model set-user-meta MODEL_ID VERSION_ID --data \'{"name":"Churn model","description":"v2"}\' --project-key PROJECT',
435
- ],
436
- },
363
+ ...trainedModelCommands({
364
+ resource: "saved-model",
365
+ ids: "MODEL_ID VERSION_ID",
366
+ noun: "saved-model version",
367
+ model: (c, a, pk) => c.savedModels.version(a[0], a[1], pk),
368
+ downloadDocumentation: (c, exportId, pk) => c.savedModels.downloadDocumentation(exportId, pk),
369
+ }),
437
370
  });
438
371
  function mlflowImportOptions(f) {
439
372
  const codeEnvName = f["code-env"];
@@ -1,6 +1,6 @@
1
1
  import { jsonInput, requiredJsonInput, } from "../coerce.js";
2
2
  import { commandUsage, withUsage, } from "../syntax.js";
3
- import { requireArgs, } from "../usage.js";
3
+ import { requireArgs, UsageError, } from "../usage.js";
4
4
  export const streamingEndpointCommands = withUsage("streaming-endpoint", {
5
5
  list: {
6
6
  handler: (c, _a, f) => c.streamingEndpoints.list(f["project-key"]),
@@ -43,4 +43,33 @@ export const streamingEndpointCommands = withUsage("streaming-endpoint", {
43
43
  description: "Delete a streaming endpoint.",
44
44
  examples: ["dss streaming-endpoint delete my-stream",],
45
45
  },
46
+ "create-managed": {
47
+ handler: async (c, a, f) => {
48
+ const connectionId = f["connection"];
49
+ if (typeof connectionId !== "string") {
50
+ throw new UsageError("--connection CONN is required.", "missing_required_flag");
51
+ }
52
+ const formatOptionId = f["format-option"];
53
+ await c.streamingEndpoints.createManaged(a[0], {
54
+ connectionId,
55
+ ...(typeof formatOptionId === "string" ? { formatOptionId, } : {}),
56
+ }, f["project-key"]);
57
+ return { created: a[0], resource: "streaming-endpoint", };
58
+ },
59
+ description: "Create a managed streaming endpoint on a connection; DSS picks type and params from the connection (--format-option for the message format).",
60
+ examples: ["dss streaming-endpoint create-managed orders_stream --connection kafka_main",],
61
+ },
62
+ schema: {
63
+ handler: (c, a, f) => c.streamingEndpoints.getSchema(a[0], f["project-key"]),
64
+ description: "Get a streaming endpoint's schema ({columns: [{name, type}]}).",
65
+ examples: ["dss streaming-endpoint schema orders_stream",],
66
+ },
67
+ "set-schema": {
68
+ handler: async (c, a, f) => {
69
+ await c.streamingEndpoints.setSchema(a[0], requiredJsonInput(f, '--data, --data-file, or --stdin is required ({"columns": [...]}).'), f["project-key"]);
70
+ return { updated: a[0], resource: "streaming-endpoint", };
71
+ },
72
+ description: "Replace a streaming endpoint's schema with an object from the schema action, edited.",
73
+ examples: ["dss streaming-endpoint set-schema orders_stream --data-file schema.json",],
74
+ },
46
75
  });
@@ -113,4 +113,12 @@ export const webappCommands = withUsage("webapp", {
113
113
  description: "Get a webapp backend's runtime state.",
114
114
  examples: ["dss webapp backend-state WEBAPP_ID",],
115
115
  },
116
+ trust: {
117
+ handler: (c, a, f) => c.webapps.trust(a[0], {
118
+ projectKey: f["project-key"],
119
+ ...(f["for-everybody"] === true ? { trustForEverybody: true, } : {}),
120
+ }),
121
+ description: "Trust a webapp's code so it can run for you, or for every user with --for-everybody (admin). Returns canAccess and trustedCodeStatus.",
122
+ examples: ["dss webapp trust VCMN2ra", "dss webapp trust VCMN2ra --for-everybody",],
123
+ },
116
124
  });