@duetso/agent 0.2.6 → 0.3.1
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/README.md +29 -16
- package/dist/package.json +8 -3
- package/dist/src/cli/auto-upgrade.d.ts.map +1 -1
- package/dist/src/cli/auto-upgrade.js +7 -54
- package/dist/src/cli/auto-upgrade.js.map +1 -1
- package/dist/src/cli/connect.d.ts +45 -0
- package/dist/src/cli/connect.d.ts.map +1 -0
- package/dist/src/cli/connect.js +265 -0
- package/dist/src/cli/connect.js.map +1 -0
- package/dist/src/cli/help.d.ts +1 -0
- package/dist/src/cli/help.d.ts.map +1 -1
- package/dist/src/cli/help.js +66 -32
- package/dist/src/cli/help.js.map +1 -1
- package/dist/src/cli/memory-add.d.ts +18 -13
- package/dist/src/cli/memory-add.d.ts.map +1 -1
- package/dist/src/cli/memory-add.js +91 -32
- package/dist/src/cli/memory-add.js.map +1 -1
- package/dist/src/cli/memory-recall.d.ts.map +1 -1
- package/dist/src/cli/memory-recall.js +3 -0
- package/dist/src/cli/memory-recall.js.map +1 -1
- package/dist/src/cli/memory-sources.d.ts +20 -0
- package/dist/src/cli/memory-sources.d.ts.map +1 -0
- package/dist/src/cli/memory-sources.js +40 -0
- package/dist/src/cli/memory-sources.js.map +1 -0
- package/dist/src/cli/model.d.ts +5 -0
- package/dist/src/cli/model.d.ts.map +1 -1
- package/dist/src/cli/model.js +43 -0
- package/dist/src/cli/model.js.map +1 -1
- package/dist/src/cli/route.d.ts +12 -0
- package/dist/src/cli/route.d.ts.map +1 -1
- package/dist/src/cli/route.js +39 -9
- package/dist/src/cli/route.js.map +1 -1
- package/dist/src/cli/rpc.d.ts +3 -2
- package/dist/src/cli/rpc.d.ts.map +1 -1
- package/dist/src/cli/rpc.js +3 -2
- package/dist/src/cli/rpc.js.map +1 -1
- package/dist/src/cli/run.d.ts +1 -0
- package/dist/src/cli/run.d.ts.map +1 -1
- package/dist/src/cli/run.js +1 -0
- package/dist/src/cli/run.js.map +1 -1
- package/dist/src/cli/shared.d.ts +11 -0
- package/dist/src/cli/shared.d.ts.map +1 -1
- package/dist/src/cli/shared.js +31 -0
- package/dist/src/cli/shared.js.map +1 -1
- package/dist/src/cli/train.d.ts +31 -7
- package/dist/src/cli/train.d.ts.map +1 -1
- package/dist/src/cli/train.js +233 -79
- package/dist/src/cli/train.js.map +1 -1
- package/dist/src/cli.d.ts +3 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +9 -1
- package/dist/src/cli.js.map +1 -1
- package/dist/src/connected-providers/billing.d.ts +6 -0
- package/dist/src/connected-providers/billing.d.ts.map +1 -0
- package/dist/src/connected-providers/billing.js +11 -0
- package/dist/src/connected-providers/billing.js.map +1 -0
- package/dist/src/connected-providers/capability-probe.d.ts +17 -0
- package/dist/src/connected-providers/capability-probe.d.ts.map +1 -0
- package/dist/src/connected-providers/capability-probe.js +92 -0
- package/dist/src/connected-providers/capability-probe.js.map +1 -0
- package/dist/src/connected-providers/fake-issuer.d.ts +61 -0
- package/dist/src/connected-providers/fake-issuer.d.ts.map +1 -0
- package/dist/src/connected-providers/fake-issuer.js +161 -0
- package/dist/src/connected-providers/fake-issuer.js.map +1 -0
- package/dist/src/connected-providers/fallback-notice.d.ts +11 -0
- package/dist/src/connected-providers/fallback-notice.d.ts.map +1 -0
- package/dist/src/connected-providers/fallback-notice.js +20 -0
- package/dist/src/connected-providers/fallback-notice.js.map +1 -0
- package/dist/src/connected-providers/fallback.d.ts +11 -0
- package/dist/src/connected-providers/fallback.d.ts.map +1 -0
- package/dist/src/connected-providers/fallback.js +37 -0
- package/dist/src/connected-providers/fallback.js.map +1 -0
- package/dist/src/connected-providers/refresh.d.ts +12 -0
- package/dist/src/connected-providers/refresh.d.ts.map +1 -0
- package/dist/src/connected-providers/refresh.js +72 -0
- package/dist/src/connected-providers/refresh.js.map +1 -0
- package/dist/src/connected-providers/registry.d.ts +17 -0
- package/dist/src/connected-providers/registry.d.ts.map +1 -0
- package/dist/src/connected-providers/registry.js +30 -0
- package/dist/src/connected-providers/registry.js.map +1 -0
- package/dist/src/connected-providers/store.d.ts +41 -0
- package/dist/src/connected-providers/store.d.ts.map +1 -0
- package/dist/src/connected-providers/store.js +145 -0
- package/dist/src/connected-providers/store.js.map +1 -0
- package/dist/src/connected-providers/tokens.d.ts +42 -0
- package/dist/src/connected-providers/tokens.d.ts.map +1 -0
- package/dist/src/connected-providers/tokens.js +148 -0
- package/dist/src/connected-providers/tokens.js.map +1 -0
- package/dist/src/connected-providers/transport-preference.d.ts +26 -0
- package/dist/src/connected-providers/transport-preference.d.ts.map +1 -0
- package/dist/src/connected-providers/transport-preference.js +42 -0
- package/dist/src/connected-providers/transport-preference.js.map +1 -0
- package/dist/src/core/structured-output.d.ts.map +1 -1
- package/dist/src/core/structured-output.js +2 -1
- package/dist/src/core/structured-output.js.map +1 -1
- package/dist/src/file-lock.d.ts +22 -0
- package/dist/src/file-lock.d.ts.map +1 -0
- package/dist/src/file-lock.js +69 -0
- package/dist/src/file-lock.js.map +1 -0
- package/dist/src/memory/context-pack.d.ts +5 -0
- package/dist/src/memory/context-pack.d.ts.map +1 -1
- package/dist/src/memory/context-pack.js +6 -0
- package/dist/src/memory/context-pack.js.map +1 -1
- package/dist/src/memory/id.d.ts +3 -0
- package/dist/src/memory/id.d.ts.map +1 -0
- package/dist/src/memory/id.js +6 -0
- package/dist/src/memory/id.js.map +1 -0
- package/dist/src/memory/observational-prompts.d.ts +2 -0
- package/dist/src/memory/observational-prompts.d.ts.map +1 -1
- package/dist/src/memory/observational-prompts.js +4 -2
- package/dist/src/memory/observational-prompts.js.map +1 -1
- package/dist/src/memory/observational.d.ts +22 -0
- package/dist/src/memory/observational.d.ts.map +1 -1
- package/dist/src/memory/observational.js +65 -22
- package/dist/src/memory/observational.js.map +1 -1
- package/dist/src/memory/paths.d.ts +9 -0
- package/dist/src/memory/paths.d.ts.map +1 -0
- package/dist/src/memory/paths.js +11 -0
- package/dist/src/memory/paths.js.map +1 -0
- package/dist/src/memory/storage.d.ts.map +1 -1
- package/dist/src/memory/storage.js +1 -4
- package/dist/src/memory/storage.js.map +1 -1
- package/dist/src/memory/store/discovery.d.ts +7 -0
- package/dist/src/memory/store/discovery.d.ts.map +1 -0
- package/dist/src/memory/store/discovery.js +34 -0
- package/dist/src/memory/store/discovery.js.map +1 -0
- package/dist/src/memory/store/file.d.ts +52 -0
- package/dist/src/memory/store/file.d.ts.map +1 -0
- package/dist/src/memory/store/file.js +287 -0
- package/dist/src/memory/store/file.js.map +1 -0
- package/dist/src/memory/store/index.d.ts +5 -0
- package/dist/src/memory/store/index.d.ts.map +1 -0
- package/dist/src/memory/store/index.js +5 -0
- package/dist/src/memory/store/index.js.map +1 -0
- package/dist/src/memory/store/pack.d.ts +25 -0
- package/dist/src/memory/store/pack.d.ts.map +1 -0
- package/dist/src/memory/store/pack.js +71 -0
- package/dist/src/memory/store/pack.js.map +1 -0
- package/dist/src/memory/store/sources.d.ts +55 -0
- package/dist/src/memory/store/sources.d.ts.map +1 -0
- package/dist/src/memory/store/sources.js +85 -0
- package/dist/src/memory/store/sources.js.map +1 -0
- package/dist/src/memory/store/store.d.ts +46 -0
- package/dist/src/memory/store/store.d.ts.map +1 -0
- package/dist/src/memory/store/store.js +171 -0
- package/dist/src/memory/store/store.js.map +1 -0
- package/dist/src/memory/store.d.ts +19 -16
- package/dist/src/memory/store.d.ts.map +1 -1
- package/dist/src/memory/store.js +14 -11
- package/dist/src/memory/store.js.map +1 -1
- package/dist/src/memory-store.js +554 -0
- package/dist/src/model-resolution/catalog.d.ts +24 -10
- package/dist/src/model-resolution/catalog.d.ts.map +1 -1
- package/dist/src/model-resolution/catalog.js +61 -36
- package/dist/src/model-resolution/catalog.js.map +1 -1
- package/dist/src/model-resolution/duet-gateway.d.ts +2 -0
- package/dist/src/model-resolution/duet-gateway.d.ts.map +1 -1
- package/dist/src/model-resolution/duet-gateway.js +57 -4
- package/dist/src/model-resolution/duet-gateway.js.map +1 -1
- package/dist/src/model-resolution/resolver.d.ts +17 -0
- package/dist/src/model-resolution/resolver.d.ts.map +1 -1
- package/dist/src/model-resolution/resolver.js +49 -4
- package/dist/src/model-resolution/resolver.js.map +1 -1
- package/dist/src/model-routing/advisor-context.d.ts +31 -5
- package/dist/src/model-routing/advisor-context.d.ts.map +1 -1
- package/dist/src/model-routing/advisor-context.js +87 -10
- package/dist/src/model-routing/advisor-context.js.map +1 -1
- package/dist/src/model-routing/advisor-lifecycle.d.ts +14 -5
- package/dist/src/model-routing/advisor-lifecycle.d.ts.map +1 -1
- package/dist/src/model-routing/advisor-lifecycle.js +24 -7
- package/dist/src/model-routing/advisor-lifecycle.js.map +1 -1
- package/dist/src/model-routing/prompts.d.ts.map +1 -1
- package/dist/src/model-routing/prompts.js +18 -10
- package/dist/src/model-routing/prompts.js.map +1 -1
- package/dist/src/model-routing/table.js +18 -18
- package/dist/src/model-routing/table.js.map +1 -1
- package/dist/src/session/session-manager.d.ts +1 -4
- package/dist/src/session/session-manager.d.ts.map +1 -1
- package/dist/src/session/session-manager.js +2 -5
- package/dist/src/session/session-manager.js.map +1 -1
- package/dist/src/session/session.d.ts +2 -2
- package/dist/src/session/session.d.ts.map +1 -1
- package/dist/src/session/session.js +10 -7
- package/dist/src/session/session.js.map +1 -1
- package/dist/src/train/archive.d.ts.map +1 -1
- package/dist/src/train/archive.js +5 -0
- package/dist/src/train/archive.js.map +1 -1
- package/dist/src/train/types.d.ts +8 -7
- package/dist/src/train/types.d.ts.map +1 -1
- package/dist/src/tui/sidebar.d.ts.map +1 -1
- package/dist/src/tui/sidebar.js +40 -1
- package/dist/src/tui/sidebar.js.map +1 -1
- package/dist/src/turn-runner/agent-events.d.ts +2 -0
- package/dist/src/turn-runner/agent-events.d.ts.map +1 -1
- package/dist/src/turn-runner/agent-events.js +11 -0
- package/dist/src/turn-runner/agent-events.js.map +1 -1
- package/dist/src/turn-runner/subagent.d.ts +4 -1
- package/dist/src/turn-runner/subagent.d.ts.map +1 -1
- package/dist/src/turn-runner/subagent.js +11 -3
- package/dist/src/turn-runner/subagent.js.map +1 -1
- package/dist/src/turn-runner/tools.d.ts +3 -3
- package/dist/src/turn-runner/tools.d.ts.map +1 -1
- package/dist/src/turn-runner/tools.js +1 -1
- package/dist/src/turn-runner/tools.js.map +1 -1
- package/dist/src/turn-runner/turn-runner.d.ts +19 -3
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +162 -33
- package/dist/src/turn-runner/turn-runner.js.map +1 -1
- package/dist/src/turn-runner/usage-accounting.d.ts +5 -2
- package/dist/src/turn-runner/usage-accounting.d.ts.map +1 -1
- package/dist/src/turn-runner/usage-accounting.js +18 -6
- package/dist/src/turn-runner/usage-accounting.js.map +1 -1
- package/dist/src/turn-runner/wire-shaping.d.ts +3 -1
- package/dist/src/turn-runner/wire-shaping.d.ts.map +1 -1
- package/dist/src/turn-runner/wire-shaping.js +5 -1
- package/dist/src/turn-runner/wire-shaping.js.map +1 -1
- package/dist/src/types/config.d.ts +7 -0
- package/dist/src/types/config.d.ts.map +1 -1
- package/dist/src/types/protocol.d.ts +13 -5
- package/dist/src/types/protocol.d.ts.map +1 -1
- package/package.json +8 -3
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { githubCopilotOAuthProvider } from "@earendil-works/pi-ai/oauth";
|
|
2
|
+
const OPENAI_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
3
|
+
const OPENAI_CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
4
|
+
const OPENAI_CODEX_SCOPE = "openid profile email";
|
|
5
|
+
/** Refresh one connected-provider credential using the provider's supported OAuth flow. */
|
|
6
|
+
export async function refreshConnectedCredentials(provider, credentials) {
|
|
7
|
+
if (provider === "github-copilot") {
|
|
8
|
+
return githubCopilotOAuthProvider.refreshToken(credentials);
|
|
9
|
+
}
|
|
10
|
+
return refreshOpenAICodexCredentials(credentials.refresh);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Refresh ChatGPT credentials with the scope pi-ai currently omits. OpenAI
|
|
14
|
+
* rotates refresh tokens, so the caller must replace the stored credential
|
|
15
|
+
* object with this complete result rather than patching only `access`.
|
|
16
|
+
*/
|
|
17
|
+
export async function refreshOpenAICodexCredentials(refreshToken, fetchImpl = (input, init) => fetch(input, init), now = Date.now) {
|
|
18
|
+
const response = await fetchImpl(OPENAI_TOKEN_URL, {
|
|
19
|
+
method: "POST",
|
|
20
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
21
|
+
body: new URLSearchParams({
|
|
22
|
+
grant_type: "refresh_token",
|
|
23
|
+
refresh_token: refreshToken,
|
|
24
|
+
client_id: OPENAI_CODEX_CLIENT_ID,
|
|
25
|
+
scope: OPENAI_CODEX_SCOPE,
|
|
26
|
+
}),
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
const detail = await response.text().catch(() => "");
|
|
30
|
+
throw new Error(`OpenAI Codex token refresh failed (${response.status}): ${detail || response.statusText}`);
|
|
31
|
+
}
|
|
32
|
+
const value = await response.json();
|
|
33
|
+
if (!isTokenResponse(value)) {
|
|
34
|
+
throw new Error("OpenAI Codex token refresh response missing required fields");
|
|
35
|
+
}
|
|
36
|
+
const accountId = accountIdFromAccessToken(value.access_token);
|
|
37
|
+
if (!accountId)
|
|
38
|
+
throw new Error("Failed to extract accountId from refreshed OpenAI Codex token");
|
|
39
|
+
return {
|
|
40
|
+
access: value.access_token,
|
|
41
|
+
refresh: value.refresh_token,
|
|
42
|
+
expires: now() + value.expires_in * 1_000,
|
|
43
|
+
accountId,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function isTokenResponse(value) {
|
|
47
|
+
if (value === null || typeof value !== "object")
|
|
48
|
+
return false;
|
|
49
|
+
const token = value;
|
|
50
|
+
return (typeof token.access_token === "string" &&
|
|
51
|
+
typeof token.refresh_token === "string" &&
|
|
52
|
+
typeof token.expires_in === "number");
|
|
53
|
+
}
|
|
54
|
+
function accountIdFromAccessToken(accessToken) {
|
|
55
|
+
try {
|
|
56
|
+
const payload = accessToken.split(".")[1];
|
|
57
|
+
if (!payload)
|
|
58
|
+
return undefined;
|
|
59
|
+
const decoded = JSON.parse(Buffer.from(payload, "base64url").toString("utf8"));
|
|
60
|
+
if (decoded === null || typeof decoded !== "object")
|
|
61
|
+
return undefined;
|
|
62
|
+
const auth = decoded["https://api.openai.com/auth"];
|
|
63
|
+
if (auth === null || typeof auth !== "object")
|
|
64
|
+
return undefined;
|
|
65
|
+
const accountId = auth.chatgpt_account_id;
|
|
66
|
+
return typeof accountId === "string" && accountId ? accountId : undefined;
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=refresh.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.js","sourceRoot":"","sources":["../../../src/connected-providers/refresh.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type OAuthProviderInterface } from "@earendil-works/pi-ai/oauth";
|
|
2
|
+
import type { ConnectedProviderId } from "./store.js";
|
|
3
|
+
export interface ConnectedProviderEntry {
|
|
4
|
+
/** Stable transport id used by pi-ai and the credential store. */
|
|
5
|
+
id: ConnectedProviderId;
|
|
6
|
+
/** User-facing subscription name. */
|
|
7
|
+
label: string;
|
|
8
|
+
/** Short CLI value accepted by `duet connect`. */
|
|
9
|
+
alias: string;
|
|
10
|
+
/** Login modes this provider can run; only device code is valid in a VM. */
|
|
11
|
+
loginModes: readonly ("device_code" | "browser")[];
|
|
12
|
+
/** Resolve the active pi-ai implementation, including an env-gated fake issuer. */
|
|
13
|
+
oauth(): OAuthProviderInterface;
|
|
14
|
+
}
|
|
15
|
+
export declare function connectedProviders(): readonly ConnectedProviderEntry[];
|
|
16
|
+
export declare function resolveConnectedProviderAlias(input: string): ConnectedProviderId | undefined;
|
|
17
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAC5F,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,WAAW,sBAAsB;IACrC,kEAAkE;IAClE,EAAE,EAAE,mBAAmB,CAAC;IACxB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAC;IACd,4EAA4E;IAC5E,UAAU,EAAE,SAAS,CAAC,aAAa,GAAG,SAAS,CAAC,EAAE,CAAC;IACnD,mFAAmF;IACnF,KAAK,IAAI,sBAAsB,CAAC;CACjC;AAyBD,wBAAgB,kBAAkB,IAAI,SAAS,sBAAsB,EAAE,CAEtE;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS,CAE5F"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getOAuthProvider } from "@earendil-works/pi-ai/oauth";
|
|
2
|
+
function oauthProvider(id) {
|
|
3
|
+
const provider = getOAuthProvider(id);
|
|
4
|
+
if (!provider)
|
|
5
|
+
throw new Error(`OAuth provider is not registered: ${id}`);
|
|
6
|
+
return provider;
|
|
7
|
+
}
|
|
8
|
+
const CONNECTED_PROVIDERS = [
|
|
9
|
+
{
|
|
10
|
+
id: "openai-codex",
|
|
11
|
+
label: "ChatGPT",
|
|
12
|
+
alias: "chatgpt",
|
|
13
|
+
loginModes: ["device_code", "browser"],
|
|
14
|
+
oauth: () => oauthProvider("openai-codex"),
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: "github-copilot",
|
|
18
|
+
label: "GitHub Copilot",
|
|
19
|
+
alias: "copilot",
|
|
20
|
+
loginModes: ["device_code"],
|
|
21
|
+
oauth: () => oauthProvider("github-copilot"),
|
|
22
|
+
},
|
|
23
|
+
];
|
|
24
|
+
export function connectedProviders() {
|
|
25
|
+
return CONNECTED_PROVIDERS;
|
|
26
|
+
}
|
|
27
|
+
export function resolveConnectedProviderAlias(input) {
|
|
28
|
+
return CONNECTED_PROVIDERS.find(({ id, alias }) => input === id || input === alias)?.id;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/connected-providers/registry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAA+B,MAAM,6BAA6B,CAAC;AAgB5F,SAAS,aAAa,CAAC,EAAuB;IAC5C,MAAM,QAAQ,GAAG,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACtC,IAAI,CAAC,QAAQ;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,EAAE,EAAE,CAAC,CAAC;IAC1E,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,mBAAmB,GAAsC;IAC7D;QACE,EAAE,EAAE,cAAc;QAClB,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,SAAS;QAChB,UAAU,EAAE,CAAC,aAAa,EAAE,SAAS,CAAC;QACtC,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC;KAC3C;IACD;QACE,EAAE,EAAE,gBAAgB;QACpB,KAAK,EAAE,gBAAgB;QACvB,KAAK,EAAE,SAAS;QAChB,UAAU,EAAE,CAAC,aAAa,CAAC;QAC3B,KAAK,EAAE,GAAG,EAAE,CAAC,aAAa,CAAC,gBAAgB,CAAC;KAC7C;CACF,CAAC;AAEF,MAAM,UAAU,kBAAkB;IAChC,OAAO,mBAAmB,CAAC;AAC7B,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,KAAa;IACzD,OAAO,mBAAmB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,KAAK,EAAE,IAAI,KAAK,KAAK,KAAK,CAAC,EAAE,EAAE,CAAC;AAC1F,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { OAuthCredentials } from "@earendil-works/pi-ai/oauth";
|
|
2
|
+
export type { OAuthCredentials } from "@earendil-works/pi-ai/oauth";
|
|
3
|
+
export type ConnectedProviderId = "openai-codex" | "github-copilot";
|
|
4
|
+
export type ConnectionEligibility = "unknown" | "eligible" | "plan_ineligible";
|
|
5
|
+
/** Stable precedence for persisted records and connected transport selection. */
|
|
6
|
+
export declare const CONNECTED_PROVIDER_ORDER: readonly ConnectedProviderId[];
|
|
7
|
+
/** Narrow an arbitrary pi-ai provider id to one owned by the connected store. */
|
|
8
|
+
export declare function isConnectedProviderId(provider: string): provider is ConnectedProviderId;
|
|
9
|
+
export interface ConnectionRecord {
|
|
10
|
+
/** Transport whose subscription these credentials authorize. */
|
|
11
|
+
provider: ConnectedProviderId;
|
|
12
|
+
/** Complete pi-ai OAuth token set; refresh replaces this object wholesale. */
|
|
13
|
+
credentials: OAuthCredentials;
|
|
14
|
+
/** Epoch milliseconds when the user completed the connection flow. */
|
|
15
|
+
connectedAt: number;
|
|
16
|
+
/** Whether the linked subscription passed the provider capability probe. */
|
|
17
|
+
eligibility: ConnectionEligibility;
|
|
18
|
+
/** Epoch milliseconds of the latest capability probe, when one has run. */
|
|
19
|
+
eligibilityCheckedAt?: number;
|
|
20
|
+
/** Epoch milliseconds of the latest successful credential refresh. */
|
|
21
|
+
lastRefreshAt?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface ConnectedProviderStore {
|
|
24
|
+
/** Read every valid record without taking the writer lock. */
|
|
25
|
+
read(): Promise<ConnectionRecord[]>;
|
|
26
|
+
/** Read one provider's current record without taking the writer lock. */
|
|
27
|
+
get(id: ConnectedProviderId): Promise<ConnectionRecord | undefined>;
|
|
28
|
+
/** Delete one provider while preserving every other valid record. */
|
|
29
|
+
remove(id: ConnectedProviderId): Promise<void>;
|
|
30
|
+
/** Locked read-modify-write; mutate receives the current on-disk record. */
|
|
31
|
+
withLock<T>(id: ConnectedProviderId, mutate: (current: ConnectionRecord | undefined) => Promise<{
|
|
32
|
+
next?: ConnectionRecord;
|
|
33
|
+
result: T;
|
|
34
|
+
}>): Promise<T>;
|
|
35
|
+
}
|
|
36
|
+
export declare const CONNECTED_PROVIDERS_FILE = "connected-providers.json";
|
|
37
|
+
export declare function createConnectedProviderStore(opts?: {
|
|
38
|
+
homeDir?: string;
|
|
39
|
+
now?: () => number;
|
|
40
|
+
}): ConnectedProviderStore;
|
|
41
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/store.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAGpE,YAAY,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,MAAM,MAAM,mBAAmB,GAAG,cAAc,GAAG,gBAAgB,CAAC;AACpE,MAAM,MAAM,qBAAqB,GAAG,SAAS,GAAG,UAAU,GAAG,iBAAiB,CAAC;AAE/E,iFAAiF;AACjF,eAAO,MAAM,wBAAwB,EAAE,SAAS,mBAAmB,EAGlE,CAAC;AAEF,iFAAiF;AACjF,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,IAAI,mBAAmB,CAEvF;AAED,MAAM,WAAW,gBAAgB;IAC/B,gEAAgE;IAChE,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,8EAA8E;IAC9E,WAAW,EAAE,gBAAgB,CAAC;IAC9B,sEAAsE;IACtE,WAAW,EAAE,MAAM,CAAC;IACpB,4EAA4E;IAC5E,WAAW,EAAE,qBAAqB,CAAC;IACnC,2EAA2E;IAC3E,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,sBAAsB;IACrC,8DAA8D;IAC9D,IAAI,IAAI,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC;IACpC,yEAAyE;IACzE,GAAG,CAAC,EAAE,EAAE,mBAAmB,GAAG,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CAAC;IACpE,qEAAqE;IACrE,MAAM,CAAC,EAAE,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/C,4EAA4E;IAC5E,QAAQ,CAAC,CAAC,EACR,EAAE,EAAE,mBAAmB,EACvB,MAAM,EAAE,CACN,OAAO,EAAE,gBAAgB,GAAG,SAAS,KAClC,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,gBAAgB,CAAC;QAAC,MAAM,EAAE,CAAC,CAAA;KAAE,CAAC,GACnD,OAAO,CAAC,CAAC,CAAC,CAAC;CACf;AAED,eAAO,MAAM,wBAAwB,6BAA6B,CAAC;AAUnE,wBAAgB,4BAA4B,CAC1C,IAAI,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CAAO,GAClD,sBAAsB,CAkGxB"}
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { chmod, mkdir, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { acquireFileLock, releaseFileLock } from "../file-lock.js";
|
|
6
|
+
/** Stable precedence for persisted records and connected transport selection. */
|
|
7
|
+
export const CONNECTED_PROVIDER_ORDER = [
|
|
8
|
+
"openai-codex",
|
|
9
|
+
"github-copilot",
|
|
10
|
+
];
|
|
11
|
+
/** Narrow an arbitrary pi-ai provider id to one owned by the connected store. */
|
|
12
|
+
export function isConnectedProviderId(provider) {
|
|
13
|
+
return CONNECTED_PROVIDER_ORDER.some((candidate) => candidate === provider);
|
|
14
|
+
}
|
|
15
|
+
export const CONNECTED_PROVIDERS_FILE = "connected-providers.json";
|
|
16
|
+
const STORE_VERSION = 1;
|
|
17
|
+
const LOCK_RETRY_MS = 20;
|
|
18
|
+
export function createConnectedProviderStore(opts = {}) {
|
|
19
|
+
const duetDir = join(opts.homeDir ?? homedir(), ".duet");
|
|
20
|
+
const storePath = join(duetDir, CONNECTED_PROVIDERS_FILE);
|
|
21
|
+
const lockPath = join(duetDir, `${CONNECTED_PROVIDERS_FILE}.lock`);
|
|
22
|
+
const now = opts.now ?? (() => Date.now());
|
|
23
|
+
const readDocument = async () => {
|
|
24
|
+
let raw;
|
|
25
|
+
try {
|
|
26
|
+
raw = await readFile(storePath, "utf8");
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (hasCode(error, "ENOENT"))
|
|
30
|
+
return emptyDocument();
|
|
31
|
+
warnUnreadable(storePath);
|
|
32
|
+
return emptyDocument();
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const value = JSON.parse(raw);
|
|
36
|
+
if (!isRecord(value) || value.version !== STORE_VERSION || !isRecord(value.connections)) {
|
|
37
|
+
warnUnreadable(storePath);
|
|
38
|
+
return emptyDocument();
|
|
39
|
+
}
|
|
40
|
+
const connections = {};
|
|
41
|
+
for (const provider of CONNECTED_PROVIDER_ORDER) {
|
|
42
|
+
const candidate = value.connections[provider];
|
|
43
|
+
if (candidate === undefined)
|
|
44
|
+
continue;
|
|
45
|
+
if (isConnectionRecord(candidate, provider))
|
|
46
|
+
connections[provider] = candidate;
|
|
47
|
+
else
|
|
48
|
+
console.warn(`Ignoring invalid ${provider} record in ${storePath}.`);
|
|
49
|
+
}
|
|
50
|
+
return { version: STORE_VERSION, connections };
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
warnUnreadable(storePath);
|
|
54
|
+
return emptyDocument();
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
const writeDocument = async (document) => {
|
|
58
|
+
await mkdir(duetDir, { recursive: true, mode: 0o700 });
|
|
59
|
+
await chmod(duetDir, 0o700);
|
|
60
|
+
const tempPath = `${storePath}.${process.pid}.${randomUUID()}.tmp`;
|
|
61
|
+
try {
|
|
62
|
+
await writeFile(tempPath, `${JSON.stringify(document, null, 2)}\n`, { mode: 0o600 });
|
|
63
|
+
await chmod(tempPath, 0o600);
|
|
64
|
+
await rename(tempPath, storePath);
|
|
65
|
+
await chmod(storePath, 0o600);
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
await unlink(tempPath).catch(() => undefined);
|
|
69
|
+
throw error;
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
const withLock = async (id, mutate) => {
|
|
73
|
+
await mkdir(duetDir, { recursive: true, mode: 0o700 });
|
|
74
|
+
await chmod(duetDir, 0o700);
|
|
75
|
+
let handle = acquireFileLock(lockPath, { now: now() });
|
|
76
|
+
while (handle === null) {
|
|
77
|
+
await new Promise((resolve) => setTimeout(resolve, LOCK_RETRY_MS));
|
|
78
|
+
handle = acquireFileLock(lockPath, { now: now() });
|
|
79
|
+
}
|
|
80
|
+
try {
|
|
81
|
+
const document = await readDocument();
|
|
82
|
+
const { next, result } = await mutate(document.connections[id]);
|
|
83
|
+
if (next === undefined)
|
|
84
|
+
delete document.connections[id];
|
|
85
|
+
else {
|
|
86
|
+
if (!isConnectionRecord(next, id)) {
|
|
87
|
+
throw new Error(`Connection record must match lock provider ${id}`);
|
|
88
|
+
}
|
|
89
|
+
document.connections[id] = next;
|
|
90
|
+
}
|
|
91
|
+
await writeDocument(document);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
finally {
|
|
95
|
+
releaseFileLock(handle);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
return {
|
|
99
|
+
async read() {
|
|
100
|
+
const document = await readDocument();
|
|
101
|
+
return CONNECTED_PROVIDER_ORDER.flatMap((provider) => {
|
|
102
|
+
const connection = document.connections[provider];
|
|
103
|
+
return connection ? [connection] : [];
|
|
104
|
+
});
|
|
105
|
+
},
|
|
106
|
+
async get(id) {
|
|
107
|
+
return (await readDocument()).connections[id];
|
|
108
|
+
},
|
|
109
|
+
async remove(id) {
|
|
110
|
+
await withLock(id, async () => ({ result: undefined }));
|
|
111
|
+
},
|
|
112
|
+
withLock,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function emptyDocument() {
|
|
116
|
+
return { version: STORE_VERSION, connections: {} };
|
|
117
|
+
}
|
|
118
|
+
function warnUnreadable(path) {
|
|
119
|
+
console.warn(`Ignoring corrupt or unsupported connected-provider store at ${path}.`);
|
|
120
|
+
}
|
|
121
|
+
function isConnectionRecord(value, provider) {
|
|
122
|
+
if (!isRecord(value) || value.provider !== provider || !isRecord(value.credentials))
|
|
123
|
+
return false;
|
|
124
|
+
const credentials = value.credentials;
|
|
125
|
+
return (typeof credentials.access === "string" &&
|
|
126
|
+
typeof credentials.refresh === "string" &&
|
|
127
|
+
typeof credentials.expires === "number" &&
|
|
128
|
+
typeof value.connectedAt === "number" &&
|
|
129
|
+
isEligibility(value.eligibility) &&
|
|
130
|
+
isOptionalNumber(value.eligibilityCheckedAt) &&
|
|
131
|
+
isOptionalNumber(value.lastRefreshAt));
|
|
132
|
+
}
|
|
133
|
+
function isEligibility(value) {
|
|
134
|
+
return value === "unknown" || value === "eligible" || value === "plan_ineligible";
|
|
135
|
+
}
|
|
136
|
+
function isOptionalNumber(value) {
|
|
137
|
+
return value === undefined || typeof value === "number";
|
|
138
|
+
}
|
|
139
|
+
function isRecord(value) {
|
|
140
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
141
|
+
}
|
|
142
|
+
function hasCode(error, code) {
|
|
143
|
+
return error instanceof Error && "code" in error && error.code === code;
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/connected-providers/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type ConnectedProviderId, type ConnectedProviderStore, type ConnectionRecord, type OAuthCredentials } from "./store.js";
|
|
2
|
+
export interface ConnectedTokenManager {
|
|
3
|
+
/** Load the process-lifetime connection snapshot and seed every still-valid access token. */
|
|
4
|
+
loadSnapshot(): Promise<readonly ConnectionRecord[]>;
|
|
5
|
+
/** Refresh all usable providers discovered by the boot snapshot. */
|
|
6
|
+
ensureFreshTokens(): Promise<void>;
|
|
7
|
+
/** Return a fresh token, coalescing concurrent refreshes for this provider. */
|
|
8
|
+
ensureFreshToken(provider: ConnectedProviderId): Promise<string | undefined>;
|
|
9
|
+
/** Synchronously read a usable access token from the in-process cache. */
|
|
10
|
+
apiKey(provider: ConnectedProviderId): string | undefined;
|
|
11
|
+
/** Start a coalesced refresh without making a synchronous caller wait. */
|
|
12
|
+
refreshInBackground(provider: ConnectedProviderId): void;
|
|
13
|
+
/** Synchronously read the cached full credential set for a provider. */
|
|
14
|
+
credentials(provider: ConnectedProviderId): OAuthCredentials | undefined;
|
|
15
|
+
}
|
|
16
|
+
interface ConnectedTokenManagerOptions {
|
|
17
|
+
/** Durable source of credentials and cross-process refresh serialization. */
|
|
18
|
+
store: ConnectedProviderStore;
|
|
19
|
+
/** Clock used for independent provider expiry decisions. */
|
|
20
|
+
now?: () => number;
|
|
21
|
+
/** Provider refresh edge, injectable so tests never call live OAuth issuers. */
|
|
22
|
+
refreshCredentials?: (provider: ConnectedProviderId, credentials: OAuthCredentials) => Promise<OAuthCredentials>;
|
|
23
|
+
}
|
|
24
|
+
/** Build an isolated token cache around a connected-provider store. */
|
|
25
|
+
export declare function createConnectedTokenManager(options: ConnectedTokenManagerOptions): ConnectedTokenManager;
|
|
26
|
+
/**
|
|
27
|
+
* Run the provider's OAuth model hook against a resolved spec. Copilot
|
|
28
|
+
* credentials rewrite the endpoint for Enterprise accounts and filter models
|
|
29
|
+
* the account cannot serve; returns undefined when the account filters the
|
|
30
|
+
* model out, and the unhooked spec when no credentials are loaded.
|
|
31
|
+
*/
|
|
32
|
+
export declare function applyConnectedModelHook<T extends {
|
|
33
|
+
id: string;
|
|
34
|
+
}>(provider: ConnectedProviderId, model: T): T | undefined;
|
|
35
|
+
export declare function loadConnectedTokensSnapshot(): Promise<readonly ConnectionRecord[]>;
|
|
36
|
+
export declare function ensureFreshConnectedTokens(): Promise<void>;
|
|
37
|
+
export declare function connectedProviderApiKey(provider: ConnectedProviderId): string | undefined;
|
|
38
|
+
export declare function refreshConnectedTokenInBackground(provider: ConnectedProviderId): void;
|
|
39
|
+
/** Persist reconnect-needed eligibility without deleting or rewriting credentials. */
|
|
40
|
+
export declare function markConnectedProviderReconnectNeeded(provider: ConnectedProviderId): Promise<void>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=tokens.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/tokens.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACtB,MAAM,YAAY,CAAC;AAIpB,MAAM,WAAW,qBAAqB;IACpC,6FAA6F;IAC7F,YAAY,IAAI,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAAC;IACrD,oEAAoE;IACpE,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACnC,+EAA+E;IAC/E,gBAAgB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IAC7E,0EAA0E;IAC1E,MAAM,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAAC;IAC1D,0EAA0E;IAC1E,mBAAmB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACzD,wEAAwE;IACxE,WAAW,CAAC,QAAQ,EAAE,mBAAmB,GAAG,gBAAgB,GAAG,SAAS,CAAC;CAC1E;AAED,UAAU,4BAA4B;IACpC,6EAA6E;IAC7E,KAAK,EAAE,sBAAsB,CAAC;IAC9B,4DAA4D;IAC5D,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,CACnB,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,EAAE,gBAAgB,KAC1B,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAID,uEAAuE;AACvE,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,4BAA4B,GACpC,qBAAqB,CAwFvB;AAUD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAC9D,QAAQ,EAAE,mBAAmB,EAC7B,KAAK,EAAE,CAAC,GACP,CAAC,GAAG,SAAS,CASf;AAED,wBAAgB,2BAA2B,IAAI,OAAO,CAAC,SAAS,gBAAgB,EAAE,CAAC,CAElF;AAED,wBAAgB,0BAA0B,IAAI,OAAO,CAAC,IAAI,CAAC,CAE1D;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,mBAAmB,GAAG,MAAM,GAAG,SAAS,CAEzF;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,mBAAmB,GAAG,IAAI,CAErF;AAED,sFAAsF;AACtF,wBAAsB,oCAAoC,CACxD,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAAC,IAAI,CAAC,CAaf"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { refreshConnectedCredentials } from "./refresh.js";
|
|
2
|
+
import { connectedProviders } from "./registry.js";
|
|
3
|
+
import { createConnectedProviderStore, } from "./store.js";
|
|
4
|
+
const REFRESH_SKEW_MS = 60_000;
|
|
5
|
+
/** Build an isolated token cache around a connected-provider store. */
|
|
6
|
+
export function createConnectedTokenManager(options) {
|
|
7
|
+
const now = options.now ?? Date.now;
|
|
8
|
+
const refreshCredentials = options.refreshCredentials ?? refreshConnectedCredentials;
|
|
9
|
+
const cache = new Map();
|
|
10
|
+
const refreshes = new Map();
|
|
11
|
+
let bootConnections = [];
|
|
12
|
+
const isFresh = (credentials) => credentials.expires > now() + REFRESH_SKEW_MS;
|
|
13
|
+
const ensureFreshToken = (provider) => {
|
|
14
|
+
const cached = cache.get(provider);
|
|
15
|
+
if (cached && isFresh(cached))
|
|
16
|
+
return Promise.resolve(cached.access);
|
|
17
|
+
const active = refreshes.get(provider);
|
|
18
|
+
if (active)
|
|
19
|
+
return active;
|
|
20
|
+
const pending = options.store
|
|
21
|
+
.withLock(provider, async (current) => {
|
|
22
|
+
if (!current)
|
|
23
|
+
return { result: undefined };
|
|
24
|
+
if (isFresh(current.credentials)) {
|
|
25
|
+
return { next: current, result: { credentials: current.credentials } };
|
|
26
|
+
}
|
|
27
|
+
let credentials;
|
|
28
|
+
try {
|
|
29
|
+
credentials = await refreshCredentials(provider, current.credentials);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (!isRefreshAuthFailure(error))
|
|
33
|
+
throw error;
|
|
34
|
+
return {
|
|
35
|
+
next: {
|
|
36
|
+
...current,
|
|
37
|
+
eligibility: "unknown",
|
|
38
|
+
eligibilityCheckedAt: now(),
|
|
39
|
+
},
|
|
40
|
+
result: { error },
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const next = { ...current, credentials, lastRefreshAt: now() };
|
|
44
|
+
return { next, result: { credentials } };
|
|
45
|
+
})
|
|
46
|
+
.then((outcome) => {
|
|
47
|
+
if (!outcome) {
|
|
48
|
+
cache.delete(provider);
|
|
49
|
+
return undefined;
|
|
50
|
+
}
|
|
51
|
+
if ("error" in outcome)
|
|
52
|
+
throw outcome.error;
|
|
53
|
+
const credentials = outcome.credentials;
|
|
54
|
+
cache.set(provider, credentials);
|
|
55
|
+
return credentials.access;
|
|
56
|
+
})
|
|
57
|
+
.finally(() => {
|
|
58
|
+
if (refreshes.get(provider) === pending)
|
|
59
|
+
refreshes.delete(provider);
|
|
60
|
+
});
|
|
61
|
+
refreshes.set(provider, pending);
|
|
62
|
+
return pending;
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
async loadSnapshot() {
|
|
66
|
+
bootConnections = await options.store.read();
|
|
67
|
+
cache.clear();
|
|
68
|
+
for (const connection of bootConnections) {
|
|
69
|
+
if (isFresh(connection.credentials))
|
|
70
|
+
cache.set(connection.provider, connection.credentials);
|
|
71
|
+
}
|
|
72
|
+
return bootConnections;
|
|
73
|
+
},
|
|
74
|
+
async ensureFreshTokens() {
|
|
75
|
+
await Promise.all(bootConnections
|
|
76
|
+
.filter(({ eligibility }) => eligibility !== "plan_ineligible")
|
|
77
|
+
// A failed issuer refresh leaves this provider uncached. Synchronous
|
|
78
|
+
// model resolution then keeps the call on router order instead of
|
|
79
|
+
// making CLI turn startup fail before fallback can apply.
|
|
80
|
+
.map(({ provider }) => ensureFreshToken(provider).catch(() => undefined)));
|
|
81
|
+
},
|
|
82
|
+
ensureFreshToken,
|
|
83
|
+
apiKey(provider) {
|
|
84
|
+
const credentials = cache.get(provider);
|
|
85
|
+
if (!credentials || !isFresh(credentials))
|
|
86
|
+
return undefined;
|
|
87
|
+
return credentials.access;
|
|
88
|
+
},
|
|
89
|
+
refreshInBackground(provider) {
|
|
90
|
+
void ensureFreshToken(provider).catch(() => undefined);
|
|
91
|
+
},
|
|
92
|
+
credentials(provider) {
|
|
93
|
+
return cache.get(provider);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function isRefreshAuthFailure(error) {
|
|
98
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
99
|
+
return /\b401\b|\b403\b|unauthori[sz]ed|invalid.*(?:token|grant)|authentication/i.test(message);
|
|
100
|
+
}
|
|
101
|
+
const connectedStore = createConnectedProviderStore();
|
|
102
|
+
const connectedTokens = createConnectedTokenManager({ store: connectedStore });
|
|
103
|
+
/**
|
|
104
|
+
* Run the provider's OAuth model hook against a resolved spec. Copilot
|
|
105
|
+
* credentials rewrite the endpoint for Enterprise accounts and filter models
|
|
106
|
+
* the account cannot serve; returns undefined when the account filters the
|
|
107
|
+
* model out, and the unhooked spec when no credentials are loaded.
|
|
108
|
+
*/
|
|
109
|
+
export function applyConnectedModelHook(provider, model) {
|
|
110
|
+
const credentials = connectedTokens.credentials(provider);
|
|
111
|
+
if (!credentials)
|
|
112
|
+
return model;
|
|
113
|
+
const oauth = connectedProviders()
|
|
114
|
+
.find((entry) => entry.id === provider)
|
|
115
|
+
?.oauth();
|
|
116
|
+
if (!oauth?.modifyModels)
|
|
117
|
+
return model;
|
|
118
|
+
const kept = oauth.modifyModels([model], credentials);
|
|
119
|
+
return kept.find((entry) => entry.id === model.id);
|
|
120
|
+
}
|
|
121
|
+
export function loadConnectedTokensSnapshot() {
|
|
122
|
+
return connectedTokens.loadSnapshot();
|
|
123
|
+
}
|
|
124
|
+
export function ensureFreshConnectedTokens() {
|
|
125
|
+
return connectedTokens.ensureFreshTokens();
|
|
126
|
+
}
|
|
127
|
+
export function connectedProviderApiKey(provider) {
|
|
128
|
+
return connectedTokens.apiKey(provider);
|
|
129
|
+
}
|
|
130
|
+
export function refreshConnectedTokenInBackground(provider) {
|
|
131
|
+
connectedTokens.refreshInBackground(provider);
|
|
132
|
+
}
|
|
133
|
+
/** Persist reconnect-needed eligibility without deleting or rewriting credentials. */
|
|
134
|
+
export async function markConnectedProviderReconnectNeeded(provider) {
|
|
135
|
+
await connectedStore.withLock(provider, async (current) => ({
|
|
136
|
+
...(current
|
|
137
|
+
? {
|
|
138
|
+
next: {
|
|
139
|
+
...current,
|
|
140
|
+
eligibility: "unknown",
|
|
141
|
+
eligibilityCheckedAt: Date.now(),
|
|
142
|
+
},
|
|
143
|
+
}
|
|
144
|
+
: {}),
|
|
145
|
+
result: undefined,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokens.js","sourceRoot":"","sources":["../../../src/connected-providers/tokens.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type RouterProviderName, type TransportName } from "../model-resolution/catalog.js";
|
|
2
|
+
import { type ConnectionRecord } from "./store.js";
|
|
3
|
+
export interface TransportSnapshot {
|
|
4
|
+
/** Connections present when the CLI process booted; later store edits apply on the next invocation. */
|
|
5
|
+
connections: readonly Pick<ConnectionRecord, "provider" | "eligibility">[];
|
|
6
|
+
/**
|
|
7
|
+
* Router providers with a configured credential. When present, router-order
|
|
8
|
+
* selection skips unconfigured routers so a fallback never lands on a
|
|
9
|
+
* backend that cannot authenticate. Absent means "assume all configured"
|
|
10
|
+
* (explicit pins bypass this check either way).
|
|
11
|
+
*/
|
|
12
|
+
configuredRouters?: readonly RouterProviderName[];
|
|
13
|
+
}
|
|
14
|
+
export interface TransportChoice {
|
|
15
|
+
/** Backend selected to carry the requested model. */
|
|
16
|
+
transport: TransportName;
|
|
17
|
+
/** Provider-specific model id sent on the selected transport. */
|
|
18
|
+
modelId: string;
|
|
19
|
+
/** True when a connected subscription, rather than a metered router, carries the call. */
|
|
20
|
+
planCovered: boolean;
|
|
21
|
+
/** Rule that selected this transport. */
|
|
22
|
+
reason: "connected" | "router_order" | "explicit_pin";
|
|
23
|
+
}
|
|
24
|
+
/** Choose a transport from an immutable boot snapshot without reading env or disk. */
|
|
25
|
+
export declare function chooseTransport(shorthand: string, snapshot: TransportSnapshot): TransportChoice;
|
|
26
|
+
//# sourceMappingURL=transport-preference.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-preference.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/transport-preference.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,kBAAkB,EACvB,KAAK,aAAa,EACnB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAmD,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAEpG,MAAM,WAAW,iBAAiB;IAChC,uGAAuG;IACvG,WAAW,EAAE,SAAS,IAAI,CAAC,gBAAgB,EAAE,UAAU,GAAG,aAAa,CAAC,EAAE,CAAC;IAC3E;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACnD;AAED,MAAM,WAAW,eAAe;IAC9B,qDAAqD;IACrD,SAAS,EAAE,aAAa,CAAC;IACzB,iEAAiE;IACjE,OAAO,EAAE,MAAM,CAAC;IAChB,0FAA0F;IAC1F,WAAW,EAAE,OAAO,CAAC;IACrB,yCAAyC;IACzC,MAAM,EAAE,WAAW,GAAG,cAAc,GAAG,cAAc,CAAC;CACvD;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,iBAAiB,GAAG,eAAe,CAuC/F"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { PROVIDER_ORDER, transportModelId, } from "../model-resolution/catalog.js";
|
|
2
|
+
import { CONNECTED_PROVIDER_ORDER, isConnectedProviderId } from "./store.js";
|
|
3
|
+
/** Choose a transport from an immutable boot snapshot without reading env or disk. */
|
|
4
|
+
export function chooseTransport(shorthand, snapshot) {
|
|
5
|
+
const separator = shorthand.indexOf(":");
|
|
6
|
+
if (separator !== -1) {
|
|
7
|
+
const transport = shorthand.slice(0, separator);
|
|
8
|
+
return {
|
|
9
|
+
transport,
|
|
10
|
+
modelId: shorthand.slice(separator + 1),
|
|
11
|
+
planCovered: isConnectedProviderId(transport),
|
|
12
|
+
reason: "explicit_pin",
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
for (const provider of CONNECTED_PROVIDER_ORDER) {
|
|
16
|
+
const connection = snapshot.connections.find((candidate) => candidate.provider === provider);
|
|
17
|
+
if (!connection || connection.eligibility === "plan_ineligible")
|
|
18
|
+
continue;
|
|
19
|
+
const modelId = transportModelId(provider, shorthand);
|
|
20
|
+
if (modelId) {
|
|
21
|
+
return { transport: provider, modelId, planCovered: true, reason: "connected" };
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
const routerConfigured = (provider) => snapshot.configuredRouters === undefined || snapshot.configuredRouters.includes(provider);
|
|
25
|
+
for (const { provider } of PROVIDER_ORDER) {
|
|
26
|
+
if (!routerConfigured(provider))
|
|
27
|
+
continue;
|
|
28
|
+
const modelId = transportModelId(provider, shorthand);
|
|
29
|
+
if (modelId) {
|
|
30
|
+
return { transport: provider, modelId, planCovered: false, reason: "router_order" };
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Unknown names retain resolver.ts's pass-through behavior until it can
|
|
34
|
+
// produce the existing user-facing "Unknown model shorthand" error.
|
|
35
|
+
return {
|
|
36
|
+
transport: PROVIDER_ORDER[0].provider,
|
|
37
|
+
modelId: shorthand,
|
|
38
|
+
planCovered: false,
|
|
39
|
+
reason: "router_order",
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=transport-preference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transport-preference.js","sourceRoot":"","sources":["../../../src/connected-providers/transport-preference.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,EACd,gBAAgB,GAGjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,EAAE,wBAAwB,EAAE,qBAAqB,EAAyB,MAAM,YAAY,CAAC;AAyBpG,sFAAsF;AACtF,MAAM,UAAU,eAAe,CAAC,SAAiB,EAAE,QAA2B;IAC5E,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IACzC,IAAI,SAAS,KAAK,CAAC,CAAC,EAAE,CAAC;QACrB,MAAM,SAAS,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,SAAS,CAAkB,CAAC;QACjE,OAAO;YACL,SAAS;YACT,OAAO,EAAE,SAAS,CAAC,KAAK,CAAC,SAAS,GAAG,CAAC,CAAC;YACvC,WAAW,EAAE,qBAAqB,CAAC,SAAS,CAAC;YAC7C,MAAM,EAAE,cAAc;SACvB,CAAC;IACJ,CAAC;IAED,KAAK,MAAM,QAAQ,IAAI,wBAAwB,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC;QAC7F,IAAI,CAAC,UAAU,IAAI,UAAU,CAAC,WAAW,KAAK,iBAAiB;YAAE,SAAS;QAC1E,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAClF,CAAC;IACH,CAAC;IAED,MAAM,gBAAgB,GAAG,CAAC,QAA4B,EAAW,EAAE,CACjE,QAAQ,CAAC,iBAAiB,KAAK,SAAS,IAAI,QAAQ,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC5F,KAAK,MAAM,EAAE,QAAQ,EAAE,IAAI,cAAc,EAAE,CAAC;QAC1C,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC;YAAE,SAAS;QAC1C,MAAM,OAAO,GAAG,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;QACtD,IAAI,OAAO,EAAE,CAAC;YACZ,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC;QACtF,CAAC;IACH,CAAC;IAED,wEAAwE;IACxE,oEAAoE;IACpE,OAAO;QACL,SAAS,EAAE,cAAc,CAAC,CAAC,CAAE,CAAC,QAAQ;QACtC,OAAO,EAAE,SAAS;QAClB,WAAW,EAAE,KAAK;QAClB,MAAM,EAAE,cAAc;KACvB,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../../src/core/structured-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,IAAI,EAET,KAAK,KAAK,EACX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"structured-output.d.ts","sourceRoot":"","sources":["../../../src/core/structured-output.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAEjB,KAAK,qBAAqB,EAC1B,KAAK,WAAW,EAChB,KAAK,IAAI,EAET,KAAK,KAAK,EACX,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAK/C,MAAM,MAAM,sBAAsB,GAAG,MAAM,GAAG,KAAK,CAAC,WAAW,GAAG,YAAY,CAAC,CAAC;AAEhF,MAAM,WAAW,uBAAuB,CAAC,YAAY,SAAS,OAAO;IACnE,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC;IACzB,MAAM,EAAE,sBAAsB,CAAC;IAC/B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,oFAAoF;IACpF,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,WAAW,CAAC,EAAE,qBAAqB,CAAC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED,wBAAsB,wBAAwB,CAAC,YAAY,SAAS,OAAO,EACzE,OAAO,EAAE,uBAAuB,CAAC,YAAY,CAAC,GAC7C,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CA4C/B"}
|