dataiku-sdk 0.11.0 → 0.12.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 (38) hide show
  1. package/LICENSE +21 -0
  2. package/dist/packages/types/src/index.d.ts +4 -0
  3. package/dist/packages/types/src/index.js +4 -0
  4. package/dist/src/cli/commands/analysis.d.ts +2 -0
  5. package/dist/src/cli/commands/analysis.js +66 -0
  6. package/dist/src/cli/commands/api-service.js +19 -0
  7. package/dist/src/cli/commands/index.js +8 -0
  8. package/dist/src/cli/commands/ml-task.d.ts +2 -0
  9. package/dist/src/cli/commands/ml-task.js +171 -0
  10. package/dist/src/cli/commands/model-evaluation-store.d.ts +2 -0
  11. package/dist/src/cli/commands/model-evaluation-store.js +83 -0
  12. package/dist/src/cli/commands/project.js +2 -1
  13. package/dist/src/cli/commands/recipe.js +29 -2
  14. package/dist/src/cli/commands/saved-model.d.ts +2 -0
  15. package/dist/src/cli/commands/saved-model.js +85 -0
  16. package/dist/src/cli/contract.js +11 -1
  17. package/dist/src/cli/flags.js +17 -0
  18. package/dist/src/cli/helpers/cleanup.js +47 -1
  19. package/dist/src/cli/usage.js +4 -0
  20. package/dist/src/client.d.ts +12 -0
  21. package/dist/src/client.js +20 -0
  22. package/dist/src/index.d.ts +4 -0
  23. package/dist/src/index.js +4 -0
  24. package/dist/src/resources/analyses.d.ts +34 -0
  25. package/dist/src/resources/analyses.js +31 -0
  26. package/dist/src/resources/api-services.d.ts +2 -0
  27. package/dist/src/resources/api-services.js +10 -0
  28. package/dist/src/resources/datasets.js +7 -5
  29. package/dist/src/resources/ml-tasks.d.ts +91 -0
  30. package/dist/src/resources/ml-tasks.js +130 -0
  31. package/dist/src/resources/model-evaluation-stores.d.ts +41 -0
  32. package/dist/src/resources/model-evaluation-stores.js +36 -0
  33. package/dist/src/resources/recipes.js +148 -31
  34. package/dist/src/resources/saved-models.d.ts +41 -0
  35. package/dist/src/resources/saved-models.js +40 -0
  36. package/package.json +1 -1
  37. package/packages/types/dist/index.d.ts +4 -0
  38. package/packages/types/dist/index.js +4 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 clssck
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -147,6 +147,10 @@ export declare const RecipeCreateOptionsSchema: import("@sinclair/typebox").TObj
147
147
  outputConnection: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
148
148
  joinOn: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>]>>;
149
149
  joinType: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
150
+ fuzzyOn: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TString, import("@sinclair/typebox").TArray<import("@sinclair/typebox").TString>]>>;
151
+ fuzzyDistance: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
152
+ fuzzyThreshold: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TNumber>;
153
+ fuzzyNormalize: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TBoolean>;
150
154
  projectKey: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
151
155
  outputFolder: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
152
156
  }>;
@@ -195,6 +195,10 @@ export const RecipeCreateOptionsSchema = Type.Object({
195
195
  outputConnection: Type.Optional(Type.String()),
196
196
  joinOn: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String()),])),
197
197
  joinType: Type.Optional(Type.String()),
198
+ fuzzyOn: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String()),])),
199
+ fuzzyDistance: Type.Optional(Type.String()),
200
+ fuzzyThreshold: Type.Optional(Type.Number()),
201
+ fuzzyNormalize: Type.Optional(Type.Boolean()),
198
202
  projectKey: Type.Optional(Type.String()),
199
203
  outputFolder: Type.Optional(Type.String()),
200
204
  });
@@ -0,0 +1,2 @@
1
+ import type { CommandMeta } from "../types.js";
2
+ export declare const analysisCommands: Record<string, CommandMeta>;
@@ -0,0 +1,66 @@
1
+ import { requiredStringFlag, } from "../coerce.js";
2
+ import { readIfExists, skipResult, } from "../output.js";
3
+ import { requireArgs, } from "../usage.js";
4
+ export const analysisCommands = {
5
+ list: {
6
+ handler: (c, _a, f) => c.analyses.list(f["project-key"]),
7
+ usage: "dss analysis list [--project-key KEY]",
8
+ description: "List Visual ML analyses in a project.",
9
+ examples: ["dss analysis list --project-key PROJECT",],
10
+ },
11
+ get: {
12
+ handler: (c, a, f) => {
13
+ requireArgs(a, 1, "dss analysis get <analysisId> [--project-key KEY]");
14
+ return c.analyses.get(a[0], f["project-key"]);
15
+ },
16
+ usage: "dss analysis get <analysisId> [--project-key KEY]",
17
+ description: "Get a Visual ML analysis.",
18
+ examples: ["dss analysis get ANALYSIS_ID --project-key PROJECT",],
19
+ },
20
+ create: {
21
+ handler: async (c, _a, f) => {
22
+ const usage = "dss analysis create --input-dataset DS [--dry-run] [--project-key KEY]";
23
+ const inputDataset = requiredStringFlag(f, "input-dataset", usage);
24
+ const projectKey = f["project-key"];
25
+ const options = { inputDataset, projectKey, };
26
+ if (f["dry-run"] === true) {
27
+ return {
28
+ dryRun: true,
29
+ action: "create",
30
+ resource: "analysis",
31
+ payload: options,
32
+ };
33
+ }
34
+ const created = await c.analyses.create(options);
35
+ return { created: created.id, resource: "analysis", ...created, };
36
+ },
37
+ usage: "dss analysis create --input-dataset DS [--dry-run] [--project-key KEY]",
38
+ description: "Create a Visual ML analysis for an input dataset.",
39
+ examples: [
40
+ "dss analysis create --input-dataset customers --project-key PROJECT",
41
+ "dss analysis create --input-dataset customers --dry-run --project-key PROJECT",
42
+ ],
43
+ },
44
+ delete: {
45
+ handler: async (c, a, f) => {
46
+ requireArgs(a, 1, "dss analysis delete <analysisId> [--if-exists] [--dry-run] [--project-key KEY]");
47
+ const projectKey = f["project-key"];
48
+ if (f["dry-run"] === true || f["if-exists"] === true) {
49
+ const current = await readIfExists(() => c.analyses.get(a[0], projectKey));
50
+ if (!current)
51
+ return skipResult("analysis", a[0], "missing");
52
+ if (f["dry-run"] === true) {
53
+ return { dryRun: true, action: "delete", resource: "analysis", id: a[0], current, };
54
+ }
55
+ }
56
+ await c.analyses.delete(a[0], projectKey);
57
+ return { deleted: a[0], resource: "analysis", };
58
+ },
59
+ usage: "dss analysis delete <analysisId> [--if-exists] [--dry-run] [--project-key KEY]",
60
+ description: "Delete a Visual ML analysis.",
61
+ examples: [
62
+ "dss analysis delete ANALYSIS_ID --if-exists --project-key PROJECT",
63
+ "dss analysis delete ANALYSIS_ID --dry-run --project-key PROJECT",
64
+ ],
65
+ },
66
+ };
@@ -36,6 +36,25 @@ export const apiServiceCommands = {
36
36
  description: "Save an API service's settings.",
37
37
  examples: ["dss api-service save-settings my-service --data-file service.json",],
38
38
  },
39
+ "add-prediction-endpoint": {
40
+ handler: async (c, a, f) => {
41
+ requireArgs(a, 3, "dss api-service add-prediction-endpoint <serviceId> <endpointId> <savedModelId> [--dry-run] [--project-key KEY]");
42
+ const endpoint = {
43
+ id: a[1],
44
+ type: "STD_PREDICTION",
45
+ modelRef: a[2],
46
+ };
47
+ if (f["dry-run"] === true)
48
+ return endpoint;
49
+ return c.apiServices.addPredictionEndpoint(a[0], a[1], a[2], f["project-key"]);
50
+ },
51
+ usage: "dss api-service add-prediction-endpoint <serviceId> <endpointId> <savedModelId> [--dry-run] [--project-key KEY]",
52
+ description: "Add a saved-model prediction endpoint to an API service.",
53
+ examples: [
54
+ "dss api-service add-prediction-endpoint my-service predict churn-model",
55
+ "dss api-service add-prediction-endpoint my-service predict churn-model --dry-run",
56
+ ],
57
+ },
39
58
  "list-packages": {
40
59
  handler: async (c, a, f) => {
41
60
  requireArgs(a, 1, "dss api-service list-packages <serviceId> [--project-key KEY]");
@@ -1,3 +1,4 @@
1
+ import { analysisCommands, } from "./analysis.js";
1
2
  import { apiDeployerCommands, } from "./api-deployer.js";
2
3
  import { apiServiceCommands, } from "./api-service.js";
3
4
  import { appCommands, } from "./app.js";
@@ -19,11 +20,14 @@ import { insightCommands, } from "./insight.js";
19
20
  import { jobCommands, } from "./job.js";
20
21
  import { meaningCommands, } from "./meaning.js";
21
22
  import { metricsCommands, } from "./metrics.js";
23
+ import { mlTaskCommands, } from "./ml-task.js";
24
+ import { modelEvaluationStoreCommands, } from "./model-evaluation-store.js";
22
25
  import { notebookCommands, } from "./notebook.js";
23
26
  import { projectDeployerCommands, } from "./project-deployer.js";
24
27
  import { projectLibraryCommands, } from "./project-library.js";
25
28
  import { projectCommands, } from "./project.js";
26
29
  import { recipeCommands, } from "./recipe.js";
30
+ import { savedModelCommands, } from "./saved-model.js";
27
31
  import { scenarioCommands, } from "./scenario.js";
28
32
  import { sqlCommands, } from "./sql.js";
29
33
  import { statisticsCommands, } from "./statistics.js";
@@ -34,6 +38,10 @@ import { wikiCommands, } from "./wiki.js";
34
38
  import { workspaceCommands, } from "./workspace.js";
35
39
  export const commands = {
36
40
  project: projectCommands,
41
+ analysis: analysisCommands,
42
+ "ml-task": mlTaskCommands,
43
+ "saved-model": savedModelCommands,
44
+ "model-evaluation-store": modelEvaluationStoreCommands,
37
45
  app: appCommands,
38
46
  "business-app": businessAppCommands,
39
47
  webapp: webappCommands,
@@ -0,0 +1,2 @@
1
+ import type { CommandMeta } from "../types.js";
2
+ export declare const mlTaskCommands: Record<string, CommandMeta>;
@@ -0,0 +1,171 @@
1
+ import { requiredJsonInput, requiredStringFlag, } from "../coerce.js";
2
+ import { requireArgs, UsageError, } from "../usage.js";
3
+ function optionalStringFlag(flags, name) {
4
+ const value = flags[name];
5
+ if (value === undefined)
6
+ return undefined;
7
+ if (typeof value !== "string" || value.trim().length === 0) {
8
+ throw new UsageError(`--${name} requires a non-empty value.`, "usage_error");
9
+ }
10
+ return value.trim();
11
+ }
12
+ function taskType(flags, usage) {
13
+ const normalized = requiredStringFlag(flags, "task-type", usage).toUpperCase();
14
+ if (normalized === "PREDICTION" || normalized === "CLUSTERING")
15
+ return normalized;
16
+ throw new UsageError("--task-type must be PREDICTION or CLUSTERING.", "invalid_enum");
17
+ }
18
+ export const mlTaskCommands = {
19
+ create: {
20
+ handler: async (c, a, f) => {
21
+ const usage = "dss ml-task create <analysisId> --task-type PREDICTION|CLUSTERING [--target COLUMN] [--prediction-type TYPE] [--backend-type TYPE] [--guess-policy POLICY] [--project-key KEY]";
22
+ requireArgs(a, 1, usage);
23
+ const projectKey = f["project-key"];
24
+ const normalizedTaskType = taskType(f, usage);
25
+ const targetVariable = optionalStringFlag(f, "target");
26
+ if (normalizedTaskType === "PREDICTION" && targetVariable === undefined) {
27
+ throw new UsageError("--target is required for PREDICTION ML tasks.", "missing_required_flag");
28
+ }
29
+ const options = {
30
+ analysisId: a[0],
31
+ taskType: normalizedTaskType,
32
+ targetVariable,
33
+ predictionType: optionalStringFlag(f, "prediction-type"),
34
+ backendType: optionalStringFlag(f, "backend-type"),
35
+ guessPolicy: optionalStringFlag(f, "guess-policy"),
36
+ projectKey,
37
+ };
38
+ const created = await c.mlTasks.create(options);
39
+ return { created: created.mlTaskId, resource: "ml-task", analysisId: a[0], ...created, };
40
+ },
41
+ usage: "dss ml-task create <analysisId> --task-type PREDICTION|CLUSTERING [--target COLUMN] [--prediction-type TYPE] [--backend-type TYPE] [--guess-policy POLICY] [--project-key KEY]",
42
+ description: "Create a prediction or clustering task in an analysis.",
43
+ examples: [
44
+ "dss ml-task create ANALYSIS_ID --task-type prediction --target churn --project-key PROJECT",
45
+ ],
46
+ },
47
+ status: {
48
+ handler: (c, a, f) => {
49
+ requireArgs(a, 2, "dss ml-task status <analysisId> <mlTaskId> [--project-key KEY]");
50
+ return c.mlTasks.status(a[0], a[1], f["project-key"]);
51
+ },
52
+ usage: "dss ml-task status <analysisId> <mlTaskId> [--project-key KEY]",
53
+ description: "Get a Visual ML task's current status.",
54
+ examples: ["dss ml-task status ANALYSIS_ID TASK_ID --project-key PROJECT",],
55
+ },
56
+ "get-settings": {
57
+ handler: (c, a, f) => {
58
+ requireArgs(a, 2, "dss ml-task get-settings <analysisId> <mlTaskId> [--project-key KEY]");
59
+ return c.mlTasks.getSettings(a[0], a[1], f["project-key"]);
60
+ },
61
+ usage: "dss ml-task get-settings <analysisId> <mlTaskId> [--project-key KEY]",
62
+ description: "Get a Visual ML task's settings.",
63
+ examples: ["dss ml-task get-settings ANALYSIS_ID TASK_ID --project-key PROJECT",],
64
+ },
65
+ "set-settings": {
66
+ handler: (c, a, f) => {
67
+ const usage = "dss ml-task set-settings <analysisId> <mlTaskId> (--data JSON|--data-file PATH|--stdin) [--project-key KEY]";
68
+ requireArgs(a, 2, usage);
69
+ const settings = requiredJsonInput(f, "--data, --data-file, or --stdin is required (ML task settings).");
70
+ const projectKey = f["project-key"];
71
+ return c.mlTasks.saveSettings(a[0], a[1], settings, projectKey);
72
+ },
73
+ usage: "dss ml-task set-settings <analysisId> <mlTaskId> (--data JSON|--data-file PATH|--stdin) [--project-key KEY]",
74
+ description: "Replace a Visual ML task's settings from JSON input.",
75
+ examples: [
76
+ "dss ml-task set-settings ANALYSIS_ID TASK_ID --data-file settings.json --project-key PROJECT",
77
+ ],
78
+ },
79
+ train: {
80
+ handler: (c, a, f) => {
81
+ requireArgs(a, 2, "dss ml-task train <analysisId> <mlTaskId> [--session-name NAME] [--wait] [--project-key KEY]");
82
+ const options = {
83
+ analysisId: a[0],
84
+ mlTaskId: a[1],
85
+ sessionName: optionalStringFlag(f, "session-name"),
86
+ wait: f["wait"] === true,
87
+ projectKey: f["project-key"],
88
+ };
89
+ return c.mlTasks.train(options);
90
+ },
91
+ usage: "dss ml-task train <analysisId> <mlTaskId> [--session-name NAME] [--wait] [--project-key KEY]",
92
+ description: "Start ML task training, optionally waiting for trained model IDs.",
93
+ examples: [
94
+ "dss ml-task train ANALYSIS_ID TASK_ID --session-name baseline --wait --project-key PROJECT",
95
+ ],
96
+ },
97
+ "list-models": {
98
+ handler: (c, a, f) => {
99
+ requireArgs(a, 2, "dss ml-task list-models <analysisId> <mlTaskId> [--project-key KEY]");
100
+ return c.mlTasks.listTrainedModels(a[0], a[1], f["project-key"]);
101
+ },
102
+ usage: "dss ml-task list-models <analysisId> <mlTaskId> [--project-key KEY]",
103
+ description: "List trained models for a Visual ML task.",
104
+ examples: ["dss ml-task list-models ANALYSIS_ID TASK_ID --project-key PROJECT",],
105
+ },
106
+ "model-details": {
107
+ handler: (c, a, f) => {
108
+ requireArgs(a, 3, "dss ml-task model-details <analysisId> <mlTaskId> <modelId> [--project-key KEY]");
109
+ return c.mlTasks.trainedModelDetails(a[0], a[1], a[2], f["project-key"]);
110
+ },
111
+ usage: "dss ml-task model-details <analysisId> <mlTaskId> <modelId> [--project-key KEY]",
112
+ description: "Get details for one trained model.",
113
+ examples: [
114
+ "dss ml-task model-details ANALYSIS_ID TASK_ID MODEL_ID --project-key PROJECT",
115
+ ],
116
+ },
117
+ deploy: {
118
+ handler: async (c, a, f) => {
119
+ const usage = "dss ml-task deploy <analysisId> <mlTaskId> <modelId> --model-name NAME --train-dataset DATASET [--test-dataset DATASET] [--dry-run] [--project-key KEY]";
120
+ requireArgs(a, 3, usage);
121
+ const options = {
122
+ analysisId: a[0],
123
+ mlTaskId: a[1],
124
+ modelId: a[2],
125
+ modelName: requiredStringFlag(f, "model-name", usage),
126
+ trainDatasetRef: requiredStringFlag(f, "train-dataset", usage),
127
+ testDatasetRef: optionalStringFlag(f, "test-dataset"),
128
+ projectKey: f["project-key"],
129
+ };
130
+ if (f["dry-run"] === true) {
131
+ return {
132
+ dryRun: true,
133
+ action: "deploy",
134
+ resource: "ml-task",
135
+ ...options,
136
+ };
137
+ }
138
+ return c.mlTasks.deployToFlow(options);
139
+ },
140
+ usage: "dss ml-task deploy <analysisId> <mlTaskId> <modelId> --model-name NAME --train-dataset DATASET [--test-dataset DATASET] [--dry-run] [--project-key KEY]",
141
+ description: "Deploy a trained model to the Flow as a saved model.",
142
+ examples: [
143
+ "dss ml-task deploy ANALYSIS_ID TASK_ID MODEL_ID --model-name churn-model --train-dataset train --test-dataset test --project-key PROJECT",
144
+ "dss ml-task deploy ANALYSIS_ID TASK_ID MODEL_ID --model-name churn-model --train-dataset train --dry-run --project-key PROJECT",
145
+ ],
146
+ },
147
+ delete: {
148
+ handler: async (c, a, f) => {
149
+ requireArgs(a, 2, "dss ml-task delete <analysisId> <mlTaskId> [--dry-run] [--project-key KEY]");
150
+ const projectKey = f["project-key"];
151
+ if (f["dry-run"] === true) {
152
+ return {
153
+ dryRun: true,
154
+ action: "delete",
155
+ resource: "ml-task",
156
+ analysisId: a[0],
157
+ mlTaskId: a[1],
158
+ projectKey,
159
+ };
160
+ }
161
+ await c.mlTasks.delete(a[0], a[1], projectKey);
162
+ return { deleted: a[1], resource: "ml-task", analysisId: a[0], };
163
+ },
164
+ usage: "dss ml-task delete <analysisId> <mlTaskId> [--dry-run] [--project-key KEY]",
165
+ description: "Delete a Visual ML task.",
166
+ examples: [
167
+ "dss ml-task delete ANALYSIS_ID TASK_ID --project-key PROJECT",
168
+ "dss ml-task delete ANALYSIS_ID TASK_ID --dry-run --project-key PROJECT",
169
+ ],
170
+ },
171
+ };
@@ -0,0 +1,2 @@
1
+ import type { CommandMeta } from "../types.js";
2
+ export declare const modelEvaluationStoreCommands: Record<string, CommandMeta>;
@@ -0,0 +1,83 @@
1
+ import { requiredStringFlag, } from "../coerce.js";
2
+ import { readIfExists, skipResult, } from "../output.js";
3
+ import { requireArgs, } from "../usage.js";
4
+ export const modelEvaluationStoreCommands = {
5
+ list: {
6
+ handler: (c, _a, f) => c.modelEvaluationStores.list(f["project-key"]),
7
+ usage: "dss model-evaluation-store list [--project-key KEY]",
8
+ description: "List model evaluation stores in a project.",
9
+ examples: ["dss model-evaluation-store list --project-key PROJECT",],
10
+ },
11
+ get: {
12
+ handler: (c, a, f) => {
13
+ requireArgs(a, 1, "dss model-evaluation-store get <storeId> [--project-key KEY]");
14
+ return c.modelEvaluationStores.get(a[0], f["project-key"]);
15
+ },
16
+ usage: "dss model-evaluation-store get <storeId> [--project-key KEY]",
17
+ description: "Get a model evaluation store.",
18
+ examples: ["dss model-evaluation-store get STORE_ID --project-key PROJECT",],
19
+ },
20
+ create: {
21
+ handler: async (c, _a, f) => {
22
+ const usage = "dss model-evaluation-store create --name NAME [--dry-run] [--project-key KEY]";
23
+ const name = requiredStringFlag(f, "name", usage);
24
+ const projectKey = f["project-key"];
25
+ const options = { name, projectKey, };
26
+ if (f["dry-run"] === true) {
27
+ return {
28
+ dryRun: true,
29
+ action: "create",
30
+ resource: "model-evaluation-store",
31
+ payload: options,
32
+ };
33
+ }
34
+ const created = await c.modelEvaluationStores.create(options);
35
+ return { created: created.id, resource: "model-evaluation-store", ...created, };
36
+ },
37
+ usage: "dss model-evaluation-store create --name NAME [--dry-run] [--project-key KEY]",
38
+ description: "Create an empty model evaluation store.",
39
+ examples: [
40
+ "dss model-evaluation-store create --name production-evaluations --project-key PROJECT",
41
+ "dss model-evaluation-store create --name production-evaluations --dry-run --project-key PROJECT",
42
+ ],
43
+ },
44
+ "list-evaluations": {
45
+ handler: (c, a, f) => {
46
+ requireArgs(a, 1, "dss model-evaluation-store list-evaluations <storeId> [--project-key KEY]");
47
+ return c.modelEvaluationStores.listEvaluations(a[0], f["project-key"]);
48
+ },
49
+ usage: "dss model-evaluation-store list-evaluations <storeId> [--project-key KEY]",
50
+ description: "List evaluations in a model evaluation store.",
51
+ examples: [
52
+ "dss model-evaluation-store list-evaluations STORE_ID --project-key PROJECT",
53
+ ],
54
+ },
55
+ delete: {
56
+ handler: async (c, a, f) => {
57
+ requireArgs(a, 1, "dss model-evaluation-store delete <storeId> [--if-exists] [--dry-run] [--project-key KEY]");
58
+ const projectKey = f["project-key"];
59
+ if (f["dry-run"] === true || f["if-exists"] === true) {
60
+ const current = await readIfExists(() => c.modelEvaluationStores.get(a[0], projectKey));
61
+ if (!current)
62
+ return skipResult("model-evaluation-store", a[0], "missing");
63
+ if (f["dry-run"] === true) {
64
+ return {
65
+ dryRun: true,
66
+ action: "delete",
67
+ resource: "model-evaluation-store",
68
+ id: a[0],
69
+ current,
70
+ };
71
+ }
72
+ }
73
+ await c.modelEvaluationStores.delete(a[0], projectKey);
74
+ return { deleted: a[0], resource: "model-evaluation-store", };
75
+ },
76
+ usage: "dss model-evaluation-store delete <storeId> [--if-exists] [--dry-run] [--project-key KEY]",
77
+ description: "Delete a model evaluation store.",
78
+ examples: [
79
+ "dss model-evaluation-store delete STORE_ID --if-exists --project-key PROJECT",
80
+ "dss model-evaluation-store delete STORE_ID --dry-run --project-key PROJECT",
81
+ ],
82
+ },
83
+ };
@@ -28,11 +28,12 @@ export const projectCommands = {
28
28
  },
29
29
  map: {
30
30
  handler: (c, _a, f) => c.projects.map({
31
+ projectKey: f["project-key"],
31
32
  maxNodes: num(f["max-nodes"]),
32
33
  maxEdges: num(f["max-edges"]),
33
34
  includeRaw: f["include-raw"] === true,
34
35
  }),
35
- usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw]",
36
+ usage: "dss project map [--max-nodes N] [--max-edges N] [--include-raw] [--project-key KEY]",
36
37
  description: "Get a summarized, truncated flow map.",
37
38
  examples: [
38
39
  "dss project map",
@@ -2,7 +2,7 @@ import { readFileSync, } from "node:fs";
2
2
  import { mkdir, writeFile, } from "node:fs/promises";
3
3
  import { join, } from "node:path";
4
4
  import { deepMerge, } from "../../utils/deep-merge.js";
5
- import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredStringFlag, rewritePairsFromFlags, sha256Hex, stableHash, } from "../coerce.js";
5
+ import { jobLogFilterFromFlag, jsonInput, maxLogLinesFromFlags, normalizeLineEndings, num, recipeInputDatasetsFromFlags, requiredStringFlag, rewritePairsFromFlags, sha256Hex, splitCsvFlag, stableHash, } from "../coerce.js";
6
6
  import { moveCreatedItemsToZone, resolveFlowZoneIdFromFlags, } from "../helpers/flow-zone.js";
7
7
  import { readRecipeBackup, recipeBackupDocument, recipeBackupPath, recipeCodeEnv, recipeGraph, recipeInputItemRef, recipeRoleInputItems, recipeRunShouldWait, } from "../helpers/recipe.js";
8
8
  import { encodedProjectEndpoint, readIfExists, skipResult, } from "../output.js";
@@ -180,6 +180,27 @@ export const recipeCommands = {
180
180
  const name = f["name"];
181
181
  const pk = f["project-key"];
182
182
  const inputDatasets = recipeInputDatasetsFromFlags(f);
183
+ const joinColumns = splitCsvFlag(f["join-on"]);
184
+ const fuzzyColumns = splitCsvFlag(f["fuzzy-on"]);
185
+ const rawFuzzyDistance = f["fuzzy-distance"];
186
+ let fuzzyDistance;
187
+ if (typeof rawFuzzyDistance === "string") {
188
+ const normalized = rawFuzzyDistance.trim().toUpperCase();
189
+ if (![
190
+ "DAMERAU_LEVENSHTEIN",
191
+ "HAMMING",
192
+ "JACCARD",
193
+ "COSINE",
194
+ "EUCLIDEAN",
195
+ ].includes(normalized)) {
196
+ throw new UsageError("--fuzzy-distance must be one of DAMERAU_LEVENSHTEIN, HAMMING, JACCARD, COSINE, or EUCLIDEAN.", "invalid_enum");
197
+ }
198
+ fuzzyDistance = normalized;
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
+ }
183
204
  const payload = {
184
205
  type,
185
206
  name,
@@ -187,6 +208,12 @@ export const recipeCommands = {
187
208
  outputDataset,
188
209
  outputFolder,
189
210
  outputConnection: f["output-connection"],
211
+ ...(joinColumns.length > 0 ? { joinOn: joinColumns, } : {}),
212
+ ...(typeof f["join-type"] === "string" ? { joinType: f["join-type"], } : {}),
213
+ ...(fuzzyColumns.length > 0 ? { fuzzyOn: fuzzyColumns, } : {}),
214
+ ...(fuzzyDistance ? { fuzzyDistance, } : {}),
215
+ ...(fuzzyThreshold !== undefined ? { fuzzyThreshold, } : {}),
216
+ fuzzyNormalize: f["normalize"] === true,
190
217
  projectKey: pk,
191
218
  };
192
219
  const zoneId = await resolveFlowZoneIdFromFlags(c, f, pk);
@@ -228,7 +255,7 @@ export const recipeCommands = {
228
255
  },], pk);
229
256
  return { created: createdName, resource: "recipe", ...created, ...moved, };
230
257
  },
231
- 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] [--project-key KEY]",
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]",
232
259
  description: "Create a recipe with one or more inputs and a dataset or managed-folder output.",
233
260
  examples: [
234
261
  "dss recipe create --type python --input raw_orders,lookup --output orders_clean",
@@ -0,0 +1,2 @@
1
+ import type { CommandMeta } from "../types.js";
2
+ export declare const savedModelCommands: Record<string, CommandMeta>;
@@ -0,0 +1,85 @@
1
+ import { readIfExists, skipResult, } from "../output.js";
2
+ import { requireArgs, } from "../usage.js";
3
+ export const savedModelCommands = {
4
+ list: {
5
+ handler: (c, _a, f) => c.savedModels.list(f["project-key"]),
6
+ usage: "dss saved-model list [--project-key KEY]",
7
+ description: "List saved models in a project.",
8
+ examples: ["dss saved-model list --project-key PROJECT",],
9
+ },
10
+ get: {
11
+ handler: (c, a, f) => {
12
+ requireArgs(a, 1, "dss saved-model get <modelId> [--project-key KEY]");
13
+ return c.savedModels.get(a[0], f["project-key"]);
14
+ },
15
+ usage: "dss saved-model get <modelId> [--project-key KEY]",
16
+ description: "Get a saved model.",
17
+ examples: ["dss saved-model get MODEL_ID --project-key PROJECT",],
18
+ },
19
+ "list-versions": {
20
+ handler: (c, a, f) => {
21
+ requireArgs(a, 1, "dss saved-model list-versions <modelId> [--project-key KEY]");
22
+ return c.savedModels.listVersions(a[0], f["project-key"]);
23
+ },
24
+ usage: "dss saved-model list-versions <modelId> [--project-key KEY]",
25
+ description: "List versions of a saved model.",
26
+ examples: ["dss saved-model list-versions MODEL_ID --project-key PROJECT",],
27
+ },
28
+ "version-details": {
29
+ handler: (c, a, f) => {
30
+ requireArgs(a, 2, "dss saved-model version-details <modelId> <versionId> [--project-key KEY]");
31
+ return c.savedModels.versionDetails(a[0], a[1], f["project-key"]);
32
+ },
33
+ usage: "dss saved-model version-details <modelId> <versionId> [--project-key KEY]",
34
+ description: "Get details for one saved-model version.",
35
+ examples: [
36
+ "dss saved-model version-details MODEL_ID VERSION_ID --project-key PROJECT",
37
+ ],
38
+ },
39
+ "set-active": {
40
+ handler: async (c, a, f) => {
41
+ requireArgs(a, 2, "dss saved-model set-active <modelId> <versionId> [--dry-run] [--project-key KEY]");
42
+ const projectKey = f["project-key"];
43
+ if (f["dry-run"] === true) {
44
+ return {
45
+ dryRun: true,
46
+ action: "set-active",
47
+ resource: "saved-model",
48
+ id: a[0],
49
+ versionId: a[1],
50
+ projectKey,
51
+ payload: { versionId: a[1], },
52
+ };
53
+ }
54
+ return c.savedModels.setActiveVersion(a[0], a[1], projectKey);
55
+ },
56
+ usage: "dss saved-model set-active <modelId> <versionId> [--dry-run] [--project-key KEY]",
57
+ description: "Set the active version of a saved model.",
58
+ examples: [
59
+ "dss saved-model set-active MODEL_ID VERSION_ID --project-key PROJECT",
60
+ "dss saved-model set-active MODEL_ID VERSION_ID --dry-run --project-key PROJECT",
61
+ ],
62
+ },
63
+ delete: {
64
+ handler: async (c, a, f) => {
65
+ requireArgs(a, 1, "dss saved-model delete <modelId> [--if-exists] [--dry-run] [--project-key KEY]");
66
+ const projectKey = f["project-key"];
67
+ if (f["dry-run"] === true || f["if-exists"] === true) {
68
+ const current = await readIfExists(() => c.savedModels.get(a[0], projectKey));
69
+ if (!current)
70
+ return skipResult("saved-model", a[0], "missing");
71
+ if (f["dry-run"] === true) {
72
+ return { dryRun: true, action: "delete", resource: "saved-model", id: a[0], current, };
73
+ }
74
+ }
75
+ await c.savedModels.delete(a[0], projectKey);
76
+ return { deleted: a[0], resource: "saved-model", };
77
+ },
78
+ usage: "dss saved-model delete <modelId> [--if-exists] [--dry-run] [--project-key KEY]",
79
+ description: "Delete a saved model.",
80
+ examples: [
81
+ "dss saved-model delete MODEL_ID --if-exists --project-key PROJECT",
82
+ "dss saved-model delete MODEL_ID --dry-run --project-key PROJECT",
83
+ ],
84
+ },
85
+ };
@@ -129,6 +129,7 @@ const READ_ACTIONS = new Set([
129
129
  "usages",
130
130
  ]);
131
131
  const PROJECT_SCOPED_RESOURCES = new Set([
132
+ "analysis",
132
133
  "data-quality",
133
134
  "dashboard",
134
135
  "dataset",
@@ -138,10 +139,13 @@ const PROJECT_SCOPED_RESOURCES = new Set([
138
139
  "fixtures",
139
140
  "job",
140
141
  "notebook",
142
+ "ml-task",
143
+ "model-evaluation-store",
141
144
  "recipe",
142
145
  "scenario",
143
146
  "sql",
144
147
  "variable",
148
+ "saved-model",
145
149
  "wiki",
146
150
  ]);
147
151
  const GLOBAL_AGENT_FLAGS = ["json", "verbose", "fields",];
@@ -182,6 +186,10 @@ const FIXTURES_EXAMPLES = [
182
186
  ];
183
187
  const ALLOWED_CLEANUP_ACTIONS = new Set([
184
188
  // Must mirror every cleanup.argv shape emitted by cleanupLedgerEntry().
189
+ "project delete",
190
+ "analysis delete",
191
+ "model-evaluation-store delete",
192
+ "ml-task delete",
185
193
  "dataset delete",
186
194
  "recipe delete",
187
195
  "scenario delete",
@@ -191,6 +199,7 @@ const ALLOWED_CLEANUP_ACTIONS = new Set([
191
199
  "insight delete",
192
200
  "data-quality delete-rule",
193
201
  "code-env delete",
202
+ "folder delete",
194
203
  "folder delete-file",
195
204
  ]);
196
205
  export function isAllowedCleanupAction(resource, action) {
@@ -576,8 +585,9 @@ function inferExitCodes(asyncKind) {
576
585
  };
577
586
  }
578
587
  function cleanupCommandFromDeleteUsage(resource, action) {
579
- if (!(action.startsWith("create") || action === "clone"))
588
+ if (!(action.startsWith("create") || action === "clone" || action === "duplicate")) {
580
589
  return undefined;
590
+ }
581
591
  const deleteAction = action === "create-rule" ? "delete-rule" : "delete";
582
592
  const deleteUsage = commands[resource]?.[deleteAction]?.usage;
583
593
  if (!deleteUsage)