@mastra/code-sdk 0.2.0-alpha.4 → 0.2.0-alpha.9
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/CHANGELOG.md +100 -0
- package/dist/agents/credential-resolver.d.ts +51 -0
- package/dist/agents/credential-resolver.d.ts.map +1 -0
- package/dist/agents/credential-resolver.js +41 -0
- package/dist/agents/credential-resolver.js.map +1 -0
- package/dist/agents/instructions.d.ts.map +1 -1
- package/dist/agents/instructions.js.map +1 -1
- package/dist/agents/mastracode-gateway.d.ts +11 -4
- package/dist/agents/mastracode-gateway.d.ts.map +1 -1
- package/dist/agents/mastracode-gateway.js +51 -30
- package/dist/agents/mastracode-gateway.js.map +1 -1
- package/dist/agents/model.d.ts +2 -0
- package/dist/agents/model.d.ts.map +1 -1
- package/dist/agents/model.js +13 -2
- package/dist/agents/model.js.map +1 -1
- package/dist/agents/workspace.d.ts +11 -1
- package/dist/agents/workspace.d.ts.map +1 -1
- package/dist/agents/workspace.js +31 -13
- package/dist/agents/workspace.js.map +1 -1
- package/dist/auth/authorization-input.d.ts +16 -0
- package/dist/auth/authorization-input.d.ts.map +1 -0
- package/dist/auth/authorization-input.js +28 -0
- package/dist/auth/authorization-input.js.map +1 -0
- package/dist/auth/device-code.d.ts +93 -0
- package/dist/auth/device-code.d.ts.map +1 -0
- package/dist/auth/device-code.js +97 -0
- package/dist/auth/device-code.js.map +1 -0
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +3 -1
- package/dist/auth/index.js.map +1 -1
- package/dist/auth/providers/anthropic.d.ts +24 -1
- package/dist/auth/providers/anthropic.d.ts.map +1 -1
- package/dist/auth/providers/anthropic.js +21 -8
- package/dist/auth/providers/anthropic.js.map +1 -1
- package/dist/auth/providers/github-copilot.d.ts +54 -0
- package/dist/auth/providers/github-copilot.d.ts.map +1 -1
- package/dist/auth/providers/github-copilot.js +88 -61
- package/dist/auth/providers/github-copilot.js.map +1 -1
- package/dist/auth/providers/openai-codex.d.ts +44 -0
- package/dist/auth/providers/openai-codex.d.ts.map +1 -1
- package/dist/auth/providers/openai-codex.js +79 -69
- package/dist/auth/providers/openai-codex.js.map +1 -1
- package/dist/auth/providers/xai.d.ts +64 -0
- package/dist/auth/providers/xai.d.ts.map +1 -0
- package/dist/auth/providers/xai.js +171 -0
- package/dist/auth/providers/xai.js.map +1 -0
- package/dist/auth/storage.d.ts.map +1 -1
- package/dist/auth/storage.js +5 -2
- package/dist/auth/storage.js.map +1 -1
- package/dist/auth/types.d.ts +21 -0
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/hooks/types.d.ts.map +1 -1
- package/dist/hooks/types.js.map +1 -1
- package/dist/index.d.ts +12 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -35
- package/dist/index.js.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.d.ts +7 -0
- package/dist/providers/amazon-bedrock-gateway.d.ts.map +1 -1
- package/dist/providers/amazon-bedrock-gateway.js +66 -2
- package/dist/providers/amazon-bedrock-gateway.js.map +1 -1
- package/dist/providers/claude-max.d.ts +3 -1
- package/dist/providers/claude-max.d.ts.map +1 -1
- package/dist/providers/claude-max.js +1 -1
- package/dist/providers/claude-max.js.map +1 -1
- package/dist/providers/github-copilot.d.ts +3 -1
- package/dist/providers/github-copilot.d.ts.map +1 -1
- package/dist/providers/github-copilot.js +1 -1
- package/dist/providers/github-copilot.js.map +1 -1
- package/dist/providers/openai-codex.d.ts +3 -1
- package/dist/providers/openai-codex.d.ts.map +1 -1
- package/dist/providers/openai-codex.js +1 -1
- package/dist/providers/openai-codex.js.map +1 -1
- package/dist/providers/xai.d.ts +33 -0
- package/dist/providers/xai.d.ts.map +1 -0
- package/dist/providers/xai.js +63 -0
- package/dist/providers/xai.js.map +1 -0
- package/dist/tools/request-sandbox-access.d.ts.map +1 -1
- package/dist/tools/request-sandbox-access.js.map +1 -1
- package/package.json +11 -11
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic RFC 8628 (OAuth 2.0 Device Authorization Grant) polling helpers.
|
|
3
|
+
*
|
|
4
|
+
* Ported from pi-mono's device-code utility, restructured as a single-step
|
|
5
|
+
* API so the same poll semantics can be driven two ways:
|
|
6
|
+
* - `pollDeviceCodeUntilComplete()` — blocking loop for TUI flows.
|
|
7
|
+
* - `stepDeviceCodePoll()` — exactly one upstream poll per call, with a
|
|
8
|
+
* JSON-serializable `DeviceCodePollState` so web routes can persist the
|
|
9
|
+
* state between HTTP requests (any replica can continue the poll).
|
|
10
|
+
*
|
|
11
|
+
* Inspired by pi-mono:
|
|
12
|
+
* https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/device-code.ts
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* Serializable poll-loop state. Safe to round-trip through JSON (e.g. a
|
|
16
|
+
* `pending jsonb` column) so device-code polling can span HTTP requests.
|
|
17
|
+
*/
|
|
18
|
+
export interface DeviceCodePollState {
|
|
19
|
+
/** ms epoch after which the device authorization is considered expired. */
|
|
20
|
+
deadlineAt: number;
|
|
21
|
+
/** Current base poll interval in ms (grows on slow_down responses). */
|
|
22
|
+
intervalMs: number;
|
|
23
|
+
/** Number of slow_down responses observed so far. */
|
|
24
|
+
slowDownResponses: number;
|
|
25
|
+
}
|
|
26
|
+
export declare function createDeviceCodePollState(options: {
|
|
27
|
+
/** Poll interval suggested by the server, in seconds. Defaults to 5 (RFC 8628). */
|
|
28
|
+
intervalSeconds?: number;
|
|
29
|
+
/** Lifetime of the device code, in seconds. */
|
|
30
|
+
expiresInSeconds: number;
|
|
31
|
+
/** Override "now" for tests. */
|
|
32
|
+
now?: number;
|
|
33
|
+
}): DeviceCodePollState;
|
|
34
|
+
/**
|
|
35
|
+
* Classified result of one upstream token-endpoint poll. Providers implement
|
|
36
|
+
* the HTTP request and map their response shape onto this union.
|
|
37
|
+
*/
|
|
38
|
+
export type DeviceCodePollOutcome<T> = {
|
|
39
|
+
status: 'complete';
|
|
40
|
+
result: T;
|
|
41
|
+
} | {
|
|
42
|
+
status: 'pending';
|
|
43
|
+
intervalSeconds?: number;
|
|
44
|
+
} | {
|
|
45
|
+
status: 'slow_down';
|
|
46
|
+
intervalSeconds?: number;
|
|
47
|
+
} | {
|
|
48
|
+
status: 'failed';
|
|
49
|
+
error: string;
|
|
50
|
+
};
|
|
51
|
+
export type DeviceCodeStepResult<T> = {
|
|
52
|
+
status: 'complete';
|
|
53
|
+
result: T;
|
|
54
|
+
state: DeviceCodePollState;
|
|
55
|
+
} | {
|
|
56
|
+
status: 'pending';
|
|
57
|
+
nextPollMs: number;
|
|
58
|
+
state: DeviceCodePollState;
|
|
59
|
+
} | {
|
|
60
|
+
status: 'slow_down';
|
|
61
|
+
nextPollMs: number;
|
|
62
|
+
state: DeviceCodePollState;
|
|
63
|
+
} | {
|
|
64
|
+
status: 'failed';
|
|
65
|
+
error: string;
|
|
66
|
+
state: DeviceCodePollState;
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* Delay to wait before the next poll, honoring slow_down growth and clamped
|
|
70
|
+
* to the remaining lifetime of the device code.
|
|
71
|
+
*/
|
|
72
|
+
export declare function nextPollDelayMs(state: DeviceCodePollState, now?: number): number;
|
|
73
|
+
/**
|
|
74
|
+
* Perform exactly one upstream poll and fold the outcome into the poll state.
|
|
75
|
+
* Never throws for flow-level conditions — timeouts and provider errors are
|
|
76
|
+
* reported as `{ status: 'failed' }` so callers can persist/report them.
|
|
77
|
+
*/
|
|
78
|
+
export declare function stepDeviceCodePoll<T>(state: DeviceCodePollState, pollOnce: () => Promise<DeviceCodePollOutcome<T>>, now?: number): Promise<DeviceCodeStepResult<T>>;
|
|
79
|
+
/** Sleep that can be interrupted by an AbortSignal. */
|
|
80
|
+
export declare function abortableSleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Blocking poll loop for TUI flows: waits the appropriate interval between
|
|
83
|
+
* polls, honors slow_down growth, aborts on the signal, and throws on
|
|
84
|
+
* failure/timeout (with a clock-drift hint after slow_down responses).
|
|
85
|
+
*/
|
|
86
|
+
export declare function pollDeviceCodeUntilComplete<T>(options: {
|
|
87
|
+
state: DeviceCodePollState;
|
|
88
|
+
pollOnce: () => Promise<DeviceCodePollOutcome<T>>;
|
|
89
|
+
signal?: AbortSignal;
|
|
90
|
+
/** Override the sleep implementation for tests. */
|
|
91
|
+
sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
92
|
+
}): Promise<T>;
|
|
93
|
+
//# sourceMappingURL=device-code.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-code.d.ts","sourceRoot":"","sources":["../../src/auth/device-code.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAOH;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,2EAA2E;IAC3E,UAAU,EAAE,MAAM,CAAC;IACnB,uEAAuE;IACvE,UAAU,EAAE,MAAM,CAAC;IACnB,qDAAqD;IACrD,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,yBAAyB,CAAC,OAAO,EAAE;IACjD,mFAAmF;IACnF,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,+CAA+C;IAC/C,gBAAgB,EAAE,MAAM,CAAC;IACzB,gCAAgC;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;CACd,GAAG,mBAAmB,CAWtB;AAED;;;GAGG;AACH,MAAM,MAAM,qBAAqB,CAAC,CAAC,IAC/B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,CAAC,CAAA;CAAE,GACjC;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GAC/C;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACjD;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAC9B;IAAE,MAAM,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,GAC7D;IAAE,MAAM,EAAE,SAAS,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,GACrE;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,GACvE;IAAE,MAAM,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,mBAAmB,CAAA;CAAE,CAAC;AAWpE;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,mBAAmB,EAAE,GAAG,GAAE,MAAmB,GAAG,MAAM,CAK5F;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,CAAC,EACxC,KAAK,EAAE,mBAAmB,EAC1B,QAAQ,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,EACjD,GAAG,GAAE,MAAmB,GACvB,OAAO,CAAC,oBAAoB,CAAC,CAAC,CAAC,CAAC,CAiClC;AAED,uDAAuD;AACvD,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB9E;AAED;;;;GAIG;AACH,wBAAsB,2BAA2B,CAAC,CAAC,EAAE,OAAO,EAAE;IAC5D,KAAK,EAAE,mBAAmB,CAAC;IAC3B,QAAQ,EAAE,MAAM,OAAO,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,mDAAmD;IACnD,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7D,GAAG,OAAO,CAAC,CAAC,CAAC,CAwBb"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
const DEFAULT_INTERVAL_SECONDS = 5;
|
|
2
|
+
const INITIAL_POLL_INTERVAL_MULTIPLIER = 1.2;
|
|
3
|
+
const SLOW_DOWN_POLL_INTERVAL_MULTIPLIER = 1.4;
|
|
4
|
+
const SLOW_DOWN_INTERVAL_INCREMENT_MS = 5e3;
|
|
5
|
+
function createDeviceCodePollState(options) {
|
|
6
|
+
const now = options.now ?? Date.now();
|
|
7
|
+
const intervalSeconds = typeof options.intervalSeconds === "number" && options.intervalSeconds > 0 ? options.intervalSeconds : DEFAULT_INTERVAL_SECONDS;
|
|
8
|
+
return {
|
|
9
|
+
deadlineAt: now + options.expiresInSeconds * 1e3,
|
|
10
|
+
intervalMs: Math.max(1e3, Math.floor(intervalSeconds * 1e3)),
|
|
11
|
+
slowDownResponses: 0
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function timeoutMessage(state) {
|
|
15
|
+
if (state.slowDownResponses > 0) {
|
|
16
|
+
return "Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.";
|
|
17
|
+
}
|
|
18
|
+
return "Device flow timed out";
|
|
19
|
+
}
|
|
20
|
+
function nextPollDelayMs(state, now = Date.now()) {
|
|
21
|
+
const multiplier = state.slowDownResponses > 0 ? SLOW_DOWN_POLL_INTERVAL_MULTIPLIER : INITIAL_POLL_INTERVAL_MULTIPLIER;
|
|
22
|
+
const remainingMs = Math.max(0, state.deadlineAt - now);
|
|
23
|
+
return Math.min(Math.ceil(state.intervalMs * multiplier), remainingMs);
|
|
24
|
+
}
|
|
25
|
+
async function stepDeviceCodePoll(state, pollOnce, now = Date.now()) {
|
|
26
|
+
if (now >= state.deadlineAt) {
|
|
27
|
+
return { status: "failed", error: timeoutMessage(state), state };
|
|
28
|
+
}
|
|
29
|
+
const outcome = await pollOnce();
|
|
30
|
+
switch (outcome.status) {
|
|
31
|
+
case "complete":
|
|
32
|
+
return { status: "complete", result: outcome.result, state };
|
|
33
|
+
case "failed":
|
|
34
|
+
return { status: "failed", error: outcome.error, state };
|
|
35
|
+
case "slow_down": {
|
|
36
|
+
const next = {
|
|
37
|
+
...state,
|
|
38
|
+
slowDownResponses: state.slowDownResponses + 1,
|
|
39
|
+
// RFC 8628 section 3.5: grow the interval by 5 seconds, unless the
|
|
40
|
+
// server told us the interval to use.
|
|
41
|
+
intervalMs: typeof outcome.intervalSeconds === "number" && outcome.intervalSeconds > 0 ? outcome.intervalSeconds * 1e3 : Math.max(1e3, state.intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS)
|
|
42
|
+
};
|
|
43
|
+
return { status: "slow_down", nextPollMs: nextPollDelayMs(next, now), state: next };
|
|
44
|
+
}
|
|
45
|
+
case "pending": {
|
|
46
|
+
const next = typeof outcome.intervalSeconds === "number" && outcome.intervalSeconds > 0 ? { ...state, intervalMs: Math.max(1e3, outcome.intervalSeconds * 1e3) } : state;
|
|
47
|
+
return { status: "pending", nextPollMs: nextPollDelayMs(next, now), state: next };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function abortableSleep(ms, signal) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
if (signal?.aborted) {
|
|
54
|
+
reject(new Error("Login cancelled"));
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let timeout;
|
|
58
|
+
const onAbort = () => {
|
|
59
|
+
clearTimeout(timeout);
|
|
60
|
+
reject(new Error("Login cancelled"));
|
|
61
|
+
};
|
|
62
|
+
timeout = setTimeout(() => {
|
|
63
|
+
signal?.removeEventListener("abort", onAbort);
|
|
64
|
+
resolve();
|
|
65
|
+
}, ms);
|
|
66
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
async function pollDeviceCodeUntilComplete(options) {
|
|
70
|
+
let state = options.state;
|
|
71
|
+
const sleep = options.sleep ?? abortableSleep;
|
|
72
|
+
while (true) {
|
|
73
|
+
if (options.signal?.aborted) {
|
|
74
|
+
throw new Error("Login cancelled");
|
|
75
|
+
}
|
|
76
|
+
if (Date.now() >= state.deadlineAt) {
|
|
77
|
+
throw new Error(timeoutMessage(state));
|
|
78
|
+
}
|
|
79
|
+
await sleep(nextPollDelayMs(state), options.signal);
|
|
80
|
+
const step = await stepDeviceCodePoll(state, options.pollOnce);
|
|
81
|
+
state = step.state;
|
|
82
|
+
if (step.status === "complete") {
|
|
83
|
+
return step.result;
|
|
84
|
+
}
|
|
85
|
+
if (step.status === "failed") {
|
|
86
|
+
throw new Error(step.error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export {
|
|
91
|
+
abortableSleep,
|
|
92
|
+
createDeviceCodePollState,
|
|
93
|
+
nextPollDelayMs,
|
|
94
|
+
pollDeviceCodeUntilComplete,
|
|
95
|
+
stepDeviceCodePoll
|
|
96
|
+
};
|
|
97
|
+
//# sourceMappingURL=device-code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/auth/device-code.ts"],"sourcesContent":["/**\n * Generic RFC 8628 (OAuth 2.0 Device Authorization Grant) polling helpers.\n *\n * Ported from pi-mono's device-code utility, restructured as a single-step\n * API so the same poll semantics can be driven two ways:\n * - `pollDeviceCodeUntilComplete()` — blocking loop for TUI flows.\n * - `stepDeviceCodePoll()` — exactly one upstream poll per call, with a\n * JSON-serializable `DeviceCodePollState` so web routes can persist the\n * state between HTTP requests (any replica can continue the poll).\n *\n * Inspired by pi-mono:\n * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/device-code.ts\n */\n\nconst DEFAULT_INTERVAL_SECONDS = 5;\nconst INITIAL_POLL_INTERVAL_MULTIPLIER = 1.2;\nconst SLOW_DOWN_POLL_INTERVAL_MULTIPLIER = 1.4;\nconst SLOW_DOWN_INTERVAL_INCREMENT_MS = 5000;\n\n/**\n * Serializable poll-loop state. Safe to round-trip through JSON (e.g. a\n * `pending jsonb` column) so device-code polling can span HTTP requests.\n */\nexport interface DeviceCodePollState {\n /** ms epoch after which the device authorization is considered expired. */\n deadlineAt: number;\n /** Current base poll interval in ms (grows on slow_down responses). */\n intervalMs: number;\n /** Number of slow_down responses observed so far. */\n slowDownResponses: number;\n}\n\nexport function createDeviceCodePollState(options: {\n /** Poll interval suggested by the server, in seconds. Defaults to 5 (RFC 8628). */\n intervalSeconds?: number;\n /** Lifetime of the device code, in seconds. */\n expiresInSeconds: number;\n /** Override \"now\" for tests. */\n now?: number;\n}): DeviceCodePollState {\n const now = options.now ?? Date.now();\n const intervalSeconds =\n typeof options.intervalSeconds === 'number' && options.intervalSeconds > 0\n ? options.intervalSeconds\n : DEFAULT_INTERVAL_SECONDS;\n return {\n deadlineAt: now + options.expiresInSeconds * 1000,\n intervalMs: Math.max(1000, Math.floor(intervalSeconds * 1000)),\n slowDownResponses: 0,\n };\n}\n\n/**\n * Classified result of one upstream token-endpoint poll. Providers implement\n * the HTTP request and map their response shape onto this union.\n */\nexport type DeviceCodePollOutcome<T> =\n | { status: 'complete'; result: T }\n | { status: 'pending'; intervalSeconds?: number }\n | { status: 'slow_down'; intervalSeconds?: number }\n | { status: 'failed'; error: string };\n\nexport type DeviceCodeStepResult<T> =\n | { status: 'complete'; result: T; state: DeviceCodePollState }\n | { status: 'pending'; nextPollMs: number; state: DeviceCodePollState }\n | { status: 'slow_down'; nextPollMs: number; state: DeviceCodePollState }\n | { status: 'failed'; error: string; state: DeviceCodePollState };\n\nfunction timeoutMessage(state: DeviceCodePollState): string {\n if (state.slowDownResponses > 0) {\n // Repeated slow_down responses followed by a timeout usually means the\n // local clock is behind the server's (common in WSL/VMs after sleep).\n return 'Device flow timed out after one or more slow_down responses. This is often caused by clock drift in WSL or VM environments. Please sync or restart the VM clock and try again.';\n }\n return 'Device flow timed out';\n}\n\n/**\n * Delay to wait before the next poll, honoring slow_down growth and clamped\n * to the remaining lifetime of the device code.\n */\nexport function nextPollDelayMs(state: DeviceCodePollState, now: number = Date.now()): number {\n const multiplier =\n state.slowDownResponses > 0 ? SLOW_DOWN_POLL_INTERVAL_MULTIPLIER : INITIAL_POLL_INTERVAL_MULTIPLIER;\n const remainingMs = Math.max(0, state.deadlineAt - now);\n return Math.min(Math.ceil(state.intervalMs * multiplier), remainingMs);\n}\n\n/**\n * Perform exactly one upstream poll and fold the outcome into the poll state.\n * Never throws for flow-level conditions — timeouts and provider errors are\n * reported as `{ status: 'failed' }` so callers can persist/report them.\n */\nexport async function stepDeviceCodePoll<T>(\n state: DeviceCodePollState,\n pollOnce: () => Promise<DeviceCodePollOutcome<T>>,\n now: number = Date.now(),\n): Promise<DeviceCodeStepResult<T>> {\n if (now >= state.deadlineAt) {\n return { status: 'failed', error: timeoutMessage(state), state };\n }\n\n const outcome = await pollOnce();\n\n switch (outcome.status) {\n case 'complete':\n return { status: 'complete', result: outcome.result, state };\n case 'failed':\n return { status: 'failed', error: outcome.error, state };\n case 'slow_down': {\n const next: DeviceCodePollState = {\n ...state,\n slowDownResponses: state.slowDownResponses + 1,\n // RFC 8628 section 3.5: grow the interval by 5 seconds, unless the\n // server told us the interval to use.\n intervalMs:\n typeof outcome.intervalSeconds === 'number' && outcome.intervalSeconds > 0\n ? outcome.intervalSeconds * 1000\n : Math.max(1000, state.intervalMs + SLOW_DOWN_INTERVAL_INCREMENT_MS),\n };\n return { status: 'slow_down', nextPollMs: nextPollDelayMs(next, now), state: next };\n }\n case 'pending': {\n const next: DeviceCodePollState =\n typeof outcome.intervalSeconds === 'number' && outcome.intervalSeconds > 0\n ? { ...state, intervalMs: Math.max(1000, outcome.intervalSeconds * 1000) }\n : state;\n return { status: 'pending', nextPollMs: nextPollDelayMs(next, now), state: next };\n }\n }\n}\n\n/** Sleep that can be interrupted by an AbortSignal. */\nexport function abortableSleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) {\n reject(new Error('Login cancelled'));\n return;\n }\n\n let timeout: ReturnType<typeof setTimeout>;\n const onAbort = () => {\n clearTimeout(timeout);\n reject(new Error('Login cancelled'));\n };\n\n timeout = setTimeout(() => {\n signal?.removeEventListener('abort', onAbort);\n resolve();\n }, ms);\n\n signal?.addEventListener('abort', onAbort, { once: true });\n });\n}\n\n/**\n * Blocking poll loop for TUI flows: waits the appropriate interval between\n * polls, honors slow_down growth, aborts on the signal, and throws on\n * failure/timeout (with a clock-drift hint after slow_down responses).\n */\nexport async function pollDeviceCodeUntilComplete<T>(options: {\n state: DeviceCodePollState;\n pollOnce: () => Promise<DeviceCodePollOutcome<T>>;\n signal?: AbortSignal;\n /** Override the sleep implementation for tests. */\n sleep?: (ms: number, signal?: AbortSignal) => Promise<void>;\n}): Promise<T> {\n let state = options.state;\n const sleep = options.sleep ?? abortableSleep;\n\n while (true) {\n if (options.signal?.aborted) {\n throw new Error('Login cancelled');\n }\n if (Date.now() >= state.deadlineAt) {\n throw new Error(timeoutMessage(state));\n }\n\n await sleep(nextPollDelayMs(state), options.signal);\n\n const step = await stepDeviceCodePoll(state, options.pollOnce);\n state = step.state;\n\n if (step.status === 'complete') {\n return step.result;\n }\n if (step.status === 'failed') {\n throw new Error(step.error);\n }\n }\n}\n"],"mappings":"AAcA,MAAM,2BAA2B;AACjC,MAAM,mCAAmC;AACzC,MAAM,qCAAqC;AAC3C,MAAM,kCAAkC;AAejC,SAAS,0BAA0B,SAOlB;AACtB,QAAM,MAAM,QAAQ,OAAO,KAAK,IAAI;AACpC,QAAM,kBACJ,OAAO,QAAQ,oBAAoB,YAAY,QAAQ,kBAAkB,IACrE,QAAQ,kBACR;AACN,SAAO;AAAA,IACL,YAAY,MAAM,QAAQ,mBAAmB;AAAA,IAC7C,YAAY,KAAK,IAAI,KAAM,KAAK,MAAM,kBAAkB,GAAI,CAAC;AAAA,IAC7D,mBAAmB;AAAA,EACrB;AACF;AAkBA,SAAS,eAAe,OAAoC;AAC1D,MAAI,MAAM,oBAAoB,GAAG;AAG/B,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAMO,SAAS,gBAAgB,OAA4B,MAAc,KAAK,IAAI,GAAW;AAC5F,QAAM,aACJ,MAAM,oBAAoB,IAAI,qCAAqC;AACrE,QAAM,cAAc,KAAK,IAAI,GAAG,MAAM,aAAa,GAAG;AACtD,SAAO,KAAK,IAAI,KAAK,KAAK,MAAM,aAAa,UAAU,GAAG,WAAW;AACvE;AAOA,eAAsB,mBACpB,OACA,UACA,MAAc,KAAK,IAAI,GACW;AAClC,MAAI,OAAO,MAAM,YAAY;AAC3B,WAAO,EAAE,QAAQ,UAAU,OAAO,eAAe,KAAK,GAAG,MAAM;AAAA,EACjE;AAEA,QAAM,UAAU,MAAM,SAAS;AAE/B,UAAQ,QAAQ,QAAQ;AAAA,IACtB,KAAK;AACH,aAAO,EAAE,QAAQ,YAAY,QAAQ,QAAQ,QAAQ,MAAM;AAAA,IAC7D,KAAK;AACH,aAAO,EAAE,QAAQ,UAAU,OAAO,QAAQ,OAAO,MAAM;AAAA,IACzD,KAAK,aAAa;AAChB,YAAM,OAA4B;AAAA,QAChC,GAAG;AAAA,QACH,mBAAmB,MAAM,oBAAoB;AAAA;AAAA;AAAA,QAG7C,YACE,OAAO,QAAQ,oBAAoB,YAAY,QAAQ,kBAAkB,IACrE,QAAQ,kBAAkB,MAC1B,KAAK,IAAI,KAAM,MAAM,aAAa,+BAA+B;AAAA,MACzE;AACA,aAAO,EAAE,QAAQ,aAAa,YAAY,gBAAgB,MAAM,GAAG,GAAG,OAAO,KAAK;AAAA,IACpF;AAAA,IACA,KAAK,WAAW;AACd,YAAM,OACJ,OAAO,QAAQ,oBAAoB,YAAY,QAAQ,kBAAkB,IACrE,EAAE,GAAG,OAAO,YAAY,KAAK,IAAI,KAAM,QAAQ,kBAAkB,GAAI,EAAE,IACvE;AACN,aAAO,EAAE,QAAQ,WAAW,YAAY,gBAAgB,MAAM,GAAG,GAAG,OAAO,KAAK;AAAA,IAClF;AAAA,EACF;AACF;AAGO,SAAS,eAAe,IAAY,QAAqC;AAC9E,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,QAAI,QAAQ,SAAS;AACnB,aAAO,IAAI,MAAM,iBAAiB,CAAC;AACnC;AAAA,IACF;AAEA,QAAI;AACJ,UAAM,UAAU,MAAM;AACpB,mBAAa,OAAO;AACpB,aAAO,IAAI,MAAM,iBAAiB,CAAC;AAAA,IACrC;AAEA,cAAU,WAAW,MAAM;AACzB,cAAQ,oBAAoB,SAAS,OAAO;AAC5C,cAAQ;AAAA,IACV,GAAG,EAAE;AAEL,YAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAAA,EAC3D,CAAC;AACH;AAOA,eAAsB,4BAA+B,SAMtC;AACb,MAAI,QAAQ,QAAQ;AACpB,QAAM,QAAQ,QAAQ,SAAS;AAE/B,SAAO,MAAM;AACX,QAAI,QAAQ,QAAQ,SAAS;AAC3B,YAAM,IAAI,MAAM,iBAAiB;AAAA,IACnC;AACA,QAAI,KAAK,IAAI,KAAK,MAAM,YAAY;AAClC,YAAM,IAAI,MAAM,eAAe,KAAK,CAAC;AAAA,IACvC;AAEA,UAAM,MAAM,gBAAgB,KAAK,GAAG,QAAQ,MAAM;AAElD,UAAM,OAAO,MAAM,mBAAmB,OAAO,QAAQ,QAAQ;AAC7D,YAAQ,KAAK;AAEb,QAAI,KAAK,WAAW,YAAY;AAC9B,aAAO,KAAK;AAAA,IACd;AACA,QAAI,KAAK,WAAW,UAAU;AAC5B,YAAM,IAAI,MAAM,KAAK,KAAK;AAAA,IAC5B;AAAA,EACF;AACF;","names":[]}
|
package/dist/auth/index.d.ts
CHANGED
|
@@ -6,4 +6,5 @@ export * from './storage.js';
|
|
|
6
6
|
export { anthropicOAuthProvider } from './providers/anthropic.js';
|
|
7
7
|
export { githubCopilotOAuthProvider } from './providers/github-copilot.js';
|
|
8
8
|
export { openaiCodexOAuthProvider } from './providers/openai-codex.js';
|
|
9
|
+
export { xaiOAuthProvider } from './providers/xai.js';
|
|
9
10
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/auth/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/auth/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC"}
|
package/dist/auth/index.js
CHANGED
|
@@ -3,9 +3,11 @@ export * from "./storage.js";
|
|
|
3
3
|
import { anthropicOAuthProvider } from "./providers/anthropic.js";
|
|
4
4
|
import { githubCopilotOAuthProvider } from "./providers/github-copilot.js";
|
|
5
5
|
import { openaiCodexOAuthProvider } from "./providers/openai-codex.js";
|
|
6
|
+
import { xaiOAuthProvider } from "./providers/xai.js";
|
|
6
7
|
export {
|
|
7
8
|
anthropicOAuthProvider,
|
|
8
9
|
githubCopilotOAuthProvider,
|
|
9
|
-
openaiCodexOAuthProvider
|
|
10
|
+
openaiCodexOAuthProvider,
|
|
11
|
+
xaiOAuthProvider
|
|
10
12
|
};
|
|
11
13
|
//# sourceMappingURL=index.js.map
|
package/dist/auth/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["/**\n * OAuth credential management for AI providers.\n */\n\nexport * from './types.js';\nexport * from './storage.js';\nexport { anthropicOAuthProvider } from './providers/anthropic.js';\nexport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nexport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\n"],"mappings":"AAIA,cAAc;AACd,cAAc;AACd,SAAS,8BAA8B;AACvC,SAAS,kCAAkC;AAC3C,SAAS,gCAAgC;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../src/auth/index.ts"],"sourcesContent":["/**\n * OAuth credential management for AI providers.\n */\n\nexport * from './types.js';\nexport * from './storage.js';\nexport { anthropicOAuthProvider } from './providers/anthropic.js';\nexport { githubCopilotOAuthProvider } from './providers/github-copilot.js';\nexport { openaiCodexOAuthProvider } from './providers/openai-codex.js';\nexport { xaiOAuthProvider } from './providers/xai.js';\n"],"mappings":"AAIA,cAAc;AACd,cAAc;AACd,SAAS,8BAA8B;AACvC,SAAS,kCAAkC;AAC3C,SAAS,gCAAgC;AACzC,SAAS,wBAAwB;","names":[]}
|
|
@@ -3,10 +3,33 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Inspired by pi-mono's OAuth implementation:
|
|
5
5
|
* https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/anthropic.ts
|
|
6
|
+
*
|
|
7
|
+
* The flow is a paste-code PKCE flow: the redirect lands on Anthropic's hosted
|
|
8
|
+
* callback page which displays `code#state` for the user to paste back. That
|
|
9
|
+
* makes it deployable without any inbound connection to the server, so the
|
|
10
|
+
* primitives are split into `startAnthropicLogin()` / `completeAnthropicLogin()`
|
|
11
|
+
* which can span separate HTTP requests (only the PKCE verifier needs to be
|
|
12
|
+
* persisted in between).
|
|
6
13
|
*/
|
|
7
14
|
import type { OAuthCredentials, OAuthProviderInterface } from '../types.js';
|
|
15
|
+
export interface AnthropicLoginStart {
|
|
16
|
+
/** Authorization URL for the user to open. */
|
|
17
|
+
url: string;
|
|
18
|
+
/** PKCE code verifier — persist it to complete the login later. */
|
|
19
|
+
verifier: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Start an Anthropic login: generate PKCE state and build the authorization URL.
|
|
23
|
+
*/
|
|
24
|
+
export declare function startAnthropicLogin(): Promise<AnthropicLoginStart>;
|
|
25
|
+
/**
|
|
26
|
+
* Complete an Anthropic login: parse the pasted authorization input
|
|
27
|
+
* (full URL, `code#state`, or query string), validate its state, and exchange
|
|
28
|
+
* it for tokens using the verifier from `startAnthropicLogin()`.
|
|
29
|
+
*/
|
|
30
|
+
export declare function completeAnthropicLogin(input: string, verifier: string): Promise<OAuthCredentials>;
|
|
8
31
|
/**
|
|
9
|
-
* Login with Anthropic OAuth (
|
|
32
|
+
* Login with Anthropic OAuth (paste-code flow), blocking on the prompt callback.
|
|
10
33
|
*/
|
|
11
34
|
export declare function loginAnthropic(onAuthUrl: (url: string) => void, onPromptCode: () => Promise<string>): Promise<OAuthCredentials>;
|
|
12
35
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/anthropic.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"anthropic.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/anthropic.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAYjG,MAAM,WAAW,mBAAmB;IAClC,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,mEAAmE;IACnE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,mBAAmB,CAAC,CAexE;AAED;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2CvG;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAChC,YAAY,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,GAClC,OAAO,CAAC,gBAAgB,CAAC,CAU3B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA2B3F;AAED,eAAO,MAAM,sBAAsB,EAAE,sBAkBpC,CAAC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parseAuthorizationInput } from "../authorization-input.js";
|
|
1
2
|
import { generatePKCE } from "../pkce.js";
|
|
2
3
|
const decode = (s) => atob(s);
|
|
3
4
|
const CLIENT_ID = decode("OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl");
|
|
@@ -5,7 +6,7 @@ const AUTHORIZE_URL = "https://claude.ai/oauth/authorize";
|
|
|
5
6
|
const TOKEN_URL = "https://console.anthropic.com/v1/oauth/token";
|
|
6
7
|
const REDIRECT_URI = "https://console.anthropic.com/oauth/code/callback";
|
|
7
8
|
const SCOPES = "org:create_api_key user:profile user:inference";
|
|
8
|
-
async function
|
|
9
|
+
async function startAnthropicLogin() {
|
|
9
10
|
const { verifier, challenge } = await generatePKCE();
|
|
10
11
|
const authParams = new URLSearchParams({
|
|
11
12
|
code: "true",
|
|
@@ -17,12 +18,16 @@ async function loginAnthropic(onAuthUrl, onPromptCode) {
|
|
|
17
18
|
code_challenge_method: "S256",
|
|
18
19
|
state: verifier
|
|
19
20
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
return { url: `${AUTHORIZE_URL}?${authParams.toString()}`, verifier };
|
|
22
|
+
}
|
|
23
|
+
async function completeAnthropicLogin(input, verifier) {
|
|
24
|
+
const { code, state } = parseAuthorizationInput(input);
|
|
25
|
+
if (!code) {
|
|
26
|
+
throw new Error("Missing authorization code");
|
|
27
|
+
}
|
|
28
|
+
if (!state || state !== verifier) {
|
|
29
|
+
throw new Error("Invalid authorization state");
|
|
30
|
+
}
|
|
26
31
|
const tokenResponse = await fetch(TOKEN_URL, {
|
|
27
32
|
method: "POST",
|
|
28
33
|
headers: {
|
|
@@ -49,6 +54,12 @@ async function loginAnthropic(onAuthUrl, onPromptCode) {
|
|
|
49
54
|
expires: expiresAt
|
|
50
55
|
};
|
|
51
56
|
}
|
|
57
|
+
async function loginAnthropic(onAuthUrl, onPromptCode) {
|
|
58
|
+
const { url, verifier } = await startAnthropicLogin();
|
|
59
|
+
onAuthUrl(url);
|
|
60
|
+
const authCode = await onPromptCode();
|
|
61
|
+
return completeAnthropicLogin(authCode, verifier);
|
|
62
|
+
}
|
|
52
63
|
async function refreshAnthropicToken(refreshToken) {
|
|
53
64
|
const response = await fetch(TOKEN_URL, {
|
|
54
65
|
method: "POST",
|
|
@@ -88,7 +99,9 @@ const anthropicOAuthProvider = {
|
|
|
88
99
|
};
|
|
89
100
|
export {
|
|
90
101
|
anthropicOAuthProvider,
|
|
102
|
+
completeAnthropicLogin,
|
|
91
103
|
loginAnthropic,
|
|
92
|
-
refreshAnthropicToken
|
|
104
|
+
refreshAnthropicToken,
|
|
105
|
+
startAnthropicLogin
|
|
93
106
|
};
|
|
94
107
|
//# sourceMappingURL=anthropic.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/auth/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n *\n * Inspired by pi-mono's OAuth implementation:\n * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/anthropic.ts\n */\n\nimport { generatePKCE } from '../pkce.js';\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from '../types.js';\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode('OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl');\nconst AUTHORIZE_URL = 'https://claude.ai/oauth/authorize';\nconst TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';\nconst REDIRECT_URI = 'https://console.anthropic.com/oauth/code/callback';\nconst SCOPES = 'org:create_api_key user:profile user:inference';\n\n
|
|
1
|
+
{"version":3,"sources":["../../../src/auth/providers/anthropic.ts"],"sourcesContent":["/**\n * Anthropic OAuth flow (Claude Pro/Max)\n *\n * Inspired by pi-mono's OAuth implementation:\n * https://github.com/badlogic/pi-mono/blob/main/packages/ai/src/utils/oauth/anthropic.ts\n *\n * The flow is a paste-code PKCE flow: the redirect lands on Anthropic's hosted\n * callback page which displays `code#state` for the user to paste back. That\n * makes it deployable without any inbound connection to the server, so the\n * primitives are split into `startAnthropicLogin()` / `completeAnthropicLogin()`\n * which can span separate HTTP requests (only the PKCE verifier needs to be\n * persisted in between).\n */\n\nimport { parseAuthorizationInput } from '../authorization-input.js';\nimport { generatePKCE } from '../pkce.js';\nimport type { OAuthCredentials, OAuthLoginCallbacks, OAuthProviderInterface } from '../types.js';\n\nconst decode = (s: string) => atob(s);\nconst CLIENT_ID = decode('OWQxYzI1MGEtZTYxYi00NGQ5LTg4ZWQtNTk0NGQxOTYyZjVl');\nconst AUTHORIZE_URL = 'https://claude.ai/oauth/authorize';\n// pi-mono uses `https://platform.claude.com/v1/oauth/token` with extra scopes\n// (user:sessions:claude_code, user:mcp_servers, user:file_upload); we keep the\n// console.anthropic.com endpoints that are known to work for our scope set.\nconst TOKEN_URL = 'https://console.anthropic.com/v1/oauth/token';\nconst REDIRECT_URI = 'https://console.anthropic.com/oauth/code/callback';\nconst SCOPES = 'org:create_api_key user:profile user:inference';\n\nexport interface AnthropicLoginStart {\n /** Authorization URL for the user to open. */\n url: string;\n /** PKCE code verifier — persist it to complete the login later. */\n verifier: string;\n}\n\n/**\n * Start an Anthropic login: generate PKCE state and build the authorization URL.\n */\nexport async function startAnthropicLogin(): Promise<AnthropicLoginStart> {\n const { verifier, challenge } = await generatePKCE();\n\n const authParams = new URLSearchParams({\n code: 'true',\n client_id: CLIENT_ID,\n response_type: 'code',\n redirect_uri: REDIRECT_URI,\n scope: SCOPES,\n code_challenge: challenge,\n code_challenge_method: 'S256',\n state: verifier,\n });\n\n return { url: `${AUTHORIZE_URL}?${authParams.toString()}`, verifier };\n}\n\n/**\n * Complete an Anthropic login: parse the pasted authorization input\n * (full URL, `code#state`, or query string), validate its state, and exchange\n * it for tokens using the verifier from `startAnthropicLogin()`.\n */\nexport async function completeAnthropicLogin(input: string, verifier: string): Promise<OAuthCredentials> {\n const { code, state } = parseAuthorizationInput(input);\n if (!code) {\n throw new Error('Missing authorization code');\n }\n if (!state || state !== verifier) {\n throw new Error('Invalid authorization state');\n }\n\n const tokenResponse = await fetch(TOKEN_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n grant_type: 'authorization_code',\n client_id: CLIENT_ID,\n code,\n state,\n redirect_uri: REDIRECT_URI,\n code_verifier: verifier,\n }),\n });\n\n if (!tokenResponse.ok) {\n const error = await tokenResponse.text();\n throw new Error(`Token exchange failed: ${error}`);\n }\n\n const tokenData = (await tokenResponse.json()) as {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n };\n\n // Calculate expiry time (current time + expires_in seconds - 5 min buffer)\n const expiresAt = Date.now() + tokenData.expires_in * 1000 - 5 * 60 * 1000;\n\n return {\n refresh: tokenData.refresh_token,\n access: tokenData.access_token,\n expires: expiresAt,\n };\n}\n\n/**\n * Login with Anthropic OAuth (paste-code flow), blocking on the prompt callback.\n */\nexport async function loginAnthropic(\n onAuthUrl: (url: string) => void,\n onPromptCode: () => Promise<string>,\n): Promise<OAuthCredentials> {\n const { url, verifier } = await startAnthropicLogin();\n\n // Notify caller with URL to open\n onAuthUrl(url);\n\n // Wait for user to paste authorization code (format: code#state)\n const authCode = await onPromptCode();\n\n return completeAnthropicLogin(authCode, verifier);\n}\n\n/**\n * Refresh Anthropic OAuth token\n */\nexport async function refreshAnthropicToken(refreshToken: string): Promise<OAuthCredentials> {\n const response = await fetch(TOKEN_URL, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n client_id: CLIENT_ID,\n refresh_token: refreshToken,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Anthropic token refresh failed: ${error}`);\n }\n\n const data = (await response.json()) as {\n access_token: string;\n refresh_token: string;\n expires_in: number;\n };\n\n return {\n refresh: data.refresh_token,\n access: data.access_token,\n expires: Date.now() + data.expires_in * 1000 - 5 * 60 * 1000,\n };\n}\n\nexport const anthropicOAuthProvider: OAuthProviderInterface = {\n id: 'anthropic',\n name: 'Anthropic (Claude Pro/Max)',\n\n async login(callbacks: OAuthLoginCallbacks): Promise<OAuthCredentials> {\n return loginAnthropic(\n url => callbacks.onAuth({ url }),\n () => callbacks.onPrompt({ message: 'Paste the authorization code:' }),\n );\n },\n\n async refreshToken(credentials: OAuthCredentials): Promise<OAuthCredentials> {\n return refreshAnthropicToken(credentials.refresh);\n },\n\n getApiKey(credentials: OAuthCredentials): string {\n return credentials.access;\n },\n};\n"],"mappings":"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","names":[]}
|
|
@@ -33,6 +33,60 @@ export declare function normalizeDomain(input: string): string | null;
|
|
|
33
33
|
* Prefers the `proxy-ep` parsed from the bearer token, then falls back to enterprise/individual defaults.
|
|
34
34
|
*/
|
|
35
35
|
export declare function getGitHubCopilotBaseUrl(token?: string, enterpriseDomain?: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Serializable pending state for a Copilot device-code login. Safe to persist
|
|
38
|
+
* (e.g. a `pending jsonb` column) so polling can span HTTP requests/replicas.
|
|
39
|
+
*/
|
|
40
|
+
export interface CopilotDeviceLoginPending {
|
|
41
|
+
/** GitHub host performing the flow (`github.com` or an Enterprise hostname). */
|
|
42
|
+
domain: string;
|
|
43
|
+
/** Enterprise hostname, or undefined for github.com. Recorded on the final credentials. */
|
|
44
|
+
enterpriseDomain?: string;
|
|
45
|
+
deviceCode: string;
|
|
46
|
+
userCode: string;
|
|
47
|
+
/** Verification URL for the user to open. */
|
|
48
|
+
url: string;
|
|
49
|
+
instructions: string;
|
|
50
|
+
/** Current base poll interval in ms (grows on slow_down). */
|
|
51
|
+
intervalMs: number;
|
|
52
|
+
/** Safety margin applied to intervalMs when computing the next wait. */
|
|
53
|
+
intervalMultiplier: number;
|
|
54
|
+
/** Number of slow_down responses seen (used for the clock-drift hint). */
|
|
55
|
+
slowDownCount: number;
|
|
56
|
+
/** ms epoch after which the device authorization expires. */
|
|
57
|
+
deadlineAt: number;
|
|
58
|
+
}
|
|
59
|
+
export type CopilotDevicePollResult = {
|
|
60
|
+
status: 'complete';
|
|
61
|
+
credentials: GitHubCopilotCredentials;
|
|
62
|
+
} | {
|
|
63
|
+
status: 'pending';
|
|
64
|
+
pending: CopilotDeviceLoginPending;
|
|
65
|
+
nextPollMs: number;
|
|
66
|
+
} | {
|
|
67
|
+
status: 'failed';
|
|
68
|
+
error: string;
|
|
69
|
+
};
|
|
70
|
+
/** Delay to wait before the next upstream poll for a pending Copilot device login. */
|
|
71
|
+
export declare function copilotNextPollDelayMs(pending: CopilotDeviceLoginPending): number;
|
|
72
|
+
/**
|
|
73
|
+
* Start a Copilot device-code login: request a user code and return the
|
|
74
|
+
* serializable pending state for subsequent polls. Wait `copilotNextPollDelayMs`
|
|
75
|
+
* before the first poll.
|
|
76
|
+
*/
|
|
77
|
+
export declare function startGitHubCopilotDeviceLogin(enterpriseDomain?: string, options?: {
|
|
78
|
+
signal?: AbortSignal;
|
|
79
|
+
}): Promise<CopilotDeviceLoginPending>;
|
|
80
|
+
/**
|
|
81
|
+
* Perform exactly one upstream poll for a pending Copilot device login.
|
|
82
|
+
* On success, exchanges the GitHub OAuth token for a Copilot bearer token and
|
|
83
|
+
* returns full credentials. Never throws for flow-level conditions; network
|
|
84
|
+
* errors from fetch still propagate.
|
|
85
|
+
*/
|
|
86
|
+
export declare function pollGitHubCopilotDeviceLogin(pending: CopilotDeviceLoginPending, options?: {
|
|
87
|
+
signal?: AbortSignal;
|
|
88
|
+
onProgress?: (message: string) => void;
|
|
89
|
+
}): Promise<CopilotDevicePollResult>;
|
|
36
90
|
/**
|
|
37
91
|
* Refresh the short-lived Copilot bearer token using the long-lived GitHub OAuth token.
|
|
38
92
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/github-copilot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAuB,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAEjG,MAAM,MAAM,wBAAwB,GAAG,gBAAgB,GAAG;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAMF,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAE7D,wEAAwE;AACxE,eAAO,MAAM,eAAe;;;;;CAKlB,CAAC;AAyBX;;;GAGG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAS5D;AA0BD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM,GAAG,MAAM,CAOzF;
|
|
1
|
+
{"version":3,"file":"github-copilot.d.ts","sourceRoot":"","sources":["../../../src/auth/providers/github-copilot.ts"],"names":[],"mappings":"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"}
|