@pasko70/pibo 1.16.6 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime/auth.js +28 -0
- package/dist/agent-runtime/capabilities.js +221 -0
- package/dist/agent-runtime/context-build.js +313 -0
- package/dist/agent-runtime/contract.js +45 -0
- package/dist/agent-runtime/errors.js +57 -0
- package/dist/agent-runtime/events.js +1 -0
- package/dist/agent-runtime/history.js +1 -0
- package/dist/agent-runtime/profile-validation.js +109 -0
- package/dist/agent-runtime/registry.js +608 -0
- package/dist/agent-runtime/resource-files.js +87 -0
- package/dist/agent-runtime/resource-service.js +635 -0
- package/dist/agent-runtime/resources.js +1 -0
- package/dist/agent-runtime/routed-session.js +931 -0
- package/dist/agent-runtime/testing/contract.js +51 -0
- package/dist/agent-runtime/testing/fake-adapter.js +192 -0
- package/dist/agent-runtime/types.js +3 -0
- package/dist/agent-runtimes/codex-native/adapter.js +905 -0
- package/dist/agent-runtimes/codex-native/auth.js +476 -0
- package/dist/agent-runtimes/codex-native/client.js +734 -0
- package/dist/agent-runtimes/codex-native/config.js +152 -0
- package/dist/agent-runtimes/codex-native/history.js +283 -0
- package/dist/agent-runtimes/codex-native/mcp-stdio-launcher.js +89 -0
- package/dist/agent-runtimes/codex-native/models.js +581 -0
- package/dist/agent-runtimes/codex-native/process.js +459 -0
- package/dist/agent-runtimes/codex-native/protocol-types.js +1 -0
- package/dist/agent-runtimes/codex-native/protocol-version.js +7 -0
- package/dist/agent-runtimes/codex-native/redaction.js +31 -0
- package/dist/agent-runtimes/codex-native/requests.js +378 -0
- package/dist/agent-runtimes/codex-native/resource-delivery.js +445 -0
- package/dist/agent-runtimes/codex-native/thread.js +388 -0
- package/dist/agent-runtimes/codex-native/turn.js +849 -0
- package/dist/agent-runtimes/pi/adapter.js +653 -0
- package/dist/agent-runtimes/pi/auth.js +455 -0
- package/dist/agent-runtimes/pi/history.js +640 -0
- package/dist/agent-runtimes/pi/model-catalog.js +87 -0
- package/dist/agent-runtimes/pi/routed-session.js +1464 -0
- package/dist/agent-runtimes/pi/runtime.js +534 -0
- package/dist/agent-runtimes/pi/tool-compiler.js +69 -0
- package/dist/apps/chat/agent-profiles.js +40 -23
- package/dist/apps/chat/agent-store.js +111 -64
- package/dist/apps/chat/chat-api-routes.js +1 -1
- package/dist/apps/chat/chat-request-normalizers.js +47 -0
- package/dist/apps/chat/data/chat-data-mappers.js +25 -9
- package/dist/apps/chat/data/history-query-service.js +108 -0
- package/dist/apps/chat/data/session-query-service.js +24 -3
- package/dist/apps/chat/data/timeline-query-service.js +3 -3
- package/dist/apps/chat/model-catalog.js +1 -55
- package/dist/apps/chat/provider-auth-actions.js +171 -23
- package/dist/apps/chat/stream.js +12 -0
- package/dist/apps/chat/trace-v2.js +1 -0
- package/dist/apps/chat/trace.js +126 -415
- package/dist/apps/chat/web-app.js +582 -157
- package/dist/apps/chat-ui/assets/{dist-BTLtT_Vf.js → dist-9v2ZXT6V.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CMk1aPug.js → dist-B1LVAytl.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-CcwA_IWN.js → dist-C6LPDFXU.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DKOthbxr.js → dist-DVtXxfHc.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-ls2I6BIw.js → dist-DW4Qrfbd.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D1rAtRWs.js → dist-DmbsHdMC.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-C1JQRskW.js → dist-DyvwuSFO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-DfpaylQ-.js → dist-I26VB0QO.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-D10bTFD5.js → dist-RtHbJRPV.js} +1 -1
- package/dist/apps/chat-ui/assets/{dist-5KpmmBBZ.js → dist-sZgJkWZb.js} +1 -1
- package/dist/apps/chat-ui/assets/index-BPbNvcZe.css +1 -0
- package/dist/apps/chat-ui/assets/index-CIOf2uL2.js +237 -0
- package/dist/apps/chat-ui/index.html +2 -2
- package/dist/apps/chat-vscode-web/assets/index-1ivRyjFs.js +41 -0
- package/dist/apps/chat-vscode-web/index.html +1 -1
- package/dist/apps/vscode-artifacts/latest.vsix +0 -0
- package/dist/apps/vscode-artifacts/pibo-vscode-ext-2.0.0.vsix +0 -0
- package/dist/auth/login-actions.js +5 -251
- package/dist/cli-session/localSessionSource.js +8 -1
- package/dist/core/context-build.js +124 -20
- package/dist/core/default-profile.js +8 -0
- package/dist/core/profiles.js +30 -3
- package/dist/core/routed-session.js +5 -1446
- package/dist/core/runtime.js +5 -561
- package/dist/core/session-router.js +637 -83
- package/dist/data/cli.js +20 -3
- package/dist/data/schema.js +104 -1
- package/dist/data/session-store.js +24 -1
- package/dist/debug/events.js +25 -10
- package/dist/debug/failures.js +23 -3
- package/dist/debug/index.js +24 -6
- package/dist/debug/messages.js +87 -7
- package/dist/debug/persisted-payloads.js +31 -0
- package/dist/debug/pty.js +7 -6
- package/dist/debug/runtime-binding.js +29 -0
- package/dist/debug/session.js +137 -0
- package/dist/debug/summary.js +12 -0
- package/dist/debug/telemetry.js +28 -0
- package/dist/debug/tools.js +12 -2
- package/dist/debug/trace.js +154 -76
- package/dist/gateway/server.js +87 -2
- package/dist/gateway/tool.js +5 -5
- package/dist/index.js +21 -2
- package/dist/loops/tools.js +13 -12
- package/dist/mcp/agent-context.js +13 -4
- package/dist/mcp/client.js +10 -6
- package/dist/mcp/config.js +5 -2
- package/dist/mcp/runtime-session.js +166 -0
- package/dist/plugins/builtin.js +191 -53
- package/dist/plugins/codex-native.js +29 -0
- package/dist/plugins/registry.js +66 -2
- package/dist/runs/tools.js +26 -25
- package/dist/session-ui/terminalRows.js +2 -2
- package/dist/sessions/pibo-data-store.js +125 -16
- package/dist/sessions/runtime-binding.js +107 -0
- package/dist/sessions/sqlite-store.js +239 -18
- package/dist/sessions/store.js +86 -7
- package/dist/shared/trace-engine.js +18 -9
- package/dist/shared/trace-event-projection.js +29 -11
- package/dist/shared/{trace-transcript.js → trace-history.js} +193 -195
- package/dist/shared/trace-order.js +9 -4
- package/dist/shared/trace-page-merge.js +1 -1
- package/dist/signals/projector.js +20 -2
- package/dist/signals/registry.js +11 -0
- package/dist/subagents/tool.js +8 -6
- package/dist/tools/codex-browser.js +20 -19
- package/dist/tools/codex-compat.js +10 -9
- package/dist/tools/codex-image-generation.js +8 -6
- package/dist/tools/contract.js +45 -0
- package/dist/tools/credential-registry.js +182 -0
- package/dist/tools/mcp-bridge.js +511 -0
- package/dist/tools/payload-writer.js +17 -0
- package/dist/tools/runtime/tool.js +10 -9
- package/dist/tools/schema.js +9 -0
- package/dist/tools/session-service.js +193 -0
- package/dist/tools/session-tool-set.js +85 -0
- package/dist/web-annotations/tools.js +17 -16
- package/docs/README.md +11 -0
- package/package.json +3 -2
- package/skills/builtin/pibo-agent-runtime-adapter/SKILL.md +183 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/README.md +10 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/evals.json +45 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/orion-full-harness.md +60 -0
- package/skills/builtin/pibo-agent-runtime-adapter/evals/fixtures/relay-partial-harness.md +52 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/capabilities-and-designer.md +194 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/history-debug-and-security.md +213 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/interfaces-and-registration.md +214 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/lifecycle-bindings-and-events.md +193 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/portable-delivery-and-native-behavior.md +226 -0
- package/skills/builtin/pibo-agent-runtime-adapter/references/testing-migration-and-validation.md +256 -0
- package/dist/apps/chat-ui/assets/index-C2YI0xfw.js +0 -237
- package/dist/apps/chat-ui/assets/index-E0RmuCkY.css +0 -1
- package/dist/apps/chat-vscode-web/assets/index-D-tSsXLG.js +0 -41
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { AgentRuntimeAuthError } from "../../agent-runtime/errors.js";
|
|
3
|
+
export const CODEX_NATIVE_AUTH_PROVIDER_ID = "openai-codex";
|
|
4
|
+
export const CODEX_NATIVE_AUTH_PROVIDER_DISPLAY_NAME = "OpenAI for native Codex";
|
|
5
|
+
const CODEX_DEVICE_METHOD = { id: "device_code", completion: "notification" };
|
|
6
|
+
const CODEX_API_KEY_METHOD = { id: "api_key", completion: "immediate" };
|
|
7
|
+
export const CODEX_NATIVE_AUTH_METHODS = [CODEX_DEVICE_METHOD, CODEX_API_KEY_METHOD];
|
|
8
|
+
function isRecord(value) {
|
|
9
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
10
|
+
}
|
|
11
|
+
function protocolFailure(operation) {
|
|
12
|
+
return new AgentRuntimeAuthError("codex_auth_protocol_error", `Native Codex ${operation} returned an invalid response.`, true);
|
|
13
|
+
}
|
|
14
|
+
function operationFailure(operation, retryable = true) {
|
|
15
|
+
return new AgentRuntimeAuthError("codex_auth_failed", `Native Codex ${operation} failed safely.`, retryable);
|
|
16
|
+
}
|
|
17
|
+
function validateAccountRead(value) {
|
|
18
|
+
if (!isRecord(value) || typeof value.requiresOpenaiAuth !== "boolean")
|
|
19
|
+
throw protocolFailure("account status");
|
|
20
|
+
const account = value.account;
|
|
21
|
+
if (account !== undefined && account !== null) {
|
|
22
|
+
if (!isRecord(account) || typeof account.type !== "string")
|
|
23
|
+
throw protocolFailure("account status");
|
|
24
|
+
if (account.type === "apiKey") {
|
|
25
|
+
return { account: { type: "apiKey" }, requiresOpenaiAuth: value.requiresOpenaiAuth };
|
|
26
|
+
}
|
|
27
|
+
if (account.type === "chatgpt") {
|
|
28
|
+
if ((account.email !== null && typeof account.email !== "string") || typeof account.planType !== "string") {
|
|
29
|
+
throw protocolFailure("account status");
|
|
30
|
+
}
|
|
31
|
+
return {
|
|
32
|
+
account: { type: "chatgpt", email: account.email, planType: account.planType },
|
|
33
|
+
requiresOpenaiAuth: value.requiresOpenaiAuth,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (account.type === "amazonBedrock") {
|
|
37
|
+
if (account.usesCodexManagedCredentials !== undefined && typeof account.usesCodexManagedCredentials !== "boolean") {
|
|
38
|
+
throw protocolFailure("account status");
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
account: {
|
|
42
|
+
type: "amazonBedrock",
|
|
43
|
+
...(typeof account.usesCodexManagedCredentials === "boolean"
|
|
44
|
+
? { usesCodexManagedCredentials: account.usesCodexManagedCredentials }
|
|
45
|
+
: {}),
|
|
46
|
+
},
|
|
47
|
+
requiresOpenaiAuth: value.requiresOpenaiAuth,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
throw protocolFailure("account status");
|
|
51
|
+
}
|
|
52
|
+
return { account: null, requiresOpenaiAuth: value.requiresOpenaiAuth };
|
|
53
|
+
}
|
|
54
|
+
function validateDeviceStart(value) {
|
|
55
|
+
if (!isRecord(value)
|
|
56
|
+
|| value.type !== "chatgptDeviceCode"
|
|
57
|
+
|| typeof value.loginId !== "string"
|
|
58
|
+
|| value.loginId.length === 0
|
|
59
|
+
|| value.loginId.length > 512
|
|
60
|
+
|| typeof value.userCode !== "string"
|
|
61
|
+
|| value.userCode.length === 0
|
|
62
|
+
|| value.userCode.length > 128
|
|
63
|
+
|| typeof value.verificationUrl !== "string"
|
|
64
|
+
|| value.verificationUrl.length === 0
|
|
65
|
+
|| value.verificationUrl.length > 2_048) {
|
|
66
|
+
throw protocolFailure("device login start");
|
|
67
|
+
}
|
|
68
|
+
let verificationUrl;
|
|
69
|
+
try {
|
|
70
|
+
verificationUrl = new URL(value.verificationUrl);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
throw protocolFailure("device login start");
|
|
74
|
+
}
|
|
75
|
+
if (verificationUrl.protocol !== "https:" && verificationUrl.protocol !== "http:") {
|
|
76
|
+
throw protocolFailure("device login start");
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
type: "chatgptDeviceCode",
|
|
80
|
+
loginId: value.loginId,
|
|
81
|
+
userCode: value.userCode,
|
|
82
|
+
verificationUrl: value.verificationUrl,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function validateApiKeyStart(value) {
|
|
86
|
+
if (!isRecord(value) || value.type !== "apiKey")
|
|
87
|
+
throw protocolFailure("API-key login");
|
|
88
|
+
}
|
|
89
|
+
function validateLoginNotification(value) {
|
|
90
|
+
if (!isRecord(value) || typeof value.success !== "boolean")
|
|
91
|
+
return undefined;
|
|
92
|
+
if (value.loginId !== undefined && value.loginId !== null && typeof value.loginId !== "string")
|
|
93
|
+
return undefined;
|
|
94
|
+
if (value.error !== undefined && value.error !== null && typeof value.error !== "string")
|
|
95
|
+
return undefined;
|
|
96
|
+
return {
|
|
97
|
+
success: value.success,
|
|
98
|
+
...(value.loginId === null || typeof value.loginId === "string" ? { loginId: value.loginId } : {}),
|
|
99
|
+
...(value.error === null || typeof value.error === "string" ? { error: value.error } : {}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function validateCancelResponse(value) {
|
|
103
|
+
if (!isRecord(value) || (value.status !== "canceled" && value.status !== "notFound")) {
|
|
104
|
+
throw protocolFailure("login cancellation");
|
|
105
|
+
}
|
|
106
|
+
return { status: value.status };
|
|
107
|
+
}
|
|
108
|
+
function validateLogoutResponse(value) {
|
|
109
|
+
if (!isRecord(value))
|
|
110
|
+
throw protocolFailure("logout");
|
|
111
|
+
return {};
|
|
112
|
+
}
|
|
113
|
+
function accountResult(account) {
|
|
114
|
+
if (!account.account) {
|
|
115
|
+
if (!account.requiresOpenaiAuth) {
|
|
116
|
+
return {
|
|
117
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
118
|
+
state: "partial",
|
|
119
|
+
configured: true,
|
|
120
|
+
message: "Native Codex reports that managed OpenAI authentication is not required.",
|
|
121
|
+
details: { accountType: "unknown" },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
return {
|
|
125
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
126
|
+
state: "disconnected",
|
|
127
|
+
configured: false,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
if (account.account.type === "apiKey") {
|
|
131
|
+
return {
|
|
132
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
133
|
+
state: "connected",
|
|
134
|
+
configured: true,
|
|
135
|
+
details: { accountType: "api_key" },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (account.account.type === "chatgpt") {
|
|
139
|
+
return {
|
|
140
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
141
|
+
state: "connected",
|
|
142
|
+
configured: true,
|
|
143
|
+
details: {
|
|
144
|
+
accountType: "chatgpt",
|
|
145
|
+
planType: account.account.planType,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
151
|
+
state: "partial",
|
|
152
|
+
configured: true,
|
|
153
|
+
message: "Native Codex reports an account type that Pibo does not configure.",
|
|
154
|
+
details: { accountType: "unknown" },
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
function statusFromResult(result) {
|
|
158
|
+
return {
|
|
159
|
+
id: result.providerId,
|
|
160
|
+
displayName: CODEX_NATIVE_AUTH_PROVIDER_DISPLAY_NAME,
|
|
161
|
+
state: result.state,
|
|
162
|
+
configured: result.configured,
|
|
163
|
+
methods: [...CODEX_NATIVE_AUTH_METHODS],
|
|
164
|
+
...(result.flow ? { pending: { ...result.flow } } : {}),
|
|
165
|
+
...(result.message ? { message: result.message } : {}),
|
|
166
|
+
...(result.details ? { details: { ...result.details } } : {}),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
export class CodexNativeAuthController {
|
|
170
|
+
config;
|
|
171
|
+
startProcess;
|
|
172
|
+
now;
|
|
173
|
+
createFlowId;
|
|
174
|
+
flows = new Map();
|
|
175
|
+
constructor(options) {
|
|
176
|
+
this.config = options.config;
|
|
177
|
+
this.startProcess = options.startProcess;
|
|
178
|
+
this.now = options.now ?? Date.now;
|
|
179
|
+
this.createFlowId = options.createFlowId ?? randomUUID;
|
|
180
|
+
}
|
|
181
|
+
async getStatus() {
|
|
182
|
+
const flow = this.latestFlow();
|
|
183
|
+
if (flow) {
|
|
184
|
+
await this.failIfProcessStopped(flow);
|
|
185
|
+
if (flow.terminal && !flow.terminal.configured)
|
|
186
|
+
return [statusFromResult(flow.terminal)];
|
|
187
|
+
if (!flow.terminal) {
|
|
188
|
+
return [statusFromResult({
|
|
189
|
+
providerId: flow.providerId,
|
|
190
|
+
state: "pending",
|
|
191
|
+
configured: false,
|
|
192
|
+
flow: { ...flow.flow },
|
|
193
|
+
})];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
try {
|
|
197
|
+
const result = await this.withProcess("status", async (process) => accountResult(await this.readAccount(process)));
|
|
198
|
+
return [statusFromResult(result)];
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return [statusFromResult({
|
|
202
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
203
|
+
state: "failed",
|
|
204
|
+
configured: false,
|
|
205
|
+
message: "Native Codex authentication status is unavailable. Retry after checking the runtime.",
|
|
206
|
+
})];
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async start(input) {
|
|
210
|
+
this.assertProvider(input.providerId);
|
|
211
|
+
const active = [...this.flows.values()].find((flow) => !flow.terminal);
|
|
212
|
+
if (active)
|
|
213
|
+
throw new AgentRuntimeAuthError("codex_auth_pending", "A native Codex login is already pending.", true);
|
|
214
|
+
this.flows.clear();
|
|
215
|
+
if (input.method === "api_key")
|
|
216
|
+
return await this.startApiKey(input.apiKey);
|
|
217
|
+
if (input.method !== "device_code") {
|
|
218
|
+
throw new AgentRuntimeAuthError("codex_auth_method_unsupported", `Native Codex does not support ${input.method} login.`, false);
|
|
219
|
+
}
|
|
220
|
+
return await this.startDeviceCode();
|
|
221
|
+
}
|
|
222
|
+
async complete(input) {
|
|
223
|
+
this.assertProvider(input.providerId);
|
|
224
|
+
const flow = this.requireFlow(input.flowId);
|
|
225
|
+
if (flow.providerId !== input.providerId)
|
|
226
|
+
throw new AgentRuntimeAuthError("codex_auth_flow_mismatch", "Login flow does not match the selected provider.");
|
|
227
|
+
await this.failIfProcessStopped(flow);
|
|
228
|
+
if (flow.settling)
|
|
229
|
+
await flow.settling;
|
|
230
|
+
if (flow.terminal)
|
|
231
|
+
return { ...flow.terminal, ...(flow.terminal.details ? { details: { ...flow.terminal.details } } : {}) };
|
|
232
|
+
return {
|
|
233
|
+
providerId: flow.providerId,
|
|
234
|
+
state: "pending",
|
|
235
|
+
configured: false,
|
|
236
|
+
flow: { ...flow.flow },
|
|
237
|
+
};
|
|
238
|
+
}
|
|
239
|
+
async cancel(input) {
|
|
240
|
+
this.assertProvider(input.providerId);
|
|
241
|
+
const flow = this.requireFlow(input.flowId);
|
|
242
|
+
if (flow.providerId !== input.providerId)
|
|
243
|
+
throw new AgentRuntimeAuthError("codex_auth_flow_mismatch", "Login flow does not match the selected provider.");
|
|
244
|
+
if (flow.settling)
|
|
245
|
+
await flow.settling;
|
|
246
|
+
if (flow.terminal)
|
|
247
|
+
return { ...flow.terminal };
|
|
248
|
+
const process = flow.process;
|
|
249
|
+
if (process) {
|
|
250
|
+
try {
|
|
251
|
+
validateCancelResponse(await process.client.request("account/login/cancel", { loginId: flow.nativeLoginId }));
|
|
252
|
+
}
|
|
253
|
+
catch {
|
|
254
|
+
// Closing the owned process is the cancellation fallback.
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
await this.settle(flow, async () => ({
|
|
258
|
+
providerId: flow.providerId,
|
|
259
|
+
state: "disconnected",
|
|
260
|
+
configured: false,
|
|
261
|
+
message: "Login was canceled.",
|
|
262
|
+
}));
|
|
263
|
+
this.flows.delete(input.flowId);
|
|
264
|
+
try {
|
|
265
|
+
const current = await this.withProcess("cancel-status", async (currentProcess) => accountResult(await this.readAccount(currentProcess)));
|
|
266
|
+
return { ...current, message: "Login was canceled." };
|
|
267
|
+
}
|
|
268
|
+
catch {
|
|
269
|
+
return { ...flow.terminal };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
async logout(input) {
|
|
273
|
+
this.assertProvider(input.providerId);
|
|
274
|
+
for (const flow of this.flows.values()) {
|
|
275
|
+
if (!flow.terminal)
|
|
276
|
+
await this.cancel({ providerId: flow.providerId, flowId: flow.flow.flowId });
|
|
277
|
+
}
|
|
278
|
+
this.flows.clear();
|
|
279
|
+
try {
|
|
280
|
+
await this.withProcess("logout", async (process) => {
|
|
281
|
+
validateLogoutResponse(await process.client.request("account/logout"));
|
|
282
|
+
const status = accountResult(await this.readAccount(process));
|
|
283
|
+
if (status.configured || status.state !== "disconnected")
|
|
284
|
+
throw protocolFailure("logout verification");
|
|
285
|
+
});
|
|
286
|
+
}
|
|
287
|
+
catch (error) {
|
|
288
|
+
if (error instanceof AgentRuntimeAuthError)
|
|
289
|
+
throw error;
|
|
290
|
+
throw operationFailure("logout");
|
|
291
|
+
}
|
|
292
|
+
return { providerId: input.providerId, state: "disconnected", configured: false };
|
|
293
|
+
}
|
|
294
|
+
async dispose() {
|
|
295
|
+
const flows = [...this.flows.values()];
|
|
296
|
+
this.flows.clear();
|
|
297
|
+
await Promise.allSettled(flows.map(async (flow) => {
|
|
298
|
+
if (flow.timer)
|
|
299
|
+
clearTimeout(flow.timer);
|
|
300
|
+
flow.unsubscribe?.();
|
|
301
|
+
await flow.process?.close();
|
|
302
|
+
flow.process = undefined;
|
|
303
|
+
}));
|
|
304
|
+
}
|
|
305
|
+
async startApiKey(apiKey) {
|
|
306
|
+
try {
|
|
307
|
+
return await this.withProcess("api-key", async (process) => {
|
|
308
|
+
validateApiKeyStart(await process.client.request("account/login/start", { type: "apiKey", apiKey }));
|
|
309
|
+
const result = accountResult(await this.readAccount(process));
|
|
310
|
+
if (!result.configured || result.details?.accountType !== "api_key")
|
|
311
|
+
throw protocolFailure("API-key login verification");
|
|
312
|
+
return result;
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
if (error instanceof AgentRuntimeAuthError)
|
|
317
|
+
throw error;
|
|
318
|
+
throw operationFailure("API-key login");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
async startDeviceCode() {
|
|
322
|
+
let process;
|
|
323
|
+
let unsubscribe;
|
|
324
|
+
const flowId = this.createFlowId();
|
|
325
|
+
let queuedNotification;
|
|
326
|
+
try {
|
|
327
|
+
process = await this.startProcess(`auth-device-${flowId}`);
|
|
328
|
+
unsubscribe = process.client.subscribeNotifications((notification) => {
|
|
329
|
+
if (notification.method !== "account/login/completed")
|
|
330
|
+
return;
|
|
331
|
+
const parsed = validateLoginNotification(notification.params);
|
|
332
|
+
if (!parsed)
|
|
333
|
+
return;
|
|
334
|
+
const flow = this.flows.get(flowId);
|
|
335
|
+
if (!flow) {
|
|
336
|
+
queuedNotification = parsed;
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
void this.handleLoginCompleted(flow, parsed);
|
|
340
|
+
});
|
|
341
|
+
const started = validateDeviceStart(await process.client.request("account/login/start", { type: "chatgptDeviceCode" }));
|
|
342
|
+
const now = this.now();
|
|
343
|
+
const flow = {
|
|
344
|
+
flowId,
|
|
345
|
+
method: "device_code",
|
|
346
|
+
completion: "notification",
|
|
347
|
+
startedAt: new Date(now).toISOString(),
|
|
348
|
+
expiresAt: new Date(now + this.config.authLoginTimeoutMs).toISOString(),
|
|
349
|
+
verificationUrl: started.verificationUrl,
|
|
350
|
+
userCode: started.userCode,
|
|
351
|
+
instructions: "Open the verification URL, enter the one-time code, and finish sign-in. Pibo will detect completion automatically.",
|
|
352
|
+
};
|
|
353
|
+
const pending = {
|
|
354
|
+
providerId: CODEX_NATIVE_AUTH_PROVIDER_ID,
|
|
355
|
+
nativeLoginId: started.loginId,
|
|
356
|
+
flow,
|
|
357
|
+
process,
|
|
358
|
+
unsubscribe,
|
|
359
|
+
};
|
|
360
|
+
pending.timer = setTimeout(() => {
|
|
361
|
+
void this.settle(pending, async () => ({
|
|
362
|
+
providerId: pending.providerId,
|
|
363
|
+
state: "failed",
|
|
364
|
+
configured: false,
|
|
365
|
+
message: "Native Codex login timed out. Start a new login.",
|
|
366
|
+
}));
|
|
367
|
+
}, this.config.authLoginTimeoutMs);
|
|
368
|
+
pending.timer.unref?.();
|
|
369
|
+
this.flows.set(flowId, pending);
|
|
370
|
+
if (queuedNotification)
|
|
371
|
+
void this.handleLoginCompleted(pending, queuedNotification);
|
|
372
|
+
return {
|
|
373
|
+
providerId: pending.providerId,
|
|
374
|
+
state: "pending",
|
|
375
|
+
configured: false,
|
|
376
|
+
flow: { ...flow },
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
unsubscribe?.();
|
|
381
|
+
await process?.close().catch(() => { });
|
|
382
|
+
if (error instanceof AgentRuntimeAuthError)
|
|
383
|
+
throw error;
|
|
384
|
+
throw operationFailure("device login start");
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async handleLoginCompleted(flow, notification) {
|
|
388
|
+
if (notification.loginId && notification.loginId !== flow.nativeLoginId)
|
|
389
|
+
return;
|
|
390
|
+
await this.settle(flow, async () => {
|
|
391
|
+
if (!notification.success) {
|
|
392
|
+
return {
|
|
393
|
+
providerId: flow.providerId,
|
|
394
|
+
state: "failed",
|
|
395
|
+
configured: false,
|
|
396
|
+
message: "Native Codex login was not completed. Retry the login flow.",
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
const process = flow.process;
|
|
400
|
+
if (!process)
|
|
401
|
+
throw operationFailure("login completion");
|
|
402
|
+
const result = accountResult(await this.readAccount(process));
|
|
403
|
+
if (!result.configured)
|
|
404
|
+
throw protocolFailure("login completion verification");
|
|
405
|
+
return result;
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
async settle(flow, operation) {
|
|
409
|
+
if (flow.terminal)
|
|
410
|
+
return;
|
|
411
|
+
if (!flow.settling) {
|
|
412
|
+
flow.settling = (async () => {
|
|
413
|
+
let result;
|
|
414
|
+
try {
|
|
415
|
+
result = await operation();
|
|
416
|
+
}
|
|
417
|
+
catch {
|
|
418
|
+
result = {
|
|
419
|
+
providerId: flow.providerId,
|
|
420
|
+
state: "failed",
|
|
421
|
+
configured: false,
|
|
422
|
+
message: "Native Codex login completion failed safely. Start a new login.",
|
|
423
|
+
};
|
|
424
|
+
}
|
|
425
|
+
flow.terminal = result;
|
|
426
|
+
if (flow.timer)
|
|
427
|
+
clearTimeout(flow.timer);
|
|
428
|
+
flow.timer = undefined;
|
|
429
|
+
flow.unsubscribe?.();
|
|
430
|
+
flow.unsubscribe = undefined;
|
|
431
|
+
await flow.process?.close().catch(() => { });
|
|
432
|
+
flow.process = undefined;
|
|
433
|
+
})();
|
|
434
|
+
}
|
|
435
|
+
await flow.settling;
|
|
436
|
+
}
|
|
437
|
+
async failIfProcessStopped(flow) {
|
|
438
|
+
if (flow.terminal || flow.settling)
|
|
439
|
+
return;
|
|
440
|
+
const state = flow.process?.client.snapshot.state;
|
|
441
|
+
if (state === "ready" || state === "starting" || state === "initializing")
|
|
442
|
+
return;
|
|
443
|
+
await this.settle(flow, async () => ({
|
|
444
|
+
providerId: flow.providerId,
|
|
445
|
+
state: "failed",
|
|
446
|
+
configured: false,
|
|
447
|
+
message: "Native Codex login process stopped. Start a new login.",
|
|
448
|
+
}));
|
|
449
|
+
}
|
|
450
|
+
async readAccount(process) {
|
|
451
|
+
return validateAccountRead(await process.client.request("account/read", { refreshToken: false }));
|
|
452
|
+
}
|
|
453
|
+
async withProcess(label, operation) {
|
|
454
|
+
const process = await this.startProcess(`auth-${label}-${randomUUID()}`);
|
|
455
|
+
try {
|
|
456
|
+
return await operation(process);
|
|
457
|
+
}
|
|
458
|
+
finally {
|
|
459
|
+
await process.close();
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
latestFlow() {
|
|
463
|
+
return [...this.flows.values()].at(-1);
|
|
464
|
+
}
|
|
465
|
+
requireFlow(flowId) {
|
|
466
|
+
const flow = this.flows.get(flowId);
|
|
467
|
+
if (!flow)
|
|
468
|
+
throw new AgentRuntimeAuthError("codex_auth_flow_missing", "Invalid or expired native Codex login flow. Start a new login.", true);
|
|
469
|
+
return flow;
|
|
470
|
+
}
|
|
471
|
+
assertProvider(providerId) {
|
|
472
|
+
if (providerId !== CODEX_NATIVE_AUTH_PROVIDER_ID) {
|
|
473
|
+
throw new AgentRuntimeAuthError("codex_auth_provider_unsupported", `Native Codex does not support provider "${providerId}".`, false);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|