From af37e876c300e9b524fbab5ae23e75e5a17c4f2a Mon Sep 17 00:00:00 2001 From: lukecp Date: Fri, 2 Oct 2026 10:38:07 -0400 Subject: [PATCH] feat(runner): take --workspace-id on every runner command The only way to pick a runner's workspace was `qawolf auth switch`, which saves it for every session on the machine. Two sessions working on different customers moved each other's next runner command to the wrong workspace. `--workspace-id` is declared once on the `runner` group, so every runner subcommand takes it, and it is never saved. A lint rule keeps runner commands on `withRunnerContext` so a new one can't accept the flag and ignore it. --- .changeset/runner-workspace-id.md | 5 + .oxlintrc.json | 27 ++ skills/qawolf-cli/SKILL.md | 6 + skills/qawolf-cli/references/runner.md | 7 + src/commands/__snapshots__/help.test.ts.snap | 320 +++++++++++++++--- src/commands/context.test.ts | 34 ++ src/commands/context.ts | 10 +- src/commands/help.test.ts | 4 +- src/commands/qawolfCliSkill.template.md | 6 + src/commands/runner/actions.register.ts | 9 +- src/commands/runner/context.test.ts | 134 ++++++++ src/commands/runner/context.ts | 47 ++- src/commands/runner/events.register.ts | 9 +- src/commands/runner/exec.register.ts | 9 +- .../runner/highlightSelector.register.ts | 9 +- src/commands/runner/importPackage.register.ts | 9 +- src/commands/runner/index.ts | 7 +- src/commands/runner/inspect.register.ts | 13 +- src/commands/runner/inspectMobile.register.ts | 15 +- src/commands/runner/interact.register.ts | 11 +- src/commands/runner/lifecycle.register.ts | 17 +- .../runner/promoteSnapshot.register.ts | 9 +- src/commands/runner/recording.register.ts | 17 +- src/commands/runner/run.register.ts | 9 +- 24 files changed, 640 insertions(+), 103 deletions(-) create mode 100644 .changeset/runner-workspace-id.md create mode 100644 src/commands/runner/context.test.ts diff --git a/.changeset/runner-workspace-id.md b/.changeset/runner-workspace-id.md new file mode 100644 index 000000000..c80b6f673 --- /dev/null +++ b/.changeset/runner-workspace-id.md @@ -0,0 +1,5 @@ +--- +"@qawolf/cli": minor +--- + +Every `qawolf runner` command takes `--workspace-id `, which works in that workspace for the one command without saving it. Until now the only way to choose a runner's workspace was `qawolf auth switch`, which saves the choice for every session on the machine, so one session switching moved every other session's next runner command to the new workspace. Without the flag, runner commands still use the saved workspace. The flag also gives an organization or user API key, which saves no workspace, one to work in. diff --git a/.oxlintrc.json b/.oxlintrc.json index 0ff230e46..a88b063ad 100644 --- a/.oxlintrc.json +++ b/.oxlintrc.json @@ -113,6 +113,33 @@ "test/worker/" ], "overrides": [ + { + "files": ["src/commands/runner/*.register.ts"], + "rules": { + "eslint/no-restricted-imports": [ + "error", + { + "paths": [ + { + "name": "~/commands/context.js", + "importNames": ["withAuthContext"], + "message": "Runner commands use withRunnerContext from ./context.js, which honours the runner group's --workspace-id. withAuthContext would accept the flag and ignore it." + } + ], + "patterns": [ + { + "group": ["../*"], + "message": "Use \"~/\" path alias instead of relative parent imports." + }, + { + "group": ["bun", "bun:*"], + "message": "Bun module imports are not available in the nodejs build \u2014 use a portable alternative." + } + ] + } + ] + } + }, { "files": ["src/core/**/*.ts"], "rules": { diff --git a/skills/qawolf-cli/SKILL.md b/skills/qawolf-cli/SKILL.md index b5d1380de..36ea18f34 100644 --- a/skills/qawolf-cli/SKILL.md +++ b/skills/qawolf-cli/SKILL.md @@ -62,6 +62,12 @@ rather than leaving it to time out. commands that follow need no `--runner`. Override that default for one command with `--runner `, or for a whole session with `QAWOLF_RUNNER_ID`. +Runner commands work in the workspace `qawolf auth switch` saved, which is +shared by every session on the machine. When more than one session may be +working in different workspaces, pass `--workspace-id ` to every runner +command instead of switching; it applies to that command only and is never +saved. + ## Output When consuming output programmatically, always pass `--json` (or `--agent`). diff --git a/skills/qawolf-cli/references/runner.md b/skills/qawolf-cli/references/runner.md index 0c112b002..bbe14971e 100644 --- a/skills/qawolf-cli/references/runner.md +++ b/skills/qawolf-cli/references/runner.md @@ -15,6 +15,13 @@ rather than silently ignored. Either answer carries a `url`, which `qawolf runner launch` prints, as does a command that launched its own runner. It is a QA Wolf page showing what the runner is doing, where a person can also take over with their own mouse and keyboard. Hand it to a person who asks what your runner is up to. The page opens for anyone on the runner's team, however the runner was launched. There is nothing to see until the runner's first run starts its screen, so the page waits until then. You read the screen with `screenshot`, not with the page. +Every runner command works in the workspace `qawolf auth switch` saved, unless +it is given `--workspace-id `. The flag holds for that one command and is +never saved, so sessions on one machine working on different workspaces do not +move each other. Since runner ids are scoped to a team, pass the same +`--workspace-id` to every command that drives a runner you launched with it; +leave it off one and that command looks for the runner in the saved workspace. + Commands that target a runner find one in this order: `--runner`, then `QAWOLF_RUNNER_ID`, then the runner stored for the current directory (which `qawolf runner launch` sets). Setting the environment variable once is the most diff --git a/src/commands/__snapshots__/help.test.ts.snap b/src/commands/__snapshots__/help.test.ts.snap index 879d1f4ef..45248ccea 100644 --- a/src/commands/__snapshots__/help.test.ts.snap +++ b/src/commands/__snapshots__/help.test.ts.snap @@ -22,7 +22,7 @@ Commands: directory install [options] [pattern] Install every runtime dependency the project's flows need - runner Drive an interactive runner on the QA Wolf + runner [options] Drive an interactive runner on the QA Wolf platform codeHostIntegration QA Wolf public API codeHostIntegration commands deployment QA Wolf public API deployment commands @@ -282,8 +282,15 @@ exports[`--help output qawolf runner 1`] = ` Drive an interactive runner on the QA Wolf platform Options: + --workspace-id Workspace to work in for this command only. Defaults to the one 'qawolf auth switch' saved, and is never saved itself -h, --help display help for command +Global Options: + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Commands: launch [options] Launch an interactive runner and make it this directory's default list [options] List the runners running on your team @@ -312,9 +319,19 @@ exports[`--help output qawolf runner launch 1`] = ` Launch an interactive runner and make it this directory's default Options: - --id Id to launch under. Relaunching an id attaches to that runner - --name Runner family to run, e.g. playwright - -h, --help display help for command + --id Id to launch under. Relaunching an id attaches to that + runner + --name Runner family to run, e.g. playwright + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption Examples: $ qawolf runner launch @@ -329,9 +346,18 @@ exports[`--help output qawolf runner terminate 1`] = ` End an interactive runner, and the pod it runs on with it Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -341,9 +367,18 @@ exports[`--help output qawolf runner stop-run 1`] = ` Stop what a runner is currently executing, leaving the runner up Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -381,6 +416,15 @@ Options: bill until the terminal closed (default: "3600") -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Examples: $ qawolf runner run flows/checkout.flow.ts $ qawolf runner run flows/checkout.flow.ts --follow @@ -411,6 +455,15 @@ Options: until the terminal closed (default: "3600") -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Examples: $ qawolf runner events recorder --tail 5 $ qawolf runner events run-logs --run --follow @@ -426,6 +479,15 @@ Control video recording on a Playwright runner Options: -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. + Defaults to the one 'qawolf auth switch' saved, + and is never saved itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Commands: start [options] Start manual video capture across runs. Requires a ready screen and suppresses @@ -452,6 +514,15 @@ Options: --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -462,9 +533,18 @@ Stop the recording with this UUID and publish it. Retrying cannot stop a later recording Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -474,9 +554,18 @@ exports[`--help output qawolf runner record status 1`] = ` Show the active recording and automatic recording setting Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -486,12 +575,21 @@ exports[`--help output qawolf runner record auto 1`] = ` Enable or disable automatic recording for subsequent full runs Arguments: - setting Automatic recording setting (choices: "on", "off") + setting Automatic recording setting (choices: "on", "off") Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -509,6 +607,15 @@ Options: --page-token Continue from nextPageToken returned by the previous page -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -518,9 +625,18 @@ exports[`--help output qawolf runner keepalive 1`] = ` Reset a runner's inactivity clock, for a caller that pauses between actions Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption Examples: $ qawolf runner keepalive @@ -535,12 +651,21 @@ Save a JPEG of an interactive runner's screen to a file, or write it to stdout with --out - Options: - --out File to write the image to. - writes the JPEG bytes to stdout - on their own and moves the confirmation, JSON included, to - stderr (default: "screenshot.jpg") - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --out File to write the image to. - writes the JPEG bytes to + stdout on their own and moves the confirmation, JSON + included, to stderr (default: "screenshot.jpg") + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption Examples: $ qawolf runner screenshot @@ -588,6 +713,15 @@ Options: --y click, tap and the like: y, in screenshot pixels -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Examples: $ qawolf runner act click --button left --x 480 --y 260 $ qawolf runner act type --text "hello@example.com" @@ -618,6 +752,15 @@ Options: then this directory's stored runner -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. + Defaults to the one 'qawolf auth switch' saved, + and is never saved itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Examples: $ qawolf runner import-package dayjs $ qawolf runner import-package dayjs --package-version 1.11.13 @@ -632,6 +775,15 @@ Read one thing off a runner's live page (browser) or Appium session (mobile) Options: -h, --help display help for command +Global Options: + --workspace-id Workspace to work in for this command only. Defaults + to the one 'qawolf auth switch' saved, and is never + saved itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption + Commands: element-html [options] Print the HTML of the first element a selector matches page-html [options] Print the page's HTML, simplified for a model to read @@ -668,6 +820,15 @@ Options: --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -681,6 +842,15 @@ Options: --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -690,10 +860,19 @@ exports[`--help output qawolf runner inspect variable 1`] = ` Print a top-level variable's value from the running workflow Options: - --name Name of the variable to read - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --name Name of the variable to read + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -703,9 +882,18 @@ exports[`--help output qawolf runner inspect session 1`] = ` Print the Appium session's status: ready, or why not Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -715,9 +903,18 @@ exports[`--help output qawolf runner inspect contexts 1`] = ` List the WebView contexts available, and which is current Options: - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -727,10 +924,19 @@ exports[`--help output qawolf runner inspect page-source 1`] = ` Print the current context's page source, as a tree Options: - --context Read this context instead of the current one - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --context Read this context instead of the current one + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -752,6 +958,15 @@ Options: --x point: whole pixels on the device's own screen --y point: whole pixels on the device's own screen -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption " `; @@ -762,11 +977,20 @@ Evaluate a snippet against a runner's live page. Use - to read the snippet from stdin Options: - --file File whose scope the snippet is evaluated in; it and the - directory's other files travel with it - --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this - directory's stored runner - -h, --help display help for command + --file File whose scope the snippet is evaluated in; it and the + directory's other files travel with it + --runner Runner to target. Defaults to QAWOLF_RUNNER_ID, then this + directory's stored runner + -h, --help display help for command + +Global Options: + --workspace-id Workspace to work in for this command only. Defaults to + the one 'qawolf auth switch' saved, and is never saved + itself + -V, --version output the version number + --verbose Enable debug logging to stderr + --json Output as JSON + --agent Output for agent consumption Examples: $ qawolf runner exec snippet.ts diff --git a/src/commands/context.test.ts b/src/commands/context.test.ts index c5dbc1ec5..0c3c38747 100644 --- a/src/commands/context.test.ts +++ b/src/commands/context.test.ts @@ -75,6 +75,40 @@ describe("withAuthContext exit code plumbing", () => { }); }); +describe("withAuthContext workspace", () => { + async function workspaceSent(options: { + saved: string | undefined; + named: string | undefined; + }): Promise { + let sent: string | undefined; + await withAuthContext(noopSignals, async () => undefined, { + requireApiKey: async () => ({ + key: "qawolf_test", + source: "browser" as const, + workspaceId: options.saved, + }), + createPlatform: (_key, deps) => { + sent = deps.workspaceId; + return makeMockPlatformClient(); + }, + workspaceId: options.named, + })({}, fakeCommand()); + return sent; + } + + it("builds the client for the saved workspace when the command names none", async () => { + expect(await workspaceSent({ saved: "ws_saved", named: undefined })).toBe( + "ws_saved", + ); + }); + + it("builds the client for the workspace the command names, over the saved one", async () => { + expect(await workspaceSent({ saved: "ws_saved", named: "ws_named" })).toBe( + "ws_named", + ); + }); +}); + describe("buildBaseContext", () => { it("should include log factory in CommandContext", () => { const cmd = new Command(); diff --git a/src/commands/context.ts b/src/commands/context.ts index 471b09272..a8b5bb4e5 100644 --- a/src/commands/context.ts +++ b/src/commands/context.ts @@ -133,6 +133,12 @@ export function withAuthContext( deps: { requireApiKey?: typeof requireApiKey; createPlatform?: typeof createPlatformClient; + /** + * The workspace this one command works in, ahead of the one + * `qawolf auth switch` saved. Never written back, so concurrent sessions + * on one machine can each name their own. + */ + workspaceId?: string | undefined; } = {}, ): (opts: unknown, command: Command) => Promise { return async (_opts: unknown, command: Command): Promise => { @@ -161,8 +167,8 @@ export function withAuthContext( fetch: globalThis.fetch, logger: ctx.log("trpc"), // A browser session names its workspace on every request; an API key - // carries its team, so it sends none. - workspaceId: resolved.workspaceId, + // carries its team, so it sends none unless the command names one. + workspaceId: deps.workspaceId ?? resolved.workspaceId, }, ); diff --git a/src/commands/help.test.ts b/src/commands/help.test.ts index 917e63569..62142cf22 100644 --- a/src/commands/help.test.ts +++ b/src/commands/help.test.ts @@ -19,7 +19,9 @@ function findSub(parent: Command, name: string): Command { function helpFor(...path: string[]): string { let cmd = buildProgram(); for (const segment of path) cmd = findSub(cmd, segment); - cmd.configureHelp({ helpWidth: 80 }); + // Merged, not replaced: a group can set its own help options, such as + // `runner` showing its --workspace-id on every subcommand. + cmd.configureHelp({ ...cmd.configureHelp(), helpWidth: 80 }); let buf = ""; cmd.configureOutput({ writeOut: (s) => { diff --git a/src/commands/qawolfCliSkill.template.md b/src/commands/qawolfCliSkill.template.md index f272e2f03..610174a0e 100644 --- a/src/commands/qawolfCliSkill.template.md +++ b/src/commands/qawolfCliSkill.template.md @@ -62,6 +62,12 @@ rather than leaving it to time out. commands that follow need no `--runner`. Override that default for one command with `--runner `, or for a whole session with `QAWOLF_RUNNER_ID`. +Runner commands work in the workspace `qawolf auth switch` saved, which is +shared by every session on the machine. When more than one session may be +working in different workspaces, pass `--workspace-id ` to every runner +command instead of switching; it applies to that command only and is never +saved. + ## Output When consuming output programmatically, always pass `--json` (or `--agent`). diff --git a/src/commands/runner/actions.register.ts b/src/commands/runner/actions.register.ts index 54c6759b6..7cbd9924f 100644 --- a/src/commands/runner/actions.register.ts +++ b/src/commands/runner/actions.register.ts @@ -2,11 +2,14 @@ import { type Command, Option } from "commander"; import { type ScreenshotMode, screenshotModes } from "@qawolf/api-contracts/v1"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerActions } from "~/domains/interactiveRunner/performActions.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const actionsExamples = ` Examples: @@ -50,7 +53,7 @@ export function registerRunnerActionsCommand( ) .addHelpText("after", actionsExamples) .action((sequence: string, opts: ActionsFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerActions( ctx, { diff --git a/src/commands/runner/context.test.ts b/src/commands/runner/context.test.ts new file mode 100644 index 000000000..e198d4de1 --- /dev/null +++ b/src/commands/runner/context.test.ts @@ -0,0 +1,134 @@ +import { afterEach, beforeEach, describe, expect, it } from "bun:test"; +import { Command, CommanderError } from "commander"; + +import { createProgram } from "~/commands/program.js"; +import { makeMockPlatformClient } from "~/shell/platform/createPlatformClient.testUtils.js"; +import { makeNoopSignals } from "~/shell/signals/createSignalRegistry.fixtures.js"; + +import { withRunnerContext, workspaceIdOption } from "./context.js"; + +const signals = makeNoopSignals(); + +beforeEach(() => { + process.exitCode = 0; +}); + +afterEach(() => { + process.exitCode = 0; +}); + +/** + * A `runner` group shaped like the real one, with one subcommand whose action + * records the workspace the platform client was built for. + */ +function makeRunnerGroup(savedWorkspaceId: string | undefined): { + program: Command; + sent: () => (string | undefined)[]; +} { + const workspaces: (string | undefined)[] = []; + const program = new Command().exitOverride(); + const runner = program + .command("runner") + .addOption(workspaceIdOption()) + .exitOverride() + .configureOutput({ writeErr: () => {}, writeOut: () => {} }); + runner.command("terminate").action((opts: unknown, command: Command) => + withRunnerContext(signals, async () => undefined, { + requireApiKey: async () => ({ + key: "qawolf_test", + source: "browser", + workspaceId: savedWorkspaceId, + }), + createPlatform: (_key, deps) => { + workspaces.push(deps.workspaceId); + return makeMockPlatformClient(); + }, + })(opts, command), + ); + return { program, sent: () => workspaces }; +} + +describe("qawolf runner --workspace-id", () => { + it("works in the named workspace instead of the saved one", async () => { + const { program, sent } = makeRunnerGroup("ws_saved"); + + await program.parseAsync( + ["runner", "terminate", "--workspace-id", "ws_named"], + { from: "user" }, + ); + + expect(sent()).toEqual(["ws_named"]); + }); + + it("takes the flag before the subcommand as well as after it", async () => { + const { program, sent } = makeRunnerGroup("ws_saved"); + + await program.parseAsync( + ["runner", "--workspace-id", "ws_named", "terminate"], + { from: "user" }, + ); + + expect(sent()).toEqual(["ws_named"]); + }); + + it("falls back to the saved workspace without the flag", async () => { + const { program, sent } = makeRunnerGroup("ws_saved"); + + await program.parseAsync(["runner", "terminate"], { from: "user" }); + + expect(sent()).toEqual(["ws_saved"]); + }); + + it("names a workspace for a credential that saved none, such as an API key", async () => { + const { program, sent } = makeRunnerGroup(undefined); + + await program.parseAsync( + ["runner", "terminate", "--workspace-id", "ws_named"], + { from: "user" }, + ); + + expect(sent()).toEqual(["ws_named"]); + }); + + it("trims the id it is given", async () => { + const { program, sent } = makeRunnerGroup("ws_saved"); + + await program.parseAsync( + ["runner", "terminate", "--workspace-id", " ws_named "], + { from: "user" }, + ); + + expect(sent()).toEqual(["ws_named"]); + }); + + it("refuses a blank id rather than sending no workspace", async () => { + const { program, sent } = makeRunnerGroup("ws_saved"); + + let caught: unknown; + try { + await program.parseAsync( + ["runner", "terminate", "--workspace-id", " "], + { from: "user" }, + ); + } catch (error) { + caught = error; + } + + expect(caught).toBeInstanceOf(CommanderError); + expect((caught as CommanderError).code).toBe("commander.invalidArgument"); + expect(sent()).toEqual([]); + }); +}); + +describe("qawolf runner subcommands", () => { + it("each list --workspace-id in their help", () => { + const program = createProgram({ signals: makeNoopSignals() }); + const runner = program.commands.find((c) => c.name() === "runner"); + if (runner === undefined) throw Error("runner group not registered"); + + expect(runner.commands.length).toBeGreaterThan(0); + for (const subcommand of runner.commands) { + expect(subcommand.helpInformation()).toContain("--workspace-id "); + } + }); +}); diff --git a/src/commands/runner/context.ts b/src/commands/runner/context.ts index 01d9996c9..b00e51105 100644 --- a/src/commands/runner/context.ts +++ b/src/commands/runner/context.ts @@ -1,9 +1,54 @@ +import { type Command, InvalidArgumentError, Option } from "commander"; + +import { withAuthContext } from "~/commands/context.js"; import { makeInteractiveRunnerDeps } from "~/domains/interactiveRunner/deps.js"; -import type { AuthCommandContext } from "~/shell/commandContext.js"; +import type { + AuthCommandContext, + CommandResult, +} from "~/shell/commandContext.js"; +import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; export const runnerFlagDescription = "Runner to target. Defaults to QAWOLF_RUNNER_ID, then this directory's stored runner"; +/** + * `--workspace-id`, declared once on `qawolf runner` so every runner command + * takes it. A runner lives in one workspace, so a flag on only some runner + * commands would leave the rest looking for it in the saved one. + */ +export function workspaceIdOption(): Option { + return new Option( + "--workspace-id ", + "Workspace to work in for this command only. Defaults to the one 'qawolf auth switch' saved, and is never saved itself", + ).argParser((value) => { + const trimmed = value.trim(); + // An empty id would send no workspace at all rather than the saved one. + if (!trimmed) throw new InvalidArgumentError("Give a workspace id."); + return trimmed; + }); +} + +/** + * `withAuthContext`, working in the workspace `--workspace-id` named when it + * was passed. Read through `optsWithGlobals` because the flag belongs to the + * `runner` group, not to the subcommand. + */ +export function withRunnerContext( + signals: SignalRegistry, + fn: (ctx: AuthCommandContext) => Promise, + deps: Omit< + NonNullable[2]>, + "workspaceId" + > = {}, +): (opts: unknown, command: Command) => Promise { + return (opts, command) => + withAuthContext(signals, fn, { + ...deps, + workspaceId: command.optsWithGlobals<{ workspaceId?: string }>() + .workspaceId, + })(opts, command); +} + /** Binds the handlers' machine dependencies to the real process and filesystem. */ export function runnerDeps( ctx: AuthCommandContext, diff --git a/src/commands/runner/events.register.ts b/src/commands/runner/events.register.ts index 7a60e9f3d..0160f654c 100644 --- a/src/commands/runner/events.register.ts +++ b/src/commands/runner/events.register.ts @@ -2,12 +2,15 @@ import type { Command } from "commander"; import { knownJournalStreams } from "@qawolf/api-contracts/v1"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { defaultFollowTimeoutSeconds } from "~/core/interactiveRunner/followTimeout.js"; import { handleRunnerEvents } from "~/domains/interactiveRunner/events.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const eventsExamples = ` Examples: @@ -48,7 +51,7 @@ export function registerRunnerEventsCommand( ) .addHelpText("after", eventsExamples) .action((stream: string, opts: EventsFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerEvents( ctx, { diff --git a/src/commands/runner/exec.register.ts b/src/commands/runner/exec.register.ts index c4a145446..301fecb70 100644 --- a/src/commands/runner/exec.register.ts +++ b/src/commands/runner/exec.register.ts @@ -1,11 +1,14 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerExec } from "~/domains/interactiveRunner/evaluateSnippet.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const execExamples = ` Examples: @@ -33,7 +36,7 @@ export function registerRunnerExecCommand( opts: { file?: string; runner?: string }, command: Command, ) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerExec( ctx, { contextFile: opts.file, runner: opts.runner, source: file }, diff --git a/src/commands/runner/highlightSelector.register.ts b/src/commands/runner/highlightSelector.register.ts index 42b9cb796..8d653ca47 100644 --- a/src/commands/runner/highlightSelector.register.ts +++ b/src/commands/runner/highlightSelector.register.ts @@ -1,11 +1,14 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerHighlightSelector } from "~/domains/interactiveRunner/highlightSelector.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const highlightExamples = ` Examples: @@ -29,7 +32,7 @@ export function registerRunnerHighlightSelectorCommand( opts: { runner?: string }, command: Command, ) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerHighlightSelector( ctx, { runner: opts.runner, selector }, diff --git a/src/commands/runner/importPackage.register.ts b/src/commands/runner/importPackage.register.ts index c10c359ac..dea07de1a 100644 --- a/src/commands/runner/importPackage.register.ts +++ b/src/commands/runner/importPackage.register.ts @@ -1,11 +1,14 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerImportPackage } from "~/domains/interactiveRunner/importPackage.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const importPackageExamples = ` Examples: @@ -29,7 +32,7 @@ export function registerRunnerImportPackageCommand( opts: { packageVersion: string; runner?: string }, command: Command, ) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerImportPackage( ctx, { name, runner: opts.runner, version: opts.packageVersion }, diff --git a/src/commands/runner/index.ts b/src/commands/runner/index.ts index 08347ad84..0ffa620c5 100644 --- a/src/commands/runner/index.ts +++ b/src/commands/runner/index.ts @@ -3,6 +3,7 @@ import type { Command } from "commander"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; import { registerRunnerActionsCommand } from "./actions.register.js"; +import { workspaceIdOption } from "./context.js"; import { registerRunnerExecCommand } from "./exec.register.js"; import { registerRunnerHighlightSelectorCommand } from "./highlightSelector.register.js"; import { registerRunnerImportPackageCommand } from "./importPackage.register.js"; @@ -20,7 +21,11 @@ export function registerRunnerCommand( ): void { const runner = program .command("runner") - .description("Drive an interactive runner on the QA Wolf platform"); + .description("Drive an interactive runner on the QA Wolf platform") + .addOption(workspaceIdOption()) + // Set before the subcommands exist, which copy it when created, so each + // one's --help lists --workspace-id alongside its own flags. + .configureHelp({ showGlobalOptions: true }); registerRunnerLifecycleCommands(runner, signals); registerRunCommand(runner, signals); diff --git a/src/commands/runner/inspect.register.ts b/src/commands/runner/inspect.register.ts index 9c0e41e2f..aa2e9b758 100644 --- a/src/commands/runner/inspect.register.ts +++ b/src/commands/runner/inspect.register.ts @@ -1,11 +1,14 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerInspect } from "~/domains/interactiveRunner/inspect.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; import { registerRunnerInspectMobileCommands } from "./inspectMobile.register.js"; const inspectExamples = ` @@ -42,7 +45,7 @@ export function registerRunnerInspectCommands( .requiredOption("--selector ", "Playwright selector to inspect") .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string; selector: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspect( ctx, { @@ -60,7 +63,7 @@ export function registerRunnerInspectCommands( .option("--selector ", "Limit the output to this subtree") .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string; selector?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspect( ctx, { @@ -78,7 +81,7 @@ export function registerRunnerInspectCommands( .requiredOption("--name ", "Name of the variable to read") .option("--runner ", runnerFlagDescription) .action((opts: { name: string; runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspect( ctx, { diff --git a/src/commands/runner/inspectMobile.register.ts b/src/commands/runner/inspectMobile.register.ts index bff9151ec..0bc58d705 100644 --- a/src/commands/runner/inspectMobile.register.ts +++ b/src/commands/runner/inspectMobile.register.ts @@ -2,11 +2,14 @@ import type { Command } from "commander"; import { blankInspectMobileFlags } from "~/core/interactiveRunner/inspectMobileRequest.js"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerInspectMobile } from "~/domains/interactiveRunner/inspectMobile.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; type InspectElementsFlags = { context?: string; @@ -28,7 +31,7 @@ export function registerRunnerInspectMobileCommands( .description("Print the Appium session's status: ready, or why not") .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspectMobile( ctx, { @@ -45,7 +48,7 @@ export function registerRunnerInspectMobileCommands( .description("List the WebView contexts available, and which is current") .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspectMobile( ctx, { @@ -63,7 +66,7 @@ export function registerRunnerInspectMobileCommands( .option("--context ", "Read this context instead of the current one") .option("--runner ", runnerFlagDescription) .action((opts: { context?: string; runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspectMobile( ctx, { @@ -98,7 +101,7 @@ export function registerRunnerInspectMobileCommands( .option("--x ", "point: whole pixels on the device's own screen") .option("--y ", "point: whole pixels on the device's own screen") .action((opts: InspectElementsFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerInspectMobile( ctx, { diff --git a/src/commands/runner/interact.register.ts b/src/commands/runner/interact.register.ts index b14b77c49..a1c1568b7 100644 --- a/src/commands/runner/interact.register.ts +++ b/src/commands/runner/interact.register.ts @@ -1,13 +1,16 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerAct } from "~/domains/interactiveRunner/performAction.js"; import { handleRunnerScreenshot } from "~/domains/interactiveRunner/takeScreenshot.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; import { actExamples } from "./actExamples.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; // JPEG, because that is what the API answers with; a .png name would be a lie // about the bytes in the file. @@ -55,7 +58,7 @@ export function registerRunnerInteractCommands( .option("--runner ", runnerFlagDescription) .addHelpText("after", screenshotExamples) .action((opts: { out: string; runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerScreenshot( ctx, { out: opts.out, runner: opts.runner }, @@ -106,7 +109,7 @@ export function registerRunnerInteractCommands( .option("--y ", "click, tap and the like: y, in screenshot pixels") .addHelpText("after", actExamples) .action((action: string, opts: ActFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerAct( ctx, { diff --git a/src/commands/runner/lifecycle.register.ts b/src/commands/runner/lifecycle.register.ts index d1f2a1404..76837a4e2 100644 --- a/src/commands/runner/lifecycle.register.ts +++ b/src/commands/runner/lifecycle.register.ts @@ -1,7 +1,6 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerKeepalive } from "~/domains/interactiveRunner/keepalive.js"; import { handleRunnerLaunch } from "~/domains/interactiveRunner/launch.js"; import { handleRunnerList } from "~/domains/interactiveRunner/list.js"; @@ -9,7 +8,11 @@ import { handleRunnerStopRun } from "~/domains/interactiveRunner/stopRun.js"; import { handleRunnerTerminate } from "~/domains/interactiveRunner/terminate.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const launchExamples = ` Examples: @@ -43,7 +46,7 @@ export function registerRunnerLifecycleCommands( .option("--name ", "Runner family to run, e.g. playwright") .addHelpText("after", launchExamples) .action((opts: { id?: string; name?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerLaunch( ctx, { id: opts.id, name: opts.name }, @@ -57,7 +60,7 @@ export function registerRunnerLifecycleCommands( .option("--here", "Only the runners this directory launched") .addHelpText("after", listExamples) .action((opts: { here?: boolean }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerList(ctx, { here: opts.here === true }, runnerDeps(ctx)), )(opts, command), ); @@ -66,7 +69,7 @@ export function registerRunnerLifecycleCommands( .description("End an interactive runner, and the pod it runs on with it") .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerTerminate(ctx, { runner: opts.runner }, runnerDeps(ctx)), )(opts, command), ); @@ -77,7 +80,7 @@ export function registerRunnerLifecycleCommands( ) .option("--runner ", runnerFlagDescription) .action((opts: { runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerStopRun(ctx, { runner: opts.runner }, runnerDeps(ctx)), )(opts, command), ); @@ -89,7 +92,7 @@ export function registerRunnerLifecycleCommands( .option("--runner ", runnerFlagDescription) .addHelpText("after", keepaliveExamples) .action((opts: { runner?: string }, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerKeepalive(ctx, { runner: opts.runner }, runnerDeps(ctx)), )(opts, command), ); diff --git a/src/commands/runner/promoteSnapshot.register.ts b/src/commands/runner/promoteSnapshot.register.ts index d1bda0c61..627a7116e 100644 --- a/src/commands/runner/promoteSnapshot.register.ts +++ b/src/commands/runner/promoteSnapshot.register.ts @@ -1,11 +1,14 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerPromoteSnapshot } from "~/domains/interactiveRunner/promoteSnapshot.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const promoteSnapshotExamples = ` Examples: @@ -37,7 +40,7 @@ export function registerRunnerPromoteSnapshotCommand( opts: { baseline: string; runner?: string; screenshot: string }, command: Command, ) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerPromoteSnapshot( ctx, { diff --git a/src/commands/runner/recording.register.ts b/src/commands/runner/recording.register.ts index aa4d1392d..bcb3446cb 100644 --- a/src/commands/runner/recording.register.ts +++ b/src/commands/runner/recording.register.ts @@ -1,14 +1,17 @@ import { Argument, type Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { handleRunnerRecord, handleRunnerRecordings, } from "~/domains/interactiveRunner/recording.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; type RecordFlags = { runner?: string; recordingId?: string }; type RecordingsFlags = RecordFlags & { pageToken?: string }; @@ -31,7 +34,7 @@ export function registerRunnerRecordingCommands( ) .option("--runner ", runnerFlagDescription) .action((opts: RecordFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRecord( ctx, { @@ -49,7 +52,7 @@ export function registerRunnerRecordingCommands( ) .option("--runner ", runnerFlagDescription) .action((recordingId: string, opts: RecordFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRecord( ctx, { @@ -65,7 +68,7 @@ export function registerRunnerRecordingCommands( .description("Show the active recording and automatic recording setting") .option("--runner ", runnerFlagDescription) .action((opts: RecordFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRecord( ctx, { @@ -89,7 +92,7 @@ export function registerRunnerRecordingCommands( ) .option("--runner ", runnerFlagDescription) .action((setting: string, opts: RecordFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRecord( ctx, { @@ -115,7 +118,7 @@ export function registerRunnerRecordingCommands( "Continue from nextPageToken returned by the previous page", ) .action((opts: RecordingsFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRecordings( ctx, { diff --git a/src/commands/runner/run.register.ts b/src/commands/runner/run.register.ts index 3ed6a579b..f5d7fc68c 100644 --- a/src/commands/runner/run.register.ts +++ b/src/commands/runner/run.register.ts @@ -1,12 +1,15 @@ import type { Command } from "commander"; import { declareCommandKind } from "~/commands/commandKind.js"; -import { withAuthContext } from "~/commands/context.js"; import { defaultFollowTimeoutSeconds } from "~/core/interactiveRunner/followTimeout.js"; import { handleRunnerRun } from "~/domains/interactiveRunner/runFlow.js"; import type { SignalRegistry } from "~/shell/signals/createSignalRegistry.js"; -import { runnerDeps, runnerFlagDescription } from "./context.js"; +import { + runnerDeps, + runnerFlagDescription, + withRunnerContext, +} from "./context.js"; const runExamples = ` Examples: @@ -84,7 +87,7 @@ export function registerRunCommand( ) .addHelpText("after", runExamples) .action((flowFile: string, opts: RunFlags, command: Command) => - withAuthContext(signals, (ctx) => + withRunnerContext(signals, (ctx) => handleRunnerRun( ctx, {