@toloka-platform/sdk 0.11.1 → 0.11.2
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 +4 -2
- package/dist/cli/toloka.js +152 -148
- 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 -1
- package/docs/cli.md +19 -6
- package/docs/getting-started.md +1 -1
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/package.json +2 -2
- package/skills/catalog.json +6 -6
- package/skills/toloka-build/SKILL.md +5 -3
- package/skills/toloka-build/references/node-semantics.md +2 -2
- package/skills/toloka-build/references/workflow-and-cli.md +7 -5
- package/src/cli/auth.ts +51 -18
- package/src/cli/check.ts +5 -5
- package/src/cli/commands/auth.ts +5 -3
- package/src/cli/commands/project.ts +7 -2
- package/src/cli/commands/ui.ts +23 -0
- package/src/cli/config.ts +15 -9
- package/src/cli/context.ts +30 -12
- package/src/cli/credentials.ts +2 -2
- package/src/cli/environment.ts +5 -1
- package/src/cli/program.ts +5 -0
- package/src/cli/project-context.ts +5 -2
- package/src/cli/ui.ts +61 -0
- package/src/core-entity-types.json +32 -0
- package/src/testing/task-ui-browser.ts +45 -2
- package/src/testing/task-ui.ts +2 -9
- package/src/transport.ts +28 -6
- package/src/validate.ts +49 -2
package/dist/cli/toloka.js.jsc
CHANGED
|
Binary file
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
1024b0621937fe114806d81e918925d05e4e71ef8c2f6aa209eabbe9aa102e02 toloka.js.jsc
|
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
2e9290a8e04d18db2dd275be6fc273d15459a67f6b1f98d27aebf1c6bdfd7fa1 toloka.js
|
package/docs/authoring.md
CHANGED
|
@@ -99,6 +99,14 @@ Choose entity cardinality from the task, not from the component's JavaScript val
|
|
|
99
99
|
|
|
100
100
|
Use `dynamic: true` only when one labeler produces multiple independent entities. Set `maxEntities` to a task-grounded upper bound rather than a generic default. A component may expose an array while still producing one logical entity; for a single upload or recording, keep a scalar field and static entity and select the completed first value with a path such as `$.video_file[0].downloadLink`.
|
|
101
101
|
|
|
102
|
+
Entity types and labels are checked locally, for both generation and quorum nodes, before anything is sent to the platform:
|
|
103
|
+
|
|
104
|
+
- `entityType` on every input and output entity must be one of the platform's core entity types. The list ships with the SDK in `src/core-entity-types.json`, generated from the server schema: `JUDGEMENT`, `EXPLANATION`, `PROMPT`, `USER_PROMPT`, `RESPONSE`, `RESPONSE_EVALUATION`, `RUBRIC`, `RUBRICS_GROUP`, `DIALOG`, `DIALOG_STEP`, `DIALOG_EVALUATION`, `SHORT_ANSWER`, `FEEDBACK`, `CONTEXT`, `IMAGE`, `ANNOTATION`, `SINGLE_LABEL`, `MULTI_LABEL`, `VIDEO`, `DOCUMENT`, `GOLDEN_SET`, `LOGS`, `SOLUTION`, `CODE`, `METADATA`, `AUDIO`, `AGENT_TRAJECTORY`, `MCP_SERVER`, `CONTAINER`, `CUSTOM`.
|
|
105
|
+
- An output entity of type `SINGLE_LABEL` or `MULTI_LABEL` needs `allowedLabels` with at least two entries.
|
|
106
|
+
- Every label `value` must be snake_case (`first_video`, not `firstVideo` or `First Video`).
|
|
107
|
+
|
|
108
|
+
A violation is thrown as `PipelineGraphValidationError`, with one issue per offending path, as soon as the pipeline source is loaded — so `toloka check`, `toloka plan`, `toloka apply` and `toloka validate` all fail on it locally before any platform request.
|
|
109
|
+
|
|
102
110
|
The following task is legitimately dynamic because the labeler can draw one independent box for every visible face:
|
|
103
111
|
|
|
104
112
|
```ts
|
|
@@ -299,7 +307,7 @@ toloka validate
|
|
|
299
307
|
|
|
300
308
|
`definePipeline` and the typed ports add local declaration and connection invariants. `toloka validate` posts each bound pipeline to `POST /pipelines/:pipelineId/graph/validate` and reports platform spec errors, warnings, and the latest matching check jobs. It does not write the graph.
|
|
301
309
|
|
|
302
|
-
A pipeline that is not on the platform yet is skipped until `toloka apply` creates it. The published JSON contract is still available as `validate(graph)` for importer and fixture tests; that helper makes no network call.
|
|
310
|
+
A pipeline that is not on the platform yet is skipped until `toloka apply` creates it. The published JSON contract is still available as `validate(graph)` for importer and fixture tests; that helper also runs the local entity checks described under [generation](#generation) and makes no network call.
|
|
303
311
|
|
|
304
312
|
A successful validate is necessary, not sufficient for launch. Gates outside the graph contract — an underpriced audience, a Training course that is enabled but unbuilt, a task UI that was never validated — can still block deployment. See [limitations](./limitations.md).
|
|
305
313
|
|
package/docs/cli.md
CHANGED
|
@@ -56,14 +56,15 @@ If both installations exist, put the desired directory first on `PATH`, run `has
|
|
|
56
56
|
|
|
57
57
|
## Configuration
|
|
58
58
|
|
|
59
|
-
Credentials and the platform base URL resolve
|
|
59
|
+
Credentials and the platform base URL resolve independently. Credentials use this precedence, highest first.
|
|
60
60
|
|
|
61
61
|
| Precedence | Credential source |
|
|
62
62
|
| ----------- | ------------------------------------------------ |
|
|
63
63
|
| 1 (highest) | `--api-key` flag |
|
|
64
|
-
| 2 | `
|
|
65
|
-
| 3 |
|
|
66
|
-
| 4
|
|
64
|
+
| 2 | `TOLOKA_SCOPED_TOKEN` environment variable |
|
|
65
|
+
| 3 | `TOLOKA_API_KEY` environment variable |
|
|
66
|
+
| 4 | API key stored by `toloka login --api-key-stdin` |
|
|
67
|
+
| 5 (lowest) | browser session stored by `toloka login` |
|
|
67
68
|
|
|
68
69
|
| Precedence | Base URL source |
|
|
69
70
|
| ----------- | --------------------------------------- |
|
|
@@ -81,6 +82,8 @@ Credentials and the platform base URL resolve from the same precedence order, hi
|
|
|
81
82
|
|
|
82
83
|
Earlier releases named these variables `POTATO_API_KEY`, `POTATO_BASE_URL`, and `POTATO_PROJECT_ID`. The CLI still reads each legacy name when its `TOLOKA_*` counterpart is unset, prefers the `TOLOKA_*` value when both are set, and prints `POTATO_API_KEY is deprecated and will stop being read; set TOLOKA_API_KEY instead.` (and the same line for the other two) on stderr once per command. The aliases remain supported for compatibility; rename them to silence the warning and use the current names in new automation.
|
|
83
84
|
|
|
85
|
+
`TOLOKA_SCOPED_TOKEN` always sends its value in `X-Scoped-Token`, so it supports both issued `pst_…` tokens and secret placeholders such as `dtn_secret_…`. The `--api-key` and `TOLOKA_API_KEY` slots continue to recognize the `pst_` prefix for backward compatibility. Use the explicit scoped-token variable for new agent and CI configuration.
|
|
86
|
+
|
|
84
87
|
`toloka login` verifies access against the platform before saving anything. A browser login is saved as a session file under `$TOLOKA_CLI_CREDENTIALS_DIR`, which defaults to `~/.toloka/cli/credentials/<auth-host>/<realm>/<client>.json`. When `TOLOKA_CREDENTIALS_FILE` points at an explicit credentials file, sessions go to a `browser-sessions` directory next to it instead, so that profile stays isolated. The platform choice, a stored API key, and local project aliases live in `$XDG_CONFIG_HOME/toloka/credentials.json`, which defaults to `~/.config/toloka/credentials.json`. Both are written with file mode `0600` in directories with mode `0700`. The legacy `~/.toloka/credentials.json` is still read when the new path has no file. `toloka logout` ends the browser session and deletes both files; it does not revoke an API key.
|
|
85
88
|
|
|
86
89
|
Ordinary usage events (`sdk_cli_command_completed` / `sdk_cli_command_failed`) go to Toloka's PostHog project so we can see which commands run and which error classes they hit. Every failed command with an exception also emits a sanitized PostHog Error Tracking `$exception`; usage errors such as unknown commands or invalid flags do not. Command events contain only an allowlisted command classifier, duration, exit code, outcome or error code, optional HTTP status and server request ID, SDK and Bun versions, actor, and channel. Error Tracking receives the same bounded metadata plus a synthetic exception; raw arguments, flag values, environment variables, API keys, secret names, base URLs and hosts, local paths, project ids, error messages, stack traces, response bodies, and source context are not sent. GeoIP enrichment is disabled. Distinct id starts as a random UUID stored next to the credentials file. After a successful authenticated command, the CLI calls `GET /me` and aliases that UUID onto the platform user id without setting person properties. Set `TOLOKA_TELEMETRY=0` or `DO_NOT_TRACK=1` to disable capture. CI environments are disabled unless `TOLOKA_TELEMETRY=1`. The explicitly approved full-text `report` event is documented in [report](#report).
|
|
@@ -190,7 +193,7 @@ By default the command asks whether to sign in through the browser or with an AP
|
|
|
190
193
|
|
|
191
194
|
Browser login is available for `https://platform.toloka.ai` and the `*.platform.toloka-test.ai` environments. Without a terminal the command opens the chooser page directly and fails fast when no browser can be opened; automation uses an API key instead.
|
|
192
195
|
|
|
193
|
-
The API key choice does not expire: the key works until it is deleted in the Business Console, which suits scripts
|
|
196
|
+
The API key choice does not expire: the key works until it is deleted in the Business Console, which suits scripts and CI. Choosing it asks for the key with masked input; `--api-key-stdin` reads it from standard input instead, so it never appears in shell history or the process list; `--api-key <key>` and `TOLOKA_API_KEY` also work but expose the key to both. For an already-issued project-scoped token, set `TOLOKA_SCOPED_TOKEN` for the command instead of storing it with `login`. Platforms without a known login service go straight to the API key prompt. The command verifies the key and prints `Authenticated against <base-url> with an API key`, then the number of projects visible, where the key is stored, and a reminder that the key does not expire until you delete it in the Business Console.
|
|
194
197
|
|
|
195
198
|
## logout
|
|
196
199
|
|
|
@@ -210,7 +213,7 @@ Shows which platform and login the CLI would use. Alias: `whoami`.
|
|
|
210
213
|
toloka auth-status [--show-token]
|
|
211
214
|
```
|
|
212
215
|
|
|
213
|
-
On success it prints the platform URL with its source, then either the masked API key or a `Login:` line for the browser session and a `Session:` line with the current refresh window
|
|
216
|
+
On success it prints the platform URL with its source, then either the masked API key, the masked scoped token with `from TOLOKA_SCOPED_TOKEN`, or a `Login:` line for the browser session and a `Session:` line with the current refresh window. It then prints the selected project, `Reachable: N project(s) visible.`, `Account:` with the email the platform reports, `Internal: true` or `Internal: false` for the account, and a `Teams:` line that marks each team `(internal)` and `(read-only)` where that applies. The project line is printed even when the probe fails. `--show-token` prints the full active credential instead of the mask; pass it only when you need the raw value on this terminal. It exits `1` when no login is in effect, when the browser session has expired, when the platform rejects the credential, or when the platform cannot be reached.
|
|
214
217
|
|
|
215
218
|
## config
|
|
216
219
|
|
|
@@ -415,6 +418,16 @@ In a terminal the command asks `Send this report to Toloka? [y/N]`; outside a te
|
|
|
415
418
|
|
|
416
419
|
One `sdk_cli_issue_report_submitted` event carries `report_id`, the complete `report_text`, its UTF-8 `report_length_bytes`, `authored_by`, and `platform`, plus the standard pseudonymous CLI telemetry context. The command never reads or sends an API key. It flushes PostHog before printing `Submitted issue report: <id>`; a delivery failure exits non-zero and prints no success ID. When CLI telemetry is disabled, issue reporting is unavailable rather than bypassing that preference.
|
|
417
420
|
|
|
421
|
+
## ui
|
|
422
|
+
|
|
423
|
+
Inspect the Task UI components the launcher renders. The command opens the launcher in a headless Chromium (Playwright from the project's `node_modules`), so it needs the same local setup as `toloka check` and no credentials.
|
|
424
|
+
|
|
425
|
+
```sh
|
|
426
|
+
toloka ui components [names...] [--json]
|
|
427
|
+
```
|
|
428
|
+
|
|
429
|
+
Without names it prints one line per public component (`name: description`). With names it prints each component's description, when to use it, capabilities, limitations, best practices, the `propsType` contract, the value shape, and the documented JSX example with its output. An unknown name fails with `Unknown Task UI components: <name>`. `--json` prints the same contracts keyed by component name. The output always reflects the currently deployed launcher, so run it instead of guessing component names or props from memory.
|
|
430
|
+
|
|
418
431
|
## skill
|
|
419
432
|
|
|
420
433
|
Discover bundled agent skills and install them into the project. The Skill Catalog ships inside the SDK package, so these actions are local and deterministic: they need no credentials, make no network requests, and change with the package version only.
|
package/docs/getting-started.md
CHANGED
|
@@ -37,7 +37,7 @@ The session renews itself for up to 14 days; after that, run `toloka login` agai
|
|
|
37
37
|
toloka whoami
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
CI does not need `login`. Set `TOLOKA_API_KEY` and the stored login is ignored; an agent without a terminal can pipe a key to `toloka login --api-key-stdin`. See the precedence table in the [CLI reference](./cli.md).
|
|
40
|
+
CI does not need `login`. Set `TOLOKA_API_KEY` for a long-lived API key or `TOLOKA_SCOPED_TOKEN` for an issued project-scoped token, and the stored login is ignored; an agent without a terminal can pipe a long-lived key to `toloka login --api-key-stdin`. See the precedence table in the [CLI reference](./cli.md).
|
|
41
41
|
|
|
42
42
|
## Start with an agent
|
|
43
43
|
|
package/docs/testing.md
CHANGED
|
@@ -83,7 +83,7 @@ Use an ordinary TypeScript loop to register several `test.code` or `test.ui` sce
|
|
|
83
83
|
|
|
84
84
|
The low-level `ui` fixture remains available inside ordinary Vitest tests for component discovery and exploratory work, but plain tests do not satisfy required node coverage. Use typed wrappers for committed gate evidence. Importing `@toloka-platform/sdk/testing` from a plain Bun script executes Vitest fixture setup and fails.
|
|
85
85
|
|
|
86
|
-
- `ui.componentDocumentation(names?)` returns the current launcher's supported component contracts.
|
|
86
|
+
- `ui.componentDocumentation(names?)` returns the current launcher's supported component contracts. `ui` is a fixture argument of `test` (`test("…", async ({ ui }) => …)`), not an export. Outside a test, `toloka ui components [names...]` prints the same contracts.
|
|
87
87
|
- `ui.render(pipeline, node, options)` creates a fresh browser context and returns a Task UI session for exactly that authored node. `test.ui` supplies the equivalent bound `render(options?)` helper.
|
|
88
88
|
|
|
89
89
|
The returned Task UI session supports:
|
package/docs/troubleshooting.md
CHANGED
|
@@ -96,7 +96,7 @@ toloka pipeline results <runId> --failed
|
|
|
96
96
|
Not logged in. Run toloka login once to sign in through your browser on this machine:
|
|
97
97
|
```
|
|
98
98
|
|
|
99
|
-
Run `toloka login`. Where a browser is not available, pipe
|
|
99
|
+
Run `toloka login`. Where a browser is not available, pipe a long-lived API key to `toloka login --api-key-stdin` or set `TOLOKA_API_KEY`; set `TOLOKA_SCOPED_TOKEN` when using an issued project-scoped token. `toloka whoami` reports which platform and which credential source is in effect, and exits non-zero when the platform is unreachable.
|
|
100
100
|
|
|
101
101
|
## The browser login expired
|
|
102
102
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@toloka-platform/sdk",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Build, inspect, and operate Toloka pipeline projects as TypeScript source. The public API may change between minor versions before 1.0.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -77,6 +77,6 @@
|
|
|
77
77
|
"bun": ">=1.4.0"
|
|
78
78
|
},
|
|
79
79
|
"tolokaBuild": {
|
|
80
|
-
"commit": "
|
|
80
|
+
"commit": "33545324d935ee7d0e9da6f845c03b25ae49759f"
|
|
81
81
|
}
|
|
82
82
|
}
|
package/skills/catalog.json
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"testing",
|
|
12
12
|
"checks"
|
|
13
13
|
],
|
|
14
|
-
"version": "0.11.
|
|
15
|
-
"digest": "
|
|
14
|
+
"version": "0.11.2",
|
|
15
|
+
"digest": "f97f94dd6eff745884e07d9f19cc414fdc2fe5d9b7992e062e7beb0058dcce59",
|
|
16
16
|
"files": [
|
|
17
17
|
"SKILL.md",
|
|
18
18
|
"references/node-semantics.md",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"bug",
|
|
30
30
|
"report"
|
|
31
31
|
],
|
|
32
|
-
"version": "0.11.
|
|
32
|
+
"version": "0.11.2",
|
|
33
33
|
"digest": "70f0bd2c80b32f87896aeb27c363b8a2d37f40e22424f57fe1aad142e125ad67",
|
|
34
34
|
"files": [
|
|
35
35
|
"SKILL.md",
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
"getting-started",
|
|
47
47
|
"agent"
|
|
48
48
|
],
|
|
49
|
-
"version": "0.11.
|
|
49
|
+
"version": "0.11.2",
|
|
50
50
|
"digest": "ccf148c682de4e452bb031db95d3933257899006b1766f0fdbc3d15265a63bb9",
|
|
51
51
|
"files": [
|
|
52
52
|
"SKILL.md"
|
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"sft",
|
|
64
64
|
"models"
|
|
65
65
|
],
|
|
66
|
-
"version": "0.11.
|
|
66
|
+
"version": "0.11.2",
|
|
67
67
|
"digest": "279c69977eef170dada20b664ee0c30858681e84a2cabb2fdff18b8bb1bdc055",
|
|
68
68
|
"files": [
|
|
69
69
|
"SKILL.md"
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"prompt",
|
|
81
81
|
"models"
|
|
82
82
|
],
|
|
83
|
-
"version": "0.11.
|
|
83
|
+
"version": "0.11.2",
|
|
84
84
|
"digest": "b9f314eb6b39754bc8d2ae79aca3dc680fced5f14948c975d1e084a83c2e0a8c",
|
|
85
85
|
"files": [
|
|
86
86
|
"SKILL.md"
|
|
@@ -80,6 +80,7 @@ Distinguish server requirements from optional design choices. The following cons
|
|
|
80
80
|
- **Training is part of the node, not extra.** A quorum node requires `onboarding`, and while it is enabled the platform wants at least three steps and at least one quiz block. Preserve an explicit `enabled: false` that a user or `toloka import` put there; never switch Training on to satisfy your own sense of completeness.
|
|
81
81
|
- **Optional settings should serve the task.** Exams, non-default QA, audience reuse, and smart search can be useful when the requirements call for them. Omit blocks that add no value rather than copying placeholders; an empty disabled block is still authored state. Preserve imported choices unless changing them is part of the task, and explain additions that affect cost or expert eligibility.
|
|
82
82
|
- **Run scope belongs to the platform.** `itemFilter` and `itemLimit` on the root node are set from the platform, and apply preserves whatever is live unless source declares its own value. Do not add an item limit to source to "restore" one you saw in the UI.
|
|
83
|
+
- **Entities use a closed vocabulary, and label entities need choices.** `entityType` must be one of the platform core types listed in [node semantics](references/node-semantics.md): `CONTEXT` for any non-media input the expert reads (text, numbers, objects), `IMAGE`/`VIDEO`/`AUDIO`/`DOCUMENT` for media inputs, `SINGLE_LABEL` or `MULTI_LABEL` for fixed choices, `SHORT_ANSWER` or `RESPONSE` for produced text, `EXPLANATION` only for reasoning about another value. Never invent `TEXT`, `LABEL`, or another name. Every `SINGLE_LABEL`/`MULTI_LABEL` output needs `allowedLabels` with at least two `{ label, value }` entries whose `value` is snake_case. The SDK checks these when it loads the source, so `toloka check` (and `toloka plan`, `toloka apply`, `toloka validate`) fails locally with `PipelineGraphValidationError` before any platform request; read the reference before writing a node instead of discovering them from a failed check.
|
|
83
84
|
|
|
84
85
|
## Know the validation boundary
|
|
85
86
|
|
|
@@ -89,7 +90,7 @@ Distinguish server requirements from optional design choices. The following cons
|
|
|
89
90
|
|
|
90
91
|
`toloka validate` posts each bound graph to the platform. Treat source/spec errors as blocking and warnings as informational. A `not_ran_node_check` concerns a separate Remote Node Check and does not replace a passing Local Check Gate, but its remote-validation severity still matters: lint checks report it as a blocking error while other checks can report it as a warning. A pipeline not yet on the platform is skipped until `toloka apply` creates it. When a reviewed plan adds a dataset field already bound by an existing live pipeline, pre-apply validation can report that exact field as missing; apply only the reviewed additive plan, then require post-apply validation to clear source/spec errors. Never rewrite valid source for a missing Remote Node Check.
|
|
91
92
|
|
|
92
|
-
Before choosing Task UI components,
|
|
93
|
+
Before choosing Task UI components, list them from the launcher with `toloka ui components` (one line per component) and read the contract of the ones you intend to use with `toloka ui components <name> [name...]`; never guess component names or props from memory. If this SDK version has no `ui` command, the same data comes from the `ui` fixture of the `test` exported by `@toloka-platform/sdk/testing` (`ui` is a fixture argument, not an export): write a throwaway `probe.spec.ts` with `test("components", async ({ ui }) => { console.log(JSON.stringify(await ui.componentDocumentation(["View.Image"]), null, 2)); });`, run `bunx vitest run probe.spec.ts`, and delete the file before `toloka check`. A component's usable contract is the returned `propsType` plus its returned `valueType` and `example`; shared `Field` props such as `path`, `label`, `hint`, and `validation` can be confirmed from any current `Field.*` example, but do not invent any other prop. For structured or array-valued Fields, derive the dataset field schema from `valueType`, then feed the documented example value through `setValue`, `getValue`, and `submit` in the UI scenario so upstream contract drift fails locally. Never translate undocumented property names or replace missing values with defaults. Every identifier in `ui.code` must be a returned component or documented `actions` API, and JSX must have one root element. Register each required UI scenario with `test.ui(pipeline, node, name, callback)` and submit through its bound `render` helper. Run `toloka check <pipeline> --node <name-or-id>` after every Task UI edit; require passing UI types, empty browser validation errors, valid postprocessed entity output, and visual inspection where relevant. Never weaken authored validation merely to make a test pass.
|
|
93
94
|
|
|
94
95
|
`ui.code` accepts TSX, including TypeScript annotations. `preprocess` and `postprocess` are JavaScript function bodies; do not put TypeScript-only syntax in either transform.
|
|
95
96
|
|
|
@@ -104,8 +105,9 @@ Register code behavior with `test.code(pipeline, node, name, callback)` and exec
|
|
|
104
105
|
|
|
105
106
|
`toloka login` signs the user in through the browser, or stores an API key when they choose that, and
|
|
106
107
|
keeps the login with owner-only permissions, so no later command needs `--api-key`. Precedence is flag,
|
|
107
|
-
then environment variable (`
|
|
108
|
-
browser session. `
|
|
108
|
+
then environment variable (`TOLOKA_SCOPED_TOKEN`, then `TOLOKA_API_KEY`; `TOLOKA_BASE_URL`), then the
|
|
109
|
+
stored API key, then the browser session. `TOLOKA_SCOPED_TOKEN` is a project-scoped token minted for the
|
|
110
|
+
user by the platform (for example inside an agent sandbox); it never grants more than that user has. `toloka auth-status` reports what is in effect and where it came from; `toloka logout`
|
|
109
111
|
ends the browser session and removes the stored files. If credentials are missing, run `toloka login --browser`
|
|
110
112
|
yourself: it opens the sign-in page in the user's browser and waits, so ask the user to pick their
|
|
111
113
|
provider and finish signing in there. Never request a key in chat.
|
|
@@ -142,11 +142,11 @@ An authored entity references a declared `field`. Choose its cardinality from th
|
|
|
142
142
|
- One logical result from one labeler is static. A caption, verdict, rating, recording, or upload remains one entity even when its value is an object or a component holds it in an array. Omit `dynamic` and `maxEntities`, and use a `jsonPath` without `[*]`.
|
|
143
143
|
- Multiple independent results from one labeler are dynamic. Examples include one box per visible face and one defect record per discovered defect. Set `dynamic: true`, use exactly one `[*]` in `jsonPath`, and set `maxEntities` to the tightest upper bound justified by the task. Never use 50 as an unexamined default.
|
|
144
144
|
- Do not choose `dynamic` from a TypeScript or JSON container shape. Ask whether the task expects one logical result or a repeated collection; the answer owns the entity model.
|
|
145
|
-
- Use `allowedLabels: [{ label, value }]` for fixed choices. Use `SINGLE_LABEL` for exactly one stored value and `MULTI_LABEL` for multiple values.
|
|
145
|
+
- Use `allowedLabels: [{ label, value }]` for fixed choices. Use `SINGLE_LABEL` for exactly one stored value and `MULTI_LABEL` for multiple values. Every `SINGLE_LABEL`/`MULTI_LABEL` output needs at least two entries, and each `value` must be snake_case.
|
|
146
146
|
- Set media duration bounds only when the expert produces the media through an upload or recording output. Input media used for annotation does not require duration bounds.
|
|
147
147
|
- Give every output entity at least one relation to the input it uses: `{ field, rel_type: 'context' | 'references' | 'depends_on' | 'evaluates' }`, or a conditional relation supported by the public type.
|
|
148
148
|
|
|
149
|
-
`entityType` is a closed platform vocabulary even where a generated TypeScript shape is currently broad. Use `CONTEXT` for any non-media input the expert reads, including text, numbers, and objects. Use `SHORT_ANSWER` for short produced text, `RESPONSE` for long produced text, and `EXPLANATION` only for reasoning about another value. Use `SINGLE_LABEL` or `MULTI_LABEL` for fixed choices and the matching media or annotation type for those values. Never invent `TEXT` or another natural-language type name; consult the documented vocabulary when these categories do not fit.
|
|
149
|
+
`entityType` is a closed platform vocabulary even where a generated TypeScript shape is currently broad. Use `CONTEXT` for any non-media input the expert reads, including text, numbers, and objects. Use `SHORT_ANSWER` for short produced text, `RESPONSE` for long produced text, and `EXPLANATION` only for reasoning about another value. Use `SINGLE_LABEL` or `MULTI_LABEL` for fixed choices and the matching media or annotation type for those values. Never invent `TEXT` or another natural-language type name; consult the documented vocabulary when these categories do not fit. The full vocabulary, generated from the server schema into the SDK's `core-entity-types.json`, is `JUDGEMENT`, `EXPLANATION`, `PROMPT`, `USER_PROMPT`, `RESPONSE`, `RESPONSE_EVALUATION`, `RUBRIC`, `RUBRICS_GROUP`, `DIALOG`, `DIALOG_STEP`, `DIALOG_EVALUATION`, `SHORT_ANSWER`, `FEEDBACK`, `CONTEXT`, `IMAGE`, `ANNOTATION`, `SINGLE_LABEL`, `MULTI_LABEL`, `VIDEO`, `DOCUMENT`, `GOLDEN_SET`, `LOGS`, `SOLUTION`, `CODE`, `METADATA`, `AUDIO`, `AGENT_TRAJECTORY`, `MCP_SERVER`, `CONTAINER`, `CUSTOM`. The SDK validates `entityType` and the `allowedLabels` rules when it loads the source, so `toloka check` fails locally with `PipelineGraphValidationError` before anything is sent to the platform.
|
|
150
150
|
|
|
151
151
|
Entities enforce format, not meaning. Put types, ranges, cardinality, requiredness, and labels in the field schema and entity. Put accuracy, correctness, completeness, and clarity in quality criteria.
|
|
152
152
|
|
|
@@ -73,8 +73,10 @@ The API key choice does not expire: the key works until it is deleted in the Bus
|
|
|
73
73
|
suits scripts, CI, and coding agents. Type it at the masked prompt, or pipe it to `toloka login --api-key-stdin`.
|
|
74
74
|
`toloka login --api-key <key>` also works but leaves the key in shell history. Most later commands read the stored login, so
|
|
75
75
|
`--api-key` becomes optional. For networked commands that do not mutate the machine project registry,
|
|
76
|
-
credential precedence is flag, then `TOLOKA_API_KEY`, then the stored API key,
|
|
77
|
-
session; CI can use an environment variable without running login.
|
|
76
|
+
credential precedence is flag, then `TOLOKA_SCOPED_TOKEN`, then `TOLOKA_API_KEY`, then the stored API key,
|
|
77
|
+
then the browser session; CI can use an environment variable without running login. A scoped token is
|
|
78
|
+
minted by the platform for one user and one project (agent sandboxes receive it in `TOLOKA_SCOPED_TOKEN`);
|
|
79
|
+
it cannot issue further tokens and expires on its own. The base URL defaults to the platform.
|
|
78
80
|
|
|
79
81
|
```sh
|
|
80
82
|
toloka auth-status # or toloka whoami
|
|
@@ -100,8 +102,8 @@ A scoped token downloads project files in two steps: a request to `ProjectFile.u
|
|
|
100
102
|
|
|
101
103
|
## Configure the CLI
|
|
102
104
|
|
|
103
|
-
Provide the
|
|
104
|
-
`TOLOKA_BASE_URL`, or with `--api-key` and `--base-url`. A base URL with a path (a gateway such as
|
|
105
|
+
Provide the credential and base URL on networked workflows through `TOLOKA_SCOPED_TOKEN` or
|
|
106
|
+
`TOLOKA_API_KEY` and `TOLOKA_BASE_URL`, or with `--api-key` and `--base-url`. A base URL with a path (a gateway such as
|
|
105
107
|
`https://gateway.example/ss/pp.1`) keeps that path on every request, with or without a trailing
|
|
106
108
|
slash; use the given URL unchanged. The registry-mutating commands
|
|
107
109
|
`toloka project add`, `toloka project switch`, `toloka project remove`, and `toloka project create`
|
|
@@ -273,7 +275,7 @@ Apply preserves existing live layout and the root's live item filter. Do not add
|
|
|
273
275
|
|
|
274
276
|
Register every code-node behavior with `test.code(pipeline, node, name, callback)`. Its callback receives a bound `run(inputs, options?)` helper with the same Python execution contract as `runCodeNode`; the test counts only after the bound executor runs, an assertion executes, and the callback passes.
|
|
275
277
|
|
|
276
|
-
Before choosing component names or props,
|
|
278
|
+
Before choosing component names or props, run `toloka ui components` to list the launcher's components and `toloka ui components <name> [name...]` for their contracts (on an SDK without the `ui` command, use the `ui` fixture argument of `test` from `@toloka-platform/sdk/testing` in a throwaway spec and delete it afterwards). A component's contract is its returned `propsType`, `valueType`, and `example`. For structured or array-valued Fields, derive the dataset field schema from `valueType`, then feed the documented example value through `setValue`, `getValue`, and `submit`; never translate undocumented property names or replace missing values with defaults. Register required human-work behavior with `test.ui(pipeline, node, name, callback)`. Its callback receives bound `render(options?)`; the test counts only after a successful `submit`, at least one assertion, and a passing callback.
|
|
277
279
|
|
|
278
280
|
Register representative connected flows with `test.pipeline(pipeline, name, callback)`. Choose fixtures from available data that exercise the task, redacting sensitive values while preserving field names, nesting, optionality, and media/value shapes. Use synthetic cases for privacy or boundaries absent from the sample. Use the callback's bound `runCode` and `renderUi` helpers, merge only declared node outputs, and assert the final `root.completed` fields. Cover meaningful match branches, optional routes, and failures as well as the happy path.
|
|
279
281
|
|
package/src/cli/auth.ts
CHANGED
|
@@ -451,27 +451,47 @@ export type AuthStatusOptions = {
|
|
|
451
451
|
export type AuthStatusResult = { readonly authenticated: boolean; readonly output: string };
|
|
452
452
|
|
|
453
453
|
type ResolvedValue = { readonly value: string; readonly source: string };
|
|
454
|
+
type ResolvedCredential = ResolvedValue & {
|
|
455
|
+
readonly type: "apiKey" | "scopedToken";
|
|
456
|
+
readonly environmentVariable?: "TOLOKA_SCOPED_TOKEN" | "TOLOKA_API_KEY";
|
|
457
|
+
};
|
|
454
458
|
|
|
455
|
-
const firstDefined = (
|
|
456
|
-
candidates
|
|
457
|
-
): ResolvedValue | undefined => candidates.find((candidate) => candidate !== undefined);
|
|
459
|
+
const firstDefined = <T>(candidates: ReadonlyArray<T | undefined>): T | undefined =>
|
|
460
|
+
candidates.find((candidate) => candidate !== undefined);
|
|
458
461
|
|
|
459
|
-
const
|
|
462
|
+
const resolveStatusCredential = (
|
|
460
463
|
options: AuthStatusOptions,
|
|
461
464
|
environment: CliEnvironment,
|
|
462
465
|
found: FoundCredentials | undefined,
|
|
463
466
|
storedPath: string,
|
|
464
|
-
):
|
|
465
|
-
firstDefined([
|
|
467
|
+
): ResolvedCredential | undefined =>
|
|
468
|
+
firstDefined<ResolvedCredential>([
|
|
466
469
|
options.flagApiKey === undefined
|
|
467
470
|
? undefined
|
|
468
|
-
: { value: options.flagApiKey, source: "from --api-key" },
|
|
471
|
+
: { type: "apiKey", value: options.flagApiKey, source: "from --api-key" },
|
|
472
|
+
environment.TOLOKA_SCOPED_TOKEN === undefined
|
|
473
|
+
? undefined
|
|
474
|
+
: {
|
|
475
|
+
type: "scopedToken",
|
|
476
|
+
value: environment.TOLOKA_SCOPED_TOKEN,
|
|
477
|
+
source: "from TOLOKA_SCOPED_TOKEN",
|
|
478
|
+
environmentVariable: "TOLOKA_SCOPED_TOKEN",
|
|
479
|
+
},
|
|
469
480
|
environment.TOLOKA_API_KEY === undefined
|
|
470
481
|
? undefined
|
|
471
|
-
: {
|
|
482
|
+
: {
|
|
483
|
+
type: "apiKey",
|
|
484
|
+
value: environment.TOLOKA_API_KEY,
|
|
485
|
+
source: "from TOLOKA_API_KEY",
|
|
486
|
+
environmentVariable: "TOLOKA_API_KEY",
|
|
487
|
+
},
|
|
472
488
|
found?.credentials.apiKey === undefined
|
|
473
489
|
? undefined
|
|
474
|
-
: {
|
|
490
|
+
: {
|
|
491
|
+
type: "apiKey",
|
|
492
|
+
value: found.credentials.apiKey,
|
|
493
|
+
source: `stored in ${storedPath}`,
|
|
494
|
+
},
|
|
475
495
|
]);
|
|
476
496
|
|
|
477
497
|
const resolvePlatform = (
|
|
@@ -576,17 +596,23 @@ export async function authStatus(options: AuthStatusOptions = {}): Promise<AuthS
|
|
|
576
596
|
const now = (options.now ?? unixSeconds)();
|
|
577
597
|
const credentialLookup =
|
|
578
598
|
options.credentialLookup ??
|
|
579
|
-
(await findCommandCredentials(
|
|
599
|
+
(await findCommandCredentials(
|
|
600
|
+
environment,
|
|
601
|
+
options.flagApiKey ??
|
|
602
|
+
environment.TOLOKA_SCOPED_TOKEN ??
|
|
603
|
+
environment.TOLOKA_API_KEY,
|
|
604
|
+
));
|
|
580
605
|
const { found, warning } = credentialLookup;
|
|
581
606
|
const storedPath = found?.path ?? credentialsPath(environment);
|
|
582
|
-
const
|
|
607
|
+
const credential = resolveStatusCredential(options, environment, found, storedPath);
|
|
583
608
|
const platform = resolvePlatform(options, environment, found);
|
|
584
|
-
const browser =
|
|
609
|
+
const browser =
|
|
610
|
+
credential === undefined ? await findBrowserLogin(platform.value, environment) : undefined;
|
|
585
611
|
const projectLine = formatProjectContextLine(options.project, found?.credentials);
|
|
586
612
|
|
|
587
613
|
const lines: string[] = warning === undefined ? [] : [warning, ""];
|
|
588
614
|
|
|
589
|
-
if (
|
|
615
|
+
if (credential === undefined && browser === undefined) {
|
|
590
616
|
lines.push(
|
|
591
617
|
"Not logged in.",
|
|
592
618
|
"",
|
|
@@ -600,11 +626,15 @@ export async function authStatus(options: AuthStatusOptions = {}): Promise<AuthS
|
|
|
600
626
|
const { value: baseUrl, source: urlSource } = platform;
|
|
601
627
|
lines.push(`Platform: ${baseUrl} (${urlSource})`);
|
|
602
628
|
let clientConfig: ClientConfig;
|
|
603
|
-
if (
|
|
629
|
+
if (credential !== undefined) {
|
|
630
|
+
const label = credential.type === "scopedToken" ? "Scoped token:" : "API key: ";
|
|
604
631
|
lines.push(
|
|
605
|
-
|
|
632
|
+
`${label} ${options.showToken === true ? credential.value : maskApiKey(credential.value)} (${credential.source})`,
|
|
606
633
|
);
|
|
607
|
-
clientConfig =
|
|
634
|
+
clientConfig =
|
|
635
|
+
credential.type === "scopedToken"
|
|
636
|
+
? { baseUrl, scopedToken: credential.value }
|
|
637
|
+
: { baseUrl, apiKey: credential.value };
|
|
608
638
|
} else {
|
|
609
639
|
assertCli(browser, "No login is in effect.", { hint: { description: "Run toloka login." } });
|
|
610
640
|
const status = await browserStatus(browser, baseUrl, environment, options, now);
|
|
@@ -617,8 +647,11 @@ export async function authStatus(options: AuthStatusOptions = {}): Promise<AuthS
|
|
|
617
647
|
}
|
|
618
648
|
lines.push(projectLine);
|
|
619
649
|
|
|
620
|
-
if (
|
|
621
|
-
lines.push(
|
|
650
|
+
if (found?.credentials.apiKey !== undefined && credential?.environmentVariable !== undefined) {
|
|
651
|
+
lines.push(
|
|
652
|
+
"",
|
|
653
|
+
`${credential.environmentVariable} is set, so the key stored in ${found.path} is ignored.`,
|
|
654
|
+
);
|
|
622
655
|
}
|
|
623
656
|
if (found?.legacy === true) {
|
|
624
657
|
lines.push(
|
package/src/cli/check.ts
CHANGED
|
@@ -25,7 +25,7 @@ import {
|
|
|
25
25
|
} from "../testing/test-evidence";
|
|
26
26
|
import { cliRelaunchCommand } from "./cli-entry";
|
|
27
27
|
import { bunSubprocessEnvironment } from "./native-runtime";
|
|
28
|
-
import {
|
|
28
|
+
import { CREDENTIAL_ENVIRONMENT_NAMES } from "./environment";
|
|
29
29
|
import { loadProjectSource, type ProjectSource } from "./source";
|
|
30
30
|
import { assertCli, REPORT_HINT, TolokaCliError } from "./cli-error";
|
|
31
31
|
import type { TolokaCliErrorOptions } from "./cli-error";
|
|
@@ -276,13 +276,13 @@ const loadSourceWithoutPlatformCredentials = async (
|
|
|
276
276
|
environment: NodeJS.ProcessEnv,
|
|
277
277
|
): Promise<ProjectSource> => {
|
|
278
278
|
const targets = [process.env, environment];
|
|
279
|
-
const saved = targets.map((current) =>
|
|
280
|
-
for (const current of targets) for (const name of
|
|
279
|
+
const saved = targets.map((current) => CREDENTIAL_ENVIRONMENT_NAMES.map((name) => current[name]));
|
|
280
|
+
for (const current of targets) for (const name of CREDENTIAL_ENVIRONMENT_NAMES) delete current[name];
|
|
281
281
|
try {
|
|
282
282
|
return await loadProjectSource(sourceRoot);
|
|
283
283
|
} finally {
|
|
284
284
|
targets.forEach((current, target) => {
|
|
285
|
-
|
|
285
|
+
CREDENTIAL_ENVIRONMENT_NAMES.forEach((name, index) => {
|
|
286
286
|
const value = saved[target]?.[index];
|
|
287
287
|
if (value === undefined) delete current[name];
|
|
288
288
|
else current[name] = value;
|
|
@@ -693,7 +693,7 @@ export async function checkProject(
|
|
|
693
693
|
try {
|
|
694
694
|
const selectedPipeline = selected.pipelines.length === 1 ? selected.pipelines[0] : undefined;
|
|
695
695
|
const checkEnvironment = { ...(options.environment ?? process.env) };
|
|
696
|
-
for (const name of
|
|
696
|
+
for (const name of CREDENTIAL_ENVIRONMENT_NAMES) delete checkEnvironment[name];
|
|
697
697
|
const environment = {
|
|
698
698
|
...checkEnvironment,
|
|
699
699
|
[TEST_EVIDENCE_DIR_ENV]: evidenceDirectory,
|
package/src/cli/commands/auth.ts
CHANGED
|
@@ -59,13 +59,15 @@ export const registerAuthCommands = (program: Command, host: CommandHost): void
|
|
|
59
59
|
program
|
|
60
60
|
.command("auth-status")
|
|
61
61
|
.alias("whoami")
|
|
62
|
-
.description("show which platform and
|
|
63
|
-
.option("--show-token", "print the API key or access token instead of masking it", false)
|
|
62
|
+
.description("show which platform and credential the CLI would use")
|
|
63
|
+
.option("--show-token", "print the API key, scoped token, or access token instead of masking it", false)
|
|
64
64
|
.action(async (options: { showToken: boolean }) => {
|
|
65
65
|
const flags = globals();
|
|
66
66
|
const credentialLookup = await findCommandCredentials(
|
|
67
67
|
context.environment,
|
|
68
|
-
flags.apiKey ??
|
|
68
|
+
flags.apiKey ??
|
|
69
|
+
context.environment.TOLOKA_SCOPED_TOKEN ??
|
|
70
|
+
context.environment.TOLOKA_API_KEY,
|
|
69
71
|
);
|
|
70
72
|
const stored = credentialLookup.found?.credentials;
|
|
71
73
|
const sourceRoot = await resolveSourceRoot(flags, context.io);
|
|
@@ -15,6 +15,7 @@ import { readTolokaState } from "../../state";
|
|
|
15
15
|
import { assertCli } from "../cli-error";
|
|
16
16
|
import { formatTable } from "../table";
|
|
17
17
|
import type { CreateScopedTokenBodyAnyOfPreset, ScopedTokenSummary } from "../../../openapi/gen/models";
|
|
18
|
+
import { SCOPED_TOKEN_PREFIX } from "../../transport";
|
|
18
19
|
|
|
19
20
|
const TOKEN_PRESETS: readonly CreateScopedTokenBodyAnyOfPreset[] = [
|
|
20
21
|
"read",
|
|
@@ -157,8 +158,12 @@ export const registerProjectCommands = (program: Command, host: CommandHost): vo
|
|
|
157
158
|
expiresIn?: number;
|
|
158
159
|
projects?: string;
|
|
159
160
|
}) => {
|
|
160
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
161
|
+
const { client, credential, projectId } = await resolveContext(globals(), context);
|
|
161
162
|
assertProjectSelected(projectId);
|
|
163
|
+
const usesScopedToken =
|
|
164
|
+
credential.type === "scopedToken" ||
|
|
165
|
+
(credential.type === "apiKey" && credential.apiKey.startsWith(SCOPED_TOKEN_PREFIX));
|
|
166
|
+
assertCli(!usesScopedToken, "A scoped token cannot issue tokens.", { hint: { disabled: true } });
|
|
162
167
|
const scope =
|
|
163
168
|
options.permissions === undefined
|
|
164
169
|
? { preset: options.preset }
|
|
@@ -174,7 +179,7 @@ export const registerProjectCommands = (program: Command, host: CommandHost): vo
|
|
|
174
179
|
: { projectIds: options.projects.split(",").map((projectId) => projectId.trim()) }),
|
|
175
180
|
});
|
|
176
181
|
context.io.stdout(token);
|
|
177
|
-
context.io.stderr(`Token expires at ${expiresAt}. Use it as
|
|
182
|
+
context.io.stderr(`Token expires at ${expiresAt}. Use it as TOLOKA_SCOPED_TOKEN.`);
|
|
178
183
|
},
|
|
179
184
|
);
|
|
180
185
|
project
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { describeTaskUiComponents } from "../../testing/task-ui-browser";
|
|
3
|
+
import { resolveSourceRoot, type CommandHost } from "../context";
|
|
4
|
+
import { uiComponentsCommand } from "../ui";
|
|
5
|
+
|
|
6
|
+
export const registerUiCommands = (program: Command, host: CommandHost): void => {
|
|
7
|
+
const { context, globals } = host;
|
|
8
|
+
const ui = program.command("ui").description("inspect the Task UI components the launcher renders");
|
|
9
|
+
ui.command("components")
|
|
10
|
+
.argument("[names...]", "component names such as Field.RadioGroup; omit to list every component")
|
|
11
|
+
.option("--json", "print the component contracts as JSON", false)
|
|
12
|
+
.description("list Task UI components, or print the props, value shape, and example of the named ones")
|
|
13
|
+
.action(async (names: string[], options: { json: boolean }) => {
|
|
14
|
+
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
15
|
+
await uiComponentsCommand({
|
|
16
|
+
sourceRoot,
|
|
17
|
+
names,
|
|
18
|
+
json: options.json,
|
|
19
|
+
io: context.io,
|
|
20
|
+
describe: context.taskUiComponents ?? describeTaskUiComponents,
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
};
|
package/src/cli/config.ts
CHANGED
|
@@ -12,12 +12,14 @@ export type TolokaCliFlags = {
|
|
|
12
12
|
|
|
13
13
|
export type TolokaEnvironment = Readonly<Record<string, string | undefined>> & {
|
|
14
14
|
readonly TOLOKA_API_KEY?: string;
|
|
15
|
+
readonly TOLOKA_SCOPED_TOKEN?: string;
|
|
15
16
|
readonly TOLOKA_BASE_URL?: string;
|
|
16
17
|
readonly TOLOKA_PROJECT_ID?: string;
|
|
17
18
|
};
|
|
18
19
|
|
|
19
20
|
export type CliCredential =
|
|
20
21
|
| { readonly type: "apiKey"; readonly apiKey: string }
|
|
22
|
+
| { readonly type: "scopedToken"; readonly scopedToken: string }
|
|
21
23
|
| { readonly type: "browser"; readonly provider: BrowserLoginProvider };
|
|
22
24
|
|
|
23
25
|
export type ResolvedTolokaConfig = {
|
|
@@ -60,15 +62,22 @@ export const MISSING_CREDENTIALS_MESSAGE = [
|
|
|
60
62
|
"",
|
|
61
63
|
" toloka login",
|
|
62
64
|
"",
|
|
63
|
-
"Automation can pass --api-key, set TOLOKA_API_KEY, or pipe a key to toloka login --api-key-stdin.",
|
|
65
|
+
"Automation can pass --api-key, set TOLOKA_SCOPED_TOKEN or TOLOKA_API_KEY, or pipe a key to toloka login --api-key-stdin.",
|
|
64
66
|
"Run toloka auth-status to see what is in effect.",
|
|
65
67
|
].join("\n");
|
|
66
68
|
|
|
67
|
-
export const
|
|
69
|
+
export const resolveConfiguredCredential = (
|
|
68
70
|
flags: TolokaCliFlags,
|
|
69
71
|
environment: TolokaEnvironment,
|
|
70
72
|
stored?: StoredCredentials,
|
|
71
|
-
):
|
|
73
|
+
): Exclude<CliCredential, { readonly type: "browser" }> | undefined => {
|
|
74
|
+
if (flags.apiKey !== undefined) return { type: "apiKey", apiKey: flags.apiKey };
|
|
75
|
+
if (environment.TOLOKA_SCOPED_TOKEN !== undefined) {
|
|
76
|
+
return { type: "scopedToken", scopedToken: environment.TOLOKA_SCOPED_TOKEN };
|
|
77
|
+
}
|
|
78
|
+
const apiKey = environment.TOLOKA_API_KEY ?? stored?.apiKey;
|
|
79
|
+
return apiKey === undefined ? undefined : { type: "apiKey", apiKey };
|
|
80
|
+
};
|
|
72
81
|
|
|
73
82
|
export const resolveBaseUrl = (
|
|
74
83
|
flags: TolokaCliFlags,
|
|
@@ -88,7 +97,7 @@ export function resolveTolokaConfig(
|
|
|
88
97
|
stored?: StoredCredentials,
|
|
89
98
|
browserLogin?: BrowserLoginProvider,
|
|
90
99
|
): ResolvedTolokaConfig {
|
|
91
|
-
const
|
|
100
|
+
const configuredCredential = resolveConfiguredCredential(flags, environment, stored);
|
|
92
101
|
const baseUrl = resolveBaseUrl(flags, environment, stored);
|
|
93
102
|
const project = resolveProjectContext({
|
|
94
103
|
flags,
|
|
@@ -98,11 +107,8 @@ export function resolveTolokaConfig(
|
|
|
98
107
|
effectiveBaseUrl: baseUrl,
|
|
99
108
|
});
|
|
100
109
|
const credential: CliCredential | undefined =
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
: browserLogin !== undefined
|
|
104
|
-
? { type: "browser", provider: browserLogin }
|
|
105
|
-
: undefined;
|
|
110
|
+
configuredCredential ??
|
|
111
|
+
(browserLogin === undefined ? undefined : { type: "browser", provider: browserLogin });
|
|
106
112
|
assertCli(credential, MISSING_CREDENTIALS_MESSAGE, { hint: { disabled: true } });
|
|
107
113
|
return { baseUrl, credential, ...(project === undefined ? {} : { projectId: project.id }) };
|
|
108
114
|
}
|