Skip to content

feat: 0.2.0 — full platform CLI (32 commands, per-agent keychain, metered calls) - #3

Merged
achris7 merged 6 commits into
mainfrom
feat/cli-0.2.0-dashboard-parity
Aug 7, 2026
Merged

feat: 0.2.0 — full platform CLI (32 commands, per-agent keychain, metered calls)#3
achris7 merged 6 commits into
mainfrom
feat/cli-0.2.0-dashboard-parity

Conversation

@rajbhensdadiya

@rajbhensdadiya rajbhensdadiya commented Aug 7, 2026

Copy link
Copy Markdown
Member

What

Grows @floelabs/cli from 5 onboarding verbs to the full Floe platform surface: 32 commands / ~75 subcommands, so everything the dev-dashboard does can be done from the terminal — built for both humans and AI coding agents (Claude Code, Codex, Cursor).

Highlights

  • Command registry — every noun is a self-contained module; floe help <cmd> everywhere; no cross-command flag collisions.
  • Per-agent keychain + floe use — switching agents reuses each agent's stored key (no more burning one of the 5 key slots per switch). v0.1 configs and the legacy keychain slot migrate automatically.
  • Metered calls: chat, embed, speak, transcribe, pay (x402 proxy, --check pre-flight) — cost + budget remaining printed on every call.
  • Management: agents (incl. pause kill-switch), keys/devkeys, budget (+ reserve), policy (+ chain, dry-run test), allowlist, credit, activity, usage (+ coverage), ledger, billing (incl. CSV export), account, team, funds, cashout, webhooks, models, estimate, providers, phone, actions, orchestrators, vendors.
  • Conventions: --json on everything, --yes for destructive/money verbs (type-to-confirm interactively; refuse non-interactively), exit codes 0/1/2/4/5, 429 retry on reads, sanitized error output, atomic config writes.

Quality

  • 420 tests / 33 files (fetch-stubbed), typecheck clean, zero runtime deps kept.
  • 5-lens adversarial review: 4 confirmed defects fixed (string/number id compares in keys rotate and use, --agent silently ignored on budget/keys, init under FLOE_AGENT_KEY) + regression suite with numeric-id fixtures.

⚠️ Before merging

  • Merging to main auto-publishes 0.2.0 to npm.
  • The new commands are not yet live-tested against production (suite is fetch-stubbed). Suggested 10-min smoke: floe status, floe agents list --rollup, floe chat "hi", floe activity, floe billing, floe policy chain, floe pay <url> --check.

Summary by CodeRabbit

  • New Features
    • Expanded Floe into a terminal platform for agent management, billing, payments, analytics, policies, webhooks, telephony, AI models, and money movement.
    • Added commands for account, teams, agents, keys, providers, orchestrators, activity, usage, vendors, credit, budgets, funds, cashout, allowlists, chat, embeddings, speech, transcription, estimates, payments, and webhooks.
    • Added multi-agent configuration, agent switching, per-agent credentials, JSON output, confirmations, retries, and global CLI options.
  • Documentation
    • Added quickstart guidance, categorized command references, exit-code conventions, and AI-agent usage instructions.
    • Updated the release to version 0.2.0.

…ered calls

Grow the CLI from 5 onboarding verbs to the full Floe platform surface
(~75 subcommands across 32 commands):

- Command registry: each noun is a self-contained module with its own
  flags and help; `floe help <cmd>` works everywhere, flags cannot
  collide across nouns.
- Per-agent keychain slots + `floe use`: switching agents reuses each
  agent's stored key instead of burning one of its 5 key slots; v0.1
  configs and the legacy keychain slot migrate automatically.
- Metered calls on the agent plane: chat, embed, speak, transcribe, and
  pay (the x402 proxy, with --check pre-flight) — every call prints its
  X-Floe-Cost-USDC cost and budget remaining.
- Management: agents (incl. pause kill-switch), keys/devkeys, budget
  (incl. reserve), policy (incl. chain + dry-run test), allowlist,
  credit, activity, usage (incl. coverage), ledger, billing (incl. the
  CSV export), account, team, funds, cashout, webhooks, models,
  estimate, providers, phone, actions, orchestrators, vendors.
- Conventions: --json everywhere, --yes for destructive/money verbs
  (type-to-confirm interactively, refuse non-interactively), exit codes
  0/1/2/4/5, 429 retry on reads, sanitized error output, atomic config
  writes, corrupt-config fail-loud.

420 tests across 33 files (all fetch-stubbed); typecheck clean.
Includes a regression suite with production-representative numeric ids.
@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Floe CLI 0.2.0 expands the terminal platform with registered commands, multi-agent credentials, developer management, governance, observability, billing, money movement, telephony, and metered gateway operations.

Changes

Floe CLI platform expansion

Layer / File(s) Summary
CLI registry and multi-agent foundation
src/main.ts, src/commands/index.ts, src/lib/*, src/commands/init.ts, src/commands/status.ts, src/commands/use.ts, src/commands/keys.ts
Commands use shared definitions, global flags, contexts, agent resolution, per-agent key storage, configuration migration, API retries, and sanitized error handling.
Developer and governance operations
src/commands/account.ts, src/commands/agents.ts, src/commands/policy.ts, src/commands/allowlist.ts, src/commands/budget.ts, src/commands/credit.ts, src/commands/team.ts, src/commands/providers.ts, src/commands/orchestrators.ts, src/commands/webhooks.ts, src/commands/devkeys.ts
The CLI adds account, agent, policy, allowlist, budget, credit, team, provider, orchestrator, webhook, and developer-key workflows.
Observability and billing
src/commands/activity.ts, src/commands/actions.ts, src/commands/billing.ts, src/commands/ledger.ts, src/commands/models.ts, src/commands/usage.ts, src/commands/vendors.ts
The CLI adds activity, action, billing, ledger, model, usage, and vendor views with filtering, pagination, JSON output, and formatted tables.
Metered gateway commands
src/commands/chat.ts, src/commands/embed.ts, src/commands/estimate.ts, src/commands/pay.ts, src/commands/speak.ts, src/commands/transcribe.ts
The CLI adds model discovery, chat streaming, embeddings, estimates, x402 proxy calls, speech output, transcription, and metering reports.
Money and telephony operations
src/commands/funds.ts, src/commands/cashout.ts, src/commands/phone.ts
The CLI adds transfers, deposits, top-ups, cashouts, phone-number management, call history, voice settings, and test calls.
Command coverage and documentation
test/*, README.md, SECURITY.md, package.json
Tests cover requests, output modes, validation, confirmations, error codes, agent targeting, and network-call suppression. Documentation and package metadata describe version 0.2.0 and the expanded command surface.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

  • Floe-Labs/floe-cli#1: Extends and refactors the same CLI commands, configuration, API client, and command dispatch introduced in that PR.

Suggested reviewers: achris7, shivamfloe

Poem

A rabbit hops through commands bright,
With agents keyed just right.
Budgets, phones, and models flow,
JSON tells the tale in rows.
The CLI blooms beneath moonlight.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main release, including the expanded CLI, command count, per-agent keychain, and metered calls.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cli-0.2.0-dashboard-parity

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 53

🤖 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 `@README.md`:
- Around line 24-29: Make the README quickstart use one consistent CLI
installation flow: add the global npm installation command before the examples,
then invoke the installed binary with floe init, floe status, floe chat, and
floe test --voice so no command depends on an unavailable global executable.

In `@src/commands/account.ts`:
- Around line 34-42: Sanitize every API-sourced value before terminal rendering:
in src/commands/account.ts lines 34-42, wrap dev.walletAddress with
sanitizeText; in src/commands/team.ts lines 88-99, wrap m.memberWallet and
result.role with sanitizeText; and in src/commands/team.ts lines 163-165,
sanitize result.role before passing it to bold.

In `@src/commands/actions.ts`:
- Around line 58-65: In targetAgent, normalize agent.id to a string once after
resolveAgentRef returns, then use that normalized value for both the
ctx.config.agents lookup and the returned id field.

In `@src/commands/activity.ts`:
- Around line 114-131: Update renderExpanded and the corresponding compact
rendering path to pass event.timestamp through sanitizeText before formatting
and output. Preserve the existing timestamp display format while ensuring both
timestamp render locations handle network-sourced values consistently with the
other fields.

In `@src/commands/billing.ts`:
- Around line 92-99: Wrap both user-supplied output file writes in the billing
command, including the write in the `flags.out` branch and the other write site,
with handling that converts filesystem failures into a `UsageError` naming the
resolved path. Preserve the existing successful output and JSON-return behavior.
- Around line 88-100: Update billingInvoiceCommand’s --out handling to match
billingExportCommand: treat "-" as the stdout sentinel instead of passing it to
resolve/writeFileSync, or explicitly reject it with UsageError. Preserve the
existing file-writing and JSON summary behavior for normal output paths.

In `@src/commands/budget.ts`:
- Around line 91-94: Update the missing-key error in the per-day budget handling
of the budget command to name the targeted agent and direct the user to the
key-listing command instead of suggesting `floe init`. Use the agent identifier
available in this flow, such as the value passed through `--agent`, while
preserving the existing UsageError behavior.

In `@src/commands/cashout.ts`:
- Around line 78-86: Move the duplicated openInBrowser helper into src/lib/ and
make it parse the URL, accept only valid https: URLs, and use cmd.exe with
['/c', 'start', '', url] on Windows without shell mode. Replace the local helper
in src/commands/cashout.ts lines 78-86, where the caller supplies res.payUrl,
and src/commands/funds.ts lines 114-122, where the caller supplies checkoutUrl,
with the shared validated helper.

In `@src/commands/chat.ts`:
- Around line 22-78: Move meterOf, meterRows, resolveGatewayModel, and readStdin
out of the chat command module into a new shared src/lib/gateway.ts module,
preserving their implementations and exports. Update chat.ts, embed.ts,
speak.ts, transcribe.ts, and pay.ts to import these helpers from the new module,
removing the sibling-command dependency without changing behavior.
- Around line 40-47: Update meterRows to pass budgetRemainingUsd through
sanitizeText before interpolating it into the “budget left” row, while
preserving the existing conditional and formatting behavior.

In `@src/commands/credit.ts`:
- Around line 171-180: Sanitize agent.name before interpolating it into the
remapped ApiError message in the catch block, using the file’s existing
sanitizeText helper consistently with the other renderings of this value.
Preserve the existing status code, error code, and message behavior apart from
sanitizing the displayed name.

In `@src/commands/devkeys.ts`:
- Line 119: Update the JSON response in the devkeys revoke flow around the
flags.json branch to convert the raw keyId argv value to a number before calling
printJson. Update the corresponding test assertion in devkeys.test.ts to expect
numeric keyId 3 while preserving the existing response shape.
- Around line 72-81: Update the limit-error handling in
src/commands/devkeys.ts:72-81 to match the stable machine code confirmed with
the API team rather than the prose “Limit exceeded”, preserving the existing
replacement error message and rethrow behavior. In
src/commands/webhooks.ts:231-240, apply the same stable-code matching for
webhook limit errors and use the server-provided webhook limit instead of
hardcoding 10.

In `@src/commands/estimate.ts`:
- Around line 82-84: Guard est.usage before the Object.entries call in the
human-readable formatting path, preserving the existing output when usage is
present and avoiding a TypeError when the gateway omits it. Leave the --json
path unchanged.

In `@src/commands/funds.ts`:
- Around line 184-188: Update the execute flow in the withdraw command to catch
failures from the api.dev call, including uncertain or lost responses, and
report the prepared.transferId together with the command users can run to
inspect that transfer’s state. Preserve the successful return of { prepared,
executed }, but avoid propagating only a raw error that encourages preparing a
second transfer.

In `@src/commands/keys.ts`:
- Around line 51-61: The key ID comparisons use mismatched string and number
types, preventing the configured key from being identified. In
src/commands/keys.ts lines 51-61, update the marker comparison to guard against
undefined and compare String(k.id) with String(agent.keyId); in
src/commands/status.ts lines 56-58, normalize both values with String() in the
keys.find predicate. Also store String(minted.id) in src/commands/init.ts line
181 and String(rotated.id) in src/commands/keys.ts line 186 to preserve the
AgentEntry.keyId string invariant.
- Around line 48-58: Sanitize all API-sourced text rendered by the keys listing
flow, including agent.name in the no-keys message and heading, plus k.keyPrefix
and k.label in the row output. Reuse the existing sanitizeText helper already
used elsewhere in this file, while preserving the current fallback values and
formatting.

In `@src/commands/models.ts`:
- Around line 84-89: Update fmtRate so rates below the cent threshold use
fixed-decimal formatting rather than toPrecision(2), preventing exponential
notation for values below 1e-6. Preserve the existing trimming and "$…/M" output
format.

In `@src/commands/pay.ts`:
- Around line 120-146: Move the response-to-ApiError mapping from proxyRefusal
into the existing mapping function in src/lib/api.ts, export that function, and
reuse it from proxyRefusal. Preserve parsing of error envelopes and the 429
Retry-After hint while removing the duplicated implementation from pay.ts.
- Around line 167-179: Update agentContext to return the already-resolved agent
key, then destructure and reuse that value in the pay command instead of calling
resolveAgentKey again. Preserve the existing “No agent key found” behavior by
guarding the key before constructing the Authorization header; do not allow an
undefined value to produce “Bearer undefined”.

In `@src/commands/phone.ts`:
- Around line 218-223: Encode agent.id with encodeURIComponent at every phone
route interpolation in this file, including the request containing the numbers
search and the other agent-specific routes. Preserve the existing route
structure and use the encoded value consistently wherever agent.id is inserted
into a path.
- Around line 333-362: Wrap the per-agent numbers request in the existing
withTelephony helper, matching the pattern used by the other phone commands,
while preserving the current GET endpoint and response handling. Also apply the
wrapper to the fleet request in the flags.all branch so both phone list paths
provide the standard telephony-unavailable guidance.
- Around line 309-318: Validate the purchase response immediately after parsing
JSON before dereferencing number, and convert missing or malformed number data
into a clear command error while preserving the successful debit context. Also
validate costRaw before calling rawToUsd, handling an absent or invalid
X-Floe-Cost-USDC header without allowing a raw TypeError or conversion failure
in either the JSON or human-readable output paths.
- Around line 421-423: Update the confirmAction call in the phone command to use
the sanitized e164 value in the confirmation prompt text, while retaining the
raw e164 argument only as the comparison token if required by confirmAction’s
input contract.

In `@src/commands/policy.ts`:
- Around line 95-134: Extract the duplicated targetAgent, formatSeconds, and
describeWindow implementations from src/commands/policy.ts (lines 95-134) and
src/commands/allowlist.ts (lines 52-84) into one shared module, preserving their
current behavior. Update both files to import the shared helpers; remove the
local copies from src/commands/allowlist.ts (lines 52-84) and replace the
implementations in src/commands/policy.ts (lines 95-134) with imports.
- Around line 263-279: Update matchKey normalization in the policy command
around the kind/match handling: preserve flags.match exactly for task policies,
and lowercase it only for API and vendor kinds. Keep API matchKind derivation
and vendor address validation operating on the normalized value, while ensuring
task IDs remain case-sensitive and consistent with the unchanged value sent by
budget handling.

In `@src/commands/speak.ts`:
- Around line 44-49: Update the file-output path in the speak command around
writeFileSync to catch write failures and report that the gateway call
succeeded, including the metered cost, while preserving the existing stdout
path. Also apply the CLI’s --yes confirmation convention before overwriting an
existing output file, or explicitly document overwrite behavior in the command
usage text.

In `@src/commands/usage.ts`:
- Around line 230-232: Update the usage output near the hardcoded dark-spend
note to render the server-provided CoverageResponse.dark value through
sanitizeText(coverage.dark), preserving the existing output formatting; do not
leave the hardcoded wording as the displayed value.
- Around line 82-89: The duplicated parseDays implementations must be
centralized to prevent the API day bound from drifting. In src/commands/usage.ts
lines 82-89, remove the local parseDays and import the shared helper; do the
same in src/commands/ledger.ts lines 44-51. Define parseDays and the MAX_DAYS
bound once in a shared module such as src/lib/duration.ts, preserving the
existing 1–90 validation and UsageError message.
- Around line 112-114: Apply sanitizeText to all specified network-sourced
terminal fields: wrap p.date in src/commands/usage.ts lines 112-114,
ledger.groupBy in src/commands/ledger.ts line 76, and the checkedAt fallback in
src/commands/vendors.ts lines 38-47. Preserve the existing table output and
sanitization of the other fields.
- Around line 261-284: Validate flags within the `run` branches in
`src/commands/usage.ts` so each subcommand rejects any inapplicable `agent`,
`days`, or `window` option instead of silently ignoring it. Keep only the flags
supported by `usageSummaryCommand`, `usageCoverageCommand`, and
`usageSeriesCommand` respectively, and raise the existing usage error before
invoking the command when an unsupported flag is provided.
- Around line 214-216: Update the usage output construction around
process.stdout.write to provide a fallback before sanitization: pass agent.name
?? String(agent.id) to sanitizeText, matching usageSeriesCommand while
preserving the existing heading format.

In `@src/commands/use.ts`:
- Around line 32-34: Add regression assertions around the use flow to verify
that migrate() persists both keyId and keyPrefix under agents['7'] after use
completes. Keep the existing use behavior unchanged and assert the stored values
match the migrated metadata.

In `@src/commands/webhooks.ts`:
- Around line 199-204: Update webhooksCreateCommand’s URL validation to parse
the URL and accept only http or https protocols, throwing the existing
UsageError before any API call for other schemes such as file. Preserve the
current invalid-URL handling and message format.
- Around line 348-355: Update the --retry branch in the webhooks command to run
parseLimit before performing any I/O, preserving the existing out-of-range
--limit validation. Wrap the retry API call and result handling with withWebhook
using the same webhook lookup/error behavior as the list path, so missing
webhook IDs produce the standardized “Webhook <id> not found.” response.

In `@src/lib/api.ts`:
- Around line 126-133: Update the 429 retry branch in the request retry flow to
clamp a finite Retry-After delay to a positive lower bound while retaining the
existing 10-second upper bound and fallback backoff. Before sleeping and
continuing, cancel or otherwise consume the discarded response body via the
existing Response object res.

In `@src/lib/config.ts`:
- Around line 69-85: Update readConfig to validate that the parsed JSON root is
a non-null, non-array object before passing it to migrate; otherwise throw the
same UsageError used for invalid JSON. Preserve the existing missing-file
behavior and migration flow for valid object roots.

In `@src/lib/confirm.ts`:
- Around line 16-21: Update confirmAction to sanitize both summary and expected
with the existing sanitizeText utility before constructing either terminal
prompt or error message. Use the sanitized expected value for comparison with
the user’s answer, ensuring the accepted input matches exactly what the prompt
displays.

In `@src/lib/types.ts`:
- Around line 4-5: Update the created-agent lookup in init.ts to compare
String(created agent ID) with String(SerializedAgent.id), ensuring numeric and
string representations match. Keep the existing lookup behavior unchanged apart
from normalizing both compared values.

In `@src/main.ts`:
- Around line 127-129: Update the catch handler in main so non-Error rejection
values are normalized to a safe string before calling sanitizeText. Preserve the
existing error prefix and exitCode assignment, while ensuring sanitizeText never
receives undefined or another non-string value.

In `@test/agents.test.ts`:
- Around line 257-275: Add a test alongside the existing agent creation
validation cases that invokes main with an agent name containing an invalid
character, such as “bad/name”. Assert process.exitCode is 2 and the stubbed
fetch calls remain empty, covering the AGENT_NAME validation in agents.ts.

In `@test/cashout.test.ts`:
- Around line 141-191: Add a `cashout start` test in the existing suite that
stubs `POST /v1/offramp/start` to return the `withdrawal_exceeds_user_balance`
error, then assert the command output includes the `spend-only` welcome-credit
hint. Reuse the established API stubbing and invocation pattern without changing
the existing success-path tests.

In `@test/chat.test.ts`:
- Around line 124-144: Extend the streaming test for main to return the SSE
response body through a ReadableStream, splitting a data frame mid-JSON across
chunks and asserting the reconstructed output contains “Hello”. Add a subsequent
frame after data: [DONE] and assert its content is absent from stdout, covering
the done guard while preserving the existing stream:true request assertions.

In `@test/embed.test.ts`:
- Around line 79-83: Update the dimension assertion in the embedding output test
around the stdout expectations so it verifies the rendered row contains the
dimension value 5, rather than matching the digit anywhere in stdout. Keep the
existing cost and vector-elision assertions unchanged.

In `@test/keys.test.ts`:
- Around line 7-17: Hoist the mocked keychain store used by the vi.mock factory
in the keys test and clear it in a beforeEach hook. Preserve the existing
getSecret and setSecret behavior while ensuring each test starts with an empty
store, following the established secretStore pattern from the regression tests.

In `@test/models.test.ts`:
- Around line 118-128: Extend the “falls back to the agent key when no dev key
is available” test to assert the rendered models output, not just the
Authorization header. Reuse the expected data from LIST_BODY and verify the
output produced by main on the agent-plane anyPlaneJson response path.

In `@test/pay.test.ts`:
- Around line 238-248: Mock the keychain module in test/pay.test.ts, matching
the existing ../src/lib/keychain.js mock pattern used by test/estimate.test.ts,
so resolveAgentKey cannot access the OS keychain during the missing-credential
test. Preserve the test’s FLOE_AGENT_KEY setup, exit-code and stderr assertions,
and fetch non-call assertion.
- Around line 95-249: Extend the pay command tests around main to cover --data
`@file` and --data - input, including resolveData’s ENOENT error message; add a
replay response with X-Floe-Idempotent-Replay: true and assert replayed output
plus the human replay note; add a large response body exceeding MAX_HUMAN_BODY
and assert the truncation footer; and add --check coverage for a 402 response
with unreadable payment terms, asserting the !result.payment behavior and
expected exit/output.

In `@test/phone.test.ts`:
- Around line 112-118: Update the afterEach cleanup hook to restore mocks,
unstub environments and globals, and reset process.exitCode before attempting
rmSync(configDir). Ensure cleanup still completes if rmSync throws by preserving
the existing try/finally cleanup pattern used by the related test files.

In `@test/regressions.test.ts`:
- Around line 90-102: Move the URL assertions out of the fetch stubs in the
affected regression tests, including the cases around the key rotation requests.
Record each requested URL in the stub, then assert the expected endpoints after
main resolves, following the existing request-recording pattern used later in
the same file. Keep the stubs focused on returning responses so URL mismatches
report directly from the assertions.

In `@test/speak.test.ts`:
- Around line 111-139: Add coverage in the speak command tests for the --out -
path: verify TTY stdout rejects binary output before fetch, --out - with --json
is rejected before fetch, and a successful request writes the raw audio bytes to
stdout while reporting byte count and cost to stderr. Use the existing main,
fetch stubs, and stdout/stderr spies, and assert no output file or network call
is produced for refusal cases.

In `@test/transcribe.test.ts`:
- Around line 115-136: Extend the transcribe command tests with a zero-byte file
case verifying the empty-file error, exit code 2, and no network call, and an
unknown-extension case verifying the upload request uses audio/wav. Reuse the
existing test setup and fetch mock patterns around main, and cover the
validation branch in transcribe.ts without changing existing behavior.

In `@test/usage.test.ts`:
- Around line 92-103: Update the beforeEach test setup to stub FLOE_API_URL to
an empty string, matching test/models.test.ts and test/vendors.test.ts, so
environment configuration cannot override the test config and URL assertions
remain isolated.
🪄 Autofix

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: ASSERTIVE

Plan: Pro

Run ID: 73a7a746-2aa8-41db-b0b5-4a7b7a5f9a40

📥 Commits

Reviewing files that changed from the base of the PR and between ae8eb1b and 81739d7.

📒 Files selected for processing (75)
  • README.md
  • SECURITY.md
  • package.json
  • src/commands/account.ts
  • src/commands/actions.ts
  • src/commands/activity.ts
  • src/commands/agents.ts
  • src/commands/allowlist.ts
  • src/commands/billing.ts
  • src/commands/budget.ts
  • src/commands/cashout.ts
  • src/commands/chat.ts
  • src/commands/credit.ts
  • src/commands/devkeys.ts
  • src/commands/embed.ts
  • src/commands/estimate.ts
  • src/commands/funds.ts
  • src/commands/index.ts
  • src/commands/init.ts
  • src/commands/keys.ts
  • src/commands/ledger.ts
  • src/commands/models.ts
  • src/commands/orchestrators.ts
  • src/commands/pay.ts
  • src/commands/phone.ts
  • src/commands/policy.ts
  • src/commands/providers.ts
  • src/commands/speak.ts
  • src/commands/status.ts
  • src/commands/team.ts
  • src/commands/test.ts
  • src/commands/transcribe.ts
  • src/commands/usage.ts
  • src/commands/use.ts
  • src/commands/vendors.ts
  • src/commands/webhooks.ts
  • src/lib/api.ts
  • src/lib/command.ts
  • src/lib/config.ts
  • src/lib/confirm.ts
  • src/lib/context.ts
  • src/lib/duration.ts
  • src/lib/keychain.ts
  • src/lib/table.ts
  • src/lib/types.ts
  • src/main.ts
  • test/account.test.ts
  • test/actions.test.ts
  • test/activity.test.ts
  • test/agents.test.ts
  • test/allowlist.test.ts
  • test/billing.test.ts
  • test/budget.test.ts
  • test/cashout.test.ts
  • test/chat.test.ts
  • test/credit.test.ts
  • test/devkeys.test.ts
  • test/embed.test.ts
  • test/estimate.test.ts
  • test/funds.test.ts
  • test/keys.test.ts
  • test/ledger.test.ts
  • test/models.test.ts
  • test/orchestrators.test.ts
  • test/pay.test.ts
  • test/phone.test.ts
  • test/policy.test.ts
  • test/providers.test.ts
  • test/regressions.test.ts
  • test/speak.test.ts
  • test/team.test.ts
  • test/transcribe.test.ts
  • test/usage.test.ts
  • test/vendors.test.ts
  • test/webhooks.test.ts

Comment thread README.md
Comment thread src/commands/account.ts
Comment thread src/commands/actions.ts
Comment thread src/commands/activity.ts
Comment thread src/commands/billing.ts
Comment thread test/pay.test.ts
Comment thread test/regressions.test.ts
Comment thread test/speak.test.ts
Comment thread test/transcribe.test.ts
Comment thread test/usage.test.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review continued from previous batch...

Comment thread src/commands/cashout.ts
Comment thread src/commands/funds.ts Outdated
Comment thread src/commands/keys.ts Outdated
Comment thread src/commands/phone.ts
Comment thread src/commands/phone.ts
Comment thread src/lib/config.ts
Comment thread src/lib/confirm.ts Outdated
Comment thread src/lib/types.ts
Comment thread test/cashout.test.ts
Comment thread test/phone.test.ts
- harden openInBrowser for network-sourced URLs (https-only, no cmd.exe
  shell parsing on Windows)
- guard undefined agent key in pay, missing usage field in estimate,
  post-charge write failures in speak, execute failures in funds (with
  transferId + no-blind-retry hint)
- sanitize remaining network-sourced strings (wallets, roles, timestamps,
  key metadata, phone numbers, confirm prompts, non-Error throws)
- clamp Retry-After floor + cancel discarded 429 bodies; reject non-object
  config roots; String()-normalize remaining id compares
- pre-network validation: webhook URL scheme, --limit/--retry conflicts,
  inapplicable --days/--window, billing invoice --out -
- README quickstart install step; devkeys --json keyId type parity
- test hardening: SSE chunk-split stream case, keychain-store isolation,
  cleanup in finally, output assertions, new edge-case coverage (429 tests)

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/commands/status.ts (2)

44-46: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Mark a missing configured agent as stale.

When configured exists but no matching agent or active fallback exists, agent is undefined. The current expression then sets configStale to false, so status hides the stale configuration.

Proposed fix
-  const configStale = Boolean(configured && agent && String(agent.id) !== String(configured.id));
+  const configStale = Boolean(
+    configured && (!agent || String(agent.id) !== String(configured.id)),
+  );
🤖 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/status.ts` around lines 44 - 46, Update the configStale
calculation near the agent selection so a present configured agent is marked
stale when no agent was resolved, as well as when the resolved agent ID differs
from the configured ID. Preserve the existing non-stale behavior when no
configuration exists or the matching agent is found.

75-82: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Apply one terminal-output sanitization rule to both command paths.

Both paths interpolate API-derived strings without sanitizeText. Sanitize values before formatting or applying color wrappers.

  • src/commands/status.ts#L75-L82: sanitize developer identity, agent name, status, and suspension reason before building rows.
  • src/commands/init.ts#L105-L106: sanitize a.name before writing the interactive selection list.
🤖 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/status.ts` around lines 75 - 82, Apply terminal-output
sanitization in both command paths: in src/commands/status.ts lines 75-82,
sanitize developer identity, agent name, status, and suspension reason before
interpolation or color wrapping; in src/commands/init.ts lines 105-106, sanitize
a.name before adding it to the interactive selection list. Use the existing
sanitizeText helper and preserve the current formatting and status behavior.
src/commands/init.ts (1)

3-9: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Avoid shell execution in openInBrowser

On Windows, shell: process.platform === 'win32' makes spawn invoke cmd.exe. Use the non-shell Windows opener already used in src/commands/cashout.ts and src/commands/funds.ts.

🤖 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/init.ts` around lines 3 - 9, Update openInBrowser in init.ts to
avoid shell-based spawn on Windows by reusing the existing non-shell Windows
opener pattern from the cashout and funds command implementations, while
preserving the current behavior on other platforms.

Source: Linters/SAST tools

♻️ Duplicate comments (1)
src/commands/budget.ts (1)

93-95: 🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Sanitize all resolved and API-derived strings before terminal output.

Both paths sanitize some fields but leave one displayed value unprotected.

  • src/commands/budget.ts#L93-L95: wrap agent.name ?? agent.id with sanitizeText before constructing UsageError.
  • src/commands/keys.ts#L59-L63: wrap used with sanitizeText before passing it to dim.
🤖 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/budget.ts` around lines 93 - 95, Sanitize both untrusted display
values before terminal output: in src/commands/budget.ts lines 93-95, wrap
agent.name ?? agent.id with sanitizeText when constructing UsageError; in
src/commands/keys.ts lines 59-63, wrap used with sanitizeText before passing it
to dim.
🤖 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 `@src/commands/keys.ts`:
- Around line 48-50: Update the no-key message in the keys command to keep the
remediation scoped to the selected agent: replace the unqualified floe init
--new-key hint with floe keys create --agent using the selected agent reference,
or otherwise include that reference in the initialization command.

In `@src/commands/speak.ts`:
- Around line 49-57: Update the writeFileSync call in the speak command to
propagate the native filesystem error directly instead of wrapping it in a new
Error. Remove the surrounding catch-and-rethrow logic while preserving the
existing successful gateway and metering flow.

In `@test/chat.test.ts`:
- Around line 171-194: Remove the duplicate streaming test covering split data
frames and ignoring content after [DONE], keeping the other equivalent test
intact. Limit the change to the redundant test block and preserve the remaining
streaming coverage.

---

Outside diff comments:
In `@src/commands/init.ts`:
- Around line 3-9: Update openInBrowser in init.ts to avoid shell-based spawn on
Windows by reusing the existing non-shell Windows opener pattern from the
cashout and funds command implementations, while preserving the current behavior
on other platforms.

In `@src/commands/status.ts`:
- Around line 44-46: Update the configStale calculation near the agent selection
so a present configured agent is marked stale when no agent was resolved, as
well as when the resolved agent ID differs from the configured ID. Preserve the
existing non-stale behavior when no configuration exists or the matching agent
is found.
- Around line 75-82: Apply terminal-output sanitization in both command paths:
in src/commands/status.ts lines 75-82, sanitize developer identity, agent name,
status, and suspension reason before interpolation or color wrapping; in
src/commands/init.ts lines 105-106, sanitize a.name before adding it to the
interactive selection list. Use the existing sanitizeText helper and preserve
the current formatting and status behavior.

---

Duplicate comments:
In `@src/commands/budget.ts`:
- Around line 93-95: Sanitize both untrusted display values before terminal
output: in src/commands/budget.ts lines 93-95, wrap agent.name ?? agent.id with
sanitizeText when constructing UsageError; in src/commands/keys.ts lines 59-63,
wrap used with sanitizeText before passing it to dim.
🪄 Autofix

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: ASSERTIVE

Plan: Pro

Run ID: 45146d20-2dbd-470c-a106-b3fac814211d

📥 Commits

Reviewing files that changed from the base of the PR and between 81739d7 and 4f99340.

📒 Files selected for processing (39)
  • README.md
  • src/commands/account.ts
  • src/commands/actions.ts
  • src/commands/activity.ts
  • src/commands/billing.ts
  • src/commands/budget.ts
  • src/commands/cashout.ts
  • src/commands/chat.ts
  • src/commands/credit.ts
  • src/commands/devkeys.ts
  • src/commands/estimate.ts
  • src/commands/funds.ts
  • src/commands/init.ts
  • src/commands/keys.ts
  • src/commands/ledger.ts
  • src/commands/pay.ts
  • src/commands/phone.ts
  • src/commands/speak.ts
  • src/commands/status.ts
  • src/commands/team.ts
  • src/commands/usage.ts
  • src/commands/vendors.ts
  • src/commands/webhooks.ts
  • src/lib/api.ts
  • src/lib/config.ts
  • src/lib/confirm.ts
  • src/main.ts
  • test/agents.test.ts
  • test/cashout.test.ts
  • test/chat.test.ts
  • test/devkeys.test.ts
  • test/embed.test.ts
  • test/keys.test.ts
  • test/models.test.ts
  • test/phone.test.ts
  • test/regressions.test.ts
  • test/speak.test.ts
  • test/transcribe.test.ts
  • test/usage.test.ts

Comment thread src/commands/keys.ts
Comment thread src/commands/speak.ts
Comment thread test/chat.test.ts Outdated
- empty keys-list hint now mints for the selected agent, not the active one
- speak write failures rethrow the native fs error (code/path/syscall kept),
  with the charged-cost note moved to stderr
- drop the accidentally duplicated stream test

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@src/commands/keys.ts`:
- Around line 48-50: Make the copy-paste command in the no-keys output of the
keys command shell-safe by quoting the agent reference or using a validated
unambiguous agent.id instead of the API-sourced name. Preserve the display name
separately, and add regression coverage for names containing whitespace and
shell metacharacters.

In `@src/commands/speak.ts`:
- Around line 52-58: Update the warning construction in the error path that
writes to process.stderr so the --out path is converted to a path-specific
single-line escaped representation before interpolation. Keep sanitizeText for
the existing output text, preserve the success-and-charged message, and continue
rethrowing the original err.
🪄 Autofix

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: ASSERTIVE

Plan: Pro

Run ID: 26314782-50b0-4beb-8c38-7b3628b4a204

📥 Commits

Reviewing files that changed from the base of the PR and between 4f99340 and e80177f.

📒 Files selected for processing (3)
  • src/commands/keys.ts
  • src/commands/speak.ts
  • test/chat.test.ts
💤 Files with no reviewable changes (1)
  • test/chat.test.ts

Comment thread src/commands/keys.ts
Comment thread src/commands/speak.ts
@achris7
achris7 requested review from achris7 and shivamfloe and a balanced review from Copilot August 7, 2026 22:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Expands Floe CLI 0.2.0 into a full platform interface with modular commands, per-agent credentials, metered calls, management operations, and machine-readable output.

Changes:

  • Introduces a command registry and broad platform command surface.
  • Adds per-agent key storage, migration, confirmations, retries, and atomic configuration writes.
  • Adds extensive fetch-stubbed coverage and updated documentation.

Reviewed changes

Copilot reviewed 75 out of 75 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
src/main.ts Registry-based command dispatch and error handling
src/lib/api.ts Public routes, raw responses, and read retries
src/lib/command.ts Shared command contract and options
src/lib/config.ts Version-two config migration and atomic writes
src/lib/confirm.ts Destructive-action confirmation gate
src/lib/context.ts Shared credential and agent resolution
src/lib/duration.ts Human-duration parsing
src/lib/keychain.ts Per-agent credential slots
src/lib/table.ts ANSI-aware table rendering
src/lib/types.ts Numeric/string agent ID support
src/commands/account.ts Account inspection and rename
src/commands/activity.ts Filtered activity feed
src/commands/billing.ts Billing summaries and exports
src/commands/chat.ts Metered chat and SSE streaming
src/commands/credit.ts Credit-line inspection and opening
src/commands/devkeys.ts Developer-key management
src/commands/embed.ts Metered embeddings
src/commands/estimate.ts Cost estimation
src/commands/index.ts Command registry and sections
src/commands/init.ts Multi-agent initialization and migration
src/commands/ledger.ts Grouped spend ledger
src/commands/models.ts Model catalog and pricing
src/commands/providers.ts BYOK provider-key management
src/commands/speak.ts Metered text-to-speech
src/commands/status.ts Multi-agent status reporting
src/commands/team.ts Team roster management
src/commands/test.ts Registry integration for gateway testing
src/commands/transcribe.ts Metered transcription
src/commands/use.ts Active-agent switching
src/commands/vendors.ts Vendor probe status
test/account.test.ts Account command coverage
test/activity.test.ts Activity filtering coverage
test/billing.test.ts Billing and export coverage
test/budget.test.ts Budget reservation coverage
test/chat.test.ts Chat and streaming coverage
test/embed.test.ts Embedding coverage
test/estimate.test.ts Estimate coverage
test/ledger.test.ts Ledger coverage
test/models.test.ts Model catalog coverage
test/pay.test.ts x402 proxy coverage
test/providers.test.ts Provider-key coverage
test/speak.test.ts Speech output coverage
test/transcribe.test.ts Transcription coverage
test/vendors.test.ts Vendor status coverage
README.md Platform command and agent guidance
SECURITY.md Supported version updated to 0.2.x
package.json Package version and description update

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/lib/duration.ts Outdated
Comment thread src/lib/config.ts Outdated
Comment thread src/commands/chat.ts
Comment thread src/commands/use.ts Outdated
Comment thread src/commands/init.ts Outdated
Comment thread README.md Outdated
@achris7

achris7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

- duration: reject a huge-but-valid value (would overflow to a non-safe integer,
  JSON.stringify → null, API applies the wrong window). Add parseDuration tests. (Copilot)
- config: readConfig only treats ENOENT as empty; EACCES/EIO and other read
  failures now surface, so a later write can't rename a new config over the
  unreadable one and lose saved agents. (Copilot, High)
- init/use: if local secret storage fails after the server minted the one-time
  key, print it (shown once) before rethrowing so it's recoverable. (Copilot, High)
- chat: cancel the reader and break as soon as [DONE] arrives, so a gateway that
  holds the SSE socket open past the terminator can't hang the CLI. (Copilot)
- README: describe --json as stable machine-readable CLI JSON (metered commands
  wrap the body), not 'exactly what the API returned'. (Copilot)
@achris7

achris7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Addressed all six — pushed.

High

  • config.ts — silent config loss: readConfig now treats only ENOENT as empty; any other read failure (EACCES/EIO/…) throws a UsageError instead of returning {}, so a later write can't rename a fresh config over an unreadable one and drop every saved agent. (The corrupt-JSON path already failed loudly; this was the missing case in the read try/catch.)
  • init.ts / use.ts — unrecoverable minted key: the server mints the one-time key (and consumes a slot) before local storage runs, so if setSecret throws we now print the key to stderr (shown once) before rethrowing — the operator can recover it.

Medium

  • duration.ts — overflow: a huge-but-valid value (e.g. 99999999999999999999w) overflowed to a non-safe integer → JSON.stringifynull → wrong window applied. Now rejected with a UsageError; added parseDuration unit tests (valid/malformed/overflow).
  • chat.ts — SSE hang: cancel the reader and break the read loop as soon as [DONE] arrives, so a gateway that keeps the socket open past the terminator can't hang a completed call.

Low

  • README --json: reworded to "stable, machine-readable CLI JSON" and noted metered commands wrap the body with model/cost — not always the raw API body.

Verify: tsc --noEmit clean, vitest 431 passed (428 + 3 new), tsup build succeeds.

@achris7

achris7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Already reviewed.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 76 out of 76 changed files in this pull request and generated 1 comment.

Suppressed comments (8)

src/main.ts:74

  • The unknown command name is unsanitized argv data, allowing terminal escape sequences to reach stderr. Apply sanitizeText before printing it.
    src/commands/activity.ts:92
  • As with --since, Date.parse accepts non-ISO date strings here even though the API contract requires ISO 8601. Validate the timestamp syntax explicitly so invalid CLI input consistently exits as usage error 2.
    if (Number.isNaN(Date.parse(flags.until))) {

src/commands/budget.ts:47

  • The API can serialize key IDs as numbers while config IDs may be strings. This strict comparison then misses the machine's key and can display the first key's budget instead. Compare canonical string forms, as the keys and status commands already do.
    ? keys.find((k) => k.id === agent.keyId)

src/commands/status.ts:46

  • When the configured agent has been deleted and there is no other active agent, agent is undefined and this expression incorrectly leaves configStale false. Track the configured-agent lookup separately so a missing configured agent is always reported as stale.
  const agent =
    profile.agents.find((a) => configured && String(a.id) === String(configured.id)) ??
    profile.agents.find((a) => a.status === 'active');
  const configStale = Boolean(configured && agent && String(agent.id) !== String(configured.id));

src/main.ts:57

  • The unknown help target comes directly from argv and is written without sanitization, so an argument containing OSC/CSI bytes can control the terminal. Sanitize it before interpolation, consistent with the command error paths below.

This issue also appears on line 74 of the same file.
src/main.ts:87

  • parseArgs error messages include the user-provided option text. Writing the message verbatim leaves this error path vulnerable to terminal escape injection, unlike the sanitized runtime error paths.
    src/commands/activity.ts:86
  • Date.parse accepts many non-ISO inputs such as locale-style dates, despite this flag promising and forwarding an ISO 8601 timestamp. Those values pass client validation and are then rejected or interpreted differently by the API; require an explicit ISO timestamp shape before parsing.

This issue also appears on line 92 of the same file.

    if (Number.isNaN(Date.parse(flags.since))) {

src/commands/funds.ts:414

  • The polling loop treats every failure as transient, including 401/403 and other permanent 4xx responses. If the credential expires, the command waits the full two minutes and misleadingly reports that no deposit arrived; rethrow non-rate-limit client errors immediately.
      } catch {
        // Transient API error mid-watch — keep polling until the deadline.
      }

Comment thread src/commands/funds.ts Outdated
…eset

Number(presetRaw) on a huge amount loses precision / becomes Infinity → JSON
serializes the preset as null, sending the wrong (or no) preset to the card
checkout. Guard with Number.isSafeInteger before dividing. (Copilot, High)
@achris7

achris7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Fixed — funds onramp preset now guards the raw→Number step with Number.isSafeInteger and throws a UsageError for an oversized --amount, so a value that would lose precision / become Infinity (→ JSON null) can't send the wrong preset to the card checkout. Same overflow class as the duration fix. typecheck clean; funds tests (17) pass.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@src/commands/funds.ts`:
- Around line 445-449: Update the amount validation around presetFiat and the
usdToRaw conversion so that after deriving the fiat number from the safe raw
integer, converting it back to raw must equal the original raw value. Throw the
existing UsageError for values that fail this round-trip check, while preserving
acceptance of values that round-trip exactly.
🪄 Autofix

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: ASSERTIVE

Plan: Pro

Run ID: 78e2ed15-9157-4589-951e-26391a5a8fe1

📥 Commits

Reviewing files that changed from the base of the PR and between 338ccfe and f33b1ba.

📒 Files selected for processing (1)
  • src/commands/funds.ts

Comment thread src/commands/funds.ts Outdated
…iat float

raw/1e6 can lose precision even for a safe-integer raw (e.g. 9007199254740991
-> 9007199254.740992), billing a different amount than validated. Reject when
Math.round(fiat*1e6) !== raw. (CodeRabbit)
@achris7

achris7 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Good catch — added a float round-trip guard alongside the overflow one. The onramp preset now rejects any --amount where Math.round((raw/1e6)*1e6) !== raw, so a value that survives isSafeInteger but loses precision through the divide (e.g. 9007199254740991 → 9007199254.740992) can't bill a different amount than validated. Spot-checked: $10 / $0.05 / $100 accept, 9007199254740991 rejects. typecheck clean.

@achris7
achris7 merged commit ad3a5b0 into main Aug 7, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants