dataiku-sdk 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +149 -46
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +102 -0
- package/dist/packages/types/src/index.js +76 -0
- package/dist/src/cli/commands/app.js +57 -15
- package/dist/src/cli/commands/bundle.js +1 -1
- package/dist/src/cli/commands/code-env.js +13 -7
- package/dist/src/cli/commands/connection.d.ts +9 -0
- package/dist/src/cli/commands/connection.js +267 -1
- package/dist/src/cli/commands/data-collection.d.ts +2 -0
- package/dist/src/cli/commands/data-collection.js +164 -0
- package/dist/src/cli/commands/dataset.js +161 -32
- package/dist/src/cli/commands/future.js +2 -2
- package/dist/src/cli/commands/group.d.ts +2 -0
- package/dist/src/cli/commands/group.js +109 -0
- package/dist/src/cli/commands/index.js +151 -78
- package/dist/src/cli/commands/knowledge-bank.d.ts +2 -0
- package/dist/src/cli/commands/knowledge-bank.js +80 -0
- package/dist/src/cli/commands/llm.d.ts +2 -0
- package/dist/src/cli/commands/llm.js +93 -0
- package/dist/src/cli/commands/macro.d.ts +2 -0
- package/dist/src/cli/commands/macro.js +221 -0
- package/dist/src/cli/commands/ml-task.js +18 -6
- package/dist/src/cli/commands/notebook.js +6 -2
- package/dist/src/cli/commands/plugin.d.ts +2 -0
- package/dist/src/cli/commands/plugin.js +799 -0
- package/dist/src/cli/commands/project-folder.d.ts +2 -0
- package/dist/src/cli/commands/project-folder.js +197 -0
- package/dist/src/cli/commands/project-git.js +14 -54
- package/dist/src/cli/commands/project.js +52 -6
- package/dist/src/cli/commands/recipe.js +36 -1
- package/dist/src/cli/commands/saved-model.d.ts +5 -0
- package/dist/src/cli/commands/saved-model.js +388 -1
- package/dist/src/cli/commands/scenario.js +138 -1
- package/dist/src/cli/commands/user.d.ts +8 -0
- package/dist/src/cli/commands/user.js +296 -0
- package/dist/src/cli/contract.d.ts +1 -2
- package/dist/src/cli/contract.js +1089 -103
- package/dist/src/cli/flags.js +30 -1
- package/dist/src/cli/helpers/app-successor.d.ts +19 -4
- package/dist/src/cli/helpers/app-successor.js +90 -30
- package/dist/src/cli/helpers/cleanup.js +3 -1
- package/dist/src/cli/helpers/flow-zone.js +1 -2
- package/dist/src/cli/output.d.ts +1 -1
- package/dist/src/cli/output.js +23 -10
- package/dist/src/cli/usage.d.ts +1 -0
- package/dist/src/cli/usage.js +47 -1
- package/dist/src/cli.js +147 -41
- package/dist/src/client.d.ts +83 -5
- package/dist/src/client.js +185 -29
- package/dist/src/errors.d.ts +3 -0
- package/dist/src/errors.js +2 -0
- package/dist/src/index.d.ts +16 -7
- package/dist/src/index.js +11 -2
- package/dist/src/resources/analyses.js +1 -8
- package/dist/src/resources/applications.d.ts +8 -6
- package/dist/src/resources/applications.js +21 -21
- package/dist/src/resources/base.d.ts +4 -0
- package/dist/src/resources/base.js +15 -0
- package/dist/src/resources/bundles.d.ts +9 -4
- package/dist/src/resources/bundles.js +10 -5
- package/dist/src/resources/connections.d.ts +78 -1
- package/dist/src/resources/connections.js +132 -1
- package/dist/src/resources/data-collections.d.ts +118 -0
- package/dist/src/resources/data-collections.js +49 -0
- package/dist/src/resources/datasets.d.ts +69 -0
- package/dist/src/resources/datasets.js +99 -1
- package/dist/src/resources/futures.d.ts +4 -1
- package/dist/src/resources/futures.js +38 -6
- package/dist/src/resources/groups.d.ts +47 -0
- package/dist/src/resources/groups.js +56 -0
- package/dist/src/resources/jobs.d.ts +8 -13
- package/dist/src/resources/jobs.js +41 -16
- package/dist/src/resources/knowledge-banks.d.ts +58 -0
- package/dist/src/resources/knowledge-banks.js +61 -0
- package/dist/src/resources/llms.d.ts +92 -0
- package/dist/src/resources/llms.js +62 -0
- package/dist/src/resources/macros.d.ts +121 -0
- package/dist/src/resources/macros.js +231 -0
- package/dist/src/resources/ml-tasks.d.ts +37 -2
- package/dist/src/resources/ml-tasks.js +100 -20
- package/dist/src/resources/model-evaluation-stores.js +1 -8
- package/dist/src/resources/notebooks.d.ts +43 -7
- package/dist/src/resources/notebooks.js +53 -13
- package/dist/src/resources/plugins.d.ts +265 -0
- package/dist/src/resources/plugins.js +458 -0
- package/dist/src/resources/project-folders.d.ts +71 -0
- package/dist/src/resources/project-folders.js +57 -0
- package/dist/src/resources/project-git.d.ts +6 -0
- package/dist/src/resources/project-git.js +69 -42
- package/dist/src/resources/project-library.d.ts +0 -1
- package/dist/src/resources/project-library.js +1 -17
- package/dist/src/resources/projects.d.ts +19 -1
- package/dist/src/resources/projects.js +27 -1
- package/dist/src/resources/recipes.d.ts +15 -1
- package/dist/src/resources/recipes.js +73 -6
- package/dist/src/resources/saved-models.d.ts +134 -1
- package/dist/src/resources/saved-models.js +214 -6
- package/dist/src/resources/scenarios.d.ts +105 -0
- package/dist/src/resources/scenarios.js +232 -106
- package/dist/src/resources/sql.js +5 -1
- package/dist/src/resources/users.d.ts +129 -0
- package/dist/src/resources/users.js +128 -0
- package/dist/src/resources/webapps.d.ts +15 -2
- package/dist/src/resources/webapps.js +18 -4
- package/dist/src/schemas.d.ts +2 -2
- package/dist/src/schemas.js +1 -1
- package/dist/src/utils/deep-merge.d.ts +1 -1
- package/dist/src/utils/polling.d.ts +20 -0
- package/dist/src/utils/polling.js +27 -0
- package/dist/src/utils/response-file.js +1 -1
- package/dist/src/utils/secret-sanitize.d.ts +54 -0
- package/dist/src/utils/secret-sanitize.js +101 -0
- package/package.json +6 -5
- package/packages/types/dist/index.d.ts +102 -0
- package/packages/types/dist/index.js +76 -0
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +7 -5
- package/skills/dataiku-dss/references/app-releases.md +8 -7
- package/skills/dataiku-dss/references/discovery.md +4 -4
package/dist/src/cli/flags.js
CHANGED
|
@@ -61,6 +61,10 @@ export const BOOLEAN_FLAGS = new Set([
|
|
|
61
61
|
"peek",
|
|
62
62
|
"raw-data",
|
|
63
63
|
"all",
|
|
64
|
+
"connected",
|
|
65
|
+
"conda",
|
|
66
|
+
"force",
|
|
67
|
+
"partitioning-folder",
|
|
64
68
|
]);
|
|
65
69
|
export const SHORT_FLAGS = {
|
|
66
70
|
v: "verbose",
|
|
@@ -82,9 +86,12 @@ export const VALUE_FLAGS = new Set([
|
|
|
82
86
|
"owner",
|
|
83
87
|
"topic",
|
|
84
88
|
"reply",
|
|
85
|
-
"
|
|
89
|
+
"configuration",
|
|
86
90
|
"partitions",
|
|
87
91
|
"since",
|
|
92
|
+
"keys",
|
|
93
|
+
"candidates",
|
|
94
|
+
"text",
|
|
88
95
|
"activity",
|
|
89
96
|
"agent",
|
|
90
97
|
"api-key",
|
|
@@ -225,6 +232,28 @@ export const VALUE_FLAGS = new Set([
|
|
|
225
232
|
"to",
|
|
226
233
|
"zone",
|
|
227
234
|
"zone-id",
|
|
235
|
+
"purpose",
|
|
236
|
+
"step-id",
|
|
237
|
+
"max-bytes",
|
|
238
|
+
"type-option-id",
|
|
239
|
+
"format-option-id",
|
|
240
|
+
"copy-partitioning-from",
|
|
241
|
+
"creation-mode",
|
|
242
|
+
"max-dataset-count",
|
|
243
|
+
"logins",
|
|
244
|
+
"max-log-bytes",
|
|
245
|
+
"llm-id",
|
|
246
|
+
"remove-intermediate",
|
|
247
|
+
"code-env",
|
|
248
|
+
"container-exec-config",
|
|
249
|
+
"set-active",
|
|
250
|
+
"binary-classification-threshold",
|
|
251
|
+
"sampling",
|
|
252
|
+
"use-optimal-threshold",
|
|
253
|
+
"skip-expensive-reports",
|
|
254
|
+
"full-class-name",
|
|
255
|
+
"include-libs",
|
|
256
|
+
"archive",
|
|
228
257
|
]);
|
|
229
258
|
export const REPEATABLE_VALUE_FLAGS = new Set([
|
|
230
259
|
"dataset",
|
|
@@ -13,6 +13,14 @@ export declare const VISUAL_UI_GATE: {
|
|
|
13
13
|
readonly requiredAuthentication: "pre-authenticated-sso-or-dedicated-ui-test-identity";
|
|
14
14
|
readonly evidenceRequired: "Open the instance in the Dataiku Apps UI and exercise the affected tiles, forms, and actions to confirm the release behaves as intended.";
|
|
15
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* The one generated instance-key shape shared by `app create-instance` and
|
|
18
|
+
* `app create-successor-instance` when no caller-chosen target is supplied:
|
|
19
|
+
* `APP_` plus 122 random bits of UUIDv4 hex, matching the official client's
|
|
20
|
+
* random-key mode. Collisions are negligible, so a generated key is never
|
|
21
|
+
* probed for absence and is never retried under an alternate key.
|
|
22
|
+
*/
|
|
23
|
+
export declare function generatedInstanceKey(): string;
|
|
16
24
|
export type AppInstanceCheckName = "project-type" | "app-registration" | "manifest-version" | "manifest-references";
|
|
17
25
|
export interface AppInstanceCheck {
|
|
18
26
|
check: AppInstanceCheckName;
|
|
@@ -46,7 +54,11 @@ export interface AppInstanceVerification {
|
|
|
46
54
|
export interface SuccessorInstanceRequest {
|
|
47
55
|
appId: string;
|
|
48
56
|
from: string;
|
|
49
|
-
|
|
57
|
+
/**
|
|
58
|
+
* Caller-chosen successor key. Omitted for generated-key mode: the key is
|
|
59
|
+
* generated once during apply and DSS's own creation future names it.
|
|
60
|
+
*/
|
|
61
|
+
to?: string;
|
|
50
62
|
name?: string;
|
|
51
63
|
copyPermissions: boolean;
|
|
52
64
|
dryRun: boolean;
|
|
@@ -92,9 +104,12 @@ interface TargetAbsenceContext {
|
|
|
92
104
|
visibleAppInstanceKeys?: readonly (string | undefined)[];
|
|
93
105
|
}
|
|
94
106
|
/**
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
107
|
+
* Caller-chosen-key guard: when the caller supplies the target key, creation
|
|
108
|
+
* cleanup is safe only when that target was proven absent before POST. A
|
|
109
|
+
* hidden 403 is not absence: project listing can prove a collision, but it
|
|
110
|
+
* cannot prove that an inaccessible project does not exist. Generated keys
|
|
111
|
+
* (app create-instance with targetProjectKey omitted) skip this guard: a
|
|
112
|
+
* freshly generated random key does not need an absence probe.
|
|
98
113
|
*/
|
|
99
114
|
export declare function requireAbsentProjectTarget(client: DataikuClient, targetProjectKey: string, targetFlag: "--to" | "targetProjectKey", context: TargetAbsenceContext): Promise<void>;
|
|
100
115
|
/**
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID, } from "node:crypto";
|
|
1
2
|
import { DataikuError, } from "../../errors.js";
|
|
2
3
|
import { projectIncarnationHash, } from "../../utils/project-incarnation.js";
|
|
3
4
|
import { stableHash, stringField, } from "../coerce.js";
|
|
@@ -17,6 +18,16 @@ export const VISUAL_UI_GATE = {
|
|
|
17
18
|
requiredAuthentication: "pre-authenticated-sso-or-dedicated-ui-test-identity",
|
|
18
19
|
evidenceRequired: "Open the instance in the Dataiku Apps UI and exercise the affected tiles, forms, and actions to confirm the release behaves as intended.",
|
|
19
20
|
};
|
|
21
|
+
/**
|
|
22
|
+
* The one generated instance-key shape shared by `app create-instance` and
|
|
23
|
+
* `app create-successor-instance` when no caller-chosen target is supplied:
|
|
24
|
+
* `APP_` plus 122 random bits of UUIDv4 hex, matching the official client's
|
|
25
|
+
* random-key mode. Collisions are negligible, so a generated key is never
|
|
26
|
+
* probed for absence and is never retried under an alternate key.
|
|
27
|
+
*/
|
|
28
|
+
export function generatedInstanceKey() {
|
|
29
|
+
return `APP_${randomUUID().replace(/-/g, "").toUpperCase()}`;
|
|
30
|
+
}
|
|
20
31
|
/**
|
|
21
32
|
* DSS answers the app-manifest probe with 400 ("neither an app template nor an
|
|
22
33
|
* app instance") for ordinary projects and 404 for missing ones. Both mean "not
|
|
@@ -183,9 +194,12 @@ export function futureTargetIdentity(result) {
|
|
|
183
194
|
return null;
|
|
184
195
|
}
|
|
185
196
|
/**
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
*
|
|
197
|
+
* Caller-chosen-key guard: when the caller supplies the target key, creation
|
|
198
|
+
* cleanup is safe only when that target was proven absent before POST. A
|
|
199
|
+
* hidden 403 is not absence: project listing can prove a collision, but it
|
|
200
|
+
* cannot prove that an inaccessible project does not exist. Generated keys
|
|
201
|
+
* (app create-instance with targetProjectKey omitted) skip this guard: a
|
|
202
|
+
* freshly generated random key does not need an absence probe.
|
|
189
203
|
*/
|
|
190
204
|
export async function requireAbsentProjectTarget(client, targetProjectKey, targetFlag, context) {
|
|
191
205
|
if (context.visibleAppInstanceKeys?.includes(targetProjectKey) === true) {
|
|
@@ -251,11 +265,14 @@ export async function requireAbsentProjectTarget(client, targetProjectKey, targe
|
|
|
251
265
|
creationPostAttempted: false,
|
|
252
266
|
};
|
|
253
267
|
if (targetVisibleInProjectList !== true && targetVisibleInAppInstances !== true) {
|
|
254
|
-
throw new UsageError(`Could not confirm target project ${targetProjectKey} is absent.`, "target_absence_unverifiable", "
|
|
268
|
+
throw new UsageError(`Could not confirm target project ${targetProjectKey} is absent.`, "target_absence_unverifiable", "Creation was not attempted. For a fresh instance with a different key, omit targetProjectKey from `dss app create-instance`. To keep this exact key, retry with an identity that has global project visibility. DSS exposes no permission-independent key-availability endpoint or published atomic duplicate-key guarantee.", {
|
|
255
269
|
...probeDetails,
|
|
256
270
|
targetProbe: "forbidden-and-not-listable",
|
|
257
271
|
deploymentMasksUnknownProjects: "possible-but-unproven",
|
|
258
|
-
supportedRecoveryModes: [
|
|
272
|
+
supportedRecoveryModes: [
|
|
273
|
+
"create-instance-with-generated-key",
|
|
274
|
+
"grant-global-project-visibility",
|
|
275
|
+
],
|
|
259
276
|
unavailableRecoveryModes: [
|
|
260
277
|
"use-supported-key-availability-endpoint",
|
|
261
278
|
"server-atomic-create",
|
|
@@ -272,7 +289,7 @@ export async function requireAbsentProjectTarget(client, targetProjectKey, targe
|
|
|
272
289
|
*/
|
|
273
290
|
async function preflightSuccessor(client, request, usage) {
|
|
274
291
|
const { appId, from, to, } = request;
|
|
275
|
-
if (from === to) {
|
|
292
|
+
if (to !== undefined && from === to) {
|
|
276
293
|
throw new UsageError("--from and --to must be different project keys: a successor instance is created alongside its predecessor.", "validation_failed", `Usage: ${usage}`, { sourceProjectKey: from, targetProjectKey: to, });
|
|
277
294
|
}
|
|
278
295
|
const instances = await client.applications.listInstances(appId);
|
|
@@ -290,10 +307,16 @@ async function preflightSuccessor(client, request, usage) {
|
|
|
290
307
|
if (source.projectAppType !== "APP_INSTANCE") {
|
|
291
308
|
throw new UsageError(`Project ${from} is a ${source.projectAppType ?? "non-app"} project, not a classic Dataiku App instance.`, "validation_failed", "--from must name an APP_INSTANCE project; app templates are versioned with `dss app set-manifest-version`.", { sourceProjectKey: from, projectAppType: source.projectAppType ?? null, });
|
|
292
309
|
}
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
310
|
+
if (to !== undefined) {
|
|
311
|
+
// Caller-chosen keys must be proven absent before the single POST.
|
|
312
|
+
// Generated keys skip the probe entirely: the key does not exist yet,
|
|
313
|
+
// so an absence probe would prove nothing and probe a project the
|
|
314
|
+
// caller never named.
|
|
315
|
+
await requireAbsentProjectTarget(client, to, "--to", {
|
|
316
|
+
appId,
|
|
317
|
+
visibleAppInstanceKeys: instances.map((instance) => instance.projectKey),
|
|
318
|
+
});
|
|
319
|
+
}
|
|
297
320
|
const templateManifest = await client.applications.getAppManifest(appId);
|
|
298
321
|
const templateVersion = rawVersion(templateManifest);
|
|
299
322
|
if (templateVersion === null) {
|
|
@@ -317,8 +340,10 @@ async function preflightSuccessor(client, request, usage) {
|
|
|
317
340
|
sourceProjectAppType: source.projectAppType,
|
|
318
341
|
sourceVersion: source.version,
|
|
319
342
|
sourceManifestHash: source.manifestHash,
|
|
320
|
-
|
|
321
|
-
|
|
343
|
+
generatedTarget: to === undefined,
|
|
344
|
+
...(request.name !== undefined || to !== undefined
|
|
345
|
+
? { targetProjectName: request.name ?? to, }
|
|
346
|
+
: {}),
|
|
322
347
|
templateManifestHash: stableHash(templateManifest),
|
|
323
348
|
templateVersion,
|
|
324
349
|
templateReferenceValidation,
|
|
@@ -343,6 +368,7 @@ export async function preflightSuccessorInstance(client, request, usage) {
|
|
|
343
368
|
preflightExecuted: true,
|
|
344
369
|
creationPostAttempted: false,
|
|
345
370
|
versionMutationAttempted: false,
|
|
371
|
+
...(preflight.generatedTarget ? { targetProjectKeyGeneratedDuringApply: true, } : {}),
|
|
346
372
|
appId: request.appId,
|
|
347
373
|
source: {
|
|
348
374
|
projectKey: request.from,
|
|
@@ -353,12 +379,28 @@ export async function preflightSuccessorInstance(client, request, usage) {
|
|
|
353
379
|
? { permissionsHash: preflight.sourcePermissionsHash, }
|
|
354
380
|
: {}),
|
|
355
381
|
},
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
382
|
+
...(preflight.generatedTarget
|
|
383
|
+
? {
|
|
384
|
+
// Generated mode: the target key does not exist yet, so no
|
|
385
|
+
// absence probe ran and none can be claimed. The plan/receipt
|
|
386
|
+
// names no key at all: it is generated once during apply and
|
|
387
|
+
// named by DSS's own terminal creation future.
|
|
388
|
+
target: {
|
|
389
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
390
|
+
...(preflight.targetProjectName !== undefined
|
|
391
|
+
? { name: preflight.targetProjectName, }
|
|
392
|
+
: { targetProjectNameGeneratedDuringApply: true, }),
|
|
393
|
+
},
|
|
394
|
+
targetPreflight: "not-applicable-generated-key",
|
|
395
|
+
}
|
|
396
|
+
: {
|
|
397
|
+
target: {
|
|
398
|
+
projectKey: request.to,
|
|
399
|
+
name: preflight.targetProjectName,
|
|
400
|
+
exists: false,
|
|
401
|
+
},
|
|
402
|
+
targetPreflight: "confirmed-absent",
|
|
403
|
+
}),
|
|
362
404
|
template: {
|
|
363
405
|
projectKey: request.appId,
|
|
364
406
|
version: preflight.templateVersion,
|
|
@@ -372,7 +414,7 @@ export async function preflightSuccessorInstance(client, request, usage) {
|
|
|
372
414
|
copyPermissions: request.copyPermissions,
|
|
373
415
|
next: {
|
|
374
416
|
setVersion: `dss app set-manifest-version --manifest-version <VERSION> --expect-hash ${preflight.templateManifestHash} --project-key ${request.appId}`,
|
|
375
|
-
createSuccessor: `dss app create-successor-instance ${request.appId} --from ${request.from} --to ${request.to}${request.name !== undefined ? ` --name ${JSON.stringify(request.name)}` : ""}${request.copyPermissions ? " --copy-permissions" : ""}`,
|
|
417
|
+
createSuccessor: `dss app create-successor-instance ${request.appId} --from ${request.from}${request.to !== undefined ? ` --to ${request.to}` : ""}${request.name !== undefined ? ` --name ${JSON.stringify(request.name)}` : ""}${request.copyPermissions ? " --copy-permissions" : ""}`,
|
|
376
418
|
},
|
|
377
419
|
};
|
|
378
420
|
}
|
|
@@ -388,7 +430,8 @@ export async function preflightSuccessorInstance(client, request, usage) {
|
|
|
388
430
|
* indeterminate and produces no cleanup entry.
|
|
389
431
|
*/
|
|
390
432
|
export async function createSuccessorInstance(client, request, usage) {
|
|
391
|
-
const { appId, from,
|
|
433
|
+
const { appId, from, } = request;
|
|
434
|
+
const generatedTarget = request.to === undefined;
|
|
392
435
|
const preflight = await preflightSuccessor(client, request, usage);
|
|
393
436
|
const source = {
|
|
394
437
|
projectKey: from,
|
|
@@ -408,27 +451,44 @@ export async function createSuccessorInstance(client, request, usage) {
|
|
|
408
451
|
sourcePreserved: true,
|
|
409
452
|
uiPublicationVerified: false,
|
|
410
453
|
requiredExternalGates: [VISUAL_UI_GATE,],
|
|
411
|
-
|
|
454
|
+
// Generated mode never probes a target, so it must not claim the
|
|
455
|
+
// probe-backed absence verdict: no target existed to verify.
|
|
456
|
+
targetPreflight: generatedTarget ? "not-applicable-generated-key" : "confirmed-absent",
|
|
412
457
|
};
|
|
413
458
|
if (request.dryRun) {
|
|
414
459
|
return {
|
|
415
460
|
dryRun: true,
|
|
416
461
|
action: "create-successor-instance",
|
|
417
462
|
...shared,
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
463
|
+
...(generatedTarget
|
|
464
|
+
? { targetProjectKeyGeneratedDuringApply: true, }
|
|
465
|
+
: {}),
|
|
466
|
+
target: generatedTarget
|
|
467
|
+
? {
|
|
468
|
+
targetProjectKeyGeneratedDuringApply: true,
|
|
469
|
+
...(preflight.targetProjectName !== undefined
|
|
470
|
+
? { name: preflight.targetProjectName, }
|
|
471
|
+
: { targetProjectNameGeneratedDuringApply: true, }),
|
|
472
|
+
}
|
|
473
|
+
: {
|
|
474
|
+
projectKey: request.to,
|
|
475
|
+
name: preflight.targetProjectName,
|
|
476
|
+
exists: false,
|
|
477
|
+
},
|
|
423
478
|
copyPermissions: request.copyPermissions,
|
|
424
479
|
preflight: "passed",
|
|
425
480
|
};
|
|
426
481
|
}
|
|
482
|
+
// Generated exactly once, here, immediately before the single POST: plan,
|
|
483
|
+
// preflight and dry-run never allocate. A definitive rejection does not
|
|
484
|
+
// retry and does not fall back to an alternate key.
|
|
485
|
+
const to = request.to ?? generatedInstanceKey();
|
|
486
|
+
const targetProjectName = preflight.targetProjectName ?? to;
|
|
427
487
|
let created;
|
|
428
488
|
try {
|
|
429
489
|
created = await client.applications.createInstance(appId, {
|
|
430
490
|
targetProjectKey: to,
|
|
431
|
-
targetProjectName
|
|
491
|
+
targetProjectName,
|
|
432
492
|
});
|
|
433
493
|
}
|
|
434
494
|
catch (error) {
|
|
@@ -436,7 +496,7 @@ export async function createSuccessorInstance(client, request, usage) {
|
|
|
436
496
|
return {
|
|
437
497
|
...shared,
|
|
438
498
|
projectKey: to,
|
|
439
|
-
target: { projectKey: to, name:
|
|
499
|
+
target: { projectKey: to, name: targetProjectName, },
|
|
440
500
|
success: false,
|
|
441
501
|
state: rejected ? "CREATE_FAILED" : "INDETERMINATE",
|
|
442
502
|
elapsedMs: 0,
|
|
@@ -465,7 +525,7 @@ export async function createSuccessorInstance(client, request, usage) {
|
|
|
465
525
|
return {
|
|
466
526
|
...shared,
|
|
467
527
|
projectKey: to,
|
|
468
|
-
target: { projectKey: to, name:
|
|
528
|
+
target: { projectKey: to, name: targetProjectName, },
|
|
469
529
|
success: false,
|
|
470
530
|
state: "VERIFICATION_FAILED",
|
|
471
531
|
elapsedMs: 0,
|
|
@@ -485,7 +545,7 @@ export async function createSuccessorInstance(client, request, usage) {
|
|
|
485
545
|
const base = {
|
|
486
546
|
...shared,
|
|
487
547
|
projectKey: to,
|
|
488
|
-
target: { projectKey: to, name:
|
|
548
|
+
target: { projectKey: to, name: targetProjectName, },
|
|
489
549
|
...(instance ? { instance, } : {}),
|
|
490
550
|
};
|
|
491
551
|
let waited;
|
|
@@ -280,7 +280,9 @@ export function cleanupLedgerEntry(resource, action, args, flags, result, projec
|
|
|
280
280
|
};
|
|
281
281
|
}
|
|
282
282
|
case "notebook.save-sql": {
|
|
283
|
-
|
|
283
|
+
// On create the persisted id is the server receipt (`saved`), not the
|
|
284
|
+
// requested handle, which only becomes the display name.
|
|
285
|
+
const id = stringField(record, ["saved", "id",]) ?? args[0];
|
|
284
286
|
if (!id)
|
|
285
287
|
return undefined;
|
|
286
288
|
return {
|
|
@@ -116,8 +116,7 @@ export function flowZonePlanPosition(value, source) {
|
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
118
|
export function flowZoneCurrentPosition(zone) {
|
|
119
|
-
const
|
|
120
|
-
const position = plainRecord(record.position);
|
|
119
|
+
const position = plainRecord(zone.position);
|
|
121
120
|
if (!position)
|
|
122
121
|
return undefined;
|
|
123
122
|
const x = position.x;
|
package/dist/src/cli/output.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ export declare function pickResultFields(item: unknown, fields: string[]): unkno
|
|
|
10
10
|
* element-wise; scalars and string results pass through untouched. Requested keys
|
|
11
11
|
* that are absent become null so every row keeps a stable, predictable shape.
|
|
12
12
|
*/
|
|
13
|
-
export declare function projectResultFields(result: unknown, fields: string[]): unknown;
|
|
13
|
+
export declare function projectResultFields(result: unknown, fields: string[], availableFieldNames?: () => string[]): unknown;
|
|
14
14
|
export declare function writeCommandResult(result: unknown): void;
|
|
15
15
|
export declare function transientBodyWithTargetContext(body: string, target: string, elapsedMs: number): string;
|
|
16
16
|
export declare function addTransientTargetContext(error: unknown, target: string, elapsedMs: number): never;
|
package/dist/src/cli/output.js
CHANGED
|
@@ -47,14 +47,15 @@ function projectionRecords(result) {
|
|
|
47
47
|
const items = Array.isArray(result) ? result : [result,];
|
|
48
48
|
return items.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
49
49
|
}
|
|
50
|
-
function warnUnknownProjectionFields(result, fields) {
|
|
50
|
+
function warnUnknownProjectionFields(result, fields, availableFieldNames) {
|
|
51
51
|
const records = projectionRecords(result);
|
|
52
52
|
if (records.length === 0)
|
|
53
53
|
return;
|
|
54
54
|
const unknownFields = fields.filter((field) => !records.some((record) => resolveFieldPath(record, field).found));
|
|
55
55
|
if (unknownFields.length === 0)
|
|
56
56
|
return;
|
|
57
|
-
const availableFields =
|
|
57
|
+
const availableFields = availableFieldNames?.()
|
|
58
|
+
?? [...new Set(records.flatMap((record) => Object.keys(record))),].sort();
|
|
58
59
|
enqueueCliWarning({
|
|
59
60
|
code: "field_projection_missing",
|
|
60
61
|
fields: unknownFields,
|
|
@@ -67,8 +68,8 @@ function warnUnknownProjectionFields(result, fields) {
|
|
|
67
68
|
* element-wise; scalars and string results pass through untouched. Requested keys
|
|
68
69
|
* that are absent become null so every row keeps a stable, predictable shape.
|
|
69
70
|
*/
|
|
70
|
-
export function projectResultFields(result, fields) {
|
|
71
|
-
warnUnknownProjectionFields(result, fields);
|
|
71
|
+
export function projectResultFields(result, fields, availableFieldNames) {
|
|
72
|
+
warnUnknownProjectionFields(result, fields, availableFieldNames);
|
|
72
73
|
if (Array.isArray(result))
|
|
73
74
|
return result.map((item) => pickResultFields(item, fields));
|
|
74
75
|
return pickResultFields(result, fields);
|
|
@@ -98,7 +99,7 @@ export function transientBodyWithTargetContext(body, target, elapsedMs) {
|
|
|
98
99
|
}
|
|
99
100
|
export function addTransientTargetContext(error, target, elapsedMs) {
|
|
100
101
|
if (error instanceof DataikuError && error.category === "transient") {
|
|
101
|
-
throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId, { target, elapsedMs, });
|
|
102
|
+
throw new DataikuError(error.status, error.statusText, transientBodyWithTargetContext(error.body, target, elapsedMs), error.retry, error.requestId, { target, elapsedMs, bodyTruncated: error.bodyTruncated, });
|
|
102
103
|
}
|
|
103
104
|
throw error;
|
|
104
105
|
}
|
|
@@ -106,10 +107,16 @@ export function isFailedWaitResult(result) {
|
|
|
106
107
|
if (result === null || typeof result !== "object" || Array.isArray(result))
|
|
107
108
|
return false;
|
|
108
109
|
const record = result;
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
110
|
+
if (record.success !== false)
|
|
111
|
+
return false;
|
|
112
|
+
if (typeof record.elapsedMs !== "number" || typeof record.pollCount !== "number")
|
|
113
|
+
return false;
|
|
114
|
+
// Job/future wait results name the terminal state as a string; macro wait
|
|
115
|
+
// results identify themselves by their runnable identity fields with the
|
|
116
|
+
// poll state attached as an object (or omitted entirely).
|
|
117
|
+
if (typeof record.state === "string" || typeof record.outcome === "string")
|
|
118
|
+
return true;
|
|
119
|
+
return typeof record.runnableType === "string" && typeof record.runId === "string";
|
|
113
120
|
}
|
|
114
121
|
export function isAssertionFailureResult(result) {
|
|
115
122
|
if (result === null || typeof result !== "object" || Array.isArray(result))
|
|
@@ -172,7 +179,13 @@ export class CommandResultFailure extends Error {
|
|
|
172
179
|
export function commandFailureMessage(result) {
|
|
173
180
|
if (isFailedWaitResult(result)) {
|
|
174
181
|
const record = result;
|
|
175
|
-
const state = typeof record.state === "string"
|
|
182
|
+
const state = typeof record.state === "string"
|
|
183
|
+
? record.state
|
|
184
|
+
: typeof record.outcome === "string"
|
|
185
|
+
? record.outcome
|
|
186
|
+
: typeof record.timedOut === "boolean"
|
|
187
|
+
? "timed out"
|
|
188
|
+
: undefined;
|
|
176
189
|
return `Command completed with failed long-running result${state ? `: ${state}` : ""}.`;
|
|
177
190
|
}
|
|
178
191
|
if (isAssertionFailureResult(result)) {
|
package/dist/src/cli/usage.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { StableErrorCode } from "../errors.js";
|
|
2
2
|
export declare const RESOURCE_NAMES: string[];
|
|
3
|
+
export declare function inferRequiresProject(resource: string, action: string, usage: string): boolean;
|
|
3
4
|
export declare class UsageError extends Error {
|
|
4
5
|
readonly code: StableErrorCode;
|
|
5
6
|
readonly hint?: string;
|
package/dist/src/cli/usage.js
CHANGED
|
@@ -9,6 +9,15 @@ export const RESOURCE_NAMES = [
|
|
|
9
9
|
"bundle",
|
|
10
10
|
"project-deployer",
|
|
11
11
|
"project-git",
|
|
12
|
+
"project-folder",
|
|
13
|
+
"data-collection",
|
|
14
|
+
"llm",
|
|
15
|
+
"knowledge-bank",
|
|
16
|
+
"macro",
|
|
17
|
+
"plugin",
|
|
18
|
+
"user",
|
|
19
|
+
"group",
|
|
20
|
+
"project",
|
|
12
21
|
"project-library",
|
|
13
22
|
"streaming-endpoint",
|
|
14
23
|
"continuous-activity",
|
|
@@ -44,10 +53,47 @@ export const RESOURCE_NAMES = [
|
|
|
44
53
|
"commands",
|
|
45
54
|
"fixtures",
|
|
46
55
|
"install-skill",
|
|
47
|
-
"project",
|
|
48
56
|
"version",
|
|
49
57
|
]
|
|
50
58
|
.sort();
|
|
59
|
+
const PROJECT_SCOPED_RESOURCES = new Set([
|
|
60
|
+
"analysis",
|
|
61
|
+
"data-quality",
|
|
62
|
+
"dashboard",
|
|
63
|
+
"knowledge-bank",
|
|
64
|
+
"dataset",
|
|
65
|
+
"llm",
|
|
66
|
+
"flow-zone",
|
|
67
|
+
"insight",
|
|
68
|
+
"folder",
|
|
69
|
+
"fixtures",
|
|
70
|
+
"job",
|
|
71
|
+
"notebook",
|
|
72
|
+
"macro",
|
|
73
|
+
"ml-task",
|
|
74
|
+
"model-evaluation-store",
|
|
75
|
+
"recipe",
|
|
76
|
+
"scenario",
|
|
77
|
+
"sql",
|
|
78
|
+
"variable",
|
|
79
|
+
"saved-model",
|
|
80
|
+
"wiki",
|
|
81
|
+
]);
|
|
82
|
+
export function inferRequiresProject(resource, action, usage) {
|
|
83
|
+
if (resource === "agent" || resource === "auth" || resource === "doctor" || resource === "commands"
|
|
84
|
+
|| resource === "install-skill" || resource === "version")
|
|
85
|
+
return false;
|
|
86
|
+
if (resource === "sql" && action === "query")
|
|
87
|
+
return false;
|
|
88
|
+
if (PROJECT_SCOPED_RESOURCES.has(resource))
|
|
89
|
+
return true;
|
|
90
|
+
// Plugin project scope is optional, not a requirement.
|
|
91
|
+
if (resource === "plugin")
|
|
92
|
+
return false;
|
|
93
|
+
if (resource === "project-git")
|
|
94
|
+
return !action.startsWith("future-");
|
|
95
|
+
return usage.includes("--project-key");
|
|
96
|
+
}
|
|
51
97
|
export class UsageError extends Error {
|
|
52
98
|
code;
|
|
53
99
|
hint;
|