@toloka-platform/sdk 0.1.0-preview.7 → 0.1.0-preview.9
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 +3 -4
- package/dist/cli/toloka.js +133 -130
- 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 +3 -5
- package/docs/cli.md +15 -0
- package/docs/limitations.md +1 -1
- package/examples/face-image-collection/nodes/order-faces/node.ts +2 -4
- package/examples/video-pair-rating/nodes/adjudication_match/node.ts +15 -17
- package/examples/video-pair-rating/nodes/assemble/node.ts +25 -27
- package/examples/video-pair-rating/nodes/cascade/node.ts +24 -26
- package/examples/video-pair-rating/nodes/finalize_overall/node.ts +17 -19
- package/examples/video-pair-rating/nodes/merge_adjudication/node.ts +13 -15
- package/examples/video-pair-rating/nodes/merge_op_adjudication/node.ts +12 -14
- package/examples/video-pair-rating/nodes/op_quorum_resolve/node.ts +13 -15
- package/examples/video-pair-rating/nodes/route_adjudication/node.ts +1 -3
- package/examples/video-pair-rating/nodes/route_op_adjudication/node.ts +5 -7
- package/examples/video-pair-rating/nodes/route_overall/node.ts +1 -3
- package/examples/video-pair-rating/nodes/shuffle_ab/node.ts +11 -13
- package/openapi/gen/models/datasetItem.ts +0 -2
- package/openapi/gen/models/index.ts +0 -1
- package/package.json +1 -1
- package/skills/catalog.json +4 -4
- package/skills/toloka-build/SKILL.md +1 -0
- package/skills/toloka-build/references/node-semantics.md +5 -5
- package/skills/toloka-build/references/workflow-and-cli.md +6 -1
- package/skills/toloka-report-issue/references/report-template.md +1 -1
- package/src/authoring/index.ts +27 -26
- package/src/checks/task-ui-typecheck.ts +1 -2
- package/src/cli/analytics.ts +8 -19
- package/src/cli/auto-update.ts +367 -0
- package/src/cli/boolean-flag.ts +13 -0
- package/src/cli/check.ts +3 -4
- package/src/cli/cli-entry.ts +41 -0
- package/src/cli/credentials.ts +3 -0
- package/src/cli/program.ts +37 -1
- package/src/cli/settings.ts +93 -0
- package/src/cli/skill.ts +4 -5
- package/src/importer.ts +12 -16
- package/openapi/gen/models/datasetItemDuplicateOfSubmissionId.ts +0 -2
package/src/cli/program.ts
CHANGED
|
@@ -19,6 +19,8 @@ import { authStatus, login, logout } from "./auth";
|
|
|
19
19
|
import type { AuthPrompt } from "./auth";
|
|
20
20
|
import { commandNameFromArgv, createCliAnalytics } from "./analytics";
|
|
21
21
|
import type { CliAnalytics, CliAnalyticsCommandOutcome } from "./analytics";
|
|
22
|
+
import type { AutoUpdateHooks } from "./auto-update";
|
|
23
|
+
import { maybeAutoUpdate } from "./auto-update";
|
|
22
24
|
import { PRODUCTION_BASE_URL, resolveInternalBaseUrl, resolveTolokaConfig } from "./config";
|
|
23
25
|
import type { TolokaEnvironment } from "./config";
|
|
24
26
|
import {
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
requireStoredCredentials,
|
|
28
30
|
} from "./credentials";
|
|
29
31
|
import type { StoredCredentials } from "./credentials";
|
|
32
|
+
import { getCliSetting, readCliSettings, setCliSetting } from "./settings";
|
|
30
33
|
import {
|
|
31
34
|
generateDatasetFieldDeclarations,
|
|
32
35
|
listProjectDatasets,
|
|
@@ -269,7 +272,8 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
269
272
|
commandNames.includes("help") ||
|
|
270
273
|
commandNames.includes("report") ||
|
|
271
274
|
commandNames.includes("url") ||
|
|
272
|
-
commandNames.includes("version")
|
|
275
|
+
commandNames.includes("version") ||
|
|
276
|
+
commandNames.includes("config")
|
|
273
277
|
)
|
|
274
278
|
return;
|
|
275
279
|
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
@@ -283,6 +287,29 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
283
287
|
.description("print the current CLI version")
|
|
284
288
|
.action(() => context.io.stdout(SDK_VERSION));
|
|
285
289
|
|
|
290
|
+
const config = program.command("config").description("read and write machine CLI settings");
|
|
291
|
+
config
|
|
292
|
+
.command("get")
|
|
293
|
+
.argument("[name]", "setting name; omit to print every setting")
|
|
294
|
+
.description("print a CLI setting, or every setting")
|
|
295
|
+
.action(async (name?: string) => {
|
|
296
|
+
const settings = await readCliSettings(context.environment);
|
|
297
|
+
context.io.stdout(getCliSetting(settings, name).output);
|
|
298
|
+
});
|
|
299
|
+
config
|
|
300
|
+
.command("set")
|
|
301
|
+
.argument("<name>", "setting name")
|
|
302
|
+
.argument("<value>", "setting value")
|
|
303
|
+
.description("write a CLI setting for this machine")
|
|
304
|
+
.action(async (name: string, value: string) => {
|
|
305
|
+
const result = await setCliSetting(context.environment, name, value);
|
|
306
|
+
context.io.stdout(result.output);
|
|
307
|
+
});
|
|
308
|
+
config.action(async () => {
|
|
309
|
+
const settings = await readCliSettings(context.environment);
|
|
310
|
+
context.io.stdout(getCliSetting(settings, undefined).output);
|
|
311
|
+
});
|
|
312
|
+
|
|
286
313
|
program
|
|
287
314
|
.command("login")
|
|
288
315
|
.description("store an API key for this machine after verifying it")
|
|
@@ -1127,7 +1154,16 @@ export async function runTolokaCli(
|
|
|
1127
1154
|
injectedClient?: PotatoClient,
|
|
1128
1155
|
analytics?: CliAnalytics,
|
|
1129
1156
|
runChecks?: typeof checkProject,
|
|
1157
|
+
autoUpdate?: AutoUpdateHooks,
|
|
1130
1158
|
): Promise<number> {
|
|
1159
|
+
const autoUpdateResult = await maybeAutoUpdate({
|
|
1160
|
+
argv,
|
|
1161
|
+
environment,
|
|
1162
|
+
io,
|
|
1163
|
+
...(autoUpdate === undefined ? {} : { hooks: autoUpdate }),
|
|
1164
|
+
});
|
|
1165
|
+
if (autoUpdateResult.status === "reexec") return autoUpdateResult.exitCode;
|
|
1166
|
+
|
|
1131
1167
|
const activeAnalytics =
|
|
1132
1168
|
environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
1133
1169
|
? (analytics ?? createCliAnalytics({ environment }))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { chmod, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { isFalseyFlag, isTruthyFlag } from "./boolean-flag";
|
|
4
|
+
import type { CliEnvironment } from "./credentials";
|
|
5
|
+
import { configDirectory, preparePrivateFilePath } from "./credentials";
|
|
6
|
+
|
|
7
|
+
export const AUTO_UPDATE_SETTING = "auto-update";
|
|
8
|
+
|
|
9
|
+
export type CliSettings = {
|
|
10
|
+
readonly autoUpdate: boolean;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_CLI_SETTINGS: CliSettings = {
|
|
14
|
+
autoUpdate: true,
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export class SettingsError extends Error {
|
|
18
|
+
constructor(message: string, options?: { readonly cause?: unknown }) {
|
|
19
|
+
super(message, options);
|
|
20
|
+
this.name = "SettingsError";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export const settingsPath = (environment: CliEnvironment = process.env): string =>
|
|
25
|
+
join(configDirectory(environment), "settings.json");
|
|
26
|
+
|
|
27
|
+
const asSettings = (payload: unknown): CliSettings => {
|
|
28
|
+
const autoUpdate =
|
|
29
|
+
typeof payload === "object" && payload !== null
|
|
30
|
+
? Reflect.get(payload, "autoUpdate")
|
|
31
|
+
: undefined;
|
|
32
|
+
if (typeof autoUpdate !== "boolean") return DEFAULT_CLI_SETTINGS;
|
|
33
|
+
return { autoUpdate };
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export async function readCliSettings(
|
|
37
|
+
environment: CliEnvironment = process.env,
|
|
38
|
+
): Promise<CliSettings> {
|
|
39
|
+
try {
|
|
40
|
+
return asSettings(JSON.parse(await readFile(settingsPath(environment), "utf8")));
|
|
41
|
+
} catch {
|
|
42
|
+
return DEFAULT_CLI_SETTINGS;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function writeCliSettings(
|
|
47
|
+
settings: CliSettings,
|
|
48
|
+
environment: CliEnvironment = process.env,
|
|
49
|
+
): Promise<string> {
|
|
50
|
+
const path = settingsPath(environment);
|
|
51
|
+
await preparePrivateFilePath(path);
|
|
52
|
+
await writeFile(path, `${JSON.stringify({ autoUpdate: settings.autoUpdate }, null, 2)}\n`, {
|
|
53
|
+
mode: 0o600,
|
|
54
|
+
});
|
|
55
|
+
await chmod(path, 0o600);
|
|
56
|
+
return path;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const parseBooleanSetting = (value: string): boolean => {
|
|
60
|
+
if (isTruthyFlag(value)) return true;
|
|
61
|
+
if (isFalseyFlag(value)) return false;
|
|
62
|
+
throw new SettingsError(`Expected a boolean (true or false), got ${JSON.stringify(value)}.`);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const formatCliSettings = (settings: CliSettings): string =>
|
|
66
|
+
`${AUTO_UPDATE_SETTING}=${settings.autoUpdate}`;
|
|
67
|
+
|
|
68
|
+
export const getCliSetting = (
|
|
69
|
+
settings: CliSettings,
|
|
70
|
+
name: string | undefined,
|
|
71
|
+
): { readonly output: string } => {
|
|
72
|
+
if (name === undefined || name.length === 0) return { output: formatCliSettings(settings) };
|
|
73
|
+
if (name !== AUTO_UPDATE_SETTING) {
|
|
74
|
+
throw new SettingsError(`Unknown setting "${name}". Known settings: ${AUTO_UPDATE_SETTING}.`);
|
|
75
|
+
}
|
|
76
|
+
return { output: String(settings.autoUpdate) };
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export async function setCliSetting(
|
|
80
|
+
environment: CliEnvironment,
|
|
81
|
+
name: string,
|
|
82
|
+
value: string,
|
|
83
|
+
): Promise<{ readonly output: string; readonly settings: CliSettings }> {
|
|
84
|
+
if (name !== AUTO_UPDATE_SETTING) {
|
|
85
|
+
throw new SettingsError(`Unknown setting "${name}". Known settings: ${AUTO_UPDATE_SETTING}.`);
|
|
86
|
+
}
|
|
87
|
+
const settings = { autoUpdate: parseBooleanSetting(value) };
|
|
88
|
+
await writeCliSettings(settings, environment);
|
|
89
|
+
return {
|
|
90
|
+
settings,
|
|
91
|
+
output: `Set ${AUTO_UPDATE_SETTING} to ${settings.autoUpdate}.`,
|
|
92
|
+
};
|
|
93
|
+
}
|
package/src/cli/skill.ts
CHANGED
|
@@ -2,11 +2,9 @@ import { assert } from "es-toolkit";
|
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import { copyFile, mkdir, mkdtemp, readFile, realpath, rename, rm, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
5
|
import { assertSafeDestination, entryStats, hasErrorCode } from "./file-system";
|
|
7
6
|
import { formatCliError } from "./format-error";
|
|
8
7
|
|
|
9
|
-
export const BUNDLED_SKILL_CATALOG_ROOT = fileURLToPath(new URL("../../skills", import.meta.url));
|
|
10
8
|
export const SKILL_CATALOG_MANIFEST = "catalog.json";
|
|
11
9
|
export const SKILL_INSTALL_ROOT = ".agents/skills";
|
|
12
10
|
export const SKILL_ENTRY_POINT = "SKILL.md";
|
|
@@ -133,15 +131,16 @@ export async function resolveBundledSkillCatalogRoot(
|
|
|
133
131
|
const runtimeRoot =
|
|
134
132
|
entry === undefined ? undefined : await findSkillCatalogRootAbove(dirname(entry));
|
|
135
133
|
if (runtimeRoot !== undefined) return runtimeRoot;
|
|
134
|
+
const workingDirectoryRoot = await findSkillCatalogRootAbove(process.cwd());
|
|
136
135
|
const searched =
|
|
137
136
|
entry === undefined
|
|
138
137
|
? "process.argv[1] names no runtime entry"
|
|
139
138
|
: `none exists between ${entry} and the filesystem root`;
|
|
140
139
|
assert(
|
|
141
|
-
|
|
142
|
-
`Cannot locate the bundled skill catalog. An installed @toloka-platform/sdk must ship ${SKILL_CATALOG_DIRECTORY}/${SKILL_CATALOG_MANIFEST} in a directory above its runtime entry, but ${searched}, and
|
|
140
|
+
workingDirectoryRoot !== undefined,
|
|
141
|
+
`Cannot locate the bundled skill catalog. An installed @toloka-platform/sdk must ship ${SKILL_CATALOG_DIRECTORY}/${SKILL_CATALOG_MANIFEST} in a directory above its runtime entry, but ${searched}, and no ${SKILL_CATALOG_DIRECTORY}/${SKILL_CATALOG_MANIFEST} exists above ${process.cwd()} either. Reinstall @toloka-platform/sdk.`,
|
|
143
142
|
);
|
|
144
|
-
return
|
|
143
|
+
return workingDirectoryRoot;
|
|
145
144
|
}
|
|
146
145
|
|
|
147
146
|
const loadBundledSkillCatalog = async (): Promise<SkillCatalog> =>
|
package/src/importer.ts
CHANGED
|
@@ -220,7 +220,7 @@ function formatSource(value: unknown, depth = 0): string {
|
|
|
220
220
|
return `{\n${properties}\n${indentation(depth)}}`;
|
|
221
221
|
}
|
|
222
222
|
|
|
223
|
-
function
|
|
223
|
+
function transformFieldReference(
|
|
224
224
|
reference: { id: string; optional: boolean },
|
|
225
225
|
fieldReference: FieldReference,
|
|
226
226
|
): unknown {
|
|
@@ -276,10 +276,10 @@ function authoringSettings(node: GraphNode, fieldReference: FieldReference): unk
|
|
|
276
276
|
return {
|
|
277
277
|
...settings,
|
|
278
278
|
started: startedFieldProxies.output.map((reference) =>
|
|
279
|
-
|
|
279
|
+
transformFieldReference(reference, fieldReference),
|
|
280
280
|
),
|
|
281
281
|
completed: completedFieldProxies.input.map((reference) =>
|
|
282
|
-
|
|
282
|
+
transformFieldReference(reference, fieldReference),
|
|
283
283
|
),
|
|
284
284
|
};
|
|
285
285
|
}
|
|
@@ -287,14 +287,12 @@ function authoringSettings(node: GraphNode, fieldReference: FieldReference): unk
|
|
|
287
287
|
const { fieldProxies, ...settings } = node.settings;
|
|
288
288
|
return {
|
|
289
289
|
...settings,
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
),
|
|
297
|
-
},
|
|
290
|
+
input: fieldProxies.input.map((reference) =>
|
|
291
|
+
transformFieldReference(reference, fieldReference),
|
|
292
|
+
),
|
|
293
|
+
output: fieldProxies.output.map((reference) =>
|
|
294
|
+
transformFieldReference(reference, fieldReference),
|
|
295
|
+
),
|
|
298
296
|
};
|
|
299
297
|
}
|
|
300
298
|
case "generation": {
|
|
@@ -322,11 +320,9 @@ function authoringSettings(node: GraphNode, fieldReference: FieldReference): unk
|
|
|
322
320
|
field: fieldReference(fieldProxyId),
|
|
323
321
|
})),
|
|
324
322
|
})),
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
),
|
|
329
|
-
},
|
|
323
|
+
input: fieldProxies.input.map((reference) =>
|
|
324
|
+
transformFieldReference(reference, fieldReference),
|
|
325
|
+
),
|
|
330
326
|
};
|
|
331
327
|
}
|
|
332
328
|
case "quorum": {
|