@lifeaitools/clauth 1.5.39 → 1.5.41
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/cli/commands/serve.js +300 -1
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -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,134 @@ 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.lastResponse = response;
|
|
5013
|
+
s.lastResponseAt = new Date().toISOString();
|
|
5014
|
+
s.turn = (s.turn || 0) + 1;
|
|
5015
|
+
s.status = 'ready';
|
|
5016
|
+
s.activeProc = null;
|
|
5017
|
+
console.log(`[terminal] session ${session_id} turn=${s.turn} complete code=${code}`);
|
|
5018
|
+
}
|
|
5019
|
+
});
|
|
5020
|
+
|
|
5021
|
+
return { queued: true, session_id };
|
|
5022
|
+
}
|
|
5023
|
+
|
|
5024
|
+
function recvTerminalResponse(session_id, timeout_ms = 30000) {
|
|
5025
|
+
const session = terminalSessions.get(session_id);
|
|
5026
|
+
if (!session) return { error: 'not_found', message: `Session ${session_id} not found` };
|
|
5027
|
+
|
|
5028
|
+
// If already ready with a response, return it immediately
|
|
5029
|
+
if (session.status === 'ready' && session.lastResponse !== undefined) {
|
|
5030
|
+
return {
|
|
5031
|
+
session_id,
|
|
5032
|
+
status: 'ready',
|
|
5033
|
+
turn: session.turn || 0,
|
|
5034
|
+
response: session.lastResponse,
|
|
5035
|
+
responded_at: session.lastResponseAt,
|
|
5036
|
+
};
|
|
5037
|
+
}
|
|
5038
|
+
|
|
5039
|
+
// Session is still busy — caller should poll again
|
|
5040
|
+
return {
|
|
5041
|
+
session_id,
|
|
5042
|
+
status: session.status,
|
|
5043
|
+
turn: session.turn || 0,
|
|
5044
|
+
response: null,
|
|
5045
|
+
message: session.status === 'busy' ? 'Still processing — poll again in a few seconds' : 'No response yet',
|
|
5046
|
+
};
|
|
5047
|
+
}
|
|
5048
|
+
|
|
5049
|
+
function listTerminalSessions() {
|
|
5050
|
+
const sessions = [];
|
|
5051
|
+
for (const [, s] of terminalSessions) {
|
|
5052
|
+
sessions.push({
|
|
5053
|
+
session_id: s.session_id,
|
|
5054
|
+
name: s.name,
|
|
5055
|
+
status: s.status,
|
|
5056
|
+
knowledge_tier: s.knowledge_tier,
|
|
5057
|
+
started_at: s.started_at,
|
|
5058
|
+
});
|
|
5059
|
+
}
|
|
5060
|
+
return sessions;
|
|
5061
|
+
}
|
|
5062
|
+
|
|
5063
|
+
function stopTerminalSession(session_id) {
|
|
5064
|
+
const session = terminalSessions.get(session_id);
|
|
5065
|
+
if (!session) return { error: 'not_found', message: `Session ${session_id} not found` };
|
|
5066
|
+
if (session.activeProc) {
|
|
5067
|
+
try { session.activeProc.kill(); } catch {}
|
|
5068
|
+
}
|
|
5069
|
+
terminalSessions.delete(session_id);
|
|
5070
|
+
console.log(`[terminal] stopped session ${session_id}`);
|
|
5071
|
+
return { stopped: true, session_id };
|
|
5072
|
+
}
|
|
5073
|
+
|
|
4869
5074
|
const ENV_MAP = {
|
|
4870
5075
|
"github": "GITHUB_TOKEN",
|
|
4871
5076
|
"supabase-anon": "NEXT_PUBLIC_SUPABASE_ANON_KEY",
|
|
@@ -5046,6 +5251,64 @@ const MCP_TOOLS = [
|
|
|
5046
5251
|
},
|
|
5047
5252
|
},
|
|
5048
5253
|
|
|
5254
|
+
// ── Terminal session manager ───────────────────────────────────────────
|
|
5255
|
+
{
|
|
5256
|
+
name: "terminal_start",
|
|
5257
|
+
description: "Start a named persistent Claude session. Sessions maintain rolling context across sends. Returns session_id for subsequent sends.",
|
|
5258
|
+
inputSchema: {
|
|
5259
|
+
type: "object",
|
|
5260
|
+
properties: {
|
|
5261
|
+
name: { type: "string", description: "Human-readable session name (e.g. 'knowledgedude-prt')" },
|
|
5262
|
+
knowledge_tier: { type: "string", enum: ["db_only", "corpus", "project"], description: "Knowledge tier for the session. 'corpus' injects context_md as preamble." },
|
|
5263
|
+
context_md: { type: "string", description: "Optional markdown preamble injected as session context (used for 'corpus' tier)" },
|
|
5264
|
+
},
|
|
5265
|
+
required: ["name"],
|
|
5266
|
+
additionalProperties: false,
|
|
5267
|
+
},
|
|
5268
|
+
},
|
|
5269
|
+
{
|
|
5270
|
+
name: "terminal_send",
|
|
5271
|
+
description: "Send a message to a running terminal session. Spawns a Claude worker with rolling context + message. Returns immediately — session processes async.",
|
|
5272
|
+
inputSchema: {
|
|
5273
|
+
type: "object",
|
|
5274
|
+
properties: {
|
|
5275
|
+
session_id: { type: "string", description: "Session ID returned by terminal_start" },
|
|
5276
|
+
message: { type: "string", description: "Message to send to the session" },
|
|
5277
|
+
},
|
|
5278
|
+
required: ["session_id", "message"],
|
|
5279
|
+
additionalProperties: false,
|
|
5280
|
+
},
|
|
5281
|
+
},
|
|
5282
|
+
{
|
|
5283
|
+
name: "terminal_list",
|
|
5284
|
+
description: "List all active terminal sessions with their status (ready/busy/stopped), knowledge tier, and start time.",
|
|
5285
|
+
inputSchema: { type: "object", properties: {}, additionalProperties: false },
|
|
5286
|
+
},
|
|
5287
|
+
{
|
|
5288
|
+
name: "terminal_recv",
|
|
5289
|
+
description: "Read the last response from a terminal session. Returns response if ready, or status='busy' if still processing. Poll every 3-5 seconds until status='ready'.",
|
|
5290
|
+
inputSchema: {
|
|
5291
|
+
type: "object",
|
|
5292
|
+
properties: {
|
|
5293
|
+
session_id: { type: "string", description: "Session ID returned by terminal_start" },
|
|
5294
|
+
},
|
|
5295
|
+
required: ["session_id"],
|
|
5296
|
+
additionalProperties: false,
|
|
5297
|
+
},
|
|
5298
|
+
},
|
|
5299
|
+
{
|
|
5300
|
+
name: "terminal_stop",
|
|
5301
|
+
description: "Stop a terminal session and remove it from memory. Kills any active process.",
|
|
5302
|
+
inputSchema: {
|
|
5303
|
+
type: "object",
|
|
5304
|
+
properties: {
|
|
5305
|
+
session_id: { type: "string", description: "Session ID to stop" },
|
|
5306
|
+
},
|
|
5307
|
+
required: ["session_id"],
|
|
5308
|
+
additionalProperties: false,
|
|
5309
|
+
},
|
|
5310
|
+
},
|
|
5311
|
+
|
|
5049
5312
|
// ── Google Workspace (gws CLI) ──────────────────────────────────────────
|
|
5050
5313
|
{
|
|
5051
5314
|
name: "gws_run",
|
|
@@ -5762,6 +6025,42 @@ async function handleMcpTool(vault, name, args) {
|
|
|
5762
6025
|
return mcpResult(JSON.stringify(result));
|
|
5763
6026
|
}
|
|
5764
6027
|
|
|
6028
|
+
case "terminal_start": {
|
|
6029
|
+
const { name, knowledge_tier, context_md } = args;
|
|
6030
|
+
if (!name) return mcpError("name required");
|
|
6031
|
+
const result = startTerminalSession(name, knowledge_tier || 'db_only', context_md || null);
|
|
6032
|
+
if (result.error) return mcpError(`${result.error}: ${result.message}`);
|
|
6033
|
+
return mcpResult(JSON.stringify(result));
|
|
6034
|
+
}
|
|
6035
|
+
|
|
6036
|
+
case "terminal_send": {
|
|
6037
|
+
const { session_id, message } = args;
|
|
6038
|
+
if (!session_id || !message) return mcpError("session_id and message required");
|
|
6039
|
+
const result = sendTerminalMessage(session_id, message);
|
|
6040
|
+
if (result.error) return mcpError(`${result.error}: ${result.message}`);
|
|
6041
|
+
return mcpResult(JSON.stringify(result));
|
|
6042
|
+
}
|
|
6043
|
+
|
|
6044
|
+
case "terminal_list": {
|
|
6045
|
+
return mcpResult(JSON.stringify(listTerminalSessions()));
|
|
6046
|
+
}
|
|
6047
|
+
|
|
6048
|
+
case "terminal_recv": {
|
|
6049
|
+
const { session_id } = args;
|
|
6050
|
+
if (!session_id) return mcpError("session_id required");
|
|
6051
|
+
const result = recvTerminalResponse(session_id);
|
|
6052
|
+
if (result.error) return mcpError(`${result.error}: ${result.message}`);
|
|
6053
|
+
return mcpResult(JSON.stringify(result));
|
|
6054
|
+
}
|
|
6055
|
+
|
|
6056
|
+
case "terminal_stop": {
|
|
6057
|
+
const { session_id } = args;
|
|
6058
|
+
if (!session_id) return mcpError("session_id required");
|
|
6059
|
+
const result = stopTerminalSession(session_id);
|
|
6060
|
+
if (result.error) return mcpError(`${result.error}: ${result.message}`);
|
|
6061
|
+
return mcpResult(JSON.stringify(result));
|
|
6062
|
+
}
|
|
6063
|
+
|
|
5765
6064
|
default:
|
|
5766
6065
|
return mcpError(`Unknown tool: ${name}`);
|
|
5767
6066
|
}
|