@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.
@@ -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
- const password = opts.pw;
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.5.58",
3
+ "version": "1.5.60",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {