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) => ({