orizu 0.0.7 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/http.js +11 -0
- package/dist/index.js +105 -19
- package/package.json +1 -1
- package/src/http.ts +14 -0
- package/src/index.ts +149 -19
package/dist/http.js
CHANGED
|
@@ -19,6 +19,17 @@ export function resolveBaseUrl(flags = runtimeFlags) {
|
|
|
19
19
|
}
|
|
20
20
|
return 'https://orizu.ai';
|
|
21
21
|
}
|
|
22
|
+
export function resolveLoginBaseUrl(flags = runtimeFlags) {
|
|
23
|
+
const fromFlags = getFlagBaseUrl(flags);
|
|
24
|
+
if (fromFlags) {
|
|
25
|
+
return fromFlags;
|
|
26
|
+
}
|
|
27
|
+
const fromEnv = process.env.ORIZU_BASE_URL;
|
|
28
|
+
if (fromEnv) {
|
|
29
|
+
return normalizeBaseUrl(fromEnv);
|
|
30
|
+
}
|
|
31
|
+
return 'https://orizu.ai';
|
|
32
|
+
}
|
|
22
33
|
export function getBaseUrl() {
|
|
23
34
|
return resolveBaseUrl();
|
|
24
35
|
}
|
package/dist/index.js
CHANGED
|
@@ -10,9 +10,9 @@ import { clearServerCredentials, getServerCredentials, saveServerCredentials } f
|
|
|
10
10
|
import { parseDatasetFile } from './file-parser.js';
|
|
11
11
|
import { parseDatasetReference } from './dataset-download.js';
|
|
12
12
|
import { parseGlobalFlags } from './global-flags.js';
|
|
13
|
-
import { authedFetch, getBaseUrl, setGlobalFlags } from './http.js';
|
|
13
|
+
import { authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags } from './http.js';
|
|
14
14
|
function printUsage() {
|
|
15
|
-
console.log(`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\norizu commands:\n\n orizu login\n orizu logout\n orizu whoami\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 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 tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userId1,userId2> [--instructions <text>] [--labels-per-item <n>]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\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
|
|
15
|
+
console.log(`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\norizu commands:\n\n orizu login\n orizu logout\n orizu whoami\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 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 tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userId1,userId2> [--instructions <text>] [--labels-per-item <n>]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\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 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>]`);
|
|
16
16
|
}
|
|
17
17
|
let cliArgs = process.argv.slice(2);
|
|
18
18
|
function getArg(name) {
|
|
@@ -341,7 +341,7 @@ function printTaskStatusSummary(data) {
|
|
|
341
341
|
}
|
|
342
342
|
}
|
|
343
343
|
async function login() {
|
|
344
|
-
const baseUrl =
|
|
344
|
+
const baseUrl = resolveLoginBaseUrl();
|
|
345
345
|
const codeVerifier = createCodeVerifier();
|
|
346
346
|
const codeChallenge = createCodeChallenge(codeVerifier);
|
|
347
347
|
const callbackCode = await new Promise((resolve, reject) => {
|
|
@@ -861,28 +861,57 @@ async function appendDatasetRows() {
|
|
|
861
861
|
const data = await parseJsonResponse(response, 'Dataset append');
|
|
862
862
|
console.log(`Appended ${data.appendedCount} rows to dataset ${data.dataset.name} (${data.dataset.id}). New row count: ${data.dataset.rowCount}`);
|
|
863
863
|
}
|
|
864
|
-
function
|
|
865
|
-
|
|
866
|
-
|
|
864
|
+
async function editDatasetRows() {
|
|
865
|
+
const projectArg = getArg('--project');
|
|
866
|
+
const datasetInput = getDatasetReferenceInput();
|
|
867
|
+
const fileArg = getArg('--file');
|
|
868
|
+
if (!fileArg) {
|
|
869
|
+
throw new Error('Usage: orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>');
|
|
867
870
|
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
.filter(Boolean);
|
|
872
|
-
const parsed = rawItems.map(item => Number(item));
|
|
873
|
-
const invalid = parsed.some(item => !Number.isInteger(item) || item < 0);
|
|
874
|
-
if (invalid) {
|
|
875
|
-
throw new Error('row-indices must be comma-separated non-negative integers');
|
|
871
|
+
let datasetId;
|
|
872
|
+
if (datasetInput) {
|
|
873
|
+
datasetId = parseDatasetReference(datasetInput).datasetId;
|
|
876
874
|
}
|
|
877
|
-
|
|
875
|
+
else {
|
|
876
|
+
const selected = await selectDatasetInteractively(projectArg);
|
|
877
|
+
datasetId = selected.datasetId;
|
|
878
|
+
}
|
|
879
|
+
const file = expandHomePath(fileArg);
|
|
880
|
+
const { rows } = parseDatasetFile(file);
|
|
881
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
882
|
+
throw new Error('Dataset edit file must contain at least one row');
|
|
883
|
+
}
|
|
884
|
+
const normalizedRows = rows.map((row, index) => {
|
|
885
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row)) {
|
|
886
|
+
throw new Error(`Dataset edit file rows[${index}] must be an object`);
|
|
887
|
+
}
|
|
888
|
+
const rowRecord = row;
|
|
889
|
+
const rowId = typeof rowRecord.id === 'string' ? rowRecord.id.trim() : '';
|
|
890
|
+
if (!rowId) {
|
|
891
|
+
throw new Error(`Dataset edit file rows[${index}] must include a non-empty string id`);
|
|
892
|
+
}
|
|
893
|
+
return {
|
|
894
|
+
...rowRecord,
|
|
895
|
+
id: rowId,
|
|
896
|
+
};
|
|
897
|
+
});
|
|
898
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/rows`, {
|
|
899
|
+
method: 'PATCH',
|
|
900
|
+
headers: { 'Content-Type': 'application/json' },
|
|
901
|
+
body: JSON.stringify({ rows: normalizedRows }),
|
|
902
|
+
});
|
|
903
|
+
if (!response.ok) {
|
|
904
|
+
throw new Error(`Edit rows failed: ${await response.text()}`);
|
|
905
|
+
}
|
|
906
|
+
const data = await parseJsonResponse(response, 'Dataset edit rows');
|
|
907
|
+
console.log(`Updated ${data.updatedCount} rows in dataset ${data.dataset.name} (${data.dataset.id}). Current row count: ${data.dataset.rowCount}`);
|
|
878
908
|
}
|
|
879
909
|
async function deleteDatasetRows() {
|
|
880
910
|
const projectArg = getArg('--project');
|
|
881
911
|
const datasetInput = getDatasetReferenceInput();
|
|
882
912
|
const rowIds = parseCommaSeparated(getArg('--row-ids'));
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
throw new Error('Usage: orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] [--row-ids <id1,id2>] [--row-indices <n1,n2>]');
|
|
913
|
+
if (rowIds.length === 0) {
|
|
914
|
+
throw new Error('Usage: orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>');
|
|
886
915
|
}
|
|
887
916
|
let datasetId;
|
|
888
917
|
if (datasetInput) {
|
|
@@ -897,7 +926,6 @@ async function deleteDatasetRows() {
|
|
|
897
926
|
headers: { 'Content-Type': 'application/json' },
|
|
898
927
|
body: JSON.stringify({
|
|
899
928
|
rowIds,
|
|
900
|
-
rowIndices,
|
|
901
929
|
}),
|
|
902
930
|
});
|
|
903
931
|
if (!response.ok) {
|
|
@@ -906,6 +934,52 @@ async function deleteDatasetRows() {
|
|
|
906
934
|
const data = await parseJsonResponse(response, 'Dataset delete rows');
|
|
907
935
|
console.log(`Deleted ${data.deletedCount} rows from dataset ${data.dataset.name} (${data.dataset.id}). New row count: ${data.dataset.rowCount}`);
|
|
908
936
|
}
|
|
937
|
+
async function lockDataset() {
|
|
938
|
+
const projectArg = getArg('--project');
|
|
939
|
+
const datasetInput = getDatasetReferenceInput();
|
|
940
|
+
const reason = getArg('--reason');
|
|
941
|
+
let datasetId;
|
|
942
|
+
if (datasetInput) {
|
|
943
|
+
datasetId = parseDatasetReference(datasetInput).datasetId;
|
|
944
|
+
}
|
|
945
|
+
else {
|
|
946
|
+
const selected = await selectDatasetInteractively(projectArg);
|
|
947
|
+
datasetId = selected.datasetId;
|
|
948
|
+
}
|
|
949
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/lock`, {
|
|
950
|
+
method: 'POST',
|
|
951
|
+
headers: { 'Content-Type': 'application/json' },
|
|
952
|
+
body: JSON.stringify(reason ? { reason } : {}),
|
|
953
|
+
});
|
|
954
|
+
if (!response.ok) {
|
|
955
|
+
throw new Error(`Lock failed: ${await response.text()}`);
|
|
956
|
+
}
|
|
957
|
+
const data = await parseJsonResponse(response, 'Dataset lock');
|
|
958
|
+
console.log(`Locked dataset ${data.dataset.name} (${data.dataset.id}) at ${data.dataset.lockedAt}. Row count: ${data.dataset.rowCount}`);
|
|
959
|
+
}
|
|
960
|
+
async function cloneDataset() {
|
|
961
|
+
const projectArg = getArg('--project');
|
|
962
|
+
const datasetInput = getDatasetReferenceInput();
|
|
963
|
+
const name = getArg('--name');
|
|
964
|
+
let datasetId;
|
|
965
|
+
if (datasetInput) {
|
|
966
|
+
datasetId = parseDatasetReference(datasetInput).datasetId;
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
const selected = await selectDatasetInteractively(projectArg);
|
|
970
|
+
datasetId = selected.datasetId;
|
|
971
|
+
}
|
|
972
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/clone`, {
|
|
973
|
+
method: 'POST',
|
|
974
|
+
headers: { 'Content-Type': 'application/json' },
|
|
975
|
+
body: JSON.stringify(name ? { name } : {}),
|
|
976
|
+
});
|
|
977
|
+
if (!response.ok) {
|
|
978
|
+
throw new Error(`Clone failed: ${await response.text()}`);
|
|
979
|
+
}
|
|
980
|
+
const data = await parseJsonResponse(response, 'Dataset clone');
|
|
981
|
+
console.log(`Cloned dataset ${data.dataset.parentDatasetId} -> ${data.dataset.name} (${data.dataset.id}). Row count: ${data.dataset.rowCount}`);
|
|
982
|
+
}
|
|
909
983
|
async function downloadAnnotations() {
|
|
910
984
|
let taskId = getArg('--task');
|
|
911
985
|
const format = (getArg('--format') || 'jsonl');
|
|
@@ -1027,10 +1101,22 @@ async function main() {
|
|
|
1027
1101
|
await appendDatasetRows();
|
|
1028
1102
|
return;
|
|
1029
1103
|
}
|
|
1104
|
+
if (command === 'datasets' && subcommand === 'edit-rows') {
|
|
1105
|
+
await editDatasetRows();
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1030
1108
|
if (command === 'datasets' && subcommand === 'delete-rows') {
|
|
1031
1109
|
await deleteDatasetRows();
|
|
1032
1110
|
return;
|
|
1033
1111
|
}
|
|
1112
|
+
if (command === 'datasets' && subcommand === 'lock') {
|
|
1113
|
+
await lockDataset();
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
if (command === 'datasets' && subcommand === 'clone') {
|
|
1117
|
+
await cloneDataset();
|
|
1118
|
+
return;
|
|
1119
|
+
}
|
|
1034
1120
|
if (command === 'tasks' && subcommand === 'export') {
|
|
1035
1121
|
await downloadAnnotations();
|
|
1036
1122
|
return;
|
package/package.json
CHANGED
package/src/http.ts
CHANGED
|
@@ -27,6 +27,20 @@ export function resolveBaseUrl(flags: GlobalFlags = runtimeFlags): string {
|
|
|
27
27
|
return 'https://orizu.ai'
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export function resolveLoginBaseUrl(flags: GlobalFlags = runtimeFlags): string {
|
|
31
|
+
const fromFlags = getFlagBaseUrl(flags)
|
|
32
|
+
if (fromFlags) {
|
|
33
|
+
return fromFlags
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const fromEnv = process.env.ORIZU_BASE_URL
|
|
37
|
+
if (fromEnv) {
|
|
38
|
+
return normalizeBaseUrl(fromEnv)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return 'https://orizu.ai'
|
|
42
|
+
}
|
|
43
|
+
|
|
30
44
|
export function getBaseUrl(): string {
|
|
31
45
|
return resolveBaseUrl()
|
|
32
46
|
}
|
package/src/index.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { clearServerCredentials, getServerCredentials, saveServerCredentials } f
|
|
|
10
10
|
import { parseDatasetFile } from './file-parser.js'
|
|
11
11
|
import { parseDatasetReference } from './dataset-download.js'
|
|
12
12
|
import { parseGlobalFlags } from './global-flags.js'
|
|
13
|
-
import { authedFetch, getBaseUrl, setGlobalFlags } from './http.js'
|
|
13
|
+
import { authedFetch, getBaseUrl, resolveLoginBaseUrl, setGlobalFlags } from './http.js'
|
|
14
14
|
import { LoginResponse } from './types.js'
|
|
15
15
|
|
|
16
16
|
interface Team {
|
|
@@ -106,7 +106,7 @@ interface TaskStatusPayload {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
function printUsage() {
|
|
109
|
-
console.log(`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\norizu commands:\n\n orizu login\n orizu logout\n orizu whoami\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 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 tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userId1,userId2> [--instructions <text>] [--labels-per-item <n>]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\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
|
|
109
|
+
console.log(`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\norizu commands:\n\n orizu login\n orizu logout\n orizu whoami\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 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 tasks list [--project <team/project>]\n orizu tasks create --project <team/project> --dataset <datasetId> --app <appId> --title <title> --assignees <userId1,userId2> [--instructions <text>] [--labels-per-item <n>]\n orizu tasks assign --task <taskId> --assignees <userId1,userId2>\n orizu tasks status --task <taskId> [--json]\n orizu datasets upload --file <path> [--project <team/project>] [--name <name>]\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 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>]`)
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
let cliArgs = process.argv.slice(2)
|
|
@@ -594,7 +594,7 @@ function printTaskStatusSummary(data: TaskStatusPayload) {
|
|
|
594
594
|
}
|
|
595
595
|
|
|
596
596
|
async function login() {
|
|
597
|
-
const baseUrl =
|
|
597
|
+
const baseUrl = resolveLoginBaseUrl()
|
|
598
598
|
const codeVerifier = createCodeVerifier()
|
|
599
599
|
const codeChallenge = createCodeChallenge(codeVerifier)
|
|
600
600
|
|
|
@@ -1265,32 +1265,73 @@ async function appendDatasetRows() {
|
|
|
1265
1265
|
)
|
|
1266
1266
|
}
|
|
1267
1267
|
|
|
1268
|
-
function
|
|
1269
|
-
|
|
1270
|
-
|
|
1268
|
+
async function editDatasetRows() {
|
|
1269
|
+
const projectArg = getArg('--project')
|
|
1270
|
+
const datasetInput = getDatasetReferenceInput()
|
|
1271
|
+
const fileArg = getArg('--file')
|
|
1272
|
+
|
|
1273
|
+
if (!fileArg) {
|
|
1274
|
+
throw new Error('Usage: orizu datasets edit-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --file <path>')
|
|
1271
1275
|
}
|
|
1272
1276
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
if (invalid) {
|
|
1280
|
-
throw new Error('row-indices must be comma-separated non-negative integers')
|
|
1277
|
+
let datasetId: string
|
|
1278
|
+
if (datasetInput) {
|
|
1279
|
+
datasetId = parseDatasetReference(datasetInput).datasetId
|
|
1280
|
+
} else {
|
|
1281
|
+
const selected = await selectDatasetInteractively(projectArg)
|
|
1282
|
+
datasetId = selected.datasetId
|
|
1281
1283
|
}
|
|
1282
1284
|
|
|
1283
|
-
|
|
1285
|
+
const file = expandHomePath(fileArg)
|
|
1286
|
+
const { rows } = parseDatasetFile(file)
|
|
1287
|
+
if (!Array.isArray(rows) || rows.length === 0) {
|
|
1288
|
+
throw new Error('Dataset edit file must contain at least one row')
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const normalizedRows = rows.map((row, index) => {
|
|
1292
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row)) {
|
|
1293
|
+
throw new Error(`Dataset edit file rows[${index}] must be an object`)
|
|
1294
|
+
}
|
|
1295
|
+
|
|
1296
|
+
const rowRecord = row as Record<string, unknown>
|
|
1297
|
+
const rowId = typeof rowRecord.id === 'string' ? rowRecord.id.trim() : ''
|
|
1298
|
+
if (!rowId) {
|
|
1299
|
+
throw new Error(`Dataset edit file rows[${index}] must include a non-empty string id`)
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
return {
|
|
1303
|
+
...rowRecord,
|
|
1304
|
+
id: rowId,
|
|
1305
|
+
}
|
|
1306
|
+
})
|
|
1307
|
+
|
|
1308
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/rows`, {
|
|
1309
|
+
method: 'PATCH',
|
|
1310
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1311
|
+
body: JSON.stringify({ rows: normalizedRows }),
|
|
1312
|
+
})
|
|
1313
|
+
|
|
1314
|
+
if (!response.ok) {
|
|
1315
|
+
throw new Error(`Edit rows failed: ${await response.text()}`)
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
const data = await parseJsonResponse<{
|
|
1319
|
+
dataset: { id: string; name: string; rowCount: number }
|
|
1320
|
+
updatedCount: number
|
|
1321
|
+
}>(response, 'Dataset edit rows')
|
|
1322
|
+
|
|
1323
|
+
console.log(
|
|
1324
|
+
`Updated ${data.updatedCount} rows in dataset ${data.dataset.name} (${data.dataset.id}). Current row count: ${data.dataset.rowCount}`
|
|
1325
|
+
)
|
|
1284
1326
|
}
|
|
1285
1327
|
|
|
1286
1328
|
async function deleteDatasetRows() {
|
|
1287
1329
|
const projectArg = getArg('--project')
|
|
1288
1330
|
const datasetInput = getDatasetReferenceInput()
|
|
1289
1331
|
const rowIds = parseCommaSeparated(getArg('--row-ids'))
|
|
1290
|
-
const rowIndices = parseCommaSeparatedIntegers(getArg('--row-indices'))
|
|
1291
1332
|
|
|
1292
|
-
if (rowIds.length === 0
|
|
1293
|
-
throw new Error('Usage: orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>]
|
|
1333
|
+
if (rowIds.length === 0) {
|
|
1334
|
+
throw new Error('Usage: orizu datasets delete-rows [--dataset <datasetId|datasetUrl>] [--project <team/project>] --row-ids <id1,id2>')
|
|
1294
1335
|
}
|
|
1295
1336
|
|
|
1296
1337
|
let datasetId: string
|
|
@@ -1306,7 +1347,6 @@ async function deleteDatasetRows() {
|
|
|
1306
1347
|
headers: { 'Content-Type': 'application/json' },
|
|
1307
1348
|
body: JSON.stringify({
|
|
1308
1349
|
rowIds,
|
|
1309
|
-
rowIndices,
|
|
1310
1350
|
}),
|
|
1311
1351
|
})
|
|
1312
1352
|
|
|
@@ -1324,6 +1364,81 @@ async function deleteDatasetRows() {
|
|
|
1324
1364
|
)
|
|
1325
1365
|
}
|
|
1326
1366
|
|
|
1367
|
+
async function lockDataset() {
|
|
1368
|
+
const projectArg = getArg('--project')
|
|
1369
|
+
const datasetInput = getDatasetReferenceInput()
|
|
1370
|
+
const reason = getArg('--reason')
|
|
1371
|
+
|
|
1372
|
+
let datasetId: string
|
|
1373
|
+
if (datasetInput) {
|
|
1374
|
+
datasetId = parseDatasetReference(datasetInput).datasetId
|
|
1375
|
+
} else {
|
|
1376
|
+
const selected = await selectDatasetInteractively(projectArg)
|
|
1377
|
+
datasetId = selected.datasetId
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/lock`, {
|
|
1381
|
+
method: 'POST',
|
|
1382
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1383
|
+
body: JSON.stringify(reason ? { reason } : {}),
|
|
1384
|
+
})
|
|
1385
|
+
|
|
1386
|
+
if (!response.ok) {
|
|
1387
|
+
throw new Error(`Lock failed: ${await response.text()}`)
|
|
1388
|
+
}
|
|
1389
|
+
|
|
1390
|
+
const data = await parseJsonResponse<{
|
|
1391
|
+
dataset: {
|
|
1392
|
+
id: string
|
|
1393
|
+
name: string
|
|
1394
|
+
rowCount: number
|
|
1395
|
+
lockedAt: string
|
|
1396
|
+
lockedBy: string | null
|
|
1397
|
+
}
|
|
1398
|
+
}>(response, 'Dataset lock')
|
|
1399
|
+
|
|
1400
|
+
console.log(
|
|
1401
|
+
`Locked dataset ${data.dataset.name} (${data.dataset.id}) at ${data.dataset.lockedAt}. Row count: ${data.dataset.rowCount}`
|
|
1402
|
+
)
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
async function cloneDataset() {
|
|
1406
|
+
const projectArg = getArg('--project')
|
|
1407
|
+
const datasetInput = getDatasetReferenceInput()
|
|
1408
|
+
const name = getArg('--name')
|
|
1409
|
+
|
|
1410
|
+
let datasetId: string
|
|
1411
|
+
if (datasetInput) {
|
|
1412
|
+
datasetId = parseDatasetReference(datasetInput).datasetId
|
|
1413
|
+
} else {
|
|
1414
|
+
const selected = await selectDatasetInteractively(projectArg)
|
|
1415
|
+
datasetId = selected.datasetId
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/clone`, {
|
|
1419
|
+
method: 'POST',
|
|
1420
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1421
|
+
body: JSON.stringify(name ? { name } : {}),
|
|
1422
|
+
})
|
|
1423
|
+
|
|
1424
|
+
if (!response.ok) {
|
|
1425
|
+
throw new Error(`Clone failed: ${await response.text()}`)
|
|
1426
|
+
}
|
|
1427
|
+
|
|
1428
|
+
const data = await parseJsonResponse<{
|
|
1429
|
+
dataset: {
|
|
1430
|
+
id: string
|
|
1431
|
+
name: string
|
|
1432
|
+
rowCount: number
|
|
1433
|
+
parentDatasetId: string
|
|
1434
|
+
}
|
|
1435
|
+
}>(response, 'Dataset clone')
|
|
1436
|
+
|
|
1437
|
+
console.log(
|
|
1438
|
+
`Cloned dataset ${data.dataset.parentDatasetId} -> ${data.dataset.name} (${data.dataset.id}). Row count: ${data.dataset.rowCount}`
|
|
1439
|
+
)
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1327
1442
|
async function downloadAnnotations() {
|
|
1328
1443
|
let taskId = getArg('--task')
|
|
1329
1444
|
const format = (getArg('--format') || 'jsonl') as 'csv' | 'json' | 'jsonl'
|
|
@@ -1474,11 +1589,26 @@ async function main() {
|
|
|
1474
1589
|
return
|
|
1475
1590
|
}
|
|
1476
1591
|
|
|
1592
|
+
if (command === 'datasets' && subcommand === 'edit-rows') {
|
|
1593
|
+
await editDatasetRows()
|
|
1594
|
+
return
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1477
1597
|
if (command === 'datasets' && subcommand === 'delete-rows') {
|
|
1478
1598
|
await deleteDatasetRows()
|
|
1479
1599
|
return
|
|
1480
1600
|
}
|
|
1481
1601
|
|
|
1602
|
+
if (command === 'datasets' && subcommand === 'lock') {
|
|
1603
|
+
await lockDataset()
|
|
1604
|
+
return
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
if (command === 'datasets' && subcommand === 'clone') {
|
|
1608
|
+
await cloneDataset()
|
|
1609
|
+
return
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1482
1612
|
if (command === 'tasks' && subcommand === 'export') {
|
|
1483
1613
|
await downloadAnnotations()
|
|
1484
1614
|
return
|