@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.
Files changed (186) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/LICENSE +21 -0
  3. package/README.md +228 -0
  4. package/dist/esm/commands/app/command.js +176 -0
  5. package/dist/esm/commands/app/command.js.map +1 -0
  6. package/dist/esm/commands/app/eval.js +92 -0
  7. package/dist/esm/commands/app/eval.js.map +1 -0
  8. package/dist/esm/commands/app/format.js +93 -0
  9. package/dist/esm/commands/app/format.js.map +1 -0
  10. package/dist/esm/commands/app/login.js +71 -0
  11. package/dist/esm/commands/app/login.js.map +1 -0
  12. package/dist/esm/commands/app/prompts/execute-step.prompt.js +44 -0
  13. package/dist/esm/commands/app/prompts/execute-step.prompt.js.map +1 -0
  14. package/dist/esm/commands/app/prompts/judge.prompt.js +116 -0
  15. package/dist/esm/commands/app/prompts/judge.prompt.js.map +1 -0
  16. package/dist/esm/commands/app/prompts/plan.prompt.js +47 -0
  17. package/dist/esm/commands/app/prompts/plan.prompt.js.map +1 -0
  18. package/dist/esm/commands/app/prompts/step.prompt.js +42 -0
  19. package/dist/esm/commands/app/prompts/step.prompt.js.map +1 -0
  20. package/dist/esm/commands/app/prompts/system.dev-server.prompt.js +21 -0
  21. package/dist/esm/commands/app/prompts/system.dev-server.prompt.js.map +1 -0
  22. package/dist/esm/commands/app/prompts/system.prompt.js +77 -0
  23. package/dist/esm/commands/app/prompts/system.prompt.js.map +1 -0
  24. package/dist/esm/commands/app/server.js +83 -0
  25. package/dist/esm/commands/app/server.js.map +1 -0
  26. package/dist/esm/commands/app/session-logger.js +171 -0
  27. package/dist/esm/commands/app/session-logger.js.map +1 -0
  28. package/dist/esm/commands/app/tools/click.js +27 -0
  29. package/dist/esm/commands/app/tools/click.js.map +1 -0
  30. package/dist/esm/commands/app/tools/errors.js +24 -0
  31. package/dist/esm/commands/app/tools/errors.js.map +1 -0
  32. package/dist/esm/commands/app/tools/eval-js.js +36 -0
  33. package/dist/esm/commands/app/tools/eval-js.js.map +1 -0
  34. package/dist/esm/commands/app/tools/fill.js +28 -0
  35. package/dist/esm/commands/app/tools/fill.js.map +1 -0
  36. package/dist/esm/commands/app/tools/find.js +44 -0
  37. package/dist/esm/commands/app/tools/find.js.map +1 -0
  38. package/dist/esm/commands/app/tools/get-styles.js +36 -0
  39. package/dist/esm/commands/app/tools/get-styles.js.map +1 -0
  40. package/dist/esm/commands/app/tools/get-url.js +24 -0
  41. package/dist/esm/commands/app/tools/get-url.js.map +1 -0
  42. package/dist/esm/commands/app/tools/go-back.js +15 -0
  43. package/dist/esm/commands/app/tools/go-back.js.map +1 -0
  44. package/dist/esm/commands/app/tools/hover.js +24 -0
  45. package/dist/esm/commands/app/tools/hover.js.map +1 -0
  46. package/dist/esm/commands/app/tools/index.js +32 -0
  47. package/dist/esm/commands/app/tools/index.js.map +1 -0
  48. package/dist/esm/commands/app/tools/navigate.js +31 -0
  49. package/dist/esm/commands/app/tools/navigate.js.map +1 -0
  50. package/dist/esm/commands/app/tools/press-key.js +24 -0
  51. package/dist/esm/commands/app/tools/press-key.js.map +1 -0
  52. package/dist/esm/commands/app/tools/read-file.js +98 -0
  53. package/dist/esm/commands/app/tools/read-file.js.map +1 -0
  54. package/dist/esm/commands/app/tools/registry.js +34 -0
  55. package/dist/esm/commands/app/tools/registry.js.map +1 -0
  56. package/dist/esm/commands/app/tools/reload.js +15 -0
  57. package/dist/esm/commands/app/tools/reload.js.map +1 -0
  58. package/dist/esm/commands/app/tools/screenshot.js +133 -0
  59. package/dist/esm/commands/app/tools/screenshot.js.map +1 -0
  60. package/dist/esm/commands/app/tools/scroll.js +41 -0
  61. package/dist/esm/commands/app/tools/scroll.js.map +1 -0
  62. package/dist/esm/commands/app/tools/select.js +25 -0
  63. package/dist/esm/commands/app/tools/select.js.map +1 -0
  64. package/dist/esm/commands/app/tools/snapshot.js +24 -0
  65. package/dist/esm/commands/app/tools/snapshot.js.map +1 -0
  66. package/dist/esm/commands/app/tools/type-text.js +29 -0
  67. package/dist/esm/commands/app/tools/type-text.js.map +1 -0
  68. package/dist/esm/commands/app/tools/types.js +2 -0
  69. package/dist/esm/commands/app/tools/types.js.map +1 -0
  70. package/dist/esm/commands/app/tools/wait.js +39 -0
  71. package/dist/esm/commands/app/tools/wait.js.map +1 -0
  72. package/dist/esm/commands/app/tools/write-file.js +98 -0
  73. package/dist/esm/commands/app/tools/write-file.js.map +1 -0
  74. package/dist/esm/commands/app/types.js +2 -0
  75. package/dist/esm/commands/app/types.js.map +1 -0
  76. package/dist/esm/eval-resolve.js +83 -0
  77. package/dist/esm/eval-resolve.js.map +1 -0
  78. package/dist/esm/index.js +18 -0
  79. package/dist/esm/index.js.map +1 -0
  80. package/dist/esm/utils/agent-browser.js +67 -0
  81. package/dist/esm/utils/agent-browser.js.map +1 -0
  82. package/dist/esm/utils/browser-poll.js +26 -0
  83. package/dist/esm/utils/browser-poll.js.map +1 -0
  84. package/dist/esm/utils/daemon.js +83 -0
  85. package/dist/esm/utils/daemon.js.map +1 -0
  86. package/dist/esm/utils/index.js +6 -0
  87. package/dist/esm/utils/index.js.map +1 -0
  88. package/dist/esm/utils/process.js +30 -0
  89. package/dist/esm/utils/process.js.map +1 -0
  90. package/dist/esm/utils/server.js +65 -0
  91. package/dist/esm/utils/server.js.map +1 -0
  92. package/dist/esm/version.js +3 -0
  93. package/dist/esm/version.js.map +1 -0
  94. package/dist/tsconfig.tsbuildinfo +1 -0
  95. package/dist/types/commands/app/command.d.ts +7 -0
  96. package/dist/types/commands/app/eval.d.ts +9 -0
  97. package/dist/types/commands/app/format.d.ts +14 -0
  98. package/dist/types/commands/app/login.d.ts +14 -0
  99. package/dist/types/commands/app/prompts/execute-step.prompt.d.ts +13 -0
  100. package/dist/types/commands/app/prompts/judge.prompt.d.ts +15 -0
  101. package/dist/types/commands/app/prompts/plan.prompt.d.ts +13 -0
  102. package/dist/types/commands/app/prompts/step.prompt.d.ts +12 -0
  103. package/dist/types/commands/app/prompts/system.dev-server.prompt.d.ts +12 -0
  104. package/dist/types/commands/app/prompts/system.prompt.d.ts +12 -0
  105. package/dist/types/commands/app/server.d.ts +26 -0
  106. package/dist/types/commands/app/session-logger.d.ts +14 -0
  107. package/dist/types/commands/app/tools/click.d.ts +9 -0
  108. package/dist/types/commands/app/tools/errors.d.ts +9 -0
  109. package/dist/types/commands/app/tools/eval-js.d.ts +13 -0
  110. package/dist/types/commands/app/tools/fill.d.ts +9 -0
  111. package/dist/types/commands/app/tools/find.d.ts +9 -0
  112. package/dist/types/commands/app/tools/get-styles.d.ts +13 -0
  113. package/dist/types/commands/app/tools/get-url.d.ts +9 -0
  114. package/dist/types/commands/app/tools/go-back.d.ts +9 -0
  115. package/dist/types/commands/app/tools/hover.d.ts +9 -0
  116. package/dist/types/commands/app/tools/index.d.ts +9 -0
  117. package/dist/types/commands/app/tools/navigate.d.ts +9 -0
  118. package/dist/types/commands/app/tools/press-key.d.ts +9 -0
  119. package/dist/types/commands/app/tools/read-file.d.ts +13 -0
  120. package/dist/types/commands/app/tools/registry.d.ts +28 -0
  121. package/dist/types/commands/app/tools/reload.d.ts +9 -0
  122. package/dist/types/commands/app/tools/screenshot.d.ts +9 -0
  123. package/dist/types/commands/app/tools/scroll.d.ts +9 -0
  124. package/dist/types/commands/app/tools/select.d.ts +9 -0
  125. package/dist/types/commands/app/tools/snapshot.d.ts +9 -0
  126. package/dist/types/commands/app/tools/type-text.d.ts +9 -0
  127. package/dist/types/commands/app/tools/types.d.ts +12 -0
  128. package/dist/types/commands/app/tools/wait.d.ts +9 -0
  129. package/dist/types/commands/app/tools/write-file.d.ts +19 -0
  130. package/dist/types/commands/app/types.d.ts +68 -0
  131. package/dist/types/eval-resolve.d.ts +31 -0
  132. package/dist/types/index.d.ts +15 -0
  133. package/dist/types/utils/agent-browser.d.ts +26 -0
  134. package/dist/types/utils/browser-poll.d.ts +9 -0
  135. package/dist/types/utils/daemon.d.ts +16 -0
  136. package/dist/types/utils/index.d.ts +5 -0
  137. package/dist/types/utils/process.d.ts +21 -0
  138. package/dist/types/utils/server.d.ts +26 -0
  139. package/dist/types/version.d.ts +1 -0
  140. package/package.json +57 -0
  141. package/src/commands/app/command.ts +204 -0
  142. package/src/commands/app/eval.ts +114 -0
  143. package/src/commands/app/format.ts +110 -0
  144. package/src/commands/app/login.ts +92 -0
  145. package/src/commands/app/prompts/execute-step.prompt.ts +48 -0
  146. package/src/commands/app/prompts/judge.prompt.ts +121 -0
  147. package/src/commands/app/prompts/plan.prompt.ts +48 -0
  148. package/src/commands/app/prompts/step.prompt.ts +45 -0
  149. package/src/commands/app/prompts/system.dev-server.prompt.ts +22 -0
  150. package/src/commands/app/prompts/system.prompt.ts +78 -0
  151. package/src/commands/app/server.ts +105 -0
  152. package/src/commands/app/session-logger.ts +187 -0
  153. package/src/commands/app/tools/click.ts +28 -0
  154. package/src/commands/app/tools/errors.ts +26 -0
  155. package/src/commands/app/tools/eval-js.ts +40 -0
  156. package/src/commands/app/tools/fill.ts +29 -0
  157. package/src/commands/app/tools/find.ts +49 -0
  158. package/src/commands/app/tools/get-styles.ts +40 -0
  159. package/src/commands/app/tools/get-url.ts +26 -0
  160. package/src/commands/app/tools/go-back.ts +16 -0
  161. package/src/commands/app/tools/hover.ts +25 -0
  162. package/src/commands/app/tools/index.ts +57 -0
  163. package/src/commands/app/tools/navigate.ts +32 -0
  164. package/src/commands/app/tools/press-key.ts +25 -0
  165. package/src/commands/app/tools/read-file.ts +113 -0
  166. package/src/commands/app/tools/registry.ts +38 -0
  167. package/src/commands/app/tools/reload.ts +16 -0
  168. package/src/commands/app/tools/screenshot.ts +168 -0
  169. package/src/commands/app/tools/scroll.ts +45 -0
  170. package/src/commands/app/tools/select.ts +26 -0
  171. package/src/commands/app/tools/snapshot.ts +27 -0
  172. package/src/commands/app/tools/type-text.ts +30 -0
  173. package/src/commands/app/tools/types.ts +15 -0
  174. package/src/commands/app/tools/wait.ts +43 -0
  175. package/src/commands/app/tools/write-file.ts +104 -0
  176. package/src/commands/app/types.ts +76 -0
  177. package/src/eval-resolve.ts +113 -0
  178. package/src/index.ts +20 -0
  179. package/src/utils/agent-browser.ts +66 -0
  180. package/src/utils/browser-poll.ts +27 -0
  181. package/src/utils/daemon.ts +84 -0
  182. package/src/utils/index.ts +5 -0
  183. package/src/utils/process.ts +33 -0
  184. package/src/utils/server.ts +66 -0
  185. package/src/version.ts +2 -0
  186. 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":"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"}