orizu 0.3.3 → 0.3.4
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 -0
- package/dist/index.js +168 -14
- package/package.json +1 -1
- package/src/index.ts +232 -14
package/README.md
CHANGED
|
@@ -173,6 +173,10 @@ orizu logout
|
|
|
173
173
|
<td>Link a dataset to an app</td>
|
|
174
174
|
<td><code>orizu apps link-dataset --app <appId> --dataset <datasetId></code></td>
|
|
175
175
|
</tr>
|
|
176
|
+
<tr>
|
|
177
|
+
<td>Export app source</td>
|
|
178
|
+
<td><code>orizu apps export --app <appId> --project ops-eval/support-qa --out ./apps/SupportLabeler.tsx</code></td>
|
|
179
|
+
</tr>
|
|
176
180
|
<tr>
|
|
177
181
|
<td colspan="2"><strong>Tasks</strong></td>
|
|
178
182
|
</tr>
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ import { parseDatasetReference } from './dataset-download.js';
|
|
|
16
16
|
import { parseGlobalFlags } from './global-flags.js';
|
|
17
17
|
import { assertSecureTokenTransport, authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags, } from './http.js';
|
|
18
18
|
import { formatTaskCreateError } from './task-create-error.js';
|
|
19
|
+
const MAX_README_LENGTH = 200_000;
|
|
19
20
|
function getCliVersion() {
|
|
20
21
|
const packageJson = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'));
|
|
21
22
|
if (typeof packageJson.version !== 'string' || packageJson.version.length === 0) {
|
|
@@ -30,10 +31,10 @@ function printVersion() {
|
|
|
30
31
|
printLine(`orizu ${getCliVersion()}`);
|
|
31
32
|
}
|
|
32
33
|
function printUsage() {
|
|
33
|
-
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> --subject-version <prompt-version-id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`);
|
|
34
|
+
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> --subject-version <prompt-version-id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu apps export [--app <appId>] [--project <team/project>] [--version <n>] [--out <path>]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`);
|
|
34
35
|
}
|
|
35
36
|
function printOptimizationUsage() {
|
|
36
|
-
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--json]`);
|
|
37
|
+
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--json]`);
|
|
37
38
|
}
|
|
38
39
|
let cliArgs = process.argv.slice(2);
|
|
39
40
|
function getArg(name) {
|
|
@@ -59,6 +60,20 @@ export function expandHomePath(path) {
|
|
|
59
60
|
}
|
|
60
61
|
return path;
|
|
61
62
|
}
|
|
63
|
+
function readReadmeMarkdownFromArgs(usage) {
|
|
64
|
+
const readmeFile = getArg('--readme-file');
|
|
65
|
+
const readmeText = getArg('--readme-text');
|
|
66
|
+
if (readmeFile !== null && readmeText !== null) {
|
|
67
|
+
throw new Error(`Use either --readme-file or --readme-text, not both.\n${usage}`);
|
|
68
|
+
}
|
|
69
|
+
const markdown = readmeFile !== null
|
|
70
|
+
? readFileSync(expandHomePath(readmeFile), 'utf8')
|
|
71
|
+
: readmeText;
|
|
72
|
+
if (markdown !== null && markdown.length > MAX_README_LENGTH) {
|
|
73
|
+
throw new Error(`README markdown is too large. Maximum size is ${MAX_README_LENGTH} characters.`);
|
|
74
|
+
}
|
|
75
|
+
return markdown;
|
|
76
|
+
}
|
|
62
77
|
function createCodeVerifier() {
|
|
63
78
|
return randomBytes(32).toString('base64url');
|
|
64
79
|
}
|
|
@@ -1244,14 +1259,19 @@ async function createDatasetVersion() {
|
|
|
1244
1259
|
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
1245
1260
|
const versionLabel = getArg('--label') || getArg('--version-label') || null;
|
|
1246
1261
|
const project = getArg('--project');
|
|
1262
|
+
const usage = 'Usage: orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]';
|
|
1247
1263
|
if (!datasetId) {
|
|
1248
|
-
throw new Error(
|
|
1264
|
+
throw new Error(usage);
|
|
1249
1265
|
}
|
|
1266
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
1250
1267
|
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
1251
1268
|
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/versions`, {
|
|
1252
1269
|
method: 'POST',
|
|
1253
1270
|
headers: { 'Content-Type': 'application/json' },
|
|
1254
|
-
body: JSON.stringify({
|
|
1271
|
+
body: JSON.stringify({
|
|
1272
|
+
versionLabel,
|
|
1273
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
1274
|
+
}),
|
|
1255
1275
|
});
|
|
1256
1276
|
if (!response.ok) {
|
|
1257
1277
|
throw new Error(`Failed to create dataset version: ${await response.text()}`);
|
|
@@ -1413,6 +1433,7 @@ function normalizeScoreResultsInput(sourcePath, raw) {
|
|
|
1413
1433
|
throw new Error(`Invalid score results JSON '${sourcePath}': ${message}`);
|
|
1414
1434
|
}
|
|
1415
1435
|
}
|
|
1436
|
+
const OPTIMIZATION_REPORT_MAX_BYTES = 2 * 1024 * 1024;
|
|
1416
1437
|
function parseOptionalNumberFlag(name) {
|
|
1417
1438
|
const value = getArg(name);
|
|
1418
1439
|
if (!value) {
|
|
@@ -1427,6 +1448,40 @@ function parseOptionalNumberFlag(name) {
|
|
|
1427
1448
|
function hasObjectKeys(value) {
|
|
1428
1449
|
return Object.keys(value).length > 0;
|
|
1429
1450
|
}
|
|
1451
|
+
function readOptimizationReportInput() {
|
|
1452
|
+
const report = getArg('--report');
|
|
1453
|
+
const reportFile = getArg('--report-file');
|
|
1454
|
+
if (report && reportFile) {
|
|
1455
|
+
throw new Error('Use either --report or --report-file, not both');
|
|
1456
|
+
}
|
|
1457
|
+
if (!report && !reportFile) {
|
|
1458
|
+
return null;
|
|
1459
|
+
}
|
|
1460
|
+
let markdown;
|
|
1461
|
+
let sourceName;
|
|
1462
|
+
if (reportFile) {
|
|
1463
|
+
const expandedPath = expandHomePath(reportFile);
|
|
1464
|
+
markdown = readSourceFile(reportFile);
|
|
1465
|
+
sourceName = basename(expandedPath);
|
|
1466
|
+
}
|
|
1467
|
+
else if (report?.startsWith('@')) {
|
|
1468
|
+
const path = report.slice(1);
|
|
1469
|
+
const expandedPath = expandHomePath(path);
|
|
1470
|
+
markdown = readSourceFile(path);
|
|
1471
|
+
sourceName = basename(expandedPath);
|
|
1472
|
+
}
|
|
1473
|
+
else {
|
|
1474
|
+
markdown = report || '';
|
|
1475
|
+
sourceName = 'inline';
|
|
1476
|
+
}
|
|
1477
|
+
if (!markdown.trim()) {
|
|
1478
|
+
throw new Error('Optimization report markdown must not be blank');
|
|
1479
|
+
}
|
|
1480
|
+
if (Buffer.byteLength(markdown, 'utf8') > OPTIMIZATION_REPORT_MAX_BYTES) {
|
|
1481
|
+
throw new Error(`Optimization report exceeds ${OPTIMIZATION_REPORT_MAX_BYTES} bytes`);
|
|
1482
|
+
}
|
|
1483
|
+
return { markdown, sourceName };
|
|
1484
|
+
}
|
|
1430
1485
|
async function startOptimizationRun() {
|
|
1431
1486
|
const project = getArg('--project') || await resolveProjectSlug(null);
|
|
1432
1487
|
const optimizerVersionId = getArg('--optimizer-version');
|
|
@@ -1503,6 +1558,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1503
1558
|
};
|
|
1504
1559
|
const metadata = readJsonObjectArg(getArg('--metadata'), 'optimization metadata');
|
|
1505
1560
|
const reason = getArg('--reason');
|
|
1561
|
+
const report = readOptimizationReportInput();
|
|
1562
|
+
if (report && (action === 'pause' || action === 'resume')) {
|
|
1563
|
+
throw new Error(`orizu optimizations ${action} does not accept optimization reports`);
|
|
1564
|
+
}
|
|
1506
1565
|
if ((action === 'pause' || action === 'cancel') && reason) {
|
|
1507
1566
|
metadata.reason = reason;
|
|
1508
1567
|
}
|
|
@@ -1513,6 +1572,10 @@ async function updateOptimizationRunLifecycle(action) {
|
|
|
1513
1572
|
if (hasObjectKeys(metadata)) {
|
|
1514
1573
|
body.metadata = metadata;
|
|
1515
1574
|
}
|
|
1575
|
+
if (report) {
|
|
1576
|
+
body.reportMarkdown = report.markdown;
|
|
1577
|
+
body.reportSourceName = report.sourceName;
|
|
1578
|
+
}
|
|
1516
1579
|
if (action === 'finish') {
|
|
1517
1580
|
const bestScore = parseOptionalNumberFlag('--best-score');
|
|
1518
1581
|
if (bestScore !== undefined) {
|
|
@@ -2185,6 +2248,14 @@ async function updateTaskStatus(targetStatus) {
|
|
|
2185
2248
|
const action = targetStatus === 'paused' ? 'Paused' : 'Unpaused';
|
|
2186
2249
|
printLine(`${action} task ${sanitizeTerminalText(data.task.id)} [${sanitizeTerminalText(data.task.status)}]`);
|
|
2187
2250
|
}
|
|
2251
|
+
function defaultAppExportFilename(appName, versionNum) {
|
|
2252
|
+
const safeName = appName
|
|
2253
|
+
.trim()
|
|
2254
|
+
.replace(/[^A-Za-z0-9._-]+/g, '_')
|
|
2255
|
+
.replace(/^_+|_+$/g, '')
|
|
2256
|
+
.slice(0, 100);
|
|
2257
|
+
return `${safeName || 'app'}.v${versionNum}.tsx`;
|
|
2258
|
+
}
|
|
2188
2259
|
async function appDetail() {
|
|
2189
2260
|
const appId = getArg('--app');
|
|
2190
2261
|
const project = getArg('--project');
|
|
@@ -2223,6 +2294,40 @@ async function appDetail() {
|
|
|
2223
2294
|
printLine(` Created: ${sanitizeTerminalText(detail.createdAt)}`);
|
|
2224
2295
|
printLine(` Updated: ${sanitizeTerminalText(detail.updatedAt)}`);
|
|
2225
2296
|
}
|
|
2297
|
+
async function exportAppSource() {
|
|
2298
|
+
const project = getArg('--project');
|
|
2299
|
+
const versionArg = getArg('--version');
|
|
2300
|
+
const outPathArg = getArg('--out');
|
|
2301
|
+
let appId = getArg('--app');
|
|
2302
|
+
const versionNum = versionArg && /^[1-9]\d*$/.test(versionArg)
|
|
2303
|
+
? Number.parseInt(versionArg, 10)
|
|
2304
|
+
: undefined;
|
|
2305
|
+
if (versionArg && versionNum === undefined) {
|
|
2306
|
+
throw new Error('--version must be a positive integer');
|
|
2307
|
+
}
|
|
2308
|
+
if (!appId) {
|
|
2309
|
+
const selected = await selectAppIdInteractively(project);
|
|
2310
|
+
appId = selected.appId;
|
|
2311
|
+
}
|
|
2312
|
+
const query = new URLSearchParams();
|
|
2313
|
+
if (project) {
|
|
2314
|
+
query.set('project', project);
|
|
2315
|
+
}
|
|
2316
|
+
if (versionNum) {
|
|
2317
|
+
query.set('version', String(versionNum));
|
|
2318
|
+
}
|
|
2319
|
+
const suffix = query.toString() ? `?${query.toString()}` : '';
|
|
2320
|
+
const response = await authedFetch(`/api/cli/apps/${encodeURIComponent(appId)}/export${suffix}`);
|
|
2321
|
+
if (!response.ok) {
|
|
2322
|
+
throw new Error(`Failed to export app (${response.status}): ${await response.text()}`);
|
|
2323
|
+
}
|
|
2324
|
+
const data = await parseJsonResponse(response, 'App export');
|
|
2325
|
+
const filename = outPathArg
|
|
2326
|
+
? expandHomePath(outPathArg)
|
|
2327
|
+
: defaultAppExportFilename(data.app.name, data.version.versionNum);
|
|
2328
|
+
writeTextFileEnsuringDir(filename, data.version.code);
|
|
2329
|
+
printLine(`Saved app ${sanitizeTerminalText(data.app.name)} v${data.version.versionNum} source to ${sanitizeTerminalText(filename)}`);
|
|
2330
|
+
}
|
|
2226
2331
|
async function listTeamMembers() {
|
|
2227
2332
|
const teamSlug = await resolveTeamSlug(getArg('--team'));
|
|
2228
2333
|
printTeamMembers(await fetchTeamMembers(teamSlug));
|
|
@@ -2287,7 +2392,7 @@ async function changeTeamMemberRole() {
|
|
|
2287
2392
|
}
|
|
2288
2393
|
printLine(`Updated ${sanitizeTerminalText(member.email)} role to ${sanitizeTerminalText(role)}`);
|
|
2289
2394
|
}
|
|
2290
|
-
async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
2395
|
+
async function createDatasetFromRows(project, name, sourceType, rows, readmeMarkdown = null) {
|
|
2291
2396
|
const response = await authedFetch('/api/cli/datasets/upload', {
|
|
2292
2397
|
method: 'POST',
|
|
2293
2398
|
headers: {
|
|
@@ -2298,6 +2403,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2298
2403
|
name,
|
|
2299
2404
|
rows,
|
|
2300
2405
|
sourceType,
|
|
2406
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
2301
2407
|
}),
|
|
2302
2408
|
});
|
|
2303
2409
|
if (!response.ok) {
|
|
@@ -2306,7 +2412,7 @@ async function createDatasetFromRows(project, name, sourceType, rows) {
|
|
|
2306
2412
|
}
|
|
2307
2413
|
return parseJsonResponse(response, 'Dataset upload');
|
|
2308
2414
|
}
|
|
2309
|
-
async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
2415
|
+
async function uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown = null) {
|
|
2310
2416
|
let dataset = null;
|
|
2311
2417
|
let totalUploaded = 0;
|
|
2312
2418
|
let chunkIndex = 0;
|
|
@@ -2334,7 +2440,7 @@ async function uploadJsonlDatasetInChunks(file, project, datasetName) {
|
|
|
2334
2440
|
printLine(`Uploading chunk ${chunkIndex} (${chunk.length} rows)...`);
|
|
2335
2441
|
try {
|
|
2336
2442
|
if (!dataset) {
|
|
2337
|
-
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk);
|
|
2443
|
+
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk, readmeMarkdown);
|
|
2338
2444
|
dataset = data.dataset;
|
|
2339
2445
|
totalUploaded = data.dataset.rowCount;
|
|
2340
2446
|
continue;
|
|
@@ -2369,18 +2475,20 @@ async function uploadDataset() {
|
|
|
2369
2475
|
const projectArg = getArg('--project');
|
|
2370
2476
|
const fileArg = getArg('--file');
|
|
2371
2477
|
const name = getArg('--name');
|
|
2478
|
+
const usage = 'Usage: orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]';
|
|
2372
2479
|
if (!fileArg) {
|
|
2373
|
-
throw new Error(
|
|
2480
|
+
throw new Error(usage);
|
|
2374
2481
|
}
|
|
2482
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2375
2483
|
const file = expandHomePath(fileArg);
|
|
2376
2484
|
const project = await resolveProjectSlug(projectArg);
|
|
2377
2485
|
const datasetName = name || basename(file);
|
|
2378
2486
|
if (extname(file).toLowerCase() === '.jsonl') {
|
|
2379
|
-
await uploadJsonlDatasetInChunks(file, project, datasetName);
|
|
2487
|
+
await uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown);
|
|
2380
2488
|
return;
|
|
2381
2489
|
}
|
|
2382
2490
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2383
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2491
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2384
2492
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
2385
2493
|
if (data.dataset.url) {
|
|
2386
2494
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
@@ -2390,20 +2498,26 @@ async function pushDataset() {
|
|
|
2390
2498
|
const projectArg = getArg('--project');
|
|
2391
2499
|
const fileArg = getPositionalArg(2) || getArg('--file');
|
|
2392
2500
|
const name = getArg('--name');
|
|
2501
|
+
const usage = 'Usage: orizu datasets push <rows.csv|rows.json|rows.jsonl> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]';
|
|
2393
2502
|
if (!fileArg) {
|
|
2394
|
-
throw new Error(
|
|
2503
|
+
throw new Error(usage);
|
|
2395
2504
|
}
|
|
2505
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2396
2506
|
const file = expandHomePath(fileArg);
|
|
2397
2507
|
const project = await resolveProjectSlug(projectArg);
|
|
2398
2508
|
const datasetName = name || basename(file);
|
|
2399
2509
|
const { rows, sourceType } = parseDatasetFile(file);
|
|
2400
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows);
|
|
2510
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown);
|
|
2401
2511
|
if (hasJsonFlag()) {
|
|
2402
|
-
|
|
2512
|
+
const payload = {
|
|
2403
2513
|
dataset_id: data.dataset.id,
|
|
2404
2514
|
name: data.dataset.name,
|
|
2405
2515
|
row_count: data.dataset.rowCount,
|
|
2406
|
-
}
|
|
2516
|
+
};
|
|
2517
|
+
if (data.readmeVersion?.id) {
|
|
2518
|
+
payload.readme_version_id = data.readmeVersion.id;
|
|
2519
|
+
}
|
|
2520
|
+
printJson(payload);
|
|
2407
2521
|
return;
|
|
2408
2522
|
}
|
|
2409
2523
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`);
|
|
@@ -2411,6 +2525,38 @@ async function pushDataset() {
|
|
|
2411
2525
|
printLine(`View dataset: ${formatTerminalLink(data.dataset.url)}`);
|
|
2412
2526
|
}
|
|
2413
2527
|
}
|
|
2528
|
+
async function setDatasetReadme() {
|
|
2529
|
+
const datasetId = getPositionalArg(3) || getArg('--dataset');
|
|
2530
|
+
const project = getArg('--project');
|
|
2531
|
+
const usage = 'Usage: orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]';
|
|
2532
|
+
if (!datasetId) {
|
|
2533
|
+
throw new Error(usage);
|
|
2534
|
+
}
|
|
2535
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage);
|
|
2536
|
+
if (readmeMarkdown === null) {
|
|
2537
|
+
throw new Error(usage);
|
|
2538
|
+
}
|
|
2539
|
+
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project);
|
|
2540
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/readme`, {
|
|
2541
|
+
method: 'POST',
|
|
2542
|
+
headers: { 'Content-Type': 'application/json' },
|
|
2543
|
+
body: JSON.stringify({ markdown: readmeMarkdown }),
|
|
2544
|
+
});
|
|
2545
|
+
if (!response.ok) {
|
|
2546
|
+
throw new Error(`Failed to save dataset README: ${await response.text()}`);
|
|
2547
|
+
}
|
|
2548
|
+
const data = await parseJsonResponse(response, 'Dataset README save');
|
|
2549
|
+
if (hasJsonFlag()) {
|
|
2550
|
+
printJson({
|
|
2551
|
+
dataset_id: data.version.dataset_id,
|
|
2552
|
+
readme_version_id: data.version.id,
|
|
2553
|
+
readme_version_num: data.version.version_num,
|
|
2554
|
+
});
|
|
2555
|
+
return;
|
|
2556
|
+
}
|
|
2557
|
+
printLine(`Saved README v${data.version.version_num} for dataset ` +
|
|
2558
|
+
`${sanitizeTerminalText(data.version.dataset_id)} (${sanitizeTerminalText(data.version.id)}).`);
|
|
2559
|
+
}
|
|
2414
2560
|
function getDatasetReferenceInput() {
|
|
2415
2561
|
const fromFlag = getArg('--dataset');
|
|
2416
2562
|
if (fromFlag) {
|
|
@@ -3155,6 +3301,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3155
3301
|
await appDetail();
|
|
3156
3302
|
return;
|
|
3157
3303
|
}
|
|
3304
|
+
if (command === 'apps' && subcommand === 'export') {
|
|
3305
|
+
await exportAppSource();
|
|
3306
|
+
return;
|
|
3307
|
+
}
|
|
3158
3308
|
if (command === 'tasks' && subcommand === 'list') {
|
|
3159
3309
|
await listTasks();
|
|
3160
3310
|
return;
|
|
@@ -3196,6 +3346,10 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
3196
3346
|
return;
|
|
3197
3347
|
}
|
|
3198
3348
|
const datasetsAction = cliArgs[2];
|
|
3349
|
+
if (command === 'datasets' && subcommand === 'readme' && datasetsAction === 'set') {
|
|
3350
|
+
await setDatasetReadme();
|
|
3351
|
+
return;
|
|
3352
|
+
}
|
|
3199
3353
|
if (command === 'datasets' && subcommand === 'versions' && datasetsAction === 'create') {
|
|
3200
3354
|
await createDatasetVersion();
|
|
3201
3355
|
return;
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -185,6 +185,8 @@ interface RunnerExecContext {
|
|
|
185
185
|
|
|
186
186
|
type DatasetUploadSourceType = 'csv' | 'json' | 'jsonl'
|
|
187
187
|
|
|
188
|
+
const MAX_README_LENGTH = 200_000
|
|
189
|
+
|
|
188
190
|
interface DatasetUploadResponse {
|
|
189
191
|
dataset: {
|
|
190
192
|
id: string
|
|
@@ -193,6 +195,11 @@ interface DatasetUploadResponse {
|
|
|
193
195
|
sourceType: string
|
|
194
196
|
url?: string
|
|
195
197
|
}
|
|
198
|
+
readmeVersion?: {
|
|
199
|
+
id: string
|
|
200
|
+
version_num: number
|
|
201
|
+
created_at: string
|
|
202
|
+
} | null
|
|
196
203
|
}
|
|
197
204
|
|
|
198
205
|
interface TeamMember {
|
|
@@ -256,11 +263,11 @@ function printVersion() {
|
|
|
256
263
|
}
|
|
257
264
|
|
|
258
265
|
function printUsage() {
|
|
259
|
-
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> --subject-version <prompt-version-id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`)
|
|
266
|
+
printLine(`orizu global options:\n\n --local Use http://localhost:3000\n --server <url> Use a specific server origin (for example: https://preview.example.com)\n --version, -v Print the orizu CLI version\n\norizu commands:\n\n orizu login [--no-prompt-if-logged-in]\n orizu logout\n orizu whoami\n orizu env [--project <team/project>] [--project-id <projectId>]\n orizu log <event_type> --run-id <id> --sequence <n> --payload @event.json\n orizu teams list\n orizu teams create [--name <name>]\n orizu teams members list [--team <teamSlug>]\n orizu teams members add --email <email> [--team <teamSlug>]\n orizu teams members remove --email <email> [--team <teamSlug>]\n orizu teams members role --team <teamSlug> --email <email> --role <admin|member>\n orizu projects list [--team <teamSlug>]\n orizu projects create --name <name> [--team <teamSlug>]\n orizu prompts list --project <team/project>\n orizu prompts comments <prompt-id-or-name> --project <team/project> [--label <label> | --version <id>] [--json]\n orizu prompts pull <prompt-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu prompts push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu prompts labels set <prompt-name> <label> --version <version-id> [--project <team/project>] [--json]\n orizu prompts scorers set-headline <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu prompts scorers add <prompt-id> --scorer-version <id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu scorers list --project <team/project>\n orizu scorers register --project <team/project> --name <name> --manifest <manifest.json> [--prompt-version <id>] [--runner-version <id>] [--label <label>] [--json]\n orizu scorers detail <scorer-id-or-name> --project <team/project> [--json]\n orizu scorers labels set <scorer-name> <label> --version <scorer-version-id> [--project <team/project>] [--json]\n orizu scores submit <results.jsonl|results.json> --scorer-version <id> --subject-version <prompt-version-id> [--dataset-version <id> --split-set <id> --split <name>] [--project <team/project>] [--json]\n orizu judges list --project <team/project>\n orizu judges pull <judge-id-or-name> --project <team/project> --out <dir> [--label <label> | --version <id>] [--json]\n orizu judges push <dir> [--runner-version <id>] [--project <team/project>] [--parent <version-id>] [--json]\n orizu runners push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runners exec (--prompt <prompt-version-id> | --prompt-version <id> --runner-version <id> | --scorer-version <id>) --dataset-version <id> --split-set <id-or-name> --split <name> [--runner-dir <dir>] --out <results.jsonl|results.jsonl.gz>\n orizu optimizers push <dir> [--project <team/project>] [--name <name>] [--label <label>] [--json]\n orizu runs submit <results.jsonl|results.jsonl.gz> --prompt-version <id> --runner-version <id> --dataset-version <id> --split-set <id> --split <name> [--project <team/project>]\n orizu apps list [--project <team/project>]\n orizu apps create --project <team/project> --name <name> --dataset <datasetId> --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps update [--app <appId>] [--project <team/project>] --file <path> --input-schema <json-path> --output-schema <json-path> [--component <name>]\n orizu apps link-dataset --dataset <datasetId> [--app <appId>] [--project <team/project>] [--version <n>]\n orizu apps detail --app <appId> [--project <team/project>] [--json]\n orizu apps export [--app <appId>] [--project <team/project>] [--version <n>] [--out <path>]\n orizu tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userIdOrEmail1,userIdOrEmail2> [--version <n>] [--instructions <text>] [--labels-per-item <n>] [--json]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu tasks pause --task <taskId>\n orizu tasks unpause --task <taskId>\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]\n orizu datasets push <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]\n orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]\n orizu datasets splits create <datasetVersionId> [--from-file <split.json>] [--json]\n orizu datasets download [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--format <csv|json|jsonl>] [--out <path>]\n orizu datasets append [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>\n orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>\n orizu datasets delete [--dataset <datasetId|datasetUrl>] [--project <team/project>]\n orizu datasets lock [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--reason <text>]\n orizu datasets clone [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--name <name>]\n orizu tasks export [--task <taskId>] [--format <csv|json|jsonl>] [--out <path>]`)
|
|
260
267
|
}
|
|
261
268
|
|
|
262
269
|
function printOptimizationUsage() {
|
|
263
|
-
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--json]`)
|
|
270
|
+
printLine(`\nOptimization lifecycle commands:\n\n orizu optimizations start --project <team/project> --optimizer-version <id> --prompt-version <id[,id]> --selection-scorer <id> [--reflection-scorer <id>] [--pareto-scorer <id>] [--best-scorer <id>] --dataset-version <id> --split-set <id> [--train-split <name>] [--validation-split <name>] [--metadata <json|@file>] [--json]\n orizu optimizations run-gepa --project <team/project> --optimizer-version-id <id> --candidate-version-id <id> --runner-version-id <id> --candidate-runner-dir <dir> --scorer-version-id <id> --scorer-runner-version-id <id> --scorer-runner-dir <dir> --dataset-version-id <id> --split-set-id <id> [--train-split train] [--val-split validation] [--log-dir logs]\n orizu optimizations export <run-id> [--out <path>] [--json]\n orizu optimizations pause <run-id> [--reason <text>] [--json]\n orizu optimizations resume <run-id> [--json]\n orizu optimizations finish <run-id> [--best-score <n>] [--best-candidate <id>] [--result-prompt-version <id>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations fail <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--metadata <json|@file>] [--json]\n orizu optimizations cancel <run-id> [--reason <text>] [--report <markdown|@file> | --report-file <path>] [--json]`)
|
|
264
271
|
}
|
|
265
272
|
|
|
266
273
|
let cliArgs = process.argv.slice(2)
|
|
@@ -295,6 +302,25 @@ export function expandHomePath(path: string): string {
|
|
|
295
302
|
return path
|
|
296
303
|
}
|
|
297
304
|
|
|
305
|
+
function readReadmeMarkdownFromArgs(usage: string): string | null {
|
|
306
|
+
const readmeFile = getArg('--readme-file')
|
|
307
|
+
const readmeText = getArg('--readme-text')
|
|
308
|
+
|
|
309
|
+
if (readmeFile !== null && readmeText !== null) {
|
|
310
|
+
throw new Error(`Use either --readme-file or --readme-text, not both.\n${usage}`)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const markdown = readmeFile !== null
|
|
314
|
+
? readFileSync(expandHomePath(readmeFile), 'utf8')
|
|
315
|
+
: readmeText
|
|
316
|
+
|
|
317
|
+
if (markdown !== null && markdown.length > MAX_README_LENGTH) {
|
|
318
|
+
throw new Error(`README markdown is too large. Maximum size is ${MAX_README_LENGTH} characters.`)
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
return markdown
|
|
322
|
+
}
|
|
323
|
+
|
|
298
324
|
function createCodeVerifier(): string {
|
|
299
325
|
return randomBytes(32).toString('base64url')
|
|
300
326
|
}
|
|
@@ -1807,16 +1833,21 @@ async function createDatasetVersion() {
|
|
|
1807
1833
|
const datasetId = getPositionalArg(3) || getArg('--dataset')
|
|
1808
1834
|
const versionLabel = getArg('--label') || getArg('--version-label') || null
|
|
1809
1835
|
const project = getArg('--project')
|
|
1836
|
+
const usage = 'Usage: orizu datasets versions create <datasetId|dataset-name> [--project <team/project>] [--label <label>] [--readme-file <README.md> | --readme-text <markdown>] [--json]'
|
|
1810
1837
|
|
|
1811
1838
|
if (!datasetId) {
|
|
1812
|
-
throw new Error(
|
|
1839
|
+
throw new Error(usage)
|
|
1813
1840
|
}
|
|
1814
1841
|
|
|
1842
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage)
|
|
1815
1843
|
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project)
|
|
1816
1844
|
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/versions`, {
|
|
1817
1845
|
method: 'POST',
|
|
1818
1846
|
headers: { 'Content-Type': 'application/json' },
|
|
1819
|
-
body: JSON.stringify({
|
|
1847
|
+
body: JSON.stringify({
|
|
1848
|
+
versionLabel,
|
|
1849
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
1850
|
+
}),
|
|
1820
1851
|
})
|
|
1821
1852
|
|
|
1822
1853
|
if (!response.ok) {
|
|
@@ -2033,6 +2064,7 @@ function normalizeScoreResultsInput(sourcePath: string, raw: string): string {
|
|
|
2033
2064
|
}
|
|
2034
2065
|
|
|
2035
2066
|
type OptimizationLifecycleAction = 'pause' | 'resume' | 'finish' | 'fail' | 'cancel'
|
|
2067
|
+
const OPTIMIZATION_REPORT_MAX_BYTES = 2 * 1024 * 1024
|
|
2036
2068
|
|
|
2037
2069
|
function parseOptionalNumberFlag(name: string): number | undefined {
|
|
2038
2070
|
const value = getArg(name)
|
|
@@ -2052,6 +2084,46 @@ function hasObjectKeys(value: Record<string, unknown>): boolean {
|
|
|
2052
2084
|
return Object.keys(value).length > 0
|
|
2053
2085
|
}
|
|
2054
2086
|
|
|
2087
|
+
function readOptimizationReportInput(): { markdown: string; sourceName: string | null } | null {
|
|
2088
|
+
const report = getArg('--report')
|
|
2089
|
+
const reportFile = getArg('--report-file')
|
|
2090
|
+
|
|
2091
|
+
if (report && reportFile) {
|
|
2092
|
+
throw new Error('Use either --report or --report-file, not both')
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
if (!report && !reportFile) {
|
|
2096
|
+
return null
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
let markdown: string
|
|
2100
|
+
let sourceName: string | null
|
|
2101
|
+
|
|
2102
|
+
if (reportFile) {
|
|
2103
|
+
const expandedPath = expandHomePath(reportFile)
|
|
2104
|
+
markdown = readSourceFile(reportFile)
|
|
2105
|
+
sourceName = basename(expandedPath)
|
|
2106
|
+
} else if (report?.startsWith('@')) {
|
|
2107
|
+
const path = report.slice(1)
|
|
2108
|
+
const expandedPath = expandHomePath(path)
|
|
2109
|
+
markdown = readSourceFile(path)
|
|
2110
|
+
sourceName = basename(expandedPath)
|
|
2111
|
+
} else {
|
|
2112
|
+
markdown = report || ''
|
|
2113
|
+
sourceName = 'inline'
|
|
2114
|
+
}
|
|
2115
|
+
|
|
2116
|
+
if (!markdown.trim()) {
|
|
2117
|
+
throw new Error('Optimization report markdown must not be blank')
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
if (Buffer.byteLength(markdown, 'utf8') > OPTIMIZATION_REPORT_MAX_BYTES) {
|
|
2121
|
+
throw new Error(`Optimization report exceeds ${OPTIMIZATION_REPORT_MAX_BYTES} bytes`)
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
return { markdown, sourceName }
|
|
2125
|
+
}
|
|
2126
|
+
|
|
2055
2127
|
async function startOptimizationRun() {
|
|
2056
2128
|
const project = getArg('--project') || await resolveProjectSlug(null)
|
|
2057
2129
|
const optimizerVersionId = getArg('--optimizer-version')
|
|
@@ -2156,6 +2228,11 @@ async function updateOptimizationRunLifecycle(action: OptimizationLifecycleActio
|
|
|
2156
2228
|
}
|
|
2157
2229
|
const metadata = readJsonObjectArg(getArg('--metadata'), 'optimization metadata')
|
|
2158
2230
|
const reason = getArg('--reason')
|
|
2231
|
+
const report = readOptimizationReportInput()
|
|
2232
|
+
|
|
2233
|
+
if (report && (action === 'pause' || action === 'resume')) {
|
|
2234
|
+
throw new Error(`orizu optimizations ${action} does not accept optimization reports`)
|
|
2235
|
+
}
|
|
2159
2236
|
|
|
2160
2237
|
if ((action === 'pause' || action === 'cancel') && reason) {
|
|
2161
2238
|
metadata.reason = reason
|
|
@@ -2170,6 +2247,11 @@ async function updateOptimizationRunLifecycle(action: OptimizationLifecycleActio
|
|
|
2170
2247
|
body.metadata = metadata
|
|
2171
2248
|
}
|
|
2172
2249
|
|
|
2250
|
+
if (report) {
|
|
2251
|
+
body.reportMarkdown = report.markdown
|
|
2252
|
+
body.reportSourceName = report.sourceName
|
|
2253
|
+
}
|
|
2254
|
+
|
|
2173
2255
|
if (action === 'finish') {
|
|
2174
2256
|
const bestScore = parseOptionalNumberFlag('--best-score')
|
|
2175
2257
|
if (bestScore !== undefined) {
|
|
@@ -3023,6 +3105,28 @@ interface AppDetailPayload {
|
|
|
3023
3105
|
projectName: string
|
|
3024
3106
|
}
|
|
3025
3107
|
|
|
3108
|
+
interface AppExportPayload {
|
|
3109
|
+
app: {
|
|
3110
|
+
id: string
|
|
3111
|
+
name: string
|
|
3112
|
+
}
|
|
3113
|
+
version: {
|
|
3114
|
+
id: string
|
|
3115
|
+
versionNum: number
|
|
3116
|
+
createdAt?: string
|
|
3117
|
+
code: string
|
|
3118
|
+
}
|
|
3119
|
+
}
|
|
3120
|
+
|
|
3121
|
+
function defaultAppExportFilename(appName: string, versionNum: number): string {
|
|
3122
|
+
const safeName = appName
|
|
3123
|
+
.trim()
|
|
3124
|
+
.replace(/[^A-Za-z0-9._-]+/g, '_')
|
|
3125
|
+
.replace(/^_+|_+$/g, '')
|
|
3126
|
+
.slice(0, 100)
|
|
3127
|
+
return `${safeName || 'app'}.v${versionNum}.tsx`
|
|
3128
|
+
}
|
|
3129
|
+
|
|
3026
3130
|
async function appDetail() {
|
|
3027
3131
|
const appId = getArg('--app')
|
|
3028
3132
|
const project = getArg('--project')
|
|
@@ -3068,6 +3172,50 @@ async function appDetail() {
|
|
|
3068
3172
|
printLine(` Updated: ${sanitizeTerminalText(detail.updatedAt)}`)
|
|
3069
3173
|
}
|
|
3070
3174
|
|
|
3175
|
+
async function exportAppSource() {
|
|
3176
|
+
const project = getArg('--project')
|
|
3177
|
+
const versionArg = getArg('--version')
|
|
3178
|
+
const outPathArg = getArg('--out')
|
|
3179
|
+
let appId = getArg('--app')
|
|
3180
|
+
|
|
3181
|
+
const versionNum = versionArg && /^[1-9]\d*$/.test(versionArg)
|
|
3182
|
+
? Number.parseInt(versionArg, 10)
|
|
3183
|
+
: undefined
|
|
3184
|
+
|
|
3185
|
+
if (versionArg && versionNum === undefined) {
|
|
3186
|
+
throw new Error('--version must be a positive integer')
|
|
3187
|
+
}
|
|
3188
|
+
|
|
3189
|
+
if (!appId) {
|
|
3190
|
+
const selected = await selectAppIdInteractively(project)
|
|
3191
|
+
appId = selected.appId
|
|
3192
|
+
}
|
|
3193
|
+
|
|
3194
|
+
const query = new URLSearchParams()
|
|
3195
|
+
if (project) {
|
|
3196
|
+
query.set('project', project)
|
|
3197
|
+
}
|
|
3198
|
+
if (versionNum) {
|
|
3199
|
+
query.set('version', String(versionNum))
|
|
3200
|
+
}
|
|
3201
|
+
|
|
3202
|
+
const suffix = query.toString() ? `?${query.toString()}` : ''
|
|
3203
|
+
const response = await authedFetch(`/api/cli/apps/${encodeURIComponent(appId)}/export${suffix}`)
|
|
3204
|
+
if (!response.ok) {
|
|
3205
|
+
throw new Error(`Failed to export app (${response.status}): ${await response.text()}`)
|
|
3206
|
+
}
|
|
3207
|
+
|
|
3208
|
+
const data = await parseJsonResponse<AppExportPayload>(response, 'App export')
|
|
3209
|
+
const filename = outPathArg
|
|
3210
|
+
? expandHomePath(outPathArg)
|
|
3211
|
+
: defaultAppExportFilename(data.app.name, data.version.versionNum)
|
|
3212
|
+
|
|
3213
|
+
writeTextFileEnsuringDir(filename, data.version.code)
|
|
3214
|
+
printLine(
|
|
3215
|
+
`Saved app ${sanitizeTerminalText(data.app.name)} v${data.version.versionNum} source to ${sanitizeTerminalText(filename)}`
|
|
3216
|
+
)
|
|
3217
|
+
}
|
|
3218
|
+
|
|
3071
3219
|
async function listTeamMembers() {
|
|
3072
3220
|
const teamSlug = await resolveTeamSlug(getArg('--team'))
|
|
3073
3221
|
|
|
@@ -3154,7 +3302,8 @@ async function createDatasetFromRows(
|
|
|
3154
3302
|
project: string,
|
|
3155
3303
|
name: string,
|
|
3156
3304
|
sourceType: DatasetUploadSourceType,
|
|
3157
|
-
rows: Array<Record<string, unknown
|
|
3305
|
+
rows: Array<Record<string, unknown>>,
|
|
3306
|
+
readmeMarkdown: string | null = null
|
|
3158
3307
|
): Promise<DatasetUploadResponse> {
|
|
3159
3308
|
const response = await authedFetch('/api/cli/datasets/upload', {
|
|
3160
3309
|
method: 'POST',
|
|
@@ -3166,6 +3315,7 @@ async function createDatasetFromRows(
|
|
|
3166
3315
|
name,
|
|
3167
3316
|
rows,
|
|
3168
3317
|
sourceType,
|
|
3318
|
+
...(readmeMarkdown !== null ? { readmeMarkdown } : {}),
|
|
3169
3319
|
}),
|
|
3170
3320
|
})
|
|
3171
3321
|
|
|
@@ -3180,7 +3330,8 @@ async function createDatasetFromRows(
|
|
|
3180
3330
|
async function uploadJsonlDatasetInChunks(
|
|
3181
3331
|
file: string,
|
|
3182
3332
|
project: string,
|
|
3183
|
-
datasetName: string
|
|
3333
|
+
datasetName: string,
|
|
3334
|
+
readmeMarkdown: string | null = null
|
|
3184
3335
|
) {
|
|
3185
3336
|
let dataset: DatasetUploadResponse['dataset'] | null = null
|
|
3186
3337
|
let totalUploaded = 0
|
|
@@ -3215,7 +3366,7 @@ async function uploadJsonlDatasetInChunks(
|
|
|
3215
3366
|
|
|
3216
3367
|
try {
|
|
3217
3368
|
if (!dataset) {
|
|
3218
|
-
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk)
|
|
3369
|
+
const data = await createDatasetFromRows(project, datasetName, 'jsonl', chunk, readmeMarkdown)
|
|
3219
3370
|
dataset = data.dataset
|
|
3220
3371
|
totalUploaded = data.dataset.rowCount
|
|
3221
3372
|
continue
|
|
@@ -3256,22 +3407,24 @@ async function uploadDataset() {
|
|
|
3256
3407
|
const projectArg = getArg('--project')
|
|
3257
3408
|
const fileArg = getArg('--file')
|
|
3258
3409
|
const name = getArg('--name')
|
|
3410
|
+
const usage = 'Usage: orizu datasets upload --file <path> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>]'
|
|
3259
3411
|
|
|
3260
3412
|
if (!fileArg) {
|
|
3261
|
-
throw new Error(
|
|
3413
|
+
throw new Error(usage)
|
|
3262
3414
|
}
|
|
3263
3415
|
|
|
3416
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage)
|
|
3264
3417
|
const file = expandHomePath(fileArg)
|
|
3265
3418
|
const project = await resolveProjectSlug(projectArg)
|
|
3266
3419
|
const datasetName = name || basename(file)
|
|
3267
3420
|
|
|
3268
3421
|
if (extname(file).toLowerCase() === '.jsonl') {
|
|
3269
|
-
await uploadJsonlDatasetInChunks(file, project, datasetName)
|
|
3422
|
+
await uploadJsonlDatasetInChunks(file, project, datasetName, readmeMarkdown)
|
|
3270
3423
|
return
|
|
3271
3424
|
}
|
|
3272
3425
|
|
|
3273
3426
|
const { rows, sourceType } = parseDatasetFile(file)
|
|
3274
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows)
|
|
3427
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown)
|
|
3275
3428
|
|
|
3276
3429
|
printLine(`Uploaded dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) with ${data.dataset.rowCount} rows.`)
|
|
3277
3430
|
if (data.dataset.url) {
|
|
@@ -3283,23 +3436,29 @@ async function pushDataset() {
|
|
|
3283
3436
|
const projectArg = getArg('--project')
|
|
3284
3437
|
const fileArg = getPositionalArg(2) || getArg('--file')
|
|
3285
3438
|
const name = getArg('--name')
|
|
3439
|
+
const usage = 'Usage: orizu datasets push <rows.csv|rows.json|rows.jsonl> [--project <team/project>] [--name <name>] [--readme-file <README.md> | --readme-text <markdown>] [--json]'
|
|
3286
3440
|
|
|
3287
3441
|
if (!fileArg) {
|
|
3288
|
-
throw new Error(
|
|
3442
|
+
throw new Error(usage)
|
|
3289
3443
|
}
|
|
3290
3444
|
|
|
3445
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage)
|
|
3291
3446
|
const file = expandHomePath(fileArg)
|
|
3292
3447
|
const project = await resolveProjectSlug(projectArg)
|
|
3293
3448
|
const datasetName = name || basename(file)
|
|
3294
3449
|
const { rows, sourceType } = parseDatasetFile(file)
|
|
3295
|
-
const data = await createDatasetFromRows(project, datasetName, sourceType, rows)
|
|
3450
|
+
const data = await createDatasetFromRows(project, datasetName, sourceType, rows, readmeMarkdown)
|
|
3296
3451
|
|
|
3297
3452
|
if (hasJsonFlag()) {
|
|
3298
|
-
|
|
3453
|
+
const payload: Record<string, unknown> = {
|
|
3299
3454
|
dataset_id: data.dataset.id,
|
|
3300
3455
|
name: data.dataset.name,
|
|
3301
3456
|
row_count: data.dataset.rowCount,
|
|
3302
|
-
}
|
|
3457
|
+
}
|
|
3458
|
+
if (data.readmeVersion?.id) {
|
|
3459
|
+
payload.readme_version_id = data.readmeVersion.id
|
|
3460
|
+
}
|
|
3461
|
+
printJson(payload)
|
|
3303
3462
|
return
|
|
3304
3463
|
}
|
|
3305
3464
|
|
|
@@ -3309,6 +3468,55 @@ async function pushDataset() {
|
|
|
3309
3468
|
}
|
|
3310
3469
|
}
|
|
3311
3470
|
|
|
3471
|
+
async function setDatasetReadme() {
|
|
3472
|
+
const datasetId = getPositionalArg(3) || getArg('--dataset')
|
|
3473
|
+
const project = getArg('--project')
|
|
3474
|
+
const usage = 'Usage: orizu datasets readme set <datasetId|dataset-name> [--project <team/project>] (--readme-file <README.md> | --readme-text <markdown>) [--json]'
|
|
3475
|
+
|
|
3476
|
+
if (!datasetId) {
|
|
3477
|
+
throw new Error(usage)
|
|
3478
|
+
}
|
|
3479
|
+
|
|
3480
|
+
const readmeMarkdown = readReadmeMarkdownFromArgs(usage)
|
|
3481
|
+
if (readmeMarkdown === null) {
|
|
3482
|
+
throw new Error(usage)
|
|
3483
|
+
}
|
|
3484
|
+
|
|
3485
|
+
const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project)
|
|
3486
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/readme`, {
|
|
3487
|
+
method: 'POST',
|
|
3488
|
+
headers: { 'Content-Type': 'application/json' },
|
|
3489
|
+
body: JSON.stringify({ markdown: readmeMarkdown }),
|
|
3490
|
+
})
|
|
3491
|
+
|
|
3492
|
+
if (!response.ok) {
|
|
3493
|
+
throw new Error(`Failed to save dataset README: ${await response.text()}`)
|
|
3494
|
+
}
|
|
3495
|
+
|
|
3496
|
+
const data = await parseJsonResponse<{
|
|
3497
|
+
version: {
|
|
3498
|
+
id: string
|
|
3499
|
+
dataset_id: string
|
|
3500
|
+
version_num: number
|
|
3501
|
+
created_at: string
|
|
3502
|
+
}
|
|
3503
|
+
}>(response, 'Dataset README save')
|
|
3504
|
+
|
|
3505
|
+
if (hasJsonFlag()) {
|
|
3506
|
+
printJson({
|
|
3507
|
+
dataset_id: data.version.dataset_id,
|
|
3508
|
+
readme_version_id: data.version.id,
|
|
3509
|
+
readme_version_num: data.version.version_num,
|
|
3510
|
+
})
|
|
3511
|
+
return
|
|
3512
|
+
}
|
|
3513
|
+
|
|
3514
|
+
printLine(
|
|
3515
|
+
`Saved README v${data.version.version_num} for dataset ` +
|
|
3516
|
+
`${sanitizeTerminalText(data.version.dataset_id)} (${sanitizeTerminalText(data.version.id)}).`
|
|
3517
|
+
)
|
|
3518
|
+
}
|
|
3519
|
+
|
|
3312
3520
|
function getDatasetReferenceInput(): string | null {
|
|
3313
3521
|
const fromFlag = getArg('--dataset')
|
|
3314
3522
|
if (fromFlag) {
|
|
@@ -4259,6 +4467,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
4259
4467
|
return
|
|
4260
4468
|
}
|
|
4261
4469
|
|
|
4470
|
+
if (command === 'apps' && subcommand === 'export') {
|
|
4471
|
+
await exportAppSource()
|
|
4472
|
+
return
|
|
4473
|
+
}
|
|
4474
|
+
|
|
4262
4475
|
if (command === 'tasks' && subcommand === 'list') {
|
|
4263
4476
|
await listTasks()
|
|
4264
4477
|
return
|
|
@@ -4310,6 +4523,11 @@ export async function main(rawArgs = process.argv.slice(2)) {
|
|
|
4310
4523
|
}
|
|
4311
4524
|
|
|
4312
4525
|
const datasetsAction = cliArgs[2]
|
|
4526
|
+
if (command === 'datasets' && subcommand === 'readme' && datasetsAction === 'set') {
|
|
4527
|
+
await setDatasetReadme()
|
|
4528
|
+
return
|
|
4529
|
+
}
|
|
4530
|
+
|
|
4313
4531
|
if (command === 'datasets' && subcommand === 'versions' && datasetsAction === 'create') {
|
|
4314
4532
|
await createDatasetVersion()
|
|
4315
4533
|
return
|