Merge branch 'main' into add-synthorai-provider

This commit is contained in:
Kayvan Sylvan 2026-08-29 08:42:12 -07:00
commit cd54c1a82f
31 changed files with 1144 additions and 219 deletions

View file

@ -1,5 +1,15 @@
# Changelog
## 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

View file

@ -1,3 +1,3 @@
package main
var version = "v1.4.470"
var version = "v1.4.471"

View file

@ -191,7 +191,7 @@ Create a `.env` file next to the `generate_changelog` binary:
```bash
GITHUB_TOKEN=your_github_token_here
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-sonnet-4-20250514
FABRIC_CHANGELOG_SUMMARIZE_MODEL=gpt-5.6-sol
```
The tool automatically loads `.env` files for convenient configuration management.
@ -226,8 +226,8 @@ The tool can generate AI-powered summaries using Fabric for more polished, profe
# Enable AI summarization
generate_changelog --ai-summarize
# Custom model (default: claude-sonnet-4-20250514)
FABRIC_CHANGELOG_SUMMARIZE_MODEL=claude-opus-4 generate_changelog --ai-summarize
# Custom model (default: claude-opus-5)
FABRIC_CHANGELOG_SUMMARIZE_MODEL=gpt-5.6-sol generate_changelog --ai-summarize
```
### AI Summary Features

Binary file not shown.

View file

@ -7,7 +7,7 @@ import (
"strings"
)
const DefaultSummarizeModel = "claude-sonnet-4-5"
const DefaultSummarizeModel = "claude-opus-5"
const MinContentLength = 256 // Minimum content length to consider for summarization
const prompt = `# ROLE

View file

@ -55,6 +55,7 @@ Use parameterized queries instead of string concatenation...
- Store secrets in environment variables or secure configuration
- Use the built-in setup process for key management
- Regularly rotate API keys
- `~/.config/fabric/.env` holds API keys and Codex OAuth tokens. Fabric writes it with mode `0600`. Treat the file as secret material. Comments and key order are not preserved when Codex tokens are updated in place.
### Input Validation

120
go.mod
View file

@ -4,61 +4,62 @@ go 1.26.0
require (
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.14.0
github.com/anthropics/anthropic-sdk-go v1.61.0
github.com/anthropics/anthropic-sdk-go v1.67.0
github.com/atotto/clipboard v0.1.4
github.com/aws/aws-sdk-go-v2 v1.43.0
github.com/aws/aws-sdk-go-v2/config v1.32.31
github.com/aws/aws-sdk-go-v2/service/bedrock v1.66.0
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.56.0
github.com/aws/aws-sdk-go-v2 v1.44.0
github.com/aws/aws-sdk-go-v2/config v1.32.40
github.com/aws/aws-sdk-go-v2/service/bedrock v1.68.0
github.com/aws/aws-sdk-go-v2/service/bedrockruntime v1.58.0
github.com/gabriel-vasile/mimetype v1.4.15
github.com/gin-gonic/gin v1.12.0
github.com/go-git/go-git/v5 v5.19.1
github.com/go-git/go-git/v5 v5.19.2
github.com/go-shiori/go-readability v0.0.0-20251205110129-5db1dc9836f0
github.com/google/go-github/v66 v66.0.0
github.com/hasura/go-graphql-client v0.16.0
github.com/jessevdk/go-flags v1.6.1
github.com/joho/godotenv v1.5.1
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
github.com/mattn/go-sqlite3 v1.14.48
github.com/mattn/go-sqlite3 v1.14.50
github.com/nicksnyder/go-i18n/v2 v2.6.1
github.com/ollama/ollama v0.32.3
github.com/ollama/ollama v0.33.1
github.com/openai/openai-go v1.12.0
github.com/otiai10/copy v1.14.1
github.com/pkg/errors v0.9.1
github.com/samber/lo v1.53.0
github.com/sgaunet/perplexity-go/v2 v2.16.1
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.12.1
github.com/swaggo/files v1.0.1
github.com/swaggo/gin-swagger v1.6.1
github.com/swaggo/swag v1.16.6
golang.org/x/oauth2 v0.36.0
golang.org/x/text v0.40.0
google.golang.org/api v0.290.0
golang.org/x/sys v0.47.0
golang.org/x/text v0.41.0
google.golang.org/api v0.294.0
gopkg.in/yaml.v3 v3.0.1
)
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.23.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.7.2 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.9.0 // indirect
github.com/KyleBanks/depth v1.2.1 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.32 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.5.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.41 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.6.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/buger/jsonparser v1.2.0 // indirect
github.com/buger/jsonparser v1.6.1 // indirect
github.com/bytedance/gopkg v0.1.4 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/go-openapi/jsonpointer v1.0.0 // indirect
github.com/go-openapi/jsonreference v1.0.0 // indirect
github.com/go-openapi/spec v0.22.9 // indirect
github.com/go-openapi/swag/conv v0.27.3 // indirect
github.com/go-openapi/swag/jsonutils v0.27.3 // indirect
github.com/go-openapi/swag/loading v0.27.3 // indirect
github.com/go-openapi/swag/pools v0.27.3 // indirect
github.com/go-openapi/swag/stringutils v0.27.3 // indirect
github.com/go-openapi/swag/typeutils v0.27.3 // indirect
github.com/go-openapi/swag/yamlutils v0.27.3 // indirect
github.com/go-openapi/jsonreference v1.0.1 // indirect
github.com/go-openapi/spec v0.22.11 // indirect
github.com/go-openapi/swag/conv v0.29.1 // indirect
github.com/go-openapi/swag/jsonutils v0.29.1 // indirect
github.com/go-openapi/swag/loading v0.29.1 // indirect
github.com/go-openapi/swag/pools v0.29.1 // indirect
github.com/go-openapi/swag/stringutils v0.29.1 // indirect
github.com/go-openapi/swag/typeutils v0.29.1 // indirect
github.com/go-openapi/swag/yamlutils v0.29.1 // indirect
github.com/goccy/go-yaml v1.19.2 // indirect
github.com/golang-jwt/jwt/v5 v5.3.1 // indirect
github.com/google/go-cmp v0.7.0 // indirect
@ -72,18 +73,18 @@ require (
github.com/quic-go/quic-go v0.61.0 // indirect
github.com/standard-webhooks/standard-webhooks/libraries v0.0.1 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
go.mongodb.org/mongo-driver/v2 v2.8.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.69.0 // indirect
go.yaml.in/yaml/v3 v3.0.4 // 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.38.0 // indirect
golang.org/x/mod v0.40.0 // indirect
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.48.0 // indirect
golang.org/x/tools v0.49.0 // indirect
)
require (
cloud.google.com/go v0.123.0 // indirect
cloud.google.com/go/auth v0.22.0 // indirect
cloud.google.com/go/auth v0.23.2 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
cloud.google.com/go/compute/metadata v0.9.0 // indirect
dario.cat/mergo v1.0.2 // indirect
@ -91,20 +92,20 @@ require (
github.com/ProtonMail/go-crypto v1.4.1 // indirect
github.com/andybalholm/cascadia v1.3.4 // indirect
github.com/araddon/dateparse v0.0.0-20210429162001-6b43995a97de // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.14 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.19.30
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.31 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.31 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.13 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.31 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.33.0 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.38.0 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.45.0 // indirect
github.com/aws/smithy-go v1.27.4 // indirect
github.com/bytedance/sonic v1.15.2 // indirect
github.com/bytedance/sonic/loader v0.5.1 // indirect
github.com/cloudflare/circl v1.6.4 // 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
@ -126,8 +127,8 @@ 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.19 // indirect
github.com/googleapis/gax-go/v2 v2.23.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect
github.com/googleapis/gax-go/v2 v2.24.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/json-iterator/go v1.1.12 // indirect
@ -149,21 +150,20 @@ require (
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/ugorji/go/codec v1.3.2 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.69.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
go.opentelemetry.io/otel/metric v1.44.0 // indirect
go.opentelemetry.io/otel/trace v1.44.0 // indirect
golang.org/x/arch v0.29.0 // indirect
golang.org/x/crypto v0.54.0 // indirect
golang.org/x/net v0.57.0 // 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
golang.org/x/sys v0.47.0 // indirect
google.golang.org/genai v1.65.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df // indirect
google.golang.org/grpc v1.82.1 // indirect
google.golang.org/protobuf v1.36.11 // indirect
google.golang.org/genai v1.70.0
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect
google.golang.org/grpc v1.83.2 // indirect
google.golang.org/protobuf v1.36.12 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
)

125
go.sum
View file

@ -2,6 +2,8 @@ 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/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=
@ -10,6 +12,8 @@ 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=
@ -20,6 +24,8 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJ
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/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,6 +41,8 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
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/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=
@ -43,52 +51,94 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
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/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/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/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=
@ -133,6 +183,8 @@ github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMj
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-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=
@ -142,29 +194,50 @@ github.com/go-openapi/jsonpointer v1.0.0 h1:kR9tHqY0CtZaOPVFm622dPVNhrvYpwr4uCxg
github.com/go-openapi/jsonpointer v1.0.0/go.mod h1:Z3rw7dWu1p9IgitXCFamSlA5lmDiklEB6vkaxcNZW5Y=
github.com/go-openapi/jsonreference v1.0.0 h1:jlmTr6torcd1YgDQvSfNmRtKzYDO4FGBkrAdlAVWnpY=
github.com/go-openapi/jsonreference v1.0.0/go.mod h1:jtwdyGbJk0Xhe5Y+rwtglQP6Sb1WZST4rT32LWB+sv0=
github.com/go-openapi/jsonreference v1.0.1 h1:4zJ7AmYDKNmD3aSpfPnFNCFA5E80/xMHUNKgydaLh38=
github.com/go-openapi/jsonreference v1.0.1/go.mod h1:dYplQXa6p5lXprLcJ8LE2iU7vNpXsAHDQ5ZAgL+Qx3A=
github.com/go-openapi/spec v0.22.9 h1:/vKIFDcGKp0ktZWGbym/tJEWbk6/XOEmAVU0kqKMH+w=
github.com/go-openapi/spec v0.22.9/go.mod h1:b/mNUYIOQOyIiUzUzXEE8xzyZqf93KvM9hQGP91yfl0=
github.com/go-openapi/spec v0.22.11 h1:/ZW28n4PCghbBpwjmvoMeyCeDwd6pIe715Ov8T3kAaQ=
github.com/go-openapi/spec v0.22.11/go.mod h1:Ypu+u1KTejey7/QCr3j1ChdjwhGgXuklbH7l8+YP1J4=
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
github.com/go-openapi/swag/conv v0.27.3 h1:iqJFmGEjmX3AY0lSszABFqRVqOSt99XS0LzNIMJYuhU=
github.com/go-openapi/swag/conv v0.27.3/go.mod h1:nPRmN6jgNme99hpf+nM0auDZGALWIqlwhisKPK/bQhQ=
github.com/go-openapi/swag/conv v0.29.1 h1:AC4Eh/5c/eUDOUCzzsRC9ghmFgOSBHeRMGIngY0ZUGA=
github.com/go-openapi/swag/conv v0.29.1/go.mod h1:S1X7/ZrBEZOC0Wc8AGxjbcGS92l3WEjA7aPtpl+RaqM=
github.com/go-openapi/swag/jsonutils v0.27.3 h1:1DEz+O82frtSMBcos/7XIn1GnpNTbsD4Bru4Dc/uhRc=
github.com/go-openapi/swag/jsonutils v0.27.3/go.mod h1:qiDCoQvzkMxrV3G8FLEdIU5L+EFYc0zcDOHWT3Yofvo=
github.com/go-openapi/swag/jsonutils v0.29.1 h1:AFCxs0eQZ24/QyfhVHM2t49rMz7Vv3XCsZQI6yrNy+c=
github.com/go-openapi/swag/jsonutils v0.29.1/go.mod h1:u3+sCfJpttDpcmS5kpm0yxL6GK0eWgODsx8Yw8fcqNM=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3 h1:h/eT9kmGCDdFLJF29lOhzLtF0FmP1AX2MhLJWVebsb8=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.27.3/go.mod h1:mofwUWx70wvskwESqRJ//k/9kURmCgyJl5m5Ppoh5kY=
github.com/go-openapi/swag/jsonutils/fixtures_test v0.29.1 h1:BiiXE31Bx9SfpsMmOQj5KYpUhTZBpLVriVhJDuLuY2o=
github.com/go-openapi/swag/loading v0.27.3 h1:L9nQkEgzU7QgFQL+pLEMfGUKxeM4pWwGwbET9Z3weW0=
github.com/go-openapi/swag/loading v0.27.3/go.mod h1:rJ0NeaKsF4CVPnMGjPQl7JlSHzvD0bc2DKXLss1hiuE=
github.com/go-openapi/swag/loading v0.29.1 h1:FCv5fG8UhTdDJa2R7w+5O9Ekpcbw7tt0nFWvmDKGBjc=
github.com/go-openapi/swag/loading v0.29.1/go.mod h1:N0ESuem4p2oedKal8EJhciqnJ9Q9Wmt83L1CRB3Fouw=
github.com/go-openapi/swag/pools v0.27.3 h1:gXjImP3F6/56wRRcFgEPld084Y6u2gs21ikPBt8NKBk=
github.com/go-openapi/swag/pools v0.27.3/go.mod h1:kVQefhSK5RWuRe7BXsL8htgBPAMpN7HDGpGEknqugeE=
github.com/go-openapi/swag/pools v0.29.1 h1:NRogYxdEW9SjRM4mkAOji9iefO4MRXq3p/ZJcoQbUKg=
github.com/go-openapi/swag/pools v0.29.1/go.mod h1:leDcaghjkRAhCuCRv9NfJU5f0mjoU3cT/XZObhMk3pc=
github.com/go-openapi/swag/stringutils v0.27.3 h1:Ru28hnbAvN5wycALQYy8IobHvASq+FUFMlp1QzLM0JI=
github.com/go-openapi/swag/stringutils v0.27.3/go.mod h1:lzRN95CxXmA03XcDWHLOb6nOMcxCqR5rGY0lOgsfRoM=
github.com/go-openapi/swag/stringutils v0.29.1 h1:1ykunK7iJQk1uOO7+oUH1ukbsK85fFCOiCFMOVSY+F0=
github.com/go-openapi/swag/stringutils v0.29.1/go.mod h1:7fSqZ+z8Qc0tOfAAK0jVa5qFGrnIlRi6n7NeGGrr1vc=
github.com/go-openapi/swag/typeutils v0.27.3 h1:l6SSrx5eR5/WVwrGNzN6bQ9WqL04mrxNBl9YgQ3rcJ4=
github.com/go-openapi/swag/typeutils v0.27.3/go.mod h1:Srm0xFNRZ1Y+vCxJclo5qzx8aj+1pAKda/YfFPrG0dQ=
github.com/go-openapi/swag/typeutils v0.29.1 h1:Nzv9nhnlLCRBPQqfOX+7lB6Guju370or8StT+lIOf6M=
github.com/go-openapi/swag/typeutils v0.29.1/go.mod h1:hxpgDZJVBkBsi/d3MIUosafoFdE5exaQRmVp0zwu3YE=
github.com/go-openapi/swag/yamlutils v0.27.3 h1:cRFCAoYtslYn9L9T0xWryHy1t7c1MACC+DMj3CLvwvs=
github.com/go-openapi/swag/yamlutils v0.27.3/go.mod h1:6JYBGj8sw/NawMllyZY+cTA8Mzk2etS3ZBASdcyPsiU=
github.com/go-openapi/swag/yamlutils v0.29.1 h1:69w3tsBajm7MR/fejLy7HD/3J68Ys1SeeZMEzZ3w2sk=
github.com/go-openapi/swag/yamlutils v0.29.1/go.mod h1:rgsp3vT/QdWzKwn43CigDwjOGIenPyTZMKnxEM8jZOA=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0 h1:gGHwAJ0R/5jU8BEGDbfRNR3hL68dAVi84WuOApp29B0=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.0/go.mod h1:tY+St1SGq4NFl0QIqdTY4aEdbChAHxhyB77XQi9iJCo=
github.com/go-openapi/testify/enable/yaml/v2 v2.6.1 h1:Jm+/ze2rMtbD98yen92AhATGLGREDYXG56Xr4gMjEtE=
github.com/go-openapi/testify/v2 v2.6.0 h1:5PKH2HE7YJ/LuRPQGvSxBRlFXNQhSetBLlGAgUEu3ug=
github.com/go-openapi/testify/v2 v2.6.0/go.mod h1:SgsVHtfooshd0tublTtJ50FPKhujf47YRqauXXOUxfw=
github.com/go-openapi/testify/v2 v2.6.1 h1:6CNJhTjMzgaeaH8WhshcsZNPIvRemiOcFpU7seO/y7Q=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@ -203,8 +276,12 @@ 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/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=
@ -247,6 +324,8 @@ github.com/mattn/go-isatty v0.0.24/go.mod h1:nMCL3Zebbrt45jsMDgnfIwz6ydEQApk5oEI
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/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=
@ -256,6 +335,8 @@ github.com/nicksnyder/go-i18n/v2 v2.6.1 h1:JDEJraFsQE17Dut9HFDHzCoAWGEQJom5s0TRd
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/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=
@ -320,6 +401,8 @@ github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXl
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
github.com/swaggo/files v1.0.1 h1:J1bVJ4XHZNq0I46UU90611i9/YzdrF7x92oX1ig5IdE=
github.com/swaggo/files v1.0.1/go.mod h1:0qXmMNH6sXNf+73t65aKeB+ApmgxdnkQzVTAj2uaMUg=
github.com/swaggo/gin-swagger v1.6.1 h1:Ri06G4gc9N4t4k8hekMigJ9zKTFSlqj/9paAQCQs7cY=
@ -341,6 +424,8 @@ 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=
@ -348,40 +433,62 @@ github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI
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.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.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/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/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/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=
@ -389,6 +496,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
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/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=
@ -420,6 +529,8 @@ 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/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=
@ -427,23 +538,37 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
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/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/api v0.290.0 h1:eMw0Xo+IfbbMlKmW7aHvpyQRv9RCXuWx/vs8AD+0x9A=
google.golang.org/api v0.290.0/go.mod h1:weJZ3lldHFYI0DBFNKpJelUDNnusTt5YaOEgxvt8ci8=
google.golang.org/api v0.294.0 h1:8gASjJxdtcIieB3OqbkLcF0FfbXVNqKtU5iozD1ssvA=
google.golang.org/api v0.294.0/go.mod h1:02qB8+Ox1ZFzcaKFMguy1nQLJmSIyvV6Ff4txJEXtl4=
google.golang.org/genai v1.65.0 h1:6QK3Rjsx0iuJjbDCE1vf1VUr1IEjRDpvexGGnhxoAIk=
google.golang.org/genai v1.65.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU=
google.golang.org/genai v1.70.0 h1:V9oYOBvTDYbmeklOHjvxGeeLau71WiS2CWKrktQtDok=
google.golang.org/genai v1.70.0/go.mod h1:mDdPDFXo1Ats7f1WXVyZgWb/CkMzFWTWJruIMy7hGIU=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
google.golang.org/genproto v0.0.0-20260715232425-e75dac1f907d h1:C9v1o0/4quuhOAfmRXA2j+we0PqZIp8traLdeogF3Ms=
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU=
google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA=
google.golang.org/genproto/googleapis/api v0.0.0-20260715232425-e75dac1f907d h1:QwnJwPte4XXAkhPu26LTDIahnsMSUV0kK8HkxbC+Pc4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df h1:O3ig1i5WDDzsVzRp+cCdgelT9vXnlnOFdlEeFtL4HCc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260724162435-b2f20204f0df/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 h1:1VUiZAXyC+zmiFYi+WLtBzr68Cj8wOofHjjrA/kkizc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5/go.mod h1:DjtHYE8FKJLivXcBEjGwndXfIC23G0VpXiXKqG179uA=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU=
google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=

View file

@ -10,6 +10,7 @@ import (
"sort"
"strconv"
"strings"
"sync"
"github.com/danielmiessler/fabric/internal/i18n"
debuglog "github.com/danielmiessler/fabric/internal/log"
@ -73,6 +74,24 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
vendors := []ai.Vendor{}
// Add non-OpenAI compatible clients
codexClient := codex.NewClient()
codexClient.WithStoreLock = func(fn func() error) error {
return db.WithEnvLock(func() error {
env, err := db.ReadEnvFile()
if err != nil {
return err
}
codexClient.LoadEnvSettings(env)
return fn()
})
}
codexClient.TokenPersist = func() error {
return db.ApplyEnvUpdates(map[string]string{
codexClient.AccessToken.EnvVariable: strings.TrimSpace(codexClient.AccessToken.Value),
codexClient.RefreshToken.EnvVariable: strings.TrimSpace(codexClient.RefreshToken.Value),
codexClient.AccountID.EnvVariable: strings.TrimSpace(codexClient.AccountID.Value),
})
}
vendors = append(vendors,
openai.NewClient(),
digitalocean.NewClient(),
@ -86,7 +105,7 @@ func NewPluginRegistry(db *fsdb.Db) (ret *PluginRegistry, err error) {
lmstudio.NewClient(),
exolab.NewClient(),
perplexity.NewClient(),
codex.NewClient(),
codexClient,
copilot.NewClient(), // Microsoft 365 Copilot
bedrock.NewClient(), // AWS Bedrock - credentials configured via setup or AWS credential chain
)
@ -123,6 +142,8 @@ func (o *PluginRegistry) ListVendors(out io.Writer) error {
type PluginRegistry struct {
Db *fsdb.Db
vendorMu sync.Mutex
VendorManager *ai.VendorsManager
VendorsAll *ai.VendorsManager
Defaults *tools.Defaults
@ -288,16 +309,12 @@ func (o *PluginRegistry) runVendorSetup() (err error) {
return pluginSetupErr
}
o.registerVendor(plugin)
if err = o.SaveEnvFile(); err != nil {
return
}
if o.VendorManager.FindByName(plugin.GetName()) == nil {
if vendor, ok := plugin.(ai.Vendor); ok {
o.VendorManager.AddVendors(vendor)
}
}
return
}
@ -349,16 +366,11 @@ func (o *PluginRegistry) runInteractiveSetup() (err error) {
if pluginSetupErr := plugin.Setup(); pluginSetupErr != nil {
println(pluginSetupErr.Error())
} else {
o.registerVendor(plugin)
if err = o.SaveEnvFile(); err != nil {
break
}
}
if o.VendorManager.FindByName(plugin.GetName()) == nil {
if vendor, ok := plugin.(ai.Vendor); ok {
o.VendorManager.AddVendors(vendor)
}
}
} else {
break
}
@ -425,11 +437,32 @@ func (o *PluginRegistry) SetupVendor(vendorName string) (err error) {
if err = o.VendorsAll.SetupVendor(vendorName, o.VendorManager.VendorsByName); err != nil {
return
}
if vendor := o.VendorsAll.FindByName(vendorName); vendor != nil {
o.registerVendor(vendor)
}
err = o.SaveEnvFile()
return
}
func (o *PluginRegistry) registerVendor(plugin plugins.Plugin) {
vendor, ok := plugin.(ai.Vendor)
if !ok {
return
}
o.vendorMu.Lock()
defer o.vendorMu.Unlock()
name := vendor.GetName()
for _, existing := range o.VendorManager.Vendors {
if strings.EqualFold(existing.GetName(), name) {
return
}
}
o.VendorManager.AddVendors(vendor)
}
func (o *PluginRegistry) ConfigureVendors() {
o.vendorMu.Lock()
defer o.vendorMu.Unlock()
o.VendorManager.Clear()
for _, vendor := range o.VendorsAll.Vendors {
if vendorErr := vendor.Configure(); vendorErr == nil && vendor.IsConfigured() {
@ -438,6 +471,34 @@ func (o *PluginRegistry) ConfigureVendors() {
}
}
func (o *PluginRegistry) activateVendor(name string) (ai.Vendor, error) {
if strings.TrimSpace(name) == "" {
return nil, nil
}
o.vendorMu.Lock()
defer o.vendorMu.Unlock()
if v := o.VendorManager.FindByName(name); v != nil {
return v, nil
}
if o.VendorsAll == nil {
return nil, nil
}
v := o.VendorsAll.FindByName(name)
if v == nil {
return nil, nil
}
if err := v.Configure(); err != nil {
return nil, err
}
if !v.IsConfigured() {
return nil, nil
}
o.VendorManager.AddVendors(v)
return v, nil
}
func (o *PluginRegistry) GetModels() (ret *ai.VendorsModels, err error) {
o.ConfigureVendors()
ret, err = o.VendorManager.GetModels()
@ -503,13 +564,25 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
ret.model = defaultModel
}
} else if model == "" {
if vendorName != "" {
ret.vendor = vendorManager.FindByName(vendorName)
} else {
ret.vendor = vendorManager.FindByName(defaultVendor)
name := vendorName
if name == "" {
name = defaultVendor
}
if ret.vendor, err = o.activateVendor(name); err != nil {
return
}
ret.model = defaultModel
} else {
if vendorName != "" {
if ret.vendor, err = o.activateVendor(vendorName); err != nil {
return
}
} else if !vendorManager.HasVendors() {
if _, err = o.activateVendor(defaultVendor); err != nil {
return
}
}
var models *ai.VendorsModels
if models, err = vendorManager.GetModels(); err != nil {
return
@ -567,6 +640,16 @@ func (o *PluginRegistry) GetChatter(model string, modelContextLength int, vendor
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 == "" {

View file

@ -3,6 +3,7 @@ package core
import (
"bytes"
"context"
"errors"
"io"
"os"
"strings"
@ -10,11 +11,14 @@ import (
"github.com/danielmiessler/fabric/internal/chat"
"github.com/danielmiessler/fabric/internal/domain"
"github.com/danielmiessler/fabric/internal/i18n"
debuglog "github.com/danielmiessler/fabric/internal/log"
"github.com/danielmiessler/fabric/internal/plugins"
"github.com/danielmiessler/fabric/internal/plugins/ai"
"github.com/danielmiessler/fabric/internal/plugins/ai/codex"
"github.com/danielmiessler/fabric/internal/plugins/db/fsdb"
"github.com/danielmiessler/fabric/internal/tools"
"github.com/joho/godotenv"
)
func TestSaveEnvFile(t *testing.T) {
@ -32,14 +36,15 @@ func TestSaveEnvFile(t *testing.T) {
// testVendor implements ai.Vendor for testing purposes
type testVendor struct {
name string
models []string
name string
models []string
configureErr error
}
func (m *testVendor) GetName() string { return m.name }
func (m *testVendor) GetSetupDescription() string { return m.name }
func (m *testVendor) IsConfigured() bool { return true }
func (m *testVendor) Configure() error { return nil }
func (m *testVendor) Configure() error { return m.configureErr }
func (m *testVendor) Setup() error { return nil }
func (m *testVendor) SetupFillEnvFileContent(*bytes.Buffer) {}
func (m *testVendor) ListModels(context.Context) ([]string, error) { return m.models, nil }
@ -210,3 +215,116 @@ func TestGetChatter_VendorPrefixIgnoredWhenNotAVendor(t *testing.T) {
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,
}
}

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Benutzerdefinierte Patterns - Verzeichnis für Ihre benutzerdefinierten Patterns festlegen",
"custom_patterns_warning_create_directory": "Warnung: Benutzerdefiniertes Musterverzeichnis %s konnte nicht erstellt werden: %v\n",
"db_error_loading_env_file": "fehler beim Laden der .env-Datei: %w",
"db_error_updating_env_file": "fehler beim Aktualisieren der .env-Datei: %w",
"defaults_model_context_length_question": "Geben Sie die Kontextlänge des Modells ein",
"defaults_model_question": "Geben Sie den Index oder den Namen Ihres Standardmodells ein",
"defaults_setup_description": "Standard-KI-Anbieter und -Modell",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Custom Patterns - Set directory for your custom patterns",
"custom_patterns_warning_create_directory": "Warning: Could not create custom patterns directory %s: %v\n",
"db_error_loading_env_file": "error loading .env file: %w",
"db_error_updating_env_file": "error updating .env file: %w",
"defaults_model_context_length_question": "Enter model context length",
"defaults_model_question": "Enter the index or the name of your default model",
"defaults_setup_description": "Default AI Vendor and Model",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Patrones personalizados - Establecer directorio para tus patrones personalizados",
"custom_patterns_warning_create_directory": "Advertencia: No se pudo crear el directorio de patrones personalizados %s: %v\n",
"db_error_loading_env_file": "error al cargar el archivo .env: %w",
"db_error_updating_env_file": "error al actualizar el archivo .env: %w",
"defaults_model_context_length_question": "Introduce la longitud del contexto del modelo",
"defaults_model_question": "Introduce el índice o el nombre de tu modelo predeterminado",
"defaults_setup_description": "Proveedor y modelo de IA predeterminados",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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": "دستور با موفقیت تکمیل شد",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "الگوهای سفارشی - تنظیم دایرکتوری برای الگوهای سفارشی شما",
"custom_patterns_warning_create_directory": "هشدار: امکان ایجاد پوشه الگوهای سفارشی %s وجود ندارد: %v\n",
"db_error_loading_env_file": "خطا در بارگذاری فایل .env: %w",
"db_error_updating_env_file": "خطا در به‌روزرسانی فایل .env: %w",
"defaults_model_context_length_question": "طول زمینه مدل را وارد کنید",
"defaults_model_question": "شاخص یا نام مدل پیش‌فرض خود را وارد کنید",
"defaults_setup_description": "ارائه‌دهنده و مدل هوش مصنوعی پیش‌فرض",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Patrons personnalisés - Définir le répertoire pour vos patrons personnalisés",
"custom_patterns_warning_create_directory": "Avertissement : Impossible de créer le répertoire de modèles personnalisés %s : %v\n",
"db_error_loading_env_file": "erreur lors du chargement du fichier .env : %w",
"db_error_updating_env_file": "erreur lors de la mise à jour du fichier .env : %w",
"defaults_model_context_length_question": "Saisissez la longueur du contexte du modèle",
"defaults_model_question": "Saisissez l'index ou le nom de votre modèle par défaut",
"defaults_setup_description": "Fournisseur et modèle d'IA par défaut",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Pattern personalizzati - Imposta la directory per i tuoi pattern personalizzati",
"custom_patterns_warning_create_directory": "Avviso: Impossibile creare la directory dei modelli personalizzati %s: %v\n",
"db_error_loading_env_file": "errore nel caricamento del file .env: %w",
"db_error_updating_env_file": "errore nell'aggiornamento del file .env: %w",
"defaults_model_context_length_question": "Inserisci la lunghezza del contesto del modello",
"defaults_model_question": "Inserisci l'indice o il nome del tuo modello predefinito",
"defaults_setup_description": "Fornitore e modello AI predefiniti",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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": "コマンドが正常に完了しました",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "カスタムパターン - カスタムパターン用のディレクトリを設定",
"custom_patterns_warning_create_directory": "警告: カスタムパターンディレクトリ%sを作成できませんでした: %v\n",
"db_error_loading_env_file": ".envファイルの読み込みエラー: %w",
"db_error_updating_env_file": ".envファイルの更新エラー: %w",
"defaults_model_context_length_question": "モデルのコンテキスト長を入力してください",
"defaults_model_question": "デフォルトモデルのインデックスまたは名前を入力してください",
"defaults_setup_description": "デフォルトのAIプロバイダーとモデル",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Niestandardowe wzorce - Ustaw katalog dla swoich niestandardowych wzorców",
"custom_patterns_warning_create_directory": "Ostrzeżenie: Nie można utworzyć katalogu niestandardowych wzorców %s: %v\n",
"db_error_loading_env_file": "błąd podczas ładowania pliku .env: %w",
"db_error_updating_env_file": "błąd podczas aktualizacji pliku .env: %w",
"defaults_model_context_length_question": "Podaj długość kontekstu modelu",
"defaults_model_question": "Podaj indeks lub nazwę domyślnego modelu",
"defaults_setup_description": "Domyślny dostawca AI i model",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Padrões personalizados - Definir diretório para seus padrões personalizados",
"custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n",
"db_error_loading_env_file": "erro ao carregar o arquivo .env: %w",
"db_error_updating_env_file": "erro ao atualizar o arquivo .env: %w",
"defaults_model_context_length_question": "Informe o comprimento do contexto do modelo",
"defaults_model_question": "Informe o índice ou o nome do seu modelo padrão",
"defaults_setup_description": "Provedor e modelo de IA padrão",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "Padrões personalizados - Definir diretório para os seus padrões personalizados",
"custom_patterns_warning_create_directory": "Aviso: Não foi possível criar o diretório de padrões personalizados %s: %v\n",
"db_error_loading_env_file": "erro ao carregar o ficheiro .env: %w",
"db_error_updating_env_file": "erro ao atualizar o ficheiro .env: %w",
"defaults_model_context_length_question": "Indique o comprimento do contexto do modelo",
"defaults_model_question": "Indique o índice ou o nome do seu modelo padrão",
"defaults_setup_description": "Fornecedor e modelo de IA padrão",

View file

@ -110,7 +110,9 @@
"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",
@ -133,13 +135,16 @@
"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": "命令执行成功",
@ -168,6 +173,7 @@
"custom_patterns_setup_description": "自定义模式 - 设置您的自定义模式目录",
"custom_patterns_warning_create_directory": "警告:无法创建自定义模式目录 %s%v\n",
"db_error_loading_env_file": "加载 .env 文件错误:%w",
"db_error_updating_env_file": "更新 .env 文件错误:%w",
"defaults_model_context_length_question": "请输入模型上下文长度",
"defaults_model_question": "请输入您的默认模型的索引或名称",
"defaults_setup_description": "默认 AI 提供商和模型",

View file

@ -24,6 +24,45 @@ 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()
@ -61,7 +100,13 @@ func (c *Client) ensureAccessToken(ctx context.Context, forceRefresh bool) (stri
c.setSettingValue(c.RefreshToken, refreshed.RefreshToken)
}
c.setSettingValue(c.AccountID, refreshedAccountID)
debuglog.Debug(debuglog.Detailed, "Codex access token refreshed for account=%s\n", refreshedAccountID)
if c.TokenPersist != nil {
if err := c.TokenPersist(); err != nil {
debuglog.Log("Codex token persist failed: %v\n", err)
return "", "", fmt.Errorf(i18n.T("codex_token_persist_failed"), err)
}
}
debuglog.Debug(debuglog.Detailed, "Codex access token refreshed account_present=%t\n", refreshedAccountID != "")
return c.AccessToken.Value, c.AccountID.Value, nil
}

View file

@ -58,6 +58,10 @@ 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 {
@ -81,11 +85,11 @@ func NewClient() *Client {
client.AccountID = client.AddSetting("Account ID", true)
client.ApiBaseURL = client.AddSetupQuestionWithEnvName("Base URL", false,
"Enter your Codex API base URL")
i18n.T("codex_api_base_url_question"))
client.ApiBaseURL.Value = defaultBaseURL
client.AuthBaseURL = client.AddSetupQuestionWithEnvName("Auth Base URL", false,
"Enter your Codex OAuth base URL")
i18n.T("codex_auth_base_url_question"))
client.AuthBaseURL.Value = defaultAuthBaseURL
client.authHTTPClient = &http.Client{Timeout: modelsRequestTimeout}
@ -101,6 +105,14 @@ func (c *Client) Setup() error {
return err
}
if strings.TrimSpace(c.RefreshToken.Value) != "" {
if err := c.configure(); err == nil {
return nil
} else {
debuglog.Log("Codex configure failed; starting browser login: %v\n", err)
}
}
ctx, cancel := context.WithTimeout(context.Background(), oauthTimeout)
defer cancel()
@ -153,11 +165,26 @@ func (c *Client) configure() error {
option.WithHTTPClient(c.apiHTTPClient),
)
c.ApiClient = &apiClient
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account=%s base_url=%s\n", c.AccountID.Value, c.ApiBaseURL.Value)
debuglog.Debug(debuglog.Detailed, "Codex configure: authenticated account_present=%t base_url=%s\n", strings.TrimSpace(c.AccountID.Value) != "", c.ApiBaseURL.Value)
return nil
}
// LoadEnvSettings copies non-empty Codex token values from env.
func (c *Client) LoadEnvSettings(env map[string]string) {
apply := func(setting *plugins.Setting) {
if setting == nil || setting.EnvVariable == "" {
return
}
if value := strings.TrimSpace(env[setting.EnvVariable]); value != "" {
c.setSettingValue(setting, value)
}
}
apply(c.AccessToken)
apply(c.RefreshToken)
apply(c.AccountID)
}
// ListModels returns the Codex models available to the configured account.
func (c *Client) ListModels(ctx context.Context) ([]string, error) {
if c.apiHTTPClient == nil {

View file

@ -19,6 +19,7 @@ import (
"github.com/danielmiessler/fabric/internal/chat"
"github.com/danielmiessler/fabric/internal/domain"
"github.com/danielmiessler/fabric/internal/i18n"
openaiapi "github.com/openai/openai-go"
"github.com/openai/openai-go/shared/constant"
)
@ -211,6 +212,10 @@ func TestNormalizeSemverLikeVersion(t *testing.T) {
}
func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
if _, err := i18n.Init("en"); err != nil {
t.Fatalf("i18n.Init() error = %v", err)
}
client := NewClient()
apiErr := &openaiapi.Error{StatusCode: http.StatusBadRequest}
if err := apiErr.UnmarshalJSON([]byte(`{"message":"The requested model is not supported.","type":"invalid_request_error","param":"model","code":"invalid_value"}`)); err != nil {
@ -221,8 +226,9 @@ func TestMapRequestErrorPreservesCodexAPIErrorMessage(t *testing.T) {
if err == nil {
t.Fatal("mapRequestError() returned nil")
}
if got := err.Error(); got != "codex request failed with status 400" {
t.Fatalf("mapRequestError() = %q, want %q", got, "codex request failed with status 400")
want := fmt.Sprintf(i18n.T("codex_request_failed_status"), http.StatusBadRequest)
if got := err.Error(); got != want {
t.Fatalf("mapRequestError() = %q, want %q", got, want)
}
if unwrapped := errors.Unwrap(err); unwrapped == nil || !strings.Contains(unwrapped.Error(), "The requested model is not supported.") {
t.Fatalf("wrapped error = %v, want provider detail", unwrapped)
@ -230,6 +236,10 @@ 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,
@ -242,8 +252,9 @@ func TestMapRequestErrorReadsAPIErrorResponseBodyWhenRawJSONMissing(t *testing.T
if err == nil {
t.Fatal("mapRequestError() returned nil")
}
if got := err.Error(); got != "codex request failed with status 400" {
t.Fatalf("mapRequestError() = %q, want %q", got, "codex request failed with status 400")
want := fmt.Sprintf(i18n.T("codex_request_failed_status"), http.StatusBadRequest)
if got := err.Error(); got != want {
t.Fatalf("mapRequestError() = %q, want %q", got, want)
}
if unwrapped := errors.Unwrap(err); unwrapped == nil || !strings.Contains(unwrapped.Error(), "The requested model is not supported for Codex.") {
t.Fatalf("wrapped error = %v, want provider detail", unwrapped)
@ -575,8 +586,8 @@ func TestSendStreamClosesChannelAndMapsHTTPError(t *testing.T) {
if err == nil {
t.Fatal("SendStream() error = nil, want mapped HTTP error")
}
if got := err.Error(); got != "codex usage limit reached" {
t.Fatalf("SendStream() error = %q, want %q", got, "codex usage limit reached")
if got := err.Error(); got != i18n.T("codex_usage_limit_reached") {
t.Fatalf("SendStream() error = %q, want %q", got, i18n.T("codex_usage_limit_reached"))
}
update, ok := <-updates
@ -585,6 +596,159 @@ 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()

View file

@ -77,7 +77,7 @@ func (c *Client) mapRequestError(err error) error {
case isUsageLimitMessage(message):
return &publicError{
message: i18n.T("codex_usage_limit_reached"),
cause: fmt.Errorf("codex request failed: %w", err),
cause: fmt.Errorf(i18n.T("codex_request_failed"), err),
}
default:
return err
@ -91,7 +91,7 @@ func wrapPublicError(message string, statusCode int, providerMessage string) err
return &publicError{
message: message,
cause: fmt.Errorf("codex provider error (status %d): %s", statusCode, providerMessage),
cause: fmt.Errorf(i18n.T("codex_provider_error"), statusCode, providerMessage),
}
}

View file

@ -5,6 +5,7 @@ import (
"os"
"path/filepath"
"strings"
"sync"
"time"
"github.com/danielmiessler/fabric/internal/i18n"
@ -41,6 +42,8 @@ type Db struct {
Contexts *ContextsEntity
EnvFilePath string
envMu sync.Mutex
}
func (o *Db) Configure() (err error) {
@ -92,9 +95,100 @@ func (o *Db) IsEnvFileExists() (ret bool) {
return
}
func (o *Db) SaveEnv(content string) (err error) {
err = os.WriteFile(o.EnvFilePath, []byte(content), 0644)
return
func (o *Db) SaveEnv(content string) error {
return o.WithEnvLock(func() error {
if err := writeFileAtomic(o.EnvFilePath, []byte(content)); err != nil {
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
}
return nil
})
}
func (o *Db) ReadEnvFile() (map[string]string, error) {
env, err := godotenv.Read(o.EnvFilePath)
if err != nil {
if o.IsEnvFileExists() {
return nil, fmt.Errorf(i18n.T("db_error_loading_env_file"), err)
}
return map[string]string{}, nil
}
return env, nil
}
func (o *Db) WithEnvLock(fn func() error) error {
o.envMu.Lock()
defer o.envMu.Unlock()
lockFile, err := os.OpenFile(o.EnvFilePath+".lock", os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
}
defer lockFile.Close()
if err := lockExclusive(lockFile); err != nil {
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
}
defer unlockExclusive(lockFile)
return fn()
}
// UpdateEnvVars merges non-empty values into .env under a file lock.
// Comments and key order are not preserved.
func (o *Db) UpdateEnvVars(updates map[string]string) error {
return o.WithEnvLock(func() error {
return o.ApplyEnvUpdates(updates)
})
}
// ApplyEnvUpdates writes non-empty updates atomically. Callers holding WithEnvLock use this.
func (o *Db) ApplyEnvUpdates(updates map[string]string) error {
env, err := o.ReadEnvFile()
if err != nil {
return err
}
for key, value := range updates {
if strings.TrimSpace(value) == "" {
continue
}
env[key] = value
}
if err := writeEnvFileAtomic(o.EnvFilePath, env); err != nil {
return fmt.Errorf(i18n.T("db_error_updating_env_file"), err)
}
return nil
}
func writeEnvFileAtomic(path string, env map[string]string) error {
content, err := godotenv.Marshal(env)
if err != nil {
return err
}
return writeFileAtomic(path, []byte(content+"\n"))
}
func writeFileAtomic(path string, content []byte) error {
tmp, err := os.CreateTemp(filepath.Dir(path), ".env.tmp-")
if err != nil {
return err
}
tmpName := tmp.Name()
defer os.Remove(tmpName)
if err := tmp.Chmod(0600); err != nil {
_ = tmp.Close()
return err
}
_, err = tmp.Write(content)
if err == nil {
err = tmp.Sync()
}
if cerr := tmp.Close(); err == nil {
err = cerr
}
if err != nil {
return err
}
return os.Rename(tmpName, path)
}
func (o *Db) FilePath(fileName string) (ret string) {

View file

@ -2,7 +2,12 @@ package fsdb
import (
"os"
"path/filepath"
"runtime"
"sync"
"testing"
"github.com/joho/godotenv"
)
func TestDb_Configure(t *testing.T) {
@ -52,4 +57,150 @@ func TestDb_SaveEnv(t *testing.T) {
if _, err := os.Stat(db.EnvFilePath); os.IsNotExist(err) {
t.Errorf("expected .env file to be saved")
}
assertEnvMode(t, db.EnvFilePath)
}
func TestDb_UpdateEnvVars(t *testing.T) {
dir := t.TempDir()
db := NewDb(dir)
if err := db.SaveEnv("KEEP=old\nCODEX_REFRESH_TOKEN=stale\n"); err != nil {
t.Fatalf("SaveEnv() error = %v", err)
}
if err := db.UpdateEnvVars(map[string]string{
"CODEX_REFRESH_TOKEN": "rotated",
"CODEX_ACCESS_TOKEN": "fresh",
}); err != nil {
t.Fatalf("UpdateEnvVars() error = %v", err)
}
parsed, err := godotenv.Read(db.EnvFilePath)
if err != nil {
t.Fatalf("godotenv.Read() error = %v", err)
}
if parsed["KEEP"] != "old" {
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
}
if parsed["CODEX_REFRESH_TOKEN"] != "rotated" {
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want rotated", parsed["CODEX_REFRESH_TOKEN"])
}
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
}
assertEnvMode(t, db.EnvFilePath)
}
func TestDb_UpdateEnvVars_MissingFile(t *testing.T) {
dir := t.TempDir()
db := NewDb(dir)
if err := db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "fresh"}); err != nil {
t.Fatalf("UpdateEnvVars() error = %v", err)
}
parsed, err := godotenv.Read(db.EnvFilePath)
if err != nil {
t.Fatalf("godotenv.Read() error = %v", err)
}
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
}
assertEnvMode(t, db.EnvFilePath)
}
func TestDb_UpdateEnvVars_CorruptFile(t *testing.T) {
dir := t.TempDir()
db := NewDb(dir)
if err := os.Mkdir(db.EnvFilePath, 0700); err != nil {
t.Fatalf("Mkdir() error = %v", err)
}
err := db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "fresh"})
if err == nil {
t.Fatal("UpdateEnvVars() error = nil, want corrupt-file error")
}
}
func TestDb_UpdateEnvVars_SkipEmpty(t *testing.T) {
dir := t.TempDir()
db := NewDb(dir)
if err := db.SaveEnv("CODEX_REFRESH_TOKEN=live\nKEEP=old\n"); err != nil {
t.Fatalf("SaveEnv() error = %v", err)
}
if err := db.UpdateEnvVars(map[string]string{
"CODEX_REFRESH_TOKEN": "",
"CODEX_ACCESS_TOKEN": "fresh",
}); err != nil {
t.Fatalf("UpdateEnvVars() error = %v", err)
}
parsed, err := godotenv.Read(db.EnvFilePath)
if err != nil {
t.Fatalf("godotenv.Read() error = %v", err)
}
if parsed["CODEX_REFRESH_TOKEN"] != "live" {
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want live", parsed["CODEX_REFRESH_TOKEN"])
}
if parsed["CODEX_ACCESS_TOKEN"] != "fresh" {
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want fresh", parsed["CODEX_ACCESS_TOKEN"])
}
if parsed["KEEP"] != "old" {
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
}
}
func TestDb_UpdateEnvVars_Concurrent(t *testing.T) {
dir := t.TempDir()
db := NewDb(dir)
if err := db.SaveEnv("KEEP=old\n"); err != nil {
t.Fatalf("SaveEnv() error = %v", err)
}
var wg sync.WaitGroup
errs := make(chan error, 2)
wg.Add(2)
go func() {
defer wg.Done()
errs <- db.UpdateEnvVars(map[string]string{"CODEX_ACCESS_TOKEN": "one"})
}()
go func() {
defer wg.Done()
errs <- db.UpdateEnvVars(map[string]string{"CODEX_REFRESH_TOKEN": "two"})
}()
wg.Wait()
close(errs)
for err := range errs {
if err != nil {
t.Fatalf("UpdateEnvVars() error = %v", err)
}
}
parsed, err := godotenv.Read(db.EnvFilePath)
if err != nil {
t.Fatalf("godotenv.Read() error = %v", err)
}
if parsed["KEEP"] != "old" {
t.Fatalf("KEEP = %q, want old", parsed["KEEP"])
}
if parsed["CODEX_ACCESS_TOKEN"] != "one" {
t.Fatalf("CODEX_ACCESS_TOKEN = %q, want one", parsed["CODEX_ACCESS_TOKEN"])
}
if parsed["CODEX_REFRESH_TOKEN"] != "two" {
t.Fatalf("CODEX_REFRESH_TOKEN = %q, want two", parsed["CODEX_REFRESH_TOKEN"])
}
}
func assertEnvMode(t *testing.T, path string) {
t.Helper()
if runtime.GOOS == "windows" {
return
}
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%s) error = %v", path, err)
}
if perm := info.Mode().Perm(); perm != 0600 {
t.Fatalf("%s mode = %o, want 0600", filepath.Base(path), perm)
}
}

View file

@ -0,0 +1,22 @@
//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
}

View file

@ -0,0 +1,19 @@
//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)
}

View file

@ -5,8 +5,8 @@ schema = 3
version = "v0.123.0"
hash = "sha256-OjuvlqYrGvoUiXO/5ALbMxHvXRlrhDjLby1BN/nuhAw="
[mod."cloud.google.com/go/auth"]
version = "v0.22.0"
hash = "sha256-EZmoz4OvycM9aIS1M3qexFo6ThMk0tESjdtq9mQytNQ="
version = "v0.23.2"
hash = "sha256-D28j2x5WFAg56/zTI9jGTH9MDUhteBo/RP1OKRKjB3s="
[mod."cloud.google.com/go/auth/oauth2adapt"]
version = "v0.2.8"
hash = "sha256-GoXFqAbp1WO1tDj07PF5EyxDYvCBP0l0qwxY2oV2hfc="
@ -17,8 +17,8 @@ schema = 3
version = "v1.0.2"
hash = "sha256-p6jdiHlLEfZES8vJnDywG4aVzIe16p0CU6iglglIweA="
[mod."github.com/Azure/azure-sdk-for-go/sdk/azcore"]
version = "v1.22.0"
hash = "sha256-JhJgAeC3G0l+th1Xdu0Vf+FzWXTALHyb5FSIsKYTgBo="
version = "v1.23.0"
hash = "sha256-eOzqLjG50rNk/gZ264KPMNq0BaSuzH4saQBbvaWJpOQ="
[mod."github.com/Azure/azure-sdk-for-go/sdk/azidentity"]
version = "v1.14.0"
hash = "sha256-oUoSbjql472sT24daGzICuBMEQLjpHXooGT83KtnUfc="
@ -26,8 +26,8 @@ schema = 3
version = "v1.12.0"
hash = "sha256-jX/JR3WToz0vEWowfDBVjpul9EE2V2xGF1EfckrhDus="
[mod."github.com/AzureAD/microsoft-authentication-library-for-go"]
version = "v1.7.2"
hash = "sha256-zB4M5bgMGDcjvF4GKu2t+NHTIHm9xQbsw9CUGT0tkk8="
version = "v1.9.0"
hash = "sha256-K29z7ruv8vG4PIz6O4DiXBenh/NAfySIrjb8ywl037A="
[mod."github.com/KyleBanks/depth"]
version = "v1.2.1"
hash = "sha256-czR52MfeKA2FdStXCebTMQRKT8jaWQcbV214O3j49qU="
@ -41,8 +41,8 @@ schema = 3
version = "v1.3.4"
hash = "sha256-CpXE/C+gW7V8MXREZguszzAQumizrCXGTdDRPBaflCs="
[mod."github.com/anthropics/anthropic-sdk-go"]
version = "v1.61.0"
hash = "sha256-xAnuyZes72nZoanCzjjIbC22bcApU4mfVHGh3C1VhNg="
version = "v1.67.0"
hash = "sha256-mdEVuL2R8bhhrNAfAiHrvyew/cS0FTBtnsV/LFhC/LY="
[mod."github.com/araddon/dateparse"]
version = "v0.0.0-20210429162001-6b43995a97de"
hash = "sha256-UuX84naeRGMsFOgIgRoBHG5sNy1CzBkWPKmd6VbLwFw="
@ -50,77 +50,77 @@ schema = 3
version = "v0.1.4"
hash = "sha256-ZZ7U5X0gWOu8zcjZcWbcpzGOGdycwq0TjTFh/eZHjXk="
[mod."github.com/aws/aws-sdk-go-v2"]
version = "v1.43.0"
hash = "sha256-4aQxyJbnh6Swz+VDlsdtnX2yup5ou56OwS8EYHddmJE="
version = "v1.44.0"
hash = "sha256-0GXyEXecrEAKw1FxElG7rTPyXns5MpgvtEJFO8PSh+0="
[mod."github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream"]
version = "v1.7.14"
hash = "sha256-EJ3UrrMG10cTJLL8hIZ1SFR8O6J9rpdicL7x4RSk5CY="
version = "v1.7.20"
hash = "sha256-ICB2yD83Gl628LRkg3HhYlDtsL4xgGhOmphZ5D0bJXI="
[mod."github.com/aws/aws-sdk-go-v2/config"]
version = "v1.32.31"
hash = "sha256-D2jpMMLn5AgkXpZbbpab3+thofD6xCScixtRF2K32LU="
version = "v1.32.40"
hash = "sha256-aCTxFSDt/1hSgFSAAwHQzjSrOcSDg0lAt1ely0Eb6X4="
[mod."github.com/aws/aws-sdk-go-v2/credentials"]
version = "v1.19.30"
hash = "sha256-SoO465t2X7w3o7hGy88N1Zy6AEcG/LiFZ7xnPUmt7Jw="
version = "v1.19.39"
hash = "sha256-zPAy8z+s46o9En+df+FhYQCNNP58S+VLGAWyBIMHx5k="
[mod."github.com/aws/aws-sdk-go-v2/feature/ec2/imds"]
version = "v1.18.31"
hash = "sha256-6X1A4Cz7j40SaSUzS3u+QRbBzmHHZmValGBJeSHMQUo="
version = "v1.18.40"
hash = "sha256-LXOaz48PXTgjwbFYQ9cUc1Y9cmPODypl6SpZz9GLHZg="
[mod."github.com/aws/aws-sdk-go-v2/internal/configsources"]
version = "v1.4.31"
hash = "sha256-xv5BgkSZbTJwgDN8mtUCg3Q6Ac0S/IdPZW1tTrEsGgY="
version = "v1.4.40"
hash = "sha256-1EmqriwNg/7JCqpj7vnGB2oHgMrm/EgkLlGMhBTNh3k="
[mod."github.com/aws/aws-sdk-go-v2/internal/endpoints/v2"]
version = "v2.7.31"
hash = "sha256-dvD/UE5fVVxOb668bZNg5hM6H9F4foclI5HbD+b7Dks="
version = "v2.7.40"
hash = "sha256-XnMvLK1GmJcPNLov91EBKh+xpwIRk6DqlzXiI+epyd8="
[mod."github.com/aws/aws-sdk-go-v2/internal/v4a"]
version = "v1.4.32"
hash = "sha256-EovofAvyGZoaQsNwXl2F6Uc0+jNwRYV8At6if3Ki/z8="
version = "v1.4.41"
hash = "sha256-tgerq8bZzDemsJsVdBzHCEm/S2TyPpdSmG+uwCd9XJ0="
[mod."github.com/aws/aws-sdk-go-v2/service/bedrock"]
version = "v1.66.0"
hash = "sha256-xlv9OA8FmWK8Y1NuL9sarAvRQWP6iK0Vr/CsCkOCe2E="
version = "v1.68.0"
hash = "sha256-NKNT8mKBH3TDrCzH4WD1ukHEp+9tzojxrxpLXajiZ/8="
[mod."github.com/aws/aws-sdk-go-v2/service/bedrockruntime"]
version = "v1.56.0"
hash = "sha256-fNMQSKKnVasKUyEybcBlkP7/vCDM+cvyAgYhFwZ6Dyw="
version = "v1.58.0"
hash = "sha256-1vapY7IXJvcsWEn4rDaI8nSIfPCmuDHZPD9gP0DYHYA="
[mod."github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding"]
version = "v1.13.13"
hash = "sha256-C0L0B5qRP8IhX02Wz/wttNtOkI6xrI9VHQr/FiTCUsc="
version = "v1.13.19"
hash = "sha256-LfifyqFh49PPH0asF1TJz3QJYHTwxQttthUD5p4z2YM="
[mod."github.com/aws/aws-sdk-go-v2/service/internal/presigned-url"]
version = "v1.13.31"
hash = "sha256-43rStuRFoLjAAz/arhzCNsyxkX1DAlnQiKFbGv6AXSA="
version = "v1.13.40"
hash = "sha256-l88t3s/MssSelRkyukz4fYSs7mZCOvfCeNrsfzdyKuI="
[mod."github.com/aws/aws-sdk-go-v2/service/signin"]
version = "v1.5.0"
hash = "sha256-zty6NW++dIHGZiy4rBCkDLZVfDf9FSc1ouMeaUI42sc="
version = "v1.6.0"
hash = "sha256-NxIbOpqkwWKLSlZJx/dRoNMQ0QDh6jolC8xKJTxxo9A="
[mod."github.com/aws/aws-sdk-go-v2/service/sso"]
version = "v1.33.0"
hash = "sha256-1IpQJTXInKuQUXIX6pwxiMgwzJx9tO/n9ZEHn5vrkP0="
version = "v1.34.0"
hash = "sha256-yRdflsmsXNXki2xJLEtQur6Dpbb9N3beLdXD5Vu2Ss0="
[mod."github.com/aws/aws-sdk-go-v2/service/ssooidc"]
version = "v1.38.0"
hash = "sha256-FYqk0nJsW9FEMSjq9Rl9lOKtXvJ9AHAvsH0AZSohKbY="
version = "v1.39.0"
hash = "sha256-XGRs+nBRUfxyuCbDJ7QnV738UPMm2nSbxbLtK19hqY0="
[mod."github.com/aws/aws-sdk-go-v2/service/sts"]
version = "v1.45.0"
hash = "sha256-5CuJRPHxwORAl/YYeLFW9JSlT4J1AqDUaOSfqxG6vgE="
version = "v1.46.0"
hash = "sha256-7j2UmV1FHIcJ/K+iUhGGO2Q+dhuoHlOxR1TuCf4M68Y="
[mod."github.com/aws/smithy-go"]
version = "v1.27.4"
hash = "sha256-V4BvRCkpbkBbFRKyxV+hGDPDKUd1nZYcr+WQ09GqC/k="
version = "v1.28.1"
hash = "sha256-52wJGnuZVxhZtqgp4Gf7mgYrK6DOL8hFgtjo8e1NlCA="
[mod."github.com/bahlo/generic-list-go"]
version = "v0.2.0"
hash = "sha256-BIzqwG61hnMDknZOn/5+VX09yemzFzMjhPF48XoALto="
[mod."github.com/buger/jsonparser"]
version = "v1.2.0"
hash = "sha256-CiwV8UFYw2PnnyUoRojiVLq+MlKJ/aviufLcRi1GLbw="
version = "v1.6.1"
hash = "sha256-barqoLEuh2FBMNG4kIjK3oRs8Izq4eVotpbzHPliqXc="
[mod."github.com/bytedance/gopkg"]
version = "v0.1.4"
hash = "sha256-GpfG8K5YTWAt5MRg7RvpcemU7jfPV0A+mMbxSfA+2ws="
[mod."github.com/bytedance/sonic"]
version = "v1.15.2"
hash = "sha256-FVhsfwSb5q7MzdVuPtElecrz8E62DJVfG2YI4mScuzk="
version = "v1.15.3"
hash = "sha256-/LTRsTbgpQPLEMX7CRIMxi/Unh9VAyys6QuALGWwgMc="
[mod."github.com/bytedance/sonic/loader"]
version = "v0.5.1"
hash = "sha256-fbUguA0wNj+aggu3oHaEzkduQVO4oawcvjohfgUmha8="
version = "v0.5.2"
hash = "sha256-tsrtIbjYXZadwLzlL3ZmC4DEytwmT1tvPDQT/nwNb20="
[mod."github.com/cespare/xxhash/v2"]
version = "v2.3.0"
hash = "sha256-7hRlwSR+fos1kx4VZmJ/7snR7zHh8ZFKX+qqqqGcQpY="
[mod."github.com/cloudflare/circl"]
version = "v1.6.4"
hash = "sha256-HfbrlhDNcQ7shn2dBzUrXJRtRPMShjo+HiqquEwzByI="
version = "v1.6.5"
hash = "sha256-nrD+9cNt6xEohb0EA2bZCuSM03GXYPg/YMD0Y+HF8WU="
[mod."github.com/cloudwego/base64x"]
version = "v0.1.7"
hash = "sha256-zhZGPCTM8HiLALQfaE/L7B0IwbXZxClDVLCFt5zbusE="
@ -155,8 +155,8 @@ schema = 3
version = "v5.9.1"
hash = "sha256-VW8mfdLjvAuiooR2WV7P0IoOosAvtDZvFWcGQJJVSSY="
[mod."github.com/go-git/go-git/v5"]
version = "v5.19.1"
hash = "sha256-C0e3oOXYRgLUhm1TQk9mBwRwwj6iusxFAt5U9vqWz8A="
version = "v5.19.2"
hash = "sha256-2du2hn5JDTEEYTWuGX+heSftkmd7HoaJXyWJG4bZeSM="
[mod."github.com/go-logr/logr"]
version = "v1.4.4"
hash = "sha256-q9HX9aelONTLsywyLY4Wc5UYCK+wG0FbSzFWisCYiIA="
@ -167,32 +167,32 @@ schema = 3
version = "v1.0.0"
hash = "sha256-he4+Ue27HuNS0imS55XaS4g54v2OrKFJppdhz8cO94s="
[mod."github.com/go-openapi/jsonreference"]
version = "v1.0.0"
hash = "sha256-+UaHDX6kNBxmhH6N2BE22KyXISiEvxPyTpJF5i6l+GE="
version = "v1.0.1"
hash = "sha256-UMGGlJPWjL7SoeLd51PLyYnUuQMz1Jx41MuULCwQUp4="
[mod."github.com/go-openapi/spec"]
version = "v0.22.9"
hash = "sha256-TJBuxXaAct9XElR6juOEa5jNSuiLNoMrWNscr3ygsEk="
version = "v0.22.11"
hash = "sha256-QmZOssbAZDUKdk/hZUmOxDYIa4u2VCIGm6E8/gY1y4Y="
[mod."github.com/go-openapi/swag/conv"]
version = "v0.27.3"
hash = "sha256-8IoLDL3UsjWefUgvndFTevdeJhKhN1Qi8cm3j8NpH9U="
version = "v0.29.1"
hash = "sha256-hOh54OqE1yVfy5O2FRuHZZKejpUk26eM8qzB0ndoJhM="
[mod."github.com/go-openapi/swag/jsonutils"]
version = "v0.27.3"
hash = "sha256-kksIySYFFusCXd+Iua+G2IjEfb+I1xCXlv7YH4E1/8o="
version = "v0.29.1"
hash = "sha256-Yj1UfqLEwJA5PWQTs3EbcohqxlknhFFwkYJXoO/Y6c0="
[mod."github.com/go-openapi/swag/loading"]
version = "v0.27.3"
hash = "sha256-HPIc1YHoh06K1OExI6NnqsOKx5L+CzArTfXanTSYABk="
version = "v0.29.1"
hash = "sha256-ig2w4q11AOWerFTVN6a2MdZ4mKiFXWkeiTCDuWFwUxY="
[mod."github.com/go-openapi/swag/pools"]
version = "v0.27.3"
hash = "sha256-v6e+U41LGxzT1aT4KV6PQvowY3N0Y6Z2zCItIpAuoG0="
version = "v0.29.1"
hash = "sha256-NhaFtD29d759M/H5AiYiHAxn6s4bkfYW63B7xFpkGvA="
[mod."github.com/go-openapi/swag/stringutils"]
version = "v0.27.3"
hash = "sha256-QnCMIHYGwrMMzZBRFz3zvOoQWPgzToMIMRCfujww8Vw="
version = "v0.29.1"
hash = "sha256-VLkfHTvfO+yU6VMNdzSp3rb2v/A4OqpScBHq77GoOA8="
[mod."github.com/go-openapi/swag/typeutils"]
version = "v0.27.3"
hash = "sha256-7DkJz+zFA2Zu6iIkoExf2H3gKKRg47KfzGE2aSXiehs="
version = "v0.29.1"
hash = "sha256-xsKC/xVt7LdZIHOv+h5KwJEULyebSFPTghUj1LC04uQ="
[mod."github.com/go-openapi/swag/yamlutils"]
version = "v0.27.3"
hash = "sha256-dDH3tA5gTeifnUgq595ERiwWxyaoOpLpXsziXNxmC9U="
version = "v0.29.1"
hash = "sha256-nrru5csGEKnr+ff28sVdkJNubhjbLwY8cPp9JqSl1DE="
[mod."github.com/go-playground/locales"]
version = "v0.14.1"
hash = "sha256-BMJGAexq96waZn60DJXZfByRHb8zA/JP/i6f/YrW9oQ="
@ -239,11 +239,11 @@ schema = 3
version = "v1.6.0"
hash = "sha256-VWl9sqUzdOuhW0KzQlv0gwwUQClYkmZwSydHG2sALYw="
[mod."github.com/googleapis/enterprise-certificate-proxy"]
version = "v0.3.19"
hash = "sha256-I9lw9E0WvndUTWiwOlBQv6ySF6/+SS/cXeGLJnPlri4="
version = "v0.3.21"
hash = "sha256-h8t4jzU7hRY/KaSI51xAkxX2934i5k6XVvpuNPef4iE="
[mod."github.com/googleapis/gax-go/v2"]
version = "v2.23.0"
hash = "sha256-aIJepJZI5PCsc9eteAP9x+n4RSSyOWi4kPnI5mmrBXk="
version = "v2.24.0"
hash = "sha256-uDcY93yvQS3wDkhM85bAxSo67Z+cWlwR1Vnv9hZRa5o="
[mod."github.com/gorilla/websocket"]
version = "v1.5.3"
hash = "sha256-vTIGEFMEi+30ZdO6ffMNJ/kId6pZs5bbyqov8xe9BM0="
@ -290,8 +290,8 @@ schema = 3
version = "v0.0.24"
hash = "sha256-pmq8KKIb2+qMF3E1cR2B7NAj5joF2tZduyXjIfIlkTk="
[mod."github.com/mattn/go-sqlite3"]
version = "v1.14.48"
hash = "sha256-a2Cs9fotWnjY+9WaOnrHk6LbbP4idOHo//tAAlc6naI="
version = "v1.14.50"
hash = "sha256-olsWcNOIhMP67ZfuYvxxe3AHvyynAUmJ6WbDXfc1VbE="
[mod."github.com/modern-go/concurrent"]
version = "v0.0.0-20180306012644-bacd9c7ef1dd"
hash = "sha256-OTySieAgPWR4oJnlohaFTeK1tRaVp/b0d1rYY8xKMzo="
@ -302,8 +302,8 @@ schema = 3
version = "v2.6.1"
hash = "sha256-ag/8GBAwqkOyIVrdlaFYLxy9dgPOq7VbactrLmzxK7E="
[mod."github.com/ollama/ollama"]
version = "v0.32.3"
hash = "sha256-giQRZODIilkGdEtyrzTMkBI1cUXMpVSZorv06U9aVA4="
version = "v0.33.1"
hash = "sha256-E297N5FElu4OEDM75FHekPr/fyxMpu+7M3GS3Robals="
[mod."github.com/openai/openai-go"]
version = "v1.12.0"
hash = "sha256-JHLlKvDwERPf728GUXBsKU58ODgCxcxEe9TKJTGAG1w="
@ -359,8 +359,8 @@ schema = 3
version = "v0.0.1"
hash = "sha256-ORbb8w6VS4Yw0vsPhVzAqCCUYyaMOPcQyyP4DBZgLzM="
[mod."github.com/stretchr/testify"]
version = "v1.11.1"
hash = "sha256-sWfjkuKJyDllDEtnM8sb/pdLzPQmUYWYtmeWz/5suUc="
version = "v1.12.1"
hash = "sha256-9MTDdVjZMh1MJ5EH3HmAFrm24YqZayeMBgKwtaURZCc="
[mod."github.com/swaggo/files"]
version = "v1.0.1"
hash = "sha256-bNBmpJaM7g1BNwd7VxNIRSdY35NKSXhYHGfnZsSEUZ8="
@ -386,8 +386,8 @@ schema = 3
version = "v0.15.1"
hash = "sha256-HLk6oUe7EoITrNvP0y8D6BtIgIcmDZYtb/xl/dufIoY="
[mod."github.com/ugorji/go/codec"]
version = "v1.3.1"
hash = "sha256-VQtXVaKxXjm5Q60hCgVKZxNywl6SJFPqju6JNjADp4w="
version = "v1.3.2"
hash = "sha256-/W4sowusGHO1Fb4mlqNQlfqgl278E0BqgMUzexkzWVw="
[mod."github.com/wk8/go-ordered-map/v2"]
version = "v2.1.8"
hash = "sha256-v7/5+7lAypZfgClXgWxhxtA1skQq9o+1yrI+V0o1j2o="
@ -395,44 +395,44 @@ schema = 3
version = "v0.3.3"
hash = "sha256-l3pGB6IdzcPA/HLk93sSN6NM2pKPy+bVOoacR5RC2+c="
[mod."go.mongodb.org/mongo-driver/v2"]
version = "v2.8.0"
hash = "sha256-VaSRntfFY0SBfIPmhFVY0jPNlv0pYp8ivwdOoeLTeIs="
version = "v2.8.2"
hash = "sha256-1syL133Npfw250xMeOfEhExvEYFU7GTnWsdHCOOmNuA="
[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.69.0"
hash = "sha256-37oe5RsiBWxaOs7lZXJWEUN4P49jkr2ej+64FQ4ybT8="
version = "v0.71.0"
hash = "sha256-2vwtDspRM7jZyvxclBmKTCDZ31qw/TiG+HHI6Z8ub2s="
[mod."go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"]
version = "v0.69.0"
hash = "sha256-ypZB41ebghWCNZmuoDgdGbWyTjCglBEirLZ+s7jqCgc="
version = "v0.71.0"
hash = "sha256-E5YtRLrs1/LqpvPt6LzC+sDpmxSROgpNmgca5s6iyCg="
[mod."go.opentelemetry.io/otel"]
version = "v1.44.0"
hash = "sha256-XTGV9RjOIKyWrrgXJ32IA59lovxXqSeRrpcpC1FAFZA="
version = "v1.46.0"
hash = "sha256-2+xTacXn22hsJuMGGgXwazMbP/ZW5gkPRnf+rNUZgvQ="
[mod."go.opentelemetry.io/otel/metric"]
version = "v1.44.0"
hash = "sha256-e8dVyRoavby/YG7aH2T05IhWHlLvwnMHyPhelAS1mCQ="
version = "v1.46.0"
hash = "sha256-cY1DXXzRfcP0kKh7oBzowbu6kbFr3jANiEVyMlD5Bt8="
[mod."go.opentelemetry.io/otel/trace"]
version = "v1.44.0"
hash = "sha256-69HorWRTLLzAHRgLe4nPp13qJthb3qp7RU7c0Y9qV/c="
version = "v1.46.0"
hash = "sha256-zfj85gCR5Z3v1N+8nlOO+6+FVShaL/7Vb/3lXjdXnnw="
[mod."go.yaml.in/yaml/v3"]
version = "v3.0.4"
hash = "sha256-NkGFiDPoCxbr3LFsI6OCygjjkY0rdmg5ggvVVwpyDQ4="
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="
[mod."golang.org/x/arch"]
version = "v0.29.0"
hash = "sha256-CqC8aNJdS0Qtr31j2ao9hTQJk8KRfcHDL7/HL3mbD4g="
version = "v0.30.0"
hash = "sha256-od8lOmP+IOtiO7s1VaFVMBCQfRmtXT8jqe7Yo077xDs="
[mod."golang.org/x/crypto"]
version = "v0.54.0"
hash = "sha256-m8jsqlPuoPxGDjClzcAVbXf1aiU07L+9j7vYcqJu/0Q="
version = "v0.55.0"
hash = "sha256-99uV/ESeqhaDEwqbXxDpePuwzO0JoEHGhqZXZVHq/H4="
[mod."golang.org/x/mod"]
version = "v0.38.0"
hash = "sha256-BpKfvsmb7ecEDvTBKx9ZHauxoWRkOAZCqg4BrHM2/S0="
version = "v0.40.0"
hash = "sha256-gmuKtxidzYWVlQYNB/8BREmajyfockaQ5/6Xk4IR32A="
[mod."golang.org/x/net"]
version = "v0.57.0"
hash = "sha256-gKo8UMw4hfETBHm8N5GOfuNadse9TWEQXJo2YWq5bY4="
version = "v0.58.0"
hash = "sha256-fDOkQJXURrbq2dBGa91bCUsXknCZlo3ru2JIofSOGRo="
[mod."golang.org/x/oauth2"]
version = "v0.36.0"
hash = "sha256-evS7WkMrpgonmTcqtWFpC5rSKZN8O+vnAhNUs1MS9kw="
@ -443,29 +443,29 @@ schema = 3
version = "v0.47.0"
hash = "sha256-TpbRyWWqHjddP6QzUgAbaLd2EE0S+GYNRUIDJd18r98="
[mod."golang.org/x/text"]
version = "v0.40.0"
hash = "sha256-LJfnki46XEreGbSgjl+DeqgcTsINTOu2owyXNvijMcA="
version = "v0.41.0"
hash = "sha256-22nHcolG87qSPahT2Ey8S5iGlCLAglE9ObYXO6XZ3ZY="
[mod."golang.org/x/time"]
version = "v0.15.0"
hash = "sha256-5D24A65wn7k93Jj3+918UKjB9ccmGHPBEqjD2XDB92E="
[mod."golang.org/x/tools"]
version = "v0.48.0"
hash = "sha256-9cRNUaup6fexA5S1zc+Ic3aaoxAgKWntyMJ2xaOdiP8="
version = "v0.49.0"
hash = "sha256-3oaXp2PlfuIn6/UTarnYD279YqJKTyaCWT/KD7ABjD4="
[mod."google.golang.org/api"]
version = "v0.290.0"
hash = "sha256-qqNG4ExSaHNa9iRHq/F4JXqdnegRrWFx8oMKEEMgApA="
version = "v0.294.0"
hash = "sha256-fSc8hIUo3a92Rf7CmI3D+gL5Vk33e5alr8EIIR613bc="
[mod."google.golang.org/genai"]
version = "v1.65.0"
hash = "sha256-PtEwnslN9VBUur0fNwXLBryOzvw2zkHfHSlb0v/z+DQ="
version = "v1.70.0"
hash = "sha256-T0jXx52Hc3aOROPpX66RGhZs0FrRg5Bhqm3WyR1zjkM="
[mod."google.golang.org/genproto/googleapis/rpc"]
version = "v0.0.0-20260724162435-b2f20204f0df"
hash = "sha256-ldJTTb7hhj1mdmzTn9IEkQVwCoj3KRlENZtUSEKHABU="
version = "v0.0.0-20260825221802-da73d73af1c5"
hash = "sha256-CQdjYGIrgAnEDfWW3DIJF9Orf7oJdhx3G9rGg6fow88="
[mod."google.golang.org/grpc"]
version = "v1.82.1"
hash = "sha256-5Q85pZWiKulJSGRAjYcVrlBe7y4tfa9gsjCqYiBhLUU="
version = "v1.83.2"
hash = "sha256-q5VtdeJvWM7kbIvui8ncNV/+Rm+d0y/b6XkNN0/u1is="
[mod."google.golang.org/protobuf"]
version = "v1.36.11"
hash = "sha256-7W+6jntfI/awWL3JP6yQedxqP5S9o3XvPgJ2XxxsIeE="
version = "v1.36.12"
hash = "sha256-MKd0AdkWEe2A79mGPzOY5I76nMqTsGJKQNPJOKyOwkQ="
[mod."gopkg.in/warnings.v0"]
version = "v0.1.2"
hash = "sha256-ATVL9yEmgYbkJ1DkltDGRn/auGAjqGOfjQyBYyUo8s8="

View file

@ -1 +1 @@
"1.4.470"
"1.4.471"