@namzu/cli 8.1.0 → 8.3.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.
- package/dist/doctor/checks/credentials.d.ts.map +1 -1
- package/dist/doctor/checks/credentials.js +2 -0
- package/dist/doctor/checks/credentials.js.map +1 -1
- package/dist/integrations/providers/credential-store.d.ts +118 -0
- package/dist/integrations/providers/credential-store.d.ts.map +1 -0
- package/dist/integrations/providers/credential-store.js +322 -0
- package/dist/integrations/providers/credential-store.js.map +1 -0
- package/dist/integrations/providers/discover.d.ts +37 -10
- package/dist/integrations/providers/discover.d.ts.map +1 -1
- package/dist/integrations/providers/discover.js +43 -13
- package/dist/integrations/providers/discover.js.map +1 -1
- package/dist/integrations/providers/identity.d.ts +75 -0
- package/dist/integrations/providers/identity.d.ts.map +1 -0
- package/dist/integrations/providers/identity.js +75 -0
- package/dist/integrations/providers/identity.js.map +1 -0
- package/dist/integrations/providers/index.d.ts +5 -2
- package/dist/integrations/providers/index.d.ts.map +1 -1
- package/dist/integrations/providers/index.js +5 -2
- package/dist/integrations/providers/index.js.map +1 -1
- package/dist/integrations/providers/oauth.d.ts +37 -7
- package/dist/integrations/providers/oauth.d.ts.map +1 -1
- package/dist/integrations/providers/oauth.js +62 -17
- package/dist/integrations/providers/oauth.js.map +1 -1
- package/dist/integrations/providers/registry.d.ts +16 -0
- package/dist/integrations/providers/registry.d.ts.map +1 -1
- package/dist/integrations/providers/registry.js +19 -0
- package/dist/integrations/providers/registry.js.map +1 -1
- package/dist/integrations/providers/subscription-login.d.ts +123 -0
- package/dist/integrations/providers/subscription-login.d.ts.map +1 -0
- package/dist/integrations/providers/subscription-login.js +348 -0
- package/dist/integrations/providers/subscription-login.js.map +1 -0
- package/dist/tui/App.d.ts.map +1 -1
- package/dist/tui/App.js +166 -7
- package/dist/tui/App.js.map +1 -1
- package/dist/tui/Picker.d.ts +28 -1
- package/dist/tui/Picker.d.ts.map +1 -1
- package/dist/tui/Picker.js +71 -20
- package/dist/tui/Picker.js.map +1 -1
- package/dist/tui/agent.d.ts +24 -0
- package/dist/tui/agent.d.ts.map +1 -1
- package/dist/tui/agent.js +68 -11
- package/dist/tui/agent.js.map +1 -1
- package/dist/tui/credential-entry.d.ts +54 -18
- package/dist/tui/credential-entry.d.ts.map +1 -1
- package/dist/tui/credential-entry.js +67 -24
- package/dist/tui/credential-entry.js.map +1 -1
- package/dist/tui/login-prompt.d.ts +41 -0
- package/dist/tui/login-prompt.d.ts.map +1 -0
- package/dist/tui/login-prompt.js +69 -0
- package/dist/tui/login-prompt.js.map +1 -0
- package/dist/tui/open-browser.d.ts +24 -0
- package/dist/tui/open-browser.d.ts.map +1 -0
- package/dist/tui/open-browser.js +57 -0
- package/dist/tui/open-browser.js.map +1 -0
- package/dist/tui/slashCommands.d.ts +16 -0
- package/dist/tui/slashCommands.d.ts.map +1 -1
- package/dist/tui/slashCommands.js +13 -0
- package/dist/tui/slashCommands.js.map +1 -1
- 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.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAiEH,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,2CAo+CpC"}
|