mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
bab040985a
* refactor(transport): exactly-once command transport — journal, waiters, absolute deadlines
Rebuilds the CLI→daemon→extension command transport around one principle:
exactly-once = at-least-once retry + an idempotent executor. This replaces
the accumulated per-layer compensation (three client retry flags, cause-code
walking, duplicate-id 409s, an inner extension retry loop, a phased
reconnect state machine) with three small primitives:
1. Command journal (extension/src/journal.ts, chrome.storage.session).
Every command id executes exactly once: duplicates attach to the
in-flight promise, completed ids replay the recorded result, and ids
whose worker died mid-execution report `command_lost` honestly.
storage.session survives service-worker restarts and clears on browser
exit — precisely the lifetime a retry cares about.
2. Stable ids + daemon waiters. Transport retries keep the SAME id; the
daemon attaches duplicate ids to the pending command instead of 409ing.
With the executor idempotent, every transport failure becomes safely
retryable (gated on extension >= 1.0.22; legacy extensions keep the old
conservative pre-connect-only retry). Semantic retries (attach_failed /
tab_gone — failures BEFORE any page code ran) are the only place a new
id is minted, once.
3. Absolute deadline (`deadlineAt`, epoch ms) instead of per-hop durations.
Same machine, one clock: every layer computes remaining = deadlineAt -
now, so daemon queueing and service-worker wake latency no longer
silently shrink the innermost budget or invert the layering.
Error classification now happens once, at the failure site: the extension
tags results with machine-readable codes (attach_failed, tab_gone,
target_navigated, detached_mid_command, cdp_timeout) and errors.ts prefers
codes over the legacy message-pattern tables (kept only for old
extensions). detached_mid_command / cdp_timeout are now correctly
non-retryable — they die mid-execution, so a blind re-run could
double-apply a write.
Deletions and stability fixes riding the same contract:
- extension evaluate()'s inner retry loop (the client owns semantic
retries now); the fast/slow reconnect window + notifyDaemonReachable
rescheduling (plain exponential backoff with jitter, reset on success);
the three parallel session-override Maps (one record per lease).
- WS application-level keepalive ({type:'ping'} every 20s): Chrome 116+
only extends the service worker's lifetime on WS *activity*, so an idle
socket lived on a knife-edge between the 30s idle kill and the 30s
keepalive alarm.
- idle-lease release is deferred while a command is executing on the
lease (refcount) — a 30s idle timer can no longer tear the tab down
mid-command; completion re-arms the timer.
- daemon shutdown flushes structured 503s to waiting clients before
exiting instead of process.exit() killing the queued responses.
- results are delivered on the freshest open socket after a reconnect
instead of being dropped when the executing socket was superseded.
* fix(transport): gate daemon_shutting_down resend on journal capability; bound ensure by deadline
From adversarial review of the transport-v2 work:
- daemon_shutting_down was resent with the same id regardless of the
extension's journal capability. The daemon fires it for DISPATCHED
commands too, so on a pre-journal extension the resend re-executes a
write. The daemon now returns the pre-dispatch contract for commands
that never reached the extension (safe to resend anywhere) and
daemon_shutting_down only for dispatched ones; the client resends
those only when the extension journals ids, else surfaces
command_result_unknown.
- ensureBridge's connect wait was a fixed 45s regardless of the
command's remaining budget — repeated daemon failures could stretch a
30s --timeout command past two minutes. The wait is now clamped to
the remaining deadline.
21 lines
426 B
JSON
21 lines
426 B
JSON
{
|
|
"name": "opencli-extension",
|
|
"version": "1.0.22",
|
|
"private": true,
|
|
"opencli": {
|
|
"compatRange": ">=1.7.0"
|
|
},
|
|
"type": "module",
|
|
"scripts": {
|
|
"dev": "vite build --watch",
|
|
"build": "vite build",
|
|
"package:release": "node scripts/package-release.mjs",
|
|
"typecheck": "tsc --noEmit"
|
|
},
|
|
"devDependencies": {
|
|
"@types/chrome": "^0.0.287",
|
|
"typescript": "^5.7.0",
|
|
"vite": "^6.0.0"
|
|
}
|
|
}
|