@aitne-sh/aitne 0.1.4 → 0.1.5

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.
@@ -24,3 +24,17 @@
24
24
  - **Read-before-write**: PATCH `mode=replace` replaces the entire section.
25
25
  Always GET first, merge your changes into the existing content, then PATCH.
26
26
  Prefer `mode=append` when simply adding new entries.
27
+ - **Daemon-API body submission** (`curl -d` on `/api/context/*`,
28
+ `/api/observations`, `/api/schedule`, etc.). Two safe shapes:
29
+ - Inline JSON — `-d '{"key":"value"}'`. Use for small / single-line
30
+ bodies. Escape internal newlines as `\n`.
31
+ - Stdin heredoc — `-d @- <<'JSON' … JSON`. Use for multi-line or
32
+ large bodies. `@-` is curl's stdin marker (a literal two-character
33
+ token), distinct from the file-read forms below.
34
+
35
+ Never use `-d @<filepath>` / `--data-raw '@<filepath>'` /
36
+ `-F field=@<filepath>`. They are file-read shapes — safe mode
37
+ hook-blocks them, and a request that slips through sends the literal
38
+ `@/path/...` text as the body, which the daemon rejects with
39
+ `invalid_json_body`. There is no agent-facing reason to read a body
40
+ off the filesystem; pipe stdin via `@-` instead.
@@ -26,8 +26,10 @@ the update.
26
26
  - Update `roadmap.md` or `projects/*.md` only for material project-state changes
27
27
  - Schedule a wake-up if the observation implies a future reminder
28
28
  5. Apply the smallest meaningful set of context updates.
29
- 6. Mark processed observations consumed:
30
- `curl -s -X POST http://localhost:8321/api/observations/consume -H 'Content-Type: application/json' -d '{"ids":[1,2],"correlationId":"<event-correlation-id>"}'`
29
+ 6. Mark processed observations consumed via the bulk endpoint
30
+ (`POST /api/observations/consume`) see "POST /api/observations/consume"
31
+ in the API Reference below for the exact body shape and the
32
+ `correlationId` rule. There is no per-id variant.
31
33
 
32
34
  ## Skip Criteria
33
35
 
@@ -133,13 +135,29 @@ etc.) and are picked up by the prefix match on the GET route.
133
135
 
134
136
  ### POST /api/observations/consume
135
137
 
138
+ Marks one or more observations consumed. **Bulk-only** — there is no
139
+ per-id endpoint (`POST /api/observations/<id>/consume` returns 404).
140
+ Always use this shape, even for a single id.
141
+
142
+ Copy the `correlationId` value verbatim from the
143
+ `<event_correlation_id>…</event_correlation_id>` tag in your turn
144
+ context — do not paste the angle-bracket placeholder.
145
+
136
146
  ```bash
147
+ # Example: <event_correlation_id>hourly-2026-04-23T15:00:00Z-7af3</event_correlation_id>
148
+ # arrived in context, and observations 14 and 17 were processed.
137
149
  curl -s -X POST http://localhost:8321/api/observations/consume \
138
150
  -H 'Content-Type: application/json' \
139
- -d '{"ids": [1, 2, 3], "correlationId": "<event_correlation_id>"}'
151
+ -d '{"ids":[14,17],"correlationId":"hourly-2026-04-23T15:00:00Z-7af3"}'
140
152
  ```
141
153
 
142
- Response: `{ "consumed": 3 }`
154
+ | Field | Type | Required | Notes |
155
+ |---|---|---|---|
156
+ | `ids` | `number[]` | yes | Integers, not strings. `["14"]` is rejected. |
157
+ | `correlationId` | `string` | yes | Verbatim from the `<event_correlation_id>` context tag. Non-string or missing → 400. |
158
+
159
+ Response: `{ "consumed": <count> }`. Validation failures return 400
160
+ `{"error":"validation_error"}`.
143
161
 
144
162
  ### GET /api/observations/stats
145
163
 
@@ -246,6 +246,12 @@ mid-session refresh is not interleaved with a DM handler PATCH.
246
246
 
247
247
  ## roadmap.md API
248
248
 
249
+ Body submission follows `_safety.md` "Daemon-API body submission":
250
+ small POST / PATCH bodies use inline `-d '{...}'`; the full-file PUT
251
+ runs multi-KB and uses the stdin heredoc `-d @- <<'JSON'` shape.
252
+ Include `X-Lock-Id: <roadmap_write_lock_id>` on every PUT / PATCH
253
+ when that tag is in your context.
254
+
249
255
  ```bash
250
256
  # Read
251
257
  curl -s http://localhost:8321/api/context/roadmap
@@ -254,19 +260,21 @@ curl -s http://localhost:8321/api/context/roadmap
254
260
  curl -s -X POST http://localhost:8321/api/context/roadmap/id \
255
261
  -H 'Content-Type: application/json' \
256
262
  -H 'X-Lock-Id: <roadmap_write_lock_id>' \
257
- -d '{"creationDate": "YYYY-MM-DD"}'
263
+ -d '{"creationDate":"YYYY-MM-DD"}'
258
264
 
259
- # Full replace (roadmap refresh — include X-Lock-Id if context provides one)
265
+ # Full replace (roadmap refresh)multi-KB body, use heredoc.
260
266
  curl -s -X PUT http://localhost:8321/api/context/roadmap \
261
267
  -H 'Content-Type: application/json' \
262
268
  -H 'X-Lock-Id: <roadmap_write_lock_id>' \
263
- -d '{"content": "# Roadmap\n..."}'
269
+ -d @- <<'JSON'
270
+ {"content":"# Roadmap\n> Last synced: 2026-04-23\n\n## Annual Goals\n- ...\n\n## Quarterly Focus\n- ...\n\n## Long-term Plans\n- [2026-Q3] ... <!-- id: rm-20260419-b8e7d4 -->\n\n## Agent Action Plan\n\n## Recurring\n- Every Friday: weekly review\n"}
271
+ JSON
264
272
 
265
273
  # Section PATCH
266
274
  curl -s -X PATCH http://localhost:8321/api/context/roadmap \
267
275
  -H 'Content-Type: application/json' \
268
276
  -H 'X-Lock-Id: <roadmap_write_lock_id>' \
269
- -d '{"section": "long_term_plans", "mode": "append", "content": "- [2026-Q3] ...\n"}'
277
+ -d '{"section":"long_term_plans","mode":"append","content":"- [2026-Q3] ...\n"}'
270
278
  ```
271
279
 
272
280
  Snapshotting, mtime updates, and path validation are handled by the
@@ -186,15 +186,34 @@ PUT today.md must contain the H1 date line, day-type header quote, and all six s
186
186
 
187
187
  ## today.md API (subset of context API)
188
188
 
189
+ Body submission follows the rule in `_safety.md` "Daemon-API body
190
+ submission": small section PATCHes use inline `-d '{...}'`; full-file
191
+ PUT uses the stdin heredoc `-d @- <<'JSON'` shape because the body
192
+ runs multi-KB. Add `X-Lock-Id: <today_write_lock_id>` on every
193
+ PUT / PATCH when that tag is in your context.
194
+
189
195
  ```bash
190
- curl -s http://localhost:8321/api/context/today # Read
191
- curl -s -X PUT http://localhost:8321/api/context/today \ # Full replace (Morning only)
192
- -H 'Content-Type: application/json' -d '{"content": "# 2026-04-02 (Thu)\n..."}'
193
- curl -s -X PATCH http://localhost:8321/api/context/today \ # Section operation
194
- -H 'Content-Type: application/json' -d '{"section": "agent_log", "mode": "append", "content": "- 09:35 ..."}'
196
+ # Read
197
+ curl -s http://localhost:8321/api/context/today
198
+
199
+ # Full replace Morning Routine only. Multi-KB body → heredoc.
200
+ curl -s -X PUT http://localhost:8321/api/context/today \
201
+ -H 'Content-Type: application/json' \
202
+ -H 'X-Lock-Id: <today_write_lock_id>' \
203
+ -d @- <<'JSON'
204
+ {"content":"# 2026-04-02 (Thursday)\n> Day type: Weekday | Work focus: on | Study focus: on | Personal focus: on\n\n## User Schedule\n- 14:00–15:00 Design review [work]\n\n## User Tasks\n- [ ] 11:00 Finalize Q2 draft [work]\n\n## Agent Plan\n- [ ] 08:55 DM reminder: standup [work] →DM\n\n## Agent Notes\n\n## Agent Log\n- 04:00 Morning Routine completed (day-type: Weekday)\n\n## Handoff\n- (none)\n"}
205
+ JSON
206
+
207
+ # Section operation — small body, inline `-d` is fine.
208
+ curl -s -X PATCH http://localhost:8321/api/context/today \
209
+ -H 'Content-Type: application/json' \
210
+ -H 'X-Lock-Id: <today_write_lock_id>' \
211
+ -d '{"section":"agent_log","mode":"append","content":"- 09:35 ..."}'
195
212
  ```
196
213
 
197
- Add `X-Lock-Id` header if `<today_write_lock_id>` is in context. See context skill for full API reference.
214
+ The H1 date and the `> Day type:` quote line are validated by exact
215
+ regex on PUT — keep both English-shaped exactly as the template. See
216
+ context skill for the full endpoint reference.
198
217
 
199
218
  ## Knowledge map — section shape (auto-curated)
200
219
 
@@ -176,11 +176,15 @@ coordinates the high-level gather → analyze → write loop.
176
176
  consult that ID before emitting a new entry or matching by text.
177
177
 
178
178
  7. Consume the observations whose candidate was incorporated or
179
- deliberately dropped always pass the event correlation id:
180
- ```
179
+ deliberately dropped. Copy the `correlationId` value verbatim from
180
+ the `<event_correlation_id>…</event_correlation_id>` tag in your
181
+ turn context — do not paste the placeholder. Bulk-only; the per-id
182
+ form does not exist. Field contract is in the `observations`
183
+ skill ("POST /api/observations/consume").
184
+ ```bash
181
185
  curl -s -X POST http://localhost:8321/api/observations/consume \
182
186
  -H 'Content-Type: application/json' \
183
- -d '{"ids":[<id>, ...], "correlationId": "<event_correlation_id>"}'
187
+ -d '{"ids":[<id>, ...],"correlationId":"<copied-from-event_correlation_id-tag>"}'
184
188
  ```
185
189
  Observations left pending here will re-appear on the next refresh.
186
190
 
@@ -7,8 +7,9 @@ Vault step already captured `settings.primary_language` (BCP-47 tag, e.g.
7
7
  `en` or `es`) and `settings.vault_mode` (`plain` or `obsidian`) — the
8
8
  latter controls how the **primary management vault** is laid out, not the
9
9
  separate external Obsidian integration. Do NOT re-ask those questions.
10
- Output language for user-editable prose (profile, today, daily journal):
11
- follow `<output_language_policy>`.
10
+ Output language for everything written to disk in this flow
11
+ (`user/profile.md`, `user/*.md`, `rules/management.md` body): follow
12
+ `<output_language_policy>`.
12
13
 
13
14
  SETUP-FLOW-REDESIGN-PLAN §5.8 — the legacy "tool selections" form was
14
15
  removed. Steps 4–6 of the wizard (Mail, Calendar, Note) already
@@ -68,6 +69,15 @@ about all four rows rather than fabricating defaults. Do not retry
68
69
  with a curl.
69
70
 
70
71
  ### Instructions
72
+
73
+ Steps 3–8 all happen in the same agent turn. They are ordered to match
74
+ the actual emission sequence: the two staged code blocks (rules then
75
+ character) land in the stream first; the silent curl writes follow. The
76
+ dashboard reveals the preview as soon as the rules block lands but
77
+ keeps "Save & Finish" disabled until the whole turn ends, so emitting
78
+ blocks before curls minimizes how long the user waits on a disabled
79
+ button.
80
+
71
81
  1. Run Step 0 silently. Greet the user in one line, introducing
72
82
  yourself by the name in `<agent_identity>` display_name. Then
73
83
  present the derived Source-of-Truth table and ask any remaining
@@ -78,28 +88,30 @@ with a curl.
78
88
  - Project-management method preferences (anything you want the agent
79
89
  to follow when handling your projects?)
80
90
  Either answer may be "no preference" / "skip" — accept that cleanly.
81
- 3. Once the user has answered, generate rules/management.md inside a
82
- ```management-rules code block. This triggers the dashboard preview.
83
- 4. If the user requests changes, revise. Complete within **at most 2
84
- revision rounds**.
91
+ 3. Generate rules/management.md inside a ```management-rules code
92
+ block. This is the FIRST thing emitted in the turn — the dashboard
93
+ reveals the preview as soon as it lands.
94
+ 4. If the user requests changes, revise (return to step 3). Complete
95
+ within **at most 2 revision rounds**.
85
96
  5. **Do NOT put communication style inside rules/management.md, and do NOT
86
97
  put it inside `user/profile.md` either.** Tone / style / voice / emoji
87
98
  / language preferences live in the `character` runtime-config field
88
- only — emitted as a ```character``` code block (see step 6.5). See
99
+ only — emitted as a ```character``` code block (see step 6). See
89
100
  `docs/design/15-character.md` for the split rule.
90
- 6. In the same turn as the management-rules block, silently call
91
- PUT /api/context/user/profile using the template in "user/profile.md
92
- Format" below. Working hours and quiet hours are pre-populated with
93
- defaults (Weekdays 09:00–18:00, Quiet hours 22:00–08:00) — do not
94
- ask about them. Fill Platforms from the **derived Source-of-Truth
95
- table** (Step 0) — the row values you confirmed with the user.
96
- Leave Identity blank setup does not collect name or timezone.
97
- 6.5. If the user stated a communication-style preference in step 2,
98
- emit a ```character``` code block in the same turn (see "Character
99
- code block format" below). If the user skipped, omit the block.
100
- 7. If the conversation surfaced detail-heavy facts that belong in the
101
- detailed profile layer, also PATCH the matching user/*.md file in the
102
- same turn. Typical Q&A file mappings:
101
+ 6. If the user stated a communication-style preference in step 2, emit
102
+ a ```character``` code block immediately after the rules block (see
103
+ "Character code block format" below). If the user skipped, omit the
104
+ block.
105
+ 7. After the staged blocks, silently call PUT /api/context/user/profile
106
+ using the template in "user/profile.md Format" below. Working hours
107
+ and quiet hours are pre-populated with defaults (Weekdays 09:00–18:00,
108
+ Quiet hours 22:00–08:00) do not ask about them. Fill Platforms
109
+ from the **derived Source-of-Truth table** (Step 0) the row values
110
+ you confirmed with the user. Leave Identity blank setup does not
111
+ collect name or timezone.
112
+ 8. If the conversation surfaced detail-heavy facts that belong in the
113
+ detailed profile layer, also PATCH the matching user/*.md file (still
114
+ the same turn, after step 7). Typical Q&A → file mappings:
103
115
  - Named colleagues, family, friends → user/people.md
104
116
  - Current company, role specifics, ongoing projects → user/work.md
105
117
  - Specific frameworks, years of experience → user/expertise.md
@@ -109,7 +121,7 @@ with a curl.
109
121
  Only seed what the user actually stated — do not invent or infer. If
110
122
  nothing came up for a given file, do not touch it. See the user-profile
111
123
  skill for the read-before-write PATCH recipe.
112
- 8. Keep the updates in steps 67 silent — the dashboard handles saving
124
+ 9. Keep steps 78 silent — the dashboard handles saving
113
125
  rules/management.md separately when the user approves the preview. Do
114
126
  not create or modify any other context files; the daemon seeds the
115
127
  rest of the primary-vault scaffold automatically.
@@ -218,6 +230,18 @@ write tone/style preferences into user/profile.md.
218
230
 
219
231
  ### rules/management.md Format
220
232
 
233
+ Output language: follow `<output_language_policy>`. rules/management.md
234
+ is Policy B — the section headers (`## Agent Identity`, `## Source of
235
+ Truth`, `## Autonomy Levels`, `## Notification Rules`, `## Schedule`,
236
+ `## Project Management`) are skeleton and stay English; the descriptive
237
+ bullets under `## Autonomy Levels`, `## Notification Rules`, `## Schedule`,
238
+ and `## Project Management` are written in `<settings primary_language>`.
239
+ File-specific carve-outs that also stay English: the `## Agent Identity`
240
+ field labels (`- AI name:`, `- WhatsApp label:`) — the daemon parses and
241
+ rewrites this section; the Source of Truth table column headers and
242
+ Domain-column row labels; and product/brand cells (`Google Calendar`,
243
+ `Obsidian`, `Notion`, `today.md`, `projects/*.md`).
244
+
221
245
  Fill the Source of Truth table from the **derived rows you confirmed
222
246
  with the user in Step 0** (Schedule / Tasks / Notes / Projects).
223
247
  Generate using this format:
@@ -274,5 +298,8 @@ heading onto the end of the previous list item (e.g. `- Label:
274
298
  <value>## Next Section` on one line). Copy user-provided values as-is,
275
299
  then newline, blank line, then the next heading.
276
300
 
277
- **Important**: Outputting a ```management-rules code block triggers the dashboard to display a preview.
278
- The dashboard handles saving when the user approves, so you do NOT need to save via curl.
301
+ **Important**: Do NOT curl-write rules/management.md to disk yourself.
302
+ The dashboard persists it via `POST /setup/save-rules` when the user
303
+ clicks Save & Finish. (The silent curl PUT to /api/context/user/profile
304
+ in step 7 and the user/*.md PATCHes in step 8 ARE required — only the
305
+ rules file is saved by the dashboard.)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aitne-sh/aitne",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Aitne — a local-first, proactive personal AI agent. A long-running TypeScript daemon is the nervous system; Claude Code (or Codex / Gemini CLI) is the brain. All persistent memory lives in local Markdown files.",
5
5
  "keywords": [
6
6
  "ai",
@@ -31,16 +31,39 @@
31
31
  },
32
32
  "files": [
33
33
  "bin",
34
- "scripts",
35
34
  "personal-agent.mjs",
36
35
  "agent-assets",
36
+ "scripts/start.mjs",
37
+ "scripts/run-node.mjs",
38
+ "scripts/browser.mjs",
39
+ "scripts/rm-paths.mjs",
40
+ "scripts/lib/",
41
+ "scripts/commands/*.mjs",
37
42
  "LICENSE",
38
43
  "README.md"
39
44
  ],
45
+ "scripts": {
46
+ "build": "turbo run build",
47
+ "start": "node bin/aitne.mjs start",
48
+ "stop": "node bin/aitne.mjs stop",
49
+ "restart": "node bin/aitne.mjs restart",
50
+ "status": "node bin/aitne.mjs status",
51
+ "logs": "node bin/aitne.mjs logs",
52
+ "dev": "node bin/aitne.mjs dev",
53
+ "doctor": "node bin/aitne.mjs doctor",
54
+ "audit": "node bin/aitne.mjs audit",
55
+ "test": "node scripts/check-redaction-coverage.mjs && vitest run --coverage",
56
+ "test:watch": "vitest",
57
+ "check:redaction": "node scripts/check-redaction-coverage.mjs",
58
+ "lint": "turbo run lint",
59
+ "typecheck:tests": "turbo run typecheck:tests",
60
+ "clean": "turbo run clean && node scripts/rm-paths.mjs node_modules .buildstamp",
61
+ "prepublishOnly": "pnpm run build"
62
+ },
40
63
  "dependencies": {
41
- "@aitne/daemon": "0.1.4",
42
- "@aitne/dashboard": "0.1.4",
43
- "@aitne/shared": "0.1.4"
64
+ "@aitne/daemon": "workspace:*",
65
+ "@aitne/dashboard": "workspace:*",
66
+ "@aitne/shared": "workspace:*"
44
67
  },
45
68
  "devDependencies": {
46
69
  "@typescript-eslint/eslint-plugin": "^8.58.1",
@@ -52,27 +75,28 @@
52
75
  "typescript-eslint": "^8.58.1",
53
76
  "vitest": "^3.2.0"
54
77
  },
78
+ "packageManager": "pnpm@10.12.1",
79
+ "pnpm": {
80
+ "onlyBuiltDependencies": [
81
+ "esbuild",
82
+ "better-sqlite3",
83
+ "ffmpeg-static",
84
+ "onnxruntime-node",
85
+ "protobufjs"
86
+ ],
87
+ "overrides": {
88
+ "dompurify": ">=3.4.0",
89
+ "vite": "7.3.2",
90
+ "@anthropic-ai/sdk": ">=0.81.0",
91
+ "protobufjs": ">=7.5.5",
92
+ "axios": ">=1.16.0",
93
+ "fast-uri": ">=3.1.2"
94
+ }
95
+ },
55
96
  "publishConfig": {
56
97
  "access": "public"
57
98
  },
58
99
  "engines": {
59
100
  "node": ">=22.0.0"
60
- },
61
- "scripts": {
62
- "build": "turbo run build",
63
- "start": "node bin/aitne.mjs start",
64
- "stop": "node bin/aitne.mjs stop",
65
- "restart": "node bin/aitne.mjs restart",
66
- "status": "node bin/aitne.mjs status",
67
- "logs": "node bin/aitne.mjs logs",
68
- "dev": "node bin/aitne.mjs dev",
69
- "doctor": "node bin/aitne.mjs doctor",
70
- "audit": "node bin/aitne.mjs audit",
71
- "test": "node scripts/check-redaction-coverage.mjs && vitest run --coverage",
72
- "test:watch": "vitest",
73
- "check:redaction": "node scripts/check-redaction-coverage.mjs",
74
- "lint": "turbo run lint",
75
- "typecheck:tests": "turbo run typecheck:tests",
76
- "clean": "turbo run clean && node scripts/rm-paths.mjs node_modules .buildstamp"
77
101
  }
78
- }
102
+ }
@@ -1,109 +0,0 @@
1
- #!/usr/bin/env node
2
- // Roadmap §2.5 / §9.2 — redaction static guard.
3
- //
4
- // Fails (exit 1) if any production source file outside the single
5
- // permitted module (`auth-health-monitor.ts`) contains a raw SQL
6
- // assignment to `auth_detail` or `last_error`. The permitted module
7
- // owns the `writeAuthFailureDetail` / `writeAuthOkDetail` helpers,
8
- // which apply `redactSensitiveString` unconditionally — every other
9
- // caller is required to go through them.
10
- //
11
- // Exemptions:
12
- // - test files (*.test.ts) — they seed raw fixtures, not
13
- // production writes
14
- // - `auth-health-monitor.ts` — the helpers themselves
15
- //
16
- // A match prints file:line:matching-line and exits non-zero. The full
17
- // scan runs in well under a second; it is intended to be wired into
18
- // `pnpm test`'s preamble (or a CI step) so that a new offending write
19
- // cannot silently land alongside a green test suite.
20
-
21
- import { execFileSync } from "node:child_process";
22
- import { fileURLToPath } from "node:url";
23
- import { dirname, relative, resolve } from "node:path";
24
- import { existsSync } from "node:fs";
25
-
26
- const here = dirname(fileURLToPath(import.meta.url));
27
- const root = resolve(here, "..");
28
- const searchRoot = resolve(root, "packages/daemon/src");
29
-
30
- if (!existsSync(searchRoot)) {
31
- console.error(`check-redaction-coverage: ${searchRoot} does not exist`);
32
- process.exit(1);
33
- }
34
-
35
- // Pattern matches any assignment target `auth_detail =` or
36
- // `last_error =`, regardless of placeholder style (`?` / `@name` /
37
- // `:name` / `NULL` / literal). The rg -P flag uses PCRE so we can
38
- // write a single union.
39
- const PATTERN = String.raw`(auth_detail|last_error)\s*=`;
40
-
41
- const EXEMPT_PATHS = new Set([
42
- "packages/daemon/src/core/backends/auth-health-monitor.ts",
43
- // `mail_accounts.last_error` is a separate column from the `backends`
44
- // table's `last_error` that the redaction helpers guard. The redaction
45
- // helper enforces scrubbing of model-auth detail strings; mail-poll
46
- // errors are IMAP/Graph status messages that never carry secrets
47
- // (credentials live in the encrypted blob store and are never inlined
48
- // into error text — see mail-poller.ts `handlePollError`).
49
- "packages/daemon/src/services/mail/account-registry.ts",
50
- ]);
51
-
52
- let rgOutput = "";
53
- try {
54
- rgOutput = execFileSync(
55
- "rg",
56
- [
57
- "--no-heading",
58
- "--line-number",
59
- "--with-filename",
60
- "--glob",
61
- "!**/*.test.ts",
62
- "--glob",
63
- "!**/*.d.ts",
64
- "-e",
65
- PATTERN,
66
- searchRoot,
67
- ],
68
- { encoding: "utf8" },
69
- );
70
- } catch (err) {
71
- // rg exits 1 when there are zero matches — that is the success case.
72
- if (err.status === 1 && !err.stdout) {
73
- process.exit(0);
74
- }
75
- // rg exits 2 on actual errors. Surface them.
76
- console.error("check-redaction-coverage: rg failed");
77
- console.error(err.stderr?.toString() ?? err.message);
78
- process.exit(2);
79
- }
80
-
81
- const lines = rgOutput.split("\n").filter((line) => line.length > 0);
82
- const offenders = [];
83
- for (const line of lines) {
84
- // Each rg line is `absolute-path:line:content`. Convert to a repo
85
- // relative path so the exemption check is stable.
86
- const firstColon = line.indexOf(":");
87
- const secondColon = line.indexOf(":", firstColon + 1);
88
- if (firstColon < 0 || secondColon < 0) continue;
89
- const absPath = line.slice(0, firstColon);
90
- const relPath = relative(root, absPath).replace(/\\/g, "/");
91
- if (EXEMPT_PATHS.has(relPath)) continue;
92
- offenders.push(`${relPath}:${line.slice(firstColon + 1)}`);
93
- }
94
-
95
- if (offenders.length > 0) {
96
- console.error(
97
- "check-redaction-coverage: found raw auth_detail / last_error writes outside the helper module.",
98
- );
99
- console.error(
100
- "All writes must go through writeAuthFailureDetail / writeAuthOkDetail in packages/daemon/src/core/backends/auth-health-monitor.ts (roadmap §9.2).",
101
- );
102
- console.error("");
103
- for (const line of offenders) {
104
- console.error(` ${line}`);
105
- }
106
- process.exit(1);
107
- }
108
-
109
- process.exit(0);
File without changes