@hover-dev/core 0.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA2C,MAAM,YAAY,CAAC;AAsF3F,eAAO,MAAM,WAAW,EAAE,eAyHzB,CAAC"}
1
+ {"version":3,"file":"claude.d.ts","sourceRoot":"","sources":["../../src/agents/claude.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAA2C,MAAM,YAAY,CAAC;AA6G3F,eAAO,MAAM,WAAW,EAAE,eA0HzB,CAAC"}
@@ -39,12 +39,35 @@ function claudeState(state) {
39
39
  }
40
40
  return state;
41
41
  }
42
+ /** Every built-in claude-code tool that has nothing to do with driving a
43
+ * browser. Combined with `--strict-mcp-config` + an allow-list of mcp__*
44
+ * ids, this leaves Claude with only the Playwright MCP (plus any
45
+ * plugin-contributed MCPs) as a usable tool surface. */
46
+ const CLAUDE_DEFAULT_DISALLOWED_TOOLS = [
47
+ // file / shell / data access — never appropriate for browser driving
48
+ 'Bash', 'BashOutput', 'KillBash',
49
+ 'Edit', 'MultiEdit', 'Write', 'Read', 'NotebookEdit',
50
+ 'Grep', 'Glob', 'Task', 'TodoWrite',
51
+ 'WebFetch', 'WebSearch',
52
+ // plan / worktree / cron / notification — irrelevant in -p mode
53
+ 'EnterPlanMode', 'ExitPlanMode',
54
+ 'EnterWorktree', 'ExitWorktree',
55
+ 'CronCreate', 'CronDelete', 'CronList',
56
+ 'PushNotification', 'RemoteTrigger',
57
+ // task & tool introspection added in claude 2.1.x — let through and
58
+ // the agent will burn turns exploring instead of executing
59
+ 'ToolSearch',
60
+ 'Monitor', 'TaskOutput', 'TaskStop',
61
+ 'AskUserQuestion',
62
+ 'ShareOnboardingGuide',
63
+ ];
42
64
  export const claudeAgent = {
43
65
  id: 'claude',
44
66
  binName: 'claude',
45
67
  protocol: 'argv',
46
68
  streamFormat: 'stream-json',
47
69
  sandboxStrength: 'hard',
70
+ defaultDisallowedTools: CLAUDE_DEFAULT_DISALLOWED_TOOLS,
48
71
  display: {
49
72
  label: 'Claude Code',
50
73
  tagline: 'Anthropic — best-in-class browser driving, hard tool sandbox',
@@ -146,6 +146,11 @@ export interface AgentDescriptor {
146
146
  streamFormat: StreamFormat;
147
147
  sandboxStrength: SandboxStrength;
148
148
  display: AgentDisplay;
149
+ /** Hard-sandbox agents pass this list to `disallowedTools` when the
150
+ * service-level allow/deny config isn't explicitly overridden. Lets the
151
+ * per-CLI deny list live alongside its descriptor instead of as a magic
152
+ * array in the service. Soft-sandbox agents leave this undefined. */
153
+ defaultDisallowedTools?: readonly string[];
149
154
  buildArgs(opts: InvokeOptions): string[];
150
155
  /**
151
156
  * Parse a single line of agent stdout into normalised InvokeEvents.
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
@@ -9,6 +9,18 @@ export interface LaunchOptions {
9
9
  readyTimeoutMs?: number;
10
10
  /** Poll interval while waiting (default 300ms). */
11
11
  pollMs?: number;
12
+ /** Security-mode proxy config. When provided, Chrome is launched with
13
+ * `--proxy-server=127.0.0.1:<proxyPort>` and the given SPKI pinned via
14
+ * `--ignore-certificate-errors-spki-list` so HTTPS certs from the
15
+ * Hover MITM CA validate without polluting the OS trust store. The
16
+ * caller is expected to pick a different `userDataDir` and `port` from
17
+ * the normal-mode launch so the two profiles don't share state. */
18
+ proxy?: {
19
+ /** Local mockttp port the proxy is listening on. */
20
+ port: number;
21
+ /** Base64 SHA-256 of the MITM CA's SubjectPublicKeyInfo. */
22
+ spki: string;
23
+ };
12
24
  }
13
25
  export type LaunchResult = {
14
26
  ok: true;
@@ -1 +1 @@
1
- {"version":3,"file":"launchChrome.d.ts","sourceRoot":"","sources":["../../src/playwright/launchChrome.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMlC,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CA0ChD;AAiCD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CAwDvF"}
1
+ {"version":3,"file":"launchChrome.d.ts","sourceRoot":"","sources":["../../src/playwright/launchChrome.ts"],"names":[],"mappings":"AAeA,MAAM,WAAW,aAAa;IAC5B,yCAAyC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,2CAA2C;IAC3C,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;wEAKoE;IACpE,KAAK,CAAC,EAAE;QACN,oDAAoD;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,4DAA4D;QAC5D,IAAI,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,MAAM,MAAM,YAAY,GACpB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,cAAc,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACxE;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAMlC,wBAAgB,gBAAgB,IAAI,MAAM,GAAG,IAAI,CA0ChD;AAiCD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,IAAI,GAAE,aAAkB,GAAG,OAAO,CAAC,YAAY,CAAC,CA8DvF"}
@@ -111,8 +111,11 @@ export async function launchDebugChrome(opts = {}) {
111
111
  `--user-data-dir=${userDataDir}`,
112
112
  '--no-first-run',
113
113
  '--no-default-browser-check',
114
- url,
115
114
  ];
115
+ if (opts.proxy) {
116
+ args.push(`--proxy-server=127.0.0.1:${opts.proxy.port}`, `--ignore-certificate-errors-spki-list=${opts.proxy.spki}`);
117
+ }
118
+ args.push(url);
116
119
  const child = spawn(chrome, args, {
117
120
  detached: true,
118
121
  stdio: 'ignore',
@@ -103,22 +103,37 @@ export async function raiseChromeWindow(pid) {
103
103
  function runCapture(cmd, args) {
104
104
  return new Promise(resolve => {
105
105
  let out = '';
106
+ let settled = false;
107
+ const finish = (v) => {
108
+ if (settled)
109
+ return;
110
+ settled = true;
111
+ resolve(v);
112
+ };
106
113
  const child = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'ignore'] });
107
114
  child.stdout.on('data', chunk => {
108
115
  out += chunk.toString();
109
116
  });
110
- child.on('error', () => resolve(null));
111
- child.on('close', code => resolve(code === 0 ? out : null));
117
+ child.on('error', () => finish(null));
118
+ child.on('close', code => finish(code === 0 ? out : null));
112
119
  });
113
120
  }
114
121
  function runDetached(cmd, args) {
115
122
  return new Promise((resolve, reject) => {
123
+ let settled = false;
116
124
  const child = spawn(cmd, args, { stdio: 'ignore' });
117
- child.on('error', reject);
118
- child.on('close', code => {
125
+ child.on('error', err => {
126
+ if (settled)
127
+ return;
128
+ settled = true;
129
+ reject(err);
130
+ });
131
+ child.on('close', () => {
132
+ if (settled)
133
+ return;
134
+ settled = true;
119
135
  // Don't treat non-zero as fatal — caller already wraps in try/catch.
120
136
  resolve();
121
- void code;
122
137
  });
123
138
  });
124
139
  }
@@ -18,10 +18,27 @@
18
18
  * which lets multiple Hover services (one per example app) coexist
19
19
  * without stepping on each other's CDP endpoint.
20
20
  */
21
+ export interface ExtraMcpServer {
22
+ /** Stable id of the server. Becomes the JSON key under mcpServers; also
23
+ * the prefix Claude exposes its tools under (`mcp__<id>__<tool>`). */
24
+ id: string;
25
+ command: string;
26
+ args?: string[];
27
+ env?: Record<string, string>;
28
+ }
21
29
  export declare function resolveMcpConfig(opts: {
22
30
  /** CDP URL passed to the MCP server's `--cdp-endpoint` flag. */
23
31
  cdpUrl: string;
24
32
  /** Service port — used to namespace the temp config file. */
25
33
  port: number;
34
+ /** Additional MCP servers contributed by active plugins. Each becomes
35
+ * a key under the mcpServers object. The id is also used to name the
36
+ * tool prefix Claude exposes (e.g. `mcp__hover_security__list_flows`),
37
+ * but Claude sanitises non-alphanumeric chars to underscores, so the
38
+ * caller does NOT need to do that. */
39
+ extra?: ExtraMcpServer[];
40
+ /** Suffix for the output filename so multiple parallel configs from
41
+ * the same service (e.g. mode toggle round-trips) don't share state. */
42
+ suffix?: string;
26
43
  }): string;
27
44
  //# sourceMappingURL=resolveMcpConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"resolveMcpConfig.d.ts","sourceRoot":"","sources":["../../src/playwright/resolveMcpConfig.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAsCT"}
1
+ {"version":3,"file":"resolveMcpConfig.d.ts","sourceRoot":"","sources":["../../src/playwright/resolveMcpConfig.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,WAAW,cAAc;IAC7B;2EACuE;IACvE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IACrC,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IACf,6DAA6D;IAC7D,IAAI,EAAE,MAAM,CAAC;IACb;;;;2CAIuC;IACvC,KAAK,CAAC,EAAE,cAAc,EAAE,CAAC;IACzB;6EACyE;IACzE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,MAAM,CAgDT"}
@@ -3,26 +3,6 @@ import { mkdirSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, resolve } from 'node:path';
4
4
  import { tmpdir } from 'node:os';
5
5
  import process from 'node:process';
6
- /**
7
- * Resolve a ready-to-use MCP config file path that points at the local
8
- * `@playwright/mcp` package via an absolute Node-resolved path.
9
- *
10
- * Why this exists: Hover originally shipped a static `mcp.config.json`
11
- * with `"command": "npx", "args": ["-y", "@playwright/mcp@latest", …]`.
12
- * That meant every `claude -p` invocation kicked off a registry lookup
13
- * for `@latest` plus a tarball metadata round-trip before the MCP server
14
- * even started — adding 300 ms - 2 s of dead air to first-token latency
15
- * on every command (verified via `time npx -y @playwright/mcp@latest`).
16
- *
17
- * The fix is to (a) declare `@playwright/mcp` as a real dependency of
18
- * `@hover-dev/core` so npm resolves it locally at install time, and
19
- * (b) write a synthetic config file pointing `node <abs-path>/cli.js`
20
- * at the resolved location. No registry hit on the hot path.
21
- *
22
- * The config file is written to `<tmpdir>/hover/mcp-config-<port>.json`,
23
- * which lets multiple Hover services (one per example app) coexist
24
- * without stepping on each other's CDP endpoint.
25
- */
26
6
  export function resolveMcpConfig(opts) {
27
7
  // Resolve the package's main file, then walk back to its package root.
28
8
  // Using `package.json` as the resolution target is the documented
@@ -46,17 +26,27 @@ export function resolveMcpConfig(opts) {
46
26
  // pin to that file directly via Node so the user doesn't need the
47
27
  // bin shim on PATH and we skip yet another resolution layer.
48
28
  const cliPath = resolve(pkgRoot, 'cli.js');
49
- const config = {
50
- mcpServers: {
51
- playwright: {
52
- command: process.execPath, // current Node binary
53
- args: [cliPath, '--cdp-endpoint', opts.cdpUrl],
54
- },
29
+ const mcpServers = {
30
+ playwright: {
31
+ command: process.execPath, // current Node binary
32
+ args: [cliPath, '--cdp-endpoint', opts.cdpUrl],
55
33
  },
56
34
  };
35
+ for (const extra of opts.extra ?? []) {
36
+ // Claude sanitises the key for tool naming; we keep the raw id here
37
+ // because mcp-config consumers (claude / codex) accept arbitrary
38
+ // strings and do their own normalisation.
39
+ mcpServers[extra.id] = {
40
+ command: extra.command,
41
+ args: extra.args,
42
+ env: extra.env,
43
+ };
44
+ }
45
+ const config = { mcpServers };
57
46
  const outDir = resolve(tmpdir(), 'hover');
58
47
  mkdirSync(outDir, { recursive: true });
59
- const outPath = resolve(outDir, `mcp-config-${opts.port}.json`);
48
+ const suffix = opts.suffix ? `-${opts.suffix}` : '';
49
+ const outPath = resolve(outDir, `mcp-config-${opts.port}${suffix}.json`);
60
50
  writeFileSync(outPath, JSON.stringify(config, null, 2), 'utf-8');
61
51
  return outPath;
62
52
  }
@@ -0,0 +1,161 @@
1
+ /**
2
+ * Hover plugin API — the public contract third-party packages target.
3
+ *
4
+ * Plugins are *mostly declarative*: they ship a manifest describing what
5
+ * resources they contribute (a mode, MCP servers, Chrome flags, agent
6
+ * prompt fragments, widget event schemas). For genuinely time-bound work
7
+ * — booting a sidecar like mockttp when a mode activates, tearing it down
8
+ * when the mode deactivates or the service shuts down — they register
9
+ * namespaced lifecycle hooks.
10
+ *
11
+ * Patterned after Astro Integrations (declarative manifest + namespaced
12
+ * hooks: `astro:config:setup` etc). The `apiVersion` literal lets us
13
+ * evolve the manifest and reject mismatched plugins at load time with a
14
+ * clear error rather than silent breakage.
15
+ *
16
+ * Stability:
17
+ * - `apiVersion: 1` is what this file declares; breaking changes bump.
18
+ * - Adding new optional fields or new hook names is non-breaking.
19
+ * - Plugin authors should import only from this module; deep imports
20
+ * into `@hover-dev/core` internals are not part of the contract.
21
+ */
22
+ /**
23
+ * The Hover plugin API version this build of @hover-dev/core understands.
24
+ * Plugins declare which version they target via their manifest; mismatches
25
+ * are rejected at load time.
26
+ */
27
+ export type HoverApiVersion = 1;
28
+ export declare const CURRENT_API_VERSION: HoverApiVersion;
29
+ export interface HoverPluginMode {
30
+ /** Globally unique id (across all loaded plugins). Lowercase kebab. */
31
+ id: string;
32
+ /** Human-readable label shown in the widget mode-picker. */
33
+ label: string;
34
+ /** One-liner help text shown in the dropdown. */
35
+ description?: string;
36
+ /** Mode ids this mode cannot be active alongside. Two plugins both
37
+ * needing an exclusive proxy would set each other here. */
38
+ conflictsWith?: string[];
39
+ }
40
+ export interface HoverPluginMcpServer {
41
+ /** Stable, namespaced id (`@hover-dev/security:flows`). Host enforces
42
+ * uniqueness across all loaded plugins. */
43
+ id: string;
44
+ command: string;
45
+ args?: string[];
46
+ env?: Record<string, string>;
47
+ /** Modes in which this MCP is exposed to the agent. Default: only the
48
+ * plugin's own mode. Use `['*']` to mean "always on". */
49
+ activeInModes?: string[];
50
+ }
51
+ export interface HoverPluginChromeFlags {
52
+ /** Extra args appended to the Chrome launch argv. */
53
+ args?: string[];
54
+ /** Custom user-data-dir for this mode. Strongly recommended when proxy
55
+ * is set, so the secured profile doesn't share cookies with normal mode. */
56
+ userDataDir?: string;
57
+ /** Custom CDP port for this mode. Strongly recommended for the same
58
+ * reason — keeps the two modes' Chromes addressable independently. */
59
+ cdpPort?: number;
60
+ /** When present, Chrome is launched with --proxy-server + the
61
+ * --ignore-certificate-errors-spki-list pin so the proxy's MITM CA is
62
+ * accepted without polluting the OS trust store. */
63
+ proxy?: {
64
+ port: number;
65
+ spki: string;
66
+ };
67
+ /** Modes in which these flags apply. Default: only the plugin's own mode. */
68
+ activeInModes?: string[];
69
+ }
70
+ export interface HoverPluginSystemPromptAddition {
71
+ text: string;
72
+ /** Modes in which this paragraph is included in the agent's system
73
+ * prompt. Default: only the plugin's own mode. */
74
+ activeInModes?: string[];
75
+ }
76
+ export interface HoverBroadcast {
77
+ /** Push a JSON event to every WebSocket-connected widget. Event `type`
78
+ * should be namespaced by the plugin (`security:flow:added`). */
79
+ (event: {
80
+ type: string;
81
+ payload?: unknown;
82
+ }): void;
83
+ }
84
+ export interface HoverHookCtxBase {
85
+ /** Absolute path of the user's project root (Vite's `server.config.root`,
86
+ * Astro's project dir, etc.). Use this for persisting CA material, not
87
+ * process.cwd(). */
88
+ devRoot: string;
89
+ /** Push a custom event to every connected widget. */
90
+ broadcast: HoverBroadcast;
91
+ }
92
+ /** Fired when this plugin's mode becomes active. The plugin may boot
93
+ * sidecars (mockttp, profilers, …) here and return any settings that
94
+ * affect downstream subsystems (Chrome relaunch, MCP server env vars). */
95
+ export interface ModeActivateCtx extends HoverHookCtxBase {
96
+ modeId: string;
97
+ /** Tell the host "Chrome should be relaunched with these proxy settings"
98
+ * for the duration of this mode. Pass null to clear. */
99
+ setChromeProxy(proxy: {
100
+ port: number;
101
+ spki: string;
102
+ } | null): void;
103
+ /** Set additional env vars on one of this plugin's declared MCP servers.
104
+ * The MCP server isn't actually spawned until the agent runs a command,
105
+ * so plugins use this in activate() to pass runtime data (port numbers,
106
+ * auth tokens) that didn't exist at manifest-construction time.
107
+ * Merged on top of any env declared in the manifest; subsequent calls
108
+ * for the same id replace previous overrides. */
109
+ setMcpServerEnv(id: string, env: Record<string, string>): void;
110
+ }
111
+ /** Fired when this plugin's mode is being deactivated. The plugin
112
+ * MUST stop any sidecar it started in activate. */
113
+ export interface ModeDeactivateCtx extends HoverHookCtxBase {
114
+ modeId: string;
115
+ }
116
+ /** Fired exactly once when the host service is shutting down for any
117
+ * reason. Hooks must release subprocesses and file handles. */
118
+ export type ShutdownCtx = HoverHookCtxBase;
119
+ export interface HoverHooks {
120
+ 'hover:mode:activate'?: (ctx: ModeActivateCtx) => void | Promise<void>;
121
+ 'hover:mode:deactivate'?: (ctx: ModeDeactivateCtx) => void | Promise<void>;
122
+ 'hover:service:shutdown'?: (ctx: ShutdownCtx) => void | Promise<void>;
123
+ }
124
+ export interface HoverPluginManifest {
125
+ /** Always 1 in this build. Future versions may add 2, 3, … */
126
+ apiVersion: HoverApiVersion;
127
+ /** Globally unique plugin name. Use the npm package name. */
128
+ name: string;
129
+ /** Optional widget mode contributed by this plugin. */
130
+ mode?: HoverPluginMode;
131
+ /** Extra MCP servers exposed to the agent in the indicated modes. */
132
+ mcpServers?: HoverPluginMcpServer[];
133
+ /** Chrome launch overrides for the indicated modes. */
134
+ chromeFlags?: HoverPluginChromeFlags;
135
+ /** System-prompt paragraphs concatenated into the agent's prompt in
136
+ * the indicated modes. */
137
+ systemPromptAdditions?: HoverPluginSystemPromptAddition[];
138
+ /** Names of custom event types this plugin broadcasts. Documented
139
+ * here so the widget side can be tree-shaken to skip handlers for
140
+ * events that no loaded plugin will ever produce. */
141
+ widgetEventTypes?: string[];
142
+ hooks?: HoverHooks;
143
+ }
144
+ /**
145
+ * Branded factory that wraps a plugin manifest factory. The wrapper
146
+ * - asserts `apiVersion` matches this core's version at construction time
147
+ * (catches authors who copy-pasted from a tutorial for a different core),
148
+ * - returns a `(opts) => manifest` so call sites read `securityMode()` /
149
+ * `perfMode({ sampleHz: 100 })` uniformly.
150
+ *
151
+ * Use:
152
+ *
153
+ * export default defineHoverPlugin<MyOpts>((opts) => ({
154
+ * apiVersion: 1,
155
+ * name: '@hover-dev/security',
156
+ * mode: { id: 'security', label: 'Security testing' },
157
+ * ...
158
+ * }));
159
+ */
160
+ export declare function defineHoverPlugin<TOpts = void>(factory: (opts: TOpts) => HoverPluginManifest): (opts: TOpts) => HoverPluginManifest;
161
+ //# sourceMappingURL=plugin-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin-api.d.ts","sourceRoot":"","sources":["../src/plugin-api.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Hover plugin API — the public contract third-party packages target.
3
+ *
4
+ * Plugins are *mostly declarative*: they ship a manifest describing what
5
+ * resources they contribute (a mode, MCP servers, Chrome flags, agent
6
+ * prompt fragments, widget event schemas). For genuinely time-bound work
7
+ * — booting a sidecar like mockttp when a mode activates, tearing it down
8
+ * when the mode deactivates or the service shuts down — they register
9
+ * namespaced lifecycle hooks.
10
+ *
11
+ * Patterned after Astro Integrations (declarative manifest + namespaced
12
+ * hooks: `astro:config:setup` etc). The `apiVersion` literal lets us
13
+ * evolve the manifest and reject mismatched plugins at load time with a
14
+ * clear error rather than silent breakage.
15
+ *
16
+ * Stability:
17
+ * - `apiVersion: 1` is what this file declares; breaking changes bump.
18
+ * - Adding new optional fields or new hook names is non-breaking.
19
+ * - Plugin authors should import only from this module; deep imports
20
+ * into `@hover-dev/core` internals are not part of the contract.
21
+ */
22
+ export const CURRENT_API_VERSION = 1;
23
+ // ──────────────────────────────────────────────────────────────────────
24
+ // Author helper
25
+ // ──────────────────────────────────────────────────────────────────────
26
+ /**
27
+ * Branded factory that wraps a plugin manifest factory. The wrapper
28
+ * - asserts `apiVersion` matches this core's version at construction time
29
+ * (catches authors who copy-pasted from a tutorial for a different core),
30
+ * - returns a `(opts) => manifest` so call sites read `securityMode()` /
31
+ * `perfMode({ sampleHz: 100 })` uniformly.
32
+ *
33
+ * Use:
34
+ *
35
+ * export default defineHoverPlugin<MyOpts>((opts) => ({
36
+ * apiVersion: 1,
37
+ * name: '@hover-dev/security',
38
+ * mode: { id: 'security', label: 'Security testing' },
39
+ * ...
40
+ * }));
41
+ */
42
+ export function defineHoverPlugin(factory) {
43
+ return (opts) => {
44
+ const manifest = factory(opts);
45
+ if (manifest.apiVersion !== CURRENT_API_VERSION) {
46
+ throw new Error(`[hover] plugin "${manifest.name}" targets apiVersion ` +
47
+ `${String(manifest.apiVersion)} but this Hover supports ` +
48
+ `${CURRENT_API_VERSION}. Update either the plugin or @hover-dev/core.`);
49
+ }
50
+ return manifest;
51
+ };
52
+ }
@@ -11,7 +11,16 @@
11
11
  * file can be a thin orchestrator.
12
12
  */
13
13
  import type { WebSocket } from 'ws';
14
+ import { type LaunchOptions } from '../playwright/launchChrome.js';
14
15
  import { type ClientMessage } from './types.js';
16
+ /** Extra launch options surfaced from the active mode (security plugin
17
+ * needs proxy + spki + separate profile + non-default CDP port). When
18
+ * none are set, behaviour is identical to pre-v0.7 normal-mode launch. */
19
+ export type LaunchExtras = Pick<LaunchOptions, 'userDataDir' | 'proxy'> & {
20
+ /** Override CDP port (mode-specific, e.g. 9333 for security). When set,
21
+ * this also wins over the `port` parsed from cdpUrl. */
22
+ cdpPort?: number;
23
+ };
15
24
  /**
16
25
  * "Is this widget running inside the debug Chrome?" The widget asks this on
17
26
  * connect (and after every status-changing event) so it can render itself as
@@ -20,14 +29,14 @@ import { type ClientMessage } from './types.js';
20
29
  * - wrong-window → disabled, with a "use the other window" notice
21
30
  * - no-cdp → enabled but click triggers launch-chrome instead
22
31
  */
23
- export declare function handleCheckCdp(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
32
+ export declare function handleCheckCdp(ws: WebSocket, msg: ClientMessage, cdpUrl: string, extras?: LaunchExtras): Promise<void>;
24
33
  /**
25
34
  * Launch a debug Chrome navigated to `pageUrl`, then re-check status. The
26
35
  * re-check usually returns 'wrong-window' (because the widget asking is in
27
36
  * the user's regular Chrome, not the freshly-launched one) — the widget then
28
37
  * displays the "use the other window" state.
29
38
  */
30
- export declare function handleLaunchChrome(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
39
+ export declare function handleLaunchChrome(ws: WebSocket, msg: ClientMessage, cdpUrl: string, extras?: LaunchExtras): Promise<void>;
31
40
  /**
32
41
  * bringToFront the debug-Chrome tab matching `pageUrl`'s origin (or open one
33
42
  * if none exists). Used by the wrong-window UI's "switch to debug Chrome"
@@ -35,5 +44,5 @@ export declare function handleLaunchChrome(ws: WebSocket, msg: ClientMessage, cd
35
44
  * the widget cares about, and the widget the user is about to focus is a
36
45
  * different page (and will run its own check-cdp on its own ws connection).
37
46
  */
38
- export declare function handleFocusDebug(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
47
+ export declare function handleFocusDebug(ws: WebSocket, msg: ClientMessage, cdpUrl: string, extras?: LaunchExtras): Promise<void>;
39
48
  //# sourceMappingURL=cdpHandlers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cdpHandlers.d.ts","sourceRoot":"","sources":["../../src/service/cdpHandlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAGpC,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtD;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC,CAUf"}
1
+ {"version":3,"file":"cdpHandlers.d.ts","sourceRoot":"","sources":["../../src/service/cdpHandlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAEpC,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACtF,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtD;;2EAE2E;AAC3E,MAAM,MAAM,YAAY,GAAG,IAAI,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,GAAG;IACxE;6DACyD;IACzD,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAWf;AAED;;;;;GAKG;AACH,wBAAsB,kBAAkB,CACtC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAkCf;AAED;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CAaf"}
@@ -21,13 +21,16 @@ import { send } from './types.js';
21
21
  * - wrong-window → disabled, with a "use the other window" notice
22
22
  * - no-cdp → enabled but click triggers launch-chrome instead
23
23
  */
24
- export async function handleCheckCdp(ws, msg, cdpUrl) {
24
+ export async function handleCheckCdp(ws, msg, cdpUrl, extras) {
25
25
  const pageUrl = msg.payload?.pageUrl;
26
26
  if (typeof pageUrl !== 'string' || !pageUrl) {
27
27
  send(ws, { type: 'error', payload: { message: 'check-cdp: pageUrl is required' } });
28
28
  return;
29
29
  }
30
- const status = await checkCdpStatus(cdpUrl, pageUrl);
30
+ const effectiveCdpUrl = extras?.cdpPort
31
+ ? `http://localhost:${extras.cdpPort}`
32
+ : cdpUrl;
33
+ const status = await checkCdpStatus(effectiveCdpUrl, pageUrl);
31
34
  send(ws, { type: 'cdp-status', payload: status });
32
35
  }
33
36
  /**
@@ -36,7 +39,7 @@ export async function handleCheckCdp(ws, msg, cdpUrl) {
36
39
  * the user's regular Chrome, not the freshly-launched one) — the widget then
37
40
  * displays the "use the other window" state.
38
41
  */
39
- export async function handleLaunchChrome(ws, msg, cdpUrl) {
42
+ export async function handleLaunchChrome(ws, msg, cdpUrl, extras) {
40
43
  const pageUrl = msg.payload?.pageUrl;
41
44
  if (typeof pageUrl !== 'string' || !pageUrl) {
42
45
  send(ws, { type: 'error', payload: { message: 'launch-chrome: pageUrl is required' } });
@@ -45,7 +48,7 @@ export async function handleLaunchChrome(ws, msg, cdpUrl) {
45
48
  // Tell the widget we're launching so it can render a spinner immediately —
46
49
  // findChromeBinary + spawn + ready-poll can take a few seconds.
47
50
  send(ws, { type: 'cdp-status', payload: { state: 'no-cdp', launching: true } });
48
- const port = (() => {
51
+ const port = extras?.cdpPort ?? (() => {
49
52
  try {
50
53
  return Number(new URL(cdpUrl).port) || 9222;
51
54
  }
@@ -53,13 +56,22 @@ export async function handleLaunchChrome(ws, msg, cdpUrl) {
53
56
  return 9222;
54
57
  }
55
58
  })();
56
- const result = await launchDebugChrome({ url: pageUrl, port });
59
+ const result = await launchDebugChrome({
60
+ url: pageUrl,
61
+ port,
62
+ userDataDir: extras?.userDataDir,
63
+ proxy: extras?.proxy,
64
+ });
57
65
  if (!result.ok) {
58
66
  send(ws, { type: 'cdp-status', payload: { state: 'no-cdp', reason: result.reason } });
59
67
  return;
60
68
  }
61
- // Re-check after launch so the widget gets the real status.
62
- const status = await checkCdpStatus(cdpUrl, pageUrl);
69
+ // Re-check status against the port we actually launched on, so a mode-
70
+ // specific port (9333 for security) doesn't get probed at 9222.
71
+ const effectiveCdpUrl = extras?.cdpPort
72
+ ? `http://localhost:${extras.cdpPort}`
73
+ : cdpUrl;
74
+ const status = await checkCdpStatus(effectiveCdpUrl, pageUrl);
63
75
  send(ws, { type: 'cdp-status', payload: status });
64
76
  }
65
77
  /**
@@ -69,13 +81,16 @@ export async function handleLaunchChrome(ws, msg, cdpUrl) {
69
81
  * the widget cares about, and the widget the user is about to focus is a
70
82
  * different page (and will run its own check-cdp on its own ws connection).
71
83
  */
72
- export async function handleFocusDebug(ws, msg, cdpUrl) {
84
+ export async function handleFocusDebug(ws, msg, cdpUrl, extras) {
73
85
  const pageUrl = msg.payload?.pageUrl;
74
86
  if (typeof pageUrl !== 'string' || !pageUrl) {
75
87
  send(ws, { type: 'error', payload: { message: 'focus-debug: pageUrl is required' } });
76
88
  return;
77
89
  }
78
- const result = await focusDebugTab(cdpUrl, pageUrl);
90
+ const effectiveCdpUrl = extras?.cdpPort
91
+ ? `http://localhost:${extras.cdpPort}`
92
+ : cdpUrl;
93
+ const result = await focusDebugTab(effectiveCdpUrl, pageUrl);
79
94
  if (!result.ok) {
80
95
  send(ws, { type: 'error', payload: { message: `focus-debug: ${result.reason}` } });
81
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"saveHandlers.d.ts","sourceRoot":"","sources":["../../src/service/saveHandlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,UAAU,EAAgC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnG,OAAO,EAAE,SAAS,EAAmB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EAAE,YAAY,EAAsB,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtD,UAAU,kBAAkB,CAAC,YAAY,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;IAC9E,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB;wDACoD;IACpD,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,0EAA0E;IAC1E,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IAC9E,wEAAwE;IACxE,KAAK,EAAE,CAAC,IAAI,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,SAAS,EAAE,CAAC;QACnB,UAAU,EAAE,aAAa,EAAE,CAAC;QAC5B,OAAO,EAAE,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/C,SAAS,EAAE,OAAO,CAAC;KACpB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7B;AAED,wBAAsB,kBAAkB,CAAC,YAAY,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC1F,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,kBAAkB,CAAC,YAAY,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED,eAAO,MAAM,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAanF,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAOjF,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAYxF,CAAC"}
1
+ {"version":3,"file":"saveHandlers.d.ts","sourceRoot":"","sources":["../../src/service/saveHandlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,UAAU,EAAgC,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnG,OAAO,EAAE,SAAS,EAAmB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACvF,OAAO,EAAE,YAAY,EAAsB,MAAM,0BAA0B,CAAC;AAC5E,OAAO,EAAQ,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAEtD,UAAU,kBAAkB,CAAC,YAAY,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE;IAC9E,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB,0BAA0B;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IACnB;wDACoD;IACpD,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5D,0EAA0E;IAC1E,WAAW,EAAE,KAAK,GAAG,IAAI,EAAE,KAAK,EAAE,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,KAAK,CAAC;IAC9E,wEAAwE;IACxE,KAAK,EAAE,CAAC,IAAI,EAAE;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;QACpB,KAAK,EAAE,SAAS,EAAE,CAAC;QACnB,UAAU,EAAE,aAAa,EAAE,CAAC;QAC5B,OAAO,EAAE,WAAW,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC;QAC/C,SAAS,EAAE,OAAO,CAAC;KACpB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC7B;AAED,wBAAsB,kBAAkB,CAAC,YAAY,SAAS;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,EAC1F,EAAE,EAAE,SAAS,EACb,GAAG,EAAE,aAAa,EAClB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,kBAAkB,CAAC,YAAY,CAAC,GACpC,OAAO,CAAC,IAAI,CAAC,CA0Cf;AAED,eAAO,MAAM,YAAY,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,UAAU,CAAC,CAAC,CAanF,CAAC;AAEF,eAAO,MAAM,WAAW,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,SAAS,CAAC,CAAC,CAOjF,CAAC;AAEF,eAAO,MAAM,eAAe,EAAE,kBAAkB,CAAC,OAAO,CAAC,UAAU,CAAC,OAAO,YAAY,CAAC,CAAC,CAYxF,CAAC"}