claude-discover 1.9.2 → 1.9.5
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/bin/claude/statusline.mjs +3 -9
- package/bin/proxy.mjs +38 -51
- package/out/main/main.js +783 -529
- package/out/preload/preload.cjs +2 -2
- package/out/renderer/assets/{index-BugPzqD2.js → index-CX9ZlZGo.js} +162 -103
- package/out/renderer/assets/{index-03LlBvFr.css → index-DZ1Gj6hz.css} +35 -54
- package/out/renderer/assets/{vendors-S1fS5xz9.js → vendors-JM3FOKMR.js} +3173 -1199
- package/out/renderer/index.html +3 -3
- package/package.json +10 -10
- package/bin/claude/hooks.mjs +0 -102
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
// Installed as the `statusLine` command from the app: StatusBar → Status line → Activate
|
|
6
|
-
// (see src/main/services/switchers/StatuslineSwitch.js).
|
|
7
|
-
//
|
|
8
|
-
// Example output:
|
|
9
|
-
// [Opus 4.8] Context: ▓▓▓░░░░░░░ 32% used (45.2k, 87% cached) | Tokens: 1.2M total (+45.2k, 3 turns) | Usage limit: 42% used (resets in 2h 30m) | Weekly limit: 18% used (resets in 3d 4h)
|
|
10
|
-
//
|
|
2
|
+
// Claude Code status line, installed by StatuslineSwitch. Reads the hook JSON from stdin and
|
|
3
|
+
// prints one colored line — model, context usage, token totals, rate-limit windows:
|
|
4
|
+
// [Opus 4.8] Context: ▓▓▓░░░░░░░ 32% used (45.2k, 87% cached) | Tokens: 1.2M total (+45.2k, 3 turns) | Usage limit: 42% used (resets in 2h 30m)
|
|
11
5
|
|
|
12
6
|
import { readFileSync } from 'node:fs'
|
|
13
7
|
import { styleText } from 'node:util'
|
package/bin/proxy.mjs
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// Local logging proxy for Claude Code's API traffic — captures what transcripts never record
|
|
2
2
|
// (system prompt, tool definitions, injected reminders, request params) plus the raw responses.
|
|
3
|
-
//
|
|
4
|
-
// Activate button); started manually with `npm run proxy`. Capture must never fail or delay a request — errors go
|
|
5
|
-
// to ~/.claude-discover/proxy.error.log instead of the client.
|
|
3
|
+
// Capture must never fail or delay a request: errors go to ~/.claude-discover/proxy.error.log.
|
|
6
4
|
//
|
|
7
|
-
// Usage: node bin/proxy.mjs [--restart] —
|
|
5
|
+
// Usage: node bin/proxy.mjs [--restart] — config in proxy.config.js. ProxySwitch runs it as a
|
|
6
|
+
// login service (LoginService) and points Claude Code here via env.ANTHROPIC_BASE_URL.
|
|
8
7
|
|
|
9
8
|
import fs from 'node:fs'
|
|
10
9
|
import path from 'node:path'
|
|
11
10
|
import http from 'node:http'
|
|
12
|
-
import { spawn } from 'node:child_process'
|
|
13
11
|
import https from 'node:https'
|
|
14
12
|
import zlib from 'node:zlib'
|
|
15
13
|
import crypto from 'node:crypto'
|
|
@@ -56,34 +54,42 @@ function createProxy({ upstream, onExchange, onError, errorBody }) {
|
|
|
56
54
|
delete headers['accept-encoding']
|
|
57
55
|
}
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
57
|
+
let up, resent = false
|
|
58
|
+
const send = () => {
|
|
59
|
+
up = mod.request(new URL(req.url, upstream), { method: req.method, headers }, upRes => {
|
|
60
|
+
const resHeaders = { ...upRes.headers }
|
|
61
|
+
delete resHeaders['transfer-encoding'] // node re-frames the piped body itself
|
|
62
|
+
delete resHeaders.connection
|
|
63
|
+
res.writeHead(upRes.statusCode, resHeaders)
|
|
64
|
+
const chunks = []
|
|
65
|
+
upRes.on('data', c => chunks.push(c))
|
|
66
|
+
upRes.pipe(res)
|
|
67
|
+
// 'close' fires after 'end' and also on an aborted stream — a truncated body is still captured
|
|
68
|
+
upRes.on('close', () => {
|
|
69
|
+
finish(upRes.statusCode, upRes.headers, decode(upRes.headers['content-encoding'], Buffer.concat(chunks)))
|
|
70
|
+
if (!upRes.complete) res.destroy() // upstream died mid-body — surface the reset so the client can retry
|
|
71
|
+
})
|
|
71
72
|
})
|
|
72
|
-
|
|
73
|
-
|
|
73
|
+
const cur = up
|
|
74
|
+
cur.on('error', err => {
|
|
75
|
+
if (done) return // our own up.destroy() below surfaces here as a 'socket hang up' — the client left, upstream is fine
|
|
76
|
+
// A pooled keep-alive socket the server closed just before we picked it: nothing reached upstream, so re-sending once is safe (Node's documented reusedSocket case)
|
|
77
|
+
if (cur.reusedSocket && err.code === 'ECONNRESET' && !resent) { resent = true; return send() }
|
|
78
|
+
onError(err, `${req.method} ${req.url} → ${upstream.host}`)
|
|
79
|
+
finish()
|
|
80
|
+
if (res.headersSent) return res.destroy() // mid-stream failure — never append an error body to a partially-piped response
|
|
81
|
+
res.writeHead(502, { 'content-type': 'application/json' })
|
|
82
|
+
res.end(errorBody(err))
|
|
83
|
+
})
|
|
84
|
+
cur.end(requestBody)
|
|
85
|
+
}
|
|
86
|
+
// Client gone mid-exchange — tear down upstream too; headersSent means upstream responded and its 'close' captures the partial body, otherwise log the attempt here
|
|
74
87
|
res.on('close', () => {
|
|
75
88
|
if (done) return
|
|
76
|
-
up.destroy()
|
|
77
89
|
if (!res.headersSent) finish()
|
|
90
|
+
up.destroy()
|
|
78
91
|
})
|
|
79
|
-
|
|
80
|
-
onError(err, `${req.method} ${req.url} → ${upstream.host}`)
|
|
81
|
-
finish()
|
|
82
|
-
if (res.headersSent) return res.destroy() // mid-stream failure — never append an error body to a partially-piped response
|
|
83
|
-
res.writeHead(502, { 'content-type': 'application/json' })
|
|
84
|
-
res.end(errorBody(err))
|
|
85
|
-
})
|
|
86
|
-
up.end(requestBody)
|
|
92
|
+
send()
|
|
87
93
|
})
|
|
88
94
|
}
|
|
89
95
|
|
|
@@ -228,35 +234,13 @@ function logError(err, context) {
|
|
|
228
234
|
}
|
|
229
235
|
|
|
230
236
|
const isMain = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href
|
|
231
|
-
if (
|
|
232
|
-
// Forked as an Electron utility process (ProxySwitch): Chromium gives those a clean handle
|
|
233
|
-
// table, unlike the app's main process whose children inherit the dev CDP server's socket on
|
|
234
|
-
// Windows and keep its port bound after the app quits. Utility processes die with the app,
|
|
235
|
-
// so re-spawn detached from here (as node — no system `node` required) and relay an early
|
|
236
|
-
// exit code back as a message until the app kills this relay.
|
|
237
|
-
const child = spawn(process.execPath, process.argv.slice(1), {
|
|
238
|
-
detached: true, stdio: 'ignore', windowsHide: true,
|
|
239
|
-
env: { ...process.env, ELECTRON_RUN_AS_NODE: '1' },
|
|
240
|
-
})
|
|
241
|
-
child.once('exit', code => { process.parentPort.postMessage(code); process.exit(0) })
|
|
242
|
-
}
|
|
243
|
-
else if (isMain) {
|
|
237
|
+
if (isMain) {
|
|
244
238
|
const args = parseArgs({
|
|
245
239
|
options: {
|
|
246
240
|
restart: { type: 'boolean' }, // replace an already-running instance instead of exiting
|
|
247
241
|
}
|
|
248
242
|
})
|
|
249
243
|
const upstream = new URL(UPSTREAM)
|
|
250
|
-
// Fail fast when the upstream is unreachable — a clear exit beats a proxy that 502s every request.
|
|
251
|
-
// Runs before --restart so a working instance is never replaced by a broken one. Exit code 2 is
|
|
252
|
-
// recognized by the app's ProxySwitch ("cannot reach upstream").
|
|
253
|
-
try {
|
|
254
|
-
await fetch(new URL('/v1/models', upstream), { signal: AbortSignal.timeout(5000) })
|
|
255
|
-
} catch (err) {
|
|
256
|
-
console.error(`Cannot reach ${upstream.origin} (${err?.cause?.code || err?.cause?.message || err?.name || err})`)
|
|
257
|
-
logError(err, `startup check → ${upstream.origin}`)
|
|
258
|
-
process.exit(2)
|
|
259
|
-
}
|
|
260
244
|
const server = createProxy({
|
|
261
245
|
upstream: upstream,
|
|
262
246
|
onExchange: logRequest,
|
|
@@ -277,5 +261,8 @@ else if (isMain) {
|
|
|
277
261
|
logError(err)
|
|
278
262
|
process.exit(1)
|
|
279
263
|
})
|
|
264
|
+
// Never die on a stray exception — log it and keep serving
|
|
265
|
+
process.on('uncaughtException', err => logError(err, 'uncaught exception'))
|
|
266
|
+
process.on('unhandledRejection', err => logError(err, 'unhandled rejection'))
|
|
280
267
|
server.listen(PORT, HOST, () => console.log(`Capture proxy listening on ${PROXY_URL} → ${upstream.origin}\nLogging requests to ${REQUESTS_DIR}`))
|
|
281
268
|
}
|