@2kw/ai 4.0.0-dev.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/LICENSE +19 -0
  2. package/README.md +91 -0
  3. package/dist/commands/ai.d.ts +3 -0
  4. package/dist/commands/ai.js +72 -0
  5. package/dist/commands/analytics.d.ts +3 -0
  6. package/dist/commands/analytics.js +161 -0
  7. package/dist/commands/auth.d.ts +3 -0
  8. package/dist/commands/auth.js +134 -0
  9. package/dist/commands/billing.d.ts +3 -0
  10. package/dist/commands/billing.js +102 -0
  11. package/dist/commands/config.d.ts +3 -0
  12. package/dist/commands/config.js +90 -0
  13. package/dist/commands/context.d.ts +3 -0
  14. package/dist/commands/context.js +149 -0
  15. package/dist/commands/convert.d.ts +3 -0
  16. package/dist/commands/convert.js +291 -0
  17. package/dist/commands/dataset-versions.d.ts +3 -0
  18. package/dist/commands/dataset-versions.js +90 -0
  19. package/dist/commands/datasets.d.ts +3 -0
  20. package/dist/commands/datasets.js +150 -0
  21. package/dist/commands/docs.d.ts +3 -0
  22. package/dist/commands/docs.js +166 -0
  23. package/dist/commands/evaluators.d.ts +3 -0
  24. package/dist/commands/evaluators.js +124 -0
  25. package/dist/commands/experiments.d.ts +3 -0
  26. package/dist/commands/experiments.js +255 -0
  27. package/dist/commands/extractions.d.ts +3 -0
  28. package/dist/commands/extractions.js +134 -0
  29. package/dist/commands/prompt-labels.d.ts +3 -0
  30. package/dist/commands/prompt-labels.js +67 -0
  31. package/dist/commands/prompt-versions.d.ts +3 -0
  32. package/dist/commands/prompt-versions.js +65 -0
  33. package/dist/commands/prompts.d.ts +3 -0
  34. package/dist/commands/prompts.js +159 -0
  35. package/dist/commands/providers.d.ts +3 -0
  36. package/dist/commands/providers.js +115 -0
  37. package/dist/commands/schema-labels.d.ts +3 -0
  38. package/dist/commands/schema-labels.js +67 -0
  39. package/dist/commands/schema-versions.d.ts +3 -0
  40. package/dist/commands/schema-versions.js +66 -0
  41. package/dist/commands/schemas.d.ts +3 -0
  42. package/dist/commands/schemas.js +178 -0
  43. package/dist/commands/scores.d.ts +3 -0
  44. package/dist/commands/scores.js +31 -0
  45. package/dist/commands/tracing.d.ts +3 -0
  46. package/dist/commands/tracing.js +85 -0
  47. package/dist/commands/transcribe.d.ts +3 -0
  48. package/dist/commands/transcribe.js +65 -0
  49. package/dist/index.d.ts +3 -0
  50. package/dist/index.js +54 -0
  51. package/dist/lib/client.d.ts +12 -0
  52. package/dist/lib/client.js +52 -0
  53. package/dist/lib/config.d.ts +44 -0
  54. package/dist/lib/config.js +217 -0
  55. package/dist/lib/datasets.d.ts +6 -0
  56. package/dist/lib/datasets.js +10 -0
  57. package/dist/lib/errors.d.ts +14 -0
  58. package/dist/lib/errors.js +54 -0
  59. package/dist/lib/multipart.d.ts +10 -0
  60. package/dist/lib/multipart.js +59 -0
  61. package/dist/lib/output.d.ts +29 -0
  62. package/dist/lib/output.js +107 -0
  63. package/dist/lib/pagination.d.ts +21 -0
  64. package/dist/lib/pagination.js +21 -0
  65. package/dist/lib/schema-compat.d.ts +11 -0
  66. package/dist/lib/schema-compat.js +51 -0
  67. package/dist/lib/update-notifier.d.ts +9 -0
  68. package/dist/lib/update-notifier.js +96 -0
  69. package/package.json +49 -0
@@ -0,0 +1,90 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import { store, isJsonOutput, getActiveContext, getActiveContextName, getContextCount, setContext, DEFAULT_BASE_URL, } from "../lib/config.js";
4
+ const ALLOWED_KEYS = ["apiKey", "baseUrl"];
5
+ const KEY_ALIASES = {
6
+ "api-key": "apiKey",
7
+ "base-url": "baseUrl",
8
+ };
9
+ function validateKey(key) {
10
+ if (key === "project") {
11
+ throw new Error('The "project" config key is no longer supported — resources are scoped by organization.');
12
+ }
13
+ const normalized = KEY_ALIASES[key] ?? key;
14
+ if (!ALLOWED_KEYS.includes(normalized)) {
15
+ throw new Error(`Unknown config key: "${key}". Allowed keys: ${ALLOWED_KEYS.join(", ")}`);
16
+ }
17
+ return normalized;
18
+ }
19
+ export function makeConfigCommand() {
20
+ const cmd = new Command("config").description("Manage CLI configuration");
21
+ cmd
22
+ .command("set")
23
+ .description("Set a configuration value")
24
+ .argument("<key>", `Config key (${ALLOWED_KEYS.join(", ")})`)
25
+ .argument("<value>", "Config value")
26
+ .action((key, value, _opts, command) => {
27
+ const validKey = validateKey(key);
28
+ // Update the active context
29
+ const contextName = getActiveContextName();
30
+ const ctx = getActiveContext() ?? {
31
+ apiKey: "",
32
+ baseUrl: DEFAULT_BASE_URL,
33
+ };
34
+ ctx[validKey] = value;
35
+ setContext(contextName, ctx);
36
+ if (isJsonOutput(command)) {
37
+ console.log(JSON.stringify({ key: validKey, value }));
38
+ }
39
+ else {
40
+ console.log(chalk.green(`Set ${validKey} = ${validKey === "apiKey" ? "****" : value}`));
41
+ }
42
+ });
43
+ cmd
44
+ .command("get")
45
+ .description("Get a configuration value")
46
+ .argument("<key>", `Config key (${ALLOWED_KEYS.join(", ")})`)
47
+ .action((key, _opts, command) => {
48
+ const validKey = validateKey(key);
49
+ const ctx = getActiveContext();
50
+ const value = ctx?.[validKey];
51
+ if (isJsonOutput(command)) {
52
+ console.log(JSON.stringify({ key: validKey, value: value ?? null }));
53
+ }
54
+ else if (value) {
55
+ console.log(validKey === "apiKey" ? "****" : value);
56
+ }
57
+ else {
58
+ console.log(chalk.dim("(not set)"));
59
+ }
60
+ });
61
+ cmd
62
+ .command("list")
63
+ .description("Show all configuration values")
64
+ .action((_opts, command) => {
65
+ const ctx = getActiveContext();
66
+ const values = {
67
+ apiKey: ctx?.apiKey,
68
+ baseUrl: ctx?.baseUrl,
69
+ };
70
+ if (isJsonOutput(command)) {
71
+ console.log(JSON.stringify(values, null, 2));
72
+ }
73
+ else {
74
+ for (const [key, value] of Object.entries(values)) {
75
+ const display = !value
76
+ ? chalk.dim("(not set)")
77
+ : key === "apiKey"
78
+ ? value.slice(0, 7) + "..." + value.slice(-4)
79
+ : value;
80
+ console.log(`${chalk.cyan(key)}: ${display}`);
81
+ }
82
+ if (getContextCount() > 1) {
83
+ console.log(chalk.dim(`\nContext: ${getActiveContextName()} (use "backbone context list" to see all)`));
84
+ }
85
+ console.log(chalk.dim(`\nConfig file: ${store.path}`));
86
+ }
87
+ });
88
+ return cmd;
89
+ }
90
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function makeContextCommand(): Command;
3
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1,149 @@
1
+ import { Command } from "commander";
2
+ import chalk from "chalk";
3
+ import Table from "cli-table3";
4
+ import { createInterface } from "node:readline/promises";
5
+ import { store, isJsonOutput, getAllContexts, getActiveContextName, getContextCount, setContext, deleteContext, renameContext, setActiveContext, validateContextName, DEFAULT_BASE_URL, } from "../lib/config.js";
6
+ function maskApiKey(key) {
7
+ if (key.length <= 11)
8
+ return "****";
9
+ return key.slice(0, 7) + "..." + key.slice(-4);
10
+ }
11
+ export function makeContextCommand() {
12
+ const cmd = new Command("context").description("Manage CLI contexts for multiple environments");
13
+ cmd
14
+ .command("list")
15
+ .description("List all contexts")
16
+ .action((_opts, command) => {
17
+ const contexts = getAllContexts();
18
+ const activeName = getActiveContextName();
19
+ if (isJsonOutput(command)) {
20
+ console.log(JSON.stringify({ activeContext: activeName, contexts }, null, 2));
21
+ return;
22
+ }
23
+ const names = Object.keys(contexts);
24
+ if (names.length === 0) {
25
+ console.log(chalk.dim('No contexts configured. Run "backbone auth login" to get started.'));
26
+ return;
27
+ }
28
+ const table = new Table({
29
+ head: ["", "NAME", "BASE URL", "API KEY"].map((h) => chalk.cyan(h)),
30
+ });
31
+ for (const name of names) {
32
+ const ctx = contexts[name];
33
+ table.push([
34
+ name === activeName ? chalk.green("*") : "",
35
+ name,
36
+ ctx.baseUrl,
37
+ maskApiKey(ctx.apiKey),
38
+ ]);
39
+ }
40
+ console.log(table.toString());
41
+ });
42
+ cmd
43
+ .command("use")
44
+ .description("Switch active context")
45
+ .argument("<name>", "Context name")
46
+ .action((name, _opts, command) => {
47
+ setActiveContext(name);
48
+ if (isJsonOutput(command)) {
49
+ console.log(JSON.stringify({ activeContext: name }));
50
+ }
51
+ else {
52
+ console.log(chalk.green(`Switched to context "${name}".`));
53
+ }
54
+ });
55
+ cmd
56
+ .command("create")
57
+ .description("Create a new context")
58
+ .argument("<name>", "Context name")
59
+ .option("--base-url <url>", "Base URL")
60
+ .option("--api-key <key>", "API key")
61
+ .action(async (name, opts) => {
62
+ validateContextName(name);
63
+ const contexts = getAllContexts();
64
+ if (contexts[name]) {
65
+ throw new Error(`Context "${name}" already exists. Delete it first or choose a different name.`);
66
+ }
67
+ let baseUrl = opts.baseUrl;
68
+ let apiKey = opts.apiKey;
69
+ if (!baseUrl || !apiKey) {
70
+ const rl = createInterface({
71
+ input: process.stdin,
72
+ output: process.stdout,
73
+ });
74
+ try {
75
+ if (!baseUrl) {
76
+ baseUrl = await rl.question(`Base URL [${DEFAULT_BASE_URL}]: `);
77
+ if (!baseUrl)
78
+ baseUrl = DEFAULT_BASE_URL;
79
+ }
80
+ if (!apiKey) {
81
+ apiKey = await rl.question("API Key (sk_...): ");
82
+ }
83
+ }
84
+ finally {
85
+ rl.close();
86
+ }
87
+ }
88
+ if (!apiKey) {
89
+ console.error(chalk.red("API key is required."));
90
+ process.exit(1);
91
+ }
92
+ const entry = { baseUrl, apiKey };
93
+ setContext(name, entry);
94
+ // Auto-set as active if it's the first context
95
+ if (getContextCount() === 1) {
96
+ setActiveContext(name);
97
+ }
98
+ console.log(chalk.green(`Context "${name}" created.`));
99
+ console.log(chalk.dim(`Config stored at: ${store.path}`));
100
+ });
101
+ cmd
102
+ .command("current")
103
+ .description("Show the active context")
104
+ .action((_opts, command) => {
105
+ const name = getActiveContextName();
106
+ const contexts = getAllContexts();
107
+ const ctx = contexts[name];
108
+ if (isJsonOutput(command)) {
109
+ console.log(JSON.stringify({ name, ...(ctx ?? {}) }, null, 2));
110
+ return;
111
+ }
112
+ if (!ctx) {
113
+ console.log(chalk.yellow('No active context configured. Run "backbone auth login" to get started.'));
114
+ return;
115
+ }
116
+ console.log(`${chalk.cyan("Context")}: ${name}`);
117
+ console.log(`${chalk.cyan("Base URL")}: ${ctx.baseUrl}`);
118
+ console.log(`${chalk.cyan("API Key")}: ${maskApiKey(ctx.apiKey)}`);
119
+ });
120
+ cmd
121
+ .command("delete")
122
+ .description("Delete a context")
123
+ .argument("<name>", "Context name")
124
+ .action((name, _opts, command) => {
125
+ deleteContext(name);
126
+ if (isJsonOutput(command)) {
127
+ console.log(JSON.stringify({ deleted: name }));
128
+ }
129
+ else {
130
+ console.log(chalk.green(`Context "${name}" deleted.`));
131
+ }
132
+ });
133
+ cmd
134
+ .command("rename")
135
+ .description("Rename a context")
136
+ .argument("<old>", "Current name")
137
+ .argument("<new>", "New name")
138
+ .action((oldName, newName, _opts, command) => {
139
+ renameContext(oldName, newName);
140
+ if (isJsonOutput(command)) {
141
+ console.log(JSON.stringify({ from: oldName, to: newName }));
142
+ }
143
+ else {
144
+ console.log(chalk.green(`Context "${oldName}" renamed to "${newName}".`));
145
+ }
146
+ });
147
+ return cmd;
148
+ }
149
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function makeConvertCommand(): Command;
3
+ //# sourceMappingURL=convert.d.ts.map
@@ -0,0 +1,291 @@
1
+ import { Command } from "commander";
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import chalk from "chalk";
4
+ import { getClient, runAction } from "../lib/client.js";
5
+ import { resolveConfig, isJsonOutput } from "../lib/config.js";
6
+ import { BackboneApiError } from "../lib/errors.js";
7
+ import { formatDetail, withSpinner } from "../lib/output.js";
8
+ import { fileToBlob, getMimeType } from "../lib/multipart.js";
9
+ function mapFormat(format) {
10
+ const map = {
11
+ markdown: "MD",
12
+ md: "MD",
13
+ text: "TEXT",
14
+ txt: "TEXT",
15
+ json: "JSON",
16
+ html: "HTML",
17
+ };
18
+ return map[format.toLowerCase()] ?? format.toUpperCase();
19
+ }
20
+ /**
21
+ * Extract the primary content from a document based on the requested formats.
22
+ * Prefers markdown > text > html.
23
+ */
24
+ function getPrimaryContent(doc, formats) {
25
+ const upper = formats.map((f) => f.toUpperCase());
26
+ if (upper.includes("MD") && doc.mdContent)
27
+ return doc.mdContent;
28
+ if (upper.includes("TEXT") && doc.textContent)
29
+ return doc.textContent;
30
+ if (upper.includes("HTML") && doc.htmlContent)
31
+ return doc.htmlContent;
32
+ if (upper.includes("JSON") && doc.jsonContent)
33
+ return JSON.stringify(doc.jsonContent.content ?? doc.jsonContent, null, 2);
34
+ return doc.mdContent ?? doc.textContent ?? doc.htmlContent ?? "";
35
+ }
36
+ function formatBytes(bytes) {
37
+ if (bytes < 1024)
38
+ return `${bytes} B`;
39
+ if (bytes < 1024 * 1024)
40
+ return `${(bytes / 1024).toFixed(1)} KB`;
41
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
42
+ }
43
+ /**
44
+ * Output conversion result: --json, --output, or human-readable.
45
+ */
46
+ function outputConvertResult(data, command, opts) {
47
+ if (isJsonOutput(command)) {
48
+ console.log(JSON.stringify(data, null, 2));
49
+ return;
50
+ }
51
+ const result = data;
52
+ const docs = result.documents ?? [];
53
+ const formats = opts.format.map(mapFormat);
54
+ // --output: write content to file
55
+ if (opts.output) {
56
+ // For JSON format, produce a valid JSON array instead of ---‑separated objects
57
+ const isJson = formats.some((f) => f.toUpperCase() === "JSON");
58
+ let content;
59
+ if (isJson) {
60
+ const jsonParts = docs
61
+ .map((doc) => doc.jsonContent?.content ?? doc.jsonContent)
62
+ .filter(Boolean);
63
+ content = JSON.stringify(jsonParts.length === 1 ? jsonParts[0] : jsonParts, null, 2);
64
+ }
65
+ else {
66
+ const parts = docs.map((doc) => getPrimaryContent(doc, formats));
67
+ content = parts.join("\n\n---\n\n");
68
+ }
69
+ writeFileSync(opts.output, content, "utf-8");
70
+ const size = Buffer.byteLength(content, "utf-8");
71
+ console.error(chalk.green(`Wrote ${formatBytes(size)} to ${opts.output}`));
72
+ return;
73
+ }
74
+ // Human-readable output
75
+ const timeStr = result.processingTime
76
+ ? ` (${result.processingTime.toFixed(1)}s)`
77
+ : "";
78
+ const count = docs.length;
79
+ console.log(chalk.green(`Converted ${count} document${count !== 1 ? "s" : ""}${timeStr}`));
80
+ if (result.errors?.length) {
81
+ for (const err of result.errors) {
82
+ console.log(chalk.yellow(` Warning: ${err.filename ?? "unknown"} — ${err.errorMessage ?? "error"}`));
83
+ }
84
+ }
85
+ for (const doc of docs) {
86
+ const content = getPrimaryContent(doc, formats);
87
+ const size = formatBytes(Buffer.byteLength(content, "utf-8"));
88
+ console.log(chalk.dim(`\n--- ${doc.filename ?? "document"} (${size}) ---\n`));
89
+ console.log(content);
90
+ }
91
+ }
92
+ /**
93
+ * Poll an async task until terminal status, then fetch and return the result.
94
+ */
95
+ async function waitForTask(client, taskId, waitSeconds) {
96
+ const terminalStatuses = new Set(["SUCCESS", "PARTIAL_SUCCESS", "FAILURE", "COMPLETED", "FAILED"]);
97
+ // Poll until done
98
+ let status;
99
+ // eslint-disable-next-line no-constant-condition
100
+ while (true) {
101
+ const { data } = await client.GET("/v1/convert/tasks/{taskId}", {
102
+ params: {
103
+ path: { taskId },
104
+ query: { wait: waitSeconds },
105
+ },
106
+ });
107
+ status = data;
108
+ if (status?.taskStatus && terminalStatuses.has(status.taskStatus.toUpperCase())) {
109
+ break;
110
+ }
111
+ }
112
+ // Fetch final result
113
+ const { data } = await client.GET("/v1/convert/tasks/{taskId}/result", {
114
+ params: { path: { taskId } },
115
+ });
116
+ return data;
117
+ }
118
+ function buildFormData(paths, opts) {
119
+ const formData = new FormData();
120
+ const outputFormats = opts.format.map(mapFormat);
121
+ for (const p of paths) {
122
+ if (p === "-") {
123
+ const name = opts.filename ?? "document";
124
+ const buffer = readFileSync(0);
125
+ const blob = new Blob([buffer], { type: getMimeType(name) });
126
+ formData.append("files", blob, name);
127
+ }
128
+ else {
129
+ const { blob, filename } = fileToBlob(p);
130
+ formData.append("files", blob, filename);
131
+ }
132
+ }
133
+ const pipelineOptions = { outputFormats };
134
+ if (opts.imageMode)
135
+ pipelineOptions.imageExportMode = opts.imageMode;
136
+ formData.append("options", new Blob([JSON.stringify(pipelineOptions)], { type: "application/json" }));
137
+ return formData;
138
+ }
139
+ async function multipartConvert(command, formData, endpoint, pipeline) {
140
+ const config = resolveConfig(command);
141
+ const baseUrl = config.baseUrl.replace(/\/+$/, "");
142
+ const url = pipeline
143
+ ? `${baseUrl}${endpoint}?pipeline=${encodeURIComponent(pipeline)}`
144
+ : `${baseUrl}${endpoint}`;
145
+ const res = await fetch(url, {
146
+ method: "POST",
147
+ headers: { Authorization: `Bearer ${config.apiKey}` },
148
+ body: formData,
149
+ });
150
+ if (!res.ok) {
151
+ let body;
152
+ try {
153
+ body = await res.json();
154
+ }
155
+ catch {
156
+ body = {
157
+ error: res.statusText,
158
+ message: `HTTP ${res.status}: ${res.statusText}`,
159
+ status: res.status,
160
+ timestamp: new Date().toISOString(),
161
+ };
162
+ }
163
+ throw new BackboneApiError(body);
164
+ }
165
+ return (await res.json());
166
+ }
167
+ export function makeConvertCommand() {
168
+ const cmd = new Command("convert").description("Convert documents");
169
+ cmd
170
+ .command("file")
171
+ .description("Convert local files (use - to read from stdin)")
172
+ .argument("<paths...>", "File paths to convert (use - for stdin)")
173
+ .option("--format <formats...>", "Output formats (md, text, html, json)", ["md"])
174
+ .option("--pipeline <name>", "Pipeline: fast (default), ocr, vlm. For compound files (MSG/EML), applies to attachments; email body is always plain text")
175
+ .option("--image-mode <mode>", "Image export mode (placeholder, embedded)")
176
+ .option("-o, --output <path>", "Write content to file instead of stdout")
177
+ .option("--filename <name>", "Filename for MIME detection when reading from stdin")
178
+ .option("--async", "Run asynchronously")
179
+ .option("-w, --wait [seconds]", "With --async: wait for completion (default: 30s)", parseFloat)
180
+ .action(async (paths, opts, command) => {
181
+ await runAction(command, async () => {
182
+ const formData = buildFormData(paths, opts);
183
+ if (opts.async) {
184
+ const data = await withSpinner("Submitting conversion...", () => multipartConvert(command, formData, "/v1/convert/file/async", opts.pipeline));
185
+ if (opts.wait !== undefined) {
186
+ const waitSeconds = opts.wait === true ? 30 : opts.wait;
187
+ const taskId = data?.taskId;
188
+ if (!taskId)
189
+ throw new Error("No taskId returned from async submission");
190
+ const client = getClient(command);
191
+ const result = await withSpinner("Waiting for completion...", () => waitForTask(client, taskId, waitSeconds));
192
+ outputConvertResult(result, command, opts);
193
+ }
194
+ else {
195
+ formatDetail(data, command);
196
+ }
197
+ }
198
+ else {
199
+ const data = await withSpinner("Converting...", () => multipartConvert(command, formData, "/v1/convert/file", opts.pipeline));
200
+ outputConvertResult(data, command, opts);
201
+ }
202
+ });
203
+ });
204
+ cmd
205
+ .command("url")
206
+ .description("Convert documents from URLs")
207
+ .argument("<urls...>", "URLs to convert")
208
+ .option("--format <formats...>", "Output formats (md, text, html, json)", ["md"])
209
+ .option("--pipeline <name>", "Pipeline: fast (default), ocr, vlm. For compound files (MSG/EML), applies to attachments; email body is always plain text")
210
+ .option("--image-mode <mode>", "Image export mode (placeholder, embedded)")
211
+ .option("-o, --output <path>", "Write content to file instead of stdout")
212
+ .option("--async", "Run asynchronously")
213
+ .option("-w, --wait [seconds]", "With --async: wait for completion (default: 30s)", parseFloat)
214
+ .action(async (urls, opts, command) => {
215
+ await runAction(command, async () => {
216
+ const client = getClient(command);
217
+ const sources = urls.map((url) => ({
218
+ kind: "http",
219
+ url,
220
+ }));
221
+ const outputFormats = opts.format.map(mapFormat);
222
+ const body = {
223
+ sources,
224
+ options: {
225
+ pipeline: opts.pipeline,
226
+ options: {
227
+ outputFormats,
228
+ ...(opts.imageMode && { imageExportMode: opts.imageMode }),
229
+ },
230
+ },
231
+ };
232
+ if (opts.async) {
233
+ const { data } = await withSpinner("Submitting conversion...", () =>
234
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
235
+ client.POST("/v1/convert/source/async", { body }));
236
+ if (opts.wait !== undefined) {
237
+ const waitSeconds = opts.wait === true ? 30 : opts.wait;
238
+ const taskId = data?.taskId;
239
+ if (!taskId)
240
+ throw new Error("No taskId returned from async submission");
241
+ const result = await withSpinner("Waiting for completion...", () => waitForTask(client, taskId, waitSeconds));
242
+ outputConvertResult(result, command, opts);
243
+ }
244
+ else {
245
+ formatDetail(data, command);
246
+ }
247
+ }
248
+ else {
249
+ const { data } = await withSpinner("Converting...", () =>
250
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
251
+ client.POST("/v1/convert/source", { body }));
252
+ outputConvertResult(data, command, opts);
253
+ }
254
+ });
255
+ });
256
+ cmd
257
+ .command("status")
258
+ .description("Check async conversion task status")
259
+ .argument("<taskId>", "Task ID")
260
+ .option("-w, --wait [seconds]", "Wait for completion (default: 30s)", parseFloat)
261
+ .action(async (taskId, opts, command) => {
262
+ await runAction(command, async () => {
263
+ const client = getClient(command);
264
+ const wait = opts.wait === true ? 30 : opts.wait;
265
+ const { data } = await client.GET("/v1/convert/tasks/{taskId}", {
266
+ params: {
267
+ path: { taskId },
268
+ query: { wait },
269
+ },
270
+ });
271
+ formatDetail(data, command);
272
+ });
273
+ });
274
+ cmd
275
+ .command("result")
276
+ .description("Get async conversion task result")
277
+ .argument("<taskId>", "Task ID")
278
+ .option("-o, --output <path>", "Write content to file instead of stdout")
279
+ .option("--format <formats...>", "Preferred format for --output (md, text, html)", ["md"])
280
+ .action(async (taskId, opts, command) => {
281
+ await runAction(command, async () => {
282
+ const client = getClient(command);
283
+ const { data } = await client.GET("/v1/convert/tasks/{taskId}/result", {
284
+ params: { path: { taskId } },
285
+ });
286
+ outputConvertResult(data, command, opts);
287
+ });
288
+ });
289
+ return cmd;
290
+ }
291
+ //# sourceMappingURL=convert.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function makeDatasetVersionsCommand(): Command;
3
+ //# sourceMappingURL=dataset-versions.d.ts.map
@@ -0,0 +1,90 @@
1
+ import { Command } from "commander";
2
+ import { getClient, runAction } from "../lib/client.js";
3
+ import { resolveLatestVersionId } from "../lib/datasets.js";
4
+ import { formatPage, formatDetail } from "../lib/output.js";
5
+ import { addPaginationOptions, paginationParams } from "../lib/pagination.js";
6
+ export function makeDatasetVersionsCommand() {
7
+ const cmd = new Command("versions").description("Manage dataset versions");
8
+ const list = new Command("list").description("List versions of a dataset");
9
+ addPaginationOptions(list);
10
+ list.requiredOption("--dataset <id>", "Dataset ID");
11
+ list.action(async (opts, command) => {
12
+ await runAction(command, async () => {
13
+ const client = getClient(command);
14
+ const { data } = await client.GET("/v1/datasets/{id}/versions", {
15
+ params: {
16
+ path: { id: opts.dataset },
17
+ query: paginationParams(opts),
18
+ },
19
+ });
20
+ formatPage(data, command, ["id", "versionNumber", "itemCount", "changeDescription", "createdAt"]);
21
+ });
22
+ });
23
+ cmd.addCommand(list);
24
+ cmd
25
+ .command("get")
26
+ .description("Get a specific dataset version")
27
+ .argument("<versionId>", "Version ID")
28
+ .requiredOption("--dataset <id>", "Dataset ID")
29
+ .action(async (versionId, opts, command) => {
30
+ await runAction(command, async () => {
31
+ const client = getClient(command);
32
+ const { data } = await client.GET("/v1/datasets/{id}/versions/{versionId}", {
33
+ params: {
34
+ path: { id: opts.dataset, versionId },
35
+ },
36
+ });
37
+ formatDetail(data, command);
38
+ });
39
+ });
40
+ cmd
41
+ .command("create")
42
+ .description("Create a new dataset version")
43
+ .requiredOption("--dataset <id>", "Dataset ID")
44
+ .option("-m, --message <text>", "Change description")
45
+ .action(async (opts, command) => {
46
+ await runAction(command, async () => {
47
+ const client = getClient(command);
48
+ const { data } = await client.POST("/v1/datasets/{id}/versions", {
49
+ params: { path: { id: opts.dataset } },
50
+ body: { changeDescription: opts.message },
51
+ });
52
+ formatDetail(data, command);
53
+ });
54
+ });
55
+ cmd
56
+ .command("latest")
57
+ .description("Get the latest version of a dataset")
58
+ .requiredOption("--dataset <id>", "Dataset ID")
59
+ .action(async (opts, command) => {
60
+ await runAction(command, async () => {
61
+ const client = getClient(command);
62
+ const { data } = await client.GET("/v1/datasets/{id}/versions/latest", {
63
+ params: {
64
+ path: { id: opts.dataset },
65
+ },
66
+ });
67
+ formatDetail(data, command);
68
+ });
69
+ });
70
+ const items = new Command("items").description("List items in a dataset version (defaults to latest version)");
71
+ addPaginationOptions(items);
72
+ items.requiredOption("--dataset <id>", "Dataset ID");
73
+ items.option("--version-id <id>", "Version ID (defaults to latest)");
74
+ items.action(async (opts, command) => {
75
+ await runAction(command, async () => {
76
+ const client = getClient(command);
77
+ const versionId = opts.versionId ?? (await resolveLatestVersionId(client, opts.dataset));
78
+ const { data } = await client.GET("/v1/datasets/{id}/versions/{versionId}/items", {
79
+ params: {
80
+ path: { id: opts.dataset, versionId },
81
+ query: paginationParams(opts),
82
+ },
83
+ });
84
+ formatPage(data, command, ["id", "input", "expectedOutput", "tags", "createdAt"]);
85
+ });
86
+ });
87
+ cmd.addCommand(items);
88
+ return cmd;
89
+ }
90
+ //# sourceMappingURL=dataset-versions.js.map
@@ -0,0 +1,3 @@
1
+ import { Command } from "commander";
2
+ export declare function makeDatasetsCommand(): Command;
3
+ //# sourceMappingURL=datasets.d.ts.map