@lifeaitools/clauth 1.5.67 → 1.5.68

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.
@@ -862,6 +862,11 @@ function dashboardHtml(port, whitelist, isStaged = false) {
862
862
  <span class="mcp-val" id="mcp-url">—</span>
863
863
  <button class="mcp-copy" onclick="copyMcp('mcp-url')">copy</button>
864
864
  </div>
865
+ <div class="mcp-row">
866
+ <span class="mcp-label">GWS</span>
867
+ <span class="mcp-val" id="mcp-gws-url">—</span>
868
+ <button class="mcp-copy" onclick="copyMcp('mcp-gws-url')">copy</button>
869
+ </div>
865
870
  <div class="mcp-row">
866
871
  <span class="mcp-label">Client ID</span>
867
872
  <span class="mcp-val" id="mcp-client-id">—</span>
@@ -1954,10 +1959,12 @@ async function toggleMcpSetup() {
1954
1959
  try {
1955
1960
  const m = await fetch(BASE + "/mcp-setup").then(r => r.json());
1956
1961
  document.getElementById("mcp-url").textContent = m.url || "(tunnel not running)";
1962
+ document.getElementById("mcp-gws-url").textContent = m.gwsUrl || "(tunnel not running)";
1957
1963
  document.getElementById("mcp-client-id").textContent = m.clientId || "—";
1958
1964
  document.getElementById("mcp-client-secret").textContent = m.clientSecret || "—";
1959
1965
  } catch {
1960
1966
  document.getElementById("mcp-url").textContent = "(error fetching)";
1967
+ document.getElementById("mcp-gws-url").textContent = "(error fetching)";
1961
1968
  }
1962
1969
  }
1963
1970
  }
@@ -2335,17 +2342,23 @@ async function wizRunCreateTunnel() {
2335
2342
  async function wizShowMcpSetup(hostname) {
2336
2343
  wizStep = "mcp_setup";
2337
2344
  const clauthUrl = \`https://\${hostname}/clauth\`;
2345
+ const gwsUrl = \`https://\${hostname}/gws\`;
2338
2346
 
2339
2347
  const mcpData = await apiFetch("/mcp-setup");
2340
2348
 
2341
2349
  renderWizBody(\`
2342
- <div class="wiz-desc">Add the Clauth MCP connector in claude.ai:</div>
2350
+ <div class="wiz-desc">Add two MCP connectors in claude.ai — one for Clauth, one for GWS:</div>
2343
2351
  <div style="display:flex;flex-direction:column;gap:8px;margin-top:10px">
2344
2352
  <div class="mcp-row">
2345
2353
  <span class="mcp-label">Clauth URL</span>
2346
2354
  <span class="mcp-val" id="wiz-mcp-url">\${clauthUrl}</span>
2347
2355
  <button class="mcp-copy" onclick="wizCopy('wiz-mcp-url',this)">copy</button>
2348
2356
  </div>
2357
+ <div class="mcp-row">
2358
+ <span class="mcp-label">GWS URL</span>
2359
+ <span class="mcp-val" id="wiz-mcp-gws">\${gwsUrl}</span>
2360
+ <button class="mcp-copy" onclick="wizCopy('wiz-mcp-gws',this)">copy</button>
2361
+ </div>
2349
2362
  <div class="mcp-row">
2350
2363
  <span class="mcp-label">Client ID</span>
2351
2364
  <span class="mcp-val" id="wiz-mcp-cid">\${mcpData?.clientId || '(unlock required)'}</span>
@@ -2940,7 +2953,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2940
2953
  if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
2941
2954
  const base = oauthBase();
2942
2955
  const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "");
2943
- const resourcePath = suffix && ["/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/sse";
2956
+ const resourcePath = suffix && ["/gws", "/clauth", "/mcp", "/fs", "/sse"].includes("/" + suffix) ? "/" + suffix : "/sse";
2944
2957
  res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store", ...CORS });
2945
2958
  return res.end(JSON.stringify({
2946
2959
  resource: `${base}${resourcePath}`,
@@ -3120,14 +3133,20 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3120
3133
  }
3121
3134
 
3122
3135
  // ── MCP path helpers ──
3123
- const MCP_PATHS = ["/mcp", "/clauth"];
3136
+ const MCP_PATHS = ["/mcp", "/gws", "/clauth", "/fs", "/chitchat"];
3124
3137
  const isMcpPath = MCP_PATHS.includes(reqPath);
3125
3138
  function toolsForPath(p) {
3139
+ if (p === "/gws") return MCP_TOOLS.filter(t => t.name.startsWith("gws_"));
3126
3140
  if (p === "/clauth") return MCP_TOOLS.filter(t => t.name.startsWith("clauth_") || t.name === "monkey_dispatch" || t.name.startsWith("terminal_") || t.name.startsWith("channel_"));
3141
+ if (p === "/fs") return MCP_TOOLS.filter(t => t.name.startsWith("fs_"));
3142
+ if (p === "/chitchat") return MCP_TOOLS.filter(t => t.name.startsWith("chitchat_"));
3127
3143
  return MCP_TOOLS; // /mcp — all tools
3128
3144
  }
3129
3145
  function serverNameForPath(p) {
3146
+ if (p === "/gws") return "gws";
3130
3147
  if (p === "/clauth") return "clauth";
3148
+ if (p === "/fs") return "fs";
3149
+ if (p === "/chitchat") return "chitchat";
3131
3150
  return "clauth";
3132
3151
  }
3133
3152
 
@@ -3392,6 +3411,90 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3392
3411
  });
3393
3412
  }
3394
3413
 
3414
+ // GET /chitchat/<session_id>/stream — SSE push stream for Claude Code (inbox events)
3415
+ // Claude Code connects once; daemon pushes an event on every chitchat_send call
3416
+ const inboxStreamMatch = reqPath.match(/^\/chitchat\/([^/]+)\/stream$/);
3417
+ if (method === "GET" && inboxStreamMatch) {
3418
+ const session_id = inboxStreamMatch[1];
3419
+ const session = terminalSessions.get(session_id);
3420
+ if (!session || !session.is_chitchat) {
3421
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
3422
+ return res.end(JSON.stringify({ error: "session not found" }));
3423
+ }
3424
+
3425
+ res.writeHead(200, {
3426
+ "Content-Type": "text/event-stream",
3427
+ "Cache-Control": "no-cache",
3428
+ "Connection": "keep-alive",
3429
+ ...CORS,
3430
+ });
3431
+
3432
+ // Send any already-queued inbox messages immediately
3433
+ if (session.inbox.length) {
3434
+ for (const entry of session.inbox) {
3435
+ res.write(`event: message\ndata: ${JSON.stringify(entry)}\n\n`);
3436
+ }
3437
+ session.inbox = [];
3438
+ }
3439
+
3440
+ session.inboxStreams.push(res);
3441
+ console.log(`[ClaudeAItoCLI] session ${session_id} inbox stream connected (${session.inboxStreams.length} total)`);
3442
+
3443
+ const keepalive = setInterval(() => {
3444
+ try { res.write(": keepalive\n\n"); } catch {}
3445
+ }, 15_000);
3446
+
3447
+ req.on("close", () => {
3448
+ clearInterval(keepalive);
3449
+ session.inboxStreams = session.inboxStreams.filter(r => r !== res);
3450
+ console.log(`[ClaudeAItoCLI] session ${session_id} inbox stream disconnected`);
3451
+ });
3452
+
3453
+ return;
3454
+ }
3455
+
3456
+ // GET /chitchat/<session_id>/watch — SSE push stream for claude.ai (outbox events)
3457
+ // claude.ai connects once; daemon pushes an event on every chitchat_reply call
3458
+ const outboxStreamMatch = reqPath.match(/^\/chitchat\/([^/]+)\/watch$/);
3459
+ if (method === "GET" && outboxStreamMatch) {
3460
+ const session_id = outboxStreamMatch[1];
3461
+ const session = terminalSessions.get(session_id);
3462
+ if (!session || !session.is_chitchat) {
3463
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
3464
+ return res.end(JSON.stringify({ error: "session not found" }));
3465
+ }
3466
+
3467
+ res.writeHead(200, {
3468
+ "Content-Type": "text/event-stream",
3469
+ "Cache-Control": "no-cache",
3470
+ "Connection": "keep-alive",
3471
+ ...CORS,
3472
+ });
3473
+
3474
+ // Send any already-queued outbox messages immediately
3475
+ if (session.outbox.length) {
3476
+ for (const entry of session.outbox) {
3477
+ res.write(`event: message\ndata: ${JSON.stringify(entry)}\n\n`);
3478
+ }
3479
+ session.outbox = [];
3480
+ }
3481
+
3482
+ session.outboxStreams.push(res);
3483
+ console.log(`[ClaudeAItoCLI] session ${session_id} outbox stream connected (${session.outboxStreams.length} total)`);
3484
+
3485
+ const keepalive = setInterval(() => {
3486
+ try { res.write(": keepalive\n\n"); } catch {}
3487
+ }, 15_000);
3488
+
3489
+ req.on("close", () => {
3490
+ clearInterval(keepalive);
3491
+ session.outboxStreams = session.outboxStreams.filter(r => r !== res);
3492
+ console.log(`[ClaudeAItoCLI] session ${session_id} outbox stream disconnected`);
3493
+ });
3494
+
3495
+ return;
3496
+ }
3497
+
3395
3498
  // GET /debug/sessions — live view of all terminal/chitchat sessions
3396
3499
  if (method === "GET" && reqPath === "/debug/sessions") {
3397
3500
  const now = Date.now();
@@ -3486,6 +3589,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3486
3589
  const base = tunnelUrl && tunnelUrl.startsWith("http") ? tunnelUrl : null;
3487
3590
  return ok(res, {
3488
3591
  url: base ? `${base}/clauth` : null,
3592
+ gwsUrl: base ? `${base}/gws` : null,
3489
3593
  note: "OAuth 2.1 public client — Claude registers dynamically, no client_secret needed",
3490
3594
  });
3491
3595
  }
@@ -3581,6 +3685,114 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3581
3685
  return;
3582
3686
  }
3583
3687
 
3688
+ // GET /chitchat/:id — session status (used by clauth chitchat CLI to verify session exists)
3689
+ const chitchatStatusMatch = reqPath.match(/^\/chitchat\/([^/]+)$/);
3690
+ if (method === "GET" && chitchatStatusMatch) {
3691
+ const sid = chitchatStatusMatch[1];
3692
+ const s = terminalSessions.get(sid);
3693
+ if (!s || !s.is_chitchat) {
3694
+ res.writeHead(404, { "Content-Type": "application/json", ...CORS });
3695
+ return res.end(JSON.stringify({ error: "not_found" }));
3696
+ }
3697
+ res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3698
+ return res.end(JSON.stringify({ session_id: sid, status: s.status, name: s.name, turn: s.turn }));
3699
+ }
3700
+
3701
+ // POST /chitchat/start — start a chitchat collab session (local REST, same as MCP chitchat_start)
3702
+ if (method === "POST" && reqPath === "/chitchat/start") {
3703
+ let body = "";
3704
+ req.on("data", d => body += d);
3705
+ req.on("end", async () => {
3706
+ try {
3707
+ const { name } = JSON.parse(body || "{}");
3708
+ const result = await startChitchatSession(name || "collab");
3709
+ res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3710
+ res.end(JSON.stringify(result));
3711
+ } catch (e) {
3712
+ res.writeHead(500, { "Content-Type": "application/json", ...CORS });
3713
+ res.end(JSON.stringify({ error: e.message }));
3714
+ }
3715
+ });
3716
+ return;
3717
+ }
3718
+
3719
+ // POST /terminal/start — start a named terminal session
3720
+ if (method === "POST" && reqPath === "/terminal/start") {
3721
+ let body = "";
3722
+ req.on("data", d => body += d);
3723
+ req.on("end", () => {
3724
+ try {
3725
+ const { name, knowledge_tier, context_md } = JSON.parse(body);
3726
+ if (!name) {
3727
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3728
+ return res.end(JSON.stringify({ error: "name required" }));
3729
+ }
3730
+ const tier = knowledge_tier || 'db_only';
3731
+ const result = startTerminalSession(name, tier, context_md || null);
3732
+ const status = result.error ? 503 : 200;
3733
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3734
+ res.end(JSON.stringify(result));
3735
+ } catch {
3736
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3737
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3738
+ }
3739
+ });
3740
+ return;
3741
+ }
3742
+
3743
+ // POST /terminal/send — send a message to a running terminal session
3744
+ if (method === "POST" && reqPath === "/terminal/send") {
3745
+ let body = "";
3746
+ req.on("data", d => body += d);
3747
+ req.on("end", () => {
3748
+ try {
3749
+ const { session_id, message } = JSON.parse(body);
3750
+ if (!session_id || !message) {
3751
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3752
+ return res.end(JSON.stringify({ error: "session_id and message required" }));
3753
+ }
3754
+ const result = sendTerminalMessage(session_id, message);
3755
+ const status = result.error === 'session_busy' ? 409 : result.error ? 404 : 200;
3756
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3757
+ res.end(JSON.stringify(result));
3758
+ } catch {
3759
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3760
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3761
+ }
3762
+ });
3763
+ return;
3764
+ }
3765
+
3766
+ // GET /terminal/list — list all active terminal sessions
3767
+ if (method === "GET" && reqPath === "/terminal/list") {
3768
+ res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3769
+ res.end(JSON.stringify(listTerminalSessions()));
3770
+ return;
3771
+ }
3772
+
3773
+ // POST /terminal/stop — stop a terminal session
3774
+ if (method === "POST" && reqPath === "/terminal/stop") {
3775
+ let body = "";
3776
+ req.on("data", d => body += d);
3777
+ req.on("end", () => {
3778
+ try {
3779
+ const { session_id } = JSON.parse(body);
3780
+ if (!session_id) {
3781
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3782
+ return res.end(JSON.stringify({ error: "session_id required" }));
3783
+ }
3784
+ const result = stopTerminalSession(session_id);
3785
+ const status = result.error ? 404 : 200;
3786
+ res.writeHead(status, { "Content-Type": "application/json", ...CORS });
3787
+ res.end(JSON.stringify(result));
3788
+ } catch {
3789
+ res.writeHead(400, { "Content-Type": "application/json", ...CORS });
3790
+ res.end(JSON.stringify({ error: "invalid JSON" }));
3791
+ }
3792
+ });
3793
+ return;
3794
+ }
3795
+
3584
3796
  // POST /channel — receive Coolify/GitHub webhook events
3585
3797
  if (method === "POST" && reqPath === "/channel") {
3586
3798
  let body = "";
@@ -5318,6 +5530,18 @@ function stopTerminalSession(session_id) {
5318
5530
  // Pure in-memory. No files. No CLI spawning on send/recv.
5319
5531
 
5320
5532
  const CHITCHAT_CWD = 'C:/Dev/regen-root';
5533
+ const CHITCHAT_FALLBACK_CWD = CHITCHAT_CWD;
5534
+
5535
+ // Resolve the working directory for Claude CLI tasks.
5536
+ // Uses the first fileserver mount from vault (if unlocked and configured),
5537
+ // falling back to CHITCHAT_CWD so the daemon always has a usable cwd.
5538
+ async function resolveChitchatRoot(vault) {
5539
+ try {
5540
+ const { mounts } = await getFileserverMounts(vault);
5541
+ if (mounts && mounts.length > 0 && mounts[0].path) return mounts[0].path;
5542
+ } catch {}
5543
+ return CHITCHAT_CWD;
5544
+ }
5321
5545
 
5322
5546
  async function startChitchatSession(name) {
5323
5547
  const session_id = generateSessionId();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.67",
3
+ "version": "1.5.68",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {