{"version":3,"file":"read-Dh_xzTLD.mjs","names":[],"sources":["../src/commands/read.ts"],"sourcesContent":["import { defineCommand } from \"citty\";\nimport { consola } from \"consola\";\nimport { sanitizeTerminalContent, sanitizeTerminalText } from \"../tui.ts\";\nimport { providerApiKeyEnvVar } from \"../core/providers.ts\";\nimport {\n  AuthError,\n  EmptyUrlError,\n  InvalidReadContinuationError,\n  ReadNotSupportedError,\n  StaleReadContinuationError,\n  UnknownProviderError,\n} from \"../core/errors.ts\";\n\nexport default defineCommand({\n  meta: {\n    name: \"read\",\n    description: \"Read one or more URLs using a provider\",\n  },\n  args: {\n    url: {\n      type: \"positional\",\n      description: \"URL to read; pass more URLs for a batch\",\n      required: true,\n    },\n    provider: {\n      type: \"string\",\n      description: \"Read provider to use; omit or pass auto for fallback\",\n    },\n    format: {\n      type: \"string\",\n      description: \"Response format: markdown, text, or html\",\n    },\n    \"max-tokens\": {\n      type: \"string\",\n      description: \"Maximum tokens to return when the provider supports it\",\n    },\n    \"max-chars\": {\n      type: \"string\",\n      description: \"Maximum page content characters to return\",\n    },\n    continuation: {\n      type: \"string\",\n      description: \"Opaque token returned by a truncated read\",\n    },\n    links: {\n      type: \"boolean\",\n      description: \"Keep the page's links in a bounded read\",\n    },\n    images: {\n      type: \"boolean\",\n      description: \"Keep the page's image URLs in a bounded read\",\n    },\n    json: {\n      type: \"boolean\",\n      description: \"Output as JSON\",\n      default: false,\n    },\n  },\n  async run({ args }) {\n    const read = await import(\"../core/read.ts\");\n    const batch = await import(\"../core/batch.ts\");\n    const parsed = parseReadArguments(args, batch.MAX_BATCH_ITEMS);\n    try {\n      if (parsed.urls.length === 1) {\n        const response = await read.readUrlDetailed(parsed.urls[0], parsed.options);\n        writeReadDetailedResult(response, args.json);\n        return;\n      }\n\n      const outcomes = await batch.readBatchDetailed(parsed.urls, parsed.options);\n      writeReadBatch(outcomes, args.json);\n      if (outcomes.some((outcome) => \"error\" in outcome)) process.exitCode = 1;\n    } catch (error) {\n      handleReadError(error, read.readProviderNames);\n    }\n  },\n});\n\ntype ReadCommandArgs = {\n  readonly _: readonly string[];\n  readonly url: string;\n  readonly provider?: string;\n  readonly format?: string;\n  readonly \"max-tokens\"?: string;\n  readonly \"max-chars\"?: string;\n  readonly continuation?: string;\n  readonly links?: boolean;\n  readonly images?: boolean;\n  readonly json: boolean;\n};\n\ntype ParsedReadArguments = {\n  readonly urls: readonly string[];\n  readonly options: {\n    readonly provider?: string;\n    readonly format?: \"markdown\" | \"text\" | \"html\";\n    readonly maxTokens?: number;\n    readonly maxChars?: number;\n    readonly continuation?: string;\n    readonly links?: boolean;\n    readonly images?: boolean;\n  };\n};\n\nfunction parseReadArguments(args: ReadCommandArgs, maxBatchItems: number): ParsedReadArguments {\n  const urls = args._.length > 0 ? args._ : [args.url];\n  if (urls.some((url) => !url.trim())) return exitWithError(\"Read URL cannot be empty.\");\n  if (urls.length > maxBatchItems) {\n    return exitWithError(`Cannot read more than ${maxBatchItems} URLs at once.`);\n  }\n  if (urls.length > 1 && args.continuation !== undefined) {\n    return exitWithError(\"--continuation is only supported for a single URL.\");\n  }\n  const format = parseFormat(args.format);\n  if (!format.ok) return exitWithError(format.message);\n  const maxTokens = parseOptionalPositiveInt(args[\"max-tokens\"], \"--max-tokens\");\n  if (!maxTokens.ok) return exitWithError(maxTokens.message);\n  const maxChars = parseOptionalPositiveInt(args[\"max-chars\"], \"--max-chars\");\n  if (!maxChars.ok) return exitWithError(maxChars.message);\n  return {\n    urls,\n    options: {\n      ...parseProviderOption(args.provider),\n      format: format.value,\n      maxTokens: maxTokens.value,\n      maxChars: maxChars.value,\n      continuation: args.continuation,\n      ...pageFieldOptions(args),\n    },\n  };\n}\n\nfunction pageFieldOptions(\n  args: ReadCommandArgs,\n): Pick<ParsedReadArguments[\"options\"], \"links\" | \"images\"> {\n  return {\n    ...(args.links ? { links: true } : {}),\n    ...(args.images ? { images: true } : {}),\n  };\n}\n\nfunction parseProviderOption(input: string | undefined): { readonly provider?: string } {\n  const provider = input?.trim();\n  return !provider || provider === \"auto\" ? {} : { provider };\n}\n\ntype ReadResultView = Readonly<\n  Pick<\n    import(\"../core/types.ts\").ReadResult,\n    \"url\" | \"title\" | \"description\" | \"content\" | \"truncated\" | \"continuation\"\n  >\n> & {\n  readonly links?: readonly string[];\n  readonly images?: readonly string[];\n};\n\ntype ReadDetailedResultView = {\n  readonly result: ReadResultView;\n  readonly requestedProvider: string;\n  readonly provider: string;\n  readonly attempts: readonly string[];\n};\n\nfunction writeReadDetailedResult(response: ReadDetailedResultView, json: boolean): void {\n  if (json) {\n    process.stdout.write(`${JSON.stringify(response, null, 2)}\\n`);\n    return;\n  }\n  consola.log(\n    `[provider=${sanitizeHeaderText(response.provider)} requested=${sanitizeHeaderText(response.requestedProvider)}] read ${sanitizeHeaderText(response.result.url)}`,\n  );\n  writeReadResult(response.result, false);\n}\n\nfunction writeReadResult(result: ReadResultView, json: boolean): void {\n  if (json) {\n    process.stdout.write(`${JSON.stringify(result, null, 2)}\\n`);\n    return;\n  }\n  if (result.title)\n    consola.log(`\\x1B[1m\\x1B[36m${sanitizeTerminalText(result.title, 2048)}\\x1B[0m`);\n  consola.log(`  ${sanitizeTerminalText(result.url, 2048)}`);\n  if (result.description) {\n    consola.log(`  \\x1B[90m${sanitizeTerminalText(result.description, 160)}\\x1B[0m`);\n  }\n  consola.log(\"\");\n  consola.log(sanitizeTerminalContent(result.content));\n  if (result.truncated && result.continuation) {\n    consola.log(\"\");\n    consola.log(`[truncated; continuation=${sanitizeHeaderText(result.continuation)}]`);\n  }\n  writeUrlList(\"Links\", result.links);\n  writeUrlList(\"Images\", result.images);\n}\n\nfunction writeUrlList(label: string, urls: readonly string[] | undefined): void {\n  if (urls === undefined || urls.length === 0) return;\n  consola.log(\"\");\n  consola.log(`${label} (${urls.length}):`);\n  for (const url of urls) consola.log(`  ${sanitizeTerminalText(url, 2048)}`);\n}\n\ntype ReadBatchItemView =\n  | { readonly url: string; readonly error: string }\n  | {\n      readonly url: string;\n      readonly requestedProvider: string;\n      readonly provider: string;\n      readonly attempts: readonly string[];\n      readonly result: ReadResultView;\n    };\n\nfunction writeReadBatch(outcomes: readonly ReadBatchItemView[], json: boolean): void {\n  if (json) {\n    process.stdout.write(`${JSON.stringify(outcomes, null, 2)}\\n`);\n    return;\n  }\n\n  for (const [index, outcome] of outcomes.entries()) {\n    if (\"error\" in outcome) {\n      consola.log(`[${index + 1}] ${sanitizeHeaderText(outcome.url)}`);\n      consola.error(`  ${sanitizeTerminalText(outcome.error)}`);\n    } else {\n      consola.log(\n        `[${index + 1}] [provider=${sanitizeHeaderText(outcome.provider)} requested=${sanitizeHeaderText(outcome.requestedProvider)}] ${sanitizeHeaderText(outcome.url)}`,\n      );\n      writeReadResult(outcome.result, false);\n    }\n    if (index < outcomes.length - 1) consola.log(\"\");\n  }\n}\n\nfunction handleReadError(error: unknown, readProviderNames: readonly string[]): never {\n  if (error instanceof EmptyUrlError) return exitWithError(\"Read URL cannot be empty.\");\n  if (error instanceof AuthError) {\n    const envVar = providerApiKeyEnvVar(error.provider);\n    if (envVar !== null) consola.info(`Set the ${envVar} environment variable.`);\n    return exitWithError(`Authentication failed for provider \"${error.provider}\".`);\n  }\n  if (error instanceof UnknownProviderError) {\n    consola.info(`Read-capable providers: ${readProviderNames.join(\", \")}`);\n    return exitWithError(`Unknown provider: ${error.provider}`);\n  }\n  if (\n    error instanceof ReadNotSupportedError ||\n    error instanceof InvalidReadContinuationError ||\n    error instanceof StaleReadContinuationError\n  ) {\n    return exitWithError(error.message);\n  }\n  throw error;\n}\n\nfunction exitWithError(message: string): never {\n  consola.error(message);\n  process.exit(1);\n}\n\ntype ParsedOptionalNumber =\n  | { ok: true; value: number | undefined }\n  | { ok: false; message: string };\n\ntype ParsedFormat =\n  | { ok: true; value: \"markdown\" | \"text\" | \"html\" | undefined }\n  | { ok: false; message: string };\n\nfunction parseOptionalPositiveInt(\n  input: string | undefined,\n  flagName: string,\n): ParsedOptionalNumber {\n  if (input === undefined || input === \"\") {\n    return { ok: true, value: undefined };\n  }\n  if (!/^\\d+$/.test(input)) {\n    return { ok: false, message: `Invalid ${flagName} value. Expected a positive integer.` };\n  }\n  const value = Number.parseInt(input, 10);\n  if (!Number.isSafeInteger(value) || value < 1) {\n    return { ok: false, message: `Invalid ${flagName} value. Expected a positive integer.` };\n  }\n  return { ok: true, value };\n}\n\nfunction parseFormat(input: string | undefined): ParsedFormat {\n  if (input === undefined || input === \"\") {\n    return { ok: true, value: undefined };\n  }\n  if (input === \"markdown\" || input === \"text\" || input === \"html\") {\n    return { ok: true, value: input };\n  }\n  return { ok: false, message: \"Invalid --format value. Expected markdown, text, or html.\" };\n}\n\nfunction sanitizeHeaderText(text: string): string {\n  return sanitizeTerminalText(text, 160);\n}\n"],"mappings":"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"}