mirror of
https://github.com/danielmiessler/fabric.git
synced 2026-09-10 15:46:39 -04:00
Compare commits
214 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b682dad740 | ||
|
|
e91b4f0af1 | ||
|
|
f8bc07f2d7 | ||
|
|
8db08ad20f | ||
|
|
6b0914d1bb | ||
|
|
3ca2133819 | ||
|
|
f141b68f12 | ||
|
|
8dce453ed7 | ||
|
|
4637fa1a22 | ||
|
|
a3d1dc2cae | ||
|
|
00f56a76e0 | ||
|
|
e9b32ba546 | ||
|
|
4835506879 | ||
|
|
852ffd8468 | ||
|
|
fc0b2c0294 | ||
|
|
a8afe60608 | ||
|
|
892c70a8bb | ||
|
|
63846b523c | ||
|
|
f37c71f9f7 | ||
|
|
cc2a9d39d6 | ||
|
|
95c5b7d6ba | ||
|
|
b136259cfb | ||
|
|
52f63d0795 | ||
|
|
2c510971dd | ||
|
|
b185a5c790 | ||
|
|
8fd6ce2418 | ||
|
|
8aeaa1bb5e | ||
|
|
3ad89a3516 | ||
|
|
cd54c1a82f | ||
|
|
435b89d936 | ||
|
|
c10bd5a380 | ||
|
|
374d2dc967 | ||
|
|
54a7775e5b | ||
|
|
19b87b33a2 | ||
|
|
1b907fe318 | ||
|
|
9b724f8cec | ||
|
|
733a1810bf | ||
|
|
338b89cfe9 | ||
|
|
c9211929c4 | ||
|
|
9614bf2f16 | ||
|
|
2580d130c0 | ||
|
|
0cb6a9facc | ||
|
|
6627337e54 | ||
|
|
56d330f5e8 | ||
|
|
829fa3bd07 | ||
|
|
28273a0533 | ||
|
|
ab64555925 | ||
|
|
cb340b6b02 | ||
|
|
32ff0b5591 | ||
|
|
2b1b31ca3f | ||
|
|
b90a07bc19 | ||
|
|
a4b89f65c2 | ||
|
|
9e92bca148 | ||
|
|
fe2fbe9cff | ||
|
|
c9fa92bf03 | ||
|
|
7a638ff3cc | ||
|
|
bd4c9c93d2 | ||
|
|
b12a1ef1eb | ||
|
|
8135a11606 | ||
|
|
fba2543895 | ||
|
|
e8fd77c40b | ||
|
|
9f38ee0eff | ||
|
|
1b1feea2b5 | ||
|
|
737c3ac372 | ||
|
|
e25005c8d5 | ||
|
|
9e685112af | ||
|
|
e65c35d63e | ||
|
|
d8e934df22 | ||
|
|
7f2e663cb9 | ||
|
|
01f88e5c2b | ||
|
|
9730808119 | ||
|
|
6cef8c10ad | ||
|
|
cd8c249744 | ||
|
|
080311effc | ||
|
|
f95bff568f | ||
|
|
ec0b7246c4 | ||
|
|
3b03c17f9a | ||
|
|
4ba8ac2919 | ||
|
|
3e7c4b902a | ||
|
|
177363a702 | ||
|
|
3ff434ed2b | ||
|
|
ba6feced62 | ||
|
|
69dbf44ad1 | ||
|
|
64b1fc1fab | ||
|
|
37fc6686fa | ||
|
|
e36a231f3f | ||
|
|
54347b5cf5 | ||
|
|
1e650266ab | ||
|
|
d49a61a32e | ||
|
|
0dbf9cb0ec | ||
|
|
5ff3330040 | ||
|
|
d85544c88a | ||
|
|
2469552d94 | ||
|
|
094691b148 | ||
|
|
ff3dd1a5ad | ||
|
|
c32ee25f03 | ||
|
|
34e02fdca2 | ||
|
|
4dc7e5a1f2 | ||
|
|
72d626ebec | ||
|
|
a1f8549620 | ||
|
|
82e14bb8f3 | ||
|
|
c7f07b6f1b | ||
|
|
f96a971168 | ||
|
|
6e44e1c66a | ||
|
|
5a5f800f68 | ||
|
|
1748c816ec | ||
|
|
67d0cd09ed | ||
|
|
47774fa8c7 | ||
|
|
f6a9b74dac | ||
|
|
43aa95e966 | ||
|
|
35c058fb4e | ||
|
|
6a999ab23e | ||
|
|
5f34b6812e | ||
|
|
3eba7a51d4 | ||
|
|
a420eaf63c | ||
|
|
51eeff1109 | ||
|
|
adabdf1e68 | ||
|
|
59d26a7e89 | ||
|
|
29b32f9ff2 | ||
|
|
8e452510e0 | ||
|
|
69a79e6efc | ||
|
|
73fa72e279 | ||
|
|
4b4821a235 | ||
|
|
be1540781d | ||
|
|
7ede03225d | ||
|
|
4f073730a6 | ||
|
|
134171de7e | ||
|
|
4527a5b919 | ||
|
|
387b226c4e | ||
|
|
4dfaf61e93 | ||
|
|
175772e8a0 | ||
|
|
6a9b55a096 | ||
|
|
90cd2bef9d | ||
|
|
6f4154a4ae | ||
|
|
d2995ee02e | ||
|
|
8a3058e2b8 | ||
|
|
d7cc49f25c | ||
|
|
d7259fd6ae | ||
|
|
c8bfd3f886 | ||
|
|
774376d19b | ||
|
|
7bc470da5f | ||
|
|
4d896b47d8 | ||
|
|
995c666e67 | ||
|
|
5973722f1a | ||
|
|
c86bd1cda9 | ||
|
|
6f4e2fad9b | ||
|
|
b545eb97a0 | ||
|
|
11f2683d50 | ||
|
|
3496213649 | ||
|
|
9930b6be71 | ||
|
|
e8d4558129 | ||
|
|
2a73277776 | ||
|
|
a0f4fb4acd | ||
|
|
f69f1ba67d | ||
|
|
2b613a4787 | ||
|
|
3e7a3a88f7 | ||
|
|
b726895299 | ||
|
|
9743e10273 | ||
|
|
82d8269da7 | ||
|
|
78f9d7c1f7 | ||
|
|
99dcff0f36 | ||
|
|
27f0f6643a | ||
|
|
b40095b4c3 | ||
|
|
8d20796050 | ||
|
|
d2537208df | ||
|
|
09f306ad2b | ||
|
|
5ed0314681 | ||
|
|
b3a610ddc6 | ||
|
|
3277c3e5a7 | ||
|
|
5ae039e726 | ||
|
|
d32e770948 | ||
|
|
0d7e1f7d52 | ||
|
|
76c164a041 | ||
|
|
3d43e1dca2 | ||
|
|
27c025be54 | ||
|
|
56a1a7f21e | ||
|
|
e387e7b86f | ||
|
|
7fd6733c12 | ||
|
|
7ced82f782 | ||
|
|
b046ddb518 | ||
|
|
42311fe04b | ||
|
|
b9aff6c0a9 | ||
|
|
014f946420 | ||
|
|
996a73ca8d | ||
|
|
90d42c8d4f | ||
|
|
18fb502c41 | ||
|
|
62b25020e5 | ||
|
|
3fa1dcde14 | ||
|
|
cb3a0e5aec | ||
|
|
ceec9335c1 | ||
|
|
febf2c429c | ||
|
|
d65b8b8213 | ||
|
|
c78576deb7 | ||
|
|
428df31486 | ||
|
|
d1217ae0fd | ||
|
|
aeb709e7e8 | ||
|
|
6115342314 | ||
|
|
dd461857ab | ||
|
|
ae8b910ef7 | ||
|
|
8ae3342bf3 | ||
|
|
8b730d1967 | ||
|
|
631d02cc27 | ||
|
|
704ef832ca | ||
|
|
83b77a59bd | ||
|
|
294954d7e6 | ||
|
|
c271816d2d | ||
|
|
2e0abdd78a | ||
|
|
070c626b7b | ||
|
|
f812188115 | ||
|
|
8aa7a703dc | ||
|
|
f44d44b077 | ||
|
|
e42d8b8fec | ||
|
|
8368ecf35f | ||
|
|
086e196ade |
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -327,3 +327,6 @@ tmp/
|
|||
.claude/
|
||||
# Claude MEMORY directory
|
||||
MEMORY/
|
||||
|
||||
# Maestro artifacts
|
||||
.maestro
|
||||
|
|
|
|||
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
|
@ -8,10 +8,13 @@
|
|||
"anthropics",
|
||||
"Aoede",
|
||||
"apikey",
|
||||
"APIM",
|
||||
"aplicar",
|
||||
"appuser",
|
||||
"Astley",
|
||||
"atotto",
|
||||
"Autonoe",
|
||||
"azurecommon",
|
||||
"azureml",
|
||||
"badfile",
|
||||
"Behrens",
|
||||
|
|
@ -131,6 +134,7 @@
|
|||
"modelines",
|
||||
"Moltbot",
|
||||
"mpga",
|
||||
"MSAL",
|
||||
"mvdan",
|
||||
"mychat",
|
||||
"mygroup",
|
||||
|
|
@ -222,6 +226,7 @@
|
|||
"cSpell.ignorePaths": [
|
||||
"go.mod",
|
||||
".gitignore",
|
||||
".vscode/**",
|
||||
"CHANGELOG.md",
|
||||
"scripts/installer/install.*",
|
||||
"web/static/data/pattern_descriptions.json",
|
||||
|
|
@ -255,6 +260,7 @@
|
|||
"module",
|
||||
"p",
|
||||
"summary",
|
||||
"strong",
|
||||
"sup"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
382
CHANGELOG.md
382
CHANGELOG.md
|
|
@ -1,5 +1,387 @@
|
|||
# Changelog
|
||||
|
||||
## v1.4.478 (2026-09-06)
|
||||
|
||||
### PR [#2216](https://github.com/danielmiessler/Fabric/pull/2216) by [ctbaum](https://github.com/ctbaum): fix: enable raw mode for GPT-6 models
|
||||
|
||||
- Fix: enable raw mode for GPT-6 models
|
||||
|
||||
## v1.4.477 (2026-09-03)
|
||||
|
||||
### PR [#2211](https://github.com/danielmiessler/Fabric/pull/2211) by [ksylvan](https://github.com/ksylvan): fix: prevent pattern loader temporary directory leaks
|
||||
|
||||
- Prevent pattern loader leaks by lazily creating temporary directories during database population and cleaning them up after successful or failed downloads.
|
||||
- Add regression tests for lazy directory creation and cleanup.
|
||||
|
||||
## v1.4.476 (2026-09-03)
|
||||
|
||||
### PR [#2210](https://github.com/danielmiessler/Fabric/pull/2210) by [ksylvan](https://github.com/ksylvan): feat: add Pzero as an OpenAI-compatible AI provider
|
||||
|
||||
- Added Pzero as an OpenAI-compatible AI provider.
|
||||
- Registered Pzero with its OpenAI-compatible API base URL.
|
||||
- Added Pzero to the README’s list of supported AI providers.
|
||||
|
||||
## v1.4.475 (2026-09-03)
|
||||
|
||||
### PR [#2209](https://github.com/danielmiessler/Fabric/pull/2209) by [kadiryildiz283](https://github.com/kadiryildiz283): feat(i18n): add Turkish (tr) translation
|
||||
|
||||
- Feat(i18n): add Turkish (tr) translation
|
||||
|
||||
## v1.4.474 (2026-09-02)
|
||||
|
||||
### PR [#2206](https://github.com/danielmiessler/Fabric/pull/2206) by [ksylvan](https://github.com/ksylvan): fix: confine storage names and authenticate Ollama serve
|
||||
|
||||
- Reject unsafe cross-platform storage names and directory traversal attempts.
|
||||
- Confine symlink targets to configured filesystem storage directories.
|
||||
- Require API keys for non-loopback server bindings and authenticate Ollama routes.
|
||||
- Validate chat pattern, context, and session names early while preventing internal filesystem details from leaking through client errors.
|
||||
- Default the REST server to loopback port 8080 and add regression coverage for traversal, symlink, and authentication security.
|
||||
|
||||
## v1.4.473 (2026-08-29)
|
||||
|
||||
### PR [#2203](https://github.com/danielmiessler/Fabric/pull/2203) by [pacocartones](https://github.com/pacocartones) and [ksylvan](https://github.com/ksylvan): fix(web): preserve multi-byte UTF-8 split across SSE chunks in chat stream
|
||||
|
||||
- Fix: preserve split UTF-8 characters in streaming responses
|
||||
- Decode API response chunks with persistent streaming state
|
||||
- Reuse streaming decoder when inspecting chat backend events
|
||||
|
||||
## v1.4.472 (2026-08-29)
|
||||
|
||||
### PR [#2198](https://github.com/danielmiessler/Fabric/pull/2198) by [cuihuan](https://github.com/cuihuan): feat(providers): add Synthorai as an OpenAI-compatible provider
|
||||
|
||||
- Feat(providers): add Synthorai as an OpenAI-compatible provider
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Chore: delete generate_changelog noise in README.
|
||||
|
||||
## v1.4.471 (2026-08-28)
|
||||
|
||||
### PR [#2200](https://github.com/danielmiessler/Fabric/pull/2200) by [ksylvan](https://github.com/ksylvan): fix: persist Codex OAuth tokens after refresh
|
||||
|
||||
- Persist refreshed Codex OAuth tokens securely across processes, writing rotated credentials atomically with cross-process locks.
|
||||
- Reload stored tokens before refresh and return valid in-memory tokens first, preventing stale disk values from overwriting fresh credentials.
|
||||
- Reuse valid refresh tokens before launching interactive authentication, requiring account identifiers and unexpired tokens for fast-path reuse.
|
||||
- Preserve existing environment values, save environment files atomically while holding locks, and enforce secret-only permissions.
|
||||
- Reactivate configured vendors and surface configuration and provider failures immediately, with localized Codex prompts and persistence errors.
|
||||
|
||||
## v1.4.470 (2026-08-04)
|
||||
|
||||
### PR [#2186](https://github.com/danielmiessler/Fabric/pull/2186) by [giodamelio](https://github.com/giodamelio): Update Nixpkgs version for newer Go version
|
||||
|
||||
- Updated the Nixpkgs version to provide a Go release that satisfies the `go.mod` requirement of Go >= 1.26.0, as the previously pinned Nixpkgs only shipped Go 1.25.4.
|
||||
|
||||
## v1.4.469 (2026-08-03)
|
||||
|
||||
### PR [#2185](https://github.com/danielmiessler/Fabric/pull/2185) by [ksylvan](https://github.com/ksylvan): feat(web): replace PDF.js pipeline with pdf-inspector WASM worker
|
||||
|
||||
- Replaced the PDF.js processing pipeline with the new `pdf-inspector.worker.ts`, backed by `@firecrawl/pdf-inspector-wasm`. The worker initializes WASM one time and processes transferred ArrayBuffers.
|
||||
- Added worker error handling. A worker crash now rejects all pending requests with a clear error and terminates the worker. The next request starts a new worker.
|
||||
- Fixed the file attachment behavior. The app now parses each file and stores its content without a chat request. One submit sends exactly one `streamChat` call, not one call for each attached file.
|
||||
- Removed the legacy PDF dependency chain: `pdf-to-markdown-core`, `pdfjs-dist`, `pdf-config.ts`, and the related build configuration. `@firecrawl/pdf-inspector-wasm` is now the only PDF dependency.
|
||||
- Simplified the worker boundary in follow-up refactors: merged redundant methods, removed dead code, and inlined a single-use interface. The full test suite continued to pass.
|
||||
|
||||
## v1.4.468 (2026-08-02)
|
||||
|
||||
### PR [#2182](https://github.com/danielmiessler/Fabric/pull/2182) by [drawliin](https://github.com/drawliin): fix(ollama): close stream channel on errors
|
||||
|
||||
- Fix(ollama): close stream channel on errors
|
||||
|
||||
## v1.4.467 (2026-07-31)
|
||||
|
||||
### PR [#2171](https://github.com/danielmiessler/Fabric/pull/2171) by [OdinKral](https://github.com/OdinKral): feat(scripts): add pattern/maintenance audit script
|
||||
|
||||
- Adds `scripts/audit-patterns.sh`, a dependency-free audit tool for the patterns library that detects thin patterns (under 15 lines), bloated patterns (over 50 KB), stale hardcoded model references, missing `INPUT` sections, i18n key gaps, and shell completion gaps. The script is read-only and exits with code `0` by default, or `1` when run with `--strict` for CI integration.
|
||||
|
||||
## v1.4.466 (2026-07-30)
|
||||
|
||||
### PR [#2116](https://github.com/danielmiessler/Fabric/pull/2116) by [ksylvan](https://github.com/ksylvan) and [dependabot](https://github.com/apps/dependabot): fix(web): repair the chat page and modernize the build toolchain
|
||||
|
||||
- Modernized the web stack by upgrading Tailwind, Skeleton, SvelteKit, Vite, and supporting dependencies.
|
||||
- Replaced removed Skeleton components with compatible local implementations to restore UI functionality.
|
||||
- Improved pre-stream chat error reporting to prevent duplication of streamed errors, and hardened vendor list parsing to return empty model arrays on malformed input.
|
||||
- Fixed stream completion crashes, normalized displayed error messages, corrected toast stacking order, and added warning notification support.
|
||||
- Restored linting, formatting, testing, and pnpm override configuration, and preserved custom themes through the Tailwind CSS-based configuration migration.
|
||||
|
||||
### PR [#2178](https://github.com/danielmiessler/Fabric/pull/2178) by [OdinKral](https://github.com/OdinKral) and [ksylvan](https://github.com/ksylvan): feat(patterns): add generate_frontmatter for PKM/Obsidian users
|
||||
|
||||
- Add `generate_frontmatter` to conversion, extraction, and writing categories
|
||||
- Document PKM-ready YAML metadata fields in pattern explanations
|
||||
- Register pattern descriptions and extracts for suggestion workflows
|
||||
- Credit both contributors in incoming changelog entry
|
||||
|
||||
## v1.4.465 (2026-07-29)
|
||||
|
||||
### PR [#2180](https://github.com/danielmiessler/Fabric/pull/2180) by [ksylvan](https://github.com/ksylvan): fix: include Grok in localized --search help text
|
||||
|
||||
- Fix: Updated all 11 locale entries in the i18n message catalog to include "Grok" in the `enable_web_search_tool` help text, ensuring Grok (xAI) web search support is discoverable via `fabric --help`. Also updates the generated README help block to match. No functional change.
|
||||
|
||||
## v1.4.464 (2026-07-29)
|
||||
|
||||
### PR [#2179](https://github.com/danielmiessler/Fabric/pull/2179) by [ksylvan](https://github.com/ksylvan): fix: declare completion arguments and synchronize CLI help
|
||||
|
||||
- Fix: declare completion arguments and synchronize CLI help
|
||||
- Declare Fish completion arguments for dynamic and fixed values
|
||||
- Enable filtered file suggestions for supported path options
|
||||
- Add Spotify, transcription, metadata, and wire-debug completion options
|
||||
- Include Grok among providers supporting web search completion
|
||||
- Keep the input/output token detail in the show-metadata description
|
||||
|
||||
## v1.4.463 (2026-07-28)
|
||||
|
||||
### PR [#2168](https://github.com/danielmiessler/Fabric/pull/2168) by [ksylvan](https://github.com/ksylvan): fix: complete localized setup and error messages for supported locales
|
||||
|
||||
- Fix: complete localized setup and error messages across supported locales
|
||||
- Translate Bedrock setup prompts across nine supported locales
|
||||
- Localize datetime and system template errors consistently
|
||||
- Translate Persian Spotify errors and setup guidance
|
||||
- Correct Japanese and Polish file operation log labels
|
||||
|
||||
## v1.4.462 (2026-07-28)
|
||||
|
||||
### PR [#2123](https://github.com/danielmiessler/Fabric/pull/2123) by [ksylvan](https://github.com/ksylvan) and [OdinKral](https://github.com/OdinKral): fix: block path traversal in pattern name lookup
|
||||
|
||||
- **Security Fix:** Blocked path traversal attacks in pattern name lookup (closes #2094) — pattern names containing `..` could previously escape the patterns directory and read arbitrary files via `filepath.Join`; a guard has been added at the top of `getFromDB`, an i18n key `pattern_invalid_name` has been added to all 11 locale files, and test cases now cover all common traversal variants.
|
||||
- New translations for the "invalid pattern" user-facing string.
|
||||
|
||||
## v1.4.461 (2026-07-28)
|
||||
|
||||
### PR [#2152](https://github.com/danielmiessler/Fabric/pull/2152) by [AUTHENSOR](https://github.com/AUTHENSOR): fix: shell-escape extension values to prevent command injection
|
||||
|
||||
- **Fix:** Shell-escape extension values to prevent command injection in the extension executor, which previously ran commands via `sh -c` with unescaped, user-controlled values interpolated into the command string. All user-controlled values are now wrapped in single quotes with embedded-single-quote escaping prior to interpolation, ensuring the shell treats them as literal arguments. A regression test (`ShellInjectionBlocked`) has been added to verify that malicious input (e.g., `hello; touch /marker`) does not execute unintended shell commands.
|
||||
|
||||
## v1.4.460 (2026-07-24)
|
||||
|
||||
### PR [#2166](https://github.com/danielmiessler/Fabric/pull/2166) by [ksylvan](https://github.com/ksylvan): feat: add Claude Opus 5 support and refresh dependencies
|
||||
|
||||
- Add Claude Opus 5 to the supported model selection.
|
||||
- Disable sampling parameters for Claude Opus 5 requests.
|
||||
- Restrict one-million-token beta headers to compatible Claude models only.
|
||||
- Remove unsupported 200K-context models from the beta header mapping.
|
||||
- Upgrade Anthropic, AWS, Ollama, Google, and supporting dependencies.
|
||||
|
||||
## v1.4.459 (2026-07-16)
|
||||
|
||||
### PR [#2135](https://github.com/danielmiessler/Fabric/pull/2135) by [octo-patch](https://github.com/octo-patch): feat: upgrade MiniMax default model to M3
|
||||
|
||||
- Upgraded the MiniMax default model to M3, making it the new flagship selection.
|
||||
- Added MiniMax-M3 to the static model list as the first entry, establishing it as the default.
|
||||
- Retained MiniMax-M2.7 and MiniMax-M2.7-highspeed as available alternative models.
|
||||
- Removed deprecated models (M2.5, M2.5-highspeed, M2.5-lightning, M2, M2.1, and M2.1-lightning) from the static list.
|
||||
|
||||
## v1.4.458 (2026-07-12)
|
||||
|
||||
### PR [#2161](https://github.com/danielmiessler/Fabric/pull/2161) by [ksylvan](https://github.com/ksylvan): fix: respect Anthropic chat option max token overrides
|
||||
|
||||
- Fix: respect Anthropic chat option max token overrides
|
||||
|
||||
- Use configured Anthropic max tokens as default
|
||||
- Apply chat option max tokens when provided
|
||||
|
||||
- Preserve existing behavior for missing token overrides
|
||||
- Add tests for default max token selection
|
||||
|
||||
- Add tests for explicit max token overrides
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Chore: clean up ChangeLog
|
||||
|
||||
## v1.4.457 (2026-07-09)
|
||||
|
||||
### PR [#2155](https://github.com/danielmiessler/Fabric/pull/2155) by [ksylvan](https://github.com/ksylvan): Claude Sonnet 5 Anthropic support
|
||||
|
||||
- Add Claude Sonnet 5 to supported Anthropic models
|
||||
- Enable one-million-token context beta for Claude 5 models
|
||||
- Omit sampling parameters for Claude Sonnet 5 requests
|
||||
- Centralize Anthropic sampling restrictions behind prefix matching
|
||||
- Remove older Claude 4 aliases from model listings
|
||||
|
||||
### PR [#2156](https://github.com/danielmiessler/Fabric/pull/2156) by [ksylvan](https://github.com/ksylvan): Make it possible to back-fill missing ChangeLog entries
|
||||
|
||||
- Add support for changelog generation for closed pull requests via a new `--closed-ok` flag, bypassing open-state validation.
|
||||
- Skip mergeability checks when processing closed pull requests to allow smooth back-filling of missing entries.
|
||||
- Store the closed pull request allowance setting in the generator configuration for consistent behavior.
|
||||
- Improve error messaging to guide users toward using `--closed-ok` when validation errors occur on closed PRs.
|
||||
- Introduce the `--closed-ok` flag as the primary mechanism for enabling back-fill workflows on previously closed pull requests.
|
||||
|
||||
## v1.4.455 (2026-06-09)
|
||||
|
||||
### PR [#2138](https://github.com/danielmiessler/Fabric/pull/2138) by [ksylvan](https://github.com/ksylvan): New Claude Fable model + cache OpenAI model discovery and handle provider rate limits
|
||||
|
||||
- Add persistent cache for provider model discovery results, improving performance and reliability of provider integrations.
|
||||
- Serve stale model caches during discovery failures, ensuring continued operation when upstream providers are unavailable.
|
||||
- Add Claude Fable 5 Anthropic model support, with sampling parameters automatically omitted for compatibility.
|
||||
- Return concise localized errors for rate-limited model fetches, with updated translations across all supported locales.
|
||||
- Update Go dependencies for AI provider integrations to keep upstream libraries current.
|
||||
|
||||
## v1.4.454 (2026-06-02)
|
||||
|
||||
### PR [#2136](https://github.com/danielmiessler/Fabric/pull/2136) by [ksylvan](https://github.com/ksylvan): chore: extend sampling param exclusion to Opus 4.8 models
|
||||
|
||||
- Extends the sampling parameter exclusion logic to cover Opus 4.8 models, ensuring consistent behavior alongside the existing Opus 4.7 exclusion.
|
||||
- Adds the `claude-opus-4-8` model prefix to the sampling parameter exclusion check.
|
||||
- Updates the associated code comment to explicitly reference Opus 4.8 models.
|
||||
|
||||
## v1.4.453 (2026-05-28)
|
||||
|
||||
### PR [#2132](https://github.com/danielmiessler/Fabric/pull/2132) by [ksylvan](https://github.com/ksylvan): Add Claude Opus 4.8 model and bump Go toolchain and dependencies
|
||||
|
||||
- Add Claude Opus 4.8 to the list of supported models.
|
||||
- Upgrade the Go toolchain to version 1.26.0.
|
||||
- Bump `anthropic-sdk-go` to v1.46.0.
|
||||
- Update AWS SDK and Bedrock service modules to their latest versions.
|
||||
- Bump the Ollama client to v0.24.0.
|
||||
|
||||
## v1.4.452 (2026-05-04)
|
||||
|
||||
### PR [#2111](https://github.com/danielmiessler/Fabric/pull/2111) by [ksylvan](https://github.com/ksylvan): fix: omit Anthropic sampling params for Claude Opus 4.7
|
||||
|
||||
- Fix: Omit Anthropic sampling parameters for Claude Opus 4.7 to ensure compatibility.
|
||||
- Add a sampling parameter guard specifically for Opus 4.7.
|
||||
- Omit `temperature` and `top_p` for models that do not support these parameters.
|
||||
- Preserve existing `TopP` and temperature selection behavior for compatible models.
|
||||
- Add unit test coverage for the Opus 4.7 sampling parameter omission.
|
||||
|
||||
## v1.4.451 (2026-04-23)
|
||||
|
||||
### PR [#2079](https://github.com/danielmiessler/Fabric/pull/2079) by [teamsincetoday](https://github.com/teamsincetoday): Add 3 commerce intelligence patterns: affiliate extraction, video entities, monetization
|
||||
|
||||
- Added `extract_affiliate_products` pattern to surface both sponsored and organic affiliate opportunities from any video transcript, going beyond the existing `extract_sponsors` pattern.
|
||||
- Added `extract_video_commerce_entities` pattern to identify all commercial entities in video content, categorized by type, timestamp position, and purchase likelihood.
|
||||
- Added `analyze_monetization_opportunities` pattern to map audience intent to revenue strategies, covering affiliate links, sponsorships, and digital products.
|
||||
- Registered all three new patterns in `pattern_descriptions.json` and `pattern_extracts.json` with appropriate metadata and tags.
|
||||
- Integrated all three patterns into `suggest_pattern` under the ANALYSIS, BUSINESS, and EXTRACT categories, and documented them in `pattern_explanations.md`.
|
||||
|
||||
### PR [#2086](https://github.com/danielmiessler/Fabric/pull/2086) by [majiayu000](https://github.com/majiayu000): fix: parse vendor prefix from model name for vendor/model convention
|
||||
|
||||
- **Fix:** Added fallback logic to parse the vendor prefix from a model name when no vendor is explicitly specified. When a model string such as `ollama/llama3` is passed, the lookup no longer fails with a "could not find vendor" error; instead, the first path segment is split and checked against known vendors, correctly resolving the model to `llama3` under the `Ollama` vendor group.
|
||||
|
||||
## v1.4.450 (2026-04-23)
|
||||
|
||||
### PR [#2092](https://github.com/danielmiessler/Fabric/pull/2092) by [Resistor52](https://github.com/Resistor52): feat(openai): add GrokAI search grounding via xAI Responses API
|
||||
|
||||
- Added support for GrokAI search grounding via xAI's Responses API, fixing HTTP 422 errors caused by a hardcoded OpenAI `web_search_preview` tool name in `buildResponseParams`.
|
||||
- Introduced two new fields to `openai_compatible.ProviderConfig`: `WebSearchToolName` (to override the default web search tool name) and `EnableXSearch` (to append xAI's `x_search` tool when search is enabled).
|
||||
- Both new fields default to empty/false, preserving full backwards compatibility for all existing providers.
|
||||
- GrokAI is pre-configured with `WebSearchToolName` set to `"web_search"` and `EnableXSearch` set to `true`, enabling grounded search results with real source URLs via `fabric -V GrokAI --search`.
|
||||
- Added tests in `openai_test.go` covering the new override paths and confirming default provider behavior remains unchanged.
|
||||
|
||||
## v1.4.449 (2026-04-23)
|
||||
|
||||
### PR [#2089](https://github.com/danielmiessler/Fabric/pull/2089) by [dependabot](https://github.com/apps/dependabot) and [ksylvan](https://github.com/ksylvan): chore(deps-dev): bump vite from 5.4.21 to 8.0.8 in /web in the npm_and_yarn group across 1 directory
|
||||
|
||||
- Chore(deps-dev): bump vite
|
||||
Bumps the npm_and_yarn group with 1 update in the /web directory: [vite](<https://github.com/vitejs/vite/tree/HEAD/packages/vite).>
|
||||
|
||||
Updates `vite` from 5.4.21 to 8.0.8
|
||||
|
||||
- [Release notes](<https://github.com/vitejs/vite/releases)>
|
||||
- [Changelog](<https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)>
|
||||
|
||||
- [Commits](<https://github.com/vitejs/vite/commits/v8.0.8/packages/vite)>
|
||||
updated-dependencies:
|
||||
|
||||
- dependency-name: vite
|
||||
dependency-version: 8.0.8
|
||||
dependency-type: direct:development
|
||||
dependency-group: npm_and_yarn
|
||||
Signed-off-by: dependabot[bot] <support@github.com>
|
||||
|
||||
### PR [#2103](https://github.com/danielmiessler/Fabric/pull/2103) by [dependabot](https://github.com/apps/dependabot) and [ksylvan](https://github.com/ksylvan): chore(deps): bump github.com/go-git/go-git/v5 from 5.17.2 to 5.18.0 in the go_modules group across 1 directory
|
||||
|
||||
- Upgraded Vite from 5.4.21 to 8.0.8 in the web layer, representing a significant major-version jump with potential performance and build tooling improvements.
|
||||
- Bumped `@sveltejs/vite-plugin-svelte` from 4.0.4 to 7.0.0, a major version update aligning the Svelte plugin with the upgraded Vite 8 runtime.
|
||||
- Updated AWS SDK Go v2 modules to their latest patches, ensuring up-to-date cloud integration support and security fixes.
|
||||
- Updated the Ollama client from 0.20.4 to 0.21.1, keeping local AI model support current with the latest client improvements.
|
||||
- Upgraded `go-git` from 5.17.2 to 5.18.0, incorporating the latest fixes and improvements to Git operations within the Go module ecosystem.
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Docs: add Scoop install instructions and expand cSpell dictionary
|
||||
|
||||
- Add Windows Scoop install section to Chinese README
|
||||
- Link Scoop install entry in both README tables of contents
|
||||
|
||||
- Extend cSpell dictionary with APIM, MSAL, and related terms
|
||||
- Ignore `.vscode/**` paths during cSpell checks
|
||||
|
||||
- Allow `strong` tag in cSpell markdown configuration
|
||||
- Docs: add Scoop installation instructions
|
||||
|
||||
## v1.4.447 (2026-04-17)
|
||||
|
||||
### PR [#2097](https://github.com/danielmiessler/Fabric/pull/2097) by [ksylvan](https://github.com/ksylvan): Add Claude Opus 4.7 model support and bump Anthropic SDK to v1.37.0
|
||||
|
||||
- Added Claude Opus 4.7 model support and bumped the Anthropic SDK to v1.37.0.
|
||||
- Upgraded the `anthropic-sdk-go` dependency from v1.34.0 to v1.37.0.
|
||||
- Added `claude-opus-4-7` to the supported models list.
|
||||
- Enabled the 1M context window beta feature for Opus 4.7.
|
||||
- Updated model beta comments to reflect Opus 4.7 support.
|
||||
|
||||
## v1.4.446 (2026-04-15)
|
||||
|
||||
### PR [#2093](https://github.com/danielmiessler/Fabric/pull/2093) by [alecjmckanna](https://github.com/alecjmckanna): feat: add --readpattern flag to print pattern contents to terminal
|
||||
|
||||
- Adds a new `--readpattern <name>` CLI flag that prints the raw contents of a named pattern's `system.md` file to stdout, making it easy to inspect a pattern's instructions without navigating the filesystem manually.
|
||||
- Custom pattern directories are respected: the user's custom patterns directory is checked first before falling back to the main patterns directory, consistent with all other pattern lookups.
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Docs: update Docker config mount path for appuser
|
||||
|
||||
- Replace container config mount path from root to appuser
|
||||
- Align setup example with non-root container home directory
|
||||
|
||||
- Align pattern usage example with appuser config location
|
||||
- Align REST API example with updated config mount target
|
||||
|
||||
- Update English and Chinese README Docker instructions consistently
|
||||
|
||||
## v1.4.445 (2026-04-13)
|
||||
|
||||
### PR [#2091](https://github.com/danielmiessler/Fabric/pull/2091) by [jimscard](https://github.com/jimscard) and [ksylvan](https://github.com/ksylvan): Update Dockerfile for best practices and critical CVE fixes
|
||||
|
||||
- Pins Alpine 3.21 and Go 1.25.9 explicitly for reproducible, auditable builds.
|
||||
- Installs the Go toolchain directly in the builder stage, removing the dependency on an unavailable upstream `golang` tag.
|
||||
- Upgrades `setuptools` to remediate the critical vulnerability CVE-2025-47273.
|
||||
- Refreshes the `yt-dlp` installation path to align with current packaging conventions.
|
||||
- Configures the final image to run as a non-root user, reducing the container's attack surface.
|
||||
|
||||
## v1.4.444 (2026-04-09)
|
||||
|
||||
### PR [#2088](https://github.com/danielmiessler/Fabric/pull/2088) by [ksylvan](https://github.com/ksylvan): Combined dependabot fixes plus other Go module upgrades
|
||||
|
||||
- Upgraded `anthropic-sdk-go` from v1.27.1 to v1.34.0, bringing in several versions of improvements and fixes from the Anthropic Go SDK.
|
||||
- Upgraded `ollama` from v0.18.2 to v0.20.4, incorporating two minor version bumps of enhancements to the Ollama client library.
|
||||
- Upgraded `google.golang.org/grpc` from v1.79.3 to v1.80.0, picking up the latest gRPC release for Go.
|
||||
- Upgraded `google.golang.org/genai` from v1.51.0 to v1.53.0 and bumped `google.golang.org/api` from v0.272.0 to v0.275.0, keeping Google AI and API client libraries current.
|
||||
- Bumped `go-sqlite3` from v1.14.37 to v1.14.42, updated `go-git/v5` from v5.17.0 to v5.17.2, and refreshed `golang.org/x` packages (crypto, net, sys, text, mod) and OpenTelemetry packages from v1.42.0 to v1.43.0 alongside AWS SDK v2 patch releases.
|
||||
|
||||
## v1.4.443 (2026-04-06)
|
||||
|
||||
### PR [#2073](https://github.com/danielmiessler/Fabric/pull/2073) by [sathvikc](https://github.com/sathvikc) and [ksylvan](https://github.com/ksylvan): feat(youtube): Implement visual text extraction via FFmpeg and OCR
|
||||
|
||||
- Implemented FFmpeg and Tesseract-based visual text extraction from YouTube videos, enabling OCR on video frames.
|
||||
- Added configurable CLI flags for visual extraction parameters, giving users fine-grained control over the feature.
|
||||
- Fixed support for multi-line `yt-dlp` outputs and updated syntax compatibility with modern FFmpeg versions.
|
||||
- Refactored OCR processing to use bounded concurrency, context timeouts, and hardened CLI argument handling for improved stability and security.
|
||||
- Resolved multiple reliability issues including Tesseract CLI argument handling, racy error handling, and timestamp overflow bugs.
|
||||
|
||||
### Direct commits
|
||||
|
||||
- Docs: make README badges clickable
|
||||
|
||||
## v1.4.442 (2026-03-25)
|
||||
|
||||
### PR [#2075](https://github.com/danielmiessler/Fabric/pull/2075) by [ksylvan](https://github.com/ksylvan) and [mikaelpr](https://github.com/mikaelpr): refactor: extract OAuth and auth logic from Codex client module
|
||||
|
||||
- Refactored the Codex client module by extracting all OAuth and authentication logic into a dedicated module, improving separation of concerns.
|
||||
- Removed the OAuth flow, PKCE handling, and token refresh logic from `codex.go`, streamlining the client's core responsibilities.
|
||||
- Removed the auth transport round-trip retry logic, simplifying the HTTP transport layer.
|
||||
- Removed JWT parsing and token expiry utilities, along with unused OAuth types and helper structs, reducing dead code in the package.
|
||||
- Added a test for `SendStream` HTTP error mapping and channel close behavior, improving test coverage for the client module.
|
||||
|
||||
## v1.4.441 (2026-03-22)
|
||||
|
||||
### PR [#2068](https://github.com/danielmiessler/Fabric/pull/2068) by [dependabot](https://github.com/apps/dependabot) and [ksylvan](https://github.com/ksylvan): chore(deps): bump google.golang.org/grpc from 1.79.0 to 1.79.3 in the go_modules group across 1 directory
|
||||
|
|
|
|||
72
README.md
72
README.md
|
|
@ -18,10 +18,10 @@
|
|||
|
||||
# `fabric`
|
||||
|
||||

|
||||
[](https://github.com/danielmiessler/fabric)
|
||||
<br />
|
||||

|
||||

|
||||
[](https://github.com/danielmiessler/fabric)
|
||||
[](https://github.com/danielmiessler/fabric/commits/main)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
[](https://deepwiki.com/danielmiessler/fabric)
|
||||
|
||||
|
|
@ -82,6 +82,7 @@ Below are the **new features and capabilities** we've added (newest first):
|
|||
|
||||
### Recent Major Features
|
||||
|
||||
- [v1.4.447](https://github.com/danielmiessler/fabric/releases/tag/v1.4.447) (April 16, 2026) — **Claude Opus 4.7**: Updates the Anthropic SDK to v1.37.0 and adds the new [Claude Opus 4.7](https://www.anthropic.com/news/claude-opus-4-7) to the available models, including 1M-token context window support.
|
||||
- [v1.4.437](https://github.com/danielmiessler/fabric/releases/tag/v1.4.437) (March 16, 2026) — **OpenAI Codex PLugin**: Fabric now supports using OpenAI Codex (with your OpenAI subscription) as a backend!
|
||||
- [v1.4.417](https://github.com/danielmiessler/fabric/releases/tag/v1.4.417) (Feb 21, 2026) — **Azure AI Gateway Plugin**: Added Azure AI Gateway plugin supporting multiple backends (AWS Bedrock, Azure OpenAI, Google Vertex AI) through a unified Azure APIM Gateway with shared subscription key authentication.
|
||||
- [v1.4.416](https://github.com/danielmiessler/fabric/releases/tag/v1.4.416) (Feb 21, 2026) — **Azure Entra ID Authentication**: Added Azure Entra ID authentication plugin with shared Azure utilities, Entra ID/MSAL support, and extracted common Azure logic into a reusable `azurecommon` package.
|
||||
|
|
@ -136,6 +137,7 @@ Keep in mind that many of these were recorded when Fabric was Python-based, so r
|
|||
- [macOS (Homebrew)](#macos-homebrew)
|
||||
- [Arch Linux (AUR)](#arch-linux-aur)
|
||||
- [Windows](#windows)
|
||||
- [Windows (Scoop)](#windows-scoop)
|
||||
- [From Source](#from-source)
|
||||
- [Docker](#docker)
|
||||
- [Environment Variables](#environment-variables)
|
||||
|
|
@ -262,6 +264,10 @@ Use the official Microsoft supported `Winget` tool:
|
|||
|
||||
`winget install danielmiessler.Fabric`
|
||||
|
||||
#### Windows (Scoop)
|
||||
|
||||
`scoop install fabric-ai`
|
||||
|
||||
### From Source
|
||||
|
||||
To install Fabric, [make sure Go is installed](https://go.dev/doc/install), and then run the following command.
|
||||
|
|
@ -284,13 +290,13 @@ docker run --rm -it ghcr.io/ksylvan/fabric:v1.4.305 --version
|
|||
|
||||
# Run setup (first time)
|
||||
mkdir -p $HOME/.fabric-config
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --setup
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest --setup
|
||||
|
||||
# Use Fabric with your patterns
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest -p summarize
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest -p summarize
|
||||
|
||||
# Run the REST API server (see REST API Server section)
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --serve
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest --serve
|
||||
```
|
||||
|
||||
**Images available at:**
|
||||
|
|
@ -368,7 +374,9 @@ Fabric supports a wide range of AI providers:
|
|||
- Mistral
|
||||
- Novita AI
|
||||
- OpenRouter
|
||||
- Pzero
|
||||
- SiliconCloud
|
||||
- Synthorai
|
||||
- Together
|
||||
- Venice AI
|
||||
- Z AI
|
||||
|
|
@ -658,34 +666,41 @@ Application Options:
|
|||
-T, --topp= Set top P (default: 0.9)
|
||||
-s, --stream Stream
|
||||
-P, --presencepenalty= Set presence penalty (default: 0.0)
|
||||
-r, --raw Use the defaults of the model without sending chat options
|
||||
(temperature, top_p, etc.). Only affects OpenAI-compatible providers.
|
||||
Anthropic models always use smart parameter selection to comply with
|
||||
model-specific requirements.
|
||||
-r, --raw Use the defaults of the model without sending chat options (temperature,
|
||||
top_p, etc.). Only affects OpenAI-compatible providers. Anthropic models
|
||||
always use smart parameter selection to comply with model-specific
|
||||
requirements.
|
||||
-F, --frequencypenalty= Set frequency penalty (default: 0.0)
|
||||
-l, --listpatterns List all patterns
|
||||
--readpattern= Print the contents of the named pattern to the terminal
|
||||
-L, --listmodels List all available models
|
||||
-x, --listcontexts List all contexts
|
||||
-X, --listsessions List all sessions
|
||||
-U, --updatepatterns Update patterns
|
||||
-c, --copy Copy to clipboard
|
||||
-m, --model= Choose model
|
||||
-V, --vendor= Specify vendor for chosen model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)
|
||||
-V, --vendor= Specify vendor for the selected model (e.g., -V "LM Studio" -m
|
||||
openai/gpt-oss-20b)
|
||||
--modelContextLength= Model context length (only affects ollama)
|
||||
-o, --output= Output to file
|
||||
--output-session Output the entire session (also a temporary one) to the output file
|
||||
-n, --latest= Number of latest patterns to list (default: 0)
|
||||
-n, --latest= Number of latest patterns to list
|
||||
-d, --changeDefaultModel Change default model
|
||||
-y, --youtube= YouTube video or play list "URL" to grab transcript, comments from it
|
||||
and send to chat or print it put to the console and store it in the
|
||||
output file
|
||||
-y, --youtube= YouTube video or play list "URL" to grab transcript, comments from it and
|
||||
send to chat or print it put to the console and store it in the output file
|
||||
--playlist Prefer playlist over video if both ids are present in the URL
|
||||
--transcript Grab transcript from YouTube video and send to chat (it is used per
|
||||
default).
|
||||
--transcript-with-timestamps Grab transcript from YouTube video with timestamps and send to chat
|
||||
--visual Extract visual data from video using OCR and FFmpeg
|
||||
--visual-sensitivity= Tolerance for FFmpeg scene detection (0.0 - 1.0) (default: 0.4)
|
||||
--visual-fps= Extract a specific number of frames per second instead of using scene
|
||||
detection
|
||||
--comments Grab comments from YouTube video and send to chat
|
||||
--metadata Output video metadata
|
||||
-g, --language= Specify the Language Code for the chat, e.g. -g=en -g=zh
|
||||
--yt-dlp-args= Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')
|
||||
--spotify= Spotify podcast or episode URL to grab metadata from and send to chat
|
||||
-g, --language= Specify the Language Code for the chat, e.g. -g=en -g=zh -g=pt-BR -g=pt-PT
|
||||
-u, --scrape_url= Scrape website URL to markdown using Jina AI
|
||||
-q, --scrape_question= Search question using Jina AI
|
||||
-e, --seed= Seed to be used for LMM generation
|
||||
|
|
@ -710,29 +725,31 @@ Application Options:
|
|||
--liststrategies List all strategies
|
||||
--listvendors List all vendors
|
||||
--shell-complete-list Output raw list without headers/formatting (for shell completion)
|
||||
--search Enable web search tool for supported models (Anthropic, OpenAI, Gemini)
|
||||
--search Enable web search tool for supported models (Anthropic, OpenAI, Gemini, Grok)
|
||||
--search-location= Set location for web search results (e.g., 'America/Los_Angeles')
|
||||
--image-file= Save generated image to specified file path (e.g., 'output.png')
|
||||
--image-size= Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)
|
||||
--image-quality= Image quality: low, medium, high, auto (default: auto)
|
||||
--image-compression= Compression level 0-100 for JPEG/WebP formats (default: not set)
|
||||
--image-background= Background type: opaque, transparent (default: opaque, only for
|
||||
PNG/WebP)
|
||||
--image-background= Background type: opaque, transparent (default: opaque, only for PNG/WebP)
|
||||
--suppress-think Suppress text enclosed in thinking tags
|
||||
--think-start-tag= Start tag for thinking sections (default: <think>)
|
||||
--think-end-tag= End tag for thinking sections (default: </think>)
|
||||
--disable-responses-api Disable OpenAI Responses API (default: false)
|
||||
--voice= TTS voice name for supported models (e.g., Kore, Charon, Puck)
|
||||
(default: Kore)
|
||||
--transcribe-file= Audio or video file to transcribe
|
||||
--transcribe-model= Model to use for transcription (separate from chat model)
|
||||
--split-media-file Split audio/video files larger than 25MB using ffmpeg
|
||||
--voice= TTS voice name for supported models (e.g., Kore, Charon, Puck) (default:
|
||||
Kore)
|
||||
--list-gemini-voices List all available Gemini TTS voices
|
||||
--list-transcription-models List all available transcription models
|
||||
--notification Send desktop notification when command completes
|
||||
--notification-command= Custom command to run for notifications (overrides built-in
|
||||
notifications)
|
||||
--yt-dlp-args= Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')
|
||||
--thinking= Set reasoning/thinking level (e.g., off, low, medium, high, or
|
||||
numeric tokens for Anthropic or Google Gemini)
|
||||
--notification-command= Custom command to run for notifications (overrides built-in notifications)
|
||||
--thinking= Set reasoning/thinking level (e.g., off, low, medium, high, or numeric
|
||||
tokens for Anthropic or Google Gemini)
|
||||
--show-metadata Print metadata (input/output tokens) to stderr
|
||||
--debug= Set debug level (0: off, 1: basic, 2: detailed, 3: trace)
|
||||
--debug= Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
```
|
||||
|
|
@ -745,6 +762,7 @@ Use the `--debug` flag to control runtime logging:
|
|||
- `1`: basic debug info
|
||||
- `2`: detailed debugging
|
||||
- `3`: trace level
|
||||
- `4`: wire level (full request and response bodies)
|
||||
|
||||
### Dry Run Mode
|
||||
|
||||
|
|
|
|||
22
README.zh.md
22
README.zh.md
|
|
@ -131,6 +131,7 @@ Fabric 按照现实世界中的任务来组织 Prompt,允许人们在一个地
|
|||
- [macOS (Homebrew)](#macos-homebrew)
|
||||
- [Arch Linux (AUR)](#arch-linux-aur)
|
||||
- [Windows](#windows)
|
||||
- [Windows (Scoop)](#windows-scoop)
|
||||
- [从源码构建](#从源码构建)
|
||||
- [Docker](#docker)
|
||||
- [环境变量](#环境变量)
|
||||
|
|
@ -260,6 +261,12 @@ yay -S fabric-ai
|
|||
winget install danielmiessler.Fabric
|
||||
```
|
||||
|
||||
#### Windows (Scoop)
|
||||
|
||||
```bash
|
||||
scoop install fabric-ai
|
||||
```
|
||||
|
||||
### 从源码构建
|
||||
|
||||
安装 Fabric 前,请先[确保已安装 Go](https://go.dev/doc/install),然后运行:
|
||||
|
|
@ -281,13 +288,13 @@ docker run --rm -it ghcr.io/ksylvan/fabric:v1.4.305 --version
|
|||
|
||||
# 首次运行时进行配置
|
||||
mkdir -p $HOME/.fabric-config
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --setup
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest --setup
|
||||
|
||||
# 使用你的 patterns
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest -p summarize
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest -p summarize
|
||||
|
||||
# 运行 REST API 服务器
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --serve
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest --serve
|
||||
```
|
||||
|
||||
**镜像来源:**
|
||||
|
|
@ -351,7 +358,7 @@ fabric --setup
|
|||
|
||||
**OpenAI 兼容供应商:**
|
||||
|
||||
- Abacus、AIML、Cerebras、DeepSeek、DigitalOcean、GitHub Models、GrokAI、Groq、Langdock、LiteLLM、MiniMax、Mistral、Novita AI、OpenRouter、SiliconCloud、Together、Venice AI、Z AI
|
||||
- Abacus、AIML、Cerebras、DeepSeek、DigitalOcean、GitHub Models、GrokAI、Groq、Langdock、LiteLLM、MiniMax、Mistral、Novita AI、OpenRouter、SiliconCloud、Synthorai、Together、Venice AI、Z AI
|
||||
|
||||
运行 `fabric --setup` 配置首选供应商,或使用 `fabric --listvendors` 查看所有可用供应商。
|
||||
|
||||
|
|
@ -453,6 +460,12 @@ cp completions/fabric.fish ~/.config/fish/completions/
|
|||
fabric -h
|
||||
```
|
||||
|
||||
处理 YouTube 视频时,还可以使用以下视觉提取选项:
|
||||
|
||||
- `--visual`:使用 OCR 和 FFmpeg 从视频中提取视觉信息
|
||||
- `--visual-sensitivity`:设置 FFmpeg 场景检测的容差(`0.0` - `1.0`)
|
||||
- `--visual-fps`:按固定每秒帧数提取画面,而不是使用场景检测
|
||||
|
||||
将你复制的任何文本流式输入到 `fabric` 并选择你想应用的 Pattern:
|
||||
|
||||
```bash
|
||||
|
|
@ -467,6 +480,7 @@ pbpaste | fabric --pattern extract_wisdom
|
|||
- `1`:基本调试信息
|
||||
- `2`:详细调试
|
||||
- `3`:追踪级别
|
||||
- `4`:wire 级别(完整的请求和响应内容)
|
||||
|
||||
### 演习模式
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package main
|
||||
|
||||
var version = "v1.4.441"
|
||||
var version = "v1.4.478"
|
||||
|
|
|
|||
|
|
@ -191,7 +191,7 @@ Create a `.env` file next to the `generate_changelog` binary:
|
|||
|
||||
```bash
|
||||
GITHUB_TOKEN=your_github_token_here
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-sonnet-4-20250514
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=gpt-5.6-sol
|
||||
```
|
||||
|
||||
The tool automatically loads `.env` files for convenient configuration management.
|
||||
|
|
@ -226,8 +226,8 @@ The tool can generate AI-powered summaries using Fabric for more polished, profe
|
|||
# Enable AI summarization
|
||||
generate_changelog --ai-summarize
|
||||
|
||||
# Custom model (default: claude-sonnet-4-20250514)
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-opus-4 generate_changelog --ai-summarize
|
||||
# Custom model (default: claude-opus-5)
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=gpt-5.6-sol generate_changelog --ai-summarize
|
||||
```
|
||||
|
||||
### AI Summary Features
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -381,11 +381,13 @@ func (g *Generator) validatePRState(prNumber int) error {
|
|||
return fmt.Errorf("failed to fetch PR %d: %w", prNumber, err)
|
||||
}
|
||||
|
||||
if details.State != "open" {
|
||||
return fmt.Errorf("PR %d is not open (current state: %s)", prNumber, details.State)
|
||||
if details.State != "open" && !g.cfg.ClosedOK {
|
||||
return fmt.Errorf("PR %d is not open (current state: %s); use --closed-ok to process it anyway", prNumber, details.State)
|
||||
}
|
||||
|
||||
if !details.Mergeable {
|
||||
// Only check mergeability for open PRs; GitHub returns nil for closed/merged PRs
|
||||
// which would incorrectly trigger this check even when using --closed-ok
|
||||
if details.State == "open" && !details.Mergeable {
|
||||
return fmt.Errorf("PR %d is not mergeable - please resolve conflicts first", prNumber)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const DefaultSummarizeModel = "claude-sonnet-4-5"
|
||||
const DefaultSummarizeModel = "claude-opus-5"
|
||||
const MinContentLength = 256 // Minimum content length to consider for summarization
|
||||
|
||||
const prompt = `# ROLE
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@ type Config struct {
|
|||
Push bool
|
||||
SyncDB bool
|
||||
Release string
|
||||
ClosedOK bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ func init() {
|
|||
rootCmd.Flags().BoolVar(&cfg.Push, "push", false, "Enable automatic git push after creating an incoming entry")
|
||||
rootCmd.Flags().BoolVar(&cfg.SyncDB, "sync-db", false, "Synchronize and validate database integrity with git history and GitHub PRs")
|
||||
rootCmd.Flags().StringVar(&cfg.Release, "release", "", "Update GitHub release description with AI summary for version (e.g., v1.2.3)")
|
||||
rootCmd.Flags().BoolVar(&cfg.ClosedOK, "closed-ok", false, "Allow processing a PR that is already closed/merged (skips open-state check)")
|
||||
}
|
||||
|
||||
func run(cmd *cobra.Command, args []string) error {
|
||||
|
|
|
|||
|
|
@ -84,13 +84,14 @@ _fabric() {
|
|||
'(-r --raw)'{-r,--raw}'[Use the defaults of the model without sending chat options. Only affects OpenAI-compatible providers. Anthropic models always use smart parameter selection to comply with model-specific requirements.]' \
|
||||
'(-F --frequencypenalty)'{-F,--frequencypenalty}'[Set frequency penalty (default: 0.0)]:frequency penalty:' \
|
||||
'(-l --listpatterns)'{-l,--listpatterns}'[List all patterns]' \
|
||||
'(--readpattern)--readpattern[Print the contents of the named pattern to the terminal]:pattern:_fabric_patterns' \
|
||||
'(-L --listmodels)'{-L,--listmodels}'[List all available models]' \
|
||||
'(-x --listcontexts)'{-x,--listcontexts}'[List all contexts]' \
|
||||
'(-X --listsessions)'{-X,--listsessions}'[List all sessions]' \
|
||||
'(-U --updatepatterns)'{-U,--updatepatterns}'[Update patterns]' \
|
||||
'(-c --copy)'{-c,--copy}'[Copy to clipboard]' \
|
||||
'(-m --model)'{-m,--model}'[Choose model]:model:_fabric_models' \
|
||||
'(-V --vendor)'{-V,--vendor}'[Specify vendor for chosen model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)]:vendor:_fabric_vendors' \
|
||||
'(-V --vendor)'{-V,--vendor}'[Specify vendor for the selected model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)]:vendor:_fabric_vendors' \
|
||||
'(--modelContextLength)--modelContextLength[Model context length (only affects ollama)]:length:' \
|
||||
'(-o --output)'{-o,--output}'[Output to file]:file:_files' \
|
||||
'(--output-session)--output-session[Output the entire session to the output file]' \
|
||||
|
|
@ -100,6 +101,9 @@ _fabric() {
|
|||
'(--playlist)--playlist[Prefer playlist over video if both ids are present in the URL]' \
|
||||
'(--transcript)--transcript[Grab transcript from YouTube video and send to chat]' \
|
||||
'(--transcript-with-timestamps)--transcript-with-timestamps[Grab transcript from YouTube video with timestamps]' \
|
||||
'(--visual)--visual[Extract visual data from video using OCR and FFmpeg]' \
|
||||
'(--visual-sensitivity)--visual-sensitivity[Tolerance for FFmpeg scene detection (0.0 - 1.0)]:visual sensitivity:' \
|
||||
'(--visual-fps)--visual-fps[Extract a specific number of frames per second instead of using scene detection]:frames per second:' \
|
||||
'(--comments)--comments[Grab comments from YouTube video and send to chat]' \
|
||||
'(--metadata)--metadata[Output video metadata]' \
|
||||
'(--yt-dlp-args)--yt-dlp-args[Additional arguments to pass to yt-dlp]:yt-dlp args:' \
|
||||
|
|
@ -122,7 +126,7 @@ _fabric() {
|
|||
'(--api-key)--api-key[API key used to secure server routes]:api-key:' \
|
||||
'(--config)--config[Path to YAML config file]:config file:_files -g "*.yaml *.yml"' \
|
||||
'(--version)--version[Print current version]' \
|
||||
'(--search)--search[Enable web search tool for supported models (Anthropic, OpenAI, Gemini)]' \
|
||||
'(--search)--search[Enable web search tool for supported models (Anthropic, OpenAI, Gemini, Grok)]' \
|
||||
'(--search-location)--search-location[Set location for web search results]:location:' \
|
||||
'(--image-file)--image-file[Save generated image to specified file path]:image file:_files -g "*.png *.webp *.jpeg *.jpg"' \
|
||||
'(--image-size)--image-size[Image dimensions]:size:(1024x1024 1536x1024 1024x1536 auto)' \
|
||||
|
|
@ -137,6 +141,7 @@ _fabric() {
|
|||
'(--listvendors)--listvendors[List all vendors]' \
|
||||
'(--voice)--voice[TTS voice name for supported models]:voice:_fabric_gemini_voices' \
|
||||
'(--list-gemini-voices)--list-gemini-voices[List all available Gemini TTS voices]' \
|
||||
'(--list-transcription-models)--list-transcription-models[List all available transcription models]' \
|
||||
'(--shell-complete-list)--shell-complete-list[Output raw list without headers/formatting (for shell completion)]' \
|
||||
'(--suppress-think)--suppress-think[Suppress text enclosed in thinking tags]' \
|
||||
'(--think-start-tag)--think-start-tag[Start tag for thinking sections (default: <think>)]:start tag:' \
|
||||
|
|
@ -145,6 +150,7 @@ _fabric() {
|
|||
'(--transcribe-file)--transcribe-file[Audio or video file to transcribe]:audio file:_files -g "*.mp3 *.mp4 *.mpeg *.mpga *.m4a *.wav *.webm"' \
|
||||
'(--transcribe-model)--transcribe-model[Model to use for transcription (separate from chat model)]:transcribe model:_fabric_transcription_models' \
|
||||
'(--split-media-file)--split-media-file[Split audio/video files larger than 25MB using ffmpeg]' \
|
||||
'(--show-metadata)--show-metadata[Print metadata (input/output tokens) to stderr]' \
|
||||
'(--debug)--debug[Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)]:debug level:(0 1 2 3 4)' \
|
||||
'(--notification)--notification[Send desktop notification when command completes]' \
|
||||
'(--notification-command)--notification-command[Custom command to run for notifications]:notification command:' \
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ _fabric() {
|
|||
fi
|
||||
|
||||
# Define all possible options/flags
|
||||
local opts="--pattern -p --variable -v --context -C --session --attachment -a --setup -S --temperature -t --topp -T --stream -s --presencepenalty -P --raw -r --frequencypenalty -F --listpatterns -l --listmodels -L --listcontexts -x --listsessions -X --updatepatterns -U --copy -c --model -m --vendor -V --modelContextLength --output -o --output-session --latest -n --changeDefaultModel -d --youtube -y --playlist --transcript --transcript-with-timestamps --comments --metadata --yt-dlp-args --language -g --scrape_url -u --scrape_question -q --seed -e --thinking --wipecontext -w --wipesession -W --printcontext --printsession --readability --input-has-vars --no-variable-replacement --dry-run --serve --serveOllama --address --api-key --config --search --search-location --image-file --image-size --image-quality --image-compression --image-background --suppress-think --think-start-tag --think-end-tag --disable-responses-api --transcribe-file --transcribe-model --split-media-file --voice --list-gemini-voices --notification --notification-command --debug --version --listextensions --addextension --rmextension --strategy --liststrategies --listvendors --shell-complete-list --help -h"
|
||||
local opts="--pattern -p --variable -v --context -C --session --attachment -a --setup -S --temperature -t --topp -T --stream -s --presencepenalty -P --raw -r --frequencypenalty -F --listpatterns -l --readpattern --listmodels -L --listcontexts -x --listsessions -X --updatepatterns -U --copy -c --model -m --vendor -V --modelContextLength --output -o --output-session --latest -n --changeDefaultModel -d --youtube -y --playlist --transcript --transcript-with-timestamps --visual --visual-sensitivity --visual-fps --comments --metadata --yt-dlp-args --spotify --language -g --scrape_url -u --scrape_question -q --seed -e --thinking --wipecontext -w --wipesession -W --printcontext --printsession --readability --input-has-vars --no-variable-replacement --dry-run --serve --serveOllama --address --api-key --config --search --search-location --image-file --image-size --image-quality --image-compression --image-background --suppress-think --think-start-tag --think-end-tag --disable-responses-api --transcribe-file --transcribe-model --split-media-file --voice --list-gemini-voices --list-transcription-models --notification --notification-command --show-metadata --debug --version --listextensions --addextension --rmextension --strategy --liststrategies --listvendors --shell-complete-list --help -h"
|
||||
|
||||
# Helper function for dynamic completions
|
||||
_fabric_get_list() {
|
||||
|
|
@ -26,7 +26,7 @@ _fabric() {
|
|||
|
||||
# Handle completions based on the previous word
|
||||
case "${prev}" in
|
||||
-p | --pattern)
|
||||
-p | --pattern | --readpattern)
|
||||
COMPREPLY=($(compgen -W "$(_fabric_get_list --listpatterns)" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
|
|
@ -105,7 +105,7 @@ _fabric() {
|
|||
return 0
|
||||
;;
|
||||
# Options requiring simple arguments (no specific completion logic here)
|
||||
-v | --variable | -t | --temperature | -T | --topp | -P | --presencepenalty | -F | --frequencypenalty | --modelContextLength | -n | --latest | -y | --youtube | --yt-dlp-args | -g | --language | -u | --scrape_url | -q | --scrape_question | -e | --seed | --address | --api-key | --search-location | --image-compression | --think-start-tag | --think-end-tag | --notification-command)
|
||||
-v | --variable | -t | --temperature | -T | --topp | -P | --presencepenalty | -F | --frequencypenalty | --modelContextLength | -n | --latest | -y | --youtube | --visual-sensitivity | --visual-fps | --yt-dlp-args | -g | --language | -u | --scrape_url | -q | --scrape_question | -e | --seed | --address | --api-key | --search-location | --image-compression | --think-start-tag | --think-end-tag | --notification-command)
|
||||
# No specific completion suggestions, user types the value
|
||||
return 0
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -53,54 +53,69 @@ function __fabric_get_transcription_models
|
|||
end
|
||||
|
||||
# Main completion function
|
||||
#
|
||||
# Options that take a value must declare it. Use -x when only the listed values
|
||||
# are valid, and -r when a file path is also valid. Fish ignores the -a list of
|
||||
# an option that declares neither, and shows no suggestion after the option.
|
||||
function __fabric_register_completions
|
||||
set cmd $argv[1]
|
||||
complete -c $cmd -f
|
||||
|
||||
# Flag completions with arguments
|
||||
complete -c $cmd -s p -l pattern -d "Choose a pattern from the available patterns" -a "(__fabric_get_patterns)"
|
||||
complete -c $cmd -s v -l variable -d "Values for pattern variables, e.g. -v=#role:expert -v=#points:30"
|
||||
complete -c $cmd -s C -l context -d "Choose a context from the available contexts" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -l session -d "Choose a session from the available sessions" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -s a -l attachment -d "Attachment path or URL (e.g. for OpenAI image recognition messages)" -r
|
||||
complete -c $cmd -s t -l temperature -d "Set temperature (default: 0.7)"
|
||||
complete -c $cmd -s T -l topp -d "Set top P (default: 0.9)"
|
||||
complete -c $cmd -s P -l presencepenalty -d "Set presence penalty (default: 0.0)"
|
||||
complete -c $cmd -s F -l frequencypenalty -d "Set frequency penalty (default: 0.0)"
|
||||
complete -c $cmd -s m -l model -d "Choose model" -a "(__fabric_get_models)"
|
||||
complete -c $cmd -s V -l vendor -d "Specify vendor for chosen model (e.g., -V \"LM Studio\" -m openai/gpt-oss-20b)" -a "(__fabric_get_vendors)"
|
||||
complete -c $cmd -l modelContextLength -d "Model context length (only affects ollama)"
|
||||
complete -c $cmd -s o -l output -d "Output to file" -r
|
||||
complete -c $cmd -s n -l latest -d "Number of latest patterns to list (default: 0)"
|
||||
complete -c $cmd -s y -l youtube -d "YouTube video or play list URL to grab transcript, comments from it"
|
||||
complete -c $cmd -s g -l language -d "Specify the Language Code for the chat, e.g. -g=en -g=zh"
|
||||
complete -c $cmd -s u -l scrape_url -d "Scrape website URL to markdown using Jina AI"
|
||||
complete -c $cmd -s q -l scrape_question -d "Search question using Jina AI"
|
||||
complete -c $cmd -s e -l seed -d "Seed to be used for LMM generation"
|
||||
complete -c $cmd -l thinking -d "Set reasoning/thinking level" -a "off low medium high"
|
||||
complete -c $cmd -s w -l wipecontext -d "Wipe context" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -s W -l wipesession -d "Wipe session" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -l printcontext -d "Print context" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -l printsession -d "Print session" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -l address -d "The address to bind the REST API (default: :8080)"
|
||||
complete -c $cmd -l api-key -d "API key used to secure server routes"
|
||||
complete -c $cmd -l config -d "Path to YAML config file" -r -a "*.yaml *.yml"
|
||||
complete -c $cmd -l search-location -d "Set location for web search results (e.g., 'America/Los_Angeles')"
|
||||
complete -c $cmd -l image-file -d "Save generated image to specified file path (e.g., 'output.png')" -r -a "*.png *.webp *.jpeg *.jpg"
|
||||
complete -c $cmd -l image-size -d "Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)" -a "1024x1024 1536x1024 1024x1536 auto"
|
||||
complete -c $cmd -l image-quality -d "Image quality: low, medium, high, auto (default: auto)" -a "low medium high auto"
|
||||
complete -c $cmd -l image-compression -d "Compression level 0-100 for JPEG/WebP formats (default: not set)" -r
|
||||
complete -c $cmd -l image-background -d "Background type: opaque, transparent (default: opaque, only for PNG/WebP)" -a "opaque transparent"
|
||||
complete -c $cmd -l addextension -d "Register a new extension from config file path" -r -a "*.yaml *.yml"
|
||||
complete -c $cmd -l rmextension -d "Remove a registered extension by name" -a "(__fabric_get_extensions)"
|
||||
complete -c $cmd -l strategy -d "Choose a strategy from the available strategies" -a "(__fabric_get_strategies)"
|
||||
complete -c $cmd -l think-start-tag -d "Start tag for thinking sections (default: <think>)"
|
||||
complete -c $cmd -l think-end-tag -d "End tag for thinking sections (default: </think>)"
|
||||
complete -c $cmd -l voice -d "TTS voice name for supported models (e.g., Kore, Charon, Puck)" -a "(__fabric_get_gemini_voices)"
|
||||
complete -c $cmd -l transcribe-file -d "Audio or video file to transcribe" -r -a "*.mp3 *.mp4 *.mpeg *.mpga *.m4a *.wav *.webm"
|
||||
complete -c $cmd -l transcribe-model -d "Model to use for transcription (separate from chat model)" -a "(__fabric_get_transcription_models)"
|
||||
complete -c $cmd -l debug -d "Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)" -a "0 1 2 3 4"
|
||||
complete -c $cmd -l notification-command -d "Custom command to run for notifications (overrides built-in notifications)"
|
||||
# Options that take a value from a dynamic list
|
||||
complete -c $cmd -s p -l pattern -x -d "Choose a pattern from the available patterns" -a "(__fabric_get_patterns)"
|
||||
complete -c $cmd -l readpattern -x -d "Print the contents of the named pattern to the terminal" -a "(__fabric_get_patterns)"
|
||||
complete -c $cmd -s C -l context -x -d "Choose a context from the available contexts" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -l session -x -d "Choose a session from the available sessions" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -s m -l model -x -d "Choose model" -a "(__fabric_get_models)"
|
||||
complete -c $cmd -s V -l vendor -x -d "Specify vendor for the selected model (e.g., -V \"LM Studio\" -m openai/gpt-oss-20b)" -a "(__fabric_get_vendors)"
|
||||
complete -c $cmd -s w -l wipecontext -x -d "Wipe context" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -s W -l wipesession -x -d "Wipe session" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -l printcontext -x -d "Print context" -a "(__fabric_get_contexts)"
|
||||
complete -c $cmd -l printsession -x -d "Print session" -a "(__fabric_get_sessions)"
|
||||
complete -c $cmd -l rmextension -x -d "Remove a registered extension by name" -a "(__fabric_get_extensions)"
|
||||
complete -c $cmd -l strategy -x -d "Choose a strategy from the available strategies" -a "(__fabric_get_strategies)"
|
||||
complete -c $cmd -l voice -x -d "TTS voice name for supported models (e.g., Kore, Charon, Puck)" -a "(__fabric_get_gemini_voices)"
|
||||
complete -c $cmd -l transcribe-model -x -d "Model to use for transcription (separate from chat model)" -a "(__fabric_get_transcription_models)"
|
||||
|
||||
# Options that take a value from a fixed list
|
||||
complete -c $cmd -l thinking -x -d "Set reasoning/thinking level" -a "off low medium high"
|
||||
complete -c $cmd -l image-size -x -d "Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)" -a "1024x1024 1536x1024 1024x1536 auto"
|
||||
complete -c $cmd -l image-quality -x -d "Image quality: low, medium, high, auto (default: auto)" -a "low medium high auto"
|
||||
complete -c $cmd -l image-background -x -d "Background type: opaque, transparent (default: opaque, only for PNG/WebP)" -a "opaque transparent"
|
||||
complete -c $cmd -l debug -x -d "Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)" -a "0 1 2 3 4"
|
||||
|
||||
# Options that take a file path
|
||||
complete -c $cmd -s a -l attachment -r -d "Attachment path or URL (e.g. for OpenAI image recognition messages)"
|
||||
complete -c $cmd -s o -l output -r -d "Output to file"
|
||||
complete -c $cmd -l config -r -d "Path to YAML config file" -a "(__fish_complete_suffix .yaml .yml)"
|
||||
complete -c $cmd -l addextension -r -d "Register a new extension from config file path" -a "(__fish_complete_suffix .yaml .yml)"
|
||||
complete -c $cmd -l image-file -r -d "Save generated image to specified file path (e.g., 'output.png')" -a "(__fish_complete_suffix .png .webp .jpeg .jpg)"
|
||||
complete -c $cmd -l transcribe-file -r -d "Audio or video file to transcribe" -a "(__fish_complete_suffix .mp3 .mp4 .mpeg .mpga .m4a .wav .webm)"
|
||||
|
||||
# Options that take a value the user types
|
||||
complete -c $cmd -s v -l variable -x -d "Values for pattern variables, e.g. -v=#role:expert -v=#points:30"
|
||||
complete -c $cmd -s t -l temperature -x -d "Set temperature (default: 0.7)"
|
||||
complete -c $cmd -s T -l topp -x -d "Set top P (default: 0.9)"
|
||||
complete -c $cmd -s P -l presencepenalty -x -d "Set presence penalty (default: 0.0)"
|
||||
complete -c $cmd -s F -l frequencypenalty -x -d "Set frequency penalty (default: 0.0)"
|
||||
complete -c $cmd -l modelContextLength -x -d "Model context length (only affects ollama)"
|
||||
complete -c $cmd -s n -l latest -x -d "Number of latest patterns to list (default: 0)"
|
||||
complete -c $cmd -s y -l youtube -x -d "YouTube video or play list URL to grab transcript, comments from it"
|
||||
complete -c $cmd -l visual-sensitivity -x -d "Tolerance for FFmpeg scene detection (0.0 - 1.0) (default: 0.4)"
|
||||
complete -c $cmd -l visual-fps -x -d "Extract a specific number of frames per second instead of using scene detection"
|
||||
complete -c $cmd -l yt-dlp-args -x -d "Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"
|
||||
complete -c $cmd -l spotify -x -d "Spotify podcast or episode URL to grab metadata from and send to chat"
|
||||
complete -c $cmd -s g -l language -x -d "Specify the Language Code for the chat, e.g. -g=en -g=zh"
|
||||
complete -c $cmd -s u -l scrape_url -x -d "Scrape website URL to markdown using Jina AI"
|
||||
complete -c $cmd -s q -l scrape_question -x -d "Search question using Jina AI"
|
||||
complete -c $cmd -s e -l seed -x -d "Seed to be used for LMM generation"
|
||||
complete -c $cmd -l address -x -d "The address to bind the REST API (default: :8080)"
|
||||
complete -c $cmd -l api-key -x -d "API key used to secure server routes"
|
||||
complete -c $cmd -l search-location -x -d "Set location for web search results (e.g., 'America/Los_Angeles')"
|
||||
complete -c $cmd -l image-compression -x -d "Compression level 0-100 for JPEG/WebP formats (default: not set)"
|
||||
complete -c $cmd -l think-start-tag -x -d "Start tag for thinking sections (default: <think>)"
|
||||
complete -c $cmd -l think-end-tag -x -d "End tag for thinking sections (default: </think>)"
|
||||
complete -c $cmd -l notification-command -x -d "Custom command to run for notifications (overrides built-in notifications)"
|
||||
|
||||
# Boolean flags (no arguments)
|
||||
complete -c $cmd -s S -l setup -d "Run setup for all reconfigurable parts of fabric"
|
||||
|
|
@ -117,14 +132,14 @@ function __fabric_register_completions
|
|||
complete -c $cmd -l playlist -d "Prefer playlist over video if both ids are present in the URL"
|
||||
complete -c $cmd -l transcript -d "Grab transcript from YouTube video and send to chat"
|
||||
complete -c $cmd -l transcript-with-timestamps -d "Grab transcript from YouTube video with timestamps"
|
||||
complete -c $cmd -l visual -d "Extract visual data from video using OCR and FFmpeg"
|
||||
complete -c $cmd -l comments -d "Grab comments from YouTube video and send to chat"
|
||||
complete -c $cmd -l metadata -d "Output video metadata"
|
||||
complete -c $cmd -l yt-dlp-args -d "Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"
|
||||
complete -c $cmd -l readability -d "Convert HTML input into a clean, readable view"
|
||||
complete -c $cmd -l input-has-vars -d "Apply variables to user input"
|
||||
complete -c $cmd -l no-variable-replacement -d "Disable pattern variable replacement"
|
||||
complete -c $cmd -l dry-run -d "Show what would be sent to the model without actually sending it"
|
||||
complete -c $cmd -l search -d "Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"
|
||||
complete -c $cmd -l search -d "Enable web search tool for supported models (Anthropic, OpenAI, Gemini, Grok)"
|
||||
complete -c $cmd -l serve -d "Serve the Fabric Rest API"
|
||||
complete -c $cmd -l serveOllama -d "Serve the Fabric Rest API with ollama endpoints"
|
||||
complete -c $cmd -l version -d "Print current version"
|
||||
|
|
@ -132,13 +147,14 @@ function __fabric_register_completions
|
|||
complete -c $cmd -l liststrategies -d "List all strategies"
|
||||
complete -c $cmd -l listvendors -d "List all vendors"
|
||||
complete -c $cmd -l list-gemini-voices -d "List all available Gemini TTS voices"
|
||||
complete -c $cmd -l list-transcription-models -d "List all available transcription models"
|
||||
complete -c $cmd -l shell-complete-list -d "Output raw list without headers/formatting (for shell completion)"
|
||||
complete -c $cmd -l suppress-think -d "Suppress text enclosed in thinking tags"
|
||||
complete -c $cmd -l disable-responses-api -d "Disable OpenAI Responses API (default: false)"
|
||||
complete -c $cmd -l split-media-file -d "Split audio/video files larger than 25MB using ffmpeg"
|
||||
complete -c $cmd -l notification -d "Send desktop notification when command completes"
|
||||
complete -c $cmd -l show-metadata -d "Print metadata (input/output tokens) to stderr"
|
||||
complete -c $cmd -s h -l help -d "Show this help message"
|
||||
complete -c $cmd -l spotify -d 'Spotify podcast or episode URL to grab metadata'
|
||||
end
|
||||
|
||||
__fabric_register_completions fabric
|
||||
|
|
|
|||
62
data/patterns/analyze_monetization_opportunities/system.md
Normal file
62
data/patterns/analyze_monetization_opportunities/system.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at identifying monetization opportunities in creator content — specifically where affiliate commerce, sponsorships, digital products, and paid communities align with audience intent and creator authority.
|
||||
|
||||
You look at content through the lens of a creator business advisor: not just what was said, but what the audience is ready to buy, what problems they're trying to solve, and where trust has already been established.
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Read the full content and identify the creator's topic, authority signals, and audience intent.
|
||||
|
||||
- Identify the primary audience archetype: buyer (ready to purchase) / learner (wants to understand) / problem-solver (has a specific need) / researcher (comparing options).
|
||||
|
||||
- Extract all monetization signals:
|
||||
- Products or services mentioned that have affiliate programs
|
||||
- Topics where the creator demonstrates enough authority to sell a course or digital product
|
||||
- Brand categories where the creator's audience has demonstrated high purchase intent
|
||||
- Recurring needs that a subscription or community could address
|
||||
|
||||
- For each opportunity, estimate:
|
||||
- Revenue type: affiliate / sponsorship / digital product / community / service
|
||||
- Effort level: low (link in description) / medium (landing page, code) / high (course, product)
|
||||
- Audience alignment: how well the opportunity fits what this audience came to consume
|
||||
- Time sensitivity: evergreen / seasonal / trending
|
||||
|
||||
- Identify the single highest-value monetization move the creator could make within 30 days.
|
||||
|
||||
# OUTPUT SECTIONS
|
||||
|
||||
## AUDIENCE INTENT
|
||||
|
||||
One paragraph: who is watching this, what do they want, and how ready are they to spend money.
|
||||
|
||||
## MONETIZATION OPPORTUNITIES
|
||||
|
||||
For each opportunity identified:
|
||||
- **Type**: affiliate / sponsorship / digital product / community / service
|
||||
- **Opportunity**: what specifically to offer or promote
|
||||
- **Revenue estimate**: rough monthly range if activated (low / medium / high — do not invent numbers)
|
||||
- **Effort**: low / medium / high
|
||||
- **Alignment**: why this audience would respond to it
|
||||
|
||||
## 30-DAY QUICK WIN
|
||||
|
||||
The single most actionable monetization move for this creator in the next 30 days, with specific next steps.
|
||||
|
||||
## MONETIZATION GAPS
|
||||
|
||||
What this content is leaving on the table — missed opportunities the creator is not currently capturing.
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Only output Markdown.
|
||||
- Do not invent specific revenue numbers — use low / medium / high ranges with brief rationale.
|
||||
- Do not output warnings or notes — only the requested sections.
|
||||
- Be specific about the opportunity — "add Amazon affiliate links for the tools mentioned" is more useful than "consider affiliate marketing."
|
||||
- Prioritize opportunities that require the least effort for the highest audience alignment.
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
58
data/patterns/extract_affiliate_products/system.md
Normal file
58
data/patterns/extract_affiliate_products/system.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at extracting commercial products, tools, services, and affiliate opportunities from content transcripts. You identify every entity that a creator could earn affiliate revenue from — whether it was explicitly promoted, casually mentioned, or demonstrated in use.
|
||||
|
||||
You understand that the most valuable affiliate opportunities are often the products a creator uses without thinking to mention they're affiliated with. Your job is to surface all of them.
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Read the entire transcript to understand the topic, creator style, and audience.
|
||||
|
||||
- Identify every named product, tool, service, book, course, plant, ingredient, or brand mentioned or implied.
|
||||
|
||||
- For each entity, determine:
|
||||
- The exact name as mentioned (or inferred if clearly implied)
|
||||
- The category (tool / product / service / book / course / plant / ingredient / brand)
|
||||
- Whether it was explicitly recommended, casually mentioned, or visually demonstrated
|
||||
- The estimated affiliate commission tier (low = <5% / mid = 5-15% / high = >15%)
|
||||
- A search-ready query string for finding its affiliate program
|
||||
|
||||
- Separate entities that were explicitly sponsored (paid promotions) from organic mentions — organic mentions are often the highest-converting affiliate opportunities.
|
||||
|
||||
- Extract a short sentence for each entity explaining why an audience member would want to buy it based on how the creator presented it.
|
||||
|
||||
# OUTPUT SECTIONS
|
||||
|
||||
## SPONSORED CONTENT
|
||||
|
||||
Entities the creator was paid to promote. Format: `Name | Category | Search query | Commission tier`
|
||||
|
||||
## ORGANIC AFFILIATE OPPORTUNITIES
|
||||
|
||||
Products and tools mentioned without a paid arrangement — highest conversion potential. Format: `Name | Category | Context (why it was mentioned) | Commission tier | Search query`
|
||||
|
||||
## HIGH-CONFIDENCE BUYS
|
||||
|
||||
The 3-5 entities most likely to convert to a purchase, based on how enthusiastically or repeatedly the creator mentioned them.
|
||||
|
||||
Format: `Name | One sentence on why the audience would buy it`
|
||||
|
||||
## AFFILIATE GAPS
|
||||
|
||||
Categories or needs the creator addressed where no specific product was named — these are placement opportunities. Format: `Need described | Suggested category to fill it`
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Only output Markdown.
|
||||
- Do not output warnings, notes, or caveats — only the requested sections.
|
||||
- If a section has no entries, write "None identified."
|
||||
- Keep entity names exact — do not paraphrase brand names.
|
||||
- Do not duplicate entries across sections.
|
||||
- Commission tier is an estimate based on typical affiliate rates for the category — label it clearly as estimated.
|
||||
- Organic mentions are more valuable than sponsored ones for affiliate strategy — reflect this in your ordering.
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
62
data/patterns/extract_video_commerce_entities/system.md
Normal file
62
data/patterns/extract_video_commerce_entities/system.md
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at identifying every commercially relevant entity in a video transcript — the products shown, tools used, plants grown, books referenced, services mentioned, and brands displayed. You think like an affiliate manager reviewing content for placement opportunities.
|
||||
|
||||
You understand that video content is uniquely rich with implicit product signals: a host reaches for a specific brand of pruners, uses a particular app on screen, wears a recognizable piece of gear. You surface all of it.
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Read the full transcript and extract all named or clearly implied commercial entities.
|
||||
|
||||
- For each entity, record:
|
||||
- Name (exact as spoken, or brand inferred from description)
|
||||
- Category: tool / plant / material / book / course / service / software / apparel / food / other
|
||||
- Timestamp or approximate position (early / mid / late) if determinable from context
|
||||
- Mention type: explicit recommendation / casual use / on-screen / background / sponsored
|
||||
- Audience fit: how well this product matches what the video's audience would buy
|
||||
|
||||
- Group entities by category.
|
||||
|
||||
- Note any entities mentioned multiple times — repetition is a strong buying signal.
|
||||
|
||||
- Identify the top 5 entities by purchase likelihood.
|
||||
|
||||
# OUTPUT SECTIONS
|
||||
|
||||
## ENTITIES BY CATEGORY
|
||||
|
||||
For each category with at least one entity:
|
||||
|
||||
### [Category Name]
|
||||
- `Name` | Mention type | Position | Audience fit (high/mid/low)
|
||||
|
||||
## REPEATED MENTIONS
|
||||
|
||||
Entities mentioned more than once — strong conversion signal:
|
||||
- `Name` | Number of mentions | Why it matters
|
||||
|
||||
## TOP 5 PURCHASE CANDIDATES
|
||||
|
||||
The entities most likely to drive a sale, ranked:
|
||||
1. `Name` — [One sentence: why this audience buys this product]
|
||||
2. ...
|
||||
|
||||
## CONTENT GAPS
|
||||
|
||||
Needs the creator addressed where no product was named — affiliate placement opportunities:
|
||||
- `Need` | Suggested category
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Only output Markdown.
|
||||
- Do not output warnings or notes — only the requested sections.
|
||||
- If a section has no entries, write "None identified."
|
||||
- Keep brand names exact.
|
||||
- Audience fit is relative to the video's topic and likely viewer — assess contextually.
|
||||
- Timestamp positions are approximate — use early (0-33%), mid (33-66%), late (66-100%) if exact times aren't determinable.
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
57
data/patterns/generate_frontmatter/system.md
Normal file
57
data/patterns/generate_frontmatter/system.md
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
# IDENTITY and PURPOSE
|
||||
|
||||
You are an expert at knowledge management and note metadata. Given any text — a document, article, essay, book chapter, transcript, meeting notes, or rough draft — you generate clean, well-structured YAML frontmatter suitable for personal knowledge management (PKM) systems such as Obsidian, Logseq, or any markdown-based notes vault.
|
||||
|
||||
Your output is immediately paste-ready: valid YAML wrapped in `---` delimiters, placed at the top of the note.
|
||||
|
||||
Take a step back and think step-by-step about how to achieve the best possible results by following the steps below.
|
||||
|
||||
# STEPS
|
||||
|
||||
- Read the entire input carefully to understand its content, type, and context.
|
||||
- Infer the most accurate and descriptive title if one is not explicitly present.
|
||||
- Identify the document type (article, chapter, meeting-notes, transcript, essay, reference, idea, etc.).
|
||||
- Extract 3–8 specific, lowercase tags that describe the content. Prefer concrete concepts over vague categories. Avoid single-word generic tags like "notes" or "text".
|
||||
- Generate 1–3 aliases: alternative titles or short names someone might search for.
|
||||
- Write a one-sentence summary (15–25 words) capturing the core argument or content.
|
||||
- Identify the author or source if present; leave blank if not.
|
||||
- Use today's date or the document date if detectable; otherwise omit the date field.
|
||||
- Note the document's primary domain or area (e.g., productivity, philosophy, technology, science, history).
|
||||
|
||||
# OUTPUT
|
||||
|
||||
Output ONLY the YAML frontmatter block. No explanation, no preamble, no commentary after the block.
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: "Exact or inferred title"
|
||||
aliases:
|
||||
- "Short name"
|
||||
- "Alternative title"
|
||||
tags:
|
||||
- specific-tag
|
||||
- another-tag
|
||||
- domain/subtopic
|
||||
type: article # article | chapter | transcript | meeting-notes | essay | reference | idea | book
|
||||
author: "Author Name" # omit if unknown
|
||||
source: "" # URL or citation if available; omit if not
|
||||
date: YYYY-MM-DD # omit if not determinable
|
||||
summary: "One sentence capturing the core content or argument of this document."
|
||||
status: unprocessed # unprocessed | reading | processed | archived
|
||||
---
|
||||
```
|
||||
|
||||
# OUTPUT INSTRUCTIONS
|
||||
|
||||
- Output ONLY the YAML block — nothing before `---` and nothing after the closing `---`.
|
||||
- Use lowercase for all tags. Use hyphens for multi-word tags (e.g., `knowledge-management`, not `KnowledgeManagement`).
|
||||
- For hierarchical tags use slash notation: `philosophy/stoicism`, `technology/ai`.
|
||||
- Be specific: `decision-making` is better than `thinking`; `ancient-rome` is better than `history`.
|
||||
- The summary must be a complete sentence, not a fragment.
|
||||
- Omit fields that cannot be reasonably inferred (author, source, date) rather than guessing.
|
||||
- Do not add any fields not shown in the template above.
|
||||
- Do not give warnings or notes; only output the YAML block.
|
||||
|
||||
# INPUT
|
||||
|
||||
INPUT:
|
||||
|
|
@ -20,236 +20,240 @@
|
|||
16. **analyze_malware**: Analyse malware details, extract key indicators, techniques, and potential detection strategies, and summarize findings concisely for a malware analyst's use in identifying and responding to threats.
|
||||
17. **analyze_military_strategy**: Analyse a historical battle, offering in-depth insights into strategic decisions, strengths, weaknesses, tactical approaches, logistical factors, pivotal moments, and consequences for a comprehensive military evaluation.
|
||||
18. **analyze_mistakes**: Analyse past mistakes in thinking patterns, map them to current beliefs, and offer recommendations to improve accuracy in predictions.
|
||||
19. **analyze_paper**: Analyses research papers by summarizing findings, evaluating rigor, and assessing quality to provide insights for documentation and review.
|
||||
20. **analyze_paper_simple**: Analyzes academic papers with a focus on primary findings, research quality, and study design evaluation.
|
||||
21. **analyze_patent**: Analyse a patent's field, problem, solution, novelty, inventive step, and advantages in detail while summarizing and extracting keywords.
|
||||
22. **analyze_personality**: Performs a deep psychological analysis of a person in the input, focusing on their behavior, language, and psychological traits.
|
||||
23. **analyze_presentation**: Reviews and critiques presentations by analyzing the content, speaker's underlying goals, self-focus, and entertainment value.
|
||||
24. **analyze_product_feedback**: A prompt for analyzing and organizing user feedback by identifying themes, consolidating similar comments, and prioritizing them based on usefulness.
|
||||
25. **analyze_proposition**: Analyzes a ballot proposition by identifying its purpose, impact, arguments for and against, and relevant background information.
|
||||
26. **analyze_prose**: Evaluates writing for novelty, clarity, and prose, providing ratings, improvement recommendations, and an overall score.
|
||||
27. **analyze_prose_json**: Evaluates writing for novelty, clarity, prose, and provides ratings, explanations, improvement suggestions, and an overall score in a JSON format.
|
||||
28. **analyze_prose_pinker**: Evaluates prose based on Steven Pinker's The Sense of Style, analyzing writing style, clarity, and bad writing elements.
|
||||
29. **analyze_risk**: Conducts a risk assessment of a third-party vendor, assigning a risk score and suggesting security controls based on analysis of provided documents and vendor website.
|
||||
30. **analyze_sales_call**: Rates sales call performance across multiple dimensions, providing scores and actionable feedback based on transcript analysis.
|
||||
31. **analyze_spiritual_text**: Compares and contrasts spiritual texts by analyzing claims and differences with the King James Bible.
|
||||
32. **analyze_tech_impact**: Analyzes the societal impact, ethical considerations, and sustainability of technology projects, evaluating their outcomes and benefits.
|
||||
33. **analyze_terraform_plan**: Analyzes Terraform plan outputs to assess infrastructure changes, security risks, cost implications, and compliance considerations.
|
||||
34. **analyze_threat_report**: Extracts surprising insights, trends, statistics, quotes, references, and recommendations from cybersecurity threat reports, summarizing key findings and providing actionable information.
|
||||
35. **analyze_threat_report_cmds**: Extract and synthesize actionable cybersecurity commands from provided materials, incorporating command-line arguments and expert insights for pentesters and non-experts.
|
||||
36. **analyze_threat_report_trends**: Extract up to 50 surprising, insightful, and interesting trends from a cybersecurity threat report in markdown format.
|
||||
37. **answer_interview_question**: Generates concise, tailored responses to technical interview questions, incorporating alternative approaches and evidence to demonstrate the candidate's expertise and experience.
|
||||
38. **apply_ul_tags**: Apply standardized content tags to categorize topics like AI, cybersecurity, politics, and culture.
|
||||
39. **ask_secure_by_design_questions**: Generates a set of security-focused questions to ensure a project is built securely by design, covering key components and considerations.
|
||||
40. **ask_uncle_duke**: Coordinates a team of AI agents to research and produce multiple software development solutions based on provided specifications, and conducts detailed code reviews to ensure adherence to best practices.
|
||||
41. **audit_consent**: Evaluates whether consent in interactions or agreements is genuine or manufactured by analyzing power asymmetries, information gaps, alternatives, and coercion using a five-test framework.
|
||||
42. **audit_transparency**: Audits decisions, systems, and algorithms for explainability across five dimensions, assessing whether opacity is justified or serves to conceal harm from affected parties.
|
||||
43. **capture_thinkers_work**: Analyze philosophers or philosophies and provide detailed summaries about their teachings, background, works, advice, and related concepts in a structured template.
|
||||
44. **check_agreement**: Analyze contracts and agreements to identify important stipulations, issues, and potential gotchas, then summarize them in Markdown.
|
||||
45. **check_falsifiability**: Evaluates whether claims, definitions, frameworks, and arguments meet the standard of falsifiability — whether they can be tested and potentially proven wrong.
|
||||
46. **clean_text**: Fix broken or malformatted text by correcting line breaks, punctuation, capitalization, and paragraphs without altering content or spelling.
|
||||
47. **coding_master**: Explain a coding concept to a beginner, providing examples, and formatting code in markdown with specific output sections like ideas, recommendations, facts, and insights.
|
||||
48. **compare_and_contrast**: Compare and contrast a list of items in a markdown table, with items on the left and topics on top.
|
||||
49. **concall_summary**: Analyzes earnings and conference call transcripts to extract management commentary, analyst Q&A, financial insights, risks, and executive summaries.
|
||||
50. **convert_to_markdown**: Convert content to clean, complete Markdown format, preserving all original structure, formatting, links, and code blocks without alterations.
|
||||
51. **create_5_sentence_summary**: Create concise summaries or answers to input at 5 different levels of depth, from 5 words to 1 word.
|
||||
52. **create_academic_paper**: Generate a high-quality academic paper in LaTeX format with clear concepts, structured content, and a professional layout.
|
||||
53. **create_ai_jobs_analysis**: Analyze job categories' susceptibility to automation, identify resilient roles, and provide strategies for personal adaptation to AI-driven changes in the workforce.
|
||||
54. **create_aphorisms**: Find and generate a list of brief, witty statements.
|
||||
55. **create_art_prompt**: Generates a detailed, compelling visual description of a concept, including stylistic references and direct AI instructions for creating art.
|
||||
56. **create_better_frame**: Identifies and analyzes different frames of interpreting reality, emphasizing the power of positive, productive lenses in shaping outcomes.
|
||||
57. **create_bd_issue**: Transform natural language descriptions into optimal bd create commands for issue tracking.
|
||||
58. **create_coding_feature**: Generates secure and composable code features using modern technology and best practices from project specifications.
|
||||
59. **create_coding_project**: Generate wireframes and starter code for any coding ideas that you have.
|
||||
60. **create_command**: Helps determine the correct parameters and switches for penetration testing tools based on a brief description of the objective.
|
||||
61. **create_conceptmap**: Transforms unstructured text or markdown content into an interactive HTML concept map using Vis.js by extracting key concepts and their logical relationships.
|
||||
62. **create_cyber_summary**: Summarizes cybersecurity threats, vulnerabilities, incidents, and malware with a 25-word summary and categorized bullet points, after thoroughly analyzing and mapping the provided input.
|
||||
63. **create_design_system**: Create comprehensive CSS design systems with tokens, typography, spacing, and components.
|
||||
64. **create_design_document**: Creates a detailed design document for a system using the C4 model, addressing business and security postures, and including a system context diagram.
|
||||
65. **create_diy**: Creates structured "Do It Yourself" tutorial patterns by analyzing prompts, organizing requirements, and providing step-by-step instructions in Markdown format.
|
||||
66. **create_excalidraw_visualization**: Creates complex Excalidraw diagrams to visualize relationships between concepts and ideas in structured format.
|
||||
67. **create_flash_cards**: Creates flashcards for key concepts, definitions, and terms with question-answer format for educational purposes.
|
||||
68. **create_formal_email**: Crafts professional, clear, and respectful emails by analyzing context, tone, and purpose, ensuring proper structure and formatting.
|
||||
69. **create_git_diff_commit**: Generates Git commands and commit messages for reflecting changes in a repository, using conventional commits and providing concise shell commands for updates.
|
||||
70. **create_golden_rules**: Extract enforceable rules from codebases to prevent common mistakes and ensure consistency.
|
||||
71. **create_graph_from_input**: Generates a CSV file with progress-over-time data for a security program, focusing on relevant metrics and KPIs.
|
||||
72. **create_hormozi_offer**: Creates a customized business offer based on principles from Alex Hormozi's book, "$100M Offers."
|
||||
73. **create_idea_compass**: Organizes and structures ideas by exploring their definition, evidence, sources, and related themes or consequences.
|
||||
74. **create_investigation_visualization**: Creates detailed Graphviz visualizations of complex input, highlighting key aspects and providing clear, well-annotated diagrams for investigative analysis and conclusions.
|
||||
75. **create_keynote**: Creates TED-style keynote presentations with a clear narrative, structured slides, and speaker notes, emphasizing impactful takeaways and cohesive flow.
|
||||
76. **create_loe_document**: Creates detailed Level of Effort documents for estimating work effort, resources, and costs for tasks or projects.
|
||||
77. **create_logo**: Creates simple, minimalist company logos without text, generating AI prompts for vector graphic logos based on input.
|
||||
78. **create_markmap_visualization**: Transforms complex ideas into clear visualizations using MarkMap syntax, simplifying concepts into diagrams with relationships, boxes, arrows, and labels.
|
||||
79. **create_mermaid_visualization**: Creates detailed, standalone visualizations of concepts using Mermaid (Markdown) syntax, ensuring clarity and coherence in diagrams.
|
||||
80. **create_mermaid_visualization_for_github**: Creates standalone, detailed visualizations using Mermaid (Markdown) syntax to effectively explain complex concepts, ensuring clarity and precision.
|
||||
81. **create_micro_summary**: Summarizes content into a concise, 20-word summary with main points and takeaways, formatted in Markdown.
|
||||
82. **create_mnemonic_phrases**: Creates memorable mnemonic sentences from given words to aid in memory retention and learning.
|
||||
83. **create_network_threat_landscape**: Analyzes open ports and services from a network scan and generates a comprehensive, insightful, and detailed security threat report in Markdown.
|
||||
84. **create_newsletter_entry**: Condenses provided article text into a concise, objective, newsletter-style summary with a title in the style of Frontend Weekly.
|
||||
85. **create_npc**: Generates a detailed D&D 5E NPC, including background, flaws, stats, appearance, personality, goals, and more in Markdown format.
|
||||
86. **create_pattern**: Extracts, organizes, and formats LLM/AI prompts into structured sections, detailing the AI's role, instructions, output format, and any provided examples for clarity and accuracy.
|
||||
87. **create_prd**: Creates a precise Product Requirements Document (PRD) in Markdown based on input.
|
||||
88. **create_prediction_block**: Extracts and formats predictions from input into a structured Markdown block for a blog post.
|
||||
89. **create_quiz**: Creates a three-phase reading plan based on an author or topic to help the user become significantly knowledgeable, including core, extended, and supplementary readings.
|
||||
90. **create_reading_plan**: Generates review questions based on learning objectives from the input, adapted to the specified student level, and outputs them in a clear markdown format.
|
||||
91. **create_recursive_outline**: Breaks down complex tasks or projects into manageable, hierarchical components with recursive outlining for clarity and simplicity.
|
||||
92. **create_report_finding**: Creates a detailed, structured security finding report in markdown, including sections on Description, Risk, Recommendations, References, One-Sentence-Summary, and Quotes.
|
||||
93. **create_rpg_summary**: Summarizes an in-person RPG session with key events, combat details, player stats, and role-playing highlights in a structured format.
|
||||
94. **create_security_update**: Creates concise security updates for newsletters, covering stories, threats, advisories, vulnerabilities, and a summary of key issues.
|
||||
95. **create_show_intro**: Creates compelling short intros for podcasts, summarizing key topics and themes discussed in the episode.
|
||||
96. **create_sigma_rules**: Extracts Tactics, Techniques, and Procedures (TTPs) from security news and converts them into Sigma detection rules for host-based detections.
|
||||
97. **create_slides**: Transforms content into engaging Reveal.js HTML slideshows with minimal text, using inline SVG illustrations, charts, and diagrams to visually support the presenter's narrative.
|
||||
98. **create_story_about_people_interaction**: Analyze two personas, compare their dynamics, and craft a realistic, character-driven story from those insights.
|
||||
99. **create_story_about_person**: Creates compelling, realistic short stories based on psychological profiles, showing how characters navigate everyday problems using strategies consistent with their personality traits.
|
||||
100. **create_story_explanation**: Summarizes complex content in a clear, approachable story format that makes the concepts easy to understand.
|
||||
101. **create_stride_threat_model**: Create a STRIDE-based threat model for a system design, identifying assets, trust boundaries, data flows, and prioritizing threats with mitigations.
|
||||
102. **create_summary**: Summarizes content into a 20-word sentence, 10 main points (16 words max), and 5 key takeaways in Markdown format.
|
||||
103. **create_tags**: Identifies at least 5 tags from text content for mind mapping tools, including authors and existing tags if present.
|
||||
104. **create_threat_scenarios**: Identifies likely attack methods for any system by providing a narrative-based threat model, balancing risk and opportunity.
|
||||
105. **create_ttrc_graph**: Creates a CSV file showing the progress of Time to Remediate Critical Vulnerabilities over time using given data.
|
||||
106. **create_ttrc_narrative**: Creates a persuasive narrative highlighting progress in reducing the Time to Remediate Critical Vulnerabilities metric over time.
|
||||
107. **create_upgrade_pack**: Extracts world model and task algorithm updates from content, providing beliefs about how the world works and task performance.
|
||||
108. **create_user_story**: Writes concise and clear technical user stories for new features in complex software programs, formatted for all stakeholders.
|
||||
109. **create_video_chapters**: Extracts interesting topics and timestamps from a transcript, providing concise summaries of key moments.
|
||||
110. **create_visualization**: Transforms complex ideas into visualizations using intricate ASCII art, simplifying concepts where necessary.
|
||||
111. **detect_mind_virus**: Detects "mind viruses" — ideas or belief systems that spread by exploiting cognitive shortcuts (fear, guilt, identity) while resisting correction through logic or evidence.
|
||||
112. **detect_silent_victims**: Analyzes actions, policies, or systems to identify parties harmed but unable to speak up — future generations, voiceless groups, unaware individuals, diffuse populations, or structural victims.
|
||||
113. **dialog_with_socrates**: Engages in deep, meaningful dialogues to explore and challenge beliefs using the Socratic method.
|
||||
114. **enrich_blog_post**: Enhances Markdown blog files by applying instructions to improve structure, visuals, and readability for HTML rendering.
|
||||
115. **explain_code**: Explains code, security tool output, configuration text, and answers questions based on the provided input.
|
||||
116. **explain_docs**: Improves and restructures tool documentation into clear, concise instructions, including overviews, usage, use cases, and key features.
|
||||
117. **explain_math**: Helps you understand mathematical concepts in a clear and engaging way.
|
||||
118. **explain_project**: Summarizes project documentation into clear, concise sections covering the project, problem, solution, installation, usage, and examples.
|
||||
119. **explain_terms**: Produces a glossary of advanced terms from content, providing a definition, analogy, and explanation of why each term matters.
|
||||
120. **explain_terms_and_conditions**: Analyzes Terms and Conditions and legal agreements, translating complex legalese into plain English, identifying red flags, hidden fees, and privacy risks, with a final verdict on whether to sign.
|
||||
121. **export_data_as_csv**: Extracts and outputs all data structures from the input in properly formatted CSV data.
|
||||
122. **extract_algorithm_update_recommendations**: Extracts concise, practical algorithm update recommendations from the input and outputs them in a bulleted list.
|
||||
123. **extract_all_quotes**: Extract all inspirational and educational quotes from content including podcasts and essays.
|
||||
124. **extract_alpha**: Extracts the most novel and surprising ideas ("alpha") from content, inspired by information theory.
|
||||
125. **extract_article_wisdom**: Extracts surprising, insightful, and interesting information from content, categorizing it into sections like summary, ideas, quotes, facts, references, and recommendations.
|
||||
126. **extract_book_ideas**: Extracts and outputs 50 to 100 of the most surprising, insightful, and interesting ideas from a book's content.
|
||||
127. **extract_book_recommendations**: Extracts and outputs 50 to 100 practical, actionable recommendations from a book's content.
|
||||
128. **extract_bd_ideas**: Extract actionable ideas from content and transform into bd create commands.
|
||||
129. **extract_business_ideas**: Extracts top business ideas from content and elaborates on the best 10 with unique differentiators.
|
||||
130. **extract_characters**: Identify all characters (human and non-human), resolve their aliases and pronouns into canonical names, and produce detailed descriptions of each character's role, motivations, and interactions ranked by narrative importance.
|
||||
131. **extract_controversial_ideas**: Extracts and outputs controversial statements and supporting quotes from the input in a structured Markdown list.
|
||||
132. **extract_core_message**: Extracts and outputs a clear, concise sentence that articulates the core message of a given text or body of work.
|
||||
133. **extract_ctf_writeup**: Extracts a short writeup from a warstory-like text about a cyber security engagement.
|
||||
134. **extract_domains**: Extracts domains and URLs from content to identify sources used for articles, newsletters, and other publications.
|
||||
135. **extract_ethical_framework**: Extracts and analyzes the implicit ethical framework embedded in any prescriptive text, checking internal consistency and whether it creates unwilling victims.
|
||||
136. **extract_extraordinary_claims**: Extracts and outputs a list of extraordinary claims from conversations, focusing on scientifically disputed or false statements.
|
||||
137. **extract_ideas**: Extracts and outputs all the key ideas from input, presented as 15-word bullet points in Markdown.
|
||||
138. **extract_insights**: Extracts and outputs the most powerful and insightful ideas from text, formatted as 16-word bullet points in the INSIGHTS section, also IDEAS section.
|
||||
139. **extract_insights_dm**: Extracts and outputs all valuable insights and a concise summary of the content, including key points and topics discussed.
|
||||
140. **extract_instructions**: Extracts clear, actionable step-by-step instructions and main objectives from instructional video transcripts, organizing them into a concise list.
|
||||
141. **extract_jokes**: Extracts jokes from text content, presenting each joke with its punchline in separate bullet points.
|
||||
142. **extract_latest_video**: Extracts the latest video URL from a YouTube RSS feed and outputs the URL only.
|
||||
143. **extract_main_activities**: Extracts key events and activities from transcripts or logs, providing a summary of what happened.
|
||||
144. **extract_main_idea**: Extracts the main idea and key recommendation from the input, summarizing them in 15-word sentences.
|
||||
145. **extract_mcp_servers**: Identify and summarize Model Context Protocol (MCP) servers referenced in the input along with their key details.
|
||||
146. **extract_most_redeeming_thing**: Extracts the most redeeming aspect from an input, summarizing it in a single 15-word sentence.
|
||||
147. **extract_patterns**: Extracts and analyzes recurring, surprising, and insightful patterns from input, providing detailed analysis and advice for builders.
|
||||
148. **extract_poc**: Extracts proof of concept URLs and validation methods from security reports, providing the URL and command to run.
|
||||
149. **extract_predictions**: Extracts predictions from input, including specific details such as date, confidence level, and verification method.
|
||||
150. **extract_primary_problem**: Extracts the primary problem with the world as presented in a given text or body of work.
|
||||
151. **extract_primary_solution**: Extracts the primary solution for the world as presented in a given text or body of work.
|
||||
152. **extract_product_features**: Extracts and outputs a list of product features from the provided input in a bulleted format.
|
||||
153. **extract_questions**: Extracts and outputs all questions asked by the interviewer in a conversation or interview.
|
||||
154. **extract_recipe**: Extracts and outputs a recipe with a short meal description, ingredients with measurements, and preparation steps.
|
||||
155. **extract_recommendations**: Extracts and outputs concise, practical recommendations from a given piece of content in a bulleted list.
|
||||
156. **extract_references**: Extracts and outputs a bulleted list of references to art, stories, books, literature, and other sources from content.
|
||||
157. **extract_skills**: Extracts and classifies skills from a job description into a table, separating each skill and classifying it as either hard or soft.
|
||||
158. **extract_song_meaning**: Analyzes a song to provide a summary of its meaning, supported by detailed evidence from lyrics, artist commentary, and fan analysis.
|
||||
159. **extract_sponsors**: Extracts and lists official sponsors and potential sponsors from a provided transcript.
|
||||
160. **extract_videoid**: Extracts and outputs the video ID from any given URL.
|
||||
161. **extract_wisdom**: Extracts surprising, insightful, and interesting information from text on topics like human flourishing, AI, learning, and more.
|
||||
162. **extract_wisdom_agents**: Extracts valuable insights, ideas, quotes, and references from content, emphasizing topics like human flourishing, AI, learning, and technology.
|
||||
163. **extract_wisdom_with_attribution**: Extracts insightful ideas and recommendations with speaker attribution for quotes, focusing on life wisdom and human flourishing.
|
||||
164. **extract_wisdom_dm**: Extracts all valuable, insightful, and thought-provoking information from content, focusing on topics like human flourishing, AI, learning, and technology.
|
||||
165. **extract_wisdom_nometa**: Extracts insights, ideas, quotes, habits, facts, references, and recommendations from content, focusing on human flourishing, AI, technology, and related topics.
|
||||
166. **find_female_life_partner**: Analyzes criteria for finding a female life partner and provides clear, direct, and poetic descriptions.
|
||||
167. **find_hidden_message**: Extracts overt and hidden political messages, justifications, audience actions, and a cynical analysis from content.
|
||||
168. **find_logical_fallacies**: Identifies and analyzes fallacies in arguments, classifying them as formal or informal with detailed reasoning.
|
||||
169. **fix_typos**: Proofreads and corrects typos, spelling, grammar, and punctuation errors in text.
|
||||
170. **generate_code_rules**: Compile best-practice coding rules and guardrails for AI-assisted development workflows from the provided content.
|
||||
171. **get_wow_per_minute**: Determines the wow-factor of content per minute based on surprise, novelty, insight, value, and wisdom, measuring how rewarding the content is for the viewer.
|
||||
172. **greybeard_secure_prompt_engineer**: Creates secure, production-grade system prompts with NASA-style mission assurance, outputting hardened prompts, injection test suites, and evaluation rubrics.
|
||||
173. **heal_person**: Develops a comprehensive plan for spiritual and mental healing based on psychological profiles, providing personalized recommendations for mental health improvement and overall life enhancement.
|
||||
174. **humanize**: Rewrites AI-generated text to sound natural, conversational, and easy to understand, maintaining clarity and simplicity.
|
||||
175. **identify_dsrp_distinctions**: Encourages creative, systems-based thinking by exploring distinctions, boundaries, and their implications, drawing on insights from prominent systems thinkers.
|
||||
176. **identify_dsrp_perspectives**: Explores the concept of distinctions in systems thinking, focusing on how boundaries define ideas, influence understanding, and reveal or obscure insights.
|
||||
177. **identify_dsrp_relationships**: Encourages exploration of connections, distinctions, and boundaries between ideas, inspired by systems thinkers to reveal new insights and patterns in complex systems.
|
||||
178. **identify_dsrp_systems**: Encourages organizing ideas into systems of parts and wholes, inspired by systems thinkers to explore relationships and how changes in organization impact meaning and understanding.
|
||||
179. **identify_job_stories**: Identifies key job stories or requirements for roles.
|
||||
180. **improve_academic_writing**: Refines text into clear, concise academic language while improving grammar, coherence, and clarity, with a list of changes.
|
||||
181. **improve_prompt**: Improves an LLM/AI prompt by applying expert prompt writing strategies for better results and clarity.
|
||||
182. **improve_report_finding**: Improves a penetration test security finding by providing detailed descriptions, risks, recommendations, references, quotes, and a concise summary in markdown format.
|
||||
183. **improve_writing**: Refines text by correcting grammar, enhancing style, improving clarity, and maintaining the original meaning. skills.
|
||||
184. **judge_output**: Evaluates Honeycomb queries by judging their effectiveness, providing critiques and outcomes based on language nuances and analytics relevance.
|
||||
185. **label_and_rate**: Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
|
||||
186. **md_callout**: Classifies content and generates a markdown callout based on the provided text, selecting the most appropriate type.
|
||||
187. **model_as_sherlock_freud**: Builds psychological models using detective reasoning and psychoanalytic insight to understand human behavior.
|
||||
188. **official_pattern_template**: Template to use if you want to create new fabric patterns.
|
||||
189. **predict_person_actions**: Predicts behavioral responses based on psychological profiles and challenges.
|
||||
190. **prepare_7s_strategy**: Prepares a comprehensive briefing document from 7S's strategy capturing organizational profile, strategic elements, and market dynamics with clear, concise, and organized content.
|
||||
191. **provide_guidance**: Provides psychological and life coaching advice, including analysis, recommendations, and potential diagnoses, with a compassionate and honest tone.
|
||||
192. **rate_ai_response**: Rates the quality of AI responses by comparing them to top human expert performance, assigning a letter grade, reasoning, and providing a 1-100 score based on the evaluation.
|
||||
193. **rate_ai_result**: Assesses the quality of AI/ML/LLM work by deeply analyzing content, instructions, and output, then rates performance based on multiple dimensions, including coverage, creativity, and interdisciplinary thinking.
|
||||
194. **rate_content**: Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
|
||||
195. **rate_value**: Produces the best possible output by deeply analyzing and understanding the input and its intended purpose.
|
||||
196. **raw_query**: Fully digests and contemplates the input to produce the best possible result based on understanding the sender's intent.
|
||||
197. **recommend_artists**: Recommends a personalized festival schedule with artists aligned to your favorite styles and interests, including rationale.
|
||||
198. **recommend_pipeline_upgrades**: Optimizes vulnerability-checking pipelines by incorporating new information and improving their efficiency, with detailed explanations of changes.
|
||||
199. **recommend_talkpanel_topics**: Produces a clean set of proposed talks or panel talking points for a person based on their interests and goals, formatted for submission to a conference organizer.
|
||||
200. **recommend_yoga_practice**: Provides personalized yoga sequences, meditation guidance, and holistic lifestyle advice based on individual profiles.
|
||||
201. **refine_design_document**: Refines a design document based on a design review by analyzing, mapping concepts, and implementing changes using valid Markdown.
|
||||
202. **review_design**: Reviews and analyzes architecture design, focusing on clarity, component design, system integrations, security, performance, scalability, and data management.
|
||||
203. **review_code**: Performs a comprehensive code review, providing detailed feedback on correctness, security, and performance.
|
||||
204. **sanitize_broken_html_to_markdown**: Converts messy HTML into clean, properly formatted Markdown, applying custom styling and ensuring compatibility with Vite.
|
||||
205. **suggest_pattern**: Suggests appropriate fabric patterns or commands based on user input, providing clear explanations and options for users.
|
||||
206. **suggest_gt_command**: Suggest optimal Gas Town (GT) commands based on user intent and task description.
|
||||
207. **suggest_openclaw_pattern**: Suggests the most appropriate Openclaw CLI command based on user intent, mapping natural language requests to commands for messaging, device management, scheduling, and automation.
|
||||
208. **summarize**: Summarizes content into a 20-word sentence, main points, and takeaways, formatted with numbered lists in Markdown.
|
||||
209. **summarize_board_meeting**: Creates formal meeting notes from board meeting transcripts for corporate governance documentation.
|
||||
210. **summarize_debate**: Summarizes debates, identifies primary disagreement, extracts arguments, and provides analysis of evidence and argument strength to predict outcomes.
|
||||
211. **summarize_git_changes**: Summarizes recent project updates from the last 7 days, focusing on key changes with enthusiasm.
|
||||
212. **summarize_git_diff**: Summarizes and organizes Git diff changes with clear, succinct commit messages and bullet points.
|
||||
213. **summarize_lecture**: Extracts relevant topics, definitions, and tools from lecture transcripts, providing structured summaries with timestamps and key takeaways.
|
||||
214. **summarize_legislation**: Summarizes complex political proposals and legislation by analyzing key points, proposed changes, and providing balanced, positive, and cynical characterizations.
|
||||
215. **summarize_meeting**: Analyzes meeting transcripts to extract a structured summary, including an overview, key points, tasks, decisions, challenges, timeline, references, and next steps.
|
||||
216. **summarize_micro**: Summarizes content into a 20-word sentence, 3 main points, and 3 takeaways, formatted in clear, concise Markdown.
|
||||
217. **summarize_newsletter**: Extracts the most meaningful, interesting, and useful content from a newsletter, summarizing key sections such as content, opinions, tools, companies, and follow-up items in clear, structured Markdown.
|
||||
218. **summarize_paper**: Summarizes an academic paper by detailing its title, authors, technical approach, distinctive features, experimental setup, results, advantages, limitations, and conclusion in a clear, structured format using human-readable Markdown.
|
||||
219. **summarize_prompt**: Summarizes AI chat prompts by describing the primary function, unique approach, and expected output in a concise paragraph. The summary is focused on the prompt's purpose without unnecessary details or formatting.
|
||||
220. **summarize_pull-requests**: Summarizes pull requests for a coding project by providing a summary and listing the top PRs with human-readable descriptions.
|
||||
221. **summarize_rpg_session**: Summarizes a role-playing game session by extracting key events, combat stats, character changes, quotes, and more.
|
||||
222. **t_analyze_challenge_handling**: Provides 8-16 word bullet points evaluating how well challenges are being addressed, calling out any lack of effort.
|
||||
223. **t_check_dunning_kruger**: Assess narratives for Dunning-Kruger patterns by contrasting self-perception with demonstrated competence and confidence cues.
|
||||
224. **t_check_metrics**: Analyzes deep context from the TELOS file and input instruction, then provides a wisdom-based output while considering metrics and KPIs to assess recent improvements.
|
||||
225. **t_create_h3_career**: Summarizes context and produces wisdom-based output by deeply analyzing both the TELOS File and the input instruction, considering the relationship between the two.
|
||||
226. **t_create_opening_sentences**: Describes from TELOS file the person's identity, goals, and actions in 4 concise, 32-word bullet points, humbly.
|
||||
227. **t_describe_life_outlook**: Describes from TELOS file a person's life outlook in 5 concise, 16-word bullet points.
|
||||
228. **t_extract_intro_sentences**: Summarizes from TELOS file a person's identity, work, and current projects in 5 concise and grounded bullet points.
|
||||
229. **t_extract_panel_topics**: Creates 5 panel ideas with titles and descriptions based on deep context from a TELOS file and input.
|
||||
230. **t_find_blindspots**: Identify potential blindspots in thinking, frames, or models that may expose the individual to error or risk.
|
||||
231. **t_find_negative_thinking**: Analyze a TELOS file and input to identify negative thinking in documents or journals, followed by tough love encouragement.
|
||||
232. **t_find_neglected_goals**: Analyze a TELOS file and input instructions to identify goals or projects that have not been worked on recently.
|
||||
233. **t_give_encouragement**: Analyze a TELOS file and input instructions to evaluate progress, provide encouragement, and offer recommendations for continued effort.
|
||||
234. **t_red_team_thinking**: Analyze a TELOS file and input instructions to red-team thinking, models, and frames, then provide recommendations for improvement.
|
||||
235. **t_threat_model_plans**: Analyze a TELOS file and input instructions to create threat models for a life plan and recommend improvements.
|
||||
236. **t_visualize_mission_goals_projects**: Analyze a TELOS file and input instructions to create an ASCII art diagram illustrating the relationship of missions, goals, and projects.
|
||||
237. **t_year_in_review**: Analyze a TELOS file to create insights about a person or entity, then summarize accomplishments and visualizations in bullet points.
|
||||
238. **to_flashcards**: Create Anki flashcards from a given text, focusing on concise, optimized questions and answers without external context.
|
||||
239. **transcribe_minutes**: Extracts (from meeting transcription) meeting minutes, identifying actionables, insightful ideas, decisions, challenges, and next steps in a structured format.
|
||||
240. **translate**: Translates sentences or documentation into the specified language code while maintaining the original formatting and tone.
|
||||
241. **tweet**: Provides a step-by-step guide on crafting engaging tweets with emojis, covering Twitter basics, account creation, features, and audience targeting.
|
||||
242. **ultimate_law_safety**: Evaluates actions, policies, or systems against the Ultimate Law framework — a minimal, falsifiable ethical constraint that prohibits creating unwilling victims.
|
||||
243. **write_essay**: Writes essays in the style of a specified author, embodying their unique voice, vocabulary, and approach. Uses `author_name` variable.
|
||||
244. **write_essay_pg**: Writes concise, clear essays in the style of Paul Graham, focusing on simplicity, clarity, and illumination of the provided topic.
|
||||
245. **write_hackerone_report**: Generates concise, clear, and reproducible bug bounty reports, detailing vulnerability impact, steps to reproduce, and exploit details for triagers.
|
||||
246. **write_latex**: Generates syntactically correct LaTeX code for a new.tex document, ensuring proper formatting and compatibility with pdflatex.
|
||||
247. **write_micro_essay**: Writes concise, clear, and illuminating essays on the given topic in the style of Paul Graham.
|
||||
248. **write_nuclei_template_rule**: Generates Nuclei YAML templates for detecting vulnerabilities using HTTP requests, matchers, extractors, and dynamic data extraction.
|
||||
249. **write_pull-request**: Drafts detailed pull request descriptions, explaining changes, providing reasoning, and identifying potential bugs from the git diff command output.
|
||||
250. **write_semgrep_rule**: Creates accurate and working Semgrep rules based on input, following syntax guidelines and specific language considerations.
|
||||
251. **youtube_summary**: Create concise, timestamped Youtube video summaries that highlight key points.
|
||||
19. **analyze_monetization_opportunities**: Identifies monetization opportunities in creator content by aligning affiliate commerce, sponsorships, digital products, and communities with audience intent, effort level, and time sensitivity.
|
||||
20. **analyze_paper**: Analyses research papers by summarizing findings, evaluating rigor, and assessing quality to provide insights for documentation and review.
|
||||
21. **analyze_paper_simple**: Analyzes academic papers with a focus on primary findings, research quality, and study design evaluation.
|
||||
22. **analyze_patent**: Analyse a patent's field, problem, solution, novelty, inventive step, and advantages in detail while summarizing and extracting keywords.
|
||||
23. **analyze_personality**: Performs a deep psychological analysis of a person in the input, focusing on their behavior, language, and psychological traits.
|
||||
24. **analyze_presentation**: Reviews and critiques presentations by analyzing the content, speaker's underlying goals, self-focus, and entertainment value.
|
||||
25. **analyze_product_feedback**: A prompt for analyzing and organizing user feedback by identifying themes, consolidating similar comments, and prioritizing them based on usefulness.
|
||||
26. **analyze_proposition**: Analyzes a ballot proposition by identifying its purpose, impact, arguments for and against, and relevant background information.
|
||||
27. **analyze_prose**: Evaluates writing for novelty, clarity, and prose, providing ratings, improvement recommendations, and an overall score.
|
||||
28. **analyze_prose_json**: Evaluates writing for novelty, clarity, prose, and provides ratings, explanations, improvement suggestions, and an overall score in a JSON format.
|
||||
29. **analyze_prose_pinker**: Evaluates prose based on Steven Pinker's The Sense of Style, analyzing writing style, clarity, and bad writing elements.
|
||||
30. **analyze_risk**: Conducts a risk assessment of a third-party vendor, assigning a risk score and suggesting security controls based on analysis of provided documents and vendor website.
|
||||
31. **analyze_sales_call**: Rates sales call performance across multiple dimensions, providing scores and actionable feedback based on transcript analysis.
|
||||
32. **analyze_spiritual_text**: Compares and contrasts spiritual texts by analyzing claims and differences with the King James Bible.
|
||||
33. **analyze_tech_impact**: Analyzes the societal impact, ethical considerations, and sustainability of technology projects, evaluating their outcomes and benefits.
|
||||
34. **analyze_terraform_plan**: Analyzes Terraform plan outputs to assess infrastructure changes, security risks, cost implications, and compliance considerations.
|
||||
35. **analyze_threat_report**: Extracts surprising insights, trends, statistics, quotes, references, and recommendations from cybersecurity threat reports, summarizing key findings and providing actionable information.
|
||||
36. **analyze_threat_report_cmds**: Extract and synthesize actionable cybersecurity commands from provided materials, incorporating command-line arguments and expert insights for pentesters and non-experts.
|
||||
37. **analyze_threat_report_trends**: Extract up to 50 surprising, insightful, and interesting trends from a cybersecurity threat report in markdown format.
|
||||
38. **answer_interview_question**: Generates concise, tailored responses to technical interview questions, incorporating alternative approaches and evidence to demonstrate the candidate's expertise and experience.
|
||||
39. **apply_ul_tags**: Apply standardized content tags to categorize topics like AI, cybersecurity, politics, and culture.
|
||||
40. **ask_secure_by_design_questions**: Generates a set of security-focused questions to ensure a project is built securely by design, covering key components and considerations.
|
||||
41. **ask_uncle_duke**: Coordinates a team of AI agents to research and produce multiple software development solutions based on provided specifications, and conducts detailed code reviews to ensure adherence to best practices.
|
||||
42. **audit_consent**: Evaluates whether consent in interactions or agreements is genuine or manufactured by analyzing power asymmetries, information gaps, alternatives, and coercion using a five-test framework.
|
||||
43. **audit_transparency**: Audits decisions, systems, and algorithms for explainability across five dimensions, assessing whether opacity is justified or serves to conceal harm from affected parties.
|
||||
44. **capture_thinkers_work**: Analyze philosophers or philosophies and provide detailed summaries about their teachings, background, works, advice, and related concepts in a structured template.
|
||||
45. **check_agreement**: Analyze contracts and agreements to identify important stipulations, issues, and potential gotchas, then summarize them in Markdown.
|
||||
46. **check_falsifiability**: Evaluates whether claims, definitions, frameworks, and arguments meet the standard of falsifiability — whether they can be tested and potentially proven wrong.
|
||||
47. **clean_text**: Fix broken or malformatted text by correcting line breaks, punctuation, capitalization, and paragraphs without altering content or spelling.
|
||||
48. **coding_master**: Explain a coding concept to a beginner, providing examples, and formatting code in markdown with specific output sections like ideas, recommendations, facts, and insights.
|
||||
49. **compare_and_contrast**: Compare and contrast a list of items in a markdown table, with items on the left and topics on top.
|
||||
50. **concall_summary**: Analyzes earnings and conference call transcripts to extract management commentary, analyst Q&A, financial insights, risks, and executive summaries.
|
||||
51. **convert_to_markdown**: Convert content to clean, complete Markdown format, preserving all original structure, formatting, links, and code blocks without alterations.
|
||||
52. **create_5_sentence_summary**: Create concise summaries or answers to input at 5 different levels of depth, from 5 words to 1 word.
|
||||
53. **create_academic_paper**: Generate a high-quality academic paper in LaTeX format with clear concepts, structured content, and a professional layout.
|
||||
54. **create_ai_jobs_analysis**: Analyze job categories' susceptibility to automation, identify resilient roles, and provide strategies for personal adaptation to AI-driven changes in the workforce.
|
||||
55. **create_aphorisms**: Find and generate a list of brief, witty statements.
|
||||
56. **create_art_prompt**: Generates a detailed, compelling visual description of a concept, including stylistic references and direct AI instructions for creating art.
|
||||
57. **create_better_frame**: Identifies and analyzes different frames of interpreting reality, emphasizing the power of positive, productive lenses in shaping outcomes.
|
||||
58. **create_bd_issue**: Transform natural language descriptions into optimal bd create commands for issue tracking.
|
||||
59. **create_coding_feature**: Generates secure and composable code features using modern technology and best practices from project specifications.
|
||||
60. **create_coding_project**: Generate wireframes and starter code for any coding ideas that you have.
|
||||
61. **create_command**: Helps determine the correct parameters and switches for penetration testing tools based on a brief description of the objective.
|
||||
62. **create_conceptmap**: Transforms unstructured text or markdown content into an interactive HTML concept map using Vis.js by extracting key concepts and their logical relationships.
|
||||
63. **create_cyber_summary**: Summarizes cybersecurity threats, vulnerabilities, incidents, and malware with a 25-word summary and categorized bullet points, after thoroughly analyzing and mapping the provided input.
|
||||
64. **create_design_system**: Create comprehensive CSS design systems with tokens, typography, spacing, and components.
|
||||
65. **create_design_document**: Creates a detailed design document for a system using the C4 model, addressing business and security postures, and including a system context diagram.
|
||||
66. **create_diy**: Creates structured "Do It Yourself" tutorial patterns by analyzing prompts, organizing requirements, and providing step-by-step instructions in Markdown format.
|
||||
67. **create_excalidraw_visualization**: Creates complex Excalidraw diagrams to visualize relationships between concepts and ideas in structured format.
|
||||
68. **create_flash_cards**: Creates flashcards for key concepts, definitions, and terms with question-answer format for educational purposes.
|
||||
69. **create_formal_email**: Crafts professional, clear, and respectful emails by analyzing context, tone, and purpose, ensuring proper structure and formatting.
|
||||
70. **create_git_diff_commit**: Generates Git commands and commit messages for reflecting changes in a repository, using conventional commits and providing concise shell commands for updates.
|
||||
71. **create_golden_rules**: Extract enforceable rules from codebases to prevent common mistakes and ensure consistency.
|
||||
72. **create_graph_from_input**: Generates a CSV file with progress-over-time data for a security program, focusing on relevant metrics and KPIs.
|
||||
73. **create_hormozi_offer**: Creates a customized business offer based on principles from Alex Hormozi's book, "$100M Offers."
|
||||
74. **create_idea_compass**: Organizes and structures ideas by exploring their definition, evidence, sources, and related themes or consequences.
|
||||
75. **create_investigation_visualization**: Creates detailed Graphviz visualizations of complex input, highlighting key aspects and providing clear, well-annotated diagrams for investigative analysis and conclusions.
|
||||
76. **create_keynote**: Creates TED-style keynote presentations with a clear narrative, structured slides, and speaker notes, emphasizing impactful takeaways and cohesive flow.
|
||||
77. **create_loe_document**: Creates detailed Level of Effort documents for estimating work effort, resources, and costs for tasks or projects.
|
||||
78. **create_logo**: Creates simple, minimalist company logos without text, generating AI prompts for vector graphic logos based on input.
|
||||
79. **create_markmap_visualization**: Transforms complex ideas into clear visualizations using MarkMap syntax, simplifying concepts into diagrams with relationships, boxes, arrows, and labels.
|
||||
80. **create_mermaid_visualization**: Creates detailed, standalone visualizations of concepts using Mermaid (Markdown) syntax, ensuring clarity and coherence in diagrams.
|
||||
81. **create_mermaid_visualization_for_github**: Creates standalone, detailed visualizations using Mermaid (Markdown) syntax to effectively explain complex concepts, ensuring clarity and precision.
|
||||
82. **create_micro_summary**: Summarizes content into a concise, 20-word summary with main points and takeaways, formatted in Markdown.
|
||||
83. **create_mnemonic_phrases**: Creates memorable mnemonic sentences from given words to aid in memory retention and learning.
|
||||
84. **create_network_threat_landscape**: Analyzes open ports and services from a network scan and generates a comprehensive, insightful, and detailed security threat report in Markdown.
|
||||
85. **create_newsletter_entry**: Condenses provided article text into a concise, objective, newsletter-style summary with a title in the style of Frontend Weekly.
|
||||
86. **create_npc**: Generates a detailed D&D 5E NPC, including background, flaws, stats, appearance, personality, goals, and more in Markdown format.
|
||||
87. **create_pattern**: Extracts, organizes, and formats LLM/AI prompts into structured sections, detailing the AI's role, instructions, output format, and any provided examples for clarity and accuracy.
|
||||
88. **create_prd**: Creates a precise Product Requirements Document (PRD) in Markdown based on input.
|
||||
89. **create_prediction_block**: Extracts and formats predictions from input into a structured Markdown block for a blog post.
|
||||
90. **create_quiz**: Creates a three-phase reading plan based on an author or topic to help the user become significantly knowledgeable, including core, extended, and supplementary readings.
|
||||
91. **create_reading_plan**: Generates review questions based on learning objectives from the input, adapted to the specified student level, and outputs them in a clear markdown format.
|
||||
92. **create_recursive_outline**: Breaks down complex tasks or projects into manageable, hierarchical components with recursive outlining for clarity and simplicity.
|
||||
93. **create_report_finding**: Creates a detailed, structured security finding report in markdown, including sections on Description, Risk, Recommendations, References, One-Sentence-Summary, and Quotes.
|
||||
94. **create_rpg_summary**: Summarizes an in-person RPG session with key events, combat details, player stats, and role-playing highlights in a structured format.
|
||||
95. **create_security_update**: Creates concise security updates for newsletters, covering stories, threats, advisories, vulnerabilities, and a summary of key issues.
|
||||
96. **create_show_intro**: Creates compelling short intros for podcasts, summarizing key topics and themes discussed in the episode.
|
||||
97. **create_sigma_rules**: Extracts Tactics, Techniques, and Procedures (TTPs) from security news and converts them into Sigma detection rules for host-based detections.
|
||||
98. **create_slides**: Transforms content into engaging Reveal.js HTML slideshows with minimal text, using inline SVG illustrations, charts, and diagrams to visually support the presenter's narrative.
|
||||
99. **create_story_about_people_interaction**: Analyze two personas, compare their dynamics, and craft a realistic, character-driven story from those insights.
|
||||
100. **create_story_about_person**: Creates compelling, realistic short stories based on psychological profiles, showing how characters navigate everyday problems using strategies consistent with their personality traits.
|
||||
101. **create_story_explanation**: Summarizes complex content in a clear, approachable story format that makes the concepts easy to understand.
|
||||
102. **create_stride_threat_model**: Create a STRIDE-based threat model for a system design, identifying assets, trust boundaries, data flows, and prioritizing threats with mitigations.
|
||||
103. **create_summary**: Summarizes content into a 20-word sentence, 10 main points (16 words max), and 5 key takeaways in Markdown format.
|
||||
104. **create_tags**: Identifies at least 5 tags from text content for mind mapping tools, including authors and existing tags if present.
|
||||
105. **create_threat_scenarios**: Identifies likely attack methods for any system by providing a narrative-based threat model, balancing risk and opportunity.
|
||||
106. **create_ttrc_graph**: Creates a CSV file showing the progress of Time to Remediate Critical Vulnerabilities over time using given data.
|
||||
107. **create_ttrc_narrative**: Creates a persuasive narrative highlighting progress in reducing the Time to Remediate Critical Vulnerabilities metric over time.
|
||||
108. **create_upgrade_pack**: Extracts world model and task algorithm updates from content, providing beliefs about how the world works and task performance.
|
||||
109. **create_user_story**: Writes concise and clear technical user stories for new features in complex software programs, formatted for all stakeholders.
|
||||
110. **create_video_chapters**: Extracts interesting topics and timestamps from a transcript, providing concise summaries of key moments.
|
||||
111. **create_visualization**: Transforms complex ideas into visualizations using intricate ASCII art, simplifying concepts where necessary.
|
||||
112. **detect_mind_virus**: Detects "mind viruses" — ideas or belief systems that spread by exploiting cognitive shortcuts (fear, guilt, identity) while resisting correction through logic or evidence.
|
||||
113. **detect_silent_victims**: Analyzes actions, policies, or systems to identify parties harmed but unable to speak up — future generations, voiceless groups, unaware individuals, diffuse populations, or structural victims.
|
||||
114. **dialog_with_socrates**: Engages in deep, meaningful dialogues to explore and challenge beliefs using the Socratic method.
|
||||
115. **enrich_blog_post**: Enhances Markdown blog files by applying instructions to improve structure, visuals, and readability for HTML rendering.
|
||||
116. **explain_code**: Explains code, security tool output, configuration text, and answers questions based on the provided input.
|
||||
117. **explain_docs**: Improves and restructures tool documentation into clear, concise instructions, including overviews, usage, use cases, and key features.
|
||||
118. **explain_math**: Helps you understand mathematical concepts in a clear and engaging way.
|
||||
119. **explain_project**: Summarizes project documentation into clear, concise sections covering the project, problem, solution, installation, usage, and examples.
|
||||
120. **explain_terms**: Produces a glossary of advanced terms from content, providing a definition, analogy, and explanation of why each term matters.
|
||||
121. **explain_terms_and_conditions**: Analyzes Terms and Conditions and legal agreements, translating complex legalese into plain English, identifying red flags, hidden fees, and privacy risks, with a final verdict on whether to sign.
|
||||
122. **export_data_as_csv**: Extracts and outputs all data structures from the input in properly formatted CSV data.
|
||||
123. **extract_affiliate_products**: Extracts commercial products, tools, services, and brands from content transcripts, separating sponsored from organic mentions and estimating commission tiers for affiliate opportunities.
|
||||
124. **extract_algorithm_update_recommendations**: Extracts concise, practical algorithm update recommendations from the input and outputs them in a bulleted list.
|
||||
125. **extract_all_quotes**: Extract all inspirational and educational quotes from content including podcasts and essays.
|
||||
126. **extract_alpha**: Extracts the most novel and surprising ideas ("alpha") from content, inspired by information theory.
|
||||
127. **extract_article_wisdom**: Extracts surprising, insightful, and interesting information from content, categorizing it into sections like summary, ideas, quotes, facts, references, and recommendations.
|
||||
128. **extract_book_ideas**: Extracts and outputs 50 to 100 of the most surprising, insightful, and interesting ideas from a book's content.
|
||||
129. **extract_book_recommendations**: Extracts and outputs 50 to 100 practical, actionable recommendations from a book's content.
|
||||
130. **extract_bd_ideas**: Extract actionable ideas from content and transform into bd create commands.
|
||||
131. **extract_business_ideas**: Extracts top business ideas from content and elaborates on the best 10 with unique differentiators.
|
||||
132. **extract_characters**: Identify all characters (human and non-human), resolve their aliases and pronouns into canonical names, and produce detailed descriptions of each character's role, motivations, and interactions ranked by narrative importance.
|
||||
133. **extract_controversial_ideas**: Extracts and outputs controversial statements and supporting quotes from the input in a structured Markdown list.
|
||||
134. **extract_core_message**: Extracts and outputs a clear, concise sentence that articulates the core message of a given text or body of work.
|
||||
135. **extract_ctf_writeup**: Extracts a short writeup from a warstory-like text about a cyber security engagement.
|
||||
136. **extract_domains**: Extracts domains and URLs from content to identify sources used for articles, newsletters, and other publications.
|
||||
137. **extract_ethical_framework**: Extracts and analyzes the implicit ethical framework embedded in any prescriptive text, checking internal consistency and whether it creates unwilling victims.
|
||||
138. **extract_extraordinary_claims**: Extracts and outputs a list of extraordinary claims from conversations, focusing on scientifically disputed or false statements.
|
||||
139. **extract_ideas**: Extracts and outputs all the key ideas from input, presented as 15-word bullet points in Markdown.
|
||||
140. **extract_insights**: Extracts and outputs the most powerful and insightful ideas from text, formatted as 16-word bullet points in the INSIGHTS section, also IDEAS section.
|
||||
141. **extract_insights_dm**: Extracts and outputs all valuable insights and a concise summary of the content, including key points and topics discussed.
|
||||
142. **extract_instructions**: Extracts clear, actionable step-by-step instructions and main objectives from instructional video transcripts, organizing them into a concise list.
|
||||
143. **extract_jokes**: Extracts jokes from text content, presenting each joke with its punchline in separate bullet points.
|
||||
144. **extract_latest_video**: Extracts the latest video URL from a YouTube RSS feed and outputs the URL only.
|
||||
145. **extract_main_activities**: Extracts key events and activities from transcripts or logs, providing a summary of what happened.
|
||||
146. **extract_main_idea**: Extracts the main idea and key recommendation from the input, summarizing them in 15-word sentences.
|
||||
147. **extract_mcp_servers**: Identify and summarize Model Context Protocol (MCP) servers referenced in the input along with their key details.
|
||||
148. **extract_most_redeeming_thing**: Extracts the most redeeming aspect from an input, summarizing it in a single 15-word sentence.
|
||||
149. **extract_patterns**: Extracts and analyzes recurring, surprising, and insightful patterns from input, providing detailed analysis and advice for builders.
|
||||
150. **extract_poc**: Extracts proof of concept URLs and validation methods from security reports, providing the URL and command to run.
|
||||
151. **extract_predictions**: Extracts predictions from input, including specific details such as date, confidence level, and verification method.
|
||||
152. **extract_primary_problem**: Extracts the primary problem with the world as presented in a given text or body of work.
|
||||
153. **extract_primary_solution**: Extracts the primary solution for the world as presented in a given text or body of work.
|
||||
154. **extract_product_features**: Extracts and outputs a list of product features from the provided input in a bulleted format.
|
||||
155. **extract_questions**: Extracts and outputs all questions asked by the interviewer in a conversation or interview.
|
||||
156. **extract_recipe**: Extracts and outputs a recipe with a short meal description, ingredients with measurements, and preparation steps.
|
||||
157. **extract_recommendations**: Extracts and outputs concise, practical recommendations from a given piece of content in a bulleted list.
|
||||
158. **extract_references**: Extracts and outputs a bulleted list of references to art, stories, books, literature, and other sources from content.
|
||||
159. **extract_skills**: Extracts and classifies skills from a job description into a table, separating each skill and classifying it as either hard or soft.
|
||||
160. **extract_song_meaning**: Analyzes a song to provide a summary of its meaning, supported by detailed evidence from lyrics, artist commentary, and fan analysis.
|
||||
161. **extract_sponsors**: Extracts and lists official sponsors and potential sponsors from a provided transcript.
|
||||
162. **extract_video_commerce_entities**: Identifies commercially relevant entities in a video transcript — products, tools, brands, services, and more — grouped by category with mention type, repetition signals, and top purchase candidates.
|
||||
163. **extract_videoid**: Extracts and outputs the video ID from any given URL.
|
||||
164. **extract_wisdom**: Extracts surprising, insightful, and interesting information from text on topics like human flourishing, AI, learning, and more.
|
||||
165. **extract_wisdom_agents**: Extracts valuable insights, ideas, quotes, and references from content, emphasizing topics like human flourishing, AI, learning, and technology.
|
||||
166. **extract_wisdom_with_attribution**: Extracts insightful ideas and recommendations with speaker attribution for quotes, focusing on life wisdom and human flourishing.
|
||||
167. **extract_wisdom_dm**: Extracts all valuable, insightful, and thought-provoking information from content, focusing on topics like human flourishing, AI, learning, and technology.
|
||||
168. **extract_wisdom_nometa**: Extracts insights, ideas, quotes, habits, facts, references, and recommendations from content, focusing on human flourishing, AI, technology, and related topics.
|
||||
169. **find_female_life_partner**: Analyzes criteria for finding a female life partner and provides clear, direct, and poetic descriptions.
|
||||
170. **find_hidden_message**: Extracts overt and hidden political messages, justifications, audience actions, and a cynical analysis from content.
|
||||
171. **find_logical_fallacies**: Identifies and analyzes fallacies in arguments, classifying them as formal or informal with detailed reasoning.
|
||||
172. **fix_typos**: Proofreads and corrects typos, spelling, grammar, and punctuation errors in text.
|
||||
173. **generate_code_rules**: Compile best-practice coding rules and guardrails for AI-assisted development workflows from the provided content.
|
||||
174. **generate_frontmatter**: Generates paste-ready YAML frontmatter for PKM notes, with title, aliases, tags, type, summary, and status fields.
|
||||
175. **get_wow_per_minute**: Determines the wow-factor of content per minute based on surprise, novelty, insight, value, and wisdom, measuring how rewarding the content is for the viewer.
|
||||
176. **greybeard_secure_prompt_engineer**: Creates secure, production-grade system prompts with NASA-style mission assurance, outputting hardened prompts, injection test suites, and evaluation rubrics.
|
||||
177. **heal_person**: Develops a comprehensive plan for spiritual and mental healing based on psychological profiles, providing personalized recommendations for mental health improvement and overall life enhancement.
|
||||
178. **humanize**: Rewrites AI-generated text to sound natural, conversational, and easy to understand, maintaining clarity and simplicity.
|
||||
179. **identify_dsrp_distinctions**: Encourages creative, systems-based thinking by exploring distinctions, boundaries, and their implications, drawing on insights from prominent systems thinkers.
|
||||
180. **identify_dsrp_perspectives**: Explores the concept of distinctions in systems thinking, focusing on how boundaries define ideas, influence understanding, and reveal or obscure insights.
|
||||
181. **identify_dsrp_relationships**: Encourages exploration of connections, distinctions, and boundaries between ideas, inspired by systems thinkers to reveal new insights and patterns in complex systems.
|
||||
182. **identify_dsrp_systems**: Encourages organizing ideas into systems of parts and wholes, inspired by systems thinkers to explore relationships and how changes in organization impact meaning and understanding.
|
||||
183. **identify_job_stories**: Identifies key job stories or requirements for roles.
|
||||
184. **improve_academic_writing**: Refines text into clear, concise academic language while improving grammar, coherence, and clarity, with a list of changes.
|
||||
185. **improve_prompt**: Improves an LLM/AI prompt by applying expert prompt writing strategies for better results and clarity.
|
||||
186. **improve_report_finding**: Improves a penetration test security finding by providing detailed descriptions, risks, recommendations, references, quotes, and a concise summary in markdown format.
|
||||
187. **improve_writing**: Refines text by correcting grammar, enhancing style, improving clarity, and maintaining the original meaning. skills.
|
||||
188. **judge_output**: Evaluates Honeycomb queries by judging their effectiveness, providing critiques and outcomes based on language nuances and analytics relevance.
|
||||
189. **label_and_rate**: Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
|
||||
190. **md_callout**: Classifies content and generates a markdown callout based on the provided text, selecting the most appropriate type.
|
||||
191. **model_as_sherlock_freud**: Builds psychological models using detective reasoning and psychoanalytic insight to understand human behavior.
|
||||
192. **official_pattern_template**: Template to use if you want to create new fabric patterns.
|
||||
193. **predict_person_actions**: Predicts behavioral responses based on psychological profiles and challenges.
|
||||
194. **prepare_7s_strategy**: Prepares a comprehensive briefing document from 7S's strategy capturing organizational profile, strategic elements, and market dynamics with clear, concise, and organized content.
|
||||
195. **provide_guidance**: Provides psychological and life coaching advice, including analysis, recommendations, and potential diagnoses, with a compassionate and honest tone.
|
||||
196. **rate_ai_response**: Rates the quality of AI responses by comparing them to top human expert performance, assigning a letter grade, reasoning, and providing a 1-100 score based on the evaluation.
|
||||
197. **rate_ai_result**: Assesses the quality of AI/ML/LLM work by deeply analyzing content, instructions, and output, then rates performance based on multiple dimensions, including coverage, creativity, and interdisciplinary thinking.
|
||||
198. **rate_content**: Labels content with up to 20 single-word tags and rates it based on idea count and relevance to human meaning, AI, and other related themes, assigning a tier (S, A, B, C, D) and a quality score.
|
||||
199. **rate_value**: Produces the best possible output by deeply analyzing and understanding the input and its intended purpose.
|
||||
200. **raw_query**: Fully digests and contemplates the input to produce the best possible result based on understanding the sender's intent.
|
||||
201. **recommend_artists**: Recommends a personalized festival schedule with artists aligned to your favorite styles and interests, including rationale.
|
||||
202. **recommend_pipeline_upgrades**: Optimizes vulnerability-checking pipelines by incorporating new information and improving their efficiency, with detailed explanations of changes.
|
||||
203. **recommend_talkpanel_topics**: Produces a clean set of proposed talks or panel talking points for a person based on their interests and goals, formatted for submission to a conference organizer.
|
||||
204. **recommend_yoga_practice**: Provides personalized yoga sequences, meditation guidance, and holistic lifestyle advice based on individual profiles.
|
||||
205. **refine_design_document**: Refines a design document based on a design review by analyzing, mapping concepts, and implementing changes using valid Markdown.
|
||||
206. **review_design**: Reviews and analyzes architecture design, focusing on clarity, component design, system integrations, security, performance, scalability, and data management.
|
||||
207. **review_code**: Performs a comprehensive code review, providing detailed feedback on correctness, security, and performance.
|
||||
208. **sanitize_broken_html_to_markdown**: Converts messy HTML into clean, properly formatted Markdown, applying custom styling and ensuring compatibility with Vite.
|
||||
209. **suggest_pattern**: Suggests appropriate fabric patterns or commands based on user input, providing clear explanations and options for users.
|
||||
210. **suggest_gt_command**: Suggest optimal Gas Town (GT) commands based on user intent and task description.
|
||||
211. **suggest_openclaw_pattern**: Suggests the most appropriate Openclaw CLI command based on user intent, mapping natural language requests to commands for messaging, device management, scheduling, and automation.
|
||||
212. **summarize**: Summarizes content into a 20-word sentence, main points, and takeaways, formatted with numbered lists in Markdown.
|
||||
213. **summarize_board_meeting**: Creates formal meeting notes from board meeting transcripts for corporate governance documentation.
|
||||
214. **summarize_debate**: Summarizes debates, identifies primary disagreement, extracts arguments, and provides analysis of evidence and argument strength to predict outcomes.
|
||||
215. **summarize_git_changes**: Summarizes recent project updates from the last 7 days, focusing on key changes with enthusiasm.
|
||||
216. **summarize_git_diff**: Summarizes and organizes Git diff changes with clear, succinct commit messages and bullet points.
|
||||
217. **summarize_lecture**: Extracts relevant topics, definitions, and tools from lecture transcripts, providing structured summaries with timestamps and key takeaways.
|
||||
218. **summarize_legislation**: Summarizes complex political proposals and legislation by analyzing key points, proposed changes, and providing balanced, positive, and cynical characterizations.
|
||||
219. **summarize_meeting**: Analyzes meeting transcripts to extract a structured summary, including an overview, key points, tasks, decisions, challenges, timeline, references, and next steps.
|
||||
220. **summarize_micro**: Summarizes content into a 20-word sentence, 3 main points, and 3 takeaways, formatted in clear, concise Markdown.
|
||||
221. **summarize_newsletter**: Extracts the most meaningful, interesting, and useful content from a newsletter, summarizing key sections such as content, opinions, tools, companies, and follow-up items in clear, structured Markdown.
|
||||
222. **summarize_paper**: Summarizes an academic paper by detailing its title, authors, technical approach, distinctive features, experimental setup, results, advantages, limitations, and conclusion in a clear, structured format using human-readable Markdown.
|
||||
223. **summarize_prompt**: Summarizes AI chat prompts by describing the primary function, unique approach, and expected output in a concise paragraph. The summary is focused on the prompt's purpose without unnecessary details or formatting.
|
||||
224. **summarize_pull-requests**: Summarizes pull requests for a coding project by providing a summary and listing the top PRs with human-readable descriptions.
|
||||
225. **summarize_rpg_session**: Summarizes a role-playing game session by extracting key events, combat stats, character changes, quotes, and more.
|
||||
226. **t_analyze_challenge_handling**: Provides 8-16 word bullet points evaluating how well challenges are being addressed, calling out any lack of effort.
|
||||
227. **t_check_dunning_kruger**: Assess narratives for Dunning-Kruger patterns by contrasting self-perception with demonstrated competence and confidence cues.
|
||||
228. **t_check_metrics**: Analyzes deep context from the TELOS file and input instruction, then provides a wisdom-based output while considering metrics and KPIs to assess recent improvements.
|
||||
229. **t_create_h3_career**: Summarizes context and produces wisdom-based output by deeply analyzing both the TELOS File and the input instruction, considering the relationship between the two.
|
||||
230. **t_create_opening_sentences**: Describes from TELOS file the person's identity, goals, and actions in 4 concise, 32-word bullet points, humbly.
|
||||
231. **t_describe_life_outlook**: Describes from TELOS file a person's life outlook in 5 concise, 16-word bullet points.
|
||||
232. **t_extract_intro_sentences**: Summarizes from TELOS file a person's identity, work, and current projects in 5 concise and grounded bullet points.
|
||||
233. **t_extract_panel_topics**: Creates 5 panel ideas with titles and descriptions based on deep context from a TELOS file and input.
|
||||
234. **t_find_blindspots**: Identify potential blindspots in thinking, frames, or models that may expose the individual to error or risk.
|
||||
235. **t_find_negative_thinking**: Analyze a TELOS file and input to identify negative thinking in documents or journals, followed by tough love encouragement.
|
||||
236. **t_find_neglected_goals**: Analyze a TELOS file and input instructions to identify goals or projects that have not been worked on recently.
|
||||
237. **t_give_encouragement**: Analyze a TELOS file and input instructions to evaluate progress, provide encouragement, and offer recommendations for continued effort.
|
||||
238. **t_red_team_thinking**: Analyze a TELOS file and input instructions to red-team thinking, models, and frames, then provide recommendations for improvement.
|
||||
239. **t_threat_model_plans**: Analyze a TELOS file and input instructions to create threat models for a life plan and recommend improvements.
|
||||
240. **t_visualize_mission_goals_projects**: Analyze a TELOS file and input instructions to create an ASCII art diagram illustrating the relationship of missions, goals, and projects.
|
||||
241. **t_year_in_review**: Analyze a TELOS file to create insights about a person or entity, then summarize accomplishments and visualizations in bullet points.
|
||||
242. **to_flashcards**: Create Anki flashcards from a given text, focusing on concise, optimized questions and answers without external context.
|
||||
243. **transcribe_minutes**: Extracts (from meeting transcription) meeting minutes, identifying actionables, insightful ideas, decisions, challenges, and next steps in a structured format.
|
||||
244. **translate**: Translates sentences or documentation into the specified language code while maintaining the original formatting and tone.
|
||||
245. **tweet**: Provides a step-by-step guide on crafting engaging tweets with emojis, covering Twitter basics, account creation, features, and audience targeting.
|
||||
246. **ultimate_law_safety**: Evaluates actions, policies, or systems against the Ultimate Law framework — a minimal, falsifiable ethical constraint that prohibits creating unwilling victims.
|
||||
247. **write_essay**: Writes essays in the style of a specified author, embodying their unique voice, vocabulary, and approach. Uses `author_name` variable.
|
||||
248. **write_essay_pg**: Writes concise, clear essays in the style of Paul Graham, focusing on simplicity, clarity, and illumination of the provided topic.
|
||||
249. **write_hackerone_report**: Generates concise, clear, and reproducible bug bounty reports, detailing vulnerability impact, steps to reproduce, and exploit details for triagers.
|
||||
250. **write_latex**: Generates syntactically correct LaTeX code for a new.tex document, ensuring proper formatting and compatibility with pdflatex.
|
||||
251. **write_micro_essay**: Writes concise, clear, and illuminating essays on the given topic in the style of Paul Graham.
|
||||
252. **write_nuclei_template_rule**: Generates Nuclei YAML templates for detecting vulnerabilities using HTTP requests, matchers, extractors, and dynamic data extraction.
|
||||
253. **write_pull-request**: Drafts detailed pull request descriptions, explaining changes, providing reasoning, and identifying potential bugs from the git diff command output.
|
||||
254. **write_semgrep_rule**: Creates accurate and working Semgrep rules based on input, following syntax guidelines and specific language considerations.
|
||||
255. **youtube_summary**: Create concise, timestamped Youtube video summaries that highlight key points.
|
||||
|
|
|
|||
|
|
@ -73,15 +73,15 @@ Match the request to one or more of these primary categories:
|
|||
|
||||
**AI**: ai, create_ai_jobs_analysis, create_art_prompt, create_pattern, create_prediction_block, extract_mcp_servers, extract_wisdom_agents, generate_code_rules, greybeard_secure_prompt_engineer, improve_prompt, judge_output, rate_ai_response, rate_ai_result, raw_query, suggest_pattern, summarize_prompt
|
||||
|
||||
**ANALYSIS**: ai, analyze_answers, analyze_bill, analyze_bill_short, analyze_candidates, analyze_cfp_submission, analyze_claims, analyze_comments, analyze_debate, analyze_discord_structure, analyze_email_headers, analyze_incident, analyze_interviewer_techniques, analyze_logs, analyze_malware, analyze_military_strategy, analyze_mistakes, analyze_paper, analyze_paper_simple, analyze_patent, analyze_personality, analyze_presentation, analyze_product_feedback, analyze_proposition, analyze_prose, analyze_prose_json, analyze_prose_pinker, analyze_risk, analyze_sales_call, analyze_spiritual_text, analyze_tech_impact, analyze_terraform_plan, analyze_threat_report, analyze_threat_report_cmds, analyze_threat_report_trends, apply_ul_tags, audit_consent, audit_transparency, check_agreement, check_falsifiability, compare_and_contrast, concall_summary, create_ai_jobs_analysis, create_golden_rules, create_idea_compass, create_investigation_visualization, create_prediction_block, create_recursive_outline, create_story_about_people_interaction, create_tags, detect_mind_virus, detect_silent_victims, dialog_with_socrates, explain_terms_and_conditions, extract_bd_ideas, extract_ethical_framework, extract_main_idea, extract_predictions, find_hidden_message, find_logical_fallacies, get_wow_per_minute, identify_dsrp_distinctions, identify_dsrp_perspectives, identify_dsrp_relationships, identify_dsrp_systems, identify_job_stories, label_and_rate, model_as_sherlock_freud, predict_person_actions, prepare_7s_strategy, provide_guidance, rate_content, rate_value, recommend_artists, recommend_talkpanel_topics, review_design, suggest_gt_command, suggest_openclaw_pattern, summarize_board_meeting, t_analyze_challenge_handling, t_check_dunning_kruger, t_check_metrics, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_find_blindspots, t_find_negative_thinking, t_red_team_thinking, t_threat_model_plans, t_year_in_review, ultimate_law_safety, write_hackerone_report
|
||||
**ANALYSIS**: ai, analyze_answers, analyze_bill, analyze_bill_short, analyze_candidates, analyze_cfp_submission, analyze_claims, analyze_comments, analyze_debate, analyze_discord_structure, analyze_email_headers, analyze_incident, analyze_interviewer_techniques, analyze_logs, analyze_malware, analyze_military_strategy, analyze_mistakes, analyze_monetization_opportunities, analyze_paper, analyze_paper_simple, analyze_patent, analyze_personality, analyze_presentation, analyze_product_feedback, analyze_proposition, analyze_prose, analyze_prose_json, analyze_prose_pinker, analyze_risk, analyze_sales_call, analyze_spiritual_text, analyze_tech_impact, analyze_terraform_plan, analyze_threat_report, analyze_threat_report_cmds, analyze_threat_report_trends, apply_ul_tags, audit_consent, audit_transparency, check_agreement, check_falsifiability, compare_and_contrast, concall_summary, create_ai_jobs_analysis, create_golden_rules, create_idea_compass, create_investigation_visualization, create_prediction_block, create_recursive_outline, create_story_about_people_interaction, create_tags, detect_mind_virus, detect_silent_victims, dialog_with_socrates, explain_terms_and_conditions, extract_bd_ideas, extract_ethical_framework, extract_main_idea, extract_predictions, find_hidden_message, find_logical_fallacies, get_wow_per_minute, identify_dsrp_distinctions, identify_dsrp_perspectives, identify_dsrp_relationships, identify_dsrp_systems, identify_job_stories, label_and_rate, model_as_sherlock_freud, predict_person_actions, prepare_7s_strategy, provide_guidance, rate_content, rate_value, recommend_artists, recommend_talkpanel_topics, review_design, suggest_gt_command, suggest_openclaw_pattern, summarize_board_meeting, t_analyze_challenge_handling, t_check_dunning_kruger, t_check_metrics, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_find_blindspots, t_find_negative_thinking, t_red_team_thinking, t_threat_model_plans, t_year_in_review, ultimate_law_safety, write_hackerone_report
|
||||
|
||||
**BILL**: analyze_bill, analyze_bill_short
|
||||
|
||||
**BUSINESS**: analyze_discord_structure, check_agreement, concall_summary, create_ai_jobs_analysis, create_formal_email, create_hormozi_offer, create_loe_document, create_logo, create_newsletter_entry, create_prd, create_upgrade_pack, explain_project, extract_business_ideas, extract_characters, extract_product_features, extract_skills, extract_sponsors, identify_job_stories, prepare_7s_strategy, rate_value, t_check_metrics, t_create_h3_career, t_visualize_mission_goals_projects, t_year_in_review, transcribe_minutes
|
||||
**BUSINESS**: analyze_discord_structure, analyze_monetization_opportunities, check_agreement, concall_summary, create_ai_jobs_analysis, create_formal_email, create_hormozi_offer, create_loe_document, create_logo, create_newsletter_entry, create_prd, create_upgrade_pack, explain_project, extract_affiliate_products, extract_business_ideas, extract_characters, extract_product_features, extract_skills, extract_sponsors, extract_video_commerce_entities, identify_job_stories, prepare_7s_strategy, rate_value, t_check_metrics, t_create_h3_career, t_visualize_mission_goals_projects, t_year_in_review, transcribe_minutes
|
||||
|
||||
**CLASSIFICATION**: apply_ul_tags
|
||||
|
||||
**CONVERSION**: clean_text, convert_to_markdown, create_graph_from_input, create_slides, export_data_as_csv, extract_videoid, humanize, md_callout, sanitize_broken_html_to_markdown, to_flashcards, transcribe_minutes, translate, tweet, write_latex
|
||||
**CONVERSION**: clean_text, convert_to_markdown, create_graph_from_input, create_slides, export_data_as_csv, extract_videoid, generate_frontmatter, humanize, md_callout, sanitize_broken_html_to_markdown, to_flashcards, transcribe_minutes, translate, tweet, write_latex
|
||||
|
||||
**CR THINKING**: audit_consent, audit_transparency, capture_thinkers_work, check_falsifiability, create_idea_compass, create_markmap_visualization, create_upgrade_pack, detect_mind_virus, detect_silent_victims, dialog_with_socrates, extract_alpha, extract_controversial_ideas, extract_ethical_framework, extract_extraordinary_claims, extract_predictions, extract_primary_problem, extract_wisdom_nometa, find_hidden_message, find_logical_fallacies, summarize_debate, t_analyze_challenge_handling, t_check_dunning_kruger, t_find_blindspots, t_find_negative_thinking, t_find_neglected_goals, t_red_team_thinking, ultimate_law_safety
|
||||
|
||||
|
|
@ -91,7 +91,7 @@ Match the request to one or more of these primary categories:
|
|||
|
||||
**DEVOPS**: analyze_terraform_plan
|
||||
|
||||
**EXTRACT**: analyze_comments, create_aphorisms, create_golden_rules, create_tags, create_upgrade_pack, create_video_chapters, extract_algorithm_update_recommendations, extract_all_quotes, extract_alpha, extract_article_wisdom, extract_bd_ideas, extract_book_ideas, extract_book_recommendations, extract_business_ideas, extract_characters, extract_controversial_ideas, extract_core_message, extract_ctf_writeup, extract_domains, extract_ethical_framework, extract_extraordinary_claims, extract_ideas, extract_insights, extract_insights_dm, extract_instructions, extract_jokes, extract_latest_video, extract_main_activities, extract_main_idea, extract_mcp_servers, extract_most_redeeming_thing, extract_patterns, extract_poc, extract_predictions, extract_primary_problem, extract_primary_solution, extract_product_features, extract_questions, extract_recipe, extract_recommendations, extract_references, extract_skills, extract_song_meaning, extract_sponsors, extract_videoid, extract_wisdom, extract_wisdom_agents, extract_wisdom_dm, extract_wisdom_nometa, extract_wisdom_short, extract_wisdom_with_attribution, generate_code_rules, t_extract_intro_sentences, t_extract_panel_topics
|
||||
**EXTRACT**: analyze_comments, create_aphorisms, create_golden_rules, create_tags, create_upgrade_pack, create_video_chapters, extract_affiliate_products, extract_algorithm_update_recommendations, extract_all_quotes, extract_alpha, extract_article_wisdom, extract_bd_ideas, extract_book_ideas, extract_book_recommendations, extract_business_ideas, extract_characters, extract_controversial_ideas, extract_core_message, extract_ctf_writeup, extract_domains, extract_ethical_framework, extract_extraordinary_claims, extract_ideas, extract_insights, extract_insights_dm, extract_instructions, extract_jokes, extract_latest_video, extract_main_activities, extract_main_idea, extract_mcp_servers, extract_most_redeeming_thing, extract_patterns, extract_poc, extract_predictions, extract_primary_problem, extract_primary_solution, extract_product_features, extract_questions, extract_recipe, extract_recommendations, extract_references, extract_skills, extract_song_meaning, extract_sponsors, extract_video_commerce_entities, extract_videoid, extract_wisdom, extract_wisdom_agents, extract_wisdom_dm, extract_wisdom_nometa, extract_wisdom_short, extract_wisdom_with_attribution, generate_code_rules, generate_frontmatter, t_extract_intro_sentences, t_extract_panel_topics
|
||||
|
||||
**GAMING**: create_npc, create_rpg_summary, summarize_rpg_session
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ Match the request to one or more of these primary categories:
|
|||
|
||||
**WELLNESS**: analyze_spiritual_text, create_better_frame, extract_wisdom_dm, heal_person, model_as_sherlock_freud, predict_person_actions, provide_guidance, recommend_yoga_practice, t_give_encouragement
|
||||
|
||||
**WRITING**: analyze_prose_json, analyze_prose_pinker, apply_ul_tags, clean_text, compare_and_contrast, convert_to_markdown, create_5_sentence_summary, create_academic_paper, create_aphorisms, create_better_frame, create_design_document, create_design_system, create_diy, create_formal_email, create_hormozi_offer, create_keynote, create_micro_summary, create_newsletter_entry, create_prediction_block, create_prd, create_show_intro, create_slides, create_story_about_people_interaction, create_story_explanation, create_summary, create_tags, create_user_story, enrich_blog_post, explain_docs, explain_terms, fix_typos, humanize, improve_academic_writing, improve_writing, label_and_rate, md_callout, official_pattern_template, recommend_talkpanel_topics, refine_design_document, summarize, summarize_debate, summarize_lecture, summarize_legislation, summarize_meeting, summarize_micro, summarize_newsletter, summarize_paper, summarize_rpg_session, t_create_opening_sentences, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_give_encouragement, t_year_in_review, transcribe_minutes, tweet, write_essay, write_essay_pg, write_hackerone_report, write_latex, write_micro_essay, write_pull-request
|
||||
**WRITING**: analyze_prose_json, analyze_prose_pinker, apply_ul_tags, clean_text, compare_and_contrast, convert_to_markdown, create_5_sentence_summary, create_academic_paper, create_aphorisms, create_better_frame, create_design_document, create_design_system, create_diy, create_formal_email, create_hormozi_offer, create_keynote, create_micro_summary, create_newsletter_entry, create_prediction_block, create_prd, create_show_intro, create_slides, create_story_about_people_interaction, create_story_explanation, create_summary, create_tags, create_user_story, enrich_blog_post, explain_docs, explain_terms, fix_typos, generate_frontmatter, humanize, improve_academic_writing, improve_writing, label_and_rate, md_callout, official_pattern_template, recommend_talkpanel_topics, refine_design_document, summarize, summarize_debate, summarize_lecture, summarize_legislation, summarize_meeting, summarize_micro, summarize_newsletter, summarize_paper, summarize_rpg_session, t_create_opening_sentences, t_describe_life_outlook, t_extract_intro_sentences, t_extract_panel_topics, t_give_encouragement, t_year_in_review, transcribe_minutes, tweet, write_essay, write_essay_pg, write_hackerone_report, write_latex, write_micro_essay, write_pull-request
|
||||
|
||||
## Workflow Suggestions
|
||||
|
||||
|
|
|
|||
|
|
@ -136,6 +136,10 @@ Examine battles analyzing strategic decisions to extract military lessons.
|
|||
|
||||
Analyze past errors to prevent similar mistakes in predictions/decisions.
|
||||
|
||||
### analyze_monetization_opportunities
|
||||
|
||||
Identify affiliate, sponsorship, digital product, and community revenue opportunities in creator content aligned with audience intent.
|
||||
|
||||
### analyze_paper
|
||||
|
||||
Analyze scientific papers to identify findings and assess conclusion.
|
||||
|
|
@ -442,6 +446,10 @@ Extract world model updates/algorithms to improve decision-making.
|
|||
|
||||
Organize video content into timestamped chapters highlighting key topics.
|
||||
|
||||
### extract_affiliate_products
|
||||
|
||||
Extract commercial products, tools, and brands from transcripts, separating sponsored from organic mentions with commission tier estimates.
|
||||
|
||||
### extract_algorithm_update_recommendations
|
||||
|
||||
Extract recommendations for improving algorithms, focusing on steps.
|
||||
|
|
@ -538,6 +546,10 @@ Extract/classify hard/soft skills from job descriptions into skill inventory.
|
|||
|
||||
Extract/organize sponsorship info, including names and messages.
|
||||
|
||||
### extract_video_commerce_entities
|
||||
|
||||
Identify every commercially relevant entity in a video transcript — products, tools, brands, services — with category, mention type, and purchase likelihood.
|
||||
|
||||
### extract_videoid
|
||||
|
||||
Extract/parse video IDs and URLs to create video lists.
|
||||
|
|
@ -690,6 +702,10 @@ Create glossaries of advanced terms with definitions and analogies.
|
|||
|
||||
Proofreads and corrects typos, spelling, grammar, and punctuation errors.
|
||||
|
||||
### generate_frontmatter
|
||||
|
||||
Generate YAML frontmatter with tags, aliases and summary for PKM notes.
|
||||
|
||||
### humanize
|
||||
|
||||
Transform technical content into approachable language.
|
||||
|
|
|
|||
|
|
@ -55,6 +55,7 @@ Use parameterized queries instead of string concatenation...
|
|||
- Store secrets in environment variables or secure configuration
|
||||
- Use the built-in setup process for key management
|
||||
- Regularly rotate API keys
|
||||
- `~/.config/fabric/.env` holds API keys and Codex OAuth tokens. Fabric writes it with mode `0600`. Treat the file as secret material. Comments and key order are not preserved when Codex tokens are updated in place.
|
||||
|
||||
### Input Validation
|
||||
|
||||
|
|
|
|||
|
|
@ -112,14 +112,18 @@ For manual installation or troubleshooting, see the detailed instructions below.
|
|||
|
||||
The completions provide intelligent suggestions for:
|
||||
|
||||
- **Patterns**: Tab-complete available patterns with `-p` or `--pattern`
|
||||
- **Patterns**: Tab-complete available patterns with `-p`, `--pattern`, or `--readpattern`
|
||||
- **Models**: Tab-complete available models with `-m` or `--model`
|
||||
- **Vendors**: Tab-complete configured vendors with `-V` or `--vendor`
|
||||
- **Contexts**: Tab-complete contexts for context-related flags
|
||||
- **Sessions**: Tab-complete sessions for session-related flags
|
||||
- **Strategies**: Tab-complete available strategies
|
||||
- **Extensions**: Tab-complete registered extensions
|
||||
- **Gemini Voices**: Tab-complete TTS voices for `--voice`
|
||||
- **File paths**: Smart file completion for attachment, output, and config options
|
||||
- **Transcription models**: Tab-complete models for `--transcribe-model`
|
||||
- **Fixed value sets**: Tab-complete the accepted values of `--thinking`, `--debug`, `--image-size`,
|
||||
`--image-quality`, and `--image-background`
|
||||
- **File paths**: Smart file completion for attachment, output, config, image, and transcription options
|
||||
- **Flag completion**: All available command-line flags and options
|
||||
|
||||
## Alternative Installation Method
|
||||
|
|
|
|||
13
docs/docs.go
13
docs/docs.go
|
|
@ -139,6 +139,15 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/fsdb.Pattern"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -346,7 +355,6 @@ const docTemplate = `{
|
|||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"description": "Add Language field to bind from request",
|
||||
"type": "string"
|
||||
},
|
||||
"maxTokens": {
|
||||
|
|
@ -412,9 +420,6 @@ const docTemplate = `{
|
|||
"type": "number",
|
||||
"format": "float64"
|
||||
},
|
||||
"updateChan": {
|
||||
"type": "object"
|
||||
},
|
||||
"voice": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,6 +133,15 @@
|
|||
"$ref": "#/definitions/fsdb.Pattern"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -340,7 +349,6 @@
|
|||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"description": "Add Language field to bind from request",
|
||||
"type": "string"
|
||||
},
|
||||
"maxTokens": {
|
||||
|
|
@ -406,9 +414,6 @@
|
|||
"type": "number",
|
||||
"format": "float64"
|
||||
},
|
||||
"updateChan": {
|
||||
"type": "object"
|
||||
},
|
||||
"voice": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,6 @@ definitions:
|
|||
imageSize:
|
||||
type: string
|
||||
language:
|
||||
description: Add Language field to bind from request
|
||||
type: string
|
||||
maxTokens:
|
||||
type: integer
|
||||
|
|
@ -95,8 +94,6 @@ definitions:
|
|||
topP:
|
||||
format: float64
|
||||
type: number
|
||||
updateChan:
|
||||
type: object
|
||||
voice:
|
||||
type: string
|
||||
type: object
|
||||
|
|
@ -265,6 +262,12 @@ paths:
|
|||
description: OK
|
||||
schema:
|
||||
$ref: '#/definitions/fsdb.Pattern'
|
||||
"400":
|
||||
description: Bad Request
|
||||
schema:
|
||||
additionalProperties:
|
||||
type: string
|
||||
type: object
|
||||
"500":
|
||||
description: Internal Server Error
|
||||
schema:
|
||||
|
|
|
|||
|
|
@ -41,11 +41,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"lastModified": 1785692966,
|
||||
"narHash": "sha256-vUfIeBEfpbAfZ5zjgIkYk7eHBeVfCYVjLbWnMkseYnk=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"rev": "643809054d65fdd466a63e3155b8c498cb483c04",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
174
go.mod
174
go.mod
|
|
@ -1,121 +1,125 @@
|
|||
module github.com/danielmiessler/fabric
|
||||
|
||||
go 1.25.1
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||
github.com/anthropics/anthropic-sdk-go v1.27.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0
|
||||
github.com/anthropics/anthropic-sdk-go v1.67.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.57.0
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.2
|
||||
github.com/gabriel-vasile/mimetype v1.4.13
|
||||
github.com/aws/aws-sdk-go-v2 v1.44.0
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.40
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.68.0
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.58.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.15
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/go-git/go-git/v5 v5.17.0
|
||||
github.com/go-git/go-git/v5 v5.19.2
|
||||
github.com/go-shiori/go-readability v0.0.0-20251205110129-5db1dc9836f0
|
||||
github.com/google/go-github/v66 v66.0.0
|
||||
github.com/hasura/go-graphql-client v0.15.1
|
||||
github.com/hasura/go-graphql-client v0.16.0
|
||||
github.com/jessevdk/go-flags v1.6.1
|
||||
github.com/joho/godotenv v1.5.1
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||
github.com/mattn/go-sqlite3 v1.14.37
|
||||
github.com/mattn/go-sqlite3 v1.14.50
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/ollama/ollama v0.18.2
|
||||
github.com/ollama/ollama v0.33.1
|
||||
github.com/openai/openai-go v1.12.0
|
||||
github.com/otiai10/copy v1.14.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/samber/lo v1.53.0
|
||||
github.com/sgaunet/perplexity-go/v2 v2.15.0
|
||||
github.com/sgaunet/perplexity-go/v2 v2.16.1
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/swaggo/files v1.0.1
|
||||
github.com/swaggo/gin-swagger v1.6.1
|
||||
github.com/swaggo/swag v1.16.6
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/text v0.35.0
|
||||
google.golang.org/api v0.272.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/text v0.41.0
|
||||
google.golang.org/api v0.294.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.9.0 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.41 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.6.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/buger/jsonparser v1.6.1 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.22.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.5 // indirect
|
||||
github.com/go-openapi/spec v0.22.4 // indirect
|
||||
github.com/go-openapi/swag/conv v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/loading v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 // indirect
|
||||
github.com/go-openapi/jsonpointer v1.0.0 // indirect
|
||||
github.com/go-openapi/jsonreference v1.0.1 // indirect
|
||||
github.com/go-openapi/spec v0.22.11 // indirect
|
||||
github.com/go-openapi/swag/conv v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/jsonutils v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/loading v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/pools v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/stringutils v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/typeutils v0.29.1 // indirect
|
||||
github.com/go-openapi/swag/yamlutils v0.29.1 // indirect
|
||||
github.com/goccy/go-yaml v1.19.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.3 // indirect
|
||||
github.com/invopop/jsonschema v0.14.0 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mailru/easyjson v0.9.2 // indirect
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/quic-go/qpack v0.6.0 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/quic-go/quic-go v0.61.0 // indirect
|
||||
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6 // indirect
|
||||
golang.org/x/mod v0.40.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/auth v0.18.2 // indirect
|
||||
cloud.google.com/go/auth v0.23.2 // indirect
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.9.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.4 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.12
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/coder/websocket v1.8.14 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.39
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.40 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.34.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.39.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.46.0 // indirect
|
||||
github.com/aws/smithy-go v1.28.1 // indirect
|
||||
github.com/bytedance/sonic v1.15.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.2 // indirect
|
||||
github.com/cloudflare/circl v1.6.5 // indirect
|
||||
github.com/cloudwego/base64x v0.1.7 // indirect
|
||||
github.com/coder/websocket v1.8.15 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.7.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v1.1.1 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.9.1 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.3 // indirect
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c // indirect
|
||||
github.com/goccy/go-json v0.10.6 // indirect
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
|
|
@ -123,45 +127,43 @@ require (
|
|||
github.com/google/go-querystring v1.2.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.24.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.5.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/otiai10/mint v1.6.3 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/gjson v1.19.0 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // indirect
|
||||
github.com/ugorji/go/codec v1.3.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
golang.org/x/arch v0.25.0 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||
golang.org/x/net v0.52.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
google.golang.org/genai v1.51.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/grpc v1.79.3 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 // indirect
|
||||
go.opentelemetry.io/otel v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.46.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.46.0 // indirect
|
||||
golang.org/x/arch v0.30.0 // indirect
|
||||
golang.org/x/crypto v0.55.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
google.golang.org/genai v1.70.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect
|
||||
google.golang.org/grpc v1.83.2 // indirect
|
||||
google.golang.org/protobuf v1.36.12 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
|||
683
go.sum
683
go.sum
|
|
@ -1,29 +1,31 @@
|
|||
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
|
||||
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
|
||||
cloud.google.com/go/auth v0.18.1 h1:IwTEx92GFUo2pJ6Qea0EU3zYvKnTAeRCODxfA/G5UWs=
|
||||
cloud.google.com/go/auth v0.18.1/go.mod h1:GfTYoS9G3CWpRA3Va9doKN9mjPGRS+v41jmZAhBzbrA=
|
||||
cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
|
||||
cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
|
||||
cloud.google.com/go/auth v0.22.0 h1:Xp9wAKkLoeaYb5pYZZoQGz4E9sdPxIbzS3gywZE3ciQ=
|
||||
cloud.google.com/go/auth v0.22.0/go.mod h1:M9o2Oz+YI2jAfxewJgb1vyI3vceHF+eohmxyzmrl+9s=
|
||||
cloud.google.com/go/auth v0.23.2 h1:pxSCpfiji41hpzpPdMCftEUCezpgpqmmDdYiAjCKXxo=
|
||||
cloud.google.com/go/auth v0.23.2/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
|
||||
cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
|
||||
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
|
||||
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 h1:aokoqcHvaGjiM3VpjKDfMMnF/8epJ+Q1HLJ7CudztqE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0/go.mod h1:/WYEx9pcM9Y+Dd/APJaNlSvVSvzl54rrMdZT5+Oi2LM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0 h1:4gRPBpN1f6xt88yi4WR26m7XaD9OlWtVT6bWPdGUIok=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0/go.mod h1:G7QVLxw1j1JVyrO1MA95S8m8HStaaleDZYTcfGgjB2o=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0 h1:CU4+EJeJi3TKYWEcYuSdWsjzw0nVsK/H0MSQOiPcymU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0/go.mod h1:q0+UTSRvShwUCrR/s5HtyInYphN7Wvxb7snFM3u+SLA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0 h1:xFaZZ+IubdftrDHnGGwZ6QvQ3KHTtWl2MCK+GMt2vxs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.4.0/go.mod h1:mCBhUhlMjLLJKr5aqw2TNS/VqJOie8MzWq3DAMJeKso=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
|
||||
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0 h1:4iB+IesclUXdP0ICgAabvq2FYLXrJWKx1fJQ+GxSo3Y=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 h1:RHK7bS+HQMslb1sZpAokUt+zTVmue0hKSs2C791hhzU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.9.0 h1:MDT4FxAPve5FnYn6vOL1r7RCRDG+l9cI7a5LlCuHsqA=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.9.0/go.mod h1:Y33QHnf0FfdVewFFISOGe20mkZbxX4H839o955/PoeI=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc=
|
||||
|
|
@ -31,235 +33,229 @@ github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6Xge
|
|||
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
|
||||
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
|
||||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
|
||||
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
|
||||
github.com/andybalholm/cascadia v1.3.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
||||
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||
github.com/anthropics/anthropic-sdk-go v1.23.0 h1:YVNnxfVVPJM+zvQ1oDgTJUBtLttGpBHe1WtJBr0QeAs=
|
||||
github.com/anthropics/anthropic-sdk-go v1.23.0/go.mod h1:WTz31rIUHUHqai2UslPpw5CwXrQP3geYBioRV4WOLvE=
|
||||
github.com/anthropics/anthropic-sdk-go v1.27.1 h1:7DgMZ2Ng3C2mPzJGHA30NXQTZolcF07mHd0tGaLwfzk=
|
||||
github.com/anthropics/anthropic-sdk-go v1.27.1/go.mod h1:qUKmaW+uuPB64iy1l+4kOSvaLqPXnHTTBKH6RVZ7q5Q=
|
||||
github.com/anthropics/anthropic-sdk-go v1.61.0 h1:JRTnm1tPqn5xo1xd1zfrcFDlcoWXVMvV1K68YmhpZKw=
|
||||
github.com/anthropics/anthropic-sdk-go v1.61.0/go.mod h1:3EfIfmFqxH6rbiLcIP4tPFyXL/IHakx2wDG4OU+TIEI=
|
||||
github.com/anthropics/anthropic-sdk-go v1.67.0 h1:tTsVSR+YD6R+1EPoMyZ9im7YKEjJpYGZk5klcDi9HRg=
|
||||
github.com/anthropics/anthropic-sdk-go v1.67.0/go.mod h1:3EfIfmFqxH6rbiLcIP4tPFyXL/IHakx2wDG4OU+TIEI=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de h1:FxWPpzIjnTlhPwqqXc4/vE0f7GvRjuAsbW+HOIe8KnA=
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de/go.mod h1:DCaWoUhZrYW9p1lxo/cm8EmUOOzAPSEZNGF2DK1dJgw=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
|
||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1 h1:ABlyEARCDLN034NhxlRUSZr4l71mh+T5KAeGh6cerhU=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.1/go.mod h1:MayyLB8y+buD9hZqkCW3kX1AKq07Y5pXxtgB+rRFhz0=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4 h1:10f50G7WyU02T56ox1wWXq+zTX9I1zxG46HYuG1hH/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.4/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4 h1:489krEF9xIGkOaaX3CE/Be2uWjiXrkCH6gUX+bZA/BU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.4/go.mod h1:IOAPF6oT9KCsceNTvvYMNHy0+kMF8akOjeDvPENWxp4=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7 h1:3kGOqnh1pPeddVa/E37XNTaWJ8W6vrbYV9lJEkCnhuY=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.7/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.7 h1:vxUyWGUwmkQ2g19n7JY/9YL8MfAIl7bTesIUykECXmY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.7/go.mod h1:2/Qm5vKUU/r7Y+zUk/Ptt2MDAEKAfUtKc1+3U1Mo3oY=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12 h1:O3csC7HUGn2895eNrLytOJQdoL2xyJy0iYXhoZ1OmP0=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.12/go.mod h1:96zTvoOFR4FURjI+/5wY1vc1ABceROO4lWgWJuxgy0g=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.7 h1:tHK47VqqtJxOymRrNtUXN5SP/zUTvZKeLx4tH6PGQc8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.7/go.mod h1:qOZk8sPDrxhf+4Wf4oT2urYJrYt3RejHSzgAquYeppw=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.12 h1:oqtA6v+y5fZg//tcTWahyN9PEn5eDU/Wpvc2+kJ4aY8=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.12/go.mod h1:U3R1RtSHx6NB0DvEQFGyf/0sbrpJrluENHdPy1j/3TE=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17 h1:I0GyV8wiYrP8XpA70g1HBcQO1JlQxCMTW9npl5UbDHY=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.17/go.mod h1:tyw7BOl5bBe/oqvoIeECFJjMdzXoa/dfVz3QQ5lgHGA=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20 h1:zOgq3uezl5nznfoK3ODuqbhVg1JzAGDUhXOsU0IDCAo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.20/go.mod h1:z/MVwUARehy6GAg/yQ1GO2IMl0k++cu1ohP9zo887wE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17 h1:xOLELNKGp2vsiteLsvLPwxC+mYmO6OZ8PYgiuPJzF8U=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.17/go.mod h1:5M5CI3D12dNOtH3/mk6minaRwI2/37ifCURZISxA/IQ=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20 h1:CNXO7mvgThFGqOFgbNAP2nol2qAWBOGfqR/7tQlvLmc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.20/go.mod h1:oydPDJKcfMhgfcgBUZaG+toBbwy8yPWubJXBVERtI4o=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17 h1:WWLqlh79iO48yLkj1v3ISRNiv+3KdQoZ6JWyfcsyQik=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.17/go.mod h1:EhG22vHRrvF8oXSTYStZhJc1aUgKtnJe+aOiFEV90cM=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20 h1:tN6W/hg+pkM+tf9XDkWUbDEjGLb+raoBMFsTodcoYKw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.20/go.mod h1:YJ898MhD067hSHA6xYCx5ts/jEd8BSOLtQDL3iZsvbc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72MnLuFK9tJwmrbHw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.53.2 h1:Z5JspbwScfbzmOmTmlagxHVRcOJmb/Ku5kXnwdY0fto=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.53.2/go.mod h1:YkwtdWa9fxpfhKuZyjb9mi+h/E3LoXFzT72BpxA9tGk=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.57.0 h1:DCONGOKen9tTc35KrPbMGEr9KjIMY0eYLkFdeYSzW4c=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.57.0/go.mod h1:C/1NPMi8p0/I4HKgDqXYl7WdcQvWrKBq5PqwGQjMz8U=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.49.0 h1:osqN479arsxXAIHmBbiAn+0nj7jCkuXtzgtZPSwt0sc=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.49.0/go.mod h1:siKVmJdui4dwPPtsKr3F5BAeJxW1MANWaLJnTDfgu7c=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.2 h1:x0eGAWpd1B5I/vMtrB4Q4Zuc3CXWI8wjHfPPqBSrKmM=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.2/go.mod h1:V9oTWSDC2MtS1DR71hbNET/bZ8psQp022amEBe1grJc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4 h1:0ryTNEdJbzUCEWkVXEXoqlXV72J5keC1GvILMOuD00E=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.4/go.mod h1:HQ4qwNZh32C3CBeO6iJLQlgtMzqeG17ziAA/3KDJFow=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17 h1:RuNSMoozM8oXlgLG/n6WLaFGoea7/CddrCfIiSA+xdY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.17/go.mod h1:F2xxQ9TZz5gDWsclCtPQscGpP0VUOc8RqgFM3vDENmU=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20 h1:2HvVAIq+YqgGotK6EkMf+KIEqTISmTYh5zLpYyeTo1Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.20/go.mod h1:V4X406Y666khGa8ghKmphma/7C0DAtEQYhkq9z4vpbk=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5 h1:VrhDvQib/i0lxvr3zqlUwLwJP4fpmpyD9wYG1vfSu+Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.5/go.mod h1:k029+U8SY30/3/ras4G/Fnv/b88N4mAfliNn08Dem4M=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8 h1:0GFOLzEbOyZABS3PhYfBIx2rNBACYcKty+XGkTgw1ow=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.8/go.mod h1:LXypKvk85AROkKhOG6/YEcHFPoX+prKTowKnVdcaIxE=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9 h1:v6EiMvhEYBoHABfbGB4alOYmCIrcgyPPiBE1wZAEbqk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.9/go.mod h1:yifAsgBxgJWn3ggx70A3urX2AN49Y5sJTD1UQFlfqBw=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13 h1:kiIDLZ005EcKomYYITtfsjn7dtOwHDOFy7IbPXKek2o=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.13/go.mod h1:2h/xGEowcW/g38g06g3KpRWDlT+OTfxxI0o1KqayAB8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13 h1:gd84Omyu9JLriJVCbGApcLzVR3XtmC4ZDPcAI6Ftvds=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.13/go.mod h1:sTGThjphYE4Ohw8vJiRStAcu3rbjtXRsdNB0TvZ5wwo=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17 h1:jzKAXIlhZhJbnYwHbvUQZEB8KfgAEuG0dc08Bkda7NU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.17/go.mod h1:Al9fFsXjv4KfbzQHGe6V4NZSZQXecFcvaIF4e70FoRA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6 h1:5fFjR/ToSOzB2OQ/XqWpZBmNvmP/pJ1jOWYlFDJTjRQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.6/go.mod h1:qgFDZQSD/Kys7nJnVqYlWKnh0SSdMjAi0uSwON4wgYQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9 h1:Cng+OOwCHmFljXIxpEVXAGMnBia8MSU6Ch5i9PgBkcU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.9/go.mod h1:LrlIndBDdjA/EeXeyNBle+gyCwTlizzW5ycgWnvIxkk=
|
||||
github.com/aws/smithy-go v1.24.0 h1:LpilSUItNPFr1eY85RYgTIg5eIEPtvFbskaFcmmIUnk=
|
||||
github.com/aws/smithy-go v1.24.0/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0=
|
||||
github.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=
|
||||
github.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.0 h1:fharf/WhbRAVZ1du0QL7roNFxZ6T/sWr+4Ni617bwSI=
|
||||
github.com/aws/aws-sdk-go-v2 v1.43.0/go.mod h1:5pKeft2eJj+gElQ38Jqg4ibCqh+/AK33/0X3hip7IjM=
|
||||
github.com/aws/aws-sdk-go-v2 v1.44.0 h1:4IbaHhtzy+4h37z4JQyO9a2QsiCml3CNYHtq5hIHigo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.44.0/go.mod h1:bttEH6JqnUL8LepvDVfdrds/fZ5bCIxzpe3abyUrhDU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14 h1:3IZY0XAJquT3aHzbkHfPzy4ACPcEjVG0x87KOwtpqGY=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14/go.mod h1:zwM6veDkhGgQFqkBy+uT28AAYpLu+uFMlPl+rCg/73E=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20 h1:GPRlPwz40I2B2VrBEASOA3Bi77NyeqejNLkifosX0rs=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.20/go.mod h1:g7PNzKcsOKWb4fkSRBA7BZVAS6Y8IcxzN+nRohhQ1Q8=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.31 h1:n4nY9O3QKoHIkL85EX+V8RcMFtOhlpTFhGArg915PXk=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.31/go.mod h1:PN0NYDCCoOpGGsZ2+elDUidmHfQBPyYzN2GCgl8HEBs=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.40 h1:lAVC9gMmKusmqDRe32dPtgKl/BWvJmMJoWELKHCAObw=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.40/go.mod h1:8xOJLbe/hOj1g4PVsfJYV7O2byq+UGET1onDdUgbwqc=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.30 h1:TTCvvzFU6gXa4iJecNG/0F/B0oYTiazoRECr2XyLHrY=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.30/go.mod h1:jKxAp2AEncnliinzpgOSZDFv6+VjvWhjw/AtbfsWT9U=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.39 h1:XOg8LC3Kgnsa3WiPQjc7Bi8k5IBN92cPYfIV9XMFss0=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.39/go.mod h1:GonTDBQ+mTpCVNwaHjj0PagspfrYYMEqOx7FehoEP/I=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.31 h1:kfVL5wAunCJycL6MOQ6aNh6PlAYEymflcjuKmrWUA0o=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.31/go.mod h1:nWfRNDAppujCQgOUd43lKT4yeLv9z3nJ3bw1G3BgQKo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.40 h1:r5aGipEVgI9aT/tAGjdrPbDQvIAKdTrS3rUPQtG4Rmo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.40/go.mod h1:vOD3CnPxAdkL6MWZeROkZsTlskklMFfgVFkHzx/oZpY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.31 h1:Z8F3hfCY33IGpJjFAnv0wvtv1FIKj1GHmRDEYqy64tw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.31/go.mod h1:aVyUoytEyOViR6jhq6jula0xkc5NfBE2hgeF6BvOrao=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.40 h1:UIXlbijuB2XK1Kr57fo8iIxCuaSHJzwZ1uo+2tbEYIk=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.40/go.mod h1:wcEsL6jscjZjVUinb0Q5qD/GXOG1yT3GNfmT9HuDwzU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.31 h1:hyOxUyXdh3AyjE93gBgsfziJag9ACwcs+ZpDBLzi8mw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.31/go.mod h1:OERqI9k0draSLB8O8woxY3q25ZWTELRK4RRoLMuMZFo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40 h1:xLQVRDs2NddDmK9BEyh5KSlJ1Gpy5/GIJXrV6WcVGAE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.40/go.mod h1:XRXnpFVFGLaEVK+olDdFIM1vNa04ETW452oFGEPUxAo=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.32 h1:0MrUL35H/Y4kdFfItoR5jCgtDQ4Z/8LudAoIHRfA4hE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.32/go.mod h1:2tNZkuWz54arj8mHVf+8Y7cKkcD8Wr/fBpENgEXpjLc=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.41 h1:nv/ILuCY0yXACzMQwvtt/HbqDDjemZiI0AeDbxGQlnU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.41/go.mod h1:dzvOSpxaPqQ3j0xS6Lc1vyVuWW0RBj7s/QqYpzu3Q/0=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.66.0 h1:RMRC4k42Bt3wHqWiPyFfiRrOyRhJZW7w6vAw5ZnMrPM=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.66.0/go.mod h1:1asCJhndokTo9QEJ68/eZv4yJsRPV9A0ROdDx7QQBkQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.68.0 h1:2T9SsmNe78eLtmobdnvxk088DVuUfYp9Ml9MxFMWQUQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.68.0/go.mod h1:ANOxqA/+nqohBqirnFCF8EqJFkjAbTLYfyBl6NEjNug=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.56.0 h1:CWw8zDpnMJLwSvZd41Ncf/eJPeZ5t74UxGAu4HbM3S4=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.56.0/go.mod h1:dGxTgK2ZKWrbZv5o/8oCeO3Uch3n0w2rtSFroeJoLcE=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.58.0 h1:TDwZrhBZTHNxvGiqqDoNjdUuoveRRVfy14VeFHbbWBc=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.58.0/go.mod h1:ZnrFfnjYjXc/PC2a2hwAIS2qf1Yqk15EMLryhca2wps=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 h1:mbRIur/BiHK6SKPjoBIXSE/hJ6g6JGRLuxQy1jGjlN4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13/go.mod h1:ITg9em2KbJx1s0y4aqRX5OYWG6HBZ5TVR//OdpEZ2CQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19 h1:bAdDl/HkGCcGPoe25ToSHEw23VIxt6CT5fLcg111BKg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.19/go.mod h1:KaUzbLxv4CeSxh6ZCl9B4m7CuFenS8kUEaDs+f/DQr4=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.31 h1:w2SIhW92DZPFrSL4ksVCr8IYff5OZwIcxg8+95tzvAI=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.31/go.mod h1:wAhpCQbkov+IcvjozJbd2xRCoZybUEHNkcFunssNACg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.40 h1:gr3Fw1cxZXNCdeo/lQ7isHEHzvHVM7z75qb2zW9aMjw=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.40/go.mod h1:8z/9CmfnQhiuXD7Ykbcg4a/whSWsniE0ODSx9uwVzfk=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.5.0 h1:OHH5iTQvVGmfHjX/5Q+vFuA/Rf2x6/95aJ/75QCQSm4=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.5.0/go.mod h1:mCF3AK9PpL49oOrhniUXWAfhVBVQ/XbytoE5eccZUIs=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.6.0 h1:agcr0j8YeFEzdXNo17Rg9MbbjLRjrimabwNtji4e+lU=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.6.0/go.mod h1:qU5PxgQ4JiUOOMotzfO3+5oUda5W+8JDVKyLQqlrJik=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.33.0 h1:CaJyYhxBE0M/HJX/YvSaSmQlsI91VHB0lKU8LtLxL3A=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.33.0/go.mod h1:+e6BMRMPjBQoCw/WovYR9GLy2IU0z4Q77smOB1DraSg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.34.0 h1:FxaN8/sn61DTXNI6Gt678tFJUY8iUsCchm6Y/F/RjaA=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.34.0/go.mod h1:vu4OY6s8LJtT8BtYG2LD6BGSZMptkYn3o5hvCPB22jc=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.0 h1:tC323YV77QdafeBr6LUhLDTsboyuyHLNRwAyCP44kGU=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.0/go.mod h1:SfLK1sgviHmbI+MozR9iDwDjL4cdCVZtahsjoR+z7wg=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.39.0 h1:crWKPeGYTBTuBxQ3p73kjfJvt4brUIsr+Fuypko8FxY=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.39.0/go.mod h1:HjjZVhaBz0JBR/kbWKThmNDhFKS7y6EURuk493tJk9Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.45.0 h1:Pd6PNlp4t8PTXxqzstICl52Wsy78vpjFZ7PRUj44mJc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.45.0/go.mod h1:rmQ0TnHzuLPmabgjPcsywhsSOmaBDgzR4zvDxSPsGdg=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.46.0 h1:IZ63JdogSNNjex/jsODNv7jGDcO/xJYd9FsgyfCsp1g=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.46.0/go.mod h1:I+rwAf3spG5dITBaAo3xXRowk8kiOhtU1kYxfvCTC44=
|
||||
github.com/aws/smithy-go v1.27.4 h1:JQcphmBN4f0q/sPqXqROIItRNV/hy10cgu7CsFy616M=
|
||||
github.com/aws/smithy-go v1.27.4/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/aws/smithy-go v1.28.1 h1:R/nXH00c8qcfCzQVELtRw+eLQWtzv+VAIEFJ1/xxXlQ=
|
||||
github.com/aws/smithy-go v1.28.1/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=
|
||||
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
|
||||
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/buger/jsonparser v1.1.2 h1:frqHqw7otoVbk5M8LlE/L7HTnIq2v9RX6EJ48i9AxJk=
|
||||
github.com/buger/jsonparser v1.1.2/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
|
||||
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
|
||||
github.com/buger/jsonparser v1.2.0 h1:4EFcvK1kD4jyj6YqNK6skK6w+y7FHHBR+XBCtxwu/6g=
|
||||
github.com/buger/jsonparser v1.2.0/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/buger/jsonparser v1.6.1 h1:I0phFv0PlbLHnM7TZAVjZ2MJ2/eWRTDyuO7GLR98IEs=
|
||||
github.com/buger/jsonparser v1.6.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/gopkg v0.1.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
|
||||
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
|
||||
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
|
||||
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/bytedance/sonic v1.15.2 h1:90H+rcF/FwLXwfB1cudOLq/je83n683Utf4Cbp0xHCo=
|
||||
github.com/bytedance/sonic v1.15.2/go.mod h1:mT2NbXunuaEbnZ+mRIX/vYqKISmgEuHFDI4UzmKx2SA=
|
||||
github.com/bytedance/sonic v1.15.3 h1:P3akjLPBtV/i6bHC6LbcLjY3KuoOvfiqF8wFHeP5IhY=
|
||||
github.com/bytedance/sonic v1.15.3/go.mod h1:8e51yTPdY8M6t+vvGL1c2Y1xL9i+frEeIAQAEl75NUc=
|
||||
github.com/bytedance/sonic/loader v0.5.1 h1:Ygpfa9zwRCCKSlrp5bBP/b/Xzc3VxsAW+5NIYXrOOpI=
|
||||
github.com/bytedance/sonic/loader v0.5.1/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/bytedance/sonic/loader v0.5.2 h1:0QtP1gevc1OZ6/H8Lb9BRZiCXd1Ftjd3OKuj1T1lBIo=
|
||||
github.com/bytedance/sonic/loader v0.5.2/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
|
||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
|
||||
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5 h1:6xNmx7iTtyBRev0+D/Tv1FZd4SCg8axKApyNyRsAt/w=
|
||||
github.com/cncf/xds/go v0.0.0-20251210132809-ee656c7534f5/go.mod h1:KdCmV+x/BuvyMxRnYBlmVaq4OLiKW6iRQfvC62cvdkI=
|
||||
github.com/coder/websocket v1.8.14 h1:9L0p0iKiNOibykf283eHkKUHHrpG7f65OE3BhhO7v9g=
|
||||
github.com/coder/websocket v1.8.14/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/cloudflare/circl v1.6.4 h1:pOXuDTCEYyzydgUpQ0CQz3LsinKjiSk6nNP5Lt5K64U=
|
||||
github.com/cloudflare/circl v1.6.4/go.mod h1:YxarevkLlbaHuWsxG6vmYNWBEsSp4pnp7j+4VljMavY=
|
||||
github.com/cloudflare/circl v1.6.5 h1:O64F26HEqNhznd/hrC5KZXVKYuKM2rx4deZDTc4ihQA=
|
||||
github.com/cloudflare/circl v1.6.5/go.mod h1:h5LNyxAc5nTue9DS5jT+48en2PSDYt3zdGnz5OstK6c=
|
||||
github.com/cloudwego/base64x v0.1.7 h1:NppS+Fgzg5ovhn4NkUXaDT3x9jldgH5ToMCqzBSi2zI=
|
||||
github.com/cloudwego/base64x v0.1.7/go.mod h1:Cu1PV9zfrSf7ET2tIbWbbEy7jO7HHJ13q4X2SQ8aWYg=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik=
|
||||
github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4=
|
||||
github.com/coder/websocket v1.8.15 h1:6B2JPeOGlpff2Uz6vOEH1Vzpi0iUz20A+lPVhPHtNUA=
|
||||
github.com/coder/websocket v1.8.15/go.mod h1:NX3SzP+inril6yawo5CQXx8+fk145lPDC6pumgx0mVg=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/cyphar/filepath-securejoin v0.7.0 h1:s0Y3ITPy6sQn5xt54DuYvTF8hu134ooYLUb58DX/HjE=
|
||||
github.com/cyphar/filepath-securejoin v0.7.0/go.mod h1:ymLGms/u3BYaviIiuKFnUx8EkQEZeK6cInNoAPJA3o4=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
|
||||
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
|
||||
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
|
||||
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
|
||||
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0 h1:yg/JjO5E7ubRyKX3m07GF3reDNEnfOboJ0QySbH736g=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.36.0/go.mod h1:ty89S1YCCVruQAm9OtKeEkQLTb+Lkz0k8v9W0Oxsv98=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0 h1:TvGH1wof4H33rezVKWSpqKz5NXWg5VPuZ0uONDT6eb4=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.0/go.mod h1:HvYl7zwPa5mffgyeTUHA9zHIH36nmrm7oCbo4YKoSWA=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13 h1:46nXokslUBsAJE/wMsp5gtO500a4F3Nkz9Ufpk2AcUM=
|
||||
github.com/gabriel-vasile/mimetype v1.4.13/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ=
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.37.0/go.mod h1:DReE9MMrmecPy+YvQOAOHNYMALuowAnbjjEMkkWOi6A=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=
|
||||
github.com/envoyproxy/protoc-gen-validate v1.3.3/go.mod h1:TsndJ/ngyIdQRhMcVVGDDHINPLWB7C82oDArY51KfB0=
|
||||
github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
|
||||
github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15 h1:05iP/CYtZ/w455R/KZM6rZ5ieAdh99UPtd+d3YzLmaI=
|
||||
github.com/gabriel-vasile/mimetype v1.4.15/go.mod h1:azpTcoLcDZRNgFou5j+APrqQx9HqVPWa6ijYQIIVswQ=
|
||||
github.com/gin-contrib/gzip v0.0.6 h1:NjcunTcGAj5CO1gn4N8jHOSIeRFHIbn51z6K+xaN4d4=
|
||||
github.com/gin-contrib/gzip v0.0.6/go.mod h1:QOJlmV2xmayAjkNS2Y8NQsMneuRShOU/kjovCXNuzzk=
|
||||
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
|
||||
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
|
||||
github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk=
|
||||
github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls=
|
||||
github.com/gin-contrib/sse v1.1.1 h1:uGYpNwTacv5R68bSGMapo62iLTRa9l5zxGCps4hK6ko=
|
||||
github.com/gin-contrib/sse v1.1.1/go.mod h1:QXzuVkA0YO7o/gun03UI1Q+FTI8ZV/n5t03kIQAI89s=
|
||||
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
|
||||
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.7.0 h1:83lBUJhGWhYp0ngzCMSgllhUSuoHP1iEWYjsPl9nwqM=
|
||||
github.com/go-git/go-billy/v5 v5.7.0/go.mod h1:/1IUejTKH8xipsAcdfcSAlUlo2J7lkYV8GTKxAT/L3E=
|
||||
github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
|
||||
github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
|
||||
github.com/go-git/go-billy/v5 v5.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
|
||||
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
|
||||
github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
|
||||
github.com/go-git/go-git/v5 v5.17.0 h1:AbyI4xf+7DsjINHMu35quAh4wJygKBKBuXVjV/pxesM=
|
||||
github.com/go-git/go-git/v5 v5.17.0/go.mod h1:f82C4YiLx+Lhi8eHxltLeGC5uBTXSFa6PC5WW9o4SjI=
|
||||
github.com/go-git/go-git/v5 v5.19.1 h1:nX27AnaU43/K5bKktKwgBmR9lawoYVe1Ckg0rgzzN00=
|
||||
github.com/go-git/go-git/v5 v5.19.1/go.mod h1:Pb1v0c7/g8aGQJwx9Us09W85yGoyvSwuhEGMH7zjDKQ=
|
||||
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
|
||||
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
|
||||
github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4=
|
||||
github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80=
|
||||
github.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=
|
||||
github.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=
|
||||
github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8=
|
||||
github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4=
|
||||
github.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=
|
||||
github.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=
|
||||
github.com/go-openapi/spec v0.22.3 h1:qRSmj6Smz2rEBxMnLRBMeBWxbbOvuOoElvSvObIgwQc=
|
||||
github.com/go-openapi/spec v0.22.3/go.mod h1:iIImLODL2loCh3Vnox8TY2YWYJZjMAKYyLH2Mu8lOZs=
|
||||
github.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxgL3h1H8s=
|
||||
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
|
||||
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
|
||||
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
|
||||
github.com/go-openapi/jsonreference v1.0.1 h1:4zJ7AmYDKNmD3aSpfPnFNCFA5E80/xMHUNKgydaLh38=
|
||||
github.com/go-openapi/jsonreference v1.0.1/go.mod h1:dYplQXa6p5lXprLcJ8LE2iU7vNpXsAHDQ5ZAgL+Qx3A=
|
||||
github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w=
|
||||
github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0=
|
||||
github.com/go-openapi/spec v0.22.11 h1:/ZW28n4PCghbBpwjmvoMeyCeDwd6pIe715Ov8T3kAaQ=
|
||||
github.com/go-openapi/spec v0.22.11/go.mod h1:Ypu+u1KTejey7/QCr3j1ChdjwhGgXuklbH7l8+YP1J4=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4=
|
||||
github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU=
|
||||
github.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=
|
||||
github.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI=
|
||||
github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=
|
||||
github.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=
|
||||
github.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4 h1:IACsSvBhiNJwlDix7wq39SS2Fh7lUOCJRmx/4SN4sVo=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.4/go.mod h1:Mt0Ost9l3cUzVv4OEZG+WSeoHwjWLnarzMePNDAOBiM=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
|
||||
github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s=
|
||||
github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE=
|
||||
github.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=
|
||||
github.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8=
|
||||
github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=
|
||||
github.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw=
|
||||
github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=
|
||||
github.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=
|
||||
github.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2 h1:0+Y41Pz1NkbTHz8NngxTuAXxEodtNSI1WG1c/m5Akw4=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.0.2/go.mod h1:kme83333GCtJQHXQ8UKX3IBZu6z8T5Dvy5+CW3NLUUg=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
|
||||
github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls=
|
||||
github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
|
||||
github.com/go-openapi/swag/conv v0.27.3 h1:iqJFmGEjmX3AY0lSszABFqRVqOSt99XS0LzNIMJYuhU=
|
||||
github.com/go-openapi/swag/conv v0.27.3/go.mod h1:nPRmN6jgNme99hpf+nM0auDZGALWIqlwhisKPK/bQhQ=
|
||||
github.com/go-openapi/swag/conv v0.29.1 h1:AC4Eh/5c/eUDOUCzzsRC9ghmFgOSBHeRMGIngY0ZUGA=
|
||||
github.com/go-openapi/swag/conv v0.29.1/go.mod h1:S1X7/ZrBEZOC0Wc8AGxjbcGS92l3WEjA7aPtpl+RaqM=
|
||||
github.com/go-openapi/swag/jsonutils v0.27.3 h1:1DEz+O82frtSMBcos/7XIn1GnpNTbsD4Bru4Dc/uhRc=
|
||||
github.com/go-openapi/swag/jsonutils v0.27.3/go.mod h1:qiDCoQvzkMxrV3G8FLEdIU5L+EFYc0zcDOHWT3Yofvo=
|
||||
github.com/go-openapi/swag/jsonutils v0.29.1 h1:AFCxs0eQZ24/QyfhVHM2t49rMz7Vv3XCsZQI6yrNy+c=
|
||||
github.com/go-openapi/swag/jsonutils v0.29.1/go.mod h1:u3+sCfJpttDpcmS5kpm0yxL6GK0eWgODsx8Yw8fcqNM=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3 h1:h/eT9kmGCDdFLJF29lOhzLtF0FmP1AX2MhLJWVebsb8=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1 h1:BiiXE31Bx9SfpsMmOQj5KYpUhTZBpLVriVhJDuLuY2o=
|
||||
github.com/go-openapi/swag/loading v0.27.3 h1:L9nQkEgzU7QgFQL+pLEMfGUKxeM4pWwGwbET9Z3weW0=
|
||||
github.com/go-openapi/swag/loading v0.27.3/go.mod h1:rJ0NeaKsF4CVPnMGjPQl7JlSHzvD0bc2DKXLss1hiuE=
|
||||
github.com/go-openapi/swag/loading v0.29.1 h1:FCv5fG8UhTdDJa2R7w+5O9Ekpcbw7tt0nFWvmDKGBjc=
|
||||
github.com/go-openapi/swag/loading v0.29.1/go.mod h1:N0ESuem4p2oedKal8EJhciqnJ9Q9Wmt83L1CRB3Fouw=
|
||||
github.com/go-openapi/swag/pools v0.27.3 h1:gXjImP3F6/56wRRcFgEPld084Y6u2gs21ikPBt8NKBk=
|
||||
github.com/go-openapi/swag/pools v0.27.3/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
|
||||
github.com/go-openapi/swag/pools v0.29.1 h1:NRogYxdEW9SjRM4mkAOji9iefO4MRXq3p/ZJcoQbUKg=
|
||||
github.com/go-openapi/swag/pools v0.29.1/go.mod h1:leDcaghjkRAhCuCRv9NfJU5f0mjoU3cT/XZObhMk3pc=
|
||||
github.com/go-openapi/swag/stringutils v0.27.3 h1:Ru28hnbAvN5wycALQYy8IobHvASq+FUFMlp1QzLM0JI=
|
||||
github.com/go-openapi/swag/stringutils v0.27.3/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
|
||||
github.com/go-openapi/swag/stringutils v0.29.1 h1:1ykunK7iJQk1uOO7+oUH1ukbsK85fFCOiCFMOVSY+F0=
|
||||
github.com/go-openapi/swag/stringutils v0.29.1/go.mod h1:7fSqZ+z8Qc0tOfAAK0jVa5qFGrnIlRi6n7NeGGrr1vc=
|
||||
github.com/go-openapi/swag/typeutils v0.27.3 h1:l6SSrx5eR5/WVwrGNzN6bQ9WqL04mrxNBl9YgQ3rcJ4=
|
||||
github.com/go-openapi/swag/typeutils v0.27.3/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
|
||||
github.com/go-openapi/swag/typeutils v0.29.1 h1:Nzv9nhnlLCRBPQqfOX+7lB6Guju370or8StT+lIOf6M=
|
||||
github.com/go-openapi/swag/typeutils v0.29.1/go.mod h1:hxpgDZJVBkBsi/d3MIUosafoFdE5exaQRmVp0zwu3YE=
|
||||
github.com/go-openapi/swag/yamlutils v0.27.3 h1:cRFCAoYtslYn9L9T0xWryHy1t7c1MACC+DMj3CLvwvs=
|
||||
github.com/go-openapi/swag/yamlutils v0.27.3/go.mod h1:6JYBGj8sw/NawMllyZY+cTA8Mzk2etS3ZBASdcyPsiU=
|
||||
github.com/go-openapi/swag/yamlutils v0.29.1 h1:69w3tsBajm7MR/fejLy7HD/3J68Ys1SeeZMEzZ3w2sk=
|
||||
github.com/go-openapi/swag/yamlutils v0.29.1/go.mod h1:rgsp3vT/QdWzKwn43CigDwjOGIenPyTZMKnxEM8jZOA=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.6.1 h1:Jm+/ze2rMtbD98yen92AhATGLGREDYXG56Xr4gMjEtE=
|
||||
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
|
||||
github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
|
||||
github.com/go-openapi/testify/v2 v2.6.1 h1:6CNJhTjMzgaeaH8WhshcsZNPIvRemiOcFpU7seO/y7Q=
|
||||
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
|
||||
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
|
||||
github.com/go-playground/validator/v10 v10.30.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c h1:wpkoddUomPfHiOziHZixGO5ZBS73cKqVzZipfrLmO1w=
|
||||
github.com/go-shiori/dom v0.0.0-20230515143342-73569d674e1c/go.mod h1:oVDCh3qjJMLVUSILBRwrm+Bc6RNXGZYtoh9xdvf1ffM=
|
||||
github.com/go-shiori/go-readability v0.0.0-20251205110129-5db1dc9836f0 h1:A3B75Yp163FAIf9nLlFMl4pwIj+T3uKxfI7mbvvY2Ls=
|
||||
github.com/go-shiori/go-readability v0.0.0-20251205110129-5db1dc9836f0/go.mod h1:suxK0Wpz4BM3/2+z1mnOVTIWHDiMCIOGoKDCRumSsk0=
|
||||
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
|
||||
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-json v0.10.6 h1:p8HrPJzOakx/mn/bQtjgNjdTcN+/S6FcG2CTtQOrHVU=
|
||||
github.com/goccy/go-json v0.10.6/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
|
||||
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
|
||||
|
|
@ -278,20 +274,22 @@ github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
|
|||
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
|
||||
github.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=
|
||||
github.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.19 h1:mMOE7DN2+p76/EdIrmAy9B9bH+yC4563vmnJ34QR8i4=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.19/go.mod h1:rSEsBUemEBZEexP2y6jPp16LUmUbjmSbcPMQizR0o4k=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21 h1:OFdQ3tnCX/zaQ0Cedur3D3z7kI6HiLX9g3TiAN4/DFU=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.21/go.mod h1:L3D/IQExI6LqEjBdXcZQ1WluSgigQmSwBboFstVPM4w=
|
||||
github.com/googleapis/gax-go/v2 v2.23.0 h1:Tchl7qkvE7Ip3y+ztvNufYFvkfqTe7NfLTYGIdJRLuE=
|
||||
github.com/googleapis/gax-go/v2 v2.23.0/go.mod h1:rBQKOVJCdb8IFEzg+FCwlt1LP/xMDGuqUXhUG+XMXEg=
|
||||
github.com/googleapis/gax-go/v2 v2.24.0 h1:myMaPYyF9MecEmvQqMqomIwn9t/4KCZN9qnwsS76wlg=
|
||||
github.com/googleapis/gax-go/v2 v2.24.0/go.mod h1:IaTHBDd7NHxSCiu0vEs8pQZu4dGZrWwuSoxCnk16OFM=
|
||||
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hasura/go-graphql-client v0.15.1 h1:mCb5I+8Bk3FU3GKWvf/zDXkTh7FbGlqJmP3oisBdnN8=
|
||||
github.com/hasura/go-graphql-client v0.15.1/go.mod h1:jfSZtBER3or+88Q9vFhWHiFMPppfYILRyl+0zsgPIIw=
|
||||
github.com/hasura/go-graphql-client v0.16.0 h1:DQLfp+djj4j5NPdJkGYym8J55hpm5etML1zqgco78Qc=
|
||||
github.com/hasura/go-graphql-client v0.16.0/go.mod h1:z/sO2T0zI+HnPNIevQcs+7xA6/gDOc8hgHMrNBzfL2c=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/invopop/jsonschema v0.14.0 h1:MHQqLhvpNUZfw+hM3AZDYK7jxO8FZoQeQM77g8iyZjg=
|
||||
github.com/invopop/jsonschema v0.14.0/go.mod h1:ygm6C2EaVNMBDPpaPlnOA2pFAxBnxGjFlMZABxm9n2I=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
|
||||
github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4=
|
||||
|
|
@ -302,14 +300,12 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
|
||||
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/keybase/go-keychain v0.0.1 h1:way+bWYa6lDppZoZcgMbYsvC7GxljxrskdNInRtuthU=
|
||||
github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
|
|
@ -319,19 +315,17 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||
github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8=
|
||||
github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/leodido/go-urn v1.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
|
||||
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
|
||||
github.com/mailru/easyjson v0.9.2 h1:dX8U45hQsZpxd80nLvDGihsQ/OxlvTkVUXH2r/8cb2M=
|
||||
github.com/mailru/easyjson v0.9.2/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk=
|
||||
github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.37 h1:3DOZp4cXis1cUIpCfXLtmlGolNLp2VEqhiB/PARNBIg=
|
||||
github.com/mattn/go-sqlite3 v1.14.37/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/mattn/go-sqlite3 v1.14.48 h1:7XHIgl0a8HwOaiK4E47ozLkST78rR9+OtNGx27D/TFs=
|
||||
github.com/mattn/go-sqlite3 v1.14.48/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/mattn/go-sqlite3 v1.14.50 h1:dmdFvo1XG4MPzA4IkAmE9upVz/Nj31uRoM5+jC8hYbY=
|
||||
github.com/mattn/go-sqlite3 v1.14.50/go.mod h1:6JTjA44L93a0QCyJef5YvlPoKXntQPjzWv5gtm9sB6w=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -339,10 +333,10 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
|||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd17NIEQ=
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1/go.mod h1:Vee0/9RD3Quc/NmwEjzzD7VTZ+Ir7QbXocrkhOzmUKA=
|
||||
github.com/ollama/ollama v0.16.2 h1:iZ/vV7t9QRU0MXfwWXl0+6HBb2xUULubksqK0dfB+og=
|
||||
github.com/ollama/ollama v0.16.2/go.mod h1:FEk95NbAJJZk+t7cLh+bPGTul72j1O3PLLlYNV3FVZ0=
|
||||
github.com/ollama/ollama v0.18.2 h1:RsOY8oZ6TufRiPgsSlKJp4/V/X+oBREscUlEHZfd554=
|
||||
github.com/ollama/ollama v0.18.2/go.mod h1:tCX4IMV8DHjl3zY0THxuEkpWDZSOchJpzTuLACpMwFw=
|
||||
github.com/ollama/ollama v0.32.3 h1:hASAqO6McQAgOhLG3Fcgrj//aXK5rMMWxqFM4eAKkGc=
|
||||
github.com/ollama/ollama v0.32.3/go.mod h1:b1ydCt2oVg0VAg22WWDgCbwW0AyOaRKAFzlS91NI4OY=
|
||||
github.com/ollama/ollama v0.33.1 h1:nEeqiZzqlO+RfjJ7AvfUgrYlV/wbrxPl2szRg9mtxPU=
|
||||
github.com/ollama/ollama v0.33.1/go.mod h1:Kekx/+OtFZHmqbkVH/QUUDVcMQS+1pg1dcz4Qy7TGn4=
|
||||
github.com/onsi/gomega v1.34.1 h1:EUMJIKUjM8sKjYbtxQI9A4z2o+rruxnzNvpknOXie6k=
|
||||
github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeDLwwY=
|
||||
github.com/openai/openai-go v1.12.0 h1:NBQCnXzqOTv5wsgNC36PrFEiskGfO5wccfCWDo9S1U0=
|
||||
|
|
@ -351,10 +345,12 @@ github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8=
|
|||
github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I=
|
||||
github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs=
|
||||
github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
|
||||
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1 h1:5319HDO0aw4DA4gzi+zv4FXU9UlSs3xGZ40wcP1nBjY=
|
||||
github.com/pb33f/ordered-map/v2 v2.3.1/go.mod h1:qxFQgd0PkVUtOMCkTapqotNgzRhMPL7VvaHKbd1HnmQ=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
|
||||
github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
@ -364,23 +360,23 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0 h1:APacT+iIaNF6fd8AGEiN3bT/Jtkd2jz4v4TzM7MFjy0=
|
||||
github.com/quic-go/go-ossfuzz-seeds v0.1.0/go.mod h1:3IOHRbJIc+L6YKMwfDtJAM9Vj9k0YY4muhuyUYk5tbk=
|
||||
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
|
||||
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
|
||||
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
|
||||
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
|
||||
github.com/quic-go/quic-go v0.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA=
|
||||
github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
|
||||
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
|
||||
github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
|
||||
github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
|
||||
github.com/scylladb/termtables v0.0.0-20191203121021-c4c0b6d42ff4/go.mod h1:C1a7PQSMz9NShzorzCiG2fk9+xuCgLkPeCvMHYR2OWg=
|
||||
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
|
||||
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.15.0 h1:YFa63ohQda5MbuLedMHzlFHtOFeifHf+G6cSwp9xKnU=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.15.0/go.mod h1:q9FDvA+UCmtcgFBmb0aZbDMzgcnJipowTl8Ycl+aDko=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.16.1 h1://Xa7P0F/eOIcJ/6SehTqgr2MRnzPfI8LdAwlAYTnTs=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.16.1/go.mod h1:5dckcaxoFKtJEfNRc9+OhL6p/W/TKxd0gbSzc1YkmkI=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
|
|
@ -389,6 +385,8 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
|
|||
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
|
||||
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 h1:uOfcYT+3QungH6tIGSVCR/Y3KJmgJiHcojJbMTPDZAI=
|
||||
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1/go.mod h1:L1MQhA6x4dn9r007T033lsaZMv9EmBAdXyU/+EF40fo=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
|
|
@ -403,6 +401,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
|
|||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
|
||||
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
|
||||
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
|
||||
|
|
@ -410,8 +410,8 @@ github.com/swaggo/gin-swagger v1.6.1/go.mod h1:LQ+hJStHakCWRiK/YNYtJOu4mR2FP+pxL
|
|||
github.com/swaggo/swag v1.16.6 h1:qBNcx53ZaX+M5dxVyTrgQ0PJ/ACK+NzhwcbieTt+9yI=
|
||||
github.com/swaggo/swag v1.16.6/go.mod h1:ngP2etMK5a0P3QBizic5MEwpRmluJZPHjXcMoj4Xesg=
|
||||
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
|
||||
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
|
|
@ -424,101 +424,86 @@ github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS
|
|||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
|
||||
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/ugorji/go/codec v1.3.2 h1:zkEASHHyEClGeURfgNT9PJZVfAbs9oEX9QXggwWNJbc=
|
||||
github.com/ugorji/go/codec v1.3.2/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0 h1:CxWDGQYY8QQwNjAl/aq2sfWakdnWZynnqJ9F4DhHbP8=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2 h1:b6o2m7zL8g2URuO8urBedAylxojybKXNZTxgkOcl+2w=
|
||||
go.mongodb.org/mongo-driver/v2 v2.8.2/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0 h1:XmiuHzgJt067+a6kwyAzkhXooYVv3/TOw9cM2VfJgUM=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.65.0/go.mod h1:KDgtbWKTQs4bM+VPUr6WlL9m/WXcmkCcBlIzqxPGzmI=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0 h1:7iP2uCb7sGddAr30RRS6xjKy7AZ2JtTOPA3oolgVSw8=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.65.0/go.mod h1:c7hN3ddxs/z6q9xwvfLPk+UHlWRQyaeR1LdgfL/66l0=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms=
|
||||
go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g=
|
||||
go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8=
|
||||
go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0 h1:mtmdVqgQkeRxHgRv4qhyJduP3fYJRMX4AtAlbuWdCYw=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.40.0/go.mod h1:4Z2bGMf0KSK3uRjlczMOeMhKU2rhUqdWNoKcYrtcBPg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw=
|
||||
go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 h1:2yEATaop1/a1I4psnSLgWVPLWwCzkqWakgJy7xTDVy0=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0/go.mod h1:D7J12YRapIekYyPWgGPlA/23pRmpSEZC5xJC/TTLI9U=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0 h1:B2h3uqicet1CT2N5TOFhS+Gq++9i0/CLmaxvhmhtP5s=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.71.0/go.mod h1:dylvB+ZiiwMvsDij9O84Uy7SijLgHMX4mbkncds+4Sw=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 h1:8tvICD4vSTOOsNrsI4Ljf6C+6UKvpTEH5XY3JMoyPoo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0/go.mod h1:z9+yiacE0IHRqM4qFfkbt/JYlmYXgss8GY/jXoNuPJI=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0 h1:3g7B90UzBltIDKq1/5mrTGxTnOFDV0ICOhLoxiZ8jlg=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.71.0/go.mod h1:Ef8SuTh59BT7+ofpDxN9z+yOlc4t2GjLmKDgYNJL/NU=
|
||||
go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU=
|
||||
go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc=
|
||||
go.opentelemetry.io/otel v1.46.0 h1:FHt5/CDyVxi/8IM1CH7VE/rRgq3kLHa2mSTVMO8AWyc=
|
||||
go.opentelemetry.io/otel v1.46.0/go.mod h1:Gj3SEScelsNC45tp4nSxRYlS+f5iez7W8XPMCt905kE=
|
||||
go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc=
|
||||
go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo=
|
||||
go.opentelemetry.io/otel/metric v1.46.0 h1:yBnkXvgV7AXFILZc5K6IZe/CBFF3OS7BJ8ov6/lj0K8=
|
||||
go.opentelemetry.io/otel/metric v1.46.0/go.mod h1:iPmdWqifKUdzziPkvvzIJXITl56fQx2mGM/DHLB3/2o=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58=
|
||||
go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0=
|
||||
go.opentelemetry.io/otel/sdk v1.46.0 h1:h5CNQQjEbuQXY/JfZtgt3i7HVFV3aHPO2OAwO2eTYPI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.46.0 h1:0piZ26EG4RBfebb2jhDH6ERCYHoVWduc3kLgPCwSnSE=
|
||||
go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk=
|
||||
go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE=
|
||||
go.opentelemetry.io/otel/trace v1.46.0 h1:OULy7ccdJnZtJ0UDYFOIGaCmiWzJ8Vi2G/Rsu60qs1c=
|
||||
go.opentelemetry.io/otel/trace v1.46.0/go.mod h1:J7GAXweO77XSFkB/rmAqk9D6ihszhFjLU+d9WuUxDLI=
|
||||
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
|
||||
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/arch v0.24.0 h1:qlJ3M9upxvFfwRM51tTg3Yl+8CP9vCC1E7vlFpgv99Y=
|
||||
golang.org/x/arch v0.24.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
|
||||
golang.org/x/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
|
||||
golang.org/x/arch v0.25.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6 h1:1h7H1ohdUh93/FyE4YaDa1Zh64K6VVbjF4K6WUxMtH4=
|
||||
go.yaml.in/yaml/v4 v4.0.0-rc.6/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=
|
||||
golang.org/x/arch v0.29.0 h1:8sSET5wB0+exBm0FGmOtdHMqjlRdV2DRD3/IV6OZgho=
|
||||
golang.org/x/arch v0.29.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/arch v0.30.0 h1:sB9h+1gRGa2+LauFSV0tm8bK1J2yo1bx6/Uyi/P6DTU=
|
||||
golang.org/x/arch v0.30.0/go.mod h1:0X+GdSIP+kL5wPmpK7sdkEVTt2XoYP0cSjQSbZBwOi8=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
||||
golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts=
|
||||
golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b h1:QoALfVG9rhQ/M7vYDScfPdWjGL9dlsVVM5VGh7aKoAA=
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b/go.mod h1:U6Lno4MTRCDY+Ba7aCcauB9T60gsv5s4ralQzP72ZoQ=
|
||||
golang.org/x/crypto v0.54.0 h1:YLIA59K4fiNzHzjnZt2tUJQjQtUWfWbeHBqKtk3eScw=
|
||||
golang.org/x/crypto v0.54.0/go.mod h1:KWL8ny2AZdGR2cWmzeHrp2azQPGogOv+HeQaVEXC2dk=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8=
|
||||
golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/mod v0.38.0 h1:MECBjubtXD7yj4HrhIUcywNaGeNVUdfVnxmPajOk4yk=
|
||||
golang.org/x/mod v0.38.0/go.mod h1:V6Xz0pq8TQ3dGqVQ1FVHuelZpAL0uNhSkk9ogYP3c40=
|
||||
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
|
||||
golang.org/x/net v0.50.0 h1:ucWh9eiCGyDR3vtzso0WMQinm2Dnt8cFMuQa9K33J60=
|
||||
golang.org/x/net v0.50.0/go.mod h1:UgoSli3F/pBgdJBHCTc+tp3gmrU4XswgGRgtnwWTfyM=
|
||||
golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
|
||||
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
|
||||
golang.org/x/net v0.57.0 h1:K5+3DljvIuDG9/Jv9rvyMywYNFCQ9RSUY6OOTTkT+tE=
|
||||
golang.org/x/net v0.57.0/go.mod h1:KpXc8iv+r3XplLAG/f7Jsf9RPszJzdR0f58q9vGOuEU=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
|
||||
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -530,81 +515,60 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
|
||||
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||
golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg=
|
||||
golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM=
|
||||
golang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk=
|
||||
golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA=
|
||||
golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/text v0.40.0 h1:Ub2Z6/xjgF1WrYQz2nuITOEegKFtiIy+rieRJ5lHZKs=
|
||||
golang.org/x/text v0.40.0/go.mod h1:hpnzDAfGV753zIKo+wk3u1bVKCGPbrnF7+7LBF/UHVY=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
|
||||
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/tools v0.48.0 h1:3+hClM1aLL5mjMKm5ovokw9epgRXPuu2tILgismM6RE=
|
||||
golang.org/x/tools v0.48.0/go.mod h1:08xX0orndb/F7jJxGDicx061tyd5pcMto75YMAXr6lk=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
google.golang.org/api v0.266.0 h1:hco+oNCf9y7DmLeAtHJi/uBAY7n/7XC9mZPxu1ROiyk=
|
||||
google.golang.org/api v0.266.0/go.mod h1:Jzc0+ZfLnyvXma3UtaTl023TdhZu6OMBP9tJ+0EmFD0=
|
||||
google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
|
||||
google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
|
||||
google.golang.org/genai v1.46.0 h1:RSsfeMaV30m8PxLOW4RUIb5ybw+mw+UBf1vSpsQTQbE=
|
||||
google.golang.org/genai v1.46.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||
google.golang.org/genai v1.51.0 h1:IZGuUqgfx40INv3hLFGCbOSGp0qFqm7LVmDghzNIYqg=
|
||||
google.golang.org/genai v1.51.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=
|
||||
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409 h1:merA0rdPeUV3YIIfHHcH4qBkiQAc1nfCKSI7lB4cV2M=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260128011058-8636f8732409/go.mod h1:fl8J1IvUjCilwZzQowmw2b7HQB2eAuYBabMXzWurF+I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 h1:mWPCjDEyshlQYzBpMNHaEof6UX1PmHcaUODUywQ0uac=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
|
||||
google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.290.0 h1:eMw0Xo+IfbbMlKmW7aHvpyQRv9RCXuWx/vs8AD+0x9A=
|
||||
google.golang.org/api v0.290.0/go.mod h1:weJZ3lldHFYI0DBFNKpJelUDNnusTt5YaOEgxvt8ci8=
|
||||
google.golang.org/api v0.294.0 h1:8gASjJxdtcIieB3OqbkLcF0FfbXVNqKtU5iozD1ssvA=
|
||||
google.golang.org/api v0.294.0/go.mod h1:02qB8+Ox1ZFzcaKFMguy1nQLJmSIyvV6Ff4txJEXtl4=
|
||||
google.golang.org/genai v1.65.0 h1:6QK3Rjsx0iuJjbDCE1vf1VUr1IEjRDpvexGGnhxoAIk=
|
||||
google.golang.org/genai v1.65.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU=
|
||||
google.golang.org/genai v1.70.0 h1:V9oYOBvTDYbmeklOHjvxGeeLau71WiS2CWKrktQtDok=
|
||||
google.golang.org/genai v1.70.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df h1:O3ig1i5WDDzsVzRp+cCdgelT9vXnlnOFdlEeFtL4HCc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
|
||||
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
|
||||
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
|
||||
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
|
|
@ -612,6 +576,7 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV
|
|||
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
|
@ -88,7 +89,7 @@ func handleChatProcessing(currentFlags *Flags, registry *core.PluginRegistry, me
|
|||
chatOptions.AudioFormat = "wav" // Default to WAV format
|
||||
}
|
||||
|
||||
if session, err = chatter.Send(chatReq, chatOptions); err != nil {
|
||||
if session, err = chatter.Send(context.Background(), chatReq, chatOptions); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func Cli(version string) (err error) {
|
|||
func processYoutubeVideo(
|
||||
flags *Flags, registry *core.PluginRegistry, videoId string) (message string, err error) {
|
||||
|
||||
if (!flags.YouTubeComments && !flags.YouTubeMetadata) || flags.YouTubeTranscript || flags.YouTubeTranscriptWithTimestamps {
|
||||
if (!flags.YouTubeComments && !flags.YouTubeMetadata && !flags.YouTubeVisual) || flags.YouTubeTranscript || flags.YouTubeTranscriptWithTimestamps {
|
||||
var transcript string
|
||||
var language = "en"
|
||||
if flags.Language != "" || registry.Language.DefaultLanguage.Value != "" {
|
||||
|
|
@ -139,6 +139,20 @@ func processYoutubeVideo(
|
|||
message = AppendMessage(message, transcript)
|
||||
}
|
||||
|
||||
if flags.YouTubeVisual {
|
||||
var visualText string
|
||||
var language = "en"
|
||||
if flags.Language != "" {
|
||||
language = flags.Language
|
||||
} else if registry.Language.DefaultLanguage.Value != "" {
|
||||
language = registry.Language.DefaultLanguage.Value
|
||||
}
|
||||
if visualText, err = registry.YouTube.GrabVisual(videoId, language, flags.YtDlpArgs, flags.YouTubeVisualSensitivity, flags.YouTubeVisualFps); err != nil {
|
||||
return
|
||||
}
|
||||
message = AppendMessage(message, visualText)
|
||||
}
|
||||
|
||||
if flags.YouTubeComments {
|
||||
var comments []string
|
||||
if comments, err = registry.YouTube.GrabComments(videoId); err != nil {
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ type Flags struct {
|
|||
Raw bool `short:"r" long:"raw" yaml:"raw" description:"Use the defaults of the model without sending chat options (temperature, top_p, etc.). Only affects OpenAI-compatible providers. Anthropic models always use smart parameter selection to comply with model-specific requirements."`
|
||||
FrequencyPenalty float64 `short:"F" long:"frequencypenalty" yaml:"frequencypenalty" description:"Set frequency penalty" default:"0.0"`
|
||||
ListPatterns bool `short:"l" long:"listpatterns" description:"List all patterns"`
|
||||
ReadPattern string `long:"readpattern" description:"Print the contents of the named pattern to the terminal"`
|
||||
ListAllModels bool `short:"L" long:"listmodels" description:"List all available models"`
|
||||
ListAllContexts bool `short:"x" long:"listcontexts" description:"List all contexts"`
|
||||
ListAllSessions bool `short:"X" long:"listsessions" description:"List all sessions"`
|
||||
|
|
@ -56,6 +57,9 @@ type Flags struct {
|
|||
YouTubePlaylist bool `long:"playlist" description:"Prefer playlist over video if both ids are present in the URL"`
|
||||
YouTubeTranscript bool `long:"transcript" description:"Grab transcript from YouTube video and send to chat (it is used per default)."`
|
||||
YouTubeTranscriptWithTimestamps bool `long:"transcript-with-timestamps" description:"Grab transcript from YouTube video with timestamps and send to chat"`
|
||||
YouTubeVisual bool `long:"visual" description:"Extract visual data from video using OCR and FFmpeg"`
|
||||
YouTubeVisualSensitivity float64 `long:"visual-sensitivity" description:"Tolerance for FFmpeg scene detection (0.0 - 1.0)" default:"0.4"`
|
||||
YouTubeVisualFps int `long:"visual-fps" description:"Extract a specific number of frames per second instead of using scene detection" default:"0"`
|
||||
YouTubeComments bool `long:"comments" description:"Grab comments from YouTube video and send to chat"`
|
||||
YouTubeMetadata bool `long:"metadata" description:"Output video metadata"`
|
||||
YtDlpArgs string `long:"yt-dlp-args" yaml:"ytDlpArgs" description:"Additional arguments to pass to yt-dlp (e.g. '--cookies-from-browser brave')"`
|
||||
|
|
@ -74,8 +78,8 @@ type Flags struct {
|
|||
DryRun bool `long:"dry-run" description:"Show what would be sent to the model without actually sending it"`
|
||||
Serve bool `long:"serve" description:"Serve the Fabric Rest API"`
|
||||
ServeOllama bool `long:"serveOllama" description:"Serve the Fabric Rest API with ollama endpoints"`
|
||||
ServeAddress string `long:"address" description:"The address to bind the REST API" default:":8080"`
|
||||
ServeAPIKey string `long:"api-key" description:"API key used to secure server routes" default:""`
|
||||
ServeAddress string `long:"address" description:"The address to bind the REST API" default:"127.0.0.1:8080"`
|
||||
ServeAPIKey string `long:"api-key" env:"FABRIC_API_KEY" description:"API key used to secure server routes" default:""`
|
||||
Config string `long:"config" description:"Path to YAML config file"`
|
||||
Version bool `long:"version" description:"Print current version"`
|
||||
ListExtensions bool `long:"listextensions" description:"List all registered extensions"`
|
||||
|
|
@ -85,7 +89,7 @@ type Flags struct {
|
|||
ListStrategies bool `long:"liststrategies" description:"List all strategies"`
|
||||
ListVendors bool `long:"listvendors" description:"List all vendors"`
|
||||
ShellCompleteOutput bool `long:"shell-complete-list" description:"Output raw list without headers/formatting (for shell completion)"`
|
||||
Search bool `long:"search" description:"Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"`
|
||||
Search bool `long:"search" description:"Enable web search tool for supported models (Anthropic, OpenAI, Gemini, Grok)"`
|
||||
SearchLocation string `long:"search-location" description:"Set location for web search results (e.g., 'America/Los_Angeles')"`
|
||||
ImageFile string `long:"image-file" description:"Save generated image to specified file path (e.g., 'output.png')"`
|
||||
ImageSize string `long:"image-size" description:"Image dimensions: 1024x1024, 1536x1024, 1024x1536, auto (default: auto)"`
|
||||
|
|
@ -105,7 +109,7 @@ type Flags struct {
|
|||
Notification bool `long:"notification" yaml:"notification" description:"Send desktop notification when command completes"`
|
||||
NotificationCommand string `long:"notification-command" yaml:"notificationCommand" description:"Custom command to run for notifications (overrides built-in notifications)"`
|
||||
Thinking domain.ThinkingLevel `long:"thinking" yaml:"thinking" description:"Set reasoning/thinking level (e.g., off, low, medium, high, or numeric tokens for Anthropic or Google Gemini)"`
|
||||
ShowMetadata bool `long:"show-metadata" description:"Print metadata to stderr"`
|
||||
ShowMetadata bool `long:"show-metadata" description:"Print metadata (input/output tokens) to stderr"`
|
||||
Debug int `long:"debug" description:"Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)" default:"0"`
|
||||
}
|
||||
|
||||
|
|
@ -119,8 +123,7 @@ func Init() (ret *Flags, err error) {
|
|||
// Create mapping from flag names (both short and long) to yaml tag names
|
||||
flagToYamlTag := make(map[string]string)
|
||||
t := reflect.TypeFor[Flags]()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
for field := range t.Fields() {
|
||||
yamlTag := field.Tag.Get("yaml")
|
||||
if yamlTag != "" {
|
||||
longTag := field.Tag.Get("long")
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ var flagDescriptionMap = map[string]string{
|
|||
"raw": "use_model_defaults_raw_help",
|
||||
"frequencypenalty": "set_frequency_penalty",
|
||||
"listpatterns": "list_all_patterns",
|
||||
"readpattern": "print_pattern_contents",
|
||||
"listmodels": "list_all_available_models",
|
||||
"listcontexts": "list_all_contexts",
|
||||
"listsessions": "list_all_sessions",
|
||||
|
|
@ -43,9 +44,13 @@ var flagDescriptionMap = map[string]string{
|
|||
"playlist": "prefer_playlist_over_video",
|
||||
"transcript": "grab_transcript_from_youtube",
|
||||
"transcript-with-timestamps": "grab_transcript_with_timestamps",
|
||||
"visual": "youtube_extract_visual_data_help",
|
||||
"visual-sensitivity": "youtube_visual_sensitivity_help",
|
||||
"visual-fps": "youtube_visual_fps_help",
|
||||
"comments": "grab_comments_from_youtube",
|
||||
"metadata": "output_video_metadata",
|
||||
"yt-dlp-args": "additional_yt_dlp_args",
|
||||
"spotify": "spotify_url_help",
|
||||
"language": "specify_language_code",
|
||||
"scrape_url": "scrape_website_url",
|
||||
"scrape_question": "search_question_jina",
|
||||
|
|
@ -91,6 +96,7 @@ var flagDescriptionMap = map[string]string{
|
|||
"notification": "send_desktop_notification",
|
||||
"notification-command": "custom_notification_command",
|
||||
"thinking": "set_reasoning_thinking_level",
|
||||
"show-metadata": "print_metadata_to_stderr",
|
||||
"debug": "set_debug_level",
|
||||
}
|
||||
|
||||
|
|
@ -139,8 +145,7 @@ func (h *TranslatedHelpWriter) getTranslatedDescription(flagName string) string
|
|||
func (h *TranslatedHelpWriter) getOriginalDescription(flagName string) string {
|
||||
flagsType := reflect.TypeFor[Flags]()
|
||||
|
||||
for i := 0; i < flagsType.NumField(); i++ {
|
||||
field := flagsType.Field(i)
|
||||
for field := range flagsType.Fields() {
|
||||
longTag := field.Tag.Get("long")
|
||||
|
||||
if longTag == flagName {
|
||||
|
|
@ -219,9 +224,7 @@ func (h *TranslatedHelpWriter) writeAllFlags() {
|
|||
// Use direct reflection on the Flags struct to get all flag definitions
|
||||
flagsType := reflect.TypeFor[Flags]()
|
||||
|
||||
for i := 0; i < flagsType.NumField(); i++ {
|
||||
field := flagsType.Field(i)
|
||||
|
||||
for field := range flagsType.Fields() {
|
||||
shortTag := field.Tag.Get("short")
|
||||
longTag := field.Tag.Get("long")
|
||||
defaultTag := field.Tag.Get("default")
|
||||
|
|
|
|||
|
|
@ -29,6 +29,11 @@ func handleListingCommands(currentFlags *Flags, fabricDb *fsdb.Db, registry *cor
|
|||
return true, nil
|
||||
}
|
||||
|
||||
if currentFlags.ReadPattern != "" {
|
||||
err = fabricDb.Patterns.PrintPattern(currentFlags.ReadPattern)
|
||||
return true, err
|
||||
}
|
||||
|
||||
if currentFlags.ListPatterns {
|
||||
// Check if patterns exist before listing
|
||||
var names []string
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ import (
|
|||
restapi "github.com/danielmiessler/fabric/internal/server"
|
||||
)
|
||||
|
||||
// serveOllama is a seam for tests, because the real entry point blocks
|
||||
// on a listening socket.
|
||||
var serveOllama = restapi.ServeOllama
|
||||
|
||||
// handleSetupAndServerCommands handles setup and server-related commands
|
||||
// Returns (handled, error) where handled indicates if a command was processed and should exit
|
||||
func handleSetupAndServerCommands(currentFlags *Flags, registry *core.PluginRegistry, version string) (handled bool, err error) {
|
||||
|
|
@ -22,7 +26,7 @@ func handleSetupAndServerCommands(currentFlags *Flags, registry *core.PluginRegi
|
|||
|
||||
if currentFlags.ServeOllama {
|
||||
registry.ConfigureVendors()
|
||||
err = restapi.ServeOllama(registry, currentFlags.ServeAddress, version)
|
||||
err = serveOllama(registry, currentFlags.ServeAddress, version, currentFlags.ServeAPIKey)
|
||||
return true, err
|
||||
}
|
||||
|
||||
|
|
|
|||
37
internal/cli/setup_server_test.go
Normal file
37
internal/cli/setup_server_test.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai"
|
||||
)
|
||||
|
||||
// The --serveOllama path must pass the address, version, and API key
|
||||
// flags through to ServeOllama unchanged.
|
||||
func TestHandleSetupAndServerCommands_ServeOllamaWiring(t *testing.T) {
|
||||
var gotAddress, gotVersion, gotKey string
|
||||
prev := serveOllama
|
||||
serveOllama = func(_ *core.PluginRegistry, address, version, apiKey string) error {
|
||||
gotAddress, gotVersion, gotKey = address, version, apiKey
|
||||
return nil
|
||||
}
|
||||
defer func() { serveOllama = prev }()
|
||||
|
||||
registry := &core.PluginRegistry{
|
||||
VendorManager: ai.NewVendorsManager(),
|
||||
VendorsAll: ai.NewVendorsManager(),
|
||||
}
|
||||
flags := &Flags{ServeOllama: true, ServeAddress: "127.0.0.1:9999", ServeAPIKey: "secret"}
|
||||
handled, err := handleSetupAndServerCommands(flags, registry, "v-test")
|
||||
if err != nil {
|
||||
t.Fatalf("handleSetupAndServerCommands() error = %v", err)
|
||||
}
|
||||
if !handled {
|
||||
t.Fatal("handleSetupAndServerCommands() handled = false, want true")
|
||||
}
|
||||
if gotAddress != "127.0.0.1:9999" || gotVersion != "v-test" || gotKey != "secret" {
|
||||
t.Fatalf("ServeOllama got (%q, %q, %q), want (127.0.0.1:9999, v-test, secret)",
|
||||
gotAddress, gotVersion, gotKey)
|
||||
}
|
||||
}
|
||||
|
|
@ -57,7 +57,7 @@ func joinPromptSections(parts ...string) string {
|
|||
}
|
||||
|
||||
// Send processes a chat request and applies file changes for create_coding_feature pattern
|
||||
func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (session *fsdb.Session, err error) {
|
||||
func (o *Chatter) Send(ctx context.Context, request *domain.ChatRequest, opts *domain.ChatOptions) (session *fsdb.Session, err error) {
|
||||
// Use o.model (normalized) for NeedsRawMode check instead of opts.Model
|
||||
// This ensures case-insensitive model names work correctly (e.g., "GPT-5" → "gpt-5")
|
||||
if o.vendor.NeedsRawMode(o.model) {
|
||||
|
|
@ -107,7 +107,7 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s
|
|||
|
||||
go func() {
|
||||
defer close(done)
|
||||
if streamErr := o.vendor.SendStream(session.GetVendorMessages(), opts, responseChan); streamErr != nil {
|
||||
if streamErr := o.vendor.SendStream(ctx, session.GetVendorMessages(), opts, responseChan); streamErr != nil {
|
||||
recordFirstStreamError(errChan, streamErr)
|
||||
}
|
||||
}()
|
||||
|
|
@ -168,7 +168,7 @@ func (o *Chatter) Send(request *domain.ChatRequest, opts *domain.ChatOptions) (s
|
|||
// No errors, continue
|
||||
}
|
||||
} else {
|
||||
if message, err = o.vendor.Send(context.Background(), session.GetVendorMessages(), opts); err != nil {
|
||||
if message, err = o.vendor.Send(ctx, session.GetVendorMessages(), opts); err != nil {
|
||||
return
|
||||
}
|
||||
if debuglog.GetLevel() >= debuglog.Wire {
|
||||
|
|
|
|||
|
|
@ -44,11 +44,11 @@ func (m *mockVendor) Setup() error {
|
|||
func (m *mockVendor) SetupFillEnvFileContent(*bytes.Buffer) {
|
||||
}
|
||||
|
||||
func (m *mockVendor) ListModels() ([]string, error) {
|
||||
func (m *mockVendor) ListModels(context.Context) ([]string, error) {
|
||||
return []string{"test-model"}, nil
|
||||
}
|
||||
|
||||
func (m *mockVendor) SendStream(messages []*chat.ChatCompletionMessage, opts *domain.ChatOptions, responseChan chan domain.StreamUpdate) error {
|
||||
func (m *mockVendor) SendStream(_ context.Context, messages []*chat.ChatCompletionMessage, opts *domain.ChatOptions, responseChan chan domain.StreamUpdate) error {
|
||||
// Send chunks if provided (for successful streaming test)
|
||||
if m.streamChunks != nil {
|
||||
for _, chunk := range m.streamChunks {
|
||||
|
|
@ -180,7 +180,7 @@ func TestChatter_Send_SuppressThink(t *testing.T) {
|
|||
return "<think>hidden</think> visible", nil
|
||||
}
|
||||
|
||||
session, err := chatter.Send(request, opts)
|
||||
session, err := chatter.Send(context.Background(), request, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Send returned error: %v", err)
|
||||
}
|
||||
|
|
@ -296,7 +296,7 @@ func TestChatter_Send_StreamingErrorPropagation(t *testing.T) {
|
|||
}
|
||||
|
||||
// Call Send and expect it to return the streaming error
|
||||
session, err := chatter.Send(request, opts)
|
||||
session, err := chatter.Send(context.Background(), request, opts)
|
||||
|
||||
// Verify that the error from SendStream is propagated
|
||||
if err == nil {
|
||||
|
|
@ -353,7 +353,7 @@ func TestChatter_Send_StreamingErrorUpdateAndReturnDoesNotDeadlock(t *testing.T)
|
|||
|
||||
done := make(chan sendResult, 1)
|
||||
go func() {
|
||||
session, err := chatter.Send(request, opts)
|
||||
session, err := chatter.Send(context.Background(), request, opts)
|
||||
done <- sendResult{session: session, err: err}
|
||||
}()
|
||||
|
||||
|
|
@ -411,7 +411,7 @@ func TestChatter_Send_StreamingSuccessfulAggregation(t *testing.T) {
|
|||
}
|
||||
|
||||
// Call Send and expect successful aggregation
|
||||
session, err := chatter.Send(request, opts)
|
||||
session, err := chatter.Send(context.Background(), request, opts)
|
||||
|
||||
// Verify no error occurred
|
||||
if err != nil {
|
||||
|
|
@ -494,7 +494,7 @@ func TestChatter_Send_StreamingMetadataPropagation(t *testing.T) {
|
|||
}
|
||||
|
||||
// Call Send
|
||||
_, err := chatter.Send(request, opts)
|
||||
_, err := chatter.Send(context.Background(), request, opts)
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, but got: %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
|
|
@ -73,6 +74,24 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
|
|||
vendors := []ai.Vendor{}
|
||||
|
||||
// Add non-OpenAI compatible clients
|
||||
codexClient := codex.NewClient()
|
||||
codexClient.WithStoreLock = func(fn func() error) error {
|
||||
return db.WithEnvLock(func() error {
|
||||
env, err := db.ReadEnvFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
codexClient.LoadEnvSettings(env)
|
||||
return fn()
|
||||
})
|
||||
}
|
||||
codexClient.TokenPersist = func() error {
|
||||
return db.ApplyEnvUpdates(map[string]string{
|
||||
codexClient.AccessToken.EnvVariable: strings.TrimSpace(codexClient.AccessToken.Value),
|
||||
codexClient.RefreshToken.EnvVariable: strings.TrimSpace(codexClient.RefreshToken.Value),
|
||||
codexClient.AccountID.EnvVariable: strings.TrimSpace(codexClient.AccountID.Value),
|
||||
})
|
||||
}
|
||||
vendors = append(vendors,
|
||||
openai.NewClient(),
|
||||
digitalocean.NewClient(),
|
||||
|
|
@ -86,7 +105,7 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
|
|||
lmstudio.NewClient(),
|
||||
exolab.NewClient(),
|
||||
perplexity.NewClient(),
|
||||
codex.NewClient(),
|
||||
codexClient,
|
||||
copilot.NewClient(), // Microsoft 365 Copilot
|
||||
bedrock.NewClient(), // AWS Bedrock - credentials configured via setup or AWS credential chain
|
||||
)
|
||||
|
|
@ -123,6 +142,8 @@ func (o *PluginRegistry) ListVendors(out io.Writer) error {
|
|||
type PluginRegistry struct {
|
||||
Db *fsdb.Db
|
||||
|
||||
vendorMu sync.Mutex
|
||||
|
||||
VendorManager *ai.VendorsManager
|
||||
VendorsAll *ai.VendorsManager
|
||||
Defaults *tools.Defaults
|
||||
|
|
@ -288,16 +309,12 @@ func (o *PluginRegistry) runVendorSetup() (err error) {
|
|||
return pluginSetupErr
|
||||
}
|
||||
|
||||
o.registerVendor(plugin)
|
||||
|
||||
if err = o.SaveEnvFile(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if o.VendorManager.FindByName(plugin.GetName()) == nil {
|
||||
if vendor, ok := plugin.(ai.Vendor); ok {
|
||||
o.VendorManager.AddVendors(vendor)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -349,16 +366,11 @@ func (o *PluginRegistry) runInteractiveSetup() (err error) {
|
|||
if pluginSetupErr := plugin.Setup(); pluginSetupErr != nil {
|
||||
println(pluginSetupErr.Error())
|
||||
} else {
|
||||
o.registerVendor(plugin)
|
||||
if err = o.SaveEnvFile(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if o.VendorManager.FindByName(plugin.GetName()) == nil {
|
||||
if vendor, ok := plugin.(ai.Vendor); ok {
|
||||
o.VendorManager.AddVendors(vendor)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
|
@ -425,11 +437,32 @@ func (o *PluginRegistry) SetupVendor(vendorName string) (err error) {
|
|||
if err = o.VendorsAll.SetupVendor(vendorName, o.VendorManager.VendorsByName); err != nil {
|
||||
return
|
||||
}
|
||||
if vendor := o.VendorsAll.FindByName(vendorName); vendor != nil {
|
||||
o.registerVendor(vendor)
|
||||
}
|
||||
err = o.SaveEnvFile()
|
||||
return
|
||||
}
|
||||
|
||||
func (o *PluginRegistry) registerVendor(plugin plugins.Plugin) {
|
||||
vendor, ok := plugin.(ai.Vendor)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
o.vendorMu.Lock()
|
||||
defer o.vendorMu.Unlock()
|
||||
name := vendor.GetName()
|
||||
for _, existing := range o.VendorManager.Vendors {
|
||||
if strings.EqualFold(existing.GetName(), name) {
|
||||
return
|
||||
}
|
||||
}
|
||||
o.VendorManager.AddVendors(vendor)
|
||||
}
|
||||
|
||||
func (o *PluginRegistry) ConfigureVendors() {
|
||||
o.vendorMu.Lock()
|
||||
defer o.vendorMu.Unlock()
|
||||
o.VendorManager.Clear()
|
||||
for _, vendor := range o.VendorsAll.Vendors {
|
||||
if vendorErr := vendor.Configure(); vendorErr == nil && vendor.IsConfigured() {
|
||||
|
|
@ -438,6 +471,34 @@ func (o *PluginRegistry) ConfigureVendors() {
|
|||
}
|
||||
}
|
||||
|
||||
func (o *PluginRegistry) activateVendor(name string) (ai.Vendor, error) {
|
||||
if strings.TrimSpace(name) == "" {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
o.vendorMu.Lock()
|
||||
defer o.vendorMu.Unlock()
|
||||
|
||||
if v := o.VendorManager.FindByName(name); v != nil {
|
||||
return v, nil
|
||||
}
|
||||
if o.VendorsAll == nil {
|
||||
return nil, nil
|
||||
}
|
||||
v := o.VendorsAll.FindByName(name)
|
||||
if v == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if err := v.Configure(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !v.IsConfigured() {
|
||||
return nil, nil
|
||||
}
|
||||
o.VendorManager.AddVendors(v)
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func (o *PluginRegistry) GetModels() (ret *ai.VendorsModels, err error) {
|
||||
o.ConfigureVendors()
|
||||
ret, err = o.VendorManager.GetModels()
|
||||
|
|
@ -503,13 +564,25 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
|
|||
ret.model = defaultModel
|
||||
}
|
||||
} else if model == "" {
|
||||
if vendorName != "" {
|
||||
ret.vendor = vendorManager.FindByName(vendorName)
|
||||
} else {
|
||||
ret.vendor = vendorManager.FindByName(defaultVendor)
|
||||
name := vendorName
|
||||
if name == "" {
|
||||
name = defaultVendor
|
||||
}
|
||||
if ret.vendor, err = o.activateVendor(name); err != nil {
|
||||
return
|
||||
}
|
||||
ret.model = defaultModel
|
||||
} else {
|
||||
if vendorName != "" {
|
||||
if ret.vendor, err = o.activateVendor(vendorName); err != nil {
|
||||
return
|
||||
}
|
||||
} else if !vendorManager.HasVendors() {
|
||||
if _, err = o.activateVendor(defaultVendor); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
var models *ai.VendorsModels
|
||||
if models, err = vendorManager.GetModels(); err != nil {
|
||||
return
|
||||
|
|
@ -539,16 +612,44 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
|
|||
return
|
||||
}
|
||||
} else {
|
||||
availableVendors := models.FindGroupsByItem(model)
|
||||
if len(availableVendors) > 1 {
|
||||
debuglog.Log("Warning: multiple vendors provide model %s: %s. Using %s. Specify --vendor to select a vendor.\n", model, strings.Join(availableVendors, ", "), availableVendors[0])
|
||||
// If the model wasn't found and contains a '/', try parsing the first
|
||||
// segment as a vendor name (e.g. "ollama/llama3" -> vendor "ollama", model "llama3").
|
||||
if actualModelName == "" {
|
||||
if idx := strings.Index(model, "/"); idx > 0 {
|
||||
prefix := model[:idx]
|
||||
if v := vendorManager.FindByName(prefix); v != nil {
|
||||
vendorName = prefix
|
||||
model = model[idx+1:]
|
||||
if normalized := models.FindModelNameCaseInsensitive(model); normalized != "" {
|
||||
model = normalized
|
||||
}
|
||||
ret.vendor = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ret.vendor == nil {
|
||||
availableVendors := models.FindGroupsByItem(model)
|
||||
if len(availableVendors) > 1 {
|
||||
debuglog.Log("Warning: multiple vendors provide model %s: %s. Using %s. Specify --vendor to select a vendor.\n", model, strings.Join(availableVendors, ", "), availableVendors[0])
|
||||
}
|
||||
ret.vendor = vendorManager.FindByName(models.FindGroupsByItemFirst(model))
|
||||
}
|
||||
ret.vendor = vendorManager.FindByName(models.FindGroupsByItemFirst(model))
|
||||
}
|
||||
|
||||
ret.model = model
|
||||
}
|
||||
|
||||
if ret.vendor == nil {
|
||||
name := vendorName
|
||||
if name == "" {
|
||||
name = defaultVendor
|
||||
}
|
||||
if ret.vendor, err = o.activateVendor(name); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ret.vendor == nil {
|
||||
var errMsg string
|
||||
if defaultModel == "" || defaultVendor == "" {
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package core
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
|
@ -10,11 +11,14 @@ import (
|
|||
|
||||
"github.com/danielmiessler/fabric/internal/chat"
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
"github.com/danielmiessler/fabric/internal/plugins"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai/codex"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
"github.com/danielmiessler/fabric/internal/tools"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func TestSaveEnvFile(t *testing.T) {
|
||||
|
|
@ -32,18 +36,19 @@ func TestSaveEnvFile(t *testing.T) {
|
|||
|
||||
// testVendor implements ai.Vendor for testing purposes
|
||||
type testVendor struct {
|
||||
name string
|
||||
models []string
|
||||
name string
|
||||
models []string
|
||||
configureErr error
|
||||
}
|
||||
|
||||
func (m *testVendor) GetName() string { return m.name }
|
||||
func (m *testVendor) GetSetupDescription() string { return m.name }
|
||||
func (m *testVendor) IsConfigured() bool { return true }
|
||||
func (m *testVendor) Configure() error { return nil }
|
||||
func (m *testVendor) Setup() error { return nil }
|
||||
func (m *testVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
|
||||
func (m *testVendor) ListModels() ([]string, error) { return m.models, nil }
|
||||
func (m *testVendor) SendStream([]*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error {
|
||||
func (m *testVendor) GetName() string { return m.name }
|
||||
func (m *testVendor) GetSetupDescription() string { return m.name }
|
||||
func (m *testVendor) IsConfigured() bool { return true }
|
||||
func (m *testVendor) Configure() error { return m.configureErr }
|
||||
func (m *testVendor) Setup() error { return nil }
|
||||
func (m *testVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
|
||||
func (m *testVendor) ListModels(context.Context) ([]string, error) { return m.models, nil }
|
||||
func (m *testVendor) SendStream(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error {
|
||||
return nil
|
||||
}
|
||||
func (m *testVendor) Send(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions) (string, error) {
|
||||
|
|
@ -150,3 +155,176 @@ func TestGetChatter_RejectsExplicitCodexModelFromOtherVendor(t *testing.T) {
|
|||
t.Fatal("expected GetChatter() to reject models that only belong to another vendor")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChatter_ParsesVendorModelPrefix(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
db := fsdb.NewDb(tempDir)
|
||||
|
||||
ollamaVendor := &testVendor{name: "Ollama", models: []string{"some-namespace/model-name"}}
|
||||
|
||||
vm := ai.NewVendorsManager()
|
||||
vm.AddVendors(ollamaVendor)
|
||||
|
||||
defaults := &tools.Defaults{
|
||||
PluginBase: &plugins.PluginBase{},
|
||||
Vendor: &plugins.Setting{Value: "Ollama"},
|
||||
Model: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "some-namespace/model-name"}},
|
||||
ModelContextLength: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "0"}},
|
||||
}
|
||||
|
||||
registry := &PluginRegistry{Db: db, VendorManager: vm, Defaults: defaults}
|
||||
|
||||
chatter, err := registry.GetChatter("ollama/some-namespace/model-name", 0, "", false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetChatter() error = %v", err)
|
||||
}
|
||||
if chatter.vendor.GetName() != "Ollama" {
|
||||
t.Fatalf("expected Ollama vendor, got %s", chatter.vendor.GetName())
|
||||
}
|
||||
if chatter.model != "some-namespace/model-name" {
|
||||
t.Fatalf("expected model 'some-namespace/model-name', got %s", chatter.model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChatter_VendorPrefixIgnoredWhenNotAVendor(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
db := fsdb.NewDb(tempDir)
|
||||
|
||||
vendorA := &testVendor{name: "VendorA", models: []string{"notavendor/model"}}
|
||||
|
||||
vm := ai.NewVendorsManager()
|
||||
vm.AddVendors(vendorA)
|
||||
|
||||
defaults := &tools.Defaults{
|
||||
PluginBase: &plugins.PluginBase{},
|
||||
Vendor: &plugins.Setting{Value: "VendorA"},
|
||||
Model: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "notavendor/model"}},
|
||||
ModelContextLength: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "0"}},
|
||||
}
|
||||
|
||||
registry := &PluginRegistry{Db: db, VendorManager: vm, Defaults: defaults}
|
||||
|
||||
chatter, err := registry.GetChatter("notavendor/model", 0, "", false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetChatter() error = %v", err)
|
||||
}
|
||||
if chatter.vendor.GetName() != "VendorA" {
|
||||
t.Fatalf("expected VendorA vendor, got %s", chatter.vendor.GetName())
|
||||
}
|
||||
if chatter.model != "notavendor/model" {
|
||||
t.Fatalf("expected model 'notavendor/model', got %s", chatter.model)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChatter_ReportsConfigureError(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
for _, tt := range []struct {
|
||||
name, model, vendor string
|
||||
}{
|
||||
{"empty model", "", ""},
|
||||
{"model", "gpt-5.6-sol", ""},
|
||||
{"named vendor", "gpt-5.6-sol", "Codex"},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
configureErr := errors.New(i18n.T("codex_login_refresh_failed"))
|
||||
registry := newInactiveVendorRegistry(t, "Codex", "gpt-5.6-sol", &testVendor{
|
||||
name: "Codex", models: []string{"gpt-5.6-sol"}, configureErr: configureErr,
|
||||
})
|
||||
_, err := registry.GetChatter(tt.model, 0, tt.vendor, false, false)
|
||||
if !errors.Is(err, configureErr) {
|
||||
t.Fatalf("GetChatter() error = %v, want %v", err, configureErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetChatter_ActivatesInactiveVendor(t *testing.T) {
|
||||
for _, tt := range []struct {
|
||||
name, model, vendor string
|
||||
}{
|
||||
{"named", "gpt-5.6-sol", "Codex"},
|
||||
{"default for model", "gpt-5.6-sol", ""},
|
||||
} {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
codexVendor := &testVendor{name: "Codex", models: []string{"gpt-5.6-sol"}}
|
||||
registry := newInactiveVendorRegistry(t, "Codex", "gpt-5.6-sol", codexVendor)
|
||||
chatter, err := registry.GetChatter(tt.model, 0, tt.vendor, false, false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetChatter() error = %v", err)
|
||||
}
|
||||
if chatter.vendor.GetName() != "Codex" {
|
||||
t.Fatalf("vendor = %s, want Codex", chatter.vendor.GetName())
|
||||
}
|
||||
if chatter.model != "gpt-5.6-sol" {
|
||||
t.Fatalf("model = %s, want gpt-5.6-sol", chatter.model)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewPluginRegistry_CodexTokenPersist(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := fsdb.NewDb(dir)
|
||||
if err := db.SaveEnv("KEEP=old\n"); err != nil {
|
||||
t.Fatalf("SaveEnv() error = %v", err)
|
||||
}
|
||||
|
||||
registry, err := NewPluginRegistry(db)
|
||||
if err != nil {
|
||||
t.Fatalf("NewPluginRegistry() error = %v", err)
|
||||
}
|
||||
|
||||
vendor := registry.VendorsAll.FindByName("Codex")
|
||||
client, ok := vendor.(*codex.Client)
|
||||
if !ok || client == nil {
|
||||
t.Fatal("expected Codex client in VendorsAll")
|
||||
}
|
||||
if client.TokenPersist == nil {
|
||||
t.Fatal("TokenPersist is nil")
|
||||
}
|
||||
|
||||
client.AccessToken.Value = "access-live"
|
||||
client.RefreshToken.Value = "refresh-live"
|
||||
client.AccountID.Value = "acct-live"
|
||||
if err := client.TokenPersist(); err != nil {
|
||||
t.Fatalf("TokenPersist() error = %v", err)
|
||||
}
|
||||
|
||||
parsed, err := godotenv.Read(db.EnvFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("godotenv.Read() error = %v", err)
|
||||
}
|
||||
if parsed["KEEP"] != "old" {
|
||||
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
|
||||
}
|
||||
if parsed[client.AccessToken.EnvVariable] != "access-live" {
|
||||
t.Fatalf("access = %q, want access-live", parsed[client.AccessToken.EnvVariable])
|
||||
}
|
||||
if parsed[client.RefreshToken.EnvVariable] != "refresh-live" {
|
||||
t.Fatalf("refresh = %q, want refresh-live", parsed[client.RefreshToken.EnvVariable])
|
||||
}
|
||||
if parsed[client.AccountID.EnvVariable] != "acct-live" {
|
||||
t.Fatalf("account = %q, want acct-live", parsed[client.AccountID.EnvVariable])
|
||||
}
|
||||
}
|
||||
|
||||
func newInactiveVendorRegistry(t *testing.T, defaultVendor, defaultModel string, vendor *testVendor) *PluginRegistry {
|
||||
t.Helper()
|
||||
db := fsdb.NewDb(t.TempDir())
|
||||
all := ai.NewVendorsManager()
|
||||
all.AddVendors(vendor)
|
||||
defaults := &tools.Defaults{
|
||||
PluginBase: &plugins.PluginBase{},
|
||||
Vendor: &plugins.Setting{Value: defaultVendor},
|
||||
Model: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: defaultModel}},
|
||||
ModelContextLength: &plugins.SetupQuestion{Setting: &plugins.Setting{Value: "0"}},
|
||||
}
|
||||
return &PluginRegistry{
|
||||
Db: db,
|
||||
VendorManager: ai.NewVendorsManager(),
|
||||
VendorsAll: all,
|
||||
Defaults: defaults,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,8 +218,7 @@ func getLocaleCandidates(locale string) []string {
|
|||
candidates = append(candidates, locale)
|
||||
|
||||
// If it's a regional variant, add the base language as a candidate
|
||||
if strings.Contains(locale, "-") {
|
||||
baseLang := strings.Split(locale, "-")[0]
|
||||
if baseLang, _, found := strings.Cut(locale, "-"); found {
|
||||
candidates = append(candidates, baseLang)
|
||||
|
||||
// Also check if the base language has a default variant
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ func TestTranslation(t *testing.T) {
|
|||
{"pt", "usa a entrada original, porque não é possível aplicar a legibilidade HTML"},
|
||||
{"fa", "از ورودی اصلی استفاده کن، چون نمیتوان خوانایی HTML را اعمال کرد"},
|
||||
{"it", "usa l'input originale, perché non è possibile applicare la leggibilità HTML"},
|
||||
{"tr", "html okunabilirliği uygulanamadığından orijinal girdiyi kullanın"},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Geben Sie Ihre AWS Access Key ID ein (leer lassen, um die AWS-Anmeldekette zu verwenden)",
|
||||
"bedrock_aws_region_label": "AWS-Region",
|
||||
"bedrock_aws_secret_key_label": "Geben Sie Ihren AWS Secret Access Key ein (leer lassen, um die AWS-Anmeldekette zu verwenden)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "Bedrock-Client nicht initialisiert — führen Sie 'fabric --setup' zur Konfiguration aus",
|
||||
"bedrock_converse_failed": "Bedrock Converse für Modell %s fehlgeschlagen: %w",
|
||||
"bedrock_conversestream_failed": "Bedrock ConverseStream für Modell %s fehlgeschlagen: %w",
|
||||
"bedrock_empty_response_content": "leerer Antwortinhalt",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "Bedrock ListModels API fehlgeschlagen, verwende statische Fallback-Liste",
|
||||
"bedrock_panic_sendstream": "Panic in SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API-Schlüssel / ABSK-Token (empfohlen — derselbe wie bei Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Geben Sie 1 oder 2 ein",
|
||||
"bedrock_setup_choose_auth_method": " Authentifizierungsmethode wählen:",
|
||||
"bedrock_setup_choose_model": " Standardmodell wählen (IDs ohne Präfix funktionieren in jeder Region; us./eu./ap. sind regionsspezifisch):",
|
||||
"bedrock_setup_choose_region": " AWS-Region wählen:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "Ungültige Auswahl: %s (geben Sie 1 oder 2 ein)",
|
||||
"bedrock_setup_model_custom_prompt": "Geben Sie die Modell-ID ein (z. B. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Eine andere Modell-ID eingeben",
|
||||
"bedrock_setup_model_prompt": "Geben Sie die Modellnummer ein oder 0, um eine eigene einzugeben",
|
||||
"bedrock_setup_region_custom_prompt": "Geben Sie eine eigene AWS-Region ein (z. B. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Eine andere Region eingeben",
|
||||
"bedrock_setup_region_prompt": "Geben Sie die Regionsnummer ein oder 0 für eine eigene",
|
||||
"bedrock_setup_selected_model": " ✓ Ausgewähltes Modell: %s",
|
||||
"bedrock_setup_use_with": " ℹ Verwendung: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "AWS-Konfiguration konnte nicht geladen werden: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "AWS-Konfiguration konnte mit Region %s nicht geladen werden: %w",
|
||||
"bedrock_unexpected_content_block_type": "unerwarteter Inhaltsblocktyp: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Wähle ein Muster aus den verfügbaren Mustern",
|
||||
"choose_session_from_available": "Wähle eine Sitzung aus den verfügbaren Sitzungen",
|
||||
"choose_strategy_from_available": "Strategie aus den verfügbaren Strategien wählen",
|
||||
"codex_api_base_url_question": "Geben Sie Ihre Codex-API-Basis-URL ein",
|
||||
"codex_auth_base_url_invalid": "Ungültige Codex-Authentifizierungs-Basis-URL: %w",
|
||||
"codex_auth_base_url_question": "Geben Sie Ihre Codex-OAuth-Basis-URL ein",
|
||||
"codex_browser_open_fallback": "Falls Ihr Browser sich nicht geöffnet hat, navigieren Sie zu dieser URL zur Authentifizierung:",
|
||||
"codex_decode_models_response_failed": "Codex-Modell-Antwort konnte nicht dekodiert werden: %w",
|
||||
"codex_decode_refresh_response_failed": "Aktualisierte Codex-Token-Antwort konnte nicht dekodiert werden: %w",
|
||||
"codex_decode_token_response_failed": "Codex-Token-Austausch-Antwort konnte nicht dekodiert werden: %w",
|
||||
"codex_image_file_not_supported": "Der Codex-Anbieter unterstützt --image-file nicht. Verwenden Sie stattdessen einen Bildanhang.",
|
||||
"codex_login_account_changed": "Die Codex-Anmeldung ist mit einem anderen ChatGPT-Konto verknüpft als in der gespeicherten Konfiguration. Bitte führen Sie 'fabric --setup' erneut aus.",
|
||||
"codex_login_completed": "Codex-Anmeldung abgeschlossen",
|
||||
"codex_login_failed": "Codex-Anmeldung fehlgeschlagen: %s",
|
||||
"codex_login_invalid": "Ihre Codex-Anmeldung ist nicht mehr gültig. Bitte führen Sie 'fabric --setup' erneut aus.",
|
||||
"codex_login_missing_account_claim": "Die Codex-Anmeldung enthielt keine ChatGPT-Konto-ID. Dieser Anmeldezustand wird nicht unterstützt.",
|
||||
"codex_login_missing_auth_code": "Die Codex-Anmeldung hat keinen Autorisierungscode zurückgegeben.",
|
||||
"codex_login_missing_tokens": "Die Codex-Anmeldung hat die erforderlichen Zugangs- und Aktualisierungstoken nicht zurückgegeben.",
|
||||
"codex_login_refresh_failed": "Die Codex-Anmeldung konnte nicht aktualisiert werden. Bitte führen Sie 'fabric --setup' erneut aus.",
|
||||
"codex_login_return_to_fabric": "Zurück zu Fabric.",
|
||||
"codex_login_revoked": "Die Codex-Anmeldung ist abgelaufen oder wurde widerrufen. Bitte führen Sie 'fabric --setup' erneut aus.",
|
||||
"codex_login_server_stopped": "Der Codex-Anmelde-Callback-Server wurde gestoppt, bevor die Authentifizierung abgeschlossen war.",
|
||||
"codex_login_state_mismatch": "Die Codex-Anmeldung konnte nicht verifiziert werden, da der OAuth-Status nicht übereinstimmte.",
|
||||
"codex_login_timed_out": "Zeitüberschreitung bei der Codex-Anmeldung vor Abschluss der Authentifizierung.",
|
||||
"codex_oauth_missing_auth_code": "Fehlender Autorisierungscode",
|
||||
"codex_oauth_random_state_failed": "Sicherer zufälliger OAuth-Status konnte nicht generiert werden: %w",
|
||||
"codex_oauth_server_start_failed": "Lokaler OAuth-Callback-Server konnte nicht gestartet werden: %w",
|
||||
"codex_oauth_state_mismatch": "Status stimmt nicht überein",
|
||||
"codex_provider_error": "Codex-Anbieterfehler (Status %d): %s",
|
||||
"codex_refresh_failed_status": "Codex-Anmeldung konnte nicht aktualisiert werden (Status %d)",
|
||||
"codex_refresh_login_failed": "Codex-Anmeldung konnte nicht aktualisiert werden: %w",
|
||||
"codex_refresh_token_required": "Codex-Aktualisierungstoken ist erforderlich. Bitte führen Sie 'fabric --setup' erneut aus.",
|
||||
"codex_replay_body_unavailable": "Anfragekörper kann für Codex-Reauthentifizierungswiederholung nicht wiedergegeben werden",
|
||||
"codex_request_failed": "Codex-Anfrage fehlgeschlagen: %w",
|
||||
"codex_request_failed_status": "Codex-Anfrage fehlgeschlagen mit Status %d",
|
||||
"codex_starting_browser_login": "Starte browserbasierte OpenAI-Anmeldung für Codex.",
|
||||
"codex_token_exchange_failed": "Codex-Token-Austausch fehlgeschlagen: %w",
|
||||
"codex_token_persist_failed": "Codex-Anmeldung konnte nicht gespeichert werden: %w",
|
||||
"codex_token_refresh_missing_access_token": "Die Codex-Token-Aktualisierung hat kein Zugriffstoken zurückgegeben.",
|
||||
"codex_usage_limit_reached": "Codex-Nutzungslimit erreicht",
|
||||
"command_completed_successfully": "Befehl erfolgreich abgeschlossen",
|
||||
"compression_level_jpeg_webp": "Komprimierungslevel 0-100 für JPEG/WebP-Formate (Standard: nicht gesetzt)",
|
||||
"config_file_not_found": "Konfigurationsdatei nicht gefunden: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Benutzerdefinierte Patterns - Verzeichnis für Ihre benutzerdefinierten Patterns festlegen",
|
||||
"custom_patterns_warning_create_directory": "Warnung: Benutzerdefiniertes Musterverzeichnis %s konnte nicht erstellt werden: %v\n",
|
||||
"db_error_loading_env_file": "fehler beim Laden der .env-Datei: %w",
|
||||
"db_error_updating_env_file": "fehler beim Aktualisieren der .env-Datei: %w",
|
||||
"defaults_model_context_length_question": "Geben Sie die Kontextlänge des Modells ein",
|
||||
"defaults_model_question": "Geben Sie den Index oder den Namen Ihres Standardmodells ein",
|
||||
"defaults_setup_description": "Standard-KI-Anbieter und -Modell",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "DigitalOcean-Modellanfrage fehlgeschlagen mit Status %d: %s",
|
||||
"disable_openai_responses_api": "OpenAI Responses API deaktivieren (Standard: false)",
|
||||
"disable_pattern_variable_replacement": "Mustervariablenersetzung deaktivieren",
|
||||
"enable_web_search_tool": "Web-Such-Tool für unterstützte Modelle aktivieren (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Web-Such-Tool für unterstützte Modelle aktivieren (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "End-Tag für Denk-Abschnitte",
|
||||
"error_creating_audio_file": "Fehler beim Erstellen der Audio-Datei: %v",
|
||||
"error_creating_file": "Fehler beim Erstellen der Datei: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "ungültiges Data-URL-Format",
|
||||
"ollama_invalid_http_timeout_using_default": "ungültiges HTTP-Zeitlimit '%s': %v, verwende Standardwert",
|
||||
"ollama_invalid_num_ctx_in_request": "Ungültiger num_ctx in Anfrage: %v",
|
||||
"ollama_invalid_request_body": "ungültiger Anfragetext",
|
||||
"ollama_no_content_from_upstream": "Kein Inhalt vom Upstream Fabric Server erhalten",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx überschreitet den maximal zulässigen Wert von %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx muss eine Zahl sein, ungültiger Typ erhalten",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "SSE Zeile überschreitet 1MB Puffer-Limit - Datenzeile zu groß",
|
||||
"ollama_upstream_non_2xx": "Upstream Fabric Server hat nicht-2xx Status %d zurückgegeben: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Upstream Fabric Server hat nicht-2xx Status %d zurückgegeben und Body konnte nicht gelesen werden: %v",
|
||||
"ollama_upstream_request_failed": "Upstream-Fabric-Server nicht erreichbar",
|
||||
"ollama_upstream_returned_status": "Upstream Fabric Server hat Status %d zurückgegeben",
|
||||
"ollama_warning_no_content": "Warnung: Kein Inhalt vom Upstream Fabric Server erhalten",
|
||||
"ollama_warning_parse_variables": "Warnung: Fehler beim Parsen von options.variables als JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "Bild konnte nicht in %s gespeichert werden: %w",
|
||||
"openai_image_saved_to": "Bild gespeichert unter: %s",
|
||||
"openai_model_no_image_generation": "Modell '%s' unterstützt keine Bildgenerierung. Unterstützte Modelle: %s",
|
||||
"openai_models_rate_limited": "Ratenlimit beim Abrufen der Modelle von Anbieter %s überschritten; erneuter Versuch in %s Sekunden",
|
||||
"openai_models_response_too_large": "Modell-Antwort zu groß von Anbieter %s (>%d Bytes)",
|
||||
"openai_unable_to_parse_models_response": "Modell-Antwort konnte nicht geparst werden; rohe Antwort: %s",
|
||||
"openai_unexpected_status_code_read_error": "unerwarteter Statuscode: %d von Anbieter %s (Fehler beim Lesen der Antwort: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Video-Metadaten ausgeben",
|
||||
"path_to_yaml_config": "Pfad zur YAML-Konfigurationsdatei",
|
||||
"pattern_not_found_list_available": "Pattern '%s' nicht gefunden. Führen Sie 'fabric -l' aus, um verfügbare Patterns anzuzeigen",
|
||||
"pattern_invalid_name": "Ungültiger Pattern-Name: %q",
|
||||
"pattern_not_found_no_patterns": "Pattern '%s' nicht gefunden.\n\nKeine Patterns installiert! Um dies zu beheben:\n • Führen Sie 'fabric --setup' aus, um Patterns zu konfigurieren und herunterzuladen\n • Oder führen Sie 'fabric -U' aus, um Patterns direkt herunterzuladen/zu aktualisieren",
|
||||
"pattern_variables_help": "Werte für Mustervariablen, z.B. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Repository %s wird geklont (Pfad: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Playlist gegenüber Video bevorzugen, wenn beide IDs in der URL vorhanden sind",
|
||||
"print_context": "Kontext ausgeben",
|
||||
"print_current_version": "Aktuelle Version ausgeben",
|
||||
"print_metadata_to_stderr": "Metadaten (Eingabe-/Ausgabe-Token) auf stderr ausgeben",
|
||||
"print_pattern_contents": "Den Inhalt des angegebenen Musters im Terminal ausgeben",
|
||||
"print_session": "Sitzung ausgeben",
|
||||
"register_new_extension": "Neue Erweiterung aus Konfigurationsdateipfad registrieren",
|
||||
"remove_registered_extension": "Registrierte Erweiterung nach Name entfernen",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Desktop-Benachrichtigung senden, wenn Befehl abgeschlossen ist",
|
||||
"serve_fabric_api_ollama_endpoints": "Fabric REST API mit ollama-Endpunkten bereitstellen",
|
||||
"serve_fabric_rest_api": "Fabric REST API bereitstellen",
|
||||
"server_api_key_required": "Server-Start auf Nicht-Loopback-Adresse %s ohne API-Schlüssel verweigert: Setzen Sie --api-key oder FABRIC_API_KEY, oder binden Sie eine Loopback-Adresse wie 127.0.0.1:8080",
|
||||
"server_chat_error": "Fehler: %v",
|
||||
"server_error_marshaling_response": "Fehler beim Serialisieren der Antwort: %v",
|
||||
"server_error_writing_response": "Fehler beim Schreiben der Antwort: %v",
|
||||
"server_invalid_request_format": "ungültiges Anfrageformat: %v",
|
||||
"server_no_api_key_warning": "REST-API-Server wird ohne API-Schlüssel-Authentifizierung gestartet. Dies kann Sicherheitsrisiken bergen.",
|
||||
"sessions_creating_new": "Erstelle neue Sitzung: %s\n",
|
||||
"set_debug_level": "Debug-Level festlegen (0=aus, 1=grundlegend, 2=detailliert, 3=Trace, 4=wire)",
|
||||
"set_frequency_penalty": "Häufigkeitsstrafe festlegen",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Sendung**: %s",
|
||||
"spotify_title_label": "**Titel**: %s",
|
||||
"spotify_total_episodes_label": "**Episoden insgesamt**: %d",
|
||||
"spotify_url_help": "Spotify-Podcast- oder Episoden-URL, um Metadaten abzurufen und an den Chat zu senden",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Start-Tag für Denk-Abschnitte",
|
||||
"storage_error_delete": "%s konnte nicht gelöscht werden: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "%s konnte nicht gespeichert werden: %v",
|
||||
"storage_error_stat_entry": "Eintrag %s konnte nicht abgefragt werden: %v",
|
||||
"storage_error_unmarshal": "%s konnte nicht deserialisiert werden: %s",
|
||||
"storage_invalid_name": "Ungültiger Name: %q",
|
||||
"strategies_available_header": "Verfügbare Strategien:",
|
||||
"strategies_cloning_repository": "Repository %s wird geklont (Pfad: %s)...\\n",
|
||||
"strategies_download_success": "✅ Strategien erfolgreich nach %s heruntergeladen und installiert\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "Strategiename %q löst sich außerhalb des Strategieverzeichnisses auf",
|
||||
"stream_help": "Streaming",
|
||||
"suppress_thinking_tags": "In Denk-Tags eingeschlossenen Text unterdrücken",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "ungültige Zahl in relativer Zeitangabe: %q",
|
||||
"template_datetime_error_invalid_relative_format": "ungültiges Format für relative Zeitangabe",
|
||||
"template_datetime_error_invalid_unit": "ungültige Zeiteinheit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative Zeitangabe erfordert einen Wert",
|
||||
"template_datetime_error_unknown_operation": "datetime: unbekannte Operation %q",
|
||||
"template_extension_error": "Erweiterung %s Fehler: %v",
|
||||
"template_file_error_expand_home_dir": "datei: home-verzeichnis konnte nicht erweitert werden: %v",
|
||||
"template_file_error_invalid_line_count": "datei: ungültige zeilenanzahl %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Erforderliche Variable fehlt: %s",
|
||||
"template_plugin_error": "Plugin %s Fehler: %v",
|
||||
"template_processing_stuck": "Vorlagenverarbeitung blockiert - mögliche Endlosschleife",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: Variablenname erforderlich",
|
||||
"template_sys_error_home": "Home-Verzeichnis konnte nicht ermittelt werden: %v",
|
||||
"template_sys_error_hostname": "Hostname konnte nicht ermittelt werden: %v",
|
||||
"template_sys_error_pwd": "Arbeitsverzeichnis konnte nicht ermittelt werden: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unbekannte Operation %q",
|
||||
"template_sys_error_user": "aktueller Benutzer konnte nicht ermittelt werden: %v",
|
||||
"template_text_empty_input": "Text: Leere Eingabe für Operation %q",
|
||||
"template_text_unknown_operation": "Text: Unbekannte Textoperation %q (unterstützt: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Unbekannter Plugin-Namespace: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "Fehler beim Abrufen der Videodetails: %v",
|
||||
"youtube_error_parsing_duration": "Fehler beim Parsen der Videodauer: %v",
|
||||
"youtube_error_saving_csv": "Fehler beim Speichern der Videos in CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Visuelle Daten mit OCR und FFmpeg aus dem Video extrahieren",
|
||||
"youtube_failed_create_temp_dir": "temporäres Verzeichnis konnte nicht erstellt werden: %v",
|
||||
"youtube_failed_fetch_comments": "Kommentare konnten nicht abgerufen werden: %v",
|
||||
"youtube_failed_get_stream_url": "Stream-URL konnte über yt-dlp nicht ermittelt werden: %v",
|
||||
"youtube_failed_parse_http_stream_url": "gültige HTTP-Stream-URL konnte aus der yt-dlp-Ausgabe nicht geparst werden",
|
||||
"youtube_failed_walk_directory": "Verzeichnis konnte nicht durchlaufen werden: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "FFmpeg-Frame-Extraktion fehlgeschlagen: %v, Ausgabe: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg wird für die visuelle Extraktion benötigt, wurde aber im PATH nicht gefunden",
|
||||
"youtube_invalid_duration_string": "ungültige Dauer-Zeichenfolge: %s",
|
||||
"youtube_invalid_seconds_format": "ungültiges Sekundenformat %q: %w",
|
||||
"youtube_invalid_timestamp_format": "ungültiges Zeitstempel-Format: %s",
|
||||
"youtube_invalid_url": "ungültige YouTube-URL, kann keine Video- oder Playlist-ID abrufen: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "ungültige yt-dlp-Argumente: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "kein klarer Text in visuellen Videoframes gefunden",
|
||||
"youtube_no_transcript_content": "kein Transkriptinhalt in VTT-Datei gefunden",
|
||||
"youtube_no_url_provided": "Keine YouTube-URL angegeben",
|
||||
"youtube_no_video_found_with_id": "kein Video mit ID gefunden: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Wiedergabeliste gespeichert unter %s",
|
||||
"youtube_rate_limit_exceeded": "YouTube-Ratenlimit überschritten. Versuche es später erneut oder verwende andere yt-dlp-Argumente wie '--sleep-requests 1', um Anfragen zu verlangsamen.",
|
||||
"youtube_setup_description": "YouTube - zum Erfassen von Video-Transkripten (via yt-dlp) und Kommentaren/Metadaten (via YouTube API)",
|
||||
"youtube_tesseract_frame_failed": "tesseract für Frame %d fehlgeschlagen: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract wird für die visuelle Extraktion benötigt, wurde aber im PATH nicht gefunden",
|
||||
"youtube_url_help": "YouTube-Video oder Playlist-\"URL\" zum Abrufen von Transkript und Kommentaren und Senden an Chat oder Ausgabe in Konsole und Speichern in Ausgabedatei",
|
||||
"youtube_url_is_playlist_not_video": "URL ist eine Playlist, kein Video",
|
||||
"youtube_video_id_title_header": "VideoID: Titel",
|
||||
"youtube_visual_fps_help": "Eine bestimmte Anzahl von Frames pro Sekunde extrahieren, statt Szenenerkennung zu verwenden",
|
||||
"youtube_visual_frame_cue": "[VISUELLER FRAME-HINWEIS]",
|
||||
"youtube_visual_sensitivity_help": "Toleranz für die FFmpeg-Szenenerkennung (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp wurde nicht in PATH gefunden. Bitte installiere yt-dlp, um die YouTube-Transkript-Funktionalität zu nutzen",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp wird für die visuelle Extraktion benötigt, wurde aber im PATH nicht gefunden",
|
||||
"youtube_ytdlp_stderr_error": "fehler beim Lesen von yt-dlp stderr"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Choose a pattern from the available patterns",
|
||||
"choose_session_from_available": "Choose a session from the available sessions",
|
||||
"choose_strategy_from_available": "Choose a strategy from the available strategies",
|
||||
"codex_api_base_url_question": "Enter your Codex API base URL",
|
||||
"codex_auth_base_url_invalid": "invalid codex auth base url: %w",
|
||||
"codex_auth_base_url_question": "Enter your Codex OAuth base URL",
|
||||
"codex_browser_open_fallback": "If your browser did not open, navigate to this URL to authenticate:",
|
||||
"codex_decode_models_response_failed": "failed to decode codex models response: %w",
|
||||
"codex_decode_refresh_response_failed": "failed to decode refreshed Codex token response: %w",
|
||||
"codex_decode_token_response_failed": "failed to decode codex token exchange response: %w",
|
||||
"codex_image_file_not_supported": "Codex vendor does not support --image-file. Use an image attachment instead.",
|
||||
"codex_login_account_changed": "Codex login is linked to a different ChatGPT account than the stored configuration. Please rerun 'fabric --setup'.",
|
||||
"codex_login_completed": "Codex login completed",
|
||||
"codex_login_failed": "Codex login failed: %s",
|
||||
"codex_login_invalid": "Codex login is no longer valid. Please rerun 'fabric --setup'.",
|
||||
"codex_login_missing_account_claim": "Codex login did not include a ChatGPT account ID. This login state is not supported.",
|
||||
"codex_login_missing_auth_code": "Codex login did not return an authorization code.",
|
||||
"codex_login_missing_tokens": "Codex login did not return the required access and refresh tokens.",
|
||||
"codex_login_refresh_failed": "Codex login could not be refreshed. Please rerun 'fabric --setup'.",
|
||||
"codex_login_return_to_fabric": "Return to Fabric.",
|
||||
"codex_login_revoked": "Codex login has expired or been revoked. Please rerun 'fabric --setup'.",
|
||||
"codex_login_server_stopped": "Codex login callback server stopped before authentication completed.",
|
||||
"codex_login_state_mismatch": "Codex login could not be verified because the OAuth state did not match.",
|
||||
"codex_login_timed_out": "Codex login timed out before authentication completed.",
|
||||
"codex_oauth_missing_auth_code": "Missing authorization code",
|
||||
"codex_oauth_random_state_failed": "failed to generate secure random oauth state: %w",
|
||||
"codex_oauth_server_start_failed": "failed to start local oauth callback server: %w",
|
||||
"codex_oauth_state_mismatch": "State mismatch",
|
||||
"codex_provider_error": "codex provider error (status %d): %s",
|
||||
"codex_refresh_failed_status": "failed to refresh codex login (status %d)",
|
||||
"codex_refresh_login_failed": "failed to refresh Codex login: %w",
|
||||
"codex_refresh_token_required": "Codex refresh token is required. Please rerun 'fabric --setup'.",
|
||||
"codex_replay_body_unavailable": "request body cannot be replayed for Codex re-authentication retry",
|
||||
"codex_request_failed": "codex request failed: %w",
|
||||
"codex_request_failed_status": "codex request failed with status %d",
|
||||
"codex_starting_browser_login": "Starting browser-based OpenAI login for Codex.",
|
||||
"codex_token_exchange_failed": "codex token exchange failed: %w",
|
||||
"codex_token_persist_failed": "failed to persist Codex login: %w",
|
||||
"codex_token_refresh_missing_access_token": "Codex token refresh did not return an access token.",
|
||||
"codex_usage_limit_reached": "codex usage limit reached",
|
||||
"command_completed_successfully": "Command completed successfully",
|
||||
"compression_level_jpeg_webp": "Compression level 0-100 for JPEG/WebP formats (default: not set)",
|
||||
"config_file_not_found": "config file not found: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Custom Patterns - Set directory for your custom patterns",
|
||||
"custom_patterns_warning_create_directory": "Warning: Could not create custom patterns directory %s: %v\n",
|
||||
"db_error_loading_env_file": "error loading .env file: %w",
|
||||
"db_error_updating_env_file": "error updating .env file: %w",
|
||||
"defaults_model_context_length_question": "Enter model context length",
|
||||
"defaults_model_question": "Enter the index or the name of your default model",
|
||||
"defaults_setup_description": "Default AI Vendor and Model",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "DigitalOcean models request failed with status %d: %s",
|
||||
"disable_openai_responses_api": "Disable OpenAI Responses API (default: false)",
|
||||
"disable_pattern_variable_replacement": "Disable pattern variable replacement",
|
||||
"enable_web_search_tool": "Enable web search tool for supported models (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Enable web search tool for supported models (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "End tag for thinking sections",
|
||||
"error_creating_audio_file": "error creating audio file: %v",
|
||||
"error_creating_file": "error creating file: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "invalid data URL format",
|
||||
"ollama_invalid_http_timeout_using_default": "invalid HTTP timeout '%s': %v, using default",
|
||||
"ollama_invalid_num_ctx_in_request": "invalid num_ctx in request: %v",
|
||||
"ollama_invalid_request_body": "invalid request body",
|
||||
"ollama_no_content_from_upstream": "no content received from upstream Fabric server",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx exceeds maximum allowed value of %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx must be a number, got invalid type",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "SSE line exceeds 1MB buffer limit - data line too large",
|
||||
"ollama_upstream_non_2xx": "Upstream Fabric server returned non-2xx status %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Upstream Fabric server returned non-2xx status %d and body could not be read: %v",
|
||||
"ollama_upstream_request_failed": "failed to reach upstream Fabric server",
|
||||
"ollama_upstream_returned_status": "upstream Fabric server returned status %d",
|
||||
"ollama_warning_no_content": "Warning: no content received from upstream Fabric server",
|
||||
"ollama_warning_parse_variables": "Warning: failed to parse options.variables as JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "failed to save image to %s: %w",
|
||||
"openai_image_saved_to": "Image saved to: %s",
|
||||
"openai_model_no_image_generation": "model '%s' does not support image generation. Supported models: %s",
|
||||
"openai_models_rate_limited": "rate limit exceeded fetching models from provider %s; retry after %s seconds",
|
||||
"openai_models_response_too_large": "models response too large from provider %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "unable to parse models response; raw response: %s",
|
||||
"openai_unexpected_status_code_read_error": "unexpected status code: %d from provider %s (failed to read response body: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Output video metadata",
|
||||
"path_to_yaml_config": "Path to YAML config file",
|
||||
"pattern_not_found_list_available": "pattern '%s' not found. Run 'fabric -l' to see available patterns",
|
||||
"pattern_invalid_name": "invalid pattern name: %q",
|
||||
"pattern_not_found_no_patterns": "pattern '%s' not found.\n\nNo patterns are installed! To fix this:\n • Run 'fabric --setup' to configure and download patterns\n • Or run 'fabric -U' to download/update patterns directly",
|
||||
"pattern_variables_help": "Values for pattern variables, e.g. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Cloning repository %s (path: %s)...\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Prefer playlist over video if both ids are present in the URL",
|
||||
"print_context": "Print context",
|
||||
"print_current_version": "Print current version",
|
||||
"print_metadata_to_stderr": "Print metadata (input/output tokens) to stderr",
|
||||
"print_pattern_contents": "Print the contents of the named pattern to the terminal",
|
||||
"print_session": "Print session",
|
||||
"register_new_extension": "Register a new extension from config file path",
|
||||
"remove_registered_extension": "Remove a registered extension by name",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Send desktop notification when command completes",
|
||||
"serve_fabric_api_ollama_endpoints": "Serve the Fabric Rest API with ollama endpoints",
|
||||
"serve_fabric_rest_api": "Serve the Fabric Rest API",
|
||||
"server_api_key_required": "refusing to serve on non-loopback address %s without an API key: set --api-key or FABRIC_API_KEY, or bind a loopback address such as 127.0.0.1:8080",
|
||||
"server_chat_error": "Error: %v",
|
||||
"server_error_marshaling_response": "error marshaling response: %v",
|
||||
"server_error_writing_response": "error writing response: %v",
|
||||
"server_invalid_request_format": "invalid request format: %v",
|
||||
"server_no_api_key_warning": "Starting REST API server without API key authentication. This may pose security risks.",
|
||||
"sessions_creating_new": "Creating new session: %s\n",
|
||||
"set_debug_level": "Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)",
|
||||
"set_frequency_penalty": "Set frequency penalty",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Show**: %s",
|
||||
"spotify_title_label": "**Title**: %s",
|
||||
"spotify_total_episodes_label": "**Total Episodes**: %d",
|
||||
"spotify_url_help": "Spotify podcast or episode URL to grab metadata from and send to chat",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Start tag for thinking sections",
|
||||
"storage_error_delete": "could not delete %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "could not save %s: %v",
|
||||
"storage_error_stat_entry": "could not stat entry %s: %v",
|
||||
"storage_error_unmarshal": "could not unmarshal %s: %s",
|
||||
"storage_invalid_name": "invalid name: %q",
|
||||
"strategies_available_header": "Available Strategies:",
|
||||
"strategies_cloning_repository": "Cloning repository %s (path: %s)...\n",
|
||||
"strategies_download_success": "✅ Successfully downloaded and installed strategies to %s\n",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "error getting video details: %v",
|
||||
"youtube_error_parsing_duration": "error parsing video duration: %v",
|
||||
"youtube_error_saving_csv": "error saving videos to CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Extract visual data from video using OCR and FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "failed to create temp directory: %v",
|
||||
"youtube_failed_fetch_comments": "failed to fetch comments: %v",
|
||||
"youtube_failed_get_stream_url": "failed to get stream URL via yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "failed to parse valid HTTP stream URL from yt-dlp output",
|
||||
"youtube_failed_walk_directory": "failed to walk directory: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "ffmpeg frame extraction failed: %v, output: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg is required for visual extraction but not found in PATH",
|
||||
"youtube_invalid_duration_string": "invalid duration string: %s",
|
||||
"youtube_invalid_seconds_format": "invalid seconds format %q: %w",
|
||||
"youtube_invalid_timestamp_format": "invalid timestamp format: %s",
|
||||
"youtube_invalid_url": "invalid YouTube URL, can't get video or playlist ID: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "invalid yt-dlp arguments: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "no clear text found in video visual frames",
|
||||
"youtube_no_transcript_content": "no transcript content found in VTT file",
|
||||
"youtube_no_url_provided": "No YouTube URL provided",
|
||||
"youtube_no_video_found_with_id": "no video found with ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Playlist saved to %s",
|
||||
"youtube_rate_limit_exceeded": "YouTube rate limit exceeded. Try again later or use different yt-dlp arguments like '--sleep-requests 1' to slow down requests.",
|
||||
"youtube_setup_description": "YouTube - to grab video transcripts (via yt-dlp) and comments/metadata (via YouTube API)",
|
||||
"youtube_tesseract_frame_failed": "tesseract failed on frame %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract is required for visual extraction but not found in PATH",
|
||||
"youtube_url_help": "YouTube video or play list \"URL\" to grab transcript, comments from it and send to chat or print it put to the console and store it in the output file",
|
||||
"youtube_url_is_playlist_not_video": "URL is a playlist, not a video",
|
||||
"youtube_video_id_title_header": "VideoID: Title",
|
||||
"youtube_visual_fps_help": "Extract a specific number of frames per second instead of using scene detection",
|
||||
"youtube_visual_frame_cue": "[VISUAL FRAME CUE]",
|
||||
"youtube_visual_sensitivity_help": "Tolerance for FFmpeg scene detection (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp not found in PATH. Please install yt-dlp to use YouTube transcript functionality",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp is required for visual extraction but not found in PATH",
|
||||
"youtube_ytdlp_stderr_error": "error reading yt-dlp stderr"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Ingrese su AWS Access Key ID (deje vacío para usar la cadena de credenciales de AWS)",
|
||||
"bedrock_aws_region_label": "Región de AWS",
|
||||
"bedrock_aws_secret_key_label": "Ingrese su AWS Secret Access Key (deje vacío para usar la cadena de credenciales de AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "cliente de Bedrock no inicializado — ejecute 'fabric --setup' para configurar",
|
||||
"bedrock_converse_failed": "bedrock converse falló para el modelo %s: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream falló para el modelo %s: %w",
|
||||
"bedrock_empty_response_content": "contenido de respuesta vacío",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "la API ListModels de Bedrock falló, usando lista estática de respaldo",
|
||||
"bedrock_panic_sendstream": "pánico en SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Clave API de Bedrock / token ABSK (recomendado — la misma que usa Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Introduzca 1 o 2",
|
||||
"bedrock_setup_choose_auth_method": " Elija el método de autenticación:",
|
||||
"bedrock_setup_choose_model": " Elija el modelo predeterminado (los ID sin prefijo funcionan en cualquier región; us./eu./ap. son específicos de región):",
|
||||
"bedrock_setup_choose_region": " Elija la región de AWS:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "selección inválida: %s (introduzca 1 o 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Introduzca el ID del modelo (p. ej. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Introducir un ID de modelo diferente",
|
||||
"bedrock_setup_model_prompt": "Introduzca el número del modelo o 0 para escribir el suyo",
|
||||
"bedrock_setup_region_custom_prompt": "Introduzca una región de AWS personalizada (p. ej. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Introducir una región diferente",
|
||||
"bedrock_setup_region_prompt": "Introduzca el número de la región o 0 para personalizarla",
|
||||
"bedrock_setup_selected_model": " ✓ Modelo seleccionado: %s",
|
||||
"bedrock_setup_use_with": " ℹ Uso: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "no se pudo cargar la configuración de AWS: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "no se pudo cargar la configuración de AWS con región %s: %w",
|
||||
"bedrock_unexpected_content_block_type": "tipo de bloque de contenido inesperado: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Elige un patrón de los patrones disponibles",
|
||||
"choose_session_from_available": "Elige una sesión de las sesiones disponibles",
|
||||
"choose_strategy_from_available": "Elegir una estrategia de las estrategias disponibles",
|
||||
"codex_api_base_url_question": "Ingrese la URL base de la API de Codex",
|
||||
"codex_auth_base_url_invalid": "URL base de autenticación de Codex no válida: %w",
|
||||
"codex_auth_base_url_question": "Ingrese la URL base de OAuth de Codex",
|
||||
"codex_browser_open_fallback": "Si su navegador no se abrió, navegue a esta URL para autenticarse:",
|
||||
"codex_decode_models_response_failed": "No se pudo decodificar la respuesta de modelos de Codex: %w",
|
||||
"codex_decode_refresh_response_failed": "No se pudo decodificar la respuesta de token actualizado de Codex: %w",
|
||||
"codex_decode_token_response_failed": "No se pudo decodificar la respuesta de intercambio de token de Codex: %w",
|
||||
"codex_image_file_not_supported": "El proveedor Codex no admite --image-file. Use un archivo adjunto de imagen en su lugar.",
|
||||
"codex_login_account_changed": "El inicio de sesión de Codex está vinculado a una cuenta de ChatGPT diferente a la configuración almacenada. Ejecute 'fabric --setup' de nuevo.",
|
||||
"codex_login_completed": "Inicio de sesión de Codex completado",
|
||||
"codex_login_failed": "Error en el inicio de sesión de Codex: %s",
|
||||
"codex_login_invalid": "Su inicio de sesión de Codex ya no es válido. Ejecute 'fabric --setup' de nuevo.",
|
||||
"codex_login_missing_account_claim": "El inicio de sesión de Codex no incluyó un ID de cuenta de ChatGPT. Este estado de inicio de sesión no es compatible.",
|
||||
"codex_login_missing_auth_code": "El inicio de sesión de Codex no devolvió un código de autorización.",
|
||||
"codex_login_missing_tokens": "El inicio de sesión de Codex no devolvió los tokens de acceso y actualización requeridos.",
|
||||
"codex_login_refresh_failed": "No se pudo actualizar el inicio de sesión de Codex. Ejecute 'fabric --setup' de nuevo.",
|
||||
"codex_login_return_to_fabric": "Volver a Fabric.",
|
||||
"codex_login_revoked": "El inicio de sesión de Codex ha expirado o fue revocado. Ejecute 'fabric --setup' de nuevo.",
|
||||
"codex_login_server_stopped": "El servidor de callback de inicio de sesión de Codex se detuvo antes de completar la autenticación.",
|
||||
"codex_login_state_mismatch": "No se pudo verificar el inicio de sesión de Codex porque el estado OAuth no coincidió.",
|
||||
"codex_login_timed_out": "El inicio de sesión de Codex agotó el tiempo de espera antes de completar la autenticación.",
|
||||
"codex_oauth_missing_auth_code": "Código de autorización faltante",
|
||||
"codex_oauth_random_state_failed": "No se pudo generar un estado OAuth aleatorio seguro: %w",
|
||||
"codex_oauth_server_start_failed": "No se pudo iniciar el servidor local de callback OAuth: %w",
|
||||
"codex_oauth_state_mismatch": "El estado no coincide",
|
||||
"codex_provider_error": "error del proveedor de Codex (estado %d): %s",
|
||||
"codex_refresh_failed_status": "No se pudo actualizar el inicio de sesión de Codex (estado %d)",
|
||||
"codex_refresh_login_failed": "No se pudo actualizar el inicio de sesión de Codex: %w",
|
||||
"codex_refresh_token_required": "Se requiere el token de actualización de Codex. Ejecute 'fabric --setup' de nuevo.",
|
||||
"codex_replay_body_unavailable": "El cuerpo de la solicitud no se puede reproducir para el reintento de reautenticación de Codex",
|
||||
"codex_request_failed": "La solicitud de Codex falló: %w",
|
||||
"codex_request_failed_status": "La solicitud de Codex falló con estado %d",
|
||||
"codex_starting_browser_login": "Iniciando inicio de sesión de OpenAI basado en navegador para Codex.",
|
||||
"codex_token_exchange_failed": "El intercambio de token de Codex falló: %w",
|
||||
"codex_token_persist_failed": "no se pudo guardar el inicio de sesión de Codex: %w",
|
||||
"codex_token_refresh_missing_access_token": "La actualización del token de Codex no devolvió un token de acceso.",
|
||||
"codex_usage_limit_reached": "Límite de uso de Codex alcanzado",
|
||||
"command_completed_successfully": "Comando completado exitosamente",
|
||||
"compression_level_jpeg_webp": "Nivel de compresión 0-100 para formatos JPEG/WebP (predeterminado: no establecido)",
|
||||
"config_file_not_found": "archivo de configuración no encontrado: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Patrones personalizados - Establecer directorio para tus patrones personalizados",
|
||||
"custom_patterns_warning_create_directory": "Advertencia: No se pudo crear el directorio de patrones personalizados %s: %v\n",
|
||||
"db_error_loading_env_file": "error al cargar el archivo .env: %w",
|
||||
"db_error_updating_env_file": "error al actualizar el archivo .env: %w",
|
||||
"defaults_model_context_length_question": "Introduce la longitud del contexto del modelo",
|
||||
"defaults_model_question": "Introduce el índice o el nombre de tu modelo predeterminado",
|
||||
"defaults_setup_description": "Proveedor y modelo de IA predeterminados",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "solicitud de modelos de DigitalOcean falló con estado %d: %s",
|
||||
"disable_openai_responses_api": "Deshabilitar API de Respuestas de OpenAI (predeterminado: false)",
|
||||
"disable_pattern_variable_replacement": "Deshabilitar reemplazo de variables de patrón",
|
||||
"enable_web_search_tool": "Habilitar herramienta de búsqueda web para modelos soportados (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Habilitar herramienta de búsqueda web para modelos soportados (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Etiqueta de fin para secciones de pensamiento",
|
||||
"error_creating_audio_file": "error al crear el archivo de audio: %v",
|
||||
"error_creating_file": "error al crear el archivo: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "formato de URL de datos inválido",
|
||||
"ollama_invalid_http_timeout_using_default": "Tiempo de espera HTTP inválido '%s': %v, usando el valor predeterminado",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx inválido en la solicitud: %v",
|
||||
"ollama_invalid_request_body": "cuerpo de solicitud no válido",
|
||||
"ollama_no_content_from_upstream": "no se recibió contenido del servidor Fabric upstream",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx excede el valor máximo permitido de %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx debe ser un número, se obtuvo tipo inválido",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "Línea SSE excede el límite de búfer de 1MB - línea de datos demasiado grande",
|
||||
"ollama_upstream_non_2xx": "El servidor Fabric upstream devolvió estado no-2xx %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "El servidor Fabric upstream devolvió estado no-2xx %d y el cuerpo no se pudo leer: %v",
|
||||
"ollama_upstream_request_failed": "no se pudo conectar con el servidor Fabric ascendente",
|
||||
"ollama_upstream_returned_status": "el servidor Fabric upstream devolvió estado %d",
|
||||
"ollama_warning_no_content": "Advertencia: no se recibió contenido del servidor Fabric upstream",
|
||||
"ollama_warning_parse_variables": "Advertencia: error al analizar options.variables como JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "no se pudo guardar la imagen en %s: %w",
|
||||
"openai_image_saved_to": "Imagen guardada en: %s",
|
||||
"openai_model_no_image_generation": "el modelo '%s' no soporta generación de imágenes. Modelos soportados: %s",
|
||||
"openai_models_rate_limited": "límite de velocidad excedido al obtener modelos del proveedor %s; reintentar después de %s segundos",
|
||||
"openai_models_response_too_large": "respuesta de modelos demasiado grande del proveedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "no se pudo analizar la respuesta de modelos; respuesta cruda: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de estado inesperado: %d del proveedor %s (error al leer cuerpo de respuesta: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Salida de metadatos del video",
|
||||
"path_to_yaml_config": "Ruta al archivo de configuración YAML",
|
||||
"pattern_not_found_list_available": "patrón '%s' no encontrado. Ejecuta 'fabric -l' para ver los patrones disponibles",
|
||||
"pattern_invalid_name": "nombre de patrón inválido: %q",
|
||||
"pattern_not_found_no_patterns": "patrón '%s' no encontrado.\n\n¡No hay patrones instalados! Para solucionar esto:\n • Ejecuta 'fabric --setup' para configurar y descargar patrones\n • O ejecuta 'fabric -U' para descargar/actualizar patrones directamente",
|
||||
"pattern_variables_help": "Valores para variables de patrón, ej. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Clonando el repositorio %s (ruta: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Preferir lista de reproducción sobre video si ambos ids están presentes en la URL",
|
||||
"print_context": "Imprimir contexto",
|
||||
"print_current_version": "Imprimir versión actual",
|
||||
"print_metadata_to_stderr": "Imprimir metadatos (tokens de entrada/salida) en stderr",
|
||||
"print_pattern_contents": "Imprimir el contenido del patrón indicado en la terminal",
|
||||
"print_session": "Imprimir sesión",
|
||||
"register_new_extension": "Registrar una nueva extensión desde la ruta del archivo de configuración",
|
||||
"remove_registered_extension": "Eliminar una extensión registrada por nombre",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Enviar notificación de escritorio cuando se complete el comando",
|
||||
"serve_fabric_api_ollama_endpoints": "Servir la API REST de Fabric con endpoints de ollama",
|
||||
"serve_fabric_rest_api": "Servir la API REST de Fabric",
|
||||
"server_api_key_required": "se rechaza servir en la dirección no loopback %s sin clave de API: configure --api-key o FABRIC_API_KEY, o use una dirección loopback como 127.0.0.1:8080",
|
||||
"server_chat_error": "Error: %v",
|
||||
"server_error_marshaling_response": "error al serializar la respuesta: %v",
|
||||
"server_error_writing_response": "error al escribir la respuesta: %v",
|
||||
"server_invalid_request_format": "formato de solicitud no válido: %v",
|
||||
"server_no_api_key_warning": "Iniciando el servidor de API REST sin autenticación por clave de API. Esto puede suponer riesgos de seguridad.",
|
||||
"sessions_creating_new": "Creando nueva sesión: %s\n",
|
||||
"set_debug_level": "Establecer nivel de depuración (0=apagado, 1=básico, 2=detallado, 3=rastreo, 4=wire)",
|
||||
"set_frequency_penalty": "Establecer penalización de frecuencia",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Programa**: %s",
|
||||
"spotify_title_label": "**Título**: %s",
|
||||
"spotify_total_episodes_label": "**Total de episodios**: %d",
|
||||
"spotify_url_help": "URL de podcast o episodio de Spotify para obtener metadatos y enviarlos al chat",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Etiqueta de inicio para secciones de pensamiento",
|
||||
"storage_error_delete": "No se pudo eliminar %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "No se pudo guardar %s: %v",
|
||||
"storage_error_stat_entry": "No se pudo obtener información de la entrada %s: %v",
|
||||
"storage_error_unmarshal": "No se pudo deserializar %s: %s",
|
||||
"storage_invalid_name": "nombre inválido: %q",
|
||||
"strategies_available_header": "Estrategias disponibles:",
|
||||
"strategies_cloning_repository": "Clonando el repositorio %s (ruta: %s)...\\n",
|
||||
"strategies_download_success": "✅ Estrategias descargadas e instaladas correctamente en %s\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "el nombre de estrategia %q se resuelve fuera del directorio de estrategias",
|
||||
"stream_help": "Transmitir",
|
||||
"suppress_thinking_tags": "Suprimir texto encerrado en etiquetas de pensamiento",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "número inválido en el tiempo relativo: %q",
|
||||
"template_datetime_error_invalid_relative_format": "formato de tiempo relativo inválido",
|
||||
"template_datetime_error_invalid_unit": "unidad de tiempo inválida: %q",
|
||||
"template_datetime_error_relative_requires_value": "el tiempo relativo requiere un valor",
|
||||
"template_datetime_error_unknown_operation": "datetime: operación desconocida %q",
|
||||
"template_extension_error": "Error en extensión %s: %v",
|
||||
"template_file_error_expand_home_dir": "archivo: no se pudo expandir el directorio home: %v",
|
||||
"template_file_error_invalid_line_count": "archivo: número de líneas no válido %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Variable requerida faltante: %s",
|
||||
"template_plugin_error": "Error en plugin %s: %v",
|
||||
"template_processing_stuck": "Procesamiento de plantilla bloqueado - posible bucle infinito",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: se requiere el nombre de la variable",
|
||||
"template_sys_error_home": "no se pudo obtener el directorio de inicio: %v",
|
||||
"template_sys_error_hostname": "no se pudo obtener el nombre del host: %v",
|
||||
"template_sys_error_pwd": "no se pudo obtener el directorio de trabajo: %v",
|
||||
"template_sys_error_unknown_operation": "sys: operación desconocida %q",
|
||||
"template_sys_error_user": "no se pudo obtener el usuario actual: %v",
|
||||
"template_text_empty_input": "Texto: entrada vacía para la operación %q",
|
||||
"template_text_unknown_operation": "Texto: operación de texto desconocida %q (soportadas: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Espacio de nombres de plugin desconocido: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "error al obtener detalles del video: %v",
|
||||
"youtube_error_parsing_duration": "error al analizar la duración del video: %v",
|
||||
"youtube_error_saving_csv": "error al guardar videos en CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Extraer datos visuales del video usando OCR y FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "falló al crear directorio temporal: %v",
|
||||
"youtube_failed_fetch_comments": "No se pudieron obtener los comentarios: %v",
|
||||
"youtube_failed_get_stream_url": "no se pudo obtener la URL del flujo mediante yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "no se pudo analizar una URL HTTP de flujo válida desde la salida de yt-dlp",
|
||||
"youtube_failed_walk_directory": "falló al recorrer el directorio: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "la extracción de fotogramas con ffmpeg falló: %v, salida: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg es requerido para la extracción visual pero no se encontró en PATH",
|
||||
"youtube_invalid_duration_string": "cadena de duración inválida: %s",
|
||||
"youtube_invalid_seconds_format": "formato de segundos inválido %q: %w",
|
||||
"youtube_invalid_timestamp_format": "formato de marca de tiempo inválido: %s",
|
||||
"youtube_invalid_url": "URL de YouTube no válida, no se puede obtener ID de video o lista de reproducción: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "argumentos de yt-dlp inválidos: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "no se encontró texto legible en los fotogramas visuales del video",
|
||||
"youtube_no_transcript_content": "no se encontró contenido de transcripción en el archivo VTT",
|
||||
"youtube_no_url_provided": "No se proporcionó una URL de YouTube",
|
||||
"youtube_no_video_found_with_id": "no se encontró video con ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Lista de reproducción guardada en %s",
|
||||
"youtube_rate_limit_exceeded": "Límite de tasa de YouTube excedido. Intenta de nuevo más tarde o usa diferentes argumentos de yt-dlp como '--sleep-requests 1' para ralentizar las solicitudes.",
|
||||
"youtube_setup_description": "YouTube - para obtener transcripciones de video (vía yt-dlp) y comentarios/metadatos (vía API de YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract falló en el fotograma %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract es requerido para la extracción visual pero no se encontró en PATH",
|
||||
"youtube_url_help": "Video de YouTube o \"URL\" de lista de reproducción para obtener transcripción, comentarios y enviar al chat o imprimir en la consola y almacenar en el archivo de salida",
|
||||
"youtube_url_is_playlist_not_video": "la URL es una lista de reproducción, no un video",
|
||||
"youtube_video_id_title_header": "VideoID: Título",
|
||||
"youtube_visual_fps_help": "Extraer un número específico de fotogramas por segundo en lugar de usar detección de escenas",
|
||||
"youtube_visual_frame_cue": "[MARCADOR DE FOTOGRAMA VISUAL]",
|
||||
"youtube_visual_sensitivity_help": "Tolerancia para la detección de escenas de FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp no encontrado en PATH. Por favor instala yt-dlp para usar la funcionalidad de transcripción de YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp es requerido para la extracción visual pero no se encontró en PATH",
|
||||
"youtube_ytdlp_stderr_error": "error al leer stderr de yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "AWS Access Key ID خود را وارد کنید (برای استفاده از زنجیره اعتبارنامه AWS خالی بگذارید)",
|
||||
"bedrock_aws_region_label": "منطقه AWS",
|
||||
"bedrock_aws_secret_key_label": "AWS Secret Access Key خود را وارد کنید (برای استفاده از زنجیره اعتبارنامه AWS خالی بگذارید)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "کلاینت Bedrock مقداردهی نشده است — 'fabric --setup' را برای پیکربندی اجرا کنید",
|
||||
"bedrock_converse_failed": "bedrock converse برای مدل %s ناموفق بود: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream برای مدل %s ناموفق بود: %w",
|
||||
"bedrock_empty_response_content": "محتوای پاسخ خالی",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels Bedrock ناموفق بود، استفاده از لیست پشتیبان ثابت",
|
||||
"bedrock_panic_sendstream": "پنیک در SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] کلید API Bedrock / توکن ABSK (توصیهشده — همان کلید Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "1 یا 2 را وارد کنید",
|
||||
"bedrock_setup_choose_auth_method": " روش احراز هویت را انتخاب کنید:",
|
||||
"bedrock_setup_choose_model": " مدل پیشفرض را انتخاب کنید (شناسههای بدون پیشوند در هر منطقهای کار میکنند؛ us./eu./ap. مخصوص منطقه هستند):",
|
||||
"bedrock_setup_choose_region": " منطقه AWS را انتخاب کنید:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "انتخاب نامعتبر: %s (1 یا 2 را وارد کنید)",
|
||||
"bedrock_setup_model_custom_prompt": "شناسه مدل را وارد کنید (مثلاً anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] وارد کردن شناسه مدل دیگر",
|
||||
"bedrock_setup_model_prompt": "شماره مدل را وارد کنید یا برای وارد کردن شناسه دلخواه 0 را بزنید",
|
||||
"bedrock_setup_region_custom_prompt": "منطقه AWS دلخواه را وارد کنید (مثلاً us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] وارد کردن منطقه دیگر",
|
||||
"bedrock_setup_region_prompt": "شماره منطقه را وارد کنید یا برای منطقه دلخواه 0 را بزنید",
|
||||
"bedrock_setup_selected_model": " ✓ مدل انتخابشده: %s",
|
||||
"bedrock_setup_use_with": " ℹ نحوه استفاده: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "بارگذاری پیکربندی AWS ناموفق بود: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "بارگذاری پیکربندی AWS با منطقه %s ناموفق بود: %w",
|
||||
"bedrock_unexpected_content_block_type": "نوع بلوک محتوای غیرمنتظره: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "الگویی از الگوهای موجود انتخاب کنید",
|
||||
"choose_session_from_available": "جلسهای از جلسات موجود انتخاب کنید",
|
||||
"choose_strategy_from_available": "انتخاب استراتژی از استراتژیهای موجود",
|
||||
"codex_api_base_url_question": "آدرس پایه API Codex را وارد کنید",
|
||||
"codex_auth_base_url_invalid": "آدرس پایه احراز هویت Codex نامعتبر است: %w",
|
||||
"codex_auth_base_url_question": "آدرس پایه OAuth Codex را وارد کنید",
|
||||
"codex_browser_open_fallback": "اگر مرورگر شما باز نشد، برای احراز هویت به این آدرس بروید:",
|
||||
"codex_decode_models_response_failed": "رمزگشایی پاسخ مدلهای Codex ناموفق بود: %w",
|
||||
"codex_decode_refresh_response_failed": "رمزگشایی پاسخ توکن بازنشانیشده Codex ناموفق بود: %w",
|
||||
"codex_decode_token_response_failed": "رمزگشایی پاسخ تبادل توکن Codex ناموفق بود: %w",
|
||||
"codex_image_file_not_supported": "ارائهدهنده Codex از --image-file پشتیبانی نمیکند. به جای آن از پیوست تصویر استفاده کنید.",
|
||||
"codex_login_account_changed": "ورود Codex به حساب ChatGPT متفاوتی از پیکربندی ذخیرهشده متصل است. لطفاً 'fabric --setup' را دوباره اجرا کنید.",
|
||||
"codex_login_completed": "ورود Codex تکمیل شد",
|
||||
"codex_login_failed": "ورود Codex ناموفق بود: %s",
|
||||
"codex_login_invalid": "ورود Codex شما دیگر معتبر نیست. لطفاً 'fabric --setup' را دوباره اجرا کنید.",
|
||||
"codex_login_missing_account_claim": "ورود Codex شامل شناسه حساب ChatGPT نبود. این وضعیت ورود پشتیبانی نمیشود.",
|
||||
"codex_login_missing_auth_code": "ورود Codex کد مجوز را برنگرداند.",
|
||||
"codex_login_missing_tokens": "ورود Codex توکنهای دسترسی و بازنشانی مورد نیاز را برنگرداند.",
|
||||
"codex_login_refresh_failed": "بازنشانی ورود Codex امکانپذیر نبود. لطفاً 'fabric --setup' را دوباره اجرا کنید.",
|
||||
"codex_login_return_to_fabric": "بازگشت به Fabric.",
|
||||
"codex_login_revoked": "ورود Codex منقضی شده یا لغو شده است. لطفاً 'fabric --setup' را دوباره اجرا کنید.",
|
||||
"codex_login_server_stopped": "سرور بازگشت ورود Codex قبل از تکمیل احراز هویت متوقف شد.",
|
||||
"codex_login_state_mismatch": "ورود Codex قابل تأیید نبود زیرا وضعیت OAuth مطابقت نداشت.",
|
||||
"codex_login_timed_out": "زمان ورود Codex قبل از تکمیل احراز هویت به پایان رسید.",
|
||||
"codex_oauth_missing_auth_code": "کد مجوز موجود نیست",
|
||||
"codex_oauth_random_state_failed": "تولید وضعیت تصادفی امن OAuth ناموفق بود: %w",
|
||||
"codex_oauth_server_start_failed": "راهاندازی سرور محلی بازگشت OAuth ناموفق بود: %w",
|
||||
"codex_oauth_state_mismatch": "وضعیت مطابقت ندارد",
|
||||
"codex_provider_error": "خطای ارائهدهنده Codex (وضعیت %d): %s",
|
||||
"codex_refresh_failed_status": "بازنشانی ورود Codex ناموفق بود (وضعیت %d)",
|
||||
"codex_refresh_login_failed": "بازنشانی ورود Codex ناموفق بود: %w",
|
||||
"codex_refresh_token_required": "توکن بازنشانی Codex مورد نیاز است. لطفاً 'fabric --setup' را دوباره اجرا کنید.",
|
||||
"codex_replay_body_unavailable": "بدنه درخواست برای تلاش مجدد احراز هویت Codex قابل بازپخش نیست",
|
||||
"codex_request_failed": "درخواست Codex ناموفق بود: %w",
|
||||
"codex_request_failed_status": "درخواست Codex با وضعیت %d ناموفق بود",
|
||||
"codex_starting_browser_login": "شروع ورود مبتنی بر مرورگر OpenAI برای Codex.",
|
||||
"codex_token_exchange_failed": "تبادل توکن Codex ناموفق بود: %w",
|
||||
"codex_token_persist_failed": "ذخیره ورود Codex ناموفق بود: %w",
|
||||
"codex_token_refresh_missing_access_token": "بازنشانی توکن Codex توکن دسترسی را برنگرداند.",
|
||||
"codex_usage_limit_reached": "محدودیت استفاده Codex به حداکثر رسیده است",
|
||||
"command_completed_successfully": "دستور با موفقیت تکمیل شد",
|
||||
"compression_level_jpeg_webp": "سطح فشردهسازی 0-100 برای فرمتهای JPEG/WebP (پیشفرض: تنظیم نشده)",
|
||||
"config_file_not_found": "فایل پیکربندی یافت نشد: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "الگوهای سفارشی - تنظیم دایرکتوری برای الگوهای سفارشی شما",
|
||||
"custom_patterns_warning_create_directory": "هشدار: امکان ایجاد پوشه الگوهای سفارشی %s وجود ندارد: %v\n",
|
||||
"db_error_loading_env_file": "خطا در بارگذاری فایل .env: %w",
|
||||
"db_error_updating_env_file": "خطا در بهروزرسانی فایل .env: %w",
|
||||
"defaults_model_context_length_question": "طول زمینه مدل را وارد کنید",
|
||||
"defaults_model_question": "شاخص یا نام مدل پیشفرض خود را وارد کنید",
|
||||
"defaults_setup_description": "ارائهدهنده و مدل هوش مصنوعی پیشفرض",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "درخواست مدلهای DigitalOcean با وضعیت %d ناموفق بود: %s",
|
||||
"disable_openai_responses_api": "غیرفعال کردن API OpenAI Responses (پیشفرض: false)",
|
||||
"disable_pattern_variable_replacement": "غیرفعال کردن جایگزینی متغیرهای الگو",
|
||||
"enable_web_search_tool": "فعالسازی ابزار جستجوی وب برای مدلهای پشتیبانی شده (Anthropic، OpenAI، Gemini)",
|
||||
"enable_web_search_tool": "فعالسازی ابزار جستجوی وب برای مدلهای پشتیبانی شده (Anthropic، OpenAI، Gemini، Grok)",
|
||||
"end_tag_thinking_sections": "تگ پایان برای بخشهای تفکر",
|
||||
"error_creating_audio_file": "خطا در ایجاد فایل صوتی: %v",
|
||||
"error_creating_file": "خطا در ایجاد فایل: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "فرمت URL داده نامعتبر",
|
||||
"ollama_invalid_http_timeout_using_default": "زمان انتظار HTTP نامعتبر '%s': %v، استفاده از مقدار پیشفرض",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx نامعتبر در درخواست: %v",
|
||||
"ollama_invalid_request_body": "بدنه درخواست نامعتبر",
|
||||
"ollama_no_content_from_upstream": "هیچ محتوایی از سرور Fabric بالادستی دریافت نشد",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx از حداکثر مقدار مجاز %d فراتر رفته است",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx باید یک عدد باشد، نوع نامعتبر دریافت شد",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "خط SSE از حد بافر 1MB فراتر رفت - خط داده بیش از حد بزرگ است",
|
||||
"ollama_upstream_non_2xx": "سرور Fabric بالادستی وضعیت غیر-2xx %d را برگرداند: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "سرور Fabric بالادستی وضعیت غیر-2xx %d را برگرداند و بدنه قابل خواندن نبود: %v",
|
||||
"ollama_upstream_request_failed": "دسترسی به سرور بالادستی Fabric ممکن نیست",
|
||||
"ollama_upstream_returned_status": "سرور Fabric بالادستی وضعیت %d را برگرداند",
|
||||
"ollama_warning_no_content": "هشدار: هیچ محتوایی از سرور Fabric بالادستی دریافت نشد",
|
||||
"ollama_warning_parse_variables": "هشدار: شکست در تجزیه options.variables به عنوان JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "ذخیره تصویر در %s ناموفق بود: %w",
|
||||
"openai_image_saved_to": "تصویر ذخیره شد در: %s",
|
||||
"openai_model_no_image_generation": "مدل '%s' از تولید تصویر پشتیبانی نمیکند. مدلهای پشتیبانی شده: %s",
|
||||
"openai_models_rate_limited": "محدودیت نرخ هنگام دریافت مدلها از ارائهدهنده %s فراتر رفت؛ پس از %s ثانیه دوباره تلاش کنید",
|
||||
"openai_models_response_too_large": "پاسخ مدلها از ارائهدهنده %s بیش از حد بزرگ است (>%d بایت)",
|
||||
"openai_unable_to_parse_models_response": "تجزیه پاسخ مدلها ناموفق بود; پاسخ خام: %s",
|
||||
"openai_unexpected_status_code_read_error": "کد وضعیت غیرمنتظره: %d از ارائهدهنده %s (خطا در خواندن پاسخ: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "نمایش فراداده ویدیو",
|
||||
"path_to_yaml_config": "مسیر فایل پیکربندی YAML",
|
||||
"pattern_not_found_list_available": "الگوی '%s' یافت نشد. برای مشاهده الگوهای موجود 'fabric -l' را اجرا کنید",
|
||||
"pattern_invalid_name": "نام الگوی نامعتبر: %q",
|
||||
"pattern_not_found_no_patterns": "الگوی '%s' یافت نشد.\n\nهیچ الگویی نصب نشده است! برای رفع این مشکل:\n • 'fabric --setup' را برای پیکربندی و دانلود الگوها اجرا کنید\n • یا 'fabric -U' را برای دانلود/بهروزرسانی الگوها اجرا کنید",
|
||||
"pattern_variables_help": "مقادیر برای متغیرهای الگو، مثال: -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "در حال کلون کردن مخزن %s (مسیر: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "اولویت فهرست پخش نسبت به ویدیو اگر هر دو ID در URL موجود باشند",
|
||||
"print_context": "چاپ زمینه",
|
||||
"print_current_version": "چاپ نسخه فعلی",
|
||||
"print_metadata_to_stderr": "چاپ فراداده (توکنهای ورودی/خروجی) در stderr",
|
||||
"print_pattern_contents": "چاپ محتوای الگوی مشخصشده در ترمینال",
|
||||
"print_session": "چاپ جلسه",
|
||||
"register_new_extension": "ثبت افزونه جدید از مسیر فایل پیکربندی",
|
||||
"remove_registered_extension": "حذف افزونه ثبت شده با نام",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "ارسال اعلان دسکتاپ هنگام تکمیل دستور",
|
||||
"serve_fabric_api_ollama_endpoints": "سرویس API REST Fabric با نقاط پایانی ollama",
|
||||
"serve_fabric_rest_api": "سرویس API REST Fabric",
|
||||
"server_api_key_required": "سرویسدهی روی آدرس غیر loopback %s بدون کلید API رد شد: --api-key یا FABRIC_API_KEY را تنظیم کنید، یا به یک آدرس loopback مانند 127.0.0.1:8080 متصل شوید",
|
||||
"server_chat_error": "خطا: %v",
|
||||
"server_error_marshaling_response": "خطا در سریالسازی پاسخ: %v",
|
||||
"server_error_writing_response": "خطا در نوشتن پاسخ: %v",
|
||||
"server_invalid_request_format": "فرمت درخواست نامعتبر: %v",
|
||||
"server_no_api_key_warning": "سرور REST API بدون احراز هویت کلید API راهاندازی میشود. این ممکن است خطرات امنیتی ایجاد کند.",
|
||||
"sessions_creating_new": "ایجاد نشست جدید: %s\n",
|
||||
"set_debug_level": "تنظیم سطح اشکالزدایی (0=خاموش، 1=پایه، 2=تفصیلی، 3=ردیابی، 4=wire)",
|
||||
"set_frequency_penalty": "تنظیم جریمه فرکانس",
|
||||
|
|
@ -546,7 +577,7 @@
|
|||
"spotify_description_header": "## توضیحات",
|
||||
"spotify_duration_label": "**مدت زمان**: %d دقیقه",
|
||||
"spotify_episode_header": "# اپیزود اسپاتیفای",
|
||||
"spotify_error_getting_metadata": "error getting Spotify metadata: %v",
|
||||
"spotify_error_getting_metadata": "خطا در دریافت فراداده Spotify: %v",
|
||||
"spotify_explicit_label": "**محتوای صریح**: %v",
|
||||
"spotify_failed_create_request": "ایجاد درخواست ناموفق بود: %w",
|
||||
"spotify_failed_create_token_request": "ایجاد درخواست توکن ناموفق بود: %w",
|
||||
|
|
@ -560,14 +591,14 @@
|
|||
"spotify_failed_parse_show_metadata": "تجزیه فراداده برنامه ناموفق بود: %w",
|
||||
"spotify_failed_read_response_body": "خواندن متن پاسخ ناموفق بود: %w",
|
||||
"spotify_failed_request_access_token": "درخواست توکن دسترسی ناموفق بود: %w",
|
||||
"spotify_invalid_url": "invalid Spotify URL, can't get show or episode ID: '%s'",
|
||||
"spotify_invalid_url": "نشانی Spotify نامعتبر است، دریافت شناسه برنامه یا قسمت ممکن نیست: '%s'",
|
||||
"spotify_label": "Spotify",
|
||||
"spotify_language_field_label": "**زبان**: %s",
|
||||
"spotify_languages_label": "**زبانها**: %s",
|
||||
"spotify_media_type_label": "**نوع رسانه**: %s",
|
||||
"spotify_no_episode_found": "no episode found with ID: %s",
|
||||
"spotify_no_show_found": "no show found with ID: %s",
|
||||
"spotify_not_configured": "Spotify is not configured, please run the setup procedure",
|
||||
"spotify_no_episode_found": "هیچ قسمتی با این شناسه یافت نشد: %s",
|
||||
"spotify_no_show_found": "هیچ برنامهای با این شناسه یافت نشد: %s",
|
||||
"spotify_not_configured": "Spotify پیکربندی نشده است، لطفاً روند راهاندازی را اجرا کنید",
|
||||
"spotify_publisher_label": "**ناشر**: %s",
|
||||
"spotify_release_date_label": "**تاریخ انتشار**: %s",
|
||||
"spotify_search_description_label": "- **توضیحات**: %s",
|
||||
|
|
@ -576,11 +607,12 @@
|
|||
"spotify_search_publisher_label": "- **ناشر**: %s",
|
||||
"spotify_search_results_header": "# نتایج جستجوی اسپاتیفای",
|
||||
"spotify_search_url_label": "- **URL**: %s",
|
||||
"spotify_setup_description": "Spotify - to grab podcast/show metadata from Spotify",
|
||||
"spotify_setup_description": "Spotify - برای دریافت فراداده پادکست/برنامه از Spotify",
|
||||
"spotify_show_header": "# پادکست/برنامه اسپاتیفای",
|
||||
"spotify_show_name_label": "**برنامه**: %s",
|
||||
"spotify_title_label": "**عنوان**: %s",
|
||||
"spotify_total_episodes_label": "**مجموع اپیزودها**: %d",
|
||||
"spotify_url_help": "نشانی پادکست یا قسمت Spotify برای دریافت فراداده و ارسال به گفتگو",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "تگ شروع برای بخشهای تفکر",
|
||||
"storage_error_delete": "حذف %s ناموفق بود: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "ذخیره %s ناموفق بود: %v",
|
||||
"storage_error_stat_entry": "دریافت اطلاعات ورودی %s ناموفق بود: %v",
|
||||
"storage_error_unmarshal": "بازسریالسازی %s ناموفق بود: %s",
|
||||
"storage_invalid_name": "نام نامعتبر: %q",
|
||||
"strategies_available_header": "راهبردهای موجود:",
|
||||
"strategies_cloning_repository": "در حال کلون کردن مخزن %s (مسیر: %s)...\\n",
|
||||
"strategies_download_success": "✅ راهبردها با موفقیت در %s دانلود و نصب شدند\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "نام راهبرد %q خارج از دایرکتوری راهبردها حل میشود",
|
||||
"stream_help": "پخش زنده",
|
||||
"suppress_thinking_tags": "سرکوب متن محصور در تگهای تفکر",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "عدد نامعتبر در زمان نسبی: %q",
|
||||
"template_datetime_error_invalid_relative_format": "قالب زمان نسبی نامعتبر است",
|
||||
"template_datetime_error_invalid_unit": "واحد زمانی نامعتبر: %q",
|
||||
"template_datetime_error_relative_requires_value": "زمان نسبی به یک مقدار نیاز دارد",
|
||||
"template_datetime_error_unknown_operation": "datetime: عملیات ناشناخته %q",
|
||||
"template_extension_error": "خطای افزونه %s: %v",
|
||||
"template_file_error_expand_home_dir": "فایل: گسترش پوشه خانگی ممکن نشد: %v",
|
||||
"template_file_error_invalid_line_count": "فایل: تعداد خط نامعتبر %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "متغیر الزامی موجود نیست: %s",
|
||||
"template_plugin_error": "خطای پلاگین %s: %v",
|
||||
"template_processing_stuck": "پردازش قالب متوقف شده - احتمال حلقه بینهایت",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: نام متغیر لازم است",
|
||||
"template_sys_error_home": "دریافت پوشه خانگی ناموفق بود: %v",
|
||||
"template_sys_error_hostname": "دریافت نام میزبان ناموفق بود: %v",
|
||||
"template_sys_error_pwd": "دریافت پوشه کاری ناموفق بود: %v",
|
||||
"template_sys_error_unknown_operation": "sys: عملیات ناشناخته %q",
|
||||
"template_sys_error_user": "دریافت کاربر فعلی ناموفق بود: %v",
|
||||
"template_text_empty_input": "متن: ورودی خالی برای عملیات %q",
|
||||
"template_text_unknown_operation": "متن: عملیات متنی ناشناخته %q (پشتیبانی شده: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "فضای نام پلاگین ناشناخته: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "خطا در دریافت جزئیات ویدیو: %v",
|
||||
"youtube_error_parsing_duration": "خطا در تجزیه مدت زمان ویدیو: %v",
|
||||
"youtube_error_saving_csv": "خطا در ذخیره ویدیوها در CSV: %v",
|
||||
"youtube_extract_visual_data_help": "استخراج دادههای بصری از ویدیو با استفاده از OCR و FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "ایجاد دایرکتوری موقت ناموفق بود: %v",
|
||||
"youtube_failed_fetch_comments": "دریافت نظرات ناموفق بود: %v",
|
||||
"youtube_failed_get_stream_url": "دریافت URL جریان از طریق yt-dlp ناموفق بود: %v",
|
||||
"youtube_failed_parse_http_stream_url": "تجزیه یک URL معتبر HTTP برای جریان از خروجی yt-dlp ناموفق بود",
|
||||
"youtube_failed_walk_directory": "پیمایش دایرکتوری ناموفق بود: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "استخراج فریم با ffmpeg ناموفق بود: %v، خروجی: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "برای استخراج بصری به ffmpeg نیاز است اما در PATH پیدا نشد",
|
||||
"youtube_invalid_duration_string": "رشته مدت زمان نامعتبر: %s",
|
||||
"youtube_invalid_seconds_format": "فرمت ثانیه نامعتبر %q: %w",
|
||||
"youtube_invalid_timestamp_format": "فرمت مهر زمانی نامعتبر: %s",
|
||||
"youtube_invalid_url": "URL یوتیوب نامعتبر است، نمیتوان ID ویدیو یا فهرست پخش را دریافت کرد: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "آرگومانهای yt-dlp نامعتبر: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "متن واضحی در فریمهای بصری ویدیو پیدا نشد",
|
||||
"youtube_no_transcript_content": "محتوای رونوشتی در فایل VTT یافت نشد",
|
||||
"youtube_no_url_provided": "هیچ URL یوتیوبی ارائه نشده است",
|
||||
"youtube_no_video_found_with_id": "هیچ ویدیویی با ID یافت نشد: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "لیست پخش ذخیره شد در %s",
|
||||
"youtube_rate_limit_exceeded": "محدودیت نرخ یوتیوب فراتر رفته است. بعداً دوباره امتحان کنید یا از آرگومانهای مختلف yt-dlp مانند '--sleep-requests 1' برای کاهش سرعت درخواستها استفاده کنید.",
|
||||
"youtube_setup_description": "YouTube - برای دریافت رونوشت ویدیو (از طریق yt-dlp) و نظرات/متادیتا (از طریق API یوتیوب)",
|
||||
"youtube_tesseract_frame_failed": "tesseract روی فریم %d شکست خورد: %v، stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "برای استخراج بصری به tesseract نیاز است اما در PATH پیدا نشد",
|
||||
"youtube_url_help": "ویدیو یوتیوب یا \"URL\" فهرست پخش برای دریافت رونوشت، نظرات و ارسال به گفتگو یا چاپ در کنسول و ذخیره در فایل خروجی",
|
||||
"youtube_url_is_playlist_not_video": "URL یک فهرست پخش است، نه یک ویدیو",
|
||||
"youtube_video_id_title_header": "شناسه ویدیو: عنوان",
|
||||
"youtube_visual_fps_help": "استخراج تعداد مشخصی فریم در هر ثانیه بهجای استفاده از تشخیص صحنه",
|
||||
"youtube_visual_frame_cue": "[نشانه فریم بصری]",
|
||||
"youtube_visual_sensitivity_help": "میزان حساسیت تشخیص صحنه در FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp در PATH یافت نشد. لطفاً yt-dlp را نصب کنید تا از قابلیت رونویسی یوتیوب استفاده کنید",
|
||||
"youtube_ytdlp_required_visual_extraction": "برای استخراج بصری به yt-dlp نیاز است اما در PATH پیدا نشد",
|
||||
"youtube_ytdlp_stderr_error": "خطا در خواندن stderr yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Entrez votre AWS Access Key ID (laissez vide pour utiliser la chaîne d'authentification AWS)",
|
||||
"bedrock_aws_region_label": "Région AWS",
|
||||
"bedrock_aws_secret_key_label": "Entrez votre AWS Secret Access Key (laissez vide pour utiliser la chaîne d'authentification AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "client Bedrock non initialisé — exécutez 'fabric --setup' pour configurer",
|
||||
"bedrock_converse_failed": "bedrock converse a échoué pour le modèle %s : %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream a échoué pour le modèle %s : %w",
|
||||
"bedrock_empty_response_content": "contenu de réponse vide",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels Bedrock échouée, utilisation de la liste statique de secours",
|
||||
"bedrock_panic_sendstream": "panique dans SendStream : %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Clé API Bedrock / jeton ABSK (recommandé — identique à Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Entrez 1 ou 2",
|
||||
"bedrock_setup_choose_auth_method": " Choisissez la méthode d'authentification :",
|
||||
"bedrock_setup_choose_model": " Choisissez le modèle par défaut (les ID sans préfixe fonctionnent dans toutes les régions ; us./eu./ap. sont spécifiques à une région) :",
|
||||
"bedrock_setup_choose_region": " Choisissez la région AWS :",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "sélection invalide : %s (entrez 1 ou 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Entrez l'ID du modèle (par ex. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Entrer un autre ID de modèle",
|
||||
"bedrock_setup_model_prompt": "Entrez le numéro du modèle ou 0 pour saisir le vôtre",
|
||||
"bedrock_setup_region_custom_prompt": "Entrez une région AWS personnalisée (par ex. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Entrer une autre région",
|
||||
"bedrock_setup_region_prompt": "Entrez le numéro de la région ou 0 pour une région personnalisée",
|
||||
"bedrock_setup_selected_model": " ✓ Modèle sélectionné : %s",
|
||||
"bedrock_setup_use_with": " ℹ Utilisation : fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "impossible de charger la configuration AWS : %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "impossible de charger la configuration AWS avec la région %s : %w",
|
||||
"bedrock_unexpected_content_block_type": "type de bloc de contenu inattendu : %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Choisissez un motif parmi les motifs disponibles",
|
||||
"choose_session_from_available": "Choisissez une session parmi les sessions disponibles",
|
||||
"choose_strategy_from_available": "Choisir une stratégie parmi les stratégies disponibles",
|
||||
"codex_api_base_url_question": "Entrez l'URL de base de l'API Codex",
|
||||
"codex_auth_base_url_invalid": "URL de base d'authentification Codex invalide : %w",
|
||||
"codex_auth_base_url_question": "Entrez l'URL de base OAuth de Codex",
|
||||
"codex_browser_open_fallback": "Si votre navigateur ne s'est pas ouvert, accédez à cette URL pour vous authentifier :",
|
||||
"codex_decode_models_response_failed": "Échec du décodage de la réponse des modèles Codex : %w",
|
||||
"codex_decode_refresh_response_failed": "Échec du décodage de la réponse du jeton Codex rafraîchi : %w",
|
||||
"codex_decode_token_response_failed": "Échec du décodage de la réponse d'échange de jeton Codex : %w",
|
||||
"codex_image_file_not_supported": "Le fournisseur Codex ne prend pas en charge --image-file. Utilisez une pièce jointe image à la place.",
|
||||
"codex_login_account_changed": "La connexion Codex est liée à un compte ChatGPT différent de la configuration enregistrée. Veuillez relancer 'fabric --setup'.",
|
||||
"codex_login_completed": "Connexion Codex terminée",
|
||||
"codex_login_failed": "Échec de la connexion Codex : %s",
|
||||
"codex_login_invalid": "Votre connexion Codex n'est plus valide. Veuillez relancer 'fabric --setup'.",
|
||||
"codex_login_missing_account_claim": "La connexion Codex n'a pas inclus d'identifiant de compte ChatGPT. Cet état de connexion n'est pas pris en charge.",
|
||||
"codex_login_missing_auth_code": "La connexion Codex n'a pas renvoyé de code d'autorisation.",
|
||||
"codex_login_missing_tokens": "La connexion Codex n'a pas renvoyé les jetons d'accès et de rafraîchissement requis.",
|
||||
"codex_login_refresh_failed": "Le rafraîchissement de la connexion Codex a échoué. Veuillez relancer 'fabric --setup'.",
|
||||
"codex_login_return_to_fabric": "Retourner à Fabric.",
|
||||
"codex_login_revoked": "La connexion Codex a expiré ou a été révoquée. Veuillez relancer 'fabric --setup'.",
|
||||
"codex_login_server_stopped": "Le serveur de rappel de connexion Codex s'est arrêté avant la fin de l'authentification.",
|
||||
"codex_login_state_mismatch": "La connexion Codex n'a pas pu être vérifiée car l'état OAuth ne correspondait pas.",
|
||||
"codex_login_timed_out": "La connexion Codex a expiré avant la fin de l'authentification.",
|
||||
"codex_oauth_missing_auth_code": "Code d'autorisation manquant",
|
||||
"codex_oauth_random_state_failed": "Échec de la génération d'un état OAuth aléatoire sécurisé : %w",
|
||||
"codex_oauth_server_start_failed": "Échec du démarrage du serveur local de rappel OAuth : %w",
|
||||
"codex_oauth_state_mismatch": "L'état ne correspond pas",
|
||||
"codex_provider_error": "erreur du fournisseur Codex (statut %d) : %s",
|
||||
"codex_refresh_failed_status": "Échec du rafraîchissement de la connexion Codex (statut %d)",
|
||||
"codex_refresh_login_failed": "Échec du rafraîchissement de la connexion Codex : %w",
|
||||
"codex_refresh_token_required": "Le jeton de rafraîchissement Codex est requis. Veuillez relancer 'fabric --setup'.",
|
||||
"codex_replay_body_unavailable": "Le corps de la requête ne peut pas être rejoué pour la tentative de réauthentification Codex",
|
||||
"codex_request_failed": "La requête Codex a échoué : %w",
|
||||
"codex_request_failed_status": "La requête Codex a échoué avec le statut %d",
|
||||
"codex_starting_browser_login": "Démarrage de la connexion OpenAI par navigateur pour Codex.",
|
||||
"codex_token_exchange_failed": "L'échange de jeton Codex a échoué : %w",
|
||||
"codex_token_persist_failed": "échec de l'enregistrement de la connexion Codex : %w",
|
||||
"codex_token_refresh_missing_access_token": "Le rafraîchissement du jeton Codex n'a pas renvoyé de jeton d'accès.",
|
||||
"codex_usage_limit_reached": "Limite d'utilisation Codex atteinte",
|
||||
"command_completed_successfully": "Commande terminée avec succès",
|
||||
"compression_level_jpeg_webp": "Niveau de compression 0-100 pour les formats JPEG/WebP (par défaut : non défini)",
|
||||
"config_file_not_found": "fichier de configuration non trouvé : %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Patrons personnalisés - Définir le répertoire pour vos patrons personnalisés",
|
||||
"custom_patterns_warning_create_directory": "Avertissement : Impossible de créer le répertoire de modèles personnalisés %s : %v\n",
|
||||
"db_error_loading_env_file": "erreur lors du chargement du fichier .env : %w",
|
||||
"db_error_updating_env_file": "erreur lors de la mise à jour du fichier .env : %w",
|
||||
"defaults_model_context_length_question": "Saisissez la longueur du contexte du modèle",
|
||||
"defaults_model_question": "Saisissez l'index ou le nom de votre modèle par défaut",
|
||||
"defaults_setup_description": "Fournisseur et modèle d'IA par défaut",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "échec de la requête de modèles DigitalOcean avec le statut %d : %s",
|
||||
"disable_openai_responses_api": "Désactiver l'API OpenAI Responses (par défaut : false)",
|
||||
"disable_pattern_variable_replacement": "Désactiver le remplacement des variables de motif",
|
||||
"enable_web_search_tool": "Activer l'outil de recherche web pour les modèles pris en charge (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Activer l'outil de recherche web pour les modèles pris en charge (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Balise de fin pour les sections de réflexion",
|
||||
"error_creating_audio_file": "erreur lors de la création du fichier audio : %v",
|
||||
"error_creating_file": "erreur lors de la création du fichier : %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "format d'URL de données invalide",
|
||||
"ollama_invalid_http_timeout_using_default": "Délai d'expiration HTTP invalide '%s' : %v, utilisation de la valeur par défaut",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx invalide dans la requête : %v",
|
||||
"ollama_invalid_request_body": "corps de requête invalide",
|
||||
"ollama_no_content_from_upstream": "aucun contenu reçu du serveur Fabric en amont",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx dépasse la valeur maximale autorisée de %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx doit être un nombre, type invalide reçu",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "La ligne SSE dépasse la limite de tampon de 1 Mo - ligne de données trop grande",
|
||||
"ollama_upstream_non_2xx": "Le serveur Fabric en amont a renvoyé un statut non-2xx %d : %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Le serveur Fabric en amont a renvoyé un statut non-2xx %d et le corps n'a pas pu être lu : %v",
|
||||
"ollama_upstream_request_failed": "impossible de joindre le serveur Fabric en amont",
|
||||
"ollama_upstream_returned_status": "le serveur Fabric en amont a renvoyé le statut %d",
|
||||
"ollama_warning_no_content": "Attention : aucun contenu reçu du serveur Fabric en amont",
|
||||
"ollama_warning_parse_variables": "Attention : échec de l'analyse de options.variables en JSON : %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "échec de l'enregistrement de l'image dans %s : %w",
|
||||
"openai_image_saved_to": "Image enregistrée dans : %s",
|
||||
"openai_model_no_image_generation": "le modèle '%s' ne prend pas en charge la génération d'images. Modèles pris en charge : %s",
|
||||
"openai_models_rate_limited": "limite de débit dépassée lors de la récupération des modèles du fournisseur %s ; réessayer après %s secondes",
|
||||
"openai_models_response_too_large": "réponse des modèles trop volumineuse du fournisseur %s (>%d octets)",
|
||||
"openai_unable_to_parse_models_response": "impossible d'analyser la réponse des modèles ; réponse brute : %s",
|
||||
"openai_unexpected_status_code_read_error": "code d'état inattendu : %d du fournisseur %s (échec de lecture du corps de réponse : %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Afficher les métadonnées de la vidéo",
|
||||
"path_to_yaml_config": "Chemin vers le fichier de configuration YAML",
|
||||
"pattern_not_found_list_available": "modèle '%s' non trouvé. Exécutez 'fabric -l' pour voir les modèles disponibles",
|
||||
"pattern_invalid_name": "nom de modèle invalide : %q",
|
||||
"pattern_not_found_no_patterns": "modèle '%s' non trouvé.\n\nAucun modèle n'est installé ! Pour résoudre ce problème :\n • Exécutez 'fabric --setup' pour configurer et télécharger les modèles\n • Ou exécutez 'fabric -U' pour télécharger/mettre à jour les modèles directement",
|
||||
"pattern_variables_help": "Valeurs pour les variables de motif, ex. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Clonage du dépôt %s (chemin : %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Préférer la liste de lecture à la vidéo si les deux IDs sont présents dans l'URL",
|
||||
"print_context": "Afficher le contexte",
|
||||
"print_current_version": "Afficher la version actuelle",
|
||||
"print_metadata_to_stderr": "Afficher les métadonnées (jetons d'entrée/sortie) sur stderr",
|
||||
"print_pattern_contents": "Afficher le contenu du motif indiqué dans le terminal",
|
||||
"print_session": "Afficher la session",
|
||||
"register_new_extension": "Enregistrer une nouvelle extension depuis le chemin du fichier de configuration",
|
||||
"remove_registered_extension": "Supprimer une extension enregistrée par nom",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Envoyer une notification de bureau quand la commande se termine",
|
||||
"serve_fabric_api_ollama_endpoints": "Servir l'API REST Fabric avec les endpoints ollama",
|
||||
"serve_fabric_rest_api": "Servir l'API REST Fabric",
|
||||
"server_api_key_required": "refus de servir sur l'adresse non loopback %s sans clé API : définissez --api-key ou FABRIC_API_KEY, ou liez une adresse loopback comme 127.0.0.1:8080",
|
||||
"server_chat_error": "Erreur : %v",
|
||||
"server_error_marshaling_response": "erreur de sérialisation de la réponse : %v",
|
||||
"server_error_writing_response": "erreur d'écriture de la réponse : %v",
|
||||
"server_invalid_request_format": "format de requête invalide : %v",
|
||||
"server_no_api_key_warning": "Démarrage du serveur API REST sans authentification par clé API. Cela peut présenter des risques de sécurité.",
|
||||
"sessions_creating_new": "Création d'une nouvelle session : %s\n",
|
||||
"set_debug_level": "Définir le niveau de débogage (0=désactivé, 1=basique, 2=détaillé, 3=trace, 4=wire)",
|
||||
"set_frequency_penalty": "Définir la pénalité de fréquence",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Émission** : %s",
|
||||
"spotify_title_label": "**Titre** : %s",
|
||||
"spotify_total_episodes_label": "**Épisodes au total** : %d",
|
||||
"spotify_url_help": "URL de podcast ou d'épisode Spotify pour récupérer les métadonnées et les envoyer au chat",
|
||||
"spotify_url_label": "**URL** : %s",
|
||||
"start_tag_thinking_sections": "Balise de début pour les sections de réflexion",
|
||||
"storage_error_delete": "Impossible de supprimer %s : %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "Impossible de sauvegarder %s : %v",
|
||||
"storage_error_stat_entry": "Impossible d'obtenir les informations de l'entrée %s : %v",
|
||||
"storage_error_unmarshal": "Impossible de désérialiser %s : %s",
|
||||
"storage_invalid_name": "nom invalide : %q",
|
||||
"strategies_available_header": "Stratégies disponibles :",
|
||||
"strategies_cloning_repository": "Clonage du dépôt %s (chemin : %s)...\\n",
|
||||
"strategies_download_success": "✅ Stratégies téléchargées et installées avec succès dans %s\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "le nom de stratégie %q se résout en dehors du répertoire des stratégies",
|
||||
"stream_help": "Streaming",
|
||||
"suppress_thinking_tags": "Supprimer le texte encadré par les balises de réflexion",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "nombre invalide dans le temps relatif : %q",
|
||||
"template_datetime_error_invalid_relative_format": "format de temps relatif invalide",
|
||||
"template_datetime_error_invalid_unit": "unité de temps invalide : %q",
|
||||
"template_datetime_error_relative_requires_value": "le temps relatif nécessite une valeur",
|
||||
"template_datetime_error_unknown_operation": "datetime: opération inconnue %q",
|
||||
"template_extension_error": "Erreur d'extension %s : %v",
|
||||
"template_file_error_expand_home_dir": "fichier : impossible d'étendre le répertoire personnel : %v",
|
||||
"template_file_error_invalid_line_count": "fichier : nombre de lignes invalide %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Variable requise manquante : %s",
|
||||
"template_plugin_error": "Erreur du plugin %s : %v",
|
||||
"template_processing_stuck": "Traitement du modèle bloqué - boucle infinie potentielle",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: nom de variable requis",
|
||||
"template_sys_error_home": "impossible d'obtenir le répertoire personnel : %v",
|
||||
"template_sys_error_hostname": "impossible d'obtenir le nom d'hôte : %v",
|
||||
"template_sys_error_pwd": "impossible d'obtenir le répertoire de travail : %v",
|
||||
"template_sys_error_unknown_operation": "sys: opération inconnue %q",
|
||||
"template_sys_error_user": "impossible d'obtenir l'utilisateur actuel : %v",
|
||||
"template_text_empty_input": "Texte : entrée vide pour l'opération %q",
|
||||
"template_text_unknown_operation": "Texte : opération de texte inconnue %q (supportées : upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Espace de noms de plugin inconnu : %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "erreur lors de l'obtention des détails de la vidéo : %v",
|
||||
"youtube_error_parsing_duration": "erreur lors de l'analyse de la durée de la vidéo : %v",
|
||||
"youtube_error_saving_csv": "erreur lors de l'enregistrement des vidéos en CSV : %v",
|
||||
"youtube_extract_visual_data_help": "Extraire les données visuelles de la vidéo avec OCR et FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "échec de création du répertoire temporaire : %v",
|
||||
"youtube_failed_fetch_comments": "Échec de la récupération des commentaires : %v",
|
||||
"youtube_failed_get_stream_url": "échec de récupération de l’URL du flux via yt-dlp : %v",
|
||||
"youtube_failed_parse_http_stream_url": "échec de l’analyse d’une URL HTTP de flux valide depuis la sortie de yt-dlp",
|
||||
"youtube_failed_walk_directory": "échec du parcours du répertoire : %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "extraction des images avec ffmpeg échouée : %v, sortie : %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg est requis pour l’extraction visuelle mais est introuvable dans PATH",
|
||||
"youtube_invalid_duration_string": "chaîne de durée invalide : %s",
|
||||
"youtube_invalid_seconds_format": "format de secondes invalide %q : %w",
|
||||
"youtube_invalid_timestamp_format": "format d'horodatage invalide : %s",
|
||||
"youtube_invalid_url": "URL YouTube invalide, impossible d'obtenir l'ID de vidéo ou de liste de lecture : '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "arguments yt-dlp invalides : %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "aucun texte lisible trouvé dans les images visuelles de la vidéo",
|
||||
"youtube_no_transcript_content": "aucun contenu de transcription trouvé dans le fichier VTT",
|
||||
"youtube_no_url_provided": "Aucune URL YouTube fournie",
|
||||
"youtube_no_video_found_with_id": "aucune vidéo trouvée avec l'ID : %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Liste de lecture enregistrée dans %s",
|
||||
"youtube_rate_limit_exceeded": "Limite de taux YouTube dépassée. Réessayez plus tard ou utilisez différents arguments yt-dlp comme '--sleep-requests 1' pour ralentir les requêtes.",
|
||||
"youtube_setup_description": "YouTube - pour récupérer les transcriptions vidéo (via yt-dlp) et les commentaires/métadonnées (via l'API YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract a échoué sur l’image %d : %v, stderr : %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract est requis pour l’extraction visuelle mais est introuvable dans PATH",
|
||||
"youtube_url_help": "Vidéo YouTube ou \"URL\" de liste de lecture pour récupérer la transcription, les commentaires et envoyer au chat ou afficher dans la console et stocker dans le fichier de sortie",
|
||||
"youtube_url_is_playlist_not_video": "l'URL est une liste de lecture, pas une vidéo",
|
||||
"youtube_video_id_title_header": "VideoID : Titre",
|
||||
"youtube_visual_fps_help": "Extraire un nombre précis d’images par seconde au lieu d’utiliser la détection de scènes",
|
||||
"youtube_visual_frame_cue": "[REPÈRE D’IMAGE VISUELLE]",
|
||||
"youtube_visual_sensitivity_help": "Tolérance pour la détection de scènes FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp introuvable dans PATH. Veuillez installer yt-dlp pour utiliser la fonctionnalité de transcription YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp est requis pour l’extraction visuelle mais est introuvable dans PATH",
|
||||
"youtube_ytdlp_stderr_error": "erreur lors de la lecture du stderr de yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Inserisci il tuo AWS Access Key ID (lascia vuoto per usare la catena di credenziali AWS)",
|
||||
"bedrock_aws_region_label": "Regione AWS",
|
||||
"bedrock_aws_secret_key_label": "Inserisci il tuo AWS Secret Access Key (lascia vuoto per usare la catena di credenziali AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "client Bedrock non inizializzato — eseguire 'fabric --setup' per configurare",
|
||||
"bedrock_converse_failed": "bedrock converse fallito per il modello %s: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream fallito per il modello %s: %w",
|
||||
"bedrock_empty_response_content": "contenuto della risposta vuoto",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels Bedrock fallita, utilizzo della lista statica di fallback",
|
||||
"bedrock_panic_sendstream": "panic in SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Chiave API Bedrock / token ABSK (consigliato — la stessa usata da Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Inserisci 1 o 2",
|
||||
"bedrock_setup_choose_auth_method": " Scegli il metodo di autenticazione:",
|
||||
"bedrock_setup_choose_model": " Scegli il modello predefinito (gli ID senza prefisso funzionano in qualsiasi regione; us./eu./ap. sono specifici per regione):",
|
||||
"bedrock_setup_choose_region": " Scegli la regione AWS:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "selezione non valida: %s (inserisci 1 o 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Inserisci l'ID del modello (es. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Inserisci un ID modello diverso",
|
||||
"bedrock_setup_model_prompt": "Inserisci il numero del modello o 0 per digitarne uno tuo",
|
||||
"bedrock_setup_region_custom_prompt": "Inserisci una regione AWS personalizzata (es. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Inserisci una regione diversa",
|
||||
"bedrock_setup_region_prompt": "Inserisci il numero della regione o 0 per personalizzarla",
|
||||
"bedrock_setup_selected_model": " ✓ Modello selezionato: %s",
|
||||
"bedrock_setup_use_with": " ℹ Uso: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "impossibile caricare la configurazione AWS: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "impossibile caricare la configurazione AWS con regione %s: %w",
|
||||
"bedrock_unexpected_content_block_type": "tipo di blocco contenuto inaspettato: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Scegli un pattern dai pattern disponibili",
|
||||
"choose_session_from_available": "Scegli una sessione dalle sessioni disponibili",
|
||||
"choose_strategy_from_available": "Scegli una strategia dalle strategie disponibili",
|
||||
"codex_api_base_url_question": "Inserisci l'URL di base dell'API Codex",
|
||||
"codex_auth_base_url_invalid": "URL base di autenticazione Codex non valido: %w",
|
||||
"codex_auth_base_url_question": "Inserisci l'URL di base OAuth di Codex",
|
||||
"codex_browser_open_fallback": "Se il browser non si è aperto, navigare a questo URL per autenticarsi:",
|
||||
"codex_decode_models_response_failed": "Decodifica della risposta dei modelli Codex non riuscita: %w",
|
||||
"codex_decode_refresh_response_failed": "Decodifica della risposta del token Codex aggiornato non riuscita: %w",
|
||||
"codex_decode_token_response_failed": "Decodifica della risposta di scambio token Codex non riuscita: %w",
|
||||
"codex_image_file_not_supported": "Il fornitore Codex non supporta --image-file. Utilizzare un allegato immagine.",
|
||||
"codex_login_account_changed": "L'accesso Codex è collegato a un account ChatGPT diverso dalla configurazione salvata. Eseguire di nuovo 'fabric --setup'.",
|
||||
"codex_login_completed": "Accesso Codex completato",
|
||||
"codex_login_failed": "Accesso Codex non riuscito: %s",
|
||||
"codex_login_invalid": "L'accesso Codex non è più valido. Eseguire di nuovo 'fabric --setup'.",
|
||||
"codex_login_missing_account_claim": "L'accesso Codex non includeva un ID account ChatGPT. Questo stato di accesso non è supportato.",
|
||||
"codex_login_missing_auth_code": "L'accesso Codex non ha restituito un codice di autorizzazione.",
|
||||
"codex_login_missing_tokens": "L'accesso Codex non ha restituito i token di accesso e aggiornamento richiesti.",
|
||||
"codex_login_refresh_failed": "Impossibile aggiornare l'accesso Codex. Eseguire di nuovo 'fabric --setup'.",
|
||||
"codex_login_return_to_fabric": "Torna a Fabric.",
|
||||
"codex_login_revoked": "L'accesso Codex è scaduto o è stato revocato. Eseguire di nuovo 'fabric --setup'.",
|
||||
"codex_login_server_stopped": "Il server di callback dell'accesso Codex si è fermato prima del completamento dell'autenticazione.",
|
||||
"codex_login_state_mismatch": "L'accesso Codex non è stato verificato perché lo stato OAuth non corrispondeva.",
|
||||
"codex_login_timed_out": "L'accesso Codex è scaduto prima del completamento dell'autenticazione.",
|
||||
"codex_oauth_missing_auth_code": "Codice di autorizzazione mancante",
|
||||
"codex_oauth_random_state_failed": "Generazione dello stato OAuth casuale sicuro non riuscita: %w",
|
||||
"codex_oauth_server_start_failed": "Avvio del server locale di callback OAuth non riuscito: %w",
|
||||
"codex_oauth_state_mismatch": "Lo stato non corrisponde",
|
||||
"codex_provider_error": "errore del fornitore Codex (stato %d): %s",
|
||||
"codex_refresh_failed_status": "Aggiornamento dell'accesso Codex non riuscito (stato %d)",
|
||||
"codex_refresh_login_failed": "Aggiornamento dell'accesso Codex non riuscito: %w",
|
||||
"codex_refresh_token_required": "Il token di aggiornamento Codex è richiesto. Eseguire di nuovo 'fabric --setup'.",
|
||||
"codex_replay_body_unavailable": "Il corpo della richiesta non può essere riprodotto per il tentativo di riautenticazione Codex",
|
||||
"codex_request_failed": "La richiesta Codex è fallita: %w",
|
||||
"codex_request_failed_status": "La richiesta Codex è fallita con stato %d",
|
||||
"codex_starting_browser_login": "Avvio dell'accesso OpenAI basato su browser per Codex.",
|
||||
"codex_token_exchange_failed": "Lo scambio di token Codex è fallito: %w",
|
||||
"codex_token_persist_failed": "impossibile salvare l'accesso Codex: %w",
|
||||
"codex_token_refresh_missing_access_token": "L'aggiornamento del token Codex non ha restituito un token di accesso.",
|
||||
"codex_usage_limit_reached": "Limite di utilizzo Codex raggiunto",
|
||||
"command_completed_successfully": "Comando completato con successo",
|
||||
"compression_level_jpeg_webp": "Livello di compressione 0-100 per formati JPEG/WebP (predefinito: non impostato)",
|
||||
"config_file_not_found": "file di configurazione non trovato: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Pattern personalizzati - Imposta la directory per i tuoi pattern personalizzati",
|
||||
"custom_patterns_warning_create_directory": "Avviso: Impossibile creare la directory dei modelli personalizzati %s: %v\n",
|
||||
"db_error_loading_env_file": "errore nel caricamento del file .env: %w",
|
||||
"db_error_updating_env_file": "errore nell'aggiornamento del file .env: %w",
|
||||
"defaults_model_context_length_question": "Inserisci la lunghezza del contesto del modello",
|
||||
"defaults_model_question": "Inserisci l'indice o il nome del tuo modello predefinito",
|
||||
"defaults_setup_description": "Fornitore e modello AI predefiniti",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "richiesta modelli DigitalOcean fallita con stato %d: %s",
|
||||
"disable_openai_responses_api": "Disabilita API OpenAI Responses (predefinito: false)",
|
||||
"disable_pattern_variable_replacement": "Disabilita sostituzione variabili pattern",
|
||||
"enable_web_search_tool": "Abilita strumento di ricerca web per modelli supportati (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Abilita strumento di ricerca web per modelli supportati (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Tag di fine per sezioni di pensiero",
|
||||
"error_creating_audio_file": "errore nella creazione del file audio: %v",
|
||||
"error_creating_file": "errore nella creazione del file: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "formato URL dati non valido",
|
||||
"ollama_invalid_http_timeout_using_default": "Timeout HTTP non valido '%s': %v, utilizzo del valore predefinito",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx non valido nella richiesta: %v",
|
||||
"ollama_invalid_request_body": "corpo della richiesta non valido",
|
||||
"ollama_no_content_from_upstream": "nessun contenuto ricevuto dal server Fabric upstream",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx supera il valore massimo consentito di %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx deve essere un numero, ricevuto tipo non valido",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "La riga SSE supera il limite del buffer di 1MB - riga di dati troppo grande",
|
||||
"ollama_upstream_non_2xx": "Il server Fabric upstream ha restituito stato non-2xx %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Il server Fabric upstream ha restituito stato non-2xx %d e il corpo non è stato leggibile: %v",
|
||||
"ollama_upstream_request_failed": "impossibile raggiungere il server Fabric a monte",
|
||||
"ollama_upstream_returned_status": "il server Fabric upstream ha restituito stato %d",
|
||||
"ollama_warning_no_content": "Avviso: nessun contenuto ricevuto dal server Fabric upstream",
|
||||
"ollama_warning_parse_variables": "Avviso: impossibile analizzare options.variables come JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "salvataggio dell'immagine in %s fallito: %w",
|
||||
"openai_image_saved_to": "Immagine salvata in: %s",
|
||||
"openai_model_no_image_generation": "il modello '%s' non supporta la generazione di immagini. Modelli supportati: %s",
|
||||
"openai_models_rate_limited": "limite di richieste superato durante il recupero dei modelli dal provider %s; riprovare dopo %s secondi",
|
||||
"openai_models_response_too_large": "risposta dei modelli troppo grande dal provider %s (>%d byte)",
|
||||
"openai_unable_to_parse_models_response": "impossibile analizzare risposta modelli; risposta grezza: %s",
|
||||
"openai_unexpected_status_code_read_error": "codice di stato imprevisto: %d dal provider %s (errore lettura corpo risposta: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Output metadati video",
|
||||
"path_to_yaml_config": "Percorso del file di configurazione YAML",
|
||||
"pattern_not_found_list_available": "pattern '%s' non trovato. Esegui 'fabric -l' per vedere i pattern disponibili",
|
||||
"pattern_invalid_name": "nome pattern non valido: %q",
|
||||
"pattern_not_found_no_patterns": "pattern '%s' non trovato.\n\nNessun pattern installato! Per risolvere:\n • Esegui 'fabric --setup' per configurare e scaricare i pattern\n • Oppure esegui 'fabric -U' per scaricare/aggiornare i pattern direttamente",
|
||||
"pattern_variables_help": "Valori per le variabili pattern, es. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Clonazione del repository %s (percorso: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Preferisci playlist al video se entrambi gli ID sono presenti nell'URL",
|
||||
"print_context": "Stampa contesto",
|
||||
"print_current_version": "Stampa versione corrente",
|
||||
"print_metadata_to_stderr": "Stampa i metadati (token di input/output) su stderr",
|
||||
"print_pattern_contents": "Stampa il contenuto del pattern indicato nel terminale",
|
||||
"print_session": "Stampa sessione",
|
||||
"register_new_extension": "Registra una nuova estensione dal percorso del file di configurazione",
|
||||
"remove_registered_extension": "Rimuovi un'estensione registrata per nome",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Invia notifica desktop quando il comando è completato",
|
||||
"serve_fabric_api_ollama_endpoints": "Servi l'API REST di Fabric con endpoint ollama",
|
||||
"serve_fabric_rest_api": "Servi l'API REST di Fabric",
|
||||
"server_api_key_required": "rifiuto di servire sull'indirizzo non loopback %s senza chiave API: impostare --api-key o FABRIC_API_KEY, oppure associare un indirizzo loopback come 127.0.0.1:8080",
|
||||
"server_chat_error": "Errore: %v",
|
||||
"server_error_marshaling_response": "errore nella serializzazione della risposta: %v",
|
||||
"server_error_writing_response": "errore nella scrittura della risposta: %v",
|
||||
"server_invalid_request_format": "formato della richiesta non valido: %v",
|
||||
"server_no_api_key_warning": "Avvio del server API REST senza autenticazione con chiave API. Ciò può comportare rischi per la sicurezza.",
|
||||
"sessions_creating_new": "Creazione nuova sessione: %s\n",
|
||||
"set_debug_level": "Imposta livello di debug (0=spento, 1=base, 2=dettagliato, 3=traccia, 4=wire)",
|
||||
"set_frequency_penalty": "Imposta penalità di frequenza",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Show**: %s",
|
||||
"spotify_title_label": "**Titolo**: %s",
|
||||
"spotify_total_episodes_label": "**Episodi totali**: %d",
|
||||
"spotify_url_help": "URL di podcast o episodio Spotify da cui ottenere i metadati e inviarli alla chat",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Tag di inizio per sezioni di pensiero",
|
||||
"storage_error_delete": "Impossibile eliminare %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "Impossibile salvare %s: %v",
|
||||
"storage_error_stat_entry": "Impossibile ottenere informazioni sulla voce %s: %v",
|
||||
"storage_error_unmarshal": "Impossibile deserializzare %s: %s",
|
||||
"storage_invalid_name": "nome non valido: %q",
|
||||
"strategies_available_header": "Strategie disponibili:",
|
||||
"strategies_cloning_repository": "Clonazione del repository %s (percorso: %s)...\\n",
|
||||
"strategies_download_success": "✅ Strategie scaricate e installate correttamente in %s\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "il nome della strategia %q si risolve al di fuori della directory delle strategie",
|
||||
"stream_help": "Streaming",
|
||||
"suppress_thinking_tags": "Sopprimi testo racchiuso in tag di pensiero",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "numero non valido nel tempo relativo: %q",
|
||||
"template_datetime_error_invalid_relative_format": "formato di tempo relativo non valido",
|
||||
"template_datetime_error_invalid_unit": "unità di tempo non valida: %q",
|
||||
"template_datetime_error_relative_requires_value": "il tempo relativo richiede un valore",
|
||||
"template_datetime_error_unknown_operation": "datetime: operazione sconosciuta %q",
|
||||
"template_extension_error": "Errore dell'estensione %s: %v",
|
||||
"template_file_error_expand_home_dir": "file: impossibile espandere la directory home: %v",
|
||||
"template_file_error_invalid_line_count": "file: numero di righe non valido %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Variabile richiesta mancante: %s",
|
||||
"template_plugin_error": "Errore del plugin %s: %v",
|
||||
"template_processing_stuck": "Elaborazione del modello bloccata - possibile ciclo infinito",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: nome della variabile richiesto",
|
||||
"template_sys_error_home": "impossibile ottenere la directory home: %v",
|
||||
"template_sys_error_hostname": "impossibile ottenere il nome host: %v",
|
||||
"template_sys_error_pwd": "impossibile ottenere la directory di lavoro: %v",
|
||||
"template_sys_error_unknown_operation": "sys: operazione sconosciuta %q",
|
||||
"template_sys_error_user": "impossibile ottenere l'utente corrente: %v",
|
||||
"template_text_empty_input": "Testo: input vuoto per l'operazione %q",
|
||||
"template_text_unknown_operation": "Testo: operazione di testo sconosciuta %q (supportate: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Namespace del plugin sconosciuto: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "errore nell'ottenere i dettagli del video: %v",
|
||||
"youtube_error_parsing_duration": "errore nell'analizzare la durata del video: %v",
|
||||
"youtube_error_saving_csv": "errore nel salvare i video in CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Estrarre dati visivi dal video usando OCR e FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "impossibile creare la directory temporanea: %v",
|
||||
"youtube_failed_fetch_comments": "Recupero dei commenti fallito: %v",
|
||||
"youtube_failed_get_stream_url": "impossibile ottenere l’URL dello stream tramite yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "impossibile analizzare un URL HTTP di stream valido dall’output di yt-dlp",
|
||||
"youtube_failed_walk_directory": "impossibile esplorare la directory: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "estrazione dei fotogrammi con ffmpeg non riuscita: %v, output: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg è richiesto per l’estrazione visiva ma non è stato trovato nel PATH",
|
||||
"youtube_invalid_duration_string": "stringa di durata non valida: %s",
|
||||
"youtube_invalid_seconds_format": "formato secondi non valido %q: %w",
|
||||
"youtube_invalid_timestamp_format": "formato timestamp non valido: %s",
|
||||
"youtube_invalid_url": "URL YouTube non valido, impossibile ottenere l'ID del video o della playlist: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "argomenti yt-dlp non validi: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "nessun testo leggibile trovato nei fotogrammi visivi del video",
|
||||
"youtube_no_transcript_content": "nessun contenuto di trascrizione trovato nel file VTT",
|
||||
"youtube_no_url_provided": "Nessun URL YouTube fornito",
|
||||
"youtube_no_video_found_with_id": "nessun video trovato con ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Playlist salvata in %s",
|
||||
"youtube_rate_limit_exceeded": "Limite di richieste YouTube superato. Riprova più tardi o usa argomenti yt-dlp diversi come '--sleep-requests 1' per rallentare le richieste.",
|
||||
"youtube_setup_description": "YouTube - per ottenere trascrizioni video (tramite yt-dlp) e commenti/metadati (tramite API YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract non riuscito sul fotogramma %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract è richiesto per l’estrazione visiva ma non è stato trovato nel PATH",
|
||||
"youtube_url_help": "Video YouTube o \"URL\" della playlist per ottenere trascrizioni, commenti e inviarli alla chat o stamparli sulla console e memorizzarli nel file di output",
|
||||
"youtube_url_is_playlist_not_video": "l'URL è una playlist, non un video",
|
||||
"youtube_video_id_title_header": "VideoID: Titolo",
|
||||
"youtube_visual_fps_help": "Estrarre un numero specifico di fotogrammi al secondo invece di usare il rilevamento scene",
|
||||
"youtube_visual_frame_cue": "[INDICATORE FOTOGRAMMA VISIVO]",
|
||||
"youtube_visual_sensitivity_help": "Tolleranza per il rilevamento scene di FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp non trovato in PATH. Per favore installa yt-dlp per usare la funzionalità di trascrizione YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp è richiesto per l’estrazione visiva ma non è stato trovato nel PATH",
|
||||
"youtube_ytdlp_stderr_error": "errore durante la lettura dello stderr di yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "AWS Access Key IDを入力してください(AWS認証チェーンを使用する場合は空のままにしてください)",
|
||||
"bedrock_aws_region_label": "AWSリージョン",
|
||||
"bedrock_aws_secret_key_label": "AWS Secret Access Keyを入力してください(AWS認証チェーンを使用する場合は空のままにしてください)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "Bedrockクライアントが初期化されていません — 設定するには 'fabric --setup' を実行してください",
|
||||
"bedrock_converse_failed": "モデル %s のbedrock converseが失敗しました: %w",
|
||||
"bedrock_conversestream_failed": "モデル %s のbedrock conversestreamが失敗しました: %w",
|
||||
"bedrock_empty_response_content": "空のレスポンスコンテンツ",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "Bedrock ListModels APIが失敗しました、静的フォールバックリストを使用",
|
||||
"bedrock_panic_sendstream": "SendStreamでパニックが発生しました: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock APIキー / ABSKトークン(推奨 — Claude Code と同じ)",
|
||||
"bedrock_setup_auth_prompt": "1 または 2 を入力してください",
|
||||
"bedrock_setup_choose_auth_method": " 認証方法を選択してください:",
|
||||
"bedrock_setup_choose_model": " デフォルトモデルを選択してください(プレフィックスなしのIDはどのリージョンでも使用できます。us./eu./ap. はリージョン固有です):",
|
||||
"bedrock_setup_choose_region": " AWSリージョンを選択してください:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "無効な選択: %s(1 または 2 を入力してください)",
|
||||
"bedrock_setup_model_custom_prompt": "モデルIDを入力してください(例: anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] 別のモデルIDを入力",
|
||||
"bedrock_setup_model_prompt": "モデル番号を入力、または 0 で任意のIDを入力",
|
||||
"bedrock_setup_region_custom_prompt": "カスタムAWSリージョンを入力してください(例: us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] 別のリージョンを入力",
|
||||
"bedrock_setup_region_prompt": "リージョン番号を入力、または 0 でカスタム指定",
|
||||
"bedrock_setup_selected_model": " ✓ 選択したモデル: %s",
|
||||
"bedrock_setup_use_with": " ℹ 使用方法: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "AWS設定を読み込めませんでした: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "リージョン %s でAWS設定を読み込めませんでした: %w",
|
||||
"bedrock_unexpected_content_block_type": "予期しないコンテンツブロックタイプ: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "利用可能なパターンからパターンを選択",
|
||||
"choose_session_from_available": "利用可能なセッションからセッションを選択",
|
||||
"choose_strategy_from_available": "利用可能な戦略から戦略を選択",
|
||||
"codex_api_base_url_question": "Codex APIベースURLを入力してください",
|
||||
"codex_auth_base_url_invalid": "Codex認証ベースURLが無効です: %w",
|
||||
"codex_auth_base_url_question": "Codex OAuthベースURLを入力してください",
|
||||
"codex_browser_open_fallback": "ブラウザが開かなかった場合は、このURLに移動して認証してください:",
|
||||
"codex_decode_models_response_failed": "Codexモデルレスポンスのデコードに失敗しました: %w",
|
||||
"codex_decode_refresh_response_failed": "更新されたCodexトークンレスポンスのデコードに失敗しました: %w",
|
||||
"codex_decode_token_response_failed": "Codexトークン交換レスポンスのデコードに失敗しました: %w",
|
||||
"codex_image_file_not_supported": "Codexベンダーは--image-fileをサポートしていません。代わりに画像添付を使用してください。",
|
||||
"codex_login_account_changed": "Codexログインが保存された設定とは異なるChatGPTアカウントに紐づけられています。'fabric --setup'を再実行してください。",
|
||||
"codex_login_completed": "Codexログインが完了しました",
|
||||
"codex_login_failed": "Codexログインに失敗しました: %s",
|
||||
"codex_login_invalid": "Codexログインは無効になりました。'fabric --setup'を再実行してください。",
|
||||
"codex_login_missing_account_claim": "CodexログインにChatGPTアカウントIDが含まれていませんでした。このログイン状態はサポートされていません。",
|
||||
"codex_login_missing_auth_code": "Codexログインが認証コードを返しませんでした。",
|
||||
"codex_login_missing_tokens": "Codexログインが必要なアクセストークンとリフレッシュトークンを返しませんでした。",
|
||||
"codex_login_refresh_failed": "Codexログインを更新できませんでした。'fabric --setup'を再実行してください。",
|
||||
"codex_login_return_to_fabric": "Fabricに戻る。",
|
||||
"codex_login_revoked": "Codexログインが期限切れまたは取り消されました。'fabric --setup'を再実行してください。",
|
||||
"codex_login_server_stopped": "認証が完了する前にCodexログインコールバックサーバーが停止しました。",
|
||||
"codex_login_state_mismatch": "OAuthステートが一致しなかったため、Codexログインを検証できませんでした。",
|
||||
"codex_login_timed_out": "認証が完了する前にCodexログインがタイムアウトしました。",
|
||||
"codex_oauth_missing_auth_code": "認証コードがありません",
|
||||
"codex_oauth_random_state_failed": "安全なランダムOAuthステートの生成に失敗しました: %w",
|
||||
"codex_oauth_server_start_failed": "ローカルOAuthコールバックサーバーの起動に失敗しました: %w",
|
||||
"codex_oauth_state_mismatch": "ステートが一致しません",
|
||||
"codex_provider_error": "Codexベンダーエラー(ステータス %d): %s",
|
||||
"codex_refresh_failed_status": "Codexログインの更新に失敗しました(ステータス %d)",
|
||||
"codex_refresh_login_failed": "Codexログインの更新に失敗しました: %w",
|
||||
"codex_refresh_token_required": "Codexリフレッシュトークンが必要です。'fabric --setup'を再実行してください。",
|
||||
"codex_replay_body_unavailable": "Codex再認証リトライのためにリクエストボディを再送できません",
|
||||
"codex_request_failed": "Codexリクエストに失敗しました: %w",
|
||||
"codex_request_failed_status": "Codexリクエストがステータス %d で失敗しました",
|
||||
"codex_starting_browser_login": "Codex用のブラウザベースOpenAIログインを開始しています。",
|
||||
"codex_token_exchange_failed": "Codexトークン交換に失敗しました: %w",
|
||||
"codex_token_persist_failed": "Codexログインの保存に失敗しました: %w",
|
||||
"codex_token_refresh_missing_access_token": "Codexトークンの更新がアクセストークンを返しませんでした。",
|
||||
"codex_usage_limit_reached": "Codex使用量制限に達しました",
|
||||
"command_completed_successfully": "コマンドが正常に完了しました",
|
||||
"compression_level_jpeg_webp": "JPEG/WebP形式の圧縮レベル0-100(デフォルト:未設定)",
|
||||
"config_file_not_found": "設定ファイルが見つかりません: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "カスタムパターン - カスタムパターン用のディレクトリを設定",
|
||||
"custom_patterns_warning_create_directory": "警告: カスタムパターンディレクトリ%sを作成できませんでした: %v\n",
|
||||
"db_error_loading_env_file": ".envファイルの読み込みエラー: %w",
|
||||
"db_error_updating_env_file": ".envファイルの更新エラー: %w",
|
||||
"defaults_model_context_length_question": "モデルのコンテキスト長を入力してください",
|
||||
"defaults_model_question": "デフォルトモデルのインデックスまたは名前を入力してください",
|
||||
"defaults_setup_description": "デフォルトのAIプロバイダーとモデル",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "DigitalOceanモデルリクエストがステータス%dで失敗しました: %s",
|
||||
"disable_openai_responses_api": "OpenAI Responses APIを無効化(デフォルト:false)",
|
||||
"disable_pattern_variable_replacement": "パターン変数の置換を無効化",
|
||||
"enable_web_search_tool": "サポートされているモデル(Anthropic、OpenAI、Gemini)でウェブ検索ツールを有効化",
|
||||
"enable_web_search_tool": "サポートされているモデル(Anthropic、OpenAI、Gemini、Grok)でウェブ検索ツールを有効化",
|
||||
"end_tag_thinking_sections": "思考セクションの終了タグ",
|
||||
"error_creating_audio_file": "音声ファイルの作成エラー: %v",
|
||||
"error_creating_file": "ファイルの作成エラー: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "無効なデータURLフォーマット",
|
||||
"ollama_invalid_http_timeout_using_default": "無効なHTTPタイムアウト '%s': %v、デフォルトを使用します",
|
||||
"ollama_invalid_num_ctx_in_request": "リクエストに無効な num_ctx があります: %v",
|
||||
"ollama_invalid_request_body": "無効なリクエスト本文",
|
||||
"ollama_no_content_from_upstream": "アップストリームの Fabric サーバーからコンテンツを受信しませんでした",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx が許可される最大値 %d を超えています",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx は数値である必要があります。無効な型を受け取りました",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "SSE 行が 1MB バッファー制限を超えています - データ行が大きすぎます",
|
||||
"ollama_upstream_non_2xx": "アップストリームの Fabric サーバーが非 2xx ステータス %d を返しました: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "アップストリームの Fabric サーバーが非 2xx ステータス %d を返し、ボディを読み取れませんでした: %v",
|
||||
"ollama_upstream_request_failed": "上流のFabricサーバーに到達できませんでした",
|
||||
"ollama_upstream_returned_status": "アップストリームの Fabric サーバーがステータス %d を返しました",
|
||||
"ollama_warning_no_content": "警告: アップストリームの Fabric サーバーからコンテンツを受信しませんでした",
|
||||
"ollama_warning_parse_variables": "警告: options.variables を JSON として解析できませんでした: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "画像を %s に保存できませんでした: %w",
|
||||
"openai_image_saved_to": "画像の保存先: %s",
|
||||
"openai_model_no_image_generation": "モデル '%s' は画像生成をサポートしていません。サポートされているモデル: %s",
|
||||
"openai_models_rate_limited": "プロバイダー %s からのモデル取得でレート制限を超過しました。%s 秒後に再試行してください",
|
||||
"openai_models_response_too_large": "プロバイダー %s からのモデルレスポンスが大きすぎます(>%d バイト)",
|
||||
"openai_unable_to_parse_models_response": "モデルレスポンスの解析に失敗しました; 生のレスポンス: %s",
|
||||
"openai_unexpected_status_code_read_error": "予期しないステータスコード: プロバイダー %s から %d (レスポンス本文の読み取りに失敗: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "動画メタデータを出力",
|
||||
"path_to_yaml_config": "YAML設定ファイルのパス",
|
||||
"pattern_not_found_list_available": "パターン '%s' が見つかりません。'fabric -l'で利用可能なパターンを確認してください",
|
||||
"pattern_invalid_name": "無効なパターン名: %q",
|
||||
"pattern_not_found_no_patterns": "パターン '%s' が見つかりません。\n\nパターンがインストールされていません!解決するには:\n • 'fabric --setup'を実行してパターンを設定・ダウンロード\n • または'fabric -U'を実行してパターンをダウンロード/更新",
|
||||
"pattern_variables_help": "パターン変数の値、例:-v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "リポジトリ %s をクローン中 (パス: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "URLに両方のIDが存在する場合、動画よりプレイリストを優先",
|
||||
"print_context": "コンテキストを出力",
|
||||
"print_current_version": "現在のバージョンを出力",
|
||||
"print_metadata_to_stderr": "メタデータ(入力/出力トークン)を stderr に出力",
|
||||
"print_pattern_contents": "指定したパターンの内容をターミナルに出力",
|
||||
"print_session": "セッションを出力",
|
||||
"register_new_extension": "設定ファイルパスから新しい拡張機能を登録",
|
||||
"remove_registered_extension": "名前で登録済み拡張機能を削除",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "コマンド完了時にデスクトップ通知を送信",
|
||||
"serve_fabric_api_ollama_endpoints": "ollamaエンドポイント付きのFabric REST APIを提供",
|
||||
"serve_fabric_rest_api": "Fabric REST APIを提供",
|
||||
"server_api_key_required": "APIキーなしで非ループバックアドレス %s での提供を拒否しました。--api-key または FABRIC_API_KEY を設定するか、127.0.0.1:8080 のようなループバックアドレスにバインドしてください",
|
||||
"server_chat_error": "エラー: %v",
|
||||
"server_error_marshaling_response": "レスポンスのシリアライズエラー: %v",
|
||||
"server_error_writing_response": "レスポンスの書き込みエラー: %v",
|
||||
"server_invalid_request_format": "無効なリクエスト形式: %v",
|
||||
"server_no_api_key_warning": "APIキー認証なしでREST APIサーバーを起動しています。セキュリティ上のリスクが生じる可能性があります。",
|
||||
"sessions_creating_new": "新しいセッションを作成中: %s\n",
|
||||
"set_debug_level": "デバッグレベルを設定(0=オフ、1=基本、2=詳細、3=トレース、4=wire)",
|
||||
"set_frequency_penalty": "頻度ペナルティを設定",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**番組**: %s",
|
||||
"spotify_title_label": "**タイトル**: %s",
|
||||
"spotify_total_episodes_label": "**エピソード合計**: %d",
|
||||
"spotify_url_help": "メタデータを取得してチャットに送信する Spotify のポッドキャストまたはエピソードの URL",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "思考セクションの開始タグ",
|
||||
"storage_error_delete": "%sを削除できませんでした: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "%sを保存できませんでした: %v",
|
||||
"storage_error_stat_entry": "エントリ%sの情報を取得できませんでした: %v",
|
||||
"storage_error_unmarshal": "%sをデシリアライズできませんでした: %s",
|
||||
"storage_invalid_name": "無効な名前: %q",
|
||||
"strategies_available_header": "利用可能な戦略:",
|
||||
"strategies_cloning_repository": "リポジトリ %s をクローン中 (パス: %s)...\\n",
|
||||
"strategies_download_success": "✅ 戦略を %s に正常にダウンロードしてインストールしました\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "戦略名 %q が戦略ディレクトリの外部に解決されます",
|
||||
"stream_help": "ストリーミング",
|
||||
"suppress_thinking_tags": "思考タグで囲まれたテキストを抑制",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "相対時間の数値が無効です: %q",
|
||||
"template_datetime_error_invalid_relative_format": "相対時間の形式が無効です",
|
||||
"template_datetime_error_invalid_unit": "時間単位が無効です: %q",
|
||||
"template_datetime_error_relative_requires_value": "相対時間には値が必要です",
|
||||
"template_datetime_error_unknown_operation": "datetime: 不明な操作 %q",
|
||||
"template_extension_error": "拡張機能%sエラー: %v",
|
||||
"template_file_error_expand_home_dir": "file: ホームディレクトリを展開できませんでした: %v",
|
||||
"template_file_error_invalid_line_count": "file: 無効な行数 %q",
|
||||
|
|
@ -631,7 +664,7 @@
|
|||
"template_file_log_cleaned_path": "File: 正規化後のパス %q",
|
||||
"template_file_log_exists_for_path": "File: パス %q の exists=%v",
|
||||
"template_file_log_modified_for_path": "File: パス %q の modified=%q",
|
||||
"template_file_log_operation_value": "File: operation=%q value=%q",
|
||||
"template_file_log_operation_value": "ファイル: operation=%q value=%q",
|
||||
"template_file_log_read_bytes": "File: %d バイトを読み取りました",
|
||||
"template_file_log_read_total_return_last": "File: 合計 %d 行を読み取り、最後の %d 行を返します",
|
||||
"template_file_log_reading_last_lines": "File: %q から最後の %d 行を読み取り中",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "必須変数が不足しています: %s",
|
||||
"template_plugin_error": "プラグイン%sエラー: %v",
|
||||
"template_processing_stuck": "テンプレート処理が停止 - 無限ループの可能性",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: 変数名が必要です",
|
||||
"template_sys_error_home": "ホームディレクトリの取得に失敗しました: %v",
|
||||
"template_sys_error_hostname": "ホスト名の取得に失敗しました: %v",
|
||||
"template_sys_error_pwd": "作業ディレクトリの取得に失敗しました: %v",
|
||||
"template_sys_error_unknown_operation": "sys: 不明な操作 %q",
|
||||
"template_sys_error_user": "現在のユーザーの取得に失敗しました: %v",
|
||||
"template_text_empty_input": "テキスト: 操作%qに対する入力が空です",
|
||||
"template_text_unknown_operation": "テキスト: 不明なテキスト操作%q (対応: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "不明なプラグイン名前空間: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "動画の詳細取得エラー: %v",
|
||||
"youtube_error_parsing_duration": "動画の長さ解析エラー: %v",
|
||||
"youtube_error_saving_csv": "動画のCSV保存エラー: %v",
|
||||
"youtube_extract_visual_data_help": "OCR と FFmpeg を使用して動画から視覚データを抽出",
|
||||
"youtube_failed_create_temp_dir": "一時ディレクトリの作成に失敗しました: %v",
|
||||
"youtube_failed_fetch_comments": "コメントの取得に失敗しました: %v",
|
||||
"youtube_failed_get_stream_url": "yt-dlp 経由でストリーム URL を取得できませんでした: %v",
|
||||
"youtube_failed_parse_http_stream_url": "yt-dlp の出力から有効な HTTP ストリーム URL を解析できませんでした",
|
||||
"youtube_failed_walk_directory": "ディレクトリの走査に失敗しました: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "ffmpeg によるフレーム抽出に失敗しました: %v, 出力: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "視覚抽出には ffmpeg が必要ですが、PATH に見つかりません",
|
||||
"youtube_invalid_duration_string": "無効な長さ文字列: %s",
|
||||
"youtube_invalid_seconds_format": "無効な秒形式 %q: %w",
|
||||
"youtube_invalid_timestamp_format": "無効なタイムスタンプ形式: %s",
|
||||
"youtube_invalid_url": "無効なYouTube URL、動画またはプレイリストIDを取得できません: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "無効なyt-dlp引数: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "動画の視覚フレーム内に判読可能なテキストが見つかりませんでした",
|
||||
"youtube_no_transcript_content": "VTTファイルにトランスクリプトコンテンツが見つかりません",
|
||||
"youtube_no_url_provided": "YouTube URLが提供されていません",
|
||||
"youtube_no_video_found_with_id": "IDの動画が見つかりません: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "プレイリストが %s に保存されました",
|
||||
"youtube_rate_limit_exceeded": "YouTubeのレート制限を超えました。後でもう一度試すか、'--sleep-requests 1'のような異なるyt-dlp引数を使用してリクエストを遅くしてください。",
|
||||
"youtube_setup_description": "YouTube - 動画の転写(yt-dlp経由)とコメント/メタデータ(YouTube API経由)を取得",
|
||||
"youtube_tesseract_frame_failed": "フレーム %d で tesseract が失敗しました: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "視覚抽出には tesseract が必要ですが、PATH に見つかりません",
|
||||
"youtube_url_help": "YouTube動画またはプレイリスト\"URL\"から転写、コメントを取得してチャットに送信、またはコンソールに出力して出力ファイルに保存",
|
||||
"youtube_url_is_playlist_not_video": "URLはプレイリストであり、動画ではありません",
|
||||
"youtube_video_id_title_header": "動画ID: タイトル",
|
||||
"youtube_visual_fps_help": "シーン検出の代わりに、1 秒あたりの特定フレーム数を抽出",
|
||||
"youtube_visual_frame_cue": "[視覚フレームキュー]",
|
||||
"youtube_visual_sensitivity_help": "FFmpeg のシーン検出の許容度 (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "PATHにyt-dlpが見つかりません。YouTubeトランスクリプト機能を使用するにはyt-dlpをインストールしてください",
|
||||
"youtube_ytdlp_required_visual_extraction": "視覚抽出には yt-dlp が必要ですが、PATH に見つかりません",
|
||||
"youtube_ytdlp_stderr_error": "yt-dlp stderrの読み取りエラー"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Wprowadź swój AWS Access Key ID (pozostaw puste, aby użyć łańcucha uwierzytelniania AWS)",
|
||||
"bedrock_aws_region_label": "Region AWS",
|
||||
"bedrock_aws_secret_key_label": "Wprowadź swój AWS Secret Access Key (pozostaw puste, aby użyć łańcucha uwierzytelniania AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "klient Bedrock nie jest zainicjalizowany — uruchom 'fabric --setup', aby skonfigurować",
|
||||
"bedrock_converse_failed": "bedrock converse nie powiodło się dla modelu %s: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream nie powiodło się dla modelu %s: %w",
|
||||
"bedrock_empty_response_content": "pusta zawartość odpowiedzi",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels Bedrock nie powiodło się, używam statycznej listy zapasowej",
|
||||
"bedrock_panic_sendstream": "panika w SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Klucz API Bedrock / token ABSK (zalecane — taki sam jak w Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Wprowadź 1 lub 2",
|
||||
"bedrock_setup_choose_auth_method": " Wybierz metodę uwierzytelniania:",
|
||||
"bedrock_setup_choose_model": " Wybierz model domyślny (identyfikatory bez prefiksu działają w każdym regionie; us./eu./ap. są specyficzne dla regionu):",
|
||||
"bedrock_setup_choose_region": " Wybierz region AWS:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "nieprawidłowy wybór: %s (wprowadź 1 lub 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Wprowadź identyfikator modelu (np. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Wprowadź inny identyfikator modelu",
|
||||
"bedrock_setup_model_prompt": "Wprowadź numer modelu lub 0, aby podać własny",
|
||||
"bedrock_setup_region_custom_prompt": "Wprowadź własny region AWS (np. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Wprowadź inny region",
|
||||
"bedrock_setup_region_prompt": "Wprowadź numer regionu lub 0, aby podać własny",
|
||||
"bedrock_setup_selected_model": " ✓ Wybrany model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Użycie: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "nie można załadować konfiguracji AWS: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "nie można załadować konfiguracji AWS dla regionu %s: %w",
|
||||
"bedrock_unexpected_content_block_type": "nieoczekiwany typ bloku zawartości: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Wybierz wzorzec spośród dostępnych wzorców",
|
||||
"choose_session_from_available": "Wybierz sesję spośród dostępnych sesji",
|
||||
"choose_strategy_from_available": "Wybierz strategię spośród dostępnych strategii",
|
||||
"codex_api_base_url_question": "Podaj bazowy URL API Codex",
|
||||
"codex_auth_base_url_invalid": "Nieprawidłowy bazowy URL uwierzytelniania Codex: %w",
|
||||
"codex_auth_base_url_question": "Podaj bazowy URL OAuth Codex",
|
||||
"codex_browser_open_fallback": "Jeśli przeglądarka się nie otworzyła, przejdź pod ten URL, aby się uwierzytelnić:",
|
||||
"codex_decode_models_response_failed": "Nie udało się zdekodować odpowiedzi modeli Codex: %w",
|
||||
"codex_decode_refresh_response_failed": "Nie udało się zdekodować odpowiedzi odświeżonego tokenu Codex: %w",
|
||||
"codex_decode_token_response_failed": "Nie udało się zdekodować odpowiedzi wymiany tokenu Codex: %w",
|
||||
"codex_image_file_not_supported": "Dostawca Codex nie obsługuje --image-file. Użyj załącznika graficznego.",
|
||||
"codex_login_account_changed": "Logowanie Codex jest powiązane z innym kontem ChatGPT niż zapisana konfiguracja. Uruchom ponownie 'fabric --setup'.",
|
||||
"codex_login_completed": "Logowanie Codex zakończone",
|
||||
"codex_login_failed": "Logowanie Codex nie powiodło się: %s",
|
||||
"codex_login_invalid": "Twoje logowanie Codex nie jest już ważne. Uruchom ponownie 'fabric --setup'.",
|
||||
"codex_login_missing_account_claim": "Logowanie Codex nie zawierało identyfikatora konta ChatGPT. Ten stan logowania nie jest obsługiwany.",
|
||||
"codex_login_missing_auth_code": "Logowanie Codex nie zwróciło kodu autoryzacji.",
|
||||
"codex_login_missing_tokens": "Logowanie Codex nie zwróciło wymaganych tokenów dostępu i odświeżania.",
|
||||
"codex_login_refresh_failed": "Nie udało się odświeżyć logowania Codex. Uruchom ponownie 'fabric --setup'.",
|
||||
"codex_login_return_to_fabric": "Wróć do Fabric.",
|
||||
"codex_login_revoked": "Logowanie Codex wygasło lub zostało unieważnione. Uruchom ponownie 'fabric --setup'.",
|
||||
"codex_login_server_stopped": "Serwer zwrotny logowania Codex zatrzymał się przed zakończeniem uwierzytelniania.",
|
||||
"codex_login_state_mismatch": "Nie można zweryfikować logowania Codex, ponieważ stan OAuth nie był zgodny.",
|
||||
"codex_login_timed_out": "Upłynął limit czasu logowania Codex przed zakończeniem uwierzytelniania.",
|
||||
"codex_oauth_missing_auth_code": "Brak kodu autoryzacji",
|
||||
"codex_oauth_random_state_failed": "Nie udało się wygenerować bezpiecznego losowego stanu OAuth: %w",
|
||||
"codex_oauth_server_start_failed": "Nie udało się uruchomić lokalnego serwera zwrotnego OAuth: %w",
|
||||
"codex_oauth_state_mismatch": "Stan nie jest zgodny",
|
||||
"codex_provider_error": "błąd dostawcy Codex (status %d): %s",
|
||||
"codex_refresh_failed_status": "Nie udało się odświeżyć logowania Codex (status %d)",
|
||||
"codex_refresh_login_failed": "Nie udało się odświeżyć logowania Codex: %w",
|
||||
"codex_refresh_token_required": "Token odświeżania Codex jest wymagany. Uruchom ponownie 'fabric --setup'.",
|
||||
"codex_replay_body_unavailable": "Treść żądania nie może być odtworzona dla ponownej próby uwierzytelnienia Codex",
|
||||
"codex_request_failed": "Żądanie Codex nie powiodło się: %w",
|
||||
"codex_request_failed_status": "Żądanie Codex nie powiodło się ze statusem %d",
|
||||
"codex_starting_browser_login": "Uruchamianie logowania OpenAI przez przeglądarkę dla Codex.",
|
||||
"codex_token_exchange_failed": "Wymiana tokenu Codex nie powiodła się: %w",
|
||||
"codex_token_persist_failed": "nie udało się zapisać logowania Codex: %w",
|
||||
"codex_token_refresh_missing_access_token": "Odświeżenie tokenu Codex nie zwróciło tokenu dostępu.",
|
||||
"codex_usage_limit_reached": "Osiągnięto limit użycia Codex",
|
||||
"command_completed_successfully": "Polecenie zakończone pomyślnie",
|
||||
"compression_level_jpeg_webp": "Poziom kompresji 0-100 dla formatów JPEG/WebP (domyślnie: nie ustawiony)",
|
||||
"config_file_not_found": "plik konfiguracyjny nie został znaleziony: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Niestandardowe wzorce - Ustaw katalog dla swoich niestandardowych wzorców",
|
||||
"custom_patterns_warning_create_directory": "Ostrzeżenie: Nie można utworzyć katalogu niestandardowych wzorców %s: %v\n",
|
||||
"db_error_loading_env_file": "błąd podczas ładowania pliku .env: %w",
|
||||
"db_error_updating_env_file": "błąd podczas aktualizacji pliku .env: %w",
|
||||
"defaults_model_context_length_question": "Podaj długość kontekstu modelu",
|
||||
"defaults_model_question": "Podaj indeks lub nazwę domyślnego modelu",
|
||||
"defaults_setup_description": "Domyślny dostawca AI i model",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "Żądanie modeli DigitalOcean nie powiodło się ze statusem %d: %s",
|
||||
"disable_openai_responses_api": "Wyłącz API odpowiedzi OpenAI (domyślnie: false)",
|
||||
"disable_pattern_variable_replacement": "Wyłącz zastępowanie zmiennych wzorców",
|
||||
"enable_web_search_tool": "Włącz narzędzie wyszukiwania internetowego dla obsługiwanych modeli (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Włącz narzędzie wyszukiwania internetowego dla obsługiwanych modeli (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Tag końcowy dla sekcji myślenia",
|
||||
"error_creating_audio_file": "błąd podczas tworzenia pliku audio: %v",
|
||||
"error_creating_file": "błąd podczas tworzenia pliku: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "nieprawidłowy format data URL",
|
||||
"ollama_invalid_http_timeout_using_default": "nieprawidłowy limit czasu HTTP '%s': %v, używam domyślnego",
|
||||
"ollama_invalid_num_ctx_in_request": "nieprawidłowa wartość num_ctx w żądaniu: %v",
|
||||
"ollama_invalid_request_body": "nieprawidłowa treść żądania",
|
||||
"ollama_no_content_from_upstream": "nie odebrano zawartości z upstream serwera fabric",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx przekracza maksymalną dozwoloną wartość %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx musi być liczbą, podano nieprawidłowy typ",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "Linia SSE przekracza limit bufora 1MB - linia danych zbyt duża",
|
||||
"ollama_upstream_non_2xx": "Upstream serwer fabric zwrócił status inny niż 2xx %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Upstream serwer fabric zwrócił status inny niż 2xx %d i nie można odczytać treści: %v",
|
||||
"ollama_upstream_request_failed": "nie można połączyć się z serwerem nadrzędnym Fabric",
|
||||
"ollama_upstream_returned_status": "upstream serwer fabric zwrócił status %d",
|
||||
"ollama_warning_no_content": "Ostrzeżenie: nie odebrano zawartości z upstream serwera fabric",
|
||||
"ollama_warning_parse_variables": "Ostrzeżenie: nie udało się przetworzyć options.variables jako JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "nie udało się zapisać obrazu do %s: %w",
|
||||
"openai_image_saved_to": "Obraz zapisano do: %s",
|
||||
"openai_model_no_image_generation": "model '%s' nie obsługuje generowania obrazów. Obsługiwane modele: %s",
|
||||
"openai_models_rate_limited": "przekroczono limit żądań podczas pobierania modeli od dostawcy %s; spróbuj ponownie za %s sekund",
|
||||
"openai_models_response_too_large": "odpowiedź z modelami zbyt duża od dostawcy %s (>%d bajtów)",
|
||||
"openai_unable_to_parse_models_response": "nie można przetworzyć odpowiedzi z modelami; surowa odpowiedź: %s",
|
||||
"openai_unexpected_status_code_read_error": "nieoczekiwany kod statusu: %d od dostawcy %s (nie udało się odczytać treści odpowiedzi: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Wyprowadź metadane wideo",
|
||||
"path_to_yaml_config": "Ścieżka do pliku konfiguracyjnego YAML",
|
||||
"pattern_not_found_list_available": "wzorzec '%s' nie został znaleziony. Uruchom 'fabric -l', aby zobaczyć dostępne wzorce",
|
||||
"pattern_invalid_name": "nieprawidłowa nazwa wzorca: %q",
|
||||
"pattern_not_found_no_patterns": "wzorzec '%s' nie został znaleziony.\n\nNie zainstalowano żadnych wzorców! Aby to naprawić:\n • Uruchom 'fabric --setup', aby skonfigurować i pobrać wzorce\n • Lub uruchom 'fabric -U', aby bezpośrednio pobrać/zaktualizować wzorce",
|
||||
"pattern_variables_help": "Wartości dla zmiennych wzorców, np. -v=#role:ekspert -v=#points:30",
|
||||
"patterns_cloning_repository": "Klonowanie repozytorium %s (ścieżka: %s)...\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Preferuj playlistę nad filmem, jeśli oba identyfikatory są obecne w URL",
|
||||
"print_context": "Wydrukuj kontekst",
|
||||
"print_current_version": "Wydrukuj bieżącą wersję",
|
||||
"print_metadata_to_stderr": "Wypisz metadane (tokeny wejściowe/wyjściowe) na stderr",
|
||||
"print_pattern_contents": "Wypisz zawartość wskazanego wzorca w terminalu",
|
||||
"print_session": "Wydrukuj sesję",
|
||||
"register_new_extension": "Zarejestruj nowe rozszerzenie z pliku konfiguracyjnego",
|
||||
"remove_registered_extension": "Usuń zarejestrowane rozszerzenie według nazwy",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Wyślij powiadomienie pulpitu po zakończeniu polecenia",
|
||||
"serve_fabric_api_ollama_endpoints": "Uruchom fabric Rest API z endpointami ollama",
|
||||
"serve_fabric_rest_api": "Uruchom fabric Rest API",
|
||||
"server_api_key_required": "odmowa udostępniania na adresie %s spoza pętli zwrotnej bez klucza API: ustaw --api-key lub FABRIC_API_KEY, albo powiąż adres pętli zwrotnej, np. 127.0.0.1:8080",
|
||||
"server_chat_error": "Błąd: %v",
|
||||
"server_error_marshaling_response": "błąd podczas serializacji odpowiedzi: %v",
|
||||
"server_error_writing_response": "błąd podczas zapisywania odpowiedzi: %v",
|
||||
"server_invalid_request_format": "nieprawidłowy format żądania: %v",
|
||||
"server_no_api_key_warning": "Uruchamianie serwera REST API bez uwierzytelniania kluczem API. Może to stwarzać zagrożenia bezpieczeństwa.",
|
||||
"sessions_creating_new": "Tworzenie nowej sesji: %s\n",
|
||||
"set_debug_level": "Ustaw poziom debugowania (0=wyłączone, 1=podstawowe, 2=szczegółowe, 3=śledzenie, 4=surowe)",
|
||||
"set_frequency_penalty": "Ustaw karę częstotliwości",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Program**: %s",
|
||||
"spotify_title_label": "**Tytuł**: %s",
|
||||
"spotify_total_episodes_label": "**Łączna liczba odcinków**: %d",
|
||||
"spotify_url_help": "URL podcastu lub odcinka Spotify do pobrania metadanych i wysłania do czatu",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Tag początkowy dla sekcji myślenia",
|
||||
"storage_error_delete": "nie można usunąć %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "nie można zapisać %s: %v",
|
||||
"storage_error_stat_entry": "nie można pobrać informacji o wpisie %s: %v",
|
||||
"storage_error_unmarshal": "nie można deserializować %s: %s",
|
||||
"storage_invalid_name": "nieprawidłowa nazwa: %q",
|
||||
"strategies_available_header": "Dostępne strategie:",
|
||||
"strategies_cloning_repository": "Klonowanie repozytorium %s (ścieżka: %s)...\n",
|
||||
"strategies_download_success": "✅ Pomyślnie pobrano i zainstalowano strategie w %s\n",
|
||||
|
|
@ -631,7 +664,7 @@
|
|||
"template_file_log_cleaned_path": "File: oczyszczona ścieżka %q",
|
||||
"template_file_log_exists_for_path": "File: exists=%v dla ścieżki %q",
|
||||
"template_file_log_modified_for_path": "File: modified=%q dla ścieżki %q",
|
||||
"template_file_log_operation_value": "File: operation=%q value=%q",
|
||||
"template_file_log_operation_value": "Plik: operation=%q value=%q",
|
||||
"template_file_log_read_bytes": "File: odczytano %d bajtów",
|
||||
"template_file_log_read_total_return_last": "File: odczytano łącznie %d linii, zwracam ostatnie %d",
|
||||
"template_file_log_reading_last_lines": "File: odczytywanie ostatnich %d linii z %q",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "błąd podczas pobierania szczegółów wideo: %v",
|
||||
"youtube_error_parsing_duration": "błąd podczas przetwarzania czasu trwania wideo: %v",
|
||||
"youtube_error_saving_csv": "błąd podczas zapisywania filmów do CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Wyodrębnij dane wizualne z filmu przy użyciu OCR i FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "nie udało się utworzyć katalogu tymczasowego: %v",
|
||||
"youtube_failed_fetch_comments": "nie udało się pobrać komentarzy: %v",
|
||||
"youtube_failed_get_stream_url": "nie udało się pobrać adresu URL strumienia przez yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "nie udało się przetworzyć prawidłowego adresu URL strumienia HTTP z danych wyjściowych yt-dlp",
|
||||
"youtube_failed_walk_directory": "nie udało się przejść przez katalog: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "ekstrakcja klatek przez ffmpeg nie powiodła się: %v, wyjście: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg jest wymagany do ekstrakcji wizualnej, ale nie został znaleziony w PATH",
|
||||
"youtube_invalid_duration_string": "nieprawidłowy ciąg czasu trwania: %s",
|
||||
"youtube_invalid_seconds_format": "nieprawidłowy format sekund %q: %w",
|
||||
"youtube_invalid_timestamp_format": "nieprawidłowy format znacznika czasu: %s",
|
||||
"youtube_invalid_url": "nieprawidłowy URL YouTube, nie można uzyskać identyfikatora wideo lub playlisty: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "nieprawidłowe argumenty yt-dlp: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "nie znaleziono czytelnego tekstu w wizualnych klatkach wideo",
|
||||
"youtube_no_transcript_content": "nie znaleziono zawartości transkrypcji w pliku VTT",
|
||||
"youtube_no_url_provided": "Nie podano URL YouTube",
|
||||
"youtube_no_video_found_with_id": "nie znaleziono wideo o ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Playlista zapisana do %s",
|
||||
"youtube_rate_limit_exceeded": "Przekroczono limit żądań YouTube. Spróbuj ponownie później lub użyj innych argumentów yt-dlp, np. '--sleep-requests 1', aby spowolnić żądania.",
|
||||
"youtube_setup_description": "YouTube - do pobierania transkrypcji wideo (przez yt-dlp) i komentarzy/metadanych (przez API YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract nie powiódł się dla klatki %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract jest wymagany do ekstrakcji wizualnej, ale nie został znaleziony w PATH",
|
||||
"youtube_url_help": "URL wideo lub playlisty YouTube do pobrania transkrypcji, komentarzy i wysłania do czatu lub wypisania na konsolę i zapisania w pliku wyjściowym",
|
||||
"youtube_url_is_playlist_not_video": "URL jest playlistą, nie filmem",
|
||||
"youtube_video_id_title_header": "ID wideo: Tytuł",
|
||||
"youtube_visual_fps_help": "Wyodrębnij określoną liczbę klatek na sekundę zamiast używać wykrywania scen",
|
||||
"youtube_visual_frame_cue": "[ZNACZNIK KLATKI WIZUALNEJ]",
|
||||
"youtube_visual_sensitivity_help": "Czułość wykrywania scen FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "nie znaleziono yt-dlp w PATH. Zainstaluj yt-dlp, aby korzystać z funkcji transkrypcji YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp jest wymagany do ekstrakcji wizualnej, ale nie został znaleziony w PATH",
|
||||
"youtube_ytdlp_stderr_error": "błąd podczas odczytu stderr yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Digite seu AWS Access Key ID (deixe vazio para usar a cadeia de credenciais AWS)",
|
||||
"bedrock_aws_region_label": "Regiao AWS",
|
||||
"bedrock_aws_secret_key_label": "Digite seu AWS Secret Access Key (deixe vazio para usar a cadeia de credenciais AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "cliente Bedrock não inicializado — execute 'fabric --setup' para configurar",
|
||||
"bedrock_converse_failed": "bedrock converse falhou para o modelo %s: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream falhou para o modelo %s: %w",
|
||||
"bedrock_empty_response_content": "conteudo de resposta vazio",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels do Bedrock falhou, usando lista estática de fallback",
|
||||
"bedrock_panic_sendstream": "panico no SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Chave API Bedrock / token ABSK (recomendado — a mesma usada pelo Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Digite 1 ou 2",
|
||||
"bedrock_setup_choose_auth_method": " Escolha o método de autenticação:",
|
||||
"bedrock_setup_choose_model": " Escolha o modelo padrão (IDs sem prefixo funcionam em qualquer região; us./eu./ap. são específicos de região):",
|
||||
"bedrock_setup_choose_region": " Escolha a região AWS:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "seleção inválida: %s (digite 1 ou 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Digite o ID do modelo (ex. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Digitar um ID de modelo diferente",
|
||||
"bedrock_setup_model_prompt": "Digite o número do modelo ou 0 para informar o seu",
|
||||
"bedrock_setup_region_custom_prompt": "Digite uma região AWS personalizada (ex. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Digitar uma região diferente",
|
||||
"bedrock_setup_region_prompt": "Digite o número da região ou 0 para personalizar",
|
||||
"bedrock_setup_selected_model": " ✓ Modelo selecionado: %s",
|
||||
"bedrock_setup_use_with": " ℹ Uso: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "nao foi possivel carregar a configuracao AWS: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "nao foi possivel carregar a configuracao AWS com regiao %s: %w",
|
||||
"bedrock_unexpected_content_block_type": "tipo de bloco de conteudo inesperado: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Escolha um padrão entre os padrões disponíveis",
|
||||
"choose_session_from_available": "Escolha uma sessão das sessões disponíveis",
|
||||
"choose_strategy_from_available": "Escolher uma estratégia das estratégias disponíveis",
|
||||
"codex_api_base_url_question": "Insira a URL base da API do Codex",
|
||||
"codex_auth_base_url_invalid": "URL base de autenticação do Codex inválida: %w",
|
||||
"codex_auth_base_url_question": "Insira a URL base de OAuth do Codex",
|
||||
"codex_browser_open_fallback": "Se o navegador não abriu, navegue até esta URL para se autenticar:",
|
||||
"codex_decode_models_response_failed": "Falha ao decodificar a resposta de modelos do Codex: %w",
|
||||
"codex_decode_refresh_response_failed": "Falha ao decodificar a resposta do token atualizado do Codex: %w",
|
||||
"codex_decode_token_response_failed": "Falha ao decodificar a resposta de troca de token do Codex: %w",
|
||||
"codex_image_file_not_supported": "O provedor Codex não suporta --image-file. Use um anexo de imagem.",
|
||||
"codex_login_account_changed": "O login do Codex está vinculado a uma conta ChatGPT diferente da configuração salva. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_completed": "Login do Codex concluído",
|
||||
"codex_login_failed": "Falha no login do Codex: %s",
|
||||
"codex_login_invalid": "Seu login do Codex não é mais válido. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_missing_account_claim": "O login do Codex não incluiu um ID de conta ChatGPT. Este estado de login não é suportado.",
|
||||
"codex_login_missing_auth_code": "O login do Codex não retornou um código de autorização.",
|
||||
"codex_login_missing_tokens": "O login do Codex não retornou os tokens de acesso e atualização necessários.",
|
||||
"codex_login_refresh_failed": "Não foi possível atualizar o login do Codex. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_return_to_fabric": "Voltar para o Fabric.",
|
||||
"codex_login_revoked": "O login do Codex expirou ou foi revogado. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_server_stopped": "O servidor de callback do login do Codex parou antes da autenticação ser concluída.",
|
||||
"codex_login_state_mismatch": "O login do Codex não pôde ser verificado porque o estado OAuth não correspondeu.",
|
||||
"codex_login_timed_out": "O login do Codex expirou antes da autenticação ser concluída.",
|
||||
"codex_oauth_missing_auth_code": "Código de autorização ausente",
|
||||
"codex_oauth_random_state_failed": "Falha ao gerar estado OAuth aleatório seguro: %w",
|
||||
"codex_oauth_server_start_failed": "Falha ao iniciar o servidor local de callback OAuth: %w",
|
||||
"codex_oauth_state_mismatch": "O estado não corresponde",
|
||||
"codex_provider_error": "erro do fornecedor Codex (status %d): %s",
|
||||
"codex_refresh_failed_status": "Falha ao atualizar o login do Codex (status %d)",
|
||||
"codex_refresh_login_failed": "Falha ao atualizar o login do Codex: %w",
|
||||
"codex_refresh_token_required": "O token de atualização do Codex é obrigatório. Execute 'fabric --setup' novamente.",
|
||||
"codex_replay_body_unavailable": "O corpo da requisição não pode ser reproduzido para a tentativa de reautenticação do Codex",
|
||||
"codex_request_failed": "A requisição do Codex falhou: %w",
|
||||
"codex_request_failed_status": "A requisição do Codex falhou com status %d",
|
||||
"codex_starting_browser_login": "Iniciando login OpenAI baseado em navegador para o Codex.",
|
||||
"codex_token_exchange_failed": "A troca de token do Codex falhou: %w",
|
||||
"codex_token_persist_failed": "falha ao persistir o login do Codex: %w",
|
||||
"codex_token_refresh_missing_access_token": "A atualização do token do Codex não retornou um token de acesso.",
|
||||
"codex_usage_limit_reached": "Limite de uso do Codex atingido",
|
||||
"command_completed_successfully": "Comando concluído com sucesso",
|
||||
"compression_level_jpeg_webp": "Nível de compressão 0-100 para formatos JPEG/WebP (padrão: não definido)",
|
||||
"config_file_not_found": "arquivo de configuração não encontrado: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Padrões personalizados - Definir diretório para seus padrões personalizados",
|
||||
"custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n",
|
||||
"db_error_loading_env_file": "erro ao carregar o arquivo .env: %w",
|
||||
"db_error_updating_env_file": "erro ao atualizar o arquivo .env: %w",
|
||||
"defaults_model_context_length_question": "Informe o comprimento do contexto do modelo",
|
||||
"defaults_model_question": "Informe o índice ou o nome do seu modelo padrão",
|
||||
"defaults_setup_description": "Provedor e modelo de IA padrão",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "requisição de modelos do DigitalOcean falhou com status %d: %s",
|
||||
"disable_openai_responses_api": "Desabilitar API OpenAI Responses (padrão: false)",
|
||||
"disable_pattern_variable_replacement": "Desabilitar substituição de variáveis de padrão",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de busca web para modelos suportados (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de busca web para modelos suportados (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Tag final para seções de pensamento",
|
||||
"error_creating_audio_file": "erro ao criar arquivo de áudio: %v",
|
||||
"error_creating_file": "erro ao criar arquivo: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "formato de URL de dados inválido",
|
||||
"ollama_invalid_http_timeout_using_default": "Tempo limite HTTP inválido '%s': %v, usando o padrão",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx inválido na requisição: %v",
|
||||
"ollama_invalid_request_body": "corpo de solicitação inválido",
|
||||
"ollama_no_content_from_upstream": "nenhum conteúdo recebido do servidor Fabric upstream",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx excede o valor máximo permitido de %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx deve ser um número, recebeu tipo inválido",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "Linha SSE excede limite de buffer de 1MB - linha de dados muito grande",
|
||||
"ollama_upstream_non_2xx": "Servidor Fabric upstream retornou status não-2xx %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Servidor Fabric upstream retornou status não-2xx %d e o corpo não pôde ser lido: %v",
|
||||
"ollama_upstream_request_failed": "falha ao conectar ao servidor Fabric upstream",
|
||||
"ollama_upstream_returned_status": "servidor Fabric upstream retornou status %d",
|
||||
"ollama_warning_no_content": "Aviso: nenhum conteúdo recebido do servidor Fabric upstream",
|
||||
"ollama_warning_parse_variables": "Aviso: falha ao analisar options.variables como JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "falha ao salvar a imagem em %s: %w",
|
||||
"openai_image_saved_to": "Imagem salva em: %s",
|
||||
"openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s",
|
||||
"openai_models_rate_limited": "limite de taxa excedido ao buscar modelos do provedor %s; tente novamente após %s segundos",
|
||||
"openai_models_response_too_large": "resposta de modelos muito grande do provedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de status inesperado: %d do provedor %s (falha ao ler corpo da resposta: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Exibir metadados do vídeo",
|
||||
"path_to_yaml_config": "Caminho para arquivo de configuração YAML",
|
||||
"pattern_not_found_list_available": "padrão '%s' não encontrado. Execute 'fabric -l' para ver os padrões disponíveis",
|
||||
"pattern_invalid_name": "nome de padrão inválido: %q",
|
||||
"pattern_not_found_no_patterns": "padrão '%s' não encontrado.\n\nNenhum padrão instalado! Para resolver:\n • Execute 'fabric --setup' para configurar e baixar padrões\n • Ou execute 'fabric -U' para baixar/atualizar padrões diretamente",
|
||||
"pattern_variables_help": "Valores para variáveis do padrão, ex. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Clonando repositório %s (caminho: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Preferir playlist ao vídeo se ambos os IDs estiverem presentes na URL",
|
||||
"print_context": "Imprimir contexto",
|
||||
"print_current_version": "Imprimir versão atual",
|
||||
"print_metadata_to_stderr": "Imprimir metadados (tokens de entrada/saída) no stderr",
|
||||
"print_pattern_contents": "Imprimir o conteúdo do padrão indicado no terminal",
|
||||
"print_session": "Imprimir sessão",
|
||||
"register_new_extension": "Registrar uma nova extensão do caminho do arquivo de configuração",
|
||||
"remove_registered_extension": "Remover uma extensão registrada por nome",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Enviar notificação desktop quando o comando for concluído",
|
||||
"serve_fabric_api_ollama_endpoints": "Servir a API REST do Fabric com endpoints ollama",
|
||||
"serve_fabric_rest_api": "Servir a API REST do Fabric",
|
||||
"server_api_key_required": "recusando servir no endereço não loopback %s sem chave de API: defina --api-key ou FABRIC_API_KEY, ou vincule um endereço loopback como 127.0.0.1:8080",
|
||||
"server_chat_error": "Erro: %v",
|
||||
"server_error_marshaling_response": "erro ao serializar resposta: %v",
|
||||
"server_error_writing_response": "erro ao escrever resposta: %v",
|
||||
"server_invalid_request_format": "formato de solicitação inválido: %v",
|
||||
"server_no_api_key_warning": "Iniciando o servidor da API REST sem autenticação por chave de API. Isso pode representar riscos de segurança.",
|
||||
"sessions_creating_new": "Criando nova sessão: %s\n",
|
||||
"set_debug_level": "Definir nível de debug (0=desligado, 1=básico, 2=detalhado, 3=rastreamento, 4=wire)",
|
||||
"set_frequency_penalty": "Definir penalidade de frequência",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Programa**: %s",
|
||||
"spotify_title_label": "**Título**: %s",
|
||||
"spotify_total_episodes_label": "**Total de episódios**: %d",
|
||||
"spotify_url_help": "URL de podcast ou episódio do Spotify para obter metadados e enviar ao chat",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Tag inicial para seções de pensamento",
|
||||
"storage_error_delete": "Não foi possível excluir %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "Não foi possível salvar %s: %v",
|
||||
"storage_error_stat_entry": "Não foi possível obter informações da entrada %s: %v",
|
||||
"storage_error_unmarshal": "Não foi possível desserializar %s: %s",
|
||||
"storage_invalid_name": "nome inválido: %q",
|
||||
"strategies_available_header": "Estratégias disponíveis:",
|
||||
"strategies_cloning_repository": "Clonando repositório %s (caminho: %s)...\\n",
|
||||
"strategies_download_success": "✅ Estratégias baixadas e instaladas com sucesso em %s\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "o nome da estratégia %q resolve fora do diretório de estratégias",
|
||||
"stream_help": "Streaming",
|
||||
"suppress_thinking_tags": "Suprimir texto contido em tags de pensamento",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "número inválido no tempo relativo: %q",
|
||||
"template_datetime_error_invalid_relative_format": "formato de tempo relativo inválido",
|
||||
"template_datetime_error_invalid_unit": "unidade de tempo inválida: %q",
|
||||
"template_datetime_error_relative_requires_value": "o tempo relativo requer um valor",
|
||||
"template_datetime_error_unknown_operation": "datetime: operação desconhecida %q",
|
||||
"template_extension_error": "Erro na extensão %s: %v",
|
||||
"template_file_error_expand_home_dir": "arquivo: não foi possível expandir o diretório home: %v",
|
||||
"template_file_error_invalid_line_count": "arquivo: contagem de linhas inválida %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Variável obrigatória ausente: %s",
|
||||
"template_plugin_error": "Erro no plugin %s: %v",
|
||||
"template_processing_stuck": "Processamento do modelo travado - possível loop infinito",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: nome da variável obrigatório",
|
||||
"template_sys_error_home": "falha ao obter o diretório home: %v",
|
||||
"template_sys_error_hostname": "falha ao obter o nome do host: %v",
|
||||
"template_sys_error_pwd": "falha ao obter o diretório de trabalho: %v",
|
||||
"template_sys_error_unknown_operation": "sys: operação desconhecida %q",
|
||||
"template_sys_error_user": "falha ao obter o usuário atual: %v",
|
||||
"template_text_empty_input": "Texto: entrada vazia para a operação %q",
|
||||
"template_text_unknown_operation": "Texto: operação de texto desconhecida %q (suportadas: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Namespace de plugin desconhecido: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "erro ao obter detalhes do vídeo: %v",
|
||||
"youtube_error_parsing_duration": "erro ao analisar a duração do vídeo: %v",
|
||||
"youtube_error_saving_csv": "erro ao salvar vídeos em CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Extrair dados visuais do vídeo usando OCR e FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "falha ao criar diretório temporário: %v",
|
||||
"youtube_failed_fetch_comments": "Falha ao buscar comentários: %v",
|
||||
"youtube_failed_get_stream_url": "falha ao obter a URL do stream via yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "falha ao analisar uma URL HTTP válida de stream da saída do yt-dlp",
|
||||
"youtube_failed_walk_directory": "falha ao percorrer o diretório: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "extração de quadros com ffmpeg falhou: %v, saída: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_invalid_duration_string": "string de duração inválida: %s",
|
||||
"youtube_invalid_seconds_format": "formato de segundos inválido %q: %w",
|
||||
"youtube_invalid_timestamp_format": "formato de timestamp inválido: %s",
|
||||
"youtube_invalid_url": "URL do YouTube inválida, não é possível obter o ID do vídeo ou da playlist: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "argumentos do yt-dlp inválidos: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "nenhum texto legível encontrado nos quadros visuais do vídeo",
|
||||
"youtube_no_transcript_content": "nenhum conteúdo de transcrição encontrado no arquivo VTT",
|
||||
"youtube_no_url_provided": "Nenhuma URL do YouTube fornecida",
|
||||
"youtube_no_video_found_with_id": "nenhum vídeo encontrado com o ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Lista de reprodução salva em %s",
|
||||
"youtube_rate_limit_exceeded": "Limite de taxa do YouTube excedido. Tente novamente mais tarde ou use argumentos diferentes do yt-dlp como '--sleep-requests 1' para desacelerar as requisições.",
|
||||
"youtube_setup_description": "YouTube - para obter transcrições de vídeo (via yt-dlp) e comentários/metadados (via API do YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract falhou no quadro %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_url_help": "Vídeo do YouTube ou URL da playlist para obter transcrição, comentários e enviar ao chat ou imprimir no console e armazenar no arquivo de saída",
|
||||
"youtube_url_is_playlist_not_video": "a URL é uma playlist, não um vídeo",
|
||||
"youtube_video_id_title_header": "VideoID: Título",
|
||||
"youtube_visual_fps_help": "Extrair um número específico de quadros por segundo em vez de usar detecção de cenas",
|
||||
"youtube_visual_frame_cue": "[MARCADOR DE QUADRO VISUAL]",
|
||||
"youtube_visual_sensitivity_help": "Tolerância para detecção de cenas do FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp não encontrado no PATH. Por favor instale o yt-dlp para usar a funcionalidade de transcrição do YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_ytdlp_stderr_error": "erro ao ler stderr do yt-dlp"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@
|
|||
"bedrock_aws_access_key_label": "Digite o seu AWS Access Key ID (deixe vazio para usar a cadeia de credenciais AWS)",
|
||||
"bedrock_aws_region_label": "Regiao AWS",
|
||||
"bedrock_aws_secret_key_label": "Digite o seu AWS Secret Access Key (deixe vazio para usar a cadeia de credenciais AWS)",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"bedrock_client_not_initialized": "cliente Bedrock não inicializado — execute 'fabric --setup' para configurar",
|
||||
"bedrock_converse_failed": "bedrock converse falhou para o modelo %s: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream falhou para o modelo %s: %w",
|
||||
"bedrock_empty_response_content": "conteudo de resposta vazio",
|
||||
|
|
@ -67,21 +67,21 @@
|
|||
"bedrock_listmodels_fallback": "API ListModels do Bedrock falhou, usando lista estática de recurso",
|
||||
"bedrock_panic_sendstream": "panico no SendStream: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Access Key + Secret Key",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Key / ABSK token (recommended — same as Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Enter 1 or 2",
|
||||
"bedrock_setup_choose_auth_method": " Choose authentication method:",
|
||||
"bedrock_setup_choose_model": " Choose default model (unprefixed IDs work in any region; us./eu./ap. are region-specific):",
|
||||
"bedrock_setup_choose_region": " Choose AWS Region:",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Chave API Bedrock / token ABSK (recomendado — a mesma usada pelo Claude Code)",
|
||||
"bedrock_setup_auth_prompt": "Introduza 1 ou 2",
|
||||
"bedrock_setup_choose_auth_method": " Escolha o método de autenticação:",
|
||||
"bedrock_setup_choose_model": " Escolha o modelo predefinido (IDs sem prefixo funcionam em qualquer região; us./eu./ap. são específicos de região):",
|
||||
"bedrock_setup_choose_region": " Escolha a região AWS:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "invalid selection: %s (enter 1 or 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Enter model ID (e.g. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Enter a different model ID",
|
||||
"bedrock_setup_model_prompt": "Enter model number or 0 to type your own",
|
||||
"bedrock_setup_region_custom_prompt": "Enter custom AWS region (e.g. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Enter a different region",
|
||||
"bedrock_setup_region_prompt": "Enter region number or 0 for custom",
|
||||
"bedrock_setup_selected_model": " ✓ Selected model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Use with: fabric -m %s -V Bedrock",
|
||||
"bedrock_setup_invalid_auth_selection": "seleção inválida: %s (introduza 1 ou 2)",
|
||||
"bedrock_setup_model_custom_prompt": "Introduza o ID do modelo (ex. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Introduzir um ID de modelo diferente",
|
||||
"bedrock_setup_model_prompt": "Introduza o número do modelo ou 0 para indicar o seu",
|
||||
"bedrock_setup_region_custom_prompt": "Introduza uma região AWS personalizada (ex. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Introduzir uma região diferente",
|
||||
"bedrock_setup_region_prompt": "Introduza o número da região ou 0 para personalizar",
|
||||
"bedrock_setup_selected_model": " ✓ Modelo selecionado: %s",
|
||||
"bedrock_setup_use_with": " ℹ Uso: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "nao foi possivel carregar a configuracao AWS: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "nao foi possivel carregar a configuracao AWS com regiao %s: %w",
|
||||
"bedrock_unexpected_content_block_type": "tipo de bloco de conteudo inesperado: %T",
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "Escolha um padrão dos padrões disponíveis",
|
||||
"choose_session_from_available": "Escolha uma sessão das sessões disponíveis",
|
||||
"choose_strategy_from_available": "Escolher uma estratégia das estratégias disponíveis",
|
||||
"codex_api_base_url_question": "Introduza o URL base da API do Codex",
|
||||
"codex_auth_base_url_invalid": "URL base de autenticação do Codex inválido: %w",
|
||||
"codex_auth_base_url_question": "Introduza o URL base de OAuth do Codex",
|
||||
"codex_browser_open_fallback": "Se o navegador não abriu, navegue até este URL para se autenticar:",
|
||||
"codex_decode_models_response_failed": "Falha ao descodificar a resposta de modelos do Codex: %w",
|
||||
"codex_decode_refresh_response_failed": "Falha ao descodificar a resposta do token atualizado do Codex: %w",
|
||||
"codex_decode_token_response_failed": "Falha ao descodificar a resposta de troca de token do Codex: %w",
|
||||
"codex_image_file_not_supported": "O fornecedor Codex não suporta --image-file. Utilize um anexo de imagem.",
|
||||
"codex_login_account_changed": "O início de sessão do Codex está associado a uma conta ChatGPT diferente da configuração guardada. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_completed": "Início de sessão do Codex concluído",
|
||||
"codex_login_failed": "Falha no início de sessão do Codex: %s",
|
||||
"codex_login_invalid": "O seu início de sessão do Codex já não é válido. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_missing_account_claim": "O início de sessão do Codex não incluiu um ID de conta ChatGPT. Este estado de início de sessão não é suportado.",
|
||||
"codex_login_missing_auth_code": "O início de sessão do Codex não devolveu um código de autorização.",
|
||||
"codex_login_missing_tokens": "O início de sessão do Codex não devolveu os tokens de acesso e atualização necessários.",
|
||||
"codex_login_refresh_failed": "Não foi possível atualizar o início de sessão do Codex. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_return_to_fabric": "Voltar ao Fabric.",
|
||||
"codex_login_revoked": "O início de sessão do Codex expirou ou foi revogado. Execute 'fabric --setup' novamente.",
|
||||
"codex_login_server_stopped": "O servidor de retorno do início de sessão do Codex parou antes da autenticação ser concluída.",
|
||||
"codex_login_state_mismatch": "O início de sessão do Codex não pôde ser verificado porque o estado OAuth não correspondeu.",
|
||||
"codex_login_timed_out": "O início de sessão do Codex expirou antes da autenticação ser concluída.",
|
||||
"codex_oauth_missing_auth_code": "Código de autorização em falta",
|
||||
"codex_oauth_random_state_failed": "Falha ao gerar estado OAuth aleatório seguro: %w",
|
||||
"codex_oauth_server_start_failed": "Falha ao iniciar o servidor local de retorno OAuth: %w",
|
||||
"codex_oauth_state_mismatch": "O estado não corresponde",
|
||||
"codex_provider_error": "erro do fornecedor Codex (estado %d): %s",
|
||||
"codex_refresh_failed_status": "Falha ao atualizar o início de sessão do Codex (estado %d)",
|
||||
"codex_refresh_login_failed": "Falha ao atualizar o início de sessão do Codex: %w",
|
||||
"codex_refresh_token_required": "O token de atualização do Codex é obrigatório. Execute 'fabric --setup' novamente.",
|
||||
"codex_replay_body_unavailable": "O corpo do pedido não pode ser reproduzido para a tentativa de reautenticação do Codex",
|
||||
"codex_request_failed": "O pedido do Codex falhou: %w",
|
||||
"codex_request_failed_status": "O pedido do Codex falhou com estado %d",
|
||||
"codex_starting_browser_login": "A iniciar início de sessão OpenAI baseado no navegador para o Codex.",
|
||||
"codex_token_exchange_failed": "A troca de token do Codex falhou: %w",
|
||||
"codex_token_persist_failed": "falha ao persistir o início de sessão do Codex: %w",
|
||||
"codex_token_refresh_missing_access_token": "A atualização do token do Codex não devolveu um token de acesso.",
|
||||
"codex_usage_limit_reached": "Limite de utilização do Codex atingido",
|
||||
"command_completed_successfully": "Comando concluído com sucesso",
|
||||
"compression_level_jpeg_webp": "Nível de compressão 0-100 para formatos JPEG/WebP (por omissão: não definido)",
|
||||
"config_file_not_found": "ficheiro de configuração não encontrado: %s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "Padrões personalizados - Definir diretório para os seus padrões personalizados",
|
||||
"custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n",
|
||||
"db_error_loading_env_file": "erro ao carregar o ficheiro .env: %w",
|
||||
"db_error_updating_env_file": "erro ao atualizar o ficheiro .env: %w",
|
||||
"defaults_model_context_length_question": "Indique o comprimento do contexto do modelo",
|
||||
"defaults_model_question": "Indique o índice ou o nome do seu modelo padrão",
|
||||
"defaults_setup_description": "Fornecedor e modelo de IA padrão",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "pedido de modelos do DigitalOcean falhou com estado %d: %s",
|
||||
"disable_openai_responses_api": "Desabilitar API OpenAI Responses (por omissão: false)",
|
||||
"disable_pattern_variable_replacement": "Desabilitar substituição de variáveis de padrão",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de pesquisa web para modelos suportados (Anthropic, OpenAI, Gemini)",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de pesquisa web para modelos suportados (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Tag final para secções de pensamento",
|
||||
"error_creating_audio_file": "erro ao criar ficheiro de áudio: %v",
|
||||
"error_creating_file": "erro ao criar ficheiro: %v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "formato de URL de dados inválido",
|
||||
"ollama_invalid_http_timeout_using_default": "Tempo limite HTTP inválido '%s': %v, a utilizar o padrão",
|
||||
"ollama_invalid_num_ctx_in_request": "num_ctx inválido no pedido: %v",
|
||||
"ollama_invalid_request_body": "corpo de pedido inválido",
|
||||
"ollama_no_content_from_upstream": "nenhum conteúdo recebido do servidor Fabric upstream",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx excede o valor máximo permitido de %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx deve ser um número, recebeu tipo inválido",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "Linha SSE excede limite de buffer de 1MB - linha de dados demasiado grande",
|
||||
"ollama_upstream_non_2xx": "Servidor Fabric upstream retornou estado não-2xx %d: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Servidor Fabric upstream retornou estado não-2xx %d e o corpo não pôde ser lido: %v",
|
||||
"ollama_upstream_request_failed": "falha ao contactar o servidor Fabric a montante",
|
||||
"ollama_upstream_returned_status": "servidor Fabric upstream retornou estado %d",
|
||||
"ollama_warning_no_content": "Aviso: nenhum conteúdo recebido do servidor Fabric upstream",
|
||||
"ollama_warning_parse_variables": "Aviso: falha ao analisar options.variables como JSON: %v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "falha ao guardar a imagem em %s: %w",
|
||||
"openai_image_saved_to": "Imagem guardada em: %s",
|
||||
"openai_model_no_image_generation": "o modelo '%s' não suporta geração de imagens. Modelos suportados: %s",
|
||||
"openai_models_rate_limited": "limite de taxa excedido ao obter modelos do fornecedor %s; tente novamente após %s segundos",
|
||||
"openai_models_response_too_large": "resposta de modelos demasiado grande do fornecedor %s (>%d bytes)",
|
||||
"openai_unable_to_parse_models_response": "não foi possível analisar a resposta de modelos; resposta bruta: %s",
|
||||
"openai_unexpected_status_code_read_error": "código de estado inesperado: %d do fornecedor %s (falha ao ler corpo da resposta: %v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "Mostrar metadados do vídeo",
|
||||
"path_to_yaml_config": "Caminho para ficheiro de configuração YAML",
|
||||
"pattern_not_found_list_available": "padrão '%s' não encontrado. Execute 'fabric -l' para ver os padrões disponíveis",
|
||||
"pattern_invalid_name": "nome de padrão inválido: %q",
|
||||
"pattern_not_found_no_patterns": "padrão '%s' não encontrado.\n\nNenhum padrão instalado! Para resolver:\n • Execute 'fabric --setup' para configurar e descarregar padrões\n • Ou execute 'fabric -U' para descarregar/atualizar padrões diretamente",
|
||||
"pattern_variables_help": "Valores para variáveis de padrão, ex. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "A clonar repositório %s (caminho: %s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "Preferir playlist ao vídeo se ambos os IDs estiverem presentes na URL",
|
||||
"print_context": "Imprimir contexto",
|
||||
"print_current_version": "Imprimir versão atual",
|
||||
"print_metadata_to_stderr": "Imprimir metadados (tokens de entrada/saída) no stderr",
|
||||
"print_pattern_contents": "Imprimir o conteúdo do padrão indicado no terminal",
|
||||
"print_session": "Imprimir sessão",
|
||||
"register_new_extension": "Registar uma nova extensão do caminho do ficheiro de configuração",
|
||||
"remove_registered_extension": "Remover uma extensão registada por nome",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "Enviar notificação no ambiente de trabalho quando o comando for concluído",
|
||||
"serve_fabric_api_ollama_endpoints": "Servir a API REST do Fabric com endpoints ollama",
|
||||
"serve_fabric_rest_api": "Servir a API REST do Fabric",
|
||||
"server_api_key_required": "recusa de servir no endereço não loopback %s sem chave de API: defina --api-key ou FABRIC_API_KEY, ou vincule um endereço loopback como 127.0.0.1:8080",
|
||||
"server_chat_error": "Erro: %v",
|
||||
"server_error_marshaling_response": "erro ao serializar resposta: %v",
|
||||
"server_error_writing_response": "erro ao escrever resposta: %v",
|
||||
"server_invalid_request_format": "formato de pedido inválido: %v",
|
||||
"server_no_api_key_warning": "A iniciar o servidor da API REST sem autenticação por chave de API. Isto pode representar riscos de segurança.",
|
||||
"sessions_creating_new": "A criar nova sessão: %s\n",
|
||||
"set_debug_level": "Definir nível de debug (0=desligado, 1=básico, 2=detalhado, 3=rastreio, 4=wire)",
|
||||
"set_frequency_penalty": "Definir penalidade de frequência",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**Programa**: %s",
|
||||
"spotify_title_label": "**Título**: %s",
|
||||
"spotify_total_episodes_label": "**Total de episódios**: %d",
|
||||
"spotify_url_help": "URL de podcast ou episódio do Spotify para obter metadados e enviar ao chat",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Tag inicial para secções de pensamento",
|
||||
"storage_error_delete": "Não foi possível eliminar %s: %v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "Não foi possível guardar %s: %v",
|
||||
"storage_error_stat_entry": "Não foi possível obter informações da entrada %s: %v",
|
||||
"storage_error_unmarshal": "Não foi possível desserializar %s: %s",
|
||||
"storage_invalid_name": "nome inválido: %q",
|
||||
"strategies_available_header": "Estratégias disponíveis:",
|
||||
"strategies_cloning_repository": "A clonar repositório %s (caminho: %s)...\\n",
|
||||
"strategies_download_success": "✅ Estratégias transferidas e instaladas com sucesso em %s\\n",
|
||||
|
|
@ -610,11 +643,11 @@
|
|||
"strategy_path_traversal": "o nome da estratégia %q resolve fora do diretório de estratégias",
|
||||
"stream_help": "Streaming",
|
||||
"suppress_thinking_tags": "Suprimir texto contido em tags de pensamento",
|
||||
"template_datetime_error_invalid_number": "invalid number in relative time: %q",
|
||||
"template_datetime_error_invalid_relative_format": "invalid relative time format",
|
||||
"template_datetime_error_invalid_unit": "invalid time unit: %q",
|
||||
"template_datetime_error_relative_requires_value": "relative time requires a value",
|
||||
"template_datetime_error_unknown_operation": "datetime: unknown operation %q",
|
||||
"template_datetime_error_invalid_number": "número inválido no tempo relativo: %q",
|
||||
"template_datetime_error_invalid_relative_format": "formato de tempo relativo inválido",
|
||||
"template_datetime_error_invalid_unit": "unidade de tempo inválida: %q",
|
||||
"template_datetime_error_relative_requires_value": "o tempo relativo requer um valor",
|
||||
"template_datetime_error_unknown_operation": "datetime: operação desconhecida %q",
|
||||
"template_extension_error": "Erro na extensão %s: %v",
|
||||
"template_file_error_expand_home_dir": "ficheiro: não foi possível expandir a diretoria home: %v",
|
||||
"template_file_error_invalid_line_count": "ficheiro: contagem de linhas inválida %q",
|
||||
|
|
@ -643,12 +676,12 @@
|
|||
"template_missing_required_variable": "Variável obrigatória em falta: %s",
|
||||
"template_plugin_error": "Erro no plugin %s: %v",
|
||||
"template_processing_stuck": "Processamento do modelo bloqueado - possível ciclo infinito",
|
||||
"template_sys_error_env_requires_var": "env: variable name required",
|
||||
"template_sys_error_home": "failed to get home directory: %v",
|
||||
"template_sys_error_hostname": "failed to get hostname: %v",
|
||||
"template_sys_error_pwd": "failed to get working directory: %v",
|
||||
"template_sys_error_unknown_operation": "sys: unknown operation %q",
|
||||
"template_sys_error_user": "failed to get current user: %v",
|
||||
"template_sys_error_env_requires_var": "env: nome da variável obrigatório",
|
||||
"template_sys_error_home": "falha ao obter o diretório pessoal: %v",
|
||||
"template_sys_error_hostname": "falha ao obter o nome do host: %v",
|
||||
"template_sys_error_pwd": "falha ao obter o diretório de trabalho: %v",
|
||||
"template_sys_error_unknown_operation": "sys: operação desconhecida %q",
|
||||
"template_sys_error_user": "falha ao obter o utilizador atual: %v",
|
||||
"template_text_empty_input": "Texto: entrada vazia para a operação %q",
|
||||
"template_text_unknown_operation": "Texto: operação de texto desconhecida %q (suportadas: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "Espaço de nomes do plugin desconhecido: %s",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "erro ao obter detalhes do vídeo: %v",
|
||||
"youtube_error_parsing_duration": "erro ao analisar a duração do vídeo: %v",
|
||||
"youtube_error_saving_csv": "erro ao guardar vídeos em CSV: %v",
|
||||
"youtube_extract_visual_data_help": "Extrair dados visuais do vídeo usando OCR e FFmpeg",
|
||||
"youtube_failed_create_temp_dir": "falha ao criar diretório temporário: %v",
|
||||
"youtube_failed_fetch_comments": "Falha ao obter comentários: %v",
|
||||
"youtube_failed_get_stream_url": "falha ao obter o URL do stream via yt-dlp: %v",
|
||||
"youtube_failed_parse_http_stream_url": "falha ao analisar um URL HTTP válido de stream a partir da saída do yt-dlp",
|
||||
"youtube_failed_walk_directory": "falha ao percorrer o diretório: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "a extração de fotogramas com ffmpeg falhou: %v, saída: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "ffmpeg é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_invalid_duration_string": "cadeia de duração inválida: %s",
|
||||
"youtube_invalid_seconds_format": "formato de segundos inválido %q: %w",
|
||||
"youtube_invalid_timestamp_format": "formato de timestamp inválido: %s",
|
||||
"youtube_invalid_url": "URL do YouTube inválido, não é possível obter o ID do vídeo ou da lista de reprodução: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "argumentos do yt-dlp inválidos: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "nenhum texto legível encontrado nos fotogramas visuais do vídeo",
|
||||
"youtube_no_transcript_content": "nenhum conteúdo de transcrição encontrado no ficheiro VTT",
|
||||
"youtube_no_url_provided": "Nenhum URL do YouTube fornecido",
|
||||
"youtube_no_video_found_with_id": "nenhum vídeo encontrado com o ID: %s",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "Lista de reprodução guardada em %s",
|
||||
"youtube_rate_limit_exceeded": "Limite de taxa do YouTube excedido. Tente novamente mais tarde ou utilize argumentos diferentes do yt-dlp como '--sleep-requests 1' para desacelerar os pedidos.",
|
||||
"youtube_setup_description": "YouTube - para obter transcrições de vídeo (via yt-dlp) e comentários/metadados (via API do YouTube)",
|
||||
"youtube_tesseract_frame_failed": "tesseract falhou no fotograma %d: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "tesseract é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_url_help": "Vídeo do YouTube ou \"URL\" de playlist para obter transcrição, comentários e enviar ao chat ou imprimir na consola e armazenar no ficheiro de saída",
|
||||
"youtube_url_is_playlist_not_video": "o URL é uma lista de reprodução, não um vídeo",
|
||||
"youtube_video_id_title_header": "VideoID: Título",
|
||||
"youtube_visual_fps_help": "Extrair um número específico de fotogramas por segundo em vez de usar deteção de cenas",
|
||||
"youtube_visual_frame_cue": "[MARCADOR DE FOTOGRAMA VISUAL]",
|
||||
"youtube_visual_sensitivity_help": "Tolerância para deteção de cenas do FFmpeg (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp não encontrado no PATH. Por favor instale o yt-dlp para usar a funcionalidade de transcrição do YouTube",
|
||||
"youtube_ytdlp_required_visual_extraction": "yt-dlp é necessário para extração visual, mas não foi encontrado no PATH",
|
||||
"youtube_ytdlp_stderr_error": "erro ao ler stderr do yt-dlp"
|
||||
}
|
||||
|
|
|
|||
770
internal/i18n/locales/tr.json
Normal file
770
internal/i18n/locales/tr.json
Normal file
|
|
@ -0,0 +1,770 @@
|
|||
{
|
||||
"abacus_models_endpoint_status": "abacus modelleri uç noktası %d durumunu döndürdü",
|
||||
"additional_yt_dlp_args": "yt-dlp'ye geçirilecek ek argümanlar (örn. '--cookies-from-browser brave')",
|
||||
"address_to_bind_rest_api": "REST API'sini bağlamak için adres",
|
||||
"anthropic_stream_error": "Akış hatası: %v",
|
||||
"api_key_secure_server_routes": "Sunucu rotalarını güvence altına almak için kullanılan API anahtarı",
|
||||
"application_options_header": "Uygulama Seçenekleri:",
|
||||
"apply_variables_to_input": "Değişkenleri kullanıcı girişine uygula",
|
||||
"attachment_could_not_determine_mimetype": "URL'nin mimetype'ı belirlenemedi",
|
||||
"attachment_file_not_exist": "dosya %s mevcut değil",
|
||||
"attachment_no_content_available": "içerik mevcut değil",
|
||||
"attachment_no_type_no_content": "eklentiye ait tür veya türetilecek içerik mevcut değil",
|
||||
"attachment_path_or_url_help": "Eklenti yolu veya URL'si (örn. OpenAI görsel tanıma mesajları için)",
|
||||
"audio_output_file_specified_but_not_tts_model": "ses çıkış dosyası '%s' belirtildi ancak '%s' modeli bir TTS modeli değil. Lütfen gemini-2.5-flash-preview-tts gibi bir TTS modeli kullanın",
|
||||
"audio_video_file_transcribe": "Ses veya video dosyasını yazıya dönüştür",
|
||||
"available_models_header": "Mevcut modeller",
|
||||
"available_transcription_models": "Mevcut yazıya dönüştürme modelleri:",
|
||||
"available_vendors_header": "Mevcut Sağlayıcılar:",
|
||||
"azure_api_key_required": "Azure API anahtarı gerekli",
|
||||
"azure_api_version_question": "Azure API sürümünü girin (varsayılan için boş bırakın)",
|
||||
"azure_base_url_question": "API Temel URL'si",
|
||||
"azure_base_url_required": "Azure temel URL'si gerekli",
|
||||
"azure_credential_failure": "Azure kimlik bilgisi oluşturulamadı",
|
||||
"azure_deployments_question": "Azure dağıtım adlarınızı girin (virgülle ayrılmış)",
|
||||
"azure_deployments_required": "en az bir Azure dağıtım adı gerekli",
|
||||
"azure_failed_extract_deployment": "istekten dağıtım adı alınamadı",
|
||||
"azure_model_field_empty": "istek gövdesinde model alanı boş",
|
||||
"azure_request_body_nil": "istek gövdesi boş",
|
||||
"azureaigateway_aoai_no_choices": "Azure OpenAI yanıtında seçenek yok",
|
||||
"azureaigateway_aoai_parse_response_failed": "Azure OpenAI yanıtı ayrıştırılamadı: %w",
|
||||
"azureaigateway_api_version_question": "Azure OpenAI API sürümü (varsayılan: 2025-04-01-preview, varsayılan için boş bırakın)",
|
||||
"azureaigateway_backend_not_initialized": "arka uç başlatılmadı - yapılandırmak için 'fabric --setup' komutunu çalıştırın",
|
||||
"azureaigateway_backend_type_question": "Arka uç türünü seçin (bedrock, azure-openai, vertex-ai)",
|
||||
"azureaigateway_bedrock_no_text_blocks": "Bedrock yanıtında metin içerik bloğu yok",
|
||||
"azureaigateway_bedrock_parse_response_failed": "Bedrock yanıtı ayrıştırılamadı: %w",
|
||||
"azureaigateway_failed_create_request": "AzureAIGateway: istek oluşturulamadı: %w",
|
||||
"azureaigateway_failed_read_response": "AzureAIGateway: yanıt okunamadı: %w",
|
||||
"azureaigateway_gateway_url_https_required": "ağ geçidi URL'si HTTPS şeması kullanmalıdır",
|
||||
"azureaigateway_gateway_url_question": "Azure APIM Ağ Geçidi temel URL'nizi girin (örn. https://gateway.company.com)",
|
||||
"azureaigateway_gateway_url_required": "Azure APIM ağ geçidi URL'si gerekli",
|
||||
"azureaigateway_http_error": "AzureAIGateway: HTTP %d: %s",
|
||||
"azureaigateway_http_request_failed": "AzureAIGateway: HTTP isteği başarısız oldu: %w",
|
||||
"azureaigateway_invalid_gateway_url": "geçersiz ağ geçidi URL'si: %w",
|
||||
"azureaigateway_no_valid_messages": "boş içerik filtrelendikten sonra geçerli mesaj yok",
|
||||
"azureaigateway_prepare_request_failed": "AzureAIGateway: %w",
|
||||
"azureaigateway_response_too_large": "AzureAIGateway: yanıt çok büyük (>%d bayt)",
|
||||
"azureaigateway_subscription_key_question": "Azure APIM abonelik anahtarınızı girin",
|
||||
"azureaigateway_subscription_key_required": "Azure APIM abonelik anahtarı gerekli",
|
||||
"azureaigateway_unsupported_backend": "desteklenmeyen arka uç: %s (geçerli seçenekler: bedrock, azure-openai, vertex-ai)",
|
||||
"azureaigateway_vertexai_no_content": "Vertex AI yanıtında içerik yok",
|
||||
"azureaigateway_vertexai_parse_response_failed": "Vertex AI yanıtı ayrıştırılamadı: %w",
|
||||
"background_type_help": "Arka plan türü: opak, şeffaf (varsayılan: opak, yalnızca PNG/WebP için)",
|
||||
"bedrock_api_key_label": "Bedrock API Anahtarınızı / ABSK belirtecinizi girin (önerilir — Claude Code tarafından kullanılan anahtarla aynı)",
|
||||
"bedrock_aws_access_key_label": "AWS Erişim Anahtarı Kimliğinizi girin (yalnızca yukarıdaki API Anahtarını kullanmıyorsanız)",
|
||||
"bedrock_aws_region_label": "AWS Bölgenizi girin (örn. us-east-1, us-west-2, eu-west-1, ap-southeast-1)",
|
||||
"bedrock_aws_secret_key_label": "AWS Gizli Erişim Anahtarınızı girin (yalnızca yukarıdaki API Anahtarını kullanmıyorsanız)",
|
||||
"bedrock_client_not_initialized": "Bedrock istemcisi başlatılmadı — yapılandırmak için 'fabric --setup' komutunu çalıştırın",
|
||||
"bedrock_converse_failed": "bedrock converse, %s modeli için başarısız oldu: %w",
|
||||
"bedrock_conversestream_failed": "bedrock conversestream, %s modeli için başarısız oldu: %w",
|
||||
"bedrock_empty_response_content": "boş yanıt içeriği",
|
||||
"bedrock_failed_list_foundation_models": "temel modeller listelenemedi: %w",
|
||||
"bedrock_failed_list_inference_profiles": "çıkarım profilleri listelenemedi: %w",
|
||||
"bedrock_fetch_regions_bad_status": "botocore uç noktaları beklenmeyen bir durum döndürdü",
|
||||
"bedrock_fetch_regions_failed": "botocore'dan Bedrock bölgeleri alınamadı",
|
||||
"bedrock_fetch_regions_parse_failed": "botocore endpoints.json ayrıştırılamadı",
|
||||
"bedrock_invalid_aws_region": "geçersiz AWS bölgesi: %s",
|
||||
"bedrock_listmodels_fallback": "Bedrock ListModels API başarısız oldu, statik yedek kullanılıyor",
|
||||
"bedrock_panic_sendstream": "SendStream'de panik: %v",
|
||||
"bedrock_setup_auth_option_accesskey": " [2] AWS Erişim Anahtarı + Gizli Anahtar",
|
||||
"bedrock_setup_auth_option_apikey": " [1] Bedrock API Anahtarı / ABSK belirteci (önerilir — Claude Code ile aynı)",
|
||||
"bedrock_setup_auth_prompt": "1 veya 2 girin",
|
||||
"bedrock_setup_choose_auth_method": " Kimlik doğrulama yöntemini seçin:",
|
||||
"bedrock_setup_choose_model": " Varsayılan modeli seçin (ön ek olmayan kimlikler herhangi bir bölgede çalışır; us./eu./ap. bölgeye özeldir):",
|
||||
"bedrock_setup_choose_region": " AWS Bölgesini seçin:",
|
||||
"bedrock_setup_header": "[Bedrock]",
|
||||
"bedrock_setup_invalid_auth_selection": "geçersiz seçim: %s (1 veya 2 girin)",
|
||||
"bedrock_setup_model_custom_prompt": "Model kimliğini girin (örn. anthropic.claude-opus-4-6-v1)",
|
||||
"bedrock_setup_model_option_custom": " [0] Farklı bir model kimliği girin",
|
||||
"bedrock_setup_model_prompt": "Model numarasını veya kendinizinkini yazmak için 0 girin",
|
||||
"bedrock_setup_region_custom_prompt": "Özel AWS bölgesini girin (örn. us-east-1)",
|
||||
"bedrock_setup_region_option_custom": " [0] Farklı bir bölge girin",
|
||||
"bedrock_setup_region_prompt": "Bölge numarasını veya özel için 0 girin",
|
||||
"bedrock_setup_selected_model": " ✓ Seçilen model: %s",
|
||||
"bedrock_setup_use_with": " ℹ Şununla kullanın: fabric -m %s -V Bedrock",
|
||||
"bedrock_unable_load_aws_config": "AWS Yapılandırması yüklenemiyor: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "AWS Yapılandırması %s bölgesiyle yüklenemiyor: %w",
|
||||
"bedrock_unexpected_content_block_type": "beklenmeyen içerik bloğu türü: %T",
|
||||
"bedrock_unexpected_response_type": "beklenmeyen yanıt türü: %T",
|
||||
"bedrock_unknown_stream_event_type": "bilinmeyen akış olayı türü: %T",
|
||||
"cannot_convert_string": "%q dizgisi %v öğesine dönüştürülemiyor",
|
||||
"change_default_model": "Varsayılan modeli değiştir",
|
||||
"chat_error_content_fields_misused": "Content ve MultiContent özelliklerini aynı anda kullanamazsınız",
|
||||
"chatter_error_empty_response": "boş yanıt",
|
||||
"chatter_error_find_context": "%s bağlamı bulunamadı: %v",
|
||||
"chatter_error_find_session": "%s oturumu bulunamadı: %v",
|
||||
"chatter_error_get_pattern": "%s deseni alınamadı: %v",
|
||||
"chatter_error_load_strategy": "%s stratejisi yüklenemedi: %v",
|
||||
"chatter_error_no_messages_provided": "mesaj sağlanmadı",
|
||||
"chatter_error_no_session_pattern_user_messages": "oturum, desen veya kullanıcı mesajı sağlanmadı",
|
||||
"chatter_error_stream_update": "Hata: %s",
|
||||
"chatter_help_review_changes_with_git_diff": "git kullanıyorsanız değişiklikleri 'git diff' ile inceleyebilirsiniz.",
|
||||
"chatter_info_file_changes_applied_successfully": "Dosya değişiklikleri başarıyla uygulandı.",
|
||||
"chatter_log_stream_usage_metadata": "[Meta Veri] Giriş: %d | Çıkış: %d | Toplam: %d",
|
||||
"chatter_prompt_enforce_response_language": "%s\n\nÖNEMLİ: Öncelikle, bu istemde verilen talimatları kullanıcının girdisini kullanarak yürütün. İkinci olarak, talimatların yürütülmesinin bir parçası olarak oluşturulan herhangi bir bölüm başlığı veya başlık dahil olmak üzere tüm nihai yanıtınızın YALNIZCA %s dilinde yazıldığından emin olun.",
|
||||
"chatter_warning_apply_file_changes_failed": "Uyarı: Dosya değişiklikleri uygulanamadı: %v",
|
||||
"chatter_warning_get_current_directory_failed": "Uyarı: Geçerli dizin alınamadı: %v",
|
||||
"chatter_warning_parse_file_changes_failed": "Uyarı: Dosya değişiklikleri ayrıştırılamadı: %v",
|
||||
"choose_context_from_available": "Mevcut bağlamlar arasından bir bağlam seçin",
|
||||
"choose_model": "Model seç",
|
||||
"choose_pattern_from_available": "Mevcut desenler arasından bir desen seçin",
|
||||
"choose_session_from_available": "Mevcut oturumlar arasından bir oturum seçin",
|
||||
"choose_strategy_from_available": "Mevcut stratejiler arasından bir strateji seçin",
|
||||
"codex_api_base_url_question": "Codex API temel URL'nizi girin",
|
||||
"codex_auth_base_url_invalid": "geçersiz codex kimlik doğrulama temel URL'si: %w",
|
||||
"codex_auth_base_url_question": "Codex OAuth temel URL'nizi girin",
|
||||
"codex_browser_open_fallback": "Tarayıcınız açılmadıysa, kimlik doğrulaması için bu URL'ye gidin:",
|
||||
"codex_decode_models_response_failed": "codex modelleri yanıtı çözülemedi: %w",
|
||||
"codex_decode_refresh_response_failed": "yenilenmiş Codex belirteç yanıtı çözülemedi: %w",
|
||||
"codex_decode_token_response_failed": "codex belirteç takas yanıtı çözülemedi: %w",
|
||||
"codex_image_file_not_supported": "Codex sağlayıcısı --image-file özelliğini desteklemiyor. Bunun yerine bir görsel eki kullanın.",
|
||||
"codex_login_account_changed": "Codex girişi, depolanan yapılandırmadan farklı bir ChatGPT hesabına bağlı. Lütfen 'fabric --setup' komutunu tekrar çalıştırın.",
|
||||
"codex_login_completed": "Codex girişi tamamlandı",
|
||||
"codex_login_failed": "Codex girişi başarısız oldu: %s",
|
||||
"codex_login_invalid": "Codex girişi artık geçerli değil. Lütfen 'fabric --setup' komutunu tekrar çalıştırın.",
|
||||
"codex_login_missing_account_claim": "Codex girişi bir ChatGPT hesap kimliği içermiyordu. Bu giriş durumu desteklenmiyor.",
|
||||
"codex_login_missing_auth_code": "Codex girişi bir yetkilendirme kodu döndürmedi.",
|
||||
"codex_login_missing_tokens": "Codex girişi gerekli erişim ve yenileme belirteçlerini döndürmedi.",
|
||||
"codex_login_refresh_failed": "Codex girişi yenilenemedi. Lütfen 'fabric --setup' komutunu tekrar çalıştırın.",
|
||||
"codex_login_return_to_fabric": "Fabric'e dönün.",
|
||||
"codex_login_revoked": "Codex girişi süresi doldu veya iptal edildi. Lütfen 'fabric --setup' komutunu tekrar çalıştırın.",
|
||||
"codex_login_server_stopped": "Codex giriş geri çağırma sunucusu, kimlik doğrulama tamamlanmadan önce durdu.",
|
||||
"codex_login_state_mismatch": "OAuth durumu eşleşmediği için Codex girişi doğrulanamadı.",
|
||||
"codex_login_timed_out": "Codex girişi, kimlik doğrulama tamamlanmadan önce zaman aşımına uğradı.",
|
||||
"codex_oauth_missing_auth_code": "Yetkilendirme kodu eksik",
|
||||
"codex_oauth_random_state_failed": "güvenli rastgele oauth durumu oluşturulamadı: %w",
|
||||
"codex_oauth_server_start_failed": "yerel oauth geri çağırma sunucusu başlatılamadı: %w",
|
||||
"codex_oauth_state_mismatch": "Durum eşleşmiyor",
|
||||
"codex_provider_error": "codex sağlayıcı hatası (durum %d): %s",
|
||||
"codex_refresh_failed_status": "codex girişi yenilenemedi (durum %d)",
|
||||
"codex_refresh_login_failed": "Codex girişi yenilenemedi: %w",
|
||||
"codex_refresh_token_required": "Codex yenileme belirteci gerekli. Lütfen 'fabric --setup' komutunu tekrar çalıştırın.",
|
||||
"codex_replay_body_unavailable": "Codex yeniden kimlik doğrulama denemesi için istek gövdesi tekrar oynatılamaz",
|
||||
"codex_request_failed": "codex isteği başarısız oldu: %w",
|
||||
"codex_request_failed_status": "codex isteği %d durumuyla başarısız oldu",
|
||||
"codex_starting_browser_login": "Codex için tarayıcı tabanlı OpenAI girişi başlatılıyor.",
|
||||
"codex_token_exchange_failed": "codex belirteç takası başarısız oldu: %w",
|
||||
"codex_token_persist_failed": "Codex girişi kalıcı hale getirilemedi: %w",
|
||||
"codex_token_refresh_missing_access_token": "Codex belirteç yenilemesi bir erişim belirteci döndürmedi.",
|
||||
"codex_usage_limit_reached": "codex kullanım limitine ulaşıldı",
|
||||
"command_completed_successfully": "Komut başarıyla tamamlandı",
|
||||
"compression_level_jpeg_webp": "JPEG/WebP biçimleri için sıkıştırma seviyesi 0-100 (varsayılan: ayarlanmadı)",
|
||||
"config_file_not_found": "yapılandırma dosyası bulunamadı: %s",
|
||||
"convert_html_readability": "HTML girdisini temiz, okunabilir bir görünüme dönüştür",
|
||||
"copilot_debug_created_conversation": "Copilot konuşması oluşturuldu: %s",
|
||||
"copilot_debug_failed_parse_sse_event": "SSE olayı ayrıştırılamadı: %v",
|
||||
"copilot_error_chat_request": "sohbet isteği başarısız oldu: %s - %s",
|
||||
"copilot_error_create_conversation": "konuşma oluşturulamadı: %s - %s",
|
||||
"copilot_error_reading_stream": "akış okunurken hata oluştu: %w",
|
||||
"copilot_error_stream_request": "akış isteği başarısız oldu: %s - %s",
|
||||
"copilot_failed_create_conversation": "konuşma oluşturulamadı: %w",
|
||||
"copilot_failed_send_message": "mesaj gönderilemedi: %w",
|
||||
"copilot_failed_stream_message": "mesaj akışı sağlanamadı: %w",
|
||||
"copilot_tenant_client_id_required": "kiracı kimliği ve istemci kimliği gereklidir",
|
||||
"copy_to_clipboard": "Panoya Kopyala",
|
||||
"could_not_copy_to_clipboard": "panoya kopyalanamadı: %v",
|
||||
"could_not_create_config_dir": "yapılandırma dizini oluşturulamadı: %w",
|
||||
"could_not_create_env_file": ".env dosyası oluşturulamadı: %w",
|
||||
"could_not_determine_home_dir": "kullanıcı ana dizini belirlenemedi: %w",
|
||||
"could_not_stat_env_file": ".env dosyası bilgileri alınamadı (stat): %w",
|
||||
"custom_notification_command": "Bildirimler için çalıştırılacak özel komut (yerleşik bildirimleri geçersiz kılar)",
|
||||
"custom_patterns_directory_question": "Özel desenler dizininizin yolunu girin",
|
||||
"custom_patterns_label": "Özel Desenler",
|
||||
"custom_patterns_setup_description": "Özel Desenler - Özel desenleriniz için dizini belirleyin",
|
||||
"custom_patterns_warning_create_directory": "Uyarı: Özel desenler dizini %s oluşturulamadı: %v\n",
|
||||
"db_error_loading_env_file": ".env dosyası yüklenirken hata oluştu: %w",
|
||||
"db_error_updating_env_file": ".env dosyası güncellenirken hata oluştu: %w",
|
||||
"defaults_model_context_length_question": "Model bağlam uzunluğunu girin",
|
||||
"defaults_model_question": "Varsayılan modelinizin dizinini veya adını girin",
|
||||
"defaults_setup_description": "Varsayılan Yapay Zeka Sağlayıcısı ve Modeli",
|
||||
"digitalocean_failed_parse_control_plane_url": "DigitalOcean kontrol düzlemi URL'si ayrıştırılamadı: %w",
|
||||
"digitalocean_model_list_unavailable": "DigitalOcean model listesi kullanılamıyor. Modelleri kontrol düzleminden almak için DIGITALOCEAN_TOKEN değerini ayarlayın",
|
||||
"digitalocean_model_list_unavailable_with_error": "DigitalOcean model listesi kullanılamıyor: %w. Modelleri kontrol düzleminden almak için DIGITALOCEAN_TOKEN değerini ayarlayın",
|
||||
"digitalocean_models_request_failed_read_error": "DigitalOcean modelleri isteği %d durumuyla başarısız oldu: %w",
|
||||
"digitalocean_models_request_failed_with_status": "DigitalOcean modelleri isteği %d durumuyla başarısız oldu: %s",
|
||||
"disable_openai_responses_api": "OpenAI Yanıt API'sini devre dışı bırak (varsayılan: false)",
|
||||
"disable_pattern_variable_replacement": "Desen değişkeni değiştirmeyi devre dışı bırak",
|
||||
"enable_web_search_tool": "Desteklenen modeller için web arama aracını etkinleştir (Anthropic, OpenAI, Gemini, Grok)",
|
||||
"end_tag_thinking_sections": "Düşünme bölümleri için bitiş etiketi",
|
||||
"error_creating_audio_file": "ses dosyası oluşturulurken hata oluştu: %v",
|
||||
"error_creating_file": "dosya oluşturulurken hata oluştu: %v",
|
||||
"error_fetching_playlist_videos": "çalma listesi videoları alınırken hata oluştu: %w",
|
||||
"error_parsing_config_file": "yapılandırma dosyası ayrıştırılırken hata oluştu: %w",
|
||||
"error_reading_config_file": "yapılandırma dosyası okunurken hata oluştu: %w",
|
||||
"error_reading_piped_message": "stdin'den yönlendirilen mesaj okunurken hata oluştu: %w",
|
||||
"error_writing_audio_data": "ses verileri dosyaya yazılırken hata oluştu: %v",
|
||||
"error_writing_to_file": "dosyaya yazılırken hata oluştu: %v",
|
||||
"extension_cmd_template_required": "işlem %s için komut şablonu gereklidir",
|
||||
"extension_command_template_label": " Komut Şablonu: %s\n",
|
||||
"extension_config_hash_mismatch": "%s için yapılandırma dosyası hash uyuşmazlığı",
|
||||
"extension_config_path_label": " Yapılandırma Yolu: %s\n\n",
|
||||
"extension_description_label": " Açıklama: %s\n",
|
||||
"extension_empty_command": "biçimlendirme sonrası boş komut",
|
||||
"extension_executable_hash_mismatch": "%s için çalıştırılabilir dosya hash uyuşmazlığı",
|
||||
"extension_executable_label": " Çalıştırılabilir: %s\n",
|
||||
"extension_executable_not_found": "çalıştırılabilir dosya bulunamadı: %w",
|
||||
"extension_executable_required": "çalıştırılabilir dosya yolu gereklidir",
|
||||
"extension_executing_command": "Komut yürütülüyor: %s\n",
|
||||
"extension_execution_failed_err": "yürütme başarısız oldu: %w\nhata: %s",
|
||||
"extension_execution_failed_stderr": "yürütme başarısız oldu: %w\nstderr: %s",
|
||||
"extension_execution_timed_out": "%v sonra yürütme zaman aşımına uğradı",
|
||||
"extension_failed_format_command": "komut biçimlendirilemedi: %w",
|
||||
"extension_failed_get_absolute_path": "mutlak yol alınamadı: %w",
|
||||
"extension_failed_get_extension": "uzantı alınamadı: %w",
|
||||
"extension_failed_get_output_path": "çıktı yolu alınamadı: %w\nhata: %s",
|
||||
"extension_failed_hash_executable": "çalıştırılabilir dosya hash'i alınamadı: %w",
|
||||
"extension_failed_marshal_registry": "uzantı kaydı sıralanamadı: %w",
|
||||
"extension_failed_parse_config": "yapılandırma dosyası ayrıştırılamadı: %w",
|
||||
"extension_failed_parse_registry": "uzantı kaydı ayrıştırılamadı: %w",
|
||||
"extension_failed_read_config": "yapılandırma dosyası okunamadı: %w",
|
||||
"extension_failed_read_output_file": "çıktı dosyası okunamadı: %w",
|
||||
"extension_failed_read_registry": "uzantı kaydı okunamadı: %w",
|
||||
"extension_failed_register": "uzantı kaydedilemedi: %w",
|
||||
"extension_failed_remove": "uzantı kaldırılamadı: %w",
|
||||
"extension_failed_verify_executable": "çalıştırılabilir dosya doğrulanamadı: %w",
|
||||
"extension_file_config_label": " Dosya Yapılandırması:\n",
|
||||
"extension_invalid_config_path": "geçersiz yapılandırma yolu: %w",
|
||||
"extension_invalid_definition": "geçersiz uzantı tanımı: %w",
|
||||
"extension_invalid_timeout": "geçersiz zaman aşımı değeri '%s': '30s' veya '1m' gibi bir süre olmalıdır: %w",
|
||||
"extension_invalid_timeout_format": "geçersiz zaman aşımı biçimi: %w",
|
||||
"extension_name_contains_spaces": "uzantı adı '%s' boşluk içeriyor - adlar boşluk içermemeli",
|
||||
"extension_name_detail_label": "Adı: %s\n",
|
||||
"extension_name_empty": "uzantı adı boş olamaz",
|
||||
"extension_name_label": "Uzantı: %s\n",
|
||||
"extension_name_required": "uzantı adı gereklidir",
|
||||
"extension_no_file_config": "dosya yapılandırması bulunamadı",
|
||||
"extension_no_output_file": "yapılandırmada çıktı dosyası belirtilmemiş",
|
||||
"extension_not_found": "uzantı %s bulunamadı",
|
||||
"extension_operation_not_found": "uzantı %s için işlem %s bulunamadı",
|
||||
"extension_operation_required": "en az bir işlem tanımlanmalıdır",
|
||||
"extension_operations_label": " İşlemler:\n",
|
||||
"extension_registered_success": "Uzantı başarıyla kaydedildi:\n",
|
||||
"extension_registry_not_initialized": "uzantı kaydı başlatılmadı",
|
||||
"extension_status_disabled": " Durum: DEVRE DIŞI - Karma doğrulama başarısız oldu: %v\n",
|
||||
"extension_status_enabled": " Durum: ETKİN\n",
|
||||
"extension_timeout_label": " Zaman Aşımı: %s\n",
|
||||
"extension_type_label": " Tür: %s\n",
|
||||
"extension_type_required": "uzantı türü gerekli",
|
||||
"extension_version_label": " Sürüm: %s\n",
|
||||
"extension_warning_load_registry": "Uyarı: uzantı kaydı yüklenemedi: %v\n",
|
||||
"fabric_command_complete": "Fabric Komutu Tamamlandı",
|
||||
"fabric_command_complete_with_pattern": "Fabric: %s Tamamlandı",
|
||||
"fetch_content_exceeds_limit": "getir: içerik çok büyük: %d baytı aşıyor",
|
||||
"fetch_content_not_utf8": "getir: içerik geçerli UTF-8 metni değil",
|
||||
"fetch_content_null_bytes": "getir: içerik boş baytlar içeriyor",
|
||||
"fetch_content_too_large": "getir: içerik çok büyük: %d bayt (maks %d bayt)",
|
||||
"fetch_error_create_request": "getir: istek oluşturulurken hata: %v",
|
||||
"fetch_error_fetching_url": "getir: URL getirilirken hata: %v",
|
||||
"fetch_error_reading_response": "getir: yanıt okunurken hata: %v",
|
||||
"fetch_http_error": "getir: HTTP hatası: %d - %s",
|
||||
"fetch_unknown_operation": "getir: bilinmeyen işlem %q (desteklenen: get)",
|
||||
"fetch_unsupported_content_type": "getir: desteklenmeyen içerik türü %q - yalnızca metin içeriğine izin verilir",
|
||||
"file_already_exists_choose_different": "dosya %s zaten mevcut. Lütfen farklı bir dosya adı seçin veya mevcut dosyayı kaldırın",
|
||||
"file_already_exists_not_overwriting": "dosya %s zaten mevcut, üzerine yazılmıyor. Mevcut dosyayı yeniden adlandırın veya farklı bir ad seçin",
|
||||
"file_manager_applied_operation": "%s işlemi %s üzerine uygulandı",
|
||||
"file_manager_empty_path": "dosya değişikliği %d için boş yol",
|
||||
"file_manager_failed_create_directory": "failed to create directory %s for file change %d: %w",
|
||||
"file_manager_failed_parse_json": "%s JSON ayrıştırılamadı: %w",
|
||||
"file_manager_failed_write_file": "failed to write file %s for file change %d: %w",
|
||||
"file_manager_file_content_too_large": "dosya değişikliği %d için dosya içeriği çok büyük: %d bayt",
|
||||
"file_manager_invalid_format_no_json_array": "geçersiz %s formatı: JSON dizisi bulunamadı",
|
||||
"file_manager_invalid_format_unbalanced_brackets": "geçersiz %s formatı: dengesiz parantezler",
|
||||
"file_manager_invalid_operation": "dosya değişikliği %d için geçersiz işlem: %s",
|
||||
"file_manager_suspicious_path": "dosya değişikliği %d için şüpheli yol: %s",
|
||||
"gemini_audio_data_too_small": "ses verisi çok küçük: %d bayt, minimum gerekli: %d",
|
||||
"gemini_empty_pcm_data": "boş PCM verisi sağlandı",
|
||||
"gemini_invalid_location_format": "geçersiz arama konumu formatı %q: saat dilimi (örn., 'America/Los_Angeles') veya dil kodu (örn., 'en-US') olmalı",
|
||||
"gemini_invalid_voice": "geçersiz ses '%s'. Geçerli sesler: %v",
|
||||
"gemini_no_audio_data": "TTS modelinden ses verisi alınmadı",
|
||||
"gemini_no_text_for_tts": "TTS oluşturma için metin içeriği bulunamadı",
|
||||
"gemini_pcm_data_too_large": "PCM verisi çok büyük: %d bayt, izin verilen maksimum: %d",
|
||||
"gemini_stream_error": "Hata: %v",
|
||||
"gemini_tts_failed": "TTS oluşturma başarısız oldu: %w",
|
||||
"gemini_unexpected_data_type": "beklenmeyen veri türü: %s, beklenen ses verisi",
|
||||
"gemini_voice_not_found": "ses '%s' bulunamadı",
|
||||
"gemini_wav_data_invalid": "oluşturulan WAV verisi geçersiz: %d bayt, minimum gerekli: %d",
|
||||
"gemini_wav_generation_failed": "WAV dosyası oluşturulamadı: %w",
|
||||
"githelper_failed_clone_repository": "depo klonlanamadı: %w",
|
||||
"githelper_failed_create_dest_directory": "hedef dizin oluşturulamadı: %w",
|
||||
"githelper_failed_create_temp_directory": "geçici dizin oluşturulamadı: %w",
|
||||
"githelper_failed_get_commit": "commit alınamadı: %w",
|
||||
"githelper_failed_get_head": "depo HEAD'i alınamadı: %w",
|
||||
"githelper_failed_get_tree": "ağaç alınamadı: %w",
|
||||
"githelper_failed_git_cli_clone": "git klonlama başarısız oldu: %w: %s",
|
||||
"githelper_failed_git_cli_fallback": "%w; git CLI geri dönüşü de başarısız oldu: %v",
|
||||
"grab_comments_from_youtube": "YouTube videosundaki yorumları al ve sohbete gönder",
|
||||
"grab_transcript_from_youtube": "YouTube videosundaki dökümü al ve sohbete gönder (varsayılan olarak kullanılır).",
|
||||
"grab_transcript_with_timestamps": "Zaman damgalı YouTube videosu dökümünü al ve sohbete gönder",
|
||||
"groups_items_number_out_of_range": "sayı %d aralık dışında",
|
||||
"help_message": "Bu yardım mesajını göster",
|
||||
"help_options_header": "Yardım Seçenekleri:",
|
||||
"html_readability_error": "html okunabilirliği uygulanamadığından orijinal girdiyi kullanın",
|
||||
"i18n_download_failed": "'%s' dili için çeviri indirilemedi: %v",
|
||||
"i18n_load_failed": "çeviri dosyası yüklenemedi: %v",
|
||||
"image_compression_jpeg_webp_only": "görüntü sıkıştırma yalnızca JPEG ve WebP formatlarıyla kullanılabilir, %s ile değil",
|
||||
"image_compression_range_error": "görüntü sıkıştırma 0 ile 100 arasında olmalı, %d alındı",
|
||||
"image_dimensions_help": "Görüntü boyutları: 1024x1024, 1536x1024, 1024x1536, otomatik (varsayılan: otomatik)",
|
||||
"image_file_already_exists": "görüntü dosyası zaten mevcut: %s",
|
||||
"image_parameters_require_image_file": "görüntü parametreleri (--image-size, --image-quality, --image-background, --image-compression) yalnızca --image-file ile kullanılabilir",
|
||||
"image_quality_help": "Görüntü kalitesi: düşük, orta, yüksek, otomatik (varsayılan: otomatik)",
|
||||
"invalid_config_path": "geçersiz yapılandırma yolu: %w",
|
||||
"invalid_image_background": "geçersiz görüntü arka planı '%s'. Desteklenen arka planlar: opak, şeffaf",
|
||||
"invalid_image_file_extension": "geçersiz görüntü dosyası uzantısı '%s'. Desteklenen formatlar: .png, .jpeg, .jpg, .webp",
|
||||
"invalid_image_quality": "geçersiz görüntü kalitesi '%s'. Desteklenen kaliteler: düşük, orta, yüksek, otomatik",
|
||||
"invalid_image_size": "geçersiz görüntü boyutu '%s'. Desteklenen boyutlar: 1024x1024, 1536x1024, 1024x1536, otomatik",
|
||||
"jina_error_creating_request": "istek oluşturulurken hata: %v",
|
||||
"jina_error_reading_response_body": "yanıt gövdesi okunurken hata: %v",
|
||||
"jina_error_sending_request": "istek gönderilirken hata: %v",
|
||||
"jina_label": "Jina AI",
|
||||
"jina_setup_description": "Jina AI Servisi - bir web sayfasını temiz, LLM dostu metin olarak almak için",
|
||||
"language_label": "Dil",
|
||||
"language_output_question": "Varsayılan çıktı dilinizi girin (örneğin: zh_CN)",
|
||||
"language_setup_description": "Dil - Varsayılan Yapay Zeka Sağlayıcısı Çıktı Dili",
|
||||
"list_all_available_models": "Mevcut tüm modelleri listele",
|
||||
"list_all_contexts": "Tüm bağlamları listele",
|
||||
"list_all_patterns": "Tüm desenleri listele",
|
||||
"list_all_registered_extensions": "Tüm kayıtlı uzantıları listele",
|
||||
"list_all_sessions": "Tüm oturumları listele",
|
||||
"list_all_strategies": "Tüm stratejileri listele",
|
||||
"list_all_vendors": "Tüm sağlayıcıları listele",
|
||||
"list_gemini_tts_voices": "Mevcut tüm Gemini TTS seslerini listele",
|
||||
"list_transcription_models": "Mevcut tüm transkripsiyon modellerini listele",
|
||||
"lmstudio_api_url_question": "%v URL'nizi girin (hatırlatıcı olarak, genellikle %v'dir)",
|
||||
"lmstudio_error_reading_response": "yanıt okunurken hata: %w",
|
||||
"lmstudio_failed_create_request": "istek oluşturulamadı: %w",
|
||||
"lmstudio_failed_decode_response": "yanıt kodu çözülemedi: %w",
|
||||
"lmstudio_failed_marshal_payload": "yük ayrıştırılamadı: %w",
|
||||
"lmstudio_failed_send_request": "istek gönderilemedi: %w",
|
||||
"lmstudio_invalid_response_missing_choices": "geçersiz yanıt biçimi: eksik veya boş seçenekler",
|
||||
"lmstudio_invalid_response_missing_content": "geçersiz yanıt biçimi: mesajda eksik veya dize olmayan içerik",
|
||||
"lmstudio_invalid_response_missing_message": "geçersiz yanıt biçimi: ilk seçenekte mesaj eksik",
|
||||
"lmstudio_invalid_response_missing_text": "geçersiz yanıt biçimi: ilk seçenekte eksik veya dize olmayan metin",
|
||||
"lmstudio_no_embeddings_returned": "gömülü ögeler döndürülmedi",
|
||||
"lmstudio_unexpected_status_code": "beklenmeyen durum kodu: %d",
|
||||
"model_context_length_ollama": "Model bağlam uzunluğu (yalnızca ollama'yı etkiler)",
|
||||
"model_for_transcription": "Transkripsiyon için kullanılacak model (sohbet modelinden ayrı)",
|
||||
"no_description_available": "Açıklama mevcut değil",
|
||||
"no_items_found": "Hiçbir %s bulunamadı",
|
||||
"no_notification_system_available": "bildirim sistemi mevcut değil",
|
||||
"notifications_no_provider_available": "bildirim sağlayıcısı mevcut değil",
|
||||
"number_of_latest_patterns": "Listelenecek en son desen sayısı",
|
||||
"ollama_cannot_parse_url": "URL '%s' ayrıştırılamıyor: %v",
|
||||
"ollama_chat_request_failed": "Sohbet isteği başarısız oldu: %v",
|
||||
"ollama_empty_address": "boş adres",
|
||||
"ollama_error_building_chat_url": "/chat URL'si oluşturulurken hata: %v",
|
||||
"ollama_error_creating_chat_request": "/chat isteği oluşturulurken hata: %v",
|
||||
"ollama_error_endpoint": "uç nokta test ediliyor",
|
||||
"ollama_error_getting_chat_body": "/chat gövdesi alınırken hata: %v",
|
||||
"ollama_error_marshalling_body": "gövde ayrıştırılırken hata: %v",
|
||||
"ollama_error_parse_upstream_response": "Hata: yukarı akış yanıtı ayrıştırılamadı",
|
||||
"ollama_error_prefix": "Hata: %s",
|
||||
"ollama_error_reading_body": "gövde okunurken hata: %v",
|
||||
"ollama_error_scanning_body": "gövde taranırken hata: %v",
|
||||
"ollama_error_unmarshalling_body": "gövde ayrıştırılamadı: %v",
|
||||
"ollama_error_writing_response": "yanıt yazılırken hata: %v",
|
||||
"ollama_failed_create_request": "istek oluşturulamadı",
|
||||
"ollama_failed_decode_data_url": "veri URL'si kodu çözülemedi: %v",
|
||||
"ollama_failed_fetch_image": "%s adresinden görsel getirilemedi: %s",
|
||||
"ollama_failed_scan_sse_stream": "SSE yanıt akışı taranamadı: %v",
|
||||
"ollama_failed_unmarshal_fabric_response": "Fabric yanıtı ayrıştırılamadı",
|
||||
"ollama_http_timeout_question": "HTTP zaman aşımını girin (örn. 20d, 60s)",
|
||||
"ollama_invalid_address": "geçersiz adres: %w",
|
||||
"ollama_invalid_address_missing_host": "geçersiz adres: ana bilgisayar eksik",
|
||||
"ollama_invalid_address_missing_hostname": "geçersiz adres: ana bilgisayar adı eksik",
|
||||
"ollama_invalid_address_path_not_allowed": "geçersiz adres: yol bileşeni yalın adreste izin verilmiyor",
|
||||
"ollama_invalid_data_url_format": "geçersiz veri URL biçimi",
|
||||
"ollama_invalid_http_timeout_using_default": "geçersiz HTTP zaman aşımı '%s': %v, varsayılan kullanılıyor",
|
||||
"ollama_invalid_num_ctx_in_request": "istekte geçersiz num_ctx: %v",
|
||||
"ollama_invalid_request_body": "geçersiz istek gövdesi",
|
||||
"ollama_no_content_from_upstream": "yukarı akış Fabric sunucusundan içerik alınamadı",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx, izin verilen maksimum değer olan %d'yi aşıyor",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx bir sayı olmalı, geçersiz tür alındı",
|
||||
"ollama_num_ctx_must_be_finite": "num_ctx sonlu bir sayı olmalı",
|
||||
"ollama_num_ctx_must_be_integer": "num_ctx bir tam sayı olmalı, kesirli kısmı olan ondalık sayı alındı",
|
||||
"ollama_num_ctx_must_be_positive": "num_ctx pozitif olmalı, alındı: %d",
|
||||
"ollama_num_ctx_must_be_valid_number": "num_ctx geçerli bir sayı olmalı",
|
||||
"ollama_num_ctx_must_be_valid_number_got": "num_ctx geçerli bir sayı olmalı, alındı: %s",
|
||||
"ollama_num_ctx_value_out_of_range": "num_ctx değeri aralık dışında",
|
||||
"ollama_num_ctx_value_too_large": "num_ctx değeri çok büyük: %d",
|
||||
"ollama_sse_buffer_limit": "SSE satırı 1MB arabellek sınırını aşıyor - veri satırı çok büyük",
|
||||
"ollama_upstream_non_2xx": "Yukarı akış Fabric sunucusu 2xx olmayan durum kodu %d döndürdü: %s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "Yukarı akış Fabric sunucusu 2xx olmayan durum kodu %d döndürdü ve gövde okunamadı: %v",
|
||||
"ollama_upstream_request_failed": "yukarı akış Fabric sunucusuna ulaşılamadı",
|
||||
"ollama_upstream_returned_status": "yukarı akış Fabric sunucusu %d durum kodunu döndürdü",
|
||||
"ollama_warning_no_content": "Uyarı: yukarı akış Fabric sunucusundan içerik alınamadı",
|
||||
"ollama_warning_parse_variables": "Uyarı: options.variables JSON olarak ayrıştırılamadı: %v",
|
||||
"openai_api_base_url_not_configured": "API temel URL'si %s sağlayıcısı için yapılandırılmamış",
|
||||
"openai_audio_ffmpeg_failed": "ffmpeg başarısız oldu: %v: %s",
|
||||
"openai_audio_ffmpeg_not_found_install": "ffmpeg bulunamadı: lütfen yükleyin",
|
||||
"openai_audio_file_exceeds_limit_enable_split": "Dosya %s 25MB sınırını aşıyor; otomatik bölmeyi etkinleştirmek için --split-media-file kullanın",
|
||||
"openai_audio_file_exceeds_limit_splitting": "Dosya %s boyut sınırından daha büyük... parçalara ayrılıyor...",
|
||||
"openai_audio_model_not_supported_for_transcription": "model '%s' transkripsiyon için desteklenmiyor",
|
||||
"openai_audio_running_ffmpeg_split_chunks": "Sesi %d saniyelik parçalara ayırmak için ffmpeg çalıştırılıyor...",
|
||||
"openai_audio_unable_to_split_acceptable_size_chunks": "dosyayı kabul edilebilir boyutta parçalara ayıramıyor",
|
||||
"openai_audio_unsupported_audio_format": "desteklenmeyen ses formatı '%s'",
|
||||
"openai_audio_using_model_to_transcribe_part": "%s modeli kullanılarak %d. parça yazıya dökülüyor (dosya adı: %s)...",
|
||||
"openai_compatible_unknown_static_model_list": "bilinmeyen statik model listesi: %s",
|
||||
"openai_failed_to_create_models_url": "modeller URL'si oluşturulamadı: %w",
|
||||
"openai_image_failed_to_create_directory": "%s dizini oluşturulamadı: %w",
|
||||
"openai_image_failed_to_decode_image_data": "görsel verisi çözülemedi: %w",
|
||||
"openai_image_failed_to_save_image": "görsel %s konumuna kaydedilemedi: %w",
|
||||
"openai_image_saved_to": "Görsel kaydedildi: %s",
|
||||
"openai_model_no_image_generation": "'%s' modeli görsel oluşturmayı desteklemiyor. Desteklenen modeller: %s",
|
||||
"openai_models_rate_limited": "%s sağlayıcısından modeller alınırken hız sınırı aşıldı; %s saniye sonra tekrar deneyin",
|
||||
"openai_models_response_too_large": "%s sağlayıcısından gelen modeller yanıtı çok büyük (>%d bayt)",
|
||||
"openai_unable_to_parse_models_response": "modeller yanıtı ayrıştırılamadı; ham yanıt: %s",
|
||||
"openai_unexpected_status_code_read_error": "unexpected status code: %d from provider %s (failed to read response body: %v)",
|
||||
"openai_unexpected_status_code_with_body": "unexpected status code: %d from provider %s, response body: %s",
|
||||
"openai_warning_model_no_image_generation": "Uyarı: '%s' modeli görsel oluşturmayı desteklemiyor. Desteklenen modeller: %s. Görsel oluşturmak için -m gpt-5.2 kullanmayı düşünebilirsiniz.\n",
|
||||
"optional_marker": "(isteğe bağlı)",
|
||||
"options_placeholder": "[SEÇENEKLER]",
|
||||
"output_entire_session": "Tüm oturumu (geçici olanı da dahil) çıktı dosyasına yaz",
|
||||
"output_full": "Çıktı: %s",
|
||||
"output_raw_list_shell_completion": "Başlıklar/biçimlendirme olmadan ham listeyi çıkar (kabuk tamamlama için)",
|
||||
"output_to_file": "Dosyaya çıktı al",
|
||||
"output_truncated": "Çıktı: %s...",
|
||||
"output_video_metadata": "Video meta verilerini çıkar",
|
||||
"path_to_yaml_config": "YAML yapılandırma dosyası yolu",
|
||||
"pattern_not_found_list_available": "'%s' deseni bulunamadı. Mevcut desenleri görmek için 'fabric -l' komutunu çalıştırın",
|
||||
"pattern_invalid_name": "geçersiz desen adı: %q",
|
||||
"pattern_not_found_no_patterns": "'%s' deseni bulunamadı.\n\nHiçbir desen yüklü değil! Bunu düzeltmek için:\n • Desenleri yapılandırmak ve indirmek için 'fabric --setup' komutunu çalıştırın\n • Veya desenleri doğrudan indirip güncellemek için 'fabric -U' komutunu çalıştırın",
|
||||
"pattern_variables_help": "Desen değişkenleri için değerler, örn. -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "Depo kopyalanıyor %s (yol: %s)...\n",
|
||||
"patterns_debug_included_custom_directory": "📂 Özel dizinden desenler de dahil edildi: %s\n",
|
||||
"patterns_detected_old_path": "🔄 Eski desen yolu 'patterns' algılandı, 'data/patterns' konumuna geçiş deneniyor...",
|
||||
"patterns_download_success": "✅ Desenler başarıyla indirildi ve %s konumuna yüklendi\n",
|
||||
"patterns_downloaded_temp": "%d desen geçici dizine indirildi\n",
|
||||
"patterns_downloading": "Desenler indiriliyor ve %s dolduruluyor...\n",
|
||||
"patterns_error_create_directory": "desen dizini oluşturulamadı: %v",
|
||||
"patterns_error_get_home_directory": "ana dizin alınamadı: %v",
|
||||
"patterns_error_load_from_file": "%s dosyasından desen yüklenemedi: %w",
|
||||
"patterns_error_read_pattern_file": "%s desen dosyası okunamadı: %v",
|
||||
"patterns_error_read_unique_file": "benzersiz desenler dosyası okunamadı. Lütfen --updatepatterns (%s) komutunu çalıştırın",
|
||||
"patterns_error_resolve_file_path": "dosya yolu çözümlenemedi: %v",
|
||||
"patterns_error_save_pattern": "desen kaydedilemedi: %v",
|
||||
"patterns_failed_access_directory": "desenler dizini '%s' erişilemedi: %w",
|
||||
"patterns_failed_create_temp_dir": "geçici dizin oluşturulamadı: %w",
|
||||
"patterns_failed_create_temp_folder": "geçici desenler klasörü oluşturulamadı: %w",
|
||||
"patterns_failed_download_from_git": "desenler git deposundan indirilemedi: %w",
|
||||
"patterns_failed_download_from_repo": "%s konumundan desenler indirilemedi: %w",
|
||||
"patterns_failed_loaded_marker": "yüklü işaretleyici dosyası '%s' oluşturulamadı: %w",
|
||||
"patterns_failed_move_patterns": "desenler yapılandırma dizinine taşınamadı: %w",
|
||||
"patterns_failed_move_test_patterns": "test desenleri geçici klasöre taşınamadı: %w",
|
||||
"patterns_failed_read_directory": "desenler dizini okunamadı: %w",
|
||||
"patterns_failed_read_temp_directory": "geçici desenler dizini okunamadı: %w",
|
||||
"patterns_failed_unique_file": "benzersiz desenler dosyası oluşturulamadı: %w",
|
||||
"patterns_failed_write_unique_file": "benzersiz desenler dosyası yazılamadı: %w",
|
||||
"patterns_found_new_path": "✅ Found %d patterns at new path '%s', updating configuration...\n",
|
||||
"patterns_git_repo_folder_question": "Desenlerin saklandığı Git deposundaki varsayılan klasörü girin",
|
||||
"patterns_git_repo_url_question": "Desenler için varsayılan Git deposu URL'sini girin",
|
||||
"patterns_loader_label": "Desen Yükleyici",
|
||||
"patterns_no_patterns_copied": "hiçbir desen %s konumuna başarıyla kopyalanamadı",
|
||||
"patterns_no_patterns_found_in_directories": "%s ve %s dizinlerinde desen bulunamadı",
|
||||
"patterns_no_patterns_found_in_directory": "%s dizininde desen bulunamadı",
|
||||
"patterns_no_patterns_migration_failed": "%s yolundaki depoda desen bulunamadı ve geçiş başarısız oldu: %w",
|
||||
"patterns_not_found_header": "⚠️ Desen bulunamadı!",
|
||||
"patterns_option_run_setup": "Seçenek 1 (Önerilen): Desenleri indirmek için kurulumu çalıştırın",
|
||||
"patterns_option_run_setup_command": "fabric --setup",
|
||||
"patterns_option_run_update": "Seçenek 2: Desenleri doğrudan indir/güncelle",
|
||||
"patterns_option_run_update_command": "fabric -U",
|
||||
"patterns_preserve_warning": "Uyarı: özel desen '%s' korunamadı: %v\n",
|
||||
"patterns_preserved_custom_pattern": "Korunan özel desen: %s\n",
|
||||
"patterns_required_to_work": "Fabric'in çalışması için desenler gereklidir. Bunu düzeltmek için:",
|
||||
"patterns_saving_updated_configuration": "💾 Güncellenmiş yapılandırma kaydediliyor (yol '%s' konumundan '%s' konumuna değişti)...\n",
|
||||
"patterns_setup_description": "Desenler - Desenleri indirir",
|
||||
"patterns_unable_to_find_or_migrate": "mevcut '%s' yolunda desenler bulunamadı veya yeni yapıya geçilemedi",
|
||||
"patterns_unique_file_created": "📝 %d desen içeren benzersiz desenler dosyası oluşturuldu\n",
|
||||
"patterns_warning_custom_directory": "Uyarı: Özel desenler dizini %s okunamadı: %v\n",
|
||||
"patterns_warning_remove_test_folder": "Uyarı: test geçici klasörü '%s' kaldırılamadı: %v\n",
|
||||
"perplexity_api_key_not_configured": "%s için API anahtarı yapılandırılmadı. %s ortam değişkenini ayarlayın veya %s yapılandırmak için 'fabric --setup' komutunu çalıştırın",
|
||||
"perplexity_api_request_failed": "Perplexity API isteği başarısız oldu: %w",
|
||||
"perplexity_citations_header": "\n\n**Alıntılar:**\n",
|
||||
"perplexity_failed_configure": "Perplexity yapılandırılamadı: %w",
|
||||
"perplexity_streaming_error": "Perplexity akış hatası: %v",
|
||||
"plugin_configured": " ✓",
|
||||
"plugin_enable_bool_question": "%v %v etkinleştirilsin mi (true/false)",
|
||||
"plugin_enter_value": "%v %v değerinizi girin",
|
||||
"plugin_invalid_bool": "geçersiz boolean: %q",
|
||||
"plugin_invalid_boolean_value": "geçersiz boolean değeri: %v",
|
||||
"plugin_not_configured": " ⚠️ YAPILANDIRILMADI",
|
||||
"plugin_question_bool": "%v%v (doğru/yanlış, '%s' için boş bırakın veya değeri kaldırmak için '%v' yazın):",
|
||||
"plugin_question_optional": "%v%v (atlamak için boş bırakın):",
|
||||
"plugin_question_with_default": "%v%v ('%s' için boş bırakın veya değeri kaldırmak için '%v' yazın):",
|
||||
"plugin_registry_could_not_find_vendor": "sağlayıcı bulunamadı",
|
||||
"plugin_registry_error_configuring_custom_patterns": "Özel Desenler yapılandırılırken hata oluştu: %w",
|
||||
"plugin_registry_model_not_available_for_vendor": "%s modeli %s sağlayıcısı için mevcut değil",
|
||||
"plugin_registry_run_setup_select_defaults": "lütfen 'fabric --setup' komutunu çalıştırın ve varsayılan modeli ve sağlayıcıyı seçin",
|
||||
"plugin_setting_not_valid": "%v=%v, geçerli değil",
|
||||
"plugin_setup_configured": "[%v] yapılandırıldı",
|
||||
"plugin_setup_skipped": "[%v] atlandı\n",
|
||||
"prefer_playlist_over_video": "URL'de her iki kimlik de varsa oynatma listesini videoya tercih et",
|
||||
"print_context": "Bağlamı yazdır",
|
||||
"print_current_version": "Mevcut sürümü yazdır",
|
||||
"print_metadata_to_stderr": "Meta verileri (girdi/çıktı jetonları) stderr'e yazdır",
|
||||
"print_pattern_contents": "Adlandırılmış desenin içeriğini terminale yazdır",
|
||||
"print_session": "Oturumu yazdır",
|
||||
"register_new_extension": "Yapılandırma dosyası yolundan yeni bir uzantı kaydet",
|
||||
"remove_registered_extension": "Kayıtlı bir uzantıyı adına göre kaldır",
|
||||
"required_marker": "[gerekli]",
|
||||
"run_setup_for_reconfigurable_parts": "Fabric'in yeniden yapılandırılabilir tüm bölümleri için kurulumu çalıştır",
|
||||
"save_generated_image_to_file": "Oluşturulan görüntüyü belirtilen dosya yoluna kaydet (örn. 'output.png')",
|
||||
"scrape_website_url": "Jina AI kullanarak web sitesi URL'sini Markdown'a dönüştür",
|
||||
"scraping_not_configured": "Kazıma işlevi yapılandırılmadı. Kazımayı etkinleştirmek için lütfen Jina'yı kurun",
|
||||
"search_question_jina": "Jina AI kullanarak soru ara",
|
||||
"seed_for_lmm_generation": "LMM üretimi için kullanılacak başlangıç değeri",
|
||||
"send_desktop_notification": "Komut tamamlandığında masaüstü bildirimi gönder",
|
||||
"serve_fabric_api_ollama_endpoints": "Fabric REST API'sini Ollama uç noktalarıyla sun",
|
||||
"serve_fabric_rest_api": "Fabric REST API'sini sun",
|
||||
"server_api_key_required": "bir API anahtarı olmadan döngüsel olmayan bir adres olan %s üzerinde hizmet vermeyi reddediyor: --api-key veya FABRIC_API_KEY ayarlayın ya da 127.0.0.1:8080 gibi bir döngüsel adres bağlayın",
|
||||
"server_chat_error": "Hata: %v",
|
||||
"server_error_marshaling_response": "yanıt sıralanırken hata oluştu: %v",
|
||||
"server_error_writing_response": "yanıt yazılırken hata oluştu: %v",
|
||||
"server_invalid_request_format": "geçersiz istek formatı: %v",
|
||||
"server_no_api_key_warning": "REST API sunucusu API anahtarı doğrulaması olmadan başlatılıyor. Bu durum güvenlik riskleri oluşturabilir.",
|
||||
"sessions_creating_new": "Yeni oturum oluşturuluyor: %s\n",
|
||||
"set_debug_level": "Hata ayıklama düzeyini ayarla (0=kapalı, 1=temel, 2=ayrıntılı, 3=izleme, 4=tel)",
|
||||
"set_frequency_penalty": "Frekans cezasını ayarla",
|
||||
"set_location_web_search": "Web arama sonuçları için konumu ayarla (örn. 'America/Los_Angeles')",
|
||||
"set_presence_penalty": "Mevcudiyet cezasını ayarla",
|
||||
"set_reasoning_thinking_level": "Akıl yürütme/düşünme düzeyini ayarla (örn. kapalı, düşük, orta, yüksek veya Anthropic veya Google Gemini için sayısal jetonlar)",
|
||||
"set_temperature": "Sıcaklığı ayarla",
|
||||
"set_top_p": "En üst P'yi ayarla",
|
||||
"setup_add_more_providers_later": "'fabric --setup' ile daha sonra ek sağlayıcılar ekleyebileceksiniz",
|
||||
"setup_ai_provider_required": "Fabric'in çalışması için en az bir yapay zeka sağlayıcısına ihtiyacı var.",
|
||||
"setup_available_ai_providers": "Mevcut Yapay Zeka Sağlayıcıları:",
|
||||
"setup_available_plugins": "Mevcut eklentiler:",
|
||||
"setup_complete_header": "✅ Kurulum tamamlandı! Artık Fabric'i kullanabilirsiniz.",
|
||||
"setup_configure_more": "• Daha fazla ayar yapılandır: fabric --setup",
|
||||
"setup_enter_ai_provider_number": "Yapay Zeka Sağlayıcı Numarası",
|
||||
"setup_failed_download_patterns": "desenler indirilemedi: %w",
|
||||
"setup_failed_download_strategies": "stratejiler indirilemedi: %w",
|
||||
"setup_failed_set_defaults": "varsayılan sağlayıcı ve model ayarlanamadı: %w",
|
||||
"setup_invalid_selection": "geçersiz seçim: %s",
|
||||
"setup_list_patterns": "• Mevcut desenleri listele: fabric -l",
|
||||
"setup_next_steps": "Sonraki adımlar:",
|
||||
"setup_no_ai_provider_selected": "hiçbir yapay zeka sağlayıcısı seçilmedi - en az biri gerekli",
|
||||
"setup_optional_configuration_header": "━━━ İSTEĞE BAĞLI YAPILANDIRMA ━━━\n\nİsteğe Bağlı Araçlar",
|
||||
"setup_plugin_number": "Eklenti Numarası",
|
||||
"setup_plugin_prompt": "Kurulacak eklentinin numarasını girin",
|
||||
"setup_required_configuration_header": "━━━ GEREKLİ YAPILANDIRMA ━━━\n\nYapay Zeka Sağlayıcıları [en az biri gerekli]",
|
||||
"setup_required_tools": "Gerekli Araçlar",
|
||||
"setup_step_configure_ai_provider": "🤖 Adım 3: Bir yapay zeka sağlayıcısı yapılandır",
|
||||
"setup_step_downloading_patterns": "📥 Adım 1: Desenler indiriliyor (Fabric'in çalışması için gerekli)...",
|
||||
"setup_step_downloading_strategies": "📥 Adım 2: Stratejiler indiriliyor (Fabric'in çalışması için gerekli)...",
|
||||
"setup_step_setting_defaults": "⚙️ Adım 4: Varsayılan sağlayıcı ve model ayarlanıyor...",
|
||||
"setup_try_pattern": "• Bir desen dene: echo 'metniniz' | fabric --pattern summarize",
|
||||
"setup_validation_ai_provider_configured": "✓ Yapay Zeka Sağlayıcısı yapılandırıldı",
|
||||
"setup_validation_ai_provider_missing": "✗ Yapay Zeka Sağlayıcısı yapılandırılmadı - Fabric'in çalışması için gerekli",
|
||||
"setup_validation_complete": "✓ Tüm gerekli bileşenler yapılandırıldı!",
|
||||
"setup_validation_defaults_configured": "✓ Varsayılan sağlayıcı/model ayarlandı: %s/%s",
|
||||
"setup_validation_defaults_missing": "✗ Varsayılan sağlayıcı/model ayarlanmadı - Fabric'in çalışması için gerekli",
|
||||
"setup_validation_header": "Yapılandırma Durumu:",
|
||||
"setup_validation_incomplete_help": "Eksik öğeleri yapılandırmak için 'fabric --setup' komutunu tekrar çalıştırın,\nveya desenleri ve stratejileri indirmek için 'fabric -U' komutunu çalıştırın.",
|
||||
"setup_validation_incomplete_warning": "⚠️ Kurulum tamamlanmadı! Gerekli bileşenler eksik.",
|
||||
"setup_validation_patterns_configured": "✓ Desenler indirildi",
|
||||
"setup_validation_patterns_missing": "✗ Desenler bulunamadı - Fabric'in çalışması için gerekli",
|
||||
"setup_validation_strategies_configured": "✓ Stratejiler indirildi",
|
||||
"setup_validation_strategies_missing": "✗ Stratejiler bulunamadı - Fabric'in çalışması için gerekli",
|
||||
"setup_welcome_header": "🎉 Fabric'e hoş geldiniz! Kurulumunuzu yapalım.",
|
||||
"show_dry_run": "Modele gerçekten göndermeden, ne gönderileceğini göster.",
|
||||
"specify_language_code": "Sohbet için Dil Kodunu belirtin, örn. -g=en -g=zh -g=pt-BR -g=pt-PT",
|
||||
"specify_vendor_for_model": "Seçilen model için sağlayıcıyı belirtin (örn., -V \"LM Studio\" -m openai/gpt-oss-20b)",
|
||||
"split_media_files_ffmpeg": "25MB'tan büyük ses/video dosyalarını ffmpeg kullanarak ayırın",
|
||||
"spotify_api_request_failed": "API isteği başarısız oldu: durum %d, gövde: %s",
|
||||
"spotify_audio_preview_label": "**Ses Önizlemesi**: %s",
|
||||
"spotify_client_id_question": "Spotify İSTEMCİ KİMLİĞİNİZİ girin",
|
||||
"spotify_client_secret_question": "Spotify İSTEMCİ SIRRINIZI girin",
|
||||
"spotify_description_header": "## Açıklama",
|
||||
"spotify_duration_label": "**Süre**: %d dakika",
|
||||
"spotify_episode_header": "# Spotify Bölümü",
|
||||
"spotify_error_getting_metadata": "Spotify meta verileri alınırken hata oluştu: %v",
|
||||
"spotify_explicit_label": "**Açık İçerik**: %v",
|
||||
"spotify_failed_create_request": "istek oluşturulamadı: %w",
|
||||
"spotify_failed_create_token_request": "jeton isteği oluşturulamadı: %w",
|
||||
"spotify_failed_decode_token_response": "jeton yanıtı çözümlenemedi: %w",
|
||||
"spotify_failed_execute_request": "istek yürütülemedi: %w",
|
||||
"spotify_failed_get_access_token": "erişim jetonu alınamadı: durum %d, gövde: %s",
|
||||
"spotify_failed_get_show_episodes": "program bölümleri alınamadı: %w",
|
||||
"spotify_failed_parse_episode_metadata": "bölüm meta verileri ayrıştırılamadı: %w",
|
||||
"spotify_failed_parse_episodes": "bölümler ayrıştırılamadı: %w",
|
||||
"spotify_failed_parse_search_results": "arama sonuçları ayrıştırılamadı: %w",
|
||||
"spotify_failed_parse_show_metadata": "program meta verileri ayrıştırılamadı: %w",
|
||||
"spotify_failed_read_response_body": "yanıt gövdesi okunamadı: %w",
|
||||
"spotify_failed_request_access_token": "erişim jetonu isteği başarısız oldu: %w",
|
||||
"spotify_invalid_url": "geçersiz Spotify URL'si, program veya bölüm kimliği alınamıyor: '%s'",
|
||||
"spotify_label": "Spotify",
|
||||
"spotify_language_field_label": "**Dil**: %s",
|
||||
"spotify_languages_label": "**Diller**: %s",
|
||||
"spotify_media_type_label": "**Medya Türü**: %s",
|
||||
"spotify_no_episode_found": "bu kimliğe sahip bölüm bulunamadı: %s",
|
||||
"spotify_no_show_found": "bu kimliğe sahip program bulunamadı: %s",
|
||||
"spotify_not_configured": "Spotify yapılandırılmamış, lütfen kurulum prosedürünü çalıştırın",
|
||||
"spotify_publisher_label": "**Yayıncı**: %s",
|
||||
"spotify_release_date_label": "**Yayın Tarihi**: %s",
|
||||
"spotify_search_description_label": "- **Açıklama**: %s",
|
||||
"spotify_search_episodes_label": "- **Bölümler**: %d",
|
||||
"spotify_search_failed": "arama başarısız oldu: %w",
|
||||
"spotify_search_publisher_label": "- **Yayıncı**: %s",
|
||||
"spotify_search_results_header": "# Spotify Arama Sonuçları",
|
||||
"spotify_search_url_label": "- **URL**: %s",
|
||||
"spotify_setup_description": "Spotify - Spotify'dan podcast/program meta verilerini almak için",
|
||||
"spotify_show_header": "# Spotify Podcast/Programı",
|
||||
"spotify_show_name_label": "**Program**: %s",
|
||||
"spotify_title_label": "**Başlık**: %s",
|
||||
"spotify_total_episodes_label": "**Toplam Bölüm Sayısı**: %d",
|
||||
"spotify_url_help": "Meta verileri almak ve sohbete göndermek için Spotify podcast veya bölüm URL'si",
|
||||
"spotify_url_label": "**URL**: %s",
|
||||
"start_tag_thinking_sections": "Düşünme bölümleri için başlangıç etiketi",
|
||||
"storage_error_delete": "%s silinemedi: %v",
|
||||
"storage_error_load": "%s yüklenemedi: %v",
|
||||
"storage_error_marshal": "%s serileştirilemedi: %s",
|
||||
"storage_error_read_directory": "dizinden öğeler okunamadı: %v",
|
||||
"storage_error_rename": "%s, %s olarak yeniden adlandırılamadı: %v",
|
||||
"storage_error_resolve_directory": "dizin yolu çözümlenemedi: %v",
|
||||
"storage_error_save": "%s kaydedilemedi: %v",
|
||||
"storage_error_stat_entry": "%s girdisi hakkında bilgi alınamadı: %v",
|
||||
"storage_error_unmarshal": "%s seri durumdan çıkarılamadı: %s",
|
||||
"storage_invalid_name": "geçersiz ad: %q",
|
||||
"strategies_available_header": "Mevcut Stratejiler:",
|
||||
"strategies_cloning_repository": "Depo klonlanıyor %s (yol: %s)...\n",
|
||||
"strategies_download_success": "✅ Stratejiler başarıyla indirildi ve %s konumuna kuruldu\n",
|
||||
"strategies_downloaded_count": "%d strateji indirildi\n",
|
||||
"strategies_downloading": "Stratejiler indiriliyor ve %s dolduruluyor...\n",
|
||||
"strategies_failed_create_directory": "stratejiler dizini oluşturulamadı: %w",
|
||||
"strategies_failed_download": "stratejiler indirilemedi: %w",
|
||||
"strategies_git_repo_folder_question": "Stratejilerin saklandığı Git deposundaki varsayılan klasörü girin",
|
||||
"strategies_git_repo_url_question": "Stratejiler için varsayılan Git deposu URL'sini girin",
|
||||
"strategies_home_dir_error": "ana dizin alınamadı: %v",
|
||||
"strategies_home_dir_fallback": "ana dizin alınamadı: %v, bunun yerine mevcut dizin kullanılıyor",
|
||||
"strategies_label": "İstem Stratejileri",
|
||||
"strategies_none_found": "strateji bulunamadı. Stratejileri indirmek için lütfen 'fabric --setup' komutunu çalıştırın",
|
||||
"strategies_setup_description": "Stratejiler - İstem Stratejilerini (düşünce zinciri gibi) indirir",
|
||||
"strategy_not_found": "desen %s bulunamadı. Lütfen listeyi görmek için 'fabric --liststrategies' komutunu çalıştırın.",
|
||||
"strategy_path_traversal": "desen adı %q desen dizininin dışına çözümleniyor",
|
||||
"stream_help": "Akış",
|
||||
"suppress_thinking_tags": "Düşünme etiketleri arasına alınmış metni gizle",
|
||||
"template_datetime_error_invalid_number": "göreceli zamanda geçersiz sayı: %q",
|
||||
"template_datetime_error_invalid_relative_format": "geçersiz göreceli zaman biçimi",
|
||||
"template_datetime_error_invalid_unit": "geçersiz zaman birimi: %q",
|
||||
"template_datetime_error_relative_requires_value": "göreceli zaman bir değer gerektirir",
|
||||
"template_datetime_error_unknown_operation": "tarihsaat: bilinmeyen işlem %q",
|
||||
"template_extension_error": "uzantı %s hatası: %v",
|
||||
"template_file_error_expand_home_dir": "dosya: ana dizin genişletilemedi: %v",
|
||||
"template_file_error_invalid_line_count": "dosya: geçersiz satır sayısı %q",
|
||||
"template_file_error_line_count_positive": "dosya: satır sayısı pozitif olmalıdır",
|
||||
"template_file_error_open_file": "dosya: açılamadı: %v",
|
||||
"template_file_error_path_contains_parent_ref": "dosya: yol '..' içeremez",
|
||||
"template_file_error_read_file": "dosya: okunamadı: %v",
|
||||
"template_file_error_scanner_read": "dosya: okuma hatası: %v",
|
||||
"template_file_error_size_exceeds_limit": "dosya: boyut %d, %d baytlık sınırı aşıyor",
|
||||
"template_file_error_stat_file": "dosya: dosya durumu alınamadı: %v",
|
||||
"template_file_error_stat_open_file": "dosya: durum alınamadı: %v",
|
||||
"template_file_error_tail_requires_path_lines": "dosya: tail, yol|satır biçimini gerektirir",
|
||||
"template_file_error_unknown_operation": "dosya: bilinmeyen işlem %q (desteklenenler: read, tail, exists, size, modified)",
|
||||
"template_file_log_cleaned_path": "Dosya: temizlenmiş yol %q",
|
||||
"template_file_log_exists_for_path": "Dosya: var=%v, yol %q için",
|
||||
"template_file_log_modified_for_path": "Dosya: değiştirildi=%q, yol %q için",
|
||||
"template_file_log_operation_value": "Dosya: işlem=%q değer=%q",
|
||||
"template_file_log_read_bytes": "Dosya: %d bayt okundu",
|
||||
"template_file_log_read_total_return_last": "Dosya: toplam %d satır okundu, son %d döndürülüyor",
|
||||
"template_file_log_reading_last_lines": "File: reading last %d lines from %q",
|
||||
"template_file_log_size_for_path": "Dosya: boyut=%d, yol %q için",
|
||||
"template_file_log_tail_returning_lines": "Dosya: tail %d satır döndürüyor",
|
||||
"template_file_log_validating_path": "Dosya: yol doğrulanıyor %q",
|
||||
"template_hash_open_file": "dosya açılamadı: %w",
|
||||
"template_hash_read_file": "dosya okunamadı: %w",
|
||||
"template_missing_required_variable": "gerekli değişken eksik: %s",
|
||||
"template_plugin_error": "eklenti %s hatası: %v",
|
||||
"template_processing_stuck": "şablon işleme takıldı - potansiyel sonsuz döngü",
|
||||
"template_sys_error_env_requires_var": "ortam: değişken adı gerekli",
|
||||
"template_sys_error_home": "ana dizin alınamadı: %v",
|
||||
"template_sys_error_hostname": "ana bilgisayar adı alınamadı: %v",
|
||||
"template_sys_error_pwd": "çalışma dizini alınamadı: %v",
|
||||
"template_sys_error_unknown_operation": "sistem: bilinmeyen işlem %q",
|
||||
"template_sys_error_user": "mevcut kullanıcı alınamadı: %v",
|
||||
"template_text_empty_input": "metin: %q işlemi için boş giriş",
|
||||
"template_text_unknown_operation": "metin: bilinmeyen metin işlemi %q (desteklenenler: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "bilinmeyen eklenti ad alanı: %s",
|
||||
"template_utils_failed_get_absolute_path": "mutlak yol alınamadı: %w",
|
||||
"template_utils_failed_get_home_dir": "kullanıcı ana dizini alınamadı: %w",
|
||||
"template_utils_path_not_exist": "yol mevcut değil: %w",
|
||||
"transcription_model_required": "transkripsiyon modeli gereklidir (--transcribe-model kullanın)",
|
||||
"transparent_background_png_webp_only": "şeffaf arka plan yalnızca PNG ve WebP biçimleriyle kullanılabilir, %s ile değil",
|
||||
"tts_audio_generated_successfully": "TTS sesi başarıyla oluşturuldu ve şuraya kaydedildi: %s\n",
|
||||
"tts_model_requires_audio_output": "TTS modeli '%s' ses çıkışı gerektirir. Lütfen -o bayrağıyla bir ses çıkış dosyası belirtin (örn: -o output.wav)",
|
||||
"tts_voice_name": "Desteklenen modeller için TTS ses adı (örn: Kore, Charon, Puck)",
|
||||
"unsupported_conversion": "desteklenmeyen dönüştürme: %v öğesinden %v öğesine",
|
||||
"update_patterns": "Desenleri Güncelle",
|
||||
"usage_header": "Kullanım:",
|
||||
"use_model_defaults_raw_help": "Modelin varsayılanlarını sohbet seçeneklerini (temperature, top_p vb.) göndermeden kullanın. Yalnızca OpenAI uyumlu sağlayıcıları etkiler. Anthropic modelleri, modele özgü gereksinimlere uymak için her zaman akıllı parametre seçimi kullanır.",
|
||||
"util_error_accessing_config_path": "varsayılan yapılandırma yoluna erişimde hata: %w",
|
||||
"util_error_determine_home_directory": "kullanıcı ana dizini belirlenemedi: %w",
|
||||
"util_error_get_absolute_path": "mutlak yol alınamadı",
|
||||
"util_error_path_is_empty": "yol boş",
|
||||
"util_error_resolve_home_directory": "ana dizin çözümlenemedi",
|
||||
"util_error_resolve_symlinks": "sembolik bağlantılar çözümlenemedi: %w",
|
||||
"vendor_no_transcription_support": "sağlayıcı %s ses transkripsiyonunu desteklemiyor",
|
||||
"vendor_not_configured": "sağlayıcı %s yapılandırılmadı",
|
||||
"vendor_not_found": "sağlayıcı %s bulunamadı",
|
||||
"vendors_no_ai_vendors_configured_read_models": "modelleri okumak için yapılandırılmış hiçbir yapay zeka sağlayıcısı yok",
|
||||
"vertexai_client_not_initialized": "VertexAI istemcisi başlatılmadı",
|
||||
"vertexai_error_api_status": "API %d durumunu döndürdü: %s",
|
||||
"vertexai_error_create_request": "istek oluşturulamadı: %w",
|
||||
"vertexai_error_parse_response": "yanıt ayrıştırılamadı: %w",
|
||||
"vertexai_error_read_response": "yanıt okunamadı: %w",
|
||||
"vertexai_error_request_failed": "istek başarısız oldu: %w",
|
||||
"vertexai_error_response_too_large": "yanıt çok büyük (>%d bayt)",
|
||||
"vertexai_failed_gemini_client": "Gemini istemcisi oluşturulamadı: %w",
|
||||
"vertexai_failed_google_credentials": "Google kimlik bilgileri alınamadı (ADC'nin yapılandırıldığından emin olun): %w",
|
||||
"vertexai_no_content_in_response": "yanıtta içerik yok",
|
||||
"vertexai_no_conversational_models": "konuşma modeli bulunamadı",
|
||||
"vertexai_no_models_found": "hiçbir yayıncıdan model bulunamadı",
|
||||
"vertexai_no_valid_messages": "gönderilecek geçerli mesaj yok",
|
||||
"vertexai_stream_error": "Hata: %v",
|
||||
"wipe_context": "Bağlamı sil",
|
||||
"wipe_session": "Oturumu sil",
|
||||
"youtube_api_key_required": "Yorumlar ve meta veriler için YouTube API anahtarı gerekli. Yapılandırmak için 'fabric --setup' komutunu çalıştırın.",
|
||||
"youtube_auth_required_bot_detection": "YouTube kimlik doğrulama gerektiriyor (bot algılama). BROWSER'ın chrome, firefox, brave vb. olduğu durumlarda --yt-dlp-args='--cookies-from-browser BROWSER' kullanın.",
|
||||
"youtube_empty_seconds_string": "boş saniye dizesi",
|
||||
"youtube_error_getting_comments": "yorumlar alınırken hata oluştu: %v",
|
||||
"youtube_error_getting_metadata": "video meta verileri alınırken hata oluştu: %v",
|
||||
"youtube_error_getting_video_details": "video detayları alınırken hata oluştu: %v",
|
||||
"youtube_error_parsing_duration": "video süresi ayrıştırılırken hata oluştu: %v",
|
||||
"youtube_error_saving_csv": "videolar CSV'ye kaydedilirken hata oluştu: %v",
|
||||
"youtube_extract_visual_data_help": "OCR ve FFmpeg kullanarak videodan görsel veri çıkarın",
|
||||
"youtube_failed_create_temp_dir": "geçici dizin oluşturulamadı: %v",
|
||||
"youtube_failed_fetch_comments": "yorumlar alınamadı: %v",
|
||||
"youtube_failed_get_stream_url": "yt-dlp aracılığıyla akış URL'si alınamadı: %v",
|
||||
"youtube_failed_parse_http_stream_url": "yt-dlp çıktısından geçerli HTTP akış URL'si ayrıştırılamadı",
|
||||
"youtube_failed_walk_directory": "dizin gezilemedi: %v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "ffmpeg kare çıkarma başarısız oldu: %v, çıktı: %s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "görsel çıkarma için ffmpeg gerekli ancak PATH'te bulunamadı",
|
||||
"youtube_invalid_duration_string": "geçersiz süre dizesi: %s",
|
||||
"youtube_invalid_seconds_format": "geçersiz saniye biçimi %q: %w",
|
||||
"youtube_invalid_timestamp_format": "geçersiz zaman damgası biçimi: %s",
|
||||
"youtube_invalid_url": "geçersiz YouTube URL'si, video veya çalma listesi kimliği alınamıyor: '%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "geçersiz yt-dlp argümanları: %v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "video görsel karelerinde net metin bulunamadı",
|
||||
"youtube_no_transcript_content": "VTT dosyasında transkript içeriği bulunamadı",
|
||||
"youtube_no_url_provided": "YouTube URL'si sağlanmadı",
|
||||
"youtube_no_video_found_with_id": "ID'si %s olan video bulunamadı",
|
||||
"youtube_no_video_id_found": "URL'de video kimliği bulunamadı",
|
||||
"youtube_no_vtt_files_found": "dizinde VTT dosyası bulunamadı",
|
||||
"youtube_not_configured": "YouTube yapılandırılmamış, lütfen kurulum prosedürünü çalıştırın",
|
||||
"youtube_playlist_header": "Çalma Listesi: %s",
|
||||
"youtube_playlist_saved_to": "Çalma listesi %s konumuna kaydedildi",
|
||||
"youtube_rate_limit_exceeded": "YouTube hız sınırı aşıldı. Daha sonra tekrar deneyin veya istekleri yavaşlatmak için '--sleep-requests 1' gibi farklı yt-dlp argümanları kullanın.",
|
||||
"youtube_setup_description": "YouTube - video transkriptlerini (yt-dlp aracılığıyla) ve yorumları/meta verilerini (YouTube API aracılığıyla) almak için",
|
||||
"youtube_tesseract_frame_failed": "tesseract %d. karede başarısız oldu: %v, stderr: %s",
|
||||
"youtube_tesseract_required_visual_extraction": "görsel çıkarma için tesseract gerekli ancak PATH'te bulunamadı",
|
||||
"youtube_url_help": "Transkripti ve yorumları almak, sohbete göndermek veya konsola yazdırmak ve çıktı dosyasına kaydetmek için YouTube video veya çalma listesi \"URL\"si",
|
||||
"youtube_url_is_playlist_not_video": "URL bir çalma listesi, video değil",
|
||||
"youtube_video_id_title_header": "Video Kimliği: Başlık",
|
||||
"youtube_visual_fps_help": "Sahne algılama kullanmak yerine saniye başına belirli sayıda kare çıkarın",
|
||||
"youtube_visual_frame_cue": "[GÖRSEL KARE İPUCU]",
|
||||
"youtube_visual_sensitivity_help": "FFmpeg sahne algılama toleransı (0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "yt-dlp PATH'te bulunamadı. YouTube transkript işlevselliğini kullanmak için lütfen yt-dlp'yi kurun",
|
||||
"youtube_ytdlp_required_visual_extraction": "görsel çıkarma için yt-dlp gerekli ancak PATH'te bulunamadı",
|
||||
"youtube_ytdlp_stderr_error": "yt-dlp stderr okunurken hata oluştu"
|
||||
}
|
||||
|
|
@ -110,21 +110,43 @@
|
|||
"choose_pattern_from_available": "从可用模式中选择一个模式",
|
||||
"choose_session_from_available": "从可用会话中选择一个会话",
|
||||
"choose_strategy_from_available": "从可用策略中选择一个策略",
|
||||
"codex_api_base_url_question": "请输入您的 Codex API 基础 URL",
|
||||
"codex_auth_base_url_invalid": "Codex 认证基础 URL 无效:%w",
|
||||
"codex_auth_base_url_question": "请输入您的 Codex OAuth 基础 URL",
|
||||
"codex_browser_open_fallback": "如果浏览器未打开,请导航到此 URL 进行身份验证:",
|
||||
"codex_decode_models_response_failed": "解码 Codex 模型响应失败:%w",
|
||||
"codex_decode_refresh_response_failed": "解码刷新的 Codex 令牌响应失败:%w",
|
||||
"codex_decode_token_response_failed": "解码 Codex 令牌交换响应失败:%w",
|
||||
"codex_image_file_not_supported": "Codex 供应商不支持 --image-file。请改用图片附件。",
|
||||
"codex_login_account_changed": "Codex 登录关联的 ChatGPT 账户与保存的配置不同。请重新运行 'fabric --setup'。",
|
||||
"codex_login_completed": "Codex 登录完成",
|
||||
"codex_login_failed": "Codex 登录失败:%s",
|
||||
"codex_login_invalid": "您的 Codex 登录已失效。请重新运行 'fabric --setup'。",
|
||||
"codex_login_missing_account_claim": "Codex 登录未包含 ChatGPT 账户 ID。此登录状态不受支持。",
|
||||
"codex_login_missing_auth_code": "Codex 登录未返回授权码。",
|
||||
"codex_login_missing_tokens": "Codex 登录未返回所需的访问令牌和刷新令牌。",
|
||||
"codex_login_refresh_failed": "无法刷新 Codex 登录。请重新运行 'fabric --setup'。",
|
||||
"codex_login_return_to_fabric": "返回 Fabric。",
|
||||
"codex_login_revoked": "Codex 登录已过期或被撤销。请重新运行 'fabric --setup'。",
|
||||
"codex_login_server_stopped": "Codex 登录回调服务器在身份验证完成前停止。",
|
||||
"codex_login_state_mismatch": "由于 OAuth 状态不匹配,无法验证 Codex 登录。",
|
||||
"codex_login_timed_out": "Codex 登录在身份验证完成前超时。",
|
||||
"codex_oauth_missing_auth_code": "缺少授权码",
|
||||
"codex_oauth_random_state_failed": "生成安全随机 OAuth 状态失败:%w",
|
||||
"codex_oauth_server_start_failed": "启动本地 OAuth 回调服务器失败:%w",
|
||||
"codex_oauth_state_mismatch": "状态不匹配",
|
||||
"codex_provider_error": "Codex 供应商错误(状态 %d):%s",
|
||||
"codex_refresh_failed_status": "刷新 Codex 登录失败(状态 %d)",
|
||||
"codex_refresh_login_failed": "刷新 Codex 登录失败:%w",
|
||||
"codex_refresh_token_required": "需要 Codex 刷新令牌。请重新运行 'fabric --setup'。",
|
||||
"codex_replay_body_unavailable": "请求正文无法重放用于 Codex 重新认证重试",
|
||||
"codex_request_failed": "Codex 请求失败:%w",
|
||||
"codex_request_failed_status": "Codex 请求失败,状态 %d",
|
||||
"codex_starting_browser_login": "正在启动基于浏览器的 OpenAI 登录以连接 Codex。",
|
||||
"codex_token_exchange_failed": "Codex 令牌交换失败:%w",
|
||||
"codex_token_persist_failed": "无法保存 Codex 登录:%w",
|
||||
"codex_token_refresh_missing_access_token": "Codex 令牌刷新未返回访问令牌。",
|
||||
"codex_usage_limit_reached": "已达到 Codex 使用限制",
|
||||
"command_completed_successfully": "命令执行成功",
|
||||
"compression_level_jpeg_webp": "JPEG/WebP 格式的压缩级别 0-100(默认:未设置)",
|
||||
"config_file_not_found": "找不到配置文件:%s",
|
||||
|
|
@ -151,6 +173,7 @@
|
|||
"custom_patterns_setup_description": "自定义模式 - 设置您的自定义模式目录",
|
||||
"custom_patterns_warning_create_directory": "警告:无法创建自定义模式目录 %s:%v\n",
|
||||
"db_error_loading_env_file": "加载 .env 文件错误:%w",
|
||||
"db_error_updating_env_file": "更新 .env 文件错误:%w",
|
||||
"defaults_model_context_length_question": "请输入模型上下文长度",
|
||||
"defaults_model_question": "请输入您的默认模型的索引或名称",
|
||||
"defaults_setup_description": "默认 AI 提供商和模型",
|
||||
|
|
@ -161,7 +184,7 @@
|
|||
"digitalocean_models_request_failed_with_status": "DigitalOcean 模型请求失败,状态码 %d:%s",
|
||||
"disable_openai_responses_api": "禁用 OpenAI 响应 API(默认:false)",
|
||||
"disable_pattern_variable_replacement": "禁用模式变量替换",
|
||||
"enable_web_search_tool": "为支持的模型启用网络搜索工具(Anthropic、OpenAI、Gemini)",
|
||||
"enable_web_search_tool": "为支持的模型启用网络搜索工具(Anthropic、OpenAI、Gemini、Grok)",
|
||||
"end_tag_thinking_sections": "思考部分的结束标签",
|
||||
"error_creating_audio_file": "创建音频文件时出错:%v",
|
||||
"error_creating_file": "创建文件时出错:%v",
|
||||
|
|
@ -352,6 +375,7 @@
|
|||
"ollama_invalid_data_url_format": "无效的数据 URL 格式",
|
||||
"ollama_invalid_http_timeout_using_default": "无效的 HTTP 超时时间 '%s':%v,使用默认值",
|
||||
"ollama_invalid_num_ctx_in_request": "请求中的 num_ctx 无效:%v",
|
||||
"ollama_invalid_request_body": "无效的请求正文",
|
||||
"ollama_no_content_from_upstream": "未从上游 Fabric 服务器收到内容",
|
||||
"ollama_num_ctx_exceeds_maximum": "num_ctx 超过允许的最大值 %d",
|
||||
"ollama_num_ctx_invalid_type": "num_ctx 必须是数字,收到无效类型",
|
||||
|
|
@ -365,6 +389,7 @@
|
|||
"ollama_sse_buffer_limit": "SSE 行超过 1MB 缓冲区限制 - 数据行过大",
|
||||
"ollama_upstream_non_2xx": "上游 Fabric 服务器返回非 2xx 状态 %d:%s",
|
||||
"ollama_upstream_non_2xx_body_unreadable": "上游 Fabric 服务器返回非 2xx 状态 %d 且无法读取正文:%v",
|
||||
"ollama_upstream_request_failed": "无法连接上游 Fabric 服务器",
|
||||
"ollama_upstream_returned_status": "上游 Fabric 服务器返回状态 %d",
|
||||
"ollama_warning_no_content": "警告:未从上游 Fabric 服务器收到内容",
|
||||
"ollama_warning_parse_variables": "警告:无法将 options.variables 解析为 JSON:%v",
|
||||
|
|
@ -385,6 +410,7 @@
|
|||
"openai_image_failed_to_save_image": "保存图像到 %s 失败:%w",
|
||||
"openai_image_saved_to": "图像已保存到:%s",
|
||||
"openai_model_no_image_generation": "模型 '%s' 不支持图像生成。支持的模型:%s",
|
||||
"openai_models_rate_limited": "从提供商 %s 获取模型时超出速率限制;请在 %s 秒后重试",
|
||||
"openai_models_response_too_large": "来自提供商 %s 的模型响应过大(>%d 字节)",
|
||||
"openai_unable_to_parse_models_response": "无法解析模型响应;原始响应:%s",
|
||||
"openai_unexpected_status_code_read_error": "意外的状态码:来自提供商 %s 的 %d(读取响应主体失败:%v)",
|
||||
|
|
@ -400,6 +426,7 @@
|
|||
"output_video_metadata": "输出视频元数据",
|
||||
"path_to_yaml_config": "YAML 配置文件路径",
|
||||
"pattern_not_found_list_available": "未找到模式 '%s'。运行 'fabric -l' 查看可用模式",
|
||||
"pattern_invalid_name": "无效的模式名称:%q",
|
||||
"pattern_not_found_no_patterns": "未找到模式 '%s'。\n\n未安装任何模式!要解决此问题:\n • 运行 'fabric --setup' 配置并下载模式\n • 或运行 'fabric -U' 直接下载/更新模式",
|
||||
"pattern_variables_help": "模式变量的值,例如 -v=#role:expert -v=#points:30",
|
||||
"patterns_cloning_repository": "正在克隆仓库 %s(至路径:%s)...\\n",
|
||||
|
|
@ -473,6 +500,8 @@
|
|||
"prefer_playlist_over_video": "如果 URL 中同时存在两个 ID,则优先选择播放列表而不是视频",
|
||||
"print_context": "打印上下文",
|
||||
"print_current_version": "打印当前版本",
|
||||
"print_metadata_to_stderr": "将元数据(输入/输出令牌)打印到 stderr",
|
||||
"print_pattern_contents": "将指定模式的内容打印到终端",
|
||||
"print_session": "打印会话",
|
||||
"register_new_extension": "从配置文件路径注册新扩展",
|
||||
"remove_registered_extension": "按名称删除已注册的扩展",
|
||||
|
|
@ -486,10 +515,12 @@
|
|||
"send_desktop_notification": "命令完成时发送桌面通知",
|
||||
"serve_fabric_api_ollama_endpoints": "提供带有 ollama 端点的 Fabric REST API 服务",
|
||||
"serve_fabric_rest_api": "提供 Fabric REST API 服务",
|
||||
"server_api_key_required": "拒绝在非回环地址 %s 上提供服务(未配置 API 密钥):请设置 --api-key 或 FABRIC_API_KEY,或绑定回环地址(如 127.0.0.1:8080)",
|
||||
"server_chat_error": "错误:%v",
|
||||
"server_error_marshaling_response": "序列化响应错误:%v",
|
||||
"server_error_writing_response": "写入响应错误:%v",
|
||||
"server_invalid_request_format": "无效的请求格式:%v",
|
||||
"server_no_api_key_warning": "正在启动 REST API 服务器,未启用 API 密钥身份验证。这可能带来安全风险。",
|
||||
"sessions_creating_new": "正在创建新会话:%s\n",
|
||||
"set_debug_level": "设置调试级别(0=关闭,1=基本,2=详细,3=跟踪,4=wire)",
|
||||
"set_frequency_penalty": "设置频率惩罚",
|
||||
|
|
@ -581,6 +612,7 @@
|
|||
"spotify_show_name_label": "**节目**:%s",
|
||||
"spotify_title_label": "**标题**:%s",
|
||||
"spotify_total_episodes_label": "**总剧集数**:%d",
|
||||
"spotify_url_help": "Spotify 播客或单集 URL,用于获取元数据并发送到聊天",
|
||||
"spotify_url_label": "**URL**:%s",
|
||||
"start_tag_thinking_sections": "思考部分的开始标签",
|
||||
"storage_error_delete": "无法删除 %s:%v",
|
||||
|
|
@ -592,6 +624,7 @@
|
|||
"storage_error_save": "无法保存 %s:%v",
|
||||
"storage_error_stat_entry": "无法获取条目 %s 的信息:%v",
|
||||
"storage_error_unmarshal": "无法反序列化 %s:%s",
|
||||
"storage_invalid_name": "无效的名称:%q",
|
||||
"strategies_available_header": "可用的策略:",
|
||||
"strategies_cloning_repository": "正在克隆仓库 %s(至路径:%s)...\\n",
|
||||
"strategies_download_success": "✅ 已成功下载并安装策略到 %s\\n",
|
||||
|
|
@ -698,15 +731,21 @@
|
|||
"youtube_error_getting_video_details": "获取视频详情时出错:%v",
|
||||
"youtube_error_parsing_duration": "解析视频时长时出错:%v",
|
||||
"youtube_error_saving_csv": "将视频保存为 CSV 时出错:%v",
|
||||
"youtube_extract_visual_data_help": "使用 OCR 和 FFmpeg 从视频中提取视觉数据",
|
||||
"youtube_failed_create_temp_dir": "创建临时目录失败:%v",
|
||||
"youtube_failed_fetch_comments": "获取评论失败:%v",
|
||||
"youtube_failed_get_stream_url": "无法通过 yt-dlp 获取流 URL:%v",
|
||||
"youtube_failed_parse_http_stream_url": "无法从 yt-dlp 输出中解析出有效的 HTTP 流 URL",
|
||||
"youtube_failed_walk_directory": "遍历目录失败:%v",
|
||||
"youtube_ffmpeg_frame_extraction_failed": "ffmpeg 提取帧失败:%v,输出:%s",
|
||||
"youtube_ffmpeg_required_visual_extraction": "视觉提取需要 ffmpeg,但在 PATH 中未找到",
|
||||
"youtube_invalid_duration_string": "无效的时长字符串:%s",
|
||||
"youtube_invalid_seconds_format": "无效的秒数格式 %q:%w",
|
||||
"youtube_invalid_timestamp_format": "无效的时间戳格式:%s",
|
||||
"youtube_invalid_url": "无效的 YouTube URL,无法获取视频或播放列表 ID:'%s'",
|
||||
"youtube_invalid_ytdlp_arguments": "无效的 yt-dlp 参数:%v",
|
||||
"youtube_label": "YouTube",
|
||||
"youtube_no_clear_text_visual_frames": "在视频视觉帧中未找到清晰文本",
|
||||
"youtube_no_transcript_content": "在 VTT 文件中未找到转录内容",
|
||||
"youtube_no_url_provided": "未提供 YouTube URL",
|
||||
"youtube_no_video_found_with_id": "未找到 ID 为 %s 的视频",
|
||||
|
|
@ -717,9 +756,15 @@
|
|||
"youtube_playlist_saved_to": "播放列表已保存到 %s",
|
||||
"youtube_rate_limit_exceeded": "超过 YouTube 速率限制。请稍后重试,或使用不同的 yt-dlp 参数(如 '--sleep-requests 1')来减慢请求速度。",
|
||||
"youtube_setup_description": "YouTube - 获取视频转录(通过 yt-dlp)和评论/元数据(通过 YouTube API)",
|
||||
"youtube_tesseract_frame_failed": "tesseract 在第 %d 帧上失败:%v,stderr:%s",
|
||||
"youtube_tesseract_required_visual_extraction": "视觉提取需要 tesseract,但在 PATH 中未找到",
|
||||
"youtube_url_help": "YouTube 视频或播放列表 \"URL\",用于获取转录、评论并发送到聊天或打印到控制台并存储到输出文件",
|
||||
"youtube_url_is_playlist_not_video": "URL 是播放列表,而不是视频",
|
||||
"youtube_video_id_title_header": "视频 ID:标题",
|
||||
"youtube_visual_fps_help": "按指定的每秒帧数提取画面,而不是使用场景检测",
|
||||
"youtube_visual_frame_cue": "[视觉帧提示]",
|
||||
"youtube_visual_sensitivity_help": "FFmpeg 场景检测的容差(0.0 - 1.0)",
|
||||
"youtube_ytdlp_not_found": "在 PATH 中未找到 yt-dlp。请安装 yt-dlp 以使用 YouTube 转录功能",
|
||||
"youtube_ytdlp_required_visual_extraction": "视觉提取需要 yt-dlp,但在 PATH 中未找到",
|
||||
"youtube_ytdlp_stderr_error": "读取 yt-dlp stderr 时出错"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
neturl "net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -24,6 +25,22 @@ const webSearchToolName = "web_search"
|
|||
const webSearchToolType = "web_search_20250305"
|
||||
const sourcesHeader = "## Sources"
|
||||
|
||||
// These models reject non-default sampling parameters.
|
||||
// Omit these params entirely for safest compatibility.
|
||||
var samplingParamsDisallowedPrefixes = []string{
|
||||
"claude-opus-4-7",
|
||||
"claude-opus-4-8",
|
||||
"claude-opus-5",
|
||||
"claude-sonnet-5",
|
||||
"claude-fable-5",
|
||||
}
|
||||
|
||||
func modelDisallowsSamplingParams(model string) bool {
|
||||
return slices.ContainsFunc(samplingParamsDisallowedPrefixes, func(prefix string) bool {
|
||||
return strings.HasPrefix(model, prefix)
|
||||
})
|
||||
}
|
||||
|
||||
func NewClient() (ret *Client) {
|
||||
vendorName := "Anthropic"
|
||||
ret = &Client{}
|
||||
|
|
@ -38,43 +55,47 @@ func NewClient() (ret *Client) {
|
|||
ret.defaultRequiredUserMessage = "Hi"
|
||||
ret.models = []string{
|
||||
// The following are the current supported models
|
||||
string(anthropic.ModelClaudeFable5),
|
||||
string(anthropic.ModelClaudeSonnet5),
|
||||
string(anthropic.ModelClaudeOpus5),
|
||||
string(anthropic.ModelClaudeOpus4_8),
|
||||
string(anthropic.ModelClaudeOpus4_7),
|
||||
string(anthropic.ModelClaudeSonnet4_6),
|
||||
string(anthropic.ModelClaudeOpus4_6),
|
||||
string(anthropic.ModelClaudeOpus4_5_20251101),
|
||||
string(anthropic.ModelClaudeOpus4_5),
|
||||
string(anthropic.ModelClaudeHaiku4_5),
|
||||
string(anthropic.ModelClaudeHaiku4_5_20251001),
|
||||
string(anthropic.ModelClaudeSonnet4_20250514),
|
||||
string(anthropic.ModelClaudeSonnet4_0),
|
||||
string(anthropic.ModelClaudeSonnet4_5),
|
||||
string(anthropic.ModelClaudeSonnet4_5_20250929),
|
||||
string(anthropic.ModelClaudeOpus4_0),
|
||||
string(anthropic.ModelClaudeOpus4_20250514),
|
||||
string(anthropic.ModelClaudeOpus4_1_20250805),
|
||||
}
|
||||
|
||||
// context1M is the beta header historically required to opt into the
|
||||
// 1-million token context window. On current models 1M is the DEFAULT and
|
||||
// no header is needed; we still send it defensively (Send/SendStream retry
|
||||
// without it if a model rejects it), so only models with a genuine 1M
|
||||
// window belong here.
|
||||
//
|
||||
// Verified against
|
||||
// https://platform.claude.com/docs/en/build-with-claude/context-windows#context-window-sizes-by-model
|
||||
// Excluded because they are 200K-context models: Sonnet 4.5, Opus 4.5,
|
||||
// Opus 4.1, and Haiku 4.5.
|
||||
//
|
||||
// Kept separate from the main model list for easier updates.
|
||||
const context1M = "context-1m-2025-08-07"
|
||||
ret.modelBetas = map[string][]string{
|
||||
// See https://platform.claude.com/docs/en/build-with-claude/context-windows#1-m-token-context-window
|
||||
// Claude Opus 4.6, Sonnet 4.6, Sonnet 4.5, and Sonnet 4 support a 1-million token context window.
|
||||
// Claude 5 family
|
||||
string(anthropic.ModelClaudeFable5): {context1M},
|
||||
string(anthropic.ModelClaudeOpus5): {context1M},
|
||||
string(anthropic.ModelClaudeSonnet5): {context1M},
|
||||
|
||||
// This list can change over time as Anthropic updates their models and beta features, so we maintain it separately from the main model list
|
||||
// for easier updates.
|
||||
// Claude Opus 4.x (1M-capable)
|
||||
string(anthropic.ModelClaudeOpus4_8): {context1M},
|
||||
string(anthropic.ModelClaudeOpus4_7): {context1M},
|
||||
string(anthropic.ModelClaudeOpus4_6): {context1M},
|
||||
|
||||
// Claude Sonnet 4 variants (1M context support)
|
||||
string(anthropic.ModelClaudeSonnet4_20250514): {"context-1m-2025-08-07"},
|
||||
string(anthropic.ModelClaudeSonnet4_0): {"context-1m-2025-08-07"},
|
||||
|
||||
// Claude Sonnet 4.5 variants (1M context support)
|
||||
string(anthropic.ModelClaudeSonnet4_5): {"context-1m-2025-08-07"},
|
||||
string(anthropic.ModelClaudeSonnet4_5_20250929): {"context-1m-2025-08-07"},
|
||||
|
||||
// Claude Sonnet 4.6 (1M context support)
|
||||
string(anthropic.ModelClaudeSonnet4_6): {"context-1m-2025-08-07"},
|
||||
|
||||
// Claude Opus 4.5 and 4.6 variants (1M context support)
|
||||
string(anthropic.ModelClaudeOpus4_5): {"context-1m-2025-08-07"},
|
||||
string(anthropic.ModelClaudeOpus4_6): {"context-1m-2025-08-07"},
|
||||
string(anthropic.ModelClaudeOpus4_5_20251101): {"context-1m-2025-08-07"},
|
||||
// Claude Sonnet 4.x (1M-capable)
|
||||
string(anthropic.ModelClaudeSonnet4_6): {context1M},
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -125,7 +146,7 @@ func (an *Client) configure() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (an *Client) ListModels() (ret []string, err error) {
|
||||
func (an *Client) ListModels(context.Context) (ret []string, err error) {
|
||||
return an.models, nil
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +171,7 @@ func parseThinking(level domain.ThinkingLevel) (anthropic.ThinkingConfigParamUni
|
|||
}
|
||||
|
||||
func (an *Client) SendStream(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
) (err error) {
|
||||
messages := an.toMessages(msgs)
|
||||
if len(messages) == 0 {
|
||||
|
|
@ -159,8 +180,6 @@ func (an *Client) SendStream(
|
|||
return
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
params := an.buildMessageParams(messages, opts)
|
||||
betas := an.modelBetas[opts.Model]
|
||||
var reqOpts []option.RequestOption
|
||||
|
|
@ -216,15 +235,21 @@ func (an *Client) SendStream(
|
|||
func (an *Client) buildMessageParams(msgs []anthropic.MessageParam, opts *domain.ChatOptions) (
|
||||
params anthropic.MessageNewParams) {
|
||||
|
||||
maxTokens := an.maxTokens
|
||||
if opts.MaxTokens > 0 {
|
||||
maxTokens = opts.MaxTokens
|
||||
}
|
||||
|
||||
params = anthropic.MessageNewParams{
|
||||
Model: anthropic.Model(opts.Model),
|
||||
MaxTokens: int64(an.maxTokens),
|
||||
MaxTokens: int64(maxTokens),
|
||||
Messages: msgs,
|
||||
}
|
||||
|
||||
// Only set one of Temperature or TopP as some models don't allow both
|
||||
// Always set temperature to ensure consistent behavior (Anthropic default is 1.0, Fabric default is 0.7)
|
||||
if opts.TopP != domain.DefaultTopP {
|
||||
// Claude Opus 4.7 disallows sampling params; omit both temperature and top_p.
|
||||
if modelDisallowsSamplingParams(opts.Model) {
|
||||
// Intentionally omit both fields.
|
||||
} else if opts.TopP != domain.DefaultTopP {
|
||||
// User explicitly set TopP, so use that instead of temperature
|
||||
params.TopP = anthropic.Opt(opts.TopP)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package anthropic
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
|
|
@ -34,7 +35,7 @@ func TestNewClient_DefaultInitialization(t *testing.T) {
|
|||
func TestClientListModels(t *testing.T) {
|
||||
client := NewClient()
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
|
@ -52,7 +53,7 @@ func TestClientListModels(t *testing.T) {
|
|||
|
||||
func TestClient_ListModels_ReturnsCorrectModels(t *testing.T) {
|
||||
client := NewClient()
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
|
|
@ -98,6 +99,43 @@ func TestBuildMessageParams_WithoutSearch(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuildMessageParams_UsesConfiguredMaxTokensByDefault(t *testing.T) {
|
||||
client := NewClient()
|
||||
opts := &domain.ChatOptions{
|
||||
Model: "claude-3-5-sonnet-latest",
|
||||
Temperature: domain.DefaultTemperature,
|
||||
TopP: domain.DefaultTopP,
|
||||
}
|
||||
messages := []anthropic.MessageParam{
|
||||
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
|
||||
}
|
||||
|
||||
params := client.buildMessageParams(messages, opts)
|
||||
|
||||
if params.MaxTokens != int64(client.maxTokens) {
|
||||
t.Errorf("Expected default max_tokens %d, got %d", client.maxTokens, params.MaxTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMessageParams_UsesChatOptionsMaxTokens(t *testing.T) {
|
||||
client := NewClient()
|
||||
opts := &domain.ChatOptions{
|
||||
Model: "claude-3-5-sonnet-latest",
|
||||
Temperature: domain.DefaultTemperature,
|
||||
TopP: domain.DefaultTopP,
|
||||
MaxTokens: 8192,
|
||||
}
|
||||
messages := []anthropic.MessageParam{
|
||||
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
|
||||
}
|
||||
|
||||
params := client.buildMessageParams(messages, opts)
|
||||
|
||||
if params.MaxTokens != int64(opts.MaxTokens) {
|
||||
t.Errorf("Expected max_tokens %d, got %d", opts.MaxTokens, params.MaxTokens)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildMessageParams_WithSearch(t *testing.T) {
|
||||
client := NewClient()
|
||||
opts := &domain.ChatOptions{
|
||||
|
|
@ -169,9 +207,32 @@ func TestBuildMessageParams_WithSearchAndLocation(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestBuildMessageParams_Opus47OmitsSamplingParams(t *testing.T) {
|
||||
client := NewClient()
|
||||
opts := &domain.ChatOptions{
|
||||
Model: string(anthropic.ModelClaudeOpus4_7),
|
||||
Temperature: 0.8,
|
||||
TopP: 0.8,
|
||||
Search: false,
|
||||
}
|
||||
|
||||
messages := []anthropic.MessageParam{
|
||||
anthropic.NewUserMessage(anthropic.NewTextBlock("Hello")),
|
||||
}
|
||||
|
||||
params := client.buildMessageParams(messages, opts)
|
||||
|
||||
if params.Temperature.Value != 0 {
|
||||
t.Errorf("expected temperature to be omitted for %s, got %f", opts.Model, params.Temperature.Value)
|
||||
}
|
||||
if params.TopP.Value != 0 {
|
||||
t.Errorf("expected top_p to be omitted for %s, got %f", opts.Model, params.TopP.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModelBetasConfiguration(t *testing.T) {
|
||||
client := NewClient()
|
||||
model := string(anthropic.ModelClaudeSonnet4_20250514)
|
||||
model := string(anthropic.ModelClaudeSonnet5)
|
||||
betas, ok := client.modelBetas[model]
|
||||
if !ok || len(betas) != 1 || betas[0] != "context-1m-2025-08-07" {
|
||||
t.Errorf("expected beta mapping for %s", model)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package azure
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ func (oi *Client) configure() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (oi *Client) ListModels() (ret []string, err error) {
|
||||
func (oi *Client) ListModels(context.Context) (ret []string, err error) {
|
||||
ret = oi.apiDeployments
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package azure
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
|
@ -78,7 +79,7 @@ func TestListModels(t *testing.T) {
|
|||
client := NewClient()
|
||||
client.apiDeployments = []string{"deployment1", "deployment2"}
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package azure_entra
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
|
@ -71,7 +72,7 @@ func (c *Client) configure() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() (ret []string, err error) {
|
||||
func (c *Client) ListModels(context.Context) (ret []string, err error) {
|
||||
ret = c.apiDeployments
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package azure_entra
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
|
@ -47,7 +48,7 @@ func TestListModels(t *testing.T) {
|
|||
client := NewClient()
|
||||
client.apiDeployments = []string{"gpt-4o", "gpt-5"}
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ var _ ai.Vendor = (*Client)(nil)
|
|||
// are handled by the Client.
|
||||
type Backend interface {
|
||||
// ListModels returns the list of models available for this backend
|
||||
ListModels() ([]string, error)
|
||||
ListModels(context.Context) ([]string, error)
|
||||
|
||||
// BuildEndpoint constructs the full API endpoint URL for the given model
|
||||
BuildEndpoint(baseURL, model string) string
|
||||
|
|
@ -132,11 +132,11 @@ func (c *Client) IsConfigured() bool {
|
|||
}
|
||||
|
||||
// ListModels delegates to the active backend
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(ctx context.Context) ([]string, error) {
|
||||
if c.backend == nil {
|
||||
return nil, errors.New(i18n.T("azureaigateway_backend_not_initialized"))
|
||||
}
|
||||
return c.backend.ListModels()
|
||||
return c.backend.ListModels(ctx)
|
||||
}
|
||||
|
||||
// Send sends a non-streaming request through the APIM gateway.
|
||||
|
|
@ -199,18 +199,13 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
|||
}
|
||||
|
||||
// SendStream falls back to non-streaming (APIM gateway doesn't support SSE pass-through).
|
||||
//
|
||||
// NOTE: This method uses context.Background() because the ai.Vendor interface does not
|
||||
// accept a context parameter for SendStream. If the caller disconnects, this request will
|
||||
// continue until the gateway timeout (300s). A future update to the ai.Vendor interface
|
||||
// should add context propagation to SendStream.
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
func (c *Client) SendStream(ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
defer close(channel)
|
||||
if c.backend == nil {
|
||||
return errors.New(i18n.T("azureaigateway_backend_not_initialized"))
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), gatewayTimeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, gatewayTimeout)
|
||||
defer cancel()
|
||||
|
||||
result, err := c.Send(ctx, msgs, opts)
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ func TestBedrockAuthHeader(t *testing.T) {
|
|||
|
||||
func TestBedrockListModels(t *testing.T) {
|
||||
b := NewBedrockBackend("key")
|
||||
models, err := b.ListModels()
|
||||
models, err := b.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels() error = %v", err)
|
||||
}
|
||||
|
|
@ -253,7 +253,7 @@ func TestAzureOpenAIAuthHeader(t *testing.T) {
|
|||
|
||||
func TestAzureOpenAIListModels(t *testing.T) {
|
||||
b := NewAzureOpenAIBackend("key", "")
|
||||
models, err := b.ListModels()
|
||||
models, err := b.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels() error = %v", err)
|
||||
}
|
||||
|
|
@ -336,7 +336,7 @@ func TestVertexAIAuthHeader(t *testing.T) {
|
|||
|
||||
func TestVertexAIListModels(t *testing.T) {
|
||||
b := NewVertexAIBackend("key")
|
||||
models, err := b.ListModels()
|
||||
models, err := b.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels() error = %v", err)
|
||||
}
|
||||
|
|
@ -549,7 +549,7 @@ func TestConfigureInvalidBackend(t *testing.T) {
|
|||
|
||||
func TestListModelsWithoutInit(t *testing.T) {
|
||||
c := NewClient()
|
||||
_, err := c.ListModels()
|
||||
_, err := c.ListModels(context.Background())
|
||||
if err == nil {
|
||||
t.Error("ListModels() expected error when backend not initialized")
|
||||
}
|
||||
|
|
@ -848,7 +848,7 @@ func TestSendStreamWithoutBackendInit(t *testing.T) {
|
|||
}
|
||||
|
||||
channel := make(chan domain.StreamUpdate, 1)
|
||||
err := c.SendStream(msgs, opts, channel)
|
||||
err := c.SendStream(context.Background(), msgs, opts, channel)
|
||||
if err == nil {
|
||||
t.Fatal("SendStream() expected error when backend not initialized")
|
||||
}
|
||||
|
|
@ -901,7 +901,7 @@ func TestSendStreamFallback(t *testing.T) {
|
|||
}
|
||||
|
||||
channel := make(chan domain.StreamUpdate, 10)
|
||||
err := c.SendStream(msgs, opts, channel)
|
||||
err := c.SendStream(context.Background(), msgs, opts, channel)
|
||||
if err != nil {
|
||||
t.Fatalf("SendStream() error = %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package azureaigateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -34,7 +35,7 @@ func NewAzureOpenAIBackend(subscriptionKey, apiVersion string) *AzureOpenAIBacke
|
|||
|
||||
// ListModels returns the list of models available through Azure OpenAI.
|
||||
// These are deployment names that must exist in your Azure OpenAI resource.
|
||||
func (b *AzureOpenAIBackend) ListModels() ([]string, error) {
|
||||
func (b *AzureOpenAIBackend) ListModels(_ context.Context) ([]string, error) {
|
||||
return []string{
|
||||
"DeepSeek-R1",
|
||||
"gpt-4o",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package azureaigateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -27,7 +28,7 @@ func NewBedrockBackend(subscriptionKey string) *BedrockBackend {
|
|||
}
|
||||
|
||||
// ListModels returns the list of available Bedrock inference profiles
|
||||
func (b *BedrockBackend) ListModels() ([]string, error) {
|
||||
func (b *BedrockBackend) ListModels(_ context.Context) ([]string, error) {
|
||||
return []string{
|
||||
"us.anthropic.claude-3-haiku-20240307-v1:0",
|
||||
"us.anthropic.claude-3-opus-20240229-v1:0",
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
package azureaigateway
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
|
@ -26,7 +27,7 @@ func NewVertexAIBackend(subscriptionKey string) *VertexAIBackend {
|
|||
}
|
||||
|
||||
// ListModels returns the list of Gemini models available through Vertex AI
|
||||
func (b *VertexAIBackend) ListModels() ([]string, error) {
|
||||
func (b *VertexAIBackend) ListModels(_ context.Context) ([]string, error) {
|
||||
return []string{
|
||||
"gemini-3-pro-preview",
|
||||
"gemini-2.5-pro",
|
||||
|
|
|
|||
|
|
@ -443,7 +443,7 @@ func (c *BedrockClient) configure() error {
|
|||
// from AWS Bedrock that can be used with this plugin.
|
||||
// When using bearer token auth, the API may not be accessible, so a static
|
||||
// fallback list of common models is returned instead.
|
||||
func (c *BedrockClient) ListModels() ([]string, error) {
|
||||
func (c *BedrockClient) ListModels(_ context.Context) ([]string, error) {
|
||||
models, err := c.listModelsFromAPI()
|
||||
if err != nil && c.bedrockAPIKey.Value != "" {
|
||||
// Bearer token auth may lack ListFoundationModels permissions;
|
||||
|
|
@ -488,7 +488,7 @@ func (c *BedrockClient) listModelsFromAPI() ([]string, error) {
|
|||
}
|
||||
|
||||
// SendStream sends the messages to the Bedrock ConverseStream API
|
||||
func (c *BedrockClient) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
func (c *BedrockClient) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
// Ensure channel is closed on all exit paths to prevent goroutine leaks
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
|
|
|
|||
|
|
@ -207,7 +207,7 @@ func TestListModels_NilClient_WithApiKey_ReturnsFallback(t *testing.T) {
|
|||
client.bedrockAPIKey.Value = "test-absk-token"
|
||||
// Don't call configure() — clients are nil
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
assert.NoError(t, err, "ListModels should not error when falling back to static list")
|
||||
assert.Equal(t, defaultBedrockModels, models, "should return default models as fallback")
|
||||
}
|
||||
|
|
@ -216,7 +216,7 @@ func TestListModels_NilClient_NoApiKey_ReturnsError(t *testing.T) {
|
|||
client := NewClient()
|
||||
// Don't call configure() and no API key — should propagate error
|
||||
|
||||
_, err := client.ListModels()
|
||||
_, err := client.ListModels(context.Background())
|
||||
assert.Error(t, err, "ListModels should error when client is nil and no API key for fallback")
|
||||
}
|
||||
|
||||
|
|
@ -227,7 +227,7 @@ func TestSendStream_NilClient_ReturnsError(t *testing.T) {
|
|||
ch := make(chan domain.StreamUpdate, 10)
|
||||
opts := &domain.ChatOptions{Model: "test-model", Temperature: 0.7, TopP: 0.9}
|
||||
|
||||
err := client.SendStream(nil, opts, ch)
|
||||
err := client.SendStream(context.Background(), nil, opts, ch)
|
||||
assert.Error(t, err, "SendStream should return error when client is nil")
|
||||
assert.Contains(t, err.Error(), i18n.T("bedrock_client_not_initialized"))
|
||||
}
|
||||
|
|
|
|||
248
internal/plugins/ai/codex/auth_transport.go
Normal file
248
internal/plugins/ai/codex/auth_transport.go
Normal file
|
|
@ -0,0 +1,248 @@
|
|||
package codex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
plugins "github.com/danielmiessler/fabric/internal/plugins"
|
||||
)
|
||||
|
||||
var errReplayBodyUnavailable = errors.New(i18n.T("codex_replay_body_unavailable"))
|
||||
|
||||
type authTransport struct {
|
||||
client *Client
|
||||
wrapped http.RoundTripper
|
||||
}
|
||||
|
||||
func (c *Client) ensureAccessToken(ctx context.Context, forceRefresh bool) (string, string, error) {
|
||||
// Fast path: a valid in-memory token needs no refresh, so avoid the store
|
||||
// lock and disk reload. This also keeps Setup's fresh OAuth tokens from
|
||||
// being overwritten by the stale values still on disk before SaveEnvFile.
|
||||
if !forceRefresh {
|
||||
if access, account, ok := c.currentToken(); ok {
|
||||
return access, account, nil
|
||||
}
|
||||
}
|
||||
if c.WithStoreLock == nil {
|
||||
return c.ensureAccessTokenLocked(ctx, forceRefresh)
|
||||
}
|
||||
var access, account string
|
||||
err := c.WithStoreLock(func() error {
|
||||
var inner error
|
||||
access, account, inner = c.ensureAccessTokenLocked(ctx, forceRefresh)
|
||||
return inner
|
||||
})
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
return access, account, nil
|
||||
}
|
||||
|
||||
// currentToken returns the in-memory token when it is present and unexpired.
|
||||
// A missing account ID falls through to the locked path, which parses it from
|
||||
// the JWT.
|
||||
func (c *Client) currentToken() (access string, account string, ok bool) {
|
||||
c.tokenMu.Lock()
|
||||
defer c.tokenMu.Unlock()
|
||||
|
||||
access = strings.TrimSpace(c.AccessToken.Value)
|
||||
account = strings.TrimSpace(c.AccountID.Value)
|
||||
if access == "" || account == "" || tokenNeedsRefresh(access, time.Now()) {
|
||||
return "", "", false
|
||||
}
|
||||
return access, account, true
|
||||
}
|
||||
|
||||
func (c *Client) ensureAccessTokenLocked(ctx context.Context, forceRefresh bool) (string, string, error) {
|
||||
c.tokenMu.Lock()
|
||||
defer c.tokenMu.Unlock()
|
||||
|
||||
accessToken := strings.TrimSpace(c.AccessToken.Value)
|
||||
accountID := strings.TrimSpace(c.AccountID.Value)
|
||||
|
||||
if !forceRefresh && accessToken != "" && !tokenNeedsRefresh(accessToken, time.Now()) {
|
||||
if accountID == "" {
|
||||
parsedAccountID, err := extractAccountIDFromJWT(accessToken)
|
||||
if err == nil && parsedAccountID != "" {
|
||||
accountID = parsedAccountID
|
||||
c.setSettingValue(c.AccountID, accountID)
|
||||
}
|
||||
}
|
||||
if accountID != "" {
|
||||
return accessToken, accountID, nil
|
||||
}
|
||||
}
|
||||
|
||||
refreshed, err := c.refreshAccessToken(ctx)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
refreshedAccountID, err := c.extractAccountID(refreshed.IDToken, refreshed.AccessToken)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if accountID != "" && refreshedAccountID != "" && !strings.EqualFold(accountID, refreshedAccountID) {
|
||||
return "", "", errors.New(i18n.T("codex_login_account_changed"))
|
||||
}
|
||||
|
||||
c.setSettingValue(c.AccessToken, refreshed.AccessToken)
|
||||
if strings.TrimSpace(refreshed.RefreshToken) != "" {
|
||||
c.setSettingValue(c.RefreshToken, refreshed.RefreshToken)
|
||||
}
|
||||
c.setSettingValue(c.AccountID, refreshedAccountID)
|
||||
if c.TokenPersist != nil {
|
||||
if err := c.TokenPersist(); err != nil {
|
||||
debuglog.Log("Codex token persist failed: %v\n", err)
|
||||
return "", "", fmt.Errorf(i18n.T("codex_token_persist_failed"), err)
|
||||
}
|
||||
}
|
||||
debuglog.Debug(debuglog.Detailed, "Codex access token refreshed account_present=%t\n", refreshedAccountID != "")
|
||||
|
||||
return c.AccessToken.Value, c.AccountID.Value, nil
|
||||
}
|
||||
|
||||
func (c *Client) refreshAccessToken(ctx context.Context) (oauthTokens, error) {
|
||||
payload := refreshRequest{
|
||||
ClientID: oauthClientID,
|
||||
GrantType: "refresh_token",
|
||||
RefreshToken: strings.TrimSpace(c.RefreshToken.Value),
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
tokenURL := strings.TrimRight(c.AuthBaseURL.Value, "/") + "/oauth/token"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.authHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf(i18n.T("codex_refresh_login_failed"), err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return oauthTokens{}, c.refreshErrorFromResponse(resp.StatusCode, responseBody)
|
||||
}
|
||||
|
||||
var refreshed refreshResponse
|
||||
if err := json.Unmarshal(responseBody, &refreshed); err != nil {
|
||||
return oauthTokens{}, fmt.Errorf(i18n.T("codex_decode_refresh_response_failed"), err)
|
||||
}
|
||||
if strings.TrimSpace(refreshed.AccessToken) == "" {
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_token_refresh_missing_access_token"))
|
||||
}
|
||||
|
||||
return oauthTokens{
|
||||
IDToken: strings.TrimSpace(refreshed.IDToken),
|
||||
AccessToken: strings.TrimSpace(refreshed.AccessToken),
|
||||
RefreshToken: strings.TrimSpace(refreshed.RefreshToken),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) extractAccountID(idToken string, accessToken string) (string, error) {
|
||||
if accountID, err := extractAccountIDFromJWT(idToken); err == nil && accountID != "" {
|
||||
return accountID, nil
|
||||
}
|
||||
if accountID, err := extractAccountIDFromJWT(accessToken); err == nil && accountID != "" {
|
||||
return accountID, nil
|
||||
}
|
||||
return "", errors.New(i18n.T("codex_login_missing_account_claim"))
|
||||
}
|
||||
|
||||
func (c *Client) setSettingValue(setting *plugins.Setting, value string) {
|
||||
setting.Value = value
|
||||
if setting.EnvVariable != "" {
|
||||
_ = os.Setenv(setting.EnvVariable, value)
|
||||
}
|
||||
}
|
||||
|
||||
// RoundTrip adds Codex authentication headers and retries once after a 401.
|
||||
func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.roundTrip(req, false)
|
||||
}
|
||||
|
||||
func (t *authTransport) roundTrip(req *http.Request, retried bool) (*http.Response, error) {
|
||||
token, accountID, err := t.client.ensureAccessToken(req.Context(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clone, err := cloneRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clone.Header.Set(http.CanonicalHeaderKey("originator"), defaultOriginator)
|
||||
clone.Header.Set("User-Agent", defaultUserAgent)
|
||||
clone.Header.Set("Authorization", "Bearer "+token)
|
||||
clone.Header.Set("ChatGPT-Account-ID", accountID)
|
||||
|
||||
resp, err := t.roundTripper().RoundTrip(clone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnauthorized || retried {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
drainAndClose(resp.Body)
|
||||
debuglog.Debug(debuglog.Detailed, "Codex request returned 401; attempting token refresh and one retry\n")
|
||||
|
||||
if _, _, err := t.client.ensureAccessToken(req.Context(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.roundTrip(req, true)
|
||||
}
|
||||
|
||||
func (t *authTransport) roundTripper() http.RoundTripper {
|
||||
if t.wrapped != nil {
|
||||
return t.wrapped
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
func cloneRequest(req *http.Request) (*http.Request, error) {
|
||||
clone := req.Clone(req.Context())
|
||||
if req.Body == nil || req.Body == http.NoBody {
|
||||
return clone, nil
|
||||
}
|
||||
// Codex retry logic assumes GetBody is available so the request can be replayed after refresh.
|
||||
if req.GetBody == nil {
|
||||
return nil, errReplayBodyUnavailable
|
||||
}
|
||||
|
||||
body, err := req.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clone.Body = body
|
||||
return clone, nil
|
||||
}
|
||||
|
||||
func drainAndClose(body io.ReadCloser) {
|
||||
if body == nil {
|
||||
return
|
||||
}
|
||||
_, _ = io.Copy(io.Discard, io.LimitReader(body, defaultRoundTripLimit))
|
||||
_ = body.Close()
|
||||
}
|
||||
|
|
@ -4,21 +4,11 @@ package codex
|
|||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -55,8 +45,7 @@ const (
|
|||
|
||||
const oauthScope = "openid profile email offline_access api.connectors.read api.connectors.invoke"
|
||||
|
||||
var errReplayBodyUnavailable = errors.New("request body cannot be replayed for Codex re-authentication retry")
|
||||
|
||||
// Client implements the Codex-backed AI vendor.
|
||||
type Client struct {
|
||||
*openaivendor.Client
|
||||
|
||||
|
|
@ -69,29 +58,10 @@ type Client struct {
|
|||
apiHTTPClient *http.Client
|
||||
|
||||
tokenMu sync.Mutex
|
||||
}
|
||||
|
||||
type oauthTokens struct {
|
||||
IDToken string `json:"id_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type refreshRequest struct {
|
||||
ClientID string `json:"client_id"`
|
||||
GrantType string `json:"grant_type"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type refreshResponse struct {
|
||||
IDToken string `json:"id_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type oauthResult struct {
|
||||
tokens oauthTokens
|
||||
err error
|
||||
// TokenPersist writes current tokens after refresh. Nil skips. Error fails the refresh.
|
||||
TokenPersist func() error
|
||||
// WithStoreLock, when set, wraps refresh+persist (reload tokens inside the lock).
|
||||
WithStoreLock func(func() error) error
|
||||
}
|
||||
|
||||
type modelInfo struct {
|
||||
|
|
@ -104,29 +74,6 @@ type modelsResponse struct {
|
|||
Models []modelInfo `json:"models"`
|
||||
}
|
||||
|
||||
type tokenClaims struct {
|
||||
Exp int64 `json:"exp"`
|
||||
Auth tokenAuthClaims `json:"https://api.openai.com/auth"`
|
||||
Profile tokenProfile `json:"https://api.openai.com/profile"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type tokenAuthClaims struct {
|
||||
ChatGPTAccountID string `json:"chatgpt_account_id"`
|
||||
ChatGPTPlanType string `json:"chatgpt_plan_type"`
|
||||
UserID string `json:"user_id"`
|
||||
ChatGPTUserID string `json:"chatgpt_user_id"`
|
||||
}
|
||||
|
||||
type tokenProfile struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type authTransport struct {
|
||||
client *Client
|
||||
wrapped http.RoundTripper
|
||||
}
|
||||
|
||||
// NewClient creates a new Codex vendor client.
|
||||
func NewClient() *Client {
|
||||
client := &Client{}
|
||||
|
|
@ -138,11 +85,11 @@ func NewClient() *Client {
|
|||
client.AccountID = client.AddSetting("Account ID", true)
|
||||
|
||||
client.ApiBaseURL = client.AddSetupQuestionWithEnvName("Base URL", false,
|
||||
"Enter your Codex API base URL")
|
||||
i18n.T("codex_api_base_url_question"))
|
||||
client.ApiBaseURL.Value = defaultBaseURL
|
||||
|
||||
client.AuthBaseURL = client.AddSetupQuestionWithEnvName("Auth Base URL", false,
|
||||
"Enter your Codex OAuth base URL")
|
||||
i18n.T("codex_auth_base_url_question"))
|
||||
client.AuthBaseURL.Value = defaultAuthBaseURL
|
||||
|
||||
client.authHTTPClient = &http.Client{Timeout: modelsRequestTimeout}
|
||||
|
|
@ -158,6 +105,14 @@ func (c *Client) Setup() error {
|
|||
return err
|
||||
}
|
||||
|
||||
if strings.TrimSpace(c.RefreshToken.Value) != "" {
|
||||
if err := c.configure(); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
debuglog.Log("Codex configure failed; starting browser login: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), oauthTimeout)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -210,20 +165,35 @@ func (c *Client) configure() error {
|
|||
option.WithHTTPClient(c.apiHTTPClient),
|
||||
)
|
||||
c.ApiClient = &apiClient
|
||||
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account=%s base_url=%s\n", c.AccountID.Value, c.ApiBaseURL.Value)
|
||||
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account_present=%t base_url=%s\n", strings.TrimSpace(c.AccountID.Value) != "", c.ApiBaseURL.Value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadEnvSettings copies non-empty Codex token values from env.
|
||||
func (c *Client) LoadEnvSettings(env map[string]string) {
|
||||
apply := func(setting *plugins.Setting) {
|
||||
if setting == nil || setting.EnvVariable == "" {
|
||||
return
|
||||
}
|
||||
if value := strings.TrimSpace(env[setting.EnvVariable]); value != "" {
|
||||
c.setSettingValue(setting, value)
|
||||
}
|
||||
}
|
||||
apply(c.AccessToken)
|
||||
apply(c.RefreshToken)
|
||||
apply(c.AccountID)
|
||||
}
|
||||
|
||||
// ListModels returns the Codex models available to the configured account.
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(ctx context.Context) ([]string, error) {
|
||||
if c.apiHTTPClient == nil {
|
||||
if err := c.configure(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), modelsRequestTimeout)
|
||||
ctx, cancel := context.WithTimeout(ctx, modelsRequestTimeout)
|
||||
defer cancel()
|
||||
|
||||
modelsURL := strings.TrimRight(c.ApiBaseURL.Value, "/") + "/models"
|
||||
|
|
@ -252,7 +222,7 @@ func (c *Client) ListModels() ([]string, error) {
|
|||
|
||||
var decoded modelsResponse
|
||||
if err := json.Unmarshal(body, &decoded); err != nil {
|
||||
return nil, fmt.Errorf("failed to decode Codex models response: %w", err)
|
||||
return nil, fmt.Errorf(i18n.T("codex_decode_models_response_failed"), err)
|
||||
}
|
||||
|
||||
models := make([]string, 0, len(decoded.Models))
|
||||
|
|
@ -299,16 +269,19 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
|||
if err := c.mapRequestError(stream.Err()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
streamedText := builder.String()
|
||||
if completedResp != nil {
|
||||
return c.ExtractText(completedResp), nil
|
||||
if extractedText := c.ExtractText(completedResp); strings.TrimSpace(extractedText) != "" {
|
||||
return extractedText, nil
|
||||
}
|
||||
}
|
||||
|
||||
return builder.String(), nil
|
||||
return streamedText, nil
|
||||
}
|
||||
|
||||
// SendStream sends a request to Codex and streams the response text updates.
|
||||
func (c *Client) SendStream(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
) error {
|
||||
defer close(channel)
|
||||
|
||||
|
|
@ -322,15 +295,17 @@ func (c *Client) SendStream(
|
|||
}
|
||||
|
||||
req := c.buildCodexResponseParams(msgs, opts)
|
||||
stream := c.ApiClient.Responses.NewStreaming(context.Background(), req)
|
||||
stream := c.ApiClient.Responses.NewStreaming(ctx, req)
|
||||
defer stream.Close()
|
||||
for stream.Next() {
|
||||
event := stream.Current()
|
||||
switch event.Type {
|
||||
case string(constant.ResponseOutputTextDelta("").Default()):
|
||||
channel <- domain.StreamUpdate{
|
||||
if err := sendStreamUpdate(ctx, channel, domain.StreamUpdate{
|
||||
Type: domain.StreamTypeContent,
|
||||
Content: event.AsResponseOutputTextDelta().Delta,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
case string(constant.ResponseOutputTextDone("").Default()):
|
||||
continue
|
||||
|
|
@ -338,15 +313,26 @@ func (c *Client) SendStream(
|
|||
}
|
||||
|
||||
if stream.Err() == nil {
|
||||
channel <- domain.StreamUpdate{
|
||||
if err := sendStreamUpdate(ctx, channel, domain.StreamUpdate{
|
||||
Type: domain.StreamTypeContent,
|
||||
Content: "\n",
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return c.mapRequestError(stream.Err())
|
||||
}
|
||||
|
||||
func sendStreamUpdate(ctx context.Context, channel chan domain.StreamUpdate, update domain.StreamUpdate) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
case channel <- update:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) buildCodexResponseParams(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions,
|
||||
) responses.ResponseNewParams {
|
||||
|
|
@ -404,669 +390,3 @@ func codexMessageText(msg chat.ChatCompletionMessage) string {
|
|||
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
func (c *Client) runOAuthFlow(
|
||||
ctx context.Context,
|
||||
openBrowserFn func(string) error,
|
||||
) (oauthTokens, error) {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", defaultCallbackPort))
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf("failed to start local OAuth callback server: %w", err)
|
||||
}
|
||||
defer listener.Close()
|
||||
debuglog.Debug(debuglog.Detailed, "Codex OAuth callback listener started on 127.0.0.1:%d\n", defaultCallbackPort)
|
||||
|
||||
pkce, err := generatePKCECodes()
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
state, err := randomBase64URL(oauthStateBytes)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
callbackURL := (&url.URL{
|
||||
Scheme: "http",
|
||||
Host: fmt.Sprintf("localhost:%d", defaultCallbackPort),
|
||||
Path: oauthCallbackPath,
|
||||
}).String()
|
||||
authURL, err := buildAuthorizeURL(c.AuthBaseURL.Value, callbackURL, pkce, state)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
results := make(chan oauthResult, 1)
|
||||
server := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c.handleOAuthCallback(w, r, callbackURL, pkce, state, results)
|
||||
}),
|
||||
}
|
||||
|
||||
serveDone := make(chan error, 1)
|
||||
go func() {
|
||||
err := server.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
serveDone <- err
|
||||
return
|
||||
}
|
||||
serveDone <- nil
|
||||
}()
|
||||
|
||||
if err := openBrowserFn(authURL); err != nil {
|
||||
fmt.Printf("If your browser did not open, navigate to this URL to authenticate:\n%s\n", authURL)
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-results:
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
_ = server.Shutdown(shutdownCtx)
|
||||
<-serveDone
|
||||
return result.tokens, result.err
|
||||
case err := <-serveDone:
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_server_stopped"))
|
||||
case <-ctx.Done():
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
_ = server.Shutdown(shutdownCtx)
|
||||
<-serveDone
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_timed_out"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleOAuthCallback(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
callbackURL string,
|
||||
pkce pkceCodes,
|
||||
expectedState string,
|
||||
results chan<- oauthResult,
|
||||
) {
|
||||
if r.URL.Path != oauthCallbackPath {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Query().Get("state") != expectedState {
|
||||
http.Error(w, "State mismatch", http.StatusBadRequest)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: errors.New(i18n.T("codex_login_state_mismatch")),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if callbackError := strings.TrimSpace(r.URL.Query().Get("error")); callbackError != "" {
|
||||
description := strings.TrimSpace(r.URL.Query().Get("error_description"))
|
||||
if description != "" {
|
||||
http.Error(w, description, http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: fmt.Errorf(i18n.T("codex_login_failed"), description),
|
||||
})
|
||||
return
|
||||
}
|
||||
http.Error(w, callbackError, http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: fmt.Errorf(i18n.T("codex_login_failed"), callbackError),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
code := strings.TrimSpace(r.URL.Query().Get("code"))
|
||||
if code == "" {
|
||||
http.Error(w, "Missing authorization code", http.StatusBadRequest)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: errors.New(i18n.T("codex_login_missing_auth_code")),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
tokens, err := c.exchangeCodeForTokens(r.Context(), callbackURL, pkce, code)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
c.publishOAuthResult(results, oauthResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.extractAccountID(tokens.IDToken, tokens.AccessToken); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
_, _ = w.Write([]byte("<html><body><h1>Codex login completed</h1><p>Return to Fabric.</p></body></html>"))
|
||||
c.publishOAuthResult(results, oauthResult{tokens: tokens})
|
||||
}
|
||||
|
||||
func (c *Client) publishOAuthResult(results chan<- oauthResult, result oauthResult) {
|
||||
select {
|
||||
case results <- result:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) exchangeCodeForTokens(
|
||||
ctx context.Context,
|
||||
callbackURL string,
|
||||
pkce pkceCodes,
|
||||
code string,
|
||||
) (oauthTokens, error) {
|
||||
form := url.Values{}
|
||||
form.Set("grant_type", "authorization_code")
|
||||
form.Set("code", code)
|
||||
form.Set("redirect_uri", callbackURL)
|
||||
form.Set("client_id", oauthClientID)
|
||||
form.Set("code_verifier", pkce.CodeVerifier)
|
||||
|
||||
tokenURL := strings.TrimRight(c.AuthBaseURL.Value, "/") + "/oauth/token"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp, err := c.authHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf("Codex token exchange failed: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return oauthTokens{}, c.errorFromHTTPResponse(resp.StatusCode, body)
|
||||
}
|
||||
|
||||
var tokens oauthTokens
|
||||
if err := json.Unmarshal(body, &tokens); err != nil {
|
||||
return oauthTokens{}, fmt.Errorf("failed to decode Codex token exchange response: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(tokens.AccessToken) == "" || strings.TrimSpace(tokens.RefreshToken) == "" {
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_missing_tokens"))
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func (c *Client) ensureAccessToken(ctx context.Context, forceRefresh bool) (string, string, error) {
|
||||
c.tokenMu.Lock()
|
||||
defer c.tokenMu.Unlock()
|
||||
|
||||
accessToken := strings.TrimSpace(c.AccessToken.Value)
|
||||
accountID := strings.TrimSpace(c.AccountID.Value)
|
||||
|
||||
if !forceRefresh && accessToken != "" && !tokenNeedsRefresh(accessToken, time.Now()) {
|
||||
if accountID == "" {
|
||||
parsedAccountID, err := extractAccountIDFromJWT(accessToken)
|
||||
if err == nil && parsedAccountID != "" {
|
||||
accountID = parsedAccountID
|
||||
c.setSettingValue(c.AccountID, accountID)
|
||||
}
|
||||
}
|
||||
if accountID != "" {
|
||||
return accessToken, accountID, nil
|
||||
}
|
||||
}
|
||||
|
||||
refreshed, err := c.refreshAccessToken(ctx)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
refreshedAccountID, err := c.extractAccountID(refreshed.IDToken, refreshed.AccessToken)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
if accountID != "" && refreshedAccountID != "" && !strings.EqualFold(accountID, refreshedAccountID) {
|
||||
return "", "", errors.New(i18n.T("codex_login_account_changed"))
|
||||
}
|
||||
|
||||
c.setSettingValue(c.AccessToken, refreshed.AccessToken)
|
||||
if strings.TrimSpace(refreshed.RefreshToken) != "" {
|
||||
c.setSettingValue(c.RefreshToken, refreshed.RefreshToken)
|
||||
}
|
||||
c.setSettingValue(c.AccountID, refreshedAccountID)
|
||||
debuglog.Debug(debuglog.Detailed, "Codex access token refreshed for account=%s\n", refreshedAccountID)
|
||||
|
||||
return c.AccessToken.Value, c.AccountID.Value, nil
|
||||
}
|
||||
|
||||
func (c *Client) refreshAccessToken(ctx context.Context) (oauthTokens, error) {
|
||||
payload := refreshRequest{
|
||||
ClientID: oauthClientID,
|
||||
GrantType: "refresh_token",
|
||||
RefreshToken: strings.TrimSpace(c.RefreshToken.Value),
|
||||
}
|
||||
|
||||
body, err := json.Marshal(payload)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
tokenURL := strings.TrimRight(c.AuthBaseURL.Value, "/") + "/oauth/token"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(string(body)))
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := c.authHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf("failed to refresh Codex login: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
responseBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return oauthTokens{}, c.refreshErrorFromResponse(resp.StatusCode, responseBody)
|
||||
}
|
||||
|
||||
var refreshed refreshResponse
|
||||
if err := json.Unmarshal(responseBody, &refreshed); err != nil {
|
||||
return oauthTokens{}, fmt.Errorf("failed to decode refreshed Codex token response: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(refreshed.AccessToken) == "" {
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_token_refresh_missing_access_token"))
|
||||
}
|
||||
|
||||
return oauthTokens{
|
||||
IDToken: strings.TrimSpace(refreshed.IDToken),
|
||||
AccessToken: strings.TrimSpace(refreshed.AccessToken),
|
||||
RefreshToken: strings.TrimSpace(refreshed.RefreshToken),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) extractAccountID(idToken string, accessToken string) (string, error) {
|
||||
if accountID, err := extractAccountIDFromJWT(idToken); err == nil && accountID != "" {
|
||||
return accountID, nil
|
||||
}
|
||||
if accountID, err := extractAccountIDFromJWT(accessToken); err == nil && accountID != "" {
|
||||
return accountID, nil
|
||||
}
|
||||
return "", errors.New(i18n.T("codex_login_missing_account_claim"))
|
||||
}
|
||||
|
||||
func (c *Client) setSettingValue(setting *plugins.Setting, value string) {
|
||||
setting.Value = value
|
||||
if setting.EnvVariable != "" {
|
||||
_ = os.Setenv(setting.EnvVariable, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) errorFromHTTPResponse(statusCode int, body []byte) error {
|
||||
message := extractErrorMessage(body)
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
return errors.New(i18n.T("codex_login_invalid"))
|
||||
}
|
||||
if isUsageLimitMessage(message) {
|
||||
return errors.New(message)
|
||||
}
|
||||
if message == "" {
|
||||
message = fmt.Sprintf("Codex request failed with status %d", statusCode)
|
||||
}
|
||||
return errors.New(message)
|
||||
}
|
||||
|
||||
func (c *Client) refreshErrorFromResponse(statusCode int, body []byte) error {
|
||||
message := extractErrorMessage(body)
|
||||
code := strings.ToLower(extractErrorCode(body))
|
||||
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
switch code {
|
||||
case "refresh_token_expired", "refresh_token_reused", "refresh_token_invalidated":
|
||||
return errors.New(i18n.T("codex_login_revoked"))
|
||||
default:
|
||||
return errors.New(i18n.T("codex_login_refresh_failed"))
|
||||
}
|
||||
}
|
||||
|
||||
if message == "" {
|
||||
message = fmt.Sprintf("failed to refresh Codex login (status %d)", statusCode)
|
||||
}
|
||||
return errors.New(message)
|
||||
}
|
||||
|
||||
func (c *Client) mapRequestError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
var apiErr *openaiapi.Error
|
||||
if errors.As(err, &apiErr) {
|
||||
body := []byte(apiErr.RawJSON())
|
||||
if len(body) == 0 {
|
||||
body = readAPIErrorBody(apiErr)
|
||||
}
|
||||
return c.errorFromHTTPResponse(apiErr.StatusCode, body)
|
||||
}
|
||||
|
||||
message := err.Error()
|
||||
lower := strings.ToLower(message)
|
||||
|
||||
switch {
|
||||
case strings.Contains(lower, "status code 401"),
|
||||
strings.Contains(lower, "401 unauthorized"),
|
||||
strings.Contains(lower, "refresh token"),
|
||||
strings.Contains(lower, "chatgpt login"):
|
||||
return errors.New(i18n.T("codex_login_invalid"))
|
||||
case isUsageLimitMessage(message):
|
||||
return errors.New(message)
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func readAPIErrorBody(apiErr *openaiapi.Error) []byte {
|
||||
if apiErr == nil || apiErr.Response == nil || apiErr.Response.Body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(apiErr.Response.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
apiErr.Response.Body = io.NopCloser(strings.NewReader(string(body)))
|
||||
return body
|
||||
}
|
||||
|
||||
// RoundTrip adds Codex authentication headers and retries once after a 401.
|
||||
func (t *authTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t.roundTrip(req, false)
|
||||
}
|
||||
|
||||
func (t *authTransport) roundTrip(req *http.Request, retried bool) (*http.Response, error) {
|
||||
token, accountID, err := t.client.ensureAccessToken(req.Context(), false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
clone, err := cloneRequest(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clone.Header.Set(http.CanonicalHeaderKey("originator"), defaultOriginator)
|
||||
clone.Header.Set("User-Agent", defaultUserAgent)
|
||||
clone.Header.Set("Authorization", "Bearer "+token)
|
||||
clone.Header.Set("ChatGPT-Account-ID", accountID)
|
||||
|
||||
resp, err := t.roundTripper().RoundTrip(clone)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusUnauthorized || retried {
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
drainAndClose(resp.Body)
|
||||
debuglog.Debug(debuglog.Detailed, "Codex request returned 401; attempting token refresh and one retry\n")
|
||||
|
||||
if _, _, err := t.client.ensureAccessToken(req.Context(), true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return t.roundTrip(req, true)
|
||||
}
|
||||
|
||||
func (t *authTransport) roundTripper() http.RoundTripper {
|
||||
if t.wrapped != nil {
|
||||
return t.wrapped
|
||||
}
|
||||
return http.DefaultTransport
|
||||
}
|
||||
|
||||
func cloneRequest(req *http.Request) (*http.Request, error) {
|
||||
clone := req.Clone(req.Context())
|
||||
if req.Body == nil || req.Body == http.NoBody {
|
||||
return clone, nil
|
||||
}
|
||||
// Codex retry logic assumes GetBody is available so the request can be replayed after refresh.
|
||||
if req.GetBody == nil {
|
||||
return nil, errReplayBodyUnavailable
|
||||
}
|
||||
|
||||
body, err := req.GetBody()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
clone.Body = body
|
||||
return clone, nil
|
||||
}
|
||||
|
||||
func drainAndClose(body io.ReadCloser) {
|
||||
if body == nil {
|
||||
return
|
||||
}
|
||||
_, _ = io.Copy(io.Discard, io.LimitReader(body, defaultRoundTripLimit))
|
||||
_ = body.Close()
|
||||
}
|
||||
|
||||
func buildAuthorizeURL(authBaseURL string, callbackURL string, pkce pkceCodes, state string) (string, error) {
|
||||
issuer, err := url.Parse(strings.TrimRight(authBaseURL, "/"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("invalid Codex auth base URL: %w", err)
|
||||
}
|
||||
|
||||
issuer.Path = strings.TrimRight(issuer.Path, "/") + "/oauth/authorize"
|
||||
query := issuer.Query()
|
||||
query.Set("response_type", "code")
|
||||
query.Set("client_id", oauthClientID)
|
||||
query.Set("redirect_uri", callbackURL)
|
||||
query.Set("scope", oauthScope)
|
||||
query.Set("code_challenge", pkce.CodeChallenge)
|
||||
query.Set("code_challenge_method", "S256")
|
||||
query.Set("id_token_add_organizations", "true")
|
||||
query.Set("codex_cli_simplified_flow", "true")
|
||||
query.Set("state", state)
|
||||
query.Set("originator", defaultOriginator)
|
||||
issuer.RawQuery = query.Encode()
|
||||
|
||||
return issuer.String(), nil
|
||||
}
|
||||
|
||||
type pkceCodes struct {
|
||||
CodeVerifier string
|
||||
CodeChallenge string
|
||||
}
|
||||
|
||||
func generatePKCECodes() (pkceCodes, error) {
|
||||
verifier, err := randomBase64URL(oauthVerifierBytes)
|
||||
if err != nil {
|
||||
return pkceCodes{}, err
|
||||
}
|
||||
|
||||
sum := sha256.Sum256([]byte(verifier))
|
||||
return pkceCodes{
|
||||
CodeVerifier: verifier,
|
||||
CodeChallenge: base64.RawURLEncoding.EncodeToString(sum[:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func randomBase64URL(size int) (string, error) {
|
||||
buf := make([]byte, size)
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
return "", fmt.Errorf("failed to generate secure random OAuth state: %w", err)
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
func tokenNeedsRefresh(jwt string, now time.Time) bool {
|
||||
expiry, err := extractExpiryFromJWT(jwt)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return now.Add(tokenRefreshLeeway).After(expiry)
|
||||
}
|
||||
|
||||
func extractExpiryFromJWT(jwt string) (time.Time, error) {
|
||||
claims, err := parseTokenClaims(jwt)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
if claims.Exp == 0 {
|
||||
return time.Time{}, errors.New("JWT did not include an exp claim")
|
||||
}
|
||||
return time.Unix(claims.Exp, 0), nil
|
||||
}
|
||||
|
||||
func extractAccountIDFromJWT(jwt string) (string, error) {
|
||||
claims, err := parseTokenClaims(jwt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(claims.Auth.ChatGPTAccountID), nil
|
||||
}
|
||||
|
||||
func parseTokenClaims(jwt string) (tokenClaims, error) {
|
||||
parts := strings.Split(jwt, ".")
|
||||
if len(parts) < 2 {
|
||||
return tokenClaims{}, errors.New("invalid JWT format")
|
||||
}
|
||||
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return tokenClaims{}, err
|
||||
}
|
||||
|
||||
var claims tokenClaims
|
||||
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||
return tokenClaims{}, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func extractErrorMessage(body []byte) string {
|
||||
if len(body) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return strings.TrimSpace(string(body))
|
||||
}
|
||||
|
||||
if errorValue, ok := payload["error"]; ok {
|
||||
switch typed := errorValue.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
case map[string]any:
|
||||
if message, ok := typed["message"].(string); ok && strings.TrimSpace(message) != "" {
|
||||
return strings.TrimSpace(message)
|
||||
}
|
||||
if code, ok := typed["code"].(string); ok && strings.TrimSpace(code) != "" {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if message, ok := payload["message"].(string); ok {
|
||||
return strings.TrimSpace(message)
|
||||
}
|
||||
if detail, ok := payload["detail"].(string); ok {
|
||||
return strings.TrimSpace(detail)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(body))
|
||||
}
|
||||
|
||||
func extractErrorCode(body []byte) string {
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if code, ok := payload["code"].(string); ok {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
|
||||
errorValue, ok := payload["error"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch typed := errorValue.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
case map[string]any:
|
||||
if code, ok := typed["code"].(string); ok {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func codexClientVersion() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
if version := normalizeSemverLikeVersion(info.Main.Version); version != "" {
|
||||
return version
|
||||
}
|
||||
}
|
||||
|
||||
return defaultClientVersion
|
||||
}
|
||||
|
||||
func normalizeSemverLikeVersion(version string) string {
|
||||
version = strings.TrimSpace(version)
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
if version == "" || version == "(devel)" {
|
||||
return ""
|
||||
}
|
||||
|
||||
end := len(version)
|
||||
for i, r := range version {
|
||||
if (r < '0' || r > '9') && r != '.' {
|
||||
end = i
|
||||
break
|
||||
}
|
||||
}
|
||||
version = strings.Trim(version[:end], ".")
|
||||
if version == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) < 3 {
|
||||
return ""
|
||||
}
|
||||
if slices.Contains(parts[:3], "") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.Join(parts[:3], ".")
|
||||
}
|
||||
|
||||
func isUsageLimitMessage(message string) bool {
|
||||
lower := strings.ToLower(strings.TrimSpace(message))
|
||||
if lower == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(lower, "usage limit") ||
|
||||
strings.Contains(lower, "purchase more credits") ||
|
||||
strings.Contains(lower, "upgrade to plus") ||
|
||||
strings.Contains(lower, "upgrade to pro") ||
|
||||
strings.Contains(lower, "plan and billing")
|
||||
}
|
||||
|
||||
func openBrowser(targetURL string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", targetURL)
|
||||
case "windows":
|
||||
cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", targetURL)
|
||||
default:
|
||||
cmd = exec.Command("xdg-open", targetURL)
|
||||
}
|
||||
return cmd.Start()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
|
|
@ -18,6 +19,7 @@ import (
|
|||
|
||||
"github.com/danielmiessler/fabric/internal/chat"
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
openaiapi "github.com/openai/openai-go"
|
||||
"github.com/openai/openai-go/shared/constant"
|
||||
)
|
||||
|
|
@ -176,7 +178,7 @@ func TestListModelsFiltersSupportedVisibleModels(t *testing.T) {
|
|||
|
||||
client := newConfiguredTestClient(t, modelsServer.URL, "acct_models", testJWT("acct_models", time.Now().Add(time.Hour)))
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("ListModels() error = %v", err)
|
||||
}
|
||||
|
|
@ -210,6 +212,10 @@ func TestNormalizeSemverLikeVersion(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
client := NewClient()
|
||||
apiErr := &openaiapi.Error{StatusCode: http.StatusBadRequest}
|
||||
if err := apiErr.UnmarshalJSON([]byte(`{"message":"The requested model is not supported.","type":"invalid_request_error","param":"model","code":"invalid_value"}`)); err != nil {
|
||||
|
|
@ -220,12 +226,20 @@ func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("mapRequestError() returned nil")
|
||||
}
|
||||
if got := err.Error(); got != "The requested model is not supported." {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, "The requested model is not supported.")
|
||||
want := fmt.Sprintf(i18n.T("codex_request_failed_status"), http.StatusBadRequest)
|
||||
if got := err.Error(); got != want {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, want)
|
||||
}
|
||||
if unwrapped := errors.Unwrap(err); unwrapped == nil || !strings.Contains(unwrapped.Error(), "The requested model is not supported.") {
|
||||
t.Fatalf("wrapped error = %v, want provider detail", unwrapped)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMapRequestErrorReadsAPIErrorResponseBodyWhenRawJSONMissing(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
client := NewClient()
|
||||
apiErr := &openaiapi.Error{
|
||||
StatusCode: http.StatusBadRequest,
|
||||
|
|
@ -238,8 +252,12 @@ func TestMapRequestErrorReadsAPIErrorResponseBodyWhenRawJSONMissing(t *testing.T
|
|||
if err == nil {
|
||||
t.Fatal("mapRequestError() returned nil")
|
||||
}
|
||||
if got := err.Error(); got != "The requested model is not supported for Codex." {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, "The requested model is not supported for Codex.")
|
||||
want := fmt.Sprintf(i18n.T("codex_request_failed_status"), http.StatusBadRequest)
|
||||
if got := err.Error(); got != want {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, want)
|
||||
}
|
||||
if unwrapped := errors.Unwrap(err); unwrapped == nil || !strings.Contains(unwrapped.Error(), "The requested model is not supported for Codex.") {
|
||||
t.Fatalf("wrapped error = %v, want provider detail", unwrapped)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -422,6 +440,61 @@ func TestSendIncludesSourcesFromAnnotatedResponse(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSendFallsBackToDeltaWhenCompletedResponseHasNoText(t *testing.T) {
|
||||
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/responses" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/event-stream")
|
||||
flusher, ok := w.(http.Flusher)
|
||||
if !ok {
|
||||
t.Fatalf("response writer does not implement http.Flusher")
|
||||
}
|
||||
fmt.Fprintf(w, "data: %s\n\n", marshalJSON(t, map[string]any{
|
||||
"type": string(constant.ResponseOutputTextDelta("").Default()),
|
||||
"delta": "hello from delta",
|
||||
}))
|
||||
flusher.Flush()
|
||||
fmt.Fprintf(w, "data: %s\n\n", marshalJSON(t, map[string]any{
|
||||
"type": "response.completed",
|
||||
"response": map[string]any{
|
||||
"output": []any{
|
||||
map[string]any{
|
||||
"type": "message",
|
||||
"content": []any{
|
||||
map[string]any{
|
||||
"type": "output_text",
|
||||
"text": "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
flusher.Flush()
|
||||
fmt.Fprint(w, "data: [DONE]\n\n")
|
||||
}))
|
||||
defer apiServer.Close()
|
||||
|
||||
client := newConfiguredTestClient(t, apiServer.URL, "acct_delta_fallback", testJWT("acct_delta_fallback", time.Now().Add(time.Hour)))
|
||||
|
||||
message, err := client.Send(context.Background(), []*chat.ChatCompletionMessage{
|
||||
{Role: chat.ChatMessageRoleUser, Content: "Hello"},
|
||||
}, &domain.ChatOptions{
|
||||
Model: "gpt-5.4",
|
||||
Temperature: 0.7,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("Send() error = %v", err)
|
||||
}
|
||||
|
||||
if message != "hello from delta" {
|
||||
t.Fatalf("Send() = %q, want %q", message, "hello from delta")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSendStreamReadsCodexSSE(t *testing.T) {
|
||||
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/responses" {
|
||||
|
|
@ -469,7 +542,7 @@ func TestSendStreamReadsCodexSSE(t *testing.T) {
|
|||
client := newConfiguredTestClient(t, apiServer.URL, "acct_stream", testJWT("acct_stream", time.Now().Add(time.Hour)))
|
||||
|
||||
updates := make(chan domain.StreamUpdate, 8)
|
||||
err := client.SendStream([]*chat.ChatCompletionMessage{
|
||||
err := client.SendStream(context.Background(), []*chat.ChatCompletionMessage{
|
||||
{Role: chat.ChatMessageRoleSystem, Content: "Follow the system prompt"},
|
||||
{Role: "user", Content: "Hello"},
|
||||
}, &domain.ChatOptions{
|
||||
|
|
@ -492,6 +565,190 @@ func TestSendStreamReadsCodexSSE(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestSendStreamClosesChannelAndMapsHTTPError(t *testing.T) {
|
||||
apiServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/responses" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
http.Error(w, `{"error":{"message":"usage limit reached"}}`, http.StatusTooManyRequests)
|
||||
}))
|
||||
defer apiServer.Close()
|
||||
|
||||
client := newConfiguredTestClient(t, apiServer.URL, "acct_stream_error", testJWT("acct_stream_error", time.Now().Add(time.Hour)))
|
||||
|
||||
updates := make(chan domain.StreamUpdate, 1)
|
||||
err := client.SendStream(context.Background(), []*chat.ChatCompletionMessage{
|
||||
{Role: chat.ChatMessageRoleUser, Content: "Hello"},
|
||||
}, &domain.ChatOptions{
|
||||
Model: "gpt-5.4",
|
||||
}, updates)
|
||||
if err == nil {
|
||||
t.Fatal("SendStream() error = nil, want mapped HTTP error")
|
||||
}
|
||||
if got := err.Error(); got != i18n.T("codex_usage_limit_reached") {
|
||||
t.Fatalf("SendStream() error = %q, want %q", got, i18n.T("codex_usage_limit_reached"))
|
||||
}
|
||||
|
||||
update, ok := <-updates
|
||||
if ok {
|
||||
t.Fatalf("expected closed channel after stream error, got update %#v", update)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureAccessTokenPersistsRotatedRefreshToken(t *testing.T) {
|
||||
authServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/oauth/token" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(refreshResponse{
|
||||
IDToken: testJWT("acct_persist", time.Now().Add(2*time.Hour)),
|
||||
AccessToken: testJWT("acct_persist", time.Now().Add(2*time.Hour)),
|
||||
RefreshToken: "refresh-rotated",
|
||||
})
|
||||
}))
|
||||
defer authServer.Close()
|
||||
|
||||
var persisted map[string]string
|
||||
client := NewClient()
|
||||
client.ApiBaseURL.Value = "https://example.invalid"
|
||||
client.AuthBaseURL.Value = authServer.URL
|
||||
client.RefreshToken.Value = "refresh-old"
|
||||
client.AccessToken.Value = testJWT("acct_persist", time.Now().Add(-time.Hour))
|
||||
client.AccountID.Value = "acct_persist"
|
||||
client.TokenPersist = func() error {
|
||||
persisted = map[string]string{
|
||||
"access": client.AccessToken.Value,
|
||||
"refresh": client.RefreshToken.Value,
|
||||
"account": client.AccountID.Value,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
access, account, err := client.ensureAccessToken(context.Background(), false)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureAccessToken() error = %v", err)
|
||||
}
|
||||
if access == "" {
|
||||
t.Fatal("ensureAccessToken() returned empty access token")
|
||||
}
|
||||
if account != "acct_persist" {
|
||||
t.Fatalf("account = %q, want acct_persist", account)
|
||||
}
|
||||
if persisted["refresh"] != "refresh-rotated" {
|
||||
t.Fatalf("persisted refresh = %q, want refresh-rotated", persisted["refresh"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureAccessTokenPersistError(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
authServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path != "/oauth/token" {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(refreshResponse{
|
||||
IDToken: testJWT("acct_persist", time.Now().Add(2*time.Hour)),
|
||||
AccessToken: testJWT("acct_persist", time.Now().Add(2*time.Hour)),
|
||||
RefreshToken: "refresh-rotated",
|
||||
})
|
||||
}))
|
||||
defer authServer.Close()
|
||||
|
||||
persistErr := errors.New("disk full")
|
||||
client := NewClient()
|
||||
client.ApiBaseURL.Value = "https://example.invalid"
|
||||
client.AuthBaseURL.Value = authServer.URL
|
||||
client.RefreshToken.Value = "refresh-old"
|
||||
client.AccessToken.Value = testJWT("acct_persist", time.Now().Add(-time.Hour))
|
||||
client.AccountID.Value = "acct_persist"
|
||||
client.TokenPersist = func() error {
|
||||
return persistErr
|
||||
}
|
||||
|
||||
_, _, err := client.ensureAccessToken(context.Background(), false)
|
||||
if err == nil {
|
||||
t.Fatal("ensureAccessToken() error = nil, want persist error")
|
||||
}
|
||||
if !errors.Is(err, persistErr) {
|
||||
t.Fatalf("ensureAccessToken() error = %v, want persist error %v", err, persistErr)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnsureAccessTokenReloadsStoreBeforeRefresh(t *testing.T) {
|
||||
authHits := 0
|
||||
authServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
authHits++
|
||||
http.Error(w, "unexpected refresh", http.StatusInternalServerError)
|
||||
}))
|
||||
defer authServer.Close()
|
||||
|
||||
fresh := testJWT("acct_reload", time.Now().Add(2*time.Hour))
|
||||
client := NewClient()
|
||||
client.ApiBaseURL.Value = "https://example.invalid"
|
||||
client.AuthBaseURL.Value = authServer.URL
|
||||
client.RefreshToken.Value = "refresh-old"
|
||||
client.AccessToken.Value = testJWT("acct_reload", time.Now().Add(-time.Hour))
|
||||
client.AccountID.Value = "acct_reload"
|
||||
client.WithStoreLock = func(fn func() error) error {
|
||||
client.AccessToken.Value = fresh
|
||||
client.AccountID.Value = "acct_reload"
|
||||
return fn()
|
||||
}
|
||||
|
||||
access, account, err := client.ensureAccessToken(context.Background(), false)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureAccessToken() error = %v", err)
|
||||
}
|
||||
if access != fresh {
|
||||
t.Fatal("ensureAccessToken() did not use reloaded access token")
|
||||
}
|
||||
if account != "acct_reload" {
|
||||
t.Fatalf("account = %q, want acct_reload", account)
|
||||
}
|
||||
if authHits != 0 {
|
||||
t.Fatalf("auth server hits = %d, want 0", authHits)
|
||||
}
|
||||
}
|
||||
|
||||
// Regression: a valid in-memory token (e.g. just obtained via Setup's OAuth)
|
||||
// must not be reloaded from the store, which still holds the stale token until
|
||||
// SaveEnvFile runs. The fast path returns before WithStoreLock can clobber it.
|
||||
func TestEnsureAccessTokenKeepsFreshTokenOverStore(t *testing.T) {
|
||||
fresh := testJWT("acct_fresh", time.Now().Add(2*time.Hour))
|
||||
client := NewClient()
|
||||
client.ApiBaseURL.Value = "https://example.invalid"
|
||||
client.AuthBaseURL.Value = "https://auth.invalid"
|
||||
client.RefreshToken.Value = "refresh-new"
|
||||
client.AccessToken.Value = fresh
|
||||
client.AccountID.Value = "acct_fresh"
|
||||
|
||||
storeLocked := false
|
||||
client.WithStoreLock = func(fn func() error) error {
|
||||
storeLocked = true
|
||||
client.AccessToken.Value = "stale-from-disk"
|
||||
client.AccountID.Value = "acct_stale"
|
||||
return fn()
|
||||
}
|
||||
|
||||
access, account, err := client.ensureAccessToken(context.Background(), false)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureAccessToken() error = %v", err)
|
||||
}
|
||||
if storeLocked {
|
||||
t.Fatal("store lock ran and clobbered a valid in-memory token")
|
||||
}
|
||||
if access != fresh || account != "acct_fresh" {
|
||||
t.Fatalf("got access=%q account=%q, want fresh token for acct_fresh", access, account)
|
||||
}
|
||||
}
|
||||
|
||||
func newConfiguredTestClient(t *testing.T, apiBaseURL string, accountID string, accessToken string) *Client {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
|||
183
internal/plugins/ai/codex/errors.go
Normal file
183
internal/plugins/ai/codex/errors.go
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
package codex
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
openaiapi "github.com/openai/openai-go"
|
||||
)
|
||||
|
||||
type publicError struct {
|
||||
message string
|
||||
cause error
|
||||
}
|
||||
|
||||
func (e *publicError) Error() string {
|
||||
return e.message
|
||||
}
|
||||
|
||||
func (e *publicError) Unwrap() error {
|
||||
return e.cause
|
||||
}
|
||||
|
||||
func (c *Client) errorFromHTTPResponse(statusCode int, body []byte) error {
|
||||
message := extractErrorMessage(body)
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
return errors.New(i18n.T("codex_login_invalid"))
|
||||
}
|
||||
if isUsageLimitMessage(message) {
|
||||
return wrapPublicError(i18n.T("codex_usage_limit_reached"), statusCode, message)
|
||||
}
|
||||
return wrapPublicError(fmt.Sprintf(i18n.T("codex_request_failed_status"), statusCode), statusCode, message)
|
||||
}
|
||||
|
||||
func (c *Client) refreshErrorFromResponse(statusCode int, body []byte) error {
|
||||
message := extractErrorMessage(body)
|
||||
code := strings.ToLower(extractErrorCode(body))
|
||||
|
||||
if statusCode == http.StatusUnauthorized {
|
||||
switch code {
|
||||
case "refresh_token_expired", "refresh_token_reused", "refresh_token_invalidated":
|
||||
return errors.New(i18n.T("codex_login_revoked"))
|
||||
default:
|
||||
return errors.New(i18n.T("codex_login_refresh_failed"))
|
||||
}
|
||||
}
|
||||
|
||||
return wrapPublicError(fmt.Sprintf(i18n.T("codex_refresh_failed_status"), statusCode), statusCode, message)
|
||||
}
|
||||
|
||||
func (c *Client) mapRequestError(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if apiErr, ok := errors.AsType[*openaiapi.Error](err); ok {
|
||||
body := []byte(apiErr.RawJSON())
|
||||
if len(body) == 0 {
|
||||
body = readAPIErrorBody(apiErr)
|
||||
}
|
||||
return c.errorFromHTTPResponse(apiErr.StatusCode, body)
|
||||
}
|
||||
|
||||
message := err.Error()
|
||||
lower := strings.ToLower(message)
|
||||
|
||||
switch {
|
||||
case strings.Contains(lower, "status code 401"),
|
||||
strings.Contains(lower, "401 unauthorized"),
|
||||
strings.Contains(lower, "refresh token"),
|
||||
strings.Contains(lower, "chatgpt login"):
|
||||
return errors.New(i18n.T("codex_login_invalid"))
|
||||
case isUsageLimitMessage(message):
|
||||
return &publicError{
|
||||
message: i18n.T("codex_usage_limit_reached"),
|
||||
cause: fmt.Errorf(i18n.T("codex_request_failed"), err),
|
||||
}
|
||||
default:
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func wrapPublicError(message string, statusCode int, providerMessage string) error {
|
||||
if providerMessage == "" {
|
||||
return errors.New(message)
|
||||
}
|
||||
|
||||
return &publicError{
|
||||
message: message,
|
||||
cause: fmt.Errorf(i18n.T("codex_provider_error"), statusCode, providerMessage),
|
||||
}
|
||||
}
|
||||
|
||||
func readAPIErrorBody(apiErr *openaiapi.Error) []byte {
|
||||
if apiErr == nil || apiErr.Response == nil || apiErr.Response.Body == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
body, err := io.ReadAll(apiErr.Response.Body)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
apiErr.Response.Body = io.NopCloser(strings.NewReader(string(body)))
|
||||
return body
|
||||
}
|
||||
|
||||
func extractErrorMessage(body []byte) string {
|
||||
if len(body) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return strings.TrimSpace(string(body))
|
||||
}
|
||||
|
||||
if errorValue, ok := payload["error"]; ok {
|
||||
switch typed := errorValue.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
case map[string]any:
|
||||
if message, ok := typed["message"].(string); ok && strings.TrimSpace(message) != "" {
|
||||
return strings.TrimSpace(message)
|
||||
}
|
||||
if code, ok := typed["code"].(string); ok && strings.TrimSpace(code) != "" {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if message, ok := payload["message"].(string); ok {
|
||||
return strings.TrimSpace(message)
|
||||
}
|
||||
if detail, ok := payload["detail"].(string); ok {
|
||||
return strings.TrimSpace(detail)
|
||||
}
|
||||
|
||||
return strings.TrimSpace(string(body))
|
||||
}
|
||||
|
||||
func extractErrorCode(body []byte) string {
|
||||
var payload map[string]any
|
||||
if err := json.Unmarshal(body, &payload); err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if code, ok := payload["code"].(string); ok {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
|
||||
errorValue, ok := payload["error"]
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
switch typed := errorValue.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(typed)
|
||||
case map[string]any:
|
||||
if code, ok := typed["code"].(string); ok {
|
||||
return strings.TrimSpace(code)
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func isUsageLimitMessage(message string) bool {
|
||||
lower := strings.ToLower(strings.TrimSpace(message))
|
||||
if lower == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.Contains(lower, "usage limit") ||
|
||||
strings.Contains(lower, "purchase more credits") ||
|
||||
strings.Contains(lower, "upgrade to plus") ||
|
||||
strings.Contains(lower, "upgrade to pro") ||
|
||||
strings.Contains(lower, "plan and billing")
|
||||
}
|
||||
304
internal/plugins/ai/codex/oauth.go
Normal file
304
internal/plugins/ai/codex/oauth.go
Normal file
|
|
@ -0,0 +1,304 @@
|
|||
package codex
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
)
|
||||
|
||||
type oauthTokens struct {
|
||||
IDToken string `json:"id_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type refreshRequest struct {
|
||||
ClientID string `json:"client_id"`
|
||||
GrantType string `json:"grant_type"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type refreshResponse struct {
|
||||
IDToken string `json:"id_token"`
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
}
|
||||
|
||||
type oauthResult struct {
|
||||
tokens oauthTokens
|
||||
err error
|
||||
}
|
||||
|
||||
type pkceCodes struct {
|
||||
CodeVerifier string
|
||||
CodeChallenge string
|
||||
}
|
||||
|
||||
func (c *Client) runOAuthFlow(
|
||||
ctx context.Context,
|
||||
openBrowserFn func(string) error,
|
||||
) (oauthTokens, error) {
|
||||
listener, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", defaultCallbackPort))
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf(i18n.T("codex_oauth_server_start_failed"), err)
|
||||
}
|
||||
defer listener.Close()
|
||||
debuglog.Debug(debuglog.Detailed, "Codex OAuth callback listener started on 127.0.0.1:%d\n", defaultCallbackPort)
|
||||
|
||||
pkce, err := generatePKCECodes()
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
state, err := randomBase64URL(oauthStateBytes)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
callbackURL := (&url.URL{
|
||||
Scheme: "http",
|
||||
Host: fmt.Sprintf("localhost:%d", defaultCallbackPort),
|
||||
Path: oauthCallbackPath,
|
||||
}).String()
|
||||
authURL, err := buildAuthorizeURL(c.AuthBaseURL.Value, callbackURL, pkce, state)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
|
||||
results := make(chan oauthResult, 1)
|
||||
server := &http.Server{
|
||||
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
c.handleOAuthCallback(w, r, callbackURL, pkce, state, results)
|
||||
}),
|
||||
}
|
||||
|
||||
serveDone := make(chan error, 1)
|
||||
go func() {
|
||||
err := server.Serve(listener)
|
||||
if err != nil && !errors.Is(err, http.ErrServerClosed) {
|
||||
serveDone <- err
|
||||
return
|
||||
}
|
||||
serveDone <- nil
|
||||
}()
|
||||
|
||||
if err := openBrowserFn(authURL); err != nil {
|
||||
fmt.Printf("%s\n%s\n", i18n.T("codex_browser_open_fallback"), authURL)
|
||||
}
|
||||
|
||||
select {
|
||||
case result := <-results:
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
_ = server.Shutdown(shutdownCtx)
|
||||
<-serveDone
|
||||
return result.tokens, result.err
|
||||
case err := <-serveDone:
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_server_stopped"))
|
||||
case <-ctx.Done():
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
|
||||
defer cancel()
|
||||
_ = server.Shutdown(shutdownCtx)
|
||||
<-serveDone
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_timed_out"))
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) handleOAuthCallback(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
callbackURL string,
|
||||
pkce pkceCodes,
|
||||
expectedState string,
|
||||
results chan<- oauthResult,
|
||||
) {
|
||||
if r.URL.Path != oauthCallbackPath {
|
||||
http.NotFound(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if !oauthStatesMatch(expectedState, r.URL.Query().Get("state")) {
|
||||
http.Error(w, i18n.T("codex_oauth_state_mismatch"), http.StatusBadRequest)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: errors.New(i18n.T("codex_login_state_mismatch")),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if callbackError := strings.TrimSpace(r.URL.Query().Get("error")); callbackError != "" {
|
||||
description := strings.TrimSpace(r.URL.Query().Get("error_description"))
|
||||
if description != "" {
|
||||
http.Error(w, description, http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: fmt.Errorf(i18n.T("codex_login_failed"), description),
|
||||
})
|
||||
return
|
||||
}
|
||||
http.Error(w, callbackError, http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: fmt.Errorf(i18n.T("codex_login_failed"), callbackError),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
code := strings.TrimSpace(r.URL.Query().Get("code"))
|
||||
if code == "" {
|
||||
http.Error(w, i18n.T("codex_oauth_missing_auth_code"), http.StatusBadRequest)
|
||||
c.publishOAuthResult(results, oauthResult{
|
||||
err: errors.New(i18n.T("codex_login_missing_auth_code")),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
tokens, err := c.exchangeCodeForTokens(r.Context(), callbackURL, pkce, code)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadGateway)
|
||||
c.publishOAuthResult(results, oauthResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
if _, err := c.extractAccountID(tokens.IDToken, tokens.AccessToken); err != nil {
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
c.publishOAuthResult(results, oauthResult{err: err})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||
_, _ = w.Write([]byte("<html><body><h1>" + i18n.T("codex_login_completed") + "</h1><p>" + i18n.T("codex_login_return_to_fabric") + "</p></body></html>"))
|
||||
c.publishOAuthResult(results, oauthResult{tokens: tokens})
|
||||
}
|
||||
|
||||
func (c *Client) publishOAuthResult(results chan<- oauthResult, result oauthResult) {
|
||||
select {
|
||||
case results <- result:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) exchangeCodeForTokens(
|
||||
ctx context.Context,
|
||||
callbackURL string,
|
||||
pkce pkceCodes,
|
||||
code string,
|
||||
) (oauthTokens, error) {
|
||||
form := url.Values{}
|
||||
form.Set("grant_type", "authorization_code")
|
||||
form.Set("code", code)
|
||||
form.Set("redirect_uri", callbackURL)
|
||||
form.Set("client_id", oauthClientID)
|
||||
form.Set("code_verifier", pkce.CodeVerifier)
|
||||
|
||||
tokenURL := strings.TrimRight(c.AuthBaseURL.Value, "/") + "/oauth/token"
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, tokenURL, strings.NewReader(form.Encode()))
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp, err := c.authHTTPClient.Do(req)
|
||||
if err != nil {
|
||||
return oauthTokens{}, fmt.Errorf(i18n.T("codex_token_exchange_failed"), err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return oauthTokens{}, err
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return oauthTokens{}, c.errorFromHTTPResponse(resp.StatusCode, body)
|
||||
}
|
||||
|
||||
var tokens oauthTokens
|
||||
if err := json.Unmarshal(body, &tokens); err != nil {
|
||||
return oauthTokens{}, fmt.Errorf(i18n.T("codex_decode_token_response_failed"), err)
|
||||
}
|
||||
if strings.TrimSpace(tokens.AccessToken) == "" || strings.TrimSpace(tokens.RefreshToken) == "" {
|
||||
return oauthTokens{}, errors.New(i18n.T("codex_login_missing_tokens"))
|
||||
}
|
||||
|
||||
return tokens, nil
|
||||
}
|
||||
|
||||
func buildAuthorizeURL(authBaseURL string, callbackURL string, pkce pkceCodes, state string) (string, error) {
|
||||
issuer, err := url.Parse(strings.TrimRight(authBaseURL, "/"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf(i18n.T("codex_auth_base_url_invalid"), err)
|
||||
}
|
||||
|
||||
issuer.Path = strings.TrimRight(issuer.Path, "/") + "/oauth/authorize"
|
||||
query := issuer.Query()
|
||||
query.Set("response_type", "code")
|
||||
query.Set("client_id", oauthClientID)
|
||||
query.Set("redirect_uri", callbackURL)
|
||||
query.Set("scope", oauthScope)
|
||||
query.Set("code_challenge", pkce.CodeChallenge)
|
||||
query.Set("code_challenge_method", "S256")
|
||||
query.Set("id_token_add_organizations", "true")
|
||||
query.Set("codex_cli_simplified_flow", "true")
|
||||
query.Set("state", state)
|
||||
query.Set("originator", defaultOriginator)
|
||||
issuer.RawQuery = query.Encode()
|
||||
|
||||
return issuer.String(), nil
|
||||
}
|
||||
|
||||
func generatePKCECodes() (pkceCodes, error) {
|
||||
verifier, err := randomBase64URL(oauthVerifierBytes)
|
||||
if err != nil {
|
||||
return pkceCodes{}, err
|
||||
}
|
||||
|
||||
sum := sha256.Sum256([]byte(verifier))
|
||||
return pkceCodes{
|
||||
CodeVerifier: verifier,
|
||||
CodeChallenge: base64.RawURLEncoding.EncodeToString(sum[:]),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func randomBase64URL(size int) (string, error) {
|
||||
buf := make([]byte, size)
|
||||
if _, err := rand.Read(buf); err != nil {
|
||||
return "", fmt.Errorf(i18n.T("codex_oauth_random_state_failed"), err)
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(buf), nil
|
||||
}
|
||||
|
||||
func oauthStatesMatch(expected string, actual string) bool {
|
||||
if len(expected) != len(actual) {
|
||||
return false
|
||||
}
|
||||
return subtle.ConstantTimeCompare([]byte(expected), []byte(actual)) == 1
|
||||
}
|
||||
|
||||
func openBrowser(targetURL string) error {
|
||||
var cmd *exec.Cmd
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
cmd = exec.Command("open", targetURL)
|
||||
case "windows":
|
||||
cmd = exec.Command("rundll32", "url.dll,FileProtocolHandler", targetURL)
|
||||
default:
|
||||
cmd = exec.Command("xdg-open", targetURL)
|
||||
}
|
||||
return cmd.Start()
|
||||
}
|
||||
115
internal/plugins/ai/codex/token.go
Normal file
115
internal/plugins/ai/codex/token.go
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
package codex
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"runtime/debug"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type tokenClaims struct {
|
||||
Exp int64 `json:"exp"`
|
||||
Auth tokenAuthClaims `json:"https://api.openai.com/auth"`
|
||||
Profile tokenProfile `json:"https://api.openai.com/profile"`
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
type tokenAuthClaims struct {
|
||||
ChatGPTAccountID string `json:"chatgpt_account_id"`
|
||||
ChatGPTPlanType string `json:"chatgpt_plan_type"`
|
||||
UserID string `json:"user_id"`
|
||||
ChatGPTUserID string `json:"chatgpt_user_id"`
|
||||
}
|
||||
|
||||
type tokenProfile struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
func tokenNeedsRefresh(jwt string, now time.Time) bool {
|
||||
expiry, err := extractExpiryFromJWT(jwt)
|
||||
if err != nil {
|
||||
return true
|
||||
}
|
||||
return now.Add(tokenRefreshLeeway).After(expiry)
|
||||
}
|
||||
|
||||
func extractExpiryFromJWT(jwt string) (time.Time, error) {
|
||||
claims, err := parseTokenClaims(jwt)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
if claims.Exp == 0 {
|
||||
return time.Time{}, errors.New("jwt did not include an exp claim")
|
||||
}
|
||||
return time.Unix(claims.Exp, 0), nil
|
||||
}
|
||||
|
||||
func extractAccountIDFromJWT(jwt string) (string, error) {
|
||||
claims, err := parseTokenClaims(jwt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return strings.TrimSpace(claims.Auth.ChatGPTAccountID), nil
|
||||
}
|
||||
|
||||
func parseTokenClaims(jwt string) (tokenClaims, error) {
|
||||
parts := strings.Split(jwt, ".")
|
||||
if len(parts) < 2 {
|
||||
return tokenClaims{}, errors.New("invalid jwt format")
|
||||
}
|
||||
|
||||
payload, err := base64.RawURLEncoding.DecodeString(parts[1])
|
||||
if err != nil {
|
||||
return tokenClaims{}, err
|
||||
}
|
||||
|
||||
var claims tokenClaims
|
||||
if err := json.Unmarshal(payload, &claims); err != nil {
|
||||
return tokenClaims{}, err
|
||||
}
|
||||
|
||||
return claims, nil
|
||||
}
|
||||
|
||||
func codexClientVersion() string {
|
||||
if info, ok := debug.ReadBuildInfo(); ok {
|
||||
if version := normalizeSemverLikeVersion(info.Main.Version); version != "" {
|
||||
return version
|
||||
}
|
||||
}
|
||||
|
||||
return defaultClientVersion
|
||||
}
|
||||
|
||||
func normalizeSemverLikeVersion(version string) string {
|
||||
version = strings.TrimSpace(version)
|
||||
version = strings.TrimPrefix(version, "v")
|
||||
if version == "" || version == "(devel)" {
|
||||
return ""
|
||||
}
|
||||
|
||||
end := len(version)
|
||||
for i, r := range version {
|
||||
if (r < '0' || r > '9') && r != '.' {
|
||||
end = i
|
||||
break
|
||||
}
|
||||
}
|
||||
version = strings.Trim(version[:end], ".")
|
||||
if version == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
parts := strings.Split(version, ".")
|
||||
if len(parts) < 3 {
|
||||
return ""
|
||||
}
|
||||
if slices.Contains(parts[:3], "") {
|
||||
return ""
|
||||
}
|
||||
|
||||
return strings.Join(parts[:3], ".")
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -159,7 +160,7 @@ func (c *Client) IsConfigured() bool {
|
|||
|
||||
// ListModels returns the available models.
|
||||
// Microsoft 365 Copilot exposes a single model - the Copilot service itself.
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(_ context.Context) ([]string, error) {
|
||||
// Copilot doesn't expose multiple models - it's a unified service
|
||||
// We expose it as a single "model" for consistency with Fabric's architecture
|
||||
return []string{copilotModelName}, nil
|
||||
|
|
@ -186,7 +187,7 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
|||
}
|
||||
|
||||
// SendStream sends a message to Copilot and streams the response.
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
func (c *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
defer close(channel)
|
||||
|
||||
ctx := context.Background()
|
||||
|
|
@ -401,9 +402,7 @@ func (c *Client) parseSSEStream(reader io.Reader, channel chan domain.StreamUpda
|
|||
// extractResponseText extracts the assistant's response from messages.
|
||||
func (c *Client) extractResponseText(messages []responseMessage) string {
|
||||
// Find the last assistant message (Copilot's response)
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
msg := messages[i]
|
||||
// Response messages from Copilot have the copilotConversationResponseMessage type
|
||||
for _, msg := range slices.Backward(messages) {
|
||||
if msg.ODataType == "#microsoft.graph.copilotConversationResponseMessage" {
|
||||
if msg.Text != "" {
|
||||
return msg.Text
|
||||
|
|
|
|||
|
|
@ -52,9 +52,9 @@ func NewClient() *Client {
|
|||
return client
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(ctx context.Context) ([]string, error) {
|
||||
if c.ControlPlaneToken.Value == "" {
|
||||
models, err := c.Client.ListModels()
|
||||
models, err := c.Client.ListModels(ctx)
|
||||
if err == nil && len(models) > 0 {
|
||||
return models, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ func NewClient() *Client {
|
|||
return &Client{PluginBase: &plugins.PluginBase{Name: "DryRun"}}
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(_ context.Context) ([]string, error) {
|
||||
return []string{"dry-run-model"}, nil
|
||||
}
|
||||
|
||||
|
|
@ -108,7 +108,7 @@ func (c *Client) constructRequest(msgs []*chat.ChatCompletionMessage, opts *doma
|
|||
return builder.String()
|
||||
}
|
||||
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
func (c *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
defer close(channel)
|
||||
request := c.constructRequest(msgs, opts)
|
||||
channel <- domain.StreamUpdate{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package dryrun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
|
|
@ -11,7 +12,7 @@ import (
|
|||
// Test generated using Keploy
|
||||
func TestListModels_ReturnsExpectedModel(t *testing.T) {
|
||||
client := NewClient()
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
if err != nil {
|
||||
t.Fatalf("Expected no error, got %v", err)
|
||||
}
|
||||
|
|
@ -41,7 +42,7 @@ func TestSendStream_SendsMessages(t *testing.T) {
|
|||
}
|
||||
channel := make(chan domain.StreamUpdate)
|
||||
go func() {
|
||||
err := client.SendStream(msgs, opts, channel)
|
||||
err := client.SendStream(context.Background(), msgs, opts, channel)
|
||||
if err != nil {
|
||||
t.Errorf("Expected no error, got %v", err)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package exolab
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/plugins"
|
||||
|
|
@ -42,7 +43,7 @@ func (oi *Client) configure() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (oi *Client) ListModels() (ret []string, err error) {
|
||||
func (oi *Client) ListModels(context.Context) (ret []string, err error) {
|
||||
ret = oi.apiModels
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -60,7 +61,7 @@ type Client struct {
|
|||
ApiKey *plugins.SetupQuestion
|
||||
}
|
||||
|
||||
func (o *Client) ListModels() (ret []string, err error) {
|
||||
func (o *Client) ListModels(_ context.Context) (ret []string, err error) {
|
||||
ctx := context.Background()
|
||||
var client *genai.Client
|
||||
if client, err = genai.NewClient(ctx, &genai.ClientConfig{
|
||||
|
|
@ -124,7 +125,7 @@ func (o *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
|||
return
|
||||
}
|
||||
|
||||
func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
func (o *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
ctx := context.Background()
|
||||
defer close(channel)
|
||||
|
||||
|
|
@ -290,9 +291,9 @@ func (o *Client) isTTSModel(modelName string) bool {
|
|||
|
||||
// extractTextForTTS extracts text content from chat messages for TTS generation
|
||||
func (o *Client) extractTextForTTS(msgs []*chat.ChatCompletionMessage) (string, error) {
|
||||
for i := len(msgs) - 1; i >= 0; i-- {
|
||||
if msgs[i].Role == chat.ChatMessageRoleUser && msgs[i].Content != "" {
|
||||
return msgs[i].Content, nil
|
||||
for _, msg := range slices.Backward(msgs) {
|
||||
if msg.Role == chat.ChatMessageRoleUser && msg.Content != "" {
|
||||
return msg.Content, nil
|
||||
}
|
||||
}
|
||||
return "", errors.New(i18n.T("gemini_no_text_for_tts"))
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ func (c *Client) configure() error {
|
|||
}
|
||||
|
||||
// ListModels returns a list of available models.
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(_ context.Context) ([]string, error) {
|
||||
url := fmt.Sprintf("%s/models", c.ApiUrl.Value)
|
||||
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
|
|
@ -89,7 +89,7 @@ func (c *Client) ListModels() ([]string, error) {
|
|||
return models, nil
|
||||
}
|
||||
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
func (c *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
url := fmt.Sprintf("%s/chat/completions", c.ApiUrl.Value)
|
||||
|
||||
payload := map[string]any{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ func TestListModelsUsesBearerTokenWhenConfigured(t *testing.T) {
|
|||
client.ApiKey.Value = "secret"
|
||||
client.HttpClient = server.Client()
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"model-1"}, models)
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ func TestListModelsDoesNotSendBearerForWhitespaceOnlyKey(t *testing.T) {
|
|||
client.ApiKey.Value = " "
|
||||
client.HttpClient = server.Client()
|
||||
|
||||
models, err := client.ListModels()
|
||||
models, err := client.ListModels(context.Background())
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"model-1"}, models)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ func (o *Client) configure() (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (o *Client) ListModels() (ret []string, err error) {
|
||||
func (o *Client) ListModels(_ context.Context) (ret []string, err error) {
|
||||
ctx := context.Background()
|
||||
|
||||
var listResp *ollamaapi.ListResponse
|
||||
|
|
@ -104,8 +104,9 @@ func (o *Client) ListModels() (ret []string, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
func (o *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) (err error) {
|
||||
ctx := context.Background()
|
||||
defer close(channel)
|
||||
|
||||
var req ollamaapi.ChatRequest
|
||||
if req, err = o.createChatRequest(ctx, msgs, opts); err != nil {
|
||||
|
|
@ -135,7 +136,6 @@ func (o *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.Cha
|
|||
return
|
||||
}
|
||||
|
||||
close(channel)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,10 +6,14 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/chat"
|
||||
"github.com/danielmiessler/fabric/internal/domain"
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
ollamaapi "github.com/ollama/ollama/api"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -60,3 +64,28 @@ func TestLoadImageBytes_DataURLSuccess(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
assert.Equal(t, expected, got)
|
||||
}
|
||||
|
||||
func TestSendStreamClosesChannelOnChatError(t *testing.T) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
_, _ = w.Write([]byte(`{"error":"ollama failed"}` + "\n"))
|
||||
}))
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
baseURL, err := url.Parse(server.URL)
|
||||
require.NoError(t, err)
|
||||
|
||||
client := &Client{client: ollamaapi.NewClient(baseURL, server.Client())}
|
||||
channel := make(chan domain.StreamUpdate)
|
||||
|
||||
err = client.SendStream(
|
||||
context.Background(),
|
||||
[]*chat.ChatCompletionMessage{{Role: chat.ChatMessageRoleUser, Content: "hello"}},
|
||||
&domain.ChatOptions{Model: "missing-model"},
|
||||
channel,
|
||||
)
|
||||
|
||||
require.Error(t, err)
|
||||
_, ok := <-channel
|
||||
assert.False(t, ok, "stream channel should be closed when Ollama chat returns an error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ func (o *Client) sendChatCompletions(ctx context.Context, msgs []*chat.ChatCompl
|
|||
|
||||
// sendStreamChatCompletions sends a streaming request using the Chat Completions API
|
||||
func (o *Client) sendStreamChatCompletions(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
) (err error) {
|
||||
defer close(channel)
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ func (o *Client) sendStreamChatCompletions(
|
|||
req.StreamOptions = openai.ChatCompletionStreamOptionsParam{
|
||||
IncludeUsage: openai.Bool(true),
|
||||
}
|
||||
stream := o.ApiClient.Chat.Completions.NewStreaming(context.Background(), req)
|
||||
stream := o.ApiClient.Chat.Completions.NewStreaming(ctx, req)
|
||||
for stream.Next() {
|
||||
chunk := stream.Current()
|
||||
if len(chunk.Choices) > 0 && chunk.Choices[0].Delta.Content != "" {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
|
|
@ -45,6 +46,13 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
return nil, fmt.Errorf(i18n.T("openai_failed_to_create_models_url"), err)
|
||||
}
|
||||
|
||||
// Serve a fresh cached list when available to avoid re-hitting discovery
|
||||
// endpoints that aggressively rate-limit (e.g. GitHub Models' catalog).
|
||||
if models, ok := readModelsCache(providerName, fullURL, modelsCacheTTL); ok {
|
||||
debuglog.Debug(debuglog.Detailed, "Using cached models list for %s (%d models)\n", providerName, len(models))
|
||||
return models, nil
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullURL, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
@ -53,6 +61,14 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
// GitHub Models' catalog endpoint sits behind GitHub's edge layer, which
|
||||
// throttles requests that omit the documented API version header (returning
|
||||
// HTTP 429 with an HTML body). Send it so the catalog fetch matches GitHub's
|
||||
// API contract and avoids the edge-level rate limiter.
|
||||
if strings.EqualFold(req.URL.Host, "models.github.ai") {
|
||||
req.Header.Set("X-GitHub-Api-Version", "2022-11-28")
|
||||
}
|
||||
|
||||
// Reuse provided HTTP client, or create a new one if not provided
|
||||
client := httpClient
|
||||
if client == nil {
|
||||
|
|
@ -62,17 +78,40 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
}
|
||||
resp, err := client.Do(req)
|
||||
if err != nil {
|
||||
// On a network error, fall back to a stale cached list if we have one.
|
||||
if models, ok := readModelsCache(providerName, fullURL, 0); ok {
|
||||
debuglog.Debug(debuglog.Basic, "Fetch failed for %s (%v); serving stale cached models\n", providerName, err)
|
||||
return models, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
// A failing discovery call (commonly HTTP 429 rate limiting) should not
|
||||
// hard-fail when we already have a list cached from a prior success.
|
||||
if models, ok := readModelsCache(providerName, fullURL, 0); ok {
|
||||
debuglog.Debug(debuglog.Basic, "Status %d from %s; serving stale cached models\n", resp.StatusCode, providerName)
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// Read the response body for debugging, but limit the number of bytes read
|
||||
bodyBytes, readErr := io.ReadAll(io.LimitReader(resp.Body, errorResponseLimit))
|
||||
if readErr != nil {
|
||||
return nil, fmt.Errorf(i18n.T("openai_unexpected_status_code_read_error"),
|
||||
resp.StatusCode, providerName, readErr)
|
||||
}
|
||||
|
||||
// Rate limiting often returns an HTML body; surface a concise, actionable
|
||||
// message instead of dumping the raw page.
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
retryAfter := strings.TrimSpace(resp.Header.Get("Retry-After"))
|
||||
if retryAfter == "" {
|
||||
retryAfter = "60"
|
||||
}
|
||||
return nil, fmt.Errorf(i18n.T("openai_models_rate_limited"), providerName, retryAfter)
|
||||
}
|
||||
|
||||
bodyString := string(bodyBytes)
|
||||
return nil, fmt.Errorf(i18n.T("openai_unexpected_status_code_with_body"),
|
||||
resp.StatusCode, providerName, bodyString)
|
||||
|
|
@ -97,12 +136,16 @@ func FetchModelsDirectly(ctx context.Context, baseURL, apiKey, providerName stri
|
|||
|
||||
if err := json.Unmarshal(bodyBytes, &openAIFormat); err == nil {
|
||||
debuglog.Debug(debuglog.Detailed, "Successfully parsed models response from %s using OpenAI format (found %d models)\n", providerName, len(openAIFormat.Data))
|
||||
return extractModelIDs(openAIFormat.Data), nil
|
||||
ids := extractModelIDs(openAIFormat.Data)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(bodyBytes, &directArray); err == nil {
|
||||
debuglog.Debug(debuglog.Detailed, "Successfully parsed models response from %s using direct array format (found %d models)\n", providerName, len(directArray))
|
||||
return extractModelIDs(directArray), nil
|
||||
ids := extractModelIDs(directArray)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
var truncatedBody string
|
||||
|
|
|
|||
95
internal/plugins/ai/openai/models_cache.go
Normal file
95
internal/plugins/ai/openai/models_cache.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
package openai
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"time"
|
||||
)
|
||||
|
||||
// modelsCacheTTL is how long a successfully fetched model list is considered
|
||||
// fresh. Model catalogs change rarely, so a long TTL avoids re-hitting
|
||||
// discovery endpoints that aggressively rate-limit (e.g. GitHub Models'
|
||||
// catalog, which returns HTTP 429 with a Retry-After header).
|
||||
const modelsCacheTTL = 24 * time.Hour
|
||||
|
||||
// modelsCacheDir returns the directory used to cache provider model lists. It
|
||||
// is a package variable so tests can redirect it to a temporary location.
|
||||
var modelsCacheDir = defaultModelsCacheDir
|
||||
|
||||
func defaultModelsCacheDir() (string, error) {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return filepath.Join(home, ".config", "fabric", "cache", "models"), nil
|
||||
}
|
||||
|
||||
type modelsCacheEntry struct {
|
||||
URL string `json:"url"`
|
||||
FetchedAt time.Time `json:"fetched_at"`
|
||||
Models []string `json:"models"`
|
||||
}
|
||||
|
||||
var cacheNameSanitizer = regexp.MustCompile(`[^A-Za-z0-9_-]+`)
|
||||
|
||||
// modelsCacheFile derives a stable, collision-resistant cache path from the
|
||||
// provider name and the full request URL. The URL is hashed so a provider that
|
||||
// changes its endpoint does not read a stale entry from the old one.
|
||||
func modelsCacheFile(dir, providerName, fullURL string) string {
|
||||
sum := sha256.Sum256([]byte(fullURL))
|
||||
slug := cacheNameSanitizer.ReplaceAllString(providerName, "_")
|
||||
name := fmt.Sprintf("%s-%s.json", slug, hex.EncodeToString(sum[:])[:12])
|
||||
return filepath.Join(dir, name)
|
||||
}
|
||||
|
||||
// readModelsCache returns the cached model list for (providerName, fullURL).
|
||||
// When maxAge > 0 the entry must be younger than maxAge; maxAge <= 0 accepts an
|
||||
// entry of any age (used to fall back to a stale list when a fetch fails).
|
||||
// Empty cached lists are never returned.
|
||||
func readModelsCache(providerName, fullURL string, maxAge time.Duration) ([]string, bool) {
|
||||
dir, err := modelsCacheDir()
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
data, err := os.ReadFile(modelsCacheFile(dir, providerName, fullURL))
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
var entry modelsCacheEntry
|
||||
if err := json.Unmarshal(data, &entry); err != nil {
|
||||
return nil, false
|
||||
}
|
||||
if entry.URL != fullURL || len(entry.Models) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
if maxAge > 0 && time.Since(entry.FetchedAt) > maxAge {
|
||||
return nil, false
|
||||
}
|
||||
return entry.Models, true
|
||||
}
|
||||
|
||||
// writeModelsCache persists a successfully fetched model list. Empty lists are
|
||||
// not cached so a transient empty response does not stick for the whole TTL.
|
||||
func writeModelsCache(providerName, fullURL string, models []string) error {
|
||||
if len(models) == 0 {
|
||||
return nil
|
||||
}
|
||||
dir, err := modelsCacheDir()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
entry := modelsCacheEntry{URL: fullURL, FetchedAt: time.Now(), Models: models}
|
||||
data, err := json.Marshal(entry)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(modelsCacheFile(dir, providerName, fullURL), data, 0o600)
|
||||
}
|
||||
165
internal/plugins/ai/openai/models_cache_test.go
Normal file
165
internal/plugins/ai/openai/models_cache_test.go
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package openai
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// withTempModelsCache redirects the models cache to a temporary directory for
|
||||
// the duration of a test and restores the original afterwards.
|
||||
func withTempModelsCache(t *testing.T) string {
|
||||
t.Helper()
|
||||
dir := t.TempDir()
|
||||
orig := modelsCacheDir
|
||||
modelsCacheDir = func() (string, error) { return dir, nil }
|
||||
t.Cleanup(func() { modelsCacheDir = orig })
|
||||
return dir
|
||||
}
|
||||
|
||||
func modelsURLFor(t *testing.T, baseURL string) string {
|
||||
t.Helper()
|
||||
full, err := url.JoinPath(baseURL, "models")
|
||||
require.NoError(t, err)
|
||||
return full
|
||||
}
|
||||
|
||||
// writeAgedCache writes a cache entry with an explicit age so freshness and
|
||||
// stale-fallback paths can be exercised deterministically.
|
||||
func writeAgedCache(t *testing.T, dir, provider, fullURL string, models []string, age time.Duration) {
|
||||
t.Helper()
|
||||
entry := modelsCacheEntry{URL: fullURL, FetchedAt: time.Now().Add(-age), Models: models}
|
||||
data, err := json.Marshal(entry)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.MkdirAll(dir, 0o755))
|
||||
require.NoError(t, os.WriteFile(modelsCacheFile(dir, provider, fullURL), data, 0o600))
|
||||
}
|
||||
|
||||
func TestModelsCache_WriteThenReadFresh(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
|
||||
require.NoError(t, writeModelsCache(provider, fullURL, []string{"a", "b"}))
|
||||
|
||||
models, ok := readModelsCache(provider, fullURL, modelsCacheTTL)
|
||||
assert.True(t, ok)
|
||||
assert.Equal(t, []string{"a", "b"}, models)
|
||||
}
|
||||
|
||||
func TestModelsCache_EmptyListNotCached(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
|
||||
require.NoError(t, writeModelsCache(provider, fullURL, nil))
|
||||
|
||||
_, ok := readModelsCache(provider, fullURL, 0)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
func TestModelsCache_ExpiredMissesWithTTLButHitsWithoutAgeLimit(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
const provider, fullURL = "GitHub", "https://models.github.ai/catalog/models"
|
||||
writeAgedCache(t, dir, provider, fullURL, []string{"old"}, 48*time.Hour)
|
||||
|
||||
_, ok := readModelsCache(provider, fullURL, modelsCacheTTL)
|
||||
assert.False(t, ok, "entry older than TTL should be a miss")
|
||||
|
||||
models, ok := readModelsCache(provider, fullURL, 0)
|
||||
assert.True(t, ok, "maxAge<=0 should accept any age")
|
||||
assert.Equal(t, []string{"old"}, models)
|
||||
}
|
||||
|
||||
func TestModelsCache_DifferentURLDoesNotCollide(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
require.NoError(t, writeModelsCache("GitHub", "https://a/models", []string{"a"}))
|
||||
|
||||
_, ok := readModelsCache("GitHub", "https://b/models", 0)
|
||||
assert.False(t, ok)
|
||||
}
|
||||
|
||||
// A fresh cache short-circuits before any network call.
|
||||
func TestFetchModelsDirectly_ServesFreshCacheWithoutRequest(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
called := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
writeAgedCache(t, dir, "GitHub", modelsURLFor(t, srv.URL), []string{"cached-model"}, time.Minute)
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"cached-model"}, models)
|
||||
assert.False(t, called, "fresh cache should prevent the network call")
|
||||
}
|
||||
|
||||
// A 429 with a stale cache present returns the stale list rather than erroring.
|
||||
func TestFetchModelsDirectly_429ServesStaleCache(t *testing.T) {
|
||||
dir := withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, _ = w.Write([]byte("<html>Whoa there!</html>"))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
// Stale so the TTL check misses and the request is actually made.
|
||||
writeAgedCache(t, dir, "GitHub", modelsURLFor(t, srv.URL), []string{"stale-model"}, 48*time.Hour)
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"stale-model"}, models)
|
||||
}
|
||||
|
||||
// A 429 with no cache yields a concise message, not the raw HTML body.
|
||||
func TestFetchModelsDirectly_429NoCacheCleanError(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Retry-After", "60")
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
_, _ = w.Write([]byte("<html><title>Rate limit</title>Whoa there!</html>"))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
_, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), "rate limit")
|
||||
assert.Contains(t, err.Error(), "60")
|
||||
assert.NotContains(t, err.Error(), "<html>")
|
||||
assert.NotContains(t, err.Error(), "Whoa there")
|
||||
}
|
||||
|
||||
// A successful fetch is cached, so a later failure is served from cache.
|
||||
func TestFetchModelsDirectly_WritesCacheOnSuccess(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
fail := false
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if fail {
|
||||
w.WriteHeader(http.StatusTooManyRequests)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, _ = w.Write([]byte(`[{"id":"m1"}]`))
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"m1"}, models)
|
||||
|
||||
// Cache is fresh now, so even with the server failing we get the cached list.
|
||||
fail = true
|
||||
models, err = FetchModelsDirectly(context.Background(), srv.URL, "key", "GitHub", nil)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, []string{"m1"}, models)
|
||||
}
|
||||
|
|
@ -66,6 +66,15 @@ type Client struct {
|
|||
ApiClient *openai.Client
|
||||
ImplementsResponses bool // Whether this provider supports the Responses API
|
||||
httpClient *http.Client
|
||||
// webSearchToolName, when non-empty, overrides the default
|
||||
// "web_search_preview" tool name emitted on the Responses API.
|
||||
// Used by OpenAI-compatible providers whose upstream API expects a
|
||||
// different tool type string (xAI expects "web_search").
|
||||
webSearchToolName string
|
||||
// enableXSearch, when true, appends an additional "x_search" tool
|
||||
// entry alongside the web search tool when Search is enabled.
|
||||
// This is an xAI-specific live search grounding tool.
|
||||
enableXSearch bool
|
||||
}
|
||||
|
||||
// SetResponsesAPIEnabled configures whether to use the Responses API
|
||||
|
|
@ -73,6 +82,21 @@ func (o *Client) SetResponsesAPIEnabled(enabled bool) {
|
|||
o.ImplementsResponses = enabled
|
||||
}
|
||||
|
||||
// SetWebSearchToolName overrides the default "web_search_preview" tool
|
||||
// name emitted on the Responses API when Search is enabled. Pass an empty
|
||||
// string to keep the OpenAI default. Non-OpenAI providers (for example,
|
||||
// xAI) may require "web_search" instead.
|
||||
func (o *Client) SetWebSearchToolName(name string) {
|
||||
o.webSearchToolName = name
|
||||
}
|
||||
|
||||
// SetEnableXSearch toggles whether an additional xAI "x_search" tool
|
||||
// entry is appended when Search is enabled. Non-xAI providers should
|
||||
// leave this false.
|
||||
func (o *Client) SetEnableXSearch(enabled bool) {
|
||||
o.enableXSearch = enabled
|
||||
}
|
||||
|
||||
// checkImageGenerationCompatibility warns if the model doesn't support image generation
|
||||
func checkImageGenerationCompatibility(model string) {
|
||||
if !supportsImageGeneration(model) {
|
||||
|
|
@ -96,9 +120,9 @@ func (o *Client) configure() (ret error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (o *Client) ListModels() (ret []string, err error) {
|
||||
func (o *Client) ListModels(ctx context.Context) (ret []string, err error) {
|
||||
var page *pagination.Page[openai.Model]
|
||||
if page, err = o.ApiClient.Models.List(context.Background()); err == nil {
|
||||
if page, err = o.ApiClient.Models.List(ctx); err == nil {
|
||||
for _, mod := range page.Data {
|
||||
ret = append(ret, mod.ID)
|
||||
}
|
||||
|
|
@ -110,26 +134,26 @@ func (o *Client) ListModels() (ret []string, err error) {
|
|||
// Some providers (e.g., GitHub Models) return non-standard response formats
|
||||
// that the SDK fails to parse.
|
||||
debuglog.Debug(debuglog.Basic, "SDK Models.List failed for %s: %v, falling back to direct API fetch\n", o.GetName(), err)
|
||||
return FetchModelsDirectly(context.Background(), o.ApiBaseURL.Value, o.ApiKey.Value, o.GetName(), o.httpClient)
|
||||
return FetchModelsDirectly(ctx, o.ApiBaseURL.Value, o.ApiKey.Value, o.GetName(), o.httpClient)
|
||||
}
|
||||
|
||||
func (o *Client) SendStream(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
) (err error) {
|
||||
// Use Responses API for OpenAI, Chat Completions API for other providers
|
||||
if o.supportsResponsesAPI() {
|
||||
return o.sendStreamResponses(msgs, opts, channel)
|
||||
return o.sendStreamResponses(ctx, msgs, opts, channel)
|
||||
}
|
||||
return o.sendStreamChatCompletions(msgs, opts, channel)
|
||||
return o.sendStreamChatCompletions(ctx, msgs, opts, channel)
|
||||
}
|
||||
|
||||
func (o *Client) sendStreamResponses(
|
||||
msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
ctx context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate,
|
||||
) (err error) {
|
||||
defer close(channel)
|
||||
|
||||
req := o.buildResponseParams(msgs, opts)
|
||||
stream := o.ApiClient.Responses.NewStreaming(context.Background(), req)
|
||||
stream := o.ApiClient.Responses.NewStreaming(ctx, req)
|
||||
for stream.Next() {
|
||||
event := stream.Current()
|
||||
switch event.Type {
|
||||
|
|
@ -199,6 +223,7 @@ func (o *Client) NeedsRawMode(modelName string) bool {
|
|||
openaiModelsPrefixes := []string{
|
||||
"glm",
|
||||
"gpt-5",
|
||||
"gpt-6",
|
||||
"o1",
|
||||
"o3",
|
||||
"o4",
|
||||
|
|
@ -253,11 +278,18 @@ func (o *Client) buildResponseParams(
|
|||
// Add tools if enabled
|
||||
var tools []responses.ToolUnionParam
|
||||
|
||||
// Add web search tool if enabled
|
||||
// Add web search tool if enabled. The default tool name is OpenAI's
|
||||
// "web_search_preview", but providers may override it (for example,
|
||||
// xAI's Responses API requires "web_search").
|
||||
if opts.Search {
|
||||
webSearchTool := responses.ToolParamOfWebSearchPreview("web_search_preview")
|
||||
searchToolName := responses.WebSearchToolType("web_search_preview")
|
||||
if o.webSearchToolName != "" {
|
||||
searchToolName = responses.WebSearchToolType(o.webSearchToolName)
|
||||
}
|
||||
webSearchTool := responses.ToolParamOfWebSearchPreview(searchToolName)
|
||||
|
||||
// Add user location if provided
|
||||
// Add user location if provided. Only attach when the caller
|
||||
// asked for it; xAI rejects unexpected location payloads.
|
||||
if opts.SearchLocation != "" {
|
||||
webSearchTool.OfWebSearchPreview.UserLocation = responses.WebSearchToolUserLocationParam{
|
||||
Type: "approximate",
|
||||
|
|
@ -266,6 +298,20 @@ func (o *Client) buildResponseParams(
|
|||
}
|
||||
|
||||
tools = append(tools, webSearchTool)
|
||||
|
||||
// Append xAI's live "x_search" tool when the provider opts in.
|
||||
// The xAI Responses API accepts a bare {"type":"x_search"}
|
||||
// entry with no other required fields. We reuse the SDK's
|
||||
// WebSearchToolParam as a minimal container since every other
|
||||
// field is omitzero and will be elided during JSON marshalling.
|
||||
if o.enableXSearch {
|
||||
xSearchTool := responses.ToolUnionParam{
|
||||
OfWebSearchPreview: &responses.WebSearchToolParam{
|
||||
Type: responses.WebSearchToolType("x_search"),
|
||||
},
|
||||
}
|
||||
tools = append(tools, xSearchTool)
|
||||
}
|
||||
}
|
||||
|
||||
// Add image generation tool if needed
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package openai
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -12,6 +14,7 @@ import (
|
|||
// Ensures we can fetch models directly when a provider returns a direct array of models
|
||||
// instead of the standard OpenAI list response structure.
|
||||
func TestFetchModelsDirectly_DirectArray(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
@ -28,6 +31,7 @@ func TestFetchModelsDirectly_DirectArray(t *testing.T) {
|
|||
|
||||
// Ensures we can fetch models when a provider returns the standard OpenAI format
|
||||
func TestFetchModelsDirectly_OpenAIFormat(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
@ -42,8 +46,54 @@ func TestFetchModelsDirectly_OpenAIFormat(t *testing.T) {
|
|||
assert.Equal(t, "openai-model", models[0])
|
||||
}
|
||||
|
||||
// Ensures non-GitHub hosts do not receive the GitHub-specific API version header.
|
||||
func TestFetchModelsDirectly_NoGitHubHeaderForOtherHosts(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Empty(t, r.Header.Get("X-GitHub-Api-Version"))
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
_, err := w.Write([]byte(`[{"id":"some-model"}]`))
|
||||
assert.NoError(t, err)
|
||||
}))
|
||||
defer srv.Close()
|
||||
|
||||
_, err := FetchModelsDirectly(context.Background(), srv.URL, "test-key", "TestProvider", nil)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// captureRoundTripper records the outgoing request and returns a canned response,
|
||||
// allowing assertions against requests sent to real hosts without networking.
|
||||
type captureRoundTripper struct {
|
||||
req *http.Request
|
||||
body string
|
||||
}
|
||||
|
||||
func (c *captureRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
c.req = req
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusOK,
|
||||
Header: http.Header{"Content-Type": []string{"application/json"}},
|
||||
Body: io.NopCloser(strings.NewReader(c.body)),
|
||||
Request: req,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Ensures the GitHub Models catalog host receives the documented API version
|
||||
// header so it is not throttled by GitHub's edge rate limiter.
|
||||
func TestFetchModelsDirectly_GitHubHostSendsAPIVersionHeader(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
rt := &captureRoundTripper{body: `[{"id":"github-model"}]`}
|
||||
client := &http.Client{Transport: rt}
|
||||
|
||||
models, err := FetchModelsDirectly(context.Background(), "https://models.github.ai/catalog", "test-key", "GitHub", client)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, []string{"github-model"}, models)
|
||||
assert.Equal(t, "2022-11-28", rt.req.Header.Get("X-GitHub-Api-Version"))
|
||||
}
|
||||
|
||||
// Ensures we handle empty model lists correctly
|
||||
func TestFetchModelsDirectly_EmptyArray(t *testing.T) {
|
||||
withTempModelsCache(t)
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
assert.Equal(t, "/models", r.URL.Path)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
|
|
|
|||
|
|
@ -12,6 +12,12 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNeedsRawModeGPT6(t *testing.T) {
|
||||
if !NewClient().NeedsRawMode("gpt-6-astra") {
|
||||
t.Fatal("gpt-6 models should use raw mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildResponseRequestWithMaxTokens(t *testing.T) {
|
||||
|
||||
var msgs []*chat.ChatCompletionMessage
|
||||
|
|
@ -129,6 +135,88 @@ func TestBuildResponseParams_WithSearchAndLocation(t *testing.T) {
|
|||
assert.Equal(t, opts.SearchLocation, userLocation.Timezone.Value)
|
||||
}
|
||||
|
||||
// TestBuildResponseParams_GrokAI_WithSearch verifies that a client
|
||||
// configured with a custom web search tool name and x_search enabled
|
||||
// emits both tool entries with the xAI-expected type strings.
|
||||
func TestBuildResponseParams_GrokAI_WithSearch(t *testing.T) {
|
||||
client := NewClient()
|
||||
client.SetWebSearchToolName("web_search")
|
||||
client.SetEnableXSearch(true)
|
||||
|
||||
opts := &domain.ChatOptions{
|
||||
Model: "grok-4-fast-reasoning",
|
||||
Temperature: 0.7,
|
||||
Search: true,
|
||||
}
|
||||
|
||||
msgs := []*chat.ChatCompletionMessage{
|
||||
{Role: "user", Content: "What happened in the news today?"},
|
||||
}
|
||||
|
||||
params := client.buildResponseParams(msgs, opts)
|
||||
|
||||
assert.NotNil(t, params.Tools, "Expected tools when search is enabled")
|
||||
assert.Len(t, params.Tools, 2, "Expected web_search plus x_search tools")
|
||||
|
||||
webSearchTool := params.Tools[0]
|
||||
assert.NotNil(t, webSearchTool.OfWebSearchPreview, "Expected web search tool slot")
|
||||
assert.Equal(t, responses.WebSearchToolType("web_search"), webSearchTool.OfWebSearchPreview.Type)
|
||||
|
||||
xSearchTool := params.Tools[1]
|
||||
assert.NotNil(t, xSearchTool.OfWebSearchPreview, "Expected x_search tool slot")
|
||||
assert.Equal(t, responses.WebSearchToolType("x_search"), xSearchTool.OfWebSearchPreview.Type)
|
||||
}
|
||||
|
||||
// TestBuildResponseParams_DefaultProvider_Unchanged guards backwards
|
||||
// compatibility. A client that does not set the new override fields
|
||||
// must continue emitting a single web_search_preview tool entry.
|
||||
func TestBuildResponseParams_DefaultProvider_Unchanged(t *testing.T) {
|
||||
client := NewClient()
|
||||
|
||||
opts := &domain.ChatOptions{
|
||||
Model: "gpt-4o",
|
||||
Temperature: 0.7,
|
||||
Search: true,
|
||||
}
|
||||
|
||||
msgs := []*chat.ChatCompletionMessage{
|
||||
{Role: "user", Content: "What is the capital of France?"},
|
||||
}
|
||||
|
||||
params := client.buildResponseParams(msgs, opts)
|
||||
|
||||
assert.NotNil(t, params.Tools, "Expected tools when search is enabled")
|
||||
assert.Len(t, params.Tools, 1, "Expected exactly one tool for default provider")
|
||||
|
||||
tool := params.Tools[0]
|
||||
assert.NotNil(t, tool.OfWebSearchPreview, "Expected web search tool slot")
|
||||
assert.Equal(t, responses.WebSearchToolType("web_search_preview"), tool.OfWebSearchPreview.Type)
|
||||
}
|
||||
|
||||
// TestBuildResponseParams_GrokAI_WithoutSearch confirms that a GrokAI
|
||||
// style client without Search enabled does not append any tools.
|
||||
// This protects the no-search path from regressions introduced by the
|
||||
// new override logic.
|
||||
func TestBuildResponseParams_GrokAI_WithoutSearch(t *testing.T) {
|
||||
client := NewClient()
|
||||
client.SetWebSearchToolName("web_search")
|
||||
client.SetEnableXSearch(true)
|
||||
|
||||
opts := &domain.ChatOptions{
|
||||
Model: "grok-4-fast-reasoning",
|
||||
Temperature: 0.7,
|
||||
Search: false,
|
||||
}
|
||||
|
||||
msgs := []*chat.ChatCompletionMessage{
|
||||
{Role: "user", Content: "Hello"},
|
||||
}
|
||||
|
||||
params := client.buildResponseParams(msgs, opts)
|
||||
|
||||
assert.Nil(t, params.Tools, "Expected no tools when search is disabled")
|
||||
}
|
||||
|
||||
func TestCitationFormatting(t *testing.T) {
|
||||
// Test the citation formatting logic by simulating the citation extraction
|
||||
var textParts []string
|
||||
|
|
|
|||
|
|
@ -21,6 +21,14 @@ type ProviderConfig struct {
|
|||
BaseURL string
|
||||
ModelsURL string // Optional: Custom endpoint for listing models (if different from BaseURL/models)
|
||||
ImplementsResponses bool // Whether the provider supports OpenAI's new Responses API
|
||||
// WebSearchToolName overrides the default "web_search_preview" tool name
|
||||
// emitted on the Responses API when Search is enabled. Leave empty to keep
|
||||
// the OpenAI default. xAI, for example, requires "web_search".
|
||||
WebSearchToolName string
|
||||
// EnableXSearch, when true, also appends an xAI "x_search" tool entry
|
||||
// alongside the web search tool when Search is enabled. Non-xAI
|
||||
// providers should leave this false.
|
||||
EnableXSearch bool
|
||||
}
|
||||
|
||||
// Client is the common structure for all OpenAI-compatible providers
|
||||
|
|
@ -40,11 +48,15 @@ func NewClient(providerConfig ProviderConfig) *Client {
|
|||
providerConfig.ImplementsResponses,
|
||||
nil,
|
||||
)
|
||||
// Apply optional Responses API tool overrides. Zero values preserve
|
||||
// existing behavior for providers that do not set these fields.
|
||||
client.Client.SetWebSearchToolName(providerConfig.WebSearchToolName)
|
||||
client.Client.SetEnableXSearch(providerConfig.EnableXSearch)
|
||||
return client
|
||||
}
|
||||
|
||||
// ListModels overrides the default ListModels to handle different response formats
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(ctx context.Context) ([]string, error) {
|
||||
// If a custom models URL is provided, handle it
|
||||
if c.modelsURL != "" {
|
||||
if c.modelsURL == "static:abacus" {
|
||||
|
|
@ -65,13 +77,13 @@ func (c *Client) ListModels() ([]string, error) {
|
|||
}
|
||||
|
||||
// First try the standard OpenAI SDK approach
|
||||
models, err := c.Client.ListModels()
|
||||
models, err := c.Client.ListModels(ctx)
|
||||
if err == nil && len(models) > 0 { // only return if OpenAI SDK returns models
|
||||
return models, nil
|
||||
}
|
||||
|
||||
// Fall back to direct API fetch
|
||||
return c.DirectlyGetModels(context.Background())
|
||||
return c.DirectlyGetModels(ctx)
|
||||
}
|
||||
|
||||
func (c *Client) fetchAbacusModels() ([]string, error) {
|
||||
|
|
@ -194,14 +206,9 @@ func (c *Client) getStaticModels(modelsKey string) ([]string, error) {
|
|||
}, nil
|
||||
case "static:minimax":
|
||||
return []string{
|
||||
"MiniMax-M3",
|
||||
"MiniMax-M2.7",
|
||||
"MiniMax-M2.7-highspeed",
|
||||
"MiniMax-M2.5",
|
||||
"MiniMax-M2.5-highspeed",
|
||||
"MiniMax-M2.5-lightning",
|
||||
"MiniMax-M2",
|
||||
"MiniMax-M2.1",
|
||||
"MiniMax-M2.1-lightning",
|
||||
}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf(i18n.T("openai_compatible_unknown_static_model_list"), modelsKey)
|
||||
|
|
@ -240,6 +247,11 @@ var ProviderMap = map[string]ProviderConfig{
|
|||
Name: "GrokAI",
|
||||
BaseURL: "https://api.x.ai/v1",
|
||||
ImplementsResponses: true,
|
||||
// xAI's Responses API expects the "web_search" tool type, not
|
||||
// OpenAI's "web_search_preview", and additionally accepts an
|
||||
// "x_search" tool entry for live search grounding.
|
||||
WebSearchToolName: "web_search",
|
||||
EnableXSearch: true,
|
||||
},
|
||||
"Groq": {
|
||||
Name: "Groq",
|
||||
|
|
@ -277,11 +289,21 @@ var ProviderMap = map[string]ProviderConfig{
|
|||
BaseURL: "https://openrouter.ai/api/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Pzero": {
|
||||
Name: "Pzero",
|
||||
BaseURL: "https://api.pzero.studio/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"SiliconCloud": {
|
||||
Name: "SiliconCloud",
|
||||
BaseURL: "https://api.siliconflow.cn/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Synthorai": {
|
||||
Name: "Synthorai",
|
||||
BaseURL: "https://synthorai.io/v1",
|
||||
ImplementsResponses: false,
|
||||
},
|
||||
"Together": {
|
||||
Name: "Together",
|
||||
BaseURL: "https://api.together.xyz/v1",
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ func (c *Client) Configure() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(_ context.Context) ([]string, error) {
|
||||
// Perplexity API does not have a ListModels endpoint.
|
||||
// We return a predefined list.
|
||||
return models, nil
|
||||
|
|
@ -119,7 +119,7 @@ func (c *Client) Send(ctx context.Context, msgs []*chat.ChatCompletionMessage, o
|
|||
return content.String(), nil
|
||||
}
|
||||
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
func (c *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
if c.client == nil {
|
||||
if err := c.Configure(); err != nil {
|
||||
close(channel) // Ensure channel is closed on error
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ import (
|
|||
|
||||
type Vendor interface {
|
||||
plugins.Plugin
|
||||
ListModels() ([]string, error)
|
||||
SendStream([]*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error
|
||||
ListModels(context.Context) ([]string, error)
|
||||
SendStream(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error
|
||||
Send(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions) (string, error)
|
||||
NeedsRawMode(modelName string) bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,7 +117,7 @@ func (o *VendorsManager) fetchVendorModels(
|
|||
|
||||
defer wg.Done()
|
||||
|
||||
models, err := vendor.ListModels()
|
||||
models, err := vendor.ListModels(ctx)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
// Context canceled, don't send the result
|
||||
|
|
|
|||
|
|
@ -13,14 +13,14 @@ type stubVendor struct {
|
|||
name string
|
||||
}
|
||||
|
||||
func (v *stubVendor) GetName() string { return v.name }
|
||||
func (v *stubVendor) GetSetupDescription() string { return "" }
|
||||
func (v *stubVendor) IsConfigured() bool { return true }
|
||||
func (v *stubVendor) Configure() error { return nil }
|
||||
func (v *stubVendor) Setup() error { return nil }
|
||||
func (v *stubVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
|
||||
func (v *stubVendor) ListModels() ([]string, error) { return nil, nil }
|
||||
func (v *stubVendor) SendStream([]*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error {
|
||||
func (v *stubVendor) GetName() string { return v.name }
|
||||
func (v *stubVendor) GetSetupDescription() string { return "" }
|
||||
func (v *stubVendor) IsConfigured() bool { return true }
|
||||
func (v *stubVendor) Configure() error { return nil }
|
||||
func (v *stubVendor) Setup() error { return nil }
|
||||
func (v *stubVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
|
||||
func (v *stubVendor) ListModels(context.Context) ([]string, error) { return nil, nil }
|
||||
func (v *stubVendor) SendStream(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions, chan domain.StreamUpdate) error {
|
||||
return nil
|
||||
}
|
||||
func (v *stubVendor) Send(context.Context, []*chat.ChatCompletionMessage, *domain.ChatOptions) (string, error) {
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ func (c *Client) configure() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) ListModels() ([]string, error) {
|
||||
func (c *Client) ListModels(_ context.Context) ([]string, error) {
|
||||
ctx := context.Background()
|
||||
|
||||
// Get ADC credentials for API authentication
|
||||
|
|
@ -179,7 +179,7 @@ func (c *Client) sendClaude(ctx context.Context, msgs []*chat.ChatCompletionMess
|
|||
return strings.Join(textParts, ""), nil
|
||||
}
|
||||
|
||||
func (c *Client) SendStream(msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
func (c *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessage, opts *domain.ChatOptions, channel chan domain.StreamUpdate) error {
|
||||
if isGeminiModel(opts.Model) {
|
||||
return c.sendStreamGemini(msgs, opts, channel)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,15 @@
|
|||
package db
|
||||
|
||||
// Storage is the contract for a named-entity store. Each implementation
|
||||
// specifies the names that are valid. It rejects an invalid name with a
|
||||
// typed error. Callers can map this error to a client error.
|
||||
type Storage[T any] interface {
|
||||
Configure() (err error)
|
||||
Get(name string) (ret *T, err error)
|
||||
GetNames() (ret []string, err error)
|
||||
Delete(name string) (err error)
|
||||
// Exists reports false for an invalid name. It cannot show the
|
||||
// difference between a rejected name and an absent entry.
|
||||
Exists(name string) (ret bool)
|
||||
Rename(oldName, newName string) (err error)
|
||||
Save(name string, content []byte) (err error)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
|
|
@ -41,6 +42,8 @@ type Db struct {
|
|||
Contexts *ContextsEntity
|
||||
|
||||
EnvFilePath string
|
||||
|
||||
envMu sync.Mutex
|
||||
}
|
||||
|
||||
func (o *Db) Configure() (err error) {
|
||||
|
|
@ -92,9 +95,100 @@ func (o *Db) IsEnvFileExists() (ret bool) {
|
|||
return
|
||||
}
|
||||
|
||||
func (o *Db) SaveEnv(content string) (err error) {
|
||||
err = os.WriteFile(o.EnvFilePath, []byte(content), 0644)
|
||||
return
|
||||
func (o *Db) SaveEnv(content string) error {
|
||||
return o.WithEnvLock(func() error {
|
||||
if err := writeFileAtomic(o.EnvFilePath, []byte(content)); err != nil {
|
||||
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (o *Db) ReadEnvFile() (map[string]string, error) {
|
||||
env, err := godotenv.Read(o.EnvFilePath)
|
||||
if err != nil {
|
||||
if o.IsEnvFileExists() {
|
||||
return nil, fmt.Errorf(i18n.T("db_error_loading_env_file"), err)
|
||||
}
|
||||
return map[string]string{}, nil
|
||||
}
|
||||
return env, nil
|
||||
}
|
||||
|
||||
func (o *Db) WithEnvLock(fn func() error) error {
|
||||
o.envMu.Lock()
|
||||
defer o.envMu.Unlock()
|
||||
|
||||
lockFile, err := os.OpenFile(o.EnvFilePath+".lock", os.O_CREATE|os.O_RDWR, 0600)
|
||||
if err != nil {
|
||||
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
|
||||
}
|
||||
defer lockFile.Close()
|
||||
if err := lockExclusive(lockFile); err != nil {
|
||||
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
|
||||
}
|
||||
defer unlockExclusive(lockFile)
|
||||
|
||||
return fn()
|
||||
}
|
||||
|
||||
// UpdateEnvVars merges non-empty values into .env under a file lock.
|
||||
// Comments and key order are not preserved.
|
||||
func (o *Db) UpdateEnvVars(updates map[string]string) error {
|
||||
return o.WithEnvLock(func() error {
|
||||
return o.ApplyEnvUpdates(updates)
|
||||
})
|
||||
}
|
||||
|
||||
// ApplyEnvUpdates writes non-empty updates atomically. Callers holding WithEnvLock use this.
|
||||
func (o *Db) ApplyEnvUpdates(updates map[string]string) error {
|
||||
env, err := o.ReadEnvFile()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for key, value := range updates {
|
||||
if strings.TrimSpace(value) == "" {
|
||||
continue
|
||||
}
|
||||
env[key] = value
|
||||
}
|
||||
if err := writeEnvFileAtomic(o.EnvFilePath, env); err != nil {
|
||||
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeEnvFileAtomic(path string, env map[string]string) error {
|
||||
content, err := godotenv.Marshal(env)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeFileAtomic(path, []byte(content+"\n"))
|
||||
}
|
||||
|
||||
func writeFileAtomic(path string, content []byte) error {
|
||||
tmp, err := os.CreateTemp(filepath.Dir(path), ".env.tmp-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tmpName := tmp.Name()
|
||||
defer os.Remove(tmpName)
|
||||
|
||||
if err := tmp.Chmod(0600); err != nil {
|
||||
_ = tmp.Close()
|
||||
return err
|
||||
}
|
||||
_, err = tmp.Write(content)
|
||||
if err == nil {
|
||||
err = tmp.Sync()
|
||||
}
|
||||
if cerr := tmp.Close(); err == nil {
|
||||
err = cerr
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.Rename(tmpName, path)
|
||||
}
|
||||
|
||||
func (o *Db) FilePath(fileName string) (ret string) {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,12 @@ package fsdb
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func TestDb_Configure(t *testing.T) {
|
||||
|
|
@ -52,4 +57,150 @@ func TestDb_SaveEnv(t *testing.T) {
|
|||
if _, err := os.Stat(db.EnvFilePath); os.IsNotExist(err) {
|
||||
t.Errorf("expected .env file to be saved")
|
||||
}
|
||||
assertEnvMode(t, db.EnvFilePath)
|
||||
}
|
||||
|
||||
func TestDb_UpdateEnvVars(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := NewDb(dir)
|
||||
if err := db.SaveEnv("KEEP=old\nCODEX_REFRESH_TOKEN=stale\n"); err != nil {
|
||||
t.Fatalf("SaveEnv() error = %v", err)
|
||||
}
|
||||
|
||||
if err := db.UpdateEnvVars(map[string]string{
|
||||
"CODEX_REFRESH_TOKEN": "rotated",
|
||||
"CODEX_ACCESS_TOKEN": "fresh",
|
||||
}); err != nil {
|
||||
t.Fatalf("UpdateEnvVars() error = %v", err)
|
||||
}
|
||||
|
||||
parsed, err := godotenv.Read(db.EnvFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("godotenv.Read() error = %v", err)
|
||||
}
|
||||
if parsed["KEEP"] != "old" {
|
||||
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
|
||||
}
|
||||
if parsed["CODEX_REFRESH_TOKEN"] != "rotated" {
|
||||
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want rotated", parsed["CODEX_REFRESH_TOKEN"])
|
||||
}
|
||||
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
|
||||
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
|
||||
}
|
||||
assertEnvMode(t, db.EnvFilePath)
|
||||
}
|
||||
|
||||
func TestDb_UpdateEnvVars_MissingFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := NewDb(dir)
|
||||
|
||||
if err := db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "fresh"}); err != nil {
|
||||
t.Fatalf("UpdateEnvVars() error = %v", err)
|
||||
}
|
||||
|
||||
parsed, err := godotenv.Read(db.EnvFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("godotenv.Read() error = %v", err)
|
||||
}
|
||||
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
|
||||
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
|
||||
}
|
||||
assertEnvMode(t, db.EnvFilePath)
|
||||
}
|
||||
|
||||
func TestDb_UpdateEnvVars_CorruptFile(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := NewDb(dir)
|
||||
if err := os.Mkdir(db.EnvFilePath, 0700); err != nil {
|
||||
t.Fatalf("Mkdir() error = %v", err)
|
||||
}
|
||||
|
||||
err := db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "fresh"})
|
||||
if err == nil {
|
||||
t.Fatal("UpdateEnvVars() error = nil, want corrupt-file error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestDb_UpdateEnvVars_SkipEmpty(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := NewDb(dir)
|
||||
if err := db.SaveEnv("CODEX_REFRESH_TOKEN=live\nKEEP=old\n"); err != nil {
|
||||
t.Fatalf("SaveEnv() error = %v", err)
|
||||
}
|
||||
|
||||
if err := db.UpdateEnvVars(map[string]string{
|
||||
"CODEX_REFRESH_TOKEN": "",
|
||||
"CODEX_ACCESS_TOKEN": "fresh",
|
||||
}); err != nil {
|
||||
t.Fatalf("UpdateEnvVars() error = %v", err)
|
||||
}
|
||||
|
||||
parsed, err := godotenv.Read(db.EnvFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("godotenv.Read() error = %v", err)
|
||||
}
|
||||
if parsed["CODEX_REFRESH_TOKEN"] != "live" {
|
||||
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want live", parsed["CODEX_REFRESH_TOKEN"])
|
||||
}
|
||||
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
|
||||
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
|
||||
}
|
||||
if parsed["KEEP"] != "old" {
|
||||
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestDb_UpdateEnvVars_Concurrent(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
db := NewDb(dir)
|
||||
if err := db.SaveEnv("KEEP=old\n"); err != nil {
|
||||
t.Fatalf("SaveEnv() error = %v", err)
|
||||
}
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errs := make(chan error, 2)
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
errs <- db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "one"})
|
||||
}()
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
errs <- db.UpdateEnvVars(map[string]string{"CODEX_REFRESH_TOKEN": "two"})
|
||||
}()
|
||||
wg.Wait()
|
||||
close(errs)
|
||||
for err := range errs {
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateEnvVars() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
parsed, err := godotenv.Read(db.EnvFilePath)
|
||||
if err != nil {
|
||||
t.Fatalf("godotenv.Read() error = %v", err)
|
||||
}
|
||||
if parsed["KEEP"] != "old" {
|
||||
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
|
||||
}
|
||||
if parsed["CODEX_ACCESS_TOKEN"] != "one" {
|
||||
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want one", parsed["CODEX_ACCESS_TOKEN"])
|
||||
}
|
||||
if parsed["CODEX_REFRESH_TOKEN"] != "two" {
|
||||
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want two", parsed["CODEX_REFRESH_TOKEN"])
|
||||
}
|
||||
}
|
||||
|
||||
func assertEnvMode(t *testing.T, path string) {
|
||||
t.Helper()
|
||||
if runtime.GOOS == "windows" {
|
||||
return
|
||||
}
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
t.Fatalf("Stat(%s) error = %v", path, err)
|
||||
}
|
||||
if perm := info.Mode().Perm(); perm != 0600 {
|
||||
t.Fatalf("%s mode = %o, want 0600", filepath.Base(path), perm)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue