zidane 6.3.6 → 6.3.8
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/{acp-Xkhrpx8r.js → acp-CAX3tOEY.js} +2 -2
- package/dist/{acp-Xkhrpx8r.js.map → acp-CAX3tOEY.js.map} +1 -1
- package/dist/acp-cli.js +2 -2
- package/dist/acp.d.ts +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-xjMzFclr.js → agent-Emjeo5Ad.js} +2 -2
- package/dist/{agent-xjMzFclr.js.map → agent-Emjeo5Ad.js.map} +1 -1
- package/dist/agent.d.ts +1 -1
- package/dist/agent.js +1 -1
- package/dist/chat/pure.d.ts +3 -3
- package/dist/chat.d.ts +50 -6
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +3 -3
- package/dist/{context-transport-O-oQHwhP.js → context-transport-3b0qpM2m.js} +3 -3
- package/dist/{context-transport-O-oQHwhP.js.map → context-transport-3b0qpM2m.js.map} +1 -1
- package/dist/contexts/daytona.d.ts +1 -1
- package/dist/contexts/e2b.d.ts +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/extensions.d.ts +1 -1
- package/dist/extensions.js +1 -1
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +1 -1
- package/dist/{index-DTIe4e1R.d.ts → index-DWXLcGB_.d.ts} +13 -1
- package/dist/{index-DTIe4e1R.d.ts.map → index-DWXLcGB_.d.ts.map} +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -3
- package/dist/{login-DZHMF7Gl.js → login-Cvx1nSVm.js} +35 -5
- package/dist/{login-DZHMF7Gl.js.map → login-Cvx1nSVm.js.map} +1 -1
- package/dist/{mcp-B5iXOceC.js → mcp-8bEHU4WB.js} +3 -3
- package/dist/{mcp-B5iXOceC.js.map → mcp-8bEHU4WB.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/output/stream-json.d.ts +1 -1
- package/dist/output/terminal.d.ts +1 -1
- package/dist/presets.d.ts +1 -1
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +1 -1
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/restate.d.ts +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/session.d.ts +1 -1
- package/dist/skills.d.ts +1 -1
- package/dist/{stdio-BsB0QEE9.js → stdio-BKFpPNvX.js} +2 -2
- package/dist/{stdio-BsB0QEE9.js.map → stdio-BKFpPNvX.js.map} +1 -1
- package/dist/{stdio-loader-p7qCIzPS.js → stdio-loader-DFDpBwwI.js} +2 -2
- package/dist/{stdio-loader-p7qCIzPS.js.map → stdio-loader-DFDpBwwI.js.map} +1 -1
- package/dist/{tool-formatters-B_dGfYqT.d.ts → tool-formatters-Co5gl_7R.d.ts} +2 -2
- package/dist/{tool-formatters-B_dGfYqT.d.ts.map → tool-formatters-Co5gl_7R.d.ts.map} +1 -1
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +1 -1
- package/dist/tools.js +1 -1
- package/dist/{transcript-anchors-CqxIlrhv.d.ts → transcript-anchors-DBPJr5Ks.d.ts} +2 -2
- package/dist/{transcript-anchors-CqxIlrhv.d.ts.map → transcript-anchors-DBPJr5Ks.d.ts.map} +1 -1
- package/dist/{transcript-anchors-Bsmm4435.js → transcript-anchors-DhBTqTyc.js} +81 -7
- package/dist/{transcript-anchors-Bsmm4435.js.map → transcript-anchors-DhBTqTyc.js.map} +1 -1
- package/dist/tui.d.ts +3 -3
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +305 -105
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-D7JhuW_X.d.ts → turn-operations-DokFalMC.d.ts} +3 -3
- package/dist/{turn-operations-D7JhuW_X.d.ts.map → turn-operations-DokFalMC.d.ts.map} +1 -1
- package/dist/types.d.ts +1 -1
- package/docs/CHAT.md +3 -1
- package/docs/TUI.md +6 -4
- package/package.json +1 -1
package/dist/tui.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { $
|
|
2
|
-
import { $ as removeInstalledExtension, At as looksBinary, C as resolveTasksDir, E as selectFilesFromSession, Gt as KEYBINDING_KEY_COL_WIDTH, Jt as groupBindings, K as createExtensionPromptBridge, Kt as ensureKeybindingsFile, Mt as createModelSearchTool, O as compactConversation, Ot as tailTruncate, S as resolveSessionDir, Sn as effectiveContextWindow, V as summaryToTurn, Xt as matchesBinding, Y as installExtensionFromSpec, Yt as keybindingsPath, _ as rehomePersistedStubs, _n as resolveExtensionConfigValue, at as composeExtensionPreset, b as resolveMcpWarningsDir, c as applyCompactSummaryCutoff, et as updateInstalledExtension, ht as basic_default, ln as parseExtensionScanPathEnv, lt as partitionExtensionCatalog, m as cleanupPersistedSession, nt as discoverAndBuildExtensionRegistry, ot as disposeExtensionRegistry, pn as coerceExtensionConfigInput, q as ExtensionInstallError, qt as formatBindingForDisplay, rn as registerExtensionHostModules, sn as inlineLoadedExtension, st as filterExtensionRegistry, t as createAgent, tn as resolveExtensionKeybindings, tt as createExtensionHud, un as scanExtensionDirectories, ut as createExtensionHostRuntimeBridge, v as resolveLegacyPersistDir, vn as sanitizeExtensionConfigActionPatch, w as buildPostCompactAttachments, x as resolvePersistDir, xn as OUTPUT_RESERVE_TOKENS, xt as isSpawnTool, y as resolveLegacySessionDir } from "./agent-
|
|
1
|
+
import { $t as toggleChainEntry, A as redirectUriFromAuthUrl, At as resolveTheme, Bt as useDiscovery, Cr as sumRunCosts, Ct as SETTINGS_TOGGLES, D as formatPathForCwd, Dr as updateToolEventOutcomes, Er as toolResultText, Et as useSettings, F as loadMcpToolsCache, G as parentServerName, Gt as resolveStoragePaths, Ht as ConfigProvider, I as mcpToolsCachePath, Jt as chainsEqual, K as createFileMcpCredentialStore, Kn as pruneTodosByRun, Kt as buildRunModel, L as refreshMcpToolsCatalog, M as runOAuthLogin, N as supportsOAuth, Nn as accentColor, O as fetchOAuthRedirect, Or as usageByModel, Q as getMcpAuthStatus, Rt as createDiscoverySlot, Sr as stripSpawnTokensLine, St as SETTINGS_INPUTS, T as suggestSafelistEntries, Tn as discoverAgentsMd, Tr as toolCallPreview, Tt as clampFps, U as buildVisibleMcpRows, Ut as useConfig, V as useMcpToolToggleMap, Vt as useDiscoveryOptional, W as indexOfServerRow, Wt as resolveConfig, X as useMcpAuthDispatch, Xt as providerKeySetChanged, Y as McpAuthProvider, Yn as useActiveTodos, Yt as filterChainFallbacks, Z as useMcpAuthState, Zt as restoreChainFallbacks, _ as useSafeModeQueue, _r as listSessionMeta, _t as listProjectFiles, a as useSurfaces, an as removeMcpServer, b as getSafelist, bt as SETTINGS_CATEGORIES, c as useStreamBuffer, cr as envSection, d as discoverProjectSkills, dn as tryOpenBrowser, dt as EMPTY_HINTS, en as useCompletion, et as ASK_USER_TOOL, fr as compactSummaryEvent, ft as clipHintsToWidth, g as useSafeModeActions, gr as lastContextSizeFromTurns, h as SafeModeProvider, hn as useUpdateCheck, hr as eventsFromTurns, ht as buildHints, i as useSelectStyle, in as mcpsWritePath, it as createInteractionTools, j as oauthUsesManualCodePaste, kt as resolveChipColor, l as buildSkillsConfig, ln as discoverProjectMcps, lr as chatReferenceTokensForPrompt, lt as useInteractionsActions, m as writeSessionExport, mn as buildUpdateHint, mr as deriveSessionTitle, mt as truncateTrailing, n as ThemeProvider, nn as hasClaudeCodeSetup, nt as PRESENT_PLAN_TOOL, on as upsertMcpServer, or as buildBuildSystem, ot as makeRequestInteraction, pn as bootTick, pt as hintsLength, qt as chainPositionOf, r as useColors, rn as importFromClaudeCode, rt as buildResumedToolResultsTurn, s as useTheme, sn as buildMcpServers, sr as buildPlanSystem, st as pendingInteractionsFromTurns, t as computeTurnAnchors, tn as countClaudeSessionFiles, tt as InteractionsProvider, u as defaultSkillScanPaths, ur as createChatTurnsReadTool, ut as useInteractionsQueue, vr as loadState, vt as useEnabledToggleSet, wn as chatAutoCompactBehavior, wt as SettingsProvider, x as isOnSafelist, xr as serverToolResultSummary, xt as SETTINGS_CHOICES, y as addToSafelist, yr as marginTopFor, yt as DEFAULT_SETTINGS, z as subscribeMcpToolsCache, zn as TODO_STATUS_GLYPHS, zt as DiscoveryProvider } from "./transcript-anchors-DhBTqTyc.js";
|
|
2
|
+
import { $ as removeInstalledExtension, At as looksBinary, C as resolveTasksDir, E as selectFilesFromSession, Gt as KEYBINDING_KEY_COL_WIDTH, Jt as groupBindings, K as createExtensionPromptBridge, Kt as ensureKeybindingsFile, Mt as createModelSearchTool, O as compactConversation, Ot as tailTruncate, S as resolveSessionDir, Sn as effectiveContextWindow, V as summaryToTurn, Xt as matchesBinding, Y as installExtensionFromSpec, Yt as keybindingsPath, _ as rehomePersistedStubs, _n as resolveExtensionConfigValue, at as composeExtensionPreset, b as resolveMcpWarningsDir, c as applyCompactSummaryCutoff, et as updateInstalledExtension, ht as basic_default, ln as parseExtensionScanPathEnv, lt as partitionExtensionCatalog, m as cleanupPersistedSession, nt as discoverAndBuildExtensionRegistry, ot as disposeExtensionRegistry, pn as coerceExtensionConfigInput, q as ExtensionInstallError, qt as formatBindingForDisplay, rn as registerExtensionHostModules, sn as inlineLoadedExtension, st as filterExtensionRegistry, t as createAgent, tn as resolveExtensionKeybindings, tt as createExtensionHud, un as scanExtensionDirectories, ut as createExtensionHostRuntimeBridge, v as resolveLegacyPersistDir, vn as sanitizeExtensionConfigActionPatch, w as buildPostCompactAttachments, x as resolvePersistDir, xn as OUTPUT_RESERVE_TOKENS, xt as isSpawnTool, y as resolveLegacySessionDir } from "./agent-Emjeo5Ad.js";
|
|
3
3
|
import { a as formatDuration, c as previewLine, i as fmtTokens, l as shortId, n as compactPath, o as formatTaskStatus, r as fmtCost, s as formatTaskSummary, t as ageString } from "./format-BF745pLL.js";
|
|
4
4
|
import { n as appendCapped } from "./output-cap-C9zukcCj.js";
|
|
5
5
|
import { i as killProcessGroup, n as SUPPORTS_PROCESS_GROUPS, r as createProcessContext } from "./contexts-DWVt7MvF.js";
|
|
@@ -7,7 +7,7 @@ import { l as errorMessage } from "./errors-DJUxZg9b.js";
|
|
|
7
7
|
import { M as findGitRoot } from "./resolve-BPTA1RpX.js";
|
|
8
8
|
import { a as createRingBufferSink, i as createLoggingHooks, l as logFileTimestamp, n as createFileSink, o as fanoutSink, r as createLogger, s as formatLogLine, t as consoleSink } from "./logger-DBX9uYOw.js";
|
|
9
9
|
import { i as sniffImageMediaType } from "./media-sniff-UKijEaiQ.js";
|
|
10
|
-
import { g as hasAuthorizationHeader, m as McpOAuthProvider, n as connectMcpServers, s as nonOwningMcpView } from "./mcp-
|
|
10
|
+
import { g as hasAuthorizationHeader, m as McpOAuthProvider, n as connectMcpServers, s as nonOwningMcpView } from "./mcp-8bEHU4WB.js";
|
|
11
11
|
import { C as estimateRebilledCostUsd } from "./openai-compat-Bf9uMTMa.js";
|
|
12
12
|
import { replaceDynamicSection } from "./prompt.js";
|
|
13
13
|
import { a as getExtensionSessionState, c as setExtensionSessionState, i as demoteStaleRunningStatus, o as loadSession, t as createSession } from "./session-Bdk31kA5.js";
|
|
@@ -17,7 +17,7 @@ import { i as installCacheBreakLogger } from "./cache-telemetry-ehVdWsg2.js";
|
|
|
17
17
|
import { a as generateStructuredData, c as shouldAutoGenerateTitle, f as commandBlockToEvent, g as parseCommandInvocation, h as executeCommand, n as patchInlineGhost, p as commandBlocksToContent, r as resolveInlineSuggestion, s as generateSessionTitle, t as createPathAutocompleteProvider } from "./inline-autocomplete-DC2cxprN.js";
|
|
18
18
|
import { s as resolveCompletionSubmit } from "./completion-core-BRo86R_L.js";
|
|
19
19
|
import { i as installSkillFromSpec, r as SkillInstallError } from "./skills-C7eIjWYm.js";
|
|
20
|
-
import { a as ZIDANE_EXTENSION_SKILL_NAME, d as extensionScaffold, f as readSkillEditMarker, h as withZidaneSkillEditSkill, i as SKILL_EDIT_METADATA_KEY, l as editSessionMetadata, m as withZidaneExtensionSkill, o as ZIDANE_SKILL_EDIT_SKILL_NAME, t as loginMcpServer, u as extensionDirForCwd } from "./login-
|
|
20
|
+
import { a as ZIDANE_EXTENSION_SKILL_NAME, d as extensionScaffold, f as readSkillEditMarker, h as withZidaneSkillEditSkill, i as SKILL_EDIT_METADATA_KEY, l as editSessionMetadata, m as withZidaneExtensionSkill, o as ZIDANE_SKILL_EDIT_SKILL_NAME, t as loginMcpServer, u as extensionDirForCwd } from "./login-Cvx1nSVm.js";
|
|
21
21
|
import { C as restoreModelOptions, S as piIdOf, _ as modelOptionsFor, m as getContextWindow, p as enabledModelOptions, s as setProviderCredential, t as detectAuth, v as modelSupportsReasoning, w as renderDefaultCallbackPage } from "./auth-Dptb8U5J.js";
|
|
22
22
|
import { createDockerContext } from "./contexts/docker.js";
|
|
23
23
|
import { createTuiStore } from "./session/sqlite.js";
|
|
@@ -3475,7 +3475,7 @@ function UserPromptBlock({ text, refs, attachments, searchQuery }) {
|
|
|
3475
3475
|
});
|
|
3476
3476
|
}
|
|
3477
3477
|
/** Click-feedback delay before `[copied]` reverts to `[copy]`, in ms. */
|
|
3478
|
-
const COPY_FEEDBACK_MS = 1200;
|
|
3478
|
+
const COPY_FEEDBACK_MS$1 = 1200;
|
|
3479
3479
|
/**
|
|
3480
3480
|
* BoxRenderable subclass that hosts a `[lang] … [copy]` header above
|
|
3481
3481
|
* an inner {@link CodeRenderable} body. Returned from
|
|
@@ -3583,7 +3583,7 @@ var CodeBlockWrapper = class extends BoxRenderable {
|
|
|
3583
3583
|
if (this.button.isDestroyed) return;
|
|
3584
3584
|
this.button.content = "[copy]";
|
|
3585
3585
|
this.button.fg = this.bag.colors.warn;
|
|
3586
|
-
}, COPY_FEEDBACK_MS);
|
|
3586
|
+
}, COPY_FEEDBACK_MS$1);
|
|
3587
3587
|
};
|
|
3588
3588
|
this.header.add(this.langLabel);
|
|
3589
3589
|
this.header.add(this.spacer);
|
|
@@ -9419,11 +9419,12 @@ function OptionList({ items, initialCursor, onPick }) {
|
|
|
9419
9419
|
* budget — default 14 fits a typical modal; pass a smaller value for
|
|
9420
9420
|
* less-padded containers (e.g. 6 for the auth wizard panel).
|
|
9421
9421
|
*/
|
|
9422
|
-
function OAuthUrlBlock({ url, fg, maxLineWidth, chromeWidth = 14 }) {
|
|
9422
|
+
function OAuthUrlBlock({ url, fg, bg, maxLineWidth, chromeWidth = 14 }) {
|
|
9423
9423
|
const { width: termWidth } = useTerminalDimensions();
|
|
9424
9424
|
return /* @__PURE__ */ jsx$1(Fragment, { children: chunkString(url, Math.max(20, Math.min(maxLineWidth, termWidth - chromeWidth))).map((line, i) => /* @__PURE__ */ jsx$1("text", {
|
|
9425
9425
|
wrapMode: "none",
|
|
9426
9426
|
fg,
|
|
9427
|
+
...bg ? { bg } : {},
|
|
9427
9428
|
children: /* @__PURE__ */ jsx$1("a", {
|
|
9428
9429
|
href: url,
|
|
9429
9430
|
children: line
|
|
@@ -9441,6 +9442,65 @@ function chunkString(s, n) {
|
|
|
9441
9442
|
/** Keystroke shown next to the open-browser button. */
|
|
9442
9443
|
const OPEN_BROWSER_KEY = "ctrl+b";
|
|
9443
9444
|
/**
|
|
9445
|
+
* Keystroke shown next to the copy-URL button. `ctrl+y` is the global
|
|
9446
|
+
* `openKeybindings` chord, but it can't collide here: every surface that
|
|
9447
|
+
* mounts this block is either a modal (`app.tsx` bails on `modal.isOpen`
|
|
9448
|
+
* before dispatching global bindings) or the auth screen (excluded by
|
|
9449
|
+
* `screen !== 'auth'`). It's also absent from OpenTUI's input key table,
|
|
9450
|
+
* so the focused paste field ignores it.
|
|
9451
|
+
*/
|
|
9452
|
+
const COPY_URL_KEY = "ctrl+y";
|
|
9453
|
+
/** How long a `copied` confirmation stays on screen. */
|
|
9454
|
+
const COPY_FEEDBACK_MS = 4e3;
|
|
9455
|
+
/**
|
|
9456
|
+
* Paste-input capacity. OpenTUI's `InputRenderable` defaults to 1000 chars
|
|
9457
|
+
* and silently drops the overflow — long enough to truncate a callback URL
|
|
9458
|
+
* carrying a fat `code` + `state` and make a correct paste look corrupt.
|
|
9459
|
+
*/
|
|
9460
|
+
const PASTE_MAX_LENGTH = 4096;
|
|
9461
|
+
/** After this long without an authorization URL, spell out the escape route. */
|
|
9462
|
+
const SLOW_START_SECONDS = 8;
|
|
9463
|
+
/**
|
|
9464
|
+
* Seconds elapsed since `active` became true, ticking once a second.
|
|
9465
|
+
* Frozen (and reset) when inactive so the panel doesn't repaint after the
|
|
9466
|
+
* URL lands.
|
|
9467
|
+
*/
|
|
9468
|
+
function useElapsedSeconds(active) {
|
|
9469
|
+
const [seconds, setSeconds] = useState(0);
|
|
9470
|
+
useEffect(() => {
|
|
9471
|
+
if (!active) {
|
|
9472
|
+
setSeconds(0);
|
|
9473
|
+
return;
|
|
9474
|
+
}
|
|
9475
|
+
const started = Date.now();
|
|
9476
|
+
const timer = setInterval(() => setSeconds(Math.floor((Date.now() - started) / 1e3)), 1e3);
|
|
9477
|
+
return () => clearInterval(timer);
|
|
9478
|
+
}, [active]);
|
|
9479
|
+
return seconds;
|
|
9480
|
+
}
|
|
9481
|
+
/**
|
|
9482
|
+
* Copy-to-clipboard with a self-clearing "copied" flag. Every copy
|
|
9483
|
+
* affordance in this module shares it so the timers are cleaned up on
|
|
9484
|
+
* unmount in exactly one place.
|
|
9485
|
+
*/
|
|
9486
|
+
function useCopyFlash() {
|
|
9487
|
+
const [copied, setCopied] = useState(false);
|
|
9488
|
+
const timer = useRef(null);
|
|
9489
|
+
const copy = useCallback((text) => {
|
|
9490
|
+
if (!writeToClipboard(text)) return;
|
|
9491
|
+
setCopied(true);
|
|
9492
|
+
if (timer.current) clearTimeout(timer.current);
|
|
9493
|
+
timer.current = setTimeout(setCopied, COPY_FEEDBACK_MS, false);
|
|
9494
|
+
}, []);
|
|
9495
|
+
useEffect(() => () => {
|
|
9496
|
+
if (timer.current) clearTimeout(timer.current);
|
|
9497
|
+
}, []);
|
|
9498
|
+
return {
|
|
9499
|
+
copied,
|
|
9500
|
+
copy
|
|
9501
|
+
};
|
|
9502
|
+
}
|
|
9503
|
+
/**
|
|
9444
9504
|
* Device-authorization affordance (RFC 8628): the user code the user must see
|
|
9445
9505
|
* on the consent screen, plus the verification URI they type it into.
|
|
9446
9506
|
*
|
|
@@ -9452,19 +9512,13 @@ const OPEN_BROWSER_KEY = "ctrl+b";
|
|
|
9452
9512
|
*/
|
|
9453
9513
|
function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
|
|
9454
9514
|
const COLOR = useColors();
|
|
9455
|
-
const
|
|
9456
|
-
const copyTimer = useRef(null);
|
|
9515
|
+
const { copied, copy } = useCopyFlash();
|
|
9457
9516
|
useKeyboard((key) => {
|
|
9458
9517
|
if (key.name === "u" && key.ctrl) {
|
|
9459
9518
|
key.preventDefault();
|
|
9460
|
-
|
|
9461
|
-
if (copyTimer.current) clearTimeout(copyTimer.current);
|
|
9462
|
-
copyTimer.current = setTimeout(setCopied, 4e3, false);
|
|
9519
|
+
copy(userCode);
|
|
9463
9520
|
}
|
|
9464
9521
|
});
|
|
9465
|
-
useEffect(() => () => {
|
|
9466
|
-
if (copyTimer.current) clearTimeout(copyTimer.current);
|
|
9467
|
-
}, []);
|
|
9468
9522
|
return /* @__PURE__ */ jsxs("box", {
|
|
9469
9523
|
style: { flexDirection: "column" },
|
|
9470
9524
|
children: [
|
|
@@ -9489,23 +9543,29 @@ function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
|
|
|
9489
9543
|
});
|
|
9490
9544
|
}
|
|
9491
9545
|
/**
|
|
9492
|
-
* Unified affordance for surfacing an OAuth authorization URL in the TUI
|
|
9546
|
+
* Unified affordance for surfacing an OAuth authorization URL in the TUI,
|
|
9547
|
+
* laid out as two numbered steps so the remote-session path reads as a
|
|
9548
|
+
* first-class option instead of a footnote:
|
|
9493
9549
|
*
|
|
9494
|
-
* 1
|
|
9495
|
-
*
|
|
9496
|
-
*
|
|
9497
|
-
*
|
|
9498
|
-
*
|
|
9499
|
-
*
|
|
9500
|
-
*
|
|
9501
|
-
*
|
|
9502
|
-
*
|
|
9503
|
-
*
|
|
9504
|
-
*
|
|
9505
|
-
*
|
|
9506
|
-
*
|
|
9507
|
-
*
|
|
9508
|
-
*
|
|
9550
|
+
* **1 — get to the consent screen.** Two real buttons (mouse-clickable
|
|
9551
|
+
* AND keyed): {@link OPEN_BROWSER_KEY} opens the URL via the OS handler,
|
|
9552
|
+
* {@link COPY_URL_KEY} copies it to the clipboard (OSC 52 → lands on the
|
|
9553
|
+
* user's LOCAL clipboard over SSH). Below them the URL itself sits in a
|
|
9554
|
+
* framed panel with its own `[copy]` button, chunked into per-row OSC 8
|
|
9555
|
+
* hyperlinks by {@link OAuthUrlBlock} — the backup channel for terminals
|
|
9556
|
+
* without clickable links and for drag-select copying.
|
|
9557
|
+
*
|
|
9558
|
+
* **2 — hand the redirect back.** When `paste` is provided, an `<input>`
|
|
9559
|
+
* accepts whatever the browser (or consent page) produced. This is THE
|
|
9560
|
+
* path for `zidane` running over SSH: the browser sits on the user's
|
|
9561
|
+
* laptop and cannot reach the remote box's loopback callback server, so
|
|
9562
|
+
* they copy the failed `http://127.0.0.1:<port>/callback?code=…` out of
|
|
9563
|
+
* the URL bar and paste it here. Servers that render the code on the
|
|
9564
|
+
* page instead of redirecting have no failed URL to copy — paste the
|
|
9565
|
+
* code itself; `normalizeRedirectPaste` completes it against the
|
|
9566
|
+
* authorization URL's `redirect_uri` + `state`. We name that exact
|
|
9567
|
+
* address (parsed out of the authorization URL) so there's no guessing
|
|
9568
|
+
* about which string to copy.
|
|
9509
9569
|
*
|
|
9510
9570
|
* The component owns presentation only — keyboard focus, input ref, and
|
|
9511
9571
|
* the submit handler stay with the caller so the affordance composes
|
|
@@ -9513,31 +9573,40 @@ function OAuthDeviceCodeBlock({ userCode, verificationUri, expiresInSeconds }) {
|
|
|
9513
9573
|
*/
|
|
9514
9574
|
function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
|
|
9515
9575
|
const COLOR = useColors();
|
|
9576
|
+
const callbackUri = redirectUriFromAuthUrl(authUrl);
|
|
9577
|
+
const pasteRef = paste?.inputRef;
|
|
9578
|
+
const pasteFocused = paste?.focused ?? false;
|
|
9579
|
+
useEffect(() => {
|
|
9580
|
+
if (pasteFocused) pasteRef?.current?.focus();
|
|
9581
|
+
}, [pasteFocused, pasteRef]);
|
|
9516
9582
|
return /* @__PURE__ */ jsxs("box", {
|
|
9517
9583
|
style: {
|
|
9518
9584
|
flexDirection: "column",
|
|
9519
9585
|
gap: 1
|
|
9520
9586
|
},
|
|
9521
9587
|
children: [
|
|
9522
|
-
/* @__PURE__ */ jsx$1(OpenBrowserButton, { authUrl }),
|
|
9523
9588
|
/* @__PURE__ */ jsxs("box", {
|
|
9524
9589
|
style: { flexDirection: "column" },
|
|
9525
|
-
children: [/* @__PURE__ */ jsx$1(
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
}), /* @__PURE__ */ jsx$1(
|
|
9529
|
-
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9590
|
+
children: [/* @__PURE__ */ jsx$1(StepHeading, {
|
|
9591
|
+
step: paste ? "1" : void 0,
|
|
9592
|
+
text: "open this URL in your browser to authorize"
|
|
9593
|
+
}), /* @__PURE__ */ jsx$1(ActionButtons, { authUrl })]
|
|
9594
|
+
}),
|
|
9595
|
+
/* @__PURE__ */ jsx$1(AuthUrlPanel, {
|
|
9596
|
+
url: authUrl,
|
|
9597
|
+
maxLineWidth,
|
|
9598
|
+
chromeWidth
|
|
9534
9599
|
}),
|
|
9535
9600
|
paste && /* @__PURE__ */ jsxs("box", {
|
|
9536
9601
|
style: { flexDirection: "column" },
|
|
9537
9602
|
children: [
|
|
9603
|
+
/* @__PURE__ */ jsx$1(StepHeading, {
|
|
9604
|
+
step: "2",
|
|
9605
|
+
text: "no browser on this machine? (SSH, headless, firewalled)"
|
|
9606
|
+
}),
|
|
9538
9607
|
/* @__PURE__ */ jsx$1("text", {
|
|
9539
|
-
fg: COLOR.
|
|
9540
|
-
children:
|
|
9608
|
+
fg: COLOR.dim,
|
|
9609
|
+
children: callbackUri ? `after you authorize, the browser lands on ${callbackUri} and fails — copy that whole address from its URL bar and paste it below.` : "after you authorize, copy the address the browser failed to reach (it starts with http://127.0.0.1) and paste it below."
|
|
9541
9610
|
}),
|
|
9542
9611
|
paste.hint && /* @__PURE__ */ jsx$1("text", {
|
|
9543
9612
|
fg: toneColor(paste.hint.tone, COLOR),
|
|
@@ -9554,7 +9623,8 @@ function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
|
|
|
9554
9623
|
children: /* @__PURE__ */ jsx$1("input", {
|
|
9555
9624
|
ref: paste.inputRef,
|
|
9556
9625
|
focused: paste.focused,
|
|
9557
|
-
|
|
9626
|
+
maxLength: PASTE_MAX_LENGTH,
|
|
9627
|
+
placeholder: paste.placeholder ?? "paste redirect URL (or just the code) and press enter…",
|
|
9558
9628
|
onSubmit: paste.onSubmit,
|
|
9559
9629
|
style: { flexGrow: 1 }
|
|
9560
9630
|
})
|
|
@@ -9565,76 +9635,181 @@ function OAuthAuthBlock({ authUrl, maxLineWidth, chromeWidth = 14, paste }) {
|
|
|
9565
9635
|
});
|
|
9566
9636
|
}
|
|
9567
9637
|
/**
|
|
9568
|
-
*
|
|
9569
|
-
*
|
|
9570
|
-
|
|
9571
|
-
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9638
|
+
* Numbered step label. The numeral is dropped when the block has only one
|
|
9639
|
+
* step (no paste input) — "1" with no "2" reads like a truncated list.
|
|
9640
|
+
*/
|
|
9641
|
+
function StepHeading({ step, text }) {
|
|
9642
|
+
const COLOR = useColors();
|
|
9643
|
+
return /* @__PURE__ */ jsxs("text", { children: [step !== void 0 && /* @__PURE__ */ jsx$1("span", {
|
|
9644
|
+
fg: COLOR.brand,
|
|
9645
|
+
attributes: 1,
|
|
9646
|
+
children: `${step} `
|
|
9647
|
+
}), /* @__PURE__ */ jsx$1("span", {
|
|
9648
|
+
fg: COLOR.mute,
|
|
9649
|
+
children: text
|
|
9650
|
+
})] });
|
|
9651
|
+
}
|
|
9652
|
+
/**
|
|
9653
|
+
* The two primary buttons, side by side: open-in-browser and copy-URL.
|
|
9654
|
+
*
|
|
9655
|
+
* Both are bordered boxes with a labeled keystroke, both are clickable
|
|
9656
|
+
* (`onMouseDown`), and both keys work regardless of which input owns focus
|
|
9657
|
+
* (`preventDefault` keeps the focused field from double-handling). Splitting
|
|
9658
|
+
* copy out of open matters over SSH: `open`/`xdg-open` on a headless remote
|
|
9659
|
+
* box does nothing, and the user's real intent is "give me the URL".
|
|
9576
9660
|
*
|
|
9577
|
-
* The
|
|
9578
|
-
* click in a
|
|
9579
|
-
*
|
|
9580
|
-
* depend on terminal capability — and the hyperlink is the bonus.
|
|
9661
|
+
* The open button's label is additionally wrapped in an OSC 8 hyperlink so
|
|
9662
|
+
* a click in a terminal that supports links (iTerm2, Kitty, WezTerm, …)
|
|
9663
|
+
* reaches the browser on the user's LOCAL machine even over SSH.
|
|
9581
9664
|
*/
|
|
9582
|
-
function
|
|
9665
|
+
function ActionButtons({ authUrl }) {
|
|
9583
9666
|
const COLOR = useColors();
|
|
9584
|
-
const [
|
|
9585
|
-
const
|
|
9586
|
-
const
|
|
9667
|
+
const [opened, setOpened] = useState(false);
|
|
9668
|
+
const openedTimer = useRef(null);
|
|
9669
|
+
const { copied, copy } = useCopyFlash();
|
|
9670
|
+
const open = useCallback(() => {
|
|
9587
9671
|
tryOpenBrowser(authUrl);
|
|
9588
|
-
|
|
9589
|
-
|
|
9590
|
-
|
|
9591
|
-
feedbackTimer.current = setTimeout(setFeedback, 4e3, null);
|
|
9672
|
+
setOpened(true);
|
|
9673
|
+
if (openedTimer.current) clearTimeout(openedTimer.current);
|
|
9674
|
+
openedTimer.current = setTimeout(setOpened, COPY_FEEDBACK_MS, false);
|
|
9592
9675
|
}, [authUrl]);
|
|
9676
|
+
useEffect(() => () => {
|
|
9677
|
+
if (openedTimer.current) clearTimeout(openedTimer.current);
|
|
9678
|
+
}, []);
|
|
9593
9679
|
useKeyboard((key) => {
|
|
9594
|
-
if (key.ctrl
|
|
9680
|
+
if (!key.ctrl) return;
|
|
9681
|
+
if (key.name === "b") {
|
|
9682
|
+
key.preventDefault();
|
|
9683
|
+
open();
|
|
9684
|
+
return;
|
|
9685
|
+
}
|
|
9686
|
+
if (key.name === "y") {
|
|
9595
9687
|
key.preventDefault();
|
|
9596
|
-
|
|
9688
|
+
copy(authUrl);
|
|
9597
9689
|
}
|
|
9598
9690
|
});
|
|
9599
|
-
useEffect(() => () => {
|
|
9600
|
-
if (feedbackTimer.current) clearTimeout(feedbackTimer.current);
|
|
9601
|
-
}, []);
|
|
9602
9691
|
return /* @__PURE__ */ jsxs("box", {
|
|
9603
9692
|
style: { flexDirection: "column" },
|
|
9604
|
-
children: [/* @__PURE__ */
|
|
9693
|
+
children: [/* @__PURE__ */ jsxs("box", {
|
|
9694
|
+
style: {
|
|
9695
|
+
flexDirection: "row",
|
|
9696
|
+
gap: 2,
|
|
9697
|
+
flexWrap: "wrap"
|
|
9698
|
+
},
|
|
9699
|
+
children: [/* @__PURE__ */ jsx$1(Button, {
|
|
9700
|
+
color: COLOR.brand,
|
|
9701
|
+
chord: OPEN_BROWSER_KEY,
|
|
9702
|
+
onPress: open,
|
|
9703
|
+
children: /* @__PURE__ */ jsx$1("a", {
|
|
9704
|
+
href: authUrl,
|
|
9705
|
+
fg: COLOR.brand,
|
|
9706
|
+
children: "↗ Open in browser"
|
|
9707
|
+
})
|
|
9708
|
+
}), /* @__PURE__ */ jsx$1(Button, {
|
|
9709
|
+
color: COLOR.warn,
|
|
9710
|
+
chord: COPY_URL_KEY,
|
|
9711
|
+
onPress: () => copy(authUrl),
|
|
9712
|
+
children: /* @__PURE__ */ jsx$1("span", {
|
|
9713
|
+
fg: COLOR.warn,
|
|
9714
|
+
children: "⧉ Copy URL"
|
|
9715
|
+
})
|
|
9716
|
+
})]
|
|
9717
|
+
}), (opened || copied) && /* @__PURE__ */ jsx$1("text", {
|
|
9718
|
+
fg: COLOR.accent,
|
|
9719
|
+
children: `✓ ${[opened ? "opened in browser" : null, copied ? "URL copied to clipboard" : null].filter(Boolean).join(" · ")}`
|
|
9720
|
+
})]
|
|
9721
|
+
});
|
|
9722
|
+
}
|
|
9723
|
+
/**
|
|
9724
|
+
* Bordered, clickable button hosting a label plus its keystroke hint.
|
|
9725
|
+
* The handler lives on the outer box: mouse events bubble from whatever
|
|
9726
|
+
* renderable was hit (label, padding, border) up through ancestors, so
|
|
9727
|
+
* the whole framed area is the target — not just the label text. Same
|
|
9728
|
+
* pattern as the settings-modal rows (`settings-modal.tsx:4006`).
|
|
9729
|
+
*/
|
|
9730
|
+
function Button({ color, chord, onPress, children }) {
|
|
9731
|
+
const COLOR = useColors();
|
|
9732
|
+
return /* @__PURE__ */ jsx$1("box", {
|
|
9733
|
+
style: {
|
|
9734
|
+
border: true,
|
|
9735
|
+
borderColor: color,
|
|
9736
|
+
paddingLeft: 1,
|
|
9737
|
+
paddingRight: 1,
|
|
9738
|
+
alignSelf: "flex-start"
|
|
9739
|
+
},
|
|
9740
|
+
onMouseDown: (event) => {
|
|
9741
|
+
event.stopPropagation();
|
|
9742
|
+
event.preventDefault();
|
|
9743
|
+
onPress();
|
|
9744
|
+
},
|
|
9745
|
+
children: /* @__PURE__ */ jsxs("text", {
|
|
9746
|
+
wrapMode: "none",
|
|
9747
|
+
selectable: false,
|
|
9748
|
+
children: [
|
|
9749
|
+
children,
|
|
9750
|
+
/* @__PURE__ */ jsx$1("span", {
|
|
9751
|
+
fg: COLOR.mute,
|
|
9752
|
+
children: " "
|
|
9753
|
+
}),
|
|
9754
|
+
/* @__PURE__ */ jsx$1("span", {
|
|
9755
|
+
fg: COLOR.warn,
|
|
9756
|
+
children: chord
|
|
9757
|
+
})
|
|
9758
|
+
]
|
|
9759
|
+
})
|
|
9760
|
+
});
|
|
9761
|
+
}
|
|
9762
|
+
/**
|
|
9763
|
+
* The authorization URL as a framed, copy-able panel — same idea as a
|
|
9764
|
+
* fenced code block in the transcript (`auth url` label left, clickable
|
|
9765
|
+
* `[copy]` right), so "here is a literal string you need to move
|
|
9766
|
+
* somewhere else" reads the same everywhere in the app.
|
|
9767
|
+
*
|
|
9768
|
+
* Framed rather than background-tinted because this block renders inside
|
|
9769
|
+
* containers that are already painted with the elevated modal surface; a
|
|
9770
|
+
* border is the only contrast that survives every host.
|
|
9771
|
+
*/
|
|
9772
|
+
function AuthUrlPanel({ url, maxLineWidth, chromeWidth }) {
|
|
9773
|
+
const COLOR = useColors();
|
|
9774
|
+
const { copied, copy } = useCopyFlash();
|
|
9775
|
+
return /* @__PURE__ */ jsx$1("box", {
|
|
9776
|
+
style: { flexDirection: "column" },
|
|
9777
|
+
children: /* @__PURE__ */ jsxs("box", {
|
|
9605
9778
|
style: {
|
|
9779
|
+
flexDirection: "column",
|
|
9606
9780
|
border: true,
|
|
9607
|
-
borderColor: COLOR.
|
|
9781
|
+
borderColor: COLOR.border,
|
|
9608
9782
|
paddingLeft: 1,
|
|
9609
|
-
paddingRight: 1
|
|
9610
|
-
alignSelf: "flex-start"
|
|
9783
|
+
paddingRight: 1
|
|
9611
9784
|
},
|
|
9612
|
-
children: /* @__PURE__ */ jsxs("
|
|
9613
|
-
|
|
9785
|
+
children: [/* @__PURE__ */ jsxs("box", {
|
|
9786
|
+
style: { flexDirection: "row" },
|
|
9614
9787
|
children: [
|
|
9615
|
-
/* @__PURE__ */ jsx$1("
|
|
9616
|
-
href: authUrl,
|
|
9617
|
-
fg: COLOR.brand,
|
|
9618
|
-
children: "↗ Open auth URL in browser"
|
|
9619
|
-
}),
|
|
9620
|
-
/* @__PURE__ */ jsx$1("span", {
|
|
9788
|
+
/* @__PURE__ */ jsx$1("text", {
|
|
9621
9789
|
fg: COLOR.mute,
|
|
9622
|
-
children: "
|
|
9790
|
+
children: "auth url"
|
|
9623
9791
|
}),
|
|
9624
|
-
/* @__PURE__ */ jsx$1("
|
|
9625
|
-
|
|
9626
|
-
|
|
9627
|
-
|
|
9628
|
-
|
|
9629
|
-
|
|
9630
|
-
|
|
9792
|
+
/* @__PURE__ */ jsx$1("box", { style: { flexGrow: 1 } }),
|
|
9793
|
+
/* @__PURE__ */ jsx$1("box", {
|
|
9794
|
+
onMouseDown: (event) => {
|
|
9795
|
+
event.stopPropagation();
|
|
9796
|
+
event.preventDefault();
|
|
9797
|
+
copy(url);
|
|
9798
|
+
},
|
|
9799
|
+
children: /* @__PURE__ */ jsx$1("text", {
|
|
9800
|
+
selectable: false,
|
|
9801
|
+
fg: copied ? COLOR.accent : COLOR.warn,
|
|
9802
|
+
children: copied ? "[copied]" : "[copy]"
|
|
9803
|
+
})
|
|
9631
9804
|
})
|
|
9632
9805
|
]
|
|
9633
|
-
})
|
|
9634
|
-
|
|
9635
|
-
|
|
9636
|
-
|
|
9637
|
-
|
|
9806
|
+
}), /* @__PURE__ */ jsx$1(OAuthUrlBlock, {
|
|
9807
|
+
url,
|
|
9808
|
+
fg: COLOR.dim,
|
|
9809
|
+
maxLineWidth: maxLineWidth - 4,
|
|
9810
|
+
chromeWidth: chromeWidth + 4
|
|
9811
|
+
})]
|
|
9812
|
+
})
|
|
9638
9813
|
});
|
|
9639
9814
|
}
|
|
9640
9815
|
function toneColor(tone, COLOR) {
|
|
@@ -9653,7 +9828,9 @@ function toneColor(tone, COLOR) {
|
|
|
9653
9828
|
* MCP SDK's loopback callback server resolves the OAuth promise
|
|
9654
9829
|
* through the same code path a real browser-redirect would have
|
|
9655
9830
|
* taken — works over SSH where the browser can't reach the remote
|
|
9656
|
-
* callback server directly.
|
|
9831
|
+
* callback server directly. `authUrl` rides along so a partial paste
|
|
9832
|
+
* (bare code, query string) is completed against the callback URI
|
|
9833
|
+
* the authorization request was minted with.
|
|
9657
9834
|
*
|
|
9658
9835
|
* `inputFocused` is forwarded as the input's `focused` prop AND read by
|
|
9659
9836
|
* the parent picker's `useKeyboard` (via a ref) to suppress single-key
|
|
@@ -9663,7 +9840,8 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9663
9840
|
const COLOR = useColors();
|
|
9664
9841
|
const inputRef = useRef(null);
|
|
9665
9842
|
const [hint, setHint] = useState(null);
|
|
9666
|
-
const
|
|
9843
|
+
const waited = useElapsedSeconds(authUrl === null);
|
|
9844
|
+
const status = hint && hint.tone !== "error" ? hint.text : authUrl === null ? `contacting ${serverName} for an authorization URL… (${waited}s)` : "complete the login in your browser — waiting for the callback…";
|
|
9667
9845
|
const onSubmit = useCallback(() => {
|
|
9668
9846
|
const value = inputRef.current?.value?.trim() ?? "";
|
|
9669
9847
|
if (!value) return;
|
|
@@ -9673,7 +9851,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9673
9851
|
});
|
|
9674
9852
|
(async () => {
|
|
9675
9853
|
try {
|
|
9676
|
-
const result = await fetchOAuthRedirect(value);
|
|
9854
|
+
const result = await fetchOAuthRedirect(value, { ...authUrl ? { authUrl } : {} });
|
|
9677
9855
|
if (result.status >= 200 && result.status < 300) setHint({
|
|
9678
9856
|
text: "redirect accepted — finalizing…",
|
|
9679
9857
|
tone: "accent"
|
|
@@ -9689,7 +9867,7 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9689
9867
|
});
|
|
9690
9868
|
}
|
|
9691
9869
|
})();
|
|
9692
|
-
}, []);
|
|
9870
|
+
}, [authUrl]);
|
|
9693
9871
|
return /* @__PURE__ */ jsxs("box", {
|
|
9694
9872
|
style: {
|
|
9695
9873
|
flexDirection: "column",
|
|
@@ -9706,15 +9884,18 @@ function McpAuthorizingPanel({ serverName, authUrl, maxLineWidth, chromeWidth, i
|
|
|
9706
9884
|
},
|
|
9707
9885
|
children: [
|
|
9708
9886
|
/* @__PURE__ */ jsx$1(Spinner, { label: status }),
|
|
9887
|
+
authUrl === null && waited >= SLOW_START_SECONDS && /* @__PURE__ */ jsx$1("text", {
|
|
9888
|
+
fg: COLOR.warn,
|
|
9889
|
+
children: `${serverName} is slow to respond — press esc to cancel and retry, or check the server URL in this list.`
|
|
9890
|
+
}),
|
|
9709
9891
|
authUrl !== null && /* @__PURE__ */ jsx$1(OAuthAuthBlock, {
|
|
9710
9892
|
authUrl,
|
|
9711
|
-
maxLineWidth,
|
|
9893
|
+
maxLineWidth: maxLineWidth - 4,
|
|
9712
9894
|
chromeWidth: (chromeWidth ?? 14) + 4,
|
|
9713
9895
|
paste: {
|
|
9714
9896
|
inputRef,
|
|
9715
9897
|
focused: inputFocused,
|
|
9716
9898
|
onSubmit,
|
|
9717
|
-
placeholder: "paste redirect URL and press enter…",
|
|
9718
9899
|
hint: hint?.tone === "error" ? hint : void 0
|
|
9719
9900
|
}
|
|
9720
9901
|
}),
|
|
@@ -10527,7 +10708,7 @@ function OAuthRunningStep({ descriptor, dataDir, onSuccess, onError }) {
|
|
|
10527
10708
|
});
|
|
10528
10709
|
(async () => {
|
|
10529
10710
|
try {
|
|
10530
|
-
const result = await fetchOAuthRedirect(value);
|
|
10711
|
+
const result = await fetchOAuthRedirect(value, { ...url ? { authUrl: url } : {} });
|
|
10531
10712
|
if (result.status >= 200 && result.status < 300) {
|
|
10532
10713
|
setPasteHint({
|
|
10533
10714
|
text: "redirect accepted — exchanging code…",
|
|
@@ -10545,7 +10726,7 @@ function OAuthRunningStep({ descriptor, dataDir, onSuccess, onError }) {
|
|
|
10545
10726
|
});
|
|
10546
10727
|
}
|
|
10547
10728
|
})();
|
|
10548
|
-
}, []);
|
|
10729
|
+
}, [url]);
|
|
10549
10730
|
return /* @__PURE__ */ jsxs(WizardPanel, {
|
|
10550
10731
|
title: `configure ${descriptor.label} — OAuth`,
|
|
10551
10732
|
children: [
|
|
@@ -21949,11 +22130,26 @@ function AppShell() {
|
|
|
21949
22130
|
signal: ac.signal,
|
|
21950
22131
|
renderCallbackPage: renderDefaultCallbackPage,
|
|
21951
22132
|
hooks: agentRef.current?.hooks,
|
|
21952
|
-
onAuthorizationUrl: (url) =>
|
|
22133
|
+
onAuthorizationUrl: (url) => {
|
|
22134
|
+
dispatchAuth({
|
|
22135
|
+
type: "auth-url",
|
|
22136
|
+
name,
|
|
22137
|
+
url: url.toString()
|
|
22138
|
+
});
|
|
22139
|
+
tryOpenBrowser(url.toString());
|
|
22140
|
+
}
|
|
22141
|
+
});
|
|
22142
|
+
dispatchAuth({
|
|
22143
|
+
type: "auth-success",
|
|
22144
|
+
name
|
|
21953
22145
|
});
|
|
21954
22146
|
rebuildActiveAgent().catch((err) => debugLog("mcp-login: rebuildActiveAgent failed", err));
|
|
21955
22147
|
} catch (err) {
|
|
21956
|
-
if (
|
|
22148
|
+
if (ac.signal.aborted) dispatchAuth({
|
|
22149
|
+
type: "login:cancel",
|
|
22150
|
+
name
|
|
22151
|
+
});
|
|
22152
|
+
else dispatchAuth({
|
|
21957
22153
|
type: "auth-error",
|
|
21958
22154
|
name,
|
|
21959
22155
|
error: errorMessage(err)
|
|
@@ -21986,8 +22182,12 @@ function AppShell() {
|
|
|
21986
22182
|
rebuildActiveAgent
|
|
21987
22183
|
]);
|
|
21988
22184
|
const onCancelLoginMcp = useCallback((name) => {
|
|
22185
|
+
dispatchAuth({
|
|
22186
|
+
type: "login:cancel",
|
|
22187
|
+
name
|
|
22188
|
+
});
|
|
21989
22189
|
mcpLoginAbortsRef.current.get(name)?.abort();
|
|
21990
|
-
}, []);
|
|
22190
|
+
}, [dispatchAuth]);
|
|
21991
22191
|
const onRefreshMcpTools = useCallback(async () => {
|
|
21992
22192
|
const agent = agentRef.current;
|
|
21993
22193
|
if (!agent) return;
|