okrapdf 0.9.1 → 0.11.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OkraPDF
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { doc } from './url.js';
2
- import './types-C7IWrjwl.js';
2
+ import './types-ZJxBNLTJ.js';
3
3
  import 'zod';
4
4
 
5
5
  declare const _default: {
package/dist/browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  doc
3
- } from "./chunk-5NINKIAC.js";
3
+ } from "./chunk-QKII53VN.js";
4
4
 
5
5
  // src/browser.ts
6
6
  var runtimeGlobal = globalThis;
@@ -846,11 +846,7 @@ async function upload(client, source, opts) {
846
846
 
847
847
  // src/cli/commands/collection.ts
848
848
  async function collectionList(client, _opts) {
849
- const res = await client.request(
850
- "/v1/collections",
851
- { method: "GET" }
852
- );
853
- return res.collections;
849
+ return client.collections.list();
854
850
  }
855
851
  function formatCollectionList(rows, json) {
856
852
  if (json) return JSON.stringify(rows);
@@ -861,6 +857,16 @@ function formatCollectionList(rows, json) {
861
857
  );
862
858
  return [header, ...lines].join("\n");
863
859
  }
860
+ async function collectionSetVisibility(client, nameOrId, visibility) {
861
+ return client.request(
862
+ `/v1/collections/${encodeURIComponent(nameOrId)}`,
863
+ {
864
+ method: "PATCH",
865
+ headers: { "Content-Type": "application/json" },
866
+ body: JSON.stringify({ visibility })
867
+ }
868
+ );
869
+ }
864
870
  async function collectionQueryRaw(baseUrl, apiKey, nameOrId, question, opts) {
865
871
  progress(`Querying collection "${nameOrId}"\u2026`, opts.quiet);
866
872
  const body = { prompt: question, stream: true };
@@ -980,9 +986,10 @@ export {
980
986
  upload,
981
987
  collectionList,
982
988
  formatCollectionList,
989
+ collectionSetVisibility,
983
990
  collectionQueryRaw,
984
991
  formatCollectionCsv,
985
992
  formatCollectionTable,
986
993
  formatQueryJsonl
987
994
  };
988
- //# sourceMappingURL=chunk-M52S3LPT.js.map
995
+ //# sourceMappingURL=chunk-4IHOG655.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/commands/tree.ts","../src/cli/query-engine.ts","../src/cli/commands/find.ts","../src/cli/commands/page.ts","../src/cli/commands/search.ts","../src/cli/commands/tables.ts","../src/cli/commands/history.ts","../src/cli/commands/toc.ts","../src/cli/config.ts","../src/cli/commands/auth.ts","../src/cli/output.ts","../src/cli/commands/upload.ts","../src/cli/commands/collection.ts"],"sourcesContent":["/**\n * okra tree - Document verification tree command\n *\n * Maps to: Left panel of review page\n * Shows page-level verification status and entity counts.\n *\n * Usage:\n * okra tree <jobId>\n * okra tree <jobId> --status pending\n * okra tree <jobId> --entity table\n * okra tree <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type {\n VerificationPageStatus,\n EntityType,\n} from '../types';\n\n// Re-export VerificationTree types from CLI types\nexport type { VerificationTree as VerificationTreeResponse } from '../types';\n\nexport interface TreeOptions {\n status?: VerificationPageStatus;\n entity?: EntityType;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface TreeResult {\n tree: any; // Will use VerificationTree from types\n filteredPages: number[];\n}\n\n/**\n * Get the verification tree for a job.\n */\nexport async function tree(\n client: OkraClient,\n jobId: string,\n options: TreeOptions = {}\n): Promise<TreeResult> {\n const treeData = await client.request<any>(`/document/${jobId}/verification-tree`);\n\n // Filter pages if status filter specified\n let filteredPages = treeData.pages.map((p: any) => p.page);\n\n if (options.status) {\n filteredPages = treeData.pages\n .filter((p: any) => p.status === options.status)\n .map((p: any) => p.page);\n }\n\n // If entity filter specified, we need to fetch entities and filter\n if (options.entity) {\n const entitiesData = await client.request<any>(`/document/${jobId}/nodes?type=${options.entity}`);\n const pagesWithEntity = new Set(entitiesData.entities.map((e: any) => e.page));\n filteredPages = filteredPages.filter((p: number) => pagesWithEntity.has(p));\n }\n\n return { tree: treeData, filteredPages };\n}\n\n/**\n * Format tree result for output.\n */\nexport function formatTreeOutput(\n result: TreeResult,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const { tree: treeData, filteredPages } = result;\n const lines: string[] = [];\n\n if (format === 'markdown') {\n lines.push(`# Verification Tree: ${treeData.jobId}`);\n lines.push('');\n lines.push(`**Total Pages:** ${treeData.totalPages}`);\n lines.push('');\n lines.push('## Summary');\n lines.push('| Status | Count |');\n lines.push('|--------|-------|');\n lines.push(`| Complete | ${treeData.summary.complete} |`);\n lines.push(`| Partial | ${treeData.summary.partial} |`);\n lines.push(`| Pending | ${treeData.summary.pending} |`);\n lines.push(`| Flagged | ${treeData.summary.flagged} |`);\n lines.push(`| Empty | ${treeData.summary.empty} |`);\n lines.push(`| Gap | ${treeData.summary.gap} |`);\n lines.push('');\n lines.push('## Pages');\n lines.push('| Page | Status | Total | Verified | Pending | Flagged |');\n lines.push('|------|--------|-------|----------|---------|---------|');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n lines.push(\n `| ${page.page} | ${page.status} | ${page.total} | ${page.verified} | ${page.pending} | ${page.flagged} |`\n );\n }\n }\n } else {\n // Text format\n lines.push(`Verification Tree: ${treeData.jobId}`);\n lines.push(`Total Pages: ${treeData.totalPages}`);\n lines.push('');\n lines.push('Summary:');\n lines.push(` Complete: ${treeData.summary.complete}`);\n lines.push(` Partial: ${treeData.summary.partial}`);\n lines.push(` Pending: ${treeData.summary.pending}`);\n lines.push(` Flagged: ${treeData.summary.flagged}`);\n lines.push(` Empty: ${treeData.summary.empty}`);\n lines.push(` Gap: ${treeData.summary.gap}`);\n lines.push('');\n lines.push('Pages:');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n const statusIcon = getStatusIcon(page.status);\n const counts = `[${page.verified}/${page.total}]`;\n const flags = page.flagged > 0 ? ` (${page.flagged} flagged)` : '';\n const gaps = page.hasCoverageGaps ? ' [GAP]' : '';\n lines.push(` ${statusIcon} p${page.page.toString().padStart(3)} ${counts}${flags}${gaps}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: VerificationPageStatus): string {\n switch (status) {\n case 'complete':\n return '✓';\n case 'partial':\n return '◐';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'empty':\n return '·';\n case 'gap':\n return '!';\n case 'error':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * Query Engine - jQuery-like entity selector\n *\n * Inspired by okra-jquery from ~/dev/okrapdf/lib/okra-jquery\n * Supports selectors like:\n * - Type: .table, .figure, .footnote\n * - ID: #entity_123\n * - Attributes: [confidence>0.9], [verified=true]\n * - Page: :page(5), :pages(1-10)\n * - Combinators: .table[confidence>0.9], .table, .figure\n */\n\nimport type { Entity, EntityType } from './types';\n\nexport interface SelectorParts {\n types: EntityType[];\n id?: string;\n pageFilter?: { type: 'single' | 'range'; value: number | [number, number] };\n confidenceFilter?: { op: '>' | '<' | '>=' | '<='; value: number };\n verificationFilter?: 'pending' | 'verified' | 'flagged' | 'rejected';\n textContains?: string;\n}\n\n/**\n * Parse a jQuery-like selector string into parts.\n *\n * Examples:\n * - \".table\" -> { types: ['table'] }\n * - \".table, .figure\" -> { types: ['table', 'figure'] }\n * - \".table:page(5)\" -> { types: ['table'], pageFilter: { type: 'single', value: 5 } }\n * - \"[confidence>0.9]\" -> { confidenceFilter: { op: '>', value: 0.9 } }\n * - \".table[confidence>=0.8]:page(1-10)\" -> complex filter\n */\nexport function parseSelector(selector: string): SelectorParts {\n const parts: SelectorParts = { types: [] };\n\n // Handle OR combinator first (comma-separated)\n if (selector.includes(',') && !selector.includes('[')) {\n const segments = selector.split(',').map((s) => s.trim());\n for (const seg of segments) {\n const subParts = parseSelector(seg);\n parts.types.push(...subParts.types);\n }\n return parts;\n }\n\n // Extract type selectors (.table, .figure, etc)\n // Must start with letter (not number) to avoid matching .8 in [confidence>=0.8]\n const typeMatches = selector.match(/\\.([a-zA-Z][a-zA-Z0-9_]*)/g);\n if (typeMatches) {\n parts.types = typeMatches.map((m) => m.slice(1) as EntityType);\n }\n\n // Extract ID selector (#entity_123)\n const idMatch = selector.match(/#([\\w-]+)/);\n if (idMatch) {\n parts.id = idMatch[1];\n }\n\n // Extract page filter (:page(5) or :pages(1-10))\n const pageMatch = selector.match(/:pages?\\((\\d+)(?:-(\\d+))?\\)/);\n if (pageMatch) {\n if (pageMatch[2]) {\n parts.pageFilter = {\n type: 'range',\n value: [parseInt(pageMatch[1], 10), parseInt(pageMatch[2], 10)],\n };\n } else {\n parts.pageFilter = {\n type: 'single',\n value: parseInt(pageMatch[1], 10),\n };\n }\n }\n\n // Extract confidence filter ([confidence>0.9])\n const confMatch = selector.match(/\\[confidence(>=?|<=?|>|<)(\\d+\\.?\\d*)\\]/);\n if (confMatch) {\n parts.confidenceFilter = {\n op: confMatch[1] as '>' | '<' | '>=' | '<=',\n value: parseFloat(confMatch[2]),\n };\n }\n\n // Extract verification filter ([verified=true], [status=pending])\n const verifyMatch = selector.match(/\\[(?:verified|status)=(\\w+)\\]/);\n if (verifyMatch) {\n const val = verifyMatch[1].toLowerCase();\n if (val === 'true') {\n parts.verificationFilter = 'verified';\n } else if (['pending', 'verified', 'flagged', 'rejected'].includes(val)) {\n parts.verificationFilter = val as typeof parts.verificationFilter;\n }\n }\n\n // Extract text contains filter (:contains(text))\n const containsMatch = selector.match(/:contains\\(([^)]+)\\)/);\n if (containsMatch) {\n parts.textContains = containsMatch[1];\n }\n\n // If no types specified and selector is \"*\", match all\n if (parts.types.length === 0 && selector.includes('*')) {\n parts.types = ['table', 'figure', 'footnote', 'summary', 'signature', 'paragraph'];\n }\n\n return parts;\n}\n\n/**\n * Filter entities based on parsed selector parts.\n */\nexport function filterEntities(entities: Entity[], parts: SelectorParts): Entity[] {\n return entities.filter((entity) => {\n // Type filter - only apply if types are specified\n if (parts.types.length > 0 && !parts.types.includes(entity.type)) {\n return false;\n }\n\n // ID filter\n if (parts.id && entity.id !== parts.id) {\n return false;\n }\n\n // Page filter\n if (parts.pageFilter) {\n if (parts.pageFilter.type === 'single') {\n if (entity.page !== parts.pageFilter.value) return false;\n } else {\n const [start, end] = parts.pageFilter.value as [number, number];\n if (entity.page < start || entity.page > end) return false;\n }\n }\n\n // Confidence filter\n if (parts.confidenceFilter && entity.confidence !== undefined) {\n const { op, value } = parts.confidenceFilter;\n switch (op) {\n case '>':\n if (!(entity.confidence > value)) return false;\n break;\n case '>=':\n if (!(entity.confidence >= value)) return false;\n break;\n case '<':\n if (!(entity.confidence < value)) return false;\n break;\n case '<=':\n if (!(entity.confidence <= value)) return false;\n break;\n }\n }\n\n // Verification filter\n if (parts.verificationFilter && entity.verificationStatus !== parts.verificationFilter) {\n return false;\n }\n\n // Text contains filter\n if (parts.textContains && entity.title) {\n if (!entity.title.toLowerCase().includes(parts.textContains.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n\nexport interface QueryOptions {\n topK?: number;\n minConfidence?: number;\n pageRange?: [number, number];\n sortBy?: 'confidence' | 'page' | 'type';\n}\n\nexport interface QueryStats {\n total: number;\n byType: Record<string, number>;\n byPage: Record<number, number>;\n avgConfidence: number;\n minConfidence: number;\n maxConfidence: number;\n}\n\nexport interface QueryResult {\n entities: Entity[];\n total: number;\n stats: QueryStats;\n}\n\n/**\n * Execute a query against entities.\n */\nexport function executeQuery(\n entities: Entity[],\n selector: string,\n options: QueryOptions = {}\n): QueryResult {\n const parts = parseSelector(selector);\n\n // Apply min confidence if specified\n if (options.minConfidence !== undefined) {\n parts.confidenceFilter = { op: '>=', value: options.minConfidence };\n }\n\n // Apply page range if specified\n if (options.pageRange) {\n parts.pageFilter = { type: 'range', value: options.pageRange };\n }\n\n let results = filterEntities(entities, parts);\n\n // Sort\n if (options.sortBy) {\n results = [...results].sort((a, b) => {\n switch (options.sortBy) {\n case 'confidence':\n return (b.confidence ?? 0) - (a.confidence ?? 0);\n case 'page':\n return a.page - b.page;\n case 'type':\n return a.type.localeCompare(b.type);\n default:\n return 0;\n }\n });\n }\n\n // Calculate stats\n const stats = calculateStats(results);\n\n // Apply top-k limit\n if (options.topK && options.topK > 0) {\n results = results.slice(0, options.topK);\n }\n\n return {\n entities: results,\n total: results.length,\n stats,\n };\n}\n\n/**\n * Calculate aggregate statistics for entities.\n */\nexport function calculateStats(entities: Entity[]): QueryStats {\n const byType: Record<string, number> = {};\n const byPage: Record<number, number> = {};\n let totalConfidence = 0;\n let minConfidence = Infinity;\n let maxConfidence = -Infinity;\n let confidenceCount = 0;\n\n for (const entity of entities) {\n // Count by type\n byType[entity.type] = (byType[entity.type] || 0) + 1;\n\n // Count by page\n byPage[entity.page] = (byPage[entity.page] || 0) + 1;\n\n // Confidence stats\n if (entity.confidence !== undefined) {\n totalConfidence += entity.confidence;\n confidenceCount++;\n if (entity.confidence < minConfidence) minConfidence = entity.confidence;\n if (entity.confidence > maxConfidence) maxConfidence = entity.confidence;\n }\n }\n\n return {\n total: entities.length,\n byType,\n byPage,\n avgConfidence: confidenceCount > 0 ? totalConfidence / confidenceCount : 0,\n minConfidence: minConfidence === Infinity ? 0 : minConfidence,\n maxConfidence: maxConfidence === -Infinity ? 0 : maxConfidence,\n };\n}\n","/**\n * okra find - Entity search with jQuery-like selectors\n *\n * Maps to: Middle panel of review page (entity overlays)\n * Find entities using CSS-like selectors.\n *\n * Usage:\n * okra find <jobId> \".table\" # Find all tables\n * okra find <jobId> \".figure:page(5)\" # Figures on page 5\n * okra find <jobId> \"[confidence>0.9]\" # High confidence entities\n * okra find <jobId> \".table, .figure\" # Tables OR figures\n * okra find <jobId> \"*\" --stats # All entities with stats\n * okra find <jobId> \".table\" --top-k 10 # Top 10 tables\n */\n\nimport type { OkraClient } from '../../client';\nimport type { Entity } from '../types';\nimport { executeQuery, QueryOptions, QueryResult, QueryStats } from '../query-engine';\n\nexport interface FindOptions extends QueryOptions {\n stats?: boolean;\n format?: 'text' | 'json' | 'entities' | 'ids';\n}\n\n/**\n * Find entities matching a selector.\n */\nexport async function find(\n client: OkraClient,\n jobId: string,\n selector: string,\n options: FindOptions = {}\n): Promise<QueryResult> {\n const entitiesData = await client.request<{ entities: Entity[] }>(`/document/${jobId}/nodes`);\n return executeQuery(entitiesData.entities, selector, options);\n}\n\n/**\n * Format find result for output.\n */\nexport function formatFindOutput(\n result: QueryResult,\n format: 'text' | 'json' | 'entities' | 'ids' = 'text',\n showStats = false\n): string {\n if (format === 'json') {\n return JSON.stringify(showStats ? result : result.entities, null, 2);\n }\n\n if (format === 'ids') {\n return result.entities.map((e) => e.id).join('\\n');\n }\n\n if (format === 'entities') {\n return result.entities\n .map((e) => `${e.type}\\t${e.page}\\t${e.id}\\t${e.title || ''}`)\n .join('\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Found ${result.total} entities`);\n lines.push('');\n\n if (showStats) {\n lines.push('Stats:');\n lines.push(` By Type:`);\n for (const [type, count] of Object.entries(result.stats.byType)) {\n lines.push(` ${type}: ${count}`);\n }\n lines.push(` Confidence: avg=${result.stats.avgConfidence.toFixed(2)}, min=${result.stats.minConfidence.toFixed(2)}, max=${result.stats.maxConfidence.toFixed(2)}`);\n lines.push(` Pages: ${Object.keys(result.stats.byPage).length}`);\n lines.push('');\n }\n\n lines.push('Entities:');\n for (const entity of result.entities) {\n const conf = entity.confidence !== undefined ? ` (${(entity.confidence * 100).toFixed(0)}%)` : '';\n const title = entity.title ? ` \"${entity.title.slice(0, 40)}${entity.title.length > 40 ? '...' : ''}\"` : '';\n lines.push(` [p${entity.page}] ${entity.type}${title}${conf}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format stats only.\n */\nexport function formatStats(stats: QueryStats): string {\n const lines: string[] = [];\n lines.push(`Total: ${stats.total}`);\n lines.push('');\n lines.push('By Type:');\n for (const [type, count] of Object.entries(stats.byType)) {\n const pct = ((count / stats.total) * 100).toFixed(1);\n lines.push(` ${type.padEnd(12)} ${count.toString().padStart(4)} (${pct}%)`);\n }\n lines.push('');\n lines.push('By Page:');\n const pageCounts = Object.entries(stats.byPage)\n .sort((a, b) => parseInt(a[0]) - parseInt(b[0]))\n .slice(0, 20); // Show first 20 pages\n for (const [page, count] of pageCounts) {\n lines.push(` p${page.padStart(3)}: ${'█'.repeat(Math.min(count, 40))} ${count}`);\n }\n if (Object.keys(stats.byPage).length > 20) {\n lines.push(` ... and ${Object.keys(stats.byPage).length - 20} more pages`);\n }\n lines.push('');\n lines.push('Confidence:');\n lines.push(` Average: ${(stats.avgConfidence * 100).toFixed(1)}%`);\n lines.push(` Min: ${(stats.minConfidence * 100).toFixed(1)}%`);\n lines.push(` Max: ${(stats.maxConfidence * 100).toFixed(1)}%`);\n\n return lines.join('\\n');\n}\n","/**\n * okra page - Page content operations\n *\n * Maps to: Right panel of review page (markdown editor)\n * Get, edit, and resolve page content.\n *\n * Usage:\n * okra page get <jobId> <pageNum> # Get page markdown\n * okra page get <jobId> <pageNum> --version 2 # Get specific version\n * okra page edit <jobId> <pageNum> <content> # Edit page content\n * okra page resolve <jobId> <pageNum> reviewed # Mark as reviewed\n * okra page versions <jobId> <pageNum> # List versions\n */\n\nimport type { OkraClient } from '../../client';\nimport type { PageContent, PageVersionsResponse } from '../types';\n\nexport type PageContentResponse = PageContent;\n\nexport interface PageGetOptions {\n version?: number;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface PageResolveOptions {\n resolution: string;\n classification?: string;\n reason?: string;\n}\n\n/**\n * Get page content.\n */\nexport async function pageGet(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageGetOptions = {}\n): Promise<PageContentResponse> {\n if (options.version) {\n return client.request<PageContentResponse>(`/document/${jobId}/pages/${pageNum}/versions/${options.version}`);\n }\n return client.request<PageContentResponse>(`/document/${jobId}/pages/${pageNum}`);\n}\n\n/**\n * Edit page content.\n */\nexport async function pageEdit(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n content: string\n): Promise<{ success: boolean; version: number }> {\n const result = await client.request<{ success: boolean; version: number }>(\n `/document/${jobId}/pages/${pageNum}`,\n {\n method: 'POST',\n body: JSON.stringify({ content }),\n headers: { 'Content-Type': 'application/json' },\n }\n );\n return { success: result.success, version: result.version };\n}\n\n/**\n * Resolve page verification status.\n */\nexport async function pageResolve(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageResolveOptions\n): Promise<{ success: boolean }> {\n return client.request<{ success: boolean }>(\n `/document/${jobId}/pages/${pageNum}/resolve`,\n {\n method: 'POST',\n body: JSON.stringify(options),\n headers: { 'Content-Type': 'application/json' },\n }\n );\n}\n\n/**\n * List page versions.\n */\nexport async function pageVersions(\n client: OkraClient,\n jobId: string,\n pageNum: number\n): Promise<PageVersionsResponse> {\n return client.request<PageVersionsResponse>(`/document/${jobId}/pages/${pageNum}/versions`);\n}\n\n/**\n * Format page content for output.\n */\nexport function formatPageOutput(\n content: PageContentResponse,\n format: 'text' | 'json' | 'markdown' = 'markdown'\n): string {\n if (format === 'json') {\n return JSON.stringify(content, null, 2);\n }\n\n if (format === 'markdown') {\n return content.content;\n }\n\n // Text format with metadata\n const lines: string[] = [];\n lines.push(`Page ${content.page}`);\n if (content.version) {\n lines.push(`Version: ${content.version}`);\n }\n lines.push(`Length: ${content.content.length} chars`);\n lines.push('');\n lines.push('---');\n lines.push(content.content);\n\n return lines.join('\\n');\n}\n\n/**\n * Format versions list for output.\n */\nexport function formatVersionsOutput(\n versions: PageVersionsResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(versions, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Page ${versions.page} - ${versions.versions.length} versions`);\n lines.push(`Current: v${versions.currentVersion}`);\n lines.push('');\n\n for (const v of versions.versions) {\n const current = v.version === versions.currentVersion ? ' *' : '';\n const date = v.createdAt ? new Date(v.createdAt).toLocaleString() : 'unknown';\n lines.push(` v${v.version}${current} [${v.editSource}] ${date}`);\n if (v.preview) {\n lines.push(` \"${v.preview.slice(0, 60)}${v.preview.length > 60 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra search - Full-text search command\n *\n * Search page content across all pages.\n *\n * Usage:\n * okra search <jobId> \"revenue\"\n * okra search <jobId> \"total\" --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { SearchResponse } from '../types';\n\nexport interface SearchOptions {\n format?: 'text' | 'json';\n limit?: number;\n}\n\n/**\n * Search page content.\n */\nexport async function search(\n client: OkraClient,\n jobId: string,\n query: string\n): Promise<SearchResponse> {\n return client.request<SearchResponse>(`/document/${jobId}/search?q=${encodeURIComponent(query)}`);\n}\n\n/**\n * Format search results for output.\n */\nexport function formatSearchOutput(\n result: SearchResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Search: \"${result.query}\"`);\n lines.push(`Found ${result.totalMatches} matches in ${result.results.length} pages`);\n lines.push('');\n\n for (const r of result.results) {\n const source = r.matchSource ? ` [${r.matchSource}]` : '';\n lines.push(`p${r.page.toString().padStart(3)} (${r.matchCount} matches)${source}`);\n if (r.snippet) {\n lines.push(` \"${r.snippet.slice(0, 80)}${r.snippet.length > 80 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra tables - List and filter tables\n *\n * Usage:\n * okra tables <jobId>\n * okra tables <jobId> --page 5\n * okra tables <jobId> --status pending\n * okra tables <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { TablesResponse, Table } from '../types';\n\nexport interface TablesOptions {\n page?: number;\n status?: 'pending' | 'verified' | 'flagged' | 'rejected';\n format?: 'text' | 'json' | 'markdown';\n}\n\n/**\n * Get tables for a job.\n */\nexport async function tables(\n client: OkraClient,\n jobId: string,\n options: TablesOptions = {}\n): Promise<TablesResponse> {\n const url = options.page\n ? `/document/${jobId}/tables?page=${options.page}`\n : `/document/${jobId}/tables`;\n const result = await client.request<TablesResponse>(url);\n\n // Filter by status if specified\n if (options.status) {\n result.tables = result.tables.filter((t) => t.verificationStatus === options.status);\n }\n\n return result;\n}\n\n/**\n * Format tables for output.\n */\nexport function formatTablesOutput(\n result: TablesResponse,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n if (format === 'markdown') {\n // Output each table's markdown\n return result.tables.map((t) => {\n return `## Table (p${t.pageNumber})\\n\\n${t.markdown}`;\n }).join('\\n\\n---\\n\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Tables: ${result.tables.length}`);\n lines.push('');\n\n // Group by page\n const byPage = new Map<number, Table[]>();\n for (const t of result.tables) {\n const pageGroup = byPage.get(t.pageNumber) || [];\n pageGroup.push(t);\n byPage.set(t.pageNumber, pageGroup);\n }\n\n for (const [page, pageTables] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {\n lines.push(`Page ${page}:`);\n for (const t of pageTables) {\n const status = getStatusIcon(t.verificationStatus);\n const conf = t.confidence !== null ? ` (${(t.confidence * 100).toFixed(0)}%)` : '';\n const preview = t.markdown.split('\\n')[0].slice(0, 50);\n lines.push(` ${status} ${t.id}${conf}`);\n lines.push(` ${preview}${t.markdown.length > 50 ? '...' : ''}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: string): string {\n switch (status) {\n case 'verified':\n return '✓';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'rejected':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * okra history - Verification audit trail\n *\n * Usage:\n * okra history <jobId>\n * okra history <jobId> --limit 20\n * okra history <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { HistoryResponse } from '../types';\n\nexport interface HistoryOptions {\n limit?: number;\n format?: 'text' | 'json';\n}\n\n/**\n * Get verification history.\n */\nexport async function history(\n client: OkraClient,\n jobId: string,\n options: HistoryOptions = {}\n): Promise<HistoryResponse> {\n const limit = options.limit || 50;\n return client.request<HistoryResponse>(`/document/${jobId}/history?limit=${limit}`);\n}\n\n/**\n * Format history for output.\n */\nexport function formatHistoryOutput(\n result: HistoryResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`History: ${result.history.length} entries`);\n lines.push('');\n\n for (const entry of result.history) {\n const date = new Date(entry.createdAt).toLocaleString();\n const page = entry.pageNum !== null ? ` p${entry.pageNum}` : '';\n const transition = entry.transitionName || `${entry.previousState || '?'} -> ${entry.state}`;\n const by = entry.triggeredByName || entry.triggeredBy || 'system';\n\n lines.push(`[${date}] ${entry.entityType}${page}`);\n lines.push(` ${transition} by ${by}`);\n if (entry.reason) {\n lines.push(` Reason: ${entry.reason}`);\n }\n if (entry.resolution) {\n lines.push(` Resolution: ${entry.resolution}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra toc - Extract table of contents from a PDF\n *\n * Usage:\n * okra toc <jobId>\n * okra toc <jobId> --max-depth 3\n * okra toc <jobId> --format json\n * okra toc <jobId> --format markdown\n */\n\nimport type { OkraClient } from '../../client';\nimport WebSocket from 'ws';\n\nexport interface TocEntry {\n level: number;\n title: string;\n page: number;\n}\n\nexport interface TocResult {\n file_name: string;\n strategy: string;\n total_entries: number;\n total_pages: number;\n elapsed_ms: number;\n total_elapsed_ms: number;\n toc: TocEntry[];\n _replay?: {\n sessionId: string;\n replayUrl: string;\n };\n}\n\nexport interface TocOptions {\n maxDepth?: number;\n format?: 'text' | 'json' | 'markdown';\n watch?: boolean;\n}\n\n/**\n * Extract table of contents from a document.\n */\nexport async function toc(\n client: OkraClient,\n jobId: string,\n options: TocOptions = {}\n): Promise<TocResult> {\n const params = options.maxDepth ? `?maxDepth=${options.maxDepth}` : '';\n const result = await client.request<TocResult>(`/document/${jobId}/toc${params}`);\n\n // Watch live events if requested\n if (options.watch && result._replay) {\n console.log(`\\n📼 Watching live events for session ${result._replay.sessionId}...\\n`);\n await watchLiveEvents(result._replay.replayUrl);\n }\n\n return result;\n}\n\n/**\n * Connect to WebSocket and watch live TOC extraction events.\n */\nasync function watchLiveEvents(wsUrl: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n let hasSeenEvents = false;\n\n ws.on('open', () => {\n // Request event history (JOIN_SESSION)\n ws.send(JSON.stringify({ type: 'JOIN_SESSION' }));\n });\n\n ws.on('message', (data: WebSocket.Data) => {\n try {\n const event = JSON.parse(data.toString());\n\n // Handle event batch (history replay)\n if (event.type === 'EVENTS_BATCH') {\n for (const evt of event.events) {\n printEvent(evt);\n hasSeenEvents = true;\n }\n }\n // Handle individual events\n else if (event.type && event.type.startsWith('TOC_')) {\n printEvent(event);\n hasSeenEvents = true;\n }\n // Track completion\n else if (event.type === 'TOC_RESPONSE_READY') {\n printEvent(event);\n hasSeenEvents = true;\n // Close after seeing final event\n setTimeout(() => {\n ws.close();\n resolve();\n }, 500);\n }\n } catch (err) {\n console.error('Failed to parse event:', err);\n }\n });\n\n ws.on('error', (error: Error) => {\n console.error('WebSocket error:', error.message);\n reject(error);\n });\n\n ws.on('close', () => {\n if (!hasSeenEvents) {\n console.log('No events received (session may have completed).');\n }\n resolve();\n });\n\n // Timeout after 10 seconds\n setTimeout(() => {\n ws.close();\n resolve();\n }, 10000);\n });\n}\n\n/**\n * Print a single event in a readable format.\n */\nfunction printEvent(event: any): void {\n const timestamp = event.eventTimestamp\n ? new Date(event.eventTimestamp).toISOString().slice(11, 23)\n : '';\n\n const duration = event.cost?.duration_ms\n ? ` (${event.cost.duration_ms}ms)`\n : '';\n\n // Format event type for display\n const eventType = event.type.replace('TOC_', '').replace(/_/g, ' ').toLowerCase();\n\n console.log(`[${timestamp}] ${eventType}${duration}`);\n\n // Print relevant data fields\n if (event.data && Object.keys(event.data).length > 0) {\n const dataStr = formatEventData(event.data);\n if (dataStr) {\n console.log(` ${dataStr}`);\n }\n }\n}\n\n/**\n * Format event data for display.\n */\nfunction formatEventData(data: Record<string, any>): string {\n const relevant: string[] = [];\n\n if (data.fileName) relevant.push(`file: ${data.fileName}`);\n if (data.gcsPath) relevant.push(`path: ${data.gcsPath}`);\n if (data.sizeBytes) relevant.push(`size: ${(data.sizeBytes / 1024 / 1024).toFixed(2)}MB`);\n if (data.sandboxId) relevant.push(`sandbox: ${data.sandboxId.slice(0, 12)}...`);\n if (data.template) relevant.push(`template: ${data.template}`);\n if (data.strategy) relevant.push(`strategy: ${data.strategy}`);\n if (data.totalEntries !== undefined) relevant.push(`entries: ${data.totalEntries}`);\n if (data.exitCode !== undefined) relevant.push(`exit: ${data.exitCode}`);\n if (data.totalElapsedMs) relevant.push(`total: ${data.totalElapsedMs}ms`);\n\n return relevant.join(', ');\n}\n\n/**\n * Format TOC for output.\n */\nexport function formatTocOutput(\n result: TocResult,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n if (format === 'markdown') {\n const lines: string[] = [];\n lines.push(`# Table of Contents\\n`);\n lines.push(`_${result.file_name}_\\n`);\n lines.push(`Strategy: ${result.strategy} | Pages: ${result.total_pages} | Entries: ${result.total_entries}\\n`);\n\n for (const entry of result.toc) {\n const hashes = '#'.repeat(entry.level + 1);\n lines.push(`${hashes} ${entry.title} (p. ${entry.page})`);\n }\n\n return lines.join('\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`File: ${result.file_name}`);\n lines.push(`Strategy: ${result.strategy}`);\n lines.push(`Entries: ${result.total_entries}`);\n lines.push(`Pages: ${result.total_pages}`);\n lines.push(`Elapsed: ${result.elapsed_ms}ms (total: ${result.total_elapsed_ms}ms)`);\n lines.push('');\n\n if (result.total_entries === 0) {\n lines.push('No table of contents found.');\n if (result.strategy === 'none') {\n lines.push('This PDF may not have bookmarks or a printed TOC page.');\n }\n } else {\n lines.push('Table of Contents:');\n lines.push('');\n\n for (const entry of result.toc) {\n const indent = ' '.repeat(entry.level - 1);\n const dots = '.'.repeat(Math.max(1, 60 - indent.length - entry.title.length));\n lines.push(`${indent}${entry.title} ${dots} ${entry.page}`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * Configuration management for okra CLI\n *\n * Priority order (highest to lowest):\n * 1. Environment variable: OKRA_API_KEY\n * 2. Project config: .okrarc or .okra.json in current directory\n * 3. Global config: ~/.okra/config.json\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport interface OkraConfig {\n apiKey?: string;\n baseUrl?: string;\n}\n\n/**\n * Get the global config directory path.\n * Supports XDG_CONFIG_HOME convention.\n */\nexport function getGlobalConfigDir(): string {\n const xdgConfigHome = process.env.XDG_CONFIG_HOME;\n if (xdgConfigHome) {\n return join(xdgConfigHome, 'okra');\n }\n return join(homedir(), '.okra');\n}\n\n/**\n * Get the global config file path.\n */\nexport function getGlobalConfigPath(): string {\n return join(getGlobalConfigDir(), 'config.json');\n}\n\n/**\n * Read global config from ~/.okra/config.json\n */\nexport function readGlobalConfig(): OkraConfig | null {\n try {\n const configPath = getGlobalConfigPath();\n if (!existsSync(configPath)) {\n return null;\n }\n const content = readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n // Silently fail and return null\n return null;\n }\n}\n\n/**\n * Write global config to ~/.okra/config.json\n */\nexport function writeGlobalConfig(config: OkraConfig): void {\n const configDir = getGlobalConfigDir();\n const configPath = getGlobalConfigPath();\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\n/**\n * Find and read project config from current directory.\n * Checks for .okrarc and .okra.json\n */\nexport function readProjectConfig(): OkraConfig | null {\n const projectFiles = ['.okrarc', '.okra.json'];\n\n for (const filename of projectFiles) {\n try {\n const path = join(process.cwd(), filename);\n if (existsSync(path)) {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n } catch (error) {\n // Continue to next file\n continue;\n }\n }\n\n return null;\n}\n\n/**\n * Get API key from all sources with proper priority.\n *\n * Priority order:\n * 1. Environment variable: OKRA_API_KEY\n * 2. Project config: .okrarc or .okra.json\n * 3. Global config: ~/.okra/config.json\n */\nexport function getApiKey(): string | undefined {\n // 1. Check environment variable\n if (process.env.OKRA_API_KEY) {\n return process.env.OKRA_API_KEY;\n }\n\n // 2. Check project config\n const projectConfig = readProjectConfig();\n if (projectConfig?.apiKey) {\n return projectConfig.apiKey;\n }\n\n // 3. Check global config\n const globalConfig = readGlobalConfig();\n if (globalConfig?.apiKey) {\n return globalConfig.apiKey;\n }\n\n return undefined;\n}\n\n/**\n * Get base URL from all sources with proper priority.\n */\nexport function getBaseUrl(): string | undefined {\n // 1. Check environment variable\n if (process.env.OKRA_BASE_URL) {\n return process.env.OKRA_BASE_URL;\n }\n\n // 2. Check project config\n const projectConfig = readProjectConfig();\n if (projectConfig?.baseUrl) {\n return projectConfig.baseUrl;\n }\n\n // 3. Check global config\n const globalConfig = readGlobalConfig();\n if (globalConfig?.baseUrl) {\n return globalConfig.baseUrl;\n }\n\n return 'https://api.okrapdf.com';\n}\n\n/**\n * Get source of API key for debugging.\n */\nexport function getApiKeySource(): string {\n if (process.env.OKRA_API_KEY) {\n return 'environment variable (OKRA_API_KEY)';\n }\n\n const projectConfig = readProjectConfig();\n if (projectConfig?.apiKey) {\n const files = ['.okrarc', '.okra.json'];\n for (const f of files) {\n if (existsSync(join(process.cwd(), f))) {\n return `project config (${f})`;\n }\n }\n }\n\n const globalConfig = readGlobalConfig();\n if (globalConfig?.apiKey) {\n return `global config (${getGlobalConfigPath()})`;\n }\n\n return 'not found';\n}\n","/**\n * okra auth - Manage authentication\n *\n * Usage:\n * okra auth login # Set API key in global config\n * okra auth status # Show current auth status\n * okra auth logout # Remove API key from global config\n */\n\nimport { readGlobalConfig, writeGlobalConfig, getApiKey, getApiKeySource, getGlobalConfigPath } from '../config';\nimport * as readline from 'readline';\n\n/**\n * Prompt user for input.\n */\nfunction prompt(question: string): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Login command - set API key in global config.\n */\nexport async function authLogin(): Promise<void> {\n console.log('okra CLI Authentication');\n console.log('');\n console.log('Get your API key from: https://app.okrapdf.com/settings/api');\n console.log('');\n\n const apiKey = await prompt('Enter your API key: ');\n\n if (!apiKey) {\n console.error('Error: API key cannot be empty');\n process.exit(1);\n }\n\n if (!apiKey.startsWith('okra_')) {\n console.warn('Warning: API key should start with \"okra_\"');\n }\n\n // Read existing config or create new one\n const config = readGlobalConfig() || {};\n config.apiKey = apiKey;\n\n // Write to global config\n writeGlobalConfig(config);\n\n console.log('');\n console.log(`✓ API key saved to ${getGlobalConfigPath()}`);\n console.log('');\n console.log('You can now use okra commands without setting OKRA_API_KEY');\n}\n\n/**\n * Status command - show current auth status.\n */\nexport async function authStatus(): Promise<void> {\n const apiKey = getApiKey();\n const source = getApiKeySource();\n\n console.log('okra CLI Authentication Status');\n console.log('');\n\n if (apiKey) {\n const maskedKey = apiKey.slice(0, 10) + '...' + apiKey.slice(-4);\n console.log(`✓ Authenticated: ${maskedKey}`);\n console.log(` Source: ${source}`);\n } else {\n console.log('✗ Not authenticated');\n console.log('');\n console.log('Set API key via:');\n console.log(' okra auth login');\n console.log(' export OKRA_API_KEY=\"okra_xxx\"');\n }\n\n console.log('');\n}\n\n/**\n * Logout command - remove API key from global config.\n */\nexport async function authLogout(): Promise<void> {\n const config = readGlobalConfig();\n\n if (!config || !config.apiKey) {\n console.log('No API key found in global config');\n return;\n }\n\n // Remove API key but keep other config\n delete config.apiKey;\n writeGlobalConfig(config);\n\n console.log(`✓ API key removed from ${getGlobalConfigPath()}`);\n console.log('');\n console.log('Note: Environment variables and project configs are not affected');\n}\n","/**\n * Shared output helpers for agent-friendly CLI.\n *\n * Conventions:\n * - Human text → stderr (progress, messages)\n * - Machine data → stdout (JSON, CSV, doc IDs)\n * - Exit codes: 0 = success, 1 = client/auth error, 2 = server error\n */\n\nimport { writeFileSync } from 'fs';\nimport { OkraRuntimeError } from '../errors';\n\n/** Global flags propagated from program.opts(). */\nexport interface GlobalFlags {\n json?: boolean;\n quiet?: boolean;\n output?: string;\n}\n\n/** Write data to stdout or --output file. */\nexport function writeOutput(data: string, outputPath?: string): void {\n if (outputPath) {\n writeFileSync(outputPath, data);\n process.stderr.write(`Wrote → ${outputPath}\\n`);\n } else {\n process.stdout.write(data + '\\n');\n }\n}\n\n/** Progress message to stderr (suppressed by --quiet). */\nexport function progress(msg: string, quiet?: boolean): void {\n if (!quiet) process.stderr.write(msg + '\\n');\n}\n\n/** Escape a value for CSV — wraps in quotes if it contains comma, quote, or newline. */\nexport function csvEscape(value: string | number | null | undefined): string {\n const str = String(value ?? '');\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\n/** Structured error handler — JSON to stderr when --json, exit with meaningful code. */\nexport function handleError(error: unknown, json?: boolean): never {\n const msg = error instanceof Error ? error.message : String(error);\n const status =\n error instanceof OkraRuntimeError ? error.status : 1;\n\n if (json) {\n process.stderr.write(JSON.stringify({ error: msg, code: status }) + '\\n');\n } else {\n process.stderr.write(`Error: ${msg}\\n`);\n }\n\n process.exit(status >= 500 ? 2 : 1);\n}\n","/**\n * okra upload — Upload a PDF and optionally wait for processing.\n *\n * Usage:\n * okra upload invoice.pdf # local file\n * okra upload https://sec.gov/filing.pdf # URL\n * okra upload invoice.pdf --no-wait # fire-and-forget\n * okra upload invoice.pdf --json # {\"id\":\"doc-xxx\",\"phase\":\"complete\",\"pages\":42}\n */\n\nimport type { OkraClient } from '../../client';\nimport type { GlobalFlags } from '../output';\nimport { progress } from '../output';\n\nexport interface UploadOpts extends GlobalFlags {\n noWait?: boolean;\n}\n\nexport interface UploadResult {\n id: string;\n phase: string;\n pages?: number;\n urls?: {\n full_md: string;\n page_png: string;\n page_md: string;\n completion: string;\n original: string;\n };\n}\n\nexport async function upload(\n client: OkraClient,\n source: string,\n opts: UploadOpts,\n): Promise<UploadResult> {\n progress(`Uploading ${source}…`, opts.quiet);\n\n const session = await client.upload(source);\n const docId = session.id;\n\n progress(`Document ID: ${docId}`, opts.quiet);\n\n if (opts.noWait) {\n return { id: docId, phase: 'uploading' };\n }\n\n progress('Waiting for processing…', opts.quiet);\n\n const status = await client.wait(docId, {\n pollIntervalMs: 2_000,\n });\n\n const base = `https://api.okrapdf.com/v1/documents/${docId}`;\n const urls = {\n full_md: `${base}/full.md`,\n page_png: `${base}/d_shimmer/pg_{N}.png`,\n page_md: `${base}/pg_{N}.md`,\n completion: `https://api.okrapdf.com/document/${docId}/completion`,\n original: `${base}/original.pdf`,\n };\n\n return {\n id: docId,\n phase: status.phase,\n pages: status.pagesTotal,\n urls,\n };\n}\n","/**\n * okra collection — List collections and run fan-out queries.\n *\n * Usage:\n * okra collection list # table\n * okra collection list --json # JSON array\n * okra collection query mag7-10k \"What was total revenue?\" # table\n * okra collection query mag7-10k \"What was total revenue?\" -o /tmp/rev.csv\n * okra collection query mag7-10k \"Revenue?\" --json # raw JSONL\n */\n\nimport type { OkraClient } from '../../client';\nimport type { GlobalFlags } from '../output';\nimport { progress, csvEscape } from '../output';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface CollectionRow {\n id: string;\n name: string;\n description?: string | null;\n document_count: number;\n}\n\nexport interface QueryResultRow {\n doc_id: string;\n status: string;\n answer: string;\n cost_usd: number;\n duration_ms: number;\n error?: string;\n}\n\nexport interface QuerySummary {\n completed: number;\n failed: number;\n total_cost_usd: number;\n}\n\nexport interface CollectionListOpts extends GlobalFlags {}\n\nexport interface CollectionQueryOpts extends GlobalFlags {\n schema?: string;\n}\n\n// ─── List ────────────────────────────────────────────────────────────────────\n\nexport async function collectionList(\n client: OkraClient,\n _opts: CollectionListOpts,\n): Promise<CollectionRow[]> {\n const res = await client.request<{ collections: CollectionRow[] }>(\n '/v1/collections',\n { method: 'GET' },\n );\n return res.collections;\n}\n\nexport function formatCollectionList(rows: CollectionRow[], json?: boolean): string {\n if (json) return JSON.stringify(rows);\n if (rows.length === 0) return 'No collections found.';\n\n const header = 'ID\\tNAME\\tDOCS';\n const lines = rows.map(\n (r) => `${r.id}\\t${r.name}\\t${r.document_count}`,\n );\n return [header, ...lines].join('\\n');\n}\n\n// ─── Query ───────────────────────────────────────────────────────────────────\n\n/**\n * Raw NDJSON fan-out query — bypasses client.request() to consume the stream.\n * Called directly from bin.ts.\n */\nexport async function collectionQueryRaw(\n baseUrl: string,\n apiKey: string,\n nameOrId: string,\n question: string,\n opts: CollectionQueryOpts,\n): Promise<{ results: QueryResultRow[]; summary: QuerySummary }> {\n progress(`Querying collection \"${nameOrId}\"…`, opts.quiet);\n\n const body: Record<string, unknown> = { prompt: question, stream: true };\n\n if (opts.schema) {\n const { readFileSync } = await import('fs');\n body.schema = JSON.parse(readFileSync(opts.schema, 'utf8'));\n }\n\n const url = `${baseUrl.replace(/\\/+$/, '')}/v1/collections/${encodeURIComponent(nameOrId)}/query`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Collection query failed (${response.status}): ${text}`);\n }\n\n const text = await response.text();\n const lines = text.trim().split('\\n').filter(Boolean);\n const events = lines.map((line) => JSON.parse(line));\n\n const results: QueryResultRow[] = [];\n let summary: QuerySummary = { completed: 0, failed: 0, total_cost_usd: 0 };\n\n for (const event of events) {\n if (event.type === 'result') {\n results.push({\n doc_id: event.doc_id,\n status: event.status,\n answer: event.answer ?? '',\n cost_usd: event.usage?.cost_usd ?? 0,\n duration_ms: event.duration_ms ?? 0,\n error: event.error,\n });\n progress(\n ` ${event.status === 'fulfilled' ? '+' : '!'} ${event.doc_id} (${event.duration_ms}ms)`,\n opts.quiet,\n );\n } else if (event.type === 'done') {\n summary = {\n completed: event.completed,\n failed: event.failed,\n total_cost_usd: event.total_cost_usd,\n };\n } else if (event.type === 'start') {\n progress(` ${event.doc_count} documents`, opts.quiet);\n } else if (event.type === 'error') {\n throw new Error(event.error || 'Collection query error');\n }\n }\n\n return { results, summary };\n}\n\n// ─── Formatters ──────────────────────────────────────────────────────────────\n\nexport function formatCollectionCsv(results: QueryResultRow[]): string {\n const header = 'doc_id,status,answer,cost_usd,duration_ms';\n const rows = results.map((r) =>\n [\n r.doc_id,\n r.status,\n csvEscape(r.answer),\n r.cost_usd,\n r.duration_ms,\n ].join(','),\n );\n return [header, ...rows].join('\\n');\n}\n\nexport function formatCollectionTable(results: QueryResultRow[]): string {\n if (results.length === 0) return 'No results.';\n const header = 'DOC_ID\\tSTATUS\\tANSWER\\tCOST\\tDUR_MS';\n const rows = results.map(\n (r) =>\n `${r.doc_id}\\t${r.status}\\t${r.answer.slice(0, 80)}${r.answer.length > 80 ? '…' : ''}\\t$${r.cost_usd.toFixed(4)}\\t${r.duration_ms}`,\n );\n return [header, ...rows].join('\\n');\n}\n\nexport function formatQueryJsonl(results: QueryResultRow[]): string {\n return results.map((r) => JSON.stringify(r)).join('\\n');\n}\n"],"mappings":"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","names":["getStatusIcon","lines","writeFileSync","writeFileSync","readFileSync","text"]}
1
+ {"version":3,"sources":["../src/cli/commands/tree.ts","../src/cli/query-engine.ts","../src/cli/commands/find.ts","../src/cli/commands/page.ts","../src/cli/commands/search.ts","../src/cli/commands/tables.ts","../src/cli/commands/history.ts","../src/cli/commands/toc.ts","../src/cli/config.ts","../src/cli/commands/auth.ts","../src/cli/output.ts","../src/cli/commands/upload.ts","../src/cli/commands/collection.ts"],"sourcesContent":["/**\n * okra tree - Document verification tree command\n *\n * Maps to: Left panel of review page\n * Shows page-level verification status and entity counts.\n *\n * Usage:\n * okra tree <jobId>\n * okra tree <jobId> --status pending\n * okra tree <jobId> --entity table\n * okra tree <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type {\n VerificationPageStatus,\n EntityType,\n} from '../types';\n\n// Re-export VerificationTree types from CLI types\nexport type { VerificationTree as VerificationTreeResponse } from '../types';\n\nexport interface TreeOptions {\n status?: VerificationPageStatus;\n entity?: EntityType;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface TreeResult {\n tree: any; // Will use VerificationTree from types\n filteredPages: number[];\n}\n\n/**\n * Get the verification tree for a job.\n */\nexport async function tree(\n client: OkraClient,\n jobId: string,\n options: TreeOptions = {}\n): Promise<TreeResult> {\n const treeData = await client.request<any>(`/document/${jobId}/verification-tree`);\n\n // Filter pages if status filter specified\n let filteredPages = treeData.pages.map((p: any) => p.page);\n\n if (options.status) {\n filteredPages = treeData.pages\n .filter((p: any) => p.status === options.status)\n .map((p: any) => p.page);\n }\n\n // If entity filter specified, we need to fetch entities and filter\n if (options.entity) {\n const entitiesData = await client.request<any>(`/document/${jobId}/nodes?type=${options.entity}`);\n const pagesWithEntity = new Set(entitiesData.entities.map((e: any) => e.page));\n filteredPages = filteredPages.filter((p: number) => pagesWithEntity.has(p));\n }\n\n return { tree: treeData, filteredPages };\n}\n\n/**\n * Format tree result for output.\n */\nexport function formatTreeOutput(\n result: TreeResult,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const { tree: treeData, filteredPages } = result;\n const lines: string[] = [];\n\n if (format === 'markdown') {\n lines.push(`# Verification Tree: ${treeData.jobId}`);\n lines.push('');\n lines.push(`**Total Pages:** ${treeData.totalPages}`);\n lines.push('');\n lines.push('## Summary');\n lines.push('| Status | Count |');\n lines.push('|--------|-------|');\n lines.push(`| Complete | ${treeData.summary.complete} |`);\n lines.push(`| Partial | ${treeData.summary.partial} |`);\n lines.push(`| Pending | ${treeData.summary.pending} |`);\n lines.push(`| Flagged | ${treeData.summary.flagged} |`);\n lines.push(`| Empty | ${treeData.summary.empty} |`);\n lines.push(`| Gap | ${treeData.summary.gap} |`);\n lines.push('');\n lines.push('## Pages');\n lines.push('| Page | Status | Total | Verified | Pending | Flagged |');\n lines.push('|------|--------|-------|----------|---------|---------|');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n lines.push(\n `| ${page.page} | ${page.status} | ${page.total} | ${page.verified} | ${page.pending} | ${page.flagged} |`\n );\n }\n }\n } else {\n // Text format\n lines.push(`Verification Tree: ${treeData.jobId}`);\n lines.push(`Total Pages: ${treeData.totalPages}`);\n lines.push('');\n lines.push('Summary:');\n lines.push(` Complete: ${treeData.summary.complete}`);\n lines.push(` Partial: ${treeData.summary.partial}`);\n lines.push(` Pending: ${treeData.summary.pending}`);\n lines.push(` Flagged: ${treeData.summary.flagged}`);\n lines.push(` Empty: ${treeData.summary.empty}`);\n lines.push(` Gap: ${treeData.summary.gap}`);\n lines.push('');\n lines.push('Pages:');\n\n for (const page of treeData.pages) {\n if (filteredPages.includes(page.page)) {\n const statusIcon = getStatusIcon(page.status);\n const counts = `[${page.verified}/${page.total}]`;\n const flags = page.flagged > 0 ? ` (${page.flagged} flagged)` : '';\n const gaps = page.hasCoverageGaps ? ' [GAP]' : '';\n lines.push(` ${statusIcon} p${page.page.toString().padStart(3)} ${counts}${flags}${gaps}`);\n }\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: VerificationPageStatus): string {\n switch (status) {\n case 'complete':\n return '✓';\n case 'partial':\n return '◐';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'empty':\n return '·';\n case 'gap':\n return '!';\n case 'error':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * Query Engine - jQuery-like entity selector\n *\n * Inspired by okra-jquery from ~/dev/okrapdf/lib/okra-jquery\n * Supports selectors like:\n * - Type: .table, .figure, .footnote\n * - ID: #entity_123\n * - Attributes: [confidence>0.9], [verified=true]\n * - Page: :page(5), :pages(1-10)\n * - Combinators: .table[confidence>0.9], .table, .figure\n */\n\nimport type { Entity, EntityType } from './types';\n\nexport interface SelectorParts {\n types: EntityType[];\n id?: string;\n pageFilter?: { type: 'single' | 'range'; value: number | [number, number] };\n confidenceFilter?: { op: '>' | '<' | '>=' | '<='; value: number };\n verificationFilter?: 'pending' | 'verified' | 'flagged' | 'rejected';\n textContains?: string;\n}\n\n/**\n * Parse a jQuery-like selector string into parts.\n *\n * Examples:\n * - \".table\" -> { types: ['table'] }\n * - \".table, .figure\" -> { types: ['table', 'figure'] }\n * - \".table:page(5)\" -> { types: ['table'], pageFilter: { type: 'single', value: 5 } }\n * - \"[confidence>0.9]\" -> { confidenceFilter: { op: '>', value: 0.9 } }\n * - \".table[confidence>=0.8]:page(1-10)\" -> complex filter\n */\nexport function parseSelector(selector: string): SelectorParts {\n const parts: SelectorParts = { types: [] };\n\n // Handle OR combinator first (comma-separated)\n if (selector.includes(',') && !selector.includes('[')) {\n const segments = selector.split(',').map((s) => s.trim());\n for (const seg of segments) {\n const subParts = parseSelector(seg);\n parts.types.push(...subParts.types);\n }\n return parts;\n }\n\n // Extract type selectors (.table, .figure, etc)\n // Must start with letter (not number) to avoid matching .8 in [confidence>=0.8]\n const typeMatches = selector.match(/\\.([a-zA-Z][a-zA-Z0-9_]*)/g);\n if (typeMatches) {\n parts.types = typeMatches.map((m) => m.slice(1) as EntityType);\n }\n\n // Extract ID selector (#entity_123)\n const idMatch = selector.match(/#([\\w-]+)/);\n if (idMatch) {\n parts.id = idMatch[1];\n }\n\n // Extract page filter (:page(5) or :pages(1-10))\n const pageMatch = selector.match(/:pages?\\((\\d+)(?:-(\\d+))?\\)/);\n if (pageMatch) {\n if (pageMatch[2]) {\n parts.pageFilter = {\n type: 'range',\n value: [parseInt(pageMatch[1], 10), parseInt(pageMatch[2], 10)],\n };\n } else {\n parts.pageFilter = {\n type: 'single',\n value: parseInt(pageMatch[1], 10),\n };\n }\n }\n\n // Extract confidence filter ([confidence>0.9])\n const confMatch = selector.match(/\\[confidence(>=?|<=?|>|<)(\\d+\\.?\\d*)\\]/);\n if (confMatch) {\n parts.confidenceFilter = {\n op: confMatch[1] as '>' | '<' | '>=' | '<=',\n value: parseFloat(confMatch[2]),\n };\n }\n\n // Extract verification filter ([verified=true], [status=pending])\n const verifyMatch = selector.match(/\\[(?:verified|status)=(\\w+)\\]/);\n if (verifyMatch) {\n const val = verifyMatch[1].toLowerCase();\n if (val === 'true') {\n parts.verificationFilter = 'verified';\n } else if (['pending', 'verified', 'flagged', 'rejected'].includes(val)) {\n parts.verificationFilter = val as typeof parts.verificationFilter;\n }\n }\n\n // Extract text contains filter (:contains(text))\n const containsMatch = selector.match(/:contains\\(([^)]+)\\)/);\n if (containsMatch) {\n parts.textContains = containsMatch[1];\n }\n\n // If no types specified and selector is \"*\", match all\n if (parts.types.length === 0 && selector.includes('*')) {\n parts.types = ['table', 'figure', 'footnote', 'summary', 'signature', 'paragraph'];\n }\n\n return parts;\n}\n\n/**\n * Filter entities based on parsed selector parts.\n */\nexport function filterEntities(entities: Entity[], parts: SelectorParts): Entity[] {\n return entities.filter((entity) => {\n // Type filter - only apply if types are specified\n if (parts.types.length > 0 && !parts.types.includes(entity.type)) {\n return false;\n }\n\n // ID filter\n if (parts.id && entity.id !== parts.id) {\n return false;\n }\n\n // Page filter\n if (parts.pageFilter) {\n if (parts.pageFilter.type === 'single') {\n if (entity.page !== parts.pageFilter.value) return false;\n } else {\n const [start, end] = parts.pageFilter.value as [number, number];\n if (entity.page < start || entity.page > end) return false;\n }\n }\n\n // Confidence filter\n if (parts.confidenceFilter && entity.confidence !== undefined) {\n const { op, value } = parts.confidenceFilter;\n switch (op) {\n case '>':\n if (!(entity.confidence > value)) return false;\n break;\n case '>=':\n if (!(entity.confidence >= value)) return false;\n break;\n case '<':\n if (!(entity.confidence < value)) return false;\n break;\n case '<=':\n if (!(entity.confidence <= value)) return false;\n break;\n }\n }\n\n // Verification filter\n if (parts.verificationFilter && entity.verificationStatus !== parts.verificationFilter) {\n return false;\n }\n\n // Text contains filter\n if (parts.textContains && entity.title) {\n if (!entity.title.toLowerCase().includes(parts.textContains.toLowerCase())) {\n return false;\n }\n }\n\n return true;\n });\n}\n\nexport interface QueryOptions {\n topK?: number;\n minConfidence?: number;\n pageRange?: [number, number];\n sortBy?: 'confidence' | 'page' | 'type';\n}\n\nexport interface QueryStats {\n total: number;\n byType: Record<string, number>;\n byPage: Record<number, number>;\n avgConfidence: number;\n minConfidence: number;\n maxConfidence: number;\n}\n\nexport interface QueryResult {\n entities: Entity[];\n total: number;\n stats: QueryStats;\n}\n\n/**\n * Execute a query against entities.\n */\nexport function executeQuery(\n entities: Entity[],\n selector: string,\n options: QueryOptions = {}\n): QueryResult {\n const parts = parseSelector(selector);\n\n // Apply min confidence if specified\n if (options.minConfidence !== undefined) {\n parts.confidenceFilter = { op: '>=', value: options.minConfidence };\n }\n\n // Apply page range if specified\n if (options.pageRange) {\n parts.pageFilter = { type: 'range', value: options.pageRange };\n }\n\n let results = filterEntities(entities, parts);\n\n // Sort\n if (options.sortBy) {\n results = [...results].sort((a, b) => {\n switch (options.sortBy) {\n case 'confidence':\n return (b.confidence ?? 0) - (a.confidence ?? 0);\n case 'page':\n return a.page - b.page;\n case 'type':\n return a.type.localeCompare(b.type);\n default:\n return 0;\n }\n });\n }\n\n // Calculate stats\n const stats = calculateStats(results);\n\n // Apply top-k limit\n if (options.topK && options.topK > 0) {\n results = results.slice(0, options.topK);\n }\n\n return {\n entities: results,\n total: results.length,\n stats,\n };\n}\n\n/**\n * Calculate aggregate statistics for entities.\n */\nexport function calculateStats(entities: Entity[]): QueryStats {\n const byType: Record<string, number> = {};\n const byPage: Record<number, number> = {};\n let totalConfidence = 0;\n let minConfidence = Infinity;\n let maxConfidence = -Infinity;\n let confidenceCount = 0;\n\n for (const entity of entities) {\n // Count by type\n byType[entity.type] = (byType[entity.type] || 0) + 1;\n\n // Count by page\n byPage[entity.page] = (byPage[entity.page] || 0) + 1;\n\n // Confidence stats\n if (entity.confidence !== undefined) {\n totalConfidence += entity.confidence;\n confidenceCount++;\n if (entity.confidence < minConfidence) minConfidence = entity.confidence;\n if (entity.confidence > maxConfidence) maxConfidence = entity.confidence;\n }\n }\n\n return {\n total: entities.length,\n byType,\n byPage,\n avgConfidence: confidenceCount > 0 ? totalConfidence / confidenceCount : 0,\n minConfidence: minConfidence === Infinity ? 0 : minConfidence,\n maxConfidence: maxConfidence === -Infinity ? 0 : maxConfidence,\n };\n}\n","/**\n * okra find - Entity search with jQuery-like selectors\n *\n * Maps to: Middle panel of review page (entity overlays)\n * Find entities using CSS-like selectors.\n *\n * Usage:\n * okra find <jobId> \".table\" # Find all tables\n * okra find <jobId> \".figure:page(5)\" # Figures on page 5\n * okra find <jobId> \"[confidence>0.9]\" # High confidence entities\n * okra find <jobId> \".table, .figure\" # Tables OR figures\n * okra find <jobId> \"*\" --stats # All entities with stats\n * okra find <jobId> \".table\" --top-k 10 # Top 10 tables\n */\n\nimport type { OkraClient } from '../../client';\nimport type { Entity } from '../types';\nimport { executeQuery, QueryOptions, QueryResult, QueryStats } from '../query-engine';\n\nexport interface FindOptions extends QueryOptions {\n stats?: boolean;\n format?: 'text' | 'json' | 'entities' | 'ids';\n}\n\n/**\n * Find entities matching a selector.\n */\nexport async function find(\n client: OkraClient,\n jobId: string,\n selector: string,\n options: FindOptions = {}\n): Promise<QueryResult> {\n const entitiesData = await client.request<{ entities: Entity[] }>(`/document/${jobId}/nodes`);\n return executeQuery(entitiesData.entities, selector, options);\n}\n\n/**\n * Format find result for output.\n */\nexport function formatFindOutput(\n result: QueryResult,\n format: 'text' | 'json' | 'entities' | 'ids' = 'text',\n showStats = false\n): string {\n if (format === 'json') {\n return JSON.stringify(showStats ? result : result.entities, null, 2);\n }\n\n if (format === 'ids') {\n return result.entities.map((e) => e.id).join('\\n');\n }\n\n if (format === 'entities') {\n return result.entities\n .map((e) => `${e.type}\\t${e.page}\\t${e.id}\\t${e.title || ''}`)\n .join('\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Found ${result.total} entities`);\n lines.push('');\n\n if (showStats) {\n lines.push('Stats:');\n lines.push(` By Type:`);\n for (const [type, count] of Object.entries(result.stats.byType)) {\n lines.push(` ${type}: ${count}`);\n }\n lines.push(` Confidence: avg=${result.stats.avgConfidence.toFixed(2)}, min=${result.stats.minConfidence.toFixed(2)}, max=${result.stats.maxConfidence.toFixed(2)}`);\n lines.push(` Pages: ${Object.keys(result.stats.byPage).length}`);\n lines.push('');\n }\n\n lines.push('Entities:');\n for (const entity of result.entities) {\n const conf = entity.confidence !== undefined ? ` (${(entity.confidence * 100).toFixed(0)}%)` : '';\n const title = entity.title ? ` \"${entity.title.slice(0, 40)}${entity.title.length > 40 ? '...' : ''}\"` : '';\n lines.push(` [p${entity.page}] ${entity.type}${title}${conf}`);\n }\n\n return lines.join('\\n');\n}\n\n/**\n * Format stats only.\n */\nexport function formatStats(stats: QueryStats): string {\n const lines: string[] = [];\n lines.push(`Total: ${stats.total}`);\n lines.push('');\n lines.push('By Type:');\n for (const [type, count] of Object.entries(stats.byType)) {\n const pct = ((count / stats.total) * 100).toFixed(1);\n lines.push(` ${type.padEnd(12)} ${count.toString().padStart(4)} (${pct}%)`);\n }\n lines.push('');\n lines.push('By Page:');\n const pageCounts = Object.entries(stats.byPage)\n .sort((a, b) => parseInt(a[0]) - parseInt(b[0]))\n .slice(0, 20); // Show first 20 pages\n for (const [page, count] of pageCounts) {\n lines.push(` p${page.padStart(3)}: ${'█'.repeat(Math.min(count, 40))} ${count}`);\n }\n if (Object.keys(stats.byPage).length > 20) {\n lines.push(` ... and ${Object.keys(stats.byPage).length - 20} more pages`);\n }\n lines.push('');\n lines.push('Confidence:');\n lines.push(` Average: ${(stats.avgConfidence * 100).toFixed(1)}%`);\n lines.push(` Min: ${(stats.minConfidence * 100).toFixed(1)}%`);\n lines.push(` Max: ${(stats.maxConfidence * 100).toFixed(1)}%`);\n\n return lines.join('\\n');\n}\n","/**\n * okra page - Page content operations\n *\n * Maps to: Right panel of review page (markdown editor)\n * Get, edit, and resolve page content.\n *\n * Usage:\n * okra page get <jobId> <pageNum> # Get page markdown\n * okra page get <jobId> <pageNum> --version 2 # Get specific version\n * okra page edit <jobId> <pageNum> <content> # Edit page content\n * okra page resolve <jobId> <pageNum> reviewed # Mark as reviewed\n * okra page versions <jobId> <pageNum> # List versions\n */\n\nimport type { OkraClient } from '../../client';\nimport type { PageContent, PageVersionsResponse } from '../types';\n\nexport type PageContentResponse = PageContent;\n\nexport interface PageGetOptions {\n version?: number;\n format?: 'text' | 'json' | 'markdown';\n}\n\nexport interface PageResolveOptions {\n resolution: string;\n classification?: string;\n reason?: string;\n}\n\n/**\n * Get page content.\n */\nexport async function pageGet(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageGetOptions = {}\n): Promise<PageContentResponse> {\n if (options.version) {\n return client.request<PageContentResponse>(`/document/${jobId}/pages/${pageNum}/versions/${options.version}`);\n }\n return client.request<PageContentResponse>(`/document/${jobId}/pages/${pageNum}`);\n}\n\n/**\n * Edit page content.\n */\nexport async function pageEdit(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n content: string\n): Promise<{ success: boolean; version: number }> {\n const result = await client.request<{ success: boolean; version: number }>(\n `/document/${jobId}/pages/${pageNum}`,\n {\n method: 'POST',\n body: JSON.stringify({ content }),\n headers: { 'Content-Type': 'application/json' },\n }\n );\n return { success: result.success, version: result.version };\n}\n\n/**\n * Resolve page verification status.\n */\nexport async function pageResolve(\n client: OkraClient,\n jobId: string,\n pageNum: number,\n options: PageResolveOptions\n): Promise<{ success: boolean }> {\n return client.request<{ success: boolean }>(\n `/document/${jobId}/pages/${pageNum}/resolve`,\n {\n method: 'POST',\n body: JSON.stringify(options),\n headers: { 'Content-Type': 'application/json' },\n }\n );\n}\n\n/**\n * List page versions.\n */\nexport async function pageVersions(\n client: OkraClient,\n jobId: string,\n pageNum: number\n): Promise<PageVersionsResponse> {\n return client.request<PageVersionsResponse>(`/document/${jobId}/pages/${pageNum}/versions`);\n}\n\n/**\n * Format page content for output.\n */\nexport function formatPageOutput(\n content: PageContentResponse,\n format: 'text' | 'json' | 'markdown' = 'markdown'\n): string {\n if (format === 'json') {\n return JSON.stringify(content, null, 2);\n }\n\n if (format === 'markdown') {\n return content.content;\n }\n\n // Text format with metadata\n const lines: string[] = [];\n lines.push(`Page ${content.page}`);\n if (content.version) {\n lines.push(`Version: ${content.version}`);\n }\n lines.push(`Length: ${content.content.length} chars`);\n lines.push('');\n lines.push('---');\n lines.push(content.content);\n\n return lines.join('\\n');\n}\n\n/**\n * Format versions list for output.\n */\nexport function formatVersionsOutput(\n versions: PageVersionsResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(versions, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Page ${versions.page} - ${versions.versions.length} versions`);\n lines.push(`Current: v${versions.currentVersion}`);\n lines.push('');\n\n for (const v of versions.versions) {\n const current = v.version === versions.currentVersion ? ' *' : '';\n const date = v.createdAt ? new Date(v.createdAt).toLocaleString() : 'unknown';\n lines.push(` v${v.version}${current} [${v.editSource}] ${date}`);\n if (v.preview) {\n lines.push(` \"${v.preview.slice(0, 60)}${v.preview.length > 60 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra search - Full-text search command\n *\n * Search page content across all pages.\n *\n * Usage:\n * okra search <jobId> \"revenue\"\n * okra search <jobId> \"total\" --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { SearchResponse } from '../types';\n\nexport interface SearchOptions {\n format?: 'text' | 'json';\n limit?: number;\n}\n\n/**\n * Search page content.\n */\nexport async function search(\n client: OkraClient,\n jobId: string,\n query: string\n): Promise<SearchResponse> {\n return client.request<SearchResponse>(`/document/${jobId}/search?q=${encodeURIComponent(query)}`);\n}\n\n/**\n * Format search results for output.\n */\nexport function formatSearchOutput(\n result: SearchResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`Search: \"${result.query}\"`);\n lines.push(`Found ${result.totalMatches} matches in ${result.results.length} pages`);\n lines.push('');\n\n for (const r of result.results) {\n const source = r.matchSource ? ` [${r.matchSource}]` : '';\n lines.push(`p${r.page.toString().padStart(3)} (${r.matchCount} matches)${source}`);\n if (r.snippet) {\n lines.push(` \"${r.snippet.slice(0, 80)}${r.snippet.length > 80 ? '...' : ''}\"`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra tables - List and filter tables\n *\n * Usage:\n * okra tables <jobId>\n * okra tables <jobId> --page 5\n * okra tables <jobId> --status pending\n * okra tables <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { TablesResponse, Table } from '../types';\n\nexport interface TablesOptions {\n page?: number;\n status?: 'pending' | 'verified' | 'flagged' | 'rejected';\n format?: 'text' | 'json' | 'markdown';\n}\n\n/**\n * Get tables for a job.\n */\nexport async function tables(\n client: OkraClient,\n jobId: string,\n options: TablesOptions = {}\n): Promise<TablesResponse> {\n const url = options.page\n ? `/document/${jobId}/tables?page=${options.page}`\n : `/document/${jobId}/tables`;\n const result = await client.request<TablesResponse>(url);\n\n // Filter by status if specified\n if (options.status) {\n result.tables = result.tables.filter((t) => t.verificationStatus === options.status);\n }\n\n return result;\n}\n\n/**\n * Format tables for output.\n */\nexport function formatTablesOutput(\n result: TablesResponse,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n if (format === 'markdown') {\n // Output each table's markdown\n return result.tables.map((t) => {\n return `## Table (p${t.pageNumber})\\n\\n${t.markdown}`;\n }).join('\\n\\n---\\n\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`Tables: ${result.tables.length}`);\n lines.push('');\n\n // Group by page\n const byPage = new Map<number, Table[]>();\n for (const t of result.tables) {\n const pageGroup = byPage.get(t.pageNumber) || [];\n pageGroup.push(t);\n byPage.set(t.pageNumber, pageGroup);\n }\n\n for (const [page, pageTables] of [...byPage.entries()].sort((a, b) => a[0] - b[0])) {\n lines.push(`Page ${page}:`);\n for (const t of pageTables) {\n const status = getStatusIcon(t.verificationStatus);\n const conf = t.confidence !== null ? ` (${(t.confidence * 100).toFixed(0)}%)` : '';\n const preview = t.markdown.split('\\n')[0].slice(0, 50);\n lines.push(` ${status} ${t.id}${conf}`);\n lines.push(` ${preview}${t.markdown.length > 50 ? '...' : ''}`);\n }\n }\n\n return lines.join('\\n');\n}\n\nfunction getStatusIcon(status: string): string {\n switch (status) {\n case 'verified':\n return '✓';\n case 'pending':\n return '○';\n case 'flagged':\n return '⚑';\n case 'rejected':\n return '✗';\n default:\n return '?';\n }\n}\n","/**\n * okra history - Verification audit trail\n *\n * Usage:\n * okra history <jobId>\n * okra history <jobId> --limit 20\n * okra history <jobId> --format json\n */\n\nimport type { OkraClient } from '../../client';\nimport type { HistoryResponse } from '../types';\n\nexport interface HistoryOptions {\n limit?: number;\n format?: 'text' | 'json';\n}\n\n/**\n * Get verification history.\n */\nexport async function history(\n client: OkraClient,\n jobId: string,\n options: HistoryOptions = {}\n): Promise<HistoryResponse> {\n const limit = options.limit || 50;\n return client.request<HistoryResponse>(`/document/${jobId}/history?limit=${limit}`);\n}\n\n/**\n * Format history for output.\n */\nexport function formatHistoryOutput(\n result: HistoryResponse,\n format: 'text' | 'json' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n const lines: string[] = [];\n lines.push(`History: ${result.history.length} entries`);\n lines.push('');\n\n for (const entry of result.history) {\n const date = new Date(entry.createdAt).toLocaleString();\n const page = entry.pageNum !== null ? ` p${entry.pageNum}` : '';\n const transition = entry.transitionName || `${entry.previousState || '?'} -> ${entry.state}`;\n const by = entry.triggeredByName || entry.triggeredBy || 'system';\n\n lines.push(`[${date}] ${entry.entityType}${page}`);\n lines.push(` ${transition} by ${by}`);\n if (entry.reason) {\n lines.push(` Reason: ${entry.reason}`);\n }\n if (entry.resolution) {\n lines.push(` Resolution: ${entry.resolution}`);\n }\n lines.push('');\n }\n\n return lines.join('\\n');\n}\n","/**\n * okra toc - Extract table of contents from a PDF\n *\n * Usage:\n * okra toc <jobId>\n * okra toc <jobId> --max-depth 3\n * okra toc <jobId> --format json\n * okra toc <jobId> --format markdown\n */\n\nimport type { OkraClient } from '../../client';\nimport WebSocket from 'ws';\n\nexport interface TocEntry {\n level: number;\n title: string;\n page: number;\n}\n\nexport interface TocResult {\n file_name: string;\n strategy: string;\n total_entries: number;\n total_pages: number;\n elapsed_ms: number;\n total_elapsed_ms: number;\n toc: TocEntry[];\n _replay?: {\n sessionId: string;\n replayUrl: string;\n };\n}\n\nexport interface TocOptions {\n maxDepth?: number;\n format?: 'text' | 'json' | 'markdown';\n watch?: boolean;\n}\n\n/**\n * Extract table of contents from a document.\n */\nexport async function toc(\n client: OkraClient,\n jobId: string,\n options: TocOptions = {}\n): Promise<TocResult> {\n const params = options.maxDepth ? `?maxDepth=${options.maxDepth}` : '';\n const result = await client.request<TocResult>(`/document/${jobId}/toc${params}`);\n\n // Watch live events if requested\n if (options.watch && result._replay) {\n console.log(`\\n📼 Watching live events for session ${result._replay.sessionId}...\\n`);\n await watchLiveEvents(result._replay.replayUrl);\n }\n\n return result;\n}\n\n/**\n * Connect to WebSocket and watch live TOC extraction events.\n */\nasync function watchLiveEvents(wsUrl: string): Promise<void> {\n return new Promise((resolve, reject) => {\n const ws = new WebSocket(wsUrl);\n let hasSeenEvents = false;\n\n ws.on('open', () => {\n // Request event history (JOIN_SESSION)\n ws.send(JSON.stringify({ type: 'JOIN_SESSION' }));\n });\n\n ws.on('message', (data: WebSocket.Data) => {\n try {\n const event = JSON.parse(data.toString());\n\n // Handle event batch (history replay)\n if (event.type === 'EVENTS_BATCH') {\n for (const evt of event.events) {\n printEvent(evt);\n hasSeenEvents = true;\n }\n }\n // Handle individual events\n else if (event.type && event.type.startsWith('TOC_')) {\n printEvent(event);\n hasSeenEvents = true;\n }\n // Track completion\n else if (event.type === 'TOC_RESPONSE_READY') {\n printEvent(event);\n hasSeenEvents = true;\n // Close after seeing final event\n setTimeout(() => {\n ws.close();\n resolve();\n }, 500);\n }\n } catch (err) {\n console.error('Failed to parse event:', err);\n }\n });\n\n ws.on('error', (error: Error) => {\n console.error('WebSocket error:', error.message);\n reject(error);\n });\n\n ws.on('close', () => {\n if (!hasSeenEvents) {\n console.log('No events received (session may have completed).');\n }\n resolve();\n });\n\n // Timeout after 10 seconds\n setTimeout(() => {\n ws.close();\n resolve();\n }, 10000);\n });\n}\n\n/**\n * Print a single event in a readable format.\n */\nfunction printEvent(event: any): void {\n const timestamp = event.eventTimestamp\n ? new Date(event.eventTimestamp).toISOString().slice(11, 23)\n : '';\n\n const duration = event.cost?.duration_ms\n ? ` (${event.cost.duration_ms}ms)`\n : '';\n\n // Format event type for display\n const eventType = event.type.replace('TOC_', '').replace(/_/g, ' ').toLowerCase();\n\n console.log(`[${timestamp}] ${eventType}${duration}`);\n\n // Print relevant data fields\n if (event.data && Object.keys(event.data).length > 0) {\n const dataStr = formatEventData(event.data);\n if (dataStr) {\n console.log(` ${dataStr}`);\n }\n }\n}\n\n/**\n * Format event data for display.\n */\nfunction formatEventData(data: Record<string, any>): string {\n const relevant: string[] = [];\n\n if (data.fileName) relevant.push(`file: ${data.fileName}`);\n if (data.gcsPath) relevant.push(`path: ${data.gcsPath}`);\n if (data.sizeBytes) relevant.push(`size: ${(data.sizeBytes / 1024 / 1024).toFixed(2)}MB`);\n if (data.sandboxId) relevant.push(`sandbox: ${data.sandboxId.slice(0, 12)}...`);\n if (data.template) relevant.push(`template: ${data.template}`);\n if (data.strategy) relevant.push(`strategy: ${data.strategy}`);\n if (data.totalEntries !== undefined) relevant.push(`entries: ${data.totalEntries}`);\n if (data.exitCode !== undefined) relevant.push(`exit: ${data.exitCode}`);\n if (data.totalElapsedMs) relevant.push(`total: ${data.totalElapsedMs}ms`);\n\n return relevant.join(', ');\n}\n\n/**\n * Format TOC for output.\n */\nexport function formatTocOutput(\n result: TocResult,\n format: 'text' | 'json' | 'markdown' = 'text'\n): string {\n if (format === 'json') {\n return JSON.stringify(result, null, 2);\n }\n\n if (format === 'markdown') {\n const lines: string[] = [];\n lines.push(`# Table of Contents\\n`);\n lines.push(`_${result.file_name}_\\n`);\n lines.push(`Strategy: ${result.strategy} | Pages: ${result.total_pages} | Entries: ${result.total_entries}\\n`);\n\n for (const entry of result.toc) {\n const hashes = '#'.repeat(entry.level + 1);\n lines.push(`${hashes} ${entry.title} (p. ${entry.page})`);\n }\n\n return lines.join('\\n');\n }\n\n // Text format\n const lines: string[] = [];\n lines.push(`File: ${result.file_name}`);\n lines.push(`Strategy: ${result.strategy}`);\n lines.push(`Entries: ${result.total_entries}`);\n lines.push(`Pages: ${result.total_pages}`);\n lines.push(`Elapsed: ${result.elapsed_ms}ms (total: ${result.total_elapsed_ms}ms)`);\n lines.push('');\n\n if (result.total_entries === 0) {\n lines.push('No table of contents found.');\n if (result.strategy === 'none') {\n lines.push('This PDF may not have bookmarks or a printed TOC page.');\n }\n } else {\n lines.push('Table of Contents:');\n lines.push('');\n\n for (const entry of result.toc) {\n const indent = ' '.repeat(entry.level - 1);\n const dots = '.'.repeat(Math.max(1, 60 - indent.length - entry.title.length));\n lines.push(`${indent}${entry.title} ${dots} ${entry.page}`);\n }\n }\n\n return lines.join('\\n');\n}\n","/**\n * Configuration management for okra CLI\n *\n * Priority order (highest to lowest):\n * 1. Environment variable: OKRA_API_KEY\n * 2. Project config: .okrarc or .okra.json in current directory\n * 3. Global config: ~/.okra/config.json\n */\n\nimport { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\n\nexport interface OkraConfig {\n apiKey?: string;\n baseUrl?: string;\n}\n\n/**\n * Get the global config directory path.\n * Supports XDG_CONFIG_HOME convention.\n */\nexport function getGlobalConfigDir(): string {\n const xdgConfigHome = process.env.XDG_CONFIG_HOME;\n if (xdgConfigHome) {\n return join(xdgConfigHome, 'okra');\n }\n return join(homedir(), '.okra');\n}\n\n/**\n * Get the global config file path.\n */\nexport function getGlobalConfigPath(): string {\n return join(getGlobalConfigDir(), 'config.json');\n}\n\n/**\n * Read global config from ~/.okra/config.json\n */\nexport function readGlobalConfig(): OkraConfig | null {\n try {\n const configPath = getGlobalConfigPath();\n if (!existsSync(configPath)) {\n return null;\n }\n const content = readFileSync(configPath, 'utf-8');\n return JSON.parse(content);\n } catch (error) {\n // Silently fail and return null\n return null;\n }\n}\n\n/**\n * Write global config to ~/.okra/config.json\n */\nexport function writeGlobalConfig(config: OkraConfig): void {\n const configDir = getGlobalConfigDir();\n const configPath = getGlobalConfigPath();\n\n // Create directory if it doesn't exist\n if (!existsSync(configDir)) {\n mkdirSync(configDir, { recursive: true });\n }\n\n writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');\n}\n\n/**\n * Find and read project config from current directory.\n * Checks for .okrarc and .okra.json\n */\nexport function readProjectConfig(): OkraConfig | null {\n const projectFiles = ['.okrarc', '.okra.json'];\n\n for (const filename of projectFiles) {\n try {\n const path = join(process.cwd(), filename);\n if (existsSync(path)) {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content);\n }\n } catch (error) {\n // Continue to next file\n continue;\n }\n }\n\n return null;\n}\n\n/**\n * Get API key from all sources with proper priority.\n *\n * Priority order:\n * 1. Environment variable: OKRA_API_KEY\n * 2. Project config: .okrarc or .okra.json\n * 3. Global config: ~/.okra/config.json\n */\nexport function getApiKey(): string | undefined {\n // 1. Check environment variable\n if (process.env.OKRA_API_KEY) {\n return process.env.OKRA_API_KEY;\n }\n\n // 2. Check project config\n const projectConfig = readProjectConfig();\n if (projectConfig?.apiKey) {\n return projectConfig.apiKey;\n }\n\n // 3. Check global config\n const globalConfig = readGlobalConfig();\n if (globalConfig?.apiKey) {\n return globalConfig.apiKey;\n }\n\n return undefined;\n}\n\n/**\n * Get base URL from all sources with proper priority.\n */\nexport function getBaseUrl(): string | undefined {\n // 1. Check environment variable\n if (process.env.OKRA_BASE_URL) {\n return process.env.OKRA_BASE_URL;\n }\n\n // 2. Check project config\n const projectConfig = readProjectConfig();\n if (projectConfig?.baseUrl) {\n return projectConfig.baseUrl;\n }\n\n // 3. Check global config\n const globalConfig = readGlobalConfig();\n if (globalConfig?.baseUrl) {\n return globalConfig.baseUrl;\n }\n\n return 'https://api.okrapdf.com';\n}\n\n/**\n * Get source of API key for debugging.\n */\nexport function getApiKeySource(): string {\n if (process.env.OKRA_API_KEY) {\n return 'environment variable (OKRA_API_KEY)';\n }\n\n const projectConfig = readProjectConfig();\n if (projectConfig?.apiKey) {\n const files = ['.okrarc', '.okra.json'];\n for (const f of files) {\n if (existsSync(join(process.cwd(), f))) {\n return `project config (${f})`;\n }\n }\n }\n\n const globalConfig = readGlobalConfig();\n if (globalConfig?.apiKey) {\n return `global config (${getGlobalConfigPath()})`;\n }\n\n return 'not found';\n}\n","/**\n * okra auth - Manage authentication\n *\n * Usage:\n * okra auth login # Set API key in global config\n * okra auth status # Show current auth status\n * okra auth logout # Remove API key from global config\n */\n\nimport { readGlobalConfig, writeGlobalConfig, getApiKey, getApiKeySource, getGlobalConfigPath } from '../config';\nimport * as readline from 'readline';\n\n/**\n * Prompt user for input.\n */\nfunction prompt(question: string): Promise<string> {\n const rl = readline.createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n/**\n * Login command - set API key in global config.\n */\nexport async function authLogin(): Promise<void> {\n console.log('okra CLI Authentication');\n console.log('');\n console.log('Get your API key from: https://app.okrapdf.com/settings/api');\n console.log('');\n\n const apiKey = await prompt('Enter your API key: ');\n\n if (!apiKey) {\n console.error('Error: API key cannot be empty');\n process.exit(1);\n }\n\n if (!apiKey.startsWith('okra_')) {\n console.warn('Warning: API key should start with \"okra_\"');\n }\n\n // Read existing config or create new one\n const config = readGlobalConfig() || {};\n config.apiKey = apiKey;\n\n // Write to global config\n writeGlobalConfig(config);\n\n console.log('');\n console.log(`✓ API key saved to ${getGlobalConfigPath()}`);\n console.log('');\n console.log('You can now use okra commands without setting OKRA_API_KEY');\n}\n\n/**\n * Status command - show current auth status.\n */\nexport async function authStatus(): Promise<void> {\n const apiKey = getApiKey();\n const source = getApiKeySource();\n\n console.log('okra CLI Authentication Status');\n console.log('');\n\n if (apiKey) {\n const maskedKey = apiKey.slice(0, 10) + '...' + apiKey.slice(-4);\n console.log(`✓ Authenticated: ${maskedKey}`);\n console.log(` Source: ${source}`);\n } else {\n console.log('✗ Not authenticated');\n console.log('');\n console.log('Set API key via:');\n console.log(' okra auth login');\n console.log(' export OKRA_API_KEY=\"okra_xxx\"');\n }\n\n console.log('');\n}\n\n/**\n * Logout command - remove API key from global config.\n */\nexport async function authLogout(): Promise<void> {\n const config = readGlobalConfig();\n\n if (!config || !config.apiKey) {\n console.log('No API key found in global config');\n return;\n }\n\n // Remove API key but keep other config\n delete config.apiKey;\n writeGlobalConfig(config);\n\n console.log(`✓ API key removed from ${getGlobalConfigPath()}`);\n console.log('');\n console.log('Note: Environment variables and project configs are not affected');\n}\n","/**\n * Shared output helpers for agent-friendly CLI.\n *\n * Conventions:\n * - Human text → stderr (progress, messages)\n * - Machine data → stdout (JSON, CSV, doc IDs)\n * - Exit codes: 0 = success, 1 = client/auth error, 2 = server error\n */\n\nimport { writeFileSync } from 'fs';\nimport { OkraRuntimeError } from '../errors';\n\n/** Global flags propagated from program.opts(). */\nexport interface GlobalFlags {\n json?: boolean;\n quiet?: boolean;\n output?: string;\n}\n\n/** Write data to stdout or --output file. */\nexport function writeOutput(data: string, outputPath?: string): void {\n if (outputPath) {\n writeFileSync(outputPath, data);\n process.stderr.write(`Wrote → ${outputPath}\\n`);\n } else {\n process.stdout.write(data + '\\n');\n }\n}\n\n/** Progress message to stderr (suppressed by --quiet). */\nexport function progress(msg: string, quiet?: boolean): void {\n if (!quiet) process.stderr.write(msg + '\\n');\n}\n\n/** Escape a value for CSV — wraps in quotes if it contains comma, quote, or newline. */\nexport function csvEscape(value: string | number | null | undefined): string {\n const str = String(value ?? '');\n if (str.includes(',') || str.includes('\"') || str.includes('\\n')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n return str;\n}\n\n/** Structured error handler — JSON to stderr when --json, exit with meaningful code. */\nexport function handleError(error: unknown, json?: boolean): never {\n const msg = error instanceof Error ? error.message : String(error);\n const status =\n error instanceof OkraRuntimeError ? error.status : 1;\n\n if (json) {\n process.stderr.write(JSON.stringify({ error: msg, code: status }) + '\\n');\n } else {\n process.stderr.write(`Error: ${msg}\\n`);\n }\n\n process.exit(status >= 500 ? 2 : 1);\n}\n","/**\n * okra upload — Upload a PDF and optionally wait for processing.\n *\n * Usage:\n * okra upload invoice.pdf # local file\n * okra upload https://sec.gov/filing.pdf # URL\n * okra upload invoice.pdf --no-wait # fire-and-forget\n * okra upload invoice.pdf --json # {\"id\":\"doc-xxx\",\"phase\":\"complete\",\"pages\":42}\n */\n\nimport type { OkraClient } from '../../client';\nimport type { GlobalFlags } from '../output';\nimport { progress } from '../output';\n\nexport interface UploadOpts extends GlobalFlags {\n noWait?: boolean;\n}\n\nexport interface UploadResult {\n id: string;\n phase: string;\n pages?: number;\n urls?: {\n full_md: string;\n page_png: string;\n page_md: string;\n completion: string;\n original: string;\n };\n}\n\nexport async function upload(\n client: OkraClient,\n source: string,\n opts: UploadOpts,\n): Promise<UploadResult> {\n progress(`Uploading ${source}…`, opts.quiet);\n\n const session = await client.upload(source);\n const docId = session.id;\n\n progress(`Document ID: ${docId}`, opts.quiet);\n\n if (opts.noWait) {\n return { id: docId, phase: 'uploading' };\n }\n\n progress('Waiting for processing…', opts.quiet);\n\n const status = await client.wait(docId, {\n pollIntervalMs: 2_000,\n });\n\n const base = `https://api.okrapdf.com/v1/documents/${docId}`;\n const urls = {\n full_md: `${base}/full.md`,\n page_png: `${base}/d_shimmer/pg_{N}.png`,\n page_md: `${base}/pg_{N}.md`,\n completion: `https://api.okrapdf.com/document/${docId}/completion`,\n original: `${base}/original.pdf`,\n };\n\n return {\n id: docId,\n phase: status.phase,\n pages: status.pagesTotal,\n urls,\n };\n}\n","/**\n * okra collection — List collections and run fan-out queries.\n *\n * Usage:\n * okra collection list # table\n * okra collection list --json # JSON array\n * okra collection query mag7-10k \"What was total revenue?\" # table\n * okra collection query mag7-10k \"What was total revenue?\" -o /tmp/rev.csv\n * okra collection query mag7-10k \"Revenue?\" --json # raw JSONL\n */\n\nimport type { OkraClient } from '../../client';\nimport type { GlobalFlags } from '../output';\nimport { progress, csvEscape } from '../output';\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface CollectionRow {\n id: string;\n name: string;\n description?: string | null;\n document_count: number;\n}\n\nexport interface QueryResultRow {\n doc_id: string;\n status: string;\n answer: string;\n cost_usd: number;\n duration_ms: number;\n error?: string;\n}\n\nexport interface QuerySummary {\n completed: number;\n failed: number;\n total_cost_usd: number;\n}\n\nexport interface CollectionListOpts extends GlobalFlags {}\n\nexport interface CollectionQueryOpts extends GlobalFlags {\n schema?: string;\n}\n\n// ─── List ────────────────────────────────────────────────────────────────────\n\nexport async function collectionList(\n client: OkraClient,\n _opts: CollectionListOpts,\n): Promise<CollectionRow[]> {\n return client.collections.list() as Promise<CollectionRow[]>;\n}\n\nexport function formatCollectionList(rows: CollectionRow[], json?: boolean): string {\n if (json) return JSON.stringify(rows);\n if (rows.length === 0) return 'No collections found.';\n\n const header = 'ID\\tNAME\\tDOCS';\n const lines = rows.map(\n (r) => `${r.id}\\t${r.name}\\t${r.document_count}`,\n );\n return [header, ...lines].join('\\n');\n}\n\n// ─── Publish / Unpublish ─────────────────────────────────────────────────────\n\nexport async function collectionSetVisibility(\n client: OkraClient,\n nameOrId: string,\n visibility: 'public' | 'private',\n): Promise<{ ok: boolean }> {\n return client.request<{ ok: boolean }>(\n `/v1/collections/${encodeURIComponent(nameOrId)}`,\n {\n method: 'PATCH',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ visibility }),\n },\n );\n}\n\n// ─── Query ───────────────────────────────────────────────────────────────────\n\n/**\n * Raw NDJSON fan-out query — bypasses client.request() to consume the stream.\n * Called directly from bin.ts.\n */\nexport async function collectionQueryRaw(\n baseUrl: string,\n apiKey: string,\n nameOrId: string,\n question: string,\n opts: CollectionQueryOpts,\n): Promise<{ results: QueryResultRow[]; summary: QuerySummary }> {\n progress(`Querying collection \"${nameOrId}\"…`, opts.quiet);\n\n const body: Record<string, unknown> = { prompt: question, stream: true };\n\n if (opts.schema) {\n const { readFileSync } = await import('fs');\n body.schema = JSON.parse(readFileSync(opts.schema, 'utf8'));\n }\n\n const url = `${baseUrl.replace(/\\/+$/, '')}/v1/collections/${encodeURIComponent(nameOrId)}/query`;\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${apiKey}`,\n },\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Collection query failed (${response.status}): ${text}`);\n }\n\n const text = await response.text();\n const lines = text.trim().split('\\n').filter(Boolean);\n const events = lines.map((line) => JSON.parse(line));\n\n const results: QueryResultRow[] = [];\n let summary: QuerySummary = { completed: 0, failed: 0, total_cost_usd: 0 };\n\n for (const event of events) {\n if (event.type === 'result') {\n results.push({\n doc_id: event.doc_id,\n status: event.status,\n answer: event.answer ?? '',\n cost_usd: event.usage?.cost_usd ?? 0,\n duration_ms: event.duration_ms ?? 0,\n error: event.error,\n });\n progress(\n ` ${event.status === 'fulfilled' ? '+' : '!'} ${event.doc_id} (${event.duration_ms}ms)`,\n opts.quiet,\n );\n } else if (event.type === 'done') {\n summary = {\n completed: event.completed,\n failed: event.failed,\n total_cost_usd: event.total_cost_usd,\n };\n } else if (event.type === 'start') {\n progress(` ${event.doc_count} documents`, opts.quiet);\n } else if (event.type === 'error') {\n throw new Error(event.error || 'Collection query error');\n }\n }\n\n return { results, summary };\n}\n\n// ─── Formatters ──────────────────────────────────────────────────────────────\n\nexport function formatCollectionCsv(results: QueryResultRow[]): string {\n const header = 'doc_id,status,answer,cost_usd,duration_ms';\n const rows = results.map((r) =>\n [\n r.doc_id,\n r.status,\n csvEscape(r.answer),\n r.cost_usd,\n r.duration_ms,\n ].join(','),\n );\n return [header, ...rows].join('\\n');\n}\n\nexport function formatCollectionTable(results: QueryResultRow[]): string {\n if (results.length === 0) return 'No results.';\n const header = 'DOC_ID\\tSTATUS\\tANSWER\\tCOST\\tDUR_MS';\n const rows = results.map(\n (r) =>\n `${r.doc_id}\\t${r.status}\\t${r.answer.slice(0, 80)}${r.answer.length > 80 ? '…' : ''}\\t$${r.cost_usd.toFixed(4)}\\t${r.duration_ms}`,\n );\n return [header, ...rows].join('\\n');\n}\n\nexport function formatQueryJsonl(results: QueryResultRow[]): string {\n return results.map((r) => JSON.stringify(r)).join('\\n');\n}\n"],"mappings":"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","names":["getStatusIcon","lines","writeFileSync","writeFileSync","readFileSync","text"]}
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  OkraClient
3
- } from "./chunk-4RJBVZJL.js";
3
+ } from "./chunk-OVRTURGN.js";
4
4
 
5
5
  // src/providers.ts
6
6
  function isPlainObject(value) {
@@ -81,4 +81,4 @@ export {
81
81
  withQualityScore,
82
82
  withSecret
83
83
  };
84
- //# sourceMappingURL=chunk-FS55NWKZ.js.map
84
+ //# sourceMappingURL=chunk-ME6F2MXQ.js.map
@@ -135,6 +135,9 @@ var OkraSessionHandle = class {
135
135
  query(sql, signal) {
136
136
  return this.#client.query(this.id, sql, signal);
137
137
  }
138
+ logs(options) {
139
+ return this.#client.logs(this.id, options);
140
+ }
138
141
  publish(signal) {
139
142
  return this.#client.publish(this.id, signal);
140
143
  }
@@ -169,6 +172,7 @@ var OkraClient = class {
169
172
  sharedSecret;
170
173
  fetchImpl;
171
174
  sessions;
175
+ collections;
172
176
  constructor(options) {
173
177
  this.baseUrl = normalizeBaseUrl(options.baseUrl || DEFAULT_BASE_URL);
174
178
  this.apiKey = options.apiKey;
@@ -217,6 +221,24 @@ var OkraClient = class {
217
221
  );
218
222
  }
219
223
  };
224
+ this.collections = {
225
+ list: (signal) => this.collectionList(signal),
226
+ get: (collectionId, signal) => this.collectionGet(collectionId, signal)
227
+ };
228
+ }
229
+ // ─── Collections ────────────────────────────────────────────────────────
230
+ async collectionList(signal) {
231
+ const res = await this.requestJson(
232
+ "/v1/collections",
233
+ { method: "GET", signal }
234
+ );
235
+ return res.collections;
236
+ }
237
+ async collectionGet(collectionId, signal) {
238
+ return this.requestJson(
239
+ `/v1/collections/${encodeURIComponent(collectionId)}`,
240
+ { method: "GET", signal }
241
+ );
220
242
  }
221
243
  // ─── Upload ──────────────────────────────────────────────────────────────
222
244
  async upload(input, options = {}) {
@@ -353,6 +375,15 @@ var OkraClient = class {
353
375
  { method: "GET", signal }
354
376
  );
355
377
  }
378
+ // ─── Logs ───────────────────────────────────────────────────────────────
379
+ async logs(documentId, options) {
380
+ const limit = options?.limit ?? 100;
381
+ const res = await this.requestJson(
382
+ `/document/${encodeURIComponent(documentId)}/log?limit=${limit}`,
383
+ { method: "GET", signal: options?.signal }
384
+ );
385
+ return res.entries;
386
+ }
356
387
  // ─── Stream (streaming completion via OpenAI SSE) ────────────────────────
357
388
  async *stream(documentId, query, options) {
358
389
  const response = await this.rawRequest(
@@ -582,4 +613,4 @@ var OkraClient = class {
582
613
  export {
583
614
  OkraClient
584
615
  };
585
- //# sourceMappingURL=chunk-4RJBVZJL.js.map
616
+ //# sourceMappingURL=chunk-OVRTURGN.js.map