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 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 = 100) {
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') || '100', 10);
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(50);
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(100);
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(50);
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 entries
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "osborn",
3
- "version": "0.9.156",
3
+ "version": "0.9.160",
4
4
  "description": "Voice AI coding assistant - local agent that connects to Osborn frontend",
5
5
  "type": "module",
6
6
  "bin": {