claude-code-session-manager 0.62.0 → 0.63.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.
Files changed (68) hide show
  1. package/dist/assets/AgentLibrary-BeJJa_zv.js +1 -0
  2. package/dist/assets/History-mWbqemhZ.js +2 -0
  3. package/dist/assets/Hooks-D_8sciZu.js +3 -0
  4. package/dist/assets/HostBilko-6aUJ7Smz.js +1 -0
  5. package/dist/assets/Library-CG1LDXLw.js +46 -0
  6. package/dist/assets/ListDetail-CIIJOWwI.js +1 -0
  7. package/dist/assets/MarkdownEditor-D0-y8V-f.js +1 -0
  8. package/dist/assets/McpServers-Kc-djiVi.js +2 -0
  9. package/dist/assets/Memory-B60DGRTW.js +8 -0
  10. package/dist/assets/Panel-CbTPsYHq.js +1 -0
  11. package/dist/assets/Permissions-C8KrSfrd.js +3 -0
  12. package/dist/assets/Plugins-BT8EumHb.js +2 -0
  13. package/dist/assets/ProvenanceBadge-cEqPpFsT.js +1 -0
  14. package/dist/assets/Scheduler-DbKPd0or.js +14 -0
  15. package/dist/assets/ScopeSwitcher-BNesCJ_t.js +1 -0
  16. package/dist/assets/Settings-Bjt7AQ6G.js +3 -0
  17. package/dist/assets/SkillReferenceGraph-DzuaPDM-.js +46 -0
  18. package/dist/assets/Skills-CVL_4X3T.js +3 -0
  19. package/dist/assets/SystemPrompt-Ct__nFF1.js +1 -0
  20. package/dist/assets/TagLibrary-DmrTfw4D.js +1 -0
  21. package/dist/assets/{TiptapBody-Bn-LMce4.js → TiptapBody-DaS0M_Ni.js} +1 -1
  22. package/dist/assets/Toggle-C0a-6xV7.js +1 -0
  23. package/dist/assets/index-CO_7DroC.js +3066 -0
  24. package/dist/assets/{index-DOXvph-Z.css → index-LlWpj2VJ.css} +1 -1
  25. package/dist/assets/listSkills-QORduPIk.js +1 -0
  26. package/dist/assets/settingsSchema-B7dMJaix.js +3 -0
  27. package/dist/assets/skillFrontmatter-Dif5JIg7.js +10 -0
  28. package/dist/index.html +2 -2
  29. package/package.json +1 -3
  30. package/src/main/__tests__/config-readText-bounded.test.cjs +84 -0
  31. package/src/main/__tests__/heapSnapshot.test.cjs +121 -0
  32. package/src/main/__tests__/historyAggregatorIntraday.test.cjs +313 -0
  33. package/src/main/__tests__/runLogRetention.test.cjs +343 -0
  34. package/src/main/__tests__/runVerify-transcript-commit-evidence.test.cjs +215 -0
  35. package/src/main/__tests__/scheduler-investigation-prompt.test.cjs +42 -1
  36. package/src/main/__tests__/scheduler-prd-missing-skip.test.cjs +54 -1
  37. package/src/main/__tests__/transcripts-batch-flush.test.cjs +249 -0
  38. package/src/main/__tests__/transcripts-doFlush-array.test.cjs +6 -0
  39. package/src/main/__tests__/transcripts-paged-reads.test.cjs +6 -0
  40. package/src/main/__tests__/transcriptsUsageFor.test.cjs +6 -0
  41. package/src/main/config.cjs +32 -29
  42. package/src/main/files.cjs +20 -0
  43. package/src/main/health.cjs +32 -0
  44. package/src/main/heapSnapshot.cjs +122 -0
  45. package/src/main/historyAggregator.cjs +176 -36
  46. package/src/main/index.cjs +10 -63
  47. package/src/main/ipcSchemas.cjs +20 -209
  48. package/src/main/lib/opsOwnership.cjs +0 -2
  49. package/src/main/lib/runLogRetention.cjs +358 -0
  50. package/src/main/lib/summarize.cjs +0 -1
  51. package/src/main/runVerify.cjs +55 -6
  52. package/src/main/scheduler.cjs +46 -7
  53. package/src/main/transcripts.cjs +29 -5
  54. package/src/preload/api.d.ts +18 -199
  55. package/src/preload/index.cjs +12 -102
  56. package/dist/assets/index-BfiMRkln.js +0 -3225
  57. package/src/main/__tests__/browserAgentServer.test.cjs +0 -235
  58. package/src/main/__tests__/browserView-destroyed-handler.test.cjs +0 -85
  59. package/src/main/__tests__/browserView-oauth-popup.test.cjs +0 -126
  60. package/src/main/__tests__/web-remote-e2e-pinning.test.cjs +0 -181
  61. package/src/main/browserAgentServer.cjs +0 -224
  62. package/src/main/browserCapture.cjs +0 -728
  63. package/src/main/browserView.cjs +0 -916
  64. package/src/main/lib/__tests__/browserAgentActions.test.cjs +0 -76
  65. package/src/main/lib/browserAgentActions.cjs +0 -114
  66. package/src/main/lib/e2eStateMachine.cjs +0 -39
  67. package/src/main/webRemote.cjs +0 -1367
  68. package/src/preload/browserViewPreload.cjs +0 -118
@@ -1,224 +0,0 @@
1
- /**
2
- * browserAgentServer.cjs — loopback-only HTTP API for driving the Browser
3
- * tab one ad-hoc action at a time (PRD 535 foundation).
4
- *
5
- * Deliberately a SEPARATE server from the scheduler's admin HTTP API
6
- * (src/main/lib/localAdminHttp.cjs + scheduler.cjs's registerAdminRoutes),
7
- * not a route added to it: that admin API documents itself as intentionally
8
- * narrow ("Only two routes... writePrd/pause/resume are intentionally NOT
9
- * exposed here"). Arbitrary click/type/navigate/screenshot control of a live
10
- * browser is a materially larger capability surface than "reset one
11
- * scheduler job" and deserves its own security boundary, not creeping scope
12
- * onto the existing narrow one. A future MCP server (PRD 536) wraps this
13
- * HTTP API the same way scripts/scheduler-mcp-server.cjs wraps the admin API.
14
- *
15
- * Security posture (mirrors the scheduler's admin API exactly):
16
- * - Binds 127.0.0.1 only, OS-assigned ephemeral port. Never reachable
17
- * off-box.
18
- * - Bearer token, regenerated every app boot, written to
19
- * ~/.claude/session-manager/browser-agent-api.json with 0600 perms.
20
- * A distinct file from admin-api.json — this token is never shared with
21
- * or derived from the scheduler's token.
22
- * - Token compared with crypto.timingSafeEqual to avoid a timing
23
- * side-channel on the comparison itself.
24
- * - Mode-scoped token file, same rationale/fix as localAdminHttp.cjs's
25
- * resolveTokenPath() (confirmed-live incident 2026-07-29): a dev/e2e
26
- * launch (SM_DEV=1 / SM_E2E=1) writes to browser-agent-api.dev.json /
27
- * browser-agent-api.e2e.json instead of the production file, so it can
28
- * never silently orphan a concurrently-running production instance.
29
- * - Four routes: list tabs (read-only), screenshot, single action
30
- * (click/type/key/scroll/navigate), DOM/text snapshot.
31
- *
32
- * Out of scope for THIS server (see PRD 535's "Out of scope" section):
33
- * no confirmation/consent gating, no domain allowlist/blocklist, no
34
- * content-injection scanning. That safety layer is a deliberate, separate
35
- * follow-up decision, not an oversight.
36
- */
37
-
38
- 'use strict';
39
-
40
- const http = require('node:http');
41
- const os = require('node:os');
42
- const path = require('node:path');
43
- const crypto = require('node:crypto');
44
- const fsp = require('node:fs/promises');
45
- const config = require('./config.cjs');
46
-
47
- const TOKEN_PATH = path.join(os.homedir(), '.claude', 'session-manager', 'browser-agent-api.json');
48
- // Screenshots are base64 PNG data URLs — bigger cap than the admin API's 1MB,
49
- // still bounded so a malformed/malicious body can't exhaust memory.
50
- const BODY_MAX_BYTES = 8 * 1024 * 1024;
51
-
52
- /**
53
- * Mode-aware token file path, resolved fresh on every call — see
54
- * localAdminHttp.cjs's resolveTokenPath() for the full rationale.
55
- */
56
- function resolveTokenPath() {
57
- if (process.env.SM_DEV === '1') {
58
- return path.join(os.homedir(), '.claude', 'session-manager', 'browser-agent-api.dev.json');
59
- }
60
- if (process.env.SM_E2E === '1') {
61
- return path.join(os.homedir(), '.claude', 'session-manager', 'browser-agent-api.e2e.json');
62
- }
63
- return TOKEN_PATH;
64
- }
65
-
66
- function timingSafeEqualStrings(a, b) {
67
- const bufA = Buffer.from(String(a ?? ''));
68
- const bufB = Buffer.from(String(b ?? ''));
69
- if (bufA.length !== bufB.length) {
70
- // Compare against a same-length buffer anyway so the failure path still
71
- // takes constant time relative to the (fixed) token length, rather than
72
- // returning early on a length mismatch.
73
- crypto.timingSafeEqual(bufA, bufA);
74
- return false;
75
- }
76
- return crypto.timingSafeEqual(bufA, bufB);
77
- }
78
-
79
- function readBody(req, maxBytes = BODY_MAX_BYTES) {
80
- return new Promise((resolve, reject) => {
81
- let total = 0;
82
- const chunks = [];
83
- req.on('data', (chunk) => {
84
- total += chunk.length;
85
- if (total > maxBytes) {
86
- reject(new Error('body too large'));
87
- req.destroy();
88
- return;
89
- }
90
- chunks.push(chunk);
91
- });
92
- req.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
93
- req.on('error', reject);
94
- });
95
- }
96
-
97
- function sendJson(res, status, obj) {
98
- const body = JSON.stringify(obj);
99
- res.writeHead(status, { 'Content-Type': 'application/json', 'Content-Length': Buffer.byteLength(body) });
100
- res.end(body);
101
- }
102
-
103
- function safeParseJson(raw) {
104
- if (!raw) return {};
105
- try {
106
- return JSON.parse(raw);
107
- } catch {
108
- return undefined;
109
- }
110
- }
111
-
112
- /**
113
- * Create the browser-agent HTTP server. `remote` exposes the browserView.cjs
114
- * functions this server dispatches to (injected, not required directly, so
115
- * this module stays testable without booting Electron) — shape:
116
- * { listTabs(): Promise<Array>, screenshot({viewId}): Promise<obj>,
117
- * action(params): Promise<obj>, dom({viewId}): Promise<obj> }
118
- */
119
- function createBrowserAgentServer(remote) {
120
- let server = null;
121
- let token = null;
122
-
123
- async function ensureToken() {
124
- token = crypto.randomBytes(32).toString('hex');
125
- const tokenPath = resolveTokenPath();
126
- await config.writeJson(tokenPath, { port: null, token });
127
- try { await fsp.chmod(tokenPath, 0o600); } catch { /* best-effort on platforms without POSIX perms */ }
128
- return token;
129
- }
130
-
131
- async function persistPort(port) {
132
- const tokenPath = resolveTokenPath();
133
- await config.writeJson(tokenPath, { port, token });
134
- try { await fsp.chmod(tokenPath, 0o600); } catch { /* */ }
135
- }
136
-
137
- function authorized(req) {
138
- const header = req.headers.authorization || '';
139
- const match = /^Bearer (.+)$/.exec(header);
140
- if (!match) return false;
141
- return timingSafeEqualStrings(match[1], token);
142
- }
143
-
144
- async function handleRequest(req, res) {
145
- if (!authorized(req)) {
146
- sendJson(res, 401, { ok: false, error: 'unauthorized' });
147
- return;
148
- }
149
- try {
150
- const url = new URL(req.url, 'http://127.0.0.1');
151
-
152
- if (req.method === 'GET' && url.pathname === '/agent/browser/tabs') {
153
- const tabs = await remote.listTabs();
154
- sendJson(res, 200, { ok: true, tabs });
155
- return;
156
- }
157
-
158
- if (req.method === 'POST' && url.pathname === '/agent/browser/screenshot') {
159
- const raw = await readBody(req);
160
- const parsed = safeParseJson(raw);
161
- if (parsed === undefined) {
162
- sendJson(res, 400, { ok: false, error: 'invalid JSON body' });
163
- return;
164
- }
165
- const result = await remote.screenshot({ viewId: typeof parsed.viewId === 'string' ? parsed.viewId : undefined });
166
- sendJson(res, result.ok ? 200 : 400, result);
167
- return;
168
- }
169
-
170
- if (req.method === 'POST' && url.pathname === '/agent/browser/action') {
171
- const raw = await readBody(req);
172
- const parsed = safeParseJson(raw);
173
- if (parsed === undefined) {
174
- sendJson(res, 400, { ok: false, error: 'invalid JSON body' });
175
- return;
176
- }
177
- if (typeof parsed.action !== 'string' || !parsed.action) {
178
- sendJson(res, 400, { ok: false, error: 'missing action' });
179
- return;
180
- }
181
- const result = await remote.action(parsed);
182
- sendJson(res, result.ok ? 200 : 400, result);
183
- return;
184
- }
185
-
186
- if (req.method === 'GET' && url.pathname === '/agent/browser/dom') {
187
- const viewId = url.searchParams.get('viewId') || undefined;
188
- const result = await remote.dom({ viewId });
189
- sendJson(res, result.ok ? 200 : 400, result);
190
- return;
191
- }
192
-
193
- sendJson(res, 404, { ok: false, error: 'not found' });
194
- } catch (e) {
195
- sendJson(res, 500, { ok: false, error: e?.message ?? 'internal error' });
196
- }
197
- }
198
-
199
- async function start() {
200
- await ensureToken();
201
- server = http.createServer((req, res) => {
202
- handleRequest(req, res).catch(() => {
203
- try { sendJson(res, 500, { ok: false, error: 'internal error' }); } catch { /* */ }
204
- });
205
- });
206
- await new Promise((resolve, reject) => {
207
- server.once('error', reject);
208
- server.listen(0, '127.0.0.1', resolve);
209
- });
210
- const { port } = server.address();
211
- await persistPort(port);
212
- return { port, token };
213
- }
214
-
215
- async function stop() {
216
- if (!server) return;
217
- await new Promise((resolve) => server.close(() => resolve()));
218
- server = null;
219
- }
220
-
221
- return { start, stop, get token() { return token; }, get server() { return server; } };
222
- }
223
-
224
- module.exports = { createBrowserAgentServer, TOKEN_PATH, resolveTokenPath };