osborn 0.8.12 → 0.8.13
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 +51 -13
- package/package.json +1 -1
package/dist/claude-auth.js
CHANGED
|
@@ -74,6 +74,26 @@ function resolveClaudePath() {
|
|
|
74
74
|
// Constants
|
|
75
75
|
// ─────────────────────────────────────────
|
|
76
76
|
const CREDENTIALS_PATH = join(homedir(), '.claude', '.credentials.json');
|
|
77
|
+
/**
|
|
78
|
+
* Strip ALL ANSI escape sequences from a string — CSI (including private
|
|
79
|
+
* prefixes like `?`), OSC (both BEL and ST terminators), and lone ESC bytes.
|
|
80
|
+
*
|
|
81
|
+
* This is a superset of the common `/\x1B\[[0-9;]*[A-Za-z]/g` pattern, which
|
|
82
|
+
* misses private-prefix modes like `\x1B[?2026h` and string terminators like
|
|
83
|
+
* `\x1B\\`. Claude's Ink UI uses these extensively and they leak into error
|
|
84
|
+
* messages and URLs when we only strip the basic CSI form.
|
|
85
|
+
*/
|
|
86
|
+
function stripAnsi(text) {
|
|
87
|
+
return text
|
|
88
|
+
// Full CSI: ESC [ <intermediates 0x20–0x3F> <final 0x40–0x7E>
|
|
89
|
+
.replace(/\x1B\[[\x20-\x3F]*[\x40-\x7E]/g, '')
|
|
90
|
+
// OSC terminated by BEL (0x07) — ESC ] <content> BEL
|
|
91
|
+
.replace(/\x1B\][^\x07]*\x07/g, '')
|
|
92
|
+
// OSC terminated by ST (ESC \) — ESC ] <content> ESC \
|
|
93
|
+
.replace(/\x1B\][^\x1B]*\x1B\\/g, '')
|
|
94
|
+
// Any remaining lone ESC bytes (e.g. ESC \ string terminator used standalone)
|
|
95
|
+
.replace(/\x1B/g, '');
|
|
96
|
+
}
|
|
77
97
|
// URL matching: strip all whitespace first (like claudebox), then match
|
|
78
98
|
// Handles Ink UI wrapping URLs across multiple lines
|
|
79
99
|
const URL_REGEX = /https:\/\/claude\.(com|ai)\/cai\/oauth\/authorize[^\s]*/;
|
|
@@ -168,18 +188,20 @@ export async function checkClaudeAuthStatus() {
|
|
|
168
188
|
* accepts.
|
|
169
189
|
*/
|
|
170
190
|
function extractOAuthUrl(text) {
|
|
171
|
-
// Replace ANSI control sequences
|
|
172
|
-
//
|
|
173
|
-
//
|
|
174
|
-
// a
|
|
175
|
-
//
|
|
176
|
-
//
|
|
177
|
-
//
|
|
191
|
+
// Replace ALL ANSI control sequences with a NUL sentinel. NUL (not a
|
|
192
|
+
// space) because we strip all whitespace next to unwrap URLs that Ink
|
|
193
|
+
// split across terminal lines — a space would vanish and text on either
|
|
194
|
+
// side of a control sequence would fuse into the URL. NUL survives the
|
|
195
|
+
// strip and acts as a hard boundary the tail-cut below can detect.
|
|
196
|
+
//
|
|
197
|
+
// Uses the same patterns as stripAnsi() but replaces with SENTINEL
|
|
198
|
+
// instead of '' so boundaries are preserved.
|
|
178
199
|
const SENTINEL = '\x00';
|
|
179
200
|
const noAnsi = text
|
|
180
|
-
.replace(/\x1B\[[
|
|
181
|
-
.replace(/\x1B\][^\x07]*\x07/g, SENTINEL)
|
|
182
|
-
.replace(/\x1B
|
|
201
|
+
.replace(/\x1B\[[\x20-\x3F]*[\x40-\x7E]/g, SENTINEL) // Full CSI (incl. private-prefix ?/</>/=)
|
|
202
|
+
.replace(/\x1B\][^\x07]*\x07/g, SENTINEL) // OSC terminated by BEL
|
|
203
|
+
.replace(/\x1B\][^\x1B]*\x1B\\/g, SENTINEL) // OSC terminated by ST (ESC \)
|
|
204
|
+
.replace(/\x1B/g, SENTINEL); // Lone ESC bytes
|
|
183
205
|
// Strip all whitespace (claudebox pattern: strings.Join(strings.Fields(pane), ""))
|
|
184
206
|
// to unwrap URLs split across terminal lines. NUL sentinels survive.
|
|
185
207
|
const stripped = noAnsi.replace(/\s+/g, '');
|
|
@@ -223,7 +245,23 @@ export function runClaudeAuthFlow(callbacks) {
|
|
|
223
245
|
const handle = {
|
|
224
246
|
submitCode: (code) => {
|
|
225
247
|
if (procRef) {
|
|
226
|
-
|
|
248
|
+
let trimmed = code.trim();
|
|
249
|
+
// User may paste the full callback URL instead of just the code:
|
|
250
|
+
// http://localhost:38719/callback?code=ZIgFd5nApQMR7...&state=TSLp6...
|
|
251
|
+
// Extract the bare `code` value so the CLI accepts it.
|
|
252
|
+
if (trimmed.startsWith('http://') || trimmed.startsWith('https://')) {
|
|
253
|
+
try {
|
|
254
|
+
const u = new URL(trimmed);
|
|
255
|
+
const extracted = u.searchParams.get('code');
|
|
256
|
+
if (extracted) {
|
|
257
|
+
console.log(`🔑 Extracted code from pasted callback URL (${extracted.length} chars)`);
|
|
258
|
+
trimmed = extracted;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
catch {
|
|
262
|
+
// Not a valid URL, use as-is
|
|
263
|
+
}
|
|
264
|
+
}
|
|
227
265
|
console.log(`🔑 Submitting auth code to Claude CLI (${trimmed.length} chars)`);
|
|
228
266
|
// Ink reads raw keypresses. Write in chunks to simulate typing.
|
|
229
267
|
const CHUNK_SIZE = 10;
|
|
@@ -274,7 +312,7 @@ export function runClaudeAuthFlow(callbacks) {
|
|
|
274
312
|
}
|
|
275
313
|
}, AUTH_TIMEOUT_MS);
|
|
276
314
|
proc.onData((data) => {
|
|
277
|
-
const clean = data
|
|
315
|
+
const clean = stripAnsi(data)
|
|
278
316
|
.replace(/\x1B\][^\x07]*\x07/g, '');
|
|
279
317
|
fullBuffer += clean;
|
|
280
318
|
recentBuffer += clean;
|
|
@@ -332,7 +370,7 @@ export function runClaudeAuthFlow(callbacks) {
|
|
|
332
370
|
}
|
|
333
371
|
// Detect errors
|
|
334
372
|
if (/OAuth error|Invalid code|expired/i.test(recentBuffer)) {
|
|
335
|
-
const errMsg = recentBuffer
|
|
373
|
+
const errMsg = stripAnsi(recentBuffer).trim().substring(0, 200);
|
|
336
374
|
console.log('⚠️ Claude auth error:', errMsg);
|
|
337
375
|
callbacks.onError(errMsg);
|
|
338
376
|
recentBuffer = '';
|