feat(agents): expand AR CLI surface#8237
Conversation
…ive model validation
|
Important Review skippedDraft detected. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR adds a typed Agents API client and expanded TypeScript types/constants, implements nine new agents subcommands (archive, commit, diff, follow-up, open, pr, publish, redeploy, revert), refactors agents:create/list/show/stop with multi-step flows (git detection, attachments, watch/polling, pagination/filters), adds attachment upload support, updates CLI wiring/examples, refreshes docs, and adds comprehensive integration tests and mock-server enhancements. Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
📊 Benchmark resultsComparing with 07fa4d1
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 9
🧹 Nitpick comments (2)
docs/index.md (1)
28-28: 💤 Low valueUse smart apostrophes per documentation style guide.
The linter prefers smart apostrophes (') over straight quotes in user-facing text.
✏️ Suggested fix
-| [`agents:commit`](/commands/agents#agentscommit) | Commit an agent task's changes directly to a branch | +| [`agents:commit`](/commands/agents#agentscommit) | Commit an agent task's changes directly to a branch |-| [`agents:publish`](/commands/agents#agentspublish) | Publish an agent task's changes to production | +| [`agents:publish`](/commands/agents#agentspublish) | Publish an agent task's changes to production |Also applies to: 35-35
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/index.md` at line 28, Replace the straight apostrophe in the user-facing table entry for `agents:commit` — the text "Commit an agent task's changes directly to a branch" — with a smart apostrophe (’) to match the documentation style guide; update the same phrase occurrence noted at the other location as well so both instances use the smart apostrophe.src/commands/agents/agents-list.ts (1)
154-163: 💤 Low valueLow-risk ReDoS pattern, but easily mitigated.
The
RegExpis constructed fromstatus.toUpperCase()where status comes fromrunner.state. While expected values are safe alphanumeric strings fromAGENT_STATES, the API response could theoretically contain unexpected characters. The practical risk is low since states are typically controlled server-side, but escaping is a simple fix.♻️ Optional: Escape regex metacharacters
+const escapeRegex = (str: string): string => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + const colorizeStatuses = (tableOutput: string, runners: AgentRunner[]): string => { let output = tableOutput const statuses = new Set(runners.map((runner) => runner.state ?? 'unknown')) for (const status of statuses) { const plain = status.toUpperCase() const colored = formatStatus(status) - output = output.replace(new RegExp(`\\b${plain}\\b`, 'g'), colored) + output = output.replace(new RegExp(`\\b${escapeRegex(plain)}\\b`, 'g'), colored) } return output }This addresses the static analysis warning about regex construction from variables.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/commands/agents/agents-list.ts` around lines 154 - 163, The RegExp in colorizeStatuses is built from untrusted status strings (from runners[i].state) and should escape regex metacharacters to avoid ReDoS or incorrect matching; update colorizeStatuses to escape status (before calling status.toUpperCase()) when creating new RegExp (or build the regex via RegExp(escape(status), 'g')), or alternatively use a safe literal-based replace strategy (e.g., split/join or indexOf loop) and still call formatStatus(status) for replacement; reference the colorizeStatuses function, the status variable derived from runner.state, and formatStatus to locate and change the code.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/commands/agents.md`:
- Line 31: Replace the straight ASCII apostrophe in each occurrence of the
phrase "task's" with a typographic (smart) apostrophe so it reads "task’s" to
satisfy the docs linter; locate the occurrences by searching for the literal
token "task's" (e.g., near the `agents:commit` command entry and other places
where "task's" appears) and update them to "task’s" everywhere in the document.
In `@src/commands/agents/agents-commit.ts`:
- Around line 20-23: The check should use process.stdin.isTTY not
process.stdout.isTTY and the branch flag must be trimmed before validation:
change the gating condition where targetBranch is computed to test
process.stdin.isTTY, and normalize options.branch (e.g., assign trimmed value to
targetBranch or similar) so whitespace-only values don't bypass validation;
update any uses of targetBranch/options.branch (including the inquirer prompt
validator referenced around the prompt) to rely on the trimmed value.
In `@src/commands/agents/agents-create.ts`:
- Around line 64-75: The code interpolates the git ref name (upstream) into a
shell string (used by run) causing possible command injection; change the logic
in agents-create.ts to avoid shell interpolation by calling a safe API (e.g.,
child_process.spawnSync/execFileSync or your run wrapper variant that accepts
args) to run git rev-list with upstream passed as an argument array instead of
via template string; update the block that computes hasUnpushedCommits (the
upstream variable, the run call that executes `git rev-list --count
${upstream}..HEAD`, and any run helper used) to pass upstream as a separate
argument so special characters are not interpreted by the shell.
In `@src/commands/agents/agents-diff.ts`:
- Around line 20-23: The current parsing uses Number.parseInt which accepts
malformed inputs like "1abc" or "12.34"; update the validation in the block
handling input/name to first check the raw input with a strict regex (e.g.
/^\d+$/) and throw the same Error(`--${name} must be a positive integer`) if it
doesn't match, then safely parse with Number.parseInt(input, 10) (or Number) and
ensure value > 0; reference the variables input, name and the existing
Number.parseInt usage to locate and change the logic.
In `@src/commands/agents/agents-redeploy.ts`:
- Around line 23-27: The auto-selection currently calls
api.listAgentRunnerSessions(id, { page: 1, per_page: 20 }) and only inspects
that first page (sessions -> latestDone), which can miss completed sessions;
change the logic in agents-redeploy.ts to paginate through
listAgentRunnerSessions results until a session with state === 'done' is found
(or all pages are exhausted) instead of relying solely on page=1, per_page=20;
implement a loop/request pattern using api.listAgentRunnerSessions(id, { page:
n, per_page: m }) to fetch subsequent pages, stop early when latestDone is
located, and preserve the existing behavior of allowing an explicit --session
override.
In `@src/commands/agents/api.ts`:
- Around line 200-206: The listAiGatewayProviders function currently calls fetch
without the Authorization header; update it to pass the same request init used
by other methods (call getInit() and use that as the second argument to fetch or
merge it into an init object) so the Authorization header is included when
fetching `${baseUrl}/ai-gateway/providers`; keep the existing providersCache and
existing throwForStatus usage intact and ensure the response handling (await
response.json()) remains unchanged.
In `@src/commands/agents/attachments.ts`:
- Around line 51-55: The fetch call uploading attachments (the PUT to uploadUrl)
can hang indefinitely; add a timeout/signal to abort stalled uploads by creating
an AbortController (or using AbortSignal.timeout) and pass its signal into the
fetch options; ensure you clear or handle the timeout appropriately and surface
the abort error in the surrounding function (e.g., the upload logic in
src/commands/agents/attachments.ts where putResponse is awaited) so the command
fails fast instead of hanging.
In `@tests/integration/commands/agents/agents-archive.test.ts`:
- Around line 18-23: The mock route for the accounts endpoint in the baseRoutes
array doesn't include the minimal=true query so the test request GET
/api/v1/accounts?minimal=true isn't matched; update the accounts entry in
baseRoutes (the object with path 'accounts') to include the query (e.g., change
path to 'accounts?minimal=true' or otherwise match query shape) so the mocked
response is used and the CI warning is resolved.
In `@tests/integration/commands/agents/agents-follow-up.test.ts`:
- Around line 81-99: The test 'should pass agent and model in the request body'
currently mocks both provider endpoints which masks wrong API paths; remove the
redundant mock for the generic 'ai-gateway/providers' and keep only the specific
'agent_runners/ai-gateway/providers' route in the routes array so the test only
matches the intended endpoint (ensure the route object with path
'ai-gateway/providers' is deleted and the rest of the routes, including
'agent_runners/ai-gateway/providers', remain).
---
Nitpick comments:
In `@docs/index.md`:
- Line 28: Replace the straight apostrophe in the user-facing table entry for
`agents:commit` — the text "Commit an agent task's changes directly to a branch"
— with a smart apostrophe (’) to match the documentation style guide; update the
same phrase occurrence noted at the other location as well so both instances use
the smart apostrophe.
In `@src/commands/agents/agents-list.ts`:
- Around line 154-163: The RegExp in colorizeStatuses is built from untrusted
status strings (from runners[i].state) and should escape regex metacharacters to
avoid ReDoS or incorrect matching; update colorizeStatuses to escape status
(before calling status.toUpperCase()) when creating new RegExp (or build the
regex via RegExp(escape(status), 'g')), or alternatively use a safe
literal-based replace strategy (e.g., split/join or indexOf loop) and still call
formatStatus(status) for replacement; reference the colorizeStatuses function,
the status variable derived from runner.state, and formatStatus to locate and
change the code.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0895fa4d-bea9-42a9-aecf-86df6c49931e
📒 Files selected for processing (34)
docs/commands/agents.mddocs/index.mdsrc/commands/agents/agents-archive.tssrc/commands/agents/agents-commit.tssrc/commands/agents/agents-create.tssrc/commands/agents/agents-diff.tssrc/commands/agents/agents-follow-up.tssrc/commands/agents/agents-list.tssrc/commands/agents/agents-open.tssrc/commands/agents/agents-pr.tssrc/commands/agents/agents-publish.tssrc/commands/agents/agents-redeploy.tssrc/commands/agents/agents-revert.tssrc/commands/agents/agents-show.tssrc/commands/agents/agents-stop.tssrc/commands/agents/agents.tssrc/commands/agents/api.tssrc/commands/agents/attachments.tssrc/commands/agents/constants.tssrc/commands/agents/types.tssrc/commands/agents/utils.tstests/integration/commands/agents/agents-archive.test.tstests/integration/commands/agents/agents-commit.test.tstests/integration/commands/agents/agents-diff.test.tstests/integration/commands/agents/agents-follow-up.test.tstests/integration/commands/agents/agents-list.test.tstests/integration/commands/agents/agents-open.test.tstests/integration/commands/agents/agents-pr.test.tstests/integration/commands/agents/agents-publish.test.tstests/integration/commands/agents/agents-redeploy.test.tstests/integration/commands/agents/agents-revert.test.tstests/integration/commands/agents/agents-show.test.tstests/integration/commands/agents/agents-stop.test.tstests/integration/utils/mock-api.ts
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
Summary
This expands the agents CLI from the small "create / list / show / stop" surface into the full set of operations the agent runners API supports, so day-to-day workflows like reviewing the diff, opening a PR, committing to a branch, publishing to production, reverting to a session, or archiving a task all live in the CLI now. The static model whitelist is gone, replaced with a live fetch from the AI gateway so adding a new model upstream does not require shipping a CLI patch. The API calls are pulled out into a typed module so the individual command files stay focused on UX. Existing commands grow filters, watch mode, JSON/NDJSON output, attachments, and a few quality-of-life touches like detecting the local git branch and warning about uncommitted changes. Docs are regenerated, the original integration tests are updated to match the new contract, and there is now coverage for every new subcommand.
Expands
netlify agentsto use the full agent_runners API surface. Adds 7 new commands, extendscreate/list/show/stopwith new flags, centralizes the API calls into a typedapi.tsmodule, and replaces the static model whitelist with a live fetch from/api/v1/ai-gateway/providers.New commands:
follow-up,open,diff,pr,commit,publish,revert,archive,redeploy. New flags on existing commands:--watch,--session,--mode,--from-deploy,--parent,--dev-server-image,--attach,--branch/--user/--title/--since/--until/--page/--per-page/--account,--ndjson,--yes. Smarter defaults (local git branch detection, dirty/unpushed warnings) and renderer upgrades (hierarchical sessions, token + credit usage, conditional action hints, append-only watch ticker).Test plan
npm run lint,npm run typecheck,npx prettier --check src/commands/agents/— all clean