browser-debug-mcp-bridge 1.10.0 → 1.11.1
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 +267 -195
- package/apps/mcp-server/dist/db/events-repository.js +61 -9
- package/apps/mcp-server/dist/db/events-repository.js.map +1 -1
- package/apps/mcp-server/dist/db/migrations.js +470 -70
- package/apps/mcp-server/dist/db/migrations.js.map +1 -1
- package/apps/mcp-server/dist/db/schema.js +134 -1
- package/apps/mcp-server/dist/db/schema.js.map +1 -1
- package/apps/mcp-server/dist/document-response-rewriter.js +196 -0
- package/apps/mcp-server/dist/document-response-rewriter.js.map +1 -0
- package/apps/mcp-server/dist/json-rewrite.js +189 -0
- package/apps/mcp-server/dist/json-rewrite.js.map +1 -0
- package/apps/mcp-server/dist/main.js +339 -2
- package/apps/mcp-server/dist/main.js.map +1 -1
- package/apps/mcp-server/dist/mcp/server.js +2712 -246
- package/apps/mcp-server/dist/mcp/server.js.map +1 -1
- package/apps/mcp-server/dist/next-asset-mapper.js +701 -0
- package/apps/mcp-server/dist/next-asset-mapper.js.map +1 -0
- package/apps/mcp-server/dist/next-source-override-planner.js +601 -0
- package/apps/mcp-server/dist/next-source-override-planner.js.map +1 -0
- package/apps/mcp-server/dist/override-audit-contract.js +51 -0
- package/apps/mcp-server/dist/override-audit-contract.js.map +1 -0
- package/apps/mcp-server/dist/override-audit.js +740 -0
- package/apps/mcp-server/dist/override-audit.js.map +1 -0
- package/apps/mcp-server/dist/override-capabilities.js +157 -0
- package/apps/mcp-server/dist/override-capabilities.js.map +1 -0
- package/apps/mcp-server/dist/override-observed-assets.js +179 -0
- package/apps/mcp-server/dist/override-observed-assets.js.map +1 -0
- package/apps/mcp-server/dist/override-poc.js +336 -0
- package/apps/mcp-server/dist/override-poc.js.map +1 -0
- package/apps/mcp-server/dist/override-profile-generator.js +403 -0
- package/apps/mcp-server/dist/override-profile-generator.js.map +1 -0
- package/apps/mcp-server/dist/override-response-planner.js +559 -0
- package/apps/mcp-server/dist/override-response-planner.js.map +1 -0
- package/apps/mcp-server/dist/override-rule-types.js +32 -0
- package/apps/mcp-server/dist/override-rule-types.js.map +1 -0
- package/apps/mcp-server/dist/retention.js +4 -3
- package/apps/mcp-server/dist/retention.js.map +1 -1
- package/apps/mcp-server/dist/rsc-flight-patch-safety.js +269 -0
- package/apps/mcp-server/dist/rsc-flight-patch-safety.js.map +1 -0
- package/apps/mcp-server/dist/websocket/messages.js +5 -0
- package/apps/mcp-server/dist/websocket/messages.js.map +1 -1
- package/apps/mcp-server/dist/websocket/websocket-server.js +10 -0
- package/apps/mcp-server/dist/websocket/websocket-server.js.map +1 -1
- package/apps/mcp-server/package.json +1 -0
- package/package.json +12 -1
|
@@ -0,0 +1,740 @@
|
|
|
1
|
+
import { isOverridePocFailureCode, isOverridePlanAuditKind, } from './override-audit-contract.js';
|
|
2
|
+
const DEBUGGER_SETUP_FAILURE_CODES = new Set([
|
|
3
|
+
'DEBUGGER_ATTACH_FAILED',
|
|
4
|
+
'DEBUGGER_SETUP_FAILED',
|
|
5
|
+
'NETWORK_ENABLE_FAILED',
|
|
6
|
+
'FETCH_ENABLE_FAILED',
|
|
7
|
+
'CACHE_DISABLE_FAILED',
|
|
8
|
+
'SERVICE_WORKER_BYPASS_FAILED',
|
|
9
|
+
'BROWSER_CACHE_CLEAR_FAILED',
|
|
10
|
+
'TAB_RELOAD_FAILED',
|
|
11
|
+
]);
|
|
12
|
+
const RSC_FAILURE_CODES = new Set([
|
|
13
|
+
'RESPONSE_BODY_READ_FAILED',
|
|
14
|
+
'RSC_PATCH_UNSUPPORTED',
|
|
15
|
+
'RSC_CONTENT_TYPE_MISMATCH',
|
|
16
|
+
'RSC_FLIGHT_UNSUPPORTED_RECORD',
|
|
17
|
+
'RSC_FLIGHT_STRUCTURAL_DRIFT',
|
|
18
|
+
'RSC_PATCH_ANCHOR_MISMATCH',
|
|
19
|
+
'RSC_PATCH_UNSAFE',
|
|
20
|
+
]);
|
|
21
|
+
function parseJsonValue(value) {
|
|
22
|
+
if (!value) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
return JSON.parse(value);
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
function parseJsonStringArray(value) {
|
|
33
|
+
const parsed = parseJsonValue(value);
|
|
34
|
+
return Array.isArray(parsed)
|
|
35
|
+
? parsed.filter((entry) => typeof entry === 'string')
|
|
36
|
+
: [];
|
|
37
|
+
}
|
|
38
|
+
function mapRunRow(row) {
|
|
39
|
+
return {
|
|
40
|
+
runId: row.run_id,
|
|
41
|
+
sessionId: row.session_id,
|
|
42
|
+
startedAt: row.started_at,
|
|
43
|
+
endedAt: row.ended_at,
|
|
44
|
+
runStatus: row.run_status,
|
|
45
|
+
tabId: row.tab_id,
|
|
46
|
+
selectedTabId: row.selected_tab_id,
|
|
47
|
+
targetAssetUrl: row.target_asset_url,
|
|
48
|
+
localFilePath: row.local_file_path,
|
|
49
|
+
resolvedLocalFilePath: row.resolved_local_file_path,
|
|
50
|
+
contentType: row.content_type,
|
|
51
|
+
autoReload: row.auto_reload === 1,
|
|
52
|
+
configPath: row.config_path,
|
|
53
|
+
fileExists: row.file_exists === 1,
|
|
54
|
+
fileSizeBytes: row.file_size_bytes,
|
|
55
|
+
matchedRequests: row.matched_requests,
|
|
56
|
+
fulfilledRequests: row.fulfilled_requests,
|
|
57
|
+
lastMatchedAt: row.last_matched_at,
|
|
58
|
+
lastFulfilledAt: row.last_fulfilled_at,
|
|
59
|
+
lastErrorCode: isOverridePocFailureCode(row.last_error_code) ? row.last_error_code : null,
|
|
60
|
+
lastErrorMessage: row.last_error_message,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function mapPlanAuditRow(row) {
|
|
64
|
+
const plannerKind = isOverridePlanAuditKind(row.planner_kind)
|
|
65
|
+
? row.planner_kind
|
|
66
|
+
: 'response-patch';
|
|
67
|
+
return {
|
|
68
|
+
planId: row.plan_id,
|
|
69
|
+
sessionId: row.session_id,
|
|
70
|
+
createdAt: row.created_at,
|
|
71
|
+
plannerKind,
|
|
72
|
+
toolName: row.tool_name,
|
|
73
|
+
profileId: row.profile_id,
|
|
74
|
+
ruleId: row.rule_id,
|
|
75
|
+
ruleType: row.rule_type,
|
|
76
|
+
requestMethod: row.request_method,
|
|
77
|
+
matchMode: row.match_mode,
|
|
78
|
+
targetAssetUrl: row.target_asset_url,
|
|
79
|
+
localFilePath: row.local_file_path,
|
|
80
|
+
configPath: row.config_path,
|
|
81
|
+
contentType: row.content_type,
|
|
82
|
+
originalSha256: row.original_sha256,
|
|
83
|
+
patchedSha256: row.patched_sha256,
|
|
84
|
+
originalBytes: row.original_bytes,
|
|
85
|
+
patchedBytes: row.patched_bytes,
|
|
86
|
+
patchSummary: parseJsonValue(row.patch_summary_json),
|
|
87
|
+
preview: parseJsonValue(row.preview_json),
|
|
88
|
+
warnings: parseJsonStringArray(row.warnings_json),
|
|
89
|
+
blockers: parseJsonStringArray(row.blockers_json),
|
|
90
|
+
capturedFromLiveSession: parseJsonValue(row.captured_from_live_session_json),
|
|
91
|
+
rollback: parseJsonValue(row.rollback_json),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function mapRequestRow(row) {
|
|
95
|
+
return {
|
|
96
|
+
requestLogId: row.request_log_id,
|
|
97
|
+
runId: row.run_id,
|
|
98
|
+
sessionId: row.session_id,
|
|
99
|
+
requestId: row.request_id,
|
|
100
|
+
timestamp: row.ts,
|
|
101
|
+
requestUrl: row.request_url,
|
|
102
|
+
status: row.request_status,
|
|
103
|
+
failureCode: isOverridePocFailureCode(row.failure_code) ? row.failure_code : null,
|
|
104
|
+
errorMessage: row.error_message,
|
|
105
|
+
responseCode: row.response_code,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
function parseCspMetaTags(value) {
|
|
109
|
+
if (!value) {
|
|
110
|
+
return [];
|
|
111
|
+
}
|
|
112
|
+
try {
|
|
113
|
+
const parsed = JSON.parse(value);
|
|
114
|
+
return Array.isArray(parsed)
|
|
115
|
+
? parsed.filter((entry) => typeof entry === 'string' && entry.trim().length > 0)
|
|
116
|
+
: [];
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
function getObservedAssetDiagnostics(db, sessionId, targetAssetUrl) {
|
|
123
|
+
const rows = db.prepare(`
|
|
124
|
+
SELECT asset_url, last_seen_at, integrity, service_worker_controlled, csp_meta_json
|
|
125
|
+
FROM override_observed_assets
|
|
126
|
+
WHERE session_id = ?
|
|
127
|
+
ORDER BY last_seen_at DESC
|
|
128
|
+
LIMIT 500
|
|
129
|
+
`).all(sessionId);
|
|
130
|
+
const target = rows.find((row) => row.asset_url === targetAssetUrl);
|
|
131
|
+
const cspMetaTags = new Set();
|
|
132
|
+
for (const row of rows) {
|
|
133
|
+
for (const tag of parseCspMetaTags(row.csp_meta_json)) {
|
|
134
|
+
cspMetaTags.add(tag);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return {
|
|
138
|
+
observedAssetCount: rows.length,
|
|
139
|
+
targetAssetObserved: target !== undefined,
|
|
140
|
+
targetAssetIntegrity: target?.integrity ?? null,
|
|
141
|
+
latestObservedAt: rows.reduce((latest, row) => {
|
|
142
|
+
return latest === null ? row.last_seen_at : Math.max(latest, row.last_seen_at);
|
|
143
|
+
}, null),
|
|
144
|
+
serviceWorkerControlled: rows.some((row) => row.service_worker_controlled === 1),
|
|
145
|
+
cspMetaTagCount: cspMetaTags.size,
|
|
146
|
+
sriAssetCount: rows.filter((row) => typeof row.integrity === 'string' && row.integrity.trim().length > 0).length,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
export function upsertOverridePocRun(db, record) {
|
|
150
|
+
const now = Date.now();
|
|
151
|
+
db.prepare(`
|
|
152
|
+
INSERT INTO override_runs (
|
|
153
|
+
run_id,
|
|
154
|
+
session_id,
|
|
155
|
+
started_at,
|
|
156
|
+
ended_at,
|
|
157
|
+
run_status,
|
|
158
|
+
tab_id,
|
|
159
|
+
selected_tab_id,
|
|
160
|
+
target_asset_url,
|
|
161
|
+
local_file_path,
|
|
162
|
+
resolved_local_file_path,
|
|
163
|
+
content_type,
|
|
164
|
+
auto_reload,
|
|
165
|
+
config_path,
|
|
166
|
+
file_exists,
|
|
167
|
+
file_size_bytes,
|
|
168
|
+
matched_requests,
|
|
169
|
+
fulfilled_requests,
|
|
170
|
+
last_matched_at,
|
|
171
|
+
last_fulfilled_at,
|
|
172
|
+
last_error_code,
|
|
173
|
+
last_error_message,
|
|
174
|
+
created_at,
|
|
175
|
+
updated_at
|
|
176
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
177
|
+
ON CONFLICT(run_id) DO UPDATE SET
|
|
178
|
+
ended_at = excluded.ended_at,
|
|
179
|
+
run_status = excluded.run_status,
|
|
180
|
+
tab_id = excluded.tab_id,
|
|
181
|
+
selected_tab_id = excluded.selected_tab_id,
|
|
182
|
+
target_asset_url = excluded.target_asset_url,
|
|
183
|
+
local_file_path = excluded.local_file_path,
|
|
184
|
+
resolved_local_file_path = excluded.resolved_local_file_path,
|
|
185
|
+
content_type = excluded.content_type,
|
|
186
|
+
auto_reload = excluded.auto_reload,
|
|
187
|
+
config_path = excluded.config_path,
|
|
188
|
+
file_exists = excluded.file_exists,
|
|
189
|
+
file_size_bytes = excluded.file_size_bytes,
|
|
190
|
+
matched_requests = excluded.matched_requests,
|
|
191
|
+
fulfilled_requests = excluded.fulfilled_requests,
|
|
192
|
+
last_matched_at = excluded.last_matched_at,
|
|
193
|
+
last_fulfilled_at = excluded.last_fulfilled_at,
|
|
194
|
+
last_error_code = excluded.last_error_code,
|
|
195
|
+
last_error_message = excluded.last_error_message,
|
|
196
|
+
updated_at = excluded.updated_at
|
|
197
|
+
`).run(record.runId, record.sessionId, record.startedAt, record.endedAt ?? null, record.runStatus, record.tabId, record.selectedTabId ?? null, record.targetAssetUrl, record.localFilePath, record.resolvedLocalFilePath, record.contentType, record.autoReload ? 1 : 0, record.configPath, record.fileExists ? 1 : 0, record.fileSizeBytes ?? null, record.matchedRequests, record.fulfilledRequests, record.lastMatchedAt ?? null, record.lastFulfilledAt ?? null, record.lastErrorCode ?? null, record.lastErrorMessage ?? null, now, now);
|
|
198
|
+
return record;
|
|
199
|
+
}
|
|
200
|
+
export function upsertOverridePocRequest(db, record) {
|
|
201
|
+
const now = Date.now();
|
|
202
|
+
db.prepare(`
|
|
203
|
+
INSERT INTO override_requests (
|
|
204
|
+
request_log_id,
|
|
205
|
+
run_id,
|
|
206
|
+
session_id,
|
|
207
|
+
request_id,
|
|
208
|
+
ts,
|
|
209
|
+
request_url,
|
|
210
|
+
request_status,
|
|
211
|
+
failure_code,
|
|
212
|
+
error_message,
|
|
213
|
+
response_code,
|
|
214
|
+
created_at,
|
|
215
|
+
updated_at
|
|
216
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
217
|
+
ON CONFLICT(request_log_id) DO UPDATE SET
|
|
218
|
+
request_status = excluded.request_status,
|
|
219
|
+
failure_code = excluded.failure_code,
|
|
220
|
+
error_message = excluded.error_message,
|
|
221
|
+
response_code = excluded.response_code,
|
|
222
|
+
updated_at = excluded.updated_at
|
|
223
|
+
`).run(record.requestLogId, record.runId, record.sessionId, record.requestId, record.timestamp, record.requestUrl, record.status, record.failureCode ?? null, record.errorMessage ?? null, record.responseCode ?? null, now, now);
|
|
224
|
+
return record;
|
|
225
|
+
}
|
|
226
|
+
function encodeJson(value) {
|
|
227
|
+
return JSON.stringify(value ?? null);
|
|
228
|
+
}
|
|
229
|
+
export function insertOverridePlanAudit(db, record) {
|
|
230
|
+
const now = Date.now();
|
|
231
|
+
db.prepare(`
|
|
232
|
+
INSERT INTO override_plan_audits (
|
|
233
|
+
plan_id,
|
|
234
|
+
session_id,
|
|
235
|
+
created_at,
|
|
236
|
+
planner_kind,
|
|
237
|
+
tool_name,
|
|
238
|
+
profile_id,
|
|
239
|
+
rule_id,
|
|
240
|
+
rule_type,
|
|
241
|
+
request_method,
|
|
242
|
+
match_mode,
|
|
243
|
+
target_asset_url,
|
|
244
|
+
local_file_path,
|
|
245
|
+
config_path,
|
|
246
|
+
content_type,
|
|
247
|
+
original_sha256,
|
|
248
|
+
patched_sha256,
|
|
249
|
+
original_bytes,
|
|
250
|
+
patched_bytes,
|
|
251
|
+
patch_summary_json,
|
|
252
|
+
preview_json,
|
|
253
|
+
warnings_json,
|
|
254
|
+
blockers_json,
|
|
255
|
+
captured_from_live_session_json,
|
|
256
|
+
rollback_json,
|
|
257
|
+
updated_at
|
|
258
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
259
|
+
ON CONFLICT(plan_id) DO UPDATE SET
|
|
260
|
+
session_id = excluded.session_id,
|
|
261
|
+
planner_kind = excluded.planner_kind,
|
|
262
|
+
tool_name = excluded.tool_name,
|
|
263
|
+
profile_id = excluded.profile_id,
|
|
264
|
+
rule_id = excluded.rule_id,
|
|
265
|
+
rule_type = excluded.rule_type,
|
|
266
|
+
request_method = excluded.request_method,
|
|
267
|
+
match_mode = excluded.match_mode,
|
|
268
|
+
target_asset_url = excluded.target_asset_url,
|
|
269
|
+
local_file_path = excluded.local_file_path,
|
|
270
|
+
config_path = excluded.config_path,
|
|
271
|
+
content_type = excluded.content_type,
|
|
272
|
+
original_sha256 = excluded.original_sha256,
|
|
273
|
+
patched_sha256 = excluded.patched_sha256,
|
|
274
|
+
original_bytes = excluded.original_bytes,
|
|
275
|
+
patched_bytes = excluded.patched_bytes,
|
|
276
|
+
patch_summary_json = excluded.patch_summary_json,
|
|
277
|
+
preview_json = excluded.preview_json,
|
|
278
|
+
warnings_json = excluded.warnings_json,
|
|
279
|
+
blockers_json = excluded.blockers_json,
|
|
280
|
+
captured_from_live_session_json = excluded.captured_from_live_session_json,
|
|
281
|
+
rollback_json = excluded.rollback_json,
|
|
282
|
+
updated_at = excluded.updated_at
|
|
283
|
+
`).run(record.planId, record.sessionId ?? null, record.createdAt, record.plannerKind, record.toolName, record.profileId ?? null, record.ruleId, record.ruleType, record.requestMethod, record.matchMode, record.targetAssetUrl, record.localFilePath ?? null, record.configPath ?? null, record.contentType, record.originalSha256 ?? null, record.patchedSha256 ?? null, record.originalBytes ?? null, record.patchedBytes ?? null, encodeJson(record.patchSummary), record.preview === undefined || record.preview === null ? null : encodeJson(record.preview), encodeJson(record.warnings), encodeJson(record.blockers), record.capturedFromLiveSession === undefined || record.capturedFromLiveSession === null
|
|
284
|
+
? null
|
|
285
|
+
: encodeJson(record.capturedFromLiveSession), encodeJson(record.rollback), now);
|
|
286
|
+
return record;
|
|
287
|
+
}
|
|
288
|
+
export function listOverridePocRuns(db, sessionId, limit, offset) {
|
|
289
|
+
const rows = db.prepare(`
|
|
290
|
+
SELECT
|
|
291
|
+
run_id,
|
|
292
|
+
session_id,
|
|
293
|
+
started_at,
|
|
294
|
+
ended_at,
|
|
295
|
+
run_status,
|
|
296
|
+
tab_id,
|
|
297
|
+
selected_tab_id,
|
|
298
|
+
target_asset_url,
|
|
299
|
+
local_file_path,
|
|
300
|
+
resolved_local_file_path,
|
|
301
|
+
content_type,
|
|
302
|
+
auto_reload,
|
|
303
|
+
config_path,
|
|
304
|
+
file_exists,
|
|
305
|
+
file_size_bytes,
|
|
306
|
+
matched_requests,
|
|
307
|
+
fulfilled_requests,
|
|
308
|
+
last_matched_at,
|
|
309
|
+
last_fulfilled_at,
|
|
310
|
+
last_error_code,
|
|
311
|
+
last_error_message
|
|
312
|
+
FROM override_runs
|
|
313
|
+
WHERE session_id = ?
|
|
314
|
+
ORDER BY started_at DESC, run_id DESC
|
|
315
|
+
LIMIT ? OFFSET ?
|
|
316
|
+
`).all(sessionId, limit + 1, offset);
|
|
317
|
+
const hasMore = rows.length > limit;
|
|
318
|
+
const page = rows.slice(0, limit).map(mapRunRow);
|
|
319
|
+
return {
|
|
320
|
+
runs: page,
|
|
321
|
+
hasMore,
|
|
322
|
+
nextOffset: hasMore ? offset + limit : null,
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
export function listOverridePlanAudits(db, options) {
|
|
326
|
+
const rows = (options.planId
|
|
327
|
+
? db.prepare(`
|
|
328
|
+
SELECT
|
|
329
|
+
plan_id,
|
|
330
|
+
session_id,
|
|
331
|
+
created_at,
|
|
332
|
+
planner_kind,
|
|
333
|
+
tool_name,
|
|
334
|
+
profile_id,
|
|
335
|
+
rule_id,
|
|
336
|
+
rule_type,
|
|
337
|
+
request_method,
|
|
338
|
+
match_mode,
|
|
339
|
+
target_asset_url,
|
|
340
|
+
local_file_path,
|
|
341
|
+
config_path,
|
|
342
|
+
content_type,
|
|
343
|
+
original_sha256,
|
|
344
|
+
patched_sha256,
|
|
345
|
+
original_bytes,
|
|
346
|
+
patched_bytes,
|
|
347
|
+
patch_summary_json,
|
|
348
|
+
preview_json,
|
|
349
|
+
warnings_json,
|
|
350
|
+
blockers_json,
|
|
351
|
+
captured_from_live_session_json,
|
|
352
|
+
rollback_json
|
|
353
|
+
FROM override_plan_audits
|
|
354
|
+
WHERE session_id = ? AND plan_id = ?
|
|
355
|
+
ORDER BY created_at DESC, plan_id DESC
|
|
356
|
+
LIMIT ? OFFSET ?
|
|
357
|
+
`).all(options.sessionId, options.planId, options.limit + 1, options.offset)
|
|
358
|
+
: db.prepare(`
|
|
359
|
+
SELECT
|
|
360
|
+
plan_id,
|
|
361
|
+
session_id,
|
|
362
|
+
created_at,
|
|
363
|
+
planner_kind,
|
|
364
|
+
tool_name,
|
|
365
|
+
profile_id,
|
|
366
|
+
rule_id,
|
|
367
|
+
rule_type,
|
|
368
|
+
request_method,
|
|
369
|
+
match_mode,
|
|
370
|
+
target_asset_url,
|
|
371
|
+
local_file_path,
|
|
372
|
+
config_path,
|
|
373
|
+
content_type,
|
|
374
|
+
original_sha256,
|
|
375
|
+
patched_sha256,
|
|
376
|
+
original_bytes,
|
|
377
|
+
patched_bytes,
|
|
378
|
+
patch_summary_json,
|
|
379
|
+
preview_json,
|
|
380
|
+
warnings_json,
|
|
381
|
+
blockers_json,
|
|
382
|
+
captured_from_live_session_json,
|
|
383
|
+
rollback_json
|
|
384
|
+
FROM override_plan_audits
|
|
385
|
+
WHERE session_id = ?
|
|
386
|
+
ORDER BY created_at DESC, plan_id DESC
|
|
387
|
+
LIMIT ? OFFSET ?
|
|
388
|
+
`).all(options.sessionId, options.limit + 1, options.offset));
|
|
389
|
+
const hasMore = rows.length > options.limit;
|
|
390
|
+
const page = rows.slice(0, options.limit).map(mapPlanAuditRow);
|
|
391
|
+
return {
|
|
392
|
+
plans: page,
|
|
393
|
+
hasMore,
|
|
394
|
+
nextOffset: hasMore ? options.offset + options.limit : null,
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
export function listOverridePocRequests(db, sessionId, limit, offset, runId) {
|
|
398
|
+
const rows = (runId
|
|
399
|
+
? db.prepare(`
|
|
400
|
+
SELECT
|
|
401
|
+
request_log_id,
|
|
402
|
+
run_id,
|
|
403
|
+
session_id,
|
|
404
|
+
request_id,
|
|
405
|
+
ts,
|
|
406
|
+
request_url,
|
|
407
|
+
request_status,
|
|
408
|
+
failure_code,
|
|
409
|
+
error_message,
|
|
410
|
+
response_code
|
|
411
|
+
FROM override_requests
|
|
412
|
+
WHERE session_id = ? AND run_id = ?
|
|
413
|
+
ORDER BY ts DESC, request_log_id DESC
|
|
414
|
+
LIMIT ? OFFSET ?
|
|
415
|
+
`).all(sessionId, runId, limit + 1, offset)
|
|
416
|
+
: db.prepare(`
|
|
417
|
+
SELECT
|
|
418
|
+
request_log_id,
|
|
419
|
+
run_id,
|
|
420
|
+
session_id,
|
|
421
|
+
request_id,
|
|
422
|
+
ts,
|
|
423
|
+
request_url,
|
|
424
|
+
request_status,
|
|
425
|
+
failure_code,
|
|
426
|
+
error_message,
|
|
427
|
+
response_code
|
|
428
|
+
FROM override_requests
|
|
429
|
+
WHERE session_id = ?
|
|
430
|
+
ORDER BY ts DESC, request_log_id DESC
|
|
431
|
+
LIMIT ? OFFSET ?
|
|
432
|
+
`).all(sessionId, limit + 1, offset));
|
|
433
|
+
const hasMore = rows.length > limit;
|
|
434
|
+
const page = rows.slice(0, limit).map(mapRequestRow);
|
|
435
|
+
return {
|
|
436
|
+
requests: page,
|
|
437
|
+
hasMore,
|
|
438
|
+
nextOffset: hasMore ? offset + limit : null,
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
export function diagnoseOverridePoc(db, sessionId, runId) {
|
|
442
|
+
const runRow = (runId
|
|
443
|
+
? db.prepare(`
|
|
444
|
+
SELECT
|
|
445
|
+
run_id,
|
|
446
|
+
session_id,
|
|
447
|
+
started_at,
|
|
448
|
+
ended_at,
|
|
449
|
+
run_status,
|
|
450
|
+
tab_id,
|
|
451
|
+
selected_tab_id,
|
|
452
|
+
target_asset_url,
|
|
453
|
+
local_file_path,
|
|
454
|
+
resolved_local_file_path,
|
|
455
|
+
content_type,
|
|
456
|
+
auto_reload,
|
|
457
|
+
config_path,
|
|
458
|
+
file_exists,
|
|
459
|
+
file_size_bytes,
|
|
460
|
+
matched_requests,
|
|
461
|
+
fulfilled_requests,
|
|
462
|
+
last_matched_at,
|
|
463
|
+
last_fulfilled_at,
|
|
464
|
+
last_error_code,
|
|
465
|
+
last_error_message
|
|
466
|
+
FROM override_runs
|
|
467
|
+
WHERE session_id = ? AND run_id = ?
|
|
468
|
+
LIMIT 1
|
|
469
|
+
`).get(sessionId, runId)
|
|
470
|
+
: db.prepare(`
|
|
471
|
+
SELECT
|
|
472
|
+
run_id,
|
|
473
|
+
session_id,
|
|
474
|
+
started_at,
|
|
475
|
+
ended_at,
|
|
476
|
+
run_status,
|
|
477
|
+
tab_id,
|
|
478
|
+
selected_tab_id,
|
|
479
|
+
target_asset_url,
|
|
480
|
+
local_file_path,
|
|
481
|
+
resolved_local_file_path,
|
|
482
|
+
content_type,
|
|
483
|
+
auto_reload,
|
|
484
|
+
config_path,
|
|
485
|
+
file_exists,
|
|
486
|
+
file_size_bytes,
|
|
487
|
+
matched_requests,
|
|
488
|
+
fulfilled_requests,
|
|
489
|
+
last_matched_at,
|
|
490
|
+
last_fulfilled_at,
|
|
491
|
+
last_error_code,
|
|
492
|
+
last_error_message
|
|
493
|
+
FROM override_runs
|
|
494
|
+
WHERE session_id = ?
|
|
495
|
+
ORDER BY started_at DESC, run_id DESC
|
|
496
|
+
LIMIT 1
|
|
497
|
+
`).get(sessionId));
|
|
498
|
+
if (!runRow) {
|
|
499
|
+
return {
|
|
500
|
+
sessionId,
|
|
501
|
+
runId: null,
|
|
502
|
+
summary: null,
|
|
503
|
+
indicators: {
|
|
504
|
+
exactUrlMismatch: 'possible',
|
|
505
|
+
cacheOrNoReload: 'possible',
|
|
506
|
+
serviceWorkerInterference: 'possible',
|
|
507
|
+
sriOrCspInterference: 'possible',
|
|
508
|
+
tabSelectionIssue: 'possible',
|
|
509
|
+
debuggerLifecycleIssue: 'possible',
|
|
510
|
+
},
|
|
511
|
+
observedAssets: null,
|
|
512
|
+
issues: [{
|
|
513
|
+
code: 'NO_OVERRIDE_RUNS',
|
|
514
|
+
severity: 'warning',
|
|
515
|
+
message: 'No override audit runs were recorded for this session.',
|
|
516
|
+
suggestedActions: [
|
|
517
|
+
'Enable the override once for the session before requesting a diagnosis.',
|
|
518
|
+
'Confirm the extension is pointed at the same local server base URL as the MCP server.',
|
|
519
|
+
],
|
|
520
|
+
}],
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
const run = mapRunRow(runRow);
|
|
524
|
+
const requestFailureRows = db.prepare(`
|
|
525
|
+
SELECT failure_code
|
|
526
|
+
FROM override_requests
|
|
527
|
+
WHERE session_id = ? AND run_id = ? AND request_status = 'failed'
|
|
528
|
+
`).all(sessionId, run.runId);
|
|
529
|
+
const requestFailureCount = requestFailureRows.length;
|
|
530
|
+
const requestFailureCodes = new Set(requestFailureRows
|
|
531
|
+
.map((row) => row.failure_code)
|
|
532
|
+
.filter((value) => isOverridePocFailureCode(value)));
|
|
533
|
+
const issues = [];
|
|
534
|
+
const observedAssets = getObservedAssetDiagnostics(db, sessionId, run.targetAssetUrl);
|
|
535
|
+
let exactUrlMismatch = 'unlikely';
|
|
536
|
+
let cacheOrNoReload = 'unlikely';
|
|
537
|
+
let serviceWorkerInterference = 'unlikely';
|
|
538
|
+
let sriOrCspInterference = 'unlikely';
|
|
539
|
+
let tabSelectionIssue = 'unlikely';
|
|
540
|
+
let debuggerLifecycleIssue = 'unlikely';
|
|
541
|
+
if (run.lastErrorCode === 'CONFIG_DISABLED') {
|
|
542
|
+
issues.push({
|
|
543
|
+
code: 'CONFIG_DISABLED',
|
|
544
|
+
severity: 'error',
|
|
545
|
+
message: 'The override config is disabled, so no replacement can occur.',
|
|
546
|
+
suggestedActions: [
|
|
547
|
+
'Set `enabled` to `true` in the selected override config file.',
|
|
548
|
+
'Refresh override status before enabling again.',
|
|
549
|
+
],
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
if (run.lastErrorCode === 'LOCAL_FILE_MISSING') {
|
|
553
|
+
issues.push({
|
|
554
|
+
code: 'LOCAL_FILE_MISSING',
|
|
555
|
+
severity: 'error',
|
|
556
|
+
message: 'The configured local override file was missing when the run started.',
|
|
557
|
+
suggestedActions: [
|
|
558
|
+
'Fix the local file path in the override config.',
|
|
559
|
+
'Verify the file exists on disk for the machine running the extension.',
|
|
560
|
+
],
|
|
561
|
+
});
|
|
562
|
+
}
|
|
563
|
+
if (run.lastErrorCode && DEBUGGER_SETUP_FAILURE_CODES.has(run.lastErrorCode)) {
|
|
564
|
+
debuggerLifecycleIssue = 'observed';
|
|
565
|
+
if (run.lastErrorCode === 'TAB_RELOAD_FAILED') {
|
|
566
|
+
cacheOrNoReload = 'observed';
|
|
567
|
+
}
|
|
568
|
+
if (run.lastErrorCode === 'SERVICE_WORKER_BYPASS_FAILED') {
|
|
569
|
+
serviceWorkerInterference = 'observed';
|
|
570
|
+
}
|
|
571
|
+
issues.push({
|
|
572
|
+
code: run.lastErrorCode,
|
|
573
|
+
severity: 'error',
|
|
574
|
+
message: 'Chrome debugger attach/setup failed before the override was fully armed.',
|
|
575
|
+
suggestedActions: [
|
|
576
|
+
'Retry after closing any other debugger attached to the same tab.',
|
|
577
|
+
'Confirm the selected tab is still open, bound to the active session, and can be reloaded.',
|
|
578
|
+
'If this is repeatable, inspect the precise failure code to see which CDP setup command failed.',
|
|
579
|
+
],
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
if (run.lastErrorCode === 'DEBUGGER_DETACHED') {
|
|
583
|
+
debuggerLifecycleIssue = 'observed';
|
|
584
|
+
issues.push({
|
|
585
|
+
code: 'DEBUGGER_DETACHED',
|
|
586
|
+
severity: 'error',
|
|
587
|
+
message: 'The debugger detached unexpectedly while the override run was active.',
|
|
588
|
+
suggestedActions: [
|
|
589
|
+
'Retry the run and watch for tab reloads or extension restarts.',
|
|
590
|
+
'Check whether another debugger client is stealing the tab attachment.',
|
|
591
|
+
],
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
if (observedAssets.observedAssetCount > 0 && !observedAssets.targetAssetObserved) {
|
|
595
|
+
exactUrlMismatch = 'observed';
|
|
596
|
+
issues.push({
|
|
597
|
+
code: 'TARGET_ASSET_NOT_OBSERVED',
|
|
598
|
+
severity: 'warning',
|
|
599
|
+
message: 'The configured target asset URL was not present in the persisted script/style observations for this session.',
|
|
600
|
+
suggestedActions: [
|
|
601
|
+
'Run `observe_override_assets` on the target route and compare the observed URLs with the override rule.',
|
|
602
|
+
'Regenerate or update the profile from observed assets before enabling the override.',
|
|
603
|
+
],
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
if (observedAssets.targetAssetIntegrity) {
|
|
607
|
+
sriOrCspInterference = 'observed';
|
|
608
|
+
issues.push({
|
|
609
|
+
code: 'TARGET_ASSET_SRI_PRESENT',
|
|
610
|
+
severity: 'error',
|
|
611
|
+
message: 'The observed target asset has an integrity attribute, so replaced bytes can be rejected by the browser.',
|
|
612
|
+
suggestedActions: [
|
|
613
|
+
'Remove or rewrite the document integrity attribute before relying on this override.',
|
|
614
|
+
'Use a target without SRI or keep this override blocked until SRI mitigation exists.',
|
|
615
|
+
],
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
else if (observedAssets.sriAssetCount > 0) {
|
|
619
|
+
sriOrCspInterference = 'possible';
|
|
620
|
+
issues.push({
|
|
621
|
+
code: 'OBSERVED_SRI_ASSETS',
|
|
622
|
+
severity: 'info',
|
|
623
|
+
message: 'Some observed assets use integrity attributes; verify the selected override target is not SRI-protected.',
|
|
624
|
+
suggestedActions: ['Inspect `list_observed_override_assets` before selecting a target asset.'],
|
|
625
|
+
});
|
|
626
|
+
}
|
|
627
|
+
if (observedAssets.cspMetaTagCount > 0) {
|
|
628
|
+
sriOrCspInterference = sriOrCspInterference === 'observed' ? 'observed' : 'possible';
|
|
629
|
+
issues.push({
|
|
630
|
+
code: 'CSP_META_PRESENT',
|
|
631
|
+
severity: 'warning',
|
|
632
|
+
message: 'The page had CSP meta tags when assets were observed; strict policies can block replacement behavior.',
|
|
633
|
+
suggestedActions: [
|
|
634
|
+
'Review CSP console errors after enabling the override.',
|
|
635
|
+
'Prefer exact script/style asset replacement over adding new script sources.',
|
|
636
|
+
],
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
if (observedAssets.serviceWorkerControlled) {
|
|
640
|
+
serviceWorkerInterference = 'possible';
|
|
641
|
+
issues.push({
|
|
642
|
+
code: 'SERVICE_WORKER_CONTROLLED_PAGE',
|
|
643
|
+
severity: 'warning',
|
|
644
|
+
message: 'The observed page was controlled by a service worker. Overrides attempt to bypass service workers, but stale registrations can still confuse reload behavior.',
|
|
645
|
+
suggestedActions: [
|
|
646
|
+
'Hard reload after enabling the override.',
|
|
647
|
+
'Unregister the service worker in devtools if requests still do not reach the override path.',
|
|
648
|
+
],
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
if (run.matchedRequests === 0) {
|
|
652
|
+
exactUrlMismatch = 'possible';
|
|
653
|
+
cacheOrNoReload = 'possible';
|
|
654
|
+
serviceWorkerInterference = 'possible';
|
|
655
|
+
tabSelectionIssue = 'possible';
|
|
656
|
+
issues.push({
|
|
657
|
+
code: 'NO_REQUEST_MATCHED',
|
|
658
|
+
severity: 'warning',
|
|
659
|
+
message: 'The run never saw a request for the configured target asset URL.',
|
|
660
|
+
suggestedActions: [
|
|
661
|
+
'Verify the configured URL exactly matches the asset requested by the live page.',
|
|
662
|
+
'Confirm the selected tab is the one loading the target asset.',
|
|
663
|
+
'Force a hard reload if the page may already have the asset cached or prefetched.',
|
|
664
|
+
],
|
|
665
|
+
});
|
|
666
|
+
}
|
|
667
|
+
if (run.matchedRequests > 0 && run.fulfilledRequests === 0) {
|
|
668
|
+
sriOrCspInterference = 'possible';
|
|
669
|
+
issues.push({
|
|
670
|
+
code: 'MATCHED_BUT_NOT_FULFILLED',
|
|
671
|
+
severity: 'warning',
|
|
672
|
+
message: 'The target asset was matched, but no fulfilled response was recorded.',
|
|
673
|
+
suggestedActions: [
|
|
674
|
+
'Inspect failed override request rows for exact failure codes such as `OVERRIDE_ASSET_FETCH_FAILED`, `FULFILL_FAILED`, or `RSC_PATCH_ANCHOR_MISMATCH`.',
|
|
675
|
+
'Check the page for integrity or CSP restrictions if the fulfilled asset still does not execute.',
|
|
676
|
+
],
|
|
677
|
+
});
|
|
678
|
+
}
|
|
679
|
+
if (requestFailureCodes.has('OVERRIDE_ASSET_FETCH_FAILED')) {
|
|
680
|
+
issues.push({
|
|
681
|
+
code: 'OVERRIDE_ASSET_FETCH_FAILED',
|
|
682
|
+
severity: 'error',
|
|
683
|
+
message: 'The extension matched the request but could not fetch local override bytes from the server.',
|
|
684
|
+
suggestedActions: [
|
|
685
|
+
'Confirm the extension server base URL points at the intended local server.',
|
|
686
|
+
'Check the override config and local file path served by `/overrides/poc/asset`.',
|
|
687
|
+
],
|
|
688
|
+
});
|
|
689
|
+
}
|
|
690
|
+
if (requestFailureCodes.has('FULFILL_FAILED')) {
|
|
691
|
+
sriOrCspInterference = 'possible';
|
|
692
|
+
issues.push({
|
|
693
|
+
code: 'FULFILL_FAILED',
|
|
694
|
+
severity: 'error',
|
|
695
|
+
message: 'Chrome accepted the match but the request fulfill step failed.',
|
|
696
|
+
suggestedActions: [
|
|
697
|
+
'Review the failed request rows and browser console for blocking errors.',
|
|
698
|
+
'Check for CSP, integrity, or page-specific script loading constraints.',
|
|
699
|
+
],
|
|
700
|
+
});
|
|
701
|
+
}
|
|
702
|
+
for (const failureCode of RSC_FAILURE_CODES) {
|
|
703
|
+
if (!requestFailureCodes.has(failureCode)) {
|
|
704
|
+
continue;
|
|
705
|
+
}
|
|
706
|
+
issues.push({
|
|
707
|
+
code: failureCode,
|
|
708
|
+
severity: failureCode === 'RSC_PATCH_UNSUPPORTED' ? 'warning' : 'error',
|
|
709
|
+
message: 'A Next.js RSC override request matched, but the live Flight response could not be safely patched.',
|
|
710
|
+
suggestedActions: [
|
|
711
|
+
'Regenerate the source override plan from the current production route and rebuild the local Next.js app.',
|
|
712
|
+
'Verify the captured RSC headers, content type, original hash, and literal patch anchors still match the live response.',
|
|
713
|
+
'Keep the override blocked if the patch would mutate Flight protocol structure instead of literal rendered text.',
|
|
714
|
+
],
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
return {
|
|
718
|
+
sessionId,
|
|
719
|
+
runId: run.runId,
|
|
720
|
+
summary: {
|
|
721
|
+
runStatus: run.runStatus,
|
|
722
|
+
matchedRequests: run.matchedRequests,
|
|
723
|
+
fulfilledRequests: run.fulfilledRequests,
|
|
724
|
+
requestFailureCount,
|
|
725
|
+
lastErrorCode: run.lastErrorCode ?? null,
|
|
726
|
+
lastErrorMessage: run.lastErrorMessage ?? null,
|
|
727
|
+
},
|
|
728
|
+
indicators: {
|
|
729
|
+
exactUrlMismatch,
|
|
730
|
+
cacheOrNoReload,
|
|
731
|
+
serviceWorkerInterference,
|
|
732
|
+
sriOrCspInterference,
|
|
733
|
+
tabSelectionIssue,
|
|
734
|
+
debuggerLifecycleIssue,
|
|
735
|
+
},
|
|
736
|
+
observedAssets,
|
|
737
|
+
issues,
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
//# sourceMappingURL=override-audit.js.map
|