@lifeaitools/clauth 1.5.58 → 1.5.60
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 +93 -1
- package/package.json +1 -1
package/cli/commands/serve.js
CHANGED
|
@@ -4807,6 +4807,39 @@ function createServer(initPassword, whitelist, port, tunnelHostnameInit = null,
|
|
|
4807
4807
|
}
|
|
4808
4808
|
}
|
|
4809
4809
|
|
|
4810
|
+
// POST /generate-token — generate a cryptographically random bearer token and store it
|
|
4811
|
+
if (method === "POST" && reqPath === "/generate-token") {
|
|
4812
|
+
if (lockedGuard(res)) return;
|
|
4813
|
+
|
|
4814
|
+
let body;
|
|
4815
|
+
try { body = await readBody(req); } catch {
|
|
4816
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
4817
|
+
return res.end(JSON.stringify({ error: "Invalid JSON body" }));
|
|
4818
|
+
}
|
|
4819
|
+
|
|
4820
|
+
const service = (body.service || "").trim().toLowerCase();
|
|
4821
|
+
const prefix = body.prefix || "";
|
|
4822
|
+
if (!service) {
|
|
4823
|
+
res.writeHead(400, { "Content-Type": "application/json", ...CORS });
|
|
4824
|
+
return res.end(JSON.stringify({ error: "service name required" }));
|
|
4825
|
+
}
|
|
4826
|
+
|
|
4827
|
+
if (whitelist && !whitelist.includes(service)) {
|
|
4828
|
+
return strike(res, 403, `Service '${service}' not in whitelist`);
|
|
4829
|
+
}
|
|
4830
|
+
|
|
4831
|
+
try {
|
|
4832
|
+
const randomHex = crypto.randomBytes(32).toString("hex");
|
|
4833
|
+
const token = `${prefix}${randomHex}`;
|
|
4834
|
+
const { token: authToken, timestamp } = deriveToken(password, machineHash);
|
|
4835
|
+
const result = await api.write(password, machineHash, authToken, timestamp, service, token);
|
|
4836
|
+
if (result.error) return strike(res, 502, result.error);
|
|
4837
|
+
return ok(res, { token, service, stored: true });
|
|
4838
|
+
} catch (err) {
|
|
4839
|
+
return strike(res, 502, err.message);
|
|
4840
|
+
}
|
|
4841
|
+
}
|
|
4842
|
+
|
|
4810
4843
|
// POST /add-service — register a new service in the vault
|
|
4811
4844
|
if (method === "POST" && reqPath === "/add-service") {
|
|
4812
4845
|
if (lockedGuard(res)) return;
|
|
@@ -4902,7 +4935,36 @@ async function verifyAuth(password) {
|
|
|
4902
4935
|
async function actionStart(opts) {
|
|
4903
4936
|
const isStaged = !!opts.staged || process.env.__CLAUTH_STAGED === "1";
|
|
4904
4937
|
const port = isStaged ? STAGED_PORT : parseInt(opts.port || String(LIVE_PORT), 10);
|
|
4905
|
-
|
|
4938
|
+
let password = opts.pw || null;
|
|
4939
|
+
|
|
4940
|
+
// Auto-unlock: if no --pw flag, try to decrypt boot.key (DPAPI on Windows, openssl on Linux)
|
|
4941
|
+
if (!password) {
|
|
4942
|
+
const bootKeyPath = getBootKeyPath();
|
|
4943
|
+
if (bootKeyPath && fs.existsSync(bootKeyPath)) {
|
|
4944
|
+
try {
|
|
4945
|
+
if (os.platform() === "win32") {
|
|
4946
|
+
const psExe = process.env.SystemRoot
|
|
4947
|
+
? `${process.env.SystemRoot}\\System32\\WindowsPowerShell\\v1.0\\powershell.exe`
|
|
4948
|
+
: "powershell.exe";
|
|
4949
|
+
const escaped = bootKeyPath.replace(/'/g, "''");
|
|
4950
|
+
const psExpr = `Add-Type -AssemblyName System.Security; [Text.Encoding]::UTF8.GetString([Security.Cryptography.ProtectedData]::Unprotect([Convert]::FromBase64String((Get-Content '${escaped}' -Raw).Trim()),\$null,'CurrentUser'))`;
|
|
4951
|
+
password = execSyncTop(`"${psExe}" -NoProfile -Command "${psExpr}"`, { encoding: "utf8", timeout: 5000 }).trim();
|
|
4952
|
+
if (!password) password = null;
|
|
4953
|
+
} else if (os.platform() !== "darwin") {
|
|
4954
|
+
// Linux: openssl decrypt with machine-id
|
|
4955
|
+
const machineId = execSyncTop("cat /etc/machine-id", { encoding: "utf8", timeout: 3000 }).trim();
|
|
4956
|
+
password = execSyncTop(`openssl enc -aes-256-cbc -pbkdf2 -iter 100000 -pass pass:"${machineId}" -base64 -d < "${bootKeyPath}"`, { encoding: "utf8", timeout: 5000 }).trim();
|
|
4957
|
+
if (!password) password = null;
|
|
4958
|
+
}
|
|
4959
|
+
if (password) {
|
|
4960
|
+
console.log(chalk.green(" \u2713 Auto-unlocked from boot.key"));
|
|
4961
|
+
}
|
|
4962
|
+
} catch {
|
|
4963
|
+
password = null; // decrypt failed — fall through to locked mode
|
|
4964
|
+
}
|
|
4965
|
+
}
|
|
4966
|
+
}
|
|
4967
|
+
|
|
4906
4968
|
const tunnelHostname = opts.tunnel || null;
|
|
4907
4969
|
const whitelist = opts.services
|
|
4908
4970
|
? opts.services.split(",").map(s => s.trim().toLowerCase())
|
|
@@ -5709,6 +5771,19 @@ const MCP_TOOLS = [
|
|
|
5709
5771
|
description: "Test whether the clauth MCP connector is reachable via the Cloudflare tunnel. Returns connectivity status and tunnel URL.",
|
|
5710
5772
|
inputSchema: { type: "object", properties: {}, additionalProperties: false }
|
|
5711
5773
|
},
|
|
5774
|
+
{
|
|
5775
|
+
name: "clauth_generate_token",
|
|
5776
|
+
description: "Generate a cryptographically random bearer token and store it as a clauth service value",
|
|
5777
|
+
inputSchema: {
|
|
5778
|
+
type: "object",
|
|
5779
|
+
properties: {
|
|
5780
|
+
service: { type: "string", description: "Service name to store the token under (e.g. web-research-api)" },
|
|
5781
|
+
prefix: { type: "string", description: "Token prefix (e.g. rdc_wr_). Default: empty string" }
|
|
5782
|
+
},
|
|
5783
|
+
required: ["service"],
|
|
5784
|
+
additionalProperties: false
|
|
5785
|
+
}
|
|
5786
|
+
},
|
|
5712
5787
|
{
|
|
5713
5788
|
name: "monkey_dispatch",
|
|
5714
5789
|
description: "Dispatch a skill job to a headless Claude Code CLI worker. Spawns claude -p with the given prompt in C:/Dev/regen-root. Max 2 concurrent workers.",
|
|
@@ -6365,6 +6440,23 @@ async function handleMcpTool(vault, name, args) {
|
|
|
6365
6440
|
return mcpResult(results.join("\n"));
|
|
6366
6441
|
}
|
|
6367
6442
|
|
|
6443
|
+
case "clauth_generate_token": {
|
|
6444
|
+
if (!vault.password) return mcpError("Vault is locked — call clauth_unlock first");
|
|
6445
|
+
const service = (args.service || "").trim().toLowerCase();
|
|
6446
|
+
const prefix = args.prefix || "";
|
|
6447
|
+
if (!service) return mcpError("service name is required");
|
|
6448
|
+
try {
|
|
6449
|
+
const randomHex = crypto.randomBytes(32).toString("hex");
|
|
6450
|
+
const generatedToken = `${prefix}${randomHex}`;
|
|
6451
|
+
const { token: authToken, timestamp } = deriveToken(vault.password, vault.machineHash);
|
|
6452
|
+
const result = await api.write(vault.password, vault.machineHash, authToken, timestamp, service, generatedToken);
|
|
6453
|
+
if (result.error) return mcpError(result.error);
|
|
6454
|
+
return mcpResult(`Token generated and stored under "${service}": ${generatedToken}`);
|
|
6455
|
+
} catch (err) {
|
|
6456
|
+
return mcpError(`generate_token failed: ${err.message}`);
|
|
6457
|
+
}
|
|
6458
|
+
}
|
|
6459
|
+
|
|
6368
6460
|
// ── Google Workspace (gws CLI) ───────────────────────────────────────
|
|
6369
6461
|
case "gws_run": {
|
|
6370
6462
|
const { service, resource, sub_resource, method, params, body } = args;
|