orizu 0.5.39 → 0.5.40

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 (85) hide show
  1. package/README.md +77 -0
  2. package/dist/dataset-commands.d.ts +3 -1
  3. package/dist/dataset-commands.d.ts.map +1 -1
  4. package/dist/dataset-commands.js +101 -62
  5. package/dist/dataset-delete-workflow.d.ts +4 -0
  6. package/dist/dataset-delete-workflow.d.ts.map +1 -0
  7. package/dist/dataset-delete-workflow.js +75 -0
  8. package/dist/dataset-lock-workflow.d.ts +12 -0
  9. package/dist/dataset-lock-workflow.d.ts.map +1 -0
  10. package/dist/dataset-lock-workflow.js +76 -0
  11. package/dist/dataset-readme-workflow.d.ts +13 -0
  12. package/dist/dataset-readme-workflow.d.ts.map +1 -0
  13. package/dist/dataset-readme-workflow.js +82 -0
  14. package/dist/dataset-row-workflow.d.ts +14 -0
  15. package/dist/dataset-row-workflow.d.ts.map +1 -0
  16. package/dist/dataset-row-workflow.js +202 -0
  17. package/dist/dataset-split-reads.d.ts +28 -0
  18. package/dist/dataset-split-reads.d.ts.map +1 -0
  19. package/dist/dataset-split-reads.js +47 -0
  20. package/dist/help.d.ts.map +1 -1
  21. package/dist/help.js +29 -3
  22. package/dist/hosted-boot.d.ts +1 -0
  23. package/dist/hosted-boot.d.ts.map +1 -1
  24. package/dist/hosted-boot.js +17 -0
  25. package/dist/hosted-harness-opencode.d.ts +4 -5
  26. package/dist/hosted-harness-opencode.d.ts.map +1 -1
  27. package/dist/hosted-harness-opencode.js +66 -52
  28. package/dist/hosted-loop-lifecycle.d.ts +4 -10
  29. package/dist/hosted-loop-lifecycle.d.ts.map +1 -1
  30. package/dist/hosted-loop-lifecycle.js +4 -10
  31. package/dist/hosted-loop.d.ts +2 -0
  32. package/dist/hosted-loop.d.ts.map +1 -1
  33. package/dist/hosted-loop.js +5 -4
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.js +9 -1
  36. package/dist/instruction-delivery.d.ts +14 -0
  37. package/dist/instruction-delivery.d.ts.map +1 -0
  38. package/dist/instruction-delivery.js +103 -0
  39. package/dist/instruction-set-sync/index.d.ts +17 -0
  40. package/dist/instruction-set-sync/index.d.ts.map +1 -1
  41. package/dist/instruction-set-sync/index.js +44 -17
  42. package/dist/instruction-set-update/index.d.ts +2 -2
  43. package/dist/instruction-set-update/index.d.ts.map +1 -1
  44. package/dist/instruction-set-update/index.js +2 -2
  45. package/dist/instruction-sets-cli.d.ts.map +1 -1
  46. package/dist/instruction-sets-cli.js +19 -27
  47. package/dist/json-response.d.ts +1 -0
  48. package/dist/json-response.d.ts.map +1 -1
  49. package/dist/json-response.js +7 -0
  50. package/dist/preview-runtime.d.ts +2 -2
  51. package/dist/preview-runtime.d.ts.map +1 -1
  52. package/dist/preview-runtime.js +37 -7
  53. package/dist/task-export-workflow.d.ts +4 -0
  54. package/dist/task-export-workflow.d.ts.map +1 -0
  55. package/dist/task-export-workflow.js +46 -0
  56. package/dist/workbench-cli.js +1 -1
  57. package/package.json +1 -1
  58. package/src/dataset-commands.ts +92 -92
  59. package/src/dataset-delete-workflow.ts +72 -0
  60. package/src/dataset-lock-workflow.ts +74 -0
  61. package/src/dataset-readme-workflow.ts +80 -0
  62. package/src/dataset-row-workflow.ts +248 -0
  63. package/src/dataset-split-reads.ts +43 -0
  64. package/src/help.ts +29 -3
  65. package/src/hosted-boot.ts +16 -1
  66. package/src/hosted-harness-opencode.ts +67 -65
  67. package/src/hosted-loop-lifecycle.ts +4 -10
  68. package/src/hosted-loop.ts +6 -4
  69. package/src/hosted-model-cache.d.mts +2 -0
  70. package/src/hosted-model-cache.mjs +53 -0
  71. package/src/index.ts +10 -0
  72. package/src/instruction-delivery.ts +123 -0
  73. package/src/instruction-set-sync/index.ts +61 -14
  74. package/src/instruction-set-update/index.ts +4 -3
  75. package/src/instruction-sets-cli.ts +19 -27
  76. package/src/json-response.ts +8 -0
  77. package/src/preview-runtime.ts +40 -10
  78. package/src/task-export-workflow.ts +56 -0
  79. package/src/workbench-cli.ts +1 -1
  80. package/vendor/skills/orizu/references/cli-reference.md +34 -0
  81. package/vendor/skills/orizu/references/dataset-design.md +1 -1
  82. package/dist/dataset-edit-rows.d.ts +0 -10
  83. package/dist/dataset-edit-rows.d.ts.map +0 -1
  84. package/dist/dataset-edit-rows.js +0 -90
  85. package/src/dataset-edit-rows.ts +0 -124
@@ -185,7 +185,7 @@ async function assertGeneratedModulesVerified(appRootPath, lock) {
185
185
  }
186
186
  }
187
187
  }
188
- export async function applyUpdate(out, project, plan, noSync, now = () => new Date()) {
188
+ export async function applyUpdate(out, project, plan, noSync, now = () => new Date(), sync = syncPayloadToDisk) {
189
189
  const absent = [];
190
190
  const warnings = [];
191
191
  if (noSync) {
@@ -236,7 +236,7 @@ export async function applyUpdate(out, project, plan, noSync, now = () => new Da
236
236
  let lock = plan.lock;
237
237
  for (const resolution of plan.resolutions) {
238
238
  const set = resolution.payload.instructionSet;
239
- const result = syncPayloadToDisk(out, project, {
239
+ const result = await sync(out, project, {
240
240
  parsed: { set: set.slug, profile: resolution.profileIdentity },
241
241
  path: '',
242
242
  lock,
@@ -1 +1 @@
1
- {"version":3,"file":"instruction-sets-cli.d.ts","sourceRoot":"","sources":["../src/instruction-sets-cli.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuC,UAAU,EAAE,MAAM,EAAY,aAAa,EAAE,MAAM,SAAS,CAAA;AAI1G,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACjE;AAwED,MAAM,WAAW,aAAa;IAAG,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAC5G,MAAM,WAAW,YAAY;IAAG,gBAAgB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAE;AAC/K,MAAM,WAAW,WAAW;IAAG,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE;AACnH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,YAAY,CAAA;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAkBD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,OAAO,aAAa,CAAA;IACnC,UAAU,CAAC,EAAE,OAAO,UAAU,CAAA;IAC9B,MAAM,CAAC,EAAE,OAAO,MAAM,CAAA;CACvB;AAuDD,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,WAA+B,EACxC,QAAQ,GAAE,MAAM,CAAC,QAA2B,UAyE7C;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA8T1G"}
1
+ {"version":3,"file":"instruction-sets-cli.d.ts","sourceRoot":"","sources":["../src/instruction-sets-cli.ts"],"names":[],"mappings":"AAOA,OAAO,EAAuC,UAAU,EAAE,MAAM,EAAY,aAAa,EAAE,MAAM,SAAS,CAAA;AAI1G,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACjE;AAoED,MAAM,WAAW,aAAa;IAAG,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE;AAC5G,MAAM,WAAW,YAAY;IAAG,gBAAgB,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAA;CAAE;AAC/K,MAAM,WAAW,WAAW;IAAG,mBAAmB,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,GAAG,IAAI,CAAA;CAAE;AACnH,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,OAAO,EAAE,YAAY,CAAA;IACrB,QAAQ,EAAE,WAAW,EAAE,CAAA;IACvB,UAAU,CAAC,EAAE,MAAM,EAAE,CAAA;CACtB;AAkBD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,OAAO,aAAa,CAAA;IACnC,UAAU,CAAC,EAAE,OAAO,UAAU,CAAA;IAC9B,MAAM,CAAC,EAAE,OAAO,MAAM,CAAA;CACvB;AAuDD,wBAAgB,UAAU,CACxB,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,WAA+B,EACxC,QAAQ,GAAE,MAAM,CAAC,QAA2B,UAyE7C;AAED,wBAAsB,sBAAsB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CA0T1G"}
@@ -1,9 +1,9 @@
1
1
  import { authedFetch } from './http.js';
2
2
  import { INSTRUCTION_SET_COMPONENT_KEY } from './instruction-set-lock/index.js';
3
3
  import { loadInstructionSetManifest, MAX_INSTRUCTION_SET_COMPONENT_BYTES } from './instruction-set-manifest.js';
4
- import { sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
5
- import { parseSyncTarget, planSyncRequest, syncPayloadToDisk } from './instruction-set-sync/index.js';
6
- import { applyPrune, applyUpdate, lockedProfileIdentity, makeUpdatePlan, planPrune, PruneKeepUnresolvedError, readUpdateLock } from './instruction-set-update/index.js';
4
+ import { responsePayload, sanitizeHumanInlineText, sanitizeTerminalText } from './json-response.js';
5
+ import { parseSyncTarget, planSyncRequest } from './instruction-set-sync/index.js';
6
+ import { applyPrune, planPrune, PruneKeepUnresolvedError } from './instruction-set-update/index.js';
7
7
  import { printVerifyReport, verifyInstructionSetTree } from './instruction-set-verify/index.js';
8
8
  import { existsSync, mkdirSync, readFileSync, renameSync, rmSync, statSync, writeFileSync } from 'node:fs';
9
9
  import { join } from 'node:path';
@@ -66,12 +66,8 @@ function lifecycleOptionValues(args, command, flag) {
66
66
  }
67
67
  return values;
68
68
  }
69
- async function responsePayload(response, action) {
70
- const payload = await response.json().catch(() => ({}));
71
- if (!response.ok) {
72
- throw new Error(`${action} failed (${response.status}): ${typeof payload.error === 'string' ? payload.error : response.statusText}`);
73
- }
74
- return payload;
69
+ function printInterrupted(io, message) {
70
+ io.print(io.json ? JSON.stringify({ error: message }) : message);
75
71
  }
76
72
  const SAFE_SEGMENT = /^[A-Za-z0-9._-]+$/u;
77
73
  const WINDOWS_RESERVED_COMPONENT_KEY = /^(?:CON|PRN|AUX|NUL|COM[1-9]|LPT[1-9])(?:\.|$)/iu;
@@ -304,23 +300,14 @@ export async function instructionSetsCommand(args, io) {
304
300
  const project = await io.resolveProjectSlug(projectOption);
305
301
  if (subcommand === 'update') {
306
302
  const output = lifecycleOptionValues(args, 'update', '--out')[0] ?? '.';
307
- const lock = readUpdateLock(output, project);
308
- const payloads = [];
309
- for (const [setSlug, set] of Object.entries(lock.instructionSets)) {
310
- const barePath = `/api/cli/instruction-sets/${encodeURIComponent(setSlug)}/sync?project=${encodeURIComponent(project)}`;
311
- payloads.push(await responsePayload(await authedFetch(barePath, { method: 'GET' }), 'Instruction sets update'));
312
- for (const profileSlugValue of Object.keys(set.profiles)) {
313
- const identity = lockedProfileIdentity(output, setSlug, profileSlugValue);
314
- const query = new URLSearchParams({ project, profile: identity });
315
- const path = `/api/cli/instruction-sets/${encodeURIComponent(setSlug)}/sync?${query.toString()}`;
316
- payloads.push(await responsePayload(await authedFetch(path, { method: 'GET' }), 'Instruction sets update'));
317
- }
318
- }
319
- const plan = makeUpdatePlan(project, lock, payloads);
320
303
  const isApproved = args.includes('--yes');
321
- const result = isApproved
322
- ? await applyUpdate(output, project, plan, args.includes('--no-sync'))
323
- : { absent: [], warnings: [] };
304
+ const { updateInstructionSets } = await import('./instruction-delivery.js');
305
+ const updated = await updateInstructionSets(output, project, isApproved, args.includes('--no-sync'));
306
+ if ('interrupted' in updated) {
307
+ printInterrupted(io, 'Instruction sets update interrupted; some sets may already be updated. Run orizu instructions update again, then orizu instructions verify.');
308
+ return updated.interrupted;
309
+ }
310
+ const { plan, result } = updated;
324
311
  if (io.json)
325
312
  io.print(JSON.stringify({ plan: plan.lines, applied: isApproved, absent: result.absent, warnings: result.warnings }));
326
313
  else {
@@ -530,11 +517,16 @@ export async function instructionSetsCommand(args, io) {
530
517
  throw new Error(`instruction_set_sync_option_unknown:${unknownOption}`);
531
518
  const output = requiredOptionValue(args, subcommand, '--out') ?? '.';
532
519
  const plan = planSyncRequest(reference, requiredOptionValue(args, subcommand, '--version'), output, project);
533
- const payload = await responsePayload(await authedFetch(plan.path, { method: 'GET' }), 'Instruction sets sync');
534
- const result = syncPayloadToDisk(output, project, plan, payload, {
520
+ const { syncInstructionSet } = await import('./instruction-delivery.js');
521
+ const delivered = await syncInstructionSet(output, project, plan, {
535
522
  forceHelpers: args.includes('--force-helpers'),
536
523
  target: syncTarget,
537
524
  });
525
+ if ('interrupted' in delivered) {
526
+ printInterrupted(io, 'Instruction set sync interrupted; any unpublished staged version was removed. Run orizu instructions sync again.');
527
+ return delivered.interrupted;
528
+ }
529
+ const { payload, result } = delivered;
538
530
  if (io.json)
539
531
  io.print(JSON.stringify({ ...payload, warnings: result.warnings }));
540
532
  else {
@@ -1,4 +1,5 @@
1
1
  export declare function sanitizeTerminalText(value: unknown): string;
2
2
  export declare function sanitizeHumanInlineText(sanitizer: (value: unknown) => string, value: unknown): string;
3
3
  export declare function parseJsonResponse<T>(response: Response, context: string): Promise<T>;
4
+ export declare function responsePayload(response: Response, action: string): Promise<Record<string, unknown>>;
4
5
  //# sourceMappingURL=json-response.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"json-response.d.ts","sourceRoot":"","sources":["../src/json-response.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK3D;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,EACrC,KAAK,EAAE,OAAO,GACb,MAAM,CAER;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAmBZ"}
1
+ {"version":3,"file":"json-response.d.ts","sourceRoot":"","sources":["../src/json-response.ts"],"names":[],"mappings":"AAAA,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK3D;AAED,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,EACrC,KAAK,EAAE,OAAO,GACb,MAAM,CAER;AAED,wBAAsB,iBAAiB,CAAC,CAAC,EACvC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,CAAC,CAAC,CAmBZ;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAM1G"}
@@ -19,3 +19,10 @@ export async function parseJsonResponse(response, context) {
19
19
  `Body preview: ${sanitizeTerminalText(rawBody.slice(0, 180))}`);
20
20
  }
21
21
  }
22
+ export async function responsePayload(response, action) {
23
+ const payload = await response.json().catch(() => ({}));
24
+ if (!response.ok) {
25
+ throw new Error(`${action} failed (${response.status}): ${typeof payload.error === 'string' ? payload.error : response.statusText}`);
26
+ }
27
+ return payload;
28
+ }
@@ -44,8 +44,8 @@ interface PreviewPage {
44
44
  }) => Promise<unknown>;
45
45
  waitForTimeout: (timeout: number) => Promise<unknown>;
46
46
  once?: (event: string, listener: () => void) => unknown;
47
- on?: (event: string, listener: () => void) => unknown;
48
- off?: (event: string, listener: () => void) => unknown;
47
+ on?: (event: string, listener: (error: Error) => void) => unknown;
48
+ off?: (event: string, listener: (error: Error) => void) => unknown;
49
49
  }
50
50
  /**
51
51
  * Removes line and block comments, walking left to right so a `//` or `/*`
@@ -1 +1 @@
1
- {"version":3,"file":"preview-runtime.d.ts","sourceRoot":"","sources":["../src/preview-runtime.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"preview-runtime.d.ts","sourceRoot":"","sources":["../src/preview-runtime.ts"],"names":[],"mappings":"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"}
@@ -186,19 +186,32 @@ export async function runLocalAppPreview(options) {
186
186
  headless: !options.headed,
187
187
  ...(chromiumExecutablePath ? { executablePath: chromiumExecutablePath } : {}),
188
188
  });
189
- const page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
189
+ let page = null;
190
+ let handleRenderError;
191
+ let hasFinishedKeepingOpen = false;
190
192
  try {
191
- await page.goto(url, { waitUntil: 'networkidle' });
192
- await page.waitForSelector('[data-orizu-preview-ready="true"]', { timeout: 15_000 });
193
+ page = await browser.newPage({ viewport: { width: 1440, height: 1000 } });
194
+ const renderError = new Promise((_, reject) => {
195
+ handleRenderError = error => reject(new Error(`App preview render failed: ${error.message}`));
196
+ page?.on?.('pageerror', handleRenderError);
197
+ });
198
+ // Listen before navigation: React can fail before the ready marker mounts.
199
+ await Promise.race([
200
+ page.goto(url, { waitUntil: 'networkidle' }).then(() => page.waitForSelector('[data-orizu-preview-ready="true"]', { timeout: 15_000 })),
201
+ renderError,
202
+ ]);
193
203
  if (options.screenshotPath) {
194
204
  await page.screenshot({ path: options.screenshotPath, fullPage: true });
195
205
  }
196
206
  if (options.keepOpen) {
197
207
  await waitForPreviewClose(page, browser);
208
+ hasFinishedKeepingOpen = true;
198
209
  }
199
210
  }
200
211
  finally {
201
- if (!options.keepOpen) {
212
+ if (handleRenderError)
213
+ page?.off?.('pageerror', handleRenderError);
214
+ if (!hasFinishedKeepingOpen) {
202
215
  await browser.close();
203
216
  }
204
217
  }
@@ -712,7 +725,8 @@ export default TextContent
712
725
  import React from 'react'
713
726
  function cn(...values) { return values.filter(Boolean).join(' ') }
714
727
  export function CodeBlock({ code, children, className, ...props }) {
715
- return <pre {...props} className={cn('overflow-auto rounded-md border bg-muted p-3 font-mono text-xs', className)}><code>{children ?? code}</code></pre>
728
+ const codeText = Array.isArray(code) ? code.map(line => line.content).join('\\n') : code
729
+ return <pre {...props} className={cn('overflow-auto rounded-md border bg-muted p-3 font-mono text-xs', className)}><code>{children ?? codeText}</code></pre>
716
730
  }
717
731
  export default CodeBlock
718
732
  `;
@@ -727,8 +741,24 @@ export default Prose
727
741
  const contentRendererSource = `
728
742
  import React from 'react'
729
743
  function cn(...values) { return values.filter(Boolean).join(' ') }
730
- export function ContentRenderer({ content, children, className, ...props }) {
731
- return <div {...props} className={cn('whitespace-pre-wrap text-sm leading-relaxed', className)}>{children ?? content}</div>
744
+ export function ContentRenderer({ contentType, content, maxHeight = '400px', customRenderer, className }) {
745
+ let renderedContent
746
+ switch (contentType) {
747
+ case 'image':
748
+ renderedContent = <div className="flex items-center justify-center p-2"><img src={content} alt="Content" width={800} height={600} className="max-w-full object-contain" style={{ maxHeight, height: 'auto', width: 'auto' }} /></div>
749
+ break
750
+ case 'video':
751
+ renderedContent = <div className="flex h-full justify-center p-2"><video src={content} controls className="max-h-full max-w-full" /></div>
752
+ break
753
+ case 'custom':
754
+ renderedContent = customRenderer ? customRenderer({ content }) : <p className="text-destructive">Custom renderer required for custom content type</p>
755
+ break
756
+ default:
757
+ renderedContent = <p>Invalid content type</p>
758
+ }
759
+ return maxHeight
760
+ ? <div className={cn('overflow-auto', className)} style={{ maxHeight }}>{renderedContent}</div>
761
+ : renderedContent
732
762
  }
733
763
  export default ContentRenderer
734
764
  `;
@@ -0,0 +1,4 @@
1
+ export type TaskExportFormat = 'csv' | 'json' | 'jsonl';
2
+ /** Promise runner for the command: rejects with the message the CLI prints. */
3
+ export declare function runTaskExportDownload(taskId: string, format: TaskExportFormat, target: string): Promise<void>;
4
+ //# sourceMappingURL=task-export-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"task-export-workflow.d.ts","sourceRoot":"","sources":["../src/task-export-workflow.ts"],"names":[],"mappings":"AAMA,MAAM,MAAM,gBAAgB,GAAG,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;AA6CvD,+EAA+E;AAC/E,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAGnH"}
@@ -0,0 +1,46 @@
1
+ import { Effect, Exit, Result } from 'effect';
2
+ import { renameSync, rmSync, writeFileSync } from 'node:fs';
3
+ import { basename, dirname, join } from 'node:path';
4
+ import { authedFetch } from './http.js';
5
+ // Node's "CODE: description, syscall 'path'" without the syscall and path, so a
6
+ // failed save names the user's target rather than the temp file.
7
+ function saveFailureReason(cause) {
8
+ const message = cause instanceof Error ? cause.message : String(cause);
9
+ return message.replace(/, \w+( '.*')?$/, '');
10
+ }
11
+ /**
12
+ * Writes the export beside `target` and renames it over the target, so a failed
13
+ * or interrupted save never touches the file the user already has. The temp file
14
+ * is removed whenever the save does not finish.
15
+ */
16
+ function saveExportFile(target, bytes) {
17
+ const temp = join(dirname(target), `.${basename(target)}.${process.pid}.tmp`);
18
+ return Effect.acquireUseRelease(Effect.succeed(temp), path => Effect.try({
19
+ try: () => {
20
+ writeFileSync(path, bytes, { flag: 'wx' });
21
+ renameSync(path, target);
22
+ },
23
+ catch: cause => new Error(`Could not save export to ${target}: ${saveFailureReason(cause)}`, { cause }),
24
+ }), (path, exit) => Exit.isSuccess(exit) ? Effect.void : Effect.sync(() => rmSync(path, { force: true })));
25
+ }
26
+ /**
27
+ * `orizu tasks export`: one GET of the export, never retried, then a safe save.
28
+ * A refused export fails with `Download failed: <body>` before anything is written.
29
+ */
30
+ function downloadTaskExport(taskId, format, target) {
31
+ return Effect.gen(function* () {
32
+ const response = yield* Effect.promise(() => authedFetch(`/api/cli/tasks/${taskId}/export?format=${format}`));
33
+ if (!response.ok) {
34
+ const body = yield* Effect.promise(() => response.text());
35
+ return yield* Effect.fail(new Error(`Download failed: ${body}`));
36
+ }
37
+ const bytes = new Uint8Array(yield* Effect.promise(() => response.arrayBuffer()));
38
+ yield* saveExportFile(target, bytes);
39
+ });
40
+ }
41
+ /** Promise runner for the command: rejects with the message the CLI prints. */
42
+ export async function runTaskExportDownload(taskId, format, target) {
43
+ const result = await Effect.runPromise(Effect.result(downloadTaskExport(taskId, format, target)));
44
+ if (Result.isFailure(result))
45
+ throw result.failure;
46
+ }
@@ -803,7 +803,7 @@ function formatSessionFinishOutcome(result) {
803
803
  // The web page's failed line, with the reason the server mapped (ORI-2264).
804
804
  function failedRunLine(run) {
805
805
  return run.status === 'failed' && typeof run.failureReason === 'string' && run.failureReason
806
- ? `The agent stopped: ${run.failureReason}.`
806
+ ? run.failureReason === 'The selected model is unavailable in this session.' ? run.failureReason : `The agent stopped: ${run.failureReason}.`
807
807
  : null;
808
808
  }
809
809
  function formatRunStatus(run) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orizu",
3
- "version": "0.5.39",
3
+ "version": "0.5.40",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -1,11 +1,12 @@
1
1
  // ORI-2044: moved from index.ts to keep dataset commands together.
2
2
 
3
- import { readFileSync, writeFileSync } from 'fs'
3
+ import { readFileSync } from 'fs'
4
4
  import { basename, extname } from 'path'
5
5
  import { createInterface } from 'readline/promises'
6
6
  import { stdin as input, stdout as output } from 'process'
7
7
  import {
8
8
  getArg,
9
+ hasArg,
9
10
  expandHomePath,
10
11
  getPositionalArg,
11
12
  hasJsonFlag,
@@ -28,7 +29,6 @@ import { printDatasetSummaries } from './archive-artifacts-cli.js'
28
29
  import { formatTerminalLink } from './auth-commands.js'
29
30
  import { parseDatasetFile } from './file-parser.js'
30
31
  import { parseDatasetReference } from './dataset-download.js'
31
- import { editDatasetRows as runEditDatasetRows } from './dataset-edit-rows.js'
32
32
  import { parseCommaSeparated } from './task-commands.js'
33
33
  import type { DatasetSelection } from './account-directory.js'
34
34
 
@@ -45,6 +45,16 @@ interface DatasetVersionResponse {
45
45
  }
46
46
  }
47
47
 
48
+ function readReadmeFile(readmeFile: string): string {
49
+ try {
50
+ return readFileSync(expandHomePath(readmeFile), 'utf8')
51
+ } catch (error) {
52
+ const missing = error instanceof Error && 'code' in error && error.code === 'ENOENT'
53
+ const reason = missing ? 'file not found' : error instanceof Error ? error.message : String(error)
54
+ throw new Error(`Could not read README file ${readmeFile}: ${reason}`)
55
+ }
56
+ }
57
+
48
58
  function readReadmeMarkdownFromArgs(usage: string): string | null {
49
59
  const readmeFile = getArg('--readme-file')
50
60
  const readmeText = getArg('--readme-text')
@@ -54,7 +64,7 @@ function readReadmeMarkdownFromArgs(usage: string): string | null {
54
64
  }
55
65
 
56
66
  const markdown = readmeFile !== null
57
- ? readFileSync(expandHomePath(readmeFile), 'utf8')
67
+ ? readReadmeFile(readmeFile)
58
68
  : readmeText
59
69
 
60
70
  if (markdown !== null && markdown.length > MAX_README_LENGTH) {
@@ -174,6 +184,36 @@ async function createDatasetVersion() {
174
184
  )
175
185
  }
176
186
 
187
+ async function listDatasetSplitSets() {
188
+ const versionId = getPositionalArg(3)
189
+ if (!versionId) throw new Error('Usage: orizu datasets splits list <datasetVersionId> [--json]')
190
+ const { listDatasetSplits } = await import('./dataset-split-reads.js')
191
+ const data = await listDatasetSplits(versionId)
192
+ if (hasJsonFlag()) return printJson({ ...data })
193
+ printLine(`Dataset version ${sanitizeTerminalText(data.dataset_version_id)}`)
194
+ for (const set of data.split_sets) {
195
+ printLine(`${sanitizeTerminalText(set.name)} (${sanitizeTerminalText(set.id)})`)
196
+ for (const partition of set.partitions) printLine(` ${sanitizeTerminalText(partition.name)}: ${partition.row_count} rows`)
197
+ }
198
+ if (!data.split_sets.length) printLine('No saved split sets.')
199
+ }
200
+
201
+ async function showDatasetSplitSet() {
202
+ const setId = getPositionalArg(3)
203
+ if (!setId) throw new Error('Usage: orizu datasets splits show <splitSetId> [--json]')
204
+ const { showDatasetSplit } = await import('./dataset-split-reads.js')
205
+ const data = await showDatasetSplit(setId)
206
+ if (hasJsonFlag()) return printJson({ ...data })
207
+ printLine(`${sanitizeTerminalText(data.name)} (${sanitizeTerminalText(data.split_set_id)})`)
208
+ printLine(`Dataset version: ${sanitizeTerminalText(data.dataset_version_id)}`)
209
+ printLine(`Strategy: ${sanitizeTerminalText(data.strategy)}; seed: ${data.seed ?? 'none'}`)
210
+ printLine(`Metadata: ${sanitizeTerminalText(JSON.stringify(data.metadata))}`)
211
+ for (const partition of data.partitions) {
212
+ printLine(`${sanitizeTerminalText(partition.name)}: ${partition.row_ids.length} rows`)
213
+ for (const id of partition.row_ids) printLine(` ${sanitizeTerminalText(id)}`)
214
+ }
215
+ }
216
+
177
217
  async function createDatasetSplitSet() {
178
218
  const datasetVersionId = getPositionalArg(3) || getArg('--dataset-version')
179
219
  const splitFile = getArg('--from-file')
@@ -194,7 +234,7 @@ async function createDatasetSplitSet() {
194
234
  }
195
235
 
196
236
  const partitions = splitSpec && Array.isArray(splitSpec.partitions) ? splitSpec.partitions : undefined
197
- const metadata = splitSpec && typeof splitSpec.metadata === 'object' && splitSpec.metadata !== null
237
+ const metadata = splitSpec && Object.prototype.hasOwnProperty.call(splitSpec, 'metadata')
198
238
  ? splitSpec.metadata
199
239
  : undefined
200
240
 
@@ -351,37 +391,21 @@ async function setDatasetReadme() {
351
391
  }
352
392
 
353
393
  const resolvedDatasetId = await resolveDatasetIdForProject(datasetId, project)
354
- const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(resolvedDatasetId)}/readme`, {
355
- method: 'POST',
356
- headers: { 'Content-Type': 'application/json' },
357
- body: JSON.stringify({ markdown: readmeMarkdown }),
358
- })
359
-
360
- if (!response.ok) {
361
- throw new Error(`Failed to save dataset README: ${await response.text()}`)
362
- }
363
-
364
- const data = await parseJsonResponse<{
365
- version: {
366
- id: string
367
- dataset_id: string
368
- version_num: number
369
- created_at: string
370
- }
371
- }>(response, 'Dataset README save')
394
+ const { saveDatasetReadme } = await import('./dataset-readme-workflow.js')
395
+ const version = await saveDatasetReadme(resolvedDatasetId, readmeMarkdown)
372
396
 
373
397
  if (hasJsonFlag()) {
374
398
  printJson({
375
- dataset_id: data.version.dataset_id,
376
- readme_version_id: data.version.id,
377
- readme_version_num: data.version.version_num,
399
+ dataset_id: version.dataset_id,
400
+ readme_version_id: version.id,
401
+ readme_version_num: version.version_num,
378
402
  })
379
403
  return
380
404
  }
381
405
 
382
406
  printLine(
383
- `Saved README v${data.version.version_num} for dataset ` +
384
- `${sanitizeTerminalText(data.version.dataset_id)} (${sanitizeTerminalText(data.version.id)}).`
407
+ `Saved README v${version.version_num} for dataset ` +
408
+ `${sanitizeTerminalText(version.dataset_id)} (${sanitizeTerminalText(version.id)}).`
385
409
  )
386
410
  }
387
411
 
@@ -409,6 +433,21 @@ async function downloadDataset() {
409
433
  throw new Error('format must be one of: csv, json, jsonl')
410
434
  }
411
435
 
436
+ const versionId = getArg('--dataset-version'), setId = getArg('--split-set')
437
+ const inlineSplit = cliArgs.find(argument => argument.startsWith('--split='))
438
+ const split = inlineSplit === undefined ? getArg('--split') : inlineSplit.slice('--split='.length)
439
+ if (['--dataset-version', '--split-set', '--split'].some(flag => hasArg(flag) || cliArgs.some(argument => argument.startsWith(`${flag}=`)))) {
440
+ if (!versionId || !setId || !split || [versionId, setId].some(value => value.startsWith('--')) || (inlineSplit === undefined && split.startsWith('--'))) throw new Error('Pinned download requires --dataset-version, --split-set and --split together')
441
+ if (datasetInput) throw new Error('Use --dataset-version with split selectors without a live --dataset or dataset argument')
442
+ if (format === 'csv') throw new Error('Pinned download format must be json or jsonl')
443
+ const filename = outPathArg ? expandHomePath(outPathArg) : `${versionId}.${format}`
444
+ const { downloadPinnedDatasetSplit } = await import('./dataset-split-reads.js')
445
+ await downloadPinnedDatasetSplit(versionId, setId, split, format, filename)
446
+ if (hasJsonFlag()) return printJson({ dataset_version_id: versionId, split_set_id: setId, split, format, savedTo: filename })
447
+ printLine(`Saved split ${sanitizeTerminalText(split)} from dataset version ${sanitizeTerminalText(versionId)} (${format.toUpperCase()}) to ${sanitizeTerminalText(filename)}`)
448
+ return
449
+ }
450
+
412
451
  let datasetId: string
413
452
  if (datasetInput) {
414
453
  datasetId = parseDatasetReference(datasetInput).datasetId
@@ -460,7 +499,8 @@ async function editDatasetRows() {
460
499
  datasetId = selected.datasetId
461
500
  }
462
501
 
463
- await runEditDatasetRows({
502
+ const { editDatasetRows: runEditDatasetRows } = await import('./dataset-row-workflow.js')
503
+ const exitCode = await runEditDatasetRows({
464
504
  datasetId,
465
505
  file: expandHomePath(fileArg),
466
506
  json: hasJsonFlag(),
@@ -468,6 +508,7 @@ async function editDatasetRows() {
468
508
  printLine,
469
509
  sanitize: sanitizeTerminalText,
470
510
  })
511
+ if (exitCode !== 0) process.exitCode = exitCode
471
512
  }
472
513
 
473
514
  async function deleteDatasetRows() {
@@ -487,30 +528,16 @@ async function deleteDatasetRows() {
487
528
  datasetId = selected.datasetId
488
529
  }
489
530
 
490
- const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/rows`, {
491
- method: 'DELETE',
492
- headers: { 'Content-Type': 'application/json' },
493
- body: JSON.stringify({
494
- rowIds,
495
- }),
531
+ const { deleteDatasetRows: runDeleteDatasetRows } = await import('./dataset-row-workflow.js')
532
+ const exitCode = await runDeleteDatasetRows({
533
+ datasetId,
534
+ rowIds,
535
+ json: hasJsonFlag(),
536
+ printJson,
537
+ printLine,
538
+ sanitize: sanitizeTerminalText,
496
539
  })
497
-
498
- if (!response.ok) {
499
- throw new Error(`Delete rows failed: ${await response.text()}`)
500
- }
501
-
502
- const data = await parseJsonResponse<{
503
- dataset: { id: string; name: string; rowCount: number }
504
- deletedCount: number
505
- }>(response, 'Dataset delete rows')
506
-
507
- if (hasJsonFlag()) {
508
- printJson({ dataset: data.dataset, deletedCount: data.deletedCount })
509
- return
510
- }
511
- printLine(
512
- `Deleted ${data.deletedCount} rows from dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}). New row count: ${data.dataset.rowCount}`
513
- )
540
+ if (exitCode !== 0) process.exitCode = exitCode
514
541
  }
515
542
 
516
543
  async function confirmDatasetDeletion(dataset: DatasetSelection) {
@@ -550,23 +577,16 @@ async function deleteDataset() {
550
577
 
551
578
  await confirmDatasetDeletion(dataset)
552
579
 
553
- const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(dataset.datasetId)}`, {
554
- method: 'DELETE',
555
- })
556
-
557
- if (!response.ok) {
558
- throw new Error(`Delete failed: ${await response.text()}`)
559
- }
560
-
561
- const data = await parseJsonResponse<{
562
- dataset: { id: string }
563
- }>(response, 'Dataset delete')
580
+ const { deleteDataset: requestDelete } = await import('./dataset-delete-workflow.js')
581
+ // Only a dataset picked from a project's list has a known project; with
582
+ // --dataset, --project is never checked against the dataset.
583
+ const deleted = await requestDelete(dataset.datasetId, dataset.project ?? null)
564
584
 
565
585
  if (hasJsonFlag()) {
566
- printJson({ dataset: data.dataset, deleted: true })
586
+ printJson({ dataset: deleted, deleted: true })
567
587
  return
568
588
  }
569
- printLine(`Deleted dataset ${sanitizeTerminalText(data.dataset.id)}.`)
589
+ printLine(`Deleted dataset ${sanitizeTerminalText(deleted.id)}.`)
570
590
  }
571
591
 
572
592
  async function lockDataset() {
@@ -582,32 +602,15 @@ async function lockDataset() {
582
602
  datasetId = selected.datasetId
583
603
  }
584
604
 
585
- const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/lock`, {
586
- method: 'POST',
587
- headers: { 'Content-Type': 'application/json' },
588
- body: JSON.stringify(reason ? { reason } : {}),
589
- })
590
-
591
- if (!response.ok) {
592
- throw new Error(`Lock failed: ${await response.text()}`)
593
- }
594
-
595
- const data = await parseJsonResponse<{
596
- dataset: {
597
- id: string
598
- name: string
599
- rowCount: number
600
- lockedAt: string
601
- lockedBy: string | null
602
- }
603
- }>(response, 'Dataset lock')
605
+ const { lockDataset: requestLock } = await import('./dataset-lock-workflow.js')
606
+ const dataset = await requestLock(datasetId, reason)
604
607
 
605
608
  if (hasJsonFlag()) {
606
- printJson({ dataset: data.dataset })
609
+ printJson({ dataset })
607
610
  return
608
611
  }
609
612
  printLine(
610
- `Locked dataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) at ${sanitizeTerminalText(data.dataset.lockedAt)}. Row count: ${data.dataset.rowCount}`
613
+ `Locked dataset ${sanitizeTerminalText(dataset.name)} (${sanitizeTerminalText(dataset.id)}) at ${sanitizeTerminalText(dataset.lockedAt)}. Row count: ${dataset.rowCount}`
611
614
  )
612
615
  }
613
616
 
@@ -665,18 +668,13 @@ async function downloadAnnotations() {
665
668
  taskId = await selectTaskIdInteractively()
666
669
  }
667
670
 
668
- const response = await authedFetch(`/api/cli/tasks/${taskId}/export?format=${format}`)
669
- if (!response.ok) {
670
- throw new Error(`Download failed: ${await response.text()}`)
671
- }
672
-
673
671
  const fallbackName = `${taskId}.${format}`
674
672
  const filename = outPathArg
675
673
  ? expandHomePath(outPathArg)
676
674
  : fallbackName
677
675
 
678
- const bytes = new Uint8Array(await response.arrayBuffer())
679
- writeFileSync(filename, bytes)
676
+ const { runTaskExportDownload } = await import('./task-export-workflow.js')
677
+ await runTaskExportDownload(taskId, format, filename)
680
678
 
681
679
  if (hasJsonFlag()) {
682
680
  printJson({ taskId, format, savedTo: filename })
@@ -694,6 +692,8 @@ export {
694
692
  setDatasetReadme,
695
693
  createDatasetVersion,
696
694
  createDatasetSplitSet,
695
+ listDatasetSplitSets,
696
+ showDatasetSplitSet,
697
697
  editDatasetRows,
698
698
  deleteDatasetRows,
699
699
  deleteDataset,