@toloka-platform/sdk 0.2.0-preview.2 → 0.2.0-preview.3
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 +10 -4
- package/dist/cli/toloka.js +159 -151
- 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 +18 -1
- package/docs/cli-v2.md +4 -3
- package/docs/cli.md +29 -10
- package/docs/getting-started.md +3 -3
- package/docs/limitations.md +1 -1
- package/docs/testing.md +1 -1
- package/docs/troubleshooting.md +1 -1
- package/examples/face-image-collection/README.md +2 -2
- package/openapi/gen/api.ts +78 -0
- package/openapi/gen/models/checkReport.ts +2 -1
- package/openapi/gen/models/checkReportAnyOfTwoseven.ts +5 -0
- package/openapi/gen/models/checkReportAnyOfTwosevenResultsItem.ts +6 -0
- package/openapi/gen/models/checkReportAnyOfTwosevenResultsItemVerdict.ts +2 -0
- package/openapi/gen/models/currentUser.ts +1 -0
- package/openapi/gen/models/deliveryOrder.ts +9 -0
- package/openapi/gen/models/deliveryOrderClientName.ts +2 -0
- package/openapi/gen/models/getTrainingRunMetricHistoryParams.ts +4 -0
- package/openapi/gen/models/index.ts +19 -0
- package/openapi/gen/models/listDeliveryOrders200.ts +5 -0
- package/openapi/gen/models/trainingRunMetricHistory.ts +9 -0
- package/openapi/gen/models/trainingRunMetricHistoryMetric.ts +2 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItem.ts +9 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItemNonFinite.ts +2 -0
- package/openapi/gen/models/trainingRunMetricHistoryPointsItemValue.ts +2 -0
- package/openapi/gen/models/trainingRunMetrics.ts +9 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItem.ts +8 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatest.ts +9 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatestNonFinite.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemLatestValue.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsMetricsItemMetric.ts +2 -0
- package/openapi/gen/models/trainingRunMetricsUnavailableReason.ts +2 -0
- package/package.json +2 -2
- package/skills/catalog.json +55 -7
- package/skills/toloka-build/SKILL.md +10 -7
- package/skills/toloka-build/references/node-semantics.md +6 -4
- package/skills/toloka-build/references/workflow-and-cli.md +21 -13
- package/skills/toloka-fine-tuning/SKILL.md +72 -0
- package/skills/toloka-get-started/SKILL.md +55 -0
- package/skills/toloka-gisting/SKILL.md +76 -0
- package/skills/toloka-report-issue/references/report-template.md +1 -1
- package/src/authoring/index.ts +14 -0
- package/src/checks/task-ui-typecheck.ts +12 -10
- package/src/cli/analytics.ts +52 -6
- package/src/cli/auth.ts +69 -16
- package/src/cli/auto-update.ts +24 -33
- package/src/cli/check.ts +12 -10
- package/src/cli/commands/auth.ts +89 -0
- package/src/cli/commands/config.ts +28 -0
- package/src/cli/commands/dataset.ts +75 -0
- package/src/cli/commands/delivery-order.ts +18 -0
- package/src/cli/commands/models.ts +122 -0
- package/src/cli/commands/pipeline.ts +132 -0
- package/src/cli/commands/project.ts +175 -0
- package/src/cli/commands/report.ts +39 -0
- package/src/cli/commands/secret.ts +39 -0
- package/src/cli/commands/skill.ts +36 -0
- package/src/cli/commands/source.ts +204 -0
- package/src/cli/commands/status.ts +18 -0
- package/src/cli/commands/training.ts +228 -0
- package/src/cli/commands/version.ts +14 -0
- package/src/cli/config.ts +5 -6
- package/src/cli/context.ts +213 -0
- package/src/cli/delivery-orders.ts +30 -0
- package/src/cli/environment.ts +26 -0
- package/src/cli/init.ts +1 -4
- package/src/cli/program.ts +168 -1344
- package/src/cli/project-context.ts +9 -11
- package/src/cli/sdk-version.ts +38 -0
- package/src/cli/skill.ts +6 -2
- package/src/cli/terminal-io.ts +103 -0
- package/src/cli/trainings.ts +118 -27
- package/src/cli/version.ts +1 -1
- package/src/client.ts +18 -0
- package/src/importer.ts +32 -2
package/src/cli/program.ts
CHANGED
|
@@ -1,309 +1,92 @@
|
|
|
1
|
-
import { Command, CommanderError
|
|
2
|
-
import {
|
|
3
|
-
import { writeFile } from "node:fs/promises";
|
|
4
|
-
import { resolve } from "node:path";
|
|
5
|
-
import { createInterface } from "node:readline/promises";
|
|
6
|
-
import { createClient } from "../client";
|
|
7
|
-
import type { PotatoClient } from "../client";
|
|
8
|
-
import { readTolokaState } from "../state";
|
|
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";
|
|
18
|
-
import { authStatus, login, logout } from "./auth";
|
|
19
|
-
import type { AuthPrompt } from "./auth";
|
|
1
|
+
import { Command, CommanderError } from "commander";
|
|
2
|
+
import { createClient, type PotatoClient } from "../client";
|
|
20
3
|
import { commandNameFromArgv, createCliAnalytics } from "./analytics";
|
|
21
4
|
import type { CliAnalytics, CliAnalyticsCommandOutcome } from "./analytics";
|
|
22
5
|
import type { AutoUpdateHooks } from "./auto-update";
|
|
23
6
|
import { maybeAutoUpdate } from "./auto-update";
|
|
24
|
-
import {
|
|
25
|
-
import
|
|
26
|
-
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} from "./
|
|
31
|
-
import
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
} from "./
|
|
40
|
-
import {
|
|
41
|
-
import {
|
|
42
|
-
applyProject,
|
|
43
|
-
importProject,
|
|
44
|
-
planProject,
|
|
45
|
-
projectStatus,
|
|
46
|
-
validateProject,
|
|
47
|
-
} from "./project";
|
|
7
|
+
import { CHECK_RESULT_FILE_ENV, type checkProject } from "./check";
|
|
8
|
+
import { registerAuthCommands } from "./commands/auth";
|
|
9
|
+
import { registerConfigCommands } from "./commands/config";
|
|
10
|
+
import { registerDatasetCommands } from "./commands/dataset";
|
|
11
|
+
import { registerDeliveryOrderCommands } from "./commands/delivery-order";
|
|
12
|
+
import { registerModelCommands } from "./commands/models";
|
|
13
|
+
import { registerPipelineCommands } from "./commands/pipeline";
|
|
14
|
+
import { registerProjectCommands } from "./commands/project";
|
|
15
|
+
import { registerReportCommand } from "./commands/report";
|
|
16
|
+
import { registerSecretCommands } from "./commands/secret";
|
|
17
|
+
import { registerSkillCommands } from "./commands/skill";
|
|
18
|
+
import { registerSourceCommands } from "./commands/source";
|
|
19
|
+
import { registerStatusCommand } from "./commands/status";
|
|
20
|
+
import { registerTrainingCommands } from "./commands/training";
|
|
21
|
+
import { registerVersionCommand } from "./commands/version";
|
|
22
|
+
import { resolveTolokaConfig, type TolokaEnvironment } from "./config";
|
|
23
|
+
import { findCommandCredentials } from "./credentials";
|
|
48
24
|
import {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
} from "./models";
|
|
59
|
-
import {
|
|
60
|
-
MISSING_PROJECT_ID_MESSAGE,
|
|
61
|
-
addProject,
|
|
62
|
-
createProject,
|
|
63
|
-
currentProject,
|
|
64
|
-
listRemoteProjects,
|
|
65
|
-
removeProject,
|
|
66
|
-
resolveProjectContext,
|
|
67
|
-
switchProject,
|
|
68
|
-
untrackProjectResource,
|
|
69
|
-
} from "./project-context";
|
|
70
|
-
import { estimatePipelineCost } from "./cost";
|
|
71
|
-
import { submitIssueReport } from "./report";
|
|
72
|
-
import type { IssueReportAuthor } from "./report";
|
|
25
|
+
CREDENTIAL_PRECEDENCE,
|
|
26
|
+
resolveInternalUser,
|
|
27
|
+
resolveSourceRoot,
|
|
28
|
+
type CliIo,
|
|
29
|
+
type CreatedProgram,
|
|
30
|
+
type GlobalOptions,
|
|
31
|
+
type ProgramContext,
|
|
32
|
+
} from "./context";
|
|
33
|
+
import { normalizeEnvironment } from "./environment";
|
|
73
34
|
import { formatCliError } from "./format-error";
|
|
74
|
-
import {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
createTrainingRun,
|
|
87
|
-
estimateTrainingCost,
|
|
88
|
-
getTraining,
|
|
89
|
-
getTrainingRun,
|
|
90
|
-
readTrainingRunLogs,
|
|
91
|
-
listTrainingRuns,
|
|
92
|
-
resumeTrainingRun,
|
|
93
|
-
listTrainings,
|
|
94
|
-
updateTraining,
|
|
95
|
-
} from "./trainings";
|
|
96
|
-
import type { TrainingDraftOptions } from "./trainings";
|
|
97
|
-
|
|
98
|
-
import type { RunOutcome } from "./runs";
|
|
99
|
-
import { skillInstallCommand, skillListCommand, skillSearchCommand } from "./skill";
|
|
100
|
-
import { cliVersionInfo } from "./version";
|
|
101
|
-
import { findSourceRoot } from "./source";
|
|
102
|
-
|
|
103
|
-
export type CliIo = {
|
|
104
|
-
readonly stdout: (text: string) => void;
|
|
105
|
-
readonly stderr: (text: string) => void;
|
|
106
|
-
readonly readStdin: () => Promise<string>;
|
|
107
|
-
readonly prompt?: AuthPrompt;
|
|
108
|
-
readonly select?: (choices: readonly { name: string; value: string }[]) => Promise<string>;
|
|
109
|
-
};
|
|
110
|
-
|
|
111
|
-
export type ProgramContext = {
|
|
112
|
-
readonly environment: TolokaEnvironment;
|
|
113
|
-
readonly io: CliIo;
|
|
114
|
-
readonly injectedClient?: PotatoClient;
|
|
115
|
-
readonly runChecks?: typeof checkProject;
|
|
116
|
-
readonly analytics?: CliAnalytics;
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
type GlobalOptions = {
|
|
120
|
-
readonly sourceRoot?: string;
|
|
121
|
-
readonly apiKey?: string;
|
|
122
|
-
readonly baseUrl?: string;
|
|
123
|
-
readonly projectId?: string;
|
|
35
|
+
import { readAgentSkillStalenessWarning } from "./init";
|
|
36
|
+
import { SDK_VERSION } from "./sdk-version";
|
|
37
|
+
import { terminalIo } from "./terminal-io";
|
|
38
|
+
|
|
39
|
+
export type { CliIo, CreatedProgram, ProgramContext } from "./context";
|
|
40
|
+
export { terminalIo } from "./terminal-io";
|
|
41
|
+
|
|
42
|
+
const argvFlag = (argv: readonly string[], flag: string): string | undefined => {
|
|
43
|
+
const index = argv.indexOf(flag);
|
|
44
|
+
if (index < 0) return undefined;
|
|
45
|
+
const value = argv[index + 1];
|
|
46
|
+
return value === undefined || value.startsWith("-") ? undefined : value;
|
|
124
47
|
};
|
|
125
48
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
const positiveInteger = (raw: string): number => {
|
|
141
|
-
const value = Number(raw);
|
|
142
|
-
if (!Number.isInteger(value) || value <= 0)
|
|
143
|
-
throw new InvalidArgumentError("Expected a positive integer.");
|
|
144
|
-
return value;
|
|
145
|
-
};
|
|
146
|
-
|
|
147
|
-
const nonNegativeInteger = (raw: string): number => {
|
|
148
|
-
const value = Number(raw);
|
|
149
|
-
if (!Number.isInteger(value) || value < 0)
|
|
150
|
-
throw new InvalidArgumentError("Expected a non-negative integer.");
|
|
151
|
-
return value;
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const trainingMethod = (raw: string): "sft" | "gisting" => {
|
|
155
|
-
if (raw !== "sft" && raw !== "gisting") {
|
|
156
|
-
throw new InvalidArgumentError('Expected training method "sft" or "gisting".');
|
|
157
|
-
}
|
|
158
|
-
return raw;
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const trainingRegime = (raw: string): "low" | "medium" | "high" => {
|
|
162
|
-
if (raw !== "low" && raw !== "medium" && raw !== "high") {
|
|
163
|
-
throw new InvalidArgumentError('Expected regime "low", "medium", or "high".');
|
|
49
|
+
const resolveInternalCommandAccess = async (
|
|
50
|
+
argv: readonly string[],
|
|
51
|
+
environment: TolokaEnvironment,
|
|
52
|
+
injectedClient: PotatoClient | undefined,
|
|
53
|
+
): Promise<boolean | undefined> => {
|
|
54
|
+
const commandName = commandNameFromArgv(argv);
|
|
55
|
+
if (
|
|
56
|
+
commandName !== "help" &&
|
|
57
|
+
commandName !== "project create" &&
|
|
58
|
+
!commandName.startsWith("delivery-order")
|
|
59
|
+
) {
|
|
60
|
+
return undefined;
|
|
164
61
|
}
|
|
165
|
-
return
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
type RawTrainingDraftOptions = {
|
|
169
|
-
method?: TrainingDraftOptions["method"];
|
|
170
|
-
baseModel?: TrainingDraftOptions["baseModelId"];
|
|
171
|
-
modelVersion?: string;
|
|
172
|
-
dataset?: string;
|
|
173
|
-
validationDataset?: string;
|
|
174
|
-
trainFilter?: string;
|
|
175
|
-
validationFilter?: string;
|
|
176
|
-
regime?: TrainingDraftOptions["regime"];
|
|
177
|
-
epochs?: number;
|
|
178
|
-
evalSteps?: number;
|
|
179
|
-
promptFile?: string;
|
|
180
|
-
};
|
|
181
|
-
|
|
182
|
-
const trainingDraftOptions = (options: RawTrainingDraftOptions): TrainingDraftOptions => ({
|
|
183
|
-
method: options.method,
|
|
184
|
-
baseModelId: options.baseModel,
|
|
185
|
-
modelVersionId: options.modelVersion,
|
|
186
|
-
datasetId: options.dataset,
|
|
187
|
-
validationDatasetId: options.validationDataset,
|
|
188
|
-
trainingFilter: options.trainFilter,
|
|
189
|
-
validationFilter: options.validationFilter,
|
|
190
|
-
regime: options.regime,
|
|
191
|
-
epochs: options.epochs,
|
|
192
|
-
evalSteps: options.evalSteps,
|
|
193
|
-
promptFilePath: options.promptFile,
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
const exitCodeForOutcome = (outcome: RunOutcome): number => {
|
|
197
|
-
if (outcome === "failed") return 1;
|
|
198
|
-
if (outcome === "timedOut") return 2;
|
|
199
|
-
return 0;
|
|
200
|
-
};
|
|
201
|
-
|
|
202
|
-
type Resolved = {
|
|
203
|
-
readonly sourceRoot: string;
|
|
204
|
-
readonly client: PotatoClient;
|
|
205
|
-
readonly baseUrl: string;
|
|
206
|
-
readonly state: TolokaState | undefined;
|
|
207
|
-
readonly stored: StoredCredentials | undefined;
|
|
208
|
-
readonly projectId?: string;
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
const resolveSourceRoot = async (globals: GlobalOptions, io: CliIo): Promise<string> => {
|
|
212
|
-
if (globals.sourceRoot !== undefined) return resolve(globals.sourceRoot);
|
|
213
|
-
|
|
214
|
-
const workingDirectory = process.cwd();
|
|
215
|
-
const discovered = await findSourceRoot(workingDirectory);
|
|
216
|
-
if (discovered === undefined || discovered === workingDirectory) return workingDirectory;
|
|
217
|
-
|
|
218
|
-
io.stderr(`Using project root ${discovered} (found from ${workingDirectory}).`);
|
|
219
|
-
return discovered;
|
|
220
|
-
};
|
|
221
|
-
|
|
222
|
-
const resolveInitRoot = (globals: GlobalOptions): string =>
|
|
223
|
-
globals.sourceRoot === undefined ? process.cwd() : resolve(globals.sourceRoot);
|
|
224
|
-
|
|
225
|
-
const readCommandCredentials = async (globals: GlobalOptions, context: ProgramContext) => {
|
|
226
|
-
const { found, warning } = await findCommandCredentials(
|
|
227
|
-
context.environment,
|
|
228
|
-
globals.apiKey ?? context.environment.POTATO_API_KEY,
|
|
229
|
-
);
|
|
230
|
-
if (warning !== undefined) context.io.stderr(warning);
|
|
231
|
-
return found?.credentials;
|
|
232
|
-
};
|
|
233
|
-
|
|
234
|
-
const identifyClientUser = async (client: PotatoClient, analytics: CliAnalytics | undefined) => {
|
|
235
|
-
if (analytics?.shouldIdentify !== true) return;
|
|
62
|
+
if (injectedClient !== undefined) return resolveInternalUser(injectedClient);
|
|
236
63
|
try {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
stored,
|
|
259
|
-
...(config.projectId === undefined ? {} : { projectId: config.projectId }),
|
|
260
|
-
};
|
|
261
|
-
};
|
|
262
|
-
|
|
263
|
-
const resolveSelectedProject = (
|
|
264
|
-
globals: GlobalOptions,
|
|
265
|
-
context: ProgramContext,
|
|
266
|
-
stored: StoredCredentials | undefined,
|
|
267
|
-
state: TolokaState | undefined,
|
|
268
|
-
) =>
|
|
269
|
-
resolveProjectContext({
|
|
270
|
-
flags: globals,
|
|
271
|
-
environment: context.environment,
|
|
272
|
-
stored,
|
|
273
|
-
...(state === undefined ? {} : { state }),
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
export type CreatedProgram = {
|
|
277
|
-
readonly program: Command;
|
|
278
|
-
readonly exitCode: { value: number };
|
|
64
|
+
const apiKey = argvFlag(argv, "--api-key");
|
|
65
|
+
const baseUrl = argvFlag(argv, "--base-url");
|
|
66
|
+
const { found } = await findCommandCredentials(
|
|
67
|
+
environment,
|
|
68
|
+
apiKey ?? environment.TOLOKA_API_KEY,
|
|
69
|
+
);
|
|
70
|
+
const config = resolveTolokaConfig(
|
|
71
|
+
{
|
|
72
|
+
...(apiKey === undefined ? {} : { apiKey }),
|
|
73
|
+
...(baseUrl === undefined ? {} : { baseUrl }),
|
|
74
|
+
},
|
|
75
|
+
undefined,
|
|
76
|
+
environment,
|
|
77
|
+
found?.credentials,
|
|
78
|
+
);
|
|
79
|
+
return await resolveInternalUser(
|
|
80
|
+
createClient({ apiKey: config.apiKey, baseUrl: config.baseUrl }),
|
|
81
|
+
);
|
|
82
|
+
} catch {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
279
85
|
};
|
|
280
86
|
|
|
281
87
|
export function createProgram(context: ProgramContext): CreatedProgram {
|
|
282
88
|
const program = new Command("toloka");
|
|
283
89
|
const exitCode = { value: 0 };
|
|
284
|
-
const runChecks = context.runChecks ?? checkProject;
|
|
285
|
-
const runChecksInterruptibly = async (
|
|
286
|
-
options: CheckProjectOptions,
|
|
287
|
-
runner: (options: CheckProjectOptions) => Promise<CheckProjectResult> = runChecks,
|
|
288
|
-
): Promise<{ result: CheckProjectResult; interruptedExitCode?: number }> => {
|
|
289
|
-
const abortController = new AbortController();
|
|
290
|
-
let interruptedExitCode: number | undefined;
|
|
291
|
-
const interrupt = (exitCode: number) => {
|
|
292
|
-
interruptedExitCode = exitCode;
|
|
293
|
-
abortController.abort();
|
|
294
|
-
};
|
|
295
|
-
const interruptSigint = () => interrupt(130);
|
|
296
|
-
const interruptSigterm = () => interrupt(143);
|
|
297
|
-
process.on("SIGINT", interruptSigint);
|
|
298
|
-
process.on("SIGTERM", interruptSigterm);
|
|
299
|
-
try {
|
|
300
|
-
const result = await runner({ ...options, signal: abortController.signal });
|
|
301
|
-
return { result, ...(interruptedExitCode === undefined ? {} : { interruptedExitCode }) };
|
|
302
|
-
} finally {
|
|
303
|
-
process.off("SIGINT", interruptSigint);
|
|
304
|
-
process.off("SIGTERM", interruptSigterm);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
90
|
let warnedAboutStaleSkills = false;
|
|
308
91
|
|
|
309
92
|
program
|
|
@@ -326,6 +109,7 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
326
109
|
.exitOverride();
|
|
327
110
|
|
|
328
111
|
const globals = (): GlobalOptions => program.opts<GlobalOptions>();
|
|
112
|
+
const host = { context, exitCode, globals };
|
|
329
113
|
|
|
330
114
|
program.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
331
115
|
if (warnedAboutStaleSkills) return;
|
|
@@ -349,1051 +133,52 @@ export function createProgram(context: ProgramContext): CreatedProgram {
|
|
|
349
133
|
context.io.stderr(warning);
|
|
350
134
|
});
|
|
351
135
|
|
|
352
|
-
program
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const settings = await readCliSettings(context.environment);
|
|
367
|
-
context.io.stdout(getCliSetting(settings, name).output);
|
|
368
|
-
});
|
|
369
|
-
config
|
|
370
|
-
.command("set")
|
|
371
|
-
.argument("<name>", "setting name")
|
|
372
|
-
.argument("<value>", "setting value")
|
|
373
|
-
.description("write a CLI setting for this machine")
|
|
374
|
-
.action(async (name: string, value: string) => {
|
|
375
|
-
const result = await setCliSetting(context.environment, name, value);
|
|
376
|
-
context.io.stdout(result.output);
|
|
377
|
-
});
|
|
378
|
-
config.action(async () => {
|
|
379
|
-
const settings = await readCliSettings(context.environment);
|
|
380
|
-
context.io.stdout(getCliSetting(settings, undefined).output);
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
program
|
|
384
|
-
.command("login")
|
|
385
|
-
.description("store an API key for this machine after verifying it")
|
|
386
|
-
.action(async () => {
|
|
387
|
-
const result = await login({
|
|
388
|
-
...(globals().apiKey === undefined ? {} : { apiKey: globals().apiKey }),
|
|
389
|
-
...(globals().baseUrl === undefined ? {} : { baseUrl: globals().baseUrl }),
|
|
390
|
-
...(context.io.prompt === undefined ? {} : { prompt: context.io.prompt }),
|
|
391
|
-
write: context.io.stdout,
|
|
392
|
-
environment: context.environment,
|
|
393
|
-
});
|
|
394
|
-
context.io.stdout(result.output);
|
|
395
|
-
try {
|
|
396
|
-
const stored = await readStoredCredentials(context.environment);
|
|
397
|
-
if (stored === undefined) return;
|
|
398
|
-
const client = context.injectedClient ?? createClient(stored);
|
|
399
|
-
await identifyClientUser(client, context.analytics);
|
|
400
|
-
} catch {}
|
|
401
|
-
});
|
|
402
|
-
|
|
403
|
-
program
|
|
404
|
-
.command("logout")
|
|
405
|
-
.description("remove stored credentials from this machine")
|
|
406
|
-
.action(async () => {
|
|
407
|
-
const result = await logout(context.environment);
|
|
408
|
-
context.io.stdout(result.output);
|
|
409
|
-
});
|
|
410
|
-
|
|
411
|
-
program
|
|
412
|
-
.command("auth-status")
|
|
413
|
-
.alias("whoami")
|
|
414
|
-
.description("show which platform and key the CLI would use")
|
|
415
|
-
.option("--show-token", "print the API key instead of masking it", false)
|
|
416
|
-
.action(async (options: { showToken: boolean }) => {
|
|
417
|
-
const flags = globals();
|
|
418
|
-
const credentialLookup = await findCommandCredentials(
|
|
419
|
-
context.environment,
|
|
420
|
-
flags.apiKey ?? context.environment.POTATO_API_KEY,
|
|
421
|
-
);
|
|
422
|
-
const stored = credentialLookup.found?.credentials;
|
|
423
|
-
const sourceRoot = await resolveSourceRoot(flags, context.io);
|
|
424
|
-
const state = await readTolokaState(sourceRoot);
|
|
425
|
-
const project = resolveSelectedProject(flags, context, stored, state);
|
|
426
|
-
const injectedClient = context.injectedClient;
|
|
427
|
-
|
|
428
|
-
const result = await authStatus({
|
|
429
|
-
environment: context.environment,
|
|
430
|
-
showToken: options.showToken,
|
|
431
|
-
credentialLookup,
|
|
432
|
-
...(flags.apiKey === undefined ? {} : { flagApiKey: flags.apiKey }),
|
|
433
|
-
...(flags.baseUrl === undefined ? {} : { flagBaseUrl: flags.baseUrl }),
|
|
434
|
-
...(injectedClient === undefined ? {} : { probe: () => injectedClient }),
|
|
435
|
-
...(project === undefined ? {} : { project }),
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
context.io.stdout(result.output);
|
|
439
|
-
if (!result.authenticated) {
|
|
440
|
-
exitCode.value = 1;
|
|
441
|
-
return;
|
|
442
|
-
}
|
|
443
|
-
try {
|
|
444
|
-
const config = resolveTolokaConfig(flags, state, context.environment, stored);
|
|
445
|
-
const client =
|
|
446
|
-
context.injectedClient ??
|
|
447
|
-
createClient({ apiKey: config.apiKey, baseUrl: config.baseUrl });
|
|
448
|
-
await identifyClientUser(client, context.analytics);
|
|
449
|
-
} catch {}
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
const project = program
|
|
453
|
-
.command("project")
|
|
454
|
-
.description("select and manage the current platform project");
|
|
455
|
-
project
|
|
456
|
-
.command("list")
|
|
457
|
-
.option("--limit <n>", "maximum projects to list", positiveInteger)
|
|
458
|
-
.description("list remote projects this key can see")
|
|
459
|
-
.action(async (options: { limit?: number }) => {
|
|
460
|
-
const flags = globals();
|
|
461
|
-
const { client, stored, state } = await resolveContext(flags, context);
|
|
462
|
-
const selected = resolveSelectedProject(flags, context, stored, state);
|
|
463
|
-
await listRemoteProjects({
|
|
464
|
-
client,
|
|
465
|
-
io: context.io,
|
|
466
|
-
...(selected === undefined ? {} : { context: selected }),
|
|
467
|
-
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
468
|
-
});
|
|
469
|
-
});
|
|
470
|
-
project
|
|
471
|
-
.command("add")
|
|
472
|
-
.argument("[alias]", "local name for this project")
|
|
473
|
-
.argument("[projectId]", "platform project id")
|
|
474
|
-
.description("register a live project and select it")
|
|
475
|
-
.action(async (alias?: string, projectId?: string) => {
|
|
476
|
-
const flags = globals();
|
|
477
|
-
const found = await requireStoredCredentials(context.environment);
|
|
478
|
-
const client = context.injectedClient ?? createClient(found.credentials);
|
|
479
|
-
await addProject({
|
|
480
|
-
environment: context.environment,
|
|
481
|
-
flags,
|
|
482
|
-
client,
|
|
483
|
-
io: context.io,
|
|
484
|
-
afterPreflight: async () => identifyClientUser(client, context.analytics),
|
|
485
|
-
...(alias === undefined ? {} : { alias }),
|
|
486
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
487
|
-
});
|
|
488
|
-
});
|
|
489
|
-
|
|
490
|
-
project
|
|
491
|
-
.command("switch")
|
|
492
|
-
.argument("<alias>", "local project alias")
|
|
493
|
-
.description("select a registered project")
|
|
494
|
-
.action(async (alias: string) => {
|
|
495
|
-
await switchProject({
|
|
496
|
-
environment: context.environment,
|
|
497
|
-
flags: globals(),
|
|
498
|
-
alias,
|
|
499
|
-
io: context.io,
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
project
|
|
503
|
-
.command("current")
|
|
504
|
-
.description("show the project this command would use")
|
|
505
|
-
.action(async () => {
|
|
506
|
-
const flags = globals();
|
|
507
|
-
const stored = await readCommandCredentials(flags, context);
|
|
508
|
-
const sourceRoot = await resolveSourceRoot(flags, context.io);
|
|
509
|
-
const state = await readTolokaState(sourceRoot);
|
|
510
|
-
const result = currentProject({
|
|
511
|
-
flags,
|
|
512
|
-
environment: context.environment,
|
|
513
|
-
stored,
|
|
514
|
-
...(state === undefined ? {} : { state }),
|
|
515
|
-
io: context.io,
|
|
516
|
-
});
|
|
517
|
-
if (result.project === undefined) exitCode.value = 1;
|
|
518
|
-
});
|
|
519
|
-
project
|
|
520
|
-
.command("url")
|
|
521
|
-
.description("print the current project URL")
|
|
522
|
-
.action(async () => {
|
|
523
|
-
const { baseUrl, client, projectId } = await resolveContext(globals(), context);
|
|
524
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
525
|
-
const live = await client.projects.get({ projectId });
|
|
526
|
-
const url = new URL(
|
|
527
|
-
`/teams/${encodeURIComponent(live.teamId)}/projects/${encodeURIComponent(live.id)}`,
|
|
528
|
-
baseUrl,
|
|
529
|
-
);
|
|
530
|
-
context.io.stdout(url.href);
|
|
531
|
-
});
|
|
532
|
-
|
|
533
|
-
project
|
|
534
|
-
.command("remove")
|
|
535
|
-
.argument("<alias>", "local project alias")
|
|
536
|
-
.description("forget a local project alias")
|
|
537
|
-
.action(async (alias: string) => {
|
|
538
|
-
await removeProject({
|
|
539
|
-
environment: context.environment,
|
|
540
|
-
flags: globals(),
|
|
541
|
-
alias,
|
|
542
|
-
io: context.io,
|
|
543
|
-
});
|
|
544
|
-
});
|
|
545
|
-
project
|
|
546
|
-
.command("untrack")
|
|
547
|
-
.argument("<type>", "pipeline or dataset")
|
|
548
|
-
.argument("<name>", "tracked resource name")
|
|
549
|
-
.option("--confirm", "confirm untracking in a non-interactive environment", false)
|
|
550
|
-
.description("remove a pipeline or dataset from local tracking without deleting it")
|
|
551
|
-
.action(async (type: string, name: string, options: { confirm: boolean }) => {
|
|
552
|
-
assert(
|
|
553
|
-
type === "pipeline" || type === "dataset",
|
|
554
|
-
"Resource type must be pipeline or dataset.",
|
|
555
|
-
);
|
|
556
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
557
|
-
await untrackProjectResource({
|
|
558
|
-
sourceRoot,
|
|
559
|
-
type,
|
|
560
|
-
name,
|
|
561
|
-
confirm: options.confirm,
|
|
562
|
-
io: context.io,
|
|
563
|
-
});
|
|
564
|
-
});
|
|
565
|
-
project
|
|
566
|
-
.command("create")
|
|
567
|
-
.argument("[alias]", "local name for the new project")
|
|
568
|
-
.option("--name <name>", "platform project name")
|
|
569
|
-
.option("--team-id <id>", "team that owns the project")
|
|
570
|
-
.description("create a live project and select it")
|
|
571
|
-
.action(async (alias: string | undefined, options: { name?: string; teamId?: string }) => {
|
|
572
|
-
const flags = globals();
|
|
573
|
-
const found = await requireStoredCredentials(context.environment);
|
|
574
|
-
const client = context.injectedClient ?? createClient(found.credentials);
|
|
575
|
-
const sourceRoot = await resolveSourceRoot(flags, context.io);
|
|
576
|
-
await createProject({
|
|
577
|
-
environment: context.environment,
|
|
578
|
-
flags,
|
|
579
|
-
client,
|
|
580
|
-
io: context.io,
|
|
581
|
-
sourceRoot,
|
|
582
|
-
afterPreflight: async () => identifyClientUser(client, context.analytics),
|
|
583
|
-
...(alias === undefined ? {} : { alias }),
|
|
584
|
-
...(options.name === undefined ? {} : { name: options.name }),
|
|
585
|
-
...(options.teamId === undefined ? {} : { teamId: options.teamId }),
|
|
586
|
-
});
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
program
|
|
590
|
-
.command("init")
|
|
591
|
-
.description("initialize this project directory and install the agent bootstrap")
|
|
592
|
-
.action(async () => {
|
|
593
|
-
const flags = globals();
|
|
594
|
-
const sourceRoot = resolveInitRoot(flags);
|
|
595
|
-
const state = await readTolokaState(sourceRoot);
|
|
596
|
-
const selected = resolveProjectContext({
|
|
597
|
-
flags,
|
|
598
|
-
environment: context.environment,
|
|
599
|
-
...(state === undefined ? {} : { state }),
|
|
600
|
-
});
|
|
601
|
-
const result = await initProject(
|
|
602
|
-
sourceRoot,
|
|
603
|
-
selected === undefined ? {} : { projectId: selected.id },
|
|
604
|
-
);
|
|
605
|
-
context.io.stdout(result.output);
|
|
606
|
-
if (result.warning !== undefined) context.io.stderr(result.warning);
|
|
607
|
-
});
|
|
608
|
-
|
|
609
|
-
program
|
|
610
|
-
.command("import")
|
|
611
|
-
.argument(
|
|
612
|
-
"[projectId]",
|
|
613
|
-
"existing project to write into source; defaults to the selected project",
|
|
614
|
-
)
|
|
615
|
-
.option("-f, --force", "replace an existing pipelines/ directory", false)
|
|
616
|
-
.description("generate TypeScript source from a live project")
|
|
617
|
-
.action(async (projectId?: string, options?: { force?: boolean }) => {
|
|
618
|
-
const {
|
|
619
|
-
sourceRoot,
|
|
620
|
-
client,
|
|
621
|
-
projectId: selectedProjectId,
|
|
622
|
-
} = await resolveContext(globals(), context);
|
|
623
|
-
const id = projectId ?? selectedProjectId;
|
|
624
|
-
assert(id, MISSING_PROJECT_ID_MESSAGE);
|
|
625
|
-
const result = await importProject(client, {
|
|
626
|
-
sourceRoot,
|
|
627
|
-
projectId: id,
|
|
628
|
-
...(options?.force === true ? { force: true } : {}),
|
|
629
|
-
});
|
|
630
|
-
context.io.stdout(result.output);
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
program
|
|
634
|
-
.command("plan")
|
|
635
|
-
.description("show what apply would change")
|
|
636
|
-
.action(async () => {
|
|
637
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
638
|
-
const result = await planProject(client, {
|
|
639
|
-
sourceRoot,
|
|
640
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
641
|
-
});
|
|
642
|
-
context.io.stdout(result.output);
|
|
643
|
-
});
|
|
644
|
-
|
|
645
|
-
program
|
|
646
|
-
.command("validate")
|
|
647
|
-
.description("run platform spec validation and report matching checks")
|
|
648
|
-
.action(async () => {
|
|
649
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
650
|
-
const result = await validateProject(client, {
|
|
651
|
-
sourceRoot,
|
|
652
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
653
|
-
});
|
|
654
|
-
context.io.stdout(result.output);
|
|
655
|
-
if (!result.valid) exitCode.value = 1;
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
program
|
|
659
|
-
.command("check")
|
|
660
|
-
.argument("[pipeline]", "pipeline source directory or name")
|
|
661
|
-
.option("--node <node>", "check one code or human-work node by name or id")
|
|
662
|
-
.option("--json", "print the structured check report", false)
|
|
663
|
-
.option("--watch", "rerun checks when project files change", false)
|
|
664
|
-
.description("run the complete local source, type, and test gate")
|
|
665
|
-
.action(
|
|
666
|
-
async (
|
|
667
|
-
pipeline: string | undefined,
|
|
668
|
-
options: { node?: string; json: boolean; watch: boolean },
|
|
669
|
-
) => {
|
|
670
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
671
|
-
const checkOptions = {
|
|
672
|
-
environment: context.environment,
|
|
673
|
-
sourceRoot,
|
|
674
|
-
...(pipeline === undefined ? {} : { pipeline }),
|
|
675
|
-
...(options.node === undefined ? {} : { node: options.node }),
|
|
676
|
-
...(options.json ? { json: true } : {}),
|
|
677
|
-
};
|
|
678
|
-
const writeCheckOutput = async (output: string): Promise<void> => {
|
|
679
|
-
const resultPath = context.environment[CHECK_RESULT_FILE_ENV];
|
|
680
|
-
if (resultPath === undefined) context.io.stdout(output);
|
|
681
|
-
else await writeFile(resultPath, output, "utf8");
|
|
682
|
-
};
|
|
683
|
-
const checkRunner =
|
|
684
|
-
context.runChecks === undefined &&
|
|
685
|
-
context.environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
686
|
-
? runFreshCheck
|
|
687
|
-
: runChecks;
|
|
688
|
-
if (options.watch) {
|
|
689
|
-
const abortController = new AbortController();
|
|
690
|
-
const stop = () => abortController.abort();
|
|
691
|
-
process.on("SIGINT", stop);
|
|
692
|
-
process.on("SIGTERM", stop);
|
|
693
|
-
try {
|
|
694
|
-
await watchProjectChecks(
|
|
695
|
-
checkOptions,
|
|
696
|
-
async (result) => {
|
|
697
|
-
await writeCheckOutput(result.output);
|
|
698
|
-
exitCode.value = result.exitCode;
|
|
699
|
-
},
|
|
700
|
-
undefined,
|
|
701
|
-
abortController.signal,
|
|
702
|
-
);
|
|
703
|
-
} finally {
|
|
704
|
-
process.off("SIGINT", stop);
|
|
705
|
-
process.off("SIGTERM", stop);
|
|
706
|
-
}
|
|
707
|
-
return;
|
|
708
|
-
}
|
|
709
|
-
const { result, interruptedExitCode } = await runChecksInterruptibly(
|
|
710
|
-
checkOptions,
|
|
711
|
-
checkRunner,
|
|
712
|
-
);
|
|
713
|
-
await writeCheckOutput(result.output);
|
|
714
|
-
exitCode.value = interruptedExitCode ?? result.exitCode;
|
|
715
|
-
},
|
|
716
|
-
);
|
|
717
|
-
|
|
718
|
-
program
|
|
719
|
-
.command("apply")
|
|
720
|
-
.description("check source, then make the platform match it")
|
|
721
|
-
.action(async () => {
|
|
722
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
723
|
-
const { result: checked, interruptedExitCode } = await runChecksInterruptibly({
|
|
724
|
-
sourceRoot,
|
|
725
|
-
environment: context.environment,
|
|
726
|
-
});
|
|
727
|
-
context.io.stdout(checked.output);
|
|
728
|
-
if (interruptedExitCode !== undefined) {
|
|
729
|
-
exitCode.value = interruptedExitCode;
|
|
730
|
-
return;
|
|
731
|
-
}
|
|
732
|
-
if (checked.exitCode !== 0) {
|
|
733
|
-
exitCode.value = checked.exitCode;
|
|
734
|
-
return;
|
|
735
|
-
}
|
|
736
|
-
assert(checked.source, "Passing checks must return project source");
|
|
737
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
738
|
-
const result = await applyProject(client, {
|
|
739
|
-
sourceRoot,
|
|
740
|
-
source: checked.source,
|
|
741
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
742
|
-
});
|
|
743
|
-
context.io.stdout(result.output);
|
|
744
|
-
});
|
|
745
|
-
|
|
746
|
-
const secret = program.command("secret").description("manage project secrets");
|
|
747
|
-
secret
|
|
748
|
-
.command("set")
|
|
749
|
-
.argument("<name>", "secret name referenced by a code node")
|
|
750
|
-
.option(
|
|
751
|
-
"--from-env <variable>",
|
|
752
|
-
"read the value from this environment variable instead of stdin",
|
|
753
|
-
)
|
|
754
|
-
.description("set a secret value without writing it to disk")
|
|
755
|
-
.action(async (name: string, options: { fromEnv?: string }) => {
|
|
756
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
757
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
758
|
-
|
|
759
|
-
const value =
|
|
760
|
-
options.fromEnv === undefined
|
|
761
|
-
? (await context.io.readStdin()).replace(/\r?\n$/u, "")
|
|
762
|
-
: context.environment[options.fromEnv];
|
|
763
|
-
assert(
|
|
764
|
-
value !== undefined,
|
|
765
|
-
`Missing secret value. Set ${options.fromEnv ?? "the named environment variable"} or pipe it to stdin.`,
|
|
766
|
-
);
|
|
767
|
-
|
|
768
|
-
try {
|
|
769
|
-
const result = await setProjectSecret(client, { projectId, name, value });
|
|
770
|
-
context.io.stdout(result.output);
|
|
771
|
-
} catch {
|
|
772
|
-
context.io.stderr(`Failed to set secret ${name}.`);
|
|
773
|
-
exitCode.value = 1;
|
|
774
|
-
}
|
|
775
|
-
});
|
|
776
|
-
|
|
777
|
-
const baseModel = program
|
|
778
|
-
.command("base-model")
|
|
779
|
-
.description("inspect the global base model catalog");
|
|
780
|
-
baseModel
|
|
781
|
-
.command("list")
|
|
782
|
-
.option("--json", "print JSON", false)
|
|
783
|
-
.description("list base models")
|
|
784
|
-
.action(async (options: { json: boolean }) => {
|
|
785
|
-
const { client } = await resolveContext(globals(), context);
|
|
786
|
-
await listBaseModels(client, { json: options.json }, context.io);
|
|
787
|
-
});
|
|
788
|
-
baseModel
|
|
789
|
-
.command("get")
|
|
790
|
-
.argument("<id>", "base model ID")
|
|
791
|
-
.option("--json", "print JSON", false)
|
|
792
|
-
.description("show one base model")
|
|
793
|
-
.action(async (id: string, options: { json: boolean }) => {
|
|
794
|
-
const { client } = await resolveContext(globals(), context);
|
|
795
|
-
await getBaseModel(client, { id: parseBaseModelId(id), json: options.json }, context.io);
|
|
796
|
-
});
|
|
797
|
-
|
|
798
|
-
const model = program
|
|
799
|
-
.command("model")
|
|
800
|
-
.description("manage project Models and immutable versions");
|
|
801
|
-
model
|
|
802
|
-
.command("list")
|
|
803
|
-
.option("--json", "print JSON", false)
|
|
804
|
-
.description("list Models owned by the selected project")
|
|
805
|
-
.action(async (options: { json: boolean }) => {
|
|
806
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
807
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
808
|
-
await listModels(client, { projectId, json: options.json }, context.io);
|
|
809
|
-
});
|
|
810
|
-
model
|
|
811
|
-
.command("get")
|
|
812
|
-
.argument("<name-or-id>", "project Model name or ID")
|
|
813
|
-
.option("--json", "print JSON", false)
|
|
814
|
-
.description("show one project Model")
|
|
815
|
-
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
816
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
817
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
818
|
-
await getModel(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
819
|
-
});
|
|
820
|
-
model
|
|
821
|
-
.command("versions")
|
|
822
|
-
.argument("<name-or-id>", "project Model name or ID")
|
|
823
|
-
.option("--json", "print JSON", false)
|
|
824
|
-
.description("list immutable versions of a model")
|
|
825
|
-
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
826
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
827
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
828
|
-
await listModelVersions(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
829
|
-
});
|
|
830
|
-
model
|
|
831
|
-
.command("create")
|
|
832
|
-
.argument("<name>", "new Model name")
|
|
833
|
-
.requiredOption("--base <id>", "base model ID")
|
|
834
|
-
.option("--description <text>", "Model description")
|
|
835
|
-
.option("--json", "print JSON", false)
|
|
836
|
-
.description("create project Model metadata without a Model Version")
|
|
837
|
-
.action(
|
|
838
|
-
async (name: string, options: { base: string; description?: string; json: boolean }) => {
|
|
839
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
840
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
841
|
-
await createModel(
|
|
842
|
-
client,
|
|
843
|
-
{
|
|
844
|
-
projectId,
|
|
845
|
-
name,
|
|
846
|
-
description: options.description,
|
|
847
|
-
baseModelId: parseBaseModelId(options.base),
|
|
848
|
-
json: options.json,
|
|
849
|
-
},
|
|
850
|
-
context.io,
|
|
851
|
-
);
|
|
852
|
-
},
|
|
853
|
-
);
|
|
854
|
-
model
|
|
855
|
-
.command("archive")
|
|
856
|
-
.option("--json", "print JSON", false)
|
|
857
|
-
.argument("<name-or-id>", "project Model name or ID")
|
|
858
|
-
.description("archive a project Model")
|
|
859
|
-
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
860
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
861
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
862
|
-
await archiveModel(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
const modelVersion = model.command("version").description("manage one immutable Model Version");
|
|
866
|
-
modelVersion
|
|
867
|
-
.command("download")
|
|
868
|
-
.argument("<model-version>", "Model Version ID or Model@name")
|
|
869
|
-
.requiredOption("--out <directory>", "output directory")
|
|
870
|
-
.description("download and verify Model Version content")
|
|
871
|
-
.action(async (modelVersionId: string, options: { out: string }) => {
|
|
872
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
873
|
-
await downloadModelVersion(
|
|
874
|
-
client,
|
|
875
|
-
{ projectId, modelVersionId, out: options.out },
|
|
876
|
-
context.io,
|
|
877
|
-
);
|
|
878
|
-
});
|
|
879
|
-
|
|
880
|
-
const training = program.command("training").description("manage project Training drafts");
|
|
881
|
-
training
|
|
882
|
-
.command("list")
|
|
883
|
-
.option("--json", "print JSON", false)
|
|
884
|
-
.description("list active Trainings owned by the selected project")
|
|
885
|
-
.action(async (options: { json: boolean }) => {
|
|
886
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
887
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
888
|
-
await listTrainings(client, { projectId, json: options.json }, context.io);
|
|
889
|
-
});
|
|
890
|
-
training
|
|
891
|
-
.command("get")
|
|
892
|
-
.argument("<name-or-id>", "Training name or ID")
|
|
893
|
-
.option("--json", "print JSON", false)
|
|
894
|
-
.description("show one Training draft")
|
|
895
|
-
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
896
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
897
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
898
|
-
await getTraining(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
899
|
-
});
|
|
900
|
-
training
|
|
901
|
-
.command("create")
|
|
902
|
-
.argument("<name>", "new Training name")
|
|
903
|
-
.option("--method <method>", "Training method", trainingMethod, "sft")
|
|
904
|
-
.option("--base-model <id>", "catalog Base Model source", parseBaseModelId)
|
|
905
|
-
.option("--model-version <id>", "exact Model Version source")
|
|
906
|
-
.requiredOption("--dataset <id>", "required training Dataset ID")
|
|
907
|
-
.option("--validation-dataset <id>", "optional validation Dataset ID")
|
|
908
|
-
.option("--train-filter <field=value>", "training Dataset field equality filter")
|
|
909
|
-
.option("--validation-filter <field=value>", "validation Dataset field equality filter")
|
|
910
|
-
.option("--regime <regime>", "Python training regime", trainingRegime)
|
|
911
|
-
.option("--epochs <count>", "training epochs", nonNegativeInteger)
|
|
912
|
-
.option("--eval-steps <count>", "SFT eval steps", positiveInteger)
|
|
913
|
-
.option("--prompt-file <path>", "gisting prompt text file")
|
|
914
|
-
.option("--json", "print JSON", false)
|
|
915
|
-
.description("create a normalized Training draft")
|
|
916
|
-
.action(async (name: string, options: RawTrainingDraftOptions & { json: boolean }) => {
|
|
917
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
918
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
919
|
-
await createTraining(
|
|
920
|
-
client,
|
|
921
|
-
{
|
|
922
|
-
...trainingDraftOptions(options),
|
|
923
|
-
projectId,
|
|
924
|
-
name,
|
|
925
|
-
json: options.json,
|
|
926
|
-
},
|
|
927
|
-
context.io,
|
|
928
|
-
);
|
|
929
|
-
});
|
|
930
|
-
training
|
|
931
|
-
.command("update")
|
|
932
|
-
.argument("<name-or-id>", "Training name or ID")
|
|
933
|
-
.option("--base-model <id>", "catalog Base Model source", parseBaseModelId)
|
|
934
|
-
.option("--model-version <id>", "exact Model Version source")
|
|
935
|
-
.option("--dataset <id>", "required training Dataset ID")
|
|
936
|
-
.option("--validation-dataset <id>", "replacement validation Dataset ID")
|
|
937
|
-
.option("--train-filter <field=value>", "replacement training Dataset field equality filter")
|
|
938
|
-
.option("--validation-filter <field=value>", "replacement validation Dataset field equality filter")
|
|
939
|
-
.option("--regime <regime>", "Python training regime", trainingRegime)
|
|
940
|
-
.option("--epochs <count>", "training epochs", nonNegativeInteger)
|
|
941
|
-
.option("--eval-steps <count>", "SFT eval steps", positiveInteger)
|
|
942
|
-
.option("--prompt-file <path>", "replacement gisting prompt text file")
|
|
943
|
-
.option("--json", "print JSON", false)
|
|
944
|
-
.description("replace the complete Training draft")
|
|
945
|
-
.action(async (nameOrId: string, options: RawTrainingDraftOptions & { json: boolean }) => {
|
|
946
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
947
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
948
|
-
await updateTraining(
|
|
949
|
-
client,
|
|
950
|
-
{
|
|
951
|
-
...trainingDraftOptions(options),
|
|
952
|
-
projectId,
|
|
953
|
-
nameOrId,
|
|
954
|
-
json: options.json,
|
|
955
|
-
},
|
|
956
|
-
context.io,
|
|
957
|
-
);
|
|
958
|
-
});
|
|
959
|
-
training
|
|
960
|
-
.command("cost")
|
|
961
|
-
.argument("<name-or-id>", "Training name or ID")
|
|
962
|
-
.option("--idempotency-key <key>", "reuse one Cost Estimate input preparation")
|
|
963
|
-
.option("--json", "print JSON", false)
|
|
964
|
-
.description("validate Training inputs and calculate the maximum charge")
|
|
965
|
-
.action(async (nameOrId: string, options: { idempotencyKey?: string; json: boolean }) => {
|
|
966
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
967
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
968
|
-
await estimateTrainingCost(
|
|
969
|
-
client,
|
|
970
|
-
{ projectId, nameOrId, idempotencyKey: options.idempotencyKey, json: options.json },
|
|
971
|
-
context.io,
|
|
972
|
-
);
|
|
973
|
-
});
|
|
974
|
-
training
|
|
975
|
-
.command("run")
|
|
976
|
-
.argument("<name-or-id>", "Training name or ID")
|
|
977
|
-
.requiredOption("--estimate-file <path>", "ready Training Cost Estimate JSON")
|
|
978
|
-
.requiredOption("--version-name <name>", "unique Model Version name for this run")
|
|
979
|
-
.option("--idempotency-key <key>", "reuse one Training Run creation")
|
|
980
|
-
.option("--json", "print JSON", false)
|
|
981
|
-
.description("create one queued Training Run from a ready Cost Estimate")
|
|
982
|
-
.action(
|
|
983
|
-
async (
|
|
984
|
-
nameOrId: string,
|
|
985
|
-
options: {
|
|
986
|
-
estimateFile: string;
|
|
987
|
-
versionName: string;
|
|
988
|
-
idempotencyKey?: string;
|
|
989
|
-
json: boolean;
|
|
990
|
-
},
|
|
991
|
-
) => {
|
|
992
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
993
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
994
|
-
await createTrainingRun(client, { projectId, nameOrId, ...options }, context.io);
|
|
995
|
-
},
|
|
996
|
-
);
|
|
997
|
-
training
|
|
998
|
-
.command("runs")
|
|
999
|
-
.argument("<name-or-id>", "Training name or ID")
|
|
1000
|
-
.option("--json", "print JSON", false)
|
|
1001
|
-
.description("list Training Run history")
|
|
1002
|
-
.action(async (nameOrId: string, options: { json: boolean }) => {
|
|
1003
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
1004
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1005
|
-
await listTrainingRuns(client, { projectId, nameOrId, json: options.json }, context.io);
|
|
1006
|
-
});
|
|
1007
|
-
training
|
|
1008
|
-
.command("status")
|
|
1009
|
-
.argument("<training-run-id>", "Training Run ID")
|
|
1010
|
-
.option("--json", "print JSON", false)
|
|
1011
|
-
.description("show one Training Run")
|
|
1012
|
-
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
1013
|
-
const { client } = await resolveContext(globals(), context);
|
|
1014
|
-
await getTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
1015
|
-
});
|
|
1016
|
-
training
|
|
1017
|
-
.command("resume")
|
|
1018
|
-
.argument("<training-run-id>", "Training Run ID")
|
|
1019
|
-
.option("--json", "print JSON", false)
|
|
1020
|
-
.description("resume a Training Run blocked on funds or payment")
|
|
1021
|
-
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
1022
|
-
const { client } = await resolveContext(globals(), context);
|
|
1023
|
-
await resumeTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
1024
|
-
});
|
|
1025
|
-
training
|
|
1026
|
-
.command("cancel")
|
|
1027
|
-
.argument("<training-run-id>", "Training Run ID")
|
|
1028
|
-
.option("--json", "print JSON", false)
|
|
1029
|
-
.description("cancel a Training Run before provider work completes")
|
|
1030
|
-
.action(async (trainingRunId: string, options: { json: boolean }) => {
|
|
1031
|
-
const { client } = await resolveContext(globals(), context);
|
|
1032
|
-
await cancelTrainingRun(client, { trainingRunId, json: options.json }, context.io);
|
|
1033
|
-
});
|
|
1034
|
-
training
|
|
1035
|
-
.command("logs")
|
|
1036
|
-
.argument("<training-run-id>", "Training Run ID")
|
|
1037
|
-
.option("--follow", "wait for new log entries until complete", false)
|
|
1038
|
-
.description("read sanitized Training Run logs")
|
|
1039
|
-
.action(async (trainingRunId: string, options: { follow: boolean }) => {
|
|
1040
|
-
const { client } = await resolveContext(globals(), context);
|
|
1041
|
-
await readTrainingRunLogs(client, { trainingRunId, follow: options.follow }, context.io);
|
|
1042
|
-
});
|
|
1043
|
-
|
|
1044
|
-
const dataset = program
|
|
1045
|
-
.command("dataset")
|
|
1046
|
-
.description("inspect and move dataset catalog and rows");
|
|
1047
|
-
dataset
|
|
1048
|
-
.command("list")
|
|
1049
|
-
.description("list live datasets in the selected project")
|
|
1050
|
-
.action(async () => {
|
|
1051
|
-
const { client, projectId } = await resolveContext(globals(), context);
|
|
1052
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1053
|
-
const result = await listProjectDatasets(client, { projectId });
|
|
1054
|
-
context.io.stdout(result.output);
|
|
1055
|
-
});
|
|
1056
|
-
dataset
|
|
1057
|
-
.command("fields")
|
|
1058
|
-
.argument("<name>", "dataset name or id")
|
|
1059
|
-
.description("print field declarations inferred from uploaded rows")
|
|
1060
|
-
.action(async (name: string) => {
|
|
1061
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
1062
|
-
const result = await generateDatasetFieldDeclarations(client, {
|
|
1063
|
-
sourceRoot,
|
|
1064
|
-
datasetName: name,
|
|
1065
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
1066
|
-
});
|
|
1067
|
-
context.io.stdout(result.output);
|
|
1068
|
-
});
|
|
1069
|
-
dataset
|
|
1070
|
-
.command("load")
|
|
1071
|
-
.argument("<name>", "dataset name or id")
|
|
1072
|
-
.requiredOption("--out <file>", "JSONL file to write")
|
|
1073
|
-
.option("--limit <n>", "how many rows to keep after download", positiveInteger)
|
|
1074
|
-
.option("--timeout <ms>", "how long to wait for the export", positiveInteger)
|
|
1075
|
-
.description("download live dataset rows into a JSONL file")
|
|
1076
|
-
.action(async (name: string, options: { out: string; limit?: number; timeout?: number }) => {
|
|
1077
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
1078
|
-
const result = await loadDataset(client, {
|
|
1079
|
-
sourceRoot,
|
|
1080
|
-
datasetName: name,
|
|
1081
|
-
outPath: resolve(options.out),
|
|
1082
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
1083
|
-
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
1084
|
-
...(options.timeout === undefined ? {} : { timeoutMs: options.timeout }),
|
|
1085
|
-
});
|
|
1086
|
-
context.io.stdout(result.output);
|
|
1087
|
-
});
|
|
1088
|
-
dataset
|
|
1089
|
-
.command("upload")
|
|
1090
|
-
.argument("<name>", "dataset name or id")
|
|
1091
|
-
.argument("<file>", "JSON array, JSONL, or CSV file")
|
|
1092
|
-
.description("append rows to a dataset")
|
|
1093
|
-
.action(async (name: string, file: string) => {
|
|
1094
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
1095
|
-
const result = await uploadDataset(client, {
|
|
1096
|
-
sourceRoot,
|
|
1097
|
-
datasetName: name,
|
|
1098
|
-
filePath: resolve(file),
|
|
1099
|
-
...(projectId === undefined ? {} : { projectId }),
|
|
1100
|
-
});
|
|
1101
|
-
context.io.stdout(result.output);
|
|
1102
|
-
if (result.warning !== undefined) context.io.stderr(result.warning);
|
|
1103
|
-
});
|
|
1104
|
-
|
|
1105
|
-
const pipelineNameHelp =
|
|
1106
|
-
"pipeline name or id; required unless a TTY picker can choose among several";
|
|
1107
|
-
const pipeline = program
|
|
1108
|
-
.command("pipeline")
|
|
1109
|
-
.description("operate on a live pipeline in the selected project");
|
|
1110
|
-
pipeline
|
|
1111
|
-
.command("list")
|
|
1112
|
-
.option("--limit <n>", "maximum pipelines to list", positiveInteger)
|
|
1113
|
-
.description("list live pipelines in the selected project")
|
|
1114
|
-
.action(async (options: { limit?: number }) => {
|
|
1115
|
-
const { client, projectId, state } = await resolveContext(globals(), context);
|
|
1116
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1117
|
-
const result = await listLivePipelines({
|
|
1118
|
-
client,
|
|
1119
|
-
projectId,
|
|
1120
|
-
state,
|
|
1121
|
-
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
1122
|
-
});
|
|
1123
|
-
context.io.stdout(result.output);
|
|
1124
|
-
});
|
|
1125
|
-
pipeline
|
|
1126
|
-
.command("run")
|
|
1127
|
-
.argument("[name]", pipelineNameHelp)
|
|
1128
|
-
.option("--wait", "wait for the run to reach a terminal state", false)
|
|
1129
|
-
.option("--timeout <ms>", "how long to wait", positiveInteger)
|
|
1130
|
-
.description("start a run on a deployed pipeline")
|
|
1131
|
-
.action(async (name: string | undefined, options: { wait: boolean; timeout?: number }) => {
|
|
1132
|
-
const { client, state, projectId } = await resolveContext(globals(), context);
|
|
1133
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1134
|
-
const result = await startPipelineRun(client, {
|
|
1135
|
-
projectId,
|
|
1136
|
-
state,
|
|
1137
|
-
...(name === undefined ? {} : { pipeline: name }),
|
|
1138
|
-
wait: options.wait,
|
|
1139
|
-
...(options.timeout === undefined ? {} : { timeoutMs: options.timeout }),
|
|
1140
|
-
...(context.io.select === undefined ? {} : { select: context.io.select }),
|
|
1141
|
-
});
|
|
1142
|
-
context.io.stdout(result.output);
|
|
1143
|
-
exitCode.value = exitCodeForOutcome(result.outcome);
|
|
1144
|
-
});
|
|
1145
|
-
pipeline
|
|
1146
|
-
.command("status")
|
|
1147
|
-
.argument("[name]", pipelineNameHelp)
|
|
1148
|
-
.description("report the latest run's state and item counts")
|
|
1149
|
-
.action(async (name: string | undefined) => {
|
|
1150
|
-
const { client, state, projectId } = await resolveContext(globals(), context);
|
|
1151
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1152
|
-
const result = await pipelineRunStatus(client, {
|
|
1153
|
-
projectId,
|
|
1154
|
-
state,
|
|
1155
|
-
...(name === undefined ? {} : { pipeline: name }),
|
|
1156
|
-
...(context.io.select === undefined ? {} : { select: context.io.select }),
|
|
1157
|
-
});
|
|
1158
|
-
context.io.stdout(result.output);
|
|
1159
|
-
exitCode.value = exitCodeForOutcome(result.outcome);
|
|
1160
|
-
});
|
|
1161
|
-
pipeline
|
|
1162
|
-
.command("runs")
|
|
1163
|
-
.argument("[name]", pipelineNameHelp)
|
|
1164
|
-
.option("--limit <n>", "how many runs to list", positiveInteger)
|
|
1165
|
-
.description("list recent runs")
|
|
1166
|
-
.action(async (name: string | undefined, options: { limit?: number }) => {
|
|
1167
|
-
const { client, state, projectId } = await resolveContext(globals(), context);
|
|
1168
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1169
|
-
const result = await listPipelineRuns(client, {
|
|
1170
|
-
projectId,
|
|
1171
|
-
state,
|
|
1172
|
-
...(name === undefined ? {} : { pipeline: name }),
|
|
1173
|
-
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
1174
|
-
...(context.io.select === undefined ? {} : { select: context.io.select }),
|
|
1175
|
-
});
|
|
1176
|
-
context.io.stdout(result.output);
|
|
1177
|
-
});
|
|
1178
|
-
pipeline
|
|
1179
|
-
.command("cost")
|
|
1180
|
-
.argument("[name]", pipelineNameHelp)
|
|
1181
|
-
.option("--item-limit <n>", "cap how many items to price", positiveInteger)
|
|
1182
|
-
.description("forecast what a run of this pipeline would bill")
|
|
1183
|
-
.action(async (name: string | undefined, options: { itemLimit?: number }) => {
|
|
1184
|
-
const { client, state, projectId } = await resolveContext(globals(), context);
|
|
1185
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1186
|
-
const result = await estimatePipelineCost(client, {
|
|
1187
|
-
projectId,
|
|
1188
|
-
state,
|
|
1189
|
-
...(name === undefined ? {} : { pipeline: name }),
|
|
1190
|
-
...(options.itemLimit === undefined ? {} : { itemLimit: options.itemLimit }),
|
|
1191
|
-
...(context.io.select === undefined ? {} : { select: context.io.select }),
|
|
1192
|
-
});
|
|
1193
|
-
context.io.stdout(result.output);
|
|
1194
|
-
});
|
|
1195
|
-
pipeline
|
|
1196
|
-
.command("cancel")
|
|
1197
|
-
.argument("<runId>", "run to cancel")
|
|
1198
|
-
.description("request cancellation of a run")
|
|
1199
|
-
.action(async (runId: string) => {
|
|
1200
|
-
const { client } = await resolveContext(globals(), context);
|
|
1201
|
-
const result = await cancelPipelineRun(client, { runId });
|
|
1202
|
-
context.io.stdout(result.output);
|
|
1203
|
-
});
|
|
1204
|
-
pipeline
|
|
1205
|
-
.command("results")
|
|
1206
|
-
.argument("<runId>", "run to read")
|
|
1207
|
-
.option("--limit <n>", "how many items to scan", positiveInteger)
|
|
1208
|
-
.option("--failed", "only report failed items", false)
|
|
1209
|
-
.description("report a run's items")
|
|
1210
|
-
.action(async (runId: string, options: { limit?: number; failed: boolean }) => {
|
|
1211
|
-
const { client } = await resolveContext(globals(), context);
|
|
1212
|
-
const result = await pipelineRunResults(client, {
|
|
1213
|
-
runId,
|
|
1214
|
-
failedOnly: options.failed,
|
|
1215
|
-
...(options.limit === undefined ? {} : { limit: options.limit }),
|
|
1216
|
-
});
|
|
1217
|
-
context.io.stdout(result.output);
|
|
1218
|
-
exitCode.value = exitCodeForOutcome(result.outcome);
|
|
1219
|
-
});
|
|
1220
|
-
|
|
1221
|
-
program
|
|
1222
|
-
.command("status")
|
|
1223
|
-
.description("report project health: live counts and pending source changes")
|
|
1224
|
-
.action(async () => {
|
|
1225
|
-
const { sourceRoot, client, projectId } = await resolveContext(globals(), context);
|
|
1226
|
-
assert(projectId, MISSING_PROJECT_ID_MESSAGE);
|
|
1227
|
-
const result = await projectStatus(client, { sourceRoot, projectId });
|
|
1228
|
-
context.io.stdout(result.output);
|
|
1229
|
-
});
|
|
1230
|
-
program
|
|
1231
|
-
.command("report")
|
|
1232
|
-
.argument("<markdownPath>", "path to a UTF-8 Markdown report")
|
|
1233
|
-
.option("--authored-by <who>", "human or agent", authoredByOption, "human")
|
|
1234
|
-
.option("--confirm", "confirm submission in a non-interactive environment", false)
|
|
1235
|
-
.description("preview and send an issue report to Toloka through PostHog")
|
|
1236
|
-
.action(
|
|
1237
|
-
async (
|
|
1238
|
-
markdownPath: string,
|
|
1239
|
-
options: { authoredBy: IssueReportAuthor; confirm: boolean },
|
|
1240
|
-
) => {
|
|
1241
|
-
assert(context.analytics, "Issue reporting requires CLI analytics.");
|
|
1242
|
-
const sourceRoot = resolveInitRoot(globals());
|
|
1243
|
-
const baseUrl =
|
|
1244
|
-
globals().baseUrl ??
|
|
1245
|
-
context.environment.POTATO_BASE_URL ??
|
|
1246
|
-
resolveInternalBaseUrl(context.environment) ??
|
|
1247
|
-
PRODUCTION_BASE_URL;
|
|
1248
|
-
exitCode.value = await submitIssueReport({
|
|
1249
|
-
path: markdownPath,
|
|
1250
|
-
authoredBy: options.authoredBy,
|
|
1251
|
-
platform: new URL(baseUrl).origin,
|
|
1252
|
-
confirm: options.confirm,
|
|
1253
|
-
sourceRoot,
|
|
1254
|
-
cwd: process.cwd(),
|
|
1255
|
-
analytics: context.analytics,
|
|
1256
|
-
io: context.io,
|
|
1257
|
-
});
|
|
1258
|
-
},
|
|
1259
|
-
);
|
|
1260
|
-
|
|
1261
|
-
const skill = program
|
|
1262
|
-
.command("skill")
|
|
1263
|
-
.description("discover bundled agent skills and install them into .agents/skills");
|
|
1264
|
-
skill
|
|
1265
|
-
.command("list")
|
|
1266
|
-
.option("--json", "print Skill Descriptors with their status as JSON", false)
|
|
1267
|
-
.description("list bundled skills as not-installed, current, or stale")
|
|
1268
|
-
.action(async (options: { json: boolean }) => {
|
|
1269
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
1270
|
-
await skillListCommand({ sourceRoot, json: options.json, io: context.io });
|
|
1271
|
-
});
|
|
1272
|
-
skill
|
|
1273
|
-
.command("search")
|
|
1274
|
-
.argument("<query>", "text matched case-insensitively against id, name, description, and tags")
|
|
1275
|
-
.option("--json", "print matching Skill Descriptors with their status as JSON", false)
|
|
1276
|
-
.description("find bundled skills for a task")
|
|
1277
|
-
.action(async (query: string, options: { json: boolean }) => {
|
|
1278
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
1279
|
-
await skillSearchCommand({ sourceRoot, query, json: options.json, io: context.io });
|
|
1280
|
-
});
|
|
1281
|
-
skill
|
|
1282
|
-
.command("install")
|
|
1283
|
-
.argument("<skillId>", "skill id from toloka skill list")
|
|
1284
|
-
.option("-f, --force", "replace an existing .agents/skills/<skillId> directory", false)
|
|
1285
|
-
.description("copy one bundled skill into .agents/skills/<skillId>")
|
|
1286
|
-
.action(async (skillId: string, options: { force: boolean }) => {
|
|
1287
|
-
const sourceRoot = await resolveSourceRoot(globals(), context.io);
|
|
1288
|
-
await skillInstallCommand({ sourceRoot, skillId, force: options.force, io: context.io });
|
|
1289
|
-
});
|
|
136
|
+
registerVersionCommand(program, host);
|
|
137
|
+
registerConfigCommands(program, host);
|
|
138
|
+
registerAuthCommands(program, host);
|
|
139
|
+
registerProjectCommands(program, host);
|
|
140
|
+
if (context.internalUser === true) registerDeliveryOrderCommands(program, host);
|
|
141
|
+
registerSourceCommands(program, host);
|
|
142
|
+
registerSecretCommands(program, host);
|
|
143
|
+
registerModelCommands(program, host);
|
|
144
|
+
registerTrainingCommands(program, host);
|
|
145
|
+
registerDatasetCommands(program, host);
|
|
146
|
+
registerPipelineCommands(program, host);
|
|
147
|
+
registerStatusCommand(program, host);
|
|
148
|
+
registerReportCommand(program, host);
|
|
149
|
+
registerSkillCommands(program, host);
|
|
1290
150
|
|
|
1291
151
|
return { program, exitCode };
|
|
1292
152
|
}
|
|
1293
153
|
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
return chunks.join("");
|
|
1299
|
-
};
|
|
1300
|
-
|
|
1301
|
-
const readMaskedLine = async (question: string): Promise<string> => {
|
|
1302
|
-
process.stdout.write(question);
|
|
1303
|
-
const wasRaw = process.stdin.isRaw;
|
|
1304
|
-
process.stdin.setRawMode(true);
|
|
1305
|
-
process.stdin.resume();
|
|
1306
|
-
process.stdin.setEncoding("utf8");
|
|
1307
|
-
|
|
1308
|
-
const { promise, resolve, reject } = Promise.withResolvers<string>();
|
|
1309
|
-
let value = "";
|
|
1310
|
-
|
|
1311
|
-
const restore = () => {
|
|
1312
|
-
process.stdin.off("data", onData);
|
|
1313
|
-
process.stdin.off("end", onEnd);
|
|
1314
|
-
process.stdin.off("error", onError);
|
|
1315
|
-
process.stdin.setRawMode(wasRaw);
|
|
1316
|
-
process.stdin.pause();
|
|
1317
|
-
};
|
|
1318
|
-
|
|
1319
|
-
const finish = () => {
|
|
1320
|
-
restore();
|
|
1321
|
-
process.stdout.write("\n");
|
|
1322
|
-
};
|
|
1323
|
-
|
|
1324
|
-
const onEnd = () => {
|
|
1325
|
-
finish();
|
|
1326
|
-
reject(new Error("Standard input closed before an API key was entered."));
|
|
1327
|
-
};
|
|
1328
|
-
|
|
1329
|
-
const onError = (error: Error) => {
|
|
1330
|
-
finish();
|
|
1331
|
-
reject(error);
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
const onData = (chunk: string) => {
|
|
1335
|
-
for (const character of chunk) {
|
|
1336
|
-
if (character === "\r" || character === "\n") {
|
|
1337
|
-
finish();
|
|
1338
|
-
resolve(value);
|
|
1339
|
-
return;
|
|
1340
|
-
}
|
|
1341
|
-
if (character === "\u0003" || character === "\u0004") {
|
|
1342
|
-
finish();
|
|
1343
|
-
reject(
|
|
1344
|
-
new Error(
|
|
1345
|
-
character === "\u0003"
|
|
1346
|
-
? "Canceled."
|
|
1347
|
-
: "Standard input closed before an API key was entered.",
|
|
1348
|
-
),
|
|
1349
|
-
);
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
if (character === "\u007f" || character === "\b") {
|
|
1353
|
-
if (value.length > 0) {
|
|
1354
|
-
value = value.slice(0, -1);
|
|
1355
|
-
process.stdout.write("\b \b");
|
|
1356
|
-
}
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
value += character;
|
|
1360
|
-
process.stdout.write("*");
|
|
1361
|
-
}
|
|
1362
|
-
};
|
|
1363
|
-
|
|
1364
|
-
process.stdin.on("data", onData);
|
|
1365
|
-
process.stdin.once("end", onEnd);
|
|
1366
|
-
process.stdin.once("error", onError);
|
|
1367
|
-
|
|
1368
|
-
try {
|
|
1369
|
-
return await promise;
|
|
1370
|
-
} finally {
|
|
1371
|
-
restore();
|
|
1372
|
-
}
|
|
154
|
+
type CommandResult = {
|
|
155
|
+
readonly code: number;
|
|
156
|
+
readonly outcome: CliAnalyticsCommandOutcome;
|
|
157
|
+
readonly error?: unknown;
|
|
1373
158
|
};
|
|
1374
159
|
|
|
1375
|
-
const
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
160
|
+
const firstOperand = (program: Command, argv: readonly string[]): string | undefined => {
|
|
161
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
162
|
+
const token = argv[index];
|
|
163
|
+
if (token === undefined) return undefined;
|
|
164
|
+
if (token === "--") return argv[index + 1];
|
|
165
|
+
if (!token.startsWith("-")) return token;
|
|
166
|
+
const option = program.options.find(
|
|
167
|
+
(candidate) => candidate.short === token || candidate.long === token,
|
|
168
|
+
);
|
|
169
|
+
if (option !== undefined && (option.required || option.optional) && !token.includes("="))
|
|
170
|
+
index += 1;
|
|
1383
171
|
}
|
|
172
|
+
return undefined;
|
|
1384
173
|
};
|
|
1385
174
|
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
type CommandResult = {
|
|
1394
|
-
readonly code: number;
|
|
1395
|
-
readonly outcome: CliAnalyticsCommandOutcome;
|
|
1396
|
-
readonly error?: unknown;
|
|
175
|
+
const unknownCommand = (program: Command, argv: readonly string[]): string | undefined => {
|
|
176
|
+
const name = firstOperand(program, argv);
|
|
177
|
+
if (name === undefined || name === "help") return undefined;
|
|
178
|
+
const known = program.commands.some(
|
|
179
|
+
(command) => command.name() === name || command.aliases().includes(name),
|
|
180
|
+
);
|
|
181
|
+
return known ? undefined : name;
|
|
1397
182
|
};
|
|
1398
183
|
|
|
1399
184
|
const runProgram = async (
|
|
@@ -1402,6 +187,13 @@ const runProgram = async (
|
|
|
1402
187
|
argv: readonly string[],
|
|
1403
188
|
io: CliIo,
|
|
1404
189
|
): Promise<CommandResult> => {
|
|
190
|
+
const unknown = unknownCommand(program, argv);
|
|
191
|
+
if (unknown !== undefined) {
|
|
192
|
+
io.stderr(
|
|
193
|
+
`error: unknown command '${unknown}'\ntoloka ${SDK_VERSION} does not have it. Run toloka --help to list its commands; a runbook that uses it may need a newer release.`,
|
|
194
|
+
);
|
|
195
|
+
return { code: 1, outcome: "usage_error" };
|
|
196
|
+
}
|
|
1405
197
|
try {
|
|
1406
198
|
await program.parseAsync([...argv], { from: "user" });
|
|
1407
199
|
const code = exitCode.value;
|
|
@@ -1416,15 +208,42 @@ const runProgram = async (
|
|
|
1416
208
|
}
|
|
1417
209
|
};
|
|
1418
210
|
|
|
211
|
+
const finalizeAnalytics = async ({
|
|
212
|
+
analytics,
|
|
213
|
+
argv,
|
|
214
|
+
startedAt,
|
|
215
|
+
result,
|
|
216
|
+
}: {
|
|
217
|
+
analytics: CliAnalytics | undefined;
|
|
218
|
+
argv: readonly string[];
|
|
219
|
+
startedAt: number;
|
|
220
|
+
result: CommandResult;
|
|
221
|
+
}): Promise<void> => {
|
|
222
|
+
if (analytics === undefined) return;
|
|
223
|
+
|
|
224
|
+
try {
|
|
225
|
+
analytics.captureCommand({
|
|
226
|
+
command: commandNameFromArgv(argv),
|
|
227
|
+
durationMs: Date.now() - startedAt,
|
|
228
|
+
exitCode: result.code,
|
|
229
|
+
outcome: result.outcome,
|
|
230
|
+
...(result.error === undefined ? {} : { error: result.error }),
|
|
231
|
+
});
|
|
232
|
+
await analytics.shutdown();
|
|
233
|
+
} catch {}
|
|
234
|
+
};
|
|
235
|
+
|
|
1419
236
|
export async function runTolokaCli(
|
|
1420
237
|
argv: readonly string[],
|
|
1421
|
-
|
|
238
|
+
rawEnvironment: TolokaEnvironment = process.env,
|
|
1422
239
|
io: CliIo = terminalIo(),
|
|
1423
240
|
injectedClient?: PotatoClient,
|
|
1424
241
|
analytics?: CliAnalytics,
|
|
1425
242
|
runChecks?: typeof checkProject,
|
|
1426
243
|
autoUpdate?: AutoUpdateHooks,
|
|
1427
244
|
): Promise<number> {
|
|
245
|
+
const { environment, warnings } = normalizeEnvironment(rawEnvironment);
|
|
246
|
+
for (const warning of warnings) io.stderr(warning);
|
|
1428
247
|
const autoUpdateResult = await maybeAutoUpdate({
|
|
1429
248
|
argv,
|
|
1430
249
|
environment,
|
|
@@ -1437,28 +256,33 @@ export async function runTolokaCli(
|
|
|
1437
256
|
environment[CHECK_RESULT_FILE_ENV] === undefined
|
|
1438
257
|
? (analytics ?? createCliAnalytics({ environment }))
|
|
1439
258
|
: undefined;
|
|
1440
|
-
const
|
|
259
|
+
const baseContext: ProgramContext = {
|
|
1441
260
|
environment,
|
|
1442
261
|
io,
|
|
1443
262
|
...(activeAnalytics === undefined ? {} : { analytics: activeAnalytics }),
|
|
1444
263
|
...(injectedClient === undefined ? {} : { injectedClient }),
|
|
1445
264
|
...(runChecks === undefined ? {} : { runChecks }),
|
|
265
|
+
};
|
|
266
|
+
const internalUser = await resolveInternalCommandAccess(argv, environment, injectedClient);
|
|
267
|
+
const { program, exitCode } = createProgram({
|
|
268
|
+
...baseContext,
|
|
269
|
+
...(internalUser === undefined ? {} : { internalUser }),
|
|
1446
270
|
});
|
|
1447
|
-
|
|
1448
271
|
const startedAt = Date.now();
|
|
1449
|
-
const result = await runProgram(program, exitCode, argv, io);
|
|
1450
|
-
if (activeAnalytics !== undefined) {
|
|
1451
|
-
try {
|
|
1452
|
-
activeAnalytics.captureCommand({
|
|
1453
|
-
command: commandNameFromArgv(argv),
|
|
1454
|
-
durationMs: Date.now() - startedAt,
|
|
1455
|
-
exitCode: result.code,
|
|
1456
|
-
outcome: result.outcome,
|
|
1457
|
-
...(result.error === undefined ? {} : { error: result.error }),
|
|
1458
|
-
});
|
|
1459
|
-
await activeAnalytics.shutdown();
|
|
1460
|
-
} catch {}
|
|
1461
|
-
}
|
|
1462
272
|
|
|
1463
|
-
|
|
273
|
+
try {
|
|
274
|
+
const result = await runProgram(program, exitCode, argv, io);
|
|
275
|
+
await finalizeAnalytics({ analytics: activeAnalytics, argv, startedAt, result });
|
|
276
|
+
|
|
277
|
+
return result.code;
|
|
278
|
+
} catch (error) {
|
|
279
|
+
await finalizeAnalytics({
|
|
280
|
+
analytics: activeAnalytics,
|
|
281
|
+
argv,
|
|
282
|
+
startedAt,
|
|
283
|
+
result: { code: 1, outcome: "failed", error },
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
throw error;
|
|
287
|
+
}
|
|
1464
288
|
}
|