@lifeaitools/clauth 1.5.39 → 1.5.40

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.
@@ -3119,7 +3119,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3119
3119
  const isMcpPath = MCP_PATHS.includes(reqPath);
3120
3120
  function toolsForPath(p) {
3121
3121
  if (p === "/gws") return MCP_TOOLS.filter(t => t.name.startsWith("gws_"));
3122
- if (p === "/clauth") return MCP_TOOLS.filter(t => t.name.startsWith("clauth_") || t.name === "monkey_dispatch");
3122
+ if (p === "/clauth") return MCP_TOOLS.filter(t => t.name.startsWith("clauth_") || t.name === "monkey_dispatch" || t.name.startsWith("terminal_"));
3123
3123
  if (p === "/fs") return MCP_TOOLS.filter(t => t.name.startsWith("fs_"));
3124
3124
  return MCP_TOOLS; // /mcp — all tools
3125
3125
  }
@@ -3494,6 +3494,83 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3494
3494
  return;
3495
3495
  }
3496
3496
 
3497
+ // POST /terminal/start — start a named terminal session
3498
+ if (method === "POST" && reqPath === "/terminal/start") {
3499
+ let body = "";
3500
+ req.on("data", d => body += d);
3501
+ req.on("end", () => {
3502
+ try {
3503
+ const { name, knowledge_tier, context_md } = JSON.parse(body);
3504
+ if (!name) {
3505
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3506
+ return res.end(JSON.stringify({ error: "name required" }));
3507
+ }
3508
+ const tier = knowledge_tier || 'db_only';
3509
+ const result = startTerminalSession(name, tier, context_md || null);
3510
+ const status = result.error ? 503 : 200;
3511
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3512
+ res.end(JSON.stringify(result));
3513
+ } catch {
3514
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3515
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3516
+ }
3517
+ });
3518
+ return;
3519
+ }
3520
+
3521
+ // POST /terminal/send — send a message to a running terminal session
3522
+ if (method === "POST" && reqPath === "/terminal/send") {
3523
+ let body = "";
3524
+ req.on("data", d => body += d);
3525
+ req.on("end", () => {
3526
+ try {
3527
+ const { session_id, message } = JSON.parse(body);
3528
+ if (!session_id || !message) {
3529
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3530
+ return res.end(JSON.stringify({ error: "session_id and message required" }));
3531
+ }
3532
+ const result = sendTerminalMessage(session_id, message);
3533
+ const status = result.error === 'session_busy' ? 409 : result.error ? 404 : 200;
3534
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3535
+ res.end(JSON.stringify(result));
3536
+ } catch {
3537
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3538
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3539
+ }
3540
+ });
3541
+ return;
3542
+ }
3543
+
3544
+ // GET /terminal/list — list all active terminal sessions
3545
+ if (method === "GET" && reqPath === "/terminal/list") {
3546
+ res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3547
+ res.end(JSON.stringify(listTerminalSessions()));
3548
+ return;
3549
+ }
3550
+
3551
+ // POST /terminal/stop — stop a terminal session
3552
+ if (method === "POST" && reqPath === "/terminal/stop") {
3553
+ let body = "";
3554
+ req.on("data", d => body += d);
3555
+ req.on("end", () => {
3556
+ try {
3557
+ const { session_id } = JSON.parse(body);
3558
+ if (!session_id) {
3559
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3560
+ return res.end(JSON.stringify({ error: "session_id required" }));
3561
+ }
3562
+ const result = stopTerminalSession(session_id);
3563
+ const status = result.error ? 404 : 200;
3564
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3565
+ res.end(JSON.stringify(result));
3566
+ } catch {
3567
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3568
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3569
+ }
3570
+ });
3571
+ return;
3572
+ }
3573
+
3497
3574
  // GET|POST /shutdown (for daemon stop — programmatic, keeps boot.key)
3498
3575
  // Accept POST as well — older scripts and curl default to POST
3499
3576
  if ((method === "GET" || method === "POST") && reqPath === "/shutdown") {
@@ -4866,6 +4943,106 @@ function spawnClaudeTask(prompt, jobId) {
4866
4943
  return { status: 'spawned', pid: proc.pid, jobId, activeWorkers: activeCliWorkers };
4867
4944
  }
4868
4945
 
4946
+ // ── Terminal session manager ─────────────────────────────────────
4947
+ // Rolling-context approach: each session stores a context string.
4948
+ // /terminal/send spawns a fresh claude -p with [context + message],
4949
+ // captures stdout, stores result back in session context.
4950
+ const terminalSessions = new Map(); // session_id → SessionState
4951
+
4952
+ function generateSessionId() {
4953
+ return crypto.randomUUID();
4954
+ }
4955
+
4956
+ function startTerminalSession(name, knowledge_tier, context_md) {
4957
+ const binary = findClaudeBinary();
4958
+ if (!binary) {
4959
+ return { error: 'binary_not_found', message: 'claude CLI not found in PATH or AppData/npm' };
4960
+ }
4961
+ const session_id = generateSessionId();
4962
+ const preamble = context_md
4963
+ ? `${context_md}\n\n---\n`
4964
+ : '';
4965
+ const initialContext = `${preamble}Session: ${name} | Tier: ${knowledge_tier}\nReady. Await instructions.`;
4966
+ const session = {
4967
+ session_id,
4968
+ name,
4969
+ knowledge_tier,
4970
+ status: 'ready',
4971
+ started_at: new Date().toISOString(),
4972
+ context: initialContext,
4973
+ activeProc: null,
4974
+ };
4975
+ terminalSessions.set(session_id, session);
4976
+ console.log(`[terminal] started session ${session_id} name=${name}`);
4977
+ return { session_id, status: 'ready' };
4978
+ }
4979
+
4980
+ function sendTerminalMessage(session_id, message) {
4981
+ const session = terminalSessions.get(session_id);
4982
+ if (!session) return { error: 'not_found', message: `Session ${session_id} not found` };
4983
+ if (session.status === 'stopped') return { error: 'stopped', message: 'Session is stopped' };
4984
+ if (session.status === 'busy') return { error: 'session_busy', message: 'Session is busy — try again shortly' };
4985
+
4986
+ const binary = findClaudeBinary();
4987
+ if (!binary) return { error: 'binary_not_found', message: 'claude CLI not found in PATH or AppData/npm' };
4988
+
4989
+ session.status = 'busy';
4990
+ const fullPrompt = `${session.context}\n\n---\nUser: ${message}`;
4991
+
4992
+ const proc = spawnProc(binary, ['-p', fullPrompt, '--dangerously-skip-permissions'], {
4993
+ cwd: 'C:/Dev/regen-root',
4994
+ env: process.env,
4995
+ stdio: ['ignore', 'pipe', 'pipe'],
4996
+ shell: true,
4997
+ });
4998
+ session.activeProc = proc;
4999
+
5000
+ let stdout = '';
5001
+ let stderr = '';
5002
+ proc.stdout.on('data', d => { stdout += d; });
5003
+ proc.stderr.on('data', d => { stderr += d; });
5004
+ proc.on('close', (code) => {
5005
+ if (terminalSessions.has(session_id)) {
5006
+ const s = terminalSessions.get(session_id);
5007
+ const response = stdout.trim() || stderr.trim() || '(no output)';
5008
+ // Append turn to rolling context (cap at ~8000 chars to avoid overflow)
5009
+ const turn = `\n\nUser: ${message}\nAssistant: ${response}`;
5010
+ const combined = s.context + turn;
5011
+ s.context = combined.length > 8000 ? combined.slice(combined.length - 8000) : combined;
5012
+ s.status = 'ready';
5013
+ s.activeProc = null;
5014
+ console.log(`[terminal] session ${session_id} turn complete code=${code}`);
5015
+ }
5016
+ });
5017
+
5018
+ return { queued: true, session_id };
5019
+ }
5020
+
5021
+ function listTerminalSessions() {
5022
+ const sessions = [];
5023
+ for (const [, s] of terminalSessions) {
5024
+ sessions.push({
5025
+ session_id: s.session_id,
5026
+ name: s.name,
5027
+ status: s.status,
5028
+ knowledge_tier: s.knowledge_tier,
5029
+ started_at: s.started_at,
5030
+ });
5031
+ }
5032
+ return sessions;
5033
+ }
5034
+
5035
+ function stopTerminalSession(session_id) {
5036
+ const session = terminalSessions.get(session_id);
5037
+ if (!session) return { error: 'not_found', message: `Session ${session_id} not found` };
5038
+ if (session.activeProc) {
5039
+ try { session.activeProc.kill(); } catch {}
5040
+ }
5041
+ terminalSessions.delete(session_id);
5042
+ console.log(`[terminal] stopped session ${session_id}`);
5043
+ return { stopped: true, session_id };
5044
+ }
5045
+
4869
5046
  const ENV_MAP = {
4870
5047
  "github": "GITHUB_TOKEN",
4871
5048
  "supabase-anon": "NEXT_PUBLIC_SUPABASE_ANON_KEY",
@@ -5046,6 +5223,52 @@ const MCP_TOOLS = [
5046
5223
  },
5047
5224
  },
5048
5225
 
5226
+ // ── Terminal session manager ───────────────────────────────────────────
5227
+ {
5228
+ name: "terminal_start",
5229
+ description: "Start a named persistent Claude session. Sessions maintain rolling context across sends. Returns session_id for subsequent sends.",
5230
+ inputSchema: {
5231
+ type: "object",
5232
+ properties: {
5233
+ name: { type: "string", description: "Human-readable session name (e.g. 'knowledgedude-prt')" },
5234
+ knowledge_tier: { type: "string", enum: ["db_only", "corpus", "project"], description: "Knowledge tier for the session. 'corpus' injects context_md as preamble." },
5235
+ context_md: { type: "string", description: "Optional markdown preamble injected as session context (used for 'corpus' tier)" },
5236
+ },
5237
+ required: ["name"],
5238
+ additionalProperties: false,
5239
+ },
5240
+ },
5241
+ {
5242
+ name: "terminal_send",
5243
+ description: "Send a message to a running terminal session. Spawns a Claude worker with rolling context + message. Returns immediately — session processes async.",
5244
+ inputSchema: {
5245
+ type: "object",
5246
+ properties: {
5247
+ session_id: { type: "string", description: "Session ID returned by terminal_start" },
5248
+ message: { type: "string", description: "Message to send to the session" },
5249
+ },
5250
+ required: ["session_id", "message"],
5251
+ additionalProperties: false,
5252
+ },
5253
+ },
5254
+ {
5255
+ name: "terminal_list",
5256
+ description: "List all active terminal sessions with their status (ready/busy/stopped), knowledge tier, and start time.",
5257
+ inputSchema: { type: "object", properties: {}, additionalProperties: false },
5258
+ },
5259
+ {
5260
+ name: "terminal_stop",
5261
+ description: "Stop a terminal session and remove it from memory. Kills any active process.",
5262
+ inputSchema: {
5263
+ type: "object",
5264
+ properties: {
5265
+ session_id: { type: "string", description: "Session ID to stop" },
5266
+ },
5267
+ required: ["session_id"],
5268
+ additionalProperties: false,
5269
+ },
5270
+ },
5271
+
5049
5272
  // ── Google Workspace (gws CLI) ──────────────────────────────────────────
5050
5273
  {
5051
5274
  name: "gws_run",
@@ -5762,6 +5985,34 @@ async function handleMcpTool(vault, name, args) {
5762
5985
  return mcpResult(JSON.stringify(result));
5763
5986
  }
5764
5987
 
5988
+ case "terminal_start": {
5989
+ const { name, knowledge_tier, context_md } = args;
5990
+ if (!name) return mcpError("name required");
5991
+ const result = startTerminalSession(name, knowledge_tier || 'db_only', context_md || null);
5992
+ if (result.error) return mcpError(`${result.error}: ${result.message}`);
5993
+ return mcpResult(JSON.stringify(result));
5994
+ }
5995
+
5996
+ case "terminal_send": {
5997
+ const { session_id, message } = args;
5998
+ if (!session_id || !message) return mcpError("session_id and message required");
5999
+ const result = sendTerminalMessage(session_id, message);
6000
+ if (result.error) return mcpError(`${result.error}: ${result.message}`);
6001
+ return mcpResult(JSON.stringify(result));
6002
+ }
6003
+
6004
+ case "terminal_list": {
6005
+ return mcpResult(JSON.stringify(listTerminalSessions()));
6006
+ }
6007
+
6008
+ case "terminal_stop": {
6009
+ const { session_id } = args;
6010
+ if (!session_id) return mcpError("session_id required");
6011
+ const result = stopTerminalSession(session_id);
6012
+ if (result.error) return mcpError(`${result.error}: ${result.message}`);
6013
+ return mcpResult(JSON.stringify(result));
6014
+ }
6015
+
5765
6016
  default:
5766
6017
  return mcpError(`Unknown tool: ${name}`);
5767
6018
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.39",
3
+ "version": "1.5.40",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {