@obtoai/agent-bridge 0.1.0-beta.18 → 0.1.0-beta.19
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/package.json +1 -1
- package/src/external-scanner.js +61 -13
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@obtoai/agent-bridge",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.19",
|
|
4
4
|
"description": "Local consumer for the OBTO Agent Bridge. Receives bridge events over SSE and drives a coding agent (Claude Code or OpenAI Codex) on your machine.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "OBTO Inc.",
|
package/src/external-scanner.js
CHANGED
|
@@ -25,6 +25,16 @@ const os = require('os');
|
|
|
25
25
|
|
|
26
26
|
const CLAUDE_DIR = path.join(os.homedir(), '.claude', 'projects');
|
|
27
27
|
const CODEX_DIR = path.join(os.homedir(), '.codex', 'sessions');
|
|
28
|
+
// Claude Desktop app's local-agent mode stores session JSONLs under
|
|
29
|
+
// ~/Library/Application Support/Claude/local-agent-mode-sessions/<uuid>/<uuid>/local_<uuid>/.claude/projects/<encoded>/<sid>.jsonl
|
|
30
|
+
// The file format is identical to Claude Code CLI's — same queue-operation
|
|
31
|
+
// preamble, same user/assistant message shape. Just a different root.
|
|
32
|
+
// Phase 6.2.5 — beta.19: surface these so the bridge sees every Claude
|
|
33
|
+
// session on disk, not just the CLI/extension subset.
|
|
34
|
+
const CLAUDE_DESKTOP_BASE = path.join(
|
|
35
|
+
os.homedir(),
|
|
36
|
+
'Library', 'Application Support', 'Claude', 'local-agent-mode-sessions',
|
|
37
|
+
);
|
|
28
38
|
|
|
29
39
|
const PREVIEW_MAX_CHARS = 200;
|
|
30
40
|
const TITLE_MAX_CHARS = 80;
|
|
@@ -351,14 +361,45 @@ const decodeClaudeProjectDir = (encoded) => {
|
|
|
351
361
|
return encoded.replace(/-/g, '/');
|
|
352
362
|
};
|
|
353
363
|
|
|
354
|
-
//
|
|
355
|
-
|
|
364
|
+
// Discover all Claude Desktop "local-agent-mode" project roots.
|
|
365
|
+
// Structure is 3 levels deep before we hit `.claude/projects/`:
|
|
366
|
+
// <BASE>/<accountUuid>/<workspaceUuid>/local_<sessionUuid>/.claude/projects/
|
|
367
|
+
// We collect every leaf `.claude/projects` dir, then walk each like we walk
|
|
368
|
+
// the CLI's ~/.claude/projects.
|
|
369
|
+
const findClaudeDesktopProjectRoots = () => {
|
|
370
|
+
const out = [];
|
|
371
|
+
let l1;
|
|
372
|
+
try { l1 = fs.readdirSync(CLAUDE_DESKTOP_BASE); } catch (_) { return out; }
|
|
373
|
+
for (const a of l1) {
|
|
374
|
+
const aPath = path.join(CLAUDE_DESKTOP_BASE, a);
|
|
375
|
+
let aStat; try { aStat = fs.statSync(aPath); } catch (_) { continue; }
|
|
376
|
+
if (!aStat.isDirectory()) continue;
|
|
377
|
+
let l2; try { l2 = fs.readdirSync(aPath); } catch (_) { continue; }
|
|
378
|
+
for (const b of l2) {
|
|
379
|
+
const bPath = path.join(aPath, b);
|
|
380
|
+
let bStat; try { bStat = fs.statSync(bPath); } catch (_) { continue; }
|
|
381
|
+
if (!bStat.isDirectory()) continue;
|
|
382
|
+
let l3; try { l3 = fs.readdirSync(bPath); } catch (_) { continue; }
|
|
383
|
+
for (const c of l3) {
|
|
384
|
+
if (!c.startsWith('local_')) continue;
|
|
385
|
+
const projects = path.join(bPath, c, '.claude', 'projects');
|
|
386
|
+
try { if (fs.statSync(projects).isDirectory()) out.push(projects); } catch (_) {}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return out;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// Walk a single Claude projects root (works for both ~/.claude/projects and
|
|
394
|
+
// each of the Desktop app's local-agent-mode project roots — file format is
|
|
395
|
+
// identical, only the path differs).
|
|
396
|
+
const walkClaudeProjectsRoot = (root) => {
|
|
356
397
|
const out = [];
|
|
357
398
|
let topEntries;
|
|
358
|
-
try { topEntries = fs.readdirSync(
|
|
399
|
+
try { topEntries = fs.readdirSync(root); } catch (_) { return out; }
|
|
359
400
|
|
|
360
401
|
for (const entry of topEntries) {
|
|
361
|
-
const projectPath = path.join(
|
|
402
|
+
const projectPath = path.join(root, entry);
|
|
362
403
|
let projectStat;
|
|
363
404
|
try { projectStat = fs.statSync(projectPath); } catch (_) { continue; }
|
|
364
405
|
if (!projectStat.isDirectory()) continue;
|
|
@@ -374,21 +415,16 @@ const scanClaude = () => {
|
|
|
374
415
|
try { stat = fs.statSync(filePath); } catch (_) { continue; }
|
|
375
416
|
const tail = readTail(filePath);
|
|
376
417
|
const lastMsg = extractLastMessage(tail);
|
|
377
|
-
// Prefer Claude Code's own LLM-summarized title (matches VSCode's list);
|
|
378
|
-
// fall back to first-user-message scanning if a session is too new to
|
|
379
|
-
// have an ai-title record yet.
|
|
380
418
|
let title = extractAiTitleFromTail(tail);
|
|
381
419
|
if (!title) title = extractTitleFromFile(filePath);
|
|
382
|
-
// Recent messages get the streaming tail (handles huge per-message
|
|
383
|
-
// assistant turns that overflow the 16KB fixed budget).
|
|
384
420
|
const recentMessages = extractRecentMessages(readTailUntilMessages(filePath));
|
|
385
421
|
out.push({
|
|
386
422
|
source: 'claude',
|
|
387
423
|
sessionId,
|
|
388
|
-
projectDir: entry,
|
|
389
|
-
projectName: decodeClaudeProjectDir(entry),
|
|
390
|
-
title: title,
|
|
391
|
-
recentMessages: recentMessages,
|
|
424
|
+
projectDir: entry,
|
|
425
|
+
projectName: decodeClaudeProjectDir(entry),
|
|
426
|
+
title: title,
|
|
427
|
+
recentMessages: recentMessages,
|
|
392
428
|
lastActivityAt: stat.mtimeMs,
|
|
393
429
|
lastMessagePreview: lastMsg ? lastMsg.preview : '',
|
|
394
430
|
lastMessageAuthor: lastMsg ? lastMsg.author : null,
|
|
@@ -398,6 +434,18 @@ const scanClaude = () => {
|
|
|
398
434
|
return out;
|
|
399
435
|
};
|
|
400
436
|
|
|
437
|
+
// Scan all Claude session storage on this machine — CLI + VSCode extension
|
|
438
|
+
// (~/.claude/projects) AND every Claude Desktop local-agent-mode subdir
|
|
439
|
+
// (~/Library/Application Support/Claude/local-agent-mode-sessions/.../).
|
|
440
|
+
const scanClaude = () => {
|
|
441
|
+
const roots = [CLAUDE_DIR].concat(findClaudeDesktopProjectRoots());
|
|
442
|
+
const out = [];
|
|
443
|
+
for (const root of roots) {
|
|
444
|
+
out.push(...walkClaudeProjectsRoot(root));
|
|
445
|
+
}
|
|
446
|
+
return out;
|
|
447
|
+
};
|
|
448
|
+
|
|
401
449
|
// Scan ~/.codex/sessions/YYYY/MM/DD/rollout-<ts>-<sessionId>.jsonl
|
|
402
450
|
// The first JSONL line for a Codex rollout is a session-meta record that
|
|
403
451
|
// contains the working directory; we read it once for projectDir.
|