dataiku-sdk 3.6.1 → 3.8.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 +14 -6
- package/bin/bun-version.js +14 -0
- package/bin/dss.js +11 -15
- package/dist/build-metadata.json +1 -1
- package/dist/packages/types/src/index.d.ts +114 -5
- package/dist/packages/types/src/index.js +75 -2
- package/dist/src/cli/coerce.d.ts +2 -0
- package/dist/src/cli/coerce.js +20 -16
- package/dist/src/cli/command-syntax.json +3369 -144
- package/dist/src/cli/commands/analysis.js +19 -1
- package/dist/src/cli/commands/bundle.js +22 -0
- package/dist/src/cli/commands/connection.js +26 -2
- package/dist/src/cli/commands/data-quality.js +33 -1
- package/dist/src/cli/commands/dataset.js +76 -9
- package/dist/src/cli/commands/discussion.js +15 -0
- package/dist/src/cli/commands/doctor.js +1 -1
- package/dist/src/cli/commands/flow-zone.js +241 -46
- package/dist/src/cli/commands/flow.d.ts +5 -0
- package/dist/src/cli/commands/flow.js +57 -0
- package/dist/src/cli/commands/future.js +8 -0
- package/dist/src/cli/commands/index.js +6 -0
- package/dist/src/cli/commands/job.js +44 -6
- package/dist/src/cli/commands/metrics.js +21 -1
- package/dist/src/cli/commands/ml-task.d.ts +5 -0
- package/dist/src/cli/commands/ml-task.js +86 -16
- package/dist/src/cli/commands/mlflow.d.ts +11 -0
- package/dist/src/cli/commands/mlflow.js +121 -0
- package/dist/src/cli/commands/plugin.js +5 -11
- package/dist/src/cli/commands/project-deployer.js +70 -0
- package/dist/src/cli/commands/project-library.js +2 -5
- package/dist/src/cli/commands/project.js +8 -0
- package/dist/src/cli/commands/recipe.d.ts +13 -0
- package/dist/src/cli/commands/recipe.js +130 -7
- package/dist/src/cli/commands/saved-model.d.ts +1 -1
- package/dist/src/cli/commands/saved-model.js +9 -76
- package/dist/src/cli/commands/streaming-endpoint.js +30 -1
- package/dist/src/cli/commands/webapp.js +8 -0
- package/dist/src/cli/commands/wiki.js +78 -0
- package/dist/src/cli/commands/workspace.js +8 -0
- package/dist/src/cli/contract.d.ts +16 -15
- package/dist/src/cli/contract.js +150 -3407
- package/dist/src/cli/doctor.d.ts +13 -0
- package/dist/src/cli/doctor.js +43 -1
- package/dist/src/cli/flags.d.ts +14 -1
- package/dist/src/cli/flags.js +106 -8
- package/dist/src/cli/helpers/app-successor.js +1 -2
- package/dist/src/cli/helpers/dataset.d.ts +12 -0
- package/dist/src/cli/helpers/dataset.js +36 -0
- package/dist/src/cli/helpers/documentation.d.ts +21 -0
- package/dist/src/cli/helpers/documentation.js +53 -0
- package/dist/src/cli/helpers/flow-zone.d.ts +21 -0
- package/dist/src/cli/helpers/flow-zone.js +144 -4
- package/dist/src/cli/helpers/trained-model.d.ts +26 -0
- package/dist/src/cli/helpers/trained-model.js +144 -0
- package/dist/src/cli/list-output.d.ts +7 -0
- package/dist/src/cli/list-output.js +77 -0
- package/dist/src/cli/list-shapes.d.ts +21 -0
- package/dist/src/cli/list-shapes.js +111 -0
- package/dist/src/cli/output-schemas.d.ts +0 -14
- package/dist/src/cli/output-schemas.js +4 -7
- package/dist/src/cli/output.d.ts +0 -1
- package/dist/src/cli/output.js +33 -19
- package/dist/src/cli/plans.d.ts +14 -0
- package/dist/src/cli/plans.js +3740 -0
- package/dist/src/cli/types.d.ts +2 -0
- package/dist/src/cli/usage.d.ts +7 -0
- package/dist/src/cli/usage.js +20 -1
- package/dist/src/cli/version.d.ts +2 -2
- package/dist/src/cli/version.js +2 -2
- package/dist/src/cli.js +137 -115
- package/dist/src/client.d.ts +52 -43
- package/dist/src/client.js +267 -96
- package/dist/src/errors.d.ts +22 -2
- package/dist/src/errors.js +49 -50
- package/dist/src/generated/action-output-schemas.json +345 -89
- package/dist/src/index.d.ts +5 -1
- package/dist/src/index.js +3 -0
- package/dist/src/resources/analyses.d.ts +8 -0
- package/dist/src/resources/analyses.js +13 -1
- package/dist/src/resources/bundles.d.ts +34 -0
- package/dist/src/resources/bundles.js +67 -0
- package/dist/src/resources/connections.d.ts +9 -0
- package/dist/src/resources/connections.js +15 -0
- package/dist/src/resources/data-quality.d.ts +20 -0
- package/dist/src/resources/data-quality.js +30 -0
- package/dist/src/resources/dataset-create.d.ts +38 -0
- package/dist/src/resources/dataset-create.js +135 -0
- package/dist/src/resources/datasets.d.ts +46 -15
- package/dist/src/resources/datasets.js +120 -112
- package/dist/src/resources/discussions.d.ts +5 -0
- package/dist/src/resources/discussions.js +7 -0
- package/dist/src/resources/documentation.d.ts +25 -0
- package/dist/src/resources/documentation.js +16 -0
- package/dist/src/resources/flow-zones.d.ts +29 -3
- package/dist/src/resources/flow-zones.js +53 -3
- package/dist/src/resources/flow.d.ts +48 -0
- package/dist/src/resources/flow.js +65 -0
- package/dist/src/resources/futures.d.ts +23 -0
- package/dist/src/resources/futures.js +32 -0
- package/dist/src/resources/jobs.d.ts +34 -0
- package/dist/src/resources/jobs.js +170 -3
- package/dist/src/resources/metrics.d.ts +10 -0
- package/dist/src/resources/metrics.js +10 -0
- package/dist/src/resources/ml-tasks.d.ts +65 -2
- package/dist/src/resources/ml-tasks.js +102 -28
- package/dist/src/resources/mlflow-extension.d.ts +73 -0
- package/dist/src/resources/mlflow-extension.js +105 -0
- package/dist/src/resources/projects.d.ts +15 -0
- package/dist/src/resources/projects.js +18 -3
- package/dist/src/resources/recipe-create.d.ts +79 -0
- package/dist/src/resources/recipe-create.js +454 -0
- package/dist/src/resources/recipes.d.ts +28 -29
- package/dist/src/resources/recipes.js +201 -267
- package/dist/src/resources/saved-models.d.ts +10 -6
- package/dist/src/resources/saved-models.js +16 -14
- package/dist/src/resources/scenarios.js +8 -14
- package/dist/src/resources/sql.js +52 -13
- package/dist/src/resources/streaming-endpoints.d.ts +12 -0
- package/dist/src/resources/streaming-endpoints.js +15 -0
- package/dist/src/resources/trained-model.d.ts +60 -0
- package/dist/src/resources/trained-model.js +98 -0
- package/dist/src/resources/webapps.d.ts +8 -0
- package/dist/src/resources/webapps.js +10 -0
- package/dist/src/resources/wiki.d.ts +52 -1
- package/dist/src/resources/wiki.js +96 -0
- package/dist/src/resources/workspaces.d.ts +2 -0
- package/dist/src/resources/workspaces.js +4 -0
- package/dist/src/utils/flow-analysis.js +4 -0
- package/dist/src/utils/response-file.js +20 -9
- package/dist/src/utils/secret-sanitize.d.ts +7 -0
- package/dist/src/utils/secret-sanitize.js +46 -0
- package/package.json +25 -26
- package/plugin.json +1 -1
- package/skills/dataiku-dss/SKILL.md +1 -1
- package/skills/dataiku-dss/references/authentication.md +3 -1
- package/skills/dataiku-dss/references/coding.md +1 -0
- package/skills/dataiku-dss/references/discovery.md +4 -6
- package/skills/dataiku-dss/references/flow-maps.md +2 -2
- package/skills/dataiku-dss/references/troubleshooting.md +3 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readFileSync, } from "node:fs";
|
|
2
|
+
import { ClientValidationError, } from "../../errors.js";
|
|
2
3
|
import { asRecord, } from "../../utils/records.js";
|
|
3
4
|
import { compareStrings, } from "../../utils/stable-hash.js";
|
|
4
5
|
import { finiteNumberField, jsonInput, optionalStringField, parseJsonObject, requiredStringArray, splitCsvFlag, } from "../coerce.js";
|
|
@@ -319,6 +320,16 @@ export function flowZonePruneItems(existing, plannedItemKeys) {
|
|
|
319
320
|
return [];
|
|
320
321
|
return flowZoneExplicitItems(existing).filter((item) => !plannedItemKeys.has(flowZoneItemKey(item)));
|
|
321
322
|
}
|
|
323
|
+
/**
|
|
324
|
+
* The position an existing zone must move to, if the plan changes it. DSS
|
|
325
|
+
* honors a zone position only on creation, so a move means recreating the zone.
|
|
326
|
+
*/
|
|
327
|
+
export function flowZoneRepositionTarget(plan, existing) {
|
|
328
|
+
return plan.position !== undefined
|
|
329
|
+
&& !flowZoneSamePosition(flowZoneCurrentPosition(existing), plan.position)
|
|
330
|
+
? plan.position
|
|
331
|
+
: undefined;
|
|
332
|
+
}
|
|
322
333
|
export function flowZoneOrganizeStep(plan, existing, sync, plannedItemKeys) {
|
|
323
334
|
ensureFlowZonePlanTarget(plan, existing);
|
|
324
335
|
const update = {};
|
|
@@ -326,10 +337,8 @@ export function flowZoneOrganizeStep(plan, existing, sync, plannedItemKeys) {
|
|
|
326
337
|
update.name = plan.name;
|
|
327
338
|
if (existing && plan.color && plan.color !== existing.color)
|
|
328
339
|
update.color = plan.color;
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
update.position = plan.position;
|
|
332
|
-
}
|
|
340
|
+
const recreatePosition = existing ? flowZoneRepositionTarget(plan, existing) : undefined;
|
|
341
|
+
const recreate = recreatePosition ? { position: recreatePosition, } : undefined;
|
|
333
342
|
const pruneItems = sync ? flowZonePruneItems(existing, plannedItemKeys) : [];
|
|
334
343
|
const moveItems = flowZoneMoveDelta(existing, plan.items);
|
|
335
344
|
return {
|
|
@@ -341,10 +350,141 @@ export function flowZoneOrganizeStep(plan, existing, sync, plannedItemKeys) {
|
|
|
341
350
|
},
|
|
342
351
|
...(existing ? { existing: flowZoneSummary(existing), } : { create: true, }),
|
|
343
352
|
...(Object.keys(update).length > 0 ? { update, } : {}),
|
|
353
|
+
...(recreate ? { recreate, } : {}),
|
|
344
354
|
moveItems,
|
|
355
|
+
...(plan.impliedItems && plan.impliedItems.length > 0
|
|
356
|
+
? { impliedItems: plan.impliedItems, }
|
|
357
|
+
: {}),
|
|
345
358
|
...(pruneItems.length > 0 ? { pruneItems, } : {}),
|
|
346
359
|
};
|
|
347
360
|
}
|
|
361
|
+
function flowNodeObjectType(node, knownTypes) {
|
|
362
|
+
switch (node.kind) {
|
|
363
|
+
case "recipe":
|
|
364
|
+
return "RECIPE";
|
|
365
|
+
case "dataset":
|
|
366
|
+
return "DATASET";
|
|
367
|
+
case "folder":
|
|
368
|
+
return "MANAGED_FOLDER";
|
|
369
|
+
default:
|
|
370
|
+
return knownTypes.get(node.id);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* DSS keeps a recipe and its outputs in one zone: moving either drags the
|
|
375
|
+
* others. Each unit is keyed by its recipe id and lists the recipe plus every
|
|
376
|
+
* object the recipe writes.
|
|
377
|
+
*/
|
|
378
|
+
function flowZoneUnits(graph) {
|
|
379
|
+
const kindById = new Map(graph.nodes.map((node) => [node.id, node.kind,]));
|
|
380
|
+
const units = new Map();
|
|
381
|
+
for (const node of graph.nodes) {
|
|
382
|
+
if (node.kind === "recipe")
|
|
383
|
+
units.set(node.id, [node.id,]);
|
|
384
|
+
}
|
|
385
|
+
for (const edge of graph.edges) {
|
|
386
|
+
if (kindById.get(edge.from) !== "recipe" || kindById.get(edge.to) === "recipe")
|
|
387
|
+
continue;
|
|
388
|
+
units.get(edge.from)?.push(edge.to);
|
|
389
|
+
}
|
|
390
|
+
return units;
|
|
391
|
+
}
|
|
392
|
+
function explicitTypesById(zones) {
|
|
393
|
+
const types = new Map();
|
|
394
|
+
for (const zone of zones) {
|
|
395
|
+
for (const item of flowZoneItems(zone)) {
|
|
396
|
+
if (!item.projectKey)
|
|
397
|
+
types.set(item.objectId, item.objectType);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
return types;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* Enforce the DSS recipe/output zone invariant on a plan. Rejects plans that
|
|
404
|
+
* place members of one unit in different zones (they would never converge),
|
|
405
|
+
* and adds the unlisted members of each planned unit to that zone as
|
|
406
|
+
* `impliedItems`, so moves, `--sync` pruning, and reruns all agree with what
|
|
407
|
+
* DSS actually does.
|
|
408
|
+
*/
|
|
409
|
+
export function applyFlowZoneUnits(plan, graph, zones, projectKey) {
|
|
410
|
+
const zoneIndexById = new Map();
|
|
411
|
+
plan.zones.forEach((zonePlan, index) => {
|
|
412
|
+
for (const item of zonePlan.items) {
|
|
413
|
+
// Shared objects from other projects never move with a local recipe.
|
|
414
|
+
if (!item.projectKey || item.projectKey === projectKey)
|
|
415
|
+
zoneIndexById.set(item.objectId, index);
|
|
416
|
+
}
|
|
417
|
+
});
|
|
418
|
+
const nodeById = new Map(graph.nodes.map((node) => [node.id, node,]));
|
|
419
|
+
const knownTypes = explicitTypesById(zones);
|
|
420
|
+
const conflicts = [];
|
|
421
|
+
const implied = plan.zones.map(() => []);
|
|
422
|
+
for (const [recipe, members,] of flowZoneUnits(graph)) {
|
|
423
|
+
const planned = members.filter((member) => zoneIndexById.has(member));
|
|
424
|
+
if (planned.length === 0)
|
|
425
|
+
continue;
|
|
426
|
+
const indexes = new Set(planned.map((member) => zoneIndexById.get(member)));
|
|
427
|
+
if (indexes.size > 1) {
|
|
428
|
+
const byZone = {};
|
|
429
|
+
for (const member of planned) {
|
|
430
|
+
const label = flowZonePlanLabel(plan.zones[zoneIndexById.get(member)]);
|
|
431
|
+
(byZone[label] ??= []).push(member);
|
|
432
|
+
}
|
|
433
|
+
conflicts.push({ recipe, zones: byZone, });
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
const [index,] = indexes;
|
|
437
|
+
for (const member of members) {
|
|
438
|
+
if (zoneIndexById.has(member))
|
|
439
|
+
continue;
|
|
440
|
+
const node = nodeById.get(member);
|
|
441
|
+
const objectType = node ? flowNodeObjectType(node, knownTypes) : undefined;
|
|
442
|
+
if (objectType)
|
|
443
|
+
implied[index].push({ objectType, objectId: member, });
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
if (conflicts.length > 0) {
|
|
447
|
+
throw new ClientValidationError(`Flow zone plan splits ${conflicts.length} recipe(s) from their outputs across zones; DSS keeps a recipe and its outputs in one zone.`, "validation_failed", "Assign each recipe to one zone (its outputs follow automatically), or list the recipe and all its outputs in the same zone.", { conflicts, });
|
|
448
|
+
}
|
|
449
|
+
return {
|
|
450
|
+
...plan,
|
|
451
|
+
zones: plan.zones.map((zonePlan, index) => implied[index].length === 0
|
|
452
|
+
? zonePlan
|
|
453
|
+
: {
|
|
454
|
+
...zonePlan,
|
|
455
|
+
items: [...zonePlan.items, ...implied[index],],
|
|
456
|
+
impliedItems: implied[index],
|
|
457
|
+
}),
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* Default-zone objects an organization plan has to place: recipes (their
|
|
462
|
+
* outputs follow) and objects no recipe produces, such as uploaded sources.
|
|
463
|
+
*/
|
|
464
|
+
export function flowZoneUnassignedItems(graph, zones) {
|
|
465
|
+
const assigned = new Set();
|
|
466
|
+
for (const zone of zones) {
|
|
467
|
+
if (zone.id === "default")
|
|
468
|
+
continue;
|
|
469
|
+
for (const item of flowZoneExplicitItems(zone))
|
|
470
|
+
assigned.add(item.objectId);
|
|
471
|
+
}
|
|
472
|
+
const produced = new Set();
|
|
473
|
+
for (const members of flowZoneUnits(graph).values()) {
|
|
474
|
+
for (const member of members.slice(1))
|
|
475
|
+
produced.add(member);
|
|
476
|
+
}
|
|
477
|
+
const knownTypes = explicitTypesById(zones);
|
|
478
|
+
const items = [];
|
|
479
|
+
for (const node of graph.nodes) {
|
|
480
|
+
if (assigned.has(node.id) || produced.has(node.id))
|
|
481
|
+
continue;
|
|
482
|
+
const objectType = flowNodeObjectType(node, knownTypes);
|
|
483
|
+
if (objectType)
|
|
484
|
+
items.push({ objectType, objectId: node.id, });
|
|
485
|
+
}
|
|
486
|
+
return items.sort((a, b) => compareStrings(flowZoneItemKey(a), flowZoneItemKey(b)));
|
|
487
|
+
}
|
|
348
488
|
export function flowZoneValidationBucket(index, objectType) {
|
|
349
489
|
switch (objectType) {
|
|
350
490
|
case "DATASET":
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { DataikuClient } from "../../client.js";
|
|
2
|
+
import { type ComputedModelDiagnostic, type ModelDiagnostic, type ModelDiagnosticsComputation, type TrainedModel } from "../../resources/trained-model.js";
|
|
3
|
+
import type { CommandDefinition } from "../types.js";
|
|
4
|
+
type Flags = Record<string, string | boolean>;
|
|
5
|
+
export declare function diagnosticKindFromFlags(flags: Flags): ModelDiagnostic;
|
|
6
|
+
/** `compute-diagnostics` flags → kind, features, and computation settings (shared with --plan). */
|
|
7
|
+
export declare function diagnosticsComputationFromFlags(flags: Flags): {
|
|
8
|
+
kind: ComputedModelDiagnostic;
|
|
9
|
+
features: string[];
|
|
10
|
+
computation: ModelDiagnosticsComputation;
|
|
11
|
+
};
|
|
12
|
+
export interface TrainedModelCommandSpec {
|
|
13
|
+
resource: "ml-task" | "saved-model";
|
|
14
|
+
/** Positional ids in examples, e.g. `ANALYSIS_ID TASK_ID MODEL_ID`. */
|
|
15
|
+
ids: string;
|
|
16
|
+
/** What one model is called in descriptions: "lab model" or "saved-model version". */
|
|
17
|
+
noun: string;
|
|
18
|
+
model: (client: DataikuClient, args: string[], projectKey: string | undefined) => TrainedModel;
|
|
19
|
+
downloadDocumentation: (client: DataikuClient, exportId: string, projectKey: string | undefined) => Promise<Response>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* The actions lab models and saved-model versions share, over one
|
|
23
|
+
* {@link TrainedModel}: user meta, scoring exports, diagnostics, documentation.
|
|
24
|
+
*/
|
|
25
|
+
export declare function trainedModelCommands(spec: TrainedModelCommandSpec): Record<string, CommandDefinition>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { unexpectedResponseError, } from "../../errors.js";
|
|
2
|
+
import { COMPUTED_MODEL_DIAGNOSTICS, MODEL_DIAGNOSTICS, } from "../../resources/trained-model.js";
|
|
3
|
+
import { writeResponseToFile, } from "../../utils/response-file.js";
|
|
4
|
+
import { num, parseBooleanOption, requiredJsonInput, splitCsvFlag, } from "../coerce.js";
|
|
5
|
+
import { executionMode, } from "../flags.js";
|
|
6
|
+
import { commandUsage, } from "../syntax.js";
|
|
7
|
+
import { requireArgs, UsageError, } from "../usage.js";
|
|
8
|
+
import { documentationTemplateFromFlags, runDocumentationCommand, taskWaitOptions, } from "./documentation.js";
|
|
9
|
+
export function diagnosticKindFromFlags(flags) {
|
|
10
|
+
const kind = flags["kind"];
|
|
11
|
+
if (typeof kind !== "string" || MODEL_DIAGNOSTICS[kind] !== true) {
|
|
12
|
+
throw new UsageError(`--kind must be one of ${Object.keys(MODEL_DIAGNOSTICS).join(", ")}.`, "invalid_enum");
|
|
13
|
+
}
|
|
14
|
+
return kind;
|
|
15
|
+
}
|
|
16
|
+
/** `compute-diagnostics` flags → kind, features, and computation settings (shared with --plan). */
|
|
17
|
+
export function diagnosticsComputationFromFlags(flags) {
|
|
18
|
+
const kind = flags["kind"];
|
|
19
|
+
if (typeof kind !== "string" || COMPUTED_MODEL_DIAGNOSTICS[kind] !== true) {
|
|
20
|
+
throw new UsageError(`--kind must be one of ${Object.keys(COMPUTED_MODEL_DIAGNOSTICS).join(", ")}.`, "invalid_enum");
|
|
21
|
+
}
|
|
22
|
+
const sampleSize = num(flags["sample-size"], "--sample-size");
|
|
23
|
+
const randomState = num(flags["random-state"], "--random-state");
|
|
24
|
+
const nJobs = num(flags["n-jobs"], "--n-jobs");
|
|
25
|
+
return {
|
|
26
|
+
kind: kind,
|
|
27
|
+
features: splitCsvFlag(flags["features"]),
|
|
28
|
+
computation: {
|
|
29
|
+
...(sampleSize !== undefined ? { sampleSize, } : {}),
|
|
30
|
+
...(randomState !== undefined ? { randomState, } : {}),
|
|
31
|
+
...(nJobs !== undefined ? { nJobs, } : {}),
|
|
32
|
+
},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
/** Write a binary export to `--output`; DSS answering without a body is an unexpected response. */
|
|
36
|
+
async function writeExport(res, out, operation) {
|
|
37
|
+
if (!res.body)
|
|
38
|
+
throw unexpectedResponseError(`${operation} response did not include a body`);
|
|
39
|
+
return { path: out, bytes: await writeResponseToFile(out, res), };
|
|
40
|
+
}
|
|
41
|
+
function requiredOutput(flags) {
|
|
42
|
+
const out = flags["output"];
|
|
43
|
+
if (typeof out !== "string") {
|
|
44
|
+
throw new UsageError("--output PATH is required.", "missing_required_flag");
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* The actions lab models and saved-model versions share, over one
|
|
50
|
+
* {@link TrainedModel}: user meta, scoring exports, diagnostics, documentation.
|
|
51
|
+
*/
|
|
52
|
+
export function trainedModelCommands(spec) {
|
|
53
|
+
const { resource, ids, noun, } = spec;
|
|
54
|
+
const argCount = ids.split(" ").length;
|
|
55
|
+
const model = (c, a, f, action) => {
|
|
56
|
+
requireArgs(a, argCount, commandUsage(resource, action));
|
|
57
|
+
return spec.model(c, a, f["project-key"]);
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
"set-user-meta": {
|
|
61
|
+
handler: async (c, a, f) => {
|
|
62
|
+
const target = model(c, a, f, "set-user-meta");
|
|
63
|
+
const userMeta = requiredJsonInput(f, "User-meta JSON is required via --data, --data-file, or --stdin.");
|
|
64
|
+
if (executionMode(f).dryRun) {
|
|
65
|
+
return {
|
|
66
|
+
dryRun: true,
|
|
67
|
+
action: "set-user-meta",
|
|
68
|
+
resource,
|
|
69
|
+
ids: a.slice(0, argCount),
|
|
70
|
+
payload: userMeta,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
await target.setUserMeta(userMeta);
|
|
74
|
+
return { updated: a[argCount - 1], resource, };
|
|
75
|
+
},
|
|
76
|
+
description: `Update the user metadata (name, description, labels, ...) of a ${noun}. Send only the "userMeta" field of its details, edited.`,
|
|
77
|
+
examples: [
|
|
78
|
+
`dss ${resource} set-user-meta ${ids} --data '{"name":"Churn model","description":"v2"}'`,
|
|
79
|
+
],
|
|
80
|
+
},
|
|
81
|
+
"download-scoring-jar": {
|
|
82
|
+
handler: async (c, a, f) => {
|
|
83
|
+
const target = model(c, a, f, "download-scoring-jar");
|
|
84
|
+
const out = requiredOutput(f);
|
|
85
|
+
const fullClassName = f["full-class-name"];
|
|
86
|
+
const res = await target.downloadScoringJar({
|
|
87
|
+
...(typeof fullClassName === "string" ? { fullClassName, } : {}),
|
|
88
|
+
...(f["include-libs"] !== undefined
|
|
89
|
+
? { includeLibs: parseBooleanOption(f["include-libs"], "--include-libs"), }
|
|
90
|
+
: {}),
|
|
91
|
+
});
|
|
92
|
+
return writeExport(res, out, "Scoring JAR download");
|
|
93
|
+
},
|
|
94
|
+
description: `Download the optimized scoring JAR of a ${noun} (license-gated server side). --full-class-name sets the Java class; --include-libs toggles bundling the scoring libraries.`,
|
|
95
|
+
examples: [`dss ${resource} download-scoring-jar ${ids} --output ./scoring.jar`,],
|
|
96
|
+
},
|
|
97
|
+
"download-scoring-pmml": {
|
|
98
|
+
handler: async (c, a, f) => {
|
|
99
|
+
const target = model(c, a, f, "download-scoring-pmml");
|
|
100
|
+
const out = requiredOutput(f);
|
|
101
|
+
return writeExport(await target.downloadScoringPmml(), out, "Scoring PMML download");
|
|
102
|
+
},
|
|
103
|
+
description: `Download the PMML scoring file of a ${noun} (license-gated server side).`,
|
|
104
|
+
examples: [`dss ${resource} download-scoring-pmml ${ids} --output ./model.pmml`,],
|
|
105
|
+
},
|
|
106
|
+
diagnostics: {
|
|
107
|
+
handler: (c, a, f) => model(c, a, f, "diagnostics").get(diagnosticKindFromFlags(f)),
|
|
108
|
+
description: `Read computed diagnostics of a ${noun}: subpopulation-analyses, partial-dependencies, timeseries-residuals, or (forecasting) per-timeseries-metrics / per-timeseries-evaluation-forecasts.`,
|
|
109
|
+
examples: [`dss ${resource} diagnostics ${ids} --kind partial-dependencies`,],
|
|
110
|
+
},
|
|
111
|
+
"compute-diagnostics": {
|
|
112
|
+
handler: async (c, a, f) => {
|
|
113
|
+
const target = model(c, a, f, "compute-diagnostics");
|
|
114
|
+
const { kind, features, computation, } = diagnosticsComputationFromFlags(f);
|
|
115
|
+
const task = await target.compute(kind, features, computation);
|
|
116
|
+
return f["wait"] === true ? c.futures.waitTask(task, taskWaitOptions(f)) : task;
|
|
117
|
+
},
|
|
118
|
+
description: `Compute subpopulation-analyses or partial-dependencies over --features COLS (--sample-size N, default 10000), or timeseries-residuals, for a ${noun}. Returns the DSS future; --wait waits and returns the result.`,
|
|
119
|
+
examples: [
|
|
120
|
+
`dss ${resource} compute-diagnostics ${ids} --kind partial-dependencies --features age,income --wait`,
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
"generate-documentation": {
|
|
124
|
+
handler: async (c, a, f) => {
|
|
125
|
+
const target = model(c, a, f, "generate-documentation");
|
|
126
|
+
const pk = f["project-key"];
|
|
127
|
+
const task = await target.generateDocumentation(documentationTemplateFromFlags(f));
|
|
128
|
+
return runDocumentationCommand(c, f, task, (exportId) => spec.downloadDocumentation(c, exportId, pk));
|
|
129
|
+
},
|
|
130
|
+
description: `Generate the model documentation (Word docx) of a ${noun} from the DSS default template, an uploaded --template-file, or a template in a managed folder (--folder FOLDER_ID --path PATH). Returns the DSS future; --wait waits; --output PATH waits and downloads.`,
|
|
131
|
+
examples: [`dss ${resource} generate-documentation ${ids} --output model.docx`,],
|
|
132
|
+
},
|
|
133
|
+
"download-documentation": {
|
|
134
|
+
handler: async (c, a, f) => {
|
|
135
|
+
requireArgs(a, 1, commandUsage(resource, "download-documentation"));
|
|
136
|
+
const out = requiredOutput(f);
|
|
137
|
+
const res = await spec.downloadDocumentation(c, a[0], f["project-key"]);
|
|
138
|
+
return writeExport(res, out, "Model documentation download");
|
|
139
|
+
},
|
|
140
|
+
description: "Download a generated model documentation by the exportId from a finished generate-documentation future.",
|
|
141
|
+
examples: [`dss ${resource} download-documentation EXPORT_ID --output model.docx`,],
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shape a `*.list` result for agents: `--contains` filters on identifier
|
|
3
|
+
* fields, `--limit` caps the count (a `list_truncated` warning reports the
|
|
4
|
+
* total), and large DSS objects compact to their decision fields unless
|
|
5
|
+
* `--full` or `--fields` asks for the DSS objects.
|
|
6
|
+
*/
|
|
7
|
+
export declare function shapeListResult(resource: string, result: unknown, flags: Record<string, string | boolean>): unknown;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { num, } from "./coerce.js";
|
|
2
|
+
import { COMPACT_LIST_FIELDS, } from "./list-shapes.js";
|
|
3
|
+
import { enqueueCliWarning, } from "./output.js";
|
|
4
|
+
function record(value) {
|
|
5
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
6
|
+
}
|
|
7
|
+
function compactValue(item, spec) {
|
|
8
|
+
let value = item;
|
|
9
|
+
for (const key of spec.path.split("."))
|
|
10
|
+
value = record(value)[key];
|
|
11
|
+
if (spec.roleRefs !== true)
|
|
12
|
+
return value;
|
|
13
|
+
// Dataset refs across every role of recipe inputs/outputs (`{main:{items:[{ref}]}}`).
|
|
14
|
+
return Object.values(record(value)).flatMap((role) => {
|
|
15
|
+
const items = record(role).items;
|
|
16
|
+
return Array.isArray(items)
|
|
17
|
+
? items.flatMap((entry) => {
|
|
18
|
+
const ref = record(entry).ref;
|
|
19
|
+
return typeof ref === "string" ? [ref,] : [];
|
|
20
|
+
})
|
|
21
|
+
: [];
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
/** Identifier fields `--contains` matches; string items match themselves. */
|
|
25
|
+
const IDENTIFIER_FIELDS = [
|
|
26
|
+
"id",
|
|
27
|
+
"name",
|
|
28
|
+
"projectKey",
|
|
29
|
+
"envName",
|
|
30
|
+
"login",
|
|
31
|
+
"displayName",
|
|
32
|
+
"label",
|
|
33
|
+
"runnableType",
|
|
34
|
+
];
|
|
35
|
+
/** `job list` filters by --contains/--limit itself (job name, state, outputs). */
|
|
36
|
+
const LISTS_WITH_OWN_FILTERS = { job: true, };
|
|
37
|
+
function itemMatches(item, needle) {
|
|
38
|
+
if (typeof item === "string")
|
|
39
|
+
return item.toLowerCase().includes(needle);
|
|
40
|
+
const fields = record(item);
|
|
41
|
+
return IDENTIFIER_FIELDS.some((field) => {
|
|
42
|
+
const value = fields[field];
|
|
43
|
+
return typeof value === "string" && value.toLowerCase().includes(needle);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Shape a `*.list` result for agents: `--contains` filters on identifier
|
|
48
|
+
* fields, `--limit` caps the count (a `list_truncated` warning reports the
|
|
49
|
+
* total), and large DSS objects compact to their decision fields unless
|
|
50
|
+
* `--full` or `--fields` asks for the DSS objects.
|
|
51
|
+
*/
|
|
52
|
+
export function shapeListResult(resource, result, flags) {
|
|
53
|
+
if (!Array.isArray(result))
|
|
54
|
+
return result;
|
|
55
|
+
let items = result;
|
|
56
|
+
if (LISTS_WITH_OWN_FILTERS[resource] !== true) {
|
|
57
|
+
const contains = flags["contains"];
|
|
58
|
+
if (typeof contains === "string" && contains.length > 0) {
|
|
59
|
+
const needle = contains.toLowerCase();
|
|
60
|
+
items = items.filter((item) => itemMatches(item, needle));
|
|
61
|
+
}
|
|
62
|
+
const limit = num(flags["limit"], "--limit");
|
|
63
|
+
if (limit !== undefined && items.length > limit) {
|
|
64
|
+
enqueueCliWarning({
|
|
65
|
+
code: "list_truncated",
|
|
66
|
+
shown: limit,
|
|
67
|
+
total: items.length,
|
|
68
|
+
hint: "Raise --limit or narrow with --contains.",
|
|
69
|
+
});
|
|
70
|
+
items = items.slice(0, Math.max(0, limit));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const fields = COMPACT_LIST_FIELDS[resource];
|
|
74
|
+
if (!fields || flags["full"] === true || typeof flags["fields"] === "string")
|
|
75
|
+
return items;
|
|
76
|
+
return items.map((item) => Object.fromEntries(Object.entries(fields).map(([name, spec,]) => [name, compactValue(record(item), spec),])));
|
|
77
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact `RESOURCE.list` item shapes, as plain data shared by the runtime
|
|
3
|
+
* mapper (src/cli/list-output.ts) and the discovery output schemas
|
|
4
|
+
* (src/cli/output-schemas.ts), so the advertised schema is the emitted shape.
|
|
5
|
+
*/
|
|
6
|
+
/** Where a compact field comes from in the DSS object, and its JSON Schema. */
|
|
7
|
+
export interface CompactListField {
|
|
8
|
+
/** Dotted path into the DSS list item. */
|
|
9
|
+
path: string;
|
|
10
|
+
/** Flatten recipe roles (`{main:{items:[{ref}]}}`) to the dataset refs. */
|
|
11
|
+
roleRefs?: true;
|
|
12
|
+
schema: Record<string, unknown>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Lists whose DSS objects are large: the identifier the follow-up get/build
|
|
16
|
+
* commands take, the kind, and the one or two attributes that decide what to
|
|
17
|
+
* call next. Lists not named here keep the DSS objects.
|
|
18
|
+
*/
|
|
19
|
+
export declare const COMPACT_LIST_FIELDS: Record<string, Record<string, CompactListField>>;
|
|
20
|
+
/** Discovery output schemas of the compact `RESOURCE.list` defaults. */
|
|
21
|
+
export declare function compactListOutputSchemas(): Record<string, Record<string, unknown>>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compact `RESOURCE.list` item shapes, as plain data shared by the runtime
|
|
3
|
+
* mapper (src/cli/list-output.ts) and the discovery output schemas
|
|
4
|
+
* (src/cli/output-schemas.ts), so the advertised schema is the emitted shape.
|
|
5
|
+
*/
|
|
6
|
+
const STRING = { type: ["string", "null",], };
|
|
7
|
+
const BOOLEAN = { type: ["boolean", "null",], };
|
|
8
|
+
const NUMBER = { type: ["number", "null",], };
|
|
9
|
+
const STRINGS = { type: "array", items: { type: "string", }, };
|
|
10
|
+
const field = (path, schema) => ({
|
|
11
|
+
path,
|
|
12
|
+
schema,
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Lists whose DSS objects are large: the identifier the follow-up get/build
|
|
16
|
+
* commands take, the kind, and the one or two attributes that decide what to
|
|
17
|
+
* call next. Lists not named here keep the DSS objects.
|
|
18
|
+
*/
|
|
19
|
+
export const COMPACT_LIST_FIELDS = {
|
|
20
|
+
project: {
|
|
21
|
+
projectKey: field("projectKey", STRING),
|
|
22
|
+
name: field("name", STRING),
|
|
23
|
+
ownerLogin: field("ownerLogin", STRING),
|
|
24
|
+
},
|
|
25
|
+
dataset: {
|
|
26
|
+
name: field("name", STRING),
|
|
27
|
+
type: field("type", STRING),
|
|
28
|
+
managed: field("managed", BOOLEAN),
|
|
29
|
+
connection: field("params.connection", STRING),
|
|
30
|
+
},
|
|
31
|
+
recipe: {
|
|
32
|
+
name: field("name", STRING),
|
|
33
|
+
type: field("type", STRING),
|
|
34
|
+
inputs: { path: "inputs", roleRefs: true, schema: STRINGS, },
|
|
35
|
+
outputs: { path: "outputs", roleRefs: true, schema: STRINGS, },
|
|
36
|
+
},
|
|
37
|
+
scenario: {
|
|
38
|
+
id: field("id", STRING),
|
|
39
|
+
name: field("name", STRING),
|
|
40
|
+
type: field("type", STRING),
|
|
41
|
+
active: field("active", BOOLEAN),
|
|
42
|
+
running: field("running", BOOLEAN),
|
|
43
|
+
},
|
|
44
|
+
folder: {
|
|
45
|
+
id: field("id", STRING),
|
|
46
|
+
name: field("name", STRING),
|
|
47
|
+
type: field("type", STRING),
|
|
48
|
+
connection: field("params.connection", STRING),
|
|
49
|
+
},
|
|
50
|
+
job: {
|
|
51
|
+
id: field("def.id", STRING),
|
|
52
|
+
name: field("def.name", STRING),
|
|
53
|
+
type: field("def.type", STRING),
|
|
54
|
+
state: field("state", STRING),
|
|
55
|
+
startTime: field("startTime", NUMBER),
|
|
56
|
+
endTime: field("endTime", NUMBER),
|
|
57
|
+
},
|
|
58
|
+
"code-env": {
|
|
59
|
+
envName: field("envName", STRING),
|
|
60
|
+
envLang: field("envLang", STRING),
|
|
61
|
+
deploymentMode: field("deploymentMode", STRING),
|
|
62
|
+
},
|
|
63
|
+
future: {
|
|
64
|
+
jobId: field("jobId", STRING),
|
|
65
|
+
owner: field("owner", STRING),
|
|
66
|
+
alive: field("alive", BOOLEAN),
|
|
67
|
+
hasResult: field("hasResult", BOOLEAN),
|
|
68
|
+
runningTime: field("runningTime", NUMBER),
|
|
69
|
+
},
|
|
70
|
+
user: {
|
|
71
|
+
login: field("login", STRING),
|
|
72
|
+
displayName: field("displayName", STRING),
|
|
73
|
+
userProfile: field("userProfile", STRING),
|
|
74
|
+
groups: field("groups", STRINGS),
|
|
75
|
+
enabled: field("enabled", BOOLEAN),
|
|
76
|
+
},
|
|
77
|
+
group: {
|
|
78
|
+
name: field("name", STRING),
|
|
79
|
+
description: field("description", STRING),
|
|
80
|
+
admin: field("admin", BOOLEAN),
|
|
81
|
+
},
|
|
82
|
+
plugin: {
|
|
83
|
+
id: field("id", STRING),
|
|
84
|
+
version: field("version", STRING),
|
|
85
|
+
label: field("meta.label", STRING),
|
|
86
|
+
},
|
|
87
|
+
macro: {
|
|
88
|
+
runnableType: field("runnableType", STRING),
|
|
89
|
+
ownerPluginId: field("ownerPluginId", STRING),
|
|
90
|
+
label: field("meta.label", STRING),
|
|
91
|
+
},
|
|
92
|
+
meaning: {
|
|
93
|
+
id: field("id", STRING),
|
|
94
|
+
label: field("label", STRING),
|
|
95
|
+
type: field("type", STRING),
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
/** Discovery output schemas of the compact `RESOURCE.list` defaults. */
|
|
99
|
+
export function compactListOutputSchemas() {
|
|
100
|
+
return Object.fromEntries(Object.entries(COMPACT_LIST_FIELDS).map(([resource, fields,]) => [
|
|
101
|
+
`${resource}.list`,
|
|
102
|
+
{
|
|
103
|
+
type: "array",
|
|
104
|
+
items: {
|
|
105
|
+
type: "object",
|
|
106
|
+
additionalProperties: false,
|
|
107
|
+
properties: Object.fromEntries(Object.entries(fields).map(([name, spec,]) => [name, spec.schema,])),
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
]));
|
|
111
|
+
}
|
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Canonical discovery output schemas, keyed by `resource.action`.
|
|
3
|
-
*
|
|
4
|
-
* TypeBox definitions from `packages/types` plus the inline literal schemas
|
|
5
|
-
* that exist only on the CLI side (code-env log envelope, code.run outcome,
|
|
6
|
-
* project-library payloads, notebook saves, sql.query handoff envelope).
|
|
7
|
-
* `scripts/generate-action-output-schemas.mjs` serializes this map into
|
|
8
|
-
* `src/generated/action-output-schemas.json`; discovery (src/cli/contract.ts)
|
|
9
|
-
* reads that generated JSON as plain data, so the TypeBox schema graph never
|
|
10
|
-
* loads on the discovery path. SDK validation keeps using the TypeBox schema
|
|
11
|
-
* objects from `packages/types` directly.
|
|
12
|
-
*
|
|
13
|
-
* Imported by the generator and the discovery parity test only.
|
|
14
|
-
*/
|
|
15
1
|
/**
|
|
16
2
|
* Canonical TypeBox-derived output schemas, keyed by `resource.action`. This is
|
|
17
3
|
* the single source of truth for `src/generated/action-output-schemas.json`,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*
|
|
13
13
|
* Imported by the generator and the discovery parity test only.
|
|
14
14
|
*/
|
|
15
|
+
import { compactListOutputSchemas, } from "./list-shapes.js";
|
|
15
16
|
const PROJECT_LIBRARY_ITEM_OUTPUT_SCHEMA = {
|
|
16
17
|
type: "object",
|
|
17
18
|
additionalProperties: false,
|
|
@@ -62,9 +63,8 @@ let commandOutputSchemas;
|
|
|
62
63
|
export function typeBoxCommandOutputSchemas() {
|
|
63
64
|
if (commandOutputSchemas)
|
|
64
65
|
return commandOutputSchemas;
|
|
65
|
-
const { CodeEnvDetailsSchema, CodeEnvLogSummaryArraySchema,
|
|
66
|
+
const { CodeEnvDetailsSchema, CodeEnvLogSummaryArraySchema, CodeEnvUsageArraySchema, CodeEnvVersionForProjectSchema, DatasetDetailsSchema, DatasetSchemaSchema, FlowZoneArraySchema, FlowZoneSchema, JobWaitResultSchema, JupyterNotebookContentSchema, JupyterNotebookSummaryArraySchema, NotebookSessionArraySchema, ProjectDetailsSchema, ProjectMetadataSchema, ScenarioDetailsSchema, ScenarioStatusSchema, SqlNotebookContentSchema, SqlNotebookHistorySchema, SqlNotebookSummaryArraySchema, SqlQueryResponseSchema, } = require("../schemas.js");
|
|
66
67
|
return commandOutputSchemas = {
|
|
67
|
-
"code-env.list": CodeEnvSummaryArraySchema,
|
|
68
68
|
"code-env.get": CodeEnvDetailsSchema,
|
|
69
69
|
"code-env.list-logs": CodeEnvLogSummaryArraySchema,
|
|
70
70
|
"code-env.get-log": {
|
|
@@ -196,17 +196,12 @@ export function typeBoxCommandOutputSchemas() {
|
|
|
196
196
|
maxLines: { type: "integer", minimum: 0, },
|
|
197
197
|
},
|
|
198
198
|
},
|
|
199
|
-
"project.list": ProjectSummaryArraySchema,
|
|
200
199
|
"project.get": ProjectDetailsSchema,
|
|
201
200
|
"project.metadata": ProjectMetadataSchema,
|
|
202
|
-
"dataset.list": DatasetSummaryArraySchema,
|
|
203
201
|
"dataset.get": DatasetDetailsSchema,
|
|
204
202
|
"dataset.schema": DatasetSchemaSchema,
|
|
205
|
-
"recipe.list": RecipeSummaryArraySchema,
|
|
206
|
-
"job.list": JobSummaryArraySchema,
|
|
207
203
|
"job.wait": JobWaitResultSchema,
|
|
208
204
|
"job.monitor": JobWaitResultSchema,
|
|
209
|
-
"scenario.list": ScenarioSummaryArraySchema,
|
|
210
205
|
"scenario.get": ScenarioDetailsSchema,
|
|
211
206
|
"scenario.status": ScenarioStatusSchema,
|
|
212
207
|
"flow-zone.list": FlowZoneArraySchema,
|
|
@@ -229,5 +224,7 @@ export function typeBoxCommandOutputSchemas() {
|
|
|
229
224
|
},
|
|
230
225
|
],
|
|
231
226
|
},
|
|
227
|
+
// `*.list` defaults to compact items; `--full` returns the DSS objects.
|
|
228
|
+
...compactListOutputSchemas(),
|
|
232
229
|
};
|
|
233
230
|
}
|
package/dist/src/cli/output.d.ts
CHANGED
|
@@ -12,7 +12,6 @@ export declare function pickResultFields(item: unknown, fields: string[]): unkno
|
|
|
12
12
|
*/
|
|
13
13
|
export declare function projectResultFields(result: unknown, fields: string[], availableFieldNames?: () => string[]): unknown;
|
|
14
14
|
export declare function writeCommandResult(result: unknown): void;
|
|
15
|
-
export declare function transientBodyWithTargetContext(body: string, target: string, elapsedMs: number): string;
|
|
16
15
|
export declare function addTransientTargetContext(error: unknown, target: string, elapsedMs: number): never;
|
|
17
16
|
export declare function isFailedWaitResult(result: unknown): boolean;
|
|
18
17
|
export declare function isAssertionFailureResult(result: unknown): boolean;
|