From 5f8b3cfd86da5bf6d11101cda909bf217a85a3e3 Mon Sep 17 00:00:00 2001 From: fancivez Date: Mon, 27 Apr 2026 13:38:11 +0800 Subject: [PATCH] chore: upgrade biome noImplicitAnyLet to error, fix parsed type in schema Set noImplicitAnyLet to error in biome.json. Fix the violation in parseCardsJson by typing the parsed variable as unknown. Widen normalizeCardsPayload to accept unknown (it already handles non-object input defensively). Remove now-unnecessary cast in provider-parsers.ts. Change-Id: I57ee46d3179775d5d7c34f35803a44c45061b0ec --- biome.json | 2 +- main.js | 2 +- src/provider-parsers.ts | 3 +-- src/schema.ts | 7 ++++--- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/biome.json b/biome.json index ae01b13..c8b0dd9 100644 --- a/biome.json +++ b/biome.json @@ -10,7 +10,7 @@ "suspicious": { "noExplicitAny": "error", "noRedundantUseStrict": "off", - "noImplicitAnyLet": "off" + "noImplicitAnyLet": "error" }, "style": { "noNonNullAssertion": "warn", diff --git a/main.js b/main.js index bd79896..3a021d8 100644 --- a/main.js +++ b/main.js @@ -5,7 +5,7 @@ {excerpt}`,errorCustomHeadersJsonParse:"\u81EA\u5B9A\u4E49 headers JSON \u89E3\u6790\u5931\u8D25\uFF1A{error}",errorCustomHeadersJsonObject:"\u81EA\u5B9A\u4E49 headers JSON \u5FC5\u987B\u662F\u5BF9\u8C61",errorCustomHeadersLineFormat:"\u81EA\u5B9A\u4E49 headers \u6BCF\u884C\u683C\u5F0F\u5E94\u4E3A `Header-Name: value`",errorProviderNonJson:`{label} \u8FD4\u56DE\u975E JSON\uFF1A {excerpt}`,errorProviderRequestFailed:"{label} \u8BF7\u6C42\u5931\u8D25\uFF1A{error}",errorProviderApiStatus:"{label} API \u8FD4\u56DE HTTP {status}\uFF1A{excerpt}",noEditor:"\u627E\u4E0D\u5230\u6E90\u7B14\u8BB0\u5BF9\u5E94\u7684\u7F16\u8F91\u5668\u7A97\u53E3",settingUiLanguageName:"\u754C\u9762\u8BED\u8A00",settingUiLanguageDesc:"\u63A7\u5236\u63D2\u4EF6\u754C\u9762\u3001\u547D\u4EE4\u548C\u63D0\u793A\u6587\u6848\uFF1BAuto \u8DDF\u968F Obsidian/\u7CFB\u7EDF\u8BED\u8A00",settingBackendName:"Backend",settingBackendDesc:"\u751F\u6210 bullet \u7684\u540E\u7AEF\uFF1ACLI \u590D\u7528\u672C\u673A\u767B\u5F55\uFF1BAPI \u652F\u6301 OpenAI/Anthropic/Gemini \u53CA\u517C\u5BB9\u4EE3\u7406",settingCliPathName:"CLI \u8DEF\u5F84\uFF08\u53EF\u9009\uFF09",settingCliPathDesc:"\u7559\u7A7A\u5219\u81EA\u52A8\u63A2\u6D4B\u5E38\u89C1\u4F4D\u7F6E\uFF1BObsidian GUI \u542F\u52A8\u65F6\u4E0D\u7EE7\u627F shell PATH\uFF0C\u5FC5\u8981\u65F6\u586B\u7EDD\u5BF9\u8DEF\u5F84",settingCliPathPlaceholder:"\u4F8B\uFF1A/Users/you/bin/codex",settingCliTimeoutName:"CLI \u8D85\u65F6 (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"\u53C2\u8003 OpenClaw \u7684 provider/model \u601D\u8DEF\uFF1Apreset \u53EA\u8D1F\u8D23\u534F\u8BAE\u3001base URL \u548C\u8BA4\u8BC1\u9ED8\u8BA4\u503C",settingApiFormatName:"API format",settingApiFormatDesc:"\u4E0D\u540C provider \u7684 wire protocol\uFF1BOpenAI-compatible \u4EE3\u7406\u901A\u5E38\u9009 Chat Completions",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"\u586B provider \u6839\u5730\u5740\uFF0C\u4E0D\u8981\u9644\u52A0 /chat/completions\uFF1B\u7559\u7A7A\u65F6\u4F7F\u7528 preset \u9ED8\u8BA4\u503C",settingApiKeyName:"API Key",settingApiKeyDesc:"\u672C\u5730 Ollama/LM Studio \u53EF\u7559\u7A7A\uFF1B\u5176\u4ED6 provider \u901A\u5E38\u9700\u8981 key",settingApiKeyEnvName:"API Key \u73AF\u5883\u53D8\u91CF",settingApiKeyEnvDesc:"\u53EF\u9009\uFF1BObsidian GUI \u4E0D\u4E00\u5B9A\u7EE7\u627F shell \u73AF\u5883\uFF0C\u76F4\u63A5\u586B API Key \u66F4\u7A33\u5B9A",settingAuthTypeName:"\u8BA4\u8BC1\u65B9\u5F0F",settingAuthTypeDesc:"Auto \u4F7F\u7528 provider preset\uFF1B\u81EA\u5B9A\u4E49\u4EE3\u7406\u53EF\u6309\u9700\u8981\u6539\u6210 Bearer\u3001x-api-key \u6216 none",settingHeadersName:"\u989D\u5916 headers",settingHeadersDesc:"\u53EF\u9009\u3002\u652F\u6301 JSON \u5BF9\u8C61\u6216\u6BCF\u884C `Header: value`\uFF0C\u7528\u4E8E Cloudflare AI Gateway \u7B49\u4EE3\u7406",settingMaxTokensName:"\u6700\u5927\u8F93\u51FA tokens",settingModelName:"Model",settingModelDescApi:"API \u8C03\u7528\u7684\u6A21\u578B ID\uFF1B\u652F\u6301 OpenClaw \u98CE\u683C provider/model\uFF0C\u82E5 provider \u524D\u7F00\u5339\u914D\u5F53\u524D preset \u4F1A\u81EA\u52A8\u5265\u79BB",settingModelDescCli:"Claude Code \u4E0B\u4F1A\u4F20 --model\uFF1BCodex \u4E0B\u901A\u5E38\u5FFD\u7565\uFF08\u7528 Codex \u9ED8\u8BA4\u914D\u7F6E\uFF09",settingMaxInputName:"\u6700\u5927\u8F93\u5165\u5B57\u7B26\u6570",settingMaxInputDesc:"\u8D85\u8FC7\u8BE5\u957F\u5EA6\u4F1A\u622A\u65AD\u540E\u518D\u53D1\u9001\u7ED9\u6A21\u578B\uFF1B\u957F\u4E0A\u4E0B\u6587\u6A21\u578B\u53EF\u9002\u5F53\u8C03\u5927",promptHeader:"Prompt",settingPromptLanguageName:"\u8F93\u51FA\u8BED\u8A00",settingPromptLanguageDesc:"\u63A7\u5236 title/gist/bullets \u7684\u8BED\u8A00\uFF1Banchor \u59CB\u7EC8\u9010\u5B57\u590D\u5236\u539F\u6587",settingCardRangeName:"\u5361\u7247\u6570\u91CF\u8303\u56F4",settingCardRangeDesc:"\u6A21\u578B\u4F1A\u5728\u8FD9\u4E2A\u8303\u56F4\u5185\u81EA\u9002\u5E94\u5207\u6BB5",settingCustomPromptName:"\u81EA\u5B9A\u4E49 system prompt",settingCustomPromptDesc:"\u53EF\u9009\u3002\u652F\u6301\u53D8\u91CF\uFF1A{minCards}\u3001{maxCards}\u3001{languageInstruction}\u3001{schema}\u3001{example}",settingCustomPromptPlaceholder:"\u7559\u7A7A\u4F7F\u7528\u5185\u7F6E prompt",settingTestBackendName:"\u6D4B\u8BD5\u5F53\u524D\u540E\u7AEF",settingTestBackendDescApi:"\u4F1A\u53D1\u8D77\u4E00\u6B21\u6700\u5C0F LLM \u8BF7\u6C42\u9A8C\u8BC1 API \u8BBE\u7F6E",settingTestBackendDescCli:"\u8C03\u7528 ` --version` \u9A8C\u8BC1 spawn \u80FD\u627E\u5230\u4E8C\u8FDB\u5236",backendTestFailed:"\u2717 \u540E\u7AEF\u6D4B\u8BD5\u5931\u8D25\uFF1A{error}",settingExportFolderName:"\u5BFC\u51FA\u6587\u4EF6\u5939",settingExportFolderDesc:"\u5BF9\u7167\u7B14\u8BB0\u751F\u6210\u4F4D\u7F6E\uFF08\u76F8\u5BF9 Vault \u6839\uFF09",cacheHeader:"\u7F13\u5B58",settingMaxCacheName:"\u6700\u5927\u7F13\u5B58\u7BC7\u6570",settingMaxCacheDesc:"\u8D85\u8FC7\u4E0A\u9650\u540E\u6309\u6700\u8FD1\u8BBF\u95EE\u65F6\u95F4\u6DD8\u6C70\u6700\u65E7\u7684\u7B14\u8BB0\u7F13\u5B58\uFF1B\u7F13\u5B58\u4FDD\u5B58\u5728\u63D2\u4EF6\u76EE\u5F55\u7684 cache.json",cachePruned:"\u5DF2\u6DD8\u6C70 {count} \u6761\u65E7\u7F13\u5B58",cachedNotesName:"\u5DF2\u7F13\u5B58\u7B14\u8BB0\uFF1A{count} \u7BC7",cachedNotesDesc:"\u7F13\u5B58\u4EE5\u6E90\u7B14\u8BB0 SHA1 + \u751F\u6210\u914D\u7F6E\u6307\u7EB9\u4F5C\u4E3A\u5931\u6548\u952E\uFF0C\u6E90\u7B14\u8BB0\u6216\u6A21\u578B\u914D\u7F6E\u4FEE\u6539\u540E\u4F1A\u663E\u793A stale \u63D0\u793A",clearAllCacheButton:"\u6E05\u9664\u6240\u6709\u7F13\u5B58",settingStreamingName:"\u6D41\u5F0F\u8F93\u51FA",settingStreamingDesc:"\u542F\u7528\u540E\u751F\u6210\u65F6\u5B9E\u65F6\u663E\u793A LLM \u8F93\u51FA\u8FDB\u5EA6\uFF08\u4EC5 OpenAI Chat \u548C Anthropic \u683C\u5F0F\u652F\u6301\uFF09",settingStreamingTimeoutName:"\u6D41\u5F0F\u8D85\u65F6 (ms)",settingStreamingTimeoutDesc:"\u5355\u6B21\u6D41\u5F0F\u8BF7\u6C42\u7684\u8D85\u65F6\u65F6\u95F4\uFF1B\u4F4E\u4E8E 1000ms \u6216\u975E\u6CD5\u503C\u4F1A\u6062\u590D\u9ED8\u8BA4\u503C",cmdBatchGenerate:"\u6279\u91CF\u751F\u6210\u5BF9\u7167\u7B14\u8BB0\uFF08\u5F53\u524D\u6587\u4EF6\u5939\uFF09",batchSelectFolder:"\u8BF7\u8F93\u5165\u8981\u6279\u91CF\u5904\u7406\u7684\u6587\u4EF6\u5939\u8DEF\u5F84\uFF08\u7559\u7A7A\u4E3A Vault \u6839\u76EE\u5F55\uFF09",batchFolderPrompt:"\u6587\u4EF6\u5939\u8DEF\u5F84",batchAlreadyRunning:"\u6279\u91CF\u751F\u6210\u6B63\u5728\u8FD0\u884C",batchCancelRequested:"\u5DF2\u8BF7\u6C42\u53D6\u6D88\u6279\u91CF\u751F\u6210\uFF1B\u5F53\u524D\u6587\u4EF6\u7ED3\u675F\u540E\u505C\u6B62",batchInvalidFolderInput:"\u6587\u4EF6\u5939\u8DEF\u5F84\u65E0\u6548\uFF1A{path}",batchFolderNotFound:"\u6587\u4EF6\u5939\u4E0D\u5B58\u5728\uFF1A{path}",batchNoMarkdown:"\u8BE5\u6587\u4EF6\u5939\u4E0B\u6CA1\u6709 Markdown \u6587\u4EF6",batchProgress:"\u6279\u91CF\u751F\u6210\uFF1A{current}/{total}",batchDone:"\u6279\u91CF\u751F\u6210\u5B8C\u6210\uFF1A\u5171\u5904\u7406 {processed}/{total} \u7BC7\uFF0C\u8DF3\u8FC7\u7F13\u5B58 {skipped} \u7BC7\uFF0C\u5931\u8D25 {errors} \u7BC7",batchCancelled:"\u6279\u91CF\u751F\u6210\u5DF2\u53D6\u6D88\uFF1A\u5DF2\u5904\u7406 {processed}/{total} \u7BC7\uFF0C\u8DF3\u8FC7\u7F13\u5B58 {skipped} \u7BC7\uFF0C\u5931\u8D25 {errors} \u7BC7"},en:{appTitle:"Parallel Reader",settingsTitle:"Parallel Reader Settings",emptyOpenNote:"Open a note, then run:",emptyNoCache:"This note has no cached parallel notes. Run:",commandGenerate:"Parallel Reader: Generate notes for current note",displayName:"Parallel Reader",ribbonOpen:"Open Parallel Reader pane",cmdRun:"Generate parallel notes for current note (cache first)",cmdRegen:"Regenerate parallel notes (bypass cache)",cmdOpenView:"Open Parallel Reader pane",cmdExport:"Export current parallel notes to Vault",cmdCopyMarkdown:"Copy current parallel notes as Markdown",cmdCancel:"Cancel current parallel-note generation",cmdClearCurrent:"Clear cache for current note",cmdClearAll:"Clear all caches",cmdCardPrev:"Focus previous summary card",cmdCardNext:"Focus next summary card",cmdCardJump:"Jump current summary card to source",actionCancel:"Cancel generation",actionRegenerate:"Regenerate",actionCopyAll:"Copy all Markdown",actionExport:"Export to Vault",staleBanner:"The source note or generation settings changed. This is stale cache.",loadingDefault:"Generating parallel notes...",loadingGenerating:"Parallel Reader: asking the LLM to read and segment the full note...",loadingSubtitle:"You can keep reading. Cards will refresh when generation finishes.",errorTitle:"Generation failed",actionCopyError:"Copy error",emptyCard:"(not generated)",anchorMismatch:"Anchor did not match; scroll sync is unavailable",menuCopyMarkdown:"Copy Markdown",menuCopyPlain:"Copy plain text",menuCopyAnchor:"Copy anchor quote",menuJumpSource:"Jump to source",menuEditCard:"Edit this card",menuDeleteCard:"Delete this card",copiedMarkdown:"Copied Markdown",copiedPlain:"Copied plain text",copiedAnchor:"Copied anchor quote",copiedAllMarkdown:"Copied all Markdown",copyFailed:"Copy failed: {error}",actionFailed:"{label} failed: {error}",exported:"Exported \u2192 {path}",noCurrentNote:"No current note",cacheClearedFile:"Cleared cache: {name}",cacheClearedAll:"Cleared {count} cache entries",noCancelableJob:"No cancellable generation job",cancelRequested:"Cancel requested",cancelRequestedApiInFlight:"Cancel requested. The in-flight API request cannot be aborted immediately; its result will be ignored.",actionGenerate:"Generate parallel notes",fileMenuGenerate:"Generate parallel notes",fileMenuRegen:"Regenerate parallel notes",fileMenuClear:"Clear parallel-note cache",noExportContent:"No parallel notes to export",noCopyContent:"No parallel notes to copy",noActiveCard:"No active summary card to jump",viewOpenFailed:"Could not open the Parallel Reader pane",confirmRegenerateEditedCards:"These parallel-reader cards were edited manually. Regenerating will overwrite those edits. Continue?",regenerateCancelled:"Regeneration cancelled",cardDeleted:"Deleted this card",cardSaved:"Saved this card",editCardTitle:"Edit summary card",editCardTitleField:"Title",editCardGistField:"Gist",editCardBulletsField:"Bullets (one per line)",editCardCancel:"Cancel",editCardSave:"Save",openNoteFirst:"Open a note first",alreadyGenerating:"This note is already being generated",emptyNote:"The note is empty",longNoteTruncated:"Long note: only the first {count} characters will be sent to the model",generatingNotice:"Parallel Reader: asking the LLM to read and segment the full note...",noCardsReturned:"LLM returned no cards",generationDone:"Generated {count} sections{suffix}",unanchoredSuffix:" (\u26A0 {count} anchors unmatched)",cancelled:"Generation cancelled",cancelledError:"Generation cancelled",generationFailed:"Generation failed{kind}: {error}",errorCustomProviderBaseUrlRequired:"Custom provider requires an API Base URL.",errorApiBaseUrlMissing:"API Base URL is not set. Choose a provider or enter a custom base URL in settings.",errorModelMissing:"Model is not set. Enter a model ID in settings.",errorApiKeyMissing:"API key is not set. Enter an API Key in settings{hint}.",errorApiKeyEnvHint:" or set environment variable {envVar}",errorLlmNonJson:`LLM returned non-JSON: {excerpt}`,errorCustomHeadersJsonParse:"Custom headers JSON parse failed: {error}",errorCustomHeadersJsonObject:"Custom headers JSON must be an object",errorCustomHeadersLineFormat:"Custom headers lines must use `Header-Name: value`",errorProviderNonJson:`{label} returned non-JSON: -{excerpt}`,errorProviderRequestFailed:"{label} request failed: {error}",errorProviderApiStatus:"{label} API returned HTTP {status}: {excerpt}",noEditor:"Could not find the source note editor",settingUiLanguageName:"UI language",settingUiLanguageDesc:"Controls plugin UI, commands, and notices. Auto follows Obsidian/system language.",settingBackendName:"Backend",settingBackendDesc:"Backend for generating bullets: CLI reuses local login; API supports OpenAI, Anthropic, Gemini, and compatible proxies.",settingCliPathName:"CLI path (optional)",settingCliPathDesc:"Leave blank to auto-detect common paths. Obsidian launched from the GUI may not inherit shell PATH.",settingCliPathPlaceholder:"Example: /Users/you/bin/codex",settingCliTimeoutName:"CLI timeout (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"OpenClaw-style provider/model setup: presets define protocol, base URL, and auth defaults.",settingApiFormatName:"API format",settingApiFormatDesc:"Wire protocol for the provider. OpenAI-compatible proxies usually use Chat Completions.",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"Provider root URL, without /chat/completions. Blank uses the preset default.",settingApiKeyName:"API Key",settingApiKeyDesc:"Can be blank for local Ollama/LM Studio; most hosted providers require a key.",settingApiKeyEnvName:"API key env var",settingApiKeyEnvDesc:"Optional. Obsidian GUI may not inherit shell env; direct API Key is usually more reliable.",settingAuthTypeName:"Auth type",settingAuthTypeDesc:"Auto uses provider preset. Custom proxies can use Bearer, x-api-key, or none.",settingHeadersName:"Extra headers",settingHeadersDesc:"Optional. JSON object or one `Header: value` per line, useful for Cloudflare AI Gateway.",settingMaxTokensName:"Max output tokens",settingModelName:"Model",settingModelDescApi:"Model ID for API calls. Supports OpenClaw-style provider/model; matching provider prefixes are stripped.",settingModelDescCli:"Passed as --model for Claude Code. Usually ignored by Codex, which uses its default config.",settingMaxInputName:"Max input characters",settingMaxInputDesc:"Longer notes are truncated before sending to the model. Raise this for long-context models.",promptHeader:"Prompt",settingPromptLanguageName:"Output language",settingPromptLanguageDesc:"Controls title/gist/bullets language. Anchor is always copied verbatim from source.",settingCardRangeName:"Card count range",settingCardRangeDesc:"The model adapts segmentation within this range.",settingCustomPromptName:"Custom system prompt",settingCustomPromptDesc:"Optional. Variables: {minCards}, {maxCards}, {languageInstruction}, {schema}, {example}",settingCustomPromptPlaceholder:"Leave blank to use built-in prompt",settingTestBackendName:"Test current backend",settingTestBackendDescApi:"Sends a minimal LLM request to validate API settings.",settingTestBackendDescCli:"Runs ` --version` to verify the binary can be spawned.",backendTestFailed:"\u2717 Backend test failed: {error}",settingExportFolderName:"Export folder",settingExportFolderDesc:"Parallel-note output location, relative to the Vault root.",cacheHeader:"Cache",settingMaxCacheName:"Max cached notes",settingMaxCacheDesc:"Prunes least-recently accessed note caches above this limit. Cache is stored in plugin cache.json.",cachePruned:"Pruned {count} old cache entries",cachedNotesName:"Cached notes: {count}",cachedNotesDesc:"Cache is invalidated by source SHA1 and generation settings fingerprint.",clearAllCacheButton:"Clear all caches",settingStreamingName:"Streaming output",settingStreamingDesc:"Show real-time LLM output progress during generation (OpenAI Chat and Anthropic formats only)",settingStreamingTimeoutName:"Streaming timeout (ms)",settingStreamingTimeoutDesc:"Timeout for one streaming request; invalid values or values below 1000ms use the default",cmdBatchGenerate:"Batch generate parallel notes (current folder)",batchSelectFolder:"Enter the folder path to batch-process (leave blank for Vault root)",batchFolderPrompt:"Folder path",batchAlreadyRunning:"Batch generation is already running",batchCancelRequested:"Batch cancellation requested; it will stop after the current note",batchInvalidFolderInput:"Invalid folder path: {path}",batchFolderNotFound:"Folder not found: {path}",batchNoMarkdown:"No Markdown files found in that folder",batchProgress:"Batch generating: {current}/{total}",batchDone:"Batch complete: processed {processed}/{total}, skipped {skipped} (cached), errors {errors}",batchCancelled:"Batch cancelled: processed {processed}/{total}, skipped {skipped} (cached), errors {errors}"}};function Wt(n){let t=n?.uiLanguage;return t==="zh"||t==="en"?t:String((typeof navigator<"u"?navigator:null)?.language||"").toLowerCase().startsWith("zh")?"zh":"en"}function b(n,t,e){let r=Wt(n),i=le[r]||le.en,a=le.en[t]||le.zh[t]||t,s=i[t]||a;return String(s).replace(/\{([a-zA-Z0-9_]+)\}/g,(o,l)=>e&&Object.hasOwn(e,l)?String(e[l]):o)}var Ne=1e5,Yt=2,G=2,U=100,Xt=1e3,K={zh:"\u4E2D\u6587",en:"English",auto:"Auto-detect"},Ie={auto:"Auto",zh:"\u4E2D\u6587",en:"English"},m={uiLanguage:"auto",backend:"api",cliPath:"",apiProvider:"anthropic",apiFormat:"anthropic-messages",apiBaseUrl:"",apiKey:"",apiKeyEnvVar:"",apiAuthType:"auto",apiHeaders:"",apiMaxTokens:4096,maxDocChars:Ne,maxCacheEntries:U,promptLanguage:"zh",minCards:5,maxCards:15,customSystemPrompt:"",model:"claude-sonnet-4-6",exportFolder:"Reading/Articles",cliTimeoutMs:12e4,streaming:!0,streamingTimeoutMs:12e4},k={"anthropic-messages":{label:"Anthropic Messages",defaultBaseUrl:"https://api.anthropic.com/v1",defaultAuthType:"x-api-key"},"openai-chat":{label:"OpenAI Chat Completions",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer",tokenLimitField:"max_tokens"},"openai-responses":{label:"OpenAI Responses",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer"},"google-generative-ai":{label:"Google Gemini generateContent",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",defaultAuthType:"x-goog-api-key"}},De={auto:"Auto",bearer:"Authorization: Bearer","x-api-key":"x-api-key","x-goog-api-key":"x-goog-api-key","api-key":"api-key",none:"None"},M={anthropic:{label:"Anthropic",format:"anthropic-messages",baseUrl:"https://api.anthropic.com/v1",authType:"x-api-key",envVar:"ANTHROPIC_API_KEY",model:"claude-sonnet-4-6"},openai:{label:"OpenAI",format:"openai-chat",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",tokenLimitField:"max_completion_tokens",model:"gpt-5.1"},"openai-responses":{label:"OpenAI Responses",format:"openai-responses",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",modelPrefix:"openai",model:"gpt-5.1"},google:{label:"Google Gemini",format:"google-generative-ai",baseUrl:"https://generativelanguage.googleapis.com/v1beta",authType:"x-goog-api-key",envVar:"GEMINI_API_KEY",model:"gemini-3-pro-preview"},openrouter:{label:"OpenRouter",format:"openai-chat",baseUrl:"https://openrouter.ai/api/v1",authType:"bearer",envVar:"OPENROUTER_API_KEY",model:""},groq:{label:"Groq",format:"openai-chat",baseUrl:"https://api.groq.com/openai/v1",authType:"bearer",envVar:"GROQ_API_KEY",model:""},deepseek:{label:"DeepSeek",format:"openai-chat",baseUrl:"https://api.deepseek.com",authType:"bearer",envVar:"DEEPSEEK_API_KEY",model:"deepseek-chat"},moonshot:{label:"Moonshot / Kimi",format:"openai-chat",baseUrl:"https://api.moonshot.ai/v1",authType:"bearer",envVar:"MOONSHOT_API_KEY",model:"kimi-k2.5"},qianfan:{label:"Baidu Qianfan",format:"openai-chat",baseUrl:"https://qianfan.baidubce.com/v2",authType:"bearer",envVar:"QIANFAN_API_KEY",model:"deepseek-v3.2"},minimax:{label:"MiniMax (Anthropic-compatible)",format:"anthropic-messages",baseUrl:"https://api.minimax.io/anthropic",authType:"bearer",envVar:"MINIMAX_API_KEY",model:"MiniMax-M2.1"},xai:{label:"xAI",format:"openai-chat",baseUrl:"https://api.x.ai/v1",authType:"bearer",envVar:"XAI_API_KEY",model:""},mistral:{label:"Mistral",format:"openai-chat",baseUrl:"https://api.mistral.ai/v1",authType:"bearer",envVar:"MISTRAL_API_KEY",model:""},cerebras:{label:"Cerebras",format:"openai-chat",baseUrl:"https://api.cerebras.ai/v1",authType:"bearer",envVar:"CEREBRAS_API_KEY",model:""},zai:{label:"Z.AI / GLM",format:"openai-chat",baseUrl:"https://api.z.ai/api/paas/v4",authType:"bearer",envVar:"ZAI_API_KEY",model:""},ollama:{label:"Ollama (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:11434/v1",authType:"none",envVar:"",model:""},lmstudio:{label:"LM Studio (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:1234/v1",authType:"none",envVar:"",model:""},"custom-openai":{label:"Custom OpenAI-compatible",format:"openai-chat",baseUrl:"",authType:"bearer",envVar:"",model:""},"custom-anthropic":{label:"Custom Anthropic-compatible",format:"anthropic-messages",baseUrl:"",authType:"x-api-key",envVar:"",model:""}};function V(n){return yt.default.createHash("sha1").update(n,"utf8").digest("hex")}function Me(n){return Array.isArray(n)?"["+n.map(Me).join(",")+"]":n&&typeof n=="object"?"{"+Object.keys(n).sort().map(t=>JSON.stringify(t)+":"+Me(n[t])).join(",")+"}":JSON.stringify(n)}function Le(n){return n==="api"||n==="anthropic-api"}function v(n){return M[n.apiProvider]||M.anthropic}function A(n){let t=v(n),e=(n.apiFormat||t.format||"").trim();return k[e]?e:t.format}function R(n){let t=A(n),e=v(n),r=(n.apiBaseUrl||"").trim();if(r)return r.replace(/\/+$/,"");if((n.apiProvider||"").startsWith("custom-"))throw new Error(b(n,"errorCustomProviderBaseUrlRequired"));let i=(e.baseUrl||k[t].defaultBaseUrl||"").trim();if(!i)throw new Error(b(n,"errorApiBaseUrlMissing"));return i.replace(/\/+$/,"")}function Oe(n){let t=(n.apiAuthType||"auto").trim();if(t&&t!=="auto")return t;let e=v(n),r=A(n);return e.authType||k[r].defaultAuthType||"bearer"}function bt(n){let t=(n.apiKey||"").trim();if(t)return t;let e=(n.apiKeyEnvVar||v(n).envVar||"").trim();return e&&process.env?.[e]?String(process.env[e]).trim():""}function D(n){let t=(n.model||"").trim();if(!t)throw new Error(b(n,"errorModelMissing"));let e=v(n),r=[n.apiProvider,e.modelPrefix].map(a=>(a||"").trim()).filter(Boolean),i=t.toLowerCase();for(let a of r){let s=a.toLowerCase();if(i.startsWith(s+"/"))return t.slice(a.length+1).trim()}return t}function Be(n,t){let e=v(n),r=M[t]||M.anthropic,i=(n.model||"").trim(),a=!i||i===m.model||!!e.model&&i===e.model;return{...n,apiProvider:t,apiFormat:r.format,apiBaseUrl:r.baseUrl,apiAuthType:r.authType||"auto",apiKeyEnvVar:r.envVar||"",...a?{model:r.model||""}:{}}}function ce(n){let t={...n};Ie[t.uiLanguage]||(t.uiLanguage=m.uiLanguage),(!t.apiProvider||!M[t.apiProvider])&&(t.apiProvider="anthropic");let e=v(t);(!t.apiFormat||!k[t.apiFormat])&&(t.apiFormat=e.format),(!t.apiAuthType||!De[t.apiAuthType])&&(t.apiAuthType="auto"),t.backend==="anthropic-api"&&(t.apiProvider=t.apiProvider||"anthropic",t.apiFormat=t.apiFormat||"anthropic-messages",t.apiBaseUrl=t.apiBaseUrl||M.anthropic.baseUrl,t.apiAuthType=t.apiAuthType||"x-api-key",t.apiKeyEnvVar=t.apiKeyEnvVar||"ANTHROPIC_API_KEY");let r=Number(t.apiMaxTokens);(!Number.isFinite(r)||r<=0)&&(t.apiMaxTokens=m.apiMaxTokens);let i=Number(t.maxDocChars);return(!Number.isFinite(i)||i<1e3)&&(t.maxDocChars=m.maxDocChars),t.maxCacheEntries=wt(t.maxCacheEntries),K[t.promptLanguage]||(t.promptLanguage=m.promptLanguage),t.minCards=mt(t.minCards,m.minCards),t.maxCards=mt(t.maxCards,m.maxCards),t.maxCardsft(s)-ft(l)||a.localeCompare(o)).slice(0,r.length-e).map(([a])=>a);for(let a of i)delete n[a];return i}function z(n){let t=ce(Object.assign({},m,n||{})),e=Le(t.backend),r=v(t),i=A(t),a=e?(t.apiBaseUrl||r.baseUrl||k[i]?.defaultBaseUrl||"").trim().replace(/\/+$/,""):"";return V(Me({cacheSchemaVersion:G,promptVersion:Yt,maxDocChars:Number(t.maxDocChars)||m.maxDocChars,promptLanguage:t.promptLanguage,minCards:t.minCards,maxCards:t.maxCards,customSystemPromptHash:V(t.customSystemPrompt||""),backend:t.backend,model:t.model,apiProvider:e?t.apiProvider:"",apiFormat:e?i:"",apiBaseUrl:a,apiAuthType:e?Oe(t):"",apiHeadersHash:e?V(t.apiHeaders||""):"",apiMaxTokens:e?Number(t.apiMaxTokens)||m.apiMaxTokens:0,structuredOutputVersion:1}))}function j(n,t,e){return!!n&&n.schemaVersion===G&&n.contentHash===V(t)&&n.settingsHash===z(e)}function de(n){return(n||"").trim().split("/").map(t=>t.trim()).filter(t=>t&&t!=="."&&t!=="..").join("/")}function _e(n){return(n||"").split("/").map(t=>t.trim()).some(t=>t==="."||t==="..")}function Ve(n,t){let e=de(n);return _e(n)?{valid:!1,reason:"unsafe",folderPath:e}:e&&!t(e)?{valid:!1,reason:"missing",folderPath:e}:{valid:!0,reason:"ok",folderPath:e}}function Zt(n,t){return t?n.parent?.path===t:!n.path.includes("/")}function Ge(n,t){let e=de(t);return n.filter(r=>Zt(r,e))}function Ue(n,t){return{current:n+1,total:t}}function je(n){return{total:n,processed:0,skipped:0,errors:0}}function He(n){return{...n,processed:n.processed+1}}function Je(n){return{...n,processed:n.processed+1,skipped:n.skipped+1}}function $e(n){return{...n,processed:n.processed+1,errors:n.errors+1}}function Ke(n,t,e){return!!n&&j(n,t,e)}function qe(){return{cancelled:!1,currentPath:""}}function ze(n,t){return n.currentPath=t,n}function pe(n){return!n||n.cancelled?!1:(n.cancelled=!0,!0)}function vt(n,t,e){return new Promise(r=>{let i=new Ct.Modal(n);i.onOpen=()=>{i.contentEl.createEl("p",{text:t});let s=i.contentEl.createDiv({cls:"parallel-reader-modal-field"}).createEl("input",{type:"text"});s.placeholder=e,s.addEventListener("keydown",o=>{o.key==="Enter"&&(r(s.value.trim()),i.close())}),i.contentEl.createEl("button",{text:"OK"}).addEventListener("click",()=>{r(s.value.trim()),i.close()})},i.onClose=()=>r(null),i.open()})}function he(n,t){return n?{...n,lastAccessedAt:t||new Date().toISOString()}:null}function ge(n){return JSON.stringify({version:1,entries:n||{}})}function We(n,t){return!!t&&!!n&&typeof n.updatedAt=="string"&&n.updatedAt.trim().length>0}var W=class{constructor(t,e,r,i){this.adapter=t;this.configDir=e;this.pluginId=r;this.getSettings=i;this.cache={};this._timer=null;this._dirty=!1}filePath(){return`${this.configDir}/plugins/${this.pluginId}/cache.json`}async ensureDir(){let t=`${this.configDir}/plugins/${this.pluginId}`;try{if(typeof this.adapter.exists=="function"&&await this.adapter.exists(t))return;await this.adapter.mkdir(t)}catch{}}async readFile(){try{let t=await this.adapter.read(this.filePath()),e=JSON.parse(t);if(e&&typeof e=="object"&&e.entries&&typeof e.entries=="object")return e.entries}catch(t){let e=t instanceof Error?t.message:String(t);/not found|does not exist|ENOENT/i.test(e)||console.warn("[parallel-reader] failed to read cache.json",t)}return{}}async writeFile(){await this.ensureDir(),await this.adapter.write(this.filePath(),ge(this.cache))}async load(){this.cache=await this.readFile(),this.prune().length>0&&await this.writeFile()}prune(){let t=this.getSettings();return ue(this.cache,t?.maxCacheEntries||U)}async pruneIfNeeded(){let t=this.prune();return t.length>0&&await this.save(),t}async save(){this._timer&&(clearTimeout(this._timer),this._timer=null),this.prune(),await this.writeFile(),this._dirty=!1}scheduleSave(t=5e3){this._dirty=!0,!this._timer&&(this._timer=setTimeout(()=>{this._timer=null,this._dirty&&this.save().catch(e=>console.error("[parallel-reader] failed to save cache",e))},t))}async flush(){this._timer&&(clearTimeout(this._timer),this._timer=null),this._dirty&&await this.save()}get(t){return this.cache[t]||null}touch(t){let e=he(this.cache[t]||null);return e?(this.cache[t]=e,this.scheduleSave(),e):null}async move(t,e){if(typeof t!="string"||typeof e!="string"||!t.trim()||!e.trim())return!1;if(t===e)return Object.hasOwn(this.cache,t);let r=this.cache[t];if(!r||Object.hasOwn(this.cache,e))return!1;let i={...this.cache,[e]:r};return delete i[t],this.cache=i,await this.save(),!0}async put(t,e,r,i){let a=new Date().toISOString();this.cache[t]={schemaVersion:G,contentHash:V(e),settingsHash:z(i||this.getSettings()),cards:r,generatedAt:a,lastAccessedAt:a},await this.save()}async replaceCards(t,e){let r=this.cache[t];if(!r)return!1;let i=new Date().toISOString();return this.cache[t]={...r,cards:(e||[]).map(a=>({title:a.title,anchor:a.anchor,gist:a.gist,bullets:a.bullets||[]})),updatedAt:i,lastAccessedAt:i},await this.save(),!0}async delete(t){this.cache[t]&&(delete this.cache[t],await this.save())}async clear(){this.cache={},await this.save()}};function me(n,t){let e=Array.isArray(n)?n.slice():[];return!Number.isInteger(t)||t<0||t>=e.length||e.splice(t,1),e}function fe(n,t,e){if(!Number.isInteger(n)||!Number.isInteger(t)||t<=0||!Number.isInteger(e)||e<0||n<0||n>=t)return e;let r=t-1;return r<=0?-1:n=r.length||(r[t]=Object.assign({},r[t],e||{})),r}function Ye(n,t){let e=(t||[]).map(r=>({title:r.title,level:2,anchor:r.anchor,gist:r.gist,startLine:_(n,r.anchor),bullets:r.bullets||[]}));return e.sort((r,i)=>r.startLine<0&&i.startLine<0?0:r.startLine<0?1:i.startLine<0?-1:r.startLine-i.startLine),e}var St=require("child_process"),kt=oe(require("fs")),we=oe(require("os")),w=oe(require("path"));var H=class extends Error{constructor(t){super("\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0"),this.name="GenerationJobAlreadyRunningError",this.code="already-running",this.key=t}},T=class extends Error{constructor(t){super("\u751F\u6210\u5DF2\u53D6\u6D88"),this.name="GenerationJobCancelledError",this.code="cancelled",this.key=t}},Xe=class{constructor(t){this.key=t,this.phase="queued",this.cancelled=!1,this.startedAt=new Date().toISOString(),this.updatedAt=this.startedAt,this._cancelHandlers=[]}setPhase(t){this.phase=t,this.updatedAt=new Date().toISOString()}onCancel(t){if(typeof t=="function"){if(this.cancelled){t();return}this._cancelHandlers.push(t)}}cancel(){if(this.cancelled)return!1;this.cancelled=!0,this.setPhase("cancelled");for(let t of this._cancelHandlers.splice(0))try{t()}catch(e){console.warn("[parallel-reader] cancel handler error",e)}return!0}throwIfCancelled(){if(this.cancelled)throw new T(this.key)}},Y=class{constructor(){this.jobs=new Map}get(t){return this.jobs.get(t)||null}isRunning(t){return this.jobs.has(t)}async start(t,e){if(this.jobs.has(t))throw new H(t);let r=new Xe(t);this.jobs.set(t,r);try{r.setPhase("running");let i=await e(r);return r.throwIfCancelled(),i}catch(i){throw r.cancelled&&!(i instanceof T)?new T(t):i}finally{this.jobs.delete(t)}}cancel(t){let e=this.jobs.get(t);return e?e.cancel():!1}};function Ze(n){if(n instanceof T)return"cancelled";let t=n;if(t?.code==="cancelled")return"cancelled";let e=String(t?.message||n);return/api key|unauthorized|401|403|认证|权限/i.test(e)?"auth":/timeout|超时|timed out/i.test(e)?"timeout":/429|rate limit|too many requests/i.test(e)?"rate-limit":/非 JSON|json_schema|schema|structured/i.test(e)?"schema":/model 未设置|base url|配置|config/i.test(e)?"config":"unknown"}var X="record_parallel_reader_cards";function be(n){let t=[],e=-1,r=0,i=!1,a=!1;for(let s=0;s0&&r--,r===0&&e>=0&&(t.push(n.slice(e,s+1)),e=-1))}return t}function Qe(n){let t=(n||"").trim();if(!t)return t;try{return JSON.parse(t),t}catch{}let e=t.match(/```(?:json)?\s*([\s\S]*?)```/);if(e){let i=e[1].trim();try{return JSON.parse(i),i}catch{}}let r=be(t);r.sort((i,a)=>a.length-i.length);for(let i of r)try{return JSON.parse(i),i}catch{}return t}function et(n){let t=n.match(/\{\s*"cards"\s*:\s*\[/);if(!t||t.index===void 0)return null;let e=t.index+t[0].length,r=be(n.slice(e)),i=[];for(let a of r)try{JSON.parse(a),i.push(a)}catch{}return i.length===0?null:'{"cards":['+i.join(",")+"]}"}function P(n,t){let e=Qe(n),r;try{r=JSON.parse(e)}catch{let a=et(n);if(a)try{return r=JSON.parse(a),console.warn("[parallel-reader] LLM output was truncated; salvaged",r.cards?.length??0,"complete cards. Consider increasing max tokens."),J(r)}catch{}throw console.warn("[parallel-reader] LLM returned non-JSON. Raw response:",n),new Error(b(t||null,"errorLlmNonJson",{excerpt:(n||"").slice(0,500)}))}return J(r)}function J(n){return(n&&Array.isArray(n.cards)?n.cards:[]).filter(e=>!!e&&typeof e=="object").map(e=>({title:typeof e.title=="string"?e.title:"(\u65E0\u6807\u9898)",anchor:typeof e.anchor=="string"?e.anchor:"",gist:typeof e.gist=="string"?e.gist:"",bullets:Array.isArray(e.bullets)?e.bullets.filter(r=>typeof r=="string"):[]}))}function Z(n){let t={type:"object",properties:{title:{type:"string"},anchor:{type:"string"},gist:{type:"string"},bullets:{type:"array",items:{type:"string"}}},required:["title","anchor","gist","bullets"]},e={type:"object",properties:{cards:{type:"array",items:t}},required:["cards"]};return n&&(t.additionalProperties=!1,e.additionalProperties=!1),e}function xt(){return{type:"json_schema",json_schema:{name:"parallel_reader_cards",strict:!0,schema:Z(!0)}}}function At(){return{format:{type:"json_schema",name:"parallel_reader_cards",strict:!0,schema:Z(!0)}}}function Pt(){return{name:X,description:"Return the generated Parallel Reader summary cards.",input_schema:Z(!0)}}function L(n,t){if(t?.trim())return t.trim();let e=we.default.homedir(),r=process.platform==="win32",i=r?[".cmd",".exe",""]:[""],a=r?[w.default.join(e,"AppData","Roaming","npm"),w.default.join(e,"AppData","Local","Programs","claude-code"),w.default.join(e,"AppData","Local","Programs","codex"),w.default.join(e,".claude","local"),w.default.join(e,".codex","bin"),w.default.join(e,".bun","bin"),w.default.join(e,"scoop","shims"),"C:\\Program Files\\nodejs"]:[w.default.join(e,"bin"),w.default.join(e,".local","bin"),w.default.join(e,".claude","local"),w.default.join(e,".codex","bin"),w.default.join(e,".bun","bin"),w.default.join(e,".npm-global","bin"),w.default.join(e,".cargo","bin"),"/opt/homebrew/bin","/usr/local/bin"];for(let s of a)for(let o of i){let l=w.default.join(s,n+o);try{if(kt.default.existsSync(l))return l}catch{}}return n}function O(n,t,e,r,i,a=St.spawn){return new Promise((s,o)=>{let l,c=!1,u=null,d=y=>{c||(c=!0,u&&clearTimeout(u),o(y))},f=y=>{c||(c=!0,u&&clearTimeout(u),s(y))};try{l=a(n,t,{stdio:["pipe","pipe","pipe"],env:{...process.env,PATH:[process.env.PATH||"","/usr/local/bin","/opt/homebrew/bin",w.default.join(we.default.homedir(),".local/bin"),w.default.join(we.default.homedir(),".claude/local")].filter(Boolean).join(":")}})}catch(y){return o(new Error(`Failed to start ${n}: ${y.message}`))}let g="",C="";if(u=setTimeout(()=>{try{l.kill("SIGKILL")}catch(y){console.warn("[parallel-reader] failed to kill timed-out CLI process",y)}d(new Error(`CLI timed out (${r}ms)`))},r),i&&i.onCancel(()=>{try{l.kill("SIGKILL")}catch(y){console.warn("[parallel-reader] failed to kill cancelled CLI process",y)}d(new T(i.key))}),!l.stdout||!l.stderr||!l.stdin){d(new Error("CLI process streams are unavailable"));return}let S=l.stdout,ae=l.stderr,Re=l.stdin;if(S.on("data",y=>{g+=y.toString("utf8")}),ae.on("data",y=>{C+=y.toString("utf8")}),l.on("error",y=>{d(new Error(`CLI startup error: ${y.message}. Try setting an absolute CLI path.`))}),l.on("close",y=>{if(!c){if(y!==0)return d(new Error(`CLI exited with code ${y} +{excerpt}`,errorProviderRequestFailed:"{label} request failed: {error}",errorProviderApiStatus:"{label} API returned HTTP {status}: {excerpt}",noEditor:"Could not find the source note editor",settingUiLanguageName:"UI language",settingUiLanguageDesc:"Controls plugin UI, commands, and notices. Auto follows Obsidian/system language.",settingBackendName:"Backend",settingBackendDesc:"Backend for generating bullets: CLI reuses local login; API supports OpenAI, Anthropic, Gemini, and compatible proxies.",settingCliPathName:"CLI path (optional)",settingCliPathDesc:"Leave blank to auto-detect common paths. Obsidian launched from the GUI may not inherit shell PATH.",settingCliPathPlaceholder:"Example: /Users/you/bin/codex",settingCliTimeoutName:"CLI timeout (ms)",apiProviderHeader:"API Provider",settingProviderPresetName:"Provider preset",settingProviderPresetDesc:"OpenClaw-style provider/model setup: presets define protocol, base URL, and auth defaults.",settingApiFormatName:"API format",settingApiFormatDesc:"Wire protocol for the provider. OpenAI-compatible proxies usually use Chat Completions.",settingBaseUrlName:"Base URL",settingBaseUrlDesc:"Provider root URL, without /chat/completions. Blank uses the preset default.",settingApiKeyName:"API Key",settingApiKeyDesc:"Can be blank for local Ollama/LM Studio; most hosted providers require a key.",settingApiKeyEnvName:"API key env var",settingApiKeyEnvDesc:"Optional. Obsidian GUI may not inherit shell env; direct API Key is usually more reliable.",settingAuthTypeName:"Auth type",settingAuthTypeDesc:"Auto uses provider preset. Custom proxies can use Bearer, x-api-key, or none.",settingHeadersName:"Extra headers",settingHeadersDesc:"Optional. JSON object or one `Header: value` per line, useful for Cloudflare AI Gateway.",settingMaxTokensName:"Max output tokens",settingModelName:"Model",settingModelDescApi:"Model ID for API calls. Supports OpenClaw-style provider/model; matching provider prefixes are stripped.",settingModelDescCli:"Passed as --model for Claude Code. Usually ignored by Codex, which uses its default config.",settingMaxInputName:"Max input characters",settingMaxInputDesc:"Longer notes are truncated before sending to the model. Raise this for long-context models.",promptHeader:"Prompt",settingPromptLanguageName:"Output language",settingPromptLanguageDesc:"Controls title/gist/bullets language. Anchor is always copied verbatim from source.",settingCardRangeName:"Card count range",settingCardRangeDesc:"The model adapts segmentation within this range.",settingCustomPromptName:"Custom system prompt",settingCustomPromptDesc:"Optional. Variables: {minCards}, {maxCards}, {languageInstruction}, {schema}, {example}",settingCustomPromptPlaceholder:"Leave blank to use built-in prompt",settingTestBackendName:"Test current backend",settingTestBackendDescApi:"Sends a minimal LLM request to validate API settings.",settingTestBackendDescCli:"Runs ` --version` to verify the binary can be spawned.",backendTestFailed:"\u2717 Backend test failed: {error}",settingExportFolderName:"Export folder",settingExportFolderDesc:"Parallel-note output location, relative to the Vault root.",cacheHeader:"Cache",settingMaxCacheName:"Max cached notes",settingMaxCacheDesc:"Prunes least-recently accessed note caches above this limit. Cache is stored in plugin cache.json.",cachePruned:"Pruned {count} old cache entries",cachedNotesName:"Cached notes: {count}",cachedNotesDesc:"Cache is invalidated by source SHA1 and generation settings fingerprint.",clearAllCacheButton:"Clear all caches",settingStreamingName:"Streaming output",settingStreamingDesc:"Show real-time LLM output progress during generation (OpenAI Chat and Anthropic formats only)",settingStreamingTimeoutName:"Streaming timeout (ms)",settingStreamingTimeoutDesc:"Timeout for one streaming request; invalid values or values below 1000ms use the default",cmdBatchGenerate:"Batch generate parallel notes (current folder)",batchSelectFolder:"Enter the folder path to batch-process (leave blank for Vault root)",batchFolderPrompt:"Folder path",batchAlreadyRunning:"Batch generation is already running",batchCancelRequested:"Batch cancellation requested; it will stop after the current note",batchInvalidFolderInput:"Invalid folder path: {path}",batchFolderNotFound:"Folder not found: {path}",batchNoMarkdown:"No Markdown files found in that folder",batchProgress:"Batch generating: {current}/{total}",batchDone:"Batch complete: processed {processed}/{total}, skipped {skipped} (cached), errors {errors}",batchCancelled:"Batch cancelled: processed {processed}/{total}, skipped {skipped} (cached), errors {errors}"}};function Wt(n){let t=n?.uiLanguage;return t==="zh"||t==="en"?t:String((typeof navigator<"u"?navigator:null)?.language||"").toLowerCase().startsWith("zh")?"zh":"en"}function b(n,t,e){let r=Wt(n),i=le[r]||le.en,a=le.en[t]||le.zh[t]||t,s=i[t]||a;return String(s).replace(/\{([a-zA-Z0-9_]+)\}/g,(o,l)=>e&&Object.hasOwn(e,l)?String(e[l]):o)}var Ne=1e5,Yt=2,G=2,U=100,Xt=1e3,K={zh:"\u4E2D\u6587",en:"English",auto:"Auto-detect"},Ie={auto:"Auto",zh:"\u4E2D\u6587",en:"English"},m={uiLanguage:"auto",backend:"api",cliPath:"",apiProvider:"anthropic",apiFormat:"anthropic-messages",apiBaseUrl:"",apiKey:"",apiKeyEnvVar:"",apiAuthType:"auto",apiHeaders:"",apiMaxTokens:4096,maxDocChars:Ne,maxCacheEntries:U,promptLanguage:"zh",minCards:5,maxCards:15,customSystemPrompt:"",model:"claude-sonnet-4-6",exportFolder:"Reading/Articles",cliTimeoutMs:12e4,streaming:!0,streamingTimeoutMs:12e4},k={"anthropic-messages":{label:"Anthropic Messages",defaultBaseUrl:"https://api.anthropic.com/v1",defaultAuthType:"x-api-key"},"openai-chat":{label:"OpenAI Chat Completions",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer",tokenLimitField:"max_tokens"},"openai-responses":{label:"OpenAI Responses",defaultBaseUrl:"https://api.openai.com/v1",defaultAuthType:"bearer"},"google-generative-ai":{label:"Google Gemini generateContent",defaultBaseUrl:"https://generativelanguage.googleapis.com/v1beta",defaultAuthType:"x-goog-api-key"}},De={auto:"Auto",bearer:"Authorization: Bearer","x-api-key":"x-api-key","x-goog-api-key":"x-goog-api-key","api-key":"api-key",none:"None"},M={anthropic:{label:"Anthropic",format:"anthropic-messages",baseUrl:"https://api.anthropic.com/v1",authType:"x-api-key",envVar:"ANTHROPIC_API_KEY",model:"claude-sonnet-4-6"},openai:{label:"OpenAI",format:"openai-chat",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",tokenLimitField:"max_completion_tokens",model:"gpt-5.1"},"openai-responses":{label:"OpenAI Responses",format:"openai-responses",baseUrl:"https://api.openai.com/v1",authType:"bearer",envVar:"OPENAI_API_KEY",modelPrefix:"openai",model:"gpt-5.1"},google:{label:"Google Gemini",format:"google-generative-ai",baseUrl:"https://generativelanguage.googleapis.com/v1beta",authType:"x-goog-api-key",envVar:"GEMINI_API_KEY",model:"gemini-3-pro-preview"},openrouter:{label:"OpenRouter",format:"openai-chat",baseUrl:"https://openrouter.ai/api/v1",authType:"bearer",envVar:"OPENROUTER_API_KEY",model:""},groq:{label:"Groq",format:"openai-chat",baseUrl:"https://api.groq.com/openai/v1",authType:"bearer",envVar:"GROQ_API_KEY",model:""},deepseek:{label:"DeepSeek",format:"openai-chat",baseUrl:"https://api.deepseek.com",authType:"bearer",envVar:"DEEPSEEK_API_KEY",model:"deepseek-chat"},moonshot:{label:"Moonshot / Kimi",format:"openai-chat",baseUrl:"https://api.moonshot.ai/v1",authType:"bearer",envVar:"MOONSHOT_API_KEY",model:"kimi-k2.5"},qianfan:{label:"Baidu Qianfan",format:"openai-chat",baseUrl:"https://qianfan.baidubce.com/v2",authType:"bearer",envVar:"QIANFAN_API_KEY",model:"deepseek-v3.2"},minimax:{label:"MiniMax (Anthropic-compatible)",format:"anthropic-messages",baseUrl:"https://api.minimax.io/anthropic",authType:"bearer",envVar:"MINIMAX_API_KEY",model:"MiniMax-M2.1"},xai:{label:"xAI",format:"openai-chat",baseUrl:"https://api.x.ai/v1",authType:"bearer",envVar:"XAI_API_KEY",model:""},mistral:{label:"Mistral",format:"openai-chat",baseUrl:"https://api.mistral.ai/v1",authType:"bearer",envVar:"MISTRAL_API_KEY",model:""},cerebras:{label:"Cerebras",format:"openai-chat",baseUrl:"https://api.cerebras.ai/v1",authType:"bearer",envVar:"CEREBRAS_API_KEY",model:""},zai:{label:"Z.AI / GLM",format:"openai-chat",baseUrl:"https://api.z.ai/api/paas/v4",authType:"bearer",envVar:"ZAI_API_KEY",model:""},ollama:{label:"Ollama (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:11434/v1",authType:"none",envVar:"",model:""},lmstudio:{label:"LM Studio (local)",format:"openai-chat",baseUrl:"http://127.0.0.1:1234/v1",authType:"none",envVar:"",model:""},"custom-openai":{label:"Custom OpenAI-compatible",format:"openai-chat",baseUrl:"",authType:"bearer",envVar:"",model:""},"custom-anthropic":{label:"Custom Anthropic-compatible",format:"anthropic-messages",baseUrl:"",authType:"x-api-key",envVar:"",model:""}};function V(n){return yt.default.createHash("sha1").update(n,"utf8").digest("hex")}function Me(n){return Array.isArray(n)?"["+n.map(Me).join(",")+"]":n&&typeof n=="object"?"{"+Object.keys(n).sort().map(t=>JSON.stringify(t)+":"+Me(n[t])).join(",")+"}":JSON.stringify(n)}function Le(n){return n==="api"||n==="anthropic-api"}function v(n){return M[n.apiProvider]||M.anthropic}function A(n){let t=v(n),e=(n.apiFormat||t.format||"").trim();return k[e]?e:t.format}function R(n){let t=A(n),e=v(n),r=(n.apiBaseUrl||"").trim();if(r)return r.replace(/\/+$/,"");if((n.apiProvider||"").startsWith("custom-"))throw new Error(b(n,"errorCustomProviderBaseUrlRequired"));let i=(e.baseUrl||k[t].defaultBaseUrl||"").trim();if(!i)throw new Error(b(n,"errorApiBaseUrlMissing"));return i.replace(/\/+$/,"")}function Oe(n){let t=(n.apiAuthType||"auto").trim();if(t&&t!=="auto")return t;let e=v(n),r=A(n);return e.authType||k[r].defaultAuthType||"bearer"}function bt(n){let t=(n.apiKey||"").trim();if(t)return t;let e=(n.apiKeyEnvVar||v(n).envVar||"").trim();return e&&process.env?.[e]?String(process.env[e]).trim():""}function D(n){let t=(n.model||"").trim();if(!t)throw new Error(b(n,"errorModelMissing"));let e=v(n),r=[n.apiProvider,e.modelPrefix].map(a=>(a||"").trim()).filter(Boolean),i=t.toLowerCase();for(let a of r){let s=a.toLowerCase();if(i.startsWith(s+"/"))return t.slice(a.length+1).trim()}return t}function Be(n,t){let e=v(n),r=M[t]||M.anthropic,i=(n.model||"").trim(),a=!i||i===m.model||!!e.model&&i===e.model;return{...n,apiProvider:t,apiFormat:r.format,apiBaseUrl:r.baseUrl,apiAuthType:r.authType||"auto",apiKeyEnvVar:r.envVar||"",...a?{model:r.model||""}:{}}}function ce(n){let t={...n};Ie[t.uiLanguage]||(t.uiLanguage=m.uiLanguage),(!t.apiProvider||!M[t.apiProvider])&&(t.apiProvider="anthropic");let e=v(t);(!t.apiFormat||!k[t.apiFormat])&&(t.apiFormat=e.format),(!t.apiAuthType||!De[t.apiAuthType])&&(t.apiAuthType="auto"),t.backend==="anthropic-api"&&(t.apiProvider=t.apiProvider||"anthropic",t.apiFormat=t.apiFormat||"anthropic-messages",t.apiBaseUrl=t.apiBaseUrl||M.anthropic.baseUrl,t.apiAuthType=t.apiAuthType||"x-api-key",t.apiKeyEnvVar=t.apiKeyEnvVar||"ANTHROPIC_API_KEY");let r=Number(t.apiMaxTokens);(!Number.isFinite(r)||r<=0)&&(t.apiMaxTokens=m.apiMaxTokens);let i=Number(t.maxDocChars);return(!Number.isFinite(i)||i<1e3)&&(t.maxDocChars=m.maxDocChars),t.maxCacheEntries=wt(t.maxCacheEntries),K[t.promptLanguage]||(t.promptLanguage=m.promptLanguage),t.minCards=mt(t.minCards,m.minCards),t.maxCards=mt(t.maxCards,m.maxCards),t.maxCardsft(s)-ft(l)||a.localeCompare(o)).slice(0,r.length-e).map(([a])=>a);for(let a of i)delete n[a];return i}function z(n){let t=ce(Object.assign({},m,n||{})),e=Le(t.backend),r=v(t),i=A(t),a=e?(t.apiBaseUrl||r.baseUrl||k[i]?.defaultBaseUrl||"").trim().replace(/\/+$/,""):"";return V(Me({cacheSchemaVersion:G,promptVersion:Yt,maxDocChars:Number(t.maxDocChars)||m.maxDocChars,promptLanguage:t.promptLanguage,minCards:t.minCards,maxCards:t.maxCards,customSystemPromptHash:V(t.customSystemPrompt||""),backend:t.backend,model:t.model,apiProvider:e?t.apiProvider:"",apiFormat:e?i:"",apiBaseUrl:a,apiAuthType:e?Oe(t):"",apiHeadersHash:e?V(t.apiHeaders||""):"",apiMaxTokens:e?Number(t.apiMaxTokens)||m.apiMaxTokens:0,structuredOutputVersion:1}))}function j(n,t,e){return!!n&&n.schemaVersion===G&&n.contentHash===V(t)&&n.settingsHash===z(e)}function de(n){return(n||"").trim().split("/").map(t=>t.trim()).filter(t=>t&&t!=="."&&t!=="..").join("/")}function _e(n){return(n||"").split("/").map(t=>t.trim()).some(t=>t==="."||t==="..")}function Ve(n,t){let e=de(n);return _e(n)?{valid:!1,reason:"unsafe",folderPath:e}:e&&!t(e)?{valid:!1,reason:"missing",folderPath:e}:{valid:!0,reason:"ok",folderPath:e}}function Zt(n,t){return t?n.parent?.path===t:!n.path.includes("/")}function Ge(n,t){let e=de(t);return n.filter(r=>Zt(r,e))}function Ue(n,t){return{current:n+1,total:t}}function je(n){return{total:n,processed:0,skipped:0,errors:0}}function He(n){return{...n,processed:n.processed+1}}function Je(n){return{...n,processed:n.processed+1,skipped:n.skipped+1}}function $e(n){return{...n,processed:n.processed+1,errors:n.errors+1}}function Ke(n,t,e){return!!n&&j(n,t,e)}function qe(){return{cancelled:!1,currentPath:""}}function ze(n,t){return n.currentPath=t,n}function pe(n){return!n||n.cancelled?!1:(n.cancelled=!0,!0)}function vt(n,t,e){return new Promise(r=>{let i=new Ct.Modal(n);i.onOpen=()=>{i.contentEl.createEl("p",{text:t});let s=i.contentEl.createDiv({cls:"parallel-reader-modal-field"}).createEl("input",{type:"text"});s.placeholder=e,s.addEventListener("keydown",o=>{o.key==="Enter"&&(r(s.value.trim()),i.close())}),i.contentEl.createEl("button",{text:"OK"}).addEventListener("click",()=>{r(s.value.trim()),i.close()})},i.onClose=()=>r(null),i.open()})}function he(n,t){return n?{...n,lastAccessedAt:t||new Date().toISOString()}:null}function ge(n){return JSON.stringify({version:1,entries:n||{}})}function We(n,t){return!!t&&!!n&&typeof n.updatedAt=="string"&&n.updatedAt.trim().length>0}var W=class{constructor(t,e,r,i){this.adapter=t;this.configDir=e;this.pluginId=r;this.getSettings=i;this.cache={};this._timer=null;this._dirty=!1}filePath(){return`${this.configDir}/plugins/${this.pluginId}/cache.json`}async ensureDir(){let t=`${this.configDir}/plugins/${this.pluginId}`;try{if(typeof this.adapter.exists=="function"&&await this.adapter.exists(t))return;await this.adapter.mkdir(t)}catch{}}async readFile(){try{let t=await this.adapter.read(this.filePath()),e=JSON.parse(t);if(e&&typeof e=="object"&&e.entries&&typeof e.entries=="object")return e.entries}catch(t){let e=t instanceof Error?t.message:String(t);/not found|does not exist|ENOENT/i.test(e)||console.warn("[parallel-reader] failed to read cache.json",t)}return{}}async writeFile(){await this.ensureDir(),await this.adapter.write(this.filePath(),ge(this.cache))}async load(){this.cache=await this.readFile(),this.prune().length>0&&await this.writeFile()}prune(){let t=this.getSettings();return ue(this.cache,t?.maxCacheEntries||U)}async pruneIfNeeded(){let t=this.prune();return t.length>0&&await this.save(),t}async save(){this._timer&&(clearTimeout(this._timer),this._timer=null),this.prune(),await this.writeFile(),this._dirty=!1}scheduleSave(t=5e3){this._dirty=!0,!this._timer&&(this._timer=setTimeout(()=>{this._timer=null,this._dirty&&this.save().catch(e=>console.error("[parallel-reader] failed to save cache",e))},t))}async flush(){this._timer&&(clearTimeout(this._timer),this._timer=null),this._dirty&&await this.save()}get(t){return this.cache[t]||null}touch(t){let e=he(this.cache[t]||null);return e?(this.cache[t]=e,this.scheduleSave(),e):null}async move(t,e){if(typeof t!="string"||typeof e!="string"||!t.trim()||!e.trim())return!1;if(t===e)return Object.hasOwn(this.cache,t);let r=this.cache[t];if(!r||Object.hasOwn(this.cache,e))return!1;let i={...this.cache,[e]:r};return delete i[t],this.cache=i,await this.save(),!0}async put(t,e,r,i){let a=new Date().toISOString();this.cache[t]={schemaVersion:G,contentHash:V(e),settingsHash:z(i||this.getSettings()),cards:r,generatedAt:a,lastAccessedAt:a},await this.save()}async replaceCards(t,e){let r=this.cache[t];if(!r)return!1;let i=new Date().toISOString();return this.cache[t]={...r,cards:(e||[]).map(a=>({title:a.title,anchor:a.anchor,gist:a.gist,bullets:a.bullets||[]})),updatedAt:i,lastAccessedAt:i},await this.save(),!0}async delete(t){this.cache[t]&&(delete this.cache[t],await this.save())}async clear(){this.cache={},await this.save()}};function me(n,t){let e=Array.isArray(n)?n.slice():[];return!Number.isInteger(t)||t<0||t>=e.length||e.splice(t,1),e}function fe(n,t,e){if(!Number.isInteger(n)||!Number.isInteger(t)||t<=0||!Number.isInteger(e)||e<0||n<0||n>=t)return e;let r=t-1;return r<=0?-1:n=r.length||(r[t]=Object.assign({},r[t],e||{})),r}function Ye(n,t){let e=(t||[]).map(r=>({title:r.title,level:2,anchor:r.anchor,gist:r.gist,startLine:_(n,r.anchor),bullets:r.bullets||[]}));return e.sort((r,i)=>r.startLine<0&&i.startLine<0?0:r.startLine<0?1:i.startLine<0?-1:r.startLine-i.startLine),e}var St=require("child_process"),kt=oe(require("fs")),we=oe(require("os")),w=oe(require("path"));var H=class extends Error{constructor(t){super("\u8BE5\u7B14\u8BB0\u6B63\u5728\u751F\u6210\u5BF9\u7167\u7B14\u8BB0"),this.name="GenerationJobAlreadyRunningError",this.code="already-running",this.key=t}},T=class extends Error{constructor(t){super("\u751F\u6210\u5DF2\u53D6\u6D88"),this.name="GenerationJobCancelledError",this.code="cancelled",this.key=t}},Xe=class{constructor(t){this.key=t,this.phase="queued",this.cancelled=!1,this.startedAt=new Date().toISOString(),this.updatedAt=this.startedAt,this._cancelHandlers=[]}setPhase(t){this.phase=t,this.updatedAt=new Date().toISOString()}onCancel(t){if(typeof t=="function"){if(this.cancelled){t();return}this._cancelHandlers.push(t)}}cancel(){if(this.cancelled)return!1;this.cancelled=!0,this.setPhase("cancelled");for(let t of this._cancelHandlers.splice(0))try{t()}catch(e){console.warn("[parallel-reader] cancel handler error",e)}return!0}throwIfCancelled(){if(this.cancelled)throw new T(this.key)}},Y=class{constructor(){this.jobs=new Map}get(t){return this.jobs.get(t)||null}isRunning(t){return this.jobs.has(t)}async start(t,e){if(this.jobs.has(t))throw new H(t);let r=new Xe(t);this.jobs.set(t,r);try{r.setPhase("running");let i=await e(r);return r.throwIfCancelled(),i}catch(i){throw r.cancelled&&!(i instanceof T)?new T(t):i}finally{this.jobs.delete(t)}}cancel(t){let e=this.jobs.get(t);return e?e.cancel():!1}};function Ze(n){if(n instanceof T)return"cancelled";let t=n;if(t?.code==="cancelled")return"cancelled";let e=String(t?.message||n);return/api key|unauthorized|401|403|认证|权限/i.test(e)?"auth":/timeout|超时|timed out/i.test(e)?"timeout":/429|rate limit|too many requests/i.test(e)?"rate-limit":/非 JSON|json_schema|schema|structured/i.test(e)?"schema":/model 未设置|base url|配置|config/i.test(e)?"config":"unknown"}var X="record_parallel_reader_cards";function be(n){let t=[],e=-1,r=0,i=!1,a=!1;for(let s=0;s0&&r--,r===0&&e>=0&&(t.push(n.slice(e,s+1)),e=-1))}return t}function Qe(n){let t=(n||"").trim();if(!t)return t;try{return JSON.parse(t),t}catch{}let e=t.match(/```(?:json)?\s*([\s\S]*?)```/);if(e){let i=e[1].trim();try{return JSON.parse(i),i}catch{}}let r=be(t);r.sort((i,a)=>a.length-i.length);for(let i of r)try{return JSON.parse(i),i}catch{}return t}function et(n){let t=n.match(/\{\s*"cards"\s*:\s*\[/);if(!t||t.index===void 0)return null;let e=t.index+t[0].length,r=be(n.slice(e)),i=[];for(let a of r)try{JSON.parse(a),i.push(a)}catch{}return i.length===0?null:'{"cards":['+i.join(",")+"]}"}function P(n,t){let e=Qe(n),r;try{r=JSON.parse(e)}catch{let a=et(n);if(a)try{return r=JSON.parse(a),console.warn("[parallel-reader] LLM output was truncated; salvaged",r.cards?.length??0,"complete cards. Consider increasing max tokens."),J(r)}catch{}throw console.warn("[parallel-reader] LLM returned non-JSON. Raw response:",n),new Error(b(t||null,"errorLlmNonJson",{excerpt:(n||"").slice(0,500)}))}return J(r)}function J(n){let t=n;return(t&&Array.isArray(t.cards)?t.cards:[]).filter(r=>!!r&&typeof r=="object").map(r=>({title:typeof r.title=="string"?r.title:"(\u65E0\u6807\u9898)",anchor:typeof r.anchor=="string"?r.anchor:"",gist:typeof r.gist=="string"?r.gist:"",bullets:Array.isArray(r.bullets)?r.bullets.filter(i=>typeof i=="string"):[]}))}function Z(n){let t={type:"object",properties:{title:{type:"string"},anchor:{type:"string"},gist:{type:"string"},bullets:{type:"array",items:{type:"string"}}},required:["title","anchor","gist","bullets"]},e={type:"object",properties:{cards:{type:"array",items:t}},required:["cards"]};return n&&(t.additionalProperties=!1,e.additionalProperties=!1),e}function xt(){return{type:"json_schema",json_schema:{name:"parallel_reader_cards",strict:!0,schema:Z(!0)}}}function At(){return{format:{type:"json_schema",name:"parallel_reader_cards",strict:!0,schema:Z(!0)}}}function Pt(){return{name:X,description:"Return the generated Parallel Reader summary cards.",input_schema:Z(!0)}}function L(n,t){if(t?.trim())return t.trim();let e=we.default.homedir(),r=process.platform==="win32",i=r?[".cmd",".exe",""]:[""],a=r?[w.default.join(e,"AppData","Roaming","npm"),w.default.join(e,"AppData","Local","Programs","claude-code"),w.default.join(e,"AppData","Local","Programs","codex"),w.default.join(e,".claude","local"),w.default.join(e,".codex","bin"),w.default.join(e,".bun","bin"),w.default.join(e,"scoop","shims"),"C:\\Program Files\\nodejs"]:[w.default.join(e,"bin"),w.default.join(e,".local","bin"),w.default.join(e,".claude","local"),w.default.join(e,".codex","bin"),w.default.join(e,".bun","bin"),w.default.join(e,".npm-global","bin"),w.default.join(e,".cargo","bin"),"/opt/homebrew/bin","/usr/local/bin"];for(let s of a)for(let o of i){let l=w.default.join(s,n+o);try{if(kt.default.existsSync(l))return l}catch{}}return n}function O(n,t,e,r,i,a=St.spawn){return new Promise((s,o)=>{let l,c=!1,u=null,d=y=>{c||(c=!0,u&&clearTimeout(u),o(y))},f=y=>{c||(c=!0,u&&clearTimeout(u),s(y))};try{l=a(n,t,{stdio:["pipe","pipe","pipe"],env:{...process.env,PATH:[process.env.PATH||"","/usr/local/bin","/opt/homebrew/bin",w.default.join(we.default.homedir(),".local/bin"),w.default.join(we.default.homedir(),".claude/local")].filter(Boolean).join(":")}})}catch(y){return o(new Error(`Failed to start ${n}: ${y.message}`))}let g="",C="";if(u=setTimeout(()=>{try{l.kill("SIGKILL")}catch(y){console.warn("[parallel-reader] failed to kill timed-out CLI process",y)}d(new Error(`CLI timed out (${r}ms)`))},r),i&&i.onCancel(()=>{try{l.kill("SIGKILL")}catch(y){console.warn("[parallel-reader] failed to kill cancelled CLI process",y)}d(new T(i.key))}),!l.stdout||!l.stderr||!l.stdin){d(new Error("CLI process streams are unavailable"));return}let S=l.stdout,ae=l.stderr,Re=l.stdin;if(S.on("data",y=>{g+=y.toString("utf8")}),ae.on("data",y=>{C+=y.toString("utf8")}),l.on("error",y=>{d(new Error(`CLI startup error: ${y.message}. Try setting an absolute CLI path.`))}),l.on("close",y=>{if(!c){if(y!==0)return d(new Error(`CLI exited with code ${y} stderr: ${C.slice(0,1e3)}`));f({stdout:g,stderr:C})}}),e)try{Re.write(e),Re.end()}catch{}else try{Re.end()}catch(y){console.warn("[parallel-reader] failed to close CLI stdin",y)}})}async function Tt(n,t,e,r){let i=L("claude",e.cliPath),a=Number(e.apiMaxTokens)||4096,s=["-p","--output-format","json","--max-tokens",String(a),"--append-system-prompt",n,"--disallowed-tools","Bash,Read,Write,Edit,Glob,Grep,WebFetch,WebSearch,TodoWrite,Task"],{stdout:o}=await O(i,s,t,e.cliTimeoutMs,r),l="";try{let c=JSON.parse(o);if(Array.isArray(c)){let u=c.find(d=>d.type==="result");u&&typeof u.result=="string"&&(l=u.result)}else c&&typeof c=="object"&&(l=c.result||c.content||"")}catch{throw new Error(`claude CLI returned unexpected output: `+o.slice(0,500))}if(!l)throw console.warn("[parallel-reader] claude CLI returned no result. Full stdout:",o),new Error(`claude CLI returned no result. Output: diff --git a/src/provider-parsers.ts b/src/provider-parsers.ts index ff856e0..b8a0225 100644 --- a/src/provider-parsers.ts +++ b/src/provider-parsers.ts @@ -55,8 +55,7 @@ export function cardsFromAnthropicToolUse( const block = content.find((c) => c && c.type === 'tool_use' && c.name === ANTHROPIC_CARD_TOOL_NAME); if (!block) return null; if (typeof block.input === 'string') return parseCardsJson(block.input, settings); - if (block.input && typeof block.input === 'object') - return normalizeCardsPayload(block.input as Record); + if (block.input && typeof block.input === 'object') return normalizeCardsPayload(block.input); return []; } diff --git a/src/schema.ts b/src/schema.ts index 09742c8..8b236c0 100644 --- a/src/schema.ts +++ b/src/schema.ts @@ -104,7 +104,7 @@ export function repairTruncatedCardsJson(text: string): string | null { export function parseCardsJson(text: string, settings?: PluginSettings | null): RawCard[] { const jsonText = extractJson(text); - let parsed; + let parsed: unknown; try { parsed = JSON.parse(jsonText); } catch (_e) { @@ -133,8 +133,9 @@ export function parseCardsJson(text: string, settings?: PluginSettings | null): return normalizeCardsPayload(parsed); } -export function normalizeCardsPayload(parsed: { cards?: unknown[] }): RawCard[] { - const raw = parsed && Array.isArray(parsed.cards) ? parsed.cards : []; +export function normalizeCardsPayload(parsed: unknown): RawCard[] { + const obj = parsed as { cards?: unknown[] } | null | undefined; + const raw = obj && Array.isArray(obj.cards) ? obj.cards : []; return raw .filter((c): c is Record => !!c && typeof c === 'object') .map((c) => ({