@lifeaitools/clauth 1.30.2 → 1.30.4
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/.clauth-skill/SKILL.md +111 -111
- package/cli/api.classify.test.js +75 -75
- package/cli/assets/codevelop/launcher-active.cmd.template +20 -20
- package/cli/assets/codevelop/launcher-static.cmd.template +7 -7
- package/cli/assets/codevelop/windows-terminal.profiles.json +48 -48
- package/cli/assets/watchdog.ps1 +42 -42
- package/cli/commands/agent-cron.js +396 -396
- package/cli/commands/agent-pool.js +22 -6
- package/cli/commands/codevelop.js +1190 -1190
- package/cli/commands/doctor.js +302 -302
- package/cli/commands/install.js +10 -10
- package/cli/commands/invite.js +175 -175
- package/cli/commands/join.js +179 -179
- package/cli/commands/npm.js +182 -182
- package/cli/commands/scrub.js +327 -327
- package/cli/commands/scrub.test.js +115 -115
- package/cli/commands/serve.js +1070 -144
- package/cli/commands/watchdog.js +209 -209
- package/cli/conf-path.js +21 -21
- package/cli/enrollment-script.js +62 -0
- package/cli/index.js +1050 -1089
- package/cli/lib/fs-git.js +282 -282
- package/cli/recovery.js +101 -101
- package/cli/studio-debug.js +1095 -1087
- package/cli/watchdog-registry.js +209 -209
- package/cli/watchdog-registry.test.js +89 -89
- package/install.ps1 +21 -21
- package/package.json +68 -68
- package/scripts/bin/bootstrap-linux +0 -0
- package/scripts/bin/bootstrap-macos +0 -0
- package/scripts/bin/bootstrap-win.exe +0 -0
- package/supabase/migrations/001_clauth_schema.sql +12 -12
- package/supabase/migrations/003_clauth_config.sql +13 -13
- package/supabase/migrations/003_machine_enrollments.sql +39 -39
- package/cli/commands/serve/tools/fs.js +0 -1055
package/.clauth-skill/SKILL.md
CHANGED
|
@@ -9,49 +9,49 @@ Hardware-bound credential vault for the LIFEAI stack. Your machine is the second
|
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
12
|
-
## How Claude Interfaces with clauth
|
|
13
|
-
|
|
14
|
-
> **CRITICAL:** Agents should use the local daemon for credential discovery and
|
|
15
|
-
> exact secret retrieval. Do not ask the user for the vault password just to get
|
|
16
|
-
> a key, do not use interactive prompts, and do not walk every service.
|
|
17
|
-
|
|
18
|
-
### Method 1 — Local daemon (preferred for agents)
|
|
19
|
-
|
|
20
|
-
Discover services without reading secrets:
|
|
21
|
-
|
|
22
|
-
```bash
|
|
23
|
-
curl -s http://127.0.0.1:52437/ping
|
|
24
|
-
curl -s http://127.0.0.1:52437/knowledge
|
|
25
|
-
curl -s http://127.0.0.1:52437/list-services
|
|
26
|
-
```
|
|
27
|
-
|
|
28
|
-
Retrieve exactly one credential only when the next command needs it:
|
|
29
|
-
|
|
30
|
-
```bash
|
|
31
|
-
curl -s http://127.0.0.1:52437/v/<service>
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
`/knowledge` is safe to cache for a session: it returns service metadata and
|
|
35
|
-
`has_key`, but no raw secrets and no Vault secret names. `/status` uses the same
|
|
36
|
-
short metadata cache and can be forced with `?refresh=1`.
|
|
37
|
-
|
|
38
|
-
### Method 2 — CLI with `--pw` flag (admin/setup only)
|
|
39
|
-
|
|
40
|
-
All clauth commands accept `-p` / `--pw <password>` to skip the interactive
|
|
41
|
-
password prompt. Use this for local admin/setup tasks, not routine agent secret
|
|
42
|
-
retrieval:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
clauth status -p "YourPassword"
|
|
46
|
-
clauth test -p "YourPassword"
|
|
47
|
-
clauth enable github -p "YourPassword"
|
|
48
|
-
clauth write key github -p "YourPassword" # still prompts for the key value
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
### Method 3 — Direct API calls (implementation/debugging only)
|
|
52
|
-
|
|
53
|
-
Call the auth-vault Edge Function directly only when debugging clauth internals.
|
|
54
|
-
Normal agents should not bypass the daemon metadata cache.
|
|
12
|
+
## How Claude Interfaces with clauth
|
|
13
|
+
|
|
14
|
+
> **CRITICAL:** Agents should use the local daemon for credential discovery and
|
|
15
|
+
> exact secret retrieval. Do not ask the user for the vault password just to get
|
|
16
|
+
> a key, do not use interactive prompts, and do not walk every service.
|
|
17
|
+
|
|
18
|
+
### Method 1 — Local daemon (preferred for agents)
|
|
19
|
+
|
|
20
|
+
Discover services without reading secrets:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -s http://127.0.0.1:52437/ping
|
|
24
|
+
curl -s http://127.0.0.1:52437/knowledge
|
|
25
|
+
curl -s http://127.0.0.1:52437/list-services
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Retrieve exactly one credential only when the next command needs it:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
curl -s http://127.0.0.1:52437/v/<service>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
`/knowledge` is safe to cache for a session: it returns service metadata and
|
|
35
|
+
`has_key`, but no raw secrets and no Vault secret names. `/status` uses the same
|
|
36
|
+
short metadata cache and can be forced with `?refresh=1`.
|
|
37
|
+
|
|
38
|
+
### Method 2 — CLI with `--pw` flag (admin/setup only)
|
|
39
|
+
|
|
40
|
+
All clauth commands accept `-p` / `--pw <password>` to skip the interactive
|
|
41
|
+
password prompt. Use this for local admin/setup tasks, not routine agent secret
|
|
42
|
+
retrieval:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
clauth status -p "YourPassword"
|
|
46
|
+
clauth test -p "YourPassword"
|
|
47
|
+
clauth enable github -p "YourPassword"
|
|
48
|
+
clauth write key github -p "YourPassword" # still prompts for the key value
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Method 3 — Direct API calls (implementation/debugging only)
|
|
52
|
+
|
|
53
|
+
Call the auth-vault Edge Function directly only when debugging clauth internals.
|
|
54
|
+
Normal agents should not bypass the daemon metadata cache.
|
|
55
55
|
|
|
56
56
|
**Base URL:** `https://<project-ref>.supabase.co/functions/v1/auth-vault`
|
|
57
57
|
**Auth header:** `Authorization: Bearer <supabase-anon-key>`
|
|
@@ -97,12 +97,12 @@ const timestamp = window * windowMs;
|
|
|
97
97
|
|
|
98
98
|
All auth routes require: `machine_hash`, `token`, `timestamp`, `password`.
|
|
99
99
|
|
|
100
|
-
### Password Handling
|
|
101
|
-
|
|
102
|
-
- Do not ask the user for their clauth password for routine credential lookup
|
|
103
|
-
- Use the unlocked local daemon whenever possible
|
|
104
|
-
- Never log or echo the password
|
|
105
|
-
- If the daemon is locked, ask the user to unlock `http://127.0.0.1:52437`
|
|
100
|
+
### Password Handling
|
|
101
|
+
|
|
102
|
+
- Do not ask the user for their clauth password for routine credential lookup
|
|
103
|
+
- Use the unlocked local daemon whenever possible
|
|
104
|
+
- Never log or echo the password
|
|
105
|
+
- If the daemon is locked, ask the user to unlock `http://127.0.0.1:52437`
|
|
106
106
|
|
|
107
107
|
---
|
|
108
108
|
|
|
@@ -165,14 +165,14 @@ See `references/keys-guide.md` for where to find every credential.
|
|
|
165
165
|
```
|
|
166
166
|
clauth install [--ref R] [--pat P] First-time: provision Supabase + install skill
|
|
167
167
|
clauth setup [--admin-token T] [-p P] Register this machine
|
|
168
|
-
clauth status [-p P] All services + state
|
|
169
|
-
clauth test [-p P] Verify HMAC connection
|
|
170
|
-
clauth list [-p P] Service names
|
|
171
|
-
clauth search <query> [-p P] Search names and metadata
|
|
172
|
-
clauth search <query> --addresses [-p P]
|
|
173
|
-
Also search redacted address hints (may read multiple secrets)
|
|
174
|
-
|
|
175
|
-
clauth write key <service> [-p P] Store a credential
|
|
168
|
+
clauth status [-p P] All services + state
|
|
169
|
+
clauth test [-p P] Verify HMAC connection
|
|
170
|
+
clauth list [-p P] Service names
|
|
171
|
+
clauth search <query> [-p P] Search names and metadata
|
|
172
|
+
clauth search <query> --addresses [-p P]
|
|
173
|
+
Also search redacted address hints (may read multiple secrets)
|
|
174
|
+
|
|
175
|
+
clauth write key <service> [-p P] Store a credential
|
|
176
176
|
clauth write pw [-p P] Change password
|
|
177
177
|
clauth enable <svc|all> [-p P] Activate service
|
|
178
178
|
clauth disable <svc|all> [-p P] Suspend service
|
|
@@ -201,65 +201,65 @@ Service type `fileserver` — mount configuration for fs tools (UI-only config).
|
|
|
201
201
|
|
|
202
202
|
---
|
|
203
203
|
|
|
204
|
-
## Daemon Metadata Cache
|
|
205
|
-
|
|
206
|
-
When discovering service names, do not retrieve secrets. Use the daemon metadata
|
|
207
|
-
cache:
|
|
208
|
-
|
|
209
|
-
```bash
|
|
210
|
-
curl -s http://127.0.0.1:52437/knowledge
|
|
211
|
-
curl -s http://127.0.0.1:52437/list-services
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
`/knowledge` is agent-safe: it returns service metadata and `has_key`, but no
|
|
215
|
-
raw secrets and no Vault secret names. Fetch `GET /v/<service>` only for the one
|
|
216
|
-
exact credential needed by the next command. Never walk every service and never
|
|
217
|
-
bulk-fetch all secrets.
|
|
218
|
-
|
|
219
|
-
## MCP Server (v1.16.9+)
|
|
220
|
-
|
|
221
|
-
clauth runs as an MCP server with five namespaced paths and safe
|
|
222
|
-
credential-discovery tools:
|
|
223
|
-
|
|
224
|
-
| Path | Namespace | Tools |
|
|
225
|
-
|------|-----------|-------|
|
|
226
|
-
| `/clauth` | `clauth_*` + runtime helpers | 23 default live tools, including `clauth_knowledge` |
|
|
227
|
-
| `/gws` | `gws_*` | 6 Google Workspace tools |
|
|
228
|
-
| `/fs` | `fs_*` | 20 filesystem and git-safe file tools |
|
|
229
|
-
| `/chitchat` | `chitchat_*` | 7 collaboration relay tools |
|
|
230
|
-
| `/codevelop` | `codevelop_*` | 7 peer development tools |
|
|
231
|
-
| `/mcp` | all | 63 tools combined |
|
|
232
|
-
|
|
233
|
-
The default live `/clauth` surface includes credential tools plus
|
|
234
|
-
`call_agent`, `monkey_dispatch`, `handoff_start`, `terminal_*`, and `channel_*`.
|
|
235
|
-
Admin write tools are gated by write-mode.
|
|
204
|
+
## Daemon Metadata Cache
|
|
205
|
+
|
|
206
|
+
When discovering service names, do not retrieve secrets. Use the daemon metadata
|
|
207
|
+
cache:
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
curl -s http://127.0.0.1:52437/knowledge
|
|
211
|
+
curl -s http://127.0.0.1:52437/list-services
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
`/knowledge` is agent-safe: it returns service metadata and `has_key`, but no
|
|
215
|
+
raw secrets and no Vault secret names. Fetch `GET /v/<service>` only for the one
|
|
216
|
+
exact credential needed by the next command. Never walk every service and never
|
|
217
|
+
bulk-fetch all secrets.
|
|
218
|
+
|
|
219
|
+
## MCP Server (v1.16.9+)
|
|
220
|
+
|
|
221
|
+
clauth runs as an MCP server with five namespaced paths and safe
|
|
222
|
+
credential-discovery tools:
|
|
223
|
+
|
|
224
|
+
| Path | Namespace | Tools |
|
|
225
|
+
|------|-----------|-------|
|
|
226
|
+
| `/clauth` | `clauth_*` + runtime helpers | 23 default live tools, including `clauth_knowledge` |
|
|
227
|
+
| `/gws` | `gws_*` | 6 Google Workspace tools |
|
|
228
|
+
| `/fs` | `fs_*` | 20 filesystem and git-safe file tools |
|
|
229
|
+
| `/chitchat` | `chitchat_*` | 7 collaboration relay tools |
|
|
230
|
+
| `/codevelop` | `codevelop_*` | 7 peer development tools |
|
|
231
|
+
| `/mcp` | all | 63 tools combined |
|
|
232
|
+
|
|
233
|
+
The default live `/clauth` surface includes credential tools plus
|
|
234
|
+
`call_agent`, `monkey_dispatch`, `handoff_start`, `terminal_*`, and `channel_*`.
|
|
235
|
+
Admin write tools are gated by write-mode.
|
|
236
236
|
|
|
237
237
|
### claude.ai Connector URLs (noauth mode)
|
|
238
|
-
- `https://clauth.regendevcorp.com/clauth` — credential tools
|
|
239
|
-
- `https://clauth.regendevcorp.com/gws` — Google Workspace
|
|
240
|
-
- `https://clauth.regendevcorp.com/chitchat` — collaboration relay tools
|
|
241
|
-
- `https://clauth.regendevcorp.com/codevelop` — peer development tools
|
|
242
|
-
- `https://fs.regendevcorp.com/fs` — filesystem tools
|
|
243
|
-
|
|
244
|
-
Noauth mode: fresh domains that return 404 on OAuth endpoints. claude.ai connects directly (Anthropic OAuth proxy bug workaround).
|
|
245
|
-
|
|
246
|
-
Use `clauth_knowledge` or `clauth_status` for discovery. Use `clauth_get` only
|
|
247
|
-
for one exact secret. `clauth_search` is metadata-only by default; pass
|
|
248
|
-
`addresses: true` only when redacted address hints are intentionally needed.
|
|
249
|
-
`clauth_inject` is guarded against accidental bulk vault sweeps.
|
|
250
|
-
|
|
251
|
-
### FS Tools
|
|
252
|
-
`fs_read` `fs_write` `fs_stat` `fs_append` `fs_write_chunk` `fs_ingest_url`
|
|
253
|
-
`fs_import_git_files` `fs_list` `fs_grep` `fs_glob` `fs_delete` `fs_mkdir`
|
|
254
|
-
`fs_edit` `fs_move` `fs_copy` `fs_mounts` `fs_repo_status` `fs_use_branch`
|
|
255
|
-
`fs_commit` `fs_diff`
|
|
238
|
+
- `https://clauth.regendevcorp.com/clauth` — credential tools
|
|
239
|
+
- `https://clauth.regendevcorp.com/gws` — Google Workspace
|
|
240
|
+
- `https://clauth.regendevcorp.com/chitchat` — collaboration relay tools
|
|
241
|
+
- `https://clauth.regendevcorp.com/codevelop` — peer development tools
|
|
242
|
+
- `https://fs.regendevcorp.com/fs` — filesystem tools
|
|
243
|
+
|
|
244
|
+
Noauth mode: fresh domains that return 404 on OAuth endpoints. claude.ai connects directly (Anthropic OAuth proxy bug workaround).
|
|
245
|
+
|
|
246
|
+
Use `clauth_knowledge` or `clauth_status` for discovery. Use `clauth_get` only
|
|
247
|
+
for one exact secret. `clauth_search` is metadata-only by default; pass
|
|
248
|
+
`addresses: true` only when redacted address hints are intentionally needed.
|
|
249
|
+
`clauth_inject` is guarded against accidental bulk vault sweeps.
|
|
250
|
+
|
|
251
|
+
### FS Tools
|
|
252
|
+
`fs_read` `fs_write` `fs_stat` `fs_append` `fs_write_chunk` `fs_ingest_url`
|
|
253
|
+
`fs_import_git_files` `fs_list` `fs_grep` `fs_glob` `fs_delete` `fs_mkdir`
|
|
254
|
+
`fs_edit` `fs_move` `fs_copy` `fs_mounts` `fs_repo_status` `fs_use_branch`
|
|
255
|
+
`fs_commit` `fs_diff`
|
|
256
256
|
|
|
257
257
|
Path-jail security: all paths resolved against mount root. Permission flags (r/w/d) per mount. Uses `@vscode/ripgrep` for grep, `fast-glob` for glob.
|
|
258
258
|
|
|
259
|
-
### Testing
|
|
260
|
-
```bash
|
|
261
|
-
node test-tools.mjs # MCP tool execution smoke tests
|
|
262
|
-
```
|
|
259
|
+
### Testing
|
|
260
|
+
```bash
|
|
261
|
+
node test-tools.mjs # MCP tool execution smoke tests
|
|
262
|
+
```
|
|
263
263
|
|
|
264
264
|
---
|
|
265
265
|
|
package/cli/api.classify.test.js
CHANGED
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
// cli/api.classify.test.js
|
|
2
|
-
// Regression tests for backend/external-resource error classification.
|
|
3
|
-
//
|
|
4
|
-
// WHY THIS EXISTS: on 2026-06-19 a saturated Postgres database made every
|
|
5
|
-
// auth-vault verify call time out. clauth counted each timeout as an
|
|
6
|
-
// authentication failure ([AUTH FAIL n/10]), hit the strike ceiling, and
|
|
7
|
-
// hard-locked the vault — turning a transient DB blip into a full lockout that
|
|
8
|
-
// blocked Claude/Codex from starting. A database timeout, a 5xx, a network
|
|
9
|
-
// drop, or a rate-limit is NOT a wrong password and must never take a strike.
|
|
10
|
-
// These tests pin that classification so the regression can't return.
|
|
11
|
-
|
|
12
|
-
import { test } from "node:test";
|
|
13
|
-
import assert from "node:assert/strict";
|
|
14
|
-
import {
|
|
15
|
-
VaultBackendError,
|
|
16
|
-
classifyBackendError,
|
|
17
|
-
classifyServerReason,
|
|
18
|
-
} from "./api.js";
|
|
19
|
-
|
|
20
|
-
test("VaultBackendError marks failures as non-strike + retriable", () => {
|
|
21
|
-
const e = new VaultBackendError("timeout", "no response", { status: 504, detail: "abort" });
|
|
22
|
-
assert.equal(e.kind, "timeout");
|
|
23
|
-
assert.equal(e.isBackend, true);
|
|
24
|
-
assert.equal(e.retriable, true);
|
|
25
|
-
assert.equal(e.status, 504);
|
|
26
|
-
assert.ok(e instanceof Error);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
test("classifyBackendError: AbortSignal.timeout shapes => 'timeout'", () => {
|
|
30
|
-
// This is the exact error that slipped through the old substring check.
|
|
31
|
-
assert.equal(classifyBackendError({ name: "TimeoutError", message: "The operation was aborted due to timeout" }), "timeout");
|
|
32
|
-
assert.equal(classifyBackendError({ name: "AbortError", message: "This operation was aborted" }), "timeout");
|
|
33
|
-
assert.equal(classifyBackendError(new Error("request timed out")), "timeout");
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test("classifyBackendError: network/transport shapes => 'network'", () => {
|
|
37
|
-
assert.equal(classifyBackendError(new Error("fetch failed")), "network");
|
|
38
|
-
assert.equal(classifyBackendError(new Error("connect ECONNREFUSED 127.0.0.1:443")), "network");
|
|
39
|
-
assert.equal(classifyBackendError(new Error("getaddrinfo ENOTFOUND db.supabase.co")), "network");
|
|
40
|
-
assert.equal(classifyBackendError(new Error("read ECONNRESET")), "network");
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
test("classifyBackendError: a typed VaultBackendError passes its kind through", () => {
|
|
44
|
-
assert.equal(classifyBackendError(new VaultBackendError("rate_limited", "slow down")), "rate_limited");
|
|
45
|
-
assert.equal(classifyBackendError(new VaultBackendError("db_error", "pool exhausted")), "db_error");
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("classifyBackendError: a genuine credential error is NOT a backend error", () => {
|
|
49
|
-
assert.equal(classifyBackendError(new Error("Wrong password")), null);
|
|
50
|
-
assert.equal(classifyBackendError(new Error("invalid_token")), null);
|
|
51
|
-
assert.equal(classifyBackendError(null), null);
|
|
52
|
-
assert.equal(classifyBackendError(undefined), null);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
test("classifyServerReason: rate-limit reasons => 'rate_limited'", () => {
|
|
56
|
-
assert.equal(classifyServerReason("rate_limited"), "rate_limited");
|
|
57
|
-
assert.equal(classifyServerReason("Rate limit: 38/30 per 60s"), "rate_limited");
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
test("classifyServerReason: database/backend reasons => 'db_error'", () => {
|
|
61
|
-
assert.equal(classifyServerReason("canceling statement due to statement timeout"), "db_error");
|
|
62
|
-
assert.equal(classifyServerReason("database connection pool exhausted"), "db_error");
|
|
63
|
-
assert.equal(classifyServerReason("internal server error"), "db_error");
|
|
64
|
-
assert.equal(classifyServerReason("service unavailable"), "db_error");
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("classifyServerReason: genuine auth verdicts are NOT backend errors (they strike)", () => {
|
|
68
|
-
assert.equal(classifyServerReason("invalid_token (3/5)"), null);
|
|
69
|
-
assert.equal(classifyServerReason("machine_locked"), null);
|
|
70
|
-
assert.equal(classifyServerReason("machine_disabled"), null);
|
|
71
|
-
assert.equal(classifyServerReason("machine_not_found"), null);
|
|
72
|
-
assert.equal(classifyServerReason("wrong_password"), null);
|
|
73
|
-
assert.equal(classifyServerReason(null), null);
|
|
74
|
-
assert.equal(classifyServerReason(""), null);
|
|
75
|
-
});
|
|
1
|
+
// cli/api.classify.test.js
|
|
2
|
+
// Regression tests for backend/external-resource error classification.
|
|
3
|
+
//
|
|
4
|
+
// WHY THIS EXISTS: on 2026-06-19 a saturated Postgres database made every
|
|
5
|
+
// auth-vault verify call time out. clauth counted each timeout as an
|
|
6
|
+
// authentication failure ([AUTH FAIL n/10]), hit the strike ceiling, and
|
|
7
|
+
// hard-locked the vault — turning a transient DB blip into a full lockout that
|
|
8
|
+
// blocked Claude/Codex from starting. A database timeout, a 5xx, a network
|
|
9
|
+
// drop, or a rate-limit is NOT a wrong password and must never take a strike.
|
|
10
|
+
// These tests pin that classification so the regression can't return.
|
|
11
|
+
|
|
12
|
+
import { test } from "node:test";
|
|
13
|
+
import assert from "node:assert/strict";
|
|
14
|
+
import {
|
|
15
|
+
VaultBackendError,
|
|
16
|
+
classifyBackendError,
|
|
17
|
+
classifyServerReason,
|
|
18
|
+
} from "./api.js";
|
|
19
|
+
|
|
20
|
+
test("VaultBackendError marks failures as non-strike + retriable", () => {
|
|
21
|
+
const e = new VaultBackendError("timeout", "no response", { status: 504, detail: "abort" });
|
|
22
|
+
assert.equal(e.kind, "timeout");
|
|
23
|
+
assert.equal(e.isBackend, true);
|
|
24
|
+
assert.equal(e.retriable, true);
|
|
25
|
+
assert.equal(e.status, 504);
|
|
26
|
+
assert.ok(e instanceof Error);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test("classifyBackendError: AbortSignal.timeout shapes => 'timeout'", () => {
|
|
30
|
+
// This is the exact error that slipped through the old substring check.
|
|
31
|
+
assert.equal(classifyBackendError({ name: "TimeoutError", message: "The operation was aborted due to timeout" }), "timeout");
|
|
32
|
+
assert.equal(classifyBackendError({ name: "AbortError", message: "This operation was aborted" }), "timeout");
|
|
33
|
+
assert.equal(classifyBackendError(new Error("request timed out")), "timeout");
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("classifyBackendError: network/transport shapes => 'network'", () => {
|
|
37
|
+
assert.equal(classifyBackendError(new Error("fetch failed")), "network");
|
|
38
|
+
assert.equal(classifyBackendError(new Error("connect ECONNREFUSED 127.0.0.1:443")), "network");
|
|
39
|
+
assert.equal(classifyBackendError(new Error("getaddrinfo ENOTFOUND db.supabase.co")), "network");
|
|
40
|
+
assert.equal(classifyBackendError(new Error("read ECONNRESET")), "network");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("classifyBackendError: a typed VaultBackendError passes its kind through", () => {
|
|
44
|
+
assert.equal(classifyBackendError(new VaultBackendError("rate_limited", "slow down")), "rate_limited");
|
|
45
|
+
assert.equal(classifyBackendError(new VaultBackendError("db_error", "pool exhausted")), "db_error");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("classifyBackendError: a genuine credential error is NOT a backend error", () => {
|
|
49
|
+
assert.equal(classifyBackendError(new Error("Wrong password")), null);
|
|
50
|
+
assert.equal(classifyBackendError(new Error("invalid_token")), null);
|
|
51
|
+
assert.equal(classifyBackendError(null), null);
|
|
52
|
+
assert.equal(classifyBackendError(undefined), null);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("classifyServerReason: rate-limit reasons => 'rate_limited'", () => {
|
|
56
|
+
assert.equal(classifyServerReason("rate_limited"), "rate_limited");
|
|
57
|
+
assert.equal(classifyServerReason("Rate limit: 38/30 per 60s"), "rate_limited");
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test("classifyServerReason: database/backend reasons => 'db_error'", () => {
|
|
61
|
+
assert.equal(classifyServerReason("canceling statement due to statement timeout"), "db_error");
|
|
62
|
+
assert.equal(classifyServerReason("database connection pool exhausted"), "db_error");
|
|
63
|
+
assert.equal(classifyServerReason("internal server error"), "db_error");
|
|
64
|
+
assert.equal(classifyServerReason("service unavailable"), "db_error");
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test("classifyServerReason: genuine auth verdicts are NOT backend errors (they strike)", () => {
|
|
68
|
+
assert.equal(classifyServerReason("invalid_token (3/5)"), null);
|
|
69
|
+
assert.equal(classifyServerReason("machine_locked"), null);
|
|
70
|
+
assert.equal(classifyServerReason("machine_disabled"), null);
|
|
71
|
+
assert.equal(classifyServerReason("machine_not_found"), null);
|
|
72
|
+
assert.equal(classifyServerReason("wrong_password"), null);
|
|
73
|
+
assert.equal(classifyServerReason(null), null);
|
|
74
|
+
assert.equal(classifyServerReason(""), null);
|
|
75
|
+
});
|
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
setlocal
|
|
3
|
-
set "CODEVELOP_SESSION=${SESSION_ID}"
|
|
4
|
-
set "CODEVELOP_PEER=${PEER}"
|
|
5
|
-
set "CODEVELOP_TARGET=${TARGET_PEER}"
|
|
6
|
-
set "CODEVELOP_BASE_URL=${BASE_URL}"
|
|
7
|
-
set "CODEVELOP_MANIFEST=${MANIFEST}"
|
|
8
|
-
set "CODEVELOP_CONTEXT=${CONTEXT_FILE}"
|
|
9
|
-
set "CELL_ROLE=codevelop-${PEER}"
|
|
10
|
-
cd /d "${CWD}"
|
|
11
|
-
if /i "%~1"=="--print-only" (
|
|
12
|
-
echo command: ${PRINT_COMMAND}
|
|
13
|
-
exit /b 0
|
|
14
|
-
)
|
|
15
|
-
echo Co-develop ${PEER} ready. Session ${SESSION_ID}. Listening for collaboration.
|
|
16
|
-
echo Context: ${CONTEXT_FILE}
|
|
17
|
-
node "${CLAUTH_CLI}" codevelop check-partner --repo "${REPO}" --peer ${PEER}
|
|
18
|
-
${COMMAND}
|
|
19
|
-
set EXITCODE=%ERRORLEVEL%
|
|
20
|
-
endlocal & exit /b %EXITCODE%
|
|
1
|
+
@echo off
|
|
2
|
+
setlocal
|
|
3
|
+
set "CODEVELOP_SESSION=${SESSION_ID}"
|
|
4
|
+
set "CODEVELOP_PEER=${PEER}"
|
|
5
|
+
set "CODEVELOP_TARGET=${TARGET_PEER}"
|
|
6
|
+
set "CODEVELOP_BASE_URL=${BASE_URL}"
|
|
7
|
+
set "CODEVELOP_MANIFEST=${MANIFEST}"
|
|
8
|
+
set "CODEVELOP_CONTEXT=${CONTEXT_FILE}"
|
|
9
|
+
set "CELL_ROLE=codevelop-${PEER}"
|
|
10
|
+
cd /d "${CWD}"
|
|
11
|
+
if /i "%~1"=="--print-only" (
|
|
12
|
+
echo command: ${PRINT_COMMAND}
|
|
13
|
+
exit /b 0
|
|
14
|
+
)
|
|
15
|
+
echo Co-develop ${PEER} ready. Session ${SESSION_ID}. Listening for collaboration.
|
|
16
|
+
echo Context: ${CONTEXT_FILE}
|
|
17
|
+
node "${CLAUTH_CLI}" codevelop check-partner --repo "${REPO}" --peer ${PEER}
|
|
18
|
+
${COMMAND}
|
|
19
|
+
set EXITCODE=%ERRORLEVEL%
|
|
20
|
+
endlocal & exit /b %EXITCODE%
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
@echo off
|
|
2
|
-
if not exist "${ACTIVE_LAUNCHER}" (
|
|
3
|
-
echo No active co-develop launcher has been written.
|
|
4
|
-
echo Run: clauth codevelop start --repo "${REPO}" --port ${PORT} --start-isolated-clauth
|
|
5
|
-
exit /b 1
|
|
6
|
-
)
|
|
7
|
-
call "${ACTIVE_LAUNCHER}" %*
|
|
1
|
+
@echo off
|
|
2
|
+
if not exist "${ACTIVE_LAUNCHER}" (
|
|
3
|
+
echo No active co-develop launcher has been written.
|
|
4
|
+
echo Run: clauth codevelop start --repo "${REPO}" --port ${PORT} --start-isolated-clauth
|
|
5
|
+
exit /b 1
|
|
6
|
+
)
|
|
7
|
+
call "${ACTIVE_LAUNCHER}" %*
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
{
|
|
2
|
-
"profiles": [
|
|
3
|
-
{
|
|
4
|
-
"name": "Co Develop Claude",
|
|
5
|
-
"guid": "{a2f8548f-82e6-44b4-bdbb-d934e8881f01}",
|
|
6
|
-
"commandline": "cmd.exe /k \"${CLAUTH_APPDATA}\\codevelop-claude.cmd\"",
|
|
7
|
-
"startingDirectory": "${REPO}",
|
|
8
|
-
"environment": {
|
|
9
|
-
"CELL_ROLE": "codevelop-claude"
|
|
10
|
-
},
|
|
11
|
-
"icon": "🔴",
|
|
12
|
-
"tabColor": "#B91C1C",
|
|
13
|
-
"colorScheme": "Dimidium"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"name": "Co Develop Codex",
|
|
17
|
-
"guid": "{31a52b07-6efd-4b3d-96dd-ded73488140a}",
|
|
18
|
-
"commandline": "cmd.exe /k \"${CLAUTH_APPDATA}\\codevelop-codex.cmd\"",
|
|
19
|
-
"startingDirectory": "${REPO}",
|
|
20
|
-
"environment": {
|
|
21
|
-
"CELL_ROLE": "codevelop-codex"
|
|
22
|
-
},
|
|
23
|
-
"icon": "🧬",
|
|
24
|
-
"tabColor": "#0F766E",
|
|
25
|
-
"colorScheme": "Dimidium"
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"layout": {
|
|
29
|
-
"wtArgs": [
|
|
30
|
-
"-w",
|
|
31
|
-
"new",
|
|
32
|
-
"new-tab",
|
|
33
|
-
"--profile",
|
|
34
|
-
"Co Develop Claude",
|
|
35
|
-
"--title",
|
|
36
|
-
"Co Develop Claude",
|
|
37
|
-
";",
|
|
38
|
-
"split-pane",
|
|
39
|
-
"-V",
|
|
40
|
-
"--size",
|
|
41
|
-
"0.50",
|
|
42
|
-
"--profile",
|
|
43
|
-
"Co Develop Codex",
|
|
44
|
-
"--title",
|
|
45
|
-
"Co Develop Codex"
|
|
46
|
-
]
|
|
47
|
-
}
|
|
48
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"profiles": [
|
|
3
|
+
{
|
|
4
|
+
"name": "Co Develop Claude",
|
|
5
|
+
"guid": "{a2f8548f-82e6-44b4-bdbb-d934e8881f01}",
|
|
6
|
+
"commandline": "cmd.exe /k \"${CLAUTH_APPDATA}\\codevelop-claude.cmd\"",
|
|
7
|
+
"startingDirectory": "${REPO}",
|
|
8
|
+
"environment": {
|
|
9
|
+
"CELL_ROLE": "codevelop-claude"
|
|
10
|
+
},
|
|
11
|
+
"icon": "🔴",
|
|
12
|
+
"tabColor": "#B91C1C",
|
|
13
|
+
"colorScheme": "Dimidium"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"name": "Co Develop Codex",
|
|
17
|
+
"guid": "{31a52b07-6efd-4b3d-96dd-ded73488140a}",
|
|
18
|
+
"commandline": "cmd.exe /k \"${CLAUTH_APPDATA}\\codevelop-codex.cmd\"",
|
|
19
|
+
"startingDirectory": "${REPO}",
|
|
20
|
+
"environment": {
|
|
21
|
+
"CELL_ROLE": "codevelop-codex"
|
|
22
|
+
},
|
|
23
|
+
"icon": "🧬",
|
|
24
|
+
"tabColor": "#0F766E",
|
|
25
|
+
"colorScheme": "Dimidium"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"layout": {
|
|
29
|
+
"wtArgs": [
|
|
30
|
+
"-w",
|
|
31
|
+
"new",
|
|
32
|
+
"new-tab",
|
|
33
|
+
"--profile",
|
|
34
|
+
"Co Develop Claude",
|
|
35
|
+
"--title",
|
|
36
|
+
"Co Develop Claude",
|
|
37
|
+
";",
|
|
38
|
+
"split-pane",
|
|
39
|
+
"-V",
|
|
40
|
+
"--size",
|
|
41
|
+
"0.50",
|
|
42
|
+
"--profile",
|
|
43
|
+
"Co Develop Codex",
|
|
44
|
+
"--title",
|
|
45
|
+
"Co Develop Codex"
|
|
46
|
+
]
|
|
47
|
+
}
|
|
48
|
+
}
|
package/cli/assets/watchdog.ps1
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
# clauth-watchdog.ps1
|
|
2
|
-
# Monitors clauth daemon on port 52437. Restarts it if not responding.
|
|
3
|
-
# Installed by: npm install -g @lifeaitools/clauth
|
|
4
|
-
# Managed by: clauth watchdog [install|uninstall|status]
|
|
5
|
-
|
|
6
|
-
$clauth = "$env:APPDATA\npm\clauth.cmd"
|
|
7
|
-
$logFile = "$env:APPDATA\clauth\watchdog.log"
|
|
8
|
-
$pingUrl = "http://127.0.0.1:52437/ping"
|
|
9
|
-
$interval = 15
|
|
10
|
-
|
|
11
|
-
function Write-Log($msg) {
|
|
12
|
-
$ts = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
13
|
-
$line = "[$ts] $msg"
|
|
14
|
-
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
Write-Log "Watchdog started (PID $PID, clauth=$clauth)"
|
|
18
|
-
|
|
19
|
-
while ($true) {
|
|
20
|
-
$alive = $false
|
|
21
|
-
try {
|
|
22
|
-
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
|
|
23
|
-
if ($r.StatusCode -eq 200) { $alive = $true }
|
|
24
|
-
} catch { }
|
|
25
|
-
|
|
26
|
-
if (-not $alive) {
|
|
27
|
-
Write-Log "Daemon not responding — restarting..."
|
|
28
|
-
Start-Process -FilePath "cmd.exe" `
|
|
29
|
-
-ArgumentList "/c `"$clauth`" serve start" `
|
|
30
|
-
-WindowStyle Hidden
|
|
31
|
-
Start-Sleep -Seconds 5
|
|
32
|
-
try {
|
|
33
|
-
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
|
|
34
|
-
if ($r.StatusCode -eq 200) { Write-Log "Daemon restarted OK." }
|
|
35
|
-
else { Write-Log "WARNING: Daemon still not responding." }
|
|
36
|
-
} catch {
|
|
37
|
-
Write-Log "WARNING: Daemon still not responding after restart."
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
Start-Sleep -Seconds $interval
|
|
42
|
-
}
|
|
1
|
+
# clauth-watchdog.ps1
|
|
2
|
+
# Monitors clauth daemon on port 52437. Restarts it if not responding.
|
|
3
|
+
# Installed by: npm install -g @lifeaitools/clauth
|
|
4
|
+
# Managed by: clauth watchdog [install|uninstall|status]
|
|
5
|
+
|
|
6
|
+
$clauth = "$env:APPDATA\npm\clauth.cmd"
|
|
7
|
+
$logFile = "$env:APPDATA\clauth\watchdog.log"
|
|
8
|
+
$pingUrl = "http://127.0.0.1:52437/ping"
|
|
9
|
+
$interval = 15
|
|
10
|
+
|
|
11
|
+
function Write-Log($msg) {
|
|
12
|
+
$ts = (Get-Date).ToUniversalTime().ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
13
|
+
$line = "[$ts] $msg"
|
|
14
|
+
Add-Content -Path $logFile -Value $line -ErrorAction SilentlyContinue
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
Write-Log "Watchdog started (PID $PID, clauth=$clauth)"
|
|
18
|
+
|
|
19
|
+
while ($true) {
|
|
20
|
+
$alive = $false
|
|
21
|
+
try {
|
|
22
|
+
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 3 -UseBasicParsing -ErrorAction Stop
|
|
23
|
+
if ($r.StatusCode -eq 200) { $alive = $true }
|
|
24
|
+
} catch { }
|
|
25
|
+
|
|
26
|
+
if (-not $alive) {
|
|
27
|
+
Write-Log "Daemon not responding — restarting..."
|
|
28
|
+
Start-Process -FilePath "cmd.exe" `
|
|
29
|
+
-ArgumentList "/c `"$clauth`" serve start" `
|
|
30
|
+
-WindowStyle Hidden
|
|
31
|
+
Start-Sleep -Seconds 5
|
|
32
|
+
try {
|
|
33
|
+
$r = Invoke-WebRequest -Uri $pingUrl -TimeoutSec 5 -UseBasicParsing -ErrorAction Stop
|
|
34
|
+
if ($r.StatusCode -eq 200) { Write-Log "Daemon restarted OK." }
|
|
35
|
+
else { Write-Log "WARNING: Daemon still not responding." }
|
|
36
|
+
} catch {
|
|
37
|
+
Write-Log "WARNING: Daemon still not responding after restart."
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
Start-Sleep -Seconds $interval
|
|
42
|
+
}
|