thinkpool-pair 0.7.25 → 0.7.26
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/README.md +32 -0
- package/bridge.mjs +20 -3
- package/package.json +1 -1
- package/provider.mjs +33 -1
package/README.md
CHANGED
|
@@ -98,5 +98,37 @@ npx thinkpool-pair@latest <ROOM> -- claude # structured Claude, no TTY
|
|
|
98
98
|
- `resize` — web viewport size → headless PTYs only (the attached terminal
|
|
99
99
|
follows your own TTY).
|
|
100
100
|
|
|
101
|
+
## Alternative LLM Providers
|
|
102
|
+
|
|
103
|
+
The bridge supports using alternative LLM providers that are compatible with the Anthropic API:
|
|
104
|
+
|
|
105
|
+
### OpenRouter
|
|
106
|
+
To use OpenRouter instead of Anthropic directly:
|
|
107
|
+
```bash
|
|
108
|
+
npx thinkpool-pair provider openrouter --token sk-or-... [--model anthropic/claude-3.5-sonnet]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
This will configure the bridge to use OpenRouter's API endpoint with your provided token. You can optionally specify a model.
|
|
112
|
+
|
|
113
|
+
### GLM (Z.ai)
|
|
114
|
+
To use GLM via Z.ai's Anthropic-compatible endpoint:
|
|
115
|
+
```bash
|
|
116
|
+
npx thinkpool-pair provider glm --token zai_...
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### Custom Provider
|
|
120
|
+
For any other Anthropic-compatible endpoint:
|
|
121
|
+
```bash
|
|
122
|
+
npx thinkpool-pair provider custom --base https://your-endpoint.com --token your-token [--model model-name]
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### Reset to Default
|
|
126
|
+
To reset back to using Anthropic directly:
|
|
127
|
+
```bash
|
|
128
|
+
npx thinkpool-pair provider anthropic
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Provider configurations are stored in `~/.thinkpool-pair/provider.json` and apply to all bridges on the machine.
|
|
132
|
+
|
|
101
133
|
Public anon creds are embedded (the same ones the web app ships). Override with
|
|
102
134
|
`TP_SUPABASE_URL` / `TP_SUPABASE_ANON` if needed. Set `TP_NAME` to label yourself.
|
package/bridge.mjs
CHANGED
|
@@ -597,9 +597,16 @@ function openStructured({ id, model, resume, log, commands, mode }) {
|
|
|
597
597
|
// on the entry so the ANNOUNCE can hand it to clients that connect/reload
|
|
598
598
|
// AFTER init (the one-time code-event would miss them), then re-announce.
|
|
599
599
|
if (evt.kind === 'system' && Array.isArray(evt.commands) && evt.commands.length && !entry.commands) { entry.commands = evt.commands; announce(); persist() }
|
|
600
|
-
// Chrome events (mode / usage / clear) are transient state, not
|
|
601
|
-
// broadcast + print them
|
|
602
|
-
const chrome = evt.kind === 'mode' || evt.kind === 'usage' || evt.kind === 'clear'
|
|
600
|
+
// Chrome events (mode / usage / clear / compact) are transient state, not
|
|
601
|
+
// transcript — broadcast + print them but keep out of the persisted/replayed log.
|
|
602
|
+
const chrome = evt.kind === 'mode' || evt.kind === 'usage' || evt.kind === 'clear' || evt.kind === 'compact'
|
|
603
|
+
// compact turn finished (or errored) → clear the pulsing indicator.
|
|
604
|
+
// No "done" ctl line — the SDK's own output in the transcript is the
|
|
605
|
+
// signal (compact summary on success, AbortError text on abort/too-small).
|
|
606
|
+
if ((evt.kind === 'result' || evt.kind === 'error') && entry.compacting) {
|
|
607
|
+
entry.compacting = false
|
|
608
|
+
bcast('code-event', { term: id, evt: { kind: 'compact', status: 'done', ts: Date.now() } })
|
|
609
|
+
}
|
|
603
610
|
if (!chrome) {
|
|
604
611
|
entry.log.push(evt)
|
|
605
612
|
if (entry.log.length > STRUCTURED_LOG_MAX) entry.log.shift()
|
|
@@ -814,6 +821,16 @@ channel
|
|
|
814
821
|
ctlLine('context cleared. You can continue with these answers in mind.')
|
|
815
822
|
return
|
|
816
823
|
}
|
|
824
|
+
// /compact → announce immediately, track so onEvent can emit a done signal when
|
|
825
|
+
// the SDK turn finishes. No echoYou — the ctl line IS the announcement.
|
|
826
|
+
if (/^\/compact\b/.test(text)) {
|
|
827
|
+
ctlLine('◈ compacting context…')
|
|
828
|
+
s.compacting = true
|
|
829
|
+
s.compactBy = payload.by
|
|
830
|
+
bcast('code-event', { term: payload.term, evt: { kind: 'compact', status: 'start', ts: Date.now() } })
|
|
831
|
+
s.session.sendTurn(text)
|
|
832
|
+
return
|
|
833
|
+
}
|
|
817
834
|
s.session.sendTurn(text)
|
|
818
835
|
echoYou()
|
|
819
836
|
})
|
package/package.json
CHANGED
package/provider.mjs
CHANGED
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
CLI (dispatched from bridge.mjs):
|
|
20
20
|
npx thinkpool-pair provider # show current
|
|
21
21
|
npx thinkpool-pair provider glm --token <zai_…> # use GLM (Z.ai)
|
|
22
|
+
npx thinkpool-pair provider openrouter --token <sk-or-…> [--model <model>]
|
|
22
23
|
npx thinkpool-pair provider custom --base <url> --token <key>
|
|
23
24
|
npx thinkpool-pair provider anthropic # reset to Claude
|
|
24
25
|
───────────────────────────────────────────────────────────── */
|
|
@@ -33,6 +34,9 @@ const FILE = path.join(DIR, 'provider.json')
|
|
|
33
34
|
// into source); --base / --model override these if the endpoint ever moves.
|
|
34
35
|
const GLM = { baseUrl: 'https://api.z.ai/api/anthropic', model: 'glm-5.2' }
|
|
35
36
|
|
|
37
|
+
// The pre-baked OpenRouter preset. --token is always required.
|
|
38
|
+
const OPENROUTER = { baseUrl: 'https://openrouter.ai/api/v1', model: 'anthropic/claude-3.5-sonnet' }
|
|
39
|
+
|
|
36
40
|
function ensureDir() { try { fs.mkdirSync(DIR, { recursive: true }) } catch { /* noop */ } }
|
|
37
41
|
function opt(args, flag) { const i = args.indexOf(flag); return i >= 0 ? args[i + 1] : undefined }
|
|
38
42
|
|
|
@@ -58,6 +62,23 @@ export function applyProviderEnv() {
|
|
|
58
62
|
const provider = (process.env.TP_PROVIDER || cfg.provider || 'anthropic').toLowerCase()
|
|
59
63
|
if (provider === 'anthropic' || !provider) return { provider: 'anthropic', source: 'default' }
|
|
60
64
|
|
|
65
|
+
// Handle OpenRouter specifically
|
|
66
|
+
if (provider === 'openrouter') {
|
|
67
|
+
const baseUrl = process.env.TP_ANTHROPIC_BASE_URL || cfg.baseUrl || OPENROUTER.baseUrl
|
|
68
|
+
const authToken = process.env.TP_ANTHROPIC_AUTH_TOKEN || cfg.authToken
|
|
69
|
+
const model = process.env.TP_ANTHROPIC_MODEL || cfg.model || OPENROUTER.model
|
|
70
|
+
|
|
71
|
+
if (!authToken) {
|
|
72
|
+
process.stderr.write(`\n ◇ provider "${provider}" is set but is missing authToken.\n Finish setup: npx thinkpool-pair provider ${provider} --token <key>\n Falling back to regular Claude for now.\n`)
|
|
73
|
+
return { provider: 'anthropic', source: 'incomplete' }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
process.env.ANTHROPIC_BASE_URL = baseUrl
|
|
77
|
+
process.env.ANTHROPIC_AUTH_TOKEN = authToken
|
|
78
|
+
if (model) process.env.ANTHROPIC_MODEL = model
|
|
79
|
+
return { provider, baseUrl, model, source: cfg.provider ? 'file' : 'env' }
|
|
80
|
+
}
|
|
81
|
+
|
|
61
82
|
const baseUrl = process.env.TP_ANTHROPIC_BASE_URL || cfg.baseUrl
|
|
62
83
|
const authToken = process.env.TP_ANTHROPIC_AUTH_TOKEN || cfg.authToken
|
|
63
84
|
const model = process.env.TP_ANTHROPIC_MODEL || cfg.model
|
|
@@ -104,6 +125,17 @@ export async function runProvider(args) {
|
|
|
104
125
|
return
|
|
105
126
|
}
|
|
106
127
|
|
|
128
|
+
// ── OpenRouter preset ──
|
|
129
|
+
if (sub === 'openrouter') {
|
|
130
|
+
const token = opt(args, '--token') || opt(args, '--key')
|
|
131
|
+
const baseUrl = opt(args, '--base') || opt(args, '--base-url') || OPENROUTER.baseUrl
|
|
132
|
+
const model = opt(args, '--model') || OPENROUTER.model
|
|
133
|
+
if (!token) { console.error('\n ✗ openrouter needs --token <sk-or-…>. Get one at https://openrouter.ai/\n'); process.exit(1) }
|
|
134
|
+
saveProvider({ provider: 'openrouter', baseUrl, authToken: token, model, savedAt: Date.now() })
|
|
135
|
+
console.error(`\n ✓ provider set to openrouter.\n base url: ${baseUrl}\n model: ${model}\n token: ${token.slice(0, 8)}…\n Restart the bridge (or its launchd service) to apply.\n`)
|
|
136
|
+
return
|
|
137
|
+
}
|
|
138
|
+
|
|
107
139
|
// ── arbitrary Anthropic-compatible endpoint ──
|
|
108
140
|
if (sub === 'custom') {
|
|
109
141
|
const token = opt(args, '--token') || opt(args, '--key')
|
|
@@ -115,6 +147,6 @@ export async function runProvider(args) {
|
|
|
115
147
|
return
|
|
116
148
|
}
|
|
117
149
|
|
|
118
|
-
console.error('\n usage:\n npx thinkpool-pair provider # show current\n npx thinkpool-pair provider glm --token <zai_…> [--model glm-5.2]\n npx thinkpool-pair provider custom --base <url> --token <key> [--model <m>]\n npx thinkpool-pair provider anthropic # reset to Claude\n')
|
|
150
|
+
console.error('\n usage:\n npx thinkpool-pair provider # show current\n npx thinkpool-pair provider glm --token <zai_…> [--model glm-5.2]\n npx thinkpool-pair provider openrouter --token <sk-or-…> [--model <model>]\n npx thinkpool-pair provider custom --base <url> --token <key> [--model <m>]\n npx thinkpool-pair provider anthropic # reset to Claude\n')
|
|
119
151
|
process.exit(1)
|
|
120
152
|
}
|