@lifeaitools/clauth 1.5.66 → 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 +5 -217
- 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,7 +3232,7 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3251
3232
|
}
|
|
3252
3233
|
|
|
3253
3234
|
// ── MCP SSE transport — /sse and namespaced paths ────
|
|
3254
|
-
// GET /sse|/
|
|
3235
|
+
// GET /sse|/clauth — open SSE stream, receive endpoint event
|
|
3255
3236
|
// Remote clients (claude.ai) arrive with a Bearer token via OAuth.
|
|
3256
3237
|
// They use Streamable HTTP (POST only) — return 405 on GET so claude.ai
|
|
3257
3238
|
// knows to POST directly. Local clients have no Bearer token and use SSE.
|
|
@@ -3411,90 +3392,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3411
3392
|
});
|
|
3412
3393
|
}
|
|
3413
3394
|
|
|
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
|
-
|
|
3498
3395
|
// GET /debug/sessions — live view of all terminal/chitchat sessions
|
|
3499
3396
|
if (method === "GET" && reqPath === "/debug/sessions") {
|
|
3500
3397
|
const now = Date.now();
|
|
@@ -3589,7 +3486,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3589
3486
|
const base = tunnelUrl && tunnelUrl.startsWith("http") ? tunnelUrl : null;
|
|
3590
3487
|
return ok(res, {
|
|
3591
3488
|
url: base ? `${base}/clauth` : null,
|
|
3592
|
-
gwsUrl: base ? `${base}/gws` : null,
|
|
3593
3489
|
note: "OAuth 2.1 public client — Claude registers dynamically, no client_secret needed",
|
|
3594
3490
|
});
|
|
3595
3491
|
}
|
|
@@ -3685,114 +3581,6 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
3685
3581
|
return;
|
|
3686
3582
|
}
|
|
3687
3583
|
|
|
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
|
-
|
|
3796
3584
|
// POST /channel — receive Coolify/GitHub webhook events
|
|
3797
3585
|
if (method === "POST" && reqPath === "/channel") {
|
|
3798
3586
|
let body = "";
|