@semanticist14/clco 0.1.0
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 +21 -0
- package/README.md +279 -0
- package/bin/clco +27 -0
- package/bun.lock +39 -0
- package/install.sh +161 -0
- package/package.json +35 -0
- package/scripts/mock-upstream.ts +129 -0
- package/scripts/write-launcher.sh +47 -0
- package/src/api.ts +90 -0
- package/src/auth.ts +130 -0
- package/src/blocks.ts +198 -0
- package/src/browsermcp.ts +430 -0
- package/src/catalog.ts +147 -0
- package/src/claudehome.ts +269 -0
- package/src/cli.ts +889 -0
- package/src/config.ts +164 -0
- package/src/responses.ts +435 -0
- package/src/route.ts +52 -0
- package/src/server.ts +641 -0
- package/src/setup.ts +218 -0
- package/src/spawn.ts +453 -0
- package/src/stream.ts +235 -0
- package/src/tls.ts +234 -0
- package/src/token.ts +298 -0
- package/src/tokens.ts +55 -0
- package/src/translate.ts +384 -0
- package/src/wire.ts +149 -0
- package/uninstall.sh +42 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 semanticist21
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
# clco
|
|
2
|
+
|
|
3
|
+
Run Claude Code on your GitHub Copilot subscription. It starts a local adapter that translates Anthropic Messages ↔ GitHub Copilot, and runs claude against a config directory of its own — so **a `/model` pick inside clco never changes the model your plain `claude` uses**.
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
$ clco
|
|
7
|
+
│
|
|
8
|
+
◇ Checking GitHub token ✓ 0.0s
|
|
9
|
+
│
|
|
10
|
+
◇ Fetching Copilot token and model list ✓ 1.8s
|
|
11
|
+
│
|
|
12
|
+
◆ Pick a model - type to search
|
|
13
|
+
│ Search models...
|
|
14
|
+
│ ● Claude Sonnet 5 claude-sonnet-5 · native · 200k
|
|
15
|
+
│ ○ GPT-5.6 Luna gpt-5.6-luna · responses · 200k
|
|
16
|
+
│ ○ Kimi K3 kimi-k3 · chat · 918k
|
|
17
|
+
└
|
|
18
|
+
+ adapter: http://127.0.0.1:56844
|
|
19
|
+
+ model: claude-sonnet-5 (sonnet=claude-sonnet-5 opus=claude-opus-5 haiku=claude-haiku-4.5)
|
|
20
|
+
+ browser: @playwright/mcp@latest
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
From npm (Bun is still required at runtime):
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
npm install --global @semanticist14/clco
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Update npm installs with `npm install --global @semanticist14/clco@latest`.
|
|
32
|
+
|
|
33
|
+
Or use the GitHub installer:
|
|
34
|
+
|
|
35
|
+
```sh
|
|
36
|
+
curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/install.sh | bash
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Installs to `~/.local/share/clco`, launcher at `~/.local/bin/clco`.
|
|
40
|
+
For custom paths: `curl -fsSL ... | CLCO_DIR=~/somewhere BIN_DIR=~/bin bash`.
|
|
41
|
+
The same `CLCO_DIR` and `BIN_DIR` values can be passed to `uninstall.sh`.
|
|
42
|
+
|
|
43
|
+
Needs the [claude CLI](https://claude.com/claude-code), a GitHub account with
|
|
44
|
+
Copilot, and [Bun](https://bun.sh). npm installs the package, but clco still
|
|
45
|
+
runs on Bun because the adapter uses Bun's server and process APIs; Node is not
|
|
46
|
+
a runtime fallback yet.
|
|
47
|
+
|
|
48
|
+
If you need to install Bun yourself:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
curl -fsSL https://bun.sh/install | bash
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Reopen your terminal afterwards so `bun` is on your `PATH`.
|
|
55
|
+
|
|
56
|
+
## First run
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
clco
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
1. **GitHub login**, once — a device-flow code to paste in your browser. The
|
|
63
|
+
token is stored at `~/.config/clco/auth.json` (mode 600).
|
|
64
|
+
2. **Three questions**, once. Re-run them any time with `clco setup`:
|
|
65
|
+
|
|
66
|
+
| Question | Default | What it does |
|
|
67
|
+
|---|---|---|
|
|
68
|
+
| Run without permission prompts? | **Yes** | Passes `--dangerously-skip-permissions`, so claude edits files and runs commands without asking. `clco --no-bypass` for one session. |
|
|
69
|
+
| Enable Playwright MCP for browser control? | **Yes** | Registers the Playwright MCP server for clco sessions only. **Also needs a Chrome extension you install yourself** — see [Browser control](#browser-control). `clco --no-browser` for one session. |
|
|
70
|
+
| Pick a model each time clco starts? | **Yes** | Shows the model prompt at launch. Answering No reuses your last pick. `clco --no-select` for one session. |
|
|
71
|
+
|
|
72
|
+
3. **Pick a model**, then claude starts. Switch mid-session with `/model`.
|
|
73
|
+
|
|
74
|
+
Answering yes to the first two together is worth understanding: claude runs
|
|
75
|
+
without permission prompts, and each session resolves the current
|
|
76
|
+
`@playwright/mcp` release from npm (`bunx -y @playwright/mcp@latest`) and runs
|
|
77
|
+
it with access to the browser tab you share. The version is re-resolved every
|
|
78
|
+
session, so a release published today runs on your machine today; nothing in
|
|
79
|
+
this path checks an integrity hash. Either answer is reasonable alone; both at
|
|
80
|
+
once is a lot of trust in one command. `clco setup` changes them,
|
|
81
|
+
`--no-bypass` / `--no-browser` skip them for a single run, and
|
|
82
|
+
`CLCO_MCP_PACKAGE` pins or redirects the package — see
|
|
83
|
+
[Browser control](#browser-control).
|
|
84
|
+
|
|
85
|
+
## Usage
|
|
86
|
+
|
|
87
|
+
```sh
|
|
88
|
+
clco # a session
|
|
89
|
+
clco -p "question" # anything clco doesn't own goes straight to claude
|
|
90
|
+
clco status # account, plan, and every model's route/policy/context
|
|
91
|
+
clco setup # change the three answers above
|
|
92
|
+
clco help # all commands and environment variables
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Models
|
|
96
|
+
|
|
97
|
+
`/model` lists every conversational model your Copilot account offers, not just
|
|
98
|
+
the Claude ones. Each row shows the upstream id, its route, its real context
|
|
99
|
+
window, and whether it supports `/effort`.
|
|
100
|
+
|
|
101
|
+
What your plan actually allows is a separate matter, and Copilot's metadata does
|
|
102
|
+
not predict it — a model can be marked `enabled` and still refuse. Picking one
|
|
103
|
+
your plan does not cover answers `400 The requested model is not supported`.
|
|
104
|
+
|
|
105
|
+
Pin a model instead of choosing: `clco --model kimi-k3`, or per slot with
|
|
106
|
+
`CLCO_OPUS` / `CLCO_SONNET` / `CLCO_HAIKU` / `CLCO_FABLE` — the four model
|
|
107
|
+
slots Claude Code asks for by name.
|
|
108
|
+
|
|
109
|
+
## Browser control
|
|
110
|
+
|
|
111
|
+
Claude's own Chrome extension cannot work here: Claude Code gates it on the
|
|
112
|
+
session's OAuth scope, and clco authenticates with `ANTHROPIC_AUTH_TOKEN`, which
|
|
113
|
+
is always `user:inference`. Passing `--chrome` registers nothing.
|
|
114
|
+
|
|
115
|
+
Playwright MCP has no such gate, and in `--extension` mode it drives the tab you
|
|
116
|
+
share from your own browser — logins and cookies intact — rather than a fresh
|
|
117
|
+
profile.
|
|
118
|
+
|
|
119
|
+
**It needs a Chrome extension, and clco cannot install it for you.** Without it
|
|
120
|
+
there is no browser control at all — clco registers no server and no tools
|
|
121
|
+
appear. Install [Playwright
|
|
122
|
+
Extension](https://chromewebstore.google.com/detail/mmlmfjhmonkocbjadbfplnigmagldckm)
|
|
123
|
+
(listed under that name, not "Playwright MCP"), then answer yes at `clco setup`.
|
|
124
|
+
Tools arrive as `mcp__playwright__*` once you click the extension to share a tab.
|
|
125
|
+
|
|
126
|
+
The extension shows a `PLAYWRIGHT_MCP_EXTENSION_TOKEN`; storing it skips the
|
|
127
|
+
connect dialog every session:
|
|
128
|
+
|
|
129
|
+
```sh
|
|
130
|
+
pbpaste | clco token # clco token --clear to remove it
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Started with `bunx` (or `npx`) and fetched from npm each session. If that fails,
|
|
134
|
+
clco says which way it failed at startup rather than leaving you with tools that
|
|
135
|
+
silently never appear — unless `CLCO_MCP_PACKAGE` names a different package, in
|
|
136
|
+
which case clco cannot say whose registry serves it and reports that instead:
|
|
137
|
+
|
|
138
|
+
```
|
|
139
|
+
! browser: cannot reach registry.npmjs.org - browser tools will not appear
|
|
140
|
+
! browser: registry.npmjs.org is slow to answer - browser tools may be slow to appear
|
|
141
|
+
! browser: TLS rejected by registry.npmjs.org - browser tools will not appear. Set CLCO_CA_BUNDLE to your company CA.
|
|
142
|
+
! browser: registry.npmjs.org presented an expired certificate - browser tools will not appear. ...
|
|
143
|
+
+ browser: @corp/playwright-mcp@1.2.3 (not the default package, so the registry check was skipped)
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
That check only knows about `registry.npmjs.org`, so it cannot speak for an
|
|
147
|
+
internal mirror. `CLCO_MCP_PACKAGE` overrides the package spec for one — or to
|
|
148
|
+
pin a version, or roll back past a bad release:
|
|
149
|
+
|
|
150
|
+
```sh
|
|
151
|
+
CLCO_MCP_PACKAGE=@playwright/mcp@0.0.80 clco
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Pinning is not an offline mode: browser control needs npm either way.
|
|
155
|
+
|
|
156
|
+
## What clco reads
|
|
157
|
+
|
|
158
|
+
Nothing clco reads from your disk leaves the machine. Its outbound requests are
|
|
159
|
+
GitHub login, the Copilot token exchange, the model list, your chat itself,
|
|
160
|
+
plus — with browser control on — resolving `@playwright/mcp` against npm each
|
|
161
|
+
session, and checking GitHub for a staged update when you run `clco update`.
|
|
162
|
+
|
|
163
|
+
`clco update` installs and smoke-tests the new revision in a temporary checkout,
|
|
164
|
+
then swaps it in only after those checks pass. Local changes or a non-fast-forward
|
|
165
|
+
branch stop the update before the live install is touched.
|
|
166
|
+
|
|
167
|
+
It first tries `bun install --frozen-lockfile`. If the installed Bun cannot read
|
|
168
|
+
this lockfile format, it falls back to `bun install --no-save`, which installs
|
|
169
|
+
dependencies without rewriting `bun.lock`; using a current Bun release is still
|
|
170
|
+
recommended.
|
|
171
|
+
|
|
172
|
+
If you installed with custom `CLCO_DIR` or `BIN_DIR` values before launcher v4,
|
|
173
|
+
re-run the installer once with those same values so the launcher can migrate.
|
|
174
|
+
|
|
175
|
+
- **Chrome, Chromium and Edge profile directories** — directory *names* only, to
|
|
176
|
+
see whether the Playwright extension is installed.
|
|
177
|
+
- **The claude binary** — scanned once per claude version for the model ids it
|
|
178
|
+
knows, cached in `~/.config/clco/catalog.json`. Without it clco cannot tell
|
|
179
|
+
which ids `/model` will accept.
|
|
180
|
+
- **`~/.claude`** — every entry is symlinked into `~/.config/clco/claude-home`
|
|
181
|
+
except `settings.json`, `settings.local.json` and `backups`, which are
|
|
182
|
+
private copies. So plugins, skills, agents, projects, history and shell
|
|
183
|
+
snapshots stay shared and are written into `~/.claude` exactly as a plain
|
|
184
|
+
`claude` run would; only the settings file, where a `/model` pick would land,
|
|
185
|
+
is clco's own.
|
|
186
|
+
- **`~/.claude.json`** — copied into clco's config dir, since it holds trust
|
|
187
|
+
decisions and MCP servers, and it carries account identifiers and any secrets
|
|
188
|
+
your MCP servers declare. Your MCP servers and per-project trust are re-read
|
|
189
|
+
from the real file on every launch, so deleting a server or withdrawing trust
|
|
190
|
+
there applies to clco too; everything else in the copy is clco's own.
|
|
191
|
+
`uninstall.sh --full` removes it; a plain uninstall leaves it.
|
|
192
|
+
|
|
193
|
+
## Corporate networks
|
|
194
|
+
|
|
195
|
+
Behind a TLS-inspecting proxy, export your company CA and point clco at it:
|
|
196
|
+
|
|
197
|
+
```sh
|
|
198
|
+
security find-certificate -a -p -c "<CA name>" > ~/ca.pem # macOS
|
|
199
|
+
CLCO_CA_BUNDLE=~/ca.pem clco
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
clco **adds** it to the OS trust store rather than replacing it, so everything
|
|
203
|
+
that worked before still does. Several paths can be joined with `:`.
|
|
204
|
+
|
|
205
|
+
Prefer this to `NODE_EXTRA_CA_CERTS`, which has been reported to supplant the
|
|
206
|
+
system store on macOS and break trust that already worked. `NODE_USE_SYSTEM_CA`
|
|
207
|
+
does nothing on Bun — its default set already includes the system roots.
|
|
208
|
+
|
|
209
|
+
## Uninstall
|
|
210
|
+
|
|
211
|
+
```sh
|
|
212
|
+
# app + launcher (keeps your GitHub token)
|
|
213
|
+
curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/uninstall.sh | bash
|
|
214
|
+
|
|
215
|
+
# also wipe ~/.config/clco, token included
|
|
216
|
+
curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/uninstall.sh | bash -s -- --full
|
|
217
|
+
|
|
218
|
+
# custom installation paths
|
|
219
|
+
curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/uninstall.sh \
|
|
220
|
+
| CLCO_DIR="$HOME/somewhere" BIN_DIR="$HOME/bin" bash
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## How it works
|
|
224
|
+
|
|
225
|
+
1. One-time GitHub OAuth device flow → a long-lived token stored locally.
|
|
226
|
+
2. That token mints short-lived Copilot tokens while a local adapter
|
|
227
|
+
(127.0.0.1, ephemeral port) runs.
|
|
228
|
+
3. claude is launched against that adapter with `--settings`, and against a
|
|
229
|
+
private `CLAUDE_CONFIG_DIR` at `~/.config/clco/claude-home`. Your plugins,
|
|
230
|
+
skills, agents and commands are symlinked in, so they stay shared and live;
|
|
231
|
+
the files claude writes back — including the model a `/model` pick saves as
|
|
232
|
+
your default — stay inside clco's directory and never reach `~/.claude`.
|
|
233
|
+
4. The adapter picks a route per model from Copilot's own `/models`:
|
|
234
|
+
- **native** — Copilot serves Claude models on `/v1/messages`, the real
|
|
235
|
+
Anthropic endpoint, so those requests pass through untranslated: thinking,
|
|
236
|
+
`cache_control`, `/effort` and tool blocks stay intact.
|
|
237
|
+
- **translated** — everything else becomes `chat/completions`, or the
|
|
238
|
+
Responses API where a model requires it (the GPT-5.x family). SSE
|
|
239
|
+
streaming, tool calling, images and parallel tool calls work on both.
|
|
240
|
+
|
|
241
|
+
A rejected native attempt falls back to the translated path. Only an
|
|
242
|
+
unsupported model/endpoint rejection (including HTTP 404) is remembered;
|
|
243
|
+
other request errors leave native routing available on the next turn.
|
|
244
|
+
Route changes are recorded in the adapter log (`CLCO_DEBUG=1` in run mode).
|
|
245
|
+
`CLCO_NO_PASSTHROUGH=1` forces translation everywhere.
|
|
246
|
+
|
|
247
|
+
Input token counts are approximate. Near the discovered limit, the adapter
|
|
248
|
+
logs a warning and forwards the request; the upstream decides whether it
|
|
249
|
+
fits. Catalog-known Claude rows use Claude Code's own per-model context
|
|
250
|
+
handling after `/model` switches. If the lineup contains a non-catalog row,
|
|
251
|
+
clco applies a conservative session ceiling based on the exact selectable
|
|
252
|
+
rows.
|
|
253
|
+
|
|
254
|
+
## Development
|
|
255
|
+
|
|
256
|
+
```sh
|
|
257
|
+
bun install
|
|
258
|
+
bun test # unit + mock-upstream integration tests
|
|
259
|
+
bun run check # tsc --noEmit
|
|
260
|
+
CLCO_UPSTREAM=http://127.0.0.1:9099 bun run scripts/mock-upstream.ts
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
## Caveats
|
|
264
|
+
|
|
265
|
+
- Using Copilot outside official clients is a gray area of GitHub's terms. clco
|
|
266
|
+
authenticates with the VS Code Copilot OAuth client id and sends the same
|
|
267
|
+
editor headers, so its requests are indistinguishable from the official
|
|
268
|
+
client — that is what makes it work, and what makes it a gray area. Heavy use
|
|
269
|
+
may flag your account, and Claude models consume premium quota. Intended for
|
|
270
|
+
personal use.
|
|
271
|
+
- Extended thinking works on the native route only; it stays disabled on
|
|
272
|
+
translated routes. `stop_sequences` are not enforced on Responses-API models.
|
|
273
|
+
- `/effort` is forwarded when the selected model declares that level, and
|
|
274
|
+
dropped otherwise — rows in `/model` say which do not support it.
|
|
275
|
+
- Catalog-known Claude rows recalculate their context handling when `/model`
|
|
276
|
+
switches. Non-catalog rows use a conservative session ceiling because
|
|
277
|
+
Claude Code cannot inspect their upstream context limit; this can compact
|
|
278
|
+
earlier than a larger model requires.
|
|
279
|
+
- License: [MIT](LICENSE)
|
package/bin/clco
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
CLCO_LAUNCHER="${BASH_SOURCE[0]}"
|
|
5
|
+
while [ -L "$CLCO_LAUNCHER" ]; do
|
|
6
|
+
CLCO_LAUNCHER_DIR="$(cd -P -- "$(dirname -- "$CLCO_LAUNCHER")" && pwd)"
|
|
7
|
+
CLCO_LAUNCHER="$(readlink -- "$CLCO_LAUNCHER")"
|
|
8
|
+
case "$CLCO_LAUNCHER" in
|
|
9
|
+
/*) ;;
|
|
10
|
+
*) CLCO_LAUNCHER="$CLCO_LAUNCHER_DIR/$CLCO_LAUNCHER" ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
CLCO_PACKAGE_DIR="$(cd -P -- "$(dirname -- "$CLCO_LAUNCHER")/.." && pwd)"
|
|
14
|
+
export CLCO_NPM_INSTALL=1
|
|
15
|
+
CLCO_BUN_BIN="${CLCO_BUN_BIN:-$(command -v bun || true)}"
|
|
16
|
+
if [ -z "$CLCO_BUN_BIN" ]; then
|
|
17
|
+
printf '%s\n' "Bun is required to run clco. Install it with: curl -fsSL https://bun.sh/install | bash" >&2
|
|
18
|
+
printf '%s\n' "Node is not a runtime fallback yet; see https://bun.sh/docs/installation" >&2
|
|
19
|
+
exit 1
|
|
20
|
+
fi
|
|
21
|
+
|
|
22
|
+
if [ "${1:-}" = "--" ]; then
|
|
23
|
+
shift
|
|
24
|
+
set -- "__clco_passthrough__" "$@"
|
|
25
|
+
fi
|
|
26
|
+
|
|
27
|
+
exec "$CLCO_BUN_BIN" run --no-install "$CLCO_PACKAGE_DIR/src/cli.ts" "$@"
|
package/bun.lock
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"lockfileVersion": 2,
|
|
3
|
+
"configVersion": 1,
|
|
4
|
+
"workspaces": {
|
|
5
|
+
"": {
|
|
6
|
+
"name": "clcopilot",
|
|
7
|
+
"dependencies": {
|
|
8
|
+
"@clack/prompts": "^1.8.0",
|
|
9
|
+
},
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/bun": "latest",
|
|
12
|
+
"typescript": "^5",
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
"packages": {
|
|
17
|
+
"@clack/core": ["@clack/core@1.5.0", "", { "dependencies": { "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-zNikCcd8BbcEvzzG1sbXFrRHFk5kHPrpwZwksPvf9qyQO1Teb7JaXaOAxXZei9nZLDW0gaZawiuTCji88bTBhw=="],
|
|
18
|
+
|
|
19
|
+
"@clack/prompts": ["@clack/prompts@1.8.0", "", { "dependencies": { "@clack/core": "1.5.0", "fast-string-width": "^3.0.2", "fast-wrap-ansi": "^0.2.0", "sisteransi": "^1.0.5" } }, "sha512-PXzLZ8N34rxmuo4dJg3xtOXhcBse94qGjDqsteoEYrFrrZ5FSjIGwMAuOcv64ln8rHVBBD06XeVGr+/JX+plcA=="],
|
|
20
|
+
|
|
21
|
+
"@types/bun": ["@types/bun@1.4.2", "", { "dependencies": { "bun-types": "1.4.2" } }, "sha512-GimotNn7+ZV0uVArItBbriZsR1oNf0+WTzPkdcFrzShI7k2norL0uzEaJT8T33dWr7O/c9ZDuAFQrctKCi72oQ=="],
|
|
22
|
+
|
|
23
|
+
"@types/node": ["@types/node@22.20.2", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-xlvWf4Vs9n1PEVYwP1n4vvG07M6y8WgvJ2t0vbrWTmijsIHp1cS+uJ2kMIRdY3nHZK0nCYKrPeD171+SzF4/zw=="],
|
|
24
|
+
|
|
25
|
+
"bun-types": ["bun-types@1.4.2", "", { "dependencies": { "@types/node": "*" } }, "sha512-bxV1FgK7yBIzjRe5zBozIM4Bem11ZJcCXSrjWRG3YWLt8yFDePu4cLjpebO8OvPeIE9trbyPF4fuj3Cia4Fj3w=="],
|
|
26
|
+
|
|
27
|
+
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
|
|
28
|
+
|
|
29
|
+
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
|
|
30
|
+
|
|
31
|
+
"fast-wrap-ansi": ["fast-wrap-ansi@0.2.2", "", { "dependencies": { "fast-string-width": "^3.0.2" } }, "sha512-7F2Fl+TjRSenLqlU3UjSH0iyqopqoZIu7eZVpEirP2g1GtWa2G/ecEmBdgz31+Mxr+ELclgg6sokpSFIQiZ02Q=="],
|
|
32
|
+
|
|
33
|
+
"sisteransi": ["sisteransi@1.0.5", "", {}, "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="],
|
|
34
|
+
|
|
35
|
+
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
|
36
|
+
|
|
37
|
+
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
|
38
|
+
}
|
|
39
|
+
}
|
package/install.sh
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# clco installer — GitHub Copilot-backed Claude Code wrapper.
|
|
3
|
+
# curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/install.sh | bash
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
REPO="https://github.com/semanticist21/clco.git"
|
|
7
|
+
CLCO_DIR="${CLCO_DIR:-$HOME/.local/share/clco}"
|
|
8
|
+
BIN_DIR="${BIN_DIR:-$HOME/.local/bin}"
|
|
9
|
+
|
|
10
|
+
log() { printf '\033[1;36m==>\033[0m %s\n' "$*"; }
|
|
11
|
+
fail() { printf '\033[1;31mError:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
12
|
+
|
|
13
|
+
command -v git >/dev/null 2>&1 || fail "git is required - please install it first"
|
|
14
|
+
|
|
15
|
+
install_dependencies() {
|
|
16
|
+
local target="$1"
|
|
17
|
+
local frozen_output
|
|
18
|
+
log "Installing dependencies (bun install)"
|
|
19
|
+
if frozen_output="$(cd "$target" && bun install --frozen-lockfile 2>&1)"; then
|
|
20
|
+
return 0
|
|
21
|
+
fi
|
|
22
|
+
if [[ "$frozen_output" != *"Unknown lockfile version"* &&
|
|
23
|
+
"$frozen_output" != *"UnknownLockfileVersion"* &&
|
|
24
|
+
"$frozen_output" != *"failed to parse lockfile"* ]]; then
|
|
25
|
+
printf '%s\n' "$frozen_output" >&2
|
|
26
|
+
return 1
|
|
27
|
+
fi
|
|
28
|
+
if (cd "$target" && bun install --no-save >/dev/null 2>&1); then
|
|
29
|
+
return 0
|
|
30
|
+
fi
|
|
31
|
+
log "bun install failed - full output:"
|
|
32
|
+
printf '%s\n' "$frozen_output" >&2
|
|
33
|
+
(cd "$target" && bun install --no-save) || return 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# --- Bun --------------------------------------------------------------------
|
|
37
|
+
if ! command -v bun >/dev/null 2>&1; then
|
|
38
|
+
fail "Bun is not installed. Please install using: curl -fsSL https://bun.sh/install | bash. Or visit https://bun.sh/docs/installation to install it."
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
# --- Source ------------------------------------------------------------------
|
|
42
|
+
if [ ! -e "$CLCO_DIR" ] && [ -d "$CLCO_DIR.previous/.git" ]; then
|
|
43
|
+
log "Recovering the previous install after an interrupted update"
|
|
44
|
+
mv "$CLCO_DIR.previous" "$CLCO_DIR" \
|
|
45
|
+
|| fail "Could not recover the previous clco install"
|
|
46
|
+
fi
|
|
47
|
+
if [ -d "$CLCO_DIR/.git" ]; then
|
|
48
|
+
log "Updating the existing install: $CLCO_DIR"
|
|
49
|
+
grep -Eq '^[[:space:]]*"name"[[:space:]]*:[[:space:]]*"(@semanticist14/)?clco"[[:space:]]*,?[[:space:]]*$' \
|
|
50
|
+
"$CLCO_DIR/package.json" \
|
|
51
|
+
|| fail "$CLCO_DIR is a git repo, but not a clco install"
|
|
52
|
+
CURRENT_URL="$(git -C "$CLCO_DIR" remote get-url origin 2>/dev/null || true)"
|
|
53
|
+
case "$CURRENT_URL" in
|
|
54
|
+
"$REPO"|git@github.com:semanticist21/clco.git) ;;
|
|
55
|
+
*) fail "$CLCO_DIR does not point to the canonical clco repository" ;;
|
|
56
|
+
esac
|
|
57
|
+
[ -z "$(git -C "$CLCO_DIR" status --porcelain)" ] \
|
|
58
|
+
|| fail "Update aborted - local changes exist in $CLCO_DIR; commit or stash them first"
|
|
59
|
+
CURRENT_HEAD="$(git -C "$CLCO_DIR" rev-parse HEAD)" \
|
|
60
|
+
|| fail "Could not read the current revision in $CLCO_DIR"
|
|
61
|
+
BRANCH="$(git -C "$CLCO_DIR" rev-parse --abbrev-ref HEAD)" \
|
|
62
|
+
|| fail "Could not read the current branch in $CLCO_DIR"
|
|
63
|
+
[ "$BRANCH" != HEAD ] || fail "Update requires a checked-out branch in $CLCO_DIR"
|
|
64
|
+
|
|
65
|
+
UPDATE_LOCK="$CLCO_DIR.update.lock"
|
|
66
|
+
mkdir "$UPDATE_LOCK" \
|
|
67
|
+
|| fail "Another clco update is already running (or left an update lock; remove it after verifying)"
|
|
68
|
+
STAGE_ROOT="$(mktemp -d "$(dirname "$CLCO_DIR")/.clco-install.XXXXXX")" \
|
|
69
|
+
|| fail "Could not create a temporary update directory"
|
|
70
|
+
STAGE="$STAGE_ROOT/app"
|
|
71
|
+
trap 'rm -rf "$STAGE_ROOT" "$UPDATE_LOCK"' EXIT
|
|
72
|
+
git clone --local "$CLCO_DIR" "$STAGE" >/dev/null \
|
|
73
|
+
|| fail "Could not create a staged update checkout"
|
|
74
|
+
git -C "$STAGE" remote set-url origin "$REPO" 2>/dev/null \
|
|
75
|
+
|| git -C "$STAGE" remote add origin "$REPO" \
|
|
76
|
+
|| fail "Could not set the canonical clco origin"
|
|
77
|
+
git -C "$STAGE" fetch --quiet origin "$BRANCH" \
|
|
78
|
+
|| fail "Could not fetch the latest clco revision"
|
|
79
|
+
git -C "$STAGE" merge-base --is-ancestor "$CURRENT_HEAD" "origin/$BRANCH" \
|
|
80
|
+
|| fail "Update is not a fast-forward - resolve the branch manually first"
|
|
81
|
+
git -C "$STAGE" checkout --quiet -B "$BRANCH" "origin/$BRANCH" \
|
|
82
|
+
|| fail "Could not check out the staged clco revision"
|
|
83
|
+
install_dependencies "$STAGE" \
|
|
84
|
+
|| fail "bun install failed - the previous revision is still active"
|
|
85
|
+
(cd "$STAGE" && bun run --no-install src/cli.ts version >/dev/null) \
|
|
86
|
+
|| fail "Updated checkout failed its smoke test - the previous revision is still active"
|
|
87
|
+
|
|
88
|
+
LIVE_HEAD="$(git -C "$CLCO_DIR" rev-parse HEAD)" \
|
|
89
|
+
|| fail "Could not re-check the live revision before activation"
|
|
90
|
+
LIVE_STATUS="$(git -C "$CLCO_DIR" status --porcelain)" \
|
|
91
|
+
|| fail "Could not re-check the live checkout before activation"
|
|
92
|
+
[ "$LIVE_HEAD" = "$CURRENT_HEAD" ] && [ -z "$LIVE_STATUS" ] \
|
|
93
|
+
|| fail "The live checkout changed while it was being updated"
|
|
94
|
+
git -C "$CLCO_DIR" config --replace-all remote.origin.url "$REPO" \
|
|
95
|
+
|| fail "Could not sanitize the clco origin before activation"
|
|
96
|
+
git -C "$CLCO_DIR" config --unset-all remote.origin.pushurl 2>/dev/null || true
|
|
97
|
+
[ -z "$(git -C "$CLCO_DIR" config --get-all remote.origin.pushurl 2>/dev/null || true)" ] \
|
|
98
|
+
|| fail "Could not remove credentials from the clco origin before activation"
|
|
99
|
+
if [ -e "$CLCO_DIR.previous" ]; then
|
|
100
|
+
[ -d "$CLCO_DIR.previous/.git" ] \
|
|
101
|
+
|| fail "$CLCO_DIR.previous is not a clco rollback directory - refusing to replace it"
|
|
102
|
+
rm -rf "$CLCO_DIR.previous"
|
|
103
|
+
fi
|
|
104
|
+
mv "$CLCO_DIR" "$CLCO_DIR.previous" \
|
|
105
|
+
|| fail "Could not prepare the existing install for activation"
|
|
106
|
+
if ! mv "$STAGE" "$CLCO_DIR"; then
|
|
107
|
+
mv "$CLCO_DIR.previous" "$CLCO_DIR" || true
|
|
108
|
+
fail "Could not activate the staged install"
|
|
109
|
+
fi
|
|
110
|
+
trap - EXIT
|
|
111
|
+
rm -rf "$STAGE_ROOT"
|
|
112
|
+
rmdir "$UPDATE_LOCK" 2>/dev/null || true
|
|
113
|
+
NEEDS_INSTALL=0
|
|
114
|
+
else
|
|
115
|
+
[ -e "$CLCO_DIR" ] && fail "$CLCO_DIR already exists and is not a git repo - remove it and retry"
|
|
116
|
+
log "Cloning into $CLCO_DIR"
|
|
117
|
+
git clone --depth 1 "$REPO" "$CLCO_DIR"
|
|
118
|
+
NEEDS_INSTALL=1
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
if [ "$NEEDS_INSTALL" = 1 ]; then
|
|
122
|
+
install_dependencies "$CLCO_DIR" || fail "bun install failed"
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
# --- claude CLI (required by clco; offer to install) -------------------------
|
|
126
|
+
CLAUDE_WARN="Install it later with: curl -fsSL https://claude.ai/install.sh | bash"
|
|
127
|
+
if command -v claude >/dev/null 2>&1; then
|
|
128
|
+
log "Found the claude CLI"
|
|
129
|
+
else
|
|
130
|
+
log "The claude CLI is missing (clco needs it)"
|
|
131
|
+
INSTALL_CLAUDE=n
|
|
132
|
+
if [ -e /dev/tty ]; then
|
|
133
|
+
printf 'Install it now? [y/N] '
|
|
134
|
+
answer=n
|
|
135
|
+
read -r answer < /dev/tty || answer=n
|
|
136
|
+
case "$answer" in y|Y|yes|Yes) INSTALL_CLAUDE=y ;; esac
|
|
137
|
+
fi
|
|
138
|
+
if [ "$INSTALL_CLAUDE" = y ]; then
|
|
139
|
+
log "Installing the claude CLI (official script)"
|
|
140
|
+
curl -fsSL https://claude.ai/install.sh | bash
|
|
141
|
+
export PATH="$HOME/.local/bin:$PATH"
|
|
142
|
+
command -v claude >/dev/null 2>&1 \
|
|
143
|
+
|| log "! Could not verify the claude install - reopen your terminal and check. $CLAUDE_WARN"
|
|
144
|
+
else
|
|
145
|
+
log "! Skipped - clco needs the claude CLI before it can run. $CLAUDE_WARN"
|
|
146
|
+
fi
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# --- Launcher ----------------------------------------------------------------
|
|
150
|
+
# Generated by a script the repo owns, so `clco update` refreshes it too.
|
|
151
|
+
bash "$CLCO_DIR/scripts/write-launcher.sh" "$CLCO_DIR" "$BIN_DIR"
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
case ":$PATH:" in
|
|
156
|
+
*":$BIN_DIR:"*) ;;
|
|
157
|
+
*) printf '\033[1;33mNote:\033[0m %s is not on your PATH. Add it to ~/.zshrc or similar:\n export PATH="%s:$PATH"\n' "$BIN_DIR" "$BIN_DIR" ;;
|
|
158
|
+
esac
|
|
159
|
+
|
|
160
|
+
log "Installed: $BIN_DIR/clco"
|
|
161
|
+
printf '\nGetting started:\n clco first run: GitHub device login, pick a model, launch claude\n clco setup set your startup defaults\n clco --help full usage\n\nUninstall:\n curl -fsSL https://raw.githubusercontent.com/semanticist21/clco/main/uninstall.sh | bash\n'
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@semanticist14/clco",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"description": "Run Claude Code on a GitHub Copilot subscription",
|
|
7
|
+
"bin": {
|
|
8
|
+
"clco": "bin/clco"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"bin",
|
|
15
|
+
"src",
|
|
16
|
+
"scripts",
|
|
17
|
+
"install.sh",
|
|
18
|
+
"uninstall.sh",
|
|
19
|
+
"README.md",
|
|
20
|
+
"LICENSE",
|
|
21
|
+
"bun.lock"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"start": "bun run src/cli.ts",
|
|
25
|
+
"test": "bun test",
|
|
26
|
+
"check": "tsc --noEmit"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/bun": "latest",
|
|
30
|
+
"typescript": "^5"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@clack/prompts": "^1.8.0"
|
|
34
|
+
}
|
|
35
|
+
}
|