@toloka-platform/sdk 0.1.0-preview.9 → 0.1.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 +5 -4
- package/dist/cli/toloka.js +138 -136
- 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 +9 -4
- package/docs/cli-v2.md +2 -2
- package/docs/cli.md +13 -2
- package/docs/getting-started.md +3 -3
- package/docs/limitations.md +2 -2
- package/package.json +3 -3
- package/skills/catalog.json +4 -4
- package/skills/toloka-build/SKILL.md +1 -1
- package/skills/toloka-build/references/node-semantics.md +2 -2
- package/skills/toloka-build/references/workflow-and-cli.md +2 -1
- package/skills/toloka-report-issue/references/report-template.md +1 -1
- package/src/authoring/index.ts +10 -0
- package/src/cli/auto-update.ts +1 -5
- package/src/cli/check.ts +91 -30
- package/src/cli/cost.ts +7 -1
- package/src/cli/program.ts +28 -2
- package/src/cli/project-context.ts +47 -2
- package/src/cli/project.ts +53 -10
- package/src/cli/version.ts +68 -0
- package/src/index.ts +2 -0
package/dist/cli/toloka.js.jsc
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
b3ff8750d4254e81d4dccbba6db162057e702b7b823d76d6adc2e6b122212251 toloka.js.jsc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
122b2c1f1afe960176a8f8f5557f847da14ed454809f44d8ed4a5212be6ace2a toloka.js
|
package/docs/authoring.md
CHANGED
|
@@ -37,19 +37,24 @@ The name is a bare identifier. When a field is a pipeline **input**, its name mu
|
|
|
37
37
|
|
|
38
38
|
## Node constructors
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Four work-node constructors take a name, settings, and optional `{ id }`: `code`, `generation`, `quorum`, and `match`. `root` is the single pipeline boundary and takes only settings plus optional `{ id }`.
|
|
41
41
|
|
|
42
42
|
### root
|
|
43
43
|
|
|
44
44
|
Defines the pipeline's input and output contract. `started` is what each item carries in from the dataset; `completed` is what gets written back.
|
|
45
45
|
|
|
46
46
|
```ts
|
|
47
|
-
import { root } from "@toloka-platform/sdk";
|
|
47
|
+
import { optional, root } from "@toloka-platform/sdk";
|
|
48
48
|
|
|
49
|
-
export const collection = root({
|
|
49
|
+
export const collection = root({
|
|
50
|
+
started: [imageUrl],
|
|
51
|
+
completed: [optional(annotations)],
|
|
52
|
+
});
|
|
50
53
|
```
|
|
51
54
|
|
|
52
|
-
Pass `{ field, optional
|
|
55
|
+
Pass a bare field for a required binding. Use `optional(field)` for new optional bindings. The explicit `{ field, optional }` form remains supported and may appear in imported source.
|
|
56
|
+
|
|
57
|
+
`root()` deliberately has no name argument: a pipeline has exactly one boundary, and its default wire id is `root`.
|
|
53
58
|
|
|
54
59
|
### code
|
|
55
60
|
|
package/docs/cli-v2.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# CLI v2: project as the root
|
|
2
2
|
|
|
3
|
-
How `toloka` should work in the next version.
|
|
3
|
+
How `toloka` should work in the next version. Beta remains a clean cutover rather than a compatibility layer.
|
|
4
4
|
|
|
5
5
|
The platform object is a **project** (`pp.…`, today's pipeline project). Windmill calls the same role a workspace. PostHog calls it a project. We keep **project** so the CLI matches the API and the console.
|
|
6
6
|
|
|
@@ -212,7 +212,7 @@ Do not add those groups until the resource exists. The rule is already fixed: th
|
|
|
212
212
|
| `toloka runs` / `cost` / `cancel` / `results` | `toloka pipeline …` |
|
|
213
213
|
| root = `toloka.json` or `pipelines/` | root = `toloka.json` only |
|
|
214
214
|
|
|
215
|
-
No shims.
|
|
215
|
+
No shims. Beta callers update.
|
|
216
216
|
|
|
217
217
|
## Out of scope
|
|
218
218
|
|
package/docs/cli.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
The `toloka` CLI reads TypeScript source as the desired state, compares it with the live platform state, and reconciles the two.
|
|
4
4
|
|
|
5
|
-
This CLI is
|
|
5
|
+
This CLI is Beta software. Commands, flags, and output can change before general availability.
|
|
6
6
|
|
|
7
7
|
`toloka` requires Bun 1.4.0. The published package ships matching CLI bytecode; the launcher falls back to TypeScript source in a development checkout when the bytecode is absent.
|
|
8
8
|
|
|
@@ -61,6 +61,14 @@ The CLI is resource-first: `toloka <resource> <action>`. A Base Action has one m
|
|
|
61
61
|
|
|
62
62
|
Resources may also expose specialized actions beyond the Base Actions, such as `pipeline cost`, `skill search`, and `skill install`.
|
|
63
63
|
|
|
64
|
+
## version
|
|
65
|
+
|
|
66
|
+
Print the SDK/CLI version. `toloka version --json` also prints build provenance: the build commit when the bytecode bundle recorded one, the install channel (`global-registry`, `linked`, or `project-local`), and the local package path for linked installs. `toloka -v` and `toloka --version` remain semver-only for scripts.
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
toloka version [--json]
|
|
70
|
+
```
|
|
71
|
+
|
|
64
72
|
## project
|
|
65
73
|
|
|
66
74
|
Register and select the platform project later commands use.
|
|
@@ -73,9 +81,12 @@ toloka project current
|
|
|
73
81
|
toloka project url
|
|
74
82
|
toloka project remove <alias>
|
|
75
83
|
toloka project create [alias] [--name <name>] [--team-id <id>]
|
|
84
|
+
toloka project untrack <pipeline|dataset> <name> [--confirm]
|
|
76
85
|
```
|
|
77
86
|
|
|
78
|
-
`add`, `switch`, `remove`, and `create` write the machine registry in the credentials file. They refuse `--api-key` / `--base-url` / `POTATO_*` when those would select a different key or platform than the stored file. `create` reuses the only visible team when every listed project shares one; otherwise pass `--team-id`.
|
|
87
|
+
`add`, `switch`, `remove`, and `create` write the machine registry in the credentials file. They refuse `--api-key` / `--base-url` / `POTATO_*` when those would select a different key or platform than the stored file. `project list` prints project id, name, and team id. `create` reuses the only visible team when every listed project shares one; otherwise pass `--team-id`.
|
|
88
|
+
|
|
89
|
+
`project untrack` removes one pipeline or dataset binding from `toloka.json` without deleting the live resource. It previews the name and id in a terminal; outside a terminal, pass `--confirm` after reviewing the state file.
|
|
79
90
|
|
|
80
91
|
`url` fetches the selected project to resolve its team and prints only its platform UI URL to stdout, making the output safe to capture in scripts and agent workflows.
|
|
81
92
|
|
package/docs/getting-started.md
CHANGED
|
@@ -4,11 +4,11 @@ Install the SDK, sign in, and get a pipeline project reconciling from source in
|
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
7
|
-
Install the bytecode-cached CLI globally and declare the
|
|
7
|
+
Install the bytecode-cached CLI globally and declare the Beta SDK in the project:
|
|
8
8
|
|
|
9
9
|
```sh
|
|
10
10
|
curl -fsSL https://platform.toloka.ai/cli/install.sh | sh
|
|
11
|
-
bun add @toloka-platform/sdk@
|
|
11
|
+
bun add @toloka-platform/sdk@latest
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The installer requires Bun 1.4.0, installs `@toloka-platform/sdk` globally, and exposes `toloka` through Bun's global binary directory. The published CLI includes matching precompiled bytecode. The project install makes SDK imports resolvable.
|
|
@@ -140,4 +140,4 @@ Exit code `0` means the run reached a successful terminal state, `1` a failed on
|
|
|
140
140
|
- [Nodes](./nodes.md) — which node to reach for, and the pricing floors you must clear
|
|
141
141
|
- [CLI](./cli.md) — every command and flag
|
|
142
142
|
- [Troubleshooting](./troubleshooting.md) — the failures you will hit
|
|
143
|
-
- [Limitations](./limitations.md) —
|
|
143
|
+
- [Limitations](./limitations.md) — Beta status and what this SDK does not do yet
|
package/docs/limitations.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Limitations
|
|
2
2
|
|
|
3
|
-
What this
|
|
3
|
+
What this Beta SDK does not do yet, stated plainly so you can decide what to depend on.
|
|
4
4
|
|
|
5
|
-
`@toloka-platform/sdk` is `0.1.0
|
|
5
|
+
`@toloka-platform/sdk` is `0.1.0`. This is a Beta: the authoring API, the CLI surface, and the generated source layout can change before general availability. Pin an exact version. Do not depend on this in production yet.
|
|
6
6
|
|
|
7
7
|
## Local checks do not certify deployability
|
|
8
8
|
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toloka-platform/sdk",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Beta. Build, inspect, and operate Toloka pipeline projects as TypeScript source. The public API may change.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"bin": {
|
|
8
8
|
"toloka": "bin/toloka.js"
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public",
|
|
30
30
|
"registry": "https://registry.npmjs.org/",
|
|
31
|
-
"tag": "
|
|
31
|
+
"tag": "latest"
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "bun ./scripts/build-cli.ts ./dist/cli",
|
package/skills/catalog.json
CHANGED
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
"testing",
|
|
14
14
|
"checks"
|
|
15
15
|
],
|
|
16
|
-
"version": "0.1.0
|
|
17
|
-
"digest": "
|
|
16
|
+
"version": "0.1.0",
|
|
17
|
+
"digest": "bcdb60e89bc4b8e345c1793c7a7d5275e8b196e171bd09adba242ed89f117a50",
|
|
18
18
|
"files": ["SKILL.md", "references/node-semantics.md", "references/workflow-and-cli.md"]
|
|
19
19
|
},
|
|
20
20
|
{
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
"name": "Report a Toloka problem",
|
|
23
23
|
"description": "Report a problem with the Toloka platform, CLI, or SDK as a redacted Markdown issue report sent through the toloka CLI only after explicit approval. Use when the user hits unexpected behavior, an error, or a bug and wants Toloka engineers to investigate.",
|
|
24
24
|
"tags": ["support", "issue", "bug", "report"],
|
|
25
|
-
"version": "0.1.0
|
|
26
|
-
"digest": "
|
|
25
|
+
"version": "0.1.0",
|
|
26
|
+
"digest": "969aedef68e56243395e0a9115d90041c7c1e27f759762b07e62a7d521a8a251",
|
|
27
27
|
"files": ["SKILL.md", "references/report-template.md"]
|
|
28
28
|
}
|
|
29
29
|
]
|
|
@@ -5,7 +5,7 @@ description: Onboard users and build, test, inspect, or change Toloka platform p
|
|
|
5
5
|
|
|
6
6
|
# Build Toloka pipelines as code
|
|
7
7
|
|
|
8
|
-
This SDK is
|
|
8
|
+
This SDK is Beta software. The authoring API, the CLI, and generated source can change before general availability. Do not invent helpers or flags that are not in the installed package.
|
|
9
9
|
|
|
10
10
|
Use this skill when the user wants to get started or to create, inspect, test, or change a Toloka pipeline project as code. Work in the project source tree; do not translate the request into direct platform API calls.
|
|
11
11
|
|
|
@@ -43,13 +43,13 @@ connect(annotate.output, collection.completed);
|
|
|
43
43
|
|
|
44
44
|
### Pick and configure it
|
|
45
45
|
|
|
46
|
-
Create exactly one `root` node. Use `started` for dataset fields the pipeline reads and `completed` for fields it writes back to the same dataset. A completed field is not a separate output destination: the platform creates or uses that dataset column per item.
|
|
46
|
+
Create exactly one `root` node with `root(settings)`. It has no name argument because it represents the pipeline boundary, not a named unit of work. Use `started` for dataset fields the pipeline reads and `completed` for fields it writes back to the same dataset. A completed field is not a separate output destination: the platform creates or uses that dataset column per item.
|
|
47
47
|
|
|
48
48
|
The public authoring shape is `RootAuthoringSettings`:
|
|
49
49
|
|
|
50
50
|
- `started`: fields exposed through the `started` output port.
|
|
51
51
|
- `completed`: fields accepted through the `completed` input port and written to the dataset.
|
|
52
|
-
- Each entry can be a `Field` or `{ field, optional }
|
|
52
|
+
- Each entry can be a required `Field`, `optional(field)`, or an explicit `{ field, optional }` binding. Prefer `optional(field)` for new optional bindings and preserve the explicit object form in imported source.
|
|
53
53
|
- `itemLimit` exists in both `RootNodeSettings` and `RootAuthoringSettings`. Do not invent a limit; author one only when the user explicitly requests a bounded test or batch. Apply preserves a live `itemLimit` unless source declares its own.
|
|
54
54
|
|
|
55
55
|
The dataset name belongs in `definePipeline({ dataset: 'name', ... })`, not in the root settings. `toloka apply` resolves or creates the named dataset and supplies its ID.
|
|
@@ -83,7 +83,7 @@ All commands accept `--source-root <directory>`. Prefer running from the source
|
|
|
83
83
|
The real command surface is:
|
|
84
84
|
|
|
85
85
|
```text
|
|
86
|
-
toloka version
|
|
86
|
+
toloka version [--json]
|
|
87
87
|
toloka -v
|
|
88
88
|
toloka --version
|
|
89
89
|
toloka login
|
|
@@ -99,6 +99,7 @@ toloka project current
|
|
|
99
99
|
toloka project url
|
|
100
100
|
toloka project remove <alias>
|
|
101
101
|
toloka project create [alias] [--name <name>] [--team-id <id>]
|
|
102
|
+
toloka project untrack <pipeline|dataset> <name> [--confirm]
|
|
102
103
|
toloka init
|
|
103
104
|
toloka import [projectId]
|
|
104
105
|
toloka check [pipeline] [--node <name-or-id>] [--json] [--watch]
|
|
@@ -26,7 +26,7 @@ The CLI exits 1 with `Potato API request failed with status 500`. The same comma
|
|
|
26
26
|
|
|
27
27
|
## Environment
|
|
28
28
|
|
|
29
|
-
- toloka CLI 0.1.0
|
|
29
|
+
- toloka CLI 0.1.0 (`toloka --version`), Bun 1.4.0, macOS 15
|
|
30
30
|
- Platform: https://platform.toloka.ai
|
|
31
31
|
|
|
32
32
|
## Redactions
|
package/src/authoring/index.ts
CHANGED
|
@@ -56,6 +56,8 @@ type FieldReference = {
|
|
|
56
56
|
readonly optional: boolean;
|
|
57
57
|
};
|
|
58
58
|
|
|
59
|
+
export type OptionalFieldBinding = { readonly field: Field; readonly optional: true };
|
|
60
|
+
|
|
59
61
|
type RootFieldReference = Field | { readonly field: Field; readonly optional?: boolean };
|
|
60
62
|
|
|
61
63
|
type FieldRelation<Relation> = Relation extends { id: string }
|
|
@@ -445,10 +447,18 @@ export function field<const Name extends string>(
|
|
|
445
447
|
};
|
|
446
448
|
}
|
|
447
449
|
|
|
450
|
+
export function optional(referencedField: Field): OptionalFieldBinding {
|
|
451
|
+
return { field: referencedField, optional: true };
|
|
452
|
+
}
|
|
453
|
+
|
|
448
454
|
function createRoot(
|
|
449
455
|
settings: RootAuthoringSettings,
|
|
450
456
|
options: AuthoringIdOptions = {},
|
|
451
457
|
): AuthoredRootNode {
|
|
458
|
+
assert(
|
|
459
|
+
typeof settings === "object" && settings !== null && !Array.isArray(settings),
|
|
460
|
+
"root() defines the single pipeline boundary; call root({ started, completed }) without a name",
|
|
461
|
+
);
|
|
452
462
|
const id = options.id ?? "root";
|
|
453
463
|
const { started, completed, ...settingsWithoutFieldProxies } = settings;
|
|
454
464
|
return {
|
package/src/cli/auto-update.ts
CHANGED
|
@@ -123,11 +123,7 @@ export const asNpmPackageView = (payload: unknown): NpmPackageView | undefined =
|
|
|
123
123
|
};
|
|
124
124
|
|
|
125
125
|
export const latestPublishedVersion = (view: NpmPackageView): string | undefined => {
|
|
126
|
-
const
|
|
127
|
-
if (tags === undefined) return undefined;
|
|
128
|
-
const preview = tags.preview;
|
|
129
|
-
if (typeof preview === "string" && preview.length > 0) return preview;
|
|
130
|
-
const latest = tags.latest;
|
|
126
|
+
const latest = view["dist-tags"]?.latest;
|
|
131
127
|
return typeof latest === "string" && latest.length > 0 ? latest : undefined;
|
|
132
128
|
};
|
|
133
129
|
|
package/src/cli/check.ts
CHANGED
|
@@ -114,6 +114,18 @@ export type CheckProjectResult = {
|
|
|
114
114
|
readonly source?: ProjectSource;
|
|
115
115
|
readonly output: string;
|
|
116
116
|
};
|
|
117
|
+
|
|
118
|
+
export type FreshCheckFailurePhase = "spawn" | "execute" | "read_result" | "parse_result";
|
|
119
|
+
|
|
120
|
+
export class FreshCheckProcessError extends Error {
|
|
121
|
+
readonly phase: FreshCheckFailurePhase;
|
|
122
|
+
|
|
123
|
+
constructor(phase: FreshCheckFailurePhase, message: string, options?: ErrorOptions) {
|
|
124
|
+
super(message, options);
|
|
125
|
+
this.name = "FreshCheckProcessError";
|
|
126
|
+
this.phase = phase;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
117
129
|
const MAX_DIAGNOSTIC_CHARS = 5_000;
|
|
118
130
|
const MAX_TOOL_OUTPUT_BYTES = 64 * 1024;
|
|
119
131
|
export const CHECK_RESULT_FILE_ENV = "TOLOKA_INTERNAL_CHECK_RESULT_FILE";
|
|
@@ -865,44 +877,93 @@ const parseFreshCheckResult = (value: unknown, json: boolean): CheckProjectResul
|
|
|
865
877
|
output: json ? JSON.stringify({ exitCode, results }) : formatCheckResults(results),
|
|
866
878
|
};
|
|
867
879
|
};
|
|
880
|
+
|
|
881
|
+
const checkProcessOutput = (run: ToolRunResult): string =>
|
|
882
|
+
[run.stderr, run.stdout].filter(Boolean).join("\n");
|
|
883
|
+
|
|
884
|
+
export const readFreshCheckResult = async (
|
|
885
|
+
run: ToolRunResult,
|
|
886
|
+
readResult: () => Promise<string>,
|
|
887
|
+
json = false,
|
|
888
|
+
): Promise<CheckProjectResult> => {
|
|
889
|
+
let raw: string;
|
|
890
|
+
try {
|
|
891
|
+
raw = await readResult();
|
|
892
|
+
} catch (error) {
|
|
893
|
+
if (run.exitCode !== 0) {
|
|
894
|
+
const output = checkProcessOutput(run);
|
|
895
|
+
throw new FreshCheckProcessError(
|
|
896
|
+
"execute",
|
|
897
|
+
output.length === 0
|
|
898
|
+
? `Local check process exited ${run.exitCode} before producing results`
|
|
899
|
+
: `Local check process failed before producing results:\n${output}`,
|
|
900
|
+
{ cause: error },
|
|
901
|
+
);
|
|
902
|
+
}
|
|
903
|
+
throw new FreshCheckProcessError(
|
|
904
|
+
"read_result",
|
|
905
|
+
"Local check process completed without producing a result file",
|
|
906
|
+
{ cause: error },
|
|
907
|
+
);
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
let parsed: unknown;
|
|
911
|
+
try {
|
|
912
|
+
parsed = JSON.parse(raw);
|
|
913
|
+
} catch (error) {
|
|
914
|
+
throw new FreshCheckProcessError("parse_result", "Local check process returned invalid JSON", {
|
|
915
|
+
cause: error,
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
try {
|
|
920
|
+
return parseFreshCheckResult(parsed, json);
|
|
921
|
+
} catch (error) {
|
|
922
|
+
throw new FreshCheckProcessError(
|
|
923
|
+
"parse_result",
|
|
924
|
+
`Local check process returned an invalid result: ${error instanceof Error ? error.message : String(error)}`,
|
|
925
|
+
{ cause: error },
|
|
926
|
+
);
|
|
927
|
+
}
|
|
928
|
+
};
|
|
868
929
|
export const runFreshCheck = async (options: CheckProjectOptions): Promise<CheckProjectResult> => {
|
|
869
930
|
const entry = resolveCliEntry();
|
|
870
931
|
const resultDirectory = await mkdtemp(join(tmpdir(), "toloka-check-watch-result-"));
|
|
871
932
|
const resultPath = join(resultDirectory, "result.json");
|
|
872
|
-
let run: ToolRunResult | undefined;
|
|
873
933
|
try {
|
|
874
|
-
run
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
[
|
|
878
|
-
"--source-root",
|
|
934
|
+
let run: ToolRunResult;
|
|
935
|
+
try {
|
|
936
|
+
run = await runModule(
|
|
879
937
|
options.sourceRoot,
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
938
|
+
entry,
|
|
939
|
+
[
|
|
940
|
+
"--source-root",
|
|
941
|
+
options.sourceRoot,
|
|
942
|
+
"check",
|
|
943
|
+
...(options.pipeline === undefined ? [] : [options.pipeline]),
|
|
944
|
+
...(options.node === undefined ? [] : ["--node", options.node]),
|
|
945
|
+
"--json",
|
|
946
|
+
],
|
|
947
|
+
{
|
|
948
|
+
...(process.env.PATH === undefined ? {} : { PATH: process.env.PATH }),
|
|
949
|
+
...(options.environment ?? process.env),
|
|
950
|
+
[CHECK_RESULT_FILE_ENV]: resultPath,
|
|
951
|
+
},
|
|
952
|
+
options.signal,
|
|
953
|
+
"bun",
|
|
954
|
+
);
|
|
955
|
+
} catch (error) {
|
|
956
|
+
throw new FreshCheckProcessError(
|
|
957
|
+
"spawn",
|
|
958
|
+
`Unable to start the local check process: ${error instanceof Error ? error.message : String(error)}`,
|
|
959
|
+
{ cause: error },
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
return await readFreshCheckResult(
|
|
963
|
+
run,
|
|
964
|
+
async () => readFile(resultPath, "utf8"),
|
|
895
965
|
options.json === true,
|
|
896
966
|
);
|
|
897
|
-
} catch (error) {
|
|
898
|
-
const detail = [
|
|
899
|
-
run?.stderr,
|
|
900
|
-
run?.stdout,
|
|
901
|
-
error instanceof Error ? error.message : String(error),
|
|
902
|
-
]
|
|
903
|
-
.filter(Boolean)
|
|
904
|
-
.join("\n");
|
|
905
|
-
throw new Error(`Fresh check process returned unreadable output: ${detail}`, { cause: error });
|
|
906
967
|
} finally {
|
|
907
968
|
await rm(resultDirectory, { recursive: true, force: true });
|
|
908
969
|
}
|
package/src/cli/cost.ts
CHANGED
|
@@ -80,7 +80,13 @@ const collectNodeRows = (
|
|
|
80
80
|
};
|
|
81
81
|
|
|
82
82
|
const collectTotalRows = (total: PipelineCostEstimate["total"]): TableRow[] => {
|
|
83
|
-
const totals: TableRow[] =
|
|
83
|
+
const totals: TableRow[] =
|
|
84
|
+
total.expected === total.worstCase
|
|
85
|
+
? [["Run cost", total.expected, ""]]
|
|
86
|
+
: [
|
|
87
|
+
["Expected run cost", total.expected, ""],
|
|
88
|
+
["Maximum run cost", "", total.worstCase],
|
|
89
|
+
];
|
|
84
90
|
if (total.bestCase != null) {
|
|
85
91
|
totals.push(["Best case", total.bestCase, ""]);
|
|
86
92
|
}
|
package/src/cli/program.ts
CHANGED
|
@@ -64,6 +64,7 @@ import {
|
|
|
64
64
|
removeProject,
|
|
65
65
|
resolveProjectContext,
|
|
66
66
|
switchProject,
|
|
67
|
+
untrackProjectResource,
|
|
67
68
|
} from "./project-context";
|
|
68
69
|
import { estimatePipelineCost } from "./cost";
|
|
69
70
|
import { submitIssueReport } from "./report";
|
|
@@ -81,6 +82,7 @@ import {
|
|
|
81
82
|
|
|
82
83
|
import type { RunOutcome } from "./runs";
|
|
83
84
|
import { skillInstallCommand, skillListCommand, skillSearchCommand } from "./skill";
|
|
85
|
+
import { cliVersionInfo } from "./version";
|
|
84
86
|
import { findSourceRoot } from "./source";
|
|
85
87
|
|
|
86
88
|
export type CliIo = {
|
|
@@ -242,7 +244,7 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
242
244
|
|
|
243
245
|
program
|
|
244
246
|
.description(
|
|
245
|
-
"
|
|
247
|
+
"Beta. Build and operate Toloka pipeline projects from TypeScript source. The public API may change before general availability.",
|
|
246
248
|
)
|
|
247
249
|
.version(SDK_VERSION, "-v, --version", "output the current version")
|
|
248
250
|
.option(
|
|
@@ -282,10 +284,14 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
282
284
|
warnedAboutStaleSkills = true;
|
|
283
285
|
context.io.stderr(warning);
|
|
284
286
|
});
|
|
287
|
+
|
|
285
288
|
program
|
|
286
289
|
.command("version")
|
|
290
|
+
.option("--json", "print version and build provenance as JSON", false)
|
|
287
291
|
.description("print the current CLI version")
|
|
288
|
-
.action(() =>
|
|
292
|
+
.action((options: { json: boolean }) =>
|
|
293
|
+
context.io.stdout(options.json ? JSON.stringify(cliVersionInfo()) : SDK_VERSION),
|
|
294
|
+
);
|
|
289
295
|
|
|
290
296
|
const config = program.command("config").description("read and write machine CLI settings");
|
|
291
297
|
config
|
|
@@ -472,6 +478,26 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
472
478
|
io: context.io,
|
|
473
479
|
});
|
|
474
480
|
});
|
|
481
|
+
project
|
|
482
|
+
.command("untrack")
|
|
483
|
+
.argument("<type>", "pipeline or dataset")
|
|
484
|
+
.argument("<name>", "tracked resource name")
|
|
485
|
+
.option("--confirm", "confirm untracking in a non-interactive environment", false)
|
|
486
|
+
.description("remove a pipeline or dataset from local tracking without deleting it")
|
|
487
|
+
.action(async (type: string, name: string, options: { confirm: boolean }) => {
|
|
488
|
+
assert(
|
|
489
|
+
type === "pipeline" || type === "dataset",
|
|
490
|
+
"Resource type must be pipeline or dataset.",
|
|
491
|
+
);
|
|
492
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
493
|
+
await untrackProjectResource({
|
|
494
|
+
sourceRoot,
|
|
495
|
+
type,
|
|
496
|
+
name,
|
|
497
|
+
confirm: options.confirm,
|
|
498
|
+
io: context.io,
|
|
499
|
+
});
|
|
500
|
+
});
|
|
475
501
|
project
|
|
476
502
|
.command("create")
|
|
477
503
|
.argument("[alias]", "local name for the new project")
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { assert } from "es-toolkit";
|
|
2
|
-
import { readTolokaState, writeTolokaState } from "../state";
|
|
2
|
+
import { readTolokaState, TOLOKA_STATE_FILENAME, writeTolokaState } from "../state";
|
|
3
3
|
import type { TolokaState } from "../state";
|
|
4
4
|
import type { AuthPrompt } from "./auth";
|
|
5
5
|
import type { TolokaCliFlags, TolokaEnvironment } from "./config";
|
|
@@ -238,7 +238,7 @@ export async function listRemoteProjects(options: {
|
|
|
238
238
|
options.context?.id === project.id && options.context.alias !== undefined
|
|
239
239
|
? `\t${options.context.alias}`
|
|
240
240
|
: "";
|
|
241
|
-
return `${mark}\t${project.id}\t${project.name}${alias}`;
|
|
241
|
+
return `${mark}\t${project.id}\t${project.name}\t${project.teamId}${alias}`;
|
|
242
242
|
});
|
|
243
243
|
if (page.pagination.hasMore) {
|
|
244
244
|
lines.push(
|
|
@@ -250,6 +250,51 @@ export async function listRemoteProjects(options: {
|
|
|
250
250
|
return { output };
|
|
251
251
|
}
|
|
252
252
|
|
|
253
|
+
export type TrackedResourceKind = "pipeline" | "dataset";
|
|
254
|
+
|
|
255
|
+
export async function untrackProjectResource(options: {
|
|
256
|
+
readonly sourceRoot: string;
|
|
257
|
+
readonly type: TrackedResourceKind;
|
|
258
|
+
readonly name: string;
|
|
259
|
+
readonly confirm: boolean;
|
|
260
|
+
readonly io: ProjectIo;
|
|
261
|
+
}): Promise<{ readonly output: string }> {
|
|
262
|
+
const state = await readTolokaState(options.sourceRoot);
|
|
263
|
+
assert(state, `No ${TOLOKA_STATE_FILENAME} found in ${options.sourceRoot}.`);
|
|
264
|
+
const bindings = options.type === "pipeline" ? state.pipelines : state.datasets;
|
|
265
|
+
const binding = bindings[options.name];
|
|
266
|
+
assert(
|
|
267
|
+
binding,
|
|
268
|
+
`${options.type === "pipeline" ? "Pipeline" : "Dataset"} ${options.name} is not tracked.`,
|
|
269
|
+
);
|
|
270
|
+
const id = "pipelineId" in binding ? binding.pipelineId : binding.datasetId;
|
|
271
|
+
const label = `${options.type} ${options.name} (${id})`;
|
|
272
|
+
|
|
273
|
+
if (!options.confirm) {
|
|
274
|
+
assert(
|
|
275
|
+
options.io.prompt,
|
|
276
|
+
`Refusing to untrack ${label} without confirmation. Pass --confirm after reviewing ${TOLOKA_STATE_FILENAME}.`,
|
|
277
|
+
);
|
|
278
|
+
const answer = (
|
|
279
|
+
await options.io.prompt(`Untrack ${label}? The live resource will not be deleted. [y/N] `)
|
|
280
|
+
)
|
|
281
|
+
.trim()
|
|
282
|
+
.toLowerCase();
|
|
283
|
+
assert(answer === "y" || answer === "yes", "Untrack canceled.");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const remaining = Object.fromEntries(
|
|
287
|
+
Object.entries(bindings).filter(([candidate]) => candidate !== options.name),
|
|
288
|
+
);
|
|
289
|
+
await writeTolokaState(options.sourceRoot, {
|
|
290
|
+
...state,
|
|
291
|
+
...(options.type === "pipeline" ? { pipelines: remaining } : { datasets: remaining }),
|
|
292
|
+
});
|
|
293
|
+
const output = `Untracked ${label}. The live ${options.type} was not deleted.`;
|
|
294
|
+
options.io.stdout(output);
|
|
295
|
+
return { output };
|
|
296
|
+
}
|
|
297
|
+
|
|
253
298
|
export async function addProject(options: {
|
|
254
299
|
readonly environment: CliEnvironment;
|
|
255
300
|
readonly flags?: TolokaCliFlags;
|