@lifeaitools/clauth 1.5.65 → 1.5.67
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 +15 -222
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -862,11 +862,6 @@ 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>
|
|
870
865
|
<div class="mcp-row">
|
|
871
866
|
<span class="mcp-label">Client ID</span>
|
|
872
867
|
<span class="mcp-val" id="mcp-client-id">—</span>
|
|
@@ -1959,12 +1954,10 @@ async function toggleMcpSetup() {
|
|
|
1959
1954
|
try {
|
|
1960
1955
|
const m = await fetch(BASE + "/mcp-setup").then(r => r.json());
|
|
1961
1956
|
document.getElementById("mcp-url").textContent = m.url || "(tunnel not running)";
|
|
1962
|
-
document.getElementById("mcp-gws-url").textContent = m.gwsUrl || "(tunnel not running)";
|
|
1963
1957
|
document.getElementById("mcp-client-id").textContent = m.clientId || "—";
|
|
1964
1958
|
document.getElementById("mcp-client-secret").textContent = m.clientSecret || "—";
|
|
1965
1959
|
} catch {
|
|
1966
1960
|
document.getElementById("mcp-url").textContent = "(error fetching)";
|
|
1967
|
-
document.getElementById("mcp-gws-url").textContent = "(error fetching)";
|
|
1968
1961
|
}
|
|
1969
1962
|
}
|
|
1970
1963
|
}
|
|
@@ -2342,23 +2335,17 @@ async function wizRunCreateTunnel() {
|
|
|
2342
2335
|
async function wizShowMcpSetup(hostname) {
|
|
2343
2336
|
wizStep = "mcp_setup";
|
|
2344
2337
|
const clauthUrl = \`https://\${hostname}/clauth\`;
|
|
2345
|
-
const gwsUrl = \`https://\${hostname}/gws\`;
|
|
2346
2338
|
|
|
2347
2339
|
const mcpData = await apiFetch("/mcp-setup");
|
|
2348
2340
|
|
|
2349
2341
|
renderWizBody(\`
|
|
2350
|
-
<div class="wiz-desc">Add
|
|
2342
|
+
<div class="wiz-desc">Add the Clauth MCP connector in claude.ai:</div>
|
|
2351
2343
|
<div style="display:flex;flex-direction:column;gap:8px;margin-top:10px">
|
|
2352
2344
|
<div class="mcp-row">
|
|
2353
2345
|
<span class="mcp-label">Clauth URL</span>
|
|
2354
2346
|
<span class="mcp-val" id="wiz-mcp-url">\${clauthUrl}</span>
|
|
2355
2347
|
<button class="mcp-copy" onclick="wizCopy('wiz-mcp-url',this)">copy</button>
|
|
2356
2348
|
</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>
|
|
2362
2349
|
<div class="mcp-row">
|
|
2363
2350
|
<span class="mcp-label">Client ID</span>
|
|
2364
2351
|
<span class="mcp-val" id="wiz-mcp-cid">\${mcpData?.clientId || '(unlock required)'}</span>
|
|
@@ -2953,7 +2940,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
2953
2940
|
if (reqPath.startsWith("/.well-known/oauth-protected-resource")) {
|
|
2954
2941
|
const base = oauthBase();
|
|
2955
2942
|
const suffix = reqPath.replace("/.well-known/oauth-protected-resource", "").replace(/^\//, "");
|
|
2956
|
-
const resourcePath = suffix && ["/
|
|
2943
|
+
const resourcePath = suffix && ["/clauth", "/mcp", "/sse"].includes("/" + suffix) ? "/" + suffix : "/sse";
|
|
2957
2944
|
res.writeHead(200, { "Content-Type": "application/json", "Cache-Control": "no-store", ...CORS });
|
|
2958
2945
|
return res.end(JSON.stringify({
|
|
2959
2946
|
resource: `${base}${resourcePath}`,
|
|
@@ -3133,20 +3120,14 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3133
3120
|
}
|
|
3134
3121
|
|
|
3135
3122
|
// ── MCP path helpers ──
|
|
3136
|
-
const MCP_PATHS = ["/mcp", "/
|
|
3123
|
+
const MCP_PATHS = ["/mcp", "/clauth"];
|
|
3137
3124
|
const isMcpPath = MCP_PATHS.includes(reqPath);
|
|
3138
3125
|
function toolsForPath(p) {
|
|
3139
|
-
if (p === "/gws") return MCP_TOOLS.filter(t => t.name.startsWith("gws_"));
|
|
3140
3126
|
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_"));
|
|
3143
3127
|
return MCP_TOOLS; // /mcp — all tools
|
|
3144
3128
|
}
|
|
3145
3129
|
function serverNameForPath(p) {
|
|
3146
|
-
if (p === "/gws") return "gws";
|
|
3147
3130
|
if (p === "/clauth") return "clauth";
|
|
3148
|
-
if (p === "/fs") return "fs";
|
|
3149
|
-
if (p === "/chitchat") return "chitchat";
|
|
3150
3131
|
return "clauth";
|
|
3151
3132
|
}
|
|
3152
3133
|
|
|
@@ -3178,7 +3159,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3178
3159
|
}
|
|
3179
3160
|
|
|
3180
3161
|
// ── MCP Streamable HTTP transport ──
|
|
3181
|
-
// POST /sse, /mcp, /
|
|
3162
|
+
// POST /sse, /mcp, /clauth — JSON-RPC over HTTP
|
|
3182
3163
|
// claude.ai requires text/event-stream SSE format (like regen-media/Express).
|
|
3183
3164
|
if (method === "POST" && (reqPath === "/sse" || isMcpPath)) {
|
|
3184
3165
|
let body;
|
|
@@ -3251,12 +3232,17 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3251
3232
|
}
|
|
3252
3233
|
|
|
3253
3234
|
// ── MCP SSE transport — /sse and namespaced paths ────
|
|
3254
|
-
// GET /sse|/
|
|
3255
|
-
// Remote clients (claude.ai)
|
|
3256
|
-
//
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3235
|
+
// GET /sse|/clauth — open SSE stream, receive endpoint event
|
|
3236
|
+
// Remote clients (claude.ai) arrive with a Bearer token via OAuth.
|
|
3237
|
+
// They use Streamable HTTP (POST only) — return 405 on GET so claude.ai
|
|
3238
|
+
// knows to POST directly. Local clients have no Bearer token and use SSE.
|
|
3239
|
+
if (method === "GET" && isMcpPath) {
|
|
3240
|
+
const getAuthHeader = req.headers.authorization;
|
|
3241
|
+
const getToken = getAuthHeader?.startsWith("Bearer ") ? getAuthHeader.slice(7) : null;
|
|
3242
|
+
if (!noAuthHost && getToken && oauthTokens.has(getToken)) {
|
|
3243
|
+
res.writeHead(405, { "Content-Type": "application/json", "Allow": "POST", ...CORS });
|
|
3244
|
+
return res.end(JSON.stringify({ error: "Method Not Allowed", detail: "Use POST for Streamable HTTP transport" }));
|
|
3245
|
+
}
|
|
3260
3246
|
}
|
|
3261
3247
|
if (method === "GET" && (reqPath === "/sse" || isMcpPath)) {
|
|
3262
3248
|
const sessionId = `ses_${++sseCounter}_${Date.now()}`;
|
|
@@ -3406,90 +3392,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3406
3392
|
});
|
|
3407
3393
|
}
|
|
3408
3394
|
|
|
3409
|
-
// GET /chitchat/<session_id>/stream — SSE push stream for Claude Code (inbox events)
|
|
3410
|
-
// Claude Code connects once; daemon pushes an event on every chitchat_send call
|
|
3411
|
-
const inboxStreamMatch = reqPath.match(/^\/chitchat\/([^/]+)\/stream$/);
|
|
3412
|
-
if (method === "GET" && inboxStreamMatch) {
|
|
3413
|
-
const session_id = inboxStreamMatch[1];
|
|
3414
|
-
const session = terminalSessions.get(session_id);
|
|
3415
|
-
if (!session || !session.is_chitchat) {
|
|
3416
|
-
res.writeHead(404, { "Content-Type": "application/json", ...CORS });
|
|
3417
|
-
return res.end(JSON.stringify({ error: "session not found" }));
|
|
3418
|
-
}
|
|
3419
|
-
|
|
3420
|
-
res.writeHead(200, {
|
|
3421
|
-
"Content-Type": "text/event-stream",
|
|
3422
|
-
"Cache-Control": "no-cache",
|
|
3423
|
-
"Connection": "keep-alive",
|
|
3424
|
-
...CORS,
|
|
3425
|
-
});
|
|
3426
|
-
|
|
3427
|
-
// Send any already-queued inbox messages immediately
|
|
3428
|
-
if (session.inbox.length) {
|
|
3429
|
-
for (const entry of session.inbox) {
|
|
3430
|
-
res.write(`event: message\ndata: ${JSON.stringify(entry)}\n\n`);
|
|
3431
|
-
}
|
|
3432
|
-
session.inbox = [];
|
|
3433
|
-
}
|
|
3434
|
-
|
|
3435
|
-
session.inboxStreams.push(res);
|
|
3436
|
-
console.log(`[ClaudeAItoCLI] session ${session_id} inbox stream connected (${session.inboxStreams.length} total)`);
|
|
3437
|
-
|
|
3438
|
-
const keepalive = setInterval(() => {
|
|
3439
|
-
try { res.write(": keepalive\n\n"); } catch {}
|
|
3440
|
-
}, 15_000);
|
|
3441
|
-
|
|
3442
|
-
req.on("close", () => {
|
|
3443
|
-
clearInterval(keepalive);
|
|
3444
|
-
session.inboxStreams = session.inboxStreams.filter(r => r !== res);
|
|
3445
|
-
console.log(`[ClaudeAItoCLI] session ${session_id} inbox stream disconnected`);
|
|
3446
|
-
});
|
|
3447
|
-
|
|
3448
|
-
return;
|
|
3449
|
-
}
|
|
3450
|
-
|
|
3451
|
-
// GET /chitchat/<session_id>/watch — SSE push stream for claude.ai (outbox events)
|
|
3452
|
-
// claude.ai connects once; daemon pushes an event on every chitchat_reply call
|
|
3453
|
-
const outboxStreamMatch = reqPath.match(/^\/chitchat\/([^/]+)\/watch$/);
|
|
3454
|
-
if (method === "GET" && outboxStreamMatch) {
|
|
3455
|
-
const session_id = outboxStreamMatch[1];
|
|
3456
|
-
const session = terminalSessions.get(session_id);
|
|
3457
|
-
if (!session || !session.is_chitchat) {
|
|
3458
|
-
res.writeHead(404, { "Content-Type": "application/json", ...CORS });
|
|
3459
|
-
return res.end(JSON.stringify({ error: "session not found" }));
|
|
3460
|
-
}
|
|
3461
|
-
|
|
3462
|
-
res.writeHead(200, {
|
|
3463
|
-
"Content-Type": "text/event-stream",
|
|
3464
|
-
"Cache-Control": "no-cache",
|
|
3465
|
-
"Connection": "keep-alive",
|
|
3466
|
-
...CORS,
|
|
3467
|
-
});
|
|
3468
|
-
|
|
3469
|
-
// Send any already-queued outbox messages immediately
|
|
3470
|
-
if (session.outbox.length) {
|
|
3471
|
-
for (const entry of session.outbox) {
|
|
3472
|
-
res.write(`event: message\ndata: ${JSON.stringify(entry)}\n\n`);
|
|
3473
|
-
}
|
|
3474
|
-
session.outbox = [];
|
|
3475
|
-
}
|
|
3476
|
-
|
|
3477
|
-
session.outboxStreams.push(res);
|
|
3478
|
-
console.log(`[ClaudeAItoCLI] session ${session_id} outbox stream connected (${session.outboxStreams.length} total)`);
|
|
3479
|
-
|
|
3480
|
-
const keepalive = setInterval(() => {
|
|
3481
|
-
try { res.write(": keepalive\n\n"); } catch {}
|
|
3482
|
-
}, 15_000);
|
|
3483
|
-
|
|
3484
|
-
req.on("close", () => {
|
|
3485
|
-
clearInterval(keepalive);
|
|
3486
|
-
session.outboxStreams = session.outboxStreams.filter(r => r !== res);
|
|
3487
|
-
console.log(`[ClaudeAItoCLI] session ${session_id} outbox stream disconnected`);
|
|
3488
|
-
});
|
|
3489
|
-
|
|
3490
|
-
return;
|
|
3491
|
-
}
|
|
3492
|
-
|
|
3493
3395
|
// GET /debug/sessions — live view of all terminal/chitchat sessions
|
|
3494
3396
|
if (method === "GET" && reqPath === "/debug/sessions") {
|
|
3495
3397
|
const now = Date.now();
|
|
@@ -3584,7 +3486,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3584
3486
|
const base = tunnelUrl && tunnelUrl.startsWith("http") ? tunnelUrl : null;
|
|
3585
3487
|
return ok(res, {
|
|
3586
3488
|
url: base ? `${base}/clauth` : null,
|
|
3587
|
-
gwsUrl: base ? `${base}/gws` : null,
|
|
3588
3489
|
note: "OAuth 2.1 public client — Claude registers dynamically, no client_secret needed",
|
|
3589
3490
|
});
|
|
3590
3491
|
}
|
|
@@ -3680,114 +3581,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3680
3581
|
return;
|
|
3681
3582
|
}
|
|
3682
3583
|
|
|
3683
|
-
// GET /chitchat/:id — session status (used by clauth chitchat CLI to verify session exists)
|
|
3684
|
-
const chitchatStatusMatch = reqPath.match(/^\/chitchat\/([^/]+)$/);
|
|
3685
|
-
if (method === "GET" && chitchatStatusMatch) {
|
|
3686
|
-
const sid = chitchatStatusMatch[1];
|
|
3687
|
-
const s = terminalSessions.get(sid);
|
|
3688
|
-
if (!s || !s.is_chitchat) {
|
|
3689
|
-
res.writeHead(404, { "Content-Type": "application/json", ...CORS });
|
|
3690
|
-
return res.end(JSON.stringify({ error: "not_found" }));
|
|
3691
|
-
}
|
|
3692
|
-
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3693
|
-
return res.end(JSON.stringify({ session_id: sid, status: s.status, name: s.name, turn: s.turn }));
|
|
3694
|
-
}
|
|
3695
|
-
|
|
3696
|
-
// POST /chitchat/start — start a chitchat collab session (local REST, same as MCP chitchat_start)
|
|
3697
|
-
if (method === "POST" && reqPath === "/chitchat/start") {
|
|
3698
|
-
let body = "";
|
|
3699
|
-
req.on("data", d => body += d);
|
|
3700
|
-
req.on("end", async () => {
|
|
3701
|
-
try {
|
|
3702
|
-
const { name } = JSON.parse(body || "{}");
|
|
3703
|
-
const result = await startChitchatSession(name || "collab");
|
|
3704
|
-
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3705
|
-
res.end(JSON.stringify(result));
|
|
3706
|
-
} catch (e) {
|
|
3707
|
-
res.writeHead(500, { "Content-Type": "application/json", ...CORS });
|
|
3708
|
-
res.end(JSON.stringify({ error: e.message }));
|
|
3709
|
-
}
|
|
3710
|
-
});
|
|
3711
|
-
return;
|
|
3712
|
-
}
|
|
3713
|
-
|
|
3714
|
-
// POST /terminal/start — start a named terminal session
|
|
3715
|
-
if (method === "POST" && reqPath === "/terminal/start") {
|
|
3716
|
-
let body = "";
|
|
3717
|
-
req.on("data", d => body += d);
|
|
3718
|
-
req.on("end", () => {
|
|
3719
|
-
try {
|
|
3720
|
-
const { name, knowledge_tier, context_md } = JSON.parse(body);
|
|
3721
|
-
if (!name) {
|
|
3722
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3723
|
-
return res.end(JSON.stringify({ error: "name required" }));
|
|
3724
|
-
}
|
|
3725
|
-
const tier = knowledge_tier || 'db_only';
|
|
3726
|
-
const result = startTerminalSession(name, tier, context_md || null);
|
|
3727
|
-
const status = result.error ? 503 : 200;
|
|
3728
|
-
res.writeHead(status, { "Content-Type": "application/json", ...CORS });
|
|
3729
|
-
res.end(JSON.stringify(result));
|
|
3730
|
-
} catch {
|
|
3731
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3732
|
-
res.end(JSON.stringify({ error: "invalid JSON" }));
|
|
3733
|
-
}
|
|
3734
|
-
});
|
|
3735
|
-
return;
|
|
3736
|
-
}
|
|
3737
|
-
|
|
3738
|
-
// POST /terminal/send — send a message to a running terminal session
|
|
3739
|
-
if (method === "POST" && reqPath === "/terminal/send") {
|
|
3740
|
-
let body = "";
|
|
3741
|
-
req.on("data", d => body += d);
|
|
3742
|
-
req.on("end", () => {
|
|
3743
|
-
try {
|
|
3744
|
-
const { session_id, message } = JSON.parse(body);
|
|
3745
|
-
if (!session_id || !message) {
|
|
3746
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3747
|
-
return res.end(JSON.stringify({ error: "session_id and message required" }));
|
|
3748
|
-
}
|
|
3749
|
-
const result = sendTerminalMessage(session_id, message);
|
|
3750
|
-
const status = result.error === 'session_busy' ? 409 : result.error ? 404 : 200;
|
|
3751
|
-
res.writeHead(status, { "Content-Type": "application/json", ...CORS });
|
|
3752
|
-
res.end(JSON.stringify(result));
|
|
3753
|
-
} catch {
|
|
3754
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3755
|
-
res.end(JSON.stringify({ error: "invalid JSON" }));
|
|
3756
|
-
}
|
|
3757
|
-
});
|
|
3758
|
-
return;
|
|
3759
|
-
}
|
|
3760
|
-
|
|
3761
|
-
// GET /terminal/list — list all active terminal sessions
|
|
3762
|
-
if (method === "GET" && reqPath === "/terminal/list") {
|
|
3763
|
-
res.writeHead(200, { "Content-Type": "application/json", ...CORS });
|
|
3764
|
-
res.end(JSON.stringify(listTerminalSessions()));
|
|
3765
|
-
return;
|
|
3766
|
-
}
|
|
3767
|
-
|
|
3768
|
-
// POST /terminal/stop — stop a terminal session
|
|
3769
|
-
if (method === "POST" && reqPath === "/terminal/stop") {
|
|
3770
|
-
let body = "";
|
|
3771
|
-
req.on("data", d => body += d);
|
|
3772
|
-
req.on("end", () => {
|
|
3773
|
-
try {
|
|
3774
|
-
const { session_id } = JSON.parse(body);
|
|
3775
|
-
if (!session_id) {
|
|
3776
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3777
|
-
return res.end(JSON.stringify({ error: "session_id required" }));
|
|
3778
|
-
}
|
|
3779
|
-
const result = stopTerminalSession(session_id);
|
|
3780
|
-
const status = result.error ? 404 : 200;
|
|
3781
|
-
res.writeHead(status, { "Content-Type": "application/json", ...CORS });
|
|
3782
|
-
res.end(JSON.stringify(result));
|
|
3783
|
-
} catch {
|
|
3784
|
-
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
3785
|
-
res.end(JSON.stringify({ error: "invalid JSON" }));
|
|
3786
|
-
}
|
|
3787
|
-
});
|
|
3788
|
-
return;
|
|
3789
|
-
}
|
|
3790
|
-
|
|
3791
3584
|
// POST /channel — receive Coolify/GitHub webhook events
|
|
3792
3585
|
if (method === "POST" && reqPath === "/channel") {
|
|
3793
3586
|
let body = "";
|