@lifeaitools/clauth 1.30.13 → 1.30.14

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.
@@ -0,0 +1,82 @@
1
+ import fs from "fs";
2
+ import os from "os";
3
+ import path from "path";
4
+
5
+ function shellSingleQuote(value) {
6
+ return `'${String(value ?? "").replace(/'/g, "''")}'`;
7
+ }
8
+
9
+ function posixShellQuote(value) {
10
+ return `'${String(value ?? "").replace(/'/g, "'\\\"'\\\"'")}'`;
11
+ }
12
+
13
+ export function enrollmentScriptName(label, target = "windows") {
14
+ const slug = String(label || "new-computer")
15
+ .toLowerCase()
16
+ .replace(/[^a-z0-9]+/g, "-")
17
+ .replace(/^-+|-+$/g, "")
18
+ .slice(0, 40) || "new-computer";
19
+ return `clauth-enroll-${slug}${target === "linux" ? ".sh" : ".ps1"}`;
20
+ }
21
+
22
+ function windowsScript({ supabaseUrl, anonKey, enrollmentCode }) {
23
+ return [
24
+ "$ErrorActionPreference = 'Stop'",
25
+ "$label = $env:COMPUTERNAME",
26
+ "if (-not $label) { $label = [System.Net.Dns]::GetHostName() }",
27
+ "npm install -g @lifeaitools/clauth@latest",
28
+ ["clauth setup", `--supabase-url ${shellSingleQuote(supabaseUrl)}`, `--anon-key ${shellSingleQuote(anonKey)}`, `--enrollment-code ${shellSingleQuote(enrollmentCode)}`, "--label \"$label\""].join(" "),
29
+ "clauth serve install",
30
+ "$self = $PSCommandPath",
31
+ "Start-Process powershell.exe -WindowStyle Hidden -ArgumentList @('-NoProfile','-Command',\"Start-Sleep -Seconds 2; & ('Remove' + '-Item') -LiteralPath '$self' -Force -ErrorAction SilentlyContinue\")",
32
+ ].join("\r\n");
33
+ }
34
+
35
+ function linuxScript({ supabaseUrl, anonKey, enrollmentCode }) {
36
+ return [
37
+ "#!/usr/bin/env sh",
38
+ "set -eu",
39
+ "label=$(hostname)",
40
+ "as_root() { if [ \"$(id -u)\" -eq 0 ]; then \"$@\"; elif command -v sudo >/dev/null; then sudo \"$@\"; else echo 'Root or sudo is required to install prerequisites.' >&2; exit 1; fi; }",
41
+ "install_prerequisites() {",
42
+ " if command -v apt-get >/dev/null; then as_root apt-get update; as_root apt-get install -y nodejs npm openssl;",
43
+ " elif command -v dnf >/dev/null; then as_root dnf install -y nodejs npm openssl;",
44
+ " else echo 'Headless enrollment supports systemd hosts with apt-get or dnf. Install Node.js 18+, npm, and openssl, then rerun this script.' >&2; exit 1; fi",
45
+ "}",
46
+ "if ! command -v systemctl >/dev/null || ! command -v loginctl >/dev/null; then echo 'Headless enrollment requires systemd and loginctl.' >&2; exit 1; fi",
47
+ "if ! command -v node >/dev/null || ! command -v npm >/dev/null || ! command -v openssl >/dev/null; then install_prerequisites; fi",
48
+ "node_major=$(node -p \"process.versions.node.split('.')[0]\")",
49
+ "if [ \"$node_major\" -lt 18 ]; then echo 'Node.js 18+ is required.' >&2; exit 1; fi",
50
+ "if ! npm install -g @lifeaitools/clauth@latest; then as_root npm install -g @lifeaitools/clauth@latest; fi",
51
+ ["clauth setup", `--supabase-url ${posixShellQuote(supabaseUrl)}`, `--anon-key ${posixShellQuote(anonKey)}`, `--enrollment-code ${posixShellQuote(enrollmentCode)}`, '--label "$label"'].join(" "),
52
+ "user_name=$(id -un)",
53
+ "if loginctl enable-linger \"$user_name\"; then echo \"Linger enabled for $user_name.\"; elif as_root loginctl enable-linger \"$user_name\"; then echo \"Linger enabled for $user_name.\"; else echo 'Could not enable linger for unattended restart.' >&2; exit 1; fi",
54
+ "if ! test -t 0; then echo 'Headless enrollment requires an interactive TTY to set the vault password.' >&2; exit 1; fi",
55
+ "restore_echo() { stty echo 2>/dev/null || true; }",
56
+ "trap restore_echo EXIT HUP INT TERM",
57
+ "printf 'Re-enter the vault password to enable unattended restart: ' >&2",
58
+ "stty -echo",
59
+ "IFS= read -r vault_password",
60
+ "stty echo",
61
+ "trap - EXIT HUP INT TERM",
62
+ "printf '\\n' >&2",
63
+ "[ -n \"$vault_password\" ] || { echo 'A vault password is required for unattended restart.' >&2; exit 1; }",
64
+ "printf %s \"$vault_password\" | clauth serve install --pw-stdin",
65
+ "unset vault_password",
66
+ "rm -f -- \"$0\"",
67
+ ].join("\n");
68
+ }
69
+
70
+ export function writeEnrollmentScript({ supabaseUrl, anonKey, enrollmentCode, label, target = "windows", appDir } = {}) {
71
+ if (!["windows", "linux"].includes(target)) throw new Error(`Unsupported enrollment target: ${target}`);
72
+ const outputDir = appDir || (process.platform === "win32"
73
+ ? path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), "clauth")
74
+ : path.join(os.homedir(), ".config", "clauth"));
75
+ fs.mkdirSync(outputDir, { recursive: true });
76
+ const scriptPath = path.join(outputDir, enrollmentScriptName(label, target));
77
+ const script = target === "linux"
78
+ ? linuxScript({ supabaseUrl, anonKey, enrollmentCode })
79
+ : windowsScript({ supabaseUrl, anonKey, enrollmentCode });
80
+ fs.writeFileSync(scriptPath, `${script}\n`, { encoding: "utf8", mode: target === "linux" ? 0o700 : undefined });
81
+ return scriptPath;
82
+ }
package/cli/index.js CHANGED
@@ -13,53 +13,11 @@ import { writeCredentialWithRecovery } from "./recovery.js";
13
13
  import os from "os";
14
14
  import fs from "fs";
15
15
  import path from "path";
16
+ import { writeEnrollmentScript } from "./enrollment-script.js";
16
17
 
17
18
  const config = new Conf(getConfOptions());
18
19
  const VERSION = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
19
20
 
20
- function shellSingleQuote(value) {
21
- return `'${String(value ?? "").replace(/'/g, "''")}'`;
22
- }
23
-
24
- function enrollmentScriptName(label) {
25
- const slug = String(label || "new-computer")
26
- .toLowerCase()
27
- .replace(/[^a-z0-9]+/g, "-")
28
- .replace(/^-+|-+$/g, "")
29
- .slice(0, 40) || "new-computer";
30
- return `clauth-enroll-${slug}.ps1`;
31
- }
32
-
33
- function writeEnrollmentScript({ supabaseUrl, anonKey, enrollmentCode, label }) {
34
- const appDir = process.platform === "win32"
35
- ? path.join(process.env.APPDATA || path.join(os.homedir(), "AppData", "Roaming"), "clauth")
36
- : path.join(os.homedir(), ".config", "clauth");
37
- fs.mkdirSync(appDir, { recursive: true });
38
- const scriptPath = path.join(appDir, enrollmentScriptName(label));
39
- const script = [
40
- "$ErrorActionPreference = 'Stop'",
41
- "$label = $env:COMPUTERNAME",
42
- "if (-not $label) { $label = [System.Net.Dns]::GetHostName() }",
43
- "Write-Host 'Installing clauth...'",
44
- "npm install -g @lifeaitools/clauth@latest",
45
- "Write-Host 'Enrolling this computer with clauth...'",
46
- [
47
- "clauth setup",
48
- `--supabase-url ${shellSingleQuote(supabaseUrl)}`,
49
- `--anon-key ${shellSingleQuote(anonKey)}`,
50
- `--enrollment-code ${shellSingleQuote(enrollmentCode)}`,
51
- "--label \"$label\"",
52
- ].join(" "),
53
- "Write-Host 'Installing clauth startup service...'",
54
- "clauth serve install",
55
- "Write-Host 'clauth enrollment complete.'",
56
- "$self = $PSCommandPath",
57
- "Start-Process powershell.exe -WindowStyle Hidden -ArgumentList @('-NoProfile','-Command',\"Start-Sleep -Seconds 2; Remove-Item -LiteralPath '$self' -Force -ErrorAction SilentlyContinue\")",
58
- ].join("\r\n");
59
- fs.writeFileSync(scriptPath, `${script}\r\n`, "utf8");
60
- return scriptPath;
61
- }
62
-
63
21
  // ============================================================
64
22
  // Password prompt helper
65
23
  // ============================================================
@@ -161,7 +119,7 @@ async function searchServices(auth, query, opts = {}) {
161
119
  .map(([field]) => field);
162
120
 
163
121
  let addressHints = [];
164
- if (opts.addresses !== false && ADDRESS_KEY_TYPES.has(String(s.key_type || "").toLowerCase()) && s.vault_key) {
122
+ if (opts.addresses === true && ADDRESS_KEY_TYPES.has(String(s.key_type || "").toLowerCase()) && s.vault_key) {
165
123
  const secret = await api.retrieve(auth.password, auth.machineHash, auth.token, auth.timestamp, s.name);
166
124
  if (!secret.error) {
167
125
  addressHints = collectAddressHints(secret.value, s.key_type);
@@ -391,6 +349,7 @@ program
391
349
  .option("--label <label>", "Suggested label for the new computer")
392
350
  .option("--ttl-minutes <minutes>", "Enrollment lifetime, 5 to 1440 minutes", "60")
393
351
  .option("--install-id <id>", "Override install id; default is current machine's install id")
352
+ .option("--target <target>", "Enrollment target: windows or linux", "windows")
394
353
  .option("-p, --pw <password>", "Password (or will prompt)")
395
354
  .action(async (opts) => {
396
355
  console.log(chalk.cyan("\nšŸ” clauth enroll\n"));
@@ -414,6 +373,7 @@ program
414
373
  anonKey,
415
374
  enrollmentCode: result.enrollment_code,
416
375
  label: opts.label,
376
+ target: opts.target,
417
377
  });
418
378
  spinner.succeed(chalk.green(`Enrollment created for install_id=${result.install_id}`));
419
379
  console.log("");
@@ -675,15 +635,15 @@ program
675
635
 
676
636
  program
677
637
  .command("search <query>")
678
- .description("Search services by name, label, project, description, type, or redacted address hints")
638
+ .description("Search services by name, label, project, description, or type")
679
639
  .option("-p, --pw <password>")
680
640
  .option("--project <name>", "Filter by project scope")
681
- .option("--no-addresses", "Skip address-bearing secret metadata scans")
641
+ .option("--addresses", "Also search redacted address hints from address-bearing secrets (may retrieve multiple secrets)")
682
642
  .action(async (query, opts) => {
683
643
  const auth = await getAuth(opts.pw);
684
644
  const spinner = ora("Searching services...").start();
685
645
  try {
686
- const rows = await searchServices(auth, query, { project: opts.project, addresses: opts.addresses });
646
+ const rows = await searchServices(auth, query, { project: opts.project, addresses: opts.addresses === true });
687
647
  spinner.stop();
688
648
  console.log(chalk.cyan(`\n Search results for "${query}":\n`));
689
649
  if (!rows.length) {
@@ -786,9 +746,13 @@ program
786
746
  .addHelpText("after", `
787
747
  Examples:
788
748
  clauth scrub Scrub the most recent (active) transcript
789
- clauth scrub <file> Scrub a specific .jsonl file
790
- clauth scrub all Scrub every transcript in ~/.claude/projects/
749
+ clauth scrub <file> Scrub a specific file
750
+ clauth scrub all Scrub every transcript + tool-result sidecar (.jsonl + .txt)
791
751
  clauth scrub all --force Rescrub all files (ignore markers)
752
+ clauth scrub session Scrub ONLY the ending session (transcript + sidecars); reads SessionEnd hook JSON on stdin
753
+
754
+ Redacts: built-in token patterns, your ~/.clauth/scrub-patterns.json,
755
+ and this machine's live vault values (best-effort via the daemon).
792
756
  `)
793
757
  .action(async (target, opts) => {
794
758
  await runScrub(target, opts);
@@ -1036,7 +1000,7 @@ program.addHelpText("beforeAll", chalk.cyan(`
1036
1000
  // ──────────────────────────────────────────────
1037
1001
  program
1038
1002
  .command("serve [action]")
1039
- .description("Manage localhost HTTP vault daemon and supervisor (start|stop|restart|ping|supervisor|install|uninstall)")
1003
+ .description("Manage localhost HTTP vault daemon and supervisor (start|stop|restart|ping|supervisor|install|uninstall)")
1040
1004
  .option("--port <n>", "Port (default: 52437)")
1041
1005
  .option("-p, --pw <password>", "clauth password (optional — omit to start locked, unlock in browser)")
1042
1006
  .option("--services <list>", "Comma-separated service whitelist (default: all)")
@@ -1044,6 +1008,8 @@ program
1044
1008
  .option("--staged", "Start on staging port (52438) for blue-green verification before make-live")
1045
1009
  .option("--isolated", "Run on a non-live port without touching live PID files, browser, or boot-key credentials")
1046
1010
  .option("--from-boot-key", "Internal: password came from boot.key auto-unlock (degrade gracefully on verify failure)")
1011
+ .option("--pw-env", "Internal: read the daemon password from CLAUTH_BOOT_PASSWORD")
1012
+ .option("--pw-stdin", "Internal: read the installer password from standard input")
1047
1013
  .option("--action <action>", "Internal: action override for daemon child")
1048
1014
  .addHelpText("after", `
1049
1015
  Actions:
@@ -1052,9 +1018,9 @@ Actions:
1052
1018
  restart Stop + start
1053
1019
  ping Check if the daemon is running
1054
1020
  foreground Run in foreground (Ctrl+C to stop) — default if no action given
1055
- mcp Run as MCP stdio server for Claude Code (JSON-RPC over stdin/stdout)
1056
- supervisor Run localhost-only supervisor control plane on port 52439
1057
- install Store password securely + register auto-start service (cross-platform)
1021
+ mcp Run as MCP stdio server for Claude Code (JSON-RPC over stdin/stdout)
1022
+ supervisor Run localhost-only supervisor control plane on port 52439
1023
+ install Store password securely + register auto-start service (cross-platform)
1058
1024
  Windows: DPAPI + HKCU\\Run | macOS: Keychain + LaunchAgent | Linux: libsecret/openssl + systemd
1059
1025
  uninstall Remove auto-start service + delete stored password
1060
1026
  upgrade Blue-green upgrade: start new version on staging port, verify, then make live
@@ -1070,10 +1036,10 @@ Examples:
1070
1036
  clauth serve ping Check status
1071
1037
  clauth serve restart Restart (stays locked until browser unlock)
1072
1038
  clauth serve start --services github,vercel
1073
- clauth serve mcp Start MCP server for Claude Code
1074
- clauth serve mcp -p mypass Start MCP server pre-unlocked
1075
- clauth serve supervisor Start supervisor API on http://127.0.0.1:52439
1076
- clauth serve foreground --port 53137 --isolated
1039
+ clauth serve mcp Start MCP server for Claude Code
1040
+ clauth serve mcp -p mypass Start MCP server pre-unlocked
1041
+ clauth serve supervisor Start supervisor API on http://127.0.0.1:52439
1042
+ clauth serve foreground --port 53137 --isolated
1077
1043
  Start isolated passwordless server for route tests
1078
1044
  clauth serve install Set up auto-start on login (DPAPI/Keychain/libsecret)
1079
1045
  clauth serve install --tunnel host Auto-start with Cloudflare Tunnel