@steipete/summarize 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +44 -1
- package/README.md +63 -17
- package/dist/cli.js +1 -1
- package/dist/esm/cache-keys.js +75 -0
- package/dist/esm/cache-keys.js.map +1 -0
- package/dist/esm/cache-slides-cleanup.js +47 -0
- package/dist/esm/cache-slides-cleanup.js.map +1 -0
- package/dist/esm/cache.js +14 -91
- package/dist/esm/cache.js.map +1 -1
- package/dist/esm/config/env.js +49 -0
- package/dist/esm/config/env.js.map +1 -0
- package/dist/esm/config/model.js +193 -0
- package/dist/esm/config/model.js.map +1 -0
- package/dist/esm/config/parse-helpers.js +50 -0
- package/dist/esm/config/parse-helpers.js.map +1 -0
- package/dist/esm/config/read.js +83 -0
- package/dist/esm/config/read.js.map +1 -0
- package/dist/esm/config/sections.js +438 -0
- package/dist/esm/config/sections.js.map +1 -0
- package/dist/esm/config/types.js +2 -0
- package/dist/esm/config/types.js.map +1 -0
- package/dist/esm/config.js +24 -807
- package/dist/esm/config.js.map +1 -1
- package/dist/esm/content/asset.js +2 -2
- package/dist/esm/content/asset.js.map +1 -1
- package/dist/esm/daemon/agent-model.js +235 -0
- package/dist/esm/daemon/agent-model.js.map +1 -0
- package/dist/esm/daemon/agent-request.js +87 -0
- package/dist/esm/daemon/agent-request.js.map +1 -0
- package/dist/esm/daemon/agent.js +42 -243
- package/dist/esm/daemon/agent.js.map +1 -1
- package/dist/esm/daemon/chat.js +69 -8
- package/dist/esm/daemon/chat.js.map +1 -1
- package/dist/esm/daemon/cli.js +21 -4
- package/dist/esm/daemon/cli.js.map +1 -1
- package/dist/esm/daemon/config.js +65 -9
- package/dist/esm/daemon/config.js.map +1 -1
- package/dist/esm/daemon/env-snapshot.js +4 -0
- package/dist/esm/daemon/env-snapshot.js.map +1 -1
- package/dist/esm/daemon/flow-context.js +8 -1
- package/dist/esm/daemon/flow-context.js.map +1 -1
- package/dist/esm/daemon/models.js +16 -0
- package/dist/esm/daemon/models.js.map +1 -1
- package/dist/esm/daemon/process-registry.js.map +1 -1
- package/dist/esm/daemon/server-admin-routes.js +134 -0
- package/dist/esm/daemon/server-admin-routes.js.map +1 -0
- package/dist/esm/daemon/server-agent-route.js +104 -0
- package/dist/esm/daemon/server-agent-route.js.map +1 -0
- package/dist/esm/daemon/server-http.js +89 -0
- package/dist/esm/daemon/server-http.js.map +1 -0
- package/dist/esm/daemon/server-session-routes.js +209 -0
- package/dist/esm/daemon/server-session-routes.js.map +1 -0
- package/dist/esm/daemon/server-session.js +118 -0
- package/dist/esm/daemon/server-session.js.map +1 -0
- package/dist/esm/daemon/server-sse.js +28 -0
- package/dist/esm/daemon/server-sse.js.map +1 -0
- package/dist/esm/daemon/server-summarize-execution.js +357 -0
- package/dist/esm/daemon/server-summarize-execution.js.map +1 -0
- package/dist/esm/daemon/server-summarize-request.js +119 -0
- package/dist/esm/daemon/server-summarize-request.js.map +1 -0
- package/dist/esm/daemon/server.js +72 -1121
- package/dist/esm/daemon/server.js.map +1 -1
- package/dist/esm/daemon/summarize-progress.js +1 -1
- package/dist/esm/daemon/summarize-progress.js.map +1 -1
- package/dist/esm/daemon/summarize.js.map +1 -1
- package/dist/esm/llm/cli-exec.js +75 -0
- package/dist/esm/llm/cli-exec.js.map +1 -0
- package/dist/esm/llm/cli-provider-output.js +191 -0
- package/dist/esm/llm/cli-provider-output.js.map +1 -0
- package/dist/esm/llm/cli.js +3 -212
- package/dist/esm/llm/cli.js.map +1 -1
- package/dist/esm/llm/generate-text-document.js +109 -0
- package/dist/esm/llm/generate-text-document.js.map +1 -0
- package/dist/esm/llm/generate-text-shared.js +102 -0
- package/dist/esm/llm/generate-text-shared.js.map +1 -0
- package/dist/esm/llm/generate-text-stream.js +258 -0
- package/dist/esm/llm/generate-text-stream.js.map +1 -0
- package/dist/esm/llm/generate-text.js +145 -480
- package/dist/esm/llm/generate-text.js.map +1 -1
- package/dist/esm/llm/model-id.js +21 -20
- package/dist/esm/llm/model-id.js.map +1 -1
- package/dist/esm/llm/provider-capabilities.js +2 -0
- package/dist/esm/llm/provider-capabilities.js.map +1 -0
- package/dist/esm/llm/provider-profile.js +142 -0
- package/dist/esm/llm/provider-profile.js.map +1 -0
- package/dist/esm/llm/providers/google.js +42 -5
- package/dist/esm/llm/providers/google.js.map +1 -1
- package/dist/esm/llm/providers/models.js +13 -0
- package/dist/esm/llm/providers/models.js.map +1 -1
- package/dist/esm/llm/providers/openai.js.map +1 -1
- package/dist/esm/llm/transcript-to-markdown.js.map +1 -1
- package/dist/esm/model-auto-cli.js +89 -0
- package/dist/esm/model-auto-cli.js.map +1 -0
- package/dist/esm/model-auto-rules.js +86 -0
- package/dist/esm/model-auto-rules.js.map +1 -0
- package/dist/esm/model-auto.js +10 -245
- package/dist/esm/model-auto.js.map +1 -1
- package/dist/esm/model-spec.js +23 -17
- package/dist/esm/model-spec.js.map +1 -1
- package/dist/esm/refresh-free.js +1 -1
- package/dist/esm/refresh-free.js.map +1 -1
- package/dist/esm/run/attachments.js +1 -1
- package/dist/esm/run/attachments.js.map +1 -1
- package/dist/esm/run/bird/exec.js +23 -0
- package/dist/esm/run/bird/exec.js.map +1 -0
- package/dist/esm/run/bird/media.js +171 -0
- package/dist/esm/run/bird/media.js.map +1 -0
- package/dist/esm/run/bird/parse.js +82 -0
- package/dist/esm/run/bird/parse.js.map +1 -0
- package/dist/esm/run/bird/types.js +2 -0
- package/dist/esm/run/bird/types.js.map +1 -0
- package/dist/esm/run/bird.js +86 -144
- package/dist/esm/run/bird.js.map +1 -1
- package/dist/esm/run/cache-state.js.map +1 -1
- package/dist/esm/run/constants.js +2 -1
- package/dist/esm/run/constants.js.map +1 -1
- package/dist/esm/run/env.js +3 -0
- package/dist/esm/run/env.js.map +1 -1
- package/dist/esm/run/finish-line-labels.js +76 -0
- package/dist/esm/run/finish-line-labels.js.map +1 -0
- package/dist/esm/run/finish-line-lengths.js +96 -0
- package/dist/esm/run/finish-line-lengths.js.map +1 -0
- package/dist/esm/run/finish-line.js +3 -169
- package/dist/esm/run/finish-line.js.map +1 -1
- package/dist/esm/run/flows/asset/extract.js.map +1 -1
- package/dist/esm/run/flows/asset/input.js +1 -1
- package/dist/esm/run/flows/asset/input.js.map +1 -1
- package/dist/esm/run/flows/asset/media.js +19 -10
- package/dist/esm/run/flows/asset/media.js.map +1 -1
- package/dist/esm/run/flows/asset/output.js.map +1 -1
- package/dist/esm/run/flows/asset/preprocess.js.map +1 -1
- package/dist/esm/run/flows/asset/summary-attempts.js +109 -0
- package/dist/esm/run/flows/asset/summary-attempts.js.map +1 -0
- package/dist/esm/run/flows/asset/summary.js +19 -107
- package/dist/esm/run/flows/asset/summary.js.map +1 -1
- package/dist/esm/run/flows/url/extract.js +7 -4
- package/dist/esm/run/flows/url/extract.js.map +1 -1
- package/dist/esm/run/flows/url/flow-progress.js +119 -0
- package/dist/esm/run/flows/url/flow-progress.js.map +1 -0
- package/dist/esm/run/flows/url/flow.js +22 -93
- package/dist/esm/run/flows/url/flow.js.map +1 -1
- package/dist/esm/run/flows/url/markdown.js +21 -3
- package/dist/esm/run/flows/url/markdown.js.map +1 -1
- package/dist/esm/run/flows/url/progress-status.js +56 -0
- package/dist/esm/run/flows/url/progress-status.js.map +1 -0
- package/dist/esm/run/flows/url/slides-output-render.js +78 -0
- package/dist/esm/run/flows/url/slides-output-render.js.map +1 -0
- package/dist/esm/run/flows/url/slides-output-state.js +86 -0
- package/dist/esm/run/flows/url/slides-output-state.js.map +1 -0
- package/dist/esm/run/flows/url/slides-output-stream.js +271 -0
- package/dist/esm/run/flows/url/slides-output-stream.js.map +1 -0
- package/dist/esm/run/flows/url/slides-output.js +29 -422
- package/dist/esm/run/flows/url/slides-output.js.map +1 -1
- package/dist/esm/run/flows/url/slides-text-markdown.js +431 -0
- package/dist/esm/run/flows/url/slides-text-markdown.js.map +1 -0
- package/dist/esm/run/flows/url/slides-text-transcript.js +199 -0
- package/dist/esm/run/flows/url/slides-text-transcript.js.map +1 -0
- package/dist/esm/run/flows/url/slides-text-types.js +2 -0
- package/dist/esm/run/flows/url/slides-text-types.js.map +1 -0
- package/dist/esm/run/flows/url/slides-text.js +2 -627
- package/dist/esm/run/flows/url/slides-text.js.map +1 -1
- package/dist/esm/run/flows/url/summary-finish.js +34 -0
- package/dist/esm/run/flows/url/summary-finish.js.map +1 -0
- package/dist/esm/run/flows/url/summary-json.js +32 -0
- package/dist/esm/run/flows/url/summary-json.js.map +1 -0
- package/dist/esm/run/flows/url/summary-prompt.js +147 -0
- package/dist/esm/run/flows/url/summary-prompt.js.map +1 -0
- package/dist/esm/run/flows/url/summary-resolution.js +320 -0
- package/dist/esm/run/flows/url/summary-resolution.js.map +1 -0
- package/dist/esm/run/flows/url/summary-timestamps.js +136 -0
- package/dist/esm/run/flows/url/summary-timestamps.js.map +1 -0
- package/dist/esm/run/flows/url/summary.js +49 -543
- package/dist/esm/run/flows/url/summary.js.map +1 -1
- package/dist/esm/run/help.js +9 -3
- package/dist/esm/run/help.js.map +1 -1
- package/dist/esm/run/markdown-transforms.js +89 -0
- package/dist/esm/run/markdown-transforms.js.map +1 -0
- package/dist/esm/run/markdown.js +1 -96
- package/dist/esm/run/markdown.js.map +1 -1
- package/dist/esm/run/run-env.js +28 -7
- package/dist/esm/run/run-env.js.map +1 -1
- package/dist/esm/run/run-settings-parse.js +73 -0
- package/dist/esm/run/run-settings-parse.js.map +1 -0
- package/dist/esm/run/run-settings.js +1 -72
- package/dist/esm/run/run-settings.js.map +1 -1
- package/dist/esm/run/runner-contexts.js +116 -0
- package/dist/esm/run/runner-contexts.js.map +1 -0
- package/dist/esm/run/runner-execution.js +62 -0
- package/dist/esm/run/runner-execution.js.map +1 -0
- package/dist/esm/run/runner-flags.js +97 -0
- package/dist/esm/run/runner-flags.js.map +1 -0
- package/dist/esm/run/runner-setup.js +109 -0
- package/dist/esm/run/runner-setup.js.map +1 -0
- package/dist/esm/run/runner-slides.js +38 -0
- package/dist/esm/run/runner-slides.js.map +1 -0
- package/dist/esm/run/runner.js +99 -390
- package/dist/esm/run/runner.js.map +1 -1
- package/dist/esm/run/slides-render.js +5 -2
- package/dist/esm/run/slides-render.js.map +1 -1
- package/dist/esm/run/stdin-temp-file.js +1 -1
- package/dist/esm/run/stdin-temp-file.js.map +1 -1
- package/dist/esm/run/streaming.js +1 -0
- package/dist/esm/run/streaming.js.map +1 -1
- package/dist/esm/run/summary-engine.js +26 -10
- package/dist/esm/run/summary-engine.js.map +1 -1
- package/dist/esm/run/summary-llm.js +2 -1
- package/dist/esm/run/summary-llm.js.map +1 -1
- package/dist/esm/run/terminal.js +4 -1
- package/dist/esm/run/terminal.js.map +1 -1
- package/dist/esm/run/transcriber-cli.js +1 -1
- package/dist/esm/run/transcriber-cli.js.map +1 -1
- package/dist/esm/slides/download.js +242 -0
- package/dist/esm/slides/download.js.map +1 -0
- package/dist/esm/slides/extract-finalize.js +98 -0
- package/dist/esm/slides/extract-finalize.js.map +1 -0
- package/dist/esm/slides/extract.js +64 -1621
- package/dist/esm/slides/extract.js.map +1 -1
- package/dist/esm/slides/frame-extraction.js +372 -0
- package/dist/esm/slides/frame-extraction.js.map +1 -0
- package/dist/esm/slides/ingest.js +167 -0
- package/dist/esm/slides/ingest.js.map +1 -0
- package/dist/esm/slides/ocr.js +91 -0
- package/dist/esm/slides/ocr.js.map +1 -0
- package/dist/esm/slides/process.js +218 -0
- package/dist/esm/slides/process.js.map +1 -0
- package/dist/esm/slides/scene-detection.js +387 -0
- package/dist/esm/slides/scene-detection.js.map +1 -0
- package/dist/esm/slides/source-id.js +42 -0
- package/dist/esm/slides/source-id.js.map +1 -0
- package/dist/esm/tty/progress/fetch-html.js.map +1 -1
- package/dist/esm/tty/progress/transcript.js +21 -8
- package/dist/esm/tty/progress/transcript.js.map +1 -1
- package/dist/esm/tty/spinner.js +8 -2
- package/dist/esm/tty/spinner.js.map +1 -1
- package/dist/esm/tty/website-progress.js +5 -3
- package/dist/esm/tty/website-progress.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/types/cache-keys.d.ts +44 -0
- package/dist/types/cache-slides-cleanup.d.ts +1 -0
- package/dist/types/cache.d.ts +1 -9
- package/dist/types/config/env.d.ts +6 -0
- package/dist/types/config/model.d.ts +3 -0
- package/dist/types/config/parse-helpers.d.ts +7 -0
- package/dist/types/config/read.d.ts +2 -0
- package/dist/types/config/sections.d.ts +33 -0
- package/dist/types/config/types.d.ts +230 -0
- package/dist/types/config.d.ts +3 -209
- package/dist/types/costs.d.ts +1 -1
- package/dist/types/daemon/agent-model.d.ts +40 -0
- package/dist/types/daemon/agent-request.d.ts +14 -0
- package/dist/types/daemon/chat.d.ts +3 -1
- package/dist/types/daemon/config.d.ts +13 -2
- package/dist/types/daemon/env-snapshot.d.ts +1 -1
- package/dist/types/daemon/flow-context.d.ts +1 -1
- package/dist/types/daemon/models.d.ts +1 -0
- package/dist/types/daemon/server-admin-routes.d.ts +22 -0
- package/dist/types/daemon/server-agent-route.d.ts +9 -0
- package/dist/types/daemon/server-http.d.ts +10 -0
- package/dist/types/daemon/server-session-routes.d.ts +11 -0
- package/dist/types/daemon/server-session.d.ts +52 -0
- package/dist/types/daemon/server-sse.d.ts +12 -0
- package/dist/types/daemon/server-summarize-execution.d.ts +70 -0
- package/dist/types/daemon/server-summarize-request.d.ts +36 -0
- package/dist/types/daemon/server.d.ts +3 -4
- package/dist/types/daemon/summarize.d.ts +1 -1
- package/dist/types/llm/cli-exec.d.ts +13 -0
- package/dist/types/llm/cli-provider-output.d.ts +16 -0
- package/dist/types/llm/generate-text-document.d.ts +34 -0
- package/dist/types/llm/generate-text-shared.d.ts +25 -0
- package/dist/types/llm/generate-text-stream.d.ts +26 -0
- package/dist/types/llm/generate-text.d.ts +6 -26
- package/dist/types/llm/html-to-markdown.d.ts +1 -1
- package/dist/types/llm/model-id.d.ts +1 -1
- package/dist/types/llm/provider-capabilities.d.ts +2 -0
- package/dist/types/llm/provider-profile.d.ts +31 -0
- package/dist/types/llm/providers/google.d.ts +6 -0
- package/dist/types/llm/providers/models.d.ts +5 -0
- package/dist/types/llm/transcript-to-markdown.d.ts +1 -1
- package/dist/types/model-auto-cli.d.ts +15 -0
- package/dist/types/model-auto-rules.d.ts +7 -0
- package/dist/types/model-auto.d.ts +5 -7
- package/dist/types/model-spec.d.ts +2 -2
- package/dist/types/run/attachments.d.ts +2 -2
- package/dist/types/run/bird/exec.d.ts +1 -0
- package/dist/types/run/bird/media.d.ts +3 -0
- package/dist/types/run/bird/parse.d.ts +3 -0
- package/dist/types/run/bird/types.d.ts +18 -0
- package/dist/types/run/bird.d.ts +12 -17
- package/dist/types/run/cache-state.d.ts +1 -1
- package/dist/types/run/constants.d.ts +2 -1
- package/dist/types/run/env.d.ts +1 -0
- package/dist/types/run/finish-line-labels.d.ts +29 -0
- package/dist/types/run/finish-line-lengths.d.ts +23 -0
- package/dist/types/run/finish-line.d.ts +2 -52
- package/dist/types/run/flows/asset/extract.d.ts +1 -1
- package/dist/types/run/flows/asset/input.d.ts +1 -1
- package/dist/types/run/flows/asset/preprocess.d.ts +1 -1
- package/dist/types/run/flows/asset/summary-attempts.d.ts +24 -0
- package/dist/types/run/flows/asset/summary.d.ts +6 -2
- package/dist/types/run/flows/url/flow-progress.d.ts +41 -0
- package/dist/types/run/flows/url/markdown.d.ts +2 -2
- package/dist/types/run/flows/url/progress-status.d.ts +16 -0
- package/dist/types/run/flows/url/slides-output-render.d.ts +43 -0
- package/dist/types/run/flows/url/slides-output-state.d.ts +21 -0
- package/dist/types/run/flows/url/slides-output-stream.d.ts +18 -0
- package/dist/types/run/flows/url/slides-output.d.ts +2 -17
- package/dist/types/run/flows/url/slides-text-markdown.d.ts +46 -0
- package/dist/types/run/flows/url/slides-text-transcript.d.ts +36 -0
- package/dist/types/run/flows/url/slides-text-types.d.ts +8 -0
- package/dist/types/run/flows/url/slides-text.d.ts +3 -87
- package/dist/types/run/flows/url/summary-finish.d.ts +16 -0
- package/dist/types/run/flows/url/summary-json.d.ts +51 -0
- package/dist/types/run/flows/url/summary-prompt.d.ts +22 -0
- package/dist/types/run/flows/url/summary-resolution.d.ts +31 -0
- package/dist/types/run/flows/url/summary-timestamps.d.ts +11 -0
- package/dist/types/run/flows/url/types.d.ts +4 -0
- package/dist/types/run/markdown-transforms.d.ts +3 -0
- package/dist/types/run/run-context.d.ts +4 -0
- package/dist/types/run/run-env.d.ts +4 -0
- package/dist/types/run/run-settings-parse.d.ts +5 -0
- package/dist/types/run/runner-contexts.d.ts +62 -0
- package/dist/types/run/runner-execution.d.ts +57 -0
- package/dist/types/run/runner-flags.d.ts +41 -0
- package/dist/types/run/runner-setup.d.ts +21 -0
- package/dist/types/run/runner-slides.d.ts +8 -0
- package/dist/types/run/streaming.d.ts +1 -1
- package/dist/types/run/summary-engine.d.ts +8 -4
- package/dist/types/run/summary-llm.d.ts +4 -3
- package/dist/types/run/terminal.d.ts +2 -0
- package/dist/types/run/types.d.ts +2 -2
- package/dist/types/slides/download.d.ts +29 -0
- package/dist/types/slides/extract-finalize.d.ts +57 -0
- package/dist/types/slides/extract.d.ts +1 -7
- package/dist/types/slides/frame-extraction.d.ts +38 -0
- package/dist/types/slides/ingest.d.ts +47 -0
- package/dist/types/slides/ocr.d.ts +5 -0
- package/dist/types/slides/process.d.ts +22 -0
- package/dist/types/slides/scene-detection.d.ts +75 -0
- package/dist/types/slides/source-id.d.ts +2 -0
- package/dist/types/version.d.ts +1 -1
- package/docs/_config.yml +1 -0
- package/docs/agent.md +3 -2
- package/docs/assets/site.css +134 -2
- package/docs/cache.md +2 -1
- package/docs/chrome-extension.md +12 -4
- package/docs/cli.md +2 -2
- package/docs/config.md +11 -4
- package/docs/index.html +5 -0
- package/docs/llm.md +5 -2
- package/docs/manual-tests.md +3 -0
- package/docs/media.md +3 -1
- package/docs/model-auto.md +2 -2
- package/docs/model-provider-resolution.md +57 -0
- package/docs/site/index.html +5 -0
- package/docs/slides-rendering-flow.md +46 -0
- package/docs/slides.md +5 -5
- package/docs/smoketest.md +1 -1
- package/docs/transcript-provider-flow.md +66 -0
- package/docs/website.md +1 -0
- package/docs/youtube.md +4 -2
- package/package.json +34 -41
package/dist/esm/config.js
CHANGED
|
@@ -1,403 +1,15 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
function isRecord(value) {
|
|
6
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
|
-
}
|
|
8
|
-
function parseOptionalBaseUrl(raw) {
|
|
9
|
-
return typeof raw === "string" && raw.trim().length > 0 ? raw.trim() : undefined;
|
|
10
|
-
}
|
|
11
|
-
function resolveLegacyApiKeysEnv(apiKeys) {
|
|
12
|
-
if (!apiKeys)
|
|
13
|
-
return {};
|
|
14
|
-
const mapped = {};
|
|
15
|
-
if (typeof apiKeys.openai === "string")
|
|
16
|
-
mapped.OPENAI_API_KEY = apiKeys.openai;
|
|
17
|
-
if (typeof apiKeys.anthropic === "string")
|
|
18
|
-
mapped.ANTHROPIC_API_KEY = apiKeys.anthropic;
|
|
19
|
-
if (typeof apiKeys.google === "string")
|
|
20
|
-
mapped.GEMINI_API_KEY = apiKeys.google;
|
|
21
|
-
if (typeof apiKeys.xai === "string")
|
|
22
|
-
mapped.XAI_API_KEY = apiKeys.xai;
|
|
23
|
-
if (typeof apiKeys.openrouter === "string")
|
|
24
|
-
mapped.OPENROUTER_API_KEY = apiKeys.openrouter;
|
|
25
|
-
if (typeof apiKeys.zai === "string")
|
|
26
|
-
mapped.Z_AI_API_KEY = apiKeys.zai;
|
|
27
|
-
if (typeof apiKeys.apify === "string")
|
|
28
|
-
mapped.APIFY_API_TOKEN = apiKeys.apify;
|
|
29
|
-
if (typeof apiKeys.firecrawl === "string")
|
|
30
|
-
mapped.FIRECRAWL_API_KEY = apiKeys.firecrawl;
|
|
31
|
-
if (typeof apiKeys.fal === "string")
|
|
32
|
-
mapped.FAL_KEY = apiKeys.fal;
|
|
33
|
-
return mapped;
|
|
34
|
-
}
|
|
35
|
-
export function resolveConfigEnv(config) {
|
|
36
|
-
if (!config)
|
|
37
|
-
return {};
|
|
38
|
-
return {
|
|
39
|
-
...resolveLegacyApiKeysEnv(config.apiKeys),
|
|
40
|
-
...(config.env ?? {}),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
export function mergeConfigEnv({ env, config, }) {
|
|
44
|
-
const configEnv = resolveConfigEnv(config);
|
|
45
|
-
if (Object.keys(configEnv).length === 0)
|
|
46
|
-
return env;
|
|
47
|
-
let changed = false;
|
|
48
|
-
const merged = { ...env };
|
|
49
|
-
for (const [key, value] of Object.entries(configEnv)) {
|
|
50
|
-
const current = merged[key];
|
|
51
|
-
if (typeof current === "string" && current.trim().length > 0)
|
|
52
|
-
continue;
|
|
53
|
-
merged[key] = value;
|
|
54
|
-
changed = true;
|
|
55
|
-
}
|
|
56
|
-
return changed ? merged : env;
|
|
57
|
-
}
|
|
58
|
-
function parseProviderBaseUrlConfig(raw, path, providerName) {
|
|
59
|
-
if (typeof raw === "undefined")
|
|
60
|
-
return undefined;
|
|
61
|
-
if (!isRecord(raw)) {
|
|
62
|
-
throw new Error(`Invalid config file ${path}: "${providerName}" must be an object.`);
|
|
63
|
-
}
|
|
64
|
-
const baseUrl = parseOptionalBaseUrl(raw.baseUrl);
|
|
65
|
-
return typeof baseUrl === "string" ? { baseUrl } : undefined;
|
|
66
|
-
}
|
|
67
|
-
function parseAutoRuleKind(value) {
|
|
68
|
-
return value === "text" ||
|
|
69
|
-
value === "website" ||
|
|
70
|
-
value === "youtube" ||
|
|
71
|
-
value === "image" ||
|
|
72
|
-
value === "video" ||
|
|
73
|
-
value === "file"
|
|
74
|
-
? value
|
|
75
|
-
: null;
|
|
76
|
-
}
|
|
77
|
-
function parseCliProvider(value, path) {
|
|
78
|
-
const trimmed = typeof value === "string" ? value.trim().toLowerCase() : "";
|
|
79
|
-
if (trimmed === "claude" || trimmed === "codex" || trimmed === "gemini" || trimmed === "agent") {
|
|
80
|
-
return trimmed;
|
|
81
|
-
}
|
|
82
|
-
throw new Error(`Invalid config file ${path}: unknown CLI provider "${String(value)}".`);
|
|
83
|
-
}
|
|
84
|
-
function parseStringArray(raw, path, label) {
|
|
85
|
-
if (!Array.isArray(raw)) {
|
|
86
|
-
throw new Error(`Invalid config file ${path}: "${label}" must be an array of strings.`);
|
|
87
|
-
}
|
|
88
|
-
const items = [];
|
|
89
|
-
for (const entry of raw) {
|
|
90
|
-
if (typeof entry !== "string") {
|
|
91
|
-
throw new Error(`Invalid config file ${path}: "${label}" must be an array of strings.`);
|
|
92
|
-
}
|
|
93
|
-
const trimmed = entry.trim();
|
|
94
|
-
if (!trimmed)
|
|
95
|
-
continue;
|
|
96
|
-
items.push(trimmed);
|
|
97
|
-
}
|
|
98
|
-
return items;
|
|
99
|
-
}
|
|
100
|
-
function parseLoggingLevel(raw, path) {
|
|
101
|
-
if (typeof raw !== "string") {
|
|
102
|
-
throw new Error(`Invalid config file ${path}: "logging.level" must be a string.`);
|
|
103
|
-
}
|
|
104
|
-
const trimmed = raw.trim().toLowerCase();
|
|
105
|
-
if (trimmed === "debug" || trimmed === "info" || trimmed === "warn" || trimmed === "error") {
|
|
106
|
-
return trimmed;
|
|
107
|
-
}
|
|
108
|
-
throw new Error(`Invalid config file ${path}: "logging.level" must be one of "debug", "info", "warn", "error".`);
|
|
109
|
-
}
|
|
110
|
-
function parseLoggingFormat(raw, path) {
|
|
111
|
-
if (typeof raw !== "string") {
|
|
112
|
-
throw new Error(`Invalid config file ${path}: "logging.format" must be a string.`);
|
|
113
|
-
}
|
|
114
|
-
const trimmed = raw.trim().toLowerCase();
|
|
115
|
-
if (trimmed === "json" || trimmed === "pretty") {
|
|
116
|
-
return trimmed;
|
|
117
|
-
}
|
|
118
|
-
throw new Error(`Invalid config file ${path}: "logging.format" must be one of "json" or "pretty".`);
|
|
119
|
-
}
|
|
120
|
-
function parseCliProviderList(raw, path, label) {
|
|
121
|
-
if (!Array.isArray(raw)) {
|
|
122
|
-
throw new Error(`Invalid config file ${path}: "${label}" must be an array.`);
|
|
123
|
-
}
|
|
124
|
-
const providers = [];
|
|
125
|
-
for (const entry of raw) {
|
|
126
|
-
const parsed = parseCliProvider(entry, path);
|
|
127
|
-
if (!providers.includes(parsed))
|
|
128
|
-
providers.push(parsed);
|
|
129
|
-
}
|
|
130
|
-
return providers.length > 0 ? providers : undefined;
|
|
131
|
-
}
|
|
132
|
-
function parseCliProviderConfig(raw, path, label) {
|
|
133
|
-
if (!isRecord(raw)) {
|
|
134
|
-
throw new Error(`Invalid config file ${path}: "cli.${label}" must be an object.`);
|
|
135
|
-
}
|
|
136
|
-
if (typeof raw.enabled !== "undefined") {
|
|
137
|
-
throw new Error(`Invalid config file ${path}: "cli.${label}.enabled" is not supported. Use "cli.enabled" instead.`);
|
|
138
|
-
}
|
|
139
|
-
const binaryValue = typeof raw.binary === "string" ? raw.binary.trim() : undefined;
|
|
140
|
-
const modelValue = typeof raw.model === "string" ? raw.model.trim() : undefined;
|
|
141
|
-
const extraArgs = typeof raw.extraArgs === "undefined"
|
|
142
|
-
? undefined
|
|
143
|
-
: parseStringArray(raw.extraArgs, path, `cli.${label}.extraArgs`);
|
|
144
|
-
return {
|
|
145
|
-
...(binaryValue ? { binary: binaryValue } : {}),
|
|
146
|
-
...(modelValue ? { model: modelValue } : {}),
|
|
147
|
-
...(extraArgs && extraArgs.length > 0 ? { extraArgs } : {}),
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
function parseCliAutoFallbackConfig(raw, path, label) {
|
|
151
|
-
if (!isRecord(raw)) {
|
|
152
|
-
throw new Error(`Invalid config file ${path}: "cli.${label}" must be an object.`);
|
|
153
|
-
}
|
|
154
|
-
const enabled = typeof raw.enabled === "boolean"
|
|
155
|
-
? raw.enabled
|
|
156
|
-
: typeof raw.enabled === "undefined"
|
|
157
|
-
? undefined
|
|
158
|
-
: (() => {
|
|
159
|
-
throw new Error(`Invalid config file ${path}: "cli.${label}.enabled" must be a boolean.`);
|
|
160
|
-
})();
|
|
161
|
-
const onlyWhenNoApiKeys = typeof raw.onlyWhenNoApiKeys === "boolean"
|
|
162
|
-
? raw.onlyWhenNoApiKeys
|
|
163
|
-
: typeof raw.onlyWhenNoApiKeys === "undefined"
|
|
164
|
-
? undefined
|
|
165
|
-
: (() => {
|
|
166
|
-
throw new Error(`Invalid config file ${path}: "cli.${label}.onlyWhenNoApiKeys" must be a boolean.`);
|
|
167
|
-
})();
|
|
168
|
-
const order = typeof raw.order === "undefined"
|
|
169
|
-
? undefined
|
|
170
|
-
: parseCliProviderList(raw.order, path, `cli.${label}.order`);
|
|
171
|
-
return {
|
|
172
|
-
...(typeof enabled === "boolean" ? { enabled } : {}),
|
|
173
|
-
...(typeof onlyWhenNoApiKeys === "boolean" ? { onlyWhenNoApiKeys } : {}),
|
|
174
|
-
...(Array.isArray(order) && order.length > 0 ? { order } : {}),
|
|
175
|
-
};
|
|
176
|
-
}
|
|
177
|
-
function parseWhenKinds(raw, path) {
|
|
178
|
-
if (!Array.isArray(raw)) {
|
|
179
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].when" must be an array of kinds.`);
|
|
180
|
-
}
|
|
181
|
-
if (raw.length === 0) {
|
|
182
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].when" must not be empty.`);
|
|
183
|
-
}
|
|
184
|
-
const kinds = [];
|
|
185
|
-
for (const entry of raw) {
|
|
186
|
-
const kind = parseAutoRuleKind(entry);
|
|
187
|
-
if (!kind) {
|
|
188
|
-
throw new Error(`Invalid config file ${path}: unknown "when" kind "${String(entry)}".`);
|
|
189
|
-
}
|
|
190
|
-
if (!kinds.includes(kind))
|
|
191
|
-
kinds.push(kind);
|
|
192
|
-
}
|
|
193
|
-
return kinds;
|
|
194
|
-
}
|
|
195
|
-
function parseModelCandidates(raw, path) {
|
|
196
|
-
if (!Array.isArray(raw)) {
|
|
197
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].candidates" must be an array of strings.`);
|
|
198
|
-
}
|
|
199
|
-
const candidates = [];
|
|
200
|
-
for (const entry of raw) {
|
|
201
|
-
if (typeof entry !== "string") {
|
|
202
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].candidates" must be an array of strings.`);
|
|
203
|
-
}
|
|
204
|
-
const trimmed = entry.trim();
|
|
205
|
-
if (trimmed.length === 0)
|
|
206
|
-
continue;
|
|
207
|
-
candidates.push(trimmed);
|
|
208
|
-
}
|
|
209
|
-
if (candidates.length === 0) {
|
|
210
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].candidates" must not be empty.`);
|
|
211
|
-
}
|
|
212
|
-
return candidates;
|
|
213
|
-
}
|
|
214
|
-
function parseTokenBand(raw, path) {
|
|
215
|
-
if (!isRecord(raw)) {
|
|
216
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].bands[]" must be an object.`);
|
|
217
|
-
}
|
|
218
|
-
const candidates = parseModelCandidates(raw.candidates, path);
|
|
219
|
-
const token = (() => {
|
|
220
|
-
if (typeof raw.token === "undefined")
|
|
221
|
-
return undefined;
|
|
222
|
-
if (!isRecord(raw.token)) {
|
|
223
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].bands[].token" must be an object.`);
|
|
224
|
-
}
|
|
225
|
-
const min = typeof raw.token.min === "number" ? raw.token.min : undefined;
|
|
226
|
-
const max = typeof raw.token.max === "number" ? raw.token.max : undefined;
|
|
227
|
-
if (typeof min === "number" && (!Number.isFinite(min) || min < 0)) {
|
|
228
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].bands[].token.min" must be >= 0.`);
|
|
229
|
-
}
|
|
230
|
-
if (typeof max === "number" && (!Number.isFinite(max) || max < 0)) {
|
|
231
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].bands[].token.max" must be >= 0.`);
|
|
232
|
-
}
|
|
233
|
-
if (typeof min === "number" && typeof max === "number" && min > max) {
|
|
234
|
-
throw new Error(`Invalid config file ${path}: "model.rules[].bands[].token.min" must be <= "token.max".`);
|
|
235
|
-
}
|
|
236
|
-
return typeof min === "number" || typeof max === "number" ? { min, max } : undefined;
|
|
237
|
-
})();
|
|
238
|
-
return { ...(token ? { token } : {}), candidates };
|
|
239
|
-
}
|
|
240
|
-
function assertNoComments(raw, path) {
|
|
241
|
-
let inString = null;
|
|
242
|
-
let escaped = false;
|
|
243
|
-
let line = 1;
|
|
244
|
-
let col = 1;
|
|
245
|
-
for (let i = 0; i < raw.length; i += 1) {
|
|
246
|
-
const ch = raw[i] ?? "";
|
|
247
|
-
const next = raw[i + 1] ?? "";
|
|
248
|
-
if (inString) {
|
|
249
|
-
if (escaped) {
|
|
250
|
-
escaped = false;
|
|
251
|
-
col += 1;
|
|
252
|
-
continue;
|
|
253
|
-
}
|
|
254
|
-
if (ch === "\\") {
|
|
255
|
-
escaped = true;
|
|
256
|
-
col += 1;
|
|
257
|
-
continue;
|
|
258
|
-
}
|
|
259
|
-
if (ch === inString) {
|
|
260
|
-
inString = null;
|
|
261
|
-
}
|
|
262
|
-
if (ch === "\n") {
|
|
263
|
-
line += 1;
|
|
264
|
-
col = 1;
|
|
265
|
-
}
|
|
266
|
-
else {
|
|
267
|
-
col += 1;
|
|
268
|
-
}
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
if (ch === '"' || ch === "'") {
|
|
272
|
-
inString = ch;
|
|
273
|
-
escaped = false;
|
|
274
|
-
col += 1;
|
|
275
|
-
continue;
|
|
276
|
-
}
|
|
277
|
-
if (ch === "/" && next === "/") {
|
|
278
|
-
throw new Error(`Invalid config file ${path}: comments are not allowed (found // at ${line}:${col}).`);
|
|
279
|
-
}
|
|
280
|
-
if (ch === "/" && next === "*") {
|
|
281
|
-
throw new Error(`Invalid config file ${path}: comments are not allowed (found /* at ${line}:${col}).`);
|
|
282
|
-
}
|
|
283
|
-
if (ch === "\n") {
|
|
284
|
-
line += 1;
|
|
285
|
-
col = 1;
|
|
286
|
-
}
|
|
287
|
-
else {
|
|
288
|
-
col += 1;
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
1
|
+
import { parseModelConfig, parseModelsConfig } from "./config/model.js";
|
|
2
|
+
import { readParsedConfigFile, resolveSummarizeConfigPath } from "./config/read.js";
|
|
3
|
+
import { parseApiKeysConfig, parseCacheConfig, parseCliConfig, parseEnvConfig, parseLoggingConfig, parseMediaConfig, parseOpenAiConfig, parseOutputConfig, parseProviderBaseUrlConfig, parseSlidesConfig, parseUiConfig, } from "./config/sections.js";
|
|
4
|
+
export { mergeConfigEnv, resolveConfigEnv } from "./config/env.js";
|
|
292
5
|
export function loadSummarizeConfig({ env }) {
|
|
293
|
-
const
|
|
294
|
-
if (!
|
|
6
|
+
const path = resolveSummarizeConfigPath(env);
|
|
7
|
+
if (!path)
|
|
295
8
|
return { config: null, path: null };
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
try {
|
|
299
|
-
raw = readFileSync(path, "utf8");
|
|
300
|
-
}
|
|
301
|
-
catch {
|
|
9
|
+
const parsed = readParsedConfigFile(path);
|
|
10
|
+
if (!parsed)
|
|
302
11
|
return { config: null, path };
|
|
303
|
-
|
|
304
|
-
let parsed;
|
|
305
|
-
assertNoComments(raw, path);
|
|
306
|
-
try {
|
|
307
|
-
parsed = JSON5.parse(raw);
|
|
308
|
-
}
|
|
309
|
-
catch (error) {
|
|
310
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
311
|
-
throw new Error(`Invalid JSON in config file ${path}: ${message}`);
|
|
312
|
-
}
|
|
313
|
-
if (!isRecord(parsed)) {
|
|
314
|
-
throw new Error(`Invalid config file ${path}: expected an object at the top level`);
|
|
315
|
-
}
|
|
316
|
-
const parseModelConfig = (raw, label) => {
|
|
317
|
-
if (typeof raw === "undefined")
|
|
318
|
-
return undefined;
|
|
319
|
-
// Shorthand:
|
|
320
|
-
// - "auto" -> { mode: "auto" }
|
|
321
|
-
// - "<provider>/<model>" or "openrouter/<provider>/<model>" -> { id: "..." }
|
|
322
|
-
// - "<name>" -> { name: "<name>" }
|
|
323
|
-
if (typeof raw === "string") {
|
|
324
|
-
const value = raw.trim();
|
|
325
|
-
if (value.length === 0) {
|
|
326
|
-
throw new Error(`Invalid config file ${path}: "${label}" must not be empty.`);
|
|
327
|
-
}
|
|
328
|
-
if (value.toLowerCase() === "auto") {
|
|
329
|
-
return { mode: "auto" };
|
|
330
|
-
}
|
|
331
|
-
if (value.includes("/")) {
|
|
332
|
-
return { id: value };
|
|
333
|
-
}
|
|
334
|
-
return { name: value };
|
|
335
|
-
}
|
|
336
|
-
if (!isRecord(raw)) {
|
|
337
|
-
throw new Error(`Invalid config file ${path}: "${label}" must be an object.`);
|
|
338
|
-
}
|
|
339
|
-
if (typeof raw.name === "string") {
|
|
340
|
-
const name = raw.name.trim();
|
|
341
|
-
if (name.length === 0) {
|
|
342
|
-
throw new Error(`Invalid config file ${path}: "${label}.name" must not be empty.`);
|
|
343
|
-
}
|
|
344
|
-
if (name.toLowerCase() === "auto") {
|
|
345
|
-
throw new Error(`Invalid config file ${path}: "${label}.name" must not be "auto".`);
|
|
346
|
-
}
|
|
347
|
-
return { name };
|
|
348
|
-
}
|
|
349
|
-
if (typeof raw.id === "string") {
|
|
350
|
-
const id = raw.id.trim();
|
|
351
|
-
if (id.length === 0) {
|
|
352
|
-
throw new Error(`Invalid config file ${path}: "${label}.id" must not be empty.`);
|
|
353
|
-
}
|
|
354
|
-
if (!id.includes("/")) {
|
|
355
|
-
throw new Error(`Invalid config file ${path}: "${label}.id" must be provider-prefixed (e.g. "openai/gpt-5-mini").`);
|
|
356
|
-
}
|
|
357
|
-
return { id };
|
|
358
|
-
}
|
|
359
|
-
const hasRules = typeof raw.rules !== "undefined";
|
|
360
|
-
if (raw.mode === "auto" || (!("mode" in raw) && hasRules)) {
|
|
361
|
-
const rules = (() => {
|
|
362
|
-
if (typeof raw.rules === "undefined")
|
|
363
|
-
return undefined;
|
|
364
|
-
if (!Array.isArray(raw.rules)) {
|
|
365
|
-
throw new Error(`Invalid config file ${path}: "${label}.rules" must be an array.`);
|
|
366
|
-
}
|
|
367
|
-
const rulesParsed = [];
|
|
368
|
-
for (const entry of raw.rules) {
|
|
369
|
-
if (!isRecord(entry))
|
|
370
|
-
continue;
|
|
371
|
-
const when = typeof entry.when === "undefined" ? undefined : parseWhenKinds(entry.when, path);
|
|
372
|
-
const hasCandidates = typeof entry.candidates !== "undefined";
|
|
373
|
-
const hasBands = typeof entry.bands !== "undefined";
|
|
374
|
-
if (hasCandidates && hasBands) {
|
|
375
|
-
throw new Error(`Invalid config file ${path}: "${label}.rules[]" must use either "candidates" or "bands" (not both).`);
|
|
376
|
-
}
|
|
377
|
-
if (hasCandidates) {
|
|
378
|
-
const candidates = parseModelCandidates(entry.candidates, path);
|
|
379
|
-
rulesParsed.push({ ...(when ? { when } : {}), candidates });
|
|
380
|
-
continue;
|
|
381
|
-
}
|
|
382
|
-
if (hasBands) {
|
|
383
|
-
if (!Array.isArray(entry.bands) || entry.bands.length === 0) {
|
|
384
|
-
throw new Error(`Invalid config file ${path}: "${label}.rules[].bands" must be a non-empty array.`);
|
|
385
|
-
}
|
|
386
|
-
const bands = entry.bands.map((b) => parseTokenBand(b, path));
|
|
387
|
-
rulesParsed.push({ ...(when ? { when } : {}), bands });
|
|
388
|
-
continue;
|
|
389
|
-
}
|
|
390
|
-
throw new Error(`Invalid config file ${path}: "${label}.rules[]" must include "candidates" or "bands".`);
|
|
391
|
-
}
|
|
392
|
-
return rulesParsed;
|
|
393
|
-
})();
|
|
394
|
-
return { mode: "auto", ...(rules ? { rules } : {}) };
|
|
395
|
-
}
|
|
396
|
-
throw new Error(`Invalid config file ${path}: "${label}" must include either "id", "name", or { "mode": "auto" }.`);
|
|
397
|
-
};
|
|
398
|
-
const model = (() => {
|
|
399
|
-
return parseModelConfig(parsed.model, "model");
|
|
400
|
-
})();
|
|
12
|
+
const model = parseModelConfig(parsed.model, path, "model");
|
|
401
13
|
const language = (() => {
|
|
402
14
|
const value = parsed.language;
|
|
403
15
|
if (typeof value === "undefined")
|
|
@@ -424,419 +36,22 @@ export function loadSummarizeConfig({ env }) {
|
|
|
424
36
|
}
|
|
425
37
|
return trimmed;
|
|
426
38
|
})();
|
|
427
|
-
const models = (
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
}
|
|
438
|
-
const out = {};
|
|
439
|
-
const seen = new Set();
|
|
440
|
-
for (const [keyRaw, value] of Object.entries(raw)) {
|
|
441
|
-
const key = keyRaw.trim();
|
|
442
|
-
if (!key)
|
|
443
|
-
continue;
|
|
444
|
-
const keyLower = key.toLowerCase();
|
|
445
|
-
if (keyLower === "auto") {
|
|
446
|
-
throw new Error(`Invalid config file ${path}: model name "auto" is reserved.`);
|
|
447
|
-
}
|
|
448
|
-
if (seen.has(keyLower)) {
|
|
449
|
-
throw new Error(`Invalid config file ${path}: duplicate model name "${key}".`);
|
|
450
|
-
}
|
|
451
|
-
if (/\s/.test(key)) {
|
|
452
|
-
throw new Error(`Invalid config file ${path}: model name "${key}" must not contain spaces.`);
|
|
453
|
-
}
|
|
454
|
-
if (key.includes("/")) {
|
|
455
|
-
throw new Error(`Invalid config file ${path}: model name "${key}" must not include "/".`);
|
|
456
|
-
}
|
|
457
|
-
const parsedModel = parseModelConfig(value, `models.${key}`);
|
|
458
|
-
if (!parsedModel)
|
|
459
|
-
continue;
|
|
460
|
-
if ("name" in parsedModel) {
|
|
461
|
-
throw new Error(`Invalid config file ${path}: "models.${key}" must not reference another model.`);
|
|
462
|
-
}
|
|
463
|
-
seen.add(keyLower);
|
|
464
|
-
out[key] = parsedModel;
|
|
465
|
-
}
|
|
466
|
-
return Object.keys(out).length > 0 ? out : undefined;
|
|
467
|
-
})();
|
|
468
|
-
const cache = (() => {
|
|
469
|
-
const value = parsed.cache;
|
|
470
|
-
if (typeof value === "undefined")
|
|
471
|
-
return undefined;
|
|
472
|
-
if (!isRecord(value)) {
|
|
473
|
-
throw new Error(`Invalid config file ${path}: "cache" must be an object.`);
|
|
474
|
-
}
|
|
475
|
-
const enabled = typeof value.enabled === "boolean" ? value.enabled : undefined;
|
|
476
|
-
const maxMbRaw = value.maxMb;
|
|
477
|
-
const maxMb = typeof maxMbRaw === "number" && Number.isFinite(maxMbRaw) && maxMbRaw > 0
|
|
478
|
-
? maxMbRaw
|
|
479
|
-
: typeof maxMbRaw === "undefined"
|
|
480
|
-
? undefined
|
|
481
|
-
: (() => {
|
|
482
|
-
throw new Error(`Invalid config file ${path}: "cache.maxMb" must be a number.`);
|
|
483
|
-
})();
|
|
484
|
-
const ttlDaysRaw = value.ttlDays;
|
|
485
|
-
const ttlDays = typeof ttlDaysRaw === "number" && Number.isFinite(ttlDaysRaw) && ttlDaysRaw > 0
|
|
486
|
-
? ttlDaysRaw
|
|
487
|
-
: typeof ttlDaysRaw === "undefined"
|
|
488
|
-
? undefined
|
|
489
|
-
: (() => {
|
|
490
|
-
throw new Error(`Invalid config file ${path}: "cache.ttlDays" must be a number.`);
|
|
491
|
-
})();
|
|
492
|
-
const pathValue = typeof value.path === "string" && value.path.trim().length > 0
|
|
493
|
-
? value.path.trim()
|
|
494
|
-
: typeof value.path === "undefined"
|
|
495
|
-
? undefined
|
|
496
|
-
: (() => {
|
|
497
|
-
throw new Error(`Invalid config file ${path}: "cache.path" must be a string.`);
|
|
498
|
-
})();
|
|
499
|
-
const media = (() => {
|
|
500
|
-
const mediaValue = value.media;
|
|
501
|
-
if (typeof mediaValue === "undefined")
|
|
502
|
-
return undefined;
|
|
503
|
-
if (!isRecord(mediaValue)) {
|
|
504
|
-
throw new Error(`Invalid config file ${path}: "cache.media" must be an object.`);
|
|
505
|
-
}
|
|
506
|
-
const mediaEnabled = typeof mediaValue.enabled === "boolean" ? mediaValue.enabled : undefined;
|
|
507
|
-
const mediaMaxRaw = mediaValue.maxMb;
|
|
508
|
-
const mediaMaxMb = typeof mediaMaxRaw === "number" && Number.isFinite(mediaMaxRaw) && mediaMaxRaw > 0
|
|
509
|
-
? mediaMaxRaw
|
|
510
|
-
: typeof mediaMaxRaw === "undefined"
|
|
511
|
-
? undefined
|
|
512
|
-
: (() => {
|
|
513
|
-
throw new Error(`Invalid config file ${path}: "cache.media.maxMb" must be a number.`);
|
|
514
|
-
})();
|
|
515
|
-
const mediaTtlRaw = mediaValue.ttlDays;
|
|
516
|
-
const mediaTtlDays = typeof mediaTtlRaw === "number" && Number.isFinite(mediaTtlRaw) && mediaTtlRaw > 0
|
|
517
|
-
? mediaTtlRaw
|
|
518
|
-
: typeof mediaTtlRaw === "undefined"
|
|
519
|
-
? undefined
|
|
520
|
-
: (() => {
|
|
521
|
-
throw new Error(`Invalid config file ${path}: "cache.media.ttlDays" must be a number.`);
|
|
522
|
-
})();
|
|
523
|
-
const mediaPath = typeof mediaValue.path === "string" && mediaValue.path.trim().length > 0
|
|
524
|
-
? mediaValue.path.trim()
|
|
525
|
-
: typeof mediaValue.path === "undefined"
|
|
526
|
-
? undefined
|
|
527
|
-
: (() => {
|
|
528
|
-
throw new Error(`Invalid config file ${path}: "cache.media.path" must be a string.`);
|
|
529
|
-
})();
|
|
530
|
-
const verifyRaw = typeof mediaValue.verify === "string" ? mediaValue.verify.trim().toLowerCase() : "";
|
|
531
|
-
const verify = verifyRaw === "none" || verifyRaw === "size" || verifyRaw === "hash"
|
|
532
|
-
? verifyRaw
|
|
533
|
-
: verifyRaw.length > 0
|
|
534
|
-
? (() => {
|
|
535
|
-
throw new Error(`Invalid config file ${path}: "cache.media.verify" must be one of "none", "size", "hash".`);
|
|
536
|
-
})()
|
|
537
|
-
: undefined;
|
|
538
|
-
return mediaEnabled || mediaMaxMb || mediaTtlDays || mediaPath || typeof verify === "string"
|
|
539
|
-
? {
|
|
540
|
-
...(typeof mediaEnabled === "boolean" ? { enabled: mediaEnabled } : {}),
|
|
541
|
-
...(typeof mediaMaxMb === "number" ? { maxMb: mediaMaxMb } : {}),
|
|
542
|
-
...(typeof mediaTtlDays === "number" ? { ttlDays: mediaTtlDays } : {}),
|
|
543
|
-
...(typeof mediaPath === "string" ? { path: mediaPath } : {}),
|
|
544
|
-
...(typeof verify === "string" ? { verify } : {}),
|
|
545
|
-
}
|
|
546
|
-
: undefined;
|
|
547
|
-
})();
|
|
548
|
-
return enabled || maxMb || ttlDays || pathValue || media
|
|
549
|
-
? {
|
|
550
|
-
...(typeof enabled === "boolean" ? { enabled } : {}),
|
|
551
|
-
...(typeof maxMb === "number" ? { maxMb } : {}),
|
|
552
|
-
...(typeof ttlDays === "number" ? { ttlDays } : {}),
|
|
553
|
-
...(typeof pathValue === "string" ? { path: pathValue } : {}),
|
|
554
|
-
...(media ? { media } : {}),
|
|
555
|
-
}
|
|
556
|
-
: undefined;
|
|
557
|
-
})();
|
|
558
|
-
const media = (() => {
|
|
559
|
-
const value = parsed.media;
|
|
560
|
-
if (!isRecord(value))
|
|
561
|
-
return undefined;
|
|
562
|
-
const videoMode = value.videoMode === "auto" ||
|
|
563
|
-
value.videoMode === "transcript" ||
|
|
564
|
-
value.videoMode === "understand"
|
|
565
|
-
? value.videoMode
|
|
566
|
-
: undefined;
|
|
567
|
-
return videoMode ? { videoMode } : undefined;
|
|
568
|
-
})();
|
|
569
|
-
const slides = (() => {
|
|
570
|
-
const value = parsed.slides;
|
|
571
|
-
if (typeof value === "undefined")
|
|
572
|
-
return undefined;
|
|
573
|
-
if (!isRecord(value)) {
|
|
574
|
-
throw new Error(`Invalid config file ${path}: "slides" must be an object.`);
|
|
575
|
-
}
|
|
576
|
-
const enabled = typeof value.enabled === "boolean" ? value.enabled : undefined;
|
|
577
|
-
const ocr = typeof value.ocr === "boolean" ? value.ocr : undefined;
|
|
578
|
-
const dir = typeof value.dir === "string" && value.dir.trim().length > 0
|
|
579
|
-
? value.dir.trim()
|
|
580
|
-
: typeof value.dir === "undefined"
|
|
581
|
-
? undefined
|
|
582
|
-
: (() => {
|
|
583
|
-
throw new Error(`Invalid config file ${path}: "slides.dir" must be a string.`);
|
|
584
|
-
})();
|
|
585
|
-
const sceneRaw = value.sceneThreshold;
|
|
586
|
-
const sceneThreshold = typeof sceneRaw === "number" && Number.isFinite(sceneRaw) && sceneRaw >= 0.1 && sceneRaw <= 1
|
|
587
|
-
? sceneRaw
|
|
588
|
-
: typeof sceneRaw === "undefined"
|
|
589
|
-
? undefined
|
|
590
|
-
: (() => {
|
|
591
|
-
throw new Error(`Invalid config file ${path}: "slides.sceneThreshold" must be a number between 0.1 and 1.0.`);
|
|
592
|
-
})();
|
|
593
|
-
const maxRaw = value.max;
|
|
594
|
-
const max = typeof maxRaw === "number" &&
|
|
595
|
-
Number.isFinite(maxRaw) &&
|
|
596
|
-
Number.isInteger(maxRaw) &&
|
|
597
|
-
maxRaw > 0
|
|
598
|
-
? maxRaw
|
|
599
|
-
: typeof maxRaw === "undefined"
|
|
600
|
-
? undefined
|
|
601
|
-
: (() => {
|
|
602
|
-
throw new Error(`Invalid config file ${path}: "slides.max" must be an integer.`);
|
|
603
|
-
})();
|
|
604
|
-
const minRaw = value.minDuration;
|
|
605
|
-
const minDuration = typeof minRaw === "number" && Number.isFinite(minRaw) && minRaw >= 0
|
|
606
|
-
? minRaw
|
|
607
|
-
: typeof minRaw === "undefined"
|
|
608
|
-
? undefined
|
|
609
|
-
: (() => {
|
|
610
|
-
throw new Error(`Invalid config file ${path}: "slides.minDuration" must be a number.`);
|
|
611
|
-
})();
|
|
612
|
-
return enabled ||
|
|
613
|
-
typeof ocr === "boolean" ||
|
|
614
|
-
dir ||
|
|
615
|
-
typeof sceneThreshold === "number" ||
|
|
616
|
-
typeof max === "number" ||
|
|
617
|
-
typeof minDuration === "number"
|
|
618
|
-
? {
|
|
619
|
-
...(typeof enabled === "boolean" ? { enabled } : {}),
|
|
620
|
-
...(typeof ocr === "boolean" ? { ocr } : {}),
|
|
621
|
-
...(typeof dir === "string" ? { dir } : {}),
|
|
622
|
-
...(typeof sceneThreshold === "number" ? { sceneThreshold } : {}),
|
|
623
|
-
...(typeof max === "number" ? { max } : {}),
|
|
624
|
-
...(typeof minDuration === "number" ? { minDuration } : {}),
|
|
625
|
-
}
|
|
626
|
-
: undefined;
|
|
627
|
-
})();
|
|
628
|
-
const cli = (() => {
|
|
629
|
-
const value = parsed.cli;
|
|
630
|
-
if (!isRecord(value))
|
|
631
|
-
return undefined;
|
|
632
|
-
if (typeof value.disabled !== "undefined") {
|
|
633
|
-
throw new Error(`Invalid config file ${path}: "cli.disabled" is not supported. Use "cli.enabled" instead.`);
|
|
634
|
-
}
|
|
635
|
-
const enabled = typeof value.enabled !== "undefined"
|
|
636
|
-
? parseCliProviderList(value.enabled, path, "cli.enabled")
|
|
637
|
-
: undefined;
|
|
638
|
-
const claude = value.claude ? parseCliProviderConfig(value.claude, path, "claude") : undefined;
|
|
639
|
-
const codex = value.codex ? parseCliProviderConfig(value.codex, path, "codex") : undefined;
|
|
640
|
-
const gemini = value.gemini ? parseCliProviderConfig(value.gemini, path, "gemini") : undefined;
|
|
641
|
-
const agent = value.agent ? parseCliProviderConfig(value.agent, path, "agent") : undefined;
|
|
642
|
-
if (typeof value.autoFallback !== "undefined" && typeof value.magicAuto !== "undefined") {
|
|
643
|
-
throw new Error(`Invalid config file ${path}: use only one of "cli.autoFallback" or legacy "cli.magicAuto".`);
|
|
644
|
-
}
|
|
645
|
-
const autoFallback = (() => {
|
|
646
|
-
if (typeof value.autoFallback !== "undefined") {
|
|
647
|
-
return parseCliAutoFallbackConfig(value.autoFallback, path, "autoFallback");
|
|
648
|
-
}
|
|
649
|
-
if (typeof value.magicAuto !== "undefined") {
|
|
650
|
-
return parseCliAutoFallbackConfig(value.magicAuto, path, "magicAuto");
|
|
651
|
-
}
|
|
652
|
-
return undefined;
|
|
653
|
-
})();
|
|
654
|
-
const promptOverride = typeof value.promptOverride === "string" && value.promptOverride.trim().length > 0
|
|
655
|
-
? value.promptOverride.trim()
|
|
656
|
-
: undefined;
|
|
657
|
-
const allowTools = typeof value.allowTools === "boolean" ? value.allowTools : undefined;
|
|
658
|
-
const cwd = typeof value.cwd === "string" && value.cwd.trim().length > 0 ? value.cwd.trim() : undefined;
|
|
659
|
-
const extraArgs = typeof value.extraArgs === "undefined"
|
|
660
|
-
? undefined
|
|
661
|
-
: parseStringArray(value.extraArgs, path, "cli.extraArgs");
|
|
662
|
-
return enabled ||
|
|
663
|
-
claude ||
|
|
664
|
-
codex ||
|
|
665
|
-
gemini ||
|
|
666
|
-
agent ||
|
|
667
|
-
autoFallback ||
|
|
668
|
-
promptOverride ||
|
|
669
|
-
typeof allowTools === "boolean" ||
|
|
670
|
-
cwd ||
|
|
671
|
-
(extraArgs && extraArgs.length > 0)
|
|
672
|
-
? {
|
|
673
|
-
...(enabled ? { enabled } : {}),
|
|
674
|
-
...(claude ? { claude } : {}),
|
|
675
|
-
...(codex ? { codex } : {}),
|
|
676
|
-
...(gemini ? { gemini } : {}),
|
|
677
|
-
...(agent ? { agent } : {}),
|
|
678
|
-
...(autoFallback ? { autoFallback } : {}),
|
|
679
|
-
...(promptOverride ? { promptOverride } : {}),
|
|
680
|
-
...(typeof allowTools === "boolean" ? { allowTools } : {}),
|
|
681
|
-
...(cwd ? { cwd } : {}),
|
|
682
|
-
...(extraArgs && extraArgs.length > 0 ? { extraArgs } : {}),
|
|
683
|
-
}
|
|
684
|
-
: undefined;
|
|
685
|
-
})();
|
|
686
|
-
const output = (() => {
|
|
687
|
-
const value = parsed.output;
|
|
688
|
-
if (typeof value === "undefined")
|
|
689
|
-
return undefined;
|
|
690
|
-
if (!isRecord(value)) {
|
|
691
|
-
throw new Error(`Invalid config file ${path}: "output" must be an object.`);
|
|
692
|
-
}
|
|
693
|
-
const language = typeof value.language === "string" && value.language.trim().length > 0
|
|
694
|
-
? value.language.trim()
|
|
695
|
-
: undefined;
|
|
696
|
-
return typeof language === "string" ? { language } : undefined;
|
|
697
|
-
})();
|
|
698
|
-
const ui = (() => {
|
|
699
|
-
const value = parsed.ui;
|
|
700
|
-
if (typeof value === "undefined")
|
|
701
|
-
return undefined;
|
|
702
|
-
if (!isRecord(value)) {
|
|
703
|
-
throw new Error(`Invalid config file ${path}: "ui" must be an object.`);
|
|
704
|
-
}
|
|
705
|
-
const themeRaw = typeof value.theme === "string" ? value.theme.trim().toLowerCase() : "";
|
|
706
|
-
if (themeRaw && !isCliThemeName(themeRaw)) {
|
|
707
|
-
throw new Error(`Invalid config file ${path}: "ui.theme" must be one of ${listCliThemes().join(", ")}.`);
|
|
708
|
-
}
|
|
709
|
-
const theme = themeRaw.length > 0 ? themeRaw : undefined;
|
|
710
|
-
return theme ? { theme } : undefined;
|
|
711
|
-
})();
|
|
712
|
-
const logging = (() => {
|
|
713
|
-
const value = parsed.logging;
|
|
714
|
-
if (typeof value === "undefined")
|
|
715
|
-
return undefined;
|
|
716
|
-
if (!isRecord(value)) {
|
|
717
|
-
throw new Error(`Invalid config file ${path}: "logging" must be an object.`);
|
|
718
|
-
}
|
|
719
|
-
const enabled = typeof value.enabled === "boolean" ? value.enabled : undefined;
|
|
720
|
-
const level = typeof value.level === "undefined" ? undefined : parseLoggingLevel(value.level, path);
|
|
721
|
-
const format = typeof value.format === "undefined" ? undefined : parseLoggingFormat(value.format, path);
|
|
722
|
-
const file = typeof value.file === "string" && value.file.trim().length > 0
|
|
723
|
-
? value.file.trim()
|
|
724
|
-
: typeof value.file === "undefined"
|
|
725
|
-
? undefined
|
|
726
|
-
: (() => {
|
|
727
|
-
throw new Error(`Invalid config file ${path}: "logging.file" must be a string.`);
|
|
728
|
-
})();
|
|
729
|
-
const maxMbRaw = value.maxMb;
|
|
730
|
-
const maxMb = typeof maxMbRaw === "number" && Number.isFinite(maxMbRaw) && maxMbRaw > 0
|
|
731
|
-
? maxMbRaw
|
|
732
|
-
: typeof maxMbRaw === "undefined"
|
|
733
|
-
? undefined
|
|
734
|
-
: (() => {
|
|
735
|
-
throw new Error(`Invalid config file ${path}: "logging.maxMb" must be a number.`);
|
|
736
|
-
})();
|
|
737
|
-
const maxFilesRaw = value.maxFiles;
|
|
738
|
-
const maxFiles = typeof maxFilesRaw === "number" && Number.isFinite(maxFilesRaw) && maxFilesRaw > 0
|
|
739
|
-
? Math.trunc(maxFilesRaw)
|
|
740
|
-
: typeof maxFilesRaw === "undefined"
|
|
741
|
-
? undefined
|
|
742
|
-
: (() => {
|
|
743
|
-
throw new Error(`Invalid config file ${path}: "logging.maxFiles" must be a number.`);
|
|
744
|
-
})();
|
|
745
|
-
return enabled ||
|
|
746
|
-
level ||
|
|
747
|
-
format ||
|
|
748
|
-
file ||
|
|
749
|
-
typeof maxMb === "number" ||
|
|
750
|
-
typeof maxFiles === "number"
|
|
751
|
-
? {
|
|
752
|
-
...(typeof enabled === "boolean" ? { enabled } : {}),
|
|
753
|
-
...(level ? { level } : {}),
|
|
754
|
-
...(format ? { format } : {}),
|
|
755
|
-
...(file ? { file } : {}),
|
|
756
|
-
...(typeof maxMb === "number" ? { maxMb } : {}),
|
|
757
|
-
...(typeof maxFiles === "number" ? { maxFiles } : {}),
|
|
758
|
-
}
|
|
759
|
-
: undefined;
|
|
760
|
-
})();
|
|
761
|
-
const openai = (() => {
|
|
762
|
-
const value = parsed.openai;
|
|
763
|
-
if (typeof value === "undefined")
|
|
764
|
-
return undefined;
|
|
765
|
-
if (!isRecord(value)) {
|
|
766
|
-
throw new Error(`Invalid config file ${path}: "openai" must be an object.`);
|
|
767
|
-
}
|
|
768
|
-
const baseUrl = parseOptionalBaseUrl(value.baseUrl);
|
|
769
|
-
const useChatCompletions = typeof value.useChatCompletions === "boolean" ? value.useChatCompletions : undefined;
|
|
770
|
-
const whisperUsdPerMinuteRaw = value.whisperUsdPerMinute;
|
|
771
|
-
const whisperUsdPerMinute = typeof whisperUsdPerMinuteRaw === "number" &&
|
|
772
|
-
Number.isFinite(whisperUsdPerMinuteRaw) &&
|
|
773
|
-
whisperUsdPerMinuteRaw > 0
|
|
774
|
-
? whisperUsdPerMinuteRaw
|
|
775
|
-
: undefined;
|
|
776
|
-
return typeof baseUrl === "string" ||
|
|
777
|
-
typeof useChatCompletions === "boolean" ||
|
|
778
|
-
typeof whisperUsdPerMinute === "number"
|
|
779
|
-
? {
|
|
780
|
-
...(typeof baseUrl === "string" ? { baseUrl } : {}),
|
|
781
|
-
...(typeof useChatCompletions === "boolean" ? { useChatCompletions } : {}),
|
|
782
|
-
...(typeof whisperUsdPerMinute === "number" ? { whisperUsdPerMinute } : {}),
|
|
783
|
-
}
|
|
784
|
-
: undefined;
|
|
785
|
-
})();
|
|
39
|
+
const models = parseModelsConfig(parsed, path);
|
|
40
|
+
const cache = parseCacheConfig(parsed, path);
|
|
41
|
+
const media = parseMediaConfig(parsed);
|
|
42
|
+
const slides = parseSlidesConfig(parsed, path);
|
|
43
|
+
const cli = parseCliConfig(parsed, path);
|
|
44
|
+
const output = parseOutputConfig(parsed, path);
|
|
45
|
+
const ui = parseUiConfig(parsed, path);
|
|
46
|
+
const logging = parseLoggingConfig(parsed, path);
|
|
47
|
+
const openai = parseOpenAiConfig(parsed, path);
|
|
48
|
+
const nvidia = parseProviderBaseUrlConfig(parsed.nvidia, path, "nvidia");
|
|
786
49
|
const anthropic = parseProviderBaseUrlConfig(parsed.anthropic, path, "anthropic");
|
|
787
50
|
const google = parseProviderBaseUrlConfig(parsed.google, path, "google");
|
|
788
51
|
const xai = parseProviderBaseUrlConfig(parsed.xai, path, "xai");
|
|
789
|
-
const
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
return undefined;
|
|
793
|
-
if (!isRecord(value)) {
|
|
794
|
-
throw new Error(`Invalid config file ${path}: "env" must be an object.`);
|
|
795
|
-
}
|
|
796
|
-
const env = {};
|
|
797
|
-
for (const [rawKey, rawValue] of Object.entries(value)) {
|
|
798
|
-
const key = rawKey.trim();
|
|
799
|
-
if (key.length === 0) {
|
|
800
|
-
throw new Error(`Invalid config file ${path}: "env" contains an empty key.`);
|
|
801
|
-
}
|
|
802
|
-
if (typeof rawValue !== "string") {
|
|
803
|
-
throw new Error(`Invalid config file ${path}: "env.${rawKey}" must be a string.`);
|
|
804
|
-
}
|
|
805
|
-
env[key] = rawValue;
|
|
806
|
-
}
|
|
807
|
-
return Object.keys(env).length > 0 ? env : undefined;
|
|
808
|
-
})();
|
|
809
|
-
const apiKeys = (() => {
|
|
810
|
-
const value = parsed.apiKeys;
|
|
811
|
-
if (typeof value === "undefined")
|
|
812
|
-
return undefined;
|
|
813
|
-
if (!isRecord(value)) {
|
|
814
|
-
throw new Error(`Invalid config file ${path}: "apiKeys" must be an object.`);
|
|
815
|
-
}
|
|
816
|
-
const keys = {};
|
|
817
|
-
const allowed = [
|
|
818
|
-
"openai",
|
|
819
|
-
"anthropic",
|
|
820
|
-
"google",
|
|
821
|
-
"xai",
|
|
822
|
-
"openrouter",
|
|
823
|
-
"zai",
|
|
824
|
-
"apify",
|
|
825
|
-
"firecrawl",
|
|
826
|
-
"fal",
|
|
827
|
-
];
|
|
828
|
-
for (const [key, val] of Object.entries(value)) {
|
|
829
|
-
const k = key.trim().toLowerCase();
|
|
830
|
-
if (!allowed.includes(k)) {
|
|
831
|
-
throw new Error(`Invalid config file ${path}: unknown apiKeys provider "${key}".`);
|
|
832
|
-
}
|
|
833
|
-
if (typeof val !== "string" || val.trim().length === 0) {
|
|
834
|
-
throw new Error(`Invalid config file ${path}: "apiKeys.${key}" must be a non-empty string.`);
|
|
835
|
-
}
|
|
836
|
-
keys[k] = val.trim();
|
|
837
|
-
}
|
|
838
|
-
return Object.keys(keys).length > 0 ? keys : undefined;
|
|
839
|
-
})();
|
|
52
|
+
const zai = parseProviderBaseUrlConfig(parsed.zai, path, "zai");
|
|
53
|
+
const configEnv = parseEnvConfig(parsed, path);
|
|
54
|
+
const apiKeys = parseApiKeysConfig(parsed, path);
|
|
840
55
|
return {
|
|
841
56
|
config: {
|
|
842
57
|
...(model ? { model } : {}),
|
|
@@ -850,9 +65,11 @@ export function loadSummarizeConfig({ env }) {
|
|
|
850
65
|
...(ui ? { ui } : {}),
|
|
851
66
|
...(cli ? { cli } : {}),
|
|
852
67
|
...(openai ? { openai } : {}),
|
|
68
|
+
...(nvidia ? { nvidia } : {}),
|
|
853
69
|
...(anthropic ? { anthropic } : {}),
|
|
854
70
|
...(google ? { google } : {}),
|
|
855
71
|
...(xai ? { xai } : {}),
|
|
72
|
+
...(zai ? { zai } : {}),
|
|
856
73
|
...(logging ? { logging } : {}),
|
|
857
74
|
...(configEnv ? { env: configEnv } : {}),
|
|
858
75
|
...(apiKeys ? { apiKeys } : {}),
|