@equinor/fusion-framework-cli-plugin-copilot 1.0.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/CHANGELOG.md +24 -0
- package/LICENSE +21 -0
- package/README.md +228 -0
- package/dist/esm/commands/app/command.js +176 -0
- package/dist/esm/commands/app/command.js.map +1 -0
- package/dist/esm/commands/app/eval.js +92 -0
- package/dist/esm/commands/app/eval.js.map +1 -0
- package/dist/esm/commands/app/format.js +93 -0
- package/dist/esm/commands/app/format.js.map +1 -0
- package/dist/esm/commands/app/login.js +71 -0
- package/dist/esm/commands/app/login.js.map +1 -0
- package/dist/esm/commands/app/prompts/execute-step.prompt.js +44 -0
- package/dist/esm/commands/app/prompts/execute-step.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/judge.prompt.js +116 -0
- package/dist/esm/commands/app/prompts/judge.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/plan.prompt.js +47 -0
- package/dist/esm/commands/app/prompts/plan.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/step.prompt.js +42 -0
- package/dist/esm/commands/app/prompts/step.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/system.dev-server.prompt.js +21 -0
- package/dist/esm/commands/app/prompts/system.dev-server.prompt.js.map +1 -0
- package/dist/esm/commands/app/prompts/system.prompt.js +77 -0
- package/dist/esm/commands/app/prompts/system.prompt.js.map +1 -0
- package/dist/esm/commands/app/server.js +83 -0
- package/dist/esm/commands/app/server.js.map +1 -0
- package/dist/esm/commands/app/session-logger.js +171 -0
- package/dist/esm/commands/app/session-logger.js.map +1 -0
- package/dist/esm/commands/app/tools/click.js +27 -0
- package/dist/esm/commands/app/tools/click.js.map +1 -0
- package/dist/esm/commands/app/tools/errors.js +24 -0
- package/dist/esm/commands/app/tools/errors.js.map +1 -0
- package/dist/esm/commands/app/tools/eval-js.js +36 -0
- package/dist/esm/commands/app/tools/eval-js.js.map +1 -0
- package/dist/esm/commands/app/tools/fill.js +28 -0
- package/dist/esm/commands/app/tools/fill.js.map +1 -0
- package/dist/esm/commands/app/tools/find.js +44 -0
- package/dist/esm/commands/app/tools/find.js.map +1 -0
- package/dist/esm/commands/app/tools/get-styles.js +36 -0
- package/dist/esm/commands/app/tools/get-styles.js.map +1 -0
- package/dist/esm/commands/app/tools/get-url.js +24 -0
- package/dist/esm/commands/app/tools/get-url.js.map +1 -0
- package/dist/esm/commands/app/tools/go-back.js +15 -0
- package/dist/esm/commands/app/tools/go-back.js.map +1 -0
- package/dist/esm/commands/app/tools/hover.js +24 -0
- package/dist/esm/commands/app/tools/hover.js.map +1 -0
- package/dist/esm/commands/app/tools/index.js +32 -0
- package/dist/esm/commands/app/tools/index.js.map +1 -0
- package/dist/esm/commands/app/tools/navigate.js +31 -0
- package/dist/esm/commands/app/tools/navigate.js.map +1 -0
- package/dist/esm/commands/app/tools/press-key.js +24 -0
- package/dist/esm/commands/app/tools/press-key.js.map +1 -0
- package/dist/esm/commands/app/tools/read-file.js +98 -0
- package/dist/esm/commands/app/tools/read-file.js.map +1 -0
- package/dist/esm/commands/app/tools/registry.js +34 -0
- package/dist/esm/commands/app/tools/registry.js.map +1 -0
- package/dist/esm/commands/app/tools/reload.js +15 -0
- package/dist/esm/commands/app/tools/reload.js.map +1 -0
- package/dist/esm/commands/app/tools/screenshot.js +133 -0
- package/dist/esm/commands/app/tools/screenshot.js.map +1 -0
- package/dist/esm/commands/app/tools/scroll.js +41 -0
- package/dist/esm/commands/app/tools/scroll.js.map +1 -0
- package/dist/esm/commands/app/tools/select.js +25 -0
- package/dist/esm/commands/app/tools/select.js.map +1 -0
- package/dist/esm/commands/app/tools/snapshot.js +24 -0
- package/dist/esm/commands/app/tools/snapshot.js.map +1 -0
- package/dist/esm/commands/app/tools/type-text.js +29 -0
- package/dist/esm/commands/app/tools/type-text.js.map +1 -0
- package/dist/esm/commands/app/tools/types.js +2 -0
- package/dist/esm/commands/app/tools/types.js.map +1 -0
- package/dist/esm/commands/app/tools/wait.js +39 -0
- package/dist/esm/commands/app/tools/wait.js.map +1 -0
- package/dist/esm/commands/app/tools/write-file.js +98 -0
- package/dist/esm/commands/app/tools/write-file.js.map +1 -0
- package/dist/esm/commands/app/types.js +2 -0
- package/dist/esm/commands/app/types.js.map +1 -0
- package/dist/esm/eval-resolve.js +83 -0
- package/dist/esm/eval-resolve.js.map +1 -0
- package/dist/esm/index.js +18 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/utils/agent-browser.js +67 -0
- package/dist/esm/utils/agent-browser.js.map +1 -0
- package/dist/esm/utils/browser-poll.js +26 -0
- package/dist/esm/utils/browser-poll.js.map +1 -0
- package/dist/esm/utils/daemon.js +83 -0
- package/dist/esm/utils/daemon.js.map +1 -0
- package/dist/esm/utils/index.js +6 -0
- package/dist/esm/utils/index.js.map +1 -0
- package/dist/esm/utils/process.js +30 -0
- package/dist/esm/utils/process.js.map +1 -0
- package/dist/esm/utils/server.js +65 -0
- package/dist/esm/utils/server.js.map +1 -0
- package/dist/esm/version.js +3 -0
- package/dist/esm/version.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/commands/app/command.d.ts +7 -0
- package/dist/types/commands/app/eval.d.ts +9 -0
- package/dist/types/commands/app/format.d.ts +14 -0
- package/dist/types/commands/app/login.d.ts +14 -0
- package/dist/types/commands/app/prompts/execute-step.prompt.d.ts +13 -0
- package/dist/types/commands/app/prompts/judge.prompt.d.ts +15 -0
- package/dist/types/commands/app/prompts/plan.prompt.d.ts +13 -0
- package/dist/types/commands/app/prompts/step.prompt.d.ts +12 -0
- package/dist/types/commands/app/prompts/system.dev-server.prompt.d.ts +12 -0
- package/dist/types/commands/app/prompts/system.prompt.d.ts +12 -0
- package/dist/types/commands/app/server.d.ts +26 -0
- package/dist/types/commands/app/session-logger.d.ts +14 -0
- package/dist/types/commands/app/tools/click.d.ts +9 -0
- package/dist/types/commands/app/tools/errors.d.ts +9 -0
- package/dist/types/commands/app/tools/eval-js.d.ts +13 -0
- package/dist/types/commands/app/tools/fill.d.ts +9 -0
- package/dist/types/commands/app/tools/find.d.ts +9 -0
- package/dist/types/commands/app/tools/get-styles.d.ts +13 -0
- package/dist/types/commands/app/tools/get-url.d.ts +9 -0
- package/dist/types/commands/app/tools/go-back.d.ts +9 -0
- package/dist/types/commands/app/tools/hover.d.ts +9 -0
- package/dist/types/commands/app/tools/index.d.ts +9 -0
- package/dist/types/commands/app/tools/navigate.d.ts +9 -0
- package/dist/types/commands/app/tools/press-key.d.ts +9 -0
- package/dist/types/commands/app/tools/read-file.d.ts +13 -0
- package/dist/types/commands/app/tools/registry.d.ts +28 -0
- package/dist/types/commands/app/tools/reload.d.ts +9 -0
- package/dist/types/commands/app/tools/screenshot.d.ts +9 -0
- package/dist/types/commands/app/tools/scroll.d.ts +9 -0
- package/dist/types/commands/app/tools/select.d.ts +9 -0
- package/dist/types/commands/app/tools/snapshot.d.ts +9 -0
- package/dist/types/commands/app/tools/type-text.d.ts +9 -0
- package/dist/types/commands/app/tools/types.d.ts +12 -0
- package/dist/types/commands/app/tools/wait.d.ts +9 -0
- package/dist/types/commands/app/tools/write-file.d.ts +19 -0
- package/dist/types/commands/app/types.d.ts +68 -0
- package/dist/types/eval-resolve.d.ts +31 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/utils/agent-browser.d.ts +26 -0
- package/dist/types/utils/browser-poll.d.ts +9 -0
- package/dist/types/utils/daemon.d.ts +16 -0
- package/dist/types/utils/index.d.ts +5 -0
- package/dist/types/utils/process.d.ts +21 -0
- package/dist/types/utils/server.d.ts +26 -0
- package/dist/types/version.d.ts +1 -0
- package/package.json +57 -0
- package/src/commands/app/command.ts +204 -0
- package/src/commands/app/eval.ts +114 -0
- package/src/commands/app/format.ts +110 -0
- package/src/commands/app/login.ts +92 -0
- package/src/commands/app/prompts/execute-step.prompt.ts +48 -0
- package/src/commands/app/prompts/judge.prompt.ts +121 -0
- package/src/commands/app/prompts/plan.prompt.ts +48 -0
- package/src/commands/app/prompts/step.prompt.ts +45 -0
- package/src/commands/app/prompts/system.dev-server.prompt.ts +22 -0
- package/src/commands/app/prompts/system.prompt.ts +78 -0
- package/src/commands/app/server.ts +105 -0
- package/src/commands/app/session-logger.ts +187 -0
- package/src/commands/app/tools/click.ts +28 -0
- package/src/commands/app/tools/errors.ts +26 -0
- package/src/commands/app/tools/eval-js.ts +40 -0
- package/src/commands/app/tools/fill.ts +29 -0
- package/src/commands/app/tools/find.ts +49 -0
- package/src/commands/app/tools/get-styles.ts +40 -0
- package/src/commands/app/tools/get-url.ts +26 -0
- package/src/commands/app/tools/go-back.ts +16 -0
- package/src/commands/app/tools/hover.ts +25 -0
- package/src/commands/app/tools/index.ts +57 -0
- package/src/commands/app/tools/navigate.ts +32 -0
- package/src/commands/app/tools/press-key.ts +25 -0
- package/src/commands/app/tools/read-file.ts +113 -0
- package/src/commands/app/tools/registry.ts +38 -0
- package/src/commands/app/tools/reload.ts +16 -0
- package/src/commands/app/tools/screenshot.ts +168 -0
- package/src/commands/app/tools/scroll.ts +45 -0
- package/src/commands/app/tools/select.ts +26 -0
- package/src/commands/app/tools/snapshot.ts +27 -0
- package/src/commands/app/tools/type-text.ts +30 -0
- package/src/commands/app/tools/types.ts +15 -0
- package/src/commands/app/tools/wait.ts +43 -0
- package/src/commands/app/tools/write-file.ts +104 -0
- package/src/commands/app/types.ts +76 -0
- package/src/eval-resolve.ts +113 -0
- package/src/index.ts +20 -0
- package/src/utils/agent-browser.ts +66 -0
- package/src/utils/browser-poll.ts +27 -0
- package/src/utils/daemon.ts +84 -0
- package/src/utils/index.ts +5 -0
- package/src/utils/process.ts +33 -0
- package/src/utils/server.ts +66 -0
- package/src/version.ts +2 -0
- package/tsconfig.json +13 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { ab, resolveAppKey, cleanup, resetDaemon, sleep, stripAnsi, waitForServer, pollConsole, } from '../../utils/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Interactive login mode for the `copilot app` command.
|
|
7
|
+
*
|
|
8
|
+
* Starts the application dev server, opens a headed browser window for
|
|
9
|
+
* manual MSAL authentication, and polls the browser console for the Fusion
|
|
10
|
+
* `"Done"` message that signals successful initialisation. The browser is
|
|
11
|
+
* left open after login so the user can finish any manual steps before
|
|
12
|
+
* pressing Ctrl+C.
|
|
13
|
+
*
|
|
14
|
+
* @param absAppPath - Absolute path to the Fusion application directory
|
|
15
|
+
* @param port - Port number for the dev server
|
|
16
|
+
* @param host - Host address for the dev server
|
|
17
|
+
*/
|
|
18
|
+
export async function runLogin(absAppPath, port, host) {
|
|
19
|
+
const pkgJsonPath = join(absAppPath, 'package.json');
|
|
20
|
+
if (!existsSync(pkgJsonPath)) {
|
|
21
|
+
console.error(`❌ No package.json found at ${absAppPath}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
25
|
+
if (!pkgJson.name) {
|
|
26
|
+
console.error(`❌ package.json at ${absAppPath} has no "name" field`);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
let serverProcess;
|
|
30
|
+
console.log('📦 Resolving app manifest...');
|
|
31
|
+
const appKey = resolveAppKey(absAppPath);
|
|
32
|
+
console.log(`🚀 Starting server for ${appKey}...`);
|
|
33
|
+
serverProcess = spawn('ffc', ['app', 'dev', '--host', host, '--port', String(port)], {
|
|
34
|
+
cwd: absAppPath,
|
|
35
|
+
stdio: 'inherit',
|
|
36
|
+
detached: false,
|
|
37
|
+
});
|
|
38
|
+
const serverUrl = `http://localhost:${port}`;
|
|
39
|
+
const ready = await waitForServer(serverUrl, 60);
|
|
40
|
+
if (!ready) {
|
|
41
|
+
console.error('❌ Server failed to start within 60s');
|
|
42
|
+
cleanup(serverProcess);
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const appUrl = `${serverUrl}/apps/${appKey}`;
|
|
46
|
+
console.log(`✅ Server ready at ${appUrl}`);
|
|
47
|
+
console.log('🌐 Opening headed browser — log in with your Equinor account...');
|
|
48
|
+
// Kill any stale daemon/Chrome so the new --headed --profile flags take effect
|
|
49
|
+
resetDaemon();
|
|
50
|
+
ab(['open', appUrl, '--headed'], 60_000);
|
|
51
|
+
// Ctrl+C handler: stop the dev server but leave the browser open
|
|
52
|
+
process.on('SIGINT', () => {
|
|
53
|
+
console.log('\n👋 Stopping server... (browser left open)');
|
|
54
|
+
cleanup(serverProcess);
|
|
55
|
+
process.exit(0);
|
|
56
|
+
});
|
|
57
|
+
// Wait for the Fusion app to initialise (browser console emits "✓ Done" after auth)
|
|
58
|
+
console.log('⏳ Waiting for app to initialize after login... (Ctrl+C to finish early)');
|
|
59
|
+
const initialized = await pollConsole((logs) => stripAnsi(logs).includes('Done'), 300_000);
|
|
60
|
+
if (initialized) {
|
|
61
|
+
await sleep(2000);
|
|
62
|
+
console.log('✅ Login complete — session saved.');
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
console.log('⚠ Timed out waiting for app init. Session may still be saved.');
|
|
66
|
+
}
|
|
67
|
+
// Always wait for manual Ctrl+C so the user can finish what they're doing
|
|
68
|
+
console.log('🌐 Browser still open. Press Ctrl+C to exit.');
|
|
69
|
+
await new Promise(() => { }); // block until SIGINT
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=login.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"login.js","sourceRoot":"","sources":["../../../../src/commands/app/login.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the execution prompt for a single test step.
|
|
3
|
+
*
|
|
4
|
+
* The agent receives one plan step and executes its actions against the
|
|
5
|
+
* running application using browser tools, collecting evidence artifacts
|
|
6
|
+
* into the evidence directory.
|
|
7
|
+
*
|
|
8
|
+
* @param actions - The action strings to execute in order
|
|
9
|
+
* @param ctx - Runtime context with output directory and app URL
|
|
10
|
+
* @returns The fully interpolated execution prompt string
|
|
11
|
+
*/
|
|
12
|
+
export const createExecuteStepPrompt = (actions, ctx) => {
|
|
13
|
+
return `
|
|
14
|
+
You are now EXECUTING one test step using real browser tools.
|
|
15
|
+
|
|
16
|
+
ACTIONS: ${JSON.stringify(actions, null, 2)}
|
|
17
|
+
|
|
18
|
+
APPLICATION URL: ${ctx.url}
|
|
19
|
+
EVIDENCE DIRECTORY: ${ctx.outDir}/evidence
|
|
20
|
+
|
|
21
|
+
Execute the "actions" array in order using the available browser tool calls.
|
|
22
|
+
Collect rich evidence at every stage — especially on failure.
|
|
23
|
+
|
|
24
|
+
Evidence rules (mandatory):
|
|
25
|
+
- Before important actions: screenshot --annotate evidence/step-{criterion-slug}-before.png
|
|
26
|
+
- After the step (always): screenshot --full --annotate evidence/step-{criterion-slug}-after.png
|
|
27
|
+
- On any failure or suspicion:
|
|
28
|
+
errors > evidence/step-{criterion-slug}-errors.log
|
|
29
|
+
console > evidence/step-{criterion-slug}-console.log
|
|
30
|
+
snapshot -i --json > evidence/step-{criterion-slug}-snapshot.json
|
|
31
|
+
- Use descriptive, unique filenames
|
|
32
|
+
Examples: step-login-button-missing-screenshot.png, step-dashboard-url.txt
|
|
33
|
+
|
|
34
|
+
Important patterns:
|
|
35
|
+
- Start most steps with: snapshot -i --annotate → use @eN refs for element stability
|
|
36
|
+
- After navigation: wait --load networkidle or wait --url "**/pattern*"
|
|
37
|
+
- On failure (non-zero exit, timeout, missing element, JS error): capture extra evidence
|
|
38
|
+
- If flaky (timing, network): take 2–3 extra screenshots ~1 s apart
|
|
39
|
+
|
|
40
|
+
When done, your work will be evaluated against the step's passEvidence and failEvidence.
|
|
41
|
+
Do NOT output markdown or explanations — only use browser tool calls and collect evidence.
|
|
42
|
+
`.trim();
|
|
43
|
+
};
|
|
44
|
+
//# sourceMappingURL=execute-step.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute-step.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/execute-step.prompt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CACrC,OAAiB,EACjB,GAA4B,EACpB,EAAE;IACV,OAAO;;;WAGE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;;mBAExB,GAAG,CAAC,GAAG;sBACJ,GAAG,CAAC,MAAM;;;;;;;;;;;;;;;;;;;;;;;GAuB7B,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { readdirSync, readFileSync, existsSync } from 'node:fs';
|
|
2
|
+
import { join, extname } from 'node:path';
|
|
3
|
+
/** Text file extensions that are safe to embed in the prompt. */
|
|
4
|
+
const TEXT_EXTENSIONS = new Set(['.txt', '.json', '.jsonl', '.log', '.html', '.csv']);
|
|
5
|
+
/**
|
|
6
|
+
* Reads `executions.jsonl` and all text evidence files from the run directory
|
|
7
|
+
* and returns them as a single string block suitable for prompt embedding.
|
|
8
|
+
*
|
|
9
|
+
* Binary files (screenshots) are listed by name but not inlined.
|
|
10
|
+
*
|
|
11
|
+
* @param outDir - The run output directory containing executions.jsonl and evidence/
|
|
12
|
+
* @returns Formatted string with execution records and text evidence
|
|
13
|
+
*/
|
|
14
|
+
function collectEvidence(outDir) {
|
|
15
|
+
const parts = [];
|
|
16
|
+
// ── executions.jsonl ──
|
|
17
|
+
const execPath = join(outDir, 'executions.jsonl');
|
|
18
|
+
if (existsSync(execPath)) {
|
|
19
|
+
parts.push('### executions.jsonl', readFileSync(execPath, 'utf-8').trim());
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
parts.push('### executions.jsonl', '(file not found)');
|
|
23
|
+
}
|
|
24
|
+
// ── evidence/ directory ──
|
|
25
|
+
const evidenceDir = join(outDir, 'evidence');
|
|
26
|
+
if (existsSync(evidenceDir)) {
|
|
27
|
+
const files = readdirSync(evidenceDir).sort();
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
const ext = extname(file).toLowerCase();
|
|
30
|
+
if (TEXT_EXTENSIONS.has(ext)) {
|
|
31
|
+
const content = readFileSync(join(evidenceDir, file), 'utf-8').trim();
|
|
32
|
+
parts.push(`### evidence/${file}`, content);
|
|
33
|
+
}
|
|
34
|
+
else {
|
|
35
|
+
parts.push(`### evidence/${file}`, `(binary file — ${ext} screenshot)`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return parts.join('\n\n');
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Creates the final-verdict prompt for the judge phase.
|
|
43
|
+
*
|
|
44
|
+
* The judge reviews ALL step executions and evidence holistically, then writes
|
|
45
|
+
* a single `verdict.json` with an overall pass/fail decision. This differs
|
|
46
|
+
* from the per-step evaluate prompt which assesses one criterion at a time.
|
|
47
|
+
*
|
|
48
|
+
* Execution records and text evidence files are embedded directly in the prompt
|
|
49
|
+
* so the model does not need file-reading tools or SDK attachment resolution.
|
|
50
|
+
*
|
|
51
|
+
* @param ctx - Runtime context containing the output directory and app URL
|
|
52
|
+
* @returns The fully interpolated judge prompt string
|
|
53
|
+
*/
|
|
54
|
+
export const createJudgePrompt = (ctx) => {
|
|
55
|
+
const evidence = collectEvidence(ctx.outDir);
|
|
56
|
+
return `
|
|
57
|
+
All test steps have been executed. You are now the JUDGE.
|
|
58
|
+
|
|
59
|
+
Review the evidence below and any screenshots using read_file, then produce a final verdict.
|
|
60
|
+
|
|
61
|
+
<evidence>
|
|
62
|
+
${evidence}
|
|
63
|
+
</evidence>
|
|
64
|
+
|
|
65
|
+
To inspect screenshots, use read_file with paths like "evidence/step-hello-fusion-header.jpg".
|
|
66
|
+
Images are returned as inline vision content you can see.
|
|
67
|
+
|
|
68
|
+
Save your verdict using write_file with path "verdict.json".
|
|
69
|
+
You MUST call write_file. Do NOT output the JSON as a message.
|
|
70
|
+
|
|
71
|
+
Evaluation rules — apply exactly:
|
|
72
|
+
1. Auto-fail a criterion if:
|
|
73
|
+
- Any "Uncaught", TypeError, or ReferenceError appears in console/errors during that step
|
|
74
|
+
- The page shows an error, HTTP 500, white screen, or spinner lasting >15 s
|
|
75
|
+
- The expected element, text, or URL never appeared
|
|
76
|
+
- An explicit assertion (is visible, get text, get count) returned a failing result
|
|
77
|
+
2. A criterion passes only when:
|
|
78
|
+
- All passEvidence conditions are clearly confirmed in screenshots (use read_file to view them), console, or URL
|
|
79
|
+
- No critical functional deviation is present
|
|
80
|
+
3. Be lenient on:
|
|
81
|
+
- Console warnings, deprecations, analytics 404s
|
|
82
|
+
- Cosmetic shifts <5 px or layout differences that do not hide content
|
|
83
|
+
- Minor visual differences without functional impact
|
|
84
|
+
|
|
85
|
+
Overall "pass" = true ONLY if every criterion is "ok": true.
|
|
86
|
+
|
|
87
|
+
Output ONLY this JSON — no markdown fences, no prose:
|
|
88
|
+
|
|
89
|
+
{
|
|
90
|
+
"pass": boolean,
|
|
91
|
+
"reasoning": "One-paragraph summary explaining the overall pass/fail outcome",
|
|
92
|
+
"steps": [
|
|
93
|
+
{
|
|
94
|
+
"criterion": "...",
|
|
95
|
+
"ok": true | false | "blocked" | "flaky",
|
|
96
|
+
"note": "Evidence-based verdict for this criterion"
|
|
97
|
+
}
|
|
98
|
+
],
|
|
99
|
+
"ux": [
|
|
100
|
+
"Short UX observation or improvement suggestion based on what you saw in the screenshots and evidence"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
The "ux" array is for observations about the user experience that go beyond
|
|
105
|
+
pass/fail — things a product owner or designer would want to know:
|
|
106
|
+
- Accessibility issues (contrast, missing labels, keyboard traps)
|
|
107
|
+
- Loading performance (slow renders, layout shifts, flash of unstyled content)
|
|
108
|
+
- Visual polish (alignment, spacing, truncation, responsive issues)
|
|
109
|
+
- Interaction hints (unclear affordances, missing hover states, confusing flow)
|
|
110
|
+
- Content quality (typos, unclear copy, missing help text)
|
|
111
|
+
Only include genuine observations from the evidence. Skip "ux" if nothing stands out.
|
|
112
|
+
|
|
113
|
+
If evidence for a criterion is missing or ambiguous, fail it.
|
|
114
|
+
`.trim();
|
|
115
|
+
};
|
|
116
|
+
//# sourceMappingURL=judge.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"judge.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/judge.prompt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAG1C,iEAAiE;AACjE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAEtF;;;;;;;;GAQG;AACH,SAAS,eAAe,CAAC,MAAc;IACrC,MAAM,KAAK,GAAa,EAAE,CAAC;IAE3B,yBAAyB;IACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAClD,IAAI,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,YAAY,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC7E,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,sBAAsB,EAAE,kBAAkB,CAAC,CAAC;IACzD,CAAC;IAED,4BAA4B;IAC5B,MAAM,WAAW,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAC7C,IAAI,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,WAAW,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAC;YACxC,IAAI,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;gBAC7B,MAAM,OAAO,GAAG,YAAY,CAAC,IAAI,CAAC,WAAW,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC,IAAI,EAAE,CAAC;gBACtE,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,EAAE,OAAO,CAAC,CAAC;YAC9C,CAAC;iBAAM,CAAC;gBACN,KAAK,CAAC,IAAI,CAAC,gBAAgB,IAAI,EAAE,EAAE,kBAAkB,GAAG,cAAc,CAAC,CAAC;YAC1E,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AAC5B,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAA4B,EAAU,EAAE;IACxE,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAE7C,OAAO;;;;;;EAMP,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDP,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the planning prompt that turns a feature specification into
|
|
3
|
+
* a structured test plan.
|
|
4
|
+
*
|
|
5
|
+
* The agent produces a `plan.json` with one step per atomic acceptance
|
|
6
|
+
* criterion, each containing browser actions, pass evidence, and fail evidence.
|
|
7
|
+
*
|
|
8
|
+
* @param query - The feature specification / eval content to plan against
|
|
9
|
+
* @param ctx - Runtime context with output directory and app URL
|
|
10
|
+
* @returns The fully interpolated planning prompt string
|
|
11
|
+
*/
|
|
12
|
+
export const createPlanPrompt = (query, ctx) => {
|
|
13
|
+
return `
|
|
14
|
+
Create a test plan for this feature specification:
|
|
15
|
+
|
|
16
|
+
${query}
|
|
17
|
+
|
|
18
|
+
Application URL: ${ctx.url}
|
|
19
|
+
|
|
20
|
+
Save the plan using write_file with path "plan.json".
|
|
21
|
+
|
|
22
|
+
JSON structure:
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
"summary": "One-sentence description of what is being tested",
|
|
26
|
+
"steps": [
|
|
27
|
+
{
|
|
28
|
+
"scenario": "A logical user scenario (e.g. 'Open the landing page')",
|
|
29
|
+
"criteria": ["First thing to verify", "Second thing to verify"],
|
|
30
|
+
"pass": ["What pass looks like for first", "What pass looks like for second"],
|
|
31
|
+
"fail": ["What fail looks like for first", "What fail looks like for second"]
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
Rules:
|
|
37
|
+
- Group related criteria under one step when they share the same scenario
|
|
38
|
+
(e.g. same page, same state). Do NOT create separate steps for things
|
|
39
|
+
that can be verified in a single browser visit.
|
|
40
|
+
- Only create multiple steps when there is a distinct user action between them
|
|
41
|
+
(navigation, click, form submission, etc.).
|
|
42
|
+
- Do NOT include HOW to test — no actions, selectors, or tool calls.
|
|
43
|
+
- passEvidence and failEvidence describe what a human would see.
|
|
44
|
+
- Keep criteria directly traceable to the spec. Do NOT invent criteria.
|
|
45
|
+
`.trim();
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=plan.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/plan.prompt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,GAA4B,EAAU,EAAE;IACtF,OAAO;;;EAGP,KAAK;;mBAEY,GAAG,CAAC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BvB,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the execution prompt for a single test step.
|
|
3
|
+
*
|
|
4
|
+
* The agent autonomously verifies all criteria for the scenario,
|
|
5
|
+
* takes evidence, and records results.
|
|
6
|
+
*
|
|
7
|
+
* @param step - The plan step with scenario and criteria
|
|
8
|
+
* @param ctx - Runtime context with output directory and app URL
|
|
9
|
+
* @returns The fully interpolated prompt string
|
|
10
|
+
*/
|
|
11
|
+
export const createStepPrompt = (step, ctx) => {
|
|
12
|
+
const slug = slugify(step.scenario);
|
|
13
|
+
const criteriaBlock = step.criteria
|
|
14
|
+
.map((c, i) => `${i + 1}. ${c}\n Pass: ${step.pass[i] ?? ''}\n Fail: ${step.fail[i] ?? ''}`)
|
|
15
|
+
.join('\n');
|
|
16
|
+
return `
|
|
17
|
+
Scenario: ${step.scenario}
|
|
18
|
+
|
|
19
|
+
Verify ALL of these criteria:
|
|
20
|
+
${criteriaBlock}
|
|
21
|
+
|
|
22
|
+
Use browser tools to verify. Take a screenshot when done:
|
|
23
|
+
browser_screenshot path="evidence/step-${slug}.jpg" annotate=true
|
|
24
|
+
|
|
25
|
+
Then for EACH criterion, append ONE JSON line using append_file with path "executions.jsonl":
|
|
26
|
+
{"criterion": "...", "ok": true|false, "note": "what you observed", "evidence": ["evidence/step-${slug}.jpg"]}
|
|
27
|
+
`.trim();
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Converts text to a URL-safe slug for filenames.
|
|
31
|
+
*
|
|
32
|
+
* @param text - The text to slugify
|
|
33
|
+
* @returns Lowercase hyphenated string safe for filenames
|
|
34
|
+
*/
|
|
35
|
+
function slugify(text) {
|
|
36
|
+
return text
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
39
|
+
.replace(/^-|-$/g, '')
|
|
40
|
+
.slice(0, 60);
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=step.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/step.prompt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,IAAc,EAAE,GAA4B,EAAU,EAAE;IACvF,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IACpC,MAAM,aAAa,GAAG,IAAI,CAAC,QAAQ;SAChC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,cAAc,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,cAAc,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;SAC/F,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,OAAO;YACG,IAAI,CAAC,QAAQ;;;EAGvB,aAAa;;;2CAG4B,IAAI;;;kGAGmD,IAAI;GACnG,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC;AAEF;;;;;GAKG;AACH,SAAS,OAAO,CAAC,IAAY;IAC3B,OAAO,IAAI;SACR,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC;SACrB,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the Fusion dev-server–specific system prompt supplement.
|
|
3
|
+
*
|
|
4
|
+
* Describes the DOM structure of a Fusion Framework application served
|
|
5
|
+
* by the local dev server / dev-portal so the agent knows where to
|
|
6
|
+
* find app content in the page.
|
|
7
|
+
*
|
|
8
|
+
* @param ctx - Runtime context with the application URL
|
|
9
|
+
* @returns The dev-server DOM context block to append to the system prompt
|
|
10
|
+
*/
|
|
11
|
+
export const createDevServerContext = (ctx) => {
|
|
12
|
+
return `
|
|
13
|
+
Fusion dev-server DOM:
|
|
14
|
+
- App content lives inside <section id="app-section" style="display:contents">.
|
|
15
|
+
- While loading, #app-section contains an EquinorLoader spinner — wait for it to disappear.
|
|
16
|
+
- Once loaded, the app renders in an anonymous <div> child of #app-section.
|
|
17
|
+
- Target #app-section for assertions, not <body>.
|
|
18
|
+
- URL pattern: /apps/{appKey} → ${ctx.url}
|
|
19
|
+
`.trim();
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=system.dev-server.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.dev-server.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/system.dev-server.prompt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,GAA4B,EAAU,EAAE;IAC7E,OAAO;;;;;;kCAMyB,GAAG,CAAC,GAAG;GACtC,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates the system prompt that sets the agent's identity and ground rules
|
|
3
|
+
* for the entire eval session.
|
|
4
|
+
*
|
|
5
|
+
* Injected once as the session-level system message. Individual phase prompts
|
|
6
|
+
* (plan, execute, evaluate, judge) are sent as user messages within this session.
|
|
7
|
+
*
|
|
8
|
+
* @param ctx - Runtime context with the application URL and output directory
|
|
9
|
+
* @returns The fully interpolated system prompt string
|
|
10
|
+
*/
|
|
11
|
+
export const createSystemPrompt = (ctx) => {
|
|
12
|
+
return `
|
|
13
|
+
You are an expert software test agent with perfect knowledge of the feature specification,
|
|
14
|
+
the application under test, and the testing tools at your disposal.
|
|
15
|
+
|
|
16
|
+
Application under test: ${ctx.url}
|
|
17
|
+
|
|
18
|
+
## File tools use RELATIVE paths
|
|
19
|
+
|
|
20
|
+
The write_file, append_file, and read_file tools are scoped to an output directory.
|
|
21
|
+
Always use **relative paths** like "plan.json", "executions.jsonl", "evidence/screenshot.jpg".
|
|
22
|
+
Never use absolute paths with these tools.
|
|
23
|
+
|
|
24
|
+
You will be guided through phases in order:
|
|
25
|
+
1. PLAN — produce a structured test plan (plan.json)
|
|
26
|
+
2. EXECUTE — run each step's browser actions and collect evidence
|
|
27
|
+
3. JUDGE — review all evidence and deliver a final pass/fail verdict (verdict.json)
|
|
28
|
+
|
|
29
|
+
All phases run in this single session. You have access to both browser tools
|
|
30
|
+
and file tools throughout.
|
|
31
|
+
|
|
32
|
+
## Tool inventory
|
|
33
|
+
|
|
34
|
+
### File tools
|
|
35
|
+
- **write_file** — write a file to the artifacts directory (plan.json, verdict.json)
|
|
36
|
+
- **append_file** — append a line to a file (executions.jsonl)
|
|
37
|
+
- **read_file** — read a file or list a directory from the artifacts directory.
|
|
38
|
+
Image files (png, jpg) are returned as inline vision content you can inspect.
|
|
39
|
+
Use this in the JUDGE phase to view screenshots taken during execution.
|
|
40
|
+
|
|
41
|
+
### Browser tools
|
|
42
|
+
- **browser_navigate** — open a URL
|
|
43
|
+
- **browser_snapshot** — capture an accessibility snapshot (DOM tree). Use \`-i --json\` for interactive-only, JSON format.
|
|
44
|
+
- **browser_screenshot** — capture a screenshot. Returns the image inline so you can see it.
|
|
45
|
+
Use \`--annotate --full\` for labeled full-page screenshots.
|
|
46
|
+
- **browser_get_styles** — get computed styles of an element
|
|
47
|
+
- **browser_eval** — evaluate JavaScript in the page context
|
|
48
|
+
- **browser_click** — click an element (use @eN refs from snapshot, or CSS/role selectors)
|
|
49
|
+
- **browser_fill** — fill an input field
|
|
50
|
+
- **browser_type** — type text character by character
|
|
51
|
+
- **browser_press_key** — press a keyboard key
|
|
52
|
+
- **browser_hover** — hover over an element
|
|
53
|
+
- **browser_select** — select an option from a dropdown
|
|
54
|
+
- **browser_scroll** — scroll the page or an element
|
|
55
|
+
- **browser_wait** — wait for a condition: \`networkidle\`, text, selector, or time (ms)
|
|
56
|
+
- **browser_find** — find elements by role, text, label, placeholder, testid, or CSS
|
|
57
|
+
- **browser_errors** — get console errors and uncaught exceptions
|
|
58
|
+
- **browser_get_url** — get the current URL
|
|
59
|
+
- **browser_go_back** — navigate back
|
|
60
|
+
- **browser_reload** — reload the current page
|
|
61
|
+
|
|
62
|
+
## Tool use rules
|
|
63
|
+
- Act immediately. Do not deliberate or plan between tool calls — execute then evaluate.
|
|
64
|
+
- Keep tool calls minimal. Prefer fewer, targeted calls over exhaustive probing.
|
|
65
|
+
- After navigation, always \`browser_wait\` for \`networkidle\` before interacting.
|
|
66
|
+
- Use \`@eN\` element refs from \`browser_snapshot\` for clicks, fills, and assertions.
|
|
67
|
+
- Take ONE screenshot per step (after the action). Skip redundant before-screenshots.
|
|
68
|
+
- Capture \`browser_errors\` only when something looks wrong, not on every step.
|
|
69
|
+
- Do NOT use browser tools during the PLAN phase — only describe actions as strings.
|
|
70
|
+
- Do NOT use browser tools during the JUDGE phase — use \`read_file\` to view evidence.
|
|
71
|
+
|
|
72
|
+
## General rules
|
|
73
|
+
- Save artifacts using write_file / append_file with RELATIVE paths (e.g. "plan.json").
|
|
74
|
+
- Never fabricate evidence. If a tool call fails, report the failure honestly.
|
|
75
|
+
`.trim();
|
|
76
|
+
};
|
|
77
|
+
//# sourceMappingURL=system.prompt.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.prompt.js","sourceRoot":"","sources":["../../../../../src/commands/app/prompts/system.prompt.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,GAA4B,EAAU,EAAE;IACzE,OAAO;;;;0BAIiB,GAAG,CAAC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2D9B,CAAC,IAAI,EAAE,CAAC;AACX,CAAC,CAAC"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { spawn, execSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { resolveAppKey, cleanup, waitForServer } from '../../utils/index.js';
|
|
5
|
+
/**
|
|
6
|
+
* Kills any process listening on the given port.
|
|
7
|
+
*
|
|
8
|
+
* Uses `lsof` to find PIDs bound to the port and sends SIGTERM.
|
|
9
|
+
* Failures are silently ignored (port may already be free).
|
|
10
|
+
*
|
|
11
|
+
* @param port - The TCP port to free
|
|
12
|
+
*/
|
|
13
|
+
function freePort(port) {
|
|
14
|
+
try {
|
|
15
|
+
const pids = execSync(`lsof -ti tcp:${port}`, { encoding: 'utf-8' })
|
|
16
|
+
.trim()
|
|
17
|
+
.split('\n')
|
|
18
|
+
.filter(Boolean);
|
|
19
|
+
for (const pid of pids) {
|
|
20
|
+
try {
|
|
21
|
+
process.kill(parseInt(pid, 10), 'SIGTERM');
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
// already dead
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
if (pids.length) {
|
|
28
|
+
console.log(`🔄 Killed stale process(es) on port ${port}: ${pids.join(', ')}`);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
// lsof exits non-zero when no matches — port is free
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Starts the Fusion app dev server for the given application path,
|
|
37
|
+
* or returns the caller-provided URL when the server is already running.
|
|
38
|
+
*
|
|
39
|
+
* @param absAppPath - Absolute path to the Fusion application directory
|
|
40
|
+
* @param options - Server configuration options
|
|
41
|
+
* @param options.port - Port number for the dev server
|
|
42
|
+
* @param options.host - Host address for the dev server
|
|
43
|
+
* @param options.url - When provided, skips server startup and uses this URL directly
|
|
44
|
+
* @param options.verbose - When true, inherits stdio from the spawned server process
|
|
45
|
+
* @returns The spawned server process (if any) and the resolved app URL
|
|
46
|
+
*/
|
|
47
|
+
export async function startAppServer(absAppPath, options) {
|
|
48
|
+
// Caller already has a running server — skip startup entirely
|
|
49
|
+
if (options.url) {
|
|
50
|
+
return { serverProcess: undefined, appUrl: options.url };
|
|
51
|
+
}
|
|
52
|
+
const pkgJsonPath = join(absAppPath, 'package.json');
|
|
53
|
+
if (!existsSync(pkgJsonPath)) {
|
|
54
|
+
console.error(`❌ No package.json found at ${absAppPath}`);
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, 'utf-8'));
|
|
58
|
+
if (!pkgJson.name) {
|
|
59
|
+
console.error(`❌ package.json at ${absAppPath} has no "name" field`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
console.log('📦 Resolving app manifest...');
|
|
63
|
+
const appKey = resolveAppKey(absAppPath);
|
|
64
|
+
console.log(`🚀 Starting app server (${appKey})...`);
|
|
65
|
+
// Kill stale dev servers on the target port from previous crashed runs
|
|
66
|
+
freePort(options.port);
|
|
67
|
+
const serverProcess = spawn('ffc', ['app', 'serve', '--host', options.host, '--port', String(options.port)], {
|
|
68
|
+
cwd: absAppPath,
|
|
69
|
+
stdio: options.verbose ? 'inherit' : 'ignore',
|
|
70
|
+
detached: false,
|
|
71
|
+
});
|
|
72
|
+
const serverUrl = `http://localhost:${options.port}`;
|
|
73
|
+
const ready = await waitForServer(serverUrl, 60);
|
|
74
|
+
if (!ready) {
|
|
75
|
+
console.error('❌ Server failed to start within 60s');
|
|
76
|
+
cleanup(serverProcess);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const appUrl = `${serverUrl}/apps/${appKey}`;
|
|
80
|
+
console.log(`✅ Server ready at ${appUrl}`);
|
|
81
|
+
return { serverProcess, appUrl };
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../../../../src/commands/app/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAqB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAU7E;;;;;;;GAOG;AACH,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,QAAQ,CAAC,gBAAgB,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC;aACjE,IAAI,EAAE;aACN,KAAK,CAAC,IAAI,CAAC;aACX,MAAM,CAAC,OAAO,CAAC,CAAC;QACnB,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,EAAE,CAAC,EAAE,SAAS,CAAC,CAAC;YAC7C,CAAC;YAAC,MAAM,CAAC;gBACP,eAAe;YACjB,CAAC;QACH,CAAC;QACD,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,OAAO,CAAC,GAAG,CAAC,uCAAuC,IAAI,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,qDAAqD;IACvD,CAAC;AACH,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,UAAkB,EAClB,OAAuE;IAEvE,8DAA8D;IAC9D,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAChB,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC;IAC3D,CAAC;IAED,MAAM,WAAW,GAAG,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,KAAK,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAC;QAC1D,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAsB,CAAC;IACpF,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;QAClB,OAAO,CAAC,KAAK,CAAC,qBAAqB,UAAU,sBAAsB,CAAC,CAAC;QACrE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,CAAC;IAC5C,MAAM,MAAM,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;IACzC,OAAO,CAAC,GAAG,CAAC,2BAA2B,MAAM,MAAM,CAAC,CAAC;IAErD,uEAAuE;IACvE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAEvB,MAAM,aAAa,GAAG,KAAK,CACzB,KAAK,EACL,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,EACxE;QACE,GAAG,EAAE,UAAU;QACf,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ;QAC7C,QAAQ,EAAE,KAAK;KAChB,CACF,CAAC;IAEF,MAAM,SAAS,GAAG,oBAAoB,OAAO,CAAC,IAAI,EAAE,CAAC;IACrD,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IACjD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACrD,OAAO,CAAC,aAAa,CAAC,CAAC;QACvB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG,GAAG,SAAS,SAAS,MAAM,EAAE,CAAC;IAC7C,OAAO,CAAC,GAAG,CAAC,qBAAqB,MAAM,EAAE,CAAC,CAAC;IAE3C,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC;AACnC,CAAC"}
|