claude-discover 1.10.3 → 1.10.4
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/proxy.mjs
CHANGED
|
@@ -16,20 +16,25 @@ import { parseArgs } from 'node:util'
|
|
|
16
16
|
|
|
17
17
|
import { HOST, PORT, PROXY_URL, UPSTREAM, EXIT_ROUTE, PING_ROUTE, PING_RESPONSE, REQUESTS_DIR, ERROR_LOG_PATH } from './proxy.config.js'
|
|
18
18
|
|
|
19
|
+
// How long an exiting proxy keeps serving in-flight responses — long replies stream for minutes
|
|
20
|
+
const DRAIN_TIMEOUT_MS = 10 * 60_000
|
|
21
|
+
|
|
19
22
|
|
|
20
23
|
// ── 1. Generic tee proxy — knows nothing about Claude ────────────────────────
|
|
21
24
|
// Forwards verbatim to `upstream` and pipes the response back while buffering both bodies.
|
|
22
25
|
// `onExchange(exchange)` fires exactly once per request, after piping (its response fields are
|
|
23
|
-
// undefined when upstream never responded); `errorBody(err)` shapes the 502 when it's unreachable
|
|
26
|
+
// undefined when upstream never responded); `errorBody(err)` shapes the 502 when it's unreachable;
|
|
27
|
+
// `onExit()` runs once the exit route has answered.
|
|
24
28
|
|
|
25
|
-
function createProxy({ upstream, onExchange, onError, errorBody }) {
|
|
29
|
+
function createProxy({ upstream, onExchange, onError, errorBody, onExit }) {
|
|
26
30
|
const mod = upstream.protocol === 'http:' ? http : https
|
|
27
31
|
|
|
28
32
|
return http.createServer(async (req, res) => {
|
|
29
33
|
if (req.method === 'POST' && req.url === EXIT_ROUTE) {
|
|
30
34
|
// Loopback-only control route. The restart client is a bare Node request; reject anything carrying browser fetch metadata so a web page can't CSRF the proxy into exiting.
|
|
31
35
|
if (req.headers.origin || req.headers.referer || req.headers['sec-fetch-site']) return res.writeHead(403).end('forbidden')
|
|
32
|
-
|
|
36
|
+
res.setHeader('connection', 'close')
|
|
37
|
+
return res.end('bye', onExit)
|
|
33
38
|
}
|
|
34
39
|
if (req.url === PING_ROUTE) return res.end(PING_RESPONSE)
|
|
35
40
|
const started = Date.now()
|
|
@@ -246,7 +251,17 @@ if (isMain) {
|
|
|
246
251
|
onExchange: logRequest,
|
|
247
252
|
onError: logError,
|
|
248
253
|
errorBody: err => JSON.stringify({ type: 'error', error: { type: 'api_error', message: `claude-discover proxy: upstream unreachable (${err.code || err.message})` } }),
|
|
254
|
+
onExit: () => drain(),
|
|
249
255
|
})
|
|
256
|
+
// Stop listening at once (frees the port for a successor) but let in-flight responses finish — exiting outright would cut a reply mid-stream
|
|
257
|
+
let draining = false
|
|
258
|
+
const drain = () => {
|
|
259
|
+
if (draining) return
|
|
260
|
+
draining = true
|
|
261
|
+
server.close(() => process.exit(0))
|
|
262
|
+
setTimeout(() => process.exit(0), DRAIN_TIMEOUT_MS)
|
|
263
|
+
}
|
|
264
|
+
process.on('SIGTERM', drain) // @macOS @linux launchd/systemd stop the service this way, right after ProxySwitch's exit request
|
|
250
265
|
if (args.values.restart) {
|
|
251
266
|
// Ask a previous instance (the port is loopback-only and assumed ours) to exit before listening.
|
|
252
267
|
const replaced = await new Promise(resolve => http.request(`${PROXY_URL}${EXIT_ROUTE}`, { method: 'POST' }, () => resolve(true)).on('error', () => resolve(false)).end())
|
package/out/main/main.js
CHANGED
|
@@ -435,7 +435,7 @@ const MEMORY_LABELS = {
|
|
|
435
435
|
"user's private project instructions, not checked in": "Local",
|
|
436
436
|
"user's auto-memory, persists across conversations": "Auto"
|
|
437
437
|
};
|
|
438
|
-
const MEMORY_FILES = { User: "CLAUDE.md", Project: "CLAUDE.md", Local: "CLAUDE.local.md", Auto: "MEMORY.md" };
|
|
438
|
+
const MEMORY_FILES = { User: ["CLAUDE.md"], Project: ["CLAUDE.md", "AGENTS.md"], Local: ["CLAUDE.local.md"], Auto: ["MEMORY.md"] };
|
|
439
439
|
function splitClaudeMd(text) {
|
|
440
440
|
const marker = text.match(/\n# claudeMd\r?\n|Codebase and user instructions are shown below\./);
|
|
441
441
|
if (!marker) return { files: [], rest: text };
|
|
@@ -449,7 +449,7 @@ function splitClaudeMd(text) {
|
|
|
449
449
|
const base = h[1].split(/[\\/]/).pop();
|
|
450
450
|
return {
|
|
451
451
|
file_path: h[1],
|
|
452
|
-
name:
|
|
452
|
+
name: MEMORY_FILES[memory_type]?.includes(base) ? base : h[1],
|
|
453
453
|
memory_type,
|
|
454
454
|
content: region.slice(h.index + h[0].length, heads[i + 1]?.index ?? region.length).trim()
|
|
455
455
|
};
|
|
@@ -1608,21 +1608,6 @@ class ClaudeSettings {
|
|
|
1608
1608
|
}
|
|
1609
1609
|
}
|
|
1610
1610
|
}
|
|
1611
|
-
// Removes every hook (any event) whose command matches `pattern`, dropping groups and events
|
|
1612
|
-
// left empty. Returns the removed commands.
|
|
1613
|
-
removeHooks(pattern) {
|
|
1614
|
-
const matches = (h) => pattern.test(h.command ?? "");
|
|
1615
|
-
const removed = [];
|
|
1616
|
-
for (const [event, groups] of Object.entries(this.cfg.hooks ?? {})) {
|
|
1617
|
-
const hit = groups.flatMap((g) => (g.hooks ?? []).filter(matches).map((h) => h.command));
|
|
1618
|
-
if (!hit.length) continue;
|
|
1619
|
-
removed.push(...hit);
|
|
1620
|
-
for (const g of groups) g.hooks = (g.hooks ?? []).filter((h) => !matches(h));
|
|
1621
|
-
this.cfg.hooks[event] = groups.filter((g) => g.hooks.length);
|
|
1622
|
-
if (!this.cfg.hooks[event].length) delete this.cfg.hooks[event];
|
|
1623
|
-
}
|
|
1624
|
-
return removed;
|
|
1625
|
-
}
|
|
1626
1611
|
// Days Claude Code keeps transcripts before auto-deleting them; undefined when unset (its default is 30).
|
|
1627
1612
|
get cleanupPeriodDays() {
|
|
1628
1613
|
return this.cfg.cleanupPeriodDays;
|
|
@@ -1790,15 +1775,9 @@ WantedBy=default.target
|
|
|
1790
1775
|
}
|
|
1791
1776
|
}
|
|
1792
1777
|
const NODE = await which("node", { nothrow: true }) ?? "node";
|
|
1778
|
+
const SETTINGS_RELOAD_GRACE_MS = 3e3;
|
|
1793
1779
|
class ProxySwitch {
|
|
1794
1780
|
#service = new LoginService({ name: "claude-discover-proxy", command: [NODE, PROXY_PATH] });
|
|
1795
|
-
constructor() {
|
|
1796
|
-
try {
|
|
1797
|
-
const settings = new ClaudeSettings();
|
|
1798
|
-
if (settings.removeHooks(/claude-discover/).length) settings.save();
|
|
1799
|
-
} catch {
|
|
1800
|
-
}
|
|
1801
|
-
}
|
|
1802
1781
|
async status() {
|
|
1803
1782
|
return {
|
|
1804
1783
|
running: await this.#running(),
|
|
@@ -1828,16 +1807,17 @@ class ProxySwitch {
|
|
|
1828
1807
|
settings.setEnv("ENABLE_TOOL_SEARCH", "true");
|
|
1829
1808
|
settings.save();
|
|
1830
1809
|
}
|
|
1831
|
-
//
|
|
1810
|
+
// Remove our env keys, then stop the proxy and remove the service — a foreign base URL is left alone
|
|
1832
1811
|
async deactivate() {
|
|
1833
|
-
await this.#exit();
|
|
1834
|
-
await this.#service.uninstall();
|
|
1835
1812
|
const settings = new ClaudeSettings();
|
|
1836
1813
|
if (settings.env?.ANTHROPIC_BASE_URL === PROXY_URL) {
|
|
1837
|
-
settings.
|
|
1814
|
+
settings.setEnv("ANTHROPIC_BASE_URL", "");
|
|
1838
1815
|
settings.deleteEnv("ENABLE_TOOL_SEARCH");
|
|
1839
1816
|
settings.save();
|
|
1817
|
+
await new Promise((r) => setTimeout(r, SETTINGS_RELOAD_GRACE_MS));
|
|
1840
1818
|
}
|
|
1819
|
+
await this.#exit();
|
|
1820
|
+
await this.#service.uninstall();
|
|
1841
1821
|
}
|
|
1842
1822
|
// Ask a running instance to exit via its control route and wait for the port to free up
|
|
1843
1823
|
async #exit() {
|
|
@@ -34,13 +34,13 @@ function useLocalStorage(key, initial) {
|
|
|
34
34
|
}
|
|
35
35
|
function clearOutdatedLocalStorage() {
|
|
36
36
|
const version = localStorage.getItem(VERSION_KEY);
|
|
37
|
-
if (version !== "1.10.
|
|
38
|
-
const [MAJOR, MINOR, PATCH] = "1.10.
|
|
37
|
+
if (version !== "1.10.4") {
|
|
38
|
+
const [MAJOR, MINOR, PATCH] = "1.10.4".split(".");
|
|
39
39
|
const [major, minor, patch] = (version ?? "").split(".");
|
|
40
40
|
if (MAJOR !== major || MINOR !== minor) {
|
|
41
41
|
localStorage.clear();
|
|
42
42
|
}
|
|
43
|
-
localStorage.setItem(VERSION_KEY, "1.10.
|
|
43
|
+
localStorage.setItem(VERSION_KEY, "1.10.4");
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
function useMouseFontScale(storageKey) {
|
|
@@ -1550,6 +1550,7 @@ function ConversationView({ items, instructions = [], filePath = null, expandAll
|
|
|
1550
1550
|
"Continues from ",
|
|
1551
1551
|
format(continuesFrom, "MMM d, yyyy HH:mm")
|
|
1552
1552
|
] }),
|
|
1553
|
+
instructions.length === 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "conv-row conv-row-instruction", children: /* @__PURE__ */ jsxRuntimeExports.jsx(InstructionsMissing, {}) }),
|
|
1553
1554
|
groups.map((g, i) => /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
|
|
1554
1555
|
i === branch?.index && branchDivider,
|
|
1555
1556
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: `conv-row conv-row-${g.kind}`, children: [
|
|
@@ -1857,13 +1858,21 @@ function safeJson(x) {
|
|
|
1857
1858
|
function JsonBlock({ value }) {
|
|
1858
1859
|
return /* @__PURE__ */ jsxRuntimeExports.jsx(Markdown, { className: "block-text", text: "```json\n" + safeJson(value) + "\n```" });
|
|
1859
1860
|
}
|
|
1861
|
+
const ProxyHeader = ({ children }) => /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "msg-header msg-proxy", children: [
|
|
1862
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "msg-icon" }),
|
|
1863
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-author", children: "Proxy" }),
|
|
1864
|
+
children
|
|
1865
|
+
] });
|
|
1866
|
+
function InstructionsMissing() {
|
|
1867
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "instruction-missing", children: [
|
|
1868
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ProxyHeader, { children: /* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-summary", children: "not captured" }) }),
|
|
1869
|
+
"Turn on the Capture proxy to see the system prompt, tools and memory files."
|
|
1870
|
+
] });
|
|
1871
|
+
}
|
|
1860
1872
|
function InstructionRun({ turns, model }) {
|
|
1861
1873
|
const strips = turns.flatMap((t) => t.blocks).map(({ it }) => ({ ...it, tokens: countTokens(it.content, it.model ?? model) }));
|
|
1862
1874
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
1863
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
1864
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "msg-icon" }),
|
|
1865
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "msg-author", children: "Proxy" })
|
|
1866
|
-
] }),
|
|
1875
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(ProxyHeader, {}),
|
|
1867
1876
|
groupInstructions(strips).map(([label, list]) => /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
|
|
1868
1877
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "aux instruction-source", children: label }),
|
|
1869
1878
|
list.map((it) => /* @__PURE__ */ jsxRuntimeExports.jsx(InstructionFile, { it, model, tokens: it.tokens }, `${it.hash ?? it.file_path}-${it.timestamp}`))
|
|
@@ -2112,7 +2121,7 @@ const Pane = React.memo(function Pane2({ value, headers, seen, expandAll }) {
|
|
|
2112
2121
|
] })
|
|
2113
2122
|
] });
|
|
2114
2123
|
});
|
|
2115
|
-
function RequestsView({ filePath, date, granularity = "day", fileSize = 0, expandAll = null }) {
|
|
2124
|
+
function RequestsView({ filePath, items, date, granularity = "day", fileSize = 0, expandAll = null }) {
|
|
2116
2125
|
const [records, setRecords] = reactExports.useState(null);
|
|
2117
2126
|
const [selected, setSelected] = reactExports.useState(0);
|
|
2118
2127
|
const [tab, setTab] = reactExports.useState("request");
|
|
@@ -2143,21 +2152,49 @@ function RequestsView({ filePath, date, granularity = "day", fileSize = 0, expan
|
|
|
2143
2152
|
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "requests-view", children: error ? /* @__PURE__ */ jsxRuntimeExports.jsx(ErrorMessage, { title: "Couldn't load the captured requests", error }) : /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "requests-empty", children: "Loading…" }) });
|
|
2144
2153
|
}
|
|
2145
2154
|
if (!records.length) {
|
|
2146
|
-
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "requests-view", children: /* @__PURE__ */ jsxRuntimeExports.
|
|
2155
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "requests-view", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "requests-pitch", children: [
|
|
2156
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("h3", { children: [
|
|
2157
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx(Globe, { className: "requests-pitch-icon" }),
|
|
2158
|
+
"No API requests captured"
|
|
2159
|
+
] }),
|
|
2160
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "See the system prompt, tools and hidden calls behind every reply." }),
|
|
2161
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("p", { className: "requests-pitch-hint", children: [
|
|
2162
|
+
"Turn on ",
|
|
2163
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("b", { children: "Capture proxy" }),
|
|
2164
|
+
" in the status bar."
|
|
2165
|
+
] })
|
|
2166
|
+
] }) });
|
|
2147
2167
|
}
|
|
2168
|
+
const captured = new Set(records.map((r) => r.responseHeaders?.["request-id"]));
|
|
2169
|
+
const missed = items?.filter((o) => o.requestId && !captured.has(o.requestId)) ?? [];
|
|
2170
|
+
const gapRow = (from = "", to) => {
|
|
2171
|
+
const n = new Set(missed.filter((o) => o.timestamp >= from && (!to || o.timestamp < to)).map((o) => o.requestId)).size;
|
|
2172
|
+
return n > 0 && /* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: "requests-missed", children: /* @__PURE__ */ jsxRuntimeExports.jsxs(Divider, { children: [
|
|
2173
|
+
n,
|
|
2174
|
+
" ",
|
|
2175
|
+
n === 1 ? "reply" : "replies",
|
|
2176
|
+
" not captured (proxy was off)"
|
|
2177
|
+
] }) });
|
|
2178
|
+
};
|
|
2148
2179
|
return /* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "requests-view", children: [
|
|
2149
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
/* @__PURE__ */ jsxRuntimeExports.
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2180
|
+
/* @__PURE__ */ jsxRuntimeExports.jsxs("ul", { className: "requests-list", children: [
|
|
2181
|
+
records.map((r, i) => {
|
|
2182
|
+
const [method, path] = splitUrl(r.url);
|
|
2183
|
+
return /* @__PURE__ */ jsxRuntimeExports.jsxs(React.Fragment, { children: [
|
|
2184
|
+
gapRow(records[i - 1]?.timestamp, r.timestamp),
|
|
2185
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("li", { className: i > 0 && r.kind?.[0] === "main" ? "new-turn" : "", children: /* @__PURE__ */ jsxRuntimeExports.jsxs("button", { type: "button", className: i === selected ? "active" : "", onClick: () => setSelected(i), children: [
|
|
2186
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `requests-method ${method.toLowerCase()}`, children: method }),
|
|
2187
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "requests-url", children: path }),
|
|
2188
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `requests-status ${!r.status || r.status >= 400 ? "error" : "ok"}`, children: r.status ?? "—" }),
|
|
2189
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "requests-meta", children: time(r.timestamp) }),
|
|
2190
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "requests-meta requests-duration", children: duration(r.durationMs) }),
|
|
2191
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: `requests-kind ${r.kind ? `kind-${r.kind[0]}` : ""}`, children: r.kind?.[1] }),
|
|
2192
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("span", { className: "requests-meta requests-tokens", title: "total tokens (input + output + cache)", children: tokens(totalTokens(r)) })
|
|
2193
|
+
] }) })
|
|
2194
|
+
] }, i);
|
|
2195
|
+
}),
|
|
2196
|
+
gapRow(records.at(-1).timestamp)
|
|
2197
|
+
] }),
|
|
2161
2198
|
/* @__PURE__ */ jsxRuntimeExports.jsxs("div", { className: "requests-detail", children: [
|
|
2162
2199
|
/* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "requests-tabs", children: ["request", "response"].map((t) => /* @__PURE__ */ jsxRuntimeExports.jsx("button", { type: "button", className: t === tab ? "active" : "", onClick: () => setTab(t), children: t }, t)) }),
|
|
2163
2200
|
/* @__PURE__ */ jsxRuntimeExports.jsx(Pane, { value: body, headers, seen, expandAll })
|
|
@@ -2594,7 +2631,7 @@ function Session({ meta, missing, date, granularity = "day", onShowPeriodOf }) {
|
|
|
2594
2631
|
}
|
|
2595
2632
|
)
|
|
2596
2633
|
] }),
|
|
2597
|
-
mode === "conversation" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "view-tab-pane-content", children: items ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationView, { items, instructions, filePath: meta.parentFilePath ?? meta.filePath, expandAll, continuesFrom: meta.continuesFrom, continuesTo: meta.continuesTo, onShowPeriodOf }) : loadError || /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "empty", children: "Loading conversation…" }) }) : mode === "jsonl" ? !items && loadError || /* @__PURE__ */ jsxRuntimeExports.jsx(JsonlView, { items, expandAll }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RequestsView, { filePath, date, granularity, fileSize, expandAll })
|
|
2634
|
+
mode === "conversation" ? /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "view-tab-pane-content", children: items ? /* @__PURE__ */ jsxRuntimeExports.jsx(ConversationView, { items, instructions, filePath: meta.parentFilePath ?? meta.filePath, expandAll, continuesFrom: meta.continuesFrom, continuesTo: meta.continuesTo, onShowPeriodOf }) : loadError || /* @__PURE__ */ jsxRuntimeExports.jsx("div", { className: "empty", children: "Loading conversation…" }) }) : mode === "jsonl" ? !items && loadError || /* @__PURE__ */ jsxRuntimeExports.jsx(JsonlView, { items, expandAll }) : /* @__PURE__ */ jsxRuntimeExports.jsx(RequestsView, { filePath, items, date, granularity, fileSize, expandAll })
|
|
2598
2635
|
] }),
|
|
2599
2636
|
/* @__PURE__ */ jsxRuntimeExports.jsx(SessionSummary, { meta, items, instructions, agent, granularity })
|
|
2600
2637
|
] }) }) }) }) });
|
|
@@ -2663,7 +2700,7 @@ function StatusSwitch({ service, on, warn, button, className = "", tooltip, chan
|
|
|
2663
2700
|
}
|
|
2664
2701
|
const proxyTooltip = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2665
2702
|
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Captures Claude Code's raw API traffic - system prompt, tool definitions, injected reminders, responses." }),
|
|
2666
|
-
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Applies
|
|
2703
|
+
/* @__PURE__ */ jsxRuntimeExports.jsx("p", { children: "Applies to running Claude Code sessions too." })
|
|
2667
2704
|
] });
|
|
2668
2705
|
const proxyChanges = /* @__PURE__ */ jsxRuntimeExports.jsxs(jsxRuntimeExports.Fragment, { children: [
|
|
2669
2706
|
/* @__PURE__ */ jsxRuntimeExports.jsx("pre", { children: `"env": {
|
|
@@ -1085,12 +1085,19 @@ button.msg-summary:hover { color: var(--text); background: var(--bg-elev-2); bor
|
|
|
1085
1085
|
/* Instruction files (system prompt / CLAUDE.md / memory) extracted from the captured API requests,
|
|
1086
1086
|
and the label over each group — one unbroken strip down the whole run. */
|
|
1087
1087
|
.instruction-file,
|
|
1088
|
-
.instruction-source
|
|
1088
|
+
.instruction-source,
|
|
1089
|
+
.instruction-missing {
|
|
1089
1090
|
border-left: 2px solid #a78bfa;
|
|
1090
1091
|
padding-left: 8px;
|
|
1091
1092
|
margin-left: -10px;
|
|
1092
1093
|
}
|
|
1093
1094
|
|
|
1095
|
+
/* Stands in for the strips when nothing was captured (see InstructionsMissing). */
|
|
1096
|
+
.instruction-missing {
|
|
1097
|
+
color: var(--text-faint);
|
|
1098
|
+
font-size: calc(11px * var(--font-scale, 1));
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1094
1101
|
/* Group label over the strips read from one part of the request (system prompt / tools / user message). */
|
|
1095
1102
|
.instruction-source {
|
|
1096
1103
|
font-family: var(--font-mono), monospace;
|
|
@@ -1254,223 +1261,256 @@ button.msg-summary:hover { color: var(--text); background: var(--bg-elev-2); bor
|
|
|
1254
1261
|
button { flex: none; padding: 0 6px; border: none; background: transparent; color: var(--text-faint); font-size: 16px; line-height: 1; }
|
|
1255
1262
|
button:hover { color: var(--text); background: transparent; }
|
|
1256
1263
|
}
|
|
1257
|
-
.requests-view {
|
|
1258
|
-
flex: 1;
|
|
1259
|
-
display: flex;
|
|
1260
|
-
min-height: 0;
|
|
1261
|
-
background: var(--bg);
|
|
1262
|
-
font-size: 12px;
|
|
1263
|
-
}
|
|
1264
|
-
|
|
1265
|
-
.requests-list {
|
|
1266
|
-
flex: 0 0 550px;
|
|
1267
|
-
overflow: auto;
|
|
1268
|
-
border-right: 1px solid var(--border);
|
|
1269
|
-
margin: 0;
|
|
1270
|
-
padding: 20px 10px;
|
|
1271
|
-
list-style: none;
|
|
1272
|
-
}
|
|
1273
|
-
|
|
1274
|
-
.requests-list li { margin: 0; }
|
|
1275
|
-
|
|
1276
|
-
/* separator above the request that starts a new user turn */
|
|
1277
|
-
.requests-list li.new-turn {
|
|
1278
|
-
border-top: 1px solid var(--border);
|
|
1279
|
-
margin-top: 6px;
|
|
1280
|
-
padding-top: 6px;
|
|
1281
|
-
}
|
|
1282
|
-
|
|
1283
|
-
.requests-list
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
.
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
overflow:
|
|
1319
|
-
white-space: nowrap;
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
/*
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
color:
|
|
1342
|
-
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
}
|
|
1421
|
-
|
|
1422
|
-
& .w-rjv-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1264
|
+
.requests-view {
|
|
1265
|
+
flex: 1;
|
|
1266
|
+
display: flex;
|
|
1267
|
+
min-height: 0;
|
|
1268
|
+
background: var(--bg);
|
|
1269
|
+
font-size: 12px;
|
|
1270
|
+
}
|
|
1271
|
+
|
|
1272
|
+
.requests-list {
|
|
1273
|
+
flex: 0 0 550px;
|
|
1274
|
+
overflow: auto;
|
|
1275
|
+
border-right: 1px solid var(--border);
|
|
1276
|
+
margin: 0;
|
|
1277
|
+
padding: 20px 10px;
|
|
1278
|
+
list-style: none;
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
.requests-list li { margin: 0; }
|
|
1282
|
+
|
|
1283
|
+
/* separator above the request that starts a new user turn */
|
|
1284
|
+
.requests-list li.new-turn {
|
|
1285
|
+
border-top: 1px solid var(--border);
|
|
1286
|
+
margin-top: 6px;
|
|
1287
|
+
padding-top: 6px;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
.requests-list li.requests-missed {
|
|
1291
|
+
margin: 18px 0;
|
|
1292
|
+
padding: 6px 10px;
|
|
1293
|
+
background: var(--bg-elev);
|
|
1294
|
+
border-radius: 4px;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
/* the wavy divider already separates the turns */
|
|
1298
|
+
.requests-list li.requests-missed + li.new-turn {
|
|
1299
|
+
border-top: 0;
|
|
1300
|
+
margin-top: 0;
|
|
1301
|
+
padding-top: 0;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.requests-list button {
|
|
1305
|
+
display: flex;
|
|
1306
|
+
align-items: center;
|
|
1307
|
+
gap: 8px;
|
|
1308
|
+
width: 100%;
|
|
1309
|
+
background: transparent;
|
|
1310
|
+
border: 0;
|
|
1311
|
+
color: var(--text);
|
|
1312
|
+
text-align: left;
|
|
1313
|
+
padding: 4px 10px;
|
|
1314
|
+
font: inherit;
|
|
1315
|
+
cursor: pointer;
|
|
1316
|
+
|
|
1317
|
+
&:hover { background: var(--hover, rgba(255, 255, 255, 0.06)); }
|
|
1318
|
+
&.active { background: var(--hover, rgba(255, 255, 255, 0.1)); }
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
.requests-url {
|
|
1322
|
+
flex: 1;
|
|
1323
|
+
color: var(--text-faint);
|
|
1324
|
+
overflow: hidden;
|
|
1325
|
+
text-overflow: ellipsis;
|
|
1326
|
+
white-space: nowrap;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
/* category label, last column — fixed width so the labels line up left-aligned,
|
|
1330
|
+
set off from the metrics by a full-height vertical rule; colors come from the
|
|
1331
|
+
shared .kind-* palette in styles.css */
|
|
1332
|
+
.requests-kind {
|
|
1333
|
+
flex: 0 0 106px;
|
|
1334
|
+
align-self: stretch;
|
|
1335
|
+
display: flex;
|
|
1336
|
+
align-items: center;
|
|
1337
|
+
padding: 0 8px;
|
|
1338
|
+
border-left: 1px solid var(--border);
|
|
1339
|
+
overflow: hidden;
|
|
1340
|
+
white-space: nowrap;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/* Postman-style method colors — muted, kept local so they don't track the app palette */
|
|
1344
|
+
.requests-method {
|
|
1345
|
+
flex: 0 0 34px;
|
|
1346
|
+
font-weight: 600;
|
|
1347
|
+
|
|
1348
|
+
&.get { color: #6ea882; }
|
|
1349
|
+
&.post { color: #6b93c4; }
|
|
1350
|
+
&.put { color: #c2835f; }
|
|
1351
|
+
&.delete { color: #c47b7b; }
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
.requests-status {
|
|
1355
|
+
font-variant-numeric: tabular-nums;
|
|
1356
|
+
|
|
1357
|
+
&.ok { color: var(--text-faint); }
|
|
1358
|
+
&.error { color: var(--danger); font-weight: bold; }
|
|
1359
|
+
}
|
|
1360
|
+
|
|
1361
|
+
.requests-meta {
|
|
1362
|
+
color: var(--text-faint);
|
|
1363
|
+
font-variant-numeric: tabular-nums;
|
|
1364
|
+
}
|
|
1365
|
+
|
|
1366
|
+
.requests-tokens {
|
|
1367
|
+
min-width: 42px;
|
|
1368
|
+
text-align: right;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
.requests-duration {
|
|
1372
|
+
min-width: 44px;
|
|
1373
|
+
text-align: right;
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
.requests-detail {
|
|
1377
|
+
flex: 1;
|
|
1378
|
+
min-width: 0;
|
|
1379
|
+
display: flex;
|
|
1380
|
+
flex-direction: column;
|
|
1381
|
+
}
|
|
1382
|
+
|
|
1383
|
+
.requests-tabs {
|
|
1384
|
+
display: flex;
|
|
1385
|
+
gap: 14px;
|
|
1386
|
+
padding: 5px 12px;
|
|
1387
|
+
border-bottom: 1px solid var(--border);
|
|
1388
|
+
|
|
1389
|
+
& button {
|
|
1390
|
+
background: transparent;
|
|
1391
|
+
border: 0;
|
|
1392
|
+
padding: 2px 0;
|
|
1393
|
+
color: var(--text-faint);
|
|
1394
|
+
text-transform: uppercase;
|
|
1395
|
+
font: inherit;
|
|
1396
|
+
font-size: 11px;
|
|
1397
|
+
letter-spacing: 0.5px;
|
|
1398
|
+
cursor: pointer;
|
|
1399
|
+
|
|
1400
|
+
&:hover { color: var(--text); }
|
|
1401
|
+
&.active { color: var(--text); }
|
|
1402
|
+
}
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
.requests-pane {
|
|
1406
|
+
flex: 1;
|
|
1407
|
+
min-width: 0;
|
|
1408
|
+
overflow: auto;
|
|
1409
|
+
padding: 8px 12px 12px;
|
|
1410
|
+
|
|
1411
|
+
/* Shift/Ctrl+wheel zoom (useMouseFontScale): font sizes below multiply this scale. */
|
|
1412
|
+
& .w-json-view-container {
|
|
1413
|
+
--w-rjv-background-color: rgb(6 6 6) !important;
|
|
1414
|
+
font-size: calc(12px * var(--font-scale, 1)) !important;
|
|
1415
|
+
line-height: 1.2 !important;
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1419
|
+
/* the hand-made braces / `"messages": [ … ]` lines stitching the body tree and the per-message
|
|
1420
|
+
trees into one document, styled to blend in with JsonView (theme vars come from the wrapper's
|
|
1421
|
+
inline style; indents mirror its KeyValues: 6px margin + 15px padding + guide line) */
|
|
1422
|
+
.requests-doc {
|
|
1423
|
+
font-family: var(--w-rjv-font-family, monospace);
|
|
1424
|
+
font-size: calc(12px * var(--font-scale, 1));
|
|
1425
|
+
line-height: 1.2;
|
|
1426
|
+
|
|
1427
|
+
& .key { color: var(--w-rjv-key-string); }
|
|
1428
|
+
& .colon { color: var(--w-rjv-colon-color); }
|
|
1429
|
+
& .bracket { color: var(--w-rjv-brackets-color); }
|
|
1430
|
+
|
|
1431
|
+
/* the root braces are ours, so hide the body tree's own open row (arrow + brace) and closing
|
|
1432
|
+
brace; the tree is the wrapper's direct child, unlike the nested per-message trees.
|
|
1433
|
+
!important beats the open row's inline display:inline-flex */
|
|
1434
|
+
& > .w-json-view-container > :is(span:first-child, div:not(.w-rjv-wrap)) { display: none !important; }
|
|
1435
|
+
|
|
1436
|
+
& .requests-msgs,
|
|
1437
|
+
& .requests-msgs .items {
|
|
1438
|
+
margin-left: 6px;
|
|
1439
|
+
padding-left: 15px;
|
|
1440
|
+
border-left: 1px solid var(--w-rjv-line-color);
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
& .w-rjv-value {
|
|
1444
|
+
color: var(--w-rjv-type-string-color)
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
/* summary shown inside a collapsed node in place of the '...' ellipsis */
|
|
1449
|
+
.requests-hint {
|
|
1450
|
+
cursor: pointer;
|
|
1451
|
+
user-select: none;
|
|
1452
|
+
font-style: italic;
|
|
1453
|
+
color: var(--text-faint);
|
|
1454
|
+
padding: 0 3px;
|
|
1455
|
+
white-space: pre; /* keeps the role's alignment padding */
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
/* parts already sent in an earlier request ($refs in the log) fade so what's new stands out */
|
|
1459
|
+
.requests-seen,
|
|
1460
|
+
.requests-pane :is(.w-rjv-inner, .w-rjv-line):has(> span .requests-seen-key) {
|
|
1461
|
+
opacity: 0.6;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
1464
|
+
.requests-headers {
|
|
1465
|
+
padding-bottom: 8px;
|
|
1466
|
+
margin-bottom: 8px;
|
|
1467
|
+
border-bottom: 1px solid var(--border);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
.requests-label {
|
|
1471
|
+
display: flex;
|
|
1472
|
+
justify-content: space-between;
|
|
1473
|
+
color: var(--text-faint);
|
|
1474
|
+
text-transform: uppercase;
|
|
1475
|
+
font-size: calc(11px * var(--font-scale, 1));
|
|
1476
|
+
letter-spacing: 0.5px;
|
|
1477
|
+
padding: 2px 0 6px;
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
/* explains the seen-part fading to first-time viewers */
|
|
1481
|
+
.requests-label-hint {
|
|
1482
|
+
text-transform: none;
|
|
1483
|
+
letter-spacing: normal;
|
|
1484
|
+
font-style: italic;
|
|
1485
|
+
}
|
|
1486
|
+
|
|
1487
|
+
.requests-pane-empty,
|
|
1488
|
+
.requests-empty {
|
|
1489
|
+
color: var(--text-faint);
|
|
1490
|
+
}
|
|
1491
|
+
|
|
1492
|
+
.requests-empty {
|
|
1493
|
+
padding: 10px 20px;
|
|
1494
|
+
}
|
|
1495
|
+
|
|
1496
|
+
/* Empty state: sells the Capture proxy instead of just saying nothing was recorded. */
|
|
1497
|
+
.requests-pitch {
|
|
1498
|
+
margin: 60px auto auto;
|
|
1499
|
+
max-width: 480px;
|
|
1500
|
+
text-align: center;
|
|
1501
|
+
padding: 24px 28px;
|
|
1502
|
+
border: 1px solid rgba(167, 139, 250, 0.35);
|
|
1503
|
+
border-radius: 8px;
|
|
1504
|
+
background: rgba(167, 139, 250, 0.06);
|
|
1505
|
+
color: var(--text-dim);
|
|
1506
|
+
line-height: 1.6;
|
|
1507
|
+
|
|
1508
|
+
& h3 { display: flex; align-items: center; justify-content: center; gap: 8px; margin: 0 0 8px; color: var(--text); font-size: 17px; }
|
|
1509
|
+
& p { margin: 0 0 6px; }
|
|
1510
|
+
& b { color: #a78bfa; }
|
|
1511
|
+
}
|
|
1512
|
+
.requests-pitch-icon { width: 22px; height: 22px; color: #a78bfa; }
|
|
1513
|
+
.requests-pitch-hint { color: var(--text); }
|
|
1474
1514
|
.agent-prompt-typing {
|
|
1475
1515
|
display: inline-flex;
|
|
1476
1516
|
align-items: center;
|
package/out/renderer/index.html
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
<meta charset="UTF-8" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>Discover</title>
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-B6LVyX-b.js"></script>
|
|
10
10
|
<link rel="modulepreload" crossorigin href="./assets/vendors-CpMM-Zb0.js">
|
|
11
11
|
<link rel="stylesheet" crossorigin href="./assets/vendors-B4XMnGdr.css">
|
|
12
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
12
|
+
<link rel="stylesheet" crossorigin href="./assets/index-DsnXk_cj.css">
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
15
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "claude-discover",
|
|
3
|
-
"version": "1.10.
|
|
4
|
-
"type": "module",
|
|
5
|
-
"description": "Claude Discover — a desktop tool to browse your local Claude Code sessions: conversations, token costs, and captured API traffic",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"author": "dichev <dichev.lab@gmail.com>",
|
|
8
|
-
"repository": "github:dichev/claude-discover",
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=22"
|
|
11
|
-
},
|
|
12
|
-
"main": "out/main/main.js",
|
|
13
|
-
"bin": {
|
|
14
|
-
"claude-discover": "bin/claude-discover.mjs"
|
|
15
|
-
},
|
|
16
|
-
"files": [
|
|
17
|
-
"out",
|
|
18
|
-
"bin"
|
|
19
|
-
],
|
|
20
|
-
"scripts": {
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"smoke:
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
"electron
|
|
36
|
-
"electron-
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
"@
|
|
45
|
-
"@fontsource/
|
|
46
|
-
"@
|
|
47
|
-
"@
|
|
48
|
-
"@
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"react
|
|
56
|
-
"react
|
|
57
|
-
"react-
|
|
58
|
-
"react-
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
"claude
|
|
69
|
-
"claude-code
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"usage
|
|
77
|
-
"
|
|
78
|
-
|
|
79
|
-
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-discover",
|
|
3
|
+
"version": "1.10.4",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Claude Discover — a desktop tool to browse your local Claude Code sessions: conversations, token costs, and captured API traffic",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "dichev <dichev.lab@gmail.com>",
|
|
8
|
+
"repository": "github:dichev/claude-discover",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22"
|
|
11
|
+
},
|
|
12
|
+
"main": "out/main/main.js",
|
|
13
|
+
"bin": {
|
|
14
|
+
"claude-discover": "bin/claude-discover.mjs"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"out",
|
|
18
|
+
"bin"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"predev": "kill-port 5555 9333",
|
|
22
|
+
"dev": "electron-vite dev",
|
|
23
|
+
"build": "electron-vite build",
|
|
24
|
+
"start": "electron .",
|
|
25
|
+
"test": "vitest run --reporter=tree",
|
|
26
|
+
"proxy": "node bin/proxy.mjs --restart",
|
|
27
|
+
"readme": "gh markdown-preview",
|
|
28
|
+
"smoke:local": "npm pack && npx -y ./claude-discover-${npm_package_version}.tgz",
|
|
29
|
+
"smoke:global": "npm pack && npm i -g ./claude-discover-${npm_package_version}.tgz && claude-discover",
|
|
30
|
+
"prepack": "npm run build",
|
|
31
|
+
"prepublishOnly": "npm whoami && npm test"
|
|
32
|
+
},
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.284",
|
|
35
|
+
"electron": "44.4.5",
|
|
36
|
+
"electron-context-menu": "^5.1.0",
|
|
37
|
+
"electron-window-state": "^5.0.3",
|
|
38
|
+
"latest-version": "^9.0.0",
|
|
39
|
+
"semver": "^7.8.5",
|
|
40
|
+
"which": "^7.0.0",
|
|
41
|
+
"write-file-atomic": "^8.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"@date-fns/tz": "^1.5.0",
|
|
45
|
+
"@fontsource/inter": "^5.3.0",
|
|
46
|
+
"@fontsource/jetbrains-mono": "^5.3.0",
|
|
47
|
+
"@tomjs/electron-devtools-installer": "^4.0.1",
|
|
48
|
+
"@uiw/react-json-view": "^2.0.0-alpha.43",
|
|
49
|
+
"@vitejs/plugin-react": "^5.2.0",
|
|
50
|
+
"date-fns": "^4.4.0",
|
|
51
|
+
"electron-vite": "^5.0.0",
|
|
52
|
+
"gpt-tokenizer": "^4.0.0",
|
|
53
|
+
"highlight.js": "^11.12.0",
|
|
54
|
+
"kill-port": "^2.0.1",
|
|
55
|
+
"lucide-react": "^1.43.0",
|
|
56
|
+
"react": "^19.3.0",
|
|
57
|
+
"react-dom": "^19.3.0",
|
|
58
|
+
"react-error-boundary": "^6.1.6",
|
|
59
|
+
"react-markdown": "^10.1.0",
|
|
60
|
+
"react-resizable-panels": "^4.12.4",
|
|
61
|
+
"rehype-highlight": "^7.0.2",
|
|
62
|
+
"remark-gfm": "^4.0.1",
|
|
63
|
+
"tippy.js": "^6.3.7",
|
|
64
|
+
"vite": "^7.3.6",
|
|
65
|
+
"vitest": "^5.0.2"
|
|
66
|
+
},
|
|
67
|
+
"keywords": [
|
|
68
|
+
"claude",
|
|
69
|
+
"claude-code",
|
|
70
|
+
"claude-code-proxy",
|
|
71
|
+
"claude-code-inspect",
|
|
72
|
+
"system-prompt",
|
|
73
|
+
"session-browser",
|
|
74
|
+
"session-profiler",
|
|
75
|
+
"transcript-viewer",
|
|
76
|
+
"token-usage",
|
|
77
|
+
"token-cost",
|
|
78
|
+
"usage-dashboard",
|
|
79
|
+
"api-inspector"
|
|
80
|
+
]
|
|
81
|
+
}
|