privateer-agent 0.3.4 → 0.3.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,7 +58,7 @@ const ANCHOR = [
58
58
  " .-----. ", // lock body top (the shackle's base)
59
59
  " | o | ", // lock body + keyhole
60
60
  " '--+--' ", // lock body base, shank exits
61
- " /\\ | /\\ ", // stock — arms flare from the shank (each \\ is one backslash)
61
+ " /\\ | /\\ ", // stock — arms flare from the shank (each \\ is one backslash)
62
62
  " \\ | / ", // arms
63
63
  " \\_|_/ ", // flukes
64
64
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "privateer-agent",
3
- "version": "0.3.4",
3
+ "version": "0.3.5",
4
4
  "description": "Privateer — a provider-agnostic, safe-by-default terminal coding agent with TEE/Tinfoil attestation, rebuilt on the Pi toolkit. Bring your own model across 20 providers.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -66,18 +66,34 @@ export async function fetchAccountModels(): Promise<string[]> {
66
66
  export const privateerOAuthProvider = {
67
67
  name: "Privateer account",
68
68
  usesCallbackServer: false,
69
- async login(cb: { onDeviceCode?: (info: unknown) => void }) {
69
+ // Pi's login dialog passes `signal` (its cancel AbortController) alongside the
70
+ // callbacks. We MUST thread it into runDeviceLogin — otherwise escape/ctrl+c
71
+ // aborts the dialog's signal but our poll loop never sees it, the login()
72
+ // promise never settles, and Pi never restores the editor: the "Waiting for
73
+ // authentication…" screen hangs with no way out. See auth/privateer.ts
74
+ // pollForToken, which checks the signal and rejects with "Login cancelled.".
75
+ async login(cb: { onDeviceCode?: (info: unknown) => void; signal?: AbortSignal }) {
70
76
  if (!hasCredentials()) {
71
- await runDeviceLogin({
72
- onCode: (code) =>
73
- cb.onDeviceCode?.({
74
- userCode: code.user_code,
75
- verificationUri: code.verification_uri_complete ?? code.verification_uri ?? "",
76
- intervalSeconds: code.interval,
77
- expiresInSeconds: code.expires_in,
78
- }),
79
- });
77
+ try {
78
+ await runDeviceLogin({
79
+ signal: cb.signal,
80
+ onCode: (code) =>
81
+ cb.onDeviceCode?.({
82
+ userCode: code.user_code,
83
+ verificationUri: code.verification_uri_complete ?? code.verification_uri ?? "",
84
+ intervalSeconds: code.interval,
85
+ expiresInSeconds: code.expires_in,
86
+ }),
87
+ });
88
+ } catch (e) {
89
+ // Normalize the cancel message to exactly "Login cancelled" (no period):
90
+ // Pi's login dialog only suppresses its "Failed to login…" error toast for
91
+ // that exact string, so a cancel should exit quietly, not flash an error.
92
+ if (cb.signal?.aborted) throw new Error("Login cancelled");
93
+ throw e;
94
+ }
80
95
  }
96
+ if (cb.signal?.aborted) throw new Error("Login cancelled");
81
97
  return spawnAccountCredentials();
82
98
  },
83
99
  async refreshToken(creds: { refresh: string }) {