diff --git a/.github/scripts/check_cli_flags.py b/.github/scripts/check_cli_flags.py index af1c9bf..bf27964 100644 --- a/.github/scripts/check_cli_flags.py +++ b/.github/scripts/check_cli_flags.py @@ -38,7 +38,7 @@ def main() -> int: with open(help_path, encoding="utf-8") as f: help_text = f.read() if not flags: - print("::error::found no flags in to_argv; has src/options.rs moved?") + print("::error::found no flags in to_argv; has crates/ccwire/src/options.rs moved?") return 1 missing = [] for flag in flags: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 096e356..9521398 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,7 +77,7 @@ jobs: features: - { name: default, flags: "" } # `runtime` adds the framing's async half and the session tests - # over the fake CLI (tests/fake_claude). + # over the fake CLI (crates/ccwire/tests/fake_claude). - { name: all-features, flags: "--all-features" } steps: - uses: actions/checkout@v7 @@ -88,7 +88,7 @@ jobs: - uses: Swatinem/rust-cache@v2 with: key: ${{ matrix.features.name }} - # Includes tests/corpus.rs, the golden corpus test. + # Includes crates/ccwire/tests/corpus.rs, the golden corpus test. - run: cargo test --workspace --all-targets --locked ${{ matrix.features.flags }} - run: cargo test --workspace --doc --locked ${{ matrix.features.flags }} @@ -101,13 +101,17 @@ jobs: - name: Read rust-version from Cargo.toml id: msrv run: | - if grep -Eq '^rust-version\.workspace|^rust-version *= *\{' Cargo.toml; then - echo "::error::Cargo.toml inherits rust-version from a workspace; this repo needs an explicit rust-version = \"x.y\"" - exit 1 - fi + # One MSRV, the root's [workspace.package] rust-version, which + # every crate inherits. + for manifest in crates/*/Cargo.toml; do + if ! grep -Eq '^rust-version\.workspace *= *true' "$manifest"; then + echo "::error::$manifest doesn't inherit rust-version (rust-version.workspace = true)" + exit 1 + fi + done msrv=$(sed -n 's/^rust-version *= *"\(.*\)"/\1/p' Cargo.toml) if [ -z "$msrv" ]; then - echo "::error::no rust-version = \"...\" in Cargo.toml" + echo "::error::no rust-version = \"...\" in Cargo.toml's [workspace.package]" exit 1 fi echo "MSRV: $msrv" @@ -125,7 +129,7 @@ jobs: with: toolchain: ${{ steps.msrv.outputs.version }} - uses: Swatinem/rust-cache@v2 - # Every crate in the workspace (they share the root's rust-version). + # Every crate in the workspace (they inherit the root's rust-version). - run: cargo +${{ steps.msrv.outputs.version }} build --workspace --all-features --locked - run: cargo +${{ steps.msrv.outputs.version }} build --workspace --locked diff --git a/.github/workflows/newest-cli.yml b/.github/workflows/newest-cli.yml index 2ed730f..6d188ad 100644 --- a/.github/workflows/newest-cli.yml +++ b/.github/workflows/newest-cli.yml @@ -43,10 +43,10 @@ jobs: - name: Compare the CLI version with TESTED_CLI_VERSION run: | - tested=$(sed -n 's/^pub const TESTED_CLI_VERSION: &str = "\(.*\)";/\1/p' src/lib.rs) + tested=$(sed -n 's/^pub const TESTED_CLI_VERSION: &str = "\(.*\)";/\1/p' crates/ccwire/src/lib.rs) newest=$(claude --version | awk '{print $1}') if [ -z "$tested" ]; then - echo "::error::no TESTED_CLI_VERSION in src/lib.rs" + echo "::error::no TESTED_CLI_VERSION in crates/ccwire/src/lib.rs" exit 1 fi { @@ -55,13 +55,13 @@ jobs: echo "- TESTED_CLI_VERSION: \`$tested\`" } >> "$GITHUB_STEP_SUMMARY" if [ "$newest" != "$tested" ]; then - echo "::warning::claude $newest differs from TESTED_CLI_VERSION $tested; re-record the corpus (tests/corpus)" + echo "::warning::claude $newest differs from TESTED_CLI_VERSION $tested; re-record the corpus (crates/ccwire/tests/corpus)" fi - name: Check that every to_argv flag is in claude --help run: | claude --help > "$RUNNER_TEMP/claude-help.txt" - python3 .github/scripts/check_cli_flags.py src/options.rs "$RUNNER_TEMP/claude-help.txt" + python3 .github/scripts/check_cli_flags.py crates/ccwire/src/options.rs "$RUNNER_TEMP/claude-help.txt" # Only with the secret: a skipped step costs nothing and needs no key. - name: Live turn (needs ANTHROPIC_API_KEY) diff --git a/Cargo.toml b/Cargo.toml index b4db871..9a4168d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,70 +1,43 @@ -[package] -name = "ccwire" -description = "A Rust SDK for Claude Code: the stream-json protocol, typed" -version = "0.1.1" +# ccwire: a Rust SDK for Claude Code. `ccwire` is the stream-json protocol +# and session core, `ccwire-oauth` a client for the Claude account endpoints +# an OAuth login can call. Each crate keeps its own version, README and +# exclude list; the rest is shared here. +[workspace] +resolver = "3" +members = ["crates/*"] + +[workspace.package] edition = "2024" license = "Apache-2.0" -readme = "README.md" repository = "https://github.com/talaagent/ccwire" -documentation = "https://docs.rs/ccwire" authors = ["Ian Macalinao "] +# The MSRV of every crate (CI's `msrv` job reads it from here). rust-version = "1.85" -keywords = ["claude", "claude-code", "agent", "sdk"] -categories = ["api-bindings"] -# The golden corpus (and scrub.py, its tool) stays in the repo; its test -# goes with it, as it include_str!s the corpus. So do the repo's CI, release -# and Nix files. -exclude = [ - "tests/corpus", - "tests/corpus.rs", - ".github", - ".gitignore", - "clippy.toml", - "deny.toml", - "release-plz.toml", - "flake.nix", - "flake.lock", -] - -[features] -default = [] -# The async layers over tokio: the framing's async reader and writer, and -# the session core. The `protocol` module and the sync framing never need it. -runtime = ["dep:tokio"] -# Logs every `Other` and `Malformed` frame (debug and warn, with its type), -# besides counting them (`protocol::stats`, always on). -tracing = ["dep:tracing"] +# Each crate turns on the features it uses (`features = [...]` beside +# `workspace = true`); the entries here only pin versions and turn default +# features off where a crate wants them off. +[workspace.dependencies] +httpdate = "1" +# reqwest's default TLS is rustls (aws-lc-rs provider, the platform's +# roots); http2 for the API, no system proxy lookup, no cookies. +reqwest = { version = "0.13", default-features = false, features = [ + "rustls", + "http2", + "charset", +] } +serde = { version = "1", features = ["derive"] } # serde_json without `preserve_order` or `float_roundtrip`: frames compare # as `Value`s, so key order and the last float digit don't matter. -[dependencies] -serde = { version = "1", features = ["derive"] } serde_json = "1" thiserror = "2" -# Only what the session core uses: async I/O traits and buffering (io-util), -# its channels (sync) and its reader/writer tasks (rt). -tokio = { version = "1", default-features = false, features = [ - "io-util", - "sync", - "rt", -], optional = true } -tracing = { version = "0.1", default-features = false, features = [ - "std", -], optional = true } - -[dev-dependencies] -# The tests' runtimes (current-thread and multi-thread) and their hang -# guard. -tokio = { version = "1", default-features = false, features = [ - "io-util", - "sync", - "rt", - "rt-multi-thread", - "macros", - "time", -] } +tokio = { version = "1", default-features = false } +tracing = { version = "0.1", default-features = false, features = ["std"] } +url = "2" +wiremock = "0.6" +zeroize = "1" -[lints.clippy] +[workspace.lints.clippy] pedantic = { level = "warn", priority = -1 } complexity = { level = "deny", priority = -1 } must_use_candidate = "allow" @@ -83,7 +56,3 @@ await_holding_lock = "deny" disallowed_methods = "deny" disallowed_types = "deny" large_futures = "allow" - -# ccwire is the root package; the rest of the repo's crates are members. -[workspace] -members = ["ccwire-oauth"] diff --git a/README.md b/README.md index 8b1f674..c5ce776 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ Until the first crates.io release, use the git repository: ccwire = { git = "https://github.com/talaagent/ccwire", features = ["runtime"] } ``` -The workspace also has [`ccwire-oauth`](https://github.com/talaagent/ccwire/tree/main/ccwire-oauth), a typed client for +The workspace also has [`ccwire-oauth`](https://github.com/talaagent/ccwire/tree/main/crates/ccwire-oauth), a typed client for the Claude account endpoints an OAuth login can call. ## `ccwire::protocol` diff --git a/ccwire-oauth/Cargo.toml b/ccwire-oauth/Cargo.toml deleted file mode 100644 index 5eb0249..0000000 --- a/ccwire-oauth/Cargo.toml +++ /dev/null @@ -1,61 +0,0 @@ -[package] -name = "ccwire-oauth" -description = "A typed async client for the Claude account endpoints an OAuth login can call" -version = "0.1.0" -edition = "2024" -license = "Apache-2.0" -readme = "README.md" -repository = "https://github.com/talaagent/ccwire" -documentation = "https://docs.rs/ccwire-oauth" -authors = ["Ian Macalinao "] -rust-version = "1.85" -keywords = ["claude", "oauth", "usage", "api-client"] -categories = ["api-bindings"] -# The recorded fixtures stay in the repo; the tests that include_str! them -# go with them. -exclude = ["tests"] - -[dependencies] -httpdate = "1" -# reqwest's default TLS is rustls (aws-lc-rs provider, the platform's -# roots); http2 for the API, no system proxy lookup, no cookies. -reqwest = { version = "0.13", default-features = false, features = [ - "rustls", - "http2", - "charset", -] } -serde = { version = "1", features = ["derive"] } -serde_json = "1" -thiserror = "2" -# Only tokio::spawn, for refresh_and_store; reqwest already runs on tokio. -tokio = { version = "1", default-features = false, features = ["rt"] } -url = "2" -zeroize = "1" - -[dev-dependencies] -tokio = { version = "1", default-features = false, features = [ - "rt", - "macros", - "sync", -] } -wiremock = "0.6" - -[lints.rust] -missing_docs = "warn" - -[lints.clippy] -pedantic = { level = "warn", priority = -1 } -complexity = { level = "deny", priority = -1 } -must_use_candidate = "allow" -needless_pass_by_value = "allow" -unnecessary_wraps = "allow" -unwrap_used = "deny" -std_instead_of_alloc = "deny" -std_instead_of_core = "deny" -cognitive_complexity = "deny" -too_many_lines = "deny" -clone_on_ref_ptr = "deny" -await_holding_lock = "deny" -disallowed_methods = "deny" -disallowed_types = "deny" -large_futures = "allow" diff --git a/ccwire-oauth/LICENSE b/ccwire-oauth/LICENSE deleted file mode 100644 index 1b5ec8b..0000000 --- a/ccwire-oauth/LICENSE +++ /dev/null @@ -1,176 +0,0 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - -2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - -3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - -4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - -5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - -6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - -8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS diff --git a/crates/ccwire-oauth/Cargo.toml b/crates/ccwire-oauth/Cargo.toml new file mode 100644 index 0000000..f658385 --- /dev/null +++ b/crates/ccwire-oauth/Cargo.toml @@ -0,0 +1,35 @@ +[package] +name = "ccwire-oauth" +description = "A typed async client for the Claude account endpoints an OAuth login can call" +version = "0.1.0" +edition.workspace = true +license.workspace = true +readme = "README.md" +repository.workspace = true +documentation = "https://docs.rs/ccwire-oauth" +authors.workspace = true +rust-version.workspace = true +keywords = ["claude", "oauth", "usage", "api-client"] +categories = ["api-bindings"] +# The recorded fixtures stay in the repo; the tests that include_str! them +# go with them. +exclude = ["tests"] + +[dependencies] +httpdate.workspace = true +reqwest.workspace = true +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +# Only tokio::spawn, for refresh_and_store; reqwest already runs on tokio. +tokio = { workspace = true, features = ["rt"] } +url.workspace = true +zeroize.workspace = true + +[dev-dependencies] +tokio = { workspace = true, features = ["rt", "macros", "sync"] } +wiremock.workspace = true + +# missing_docs is warned in src/lib.rs (#![warn(missing_docs)]). +[lints] +workspace = true diff --git a/crates/ccwire-oauth/LICENSE b/crates/ccwire-oauth/LICENSE new file mode 120000 index 0000000..30cff74 --- /dev/null +++ b/crates/ccwire-oauth/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/ccwire-oauth/README.md b/crates/ccwire-oauth/README.md similarity index 100% rename from ccwire-oauth/README.md rename to crates/ccwire-oauth/README.md diff --git a/ccwire-oauth/src/client.rs b/crates/ccwire-oauth/src/client.rs similarity index 100% rename from ccwire-oauth/src/client.rs rename to crates/ccwire-oauth/src/client.rs diff --git a/ccwire-oauth/src/error.rs b/crates/ccwire-oauth/src/error.rs similarity index 100% rename from ccwire-oauth/src/error.rs rename to crates/ccwire-oauth/src/error.rs diff --git a/ccwire-oauth/src/lenient.rs b/crates/ccwire-oauth/src/lenient.rs similarity index 100% rename from ccwire-oauth/src/lenient.rs rename to crates/ccwire-oauth/src/lenient.rs diff --git a/ccwire-oauth/src/lib.rs b/crates/ccwire-oauth/src/lib.rs similarity index 100% rename from ccwire-oauth/src/lib.rs rename to crates/ccwire-oauth/src/lib.rs diff --git a/ccwire-oauth/src/secret.rs b/crates/ccwire-oauth/src/secret.rs similarity index 100% rename from ccwire-oauth/src/secret.rs rename to crates/ccwire-oauth/src/secret.rs diff --git a/ccwire-oauth/src/token.rs b/crates/ccwire-oauth/src/token.rs similarity index 100% rename from ccwire-oauth/src/token.rs rename to crates/ccwire-oauth/src/token.rs diff --git a/ccwire-oauth/src/types.rs b/crates/ccwire-oauth/src/types.rs similarity index 100% rename from ccwire-oauth/src/types.rs rename to crates/ccwire-oauth/src/types.rs diff --git a/ccwire-oauth/tests/client.rs b/crates/ccwire-oauth/tests/client.rs similarity index 100% rename from ccwire-oauth/tests/client.rs rename to crates/ccwire-oauth/tests/client.rs diff --git a/ccwire-oauth/tests/fixtures/README.md b/crates/ccwire-oauth/tests/fixtures/README.md similarity index 100% rename from ccwire-oauth/tests/fixtures/README.md rename to crates/ccwire-oauth/tests/fixtures/README.md diff --git a/ccwire-oauth/tests/fixtures/api_error_auth.json b/crates/ccwire-oauth/tests/fixtures/api_error_auth.json similarity index 100% rename from ccwire-oauth/tests/fixtures/api_error_auth.json rename to crates/ccwire-oauth/tests/fixtures/api_error_auth.json diff --git a/ccwire-oauth/tests/fixtures/api_error_rate_limit.json b/crates/ccwire-oauth/tests/fixtures/api_error_rate_limit.json similarity index 100% rename from ccwire-oauth/tests/fixtures/api_error_rate_limit.json rename to crates/ccwire-oauth/tests/fixtures/api_error_rate_limit.json diff --git a/ccwire-oauth/tests/fixtures/overage_spend_limit.json b/crates/ccwire-oauth/tests/fixtures/overage_spend_limit.json similarity index 100% rename from ccwire-oauth/tests/fixtures/overage_spend_limit.json rename to crates/ccwire-oauth/tests/fixtures/overage_spend_limit.json diff --git a/ccwire-oauth/tests/fixtures/prepaid_credits.json b/crates/ccwire-oauth/tests/fixtures/prepaid_credits.json similarity index 100% rename from ccwire-oauth/tests/fixtures/prepaid_credits.json rename to crates/ccwire-oauth/tests/fixtures/prepaid_credits.json diff --git a/ccwire-oauth/tests/fixtures/profile.json b/crates/ccwire-oauth/tests/fixtures/profile.json similarity index 100% rename from ccwire-oauth/tests/fixtures/profile.json rename to crates/ccwire-oauth/tests/fixtures/profile.json diff --git a/ccwire-oauth/tests/fixtures/profile_email_address.json b/crates/ccwire-oauth/tests/fixtures/profile_email_address.json similarity index 100% rename from ccwire-oauth/tests/fixtures/profile_email_address.json rename to crates/ccwire-oauth/tests/fixtures/profile_email_address.json diff --git a/ccwire-oauth/tests/fixtures/roles.json b/crates/ccwire-oauth/tests/fixtures/roles.json similarity index 100% rename from ccwire-oauth/tests/fixtures/roles.json rename to crates/ccwire-oauth/tests/fixtures/roles.json diff --git a/ccwire-oauth/tests/fixtures/token.json b/crates/ccwire-oauth/tests/fixtures/token.json similarity index 100% rename from ccwire-oauth/tests/fixtures/token.json rename to crates/ccwire-oauth/tests/fixtures/token.json diff --git a/ccwire-oauth/tests/fixtures/token_invalid_grant.json b/crates/ccwire-oauth/tests/fixtures/token_invalid_grant.json similarity index 100% rename from ccwire-oauth/tests/fixtures/token_invalid_grant.json rename to crates/ccwire-oauth/tests/fixtures/token_invalid_grant.json diff --git a/ccwire-oauth/tests/fixtures/token_no_rotation.json b/crates/ccwire-oauth/tests/fixtures/token_no_rotation.json similarity index 100% rename from ccwire-oauth/tests/fixtures/token_no_rotation.json rename to crates/ccwire-oauth/tests/fixtures/token_no_rotation.json diff --git a/ccwire-oauth/tests/fixtures/usage.json b/crates/ccwire-oauth/tests/fixtures/usage.json similarity index 100% rename from ccwire-oauth/tests/fixtures/usage.json rename to crates/ccwire-oauth/tests/fixtures/usage.json diff --git a/ccwire-oauth/tests/fixtures/usage_idle.json b/crates/ccwire-oauth/tests/fixtures/usage_idle.json similarity index 100% rename from ccwire-oauth/tests/fixtures/usage_idle.json rename to crates/ccwire-oauth/tests/fixtures/usage_idle.json diff --git a/crates/ccwire/Cargo.toml b/crates/ccwire/Cargo.toml new file mode 100644 index 0000000..5c0a493 --- /dev/null +++ b/crates/ccwire/Cargo.toml @@ -0,0 +1,54 @@ +[package] +name = "ccwire" +description = "A Rust SDK for Claude Code: the stream-json protocol, typed" +version = "0.1.1" +edition.workspace = true +license.workspace = true +# The repository README (a symlink): the crate's README on crates.io. +readme = "README.md" +repository.workspace = true +documentation = "https://docs.rs/ccwire" +authors.workspace = true +rust-version.workspace = true +keywords = ["claude", "claude-code", "agent", "sdk"] +categories = ["api-bindings"] +# The golden corpus (and scrub.py, its tool) stays in the repo; its test +# goes with it, as it include_str!s the corpus. +exclude = ["tests/corpus", "tests/corpus.rs"] + +[features] +default = [] +# The async layers over tokio: the framing's async reader and writer, and +# the session core. The `protocol` module and the sync framing never need it. +runtime = ["dep:tokio"] +# Logs every `Other` and `Malformed` frame (debug and warn, with its type), +# besides counting them (`protocol::stats`, always on). +tracing = ["dep:tracing"] + +[dependencies] +serde.workspace = true +serde_json.workspace = true +thiserror.workspace = true +# Only what the session core uses: async I/O traits and buffering (io-util), +# its channels (sync) and its reader/writer tasks (rt). +tokio = { workspace = true, features = [ + "io-util", + "sync", + "rt", +], optional = true } +tracing = { workspace = true, optional = true } + +[dev-dependencies] +# The tests' runtimes (current-thread and multi-thread) and their hang +# guard. +tokio = { workspace = true, features = [ + "io-util", + "sync", + "rt", + "rt-multi-thread", + "macros", + "time", +] } + +[lints] +workspace = true diff --git a/crates/ccwire/LICENSE b/crates/ccwire/LICENSE new file mode 120000 index 0000000..30cff74 --- /dev/null +++ b/crates/ccwire/LICENSE @@ -0,0 +1 @@ +../../LICENSE \ No newline at end of file diff --git a/crates/ccwire/README.md b/crates/ccwire/README.md new file mode 120000 index 0000000..fe84005 --- /dev/null +++ b/crates/ccwire/README.md @@ -0,0 +1 @@ +../../README.md \ No newline at end of file diff --git a/src/framing.rs b/crates/ccwire/src/framing.rs similarity index 100% rename from src/framing.rs rename to crates/ccwire/src/framing.rs diff --git a/src/lib.rs b/crates/ccwire/src/lib.rs similarity index 100% rename from src/lib.rs rename to crates/ccwire/src/lib.rs diff --git a/src/options.rs b/crates/ccwire/src/options.rs similarity index 100% rename from src/options.rs rename to crates/ccwire/src/options.rs diff --git a/src/options/tests.rs b/crates/ccwire/src/options/tests.rs similarity index 100% rename from src/options/tests.rs rename to crates/ccwire/src/options/tests.rs diff --git a/src/protocol/content.rs b/crates/ccwire/src/protocol/content.rs similarity index 100% rename from src/protocol/content.rs rename to crates/ccwire/src/protocol/content.rs diff --git a/src/protocol/control.rs b/crates/ccwire/src/protocol/control.rs similarity index 100% rename from src/protocol/control.rs rename to crates/ccwire/src/protocol/control.rs diff --git a/src/protocol/field.rs b/crates/ccwire/src/protocol/field.rs similarity index 100% rename from src/protocol/field.rs rename to crates/ccwire/src/protocol/field.rs diff --git a/src/protocol/inbound.rs b/crates/ccwire/src/protocol/inbound.rs similarity index 100% rename from src/protocol/inbound.rs rename to crates/ccwire/src/protocol/inbound.rs diff --git a/src/protocol/input.rs b/crates/ccwire/src/protocol/input.rs similarity index 100% rename from src/protocol/input.rs rename to crates/ccwire/src/protocol/input.rs diff --git a/src/protocol/message.rs b/crates/ccwire/src/protocol/message.rs similarity index 100% rename from src/protocol/message.rs rename to crates/ccwire/src/protocol/message.rs diff --git a/src/protocol/mod.rs b/crates/ccwire/src/protocol/mod.rs similarity index 100% rename from src/protocol/mod.rs rename to crates/ccwire/src/protocol/mod.rs diff --git a/src/protocol/partial.rs b/crates/ccwire/src/protocol/partial.rs similarity index 100% rename from src/protocol/partial.rs rename to crates/ccwire/src/protocol/partial.rs diff --git a/src/protocol/result.rs b/crates/ccwire/src/protocol/result.rs similarity index 100% rename from src/protocol/result.rs rename to crates/ccwire/src/protocol/result.rs diff --git a/src/protocol/stats.rs b/crates/ccwire/src/protocol/stats.rs similarity index 100% rename from src/protocol/stats.rs rename to crates/ccwire/src/protocol/stats.rs diff --git a/src/protocol/system.rs b/crates/ccwire/src/protocol/system.rs similarity index 100% rename from src/protocol/system.rs rename to crates/ccwire/src/protocol/system.rs diff --git a/src/session.rs b/crates/ccwire/src/session.rs similarity index 100% rename from src/session.rs rename to crates/ccwire/src/session.rs diff --git a/tests/corpus.rs b/crates/ccwire/tests/corpus.rs similarity index 100% rename from tests/corpus.rs rename to crates/ccwire/tests/corpus.rs diff --git a/tests/corpus/README.md b/crates/ccwire/tests/corpus/README.md similarity index 100% rename from tests/corpus/README.md rename to crates/ccwire/tests/corpus/README.md diff --git a/tests/corpus/activity.jsonl b/crates/ccwire/tests/corpus/activity.jsonl similarity index 100% rename from tests/corpus/activity.jsonl rename to crates/ccwire/tests/corpus/activity.jsonl diff --git a/tests/corpus/multiturn.jsonl b/crates/ccwire/tests/corpus/multiturn.jsonl similarity index 100% rename from tests/corpus/multiturn.jsonl rename to crates/ccwire/tests/corpus/multiturn.jsonl diff --git a/tests/corpus/partial-text.jsonl b/crates/ccwire/tests/corpus/partial-text.jsonl similarity index 100% rename from tests/corpus/partial-text.jsonl rename to crates/ccwire/tests/corpus/partial-text.jsonl diff --git a/tests/corpus/partial-tool.jsonl b/crates/ccwire/tests/corpus/partial-tool.jsonl similarity index 100% rename from tests/corpus/partial-tool.jsonl rename to crates/ccwire/tests/corpus/partial-tool.jsonl diff --git a/tests/corpus/scrub.py b/crates/ccwire/tests/corpus/scrub.py similarity index 100% rename from tests/corpus/scrub.py rename to crates/ccwire/tests/corpus/scrub.py diff --git a/tests/corpus/stream.jsonl b/crates/ccwire/tests/corpus/stream.jsonl similarity index 100% rename from tests/corpus/stream.jsonl rename to crates/ccwire/tests/corpus/stream.jsonl diff --git a/tests/corpus/subagents-background.jsonl b/crates/ccwire/tests/corpus/subagents-background.jsonl similarity index 100% rename from tests/corpus/subagents-background.jsonl rename to crates/ccwire/tests/corpus/subagents-background.jsonl diff --git a/tests/corpus/subagents-fanout.jsonl b/crates/ccwire/tests/corpus/subagents-fanout.jsonl similarity index 100% rename from tests/corpus/subagents-fanout.jsonl rename to crates/ccwire/tests/corpus/subagents-fanout.jsonl diff --git a/tests/corpus/subagents-nested.jsonl b/crates/ccwire/tests/corpus/subagents-nested.jsonl similarity index 100% rename from tests/corpus/subagents-nested.jsonl rename to crates/ccwire/tests/corpus/subagents-nested.jsonl diff --git a/tests/corpus/subagents-self_resume.jsonl b/crates/ccwire/tests/corpus/subagents-self_resume.jsonl similarity index 100% rename from tests/corpus/subagents-self_resume.jsonl rename to crates/ccwire/tests/corpus/subagents-self_resume.jsonl diff --git a/tests/corpus/subagents-single.jsonl b/crates/ccwire/tests/corpus/subagents-single.jsonl similarity index 100% rename from tests/corpus/subagents-single.jsonl rename to crates/ccwire/tests/corpus/subagents-single.jsonl diff --git a/tests/corpus/synthetic.jsonl b/crates/ccwire/tests/corpus/synthetic.jsonl similarity index 100% rename from tests/corpus/synthetic.jsonl rename to crates/ccwire/tests/corpus/synthetic.jsonl diff --git a/tests/fake_claude/mod.rs b/crates/ccwire/tests/fake_claude/mod.rs similarity index 100% rename from tests/fake_claude/mod.rs rename to crates/ccwire/tests/fake_claude/mod.rs diff --git a/tests/framing.rs b/crates/ccwire/tests/framing.rs similarity index 100% rename from tests/framing.rs rename to crates/ccwire/tests/framing.rs diff --git a/tests/property.rs b/crates/ccwire/tests/property.rs similarity index 100% rename from tests/property.rs rename to crates/ccwire/tests/property.rs diff --git a/tests/protocol.rs b/crates/ccwire/tests/protocol.rs similarity index 100% rename from tests/protocol.rs rename to crates/ccwire/tests/protocol.rs diff --git a/tests/session.rs b/crates/ccwire/tests/session.rs similarity index 100% rename from tests/session.rs rename to crates/ccwire/tests/session.rs diff --git a/deny.toml b/deny.toml index 1aaaaa3..a9804ed 100644 --- a/deny.toml +++ b/deny.toml @@ -30,7 +30,11 @@ confidence-threshold = 0.9 # The list is the policy, not an inventory: no warning for an allowed # license no dependency uses (yet). unused-allowed-license = "allow" -exceptions = [] +exceptions = [ + # Mozilla's root certificates as data (reqwest -> rustls-platform-verifier, + # ccwire-oauth): CDLA-Permissive-2.0, a permissive data license. + { crate = "webpki-root-certs", allow = ["CDLA-Permissive-2.0"] }, +] [licenses.private] ignore = false diff --git a/flake.nix b/flake.nix index 8bbe394..8ddb48f 100644 --- a/flake.nix +++ b/flake.nix @@ -13,7 +13,7 @@ "aarch64-darwin" ]; forAllSystems = f: nixpkgs.lib.genAttrs systems (system: f nixpkgs.legacyPackages.${system}); - manifest = (nixpkgs.lib.importTOML ./Cargo.toml).package; + manifest = (nixpkgs.lib.importTOML ./crates/ccwire/Cargo.toml).package; in { # The whole workspace, built and tested (every feature) in the sandbox. diff --git a/release-plz.toml b/release-plz.toml index 78b97ca..d344819 100644 --- a/release-plz.toml +++ b/release-plz.toml @@ -22,10 +22,10 @@ pr_draft = false name = "ccwire" git_tag_name = "v{{ version }}" git_release_name = "v{{ version }}" -changelog_path = "CHANGELOG.md" +changelog_path = "crates/ccwire/CHANGELOG.md" [[package]] name = "ccwire-oauth" git_tag_name = "ccwire-oauth-v{{ version }}" git_release_name = "ccwire-oauth-v{{ version }}" -changelog_path = "ccwire-oauth/CHANGELOG.md" +changelog_path = "crates/ccwire-oauth/CHANGELOG.md"