@brainmcp/brainmcp 0.1.11 → 0.1.13
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/README.md +72 -5
- package/dist/agent-config.js +229 -32
- package/dist/commands/auth.d.ts +47 -0
- package/dist/commands/auth.js +390 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/capture.d.ts +34 -0
- package/dist/commands/capture.js +349 -0
- package/dist/commands/capture.js.map +1 -0
- package/dist/commands/dispatch.d.ts +43 -0
- package/dist/commands/dispatch.js +287 -0
- package/dist/commands/dispatch.js.map +1 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/index.js +180 -36
- package/dist/commands/index.js.map +1 -1
- package/dist/hooks/adapters.d.ts +14 -0
- package/dist/hooks/adapters.js +41 -0
- package/dist/hooks/adapters.js.map +1 -0
- package/dist/hooks/claude.d.ts +24 -0
- package/dist/hooks/claude.js +36 -0
- package/dist/hooks/claude.js.map +1 -0
- package/dist/hooks/codex.d.ts +18 -0
- package/dist/hooks/codex.js +36 -0
- package/dist/hooks/codex.js.map +1 -0
- package/dist/hooks/cursor.d.ts +22 -0
- package/dist/hooks/cursor.js +26 -0
- package/dist/hooks/cursor.js.map +1 -0
- package/dist/hooks/managed-hooks.d.ts +30 -0
- package/dist/hooks/managed-hooks.js +189 -0
- package/dist/hooks/managed-hooks.js.map +1 -0
- package/dist/hooks/vscode.d.ts +22 -0
- package/dist/hooks/vscode.js +27 -0
- package/dist/hooks/vscode.js.map +1 -0
- package/dist/index.js +7 -102
- package/dist/index.js.map +1 -1
- package/dist/lib/api-client.d.ts +32 -0
- package/dist/lib/api-client.js +203 -0
- package/dist/lib/api-client.js.map +1 -0
- package/dist/lib/callback-server.d.ts +17 -0
- package/dist/lib/callback-server.js +145 -0
- package/dist/lib/callback-server.js.map +1 -0
- package/dist/lib/cli-contracts.d.ts +193 -0
- package/dist/lib/cli-contracts.js +242 -0
- package/dist/lib/cli-contracts.js.map +1 -0
- package/dist/lib/config.d.ts +4 -2
- package/dist/lib/config.js +11 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/credentials.d.ts +89 -0
- package/dist/lib/credentials.js +304 -0
- package/dist/lib/credentials.js.map +1 -0
- package/dist/lib/http.d.ts +3 -0
- package/dist/lib/http.js +50 -0
- package/dist/lib/http.js.map +1 -0
- package/dist/lib/managed.d.ts +8 -0
- package/dist/lib/managed.js +72 -6
- package/dist/lib/managed.js.map +1 -1
- package/dist/lib/oauth-client.d.ts +55 -0
- package/dist/lib/oauth-client.js +154 -0
- package/dist/lib/oauth-client.js.map +1 -0
- package/dist/lib/pkce.d.ts +14 -0
- package/dist/lib/pkce.js +33 -0
- package/dist/lib/pkce.js.map +1 -0
- package/dist/lib/vault.d.ts +26 -0
- package/dist/lib/vault.js +221 -0
- package/dist/lib/vault.js.map +1 -0
- package/dist/runtime/brainmcp-hook-runtime.mjs +3211 -0
- package/dist/runtime/capture.d.ts +83 -0
- package/dist/runtime/capture.js +332 -0
- package/dist/runtime/capture.js.map +1 -0
- package/dist/runtime/diagnostics.d.ts +7 -0
- package/dist/runtime/diagnostics.js +75 -0
- package/dist/runtime/diagnostics.js.map +1 -0
- package/dist/runtime/doctor-checks.d.ts +14 -0
- package/dist/runtime/doctor-checks.js +205 -0
- package/dist/runtime/doctor-checks.js.map +1 -0
- package/dist/runtime/flush.d.ts +30 -0
- package/dist/runtime/flush.js +193 -0
- package/dist/runtime/flush.js.map +1 -0
- package/dist/runtime/hook-dispatcher.d.ts +31 -0
- package/dist/runtime/hook-dispatcher.js +381 -0
- package/dist/runtime/hook-dispatcher.js.map +1 -0
- package/dist/runtime/index.d.ts +12 -0
- package/dist/runtime/index.js +69 -0
- package/dist/runtime/index.js.map +1 -0
- package/dist/runtime/installer.d.ts +26 -0
- package/dist/runtime/installer.js +134 -0
- package/dist/runtime/installer.js.map +1 -0
- package/dist/runtime/lock.d.ts +19 -0
- package/dist/runtime/lock.js +116 -0
- package/dist/runtime/lock.js.map +1 -0
- package/dist/runtime/orient.d.ts +55 -0
- package/dist/runtime/orient.js +255 -0
- package/dist/runtime/orient.js.map +1 -0
- package/dist/runtime/queue.d.ts +7 -0
- package/dist/runtime/queue.js +80 -0
- package/dist/runtime/queue.js.map +1 -0
- package/dist/runtime/retention.d.ts +26 -0
- package/dist/runtime/retention.js +175 -0
- package/dist/runtime/retention.js.map +1 -0
- package/dist/runtime/session.d.ts +68 -0
- package/dist/runtime/session.js +283 -0
- package/dist/runtime/session.js.map +1 -0
- package/dist/runtime/storage.d.ts +32 -0
- package/dist/runtime/storage.js +236 -0
- package/dist/runtime/storage.js.map +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { type CaptureTrigger, type PreferencesRecord } from '../lib/cli-contracts.js';
|
|
2
|
+
/** Match the server digest body cap (`MCP_SESSION_DIGEST_MAX_BYTES`). */
|
|
3
|
+
export declare const MAX_DIGEST_BYTES: number;
|
|
4
|
+
export interface ProjectCaptureRestrictions {
|
|
5
|
+
captureDisabled: boolean;
|
|
6
|
+
promptsDisabled: boolean;
|
|
7
|
+
}
|
|
8
|
+
export declare function isStopEvent(event: string, stopHookActive?: boolean): boolean;
|
|
9
|
+
export declare function captureTriggerForEvent(event: string): CaptureTrigger | null;
|
|
10
|
+
/**
|
|
11
|
+
* Project `.brainmcp.json` may disable or further restrict capture; it can
|
|
12
|
+
* never enable capture or broaden captured fields (AHI-D02).
|
|
13
|
+
*/
|
|
14
|
+
export declare function readProjectCaptureRestrictions(startDir?: string): ProjectCaptureRestrictions;
|
|
15
|
+
export declare function resolveCaptureWorkspaceId(prefs?: PreferencesRecord | null): string | null;
|
|
16
|
+
/** An opaque project boundary for selecting a manual session without storing paths. */
|
|
17
|
+
export declare function captureProjectKey(cwd: string): string;
|
|
18
|
+
export interface DigestPayload {
|
|
19
|
+
clientDigestId: string;
|
|
20
|
+
trigger: CaptureTrigger;
|
|
21
|
+
workspaceId: string;
|
|
22
|
+
summary?: string;
|
|
23
|
+
relativePaths?: string[];
|
|
24
|
+
host?: string;
|
|
25
|
+
model?: string;
|
|
26
|
+
gitMetadata?: GitMetadata;
|
|
27
|
+
prompts?: string[];
|
|
28
|
+
}
|
|
29
|
+
export interface GitMetadata {
|
|
30
|
+
repository?: string;
|
|
31
|
+
branch?: string;
|
|
32
|
+
commit?: string;
|
|
33
|
+
diffStats?: {
|
|
34
|
+
filesChanged: number;
|
|
35
|
+
insertions: number;
|
|
36
|
+
deletions: number;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
type GitRunner = (args: string[]) => string;
|
|
40
|
+
/** Read only bounded Git labels/counts; never persist the repository root or changed paths. */
|
|
41
|
+
export declare function collectGitMetadata(cwd?: string, runner?: GitRunner): GitMetadata | undefined;
|
|
42
|
+
/**
|
|
43
|
+
* Normalizes and filters capture payload fields (AHI-073, AHI-074, AHI-075).
|
|
44
|
+
* Drops absolute/traversal paths, raw file contents, secrets, and prompts (unless opt-in enabled).
|
|
45
|
+
*/
|
|
46
|
+
export declare function buildDigestPayload(input: {
|
|
47
|
+
trigger: CaptureTrigger;
|
|
48
|
+
workspaceId: string;
|
|
49
|
+
summary?: string;
|
|
50
|
+
paths?: string[];
|
|
51
|
+
host?: string;
|
|
52
|
+
model?: string;
|
|
53
|
+
gitMetadata?: GitMetadata;
|
|
54
|
+
prompts?: string[];
|
|
55
|
+
includePrompts?: boolean;
|
|
56
|
+
}): DigestPayload;
|
|
57
|
+
/**
|
|
58
|
+
* Serializes payload to JSON and verifies it fits inside MAX_DIGEST_BYTES.
|
|
59
|
+
*/
|
|
60
|
+
export declare function serializeDigestPayload(payload: DigestPayload): string;
|
|
61
|
+
/**
|
|
62
|
+
* Enables capture for the user (AHI-071, AHI-072).
|
|
63
|
+
*/
|
|
64
|
+
export declare function enableCapture(options?: {
|
|
65
|
+
baseDir?: string;
|
|
66
|
+
includePrompts?: boolean;
|
|
67
|
+
now?: number;
|
|
68
|
+
workspaceId?: string;
|
|
69
|
+
}): PreferencesRecord;
|
|
70
|
+
/**
|
|
71
|
+
* Disables capture and atomically cancels all pending automatic queue items (AHI-071).
|
|
72
|
+
* Re-enabling never resurrects cancelled items.
|
|
73
|
+
*/
|
|
74
|
+
export declare function disableCapture(options?: {
|
|
75
|
+
baseDir?: string;
|
|
76
|
+
now?: number;
|
|
77
|
+
}): PreferencesRecord;
|
|
78
|
+
/**
|
|
79
|
+
* Checks whether an event may be queued or sent (AHI-076).
|
|
80
|
+
* Stop events MUST NEVER queue or send digests.
|
|
81
|
+
*/
|
|
82
|
+
export declare function isCaptureEligible(event: string, prefs?: PreferencesRecord | null, projectPolicy?: ProjectCaptureRestrictions): boolean;
|
|
83
|
+
export {};
|
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { execFileSync } from 'node:child_process';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { PREFERENCES_SCHEMA_VERSION, scrubSecrets, } from '../lib/cli-contracts.js';
|
|
6
|
+
import { readPreferences, writePreferences } from './storage.js';
|
|
7
|
+
import { listQueueItems, updateQueueItem } from './queue.js';
|
|
8
|
+
/** Match the server digest body cap (`MCP_SESSION_DIGEST_MAX_BYTES`). */
|
|
9
|
+
export const MAX_DIGEST_BYTES = 14 * 1024;
|
|
10
|
+
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
11
|
+
const STOP_EVENTS = new Set(['Stop', 'StopNudge', 'stop', 'SessionStop', 'session_stop']);
|
|
12
|
+
const CAPTURE_EVENTS = new Set([
|
|
13
|
+
'PreCompact',
|
|
14
|
+
'preCompact',
|
|
15
|
+
'pre_compact',
|
|
16
|
+
'precompact',
|
|
17
|
+
'SessionEnd',
|
|
18
|
+
'sessionEnd',
|
|
19
|
+
'session_end',
|
|
20
|
+
'manual',
|
|
21
|
+
]);
|
|
22
|
+
export function isStopEvent(event, stopHookActive) {
|
|
23
|
+
return stopHookActive === true || STOP_EVENTS.has(event);
|
|
24
|
+
}
|
|
25
|
+
export function captureTriggerForEvent(event) {
|
|
26
|
+
if (event === 'SessionEnd' || event === 'sessionEnd' || event === 'session_end')
|
|
27
|
+
return 'session_end';
|
|
28
|
+
if (event === 'PreCompact' || event === 'preCompact' || event === 'pre_compact' || event === 'precompact') {
|
|
29
|
+
return 'pre_compact';
|
|
30
|
+
}
|
|
31
|
+
if (event === 'manual')
|
|
32
|
+
return 'manual';
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Project `.brainmcp.json` may disable or further restrict capture; it can
|
|
37
|
+
* never enable capture or broaden captured fields (AHI-D02).
|
|
38
|
+
*/
|
|
39
|
+
export function readProjectCaptureRestrictions(startDir) {
|
|
40
|
+
const result = { captureDisabled: false, promptsDisabled: false };
|
|
41
|
+
if (!startDir)
|
|
42
|
+
return result;
|
|
43
|
+
let dir = path.resolve(startDir);
|
|
44
|
+
for (let i = 0; i < 16; i += 1) {
|
|
45
|
+
const candidate = path.join(dir, '.brainmcp.json');
|
|
46
|
+
if (fs.lstatSync(candidate, { throwIfNoEntry: false })) {
|
|
47
|
+
try {
|
|
48
|
+
const stat = fs.lstatSync(candidate);
|
|
49
|
+
if (stat.isSymbolicLink() || !stat.isFile() || stat.size > 64 * 1024)
|
|
50
|
+
return { captureDisabled: true, promptsDisabled: true };
|
|
51
|
+
const parsed = JSON.parse(fs.readFileSync(candidate, 'utf8'));
|
|
52
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
53
|
+
return { captureDisabled: true, promptsDisabled: true };
|
|
54
|
+
const consent = parsed.consent && typeof parsed.consent === 'object'
|
|
55
|
+
? parsed.consent
|
|
56
|
+
: undefined;
|
|
57
|
+
if (parsed.capture === false ||
|
|
58
|
+
parsed.captureEnabled === false ||
|
|
59
|
+
consent?.captureEnabled === false) {
|
|
60
|
+
result.captureDisabled = true;
|
|
61
|
+
}
|
|
62
|
+
if (parsed.includePrompts === false || consent?.includePrompts === false) {
|
|
63
|
+
result.promptsDisabled = true;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch {
|
|
67
|
+
return { captureDisabled: true, promptsDisabled: true };
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
const parent = path.dirname(dir);
|
|
72
|
+
if (parent === dir)
|
|
73
|
+
break;
|
|
74
|
+
dir = parent;
|
|
75
|
+
}
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
export function resolveCaptureWorkspaceId(prefs) {
|
|
79
|
+
const candidate = prefs?.enabledWorkspaceId?.trim();
|
|
80
|
+
if (candidate && UUID_RE.test(candidate))
|
|
81
|
+
return candidate;
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
/** An opaque project boundary for selecting a manual session without storing paths. */
|
|
85
|
+
export function captureProjectKey(cwd) {
|
|
86
|
+
let dir = fs.realpathSync(path.resolve(cwd));
|
|
87
|
+
const original = dir;
|
|
88
|
+
for (let i = 0; i < 32; i += 1) {
|
|
89
|
+
if (fs.existsSync(path.join(dir, '.git')) || fs.lstatSync(path.join(dir, '.brainmcp.json'), { throwIfNoEntry: false }))
|
|
90
|
+
break;
|
|
91
|
+
const parent = path.dirname(dir);
|
|
92
|
+
if (parent === dir) {
|
|
93
|
+
dir = original;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
dir = parent;
|
|
97
|
+
}
|
|
98
|
+
return crypto.createHash('sha256').update(dir).digest('hex');
|
|
99
|
+
}
|
|
100
|
+
/** Read only bounded Git labels/counts; never persist the repository root or changed paths. */
|
|
101
|
+
export function collectGitMetadata(cwd, runner) {
|
|
102
|
+
if (!cwd)
|
|
103
|
+
return undefined;
|
|
104
|
+
const run = runner ?? ((args) => execFileSync('git', ['-C', cwd, ...args], {
|
|
105
|
+
encoding: 'utf8',
|
|
106
|
+
timeout: 125,
|
|
107
|
+
maxBuffer: 64 * 1024,
|
|
108
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
109
|
+
}).trim());
|
|
110
|
+
const safely = (args) => {
|
|
111
|
+
try {
|
|
112
|
+
return run(args).trim();
|
|
113
|
+
}
|
|
114
|
+
catch {
|
|
115
|
+
return '';
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
const root = safely(['rev-parse', '--show-toplevel']);
|
|
119
|
+
if (!root)
|
|
120
|
+
return undefined;
|
|
121
|
+
const branch = safely(['branch', '--show-current']);
|
|
122
|
+
const commit = safely(['rev-parse', '--short=12', 'HEAD']);
|
|
123
|
+
const numstat = safely(['diff', '--numstat', 'HEAD']);
|
|
124
|
+
const changedFiles = safely(['status', '--porcelain=v1', '--untracked-files=no']);
|
|
125
|
+
let insertions = 0;
|
|
126
|
+
let deletions = 0;
|
|
127
|
+
for (const line of numstat.split('\n')) {
|
|
128
|
+
if (!line)
|
|
129
|
+
continue;
|
|
130
|
+
const [added, removed] = line.split('\t');
|
|
131
|
+
if (/^\d+$/.test(added ?? ''))
|
|
132
|
+
insertions += Number(added);
|
|
133
|
+
if (/^\d+$/.test(removed ?? ''))
|
|
134
|
+
deletions += Number(removed);
|
|
135
|
+
}
|
|
136
|
+
const filesChanged = changedFiles ? changedFiles.split('\n').filter(Boolean).length : 0;
|
|
137
|
+
return {
|
|
138
|
+
repository: path.basename(root),
|
|
139
|
+
branch: branch || undefined,
|
|
140
|
+
commit: commit || undefined,
|
|
141
|
+
diffStats: { filesChanged, insertions, deletions },
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
function truncateUtf8(value, maxBytes) {
|
|
145
|
+
let output = '';
|
|
146
|
+
let bytes = 0;
|
|
147
|
+
for (const character of value) {
|
|
148
|
+
const characterBytes = Buffer.byteLength(character, 'utf8');
|
|
149
|
+
if (bytes + characterBytes > maxBytes)
|
|
150
|
+
break;
|
|
151
|
+
output += character;
|
|
152
|
+
bytes += characterBytes;
|
|
153
|
+
}
|
|
154
|
+
return output;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Normalizes and filters capture payload fields (AHI-073, AHI-074, AHI-075).
|
|
158
|
+
* Drops absolute/traversal paths, raw file contents, secrets, and prompts (unless opt-in enabled).
|
|
159
|
+
*/
|
|
160
|
+
export function buildDigestPayload(input) {
|
|
161
|
+
const clientDigestId = crypto.randomUUID();
|
|
162
|
+
// Filter paths: relative only, no traversal (../), no absolute
|
|
163
|
+
const safePaths = [];
|
|
164
|
+
if (input.paths) {
|
|
165
|
+
for (const p of input.paths) {
|
|
166
|
+
if (typeof p !== 'string')
|
|
167
|
+
continue;
|
|
168
|
+
const normalized = path.normalize(p.trim());
|
|
169
|
+
if (path.isAbsolute(normalized))
|
|
170
|
+
continue;
|
|
171
|
+
if (normalized.startsWith('..') || normalized.includes('../') || normalized.includes('..\\'))
|
|
172
|
+
continue;
|
|
173
|
+
safePaths.push(truncateUtf8(scrubSecrets(normalized), 180));
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Include prompts only if explicitly opted in
|
|
177
|
+
const safePrompts = [];
|
|
178
|
+
if (input.includePrompts && input.prompts) {
|
|
179
|
+
for (const pr of input.prompts) {
|
|
180
|
+
if (typeof pr === 'string' && pr.trim()) {
|
|
181
|
+
safePrompts.push(truncateUtf8(scrubSecrets(pr), 800));
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
const payload = {
|
|
186
|
+
clientDigestId,
|
|
187
|
+
trigger: input.trigger,
|
|
188
|
+
workspaceId: input.workspaceId,
|
|
189
|
+
host: input.host ? truncateUtf8(scrubSecrets(input.host), 80) : undefined,
|
|
190
|
+
model: input.model ? truncateUtf8(scrubSecrets(input.model), 120) : undefined,
|
|
191
|
+
summary: input.summary ? truncateUtf8(scrubSecrets(input.summary), 4_000) : undefined,
|
|
192
|
+
relativePaths: safePaths.slice(0, 50),
|
|
193
|
+
gitMetadata: input.gitMetadata
|
|
194
|
+
? {
|
|
195
|
+
branch: input.gitMetadata.branch
|
|
196
|
+
? truncateUtf8(scrubSecrets(input.gitMetadata.branch), 180)
|
|
197
|
+
: undefined,
|
|
198
|
+
commit: input.gitMetadata.commit
|
|
199
|
+
? truncateUtf8(scrubSecrets(input.gitMetadata.commit), 180)
|
|
200
|
+
: undefined,
|
|
201
|
+
repository: input.gitMetadata.repository
|
|
202
|
+
? truncateUtf8(scrubSecrets(input.gitMetadata.repository), 180)
|
|
203
|
+
: undefined,
|
|
204
|
+
diffStats: input.gitMetadata.diffStats
|
|
205
|
+
? {
|
|
206
|
+
filesChanged: Math.max(0, Math.min(100_000, Math.trunc(input.gitMetadata.diffStats.filesChanged))),
|
|
207
|
+
insertions: Math.max(0, Math.min(10_000_000, Math.trunc(input.gitMetadata.diffStats.insertions))),
|
|
208
|
+
deletions: Math.max(0, Math.min(10_000_000, Math.trunc(input.gitMetadata.diffStats.deletions))),
|
|
209
|
+
}
|
|
210
|
+
: undefined,
|
|
211
|
+
}
|
|
212
|
+
: undefined,
|
|
213
|
+
prompts: safePrompts.length > 0 ? safePrompts.slice(0, 10) : undefined,
|
|
214
|
+
};
|
|
215
|
+
// JSON escaping and multibyte text can expand independently of character counts.
|
|
216
|
+
while (Buffer.byteLength(JSON.stringify(payload), 'utf8') > MAX_DIGEST_BYTES) {
|
|
217
|
+
if (payload.prompts?.length) {
|
|
218
|
+
payload.prompts.pop();
|
|
219
|
+
if (payload.prompts.length === 0)
|
|
220
|
+
payload.prompts = undefined;
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
if (payload.relativePaths?.length) {
|
|
224
|
+
payload.relativePaths.pop();
|
|
225
|
+
if (payload.relativePaths.length === 0)
|
|
226
|
+
payload.relativePaths = undefined;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (payload.summary) {
|
|
230
|
+
payload.summary = truncateUtf8(payload.summary, Math.max(0, Buffer.byteLength(payload.summary, 'utf8') - 512));
|
|
231
|
+
if (!payload.summary)
|
|
232
|
+
payload.summary = undefined;
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
payload.gitMetadata = undefined;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
return payload;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Serializes payload to JSON and verifies it fits inside MAX_DIGEST_BYTES.
|
|
242
|
+
*/
|
|
243
|
+
export function serializeDigestPayload(payload) {
|
|
244
|
+
const json = JSON.stringify(payload);
|
|
245
|
+
const bytes = Buffer.byteLength(json, 'utf8');
|
|
246
|
+
if (bytes > MAX_DIGEST_BYTES) {
|
|
247
|
+
throw new Error(`Digest payload exceeds max byte cap: ${bytes} > ${MAX_DIGEST_BYTES}`);
|
|
248
|
+
}
|
|
249
|
+
return json;
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Enables capture for the user (AHI-071, AHI-072).
|
|
253
|
+
*/
|
|
254
|
+
export function enableCapture(options) {
|
|
255
|
+
const current = readPreferences(options?.baseDir) ?? {
|
|
256
|
+
version: PREFERENCES_SCHEMA_VERSION,
|
|
257
|
+
consent: {
|
|
258
|
+
captureEnabled: false,
|
|
259
|
+
includePrompts: false,
|
|
260
|
+
policyVersion: '2026-09-02.capture-v1',
|
|
261
|
+
revision: 0,
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
const workspaceId = options?.workspaceId?.trim();
|
|
265
|
+
const updated = {
|
|
266
|
+
...current,
|
|
267
|
+
consent: {
|
|
268
|
+
...current.consent,
|
|
269
|
+
captureEnabled: true,
|
|
270
|
+
includePrompts: Boolean(options?.includePrompts),
|
|
271
|
+
consentedAt: new Date(options?.now ?? Date.now()).toISOString(),
|
|
272
|
+
revision: (current.consent.revision ?? 0) + 1,
|
|
273
|
+
},
|
|
274
|
+
enabledWorkspaceId: workspaceId && UUID_RE.test(workspaceId) ? workspaceId : current.enabledWorkspaceId,
|
|
275
|
+
};
|
|
276
|
+
writePreferences(updated, options?.baseDir);
|
|
277
|
+
return updated;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Disables capture and atomically cancels all pending automatic queue items (AHI-071).
|
|
281
|
+
* Re-enabling never resurrects cancelled items.
|
|
282
|
+
*/
|
|
283
|
+
export function disableCapture(options) {
|
|
284
|
+
const current = readPreferences(options?.baseDir) ?? {
|
|
285
|
+
version: PREFERENCES_SCHEMA_VERSION,
|
|
286
|
+
consent: {
|
|
287
|
+
captureEnabled: false,
|
|
288
|
+
includePrompts: false,
|
|
289
|
+
policyVersion: '2026-09-02.capture-v1',
|
|
290
|
+
revision: 0,
|
|
291
|
+
},
|
|
292
|
+
};
|
|
293
|
+
const updated = {
|
|
294
|
+
...current,
|
|
295
|
+
consent: {
|
|
296
|
+
...current.consent,
|
|
297
|
+
captureEnabled: false,
|
|
298
|
+
includePrompts: false,
|
|
299
|
+
revision: (current.consent.revision ?? 0) + 1,
|
|
300
|
+
},
|
|
301
|
+
};
|
|
302
|
+
writePreferences(updated, options?.baseDir);
|
|
303
|
+
// Cancel only unsent automatic items. Manual items remain explicit user
|
|
304
|
+
// actions, and ambiguous in-flight deliveries retain their honest status.
|
|
305
|
+
const items = listQueueItems(options?.baseDir);
|
|
306
|
+
for (const item of items) {
|
|
307
|
+
if (item.captureTrigger !== 'manual' && item.status === 'pending') {
|
|
308
|
+
updateQueueItem(item.digestId, {
|
|
309
|
+
status: 'cancelled',
|
|
310
|
+
lastError: 'Capture disabled by user',
|
|
311
|
+
}, options?.baseDir);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return updated;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Checks whether an event may be queued or sent (AHI-076).
|
|
318
|
+
* Stop events MUST NEVER queue or send digests.
|
|
319
|
+
*/
|
|
320
|
+
export function isCaptureEligible(event, prefs, projectPolicy) {
|
|
321
|
+
if (isStopEvent(event)) {
|
|
322
|
+
return false;
|
|
323
|
+
}
|
|
324
|
+
if (projectPolicy?.captureDisabled) {
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
if (!prefs?.consent?.captureEnabled) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
return CAPTURE_EVENTS.has(event);
|
|
331
|
+
}
|
|
332
|
+
//# sourceMappingURL=capture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/runtime/capture.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EACL,0BAA0B,EAC1B,YAAY,GAGb,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAE7D,yEAAyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,GAAG,IAAI,CAAC;AAE1C,MAAM,OAAO,GAAG,iEAAiE,CAAC;AAClF,MAAM,WAAW,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC,CAAC;AAC1F,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC;IAC7B,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,YAAY;IACZ,YAAY;IACZ,YAAY;IACZ,aAAa;IACb,QAAQ;CACT,CAAC,CAAC;AAOH,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,cAAwB;IACjE,OAAO,cAAc,KAAK,IAAI,IAAI,WAAW,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AAC3D,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,KAAa;IAClD,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,aAAa;QAAE,OAAO,aAAa,CAAC;IACtG,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,aAAa,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;QAC1G,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACxC,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,8BAA8B,CAAC,QAAiB;IAC9D,MAAM,MAAM,GAA+B,EAAE,eAAe,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC;IAC9F,IAAI,CAAC,QAAQ;QAAE,OAAO,MAAM,CAAC;IAE7B,IAAI,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACjC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,CAAC;QACnD,IAAI,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;YACvD,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;gBACrC,IAAI,IAAI,CAAC,cAAc,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,GAAG,EAAE,GAAG,IAAI;oBAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;gBAC9H,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAA4B,CAAC;gBACzF,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;oBAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;gBAC5H,MAAM,OAAO,GACX,MAAM,CAAC,OAAO,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;oBAClD,CAAC,CAAE,MAAM,CAAC,OAAmC;oBAC7C,CAAC,CAAC,SAAS,CAAC;gBAChB,IACE,MAAM,CAAC,OAAO,KAAK,KAAK;oBACxB,MAAM,CAAC,cAAc,KAAK,KAAK;oBAC/B,OAAO,EAAE,cAAc,KAAK,KAAK,EACjC,CAAC;oBACD,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC;gBAChC,CAAC;gBACD,IAAI,MAAM,CAAC,cAAc,KAAK,KAAK,IAAI,OAAO,EAAE,cAAc,KAAK,KAAK,EAAE,CAAC;oBACzE,MAAM,CAAC,eAAe,GAAG,IAAI,CAAC;gBAChC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC;YAC1D,CAAC;YACD,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG;YAAE,MAAM;QAC1B,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,yBAAyB,CAAC,KAAgC;IACxE,MAAM,SAAS,GAAG,KAAK,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACpD,IAAI,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC;QAAE,OAAO,SAAS,CAAC;IAC3D,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,iBAAiB,CAAC,GAAW;IAC3C,IAAI,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,GAAG,CAAC;IACrB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,IAAI,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;YAAE,MAAM;QAC9H,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YAAC,GAAG,GAAG,QAAQ,CAAC;YAAC,MAAM;QAAC,CAAC;QAC9C,GAAG,GAAG,MAAM,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AAC/D,CAAC;AA2BD,+FAA+F;AAC/F,MAAM,UAAU,kBAAkB,CAAC,GAAY,EAAE,MAAkB;IACjE,IAAI,CAAC,GAAG;QAAE,OAAO,SAAS,CAAC;IAC3B,MAAM,GAAG,GAAc,MAAM,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC,EAAE;QACpF,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,GAAG;QACZ,SAAS,EAAE,EAAE,GAAG,IAAI;QACpB,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;KACpC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACX,MAAM,MAAM,GAAG,CAAC,IAAc,EAAE,EAAE;QAChC,IAAI,CAAC;YACH,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAC1B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;IACF,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,iBAAiB,CAAC,CAAC,CAAC;IACtD,IAAI,CAAC,IAAI;QAAE,OAAO,SAAS,CAAC;IAC5B,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAAC,CAAC;IACpD,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,WAAW,EAAE,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;IACtD,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,QAAQ,EAAE,gBAAgB,EAAE,sBAAsB,CAAC,CAAC,CAAC;IAClF,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;QACvC,IAAI,CAAC,IAAI;YAAE,SAAS;QACpB,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,EAAE,CAAC;YAAE,UAAU,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;QAC3D,IAAI,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC;YAAE,SAAS,IAAI,MAAM,CAAC,OAAO,CAAC,CAAC;IAChE,CAAC;IACD,MAAM,YAAY,GAAG,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IACxF,OAAO;QACL,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;QAC/B,MAAM,EAAE,MAAM,IAAI,SAAS;QAC3B,MAAM,EAAE,MAAM,IAAI,SAAS;QAC3B,SAAS,EAAE,EAAE,YAAY,EAAE,UAAU,EAAE,SAAS,EAAE;KACnD,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,KAAa,EAAE,QAAgB;IACnD,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC9B,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;QAC5D,IAAI,KAAK,GAAG,cAAc,GAAG,QAAQ;YAAE,MAAM;QAC7C,MAAM,IAAI,SAAS,CAAC;QACpB,KAAK,IAAI,cAAc,CAAC;IAC1B,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAUlC;IACC,MAAM,cAAc,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;IAE3C,+DAA+D;IAC/D,MAAM,SAAS,GAAa,EAAE,CAAC;IAC/B,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,SAAS;YACpC,MAAM,UAAU,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;YAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC;gBAAE,SAAS;YAC1C,IAAI,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC;gBAAE,SAAS;YACvG,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,8CAA8C;IAC9C,MAAM,WAAW,GAAa,EAAE,CAAC;IACjC,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;QAC1C,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;YAC/B,IAAI,OAAO,EAAE,KAAK,QAAQ,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;gBACxC,WAAW,CAAC,IAAI,CAAC,YAAY,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;YACxD,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,OAAO,GAAkB;QAC7B,cAAc;QACd,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;QACzE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS;QAC7E,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;QACrF,aAAa,EAAE,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;QACrC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC5B,CAAC,CAAC;gBACE,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM;oBAC9B,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC;oBAC3D,CAAC,CAAC,SAAS;gBACb,MAAM,EAAE,KAAK,CAAC,WAAW,CAAC,MAAM;oBAC9B,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC;oBAC3D,CAAC,CAAC,SAAS;gBACb,UAAU,EAAE,KAAK,CAAC,WAAW,CAAC,UAAU;oBACtC,CAAC,CAAC,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,WAAW,CAAC,UAAU,CAAC,EAAE,GAAG,CAAC;oBAC/D,CAAC,CAAC,SAAS;gBACb,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC,SAAS;oBACpC,CAAC,CAAC;wBACE,YAAY,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC;wBAClG,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC;wBACjG,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,WAAW,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,CAAC;qBAChG;oBACH,CAAC,CAAC,SAAS;aACd;YACH,CAAC,CAAC,SAAS;QACb,OAAO,EAAE,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS;KACvE,CAAC;IAEF,iFAAiF;IACjF,OAAO,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,GAAG,gBAAgB,EAAE,CAAC;QAC7E,IAAI,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACtB,IAAI,OAAO,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;YAC9D,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;YAClC,OAAO,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC;YAC5B,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;YAC1E,SAAS;QACX,CAAC;QACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,OAAO,GAAG,YAAY,CAC5B,OAAO,CAAC,OAAO,EACf,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,GAAG,GAAG,CAAC,CAC9D,CAAC;YACF,IAAI,CAAC,OAAO,CAAC,OAAO;gBAAE,OAAO,CAAC,OAAO,GAAG,SAAS,CAAC;YAClD,SAAS;QACX,CAAC;QACD,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;QAChC,MAAM;IACR,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CAAC,OAAsB;IAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAC9C,IAAI,KAAK,GAAG,gBAAgB,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,wCAAwC,KAAK,MAAM,gBAAgB,EAAE,CAAC,CAAC;IACzF,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,OAK7B;IACC,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI;QACnD,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE;YACP,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,uBAAuB;YACtC,QAAQ,EAAE,CAAC;SACZ;KACF,CAAC;IAEF,MAAM,WAAW,GAAG,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IACjD,MAAM,OAAO,GAAsB;QACjC,GAAG,OAAO;QACV,OAAO,EAAE;YACP,GAAG,OAAO,CAAC,OAAO;YAClB,cAAc,EAAE,IAAI;YACpB,cAAc,EAAE,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC;YAChD,WAAW,EAAE,IAAI,IAAI,CAAC,OAAO,EAAE,GAAG,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,WAAW,EAAE;YAC/D,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;SAC9C;QACD,kBAAkB,EAAE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,kBAAkB;KACxG,CAAC;IAEF,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC5C,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,OAA4C;IACzE,MAAM,OAAO,GAAG,eAAe,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI;QACnD,OAAO,EAAE,0BAA0B;QACnC,OAAO,EAAE;YACP,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,aAAa,EAAE,uBAAuB;YACtC,QAAQ,EAAE,CAAC;SACZ;KACF,CAAC;IAEF,MAAM,OAAO,GAAsB;QACjC,GAAG,OAAO;QACV,OAAO,EAAE;YACP,GAAG,OAAO,CAAC,OAAO;YAClB,cAAc,EAAE,KAAK;YACrB,cAAc,EAAE,KAAK;YACrB,QAAQ,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC;SAC9C;KACF,CAAC;IAEF,gBAAgB,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAE5C,wEAAwE;IACxE,0EAA0E;IAC1E,MAAM,KAAK,GAAG,cAAc,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAC/C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,cAAc,KAAK,QAAQ,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAClE,eAAe,CACb,IAAI,CAAC,QAAQ,EACb;gBACE,MAAM,EAAE,WAAW;gBACnB,SAAS,EAAE,0BAA0B;aACtC,EACD,OAAO,EAAE,OAAO,CACjB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,KAAa,EACb,KAAgC,EAChC,aAA0C;IAE1C,IAAI,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,aAAa,EAAE,eAAe,EAAE,CAAC;QACnC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,CAAC;QACpC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { LogEntryRecord } from '../lib/cli-contracts.js';
|
|
2
|
+
/**
|
|
3
|
+
* Recursively scrubs sensitive values from metadata objects (AHI-D04, AHI-D06).
|
|
4
|
+
*/
|
|
5
|
+
export declare function scrubMetadata(value: unknown): any;
|
|
6
|
+
export declare function getLogsDir(baseDir?: string): string;
|
|
7
|
+
export declare function logRuntimeEvent(level: LogEntryRecord['level'], event: string, metadata?: Record<string, unknown>, baseDir?: string): void;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { ensureDirectory0700, assertNotSymlink, resolveStateDir } from './storage.js';
|
|
4
|
+
const SENSITIVE_KEY_PATTERNS = [
|
|
5
|
+
/token/i,
|
|
6
|
+
/bearer/i,
|
|
7
|
+
/auth/i,
|
|
8
|
+
/secret/i,
|
|
9
|
+
/cookie/i,
|
|
10
|
+
/password/i,
|
|
11
|
+
/verifier/i,
|
|
12
|
+
/code/i,
|
|
13
|
+
/prompt/i,
|
|
14
|
+
/assistant/i,
|
|
15
|
+
/content/i,
|
|
16
|
+
/(api|secret|private|access|auth)key$/i,
|
|
17
|
+
/^key$/i,
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Recursively scrubs sensitive values from metadata objects (AHI-D04, AHI-D06).
|
|
21
|
+
*/
|
|
22
|
+
export function scrubMetadata(value) {
|
|
23
|
+
if (value === null || value === undefined) {
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
if (typeof value !== 'object') {
|
|
27
|
+
if (typeof value === 'string') {
|
|
28
|
+
// Scrub potential token patterns
|
|
29
|
+
if (/bearer\s+[a-zA-Z0-9._-]+/i.test(value) || /brain_[a-zA-Z0-9_-]{20,}/i.test(value)) {
|
|
30
|
+
return '[REDACTED_TOKEN]';
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
if (Array.isArray(value)) {
|
|
36
|
+
return value.map(scrubMetadata);
|
|
37
|
+
}
|
|
38
|
+
const result = {};
|
|
39
|
+
for (const [k, v] of Object.entries(value)) {
|
|
40
|
+
if (SENSITIVE_KEY_PATTERNS.some((pattern) => pattern.test(k))) {
|
|
41
|
+
result[k] = '[REDACTED]';
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
result[k] = scrubMetadata(v);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
export function getLogsDir(baseDir) {
|
|
50
|
+
const logsDir = path.join(resolveStateDir(baseDir), 'logs');
|
|
51
|
+
ensureDirectory0700(logsDir);
|
|
52
|
+
return logsDir;
|
|
53
|
+
}
|
|
54
|
+
export function logRuntimeEvent(level, event, metadata, baseDir) {
|
|
55
|
+
try {
|
|
56
|
+
const logsDir = getLogsDir(baseDir);
|
|
57
|
+
const logFile = path.join(logsDir, 'runtime.log');
|
|
58
|
+
assertNotSymlink(logFile);
|
|
59
|
+
if (fs.existsSync(logFile) && fs.lstatSync(logFile).size >= 5 * 1024 * 1024) {
|
|
60
|
+
// Bound diagnostics even when capture is disabled and no ledger is pruned.
|
|
61
|
+
fs.truncateSync(logFile, 0);
|
|
62
|
+
}
|
|
63
|
+
const entry = {
|
|
64
|
+
timestamp: new Date().toISOString(),
|
|
65
|
+
level,
|
|
66
|
+
event,
|
|
67
|
+
metadata: metadata ? scrubMetadata(metadata) : undefined,
|
|
68
|
+
};
|
|
69
|
+
fs.appendFileSync(logFile, JSON.stringify(entry) + '\n', { mode: 0o600 });
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Logging failures fail open
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/runtime/diagnostics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type SecretVault } from '../lib/vault.js';
|
|
2
|
+
export interface DoctorCheckResult {
|
|
3
|
+
name: string;
|
|
4
|
+
passed: boolean;
|
|
5
|
+
detail: string;
|
|
6
|
+
repairGuidance?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function checkCredentialHealth(baseDir?: string, options?: {
|
|
9
|
+
probeVault?: boolean;
|
|
10
|
+
vault?: SecretVault;
|
|
11
|
+
}): Promise<DoctorCheckResult>;
|
|
12
|
+
export declare function checkRuntimeDrift(expectedVersion?: string, baseDir?: string): DoctorCheckResult;
|
|
13
|
+
export declare function checkConsentPreferences(baseDir?: string): DoctorCheckResult;
|
|
14
|
+
export declare function checkQueueStatus(baseDir?: string): DoctorCheckResult;
|