@lifeaitools/clauth 1.5.43 → 1.5.45

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 (2) hide show
  1. package/cli/commands/serve.js +214 -53
  2. package/package.json +1 -1
@@ -3382,6 +3382,81 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3382
3382
  });
3383
3383
  }
3384
3384
 
3385
+ // GET /debug/sessions — live view of all terminal/chitchat sessions
3386
+ if (method === "GET" && reqPath === "/debug/sessions") {
3387
+ const now = Date.now();
3388
+ const sessions = [];
3389
+ for (const [, s] of terminalSessions) {
3390
+ const startedMs = new Date(s.started_at).getTime();
3391
+ sessions.push({
3392
+ session_id: s.session_id,
3393
+ name: s.name,
3394
+ type: s.is_chitchat ? "chitchat" : "terminal",
3395
+ status: s.status,
3396
+ turn: s.turn || 0,
3397
+ cwd: s.cwd || null,
3398
+ knowledge_tier: s.knowledge_tier,
3399
+ started_at: s.started_at,
3400
+ age_seconds: Math.round((now - startedMs) / 1000),
3401
+ last_response_at: s.lastResponseAt || null,
3402
+ last_response_preview: s.lastResponse
3403
+ ? s.lastResponse.slice(0, 300) + (s.lastResponse.length > 300 ? "…" : "")
3404
+ : null,
3405
+ context_chars: s.context ? s.context.length : 0,
3406
+ active_pid: s.activeProc ? s.activeProc.pid : null,
3407
+ });
3408
+ }
3409
+ const html = `<!DOCTYPE html>
3410
+ <html>
3411
+ <head>
3412
+ <meta charset="utf-8">
3413
+ <meta http-equiv="refresh" content="3">
3414
+ <title>clauth sessions</title>
3415
+ <style>
3416
+ body { font-family: monospace; background: #0d1117; color: #c9d1d9; padding: 20px; }
3417
+ h1 { color: #58a6ff; font-size: 16px; margin-bottom: 4px; }
3418
+ .meta { color: #6e7681; font-size: 12px; margin-bottom: 20px; }
3419
+ .empty { color: #6e7681; }
3420
+ .session { border: 1px solid #30363d; border-radius: 6px; padding: 16px; margin-bottom: 16px; }
3421
+ .session.busy { border-color: #f0883e; }
3422
+ .session.ready { border-color: #3fb950; }
3423
+ .session.stopped { border-color: #6e7681; opacity: 0.6; }
3424
+ .label { color: #6e7681; font-size: 11px; text-transform: uppercase; }
3425
+ .value { color: #e6edf3; font-size: 13px; margin-bottom: 8px; }
3426
+ .badge { display: inline-block; padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: bold; }
3427
+ .badge.chitchat { background: #1f4068; color: #58a6ff; }
3428
+ .badge.terminal { background: #2d1f3d; color: #bc8cff; }
3429
+ .badge.busy { background: #3d2400; color: #f0883e; }
3430
+ .badge.ready { background: #0f2a0f; color: #3fb950; }
3431
+ .preview { background: #161b22; border-radius: 4px; padding: 10px; font-size: 12px; color: #8b949e; white-space: pre-wrap; word-break: break-word; max-height: 120px; overflow: auto; }
3432
+ </style>
3433
+ </head>
3434
+ <body>
3435
+ <h1>clauth · live sessions</h1>
3436
+ <div class="meta">v${VERSION} · ${sessions.length} session${sessions.length !== 1 ? "s" : ""} · auto-refresh 3s · ${new Date().toISOString()}</div>
3437
+ ${sessions.length === 0
3438
+ ? '<div class="empty">No active sessions.</div>'
3439
+ : sessions.map(s => `
3440
+ <div class="session ${s.status}">
3441
+ <span class="badge ${s.type}">${s.type}</span>
3442
+ <span class="badge ${s.status}" style="margin-left:6px">${s.status}${s.status === "busy" ? " ⟳" : ""}</span>
3443
+ <div style="margin-top:10px">
3444
+ <div class="label">name</div><div class="value">${s.name}</div>
3445
+ <div class="label">session_id</div><div class="value" style="color:#6e7681;font-size:11px">${s.session_id}</div>
3446
+ <div class="label">turn / age / context</div>
3447
+ <div class="value">turn ${s.turn} · ${s.age_seconds}s old · ${s.context_chars} chars context${s.active_pid ? ` · pid ${s.active_pid}` : ""}</div>
3448
+ <div class="label">cwd</div><div class="value" style="color:#6e7681">${s.cwd || "(not set)"}</div>
3449
+ ${s.last_response_preview ? `
3450
+ <div class="label">last response ${s.last_response_at ? "· " + s.last_response_at : ""}</div>
3451
+ <div class="preview">${s.last_response_preview.replace(/</g, "&lt;").replace(/>/g, "&gt;")}</div>` : ""}
3452
+ </div>
3453
+ </div>`).join("")}
3454
+ </body>
3455
+ </html>`;
3456
+ res.writeHead(200, { "Content-Type": "text/html", ...CORS });
3457
+ return res.end(html);
3458
+ }
3459
+
3385
3460
  // GET /builds — CI build status (no auth required, same as /ping)
3386
3461
  if (method === "GET" && reqPath === "/builds") {
3387
3462
  return ok(res, buildStatus);
@@ -5074,11 +5149,16 @@ function stopTerminalSession(session_id) {
5074
5149
  return { stopped: true, session_id };
5075
5150
  }
5076
5151
 
5077
- // ── Chitchat helpers ──────────────────────────────────────────────────────
5078
- // Wrappers around terminal session logic, pre-configured for regen-root collab.
5079
- // cwd + bootstrap path are read from the vault's first fileserver mount at
5080
- // call time no hardcoded paths. Falls back to C:/Dev/regen-root if vault
5081
- // is locked or no fileserver mount is configured.
5152
+ // ── Chitchat helpers — file-relay transport ───────────────────────────────
5153
+ // claude.ai sends messages by writing to inbox files. A running /rdc:collab
5154
+ // CLI session watches inbox, acts, writes responses to outbox. clauth reads
5155
+ // outbox and returns them to claude.ai via chitchat_recv. Dave can watch the
5156
+ // terminal, interject, and see exactly what Claude Code is doing.
5157
+ //
5158
+ // Session dirs: <root>/.rdc/relay/sessions/<session_id>/inbox/ + outbox/
5159
+ //
5160
+ // cwd is resolved from the vault's first fileserver mount at call time.
5161
+ // Falls back to C:/Dev/regen-root if vault is locked or no mount configured.
5082
5162
 
5083
5163
  const CHITCHAT_FALLBACK_CWD = 'C:/Dev/regen-root';
5084
5164
 
@@ -5090,34 +5170,52 @@ async function resolveChitchatRoot(vault) {
5090
5170
  return CHITCHAT_FALLBACK_CWD;
5091
5171
  }
5092
5172
 
5093
- async function readBootstrapContext(rootPath) {
5094
- const bootstrapPath = path.join(rootPath, '.rdc', 'guides', 'agent-bootstrap.md');
5095
- try {
5096
- return fs.readFileSync(bootstrapPath, 'utf-8');
5097
- } catch {
5098
- return `# agent-bootstrap.md not found at ${bootstrapPath} — proceeding without corpus context\n`;
5099
- }
5173
+ function chitchatSessionDir(rootPath, session_id) {
5174
+ return path.join(rootPath, '.rdc', 'relay', 'sessions', session_id);
5100
5175
  }
5101
5176
 
5102
5177
  async function startChitchatSession(name, vault) {
5103
5178
  const rootPath = await resolveChitchatRoot(vault);
5104
- const corpus = await readBootstrapContext(rootPath);
5105
5179
  const session_id = generateSessionId();
5106
- const preamble = `${corpus}\n\n---\nCollab session: ${name} | cwd: ${rootPath}\nRead the bootstrap above. Ready to receive tasks from claude.ai.`;
5180
+ const sessionDir = chitchatSessionDir(rootPath, session_id);
5181
+
5182
+ // Create inbox + outbox dirs
5183
+ fs.mkdirSync(path.join(sessionDir, 'inbox'), { recursive: true });
5184
+ fs.mkdirSync(path.join(sessionDir, 'outbox'), { recursive: true });
5185
+
5186
+ // Write session metadata
5187
+ fs.writeFileSync(path.join(sessionDir, 'status.json'), JSON.stringify({
5188
+ session_id,
5189
+ name,
5190
+ status: 'waiting',
5191
+ started_at: new Date().toISOString(),
5192
+ cwd: rootPath,
5193
+ }, null, 2));
5194
+
5107
5195
  const session = {
5108
5196
  session_id,
5109
5197
  name,
5110
- knowledge_tier: 'corpus',
5111
- status: 'ready',
5198
+ status: 'waiting', // waiting = CLI not yet connected
5112
5199
  started_at: new Date().toISOString(),
5113
- context: preamble,
5114
- activeProc: null,
5115
5200
  is_chitchat: true,
5116
5201
  cwd: rootPath,
5202
+ sessionDir,
5203
+ turn: 0,
5204
+ lastResponse: undefined,
5205
+ lastResponseAt: undefined,
5117
5206
  };
5118
5207
  terminalSessions.set(session_id, session);
5119
- console.log(`[chitchat] started session ${session_id} name=${name} cwd=${rootPath}`);
5120
- return { session_id, status: 'ready', name, cwd: rootPath };
5208
+ console.log(`[chitchat] started session ${session_id} name=${name} dir=${sessionDir}`);
5209
+
5210
+ return {
5211
+ session_id,
5212
+ status: 'waiting',
5213
+ name,
5214
+ cwd: rootPath,
5215
+ next_step: `Open a terminal and run: /rdc:collab --session ${session_id}`,
5216
+ inbox: path.join(sessionDir, 'inbox'),
5217
+ outbox: path.join(sessionDir, 'outbox'),
5218
+ };
5121
5219
  }
5122
5220
 
5123
5221
  function sendChitchatMessage(session_id, message) {
@@ -5125,43 +5223,80 @@ function sendChitchatMessage(session_id, message) {
5125
5223
  if (!session) return { error: 'not_found', message: `Chitchat session ${session_id} not found` };
5126
5224
  if (!session.is_chitchat) return { error: 'wrong_type', message: 'Use terminal_send for non-chitchat sessions' };
5127
5225
  if (session.status === 'stopped') return { error: 'stopped', message: 'Session is stopped' };
5128
- if (session.status === 'busy') return { error: 'session_busy', message: 'Session is busy — try again shortly' };
5129
5226
 
5130
- const binary = findClaudeBinary();
5131
- if (!binary) return { error: 'binary_not_found', message: 'claude CLI not found in PATH or AppData/npm' };
5227
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
5228
+ session.turn = (session.turn || 0) + 1;
5229
+ const filename = `${ts}-turn${session.turn}.md`;
5230
+ const inboxPath = path.join(session.sessionDir, 'inbox', filename);
5231
+
5232
+ const content = [
5233
+ '---',
5234
+ `from: claude-ai`,
5235
+ `to: claude-code`,
5236
+ `session_id: ${session_id}`,
5237
+ `turn: ${session.turn}`,
5238
+ `sent_at: ${new Date().toISOString()}`,
5239
+ `status: pending`,
5240
+ '---',
5241
+ '',
5242
+ message,
5243
+ ].join('\n');
5244
+
5245
+ try {
5246
+ fs.writeFileSync(inboxPath, content, 'utf-8');
5247
+ } catch (err) {
5248
+ return { error: 'write_failed', message: `Could not write to inbox: ${err.message}` };
5249
+ }
5132
5250
 
5133
5251
  session.status = 'busy';
5134
- const fullPrompt = `${session.context}\n\n---\nFrom claude.ai: ${message}`;
5252
+ console.log(`[chitchat] session ${session_id} turn=${session.turn} message written to inbox`);
5253
+ return { queued: true, session_id, turn: session.turn, inbox_file: filename };
5254
+ }
5135
5255
 
5136
- const proc = spawnProc(binary, ['-p', fullPrompt, '--dangerously-skip-permissions'], {
5137
- cwd: session.cwd,
5138
- env: process.env,
5139
- stdio: ['ignore', 'pipe', 'pipe'],
5140
- shell: true,
5141
- });
5142
- session.activeProc = proc;
5256
+ function recvChitchatResponse(session_id) {
5257
+ const session = terminalSessions.get(session_id);
5258
+ if (!session) return { error: 'not_found', message: `Chitchat session ${session_id} not found` };
5143
5259
 
5144
- let stdout = '';
5145
- let stderr = '';
5146
- proc.stdout.on('data', d => { stdout += d; });
5147
- proc.stderr.on('data', d => { stderr += d; });
5148
- proc.on('close', (code) => {
5149
- if (terminalSessions.has(session_id)) {
5150
- const s = terminalSessions.get(session_id);
5151
- const response = stdout.trim() || stderr.trim() || '(no output)';
5152
- const turn = `\n\nFrom claude.ai: ${message}\nClaude Code: ${response}`;
5153
- const combined = s.context + turn;
5154
- s.context = combined.length > 8000 ? combined.slice(combined.length - 8000) : combined;
5155
- s.lastResponse = response;
5156
- s.lastResponseAt = new Date().toISOString();
5157
- s.turn = (s.turn || 0) + 1;
5158
- s.status = 'ready';
5159
- s.activeProc = null;
5160
- console.log(`[chitchat] session ${session_id} turn=${s.turn} complete code=${code}`);
5161
- }
5162
- });
5260
+ const outboxDir = path.join(session.sessionDir, 'outbox');
5261
+ let files;
5262
+ try {
5263
+ files = fs.readdirSync(outboxDir).filter(f => f.endsWith('.md')).sort();
5264
+ } catch {
5265
+ return { session_id, status: session.status, turn: session.turn || 0, response: null,
5266
+ message: 'Outbox not readable — is /rdc:collab running?' };
5267
+ }
5163
5268
 
5164
- return { queued: true, session_id };
5269
+ if (files.length === 0) {
5270
+ return { session_id, status: 'busy', turn: session.turn || 0, response: null,
5271
+ message: session.status === 'waiting'
5272
+ ? 'Waiting for /rdc:collab to connect — open your terminal and run it'
5273
+ : 'Claude Code is working — poll again in a few seconds' };
5274
+ }
5275
+
5276
+ // Read the latest response file
5277
+ const latest = files[files.length - 1];
5278
+ const responsePath = path.join(outboxDir, latest);
5279
+ let raw;
5280
+ try {
5281
+ raw = fs.readFileSync(responsePath, 'utf-8');
5282
+ } catch (err) {
5283
+ return { error: 'read_failed', message: `Could not read outbox: ${err.message}` };
5284
+ }
5285
+
5286
+ // Strip frontmatter, return body
5287
+ const body = raw.replace(/^---[\s\S]*?---\n?/, '').trim();
5288
+ const respondedAt = new Date().toISOString();
5289
+
5290
+ // Archive the file so next recv doesn't return it again
5291
+ const archivePath = responsePath + '.processed';
5292
+ try { fs.renameSync(responsePath, archivePath); } catch {}
5293
+
5294
+ session.status = 'ready';
5295
+ session.lastResponse = body;
5296
+ session.lastResponseAt = respondedAt;
5297
+ console.log(`[chitchat] session ${session_id} response received from outbox file=${latest}`);
5298
+
5299
+ return { session_id, status: 'ready', turn: session.turn || 0, response: body, responded_at: respondedAt };
5165
5300
  }
5166
5301
 
5167
5302
  function listChitchatSessions() {
@@ -5181,6 +5316,32 @@ function listChitchatSessions() {
5181
5316
  return sessions;
5182
5317
  }
5183
5318
 
5319
+ function stopChitchatSession(session_id) {
5320
+ const session = terminalSessions.get(session_id);
5321
+ if (!session) return { error: 'not_found', message: `Session ${session_id} not found` };
5322
+
5323
+ // Write a stop signal to inbox so the running /rdc:collab loop knows to exit
5324
+ try {
5325
+ const ts = new Date().toISOString().replace(/[:.]/g, '-');
5326
+ const stopFile = path.join(session.sessionDir, 'inbox', `${ts}-stop.md`);
5327
+ fs.writeFileSync(stopFile, [
5328
+ '---',
5329
+ 'from: claude-ai',
5330
+ 'to: claude-code',
5331
+ `session_id: ${session_id}`,
5332
+ 'type: stop',
5333
+ `sent_at: ${new Date().toISOString()}`,
5334
+ '---',
5335
+ '',
5336
+ 'Session ended by claude.ai. Write final summary and exit.',
5337
+ ].join('\n'));
5338
+ } catch {}
5339
+
5340
+ terminalSessions.delete(session_id);
5341
+ console.log(`[chitchat] stopped session ${session_id}`);
5342
+ return { stopped: true, session_id };
5343
+ }
5344
+
5184
5345
  const ENV_MAP = {
5185
5346
  "github": "GITHUB_TOKEN",
5186
5347
  "supabase-anon": "NEXT_PUBLIC_SUPABASE_ANON_KEY",
@@ -6248,7 +6409,7 @@ async function handleMcpTool(vault, name, args) {
6248
6409
  case "chitchat_recv": {
6249
6410
  const { session_id } = args;
6250
6411
  if (!session_id) return mcpError("session_id required");
6251
- const result = recvTerminalResponse(session_id);
6412
+ const result = recvChitchatResponse(session_id);
6252
6413
  if (result.error) return mcpError(`${result.error}: ${result.message}`);
6253
6414
  return mcpResult(JSON.stringify(result));
6254
6415
  }
@@ -6260,7 +6421,7 @@ async function handleMcpTool(vault, name, args) {
6260
6421
  case "chitchat_stop": {
6261
6422
  const { session_id } = args;
6262
6423
  if (!session_id) return mcpError("session_id required");
6263
- const result = stopTerminalSession(session_id);
6424
+ const result = stopChitchatSession(session_id);
6264
6425
  if (result.error) return mcpError(`${result.error}: ${result.message}`);
6265
6426
  return mcpResult(JSON.stringify(result));
6266
6427
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.43",
3
+ "version": "1.5.45",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {