@nzkbuild/toche 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/NOTICE +18 -0
- package/README.md +367 -0
- package/THIRD_PARTY_NOTICES.md +73 -0
- package/npm/bin/toche.js +33 -0
- package/npm/install.js +147 -0
- package/npm/install.test.js +25 -0
- package/package.json +50 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 Toche Contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/NOTICE
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
Toche
|
|
2
|
+
Copyright 2026 Toche Contributors
|
|
3
|
+
|
|
4
|
+
This product includes software developed by:
|
|
5
|
+
|
|
6
|
+
- rtk-ai (https://github.com/rtk-ai/rtk) — Apache-2.0
|
|
7
|
+
Copyright 2024 rtk-ai and rtk-ai Labs
|
|
8
|
+
|
|
9
|
+
- ryoppippi (https://github.com/ccusage/ccusage) — MIT
|
|
10
|
+
Copyright (c) 2025 ryoppippi
|
|
11
|
+
|
|
12
|
+
- Safi Shamsi (https://github.com/Graphify-Labs/graphify) — MIT
|
|
13
|
+
Copyright (c) 2026 Safi Shamsi
|
|
14
|
+
|
|
15
|
+
- Julius Brussee (https://github.com/juliusbrussee/caveman) — MIT
|
|
16
|
+
Copyright (c) 2026 Julius Brussee
|
|
17
|
+
|
|
18
|
+
- multica-ai (https://github.com/multica-ai/andrej-karpathy-skills) — MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/branding/toche-wordmark.png" alt="Toche" width="620">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<p align="center">
|
|
6
|
+
<strong>Claude Code repeats less, carries less noise, and leaves you a receipt.</strong>
|
|
7
|
+
</p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
Toche is a local gateway between Claude Code and your Anthropic API endpoint.
|
|
11
|
+
It removes avoidable work while keeping every optimization visible and reversible.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="assets/branding/toche-status.svg" alt="Toche 1.0.9: 65 filters, duplicate requests coalesced from many to one, zero hosted accounts, 354 Rust tests" width="820">
|
|
16
|
+
</p>
|
|
17
|
+
|
|
18
|
+
<p align="center">
|
|
19
|
+
<a href="#why-toche">Why Toche</a> ·
|
|
20
|
+
<a href="#what-changes">What changes</a> ·
|
|
21
|
+
<a href="#install">Install</a> ·
|
|
22
|
+
<a href="#how-it-works">How it works</a> ·
|
|
23
|
+
<a href="#command-reference">Commands</a> ·
|
|
24
|
+
<a href="#safety-and-control">Safety</a> ·
|
|
25
|
+
<a href="#documentation">Docs</a>
|
|
26
|
+
</p>
|
|
27
|
+
|
|
28
|
+
## Why Toche
|
|
29
|
+
|
|
30
|
+
Claude Code sessions often pay for the same kind of waste more than once: an
|
|
31
|
+
identical request is already running, a safe response was just fetched, or a tool
|
|
32
|
+
prints thousands of tokens when the useful result is a few lines. Toche handles
|
|
33
|
+
those cases before they consume another upstream call or more conversation space.
|
|
34
|
+
|
|
35
|
+
The useful part is not a universal marketing percentage. Toche measures your
|
|
36
|
+
actual traffic and reports what happened with `toche stats`.
|
|
37
|
+
|
|
38
|
+
## What changes
|
|
39
|
+
|
|
40
|
+
- **Three identical requests at the same time can become one upstream call.** The
|
|
41
|
+
first request runs while the other two wait for and share its response.
|
|
42
|
+
- **An eligible local replay becomes zero new upstream calls.** Toche only replays
|
|
43
|
+
workspace-matched, text-only responses that pass its safety checks.
|
|
44
|
+
- **Sixty-five command filters remove known noise.** Cargo, Git, Terraform, Helm,
|
|
45
|
+
Ansible, linters, and other supported output can be shortened while the original
|
|
46
|
+
remains recoverable with `toche expand`.
|
|
47
|
+
- **Every routed request leaves a local record.** Token counts, estimated cost,
|
|
48
|
+
latency, cache decisions, coalescing, and reduction are written to a local SQLite
|
|
49
|
+
ledger instead of a Toche cloud account.
|
|
50
|
+
|
|
51
|
+
Here is a deliberately simple example. If a supported tool prints an estimated
|
|
52
|
+
10,000 tokens and its useful reduced form is 3,000, the ledger records 7,000 tokens
|
|
53
|
+
removed, or 70%. That explains the measurement. It does not promise that every
|
|
54
|
+
command, repository, or session will save 70%.
|
|
55
|
+
|
|
56
|
+
Toche is most useful during long coding sessions, repeated test and diff cycles,
|
|
57
|
+
parallel agent work, and projects where you want to inspect the cost of the work.
|
|
58
|
+
A short conversation with no repetition or noisy tools may show little difference.
|
|
59
|
+
|
|
60
|
+
## Install
|
|
61
|
+
|
|
62
|
+
### Before you start
|
|
63
|
+
|
|
64
|
+
You need:
|
|
65
|
+
|
|
66
|
+
1. Claude Code installed and already working.
|
|
67
|
+
2. Node.js 18 or newer for the npm installer.
|
|
68
|
+
3. Windows x64, Linux x64, macOS Intel, or macOS Apple silicon.
|
|
69
|
+
|
|
70
|
+
Rust is not required when installing from npm.
|
|
71
|
+
|
|
72
|
+
### One-time setup
|
|
73
|
+
|
|
74
|
+
1. Install Toche globally. This makes the `toche` command available anywhere.
|
|
75
|
+
|
|
76
|
+
```shell
|
|
77
|
+
npm install -g @nzkbuild/toche
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
2. Import your existing Claude Code endpoint into a local Toche profile.
|
|
81
|
+
|
|
82
|
+
```shell
|
|
83
|
+
toche setup
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
3. Start Toche and leave this terminal open while using Claude Code.
|
|
87
|
+
|
|
88
|
+
```shell
|
|
89
|
+
toche
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
4. Open a second terminal, connect Claude Code, and verify the setup.
|
|
93
|
+
|
|
94
|
+
```shell
|
|
95
|
+
toche connect
|
|
96
|
+
toche doctor
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
5. Start Claude Code normally.
|
|
100
|
+
|
|
101
|
+
```shell
|
|
102
|
+
claude
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
`toche connect` creates a backup before changing Claude Code's local routing.
|
|
106
|
+
|
|
107
|
+
### Normal daily use
|
|
108
|
+
|
|
109
|
+
After the one-time setup, the routine is only:
|
|
110
|
+
|
|
111
|
+
1. Run `toche` and leave it open.
|
|
112
|
+
2. Run `claude` in another terminal.
|
|
113
|
+
|
|
114
|
+
You do not need to run `setup` again. You also do not need to run `connect` again
|
|
115
|
+
unless you previously used `toche disconnect`.
|
|
116
|
+
|
|
117
|
+
### Use these only when needed
|
|
118
|
+
|
|
119
|
+
| Command | When to use it |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `toche stats` | You want to see requests, tokens, estimated cost, and measured savings |
|
|
122
|
+
| `toche doctor` | Something is not connecting or you want a health check |
|
|
123
|
+
| `toche disconnect` | You want Claude Code to bypass Toche and use its original endpoint |
|
|
124
|
+
| `toche setup --force` | You intentionally want to replace the current Toche profile; a backup is created |
|
|
125
|
+
|
|
126
|
+
To uninstall cleanly, restore Claude Code's original route first:
|
|
127
|
+
|
|
128
|
+
```shell
|
|
129
|
+
toche disconnect
|
|
130
|
+
npm uninstall -g @nzkbuild/toche
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
<details>
|
|
134
|
+
<summary><strong>Build from source instead of npm</strong></summary>
|
|
135
|
+
|
|
136
|
+
You need Rust 1.86 or newer.
|
|
137
|
+
|
|
138
|
+
```shell
|
|
139
|
+
git clone https://github.com/nzkbuild/toche.git
|
|
140
|
+
cd toche
|
|
141
|
+
cargo build --release
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The binary is `target/release/toche` on Linux and macOS or
|
|
145
|
+
`target\release\toche.exe` on Windows. Run that binary with the same numbered setup
|
|
146
|
+
steps above.
|
|
147
|
+
|
|
148
|
+
</details>
|
|
149
|
+
|
|
150
|
+
## How it works
|
|
151
|
+
|
|
152
|
+
```mermaid
|
|
153
|
+
flowchart LR
|
|
154
|
+
A[Claude Code] --> B[Toche on 127.0.0.1:8743]
|
|
155
|
+
B --> C{Local pipeline}
|
|
156
|
+
C --> D[Shield and safe cache]
|
|
157
|
+
C --> E[Output reduction]
|
|
158
|
+
C --> F[Efficiency and prompt cache]
|
|
159
|
+
D --> G[Configured Anthropic API endpoint]
|
|
160
|
+
E --> G
|
|
161
|
+
F --> G
|
|
162
|
+
G --> B
|
|
163
|
+
B --> H[Local ledger and content store]
|
|
164
|
+
B --> A
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
In plain language, Claude Code sends its normal Anthropic Messages API request to
|
|
168
|
+
Toche on `127.0.0.1:8743`. Toche then:
|
|
169
|
+
|
|
170
|
+
1. Gives the request a stable fingerprint.
|
|
171
|
+
2. Shares an already-running identical request or checks for an eligible local replay.
|
|
172
|
+
3. Shortens supported tool output and keeps the original locally recoverable.
|
|
173
|
+
4. Applies the selected efficiency and provider prompt-cache policy.
|
|
174
|
+
5. Forwards any remaining work to your configured Anthropic API endpoint.
|
|
175
|
+
6. Records the outcome locally so `toche stats` can explain it.
|
|
176
|
+
|
|
177
|
+
The exact internal order is:
|
|
178
|
+
|
|
179
|
+
```text
|
|
180
|
+
fingerprint -> shield -> safe cache -> reduce -> efficiency -> cache -> forward -> ledger
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Toche fingerprints the canonical request, checks whether work can be safely shared
|
|
184
|
+
or replayed, reduces known tool-output noise, applies the selected efficiency and
|
|
185
|
+
prompt-cache policy, forwards the resulting request upstream, then records the
|
|
186
|
+
outcome locally.
|
|
187
|
+
|
|
188
|
+
For the module map, database schema, cache rules, and content-addressed storage
|
|
189
|
+
layout, see [the architecture guide](docs/ARCHITECTURE.md).
|
|
190
|
+
|
|
191
|
+
## Safety and control
|
|
192
|
+
|
|
193
|
+
- The gateway binds to `127.0.0.1:8743` by default.
|
|
194
|
+
- Toche configuration, its ledger, cache metadata, checkpoints, and stored content live locally.
|
|
195
|
+
- Requests that require upstream work still go to the Anthropic API endpoint configured in your profile.
|
|
196
|
+
- Persistent replay is limited to eligible responses. Responses containing `tool_use` blocks are rejected.
|
|
197
|
+
- `toche doctor` reports configuration and integration health.
|
|
198
|
+
- Every optimization stage has an explicit bypass header.
|
|
199
|
+
- `toche expand <hash>` restores original tool output after reduction.
|
|
200
|
+
- Cache entries can be inspected, explained, and cleared.
|
|
201
|
+
- `toche disconnect` restores direct Claude Code routing.
|
|
202
|
+
|
|
203
|
+
These controls make Toche inspectable and reversible. They are not a promise that
|
|
204
|
+
every request will be cheaper or that an optimization can never affect model behavior.
|
|
205
|
+
|
|
206
|
+
## Command reference
|
|
207
|
+
|
|
208
|
+
<details>
|
|
209
|
+
<summary><strong>Gateway, setup, and diagnostics</strong></summary>
|
|
210
|
+
|
|
211
|
+
| Command | What it does |
|
|
212
|
+
|---|---|
|
|
213
|
+
| `toche` | Start the gateway on `127.0.0.1:8743` |
|
|
214
|
+
| `toche setup` | Generate `profiles.toml` from Claude Code configuration |
|
|
215
|
+
| `toche setup --force` | Regenerate the profile and back up the existing file |
|
|
216
|
+
| `toche connect` | Route Claude Code through Toche |
|
|
217
|
+
| `toche disconnect` | Restore direct upstream routing |
|
|
218
|
+
| `toche doctor` | Show configuration and integration health |
|
|
219
|
+
| `toche status` | Show gateway status |
|
|
220
|
+
|
|
221
|
+
</details>
|
|
222
|
+
|
|
223
|
+
<details>
|
|
224
|
+
<summary><strong>Usage, reduction, and persistent cache</strong></summary>
|
|
225
|
+
|
|
226
|
+
| Command | What it does |
|
|
227
|
+
|---|---|
|
|
228
|
+
| `toche stats` | Show a human-readable usage and cost breakdown |
|
|
229
|
+
| `toche stats --json` | Print machine-readable statistics |
|
|
230
|
+
| `toche stats --entries 100` | Include the last 100 ledger entries |
|
|
231
|
+
| `toche expand <hash>` | Restore original tool output from a reduction hash |
|
|
232
|
+
| `toche cache inspect` | List persistent safe-cache entries |
|
|
233
|
+
| `toche cache clear` | Clear entries for the current project |
|
|
234
|
+
| `toche cache clear --all` | Clear all persistent cache entries |
|
|
235
|
+
| `toche cache why <fingerprint>` | Explain the cache decision for a fingerprint |
|
|
236
|
+
|
|
237
|
+
</details>
|
|
238
|
+
|
|
239
|
+
<details>
|
|
240
|
+
<summary><strong>Continuity and project graph</strong></summary>
|
|
241
|
+
|
|
242
|
+
| Command | What it does |
|
|
243
|
+
|---|---|
|
|
244
|
+
| `toche checkpoint save` | Save a session checkpoint |
|
|
245
|
+
| `toche checkpoint list` | List saved checkpoints |
|
|
246
|
+
| `toche checkpoint show` | Show the latest checkpoint |
|
|
247
|
+
| `toche checkpoint delete <id>` | Delete a checkpoint |
|
|
248
|
+
| `toche graph query <question>` | Query the optional knowledge graph |
|
|
249
|
+
| `toche graph status` | Show graph node and edge counts |
|
|
250
|
+
| `toche graph extract` | Rebuild the knowledge graph |
|
|
251
|
+
|
|
252
|
+
</details>
|
|
253
|
+
|
|
254
|
+
### Per-request bypasses
|
|
255
|
+
|
|
256
|
+
Set a header to `true`, case-insensitively, to skip a stage for one request.
|
|
257
|
+
The umbrella bypass takes precedence over individual bypasses.
|
|
258
|
+
|
|
259
|
+
| Header | Skips |
|
|
260
|
+
|---|---|
|
|
261
|
+
| `x-toche-bypass` | The complete optimization pipeline |
|
|
262
|
+
| `x-toche-bypass-shield` | Request coalescing |
|
|
263
|
+
| `x-toche-bypass-safe-cache` | Persistent cache lookup and storage |
|
|
264
|
+
| `x-toche-bypass-reduce` | Tool-output reduction |
|
|
265
|
+
| `x-toche-bypass-efficiency` | Efficiency instruction injection |
|
|
266
|
+
| `x-toche-bypass-cache` | Provider prompt-cache injection |
|
|
267
|
+
|
|
268
|
+
## Configuration
|
|
269
|
+
|
|
270
|
+
Profiles live in `~/.toche/profiles.toml`. Running `toche setup` generates a
|
|
271
|
+
profile from your existing Claude Code configuration.
|
|
272
|
+
|
|
273
|
+
<details>
|
|
274
|
+
<summary><strong>Example profile</strong></summary>
|
|
275
|
+
|
|
276
|
+
```toml
|
|
277
|
+
default = "default"
|
|
278
|
+
|
|
279
|
+
[[profiles]]
|
|
280
|
+
name = "default"
|
|
281
|
+
upstream_url = "https://api.anthropic.com"
|
|
282
|
+
auth_method = { type = "api_key", header_name = "x-api-key", key = "YOUR_ANTHROPIC_API_KEY" }
|
|
283
|
+
|
|
284
|
+
[profiles.cache]
|
|
285
|
+
enabled = true
|
|
286
|
+
mode = "auto"
|
|
287
|
+
breakpoint = "standard"
|
|
288
|
+
|
|
289
|
+
[profiles.reduce]
|
|
290
|
+
enabled = true
|
|
291
|
+
|
|
292
|
+
[profiles.efficiency]
|
|
293
|
+
mode = "concise"
|
|
294
|
+
|
|
295
|
+
[profiles.safe_cache]
|
|
296
|
+
enabled = true
|
|
297
|
+
ttl_days = 30
|
|
298
|
+
max_entry_bytes = 1048576
|
|
299
|
+
|
|
300
|
+
[profiles.graphify]
|
|
301
|
+
enabled = false
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
</details>
|
|
305
|
+
|
|
306
|
+
Set `TOCHE_CONFIG_DIR` to override the default `~/.toche/` directory.
|
|
307
|
+
|
|
308
|
+
## Troubleshooting
|
|
309
|
+
|
|
310
|
+
<details>
|
|
311
|
+
<summary><strong>The gateway will not start</strong></summary>
|
|
312
|
+
|
|
313
|
+
- Check that nothing else is listening on port 8743.
|
|
314
|
+
- Run `toche doctor` to verify that `profiles.toml` exists and is valid.
|
|
315
|
+
- Enable debug logging with `RUST_LOG=toche=debug toche`.
|
|
316
|
+
|
|
317
|
+
</details>
|
|
318
|
+
|
|
319
|
+
<details>
|
|
320
|
+
<summary><strong>Claude Code cannot connect</strong></summary>
|
|
321
|
+
|
|
322
|
+
- Start the gateway before running `toche connect`.
|
|
323
|
+
- Run `toche doctor` in a second terminal after connecting.
|
|
324
|
+
|
|
325
|
+
</details>
|
|
326
|
+
|
|
327
|
+
<details>
|
|
328
|
+
<summary><strong>Stats or cache entries are empty</strong></summary>
|
|
329
|
+
|
|
330
|
+
- The ledger records only requests routed through the gateway.
|
|
331
|
+
- The persistent cache stores only eligible text-only responses without `tool_use` blocks.
|
|
332
|
+
- Use `toche cache why <fingerprint>` to inspect a cache rejection.
|
|
333
|
+
|
|
334
|
+
</details>
|
|
335
|
+
|
|
336
|
+
<details>
|
|
337
|
+
<summary><strong>Routing still points to Toche after disconnecting</strong></summary>
|
|
338
|
+
|
|
339
|
+
Run `toche doctor`. If `env.ANTHROPIC_BASE_URL` still points to Toche while the
|
|
340
|
+
gateway is stopped, inspect `~/.claude/settings.json` and its Toche backup.
|
|
341
|
+
|
|
342
|
+
</details>
|
|
343
|
+
|
|
344
|
+
## Requirements
|
|
345
|
+
|
|
346
|
+
- Rust 1.86 or newer, edition 2024
|
|
347
|
+
- Claude Code or another Anthropic Messages API client
|
|
348
|
+
- No hosted Toche service
|
|
349
|
+
- SQLite is bundled through `rusqlite`
|
|
350
|
+
|
|
351
|
+
## Documentation
|
|
352
|
+
|
|
353
|
+
- [Architecture](docs/ARCHITECTURE.md): pipeline, modules, databases, and storage
|
|
354
|
+
- [Changelog](CHANGELOG.md): release history from 0.1.0 through 1.0.9
|
|
355
|
+
- [Bug tracker](docs/BUG_TRACKER.md): issues found and fixed during dogfooding
|
|
356
|
+
- [npm publishing](docs/NPM_PUBLISHING.md): maintainer checklist for the first npm release
|
|
357
|
+
- [Third-party notices](THIRD_PARTY_NOTICES.md): reused ideas, integration decisions, and attribution
|
|
358
|
+
|
|
359
|
+
## Built from good work
|
|
360
|
+
|
|
361
|
+
Toche's Rust implementation was informed by ideas and patterns from ccusage, RTK,
|
|
362
|
+
Graphify, andrej-karpathy-skills, and caveman-claude. Their licenses and attribution
|
|
363
|
+
are preserved in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md).
|
|
364
|
+
|
|
365
|
+
## License
|
|
366
|
+
|
|
367
|
+
Licensed under the [Apache License 2.0](LICENSE).
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Third-Party Notices
|
|
2
|
+
|
|
3
|
+
Toche's Rust implementation draws on design ideas and patterns from the projects
|
|
4
|
+
listed below. The repository keeps reference clones in the gitignored
|
|
5
|
+
`vendor_reuse/` directory, while Toche implements its integration under `src/`
|
|
6
|
+
and commits required build inputs under `assets/`. Applicable licenses,
|
|
7
|
+
copyright notices, project links, and integration decisions are preserved here.
|
|
8
|
+
|
|
9
|
+
## Reuse inventory
|
|
10
|
+
|
|
11
|
+
| Project | License | Clone commit | Integration | Purpose |
|
|
12
|
+
|---------|---------|-------------|-------------|---------|
|
|
13
|
+
| [ccusage](https://github.com/ccusage/ccusage) | MIT | `ba99c0d` | Reference: adapt pricing/reporting patterns | Usage ledger concepts, pricing models, reporting |
|
|
14
|
+
| [RTK](https://github.com/rtk-ai/rtk) | Apache-2.0 | `5d32d07` | Reference: adapt filter and hook architecture | Command/tool-output reduction, Claude Code hook integration |
|
|
15
|
+
| [Graphify](https://github.com/Graphify-Labs/graphify) | MIT | `43b2aff` | External CLI/MCP adapter | Local project graph and query service |
|
|
16
|
+
| [andrej-karpathy-skills](https://github.com/multica-ai/andrej-karpathy-skills) | MIT | `2c60614` | Reference: adapt policy rules | Careful-work policy profile (Section 0.6.0) |
|
|
17
|
+
| [caveman-claude](https://github.com/juliusbrussee/caveman) | MIT | `0d95a81` | Reference: adapt concision patterns | Concise response profile, token reduction strategies |
|
|
18
|
+
|
|
19
|
+
## License texts
|
|
20
|
+
|
|
21
|
+
### ccusage: MIT
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
MIT License
|
|
25
|
+
Copyright (c) 2025 ryoppippi
|
|
26
|
+
```
|
|
27
|
+
Full text: `vendor_reuse/ccusage/apps/ccusage/LICENSE`
|
|
28
|
+
|
|
29
|
+
### RTK: Apache-2.0
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
Apache License, Version 2.0
|
|
33
|
+
Copyright 2024 rtk-ai and rtk-ai Labs
|
|
34
|
+
```
|
|
35
|
+
The imported filter definitions are documented in
|
|
36
|
+
[`assets/filters/README.md`](assets/filters/README.md). The applicable
|
|
37
|
+
Apache-2.0 terms are reproduced in Toche's [LICENSE](LICENSE).
|
|
38
|
+
|
|
39
|
+
### Graphify: MIT
|
|
40
|
+
|
|
41
|
+
```
|
|
42
|
+
MIT License
|
|
43
|
+
Copyright (c) 2026 Safi Shamsi
|
|
44
|
+
```
|
|
45
|
+
Full text: `vendor_reuse/graphify/LICENSE`
|
|
46
|
+
|
|
47
|
+
### andrej-karpathy-skills: MIT
|
|
48
|
+
|
|
49
|
+
License type confirmed as MIT per project documentation. No standalone LICENSE file in repository root as of the cloned commit.
|
|
50
|
+
|
|
51
|
+
### caveman-claude: MIT
|
|
52
|
+
|
|
53
|
+
```
|
|
54
|
+
MIT License
|
|
55
|
+
Copyright (c) 2026 Julius Brussee
|
|
56
|
+
```
|
|
57
|
+
Full text: `vendor_reuse/caveman-claude/LICENSE`
|
|
58
|
+
|
|
59
|
+
## Integration decisions
|
|
60
|
+
|
|
61
|
+
Per Section 4 of the Toche plan, the decision for each project:
|
|
62
|
+
|
|
63
|
+
- **ccusage**: Reference adaptation. Study Rust usage-tracking patterns; adapt pricing models and reporting concepts into Toche's Meter module. Not a library dependency. Toche implements its own SQLite ledger.
|
|
64
|
+
- **RTK**: Reference adaptation. Toche implements its own deterministic reducer and imports 63 Apache-2.0 TOML filter definitions from the pinned RTK commit. The RTK binary is not bundled.
|
|
65
|
+
- **Graphify**: External adapter. Toche calls Graphify's CLI/MCP interface as an optional integration. Graphify is not compiled into Toche. No Python engine code is ported.
|
|
66
|
+
- **andrej-karpathy-skills**: Reference adaptation. Policy rules (think-first, surgical-change, verify-outcome) adapted into Toche's efficiency profiles. No verbatim copy.
|
|
67
|
+
- **caveman-claude**: Reference adaptation. Concision patterns and intensity levels adapted into Toche's response profiles. Hook architecture patterns inform Toche's own hook system.
|
|
68
|
+
|
|
69
|
+
No transitive dependencies from these projects are included in Toche. Each project's own dependency tree is documented in its respective repository.
|
|
70
|
+
|
|
71
|
+
## Toche license
|
|
72
|
+
|
|
73
|
+
Toche is distributed under the Apache License, Version 2.0. See [LICENSE](LICENSE) for the full text.
|
package/npm/bin/toche.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { spawn } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
const executable = process.platform === "win32" ? "toche-bin.exe" : "toche-bin";
|
|
9
|
+
const binary = path.join(__dirname, executable);
|
|
10
|
+
|
|
11
|
+
if (!fs.existsSync(binary)) {
|
|
12
|
+
console.error("Toche's native binary is missing.");
|
|
13
|
+
console.error("Reinstall without --ignore-scripts: npm install -g @nzkbuild/toche");
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const child = spawn(binary, process.argv.slice(2), {
|
|
18
|
+
argv0: "toche",
|
|
19
|
+
stdio: "inherit"
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
child.on("error", (error) => {
|
|
23
|
+
console.error(`Could not start Toche: ${error.message}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
child.on("exit", (code, signal) => {
|
|
28
|
+
if (signal) {
|
|
29
|
+
process.kill(process.pid, signal);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
process.exit(code === null ? 1 : code);
|
|
33
|
+
});
|
package/npm/install.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const crypto = require("node:crypto");
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const https = require("node:https");
|
|
6
|
+
const os = require("node:os");
|
|
7
|
+
const path = require("node:path");
|
|
8
|
+
const { spawnSync } = require("node:child_process");
|
|
9
|
+
|
|
10
|
+
const packageJson = require("../package.json");
|
|
11
|
+
const binDir = path.join(__dirname, "bin");
|
|
12
|
+
|
|
13
|
+
const targets = {
|
|
14
|
+
"darwin-arm64": { triple: "aarch64-apple-darwin", format: "tar.gz" },
|
|
15
|
+
"darwin-x64": { triple: "x86_64-apple-darwin", format: "tar.gz" },
|
|
16
|
+
"linux-x64": { triple: "x86_64-unknown-linux-gnu", format: "tar.gz" },
|
|
17
|
+
"win32-x64": { triple: "x86_64-pc-windows-msvc", format: "zip" }
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function releaseAsset(platform = process.platform, arch = process.arch) {
|
|
21
|
+
const key = `${platform}-${arch}`;
|
|
22
|
+
const target = targets[key];
|
|
23
|
+
if (!target) {
|
|
24
|
+
const supported = Object.keys(targets).join(", ");
|
|
25
|
+
throw new Error(`Unsupported platform ${key}. Supported platforms: ${supported}.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const stem = `toche-${packageJson.version}-${target.triple}`;
|
|
29
|
+
return { ...target, archive: `${stem}.${target.format}`, folder: stem };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseChecksum(text) {
|
|
33
|
+
const match = text.trim().match(/^([a-fA-F0-9]{64})\s+/);
|
|
34
|
+
if (!match) throw new Error("Release checksum file is malformed.");
|
|
35
|
+
return match[1].toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function sha256(file) {
|
|
39
|
+
const hash = crypto.createHash("sha256");
|
|
40
|
+
hash.update(fs.readFileSync(file));
|
|
41
|
+
return hash.digest("hex");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function download(url, destination, redirects = 0) {
|
|
45
|
+
return new Promise((resolve, reject) => {
|
|
46
|
+
https
|
|
47
|
+
.get(url, { headers: { "user-agent": "toche-npm-installer" } }, (response) => {
|
|
48
|
+
if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
|
|
49
|
+
response.resume();
|
|
50
|
+
if (redirects >= 5) {
|
|
51
|
+
reject(new Error(`Too many redirects while downloading ${url}.`));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const next = new URL(response.headers.location, url).toString();
|
|
55
|
+
download(next, destination, redirects + 1).then(resolve, reject);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (response.statusCode !== 200) {
|
|
60
|
+
response.resume();
|
|
61
|
+
reject(new Error(`Download failed with HTTP ${response.statusCode}: ${url}`));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const file = fs.createWriteStream(destination, { mode: 0o600 });
|
|
66
|
+
response.pipe(file);
|
|
67
|
+
file.on("finish", () => file.close(resolve));
|
|
68
|
+
file.on("error", reject);
|
|
69
|
+
})
|
|
70
|
+
.on("error", reject);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function extract(archive, destination, format) {
|
|
75
|
+
fs.mkdirSync(destination, { recursive: true });
|
|
76
|
+
if (format === "tar.gz") {
|
|
77
|
+
const result = spawnSync("tar", ["-xzf", archive, "-C", destination], { stdio: "inherit" });
|
|
78
|
+
if (result.status !== 0) throw new Error("Could not extract the Toche tar archive.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const powershell = process.env.SystemRoot
|
|
83
|
+
? path.join(process.env.SystemRoot, "System32", "WindowsPowerShell", "v1.0", "powershell.exe")
|
|
84
|
+
: "powershell.exe";
|
|
85
|
+
const command = "Expand-Archive -LiteralPath $args[0] -DestinationPath $args[1] -Force";
|
|
86
|
+
const result = spawnSync(
|
|
87
|
+
powershell,
|
|
88
|
+
["-NoProfile", "-NonInteractive", "-Command", command, archive, destination],
|
|
89
|
+
{ stdio: "inherit" }
|
|
90
|
+
);
|
|
91
|
+
if (result.status !== 0) throw new Error("Could not extract the Toche zip archive.");
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function install() {
|
|
95
|
+
const executable = process.platform === "win32" ? "toche-bin.exe" : "toche-bin";
|
|
96
|
+
const destination = path.join(binDir, executable);
|
|
97
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
98
|
+
|
|
99
|
+
if (process.env.TOCHE_BINARY_PATH) {
|
|
100
|
+
fs.copyFileSync(path.resolve(process.env.TOCHE_BINARY_PATH), destination);
|
|
101
|
+
if (process.platform !== "win32") fs.chmodSync(destination, 0o755);
|
|
102
|
+
console.log(`Toche ${packageJson.version} installed from TOCHE_BINARY_PATH.`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const asset = releaseAsset();
|
|
107
|
+
const tag = `v${packageJson.version}`;
|
|
108
|
+
const base = process.env.TOCHE_DOWNLOAD_BASE ||
|
|
109
|
+
`https://github.com/nzkbuild/toche/releases/download/${tag}`;
|
|
110
|
+
const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "toche-install-"));
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
const archive = path.join(temporary, asset.archive);
|
|
114
|
+
const checksumFile = `${archive}.sha256`;
|
|
115
|
+
console.log(`Downloading Toche ${packageJson.version} for ${process.platform}-${process.arch}...`);
|
|
116
|
+
await download(`${base}/${asset.archive}`, archive);
|
|
117
|
+
await download(`${base}/${asset.archive}.sha256`, checksumFile);
|
|
118
|
+
|
|
119
|
+
const expected = parseChecksum(fs.readFileSync(checksumFile, "utf8"));
|
|
120
|
+
const actual = sha256(archive);
|
|
121
|
+
if (actual !== expected) throw new Error(`Checksum mismatch for ${asset.archive}.`);
|
|
122
|
+
|
|
123
|
+
const extracted = path.join(temporary, "extracted");
|
|
124
|
+
extract(archive, extracted, asset.format);
|
|
125
|
+
const binaryName = process.platform === "win32" ? "toche.exe" : "toche";
|
|
126
|
+
const source = path.join(extracted, asset.folder, binaryName);
|
|
127
|
+
if (!fs.existsSync(source)) {
|
|
128
|
+
throw new Error(`Release archive does not contain ${asset.folder}/${binaryName}.`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fs.copyFileSync(source, destination);
|
|
132
|
+
if (process.platform !== "win32") fs.chmodSync(destination, 0o755);
|
|
133
|
+
console.log(`Toche ${packageJson.version} installed. Run: toche setup`);
|
|
134
|
+
} finally {
|
|
135
|
+
fs.rmSync(temporary, { recursive: true, force: true });
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (require.main === module) {
|
|
140
|
+
install().catch((error) => {
|
|
141
|
+
console.error(`Toche installation failed: ${error.message}`);
|
|
142
|
+
console.error("See https://github.com/nzkbuild/toche#install");
|
|
143
|
+
process.exit(1);
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = { install, parseChecksum, releaseAsset };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const assert = require("node:assert/strict");
|
|
4
|
+
const test = require("node:test");
|
|
5
|
+
const { parseChecksum, releaseAsset } = require("./install");
|
|
6
|
+
|
|
7
|
+
test("maps every published platform to its release archive", () => {
|
|
8
|
+
assert.equal(releaseAsset("linux", "x64").archive, "toche-1.0.9-x86_64-unknown-linux-gnu.tar.gz");
|
|
9
|
+
assert.equal(releaseAsset("win32", "x64").archive, "toche-1.0.9-x86_64-pc-windows-msvc.zip");
|
|
10
|
+
assert.equal(releaseAsset("darwin", "x64").archive, "toche-1.0.9-x86_64-apple-darwin.tar.gz");
|
|
11
|
+
assert.equal(releaseAsset("darwin", "arm64").archive, "toche-1.0.9-aarch64-apple-darwin.tar.gz");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("rejects a platform without a published binary", () => {
|
|
15
|
+
assert.throws(() => releaseAsset("linux", "arm64"), /Unsupported platform/);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test("parses sha256sum output", () => {
|
|
19
|
+
const hash = "a".repeat(64);
|
|
20
|
+
assert.equal(parseChecksum(`${hash} toche.tar.gz\n`), hash);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("rejects malformed checksums", () => {
|
|
24
|
+
assert.throws(() => parseChecksum("not-a-checksum"), /malformed/);
|
|
25
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nzkbuild/toche",
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"description": "Local efficiency gateway for Claude Code",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/nzkbuild/toche.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/nzkbuild/toche#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/nzkbuild/toche/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"claude-code",
|
|
16
|
+
"anthropic",
|
|
17
|
+
"context",
|
|
18
|
+
"cache",
|
|
19
|
+
"cli",
|
|
20
|
+
"rust"
|
|
21
|
+
],
|
|
22
|
+
"bin": {
|
|
23
|
+
"toche": "npm/bin/toche.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"npm/bin/toche.js",
|
|
27
|
+
"npm/install.js",
|
|
28
|
+
"npm/install.test.js",
|
|
29
|
+
"README.md",
|
|
30
|
+
"LICENSE",
|
|
31
|
+
"NOTICE",
|
|
32
|
+
"THIRD_PARTY_NOTICES.md"
|
|
33
|
+
],
|
|
34
|
+
"scripts": {
|
|
35
|
+
"postinstall": "node npm/install.js",
|
|
36
|
+
"test:npm": "node --test npm/install.test.js",
|
|
37
|
+
"check:package": "npm pack --dry-run"
|
|
38
|
+
},
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18"
|
|
41
|
+
},
|
|
42
|
+
"os": [
|
|
43
|
+
"darwin",
|
|
44
|
+
"linux",
|
|
45
|
+
"win32"
|
|
46
|
+
],
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public"
|
|
49
|
+
}
|
|
50
|
+
}
|