@lifeaitools/clauth 1.5.6 β†’ 1.5.8

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.
@@ -739,6 +739,7 @@ function dashboardHtml(port, whitelist, isStaged = false) {
739
739
  <button class="btn-add" onclick="toggleAddService()">+ Add Service</button>
740
740
  <button class="btn-check" id="check-btn" onclick="checkAll()">⬀ Check All</button>
741
741
  <button class="btn-lock" onclick="lockVault()">πŸ”’ Lock</button>
742
+ <button class="btn-stop" onclick="restartDaemon()" style="background:#1a2e1a;border:1px solid #166534;color:#86efac" title="Restart daemon β€” keeps vault unlocked">β†Ί Restart</button>
742
743
  <button class="btn-stop" onclick="stopDaemon()" style="background:#7f1d1d;border:1px solid #991b1b;color:#fca5a5" title="Stop daemon β€” password required on next start">⏹ Stop</button>
743
744
  <button class="btn-cancel" style="margin-left:auto" onclick="toggleChangePw()">Change Password</button>
744
745
  </div>
@@ -1131,6 +1132,17 @@ async function makeLive() {
1131
1132
  }
1132
1133
  }
1133
1134
 
1135
+ // ── Restart daemon (keeps boot.key β€” vault stays unlocked) ──
1136
+ async function restartDaemon() {
1137
+ if (!confirm("Restart the daemon?\\n\\nThe vault will stay unlocked (boot.key kept).")) return;
1138
+ const btn = document.querySelector('[onclick="restartDaemon()"]');
1139
+ if (btn) { btn.disabled = true; btn.textContent = "β†Ί Restarting…"; }
1140
+ try {
1141
+ await fetch(BASE + "/restart", { method: "POST" });
1142
+ } catch {}
1143
+ document.getElementById("main-view").innerHTML = '<div style="text-align:center;padding:80px 20px"><div style="font-size:3rem;margin-bottom:16px">β†Ί</div><div style="font-size:1.1rem;color:#86efac">Restarting…</div><div style="font-size:.85rem;color:#64748b;margin-top:8px">Page will reload automatically.</div></div>';
1144
+ }
1145
+
1134
1146
  // ── Stop daemon (user-initiated β€” clears boot.key, requires password on restart) ──
1135
1147
  async function stopDaemon() {
1136
1148
  if (!confirm("Stop the daemon?\\n\\nCredentials will need to be re-entered on next start.")) return;
@@ -2988,9 +3000,23 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
2988
3000
  return res.end(JSON.stringify({ access_token: accessToken, token_type: "Bearer", expires_in: 86400 }));
2989
3001
  }
2990
3002
 
3003
+ // ── MCP path helpers ──
3004
+ const MCP_PATHS = ["/mcp", "/gws", "/clauth"];
3005
+ const isMcpPath = MCP_PATHS.includes(reqPath);
3006
+ function toolsForPath(p) {
3007
+ if (p === "/gws") return MCP_TOOLS.filter(t => t.name.startsWith("gws_"));
3008
+ if (p === "/clauth") return MCP_TOOLS.filter(t => t.name.startsWith("clauth_"));
3009
+ return MCP_TOOLS; // /mcp β€” all tools
3010
+ }
3011
+ function serverNameForPath(p) {
3012
+ if (p === "/gws") return "gws";
3013
+ if (p === "/clauth") return "clauth";
3014
+ return "clauth";
3015
+ }
3016
+
2991
3017
  // ── MCP OAuth-protected endpoint (for claude.ai web) ──
2992
- // POST /mcp β€” requires Bearer token; returns 401 to trigger OAuth flow
2993
- if (method === "POST" && reqPath === "/mcp") {
3018
+ // POST /mcp|/gws|/clauth β€” requires Bearer token; returns 401 to trigger OAuth flow
3019
+ if (method === "POST" && isMcpPath) {
2994
3020
  const authHeader = req.headers.authorization;
2995
3021
  if (!authHeader || !authHeader.startsWith("Bearer ")) {
2996
3022
  const base = oauthBase();
@@ -3012,8 +3038,8 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3012
3038
  }
3013
3039
 
3014
3040
  // ── MCP Streamable HTTP transport (2025-03-26 spec) ──
3015
- // POST /sse or POST /mcp β€” JSON-RPC over HTTP
3016
- if (method === "POST" && (reqPath === "/sse" || reqPath === "/mcp")) {
3041
+ // POST /sse, /mcp, /gws, /clauth β€” JSON-RPC over HTTP
3042
+ if (method === "POST" && (reqPath === "/sse" || isMcpPath)) {
3017
3043
  let body;
3018
3044
  try { body = await readBody(req); } catch {
3019
3045
  res.writeHead(400, { "Content-Type": "application/json", ...CORS });
@@ -3022,7 +3048,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3022
3048
 
3023
3049
  const id = body.id;
3024
3050
  const rpcMethod = body.method;
3025
- const logMsg = `[${new Date().toISOString()}] Streamable HTTP: ${rpcMethod} id=${id}\n`;
3051
+ const logMsg = `[${new Date().toISOString()}] Streamable HTTP [${reqPath}]: ${rpcMethod} id=${id}\n`;
3026
3052
  try { fs.appendFileSync(LOG_FILE, logMsg); } catch {}
3027
3053
 
3028
3054
  // Notifications β€” no response needed
@@ -3034,7 +3060,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3034
3060
  if (rpcMethod === "initialize") {
3035
3061
  const result = {
3036
3062
  protocolVersion: "2025-03-26",
3037
- serverInfo: { name: "clauth", version: VERSION },
3063
+ serverInfo: { name: serverNameForPath(reqPath), version: VERSION },
3038
3064
  capabilities: { tools: {} }
3039
3065
  };
3040
3066
  res.writeHead(200, { "Content-Type": "application/json", ...CORS });
@@ -3043,7 +3069,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3043
3069
 
3044
3070
  if (rpcMethod === "tools/list") {
3045
3071
  res.writeHead(200, { "Content-Type": "application/json", ...CORS });
3046
- return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: MCP_TOOLS } }));
3072
+ return res.end(JSON.stringify({ jsonrpc: "2.0", id, result: { tools: toolsForPath(reqPath) } }));
3047
3073
  }
3048
3074
 
3049
3075
  if (rpcMethod === "tools/call") {
@@ -3272,6 +3298,28 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
3272
3298
  return ok(res, { status: tunnelStatus });
3273
3299
  }
3274
3300
 
3301
+ // POST /restart β€” spawn fresh process then exit (keeps boot.key, vault stays unlocked)
3302
+ if (method === "POST" && reqPath === "/restart") {
3303
+ ok(res, { ok: true, message: "restarting" });
3304
+ const { spawn } = await import("child_process");
3305
+ const cliEntry = path.resolve(__dirname, "../index.js");
3306
+ const childArgs = [cliEntry, "serve", "start", "--port", String(port)];
3307
+ if (password) childArgs.push("--pw", password);
3308
+ if (whitelist) childArgs.push("--services", whitelist.join(","));
3309
+ if (tunnelHostname) childArgs.push("--tunnel", tunnelHostname);
3310
+ const out = fs.openSync(LOG_FILE, "a");
3311
+ const child = spawn(process.execPath, childArgs, {
3312
+ detached: true,
3313
+ stdio: ["ignore", out, out],
3314
+ env: { ...process.env, __CLAUTH_DAEMON: "1" },
3315
+ });
3316
+ child.unref();
3317
+ stopTunnel();
3318
+ removePid();
3319
+ setTimeout(() => process.exit(0), 300);
3320
+ return;
3321
+ }
3322
+
3275
3323
  // GET|POST /shutdown (for daemon stop β€” programmatic, keeps boot.key)
3276
3324
  // Accept POST as well β€” older scripts and curl default to POST
3277
3325
  if ((method === "GET" || method === "POST") && reqPath === "/shutdown") {
@@ -4287,7 +4335,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
4287
4335
  // Don't count browser noise, MCP discovery probes, or OAuth probes as auth failures
4288
4336
  const isBenign = reqPath.startsWith("/.well-known/") || [
4289
4337
  "/favicon.ico", "/robots.txt", "/apple-touch-icon.png", "/apple-touch-icon-precomposed.png",
4290
- "/sse", "/mcp", "/message", "/register", "/authorize", "/token", "/shutdown",
4338
+ "/sse", "/mcp", "/gws", "/clauth", "/message", "/register", "/authorize", "/token", "/shutdown", "/restart",
4291
4339
  ].includes(reqPath);
4292
4340
  if (isBenign) {
4293
4341
  res.writeHead(404, { "Content-Type": "application/json", ...CORS });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.6",
3
+ "version": "1.5.8",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {