@duetso/agent 0.3.0 → 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 +14 -4
- package/dist/package.json +1 -1
- 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 +25 -5
- package/dist/src/cli/help.js.map +1 -1
- 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 +10 -0
- package/dist/src/cli/route.d.ts.map +1 -1
- package/dist/src/cli/route.js +31 -1
- package/dist/src/cli/route.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.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/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/table.js +18 -18
- package/dist/src/model-routing/table.js.map +1 -1
- package/dist/src/session/session.d.ts.map +1 -1
- package/dist/src/session/session.js +8 -5
- package/dist/src/session/session.js.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/turn-runner.d.ts +4 -1
- package/dist/src/turn-runner/turn-runner.d.ts.map +1 -1
- package/dist/src/turn-runner/turn-runner.js +74 -19
- 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/types/protocol.d.ts +11 -3
- package/dist/src/types/protocol.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { Usage } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { TransportName } from "../model-resolution/catalog.js";
|
|
3
|
+
import type { TurnTokenUsage } from "../types/protocol.js";
|
|
4
|
+
/** Return provider usage with subscription-covered prices forced to zero. */
|
|
5
|
+
export declare function usageForTransport(usage: TurnTokenUsage | Usage, provider: TransportName): TurnTokenUsage | Usage;
|
|
6
|
+
//# sourceMappingURL=billing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/billing.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AACnD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3D,6EAA6E;AAC7E,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,GAAG,KAAK,EAC7B,QAAQ,EAAE,aAAa,GACtB,cAAc,GAAG,KAAK,CAMxB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { isConnectedProviderId } from "./store.js";
|
|
2
|
+
/** Return provider usage with subscription-covered prices forced to zero. */
|
|
3
|
+
export function usageForTransport(usage, provider) {
|
|
4
|
+
if (!isConnectedProviderId(provider))
|
|
5
|
+
return usage;
|
|
6
|
+
return {
|
|
7
|
+
...usage,
|
|
8
|
+
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: 0 },
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=billing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"billing.js","sourceRoot":"","sources":["../../../src/connected-providers/billing.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEnD,6EAA6E;AAC7E,MAAM,UAAU,iBAAiB,CAC/B,KAA6B,EAC7B,QAAuB;IAEvB,IAAI,CAAC,qBAAqB,CAAC,QAAQ,CAAC;QAAE,OAAO,KAAK,CAAC;IACnD,OAAO;QACL,GAAG,KAAK;QACR,IAAI,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;KACrE,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type Api, type AssistantMessage, type Context, type Model, type ProviderStreamOptions } from "@earendil-works/pi-ai";
|
|
2
|
+
import type { OAuthCredentials } from "@earendil-works/pi-ai/oauth";
|
|
3
|
+
import type { ConnectedProviderId, ConnectionEligibility } from "./store.js";
|
|
4
|
+
export interface CapabilityProbeResult {
|
|
5
|
+
/** Whether this account can serve plan-covered model traffic. */
|
|
6
|
+
eligibility: ConnectionEligibility;
|
|
7
|
+
/** Provider model ids available to the connected account. */
|
|
8
|
+
servedModelIds: string[];
|
|
9
|
+
/** Stable redacted reason when eligibility could not be confirmed. */
|
|
10
|
+
detail?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface CapabilityProbeDependencies {
|
|
13
|
+
/** Completion edge replaced by deterministic HTTP-status fixtures in tests. */
|
|
14
|
+
complete?: (model: Model<Api>, context: Context, options?: ProviderStreamOptions) => Promise<AssistantMessage>;
|
|
15
|
+
}
|
|
16
|
+
export declare function probeConnectedProvider(id: ConnectedProviderId, credentials: OAuthCredentials, deps?: CapabilityProbeDependencies): Promise<CapabilityProbeResult>;
|
|
17
|
+
//# sourceMappingURL=capability-probe.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-probe.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/capability-probe.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,GAAG,EACR,KAAK,gBAAgB,EACrB,KAAK,OAAO,EACZ,KAAK,KAAK,EACV,KAAK,qBAAqB,EAC3B,MAAM,uBAAuB,CAAC;AAC/B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAEpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAK7E,MAAM,WAAW,qBAAqB;IACpC,iEAAiE;IACjE,WAAW,EAAE,qBAAqB,CAAC;IACnC,6DAA6D;IAC7D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,sEAAsE;IACtE,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,CACT,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,EACjB,OAAO,EAAE,OAAO,EAChB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,mBAAmB,EACvB,WAAW,EAAE,gBAAgB,EAC7B,IAAI,GAAE,2BAAgC,GACrC,OAAO,CAAC,qBAAqB,CAAC,CA8ChC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { complete, getModels, } from "@earendil-works/pi-ai";
|
|
2
|
+
import { connectedProviders } from "./registry.js";
|
|
3
|
+
const CHATGPT_SERVED_MODEL_IDS = ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"];
|
|
4
|
+
const CHATGPT_PROBE_MODEL_ID = "gpt-5.6-luna";
|
|
5
|
+
export async function probeConnectedProvider(id, credentials, deps = {}) {
|
|
6
|
+
const provider = connectedProviders()
|
|
7
|
+
.find((entry) => entry.id === id)
|
|
8
|
+
?.oauth();
|
|
9
|
+
if (!provider)
|
|
10
|
+
return unknown("Provider is not registered.");
|
|
11
|
+
const catalog = getModels(id);
|
|
12
|
+
const available = provider.modifyModels?.(catalog, credentials) ?? catalog;
|
|
13
|
+
const servedModelIds = id === "openai-codex" ? [...CHATGPT_SERVED_MODEL_IDS] : available.map((model) => model.id);
|
|
14
|
+
const copilotHasNoModels = id === "github-copilot" && hasEmptyAvailableModelIds(credentials);
|
|
15
|
+
// An ineligible Copilot account filters every model out. Probe a catalog donor
|
|
16
|
+
// anyway so connect still exercises the transport and observes its stop=error.
|
|
17
|
+
const model = selectProbeModel(id, available.length > 0 ? available : catalog);
|
|
18
|
+
if (!model)
|
|
19
|
+
return unknown("No probe model is available.");
|
|
20
|
+
let responseStatus;
|
|
21
|
+
try {
|
|
22
|
+
const message = await (deps.complete ?? complete)(model, {
|
|
23
|
+
systemPrompt: "Return one short token.",
|
|
24
|
+
messages: [{ role: "user", content: "ok", timestamp: Date.now() }],
|
|
25
|
+
}, {
|
|
26
|
+
apiKey: provider.getApiKey(credentials),
|
|
27
|
+
maxTokens: 1,
|
|
28
|
+
onResponse: (response) => {
|
|
29
|
+
responseStatus = response.status;
|
|
30
|
+
},
|
|
31
|
+
});
|
|
32
|
+
if (copilotHasNoModels) {
|
|
33
|
+
return {
|
|
34
|
+
eligibility: "plan_ineligible",
|
|
35
|
+
servedModelIds: [],
|
|
36
|
+
detail: "No models are available for this Copilot plan.",
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// pi-ai transports surface HTTP failures as stopReason "error" instead of
|
|
40
|
+
// rejecting complete(), so billing classification must run here too.
|
|
41
|
+
if (message.stopReason === "error")
|
|
42
|
+
return classifyFailure(responseStatus);
|
|
43
|
+
return { eligibility: "eligible", servedModelIds };
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
return classifyFailure(responseStatus ?? errorStatus(error));
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Only a confirmed billing refusal may mark a plan ineligible; auth failures,
|
|
51
|
+
* server errors, and network outages stay "unknown" so a transient outage
|
|
52
|
+
* never persists a false eligibility verdict.
|
|
53
|
+
*/
|
|
54
|
+
function classifyFailure(responseStatus) {
|
|
55
|
+
if (responseStatus === 402) {
|
|
56
|
+
return {
|
|
57
|
+
eligibility: "plan_ineligible",
|
|
58
|
+
servedModelIds: [],
|
|
59
|
+
detail: "Provider plan does not cover the probe model.",
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
return unknown(responseStatus === undefined
|
|
63
|
+
? "Capability probe request failed."
|
|
64
|
+
: `Capability probe failed with HTTP ${responseStatus}.`);
|
|
65
|
+
}
|
|
66
|
+
function errorStatus(error) {
|
|
67
|
+
if (error === null || typeof error !== "object" || !("status" in error))
|
|
68
|
+
return undefined;
|
|
69
|
+
return typeof error.status === "number" ? error.status : undefined;
|
|
70
|
+
}
|
|
71
|
+
function selectProbeModel(id, available) {
|
|
72
|
+
if (id === "openai-codex") {
|
|
73
|
+
const donor = available.find((model) => model.id === "gpt-5.4-mini") ?? available[0];
|
|
74
|
+
return donor
|
|
75
|
+
? { ...donor, id: CHATGPT_PROBE_MODEL_ID, name: CHATGPT_PROBE_MODEL_ID }
|
|
76
|
+
: undefined;
|
|
77
|
+
}
|
|
78
|
+
return available.reduce((cheapest, model) => {
|
|
79
|
+
if (!cheapest)
|
|
80
|
+
return model;
|
|
81
|
+
return model.cost.input + model.cost.output < cheapest.cost.input + cheapest.cost.output
|
|
82
|
+
? model
|
|
83
|
+
: cheapest;
|
|
84
|
+
}, undefined);
|
|
85
|
+
}
|
|
86
|
+
function hasEmptyAvailableModelIds(credentials) {
|
|
87
|
+
return Array.isArray(credentials.availableModelIds) && credentials.availableModelIds.length === 0;
|
|
88
|
+
}
|
|
89
|
+
function unknown(detail) {
|
|
90
|
+
return { eligibility: "unknown", servedModelIds: [], detail };
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=capability-probe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"capability-probe.js","sourceRoot":"","sources":["../../../src/connected-providers/capability-probe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare const FAKE_ISSUER_ENV = "DUET_CONNECT_FAKE_ISSUER_URL";
|
|
2
|
+
/** Concrete RFC-8628 contract imported by both client and issuer conformance tests. */
|
|
3
|
+
export declare const FAKE_ISSUER_WIRE_TABLE: {
|
|
4
|
+
readonly deviceCode: {
|
|
5
|
+
readonly method: "POST";
|
|
6
|
+
readonly path: "/device/code";
|
|
7
|
+
readonly request: {
|
|
8
|
+
readonly client_id: "duet-agent:openai-codex";
|
|
9
|
+
readonly scope: "openid profile email";
|
|
10
|
+
};
|
|
11
|
+
readonly response: {
|
|
12
|
+
readonly device_code: "fixture-device-code";
|
|
13
|
+
readonly user_code: "ABCD-EFGH";
|
|
14
|
+
readonly verification_uri: "https://issuer.test/device";
|
|
15
|
+
readonly interval: 1;
|
|
16
|
+
readonly expires_in: 600;
|
|
17
|
+
};
|
|
18
|
+
};
|
|
19
|
+
readonly pendingToken: {
|
|
20
|
+
readonly method: "POST";
|
|
21
|
+
readonly path: "/token";
|
|
22
|
+
readonly request: {
|
|
23
|
+
readonly device_code: "fixture-device-code";
|
|
24
|
+
readonly grant_type: "urn:ietf:params:oauth:grant-type:device_code";
|
|
25
|
+
};
|
|
26
|
+
readonly response: {
|
|
27
|
+
readonly status: "pending";
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
readonly approvedToken: {
|
|
31
|
+
readonly method: "POST";
|
|
32
|
+
readonly path: "/token";
|
|
33
|
+
readonly request: {
|
|
34
|
+
readonly device_code: "fixture-device-code";
|
|
35
|
+
readonly grant_type: "urn:ietf:params:oauth:grant-type:device_code";
|
|
36
|
+
};
|
|
37
|
+
readonly response: {
|
|
38
|
+
readonly access_token: "fixture-access-token";
|
|
39
|
+
readonly refresh_token: "fixture-refresh-token";
|
|
40
|
+
readonly expires_in: 3600;
|
|
41
|
+
readonly account_id: "fixture-account";
|
|
42
|
+
};
|
|
43
|
+
};
|
|
44
|
+
readonly refreshToken: {
|
|
45
|
+
readonly method: "POST";
|
|
46
|
+
readonly path: "/token";
|
|
47
|
+
readonly request: {
|
|
48
|
+
readonly refresh_token: "fixture-refresh-token";
|
|
49
|
+
readonly grant_type: "refresh_token";
|
|
50
|
+
};
|
|
51
|
+
readonly response: {
|
|
52
|
+
readonly access_token: "fixture-rotated-access-token";
|
|
53
|
+
readonly refresh_token: "fixture-rotated-refresh-token";
|
|
54
|
+
readonly expires_in: 3600;
|
|
55
|
+
readonly account_id: "fixture-account";
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
/** Registers fake providers through pi-ai only when explicitly enabled. */
|
|
60
|
+
export declare function installFakeIssuerIfConfigured(env?: Record<string, string | undefined>): "installed" | "skipped";
|
|
61
|
+
//# sourceMappingURL=fake-issuer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake-issuer.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/fake-issuer.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,eAAe,iCAAiC,CAAC;AAE9D,uFAAuF;AACvF,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+CzB,CAAC;AAqBX,2EAA2E;AAC3E,wBAAgB,6BAA6B,CAC3C,GAAG,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAe,GACpD,WAAW,GAAG,SAAS,CAazB"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { pollOAuthDeviceCodeFlow, registerOAuthProvider, } from "@earendil-works/pi-ai/oauth";
|
|
2
|
+
export const FAKE_ISSUER_ENV = "DUET_CONNECT_FAKE_ISSUER_URL";
|
|
3
|
+
/** Concrete RFC-8628 contract imported by both client and issuer conformance tests. */
|
|
4
|
+
export const FAKE_ISSUER_WIRE_TABLE = {
|
|
5
|
+
deviceCode: {
|
|
6
|
+
method: "POST",
|
|
7
|
+
path: "/device/code",
|
|
8
|
+
request: { client_id: "duet-agent:openai-codex", scope: "openid profile email" },
|
|
9
|
+
response: {
|
|
10
|
+
device_code: "fixture-device-code",
|
|
11
|
+
user_code: "ABCD-EFGH",
|
|
12
|
+
verification_uri: "https://issuer.test/device",
|
|
13
|
+
interval: 1,
|
|
14
|
+
expires_in: 600,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
pendingToken: {
|
|
18
|
+
method: "POST",
|
|
19
|
+
path: "/token",
|
|
20
|
+
request: {
|
|
21
|
+
device_code: "fixture-device-code",
|
|
22
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
23
|
+
},
|
|
24
|
+
response: { status: "pending" },
|
|
25
|
+
},
|
|
26
|
+
approvedToken: {
|
|
27
|
+
method: "POST",
|
|
28
|
+
path: "/token",
|
|
29
|
+
request: {
|
|
30
|
+
device_code: "fixture-device-code",
|
|
31
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
32
|
+
},
|
|
33
|
+
response: {
|
|
34
|
+
access_token: "fixture-access-token",
|
|
35
|
+
refresh_token: "fixture-refresh-token",
|
|
36
|
+
expires_in: 3600,
|
|
37
|
+
account_id: "fixture-account",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
refreshToken: {
|
|
41
|
+
method: "POST",
|
|
42
|
+
path: "/token",
|
|
43
|
+
request: { refresh_token: "fixture-refresh-token", grant_type: "refresh_token" },
|
|
44
|
+
response: {
|
|
45
|
+
access_token: "fixture-rotated-access-token",
|
|
46
|
+
refresh_token: "fixture-rotated-refresh-token",
|
|
47
|
+
expires_in: 3600,
|
|
48
|
+
account_id: "fixture-account",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
let installedBaseUrl;
|
|
53
|
+
let installedProviders;
|
|
54
|
+
/** Registers fake providers through pi-ai only when explicitly enabled. */
|
|
55
|
+
export function installFakeIssuerIfConfigured(env = process.env) {
|
|
56
|
+
const configured = env[FAKE_ISSUER_ENV]?.trim();
|
|
57
|
+
if (!configured)
|
|
58
|
+
return "skipped";
|
|
59
|
+
const baseUrl = configured.replace(/\/+$/, "");
|
|
60
|
+
if (installedBaseUrl !== baseUrl || !installedProviders) {
|
|
61
|
+
installedBaseUrl = baseUrl;
|
|
62
|
+
installedProviders = [
|
|
63
|
+
createFakeProvider("openai-codex", "ChatGPT", baseUrl),
|
|
64
|
+
createFakeProvider("github-copilot", "GitHub Copilot", baseUrl),
|
|
65
|
+
];
|
|
66
|
+
}
|
|
67
|
+
for (const provider of installedProviders)
|
|
68
|
+
registerOAuthProvider(provider);
|
|
69
|
+
return "installed";
|
|
70
|
+
}
|
|
71
|
+
function createFakeProvider(id, name, baseUrl) {
|
|
72
|
+
return {
|
|
73
|
+
id,
|
|
74
|
+
name,
|
|
75
|
+
async login(callbacks) {
|
|
76
|
+
const device = await postJson(`${baseUrl}/device/code`, {
|
|
77
|
+
client_id: `duet-agent:${id}`,
|
|
78
|
+
scope: id === "openai-codex" ? "openid profile email" : "read:user",
|
|
79
|
+
});
|
|
80
|
+
callbacks.onDeviceCode({
|
|
81
|
+
userCode: device.user_code,
|
|
82
|
+
verificationUri: device.verification_uri,
|
|
83
|
+
intervalSeconds: device.interval,
|
|
84
|
+
expiresInSeconds: device.expires_in,
|
|
85
|
+
});
|
|
86
|
+
return pollOAuthDeviceCodeFlow({
|
|
87
|
+
intervalSeconds: device.interval,
|
|
88
|
+
expiresInSeconds: device.expires_in,
|
|
89
|
+
signal: callbacks.signal,
|
|
90
|
+
poll: async () => tokenPollResult(await postJson(`${baseUrl}/token`, {
|
|
91
|
+
device_code: device.device_code,
|
|
92
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
93
|
+
})),
|
|
94
|
+
});
|
|
95
|
+
},
|
|
96
|
+
async refreshToken(credentials) {
|
|
97
|
+
const token = await postJson(`${baseUrl}/token`, {
|
|
98
|
+
refresh_token: credentials.refresh,
|
|
99
|
+
grant_type: "refresh_token",
|
|
100
|
+
});
|
|
101
|
+
return credentialsFromToken(token, credentials.refresh);
|
|
102
|
+
},
|
|
103
|
+
getApiKey(credentials) {
|
|
104
|
+
return credentials.access;
|
|
105
|
+
},
|
|
106
|
+
modifyModels(models) {
|
|
107
|
+
return models.map((model) => ({
|
|
108
|
+
...model,
|
|
109
|
+
api: "openai-completions",
|
|
110
|
+
baseUrl,
|
|
111
|
+
compat: {
|
|
112
|
+
...model.compat,
|
|
113
|
+
supportsStore: false,
|
|
114
|
+
supportsDeveloperRole: false,
|
|
115
|
+
supportsReasoningEffort: false,
|
|
116
|
+
},
|
|
117
|
+
}));
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
function tokenPollResult(response) {
|
|
122
|
+
if (response.status === "pending" || response.status === "slow_down") {
|
|
123
|
+
return { status: response.status };
|
|
124
|
+
}
|
|
125
|
+
if (response.status === "denied") {
|
|
126
|
+
return { status: "failed", message: "Device login denied." };
|
|
127
|
+
}
|
|
128
|
+
if (response.status === "expired") {
|
|
129
|
+
return { status: "failed", message: "Device login expired." };
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
return { status: "complete", value: credentialsFromToken(response) };
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return { status: "failed", message: "Device token response was malformed." };
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
function credentialsFromToken(response, previousRefresh) {
|
|
139
|
+
if (typeof response.access_token !== "string" ||
|
|
140
|
+
typeof response.expires_in !== "number" ||
|
|
141
|
+
(typeof response.refresh_token !== "string" && previousRefresh === undefined)) {
|
|
142
|
+
throw new Error("Device token response was malformed.");
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
access: response.access_token,
|
|
146
|
+
refresh: response.refresh_token ?? previousRefresh,
|
|
147
|
+
expires: Date.now() + response.expires_in * 1000,
|
|
148
|
+
...(response.account_id === undefined ? {} : { accountId: response.account_id }),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
async function postJson(url, body) {
|
|
152
|
+
const response = await fetch(url, {
|
|
153
|
+
method: "POST",
|
|
154
|
+
headers: { "Content-Type": "application/json" },
|
|
155
|
+
body: JSON.stringify(body),
|
|
156
|
+
});
|
|
157
|
+
if (!response.ok)
|
|
158
|
+
throw new Error(`Fake issuer request failed (${response.status}).`);
|
|
159
|
+
return (await response.json());
|
|
160
|
+
}
|
|
161
|
+
//# sourceMappingURL=fake-issuer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fake-issuer.js","sourceRoot":"","sources":["../../../src/connected-providers/fake-issuer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { TransportName } from "../model-resolution/catalog.js";
|
|
2
|
+
import type { ConnectedProviderId } from "./store.js";
|
|
3
|
+
export type TransportFallbackCause = "plan_exhausted" | "auth_failed" | "transport_error";
|
|
4
|
+
/** Own the single user-visible sentence emitted when a connected call falls back. */
|
|
5
|
+
export declare function transportFallbackNotice(input: {
|
|
6
|
+
provider: ConnectedProviderId;
|
|
7
|
+
modelName: string;
|
|
8
|
+
fallbackTransport: TransportName;
|
|
9
|
+
cause: TransportFallbackCause;
|
|
10
|
+
}): string;
|
|
11
|
+
//# sourceMappingURL=fallback-notice.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback-notice.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/fallback-notice.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAEtD,MAAM,MAAM,sBAAsB,GAAG,gBAAgB,GAAG,aAAa,GAAG,iBAAiB,CAAC;AAE1F,qFAAqF;AACrF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE;IAC7C,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,iBAAiB,EAAE,aAAa,CAAC;IACjC,KAAK,EAAE,sBAAsB,CAAC;CAC/B,GAAG,MAAM,CAST"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Own the single user-visible sentence emitted when a connected call falls back. */
|
|
2
|
+
export function transportFallbackNotice(input) {
|
|
3
|
+
const provider = input.provider === "openai-codex" ? "ChatGPT" : "Copilot";
|
|
4
|
+
const reason = input.cause === "plan_exhausted"
|
|
5
|
+
? "plan limit hit"
|
|
6
|
+
: input.cause === "auth_failed"
|
|
7
|
+
? "connection needs attention"
|
|
8
|
+
: "plan transport unavailable";
|
|
9
|
+
return `${provider} ${reason} for ${input.modelName} — continuing on ${transportLabel(input.fallbackTransport)}.`;
|
|
10
|
+
}
|
|
11
|
+
function transportLabel(transport) {
|
|
12
|
+
if (transport === "duet-gateway")
|
|
13
|
+
return "Duet credits";
|
|
14
|
+
if (transport === "vercel-ai-gateway")
|
|
15
|
+
return "Vercel AI Gateway";
|
|
16
|
+
if (transport === "openrouter")
|
|
17
|
+
return "OpenRouter";
|
|
18
|
+
return transport === "openai-codex" ? "ChatGPT plan" : "Copilot plan";
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=fallback-notice.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback-notice.js","sourceRoot":"","sources":["../../../src/connected-providers/fallback-notice.ts"],"names":[],"mappings":"AAKA,qFAAqF;AACrF,MAAM,UAAU,uBAAuB,CAAC,KAKvC;IACC,MAAM,QAAQ,GAAG,KAAK,CAAC,QAAQ,KAAK,cAAc,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,MAAM,MAAM,GACV,KAAK,CAAC,KAAK,KAAK,gBAAgB;QAC9B,CAAC,CAAC,gBAAgB;QAClB,CAAC,CAAC,KAAK,CAAC,KAAK,KAAK,aAAa;YAC7B,CAAC,CAAC,4BAA4B;YAC9B,CAAC,CAAC,4BAA4B,CAAC;IACrC,OAAO,GAAG,QAAQ,IAAI,MAAM,QAAQ,KAAK,CAAC,SAAS,oBAAoB,cAAc,CAAC,KAAK,CAAC,iBAAiB,CAAC,GAAG,CAAC;AACpH,CAAC;AAED,SAAS,cAAc,CAAC,SAAwB;IAC9C,IAAI,SAAS,KAAK,cAAc;QAAE,OAAO,cAAc,CAAC;IACxD,IAAI,SAAS,KAAK,mBAAmB;QAAE,OAAO,mBAAmB,CAAC;IAClE,IAAI,SAAS,KAAK,YAAY;QAAE,OAAO,YAAY,CAAC;IACpD,OAAO,SAAS,KAAK,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,cAAc,CAAC;AACxE,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { AgentMessage } from "@earendil-works/pi-agent-core";
|
|
2
|
+
import { type TransportChoice, type TransportSnapshot } from "./transport-preference.js";
|
|
3
|
+
import { type ConnectedProviderId } from "./store.js";
|
|
4
|
+
import type { TransportFallbackCause } from "./fallback-notice.js";
|
|
5
|
+
/** Classify only failures eligible for cross-transport fallback. */
|
|
6
|
+
export declare function connectedFallbackCause(message: AgentMessage | undefined): TransportFallbackCause | undefined;
|
|
7
|
+
/** True when a connected failure must terminate because replay is no longer safe. */
|
|
8
|
+
export declare function isPostOutputConnectedFailure(provider: string, message: AgentMessage | undefined, visibleOutput: boolean): boolean;
|
|
9
|
+
/** Select the next transport after treating the failed connection as ineligible. */
|
|
10
|
+
export declare function nextTransportAfterConnectedFailure(provider: ConnectedProviderId, modelId: string, snapshot: TransportSnapshot): TransportChoice | undefined;
|
|
11
|
+
//# sourceMappingURL=fallback.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/fallback.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,iBAAiB,EACvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAyB,KAAK,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAEnE,oEAAoE;AACpE,wBAAgB,sBAAsB,CACpC,OAAO,EAAE,YAAY,GAAG,SAAS,GAChC,sBAAsB,GAAG,SAAS,CAWpC;AAED,qFAAqF;AACrF,wBAAgB,4BAA4B,CAC1C,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,YAAY,GAAG,SAAS,EACjC,aAAa,EAAE,OAAO,GACrB,OAAO,CAMT;AAED,oFAAoF;AACpF,wBAAgB,kCAAkC,CAChD,QAAQ,EAAE,mBAAmB,EAC7B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,iBAAiB,GAC1B,eAAe,GAAG,SAAS,CAW7B"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { chooseTransport, } from "./transport-preference.js";
|
|
2
|
+
import { shorthandForTransportModel } from "../model-resolution/catalog.js";
|
|
3
|
+
import { isConnectedProviderId } from "./store.js";
|
|
4
|
+
/** Classify only failures eligible for cross-transport fallback. */
|
|
5
|
+
export function connectedFallbackCause(message) {
|
|
6
|
+
if (!message || message.role !== "assistant" || message.stopReason !== "error")
|
|
7
|
+
return undefined;
|
|
8
|
+
const error = message.errorMessage ?? "";
|
|
9
|
+
if (/context|prompt is too long|token.*maximum/i.test(error))
|
|
10
|
+
return undefined;
|
|
11
|
+
if (/\b401\b|\b403\b|unauthori[sz]ed|invalid.*(?:token|key)|authentication/i.test(error)) {
|
|
12
|
+
return "auth_failed";
|
|
13
|
+
}
|
|
14
|
+
if (/\b402\b|\b429\b|usage limit|plan limit|quota|exhausted|not included/i.test(error)) {
|
|
15
|
+
return "plan_exhausted";
|
|
16
|
+
}
|
|
17
|
+
return "transport_error";
|
|
18
|
+
}
|
|
19
|
+
/** True when a connected failure must terminate because replay is no longer safe. */
|
|
20
|
+
export function isPostOutputConnectedFailure(provider, message, visibleOutput) {
|
|
21
|
+
return (visibleOutput &&
|
|
22
|
+
isConnectedProviderId(provider) &&
|
|
23
|
+
connectedFallbackCause(message) !== undefined);
|
|
24
|
+
}
|
|
25
|
+
/** Select the next transport after treating the failed connection as ineligible. */
|
|
26
|
+
export function nextTransportAfterConnectedFailure(provider, modelId, snapshot) {
|
|
27
|
+
const shorthand = shorthandForTransportModel(provider, modelId);
|
|
28
|
+
if (!shorthand)
|
|
29
|
+
return undefined;
|
|
30
|
+
const connections = snapshot.connections.some((connection) => connection.provider === provider)
|
|
31
|
+
? snapshot.connections.map((connection) => connection.provider === provider
|
|
32
|
+
? { ...connection, eligibility: "plan_ineligible" }
|
|
33
|
+
: connection)
|
|
34
|
+
: [...snapshot.connections, { provider, eligibility: "plan_ineligible" }];
|
|
35
|
+
return chooseTransport(shorthand, { ...snapshot, connections });
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=fallback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fallback.js","sourceRoot":"","sources":["../../../src/connected-providers/fallback.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,GAGhB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,0BAA0B,EAAE,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAE,qBAAqB,EAA4B,MAAM,YAAY,CAAC;AAG7E,oEAAoE;AACpE,MAAM,UAAU,sBAAsB,CACpC,OAAiC;IAEjC,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,UAAU,KAAK,OAAO;QAAE,OAAO,SAAS,CAAC;IACjG,MAAM,KAAK,GAAG,OAAO,CAAC,YAAY,IAAI,EAAE,CAAC;IACzC,IAAI,4CAA4C,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,OAAO,SAAS,CAAC;IAC/E,IAAI,wEAAwE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACzF,OAAO,aAAa,CAAC;IACvB,CAAC;IACD,IAAI,sEAAsE,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACvF,OAAO,gBAAgB,CAAC;IAC1B,CAAC;IACD,OAAO,iBAAiB,CAAC;AAC3B,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,4BAA4B,CAC1C,QAAgB,EAChB,OAAiC,EACjC,aAAsB;IAEtB,OAAO,CACL,aAAa;QACb,qBAAqB,CAAC,QAAQ,CAAC;QAC/B,sBAAsB,CAAC,OAAO,CAAC,KAAK,SAAS,CAC9C,CAAC;AACJ,CAAC;AAED,oFAAoF;AACpF,MAAM,UAAU,kCAAkC,CAChD,QAA6B,EAC7B,OAAe,EACf,QAA2B;IAE3B,MAAM,SAAS,GAAG,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChE,IAAI,CAAC,SAAS;QAAE,OAAO,SAAS,CAAC;IACjC,MAAM,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,QAAQ,KAAK,QAAQ,CAAC;QAC7F,CAAC,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CACtC,UAAU,CAAC,QAAQ,KAAK,QAAQ;YAC9B,CAAC,CAAC,EAAE,GAAG,UAAU,EAAE,WAAW,EAAE,iBAA0B,EAAE;YAC5D,CAAC,CAAC,UAAU,CACf;QACH,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAA0B,EAAE,CAAC,CAAC;IACrF,OAAO,eAAe,CAAC,SAAS,EAAE,EAAE,GAAG,QAAQ,EAAE,WAAW,EAAE,CAAC,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ConnectedProviderId, OAuthCredentials } from "./store.js";
|
|
2
|
+
type FetchToken = (input: string, init: RequestInit) => Promise<Response>;
|
|
3
|
+
/** Refresh one connected-provider credential using the provider's supported OAuth flow. */
|
|
4
|
+
export declare function refreshConnectedCredentials(provider: ConnectedProviderId, credentials: OAuthCredentials): Promise<OAuthCredentials>;
|
|
5
|
+
/**
|
|
6
|
+
* Refresh ChatGPT credentials with the scope pi-ai currently omits. OpenAI
|
|
7
|
+
* rotates refresh tokens, so the caller must replace the stored credential
|
|
8
|
+
* object with this complete result rather than patching only `access`.
|
|
9
|
+
*/
|
|
10
|
+
export declare function refreshOpenAICodexCredentials(refreshToken: string, fetchImpl?: FetchToken, now?: () => number): Promise<OAuthCredentials>;
|
|
11
|
+
export {};
|
|
12
|
+
//# sourceMappingURL=refresh.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refresh.d.ts","sourceRoot":"","sources":["../../../src/connected-providers/refresh.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAMxE,KAAK,UAAU,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;AAE1E,2FAA2F;AAC3F,wBAAsB,2BAA2B,CAC/C,QAAQ,EAAE,mBAAmB,EAC7B,WAAW,EAAE,gBAAgB,GAC5B,OAAO,CAAC,gBAAgB,CAAC,CAK3B;AAED;;;;GAIG;AACH,wBAAsB,6BAA6B,CACjD,YAAY,EAAE,MAAM,EACpB,SAAS,GAAE,UAAgD,EAC3D,GAAG,GAAE,MAAM,MAAiB,GAC3B,OAAO,CAAC,gBAAgB,CAAC,CA6B3B"}
|
|
@@ -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"}
|