@toloka-platform/sdk 0.1.0-preview.5 → 0.1.0-preview.7
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 +7 -11
- package/dist/cli/toloka.js +211 -125
- package/dist/cli/toloka.js.jsc +0 -0
- package/dist/cli/toloka.js.jsc.sha256 +1 -1
- package/dist/cli/toloka.js.sha256 +1 -1
- package/docs/authoring.md +17 -4
- package/docs/cli-v2.md +3 -3
- package/docs/cli.md +55 -17
- package/docs/datasets.md +8 -9
- package/docs/getting-started.md +7 -12
- package/docs/limitations.md +10 -13
- package/docs/testing-examples.md +248 -0
- package/docs/testing.md +115 -0
- package/docs/troubleshooting.md +0 -11
- package/examples/face-image-collection/README.md +7 -1
- package/examples/face-image-collection/fields.ts +20 -8
- package/examples/face-image-collection/nodes/annotate-faces/node.ts +1 -9
- package/examples/face-image-collection/nodes/order-faces/node.ts +1 -1
- package/examples/face-image-collection/pipeline.browser.integration.spec.ts +78 -0
- package/examples/video-pair-rating/README.md +14 -6
- package/examples/video-pair-rating/nodes/adjudicate/node.ts +1 -3
- package/examples/video-pair-rating/nodes/adjudicate_op/node.ts +1 -3
- package/examples/video-pair-rating/nodes/overall_preference_pick/node.ts +1 -1
- package/examples/video-pair-rating/nodes/rate_pair/node.ts +1 -1
- package/examples/video-pair-rating/pipeline.browser.integration.spec.ts +267 -0
- package/openapi/gen/api.ts +819 -898
- package/openapi/gen/models/baseModel.ts +9 -0
- package/openapi/gen/models/baseModelId.ts +2 -0
- package/openapi/gen/models/baseModelIdProperty.ts +2 -0
- package/openapi/gen/models/createDatasetExportBody.ts +3 -3
- package/openapi/gen/models/createDatasetExportBodyOneOf.ts +6 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfFormat.ts +2 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThree.ts +10 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeContract.ts +2 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeFormat.ts +2 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilter.ts +9 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItem.ts +6 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItem.ts +12 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemOperator.ts +2 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/createDatasetExportBodyOneOfThreeItemFilterFieldValuesItemValue.ts +2 -0
- package/openapi/gen/models/createProjectModelBody.ts +8 -0
- package/openapi/gen/models/createProjectModelBodyDescription.ts +2 -0
- package/openapi/gen/models/datasetExportReport.ts +9 -0
- package/openapi/gen/models/datasetExportReportErrorsItem.ts +5 -0
- package/openapi/gen/models/datasetExportReportTokens.ts +7 -0
- package/openapi/gen/models/exportJob.ts +2 -0
- package/openapi/gen/models/exportJobReport.ts +3 -0
- package/openapi/gen/models/generationNodeSettingsAudienceAndPricingOneOfOneone.ts +1 -0
- package/openapi/gen/models/index.ts +64 -0
- package/openapi/gen/models/listBaseModels200.ts +5 -0
- package/openapi/gen/models/listModelVersions200.ts +7 -0
- package/openapi/gen/models/listModelVersionsParams.ts +5 -0
- package/openapi/gen/models/listProjectModels200.ts +7 -0
- package/openapi/gen/models/listProjectModelsParams.ts +5 -0
- package/openapi/gen/models/model.ts +16 -0
- package/openapi/gen/models/modelArchivedAt.ts +2 -0
- package/openapi/gen/models/modelDescription.ts +2 -0
- package/openapi/gen/models/modelId.ts +2 -0
- package/openapi/gen/models/modelVersion.ts +9 -0
- package/openapi/gen/models/modelVersionId.ts +2 -0
- package/openapi/gen/models/pipelineGraphSnapshotGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
- package/openapi/gen/models/sampleDatasetItemsBody.ts +4 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOf.ts +8 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilter.ts +9 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItem.ts +6 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItem.ts +12 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemOperator.ts +2 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfItemFilterFieldValuesItemValue.ts +2 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethree.ts +8 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilter.ts +9 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItem.ts +6 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldSourcesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItem.ts +12 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemOperator.ts +2 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItem.ts +4 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOf.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemSourcesItemOneOfTwo.ts +5 -0
- package/openapi/gen/models/sampleDatasetItemsBodyAnyOfOnethreeItemFilterFieldValuesItemValue.ts +2 -0
- package/openapi/gen/models/sampleJob.ts +13 -0
- package/openapi/gen/models/sampleJobStatus.ts +2 -0
- package/openapi/gen/models/sampleResult.ts +5 -0
- package/openapi/gen/models/validatePipelineGraphBodyGraphNodesItemOneOfThreeoneSettingsAudienceAndPricingOneOfOneone.ts +1 -0
- package/package.json +23 -2
- package/skills/catalog.json +14 -6
- package/skills/toloka-build/SKILL.md +34 -12
- package/skills/toloka-build/references/node-semantics.md +27 -24
- package/skills/toloka-build/references/workflow-and-cli.md +67 -27
- package/skills/toloka-report-issue/references/report-template.md +1 -1
- package/src/checks/ast.ts +32 -0
- package/src/checks/index.ts +14 -0
- package/src/checks/postprocess-collisions.ts +145 -0
- package/src/checks/task-ui-declarations.generated.ts +19 -0
- package/src/checks/task-ui-typecheck.ts +345 -0
- package/src/checks/typescript-vfs.d.ts +26 -0
- package/src/checks/typescript-vfs.js +5 -0
- package/src/cli/analytics.ts +1 -0
- package/src/cli/check.ts +977 -0
- package/src/cli/data.ts +148 -25
- package/src/cli/models.ts +188 -0
- package/src/cli/program.ts +246 -20
- package/src/cli/project-context.ts +14 -4
- package/src/cli/runs.ts +21 -11
- package/src/cli/skill.ts +17 -6
- package/src/client.ts +109 -35
- package/src/errors.ts +25 -10
- package/src/testing/code-node.ts +160 -0
- package/src/testing/index.ts +15 -0
- package/src/testing/task-ui-browser.ts +120 -0
- package/src/testing/task-ui-validation.ts +135 -0
- package/src/testing/task-ui.ts +557 -0
- package/src/testing/test-evidence.ts +170 -0
- package/src/testing/types.ts +141 -0
- package/src/transport.ts +16 -13
package/src/cli/program.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
1
|
import { Command, CommanderError, InvalidArgumentError } from "commander";
|
|
2
2
|
import { assert } from "es-toolkit";
|
|
3
|
+
import { writeFile } from "node:fs/promises";
|
|
3
4
|
import { resolve } from "node:path";
|
|
4
5
|
import { createInterface } from "node:readline/promises";
|
|
5
6
|
import { createClient } from "../client";
|
|
6
7
|
import type { PotatoClient } from "../client";
|
|
7
8
|
import { readTolokaState } from "../state";
|
|
8
9
|
import type { TolokaState } from "../state";
|
|
10
|
+
import {
|
|
11
|
+
CHECK_RESULT_FILE_ENV,
|
|
12
|
+
checkProject,
|
|
13
|
+
watchProjectChecks,
|
|
14
|
+
runFreshCheck,
|
|
15
|
+
type CheckProjectOptions,
|
|
16
|
+
type CheckProjectResult,
|
|
17
|
+
} from "./check";
|
|
9
18
|
import { authStatus, login, logout } from "./auth";
|
|
10
19
|
import type { AuthPrompt } from "./auth";
|
|
11
20
|
import { commandNameFromArgv, createCliAnalytics } from "./analytics";
|
|
@@ -33,6 +42,16 @@ import {
|
|
|
33
42
|
projectStatus,
|
|
34
43
|
validateProject,
|
|
35
44
|
} from "./project";
|
|
45
|
+
import {
|
|
46
|
+
archiveModel,
|
|
47
|
+
createModel,
|
|
48
|
+
getBaseModel,
|
|
49
|
+
getModel,
|
|
50
|
+
listBaseModels,
|
|
51
|
+
listModels,
|
|
52
|
+
listModelVersions,
|
|
53
|
+
parseBaseModelId,
|
|
54
|
+
} from "./models";
|
|
36
55
|
import {
|
|
37
56
|
MISSING_PROJECT_ID_MESSAGE,
|
|
38
57
|
addProject,
|
|
@@ -73,6 +92,7 @@ export type ProgramContext = {
|
|
|
73
92
|
readonly environment: TolokaEnvironment;
|
|
74
93
|
readonly io: CliIo;
|
|
75
94
|
readonly injectedClient?: PotatoClient;
|
|
95
|
+
readonly runChecks?: typeof checkProject;
|
|
76
96
|
readonly analytics?: CliAnalytics;
|
|
77
97
|
};
|
|
78
98
|
|
|
@@ -192,6 +212,29 @@ export type CreatedProgram = {
|
|
|
192
212
|
export function createProgram(context: ProgramContext): CreatedProgram {
|
|
193
213
|
const program = new Command("toloka");
|
|
194
214
|
const exitCode = { value: 0 };
|
|
215
|
+
const runChecks = context.runChecks ?? checkProject;
|
|
216
|
+
const runChecksInterruptibly = async (
|
|
217
|
+
options: CheckProjectOptions,
|
|
218
|
+
runner: (options: CheckProjectOptions) => Promise<CheckProjectResult> = runChecks,
|
|
219
|
+
): Promise<{ result: CheckProjectResult; interruptedExitCode?: number }> => {
|
|
220
|
+
const abortController = new AbortController();
|
|
221
|
+
let interruptedExitCode: number | undefined;
|
|
222
|
+
const interrupt = (exitCode: number) => {
|
|
223
|
+
interruptedExitCode = exitCode;
|
|
224
|
+
abortController.abort();
|
|
225
|
+
};
|
|
226
|
+
const interruptSigint = () => interrupt(130);
|
|
227
|
+
const interruptSigterm = () => interrupt(143);
|
|
228
|
+
process.on("SIGINT", interruptSigint);
|
|
229
|
+
process.on("SIGTERM", interruptSigterm);
|
|
230
|
+
try {
|
|
231
|
+
const result = await runner({ ...options, signal: abortController.signal });
|
|
232
|
+
return { result, ...(interruptedExitCode === undefined ? {} : { interruptedExitCode }) };
|
|
233
|
+
} finally {
|
|
234
|
+
process.off("SIGINT", interruptSigint);
|
|
235
|
+
process.off("SIGTERM", interruptSigterm);
|
|
236
|
+
}
|
|
237
|
+
};
|
|
195
238
|
let warnedAboutStaleSkills = false;
|
|
196
239
|
|
|
197
240
|
program
|
|
@@ -314,8 +357,9 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
314
357
|
.description("select and manage the current platform project");
|
|
315
358
|
project
|
|
316
359
|
.command("list")
|
|
360
|
+
.option("--limit <n>", "maximum projects to list", positiveInteger)
|
|
317
361
|
.description("list remote projects this key can see")
|
|
318
|
-
.action(async () => {
|
|
362
|
+
.action(async (options: { limit?: number }) => {
|
|
319
363
|
const flags = globals();
|
|
320
364
|
const { client, stored, state } = await resolveContext(flags, context);
|
|
321
365
|
const selected = resolveSelectedProject(flags, context, stored, state);
|
|
@@ -323,6 +367,7 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
323
367
|
client,
|
|
324
368
|
io: context.io,
|
|
325
369
|
...(selected === undefined ? {} : { context: selected }),
|
|
370
|
+
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
326
371
|
});
|
|
327
372
|
});
|
|
328
373
|
project
|
|
@@ -430,9 +475,12 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
430
475
|
.action(async () => {
|
|
431
476
|
const flags = globals();
|
|
432
477
|
const sourceRoot = resolveInitRoot(flags);
|
|
433
|
-
const stored = await readCommandCredentials(flags, context);
|
|
434
478
|
const state = await readTolokaState(sourceRoot);
|
|
435
|
-
const selected =
|
|
479
|
+
const selected = resolveProjectContext({
|
|
480
|
+
flags,
|
|
481
|
+
environment: context.environment,
|
|
482
|
+
...(state === undefined ? {} : { state }),
|
|
483
|
+
});
|
|
436
484
|
const result = await initProject(
|
|
437
485
|
sourceRoot,
|
|
438
486
|
selected === undefined ? {} : { projectId: selected.id },
|
|
@@ -490,13 +538,89 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
490
538
|
if (!result.valid) exitCode.value = 1;
|
|
491
539
|
});
|
|
492
540
|
|
|
541
|
+
program
|
|
542
|
+
.command("check")
|
|
543
|
+
.argument("[pipeline]", "pipeline source directory or name")
|
|
544
|
+
.option("--node <node>", "check one code or human-work node by name or id")
|
|
545
|
+
.option("--json", "print the structured check report", false)
|
|
546
|
+
.option("--watch", "rerun checks when project files change", false)
|
|
547
|
+
.description("run the complete local source, type, and test gate")
|
|
548
|
+
.action(
|
|
549
|
+
async (
|
|
550
|
+
pipeline: string | undefined,
|
|
551
|
+
options: { node?: string; json: boolean; watch: boolean },
|
|
552
|
+
) => {
|
|
553
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
554
|
+
const checkOptions = {
|
|
555
|
+
environment: context.environment,
|
|
556
|
+
sourceRoot,
|
|
557
|
+
...(pipeline === undefined ? {} : { pipeline }),
|
|
558
|
+
...(options.node === undefined ? {} : { node: options.node }),
|
|
559
|
+
...(options.json ? { json: true } : {}),
|
|
560
|
+
};
|
|
561
|
+
const writeCheckOutput = async (output: string): Promise<void> => {
|
|
562
|
+
const resultPath = context.environment[CHECK_RESULT_FILE_ENV];
|
|
563
|
+
if (resultPath === undefined) context.io.stdout(output);
|
|
564
|
+
else await writeFile(resultPath, output, "utf8");
|
|
565
|
+
};
|
|
566
|
+
const checkRunner =
|
|
567
|
+
context.runChecks === undefined &&
|
|
568
|
+
context.environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
569
|
+
? runFreshCheck
|
|
570
|
+
: runChecks;
|
|
571
|
+
if (options.watch) {
|
|
572
|
+
const abortController = new AbortController();
|
|
573
|
+
const stop = () => abortController.abort();
|
|
574
|
+
process.on("SIGINT", stop);
|
|
575
|
+
process.on("SIGTERM", stop);
|
|
576
|
+
try {
|
|
577
|
+
await watchProjectChecks(
|
|
578
|
+
checkOptions,
|
|
579
|
+
async (result) => {
|
|
580
|
+
await writeCheckOutput(result.output);
|
|
581
|
+
exitCode.value = result.exitCode;
|
|
582
|
+
},
|
|
583
|
+
undefined,
|
|
584
|
+
abortController.signal,
|
|
585
|
+
);
|
|
586
|
+
} finally {
|
|
587
|
+
process.off("SIGINT", stop);
|
|
588
|
+
process.off("SIGTERM", stop);
|
|
589
|
+
}
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
const { result, interruptedExitCode } = await runChecksInterruptibly(
|
|
593
|
+
checkOptions,
|
|
594
|
+
checkRunner,
|
|
595
|
+
);
|
|
596
|
+
await writeCheckOutput(result.output);
|
|
597
|
+
exitCode.value = interruptedExitCode ?? result.exitCode;
|
|
598
|
+
},
|
|
599
|
+
);
|
|
600
|
+
|
|
493
601
|
program
|
|
494
602
|
.command("apply")
|
|
495
|
-
.description("make the platform match
|
|
603
|
+
.description("check source, then make the platform match it")
|
|
496
604
|
.action(async () => {
|
|
497
|
-
const
|
|
605
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
606
|
+
const { result: checked, interruptedExitCode } = await runChecksInterruptibly({
|
|
607
|
+
sourceRoot,
|
|
608
|
+
environment: context.environment,
|
|
609
|
+
});
|
|
610
|
+
context.io.stdout(checked.output);
|
|
611
|
+
if (interruptedExitCode !== undefined) {
|
|
612
|
+
exitCode.value = interruptedExitCode;
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
if (checked.exitCode !== 0) {
|
|
616
|
+
exitCode.value = checked.exitCode;
|
|
617
|
+
return;
|
|
618
|
+
}
|
|
619
|
+
assert(checked.source, "Passing checks must return project source");
|
|
620
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
498
621
|
const result = await applyProject(client, {
|
|
499
622
|
sourceRoot,
|
|
623
|
+
source: checked.source,
|
|
500
624
|
...(projectId === undefined ? {} : { projectId }),
|
|
501
625
|
});
|
|
502
626
|
context.io.stdout(result.output);
|
|
@@ -533,6 +657,94 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
533
657
|
}
|
|
534
658
|
});
|
|
535
659
|
|
|
660
|
+
const baseModel = program
|
|
661
|
+
.command("base-model")
|
|
662
|
+
.description("inspect the global base model catalog");
|
|
663
|
+
baseModel
|
|
664
|
+
.command("list")
|
|
665
|
+
.option("--json", "print JSON", false)
|
|
666
|
+
.description("list base models")
|
|
667
|
+
.action(async (options: { json: boolean }) => {
|
|
668
|
+
const { client } = await resolveContext(globals(), context);
|
|
669
|
+
await listBaseModels(client, { json: options.json }, context.io);
|
|
670
|
+
});
|
|
671
|
+
baseModel
|
|
672
|
+
.command("get")
|
|
673
|
+
.argument("<id>", "base model ID")
|
|
674
|
+
.option("--json", "print JSON", false)
|
|
675
|
+
.description("show one base model")
|
|
676
|
+
.action(async (id: string, options: { json: boolean }) => {
|
|
677
|
+
const { client } = await resolveContext(globals(), context);
|
|
678
|
+
await getBaseModel(client, { id: parseBaseModelId(id), json: options.json }, context.io);
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
const model = program
|
|
682
|
+
.command("model")
|
|
683
|
+
.description("manage project Models and immutable versions");
|
|
684
|
+
model
|
|
685
|
+
.command("list")
|
|
686
|
+
.option("--json", "print JSON", false)
|
|
687
|
+
.description("list Models owned by the selected project")
|
|
688
|
+
.action(async (options: { json: boolean }) => {
|
|
689
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
690
|
+
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
691
|
+
await listModels(client, { projectId, json: options.json }, context.io);
|
|
692
|
+
});
|
|
693
|
+
model
|
|
694
|
+
.command("get")
|
|
695
|
+
.argument("<name-or-id>", "project Model name or ID")
|
|
696
|
+
.option("--json", "print JSON", false)
|
|
697
|
+
.description("show one project Model")
|
|
698
|
+
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
699
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
700
|
+
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
701
|
+
await getModel(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
702
|
+
});
|
|
703
|
+
model
|
|
704
|
+
.command("versions")
|
|
705
|
+
.argument("<name-or-id>", "project Model name or ID")
|
|
706
|
+
.option("--json", "print JSON", false)
|
|
707
|
+
.description("list immutable versions of a model")
|
|
708
|
+
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
709
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
710
|
+
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
711
|
+
await listModelVersions(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
712
|
+
});
|
|
713
|
+
model
|
|
714
|
+
.command("create")
|
|
715
|
+
.argument("<name>", "new Model name")
|
|
716
|
+
.requiredOption("--base <id>", "base model ID")
|
|
717
|
+
.option("--description <text>", "Model description")
|
|
718
|
+
.option("--json", "print JSON", false)
|
|
719
|
+
.description("create project Model metadata without a Model Version")
|
|
720
|
+
.action(
|
|
721
|
+
async (name: string, options: { base: string; description?: string; json: boolean }) => {
|
|
722
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
723
|
+
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
724
|
+
await createModel(
|
|
725
|
+
client,
|
|
726
|
+
{
|
|
727
|
+
projectId,
|
|
728
|
+
name,
|
|
729
|
+
description: options.description,
|
|
730
|
+
baseModelId: parseBaseModelId(options.base),
|
|
731
|
+
json: options.json,
|
|
732
|
+
},
|
|
733
|
+
context.io,
|
|
734
|
+
);
|
|
735
|
+
},
|
|
736
|
+
);
|
|
737
|
+
model
|
|
738
|
+
.command("archive")
|
|
739
|
+
.option("--json", "print JSON", false)
|
|
740
|
+
.argument("<name-or-id>", "project Model name or ID")
|
|
741
|
+
.description("archive a project Model")
|
|
742
|
+
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
743
|
+
const { client, projectId } = await resolveContext(globals(), context);
|
|
744
|
+
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
745
|
+
await archiveModel(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
746
|
+
});
|
|
747
|
+
|
|
536
748
|
const dataset = program
|
|
537
749
|
.command("dataset")
|
|
538
750
|
.description("inspect and move dataset catalog and rows");
|
|
@@ -580,7 +792,7 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
580
792
|
dataset
|
|
581
793
|
.command("upload")
|
|
582
794
|
.argument("<name>", "dataset name or id")
|
|
583
|
-
.argument("<file>", "
|
|
795
|
+
.argument("<file>", "JSON array, JSONL, or CSV file")
|
|
584
796
|
.description("append rows to a dataset")
|
|
585
797
|
.action(async (name: string, file: string) => {
|
|
586
798
|
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
@@ -601,11 +813,17 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
601
813
|
.description("operate on a live pipeline in the selected project");
|
|
602
814
|
pipeline
|
|
603
815
|
.command("list")
|
|
816
|
+
.option("--limit <n>", "maximum pipelines to list", positiveInteger)
|
|
604
817
|
.description("list live pipelines in the selected project")
|
|
605
|
-
.action(async () => {
|
|
818
|
+
.action(async (options: { limit?: number }) => {
|
|
606
819
|
const { client, projectId, state } = await resolveContext(globals(), context);
|
|
607
820
|
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
608
|
-
const result = await listLivePipelines({
|
|
821
|
+
const result = await listLivePipelines({
|
|
822
|
+
client,
|
|
823
|
+
projectId,
|
|
824
|
+
state,
|
|
825
|
+
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
826
|
+
});
|
|
609
827
|
context.io.stdout(result.output);
|
|
610
828
|
});
|
|
611
829
|
pipeline
|
|
@@ -907,27 +1125,35 @@ export async function runTolokaCli(
|
|
|
907
1125
|
environment: TolokaEnvironment = process.env,
|
|
908
1126
|
io: CliIo = terminalIo(),
|
|
909
1127
|
injectedClient?: PotatoClient,
|
|
910
|
-
analytics
|
|
1128
|
+
analytics?: CliAnalytics,
|
|
1129
|
+
runChecks?: typeof checkProject,
|
|
911
1130
|
): Promise<number> {
|
|
1131
|
+
const activeAnalytics =
|
|
1132
|
+
environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
1133
|
+
? (analytics ?? createCliAnalytics({ environment }))
|
|
1134
|
+
: undefined;
|
|
912
1135
|
const { program, exitCode } = createProgram({
|
|
913
1136
|
environment,
|
|
914
1137
|
io,
|
|
915
|
-
analytics,
|
|
1138
|
+
...(activeAnalytics === undefined ? {} : { analytics: activeAnalytics }),
|
|
916
1139
|
...(injectedClient === undefined ? {} : { injectedClient }),
|
|
1140
|
+
...(runChecks === undefined ? {} : { runChecks }),
|
|
917
1141
|
});
|
|
918
1142
|
|
|
919
1143
|
const startedAt = Date.now();
|
|
920
1144
|
const result = await runProgram(program, exitCode, argv, io);
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
1145
|
+
if (activeAnalytics !== undefined) {
|
|
1146
|
+
try {
|
|
1147
|
+
activeAnalytics.captureCommand({
|
|
1148
|
+
command: commandNameFromArgv(argv),
|
|
1149
|
+
durationMs: Date.now() - startedAt,
|
|
1150
|
+
exitCode: result.code,
|
|
1151
|
+
outcome: result.outcome,
|
|
1152
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
1153
|
+
});
|
|
1154
|
+
await activeAnalytics.shutdown();
|
|
1155
|
+
} catch {}
|
|
1156
|
+
}
|
|
931
1157
|
|
|
932
1158
|
return result.code;
|
|
933
1159
|
}
|
|
@@ -11,7 +11,10 @@ type ProjectRecord = { readonly id: string; readonly name: string; readonly team
|
|
|
11
11
|
|
|
12
12
|
export type ProjectListApi = {
|
|
13
13
|
readonly projects: {
|
|
14
|
-
readonly
|
|
14
|
+
readonly listPage: (params: { readonly limit: number }) => Promise<{
|
|
15
|
+
readonly projects: readonly ProjectRecord[];
|
|
16
|
+
readonly pagination: { readonly total: number; readonly hasMore: boolean };
|
|
17
|
+
}>;
|
|
15
18
|
};
|
|
16
19
|
};
|
|
17
20
|
|
|
@@ -225,15 +228,22 @@ export async function listRemoteProjects(options: {
|
|
|
225
228
|
readonly client: ProjectListApi;
|
|
226
229
|
readonly context?: ProjectContext;
|
|
227
230
|
readonly io: ProjectIo;
|
|
231
|
+
readonly limit?: number;
|
|
228
232
|
}): Promise<{ readonly output: string }> {
|
|
229
|
-
const
|
|
230
|
-
|
|
233
|
+
const limit = options.limit ?? 20;
|
|
234
|
+
const page = await options.client.projects.listPage({ limit });
|
|
235
|
+
const lines = page.projects.map((project) => {
|
|
231
236
|
const mark = options.context?.id === project.id ? "*" : " ";
|
|
232
237
|
const alias =
|
|
233
238
|
options.context?.id === project.id && options.context.alias !== undefined
|
|
234
239
|
? `\t${options.context.alias}`
|
|
235
240
|
: "";
|
|
236
|
-
|
|
241
|
+
return `${mark}\t${project.id}\t${project.name}${alias}`;
|
|
242
|
+
});
|
|
243
|
+
if (page.pagination.hasMore) {
|
|
244
|
+
lines.push(
|
|
245
|
+
`Showing ${page.projects.length} of ${page.pagination.total} projects. Raise --limit to see more.`,
|
|
246
|
+
);
|
|
237
247
|
}
|
|
238
248
|
const output = lines.length === 0 ? "No projects visible." : lines.join("\n");
|
|
239
249
|
options.io.stdout(output);
|
package/src/cli/runs.ts
CHANGED
|
@@ -84,21 +84,31 @@ export async function listLivePipelines(options: {
|
|
|
84
84
|
readonly client: PotatoClient;
|
|
85
85
|
readonly projectId: string;
|
|
86
86
|
readonly state?: TolokaState;
|
|
87
|
+
readonly limit?: number;
|
|
87
88
|
}): Promise<RunCommandResult> {
|
|
88
|
-
const
|
|
89
|
+
const limit = options.limit ?? 20;
|
|
90
|
+
const page = await options.client.pipelines.listPage({ projectId: options.projectId, limit });
|
|
91
|
+
const sourceDirectoryById = bindingByLiveId(options.projectId, options.state);
|
|
92
|
+
const live = page.pipelines
|
|
93
|
+
.map((pipeline) => toRef(pipeline, sourceDirectoryById))
|
|
94
|
+
.toSorted((left, right) => {
|
|
95
|
+
const byName = left.name.localeCompare(right.name);
|
|
96
|
+
return byName === 0 ? left.pipelineId.localeCompare(right.pipelineId) : byName;
|
|
97
|
+
});
|
|
89
98
|
if (live.length === 0) {
|
|
90
99
|
return { outcome: "reported", output: "This project has no pipelines." };
|
|
91
100
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
.
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
101
|
+
const lines = live.map((ref) =>
|
|
102
|
+
ref.sourceDirectory === undefined
|
|
103
|
+
? `${ref.name}\t${ref.pipelineId}`
|
|
104
|
+
: `${ref.name}\t${ref.pipelineId}\t${ref.sourceDirectory}`,
|
|
105
|
+
);
|
|
106
|
+
if (page.pagination.hasMore) {
|
|
107
|
+
lines.push(
|
|
108
|
+
`Showing ${live.length} of ${page.pagination.total} pipelines. Raise --limit to see more.`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
return { outcome: "reported", output: lines.join("\n") };
|
|
102
112
|
}
|
|
103
113
|
|
|
104
114
|
const collectLivePipelineRefs = async (
|
package/src/cli/skill.ts
CHANGED
|
@@ -191,12 +191,23 @@ export const searchSkills = (
|
|
|
191
191
|
skills: readonly SkillDescriptor[],
|
|
192
192
|
query: string,
|
|
193
193
|
): SkillDescriptor[] => {
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
)
|
|
199
|
-
|
|
194
|
+
const needles = query.trim().toLowerCase().split(/\s+/u).filter(Boolean);
|
|
195
|
+
if (needles.length === 0) return [];
|
|
196
|
+
|
|
197
|
+
return skills
|
|
198
|
+
.map((skill, index) => {
|
|
199
|
+
const searchable = [skill.id, skill.name, skill.description, ...skill.tags]
|
|
200
|
+
.join("\n")
|
|
201
|
+
.toLowerCase();
|
|
202
|
+
return {
|
|
203
|
+
skill,
|
|
204
|
+
index,
|
|
205
|
+
score: needles.filter((needle) => searchable.includes(needle)).length,
|
|
206
|
+
};
|
|
207
|
+
})
|
|
208
|
+
.filter(({ score }) => score > 0)
|
|
209
|
+
.toSorted((left, right) => right.score - left.score || left.index - right.index)
|
|
210
|
+
.map(({ skill }) => skill);
|
|
200
211
|
};
|
|
201
212
|
|
|
202
213
|
export async function skillStatus(
|