@ponythewhite/base-context 1.0.2 → 1.0.4

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 (44) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/base-context-runtime/pyproject.toml +1 -1
  3. package/dist/base-context-runtime/src/rlm/mcp.py +39 -17
  4. package/dist/base-context-runtime/test/test_mcp.py +87 -1
  5. package/dist/base-context-runtime/uv.lock +1 -1
  6. package/dist/build-info.json +1 -1
  7. package/dist/bundle/{bundled-modules-PWOGKUQQ.js → bundled-modules-R5EAJ62K.js} +2 -2
  8. package/dist/bundle/{chunk-44PEHPOG.js → chunk-BV25CVRM.js} +322 -171
  9. package/dist/bundle/{chunk-IADCPVEI.js → chunk-XDJ56NJA.js} +26 -11
  10. package/dist/bundle/{cli-main-4DS7Y3PI.js → cli-main-OUKN743U.js} +2 -2
  11. package/dist/bundle/cli.js +1 -1
  12. package/dist/bundle/{main-SV7JLVEB.js → main-QPCXSW6X.js} +2 -2
  13. package/dist/cli/daemon-ps.d.ts.map +1 -1
  14. package/dist/cli/daemon-ps.js +1 -0
  15. package/dist/cli/daemon-ps.js.map +1 -1
  16. package/dist/core/auth-storage.d.ts +4 -0
  17. package/dist/core/auth-storage.d.ts.map +1 -1
  18. package/dist/core/auth-storage.js +77 -19
  19. package/dist/core/auth-storage.js.map +1 -1
  20. package/dist/core/kernel/bootstrap.d.ts.map +1 -1
  21. package/dist/core/kernel/bootstrap.js +5 -2
  22. package/dist/core/kernel/bootstrap.js.map +1 -1
  23. package/dist/core/mcp/mcp-manager.d.ts.map +1 -1
  24. package/dist/core/mcp/mcp-manager.js +2 -2
  25. package/dist/core/mcp/mcp-manager.js.map +1 -1
  26. package/dist/installer.mjs +5 -2
  27. package/dist/modes/daemon/daemon-supervisor-ownership.d.ts.map +1 -1
  28. package/dist/modes/daemon/daemon-supervisor-ownership.js +16 -16
  29. package/dist/modes/daemon/daemon-supervisor-ownership.js.map +1 -1
  30. package/dist/modes/interactive/auth-flows.d.ts.map +1 -1
  31. package/dist/modes/interactive/auth-flows.js +7 -6
  32. package/dist/modes/interactive/auth-flows.js.map +1 -1
  33. package/dist/modes/interactive/components/login-dialog.d.ts +3 -1
  34. package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -1
  35. package/dist/modes/interactive/components/login-dialog.js +3 -1
  36. package/dist/modes/interactive/components/login-dialog.js.map +1 -1
  37. package/dist/modes/interactive/components/menu-panel.d.ts +1 -0
  38. package/dist/modes/interactive/components/menu-panel.d.ts.map +1 -1
  39. package/dist/modes/interactive/components/menu-panel.js +3 -0
  40. package/dist/modes/interactive/components/menu-panel.js.map +1 -1
  41. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  42. package/dist/modes/interactive/interactive-mode.js +7 -5
  43. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  44. package/package.json +4 -4
@@ -347,7 +347,7 @@ import {
347
347
  watchWithErrorHandler,
348
348
  workerRosterEntryFromSummary,
349
349
  wrapTextWithAnsi
350
- } from "./chunk-IADCPVEI.js";
350
+ } from "./chunk-XDJ56NJA.js";
351
351
  import {
352
352
  array,
353
353
  boolean,
@@ -2348,7 +2348,7 @@ function formatErrorDetails(error) {
2348
2348
  return String(error);
2349
2349
  }
2350
2350
  async function startCallbackServer(expectedState) {
2351
- const { createServer: createServer3 } = await getNodeApis();
2351
+ const { createServer: createServer4 } = await getNodeApis();
2352
2352
  return new Promise((resolve26, reject) => {
2353
2353
  let settleWait;
2354
2354
  const waitForCodePromise = new Promise((resolveWait) => {
@@ -2359,7 +2359,7 @@ async function startCallbackServer(expectedState) {
2359
2359
  resolveWait(value);
2360
2360
  };
2361
2361
  });
2362
- const server = createServer3((req, res) => {
2362
+ const server = createServer4((req, res) => {
2363
2363
  try {
2364
2364
  const url2 = new URL(req.url || "", "http://localhost");
2365
2365
  if (url2.pathname !== CALLBACK_PATH) {
@@ -3263,6 +3263,7 @@ async function getOAuthApiKey(providerId, credentials) {
3263
3263
  }
3264
3264
 
3265
3265
  // ../ai/src/mcp/oauth.ts
3266
+ import { createServer } from "node:http";
3266
3267
  var CALLBACK_HOST3 = process.env.BASE_CONTEXT_OAUTH_CALLBACK_HOST || "127.0.0.1";
3267
3268
  var CALLBACK_PORT_BASE = Number(process.env.BASE_CONTEXT_MCP_OAUTH_CALLBACK_PORT || 53700);
3268
3269
  var CALLBACK_PORT_COUNT = 10;
@@ -3271,6 +3272,8 @@ var CALLBACK_PORTS = Array.from({ length: CALLBACK_PORT_COUNT }, (_2, i) => CALL
3271
3272
  var redirectUriFor = (port) => `http://localhost:${port}${CALLBACK_PATH2}`;
3272
3273
  var ALL_REDIRECT_URIS = CALLBACK_PORTS.map(redirectUriFor);
3273
3274
  var TOKEN_EXPIRY_BUFFER_MS = 5 * 60 * 1e3;
3275
+ var OAUTH_REQUEST_TIMEOUT_MS = 3e4;
3276
+ var OAUTH_MAX_RESPONSE_BYTES = 1024 * 1024;
3274
3277
  function validatedHttpsUrl(value, name) {
3275
3278
  let url2;
3276
3279
  try {
@@ -3296,15 +3299,74 @@ function authorizationServerMetadataUrls(issuer) {
3296
3299
  new URL(`${path6}/.well-known/openid-configuration`, url2.origin).toString()
3297
3300
  ];
3298
3301
  }
3299
- async function fetchResponse(url2, init) {
3300
- return fetch(url2, { ...init, redirect: "error" });
3302
+ async function withResponse(url2, init, read) {
3303
+ const controller = new AbortController();
3304
+ const signal = init.signal ? AbortSignal.any([init.signal, controller.signal]) : controller.signal;
3305
+ const timer = setTimeout(() => controller.abort(new Error("MCP OAuth request timed out")), OAUTH_REQUEST_TIMEOUT_MS);
3306
+ let response;
3307
+ try {
3308
+ signal.throwIfAborted();
3309
+ response = await fetch(url2, { ...init, signal, redirect: "error" });
3310
+ return await read(response, signal);
3311
+ } finally {
3312
+ clearTimeout(timer);
3313
+ if (response?.body && !response.body.locked) void response.body.cancel().catch(() => {
3314
+ });
3315
+ }
3316
+ }
3317
+ async function readJson(response, signal, label) {
3318
+ const reader = response.body?.getReader();
3319
+ if (!reader) throw new Error(`${label} returned invalid JSON`);
3320
+ const decoder = new TextDecoder();
3321
+ let text = "";
3322
+ let bytes = 0;
3323
+ const cancel = () => {
3324
+ void reader.cancel().catch(() => {
3325
+ });
3326
+ };
3327
+ signal.addEventListener("abort", cancel, { once: true });
3328
+ try {
3329
+ while (true) {
3330
+ signal.throwIfAborted();
3331
+ const { done, value } = await reader.read();
3332
+ signal.throwIfAborted();
3333
+ if (done) break;
3334
+ bytes += value.byteLength;
3335
+ if (bytes > OAUTH_MAX_RESPONSE_BYTES) throw new Error(`${label} exceeded the OAuth response size limit`);
3336
+ text += decoder.decode(value, { stream: true });
3337
+ }
3338
+ text += decoder.decode();
3339
+ } finally {
3340
+ signal.removeEventListener("abort", cancel);
3341
+ cancel();
3342
+ reader.releaseLock();
3343
+ }
3344
+ try {
3345
+ return JSON.parse(text);
3346
+ } catch {
3347
+ throw new Error(`${label} returned invalid JSON`);
3348
+ }
3301
3349
  }
3302
3350
  async function fetchJson2(url2, init) {
3303
- const res = await fetchResponse(url2, init);
3304
- if (!res.ok) {
3305
- throw new Error(`${init?.method ?? "GET"} ${url2} failed: ${res.status}`);
3351
+ return withResponse(url2, init, async (response, signal) => {
3352
+ const label = `${init.method ?? "GET"} ${url2}`;
3353
+ if (!response.ok) throw new Error(`${label} failed: ${response.status}`);
3354
+ return readJson(response, signal, label);
3355
+ });
3356
+ }
3357
+ async function abortable(work, signal) {
3358
+ if (!signal) return work;
3359
+ let onAbort;
3360
+ const aborted = new Promise((_2, reject) => {
3361
+ onAbort = () => reject(signal.reason);
3362
+ signal.addEventListener("abort", onAbort, { once: true });
3363
+ if (signal.aborted) onAbort();
3364
+ });
3365
+ try {
3366
+ return await Promise.race([work, aborted]);
3367
+ } finally {
3368
+ if (onAbort) signal.removeEventListener("abort", onAbort);
3306
3369
  }
3307
- return res.json();
3308
3370
  }
3309
3371
  function authorizationServerMetadata(value, issuer, requireExactIssuer) {
3310
3372
  if (!value || typeof value !== "object") throw new Error(`Authorization server metadata for ${issuer} is invalid`);
@@ -3330,21 +3392,28 @@ function authorizationServerMetadata(value, issuer, requireExactIssuer) {
3330
3392
  if (metadata.registration_endpoint) validatedHttpsUrl(metadata.registration_endpoint, "Registration endpoint");
3331
3393
  return metadata;
3332
3394
  }
3333
- async function jsonMetadata(response, url2) {
3395
+ async function jsonMetadata(response, url2, signal) {
3334
3396
  if (response.status !== 200) throw new Error(`GET ${url2} failed: ${response.status}`);
3335
3397
  const contentType = response.headers.get("content-type")?.split(";", 1)[0].trim().toLowerCase();
3336
3398
  if (contentType !== "application/json") throw new Error(`GET ${url2} did not return application/json`);
3337
- return response.json();
3399
+ return readJson(response, signal, `GET ${url2}`);
3338
3400
  }
3339
- async function discoverAuthorizationServer(issuer, requireExactIssuer) {
3401
+ async function discoverAuthorizationServer(issuer, requireExactIssuer, signal) {
3340
3402
  const candidates = authorizationServerMetadataUrls(issuer);
3341
3403
  let lastError;
3342
3404
  for (const candidate of candidates) {
3343
3405
  try {
3344
- const response = await fetchResponse(candidate);
3345
- if (response.status === 404) continue;
3346
- return authorizationServerMetadata(await jsonMetadata(response, candidate), issuer, requireExactIssuer);
3406
+ const metadata = await withResponse(candidate, { signal }, async (response, requestSignal) => {
3407
+ if (response.status === 404) return void 0;
3408
+ return authorizationServerMetadata(
3409
+ await jsonMetadata(response, candidate, requestSignal),
3410
+ issuer,
3411
+ requireExactIssuer
3412
+ );
3413
+ });
3414
+ if (metadata) return metadata;
3347
3415
  } catch (error) {
3416
+ signal?.throwIfAborted();
3348
3417
  lastError = error;
3349
3418
  }
3350
3419
  }
@@ -3370,6 +3439,9 @@ function resourceMetadata(value, resource) {
3370
3439
  throw new Error("Protected-resource metadata has an invalid authorization server");
3371
3440
  validatedHttpsUrl(issuer, "Authorization server issuer");
3372
3441
  }
3442
+ if (metadata.scopes_supported !== void 0 && (!Array.isArray(metadata.scopes_supported) || metadata.scopes_supported.some((scope) => typeof scope !== "string"))) {
3443
+ throw new Error("Protected-resource metadata has invalid scopes_supported");
3444
+ }
3373
3445
  return metadata;
3374
3446
  }
3375
3447
  function resourceMetadataUrl(resource) {
@@ -3381,34 +3453,39 @@ function headerResourceMetadata(value) {
3381
3453
  if (!match2) return void 0;
3382
3454
  return match2[1].replace(/\\(.)/g, "$1");
3383
3455
  }
3384
- async function tryProtectedResourceMetadata(url2) {
3456
+ async function tryProtectedResourceMetadata(url2, signal) {
3385
3457
  const resource = validatedHttpsUrl(url2, "MCP endpoint");
3386
3458
  let headerUrl;
3387
3459
  try {
3388
- const response2 = await fetchResponse(resource.toString());
3389
- headerUrl = headerResourceMetadata(response2.headers.get("www-authenticate"));
3390
- await response2.body?.cancel();
3460
+ headerUrl = await withResponse(
3461
+ resource.toString(),
3462
+ { signal },
3463
+ async (response) => headerResourceMetadata(response.headers.get("www-authenticate"))
3464
+ );
3391
3465
  } catch {
3466
+ signal?.throwIfAborted();
3392
3467
  }
3393
3468
  const candidate = headerUrl ? validatedHttpsUrl(headerUrl, "resource_metadata").toString() : resourceMetadataUrl(resource);
3394
- const response = await fetchResponse(candidate);
3395
- if (response.status === 404 && !headerUrl) return void 0;
3396
- return resourceMetadata(await jsonMetadata(response, candidate), canonicalResource(resource));
3469
+ return withResponse(candidate, { signal }, async (response, requestSignal) => {
3470
+ if (response.status === 404 && !headerUrl) return void 0;
3471
+ return resourceMetadata(await jsonMetadata(response, candidate, requestSignal), canonicalResource(resource));
3472
+ });
3397
3473
  }
3398
- async function discover(url2) {
3399
- const protectedResource = await tryProtectedResourceMetadata(url2);
3474
+ async function discover(url2, signal) {
3475
+ const protectedResource = await tryProtectedResourceMetadata(url2, signal);
3400
3476
  if (protectedResource) {
3401
3477
  const issuer2 = protectedResource.authorization_servers[0];
3402
3478
  return {
3403
- metadata: await discoverAuthorizationServer(issuer2, true),
3479
+ metadata: await discoverAuthorizationServer(issuer2, true, signal),
3480
+ scopes: protectedResource.scopes_supported,
3404
3481
  resource: protectedResource.resource,
3405
3482
  issuer: issuer2
3406
3483
  };
3407
3484
  }
3408
3485
  const issuer = validatedHttpsUrl(url2, "MCP endpoint").origin;
3409
- return { metadata: await discoverAuthorizationServer(issuer, false) };
3486
+ return { metadata: await discoverAuthorizationServer(issuer, false, signal) };
3410
3487
  }
3411
- async function registerClient(registrationEndpoint, label) {
3488
+ async function registerClient(registrationEndpoint, label, signal) {
3412
3489
  validatedHttpsUrl(registrationEndpoint, "Registration endpoint");
3413
3490
  const body = {
3414
3491
  client_name: label,
@@ -3420,7 +3497,8 @@ async function registerClient(registrationEndpoint, label) {
3420
3497
  const data = await fetchJson2(registrationEndpoint, {
3421
3498
  method: "POST",
3422
3499
  headers: { "Content-Type": "application/json" },
3423
- body: JSON.stringify(body)
3500
+ body: JSON.stringify(body),
3501
+ signal
3424
3502
  });
3425
3503
  if (typeof data.client_id !== "string" || !data.client_id) {
3426
3504
  throw new Error(`Dynamic client registration at ${registrationEndpoint} returned no client_id`);
@@ -3428,7 +3506,6 @@ async function registerClient(registrationEndpoint, label) {
3428
3506
  return data.client_id;
3429
3507
  }
3430
3508
  async function startCallbackServer2(label) {
3431
- const { createServer: createServer3 } = await import("node:http");
3432
3509
  let settle;
3433
3510
  const waitPromise = new Promise((resolve26) => {
3434
3511
  let settled = false;
@@ -3465,7 +3542,7 @@ async function startCallbackServer2(label) {
3465
3542
  };
3466
3543
  let lastError;
3467
3544
  for (const port of CALLBACK_PORTS) {
3468
- const server = createServer3(handler);
3545
+ const server = createServer(handler);
3469
3546
  let bindErr;
3470
3547
  server.on("error", (err) => bindErr?.(err));
3471
3548
  try {
@@ -3516,23 +3593,22 @@ function parseRedirectInput(input, expectedState) {
3516
3593
  }
3517
3594
  return { code, state: state ?? expectedState };
3518
3595
  }
3519
- async function exchangeToken(tokenEndpoint, params) {
3596
+ async function exchangeToken(tokenEndpoint, params, signal) {
3520
3597
  validatedHttpsUrl(tokenEndpoint, "Token endpoint");
3521
- const res = await fetchResponse(tokenEndpoint, {
3522
- method: "POST",
3523
- headers: { "Content-Type": "application/x-www-form-urlencoded" },
3524
- body: new URLSearchParams(params).toString()
3525
- });
3526
- const text = await res.text();
3527
- if (!res.ok) {
3528
- throw new Error(`Token request to ${tokenEndpoint} failed: ${res.status}`);
3529
- }
3530
- let token;
3531
- try {
3532
- token = JSON.parse(text);
3533
- } catch {
3534
- throw new Error(`Token request to ${tokenEndpoint} returned invalid JSON`);
3535
- }
3598
+ const token = await withResponse(
3599
+ tokenEndpoint,
3600
+ {
3601
+ method: "POST",
3602
+ headers: { "Content-Type": "application/x-www-form-urlencoded" },
3603
+ body: new URLSearchParams(params).toString(),
3604
+ signal
3605
+ },
3606
+ async (response, requestSignal) => {
3607
+ const label = `Token request to ${tokenEndpoint}`;
3608
+ if (!response.ok) throw new Error(`${label} failed: ${response.status}`);
3609
+ return readJson(response, requestSignal, label);
3610
+ }
3611
+ );
3536
3612
  if (!token || typeof token !== "object" || typeof token.access_token !== "string") {
3537
3613
  throw new Error(`Token request to ${tokenEndpoint} returned no access_token`);
3538
3614
  }
@@ -3562,7 +3638,7 @@ function toCredentials(token, tokenEndpoint, clientId, endpoint, resource, issue
3562
3638
  function createMcpOAuthProvider(config) {
3563
3639
  const label = config.label ?? config.server;
3564
3640
  async function login(callbacks) {
3565
- const discovery = await discover(config.url);
3641
+ const discovery = await discover(config.url, callbacks.signal);
3566
3642
  const { metadata: meta } = discovery;
3567
3643
  callbacks.onProgress?.(`Discovered ${discovery.issuer ?? meta.issuer}`);
3568
3644
  let clientId = config.clientId;
@@ -3573,13 +3649,15 @@ function createMcpOAuthProvider(config) {
3573
3649
  );
3574
3650
  }
3575
3651
  callbacks.onProgress?.("Registering OAuth client\u2026");
3576
- clientId = await registerClient(meta.registration_endpoint, `Prime Agent (${label})`);
3652
+ clientId = await registerClient(meta.registration_endpoint, `Base Context (${label})`, callbacks.signal);
3577
3653
  }
3578
3654
  const { verifier, challenge } = await generatePKCE();
3579
3655
  const state = randomState();
3580
- const scope = config.scopes ?? meta.scopes_supported?.join(" ");
3656
+ const scope = config.scopes ?? discovery.scopes?.join(" ");
3657
+ callbacks.signal?.throwIfAborted();
3581
3658
  const cb = await startCallbackServer2(label);
3582
3659
  try {
3660
+ callbacks.signal?.throwIfAborted();
3583
3661
  const authParams = new URLSearchParams({
3584
3662
  client_id: clientId,
3585
3663
  response_type: "code",
@@ -3596,6 +3674,7 @@ function createMcpOAuthProvider(config) {
3596
3674
  url: authorizationUrl.toString(),
3597
3675
  instructions: "Complete login in your browser. If the browser is on another machine, paste the final redirect URL here."
3598
3676
  });
3677
+ callbacks.signal?.throwIfAborted();
3599
3678
  let result;
3600
3679
  let manualCancelled = false;
3601
3680
  let manualError;
@@ -3614,16 +3693,19 @@ function createMcpOAuthProvider(config) {
3614
3693
  cb.cancel();
3615
3694
  return null;
3616
3695
  });
3617
- const fromCallback = await cb.waitForCode();
3618
- result = fromCallback ?? await manual;
3696
+ const fromCallback = await abortable(cb.waitForCode(), callbacks.signal);
3697
+ result = fromCallback ?? await abortable(manual, callbacks.signal);
3619
3698
  if (!result && manualError) throw manualError;
3620
3699
  } else {
3621
- result = await cb.waitForCode();
3700
+ result = await abortable(cb.waitForCode(), callbacks.signal);
3622
3701
  if (!result) {
3623
- const input = await callbacks.onPrompt({
3624
- message: "Paste the authorization code or full redirect URL:",
3625
- placeholder: cb.redirectUri
3626
- });
3702
+ const input = await abortable(
3703
+ callbacks.onPrompt({
3704
+ message: "Paste the authorization code or full redirect URL:",
3705
+ placeholder: cb.redirectUri
3706
+ }),
3707
+ callbacks.signal
3708
+ );
3627
3709
  result = parseRedirectInput(input, state);
3628
3710
  }
3629
3711
  }
@@ -3634,16 +3716,21 @@ function createMcpOAuthProvider(config) {
3634
3716
  throw new Error("OAuth state mismatch");
3635
3717
  }
3636
3718
  callbacks.onProgress?.("Exchanging authorization code for tokens\u2026");
3637
- const token = await exchangeToken(meta.token_endpoint, {
3638
- grant_type: "authorization_code",
3639
- code: result.code,
3640
- redirect_uri: cb.redirectUri,
3641
- client_id: clientId,
3642
- code_verifier: verifier,
3643
- ...discovery.resource ? { resource: discovery.resource } : {}
3644
- });
3719
+ const token = await exchangeToken(
3720
+ meta.token_endpoint,
3721
+ {
3722
+ grant_type: "authorization_code",
3723
+ code: result.code,
3724
+ redirect_uri: cb.redirectUri,
3725
+ client_id: clientId,
3726
+ code_verifier: verifier,
3727
+ ...discovery.resource ? { resource: discovery.resource } : {}
3728
+ },
3729
+ callbacks.signal
3730
+ );
3645
3731
  return toCredentials(token, meta.token_endpoint, clientId, config.url, discovery.resource, discovery.issuer);
3646
3732
  } finally {
3733
+ cb.cancel();
3647
3734
  cb.server.close();
3648
3735
  }
3649
3736
  }
@@ -4638,7 +4725,7 @@ ${loadError}`));
4638
4725
  import { resolve as resolve11 } from "node:path";
4639
4726
 
4640
4727
  // dist/core/auth-storage.js
4641
- import { createHash } from "node:crypto";
4728
+ import { createHash, randomUUID } from "node:crypto";
4642
4729
 
4643
4730
  // ../ai/src/oauth.ts
4644
4731
  var oauth_exports = {};
@@ -5143,11 +5230,18 @@ var AuthStorage = class _AuthStorage {
5143
5230
  this.staleAuthSources.set(provider, next);
5144
5231
  }
5145
5232
  }
5233
+ parseStorageState(content) {
5234
+ const { __base_context_login_attempts: loginAttempts = {}, ...credentials } = JSON.parse(content || "{}");
5235
+ return { credentials, loginAttempts };
5236
+ }
5237
+ serializeStorageState({ credentials, loginAttempts }) {
5238
+ return JSON.stringify({
5239
+ ...credentials,
5240
+ ...Object.keys(loginAttempts).length > 0 ? { __base_context_login_attempts: loginAttempts } : {}
5241
+ }, null, 2);
5242
+ }
5146
5243
  parseStorageData(content) {
5147
- if (!content) {
5148
- return {};
5149
- }
5150
- return JSON.parse(content);
5244
+ return this.parseStorageState(content).credentials;
5151
5245
  }
5152
5246
  /**
5153
5247
  * Reload credentials from storage.
@@ -5172,14 +5266,14 @@ var AuthStorage = class _AuthStorage {
5172
5266
  }
5173
5267
  try {
5174
5268
  this.storage.withLock((current) => {
5175
- const currentData = this.parseStorageData(current);
5176
- const merged = { ...currentData };
5269
+ const state = this.parseStorageState(current);
5270
+ delete state.loginAttempts[provider];
5177
5271
  if (credential) {
5178
- merged[provider] = credential;
5272
+ state.credentials[provider] = credential;
5179
5273
  } else {
5180
- delete merged[provider];
5274
+ delete state.credentials[provider];
5181
5275
  }
5182
- return { result: void 0, next: JSON.stringify(merged, null, 2) };
5276
+ return { result: void 0, next: this.serializeStorageState(state) };
5183
5277
  });
5184
5278
  } catch (error) {
5185
5279
  this.recordError(error);
@@ -5218,12 +5312,12 @@ var AuthStorage = class _AuthStorage {
5218
5312
  removeVerified(provider) {
5219
5313
  this.assertWritableStorage();
5220
5314
  this.storage.withLock((current) => {
5221
- const currentData = this.parseStorageData(current);
5222
- if (!(provider in currentData))
5315
+ const state = this.parseStorageState(current);
5316
+ if (!(provider in state.credentials) && !(provider in state.loginAttempts))
5223
5317
  return { result: void 0 };
5224
- const merged = { ...currentData };
5225
- delete merged[provider];
5226
- return { result: void 0, next: JSON.stringify(merged, null, 2) };
5318
+ delete state.credentials[provider];
5319
+ delete state.loginAttempts[provider];
5320
+ return { result: void 0, next: this.serializeStorageState(state) };
5227
5321
  });
5228
5322
  delete this.data[provider];
5229
5323
  this.clearStaleAuthSource(provider, "stored");
@@ -5277,8 +5371,55 @@ var AuthStorage = class _AuthStorage {
5277
5371
  if (!provider) {
5278
5372
  throw new Error(`Unknown OAuth provider: ${providerId}`);
5279
5373
  }
5280
- const credentials = await provider.login(callbacks);
5281
- this.set(providerId, { type: "oauth", ...credentials });
5374
+ await this.setFromLogin(providerId, async () => ({ type: "oauth", ...await provider.login(callbacks) }), callbacks.signal);
5375
+ }
5376
+ /** Collect credentials without holding the lock, then commit only for the current login. */
5377
+ async setFromLogin(providerId, collectCredential, signal) {
5378
+ this.assertWritableStorage();
5379
+ if (signal?.aborted)
5380
+ throw new Error("Login cancelled");
5381
+ const attemptId = randomUUID();
5382
+ this.storage.withLock((current) => {
5383
+ const state = this.parseStorageState(current);
5384
+ state.loginAttempts[providerId] = attemptId;
5385
+ return { result: void 0, next: this.serializeStorageState(state) };
5386
+ });
5387
+ const releaseAttempt = () => {
5388
+ try {
5389
+ this.storage.withLock((current) => {
5390
+ const state = this.parseStorageState(current);
5391
+ if (state.loginAttempts[providerId] !== attemptId)
5392
+ return { result: void 0 };
5393
+ delete state.loginAttempts[providerId];
5394
+ return { result: void 0, next: this.serializeStorageState(state) };
5395
+ });
5396
+ } catch (error) {
5397
+ this.recordError(error);
5398
+ }
5399
+ };
5400
+ signal?.addEventListener("abort", releaseAttempt, { once: true });
5401
+ try {
5402
+ const credential = await collectCredential();
5403
+ if (signal?.aborted)
5404
+ throw new Error("Login cancelled");
5405
+ const data = this.storage.withLock((current) => {
5406
+ const state = this.parseStorageState(current);
5407
+ if (state.loginAttempts[providerId] !== attemptId) {
5408
+ throw new Error("Login cancelled: credentials changed or login was superseded");
5409
+ }
5410
+ delete state.loginAttempts[providerId];
5411
+ state.credentials[providerId] = credential;
5412
+ return { result: state.credentials, next: this.serializeStorageState(state) };
5413
+ });
5414
+ this.data = data;
5415
+ this.loadError = null;
5416
+ this.clearStaleAuthSource(providerId, "stored");
5417
+ } catch (error) {
5418
+ releaseAttempt();
5419
+ throw error;
5420
+ } finally {
5421
+ signal?.removeEventListener("abort", releaseAttempt);
5422
+ }
5282
5423
  }
5283
5424
  /**
5284
5425
  * Logout from a provider.
@@ -5299,7 +5440,8 @@ var AuthStorage = class _AuthStorage {
5299
5440
  return null;
5300
5441
  }
5301
5442
  const result = await this.storage.withLockAsync(async (current) => {
5302
- const currentData = this.parseStorageData(current);
5443
+ const state = this.parseStorageState(current);
5444
+ const currentData = state.credentials;
5303
5445
  this.data = currentData;
5304
5446
  this.loadError = null;
5305
5447
  const cred = currentData[providerId];
@@ -5325,7 +5467,7 @@ var AuthStorage = class _AuthStorage {
5325
5467
  };
5326
5468
  this.data = merged;
5327
5469
  this.loadError = null;
5328
- return { result: refreshed, next: JSON.stringify(merged, null, 2) };
5470
+ return { result: refreshed, next: this.serializeStorageState({ ...state, credentials: merged }) };
5329
5471
  });
5330
5472
  return result;
5331
5473
  }
@@ -7080,7 +7222,7 @@ async function selectConfig(options) {
7080
7222
  // dist/cli/daemon-update-restart.js
7081
7223
  var import_proper_lockfile3 = __toESM(require_proper_lockfile(), 1);
7082
7224
  import { spawn } from "node:child_process";
7083
- import { createHash as createHash2, randomUUID } from "node:crypto";
7225
+ import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
7084
7226
  import { mkdirSync as mkdirSync3, readFileSync as readFileSync3, renameSync as renameSync2, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
7085
7227
  import { resolve as resolve2 } from "node:path";
7086
7228
  var DAEMON_UPDATE_RESTART_COORDINATOR_FLAG = "--internal-update-restart-coordinator";
@@ -7141,7 +7283,7 @@ function socketKey(socketPath) {
7141
7283
  return createHash2("sha256").update(normalizeSocketPath(socketPath)).digest("hex");
7142
7284
  }
7143
7285
  function writeJsonAtomically(path6, value) {
7144
- const tempPath = `${path6}.${process.pid}.${randomUUID()}.tmp`;
7286
+ const tempPath = `${path6}.${process.pid}.${randomUUID2()}.tmp`;
7145
7287
  try {
7146
7288
  writeFileSync3(tempPath, `${JSON.stringify(value, null, 2)}
7147
7289
  `, { mode: 384 });
@@ -7375,7 +7517,7 @@ var DaemonUpdateRestartCoordinatorAlreadyRunningError = class extends Error {
7375
7517
  async function acquireDaemonUpdateRestartCoordinator(options) {
7376
7518
  const registryDir = options.registryDir ?? defaultCoordinatorRegistryDir();
7377
7519
  const path6 = coordinatorRecordPath(registryDir, options.socketPath);
7378
- const token = randomUUID();
7520
+ const token = randomUUID2();
7379
7521
  const processStartId = getProcessStartId(process.pid);
7380
7522
  const record3 = {
7381
7523
  version: 1,
@@ -7461,7 +7603,7 @@ function selectedUpdateInstallation() {
7461
7603
  return ownedVersion(owned.root, selected.active);
7462
7604
  }
7463
7605
  async function launchDaemonUpdateRestartCoordinator(options) {
7464
- const requestId = randomUUID();
7606
+ const requestId = randomUUID2();
7465
7607
  const agentDir = resolve2(options.agentDir);
7466
7608
  const socketPath = resolveDaemonUpdateRestartSocketPath(options.socketPath);
7467
7609
  const statusPath = createStatusPath(agentDir, socketPath, requestId);
@@ -14345,7 +14487,7 @@ var DefaultPackageManager = class {
14345
14487
 
14346
14488
  // dist/modes/daemon/daemon-supervisor-ownership.js
14347
14489
  var import_proper_lockfile4 = __toESM(require_proper_lockfile(), 1);
14348
- import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
14490
+ import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
14349
14491
  import { existsSync as existsSync4, mkdirSync as mkdirSync5, readdirSync as readdirSync2, readFileSync as readFileSync5, realpathSync, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync5 } from "node:fs";
14350
14492
  import { basename as basename3, dirname as dirname6, join as join7, resolve as resolve4 } from "node:path";
14351
14493
  var DAEMON_SUPERVISOR_REGISTRY_DIR_ENV = "BASE_CONTEXT_INTERNAL_DAEMON_SUPERVISOR_REGISTRY_DIR";
@@ -14413,17 +14555,15 @@ var RenewableRegistryRecord = class {
14413
14555
  await this.refreshPromise;
14414
14556
  }
14415
14557
  async performRenew() {
14416
- try {
14417
- await withDaemonSupervisorRegistryGuard(this.registryDir, () => {
14418
- if (this.stopped || this.lost) {
14419
- throw this.createLostError();
14420
- }
14421
- this.renewUnderGuard();
14422
- });
14423
- } catch (error) {
14558
+ const held = await withDaemonSupervisorRegistryGuard(this.registryDir, () => {
14559
+ if (this.stopped || this.lost)
14560
+ return false;
14561
+ return this.renewUnderGuard();
14562
+ });
14563
+ if (!held) {
14424
14564
  this.lost = true;
14425
14565
  clearInterval(this.refreshTimer);
14426
- throw error;
14566
+ throw this.createLostError();
14427
14567
  }
14428
14568
  }
14429
14569
  async stop() {
@@ -14490,7 +14630,7 @@ var DaemonSupervisorOwnership = class {
14490
14630
  if (!current || current.token !== this.record.token) {
14491
14631
  return;
14492
14632
  }
14493
- releasedDirectory = `${this.ownerDirectory}.released-${randomUUID2()}`;
14633
+ releasedDirectory = `${this.ownerDirectory}.released-${randomUUID3()}`;
14494
14634
  renameSync3(this.ownerDirectory, releasedDirectory);
14495
14635
  });
14496
14636
  this.released = true;
@@ -14520,13 +14660,14 @@ var DaemonShutdownAdmission = class {
14520
14660
  renewUnderGuard() {
14521
14661
  const path6 = shutdownAdmissionPath(this.registryDir);
14522
14662
  const current = readShutdownAdmission(path6);
14523
- if (!current || current.token !== this.record.token || current.pid !== this.record.pid || current.processStartId !== this.record.processStartId || Date.parse(current.expiresAt) <= Date.now() || !matchesExactProcessIdentity(this.record)) {
14524
- throw new DaemonShutdownAdmissionError("Daemon shutdown admission was lost");
14663
+ if (!current || current.token !== this.record.token || current.pid !== this.record.pid || current.processStartId !== this.record.processStartId || !matchesExactProcessIdentity(this.record)) {
14664
+ return false;
14525
14665
  }
14526
14666
  const now = Date.now();
14527
14667
  this.record.updatedAt = new Date(now).toISOString();
14528
14668
  this.record.expiresAt = new Date(now + SHUTDOWN_ADMISSION_LEASE_MS).toISOString();
14529
14669
  writeJsonAtomically2(path6, this.record);
14670
+ return true;
14530
14671
  }
14531
14672
  async release() {
14532
14673
  if (this.released) {
@@ -14636,7 +14777,7 @@ async function mutateDaemonSupervisorOwner(generation, expectedToken, mutation,
14636
14777
  async function acquireDaemonSupervisorOwnership(options) {
14637
14778
  const registryDir = options.registryDir ?? defaultDaemonSupervisorRegistryDir();
14638
14779
  mkdirSync5(registryDir, { recursive: true, mode: 448 });
14639
- const token = randomUUID2();
14780
+ const token = randomUUID3();
14640
14781
  const processStartId = getProcessStartId(process.pid);
14641
14782
  const now = (/* @__PURE__ */ new Date()).toISOString();
14642
14783
  const record3 = {
@@ -14677,7 +14818,7 @@ async function acquireDaemonSupervisorOwnership(options) {
14677
14818
  if (isProcessIdentityAlive2(owner)) {
14678
14819
  throw new DaemonSupervisorAlreadyRunningError(owner);
14679
14820
  }
14680
- const staleDirectory = `${directory}.stale-${randomUUID2()}`;
14821
+ const staleDirectory = `${directory}.stale-${randomUUID3()}`;
14681
14822
  renameSync3(directory, staleDirectory);
14682
14823
  staleDirectories.push(staleDirectory);
14683
14824
  }
@@ -14740,7 +14881,7 @@ async function acquireDaemonShutdownAdmission() {
14740
14881
  const now = Date.now();
14741
14882
  acquired = {
14742
14883
  version: OWNER_VERSION,
14743
- token: randomUUID2(),
14884
+ token: randomUUID3(),
14744
14885
  pid: process.pid,
14745
14886
  ...processStartId ? { processStartId } : {},
14746
14887
  createdAt: new Date(now).toISOString(),
@@ -14757,7 +14898,7 @@ async function acquireDaemonShutdownAdmission() {
14757
14898
  }
14758
14899
  async function isDaemonShutdownAdmissionActive() {
14759
14900
  const registryDir = defaultDaemonSupervisorRegistryDir();
14760
- return withDaemonSupervisorRegistryGuard(registryDir, () => readActiveShutdownAdmission(registryDir) !== void 0);
14901
+ return withDaemonSupervisorRegistryGuard(registryDir, () => shutdownAdmissionIsActive(readShutdownAdmission(shutdownAdmissionPath(registryDir))));
14761
14902
  }
14762
14903
  async function persistDaemonStartupFenceFromOwner(socketPath, hello, registryDir, legacyRegistryDir = void 0) {
14763
14904
  registryDir ??= defaultDaemonSupervisorRegistryDir();
@@ -14795,7 +14936,7 @@ async function persistDaemonStartupFenceFromOwner(socketPath, hello, registryDir
14795
14936
  }
14796
14937
  const record3 = {
14797
14938
  version: OWNER_VERSION,
14798
- token: randomUUID2(),
14939
+ token: randomUUID3(),
14799
14940
  ownerToken: owner.token,
14800
14941
  pid: owner.pid,
14801
14942
  processStartId: owner.processStartId,
@@ -14923,7 +15064,7 @@ function readOwnerRecordForScope(directory, isRelevant) {
14923
15064
  const scope = readOwnerScope(directory);
14924
15065
  const entries2 = !scope ? readdirSync2(directory) : [];
14925
15066
  if (!scope && !entries2.includes("owner.json") && !entries2.includes("scope.json")) {
14926
- const abandonedDirectory = `${directory}.abandoned-${randomUUID2()}`;
15067
+ const abandonedDirectory = `${directory}.abandoned-${randomUUID3()}`;
14927
15068
  renameSync3(directory, abandonedDirectory);
14928
15069
  rmSync3(abandonedDirectory, { recursive: true, force: true });
14929
15070
  return void 0;
@@ -15000,13 +15141,16 @@ function readStartupFence(path6) {
15000
15141
  throw error;
15001
15142
  }
15002
15143
  }
15144
+ function shutdownAdmissionIsActive(admission) {
15145
+ return admission !== void 0 && Date.parse(admission.expiresAt) > Date.now() && isProcessIdentityAlive2(admission);
15146
+ }
15003
15147
  function readActiveShutdownAdmission(registryDir) {
15004
15148
  const path6 = shutdownAdmissionPath(registryDir);
15005
15149
  const admission = readShutdownAdmission(path6);
15006
15150
  if (!admission) {
15007
15151
  return void 0;
15008
15152
  }
15009
- if (Date.parse(admission.expiresAt) > Date.now() && isProcessIdentityAlive2(admission)) {
15153
+ if (shutdownAdmissionIsActive(admission)) {
15010
15154
  return admission;
15011
15155
  }
15012
15156
  rmSync3(path6, { force: true });
@@ -15031,7 +15175,7 @@ function readShutdownAdmission(path6) {
15031
15175
  }
15032
15176
  }
15033
15177
  function writeJsonAtomically2(path6, value) {
15034
- const tempPath = `${path6}.${process.pid}.${randomUUID2()}.tmp`;
15178
+ const tempPath = `${path6}.${process.pid}.${randomUUID3()}.tmp`;
15035
15179
  try {
15036
15180
  writeFileSync5(tempPath, `${JSON.stringify(value, null, 2)}
15037
15181
  `, { mode: 384 });
@@ -15053,7 +15197,7 @@ function delay2(ms2) {
15053
15197
  }
15054
15198
 
15055
15199
  // dist/owned-install.js
15056
- import { randomUUID as randomUUID3 } from "node:crypto";
15200
+ import { randomUUID as randomUUID4 } from "node:crypto";
15057
15201
  import { existsSync as existsSync5, mkdirSync as mkdirSync6, readdirSync as readdirSync3, readFileSync as readFileSync6, renameSync as renameSync4, statSync as statSync3, writeFileSync as writeFileSync6 } from "node:fs";
15058
15202
  import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
15059
15203
  import { dirname as dirname7, join as join8, resolve as resolve5 } from "node:path";
@@ -15214,7 +15358,7 @@ async function installOwnedRelease(options) {
15214
15358
  });
15215
15359
  assertSelection(root, expected);
15216
15360
  initializeRoot(root);
15217
- const installation = ownedVersion(root, `${version}-${randomUUID3()}`);
15361
+ const installation = ownedVersion(root, `${version}-${randomUUID4()}`);
15218
15362
  const directory = join8(root, "versions", installation.version);
15219
15363
  mkdirSync6(directory, { recursive: true, mode: 448 });
15220
15364
  writeFileSync6(join8(directory, "package.json"), `${JSON.stringify({
@@ -17979,6 +18123,7 @@ async function runShutdownAllConverging(json, force, assertAdmission) {
17979
18123
  }
17980
18124
  async function stopHiddenSupervisors(stopped, failed, handledPids, reportedFailures, assertAdmission) {
17981
18125
  while (true) {
18126
+ await assertAdmission();
17982
18127
  const listeners = scanListeningDaemons().filter((listener) => !isWorkerSocketPath(listener.socketPath));
17983
18128
  const bySocket = /* @__PURE__ */ new Map();
17984
18129
  for (const listener of listeners) {
@@ -20989,8 +21134,8 @@ var McpManager = class {
20989
21134
  const { bearerTokenEnvVar } = integration.config;
20990
21135
  if (!integration.usesOAuth && !bearerTokenEnvVar)
20991
21136
  return true;
20992
- if (bearerTokenEnvVar && process.env[bearerTokenEnvVar]?.trim()) {
20993
- return true;
21137
+ if (bearerTokenEnvVar) {
21138
+ return Boolean(process.env[bearerTokenEnvVar]?.trim());
20994
21139
  }
20995
21140
  const providerId = this.providerId(integration.server);
20996
21141
  const cred = this.authStorage.get(providerId);
@@ -24557,7 +24702,7 @@ function runMigrations(cwd) {
24557
24702
 
24558
24703
  // dist/modes/daemon/daemon-catalog-process.js
24559
24704
  import { spawn as spawn6 } from "node:child_process";
24560
- import { randomUUID as randomUUID4 } from "node:crypto";
24705
+ import { randomUUID as randomUUID5 } from "node:crypto";
24561
24706
  import { existsSync as existsSync16 } from "node:fs";
24562
24707
  import { createRequire as createRequire2 } from "node:module";
24563
24708
  import { join as join19, sep as sep7 } from "node:path";
@@ -27434,7 +27579,7 @@ var DaemonCatalogClient = class {
27434
27579
  if (options && "page" in options)
27435
27580
  return this.request({
27436
27581
  type: "request",
27437
- id: randomUUID4(),
27582
+ id: randomUUID5(),
27438
27583
  command: "list",
27439
27584
  cwd,
27440
27585
  sessionDir,
@@ -27443,16 +27588,16 @@ var DaemonCatalogClient = class {
27443
27588
  ledgerSessionDir: options.ledgerSessionDir,
27444
27589
  scope: options.scope
27445
27590
  });
27446
- const data = await this.request({ type: "request", id: randomUUID4(), command: "list", cwd, sessionDir }, options);
27591
+ const data = await this.request({ type: "request", id: randomUUID5(), command: "list", cwd, sessionDir }, options);
27447
27592
  return data.sessions.map(deserializeSessionInfo);
27448
27593
  }
27449
27594
  async rename(sessionPath, name) {
27450
- await this.request({ type: "request", id: randomUUID4(), command: "rename", sessionPath, name });
27595
+ await this.request({ type: "request", id: randomUUID5(), command: "rename", sessionPath, name });
27451
27596
  }
27452
27597
  async resolve(selector, cwd, sessionDir) {
27453
27598
  const data = await this.request({
27454
27599
  type: "request",
27455
- id: randomUUID4(),
27600
+ id: randomUUID5(),
27456
27601
  command: "resolve",
27457
27602
  selector,
27458
27603
  cwd,
@@ -27461,12 +27606,12 @@ var DaemonCatalogClient = class {
27461
27606
  return data.sessionPath;
27462
27607
  }
27463
27608
  delete(sessionPath) {
27464
- return this.request({ type: "request", id: randomUUID4(), command: "delete", sessionPath });
27609
+ return this.request({ type: "request", id: randomUUID5(), command: "delete", sessionPath });
27465
27610
  }
27466
27611
  async archive(sessionPath, sessionId) {
27467
27612
  const data = await this.request({
27468
27613
  type: "request",
27469
- id: randomUUID4(),
27614
+ id: randomUUID5(),
27470
27615
  command: "archive",
27471
27616
  sessionPath,
27472
27617
  sessionId
@@ -27476,7 +27621,7 @@ var DaemonCatalogClient = class {
27476
27621
  async markInterrupted(sessionPath, activeSessionId, operations) {
27477
27622
  await this.request({
27478
27623
  type: "request",
27479
- id: randomUUID4(),
27624
+ id: randomUUID5(),
27480
27625
  command: "mark_interrupted",
27481
27626
  sessionPath,
27482
27627
  activeSessionId,
@@ -27490,7 +27635,7 @@ var DaemonCatalogClient = class {
27490
27635
  const child = this.child;
27491
27636
  if (!child && !this.starting)
27492
27637
  return;
27493
- await this.request({ type: "request", id: randomUUID4(), command: "shutdown" }).catch(() => void 0);
27638
+ await this.request({ type: "request", id: randomUUID5(), command: "shutdown" }).catch(() => void 0);
27494
27639
  if (child?.connected)
27495
27640
  child.disconnect();
27496
27641
  if (this.child === child)
@@ -27964,7 +28109,7 @@ function bashToolCallId(runId) {
27964
28109
  }
27965
28110
 
27966
28111
  // dist/modes/acp/acp-mode.js
27967
- import { randomUUID as randomUUID5 } from "node:crypto";
28112
+ import { randomUUID as randomUUID6 } from "node:crypto";
27968
28113
  import { realpathSync as realpathSync4, statSync as statSync6 } from "node:fs";
27969
28114
  import { resolve as resolve19 } from "node:path";
27970
28115
  import { Readable } from "node:stream";
@@ -33064,7 +33209,7 @@ async function runAcpModeWithConnection(connection, options = {}) {
33064
33209
  takeOverStdout();
33065
33210
  }
33066
33211
  const supportsMcpServers = connection.supportsAcpMcpServers?.() === true && connection.replaceAcpMcpServers !== void 0 && connection.releaseAcpMcpServers !== void 0;
33067
- const acpMcpOwnerId = randomUUID5();
33212
+ const acpMcpOwnerId = randomUUID6();
33068
33213
  let acpMcpServerNames = [];
33069
33214
  const clearAcpMcpServers = async (serverNames = acpMcpServerNames) => {
33070
33215
  if (!supportsMcpServers || !connection.releaseAcpMcpServers)
@@ -33187,7 +33332,7 @@ async function runAcpModeWithConnection(connection, options = {}) {
33187
33332
  await entry.inputPauseRelease.promise.catch(() => void 0);
33188
33333
  entry.inputPauseRelease = void 0;
33189
33334
  }
33190
- const leaseKey = entry.inputPauseKey ?? randomUUID5();
33335
+ const leaseKey = entry.inputPauseKey ?? randomUUID6();
33191
33336
  entry.inputPauseKey = leaseKey;
33192
33337
  const pause = await connection.acquireSessionInputPause(leaseKey);
33193
33338
  entry.inputPause = pause;
@@ -33279,7 +33424,7 @@ async function runAcpModeWithConnection(connection, options = {}) {
33279
33424
  if (typeof requestedCwd === "string" && requestedCwd.length > 0 && actualCwd && !sameCwd(requestedCwd, actualCwd)) {
33280
33425
  cwdMismatch = { requested: requestedCwd, actual: actualCwd };
33281
33426
  }
33282
- const sessionId = randomUUID5();
33427
+ const sessionId = randomUUID6();
33283
33428
  const producer = new AcpUpdateProducer(sessionId, ctx.client);
33284
33429
  let inputPauseRelease;
33285
33430
  if (closedInputPause) {
@@ -33605,7 +33750,7 @@ async function runAcpModeWithConnection(connection, options = {}) {
33605
33750
  }
33606
33751
 
33607
33752
  // dist/modes/agent-connection/daemon-agent-connection.js
33608
- import { randomUUID as randomUUID6 } from "node:crypto";
33753
+ import { randomUUID as randomUUID7 } from "node:crypto";
33609
33754
 
33610
33755
  // dist/modes/agents-view/roster-store.js
33611
33756
  var STALE_ROSTER_DAEMON_MESSAGE = "Daemon is stale: it does not advertise the agent_roster capability; restart the daemon";
@@ -34084,7 +34229,7 @@ var DaemonAgentConnection = class _DaemonAgentConnection {
34084
34229
  listeners = /* @__PURE__ */ new Set();
34085
34230
  unsubscribeDaemonMessages;
34086
34231
  unsubscribeDaemonClose;
34087
- clientId = `daemon-agent-connection:${randomUUID6()}`;
34232
+ clientId = `daemon-agent-connection:${randomUUID7()}`;
34088
34233
  sessionInputPauses = /* @__PURE__ */ new Map();
34089
34234
  sessionInputPauseGeneration = 0;
34090
34235
  ownedSessionPromotionTail = Promise.resolve();
@@ -34709,7 +34854,7 @@ var DaemonAgentConnection = class _DaemonAgentConnection {
34709
34854
  }, DAEMON_LONG_RUNNING_REQUEST_TIMEOUT_MS);
34710
34855
  return;
34711
34856
  }
34712
- const admissionId = `prompt-admission:${randomUUID6()}`;
34857
+ const admissionId = `prompt-admission:${randomUUID7()}`;
34713
34858
  let resolveAbort = () => {
34714
34859
  };
34715
34860
  const aborted = new Promise((resolve26) => {
@@ -36560,7 +36705,7 @@ var CustomEditor = class extends Editor {
36560
36705
  };
36561
36706
 
36562
36707
  // dist/modes/interactive/interactive-mode.js
36563
- import { randomUUID as randomUUID8 } from "node:crypto";
36708
+ import { randomUUID as randomUUID9 } from "node:crypto";
36564
36709
  import * as fs5 from "node:fs";
36565
36710
  import * as os3 from "node:os";
36566
36711
  import * as path5 from "node:path";
@@ -36943,7 +37088,7 @@ import { platform as platform2 } from "os";
36943
37088
 
36944
37089
  // dist/utils/clipboard-image.js
36945
37090
  import { spawnSync as spawnSync6 } from "child_process";
36946
- import { randomUUID as randomUUID7 } from "crypto";
37091
+ import { randomUUID as randomUUID8 } from "crypto";
36947
37092
  import { readFileSync as readFileSync17, unlinkSync as unlinkSync3 } from "fs";
36948
37093
  import { tmpdir as tmpdir3 } from "os";
36949
37094
  import { join as join22 } from "path";
@@ -37049,7 +37194,7 @@ function isWSL(env = process.env) {
37049
37194
  }
37050
37195
  }
37051
37196
  function readClipboardImageViaPowerShell() {
37052
- const tmpFile = join22(tmpdir3(), `pi-wsl-clip-${randomUUID7()}.png`);
37197
+ const tmpFile = join22(tmpdir3(), `pi-wsl-clip-${randomUUID8()}.png`);
37053
37198
  try {
37054
37199
  const winPathResult = runCommand("wslpath", ["-w", tmpFile], { timeoutMs: DEFAULT_LIST_TIMEOUT_MS });
37055
37200
  if (!winPathResult.ok) {
@@ -37632,6 +37777,9 @@ var MenuSearchInput = class {
37632
37777
  setValue(value) {
37633
37778
  this.input.setValue(value);
37634
37779
  }
37780
+ setMasked(masked) {
37781
+ this.input.setMasked(masked);
37782
+ }
37635
37783
  handleInput(data) {
37636
37784
  this.input.handleInput(data);
37637
37785
  }
@@ -37967,6 +38115,7 @@ var LoginDialogComponent = class extends Container {
37967
38115
  * Show input for manual code/URL entry (for callback server providers)
37968
38116
  */
37969
38117
  showManualInput(prompt) {
38118
+ this.input.setMasked(true);
37970
38119
  this.addSectionSpacer();
37971
38120
  this.addSectionTitle("Manual fallback");
37972
38121
  this.addMutedText(prompt);
@@ -37990,7 +38139,8 @@ var LoginDialogComponent = class extends Container {
37990
38139
  * Called by onPrompt callback - show prompt and wait for input
37991
38140
  * Note: Does NOT clear content, appends to existing (preserves URL from showAuth)
37992
38141
  */
37993
- showPrompt(message, placeholder) {
38142
+ showPrompt(message, placeholder, options) {
38143
+ this.input.setMasked(options?.masked === true);
37994
38144
  this.addSectionSpacer();
37995
38145
  this.addSectionTitle(message);
37996
38146
  if (placeholder) {
@@ -38528,7 +38678,7 @@ var ProviderAuthFlows = class {
38528
38678
  const selector = new OAuthSelectorComponent("logout", this.host.modelRegistry.authStorage, providerOptions, async (providerOption) => {
38529
38679
  close();
38530
38680
  try {
38531
- this.host.modelRegistry.authStorage.logout(providerOption.id);
38681
+ this.host.modelRegistry.authStorage.removeVerified(providerOption.id);
38532
38682
  this.host.modelRegistry.refresh();
38533
38683
  await this.host.onAuthChanged?.();
38534
38684
  const message = providerOption.authType === "oauth" ? `Logged out of ${providerOption.name}` : `Removed stored API key for ${providerOption.name}. Environment variables and models.json config are unchanged.`;
@@ -38666,11 +38816,12 @@ var ProviderAuthFlows = class {
38666
38816
  this.host.ui.requestRender();
38667
38817
  };
38668
38818
  try {
38669
- const apiKey = (await dialog.showPrompt("Enter API key:")).trim();
38670
- if (!apiKey) {
38671
- throw new Error("API key cannot be empty.");
38672
- }
38673
- this.host.modelRegistry.authStorage.set(providerId, { type: "api_key", key: apiKey });
38819
+ await this.host.modelRegistry.authStorage.setFromLogin(providerId, async () => {
38820
+ const apiKey = (await dialog.showPrompt("Enter API key:", void 0, { masked: true })).trim();
38821
+ if (!apiKey)
38822
+ throw new Error("API key cannot be empty.");
38823
+ return { type: "api_key", key: apiKey };
38824
+ }, dialog.signal);
38674
38825
  closeDialog();
38675
38826
  return await this.completeProviderAuthentication(providerId, providerName, "api_key", void 0, kind);
38676
38827
  } catch (error) {
@@ -51827,7 +51978,7 @@ ${message}`, ["Yes", "No"], opts);
51827
51978
  }
51828
51979
  const previousTurns = this.sideQuestionTurns.filter((turn) => turn.answer).map((turn) => ({ question: turn.question, answer: turn.answer }));
51829
51980
  const event = {
51830
- id: randomUUID8(),
51981
+ id: randomUUID9(),
51831
51982
  question,
51832
51983
  answer: "",
51833
51984
  status: "running"
@@ -51881,7 +52032,7 @@ ${message}`, ["Yes", "No"], opts);
51881
52032
  const truncation = truncateTail(rawOutput);
51882
52033
  const output = truncation.content.replace(/\n+$/, "");
51883
52034
  this.sideQuestionTurns.push({
51884
- id: `side-bash-${randomUUID8()}`,
52035
+ id: `side-bash-${randomUUID9()}`,
51885
52036
  question: bash.input,
51886
52037
  answer: bashOutputToText({
51887
52038
  output,
@@ -51975,7 +52126,7 @@ ${message}`, ["Yes", "No"], opts);
51975
52126
  if (this.sideQuestionComponent && slashCommand !== void 0 && (isBuiltinSlashCommandName(slashCommand.name) || this.connectionCommands.some((command) => command.name === slashCommand.name))) {
51976
52127
  this.editor.addToHistory?.(text);
51977
52128
  this.sideQuestionComponent.addTurn({
51978
- id: `side-notice-${randomUUID8()}`,
52129
+ id: `side-notice-${randomUUID9()}`,
51979
52130
  question: text,
51980
52131
  answer: "Slash commands are not available in side conversations. Press esc to return to the main thread.",
51981
52132
  status: "complete"
@@ -52214,7 +52365,7 @@ ${message}`, ["Yes", "No"], opts);
52214
52365
  this.showWarning("Wait for the current side question to finish or cancel it first.");
52215
52366
  return;
52216
52367
  }
52217
- const sideBash = this.sideQuestionComponent ? { runId: randomUUID8(), input: text, seedTranscript: !isExcluded } : void 0;
52368
+ const sideBash = this.sideQuestionComponent ? { runId: randomUUID9(), input: text, seedTranscript: !isExcluded } : void 0;
52218
52369
  if (sideBash) {
52219
52370
  this.sideQuestionBash = sideBash;
52220
52371
  } else {
@@ -52259,7 +52410,7 @@ ${message}`, ["Yes", "No"], opts);
52259
52410
  if (this.hasPastedImagesFor(text)) {
52260
52411
  this.editor.setText(text);
52261
52412
  this.sideQuestionComponent.addTurn({
52262
- id: `side-notice-${randomUUID8()}`,
52413
+ id: `side-notice-${randomUUID9()}`,
52263
52414
  question: text,
52264
52415
  answer: "Images are not supported in side conversations. Press esc to return to the main thread.",
52265
52416
  status: "complete"
@@ -55239,12 +55390,12 @@ Fell back to dark theme.`);
55239
55390
  this.showError("Usage: /mcp logout <name>");
55240
55391
  return;
55241
55392
  }
55242
- if (!isAuthed(server)) {
55243
- this.showStatus(`${server} is not connected.`);
55244
- return;
55393
+ try {
55394
+ authStorage.removeVerified(`mcp:${server}`);
55395
+ await this.reloadAfterMcpChange(`Disconnected ${server}.`);
55396
+ } catch (error) {
55397
+ this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
55245
55398
  }
55246
- authStorage.logout(`mcp:${server}`);
55247
- await this.reloadAfterMcpChange(`Disconnected ${server}.`);
55248
55399
  return;
55249
55400
  }
55250
55401
  try {
@@ -58723,10 +58874,10 @@ function padLine(line, width) {
58723
58874
 
58724
58875
  // dist/modes/daemon/daemon-mode.js
58725
58876
  import { spawn as spawn9 } from "node:child_process";
58726
- import { createHash as createHash8, randomUUID as randomUUID12, timingSafeEqual } from "node:crypto";
58877
+ import { createHash as createHash8, randomUUID as randomUUID13, timingSafeEqual } from "node:crypto";
58727
58878
  import { existsSync as existsSync24, mkdirSync as mkdirSync16, readFileSync as readFileSync23, renameSync as renameSync11, rmSync as rmSync12, writeFileSync as writeFileSync11 } from "node:fs";
58728
58879
  import { stat as stat3 } from "node:fs/promises";
58729
- import { createConnection as createConnection2, createServer } from "node:net";
58880
+ import { createConnection as createConnection2, createServer as createServer2 } from "node:net";
58730
58881
  import { basename as basename11, dirname as dirname18, isAbsolute as isAbsolute5, join as join30, resolve as resolve23 } from "node:path";
58731
58882
 
58732
58883
  // dist/core/rlm-journal-owner.js
@@ -58904,10 +59055,10 @@ var RlmJournalOwner = class _RlmJournalOwner {
58904
59055
  };
58905
59056
 
58906
59057
  // dist/modes/daemon/active-session-state.js
58907
- import { randomUUID as randomUUID9 } from "node:crypto";
59058
+ import { randomUUID as randomUUID10 } from "node:crypto";
58908
59059
  function createActiveSessionId(existingIds) {
58909
59060
  while (true) {
58910
- const activeSessionId = formatSessionDisplayId(randomUUID9());
59061
+ const activeSessionId = formatSessionDisplayId(randomUUID10());
58911
59062
  if (!existingIds?.has(activeSessionId)) {
58912
59063
  return activeSessionId;
58913
59064
  }
@@ -59204,7 +59355,7 @@ async function withClientEnv(env, fn) {
59204
59355
  }
59205
59356
 
59206
59357
  // dist/modes/daemon/daemon-extension-binding.js
59207
- import { randomUUID as randomUUID10 } from "node:crypto";
59358
+ import { randomUUID as randomUUID11 } from "node:crypto";
59208
59359
  function slimSessionEventForWire(event) {
59209
59360
  if (event.type !== "message_update") {
59210
59361
  return event;
@@ -59281,7 +59432,7 @@ function createCommandContextActions(state) {
59281
59432
  }
59282
59433
  function createExtensionUIContext(state, broadcast) {
59283
59434
  const emitUiRequest = (method, payload) => {
59284
- const id = randomUUID10();
59435
+ const id = randomUUID11();
59285
59436
  broadcast(state, {
59286
59437
  type: "extension_ui_request",
59287
59438
  activeSessionId: state.activeSessionId,
@@ -59381,7 +59532,7 @@ function hasExtensionUiClientForMethod(state, method) {
59381
59532
  }
59382
59533
 
59383
59534
  // dist/modes/daemon/daemon-rlm-capacity.js
59384
- import { randomUUID as randomUUID11 } from "node:crypto";
59535
+ import { randomUUID as randomUUID12 } from "node:crypto";
59385
59536
  var DaemonRlmCapacityClient = class {
59386
59537
  options;
59387
59538
  reservations = /* @__PURE__ */ new Map();
@@ -59405,7 +59556,7 @@ var DaemonRlmCapacityClient = class {
59405
59556
  return this.reserve(origin, true);
59406
59557
  }
59407
59558
  async reserve(origin, residentRoot) {
59408
- const reservationId = randomUUID11();
59559
+ const reservationId = randomUUID12();
59409
59560
  const record3 = { ...origin, reservationId, ...residentRoot ? { residentRoot } : {} };
59410
59561
  this.reservations.set(reservationId, record3);
59411
59562
  let released = false;
@@ -60552,7 +60703,7 @@ var AgentDaemon = class {
60552
60703
  await this.openRlmJournalOwner();
60553
60704
  try {
60554
60705
  await prepareDaemonSocketPath(this.socketPath);
60555
- this.server = createServer((socket) => this.handleConnection(socket));
60706
+ this.server = createServer2((socket) => this.handleConnection(socket));
60556
60707
  await new Promise((resolveListen, rejectListen) => {
60557
60708
  const onError = (error) => {
60558
60709
  this.server?.off("listening", onListening);
@@ -60731,7 +60882,7 @@ var AgentDaemon = class {
60731
60882
  let ownsLock = false;
60732
60883
  try {
60733
60884
  for (let attempt2 = 0; attempt2 < 3 && !ownsLock; attempt2++) {
60734
- const token = randomUUID12();
60885
+ const token = randomUUID13();
60735
60886
  const candidateDirectory = `${lockDirectory}.candidate-${process.pid}-${token}`;
60736
60887
  mkdirSync16(candidateDirectory, { mode: 448 });
60737
60888
  writeFileSync11(join30(candidateDirectory, "pid"), `${process.pid}
@@ -63793,7 +63944,7 @@ var AgentDaemon = class {
63793
63944
  return success(command.id, "acquire_session_input_pause", { pauseId: existing[0] });
63794
63945
  }
63795
63946
  const state = this.getSessionState(command.activeSessionId);
63796
- const pauseId = randomUUID12();
63947
+ const pauseId = randomUUID13();
63797
63948
  this.sessionInputPauses.set(pauseId, {
63798
63949
  activeSessionId: command.activeSessionId,
63799
63950
  owner: client,
@@ -66340,9 +66491,9 @@ async function resolveDaemonSessionPath(selector, cwd, sessionDir) {
66340
66491
 
66341
66492
  // dist/modes/daemon/daemon-supervisor.js
66342
66493
  import { spawn as spawn10 } from "node:child_process";
66343
- import { createHash as createHash9, randomBytes, randomUUID as randomUUID13 } from "node:crypto";
66494
+ import { createHash as createHash9, randomBytes, randomUUID as randomUUID14 } from "node:crypto";
66344
66495
  import { chmodSync as chmodSync3, existsSync as existsSync25, mkdirSync as mkdirSync18, readdirSync as readdirSync10, readFileSync as readFileSync25, renameSync as renameSync13, rmSync as rmSync13, writeFileSync as writeFileSync12 } from "node:fs";
66345
- import { createServer as createServer2 } from "node:net";
66496
+ import { createServer as createServer3 } from "node:net";
66346
66497
  import { basename as basename12, dirname as dirname20, join as join31, resolve as resolve24 } from "node:path";
66347
66498
  import { Writable } from "node:stream";
66348
66499
 
@@ -66974,7 +67125,7 @@ var DaemonSupervisor = class {
66974
67125
  sessionInputPauses = /* @__PURE__ */ new Map();
66975
67126
  signalCleanupHandlers = [];
66976
67127
  descriptorDir;
66977
- generation = randomUUID13();
67128
+ generation = randomUUID14();
66978
67129
  supervisorConfigPath;
66979
67130
  defaultSessionConfig;
66980
67131
  snapshotCacheRoot;
@@ -67057,7 +67208,7 @@ var DaemonSupervisor = class {
67057
67208
  const workersToAdopt = [...this.workers.values()];
67058
67209
  for (const worker of workersToAdopt)
67059
67210
  this.pendingRlmCapacity.add(worker);
67060
- this.server = createServer2((socket) => this.handleConnection(socket));
67211
+ this.server = createServer3((socket) => this.handleConnection(socket));
67061
67212
  await this.listen();
67062
67213
  this.assertSocketLeaseHeld();
67063
67214
  this.socketIdentity = getDaemonSocketIdentity(this.socketPath);
@@ -67709,7 +67860,7 @@ var DaemonSupervisor = class {
67709
67860
  }
67710
67861
  try {
67711
67862
  const response = await this.forwardToWorker(entry.worker, {
67712
- id: randomUUID13(),
67863
+ id: randomUUID14(),
67713
67864
  type: "release_session_input_pause",
67714
67865
  activeSessionId: entry.activeSessionId,
67715
67866
  pauseId: entry.pauseId
@@ -67846,7 +67997,7 @@ var DaemonSupervisor = class {
67846
67997
  client,
67847
67998
  activeSessionId: command.activeSessionId,
67848
67999
  publicAdmissionId: command.admissionId,
67849
- workerAdmissionId: `supervisor-admission:${randomUUID13()}`,
68000
+ workerAdmissionId: `supervisor-admission:${randomUUID14()}`,
67850
68001
  status: "waiting",
67851
68002
  controller: new AbortController()
67852
68003
  };
@@ -68339,7 +68490,7 @@ var DaemonSupervisor = class {
68339
68490
  if (!this.clients.has(client) || (this.sessionInputPauseEpochs.get(client) ?? 0) !== acquisitionEpoch) {
68340
68491
  try {
68341
68492
  const release = await this.forwardToWorker(match2.worker, {
68342
- id: randomUUID13(),
68493
+ id: randomUUID14(),
68343
68494
  type: "release_session_input_pause",
68344
68495
  activeSessionId,
68345
68496
  pauseId
@@ -69164,7 +69315,7 @@ var DaemonSupervisor = class {
69164
69315
  const rootActiveSessionId = existing?.descriptor.rootActiveSessionId ?? createActiveSessionId();
69165
69316
  const socketPath = existing?.descriptor.socketPath ?? workerSocketPath(this.socketPath, workerId);
69166
69317
  const token = existing?.descriptor.authenticationToken ?? randomBytes(32).toString("base64url");
69167
- const workerInstanceId = randomUUID13();
69318
+ const workerInstanceId = randomUUID14();
69168
69319
  const now = (/* @__PURE__ */ new Date()).toISOString();
69169
69320
  const descriptorPath = existing?.descriptorPath ?? join31(this.descriptorDir, `${workerId}.json`);
69170
69321
  const recoveryJournalPath = existing?.descriptor.recoveryJournalPath ?? join31(this.descriptorDir, `${workerId}.recovery.jsonl`);
@@ -70624,7 +70775,7 @@ var DaemonSupervisor = class {
70624
70775
  if (!socketIdentity) {
70625
70776
  throw new Error("Direct transport requires an exact worker socket identity");
70626
70777
  }
70627
- const grantId = randomUUID13();
70778
+ const grantId = randomUUID14();
70628
70779
  const token = randomBytes(32).toString("base64url");
70629
70780
  const expiresAt = new Date(Date.now() + PEER_TRANSPORT_GRANT_TTL_MS).toISOString();
70630
70781
  const resolvedActiveSessionId = currentSummary.activeSessionId ?? currentSummary.id;
@@ -73137,7 +73288,7 @@ var RpcClient = class {
73137
73288
  };
73138
73289
 
73139
73290
  // dist/modes/rpc/rpc-extension-ui-context.js
73140
- import { randomUUID as randomUUID14 } from "node:crypto";
73291
+ import { randomUUID as randomUUID15 } from "node:crypto";
73141
73292
  function createRpcExtensionUiBridge(output) {
73142
73293
  const pending = /* @__PURE__ */ new Map();
73143
73294
  let closed = false;
@@ -73145,7 +73296,7 @@ function createRpcExtensionUiBridge(output) {
73145
73296
  if (closed || opts?.signal?.aborted) {
73146
73297
  return Promise.resolve(defaultValue);
73147
73298
  }
73148
- const id = randomUUID14();
73299
+ const id = randomUUID15();
73149
73300
  return new Promise((resolve26) => {
73150
73301
  let timeout;
73151
73302
  const cleanup = () => {
@@ -73170,7 +73321,7 @@ function createRpcExtensionUiBridge(output) {
73170
73321
  });
73171
73322
  };
73172
73323
  const fireAndForget = (request) => {
73173
- output({ type: "extension_ui_request", id: randomUUID14(), ...request });
73324
+ output({ type: "extension_ui_request", id: randomUUID15(), ...request });
73174
73325
  };
73175
73326
  const uiContext = {
73176
73327
  select: (title, options, opts) => createDialogPromise(opts, void 0, { method: "select", title, options, timeout: opts?.timeout }, (response) => "cancelled" in response && response.cancelled ? void 0 : "value" in response ? response.value : void 0),