mirror of
https://github.com/danielmiessler/fabric.git
synced 2026-09-10 15:46:39 -04:00
Compare commits
No commits in common. "main" and "v1.4.445" have entirely different histories.
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
|
@ -8,13 +8,10 @@
|
|||
"anthropics",
|
||||
"Aoede",
|
||||
"apikey",
|
||||
"APIM",
|
||||
"aplicar",
|
||||
"appuser",
|
||||
"Astley",
|
||||
"atotto",
|
||||
"Autonoe",
|
||||
"azurecommon",
|
||||
"azureml",
|
||||
"badfile",
|
||||
"Behrens",
|
||||
|
|
@ -134,7 +131,6 @@
|
|||
"modelines",
|
||||
"Moltbot",
|
||||
"mpga",
|
||||
"MSAL",
|
||||
"mvdan",
|
||||
"mychat",
|
||||
"mygroup",
|
||||
|
|
@ -226,7 +222,6 @@
|
|||
"cSpell.ignorePaths": [
|
||||
"go.mod",
|
||||
".gitignore",
|
||||
".vscode/**",
|
||||
"CHANGELOG.md",
|
||||
"scripts/installer/install.*",
|
||||
"web/static/data/pattern_descriptions.json",
|
||||
|
|
@ -260,7 +255,6 @@
|
|||
"module",
|
||||
"p",
|
||||
"summary",
|
||||
"strong",
|
||||
"sup"
|
||||
]
|
||||
},
|
||||
|
|
|
|||
338
CHANGELOG.md
338
CHANGELOG.md
|
|
@ -1,343 +1,5 @@
|
|||
# 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
|
||||
|
|
|
|||
67
README.md
67
README.md
|
|
@ -82,7 +82,6 @@ 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.
|
||||
|
|
@ -137,7 +136,6 @@ 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)
|
||||
|
|
@ -264,10 +262,6 @@ 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.
|
||||
|
|
@ -290,13 +284,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:/home/appuser/.config/fabric kayvan/fabric:latest --setup
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --setup
|
||||
|
||||
# Use Fabric with your patterns
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest -p summarize
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.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:/home/appuser/.config/fabric kayvan/fabric:latest --serve
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --serve
|
||||
```
|
||||
|
||||
**Images available at:**
|
||||
|
|
@ -374,9 +368,7 @@ Fabric supports a wide range of AI providers:
|
|||
- Mistral
|
||||
- Novita AI
|
||||
- OpenRouter
|
||||
- Pzero
|
||||
- SiliconCloud
|
||||
- Synthorai
|
||||
- Together
|
||||
- Venice AI
|
||||
- Z AI
|
||||
|
|
@ -666,41 +658,37 @@ 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 the selected model (e.g., -V "LM Studio" -m
|
||||
openai/gpt-oss-20b)
|
||||
-V, --vendor= Specify vendor for chosen 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
|
||||
-n, --latest= Number of latest patterns to list (default: 0)
|
||||
-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
|
||||
--visual-sensitivity Tolerance for FFmpeg scene detection (0.0 - 1.0)
|
||||
--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
|
||||
--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
|
||||
-g, --language= Specify the Language Code for the chat, e.g. -g=en -g=zh
|
||||
-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
|
||||
|
|
@ -725,31 +713,29 @@ 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, Grok)
|
||||
--search Enable web search tool for supported models (Anthropic, OpenAI, Gemini)
|
||||
--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)
|
||||
--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)
|
||||
--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)
|
||||
--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)
|
||||
--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)
|
||||
--show-metadata Print metadata (input/output tokens) to stderr
|
||||
--debug= Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)
|
||||
|
||||
--debug= Set debug level (0: off, 1: basic, 2: detailed, 3: trace)
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
```
|
||||
|
|
@ -762,7 +748,6 @@ 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
|
||||
|
||||
|
|
|
|||
16
README.zh.md
16
README.zh.md
|
|
@ -131,7 +131,6 @@ Fabric 按照现实世界中的任务来组织 Prompt,允许人们在一个地
|
|||
- [macOS (Homebrew)](#macos-homebrew)
|
||||
- [Arch Linux (AUR)](#arch-linux-aur)
|
||||
- [Windows](#windows)
|
||||
- [Windows (Scoop)](#windows-scoop)
|
||||
- [从源码构建](#从源码构建)
|
||||
- [Docker](#docker)
|
||||
- [环境变量](#环境变量)
|
||||
|
|
@ -261,12 +260,6 @@ yay -S fabric-ai
|
|||
winget install danielmiessler.Fabric
|
||||
```
|
||||
|
||||
#### Windows (Scoop)
|
||||
|
||||
```bash
|
||||
scoop install fabric-ai
|
||||
```
|
||||
|
||||
### 从源码构建
|
||||
|
||||
安装 Fabric 前,请先[确保已安装 Go](https://go.dev/doc/install),然后运行:
|
||||
|
|
@ -288,13 +281,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:/home/appuser/.config/fabric kayvan/fabric:latest --setup
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --setup
|
||||
|
||||
# 使用你的 patterns
|
||||
docker run --rm -it -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest -p summarize
|
||||
docker run --rm -it -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest -p summarize
|
||||
|
||||
# 运行 REST API 服务器
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/home/appuser/.config/fabric kayvan/fabric:latest --serve
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric kayvan/fabric:latest --serve
|
||||
```
|
||||
|
||||
**镜像来源:**
|
||||
|
|
@ -358,7 +351,7 @@ fabric --setup
|
|||
|
||||
**OpenAI 兼容供应商:**
|
||||
|
||||
- Abacus、AIML、Cerebras、DeepSeek、DigitalOcean、GitHub Models、GrokAI、Groq、Langdock、LiteLLM、MiniMax、Mistral、Novita AI、OpenRouter、SiliconCloud、Synthorai、Together、Venice AI、Z AI
|
||||
- Abacus、AIML、Cerebras、DeepSeek、DigitalOcean、GitHub Models、GrokAI、Groq、Langdock、LiteLLM、MiniMax、Mistral、Novita AI、OpenRouter、SiliconCloud、Together、Venice AI、Z AI
|
||||
|
||||
运行 `fabric --setup` 配置首选供应商,或使用 `fabric --listvendors` 查看所有可用供应商。
|
||||
|
||||
|
|
@ -480,7 +473,6 @@ pbpaste | fabric --pattern extract_wisdom
|
|||
- `1`:基本调试信息
|
||||
- `2`:详细调试
|
||||
- `3`:追踪级别
|
||||
- `4`:wire 级别(完整的请求和响应内容)
|
||||
|
||||
### 演习模式
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
package main
|
||||
|
||||
var version = "v1.4.478"
|
||||
var version = "v1.4.445"
|
||||
|
|
|
|||
|
|
@ -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=gpt-5.6-sol
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-sonnet-4-20250514
|
||||
```
|
||||
|
||||
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-opus-5)
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=gpt-5.6-sol generate_changelog --ai-summarize
|
||||
# Custom model (default: claude-sonnet-4-20250514)
|
||||
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-opus-4 generate_changelog --ai-summarize
|
||||
```
|
||||
|
||||
### AI Summary Features
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -381,13 +381,11 @@ func (g *Generator) validatePRState(prNumber int) error {
|
|||
return fmt.Errorf("failed to fetch PR %d: %w", prNumber, err)
|
||||
}
|
||||
|
||||
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.State != "open" {
|
||||
return fmt.Errorf("PR %d is not open (current state: %s)", prNumber, details.State)
|
||||
}
|
||||
|
||||
// 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 {
|
||||
if !details.Mergeable {
|
||||
return fmt.Errorf("PR %d is not mergeable - please resolve conflicts first", prNumber)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import (
|
|||
"strings"
|
||||
)
|
||||
|
||||
const DefaultSummarizeModel = "claude-opus-5"
|
||||
const DefaultSummarizeModel = "claude-sonnet-4-5"
|
||||
const MinContentLength = 256 // Minimum content length to consider for summarization
|
||||
|
||||
const prompt = `# ROLE
|
||||
|
|
|
|||
|
|
@ -18,5 +18,4 @@ type Config struct {
|
|||
Push bool
|
||||
SyncDB bool
|
||||
Release string
|
||||
ClosedOK bool
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,7 +45,6 @@ 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,14 +84,13 @@ _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 the selected model (e.g., -V "LM Studio" -m openai/gpt-oss-20b)]:vendor:_fabric_vendors' \
|
||||
'(-V --vendor)'{-V,--vendor}'[Specify vendor for chosen 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]' \
|
||||
|
|
@ -126,7 +125,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, Grok)]' \
|
||||
'(--search)--search[Enable web search tool for supported models (Anthropic, OpenAI, Gemini)]' \
|
||||
'(--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)' \
|
||||
|
|
@ -141,7 +140,6 @@ _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:' \
|
||||
|
|
@ -150,7 +148,6 @@ _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 --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"
|
||||
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 --visual --visual-sensitivity --visual-fps --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"
|
||||
|
||||
# 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 | --readpattern)
|
||||
-p | --pattern)
|
||||
COMPREPLY=($(compgen -W "$(_fabric_get_list --listpatterns)" -- "${cur}"))
|
||||
return 0
|
||||
;;
|
||||
|
|
|
|||
|
|
@ -53,69 +53,56 @@ 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
|
||||
|
||||
# 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)"
|
||||
# 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 -l visual-sensitivity -d "Tolerance for FFmpeg scene detection (0.0 - 1.0)"
|
||||
complete -c $cmd -l visual-fps -d "Extract a specific number of frames per second instead of using scene detection"
|
||||
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)"
|
||||
|
||||
# Boolean flags (no arguments)
|
||||
complete -c $cmd -s S -l setup -d "Run setup for all reconfigurable parts of fabric"
|
||||
|
|
@ -135,11 +122,12 @@ function __fabric_register_completions
|
|||
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, Grok)"
|
||||
complete -c $cmd -l search -d "Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"
|
||||
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"
|
||||
|
|
@ -147,14 +135,13 @@ 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
|
||||
|
|
|
|||
|
|
@ -1,62 +0,0 @@
|
|||
# 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:
|
||||
|
|
@ -1,58 +0,0 @@
|
|||
# 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:
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
# 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:
|
||||
|
|
@ -1,57 +0,0 @@
|
|||
# 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,240 +20,236 @@
|
|||
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_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.
|
||||
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.
|
||||
|
|
|
|||
|
|
@ -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_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
|
||||
**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
|
||||
|
||||
**BILL**: analyze_bill, analyze_bill_short
|
||||
|
||||
**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
|
||||
**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
|
||||
|
||||
**CLASSIFICATION**: apply_ul_tags
|
||||
|
||||
**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
|
||||
**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
|
||||
|
||||
**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_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
|
||||
**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
|
||||
|
||||
**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, 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
|
||||
**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
|
||||
|
||||
## Workflow Suggestions
|
||||
|
||||
|
|
|
|||
|
|
@ -136,10 +136,6 @@ 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.
|
||||
|
|
@ -446,10 +442,6 @@ 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.
|
||||
|
|
@ -546,10 +538,6 @@ 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.
|
||||
|
|
@ -702,10 +690,6 @@ 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,7 +55,6 @@ 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,18 +112,14 @@ For manual installation or troubleshooting, see the detailed instructions below.
|
|||
|
||||
The completions provide intelligent suggestions for:
|
||||
|
||||
- **Patterns**: Tab-complete available patterns with `-p`, `--pattern`, or `--readpattern`
|
||||
- **Patterns**: Tab-complete available patterns with `-p` or `--pattern`
|
||||
- **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`
|
||||
- **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
|
||||
- **File paths**: Smart file completion for attachment, output, and config options
|
||||
- **Flag completion**: All available command-line flags and options
|
||||
|
||||
## Alternative Installation Method
|
||||
|
|
|
|||
13
docs/docs.go
13
docs/docs.go
|
|
@ -139,15 +139,6 @@ const docTemplate = `{
|
|||
"$ref": "#/definitions/fsdb.Pattern"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -355,6 +346,7 @@ const docTemplate = `{
|
|||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"description": "Add Language field to bind from request",
|
||||
"type": "string"
|
||||
},
|
||||
"maxTokens": {
|
||||
|
|
@ -420,6 +412,9 @@ const docTemplate = `{
|
|||
"type": "number",
|
||||
"format": "float64"
|
||||
},
|
||||
"updateChan": {
|
||||
"type": "object"
|
||||
},
|
||||
"voice": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -133,15 +133,6 @@
|
|||
"$ref": "#/definitions/fsdb.Pattern"
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"schema": {
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"500": {
|
||||
"description": "Internal Server Error",
|
||||
"schema": {
|
||||
|
|
@ -349,6 +340,7 @@
|
|||
"type": "string"
|
||||
},
|
||||
"language": {
|
||||
"description": "Add Language field to bind from request",
|
||||
"type": "string"
|
||||
},
|
||||
"maxTokens": {
|
||||
|
|
@ -414,6 +406,9 @@
|
|||
"type": "number",
|
||||
"format": "float64"
|
||||
},
|
||||
"updateChan": {
|
||||
"type": "object"
|
||||
},
|
||||
"voice": {
|
||||
"type": "string"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ definitions:
|
|||
imageSize:
|
||||
type: string
|
||||
language:
|
||||
description: Add Language field to bind from request
|
||||
type: string
|
||||
maxTokens:
|
||||
type: integer
|
||||
|
|
@ -94,6 +95,8 @@ definitions:
|
|||
topP:
|
||||
format: float64
|
||||
type: number
|
||||
updateChan:
|
||||
type: object
|
||||
voice:
|
||||
type: string
|
||||
type: object
|
||||
|
|
@ -262,12 +265,6 @@ 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": 1785692966,
|
||||
"narHash": "sha256-vUfIeBEfpbAfZ5zjgIkYk7eHBeVfCYVjLbWnMkseYnk=",
|
||||
"lastModified": 1765472234,
|
||||
"narHash": "sha256-9VvC20PJPsleGMewwcWYKGzDIyjckEz8uWmT0vCDYK0=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "643809054d65fdd466a63e3155b8c498cb483c04",
|
||||
"rev": "2fbfb1d73d239d2402a8fe03963e37aab15abe8b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
|
|||
169
go.mod
169
go.mod
|
|
@ -1,125 +1,120 @@
|
|||
module github.com/danielmiessler/fabric
|
||||
|
||||
go 1.26.0
|
||||
go 1.25.1
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0
|
||||
github.com/anthropics/anthropic-sdk-go v1.67.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
|
||||
github.com/anthropics/anthropic-sdk-go v1.34.0
|
||||
github.com/atotto/clipboard v0.1.4
|
||||
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/aws/aws-sdk-go-v2 v1.41.5
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.14
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.59.0
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.4
|
||||
github.com/gabriel-vasile/mimetype v1.4.13
|
||||
github.com/gin-gonic/gin v1.12.0
|
||||
github.com/go-git/go-git/v5 v5.19.2
|
||||
github.com/go-git/go-git/v5 v5.17.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.16.0
|
||||
github.com/hasura/go-graphql-client v0.15.1
|
||||
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.50
|
||||
github.com/mattn/go-sqlite3 v1.14.42
|
||||
github.com/nicksnyder/go-i18n/v2 v2.6.1
|
||||
github.com/ollama/ollama v0.33.1
|
||||
github.com/ollama/ollama v0.20.4
|
||||
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.16.1
|
||||
github.com/sgaunet/perplexity-go/v2 v2.15.0
|
||||
github.com/spf13/cobra v1.10.2
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/stretchr/testify v1.11.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/sys v0.47.0
|
||||
golang.org/x/text v0.41.0
|
||||
google.golang.org/api v0.294.0
|
||||
golang.org/x/text v0.36.0
|
||||
google.golang.org/api v0.275.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.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/AzureAD/microsoft-authentication-library-for-go v1.7.1 // indirect
|
||||
github.com/KyleBanks/depth v1.2.1 // 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/aws/aws-sdk-go-v2/service/signin v1.0.9 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/buger/jsonparser v1.6.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/bytedance/gopkg v0.1.4 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // 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/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/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.61.0 // indirect
|
||||
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 // indirect
|
||||
github.com/quic-go/quic-go v0.59.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // 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
|
||||
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/mod v0.35.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.123.0 // indirect
|
||||
cloud.google.com/go/auth v0.23.2 // indirect
|
||||
cloud.google.com/go/auth v0.20.0 // 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.4 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // 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/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // 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.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10 // indirect
|
||||
github.com/aws/smithy-go v1.24.3 // indirect
|
||||
github.com/bytedance/sonic v1.15.0 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.1 // 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.1.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // 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.9.1 // indirect
|
||||
github.com/go-logr/logr v1.4.4 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // 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.3 // indirect
|
||||
github.com/go-playground/validator/v10 v10.30.2 // 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
|
||||
|
|
@ -127,43 +122,45 @@ 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.21 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.24.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.21.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.4.0 // indirect
|
||||
github.com/leodido/go-urn v1.5.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.24 // 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.21 // 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.4.3 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.3.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.5.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.19.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.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.2 // indirect
|
||||
github.com/ugorji/go/codec v1.3.1 // 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.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
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.43.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
golang.org/x/arch v0.25.0 // indirect
|
||||
golang.org/x/crypto v0.50.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250531010427-b6e5de432a8b // indirect
|
||||
golang.org/x/net v0.53.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.43.0 // indirect
|
||||
google.golang.org/genai v1.53.0
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
)
|
||||
|
|
|
|||
620
go.sum
620
go.sum
|
|
@ -1,31 +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.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 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.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA=
|
||||
cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q=
|
||||
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.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/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/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.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/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.1 h1:edShSHV3DV90+kt+CMaEXEzR9QF7wFrPJxVGz2blMIU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.1/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
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=
|
||||
|
|
@ -35,119 +35,109 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo
|
|||
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
|
||||
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.4 h1:vM2lgh0Vru9Vwyfm4cQqWP2HHMW0u0+2PAW7Q38Qufg=
|
||||
github.com/andybalholm/cascadia v1.3.4/go.mod h1:BLRmbRjpEtNKieZOCCvYj4RqN+KRA41GBe/5O+G93kM=
|
||||
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/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.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/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.34.0 h1:IV+Wwxkwypit9Md8dr48zc626NS4o9PoQieESoNE0TE=
|
||||
github.com/anthropics/anthropic-sdk-go v1.34.0/go.mod h1:dSIO7kSrOI7MA4fE6RRVaw8tyWP7HNQU5/H/KS4cax8=
|
||||
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.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/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 v1.41.5 h1:dj5kopbwUsVUVFgO4Fi5BIT3t4WyqIDjGKCangnV/yY=
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=
|
||||
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/aws/protocol/eventstream v1.7.8 h1:eBMB84YGghSocM7PsjmmPffTa+1FBUeNvGvFou6V/4o=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8/go.mod h1:lyw7GFp3qENLh7kwzf7iMzAxDn+NzjXEAGjKS2UOKqI=
|
||||
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/config v1.32.14 h1:opVIRo/ZbbI8OIqSOKmpFaY7IwfFUOCCXBsUpJOwDdI=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.32.14/go.mod h1:U4/V0uKxh0Tl5sxmCBZ3AecYny4UNlVmObYjKuuaiOo=
|
||||
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/credentials v1.19.14 h1:n+UcGWAIZHkXzYt87uMFBv/l8THYELoX6gVcUvgl6fI=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.19.14/go.mod h1:cJKuyWB59Mqi0jM3nFYQRmnHVQIcgoxjEMAbLkpr62w=
|
||||
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/feature/ec2/imds v1.18.21 h1:NUS3K4BTDArQqNu2ih7yeDLaS3bmHD0YndtA6UP884g=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.21/go.mod h1:YWNWJQNjKigKY1RHVJCuupeWDrrHjRqHm0N9rdrWzYI=
|
||||
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/configsources v1.4.21 h1:Rgg6wvjjtX8bNHcvi9OnXWwcE0a2vGpbwmtICOsvcf4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21/go.mod h1:A/kJFst/nm//cyqonihbdpQZwiUhhzpqTsdbhDdRF9c=
|
||||
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/endpoints/v2 v2.7.21 h1:PEgGVtPoB6NTpPrBgqSE5hE/o47Ij9qk/SEZFbUOe9A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21/go.mod h1:p+hz+PRAYlY3zcpJhPwXlLC4C+kqn70WIHwnzAfs6ps=
|
||||
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.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/bedrock v1.59.0 h1:iddGKq5eJ8ABEDE543752cUuI0mSAJOBDuoC1RcS+bQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrock v1.59.0/go.mod h1:C8KxG3IPteAlDQUCBVIXfkn81kDh9iIBZ4SS8B5UbDE=
|
||||
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/bedrockruntime v1.50.4 h1:W6tKfa/s37faUnwJ71pGqsBO7/wfUX1L7tVprupQGo4=
|
||||
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.50.4/go.mod h1:BZ+9thH0QOTDUwE8KAv/ZwUzsNC7CSMJXj/wtnZMs5k=
|
||||
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.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/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA=
|
||||
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/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg=
|
||||
github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI=
|
||||
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/sso v1.30.15 h1:lFd1+ZSEYJZYvv9d6kXzhkZu07si3f+GQ1AaYwa2LUM=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.30.15/go.mod h1:WSvS1NLr7JaPunCXqpJnWk1Bjo7IxzZXrZi1QQCkuqM=
|
||||
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/ssooidc v1.35.19 h1:dzztQ1YmfPrxdrOiuZRMF6fuOwWlWpD2StNLTceKpys=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.19/go.mod h1:YO8TrYtFdl5w/4vmjL8zaBSsiNp3w0L1FfKVKenZT7w=
|
||||
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/aws-sdk-go-v2/service/sts v1.41.10 h1:p8ogvvLugcR/zLBXTXrTkj0RYBUdErbMnAFFp12Lm/U=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.41.10/go.mod h1:60dv0eZJfeVXfbT1tFJinbHrDfSJ2GZl4Q//OSSNAVw=
|
||||
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/smithy-go v1.24.3 h1:XgOAaUgx+HhVBoP4v8n6HCQoTRDhoMghKqw4LNHsDNg=
|
||||
github.com/aws/smithy-go v1.24.3/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.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/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.4 h1:oZnQwnX82KAIWb7033bEwtxvTqXcYMxDBaQxo5JJHWM=
|
||||
github.com/bytedance/gopkg v0.1.4/go.mod h1:v1zWfPm21Fb+OsyXN2VAHdL6TBb2L88anLQgdyje6R4=
|
||||
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 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/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.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/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/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
|
||||
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/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
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=
|
||||
|
|
@ -159,16 +149,18 @@ github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVo
|
|||
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.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/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/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-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=
|
||||
|
|
@ -177,75 +169,56 @@ 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.9.1 h1:8U73XiOTfINdItHVa6z4Gv7ToObcZ6grkqQbLryLCdA=
|
||||
github.com/go-git/go-billy/v5 v5.9.1/go.mod h1:ExsU+jcGwXTBOnyilvAnEM1wug1IxHr4yP2ZXsNRtV0=
|
||||
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-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.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-git/go-git/v5 v5.17.1 h1:WnljyxIzSj9BRRUlnmAU35ohDsjRK0EKmL0evDqi5Jk=
|
||||
github.com/go-git/go-git/v5 v5.17.1/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
|
||||
github.com/go-git/go-git/v5 v5.17.2 h1:B+nkdlxdYrvyFK4GPXVU8w1U+YkbsgciIR7f2sZJ104=
|
||||
github.com/go-git/go-git/v5 v5.17.2/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
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/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
|
||||
github.com/go-logr/logr v1.4.3/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 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/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.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.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=
|
||||
github.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
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-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.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.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.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=
|
||||
github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=
|
||||
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.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.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.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.4.0 h1:7SgOMTvJkM8yWrQlU8Jm18VeDPuAvB/xWrdxFJkoFag=
|
||||
github.com/go-openapi/testify/enable/yaml/v2 v2.4.0/go.mod h1:14iV8jyyQlinc9StD7w1xVPW3CO3q1Gj04Jy//Kw4VM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0 h1:8nsPrHVCWkQ4p8h1EsRVymA2XABB4OT40gcvAu+voFM=
|
||||
github.com/go-openapi/testify/v2 v2.4.0/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=
|
||||
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.3 h1:4MU6YkEwx7GbcPJOZxrtbu+QfF3pJLJuaYTeAH0DYy8=
|
||||
github.com/go-playground/validator/v10 v10.30.3/go.mod h1:4Axh7oCNGcoGkqLoE4YWt6n20mcEIsPRlB7vPk3lpyc=
|
||||
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.2 h1:JiFIMtSSHb2/XBUbWM4i/MpeQm9ZK2xqPNk8vgvu5JQ=
|
||||
github.com/go-playground/validator/v10 v10.30.2/go.mod h1:mAf2pIOVXjTEBrwUMGKkCWKKPs9NheYGabeB04txQSc=
|
||||
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=
|
||||
|
|
@ -274,22 +247,18 @@ 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.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/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.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
|
||||
github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
|
||||
github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI=
|
||||
github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4=
|
||||
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.16.0 h1:DQLfp+djj4j5NPdJkGYym8J55hpm5etML1zqgco78Qc=
|
||||
github.com/hasura/go-graphql-client v0.16.0/go.mod h1:z/sO2T0zI+HnPNIevQcs+7xA6/gDOc8hgHMrNBzfL2c=
|
||||
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/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=
|
||||
|
|
@ -304,8 +273,8 @@ github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRg
|
|||
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.4.0 h1:S6Hrbc7+ywsr0r+RLapfGBHfyefhCTwEh3A0tV913Dw=
|
||||
github.com/klauspost/cpuid/v2 v2.4.0/go.mod h1:19jmZ9mjzoF//ddRSUsv0zfBTJWh3QJh9FNxZTMrGxU=
|
||||
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/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=
|
||||
|
|
@ -315,17 +284,19 @@ 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.5.0 h1:pLqT2kq1zpHW/1D18QMjMpdtX7cekxqtJJjg5ANyWw0=
|
||||
github.com/leodido/go-urn v1.5.0/go.mod h1:9BORnCDhdPBJNDEX+w1bJisa8yOKYi116VeO96s4ifE=
|
||||
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.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.24 h1:tGZZoVgT/KiqK1c8ocVLeDS8BSWMRd47J3Lbz7vsReI=
|
||||
github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI3HqDio6A=
|
||||
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.21 h1:xYae+lCNBP7QuW4PUnNG61ffM4hVIfm+zUzDuSzYLGs=
|
||||
github.com/mattn/go-isatty v0.0.21/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
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/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.42 h1:MigqEP4ZmHw3aIdIT7T+9TLa90Z6smwcthx+Azv4Cgo=
|
||||
github.com/mattn/go-sqlite3 v1.14.42/go.mod h1:pjEuOr8IwzLJP2MfGeTb0A35jauH+C2kbHKBr7yXKVQ=
|
||||
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=
|
||||
|
|
@ -333,10 +304,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.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/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.20.4 h1:XXquZkzAptOoAzNHAyKQOhiShoDFMfn3Yp56C7Vfsjs=
|
||||
github.com/ollama/ollama v0.20.4/go.mod h1:tCX4IMV8DHjl3zY0THxuEkpWDZSOchJpzTuLACpMwFw=
|
||||
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=
|
||||
|
|
@ -345,12 +316,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/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/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/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
|
||||
github.com/pelletier/go-toml/v2 v2.3.0/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/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=
|
||||
|
|
@ -360,12 +331,10 @@ 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.61.0 h1:ui88A53s8MSVYLC56en0KQ17HARk+9986Dn0SBfKNvA=
|
||||
github.com/quic-go/quic-go v0.61.0/go.mod h1:9So2anK4Tp22URSQq00k+Vo2PNkle96ycDPDHL4s9vs=
|
||||
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/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=
|
||||
|
|
@ -375,8 +344,8 @@ github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRo
|
|||
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.16.1 h1://Xa7P0F/eOIcJ/6SehTqgr2MRnzPfI8LdAwlAYTnTs=
|
||||
github.com/sgaunet/perplexity-go/v2 v2.16.1/go.mod h1:5dckcaxoFKtJEfNRc9+OhL6p/W/TKxd0gbSzc1YkmkI=
|
||||
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/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=
|
||||
|
|
@ -385,8 +354,6 @@ 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=
|
||||
|
|
@ -401,8 +368,6 @@ 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 +375,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.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU=
|
||||
github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc=
|
||||
github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
|
||||
github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
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,86 +389,95 @@ 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.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.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.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.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.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/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
|
||||
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/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
|
||||
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 v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
|
||||
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
|
||||
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/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
|
||||
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
|
||||
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/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
|
||||
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
|
||||
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=
|
||||
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/arch v0.25.0 h1:qnk6Ksugpi5Bz32947rkUgDt9/s5qvqDPl/gBKdMJLE=
|
||||
golang.org/x/arch v0.25.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.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/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.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
|
||||
golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
|
||||
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/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
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/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.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
|
||||
golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
|
||||
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.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/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.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
|
||||
golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
|
||||
golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
|
||||
golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
|
||||
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.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
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.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.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=
|
||||
|
|
@ -515,60 +489,80 @@ 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.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
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.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
|
||||
golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
|
||||
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.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
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.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=
|
||||
golang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=
|
||||
golang.org/x/term v0.42.0 h1:UiKe+zDFmJobeJ5ggPwOshJIVt6/Ft0rcfrXZDLWAWY=
|
||||
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.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/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.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
|
||||
golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
|
||||
golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
|
||||
golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
|
||||
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.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/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.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
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=
|
||||
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/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
|
||||
google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
|
||||
google.golang.org/api v0.275.0 h1:vfY5d9vFVJeWEZT65QDd9hbndr7FyZ2+6mIzGAh71NI=
|
||||
google.golang.org/api v0.275.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw=
|
||||
google.golang.org/genai v1.51.0 h1:IZGuUqgfx40INv3hLFGCbOSGp0qFqm7LVmDghzNIYqg=
|
||||
google.golang.org/genai v1.51.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||
google.golang.org/genai v1.53.0 h1:8tR9MuO/TdaXSc8PEFamohQKxRz5M/qctbyzhV2YwMM=
|
||||
google.golang.org/genai v1.53.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
|
||||
google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
|
||||
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/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
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/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/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=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
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=
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ 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"`
|
||||
|
|
@ -57,9 +56,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"`
|
||||
YouTubeVisual bool `long:"visual"`
|
||||
YouTubeVisualSensitivity float64 `long:"visual-sensitivity" default:"0.4"`
|
||||
YouTubeVisualFps int `long:"visual-fps" 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')"`
|
||||
|
|
@ -78,8 +77,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:"127.0.0.1:8080"`
|
||||
ServeAPIKey string `long:"api-key" env:"FABRIC_API_KEY" description:"API key used to secure server routes" default:""`
|
||||
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:""`
|
||||
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"`
|
||||
|
|
@ -89,7 +88,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, Grok)"`
|
||||
Search bool `long:"search" description:"Enable web search tool for supported models (Anthropic, OpenAI, Gemini)"`
|
||||
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)"`
|
||||
|
|
@ -109,7 +108,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 (input/output tokens) to stderr"`
|
||||
ShowMetadata bool `long:"show-metadata" description:"Print metadata to stderr"`
|
||||
Debug int `long:"debug" description:"Set debug level (0=off, 1=basic, 2=detailed, 3=trace, 4=wire)" default:"0"`
|
||||
}
|
||||
|
||||
|
|
@ -123,7 +122,8 @@ 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 field := range t.Fields() {
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
yamlTag := field.Tag.Get("yaml")
|
||||
if yamlTag != "" {
|
||||
longTag := field.Tag.Get("long")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ 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",
|
||||
|
|
@ -50,7 +49,6 @@ var flagDescriptionMap = map[string]string{
|
|||
"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",
|
||||
|
|
@ -96,7 +94,6 @@ 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",
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +142,8 @@ func (h *TranslatedHelpWriter) getTranslatedDescription(flagName string) string
|
|||
func (h *TranslatedHelpWriter) getOriginalDescription(flagName string) string {
|
||||
flagsType := reflect.TypeFor[Flags]()
|
||||
|
||||
for field := range flagsType.Fields() {
|
||||
for i := 0; i < flagsType.NumField(); i++ {
|
||||
field := flagsType.Field(i)
|
||||
longTag := field.Tag.Get("long")
|
||||
|
||||
if longTag == flagName {
|
||||
|
|
@ -224,7 +222,9 @@ func (h *TranslatedHelpWriter) writeAllFlags() {
|
|||
// Use direct reflection on the Flags struct to get all flag definitions
|
||||
flagsType := reflect.TypeFor[Flags]()
|
||||
|
||||
for field := range flagsType.Fields() {
|
||||
for i := 0; i < flagsType.NumField(); i++ {
|
||||
field := flagsType.Field(i)
|
||||
|
||||
shortTag := field.Tag.Get("short")
|
||||
longTag := field.Tag.Get("long")
|
||||
defaultTag := field.Tag.Get("default")
|
||||
|
|
|
|||
|
|
@ -29,11 +29,6 @@ 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,10 +5,6 @@ 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) {
|
||||
|
|
@ -26,7 +22,7 @@ func handleSetupAndServerCommands(currentFlags *Flags, registry *core.PluginRegi
|
|||
|
||||
if currentFlags.ServeOllama {
|
||||
registry.ConfigureVendors()
|
||||
err = serveOllama(registry, currentFlags.ServeAddress, version, currentFlags.ServeAPIKey)
|
||||
err = restapi.ServeOllama(registry, currentFlags.ServeAddress, version)
|
||||
return true, err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,37 +0,0 @@
|
|||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -10,7 +10,6 @@ import (
|
|||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
debuglog "github.com/danielmiessler/fabric/internal/log"
|
||||
|
|
@ -74,24 +73,6 @@ 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(),
|
||||
|
|
@ -105,7 +86,7 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
|
|||
lmstudio.NewClient(),
|
||||
exolab.NewClient(),
|
||||
perplexity.NewClient(),
|
||||
codexClient,
|
||||
codex.NewClient(),
|
||||
copilot.NewClient(), // Microsoft 365 Copilot
|
||||
bedrock.NewClient(), // AWS Bedrock - credentials configured via setup or AWS credential chain
|
||||
)
|
||||
|
|
@ -142,8 +123,6 @@ 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
|
||||
|
|
@ -309,12 +288,16 @@ 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
|
||||
}
|
||||
|
||||
|
|
@ -366,11 +349,16 @@ 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
|
||||
}
|
||||
|
|
@ -437,32 +425,11 @@ 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() {
|
||||
|
|
@ -471,34 +438,6 @@ 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()
|
||||
|
|
@ -564,25 +503,13 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
|
|||
ret.model = defaultModel
|
||||
}
|
||||
} else if model == "" {
|
||||
name := vendorName
|
||||
if name == "" {
|
||||
name = defaultVendor
|
||||
}
|
||||
if ret.vendor, err = o.activateVendor(name); err != nil {
|
||||
return
|
||||
if vendorName != "" {
|
||||
ret.vendor = vendorManager.FindByName(vendorName)
|
||||
} else {
|
||||
ret.vendor = vendorManager.FindByName(defaultVendor)
|
||||
}
|
||||
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
|
||||
|
|
@ -612,44 +539,16 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
|
|||
return
|
||||
}
|
||||
} else {
|
||||
// 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))
|
||||
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.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,7 +3,6 @@ package core
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
|
@ -11,14 +10,11 @@ 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) {
|
||||
|
|
@ -36,15 +32,14 @@ func TestSaveEnvFile(t *testing.T) {
|
|||
|
||||
// testVendor implements ai.Vendor for testing purposes
|
||||
type testVendor struct {
|
||||
name string
|
||||
models []string
|
||||
configureErr error
|
||||
name string
|
||||
models []string
|
||||
}
|
||||
|
||||
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) Configure() error { return nil }
|
||||
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 }
|
||||
|
|
@ -155,176 +150,3 @@ 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,7 +218,8 @@ func getLocaleCandidates(locale string) []string {
|
|||
candidates = append(candidates, locale)
|
||||
|
||||
// If it's a regional variant, add the base language as a candidate
|
||||
if baseLang, _, found := strings.Cut(locale, "-"); found {
|
||||
if strings.Contains(locale, "-") {
|
||||
baseLang := strings.Split(locale, "-")[0]
|
||||
candidates = append(candidates, baseLang)
|
||||
|
||||
// Also check if the base language has a default variant
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ 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 nicht initialisiert — führen Sie 'fabric --setup' zur Konfiguration aus",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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-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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Web-Such-Tool für unterstützte Modelle aktivieren (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -110,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Enable web search tool for supported models (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
|
|||
|
|
@ -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": "cliente de Bedrock no inicializado — ejecute 'fabric --setup' para configurar",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Habilitar herramienta de búsqueda web para modelos soportados (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -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 مقداردهی نشده است — 'fabric --setup' را برای پیکربندی اجرا کنید",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] کلید 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_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_header": "[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_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_unable_load_aws_config": "بارگذاری پیکربندی AWS ناموفق بود: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "بارگذاری پیکربندی AWS با منطقه %s ناموفق بود: %w",
|
||||
"bedrock_unexpected_content_block_type": "نوع بلوک محتوای غیرمنتظره: %T",
|
||||
|
|
@ -110,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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": "دستور با موفقیت تکمیل شد",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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": "ارائهدهنده و مدل هوش مصنوعی پیشفرض",
|
||||
|
|
@ -184,7 +178,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، Grok)",
|
||||
"enable_web_search_tool": "فعالسازی ابزار جستجوی وب برای مدلهای پشتیبانی شده (Anthropic، OpenAI، Gemini)",
|
||||
"end_tag_thinking_sections": "تگ پایان برای بخشهای تفکر",
|
||||
"error_creating_audio_file": "خطا در ایجاد فایل صوتی: %v",
|
||||
"error_creating_file": "خطا در ایجاد فایل: %v",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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 باید یک عدد باشد، نوع نامعتبر دریافت شد",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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": "حذف افزونه ثبت شده با نام",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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": "تنظیم جریمه فرکانس",
|
||||
|
|
@ -577,7 +563,7 @@
|
|||
"spotify_description_header": "## توضیحات",
|
||||
"spotify_duration_label": "**مدت زمان**: %d دقیقه",
|
||||
"spotify_episode_header": "# اپیزود اسپاتیفای",
|
||||
"spotify_error_getting_metadata": "خطا در دریافت فراداده Spotify: %v",
|
||||
"spotify_error_getting_metadata": "error getting Spotify metadata: %v",
|
||||
"spotify_explicit_label": "**محتوای صریح**: %v",
|
||||
"spotify_failed_create_request": "ایجاد درخواست ناموفق بود: %w",
|
||||
"spotify_failed_create_token_request": "ایجاد درخواست توکن ناموفق بود: %w",
|
||||
|
|
@ -591,14 +577,14 @@
|
|||
"spotify_failed_parse_show_metadata": "تجزیه فراداده برنامه ناموفق بود: %w",
|
||||
"spotify_failed_read_response_body": "خواندن متن پاسخ ناموفق بود: %w",
|
||||
"spotify_failed_request_access_token": "درخواست توکن دسترسی ناموفق بود: %w",
|
||||
"spotify_invalid_url": "نشانی Spotify نامعتبر است، دریافت شناسه برنامه یا قسمت ممکن نیست: '%s'",
|
||||
"spotify_invalid_url": "invalid Spotify URL, can't get show or episode ID: '%s'",
|
||||
"spotify_label": "Spotify",
|
||||
"spotify_language_field_label": "**زبان**: %s",
|
||||
"spotify_languages_label": "**زبانها**: %s",
|
||||
"spotify_media_type_label": "**نوع رسانه**: %s",
|
||||
"spotify_no_episode_found": "هیچ قسمتی با این شناسه یافت نشد: %s",
|
||||
"spotify_no_show_found": "هیچ برنامهای با این شناسه یافت نشد: %s",
|
||||
"spotify_not_configured": "Spotify پیکربندی نشده است، لطفاً روند راهاندازی را اجرا کنید",
|
||||
"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_publisher_label": "**ناشر**: %s",
|
||||
"spotify_release_date_label": "**تاریخ انتشار**: %s",
|
||||
"spotify_search_description_label": "- **توضیحات**: %s",
|
||||
|
|
@ -607,12 +593,11 @@
|
|||
"spotify_search_publisher_label": "- **ناشر**: %s",
|
||||
"spotify_search_results_header": "# نتایج جستجوی اسپاتیفای",
|
||||
"spotify_search_url_label": "- **URL**: %s",
|
||||
"spotify_setup_description": "Spotify - برای دریافت فراداده پادکست/برنامه از Spotify",
|
||||
"spotify_setup_description": "Spotify - to grab podcast/show metadata from 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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,11 @@
|
|||
"strategy_path_traversal": "نام راهبرد %q خارج از دایرکتوری راهبردها حل میشود",
|
||||
"stream_help": "پخش زنده",
|
||||
"suppress_thinking_tags": "سرکوب متن محصور در تگهای تفکر",
|
||||
"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_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_extension_error": "خطای افزونه %s: %v",
|
||||
"template_file_error_expand_home_dir": "فایل: گسترش پوشه خانگی ممکن نشد: %v",
|
||||
"template_file_error_invalid_line_count": "فایل: تعداد خط نامعتبر %q",
|
||||
|
|
@ -676,12 +660,12 @@
|
|||
"template_missing_required_variable": "متغیر الزامی موجود نیست: %s",
|
||||
"template_plugin_error": "خطای پلاگین %s: %v",
|
||||
"template_processing_stuck": "پردازش قالب متوقف شده - احتمال حلقه بینهایت",
|
||||
"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_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_text_empty_input": "متن: ورودی خالی برای عملیات %q",
|
||||
"template_text_unknown_operation": "متن: عملیات متنی ناشناخته %q (پشتیبانی شده: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "فضای نام پلاگین ناشناخته: %s",
|
||||
|
|
|
|||
|
|
@ -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": "client Bedrock non initialisé — exécutez 'fabric --setup' pour configurer",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Activer l'outil de recherche web pour les modèles pris en charge (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -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": "client Bedrock non inizializzato — eseguire 'fabric --setup' per configurare",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Abilita strumento di ricerca web per modelli supportati (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -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クライアントが初期化されていません — 設定するには 'fabric --setup' を実行してください",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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キー / 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_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_header": "[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_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_unable_load_aws_config": "AWS設定を読み込めませんでした: %w",
|
||||
"bedrock_unable_load_aws_config_with_region": "リージョン %s でAWS設定を読み込めませんでした: %w",
|
||||
"bedrock_unexpected_content_block_type": "予期しないコンテンツブロックタイプ: %T",
|
||||
|
|
@ -110,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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": "コマンドが正常に完了しました",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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プロバイダーとモデル",
|
||||
|
|
@ -184,7 +178,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、Grok)でウェブ検索ツールを有効化",
|
||||
"enable_web_search_tool": "サポートされているモデル(Anthropic、OpenAI、Gemini)でウェブ検索ツールを有効化",
|
||||
"end_tag_thinking_sections": "思考セクションの終了タグ",
|
||||
"error_creating_audio_file": "音声ファイルの作成エラー: %v",
|
||||
"error_creating_file": "ファイルの作成エラー: %v",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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 は数値である必要があります。無効な型を受け取りました",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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": "名前で登録済み拡張機能を削除",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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": "頻度ペナルティを設定",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,11 @@
|
|||
"strategy_path_traversal": "戦略名 %q が戦略ディレクトリの外部に解決されます",
|
||||
"stream_help": "ストリーミング",
|
||||
"suppress_thinking_tags": "思考タグで囲まれたテキストを抑制",
|
||||
"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_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_extension_error": "拡張機能%sエラー: %v",
|
||||
"template_file_error_expand_home_dir": "file: ホームディレクトリを展開できませんでした: %v",
|
||||
"template_file_error_invalid_line_count": "file: 無効な行数 %q",
|
||||
|
|
@ -664,7 +648,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": "ファイル: operation=%q value=%q",
|
||||
"template_file_log_operation_value": "File: 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 行を読み取り中",
|
||||
|
|
@ -676,12 +660,12 @@
|
|||
"template_missing_required_variable": "必須変数が不足しています: %s",
|
||||
"template_plugin_error": "プラグイン%sエラー: %v",
|
||||
"template_processing_stuck": "テンプレート処理が停止 - 無限ループの可能性",
|
||||
"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_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_text_empty_input": "テキスト: 操作%qに対する入力が空です",
|
||||
"template_text_unknown_operation": "テキスト: 不明なテキスト操作%q (対応: upper, lower, title, trim)",
|
||||
"template_unknown_plugin_namespace": "不明なプラグイン名前空間: %s",
|
||||
|
|
|
|||
|
|
@ -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": "klient Bedrock nie jest zainicjalizowany — uruchom 'fabric --setup', aby skonfigurować",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Włącz narzędzie wyszukiwania internetowego dla obsługiwanych modeli (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -664,7 +648,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": "Plik: operation=%q value=%q",
|
||||
"template_file_log_operation_value": "File: 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",
|
||||
|
|
|
|||
|
|
@ -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": "cliente Bedrock não inicializado — execute 'fabric --setup' para configurar",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de busca web para modelos suportados (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -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": "cliente Bedrock não inicializado — execute 'fabric --setup' para configurar",
|
||||
"bedrock_client_not_initialized": "Bedrock client not initialized — run 'fabric --setup' to configure",
|
||||
"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] 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_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_header": "[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_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_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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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",
|
||||
|
|
@ -184,7 +178,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, Grok)",
|
||||
"enable_web_search_tool": "Habilitar ferramenta de pesquisa web para modelos suportados (Anthropic, OpenAI, Gemini)",
|
||||
"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",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
@ -643,11 +627,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": "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_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_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",
|
||||
|
|
@ -676,12 +660,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: 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_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_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",
|
||||
|
|
|
|||
|
|
@ -1,770 +0,0 @@
|
|||
{
|
||||
"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,9 +110,7 @@
|
|||
"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",
|
||||
|
|
@ -135,16 +133,13 @@
|
|||
"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": "命令执行成功",
|
||||
|
|
@ -173,7 +168,6 @@
|
|||
"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 提供商和模型",
|
||||
|
|
@ -184,7 +178,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、Grok)",
|
||||
"enable_web_search_tool": "为支持的模型启用网络搜索工具(Anthropic、OpenAI、Gemini)",
|
||||
"end_tag_thinking_sections": "思考部分的结束标签",
|
||||
"error_creating_audio_file": "创建音频文件时出错:%v",
|
||||
"error_creating_file": "创建文件时出错:%v",
|
||||
|
|
@ -375,7 +369,6 @@
|
|||
"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 必须是数字,收到无效类型",
|
||||
|
|
@ -389,7 +382,6 @@
|
|||
"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",
|
||||
|
|
@ -410,7 +402,6 @@
|
|||
"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)",
|
||||
|
|
@ -426,7 +417,6 @@
|
|||
"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",
|
||||
|
|
@ -500,8 +490,6 @@
|
|||
"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": "按名称删除已注册的扩展",
|
||||
|
|
@ -515,12 +503,10 @@
|
|||
"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": "设置频率惩罚",
|
||||
|
|
@ -612,7 +598,6 @@
|
|||
"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",
|
||||
|
|
@ -624,7 +609,6 @@
|
|||
"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",
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import (
|
|||
neturl "net/url"
|
||||
"os"
|
||||
"path"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -25,22 +24,6 @@ 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{}
|
||||
|
|
@ -55,47 +38,43 @@ 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{
|
||||
// Claude 5 family
|
||||
string(anthropic.ModelClaudeFable5): {context1M},
|
||||
string(anthropic.ModelClaudeOpus5): {context1M},
|
||||
string(anthropic.ModelClaudeSonnet5): {context1M},
|
||||
// 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 Opus 4.x (1M-capable)
|
||||
string(anthropic.ModelClaudeOpus4_8): {context1M},
|
||||
string(anthropic.ModelClaudeOpus4_7): {context1M},
|
||||
string(anthropic.ModelClaudeOpus4_6): {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 Sonnet 4.x (1M-capable)
|
||||
string(anthropic.ModelClaudeSonnet4_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"},
|
||||
}
|
||||
|
||||
return
|
||||
|
|
@ -235,21 +214,15 @@ 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(maxTokens),
|
||||
MaxTokens: int64(an.maxTokens),
|
||||
Messages: msgs,
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// 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 {
|
||||
// User explicitly set TopP, so use that instead of temperature
|
||||
params.TopP = anthropic.Opt(opts.TopP)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -99,43 +99,6 @@ 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{
|
||||
|
|
@ -207,32 +170,9 @@ 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.ModelClaudeSonnet5)
|
||||
model := string(anthropic.ModelClaudeSonnet4_20250514)
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -24,45 +24,6 @@ type authTransport struct {
|
|||
}
|
||||
|
||||
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()
|
||||
|
||||
|
|
@ -100,13 +61,7 @@ func (c *Client) ensureAccessTokenLocked(ctx context.Context, forceRefresh bool)
|
|||
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 != "")
|
||||
debuglog.Debug(debuglog.Detailed, "Codex access token refreshed for account=%s\n", refreshedAccountID)
|
||||
|
||||
return c.AccessToken.Value, c.AccountID.Value, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,10 +58,6 @@ type Client struct {
|
|||
apiHTTPClient *http.Client
|
||||
|
||||
tokenMu sync.Mutex
|
||||
// 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 {
|
||||
|
|
@ -85,11 +81,11 @@ func NewClient() *Client {
|
|||
client.AccountID = client.AddSetting("Account ID", true)
|
||||
|
||||
client.ApiBaseURL = client.AddSetupQuestionWithEnvName("Base URL", false,
|
||||
i18n.T("codex_api_base_url_question"))
|
||||
"Enter your Codex API base URL")
|
||||
client.ApiBaseURL.Value = defaultBaseURL
|
||||
|
||||
client.AuthBaseURL = client.AddSetupQuestionWithEnvName("Auth Base URL", false,
|
||||
i18n.T("codex_auth_base_url_question"))
|
||||
"Enter your Codex OAuth base URL")
|
||||
client.AuthBaseURL.Value = defaultAuthBaseURL
|
||||
|
||||
client.authHTTPClient = &http.Client{Timeout: modelsRequestTimeout}
|
||||
|
|
@ -105,14 +101,6 @@ 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()
|
||||
|
||||
|
|
@ -165,26 +153,11 @@ func (c *Client) configure() error {
|
|||
option.WithHTTPClient(c.apiHTTPClient),
|
||||
)
|
||||
c.ApiClient = &apiClient
|
||||
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account_present=%t base_url=%s\n", strings.TrimSpace(c.AccountID.Value) != "", c.ApiBaseURL.Value)
|
||||
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account=%s base_url=%s\n", 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(ctx context.Context) ([]string, error) {
|
||||
if c.apiHTTPClient == nil {
|
||||
|
|
@ -269,14 +242,11 @@ 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 {
|
||||
if extractedText := c.ExtractText(completedResp); strings.TrimSpace(extractedText) != "" {
|
||||
return extractedText, nil
|
||||
}
|
||||
return c.ExtractText(completedResp), nil
|
||||
}
|
||||
|
||||
return streamedText, nil
|
||||
return builder.String(), nil
|
||||
}
|
||||
|
||||
// SendStream sends a request to Codex and streams the response text updates.
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ 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"
|
||||
)
|
||||
|
|
@ -212,10 +211,6 @@ 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 {
|
||||
|
|
@ -226,9 +221,8 @@ func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
|
|||
if err == nil {
|
||||
t.Fatal("mapRequestError() returned nil")
|
||||
}
|
||||
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 got := err.Error(); got != "codex request failed with status 400" {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, "codex request failed with status 400")
|
||||
}
|
||||
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)
|
||||
|
|
@ -236,10 +230,6 @@ func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
@ -252,9 +242,8 @@ func TestMapRequestErrorReadsAPIErrorResponseBodyWhenRawJSONMissing(t *testing.T
|
|||
if err == nil {
|
||||
t.Fatal("mapRequestError() returned nil")
|
||||
}
|
||||
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 got := err.Error(); got != "codex request failed with status 400" {
|
||||
t.Fatalf("mapRequestError() = %q, want %q", got, "codex request failed with status 400")
|
||||
}
|
||||
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)
|
||||
|
|
@ -440,61 +429,6 @@ 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" {
|
||||
|
|
@ -586,8 +520,8 @@ func TestSendStreamClosesChannelAndMapsHTTPError(t *testing.T) {
|
|||
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"))
|
||||
if got := err.Error(); got != "codex usage limit reached" {
|
||||
t.Fatalf("SendStream() error = %q, want %q", got, "codex usage limit reached")
|
||||
}
|
||||
|
||||
update, ok := <-updates
|
||||
|
|
@ -596,159 +530,6 @@ func TestSendStreamClosesChannelAndMapsHTTPError(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ func (c *Client) mapRequestError(err error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
if apiErr, ok := errors.AsType[*openaiapi.Error](err); ok {
|
||||
var apiErr *openaiapi.Error
|
||||
if errors.As(err, &apiErr) {
|
||||
body := []byte(apiErr.RawJSON())
|
||||
if len(body) == 0 {
|
||||
body = readAPIErrorBody(apiErr)
|
||||
|
|
@ -77,7 +78,7 @@ func (c *Client) mapRequestError(err error) error {
|
|||
case isUsageLimitMessage(message):
|
||||
return &publicError{
|
||||
message: i18n.T("codex_usage_limit_reached"),
|
||||
cause: fmt.Errorf(i18n.T("codex_request_failed"), err),
|
||||
cause: fmt.Errorf("codex request failed: %w", err),
|
||||
}
|
||||
default:
|
||||
return err
|
||||
|
|
@ -91,7 +92,7 @@ func wrapPublicError(message string, statusCode int, providerMessage string) err
|
|||
|
||||
return &publicError{
|
||||
message: message,
|
||||
cause: fmt.Errorf(i18n.T("codex_provider_error"), statusCode, providerMessage),
|
||||
cause: fmt.Errorf("codex provider error (status %d): %s", statusCode, providerMessage),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -106,7 +106,6 @@ func (o *Client) ListModels(_ context.Context) (ret []string, 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 {
|
||||
|
|
@ -136,6 +135,7 @@ func (o *Client) SendStream(_ context.Context, msgs []*chat.ChatCompletionMessag
|
|||
return
|
||||
}
|
||||
|
||||
close(channel)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,14 +6,10 @@ 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"
|
||||
)
|
||||
|
|
@ -64,28 +60,3 @@ 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")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
|
|
@ -46,13 +45,6 @@ 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
|
||||
|
|
@ -61,14 +53,6 @@ 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 {
|
||||
|
|
@ -78,40 +62,17 @@ 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)
|
||||
|
|
@ -136,16 +97,12 @@ 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))
|
||||
ids := extractModelIDs(openAIFormat.Data)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
return extractModelIDs(openAIFormat.Data), 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))
|
||||
ids := extractModelIDs(directArray)
|
||||
_ = writeModelsCache(providerName, fullURL, ids)
|
||||
return ids, nil
|
||||
return extractModelIDs(directArray), nil
|
||||
}
|
||||
|
||||
var truncatedBody string
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
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)
|
||||
}
|
||||
|
|
@ -1,165 +0,0 @@
|
|||
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,15 +66,6 @@ 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
|
||||
|
|
@ -82,21 +73,6 @@ 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) {
|
||||
|
|
@ -223,7 +199,6 @@ func (o *Client) NeedsRawMode(modelName string) bool {
|
|||
openaiModelsPrefixes := []string{
|
||||
"glm",
|
||||
"gpt-5",
|
||||
"gpt-6",
|
||||
"o1",
|
||||
"o3",
|
||||
"o4",
|
||||
|
|
@ -278,18 +253,11 @@ func (o *Client) buildResponseParams(
|
|||
// Add tools if enabled
|
||||
var tools []responses.ToolUnionParam
|
||||
|
||||
// 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").
|
||||
// Add web search tool if enabled
|
||||
if opts.Search {
|
||||
searchToolName := responses.WebSearchToolType("web_search_preview")
|
||||
if o.webSearchToolName != "" {
|
||||
searchToolName = responses.WebSearchToolType(o.webSearchToolName)
|
||||
}
|
||||
webSearchTool := responses.ToolParamOfWebSearchPreview(searchToolName)
|
||||
webSearchTool := responses.ToolParamOfWebSearchPreview("web_search_preview")
|
||||
|
||||
// Add user location if provided. Only attach when the caller
|
||||
// asked for it; xAI rejects unexpected location payloads.
|
||||
// Add user location if provided
|
||||
if opts.SearchLocation != "" {
|
||||
webSearchTool.OfWebSearchPreview.UserLocation = responses.WebSearchToolUserLocationParam{
|
||||
Type: "approximate",
|
||||
|
|
@ -298,20 +266,6 @@ 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,10 +2,8 @@ package openai
|
|||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
|
@ -14,7 +12,6 @@ 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")
|
||||
|
|
@ -31,7 +28,6 @@ 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")
|
||||
|
|
@ -46,54 +42,8 @@ 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,12 +12,6 @@ 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
|
||||
|
|
@ -135,88 +129,6 @@ 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,14 +21,6 @@ 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
|
||||
|
|
@ -48,10 +40,6 @@ 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
|
||||
}
|
||||
|
||||
|
|
@ -206,9 +194,14 @@ 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)
|
||||
|
|
@ -247,11 +240,6 @@ 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",
|
||||
|
|
@ -289,21 +277,11 @@ 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",
|
||||
|
|
|
|||
|
|
@ -1,15 +1,10 @@
|
|||
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,7 +5,6 @@ import (
|
|||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
|
|
@ -42,8 +41,6 @@ type Db struct {
|
|||
Contexts *ContextsEntity
|
||||
|
||||
EnvFilePath string
|
||||
|
||||
envMu sync.Mutex
|
||||
}
|
||||
|
||||
func (o *Db) Configure() (err error) {
|
||||
|
|
@ -95,100 +92,9 @@ func (o *Db) IsEnvFileExists() (ret bool) {
|
|||
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) SaveEnv(content string) (err error) {
|
||||
err = os.WriteFile(o.EnvFilePath, []byte(content), 0644)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *Db) FilePath(fileName string) (ret string) {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,7 @@ package fsdb
|
|||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func TestDb_Configure(t *testing.T) {
|
||||
|
|
@ -57,150 +52,4 @@ 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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
//go:build unix
|
||||
|
||||
package fsdb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func lockExclusive(f *os.File) error {
|
||||
err := unix.Flock(int(f.Fd()), unix.LOCK_EX)
|
||||
runtime.KeepAlive(f)
|
||||
return err
|
||||
}
|
||||
|
||||
func unlockExclusive(f *os.File) error {
|
||||
err := unix.Flock(int(f.Fd()), unix.LOCK_UN)
|
||||
runtime.KeepAlive(f)
|
||||
return err
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
//go:build windows
|
||||
|
||||
package fsdb
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func lockExclusive(f *os.File) error {
|
||||
var overlapped windows.Overlapped
|
||||
return windows.LockFileEx(windows.Handle(f.Fd()), windows.LOCKFILE_EXCLUSIVE_LOCK, 0, 1, 0, &overlapped)
|
||||
}
|
||||
|
||||
func unlockExclusive(f *os.File) error {
|
||||
var overlapped windows.Overlapped
|
||||
return windows.UnlockFileEx(windows.Handle(f.Fd()), 0, 1, 0, &overlapped)
|
||||
}
|
||||
|
|
@ -55,18 +55,14 @@ func (o *PatternsEntity) GetRaw(name string) (*Pattern, error) {
|
|||
return o.getFromDB(name)
|
||||
}
|
||||
|
||||
// LooksLikePatternFilePath reports whether loadPattern uses source as a
|
||||
// filesystem path. HTTP handlers must reject these names to keep the
|
||||
// CLI file-path feature out of the REST API.
|
||||
func LooksLikePatternFilePath(source string) bool {
|
||||
return strings.HasPrefix(source, "\\") ||
|
||||
func (o *PatternsEntity) loadPattern(source string) (pattern *Pattern, err error) {
|
||||
// Determine if this is a file path
|
||||
isFilePath := strings.HasPrefix(source, "\\") ||
|
||||
strings.HasPrefix(source, "/") ||
|
||||
strings.HasPrefix(source, "~") ||
|
||||
strings.HasPrefix(source, ".")
|
||||
}
|
||||
|
||||
func (o *PatternsEntity) loadPattern(source string) (pattern *Pattern, err error) {
|
||||
if LooksLikePatternFilePath(source) {
|
||||
if isFilePath {
|
||||
// Resolve the file path using GetAbsolutePath
|
||||
var absPath string
|
||||
if absPath, err = util.GetAbsolutePath(source); err != nil {
|
||||
|
|
@ -123,15 +119,6 @@ func (o *PatternsEntity) applyVariables(
|
|||
|
||||
// retrieves a pattern from the database by name
|
||||
func (o *PatternsEntity) getFromDB(name string) (ret *Pattern, err error) {
|
||||
if ValidateStorageName(name) != nil {
|
||||
// The typed error lets an HTTP route without a pre-validation
|
||||
// guard map this rejection to 400, not 500.
|
||||
return nil, &InvalidStorageNameError{
|
||||
Name: name,
|
||||
Message: fmt.Sprintf(i18n.T("pattern_invalid_name"), name),
|
||||
}
|
||||
}
|
||||
|
||||
// First check custom patterns directory if it exists
|
||||
if o.CustomPatternsDir != "" {
|
||||
customPatternPath := filepath.Join(o.CustomPatternsDir, name, o.SystemPatternFile)
|
||||
|
|
@ -169,17 +156,6 @@ func (o *PatternsEntity) getFromDB(name string) (ret *Pattern, err error) {
|
|||
return
|
||||
}
|
||||
|
||||
// PrintPattern prints the raw contents of the named pattern to the terminal.
|
||||
// It checks the custom patterns directory first, then falls back to the main directory.
|
||||
func (o *PatternsEntity) PrintPattern(name string) (err error) {
|
||||
var pattern *Pattern
|
||||
if pattern, err = o.GetRaw(name); err != nil {
|
||||
return
|
||||
}
|
||||
fmt.Print(pattern.Pattern)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *PatternsEntity) PrintLatestPatterns(latestNumber int) (err error) {
|
||||
var contents []byte
|
||||
if contents, err = os.ReadFile(o.UniquePatternsFilePath); err != nil {
|
||||
|
|
@ -292,45 +268,13 @@ func (o *PatternsEntity) Get(name string) (*Pattern, error) {
|
|||
return o.GetApplyVariables(name, nil, "")
|
||||
}
|
||||
func (o *PatternsEntity) Save(name string, content []byte) (err error) {
|
||||
// Do not store a name that loadPattern uses as a file path, for
|
||||
// example ".foo" or "~bar". For such a name, GetApplyVariables reads
|
||||
// from the filesystem, not from the database.
|
||||
if LooksLikePatternFilePath(name) {
|
||||
return &InvalidStorageNameError{
|
||||
Name: name,
|
||||
Message: fmt.Sprintf(i18n.T("pattern_invalid_name"), name),
|
||||
}
|
||||
}
|
||||
var patternDir string
|
||||
if patternDir, err = o.resolvedPath(name); err != nil {
|
||||
return
|
||||
}
|
||||
patternDir := filepath.Join(o.Dir, name)
|
||||
if err = os.MkdirAll(patternDir, os.ModePerm); err != nil {
|
||||
return fmt.Errorf(i18n.T("patterns_error_create_directory"), err)
|
||||
}
|
||||
patternPath := filepath.Join(patternDir, o.SystemPatternFile)
|
||||
// The pattern file can be a symlink that already exists. Do not
|
||||
// write through a symlink that goes out of the pattern directory.
|
||||
if err = symlinkContained(patternDir, patternPath, name); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = os.WriteFile(patternPath, content, 0644); err != nil {
|
||||
return fmt.Errorf(i18n.T("patterns_error_save_pattern"), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Rename applies the file-path guard from Save to the destination name.
|
||||
// Without the guard, the inherited StorageEntity.Rename accepts ".foo"
|
||||
// or "~foo", and loadPattern then reads these names from the
|
||||
// filesystem. A path-like source stays permitted, which lets you rename
|
||||
// a legacy entry to a valid name.
|
||||
func (o *PatternsEntity) Rename(oldName, newName string) error {
|
||||
if LooksLikePatternFilePath(newName) {
|
||||
return &InvalidStorageNameError{
|
||||
Name: newName,
|
||||
Message: fmt.Sprintf(i18n.T("pattern_invalid_name"), newName),
|
||||
}
|
||||
}
|
||||
return o.StorageEntity.Rename(oldName, newName)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,10 +3,8 @@ package fsdb
|
|||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
|
@ -181,88 +179,6 @@ func TestPatternsEntity_Save(t *testing.T) {
|
|||
assert.Equal(t, content, data)
|
||||
}
|
||||
|
||||
// Save must reject a name that loadPattern uses as a filesystem path,
|
||||
// and that includes a traversal name. For such a name,
|
||||
// GetApplyVariables reads from the disk, not from the database.
|
||||
func TestPatternsEntity_SaveRejectsFilePathNames(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
for _, name := range []string{"..", ".foo", "..bar", "~bar", "/abs/path", `\win\path`} {
|
||||
err := entity.Save(name, []byte("pwned"))
|
||||
assert.Error(t, err, "expected error for file-path name: %q", name)
|
||||
if strings.HasPrefix(name, "/") || strings.HasPrefix(name, `\`) {
|
||||
// If Save accepts an absolute name, it does not write in
|
||||
// entity.Dir, and the join below points to the incorrect
|
||||
// location. For these two names, only the error assertion
|
||||
// gives protection.
|
||||
continue
|
||||
}
|
||||
// For ".." this path is the system.md of the parent directory,
|
||||
// which Save makes if it accepts a traversal name.
|
||||
_, statErr := os.Stat(filepath.Join(entity.Dir, name, entity.SystemPatternFile))
|
||||
assert.True(t, os.IsNotExist(statErr), "wrote a pattern file for: %q", name)
|
||||
}
|
||||
}
|
||||
|
||||
// Rename must reject a file-path-like destination, the same as Save.
|
||||
// The inherited StorageEntity.Rename accepts such a destination.
|
||||
func TestPatternsEntity_RenameRejectsFilePathDestination(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
createTestPattern(t, entity, "good-name", "content")
|
||||
for _, newName := range []string{".foo", "~bar"} {
|
||||
err := entity.Rename("good-name", newName)
|
||||
var invalidName *InvalidStorageNameError
|
||||
require.ErrorAs(t, err, &invalidName, "expected rejection for destination: %q", newName)
|
||||
_, statErr := os.Stat(filepath.Join(entity.Dir, newName))
|
||||
assert.True(t, os.IsNotExist(statErr), "renamed to: %q", newName)
|
||||
}
|
||||
|
||||
// A valid destination still works.
|
||||
require.NoError(t, entity.Rename("good-name", "better-name"))
|
||||
_, err := os.Stat(filepath.Join(entity.Dir, "better-name"))
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
// Save must not write through a symlinked pattern directory or a
|
||||
// symlinked pattern file that points outside the storage tree.
|
||||
func TestPatternsEntity_SaveRejectsSymlinkEscape(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
outsideDir := t.TempDir()
|
||||
mustSymlink(t, outsideDir, filepath.Join(entity.Dir, "linked-dir"))
|
||||
err := entity.Save("linked-dir", []byte("pwned"))
|
||||
var invalidName *InvalidStorageNameError
|
||||
require.ErrorAs(t, err, &invalidName)
|
||||
_, statErr := os.Stat(filepath.Join(outsideDir, entity.SystemPatternFile))
|
||||
assert.True(t, os.IsNotExist(statErr), "wrote through the symlinked pattern dir")
|
||||
|
||||
outsideFile := filepath.Join(outsideDir, "target.md")
|
||||
require.NoError(t, os.WriteFile(outsideFile, []byte("keep"), 0o644))
|
||||
require.NoError(t, os.MkdirAll(filepath.Join(entity.Dir, "real-pattern"), 0o755))
|
||||
mustSymlink(t, outsideFile, filepath.Join(entity.Dir, "real-pattern", entity.SystemPatternFile))
|
||||
err = entity.Save("real-pattern", []byte("pwned"))
|
||||
require.ErrorAs(t, err, &invalidName)
|
||||
got, readErr := os.ReadFile(outsideFile)
|
||||
require.NoError(t, readErr)
|
||||
assert.Equal(t, "keep", string(got), "outside pattern file was overwritten")
|
||||
}
|
||||
|
||||
func TestGetApplyVariables_FromFile(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
path := filepath.Join(t.TempDir(), "fromfile.md")
|
||||
require.NoError(t, os.WriteFile(path, []byte("Hello {{input}}"), 0o644))
|
||||
|
||||
result, err := entity.GetApplyVariables(path, nil, "world")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "Hello world", result.Pattern)
|
||||
}
|
||||
|
||||
func TestPatternsEntity_CustomPatterns(t *testing.T) {
|
||||
// Create main patterns directory
|
||||
mainDir, err := os.MkdirTemp("", "test-main-patterns-*")
|
||||
|
|
@ -351,112 +267,6 @@ func TestPatternsEntity_CustomPatterns(t *testing.T) {
|
|||
assert.Equal(t, "Custom pattern content", pattern.Pattern)
|
||||
}
|
||||
|
||||
func TestPrintPattern(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
createTestPattern(t, entity, "test-pattern", "# IDENTITY\nYou are a test assistant.\n")
|
||||
|
||||
t.Run("prints pattern content to stdout", func(t *testing.T) {
|
||||
// Capture stdout
|
||||
oldStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
require.NoError(t, err)
|
||||
os.Stdout = w
|
||||
|
||||
printErr := entity.PrintPattern("test-pattern")
|
||||
|
||||
w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf [4096]byte
|
||||
n, _ := r.Read(buf[:])
|
||||
output := string(buf[:n])
|
||||
|
||||
require.NoError(t, printErr)
|
||||
assert.Equal(t, "# IDENTITY\nYou are a test assistant.\n", output)
|
||||
})
|
||||
|
||||
t.Run("returns error for non-existent pattern", func(t *testing.T) {
|
||||
err := entity.PrintPattern("nonexistent-pattern")
|
||||
assert.Error(t, err)
|
||||
})
|
||||
|
||||
t.Run("custom pattern directory takes precedence", func(t *testing.T) {
|
||||
customDir, err := os.MkdirTemp("", "test-custom-*")
|
||||
require.NoError(t, err)
|
||||
defer os.RemoveAll(customDir)
|
||||
|
||||
entityWithCustom := &PatternsEntity{
|
||||
StorageEntity: entity.StorageEntity,
|
||||
SystemPatternFile: entity.SystemPatternFile,
|
||||
CustomPatternsDir: customDir,
|
||||
}
|
||||
createTestPattern(t, &PatternsEntity{
|
||||
StorageEntity: &StorageEntity{Dir: customDir, Label: "patterns", ItemIsDir: true},
|
||||
SystemPatternFile: "system.md",
|
||||
}, "test-pattern", "# CUSTOM\nCustom version.\n")
|
||||
|
||||
oldStdout := os.Stdout
|
||||
r, w, err := os.Pipe()
|
||||
require.NoError(t, err)
|
||||
os.Stdout = w
|
||||
|
||||
printErr := entityWithCustom.PrintPattern("test-pattern")
|
||||
|
||||
w.Close()
|
||||
os.Stdout = oldStdout
|
||||
|
||||
var buf [4096]byte
|
||||
n, _ := r.Read(buf[:])
|
||||
output := string(buf[:n])
|
||||
|
||||
require.NoError(t, printErr)
|
||||
assert.Equal(t, "# CUSTOM\nCustom version.\n", output)
|
||||
})
|
||||
}
|
||||
|
||||
func TestGetFromDB_PathTraversal(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
for _, name := range invalidStorageNames {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
_, err := entity.GetRaw(name)
|
||||
require.Error(t, err, "expected error for traversal name: %q", name)
|
||||
assert.Contains(t, err.Error(), "invalid pattern name", "wrong error for: %q", name)
|
||||
var invalidName *InvalidStorageNameError
|
||||
assert.ErrorAs(t, err, &invalidName, "want typed rejection for: %q", name)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// A ".." in a name without separators is one safe path element.
|
||||
// ValidateStorageName accepts it, and getFromDB also accepts it.
|
||||
func TestGetFromDB_AllowsDotsWithinName(t *testing.T) {
|
||||
entity, cleanup := setupTestPatternsEntity(t)
|
||||
defer cleanup()
|
||||
|
||||
createTestPattern(t, entity, "foo..bar", "dotty {{input}}")
|
||||
|
||||
pattern, err := entity.GetRaw("foo..bar")
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, "dotty {{input}}", pattern.Pattern)
|
||||
}
|
||||
|
||||
func TestLooksLikePatternFilePath(t *testing.T) {
|
||||
for _, source := range []string{"/x", `~\x`, `\x`, `.\x`, "~", ".", ".."} {
|
||||
assert.True(t, LooksLikePatternFilePath(source), "expected file-path detection for: %q", source)
|
||||
}
|
||||
for _, source := range []string{"", "pattern", "foo..bar", "a/b", "x~y", "x.y"} {
|
||||
assert.False(t, LooksLikePatternFilePath(source), "unexpected file-path detection for: %q", source)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternsEntity_CustomPatternsEmpty(t *testing.T) {
|
||||
// Test behavior when custom patterns directory is empty or doesn't exist
|
||||
mainDir, err := os.MkdirTemp("", "test-main-patterns-*")
|
||||
|
|
|
|||
|
|
@ -13,12 +13,6 @@ type SessionsEntity struct {
|
|||
}
|
||||
|
||||
func (o *SessionsEntity) Get(name string) (session *Session, err error) {
|
||||
// Reject invalid names here. Exists reports false for them, and the
|
||||
// missing-session branch then answers with a new empty session and
|
||||
// no error.
|
||||
if err = ValidateStorageName(name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
session = &Session{Name: name}
|
||||
|
||||
if o.Exists(name) {
|
||||
|
|
|
|||
|
|
@ -21,20 +21,6 @@ func TestSessions_GetOrCreateSession(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// Get must reject an invalid name and must not answer with a new empty
|
||||
// session. GET /sessions/<bad-name> is then a 400, the same as for the
|
||||
// other entities.
|
||||
func TestSessions_GetRejectsInvalidNames(t *testing.T) {
|
||||
sessions := &SessionsEntity{
|
||||
StorageEntity: &StorageEntity{Dir: t.TempDir(), FileExtension: ".json"},
|
||||
}
|
||||
for _, name := range invalidStorageNames {
|
||||
if _, err := sessions.Get(name); err == nil {
|
||||
t.Errorf("Get(%q) succeeded, want error", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessions_SaveSession(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
sessions := &SessionsEntity{
|
||||
|
|
|
|||
|
|
@ -11,10 +11,6 @@ import (
|
|||
"github.com/danielmiessler/fabric/internal/util"
|
||||
)
|
||||
|
||||
// StorageEntity is the filesystem-backed db.Storage implementation.
|
||||
// Each method that gets a name requires one that obeys
|
||||
// ValidateStorageName. It rejects other names with
|
||||
// *InvalidStorageNameError, which HTTP handlers map to 400.
|
||||
type StorageEntity struct {
|
||||
Label string
|
||||
Dir string
|
||||
|
|
@ -72,57 +68,34 @@ func (o *StorageEntity) GetNames() (ret []string, err error) {
|
|||
}
|
||||
|
||||
func (o *StorageEntity) Delete(name string) (err error) {
|
||||
var path string
|
||||
if path, err = o.resolvedPath(name); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.RemoveAll(path); err != nil {
|
||||
if err = os.RemoveAll(o.BuildFilePathByName(name)); err != nil {
|
||||
err = fmt.Errorf(i18n.T("storage_error_delete"), name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) Exists(name string) (ret bool) {
|
||||
path, err := o.resolvedPath(name)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
_, err = os.Stat(path)
|
||||
_, err := os.Stat(o.BuildFilePathByName(name))
|
||||
ret = !os.IsNotExist(err)
|
||||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) Rename(oldName, newName string) (err error) {
|
||||
var oldPath, newPath string
|
||||
if oldPath, err = o.resolvedPath(oldName); err != nil {
|
||||
return
|
||||
}
|
||||
if newPath, err = o.resolvedPath(newName); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.Rename(oldPath, newPath); err != nil {
|
||||
if err = os.Rename(o.BuildFilePathByName(oldName), o.BuildFilePathByName(newName)); err != nil {
|
||||
err = fmt.Errorf(i18n.T("storage_error_rename"), oldName, newName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) Save(name string, content []byte) (err error) {
|
||||
var path string
|
||||
if path, err = o.resolvedPath(name); err != nil {
|
||||
return
|
||||
}
|
||||
if err = os.WriteFile(path, content, 0644); err != nil {
|
||||
if err = os.WriteFile(o.BuildFilePathByName(name), content, 0644); err != nil {
|
||||
err = fmt.Errorf(i18n.T("storage_error_save"), name, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) Load(name string) (ret []byte, err error) {
|
||||
var path string
|
||||
if path, err = o.resolvedPath(name); err != nil {
|
||||
return
|
||||
}
|
||||
if ret, err = os.ReadFile(path); err != nil {
|
||||
if ret, err = os.ReadFile(o.BuildFilePathByName(name)); err != nil {
|
||||
err = fmt.Errorf(i18n.T("storage_error_load"), name, err)
|
||||
}
|
||||
return
|
||||
|
|
@ -147,6 +120,11 @@ func (o *StorageEntity) ListNames(shellCompleteList bool) (err error) {
|
|||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) BuildFilePathByName(name string) (ret string) {
|
||||
ret = o.BuildFilePath(o.buildFileName(name))
|
||||
return
|
||||
}
|
||||
|
||||
func (o *StorageEntity) BuildFilePath(fileName string) (ret string) {
|
||||
ret = filepath.Join(o.Dir, fileName)
|
||||
return
|
||||
|
|
@ -156,122 +134,6 @@ func (o *StorageEntity) buildFileName(name string) string {
|
|||
return fmt.Sprintf("%s%v", name, o.FileExtension)
|
||||
}
|
||||
|
||||
// InvalidStorageNameError reports a name that storage-name validation
|
||||
// rejected. HTTP handlers map it to 400 Bad Request. All other storage
|
||||
// errors stay 500 errors.
|
||||
type InvalidStorageNameError struct {
|
||||
Name string
|
||||
Message string // optional: the default is the storage_invalid_name translation
|
||||
}
|
||||
|
||||
func (e *InvalidStorageNameError) Error() string {
|
||||
if e.Message != "" {
|
||||
return e.Message
|
||||
}
|
||||
return fmt.Sprintf(i18n.T("storage_invalid_name"), e.Name)
|
||||
}
|
||||
|
||||
// windowsReservedNames are DOS device names. On Windows, these names
|
||||
// identify devices, not files. The match ignores case and all text after
|
||||
// the first dot, because Windows maps "CON.tar.gz" to the CON device.
|
||||
var windowsReservedNames = map[string]bool{
|
||||
"CON": true, "PRN": true, "AUX": true, "NUL": true,
|
||||
"COM1": true, "COM2": true, "COM3": true, "COM4": true, "COM5": true,
|
||||
"COM6": true, "COM7": true, "COM8": true, "COM9": true,
|
||||
"LPT1": true, "LPT2": true, "LPT3": true, "LPT4": true, "LPT5": true,
|
||||
"LPT6": true, "LPT7": true, "LPT8": true, "LPT9": true,
|
||||
}
|
||||
|
||||
// ValidateStorageName rejects an empty name, ".", "..", and each name
|
||||
// that is not a single path element. It also rejects names that are
|
||||
// dangerous only on Windows: names with ":" (an NTFS alternate data
|
||||
// stream suffix), names with a dot or space at the end, and reserved
|
||||
// DOS device names. Windows removes a dot or space at the end, and the
|
||||
// shortened name then collides with an existing entry. The policy is
|
||||
// the same on each platform, and an entry made on one system stays
|
||||
// valid on the other systems. A Unix
|
||||
// entry that already has a name against these rules shows in GetNames
|
||||
// but is not accessible. To repair it, rename its file or directory on
|
||||
// disk. Call this function before you join a name to a storage
|
||||
// directory.
|
||||
func ValidateStorageName(name string) error {
|
||||
if name == "" || name == "." || name == ".." {
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
if strings.ContainsAny(name, `/\:`) {
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
if name != strings.TrimRight(name, ". ") {
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
base := strings.ToUpper(name)
|
||||
if i := strings.IndexByte(base, '.'); i >= 0 {
|
||||
base = base[:i]
|
||||
}
|
||||
if windowsReservedNames[base] {
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// symlinkContained rejects an entry at path if the entry resolves,
|
||||
// through symlinks, to a target outside absDir. A missing entry passes,
|
||||
// because the lexical check in resolvedPath already keeps the path that
|
||||
// a write will make in the directory. The two inputs must be absolute
|
||||
// paths. If they are not, you cannot compare the resolved forms. The
|
||||
// check does not fully prevent local races. If a hostile local writer
|
||||
// enters the threat model, move to os.Root.
|
||||
func symlinkContained(absDir, path, name string) error {
|
||||
target, err := filepath.EvalSymlinks(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
if _, lerr := os.Lstat(path); os.IsNotExist(lerr) {
|
||||
return nil
|
||||
}
|
||||
// This is a dangling symlink. A write through it makes the
|
||||
// outside target.
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
return err
|
||||
}
|
||||
resolvedDir, err := filepath.EvalSymlinks(absDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rel, err := filepath.Rel(resolvedDir, target)
|
||||
if err != nil || !filepath.IsLocal(rel) {
|
||||
return &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// resolvedPath keeps name in the entity directory. It validates the
|
||||
// name, checks containment again after absolute resolution, and
|
||||
// rejects a symlinked entry that resolves out of the directory.
|
||||
// Symlinks that stay in the directory are permitted. A storage
|
||||
// directory that is a symlink is also permitted.
|
||||
func (o *StorageEntity) resolvedPath(name string) (string, error) {
|
||||
if err := ValidateStorageName(name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
absDir, err := filepath.Abs(o.Dir)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
absFull, err := filepath.Abs(filepath.Join(o.Dir, o.buildFileName(name)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rel, err := filepath.Rel(absDir, absFull)
|
||||
if err != nil || !filepath.IsLocal(rel) {
|
||||
return "", &InvalidStorageNameError{Name: name}
|
||||
}
|
||||
if err := symlinkContained(absDir, absFull, name); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return absFull, nil
|
||||
}
|
||||
|
||||
func (o *StorageEntity) SaveAsJson(name string, item any) (err error) {
|
||||
var jsonString []byte
|
||||
if jsonString, err = json.Marshal(item); err == nil {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,7 @@
|
|||
package fsdb
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
)
|
||||
|
||||
func TestStorage_SaveAndLoad(t *testing.T) {
|
||||
|
|
@ -54,197 +50,3 @@ func TestStorage_Delete(t *testing.T) {
|
|||
t.Errorf("expected file to be deleted")
|
||||
}
|
||||
}
|
||||
|
||||
// invalidStorageNames are names that ValidateStorageName must reject on
|
||||
// each platform. The storage tests and the pattern traversal tests
|
||||
// share this list, and one new attack name gets a test at each
|
||||
// location. The backslash cases guard the `\` half of the separator
|
||||
// check. That half is the Windows-only escape guard that a "simplify
|
||||
// to filepath.Base" refactor removes without a test failure. The colon,
|
||||
// reserved-name, and trailing dot and space cases guard the Windows
|
||||
// protections: NTFS alternate data streams, DOS device names, and name
|
||||
// suffixes that Windows removes.
|
||||
var invalidStorageNames = []string{
|
||||
"..", "../keep.txt", "/etc/passwd", "foo/../../keep.txt", ".", "",
|
||||
`foo\bar`, `..\x`,
|
||||
"foo:bar", "NUL", "con.txt", "CON.tar.gz", "foo.", "foo ",
|
||||
}
|
||||
|
||||
// newTraversalFixture returns a storage entity in a temporary root and
|
||||
// a marker file out of the entity directory. It also returns a check
|
||||
// that fails the test if the marker or the entity directory is gone.
|
||||
func newTraversalFixture(t *testing.T) (storage *StorageEntity, checkSurvived func()) {
|
||||
t.Helper()
|
||||
root := t.TempDir()
|
||||
dir := filepath.Join(root, "contexts")
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
marker := filepath.Join(root, "keep.txt")
|
||||
if err := os.WriteFile(marker, []byte("keep"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
storage = &StorageEntity{Dir: dir, Label: "Contexts"}
|
||||
checkSurvived = func() {
|
||||
t.Helper()
|
||||
if _, err := os.Stat(marker); err != nil {
|
||||
t.Fatalf("parent marker was removed: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(dir); err != nil {
|
||||
t.Fatalf("storage dir was removed: %v", err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func TestStorage_RejectsPathTraversal(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
storage, checkSurvived := newTraversalFixture(t)
|
||||
for _, name := range invalidStorageNames {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if err := storage.Delete(name); err == nil {
|
||||
t.Fatalf("Delete(%q) succeeded, want error", name)
|
||||
}
|
||||
if err := storage.Save(name, []byte("pwned")); err == nil {
|
||||
t.Fatalf("Save(%q) succeeded, want error", name)
|
||||
}
|
||||
if _, err := storage.Load(name); err == nil {
|
||||
t.Fatalf("Load(%q) succeeded, want error", name)
|
||||
}
|
||||
if storage.Exists(name) {
|
||||
t.Fatalf("Exists(%q) is true, want false", name)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
checkSurvived()
|
||||
}
|
||||
|
||||
func TestInvalidStorageNameError_DefaultMessage(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
err := &InvalidStorageNameError{Name: "bad:name"}
|
||||
if got, want := err.Error(), `invalid name: "bad:name"`; got != want {
|
||||
t.Fatalf("Error() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_Rename(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
storage := &StorageEntity{Dir: dir, Label: "Contexts"}
|
||||
if err := storage.Save("old", []byte("content")); err != nil {
|
||||
t.Fatalf("failed to save content: %v", err)
|
||||
}
|
||||
if err := storage.Rename("old", "new"); err != nil {
|
||||
t.Fatalf("failed to rename: %v", err)
|
||||
}
|
||||
if storage.Exists("old") {
|
||||
t.Errorf("expected old name to be gone")
|
||||
}
|
||||
loaded, err := storage.Load("new")
|
||||
if err != nil {
|
||||
t.Fatalf("failed to load renamed content: %v", err)
|
||||
}
|
||||
if string(loaded) != "content" {
|
||||
t.Errorf("expected %q, got %q", "content", string(loaded))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_RenameRejectsPathTraversal(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
|
||||
storage, checkSurvived := newTraversalFixture(t)
|
||||
if err := storage.Save("ok", []byte("content")); err != nil {
|
||||
t.Fatalf("failed to save content: %v", err)
|
||||
}
|
||||
|
||||
for _, name := range invalidStorageNames {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
if err := storage.Rename("ok", name); err == nil {
|
||||
t.Fatalf("Rename(%q, %q) succeeded, want error", "ok", name)
|
||||
}
|
||||
if err := storage.Rename(name, "ok"); err == nil {
|
||||
t.Fatalf("Rename(%q, %q) succeeded, want error", name, "ok")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
checkSurvived()
|
||||
if !storage.Exists("ok") {
|
||||
t.Fatalf("legitimate entry was moved or deleted")
|
||||
}
|
||||
}
|
||||
|
||||
// mustSymlink makes a symlink. If symlinks are not available, for
|
||||
// example on Windows without the privilege, it skips the test.
|
||||
func mustSymlink(t *testing.T, target, link string) {
|
||||
t.Helper()
|
||||
if err := os.Symlink(target, link); err != nil {
|
||||
t.Skipf("cannot create symlink: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStorage_RejectsSymlinkEscape(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
dir := filepath.Join(root, "store")
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outside := filepath.Join(root, "outside.txt")
|
||||
if err := os.WriteFile(outside, []byte("keep"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSymlink(t, outside, filepath.Join(dir, "escape"))
|
||||
mustSymlink(t, filepath.Join(root, "missing.txt"), filepath.Join(dir, "dangling"))
|
||||
|
||||
storage := &StorageEntity{Dir: dir}
|
||||
for _, name := range []string{"escape", "dangling"} {
|
||||
if _, err := storage.Load(name); err == nil {
|
||||
t.Fatalf("Load(%q) through an outside symlink did not fail", name)
|
||||
}
|
||||
if err := storage.Save(name, []byte("pwned")); err == nil {
|
||||
t.Fatalf("Save(%q) through an outside symlink did not fail", name)
|
||||
}
|
||||
}
|
||||
if got, _ := os.ReadFile(outside); string(got) != "keep" {
|
||||
t.Fatalf("outside file was overwritten: %q", got)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "missing.txt")); err == nil {
|
||||
t.Fatal("dangling symlink target was created")
|
||||
}
|
||||
}
|
||||
|
||||
// Load and Save operate through a symlink that stays in the storage
|
||||
// directory, and through a storage directory that is a symlink.
|
||||
func TestStorage_AllowsInternalAndDirSymlinks(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
realDir := filepath.Join(root, "real")
|
||||
if err := os.MkdirAll(realDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
storage := &StorageEntity{Dir: realDir}
|
||||
if err := storage.Save("target", []byte("content")); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
mustSymlink(t, filepath.Join(realDir, "target"), filepath.Join(realDir, "alias"))
|
||||
if got, err := storage.Load("alias"); err != nil || string(got) != "content" {
|
||||
t.Fatalf("Load through an internal symlink: got %q, err %v", got, err)
|
||||
}
|
||||
|
||||
linkDir := filepath.Join(root, "link")
|
||||
mustSymlink(t, realDir, linkDir)
|
||||
linked := &StorageEntity{Dir: linkDir}
|
||||
if got, err := linked.Load("target"); err != nil || string(got) != "content" {
|
||||
t.Fatalf("Load via a symlinked storage dir: got %q, err %v", got, err)
|
||||
}
|
||||
if err := linked.Save("new", []byte("x")); err != nil {
|
||||
t.Fatalf("Save via a symlinked storage dir: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,32 +78,20 @@ func (e *ExtensionExecutor) formatCommand(ext *ExtensionDefinition, operation st
|
|||
return "", fmt.Errorf("%s", fmt.Sprintf(i18n.T("extension_operation_not_found"), operation, ext.Name))
|
||||
}
|
||||
|
||||
// Shell-escape all user-controlled values to prevent command injection.
|
||||
// The command string is ultimately passed to "sh -c", so any shell
|
||||
// metacharacters (;, |, $(), backticks, etc.) in the value would be
|
||||
// executed. Wrapping each value in single quotes and escaping embedded
|
||||
// single quotes ensures the value is treated as a literal argument.
|
||||
vars := make(map[string]string)
|
||||
vars["executable"] = ext.Executable
|
||||
vars["operation"] = operation
|
||||
vars["value"] = shellEscape(value)
|
||||
vars["value"] = value
|
||||
|
||||
// Split on pipe for numbered variables
|
||||
values := strings.Split(value, "|")
|
||||
for i, val := range values {
|
||||
vars[fmt.Sprintf("%d", i+1)] = shellEscape(val)
|
||||
vars[fmt.Sprintf("%d", i+1)] = val
|
||||
}
|
||||
|
||||
return ApplyTemplate(opConfig.CmdTemplate, vars, "")
|
||||
}
|
||||
|
||||
// shellEscape wraps a string in single quotes for safe use in a shell command,
|
||||
// escaping any embedded single quotes. This prevents command injection when
|
||||
// untrusted input is passed as an argument to "sh -c".
|
||||
func shellEscape(s string) string {
|
||||
return "'" + strings.ReplaceAll(s, "'", "'\"'\"'") + "'"
|
||||
}
|
||||
|
||||
// executeStdout runs the command and captures its stdout
|
||||
func (e *ExtensionExecutor) executeStdout(cmd *exec.Cmd, ext *ExtensionDefinition) (string, error) {
|
||||
var stdout bytes.Buffer
|
||||
|
|
|
|||
|
|
@ -21,9 +21,6 @@ case "$1" in
|
|||
"stdout")
|
||||
echo "Hello, $2!"
|
||||
;;
|
||||
"echo")
|
||||
echo "$2"
|
||||
;;
|
||||
"file")
|
||||
echo "Hello, $2!" > "$3"
|
||||
echo "$3" # Print the filename for path_from_stdout
|
||||
|
|
@ -42,54 +39,6 @@ esac`
|
|||
registry := NewExtensionRegistry(tmpDir)
|
||||
executor := NewExtensionExecutor(registry)
|
||||
|
||||
// Test that shell metacharacters in user input are neutralized.
|
||||
// Before the fix, value flowed unescaped into "sh -c", so input
|
||||
// like "; touch /tmp/pwned" would execute arbitrary commands.
|
||||
t.Run("ShellInjectionBlocked", func(t *testing.T) {
|
||||
// Use a marker file to detect if injection succeeded.
|
||||
markerFile := filepath.Join(tmpDir, "injection-marker")
|
||||
_ = os.Remove(markerFile)
|
||||
|
||||
configPath := filepath.Join(tmpDir, "inject-test.yaml")
|
||||
configContent := `name: inject-test
|
||||
executable: ` + testScript + `
|
||||
type: executable
|
||||
timeout: 5s
|
||||
operations:
|
||||
echo:
|
||||
cmd_template: "{{executable}} echo {{value}}"
|
||||
config:
|
||||
output:
|
||||
method: stdout`
|
||||
|
||||
if err := os.WriteFile(configPath, []byte(configContent), 0644); err != nil {
|
||||
t.Fatalf("Failed to create config: %v", err)
|
||||
}
|
||||
|
||||
if err := registry.Register(configPath); err != nil {
|
||||
t.Fatalf("Failed to register extension: %v", err)
|
||||
}
|
||||
|
||||
// Malicious input: attempt to run a separate command after echo.
|
||||
maliciousValue := "hello; touch " + markerFile
|
||||
|
||||
output, err := executor.Execute("inject-test", "echo", maliciousValue)
|
||||
if err != nil {
|
||||
t.Fatalf("Execute failed: %v", err)
|
||||
}
|
||||
|
||||
// The output should contain the full malicious string as a literal
|
||||
// argument (proving the shell did NOT interpret the semicolon).
|
||||
if !strings.Contains(output, "hello; touch") {
|
||||
t.Errorf("Expected literal value in output, got: %q", output)
|
||||
}
|
||||
|
||||
// The marker file must NOT exist (injection was blocked).
|
||||
if _, err := os.Stat(markerFile); !os.IsNotExist(err) {
|
||||
t.Error("SECURITY: command injection succeeded — marker file was created")
|
||||
}
|
||||
})
|
||||
|
||||
// Test stdout-based extension
|
||||
t.Run("StdoutExecution", func(t *testing.T) {
|
||||
configPath := filepath.Join(tmpDir, "stdout-extension.yaml")
|
||||
|
|
|
|||
|
|
@ -1,47 +1,18 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
const APIKeyHeader = "X-API-Key"
|
||||
|
||||
// requireAPIKeyForBind rejects a non-loopback bind address that has no
|
||||
// API key. An empty or unspecified host binds each interface, and that
|
||||
// counts as non-loopback.
|
||||
func requireAPIKeyForBind(address, apiKey string) error {
|
||||
if apiKey != "" {
|
||||
return nil
|
||||
}
|
||||
host := address
|
||||
if h, _, err := net.SplitHostPort(address); err == nil {
|
||||
host = h
|
||||
}
|
||||
if host == "localhost" {
|
||||
return nil
|
||||
}
|
||||
if ip := net.ParseIP(host); ip != nil && ip.IsLoopback() {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf(i18n.T("server_api_key_required"), address)
|
||||
}
|
||||
|
||||
// APIKeyMiddleware validates API key for protected endpoints.
|
||||
// Swagger documentation endpoints (/swagger/*) are exempt from authentication
|
||||
// to allow users to browse and test the API documentation freely.
|
||||
func APIKeyMiddleware(apiKey string) gin.HandlerFunc {
|
||||
// Compare digests, not the raw values. ConstantTimeCompare returns
|
||||
// early when the lengths are different, and that shows the length of
|
||||
// the configured key.
|
||||
expectedKey := sha256.Sum256([]byte(apiKey))
|
||||
return func(c *gin.Context) {
|
||||
// Skip authentication for Swagger documentation endpoints
|
||||
// This allows public access to API docs even when authentication is enabled
|
||||
|
|
@ -57,8 +28,7 @@ func APIKeyMiddleware(apiKey string) gin.HandlerFunc {
|
|||
return
|
||||
}
|
||||
|
||||
headerKey := sha256.Sum256([]byte(headerApiKey))
|
||||
if subtle.ConstantTimeCompare(headerKey[:], expectedKey[:]) != 1 {
|
||||
if headerApiKey != apiKey {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "Wrong API Key"})
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,23 +73,11 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
|
||||
if err := c.BindJSON(&request); err != nil {
|
||||
log.Printf("Error binding JSON: %v", err)
|
||||
setHSTS(c)
|
||||
c.Writer.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf(i18n.T("server_invalid_request_format"), err)})
|
||||
return
|
||||
}
|
||||
|
||||
for _, prompt := range request.Prompts {
|
||||
if rejectUnsafePatternName(c, prompt.PatternName) {
|
||||
return
|
||||
}
|
||||
if rejectInvalidStorageName(c, prompt.ContextName) {
|
||||
return
|
||||
}
|
||||
if rejectInvalidStorageName(c, prompt.SessionName) {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Add log to check received language field
|
||||
log.Printf("Received chat request - Language: '%s', Prompts: %d", request.Language, len(request.Prompts))
|
||||
|
||||
|
|
@ -112,11 +100,6 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
i+1, prompt.Model, prompt.PatternName, prompt.ContextName)
|
||||
|
||||
streamChan := make(chan domain.StreamUpdate)
|
||||
// Send returns an error for a failure that happens before it starts
|
||||
// to stream, such as a pattern that needs a variable the request
|
||||
// does not give. Such an error never reaches streamChan, so keep it
|
||||
// here and report it after the stream ends.
|
||||
sendErrChan := make(chan error, 1)
|
||||
|
||||
go func(p PromptRequest) {
|
||||
defer close(streamChan)
|
||||
|
|
@ -152,12 +135,11 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
_, err = chatter.Send(c.Request.Context(), chatReq, opts)
|
||||
if err != nil {
|
||||
log.Printf("Error from chatter.Send: %v", err)
|
||||
sendErrChan <- err
|
||||
// Error already sent to streamChan via domain.StreamTypeError if occurred in Send loop
|
||||
return
|
||||
}
|
||||
}(prompt)
|
||||
|
||||
sawError := false
|
||||
for update := range streamChan {
|
||||
select {
|
||||
case <-clientGone:
|
||||
|
|
@ -177,7 +159,6 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
Usage: update.Usage,
|
||||
}
|
||||
case domain.StreamTypeError:
|
||||
sawError = true
|
||||
response = StreamResponse{
|
||||
Type: "error",
|
||||
Format: "plain",
|
||||
|
|
@ -192,15 +173,6 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// The goroutine writes sendErrChan before it closes streamChan, so
|
||||
// the value is here by the time the loop above ends.
|
||||
if response, ok := unreportedSendError(sendErrChan, sawError); ok {
|
||||
if err := writeSSEResponse(c.Writer, response); err != nil {
|
||||
log.Printf("Error writing error response: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
completeResponse := StreamResponse{
|
||||
Type: "complete",
|
||||
Format: "plain",
|
||||
|
|
@ -214,32 +186,6 @@ func (h *ChatHandler) HandleChat(c *gin.Context) {
|
|||
}
|
||||
}
|
||||
|
||||
// unreportedSendError reads the error that Send returned and makes the response
|
||||
// that tells the client about it. The second result is false when there is
|
||||
// nothing to send.
|
||||
//
|
||||
// Send both gives a stream error to the update channel and returns it, so the
|
||||
// client already has that error and must not get it twice. The sawError
|
||||
// argument says whether an error went to the client during the stream. An error
|
||||
// that Send makes before or after the stream, such as a pattern that needs a
|
||||
// variable the request does not give, never goes to the update channel, and
|
||||
// this function is the only way the client learns about it.
|
||||
func unreportedSendError(sendErrChan <-chan error, sawError bool) (StreamResponse, bool) {
|
||||
select {
|
||||
case sendErr := <-sendErrChan:
|
||||
if sendErr == nil || sawError {
|
||||
return StreamResponse{}, false
|
||||
}
|
||||
return StreamResponse{
|
||||
Type: "error",
|
||||
Format: "plain",
|
||||
Content: fmt.Sprintf(i18n.T("server_chat_error"), sendErr),
|
||||
}, true
|
||||
default:
|
||||
return StreamResponse{}, false
|
||||
}
|
||||
}
|
||||
|
||||
func buildPromptChatRequest(p PromptRequest, language string) *domain.ChatRequest {
|
||||
return &domain.ChatRequest{
|
||||
Message: &chat.ChatCompletionMessage{
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestBuildPromptChatRequest_PreservesStrategyAndUserInput(t *testing.T) {
|
||||
prompt := PromptRequest{
|
||||
|
|
@ -46,72 +43,3 @@ func TestBuildPromptChatRequest_PreservesStrategyAndUserInput(t *testing.T) {
|
|||
t.Fatalf("expected variables to be preserved, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnreportedSendError(t *testing.T) {
|
||||
patternErr := errors.New("could not get pattern write_essay: missing required variable: author_name")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
sendErr error
|
||||
sawError bool
|
||||
wantReport bool
|
||||
wantContent string
|
||||
}{
|
||||
{
|
||||
// A pattern that needs a variable the request does not give fails
|
||||
// before Send starts to stream, so no error reached the client yet.
|
||||
// This is the only chance to tell the client about it.
|
||||
name: "error before the stream reaches the client",
|
||||
sendErr: patternErr,
|
||||
sawError: false,
|
||||
wantReport: true,
|
||||
wantContent: "Error: " + patternErr.Error(),
|
||||
},
|
||||
{
|
||||
// Send gives a stream error to the update channel and also returns
|
||||
// it. The client has it already, so a second report would show the
|
||||
// same failure twice.
|
||||
name: "error during the stream is not sent again",
|
||||
sendErr: errors.New("vendor stream failed"),
|
||||
sawError: true,
|
||||
wantReport: false,
|
||||
},
|
||||
{
|
||||
name: "no error and no report",
|
||||
sendErr: nil,
|
||||
sawError: false,
|
||||
wantReport: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
sendErrChan := make(chan error, 1)
|
||||
if test.sendErr != nil {
|
||||
sendErrChan <- test.sendErr
|
||||
}
|
||||
|
||||
response, ok := unreportedSendError(sendErrChan, test.sawError)
|
||||
|
||||
if ok != test.wantReport {
|
||||
t.Fatalf("want report=%v, got %v", test.wantReport, ok)
|
||||
}
|
||||
if !test.wantReport {
|
||||
return
|
||||
}
|
||||
if response.Type != "error" {
|
||||
t.Errorf("want type %q, got %q", "error", response.Type)
|
||||
}
|
||||
if response.Content != test.wantContent {
|
||||
t.Errorf("want content %q, got %q", test.wantContent, response.Content)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// An empty channel must not block the response, which the client waits for.
|
||||
func TestUnreportedSendErrorWithEmptyChannel(t *testing.T) {
|
||||
if _, ok := unreportedSendError(make(chan error, 1), false); ok {
|
||||
t.Error("want no report from an empty channel")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,26 +34,15 @@ func (h *ModelsHandler) GetModelNames(c *gin.Context) {
|
|||
}
|
||||
|
||||
response := make(map[string]any)
|
||||
response["models"] = h.getAllModelNames(vendorsModels)
|
||||
response["vendors"] = buildVendorsMap(vendorsModels)
|
||||
c.JSON(200, response)
|
||||
}
|
||||
|
||||
// buildVendorsMap groups the model names by vendor name for the response.
|
||||
// A vendor with no models gets an empty slice, not a nil slice, because a nil
|
||||
// slice becomes null in JSON and a client that reads the list of a vendor then
|
||||
// gets null in place of an array. Ollama does this when it is in the
|
||||
// configuration but serves no models.
|
||||
func buildVendorsMap(vendorsModels *ai.VendorsModels) map[string][]string {
|
||||
vendors := make(map[string][]string)
|
||||
|
||||
for _, groupItems := range vendorsModels.GroupsItems {
|
||||
if groupItems.Items == nil {
|
||||
vendors[groupItems.Group] = []string{}
|
||||
continue
|
||||
}
|
||||
vendors[groupItems.Group] = groupItems.Items
|
||||
}
|
||||
return vendors
|
||||
|
||||
response["models"] = h.getAllModelNames(vendorsModels)
|
||||
response["vendors"] = vendors
|
||||
c.JSON(200, response)
|
||||
}
|
||||
|
||||
func (h *ModelsHandler) getAllModelNames(vendorsModels *ai.VendorsModels) []string {
|
||||
|
|
|
|||
|
|
@ -1,48 +0,0 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/plugins/ai"
|
||||
)
|
||||
|
||||
func TestBuildVendorsMapGivesEmptyArrayForVendorWithoutModels(t *testing.T) {
|
||||
vendorsModels := ai.NewVendorsModels()
|
||||
vendorsModels.AddGroupItems("Anthropic", "claude-opus-5", "claude-sonnet-5")
|
||||
// A variadic call with no items makes a nil slice, which is what a vendor in
|
||||
// the configuration that serves no models produces.
|
||||
vendorsModels.AddGroupItems("Ollama")
|
||||
|
||||
vendors := buildVendorsMap(vendorsModels)
|
||||
|
||||
if got := vendors["Ollama"]; got == nil {
|
||||
t.Error("want an empty slice for a vendor with no models, got nil")
|
||||
}
|
||||
if got := len(vendors["Ollama"]); got != 0 {
|
||||
t.Errorf("want 0 models for Ollama, got %d", got)
|
||||
}
|
||||
if got := len(vendors["Anthropic"]); got != 2 {
|
||||
t.Errorf("want 2 models for Anthropic, got %d", got)
|
||||
}
|
||||
|
||||
// The client reads vendors[name] as an array. Confirm the JSON holds an
|
||||
// array and not null, which is what stopped the web UI from listing models.
|
||||
encoded, err := json.Marshal(vendors)
|
||||
if err != nil {
|
||||
t.Fatalf("marshal failed: %v", err)
|
||||
}
|
||||
var decoded map[string][]string
|
||||
if err = json.Unmarshal(encoded, &decoded); err != nil {
|
||||
t.Fatalf("unmarshal failed: %v", err)
|
||||
}
|
||||
if decoded["Ollama"] == nil {
|
||||
t.Errorf("want an array for Ollama in JSON, got null: %s", encoded)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBuildVendorsMapWithNoVendors(t *testing.T) {
|
||||
if got := len(buildVendorsMap(ai.NewVendorsModels())); got != 0 {
|
||||
t.Errorf("want an empty map, got %d entries", got)
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,6 @@ import (
|
|||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"log/slog"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
|
@ -46,7 +45,6 @@ type APIConvert struct {
|
|||
registry *core.PluginRegistry
|
||||
r *gin.Engine
|
||||
addr *string
|
||||
apiKey string
|
||||
}
|
||||
|
||||
type OllamaRequestBody struct {
|
||||
|
|
@ -190,49 +188,12 @@ func parseOllamaNumCtx(options map[string]any) (int, error) {
|
|||
return contextLength, nil
|
||||
}
|
||||
|
||||
// fabricChatClient sends the /api/chat self-forward, which can contain
|
||||
// the configured API key. It does not use a proxy, because the default
|
||||
// transport obeys HTTP_PROXY and can send the key to the proxy. It does
|
||||
// not obey redirects, and cannot send the key again to a location that
|
||||
// the operator did not configure.
|
||||
var fabricChatClient = newFabricChatClient()
|
||||
|
||||
func newFabricChatClient() *http.Client {
|
||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
transport.Proxy = nil
|
||||
return &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// ServeOllama operates the Ollama-compatible API server on address. An
|
||||
// empty apiKey sets authentication to off. This is permitted only for
|
||||
// loopback binds.
|
||||
func ServeOllama(registry *core.PluginRegistry, address string, version string, apiKey string) error {
|
||||
if err := requireAPIKeyForBind(address, apiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
return newOllamaEngine(registry, address, version, apiKey).Run(address)
|
||||
}
|
||||
|
||||
// newOllamaEngine makes the engine but does not start it, which lets
|
||||
// tests operate the routes. The address parameter is the /api/chat
|
||||
// forward target, not the listen address that Run gets. In production
|
||||
// the two are the same value.
|
||||
func newOllamaEngine(registry *core.PluginRegistry, address string, version string, apiKey string) *gin.Engine {
|
||||
func ServeOllama(registry *core.PluginRegistry, address string, version string) (err error) {
|
||||
r := gin.New()
|
||||
|
||||
// Middleware
|
||||
r.Use(gin.Logger())
|
||||
r.Use(gin.Recovery())
|
||||
if apiKey != "" {
|
||||
r.Use(APIKeyMiddleware(apiKey))
|
||||
} else {
|
||||
slog.Warn("Starting Ollama-compatible API server without API key authentication. This may pose security risks.")
|
||||
}
|
||||
|
||||
// Register routes
|
||||
fabricDb := registry.Db
|
||||
|
|
@ -247,7 +208,6 @@ func newOllamaEngine(registry *core.PluginRegistry, address string, version stri
|
|||
registry: registry,
|
||||
r: r,
|
||||
addr: &address,
|
||||
apiKey: apiKey,
|
||||
}
|
||||
// Ollama Endpoints
|
||||
r.GET("/api/tags", typeConversion.ollamaTags)
|
||||
|
|
@ -256,7 +216,13 @@ func newOllamaEngine(registry *core.PluginRegistry, address string, version stri
|
|||
})
|
||||
r.POST("/api/chat", typeConversion.ollamaChat)
|
||||
|
||||
return r
|
||||
// Start server
|
||||
err = r.Run(address)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (f APIConvert) ollamaTags(c *gin.Context) {
|
||||
|
|
@ -301,7 +267,7 @@ func (f APIConvert) ollamaChat(c *gin.Context) {
|
|||
err = json.Unmarshal(body, &prompt)
|
||||
if err != nil {
|
||||
log.Printf(i18n.T("ollama_error_unmarshalling_body"), err)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": i18n.T("ollama_invalid_request_body")})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_error_endpoint")})
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -369,14 +335,14 @@ func (f APIConvert) ollamaChat(c *gin.Context) {
|
|||
fabricChatReq, err := json.Marshal(chat)
|
||||
if err != nil {
|
||||
log.Printf(i18n.T("ollama_error_marshalling_body"), err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_failed_create_request")})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
var req *http.Request
|
||||
baseURL, err := buildFabricChatURL(*f.addr)
|
||||
if err != nil {
|
||||
log.Printf(i18n.T("ollama_error_building_chat_url"), err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_failed_create_request")})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
req, err = http.NewRequest("POST", fmt.Sprintf("%s/chat", baseURL), bytes.NewBuffer(fabricChatReq))
|
||||
|
|
@ -385,16 +351,13 @@ func (f APIConvert) ollamaChat(c *gin.Context) {
|
|||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_failed_create_request")})
|
||||
return
|
||||
}
|
||||
if f.apiKey != "" {
|
||||
req.Header.Set(APIKeyHeader, f.apiKey)
|
||||
}
|
||||
|
||||
req = req.WithContext(c.Request.Context())
|
||||
|
||||
fabricRes, err := fabricChatClient.Do(req)
|
||||
fabricRes, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Printf(i18n.T("ollama_error_getting_chat_body"), err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": i18n.T("ollama_upstream_request_failed")})
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
defer fabricRes.Body.Close()
|
||||
|
|
|
|||
|
|
@ -2,16 +2,9 @@ package restapi
|
|||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestBuildFabricChatURL(t *testing.T) {
|
||||
|
|
@ -368,193 +361,3 @@ func TestParseOllamaNumCtx(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNewOllamaEngine_APIKeyWiring(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
registry := &core.PluginRegistry{Db: fsdb.NewDb(t.TempDir())}
|
||||
|
||||
getVersion := func(r *gin.Engine, key string) int {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/version", nil)
|
||||
if key != "" {
|
||||
req.Header.Set(APIKeyHeader, key)
|
||||
}
|
||||
r.ServeHTTP(w, req)
|
||||
return w.Code
|
||||
}
|
||||
|
||||
withKey := newOllamaEngine(registry, ":0", "test-version", "secret")
|
||||
if code := getVersion(withKey, ""); code != http.StatusUnauthorized {
|
||||
t.Fatalf("no key presented: got %d, want 401", code)
|
||||
}
|
||||
if code := getVersion(withKey, "secret"); code != http.StatusOK {
|
||||
t.Fatalf("valid key presented: got %d, want 200", code)
|
||||
}
|
||||
|
||||
withoutKey := newOllamaEngine(registry, ":0", "test-version", "")
|
||||
if code := getVersion(withoutKey, ""); code != http.StatusOK {
|
||||
t.Fatalf("no key configured: got %d, want 200", code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestOllamaChat_ForwardsAPIKeyToChat(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// Make the loopback /chat route with the middleware installed, the
|
||||
// same as newOllamaEngine makes it when --api-key is set.
|
||||
upstream := gin.New()
|
||||
upstream.Use(APIKeyMiddleware("secret"))
|
||||
upstream.POST("/chat", func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
fmt.Fprint(c.Writer, "data: {\"type\":\"content\",\"format\":\"markdown\",\"content\":\"hi\"}\n\n")
|
||||
})
|
||||
server := httptest.NewServer(upstream)
|
||||
defer server.Close()
|
||||
|
||||
chatRequest := func(key string) int {
|
||||
r := gin.New()
|
||||
conv := APIConvert{addr: &server.URL, apiKey: key}
|
||||
r.POST("/api/chat", conv.ollamaChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"model":"test:latest","messages":[{"role":"user","content":"hi"}]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
return w.Code
|
||||
}
|
||||
|
||||
if code := chatRequest("secret"); code != http.StatusOK {
|
||||
t.Fatalf("matching key: got %d, want 200", code)
|
||||
}
|
||||
if code := chatRequest("wrong"); code != http.StatusUnauthorized {
|
||||
t.Fatalf("wrong key: got %d, want 401", code)
|
||||
}
|
||||
}
|
||||
|
||||
// The self-forward client must not use a proxy. A proxy gets the
|
||||
// configured API key, and the operator did not configure that host.
|
||||
// The redirect test below covers the no-redirect property.
|
||||
func TestFabricChatClient_NoProxy(t *testing.T) {
|
||||
transport, ok := fabricChatClient.Transport.(*http.Transport)
|
||||
if !ok {
|
||||
t.Fatalf("transport is %T, want *http.Transport", fabricChatClient.Transport)
|
||||
}
|
||||
if transport.Proxy != nil {
|
||||
t.Fatal("self-forward transport has a proxy configured")
|
||||
}
|
||||
}
|
||||
|
||||
// An upstream redirect must show as an upstream error. The client must
|
||||
// not go to the redirect target with the API key.
|
||||
func TestOllamaChat_DoesNotFollowUpstreamRedirect(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
redirectTargetHit := false
|
||||
target := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
|
||||
redirectTargetHit = true
|
||||
}))
|
||||
defer target.Close()
|
||||
|
||||
upstream := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, target.URL+"/chat", http.StatusFound)
|
||||
}))
|
||||
defer upstream.Close()
|
||||
|
||||
r := gin.New()
|
||||
conv := APIConvert{addr: &upstream.URL, apiKey: "secret"}
|
||||
r.POST("/api/chat", conv.ollamaChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"model":"test:latest","messages":[{"role":"user","content":"hi"}]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if redirectTargetHit {
|
||||
t.Fatal("the redirect target was contacted")
|
||||
}
|
||||
if w.Code != http.StatusFound {
|
||||
t.Fatalf("got %d, want the upstream 302 surfaced as an error", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// Malformed client JSON is a client error. The answer is a 400 with a
|
||||
// stable generic message, not a 500.
|
||||
func TestOllamaChat_MalformedJSONIs400(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
addr := ":0"
|
||||
r := gin.New()
|
||||
conv := APIConvert{addr: &addr}
|
||||
r.POST("/api/chat", conv.ollamaChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/chat", strings.NewReader("{not json"))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("got %d, want 400", w.Code)
|
||||
}
|
||||
}
|
||||
|
||||
// An upstream that is not available is a 500. The body must not contain
|
||||
// the raw transport error, because that error shows the internal
|
||||
// upstream URL.
|
||||
func TestOllamaChat_UpstreamFailureHidesDetails(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
server := httptest.NewServer(http.NotFoundHandler())
|
||||
url := server.URL
|
||||
server.Close() // nothing listens on url anymore
|
||||
|
||||
r := gin.New()
|
||||
conv := APIConvert{addr: &url}
|
||||
r.POST("/api/chat", conv.ollamaChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"model":"test:latest","messages":[{"role":"user","content":"hi"}]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("got %d, want 500", w.Code)
|
||||
}
|
||||
got := w.Body.String()
|
||||
if strings.Contains(got, "dial tcp") || strings.Contains(got, strings.TrimPrefix(url, "http://")) {
|
||||
t.Fatalf("500 body leaks transport details: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
// With no configured key, the forwarded request must not contain the header.
|
||||
func TestOllamaChat_NoKeyOmitsHeader(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
var gotHeader string
|
||||
upstream := gin.New()
|
||||
upstream.POST("/chat", func(c *gin.Context) {
|
||||
gotHeader = c.GetHeader(APIKeyHeader)
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
fmt.Fprint(c.Writer, "data: {\"type\":\"content\",\"format\":\"markdown\",\"content\":\"hi\"}\n\n")
|
||||
})
|
||||
server := httptest.NewServer(upstream)
|
||||
defer server.Close()
|
||||
|
||||
r := gin.New()
|
||||
conv := APIConvert{addr: &server.URL, apiKey: ""}
|
||||
r.POST("/api/chat", conv.ollamaChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"model":"test:latest","messages":[{"role":"user","content":"hi"}]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/api/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("got %d, want 200", w.Code)
|
||||
}
|
||||
if gotHeader != "" {
|
||||
t.Fatalf("X-API-Key was forwarded with no key configured: %q", gotHeader)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,330 +0,0 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
// Each storage route that gets a name must reject a traversal name, not
|
||||
// only DELETE. Most routes share storageError through the fsdb layer.
|
||||
// The exists route validates in the handler, because its storage
|
||||
// contract returns only a bool.
|
||||
func TestStorageHandler_RejectsTraversalOnAllRoutes(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
root := t.TempDir()
|
||||
contextsDir := filepath.Join(root, "contexts")
|
||||
if err := os.MkdirAll(contextsDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
marker := filepath.Join(root, "keep.txt")
|
||||
if err := os.WriteFile(marker, []byte("keep"), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
NewContextsHandler(r, &fsdb.ContextsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Label: "Contexts", Dir: contextsDir},
|
||||
})
|
||||
|
||||
for _, tc := range []struct{ method, path string }{
|
||||
{http.MethodGet, "/contexts/%2e%2e"},
|
||||
{http.MethodDelete, "/contexts/%2e%2e"},
|
||||
{http.MethodDelete, "/contexts/.."},
|
||||
{http.MethodPost, "/contexts/%2e%2e"},
|
||||
{http.MethodPut, "/contexts/rename/%2e%2e/ok"},
|
||||
{http.MethodPut, "/contexts/rename/ok/%2e%2e"},
|
||||
{http.MethodGet, "/contexts/exists/%2e%2e"},
|
||||
} {
|
||||
var body io.Reader
|
||||
if tc.method == http.MethodPost {
|
||||
body = strings.NewReader("x")
|
||||
}
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(tc.method, tc.path, body)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("%s %s: got %d, want 400", tc.method, tc.path, w.Code)
|
||||
}
|
||||
if w.Header().Get("Strict-Transport-Security") == "" {
|
||||
t.Fatalf("%s %s: validation 400 lacks the HSTS header", tc.method, tc.path)
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := os.Stat(marker); err != nil {
|
||||
t.Fatalf("parent marker was deleted: %v", err)
|
||||
}
|
||||
if _, err := os.Stat(contextsDir); err != nil {
|
||||
t.Fatalf("contexts dir was deleted: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// A non-validation failure stays a 500. Its body must not show the
|
||||
// filesystem path that is in the wrapped *os.PathError.
|
||||
func TestStorageHandler_GenericErrorHidesPaths(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
dir := t.TempDir()
|
||||
r := gin.New()
|
||||
NewContextsHandler(r, &fsdb.ContextsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Label: "Contexts", Dir: dir},
|
||||
})
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/contexts/missing", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("got %d, want 500", w.Code)
|
||||
}
|
||||
if strings.Contains(w.Body.String(), dir) {
|
||||
t.Fatalf("500 body leaks the storage path: %s", w.Body.String())
|
||||
}
|
||||
if !strings.Contains(w.Body.String(), "internal error") {
|
||||
t.Fatalf("500 body is not the generic message: %s", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// A pattern backend failure must use the shared storageError mapping.
|
||||
// That is a JSON envelope with the generic message, never the wrapped
|
||||
// os.PathError.
|
||||
func TestPatternsHandler_BackendErrorHidesPaths(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
dir := t.TempDir()
|
||||
r := gin.New()
|
||||
NewPatternsHandler(r, &fsdb.PatternsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Label: "Patterns", Dir: dir, ItemIsDir: true},
|
||||
SystemPatternFile: "system.md",
|
||||
})
|
||||
|
||||
for _, req := range []*http.Request{
|
||||
httptest.NewRequest(http.MethodGet, "/patterns/missing", nil),
|
||||
httptest.NewRequest(http.MethodPost, "/patterns/missing/apply", strings.NewReader(`{"input":"x"}`)),
|
||||
} {
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("%s %s: got %d, want 500", req.Method, req.URL.Path, w.Code)
|
||||
}
|
||||
if strings.Contains(w.Body.String(), dir) {
|
||||
t.Fatalf("%s %s: 500 body leaks the storage path: %s", req.Method, req.URL.Path, w.Body.String())
|
||||
}
|
||||
if !strings.Contains(w.Body.String(), "internal error") {
|
||||
t.Fatalf("%s %s: 500 body is not the generic envelope: %s", req.Method, req.URL.Path, w.Body.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternsHandler_RejectsPathTraversalSave(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
root := t.TempDir()
|
||||
patternsDir := filepath.Join(root, "patterns")
|
||||
if err := os.MkdirAll(patternsDir, 0o755); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
NewPatternsHandler(r, &fsdb.PatternsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Label: "Patterns", Dir: patternsDir, ItemIsDir: true},
|
||||
SystemPatternFile: "system.md",
|
||||
})
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/patterns/%2e%2e", strings.NewReader("pwned"))
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("POST /patterns/%%2e%%2e: got %d, want 400", w.Code)
|
||||
}
|
||||
if _, err := os.Stat(filepath.Join(root, "system.md")); err == nil {
|
||||
t.Fatal("wrote system.md in the parent directory")
|
||||
}
|
||||
}
|
||||
|
||||
func TestChatHandler_RejectsUnsafeNames(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
// The zero-value handler is an intentional seam. A request that goes
|
||||
// through pre-validation causes a nil panic in HandleChat. These
|
||||
// tests cannot pass on a request that got no validation.
|
||||
r := gin.New()
|
||||
r.POST("/chat", (&ChatHandler{}).HandleChat)
|
||||
|
||||
cases := map[string]string{
|
||||
// Every prefix class loadPattern treats as a filesystem path
|
||||
"absolute pattern name": `{"prompts":[{"patternName":"/etc/hosts","userInput":"x"}]}`,
|
||||
"home pattern name": `{"prompts":[{"patternName":"~/secret.md","userInput":"x"}]}`,
|
||||
"relative pattern name": `{"prompts":[{"patternName":"./secret.md","userInput":"x"}]}`,
|
||||
"backslash pattern name": `{"prompts":[{"patternName":"\\secret.md","userInput":"x"}]}`,
|
||||
// Context and session names get the same pre-validation
|
||||
"traversal context name": `{"prompts":[{"userInput":"x","contextName":"../keep.txt"}]}`,
|
||||
"traversal session name": `{"prompts":[{"userInput":"x","sessionName":".."}]}`,
|
||||
// The rejection loop stops at the first bad name, at each depth
|
||||
"second prompt path-like": `{"prompts":[{"userInput":"x"},{"patternName":"/etc/hosts","userInput":"y"}]}`,
|
||||
}
|
||||
for name, body := range cases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodPost, "/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("got status %d, want 400", w.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPatternsHandler_RejectsUnsafeNamesOnReadRoutes(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
r := gin.New()
|
||||
NewPatternsHandler(r, &fsdb.PatternsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Label: "Patterns", Dir: t.TempDir(), ItemIsDir: true},
|
||||
SystemPatternFile: "system.md",
|
||||
})
|
||||
|
||||
for _, req := range []*http.Request{
|
||||
httptest.NewRequest(http.MethodGet, "/patterns/%2e%2e", nil),
|
||||
httptest.NewRequest(http.MethodPost, "/patterns/%2e%2e/apply", strings.NewReader(`{"input":"x"}`)),
|
||||
// Names that fail only ValidateStorageName, not the file-path check
|
||||
httptest.NewRequest(http.MethodGet, "/patterns/foo:bar", nil),
|
||||
httptest.NewRequest(http.MethodGet, "/patterns/NUL", nil),
|
||||
httptest.NewRequest(http.MethodPost, "/patterns/foo:bar/apply", strings.NewReader(`{"input":"x"}`)),
|
||||
} {
|
||||
w := httptest.NewRecorder()
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusBadRequest {
|
||||
t.Fatalf("%s %s: got %d, want 400", req.Method, req.URL.Path, w.Code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A safe request must go through pre-validation. With the zero-value
|
||||
// handler seam, a request that goes through causes a nil panic, and
|
||||
// Recovery changes that into a 500. A 400 shows that validation
|
||||
// rejected valid names.
|
||||
func TestChatHandler_AcceptsBenignNames(t *testing.T) {
|
||||
if _, err := i18n.Init("en"); err != nil {
|
||||
t.Fatalf("i18n.Init() error = %v", err)
|
||||
}
|
||||
gin.SetMode(gin.TestMode)
|
||||
|
||||
r := gin.New()
|
||||
r.Use(gin.Recovery())
|
||||
r.POST("/chat", (&ChatHandler{}).HandleChat)
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
body := `{"prompts":[{"userInput":"x","patternName":"summarize","contextName":"myctx","sessionName":"mysession"}]}`
|
||||
req := httptest.NewRequest(http.MethodPost, "/chat", strings.NewReader(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code == http.StatusBadRequest {
|
||||
t.Fatalf("benign request was rejected with 400: %s", w.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
// A non-loopback bind without an API key must fail closed before the
|
||||
// server starts. A loopback bind operates without a key.
|
||||
func TestRequireAPIKeyForBind(t *testing.T) {
|
||||
tests := []struct {
|
||||
address string
|
||||
apiKey string
|
||||
wantErr bool
|
||||
}{
|
||||
{"127.0.0.1:8080", "", false},
|
||||
{"localhost:8080", "", false},
|
||||
{"localhost", "", false},
|
||||
{"[::1]:8080", "", false},
|
||||
{":8080", "", true}, // wildcard bind exposes every interface
|
||||
{"0.0.0.0:8080", "", true},
|
||||
{"[::]:8080", "", true},
|
||||
{"192.168.1.50:8080", "", true},
|
||||
{"example.com:8080", "", true},
|
||||
{":8080", "secret", false},
|
||||
{"0.0.0.0:8080", "secret", false},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.address, func(t *testing.T) {
|
||||
err := requireAPIKeyForBind(tt.address, tt.apiKey)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Fatalf("requireAPIKeyForBind(%q, %q) error = %v, wantErr %v", tt.address, tt.apiKey, err, tt.wantErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Serve and ServeOllama must return the fail-closed error and must not
|
||||
// start an unauthenticated server on a non-loopback bind. The registry
|
||||
// is nil, and a check that does not occur first causes a panic.
|
||||
func TestServeFailsClosedOnNonLoopbackBind(t *testing.T) {
|
||||
if err := Serve(nil, ":0", ""); err == nil {
|
||||
t.Fatal("Serve on a wildcard bind without a key did not fail")
|
||||
}
|
||||
if err := ServeOllama(nil, ":0", "v", ""); err == nil {
|
||||
t.Fatal("ServeOllama on a wildcard bind without a key did not fail")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAPIKeyMiddleware(t *testing.T) {
|
||||
gin.SetMode(gin.TestMode)
|
||||
r := gin.New()
|
||||
r.Use(APIKeyMiddleware("secret"))
|
||||
r.GET("/ping", func(c *gin.Context) { c.Status(http.StatusOK) })
|
||||
|
||||
t.Run("missing key", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("got %d, want 401", w.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrong key", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
|
||||
req.Header.Set(APIKeyHeader, "wrong")
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusUnauthorized {
|
||||
t.Fatalf("got %d, want 401", w.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("valid key", func(t *testing.T) {
|
||||
w := httptest.NewRecorder()
|
||||
req := httptest.NewRequest(http.MethodGet, "/ping", nil)
|
||||
req.Header.Set(APIKeyHeader, "secret")
|
||||
r.ServeHTTP(w, req)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("got %d, want 200", w.Code)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
"net/http"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
|
@ -16,22 +14,6 @@ type PatternsHandler struct {
|
|||
patterns *fsdb.PatternsEntity
|
||||
}
|
||||
|
||||
// rejectUnsafePatternName answers a 400 when name is a file-path-like
|
||||
// pattern name or does not obey storage-name validation. An empty name
|
||||
// passes, because the chat handler guards prompt.PatternName, which is
|
||||
// optional.
|
||||
func rejectUnsafePatternName(c *gin.Context, name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
if fsdb.LooksLikePatternFilePath(name) || fsdb.ValidateStorageName(name) != nil {
|
||||
setHSTS(c)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": fmt.Sprintf(i18n.T("pattern_invalid_name"), name)})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewPatternsHandler creates a new PatternsHandler
|
||||
func NewPatternsHandler(r *gin.Engine, patterns *fsdb.PatternsEntity) (ret *PatternsHandler) {
|
||||
// Create a storage handler but don't register any routes yet
|
||||
|
|
@ -58,19 +40,15 @@ func NewPatternsHandler(r *gin.Engine, patterns *fsdb.PatternsEntity) (ret *Patt
|
|||
// @Produce json
|
||||
// @Param name path string true "Pattern name"
|
||||
// @Success 200 {object} fsdb.Pattern
|
||||
// @Failure 400 {object} map[string]string
|
||||
// @Failure 500 {object} map[string]string
|
||||
// @Security ApiKeyAuth
|
||||
// @Router /patterns/{name} [get]
|
||||
func (h *PatternsHandler) Get(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
if rejectUnsafePatternName(c, name) {
|
||||
return
|
||||
}
|
||||
|
||||
pattern, err := h.patterns.GetRaw(name)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, pattern)
|
||||
|
|
@ -97,9 +75,6 @@ type PatternApplyRequest struct {
|
|||
// @Router /patterns/{name}/apply [post]
|
||||
func (h *PatternsHandler) ApplyPattern(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
if rejectUnsafePatternName(c, name) {
|
||||
return
|
||||
}
|
||||
|
||||
var request PatternApplyRequest
|
||||
if err := c.ShouldBindJSON(&request); err != nil {
|
||||
|
|
@ -118,7 +93,7 @@ func (h *PatternsHandler) ApplyPattern(c *gin.Context) {
|
|||
|
||||
pattern, err := h.patterns.GetApplyVariables(name, variables, request.Input)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, pattern)
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import (
|
|||
"path/filepath"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/core"
|
||||
"github.com/danielmiessler/fabric/internal/i18n"
|
||||
"github.com/gin-gonic/gin"
|
||||
swaggerFiles "github.com/swaggo/files"
|
||||
ginSwagger "github.com/swaggo/gin-swagger"
|
||||
|
|
@ -28,10 +27,6 @@ import (
|
|||
// @in header
|
||||
// @name X-API-Key
|
||||
func Serve(registry *core.PluginRegistry, address string, apiKey string) (err error) {
|
||||
if err = requireAPIKeyForBind(address, apiKey); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r := gin.New()
|
||||
|
||||
// Middleware
|
||||
|
|
@ -41,7 +36,7 @@ func Serve(registry *core.PluginRegistry, address string, apiKey string) (err er
|
|||
if apiKey != "" {
|
||||
r.Use(APIKeyMiddleware(apiKey))
|
||||
} else {
|
||||
slog.Warn(i18n.T("server_no_api_key_warning"))
|
||||
slog.Warn("Starting REST API server without API key authentication. This may pose security risks.")
|
||||
}
|
||||
|
||||
// Swagger UI and documentation endpoint with custom YAML handler
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
package restapi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db"
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
|
|
@ -17,42 +14,6 @@ type StorageHandler[T any] struct {
|
|||
storage db.Storage[T]
|
||||
}
|
||||
|
||||
// setHSTS sets the Strict-Transport-Security header. Each validation
|
||||
// 400 sends it, the same as the chat BindJSON 400 path.
|
||||
func setHSTS(c *gin.Context) {
|
||||
c.Writer.Header().Set("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
|
||||
}
|
||||
|
||||
// storageError answers err. A name-validation rejection becomes a 400,
|
||||
// and its body contains only the rejected name. All other errors stay
|
||||
// 500 errors with a generic body, because fsdb wraps *os.PathError
|
||||
// values and err.Error() then sends absolute filesystem paths to the
|
||||
// client. The full error goes to the log.
|
||||
func storageError(c *gin.Context, err error) {
|
||||
if _, ok := errors.AsType[*fsdb.InvalidStorageNameError](err); ok {
|
||||
setHSTS(c)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return
|
||||
}
|
||||
slog.Error("storage operation failed", "error", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "internal error"})
|
||||
}
|
||||
|
||||
// rejectInvalidStorageName answers a 400 when name does not obey
|
||||
// storage-name validation. An empty name passes, because the fields
|
||||
// that this guards are optional.
|
||||
func rejectInvalidStorageName(c *gin.Context, name string) bool {
|
||||
if name == "" {
|
||||
return false
|
||||
}
|
||||
if err := fsdb.ValidateStorageName(name); err != nil {
|
||||
setHSTS(c)
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// NewStorageHandler creates a new StorageHandler
|
||||
func NewStorageHandler[T any](r *gin.Engine, entityType string, storage db.Storage[T]) (ret *StorageHandler[T]) {
|
||||
ret = &StorageHandler[T]{storage: storage}
|
||||
|
|
@ -70,7 +31,7 @@ func (h *StorageHandler[T]) Get(c *gin.Context) {
|
|||
name := c.Param("name")
|
||||
item, err := h.storage.Get(name)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, item)
|
||||
|
|
@ -80,7 +41,7 @@ func (h *StorageHandler[T]) Get(c *gin.Context) {
|
|||
func (h *StorageHandler[T]) GetNames(c *gin.Context) {
|
||||
names, err := h.storage.GetNames()
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, names)
|
||||
|
|
@ -91,20 +52,15 @@ func (h *StorageHandler[T]) Delete(c *gin.Context) {
|
|||
name := c.Param("name")
|
||||
err := h.storage.Delete(name)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
}
|
||||
|
||||
// Exists handles the GET /storage/exists/:name route. The storage
|
||||
// Exists contract cannot report an invalid name, and the handler must
|
||||
// validate the name itself. An invalid name is a 400, not a "false".
|
||||
// Exists handles the GET /storage/exists/:name route
|
||||
func (h *StorageHandler[T]) Exists(c *gin.Context) {
|
||||
name := c.Param("name")
|
||||
if rejectInvalidStorageName(c, name) {
|
||||
return
|
||||
}
|
||||
exists := h.storage.Exists(name)
|
||||
c.JSON(http.StatusOK, exists)
|
||||
}
|
||||
|
|
@ -115,7 +71,7 @@ func (h *StorageHandler[T]) Rename(c *gin.Context) {
|
|||
newName := c.Param("newName")
|
||||
err := h.storage.Rename(oldName, newName)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
|
|
@ -131,14 +87,14 @@ func (h *StorageHandler[T]) Save(c *gin.Context) {
|
|||
|
||||
content, err := io.ReadAll(body)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Save the content to storage
|
||||
err = h.storage.Save(name, content)
|
||||
if err != nil {
|
||||
storageError(c, err)
|
||||
c.JSON(http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
c.Status(http.StatusOK)
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@ type PatternsLoader struct {
|
|||
|
||||
func (o *PatternsLoader) configure() (err error) {
|
||||
o.pathPatternsPrefix = fmt.Sprintf("%v/", o.DefaultFolder.Value)
|
||||
// Use a consistent temp folder name regardless of the source path structure
|
||||
tempDir, err := os.MkdirTemp("", "fabric-patterns-")
|
||||
if err != nil {
|
||||
return fmt.Errorf(i18n.T("patterns_failed_create_temp_folder"), err)
|
||||
}
|
||||
o.tempPatternsFolder = tempDir
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -89,15 +96,6 @@ func (o *PatternsLoader) PopulateDB() (err error) {
|
|||
fmt.Println()
|
||||
fmt.Println()
|
||||
|
||||
// Create the temp folder here, not in configure(), so invocations that
|
||||
// do not download patterns do not leak an empty directory (issue #2190).
|
||||
var tempDir string
|
||||
if tempDir, err = os.MkdirTemp("", "fabric-patterns-"); err != nil {
|
||||
return fmt.Errorf(i18n.T("patterns_failed_create_temp_folder"), err)
|
||||
}
|
||||
o.tempPatternsFolder = tempDir
|
||||
defer os.RemoveAll(tempDir)
|
||||
|
||||
originalPath := o.DefaultFolder.Value
|
||||
if err = o.gitCloneAndCopy(); err != nil {
|
||||
return fmt.Errorf(i18n.T("patterns_failed_download_from_git"), err)
|
||||
|
|
|
|||
|
|
@ -1,63 +0,0 @@
|
|||
package tools
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
|
||||
)
|
||||
|
||||
// Configure runs on every fabric invocation via the plugin registry. It must
|
||||
// not create the patterns temp directory; only PopulateDB uses it.
|
||||
func TestConfigureDoesNotCreateTempDir(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
t.Setenv("TMPDIR", tmp)
|
||||
t.Setenv("TMP", tmp)
|
||||
t.Setenv("TEMP", tmp)
|
||||
|
||||
loader := NewPatternsLoader(&fsdb.PatternsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Dir: t.TempDir()},
|
||||
})
|
||||
if err := loader.Configure(); err != nil {
|
||||
t.Fatalf("Configure() failed: %v", err)
|
||||
}
|
||||
|
||||
matches, err := filepath.Glob(filepath.Join(tmp, "fabric-patterns-*"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if len(matches) != 0 {
|
||||
t.Errorf("Configure() created temp directories: %v", matches)
|
||||
}
|
||||
}
|
||||
|
||||
// PopulateDB must create the temp directory lazily and remove it when done,
|
||||
// even on failure.
|
||||
func TestPopulateDBCleansUpTempDir(t *testing.T) {
|
||||
tmp := t.TempDir()
|
||||
t.Setenv("TMPDIR", tmp)
|
||||
t.Setenv("TMP", tmp)
|
||||
t.Setenv("TEMP", tmp)
|
||||
|
||||
loader := NewPatternsLoader(&fsdb.PatternsEntity{
|
||||
StorageEntity: &fsdb.StorageEntity{Dir: t.TempDir()},
|
||||
})
|
||||
if err := loader.Configure(); err != nil {
|
||||
t.Fatalf("Configure() failed: %v", err)
|
||||
}
|
||||
// Point at an invalid repo so PopulateDB fails fast without network.
|
||||
loader.DefaultGitRepoUrl.Value = filepath.Join(t.TempDir(), "no-such-repo")
|
||||
|
||||
if err := loader.PopulateDB(); err == nil {
|
||||
t.Fatal("PopulateDB() unexpectedly succeeded with invalid repo")
|
||||
}
|
||||
|
||||
entries, err := os.ReadDir(tmp)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, e := range entries {
|
||||
t.Errorf("PopulateDB() left temp entry behind: %v", e.Name())
|
||||
}
|
||||
}
|
||||
|
|
@ -5,8 +5,8 @@ schema = 3
|
|||
version = "v0.123.0"
|
||||
hash = "sha256-OjuvlqYrGvoUiXO/5ALbMxHvXRlrhDjLby1BN/nuhAw="
|
||||
[mod."cloud.google.com/go/auth"]
|
||||
version = "v0.23.2"
|
||||
hash = "sha256-D28j2x5WFAg56/zTI9jGTH9MDUhteBo/RP1OKRKjB3s="
|
||||
version = "v0.20.0"
|
||||
hash = "sha256-HlKu/WvrK7oj5hgaAkpv9WAufMLfEVLiRJAdM5W6GVU="
|
||||
[mod."cloud.google.com/go/auth/oauth2adapt"]
|
||||
version = "v0.2.8"
|
||||
hash = "sha256-GoXFqAbp1WO1tDj07PF5EyxDYvCBP0l0qwxY2oV2hfc="
|
||||
|
|
@ -17,17 +17,17 @@ schema = 3
|
|||
version = "v1.0.2"
|
||||
hash = "sha256-p6jdiHlLEfZES8vJnDywG4aVzIe16p0CU6iglglIweA="
|
||||
[mod."github.com/Azure/azure-sdk-for-go/sdk/azcore"]
|
||||
version = "v1.23.0"
|
||||
hash = "sha256-eOzqLjG50rNk/gZ264KPMNq0BaSuzH4saQBbvaWJpOQ="
|
||||
version = "v1.21.0"
|
||||
hash = "sha256-D4o1uNsQrI4HiSxpfQUL2dNr5/TT3MmF3bRmP1UibNE="
|
||||
[mod."github.com/Azure/azure-sdk-for-go/sdk/azidentity"]
|
||||
version = "v1.14.0"
|
||||
hash = "sha256-oUoSbjql472sT24daGzICuBMEQLjpHXooGT83KtnUfc="
|
||||
version = "v1.13.1"
|
||||
hash = "sha256-PGB5CIcrLDr4BssBXNCnd1mOzkfQBhrfJqUh1f3ofK8="
|
||||
[mod."github.com/Azure/azure-sdk-for-go/sdk/internal"]
|
||||
version = "v1.12.0"
|
||||
hash = "sha256-jX/JR3WToz0vEWowfDBVjpul9EE2V2xGF1EfckrhDus="
|
||||
[mod."github.com/AzureAD/microsoft-authentication-library-for-go"]
|
||||
version = "v1.9.0"
|
||||
hash = "sha256-K29z7ruv8vG4PIz6O4DiXBenh/NAfySIrjb8ywl037A="
|
||||
version = "v1.7.1"
|
||||
hash = "sha256-2FLYe0WI3C5hJ/wi1mqO1zdsmAZUequ1qaktsn0J+P8="
|
||||
[mod."github.com/KyleBanks/depth"]
|
||||
version = "v1.2.1"
|
||||
hash = "sha256-czR52MfeKA2FdStXCebTMQRKT8jaWQcbV214O3j49qU="
|
||||
|
|
@ -38,11 +38,11 @@ schema = 3
|
|||
version = "v1.4.1"
|
||||
hash = "sha256-6iGAFCjoNveY+ipbKqq2gt+RXpi2eQyPXAY01rxPcWc="
|
||||
[mod."github.com/andybalholm/cascadia"]
|
||||
version = "v1.3.4"
|
||||
hash = "sha256-CpXE/C+gW7V8MXREZguszzAQumizrCXGTdDRPBaflCs="
|
||||
version = "v1.3.3"
|
||||
hash = "sha256-jv7ZshpSd7FZzKKN6hqlUgiR8C3y85zNIS/hq7g76Ho="
|
||||
[mod."github.com/anthropics/anthropic-sdk-go"]
|
||||
version = "v1.67.0"
|
||||
hash = "sha256-mdEVuL2R8bhhrNAfAiHrvyew/cS0FTBtnsV/LFhC/LY="
|
||||
version = "v1.34.0"
|
||||
hash = "sha256-uHnmsIdszZiiYQ3mgqNdzKgtuYJV58GJ7sdtWCVgQ8Y="
|
||||
[mod."github.com/araddon/dateparse"]
|
||||
version = "v0.0.0-20210429162001-6b43995a97de"
|
||||
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
|
||||
|
|
@ -50,86 +50,86 @@ schema = 3
|
|||
version = "v0.1.4"
|
||||
hash = "sha256-ZZ7U5X0gWOu8zcjZcWbcpzGOGdycwq0TjTFh/eZHjXk="
|
||||
[mod."github.com/aws/aws-sdk-go-v2"]
|
||||
version = "v1.44.0"
|
||||
hash = "sha256-0GXyEXecrEAKw1FxElG7rTPyXns5MpgvtEJFO8PSh+0="
|
||||
version = "v1.41.5"
|
||||
hash = "sha256-gPtAxzRKF5Uep8f1NElsuXrWVePOoU3bxfPv5CRDwvI="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream"]
|
||||
version = "v1.7.20"
|
||||
hash = "sha256-ICB2yD83Gl628LRkg3HhYlDtsL4xgGhOmphZ5D0bJXI="
|
||||
version = "v1.7.8"
|
||||
hash = "sha256-UW9P6XKdTv9MXG8NcnZFli6RVZ1s6LXfD9j3EUBr1FE="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/config"]
|
||||
version = "v1.32.40"
|
||||
hash = "sha256-aCTxFSDt/1hSgFSAAwHQzjSrOcSDg0lAt1ely0Eb6X4="
|
||||
version = "v1.32.14"
|
||||
hash = "sha256-y5sH8w7E80cT6ZWy7u6TVzkegd1jYPK0syi/QgF7sH4="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/credentials"]
|
||||
version = "v1.19.39"
|
||||
hash = "sha256-zPAy8z+s46o9En+df+FhYQCNNP58S+VLGAWyBIMHx5k="
|
||||
version = "v1.19.14"
|
||||
hash = "sha256-1m4tRo0bKEgaIh3uYufB6i1xAEMMqXbsPLjrHpQAsxs="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/feature/ec2/imds"]
|
||||
version = "v1.18.40"
|
||||
hash = "sha256-LXOaz48PXTgjwbFYQ9cUc1Y9cmPODypl6SpZz9GLHZg="
|
||||
version = "v1.18.21"
|
||||
hash = "sha256-KIQqh21N0WhlDs5hM+Sle3IocWHgn9F9oYwl70/t8ko="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/configsources"]
|
||||
version = "v1.4.40"
|
||||
hash = "sha256-1EmqriwNg/7JCqpj7vnGB2oHgMrm/EgkLlGMhBTNh3k="
|
||||
version = "v1.4.21"
|
||||
hash = "sha256-oVZq0A/muPG2CKD1HLeQ/kuHAESSSwbIFB9V181tKCc="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/endpoints/v2"]
|
||||
version = "v2.7.40"
|
||||
hash = "sha256-XnMvLK1GmJcPNLov91EBKh+xpwIRk6DqlzXiI+epyd8="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/v4a"]
|
||||
version = "v1.4.41"
|
||||
hash = "sha256-tgerq8bZzDemsJsVdBzHCEm/S2TyPpdSmG+uwCd9XJ0="
|
||||
version = "v2.7.21"
|
||||
hash = "sha256-M4SiYSVAQU3wXKdIc0aW2Mv2zgGwXvd/+3oyqRKzKQc="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/internal/ini"]
|
||||
version = "v1.8.6"
|
||||
hash = "sha256-oIRPqu99vnGINAWKnCEytpv7N0gRWO7S72tb1r8oxvk="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/bedrock"]
|
||||
version = "v1.68.0"
|
||||
hash = "sha256-NKNT8mKBH3TDrCzH4WD1ukHEp+9tzojxrxpLXajiZ/8="
|
||||
version = "v1.59.0"
|
||||
hash = "sha256-hp2b+cOzaIz5gb2VuqOLsh50F0t0dsOTR3+S+0GOBDU="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/bedrockruntime"]
|
||||
version = "v1.58.0"
|
||||
hash = "sha256-1vapY7IXJvcsWEn4rDaI8nSIfPCmuDHZPD9gP0DYHYA="
|
||||
version = "v1.50.4"
|
||||
hash = "sha256-Q/M6bJAfC7RZq+0lEQvT8xOcmJ6n337C62pnNRho0pQ="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding"]
|
||||
version = "v1.13.19"
|
||||
hash = "sha256-LfifyqFh49PPH0asF1TJz3QJYHTwxQttthUD5p4z2YM="
|
||||
version = "v1.13.7"
|
||||
hash = "sha256-AfYJdpmnW01Bk/jfHATlNU6lddjqcigFkHw/zcT9WO4="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/internal/presigned-url"]
|
||||
version = "v1.13.40"
|
||||
hash = "sha256-l88t3s/MssSelRkyukz4fYSs7mZCOvfCeNrsfzdyKuI="
|
||||
version = "v1.13.21"
|
||||
hash = "sha256-FE2sj0e8hoQ9cVzCnbGRZF068hkQI3Om9yUB/kEB+H0="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/signin"]
|
||||
version = "v1.6.0"
|
||||
hash = "sha256-NxIbOpqkwWKLSlZJx/dRoNMQ0QDh6jolC8xKJTxxo9A="
|
||||
version = "v1.0.9"
|
||||
hash = "sha256-K2n7i40z1gnT6xVWrFUSGG1y0guHzY+cD1MipLWU1F8="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/sso"]
|
||||
version = "v1.34.0"
|
||||
hash = "sha256-yRdflsmsXNXki2xJLEtQur6Dpbb9N3beLdXD5Vu2Ss0="
|
||||
version = "v1.30.15"
|
||||
hash = "sha256-kVLP0yADAXOWET9UN2AiaVUs3xd47e8hXLXHuEo1gqY="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/ssooidc"]
|
||||
version = "v1.39.0"
|
||||
hash = "sha256-XGRs+nBRUfxyuCbDJ7QnV738UPMm2nSbxbLtK19hqY0="
|
||||
version = "v1.35.19"
|
||||
hash = "sha256-WwfJCRz8pkBmFfxu3pJMTmRoOf/Kzt1450G11vcnyO8="
|
||||
[mod."github.com/aws/aws-sdk-go-v2/service/sts"]
|
||||
version = "v1.46.0"
|
||||
hash = "sha256-7j2UmV1FHIcJ/K+iUhGGO2Q+dhuoHlOxR1TuCf4M68Y="
|
||||
version = "v1.41.10"
|
||||
hash = "sha256-T1F3RJd3iQSkCBI/jYmHrXi2JMWWuRCuYIymlXFhylI="
|
||||
[mod."github.com/aws/smithy-go"]
|
||||
version = "v1.28.1"
|
||||
hash = "sha256-52wJGnuZVxhZtqgp4Gf7mgYrK6DOL8hFgtjo8e1NlCA="
|
||||
version = "v1.24.3"
|
||||
hash = "sha256-sruA0NYq8eTnQ121ezdKZUvtwiMdPYT3dvIlgGwzVAA="
|
||||
[mod."github.com/bahlo/generic-list-go"]
|
||||
version = "v0.2.0"
|
||||
hash = "sha256-BIzqwG61hnMDknZOn/5+VX09yemzFzMjhPF48XoALto="
|
||||
[mod."github.com/buger/jsonparser"]
|
||||
version = "v1.6.1"
|
||||
hash = "sha256-barqoLEuh2FBMNG4kIjK3oRs8Izq4eVotpbzHPliqXc="
|
||||
version = "v1.1.2"
|
||||
hash = "sha256-zyB2AEJX1ZuXyZ6vh45KOe9NSyPNQBrqVs45e3mYjo4="
|
||||
[mod."github.com/bytedance/gopkg"]
|
||||
version = "v0.1.4"
|
||||
hash = "sha256-GpfG8K5YTWAt5MRg7RvpcemU7jfPV0A+mMbxSfA+2ws="
|
||||
[mod."github.com/bytedance/sonic"]
|
||||
version = "v1.15.3"
|
||||
hash = "sha256-/LTRsTbgpQPLEMX7CRIMxi/Unh9VAyys6QuALGWwgMc="
|
||||
version = "v1.15.0"
|
||||
hash = "sha256-zxpmkDsKpW1bcYuv/Ogm8wXMAZt1yKDALt2MGLYZtOI="
|
||||
[mod."github.com/bytedance/sonic/loader"]
|
||||
version = "v0.5.2"
|
||||
hash = "sha256-tsrtIbjYXZadwLzlL3ZmC4DEytwmT1tvPDQT/nwNb20="
|
||||
version = "v0.5.1"
|
||||
hash = "sha256-fbUguA0wNj+aggu3oHaEzkduQVO4oawcvjohfgUmha8="
|
||||
[mod."github.com/cespare/xxhash/v2"]
|
||||
version = "v2.3.0"
|
||||
hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY="
|
||||
[mod."github.com/cloudflare/circl"]
|
||||
version = "v1.6.5"
|
||||
hash = "sha256-nrD+9cNt6xEohb0EA2bZCuSM03GXYPg/YMD0Y+HF8WU="
|
||||
version = "v1.6.3"
|
||||
hash = "sha256-XZm4EastgX67Dgm5BpOEW/PY4aLcHM/O8+Xbz26PuTY="
|
||||
[mod."github.com/cloudwego/base64x"]
|
||||
version = "v0.1.7"
|
||||
hash = "sha256-zhZGPCTM8HiLALQfaE/L7B0IwbXZxClDVLCFt5zbusE="
|
||||
version = "v0.1.6"
|
||||
hash = "sha256-VzYJsGubsDk3FAMH6e0Xk0Cl4HtUFHHmWkVASUYidsI="
|
||||
[mod."github.com/coder/websocket"]
|
||||
version = "v1.8.15"
|
||||
hash = "sha256-6QnIQMMUSzrDXaUfO+9cl4uDh0SHizFUf4GpHmJedtk="
|
||||
version = "v1.8.14"
|
||||
hash = "sha256-3lTpVWweJJ+VjIIwcK9Ca+mWrbAYPxi4tx7anP1sFYw="
|
||||
[mod."github.com/cyphar/filepath-securejoin"]
|
||||
version = "v0.7.0"
|
||||
hash = "sha256-LMTkRIE3ZkhYDxfDt/l8EZugyoyji1u620BYzo8dd9E="
|
||||
version = "v0.6.1"
|
||||
hash = "sha256-obqip8c1c9mjXFznyXF8aDnpcMw7ttzv+e28anCa/v0="
|
||||
[mod."github.com/davecgh/go-spew"]
|
||||
version = "v1.1.2-0.20180830191138-d8f796af33cc"
|
||||
hash = "sha256-fV9oI51xjHdOmEx6+dlq7Ku2Ag+m/bmbzPo6A4Y74qc="
|
||||
|
|
@ -137,11 +137,11 @@ schema = 3
|
|||
version = "v1.18.1"
|
||||
hash = "sha256-hGDKddjLj+5dn2woHtXKUdd49/3xdsqnhx7VEdCu1m4="
|
||||
[mod."github.com/felixge/httpsnoop"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-oePAjhI+dYRrMQAeUWnJ3o+PdHFU1tk1Z/R3/kGdjnw="
|
||||
version = "v1.0.4"
|
||||
hash = "sha256-c1JKoRSndwwOyOxq9ddCe+8qn7mG9uRq2o/822x5O/c="
|
||||
[mod."github.com/gabriel-vasile/mimetype"]
|
||||
version = "v1.4.15"
|
||||
hash = "sha256-MSmdWIAtF32qz02SDQx+cuj8EZCFUemTCAQPFq42JN0="
|
||||
version = "v1.4.13"
|
||||
hash = "sha256-uKTnpctmnEIzzxysqINbFN8SdKwLTrPQsRiQjgZcvxk="
|
||||
[mod."github.com/gin-contrib/sse"]
|
||||
version = "v1.1.1"
|
||||
hash = "sha256-bkz6h87WNRO7inpfBo2DSrw2tnc678CwBswKzsl++8s="
|
||||
|
|
@ -152,47 +152,47 @@ schema = 3
|
|||
version = "v1.5.1-0.20230307220236-3a3c6141e376"
|
||||
hash = "sha256-f4k0gSYuo0/q3WOoTxl2eFaj7WZpdz29ih6CKc8Ude8="
|
||||
[mod."github.com/go-git/go-billy/v5"]
|
||||
version = "v5.9.1"
|
||||
hash = "sha256-VW8mfdLjvAuiooR2WV7P0IoOosAvtDZvFWcGQJJVSSY="
|
||||
version = "v5.8.0"
|
||||
hash = "sha256-c4ScYknbM+MPHF+2vqnGMBuBLc4U1iYuyymq61Vts9Y="
|
||||
[mod."github.com/go-git/go-git/v5"]
|
||||
version = "v5.19.2"
|
||||
hash = "sha256-2du2hn5JDTEEYTWuGX+heSftkmd7HoaJXyWJG4bZeSM="
|
||||
version = "v5.17.2"
|
||||
hash = "sha256-Dr4R309hkAZ8wZIr4PcL5ftffjKGy+UzJx3riJbCC/k="
|
||||
[mod."github.com/go-logr/logr"]
|
||||
version = "v1.4.4"
|
||||
hash = "sha256-q9HX9aelONTLsywyLY4Wc5UYCK+wG0FbSzFWisCYiIA="
|
||||
version = "v1.4.3"
|
||||
hash = "sha256-Nnp/dEVNMxLp3RSPDHZzGbI8BkSNuZMX0I0cjWKXXLA="
|
||||
[mod."github.com/go-logr/stdr"]
|
||||
version = "v1.2.2"
|
||||
hash = "sha256-rRweAP7XIb4egtT1f2gkz4sYOu7LDHmcJ5iNsJUd0sE="
|
||||
[mod."github.com/go-openapi/jsonpointer"]
|
||||
version = "v1.0.0"
|
||||
hash = "sha256-he4+Ue27HuNS0imS55XaS4g54v2OrKFJppdhz8cO94s="
|
||||
version = "v0.22.5"
|
||||
hash = "sha256-btK8c3hxbO9mAlvRYuAaNdjaHNYyLvNl/RfNxPsqQuw="
|
||||
[mod."github.com/go-openapi/jsonreference"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-UMGGlJPWjL7SoeLd51PLyYnUuQMz1Jx41MuULCwQUp4="
|
||||
version = "v0.21.5"
|
||||
hash = "sha256-fBeVESt+JKLthLDTyuABzPV/hOmg4t8dPtwvAs1Ojog="
|
||||
[mod."github.com/go-openapi/spec"]
|
||||
version = "v0.22.11"
|
||||
hash = "sha256-QmZOssbAZDUKdk/hZUmOxDYIa4u2VCIGm6E8/gY1y4Y="
|
||||
version = "v0.22.4"
|
||||
hash = "sha256-nwNLmtrjR3w+vFGlBFSq4vM2ZsDQS4RDBQGYmlGv7BY="
|
||||
[mod."github.com/go-openapi/swag/conv"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-hOh54OqE1yVfy5O2FRuHZZKejpUk26eM8qzB0ndoJhM="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-+yLC40AK2pyn62zStk7Q13Bsb4/HDsJUKTTNBSWSTvg="
|
||||
[mod."github.com/go-openapi/swag/jsonname"]
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-ypcI24qrUOd0lbZUJcFByQr07U7WtQvIu/YhuewUWDo="
|
||||
[mod."github.com/go-openapi/swag/jsonutils"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-Yj1UfqLEwJA5PWQTs3EbcohqxlknhFFwkYJXoO/Y6c0="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-e6OOoTIH/zrI/unpNIu3foYEvSWKb7Jvf+6E6/nvpMg="
|
||||
[mod."github.com/go-openapi/swag/loading"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-ig2w4q11AOWerFTVN6a2MdZ4mKiFXWkeiTCDuWFwUxY="
|
||||
[mod."github.com/go-openapi/swag/pools"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-NhaFtD29d759M/H5AiYiHAxn6s4bkfYW63B7xFpkGvA="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-gwy+xJkF3PHT5YMYnXgSX9XhuvwwOVpH60QTLsAh6/E="
|
||||
[mod."github.com/go-openapi/swag/stringutils"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-VLkfHTvfO+yU6VMNdzSp3rb2v/A4OqpScBHq77GoOA8="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-Ze2Y056Imqyq6kHPcACuqHt992WbXfC9LDziSCFuO/c="
|
||||
[mod."github.com/go-openapi/swag/typeutils"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-xsKC/xVt7LdZIHOv+h5KwJEULyebSFPTghUj1LC04uQ="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-A1mGLvoaLCT0iORn4tiyKWB8L69dMJzFjBFpt80Xzkg="
|
||||
[mod."github.com/go-openapi/swag/yamlutils"]
|
||||
version = "v0.29.1"
|
||||
hash = "sha256-nrru5csGEKnr+ff28sVdkJNubhjbLwY8cPp9JqSl1DE="
|
||||
version = "v0.25.5"
|
||||
hash = "sha256-+EumuV+qkhYn08XfR1ngIKMh79Mkj8vItpg0y0spX+c="
|
||||
[mod."github.com/go-playground/locales"]
|
||||
version = "v0.14.1"
|
||||
hash = "sha256-BMJGAexq96waZn60DJXZfByRHb8zA/JP/i6f/YrW9oQ="
|
||||
|
|
@ -200,8 +200,8 @@ schema = 3
|
|||
version = "v0.18.1"
|
||||
hash = "sha256-2/B2qP51zfiY+k8G0w0D03KXUc7XpWj6wKY7NjNP/9E="
|
||||
[mod."github.com/go-playground/validator/v10"]
|
||||
version = "v10.30.3"
|
||||
hash = "sha256-g5qbJfl8DPwWOZT+EQjMPc8ABhtkf/wS5IxxJt6T+94="
|
||||
version = "v10.30.2"
|
||||
hash = "sha256-2Jl5h9S8RtyMwBScZxLsJJde0FEkTdnYxV8/TY7I1hM="
|
||||
[mod."github.com/go-shiori/dom"]
|
||||
version = "v0.0.0-20230515143342-73569d674e1c"
|
||||
hash = "sha256-4lm9KZfR2XnfZU9KTG+4jqLYZqbfL74AMO4y3dKpIbg="
|
||||
|
|
@ -239,23 +239,20 @@ schema = 3
|
|||
version = "v1.6.0"
|
||||
hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw="
|
||||
[mod."github.com/googleapis/enterprise-certificate-proxy"]
|
||||
version = "v0.3.21"
|
||||
hash = "sha256-h8t4jzU7hRY/KaSI51xAkxX2934i5k6XVvpuNPef4iE="
|
||||
version = "v0.3.14"
|
||||
hash = "sha256-yuNtkMq+gqN1vZ9alekxe47vAEbTCBHXO6Mwwv/lwrE="
|
||||
[mod."github.com/googleapis/gax-go/v2"]
|
||||
version = "v2.24.0"
|
||||
hash = "sha256-uDcY93yvQS3wDkhM85bAxSo67Z+cWlwR1Vnv9hZRa5o="
|
||||
version = "v2.21.0"
|
||||
hash = "sha256-PZ9OHKj/+AroeCWkhJCzqDKzU1x7b3Eh9WCwefhTmN0="
|
||||
[mod."github.com/gorilla/websocket"]
|
||||
version = "v1.5.3"
|
||||
hash = "sha256-vTIGEFMEi+30ZdO6ffMNJ/kId6pZs5bbyqov8xe9BM0="
|
||||
[mod."github.com/hasura/go-graphql-client"]
|
||||
version = "v0.16.0"
|
||||
hash = "sha256-v3aXg7ae5q7G9qfbahLXrWhFDtcb2lmNsnChoLcrhrE="
|
||||
version = "v0.15.1"
|
||||
hash = "sha256-Cy8pWsgdlUy1LwOjXzZItYVNKRjJAdCgJTClrQOk7OU="
|
||||
[mod."github.com/inconshreveable/mousetrap"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-XWlYH0c8IcxAwQTnIi6WYqq44nOKUylSWxWO/vi+8pE="
|
||||
[mod."github.com/invopop/jsonschema"]
|
||||
version = "v0.14.0"
|
||||
hash = "sha256-gndZdk5eUqIsFMDsYjcDEbKpY5XCC4sLLZZ55Z4KCHk="
|
||||
[mod."github.com/jbenet/go-context"]
|
||||
version = "v0.0.0-20150711004518-d14ea06fba99"
|
||||
hash = "sha256-VANNCWNNpARH/ILQV9sCQsBWgyL2iFT+4AHZREpxIWE="
|
||||
|
|
@ -275,23 +272,23 @@ schema = 3
|
|||
version = "v1.6.0"
|
||||
hash = "sha256-i/EYNJx0+HbAGFVoiKV4QF/zqb4fWewh+bpBKUkXDCc="
|
||||
[mod."github.com/klauspost/cpuid/v2"]
|
||||
version = "v2.4.0"
|
||||
hash = "sha256-nvTeRVxDekVNLd25aJ2NslBnG7nT25ewoSTk3iW5Xok="
|
||||
version = "v2.3.0"
|
||||
hash = "sha256-50JhbQyT67BK38HIdJihPtjV7orYp96HknI2VP7A9Yc="
|
||||
[mod."github.com/kylelemons/godebug"]
|
||||
version = "v1.1.0"
|
||||
hash = "sha256-DJ0re9mGqZb6PROQI8NPC0JVyDHdZ/y4uehNH7MbczY="
|
||||
[mod."github.com/leodido/go-urn"]
|
||||
version = "v1.5.0"
|
||||
hash = "sha256-CEeM2l10p4WK0GoRxIzKcx+6hn1eQj1d3MyyY35XNh0="
|
||||
version = "v1.4.0"
|
||||
hash = "sha256-Q6kplWkY37Tzy6GOme3Wut40jFK4Izun+ij/BJvcEu0="
|
||||
[mod."github.com/mailru/easyjson"]
|
||||
version = "v0.9.2"
|
||||
hash = "sha256-178Wz0XN1ghBYXWYQMSbByqPfsUc0mAf+p6j6mAJ4jg="
|
||||
[mod."github.com/mattn/go-isatty"]
|
||||
version = "v0.0.24"
|
||||
hash = "sha256-pmq8KKIb2+qMF3E1cR2B7NAj5joF2tZduyXjIfIlkTk="
|
||||
version = "v0.0.21"
|
||||
hash = "sha256-B70ZCZYoajKE8xxKPh9sgyVz2URKi0EyaOX8XFDqWKw="
|
||||
[mod."github.com/mattn/go-sqlite3"]
|
||||
version = "v1.14.50"
|
||||
hash = "sha256-olsWcNOIhMP67ZfuYvxxe3AHvyynAUmJ6WbDXfc1VbE="
|
||||
version = "v1.14.42"
|
||||
hash = "sha256-nKwj5/qKXo+r7ljhLaM+2SlgqsLtv8c/iN77r6NrjMg="
|
||||
[mod."github.com/modern-go/concurrent"]
|
||||
version = "v0.0.0-20180306012644-bacd9c7ef1dd"
|
||||
hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo="
|
||||
|
|
@ -302,8 +299,8 @@ schema = 3
|
|||
version = "v2.6.1"
|
||||
hash = "sha256-ag/8GBAwqkOyIVrdlaFYLxy9dgPOq7VbactrLmzxK7E="
|
||||
[mod."github.com/ollama/ollama"]
|
||||
version = "v0.33.1"
|
||||
hash = "sha256-E297N5FElu4OEDM75FHekPr/fyxMpu+7M3GS3Robals="
|
||||
version = "v0.20.4"
|
||||
hash = "sha256-gHVHgmLjmZwfKMAfcHRGitzynUC45gVveMZmKeVDMXE="
|
||||
[mod."github.com/openai/openai-go"]
|
||||
version = "v1.12.0"
|
||||
hash = "sha256-JHLlKvDwERPf728GUXBsKU58ODgCxcxEe9TKJTGAG1w="
|
||||
|
|
@ -313,15 +310,12 @@ schema = 3
|
|||
[mod."github.com/otiai10/mint"]
|
||||
version = "v1.6.3"
|
||||
hash = "sha256-/FT3dYP2+UiW/qe1pxQ7HiS8et4+KHGPIMhc+8mHvzw="
|
||||
[mod."github.com/pb33f/ordered-map/v2"]
|
||||
version = "v2.3.1"
|
||||
hash = "sha256-eDCb6p/b7dhOB2YOshY/0EU+Do3eoUXFR9f1EMrCK8E="
|
||||
[mod."github.com/pelletier/go-toml/v2"]
|
||||
version = "v2.4.3"
|
||||
hash = "sha256-CyCjKBN+zUciGhQiaFpq54qv+NDITcuh5U2HmjArow4="
|
||||
version = "v2.3.0"
|
||||
hash = "sha256-3ftKBqSwUp5rs10NigReAJ8RxfnP4Aol45EkP0XRaa4="
|
||||
[mod."github.com/pjbgf/sha1cd"]
|
||||
version = "v0.6.0"
|
||||
hash = "sha256-WC/sYIy9Iznlra87K9Gonn6/bo4a2aE+kvldecfBPfE="
|
||||
version = "v0.5.0"
|
||||
hash = "sha256-11XBkhdciQGsQ7jEMZ6PgphRKjruTSc7ZxfOwDuPCr8="
|
||||
[mod."github.com/pkg/browser"]
|
||||
version = "v0.0.0-20240102092130-5ac0b6a4141c"
|
||||
hash = "sha256-9iaSHHpcA1fXVF5f8RlKyo1DSoHx7eGXIC2/4LFaoBY="
|
||||
|
|
@ -335,8 +329,8 @@ schema = 3
|
|||
version = "v0.6.0"
|
||||
hash = "sha256-xaxHnTKIZt1cHK5ZqTuSTOt5RNSjQB37GlrIgEGBskM="
|
||||
[mod."github.com/quic-go/quic-go"]
|
||||
version = "v0.61.0"
|
||||
hash = "sha256-y63Pou3btx73nwoKf/AHNdYgS4ahtdTnPsnih3s7OSk="
|
||||
version = "v0.59.0"
|
||||
hash = "sha256-PCXo4SV8kZ/aXXVaeRvkcJW42GO75P5/1O7UhtXS3Pk="
|
||||
[mod."github.com/samber/lo"]
|
||||
version = "v1.53.0"
|
||||
hash = "sha256-RCf4Buf357TTWQnMPSWKrfdJ4L/RqOHNBD0g3+VpMw8="
|
||||
|
|
@ -344,8 +338,8 @@ schema = 3
|
|||
version = "v1.4.0"
|
||||
hash = "sha256-rs9NKpv/qcQEMRg7CmxGdP4HGuFdBxlpWf9LbA9wS4k="
|
||||
[mod."github.com/sgaunet/perplexity-go/v2"]
|
||||
version = "v2.16.1"
|
||||
hash = "sha256-NALxOW2F57eGtxwnGj2Dhpbl7SMRcb2wHYqyk4C667M="
|
||||
version = "v2.15.0"
|
||||
hash = "sha256-cRBxrQex4bLG47OhJ7LAGBytHnWBPP83+9KXrrosEgs="
|
||||
[mod."github.com/skeema/knownhosts"]
|
||||
version = "v1.3.2"
|
||||
hash = "sha256-fEGeX/V9fC3MVjY34yuL8vn00brqIoTXLAX0x4ecBzg="
|
||||
|
|
@ -355,12 +349,9 @@ schema = 3
|
|||
[mod."github.com/spf13/pflag"]
|
||||
version = "v1.0.10"
|
||||
hash = "sha256-uDPnWjHpSrzXr17KEYEA1yAbizfcsfo5AyztY2tS6ZU="
|
||||
[mod."github.com/standard-webhooks/standard-webhooks/libraries"]
|
||||
version = "v0.0.1"
|
||||
hash = "sha256-ORbb8w6VS4Yw0vsPhVzAqCCUYyaMOPcQyyP4DBZgLzM="
|
||||
[mod."github.com/stretchr/testify"]
|
||||
version = "v1.12.1"
|
||||
hash = "sha256-9MTDdVjZMh1MJ5EH3HmAFrm24YqZayeMBgKwtaURZCc="
|
||||
version = "v1.11.1"
|
||||
hash = "sha256-sWfjkuKJyDllDEtnM8sb/pdLzPQmUYWYtmeWz/5suUc="
|
||||
[mod."github.com/swaggo/files"]
|
||||
version = "v1.0.1"
|
||||
hash = "sha256-bNBmpJaM7g1BNwd7VxNIRSdY35NKSXhYHGfnZsSEUZ8="
|
||||
|
|
@ -371,8 +362,8 @@ schema = 3
|
|||
version = "v1.16.6"
|
||||
hash = "sha256-na5+ZylxPFKoMJcgx8nmSJXMMkKFCyuh1qSkb7Qth/I="
|
||||
[mod."github.com/tidwall/gjson"]
|
||||
version = "v1.19.0"
|
||||
hash = "sha256-H0tqaftwuKVjuvlPov1Fr1a5W0JZmWUkOlPxY5qtSTE="
|
||||
version = "v1.18.0"
|
||||
hash = "sha256-CO6hqDu8Y58Po6A01e5iTpwiUBQ5khUZsw7czaJHw0I="
|
||||
[mod."github.com/tidwall/match"]
|
||||
version = "v1.2.0"
|
||||
hash = "sha256-O2wTU0SmNIEEOxfncl2BW2czgWeIW5vqR6+A7dtNtXI="
|
||||
|
|
@ -386,8 +377,8 @@ schema = 3
|
|||
version = "v0.15.1"
|
||||
hash = "sha256-HLk6oUe7EoITrNvP0y8D6BtIgIcmDZYtb/xl/dufIoY="
|
||||
[mod."github.com/ugorji/go/codec"]
|
||||
version = "v1.3.2"
|
||||
hash = "sha256-/W4sowusGHO1Fb4mlqNQlfqgl278E0BqgMUzexkzWVw="
|
||||
version = "v1.3.1"
|
||||
hash = "sha256-VQtXVaKxXjm5Q60hCgVKZxNywl6SJFPqju6JNjADp4w="
|
||||
[mod."github.com/wk8/go-ordered-map/v2"]
|
||||
version = "v2.1.8"
|
||||
hash = "sha256-v7/5+7lAypZfgClXgWxhxtA1skQq9o+1yrI+V0o1j2o="
|
||||
|
|
@ -395,77 +386,77 @@ schema = 3
|
|||
version = "v0.3.3"
|
||||
hash = "sha256-l3pGB6IdzcPA/HLk93sSN6NM2pKPy+bVOoacR5RC2+c="
|
||||
[mod."go.mongodb.org/mongo-driver/v2"]
|
||||
version = "v2.8.2"
|
||||
hash = "sha256-1syL133Npfw250xMeOfEhExvEYFU7GTnWsdHCOOmNuA="
|
||||
version = "v2.5.0"
|
||||
hash = "sha256-qwmn+cW47cFz9AsTvbOJjnkhS+d7MlbTK8/vI8fTdPQ="
|
||||
[mod."go.opentelemetry.io/auto/sdk"]
|
||||
version = "v1.2.1"
|
||||
hash = "sha256-73bFYhnxNf4SfeQ52ebnwOWywdQbqc9lWawCcSgofvE="
|
||||
[mod."go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"]
|
||||
version = "v0.71.0"
|
||||
hash = "sha256-2vwtDspRM7jZyvxclBmKTCDZ31qw/TiG+HHI6Z8ub2s="
|
||||
version = "v0.68.0"
|
||||
hash = "sha256-u3x5NLbkkAUyrcKyxwX+LFOZOzH9TtzTRil5pPCv8sw="
|
||||
[mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"]
|
||||
version = "v0.71.0"
|
||||
hash = "sha256-E5YtRLrs1/LqpvPt6LzC+sDpmxSROgpNmgca5s6iyCg="
|
||||
version = "v0.68.0"
|
||||
hash = "sha256-n90fV18xmJMBnxzLF4S0rqEdAcAPmLl835eFGoQc3K8="
|
||||
[mod."go.opentelemetry.io/otel"]
|
||||
version = "v1.46.0"
|
||||
hash = "sha256-2+xTacXn22hsJuMGGgXwazMbP/ZW5gkPRnf+rNUZgvQ="
|
||||
version = "v1.43.0"
|
||||
hash = "sha256-oRemJUZhA7AzfUoBbRVA32u/XhMpipxLywHoJ1qsHBs="
|
||||
[mod."go.opentelemetry.io/otel/metric"]
|
||||
version = "v1.46.0"
|
||||
hash = "sha256-cY1DXXzRfcP0kKh7oBzowbu6kbFr3jANiEVyMlD5Bt8="
|
||||
version = "v1.43.0"
|
||||
hash = "sha256-iUfx5AvN2oiqlh2v8/oFa+2jm8RX4kbb6X1EOKRyPPw="
|
||||
[mod."go.opentelemetry.io/otel/trace"]
|
||||
version = "v1.46.0"
|
||||
hash = "sha256-zfj85gCR5Z3v1N+8nlOO+6+FVShaL/7Vb/3lXjdXnnw="
|
||||
version = "v1.43.0"
|
||||
hash = "sha256-LLx1PjBGzDwZ3//Gp14R1DCMlnMCzFxnGYqVUz5jTmk="
|
||||
[mod."go.yaml.in/yaml/v3"]
|
||||
version = "v3.0.5"
|
||||
hash = "sha256-ygho+GU5kE7vPMx+dZYyNfCaeMjNxj66XmrcVf3afFE="
|
||||
[mod."go.yaml.in/yaml/v4"]
|
||||
version = "v4.0.0-rc.6"
|
||||
hash = "sha256-9aOIQpQ53KLv/Hb14104qYKLVrZUcCGLwzYp6UGLP90="
|
||||
version = "v3.0.4"
|
||||
hash = "sha256-NkGFiDPoCxbr3LFsI6OCygjjkY0rdmg5ggvVVwpyDQ4="
|
||||
[mod."golang.org/x/arch"]
|
||||
version = "v0.30.0"
|
||||
hash = "sha256-od8lOmP+IOtiO7s1VaFVMBCQfRmtXT8jqe7Yo077xDs="
|
||||
version = "v0.25.0"
|
||||
hash = "sha256-u3hH+9GYsNR4noPGBlIaNrSO6M1AbvRRABUCHvymFlU="
|
||||
[mod."golang.org/x/crypto"]
|
||||
version = "v0.55.0"
|
||||
hash = "sha256-99uV/ESeqhaDEwqbXxDpePuwzO0JoEHGhqZXZVHq/H4="
|
||||
version = "v0.50.0"
|
||||
hash = "sha256-vC1BJT7+3UBWLyEE5n3to0NKhMo6m2HGow2HiFgpQLo="
|
||||
[mod."golang.org/x/exp"]
|
||||
version = "v0.0.0-20250531010427-b6e5de432a8b"
|
||||
hash = "sha256-QaFfjyB+pogCkUkJskR9xnXwkCOU828XJRrzwwLm6Ms="
|
||||
[mod."golang.org/x/mod"]
|
||||
version = "v0.40.0"
|
||||
hash = "sha256-gmuKtxidzYWVlQYNB/8BREmajyfockaQ5/6Xk4IR32A="
|
||||
version = "v0.35.0"
|
||||
hash = "sha256-ICEQxokHywOFInDPqoP+go9l1tZSz3roknF5SXPtNV4="
|
||||
[mod."golang.org/x/net"]
|
||||
version = "v0.58.0"
|
||||
hash = "sha256-fDOkQJXURrbq2dBGa91bCUsXknCZlo3ru2JIofSOGRo="
|
||||
version = "v0.53.0"
|
||||
hash = "sha256-G9gKLmyaf6lIV429NKX+YlL6oUPJwlv+BrG6qGhzvmU="
|
||||
[mod."golang.org/x/oauth2"]
|
||||
version = "v0.36.0"
|
||||
hash = "sha256-evS7WkMrpgonmTcqtWFpC5rSKZN8O+vnAhNUs1MS9kw="
|
||||
[mod."golang.org/x/sync"]
|
||||
version = "v0.22.0"
|
||||
hash = "sha256-VZjl0fAM0p/nI81zh+pdBjzxFupx0UcKYXBBpL2ZS7k="
|
||||
version = "v0.20.0"
|
||||
hash = "sha256-ybcjhCfK6lroUM0yswUvWooW8MOQZBXyiSqoxG6Uy0Y="
|
||||
[mod."golang.org/x/sys"]
|
||||
version = "v0.47.0"
|
||||
hash = "sha256-TpbRyWWqHjddP6QzUgAbaLd2EE0S+GYNRUIDJd18r98="
|
||||
version = "v0.43.0"
|
||||
hash = "sha256-aDQXqSTZES2l/132PBxhZN4ywldpPyfm7LByYCHzzwM="
|
||||
[mod."golang.org/x/text"]
|
||||
version = "v0.41.0"
|
||||
hash = "sha256-22nHcolG87qSPahT2Ey8S5iGlCLAglE9ObYXO6XZ3ZY="
|
||||
version = "v0.36.0"
|
||||
hash = "sha256-/0t9C6Mc8kYjxweFB0us2lGKo8GovHhBiq5nlMOppC0="
|
||||
[mod."golang.org/x/time"]
|
||||
version = "v0.15.0"
|
||||
hash = "sha256-5D24A65wn7k93Jj3+918UKjB9ccmGHPBEqjD2XDB92E="
|
||||
[mod."golang.org/x/tools"]
|
||||
version = "v0.49.0"
|
||||
hash = "sha256-3oaXp2PlfuIn6/UTarnYD279YqJKTyaCWT/KD7ABjD4="
|
||||
version = "v0.43.0"
|
||||
hash = "sha256-CqwqQl9inMT0/5LJ8hmV3HNR1XdI/xBFbi3305VJUlo="
|
||||
[mod."google.golang.org/api"]
|
||||
version = "v0.294.0"
|
||||
hash = "sha256-fSc8hIUo3a92Rf7CmI3D+gL5Vk33e5alr8EIIR613bc="
|
||||
version = "v0.275.0"
|
||||
hash = "sha256-U0BzhiPrb0cShIUR9s1j6A7e0pOGTGln97Zv9ziC6z0="
|
||||
[mod."google.golang.org/genai"]
|
||||
version = "v1.70.0"
|
||||
hash = "sha256-T0jXx52Hc3aOROPpX66RGhZs0FrRg5Bhqm3WyR1zjkM="
|
||||
version = "v1.53.0"
|
||||
hash = "sha256-J3cB0lKtCJ2RK3zhDrR8q3LBUxrp5lAPe5RNaJNTc7k="
|
||||
[mod."google.golang.org/genproto/googleapis/rpc"]
|
||||
version = "v0.0.0-20260825221802-da73d73af1c5"
|
||||
hash = "sha256-CQdjYGIrgAnEDfWW3DIJF9Orf7oJdhx3G9rGg6fow88="
|
||||
version = "v0.0.0-20260406210006-6f92a3bedf2d"
|
||||
hash = "sha256-ldJTTb7hhj1mdmzTn9IEkQVwCoj3KRlENZtUSEKHABU="
|
||||
[mod."google.golang.org/grpc"]
|
||||
version = "v1.83.2"
|
||||
hash = "sha256-q5VtdeJvWM7kbIvui8ncNV/+Rm+d0y/b6XkNN0/u1is="
|
||||
version = "v1.80.0"
|
||||
hash = "sha256-+p50KGJvGWdpB/4f0h477dCAfoOL5m2PzG8BGOekVgY="
|
||||
[mod."google.golang.org/protobuf"]
|
||||
version = "v1.36.12"
|
||||
hash = "sha256-MKd0AdkWEe2A79mGPzOY5I76nMqTsGJKQNPJOKyOwkQ="
|
||||
version = "v1.36.11"
|
||||
hash = "sha256-7W+6jntfI/awWL3JP6yQedxqP5S9o3XvPgJ2XxxsIeE="
|
||||
[mod."gopkg.in/warnings.v0"]
|
||||
version = "v0.1.2"
|
||||
hash = "sha256-ATVL9yEmgYbkJ1DkltDGRn/auGAjqGOfjQyBYyUo8s8="
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
"1.4.478"
|
||||
"1.4.445"
|
||||
|
|
|
|||
|
|
@ -1,190 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
# audit-patterns.sh — Find maintenance issues in the Fabric patterns directory.
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/audit-patterns.sh [--strict] [patterns_dir]
|
||||
#
|
||||
# --strict exit 1 when any issue is found (for CI); default is always exit 0
|
||||
# -h,--help show this help
|
||||
#
|
||||
# Checks:
|
||||
# 1. Thin patterns — system.md files under 15 lines (likely stubs)
|
||||
# 2. Bloated patterns — system.md files over 50 KB (likely embedded examples)
|
||||
# 3. Stale model refs — mentions of GPT-4, ChatGPT, or other vendor-specific models
|
||||
# 4. Missing INPUT marker — pattern has no "# INPUT" section
|
||||
# 5. Locale key gaps — i18n keys present in en.json but missing from other locales
|
||||
# 6. Completion gaps — flags in flags.go with no entry in completions files
|
||||
#
|
||||
# Output: plain text report. Exit 0 even when issues found (use --strict for non-zero exit).
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
STRICT=0
|
||||
PATTERNS_DIR=""
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--strict) STRICT=1; shift ;;
|
||||
-h|--help) sed -n '2,16p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
|
||||
-*) echo "unknown option: $1 (try --help)" >&2; exit 2 ;;
|
||||
*) PATTERNS_DIR="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
PATTERNS_DIR="${PATTERNS_DIR:-$REPO_ROOT/data/patterns}"
|
||||
|
||||
if [[ ! -d "$PATTERNS_DIR" ]]; then
|
||||
echo "patterns directory not found: $PATTERNS_DIR" >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
# Colorize only when writing to a terminal, so redirected output stays clean.
|
||||
if [[ -t 1 ]]; then
|
||||
YLW='\033[0;33m'
|
||||
GRN='\033[0;32m'
|
||||
DIM='\033[2m'
|
||||
RST='\033[0m'
|
||||
else
|
||||
YLW='' GRN='' DIM='' RST=''
|
||||
fi
|
||||
|
||||
# Print paths relative to the repo root instead of scripts/../data/...
|
||||
rel() { echo "${1#"$REPO_ROOT"/}"; }
|
||||
|
||||
issues=0
|
||||
|
||||
header() { echo; echo "━━━ $1 ━━━"; }
|
||||
warn() { echo -e " ${YLW}⚠${RST} $*"; issues=$((issues + 1)); }
|
||||
info() { echo -e " ${DIM}·${RST} $*"; }
|
||||
ok() { echo -e " ${GRN}✓${RST} $*"; }
|
||||
|
||||
# ── 1. Thin patterns ──────────────────────────────────────────────────────────
|
||||
header "Thin patterns (< 15 lines)"
|
||||
found=0
|
||||
while IFS= read -r f; do
|
||||
lines=$(wc -l < "$f")
|
||||
name=$(basename "$(dirname "$f")")
|
||||
if [[ $lines -lt 15 ]]; then
|
||||
warn "$name ($lines lines) → $(rel "$f")"
|
||||
found=1
|
||||
fi
|
||||
done < <(find "$PATTERNS_DIR" -name "system.md" | sort)
|
||||
[[ $found -eq 0 ]] && ok "None found"
|
||||
|
||||
# ── 2. Bloated patterns ───────────────────────────────────────────────────────
|
||||
header "Bloated patterns (> 50 KB — likely embedded examples)"
|
||||
found=0
|
||||
while IFS= read -r f; do
|
||||
size=$(wc -c < "$f")
|
||||
name=$(basename "$(dirname "$f")")
|
||||
if [[ $size -gt 51200 ]]; then
|
||||
kb=$(( size / 1024 ))
|
||||
warn "$name (${kb} KB) → $(rel "$f")"
|
||||
found=1
|
||||
fi
|
||||
done < <(find "$PATTERNS_DIR" -name "system.md" | sort)
|
||||
[[ $found -eq 0 ]] && ok "None found"
|
||||
|
||||
# ── 3. Stale model/vendor references ─────────────────────────────────────────
|
||||
header "Stale model/vendor references"
|
||||
STALE_PATTERN='GPT-4|GPT-3|gpt-4|gpt-3|ChatGPT|OpenAI'\''s|text-davinci|gpt4|gpt3|claude-[0-9]|gemini-pro'
|
||||
found=0
|
||||
while IFS= read -r f; do
|
||||
matches=$(grep -oE "$STALE_PATTERN" "$f" 2>/dev/null | sort -u | tr '\n' ' ')
|
||||
if [[ -n "$matches" ]]; then
|
||||
name=$(basename "$(dirname "$f")")
|
||||
warn "$name → found: ${matches% }"
|
||||
found=1
|
||||
fi
|
||||
done < <(find "$PATTERNS_DIR" -name "system.md" | sort)
|
||||
[[ $found -eq 0 ]] && ok "None found"
|
||||
|
||||
# ── 4. Missing INPUT marker ───────────────────────────────────────────────────
|
||||
header "Patterns missing # INPUT section"
|
||||
found=0
|
||||
while IFS= read -r f; do
|
||||
if ! grep -q "^# INPUT" "$f" 2>/dev/null; then
|
||||
name=$(basename "$(dirname "$f")")
|
||||
warn "$name → $(rel "$f")"
|
||||
found=1
|
||||
fi
|
||||
done < <(find "$PATTERNS_DIR" -name "system.md" | sort)
|
||||
[[ $found -eq 0 ]] && ok "None found"
|
||||
|
||||
# ── 5. i18n locale key gaps ───────────────────────────────────────────────────
|
||||
LOCALES_DIR="$REPO_ROOT/internal/i18n/locales"
|
||||
if [[ -d "$LOCALES_DIR" ]]; then
|
||||
header "i18n locale key gaps (keys in en.json missing from other locales)"
|
||||
found=0
|
||||
for locale_file in "$LOCALES_DIR"/*.json; do
|
||||
lang=$(basename "$locale_file" .json)
|
||||
[[ "$lang" == "en" ]] && continue
|
||||
missing=$(python3 -c "
|
||||
import json
|
||||
with open('$LOCALES_DIR/en.json') as f:
|
||||
en = set(json.load(f).keys())
|
||||
with open('$locale_file') as f:
|
||||
other = set(json.load(f).keys())
|
||||
missing = sorted(en - other)
|
||||
if missing:
|
||||
print('\n'.join(missing))
|
||||
" 2>/dev/null)
|
||||
if [[ -n "$missing" ]]; then
|
||||
count=$(echo "$missing" | wc -l)
|
||||
warn "$lang → $count missing key(s):"
|
||||
echo "$missing" | while IFS= read -r key; do
|
||||
info " $key"
|
||||
done
|
||||
found=1
|
||||
fi
|
||||
done
|
||||
[[ $found -eq 0 ]] && ok "All locales in sync"
|
||||
fi
|
||||
|
||||
# ── 6. Shell completion gaps ──────────────────────────────────────────────────
|
||||
FLAGS_FILE="$REPO_ROOT/internal/cli/flags.go"
|
||||
BASH_FILE="$REPO_ROOT/completions/fabric.bash"
|
||||
FISH_FILE="$REPO_ROOT/completions/fabric.fish"
|
||||
ZSH_FILE="$REPO_ROOT/completions/_fabric"
|
||||
|
||||
if [[ -f "$FLAGS_FILE" ]]; then
|
||||
header "Shell completion gaps (long flags in flags.go missing from completions)"
|
||||
# Extract long flag names from struct tags: `long:"flag-name"`
|
||||
go_flags=$(grep -oP 'long:"[^"]+"' "$FLAGS_FILE" | sed 's/long:"//;s/"//' | sort -u)
|
||||
|
||||
found=0
|
||||
# bash and zsh use --flag-name; fish uses -l flag-name
|
||||
for comp_file in "$BASH_FILE" "$ZSH_FILE"; do
|
||||
[[ -f "$comp_file" ]] || continue
|
||||
comp_name=$(basename "$comp_file")
|
||||
while IFS= read -r flag; do
|
||||
if ! grep -qF -- "--$flag" "$comp_file" 2>/dev/null; then
|
||||
warn "$comp_name → missing --$flag"
|
||||
found=1
|
||||
fi
|
||||
done <<< "$go_flags"
|
||||
done
|
||||
if [[ -f "$FISH_FILE" ]]; then
|
||||
comp_name=$(basename "$FISH_FILE")
|
||||
while IFS= read -r flag; do
|
||||
# fish registers long options with: complete -c cmd -l flag-name
|
||||
if ! grep -qF -- "-l $flag" "$FISH_FILE" 2>/dev/null; then
|
||||
warn "$comp_name → missing -l $flag"
|
||||
found=1
|
||||
fi
|
||||
done <<< "$go_flags"
|
||||
fi
|
||||
[[ $found -eq 0 ]] && ok "All completions in sync"
|
||||
fi
|
||||
|
||||
# ── Summary ───────────────────────────────────────────────────────────────────
|
||||
echo
|
||||
echo "━━━ Summary ━━━"
|
||||
if [[ $issues -eq 0 ]]; then
|
||||
echo -e "${GRN}No issues found.${RST}"
|
||||
else
|
||||
echo -e "${YLW}${issues} issue(s) found.${RST}"
|
||||
fi
|
||||
|
||||
[[ $STRICT -eq 1 && $issues -gt 0 ]] && exit 1
|
||||
exit 0
|
||||
|
|
@ -39,17 +39,13 @@ docker run --rm -it -v $PWD/.env:/root/.config/fabric/.env fabric -p your-patter
|
|||
|
||||
## Running the server
|
||||
|
||||
Expose port 8080 to use Fabric's REST API. In a container, bind all
|
||||
interfaces with `--address :8080` so the mapped port can reach the
|
||||
server, and set an API key, which is mandatory for non-loopback binds:
|
||||
Expose port 8080 to use Fabric's REST API:
|
||||
|
||||
```bash
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric \
|
||||
-e FABRIC_API_KEY=your-secret-key fabric --serve --address :8080
|
||||
docker run --rm -it -p 8080:8080 -v $HOME/.fabric-config:/root/.config/fabric fabric --serve
|
||||
```
|
||||
|
||||
The API will be available at `http://localhost:8080`. Requests must send
|
||||
the key in the `X-API-Key` header.
|
||||
The API will be available at `http://localhost:8080`.
|
||||
|
||||
## Multi-arch builds and GHCR packages
|
||||
|
||||
|
|
|
|||
|
|
@ -2083,40 +2083,6 @@
|
|||
"WRITING",
|
||||
"CONVERSION"
|
||||
]
|
||||
},
|
||||
{
|
||||
"patternName": "analyze_monetization_opportunities",
|
||||
"description": "Identify affiliate, sponsorship, product, and community monetization opportunities in creator content.",
|
||||
"tags": [
|
||||
"ANALYSIS",
|
||||
"BUSINESS",
|
||||
"STRATEGY"
|
||||
]
|
||||
},
|
||||
{
|
||||
"patternName": "extract_affiliate_products",
|
||||
"description": "Extract products, tools, and brands from transcripts as affiliate revenue opportunities.",
|
||||
"tags": [
|
||||
"EXTRACT",
|
||||
"BUSINESS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"patternName": "extract_video_commerce_entities",
|
||||
"description": "Extract commercially relevant entities, products, and brands from video transcripts.",
|
||||
"tags": [
|
||||
"EXTRACT",
|
||||
"BUSINESS"
|
||||
]
|
||||
},
|
||||
{
|
||||
"patternName": "generate_frontmatter",
|
||||
"description": "Generate YAML frontmatter with tags, aliases and summary for PKM notes.",
|
||||
"tags": [
|
||||
"WRITING",
|
||||
"EXTRACT",
|
||||
"CONVERSION"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1003,22 +1003,6 @@
|
|||
{
|
||||
"patternName": "create_slides",
|
||||
"pattern_extract": "### IDENTITY AND PURPOSE You are an expert communicator, capable of explaining and visualizing complex concepts, conveying even the most complex narratives in the form of an engaging, well-structured presentation. Your task is to create a slideshow to assist a presenter in conveying the key points of the document provided as INPUT. Take a deep breath and think step by step how to best accomplish this goal. * Analyze the full contents, understand the information it intends to convey in full depth. * Come up with a narrative structure that could be used to convey this narrative in the most efficient way to a general audience. * Craft an engaging sequence of theses that would best represent this narrative. * Design a slideshow that could assist the presenter in communicating these theses in the best possible way. Remember, a slide show is a means of providing *relevant visual context* to the audience to accompany the words of the presenter. AVOID slides that are simple textual recitals. Instead, strive to limit textual content to the bare minimum necessary to illustrate the idea. Instead, come up with appropriate VISUAL illustrations relevant to what is being said (charts, diagrams, maps, icons, pointers, etc). Prefer a light, minimalistic theme. ### OUTPUT INSTRUCTIONS Output the slideshow as a single, autonomous HTML document that uses the the Reveal.js framework to model the slideshow. Make ample use of inline SVG to provide illustrations. If a 3D illustration would be approporiate, use the Three.js framework. For network visualizations prefer Vis.js. There may be situations where animation and interactivity would benefit exposition. In these cases feel free to include animation and/or interactivity, but generally do not overdo it. DO NOT output anything outside the <html></html> tags. ### INPUT"
|
||||
},
|
||||
{
|
||||
"patternName": "analyze_monetization_opportunities",
|
||||
"pattern_extract": "# 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:"
|
||||
},
|
||||
{
|
||||
"patternName": "extract_affiliate_products",
|
||||
"pattern_extract": "# 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:"
|
||||
},
|
||||
{
|
||||
"patternName": "extract_video_commerce_entities",
|
||||
"pattern_extract": "# 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:"
|
||||
},
|
||||
{
|
||||
"patternName": "generate_frontmatter",
|
||||
"pattern_extract": "# 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:"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,9 +1,8 @@
|
|||
{
|
||||
"useTabs": false,
|
||||
"tabWidth": 2,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
"useTabs": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "none",
|
||||
"printWidth": 100,
|
||||
"plugins": ["prettier-plugin-svelte"],
|
||||
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,14 +24,6 @@ export default tseslint.config(
|
|||
parserOptions: {
|
||||
parser: tseslint.parser
|
||||
}
|
||||
},
|
||||
rules: {
|
||||
// svelte-eslint-parser gives a `$:` reactive statement a variable
|
||||
// definition of type "ComputedVariable". The typescript-eslint rule
|
||||
// does not know that type and stops ESLint with a TypeError. Use the
|
||||
// core ESLint rule for Svelte components instead.
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
'no-unused-vars': 'error'
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
102
web/my-custom-theme.ts
Normal file
102
web/my-custom-theme.ts
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
|
||||
import type { CustomThemeConfig } from '@skeletonlabs/tw-plugin';
|
||||
|
||||
export const myCustomTheme: CustomThemeConfig = {
|
||||
name: 'my-custom-theme',
|
||||
properties: {
|
||||
// =~= Theme Properties =~=
|
||||
"--theme-font-family-base": `system-ui`,
|
||||
"--theme-font-family-heading": `system-ui`,
|
||||
"--theme-font-color-base": "var(--color-primary-800)",
|
||||
"--theme-font-color-dark": "var(--color-primary-300)",
|
||||
"--theme-rounded-base": "9999px",
|
||||
"--theme-rounded-container": "8px",
|
||||
"--theme-border-base": "1px",
|
||||
// =~= Theme On-X Colors =~=
|
||||
"--on-primary": "0 0 0",
|
||||
"--on-secondary": "0 0 0",
|
||||
"--on-tertiary": "0 0 0",
|
||||
"--on-success": "0 0 0",
|
||||
"--on-warning": "0 0 0",
|
||||
"--on-error": "0 0 0",
|
||||
"--on-surface": "0 0 0",
|
||||
// =~= Theme Colors =~=
|
||||
// primary | #613bf7
|
||||
"--color-primary-50": "231 226 254", // #e7e2fe
|
||||
"--color-primary-100": "223 216 253", // #dfd8fd
|
||||
"--color-primary-200": "216 206 253", // #d8cefd
|
||||
"--color-primary-300": "192 177 252", // #c0b1fc
|
||||
"--color-primary-400": "144 118 249", // #9076f9
|
||||
"--color-primary-500": "97 59 247", // #613bf7
|
||||
"--color-primary-600": "87 53 222", // #5735de
|
||||
"--color-primary-700": "73 44 185", // #492cb9
|
||||
"--color-primary-800": "58 35 148", // #3a2394
|
||||
"--color-primary-900": "48 29 121", // #301d79
|
||||
// secondary | #9de1ae
|
||||
"--color-secondary-50": "240 251 243", // #f0fbf3
|
||||
"--color-secondary-100": "235 249 239", // #ebf9ef
|
||||
"--color-secondary-200": "231 248 235", // #e7f8eb
|
||||
"--color-secondary-300": "216 243 223", // #d8f3df
|
||||
"--color-secondary-400": "186 234 198", // #baeac6
|
||||
"--color-secondary-500": "157 225 174", // #9de1ae
|
||||
"--color-secondary-600": "141 203 157", // #8dcb9d
|
||||
"--color-secondary-700": "118 169 131", // #76a983
|
||||
"--color-secondary-800": "94 135 104", // #5e8768
|
||||
"--color-secondary-900": "77 110 85", // #4d6e55
|
||||
// tertiary | #3fa0a6
|
||||
"--color-tertiary-50": "226 241 242", // #e2f1f2
|
||||
"--color-tertiary-100": "217 236 237", // #d9eced
|
||||
"--color-tertiary-200": "207 231 233", // #cfe7e9
|
||||
"--color-tertiary-300": "178 217 219", // #b2d9db
|
||||
"--color-tertiary-400": "121 189 193", // #79bdc1
|
||||
"--color-tertiary-500": "63 160 166", // #3fa0a6
|
||||
"--color-tertiary-600": "57 144 149", // #399095
|
||||
"--color-tertiary-700": "47 120 125", // #2f787d
|
||||
"--color-tertiary-800": "38 96 100", // #266064
|
||||
"--color-tertiary-900": "31 78 81", // #1f4e51
|
||||
// success | #37b3fc
|
||||
"--color-success-50": "225 244 255", // #e1f4ff
|
||||
"--color-success-100": "215 240 254", // #d7f0fe
|
||||
"--color-success-200": "205 236 254", // #cdecfe
|
||||
"--color-success-300": "175 225 254", // #afe1fe
|
||||
"--color-success-400": "115 202 253", // #73cafd
|
||||
"--color-success-500": "55 179 252", // #37b3fc
|
||||
"--color-success-600": "50 161 227", // #32a1e3
|
||||
"--color-success-700": "41 134 189", // #2986bd
|
||||
"--color-success-800": "33 107 151", // #216b97
|
||||
"--color-success-900": "27 88 123", // #1b587b
|
||||
// warning | #d209f8
|
||||
"--color-warning-50": "248 218 254", // #f8dafe
|
||||
"--color-warning-100": "246 206 254", // #f6cefe
|
||||
"--color-warning-200": "244 194 253", // #f4c2fd
|
||||
"--color-warning-300": "237 157 252", // #ed9dfc
|
||||
"--color-warning-400": "224 83 250", // #e053fa
|
||||
"--color-warning-500": "210 9 248", // #d209f8
|
||||
"--color-warning-600": "189 8 223", // #bd08df
|
||||
"--color-warning-700": "158 7 186", // #9e07ba
|
||||
"--color-warning-800": "126 5 149", // #7e0595
|
||||
"--color-warning-900": "103 4 122", // #67047a
|
||||
// error | #90df16
|
||||
"--color-error-50": "238 250 220", // #eefadc
|
||||
"--color-error-100": "233 249 208", // #e9f9d0
|
||||
"--color-error-200": "227 247 197", // #e3f7c5
|
||||
"--color-error-300": "211 242 162", // #d3f2a2
|
||||
"--color-error-400": "177 233 92", // #b1e95c
|
||||
"--color-error-500": "144 223 22", // #90df16
|
||||
"--color-error-600": "130 201 20", // #82c914
|
||||
"--color-error-700": "108 167 17", // #6ca711
|
||||
"--color-error-800": "86 134 13", // #56860d
|
||||
"--color-error-900": "71 109 11", // #476d0b
|
||||
// surface | #46a1ed
|
||||
"--color-surface-50": "227 241 252", // #e3f1fc
|
||||
"--color-surface-100": "218 236 251", // #daecfb
|
||||
"--color-surface-200": "209 232 251", // #d1e8fb
|
||||
"--color-surface-300": "181 217 248", // #b5d9f8
|
||||
"--color-surface-400": "126 189 242", // #7ebdf2
|
||||
"--color-surface-500": "70 161 237", // #46a1ed
|
||||
"--color-surface-600": "63 145 213", // #3f91d5
|
||||
"--color-surface-700": "53 121 178", // #3579b2
|
||||
"--color-surface-800": "42 97 142", // #2a618e
|
||||
"--color-surface-900": "34 79 116", // #224f74
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue