spawn/packages/cli/package.json
A 844808cd7d
Some checks failed
CLI Release / Build and release CLI (push) Has been cancelled
Lint / ShellCheck (push) Has been cancelled
Lint / Biome Lint (push) Has been cancelled
Lint / macOS Compatibility (push) Has been cancelled
feat(ssh): let user pick SSH key when handshake auth keeps failing (#3402)
* feat(ssh): let user pick SSH key when handshake auth keeps failing

When `waitForSsh` sees consecutive "Permission denied (publickey)"
responses from the remote, the auto-discovered set of `~/.ssh/*` keys
clearly isn't the right one. Today the user just watches the same
error scroll past until the retry budget runs out.

This adds an interactive picker after 2 consecutive auth failures (and
only in interactive mode). The picker lists every discovered key (with
type + an "already tried" hint), lets the user paste a custom path, or
keep retrying with the current set. The chosen path replaces the `-i`
identity flags on the next handshake attempt.

- New `promptForSshKey()` in `shared/ssh-keys.ts` — clack-driven picker
  with custom-path support and `~` expansion. Returns `null` in
  non-interactive mode so unattended runs are unaffected.
- `waitForSsh` tracks consecutive publickey rejections, ignores
  unrelated transient failures (timeouts, connection refused), and
  offers the picker once per call when the threshold is hit.
- Adds dedicated unit tests covering non-interactive bypass, the skip
  path, key selection, custom-path entry, `~/` expansion, and the
  "already tried" hint.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(ssh): resolve rebase conflict — use discoverLegacyKeys in promptForSshKey

After #3401 refactored ssh-keys.ts (renamed discoverSshKeys → discoverLegacyKeys
+ getSpawnKey), the key-picker PR's promptForSshKey still called the old
discoverSshKeys name. Update to use discoverLegacyKeys() directly for picker
options (spawn key is excluded since it's always the default, not a choice).
Also remove now-unused readdirSync import.

Agent: pr-maintainer
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(ssh): final-fallback picker after exhausted retries + remember choice

Builds on the mid-loop picker: now if every auto-discovered key still
gets rejected after the retry budget runs out, the user gets one more
chance to point spawn at the right key — and the choice is persisted
to ~/.config/spawn/preferences.json (sshKeyPath) so subsequent spawn
runs use it directly with no further prompts.

- getPreferredSshKeyPath() reads the saved path and gracefully ignores
  stale entries (file deleted, malformed JSON, missing field).
- setPreferredSshKeyPath() merges the value into preferences.json
  while preserving every other field (models, starPromptShownAt).
- clearPreferredSshKeyPath() is added for completeness / recovery.
- ensureSshKeys() honors the saved preference — if set, only that key
  is offered to SSH (rather than diluting the explicit choice with
  the spawn-managed key + legacy fallbacks).
- waitForSsh now offers a final fallback picker after the main loop
  exhausts (only if every failure was publickey-auth and the user is
  interactive). One extra handshake attempt with the picked key. On
  success — mid-loop swap or final fallback — the chosen key is
  persisted via setPreferredSshKeyPath.
- 10 new unit tests cover the preference round-trip, malformed-JSON
  / stale-path resilience, field preservation, directory creation,
  clearPreferredSshKeyPath, and ensureSshKeys honoring/falling back
  as expected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude <claude@anthropic.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: B <6723574+louisgv@users.noreply.github.com>
2026-05-11 22:16:33 -07:00

27 lines
696 B
JSON

{
"name": "@openrouter/spawn",
"version": "1.0.43",
"type": "module",
"bin": {
"spawn": "cli.js"
},
"scripts": {
"dev": "bun run src/index.ts",
"build": "bun build src/index.ts --outfile cli.js --target bun --minify --packages bundle",
"compile": "bun build src/index.ts --compile --outfile spawn",
"lint": "biome lint src/",
"test": "bun test",
"test:watch": "bun test --watch"
},
"dependencies": {
"@clack/prompts": "1.0.0",
"@daytonaio/sdk": "0.160.0",
"@openrouter/spawn-shared": "workspace:*",
"picocolors": "1.1.1",
"valibot": "1.2.0"
},
"devDependencies": {
"@biomejs/biome": "2.4.3",
"@types/bun": "1.3.8"
}
}