@namzu/cli 8.2.0 → 8.4.0

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.
Files changed (76) hide show
  1. package/dist/doctor/checks/credentials.d.ts.map +1 -1
  2. package/dist/doctor/checks/credentials.js +2 -0
  3. package/dist/doctor/checks/credentials.js.map +1 -1
  4. package/dist/integrations/providers/credential-store.d.ts +118 -0
  5. package/dist/integrations/providers/credential-store.d.ts.map +1 -0
  6. package/dist/integrations/providers/credential-store.js +322 -0
  7. package/dist/integrations/providers/credential-store.js.map +1 -0
  8. package/dist/integrations/providers/discover.d.ts +37 -10
  9. package/dist/integrations/providers/discover.d.ts.map +1 -1
  10. package/dist/integrations/providers/discover.js +67 -13
  11. package/dist/integrations/providers/discover.js.map +1 -1
  12. package/dist/integrations/providers/identity.d.ts +75 -0
  13. package/dist/integrations/providers/identity.d.ts.map +1 -0
  14. package/dist/integrations/providers/identity.js +75 -0
  15. package/dist/integrations/providers/identity.js.map +1 -0
  16. package/dist/integrations/providers/index.d.ts +4 -1
  17. package/dist/integrations/providers/index.d.ts.map +1 -1
  18. package/dist/integrations/providers/index.js +4 -1
  19. package/dist/integrations/providers/index.js.map +1 -1
  20. package/dist/integrations/providers/oauth.d.ts +48 -7
  21. package/dist/integrations/providers/oauth.d.ts.map +1 -1
  22. package/dist/integrations/providers/oauth.js +73 -17
  23. package/dist/integrations/providers/oauth.js.map +1 -1
  24. package/dist/integrations/providers/subscription-login.d.ts +123 -0
  25. package/dist/integrations/providers/subscription-login.d.ts.map +1 -0
  26. package/dist/integrations/providers/subscription-login.js +348 -0
  27. package/dist/integrations/providers/subscription-login.js.map +1 -0
  28. package/dist/tui/App.d.ts +1 -17
  29. package/dist/tui/App.d.ts.map +1 -1
  30. package/dist/tui/App.js +185 -49
  31. package/dist/tui/App.js.map +1 -1
  32. package/dist/tui/Markdown.d.ts +14 -0
  33. package/dist/tui/Markdown.d.ts.map +1 -1
  34. package/dist/tui/Markdown.js +31 -4
  35. package/dist/tui/Markdown.js.map +1 -1
  36. package/dist/tui/Picker.js +4 -0
  37. package/dist/tui/Picker.js.map +1 -1
  38. package/dist/tui/Transcript.d.ts +18 -2
  39. package/dist/tui/Transcript.d.ts.map +1 -1
  40. package/dist/tui/Transcript.js +48 -13
  41. package/dist/tui/Transcript.js.map +1 -1
  42. package/dist/tui/agent.d.ts.map +1 -1
  43. package/dist/tui/agent.js +15 -7
  44. package/dist/tui/agent.js.map +1 -1
  45. package/dist/tui/bottom-spacer.d.ts +22 -8
  46. package/dist/tui/bottom-spacer.d.ts.map +1 -1
  47. package/dist/tui/bottom-spacer.js.map +1 -1
  48. package/dist/tui/credential-entry.d.ts +16 -12
  49. package/dist/tui/credential-entry.d.ts.map +1 -1
  50. package/dist/tui/credential-entry.js +16 -12
  51. package/dist/tui/credential-entry.js.map +1 -1
  52. package/dist/tui/live-window.d.ts +97 -0
  53. package/dist/tui/live-window.d.ts.map +1 -0
  54. package/dist/tui/live-window.js +140 -0
  55. package/dist/tui/live-window.js.map +1 -0
  56. package/dist/tui/login-prompt.d.ts +41 -0
  57. package/dist/tui/login-prompt.d.ts.map +1 -0
  58. package/dist/tui/login-prompt.js +69 -0
  59. package/dist/tui/login-prompt.js.map +1 -0
  60. package/dist/tui/markdown-block-cache.d.ts +57 -0
  61. package/dist/tui/markdown-block-cache.d.ts.map +1 -0
  62. package/dist/tui/markdown-block-cache.js +74 -0
  63. package/dist/tui/markdown-block-cache.js.map +1 -0
  64. package/dist/tui/markdownParser.d.ts +33 -0
  65. package/dist/tui/markdownParser.d.ts.map +1 -1
  66. package/dist/tui/markdownParser.js +124 -54
  67. package/dist/tui/markdownParser.js.map +1 -1
  68. package/dist/tui/open-browser.d.ts +24 -0
  69. package/dist/tui/open-browser.d.ts.map +1 -0
  70. package/dist/tui/open-browser.js +57 -0
  71. package/dist/tui/open-browser.js.map +1 -0
  72. package/dist/tui/slashCommands.d.ts +16 -0
  73. package/dist/tui/slashCommands.d.ts.map +1 -1
  74. package/dist/tui/slashCommands.js +13 -0
  75. package/dist/tui/slashCommands.js.map +1 -1
  76. package/package.json +3 -3
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Obtaining a subscription credential from inside namzu.
3
+ *
4
+ * namzu could already USE and REFRESH a plan-backed credential; it could not
5
+ * get one. The only way in was to install another product, sign in there, and
6
+ * let discovery borrow what that product had stored — which works on exactly
7
+ * one operating system and requires the other product. This is the missing
8
+ * half: an authorization-code exchange, run by namzu, that ends with a
9
+ * credential in namzu's own store (`credential-store.ts`).
10
+ *
11
+ * ## The flow
12
+ *
13
+ * Authorization code with PKCE (RFC 7636, S256), redirected to a loopback
14
+ * address (RFC 8252 §7.3). Two ways to finish, and BOTH are always offered:
15
+ *
16
+ * 1. **Loopback.** A one-request server on `127.0.0.1` at the port the
17
+ * redirect is registered under. The browser lands on it and the code
18
+ * never touches a screen.
19
+ * 2. **Paste.** The operator copies the code — or the whole redirect URL —
20
+ * out of a browser and hands it back.
21
+ *
22
+ * The second is not a consolation prize. A container, a remote shell over
23
+ * SSH, a machine with no graphical session: none of them can be redirected
24
+ * to, and on all of them the loopback server is either unreachable from
25
+ * wherever the browser actually is or cannot bind at all. `start()` says
26
+ * which of the two it managed to arrange (`loopback`), and the paste path
27
+ * works whether or not it did.
28
+ *
29
+ * ## What never happens here
30
+ *
31
+ * No token is logged, printed, or put in an error. Failures name the status,
32
+ * the endpoint and the stage; they never carry the response body, because a
33
+ * token endpoint's body is where the token is. Nothing is written to the
34
+ * store until an exchange has fully succeeded, so an abandoned or rejected
35
+ * login leaves no partial credential behind.
36
+ */
37
+ import type { DetectedProvider } from './discover.js';
38
+ import type { AgentOAuthCredential } from './keychain.js';
39
+ /** What a completed attempt produced. Never carries a secret in `reason`. */
40
+ export type LoginOutcome = {
41
+ readonly ok: true;
42
+ /** The stored credential, so the caller can open a session without re-reading. */
43
+ readonly credential: AgentOAuthCredential;
44
+ /** Where it landed, so a surface can say so without guessing the path. */
45
+ readonly storedAt: string;
46
+ } | {
47
+ readonly ok: false;
48
+ readonly reason: string;
49
+ };
50
+ export interface SubscriptionLogin {
51
+ /** The page to open. Safe to print — it carries a challenge, never a secret. */
52
+ readonly url: string;
53
+ readonly redirectUri: string;
54
+ /**
55
+ * Whether the loopback listener came up.
56
+ *
57
+ * `false` is not a failure: the port was busy, or the environment forbids
58
+ * listening. The paste path is unaffected, and a surface should say so
59
+ * rather than imply the login is broken.
60
+ */
61
+ readonly loopback: boolean;
62
+ /**
63
+ * Resolve when the browser reaches the loopback listener, or never.
64
+ *
65
+ * `null` when `loopback` is false, so a caller cannot await a promise that
66
+ * has no way to settle. Racing this against `completeWithPastedCode` is the
67
+ * intended use; whichever finishes first wins and `cancel()` releases the
68
+ * other.
69
+ */
70
+ waitForCallback(): Promise<LoginOutcome> | null;
71
+ /** Finish from a pasted redirect URL, `code#state`, or a bare code. */
72
+ completeWithPastedCode(input: string): Promise<LoginOutcome>;
73
+ /** Release the listener. Safe to call twice, and safe to call after success. */
74
+ cancel(): void;
75
+ }
76
+ export interface SubscriptionLoginOptions {
77
+ /** Override `homedir()` — tests, and an operator with a relocated home. */
78
+ readonly home?: string;
79
+ /** Override the token-exchange transport (tests inject a stub). */
80
+ readonly fetch?: typeof fetch;
81
+ /**
82
+ * Try to listen on the loopback redirect. Default true.
83
+ *
84
+ * A caller that knows there is no browser here — `--no-browser`, a
85
+ * container — passes false and skips a bind that would only ever time out.
86
+ */
87
+ readonly loopback?: boolean;
88
+ }
89
+ /**
90
+ * Begin a login. Returns the page to open plus the two ways to finish it.
91
+ *
92
+ * Deliberately does NOT open a browser. Which of `open`, `xdg-open` or a
93
+ * Windows protocol handler to spawn — and whether spawning anything is even
94
+ * appropriate — is a property of the surface the operator is sitting in front
95
+ * of, not of the protocol. A module that reached for a browser would be
96
+ * unusable from the one place that most needs it: a machine that has none.
97
+ */
98
+ export declare function beginSubscriptionLogin(options?: SubscriptionLoginOptions): Promise<SubscriptionLogin>;
99
+ /**
100
+ * The detected-provider record a completed sign-in produces.
101
+ *
102
+ * Shaped exactly like a discovered one — the same shape `discoverProviders`
103
+ * would return on the NEXT launch, having read the file this login just
104
+ * wrote — so every downstream path treats it identically and nothing has to
105
+ * learn that a session began with a login. It carries `origin: 'stored'`, so
106
+ * the between-turn refresh writes back to the file rather than to a Keychain
107
+ * this machine may not have.
108
+ */
109
+ export declare function subscriptionDetectedProvider(credential: AgentOAuthCredential, storedAt: string): DetectedProvider;
110
+ /**
111
+ * Pull a code (and a state, when there is one) out of whatever got pasted.
112
+ *
113
+ * Four spellings reach this in practice: the full redirect address, its query
114
+ * string alone, the `code#state` pair a consent screen offers for copying,
115
+ * and a bare code. Anything else yields no code and is refused by the caller
116
+ * — guessing at a fifth shape would send an arbitrary string to a token
117
+ * endpoint as if it were an authorization code.
118
+ */
119
+ export declare function parsePastedInput(input: string): {
120
+ code?: string;
121
+ state?: string;
122
+ };
123
+ //# sourceMappingURL=subscription-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription-login.d.ts","sourceRoot":"","sources":["../../../src/integrations/providers/subscription-login.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAMH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAA;AASrD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AAGzD,6EAA6E;AAC7E,MAAM,MAAM,YAAY,GACrB;IACA,QAAQ,CAAC,EAAE,EAAE,IAAI,CAAA;IACjB,kFAAkF;IAClF,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAA;IACzC,0EAA0E;IAC1E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CACxB,GACD;IAAE,QAAQ,CAAC,EAAE,EAAE,KAAK,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAElD,MAAM,WAAW,iBAAiB;IACjC,gFAAgF;IAChF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B;;;;;;;OAOG;IACH,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;IAC/C,uEAAuE;IACvE,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IAC5D,gFAAgF;IAChF,MAAM,IAAI,IAAI,CAAA;CACd;AAED,MAAM,WAAW,wBAAwB;IACxC,2EAA2E;IAC3E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED;;;;;;;;GAQG;AACH,wBAAsB,sBAAsB,CAC3C,OAAO,GAAE,wBAA6B,GACpC,OAAO,CAAC,iBAAiB,CAAC,CA6D5B;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,CAC3C,UAAU,EAAE,oBAAoB,EAChC,QAAQ,EAAE,MAAM,GACd,gBAAgB,CAYlB;AA4KD;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,CA+BjF"}
@@ -0,0 +1,348 @@
1
+ /**
2
+ * Obtaining a subscription credential from inside namzu.
3
+ *
4
+ * namzu could already USE and REFRESH a plan-backed credential; it could not
5
+ * get one. The only way in was to install another product, sign in there, and
6
+ * let discovery borrow what that product had stored — which works on exactly
7
+ * one operating system and requires the other product. This is the missing
8
+ * half: an authorization-code exchange, run by namzu, that ends with a
9
+ * credential in namzu's own store (`credential-store.ts`).
10
+ *
11
+ * ## The flow
12
+ *
13
+ * Authorization code with PKCE (RFC 7636, S256), redirected to a loopback
14
+ * address (RFC 8252 §7.3). Two ways to finish, and BOTH are always offered:
15
+ *
16
+ * 1. **Loopback.** A one-request server on `127.0.0.1` at the port the
17
+ * redirect is registered under. The browser lands on it and the code
18
+ * never touches a screen.
19
+ * 2. **Paste.** The operator copies the code — or the whole redirect URL —
20
+ * out of a browser and hands it back.
21
+ *
22
+ * The second is not a consolation prize. A container, a remote shell over
23
+ * SSH, a machine with no graphical session: none of them can be redirected
24
+ * to, and on all of them the loopback server is either unreachable from
25
+ * wherever the browser actually is or cannot bind at all. `start()` says
26
+ * which of the two it managed to arrange (`loopback`), and the paste path
27
+ * works whether or not it did.
28
+ *
29
+ * ## What never happens here
30
+ *
31
+ * No token is logged, printed, or put in an error. Failures name the status,
32
+ * the endpoint and the stage; they never carry the response body, because a
33
+ * token endpoint's body is where the token is. Nothing is written to the
34
+ * store until an exchange has fully succeeded, so an abandoned or rejected
35
+ * login leaves no partial credential behind.
36
+ */
37
+ import { createHash, randomBytes, timingSafeEqual } from 'node:crypto';
38
+ import { createServer } from 'node:http';
39
+ import { writeStoredSubscriptionCredential } from './credential-store.js';
40
+ import { AUTHORIZE_URL, OAUTH_CLIENT_ID, OAUTH_SCOPES, OAUTH_TOKEN_URL, REDIRECT_URI, SUBSCRIPTION_REDIRECT_PORT, } from './identity.js';
41
+ import { PROVIDER_REGISTRY } from './registry.js';
42
+ /**
43
+ * Begin a login. Returns the page to open plus the two ways to finish it.
44
+ *
45
+ * Deliberately does NOT open a browser. Which of `open`, `xdg-open` or a
46
+ * Windows protocol handler to spawn — and whether spawning anything is even
47
+ * appropriate — is a property of the surface the operator is sitting in front
48
+ * of, not of the protocol. A module that reached for a browser would be
49
+ * unusable from the one place that most needs it: a machine that has none.
50
+ */
51
+ export async function beginSubscriptionLogin(options = {}) {
52
+ const verifier = base64Url(randomBytes(32));
53
+ const challenge = base64Url(createHash('sha256').update(verifier).digest());
54
+ // An independent value, not the verifier reused.
55
+ //
56
+ // Reusing the verifier as `state` is a thing implementations do, and it
57
+ // undoes PKCE: `state` travels in the authorization URL, so the verifier
58
+ // would sit in the address bar, the browser history and any referrer along
59
+ // the way — and the secret PKCE exists to keep out of that URL is exactly
60
+ // the verifier.
61
+ const state = base64Url(randomBytes(16));
62
+ const listener = options.loopback === false ? null : await openLoopback(state);
63
+ let settled = false;
64
+ const exchangeOnce = async (code, seenState) => {
65
+ if (settled)
66
+ return { ok: false, reason: 'This login was already completed.' };
67
+ if (!constantTimeEquals(seenState, state)) {
68
+ return {
69
+ ok: false,
70
+ reason: 'The sign-in did not come back with the value namzu sent, so it cannot be matched to this attempt. Nothing was stored. Start the login again.',
71
+ };
72
+ }
73
+ settled = true;
74
+ return exchange(code, state, verifier, options);
75
+ };
76
+ return {
77
+ url: authorizeUrl(challenge, state),
78
+ redirectUri: REDIRECT_URI,
79
+ loopback: listener !== null,
80
+ waitForCallback: () => listener === null
81
+ ? null
82
+ : listener.received.then((got) => got === null
83
+ ? {
84
+ ok: false,
85
+ reason: 'The sign-in was cancelled before the browser came back.',
86
+ }
87
+ : got.error
88
+ ? {
89
+ ok: false,
90
+ reason: `The sign-in did not complete (${got.error}). Nothing was stored.`,
91
+ }
92
+ : exchangeOnce(got.code, got.state)),
93
+ completeWithPastedCode: async (input) => {
94
+ const parsed = parsePastedInput(input);
95
+ if (!parsed.code) {
96
+ return {
97
+ ok: false,
98
+ reason: 'That does not contain an authorization code. Paste the whole address the browser finished on, or the code it showed you.',
99
+ };
100
+ }
101
+ return exchangeOnce(parsed.code, parsed.state ?? state);
102
+ },
103
+ cancel: () => listener?.close(),
104
+ };
105
+ }
106
+ /**
107
+ * The detected-provider record a completed sign-in produces.
108
+ *
109
+ * Shaped exactly like a discovered one — the same shape `discoverProviders`
110
+ * would return on the NEXT launch, having read the file this login just
111
+ * wrote — so every downstream path treats it identically and nothing has to
112
+ * learn that a session began with a login. It carries `origin: 'stored'`, so
113
+ * the between-turn refresh writes back to the file rather than to a Keychain
114
+ * this machine may not have.
115
+ */
116
+ export function subscriptionDetectedProvider(credential, storedAt) {
117
+ return {
118
+ entry: PROVIDER_REGISTRY.anthropic,
119
+ source: { kind: 'stored', path: storedAt },
120
+ apiKey: credential.accessToken,
121
+ oauth: {
122
+ refreshToken: credential.refreshToken,
123
+ expiresAt: credential.expiresAt,
124
+ origin: 'stored',
125
+ },
126
+ alternatives: [],
127
+ };
128
+ }
129
+ function authorizeUrl(challenge, state) {
130
+ const params = new URLSearchParams({
131
+ code: 'true',
132
+ client_id: OAUTH_CLIENT_ID,
133
+ response_type: 'code',
134
+ redirect_uri: REDIRECT_URI,
135
+ scope: OAUTH_SCOPES,
136
+ code_challenge: challenge,
137
+ code_challenge_method: 'S256',
138
+ state,
139
+ });
140
+ return `${AUTHORIZE_URL}?${params.toString()}`;
141
+ }
142
+ /**
143
+ * Exchange the code, and store the result only once it is whole.
144
+ *
145
+ * The store write is the LAST thing. A response missing an access token, a
146
+ * non-2xx, a transport failure and a malformed body all return before it, so
147
+ * there is no path on which a failed login leaves a file behind.
148
+ */
149
+ async function exchange(code, state, verifier, options) {
150
+ const fetchFn = options.fetch ?? globalThis.fetch;
151
+ let res;
152
+ try {
153
+ res = await fetchFn(OAUTH_TOKEN_URL, {
154
+ method: 'POST',
155
+ headers: { 'content-type': 'application/json', accept: 'application/json' },
156
+ body: JSON.stringify({
157
+ grant_type: 'authorization_code',
158
+ client_id: OAUTH_CLIENT_ID,
159
+ code,
160
+ state,
161
+ redirect_uri: REDIRECT_URI,
162
+ code_verifier: verifier,
163
+ }),
164
+ signal: AbortSignal.timeout(30_000),
165
+ });
166
+ }
167
+ catch (err) {
168
+ // The MESSAGE of a transport error is safe (a hostname, a timeout); the
169
+ // body of a response is not, and there is none here.
170
+ return {
171
+ ok: false,
172
+ reason: `Could not reach the sign-in service (${err instanceof Error ? err.message : String(err)}). Nothing was stored.`,
173
+ };
174
+ }
175
+ if (!res.ok) {
176
+ // The status, never the body. A token endpoint answers failures with a
177
+ // document that can contain the very thing this module must not leak.
178
+ return {
179
+ ok: false,
180
+ reason: `The sign-in service refused the exchange (HTTP ${res.status}). The code may have already been used, or expired. Nothing was stored.`,
181
+ };
182
+ }
183
+ let data;
184
+ try {
185
+ data = (await res.json());
186
+ }
187
+ catch {
188
+ return {
189
+ ok: false,
190
+ reason: 'The sign-in service answered with something that is not a token. Nothing was stored.',
191
+ };
192
+ }
193
+ if (typeof data.access_token !== 'string' || data.access_token.length === 0) {
194
+ return {
195
+ ok: false,
196
+ reason: 'The sign-in service answered without a token. Nothing was stored.',
197
+ };
198
+ }
199
+ const credential = {
200
+ accessToken: data.access_token,
201
+ ...(typeof data.refresh_token === 'string' && data.refresh_token.length > 0
202
+ ? { refreshToken: data.refresh_token }
203
+ : {}),
204
+ ...(typeof data.expires_in === 'number'
205
+ ? { expiresAt: Date.now() + data.expires_in * 1_000 }
206
+ : {}),
207
+ };
208
+ let storedAt;
209
+ try {
210
+ storedAt = writeStoredSubscriptionCredential(credential, options.home);
211
+ }
212
+ catch (err) {
213
+ // The store refused to prove the file private, so there is no file. Say
214
+ // what happened; the credential is dropped with the process.
215
+ return {
216
+ ok: false,
217
+ reason: `Signed in, but the credential could not be stored privately: ${err instanceof Error ? err.message : String(err)}`,
218
+ };
219
+ }
220
+ return { ok: true, credential, storedAt };
221
+ }
222
+ /**
223
+ * A single-purpose listener on the loopback redirect, or `null`.
224
+ *
225
+ * `null` on every failure to bind — the port is in use (a second namzu, or
226
+ * another product's login mid-flight), the sandbox forbids listening, there
227
+ * is no loopback interface. None of those is fatal, because the paste path
228
+ * does not need this.
229
+ */
230
+ async function openLoopback(expectedState) {
231
+ let settle = () => { };
232
+ const received = new Promise((r) => {
233
+ let done = false;
234
+ settle = (v) => {
235
+ if (done)
236
+ return;
237
+ done = true;
238
+ r(v);
239
+ };
240
+ });
241
+ const server = createServer((req, res) => {
242
+ const url = new URL(req.url ?? '/', `http://127.0.0.1:${SUBSCRIPTION_REDIRECT_PORT}`);
243
+ const error = url.searchParams.get('error');
244
+ const code = url.searchParams.get('code');
245
+ const state = url.searchParams.get('state');
246
+ const finish = (status, text) => {
247
+ res.writeHead(status, { 'content-type': 'text/plain; charset=utf-8' });
248
+ res.end(text);
249
+ };
250
+ if (error) {
251
+ finish(400, 'Sign-in did not complete. Return to namzu.');
252
+ settle({ code: '', state: '', error });
253
+ return;
254
+ }
255
+ if (!code || !state) {
256
+ // Not our redirect — a browser probing the port, a favicon request.
257
+ // Answering and NOT settling keeps the listener alive for the real one.
258
+ finish(404, 'Not the sign-in callback.');
259
+ return;
260
+ }
261
+ if (!constantTimeEquals(state, expectedState)) {
262
+ // Refused here as well as at the exchange. This is the request a
263
+ // cross-site attempt would make, and answering it with "done" would
264
+ // tell whoever made it that the port is a live login.
265
+ finish(400, 'Sign-in did not complete. Return to namzu.');
266
+ return;
267
+ }
268
+ finish(200, 'Signed in. You can close this tab and return to namzu.');
269
+ settle({ code, state });
270
+ });
271
+ const bound = await new Promise((resolve) => {
272
+ server.once('error', () => resolve(false));
273
+ server.listen(SUBSCRIPTION_REDIRECT_PORT, '127.0.0.1', () => resolve(true));
274
+ });
275
+ if (!bound) {
276
+ server.close();
277
+ return null;
278
+ }
279
+ return {
280
+ received,
281
+ close: () => {
282
+ settle(null);
283
+ server.close();
284
+ },
285
+ };
286
+ }
287
+ /**
288
+ * Pull a code (and a state, when there is one) out of whatever got pasted.
289
+ *
290
+ * Four spellings reach this in practice: the full redirect address, its query
291
+ * string alone, the `code#state` pair a consent screen offers for copying,
292
+ * and a bare code. Anything else yields no code and is refused by the caller
293
+ * — guessing at a fifth shape would send an arbitrary string to a token
294
+ * endpoint as if it were an authorization code.
295
+ */
296
+ export function parsePastedInput(input) {
297
+ const value = input.trim();
298
+ if (value.length === 0)
299
+ return {};
300
+ if (/^https?:\/\//i.test(value)) {
301
+ try {
302
+ const url = new URL(value);
303
+ return {
304
+ code: url.searchParams.get('code') ?? undefined,
305
+ state: url.searchParams.get('state') ?? undefined,
306
+ };
307
+ }
308
+ catch {
309
+ return {};
310
+ }
311
+ }
312
+ if (value.includes('code=')) {
313
+ const params = new URLSearchParams(value.replace(/^\?/, ''));
314
+ return {
315
+ code: params.get('code') ?? undefined,
316
+ state: params.get('state') ?? undefined,
317
+ };
318
+ }
319
+ if (value.includes('#')) {
320
+ const [code, state] = value.split('#', 2);
321
+ return { code: code || undefined, state: state || undefined };
322
+ }
323
+ // A bare code. Whitespace already ruled out by the trim + the checks above
324
+ // only when it contained a separator, so reject an obviously-wrong paste
325
+ // (a sentence, a wrapped line) rather than posting it.
326
+ if (/\s/.test(value))
327
+ return {};
328
+ return { code: value };
329
+ }
330
+ /**
331
+ * Compare two opaque values without leaking their relationship through timing.
332
+ *
333
+ * `state` is compared, not a secret, so this is belt-and-braces — but the
334
+ * cheap version of this comparison is `===`, and `===` on strings of equal
335
+ * length is the shape that teaches the next person to write it for the
336
+ * verifier too.
337
+ */
338
+ function constantTimeEquals(a, b) {
339
+ const left = Buffer.from(a, 'utf8');
340
+ const right = Buffer.from(b, 'utf8');
341
+ if (left.length !== right.length)
342
+ return false;
343
+ return timingSafeEqual(left, right);
344
+ }
345
+ function base64Url(bytes) {
346
+ return bytes.toString('base64url');
347
+ }
348
+ //# sourceMappingURL=subscription-login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription-login.js","sourceRoot":"","sources":["../../../src/integrations/providers/subscription-login.ts"],"names":[],"mappings":"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"}
package/dist/tui/App.d.ts CHANGED
@@ -10,25 +10,9 @@
10
10
  * 4. If discovery returned zero providers → show <Picker/> in
11
11
  * empty-state mode (explains where to put credentials).
12
12
  */
13
- import type { TranscriptMessage, TuiContext } from './types.js';
13
+ import type { TuiContext } from './types.js';
14
14
  export interface AppProps {
15
15
  readonly ctx: TuiContext;
16
16
  }
17
- /**
18
- * Every line the finalized transcript will print, for the spacer to measure.
19
- *
20
- * Exported so the wiring is testable rather than only typecheckable. The
21
- * spacer's own docblock states the asymmetry it depends on: over-count the
22
- * content and the composer merely floats, under-count it and the composer is
23
- * pushed off the bottom. The caller used to pass each row's `content` alone,
24
- * which counted a six-line collapsed tool body as nothing at all — the estimate
25
- * ran low by six per tool call, in the direction that costs the usability
26
- * rather than the feature. `/expand` makes it acute: a row whose entire
27
- * substance is a two-hundred-line body would have been handed over as one line.
28
- *
29
- * A pending row is excluded because it is not in the static log yet; the
30
- * spacer's `liveRows` covers the live region.
31
- */
32
- export declare function spacerTranscript(messages: readonly TranscriptMessage[]): readonly string[];
33
17
  export declare function App({ ctx }: AppProps): import("react/jsx-runtime").JSX.Element;
34
18
  //# sourceMappingURL=App.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AA0DH,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CACxB;AAWD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,SAAS,iBAAiB,EAAE,GAAG,SAAS,MAAM,EAAE,CAY1F;AAGD,wBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,2CAq3CpC"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAkEH,OAAO,KAAK,EAAqB,UAAU,EAAE,MAAM,YAAY,CAAA;AAE/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;CACxB;AAsBD,wBAAgB,GAAG,CAAC,EAAE,GAAG,EAAE,EAAE,QAAQ,2CAuhDpC"}