osborn 0.9.156 → 0.9.160
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/dist/config.js +1 -1
- package/dist/index.js +75 -5
- package/package.json +1 -1
package/dist/config.js
CHANGED
|
@@ -595,7 +595,7 @@ async function extractCwd(filePath) {
|
|
|
595
595
|
*
|
|
596
596
|
* @param limit - Max sessions to return (default 100, sorted by recency)
|
|
597
597
|
*/
|
|
598
|
-
export async function listAllClaudeSessions(limit =
|
|
598
|
+
export async function listAllClaudeSessions(limit = 2000) {
|
|
599
599
|
const projectsDir = getClaudeProjectsDir();
|
|
600
600
|
if (!existsSync(projectsDir))
|
|
601
601
|
return [];
|
package/dist/index.js
CHANGED
|
@@ -268,6 +268,41 @@ let codeServerStartHook = null;
|
|
|
268
268
|
// public HTTP server so no Cloudflare tunnel is needed.
|
|
269
269
|
const IDE_TARGET = 'http://127.0.0.1:8300';
|
|
270
270
|
const OSBORN_API_PORT = parseInt(process.env.OSBORN_API_PORT || '8741', 10);
|
|
271
|
+
// Dev-domain wildcard subdomain routing.
|
|
272
|
+
// When DEV_DOMAIN is set (e.g. "dev.voice-native.com"), requests arriving with
|
|
273
|
+
// Host: PORT-APPNAME.dev.voice-native.com are proxied to localhost:PORT at root.
|
|
274
|
+
// The fleet router (apps/dev-router) handles TLS + fly-replay to reach this machine;
|
|
275
|
+
// the agent only needs to detect the pattern and forward.
|
|
276
|
+
const DEV_DOMAIN = process.env.DEV_DOMAIN || '';
|
|
277
|
+
function parseDevSubdomainPort(req) {
|
|
278
|
+
if (!DEV_DOMAIN)
|
|
279
|
+
return null;
|
|
280
|
+
// Primary: Host header — preserved through fly-replay by Fly
|
|
281
|
+
const hostname = (req.headers.host || '').split(':')[0].toLowerCase();
|
|
282
|
+
const suffix = '.' + DEV_DOMAIN;
|
|
283
|
+
if (hostname.endsWith(suffix)) {
|
|
284
|
+
const label = hostname.slice(0, -suffix.length); // e.g. "3000-osborn-abc"
|
|
285
|
+
const dashIdx = label.indexOf('-');
|
|
286
|
+
if (dashIdx > 0) {
|
|
287
|
+
const port = parseInt(label.slice(0, dashIdx), 10);
|
|
288
|
+
if (!isNaN(port) && port > 1024 && port < 65536 && port !== 8300 && port !== OSBORN_API_PORT) {
|
|
289
|
+
return port;
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Fallback: fly-replay-src state field — fleet router encodes "state=port:PORT"
|
|
294
|
+
const replaySrc = req.headers['fly-replay-src'];
|
|
295
|
+
if (replaySrc) {
|
|
296
|
+
const m = replaySrc.match(/state=port:(\d+)/);
|
|
297
|
+
if (m) {
|
|
298
|
+
const port = parseInt(m[1], 10);
|
|
299
|
+
if (!isNaN(port) && port > 1024 && port < 65536 && port !== 8300 && port !== OSBORN_API_PORT) {
|
|
300
|
+
return port;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
271
306
|
// Route prefixes that belong to the agent itself. The proxy fall-through checks
|
|
272
307
|
// this list FIRST so it never intercepts an agent API path.
|
|
273
308
|
const AGENT_ROUTE_PREFIXES = [
|
|
@@ -485,7 +520,7 @@ function startApiServer(workingDir, port) {
|
|
|
485
520
|
const syncToken = process.env.OSBORN_SYNC_TOKEN;
|
|
486
521
|
if (req.method === 'GET' && url.pathname === '/sessions') {
|
|
487
522
|
try {
|
|
488
|
-
const limit = parseInt(url.searchParams.get('limit') || '
|
|
523
|
+
const limit = parseInt(url.searchParams.get('limit') || '2000', 10);
|
|
489
524
|
const sessions = await listAllClaudeSessions(limit);
|
|
490
525
|
const payload = {
|
|
491
526
|
// The agent's working directory at launch — the BASE LAYER of all
|
|
@@ -1552,6 +1587,32 @@ function startApiServer(workingDir, port) {
|
|
|
1552
1587
|
return;
|
|
1553
1588
|
}
|
|
1554
1589
|
// ────────────────────────────────────────────────────────────────────────
|
|
1590
|
+
// ── Dev-domain subdomain proxy: PORT-slug.dev.voice-native.com → localhost:PORT/* ──
|
|
1591
|
+
// Fly replay routes the request here from the fleet router; the app lives at root
|
|
1592
|
+
// so the full path is forwarded unchanged (no basePath prefix needed).
|
|
1593
|
+
const devPort = parseDevSubdomainPort(req);
|
|
1594
|
+
if (devPort !== null) {
|
|
1595
|
+
ideLastProxiedActivity = Date.now();
|
|
1596
|
+
const devProxyOptions = {
|
|
1597
|
+
hostname: '127.0.0.1',
|
|
1598
|
+
port: devPort,
|
|
1599
|
+
path: url.pathname + (url.search || ''),
|
|
1600
|
+
method: req.method,
|
|
1601
|
+
headers: { ...req.headers, host: `127.0.0.1:${devPort}` },
|
|
1602
|
+
};
|
|
1603
|
+
const devProxyReq = httpRequest(devProxyOptions, (devProxyRes) => {
|
|
1604
|
+
res.writeHead(devProxyRes.statusCode || 502, devProxyRes.headers);
|
|
1605
|
+
devProxyRes.pipe(res);
|
|
1606
|
+
});
|
|
1607
|
+
devProxyReq.on('error', () => {
|
|
1608
|
+
if (!res.headersSent) {
|
|
1609
|
+
res.writeHead(502);
|
|
1610
|
+
res.end('Dev port proxy error');
|
|
1611
|
+
}
|
|
1612
|
+
});
|
|
1613
|
+
req.pipe(devProxyReq);
|
|
1614
|
+
return;
|
|
1615
|
+
}
|
|
1555
1616
|
// ── Port proxy: /proxy/PORT/* → localhost:PORT/proxy/PORT/* ─────────────
|
|
1556
1617
|
// Forwards to a local dev server while PRESERVING the full path so that
|
|
1557
1618
|
// basePath-aware apps (e.g. Next.js with basePath: '/proxy/3000') receive
|
|
@@ -1659,6 +1720,13 @@ function startApiServer(workingDir, port) {
|
|
|
1659
1720
|
// — this must work for the integrated terminal to connect.
|
|
1660
1721
|
// Reject all other upgrades (meeting audio moved to polling in 0.9.xx).
|
|
1661
1722
|
server.on('upgrade', (req, socket, head) => {
|
|
1723
|
+
// Dev-domain subdomain WebSocket proxy (same pattern as HTTP handler above)
|
|
1724
|
+
const wsDevPort = parseDevSubdomainPort(req);
|
|
1725
|
+
if (wsDevPort !== null) {
|
|
1726
|
+
ideLastProxiedActivity = Date.now();
|
|
1727
|
+
wsProxy.ws(req, socket, head, { target: `http://127.0.0.1:${wsDevPort}` });
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1662
1730
|
const upgradePath = new URL(req.url || '/', `http://localhost`).pathname;
|
|
1663
1731
|
const isAgentRoute = AGENT_ROUTE_PREFIXES.some(p => upgradePath === p || upgradePath.startsWith(p + '/'));
|
|
1664
1732
|
if (ideProxyEnabled && hasValidIdeCookie(req) && !isAgentRoute) {
|
|
@@ -5161,7 +5229,7 @@ async function main() {
|
|
|
5161
5229
|
let readySent = false;
|
|
5162
5230
|
const provider = sessionVoiceMode === 'realtime' ? realtimeConfig.provider : 'claude';
|
|
5163
5231
|
// Fetch full session list for startup session browser (all Claude projects)
|
|
5164
|
-
const allSessions = await listAllClaudeSessions(
|
|
5232
|
+
const allSessions = await listAllClaudeSessions();
|
|
5165
5233
|
const recentSessionId = allSessions.length > 0 ? allSessions[0].sessionId : null;
|
|
5166
5234
|
const hasRecentSession = allSessions.length > 0;
|
|
5167
5235
|
// Prepare sessions for frontend (up to 50)
|
|
@@ -5415,7 +5483,7 @@ async function main() {
|
|
|
5415
5483
|
// List available sessions across all Claude projects
|
|
5416
5484
|
console.log('📋 Listing available sessions (all projects)...');
|
|
5417
5485
|
try {
|
|
5418
|
-
const sessions = await listAllClaudeSessions(
|
|
5486
|
+
const sessions = await listAllClaudeSessions();
|
|
5419
5487
|
await sendToFrontend({
|
|
5420
5488
|
type: 'sessions_list',
|
|
5421
5489
|
// See `/sessions` HTTP handler — baseCwd is the agent's
|
|
@@ -6244,7 +6312,7 @@ async function main() {
|
|
|
6244
6312
|
let resolvedSessionId = targetSessionId;
|
|
6245
6313
|
if (!resolvedSessionId) {
|
|
6246
6314
|
// Pick the most recently modified session, excluding the focused one.
|
|
6247
|
-
const allSess = await listAllClaudeSessions(
|
|
6315
|
+
const allSess = await listAllClaudeSessions();
|
|
6248
6316
|
const focusedId = currentLLM?.sessionId || currentResumeSessionId || null;
|
|
6249
6317
|
const candidate = allSess.find(s => s.sessionId !== focusedId);
|
|
6250
6318
|
resolvedSessionId = candidate?.sessionId ?? undefined;
|
|
@@ -6318,8 +6386,9 @@ async function main() {
|
|
|
6318
6386
|
// Process exited between readdir and read — skip it
|
|
6319
6387
|
}
|
|
6320
6388
|
}
|
|
6321
|
-
// Sort by resident memory descending, cap at 40
|
|
6389
|
+
// Sort by resident memory descending, cap table at 40 but keep real total
|
|
6322
6390
|
processes.sort((a, b) => b.rssMb - a.rssMb);
|
|
6391
|
+
const totalCount = processes.length;
|
|
6323
6392
|
const topProcesses = processes.slice(0, 40);
|
|
6324
6393
|
// Reuse the existing os module memory totals (same source as /health endpoint)
|
|
6325
6394
|
const totalMb = Math.round(totalmem() / 1024 / 1024);
|
|
@@ -6328,6 +6397,7 @@ async function main() {
|
|
|
6328
6397
|
await sendToFrontend({
|
|
6329
6398
|
type: 'process_list',
|
|
6330
6399
|
processes: topProcesses,
|
|
6400
|
+
totalCount,
|
|
6331
6401
|
memory: { usedMb, totalMb, freeMb },
|
|
6332
6402
|
});
|
|
6333
6403
|
}
|