Compare commits

19 Commits
Author SHA1 Message Date
github-actions[bot] 7b37eb4602 chore: bump Cargo.toml and sandbox image to 0.10.1
CI / All (macos-latest) (push) Waiting to run
CI / All (windows-latest) (push) Waiting to run
CI / All (ubuntu-latest) (push) Failing after 29s
2026-08-31 20:08:42 +00:00
github-actions[bot] 40996a06fe bump: version 0.10.0 → 0.10.1 [skip ci] 2026-08-31 20:08:39 +00:00
Dark-Alex-17 00a21ee777 Merge branch 'main' of github.com:Dark-Alex-17/coyote
CI / All (macos-latest) (push) Waiting to run
CI / All (windows-latest) (push) Waiting to run
CI / All (ubuntu-latest) (push) Failing after 32s
2026-08-31 13:49:09 -06:00
Dark-Alex-17 016f2654e4 fix: make binary shims for custom tools cross-device compatible so users can't accidentally break sandboxes via sbx cp ~/.config/coyote <sbx-name>:/home/agent/.config 2026-08-31 13:48:56 -06:00
Dark-Alex-17 9a1d00f5e6 docs: fixed a broken link in the readme 2026-08-29 13:16:34 -06:00
Dark-Alex-17 a14c768f9a docs: corrected a typo in the README 2026-08-29 12:59:33 -06:00
github-actions[bot] c102da5665 chore: bump Cargo.toml and sandbox image to 0.10.0
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-29 05:49:07 +00:00
github-actions[bot] 4402d254f6 bump: version 0.9.0 → 0.10.0 [skip ci] 2026-08-29 05:49:04 +00:00
Dark-Alex-17 02974c8abc docs: reset changelog 2026-08-28 23:48:33 -06:00
Dark-Alex-17 9f7eeb7dc6 fix: Harden install scripts: detect libssl3 without ldconfig on PATH, survive noexec tmp dirs, and guard against partial curl|bash execution 2026-08-28 23:46:14 -06:00
Dark-Alex-17 f3d44212d5 fix: ldconfig on Debian-based distros lives in /usr/sbin 2026-08-28 23:04:35 -06:00
Dark-Alex-17 f0661b4ea4 feat: Support Linux ARM64 GNU 2026-08-28 22:49:58 -06:00
Dark-Alex-17 0946e3ec64 feat: Note that duckdb RAG driver is unavailable on MUSL Linux builds when initializing a RAG so users know when using the binary, not just based on docs 2026-08-28 22:35:39 -06:00
Dark-Alex-17 65e6249adb feat: updated coyote installation scripts to detect when gnu is installable 2026-08-28 22:23:31 -06:00
Dark-Alex-17 18c2cfeb41 fix: Prefer GNU linux builds in install scripts for duckdb support, and gate duckdb support on linux hosts that are MUSL until MUSL support is added 2026-08-28 20:06:39 -06:00
Dark-Alex-17 50d2a74e80 docs: updated README duckdb install script 2026-08-28 20:06:09 -06:00
github-actions[bot] ea001ade7a bump: version 0.8.3 → 0.9.0 [skip ci] 2026-08-29 00:36:51 +00:00
Dark-Alex-17 baa45970da feat: upgraded to v2 mixin schema
CI / All (ubuntu-latest) (push) Failing after 29s
CI / All (macos-latest) (push) Canceled after 0s
CI / All (windows-latest) (push) Canceled after 0s
2026-08-28 18:24:58 -06:00
Dark-Alex-17 5039b74926 test: fix Windows crlf issue and pin the template to lf to prevent future regressions 2026-08-28 18:03:12 -06:00
21 changed files with 1105 additions and 504 deletions
+1
View File
@@ -0,0 +1 @@
assets/config-template.yaml text eol=lf
+4 -1
View File
@@ -166,6 +166,9 @@ jobs:
os: ubuntu-latest os: ubuntu-latest
use-cross: true use-cross: true
cargo-flags: '' cargo-flags: ''
- target: aarch64-unknown-linux-gnu
os: ubuntu-24.04-arm
cargo-flags: ''
- target: aarch64-apple-darwin - target: aarch64-apple-darwin
os: macos-latest os: macos-latest
use-cross: true use-cross: true
@@ -253,7 +256,7 @@ jobs:
run: echo "BUILD_CMD=cross" >> $GITHUB_ENV run: echo "BUILD_CMD=cross" >> $GITHUB_ENV
- name: Install latest LLVM/Clang - name: Install latest LLVM/Clang
if: matrix.os == 'ubuntu-latest' if: startsWith(matrix.os, 'ubuntu')
run: | run: |
wget https://apt.llvm.org/llvm.sh wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh chmod +x llvm.sh
+199
View File
@@ -1,3 +1,202 @@
## v0.10.1 (2026-08-31)
### Fix
- make binary shims for custom tools cross-device compatible so users can't accidentally break sandboxes via `sbx cp ~/.config/coyote <sbx-name>:/home/agent/.config`
## v0.10.0 (2026-08-29)
### BREAKING CHANGE
- invocations using --prompt <text> must switch to
--temp-role <text>; clap rejects the old flag loudly.
### Feat
- Support Linux ARM64 GNU
- Note that duckdb RAG driver is unavailable on MUSL Linux builds when initializing a RAG so users know when using the binary, not just based on docs
- updated coyote installation scripts to detect when gnu is installable
- upgraded to v2 mixin schema
- improved first-time run experience and included a templated configuration file that now has comments like the config.example.yaml so users don't have to go to the repo to see all the knobs
- **TASK-005**: carry the quality bar through sisyphus and architect
- **TASK-004**: reviewer routing — code-reviewer quality-bar resolution, domain linter pass, surface-skill routing, rigor folding; file-reviewer surface-skill whitelist + convention/correctness marker
- **TASK-003**: add surface skills batch B — worker-review, iac-review, migration-review, cicd-review
- **TASK-002**: add surface skills batch A — rest-api-review, cli-review, library-review
- **TASK-001**: add rigor/surfaces declaration layer to planning skills
- **repl**: color .info mcp-server verdicts and complete only running servers
- **cli**: rename mcp_config asset category to mcp-config with snake_case alias
- create a new probe agent to probe code verifications using usage-pattern testing
- Add in the web_search_coyote tool where helpful
- **mcp**: add .info mcp-server, .set mcp_tools, and filtered-server listing to the REPL
- **mcp**: enforce per-server tool allowlists across runtime, jobs, agents, and graph nodes
- **mcp**: add mcp_tools allowlist config surfaces across roles, sessions, agents, graphs, and skills
- **mcp**: add per-server tool allowlist policy module and allowedTools config field
- support mcp.json in the root of bundle repos as well as in the legacy functions directory
- prefer ~/.config/coyote/mcp.json for the user-scope MCP config
- claude and openai native web search via web_search_coyote
- **jobs**: node-local job ownership and capability-gated job__* visibility
- **graph**: support max_concurrent_jobs at the graph level
- **jobs**: allow uncapped collect via full_result
- **jobs**: exempt polling tools from loop tracker and hint on unchanged checks
- **supervisor**: push agent completion notifications to the spawning context
- **jobs**: push background-job completion notifications via per-context queue
- inject background-jobs prompt guidance when jobs are enabled
- add background job runner, job__* handlers, and start gates
- generalize supervisor registry to TaskHandle enum with job scaffolding, kill discipline, and max_concurrent_jobs config
- run an advisory observability pass after implementation in sisyphus
- add an observability-review skill for post-implementation monitoring analysis
- check under- and over-logging in the code review gate
- calibrate logging registers in the code-writing agents
- add a logging-discipline skill for calibrating log output to repo conventions
- **cli**: rename --prompt to --temp-role
- complete --filter and --force on the first .install argument
- **mcp**: bound tool-result passthrough and surface resource audience annotations
- **mcp**: add mcp_prompt meta-tool and harden prompt display rendering
- **repl**: add .prompt command with live staged tab-completion
- **mcp**: add mcp_read meta-tool for resource reads
- **mcp**: gate meta-function emission on advertised server capabilities
- **mcp**: add render.rs content policy (text paging, pattern filter, blob spill)
- **mcp**: extend the server catalog to resources, templates, and prompts
- complete --filter and --force on the first .install argument
- run design interviews as grilling frontier rounds across the planning agents
- add a grilling skill for frontier-round design interviews
- add an on-demand architecture-reviewer agent for deepening scans
- add a codebase-design skill with the deep-module design vocabulary
- add a feedback-loop-first diagnosing-bugs skill to the coding suite
- add a Fowler code-smell baseline to the code-review skill
- flag duplicate helpers in code reviews with a repo-wide DRY check
- add a transactional-integrity review skill to the code review gate
- add an operational-history prior-art lane to the code-reviewer agent
- support name=value macro arguments with variable tab completion
- add --help guides to the .install and .uninstall REPL commands
- expand owner/repo shorthand for --install with a --git-host flag
- remove the deprecated --install-from flag and .install remote form
- rename install flags and unify .install dispatch
- add --uninstall and .uninstall for installed bundles
- add --update-bundle with provenance-aware conflict handling
- add --list-bundles and .list bundles with drift detection
- record bundle provenance when installing from remote repos
- add bundle provenance store
- parse bundle manifests and capture resolved SHAs for remote installs
- created a new comment-discipline skill for the built in sisyphus suite
- created a dedicated git_command tool to tighten tool calling permissions in the git-master skill
- Added a new security review step to the code writing quality gates
- addressed review comments
- add --no-workspace-macros opt-out for workspace macro loading
- execute non-isolated macros on the live REPL context
- surface macros as first-class custom commands in the REPL
- add lazy macro resolver with two-dir discovery and per-macro states
- add enabled_macros config field at global, role, agent, and session levels
- add description and isolated fields to Macro struct
- Dynamically detect RAG embedding model dimension for any given model
- Support auto confirmation for gatekeeper agents
- retry LLM API calls once after 401 by force-refreshing the OAuth token
- identity-aware rejected-token marker for LLM OAuth cache
- typed ApiStatusError carrying HTTP status through LLM client errors
- per-request OAuth token injection with mid-session refresh for HTTP MCP servers
- reason-specific warnings for MCP servers that fail OAuth at startup
- Installed duckdb into the coyote image
- Support managing MCP servers from the CLI directly
- append new built-in rag__query function to RAG contexts to allow further querying by LLMs
- support static file bundling with sbx-mixins
- **rag**: offer the storage driver when an agent initializes its RAG
- simplified the duckdb selection prompt
- **rag**: let several Coyote processes query one duckdb RAG at once
- **rag**: support string and UUID Qdrant point IDs
- **rag**: create the API key secret inline in the attach wizard
- let workflow rag nodes select a RAG driver
- improved wording and heuristic detection for sisyphus suite of agents
- upgraded to sbx kit v2 spec for improved integration
- **rag**: add attach-only Qdrant provider, attach wizard and sandbox wiring
- **rag**: add DuckDB provider behind the RAG driver abstraction
- **rag**: add driver/attached fields, validation floors and force-reingest
- **mcp**: send RFC 8707 resource indicator in OAuth flows
- Added loaded indicators to .list tools/mcp-servers/skills
- hide .recover from tab completions when no session is active
- add a new .recover command for sessions to recover from errors
- integrated the git-ssh-sign kit into the coyote sandbox kit
### Fix
- Harden install scripts: detect libssl3 without ldconfig on PATH, survive noexec tmp dirs, and guard against partial curl|bash execution
- ldconfig on Debian-based distros lives in /usr/sbin
- Prefer GNU linux builds in install scripts for duckdb support, and gate duckdb support on linux hosts that are MUSL until MUSL support is added
- **mcp**: omit declared-but-empty prompt/resource capabilities from .info mcp-server
- **mcp**: annotate declared-but-empty prompt/resource capabilities in .info mcp-server
- also support the .continue edge case for session crashing checkpointing
- checkpoint sessions that crash for easy resuming
- job__check ring buffer also collects file output for LLM_OUTPUT as well as stdout
- **tools**: interactive-shell semantics and stderr capture in execute_command
- **function**: gate test-only declaration appender behind cfg(test)
- **graph**: gate unix-only test imports behind cfg(unix)
- harden job runner lifecycle and whitelist conformance
- **function**: floor tool-output truncation cut to a UTF-8 char boundary
- **supervisor**: make agent__check a pure status probe that never consumes the handle
- **supervisor**: surface finished-but-uncollected tasks in turn-end guardrail
- fix grep "binary" errors when searching UTF-8 files with unicode characters like some of the Coyote source
- **bundles**: harden the install pipeline for cross-platform correctness
- **mcp**: harden the spill path for cross-platform correctness
- **repl**: offer prompts in .list tab completion and rename its listing helpers
- **mcp**: gate unix-only spill permission APIs for windows builds
- harden the bundle lifecycle per code review
- address code review findings on the bundle lifecycle
- reserve category names, confirm fork-name collisions, report secrets on uninstall
- make bundle provenance portable to Windows
- user__ask should have been renamed to user__select in graph agent user interaction invocations
- support tab completions for graph-based agents with variables as well as standard agents
- surface macro parse errors on top-level invocation
- render .list macros as a comfy-table instead of fixed-width columns
- drain crossterm characters in zellij in kitty contexts to prevent DA1 responses from entering prompt
- drain tty input when displaying inquire prompts to prevent unintentional escapes
- breaking iwe MCP server changes with newest version
- improved handling of non service-specific secrets using sbx custom-secrets
- include tool output to LLM_OUTPUT in errors as well as stderr
- prevent tmp-overwriting
- Corrected a rare edge case on how tool files are generated during parallel executions
- cosmetic fix after improved bash tool handling
- Improved subagent escalation handling
- latent parsing bugs in fs_patch and argc
- Prevent infinite hangs in coder agent and implement timeouts for LLM API calls and interactive tools
- allow nested italics inside bold spans in markdown renderer
- properly handle OAuth refreshes
- correct newline removal from fs_write and fs_patch
- detect duplicate tool call IDs client-side before sending to Claude
- **rag**: warn when a duckdb store is empty but files are indexed
- **rag**: keep a local Qdrant off an ambient proxy
- keep loopback and LAN traffic off an ambient proxy
- **rag**: stop routing Qdrant requests through an ambient proxy
- **rag**: treat a zero min_score as no floor on Qdrant searches
- **rag**: address Copilot review findings on the driver abstraction
- **rag**: delete the DuckDB write-ahead log alongside the store
- **sandbox**: discover agent-scoped RAG mixin sidecars
- **rag**: stop the attach wizard from silently accepting an empty collection
- **rag**: fail loudly when a RAG's vault secret is missing
- **rag**: serialize DuckDB extension installs to stop a Windows race
- **rag**: emit an sbx kit v2 mixin and declare RAG credentials to the proxy
- **rag**: install DuckDB vss and fts extensions when they are missing
- don't output thinking blocks for claude-based models
- additional edge case fix for duplicate tool call IDs in anthropic API calls
- removed temperature modifier in librarian agent to mitigate invisible errors
- strip reasoning blocks for structured LLM output in graph agents
- prevent rare duplicate tool call IDs in long running claude prompts
- agents inherit global reasoning effort if unset
### Refactor
- **function**: finish supervisor-to-agent vocabulary migration
- **function**: rename agent-tool symbols out of supervisor vocabulary
- Modified the naming of several generalized supervisor values
- pulled out some imports to clean up the MCP render module a bit
- **mcp**: centralize meta-function prefix predicates and fix list_tools pagination
- Refactored some bundle const locations
- drop the .install remote migration hint
- drop the --install-from tombstone entirely
- render .list agents and .list skills as comfy-tables
- **rag**: discover driver_config secrets by grammar, not field name
- **rag**: drop the hardcoded embedding model hint from attach
- **rag**: interpolate every driver_config value, not just api_key
- **rag**: extract RagProvider trait and add YamlProvider
## v0.8.3 (2026-08-03) ## v0.8.3 (2026-08-03)
### Fix ### Fix
Generated
+173 -162
View File
@@ -213,7 +213,7 @@ dependencies = [
"convert_case 0.11.0", "convert_case 0.11.0",
"dirs", "dirs",
"either", "either",
"indexmap 2.14.0", "indexmap 2.14.1",
"natord", "natord",
"nom 8.0.0", "nom 8.0.0",
"num_cpus", "num_cpus",
@@ -451,7 +451,7 @@ checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -755,12 +755,12 @@ dependencies = [
"aws-smithy-runtime-api", "aws-smithy-runtime-api",
"aws-smithy-types", "aws-smithy-types",
"h2 0.3.27", "h2 0.3.27",
"h2 0.4.15", "h2 0.4.19",
"http 0.2.12", "http 0.2.12",
"http 1.5.0", "http 1.5.0",
"http-body 0.4.6", "http-body 0.4.6",
"hyper 0.14.32", "hyper 0.14.32",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-rustls 0.24.2", "hyper-rustls 0.24.2",
"hyper-rustls 0.27.9", "hyper-rustls 0.27.9",
"hyper-util", "hyper-util",
@@ -1040,7 +1040,7 @@ dependencies = [
"addr2line", "addr2line",
"cfg-if", "cfg-if",
"libc", "libc",
"miniz_oxide", "miniz_oxide 0.8.9",
"object", "object",
"rustc-demangle", "rustc-demangle",
"windows-link", "windows-link",
@@ -1284,9 +1284,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]] [[package]]
name = "cc" name = "cc"
version = "1.4.2" version = "1.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e" checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273"
dependencies = [ dependencies = [
"find-msvc-tools", "find-msvc-tools",
"jobserver", "jobserver",
@@ -1328,12 +1328,12 @@ dependencies = [
[[package]] [[package]]
name = "chacha20" name = "chacha20"
version = "0.10.1" version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" checksum = "65c35e4b699c7e15ccbe7ee35c005e4fc0a278d22238a2857e6ce2dadeda1b06"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.3.0", "cpufeatures 0.3.1",
"rand_core 0.10.1", "rand_core 0.10.1",
] ]
@@ -1440,7 +1440,7 @@ dependencies = [
"heck", "heck",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -1490,9 +1490,9 @@ dependencies = [
[[package]] [[package]]
name = "combine" name = "combine"
version = "4.6.7" version = "4.6.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e"
dependencies = [ dependencies = [
"bytes", "bytes",
"memchr", "memchr",
@@ -1636,7 +1636,7 @@ dependencies = [
"cookie", "cookie",
"document-features", "document-features",
"idna", "idna",
"indexmap 2.14.0", "indexmap 2.14.1",
"log", "log",
"serde", "serde",
"serde_derive", "serde_derive",
@@ -1663,7 +1663,7 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
[[package]] [[package]]
name = "coyote-ai" name = "coyote-ai"
version = "0.8.3" version = "0.10.1"
dependencies = [ dependencies = [
"ansi_colours", "ansi_colours",
"anyhow", "anyhow",
@@ -1699,7 +1699,7 @@ dependencies = [
"hnsw_rs", "hnsw_rs",
"html_to_markdown", "html_to_markdown",
"http 1.5.0", "http 1.5.0",
"indexmap 2.14.0", "indexmap 2.14.1",
"indoc", "indoc",
"inquire", "inquire",
"is-terminal", "is-terminal",
@@ -1767,9 +1767,9 @@ dependencies = [
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.3.0" version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" checksum = "5ca28b0ae3115b884660db4118d803791fd6756b6e88f39c0f3f7859060d7566"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -1785,9 +1785,9 @@ dependencies = [
[[package]] [[package]]
name = "crc32fast" name = "crc32fast"
version = "1.5.0" version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
] ]
@@ -1976,12 +1976,12 @@ dependencies = [
[[package]] [[package]]
name = "darling" name = "darling"
version = "0.24.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88490bf1b990d87eaaa7ac8aa887f629a08e7359765b4911faf63c3763347d23" checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
dependencies = [ dependencies = [
"darling_core 0.24.0", "darling_core 0.24.1",
"darling_macro 0.24.0", "darling_macro 0.24.1",
] ]
[[package]] [[package]]
@@ -2013,15 +2013,15 @@ dependencies = [
[[package]] [[package]]
name = "darling_core" name = "darling_core"
version = "0.24.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "084e274f91c482280130e1e34e0b8d6e66776a060d7b6de7b84289ca778868c4" checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [ dependencies = [
"ident_case", "ident_case",
"proc-macro2", "proc-macro2",
"quote", "quote",
"strsim", "strsim",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -2048,13 +2048,13 @@ dependencies = [
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.24.0" version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68f5792fa0d41cd2325ce0ffa64f0a340eaebd4971a3a0c5e1ffd2cc488a355e" checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [ dependencies = [
"darling_core 0.24.0", "darling_core 0.24.1",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -2245,7 +2245,7 @@ checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -2352,9 +2352,9 @@ checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8"
[[package]] [[package]]
name = "either" name = "either"
version = "1.17.0" version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34"
[[package]] [[package]]
name = "encode_unicode" name = "encode_unicode"
@@ -2424,9 +2424,9 @@ dependencies = [
[[package]] [[package]]
name = "error-code" name = "error-code"
version = "3.3.2" version = "3.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7"
[[package]] [[package]]
name = "etcetera" name = "etcetera"
@@ -2539,9 +2539,9 @@ dependencies = [
[[package]] [[package]]
name = "find-msvc-tools" name = "find-msvc-tools"
version = "0.1.10" version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de" checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890"
[[package]] [[package]]
name = "fixedbitset" name = "fixedbitset"
@@ -2551,12 +2551,12 @@ checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]] [[package]]
name = "flate2" name = "flate2"
version = "1.1.9" version = "1.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" checksum = "6e634e2e0ebac1ee034020da1ca582e17ffe4e0f5e985823721e168928136dcb"
dependencies = [ dependencies = [
"crc32fast", "crc32fast",
"miniz_oxide", "miniz_oxide 0.9.1",
"zlib-rs", "zlib-rs",
] ]
@@ -2668,7 +2668,7 @@ checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -2728,7 +2728,7 @@ dependencies = [
"bytes", "bytes",
"chrono", "chrono",
"futures", "futures",
"hyper 1.11.0", "hyper 1.11.1",
"jsonwebtoken", "jsonwebtoken",
"once_cell", "once_cell",
"prost", "prost",
@@ -2878,7 +2878,7 @@ dependencies = [
"futures-sink", "futures-sink",
"futures-util", "futures-util",
"http 0.2.12", "http 0.2.12",
"indexmap 2.14.0", "indexmap 2.14.1",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -2887,9 +2887,9 @@ dependencies = [
[[package]] [[package]]
name = "h2" name = "h2"
version = "0.4.15" version = "0.4.19"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
@@ -2897,7 +2897,7 @@ dependencies = [
"futures-core", "futures-core",
"futures-sink", "futures-sink",
"http 1.5.0", "http 1.5.0",
"indexmap 2.14.0", "indexmap 2.14.1",
"slab", "slab",
"tokio", "tokio",
"tokio-util", "tokio-util",
@@ -2956,9 +2956,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
[[package]] [[package]]
name = "hermit-abi" name = "hermit-abi"
version = "0.5.2" version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" checksum = "e17592d60ebacc7d5e169f4663c5f84f9161cc90328abcfe8456f41e4dfcb284"
[[package]] [[package]]
name = "hex" name = "hex"
@@ -2997,7 +2997,7 @@ dependencies = [
"cpu-time", "cpu-time",
"env_logger", "env_logger",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.14.0", "indexmap 2.14.1",
"lazy_static", "lazy_static",
"log", "log",
"mmap-rs", "mmap-rs",
@@ -3100,9 +3100,9 @@ dependencies = [
[[package]] [[package]]
name = "http-body-util" name = "http-body-util"
version = "0.1.4" version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c"
dependencies = [ dependencies = [
"bytes", "bytes",
"futures-core", "futures-core",
@@ -3180,15 +3180,15 @@ dependencies = [
[[package]] [[package]]
name = "hyper" name = "hyper"
version = "1.11.0" version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" checksum = "27b501faa50e7a26c3d3560ca625132f4078a17771f4810baf70475ae48cbe43"
dependencies = [ dependencies = [
"atomic-waker", "atomic-waker",
"bytes", "bytes",
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"h2 0.4.15", "h2 0.4.19",
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"httparse", "httparse",
@@ -3222,7 +3222,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f"
dependencies = [ dependencies = [
"http 1.5.0", "http 1.5.0",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-util", "hyper-util",
"rustls 0.23.43", "rustls 0.23.43",
"rustls-native-certs", "rustls-native-certs",
@@ -3237,7 +3237,7 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0"
dependencies = [ dependencies = [
"hyper 1.11.0", "hyper 1.11.1",
"hyper-util", "hyper-util",
"pin-project-lite", "pin-project-lite",
"tokio", "tokio",
@@ -3252,7 +3252,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
dependencies = [ dependencies = [
"bytes", "bytes",
"http-body-util", "http-body-util",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-util", "hyper-util",
"native-tls", "native-tls",
"tokio", "tokio",
@@ -3272,7 +3272,7 @@ dependencies = [
"futures-util", "futures-util",
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"hyper 1.11.0", "hyper 1.11.1",
"ipnet", "ipnet",
"libc", "libc",
"percent-encoding", "percent-encoding",
@@ -3309,9 +3309,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_collections" name = "icu_collections"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"potential_utf", "potential_utf",
@@ -3323,9 +3323,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_locale_core" name = "icu_locale_core"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"litemap", "litemap",
@@ -3336,9 +3336,9 @@ dependencies = [
[[package]] [[package]]
name = "icu_normalizer" name = "icu_normalizer"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f"
dependencies = [ dependencies = [
"icu_collections", "icu_collections",
"icu_normalizer_data", "icu_normalizer_data",
@@ -3350,16 +3350,17 @@ dependencies = [
[[package]] [[package]]
name = "icu_normalizer_data" name = "icu_normalizer_data"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0"
[[package]] [[package]]
name = "icu_properties" name = "icu_properties"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148"
dependencies = [ dependencies = [
"displaydoc",
"icu_collections", "icu_collections",
"icu_locale_core", "icu_locale_core",
"icu_properties_data", "icu_properties_data",
@@ -3370,15 +3371,15 @@ dependencies = [
[[package]] [[package]]
name = "icu_properties_data" name = "icu_properties_data"
version = "2.2.0" version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa"
[[package]] [[package]]
name = "icu_provider" name = "icu_provider"
version = "2.2.0" version = "2.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"icu_locale_core", "icu_locale_core",
@@ -3441,9 +3442,9 @@ dependencies = [
[[package]] [[package]]
name = "indexmap" name = "indexmap"
version = "2.14.0" version = "2.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" checksum = "07aa2048142242915a31d35844fb311e0e53fcca590c3a0a40dcf1b841fa09eb"
dependencies = [ dependencies = [
"equivalent", "equivalent",
"hashbrown 0.17.1", "hashbrown 0.17.1",
@@ -3837,9 +3838,9 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981"
[[package]] [[package]]
name = "libredox" name = "libredox"
version = "0.1.19" version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2026a5056764a10b2bf5d56488cba40da507f5493a6a429340e2004d9ed085fa" checksum = "d7955dfc218a8afb29dfeffd540e3a6e96baeb94fe7138228dd7cc6937fbbf96"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -3870,9 +3871,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
[[package]] [[package]]
name = "litemap" name = "litemap"
version = "0.8.2" version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae"
[[package]] [[package]]
name = "litrs" name = "litrs"
@@ -3891,9 +3892,9 @@ dependencies = [
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.33" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
dependencies = [ dependencies = [
"serde_core", "serde_core",
] ]
@@ -4044,6 +4045,15 @@ name = "miniz_oxide"
version = "0.8.9" version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "miniz_oxide"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b63fbc4a50860e98e7b2aa7804ded1db5cbc3aff9193adaff57a6931bf7c4b4c"
dependencies = [ dependencies = [
"adler2", "adler2",
"simd-adler32", "simd-adler32",
@@ -4211,9 +4221,9 @@ checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441"
[[package]] [[package]]
name = "num-integer" name = "num-integer"
version = "0.1.46" version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
dependencies = [ dependencies = [
"num-traits", "num-traits",
] ]
@@ -4470,9 +4480,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]] [[package]]
name = "open" name = "open"
version = "5.4.1" version = "5.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9cfef937e9c486488c7e3d949ae31c0f1d06bdacd75b99c086cb35356e30408" checksum = "ade3be4664bc1ef537ce133015f04c176b737815c2ba9fd60edf212d6e90dd55"
dependencies = [ dependencies = [
"is-wsl", "is-wsl",
"libc", "libc",
@@ -4664,7 +4674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772" checksum = "3672b37090dbd86368a4145bc067582552b29c27377cad4e0a306c97f9bd7772"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"indexmap 2.14.0", "indexmap 2.14.1",
"serde", "serde",
"serde_derive", "serde_derive",
] ]
@@ -4677,7 +4687,7 @@ checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455"
dependencies = [ dependencies = [
"fixedbitset", "fixedbitset",
"hashbrown 0.15.5", "hashbrown 0.15.5",
"indexmap 2.14.0", "indexmap 2.14.1",
] ]
[[package]] [[package]]
@@ -4707,7 +4717,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d"
dependencies = [ dependencies = [
"phf_shared", "phf_shared",
"rand 0.8.7", "rand 0.8.8",
] ]
[[package]] [[package]]
@@ -4776,9 +4786,9 @@ dependencies = [
[[package]] [[package]]
name = "pkg-config" name = "pkg-config"
version = "0.3.33" version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548"
[[package]] [[package]]
name = "plist" name = "plist"
@@ -4787,7 +4797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"indexmap 2.14.0", "indexmap 2.14.1",
"quick-xml 0.41.0", "quick-xml 0.41.0",
"serde", "serde",
"time", "time",
@@ -4821,9 +4831,9 @@ dependencies = [
[[package]] [[package]]
name = "potential_utf" name = "potential_utf"
version = "0.1.5" version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661"
dependencies = [ dependencies = [
"zerovec", "zerovec",
] ]
@@ -4871,9 +4881,9 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-error-attr3" name = "proc-macro-error-attr3"
version = "3.1.0" version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b0084e6206a967a2dad822180626b2f6b07a3b379325e8f1ec0438e33a469ba7" checksum = "9e564d14133360e1ae169ffde5da25881b5fa47261665b8e5713c212c27799da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -4881,14 +4891,14 @@ dependencies = [
[[package]] [[package]]
name = "proc-macro-error3" name = "proc-macro-error3"
version = "3.1.0" version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cf066225f2373bc711684792b69bdeac0356019b007e721090c24d92d5d5a50" checksum = "8f0d4471b3436c22106b21913b1dda531558918ae9b7ec55d58aa84b43552233"
dependencies = [ dependencies = [
"proc-macro-error-attr3", "proc-macro-error-attr3",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -4907,7 +4917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55"
dependencies = [ dependencies = [
"futures", "futures",
"indexmap 2.14.0", "indexmap 2.14.1",
"nix 0.31.3", "nix 0.31.3",
"tokio", "tokio",
"tracing", "tracing",
@@ -5001,9 +5011,9 @@ dependencies = [
[[package]] [[package]]
name = "quinn-proto" name = "quinn-proto"
version = "0.11.16" version = "0.11.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" checksum = "04759210543be93709136e28212294a659ef5001836ff4eab4d663e4529bba83"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"bytes", "bytes",
@@ -5059,9 +5069,9 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]] [[package]]
name = "rand" name = "rand"
version = "0.8.7" version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" checksum = "e058c7de0b26af77780c769414d6257830bb240f3c38477dbc2c16e5f54d6d4c"
dependencies = [ dependencies = [
"rand_core 0.6.4", "rand_core 0.6.4",
] ]
@@ -5082,7 +5092,7 @@ version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [ dependencies = [
"chacha20 0.10.1", "chacha20 0.10.2",
"getrandom 0.4.3", "getrandom 0.4.3",
"rand_core 0.10.1", "rand_core 0.10.1",
] ]
@@ -5192,22 +5202,22 @@ dependencies = [
[[package]] [[package]]
name = "ref-cast" name = "ref-cast"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3"
dependencies = [ dependencies = [
"ref-cast-impl", "ref-cast-impl",
] ]
[[package]] [[package]]
name = "ref-cast-impl" name = "ref-cast-impl"
version = "1.0.26" version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -5258,7 +5268,7 @@ dependencies = [
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-rustls 0.27.9", "hyper-rustls 0.27.9",
"hyper-tls", "hyper-tls",
"hyper-util", "hyper-util",
@@ -5300,11 +5310,11 @@ dependencies = [
"futures-channel", "futures-channel",
"futures-core", "futures-core",
"futures-util", "futures-util",
"h2 0.4.15", "h2 0.4.19",
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-rustls 0.27.9", "hyper-rustls 0.27.9",
"hyper-tls", "hyper-tls",
"hyper-util", "hyper-util",
@@ -5361,15 +5371,16 @@ dependencies = [
[[package]] [[package]]
name = "rmcp" name = "rmcp"
version = "3.1.2" version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8dddc5b1924b9a59fba420166160ca2c4663a4e01803e52eda33070f56d63c8" checksum = "1a15bc53261a9dc37e105df006e4656c598379a8f9581f8950debb130f27a7cf"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"bytes", "bytes",
"chrono", "chrono",
"futures", "futures",
"http 1.5.0", "http 1.5.0",
"indexmap 2.14.1",
"pastey", "pastey",
"pin-project-lite", "pin-project-lite",
"process-wrap", "process-wrap",
@@ -5389,15 +5400,15 @@ dependencies = [
[[package]] [[package]]
name = "rmcp-macros" name = "rmcp-macros"
version = "3.1.2" version = "3.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6898e24cd16342b59bfa8a53c2c04b9cf62fc8a2cfea57b9c038b09984bfc521" checksum = "a85d45508e9b4ba024fe996c2638799635d75b6dd0ba8f32ccf08f8026f0c780"
dependencies = [ dependencies = [
"darling 0.24.0", "darling 0.24.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_json", "serde_json",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -5419,12 +5430,12 @@ dependencies = [
[[package]] [[package]]
name = "rtoolbox" name = "rtoolbox"
version = "0.0.5" version = "0.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "50a0e551c1e27e1731aba276dbeaeac73f53c7cd34d1bda485d02bd1e0f36844" checksum = "9a1efe12a1469752d0e6ff5ebec0b6ef4924cc5c4c71046b0ec730040535819d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.59.0", "windows-sys 0.61.2",
] ]
[[package]] [[package]]
@@ -5542,7 +5553,7 @@ dependencies = [
"once_cell", "once_cell",
"ring", "ring",
"rustls-pki-types", "rustls-pki-types",
"rustls-webpki 0.103.14", "rustls-webpki 0.103.15",
"subtle", "subtle",
"zeroize", "zeroize",
] ]
@@ -5583,7 +5594,7 @@ dependencies = [
"rustls 0.23.43", "rustls 0.23.43",
"rustls-native-certs", "rustls-native-certs",
"rustls-platform-verifier-android", "rustls-platform-verifier-android",
"rustls-webpki 0.103.14", "rustls-webpki 0.103.15",
"security-framework", "security-framework",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
@@ -5608,9 +5619,9 @@ dependencies = [
[[package]] [[package]]
name = "rustls-webpki" name = "rustls-webpki"
version = "0.103.14" version = "0.103.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0527518605e68109d875e248ea259b6758801cf165e4b2c2733ae3b51f12535a" checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2"
dependencies = [ dependencies = [
"aws-lc-rs", "aws-lc-rs",
"ring", "ring",
@@ -5683,7 +5694,7 @@ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"serde_derive_internals", "serde_derive_internals",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -5701,7 +5712,7 @@ dependencies = [
"cssparser", "cssparser",
"ego-tree", "ego-tree",
"html5ever 0.29.1", "html5ever 0.29.1",
"indexmap 2.14.0", "indexmap 2.14.1",
"precomputed-hash", "precomputed-hash",
"selectors", "selectors",
"tendril", "tendril",
@@ -5859,7 +5870,7 @@ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -5870,7 +5881,7 @@ checksum = "f852137cce035d6a4df67ccce505ff6b3e9fd3a10e3e52b24dc71e650bb1a9bd"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -5879,7 +5890,7 @@ version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [ dependencies = [
"indexmap 2.14.0", "indexmap 2.14.1",
"itoa", "itoa",
"memchr", "memchr",
"serde", "serde",
@@ -5919,7 +5930,7 @@ dependencies = [
"chrono", "chrono",
"hex", "hex",
"indexmap 1.9.3", "indexmap 1.9.3",
"indexmap 2.14.0", "indexmap 2.14.1",
"jiff", "jiff",
"schemars 0.9.0", "schemars 0.9.0",
"schemars 1.2.2", "schemars 1.2.2",
@@ -5947,7 +5958,7 @@ version = "0.9.34+deprecated"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
dependencies = [ dependencies = [
"indexmap 2.14.0", "indexmap 2.14.1",
"itoa", "itoa",
"ryu", "ryu",
"serde", "serde",
@@ -6017,7 +6028,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"cpufeatures 0.3.0", "cpufeatures 0.3.1",
"digest 0.11.3", "digest 0.11.3",
] ]
@@ -6321,9 +6332,9 @@ dependencies = [
[[package]] [[package]]
name = "syn" name = "syn"
version = "3.0.3" version = "3.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
@@ -6526,7 +6537,7 @@ checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -6600,9 +6611,9 @@ dependencies = [
[[package]] [[package]]
name = "tinystr" name = "tinystr"
version = "0.8.3" version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"zerovec", "zerovec",
@@ -6648,7 +6659,7 @@ checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.3", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -6743,11 +6754,11 @@ dependencies = [
"axum", "axum",
"base64 0.22.1", "base64 0.22.1",
"bytes", "bytes",
"h2 0.4.15", "h2 0.4.19",
"http 1.5.0", "http 1.5.0",
"http-body 1.1.0", "http-body 1.1.0",
"http-body-util", "http-body-util",
"hyper 1.11.0", "hyper 1.11.1",
"hyper-timeout", "hyper-timeout",
"hyper-util", "hyper-util",
"percent-encoding", "percent-encoding",
@@ -6783,7 +6794,7 @@ checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4"
dependencies = [ dependencies = [
"futures-core", "futures-core",
"futures-util", "futures-util",
"indexmap 2.14.0", "indexmap 2.14.1",
"pin-project-lite", "pin-project-lite",
"slab", "slab",
"sync_wrapper", "sync_wrapper",
@@ -6862,9 +6873,9 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter" name = "tree-sitter"
version = "0.26.12" version = "0.26.13"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83c567a8e18ae93f20982c90370b16fd24023aeaf52f6052b96957ab253a0fec" checksum = "17ebdd3a5a7e28a1890b876fdbd0c3c0fe0a6336cffaa104f11b9f720c9daa29"
dependencies = [ dependencies = [
"cc", "cc",
"regex", "regex",
@@ -6876,9 +6887,9 @@ dependencies = [
[[package]] [[package]]
name = "tree-sitter-language" name = "tree-sitter-language"
version = "0.1.7" version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
[[package]] [[package]]
name = "tree-sitter-python" name = "tree-sitter-python"
@@ -7140,9 +7151,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
[[package]] [[package]]
name = "uuid" name = "uuid"
version = "1.24.0" version = "1.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" checksum = "b5772d71c9be8a8a6ac2117d949c5b224c1b72241bb611d9a3012edcf8af7812"
dependencies = [ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"js-sys", "js-sys",
@@ -7323,9 +7334,9 @@ dependencies = [
[[package]] [[package]]
name = "wayland-backend" name = "wayland-backend"
version = "0.3.16" version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "016ccf01d1c58b6f8999612813e17c9b2390f7d70671428869913310f83f54b8" checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078"
dependencies = [ dependencies = [
"cc", "cc",
"downcast-rs", "downcast-rs",
@@ -7431,9 +7442,9 @@ dependencies = [
[[package]] [[package]]
name = "which" name = "which"
version = "8.0.5" version = "8.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f3ef584124b911bcc3875c2f1472e80f24361ceb789bd1c62b3e9a3df9ff43c" checksum = "bae2f2b2b816647a1cab1acc91f5bd20812d53cb344382635ec2181940c8034f"
dependencies = [ dependencies = [
"libc", "libc",
] ]
@@ -7842,9 +7853,9 @@ dependencies = [
[[package]] [[package]]
name = "writeable" name = "writeable"
version = "0.6.3" version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc"
[[package]] [[package]]
name = "x11rb" name = "x11rb"
@@ -7988,9 +7999,9 @@ dependencies = [
[[package]] [[package]]
name = "zerotrie" name = "zerotrie"
version = "0.2.4" version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f"
dependencies = [ dependencies = [
"displaydoc", "displaydoc",
"yoke", "yoke",
@@ -7999,9 +8010,9 @@ dependencies = [
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.11.6" version = "0.11.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8"
dependencies = [ dependencies = [
"yoke", "yoke",
"zerofrom", "zerofrom",
@@ -8010,13 +8021,13 @@ dependencies = [
[[package]] [[package]]
name = "zerovec-derive" name = "zerovec-derive"
version = "0.11.3" version = "0.11.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da"
dependencies = [ dependencies = [
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 3.0.4",
] ]
[[package]] [[package]]
@@ -8028,7 +8039,7 @@ dependencies = [
"arbitrary", "arbitrary",
"crc32fast", "crc32fast",
"flate2", "flate2",
"indexmap 2.14.0", "indexmap 2.14.1",
"memchr", "memchr",
"time", "time",
"zopfli", "zopfli",
+1 -1
View File
@@ -1,6 +1,6 @@
[package] [package]
name = "coyote-ai" name = "coyote-ai"
version = "0.8.3" version = "0.10.1"
edition = "2024" edition = "2024"
authors = ["Alex Clarke <alex.j.tusa@gmail.com>"] authors = ["Alex Clarke <alex.j.tusa@gmail.com>"]
description = "The batteries-included runtime for LLMs" description = "The batteries-included runtime for LLMs"
+4 -4
View File
@@ -27,7 +27,7 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g
* [AIChat Migration Guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration): Coming from AIChat? Follow the migration guide to get started. * [AIChat Migration Guide](https://github.com/Dark-Alex-17/coyote/wiki/AIChat-Migration): Coming from AIChat? Follow the migration guide to get started.
* [Installation](#install): Install Coyote * [Installation](#install): Install Coyote
* [Getting Started](#getting-started): Get started with Coyote by doing first-run setup steps. * [Getting Started](#getting-started): Get started with Coyote by doing first-run setup steps.
* [Sharing Configurations](https://github.com/Dark-Alex-17/coyote/wiki/Sharing-Configurations): Install bundles of agents, roles, skills, macros, tools, and MCP servers from any git repo, and share your own. Bundles are Coyote's equivalent of plugins in other CLI agents. * [Bundles](https://github.com/Dark-Alex-17/coyote/wiki/Bundles): Install bundles of agents, roles, skills, macros, tools, and MCP servers from any git repo, and share your own. Bundles are Coyote's equivalent of plugins in other CLI agents.
* [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote. * [REPL](https://github.com/Dark-Alex-17/coyote/wiki/REPL): Interactive Read-Eval-Print Loop for conversational interactions with LLMs and Coyote.
* [Custom REPL Prompt](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt): Customize the REPL prompt to provide useful contextual information. * [Custom REPL Prompt](https://github.com/Dark-Alex-17/coyote/wiki/REPL-Prompt): Customize the REPL prompt to provide useful contextual information.
* [Vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault): Securely store and manage sensitive information such as API keys and credentials. * [Vault](https://github.com/Dark-Alex-17/coyote/wiki/Vault): Securely store and manage sensitive information such as API keys and credentials.
@@ -43,8 +43,8 @@ Coming from [AIChat](https://github.com/sigoden/aichat)? Follow the [migration g
* Models interact with each server through a compact set of capability-gated meta-tools: `mcp_search`/`mcp_describe` for discovery across tools, resources, and prompts, `mcp_invoke` for tool calls, `mcp_read` for paged and regex-filterable resource reads, and `mcp_prompt` for server-defined prompts. Binary content is spilled to disk instead of inlined, and oversized tool results are bounded before they reach the model. * Models interact with each server through a compact set of capability-gated meta-tools: `mcp_search`/`mcp_describe` for discovery across tools, resources, and prompts, `mcp_invoke` for tool calls, `mcp_read` for paged and regex-filterable resource reads, and `mcp_prompt` for server-defined prompts. Binary content is spilled to disk instead of inlined, and oversized tool results are bounded before they reach the model.
* Invoke server prompts yourself with `.prompt <server> <name> [key=value ...]` in the REPL, with live staged tab-completion (servers, then prompt names, then `key=` arguments), and discover them with `.list prompts`. * Invoke server prompts yourself with `.prompt <server> <name> [key=value ...]` in the REPL, with live staged tab-completion (servers, then prompt names, then `key=` arguments), and discover them with `.list prompts`.
* [Macros](https://github.com/Dark-Alex-17/coyote/wiki/Macros): Automate repetitive tasks and workflows with Coyote "scripts" (macros). Macros are Coyote's custom commands: invoke any macro directly by name (e.g. `.review main`), with tab-completion, right alongside the built-in REPL commands. * [Macros](https://github.com/Dark-Alex-17/coyote/wiki/Macros): Automate repetitive tasks and workflows with Coyote "scripts" (macros). Macros are Coyote's custom commands: invoke any macro directly by name (e.g. `.review main`), with tab-completion, right alongside the built-in REPL commands.
* Give a macro a `description` (shown in `.list macros` and completions) and set `isolated: false` to run its steps on the live session, exactly as if you typed them. Note that non-isolated steps are recorded in the session, and mutating steps (`.role`, `.model`, ...) persist after the macro ends by design. Steps are fail-fast: an error aborts the remaining steps, but completed steps' effects remain. A non-isolated macro step cannot invoke another macro, and a `.exit` step never exits the REPL. * Give a macro a `description` (shown in `.list macros` and completions) and set `isolated: false` to run its steps on the live session, exactly as if you typed them. Note that non-isolated steps are recorded in the session, and mutating steps (`.role`, `.model`, ...) persist after the macro ends, by design. Steps are fail-fast: an error aborts the remaining steps, but completed steps' effects remain. A non-isolated macro step cannot invoke another macro, and a `.exit` step never exits the REPL.
* Commit project-specific macros to `.coyote/macros/` in your repo — they shadow same-named global macros (opt out with `--no-workspace-macros`). * Commit project-specific macros to `.coyote/macros/` in your repo. They shadow same-named global macros (opt out with `--no-workspace-macros`).
* Pass variables positionally or by name: leading `name=value` args set declared variables directly (letting earlier variables keep their defaults), and remaining args fill the rest in order. Tab completion after a macro name lists each variable with its description and default. * Pass variables positionally or by name: leading `name=value` args set declared variables directly (letting earlier variables keep their defaults), and remaining args fill the rest in order. Tab completion after a macro name lists each variable with its description and default.
* Scope which macros are invocable with `enabled_macros` in the global config, a role, an agent, or a session (most specific wins; an empty list disables all macros), and toggle at runtime with `.macro enable|disable <name>`. * Scope which macros are invocable with `enabled_macros` in the global config, a role, an agent, or a session (most specific wins; an empty list disables all macros), and toggle at runtime with `.macro enable|disable <name>`.
* [RAG](https://github.com/Dark-Alex-17/coyote/wiki/RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation. * [RAG](https://github.com/Dark-Alex-17/coyote/wiki/RAG): Retrieval-Augmented Generation for enhanced information retrieval and generation.
@@ -82,7 +82,7 @@ Coyote requires the following tools to be installed on your system:
* **npm:** `npm i -g @ast-grep/cli` * **npm:** `npm i -g @ast-grep/cli`
* Optional: if `ast-grep` is not installed, the `ast_grep` tool reports it and agents fall back to `fs_grep` * Optional: if `ast-grep` is not installed, the `ast_grep` tool reports it and agents fall back to `fs_grep`
* [duckdb](https://duckdb.org/) (for fast, local RAGs) * [duckdb](https://duckdb.org/) (for fast, local RAGs)
* `curl https://install.duckdb.org | sh` * `curl https://install.duckdb.org | bash`
These tools are used to provide various functionalities within Coyote, such as document processing, JSON manipulation, These tools are used to provide various functionalities within Coyote, such as document processing, JSON manipulation,
etc., and they are used within agents and tools. etc., and they are used within agents and tools.
+5 -4
View File
@@ -1,4 +1,4 @@
schemaVersion: '1' schemaVersion: '2'
kind: mixin kind: mixin
name: agent-probe name: agent-probe
description: > description: >
@@ -12,8 +12,9 @@ description: >
to libxml2.so.16). The services under probe run on localhost, which needs to libxml2.so.16). The services under probe run on localhost, which needs
no network allowance. POSIX-only: sbx runs these commands with /bin/sh (dash). no network allowance. POSIX-only: sbx runs these commands with /bin/sh (dash).
network: permissions:
allowedDomains: network:
allow:
# Latest-release lookup + tarball downloads (GitHub redirects release # Latest-release lookup + tarball downloads (GitHub redirects release
# assets to *.githubusercontent.com object hosts) # assets to *.githubusercontent.com object hosts)
- 'api.github.com:443' - 'api.github.com:443'
@@ -21,7 +22,7 @@ network:
- 'objects.githubusercontent.com:443' - 'objects.githubusercontent.com:443'
- 'release-assets.githubusercontent.com:443' - 'release-assets.githubusercontent.com:443'
commands: setup:
install: install:
- command: | - command: |
set -eu set -eu
+21 -5
View File
@@ -27,14 +27,30 @@ def _ensure_cwd_venv():
_ensure_cwd_venv() _ensure_cwd_venv()
def resolve_dir(env_name, default_path):
"""Resolve a directory at run time.
Prefer the override env var when set, otherwise fall back to the default
path derived from this script's own location, so the shim keeps working
when the config dir moves or is shared across environments with different
home directories.
"""
value = os.environ.get(env_name)
if value:
return value
return os.path.normpath(default_path)
def main(): def main():
(agent_func, raw_data) = parse_argv() (agent_func, raw_data) = parse_argv()
agent_data = parse_raw_data(raw_data) agent_data = parse_raw_data(raw_data)
root_dir = "{config_dir}" self_dir = os.path.dirname(os.path.abspath(__file__))
setup_env(root_dir, agent_func, raw_data) agent_dir = os.path.normpath(os.path.join(self_dir, ".."))
root_dir = resolve_dir("{root_dir_env}", os.path.join(self_dir, "{root_dir_rel}"))
setup_env(root_dir, agent_dir, agent_func, raw_data)
agent_tools_path = os.path.join(root_dir, "agents/{agent_name}/tools.py") agent_tools_path = os.path.join(agent_dir, "tools.py")
run(agent_tools_path, agent_func, agent_data) run(agent_tools_path, agent_func, agent_data)
@@ -65,12 +81,12 @@ def parse_argv():
return agent_func, agent_data return agent_func, agent_data
def setup_env(root_dir, agent_func, raw_data): def setup_env(root_dir, agent_dir, agent_func, raw_data):
load_env(os.path.join(root_dir, ".env")) load_env(os.path.join(root_dir, ".env"))
os.environ["LLM_ROOT_DIR"] = root_dir os.environ["LLM_ROOT_DIR"] = root_dir
os.environ["LLM_AGENT_NAME"] = "{agent_name}" os.environ["LLM_AGENT_NAME"] = "{agent_name}"
os.environ["LLM_AGENT_FUNC"] = agent_func os.environ["LLM_AGENT_FUNC"] = agent_func
os.environ["LLM_AGENT_ROOT_DIR"] = os.path.join(root_dir, "agents", "{agent_name}") os.environ["LLM_AGENT_ROOT_DIR"] = agent_dir
os.environ["LLM_AGENT_CACHE_DIR"] = os.path.join(root_dir, "cache", "{agent_name}") os.environ["LLM_AGENT_CACHE_DIR"] = os.path.join(root_dir, "cache", "{agent_name}")
os.environ["LLM_AGENT_RAW_JSON"] = raw_data os.environ["LLM_AGENT_RAW_JSON"] = raw_data
+25 -5
View File
@@ -5,13 +5,30 @@
set -e set -e
main() { main() {
root_dir="{config_dir}" self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
agent_dir="$(cd "$self_dir/.." && pwd)"
root_dir="$(resolve_dir "{root_dir_env}" "$self_dir/{root_dir_rel}")"
functions_dir="$(resolve_dir "{functions_dir_env}" "$self_dir/{functions_dir_rel}")"
parse_argv "$@" parse_argv "$@"
setup_env setup_env
tools_path="$root_dir/agents/{agent_name}/tools.sh" tools_path="$agent_dir/tools.sh"
run run
} }
# Resolve a directory at run time: prefer the override env var ($1) when set,
# otherwise fall back to the default path ($2) derived from this script's own
# location, so the shim keeps working when the config dir moves or is shared
# across environments with different home directories.
resolve_dir() {
local override
override="$(printenv "$1" 2>/dev/null || true)"
if [[ -n "$override" ]]; then
echo "$override"
else
(cd "$2" 2>/dev/null && pwd) || echo "$2"
fi
}
parse_argv() { parse_argv() {
agent_func="$1" agent_func="$1"
if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then
@@ -29,9 +46,9 @@ setup_env() {
export LLM_ROOT_DIR="$root_dir" export LLM_ROOT_DIR="$root_dir"
export LLM_AGENT_NAME="{agent_name}" export LLM_AGENT_NAME="{agent_name}"
export LLM_AGENT_FUNC="$agent_func" export LLM_AGENT_FUNC="$agent_func"
export LLM_AGENT_ROOT_DIR="$LLM_ROOT_DIR/agents/{agent_name}" export LLM_AGENT_ROOT_DIR="$agent_dir"
export LLM_AGENT_CACHE_DIR="$LLM_ROOT_DIR/cache/{agent_name}" export LLM_AGENT_CACHE_DIR="$LLM_ROOT_DIR/cache/{agent_name}"
export LLM_PROMPT_UTILS_FILE="{prompt_utils_file}" export LLM_PROMPT_UTILS_FILE="$functions_dir/utils/prompt-utils.sh"
export LLM_AGENT_RAW_JSON="$agent_data" export LLM_AGENT_RAW_JSON="$agent_data"
} }
@@ -59,6 +76,10 @@ run() {
die "error: no JSON data" die "error: no JSON data"
fi fi
if [[ ! -f "$tools_path" ]]; then
die "error: agent tools script not found: $tools_path"
fi
if [[ "$OS" == "Windows_NT" ]]; then if [[ "$OS" == "Windows_NT" ]]; then
set -o igncr set -o igncr
tools_path="$(cygpath -w "$tools_path")" tools_path="$(cygpath -w "$tools_path")"
@@ -122,4 +143,3 @@ die() {
} }
main "$@" main "$@"
+33 -7
View File
@@ -3,17 +3,38 @@
// Usage: ./{agent_name}.ts <agent-func> <agent-data> // Usage: ./{agent_name}.ts <agent-func> <agent-data>
import { readFileSync, writeFileSync, existsSync } from "fs"; import { readFileSync, writeFileSync, existsSync } from "fs";
import { join } from "path"; import { dirname, join, resolve } from "path";
import { pathToFileURL } from "url"; import { fileURLToPath, pathToFileURL } from "url";
function selfDir(): string {
if (typeof __dirname !== "undefined") {
return __dirname;
}
return dirname(fileURLToPath(import.meta.url));
}
// Resolve a directory at run time: prefer the override env var when set,
// otherwise fall back to the default path derived from this script's own
// location, so the shim keeps working when the config dir moves or is shared
// across environments with different home directories.
function resolveDir(envName: string, defaultPath: string): string {
const value = process.env[envName];
if (value) {
return value;
}
return resolve(defaultPath);
}
async function main(): Promise<void> { async function main(): Promise<void> {
const { agentFunc, rawData } = parseArgv(); const { agentFunc, rawData } = parseArgv();
const agentData = parseRawData(rawData); const agentData = parseRawData(rawData);
const configDir = "{config_dir}"; const binDir = selfDir();
setupEnv(configDir, agentFunc, rawData); const agentDir = resolve(binDir, "..");
const configDir = resolveDir("{root_dir_env}", join(binDir, "{root_dir_rel}"));
setupEnv(configDir, agentDir, agentFunc, rawData);
const agentToolsPath = join(configDir, "agents", "{agent_name}", "tools.ts"); const agentToolsPath = join(agentDir, "tools.ts");
await run(agentToolsPath, agentFunc, agentData); await run(agentToolsPath, agentFunc, agentData);
} }
@@ -48,12 +69,17 @@ function parseArgv(): { agentFunc: string; rawData: string } {
return { agentFunc, rawData: agentData }; return { agentFunc, rawData: agentData };
} }
function setupEnv(configDir: string, agentFunc: string, rawData: string): void { function setupEnv(
configDir: string,
agentDir: string,
agentFunc: string,
rawData: string,
): void {
loadEnv(join(configDir, ".env")); loadEnv(join(configDir, ".env"));
process.env["LLM_ROOT_DIR"] = configDir; process.env["LLM_ROOT_DIR"] = configDir;
process.env["LLM_AGENT_NAME"] = "{agent_name}"; process.env["LLM_AGENT_NAME"] = "{agent_name}";
process.env["LLM_AGENT_FUNC"] = agentFunc; process.env["LLM_AGENT_FUNC"] = agentFunc;
process.env["LLM_AGENT_ROOT_DIR"] = join(configDir, "agents", "{agent_name}"); process.env["LLM_AGENT_ROOT_DIR"] = agentDir;
process.env["LLM_AGENT_CACHE_DIR"] = join(configDir, "cache", "{agent_name}"); process.env["LLM_AGENT_CACHE_DIR"] = join(configDir, "cache", "{agent_name}");
process.env["LLM_AGENT_RAW_JSON"] = rawData; process.env["LLM_AGENT_RAW_JSON"] = rawData;
} }
+18 -2
View File
@@ -27,14 +27,30 @@ def _ensure_cwd_venv():
_ensure_cwd_venv() _ensure_cwd_venv()
def resolve_dir(env_name, default_path):
"""Resolve a directory at run time.
Prefer the override env var when set, otherwise fall back to the default
path derived from this script's own location, so the shim keeps working
when the config dir moves or is shared across environments with different
home directories.
"""
value = os.environ.get(env_name)
if value:
return value
return os.path.normpath(default_path)
def main(): def main():
raw_data = parse_argv() raw_data = parse_argv()
tool_data = parse_raw_data(raw_data) tool_data = parse_raw_data(raw_data)
root_dir = "{root_dir}" self_dir = os.path.dirname(os.path.abspath(__file__))
root_dir = resolve_dir("{root_dir_env}", os.path.join(self_dir, "{root_dir_rel}"))
functions_dir = resolve_dir("{functions_dir_env}", os.path.join(self_dir, "{functions_dir_rel}"))
setup_env(root_dir, raw_data) setup_env(root_dir, raw_data)
tool_path = "{tool_path}.py" tool_path = os.path.join(functions_dir, "tools", "{function_name}.py")
run(tool_path, "run", tool_data) run(tool_path, "run", tool_data)
+23 -3
View File
@@ -5,13 +5,29 @@
set -e set -e
main() { main() {
root_dir="{root_dir}" self_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
root_dir="$(resolve_dir "{root_dir_env}" "$self_dir/{root_dir_rel}")"
functions_dir="$(resolve_dir "{functions_dir_env}" "$self_dir/{functions_dir_rel}")"
parse_argv "$@" parse_argv "$@"
setup_env setup_env
tool_path="{tool_path}.sh" tool_path="$functions_dir/tools/{function_name}.sh"
run run
} }
# Resolve a directory at run time: prefer the override env var ($1) when set,
# otherwise fall back to the default path ($2) derived from this script's own
# location, so the shim keeps working when the config dir moves or is shared
# across environments with different home directories.
resolve_dir() {
local override
override="$(printenv "$1" 2>/dev/null || true)"
if [[ -n "$override" ]]; then
echo "$override"
else
(cd "$2" 2>/dev/null && pwd) || echo "$2"
fi
}
parse_argv() { parse_argv() {
if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then if [[ -n "$LLM_TOOL_DATA_FILE" ]] && [[ -f "$LLM_TOOL_DATA_FILE" ]]; then
tool_data="$(cat "$LLM_TOOL_DATA_FILE")" tool_data="$(cat "$LLM_TOOL_DATA_FILE")"
@@ -28,7 +44,7 @@ setup_env() {
export LLM_ROOT_DIR="$root_dir" export LLM_ROOT_DIR="$root_dir"
export LLM_TOOL_NAME="{function_name}" export LLM_TOOL_NAME="{function_name}"
export LLM_TOOL_CACHE_DIR="$LLM_ROOT_DIR/cache/{function_name}" export LLM_TOOL_CACHE_DIR="$LLM_ROOT_DIR/cache/{function_name}"
export LLM_PROMPT_UTILS_FILE="{prompt_utils_file}" export LLM_PROMPT_UTILS_FILE="$functions_dir/utils/prompt-utils.sh"
export LLM_TOOL_RAW_JSON="$tool_data" export LLM_TOOL_RAW_JSON="$tool_data"
} }
@@ -56,6 +72,10 @@ run() {
die "error: no JSON data" die "error: no JSON data"
fi fi
if [[ ! -f "$tool_path" ]]; then
die "error: tool script not found: $tool_path"
fi
if [[ "$OS" == "Windows_NT" ]]; then if [[ "$OS" == "Windows_NT" ]]; then
set -o igncr set -o igncr
tool_path="$(cygpath -w "$tool_path")" tool_path="$(cygpath -w "$tool_path")"
+28 -4
View File
@@ -3,17 +3,41 @@
// Usage: ./{function_name}.ts <tool-data> // Usage: ./{function_name}.ts <tool-data>
import { readFileSync, writeFileSync, existsSync } from "fs"; import { readFileSync, writeFileSync, existsSync } from "fs";
import { join } from "path"; import { dirname, join, resolve } from "path";
import { pathToFileURL } from "url"; import { fileURLToPath, pathToFileURL } from "url";
function selfDir(): string {
if (typeof __dirname !== "undefined") {
return __dirname;
}
return dirname(fileURLToPath(import.meta.url));
}
// Resolve a directory at run time: prefer the override env var when set,
// otherwise fall back to the default path derived from this script's own
// location, so the shim keeps working when the config dir moves or is shared
// across environments with different home directories.
function resolveDir(envName: string, defaultPath: string): string {
const value = process.env[envName];
if (value) {
return value;
}
return resolve(defaultPath);
}
async function main(): Promise<void> { async function main(): Promise<void> {
const rawData = parseArgv(); const rawData = parseArgv();
const toolData = parseRawData(rawData); const toolData = parseRawData(rawData);
const rootDir = "{root_dir}"; const binDir = selfDir();
const rootDir = resolveDir("{root_dir_env}", join(binDir, "{root_dir_rel}"));
const functionsDir = resolveDir(
"{functions_dir_env}",
join(binDir, "{functions_dir_rel}"),
);
setupEnv(rootDir, rawData); setupEnv(rootDir, rawData);
const toolPath = "{tool_path}.ts"; const toolPath = join(functionsDir, "tools", "{function_name}.ts");
await run(toolPath, "run", toolData); await run(toolPath, "run", toolData);
} }
+1 -1
View File
@@ -13,7 +13,7 @@ description: >
CLI & REPL mode, RAG, AI tools & agents, MCP servers, skills, and macros. CLI & REPL mode, RAG, AI tools & agents, MCP servers, skills, and macros.
sandbox: sandbox:
image: 'darkalex17/coyote:v0.8.3' image: 'darkalex17/coyote:v0.10.1'
entrypoint: ['bash', '-lc', 'exec /home/agent/.cargo/bin/coyote'] entrypoint: ['bash', '-lc', 'exec /home/agent/.cargo/bin/coyote']
permissions: permissions:
+122 -44
View File
@@ -16,6 +16,8 @@ param(
[string]$BinDir = $env:BIN_DIR [string]$BinDir = $env:BIN_DIR
) )
if ($Version -and $Version -match '^[0-9]') { $Version = "v$Version" }
$Repo = 'Dark-Alex-17/coyote' $Repo = 'Dark-Alex-17/coyote'
function Write-Info($msg) { Write-Host "[coyote-install] $msg" } function Write-Info($msg) { Write-Host "[coyote-install] $msg" }
@@ -61,79 +63,155 @@ if ($os -eq 'windows') {
if ($arch -eq 'x86_64') { $candidates += 'coyote-x86_64-apple-darwin.tar.gz' } if ($arch -eq 'x86_64') { $candidates += 'coyote-x86_64-apple-darwin.tar.gz' }
else { $candidates += 'coyote-aarch64-apple-darwin.tar.gz' } else { $candidates += 'coyote-aarch64-apple-darwin.tar.gz' }
} elseif ($os -eq 'linux') { } elseif ($os -eq 'linux') {
if ($arch -eq 'x86_64') { $libc = 'musl'
$candidates += 'coyote-x86_64-unknown-linux-gnu.tar.gz' try { getconf GNU_LIBC_VERSION *> $null; if ($LASTEXITCODE -eq 0) { $libc = 'gnu' } } catch { }
$candidates += 'coyote-x86_64-unknown-linux-musl.tar.gz' try { if ((ldd --version 2>&1 | Out-String) -imatch 'glibc') { $libc = 'gnu' } } catch { }
} else { if ($libc -eq 'gnu') {
$candidates += 'coyote-aarch64-unknown-linux-musl.tar.gz' # ldconfig lives in /usr/sbin on Debian/Ubuntu, often missing from non-root
# PATHs, so try its known locations and fall back to probing library dirs.
$libssl3 = $false
foreach ($ldc in @('ldconfig', '/sbin/ldconfig', '/usr/sbin/ldconfig')) {
try { if ((& $ldc -p 2>&1 | Out-String) -match 'libssl\.so\.3') { $libssl3 = $true; break } } catch { }
} }
if (-not $libssl3) {
foreach ($libDir in @('/usr/lib/*/libssl.so.3', '/lib/*/libssl.so.3', '/usr/lib64/libssl.so.3', '/usr/lib/libssl.so.3', '/usr/local/lib/libssl.so.3', '/usr/local/lib/*/libssl.so.3')) {
if (Get-Item -Path $libDir -ErrorAction SilentlyContinue) { $libssl3 = $true; break }
}
}
if ($libssl3) {
$candidates += "coyote-$arch-unknown-linux-gnu.tar.gz"
} else {
Write-Info "glibc detected but OpenSSL 3 (libssl.so.3) not found; using musl build"
}
}
$candidates += "coyote-$arch-unknown-linux-musl.tar.gz"
} else { } else {
Fail "Unsupported OS for this installer: $os" Fail "Unsupported OS for this installer: $os"
} }
$asset = $null
foreach ($c in $candidates) {
$asset = $release.assets | Where-Object { $_.name -eq $c } | Select-Object -First 1
if ($asset) { break }
}
if (-not $asset) {
Write-Error "No matching asset found for $os-$arch. Tried:"; $candidates | ForEach-Object { Write-Error " - $_" }
exit 1
}
Write-Info "Selected asset: $($asset.name)"
Write-Info "Download URL: $($asset.browser_download_url)"
$tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "coyote-$(Get-Random)")) $tmp = New-Item -ItemType Directory -Force -Path ([IO.Path]::Combine([IO.Path]::GetTempPath(), "coyote-$(Get-Random)"))
$archive = Join-Path $tmp.FullName 'asset'
try { Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $asset.browser_download_url -OutFile $archive } catch { Fail "Failed to download asset. $_" }
$extractDir = Join-Path $tmp.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null try {
$exec = if ($isWin) { 'coyote.exe' } else { 'coyote' }
$dest = Join-Path $BinDir $exec
if ($asset.name -match '\.zip$') { $installed = $false
$tried = @()
$attempt = 0
foreach ($c in $candidates) {
$asset = $release.assets | Where-Object { $_.name -eq $c } | Select-Object -First 1
if (-not $asset) {
$tried += "${c}: no matching release asset"
continue
}
$attempt++
$work = New-Item -ItemType Directory -Force -Path (Join-Path $tmp.FullName "attempt-$attempt")
Write-Info "Selected asset: $($asset.name)"
Write-Info "Download URL: $($asset.browser_download_url)"
$archive = Join-Path $work.FullName 'asset'
try {
Invoke-WebRequest -UseBasicParsing -Headers @{ 'User-Agent' = 'coyote-installer' } -Uri $asset.browser_download_url -OutFile $archive
} catch {
Write-Info "Failed to download ${c}; trying next candidate. $_"
$tried += "${c}: download failed"
continue
}
$extractDir = Join-Path $work.FullName 'extract'; New-Item -ItemType Directory -Force -Path $extractDir | Out-Null
try {
if ($asset.name -match '\.zip$') {
Add-Type -AssemblyName System.IO.Compression.FileSystem Add-Type -AssemblyName System.IO.Compression.FileSystem
[System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir)
} elseif ($asset.name -match '\.tar\.gz$' -or $asset.name -match '\.tgz$') { } elseif ($asset.name -match '\.tar\.gz$' -or $asset.name -match '\.tgz$') {
$tar = Get-Command tar -ErrorAction SilentlyContinue $tar = Get-Command tar -ErrorAction SilentlyContinue
if ($tar) { & $tar.Source -xzf $archive -C $extractDir } if ($tar) { & $tar.Source -xzf $archive -C $extractDir }
else { Fail "Asset is tar archive but 'tar' is not available." } else { throw "Asset is tar archive but 'tar' is not available." }
} else { } else {
try { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) } try { Add-Type -AssemblyName System.IO.Compression.FileSystem; [System.IO.Compression.ZipFile]::ExtractToDirectory($archive, $extractDir) }
catch { catch {
$tar = Get-Command tar -ErrorAction SilentlyContinue $tar = Get-Command tar -ErrorAction SilentlyContinue
if ($tar) { & $tar.Source -xf $archive -C $extractDir } else { Fail "Unknown archive format; neither zip nor tar workable." } if ($tar) { & $tar.Source -xf $archive -C $extractDir } else { throw "Unknown archive format; neither zip nor tar workable." }
}
}
} catch {
Write-Info "Failed to extract ${c}; trying next candidate. $_"
$tried += "${c}: extract failed"
continue
} }
}
$bin = $null $bin = $null
Get-ChildItem -Recurse -File $extractDir | ForEach-Object { Get-ChildItem -Recurse -File $extractDir | ForEach-Object {
if ($isWin) { if ($_.Name -ieq 'coyote.exe') { $bin = $_.FullName } } if ($isWin) { if ($_.Name -ieq 'coyote.exe') { $bin = $_.FullName } }
else { if ($_.Name -ieq 'coyote') { $bin = $_.FullName } } else { if ($_.Name -ieq 'coyote') { $bin = $_.FullName } }
} }
if (-not $bin) { Fail "Could not find coyote binary inside the archive." } if (-not $bin) {
Write-Info "Could not find coyote binary inside ${c}; trying next candidate"
$tried += "${c}: no coyote binary in archive"
continue
}
if (-not $isWin) { try { & chmod +x -- $bin } catch {} } if (-not $isWin) { try { & chmod +x -- $bin } catch {} }
$exec = if ($isWin) { 'coyote.exe'} else { 'coyote' } $works = $false
$dest = Join-Path $BinDir $exec try { & $bin --version *> $null; if ($LASTEXITCODE -eq 0) { $works = $true } } catch { }
Copy-Item -Force $bin $dest if (-not $works -and -not $isWin) {
Write-Info "Installed: $dest" # The temp dir may live on a noexec mount; retry from a probe file in
# the install directory before rejecting.
$probe = Join-Path $BinDir ".coyote-install-probe-$PID"
try {
Copy-Item -Force $bin $probe
& chmod +x -- $probe
& $probe --version *> $null
if ($LASTEXITCODE -eq 0) { $works = $true }
} catch { } finally {
Remove-Item -Force -ErrorAction SilentlyContinue $probe
}
}
if (-not $works) {
Write-Info "Downloaded $c but it failed to run on this system; trying next candidate"
$tried += "${c}: binary failed to run on this system"
continue
}
if ($isWin) { Copy-Item -Force $bin $dest
Write-Info "Installed: $dest"
$installed = $true
break
}
if (-not $installed) {
Write-Error "No usable asset found for $os-$arch. Tried:"
$tried | ForEach-Object { Write-Error " - $_" }
exit 1
}
if ($isWin) {
$pathParts = ($env:Path -split ';') | Where-Object { $_ -ne '' } $pathParts = ($env:Path -split ';') | Where-Object { $_ -ne '' }
if ($pathParts -notcontains $BinDir) { if ($pathParts -notcontains $BinDir) {
$userPath = [Environment]::GetEnvironmentVariable('Path', 'User'); if (-not $userPath) { $userPath = '' } # Read/write the User PATH via the registry directly: the [Environment]
if (-not ($userPath -split ';' | Where-Object { $_ -eq $BinDir })) { # round-trip expands %VAR% entries and bakes them in on write.
$regKey = [Microsoft.Win32.Registry]::CurrentUser.OpenSubKey('Environment', $true)
if ($regKey) {
$userPath = [string]$regKey.GetValue('Path', '', [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
if (-not (($userPath -split ';') -contains $BinDir)) {
$newUserPath = if ($userPath.Trim().Length -gt 0) { "$userPath;$BinDir" } else { $BinDir } $newUserPath = if ($userPath.Trim().Length -gt 0) { "$userPath;$BinDir" } else { $BinDir }
[Environment]::SetEnvironmentVariable('Path', $newUserPath, 'User') $regKey.SetValue('Path', $newUserPath, [Microsoft.Win32.RegistryValueKind]::ExpandString)
Write-Info "Added to User PATH: $BinDir (restart shell to take effect)" Write-Info "Added to User PATH: $BinDir (restart shell to take effect)"
} }
$regKey.Close()
} }
} else { }
} else {
if (-not ($env:PATH -split ':' | Where-Object { $_ -eq $BinDir })) { if (-not ($env:PATH -split ':' | Where-Object { $_ -eq $BinDir })) {
Write-Info "Note: $BinDir is not in PATH. Add it to your shell profile." Write-Info "Note: $BinDir is not in PATH. Add it to your shell profile."
} }
}
Write-Info "Done. Try: coyote --help"
} finally {
Remove-Item -Recurse -Force -ErrorAction SilentlyContinue $tmp
} }
Write-Info "Done. Try: coyote --help"
+190 -120
View File
@@ -13,8 +13,6 @@ set -euo pipefail
# --bin-dir <dir> Install directory (default: /usr/local/bin or ~/.local/bin). Or set BIN_DIR. # --bin-dir <dir> Install directory (default: /usr/local/bin or ~/.local/bin). Or set BIN_DIR.
REPO="Dark-Alex-17/coyote" REPO="Dark-Alex-17/coyote"
VERSION="${COYOTE_VERSION:-}"
BIN_DIR="${BIN_DIR:-}"
usage() { usage() {
echo "coyote installer (Linux/macOS)" echo "coyote installer (Linux/macOS)"
@@ -25,24 +23,6 @@ usage() {
echo " -h, --help Show help" echo " -h, --help Show help"
} }
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2;;
--bin-dir) BIN_DIR="$2"; shift 2;;
-h|--help) usage; exit 0;;
*) echo "Unknown argument: $1" >&2; usage; exit 2;;
esac
done
if [[ -z "${BIN_DIR}" ]]; then
if [[ -w "/usr/local/bin" ]]; then
BIN_DIR="/usr/local/bin"
else
BIN_DIR="${HOME}/.local/bin"
fi
fi
mkdir -p "${BIN_DIR}"
log() { log() {
echo "[coyote-install] $*" echo "[coyote-install] $*"
} }
@@ -54,42 +34,6 @@ need_cmd() {
fi fi
} }
need_cmd uname
need_cmd mktemp
need_cmd tar
if command -v curl >/dev/null 2>&1; then
DL=curl
elif command -v wget >/dev/null 2>&1; then
DL=wget
else
echo "Error: need curl or wget" >&2
exit 1
fi
UNAME_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$UNAME_OS" in
linux) OS=linux ;;
darwin) OS=darwin ;;
*) echo "Error: unsupported OS '$UNAME_OS'" >&2; exit 1;;
esac
UNAME_ARCH=$(uname -m)
case "$UNAME_ARCH" in
x86_64|amd64) ARCH=x86_64 ;;
aarch64|arm64) ARCH=aarch64 ;;
*) echo "Error: unsupported arch '$UNAME_ARCH'" >&2; exit 1;;
esac
log "Target: ${OS}-${ARCH}"
API_BASE="https://api.github.com/repos/${REPO}/releases"
if [[ -z "${VERSION}" ]]; then
RELEASE_URL="${API_BASE}/latest"
else
RELEASE_URL="${API_BASE}/tags/${VERSION}"
fi
http_get() { http_get() {
if [[ "$DL" == "curl" ]]; then if [[ "$DL" == "curl" ]]; then
curl -fsSL -H 'User-Agent: coyote-installer' "$1" curl -fsSL -H 'User-Agent: coyote-installer' "$1"
@@ -98,114 +42,240 @@ http_get() {
fi fi
} }
TMPDIR="$(mktemp -d)" smoke_test() {
trap 'rm -rf "$TMPDIR"' EXIT # The scratch dir may live on a noexec mount; if running in place fails,
# retry from a probe file in the install directory before rejecting.
local bin="$1"
if "$bin" --version >/dev/null 2>&1; then return 0; fi
local probe="${BIN_DIR}/.coyote-install-probe.$$"
local ok=1
if cp "$bin" "$probe" 2>/dev/null && chmod +x "$probe" 2>/dev/null; then
if "$probe" --version >/dev/null 2>&1; then ok=0; fi
fi
rm -f "$probe"
return "$ok"
}
log "Fetching release metadata from $RELEASE_URL" main() {
JSON="$TMPDIR/release.json" VERSION="${COYOTE_VERSION:-}"
if ! http_get "$RELEASE_URL" > "$JSON"; then BIN_DIR="${BIN_DIR:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2;;
--bin-dir) BIN_DIR="$2"; shift 2;;
-h|--help) usage; exit 0;;
*) echo "Unknown argument: $1" >&2; usage; exit 2;;
esac
done
if [[ -n "$VERSION" && "$VERSION" =~ ^[0-9] ]]; then VERSION="v${VERSION}"; fi
if [[ -z "${BIN_DIR}" ]]; then
if [[ -w "/usr/local/bin" ]]; then
BIN_DIR="/usr/local/bin"
else
BIN_DIR="${HOME}/.local/bin"
fi
fi
mkdir -p "${BIN_DIR}"
need_cmd uname
need_cmd mktemp
need_cmd tar
if command -v curl >/dev/null 2>&1; then
DL=curl
elif command -v wget >/dev/null 2>&1; then
DL=wget
else
echo "Error: need curl or wget" >&2
exit 1
fi
UNAME_OS=$(uname -s | tr '[:upper:]' '[:lower:]')
case "$UNAME_OS" in
linux) OS=linux ;;
darwin) OS=darwin ;;
*) echo "Error: unsupported OS '$UNAME_OS'" >&2; exit 1;;
esac
UNAME_ARCH=$(uname -m)
case "$UNAME_ARCH" in
x86_64|amd64) ARCH=x86_64 ;;
aarch64|arm64) ARCH=aarch64 ;;
*) echo "Error: unsupported arch '$UNAME_ARCH'" >&2; exit 1;;
esac
log "Target: ${OS}-${ARCH}"
API_BASE="https://api.github.com/repos/${REPO}/releases"
if [[ -z "${VERSION}" ]]; then
RELEASE_URL="${API_BASE}/latest"
else
RELEASE_URL="${API_BASE}/tags/${VERSION}"
fi
WORKDIR="$(mktemp -d)"
trap 'rm -rf "$WORKDIR"; rm -f "${BIN_DIR}/.coyote-install-probe.$$"' EXIT
log "Fetching release metadata from $RELEASE_URL"
JSON="$WORKDIR/release.json"
if ! http_get "$RELEASE_URL" > "$JSON"; then
echo "Error: failed to fetch release metadata. Check version tag." >&2 echo "Error: failed to fetch release metadata. Check version tag." >&2
exit 1 exit 1
fi fi
ASSET_CANDIDATES=() ASSET_CANDIDATES=()
if [[ "$OS" == "darwin" ]]; then if [[ "$OS" == "darwin" ]]; then
if [[ "$ARCH" == "x86_64" ]]; then if [[ "$ARCH" == "x86_64" ]]; then
ASSET_CANDIDATES+=("coyote-x86_64-apple-darwin.tar.gz") ASSET_CANDIDATES+=("coyote-x86_64-apple-darwin.tar.gz")
else else
ASSET_CANDIDATES+=("coyote-aarch64-apple-darwin.tar.gz") ASSET_CANDIDATES+=("coyote-aarch64-apple-darwin.tar.gz")
fi fi
elif [[ "$OS" == "linux" ]]; then elif [[ "$OS" == "linux" ]]; then
if [[ "$ARCH" == "x86_64" ]]; then
LIBC="musl" LIBC="musl"
if command -v getconf >/dev/null 2>&1 && getconf GNU_LIBC_VERSION >/dev/null 2>&1; then LIBC="gnu"; fi if command -v getconf >/dev/null 2>&1 && getconf GNU_LIBC_VERSION >/dev/null 2>&1; then LIBC="gnu"; fi
if ldd --version 2>&1 | grep -qi glibc; then LIBC="gnu"; fi if ldd --version 2>&1 | grep -qi glibc; then LIBC="gnu"; fi
if [[ "$LIBC" == "gnu" ]]; then if [[ "$LIBC" == "gnu" ]]; then
ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-gnu.tar.gz") # The gnu binary dynamically links OpenSSL 3. On Debian/Ubuntu, ldconfig lives
# in /usr/sbin, which is often missing from non-root PATHs, so try its known
# locations and fall back to probing the usual library directories directly.
LIBSSL3=""
for LDCONFIG in ldconfig /sbin/ldconfig /usr/sbin/ldconfig; do
if command -v "$LDCONFIG" >/dev/null 2>&1; then
if "$LDCONFIG" -p 2>/dev/null | grep -q 'libssl\.so\.3'; then LIBSSL3="yes"; fi
break
fi fi
done
ASSET_CANDIDATES+=("coyote-x86_64-unknown-linux-musl.tar.gz") if [[ -z "$LIBSSL3" ]]; then
for LIBSSL_CANDIDATE in /usr/lib/*/libssl.so.3 /lib/*/libssl.so.3 /usr/lib64/libssl.so.3 /usr/lib/libssl.so.3 /usr/local/lib/libssl.so.3 /usr/local/lib/*/libssl.so.3; do
if [[ -e "$LIBSSL_CANDIDATE" ]]; then LIBSSL3="yes"; break; fi
done
fi
if [[ -n "$LIBSSL3" ]]; then
ASSET_CANDIDATES+=("coyote-${ARCH}-unknown-linux-gnu.tar.gz")
else else
ASSET_CANDIDATES+=("coyote-aarch64-unknown-linux-musl.tar.gz") log "glibc detected but OpenSSL 3 (libssl.so.3) not found; using musl build"
fi
fi fi
else
echo "Error: unsupported OS for this installer: $OS" >&2; exit 1
fi
DL_URLS=$(grep -oE '"browser_download_url":[[:space:]]*"[^"]+"' "$JSON" \ ASSET_CANDIDATES+=("coyote-${ARCH}-unknown-linux-musl.tar.gz")
else
echo "Error: unsupported OS for this installer: $OS" >&2; exit 1
fi
DL_URLS=$(grep -oE '"browser_download_url":[[:space:]]*"[^"]+"' "$JSON" \
| sed -E 's/.*"browser_download_url":[[:space:]]*"//; s/"$//' \ | sed -E 's/.*"browser_download_url":[[:space:]]*"//; s/"$//' \
|| true) || true)
ASSET_NAME=""; ASSET_URL="" INSTALLED=""
for candidate in "${ASSET_CANDIDATES[@]}"; do TRIED=()
ATTEMPT=0
for candidate in "${ASSET_CANDIDATES[@]}"; do
ASSET_URL=""
while IFS= read -r url; do while IFS= read -r url; do
[[ -z "$url" ]] && continue [[ -z "$url" ]] && continue
if [[ "$url" == */"$candidate" ]]; then if [[ "$url" == */"$candidate" ]]; then
ASSET_NAME="$candidate"
ASSET_URL="$url" ASSET_URL="$url"
break break
fi fi
done <<< "$DL_URLS" done <<< "$DL_URLS"
[[ -n "$ASSET_URL" ]] && break
done
if [[ -z "$ASSET_URL" ]]; then if [[ -z "$ASSET_URL" ]]; then
echo "Error: no matching asset found for ${OS}-${ARCH}. Tried:" >&2 TRIED+=("$candidate: no matching release asset")
for c in "${ASSET_CANDIDATES[@]}"; do echo " - $c" >&2; done continue
exit 1
fi
log "Selected asset: $ASSET_NAME"
log "Download URL: $ASSET_URL"
ARCHIVE="$TMPDIR/asset"
if [[ "$DL" == "curl" ]]; then
curl -fL -H 'User-Agent: coyote-installer' "$ASSET_URL" -o "$ARCHIVE"
else
wget -q --header='User-Agent: coyote-installer' "$ASSET_URL" -O "$ARCHIVE"
fi
WORK="$TMPDIR/work"; mkdir -p "$WORK"
EXTRACTED_DIR="$WORK/extracted"; mkdir -p "$EXTRACTED_DIR"
if tar -tf "$ARCHIVE" >/dev/null 2>&1; then
tar -xzf "$ARCHIVE" -C "$EXTRACTED_DIR"
else
if command -v unzip >/dev/null 2>&1; then
unzip -q "$ARCHIVE" -d "$EXTRACTED_DIR"
else
echo "Error: unknown archive format; install 'unzip'" >&2
exit 1
fi fi
fi
BIN_PATH="" ATTEMPT=$((ATTEMPT + 1))
while IFS= read -r -d '' f; do WORK="$WORKDIR/attempt-$ATTEMPT"
mkdir -p "$WORK"
log "Selected asset: $candidate"
log "Download URL: $ASSET_URL"
ARCHIVE="$WORK/asset"
if [[ "$DL" == "curl" ]]; then
if ! curl -fL -H 'User-Agent: coyote-installer' "$ASSET_URL" -o "$ARCHIVE"; then
log "Failed to download $candidate; trying next candidate"
TRIED+=("$candidate: download failed")
continue
fi
else
if ! wget -q --header='User-Agent: coyote-installer' "$ASSET_URL" -O "$ARCHIVE"; then
log "Failed to download $candidate; trying next candidate"
TRIED+=("$candidate: download failed")
continue
fi
fi
EXTRACTED_DIR="$WORK/extracted"; mkdir -p "$EXTRACTED_DIR"
if tar -tf "$ARCHIVE" >/dev/null 2>&1; then
if ! tar -xzf "$ARCHIVE" -C "$EXTRACTED_DIR"; then
log "Failed to extract $candidate; trying next candidate"
TRIED+=("$candidate: extract failed")
continue
fi
else
if command -v unzip >/dev/null 2>&1; then
if ! unzip -q "$ARCHIVE" -d "$EXTRACTED_DIR"; then
log "Failed to extract $candidate; trying next candidate"
TRIED+=("$candidate: extract failed")
continue
fi
else
log "Unknown archive format for $candidate and 'unzip' is not available; trying next candidate"
TRIED+=("$candidate: unknown archive format and 'unzip' unavailable")
continue
fi
fi
BIN_PATH=""
while IFS= read -r -d '' f; do
base=$(basename "$f") base=$(basename "$f")
if [[ "$base" == "coyote" ]]; then if [[ "$base" == "coyote" ]]; then
BIN_PATH="$f" BIN_PATH="$f"
break break
fi fi
done < <(find "$EXTRACTED_DIR" -type f -print0) done < <(find "$EXTRACTED_DIR" -type f -print0)
if [[ -z "$BIN_PATH" ]]; then if [[ -z "$BIN_PATH" ]]; then
echo "Error: could not find 'coyote' binary in the archive" >&2 log "Could not find 'coyote' binary in $candidate; trying next candidate"
TRIED+=("$candidate: no 'coyote' binary in archive")
continue
fi
chmod +x "$BIN_PATH"
if ! smoke_test "$BIN_PATH"; then
log "Downloaded $candidate but it failed to run on this system; trying next candidate"
TRIED+=("$candidate: binary failed to run on this system")
continue
fi
install -m 0755 "$BIN_PATH" "${BIN_DIR}/coyote"
INSTALLED="$candidate"
break
done
if [[ -z "$INSTALLED" ]]; then
echo "Error: no usable asset found for ${OS}-${ARCH}. Tried:" >&2
for t in "${TRIED[@]}"; do echo " - $t" >&2; done
exit 1 exit 1
fi fi
chmod +x "$BIN_PATH" log "Installed: ${BIN_DIR}/coyote"
install -m 0755 "$BIN_PATH" "${BIN_DIR}/coyote"
log "Installed: ${BIN_DIR}/coyote" case ":$PATH:" in
case ":$PATH:" in
*":${BIN_DIR}:"*) ;; *":${BIN_DIR}:"*) ;;
*) *)
log "Note: ${BIN_DIR} is not in PATH. Add it, e.g.:" log "Note: ${BIN_DIR} is not in PATH. Add it, e.g.:"
log " export PATH=\"${BIN_DIR}:\$PATH\"" log " export PATH=\"${BIN_DIR}:\$PATH\""
;; ;;
esac esac
log "Done. Try: coyote --help" log "Done. Try: coyote --help"
}
main "$@"
+31 -1
View File
@@ -857,6 +857,15 @@ fn render_config_template(
model: &str, model: &str,
secrets: Option<&serde_json::Value>, secrets: Option<&serde_json::Value>,
clients: &serde_json::Value, clients: &serde_json::Value,
) -> Result<String> {
render_config_template_from(CONFIG_TEMPLATE, model, secrets, clients)
}
fn render_config_template_from(
template: &str,
model: &str,
secrets: Option<&serde_json::Value>,
clients: &serde_json::Value,
) -> Result<String> { ) -> Result<String> {
let to_yaml = |value: &serde_json::Value| { let to_yaml = |value: &serde_json::Value| {
serde_yaml::to_string(value).with_context(|| "Failed to create config") serde_yaml::to_string(value).with_context(|| "Failed to create config")
@@ -871,7 +880,8 @@ fn render_config_template(
}; };
let clients_block = to_yaml(&json!({ CLIENTS_FIELD: clients }))?; let clients_block = to_yaml(&json!({ CLIENTS_FIELD: clients }))?;
Ok(CONFIG_TEMPLATE Ok(template
.replace("\r\n", "\n")
.replacen("__MODEL_BLOCK__\n", &model_block, 1) .replacen("__MODEL_BLOCK__\n", &model_block, 1)
.replacen("__SECRETS_BLOCK__\n", &secrets_block, 1) .replacen("__SECRETS_BLOCK__\n", &secrets_block, 1)
.replacen("__CLIENTS_BLOCK__\n", &clients_block, 1)) .replacen("__CLIENTS_BLOCK__\n", &clients_block, 1))
@@ -1182,6 +1192,26 @@ clients:
assert_eq!(cfg.clients.len(), 1); assert_eq!(cfg.clients.len(), 1);
} }
#[test]
fn config_template_renders_with_crlf_line_endings() {
let crlf_template = CONFIG_TEMPLATE.replace('\n', "\r\n");
let secrets = json!({ "vault_password_file": "/home/user/.coyote_password" });
let clients = json!([{ "type": "openai", "api_key": "sk-test" }]);
let rendered =
render_config_template_from(&crlf_template, "openai:gpt-4o", Some(&secrets), &clients)
.unwrap();
assert!(!rendered.contains("__MODEL_BLOCK__"));
assert!(!rendered.contains("__SECRETS_BLOCK__"));
assert!(!rendered.contains("__CLIENTS_BLOCK__"));
assert!(!rendered.contains('\r'));
let cfg = Config::load_from_str(&rendered).unwrap();
assert_eq!(cfg.model_id, "openai:gpt-4o");
assert_eq!(cfg.clients.len(), 1);
}
#[test] #[test]
fn config_enabled_macros_empty_string_is_some_empty() { fn config_enabled_macros_empty_string_is_some_empty() {
let cfg: Config = serde_yaml::from_str("enabled_macros: \"\"").unwrap(); let cfg: Config = serde_yaml::from_str("enabled_macros: \"\"").unwrap();
+2
View File
@@ -70,7 +70,9 @@ fn normalize_version(requested: Option<String>) -> Option<String> {
fn preferred_update_target() -> Option<&'static str> { fn preferred_update_target() -> Option<&'static str> {
match (env::consts::OS, env::consts::ARCH) { match (env::consts::OS, env::consts::ARCH) {
("linux", "x86_64") if cfg!(target_env = "gnu") => Some("x86_64-unknown-linux-gnu"),
("linux", "x86_64") => Some("x86_64-unknown-linux-musl"), ("linux", "x86_64") => Some("x86_64-unknown-linux-musl"),
("linux", "aarch64") if cfg!(target_env = "gnu") => Some("aarch64-unknown-linux-gnu"),
("linux", "aarch64") => Some("aarch64-unknown-linux-musl"), ("linux", "aarch64") => Some("aarch64-unknown-linux-musl"),
_ => None, _ => None,
} }
+130 -77
View File
@@ -1175,6 +1175,35 @@ impl Functions {
Self::build_binaries(name, language, BinaryType::Agent, custom_runtime.as_deref()) Self::build_binaries(name, language, BinaryType::Agent, custom_runtime.as_deref())
} }
fn render_shim_template(
content_template: &str,
binary_name: &str,
binary_type: &BinaryType,
) -> String {
let functions_dir_env = get_env_name("functions_dir");
match binary_type {
BinaryType::Tool(None) => content_template
.replace("{function_name}", binary_name)
.replace("{root_dir_env}", &functions_dir_env)
.replace("{root_dir_rel}", "..")
.replace("{functions_dir_rel}", ".."),
BinaryType::Tool(Some(agent_name)) => content_template
.replace("{function_name}", binary_name)
.replace(
"{root_dir_env}",
&format!("{}_DATA_DIR", normalize_env_name(agent_name)),
)
.replace("{root_dir_rel}", "..")
.replace("{functions_dir_rel}", "../../../functions"),
BinaryType::Agent => content_template
.replace("{agent_name}", binary_name)
.replace("{root_dir_env}", &get_env_name("config_dir"))
.replace("{root_dir_rel}", "../../..")
.replace("{functions_dir_rel}", "../../../functions"),
}
.replace("{functions_dir_env}", &functions_dir_env)
}
#[cfg(windows)] #[cfg(windows)]
fn build_binaries( fn build_binaries(
binary_name: &str, binary_name: &str,
@@ -1218,41 +1247,7 @@ impl Functions {
) )
})?; })?;
let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) }; let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) };
let to_script_path = |p: &str| -> String { p.replace('\\', "/") }; let content = Self::render_shim_template(content_template, binary_name, &binary_type);
let content = match binary_type {
BinaryType::Tool(None) => {
let root_dir = paths::functions_dir();
let tool_path = format!(
"{}/{binary_name}",
paths::global_tools_dir().to_string_lossy()
);
content_template
.replace("{function_name}", binary_name)
.replace("{root_dir}", &to_script_path(&root_dir.to_string_lossy()))
.replace("{tool_path}", &to_script_path(&tool_path))
}
BinaryType::Tool(Some(agent_name)) => {
let root_dir = paths::agent_data_dir(agent_name);
let tool_path = format!(
"{}/{binary_name}",
paths::global_tools_dir().to_string_lossy()
);
content_template
.replace("{function_name}", binary_name)
.replace("{root_dir}", &to_script_path(&root_dir.to_string_lossy()))
.replace("{tool_path}", &to_script_path(&tool_path))
}
BinaryType::Agent => content_template
.replace("{agent_name}", binary_name)
.replace(
"{config_dir}",
&to_script_path(&paths::config_dir().to_string_lossy()),
),
}
.replace(
"{prompt_utils_file}",
&to_script_path(&paths::bash_prompt_utils_file().to_string_lossy()),
);
write_file_atomic(&binary_script_file, &content, None)?; write_file_atomic(&binary_script_file, &content, None)?;
info!( info!(
@@ -1298,21 +1293,17 @@ impl Functions {
_ => bail!("Unsupported language: {}", language.as_ref()), _ => bail!("Unsupported language: {}", language.as_ref()),
} }
}; };
let bin_dir = binary_file // %~dp0 (the .cmd's own directory) keeps the launcher relocatable: no
.parent() // absolute paths may be baked into it (see render_shim_template).
.expect("Failed to get parent directory of binary file"); let script_name = format!("run-{binary_name}.{}", language.to_extension());
let canonical_bin_dir = dunce::canonicalize(bin_dir)?.to_string_lossy().into_owned();
let wrapper_binary = dunce::canonicalize(&binary_script_file)?
.to_string_lossy()
.into_owned();
let content = formatdoc!( let content = formatdoc!(
r#" r#"
@echo off @echo off
setlocal setlocal
set "bin_dir={canonical_bin_dir}" set "bin_dir=%~dp0"
{run} "{wrapper_binary}" %*"#, {run} "%~dp0{script_name}" %*"#,
); );
write_file_atomic(&binary_file, &content, None)?; write_file_atomic(&binary_file, &content, None)?;
@@ -1352,37 +1343,7 @@ impl Functions {
) )
})?; })?;
let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) }; let content_template = unsafe { std::str::from_utf8_unchecked(&embedded_file.data) };
let mut content = match binary_type { let mut content = Self::render_shim_template(content_template, binary_name, &binary_type);
BinaryType::Tool(None) => {
let root_dir = paths::functions_dir();
let tool_path = format!(
"{}/{binary_name}",
paths::global_tools_dir().to_string_lossy()
);
content_template
.replace("{function_name}", binary_name)
.replace("{root_dir}", &root_dir.to_string_lossy())
.replace("{tool_path}", &tool_path)
}
BinaryType::Tool(Some(agent_name)) => {
let root_dir = paths::agent_data_dir(agent_name);
let tool_path = format!(
"{}/{binary_name}",
paths::global_tools_dir().to_string_lossy()
);
content_template
.replace("{function_name}", binary_name)
.replace("{root_dir}", &root_dir.to_string_lossy())
.replace("{tool_path}", &tool_path)
}
BinaryType::Agent => content_template
.replace("{agent_name}", binary_name)
.replace("{config_dir}", &paths::config_dir().to_string_lossy()),
}
.replace(
"{prompt_utils_file}",
&paths::bash_prompt_utils_file().to_string_lossy(),
);
if let Some(rt) = custom_runtime if let Some(rt) = custom_runtime
&& let Some(newline_pos) = content.find('\n') && let Some(newline_pos) = content.find('\n')
@@ -1398,9 +1359,10 @@ impl Functions {
write_file_atomic(&script_file, &content, Some(0o755))?; write_file_atomic(&script_file, &content, Some(0o755))?;
let ts_runtime = custom_runtime.unwrap_or("tsx"); let ts_runtime = custom_runtime.unwrap_or("tsx");
// Locate the script next to the wrapper at run time instead of
// baking an absolute path (see render_shim_template).
let wrapper = format!( let wrapper = format!(
"#!/bin/sh\nexec {ts_runtime} \"{}\" \"$@\"\n", "#!/bin/sh\nexec {ts_runtime} \"$(dirname \"$0\")/run-{binary_name}.ts\" \"$@\"\n",
script_file.display()
); );
write_file_atomic(&binary_file, &wrapper, Some(0o755))?; write_file_atomic(&binary_file, &wrapper, Some(0o755))?;
} else { } else {
@@ -4613,6 +4575,97 @@ mod tests {
fs::remove_dir_all(&dir).unwrap(); fs::remove_dir_all(&dir).unwrap();
} }
#[cfg(unix)]
#[test]
#[serial]
fn built_shims_resolve_paths_at_runtime_after_relocation() {
use std::os::unix::fs::PermissionsExt;
let root = temp_file("-shim-relocate-", "");
let home_a = root.join("home-a");
let config_a = home_a.join("coyote");
let tools_dir = config_a.join("functions").join("tools");
fs::create_dir_all(&tools_dir).unwrap();
fs::create_dir_all(config_a.join("functions").join("bin")).unwrap();
let tool_src = tools_dir.join("relocheck.sh");
fs::write(
&tool_src,
"#!/usr/bin/env bash\necho \"relocated-ok\" >> \"$LLM_OUTPUT\"\n",
)
.unwrap();
fs::set_permissions(&tool_src, fs::Permissions::from_mode(0o755)).unwrap();
let config_env = get_env_name("config_dir");
let functions_env = get_env_name("functions_dir");
let saved_config = env::var(&config_env).ok();
let saved_functions = env::var(&functions_env).ok();
unsafe {
env::set_var(&config_env, &config_a);
env::remove_var(&functions_env);
}
let build_result = Functions::build_global_function_binary("relocheck.sh", None);
unsafe {
match saved_config {
Some(v) => env::set_var(&config_env, v),
None => env::remove_var(&config_env),
}
match saved_functions {
Some(v) => env::set_var(&functions_env, v),
None => env::remove_var(&functions_env),
}
}
build_result.unwrap();
let shim = config_a.join("functions").join("bin").join("relocheck");
let content = fs::read_to_string(&shim).unwrap();
assert!(
!content.contains(config_a.to_str().unwrap()),
"shim must not bake the generation-time config path:\n{content}"
);
assert!(
content.contains(&functions_env),
"shim must consult the functions dir env override at run time:\n{content}"
);
// Simulate the config dir landing under a different home directory
// (shared/synced config dir, or a sandbox with another $HOME).
let home_b = root.join("home-b");
fs::rename(&home_a, &home_b).unwrap();
let moved_shim = home_b
.join("coyote")
.join("functions")
.join("bin")
.join("relocheck");
if which::which("bash").is_err() || which::which("jq").is_err() {
fs::remove_dir_all(&root).unwrap();
return;
}
let output = process::Command::new(&moved_shim)
.arg("{}")
.env_remove(&config_env)
.env_remove(&functions_env)
.env_remove("LLM_OUTPUT")
.env_remove("LLM_TOOL_DATA_FILE")
.output()
.unwrap();
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
output.status.success(),
"relocated shim failed: stderr={} stdout={stdout}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
stdout.contains("relocated-ok"),
"unexpected output: {stdout}"
);
fs::remove_dir_all(&root).unwrap();
}
#[test] #[test]
fn eval_tool_calls_partitions_mcp_and_sequential_then_resorts() { fn eval_tool_calls_partitions_mcp_and_sequential_then_resorts() {
let mut ctx = RequestContext::new(Arc::new(AppState::test_default()), WorkingMode::Cmd); let mut ctx = RequestContext::new(Arc::new(AppState::test_default()), WorkingMode::Cmd);
+12
View File
@@ -1869,6 +1869,17 @@ fn select_embedding_model(models: &[&Model]) -> Result<String> {
} }
pub(crate) fn select_rag_driver() -> Result<String> { pub(crate) fn select_rag_driver() -> Result<String> {
#[cfg(target_env = "musl")]
{
println!(
"Note: the duckdb RAG driver is only available in GNU builds of coyote \
(musl builds are statically linked and cannot load DuckDB extensions). \
Using the yaml driver."
);
Ok("yaml".to_string())
}
#[cfg(not(target_env = "musl"))]
{
let options = vec![ let options = vec![
"yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)", "yaml — portable, in-memory HNSW; usable from several Coyote processes at once (default)",
"duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.", "duckdb — persistent on-disk store; vectors and content survive restarts; HNSW approximate search.",
@@ -1887,6 +1898,7 @@ pub(crate) fn select_rag_driver() -> Result<String> {
} else { } else {
Ok("yaml".to_string()) Ok("yaml".to_string())
} }
}
} }
const EXTRACTOR_SKIP: &str = "Skip"; const EXTRACTOR_SKIP: &str = "Skip";
+20 -1
View File
@@ -85,6 +85,21 @@ pub struct DuckDbProvider {
} }
impl DuckDbProvider { impl DuckDbProvider {
/// Fail fast on musl builds, before any filesystem or DuckDB work. Coyote's musl
/// builds are statically linked, and a statically linked binary cannot dlopen the
/// `vss`/`fts` extension shared objects, so this driver can never work there.
fn reject_musl() -> Result<()> {
#[cfg(target_env = "musl")]
bail!(
"The duckdb RAG driver is unavailable in this build of coyote: musl builds \
are statically linked, and a statically linked binary cannot load DuckDB \
extensions like `vss`/`fts` (dlopen is unsupported). Use the `qdrant` RAG \
driver instead, or a gnu-libc build of coyote."
);
#[cfg(not(target_env = "musl"))]
Ok(())
}
/// Open (or create) the DuckDB file. `dim` is the embedding vector dimension, /// Open (or create) the DuckDB file. `dim` is the embedding vector dimension,
/// supplied by the caller who knows the model. /// supplied by the caller who knows the model.
/// ///
@@ -95,6 +110,7 @@ impl DuckDbProvider {
/// created or initialized here, or lazily through `ensure_writable` on the rebuild /// created or initialized here, or lazily through `ensure_writable` on the rebuild
/// path. /// path.
pub fn open(db_path: &Path, dim: usize) -> Result<Self> { pub fn open(db_path: &Path, dim: usize) -> Result<Self> {
Self::reject_musl()?;
let (conn, writable) = Self::open_for_workload(db_path, dim)?; let (conn, writable) = Self::open_for_workload(db_path, dim)?;
// A reopened file may already carry a live FTS index from a previous session, // A reopened file may already carry a live FTS index from a previous session,
// in which case keyword search works immediately. // in which case keyword search works immediately.
@@ -173,6 +189,7 @@ impl DuckDbProvider {
} }
pub fn introspect_dim(db_path: &Path) -> Result<Option<usize>> { pub fn introspect_dim(db_path: &Path) -> Result<Option<usize>> {
Self::reject_musl()?;
if !db_path.exists() { if !db_path.exists() {
return Ok(None); return Ok(None);
} }
@@ -819,7 +836,9 @@ impl RagProvider for DuckDbProvider {
} }
} }
#[cfg(test)] // Not compiled for musl: `open` bails there (statically linked binaries cannot dlopen
// the extension shared objects), so every test in this module would fail at setup.
#[cfg(all(test, not(target_env = "musl")))]
mod tests { mod tests {
use super::*; use super::*;
use crate::rag::provider::RagProvider; use crate::rag::provider::RagProvider;