zidane 5.14.4 → 6.0.0
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-24uU2WDs.js → acp-Irby04Us.js} +7 -8
- package/dist/{acp-24uU2WDs.js.map → acp-Irby04Us.js.map} +1 -1
- package/dist/acp-cli.js +7 -7
- package/dist/acp.d.ts +2 -3
- package/dist/acp.d.ts.map +1 -1
- package/dist/acp.js +1 -1
- package/dist/{agent-Cch2ayTt.js → agent-DkSmGkDM.js} +10871 -5411
- package/dist/agent-DkSmGkDM.js.map +1 -0
- package/dist/agent.d.ts +1 -2
- package/dist/agent.js +2 -2
- package/dist/{anthropic-RGqsJlC8.js → anthropic-CTHsdFG9.js} +46 -6
- package/dist/anthropic-CTHsdFG9.js.map +1 -0
- package/dist/{auth-C-ms5N2x.js → auth-BDSu_0t3.js} +13 -86
- package/dist/auth-BDSu_0t3.js.map +1 -0
- package/dist/chat/pure.d.ts +4 -4
- package/dist/chat/pure.js +4 -3
- package/dist/chat.d.ts +45 -8
- package/dist/chat.d.ts.map +1 -1
- package/dist/chat.js +9 -6
- package/dist/chat.js.map +1 -1
- package/dist/completion-core-CXYSVhZo.js +147 -0
- package/dist/completion-core-CXYSVhZo.js.map +1 -0
- package/dist/{context-breakdown-kO-pDsay.js → context-breakdown-Dzo25N45.js} +2 -23
- package/dist/context-breakdown-Dzo25N45.js.map +1 -0
- package/dist/contexts/daytona.d.ts +3 -3
- package/dist/contexts/daytona.js +1 -1
- package/dist/contexts/docker.d.ts +10 -2
- package/dist/contexts/docker.d.ts.map +1 -1
- package/dist/contexts/docker.js +142 -103
- package/dist/contexts/docker.js.map +1 -1
- package/dist/contexts/e2b.d.ts +2 -2
- package/dist/contexts/e2b.js +1 -1
- package/dist/contexts/sandbox.d.ts +1 -1
- package/dist/contexts/sandbox.js +4 -1
- package/dist/contexts/sandbox.js.map +1 -1
- package/dist/{contexts-Lzf5huID.js → contexts-Vone3qQQ.js} +26 -37
- package/dist/contexts-Vone3qQQ.js.map +1 -0
- package/dist/contexts.d.ts +3 -3
- package/dist/contexts.js +1 -1
- package/dist/errors.d.ts +1 -2
- package/dist/errors.js +1 -1
- package/dist/eval.d.ts +1 -1
- package/dist/eval.js +3 -3
- package/dist/exec-file-size-PXkr-MGA.js +63 -0
- package/dist/exec-file-size-PXkr-MGA.js.map +1 -0
- package/dist/extensions.d.ts +2 -0
- package/dist/extensions.js +2 -0
- package/dist/generate-data-WL_yGVL6.js +324 -0
- package/dist/generate-data-WL_yGVL6.js.map +1 -0
- package/dist/headless.d.ts +1 -1
- package/dist/headless.js +15 -10
- package/dist/headless.js.map +1 -1
- package/dist/index-B8UtiZy7.d.ts +14967 -0
- package/dist/index-B8UtiZy7.d.ts.map +1 -0
- package/dist/{index-B3ciFUZx.d.ts → index-DsPQbyUn.d.ts} +2 -2
- package/dist/index-DsPQbyUn.d.ts.map +1 -0
- package/dist/index.d.ts +6 -12
- package/dist/index.js +20 -43
- package/dist/index.js.map +1 -1
- package/dist/{logger-Ktm-lj1s.js → logger-DBX9uYOw.js} +108 -5
- package/dist/logger-DBX9uYOw.js.map +1 -0
- package/dist/login-DOF8pMVM.js +539 -0
- package/dist/login-DOF8pMVM.js.map +1 -0
- package/dist/{mcp-Dn5W65Lv.js → mcp-CcvuVXB9.js} +2 -2
- package/dist/{mcp-Dn5W65Lv.js.map → mcp-CcvuVXB9.js.map} +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/{messages-YZQLKaz2.js → messages-CwujiS74.js} +4 -4
- package/dist/messages-CwujiS74.js.map +1 -0
- package/dist/{openai-compat-0Y7jcncn.js → openai-compat-BvgYGSR1.js} +7 -6
- package/dist/{openai-compat-0Y7jcncn.js.map → openai-compat-BvgYGSR1.js.map} +1 -1
- package/dist/output/stream-json.d.ts +1 -2
- package/dist/output/stream-json.d.ts.map +1 -1
- package/dist/output/terminal.d.ts +1 -2
- package/dist/output/terminal.d.ts.map +1 -1
- package/dist/{glob-DluQFSw1.js → output-cap-kBUHBlnq.js} +50 -2
- package/dist/output-cap-kBUHBlnq.js.map +1 -0
- package/dist/presets.d.ts +1 -2
- package/dist/presets.js +1 -1
- package/dist/providers/anthropic.d.ts +2 -2
- package/dist/providers/anthropic.js +2 -2
- package/dist/providers/arcee.d.ts +1 -1
- package/dist/providers/arcee.js +1 -1
- package/dist/providers/baseten.d.ts +1 -1
- package/dist/providers/baseten.js +1 -1
- package/dist/providers/cerebras.d.ts +1 -1
- package/dist/providers/cerebras.js +1 -1
- package/dist/providers/local.d.ts +1 -1
- package/dist/providers/local.js +1 -1
- package/dist/providers/openai-compat.d.ts +1 -1
- package/dist/providers/openai-compat.js +1 -1
- package/dist/providers/openai.d.ts +1 -1
- package/dist/providers/openai.js +4 -4
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/openrouter.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/providers/xai.d.ts +1 -1
- package/dist/providers/xai.js +1 -1
- package/dist/{providers-DgmdYGKm.js → providers-z6LDMAr3.js} +3 -3
- package/dist/{providers-DgmdYGKm.js.map → providers-z6LDMAr3.js.map} +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.js +4 -4
- package/dist/{read-state-BymF41Jb.js → read-state-Dq_TXUX1.js} +2 -2
- package/dist/{read-state-BymF41Jb.js.map → read-state-Dq_TXUX1.js.map} +1 -1
- package/dist/{interpolate-BtIgcCuz.js → resolve-Cr8JSL_O.js} +562 -136
- package/dist/resolve-Cr8JSL_O.js.map +1 -0
- package/dist/restate.d.ts +2 -2
- package/dist/restate.js +2 -2
- package/dist/{sandbox-Dgy-m6UF.d.ts → sandbox-CgjG2VvQ.d.ts} +2 -2
- package/dist/{sandbox-Dgy-m6UF.d.ts.map → sandbox-CgjG2VvQ.d.ts.map} +1 -1
- package/dist/session/sqlite.d.ts +1 -1
- package/dist/{session-CqrAFAKJ.js → session-CsdDIPY8.js} +108 -3
- package/dist/session-CsdDIPY8.js.map +1 -0
- package/dist/session.d.ts +2 -2
- package/dist/session.js +3 -3
- package/dist/skills-Bsb0rvWI.js +295 -0
- package/dist/skills-Bsb0rvWI.js.map +1 -0
- package/dist/skills.d.ts +2 -3
- package/dist/skills.js +3 -24
- package/dist/{errors-CkTvg97v.d.ts → timeout-8vSIRjzl.d.ts} +10 -2
- package/dist/timeout-8vSIRjzl.d.ts.map +1 -0
- package/dist/{timeout-kGGSXagK.js → timeout-BDetbuN6.js} +59 -2
- package/dist/timeout-BDetbuN6.js.map +1 -0
- package/dist/tool-formatters-DY0TDb1r.d.ts +378 -0
- package/dist/tool-formatters-DY0TDb1r.d.ts.map +1 -0
- package/dist/tools/fetch-url.d.ts +1 -1
- package/dist/tools/web-search.d.ts +1 -1
- package/dist/tools.d.ts +2 -3
- package/dist/tools.js +3 -4
- package/dist/{transcript-anchors-BHIgMlPq.d.ts → transcript-anchors-Cb-bDbNH.d.ts} +233 -640
- package/dist/transcript-anchors-Cb-bDbNH.d.ts.map +1 -0
- package/dist/{transcript-anchors-DsoBHg43.js → transcript-anchors-DSRPWjiH.js} +391 -961
- package/dist/transcript-anchors-DSRPWjiH.js.map +1 -0
- package/dist/tui.d.ts +335 -11
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +6375 -1892
- package/dist/tui.js.map +1 -1
- package/dist/{turn-operations-Dw02ZoSQ.d.ts → turn-operations-CgMROLsr.d.ts} +29 -4
- package/dist/turn-operations-CgMROLsr.d.ts.map +1 -0
- package/dist/{turn-operations-tG0vuBNc.js → turn-operations-Dq9Kx7m2.js} +321 -81
- package/dist/turn-operations-Dq9Kx7m2.js.map +1 -0
- package/dist/{types-BqdTeBaC.d.ts → types-BDccavwj.d.ts} +42 -1
- package/dist/types-BDccavwj.d.ts.map +1 -0
- package/dist/{types-CyVGdbia.js → types-BIarq1qE.js} +90 -2
- package/dist/types-BIarq1qE.js.map +1 -0
- package/dist/types.d.ts +5 -6
- package/dist/types.js +1 -1
- package/dist/{xai-tPdbAGkq.js → xai-B_e6TOA8.js} +2 -2
- package/dist/{xai-tPdbAGkq.js.map → xai-B_e6TOA8.js.map} +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/CHAT.md +7 -6
- package/docs/EXECUTION_CONTEXT.md +16 -0
- package/docs/EXTENSIONS.md +1063 -0
- package/docs/SKILL.md +16 -0
- package/docs/TUI.md +10 -3
- package/package.json +12 -3
- package/dist/agent-Cch2ayTt.js.map +0 -1
- package/dist/agent-EFWt6uQ_.d.ts +0 -6914
- package/dist/agent-EFWt6uQ_.d.ts.map +0 -1
- package/dist/anthropic-RGqsJlC8.js.map +0 -1
- package/dist/auth-C-ms5N2x.js.map +0 -1
- package/dist/context-breakdown-kO-pDsay.js.map +0 -1
- package/dist/contexts-Lzf5huID.js.map +0 -1
- package/dist/errors-CkTvg97v.d.ts.map +0 -1
- package/dist/glob-DluQFSw1.js.map +0 -1
- package/dist/glob-shell-rJMoCIGb.js +0 -21
- package/dist/glob-shell-rJMoCIGb.js.map +0 -1
- package/dist/index-B3ciFUZx.d.ts.map +0 -1
- package/dist/index-BtzE3Uaq.d.ts +0 -2738
- package/dist/index-BtzE3Uaq.d.ts.map +0 -1
- package/dist/index-tSzOaWNt.d.ts +0 -316
- package/dist/index-tSzOaWNt.d.ts.map +0 -1
- package/dist/interpolate-BtIgcCuz.js.map +0 -1
- package/dist/logger-DGiGf7DW.d.ts +0 -102
- package/dist/logger-DGiGf7DW.d.ts.map +0 -1
- package/dist/logger-Ktm-lj1s.js.map +0 -1
- package/dist/login-CCA-1lgK.js +0 -267
- package/dist/login-CCA-1lgK.js.map +0 -1
- package/dist/messages-YZQLKaz2.js.map +0 -1
- package/dist/policy-DcGlpaNs.d.ts +0 -129
- package/dist/policy-DcGlpaNs.d.ts.map +0 -1
- package/dist/presets-BQWYMHdd.js +0 -114
- package/dist/presets-BQWYMHdd.js.map +0 -1
- package/dist/run-summary-DkN8KsHM.d.ts +0 -249
- package/dist/run-summary-DkN8KsHM.d.ts.map +0 -1
- package/dist/session-CqrAFAKJ.js.map +0 -1
- package/dist/shell-quote-BmnhZmdM.js +0 -33
- package/dist/shell-quote-BmnhZmdM.js.map +0 -1
- package/dist/skills.js.map +0 -1
- package/dist/timeout-CpFm0jJ6.d.ts +0 -10
- package/dist/timeout-CpFm0jJ6.d.ts.map +0 -1
- package/dist/timeout-kGGSXagK.js.map +0 -1
- package/dist/tool-formatters-C6iLgc9f.d.ts +0 -1454
- package/dist/tool-formatters-C6iLgc9f.d.ts.map +0 -1
- package/dist/tools-CCd8e-HT.js +0 -1739
- package/dist/tools-CCd8e-HT.js.map +0 -1
- package/dist/transcript-anchors-BHIgMlPq.d.ts.map +0 -1
- package/dist/transcript-anchors-DsoBHg43.js.map +0 -1
- package/dist/turn-operations-Dw02ZoSQ.d.ts.map +0 -1
- package/dist/turn-operations-tG0vuBNc.js.map +0 -1
- package/dist/types-BqdTeBaC.d.ts.map +0 -1
- package/dist/types-CyVGdbia.js.map +0 -1
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { r as AgentHooks } from "./agent-EFWt6uQ_.js";
|
|
2
|
-
import { Hookable } from "hookable";
|
|
3
|
-
|
|
4
|
-
//#region src/logger.d.ts
|
|
5
|
-
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
6
|
-
interface LogRecord {
|
|
7
|
-
level: LogLevel;
|
|
8
|
-
/** Unix ms — set by `Logger` at emit time. */
|
|
9
|
-
timestamp: number;
|
|
10
|
-
/** Free-form message. Sinks render this as the human-facing line. */
|
|
11
|
-
message: string;
|
|
12
|
-
/** Structured fields. Correlation ids land here automatically. */
|
|
13
|
-
attrs: Record<string, unknown>;
|
|
14
|
-
}
|
|
15
|
-
interface LogSink {
|
|
16
|
-
emit: (record: LogRecord) => void;
|
|
17
|
-
}
|
|
18
|
-
interface Logger {
|
|
19
|
-
debug: (message: string, attrs?: Record<string, unknown>) => void;
|
|
20
|
-
info: (message: string, attrs?: Record<string, unknown>) => void;
|
|
21
|
-
warn: (message: string, attrs?: Record<string, unknown>) => void;
|
|
22
|
-
error: (message: string, attrs?: Record<string, unknown>) => void;
|
|
23
|
-
/**
|
|
24
|
-
* Returns a child logger that prepends the given attributes onto every
|
|
25
|
-
* subsequent emit. Equivalent to `pino.child` / `winston.child`. The
|
|
26
|
-
* parent and child share the same sink — children are zero-cost.
|
|
27
|
-
*/
|
|
28
|
-
with: (extra: Record<string, unknown>) => Logger;
|
|
29
|
-
/**
|
|
30
|
-
* Inspectable baseline attributes — handy for tests and for hook
|
|
31
|
-
* handlers that want to clone-with-extra without recursing.
|
|
32
|
-
*/
|
|
33
|
-
readonly baseAttributes: Readonly<Record<string, unknown>>;
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Build a Logger from a sink. Stateless and cheap; create one per agent
|
|
37
|
-
* (or per app) and use `.with()` to attach correlation ids per-call.
|
|
38
|
-
*/
|
|
39
|
-
declare function createLogger(sink: LogSink, baseAttributes?: Readonly<Record<string, unknown>>): Logger;
|
|
40
|
-
interface ConsoleSinkOptions {
|
|
41
|
-
/**
|
|
42
|
-
* Minimum level to emit. Defaults to `'info'` — `debug` is dropped so
|
|
43
|
-
* the harness's lifecycle logging is not noisy by default. Set to
|
|
44
|
-
* `'debug'` to see every event.
|
|
45
|
-
*/
|
|
46
|
-
minLevel?: LogLevel;
|
|
47
|
-
/** Custom output stream. Defaults to `process.stderr` so logs don't pollute stdout. */
|
|
48
|
-
stream?: {
|
|
49
|
-
write: (chunk: string) => void;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Human-readable terminal sink. Renders each record as
|
|
54
|
-
* `<ISO timestamp> <LEVEL> <message> <attrs as kv pairs>`.
|
|
55
|
-
*
|
|
56
|
-
* Honors `process.stderr` by default so log lines don't interleave with
|
|
57
|
-
* the agent's stdout-bound output (chat responses, JSON results).
|
|
58
|
-
*/
|
|
59
|
-
declare function consoleSink(options?: ConsoleSinkOptions): LogSink;
|
|
60
|
-
/**
|
|
61
|
-
* One-JSON-object-per-line sink. Suitable for piping into log aggregators
|
|
62
|
-
* (Datadog Agent, Fluent Bit, Loki, Vector) that expect JSONL.
|
|
63
|
-
*/
|
|
64
|
-
declare function jsonSink(options?: ConsoleSinkOptions): LogSink;
|
|
65
|
-
interface LoggingHooksOptions {
|
|
66
|
-
logger: Logger;
|
|
67
|
-
/**
|
|
68
|
-
* Minimum interesting level for harness-emitted lines. Default `'info'`.
|
|
69
|
-
* Set to `'debug'` to see every tool dispatch / stream event. Set to
|
|
70
|
-
* `'warn'` to mute the chatty ones and only see failures + budgets.
|
|
71
|
-
*/
|
|
72
|
-
level?: LogLevel;
|
|
73
|
-
/**
|
|
74
|
-
* When true (default), lifecycle events (`agent:start`, `turn:before`,
|
|
75
|
-
* `tool:before`, `mcp:bootstrap:start`) emit at `debug` level so they
|
|
76
|
-
* stay quiet by default. Set false to mute them entirely regardless of
|
|
77
|
-
* the configured minimum level — useful when piping into a tracer
|
|
78
|
-
* that already captures lifecycle.
|
|
79
|
-
*/
|
|
80
|
-
includeLifecycle?: boolean;
|
|
81
|
-
}
|
|
82
|
-
interface LoggingHookSet {
|
|
83
|
-
install: (hooks: Hookable<AgentHooks>) => () => void;
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Install a bundle of hook handlers that emit a structured line per
|
|
87
|
-
* relevant lifecycle event, automatically attaching correlation ids
|
|
88
|
-
* (`runId`, `turnId`, `callId`, `childId`, `depth`, `agentName`).
|
|
89
|
-
*
|
|
90
|
-
* @example
|
|
91
|
-
* ```ts
|
|
92
|
-
* const logger = createLogger(consoleSink({ minLevel: 'debug' }), { service: 'tui' })
|
|
93
|
-
* const lh = createLoggingHooks({ logger })
|
|
94
|
-
* const uninstall = lh.install(agent.hooks)
|
|
95
|
-
* try { await agent.run({ prompt }) }
|
|
96
|
-
* finally { uninstall() }
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
declare function createLoggingHooks(options: LoggingHooksOptions): LoggingHookSet;
|
|
100
|
-
//#endregion
|
|
101
|
-
export { Logger as a, consoleSink as c, jsonSink as d, LogSink as i, createLogger as l, LogLevel as n, LoggingHookSet as o, LogRecord as r, LoggingHooksOptions as s, ConsoleSinkOptions as t, createLoggingHooks as u };
|
|
102
|
-
//# sourceMappingURL=logger-DGiGf7DW.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger-DGiGf7DW.d.ts","names":[],"sources":["../src/logger.ts"],"mappings":";;;;KAgCY,QAAA;AAAA,UAEK,SAAA;EACf,KAAA,EAAO,QAAA;EAUiB;EARxB,SAAA;EAWqB;EATrB,OAAA;EAUiC;EARjC,KAAA,EAAO,MAAM;AAAA;AAAA,UAGE,OAAA;EACf,IAAA,GAAO,MAAA,EAAQ,SAAS;AAAA;AAAA,UAGT,MAAA;EACf,KAAA,GAAQ,OAAA,UAAiB,KAAA,GAAQ,MAAA;EACjC,IAAA,GAAO,OAAA,UAAiB,KAAA,GAAQ,MAAA;EAChC,IAAA,GAAO,OAAA,UAAiB,KAAA,GAAQ,MAAA;EAChC,KAAA,GAAQ,OAAA,UAAiB,KAAA,GAAQ,MAAA;EAHzB;;;;;EASR,IAAA,GAAO,KAAA,EAAO,MAAA,sBAA4B,MAAA;EARlB;;;;EAAA,SAaf,cAAA,EAAgB,QAAA,CAAS,MAAA;AAAA;;;;;iBAWpB,YAAA,CACd,IAAA,EAAM,OAAA,EACN,cAAA,GAAgB,QAAA,CAAS,MAAA,qBACxB,MAAA;AAAA,UA6Bc,kBAAA;EAhD2B;;;;;EAsD1C,QAAA,GAAW,QAAQ;EAtCL;EAwCd,MAAA;IAAW,KAAA,GAAQ,KAAA;EAAA;AAAA;;;;;;;;iBAiBL,WAAA,CAAY,OAAA,GAAS,kBAAA,GAA0B,OAAO;;;;AAtD7D;iBA2EO,QAAA,CAAS,OAAA,GAAS,kBAAA,GAA0B,OAAO;AAAA,UAqBlD,mBAAA;EACf,MAAA,EAAQ,MAAA;EA9DW;;;;;EAoEnB,KAAA,GAAQ,QAAQ;EAlEgB;AAAA;AAiBlC;;;;;EAyDE,gBAAA;AAAA;AAAA,UAGe,cAAA;EACf,OAAA,GAAU,KAAA,EAAO,QAAQ,CAAC,UAAA;AAAA;;;;;;;;AAxCuC;AAqBnE;;;;;;iBAoCgB,kBAAA,CAAmB,OAAA,EAAS,mBAAA,GAAsB,cAAc"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"logger-Ktm-lj1s.js","names":[],"sources":["../src/logger.ts"],"sourcesContent":["/**\n * Structured logging primitive with auto-correlation.\n *\n * Every consumer of the harness used to roll its own logger; the TUI has\n * one, downstream apps have theirs, observability backends each want a\n * slightly different shape. This module ships the smallest reasonable\n * shared surface:\n *\n * - {@link Logger} — minimal level-tagged interface (`debug` / `info` /\n * `warn` / `error`) plus a `with(...)` method that returns a child\n * logger carrying baseline attributes (`runId`, `turnId`, `callId`,\n * `childId`, `depth`, anything you want).\n * - {@link createLogger} — builds a Logger from a {@link LogSink}.\n * - {@link consoleSink} / {@link jsonSink} — built-in sinks that cover\n * the common cases (human-readable to a terminal vs. one JSON object\n * per line to a log aggregator).\n * - {@link createLoggingHooks} — installs Logger-attached hook handlers\n * on an agent so every lifecycle event lands on the sink with the\n * right correlation ids already stamped in.\n *\n * Hosts that already own a logger (pino, winston, structlog binding,\n * platform-native) plug it in via a custom {@link LogSink} — the helper\n * itself does no I/O.\n */\n\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from './agent'\n\n// ---------------------------------------------------------------------------\n// Core types\n// ---------------------------------------------------------------------------\n\nexport type LogLevel = 'debug' | 'info' | 'warn' | 'error'\n\nexport interface LogRecord {\n level: LogLevel\n /** Unix ms — set by `Logger` at emit time. */\n timestamp: number\n /** Free-form message. Sinks render this as the human-facing line. */\n message: string\n /** Structured fields. Correlation ids land here automatically. */\n attrs: Record<string, unknown>\n}\n\nexport interface LogSink {\n emit: (record: LogRecord) => void\n}\n\nexport interface Logger {\n debug: (message: string, attrs?: Record<string, unknown>) => void\n info: (message: string, attrs?: Record<string, unknown>) => void\n warn: (message: string, attrs?: Record<string, unknown>) => void\n error: (message: string, attrs?: Record<string, unknown>) => void\n /**\n * Returns a child logger that prepends the given attributes onto every\n * subsequent emit. Equivalent to `pino.child` / `winston.child`. The\n * parent and child share the same sink — children are zero-cost.\n */\n with: (extra: Record<string, unknown>) => Logger\n /**\n * Inspectable baseline attributes — handy for tests and for hook\n * handlers that want to clone-with-extra without recursing.\n */\n readonly baseAttributes: Readonly<Record<string, unknown>>\n}\n\n// ---------------------------------------------------------------------------\n// createLogger\n// ---------------------------------------------------------------------------\n\n/**\n * Build a Logger from a sink. Stateless and cheap; create one per agent\n * (or per app) and use `.with()` to attach correlation ids per-call.\n */\nexport function createLogger(\n sink: LogSink,\n baseAttributes: Readonly<Record<string, unknown>> = {},\n): Logger {\n function emit(level: LogLevel, message: string, attrs?: Record<string, unknown>): void {\n try {\n sink.emit({\n level,\n timestamp: Date.now(),\n message,\n attrs: attrs ? { ...baseAttributes, ...attrs } : { ...baseAttributes },\n })\n }\n catch {\n // Sinks should never crash the run.\n }\n }\n\n return {\n debug: (m, a) => emit('debug', m, a),\n info: (m, a) => emit('info', m, a),\n warn: (m, a) => emit('warn', m, a),\n error: (m, a) => emit('error', m, a),\n with: extra => createLogger(sink, { ...baseAttributes, ...extra }),\n baseAttributes,\n }\n}\n\n// ---------------------------------------------------------------------------\n// Built-in sinks\n// ---------------------------------------------------------------------------\n\nexport interface ConsoleSinkOptions {\n /**\n * Minimum level to emit. Defaults to `'info'` — `debug` is dropped so\n * the harness's lifecycle logging is not noisy by default. Set to\n * `'debug'` to see every event.\n */\n minLevel?: LogLevel\n /** Custom output stream. Defaults to `process.stderr` so logs don't pollute stdout. */\n stream?: { write: (chunk: string) => void }\n}\n\nconst LEVEL_ORDER: Record<LogLevel, number> = {\n debug: 0,\n info: 1,\n warn: 2,\n error: 3,\n}\n\n/**\n * Human-readable terminal sink. Renders each record as\n * `<ISO timestamp> <LEVEL> <message> <attrs as kv pairs>`.\n *\n * Honors `process.stderr` by default so log lines don't interleave with\n * the agent's stdout-bound output (chat responses, JSON results).\n */\nexport function consoleSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n const ts = new Date(record.timestamp).toISOString()\n const kv = Object.entries(record.attrs)\n .filter(([, v]) => v !== undefined)\n .map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)\n .join(' ')\n stream.write(`${ts} ${record.level.toUpperCase().padEnd(5)} ${record.message}${kv ? ` ${kv}` : ''}\\n`)\n },\n }\n}\n\n/**\n * One-JSON-object-per-line sink. Suitable for piping into log aggregators\n * (Datadog Agent, Fluent Bit, Loki, Vector) that expect JSONL.\n */\nexport function jsonSink(options: ConsoleSinkOptions = {}): LogSink {\n const min = LEVEL_ORDER[options.minLevel ?? 'info']\n const stream = options.stream ?? process.stderr\n return {\n emit(record) {\n if (LEVEL_ORDER[record.level] < min)\n return\n try {\n stream.write(`${JSON.stringify(record)}\\n`)\n }\n catch {\n // Non-serializable attr (cycle, BigInt, etc.). Skip rather than throw.\n }\n },\n }\n}\n\n// ---------------------------------------------------------------------------\n// createLoggingHooks\n// ---------------------------------------------------------------------------\n\nexport interface LoggingHooksOptions {\n logger: Logger\n /**\n * Minimum interesting level for harness-emitted lines. Default `'info'`.\n * Set to `'debug'` to see every tool dispatch / stream event. Set to\n * `'warn'` to mute the chatty ones and only see failures + budgets.\n */\n level?: LogLevel\n /**\n * When true (default), lifecycle events (`agent:start`, `turn:before`,\n * `tool:before`, `mcp:bootstrap:start`) emit at `debug` level so they\n * stay quiet by default. Set false to mute them entirely regardless of\n * the configured minimum level — useful when piping into a tracer\n * that already captures lifecycle.\n */\n includeLifecycle?: boolean\n}\n\nexport interface LoggingHookSet {\n install: (hooks: Hookable<AgentHooks>) => () => void\n}\n\n/**\n * Install a bundle of hook handlers that emit a structured line per\n * relevant lifecycle event, automatically attaching correlation ids\n * (`runId`, `turnId`, `callId`, `childId`, `depth`, `agentName`).\n *\n * @example\n * ```ts\n * const logger = createLogger(consoleSink({ minLevel: 'debug' }), { service: 'tui' })\n * const lh = createLoggingHooks({ logger })\n * const uninstall = lh.install(agent.hooks)\n * try { await agent.run({ prompt }) }\n * finally { uninstall() }\n * ```\n */\nexport function createLoggingHooks(options: LoggingHooksOptions): LoggingHookSet {\n const root = options.logger\n const includeLifecycle = options.includeLifecycle ?? true\n const minLevel = LEVEL_ORDER[options.level ?? 'info']\n\n /**\n * Wrap a Logger so emissions below `minLevel` are dropped before they\n * hit the sink. Lets us gate harness chatter without forcing every\n * `LogSink` to re-implement level filtering. Preserves `.with()`\n * composition (children inherit the filter).\n */\n function gateLevel(logger: Logger): Logger {\n const skip = (level: LogLevel): boolean => LEVEL_ORDER[level] < minLevel\n const wrap = (l: Logger): Logger => ({\n debug: (m, a) => {\n if (!skip('debug'))\n l.debug(m, a)\n },\n info: (m, a) => {\n if (!skip('info'))\n l.info(m, a)\n },\n warn: (m, a) => {\n if (!skip('warn'))\n l.warn(m, a)\n },\n error: (m, a) => {\n if (!skip('error'))\n l.error(m, a)\n },\n with: extra => wrap(l.with(extra)),\n baseAttributes: l.baseAttributes,\n })\n return wrap(logger)\n }\n\n return {\n install(hooks: Hookable<AgentHooks>): () => void {\n const unregisters: Array<() => void> = []\n // Per-install loggers, refreshed on `agent:start` / `turn:before`.\n // Scoped INSIDE install() so concurrent installs across different\n // agents don't share state — each call to `install()` gets its own\n // attribution state.\n const gatedRoot = gateLevel(root)\n let runLogger = gatedRoot\n let turnLogger = gatedRoot\n\n // ---- Agent lifecycle --------------------------------------------\n\n unregisters.push(hooks.hook('agent:start', (ctx) => {\n runLogger = gatedRoot.with({\n runId: ctx.runId,\n ...(ctx.parentRunId ? { parentRunId: ctx.parentRunId } : {}),\n depth: ctx.depth,\n ...(ctx.agentName ? { agentName: ctx.agentName } : {}),\n })\n turnLogger = runLogger\n if (includeLifecycle)\n runLogger.debug('agent run started')\n }))\n\n unregisters.push(hooks.hook('agent:done', (stats) => {\n runLogger.info('agent run completed', {\n turns: stats.turns,\n totalIn: stats.totalIn,\n totalOut: stats.totalOut,\n ...(typeof stats.cost === 'number' ? { cost: stats.cost } : {}),\n elapsedMs: stats.elapsed,\n ...(typeof stats.timeTillFirstTokenMs === 'number' ? { ttftMs: stats.timeTillFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('agent:abort', () => {\n runLogger.warn('agent run aborted')\n }))\n\n // ---- Turn / stream ---------------------------------------------\n\n unregisters.push(hooks.hook('turn:before', (ctx) => {\n turnLogger = runLogger.with({ turnId: ctx.turnId, turn: ctx.turn })\n if (includeLifecycle)\n turnLogger.debug('turn started')\n }))\n\n unregisters.push(hooks.hook('turn:after', (ctx) => {\n turnLogger.debug('turn ended', {\n inputTokens: ctx.usage.input,\n outputTokens: ctx.usage.output,\n ...(ctx.usage.finishReason ? { finishReason: ctx.usage.finishReason } : {}),\n ...(ctx.usage.modelId ? { modelId: ctx.usage.modelId } : {}),\n ...(typeof ctx.usage.timeToFirstTokenMs === 'number' ? { ttftMs: ctx.usage.timeToFirstTokenMs } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('stream:error', (ctx) => {\n turnLogger.error('stream error', {\n message: ctx.err instanceof Error ? ctx.err.message : String(ctx.err),\n ...(ctx.statusCode !== undefined ? { statusCode: ctx.statusCode } : {}),\n ...(ctx.requestId !== undefined ? { requestId: ctx.requestId } : {}),\n })\n }))\n\n // ---- Tool calls -------------------------------------------------\n\n unregisters.push(hooks.hook('tool:before', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool started', {\n toolName: ctx.name,\n displayName: ctx.displayName,\n callId: ctx.callId,\n })\n }))\n\n unregisters.push(hooks.hook('tool:after', (ctx) => {\n if (!includeLifecycle)\n return\n turnLogger.debug('tool ended', {\n toolName: ctx.name,\n callId: ctx.callId,\n outputBytes: ctx.outputBytes,\n })\n }))\n\n unregisters.push(hooks.hook('tool:error', (ctx) => {\n turnLogger.error('tool error', {\n toolName: ctx.name,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('tool:dispatched', (ctx) => {\n // Successful + gate-substitute paths are routine — only log at\n // debug level. The three \"something refused / went wrong\" paths\n // (gate-block, unknown tool, invalid input) land at warn so they\n // surface in default-config dashboards without forcing debug.\n const isAnomaly = ctx.outcome === 'gate-block'\n || ctx.outcome === 'unknown'\n || ctx.outcome === 'invalid-input'\n if (!isAnomaly && !includeLifecycle)\n return\n const lvl: LogLevel = isAnomaly ? 'warn' : 'debug'\n turnLogger[lvl]('tool dispatched', {\n toolName: ctx.name,\n callId: ctx.callId,\n outcome: ctx.outcome,\n ...(ctx.reason ? { reason: ctx.reason } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('validation:reject', (ctx) => {\n turnLogger.warn('tool input rejected', {\n toolName: ctx.name,\n callId: ctx.callId,\n reason: ctx.reason,\n })\n }))\n\n // ---- Budgets ----------------------------------------------------\n\n unregisters.push(hooks.hook('budget:exceeded', (ctx) => {\n turnLogger.warn('byte budget exceeded', {\n bytes: ctx.bytes,\n budget: ctx.budget,\n })\n }))\n\n unregisters.push(hooks.hook('tool-budget:exceeded', (ctx) => {\n turnLogger.warn('tool budget exceeded', {\n toolName: ctx.tool,\n count: ctx.count,\n max: ctx.max,\n mode: ctx.mode,\n })\n }))\n\n // ---- MCP --------------------------------------------------------\n\n unregisters.push(hooks.hook('mcp:bootstrap:end', (ctx) => {\n if (ctx.ok) {\n if (includeLifecycle) {\n runLogger.debug('mcp bootstrap ok', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n toolCount: ctx.toolCount,\n ...(ctx.lazy ? { lazy: true } : {}),\n ...(ctx.cached ? { cached: true } : {}),\n })\n }\n }\n else {\n runLogger.warn('mcp bootstrap failed', {\n server: ctx.name,\n transport: ctx.transport,\n durationMs: ctx.durationMs,\n message: ctx.error.message,\n })\n }\n }))\n\n unregisters.push(hooks.hook('mcp:error', (ctx) => {\n runLogger.error('mcp error', {\n server: ctx.name,\n message: ctx.error.message,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:warn', (ctx) => {\n runLogger.warn('mcp warn', {\n server: ctx.name,\n message: ctx.message,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:auth:required', (ctx) => {\n runLogger.warn('mcp auth required', {\n server: ctx.name,\n transport: ctx.transport,\n reason: ctx.reason,\n })\n }))\n\n unregisters.push(hooks.hook('mcp:tool:error', (ctx) => {\n turnLogger.error('mcp tool error', {\n server: ctx.server,\n tool: ctx.displayName,\n callId: ctx.callId,\n message: ctx.error.message,\n })\n }))\n\n // ---- Spawn ------------------------------------------------------\n\n unregisters.push(hooks.hook('spawn:before', (ctx) => {\n if (!includeLifecycle)\n return\n runLogger.debug('spawn started', {\n childId: ctx.id,\n depth: ctx.depth,\n })\n }))\n\n unregisters.push(hooks.hook('spawn:complete', (ctx) => {\n runLogger.info('spawn completed', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n status: ctx.status ?? 'completed',\n turns: ctx.stats.turns,\n totalIn: ctx.stats.totalIn,\n totalOut: ctx.stats.totalOut,\n ...(typeof ctx.stats.cost === 'number' ? { cost: ctx.stats.cost } : {}),\n })\n }))\n\n unregisters.push(hooks.hook('spawn:error', (ctx) => {\n runLogger.error('spawn error', {\n childId: ctx.id,\n ...(ctx.depth ? { depth: ctx.depth } : {}),\n message: ctx.error.message,\n })\n }))\n\n // -----------------------------------------------------------------\n // Disposal\n // -----------------------------------------------------------------\n\n let disposed = false\n return function uninstall() {\n if (disposed)\n return\n disposed = true\n for (const un of unregisters) {\n try {\n un()\n }\n catch { /* ignore */ }\n }\n }\n },\n }\n}\n"],"mappings":"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"}
|
package/dist/login-CCA-1lgK.js
DELETED
|
@@ -1,267 +0,0 @@
|
|
|
1
|
-
import { c as createTolerantClient, l as sseToJsonFetchIfNeeded, u as McpOAuthProvider } from "./mcp-Dn5W65Lv.js";
|
|
2
|
-
import { createServer } from "node:http";
|
|
3
|
-
//#region src/mcp/oauth-callback.ts
|
|
4
|
-
const DEFAULT_PATH = "/callback";
|
|
5
|
-
const DEFAULT_HOST = "127.0.0.1";
|
|
6
|
-
const SUCCESS_HTML = `<!doctype html>
|
|
7
|
-
<html><head><meta charset="utf-8"><title>Logged in</title>
|
|
8
|
-
<style>body{font:14px/1.5 -apple-system,system-ui,sans-serif;margin:6rem auto;max-width:28rem;text-align:center;color:#1d1d1f}.ok{color:#1f8a4c;font-weight:600}</style>
|
|
9
|
-
</head><body>
|
|
10
|
-
<p class="ok">Logged in.</p>
|
|
11
|
-
<p>You can close this tab and return to the terminal.</p>
|
|
12
|
-
</body></html>`;
|
|
13
|
-
function errorHtml(message) {
|
|
14
|
-
return `<!doctype html>
|
|
15
|
-
<html><head><meta charset="utf-8"><title>Login failed</title>
|
|
16
|
-
<style>body{font:14px/1.5 -apple-system,system-ui,sans-serif;margin:6rem auto;max-width:28rem;text-align:center;color:#1d1d1f}.err{color:#c43c2c;font-weight:600}</style>
|
|
17
|
-
</head><body>
|
|
18
|
-
<p class="err">Login failed.</p>
|
|
19
|
-
<p>${message.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">")}</p>
|
|
20
|
-
<p>You can close this tab and return to the terminal.</p>
|
|
21
|
-
</body></html>`;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Start a one-shot OAuth callback server. The returned handle's `redirectUri`
|
|
25
|
-
* should be passed to the authorization server as the `redirect_uri` query
|
|
26
|
-
* parameter; `promise` resolves once the user finishes the browser flow.
|
|
27
|
-
*
|
|
28
|
-
* Always `await handle.close()` in a `finally` block — even on success, the
|
|
29
|
-
* server stays open until told to shut down (so it can serve the
|
|
30
|
-
* "you can close this tab" page).
|
|
31
|
-
*/
|
|
32
|
-
async function startOAuthCallback(opts = {}) {
|
|
33
|
-
const path = opts.path ?? DEFAULT_PATH;
|
|
34
|
-
const host = opts.host ?? DEFAULT_HOST;
|
|
35
|
-
const port = opts.port ?? 0;
|
|
36
|
-
if (!path.startsWith("/")) throw new Error(`OAuth callback path must start with "/" (got: ${JSON.stringify(path)})`);
|
|
37
|
-
if (opts.signal?.aborted) throw new Error("OAuth callback aborted");
|
|
38
|
-
let resolveResult;
|
|
39
|
-
let rejectResult;
|
|
40
|
-
const promise = new Promise((resolve, reject) => {
|
|
41
|
-
resolveResult = resolve;
|
|
42
|
-
rejectResult = reject;
|
|
43
|
-
});
|
|
44
|
-
promise.catch(() => {});
|
|
45
|
-
let settled = false;
|
|
46
|
-
const resolveOnce = (value) => {
|
|
47
|
-
if (settled) return;
|
|
48
|
-
settled = true;
|
|
49
|
-
resolveResult(value);
|
|
50
|
-
};
|
|
51
|
-
const rejectOnce = (error) => {
|
|
52
|
-
if (settled) return;
|
|
53
|
-
settled = true;
|
|
54
|
-
rejectResult(error);
|
|
55
|
-
};
|
|
56
|
-
const server = createServer((req, res) => {
|
|
57
|
-
const url = new URL(req.url ?? "/", `http://${host}`);
|
|
58
|
-
if (url.pathname !== path) {
|
|
59
|
-
res.writeHead(404, { "content-type": "text/plain" });
|
|
60
|
-
res.end("Not Found");
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
const htmlHeaders = {
|
|
64
|
-
"content-type": "text/html; charset=utf-8",
|
|
65
|
-
"cache-control": "no-store"
|
|
66
|
-
};
|
|
67
|
-
const error = url.searchParams.get("error");
|
|
68
|
-
if (error) {
|
|
69
|
-
const desc = url.searchParams.get("error_description") ?? error;
|
|
70
|
-
res.writeHead(400, htmlHeaders);
|
|
71
|
-
res.end(errorHtml(desc));
|
|
72
|
-
rejectOnce(/* @__PURE__ */ new Error(`OAuth authorization failed: ${desc}`));
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const code = url.searchParams.get("code");
|
|
76
|
-
if (!code) {
|
|
77
|
-
res.writeHead(400, { "content-type": "text/plain" });
|
|
78
|
-
res.end("Missing \"code\" query parameter.");
|
|
79
|
-
return;
|
|
80
|
-
}
|
|
81
|
-
const state = url.searchParams.get("state") ?? void 0;
|
|
82
|
-
res.writeHead(200, htmlHeaders);
|
|
83
|
-
res.end(SUCCESS_HTML);
|
|
84
|
-
resolveOnce({
|
|
85
|
-
code,
|
|
86
|
-
state
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
server.on("error", (err) => {
|
|
90
|
-
rejectOnce(err instanceof Error ? err : new Error(String(err)));
|
|
91
|
-
});
|
|
92
|
-
await new Promise((resolve, reject) => {
|
|
93
|
-
server.once("error", reject);
|
|
94
|
-
server.listen(port, host, () => {
|
|
95
|
-
server.off("error", reject);
|
|
96
|
-
resolve();
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
const addr = server.address();
|
|
100
|
-
if (!addr || typeof addr === "string") {
|
|
101
|
-
server.close();
|
|
102
|
-
throw new Error("OAuth callback server did not bind to a TCP port");
|
|
103
|
-
}
|
|
104
|
-
let closing;
|
|
105
|
-
let closeServer;
|
|
106
|
-
const onAbort = () => {
|
|
107
|
-
rejectOnce(/* @__PURE__ */ new Error("OAuth callback aborted"));
|
|
108
|
-
closeServer();
|
|
109
|
-
};
|
|
110
|
-
closeServer = () => {
|
|
111
|
-
if (closing) return closing;
|
|
112
|
-
closing = new Promise((resolve) => {
|
|
113
|
-
server.closeAllConnections?.();
|
|
114
|
-
server.close(() => {
|
|
115
|
-
opts.signal?.removeEventListener("abort", onAbort);
|
|
116
|
-
if (opts.signal?.aborted) rejectOnce(/* @__PURE__ */ new Error("OAuth callback aborted"));
|
|
117
|
-
else rejectOnce(/* @__PURE__ */ new Error("OAuth callback server closed"));
|
|
118
|
-
resolve();
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
return closing;
|
|
122
|
-
};
|
|
123
|
-
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
124
|
-
return {
|
|
125
|
-
redirectUri: `http://${host}:${addr.port}${path}`,
|
|
126
|
-
promise,
|
|
127
|
-
close: closeServer
|
|
128
|
-
};
|
|
129
|
-
}
|
|
130
|
-
//#endregion
|
|
131
|
-
//#region src/mcp/login.ts
|
|
132
|
-
const DEFAULT_LOGIN_TIMEOUT_MS = 5 * 6e4;
|
|
133
|
-
/**
|
|
134
|
-
* Run the full interactive OAuth flow for `config`. Only supports `sse` and
|
|
135
|
-
* `streamable-http` transports — `stdio` MCP servers don't speak OAuth.
|
|
136
|
-
*
|
|
137
|
-
* Throws on:
|
|
138
|
-
* - Wrong transport.
|
|
139
|
-
* - Abort signal.
|
|
140
|
-
* - Browser-side error (user denied, server rejected, etc.).
|
|
141
|
-
* - Code exchange failure.
|
|
142
|
-
* - Post-exchange connect failure.
|
|
143
|
-
*
|
|
144
|
-
* Always closes the loopback callback server before returning, success or
|
|
145
|
-
* failure.
|
|
146
|
-
*/
|
|
147
|
-
async function loginMcpServer(config, options) {
|
|
148
|
-
if (config.transport !== "sse" && config.transport !== "streamable-http") throw new Error(`MCP OAuth: cannot login for transport "${config.transport}" — only sse / streamable-http are supported`);
|
|
149
|
-
if (!config.url) throw new Error(`MCP OAuth: server "${config.name}" is missing a url`);
|
|
150
|
-
const hooks = options.hooks;
|
|
151
|
-
let callback;
|
|
152
|
-
try {
|
|
153
|
-
callback = await startOAuthCallback({
|
|
154
|
-
signal: options.signal,
|
|
155
|
-
path: options.callbackPath
|
|
156
|
-
});
|
|
157
|
-
const handle = callback;
|
|
158
|
-
const provider = new McpOAuthProvider({
|
|
159
|
-
name: config.name,
|
|
160
|
-
store: options.store,
|
|
161
|
-
redirectUri: handle.redirectUri,
|
|
162
|
-
clientName: options.clientName,
|
|
163
|
-
scope: options.scope,
|
|
164
|
-
onAuthorizationUrl: async (url) => {
|
|
165
|
-
await hooks?.callHook("mcp:auth:url", {
|
|
166
|
-
name: config.name,
|
|
167
|
-
url: url.toString()
|
|
168
|
-
});
|
|
169
|
-
await options.onAuthorizationUrl?.(url);
|
|
170
|
-
}
|
|
171
|
-
});
|
|
172
|
-
const transport = await createInteractiveTransport(config, provider);
|
|
173
|
-
const client = await createTolerantClient({
|
|
174
|
-
name: "zidane",
|
|
175
|
-
version: "1.0.0"
|
|
176
|
-
});
|
|
177
|
-
let needsAuth = false;
|
|
178
|
-
try {
|
|
179
|
-
await client.connect(transport);
|
|
180
|
-
} catch (err) {
|
|
181
|
-
if (!isUnauthorizedError(err)) {
|
|
182
|
-
await client.close().catch(() => {});
|
|
183
|
-
throw err;
|
|
184
|
-
}
|
|
185
|
-
needsAuth = true;
|
|
186
|
-
}
|
|
187
|
-
if (needsAuth) {
|
|
188
|
-
const { code } = await raceLoginCallback(handle, options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS, options.signal);
|
|
189
|
-
await transport.finishAuth(code);
|
|
190
|
-
await transport.close().catch(() => {});
|
|
191
|
-
const freshTransport = await createInteractiveTransport(config, provider);
|
|
192
|
-
await client.connect(freshTransport);
|
|
193
|
-
}
|
|
194
|
-
const { tools } = await client.listTools();
|
|
195
|
-
await client.close();
|
|
196
|
-
const tokens = provider.tokens();
|
|
197
|
-
if (!tokens) throw new Error(`MCP OAuth: login for "${config.name}" returned no tokens (server may have rejected the exchange silently)`);
|
|
198
|
-
await hooks?.callHook("mcp:auth:success", { name: config.name });
|
|
199
|
-
return {
|
|
200
|
-
tokens,
|
|
201
|
-
tools
|
|
202
|
-
};
|
|
203
|
-
} catch (err) {
|
|
204
|
-
const error = err instanceof Error ? err : new Error(String(err));
|
|
205
|
-
await hooks?.callHook("mcp:auth:error", {
|
|
206
|
-
name: config.name,
|
|
207
|
-
error
|
|
208
|
-
});
|
|
209
|
-
throw error;
|
|
210
|
-
} finally {
|
|
211
|
-
await callback?.close();
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Build an `sse` / `streamable-http` transport pre-wired with `authProvider`.
|
|
216
|
-
* Mirrors the bootstrap-side `createTransport` shape but inlined here so the
|
|
217
|
-
* login flow doesn't depend on the bootstrap module's private export.
|
|
218
|
-
*/
|
|
219
|
-
async function createInteractiveTransport(config, authProvider) {
|
|
220
|
-
if (config.transport === "sse") {
|
|
221
|
-
const { SSEClientTransport } = await import("@modelcontextprotocol/sdk/client/sse.js");
|
|
222
|
-
return new SSEClientTransport(new URL(config.url), {
|
|
223
|
-
requestInit: config.headers ? { headers: config.headers } : void 0,
|
|
224
|
-
authProvider
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
const { StreamableHTTPClientTransport } = await import("@modelcontextprotocol/sdk/client/streamableHttp.js");
|
|
228
|
-
return new StreamableHTTPClientTransport(new URL(config.url), {
|
|
229
|
-
requestInit: config.headers ? { headers: config.headers } : void 0,
|
|
230
|
-
fetch: sseToJsonFetchIfNeeded(),
|
|
231
|
-
authProvider
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
function isUnauthorizedError(err) {
|
|
235
|
-
if (!err || typeof err !== "object") return false;
|
|
236
|
-
const e = err;
|
|
237
|
-
if (e.name === "UnauthorizedError" || e.constructor?.name === "UnauthorizedError") return true;
|
|
238
|
-
return typeof e.message === "string" && e.message.toLowerCase().startsWith("unauthorized");
|
|
239
|
-
}
|
|
240
|
-
/**
|
|
241
|
-
* Wait on the callback handle, with a hard timeout AND honor the external
|
|
242
|
-
* abort signal. Unlike `callback.promise` alone, this provides a deterministic
|
|
243
|
-
* failure mode for "user opened the browser then walked away" — the modal
|
|
244
|
-
* doesn't hang forever waiting for a redirect that may never come.
|
|
245
|
-
*/
|
|
246
|
-
async function raceLoginCallback(handle, timeoutMs, signal) {
|
|
247
|
-
if (signal?.aborted) throw new Error("OAuth login aborted");
|
|
248
|
-
let timer;
|
|
249
|
-
let onAbort;
|
|
250
|
-
try {
|
|
251
|
-
return await new Promise((resolve, reject) => {
|
|
252
|
-
timer = setTimeout(() => reject(/* @__PURE__ */ new Error(`OAuth login timed out after ${timeoutMs}ms`)), timeoutMs);
|
|
253
|
-
if (signal) {
|
|
254
|
-
onAbort = () => reject(/* @__PURE__ */ new Error("OAuth login aborted"));
|
|
255
|
-
signal.addEventListener("abort", onAbort, { once: true });
|
|
256
|
-
}
|
|
257
|
-
handle.promise.then(resolve, reject);
|
|
258
|
-
});
|
|
259
|
-
} finally {
|
|
260
|
-
if (timer) clearTimeout(timer);
|
|
261
|
-
if (signal && onAbort) signal.removeEventListener("abort", onAbort);
|
|
262
|
-
}
|
|
263
|
-
}
|
|
264
|
-
//#endregion
|
|
265
|
-
export { startOAuthCallback as n, loginMcpServer as t };
|
|
266
|
-
|
|
267
|
-
//# sourceMappingURL=login-CCA-1lgK.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"login-CCA-1lgK.js","names":[],"sources":["../src/mcp/oauth-callback.ts","../src/mcp/login.ts"],"sourcesContent":["/**\n * Local loopback HTTP callback for OAuth 2.0 authorization code flows.\n *\n * Stands up a one-shot server on `127.0.0.1:<random>` that captures the\n * `?code=...` redirect from a browser-driven OAuth flow and resolves a\n * promise with the code. Used as the `redirectUrl` half of the MCP SDK's\n * `OAuthClientProvider` (the persistence half lives separately).\n *\n * Design:\n * - Loopback-only (`127.0.0.1`) — the OAuth spec treats `http://127.0.0.1:<port>`\n * as a public-client redirect URI per RFC 8252 §7.3. Browsers do NOT block it,\n * and Anthropic / OpenAI / Linear / GitHub all accept it.\n * - Random port (`port = 0`) — the OS picks an unused one. We read the actual\n * port back from `server.address()` after `listen()`.\n * - Single-shot — the first GET to `path` with a `code` (or `error`) wins;\n * subsequent requests get 404. The server keeps listening (in case the user\n * hits \"back\" and re-authorizes), so callers must `close()` once they have\n * the code or have given up.\n * - Abort-aware — wiring an external `AbortSignal` rejects the promise and\n * closes the server immediately. Required for the TUI's \"esc cancels login\"\n * UX.\n * - No HTML framework — a single inline `<html>` string keeps this isolated\n * from any UI dependency.\n */\n\nimport type { AddressInfo } from 'node:net'\nimport { createServer } from 'node:http'\n\n/**\n * Result of a successful callback. `state` is forwarded verbatim from the\n * query string — callers verify it against their pre-flight value to defend\n * against CSRF (the MCP SDK does this internally when it controls `state`).\n */\nexport interface OAuthCallbackResult {\n code: string\n state?: string\n}\n\nexport interface OAuthCallbackHandle {\n /**\n * Full URI to register with the authorization server, e.g.\n * `http://127.0.0.1:51823/callback`. Stable for the lifetime of the\n * handle.\n */\n redirectUri: string\n /**\n * Resolves with `{ code, state }` on a successful callback. Rejects with:\n * - The OAuth-spec `error` field (`access_denied`, `server_error`, ...)\n * when the authorization server redirects with `?error=...`.\n * - `'OAuth callback aborted'` when the external `AbortSignal` fires.\n * - `'OAuth callback server closed'` when `close()` is called before any\n * callback arrives.\n *\n * Single-shot — only the first matching request resolves the promise.\n */\n promise: Promise<OAuthCallbackResult>\n /**\n * Idempotent shutdown. Safe to call from a `finally` block whether the\n * flow succeeded, failed, or was aborted. Resolves once the server stops\n * accepting connections.\n */\n close: () => Promise<void>\n}\n\nexport interface OAuthCallbackOptions {\n /** Cancels the flow — rejects `promise` and closes the server. */\n signal?: AbortSignal\n /**\n * Path component the authorization server should redirect to. Defaults\n * to `/callback`. Useful when matching a pre-registered URI that uses a\n * different path.\n */\n path?: string\n /**\n * Override the loopback host. Defaults to `127.0.0.1`. Don't bind to\n * `0.0.0.0` here — the OAuth code is a one-time secret and the server\n * would otherwise accept it from any host on the LAN.\n */\n host?: string\n /**\n * Override the port. Defaults to `0` (OS-assigned). Pin to a fixed port\n * only when the authorization server requires a pre-registered redirect\n * URI; the random-port path is preferred so concurrent flows don't clash.\n */\n port?: number\n}\n\nconst DEFAULT_PATH = '/callback'\nconst DEFAULT_HOST = '127.0.0.1'\n\nconst SUCCESS_HTML = `<!doctype html>\n<html><head><meta charset=\"utf-8\"><title>Logged in</title>\n<style>body{font:14px/1.5 -apple-system,system-ui,sans-serif;margin:6rem auto;max-width:28rem;text-align:center;color:#1d1d1f}.ok{color:#1f8a4c;font-weight:600}</style>\n</head><body>\n<p class=\"ok\">Logged in.</p>\n<p>You can close this tab and return to the terminal.</p>\n</body></html>`\n\nfunction errorHtml(message: string): string {\n // No interpolation into HTML attributes — message goes only inside <p> text\n // where the SAMEORIGIN browser context is rendering. Escape angle brackets\n // and ampersands defensively anyway: a malicious authorization server could\n // theoretically craft an `error_description` containing markup, and we\n // don't want stored XSS even in a one-shot loopback page.\n const escaped = message\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n return `<!doctype html>\n<html><head><meta charset=\"utf-8\"><title>Login failed</title>\n<style>body{font:14px/1.5 -apple-system,system-ui,sans-serif;margin:6rem auto;max-width:28rem;text-align:center;color:#1d1d1f}.err{color:#c43c2c;font-weight:600}</style>\n</head><body>\n<p class=\"err\">Login failed.</p>\n<p>${escaped}</p>\n<p>You can close this tab and return to the terminal.</p>\n</body></html>`\n}\n\n/**\n * Start a one-shot OAuth callback server. The returned handle's `redirectUri`\n * should be passed to the authorization server as the `redirect_uri` query\n * parameter; `promise` resolves once the user finishes the browser flow.\n *\n * Always `await handle.close()` in a `finally` block — even on success, the\n * server stays open until told to shut down (so it can serve the\n * \"you can close this tab\" page).\n */\nexport async function startOAuthCallback(\n opts: OAuthCallbackOptions = {},\n): Promise<OAuthCallbackHandle> {\n const path = opts.path ?? DEFAULT_PATH\n const host = opts.host ?? DEFAULT_HOST\n const port = opts.port ?? 0\n\n if (!path.startsWith('/'))\n throw new Error(`OAuth callback path must start with \"/\" (got: ${JSON.stringify(path)})`)\n\n if (opts.signal?.aborted)\n throw new Error('OAuth callback aborted')\n\n let resolveResult: (value: OAuthCallbackResult) => void\n let rejectResult: (error: Error) => void\n const promise = new Promise<OAuthCallbackResult>((resolve, reject) => {\n resolveResult = resolve\n rejectResult = reject\n })\n // Attach a default no-op catch so a rejection without a consumer doesn't\n // raise an unhandled-rejection warning. Callers that DO `await promise`\n // still receive the rejection — Promises fan out to all listeners.\n promise.catch(() => {})\n\n let settled = false\n const resolveOnce = (value: OAuthCallbackResult) => {\n if (settled)\n return\n settled = true\n resolveResult(value)\n }\n const rejectOnce = (error: Error) => {\n if (settled)\n return\n settled = true\n rejectResult(error)\n }\n\n const server = createServer((req, res) => {\n const url = new URL(req.url ?? '/', `http://${host}`)\n if (url.pathname !== path) {\n res.writeHead(404, { 'content-type': 'text/plain' })\n res.end('Not Found')\n return\n }\n\n // `no-store` prevents the browser from replaying the page (with the\n // now-spent code in its URL bar) when the user hits back, refreshes,\n // or restores the tab from history. The code is a one-time secret.\n const htmlHeaders = {\n 'content-type': 'text/html; charset=utf-8',\n 'cache-control': 'no-store',\n }\n\n const error = url.searchParams.get('error')\n if (error) {\n const desc = url.searchParams.get('error_description') ?? error\n res.writeHead(400, htmlHeaders)\n res.end(errorHtml(desc))\n rejectOnce(new Error(`OAuth authorization failed: ${desc}`))\n return\n }\n\n const code = url.searchParams.get('code')\n if (!code) {\n // No code, no error — likely a stray probe. Serve a minimal page,\n // don't resolve. The browser will eventually arrive with the real\n // redirect, or the caller will time out via signal.\n res.writeHead(400, { 'content-type': 'text/plain' })\n res.end('Missing \"code\" query parameter.')\n return\n }\n\n const state = url.searchParams.get('state') ?? undefined\n res.writeHead(200, htmlHeaders)\n res.end(SUCCESS_HTML)\n resolveOnce({ code, state })\n })\n\n // Surface socket errors that arrive before the request handler — e.g. EADDRINUSE\n // on a pinned port, or an early TLS-style probe that the parser rejects.\n server.on('error', (err) => {\n rejectOnce(err instanceof Error ? err : new Error(String(err)))\n })\n\n await new Promise<void>((resolve, reject) => {\n server.once('error', reject)\n server.listen(port, host, () => {\n server.off('error', reject)\n resolve()\n })\n })\n\n const addr = server.address() as AddressInfo | null\n if (!addr || typeof addr === 'string') {\n server.close()\n throw new Error('OAuth callback server did not bind to a TCP port')\n }\n\n // `closeServer` and `onAbort` reference each other — pre-declare both so\n // either ordering reads cleanly and the lint doesn't trip on the cycle.\n let closing: Promise<void> | undefined\n let closeServer: () => Promise<void>\n const onAbort = () => {\n rejectOnce(new Error('OAuth callback aborted'))\n void closeServer()\n }\n closeServer = (): Promise<void> => {\n if (closing)\n return closing\n closing = new Promise<void>((resolve) => {\n // `close()` on a Node http server waits for in-flight requests to drain.\n // The flow has already settled (or is being aborted), so we also call\n // `closeAllConnections()` to drop keep-alive sockets immediately — a\n // browser keeps the connection open after the success page, which would\n // otherwise hang the close for the keep-alive timeout (~5s).\n const anyServer = server as { closeAllConnections?: () => void }\n anyServer.closeAllConnections?.()\n server.close(() => {\n opts.signal?.removeEventListener('abort', onAbort)\n // If neither a callback nor an abort happened before close(), reject\n // pending awaits so callers don't hang. Tag the rejection as\n // 'aborted' when the signal already fired — Bun fires the abort\n // listener via a microtask, so server.close() may resolve first\n // even though semantically the abort came first.\n if (opts.signal?.aborted)\n rejectOnce(new Error('OAuth callback aborted'))\n else\n rejectOnce(new Error('OAuth callback server closed'))\n resolve()\n })\n })\n return closing\n }\n opts.signal?.addEventListener('abort', onAbort, { once: true })\n\n return {\n redirectUri: `http://${host}:${addr.port}${path}`,\n promise,\n close: closeServer,\n }\n}\n","/**\n * Interactive OAuth login orchestrator for one MCP server.\n *\n * Composes the three pieces shipped separately:\n * - `startOAuthCallback` (`./oauth-callback`) — local loopback HTTP\n * listener that catches the `?code=...` redirect.\n * - `McpOAuthProvider` (`./oauth-provider`) — SDK adapter that persists\n * tokens / client info via an injected `McpCredentialStore`.\n * - The MCP SDK's transport `authProvider` + `finishAuth` plumbing —\n * `client.connect()` triggers the SDK to call `redirectToAuthorization`\n * (which opens the browser); we wait for the loopback callback to\n * deliver the code, hand it to `transport.finishAuth`, and reconnect.\n *\n * Returns once tokens are persisted. Caller decides whether to immediately\n * reconnect the server (re-run `connectMcpServers` / `agent.warmup()`) or\n * defer to the next session activation. The returned `tools` array is\n * provided as a convenience — callers that want the connection live in\n * this call rather than rebuilding can wire them in directly.\n */\n\nimport type { OAuthClientProvider } from '@modelcontextprotocol/sdk/client/auth.js'\nimport type { Hookable } from 'hookable'\nimport type { AgentHooks } from '../agent'\nimport type { McpServerConfig } from '../types'\nimport type { OAuthCallbackHandle } from './oauth-callback'\nimport type { McpCredentialStore } from './oauth-provider'\nimport { startOAuthCallback } from './oauth-callback'\nimport { McpOAuthProvider } from './oauth-provider'\nimport { sseToJsonFetchIfNeeded } from './sse-to-json-fetch'\nimport { createTolerantClient } from './tolerant-client'\n\nexport interface LoginMcpServerOptions {\n /** Persistence — same store the bootstrap path reads from. */\n store: McpCredentialStore\n /**\n * Invoked with the authorization URL once it's ready. Hosts typically\n * (a) emit `mcp:auth:url` for the TUI, and (b) call `tryOpenBrowser`.\n * The URL is identical to the one passed to the `mcp:auth:url` hook\n * fired automatically — this callback is a synchronous hook for callers\n * that don't want to wire the agent hook machinery.\n */\n onAuthorizationUrl?: (url: URL) => void | Promise<void>\n /** Cancels the flow (esc / close modal / SIGINT). */\n signal?: AbortSignal\n /** Agent hooks. The flow emits `mcp:auth:url`/`success`/`error` when wired. */\n hooks?: Hookable<AgentHooks>\n /** Override `client_name` shown on consent screens. Default: 'zidane'. */\n clientName?: string\n /** Override the requested OAuth scope. */\n scope?: string\n /**\n * Override the loopback callback path. Default: `/callback`. Useful only\n * for servers that pinned a different path during registration.\n */\n callbackPath?: string\n /**\n * Maximum time to wait for the user to complete the browser flow, in ms.\n * The user can also cancel via `signal`. Default: 5 minutes.\n */\n timeoutMs?: number\n}\n\nexport interface LoginMcpServerResult {\n /** Stored OAuth tokens after a successful exchange. */\n tokens: NonNullable<ReturnType<McpOAuthProvider['tokens']>>\n /**\n * Upstream tool descriptors discovered after re-connecting with the new\n * tokens. Already filtered by the server's `enabledTools` / `disabledTools`\n * is NOT applied here — that's a bootstrap concern. Hosts that want filtering\n * should pass the result through `connectMcpServers` rebuild on the next\n * session activation rather than reusing this list verbatim.\n */\n tools: Array<{ name: string, description?: string | null, inputSchema?: unknown }>\n}\n\nconst DEFAULT_LOGIN_TIMEOUT_MS = 5 * 60_000\n\n/**\n * Run the full interactive OAuth flow for `config`. Only supports `sse` and\n * `streamable-http` transports — `stdio` MCP servers don't speak OAuth.\n *\n * Throws on:\n * - Wrong transport.\n * - Abort signal.\n * - Browser-side error (user denied, server rejected, etc.).\n * - Code exchange failure.\n * - Post-exchange connect failure.\n *\n * Always closes the loopback callback server before returning, success or\n * failure.\n */\nexport async function loginMcpServer(\n config: McpServerConfig,\n options: LoginMcpServerOptions,\n): Promise<LoginMcpServerResult> {\n if (config.transport !== 'sse' && config.transport !== 'streamable-http')\n throw new Error(`MCP OAuth: cannot login for transport \"${config.transport}\" — only sse / streamable-http are supported`)\n if (!config.url)\n throw new Error(`MCP OAuth: server \"${config.name}\" is missing a url`)\n\n const hooks = options.hooks\n\n let callback: OAuthCallbackHandle | undefined\n try {\n callback = await startOAuthCallback({\n signal: options.signal,\n path: options.callbackPath,\n })\n\n const handle = callback\n const provider = new McpOAuthProvider({\n name: config.name,\n store: options.store,\n redirectUri: handle.redirectUri,\n clientName: options.clientName,\n scope: options.scope,\n onAuthorizationUrl: async (url) => {\n await hooks?.callHook('mcp:auth:url', { name: config.name, url: url.toString() })\n await options.onAuthorizationUrl?.(url)\n },\n })\n\n const transport = await createInteractiveTransport(config, provider)\n const client = await createTolerantClient({ name: 'zidane', version: '1.0.0' })\n\n // First connect → SDK has no tokens, so it calls\n // `provider.redirectToAuthorization(url)` (which routes to our\n // `onAuthorizationUrl`), then throws `UnauthorizedError`. This is the\n // expected path — we catch and wait on the loopback for the code.\n let needsAuth = false\n try {\n await client.connect(transport)\n }\n catch (err) {\n if (!isUnauthorizedError(err)) {\n await client.close().catch(() => {})\n throw err\n }\n needsAuth = true\n }\n\n if (needsAuth) {\n const { code } = await raceLoginCallback(handle, options.timeoutMs ?? DEFAULT_LOGIN_TIMEOUT_MS, options.signal)\n // SDK exchanges code → tokens; provider.saveTokens persists them.\n await (transport as { finishAuth: (code: string) => Promise<void> }).finishAuth(code)\n // Reconnect with a FRESH transport. The previous one is already in\n // the \"started\" state from the failed connect — reusing it throws\n // \"StreamableHTTPClientTransport already started!\" inside\n // `transport.start()`. The SDK's canonical example\n // (`simpleOAuthClient.js`) uses the same pattern: recurse with a\n // new transport, reuse the client. Close the stale transport\n // first so its abort controllers + sockets don't leak.\n await transport.close().catch(() => {})\n const freshTransport = await createInteractiveTransport(config, provider)\n await client.connect(freshTransport)\n }\n\n const { tools } = await client.listTools()\n await client.close()\n\n const tokens = provider.tokens()\n if (!tokens)\n throw new Error(`MCP OAuth: login for \"${config.name}\" returned no tokens (server may have rejected the exchange silently)`)\n\n await hooks?.callHook('mcp:auth:success', { name: config.name })\n return { tokens, tools }\n }\n catch (err) {\n const error = err instanceof Error ? err : new Error(String(err))\n await hooks?.callHook('mcp:auth:error', { name: config.name, error })\n throw error\n }\n finally {\n await callback?.close()\n }\n}\n\n/**\n * Build an `sse` / `streamable-http` transport pre-wired with `authProvider`.\n * Mirrors the bootstrap-side `createTransport` shape but inlined here so the\n * login flow doesn't depend on the bootstrap module's private export.\n */\nasync function createInteractiveTransport(config: McpServerConfig, authProvider: OAuthClientProvider) {\n if (config.transport === 'sse') {\n const { SSEClientTransport } = await import('@modelcontextprotocol/sdk/client/sse.js')\n return new SSEClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n authProvider,\n })\n }\n // streamable-http\n const { StreamableHTTPClientTransport } = await import('@modelcontextprotocol/sdk/client/streamableHttp.js')\n return new StreamableHTTPClientTransport(new URL(config.url!), {\n requestInit: config.headers ? { headers: config.headers } : undefined,\n fetch: sseToJsonFetchIfNeeded(),\n authProvider,\n })\n}\n\nfunction isUnauthorizedError(err: unknown): boolean {\n if (!err || typeof err !== 'object')\n return false\n const e = err as { name?: string, message?: string, constructor?: { name?: string } }\n if (e.name === 'UnauthorizedError' || e.constructor?.name === 'UnauthorizedError')\n return true\n return typeof e.message === 'string' && e.message.toLowerCase().startsWith('unauthorized')\n}\n\n/**\n * Wait on the callback handle, with a hard timeout AND honor the external\n * abort signal. Unlike `callback.promise` alone, this provides a deterministic\n * failure mode for \"user opened the browser then walked away\" — the modal\n * doesn't hang forever waiting for a redirect that may never come.\n */\nasync function raceLoginCallback(\n handle: OAuthCallbackHandle,\n timeoutMs: number,\n signal: AbortSignal | undefined,\n): Promise<{ code: string, state?: string }> {\n if (signal?.aborted)\n throw new Error('OAuth login aborted')\n let timer: ReturnType<typeof setTimeout> | undefined\n let onAbort: (() => void) | undefined\n try {\n return await new Promise<{ code: string, state?: string }>((resolve, reject) => {\n timer = setTimeout(\n () => reject(new Error(`OAuth login timed out after ${timeoutMs}ms`)),\n timeoutMs,\n )\n if (signal) {\n onAbort = () => reject(new Error('OAuth login aborted'))\n signal.addEventListener('abort', onAbort, { once: true })\n }\n handle.promise.then(resolve, reject)\n })\n }\n finally {\n if (timer)\n clearTimeout(timer)\n if (signal && onAbort)\n signal.removeEventListener('abort', onAbort)\n }\n}\n"],"mappings":"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"}
|