@hover-dev/core 0.16.0 → 0.18.0
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 +26 -55
- package/dist/agentDirectives.d.ts +55 -0
- package/dist/agentDirectives.d.ts.map +1 -0
- package/dist/agentDirectives.js +276 -0
- package/dist/engine.d.ts +28 -0
- package/dist/engine.d.ts.map +1 -0
- package/dist/engine.js +27 -0
- package/dist/memory/businessMemory.d.ts +29 -0
- package/dist/memory/businessMemory.d.ts.map +1 -0
- package/dist/memory/businessMemory.js +125 -0
- package/dist/playwright/launchChrome.d.ts +18 -0
- package/dist/playwright/launchChrome.d.ts.map +1 -1
- package/dist/playwright/launchChrome.js +46 -3
- package/dist/qa/candidates.d.ts +32 -0
- package/dist/qa/candidates.d.ts.map +1 -0
- package/dist/qa/candidates.js +20 -0
- package/dist/qa/intensity.d.ts +33 -0
- package/dist/qa/intensity.d.ts.map +1 -0
- package/dist/qa/intensity.js +25 -0
- package/dist/qa/qaReport.d.ts +19 -0
- package/dist/qa/qaReport.d.ts.map +1 -0
- package/dist/qa/qaReport.js +50 -0
- package/dist/sessions/sessions.d.ts +125 -0
- package/dist/sessions/sessions.d.ts.map +1 -0
- package/dist/sessions/sessions.js +175 -0
- package/dist/specs/authFixture.d.ts +30 -0
- package/dist/specs/authFixture.d.ts.map +1 -0
- package/dist/specs/authFixture.js +145 -0
- package/dist/specs/detectSharedFlows.d.ts +1 -1
- package/dist/specs/detectSharedFlows.d.ts.map +1 -1
- package/dist/specs/detectSharedFlows.js +20 -21
- package/dist/specs/generatePageObject.d.ts +1 -1
- package/dist/specs/generatePageObject.d.ts.map +1 -1
- package/dist/specs/healPrompt.d.ts +19 -0
- package/dist/specs/healPrompt.d.ts.map +1 -0
- package/dist/specs/healPrompt.js +48 -0
- package/dist/specs/humanSteps.d.ts +4 -8
- package/dist/specs/humanSteps.d.ts.map +1 -1
- package/dist/specs/humanSteps.js +6 -1
- package/dist/specs/optimizeSpec.d.ts +15 -8
- package/dist/specs/optimizeSpec.d.ts.map +1 -1
- package/dist/specs/optimizeSpec.js +71 -41
- package/dist/specs/pageObjectManifest.d.ts +3 -1
- package/dist/specs/pageObjectManifest.d.ts.map +1 -1
- package/dist/specs/pageObjectManifest.js +24 -19
- package/dist/specs/replayGrounded.d.ts +45 -0
- package/dist/specs/replayGrounded.d.ts.map +1 -0
- package/dist/specs/replayGrounded.js +155 -0
- package/dist/specs/runFailures.d.ts +34 -0
- package/dist/specs/runFailures.d.ts.map +1 -0
- package/dist/specs/runFailures.js +93 -0
- package/dist/specs/seeds.d.ts +16 -15
- package/dist/specs/seeds.d.ts.map +1 -1
- package/dist/specs/seeds.js +86 -54
- package/dist/specs/sidecar.d.ts +34 -6
- package/dist/specs/sidecar.d.ts.map +1 -1
- package/dist/specs/sidecar.js +79 -9
- package/dist/specs/specStep.d.ts +21 -0
- package/dist/specs/specStep.d.ts.map +1 -0
- package/dist/specs/specStep.js +1 -0
- package/dist/specs/text.d.ts +8 -6
- package/dist/specs/text.d.ts.map +1 -1
- package/dist/specs/text.js +10 -7
- package/dist/specs/writeSpec.d.ts +62 -1
- package/dist/specs/writeSpec.d.ts.map +1 -1
- package/dist/specs/writeSpec.js +596 -21
- package/package.json +9 -29
- package/dist/agents/aider.d.ts +0 -16
- package/dist/agents/aider.d.ts.map +0 -1
- package/dist/agents/aider.js +0 -161
- package/dist/agents/argv.d.ts +0 -11
- package/dist/agents/argv.d.ts.map +0 -1
- package/dist/agents/argv.js +0 -23
- package/dist/agents/claude.d.ts +0 -3
- package/dist/agents/claude.d.ts.map +0 -1
- package/dist/agents/claude.js +0 -195
- package/dist/agents/codex.d.ts +0 -19
- package/dist/agents/codex.d.ts.map +0 -1
- package/dist/agents/codex.js +0 -216
- package/dist/agents/cursor.d.ts +0 -18
- package/dist/agents/cursor.d.ts.map +0 -1
- package/dist/agents/cursor.js +0 -220
- package/dist/agents/detect.d.ts +0 -46
- package/dist/agents/detect.d.ts.map +0 -1
- package/dist/agents/detect.js +0 -80
- package/dist/agents/gemini.d.ts +0 -17
- package/dist/agents/gemini.d.ts.map +0 -1
- package/dist/agents/gemini.js +0 -186
- package/dist/agents/index.d.ts +0 -6
- package/dist/agents/index.d.ts.map +0 -1
- package/dist/agents/index.js +0 -5
- package/dist/agents/invoke.d.ts +0 -12
- package/dist/agents/invoke.d.ts.map +0 -1
- package/dist/agents/invoke.js +0 -96
- package/dist/agents/qwen.d.ts +0 -17
- package/dist/agents/qwen.d.ts.map +0 -1
- package/dist/agents/qwen.js +0 -172
- package/dist/agents/registry.d.ts +0 -19
- package/dist/agents/registry.d.ts.map +0 -1
- package/dist/agents/registry.js +0 -34
- package/dist/agents/shared.d.ts +0 -28
- package/dist/agents/shared.d.ts.map +0 -1
- package/dist/agents/shared.js +0 -35
- package/dist/agents/types.d.ts +0 -186
- package/dist/agents/types.d.ts.map +0 -1
- package/dist/agents/types.js +0 -23
- package/dist/index.d.ts +0 -3
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -2
- package/dist/mcp/sourceFence.d.ts +0 -23
- package/dist/mcp/sourceFence.d.ts.map +0 -1
- package/dist/mcp/sourceFence.js +0 -75
- package/dist/mcp/sourceServer.d.ts +0 -3
- package/dist/mcp/sourceServer.d.ts.map +0 -1
- package/dist/mcp/sourceServer.js +0 -116
- package/dist/playwright/cdpStatus.d.ts +0 -29
- package/dist/playwright/cdpStatus.d.ts.map +0 -1
- package/dist/playwright/cdpStatus.js +0 -119
- package/dist/playwright/preflight.d.ts +0 -31
- package/dist/playwright/preflight.d.ts.map +0 -1
- package/dist/playwright/preflight.js +0 -82
- package/dist/playwright/preflightCache.d.ts +0 -27
- package/dist/playwright/preflightCache.d.ts.map +0 -1
- package/dist/playwright/preflightCache.js +0 -21
- package/dist/playwright/raiseWindow.d.ts +0 -10
- package/dist/playwright/raiseWindow.d.ts.map +0 -1
- package/dist/playwright/raiseWindow.js +0 -158
- package/dist/playwright/resolveMcpConfig.d.ts +0 -55
- package/dist/playwright/resolveMcpConfig.d.ts.map +0 -1
- package/dist/playwright/resolveMcpConfig.js +0 -66
- package/dist/plugin-api.d.ts +0 -235
- package/dist/plugin-api.d.ts.map +0 -1
- package/dist/plugin-api.js +0 -52
- package/dist/runSession.d.ts +0 -42
- package/dist/runSession.d.ts.map +0 -1
- package/dist/runSession.js +0 -81
- package/dist/scripts/bench-multi-tab.d.ts +0 -2
- package/dist/scripts/bench-multi-tab.d.ts.map +0 -1
- package/dist/scripts/bench-multi-tab.js +0 -192
- package/dist/scripts/bench-ttfb.d.ts +0 -2
- package/dist/scripts/bench-ttfb.d.ts.map +0 -1
- package/dist/scripts/bench-ttfb.js +0 -127
- package/dist/scripts/start-chrome.d.ts +0 -3
- package/dist/scripts/start-chrome.d.ts.map +0 -1
- package/dist/scripts/start-chrome.js +0 -23
- package/dist/service/cdpHandlers.d.ts +0 -44
- package/dist/service/cdpHandlers.d.ts.map +0 -1
- package/dist/service/cdpHandlers.js +0 -85
- package/dist/service/cdpHint.d.ts +0 -48
- package/dist/service/cdpHint.d.ts.map +0 -1
- package/dist/service/cdpHint.js +0 -216
- package/dist/service/conventions.d.ts +0 -8
- package/dist/service/conventions.d.ts.map +0 -1
- package/dist/service/conventions.js +0 -42
- package/dist/service/saveHandlers.d.ts +0 -52
- package/dist/service/saveHandlers.d.ts.map +0 -1
- package/dist/service/saveHandlers.js +0 -75
- package/dist/service/types.d.ts +0 -58
- package/dist/service/types.d.ts.map +0 -1
- package/dist/service/types.js +0 -26
- package/dist/service.d.ts +0 -50
- package/dist/service.d.ts.map +0 -1
- package/dist/service.js +0 -1065
- package/dist/skills/writeSkill.d.ts +0 -27
- package/dist/skills/writeSkill.d.ts.map +0 -1
- package/dist/skills/writeSkill.js +0 -13
- package/dist/specs/extractPageObjects.d.ts +0 -18
- package/dist/specs/extractPageObjects.d.ts.map +0 -1
- package/dist/specs/extractPageObjects.js +0 -98
- package/dist/specs/listSpecs.d.ts +0 -52
- package/dist/specs/listSpecs.d.ts.map +0 -1
- package/dist/specs/listSpecs.js +0 -139
- package/dist/specs/optimizationSuggestion.d.ts +0 -26
- package/dist/specs/optimizationSuggestion.d.ts.map +0 -1
- package/dist/specs/optimizationSuggestion.js +0 -28
- package/dist/specs/optimizeSpecWithAgent.d.ts +0 -11
- package/dist/specs/optimizeSpecWithAgent.d.ts.map +0 -1
- package/dist/specs/optimizeSpecWithAgent.js +0 -40
- package/dist/specs/writeCaseCsv.d.ts +0 -28
- package/dist/specs/writeCaseCsv.d.ts.map +0 -1
- package/dist/specs/writeCaseCsv.js +0 -134
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* CDP-related WebSocket message handlers.
|
|
3
|
-
*
|
|
4
|
-
* check-cdp → checkCdpStatus → emit cdp-status
|
|
5
|
-
* launch-chrome → emit "launching" placeholder → launchDebugChrome →
|
|
6
|
-
* re-check status → emit cdp-status
|
|
7
|
-
* focus-debug → focusDebugTab → no message on success (the widget the
|
|
8
|
-
* user is about to focus runs its own check-cdp anyway)
|
|
9
|
-
*
|
|
10
|
-
* Extracted from service.ts during the v0.2.x refactor pass so the main
|
|
11
|
-
* file can be a thin orchestrator.
|
|
12
|
-
*/
|
|
13
|
-
import { checkCdpStatus, focusDebugTab } from '../playwright/cdpStatus.js';
|
|
14
|
-
import { launchDebugChrome } from '../playwright/launchChrome.js';
|
|
15
|
-
import { send } from './types.js';
|
|
16
|
-
/**
|
|
17
|
-
* "Is this widget running inside the debug Chrome?" The widget asks this on
|
|
18
|
-
* connect (and after every status-changing event) so it can render itself as
|
|
19
|
-
* either:
|
|
20
|
-
* - same-window → normal, drives the page
|
|
21
|
-
* - wrong-window → disabled, with a "use the other window" notice
|
|
22
|
-
* - no-cdp → enabled but click triggers launch-chrome instead
|
|
23
|
-
*/
|
|
24
|
-
export async function handleCheckCdp(ws, msg, cdpUrl, extras) {
|
|
25
|
-
const pageUrl = msg.payload?.pageUrl;
|
|
26
|
-
if (typeof pageUrl !== 'string' || !pageUrl) {
|
|
27
|
-
send(ws, { type: 'error', payload: { message: 'check-cdp: pageUrl is required' } });
|
|
28
|
-
return;
|
|
29
|
-
}
|
|
30
|
-
const status = await checkCdpStatus(cdpUrl, pageUrl);
|
|
31
|
-
send(ws, { type: 'cdp-status', payload: status });
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Launch a debug Chrome navigated to `pageUrl`, then re-check status. The
|
|
35
|
-
* re-check usually returns 'wrong-window' (because the widget asking is in
|
|
36
|
-
* the user's regular Chrome, not the freshly-launched one) — the widget then
|
|
37
|
-
* displays the "use the other window" state.
|
|
38
|
-
*/
|
|
39
|
-
export async function handleLaunchChrome(ws, msg, cdpUrl, extras) {
|
|
40
|
-
const pageUrl = msg.payload?.pageUrl;
|
|
41
|
-
if (typeof pageUrl !== 'string' || !pageUrl) {
|
|
42
|
-
send(ws, { type: 'error', payload: { message: 'launch-chrome: pageUrl is required' } });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
// Tell the widget we're launching so it can render a spinner immediately —
|
|
46
|
-
// findChromeBinary + spawn + ready-poll can take a few seconds.
|
|
47
|
-
send(ws, { type: 'cdp-status', payload: { state: 'no-cdp', launching: true } });
|
|
48
|
-
const port = (() => {
|
|
49
|
-
try {
|
|
50
|
-
return Number(new URL(cdpUrl).port) || 9222;
|
|
51
|
-
}
|
|
52
|
-
catch {
|
|
53
|
-
return 9222;
|
|
54
|
-
}
|
|
55
|
-
})();
|
|
56
|
-
const result = await launchDebugChrome({
|
|
57
|
-
url: pageUrl,
|
|
58
|
-
port,
|
|
59
|
-
proxy: extras?.proxy,
|
|
60
|
-
});
|
|
61
|
-
if (!result.ok) {
|
|
62
|
-
send(ws, { type: 'cdp-status', payload: { state: 'no-cdp', reason: result.reason } });
|
|
63
|
-
return;
|
|
64
|
-
}
|
|
65
|
-
const status = await checkCdpStatus(cdpUrl, pageUrl);
|
|
66
|
-
send(ws, { type: 'cdp-status', payload: status });
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* bringToFront the debug-Chrome tab matching `pageUrl`'s origin (or open one
|
|
70
|
-
* if none exists). Used by the wrong-window UI's "switch to debug Chrome"
|
|
71
|
-
* button. Doesn't return cdp-status — bringToFront doesn't change anything
|
|
72
|
-
* the widget cares about, and the widget the user is about to focus is a
|
|
73
|
-
* different page (and will run its own check-cdp on its own ws connection).
|
|
74
|
-
*/
|
|
75
|
-
export async function handleFocusDebug(ws, msg, cdpUrl, extras) {
|
|
76
|
-
const pageUrl = msg.payload?.pageUrl;
|
|
77
|
-
if (typeof pageUrl !== 'string' || !pageUrl) {
|
|
78
|
-
send(ws, { type: 'error', payload: { message: 'focus-debug: pageUrl is required' } });
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const result = await focusDebugTab(cdpUrl, pageUrl);
|
|
82
|
-
if (!result.ok) {
|
|
83
|
-
send(ws, { type: 'error', payload: { message: `focus-debug: ${result.reason}` } });
|
|
84
|
-
}
|
|
85
|
-
}
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* System-prompt addendum sent to the agent on every command.
|
|
3
|
-
*
|
|
4
|
-
* Two roles:
|
|
5
|
-
* 1. Navigation rules — the most failure-prone agent behaviours are
|
|
6
|
-
* `browser_navigate` to same-origin paths (kills the widget) and
|
|
7
|
-
* reading the JS bundle for credentials. We tell the agent both
|
|
8
|
-
* mistakes by name, including the actual origin to forbid.
|
|
9
|
-
* 2. Narration format — how the widget renders the run depends on the
|
|
10
|
-
* agent emitting short imperative one-liners before each logical
|
|
11
|
-
* step. The good/bad examples are present-tense and 3–8 words.
|
|
12
|
-
*
|
|
13
|
-
* Lives in its own file because this string is the most-tuned text in the
|
|
14
|
-
* repo and the easiest to break with a typo. Tests can import directly.
|
|
15
|
-
*
|
|
16
|
-
* Two-tier split (since v0.4.x perf pass):
|
|
17
|
-
* - `buildCdpHint(tabs)` returns the full rules + narration block.
|
|
18
|
-
* Used on the *first* turn of a session (no `--resume`).
|
|
19
|
-
* - `buildCdpHintResume(tabs)` returns ONLY the volatile tab list +
|
|
20
|
-
* active-origin guard. Used on subsequent turns once `--resume`
|
|
21
|
-
* re-anchors the agent to the prior turn's full system prompt —
|
|
22
|
-
* the stable rules are already in context, so re-sending them
|
|
23
|
-
* fragments Anthropic's prompt cache and bills ~500 extra input
|
|
24
|
-
* tokens per turn for zero behavioural change.
|
|
25
|
-
*/
|
|
26
|
-
interface Tab {
|
|
27
|
-
url: string;
|
|
28
|
-
title?: string;
|
|
29
|
-
}
|
|
30
|
-
export declare function buildCdpHint(tabs: Tab[]): string;
|
|
31
|
-
/**
|
|
32
|
-
* Volatile-only hint for `--resume` turns: just the tab list snapshot.
|
|
33
|
-
* Empty string when the tab list is empty (nothing to refresh).
|
|
34
|
-
*
|
|
35
|
-
* The rules and narration format from `buildCdpHint` are already
|
|
36
|
-
* established in the prior turn's context; re-sending them here would
|
|
37
|
-
* fragment Anthropic's prompt-cache fingerprint (cache hits require the
|
|
38
|
-
* system prompt to match byte-for-byte across turns) and bill ~500
|
|
39
|
-
* extra input tokens per follow-up turn for no behaviour change.
|
|
40
|
-
*
|
|
41
|
-
* We DO re-send the tab list because it can drift between turns (user
|
|
42
|
-
* opens a second tab, switches focus). The active-origin nav-guard is
|
|
43
|
-
* not repeated — the agent has it from turn 1 and the tab-list update
|
|
44
|
-
* keeps it grounded in the current URL.
|
|
45
|
-
*/
|
|
46
|
-
export declare function buildCdpHintResume(tabs: Tab[]): string;
|
|
47
|
-
export {};
|
|
48
|
-
//# sourceMappingURL=cdpHint.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cdpHint.d.ts","sourceRoot":"","sources":["../../src/service/cdpHint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,UAAU,GAAG;IAAG,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE;AAa7C,wBAAgB,YAAY,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAmJhD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,MAAM,CAYtD"}
|
package/dist/service/cdpHint.js
DELETED
|
@@ -1,216 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* System-prompt addendum sent to the agent on every command.
|
|
3
|
-
*
|
|
4
|
-
* Two roles:
|
|
5
|
-
* 1. Navigation rules — the most failure-prone agent behaviours are
|
|
6
|
-
* `browser_navigate` to same-origin paths (kills the widget) and
|
|
7
|
-
* reading the JS bundle for credentials. We tell the agent both
|
|
8
|
-
* mistakes by name, including the actual origin to forbid.
|
|
9
|
-
* 2. Narration format — how the widget renders the run depends on the
|
|
10
|
-
* agent emitting short imperative one-liners before each logical
|
|
11
|
-
* step. The good/bad examples are present-tense and 3–8 words.
|
|
12
|
-
*
|
|
13
|
-
* Lives in its own file because this string is the most-tuned text in the
|
|
14
|
-
* repo and the easiest to break with a typo. Tests can import directly.
|
|
15
|
-
*
|
|
16
|
-
* Two-tier split (since v0.4.x perf pass):
|
|
17
|
-
* - `buildCdpHint(tabs)` returns the full rules + narration block.
|
|
18
|
-
* Used on the *first* turn of a session (no `--resume`).
|
|
19
|
-
* - `buildCdpHintResume(tabs)` returns ONLY the volatile tab list +
|
|
20
|
-
* active-origin guard. Used on subsequent turns once `--resume`
|
|
21
|
-
* re-anchors the agent to the prior turn's full system prompt —
|
|
22
|
-
* the stable rules are already in context, so re-sending them
|
|
23
|
-
* fragments Anthropic's prompt cache and bills ~500 extra input
|
|
24
|
-
* tokens per turn for zero behavioural change.
|
|
25
|
-
*/
|
|
26
|
-
function resolveActiveOrigin(tabs) {
|
|
27
|
-
if (tabs.length === 0)
|
|
28
|
-
return null;
|
|
29
|
-
// Prefer the localhost tab if we have multiple — that's almost always the
|
|
30
|
-
// dev server the user is testing against.
|
|
31
|
-
const localhost = tabs.find(t => /localhost|127\.0\.0\.1/.test(t.url));
|
|
32
|
-
const active = localhost ?? tabs[0];
|
|
33
|
-
let activeOrigin = '';
|
|
34
|
-
try {
|
|
35
|
-
activeOrigin = new URL(active.url).origin;
|
|
36
|
-
}
|
|
37
|
-
catch { /* malformed url — fall back to no-origin guard */ }
|
|
38
|
-
return { active, activeOrigin };
|
|
39
|
-
}
|
|
40
|
-
export function buildCdpHint(tabs) {
|
|
41
|
-
const resolved = resolveActiveOrigin(tabs);
|
|
42
|
-
if (!resolved)
|
|
43
|
-
return '';
|
|
44
|
-
const { active, activeOrigin } = resolved;
|
|
45
|
-
return [
|
|
46
|
-
`Your job — read this first:`,
|
|
47
|
-
``,
|
|
48
|
-
` You are an end-to-end testing agent. Match the scope of your run to how`,
|
|
49
|
-
` specific the user's prompt is — do NOT over-test.`,
|
|
50
|
-
``,
|
|
51
|
-
` SPECIFIC prompt — it names a flow or action ("log in as alice and add a`,
|
|
52
|
-
` todo", "test the login flow", "只测试登录"): do EXACTLY that flow and`,
|
|
53
|
-
` verify its outcome, then STOP. Stay inside the named scope. Do NOT wander`,
|
|
54
|
-
` into adjacent flows, extra edge cases (empty/invalid input, boundary`,
|
|
55
|
-
` values), logout, or bug-hunting unless the prompt explicitly asks. A`,
|
|
56
|
-
` focused run that does what was asked and asserts the result is the goal,`,
|
|
57
|
-
` not breadth — one clean verified flow is a complete, successful result.`,
|
|
58
|
-
` But if you DO hit a real problem while doing the asked flow — a broken`,
|
|
59
|
-
` button, a wrong message, a console error, a failed verification — still`,
|
|
60
|
-
` report it under ## Findings. Don't go hunting for more; just don't swallow`,
|
|
61
|
-
` what you ran into.`,
|
|
62
|
-
``,
|
|
63
|
-
` VAGUE or short prompt ("test", "check", "see if it works", "find bugs",`,
|
|
64
|
-
` or a single word): DO NOT ask for clarification and DO NOT just take a`,
|
|
65
|
-
` snapshot and call it done. Run a real exploratory test pass:`,
|
|
66
|
-
``,
|
|
67
|
-
` 1. browser_snapshot to learn the app's structure.`,
|
|
68
|
-
` 2. Identify the main interactive surfaces (forms, buttons, links,`,
|
|
69
|
-
` inputs, navigation). Plan 2–5 distinct user flows to exercise.`,
|
|
70
|
-
` 3. Drive each flow end-to-end. Submit forms with real-ish input,`,
|
|
71
|
-
` click through navigation, exercise lists / counters / toggles.`,
|
|
72
|
-
` Try a couple of edge cases — empty submissions, invalid input,`,
|
|
73
|
-
` boundary values — and observe the response.`,
|
|
74
|
-
` 4. Note anything that looks broken, inconsistent, slow, or`,
|
|
75
|
-
` confusing in the final summary's "## Findings" section.`,
|
|
76
|
-
``,
|
|
77
|
-
` A short "App is running fine" reply after one snapshot is NOT an`,
|
|
78
|
-
` acceptable result for a vague prompt — either the app works and you ran`,
|
|
79
|
-
` several flows to confirm it, or you found something interesting.`,
|
|
80
|
-
``,
|
|
81
|
-
`The user's Chrome currently has these tabs open:`,
|
|
82
|
-
...tabs.map(t => ` - ${t.url}${t.title ? ` (${t.title})` : ''}`),
|
|
83
|
-
``,
|
|
84
|
-
`The likely active dev tab is: ${active.url}`,
|
|
85
|
-
``,
|
|
86
|
-
`Navigation rules — read carefully, these mistakes are the #1 cause of failed`,
|
|
87
|
-
`runs:`,
|
|
88
|
-
``,
|
|
89
|
-
` 1. Do NOT call browser_navigate to a URL that is already the active tab.`,
|
|
90
|
-
` The widget that hosts this session lives inside the page; reloading the`,
|
|
91
|
-
` page kills the WebSocket connection and your run gets aborted mid-flight.`,
|
|
92
|
-
``,
|
|
93
|
-
activeOrigin
|
|
94
|
-
? ` 2. Do NOT call browser_navigate to ANY path on origin ${activeOrigin}`
|
|
95
|
-
: ` 2. Do NOT call browser_navigate to source-file paths on the dev server`,
|
|
96
|
-
` just to "read source code for hints" — paths like /src/Login.tsx,`,
|
|
97
|
-
` /@vite/client, /node_modules/* are served by Vite as JS modules and`,
|
|
98
|
-
` loading them triggers the same widget-killing reload. To inspect the`,
|
|
99
|
-
` page, use browser_snapshot — the accessibility tree already exposes`,
|
|
100
|
-
` labels, placeholders, and roles.`,
|
|
101
|
-
``,
|
|
102
|
-
` 3. Do NOT read the JS bundle, evaluate page source, or scrape DOM for`,
|
|
103
|
-
` hardcoded credentials, API keys, or secrets. If the task needs login,`,
|
|
104
|
-
` the user must provide credentials in their prompt; if they didn't,`,
|
|
105
|
-
` report "no credentials provided" and stop — do not guess.`,
|
|
106
|
-
``,
|
|
107
|
-
` 4. To see the current page state, call browser_snapshot first. Only`,
|
|
108
|
-
` navigate if you actually need a different URL.`,
|
|
109
|
-
``,
|
|
110
|
-
`Multi-tab + cross-origin flows (Stripe Checkout, OAuth login, "Pay with X" popups):`,
|
|
111
|
-
``,
|
|
112
|
-
` 5. When you click something that may open a new tab (target=_blank, a`,
|
|
113
|
-
` window.open trigger, a "Pay with …" / "Sign in with …" button), the`,
|
|
114
|
-
` popup tab is where the next user-visible step happens — but your tools`,
|
|
115
|
-
` stay anchored to the prior tab until you switch. After such a click:`,
|
|
116
|
-
``,
|
|
117
|
-
` a) Call browser_tabs(action='list') to see if a new tab appeared.`,
|
|
118
|
-
` A new entry at a different origin is the popup.`,
|
|
119
|
-
` b) Call browser_tabs(action='select', idx=<popup idx>) to focus it,`,
|
|
120
|
-
` then browser_snapshot the new tab and proceed.`,
|
|
121
|
-
` c) When the popup closes (it usually does so on success/cancel —`,
|
|
122
|
-
` window.close() or after a redirect chain), browser_tabs(list)`,
|
|
123
|
-
` will no longer show it. The current page may be invalid; call`,
|
|
124
|
-
` browser_tabs(action='select', idx=0) to refocus the original tab,`,
|
|
125
|
-
` then browser_snapshot it. The original tab's DOM may have updated`,
|
|
126
|
-
` via a postMessage handler (e.g. it should now show a "Success" or`,
|
|
127
|
-
` "Payment complete" state).`,
|
|
128
|
-
` d) If the original tab's snapshot looks unchanged (still showing the`,
|
|
129
|
-
` checkout form / login button), the postMessage handler may not`,
|
|
130
|
-
` have fired yet or may not exist. Wait once with`,
|
|
131
|
-
` browser_wait_for_text("<expected success copy>", timeout=3000)`,
|
|
132
|
-
` before concluding the flow is broken.`,
|
|
133
|
-
``,
|
|
134
|
-
` 6. OAuth-style redirect chains: when a tab redirects through several`,
|
|
135
|
-
` origins (myapp → identity provider → /callback?code=… → myapp), watch`,
|
|
136
|
-
` browser_tabs after each browser_snapshot — the same tab idx can switch`,
|
|
137
|
-
` origin underneath you. The URL in browser_tabs(list) is authoritative.`,
|
|
138
|
-
``,
|
|
139
|
-
` 7. Cross-origin cookie/session updates: after the popup closes and you're`,
|
|
140
|
-
` back on the original tab, the server-set session cookie may be present`,
|
|
141
|
-
` in the browser but the React state hasn't yet picked it up. The most`,
|
|
142
|
-
` likely cause is a missing or slow postMessage handler — NOT a real`,
|
|
143
|
-
` bug yet. Try browser_wait_for_text once for the expected logged-in`,
|
|
144
|
-
` copy with a 3s timeout. If nothing shows, report it as a Finding`,
|
|
145
|
-
` ("Original tab did not update after popup closed — likely missing`,
|
|
146
|
-
` postMessage listener or auth refresh"); do NOT browser_navigate to`,
|
|
147
|
-
` same-origin to force a refresh (rule #2 still applies).`,
|
|
148
|
-
``,
|
|
149
|
-
`Tool usage — operate and verify through the structured Playwright tools:`,
|
|
150
|
-
``,
|
|
151
|
-
` 8. Drive the page only with click / fill / select / snapshot / wait. Do`,
|
|
152
|
-
` NOT use browser_run_code_unsafe or browser_evaluate to run JavaScript`,
|
|
153
|
-
` — they are disabled, and any action taken in raw JS cannot be`,
|
|
154
|
-
` crystallized into a deterministic Playwright spec (it is dropped as a`,
|
|
155
|
-
` TODO). To VERIFY an outcome, assert on what browser_snapshot shows —`,
|
|
156
|
-
` a heading, an error message, a counter value; the accessibility tree`,
|
|
157
|
-
` already exposes the text and roles you need.`,
|
|
158
|
-
``,
|
|
159
|
-
`Narration format — affects how the widget renders your run for the user:`,
|
|
160
|
-
``,
|
|
161
|
-
` Before each LOGICAL STEP (a coherent unit of work like "Open the login`,
|
|
162
|
-
` form", "Fill credentials", "Verify the welcome message"), emit ONE short`,
|
|
163
|
-
` imperative sentence describing what you're about to do — present tense,`,
|
|
164
|
-
` 3–8 words, no markdown. The widget uses that sentence as the step's title.`,
|
|
165
|
-
``,
|
|
166
|
-
` Good examples:`,
|
|
167
|
-
` "Open the login form."`,
|
|
168
|
-
` "Fill credentials and submit."`,
|
|
169
|
-
` "Verify the welcome message."`,
|
|
170
|
-
` "Now testing the Counter section."`,
|
|
171
|
-
``,
|
|
172
|
-
` Bad examples (too verbose / too vague):`,
|
|
173
|
-
` "Let me check the current state of the app and then drive the login flow."`,
|
|
174
|
-
` "First, I'll take a snapshot, then I'll look at the page structure, and..."`,
|
|
175
|
-
``,
|
|
176
|
-
` After the run, if you discovered bugs or unexpected behavior, summarize`,
|
|
177
|
-
` them in the FINAL message using these markers so the widget can extract`,
|
|
178
|
-
` them into a Findings card:`,
|
|
179
|
-
``,
|
|
180
|
-
` ## Findings`,
|
|
181
|
-
` - **Bug** — <one-line summary>`,
|
|
182
|
-
` - **Minor** — <one-line summary>`,
|
|
183
|
-
``,
|
|
184
|
-
` Do NOT spread bug discoveries across mid-run narration — keep them in the`,
|
|
185
|
-
` final summary so they group cleanly. Mid-run, just narrate the next step.`,
|
|
186
|
-
].join('\n');
|
|
187
|
-
}
|
|
188
|
-
/**
|
|
189
|
-
* Volatile-only hint for `--resume` turns: just the tab list snapshot.
|
|
190
|
-
* Empty string when the tab list is empty (nothing to refresh).
|
|
191
|
-
*
|
|
192
|
-
* The rules and narration format from `buildCdpHint` are already
|
|
193
|
-
* established in the prior turn's context; re-sending them here would
|
|
194
|
-
* fragment Anthropic's prompt-cache fingerprint (cache hits require the
|
|
195
|
-
* system prompt to match byte-for-byte across turns) and bill ~500
|
|
196
|
-
* extra input tokens per follow-up turn for no behaviour change.
|
|
197
|
-
*
|
|
198
|
-
* We DO re-send the tab list because it can drift between turns (user
|
|
199
|
-
* opens a second tab, switches focus). The active-origin nav-guard is
|
|
200
|
-
* not repeated — the agent has it from turn 1 and the tab-list update
|
|
201
|
-
* keeps it grounded in the current URL.
|
|
202
|
-
*/
|
|
203
|
-
export function buildCdpHintResume(tabs) {
|
|
204
|
-
const resolved = resolveActiveOrigin(tabs);
|
|
205
|
-
if (!resolved)
|
|
206
|
-
return '';
|
|
207
|
-
const { active } = resolved;
|
|
208
|
-
return [
|
|
209
|
-
`(Resumed session — full nav + narration rules already in context.)`,
|
|
210
|
-
``,
|
|
211
|
-
`Current Chrome tabs:`,
|
|
212
|
-
...tabs.map(t => ` - ${t.url}${t.title ? ` (${t.title})` : ''}`),
|
|
213
|
-
``,
|
|
214
|
-
`Likely active dev tab: ${active.url}`,
|
|
215
|
-
].join('\n');
|
|
216
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
/** Max characters of the conventions file folded into the prompt. */
|
|
2
|
-
export declare const CONVENTIONS_MAX_CHARS = 4000;
|
|
3
|
-
/**
|
|
4
|
-
* Read `<projectRoot>/.hover/conventions.md` and return it wrapped as a
|
|
5
|
-
* system-prompt block, or null when the file is absent or empty.
|
|
6
|
-
*/
|
|
7
|
-
export declare function readConventions(projectRoot: string, maxChars?: number): Promise<string | null>;
|
|
8
|
-
//# sourceMappingURL=conventions.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"conventions.d.ts","sourceRoot":"","sources":["../../src/service/conventions.ts"],"names":[],"mappings":"AAgBA,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,OAAO,CAAC;AAE1C;;;GAGG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,SAAwB,GAC/B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAqBxB"}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Knowledge layer (F5): the project's testing conventions, injected into the
|
|
3
|
-
* agent's system prompt so the developer can steer *how it explores* — which
|
|
4
|
-
* flows matter, where login lives, the preferred selector attribute.
|
|
5
|
-
*
|
|
6
|
-
* Read by the SERVICE (not the agent) from `<projectRoot>/.hover/conventions.md`
|
|
7
|
-
* and folded into the system prompt — the agent never gains a file-read tool
|
|
8
|
-
* (D2). This shapes exploration only; it does NOT change how the saved spec is
|
|
9
|
-
* generated (that's the translator's job — D9).
|
|
10
|
-
*
|
|
11
|
-
* Capped to avoid prompt bloat, and injected on the FIRST turn only (it's
|
|
12
|
-
* static, like cdpHint's rules) so it doesn't fragment the prompt cache.
|
|
13
|
-
*/
|
|
14
|
-
import { readFile } from 'node:fs/promises';
|
|
15
|
-
import { join } from 'node:path';
|
|
16
|
-
/** Max characters of the conventions file folded into the prompt. */
|
|
17
|
-
export const CONVENTIONS_MAX_CHARS = 4000;
|
|
18
|
-
/**
|
|
19
|
-
* Read `<projectRoot>/.hover/conventions.md` and return it wrapped as a
|
|
20
|
-
* system-prompt block, or null when the file is absent or empty.
|
|
21
|
-
*/
|
|
22
|
-
export async function readConventions(projectRoot, maxChars = CONVENTIONS_MAX_CHARS) {
|
|
23
|
-
let raw;
|
|
24
|
-
try {
|
|
25
|
-
raw = await readFile(join(projectRoot, '.hover', 'conventions.md'), 'utf-8');
|
|
26
|
-
}
|
|
27
|
-
catch {
|
|
28
|
-
return null; // no conventions file — nothing to inject
|
|
29
|
-
}
|
|
30
|
-
const trimmed = raw.trim();
|
|
31
|
-
if (!trimmed)
|
|
32
|
-
return null;
|
|
33
|
-
const body = trimmed.length > maxChars ? `${trimmed.slice(0, maxChars)}\n…(truncated)` : trimmed;
|
|
34
|
-
return [
|
|
35
|
-
`Project testing conventions — the developer's house rules for this app,`,
|
|
36
|
-
`from .hover/conventions.md. Use them while EXPLORING (which flows matter,`,
|
|
37
|
-
`where login lives, preferred selectors, test data). They guide exploration`,
|
|
38
|
-
`only — they do not change how the saved spec is generated.`,
|
|
39
|
-
``,
|
|
40
|
-
body,
|
|
41
|
-
].join('\n');
|
|
42
|
-
}
|
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Save-artifact WebSocket handlers (spec / Jira CSV).
|
|
3
|
-
*
|
|
4
|
-
* Both save-* messages share the same shape: validate `name + steps`, call a
|
|
5
|
-
* per-kind writer, fork on Exists-error vs. success. The differences (which
|
|
6
|
-
* writer, which message names, which fields to pluck) are captured in the
|
|
7
|
-
* `SaveArtifactConfig` descriptor table below. (Save-as-Skill was retired; the
|
|
8
|
-
* generic `onSaved` hook it used is kept for any future artifact that needs a
|
|
9
|
-
* post-write tail.)
|
|
10
|
-
*/
|
|
11
|
-
import type { WebSocket } from 'ws';
|
|
12
|
-
import { type SkillStep } from '../skills/writeSkill.js';
|
|
13
|
-
import { writeSpec, type SpecAssertion } from '../specs/writeSpec.js';
|
|
14
|
-
import { writeCaseCsv } from '../specs/writeCaseCsv.js';
|
|
15
|
-
import { type ClientMessage } from './types.js';
|
|
16
|
-
interface SaveArtifactConfig<TWriteResult extends {
|
|
17
|
-
slug: string;
|
|
18
|
-
path: string;
|
|
19
|
-
}> {
|
|
20
|
-
/** Used in error messages. Mirrors the WS `type` the client sent. */
|
|
21
|
-
requestName: string;
|
|
22
|
-
/** Emitted on success. */
|
|
23
|
-
savedType: string;
|
|
24
|
-
/** Emitted when the writer threw an Exists-error. */
|
|
25
|
-
existsType: string;
|
|
26
|
-
/** Optional — fires after a successful write (e.g. to push a refreshed list
|
|
27
|
-
* to the widget). Currently unused; kept generic for future artifacts. */
|
|
28
|
-
onSaved?: (ws: WebSocket, devRoot: string) => Promise<void>;
|
|
29
|
-
/** Class used in `err instanceof …` to detect "already exists" errors. */
|
|
30
|
-
ExistsError: new (...args: never[]) => {
|
|
31
|
-
slug: string;
|
|
32
|
-
path: string;
|
|
33
|
-
} & Error;
|
|
34
|
-
/** Pluck the payload fields this artifact needs and call its writer. */
|
|
35
|
-
write: (args: {
|
|
36
|
-
devRoot: string;
|
|
37
|
-
name: string;
|
|
38
|
-
description: string;
|
|
39
|
-
steps: SkillStep[];
|
|
40
|
-
assertions: SpecAssertion[];
|
|
41
|
-
payload: NonNullable<ClientMessage['payload']>;
|
|
42
|
-
overwrite: boolean;
|
|
43
|
-
}) => Promise<TWriteResult>;
|
|
44
|
-
}
|
|
45
|
-
export declare function handleSaveArtifact<TWriteResult extends {
|
|
46
|
-
slug: string;
|
|
47
|
-
path: string;
|
|
48
|
-
}>(ws: WebSocket, msg: ClientMessage, devRoot: string, cfg: SaveArtifactConfig<TWriteResult>): Promise<void>;
|
|
49
|
-
export declare const SPEC_CONFIG: SaveArtifactConfig<Awaited<ReturnType<typeof writeSpec>>>;
|
|
50
|
-
export declare const CASE_CSV_CONFIG: SaveArtifactConfig<Awaited<ReturnType<typeof writeCaseCsv>>>;
|
|
51
|
-
export {};
|
|
52
|
-
//# sourceMappingURL=saveHandlers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"saveHandlers.d.ts","sourceRoot":"","sources":["../../src/service/saveHandlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,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;+EAC2E;IAC3E,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,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,75 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Save-artifact WebSocket handlers (spec / Jira CSV).
|
|
3
|
-
*
|
|
4
|
-
* Both save-* messages share the same shape: validate `name + steps`, call a
|
|
5
|
-
* per-kind writer, fork on Exists-error vs. success. The differences (which
|
|
6
|
-
* writer, which message names, which fields to pluck) are captured in the
|
|
7
|
-
* `SaveArtifactConfig` descriptor table below. (Save-as-Skill was retired; the
|
|
8
|
-
* generic `onSaved` hook it used is kept for any future artifact that needs a
|
|
9
|
-
* post-write tail.)
|
|
10
|
-
*/
|
|
11
|
-
import { writeSpec, SpecExistsError } from '../specs/writeSpec.js';
|
|
12
|
-
import { writeCaseCsv, CaseCsvExistsError } from '../specs/writeCaseCsv.js';
|
|
13
|
-
import { send } from './types.js';
|
|
14
|
-
export async function handleSaveArtifact(ws, msg, devRoot, cfg) {
|
|
15
|
-
const name = msg.payload?.name;
|
|
16
|
-
const description = msg.payload?.description ?? '';
|
|
17
|
-
const steps = msg.payload?.steps;
|
|
18
|
-
const assertions = msg.payload?.assertions ?? [];
|
|
19
|
-
const overwrite = msg.payload?.overwrite === true;
|
|
20
|
-
if (typeof name !== 'string' || !name.trim()) {
|
|
21
|
-
send(ws, { type: 'error', payload: { message: `${cfg.requestName}: name is required` } });
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (!Array.isArray(steps) || steps.length === 0) {
|
|
25
|
-
send(ws, { type: 'error', payload: { message: `${cfg.requestName}: no steps to save` } });
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
let result;
|
|
29
|
-
try {
|
|
30
|
-
result = await cfg.write({
|
|
31
|
-
devRoot, name, description, steps, assertions,
|
|
32
|
-
payload: msg.payload, overwrite,
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
if (err instanceof cfg.ExistsError) {
|
|
37
|
-
send(ws, { type: cfg.existsType, payload: { slug: err.slug, existingPath: err.path } });
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
41
|
-
send(ws, { type: 'error', payload: { message: `${cfg.requestName} failed: ${message}` } });
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
send(ws, { type: cfg.savedType, payload: { name: result.slug, path: result.path } });
|
|
45
|
-
// The artifact is already on disk; an onSaved failure (e.g. a follow-up
|
|
46
|
-
// list re-scan) shouldn't surface as if the save itself failed — log on.
|
|
47
|
-
if (cfg.onSaved) {
|
|
48
|
-
try {
|
|
49
|
-
await cfg.onSaved(ws, devRoot);
|
|
50
|
-
}
|
|
51
|
-
catch (err) {
|
|
52
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
53
|
-
console.warn(`[hover] ${cfg.requestName} onSaved failed: ${message}`);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export const SPEC_CONFIG = {
|
|
58
|
-
requestName: 'save-spec',
|
|
59
|
-
savedType: 'spec-saved',
|
|
60
|
-
existsType: 'spec-exists',
|
|
61
|
-
ExistsError: SpecExistsError,
|
|
62
|
-
write: ({ devRoot, name, description, steps, assertions, overwrite }) => writeSpec({ devRoot, name, description, steps, assertions, overwrite }),
|
|
63
|
-
};
|
|
64
|
-
export const CASE_CSV_CONFIG = {
|
|
65
|
-
requestName: 'save-case-csv',
|
|
66
|
-
savedType: 'case-csv-saved',
|
|
67
|
-
existsType: 'case-csv-exists',
|
|
68
|
-
ExistsError: CaseCsvExistsError,
|
|
69
|
-
write: ({ devRoot, name, description, steps, assertions, payload, overwrite }) => writeCaseCsv({
|
|
70
|
-
devRoot, name, description, steps, assertions,
|
|
71
|
-
jiraProjectKey: payload.jiraProjectKey,
|
|
72
|
-
labels: payload.labels,
|
|
73
|
-
overwrite,
|
|
74
|
-
}),
|
|
75
|
-
};
|
package/dist/service/types.d.ts
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for the service/ handler modules.
|
|
3
|
-
*
|
|
4
|
-
* `ClientMessage` describes the wire-protocol envelope every message from
|
|
5
|
-
* the widget arrives in. Lives here (not in service.ts) so individual
|
|
6
|
-
* handlers can type their `msg` argument without circular imports.
|
|
7
|
-
*
|
|
8
|
-
* `send` is the one-liner used by every handler to emit a typed message
|
|
9
|
-
* back to the widget. Centralised so the JSON.stringify happens in exactly
|
|
10
|
-
* one place.
|
|
11
|
-
*/
|
|
12
|
-
import { WebSocket } from 'ws';
|
|
13
|
-
import type { SkillStep } from '../skills/writeSkill.js';
|
|
14
|
-
import type { SpecAssertion } from '../specs/writeSpec.js';
|
|
15
|
-
export interface ClientMessage {
|
|
16
|
-
type: string;
|
|
17
|
-
payload?: {
|
|
18
|
-
text?: string;
|
|
19
|
-
sessionId?: string;
|
|
20
|
-
name?: string;
|
|
21
|
-
description?: string;
|
|
22
|
-
steps?: SkillStep[];
|
|
23
|
-
assertions?: SpecAssertion[];
|
|
24
|
-
overwrite?: boolean;
|
|
25
|
-
/** save-case-csv only — passed through to writeCaseCsv as extra
|
|
26
|
-
* fields on the test case's Labels column. */
|
|
27
|
-
jiraProjectKey?: string;
|
|
28
|
-
labels?: string;
|
|
29
|
-
/** check-cdp / launch-chrome / focus-debug — the widget's
|
|
30
|
-
* window.location.href so service can compare origins or navigate the
|
|
31
|
-
* newly-launched debug Chrome to the same URL. */
|
|
32
|
-
pageUrl?: string;
|
|
33
|
-
/** switch-agent only — id of the agent to switch the service to. */
|
|
34
|
-
agentId?: string;
|
|
35
|
-
/** set-mode only — id of the plugin-contributed mode to activate,
|
|
36
|
-
* or null to return to normal (unmoded) operation. */
|
|
37
|
-
modeId?: string | null;
|
|
38
|
-
/** optimize-spec / promote-optimized / discard-optimized — the spec slug. */
|
|
39
|
-
slug?: string;
|
|
40
|
-
/** set-api-key only — the model API key to inject into the spawned CLI's
|
|
41
|
-
* env (or empty/missing to clear it). Held in memory only, never logged. */
|
|
42
|
-
key?: string;
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
export declare function send(ws: WebSocket, message: {
|
|
46
|
-
type: string;
|
|
47
|
-
payload?: unknown;
|
|
48
|
-
}): void;
|
|
49
|
-
/** Send a message only if the socket is still open. Use this from delayed
|
|
50
|
-
* callbacks (promise `.then`, timers) where the client may have disconnected
|
|
51
|
-
* between scheduling and firing — calling `ws.send` on a closed socket
|
|
52
|
-
* is a silent no-op for some states and throws for others, so a single
|
|
53
|
-
* guarded helper makes the intent obvious and prevents surprises. */
|
|
54
|
-
export declare function sendIfOpen(ws: WebSocket, message: {
|
|
55
|
-
type: string;
|
|
56
|
-
payload?: unknown;
|
|
57
|
-
}): boolean;
|
|
58
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/service/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAE3D,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE;QACR,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,KAAK,CAAC,EAAE,SAAS,EAAE,CAAC;QACpB,UAAU,CAAC,EAAE,aAAa,EAAE,CAAC;QAC7B,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB;uDAC+C;QAC/C,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB;;2DAEmD;QACnD,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,oEAAoE;QACpE,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB;+DACuD;QACvD,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,6EAA6E;QAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;QACd;qFAC6E;QAC7E,GAAG,CAAC,EAAE,MAAM,CAAC;KACd,CAAC;CACH;AAED,wBAAgB,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAEtF;AAED;;;;sEAIsE;AACtE,wBAAgB,UAAU,CACxB,EAAE,EAAE,SAAS,EACb,OAAO,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GAC3C,OAAO,CAIT"}
|
package/dist/service/types.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for the service/ handler modules.
|
|
3
|
-
*
|
|
4
|
-
* `ClientMessage` describes the wire-protocol envelope every message from
|
|
5
|
-
* the widget arrives in. Lives here (not in service.ts) so individual
|
|
6
|
-
* handlers can type their `msg` argument without circular imports.
|
|
7
|
-
*
|
|
8
|
-
* `send` is the one-liner used by every handler to emit a typed message
|
|
9
|
-
* back to the widget. Centralised so the JSON.stringify happens in exactly
|
|
10
|
-
* one place.
|
|
11
|
-
*/
|
|
12
|
-
import { WebSocket } from 'ws';
|
|
13
|
-
export function send(ws, message) {
|
|
14
|
-
ws.send(JSON.stringify(message));
|
|
15
|
-
}
|
|
16
|
-
/** Send a message only if the socket is still open. Use this from delayed
|
|
17
|
-
* callbacks (promise `.then`, timers) where the client may have disconnected
|
|
18
|
-
* between scheduling and firing — calling `ws.send` on a closed socket
|
|
19
|
-
* is a silent no-op for some states and throws for others, so a single
|
|
20
|
-
* guarded helper makes the intent obvious and prevents surprises. */
|
|
21
|
-
export function sendIfOpen(ws, message) {
|
|
22
|
-
if (ws.readyState !== WebSocket.OPEN)
|
|
23
|
-
return false;
|
|
24
|
-
ws.send(JSON.stringify(message));
|
|
25
|
-
return true;
|
|
26
|
-
}
|