@gherkle/runner 0.1.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 (59) hide show
  1. package/README.md +482 -0
  2. package/bin/gherkle-runner.js +111 -0
  3. package/dist/__tests__/agent-executor-selection.test.d.ts +15 -0
  4. package/dist/__tests__/agent-executor-selection.test.d.ts.map +1 -0
  5. package/dist/__tests__/agent-executor-selection.test.js +192 -0
  6. package/dist/__tests__/agent-executor-selection.test.js.map +1 -0
  7. package/dist/__tests__/envelope-mapper.test.d.ts +17 -0
  8. package/dist/__tests__/envelope-mapper.test.d.ts.map +1 -0
  9. package/dist/__tests__/envelope-mapper.test.js +217 -0
  10. package/dist/__tests__/envelope-mapper.test.js.map +1 -0
  11. package/dist/__tests__/gherkin-parser.test.d.ts +2 -0
  12. package/dist/__tests__/gherkin-parser.test.d.ts.map +1 -0
  13. package/dist/__tests__/gherkin-parser.test.js +375 -0
  14. package/dist/__tests__/gherkin-parser.test.js.map +1 -0
  15. package/dist/__tests__/integration-fork.test.d.ts +16 -0
  16. package/dist/__tests__/integration-fork.test.d.ts.map +1 -0
  17. package/dist/__tests__/integration-fork.test.js +141 -0
  18. package/dist/__tests__/integration-fork.test.js.map +1 -0
  19. package/dist/__tests__/tmpdir-materialization.test.d.ts +18 -0
  20. package/dist/__tests__/tmpdir-materialization.test.d.ts.map +1 -0
  21. package/dist/__tests__/tmpdir-materialization.test.js +177 -0
  22. package/dist/__tests__/tmpdir-materialization.test.js.map +1 -0
  23. package/dist/agent.d.ts +71 -0
  24. package/dist/agent.d.ts.map +1 -0
  25. package/dist/agent.js +312 -0
  26. package/dist/agent.js.map +1 -0
  27. package/dist/config.d.ts +31 -0
  28. package/dist/config.d.ts.map +1 -0
  29. package/dist/config.js +165 -0
  30. package/dist/config.js.map +1 -0
  31. package/dist/frameworks/cucumber-subprocess.d.ts +129 -0
  32. package/dist/frameworks/cucumber-subprocess.d.ts.map +1 -0
  33. package/dist/frameworks/cucumber-subprocess.js +469 -0
  34. package/dist/frameworks/cucumber-subprocess.js.map +1 -0
  35. package/dist/frameworks/playwright.d.ts +22 -0
  36. package/dist/frameworks/playwright.d.ts.map +1 -0
  37. package/dist/frameworks/playwright.js +234 -0
  38. package/dist/frameworks/playwright.js.map +1 -0
  39. package/dist/gherkin-parser.d.ts +22 -0
  40. package/dist/gherkin-parser.d.ts.map +1 -0
  41. package/dist/gherkin-parser.js +148 -0
  42. package/dist/gherkin-parser.js.map +1 -0
  43. package/dist/index.d.ts +10 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +31 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/step-registry.d.ts +30 -0
  48. package/dist/step-registry.d.ts.map +1 -0
  49. package/dist/step-registry.js +160 -0
  50. package/dist/step-registry.js.map +1 -0
  51. package/dist/types.d.ts +117 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +6 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/workers/cucumber-worker.d.ts +29 -0
  56. package/dist/workers/cucumber-worker.d.ts.map +1 -0
  57. package/dist/workers/cucumber-worker.js +191 -0
  58. package/dist/workers/cucumber-worker.js.map +1 -0
  59. package/package.json +74 -0
package/README.md ADDED
@@ -0,0 +1,482 @@
1
+ # @gherkle/runner
2
+
3
+ Test runner agent for Gherkle. Connects to the Gherkle platform over WebSocket, receives Gherkin feature files, executes them against a configured test framework, and streams results back in real time.
4
+
5
+ ## Installation
6
+
7
+ ### Option 1 — npm (recommended)
8
+
9
+ Global install gives you the `gherkle-runner` CLI:
10
+
11
+ ```bash
12
+ npm install -g @gherkle/runner @playwright/test playwright-core
13
+ npx playwright install chromium
14
+ gherkle-runner start --token ghr_xxxxx --url https://gherkle-runner.osita.ai
15
+ ```
16
+
17
+ Or one-shot via `npx` (no global install):
18
+
19
+ ```bash
20
+ npm install @playwright/test playwright-core
21
+ npx playwright install chromium
22
+ npx @gherkle/runner start --token ghr_xxxxx --url https://gherkle-runner.osita.ai
23
+ ```
24
+
25
+ `@playwright/test` and `playwright-core` are peer dependencies — install
26
+ them alongside the runner. AI-generated step definitions import `expect`
27
+ from `@playwright/test`, and the bundled Cucumber executor drives
28
+ `playwright-core` for browser automation.
29
+
30
+ ### Option 2 — Docker
31
+
32
+ A prebuilt image with browsers preinstalled is available at the
33
+ monorepo's `packages/runner/Dockerfile.browsers`. Build locally:
34
+
35
+ ```bash
36
+ git clone https://github.com/rvasqz86/gherkle-monorepo.git
37
+ cd gherkle-monorepo
38
+ docker build -t gherkle-runner -f packages/runner/Dockerfile.browsers .
39
+ docker run --rm \
40
+ -e GHERKLE_API_URL=https://gherkle-runner.osita.ai \
41
+ -e GHERKLE_TOKEN=ghr_xxxxx \
42
+ gherkle-runner
43
+ ```
44
+
45
+ ### Option 3 — From source (for development)
46
+
47
+ ```bash
48
+ git clone https://github.com/rvasqz86/gherkle-monorepo.git
49
+ cd gherkle-monorepo
50
+ npm install
51
+ npm run build --workspace=@gherkle/runner
52
+ npx playwright install chromium
53
+ node packages/runner/bin/gherkle-runner.js start \
54
+ --token ghr_xxxxx \
55
+ --url https://gherkle-runner.osita.ai
56
+ ```
57
+
58
+ ## How runs work
59
+
60
+ When you click **Run** in Gherkle the server pushes the feature file AND the
61
+ saved AI-generated step definitions (`step_definition_files` table) to your
62
+ runner over the existing WebSocket. The runner materializes a self-contained
63
+ temp project on disk, forks `@cucumber/cucumber`, and streams pass/fail
64
+ results back. **No step-definition code needs to exist on the runner machine.**
65
+
66
+ For the BYO workflow (your own step-defs already committed to disk), set
67
+ `stepDefinitionsPath` in `gherkle-runner.config.js` and dispatch with a
68
+ project that has no saved step-defs — the runner falls back to the legacy
69
+ Playwright executor against your local files.
70
+
71
+ ## Quick Start
72
+
73
+ ### 1. Generate a config file
74
+
75
+ ```bash
76
+ gherkle-runner init
77
+ ```
78
+
79
+ This creates `gherkle-runner.config.js` in the current directory.
80
+
81
+ ### 2. Set your runner token
82
+
83
+ ```bash
84
+ export GHERKLE_TOKEN=ghr_xxxxx
85
+ ```
86
+
87
+ ### 3. Start the runner
88
+
89
+ ```bash
90
+ gherkle-runner start
91
+ ```
92
+
93
+ The runner connects to Gherkle and waits for test runs. It automatically reconnects if the connection drops.
94
+
95
+ ## CLI Reference
96
+
97
+ ```
98
+ gherkle-runner <command> [options]
99
+ ```
100
+
101
+ ### `start`
102
+
103
+ Start the runner agent and connect to Gherkle.
104
+
105
+ | Option | Description |
106
+ | ----------------------------- | ------------------------------------------------------ |
107
+ | `-t, --token <token>` | Runner token (overrides `GHERKLE_TOKEN` env var) |
108
+ | `-n, --name <name>` | Runner name |
109
+ | `-u, --url <url>` | Gherkle API URL |
110
+ | `-f, --framework <framework>` | Test framework: `playwright`, `cypress`, or `cucumber` |
111
+ | `--headless` | Run browsers in headless mode (default: `true`) |
112
+ | `--no-headless` | Run browsers with visible UI |
113
+
114
+ ```bash
115
+ gherkle-runner start --token ghr_xxxxx --name ci-runner --framework playwright
116
+ ```
117
+
118
+ ### `init`
119
+
120
+ Generate a sample `gherkle-runner.config.js` in the current directory. Fails if one already exists.
121
+
122
+ ```bash
123
+ gherkle-runner init
124
+ ```
125
+
126
+ ### `status`
127
+
128
+ Print runner version, config file detection, and token status.
129
+
130
+ ```bash
131
+ gherkle-runner status
132
+ ```
133
+
134
+ ## Configuration
135
+
136
+ Configuration is resolved by merging sources in this order (later wins):
137
+
138
+ 1. Built-in defaults
139
+ 2. Config file
140
+ 3. Environment variables
141
+ 4. CLI options
142
+
143
+ ### Config File
144
+
145
+ The runner looks for config files in the working directory, checking these names in order:
146
+
147
+ - `gherkle-runner.config.js`
148
+ - `gherkle-runner.config.mjs`
149
+ - `gherkle-runner.config.ts`
150
+ - `.gherklerc.js`
151
+ - `.gherklerc.json`
152
+
153
+ Example config:
154
+
155
+ ```js
156
+ // gherkle-runner.config.js
157
+ module.exports = {
158
+ // Connection (required)
159
+ token: process.env.GHERKLE_TOKEN,
160
+
161
+ // Runner identification
162
+ name: "my-runner",
163
+ labels: ["local", "dev"],
164
+
165
+ // Test framework
166
+ framework: "playwright", // 'playwright' | 'cypress' | 'cucumber'
167
+
168
+ // Paths
169
+ featuresPath: "./features",
170
+ stepDefinitionsPath: "./steps",
171
+ supportPath: "./support",
172
+
173
+ // Browser configuration (E2E only)
174
+ browsers: ["chromium"],
175
+ headless: true,
176
+
177
+ // Timeouts
178
+ testTimeout: 30000,
179
+ stepTimeout: 10000,
180
+
181
+ // Artifacts
182
+ screenshots: "on-failure", // 'always' | 'on-failure' | 'never'
183
+ video: "never", // 'always' | 'on-failure' | 'never'
184
+
185
+ // Environment
186
+ baseUrl: process.env.BASE_URL || "http://localhost:3000",
187
+ env: {
188
+ API_URL: process.env.API_URL,
189
+ },
190
+ };
191
+ ```
192
+
193
+ ### Environment Variables
194
+
195
+ | Variable | Maps to |
196
+ | --------------------- | -------- |
197
+ | `GHERKLE_TOKEN` | `token` |
198
+ | `GHERKLE_API_URL` | `apiUrl` |
199
+ | `GHERKLE_RUNNER_NAME` | `name` |
200
+
201
+ ### Config Options Reference
202
+
203
+ | Option | Type | Default | Description |
204
+ | --------------------- | ---------- | ------------------------- | ------------------------------------------------------------ |
205
+ | `token` | `string` | **required** | Runner authentication token |
206
+ | `apiUrl` | `string` | `https://app.gherkle.com` | Gherkle platform URL |
207
+ | `name` | `string` | `gherkle-runner` | Display name for this runner |
208
+ | `labels` | `string[]` | `[]` | Labels for filtering/routing runs to this runner |
209
+ | `framework` | `string` | `playwright` | Test framework: `playwright`, `cypress`, or `cucumber` |
210
+ | `featuresPath` | `string` | `./features` | Directory containing `.feature` files |
211
+ | `stepDefinitionsPath` | `string` | `./steps` | Directory containing step definition files |
212
+ | `supportPath` | `string` | `./support` | Directory containing support/helper files |
213
+ | `browsers` | `string[]` | `['chromium']` | Browsers to use: `chromium`, `firefox`, `webkit` |
214
+ | `headless` | `boolean` | `true` | Run browsers without UI |
215
+ | `testTimeout` | `number` | `30000` | Max duration per test scenario (ms) |
216
+ | `stepTimeout` | `number` | `10000` | Max duration per step (ms) |
217
+ | `screenshots` | `string` | `on-failure` | When to capture screenshots: `always`, `on-failure`, `never` |
218
+ | `video` | `string` | `never` | When to record video: `always`, `on-failure`, `never` |
219
+ | `baseUrl` | `string` | `undefined` | Base URL for the application under test |
220
+ | `env` | `object` | `{}` | Key-value pairs passed to step definitions as `context.env` |
221
+
222
+ ## Writing Step Definitions
223
+
224
+ Step definition files are loaded from `stepDefinitionsPath`. The runner scans recursively for files matching:
225
+
226
+ - `*.steps.ts` / `*.steps.js`
227
+ - `*.step.ts` / `*.step.js`
228
+ - `*_steps.ts` / `*_steps.js`
229
+
230
+ ### Step File Format
231
+
232
+ A step file exports a function that receives registration helpers:
233
+
234
+ ```js
235
+ // steps/login.steps.js
236
+ module.exports = function ({ Given, When, Then }) {
237
+ Given(/the user is on the login page/, async (context) => {
238
+ await context.page.goto(`${context.baseUrl}/login`);
239
+ });
240
+
241
+ When(
242
+ /the user enters "(.*)" and "(.*)"/,
243
+ async (context, username, password) => {
244
+ await context.page.fill("#username", username);
245
+ await context.page.fill("#password", password);
246
+ await context.page.click('button[type="submit"]');
247
+ },
248
+ );
249
+
250
+ Then(/the user sees the dashboard/, async (context) => {
251
+ await context.page.waitForURL("**/dashboard");
252
+ });
253
+ };
254
+ ```
255
+
256
+ ### Step Context
257
+
258
+ Every step function receives a `StepContext` object as its first argument:
259
+
260
+ | Property | Type | Description |
261
+ | ----------- | --------------------------- | -------------------------------------------------- |
262
+ | `page` | `playwright.Page` | Playwright Page instance for the current scenario |
263
+ | `context` | `playwright.BrowserContext` | Playwright BrowserContext for the current scenario |
264
+ | `baseUrl` | `string \| undefined` | The configured `baseUrl` |
265
+ | `env` | `Record<string, string>` | Environment variables from config |
266
+ | `dataTable` | `string[][] \| undefined` | Data table attached to the current step |
267
+ | `docString` | `string \| undefined` | Doc string attached to the current step |
268
+
269
+ Capture groups from the step pattern regex are passed as additional string arguments after the context.
270
+
271
+ ### Keyword Matching
272
+
273
+ Steps registered with `Given`, `When`, or `Then` are matched by keyword first. Steps using `And` or `But` in the feature file inherit their effective keyword from the preceding step. If no keyword-specific match is found, the runner falls back to matching against all registered steps regardless of keyword.
274
+
275
+ ## Gherkin Parser
276
+
277
+ The runner includes a built-in Gherkin parser (`@cucumber/gherkin`) that supports:
278
+
279
+ - Feature descriptions and tags
280
+ - Scenarios with Given/When/Then/And/But steps
281
+ - Background sections (run before each scenario)
282
+ - Scenario Outlines with Examples tables (expanded into individual scenarios)
283
+ - Data Tables on steps
284
+ - Doc Strings on steps
285
+ - Multiple Examples blocks per outline
286
+ - Tags at feature, scenario, and examples level
287
+
288
+ ## Architecture
289
+
290
+ ```
291
+ bin/gherkle-runner.js CLI entrypoint (commander)
292
+ src/
293
+ index.ts Public API exports
294
+ types.ts Type definitions and WebSocket message types
295
+ config.ts Config loading, merging, validation
296
+ agent.ts GherkleAgent - WebSocket client, run orchestration
297
+ step-registry.ts Step definition registry and matching
298
+ gherkin-parser.ts Gherkin AST parsing
299
+ frameworks/
300
+ playwright.ts Playwright executor (browser launch, scenario execution)
301
+ ```
302
+
303
+ ### How It Works
304
+
305
+ 1. **Connect** -- `GherkleAgent` opens a WebSocket connection to the Gherkle platform, authenticating with the runner token.
306
+ 2. **Wait** -- The agent sends heartbeats every 30 seconds and waits for an `execute` message.
307
+ 3. **Execute** -- When a run is dispatched, the agent parses the received Gherkin feature files, loads step definitions, launches a browser, and runs each scenario.
308
+ 4. **Stream** -- Step and scenario results are sent back to Gherkle in real time over the WebSocket (`scenario:started`, `step:completed`, `scenario:completed`).
309
+ 5. **Artifacts** -- On step failure (when `screenshots` is not `never`), a full-page screenshot is captured and reported as an artifact. Screenshots are saved to `.gherkle-artifacts/` in the working directory.
310
+ 6. **Complete** -- A `run:completed` message with the full summary is sent when all features finish.
311
+
312
+ The agent automatically reconnects after 5 seconds if the WebSocket connection drops.
313
+
314
+ ## Programmatic API
315
+
316
+ The package exports the agent and config loader for use in custom tooling:
317
+
318
+ ```ts
319
+ import { GherkleAgent, loadConfig } from "@gherkle/runner";
320
+
321
+ const config = loadConfig({
322
+ token: "ghr_xxxxx",
323
+ framework: "playwright",
324
+ });
325
+
326
+ const agent = new GherkleAgent(config);
327
+
328
+ // Handle shutdown
329
+ process.on("SIGINT", () => agent.stop());
330
+
331
+ await agent.start();
332
+ ```
333
+
334
+ ### Exports
335
+
336
+ | Export | Description |
337
+ | ------------------------ | ------------------------------------------------------------------------------------ |
338
+ | `GherkleAgent` | Main agent class. Call `start()` to connect, `stop()` to disconnect. |
339
+ | `loadConfig(overrides?)` | Load and merge config from file, env, and provided overrides. Returns `AgentConfig`. |
340
+ | `AgentConfig` | Configuration interface |
341
+ | `RunnerStatus` | `'online' \| 'offline' \| 'busy' \| 'error'` |
342
+ | `TestStatus` | `'pending' \| 'running' \| 'passed' \| 'failed' \| 'skipped'` |
343
+ | `Framework` | `'playwright' \| 'cypress' \| 'cucumber'` |
344
+ | `StepResult` | Result of a single step execution |
345
+ | `ScenarioResult` | Result of a scenario including all steps and artifacts |
346
+ | `RunSummary` | Aggregate pass/fail/skip counts and duration |
347
+ | `FeatureContent` | Feature file id, name, path, and raw content |
348
+ | `TestConfig` | Framework and path configuration for a test run |
349
+ | `ArtifactInfo` | Metadata for a captured artifact (screenshot, video, trace, log) |
350
+ | `RunnerMessage` | Union type of all messages sent from runner to server |
351
+ | `ServerMessage` | Union type of all messages sent from server to runner |
352
+
353
+ ## Docker
354
+
355
+ Two Dockerfiles are provided depending on your workload:
356
+
357
+ | File | Base image | Size | Use case |
358
+ | --------------------- | ------------------------------ | ------- | ----------------------------------------------------------------- |
359
+ | `Dockerfile` | `node:22-slim` | ~200 MB | Step definitions, assertions, API tests, any non-browser workload |
360
+ | `Dockerfile.browsers` | `mcr.microsoft.com/playwright` | ~2 GB | E2E tests requiring Chromium, Firefox, or WebKit |
361
+
362
+ Use the default `Dockerfile` unless your step definitions launch a browser.
363
+
364
+ ### Build
365
+
366
+ Both Dockerfiles must be built from the **monorepo root** so the workspace lockfile is available:
367
+
368
+ ```bash
369
+ # Standard image (step definitions + assertions, no browsers)
370
+ docker build -t gherkle-runner -f packages/runner/Dockerfile .
371
+
372
+ # With Playwright browsers for E2E
373
+ docker build -t gherkle-runner-browsers -f packages/runner/Dockerfile.browsers .
374
+ ```
375
+
376
+ ### Run
377
+
378
+ ```bash
379
+ docker run --rm \
380
+ -e GHERKLE_TOKEN=ghr_xxxxx \
381
+ gherkle-runner
382
+ ```
383
+
384
+ ### Mount step definitions and features
385
+
386
+ Bind-mount your project directories so the runner can find your test code:
387
+
388
+ ```bash
389
+ docker run --rm \
390
+ -e GHERKLE_TOKEN=ghr_xxxxx \
391
+ -v $(pwd)/features:/app/features \
392
+ -v $(pwd)/steps:/app/steps \
393
+ -v $(pwd)/support:/app/support \
394
+ gherkle-runner
395
+ ```
396
+
397
+ ### Pass CLI options
398
+
399
+ Arguments after the image name are forwarded to `gherkle-runner start`:
400
+
401
+ ```bash
402
+ docker run --rm \
403
+ -e GHERKLE_TOKEN=ghr_xxxxx \
404
+ gherkle-runner start --name ci-runner --framework cucumber
405
+ ```
406
+
407
+ ### Use a config file
408
+
409
+ Mount a config file into `/app`:
410
+
411
+ ```bash
412
+ docker run --rm \
413
+ -v $(pwd)/gherkle-runner.config.js:/app/gherkle-runner.config.js \
414
+ -v $(pwd)/features:/app/features \
415
+ -v $(pwd)/steps:/app/steps \
416
+ gherkle-runner
417
+ ```
418
+
419
+ ### Docker Compose
420
+
421
+ ```yaml
422
+ services:
423
+ # Headless step execution (API tests, assertions, etc.)
424
+ gherkle-runner:
425
+ build:
426
+ context: .
427
+ dockerfile: packages/runner/Dockerfile
428
+ environment:
429
+ - GHERKLE_TOKEN=${GHERKLE_TOKEN}
430
+ - GHERKLE_RUNNER_NAME=docker-runner
431
+ volumes:
432
+ - ./features:/app/features
433
+ - ./steps:/app/steps
434
+ - ./support:/app/support
435
+ restart: unless-stopped
436
+
437
+ # E2E variant with Playwright browsers
438
+ gherkle-runner-e2e:
439
+ build:
440
+ context: .
441
+ dockerfile: packages/runner/Dockerfile.browsers
442
+ environment:
443
+ - GHERKLE_TOKEN=${GHERKLE_TOKEN}
444
+ - GHERKLE_RUNNER_NAME=docker-e2e-runner
445
+ volumes:
446
+ - ./features:/app/features
447
+ - ./steps:/app/steps
448
+ - ./support:/app/support
449
+ restart: unless-stopped
450
+ ```
451
+
452
+ ### Environment variables
453
+
454
+ All [configuration environment variables](#environment-variables) work inside the container. Both images run as a non-root user (`gherkle` / `pwuser`) by default.
455
+
456
+ ## Requirements
457
+
458
+ - Node.js >= 18
459
+ - Playwright browsers installed (for E2E execution): `npx playwright install`
460
+
461
+ ## Development
462
+
463
+ ```bash
464
+ # Build
465
+ npm run build
466
+
467
+ # Watch mode
468
+ npm run dev
469
+
470
+ # Run tests
471
+ npm test
472
+
473
+ # Run tests in watch mode
474
+ npm run test:watch
475
+
476
+ # Lint
477
+ npm run lint
478
+ ```
479
+
480
+ ## License
481
+
482
+ MIT
@@ -0,0 +1,111 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Gherkle Runner CLI
5
+ *
6
+ * Usage:
7
+ * gherkle-runner start --token=ghr_xxxxx
8
+ * gherkle-runner init
9
+ * gherkle-runner status
10
+ */
11
+
12
+ const { program } = require('commander');
13
+ const path = require('path');
14
+ const fs = require('fs');
15
+
16
+ // Try to load the compiled version, fall back to ts-node for development
17
+ let GherkleAgent, loadConfig, generateSampleConfig;
18
+ try {
19
+ const dist = require('../dist/index.js');
20
+ GherkleAgent = dist.GherkleAgent;
21
+ loadConfig = dist.loadConfig;
22
+ generateSampleConfig = dist.generateSampleConfig;
23
+ } catch {
24
+ console.log('Note: Running in development mode');
25
+ // In development, you'd use ts-node or similar
26
+ process.exit(1);
27
+ }
28
+
29
+ program
30
+ .name('gherkle-runner')
31
+ .description('Test runner agent for Gherkle')
32
+ .version('0.1.0');
33
+
34
+ // Start command
35
+ program
36
+ .command('start')
37
+ .description('Start the runner agent and connect to Gherkle')
38
+ .option('-t, --token <token>', 'Runner token (or use GHERKLE_TOKEN env var)')
39
+ .option('-n, --name <name>', 'Runner name')
40
+ .option('-u, --url <url>', 'Gherkle API URL')
41
+ .option('-f, --framework <framework>', 'Test framework (playwright, cypress, cucumber)')
42
+ .option('--headless', 'Run browsers in headless mode', true)
43
+ .option('--no-headless', 'Run browsers with UI')
44
+ .action(async (options) => {
45
+ try {
46
+ const config = loadConfig({
47
+ token: options.token,
48
+ name: options.name,
49
+ apiUrl: options.url,
50
+ framework: options.framework,
51
+ headless: options.headless,
52
+ });
53
+
54
+ const agent = new GherkleAgent(config);
55
+
56
+ // Handle shutdown signals
57
+ process.on('SIGINT', async () => {
58
+ console.log('\nReceived SIGINT, shutting down...');
59
+ await agent.stop();
60
+ process.exit(0);
61
+ });
62
+
63
+ process.on('SIGTERM', async () => {
64
+ console.log('\nReceived SIGTERM, shutting down...');
65
+ await agent.stop();
66
+ process.exit(0);
67
+ });
68
+
69
+ await agent.start();
70
+
71
+ // Keep the process alive
72
+ console.log('\nšŸ‘€ Waiting for test runs... (Ctrl+C to stop)\n');
73
+ } catch (err) {
74
+ console.error('Error:', err.message);
75
+ process.exit(1);
76
+ }
77
+ });
78
+
79
+ // Init command
80
+ program
81
+ .command('init')
82
+ .description('Generate a sample configuration file')
83
+ .action(() => {
84
+ const configPath = path.join(process.cwd(), 'gherkle-runner.config.js');
85
+
86
+ if (fs.existsSync(configPath)) {
87
+ console.error('Error: Configuration file already exists');
88
+ process.exit(1);
89
+ }
90
+
91
+ fs.writeFileSync(configPath, generateSampleConfig());
92
+ console.log('āœ… Created gherkle-runner.config.js');
93
+ console.log('\nNext steps:');
94
+ console.log('1. Edit the config file with your settings');
95
+ console.log('2. Set GHERKLE_TOKEN environment variable');
96
+ console.log('3. Run: gherkle-runner start');
97
+ });
98
+
99
+ // Status command
100
+ program
101
+ .command('status')
102
+ .description('Check runner status')
103
+ .action(() => {
104
+ console.log('Gherkle Runner Status');
105
+ console.log('---------------------');
106
+ console.log('Version: 0.1.0');
107
+ console.log('Config file:', fs.existsSync('gherkle-runner.config.js') ? 'Found' : 'Not found');
108
+ console.log('Token:', process.env.GHERKLE_TOKEN ? 'Set (from env)' : 'Not set');
109
+ });
110
+
111
+ program.parse();
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Tests for GherkleAgent.executeRun executor selection logic.
3
+ *
4
+ * The agent branches based on the incoming 'execute' WebSocket message:
5
+ * stepDefinitions non-empty → CucumberSubprocessExecutor.executeAll
6
+ * empty + stepDefinitionsPath set → PlaywrightExecutor.execute (BYO)
7
+ * empty + no path → throw "No step definitions"
8
+ *
9
+ * Also covers:
10
+ * - Busy-guard (refuses 2nd 'execute' while first is in flight)
11
+ * - cancelRun routes to currentCucumberExecutor.cancel()
12
+ * - cancelRun for non-matching runId is a no-op
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=agent-executor-selection.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-executor-selection.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/agent-executor-selection.test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG"}