patchwork-os 1.2.0-beta.3.canary.887 → 1.2.0-beta.3.canary.889

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.
@@ -1,41 +1,33 @@
1
1
  /**
2
- * Claude Code subscription OAuth 2.0 + PKCE flow.
3
- *
4
- * Implements the three endpoints the dashboard settings "Connect Claude" UX
5
- * expects on the bridge:
2
+ * Claude sign-in relay.
6
3
  *
7
4
  * POST /auth/claude/start → { sessionId, url }
8
- * POST /auth/claude/complete → { token }
9
- * POST /auth/claude/cancel → { ok: true }
5
+ * POST /auth/claude/complete → { ok, loggedIn, authMethod } body { sessionId, code }
6
+ * POST /auth/claude/cancel → { ok: true } body { sessionId? }
7
+ * GET /auth/claude/status → { loggedIn, authMethod }
8
+ * POST /auth/claude/logout → { ok: true }
10
9
  *
11
- * Flow:
12
- * 1. start — generate PKCE verifier/challenge, build the claude.ai auth URL,
13
- * store the verifier in an in-memory session map, return the URL.
14
- * 2. User opens the URL, signs in, and copies the authorization code shown on
15
- * https://platform.claude.com/oauth/code/callback.
16
- * 3. complete — exchange the code + verifier for an access token at
17
- * https://api.anthropic.com/v1/oauth/token. Return the token.
18
- * 4. Dashboard POSTs the token to /api/auth/anthropic-key → control-plane
19
- * stores it encrypted and re-provisions the container with
20
- * CLAUDE_CODE_OAUTH_TOKEN=<token> so the subprocess driver works.
21
- */
22
- import type { IncomingMessage, ServerResponse } from "node:http";
23
- /**
24
- * Turn whatever the user pasted into `{ code, state }`.
10
+ * Sign-in is performed by the UNMODIFIED Claude Code CLI (`claude auth login`),
11
+ * which talks to Anthropic and stores its own login where Claude Code always
12
+ * keeps it. This module only relays two things: the sign-in link the CLI
13
+ * prints (out to the person) and the code the person pastes (into the CLI's
14
+ * stdin). It never sees, stores, logs or returns a Claude credential.
25
15
  *
26
- * Anthropic's callback page renders the value as `<code>#<state>`, and users
27
- * variously paste that verbatim, paste just the code, or paste the entire
28
- * callback URL out of the address bar. All three must work.
16
+ * Why: Anthropic does not permit applications to run their own Claude.ai
17
+ * login, or to collect, store or intermediate Claude.ai credentials; sign-in
18
+ * must complete through Anthropic's own flow. The previous implementation
19
+ * ran its own OAuth exchange and returned the access token (and dropped the
20
+ * refresh token, so the login also expired within hours).
29
21
  *
30
- * `state` matters: /v1/oauth/token rejects a body without it as
31
- * `invalid_request_error / "Invalid request format"` — verified against the
32
- * live endpoint 2026-08-12. Sending only `code` failed for every user, which
33
- * is why the flow never worked regardless of what was pasted.
22
+ * The binary is `PATCHWORK_CLAUDE_BINARY` or `claude` on PATH, run with the
23
+ * bridge's HOME / CLAUDE_CONFIG_DIR so the login lands where every later
24
+ * `claude` invocation by this bridge will find it.
34
25
  */
35
- export declare function parsePastedAuthCode(raw: string): {
36
- code: string;
37
- state?: string;
38
- };
26
+ import type { IncomingMessage, ServerResponse } from "node:http";
27
+ /** Test seam: drop any in-flight session. */
28
+ export declare function _resetClaudeLoginForTesting(): void;
39
29
  export declare function handleClaudeAuthStart(_req: IncomingMessage, res: ServerResponse): Promise<void>;
40
30
  export declare function handleClaudeAuthComplete(req: IncomingMessage, res: ServerResponse): Promise<void>;
41
31
  export declare function handleClaudeAuthCancel(req: IncomingMessage, res: ServerResponse): Promise<void>;
32
+ export declare function handleClaudeAuthStatus(_req: IncomingMessage, res: ServerResponse): Promise<void>;
33
+ export declare function handleClaudeAuthLogout(_req: IncomingMessage, res: ServerResponse): Promise<void>;
@@ -1,108 +1,131 @@
1
1
  /**
2
- * Claude Code subscription OAuth 2.0 + PKCE flow.
3
- *
4
- * Implements the three endpoints the dashboard settings "Connect Claude" UX
5
- * expects on the bridge:
2
+ * Claude sign-in relay.
6
3
  *
7
4
  * POST /auth/claude/start → { sessionId, url }
8
- * POST /auth/claude/complete → { token }
9
- * POST /auth/claude/cancel → { ok: true }
5
+ * POST /auth/claude/complete → { ok, loggedIn, authMethod } body { sessionId, code }
6
+ * POST /auth/claude/cancel → { ok: true } body { sessionId? }
7
+ * GET /auth/claude/status → { loggedIn, authMethod }
8
+ * POST /auth/claude/logout → { ok: true }
9
+ *
10
+ * Sign-in is performed by the UNMODIFIED Claude Code CLI (`claude auth login`),
11
+ * which talks to Anthropic and stores its own login where Claude Code always
12
+ * keeps it. This module only relays two things: the sign-in link the CLI
13
+ * prints (out to the person) and the code the person pastes (into the CLI's
14
+ * stdin). It never sees, stores, logs or returns a Claude credential.
10
15
  *
11
- * Flow:
12
- * 1. start — generate PKCE verifier/challenge, build the claude.ai auth URL,
13
- * store the verifier in an in-memory session map, return the URL.
14
- * 2. User opens the URL, signs in, and copies the authorization code shown on
15
- * https://platform.claude.com/oauth/code/callback.
16
- * 3. complete — exchange the code + verifier for an access token at
17
- * https://api.anthropic.com/v1/oauth/token. Return the token.
18
- * 4. Dashboard POSTs the token to /api/auth/anthropic-key → control-plane
19
- * stores it encrypted and re-provisions the container with
20
- * CLAUDE_CODE_OAUTH_TOKEN=<token> so the subprocess driver works.
16
+ * Why: Anthropic does not permit applications to run their own Claude.ai
17
+ * login, or to collect, store or intermediate Claude.ai credentials; sign-in
18
+ * must complete through Anthropic's own flow. The previous implementation
19
+ * ran its own OAuth exchange and returned the access token (and dropped the
20
+ * refresh token, so the login also expired within hours).
21
+ *
22
+ * The binary is `PATCHWORK_CLAUDE_BINARY` or `claude` on PATH, run with the
23
+ * bridge's HOME / CLAUDE_CONFIG_DIR so the login lands where every later
24
+ * `claude` invocation by this bridge will find it.
21
25
  */
22
- import { createHash, randomBytes } from "node:crypto";
23
- // ---------------------------------------------------------------------------
24
- // Constants (extracted from the claude binary via strings analysis)
25
- // ---------------------------------------------------------------------------
26
- // Claude Code's public OAuth client id. It must be a UUID: claude.ai's
27
- // /oauth/authorize rejects a non-UUID client_id ("Input should be a valid
28
- // UUID") and renders an "OAuth Request Failed" page, so the client-metadata
29
- // URL that used to live here made the flow unreachable before the user could
30
- // even approve. Public by design — an OAuth public client id is an
31
- // identifier, not a secret; PKCE is what protects the exchange.
32
- const CLAUDE_OAUTH_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
33
- const CLAUDE_OAUTH_AUTH_URL = "https://claude.com/cai/oauth/authorize";
34
- const CLAUDE_OAUTH_TOKEN_URL = "https://api.anthropic.com/v1/oauth/token";
35
- const CLAUDE_OAUTH_REDIRECT_URI = "https://platform.claude.com/oauth/code/callback";
36
- const CLAUDE_OAUTH_SCOPES = "user:inference user:profile org:create_api_key";
37
- // Sessions expire after 10 minutes — long enough for a user to complete the
38
- // browser flow, short enough not to accumulate stale entries.
26
+ import { spawn } from "node:child_process";
27
+ import { randomBytes } from "node:crypto";
28
+ import { ensureCmdShim } from "./winShim.js";
29
+ const URL_TIMEOUT_MS = 20_000;
30
+ const COMPLETE_TIMEOUT_MS = 30_000;
31
+ const STATUS_TIMEOUT_MS = 15_000;
39
32
  const SESSION_TTL_MS = 10 * 60 * 1000;
40
- // http-routes-4: hard ceiling on the in-memory session map. An authenticated
41
- // caller flooding POST /auth/claude/start faster than the 10-minute TTL expires
42
- // entries would otherwise grow the map without bound (memory-exhaustion DoS).
43
- // 500 concurrent in-flight OAuth flows is far more than any real deployment.
44
- const MAX_SESSIONS = 500;
45
- // Hard ceiling on the Anthropic token-exchange request (audit 2026-06-08 auth-1).
46
- const TOKEN_EXCHANGE_TIMEOUT_MS = 15_000;
47
- const sessions = new Map();
48
- function pruneExpiredSessions() {
49
- const now = Date.now();
50
- for (const [id, s] of sessions) {
51
- if (now - s.createdAt > SESSION_TTL_MS)
52
- sessions.delete(id);
33
+ /** Anthropic's sign-in pages; any other URL the CLI prints is not relayed. */
34
+ const SIGN_IN_URL_RE = /https:\/\/(?:claude\.com|claude\.ai)\/[^\s"'<>]+/;
35
+ /** One sign-in at a time per bridge: a new start replaces any pending one. */
36
+ let current = null;
37
+ function claudeBinary() {
38
+ // npm installs `claude` as a `.cmd` shim on Windows, which spawn() cannot
39
+ // resolve from a bare name; same helper as the Claude subprocess driver.
40
+ return ensureCmdShim(process.env.PATCHWORK_CLAUDE_BINARY || "claude");
41
+ }
42
+ /** Only what Claude Code needs to find its own config and reach the network. */
43
+ function childEnv() {
44
+ const keep = [
45
+ "HOME",
46
+ "PATH",
47
+ "USER",
48
+ "LANG",
49
+ "TMPDIR",
50
+ "CLAUDE_CONFIG_DIR",
51
+ "HTTPS_PROXY",
52
+ "HTTP_PROXY",
53
+ "NO_PROXY",
54
+ "SSL_CERT_FILE",
55
+ "NODE_EXTRA_CA_CERTS",
56
+ ];
57
+ const env = {};
58
+ for (const k of keep)
59
+ if (process.env[k] !== undefined)
60
+ env[k] = process.env[k];
61
+ // Never let a stray credential in the bridge's env decide which account
62
+ // Claude Code signs in as.
63
+ return env;
64
+ }
65
+ function endSession(s) {
66
+ if (!s)
67
+ return;
68
+ try {
69
+ s.child.stdin?.end();
53
70
  }
71
+ catch {
72
+ /* already closed */
73
+ }
74
+ if (s.child.exitCode === null)
75
+ s.child.kill("SIGTERM");
54
76
  }
55
- // ---------------------------------------------------------------------------
56
- // PKCE helpers
57
- // ---------------------------------------------------------------------------
58
- function generateCodeVerifier() {
59
- return randomBytes(32).toString("base64url");
77
+ /** Test seam: drop any in-flight session. */
78
+ export function _resetClaudeLoginForTesting() {
79
+ endSession(current);
80
+ current = null;
60
81
  }
61
- function deriveCodeChallenge(verifier) {
62
- return createHash("sha256").update(verifier).digest("base64url");
82
+ function run(args, timeoutMs) {
83
+ return new Promise((resolve) => {
84
+ let stdout = "";
85
+ let settled = false;
86
+ const child = spawn(claudeBinary(), args, {
87
+ env: childEnv(),
88
+ stdio: ["ignore", "pipe", "pipe"],
89
+ });
90
+ const timer = setTimeout(() => {
91
+ child.kill("SIGTERM");
92
+ }, timeoutMs);
93
+ child.stdout?.on("data", (d) => {
94
+ stdout += d.toString("utf8");
95
+ });
96
+ child.on("error", (err) => {
97
+ if (settled)
98
+ return;
99
+ settled = true;
100
+ clearTimeout(timer);
101
+ resolve({ code: null, stdout, spawnError: err.code ?? "spawn_failed" });
102
+ });
103
+ child.on("close", (code) => {
104
+ if (settled)
105
+ return;
106
+ settled = true;
107
+ clearTimeout(timer);
108
+ resolve({ code, stdout });
109
+ });
110
+ });
63
111
  }
64
- // ---------------------------------------------------------------------------
65
- // Pasted-value normalisation
66
- // ---------------------------------------------------------------------------
67
- /**
68
- * Turn whatever the user pasted into `{ code, state }`.
69
- *
70
- * Anthropic's callback page renders the value as `<code>#<state>`, and users
71
- * variously paste that verbatim, paste just the code, or paste the entire
72
- * callback URL out of the address bar. All three must work.
73
- *
74
- * `state` matters: /v1/oauth/token rejects a body without it as
75
- * `invalid_request_error / "Invalid request format"` — verified against the
76
- * live endpoint 2026-08-12. Sending only `code` failed for every user, which
77
- * is why the flow never worked regardless of what was pasted.
78
- */
79
- export function parsePastedAuthCode(raw) {
80
- const trimmed = raw.trim();
81
- // Full callback URL — pull code/state out of the query string.
82
- if (/^https?:\/\//i.test(trimmed)) {
83
- try {
84
- const url = new URL(trimmed);
85
- const code = url.searchParams.get("code")?.trim();
86
- const state = url.searchParams.get("state")?.trim();
87
- if (code)
88
- return { code, state: state || undefined };
89
- }
90
- catch {
91
- // Not a parseable URL — fall through to the `#` handling below.
92
- }
112
+ async function readStatus() {
113
+ const r = await run(["auth", "status"], STATUS_TIMEOUT_MS);
114
+ if (r.spawnError)
115
+ return { error: "claude_not_installed" };
116
+ try {
117
+ const parsed = JSON.parse(r.stdout);
118
+ // Return only these two fields: the rest (e.g. email, organisation) is
119
+ // not this endpoint's business.
120
+ return {
121
+ loggedIn: parsed.loggedIn === true,
122
+ authMethod: typeof parsed.authMethod === "string" ? parsed.authMethod : "unknown",
123
+ };
93
124
  }
94
- // `<code>#<state>` — the shape the callback page displays.
95
- const hash = trimmed.indexOf("#");
96
- if (hash !== -1) {
97
- const code = trimmed.slice(0, hash).trim();
98
- const state = trimmed.slice(hash + 1).trim();
99
- return { code, state: state || undefined };
125
+ catch {
126
+ return { error: "status_unreadable" };
100
127
  }
101
- return { code: trimmed };
102
128
  }
103
- // ---------------------------------------------------------------------------
104
- // Body reader (reuse the same pattern as the bridge's other POST handlers)
105
- // ---------------------------------------------------------------------------
106
129
  async function readBody(req) {
107
130
  return new Promise((resolve, reject) => {
108
131
  const chunks = [];
@@ -127,48 +150,83 @@ function jsonReply(res, status, body) {
127
150
  });
128
151
  res.end(payload);
129
152
  }
130
- // ---------------------------------------------------------------------------
131
- // POST /auth/claude/start
132
- // ---------------------------------------------------------------------------
153
+ async function parseJson(req) {
154
+ try {
155
+ const raw = await readBody(req);
156
+ if (!raw.trim())
157
+ return {};
158
+ const v = JSON.parse(raw);
159
+ return v && typeof v === "object" && !Array.isArray(v)
160
+ ? v
161
+ : null;
162
+ }
163
+ catch {
164
+ return null;
165
+ }
166
+ }
167
+ // --- POST /auth/claude/start ----------------------------------------------
133
168
  export async function handleClaudeAuthStart(_req, res) {
134
- pruneExpiredSessions();
135
- // http-routes-4: reject new sessions once the (post-prune) map is at
136
- // capacity so a flood of /start requests can't exhaust memory.
137
- if (sessions.size >= MAX_SESSIONS) {
138
- jsonReply(res, 503, {
139
- error: "too_many_sessions",
140
- detail: "Too many in-flight authorization sessions. Try again shortly.",
169
+ endSession(current);
170
+ current = null;
171
+ let child;
172
+ try {
173
+ child = spawn(claudeBinary(), ["auth", "login", "--claudeai"], {
174
+ env: childEnv(),
175
+ stdio: ["pipe", "pipe", "pipe"],
141
176
  });
177
+ }
178
+ catch {
179
+ jsonReply(res, 503, { error: "claude_not_installed" });
142
180
  return;
143
181
  }
144
- const codeVerifier = generateCodeVerifier();
145
- const codeChallenge = deriveCodeChallenge(codeVerifier);
146
- const sessionId = randomBytes(16).toString("hex");
147
- sessions.set(sessionId, { sessionId, codeVerifier, createdAt: Date.now() });
148
- const params = new URLSearchParams({
149
- client_id: CLAUDE_OAUTH_CLIENT_ID,
150
- response_type: "code",
151
- redirect_uri: CLAUDE_OAUTH_REDIRECT_URI,
152
- scope: CLAUDE_OAUTH_SCOPES,
153
- code_challenge: codeChallenge,
154
- code_challenge_method: "S256",
155
- state: sessionId,
182
+ const session = {
183
+ sessionId: randomBytes(16).toString("hex"),
184
+ child,
185
+ createdAt: Date.now(),
186
+ output: "",
187
+ exited: new Promise((resolve) => {
188
+ child.on("close", (code) => resolve(code));
189
+ child.on("error", () => resolve(null));
190
+ }),
191
+ };
192
+ const outcome = await new Promise((resolve) => {
193
+ const timer = setTimeout(() => {
194
+ resolve({ error: "sign_in_link_timeout", status: 504 });
195
+ }, URL_TIMEOUT_MS);
196
+ const onData = (d) => {
197
+ session.output += d.toString("utf8");
198
+ const m = SIGN_IN_URL_RE.exec(session.output);
199
+ if (m) {
200
+ clearTimeout(timer);
201
+ resolve({ url: m[0] });
202
+ }
203
+ };
204
+ child.stdout?.on("data", onData);
205
+ child.stderr?.on("data", onData);
206
+ child.on("error", (err) => {
207
+ clearTimeout(timer);
208
+ resolve({
209
+ error: err.code === "ENOENT" ? "claude_not_installed" : "spawn_failed",
210
+ status: 503,
211
+ });
212
+ });
213
+ child.on("close", () => {
214
+ clearTimeout(timer);
215
+ resolve({ error: "sign_in_exited_early", status: 502 });
216
+ });
156
217
  });
157
- const url = `${CLAUDE_OAUTH_AUTH_URL}?${params.toString()}`;
158
- jsonReply(res, 200, { sessionId, url });
218
+ if ("error" in outcome) {
219
+ endSession(session);
220
+ jsonReply(res, outcome.status, { error: outcome.error });
221
+ return;
222
+ }
223
+ current = session;
224
+ jsonReply(res, 200, { sessionId: session.sessionId, url: outcome.url });
159
225
  }
160
- // ---------------------------------------------------------------------------
161
- // POST /auth/claude/complete { sessionId, code }
162
- // ---------------------------------------------------------------------------
226
+ // --- POST /auth/claude/complete -------------------------------------------
163
227
  export async function handleClaudeAuthComplete(req, res) {
164
- // http-routes-4: keep the session map tidy on the completion path too —
165
- // start() was the only pruning site, so abandoned flows lingered.
166
- pruneExpiredSessions();
167
- let body;
168
- try {
169
- body = JSON.parse(await readBody(req));
170
- }
171
- catch {
228
+ const body = await parseJson(req);
229
+ if (!body) {
172
230
  jsonReply(res, 400, { error: "invalid_json" });
173
231
  return;
174
232
  }
@@ -182,97 +240,76 @@ export async function handleClaudeAuthComplete(req, res) {
182
240
  });
183
241
  return;
184
242
  }
185
- const session = sessions.get(sessionId);
186
- if (!session) {
243
+ const s = current;
244
+ if (!s ||
245
+ s.sessionId !== sessionId ||
246
+ Date.now() - s.createdAt > SESSION_TTL_MS ||
247
+ s.child.exitCode !== null) {
187
248
  jsonReply(res, 404, {
188
249
  error: "session_not_found",
189
- detail: "Session expired or not found. Click 'Start over' to begin again.",
250
+ detail: "Sign-in expired or was replaced. Start again.",
190
251
  });
191
252
  return;
192
253
  }
193
- if (Date.now() - session.createdAt > SESSION_TTL_MS) {
194
- sessions.delete(sessionId);
195
- jsonReply(res, 410, {
196
- error: "session_expired",
197
- detail: "Authorization session expired. Click 'Start over' to begin again.",
198
- });
254
+ current = null;
255
+ // The code goes to Claude Code's stdin and nowhere else: not logged, not
256
+ // stored, not echoed back.
257
+ const outputBefore = s.output.length;
258
+ s.child.stdin?.write(`${code.trim()}\n`);
259
+ const exit = await Promise.race([
260
+ s.exited,
261
+ new Promise((r) => setTimeout(() => r("timeout"), COMPLETE_TIMEOUT_MS)),
262
+ ]);
263
+ endSession(s);
264
+ if (exit === "timeout") {
265
+ jsonReply(res, 504, { error: "sign_in_timeout" });
199
266
  return;
200
267
  }
201
- // Exchange authorization code + PKCE verifier for an access token.
202
- // Audit 2026-06-08 HIGH (auth-1): bound the fetch with an AbortController so a
203
- // slow/hung Anthropic endpoint can't keep the HTTP connection + session open
204
- // indefinitely. Map an abort to a 504 (distinct from a 502 network error).
205
- // The pasted value may be `<code>#<state>`, a bare code, or the whole
206
- // callback URL. Fall back to the session id for `state` — that is what we
207
- // put in the authorize URL, so it round-trips correctly when the user pasted
208
- // only the code half.
209
- const { code: authCode, state: pastedState } = parsePastedAuthCode(code);
210
- if (!authCode) {
211
- jsonReply(res, 400, {
212
- error: "missing_fields",
213
- detail: "sessionId and code required",
214
- });
268
+ const status = await readStatus();
269
+ if ("error" in status) {
270
+ jsonReply(res, 502, { error: status.error });
215
271
  return;
216
272
  }
217
- const authState = pastedState ?? session.sessionId;
218
- let tokenRes;
219
- const controller = new AbortController();
220
- const timeout = setTimeout(() => controller.abort(), TOKEN_EXCHANGE_TIMEOUT_MS);
221
- try {
222
- tokenRes = await fetch(CLAUDE_OAUTH_TOKEN_URL, {
223
- method: "POST",
224
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
225
- body: new URLSearchParams({
226
- grant_type: "authorization_code",
227
- client_id: CLAUDE_OAUTH_CLIENT_ID,
228
- code: authCode,
229
- state: authState,
230
- redirect_uri: CLAUDE_OAUTH_REDIRECT_URI,
231
- code_verifier: session.codeVerifier,
232
- }).toString(),
233
- signal: controller.signal,
234
- });
235
- }
236
- catch (err) {
237
- const aborted = err instanceof Error &&
238
- (err.name === "AbortError" || controller.signal.aborted);
239
- jsonReply(res, aborted ? 504 : 502, {
240
- error: aborted ? "token_exchange_timeout" : "token_exchange_failed",
241
- detail: aborted
242
- ? "Timed out contacting Anthropic. Please try again."
243
- : `Network error contacting Anthropic: ${err instanceof Error ? err.message : String(err)}`,
244
- });
245
- return;
246
- }
247
- finally {
248
- clearTimeout(timeout);
249
- }
250
- const tokenData = (await tokenRes.json().catch(() => ({})));
251
- if (!tokenRes.ok || !tokenData.access_token) {
273
+ // The CLI prints "Invalid code" yet still exits 0, and an earlier login
274
+ // would make `status` look signed in, so neither the exit code nor status
275
+ // alone proves THIS code worked: also read what it printed after the code.
276
+ const reply = s.output.slice(outputBefore);
277
+ const rejected = /invalid|error|fail/i.test(reply);
278
+ if (exit !== 0 || rejected || !status.loggedIn) {
252
279
  jsonReply(res, 400, {
253
- error: "token_exchange_rejected",
254
- detail: tokenData.error_description ??
255
- tokenData.error ??
256
- `Anthropic returned ${tokenRes.status}. Make sure you pasted the correct authorization code.`,
280
+ error: "invalid_code",
281
+ detail: "Claude Code did not accept that code. Copy the whole code and try again.",
257
282
  });
258
283
  return;
259
284
  }
260
- sessions.delete(sessionId);
261
- jsonReply(res, 200, { token: tokenData.access_token });
285
+ jsonReply(res, 200, { ok: true, ...status });
262
286
  }
263
- // ---------------------------------------------------------------------------
264
- // POST /auth/claude/cancel { sessionId? }
265
- // ---------------------------------------------------------------------------
287
+ // --- POST /auth/claude/cancel ---------------------------------------------
266
288
  export async function handleClaudeAuthCancel(req, res) {
267
- try {
268
- const body = JSON.parse(await readBody(req));
269
- if (typeof body.sessionId === "string")
270
- sessions.delete(body.sessionId);
271
- }
272
- catch {
273
- // Body parse failure is fine — we just won't delete a specific session.
289
+ const body = (await parseJson(req)) ?? {};
290
+ if (current &&
291
+ (body.sessionId === undefined || body.sessionId === current.sessionId)) {
292
+ endSession(current);
293
+ current = null;
274
294
  }
275
- pruneExpiredSessions();
276
295
  jsonReply(res, 200, { ok: true });
277
296
  }
297
+ // --- GET /auth/claude/status ----------------------------------------------
298
+ export async function handleClaudeAuthStatus(_req, res) {
299
+ const status = await readStatus();
300
+ if ("error" in status) {
301
+ jsonReply(res, status.error === "claude_not_installed" ? 503 : 502, status);
302
+ return;
303
+ }
304
+ jsonReply(res, 200, status);
305
+ }
306
+ // --- POST /auth/claude/logout ---------------------------------------------
307
+ export async function handleClaudeAuthLogout(_req, res) {
308
+ const r = await run(["auth", "logout"], STATUS_TIMEOUT_MS);
309
+ if (r.spawnError) {
310
+ jsonReply(res, 503, { error: "claude_not_installed" });
311
+ return;
312
+ }
313
+ jsonReply(res, r.code === 0 ? 200 : 502, { ok: r.code === 0 });
314
+ }
278
315
  //# sourceMappingURL=claudeAuthHttp.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"claudeAuthHttp.js","sourceRoot":"","sources":["../src/claudeAuthHttp.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"claudeAuthHttp.js","sourceRoot":"","sources":["../src/claudeAuthHttp.ts"],"names":[],"mappings":"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"}