@lifeaitools/clauth 1.30.23 → 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 +41 -95
  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
@@ -1,89 +1,89 @@
1
- import assert from "node:assert/strict";
2
- import fs from "node:fs";
3
- import os from "node:os";
4
- import path from "node:path";
5
- import test from "node:test";
6
-
7
- import {
8
- getRegistryPath,
9
- loadRegistry,
10
- registerWatchdogManifest,
11
- restartWatchdogService,
12
- validateWatchdogManifest,
13
- validateWatchdogService,
14
- } from "./watchdog-registry.js";
15
-
16
- function withTempRegistry(fn) {
17
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-watchdog-"));
18
- const old = process.env.CLAUTH_WATCHDOG_DIR;
19
- process.env.CLAUTH_WATCHDOG_DIR = dir;
20
- try {
21
- return fn(dir);
22
- } finally {
23
- if (old === undefined) delete process.env.CLAUTH_WATCHDOG_DIR;
24
- else process.env.CLAUTH_WATCHDOG_DIR = old;
25
- fs.rmSync(dir, { recursive: true, force: true });
26
- }
27
- }
28
-
29
- test("validateWatchdogService accepts a localhost HTTP service", () => {
30
- const service = validateWatchdogService({
31
- id: "codeflow-explorer",
32
- label: "CodeFlow Explorer",
33
- owner: "codeflow",
34
- kind: "http",
35
- health: { url: "http://127.0.0.1:3109/health" },
36
- restart: { cmd: "pnpm", args: ["--filter", "@regen/codeflow-explorer", "dev"] },
37
- });
38
- assert.equal(service.id, "codeflow-explorer");
39
- assert.equal(service.approvalRequired, true);
40
- });
41
-
42
- test("validateWatchdogService rejects non-local health URLs and shell syntax commands", () => {
43
- assert.throws(() => validateWatchdogService({
44
- id: "bad",
45
- label: "Bad",
46
- kind: "http",
47
- health: { url: "https://example.com/health" },
48
- }), /localhost-only/);
49
-
50
- assert.throws(() => validateWatchdogService({
51
- id: "bad",
52
- label: "Bad",
53
- kind: "process",
54
- restart: { cmd: "cmd.exe & del" },
55
- }), /not shell syntax/);
56
- });
57
-
58
- test("registerWatchdogManifest upserts services by id", () => withTempRegistry(() => {
59
- const manifest = validateWatchdogManifest({
60
- source: "test",
61
- services: [
62
- { id: "clauth", label: "clauth", kind: "http", health: { url: "http://127.0.0.1:52437/ping" } },
63
- { id: "codeflow", label: "CodeFlow", kind: "http", health: { url: "http://localhost:3109/health" } },
64
- ],
65
- });
66
- const result = registerWatchdogManifest(manifest);
67
- assert.equal(result.registered, 2);
68
- assert.ok(fs.existsSync(getRegistryPath()));
69
- assert.deepEqual(loadRegistry().services.map((service) => service.id), ["clauth", "codeflow"]);
70
-
71
- registerWatchdogManifest({
72
- services: [
73
- { id: "codeflow", label: "CodeFlow Updated", kind: "http", health: { url: "http://localhost:3109/health" } },
74
- ],
75
- });
76
- const registry = loadRegistry();
77
- assert.equal(registry.services.length, 2);
78
- assert.equal(registry.services.find((service) => service.id === "codeflow").label, "CodeFlow Updated");
79
- }));
80
-
81
- test("restartWatchdogService rejects missing and unapproved services", () => withTempRegistry(() => {
82
- assert.deepEqual(restartWatchdogService("missing"), { ok: false, error: "service_not_registered" });
83
- registerWatchdogManifest({
84
- services: [
85
- { id: "dev-center", label: "Dev Center", kind: "process", restart: { cmd: "node", args: ["--version"] } },
86
- ],
87
- });
88
- assert.deepEqual(restartWatchdogService("dev-center"), { ok: false, error: "approval_required" });
89
- }));
1
+ import assert from "node:assert/strict";
2
+ import fs from "node:fs";
3
+ import os from "node:os";
4
+ import path from "node:path";
5
+ import test from "node:test";
6
+
7
+ import {
8
+ getRegistryPath,
9
+ loadRegistry,
10
+ registerWatchdogManifest,
11
+ restartWatchdogService,
12
+ validateWatchdogManifest,
13
+ validateWatchdogService,
14
+ } from "./watchdog-registry.js";
15
+
16
+ function withTempRegistry(fn) {
17
+ const dir = fs.mkdtempSync(path.join(os.tmpdir(), "clauth-watchdog-"));
18
+ const old = process.env.CLAUTH_WATCHDOG_DIR;
19
+ process.env.CLAUTH_WATCHDOG_DIR = dir;
20
+ try {
21
+ return fn(dir);
22
+ } finally {
23
+ if (old === undefined) delete process.env.CLAUTH_WATCHDOG_DIR;
24
+ else process.env.CLAUTH_WATCHDOG_DIR = old;
25
+ fs.rmSync(dir, { recursive: true, force: true });
26
+ }
27
+ }
28
+
29
+ test("validateWatchdogService accepts a localhost HTTP service", () => {
30
+ const service = validateWatchdogService({
31
+ id: "codeflow-explorer",
32
+ label: "CodeFlow Explorer",
33
+ owner: "codeflow",
34
+ kind: "http",
35
+ health: { url: "http://127.0.0.1:3109/health" },
36
+ restart: { cmd: "pnpm", args: ["--filter", "@regen/codeflow-explorer", "dev"] },
37
+ });
38
+ assert.equal(service.id, "codeflow-explorer");
39
+ assert.equal(service.approvalRequired, true);
40
+ });
41
+
42
+ test("validateWatchdogService rejects non-local health URLs and shell syntax commands", () => {
43
+ assert.throws(() => validateWatchdogService({
44
+ id: "bad",
45
+ label: "Bad",
46
+ kind: "http",
47
+ health: { url: "https://example.com/health" },
48
+ }), /localhost-only/);
49
+
50
+ assert.throws(() => validateWatchdogService({
51
+ id: "bad",
52
+ label: "Bad",
53
+ kind: "process",
54
+ restart: { cmd: "cmd.exe & del" },
55
+ }), /not shell syntax/);
56
+ });
57
+
58
+ test("registerWatchdogManifest upserts services by id", () => withTempRegistry(() => {
59
+ const manifest = validateWatchdogManifest({
60
+ source: "test",
61
+ services: [
62
+ { id: "clauth", label: "clauth", kind: "http", health: { url: "http://127.0.0.1:52437/ping" } },
63
+ { id: "codeflow", label: "CodeFlow", kind: "http", health: { url: "http://localhost:3109/health" } },
64
+ ],
65
+ });
66
+ const result = registerWatchdogManifest(manifest);
67
+ assert.equal(result.registered, 2);
68
+ assert.ok(fs.existsSync(getRegistryPath()));
69
+ assert.deepEqual(loadRegistry().services.map((service) => service.id), ["clauth", "codeflow"]);
70
+
71
+ registerWatchdogManifest({
72
+ services: [
73
+ { id: "codeflow", label: "CodeFlow Updated", kind: "http", health: { url: "http://localhost:3109/health" } },
74
+ ],
75
+ });
76
+ const registry = loadRegistry();
77
+ assert.equal(registry.services.length, 2);
78
+ assert.equal(registry.services.find((service) => service.id === "codeflow").label, "CodeFlow Updated");
79
+ }));
80
+
81
+ test("restartWatchdogService rejects missing and unapproved services", () => withTempRegistry(() => {
82
+ assert.deepEqual(restartWatchdogService("missing"), { ok: false, error: "service_not_registered" });
83
+ registerWatchdogManifest({
84
+ services: [
85
+ { id: "dev-center", label: "Dev Center", kind: "process", restart: { cmd: "node", args: ["--version"] } },
86
+ ],
87
+ });
88
+ assert.deepEqual(restartWatchdogService("dev-center"), { ok: false, error: "approval_required" });
89
+ }));
package/install.ps1 CHANGED
@@ -100,24 +100,24 @@ if (Test-Path $autostartScript) {
100
100
  $lnk.Save()
101
101
  Write-Host " + Autostart registered via Startup folder (15s delay, crash-recovery loop active)." -ForegroundColor Green
102
102
  }
103
- } else {
104
- Write-Host ""
105
- Write-Host " ! autostart.ps1 not found - skipping autostart registration." -ForegroundColor Yellow
106
- Write-Host " Run: clauth autostart install" -ForegroundColor Gray
107
- }
108
-
109
- Write-Host ""
110
- Write-Host " Installing co-development Windows Terminal profiles..." -ForegroundColor Cyan
111
- try {
112
- & clauth codevelop install-terminal --repo "C:\Dev\regen-root"
113
- if ($LASTEXITCODE -eq 0) {
114
- Write-Host " + Co-development terminal profiles installed." -ForegroundColor Green
115
- } else {
116
- Write-Host " ! Co-development terminal profile install returned exit code $LASTEXITCODE." -ForegroundColor Yellow
117
- }
118
- } catch {
119
- Write-Host " ! Co-development terminal profile install skipped: $($_.Exception.Message)" -ForegroundColor Yellow
120
- Write-Host " Run later: clauth codevelop install-terminal --repo C:\Dev\regen-root" -ForegroundColor Gray
121
- }
122
-
123
- exit $LASTEXITCODE
103
+ } else {
104
+ Write-Host ""
105
+ Write-Host " ! autostart.ps1 not found - skipping autostart registration." -ForegroundColor Yellow
106
+ Write-Host " Run: clauth autostart install" -ForegroundColor Gray
107
+ }
108
+
109
+ Write-Host ""
110
+ Write-Host " Installing co-development Windows Terminal profiles..." -ForegroundColor Cyan
111
+ try {
112
+ & clauth codevelop install-terminal --repo "C:\Dev\regen-root"
113
+ if ($LASTEXITCODE -eq 0) {
114
+ Write-Host " + Co-development terminal profiles installed." -ForegroundColor Green
115
+ } else {
116
+ Write-Host " ! Co-development terminal profile install returned exit code $LASTEXITCODE." -ForegroundColor Yellow
117
+ }
118
+ } catch {
119
+ Write-Host " ! Co-development terminal profile install skipped: $($_.Exception.Message)" -ForegroundColor Yellow
120
+ Write-Host " Run later: clauth codevelop install-terminal --repo C:\Dev\regen-root" -ForegroundColor Gray
121
+ }
122
+
123
+ exit $LASTEXITCODE
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lifeaitools/clauth",
3
- "version": "1.30.23",
3
+ "version": "1.30.24",
4
4
  "description": "Hardware-bound credential vault for the LIFEAI infrastructure stack",
5
5
  "type": "module",
6
6
  "bin": {
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "bash scripts/build.sh",
11
- "test": "node --test cli/commands/scrub.test.js cli/api.classify.test.js cli/supervisor-registry.test.js cli/supervisor-ui.test.js cli/served-script-syntax.test.mjs test/studio-debug.test.mjs test/dashboard-key-generator.test.mjs && node test-auth-verdict.mjs",
11
+ "test": "node --test cli/commands/scrub.test.js cli/api.classify.test.js test/studio-debug.test.mjs test/dashboard-key-generator.test.mjs && node test-auth-verdict.mjs",
12
12
  "test:studio-debug": "node --test test/studio-debug.test.mjs",
13
13
  "test:agent-pool": "node test/agent-pool.test.mjs",
14
14
  "test:call-agent-10": "node test/call-agent-10-skills.test.mjs",
Binary file
Binary file
Binary file
@@ -27,18 +27,18 @@ create table if not exists public.clauth_services (
27
27
  -- ============================================================
28
28
  -- Machine Registry (hardware fingerprints — hashed only)
29
29
  -- ============================================================
30
- create table if not exists public.clauth_machines (
31
- id uuid primary key default gen_random_uuid(),
32
- install_id text not null default 'default', -- logical vault install / owner group
33
- machine_hash text not null unique, -- SHA256(machine_id + os_install_id)
34
- label text, -- e.g. 'Dave-Desktop-Win11'
35
- hmac_seed_hash text not null, -- SHA256 of the HMAC seed stored in vault
36
- enabled boolean not null default true,
37
- fail_count integer not null default 0,
38
- locked boolean not null default false,
39
- created_at timestamptz not null default now(),
40
- last_seen timestamptz
41
- );
30
+ create table if not exists public.clauth_machines (
31
+ id uuid primary key default gen_random_uuid(),
32
+ install_id text not null default 'default', -- logical vault install / owner group
33
+ machine_hash text not null unique, -- SHA256(machine_id + os_install_id)
34
+ label text, -- e.g. 'Dave-Desktop-Win11'
35
+ hmac_seed_hash text not null, -- SHA256 of the HMAC seed stored in vault
36
+ enabled boolean not null default true,
37
+ fail_count integer not null default 0,
38
+ locked boolean not null default false,
39
+ created_at timestamptz not null default now(),
40
+ last_seen timestamptz
41
+ );
42
42
 
43
43
  -- ============================================================
44
44
  -- Audit Log
@@ -1,13 +1,13 @@
1
- -- clauth_config: key/value store for daemon configuration
2
- -- Used to persist tunnel hostname and other daemon settings
3
- CREATE TABLE IF NOT EXISTS clauth_config (
4
- key text PRIMARY KEY,
5
- value jsonb NOT NULL,
6
- updated_at timestamptz DEFAULT now()
7
- );
8
-
9
- -- RLS: only service role can access (same pattern as other clauth tables)
10
- ALTER TABLE clauth_config ENABLE ROW LEVEL SECURITY;
11
-
12
- -- Seed: no tunnel by default (user must run clauth tunnel setup)
13
- -- INSERT INTO clauth_config (key, value) VALUES ('tunnel_hostname', 'null');
1
+ -- clauth_config: key/value store for daemon configuration
2
+ -- Used to persist tunnel hostname and other daemon settings
3
+ CREATE TABLE IF NOT EXISTS clauth_config (
4
+ key text PRIMARY KEY,
5
+ value jsonb NOT NULL,
6
+ updated_at timestamptz DEFAULT now()
7
+ );
8
+
9
+ -- RLS: only service role can access (same pattern as other clauth tables)
10
+ ALTER TABLE clauth_config ENABLE ROW LEVEL SECURITY;
11
+
12
+ -- Seed: no tunnel by default (user must run clauth tunnel setup)
13
+ -- INSERT INTO clauth_config (key, value) VALUES ('tunnel_hostname', 'null');
@@ -1,39 +1,39 @@
1
- -- ============================================================
2
- -- clauth machine enrollment
3
- -- Migration: 003_machine_enrollments.sql
4
- -- ============================================================
5
-
6
- alter table public.clauth_machines
7
- add column if not exists install_id text not null default 'default';
8
-
9
- create index if not exists clauth_machines_install_id_idx
10
- on public.clauth_machines (install_id);
11
-
12
- create table if not exists public.clauth_machine_enrollments (
13
- id uuid primary key default gen_random_uuid(),
14
- install_id text not null default 'default',
15
- token_hash text not null unique,
16
- label text,
17
- created_by_machine_hash text not null references public.clauth_machines(machine_hash) on delete cascade,
18
- redeemed_by_machine_hash text references public.clauth_machines(machine_hash) on delete set null,
19
- expires_at timestamptz not null,
20
- consumed_at timestamptz,
21
- created_at timestamptz not null default now()
22
- );
23
-
24
- create index if not exists clauth_machine_enrollments_install_idx
25
- on public.clauth_machine_enrollments (install_id, expires_at);
26
-
27
- alter table public.clauth_machine_enrollments enable row level security;
28
-
29
- do $$ begin
30
- if not exists (
31
- select 1
32
- from pg_policies
33
- where policyname = 'no_anon_machine_enrollments'
34
- and tablename = 'clauth_machine_enrollments'
35
- ) then
36
- create policy "no_anon_machine_enrollments"
37
- on public.clauth_machine_enrollments for all using (false);
38
- end if;
39
- end $$;
1
+ -- ============================================================
2
+ -- clauth machine enrollment
3
+ -- Migration: 003_machine_enrollments.sql
4
+ -- ============================================================
5
+
6
+ alter table public.clauth_machines
7
+ add column if not exists install_id text not null default 'default';
8
+
9
+ create index if not exists clauth_machines_install_id_idx
10
+ on public.clauth_machines (install_id);
11
+
12
+ create table if not exists public.clauth_machine_enrollments (
13
+ id uuid primary key default gen_random_uuid(),
14
+ install_id text not null default 'default',
15
+ token_hash text not null unique,
16
+ label text,
17
+ created_by_machine_hash text not null references public.clauth_machines(machine_hash) on delete cascade,
18
+ redeemed_by_machine_hash text references public.clauth_machines(machine_hash) on delete set null,
19
+ expires_at timestamptz not null,
20
+ consumed_at timestamptz,
21
+ created_at timestamptz not null default now()
22
+ );
23
+
24
+ create index if not exists clauth_machine_enrollments_install_idx
25
+ on public.clauth_machine_enrollments (install_id, expires_at);
26
+
27
+ alter table public.clauth_machine_enrollments enable row level security;
28
+
29
+ do $$ begin
30
+ if not exists (
31
+ select 1
32
+ from pg_policies
33
+ where policyname = 'no_anon_machine_enrollments'
34
+ and tablename = 'clauth_machine_enrollments'
35
+ ) then
36
+ create policy "no_anon_machine_enrollments"
37
+ on public.clauth_machine_enrollments for all using (false);
38
+ end if;
39
+ end $$;
@@ -1,54 +0,0 @@
1
- import test from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import vm from 'node:vm';
4
- import { spawn } from 'node:child_process';
5
- import path from 'node:path';
6
- import { fileURLToPath } from 'node:url';
7
-
8
- const here = path.dirname(fileURLToPath(import.meta.url));
9
- const cliEntry = path.join(here, 'index.js');
10
- const PORT = 52499;
11
-
12
- function waitForPort(url, timeoutMs) {
13
- const deadline = Date.now() + timeoutMs;
14
- const attempt = async () => {
15
- try {
16
- const r = await fetch(url);
17
- if (r.ok) return true;
18
- } catch {}
19
- if (Date.now() > deadline) throw new Error('timed out waiting for ' + url);
20
- await new Promise((r) => setTimeout(r, 200));
21
- return attempt();
22
- };
23
- return attempt();
24
- }
25
-
26
- // Regression coverage for the 2026-08-06 incident: a stray tool-authoring
27
- // artifact ("[char]0x2192" -- PowerShell character-literal syntax, never
28
- // evaluated because it was written inside a single-quoted PowerShell string)
29
- // landed as literal text inside the embedded dashboard <script> block. That
30
- // produced a real SyntaxError in every browser that loaded the page, so the
31
- // ENTIRE inline script failed to parse -- meaning unlock(), unlockWrites(),
32
- // runSupervisorSurface(), and every other function on the page silently
33
- // never existed. `node --check` on serve.js itself never catches this class
34
- // of bug: the broken text lives inside a JS string/template FOR the browser,
35
- // which is opaque to Node's own parser. This test extracts the actual served
36
- // script -- the same bytes a real browser executes -- and parses it for real.
37
- test('the served dashboard script has no syntax errors (would have caught the 2026-08-06 incident)', async () => {
38
- const child = spawn(process.execPath, [cliEntry, 'serve', 'start', '--isolated', '--port', String(PORT)], {
39
- stdio: 'ignore',
40
- windowsHide: true,
41
- });
42
- try {
43
- await waitForPort('http://127.0.0.1:' + PORT + '/ping', 15000);
44
- const html = await fetch('http://127.0.0.1:' + PORT + '/').then((r) => r.text());
45
- const start = html.indexOf('<script>');
46
- const end = html.lastIndexOf('</script>');
47
- assert.ok(start !== -1 && end !== -1, 'expected an inline <script> block in the served dashboard');
48
- const js = html.slice(start + 8, end);
49
- assert.doesNotThrow(() => new vm.Script(js), 'the served dashboard script must be syntactically valid JavaScript');
50
- } finally {
51
- await fetch('http://127.0.0.1:' + PORT + '/shutdown').catch(() => {});
52
- child.kill();
53
- }
54
- });