@hover-dev/core 0.13.0 → 0.14.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/dist/playwright/preflight.js +1 -1
- package/dist/plugin-api.d.ts +23 -0
- package/dist/plugin-api.d.ts.map +1 -1
- package/dist/service.d.ts +12 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +115 -45
- package/package.json +1 -1
|
@@ -34,7 +34,7 @@ export async function preflightCDP(cdpUrl, timeoutMs = 2000) {
|
|
|
34
34
|
catch (err) {
|
|
35
35
|
return {
|
|
36
36
|
ok: false,
|
|
37
|
-
reason: `Chrome debug session not detected at ${cdpUrl}.
|
|
37
|
+
reason: `Chrome debug session not detected at ${cdpUrl}. Click the ✨ launcher in the widget to start it, or run \`pnpm exec hover-chrome\` (npx hover-chrome).`,
|
|
38
38
|
};
|
|
39
39
|
}
|
|
40
40
|
if (!versionRes.ok) {
|
package/dist/plugin-api.d.ts
CHANGED
|
@@ -33,6 +33,10 @@ export interface HoverPluginMode {
|
|
|
33
33
|
label: string;
|
|
34
34
|
/** One-liner help text shown in the dropdown. */
|
|
35
35
|
description?: string;
|
|
36
|
+
/** Short status shown in the mode bar's right-hand hint slot while this
|
|
37
|
+
* mode is engaged. Defaults to "active" if omitted. Keep it terse — e.g.
|
|
38
|
+
* "MITM proxy active". */
|
|
39
|
+
engagedHint?: string;
|
|
36
40
|
/** Mode ids this mode cannot be active alongside. Two plugins both
|
|
37
41
|
* needing an exclusive proxy would set each other here. */
|
|
38
42
|
conflictsWith?: string[];
|
|
@@ -113,10 +117,29 @@ export interface ModeActivateCtx extends HoverHookCtxBase {
|
|
|
113
117
|
export interface ModeDeactivateCtx extends HoverHookCtxBase {
|
|
114
118
|
modeId: string;
|
|
115
119
|
}
|
|
120
|
+
/** Fired exactly once when the host service starts, BEFORE the debug Chrome
|
|
121
|
+
* is (auto-)launched. A plugin that needs Chrome to be born with specific
|
|
122
|
+
* flags — e.g. a resident MITM proxy that Chrome must point through from the
|
|
123
|
+
* first navigation — boots that sidecar here and calls setChromeProxy so the
|
|
124
|
+
* host bakes the flags into the single Chrome launch. This is what lets the
|
|
125
|
+
* security plugin run one always-on (transparent-by-default) proxy instead
|
|
126
|
+
* of launching a second Chrome on mode entry. */
|
|
127
|
+
export interface ServiceStartCtx extends HoverHookCtxBase {
|
|
128
|
+
/** Tell the host "the debug Chrome should be launched with these proxy
|
|
129
|
+
* settings". Set once here; persists for the whole session. */
|
|
130
|
+
setChromeProxy(proxy: {
|
|
131
|
+
port: number;
|
|
132
|
+
spki: string;
|
|
133
|
+
} | null): void;
|
|
134
|
+
/** Same as the activate-time variant — seed runtime env for a declared MCP
|
|
135
|
+
* server before it's spawned. */
|
|
136
|
+
setMcpServerEnv(id: string, env: Record<string, string>): void;
|
|
137
|
+
}
|
|
116
138
|
/** Fired exactly once when the host service is shutting down for any
|
|
117
139
|
* reason. Hooks must release subprocesses and file handles. */
|
|
118
140
|
export type ShutdownCtx = HoverHookCtxBase;
|
|
119
141
|
export interface HoverHooks {
|
|
142
|
+
'hover:service:start'?: (ctx: ServiceStartCtx) => void | Promise<void>;
|
|
120
143
|
'hover:mode:activate'?: (ctx: ModeActivateCtx) => void | Promise<void>;
|
|
121
144
|
'hover:mode:deactivate'?: (ctx: ModeDeactivateCtx) => void | Promise<void>;
|
|
122
145
|
'hover:service:shutdown'?: (ctx: ShutdownCtx) => void | Promise<void>;
|
package/dist/plugin-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"}
|
package/dist/service.d.ts
CHANGED
|
@@ -18,6 +18,18 @@ export interface ServiceOptions {
|
|
|
18
18
|
* pre-plugin Hover" — important for the long tail of users who never
|
|
19
19
|
* install one. */
|
|
20
20
|
plugins?: HoverPluginManifest[];
|
|
21
|
+
/** When true, the service launches the single debug Chrome itself at
|
|
22
|
+
* startup — AFTER firing plugin `hover:service:start` hooks, so a plugin
|
|
23
|
+
* that set a resident proxy (e.g. security's MITM) has its flags baked
|
|
24
|
+
* into that one Chrome. Previously each bundler shim called
|
|
25
|
+
* launchDebugChrome() directly, which bypassed the service and so couldn't
|
|
26
|
+
* see the proxy; moving it here is what enables the single-Chrome model.
|
|
27
|
+
* Default false (shims pass it through from their own option). */
|
|
28
|
+
autoLaunchChrome?: boolean;
|
|
29
|
+
/** The dev-server URL the auto-launched Chrome should open. Each shim knows
|
|
30
|
+
* its own framework's dev URL and passes it here. Defaults to the cdp host
|
|
31
|
+
* if unset, but shims should always provide it. */
|
|
32
|
+
devUrl?: string;
|
|
21
33
|
}
|
|
22
34
|
export interface ServiceHandle {
|
|
23
35
|
/** The port the WebSocketServer actually bound to. May differ from
|
package/dist/service.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AA8EA,OAAO,EAEL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gFAAgF;IAChF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;6EAGyE;IACzE,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;uBAImB;IACnB,OAAO,CAAC,EAAE,mBAAmB,EAAE,CAAC;IAChC;;;;;;uEAMmE;IACnE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B;;wDAEoD;IACpD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,aAAa;IAC5B;4EACwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAiED,wBAAsB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAs2B/E"}
|
package/dist/service.js
CHANGED
|
@@ -54,6 +54,7 @@ import { listAgentAvailability, pickPrimaryAgent, } from './agents/detect.js';
|
|
|
54
54
|
import { getAgent } from './agents/registry.js';
|
|
55
55
|
import { getPreflight, invalidatePreflight } from './playwright/preflightCache.js';
|
|
56
56
|
import { resolveMcpConfig } from './playwright/resolveMcpConfig.js';
|
|
57
|
+
import { launchDebugChrome } from './playwright/launchChrome.js';
|
|
57
58
|
import { listSkills } from './skills/writeSkill.js';
|
|
58
59
|
import { listSpecs } from './specs/listSpecs.js';
|
|
59
60
|
import { send, sendIfOpen } from './service/types.js';
|
|
@@ -65,6 +66,19 @@ import { CURRENT_API_VERSION, } from './plugin-api.js';
|
|
|
65
66
|
// handler modules can share them. See those files for the wire shape.
|
|
66
67
|
const PROTOCOL_VERSION = 1;
|
|
67
68
|
const PORT_RETRIES = 10;
|
|
69
|
+
/** CJK-presence test — mirrors voice.js's detectLanguage. Any Han character
|
|
70
|
+
* in the prompt flips the agent's prose output to Chinese. */
|
|
71
|
+
const CJK_RE = /[一-鿿]/;
|
|
72
|
+
/** Appended to the agent's system prompt when the user's prompt contains CJK,
|
|
73
|
+
* so the human-facing prose (verification summary / ## Findings / step
|
|
74
|
+
* narration) comes back in Chinese — matching how Voice mode picks a Chinese
|
|
75
|
+
* TTS voice for the same prompt. Deliberately scoped to PROSE only: the agent
|
|
76
|
+
* must still use the page's real (often English) accessible names, labels,
|
|
77
|
+
* and selectors when driving the browser. */
|
|
78
|
+
const ZH_OUTPUT_DIRECTIVE = '用户使用中文下达指令。请用简体中文撰写所有面向用户的文字输出:验证结论摘要、' +
|
|
79
|
+
'`## Findings` 区块(bug / 问题 / 备注)、以及每一步的中文描述。' +
|
|
80
|
+
'注意:这只影响你写给用户看的文字。操作浏览器时仍要使用页面真实的(通常是英文的)' +
|
|
81
|
+
'角色名、标签、可访问名称和选择器——不要把它们翻译成中文。';
|
|
68
82
|
/**
|
|
69
83
|
* Try to bind a WebSocketServer to <host>:<port>. Resolves with the wss on
|
|
70
84
|
* success; rejects with the bind error (typically EADDRINUSE) on failure.
|
|
@@ -175,15 +189,11 @@ export async function startService(opts) {
|
|
|
175
189
|
}
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
|
-
//
|
|
179
|
-
// Chrome
|
|
180
|
-
//
|
|
181
|
-
const extras = effectiveLaunchExtras();
|
|
182
|
-
const effectiveCdpUrl = extras?.cdpPort
|
|
183
|
-
? `http://localhost:${extras.cdpPort}`
|
|
184
|
-
: cdpUrl;
|
|
192
|
+
// Single-Chrome model: the Playwright MCP always points at the one debug
|
|
193
|
+
// Chrome on the normal cdpUrl. (Pre-single-Chrome this branched to a
|
|
194
|
+
// mode-specific port like 9333; there's no second Chrome anymore.)
|
|
185
195
|
return resolveMcpConfig({
|
|
186
|
-
cdpUrl
|
|
196
|
+
cdpUrl,
|
|
187
197
|
port,
|
|
188
198
|
extra,
|
|
189
199
|
// Suffix the filename by the mode so different mode toggles within
|
|
@@ -223,44 +233,31 @@ export async function startService(opts) {
|
|
|
223
233
|
}
|
|
224
234
|
/** id of the currently-active mode, or null for normal (unmoded) mode. */
|
|
225
235
|
let currentModeId = null;
|
|
226
|
-
/** Chrome-proxy settings
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
|
|
236
|
+
/** Chrome-proxy settings a plugin's `hover:service:start` hook set on us
|
|
237
|
+
* (security's resident MITM). RESIDENT for the whole session — set once
|
|
238
|
+
* before Chrome launches, never cleared on mode change — so the single
|
|
239
|
+
* debug Chrome is born with `--proxy-server` + the SPKI pin and entering
|
|
240
|
+
* Security mode is just a runtime flip of the proxy, not a Chrome relaunch.
|
|
241
|
+
* Read by `effectiveLaunchExtras()` and threaded into every cdp handler
|
|
242
|
+
* (check-cdp / launch-chrome / focus-debug) plus the initial auto-launch. */
|
|
243
|
+
let residentChromeProxy = null;
|
|
232
244
|
/** Runtime env overrides keyed by mcpServer id, set by plugin
|
|
233
245
|
* activate hooks (via ctx.setMcpServerEnv). Cleared on mode change.
|
|
234
246
|
* Merged with the manifest-declared env when the agent's spawn-time
|
|
235
247
|
* MCP config is built. */
|
|
236
248
|
const mcpEnvOverrides = new Map();
|
|
237
|
-
/** The cdp-handler extras (
|
|
238
|
-
*
|
|
239
|
-
*
|
|
240
|
-
*
|
|
249
|
+
/** The cdp-handler extras (proxy) threaded into launch-chrome / check-cdp /
|
|
250
|
+
* focus-debug and the initial auto-launch. In the single-Chrome model this
|
|
251
|
+
* is driven purely by the RESIDENT proxy (set in `hover:service:start`),
|
|
252
|
+
* NOT by the active mode — there is one Chrome on the normal CDP port that
|
|
253
|
+
* is always proxied; entering Security mode flips the proxy's behaviour,
|
|
254
|
+
* it does not relaunch Chrome on a different port. Returns undefined when
|
|
255
|
+
* no plugin set a resident proxy (the common no-security case), so plain
|
|
256
|
+
* Hover is byte-for-byte unchanged. */
|
|
241
257
|
const effectiveLaunchExtras = () => {
|
|
242
|
-
if (!
|
|
258
|
+
if (!residentChromeProxy)
|
|
243
259
|
return undefined;
|
|
244
|
-
|
|
245
|
-
const flags = plugin?.chromeFlags;
|
|
246
|
-
if (!flags && !modeChromeProxy)
|
|
247
|
-
return undefined;
|
|
248
|
-
// Belt + suspenders — flags.activeInModes is honoured if set, but
|
|
249
|
-
// since chromeFlags lives on the plugin that contributed this mode,
|
|
250
|
-
// the default of "applies in own mode" matches what we want.
|
|
251
|
-
if (flags?.activeInModes && !flags.activeInModes.includes('*') && !flags.activeInModes.includes(currentModeId)) {
|
|
252
|
-
// Plugin explicitly restricted its chromeFlags to a different mode.
|
|
253
|
-
// Honour that and only carry modeChromeProxy (set by setChromeProxy).
|
|
254
|
-
return modeChromeProxy ? { proxy: modeChromeProxy } : undefined;
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
cdpPort: flags?.cdpPort,
|
|
258
|
-
userDataDir: flags?.userDataDir,
|
|
259
|
-
// modeChromeProxy wins over flags.proxy because it's the runtime
|
|
260
|
-
// value the activate hook computed (after starting mockttp);
|
|
261
|
-
// flags.proxy is only ever set by tests stubbing the manifest.
|
|
262
|
-
proxy: modeChromeProxy ?? flags?.proxy,
|
|
263
|
-
};
|
|
260
|
+
return { proxy: residentChromeProxy };
|
|
264
261
|
};
|
|
265
262
|
/** Send the current mode catalogue to one ws (or all if undefined). */
|
|
266
263
|
const broadcastModes = (target) => {
|
|
@@ -309,8 +306,14 @@ export async function startService(opts) {
|
|
|
309
306
|
}
|
|
310
307
|
}
|
|
311
308
|
}
|
|
312
|
-
|
|
313
|
-
|
|
309
|
+
// NOTE: neither residentChromeProxy NOR mcpEnvOverrides is cleared here.
|
|
310
|
+
// In the single-Chrome model both are RESIDENT — set once in
|
|
311
|
+
// service:start (e.g. security's HOVER_SECURITY_API base + token), they
|
|
312
|
+
// must survive every mode toggle so the agent's spawned MCP server can
|
|
313
|
+
// always reach the control plane. Clearing them on mode change was the
|
|
314
|
+
// pre-resident behaviour and would leave the security MCP server with no
|
|
315
|
+
// env → it exits with "failed". Mode changes now only flip plugin runtime
|
|
316
|
+
// state via the plugin's own activate/deactivate hooks.
|
|
314
317
|
currentModeId = null;
|
|
315
318
|
// Bring up new mode
|
|
316
319
|
if (newModeId) {
|
|
@@ -325,7 +328,10 @@ export async function startService(opts) {
|
|
|
325
328
|
broadcast: broadcastPluginEvent,
|
|
326
329
|
modeId: newModeId,
|
|
327
330
|
setChromeProxy(proxy) {
|
|
328
|
-
|
|
331
|
+
// Retained for API compatibility. In the single-Chrome model the
|
|
332
|
+
// proxy is normally set once in service:start; if an activate hook
|
|
333
|
+
// still calls this, treat it as updating the resident proxy.
|
|
334
|
+
residentChromeProxy = proxy;
|
|
329
335
|
},
|
|
330
336
|
setMcpServerEnv(id, env) {
|
|
331
337
|
mcpEnvOverrides.set(id, env);
|
|
@@ -339,9 +345,10 @@ export async function startService(opts) {
|
|
|
339
345
|
// pretend to be in `newModeId` with no sidecars running.
|
|
340
346
|
// Widget still trusts the broadcast below to learn we're back
|
|
341
347
|
// to default. The error is rethrown so the caller can surface
|
|
342
|
-
// it to the user.
|
|
343
|
-
|
|
344
|
-
|
|
348
|
+
// it to the user. residentChromeProxy and mcpEnvOverrides are NOT
|
|
349
|
+
// touched — both are owned by service:start, independent of mode
|
|
350
|
+
// activation (clearing the env would break the resident security
|
|
351
|
+
// MCP server).
|
|
345
352
|
currentModeId = null;
|
|
346
353
|
broadcastModes();
|
|
347
354
|
throw err;
|
|
@@ -694,6 +701,17 @@ export async function startService(opts) {
|
|
|
694
701
|
}
|
|
695
702
|
}
|
|
696
703
|
}
|
|
704
|
+
// Mirror the prompt's language in the agent's *prose* output — the
|
|
705
|
+
// verification summary (Result card), the ## Findings block, and the
|
|
706
|
+
// step narration — the same way Voice mode mirrors it in TTS. A
|
|
707
|
+
// Chinese prompt should produce a Chinese report. This does NOT change
|
|
708
|
+
// how the agent operates the browser: selectors, role names, and the
|
|
709
|
+
// app's own (often English) UI text are unaffected — only the agent's
|
|
710
|
+
// human-facing writing follows the user. Detection mirrors voice.js's
|
|
711
|
+
// detectLanguage (CJK presence → zh).
|
|
712
|
+
if (CJK_RE.test(text)) {
|
|
713
|
+
appendSystemPrompt = `${appendSystemPrompt}\n\n${ZH_OUTPUT_DIRECTIVE}`;
|
|
714
|
+
}
|
|
697
715
|
// Snapshot the agent id so a switch-agent message during the run
|
|
698
716
|
// can't smear two agents across one invocation. (We also gate
|
|
699
717
|
// switch-agent on `busy`, but defense in depth.)
|
|
@@ -844,6 +862,58 @@ export async function startService(opts) {
|
|
|
844
862
|
}
|
|
845
863
|
});
|
|
846
864
|
});
|
|
865
|
+
// ───────────────────────── service:start + single Chrome ─────────────────
|
|
866
|
+
// Fire plugin `hover:service:start` hooks BEFORE launching Chrome, so a
|
|
867
|
+
// plugin (security) can boot its resident proxy and call setChromeProxy.
|
|
868
|
+
// residentChromeProxy is then baked into the one auto-launched Chrome.
|
|
869
|
+
for (const p of plugins) {
|
|
870
|
+
const hook = p.hooks?.['hover:service:start'];
|
|
871
|
+
if (!hook)
|
|
872
|
+
continue;
|
|
873
|
+
try {
|
|
874
|
+
await hook({
|
|
875
|
+
devRoot,
|
|
876
|
+
broadcast: broadcastPluginEvent,
|
|
877
|
+
setChromeProxy(proxy) {
|
|
878
|
+
residentChromeProxy = proxy;
|
|
879
|
+
},
|
|
880
|
+
setMcpServerEnv(id, env) {
|
|
881
|
+
mcpEnvOverrides.set(id, env);
|
|
882
|
+
},
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
catch (err) {
|
|
886
|
+
process.stderr.write(`[hover] plugin "${p.name}" service:start failed: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
// Auto-launch the single debug Chrome here (moved out of the bundler shims
|
|
890
|
+
// so it happens AFTER service:start and can carry residentChromeProxy).
|
|
891
|
+
// Fire-and-forget — startup must not block on Chrome, and a launch failure
|
|
892
|
+
// is non-fatal (the widget's amber ✨ lets the user retry on demand).
|
|
893
|
+
if (opts.autoLaunchChrome) {
|
|
894
|
+
const launchPort = (() => {
|
|
895
|
+
try {
|
|
896
|
+
return Number(new URL(cdpUrl).port) || 9222;
|
|
897
|
+
}
|
|
898
|
+
catch {
|
|
899
|
+
return 9222;
|
|
900
|
+
}
|
|
901
|
+
})();
|
|
902
|
+
const launchUrl = opts.devUrl ?? cdpUrl;
|
|
903
|
+
launchDebugChrome({
|
|
904
|
+
url: launchUrl,
|
|
905
|
+
port: launchPort,
|
|
906
|
+
proxy: residentChromeProxy ?? undefined,
|
|
907
|
+
})
|
|
908
|
+
.then((r) => {
|
|
909
|
+
if (!r.ok) {
|
|
910
|
+
process.stderr.write(`[hover] auto-launch Chrome failed: ${r.reason}\n`);
|
|
911
|
+
}
|
|
912
|
+
})
|
|
913
|
+
.catch((err) => {
|
|
914
|
+
process.stderr.write(`[hover] auto-launch Chrome error: ${err instanceof Error ? err.message : String(err)}\n`);
|
|
915
|
+
});
|
|
916
|
+
}
|
|
847
917
|
return {
|
|
848
918
|
port,
|
|
849
919
|
async close() {
|