@lifeaitools/clauth 1.30.22 → 1.30.24

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.
Files changed (40) hide show
  1. package/.clauth-skill/SKILL.md +111 -111
  2. package/README.md +25 -0
  3. package/cli/api.classify.test.js +75 -75
  4. package/cli/assets/codevelop/launcher-active.cmd.template +20 -20
  5. package/cli/assets/codevelop/launcher-static.cmd.template +7 -7
  6. package/cli/assets/codevelop/windows-terminal.profiles.json +48 -48
  7. package/cli/assets/watchdog.ps1 +42 -42
  8. package/cli/commands/agent-cron.js +396 -396
  9. package/cli/commands/agent-pool.js +1962 -1962
  10. package/cli/commands/codevelop.js +1190 -1190
  11. package/cli/commands/doctor.js +302 -302
  12. package/cli/commands/install.js +10 -10
  13. package/cli/commands/invite.js +175 -175
  14. package/cli/commands/join.js +179 -179
  15. package/cli/commands/npm.js +182 -182
  16. package/cli/commands/scrub.js +327 -327
  17. package/cli/commands/scrub.test.js +115 -115
  18. package/cli/commands/serve.js +43 -86
  19. package/cli/commands/watchdog.js +209 -209
  20. package/cli/conf-path.js +21 -21
  21. package/cli/enrollment-script.js +82 -82
  22. package/cli/fingerprint.js +143 -143
  23. package/cli/index.js +1053 -1053
  24. package/cli/lib/fs-git.js +282 -282
  25. package/cli/recovery.js +101 -101
  26. package/cli/studio-debug.js +1095 -1095
  27. package/cli/supervisor-registry.js +594 -589
  28. package/cli/supervisor-registry.test.js +397 -397
  29. package/cli/supervisor-ui.test.js +5 -83
  30. package/cli/watchdog-registry.js +209 -209
  31. package/cli/watchdog-registry.test.js +89 -89
  32. package/install.ps1 +21 -21
  33. package/package.json +2 -2
  34. package/scripts/bin/bootstrap-linux +0 -0
  35. package/scripts/bin/bootstrap-macos +0 -0
  36. package/scripts/bin/bootstrap-win.exe +0 -0
  37. package/supabase/migrations/001_clauth_schema.sql +12 -12
  38. package/supabase/migrations/003_clauth_config.sql +13 -13
  39. package/supabase/migrations/003_machine_enrollments.sql +39 -39
  40. package/cli/served-script-syntax.test.mjs +0 -54
package/cli/recovery.js CHANGED
@@ -1,101 +1,101 @@
1
- import crypto from "crypto";
2
- import fs from "fs";
3
- import os from "os";
4
- import path from "path";
5
- import { getConfOptions } from "./conf-path.js";
6
- import { deriveToken } from "./fingerprint.js";
7
- import * as api from "./api.js";
8
-
9
- const RECOVERY_VERSION = 1;
10
-
11
- function getRecoveryDir() {
12
- const opts = getConfOptions();
13
- if (opts.cwd) return path.join(opts.cwd, "recovery");
14
- return path.join(os.homedir(), ".config", "clauth", "recovery");
15
- }
16
-
17
- function hashValue(value) {
18
- return crypto.createHash("sha256").update(String(value), "utf8").digest("hex");
19
- }
20
-
21
- function deriveRecoveryKey(password, machineHash, salt) {
22
- return crypto.scryptSync(`${password}:${machineHash}`, salt, 32);
23
- }
24
-
25
- function safeServiceName(service) {
26
- return String(service || "unknown").replace(/[^a-zA-Z0-9_-]/g, "_");
27
- }
28
-
29
- export function normalizeCredentialValue(value, { keyType = "", service = "" } = {}) {
30
- if (typeof value !== "string") return value;
31
- const text = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
32
- const looksPem = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/.test(text);
33
- const looksSsh = String(keyType).toLowerCase() === "ssh" || /ssh|pem|private/i.test(service);
34
- if (!looksPem && !looksSsh) return value;
35
- return text.replace(/\n*$/, "\n");
36
- }
37
-
38
- export async function snapshotCredentialBeforeWrite({ password, machineHash, service, logFile }) {
39
- if (!password || !machineHash || !service) return { ok: false, skipped: "missing_context" };
40
-
41
- let current;
42
- try {
43
- const { token, timestamp } = deriveToken(password, machineHash);
44
- current = await api.retrieve(password, machineHash, token, timestamp, service);
45
- } catch (err) {
46
- return { ok: false, skipped: "retrieve_failed", error: err.message };
47
- }
48
-
49
- if (current?.error || current?.value === undefined || current?.value === null) {
50
- return { ok: false, skipped: current?.error || "no_existing_value" };
51
- }
52
-
53
- const value = typeof current.value === "string" ? current.value : JSON.stringify(current.value);
54
- const now = new Date().toISOString();
55
- const salt = crypto.randomBytes(16);
56
- const iv = crypto.randomBytes(12);
57
- const key = deriveRecoveryKey(password, machineHash, salt);
58
- const meta = {
59
- version: RECOVERY_VERSION,
60
- service,
61
- key_type: current.key_type || null,
62
- created_at: now,
63
- value_sha256: hashValue(value),
64
- };
65
-
66
- const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
67
- cipher.setAAD(Buffer.from(JSON.stringify(meta), "utf8"));
68
- const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
69
- const tag = cipher.getAuthTag();
70
-
71
- const payload = {
72
- ...meta,
73
- kdf: "scrypt",
74
- cipher: "aes-256-gcm",
75
- salt: salt.toString("base64"),
76
- iv: iv.toString("base64"),
77
- tag: tag.toString("base64"),
78
- ciphertext: ciphertext.toString("base64"),
79
- };
80
-
81
- const dir = getRecoveryDir();
82
- fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
83
- const stamp = now.replace(/[-:.]/g, "").replace("T", "-").replace("Z", "");
84
- const filePath = path.join(dir, `${stamp}-${safeServiceName(service)}.json`);
85
- fs.writeFileSync(filePath, JSON.stringify(payload, null, 2) + "\n", { mode: 0o600 });
86
-
87
- if (logFile) {
88
- try { fs.appendFileSync(logFile, `[${now}] Recovery snapshot written for ${service}: ${filePath}\n`); } catch {}
89
- }
90
- return { ok: true, filePath, value_sha256: meta.value_sha256 };
91
- }
92
-
93
- export async function writeCredentialWithRecovery({ password, machineHash, service, value, logFile, normalize = true }) {
94
- const { token, timestamp } = deriveToken(password, machineHash);
95
- const status = await api.status(password, machineHash, token, timestamp);
96
- const svc = (status.services || []).find(s => String(s.name || "").toLowerCase() === String(service || "").toLowerCase());
97
- const normalizedValue = normalize ? normalizeCredentialValue(value, { keyType: svc?.key_type, service }) : value;
98
- const snapshot = await snapshotCredentialBeforeWrite({ password, machineHash, service, logFile });
99
- const result = await api.write(password, machineHash, token, timestamp, service, normalizedValue);
100
- return { result, snapshot, normalized: normalizedValue !== value };
101
- }
1
+ import crypto from "crypto";
2
+ import fs from "fs";
3
+ import os from "os";
4
+ import path from "path";
5
+ import { getConfOptions } from "./conf-path.js";
6
+ import { deriveToken } from "./fingerprint.js";
7
+ import * as api from "./api.js";
8
+
9
+ const RECOVERY_VERSION = 1;
10
+
11
+ function getRecoveryDir() {
12
+ const opts = getConfOptions();
13
+ if (opts.cwd) return path.join(opts.cwd, "recovery");
14
+ return path.join(os.homedir(), ".config", "clauth", "recovery");
15
+ }
16
+
17
+ function hashValue(value) {
18
+ return crypto.createHash("sha256").update(String(value), "utf8").digest("hex");
19
+ }
20
+
21
+ function deriveRecoveryKey(password, machineHash, salt) {
22
+ return crypto.scryptSync(`${password}:${machineHash}`, salt, 32);
23
+ }
24
+
25
+ function safeServiceName(service) {
26
+ return String(service || "unknown").replace(/[^a-zA-Z0-9_-]/g, "_");
27
+ }
28
+
29
+ export function normalizeCredentialValue(value, { keyType = "", service = "" } = {}) {
30
+ if (typeof value !== "string") return value;
31
+ const text = value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
32
+ const looksPem = /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----/.test(text);
33
+ const looksSsh = String(keyType).toLowerCase() === "ssh" || /ssh|pem|private/i.test(service);
34
+ if (!looksPem && !looksSsh) return value;
35
+ return text.replace(/\n*$/, "\n");
36
+ }
37
+
38
+ export async function snapshotCredentialBeforeWrite({ password, machineHash, service, logFile }) {
39
+ if (!password || !machineHash || !service) return { ok: false, skipped: "missing_context" };
40
+
41
+ let current;
42
+ try {
43
+ const { token, timestamp } = deriveToken(password, machineHash);
44
+ current = await api.retrieve(password, machineHash, token, timestamp, service);
45
+ } catch (err) {
46
+ return { ok: false, skipped: "retrieve_failed", error: err.message };
47
+ }
48
+
49
+ if (current?.error || current?.value === undefined || current?.value === null) {
50
+ return { ok: false, skipped: current?.error || "no_existing_value" };
51
+ }
52
+
53
+ const value = typeof current.value === "string" ? current.value : JSON.stringify(current.value);
54
+ const now = new Date().toISOString();
55
+ const salt = crypto.randomBytes(16);
56
+ const iv = crypto.randomBytes(12);
57
+ const key = deriveRecoveryKey(password, machineHash, salt);
58
+ const meta = {
59
+ version: RECOVERY_VERSION,
60
+ service,
61
+ key_type: current.key_type || null,
62
+ created_at: now,
63
+ value_sha256: hashValue(value),
64
+ };
65
+
66
+ const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
67
+ cipher.setAAD(Buffer.from(JSON.stringify(meta), "utf8"));
68
+ const ciphertext = Buffer.concat([cipher.update(value, "utf8"), cipher.final()]);
69
+ const tag = cipher.getAuthTag();
70
+
71
+ const payload = {
72
+ ...meta,
73
+ kdf: "scrypt",
74
+ cipher: "aes-256-gcm",
75
+ salt: salt.toString("base64"),
76
+ iv: iv.toString("base64"),
77
+ tag: tag.toString("base64"),
78
+ ciphertext: ciphertext.toString("base64"),
79
+ };
80
+
81
+ const dir = getRecoveryDir();
82
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
83
+ const stamp = now.replace(/[-:.]/g, "").replace("T", "-").replace("Z", "");
84
+ const filePath = path.join(dir, `${stamp}-${safeServiceName(service)}.json`);
85
+ fs.writeFileSync(filePath, JSON.stringify(payload, null, 2) + "\n", { mode: 0o600 });
86
+
87
+ if (logFile) {
88
+ try { fs.appendFileSync(logFile, `[${now}] Recovery snapshot written for ${service}: ${filePath}\n`); } catch {}
89
+ }
90
+ return { ok: true, filePath, value_sha256: meta.value_sha256 };
91
+ }
92
+
93
+ export async function writeCredentialWithRecovery({ password, machineHash, service, value, logFile, normalize = true }) {
94
+ const { token, timestamp } = deriveToken(password, machineHash);
95
+ const status = await api.status(password, machineHash, token, timestamp);
96
+ const svc = (status.services || []).find(s => String(s.name || "").toLowerCase() === String(service || "").toLowerCase());
97
+ const normalizedValue = normalize ? normalizeCredentialValue(value, { keyType: svc?.key_type, service }) : value;
98
+ const snapshot = await snapshotCredentialBeforeWrite({ password, machineHash, service, logFile });
99
+ const result = await api.write(password, machineHash, token, timestamp, service, normalizedValue);
100
+ return { result, snapshot, normalized: normalizedValue !== value };
101
+ }