osborn 0.8.9 → 0.8.10
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/claude-auth.js +49 -52
- package/package.json +1 -1
package/dist/claude-auth.js
CHANGED
|
@@ -147,67 +147,64 @@ export async function checkClaudeAuthStatus() {
|
|
|
147
147
|
// ─────────────────────────────────────────
|
|
148
148
|
/**
|
|
149
149
|
* Extract OAuth URL from CLI output.
|
|
150
|
-
* Strips ALL whitespace first (like vutran1710/claudebox) to handle
|
|
151
|
-
* Ink UI wrapping the URL across multiple lines.
|
|
152
|
-
* Also cleans trailing "Pastecodehereifprompted" that Ink appends.
|
|
153
150
|
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
151
|
+
* Strips ALL whitespace first (like vutran1710/claudebox) to handle Ink UI
|
|
152
|
+
* wrapping the URL across multiple lines. Cleans trailing junk the Ink UI
|
|
153
|
+
* appends (e.g. "Pastecodehereifprompted") and any stray ESC bytes.
|
|
154
|
+
*
|
|
155
|
+
* Note on redirect_uri: we used to strip it hoping claude.ai would fall back
|
|
156
|
+
* to an in-page code display, but claude.ai REQUIRES redirect_uri and returns
|
|
157
|
+
* "Invalid OAuth Request: Missing redirect_uri parameter" when it's missing.
|
|
158
|
+
* The pinned Claude Code client ID (9d1c250a-e61b-44d9-88ed-5944d1962f5e)
|
|
159
|
+
* only has http://localhost:<port>/callback URIs in its whitelist, so we
|
|
160
|
+
* can't rewrite to a public callback either — it would be rejected.
|
|
161
|
+
*
|
|
162
|
+
* Actual flow that works: keep the localhost redirect AS-IS. User clicks the
|
|
163
|
+
* URL on any device, authorizes. claude.ai 302s the browser to the localhost
|
|
164
|
+
* URL (which is unreachable). The browser shows "connection refused" but
|
|
165
|
+
* leaves the full URL in the address bar — including ?code=XXX&state=YYY.
|
|
166
|
+
* The user copies the `code` value from the address bar and pastes it into
|
|
167
|
+
* the modal. This is ugly on mobile but it's the only flow the server
|
|
168
|
+
* accepts.
|
|
160
169
|
*/
|
|
161
170
|
function extractOAuthUrl(text) {
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
|
|
171
|
+
// Replace ANSI control sequences AND lone ESC bytes with a NUL sentinel.
|
|
172
|
+
// Why NUL and not a space: we need to strip all whitespace next (to
|
|
173
|
+
// unwrap URLs that Ink wrapped across terminal lines), and if we used
|
|
174
|
+
// a space it would be eaten by the whitespace strip — then text on
|
|
175
|
+
// either side of the control sequence would fuse into the URL. NUL
|
|
176
|
+
// survives the whitespace strip and acts as a hard boundary the
|
|
177
|
+
// tail-cut logic below can detect.
|
|
178
|
+
const SENTINEL = '\x00';
|
|
179
|
+
const noAnsi = text
|
|
180
|
+
.replace(/\x1B\[[0-9;]*[A-Za-z]/g, SENTINEL)
|
|
181
|
+
.replace(/\x1B\][^\x07]*\x07/g, SENTINEL)
|
|
182
|
+
.replace(/\x1B/g, SENTINEL);
|
|
165
183
|
// Strip all whitespace (claudebox pattern: strings.Join(strings.Fields(pane), ""))
|
|
184
|
+
// to unwrap URLs split across terminal lines. NUL sentinels survive.
|
|
166
185
|
const stripped = noAnsi.replace(/\s+/g, '');
|
|
167
186
|
const match = stripped.match(URL_REGEX);
|
|
168
187
|
if (!match)
|
|
169
188
|
return null;
|
|
170
189
|
let url = match[0];
|
|
171
|
-
//
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
//
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
* user is on the same machine as the CLI, but on a sprite the URL points to
|
|
190
|
-
* the *sprite's* localhost — unreachable from the user's browser regardless
|
|
191
|
-
* of whether they open the auth link on their PC or their phone. With no
|
|
192
|
-
* redirect_uri at all, claude.ai falls back to its in-page code display
|
|
193
|
-
* (the same flow that `claude setup-token`'s "Paste code here if prompted"
|
|
194
|
-
* Ink input is built to consume), and the user can paste the code back into
|
|
195
|
-
* our modal — which works whether they signed in on phone or desktop.
|
|
196
|
-
*
|
|
197
|
-
* Done with regex rather than `new URL()` because the URL constructor
|
|
198
|
-
* normalizes the path (which can break Claude's strict redirect check)
|
|
199
|
-
* and re-encodes spaces/special chars in other params.
|
|
200
|
-
*/
|
|
201
|
-
function stripRedirectUri(url) {
|
|
202
|
-
const before = url;
|
|
203
|
-
// Three cases: leading param (?redirect_uri=...&), middle/trailing (&redirect_uri=...),
|
|
204
|
-
// and only param (?redirect_uri=...). Order matters so cleanup leaves the URL well-formed.
|
|
205
|
-
url = url.replace(/&redirect_uri=[^&]*/g, '');
|
|
206
|
-
url = url.replace(/\?redirect_uri=[^&]*&/g, '?');
|
|
207
|
-
url = url.replace(/\?redirect_uri=[^&]*$/g, '');
|
|
208
|
-
if (before !== url) {
|
|
209
|
-
console.log('🔑 Stripped localhost redirect_uri from OAuth URL — claude.ai will show a pasteable code instead of redirecting');
|
|
210
|
-
}
|
|
190
|
+
// Cut at the first NUL sentinel — that marks where an ANSI control code
|
|
191
|
+
// USED to be, which is a reliable boundary between the URL and adjacent
|
|
192
|
+
// terminal output that got fused by the whitespace strip.
|
|
193
|
+
const sentinelCut = url.indexOf(SENTINEL);
|
|
194
|
+
if (sentinelCut > 0)
|
|
195
|
+
url = url.substring(0, sentinelCut);
|
|
196
|
+
// Cut at the first `paste` (case-insensitive) — Ink always appends a
|
|
197
|
+
// "Paste code here if prompted" input box after the URL, and any case
|
|
198
|
+
// variant of it is junk. None of Claude's query-param values begin
|
|
199
|
+
// with "paste" so this is safe.
|
|
200
|
+
const pasteCut = url.toLowerCase().indexOf('paste');
|
|
201
|
+
if (pasteCut > 0)
|
|
202
|
+
url = url.substring(0, pasteCut);
|
|
203
|
+
// Defensive: cut at any byte outside the URL-valid character set. OAuth
|
|
204
|
+
// URLs use letters, digits, `%`, and URL-safe punctuation only.
|
|
205
|
+
const tailCut = url.search(/[^A-Za-z0-9%._~:/?#\[\]@!$&'()*+,;=\-]/);
|
|
206
|
+
if (tailCut > 0)
|
|
207
|
+
url = url.substring(0, tailCut);
|
|
211
208
|
return url;
|
|
212
209
|
}
|
|
213
210
|
// ─────────────────────────────────────────
|