@btraut/browser-bridge 0.7.0 → 0.7.2
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/CHANGELOG.md +13 -0
- package/dist/api.js +77 -9
- package/dist/api.js.map +2 -2
- package/dist/index.js +3 -0
- package/dist/index.js.map +3 -3
- package/extension/manifest.json +1 -1
- package/package.json +1 -1
- package/skills/browser-bridge/skill.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,19 @@ The format is based on "Keep a Changelog", and this project adheres to Semantic
|
|
|
8
8
|
|
|
9
9
|
_TBD_
|
|
10
10
|
|
|
11
|
+
## [0.7.2] - 2026-02-12
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Core debugger bridge now self-heals stale attach state: when a command fails with "Debugger is not attached to the requested tab.", it marks the tab detached, re-attaches once, and retries the command once. It also clears cached attachment state after extension disconnects to avoid false "attached" assumptions that can trigger inspect recovery loops/timeouts.
|
|
16
|
+
|
|
17
|
+
## [0.7.1] - 2026-02-11
|
|
18
|
+
|
|
19
|
+
### Fixed
|
|
20
|
+
|
|
21
|
+
- Diagnostics doctor: stop failing by default when `session_id` is omitted, treat detached debugger as expected idle behavior, and downgrade stale drive/inspect errors to warnings.
|
|
22
|
+
- Core error latching: clear drive/inspect/debugger `last_error` state after successful operations so recovered sessions report healthy diagnostics.
|
|
23
|
+
|
|
11
24
|
## [0.7.0] - 2026-02-10
|
|
12
25
|
|
|
13
26
|
### Fixed
|
package/dist/api.js
CHANGED
|
@@ -848,6 +848,10 @@ var DriveController = class {
|
|
|
848
848
|
this.lastError = error;
|
|
849
849
|
this.lastErrorAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
850
850
|
}
|
|
851
|
+
clearLastError() {
|
|
852
|
+
this.lastError = void 0;
|
|
853
|
+
this.lastErrorAt = void 0;
|
|
854
|
+
}
|
|
851
855
|
async execute(sessionId, action, params, timeoutMs) {
|
|
852
856
|
return await driveMutex.runExclusive(async () => {
|
|
853
857
|
try {
|
|
@@ -888,6 +892,7 @@ var DriveController = class {
|
|
|
888
892
|
timeoutMs
|
|
889
893
|
);
|
|
890
894
|
if (response.status === "ok") {
|
|
895
|
+
this.clearLastError();
|
|
891
896
|
return {
|
|
892
897
|
ok: true,
|
|
893
898
|
result: response.result
|
|
@@ -2800,6 +2805,7 @@ var InspectService = class {
|
|
|
2800
2805
|
});
|
|
2801
2806
|
}
|
|
2802
2807
|
markInspectConnected(sessionId) {
|
|
2808
|
+
this.clearLastError();
|
|
2803
2809
|
try {
|
|
2804
2810
|
const session = this.registry.require(sessionId);
|
|
2805
2811
|
if (session.state === "INIT" /* INIT */ || session.state === "DRIVE_READY" /* DRIVE_READY */) {
|
|
@@ -2814,6 +2820,10 @@ var InspectService = class {
|
|
|
2814
2820
|
this.lastError = error;
|
|
2815
2821
|
this.lastErrorAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2816
2822
|
}
|
|
2823
|
+
clearLastError() {
|
|
2824
|
+
this.lastError = void 0;
|
|
2825
|
+
this.lastErrorAt = void 0;
|
|
2826
|
+
}
|
|
2817
2827
|
buildUnavailableError() {
|
|
2818
2828
|
return new InspectError(
|
|
2819
2829
|
"INSPECT_UNAVAILABLE",
|
|
@@ -3474,10 +3484,20 @@ var registerArtifactsRoutes = (router, options = {}) => {
|
|
|
3474
3484
|
};
|
|
3475
3485
|
|
|
3476
3486
|
// packages/core/src/diagnostics.ts
|
|
3487
|
+
var STALE_ERROR_THRESHOLD_MS = 2 * 60 * 1e3;
|
|
3488
|
+
var getErrorAgeMs = (timestamp) => {
|
|
3489
|
+
const parsed = Date.parse(timestamp);
|
|
3490
|
+
if (!Number.isFinite(parsed)) {
|
|
3491
|
+
return void 0;
|
|
3492
|
+
}
|
|
3493
|
+
return Math.max(0, Date.now() - parsed);
|
|
3494
|
+
};
|
|
3477
3495
|
var buildDiagnosticReport = (sessionId, context = {}) => {
|
|
3478
3496
|
const extensionConnected = context.extension?.connected ?? false;
|
|
3479
3497
|
const debuggerAttached = context.debugger?.attached ?? false;
|
|
3480
3498
|
const sessionState = context.sessionState;
|
|
3499
|
+
const hasSessionId = Boolean(sessionId);
|
|
3500
|
+
const warnings = [];
|
|
3481
3501
|
const checks = [
|
|
3482
3502
|
{
|
|
3483
3503
|
name: "extension.connected",
|
|
@@ -3486,13 +3506,13 @@ var buildDiagnosticReport = (sessionId, context = {}) => {
|
|
|
3486
3506
|
},
|
|
3487
3507
|
{
|
|
3488
3508
|
name: "debugger.attached",
|
|
3489
|
-
ok:
|
|
3490
|
-
message: debuggerAttached ? "Debugger is attached." : "Debugger is not attached."
|
|
3509
|
+
ok: true,
|
|
3510
|
+
message: debuggerAttached ? "Debugger is attached." : "Debugger is not attached (inspect is idle)."
|
|
3491
3511
|
},
|
|
3492
3512
|
{
|
|
3493
3513
|
name: "session.state",
|
|
3494
|
-
ok: Boolean(sessionState),
|
|
3495
|
-
message: sessionState ? `Session state is ${sessionState}.` :
|
|
3514
|
+
ok: hasSessionId ? Boolean(sessionState) : true,
|
|
3515
|
+
message: sessionState ? `Session state is ${sessionState}.` : hasSessionId ? "Session state unavailable." : "Session id not provided.",
|
|
3496
3516
|
details: {
|
|
3497
3517
|
session_id: sessionId || null,
|
|
3498
3518
|
state: sessionState ?? "UNKNOWN"
|
|
@@ -3500,26 +3520,42 @@ var buildDiagnosticReport = (sessionId, context = {}) => {
|
|
|
3500
3520
|
}
|
|
3501
3521
|
];
|
|
3502
3522
|
if (context.driveLastError) {
|
|
3523
|
+
const ageMs = getErrorAgeMs(context.driveLastError.at);
|
|
3524
|
+
const isStale = ageMs !== void 0 && ageMs > STALE_ERROR_THRESHOLD_MS;
|
|
3525
|
+
if (isStale) {
|
|
3526
|
+
warnings.push(
|
|
3527
|
+
`Ignoring stale drive error (${Math.round(ageMs / 1e3)}s old): ${context.driveLastError.message}`
|
|
3528
|
+
);
|
|
3529
|
+
}
|
|
3503
3530
|
checks.push({
|
|
3504
3531
|
name: "drive.last_error",
|
|
3505
|
-
ok:
|
|
3532
|
+
ok: isStale,
|
|
3506
3533
|
message: context.driveLastError.message,
|
|
3507
3534
|
details: {
|
|
3508
3535
|
code: context.driveLastError.code,
|
|
3509
3536
|
retryable: context.driveLastError.retryable,
|
|
3510
|
-
at: context.driveLastError.at
|
|
3537
|
+
at: context.driveLastError.at,
|
|
3538
|
+
...ageMs !== void 0 ? { age_ms: ageMs } : {}
|
|
3511
3539
|
}
|
|
3512
3540
|
});
|
|
3513
3541
|
}
|
|
3514
3542
|
if (context.inspectLastError) {
|
|
3543
|
+
const ageMs = getErrorAgeMs(context.inspectLastError.at);
|
|
3544
|
+
const isStale = ageMs !== void 0 && ageMs > STALE_ERROR_THRESHOLD_MS;
|
|
3545
|
+
if (isStale) {
|
|
3546
|
+
warnings.push(
|
|
3547
|
+
`Ignoring stale inspect error (${Math.round(ageMs / 1e3)}s old): ${context.inspectLastError.message}`
|
|
3548
|
+
);
|
|
3549
|
+
}
|
|
3515
3550
|
checks.push({
|
|
3516
3551
|
name: "inspect.last_error",
|
|
3517
|
-
ok:
|
|
3552
|
+
ok: isStale,
|
|
3518
3553
|
message: context.inspectLastError.message,
|
|
3519
3554
|
details: {
|
|
3520
3555
|
code: context.inspectLastError.code,
|
|
3521
3556
|
retryable: context.inspectLastError.retryable,
|
|
3522
|
-
at: context.inspectLastError.at
|
|
3557
|
+
at: context.inspectLastError.at,
|
|
3558
|
+
...ageMs !== void 0 ? { age_ms: ageMs } : {}
|
|
3523
3559
|
}
|
|
3524
3560
|
});
|
|
3525
3561
|
}
|
|
@@ -3578,6 +3614,7 @@ var buildDiagnosticReport = (sessionId, context = {}) => {
|
|
|
3578
3614
|
loop_detected: context.recoveryMetrics.loopDetected
|
|
3579
3615
|
} : {}
|
|
3580
3616
|
} : void 0,
|
|
3617
|
+
...warnings.length > 0 ? { warnings } : {},
|
|
3581
3618
|
notes: ["Diagnostics include runtime status; some checks may be stubbed."]
|
|
3582
3619
|
};
|
|
3583
3620
|
return report;
|
|
@@ -4271,6 +4308,7 @@ var DebuggerBridge = class {
|
|
|
4271
4308
|
}
|
|
4272
4309
|
state.attached = true;
|
|
4273
4310
|
this.touch(tabId, state);
|
|
4311
|
+
this.clearLastError();
|
|
4274
4312
|
return { ok: true, result: { attached: true } };
|
|
4275
4313
|
} catch (error) {
|
|
4276
4314
|
const info = this.handleBridgeError(error);
|
|
@@ -4298,6 +4336,7 @@ var DebuggerBridge = class {
|
|
|
4298
4336
|
return { ok: false, error };
|
|
4299
4337
|
}
|
|
4300
4338
|
this.markDetached(tabId);
|
|
4339
|
+
this.clearLastError();
|
|
4301
4340
|
return { ok: true, result: { attached: false } };
|
|
4302
4341
|
} catch (error) {
|
|
4303
4342
|
const info = this.handleBridgeError(error);
|
|
@@ -4311,7 +4350,7 @@ var DebuggerBridge = class {
|
|
|
4311
4350
|
return attachResult;
|
|
4312
4351
|
}
|
|
4313
4352
|
try {
|
|
4314
|
-
const
|
|
4353
|
+
const runCommand = async () => await this.bridge.requestDebugger(
|
|
4315
4354
|
"debugger.command",
|
|
4316
4355
|
{
|
|
4317
4356
|
tab_id: tabId,
|
|
@@ -4320,6 +4359,15 @@ var DebuggerBridge = class {
|
|
|
4320
4359
|
},
|
|
4321
4360
|
timeoutMs
|
|
4322
4361
|
);
|
|
4362
|
+
let response = await runCommand();
|
|
4363
|
+
if (response.status === "error" && this.shouldRetryAfterStaleAttach(response.error)) {
|
|
4364
|
+
this.markDetached(tabId);
|
|
4365
|
+
const reattach = await this.attach(tabId);
|
|
4366
|
+
if (!reattach.ok) {
|
|
4367
|
+
return reattach;
|
|
4368
|
+
}
|
|
4369
|
+
response = await runCommand();
|
|
4370
|
+
}
|
|
4323
4371
|
if (response.status === "error") {
|
|
4324
4372
|
const error = response.error ?? {
|
|
4325
4373
|
code: "INSPECT_UNAVAILABLE",
|
|
@@ -4330,6 +4378,7 @@ var DebuggerBridge = class {
|
|
|
4330
4378
|
return { ok: false, error };
|
|
4331
4379
|
}
|
|
4332
4380
|
this.touch(tabId, this.ensureTab(tabId));
|
|
4381
|
+
this.clearLastError();
|
|
4333
4382
|
return { ok: true, result: response.result };
|
|
4334
4383
|
} catch (error) {
|
|
4335
4384
|
const info = this.handleBridgeError(error);
|
|
@@ -4415,8 +4464,15 @@ var DebuggerBridge = class {
|
|
|
4415
4464
|
this.lastError = error;
|
|
4416
4465
|
this.lastErrorAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
4417
4466
|
}
|
|
4467
|
+
clearLastError() {
|
|
4468
|
+
this.lastError = void 0;
|
|
4469
|
+
this.lastErrorAt = void 0;
|
|
4470
|
+
}
|
|
4418
4471
|
handleBridgeError(error) {
|
|
4419
4472
|
if (error instanceof ExtensionBridgeError) {
|
|
4473
|
+
if (error.code === "EXTENSION_DISCONNECTED") {
|
|
4474
|
+
this.markAllDetached();
|
|
4475
|
+
}
|
|
4420
4476
|
const info2 = toDriveError(error);
|
|
4421
4477
|
this.recordError(info2);
|
|
4422
4478
|
return info2;
|
|
@@ -4429,6 +4485,18 @@ var DebuggerBridge = class {
|
|
|
4429
4485
|
this.recordError(info);
|
|
4430
4486
|
return info;
|
|
4431
4487
|
}
|
|
4488
|
+
shouldRetryAfterStaleAttach(error) {
|
|
4489
|
+
if (!error) return false;
|
|
4490
|
+
if (error.code !== "FAILED_PRECONDITION" && error.code !== "INSPECT_UNAVAILABLE") {
|
|
4491
|
+
return false;
|
|
4492
|
+
}
|
|
4493
|
+
return error.message.toLowerCase().includes("not attached");
|
|
4494
|
+
}
|
|
4495
|
+
markAllDetached() {
|
|
4496
|
+
for (const tabId of this.tabs.keys()) {
|
|
4497
|
+
this.markDetached(tabId);
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4432
4500
|
};
|
|
4433
4501
|
|
|
4434
4502
|
// packages/core/src/server.ts
|