okrapdf 0.8.0 → 0.9.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/dist/browser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { doc } from './url.js';
2
- import './types-DEYgGUnH.js';
2
+ import './types-C7IWrjwl.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-C6ZT7DKX.js";
3
+ } from "./chunk-5NINKIAC.js";
4
4
 
5
5
  // src/browser.ts
6
6
  var runtimeGlobal = globalThis;
@@ -1,24 +1,7 @@
1
- // src/errors.ts
2
- var OkraRuntimeError = class extends Error {
3
- code;
4
- status;
5
- details;
6
- constructor(code, message, status = 500, details) {
7
- super(message);
8
- this.name = "OkraRuntimeError";
9
- this.code = code;
10
- this.status = status;
11
- this.details = details;
12
- }
13
- };
14
- var StructuredOutputError = class extends OkraRuntimeError {
15
- code;
16
- constructor(code, message, status, details) {
17
- super(code, message, status, details);
18
- this.name = "StructuredOutputError";
19
- this.code = code;
20
- }
21
- };
1
+ import {
2
+ OkraRuntimeError,
3
+ StructuredOutputError
4
+ } from "./chunk-NIZM2ETT.js";
22
5
 
23
6
  // src/client.ts
24
7
  import { z } from "zod";
@@ -186,7 +169,6 @@ var OkraClient = class {
186
169
  sharedSecret;
187
170
  fetchImpl;
188
171
  sessions;
189
- deployments;
190
172
  constructor(options) {
191
173
  this.baseUrl = normalizeBaseUrl(options.baseUrl || DEFAULT_BASE_URL);
192
174
  this.apiKey = options.apiKey;
@@ -235,46 +217,6 @@ var OkraClient = class {
235
217
  );
236
218
  }
237
219
  };
238
- this.deployments = {
239
- create: async (opts) => {
240
- return this.requestJson("/v1/deployments", {
241
- method: "POST",
242
- headers: { "Content-Type": "application/json" },
243
- body: JSON.stringify(opts)
244
- });
245
- },
246
- status: async (deploymentId) => {
247
- return this.requestJson(`/d/${deploymentId}/status`, {
248
- method: "GET"
249
- });
250
- },
251
- completion: async (deploymentId, opts) => {
252
- return this.requestJson(`/d/${deploymentId}/completion`, {
253
- method: "POST",
254
- headers: { "Content-Type": "application/json" },
255
- body: JSON.stringify(opts)
256
- });
257
- },
258
- tokens: {
259
- create: async (deploymentId, opts) => {
260
- return this.requestJson(`/d/${deploymentId}/tokens`, {
261
- method: "POST",
262
- headers: { "Content-Type": "application/json" },
263
- body: JSON.stringify(opts)
264
- });
265
- },
266
- list: async (deploymentId) => {
267
- return this.requestJson(`/d/${deploymentId}/tokens`, {
268
- method: "GET"
269
- });
270
- },
271
- revoke: async (deploymentId, tokenHint) => {
272
- return this.requestJson(`/d/${deploymentId}/tokens/${tokenHint}`, {
273
- method: "DELETE"
274
- });
275
- }
276
- }
277
- };
278
220
  }
279
221
  // ─── Upload ──────────────────────────────────────────────────────────────
280
222
  async upload(input, options = {}) {
@@ -411,15 +353,15 @@ var OkraClient = class {
411
353
  { method: "GET", signal }
412
354
  );
413
355
  }
414
- // ─── Stream (streaming completion) ────────────────────────────────────────
356
+ // ─── Stream (streaming completion via OpenAI SSE) ────────────────────────
415
357
  async *stream(documentId, query, options) {
416
358
  const response = await this.rawRequest(
417
- `/document/${encodeURIComponent(documentId)}/completion`,
359
+ `/document/${encodeURIComponent(documentId)}/chat/completions`,
418
360
  {
419
361
  method: "POST",
420
362
  headers: { "Content-Type": "application/json" },
421
363
  body: JSON.stringify({
422
- prompt: query,
364
+ messages: [{ role: "user", content: query }],
423
365
  stream: options?.stream !== false,
424
366
  ...options?.model ? { model: options.model } : {}
425
367
  }),
@@ -436,6 +378,7 @@ var OkraClient = class {
436
378
  const reader = response.body.getReader();
437
379
  const decoder = new TextDecoder();
438
380
  let buffer = "";
381
+ let fullText = "";
439
382
  try {
440
383
  while (true) {
441
384
  const { done, value } = await reader.read();
@@ -445,19 +388,27 @@ var OkraClient = class {
445
388
  buffer = lines.pop() || "";
446
389
  for (const line of lines) {
447
390
  const trimmed = line.trim();
448
- if (!trimmed) continue;
391
+ if (!trimmed || trimmed === "data: [DONE]") continue;
392
+ if (!trimmed.startsWith("data: ")) continue;
393
+ const json = trimmed.slice(6);
449
394
  try {
450
- yield JSON.parse(trimmed);
395
+ const chunk = JSON.parse(json);
396
+ if (chunk.error) {
397
+ yield { type: "error", message: chunk.error.message || "Stream error" };
398
+ continue;
399
+ }
400
+ const delta = chunk.choices?.[0]?.delta?.content;
401
+ if (delta) {
402
+ fullText += delta;
403
+ yield { type: "text_delta", text: delta };
404
+ }
405
+ if (chunk.choices?.[0]?.finish_reason === "stop") {
406
+ yield { type: "done", answer: fullText };
407
+ }
451
408
  } catch {
452
409
  }
453
410
  }
454
411
  }
455
- if (buffer.trim()) {
456
- try {
457
- yield JSON.parse(buffer.trim());
458
- } catch {
459
- }
460
- }
461
412
  } finally {
462
413
  reader.releaseLock();
463
414
  }
@@ -467,20 +418,19 @@ var OkraClient = class {
467
418
  return this.generateStructured(documentId, query, options);
468
419
  }
469
420
  const result = await this.requestJson(
470
- `/document/${encodeURIComponent(documentId)}/completion`,
421
+ `/document/${encodeURIComponent(documentId)}/chat/completions`,
471
422
  {
472
423
  method: "POST",
473
424
  headers: { "Content-Type": "application/json" },
474
425
  body: JSON.stringify({
475
- prompt: query,
426
+ messages: [{ role: "user", content: query }],
476
427
  ...options?.model ? { model: options.model } : {}
477
428
  }),
478
429
  signal: options?.signal
479
430
  }
480
431
  );
481
432
  return {
482
- answer: result.answer,
483
- costUsd: result.usage?.costUsd
433
+ answer: result.choices?.[0]?.message?.content || ""
484
434
  };
485
435
  }
486
436
  async generateStructured(documentId, query, options) {
@@ -488,39 +438,50 @@ var OkraClient = class {
488
438
  throw new OkraRuntimeError("INVALID_REQUEST", "generate with schema requires a non-empty query", 400);
489
439
  }
490
440
  const normalized = normalizeSchema(options.schema);
491
- const body = await this.requestJson(
492
- `/document/${encodeURIComponent(documentId)}/completion`,
441
+ const result = await this.requestJson(
442
+ `/document/${encodeURIComponent(documentId)}/chat/completions`,
493
443
  {
494
444
  method: "POST",
495
445
  headers: { "Content-Type": "application/json" },
496
446
  body: JSON.stringify({
497
- prompt: query,
498
- schema: normalized.jsonSchema,
499
- ...options.model ? { model: options.model } : {},
500
- ...options.timeoutMs ? { timeoutMs: options.timeoutMs } : {}
447
+ messages: [{ role: "user", content: query }],
448
+ response_format: {
449
+ type: "json_schema",
450
+ json_schema: { name: "result", schema: normalized.jsonSchema }
451
+ },
452
+ ...options.model ? { model: options.model } : {}
501
453
  }),
502
454
  signal: options.signal
503
455
  }
504
456
  );
457
+ const raw = result.choices?.[0]?.message?.content;
458
+ if (!raw) {
459
+ throw new OkraRuntimeError("INVALID_RESPONSE", "No content in structured output response", 500);
460
+ }
461
+ let parsed;
462
+ try {
463
+ parsed = JSON.parse(raw);
464
+ } catch {
465
+ throw new OkraRuntimeError("INVALID_RESPONSE", "Structured output response is not valid JSON", 500);
466
+ }
505
467
  let data;
506
468
  if (normalized.parser) {
507
- const parsed = normalized.parser.safeParse(body.data);
508
- if (!parsed.success) {
469
+ const zodResult = normalized.parser.safeParse(parsed);
470
+ if (!zodResult.success) {
509
471
  throw new StructuredOutputError(
510
472
  "SCHEMA_VALIDATION_FAILED",
511
473
  "Client-side schema validation failed for structured output response",
512
474
  422,
513
- parsed.error.issues
475
+ zodResult.error.issues
514
476
  );
515
477
  }
516
- data = parsed.data;
478
+ data = zodResult.data;
517
479
  } else {
518
- data = body.data;
480
+ data = parsed;
519
481
  }
520
482
  return {
521
483
  answer: "",
522
- data,
523
- meta: body.meta
484
+ data
524
485
  };
525
486
  }
526
487
  // ─── Model Endpoint ──────────────────────────────────────────────────────
@@ -619,8 +580,6 @@ var OkraClient = class {
619
580
  };
620
581
 
621
582
  export {
622
- OkraRuntimeError,
623
- StructuredOutputError,
624
583
  OkraClient
625
584
  };
626
- //# sourceMappingURL=chunk-HITG34US.js.map
585
+ //# sourceMappingURL=chunk-4RJBVZJL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["import { z, type ZodType } from 'zod';\nimport { OkraRuntimeError, StructuredOutputError } from './errors';\nimport type {\n OkraClientOptions,\n CompletionEvent,\n CompletionOptions,\n DocumentStatus,\n EntitiesResponse,\n GenerateOptions,\n GenerateResult,\n JsonSchema,\n Page,\n PublishResult,\n QueryResult,\n RuntimeErrorCode,\n ShareLinkOptions,\n ShareLinkResult,\n SessionAttachOptions,\n SessionCreateOptions,\n SessionState,\n OkraSession,\n StructuredOutputErrorCode,\n StructuredSchema,\n UploadInput,\n UploadOptions,\n WaitOptions,\n} from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.okrapdf.com';\nconst DEFAULT_WAIT_TIMEOUT_MS = 5 * 60_000;\nconst DEFAULT_WAIT_POLL_MS = 1_500;\nconst COMPLETE_PHASES = new Set(['complete', 'awaiting_review']);\nconst TERMINAL_ERROR_PHASES = new Set(['error']);\nconst STRUCTURED_CODES = new Set<StructuredOutputErrorCode>([\n 'SCHEMA_VALIDATION_FAILED',\n 'EXTRACTION_FAILED',\n 'TIMEOUT',\n 'DOCUMENT_NOT_FOUND',\n]);\nconst NODE_FS_PROMISES_SPECIFIER = `node:${'fs/promises'}`;\nconst NODE_PATH_SPECIFIER = `node:${'path'}`;\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nfunction isHttpUrl(value: string): boolean {\n return /^https?:\\/\\//i.test(value);\n}\n\nfunction isDocumentId(value: string): boolean {\n return /^(?:ocr|doc)-[A-Za-z0-9_-]+$/.test(value);\n}\n\nfunction normalizeBaseUrl(baseUrl: string): string {\n return baseUrl.replace(/\\/+$/, '');\n}\n\nfunction makeDocId(): string {\n if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {\n return `doc-${crypto.randomUUID().replace(/-/g, '').slice(0, 20)}`;\n }\n const rand = Math.random().toString(36).slice(2, 22);\n return `doc-${rand}`;\n}\n\nfunction toUint8Array(input: ArrayBuffer | Uint8Array): Uint8Array {\n if (input instanceof Uint8Array) return input;\n return new Uint8Array(input);\n}\n\ninterface NodeFsModule {\n readFile(path: string): Promise<ArrayBuffer | Uint8Array>;\n}\n\ninterface NodePathModule {\n basename(path: string): string;\n}\n\ninterface NamedBlob extends Blob {\n name?: string;\n}\n\nfunction isBlobLike(input: unknown): input is Blob {\n if (typeof Blob !== 'undefined' && input instanceof Blob) return true;\n return !!input\n && typeof input === 'object'\n && typeof (input as { arrayBuffer?: unknown }).arrayBuffer === 'function';\n}\n\nfunction inferBlobName(input: Blob, fallback: string): string {\n const named = input as NamedBlob;\n if (typeof named.name === 'string' && named.name.trim() !== '') {\n return named.name;\n }\n return fallback;\n}\n\nasync function readLocalFileFromNode(inputPath: string): Promise<{ bytes: Uint8Array; fileName: string }> {\n try {\n const [fsModule, pathModule] = await Promise.all([\n import(NODE_FS_PROMISES_SPECIFIER) as Promise<NodeFsModule>,\n import(NODE_PATH_SPECIFIER) as Promise<NodePathModule>,\n ]);\n const raw = await fsModule.readFile(inputPath);\n return {\n bytes: toUint8Array(raw),\n fileName: pathModule.basename(inputPath),\n };\n } catch (error) {\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n 'Local file path uploads are only supported in Node.js. In browser runtimes, pass File/Blob, ArrayBuffer, Uint8Array, or URL.',\n 400,\n error,\n );\n }\n}\n\ninterface StructuredErrorEnvelope {\n code?: string;\n message?: string;\n details?: unknown;\n error?: string;\n}\n\ninterface NormalizedSchema<T> {\n jsonSchema: JsonSchema;\n parser?: ZodType<T>;\n}\n\nfunction normalizeSchema<T>(schema: StructuredSchema<T>): NormalizedSchema<T> {\n const maybeZod = schema as ZodType<T>;\n const hasSafeParse = typeof (maybeZod as { safeParse?: unknown }).safeParse === 'function';\n if (hasSafeParse) {\n return {\n jsonSchema: z.toJSONSchema(maybeZod, { target: 'draft-2020-12' }) as JsonSchema,\n parser: maybeZod,\n };\n }\n return { jsonSchema: schema as JsonSchema };\n}\n\nfunction isStructuredCode(code: string | undefined): code is StructuredOutputErrorCode {\n return !!code && STRUCTURED_CODES.has(code as StructuredOutputErrorCode);\n}\n\n// ─── Session Handle ──────────────────────────────────────────────────────────\n\nclass OkraSessionHandle implements OkraSession {\n readonly id: string;\n readonly modelEndpoint: string;\n #model?: string;\n #client: OkraClient;\n\n constructor(client: OkraClient, documentId: string, model?: string) {\n this.#client = client;\n this.id = documentId;\n this.modelEndpoint = client.modelEndpoint(documentId);\n this.#model = model;\n }\n\n get model(): string | undefined {\n return this.#model;\n }\n\n state(): SessionState {\n return {\n id: this.id,\n model: this.#model,\n modelEndpoint: this.modelEndpoint,\n };\n }\n\n async setModel(model: string): Promise<void> {\n const normalized = model.trim();\n if (!normalized) {\n throw new OkraRuntimeError('INVALID_REQUEST', 'session.setModel requires a non-empty model', 400);\n }\n this.#model = normalized;\n }\n\n status(signal?: AbortSignal): Promise<DocumentStatus> {\n return this.#client.status(this.id, signal);\n }\n\n wait(options?: WaitOptions): Promise<DocumentStatus> {\n return this.#client.wait(this.id, options);\n }\n\n pages(options?: { range?: string; signal?: AbortSignal }): Promise<Page[]> {\n return this.#client.pages(this.id, options);\n }\n\n page(pageNumber: number, signal?: AbortSignal): Promise<Page> {\n return this.#client.page(this.id, pageNumber, signal);\n }\n\n entities(options?: { type?: string; limit?: number; offset?: number; signal?: AbortSignal }): Promise<EntitiesResponse> {\n return this.#client.entities(this.id, options);\n }\n\n downloadUrl(): string {\n return this.#client.downloadUrl(this.id);\n }\n\n query(sql: string, signal?: AbortSignal): Promise<QueryResult> {\n return this.#client.query(this.id, sql, signal);\n }\n\n publish(signal?: AbortSignal): Promise<PublishResult> {\n return this.#client.publish(this.id, signal);\n }\n\n shareLink(options?: ShareLinkOptions): Promise<ShareLinkResult> {\n return this.#client.shareLink(this.id, options);\n }\n\n prompt(\n query: string,\n options?: GenerateOptions & { schema?: undefined },\n ): Promise<GenerateResult>;\n prompt<T>(\n query: string,\n options: GenerateOptions & { schema: StructuredSchema<T> },\n ): Promise<GenerateResult<T>>;\n prompt<T = undefined>(\n query: string,\n options?: GenerateOptions,\n ): Promise<GenerateResult<T>> {\n const model = options?.model ?? this.#model;\n const merged = model ? { ...options, model } : options;\n if (merged?.schema !== undefined) {\n return this.#client.generate(\n this.id,\n query,\n merged as GenerateOptions & { schema: StructuredSchema<unknown> },\n ) as Promise<GenerateResult<T>>;\n }\n return this.#client.generate(\n this.id,\n query,\n merged as GenerateOptions & { schema?: undefined },\n ) as Promise<GenerateResult<T>>;\n }\n\n stream(\n query: string,\n options?: CompletionOptions,\n ): AsyncGenerator<CompletionEvent> {\n const model = options?.model ?? this.#model;\n const merged = model ? { ...options, model } : options;\n return this.#client.stream(this.id, query, merged);\n }\n}\n\n// ─── Client ──────────────────────────────────────────────────────────────────\n\nexport class OkraClient {\n private readonly baseUrl: string;\n private readonly apiKey?: string;\n private readonly sharedSecret?: string;\n private readonly fetchImpl: typeof globalThis.fetch;\n readonly sessions: {\n create: (sourceOrDocId: UploadInput, options?: SessionCreateOptions) => Promise<OkraSession>;\n from: (documentId: string, options?: SessionAttachOptions) => OkraSession;\n };\n constructor(options: OkraClientOptions) {\n this.baseUrl = normalizeBaseUrl(options.baseUrl || DEFAULT_BASE_URL);\n this.apiKey = options.apiKey;\n this.sharedSecret = options.sharedSecret;\n this.fetchImpl = options.fetch || globalThis.fetch.bind(globalThis);\n\n if (!this.apiKey && !this.sharedSecret) {\n throw new OkraRuntimeError(\n 'UNAUTHORIZED',\n 'OkraClient requires either apiKey or sharedSecret',\n 401,\n );\n }\n\n if (\n typeof globalThis !== 'undefined' && 'window' in globalThis\n && this.apiKey\n && !this.apiKey.startsWith('okra_pk_')\n ) {\n console.warn(\n '[OkraPDF] Secret API key detected in browser. Use a publishable key (okra_pk_...) for client-side usage. ' +\n 'See https://docs.okrapdf.dev/api-keys#publishable-keys',\n );\n }\n\n this.sessions = {\n create: async (sourceOrDocId, sessionOptions = {}) => {\n let documentId: string;\n if (typeof sourceOrDocId === 'string' && isDocumentId(sourceOrDocId.trim())) {\n documentId = sourceOrDocId.trim();\n } else {\n const session = await this.upload(sourceOrDocId, sessionOptions.upload);\n documentId = session.id;\n }\n\n const session = this.sessions.from(documentId, { model: sessionOptions.model });\n if (sessionOptions.wait ?? true) {\n await session.wait(sessionOptions.waitOptions);\n }\n return session;\n },\n from: (documentId, sessionOptions = {}) => {\n const normalized = documentId.trim();\n if (!normalized) {\n throw new OkraRuntimeError(\n 'INVALID_REQUEST',\n 'sessions.from requires a non-empty documentId',\n 400,\n );\n }\n\n return new OkraSessionHandle(\n this,\n normalized,\n sessionOptions.model?.trim() || undefined,\n );\n },\n };\n }\n\n // ─── Upload ──────────────────────────────────────────────────────────────\n\n async upload(input: UploadInput, options: UploadOptions = {}): Promise<OkraSession> {\n const documentId = options.documentId || makeDocId();\n const path = `/document/${encodeURIComponent(documentId)}`;\n const visibility = options.visibility || 'private';\n\n if (typeof input === 'string' && isHttpUrl(input)) {\n const urlHeaders: Record<string, string> = { 'Content-Type': 'application/json' };\n if (options.vendorKeys) {\n urlHeaders['X-Vendor-Keys'] = JSON.stringify(options.vendorKeys);\n }\n await this.requestJson<{ phase?: string }>(`${path}/upload-url`, {\n method: 'POST',\n headers: urlHeaders,\n body: JSON.stringify({\n url: input,\n capabilities: options.capabilities,\n visibility,\n redact: options.redact,\n }),\n });\n return this.sessions.from(documentId);\n }\n\n let bytes: Uint8Array;\n let fileName = options.fileName || 'document.pdf';\n if (typeof input === 'string') {\n const local = await readLocalFileFromNode(input);\n bytes = local.bytes;\n if (!options.fileName) fileName = local.fileName;\n } else if (isBlobLike(input)) {\n bytes = toUint8Array(await input.arrayBuffer());\n if (!options.fileName) {\n fileName = inferBlobName(input, fileName);\n }\n } else {\n bytes = toUint8Array(input);\n }\n\n const headers: Record<string, string> = {\n 'Content-Type': 'application/pdf',\n 'X-File-Name': fileName,\n };\n if (options.capabilities) {\n headers['X-Capabilities'] = JSON.stringify(options.capabilities);\n }\n if (options.vendorKeys) {\n headers['X-Vendor-Keys'] = JSON.stringify(options.vendorKeys);\n }\n if (options.redact) {\n headers['X-Redact'] = JSON.stringify(options.redact);\n }\n if (visibility === 'public') {\n headers['X-Visibility'] = 'public';\n }\n\n await this.requestJson<{ phase?: string }>(`${path}/upload`, {\n method: 'POST',\n headers,\n body: bytes as unknown as BodyInit,\n });\n\n return this.sessions.from(documentId);\n }\n\n // ─── Status / Wait ───────────────────────────────────────────────────────\n\n async status(documentId: string, signal?: AbortSignal): Promise<DocumentStatus> {\n return this.requestJson<DocumentStatus>(\n `/document/${encodeURIComponent(documentId)}/status`,\n { method: 'GET', signal },\n );\n }\n\n async wait(documentId: string, options: WaitOptions = {}): Promise<DocumentStatus> {\n const startedAt = Date.now();\n const timeoutMs = options.timeoutMs ?? DEFAULT_WAIT_TIMEOUT_MS;\n const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_WAIT_POLL_MS;\n\n while (true) {\n if (options.signal?.aborted) {\n throw new OkraRuntimeError('TIMEOUT', 'Wait aborted', 499);\n }\n\n const current = await this.status(documentId, options.signal);\n if (COMPLETE_PHASES.has(current.phase)) {\n return current;\n }\n if (TERMINAL_ERROR_PHASES.has(current.phase)) {\n throw new OkraRuntimeError(\n 'EXTRACTION_FAILED',\n `Document entered terminal error phase (${current.phase})`,\n 500,\n current,\n );\n }\n\n const elapsed = Date.now() - startedAt;\n if (elapsed >= timeoutMs) {\n throw new OkraRuntimeError(\n 'TIMEOUT',\n `Timed out waiting for document ${documentId} after ${timeoutMs}ms`,\n 504,\n current,\n );\n }\n\n await sleep(pollIntervalMs);\n }\n }\n\n // ─── Pages ───────────────────────────────────────────────────────────────\n\n async pages(documentId: string, options?: { range?: string; signal?: AbortSignal }): Promise<Page[]> {\n const params = options?.range ? `?range=${encodeURIComponent(options.range)}` : '';\n return this.requestJson<Page[]>(\n `/document/${encodeURIComponent(documentId)}/pages${params}`,\n { method: 'GET', signal: options?.signal },\n );\n }\n\n async page(documentId: string, pageNumber: number, signal?: AbortSignal): Promise<Page> {\n return this.requestJson<Page>(\n `/document/${encodeURIComponent(documentId)}/page/${pageNumber}`,\n { method: 'GET', signal },\n );\n }\n\n // ─── Download ──────────────────────────────────────────────────────────\n\n downloadUrl(documentId: string): string {\n return `${this.baseUrl}/document/${encodeURIComponent(documentId)}/download`;\n }\n\n // ─── Entities ────────────────────────────────────────────────────────────\n\n async entities(\n documentId: string,\n options?: { type?: string; limit?: number; offset?: number; signal?: AbortSignal },\n ): Promise<EntitiesResponse> {\n const params = new URLSearchParams();\n if (options?.type) params.set('type', options.type);\n if (options?.limit) params.set('limit', String(options.limit));\n if (options?.offset) params.set('offset', String(options.offset));\n const qs = params.toString();\n return this.requestJson<EntitiesResponse>(\n `/document/${encodeURIComponent(documentId)}/nodes${qs ? `?${qs}` : ''}`,\n { method: 'GET', signal: options?.signal },\n );\n }\n\n // ─── Query (SQL) ─────────────────────────────────────────────────────────\n\n async query(documentId: string, sql: string, signal?: AbortSignal): Promise<QueryResult> {\n return this.requestJson<QueryResult>(\n `/document/${encodeURIComponent(documentId)}/query?select=${encodeURIComponent(sql)}`,\n { method: 'GET', signal },\n );\n }\n\n // ─── Stream (streaming completion via OpenAI SSE) ────────────────────────\n\n async *stream(\n documentId: string,\n query: string,\n options?: CompletionOptions,\n ): AsyncGenerator<CompletionEvent> {\n const response = await this.rawRequest(\n `/document/${encodeURIComponent(documentId)}/chat/completions`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n messages: [{ role: 'user', content: query }],\n stream: options?.stream !== false,\n ...(options?.model ? { model: options.model } : {}),\n }),\n signal: options?.signal,\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new OkraRuntimeError('HTTP_ERROR', `Completion failed: ${text}`, response.status);\n }\n\n if (!response.body) {\n throw new OkraRuntimeError('INVALID_RESPONSE', 'No response body for completion stream', 500);\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n let fullText = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed || trimmed === 'data: [DONE]') continue;\n if (!trimmed.startsWith('data: ')) continue;\n const json = trimmed.slice(6);\n try {\n const chunk = JSON.parse(json) as {\n choices?: Array<{ delta?: { content?: string }; finish_reason?: string | null }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };\n error?: { message?: string };\n };\n\n if (chunk.error) {\n yield { type: 'error', message: chunk.error.message || 'Stream error' };\n continue;\n }\n\n const delta = chunk.choices?.[0]?.delta?.content;\n if (delta) {\n fullText += delta;\n yield { type: 'text_delta', text: delta };\n }\n\n if (chunk.choices?.[0]?.finish_reason === 'stop') {\n yield { type: 'done', answer: fullText };\n }\n } catch {\n // skip malformed lines\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n // ─── Generate (non-streaming AI) ─────────────────────────────────────────\n\n async generate(\n documentId: string,\n query: string,\n options?: GenerateOptions & { schema?: undefined },\n ): Promise<GenerateResult>;\n async generate<T>(\n documentId: string,\n query: string,\n options: GenerateOptions & { schema: StructuredSchema<T> },\n ): Promise<GenerateResult<T>>;\n async generate<T = undefined>(\n documentId: string,\n query: string,\n options?: GenerateOptions,\n ): Promise<GenerateResult<T>> {\n if (options?.schema) {\n return this.generateStructured<T>(documentId, query, options);\n }\n\n const result = await this.requestJson<{\n id: string;\n choices: Array<{ message: { content: string } }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };\n }>(\n `/document/${encodeURIComponent(documentId)}/chat/completions`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n messages: [{ role: 'user', content: query }],\n ...(options?.model ? { model: options.model } : {}),\n }),\n signal: options?.signal,\n },\n );\n\n return {\n answer: result.choices?.[0]?.message?.content || '',\n };\n }\n\n private async generateStructured<T>(\n documentId: string,\n query: string,\n options: GenerateOptions,\n ): Promise<GenerateResult<T>> {\n if (!query || query.trim() === '') {\n throw new OkraRuntimeError('INVALID_REQUEST', 'generate with schema requires a non-empty query', 400);\n }\n\n const normalized = normalizeSchema(options.schema as StructuredSchema<T>);\n const result = await this.requestJson<{\n id: string;\n choices: Array<{ message: { content: string } }>;\n usage?: { prompt_tokens?: number; completion_tokens?: number; total_tokens?: number };\n }>(\n `/document/${encodeURIComponent(documentId)}/chat/completions`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n messages: [{ role: 'user', content: query }],\n response_format: {\n type: 'json_schema',\n json_schema: { name: 'result', schema: normalized.jsonSchema },\n },\n ...(options.model ? { model: options.model } : {}),\n }),\n signal: options.signal,\n },\n );\n\n const raw = result.choices?.[0]?.message?.content;\n if (!raw) {\n throw new OkraRuntimeError('INVALID_RESPONSE', 'No content in structured output response', 500);\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new OkraRuntimeError('INVALID_RESPONSE', 'Structured output response is not valid JSON', 500);\n }\n\n let data: T;\n if (normalized.parser) {\n const zodResult = normalized.parser.safeParse(parsed);\n if (!zodResult.success) {\n throw new StructuredOutputError(\n 'SCHEMA_VALIDATION_FAILED',\n 'Client-side schema validation failed for structured output response',\n 422,\n zodResult.error.issues,\n );\n }\n data = zodResult.data;\n } else {\n data = parsed as T;\n }\n\n return {\n answer: '',\n data,\n };\n }\n\n // ─── Model Endpoint ──────────────────────────────────────────────────────\n\n modelEndpoint(documentId: string): string {\n return `${this.baseUrl}/v1/documents/${encodeURIComponent(documentId)}`;\n }\n\n // ─── Publish / Share ────────────────────────────────────────────────────\n\n async publish(documentId: string, signal?: AbortSignal): Promise<PublishResult> {\n const result = await this.requestJson<Omit<PublishResult, 'url'>>(\n `/document/${encodeURIComponent(documentId)}/publish`,\n { method: 'POST', signal },\n );\n return {\n ...result,\n url: `${this.baseUrl}/v1/documents/${encodeURIComponent(documentId)}`,\n };\n }\n\n async shareLink(documentId: string, options?: ShareLinkOptions): Promise<ShareLinkResult> {\n return this.requestJson<ShareLinkResult>(\n `/document/${encodeURIComponent(documentId)}/share-link`,\n {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n role: options?.role,\n label: options?.label,\n expiresInMs: options?.expiresInMs,\n maxViews: options?.maxViews,\n }),\n signal: options?.signal,\n },\n );\n }\n\n // ─── Public HTTP ─────────────────────────────────────────────────────────\n\n async request<T>(path: string, init: RequestInit = {}): Promise<T> {\n return this.requestJson<T>(path, init);\n }\n\n get url(): string {\n return this.baseUrl;\n }\n\n // ─── Internal HTTP ───────────────────────────────────────────────────────\n\n private authHeaders(): Record<string, string> {\n if (this.apiKey) return { Authorization: `Bearer ${this.apiKey}` };\n if (this.sharedSecret) return { 'x-document-agent-secret': this.sharedSecret };\n return {};\n }\n\n private async rawRequest(path: string, init: RequestInit): Promise<Response> {\n const headers = new Headers(init.headers);\n for (const [key, value] of Object.entries(this.authHeaders())) {\n if (!headers.has(key)) headers.set(key, value);\n }\n\n try {\n return await this.fetchImpl(`${this.baseUrl}${path}`, { ...init, headers });\n } catch (err) {\n throw new OkraRuntimeError(\n 'HTTP_ERROR',\n err instanceof Error ? err.message : String(err),\n 502,\n );\n }\n }\n\n private async requestJson<T>(path: string, init: RequestInit): Promise<T> {\n const response = await this.rawRequest(path, init);\n const text = await response.text();\n const parsed = this.parseBody(text);\n\n if (!response.ok) {\n const envelope = parsed as StructuredErrorEnvelope | null;\n const code = envelope?.code;\n const message = envelope?.message || envelope?.error || `Request failed with status ${response.status}`;\n const details = envelope?.details ?? parsed ?? text;\n if (isStructuredCode(code)) {\n throw new StructuredOutputError(code, message, response.status, details);\n }\n const runtimeCode: RuntimeErrorCode = response.status === 401 ? 'UNAUTHORIZED' : 'HTTP_ERROR';\n throw new OkraRuntimeError(runtimeCode, message, response.status, details);\n }\n\n if (parsed === null) {\n throw new OkraRuntimeError(\n 'INVALID_RESPONSE',\n `Expected JSON response for ${path}`,\n response.status,\n text,\n );\n }\n\n return parsed as T;\n }\n\n private parseBody(text: string): unknown | null {\n const trimmed = text.trim();\n if (!trimmed) return null;\n try {\n return JSON.parse(trimmed);\n } catch {\n return null;\n }\n }\n}\n"],"mappings":"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","names":["session"]}
@@ -24,12 +24,15 @@ function doc(documentId, baseUrlOrOptions = DEFAULT_BASE_URL, maybeOptions = {})
24
24
  const options = typeof baseUrlOrOptions === "string" ? maybeOptions : baseUrlOrOptions;
25
25
  const base = baseUrl.replace(/\/+$/, "");
26
26
  const defaultProvider = options.provider;
27
- const defaultImage = options.defaultImage;
27
+ const defaultImage = options.placeholder || options.defaultImage;
28
28
  const providerSegment = (p) => {
29
29
  const provider = p || defaultProvider;
30
30
  return provider ? `/t_${provider}` : "";
31
31
  };
32
- const defaultImageSegment = () => defaultImage ? `/d_${defaultImage}` : "";
32
+ const defaultImageSegment = (override) => {
33
+ const img = override || defaultImage;
34
+ return img ? `/d_${img}` : "";
35
+ };
33
36
  const docBase = `${base}/v1/documents/${encodeURIComponent(documentId)}`;
34
37
  const artifactBase = options.fileName ? slugifyFileStem(options.fileName) : "document";
35
38
  const withArtifact = (path, ext, qs = "") => `${path}/${artifactBase}.${ext}${qs}`;
@@ -78,15 +81,34 @@ function doc(documentId, baseUrlOrOptions = DEFAULT_BASE_URL, maybeOptions = {})
78
81
  }
79
82
  });
80
83
  };
84
+ const pgUrl = (segment, ext, placeholderOverride) => {
85
+ const imgSeg = defaultImageSegment(placeholderOverride);
86
+ const provSeg = providerSegment();
87
+ const seg = provSeg + imgSeg;
88
+ const flatPath = `${docBase}/pg_${segment}.${ext}`;
89
+ if (!seg) return flatPath;
90
+ return flatPath.replace(docBase, `${docBase}${seg}`);
91
+ };
92
+ const makePgPage = (pageNum) => ({
93
+ png: (opts) => pgUrl(String(pageNum), "png", opts?.placeholder),
94
+ md: () => pgUrl(String(pageNum), "md"),
95
+ json: () => pgUrl(String(pageNum), "json")
96
+ });
97
+ const makePgRange = (segment) => ({
98
+ md: () => pgUrl(segment, "md"),
99
+ json: () => pgUrl(segment, "json")
100
+ });
81
101
  const pg = new Proxy({}, {
82
102
  get(_target, prop) {
103
+ if (prop === "range") {
104
+ return (start, end) => makePgRange(`${start}-${end}`);
105
+ }
106
+ if (prop === "list") {
107
+ return (...pages) => makePgRange(pages.join(","));
108
+ }
83
109
  const pageNum = typeof prop === "string" ? parseInt(prop, 10) : NaN;
84
110
  if (!isNaN(pageNum)) {
85
- return {
86
- png: () => withProvider(withArtifact(`${docBase}/pg_${pageNum}`, "png")),
87
- md: () => withProvider(withArtifact(`${docBase}/pg_${pageNum}`, "md")),
88
- json: () => withProvider(withArtifact(`${docBase}/pg_${pageNum}`, "json"))
89
- };
111
+ return makePgPage(pageNum);
90
112
  }
91
113
  return void 0;
92
114
  }
@@ -97,8 +119,12 @@ function doc(documentId, baseUrlOrOptions = DEFAULT_BASE_URL, maybeOptions = {})
97
119
  opts?.provider
98
120
  ),
99
121
  thumbnail: {
100
- url: () => withProvider(withArtifact(`${docBase}/pg_1`, "png"))
122
+ url: () => pgUrl("1", "png")
123
+ },
124
+ full: {
125
+ md: () => `${docBase}/full.md`
101
126
  },
127
+ download: () => `${base}/document/${encodeURIComponent(documentId)}/download`,
102
128
  pg,
103
129
  entities: {
104
130
  tables: makeEntityCollection("tables"),
@@ -110,4 +136,4 @@ function doc(documentId, baseUrlOrOptions = DEFAULT_BASE_URL, maybeOptions = {})
110
136
  export {
111
137
  doc
112
138
  };
113
- //# sourceMappingURL=chunk-C6ZT7DKX.js.map
139
+ //# sourceMappingURL=chunk-5NINKIAC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/url.ts"],"sourcesContent":["import type { DocUrlOptions, UrlBuilderOptions } from './types';\n\nconst DEFAULT_BASE_URL = 'https://api.okrapdf.com';\n\ninterface PgPage {\n png: (opts?: { placeholder?: string }) => string;\n md: () => string;\n json: () => string;\n}\n\ninterface PgPageRange {\n md: () => string;\n json: () => string;\n}\n\ninterface PgProxy {\n [index: number]: PgPage;\n range: (start: number, end: number) => PgPageRange;\n list: (...pages: number[]) => PgPageRange;\n}\n\ninterface DocumentUrl {\n /** Base document URL */\n url: (opts?: UrlBuilderOptions) => string;\n /** Thumbnail image URL (pg_1.png) */\n thumbnail: { url: () => string };\n /** Full document markdown */\n full: { md: () => string };\n /** Original PDF download (auth required) */\n download: () => string;\n /** Page access: d.pg[1].png(), d.pg[1].md(), d.pg[1].json() */\n pg: PgProxy;\n /** Entity-level access */\n entities: EntitiesProxy;\n}\n\ninterface EntitiesProxy {\n tables: EntityCollectionProxy;\n figures: EntityCollectionProxy;\n}\n\ninterface EntityCollectionProxy {\n [index: number]: {\n url: (opts?: { format?: 'json' | 'csv' | 'html' }) => string;\n };\n url: (opts?: UrlBuilderOptions) => string;\n}\n\nconst FORMAT_TO_EXT: Record<NonNullable<UrlBuilderOptions['format']>, string> = {\n json: 'json',\n csv: 'csv',\n html: 'html',\n markdown: 'md',\n png: 'png',\n};\n\nfunction slugifyFileStem(fileName: string): string {\n const leaf = fileName.split('/').pop() || fileName;\n const noExt = leaf.replace(/\\.[A-Za-z0-9]{1,8}$/, '');\n const slug = noExt\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-+|-+$/g, '')\n .slice(0, 48);\n return slug || 'document';\n}\n\nfunction extensionFor(format: string | undefined, fallback: string): string {\n if (!format) return fallback;\n const lower = format.toLowerCase();\n if (lower === 'markdown') return 'md';\n return FORMAT_TO_EXT[lower as keyof typeof FORMAT_TO_EXT] || lower;\n}\n\n/**\n * Build-time URL builder — Cloudinary for documents.\n *\n * ```tsx\n * import { doc } from 'okrapdf';\n * const d = doc('doc_7fK3x');\n * <Image src={d.thumbnail.url()} />\n * <a href={d.entities.tables[0].url({ format: 'csv' })}>CSV</a>\n * ```\n */\nexport function doc(\n documentId: string,\n baseUrlOrOptions: string | DocUrlOptions = DEFAULT_BASE_URL,\n maybeOptions: DocUrlOptions = {},\n): DocumentUrl {\n const baseUrl = typeof baseUrlOrOptions === 'string' ? baseUrlOrOptions : DEFAULT_BASE_URL;\n const options = typeof baseUrlOrOptions === 'string' ? maybeOptions : baseUrlOrOptions;\n\n const base = baseUrl.replace(/\\/+$/, '');\n const defaultProvider = options.provider;\n const defaultImage = options.placeholder || options.defaultImage;\n const providerSegment = (p?: string) => {\n const provider = p || defaultProvider;\n return provider ? `/t_${provider}` : '';\n };\n const defaultImageSegment = (override?: string) => {\n const img = override || defaultImage;\n return img ? `/d_${img}` : '';\n };\n const docBase = `${base}/v1/documents/${encodeURIComponent(documentId)}`;\n const artifactBase = options.fileName\n ? slugifyFileStem(options.fileName)\n : 'document';\n\n const withArtifact = (path: string, ext: string, qs: string = '') =>\n `${path}/${artifactBase}.${ext}${qs}`;\n\n const withProvider = (path: string, provider?: string) => {\n const seg = providerSegment(provider) + defaultImageSegment();\n if (!seg) return path;\n return path.replace(docBase, `${docBase}${seg}`);\n };\n\n const formatParams = (opts?: UrlBuilderOptions) => {\n const params = new URLSearchParams();\n if (opts?.format) params.set('format', opts.format);\n if (opts?.include?.length) params.set('include', opts.include.join(','));\n const qs = params.toString();\n return qs ? `?${qs}` : '';\n };\n\n const makeEntityCollection = (type: string): EntityCollectionProxy => {\n return new Proxy({} as EntityCollectionProxy, {\n get(_target, prop) {\n if (prop === 'url') {\n return (opts?: UrlBuilderOptions) =>\n withProvider(\n withArtifact(\n `${docBase}/entities/${type}`,\n extensionFor(opts?.format, 'json'),\n formatParams(opts),\n ),\n opts?.provider,\n );\n }\n const index = typeof prop === 'string' ? parseInt(prop, 10) : NaN;\n if (!isNaN(index)) {\n return {\n url: (opts?: { format?: string; provider?: string }) => {\n const params = opts?.format ? `?format=${opts.format}` : '';\n return withProvider(\n withArtifact(\n `${docBase}/entities/${type}/${index}`,\n extensionFor(opts?.format, 'json'),\n params,\n ),\n opts?.provider,\n );\n },\n };\n }\n return undefined;\n },\n });\n };\n\n // Build a flat pg URL: .../pg_1.png (no artifact suffix)\n const pgUrl = (segment: string, ext: string, placeholderOverride?: string) => {\n const imgSeg = defaultImageSegment(placeholderOverride);\n const provSeg = providerSegment();\n const seg = provSeg + imgSeg;\n const flatPath = `${docBase}/pg_${segment}.${ext}`;\n if (!seg) return flatPath;\n return flatPath.replace(docBase, `${docBase}${seg}`);\n };\n\n const makePgPage = (pageNum: number): PgPage => ({\n png: (opts?: { placeholder?: string }) => pgUrl(String(pageNum), 'png', opts?.placeholder),\n md: () => pgUrl(String(pageNum), 'md'),\n json: () => pgUrl(String(pageNum), 'json'),\n });\n\n const makePgRange = (segment: string): PgPageRange => ({\n md: () => pgUrl(segment, 'md'),\n json: () => pgUrl(segment, 'json'),\n });\n\n const pg: PgProxy = new Proxy({} as PgProxy, {\n get(_target, prop) {\n if (prop === 'range') {\n return (start: number, end: number) => makePgRange(`${start}-${end}`);\n }\n if (prop === 'list') {\n return (...pages: number[]) => makePgRange(pages.join(','));\n }\n const pageNum = typeof prop === 'string' ? parseInt(prop, 10) : NaN;\n if (!isNaN(pageNum)) {\n return makePgPage(pageNum);\n }\n return undefined;\n },\n });\n\n return {\n url: (opts?: UrlBuilderOptions) =>\n withProvider(\n withArtifact(docBase, extensionFor(opts?.format, 'json'), formatParams(opts)),\n opts?.provider,\n ),\n thumbnail: {\n url: () => pgUrl('1', 'png'),\n },\n full: {\n md: () => `${docBase}/full.md`,\n },\n download: () => `${base}/document/${encodeURIComponent(documentId)}/download`,\n pg,\n entities: {\n tables: makeEntityCollection('tables'),\n figures: makeEntityCollection('figures'),\n },\n };\n}\n"],"mappings":"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","names":[]}
@@ -1,3 +1,7 @@
1
+ import {
2
+ OkraRuntimeError
3
+ } from "./chunk-NIZM2ETT.js";
4
+
1
5
  // src/cli/commands/tree.ts
2
6
  async function tree(client, jobId, options = {}) {
3
7
  const treeData = await client.request(`/document/${jobId}/verification-tree`);
@@ -690,7 +694,7 @@ function getBaseUrl() {
690
694
  if (globalConfig?.baseUrl) {
691
695
  return globalConfig.baseUrl;
692
696
  }
693
- return "https://app.okrapdf.com";
697
+ return "https://api.okrapdf.com";
694
698
  }
695
699
  function getApiKeySource() {
696
700
  if (process.env.OKRA_API_KEY) {
@@ -778,6 +782,167 @@ async function authLogout() {
778
782
  console.log("Note: Environment variables and project configs are not affected");
779
783
  }
780
784
 
785
+ // src/cli/output.ts
786
+ import { writeFileSync as writeFileSync2 } from "fs";
787
+ function writeOutput(data, outputPath) {
788
+ if (outputPath) {
789
+ writeFileSync2(outputPath, data);
790
+ process.stderr.write(`Wrote \u2192 ${outputPath}
791
+ `);
792
+ } else {
793
+ process.stdout.write(data + "\n");
794
+ }
795
+ }
796
+ function progress(msg, quiet) {
797
+ if (!quiet) process.stderr.write(msg + "\n");
798
+ }
799
+ function csvEscape(value) {
800
+ const str = String(value ?? "");
801
+ if (str.includes(",") || str.includes('"') || str.includes("\n")) {
802
+ return `"${str.replace(/"/g, '""')}"`;
803
+ }
804
+ return str;
805
+ }
806
+ function handleError(error, json) {
807
+ const msg = error instanceof Error ? error.message : String(error);
808
+ const status = error instanceof OkraRuntimeError ? error.status : 1;
809
+ if (json) {
810
+ process.stderr.write(JSON.stringify({ error: msg, code: status }) + "\n");
811
+ } else {
812
+ process.stderr.write(`Error: ${msg}
813
+ `);
814
+ }
815
+ process.exit(status >= 500 ? 2 : 1);
816
+ }
817
+
818
+ // src/cli/commands/upload.ts
819
+ async function upload(client, source, opts) {
820
+ progress(`Uploading ${source}\u2026`, opts.quiet);
821
+ const session = await client.upload(source);
822
+ const docId = session.id;
823
+ progress(`Document ID: ${docId}`, opts.quiet);
824
+ if (opts.noWait) {
825
+ return { id: docId, phase: "uploading" };
826
+ }
827
+ progress("Waiting for processing\u2026", opts.quiet);
828
+ const status = await client.wait(docId, {
829
+ pollIntervalMs: 2e3
830
+ });
831
+ const base = `https://api.okrapdf.com/v1/documents/${docId}`;
832
+ const urls = {
833
+ full_md: `${base}/full.md`,
834
+ page_png: `${base}/d_shimmer/pg_{N}.png`,
835
+ page_md: `${base}/pg_{N}.md`,
836
+ completion: `https://api.okrapdf.com/document/${docId}/completion`,
837
+ original: `${base}/original.pdf`
838
+ };
839
+ progress(
840
+ `Done \u2014 phase: ${status.phase}, pages: ${status.pagesTotal ?? "?"}`,
841
+ opts.quiet
842
+ );
843
+ return {
844
+ id: docId,
845
+ phase: status.phase,
846
+ pages: status.pagesTotal,
847
+ urls
848
+ };
849
+ }
850
+
851
+ // src/cli/commands/collection.ts
852
+ async function collectionList(client, _opts) {
853
+ const res = await client.request(
854
+ "/v1/collections",
855
+ { method: "GET" }
856
+ );
857
+ return res.collections;
858
+ }
859
+ function formatCollectionList(rows, json) {
860
+ if (json) return JSON.stringify(rows);
861
+ if (rows.length === 0) return "No collections found.";
862
+ const header = "ID NAME DOCS";
863
+ const lines = rows.map(
864
+ (r) => `${r.id} ${r.name} ${r.document_count}`
865
+ );
866
+ return [header, ...lines].join("\n");
867
+ }
868
+ async function collectionQueryRaw(baseUrl, apiKey, nameOrId, question, opts) {
869
+ progress(`Querying collection "${nameOrId}"\u2026`, opts.quiet);
870
+ const body = { prompt: question, stream: true };
871
+ if (opts.schema) {
872
+ const { readFileSync: readFileSync2 } = await import("fs");
873
+ body.schema = JSON.parse(readFileSync2(opts.schema, "utf8"));
874
+ }
875
+ const url = `${baseUrl.replace(/\/+$/, "")}/v1/collections/${encodeURIComponent(nameOrId)}/query`;
876
+ const response = await fetch(url, {
877
+ method: "POST",
878
+ headers: {
879
+ "Content-Type": "application/json",
880
+ Authorization: `Bearer ${apiKey}`
881
+ },
882
+ body: JSON.stringify(body)
883
+ });
884
+ if (!response.ok) {
885
+ const text2 = await response.text();
886
+ throw new Error(`Collection query failed (${response.status}): ${text2}`);
887
+ }
888
+ const text = await response.text();
889
+ const lines = text.trim().split("\n").filter(Boolean);
890
+ const events = lines.map((line) => JSON.parse(line));
891
+ const results = [];
892
+ let summary = { completed: 0, failed: 0, total_cost_usd: 0 };
893
+ for (const event of events) {
894
+ if (event.type === "result") {
895
+ results.push({
896
+ doc_id: event.doc_id,
897
+ status: event.status,
898
+ answer: event.answer ?? "",
899
+ cost_usd: event.usage?.cost_usd ?? 0,
900
+ duration_ms: event.duration_ms ?? 0,
901
+ error: event.error
902
+ });
903
+ progress(
904
+ ` ${event.status === "fulfilled" ? "+" : "!"} ${event.doc_id} (${event.duration_ms}ms)`,
905
+ opts.quiet
906
+ );
907
+ } else if (event.type === "done") {
908
+ summary = {
909
+ completed: event.completed,
910
+ failed: event.failed,
911
+ total_cost_usd: event.total_cost_usd
912
+ };
913
+ } else if (event.type === "start") {
914
+ progress(` ${event.doc_count} documents`, opts.quiet);
915
+ } else if (event.type === "error") {
916
+ throw new Error(event.error || "Collection query error");
917
+ }
918
+ }
919
+ return { results, summary };
920
+ }
921
+ function formatCollectionCsv(results) {
922
+ const header = "doc_id,status,answer,cost_usd,duration_ms";
923
+ const rows = results.map(
924
+ (r) => [
925
+ r.doc_id,
926
+ r.status,
927
+ csvEscape(r.answer),
928
+ r.cost_usd,
929
+ r.duration_ms
930
+ ].join(",")
931
+ );
932
+ return [header, ...rows].join("\n");
933
+ }
934
+ function formatCollectionTable(results) {
935
+ if (results.length === 0) return "No results.";
936
+ const header = "DOC_ID STATUS ANSWER COST DUR_MS";
937
+ const rows = results.map(
938
+ (r) => `${r.doc_id} ${r.status} ${r.answer.slice(0, 80)}${r.answer.length > 80 ? "\u2026" : ""} $${r.cost_usd.toFixed(4)} ${r.duration_ms}`
939
+ );
940
+ return [header, ...rows].join("\n");
941
+ }
942
+ function formatQueryJsonl(results) {
943
+ return results.map((r) => JSON.stringify(r)).join("\n");
944
+ }
945
+
781
946
  export {
782
947
  tree,
783
948
  formatTreeOutput,
@@ -812,6 +977,16 @@ export {
812
977
  getApiKeySource,
813
978
  authLogin,
814
979
  authStatus,
815
- authLogout
980
+ authLogout,
981
+ writeOutput,
982
+ progress,
983
+ handleError,
984
+ upload,
985
+ collectionList,
986
+ formatCollectionList,
987
+ collectionQueryRaw,
988
+ formatCollectionCsv,
989
+ formatCollectionTable,
990
+ formatQueryJsonl
816
991
  };
817
- //# sourceMappingURL=chunk-SBT5T6ZK.js.map
992
+ //# sourceMappingURL=chunk-DQ3ZXFXO.js.map