@hover-dev/core 0.2.1 → 0.2.4
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 +1 -1
- package/dist/agents/claude.d.ts.map +1 -1
- package/dist/agents/claude.js +31 -18
- package/dist/agents/codex.d.ts +19 -0
- package/dist/agents/codex.d.ts.map +1 -0
- package/dist/agents/codex.js +210 -0
- package/dist/agents/detect.d.ts +27 -1
- package/dist/agents/detect.d.ts.map +1 -1
- package/dist/agents/detect.js +46 -3
- package/dist/agents/invoke.d.ts.map +1 -1
- package/dist/agents/invoke.js +21 -7
- package/dist/agents/registry.d.ts +10 -5
- package/dist/agents/registry.d.ts.map +1 -1
- package/dist/agents/registry.js +14 -5
- package/dist/agents/types.d.ts +72 -1
- package/dist/agents/types.d.ts.map +1 -1
- package/dist/service/cdpHandlers.d.ts +39 -0
- package/dist/service/cdpHandlers.d.ts.map +1 -0
- package/dist/service/cdpHandlers.js +82 -0
- package/dist/service/cdpHint.d.ts +20 -0
- package/dist/service/cdpHint.d.ts.map +1 -0
- package/dist/service/cdpHint.js +86 -0
- package/dist/service/saveHandlers.d.ts +55 -0
- package/dist/service/saveHandlers.d.ts.map +1 -0
- package/dist/service/saveHandlers.js +80 -0
- package/dist/service/types.d.ts +41 -0
- package/dist/service/types.d.ts.map +1 -0
- package/dist/service/types.js +14 -0
- package/dist/service.d.ts.map +1 -1
- package/dist/service.js +141 -235
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,MAAM,MAAM,aAAa,GACrB,MAAM,GACN,OAAO,GACP,KAAK,GACL,QAAQ,CAAC;AAEb,MAAM,MAAM,YAAY,GACpB,aAAa,GACb,KAAK,GACL,YAAY,GACZ,YAAY,CAAC;AAEjB,qBAAa,6BAA8B,SAAQ,KAAK;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,sBAAuB,SAAQ,KAAK;aACnB,OAAO,EAAE,MAAM;gBAAf,OAAO,EAAE,MAAM;CAI5C;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB;;;yCAGqC;IACrC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;6EACyE;IACzE,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;GAGG;AACH,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,eAAe,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5D;IAAE,IAAI,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/agents/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
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 type { WebSocket } from 'ws';
|
|
14
|
+
import { type ClientMessage } from './types.js';
|
|
15
|
+
/**
|
|
16
|
+
* "Is this widget running inside the debug Chrome?" The widget asks this on
|
|
17
|
+
* connect (and after every status-changing event) so it can render itself as
|
|
18
|
+
* either:
|
|
19
|
+
* - same-window → normal, drives the page
|
|
20
|
+
* - wrong-window → disabled, with a "use the other window" notice
|
|
21
|
+
* - no-cdp → enabled but click triggers launch-chrome instead
|
|
22
|
+
*/
|
|
23
|
+
export declare function handleCheckCdp(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
|
|
24
|
+
/**
|
|
25
|
+
* Launch a debug Chrome navigated to `pageUrl`, then re-check status. The
|
|
26
|
+
* re-check usually returns 'wrong-window' (because the widget asking is in
|
|
27
|
+
* the user's regular Chrome, not the freshly-launched one) — the widget then
|
|
28
|
+
* displays the "use the other window" state.
|
|
29
|
+
*/
|
|
30
|
+
export declare function handleLaunchChrome(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* bringToFront the debug-Chrome tab matching `pageUrl`'s origin (or open one
|
|
33
|
+
* if none exists). Used by the wrong-window UI's "switch to debug Chrome"
|
|
34
|
+
* button. Doesn't return cdp-status — bringToFront doesn't change anything
|
|
35
|
+
* the widget cares about, and the widget the user is about to focus is a
|
|
36
|
+
* different page (and will run its own check-cdp on its own ws connection).
|
|
37
|
+
*/
|
|
38
|
+
export declare function handleFocusDebug(ws: WebSocket, msg: ClientMessage, cdpUrl: string): Promise<void>;
|
|
39
|
+
//# sourceMappingURL=cdpHandlers.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
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) {
|
|
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) {
|
|
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({ url: pageUrl, port });
|
|
57
|
+
if (!result.ok) {
|
|
58
|
+
send(ws, { type: 'cdp-status', payload: { state: 'no-cdp', reason: result.reason } });
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
// Re-check after launch so the widget gets the real status.
|
|
62
|
+
const status = await checkCdpStatus(cdpUrl, pageUrl);
|
|
63
|
+
send(ws, { type: 'cdp-status', payload: status });
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* bringToFront the debug-Chrome tab matching `pageUrl`'s origin (or open one
|
|
67
|
+
* if none exists). Used by the wrong-window UI's "switch to debug Chrome"
|
|
68
|
+
* button. Doesn't return cdp-status — bringToFront doesn't change anything
|
|
69
|
+
* the widget cares about, and the widget the user is about to focus is a
|
|
70
|
+
* different page (and will run its own check-cdp on its own ws connection).
|
|
71
|
+
*/
|
|
72
|
+
export async function handleFocusDebug(ws, msg, cdpUrl) {
|
|
73
|
+
const pageUrl = msg.payload?.pageUrl;
|
|
74
|
+
if (typeof pageUrl !== 'string' || !pageUrl) {
|
|
75
|
+
send(ws, { type: 'error', payload: { message: 'focus-debug: pageUrl is required' } });
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const result = await focusDebugTab(cdpUrl, pageUrl);
|
|
79
|
+
if (!result.ok) {
|
|
80
|
+
send(ws, { type: 'error', payload: { message: `focus-debug: ${result.reason}` } });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
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
|
+
export declare function buildCdpHint(tabs: {
|
|
17
|
+
url: string;
|
|
18
|
+
title?: string;
|
|
19
|
+
}[]): string;
|
|
20
|
+
//# sourceMappingURL=cdpHint.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cdpHint.d.ts","sourceRoot":"","sources":["../../src/service/cdpHint.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,wBAAgB,YAAY,CAAC,IAAI,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,EAAE,GAAG,MAAM,CAmE5E"}
|
|
@@ -0,0 +1,86 @@
|
|
|
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
|
+
export function buildCdpHint(tabs) {
|
|
17
|
+
if (tabs.length === 0)
|
|
18
|
+
return '';
|
|
19
|
+
// Prefer the localhost tab if we have multiple — that's almost always the
|
|
20
|
+
// dev server the user is testing against.
|
|
21
|
+
const localhost = tabs.find(t => /localhost|127\.0\.0\.1/.test(t.url));
|
|
22
|
+
const active = localhost ?? tabs[0];
|
|
23
|
+
let activeOrigin = '';
|
|
24
|
+
try {
|
|
25
|
+
activeOrigin = new URL(active.url).origin;
|
|
26
|
+
}
|
|
27
|
+
catch { /* malformed url — fall back to no-origin guard */ }
|
|
28
|
+
return [
|
|
29
|
+
`The user's Chrome currently has these tabs open:`,
|
|
30
|
+
...tabs.map(t => ` - ${t.url}${t.title ? ` (${t.title})` : ''}`),
|
|
31
|
+
``,
|
|
32
|
+
`The likely active dev tab is: ${active.url}`,
|
|
33
|
+
``,
|
|
34
|
+
`Navigation rules — read carefully, these mistakes are the #1 cause of failed`,
|
|
35
|
+
`runs:`,
|
|
36
|
+
``,
|
|
37
|
+
` 1. Do NOT call browser_navigate to a URL that is already the active tab.`,
|
|
38
|
+
` The widget that hosts this session lives inside the page; reloading the`,
|
|
39
|
+
` page kills the WebSocket connection and your run gets aborted mid-flight.`,
|
|
40
|
+
``,
|
|
41
|
+
activeOrigin
|
|
42
|
+
? ` 2. Do NOT call browser_navigate to ANY path on origin ${activeOrigin}`
|
|
43
|
+
: ` 2. Do NOT call browser_navigate to source-file paths on the dev server`,
|
|
44
|
+
` just to "read source code for hints" — paths like /src/Login.tsx,`,
|
|
45
|
+
` /@vite/client, /node_modules/* are served by Vite as JS modules and`,
|
|
46
|
+
` loading them triggers the same widget-killing reload. To inspect the`,
|
|
47
|
+
` page, use browser_snapshot — the accessibility tree already exposes`,
|
|
48
|
+
` labels, placeholders, and roles.`,
|
|
49
|
+
``,
|
|
50
|
+
` 3. Do NOT read the JS bundle, evaluate page source, or scrape DOM for`,
|
|
51
|
+
` hardcoded credentials, API keys, or secrets. If the task needs login,`,
|
|
52
|
+
` the user must provide credentials in their prompt; if they didn't,`,
|
|
53
|
+
` report "no credentials provided" and stop — do not guess.`,
|
|
54
|
+
``,
|
|
55
|
+
` 4. To see the current page state, call browser_snapshot first. Only`,
|
|
56
|
+
` navigate if you actually need a different URL.`,
|
|
57
|
+
``,
|
|
58
|
+
`Narration format — affects how the widget renders your run for the user:`,
|
|
59
|
+
``,
|
|
60
|
+
` Before each LOGICAL STEP (a coherent unit of work like "Open the login`,
|
|
61
|
+
` form", "Fill credentials", "Verify the welcome message"), emit ONE short`,
|
|
62
|
+
` imperative sentence describing what you're about to do — present tense,`,
|
|
63
|
+
` 3–8 words, no markdown. The widget uses that sentence as the step's title.`,
|
|
64
|
+
``,
|
|
65
|
+
` Good examples:`,
|
|
66
|
+
` "Open the login form."`,
|
|
67
|
+
` "Fill credentials and submit."`,
|
|
68
|
+
` "Verify the welcome message."`,
|
|
69
|
+
` "Now testing the Counter section."`,
|
|
70
|
+
``,
|
|
71
|
+
` Bad examples (too verbose / too vague):`,
|
|
72
|
+
` "Let me check the current state of the app and then drive the login flow."`,
|
|
73
|
+
` "First, I'll take a snapshot, then I'll look at the page structure, and..."`,
|
|
74
|
+
``,
|
|
75
|
+
` After the run, if you discovered bugs or unexpected behavior, summarize`,
|
|
76
|
+
` them in the FINAL message using these markers so the widget can extract`,
|
|
77
|
+
` them into a Findings card:`,
|
|
78
|
+
``,
|
|
79
|
+
` ## Findings`,
|
|
80
|
+
` - **Bug** — <one-line summary>`,
|
|
81
|
+
` - **Minor** — <one-line summary>`,
|
|
82
|
+
``,
|
|
83
|
+
` Do NOT spread bug discoveries across mid-run narration — keep them in the`,
|
|
84
|
+
` final summary so they group cleanly. Mid-run, just narrate the next step.`,
|
|
85
|
+
].join('\n');
|
|
86
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save-artifact WebSocket handlers (skill / spec / Jira CSV).
|
|
3
|
+
*
|
|
4
|
+
* All three save-* messages share the same shape: validate `name + steps`,
|
|
5
|
+
* call a per-kind writer, fork on Exists-error vs. success. The differences
|
|
6
|
+
* (which writer, which message names, which fields to pluck, skill's
|
|
7
|
+
* "push a fresh skills-list afterwards" tail) are captured in the
|
|
8
|
+
* `SaveArtifactConfig` descriptor table below.
|
|
9
|
+
*
|
|
10
|
+
* Replaces three near-identical handlers that drifted apart over time —
|
|
11
|
+
* see the v0.2.x refactor pass for the full rationale.
|
|
12
|
+
*/
|
|
13
|
+
import type { WebSocket } from 'ws';
|
|
14
|
+
import { writeSkill, type SkillStep } from '../skills/writeSkill.js';
|
|
15
|
+
import { writeSpec, type SpecAssertion } from '../specs/writeSpec.js';
|
|
16
|
+
import { writeCaseCsv } from '../specs/writeCaseCsv.js';
|
|
17
|
+
import { type ClientMessage } from './types.js';
|
|
18
|
+
interface SaveArtifactConfig<TWriteResult extends {
|
|
19
|
+
slug: string;
|
|
20
|
+
path: string;
|
|
21
|
+
}> {
|
|
22
|
+
/** Used in error messages. Mirrors the WS `type` the client sent. */
|
|
23
|
+
requestName: string;
|
|
24
|
+
/** Emitted on success. */
|
|
25
|
+
savedType: string;
|
|
26
|
+
/** Emitted when the writer threw an Exists-error. */
|
|
27
|
+
existsType: string;
|
|
28
|
+
/** Optional — fires after a successful write. Used by the skill flow to
|
|
29
|
+
* push a refreshed `skills-list` to the widget. */
|
|
30
|
+
onSaved?: (ws: WebSocket, devRoot: string) => Promise<void>;
|
|
31
|
+
/** Class used in `err instanceof …` to detect "already exists" errors. */
|
|
32
|
+
ExistsError: new (...args: never[]) => {
|
|
33
|
+
slug: string;
|
|
34
|
+
path: string;
|
|
35
|
+
} & Error;
|
|
36
|
+
/** Pluck the payload fields this artifact needs and call its writer. */
|
|
37
|
+
write: (args: {
|
|
38
|
+
devRoot: string;
|
|
39
|
+
name: string;
|
|
40
|
+
description: string;
|
|
41
|
+
steps: SkillStep[];
|
|
42
|
+
assertions: SpecAssertion[];
|
|
43
|
+
payload: NonNullable<ClientMessage['payload']>;
|
|
44
|
+
overwrite: boolean;
|
|
45
|
+
}) => Promise<TWriteResult>;
|
|
46
|
+
}
|
|
47
|
+
export declare function handleSaveArtifact<TWriteResult extends {
|
|
48
|
+
slug: string;
|
|
49
|
+
path: string;
|
|
50
|
+
}>(ws: WebSocket, msg: ClientMessage, devRoot: string, cfg: SaveArtifactConfig<TWriteResult>): Promise<void>;
|
|
51
|
+
export declare const SKILL_CONFIG: SaveArtifactConfig<Awaited<ReturnType<typeof writeSkill>>>;
|
|
52
|
+
export declare const SPEC_CONFIG: SaveArtifactConfig<Awaited<ReturnType<typeof writeSpec>>>;
|
|
53
|
+
export declare const CASE_CSV_CONFIG: SaveArtifactConfig<Awaited<ReturnType<typeof writeCaseCsv>>>;
|
|
54
|
+
export {};
|
|
55
|
+
//# sourceMappingURL=saveHandlers.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Save-artifact WebSocket handlers (skill / spec / Jira CSV).
|
|
3
|
+
*
|
|
4
|
+
* All three save-* messages share the same shape: validate `name + steps`,
|
|
5
|
+
* call a per-kind writer, fork on Exists-error vs. success. The differences
|
|
6
|
+
* (which writer, which message names, which fields to pluck, skill's
|
|
7
|
+
* "push a fresh skills-list afterwards" tail) are captured in the
|
|
8
|
+
* `SaveArtifactConfig` descriptor table below.
|
|
9
|
+
*
|
|
10
|
+
* Replaces three near-identical handlers that drifted apart over time —
|
|
11
|
+
* see the v0.2.x refactor pass for the full rationale.
|
|
12
|
+
*/
|
|
13
|
+
import { writeSkill, listSkills, SkillExistsError } from '../skills/writeSkill.js';
|
|
14
|
+
import { writeSpec, SpecExistsError } from '../specs/writeSpec.js';
|
|
15
|
+
import { writeCaseCsv, CaseCsvExistsError } from '../specs/writeCaseCsv.js';
|
|
16
|
+
import { send } from './types.js';
|
|
17
|
+
export async function handleSaveArtifact(ws, msg, devRoot, cfg) {
|
|
18
|
+
const name = msg.payload?.name;
|
|
19
|
+
const description = msg.payload?.description ?? '';
|
|
20
|
+
const steps = msg.payload?.steps;
|
|
21
|
+
const assertions = msg.payload?.assertions ?? [];
|
|
22
|
+
const overwrite = msg.payload?.overwrite === true;
|
|
23
|
+
if (typeof name !== 'string' || !name.trim()) {
|
|
24
|
+
send(ws, { type: 'error', payload: { message: `${cfg.requestName}: name is required` } });
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
if (!Array.isArray(steps) || steps.length === 0) {
|
|
28
|
+
send(ws, { type: 'error', payload: { message: `${cfg.requestName}: no steps to save` } });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const result = await cfg.write({
|
|
33
|
+
devRoot, name, description, steps, assertions,
|
|
34
|
+
payload: msg.payload, overwrite,
|
|
35
|
+
});
|
|
36
|
+
send(ws, { type: cfg.savedType, payload: { name: result.slug, path: result.path } });
|
|
37
|
+
if (cfg.onSaved)
|
|
38
|
+
await cfg.onSaved(ws, devRoot);
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
if (err instanceof cfg.ExistsError) {
|
|
42
|
+
send(ws, { type: cfg.existsType, payload: { slug: err.slug, existingPath: err.path } });
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
46
|
+
send(ws, { type: 'error', payload: { message: `${cfg.requestName} failed: ${message}` } });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
export const SKILL_CONFIG = {
|
|
50
|
+
requestName: 'save-skill',
|
|
51
|
+
savedType: 'skill-saved',
|
|
52
|
+
existsType: 'skill-exists',
|
|
53
|
+
ExistsError: SkillExistsError,
|
|
54
|
+
write: ({ devRoot, name, description, steps, overwrite }) => writeSkill({ devRoot, name, description, steps, overwrite }),
|
|
55
|
+
onSaved: async (ws, devRoot) => {
|
|
56
|
+
// Push a fresh list so the widget's skills overlay updates without a
|
|
57
|
+
// round-trip — most relevant right after the save.
|
|
58
|
+
const skills = await listSkills(devRoot);
|
|
59
|
+
send(ws, { type: 'skills-list', payload: { skills } });
|
|
60
|
+
},
|
|
61
|
+
};
|
|
62
|
+
export const SPEC_CONFIG = {
|
|
63
|
+
requestName: 'save-spec',
|
|
64
|
+
savedType: 'spec-saved',
|
|
65
|
+
existsType: 'spec-exists',
|
|
66
|
+
ExistsError: SpecExistsError,
|
|
67
|
+
write: ({ devRoot, name, description, steps, assertions, overwrite }) => writeSpec({ devRoot, name, description, steps, assertions, overwrite }),
|
|
68
|
+
};
|
|
69
|
+
export const CASE_CSV_CONFIG = {
|
|
70
|
+
requestName: 'save-case-csv',
|
|
71
|
+
savedType: 'case-csv-saved',
|
|
72
|
+
existsType: 'case-csv-exists',
|
|
73
|
+
ExistsError: CaseCsvExistsError,
|
|
74
|
+
write: ({ devRoot, name, description, steps, assertions, payload, overwrite }) => writeCaseCsv({
|
|
75
|
+
devRoot, name, description, steps, assertions,
|
|
76
|
+
jiraProjectKey: payload.jiraProjectKey,
|
|
77
|
+
labels: payload.labels,
|
|
78
|
+
overwrite,
|
|
79
|
+
}),
|
|
80
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
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 type { 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
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare function send(ws: WebSocket, message: {
|
|
38
|
+
type: string;
|
|
39
|
+
payload?: unknown;
|
|
40
|
+
}): void;
|
|
41
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/service/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AACpC,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;KAClB,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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
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
|
+
export function send(ws, message) {
|
|
13
|
+
ws.send(JSON.stringify(message));
|
|
14
|
+
}
|
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":"AAmEA,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;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B;4EACwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAiDD,wBAAsB,YAAY,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAmT/E"}
|