@jeffusion/bungee-core 5.10.0 → 5.12.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.
Files changed (51) hide show
  1. package/dist/main.js +31436 -17327
  2. package/dist/plugins/ai-transformer/index.js.map +2 -2
  3. package/dist/plugins/anthropic-request-sanitizer/index.js.map +2 -2
  4. package/dist/plugins/anthropic-tool-name-transformer/index.js.map +2 -2
  5. package/dist/plugins/chatgpt-oauth/control.js +850 -47
  6. package/dist/plugins/chatgpt-oauth/control.js.map +16 -7
  7. package/dist/plugins/chatgpt-oauth/index.js +691 -16
  8. package/dist/plugins/chatgpt-oauth/index.js.map +15 -7
  9. package/dist/plugins/chatgpt-oauth/manifest.json +117 -7
  10. package/dist/plugins/chatgpt-oauth/ui/AccountsPage.svelte +25 -15
  11. package/dist/plugins/chatgpt-oauth/ui/ChatgptQuotaWidget.svelte +7 -4
  12. package/dist/plugins/chatgpt-oauth/ui/account-model.js +16 -7
  13. package/dist/plugins/deepseek-reasoning-fix/index.js.map +2 -2
  14. package/dist/plugins/key-access/index.js.map +2 -2
  15. package/dist/plugins/key-rate-limit/index.js.map +2 -2
  16. package/dist/plugins/local-accounts/control.js.map +2 -2
  17. package/dist/plugins/local-accounts/index.js.map +2 -2
  18. package/dist/plugins/model-mapping/control.js +72 -17741
  19. package/dist/plugins/model-mapping/control.js.map +6 -58
  20. package/dist/plugins/model-mapping/index.js +32 -17433
  21. package/dist/plugins/model-mapping/index.js.map +6 -58
  22. package/dist/plugins/model-mapping/manifest.json +30 -14
  23. package/dist/plugins/models-dev/control.js +1042 -0
  24. package/dist/plugins/models-dev/control.js.map +16 -0
  25. package/dist/plugins/models-dev/index.js +15418 -0
  26. package/dist/plugins/models-dev/index.js.map +134 -0
  27. package/dist/plugins/models-dev/manifest.json +199 -0
  28. package/dist/plugins/models-dev/ui/ModelsDevSettings.svelte +120 -0
  29. package/dist/plugins/openai-messages-to-chat/index.js.map +2 -2
  30. package/dist/plugins/signature-repair/index.js.map +2 -2
  31. package/dist/plugins/token-budget/control.js +524 -9
  32. package/dist/plugins/token-budget/control.js.map +12 -4
  33. package/dist/plugins/token-budget/index.js +519 -12
  34. package/dist/plugins/token-budget/index.js.map +13 -6
  35. package/dist/plugins/token-budget/manifest.json +16 -2
  36. package/dist/plugins/token-metering/index.js +93 -2
  37. package/dist/plugins/token-metering/index.js.map +12 -6
  38. package/dist/plugins/token-stats/control.js +174 -312
  39. package/dist/plugins/token-stats/control.js.map +7 -7
  40. package/dist/plugins/token-stats/index.js +283 -486
  41. package/dist/plugins/token-stats/index.js.map +16 -12
  42. package/dist/plugins/token-stats/manifest.json +99 -12
  43. package/dist/plugins/token-stats/ui/ClientModelPicker.svelte +76 -0
  44. package/dist/plugins/token-stats/ui/PriceModelPicker.svelte +87 -0
  45. package/dist/plugins/token-stats/ui/TokenStatsSettings.svelte +136 -80
  46. package/dist/plugins/token-stats/ui/client-model-search.test.ts +58 -0
  47. package/dist/plugins/token-stats/ui/client-model-search.ts +16 -0
  48. package/dist/plugins/token-stats/ui/model-search.ts +45 -0
  49. package/dist/plugins/token-stats/ui/pricing-catalog.test.ts +114 -0
  50. package/dist/plugins/token-stats/ui/pricing-catalog.ts +17 -0
  51. package/package.json +2 -2
@@ -1,6 +1,512 @@
1
1
  // @bun
2
+ // packages/core/src/plugin-services/host-rpc.ts
3
+ import { AsyncLocalStorage } from "async_hooks";
4
+
5
+ // packages/core/src/plugin-services/wire-contract.ts
6
+ import { isProxy } from "util/types";
7
+ var RPC_JSON_MAX_BYTES = 64 * 1024;
8
+ var RPC_CONTRACT_MAX_BYTES = 256 * 1024;
9
+ var JSON_MAX_DEPTH = 64;
10
+ var SCHEMA_MAX_DEPTH = 64;
11
+ var RPC_MAX_TIMEOUT_MS = 300000;
12
+ var RPC_MAX_METHODS = 128;
13
+ var RPC_SERVICE_ID = /^[a-z][a-z0-9.-]{0,127}$/;
14
+ var RPC_METHOD_NAME = /^[A-Za-z_][A-Za-z0-9_.-]{0,127}$/;
15
+ var RPC_CALL_PURPOSES = new Set(["bootstrap", "background", "management", "request", "attempt"]);
16
+ var RPC_COMMAND_DEDUPLICATION = new Set(["local-transaction", "external-contract", "none"]);
17
+ var RPC_COMMAND_POLICY_KEYS = new Set(["deduplication", "resultRetentionMs", "quotaBytes", "maxResultBytes"]);
18
+ var CONTRACT_KEYS = ["id", "version", "methods"];
19
+ var METHOD_KEYS = [
20
+ "kind",
21
+ "input",
22
+ "output",
23
+ "purposes",
24
+ "timeoutMs",
25
+ "maxInputBytes",
26
+ "maxOutputBytes",
27
+ "command"
28
+ ];
29
+
30
+ class RpcProtocolError extends Error {
31
+ code;
32
+ name = "RpcProtocolError";
33
+ constructor(code, message) {
34
+ super(message);
35
+ this.code = code;
36
+ }
37
+ }
38
+ function failData(category) {
39
+ throw new RpcProtocolError("invalid_data", `invalid_data in ${category}`);
40
+ }
41
+ function failSize() {
42
+ throw new RpcProtocolError("size_limit", "size_limit: JSON value exceeds the byte limit");
43
+ }
44
+ function failContract(reason) {
45
+ throw new RpcProtocolError("invalid_contract", `invalid_contract: ${reason}`);
46
+ }
47
+ function isPlainObject(value) {
48
+ if (typeof value !== "object" || value === null || Array.isArray(value))
49
+ return false;
50
+ if (isProxy(value))
51
+ return false;
52
+ const prototype = Object.getPrototypeOf(value);
53
+ return prototype === Object.prototype || prototype === null;
54
+ }
55
+ function isPositiveSafeInteger(value) {
56
+ return typeof value === "number" && Number.isSafeInteger(value) && value > 0;
57
+ }
58
+ function isNonNegativeSafeInteger(value) {
59
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
60
+ }
61
+ function hasLoneSurrogate(value) {
62
+ for (let index = 0;index < value.length; index += 1) {
63
+ const code = value.charCodeAt(index);
64
+ if (code >= 55296 && code <= 56319) {
65
+ if (index + 1 >= value.length)
66
+ return true;
67
+ const next = value.charCodeAt(index + 1);
68
+ if (next < 56320 || next > 57343)
69
+ return true;
70
+ index += 1;
71
+ } else if (code >= 56320 && code <= 57343) {
72
+ return true;
73
+ }
74
+ }
75
+ return false;
76
+ }
77
+ function isArrayIndex(key, length) {
78
+ if (!/^(?:0|[1-9]\d*)$/.test(key))
79
+ return false;
80
+ const index = Number(key);
81
+ return Number.isSafeInteger(index) && index >= 0 && index < length;
82
+ }
83
+ function assertKnownKeys(input, allowed, category) {
84
+ for (const key of Object.keys(input)) {
85
+ if (!allowed.includes(key))
86
+ failContract(`unknown ${category} field`);
87
+ }
88
+ }
89
+ function spend(budget, bytes) {
90
+ budget.left -= bytes;
91
+ if (budget.left < 0)
92
+ failSize();
93
+ }
94
+ function snapshot(value, depth, ancestors, budget) {
95
+ if (value === null) {
96
+ spend(budget, 4);
97
+ return null;
98
+ }
99
+ if (typeof value === "boolean") {
100
+ spend(budget, value ? 4 : 5);
101
+ return value;
102
+ }
103
+ if (typeof value === "string") {
104
+ if (hasLoneSurrogate(value))
105
+ failData("string");
106
+ spend(budget, Buffer.byteLength(value, "utf8") + 2);
107
+ return value;
108
+ }
109
+ if (typeof value === "number") {
110
+ if (!Number.isFinite(value))
111
+ failData("number");
112
+ spend(budget, 1);
113
+ return value;
114
+ }
115
+ if (typeof value !== "object")
116
+ failData("json");
117
+ const object = value;
118
+ if (isProxy(object))
119
+ failData("json");
120
+ if (ancestors.has(object))
121
+ failData("json");
122
+ const nextDepth = depth + 1;
123
+ if (nextDepth > JSON_MAX_DEPTH)
124
+ failData("json");
125
+ if (Array.isArray(object)) {
126
+ if (Object.getPrototypeOf(object) !== Array.prototype)
127
+ failData("array");
128
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(object, "length");
129
+ if (!lengthDescriptor || !("value" in lengthDescriptor) || typeof lengthDescriptor.value !== "number") {
130
+ failData("array");
131
+ }
132
+ const length = lengthDescriptor.value;
133
+ if (!Number.isSafeInteger(length) || length < 0)
134
+ failData("array");
135
+ if (length > budget.left)
136
+ failSize();
137
+ spend(budget, 2);
138
+ if (length > 1)
139
+ spend(budget, length - 1);
140
+ ancestors.add(object);
141
+ const items = new Array(length);
142
+ let count = 0;
143
+ for (const key of Reflect.ownKeys(object)) {
144
+ if (key === "length")
145
+ continue;
146
+ if (typeof key !== "string" || !isArrayIndex(key, length))
147
+ failData("array");
148
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
149
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
150
+ failData("array");
151
+ items[Number(key)] = snapshot(descriptor.value, nextDepth, ancestors, budget);
152
+ count += 1;
153
+ }
154
+ if (count !== length)
155
+ failData("array");
156
+ ancestors.delete(object);
157
+ return items;
158
+ }
159
+ const prototype = Object.getPrototypeOf(object);
160
+ if (prototype !== Object.prototype && prototype !== null)
161
+ failData("object");
162
+ const keys = Reflect.ownKeys(object);
163
+ if (keys.length > budget.left)
164
+ failSize();
165
+ spend(budget, 2);
166
+ if (keys.length > 1)
167
+ spend(budget, keys.length - 1);
168
+ ancestors.add(object);
169
+ const result = {};
170
+ for (const key of keys) {
171
+ if (typeof key !== "string")
172
+ failData("object");
173
+ if (hasLoneSurrogate(key))
174
+ failData("object");
175
+ const descriptor = Object.getOwnPropertyDescriptor(object, key);
176
+ if (!descriptor || !descriptor.enumerable || !("value" in descriptor))
177
+ failData("object");
178
+ spend(budget, Buffer.byteLength(key, "utf8") + 3);
179
+ Object.defineProperty(result, key, {
180
+ value: snapshot(descriptor.value, nextDepth, ancestors, budget),
181
+ enumerable: true,
182
+ writable: true,
183
+ configurable: true
184
+ });
185
+ }
186
+ ancestors.delete(object);
187
+ return result;
188
+ }
189
+ function snapshotPureInput(input) {
190
+ try {
191
+ const pure = snapshot(input, 0, new Set, { left: RPC_CONTRACT_MAX_BYTES });
192
+ if (Buffer.byteLength(JSON.stringify(pure), "utf8") > RPC_CONTRACT_MAX_BYTES)
193
+ failContract("input must be bounded pure JSON data");
194
+ return pure;
195
+ } catch (error) {
196
+ if (error instanceof RpcProtocolError && (error.code === "invalid_data" || error.code === "size_limit")) {
197
+ failContract("input must be bounded pure JSON data");
198
+ }
199
+ throw error;
200
+ }
201
+ }
202
+ function checkOptionalNonNegative(value, field) {
203
+ if (value === undefined)
204
+ return;
205
+ if (!isNonNegativeSafeInteger(value))
206
+ failContract(`${field} must be a non-negative safe integer`);
207
+ return value;
208
+ }
209
+ function checkOptionalFinite(value, field) {
210
+ if (value === undefined)
211
+ return;
212
+ if (typeof value !== "number" || !Number.isFinite(value))
213
+ failContract(`${field} must be a finite number`);
214
+ return value;
215
+ }
216
+ function checkSchema(input, depth, seen) {
217
+ if (depth > SCHEMA_MAX_DEPTH)
218
+ failContract("schema nesting exceeds the limit");
219
+ if (!isPlainObject(input))
220
+ failContract("schema must be a plain object");
221
+ if (seen.has(input))
222
+ failContract("schema must not contain cycles");
223
+ seen.add(input);
224
+ const type = input.type;
225
+ if (type === "string") {
226
+ assertKnownKeys(input, ["type", "minLength", "maxLength"], "schema");
227
+ const min = checkOptionalNonNegative(input.minLength, "minLength");
228
+ const max = checkOptionalNonNegative(input.maxLength, "maxLength");
229
+ if (min !== undefined && max !== undefined && min > max)
230
+ failContract("string minLength must not exceed maxLength");
231
+ } else if (type === "number") {
232
+ assertKnownKeys(input, ["type", "integer", "minimum", "maximum"], "schema");
233
+ if (input.integer !== undefined && typeof input.integer !== "boolean")
234
+ failContract("number integer flag must be boolean");
235
+ const min = checkOptionalFinite(input.minimum, "minimum");
236
+ const max = checkOptionalFinite(input.maximum, "maximum");
237
+ if (min !== undefined && max !== undefined && min > max)
238
+ failContract("number minimum must not exceed maximum");
239
+ } else if (type === "boolean") {
240
+ assertKnownKeys(input, ["type"], "schema");
241
+ } else if (type === "null") {
242
+ assertKnownKeys(input, ["type"], "schema");
243
+ } else if (type === "literal") {
244
+ assertKnownKeys(input, ["type", "value"], "schema");
245
+ if (!Object.prototype.hasOwnProperty.call(input, "value"))
246
+ failContract("literal schema requires a value");
247
+ try {
248
+ snapshot(input.value, 0, new Set, { left: Infinity });
249
+ } catch (error) {
250
+ if (error instanceof RpcProtocolError && error.code === "invalid_data") {
251
+ failContract("literal value must be pure JSON");
252
+ }
253
+ throw error;
254
+ }
255
+ } else if (type === "array") {
256
+ assertKnownKeys(input, ["type", "items", "maxItems"], "schema");
257
+ if (!Object.prototype.hasOwnProperty.call(input, "items"))
258
+ failContract("array schema requires items");
259
+ checkSchema(input.items, depth + 1, seen);
260
+ checkOptionalNonNegative(input.maxItems, "maxItems");
261
+ } else if (type === "object") {
262
+ assertKnownKeys(input, ["type", "properties", "optional"], "schema");
263
+ const properties = input.properties;
264
+ if (!isPlainObject(properties))
265
+ failContract("object schema requires a properties record");
266
+ for (const key of Object.keys(properties))
267
+ checkSchema(properties[key], depth + 1, seen);
268
+ const optional = input.optional;
269
+ if (optional !== undefined) {
270
+ if (!Array.isArray(optional))
271
+ failContract("object optional must be an array of field names");
272
+ const names = new Set;
273
+ for (const name of optional) {
274
+ if (typeof name !== "string")
275
+ failContract("object optional entries must be strings");
276
+ if (names.has(name))
277
+ failContract("object optional entries must be unique");
278
+ names.add(name);
279
+ if (!Object.prototype.hasOwnProperty.call(properties, name)) {
280
+ failContract("object optional names must exist in properties");
281
+ }
282
+ }
283
+ }
284
+ } else if (type === "record") {
285
+ assertKnownKeys(input, ["type", "values", "maxEntries"], "schema");
286
+ if (!Object.prototype.hasOwnProperty.call(input, "values"))
287
+ failContract("record schema requires values");
288
+ checkSchema(input.values, depth + 1, seen);
289
+ checkOptionalNonNegative(input.maxEntries, "maxEntries");
290
+ } else if (type === "union") {
291
+ assertKnownKeys(input, ["type", "variants"], "schema");
292
+ const variants = input.variants;
293
+ if (!Array.isArray(variants) || variants.length === 0) {
294
+ failContract("union schema requires a non-empty variants array");
295
+ }
296
+ for (const variant of variants)
297
+ checkSchema(variant, depth + 1, seen);
298
+ } else if (type === "json") {
299
+ assertKnownKeys(input, ["type", "maxBytes"], "schema");
300
+ if (input.maxBytes !== undefined && !isPositiveSafeInteger(input.maxBytes)) {
301
+ failContract("json schema maxBytes must be a positive safe integer");
302
+ }
303
+ } else {
304
+ failContract("unknown schema type");
305
+ }
306
+ seen.delete(input);
307
+ }
308
+ function checkByteLimit(value, field) {
309
+ if (value === undefined)
310
+ return;
311
+ if (!isPositiveSafeInteger(value) || value > RPC_JSON_MAX_BYTES) {
312
+ failContract(`${field} must be a positive integer no larger than the RPC envelope`);
313
+ }
314
+ return value;
315
+ }
316
+ function checkCommandPolicy(policy, maxOutputBytes) {
317
+ const keys = Object.keys(policy);
318
+ if (keys.length !== RPC_COMMAND_POLICY_KEYS.size || keys.some((key) => !RPC_COMMAND_POLICY_KEYS.has(key))) {
319
+ failContract("command policy fields are fixed");
320
+ }
321
+ const deduplication = policy.deduplication;
322
+ if (typeof deduplication !== "string" || !RPC_COMMAND_DEDUPLICATION.has(deduplication)) {
323
+ failContract("invalid command deduplication");
324
+ }
325
+ const retention = policy.resultRetentionMs;
326
+ if (retention !== null && !isPositiveSafeInteger(retention)) {
327
+ failContract("resultRetentionMs must be a positive integer or null");
328
+ }
329
+ const maxResultBytes = policy.maxResultBytes;
330
+ if (!isPositiveSafeInteger(maxResultBytes))
331
+ failContract("maxResultBytes must be a positive safe integer");
332
+ if (maxResultBytes > maxOutputBytes)
333
+ failContract("maxResultBytes must not exceed maxOutputBytes");
334
+ const quotaBytes = policy.quotaBytes;
335
+ if (!isPositiveSafeInteger(quotaBytes))
336
+ failContract("quotaBytes must be a positive safe integer");
337
+ if (quotaBytes < maxResultBytes)
338
+ failContract("quotaBytes must be able to hold maxResultBytes");
339
+ }
340
+ function checkMethod(definition) {
341
+ assertKnownKeys(definition, METHOD_KEYS, "method");
342
+ const kind = definition.kind;
343
+ if (kind !== "query" && kind !== "command")
344
+ failContract("method kind must be query or command");
345
+ checkSchema(definition.input, 0, new Set);
346
+ checkSchema(definition.output, 0, new Set);
347
+ const purposes = definition.purposes;
348
+ if (!Array.isArray(purposes) || purposes.length === 0)
349
+ failContract("purposes must be a non-empty array");
350
+ const seenPurposes = new Set;
351
+ for (const purpose of purposes) {
352
+ if (typeof purpose !== "string" || !RPC_CALL_PURPOSES.has(purpose))
353
+ failContract("invalid purpose");
354
+ if (seenPurposes.has(purpose))
355
+ failContract("purposes must be unique");
356
+ seenPurposes.add(purpose);
357
+ }
358
+ if (definition.timeoutMs !== undefined) {
359
+ if (!isPositiveSafeInteger(definition.timeoutMs) || definition.timeoutMs > RPC_MAX_TIMEOUT_MS) {
360
+ failContract("timeoutMs must be a positive integer no larger than the timeout bound");
361
+ }
362
+ }
363
+ checkByteLimit(definition.maxInputBytes, "maxInputBytes");
364
+ const maxOutputBytes = checkByteLimit(definition.maxOutputBytes, "maxOutputBytes");
365
+ const command = definition.command;
366
+ if (kind === "command") {
367
+ if (!isPlainObject(command))
368
+ failContract("command methods require an explicit command policy");
369
+ checkCommandPolicy(command, maxOutputBytes ?? RPC_JSON_MAX_BYTES);
370
+ } else if (command !== undefined) {
371
+ failContract("query methods must not declare a command policy");
372
+ }
373
+ }
374
+ function checkContract(contract) {
375
+ if (!isPlainObject(contract))
376
+ failContract("contract must be a plain object");
377
+ assertKnownKeys(contract, CONTRACT_KEYS, "contract");
378
+ if (typeof contract.id !== "string" || !RPC_SERVICE_ID.test(contract.id))
379
+ failContract("invalid service id");
380
+ if (!isPositiveSafeInteger(contract.version))
381
+ failContract("service version must be a positive safe integer");
382
+ const methods = contract.methods;
383
+ if (!isPlainObject(methods))
384
+ failContract("methods must be a plain object");
385
+ const names = Object.keys(methods);
386
+ if (names.length === 0)
387
+ failContract("service must declare at least one method");
388
+ if (names.length > RPC_MAX_METHODS)
389
+ failContract("service declares too many methods");
390
+ for (const name of names) {
391
+ if (!RPC_METHOD_NAME.test(name))
392
+ failContract("invalid method name");
393
+ const definition = methods[name];
394
+ if (!isPlainObject(definition))
395
+ failContract("method definition must be a plain object");
396
+ checkMethod(definition);
397
+ }
398
+ }
399
+ function deepFreeze(value) {
400
+ if (Array.isArray(value)) {
401
+ for (const item of value)
402
+ deepFreeze(item);
403
+ return Object.freeze(value);
404
+ }
405
+ if (isPlainObject(value)) {
406
+ for (const key of Object.keys(value))
407
+ deepFreeze(value[key]);
408
+ return Object.freeze(value);
409
+ }
410
+ return value;
411
+ }
412
+ function defineRpcService(contract) {
413
+ const pure = snapshotPureInput(contract);
414
+ checkContract(pure);
415
+ return deepFreeze(pure);
416
+ }
417
+
418
+ // packages/core/src/plugin-services/rpc-runtime.ts
419
+ var PROCESSES = new Set(["control", "worker", "ingress"]);
420
+ var SCOPES = new Set(["global", "binding"]);
421
+ var PURPOSES = new Set(["bootstrap", "background", "management", "request", "attempt"]);
422
+ var ENDPOINTS = new WeakMap;
423
+ var REVOKED = new WeakMap;
424
+
425
+ // packages/core/src/plugin-services/host-rpc.ts
426
+ var SERVICE_ERROR_MESSAGES = {
427
+ unauthorized: "RPC call is not authorized",
428
+ not_ready: "RPC endpoint is not ready",
429
+ retired: "RPC endpoint is retiring",
430
+ revoked: "RPC endpoint generation was revoked",
431
+ unsupported_method: "RPC method is not published",
432
+ wrong_purpose: "RPC method is not published for this purpose",
433
+ invalid_input: "RPC input is not valid",
434
+ invalid_output: "RPC output is not valid",
435
+ invalid_operation_id: "RPC command operation id is invalid",
436
+ overloaded: "RPC runtime is at capacity",
437
+ cancelled: "RPC call was cancelled",
438
+ timeout: "RPC call deadline exceeded",
439
+ unknown: "RPC command outcome is unknown",
440
+ capability_unavailable: "RPC command execution is unavailable",
441
+ closed: "RPC runtime is closed",
442
+ failed: "RPC call failed",
443
+ conflict: "RPC command identity conflicts with an existing record",
444
+ expired: "RPC command result has expired",
445
+ pending: "RPC command is still pending",
446
+ rejected: "RPC command was rejected",
447
+ missing: "RPC command has no stored result",
448
+ storage_failure: "RPC command storage failed",
449
+ deadlock: "RPC call chain would deadlock",
450
+ call_depth_exceeded: "RPC call depth exceeded",
451
+ undeclared: "RPC service is not declared by the manifest",
452
+ unavailable: "RPC service is unavailable",
453
+ ambiguous_placement: "RPC service placement is ambiguous",
454
+ invalid_registration: "RPC registration is invalid"
455
+ };
456
+ var SERVICE_ERROR_CODES = new Set(Object.keys(SERVICE_ERROR_MESSAGES));
457
+ var EMPTY_OPTIONS = Object.freeze({});
458
+ var FRAME_STORE = new AsyncLocalStorage;
459
+ var CALLEE_INFO = new WeakMap;
460
+ // packages/core/src/plugin-services/peer-channel-protocol.ts
461
+ var CHANNEL_REQUEST_MAX_BYTES = 4 * 1024;
462
+ var CHANNEL_RESULT_MAX_BYTES = 60 * 1024;
463
+ var CHANNEL_MAX_CHUNK_BYTES = 60 * 1024;
464
+ var PLUGIN_CHANNEL_LANES = Object.freeze(["event", "snapshot", "stream"]);
465
+ var ERROR_CODES = new Set([
466
+ "unavailable",
467
+ "ambiguous",
468
+ "unauthorized",
469
+ "invalid",
470
+ "overloaded",
471
+ "expired",
472
+ "conflict",
473
+ "gap",
474
+ "truncated",
475
+ "rejected",
476
+ "cancelled",
477
+ "closed",
478
+ "unknown",
479
+ "failed"
480
+ ]);
481
+ var RPC_CALL_PURPOSES2 = new Set(["bootstrap", "background", "management", "request", "attempt"]);
482
+
483
+ // packages/core/src/plugin-services/peer-channel-hub.ts
484
+ var CHANNEL_MAX_TRANSFER_BUFFER_BYTES = 4 * CHANNEL_MAX_CHUNK_BYTES;
485
+ var CHANNEL_MAX_TOTAL_BUFFER_BYTES = 16 * CHANNEL_MAX_CHUNK_BYTES;
486
+ var CHANNEL_STREAM_MAX_TOTAL_BYTES = 64 * 1024 * 1024;
487
+ var CHANNEL_STREAM_MAX_CREDIT_BYTES = 8 * CHANNEL_MAX_CHUNK_BYTES;
488
+ var CHANNEL_STREAM_DEFAULT_CREDIT_BYTES = 4 * CHANNEL_MAX_CHUNK_BYTES;
489
+ var CHANNEL_SNAPSHOT_MAX_BYTES = 64 * 1024 * 1024;
490
+ var CHANNEL_EVENT_MAX_PAYLOAD_BYTES = 60 * 1024;
491
+ var EMPTY = new Uint8Array(0);
492
+ var HANDLE_REGISTRATIONS = new WeakMap;
493
+
494
+ // packages/core/src/plugin-services/snapshot-store.ts
495
+ var DEFAULT_CHUNK_BYTES = 60 * 1024;
496
+ var SNAPSHOT_FAMILY_REFS = new Map;
497
+
498
+ // packages/core/src/plugin-services.ts
499
+ var CHANNEL_KINDS = new Set(["events", "snapshot", "stream"]);
500
+
501
+ // plugins/chatgpt-oauth/server/rpc.ts
502
+ var command = { deduplication: "none", resultRetentionMs: null, quotaBytes: 65536, maxResultBytes: 65536 };
503
+ var businessRpc = defineRpcService({ id: "chatgpt-oauth.credentials.v1", version: 1, methods: {
504
+ getCredential: { kind: "command", input: { type: "object", properties: {} }, output: { type: "object", properties: { version: { type: "number", integer: true, minimum: 1 }, expiresAt: { type: "number" }, headers: { type: "record", values: { type: "string" }, maxEntries: 16 } } }, purposes: ["attempt"], command, timeoutMs: 15000 },
505
+ rejectAccess: { kind: "command", input: { type: "object", properties: { version: { type: "number", integer: true, minimum: 1 } } }, output: { type: "object", properties: { rejected: { type: "boolean" } } }, purposes: ["attempt"], command, timeoutMs: 15000 }
506
+ } });
507
+
2
508
  // plugins/chatgpt-oauth/server/control.ts
3
- import { randomUUID as randomUUID3 } from "crypto";
509
+ import { randomUUID as randomUUID4 } from "crypto";
4
510
 
5
511
  // plugins/chatgpt-oauth/server/oauth.ts
6
512
  import { createHash, randomBytes } from "crypto";
@@ -519,8 +1025,181 @@ function extractCodexIdentity(idToken) {
519
1025
  };
520
1026
  }
521
1027
 
1028
+ // plugins/chatgpt-oauth/server/siwc.ts
1029
+ import { createPublicKey, randomBytes as randomBytes2, randomUUID, verify } from "crypto";
1030
+ var SIWC_AUTH_URL = "https://auth.openai.com/api/accounts/authorize";
1031
+ var SIWC_TOKEN_URL = "https://auth.openai.com/api/accounts/oauth/token";
1032
+ var SIWC_JWKS_URL = "https://auth.openai.com/.well-known/jwks.json";
1033
+ var SIWC_ISSUER = "https://auth.openai.com";
1034
+ var SIWC_REDIRECT_URI = "http://127.0.0.1:1455/auth/callback";
1035
+ var SIWC_RESOURCE = "https://api.openai.com/v1";
1036
+ var SIWC_SCOPES = ["openid", "profile", "email", "offline_access", "resource.invoke", "chatgpt.tokens.use.direct"];
1037
+ var SIWC_REGISTRATION_KEY = "siwc.registration.v1";
1038
+ var CLIENT_ID = /^oaiapp_[A-Za-z0-9_-]{1,200}$/;
1039
+ var HOST_ID = /^urn:uuid:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
1040
+ function invalid(kind = "invalid_response") {
1041
+ throw new CodexOAuthError(kind, `SIWC ${kind === "invalid_callback" ? "callback" : "response"} is invalid`, { retryable: false });
1042
+ }
1043
+ async function getSiwcHostId(store, canWrite = () => true) {
1044
+ for (let attempt = 0;attempt < 12; attempt++) {
1045
+ if (!canWrite())
1046
+ throw new CodexOAuthError("cancelled", "SIWC operation was cancelled", { retryable: false });
1047
+ const current = await store.get(SIWC_REGISTRATION_KEY);
1048
+ if (current) {
1049
+ try {
1050
+ const record = JSON.parse(current.value);
1051
+ if (record?.schema === 1 && typeof record.hostId === "string" && HOST_ID.test(record.hostId))
1052
+ return record.hostId;
1053
+ } catch {}
1054
+ return invalid();
1055
+ }
1056
+ const hostId = `urn:uuid:${randomUUID()}`;
1057
+ if (!canWrite())
1058
+ throw new CodexOAuthError("cancelled", "SIWC operation was cancelled", { retryable: false });
1059
+ try {
1060
+ await store.compareAndSet(SIWC_REGISTRATION_KEY, null, JSON.stringify({ schema: 1, hostId }));
1061
+ return hostId;
1062
+ } catch (error) {
1063
+ if (error.code !== "version_conflict")
1064
+ throw error;
1065
+ }
1066
+ }
1067
+ return invalid();
1068
+ }
1069
+ function validSiwcMetadata(value) {
1070
+ if (!value || typeof value !== "object" || Array.isArray(value))
1071
+ return false;
1072
+ const item = value;
1073
+ return Object.keys(item).every((key) => ["clientId", "subject", "scopes"].includes(key)) && typeof item.clientId === "string" && CLIENT_ID.test(item.clientId) && typeof item.subject === "string" && item.subject.length > 0 && item.subject.length <= 512 && item.subject.trim() === item.subject && !/[\u0000-\u001f\u007f]/.test(item.subject) && Array.isArray(item.scopes) && item.scopes.length <= 128 && item.scopes.every((scope) => typeof scope === "string" && /^[\x21\x23-\x5b\x5d-\x7e]{1,128}$/.test(scope)) && item.scopes.includes("resource.invoke") && item.scopes.includes("chatgpt.tokens.use.direct");
1074
+ }
1075
+ function createSiwcNonce(random = randomBytes2) {
1076
+ return Buffer.from(random(32)).toString("base64url");
1077
+ }
1078
+ function buildSiwcAuthorizationUrl(pkce, nonce, hostId, clientId) {
1079
+ if (!HOST_ID.test(hostId) || clientId !== undefined && !CLIENT_ID.test(clientId) || !nonce || !pkce.state || !pkce.codeChallenge)
1080
+ return invalid();
1081
+ const url = new URL(SIWC_AUTH_URL);
1082
+ url.search = new URLSearchParams({
1083
+ client_id: clientId ?? "dynamic_agent_client",
1084
+ agent_name_hint: "Bungee",
1085
+ ext_agent_host_id: hostId,
1086
+ response_type: "code",
1087
+ redirect_uri: SIWC_REDIRECT_URI,
1088
+ resource: SIWC_RESOURCE,
1089
+ scope: SIWC_SCOPES.join(" "),
1090
+ state: pkce.state,
1091
+ nonce,
1092
+ code_challenge: pkce.codeChallenge,
1093
+ code_challenge_method: "S256"
1094
+ }).toString();
1095
+ return url.toString();
1096
+ }
1097
+ function parseSiwcCallbackUrl(raw, options) {
1098
+ let url;
1099
+ try {
1100
+ url = new URL(raw);
1101
+ } catch {
1102
+ return invalid("invalid_callback");
1103
+ }
1104
+ const expected = new URL(SIWC_REDIRECT_URI);
1105
+ const now = options.now ?? Date.now();
1106
+ if (raw.length > 4096 || raw.trim() !== raw || raw.split("?")[0] !== SIWC_REDIRECT_URI || url.origin !== expected.origin || url.pathname !== expected.pathname || url.username || url.password || url.hash || !Number.isFinite(now) || !Number.isFinite(options.issuedAt) || options.issuedAt < 0 || now < options.issuedAt || now - options.issuedAt > CODEX_CALLBACK_TTL_MS || [...url.searchParams.keys()].some((key) => url.searchParams.getAll(key).length !== 1) || url.searchParams.has("userinfo") || !options.expectedState || url.searchParams.get("state") !== options.expectedState || url.searchParams.has("error") || url.searchParams.has("error_description"))
1107
+ return invalid("invalid_callback");
1108
+ const code = url.searchParams.get("code");
1109
+ const clientId = url.searchParams.get("client_id");
1110
+ if (!code || code.trim() !== code || /[\u0000-\u001f\u007f]/.test(code) || !clientId || !CLIENT_ID.test(clientId) || options.expectedClientId !== undefined && clientId !== options.expectedClientId)
1111
+ return invalid("invalid_callback");
1112
+ return { code, clientId };
1113
+ }
1114
+ function jwtObject(part) {
1115
+ try {
1116
+ if (!/^[A-Za-z0-9_-]+$/.test(part))
1117
+ return invalid();
1118
+ const value = JSON.parse(Buffer.from(part, "base64url").toString("utf8"));
1119
+ if (value && typeof value === "object" && !Array.isArray(value))
1120
+ return value;
1121
+ } catch {}
1122
+ return invalid();
1123
+ }
1124
+ async function verifySiwcIdToken(idToken, expected, options = {}) {
1125
+ if (!CLIENT_ID.test(expected.clientId) || idToken.length > 32768)
1126
+ return invalid();
1127
+ const parts = idToken.split(".");
1128
+ if (parts.length !== 3 || !/^[A-Za-z0-9_-]+$/.test(parts[2]))
1129
+ return invalid();
1130
+ const header = jwtObject(parts[0]);
1131
+ const payload = jwtObject(parts[1]);
1132
+ if (header.alg !== "RS256" || typeof header.kid !== "string" || !header.kid || header.crit !== undefined)
1133
+ return invalid();
1134
+ const jwks = await requestJson(SIWC_JWKS_URL, { method: "GET", headers: { accept: "application/json" } }, options);
1135
+ if (!Array.isArray(jwks.keys) || jwks.keys.length > 128)
1136
+ return invalid();
1137
+ const keys = jwks.keys.filter((key) => key && typeof key === "object" && key.kid === header.kid && key.kty === "RSA" && (key.alg === undefined || key.alg === "RS256") && (key.use === undefined || key.use === "sig") && (key.key_ops === undefined || Array.isArray(key.key_ops) && key.key_ops.includes("verify")) && key.d === undefined);
1138
+ if (keys.length !== 1)
1139
+ return invalid();
1140
+ try {
1141
+ const publicKey = createPublicKey({ key: keys[0], format: "jwk" });
1142
+ if (!verify("RSA-SHA256", Buffer.from(`${parts[0]}.${parts[1]}`), publicKey, Buffer.from(parts[2], "base64url")))
1143
+ return invalid();
1144
+ } catch {
1145
+ return invalid();
1146
+ }
1147
+ const aud = payload.aud;
1148
+ const now = expected.now ?? Date.now();
1149
+ if (payload.iss !== SIWC_ISSUER || !(aud === expected.clientId || Array.isArray(aud) && aud.every((item) => typeof item === "string") && aud.includes(expected.clientId)) || Array.isArray(aud) && aud.length > 1 && payload.azp !== expected.clientId || payload.azp !== undefined && payload.azp !== expected.clientId || typeof payload.exp !== "number" || !Number.isFinite(payload.exp) || !Number.isFinite(now) || payload.exp * 1000 <= now || payload.nbf !== undefined && (typeof payload.nbf !== "number" || !Number.isFinite(payload.nbf) || payload.nbf * 1000 > now) || typeof payload.sub !== "string" || !payload.sub || payload.sub.length > 512 || payload.sub.trim() !== payload.sub || /[\u0000-\u001f\u007f]/.test(payload.sub) || expected.nonce !== undefined && payload.nonce !== expected.nonce || expected.subject !== undefined && payload.sub !== expected.subject)
1150
+ return invalid();
1151
+ return { subject: payload.sub, email: typeof payload.email === "string" && payload.email.length <= 512 && !/[\u0000-\u001f\u007f]/.test(payload.email) ? payload.email : undefined };
1152
+ }
1153
+ function tokenString(record, key, fallback) {
1154
+ if (record[key] === undefined && fallback !== undefined)
1155
+ return fallback;
1156
+ const value = record[key];
1157
+ if (typeof value !== "string" || !value || value.trim() !== value || value.length > 32768 || /[\r\n\0]/.test(value))
1158
+ return invalid();
1159
+ return value;
1160
+ }
1161
+ async function siwcTokenSet(record, expected, options, previous) {
1162
+ const accessToken = tokenString(record, "access_token");
1163
+ const refreshToken = tokenString(record, "refresh_token", previous?.refreshToken);
1164
+ const idToken = tokenString(record, "id_token", previous?.idToken);
1165
+ if (record.token_type !== undefined && (typeof record.token_type !== "string" || record.token_type.toLowerCase() !== "bearer"))
1166
+ return invalid();
1167
+ if (typeof record.expires_in !== "number" || !Number.isFinite(record.expires_in) || record.expires_in <= 0 || record.expires_in * 1000 > Number.MAX_SAFE_INTEGER - Date.now())
1168
+ return invalid();
1169
+ const scopes = record.scope === undefined && previous?.siwc ? [...previous.siwc.scopes] : tokenString(record, "scope").split(/\s+/);
1170
+ const verified = record.id_token === undefined && previous?.siwc ? { subject: previous.siwc.subject, email: previous.identity?.email } : await verifySiwcIdToken(idToken, expected, options);
1171
+ const siwc = { clientId: expected.clientId, subject: verified.subject, scopes };
1172
+ if (!validSiwcMetadata(siwc))
1173
+ return invalid();
1174
+ return {
1175
+ accessToken,
1176
+ refreshToken,
1177
+ idToken,
1178
+ expiresIn: record.expires_in,
1179
+ expiresAt: Date.now() + record.expires_in * 1000,
1180
+ tokenType: "Bearer",
1181
+ identity: { email: verified.email },
1182
+ identityStatus: "parsed",
1183
+ siwc
1184
+ };
1185
+ }
1186
+ async function exchangeSiwcCode(code, codeVerifier, clientId, nonce, options = {}) {
1187
+ if (!code || !codeVerifier || !nonce || !CLIENT_ID.test(clientId))
1188
+ return invalid();
1189
+ const body = new URLSearchParams({ grant_type: "authorization_code", client_id: clientId, code, code_verifier: codeVerifier, redirect_uri: SIWC_REDIRECT_URI, resource: SIWC_RESOURCE });
1190
+ const record = await requestJson(SIWC_TOKEN_URL, { method: "POST", headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" }, body: body.toString() }, options);
1191
+ return siwcTokenSet(record, { clientId, nonce }, options);
1192
+ }
1193
+ async function refreshSiwcToken(previous, options = {}) {
1194
+ if (!validSiwcMetadata(previous.siwc) || !previous.refreshToken)
1195
+ return invalid();
1196
+ const body = new URLSearchParams({ grant_type: "refresh_token", client_id: previous.siwc.clientId, refresh_token: previous.refreshToken, resource: SIWC_RESOURCE });
1197
+ const record = await requestJson(SIWC_TOKEN_URL, { method: "POST", headers: { accept: "application/json", "content-type": "application/x-www-form-urlencoded" }, body: body.toString() }, options, true);
1198
+ return siwcTokenSet(record, { clientId: previous.siwc.clientId, subject: previous.siwc.subject }, options, previous);
1199
+ }
1200
+
522
1201
  // plugins/chatgpt-oauth/server/accounts.ts
523
- import { randomUUID } from "crypto";
1202
+ import { randomUUID as randomUUID2 } from "crypto";
524
1203
  var ACCOUNT_STORE_KEY = "accounts.v1";
525
1204
  var ACCOUNT_SCHEMA_VERSION = 1;
526
1205
 
@@ -553,6 +1232,7 @@ function copyIdentity(value) {
553
1232
  function copyAccount(value) {
554
1233
  return {
555
1234
  ...value,
1235
+ siwc: value.siwc && { ...value.siwc, scopes: [...value.siwc.scopes] },
556
1236
  identity: copyIdentity(value.identity),
557
1237
  refreshLock: value.refreshLock && { ...value.refreshLock },
558
1238
  autoResetAttempts: value.autoResetAttempts?.map((item) => ({ ...item }))
@@ -565,7 +1245,7 @@ function validStoredAccount(value) {
565
1245
  if (typeof value !== "object" || value === null || Array.isArray(value))
566
1246
  return false;
567
1247
  const account = value;
568
- return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && (account.autoResetCredits === undefined || typeof account.autoResetCredits === "boolean") && validAutoResetAttempts(account.autoResetAttempts) && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
1248
+ return typeof account.id === "string" && account.id.length > 0 && account.id.length <= 128 && typeof account.label === "string" && account.label.length > 0 && account.label.length <= MAX_TEXT && ["active", "disabled", "reauth_required", "revoked"].includes(account.status) && Number.isSafeInteger(account.generation) && account.generation > 0 && (account.loginFence === undefined || Number.isSafeInteger(account.loginFence) && account.loginFence > 0) && (account.credentialValid === undefined || typeof account.credentialValid === "boolean") && (account.autoResetCredits === undefined || typeof account.autoResetCredits === "boolean") && (account.siwc === undefined || validSiwcMetadata(account.siwc)) && validAutoResetAttempts(account.autoResetAttempts) && Number.isFinite(account.createdAt) && Number.isFinite(account.updatedAt);
569
1249
  }
570
1250
  function decode(value) {
571
1251
  if (value === undefined)
@@ -601,6 +1281,7 @@ function statusReason(account) {
601
1281
  function accountListItem(account) {
602
1282
  const pending = account.autoResetAttempts?.find((item) => !item.completed);
603
1283
  return {
1284
+ authType: account.siwc ? "siwc" : "codex",
604
1285
  id: account.id,
605
1286
  label: account.label,
606
1287
  status: account.status,
@@ -608,10 +1289,25 @@ function accountListItem(account) {
608
1289
  reason: statusReason(account),
609
1290
  expiresAt: account.expiresAt,
610
1291
  identity: copyIdentity(account.identity),
611
- autoResetCredits: account.autoResetCredits === true,
612
- pendingAutoReset: pending && { ...pending }
1292
+ autoResetCredits: !account.siwc && account.autoResetCredits === true,
1293
+ pendingAutoReset: !account.siwc && pending ? { ...pending } : undefined
613
1294
  };
614
1295
  }
1296
+ function validateTokenIdentity(token) {
1297
+ if (token.identityStatus !== "parsed" || (token.siwc === undefined ? !token.identity?.accountId : !validSiwcMetadata(token.siwc))) {
1298
+ throw new AccountControlError("invalid_identity");
1299
+ }
1300
+ }
1301
+ function validateSiwcBinding(account, token) {
1302
+ if (Boolean(account.siwc) !== Boolean(token.siwc))
1303
+ throw new AccountControlError("identity_mismatch");
1304
+ if (account.siwc) {
1305
+ if (!validSiwcMetadata(token.siwc) || token.identityStatus !== "parsed")
1306
+ throw new AccountControlError("invalid_identity");
1307
+ if (account.siwc.clientId !== token.siwc.clientId || account.siwc.subject !== token.siwc.subject)
1308
+ throw new AccountControlError("identity_mismatch");
1309
+ }
1310
+ }
615
1311
 
616
1312
  class AccountStore {
617
1313
  secretStore;
@@ -646,6 +1342,19 @@ class AccountStore {
646
1342
  async list() {
647
1343
  return (await this.read()).aggregate.accounts.map(accountListItem);
648
1344
  }
1345
+ async remove(id) {
1346
+ text(id, 128);
1347
+ await this.mutate((aggregate) => {
1348
+ aggregate.accounts = aggregate.accounts.filter((account) => account.id !== id);
1349
+ });
1350
+ }
1351
+ async purgeRevoked() {
1352
+ if (!(await this.read()).aggregate.accounts.some((account) => account.status === "revoked"))
1353
+ return;
1354
+ await this.mutate((aggregate) => {
1355
+ aggregate.accounts = aggregate.accounts.filter((account) => account.status !== "revoked");
1356
+ });
1357
+ }
649
1358
  async get(id) {
650
1359
  const account = (await this.read()).aggregate.accounts.find((item) => item.id === id);
651
1360
  if (!account)
@@ -654,14 +1363,13 @@ class AccountStore {
654
1363
  }
655
1364
  async create(label, token, canCommit = () => true) {
656
1365
  text(label);
657
- if (token.identityStatus !== "parsed" || !token.identity?.accountId)
658
- throw new AccountControlError("invalid_identity");
1366
+ validateTokenIdentity(token);
659
1367
  return this.mutate((aggregate) => {
660
1368
  if (aggregate.accounts.length >= MAX_ACCOUNTS)
661
1369
  throw new AccountControlError("invalid_input");
662
1370
  const now = Date.now();
663
1371
  const account = {
664
- id: randomUUID(),
1372
+ id: randomUUID2(),
665
1373
  label,
666
1374
  status: token.expiresAt === undefined ? "reauth_required" : "active",
667
1375
  generation: 1,
@@ -673,7 +1381,8 @@ class AccountStore {
673
1381
  refreshToken: text(token.refreshToken, MAX_TOKEN),
674
1382
  idToken: optionalText(token.idToken, MAX_TOKEN),
675
1383
  expiresAt: token.expiresAt,
676
- identity: copyIdentity(token.identity)
1384
+ identity: copyIdentity(token.identity),
1385
+ siwc: token.siwc && { ...token.siwc, scopes: [...token.siwc.scopes] }
677
1386
  };
678
1387
  aggregate.accounts.push(account);
679
1388
  return copyAccount(account);
@@ -697,7 +1406,9 @@ class AccountStore {
697
1406
  if (token.identity?.accountId && account.identity?.accountId && token.identity.accountId !== account.identity.accountId) {
698
1407
  throw new AccountControlError("identity_mismatch");
699
1408
  }
1409
+ validateSiwcBinding(account, token);
700
1410
  const now = Date.now();
1411
+ account.siwc = token.siwc && { ...token.siwc, scopes: [...token.siwc.scopes] };
701
1412
  account.accessToken = text(token.accessToken, MAX_TOKEN);
702
1413
  account.refreshToken = text(token.refreshToken, MAX_TOKEN);
703
1414
  if (token.idToken !== undefined && token.identityStatus !== "invalid")
@@ -739,9 +1450,9 @@ class AccountStore {
739
1450
  if (account.generation !== expected.generation || account.loginFence !== expected.loginFence) {
740
1451
  throw new AccountControlError("stale_refresh");
741
1452
  }
742
- if (token.identityStatus !== "parsed" || !token.identity?.accountId)
743
- throw new AccountControlError("invalid_identity");
744
- if (account.identity?.accountId && token.identity.accountId !== account.identity.accountId) {
1453
+ validateTokenIdentity(token);
1454
+ validateSiwcBinding(account, token);
1455
+ if (account.identity?.accountId && token.identity?.accountId !== account.identity.accountId) {
745
1456
  throw new AccountControlError("identity_mismatch");
746
1457
  }
747
1458
  account.accessToken = text(token.accessToken, MAX_TOKEN);
@@ -758,6 +1469,7 @@ class AccountStore {
758
1469
  account.status = "active";
759
1470
  }
760
1471
  account.identity = copyIdentity(token.identity);
1472
+ account.siwc = token.siwc && { ...token.siwc, scopes: [...token.siwc.scopes] };
761
1473
  account.generation += 1;
762
1474
  account.updatedAt = Date.now();
763
1475
  delete account.refreshLock;
@@ -769,22 +1481,12 @@ class AccountStore {
769
1481
  const account = aggregate.accounts.find((item) => item.id === id);
770
1482
  if (!account)
771
1483
  throw new AccountControlError("not_found");
772
- if (account.status === "revoked") {
773
- if (status === "revoked")
774
- return copyAccount(account);
1484
+ if (account.status === "revoked")
775
1485
  throw new AccountControlError("revoked");
776
- }
777
1486
  account.status = status;
778
1487
  account.generation += 1;
779
1488
  account.updatedAt = Date.now();
780
1489
  delete account.refreshLock;
781
- if (status === "revoked") {
782
- delete account.accessToken;
783
- delete account.refreshToken;
784
- delete account.idToken;
785
- delete account.expiresAt;
786
- account.credentialValid = false;
787
- }
788
1490
  return copyAccount(account);
789
1491
  });
790
1492
  }
@@ -810,6 +1512,8 @@ class AccountStore {
810
1512
  throw new AccountControlError("not_found");
811
1513
  if (account.status === "revoked")
812
1514
  throw new AccountControlError("revoked");
1515
+ if (account.siwc)
1516
+ throw new AccountControlError("unsupported_operation");
813
1517
  account.autoResetCredits = enabled;
814
1518
  account.updatedAt = Date.now();
815
1519
  return copyAccount(account);
@@ -818,7 +1522,7 @@ class AccountStore {
818
1522
  async claimAutoReset(id, credit, now) {
819
1523
  return this.mutate((aggregate) => {
820
1524
  const account = aggregate.accounts.find((item) => item.id === id);
821
- if (!account || !accountListItem(account).available || account.autoResetCredits !== true)
1525
+ if (!account || account.siwc || !accountListItem(account).available || account.autoResetCredits !== true)
822
1526
  return;
823
1527
  const attempts = account.autoResetAttempts ?? [];
824
1528
  if (attempts.some((item) => !item.completed || item.creditId === credit.id))
@@ -826,7 +1530,7 @@ class AccountStore {
826
1530
  account.autoResetAttempts = attempts.filter((item) => !item.completed || item.expiresAt === undefined || item.expiresAt > now);
827
1531
  if (account.autoResetAttempts.length >= MAX_AUTO_RESET_ATTEMPTS || credit.expiresAt === undefined || credit.expiresAt <= now)
828
1532
  return;
829
- const attempt = { creditId: credit.id, redeemRequestId: randomUUID(), expiresAt: credit.expiresAt, completed: false };
1533
+ const attempt = { creditId: credit.id, redeemRequestId: randomUUID2(), expiresAt: credit.expiresAt, completed: false };
830
1534
  account.autoResetAttempts.push(attempt);
831
1535
  account.updatedAt = Date.now();
832
1536
  return { ...attempt };
@@ -837,6 +1541,8 @@ class AccountStore {
837
1541
  const account = aggregate.accounts.find((item) => item.id === id);
838
1542
  if (!account)
839
1543
  throw new AccountControlError("not_found");
1544
+ if (account.siwc)
1545
+ throw new AccountControlError("unsupported_operation");
840
1546
  const attempts = account.autoResetAttempts ?? [];
841
1547
  const pending = attempts.find((item) => !item.completed);
842
1548
  if (pending) {
@@ -945,7 +1651,7 @@ class AccountStore {
945
1651
  }
946
1652
 
947
1653
  // plugins/chatgpt-oauth/server/sessions.ts
948
- import { randomUUID as randomUUID2 } from "crypto";
1654
+ import { randomUUID as randomUUID3 } from "crypto";
949
1655
  class LoginSessionError extends Error {
950
1656
  code;
951
1657
  name = "LoginSessionError";
@@ -988,7 +1694,7 @@ class LoginSessionManager {
988
1694
  this.hostSignal.addEventListener("abort", onHostAbort, { once: true });
989
1695
  const issuedAt = this.now();
990
1696
  const session = {
991
- id: randomUUID2(),
1697
+ id: randomUUID3(),
992
1698
  kind,
993
1699
  state: "pending",
994
1700
  issuedAt,
@@ -1076,6 +1782,19 @@ class LoginSessionManager {
1076
1782
  session.codeVerifier = pkce.codeVerifier;
1077
1783
  return { sessionId: session.id, authorizationUrl: buildCodexAuthorizationUrl(pkce), expiresAt: session.expiresAt };
1078
1784
  }
1785
+ startSiwc(hostId, accountRef, clientId, fence) {
1786
+ if (accountRef !== undefined && (typeof accountRef !== "string" || !accountRef || accountRef.length > 128))
1787
+ throw new LoginSessionError("invalid_input");
1788
+ const pkce = createPKCE(this.deps.random);
1789
+ const nonce = createSiwcNonce(this.deps.random);
1790
+ const authorizationUrl = buildSiwcAuthorizationUrl(pkce, nonce, hostId, clientId);
1791
+ const session = this.newSession("siwc", accountRef, fence, CODEX_CALLBACK_TTL_MS);
1792
+ session.stateValue = pkce.state;
1793
+ session.codeVerifier = pkce.codeVerifier;
1794
+ session.nonce = nonce;
1795
+ session.clientId = clientId;
1796
+ return { sessionId: session.id, authorizationUrl, expiresAt: session.expiresAt };
1797
+ }
1079
1798
  setFence(id, fence) {
1080
1799
  const session = this.get(id);
1081
1800
  if (session.state !== "pending")
@@ -1188,6 +1907,24 @@ class LoginSessionManager {
1188
1907
  throw error;
1189
1908
  }
1190
1909
  }
1910
+ async completeSiwc(id, callbackUrl, commit) {
1911
+ const session = this.claim(id, "siwc");
1912
+ try {
1913
+ const callback = parseSiwcCallbackUrl(callbackUrl, { expectedState: session.stateValue, expectedClientId: session.clientId, issuedAt: session.issuedAt, now: this.now() });
1914
+ const token = await exchangeSiwcCode(callback.code, session.codeVerifier, callback.clientId, session.nonce, safeOptions(this.deps, session.controller.signal));
1915
+ return await this.finish(session, token, commit);
1916
+ } catch (error) {
1917
+ if (session.controller.signal.aborted && session.state === "cancelled")
1918
+ throw new LoginSessionError("cancelled");
1919
+ if (session.state === "exchanging") {
1920
+ session.state = session.controller.signal.aborted ? "cancelled" : "failed";
1921
+ session.errorCode = session.controller.signal.aborted ? "cancelled" : "login_failed";
1922
+ if (session.state === "failed")
1923
+ throw new LoginSessionError("failed");
1924
+ }
1925
+ throw error;
1926
+ }
1927
+ }
1191
1928
  cancel(id) {
1192
1929
  const session = this.get(id);
1193
1930
  if (session.state === "success" || session.state === "cancelled" || session.state === "failed" || session.state === "committing")
@@ -1956,6 +2693,7 @@ class AutoResetScheduler {
1956
2693
 
1957
2694
  // plugins/chatgpt-oauth/server/control.ts
1958
2695
  var CHATGPT_TARGET = "https://chatgpt.com";
2696
+ var SIWC_TARGET = "https://api.openai.com";
1959
2697
  var CONTROL_HOST_CONTRACT_GAP = "ControlRpcContext.binding.bindingOptions.accountRef is required and host-resolved; RPC payload is never used as a fallback.";
1960
2698
 
1961
2699
  class ControlError extends Error {
@@ -1982,7 +2720,8 @@ function safeError(error) {
1982
2720
  identity_mismatch: "identity_mismatch",
1983
2721
  invalid_identity: "invalid_identity",
1984
2722
  disposed: "disposed",
1985
- stale_refresh: "stale_refresh"
2723
+ stale_refresh: "stale_refresh",
2724
+ unsupported_operation: "unsupported_operation"
1986
2725
  };
1987
2726
  return new ControlError(map[error.code] ?? "refresh_failed");
1988
2727
  }
@@ -2147,6 +2886,11 @@ function lease(account) {
2147
2886
  throw new ControlError(account.status);
2148
2887
  if (!account.accessToken)
2149
2888
  throw new ControlError("reauth_required");
2889
+ if (account.siwc) {
2890
+ if (!validSiwcMetadata(account.siwc) || !Number.isFinite(account.expiresAt))
2891
+ throw new ControlError("reauth_required");
2892
+ return { version: account.generation, expiresAt: account.expiresAt, headers: { Authorization: `Bearer ${account.accessToken}` } };
2893
+ }
2150
2894
  if (!account.identity?.accountId)
2151
2895
  throw new ControlError("identity_missing");
2152
2896
  if (!Number.isFinite(account.expiresAt))
@@ -2165,7 +2909,7 @@ class ChatgptControl {
2165
2909
  accounts;
2166
2910
  sessions;
2167
2911
  now;
2168
- workerOwner = `chatgpt-control-${randomUUID3()}`;
2912
+ workerOwner = `chatgpt-control-${randomUUID4()}`;
2169
2913
  inFlight = new Map;
2170
2914
  refreshControllers = new Map;
2171
2915
  apiLifetime = new AbortController;
@@ -2184,7 +2928,7 @@ class ChatgptControl {
2184
2928
  now: this.now,
2185
2929
  timeoutMs: deps.timeoutMs,
2186
2930
  hostSignal: this.apiLifetime.signal,
2187
- credential: (id, signal) => this.credential(id, signal),
2931
+ credential: (id, signal) => this.credential(id, signal, "codex"),
2188
2932
  rejectAccess: (id, version) => this.accounts.rejectAccess(id, version)
2189
2933
  });
2190
2934
  this.autoReset = new AutoResetScheduler(this.accounts, this.usage, this.apiLifetime.signal, this.now, deps.autoResetTimer);
@@ -2197,6 +2941,12 @@ class ChatgptControl {
2197
2941
  host.signal.addEventListener("abort", this.abortListener, { once: true });
2198
2942
  this.api = this.buildApi();
2199
2943
  this.rpc = this.buildRpc();
2944
+ host.services?.rpc?.publish(businessRpc, Object.fromEntries(this.rpc.map((declaration) => [declaration.name, (payload, context) => {
2945
+ const frame = host.resolveRpcCallee?.(context.callee);
2946
+ if (!frame?.binding || !frame.attempt || frame.binding.plugin !== "chatgpt-oauth")
2947
+ throw new ControlError("binding_options_unavailable");
2948
+ return declaration.invoke(payload, { ...host, binding: frame.binding, attempt: { ...frame.attempt, signal: AbortSignal.any([frame.attempt.signal, context.signal]) } });
2949
+ }])));
2200
2950
  }
2201
2951
  assertAlive(signal) {
2202
2952
  if (this.disposed || this.host.signal.aborted)
@@ -2206,7 +2956,7 @@ class ChatgptControl {
2206
2956
  }
2207
2957
  async start() {
2208
2958
  this.assertAlive();
2209
- await this.accounts.read();
2959
+ await this.accounts.purgeRevoked();
2210
2960
  this.assertAlive();
2211
2961
  this.autoReset.start();
2212
2962
  }
@@ -2229,7 +2979,8 @@ class ChatgptControl {
2229
2979
  }
2230
2980
  const generation = lock.account.generation;
2231
2981
  try {
2232
- const token = await refreshCodexToken(lock.account.refreshToken, { fetchImpl: this.deps.fetchImpl, signal });
2982
+ const refreshOptions = { fetchImpl: this.deps.fetchImpl, signal, timeoutMs: this.deps.timeoutMs };
2983
+ const token = lock.account.siwc ? await refreshSiwcToken({ accessToken: lock.account.accessToken, refreshToken: lock.account.refreshToken, idToken: lock.account.idToken, identity: lock.account.identity, identityStatus: "parsed", siwc: lock.account.siwc }, refreshOptions) : await refreshCodexToken(lock.account.refreshToken, refreshOptions);
2233
2984
  this.assertAlive(signal);
2234
2985
  const updated = await this.accounts.replaceCredentials(id, token, { owner: this.workerOwner, generation });
2235
2986
  this.assertAlive(signal);
@@ -2238,12 +2989,14 @@ class ChatgptControl {
2238
2989
  if (this.disposed || this.host.signal.aborted)
2239
2990
  throw new ControlError("disposed");
2240
2991
  const oauthCode = error.kind;
2241
- if (oauthCode === "refresh_token_reused") {
2992
+ if (oauthCode === "refresh_token_reused" || lock.account.siwc && (error.upstreamCode === "invalid_grant" || oauthCode === "invalid_response" || error instanceof AccountControlError && ["identity_mismatch", "invalid_identity"].includes(error.code))) {
2242
2993
  const fenced = await this.accounts.failRefreshReauth(id, this.workerOwner, generation).catch(() => false);
2243
2994
  if (!fenced) {
2244
2995
  const latest = await this.accounts.get(id).catch(() => {
2245
2996
  return;
2246
2997
  });
2998
+ if (!latest)
2999
+ throw new ControlError("not_found");
2247
3000
  if (latest?.status === "revoked")
2248
3001
  throw new ControlError("revoked");
2249
3002
  if (latest?.status === "disabled")
@@ -2260,6 +3013,8 @@ class ChatgptControl {
2260
3013
  const latest = await this.accounts.get(id).catch(() => {
2261
3014
  return;
2262
3015
  });
3016
+ if (!latest)
3017
+ throw new ControlError("not_found");
2263
3018
  if (latest?.status === "revoked")
2264
3019
  throw new ControlError("revoked");
2265
3020
  if (latest?.status === "disabled")
@@ -2274,10 +3029,38 @@ class ChatgptControl {
2274
3029
  }
2275
3030
  throw new ControlError("refresh_in_progress");
2276
3031
  }
2277
- async credential(id, signal) {
3032
+ async requireKind(id, kind, error = "source_mismatch") {
3033
+ const account = await this.accounts.get(id);
3034
+ if (Boolean(account.siwc) !== (kind === "siwc"))
3035
+ throw new ControlError(error);
3036
+ return account;
3037
+ }
3038
+ async bindingAccount(context) {
3039
+ const id = accountRefFromBinding(context);
3040
+ const source = context.binding.contributionId;
3041
+ if (source === "chatgpt-siwc")
3042
+ await this.requireKind(id, "siwc");
3043
+ else if (source === "chatgpt" || source === "upstream")
3044
+ await this.requireKind(id, "codex");
3045
+ else
3046
+ throw new ControlError("source_mismatch");
3047
+ return id;
3048
+ }
3049
+ async draft(context, kind) {
3050
+ const body = await this.readJson(context);
3051
+ exactKeys(body, ["accountRef"]);
3052
+ const account = await this.requireKind(requiredText(body.accountRef, 128), kind);
3053
+ if (!accountListItem(account).available)
3054
+ throw new ControlError(account.status === "active" ? "reauth_required" : account.status);
3055
+ const draft = { target: kind === "siwc" ? SIWC_TARGET : CHATGPT_TARGET, bindingOptions: { accountRef: account.id } };
3056
+ return jsonResponse(draft);
3057
+ }
3058
+ async credential(id, signal, kind) {
2278
3059
  this.assertAlive(signal);
2279
3060
  const account = await this.accounts.get(id);
2280
3061
  this.assertAlive(signal);
3062
+ if (kind === "codex" && account.siwc)
3063
+ throw new ControlError("unsupported_operation");
2281
3064
  if (account.status !== "active")
2282
3065
  throw new ControlError(account.status);
2283
3066
  if (!isRefreshable(account, this.now())) {
@@ -2318,6 +3101,8 @@ class ChatgptControl {
2318
3101
  return readRequestJson(context.request, context.requestSignal, context.signal, this.apiLifetime.signal);
2319
3102
  }
2320
3103
  async startDevice(accountRef) {
3104
+ if (accountRef !== undefined)
3105
+ await this.requireKind(accountRef, "codex");
2321
3106
  const started = await this.sessions.startDevice(accountRef, () => this.loginFence(accountRef));
2322
3107
  this.sessions.completeDevice(started.sessionId, (token, info, guard) => this.commitLogin(token, info, guard)).catch(() => {
2323
3108
  return;
@@ -2342,11 +3127,17 @@ class ChatgptControl {
2342
3127
  };
2343
3128
  return [
2344
3129
  { path: "/accounts", methods: ["GET"], handler: "listAccounts", invoke: invoke(async () => jsonResponse({ accounts: await this.accounts.list() })) },
2345
- { path: "/accounts/usage", methods: ["GET"], handler: "getAccountUsage", invoke: invoke(async (context) => jsonResponse(await this.usage.get(this.accountRefQuery(context.request), context.requestSignal))) },
3130
+ { path: "/accounts/codex", methods: ["GET"], handler: "listCodexAccounts", invoke: invoke(async () => jsonResponse({ accounts: (await this.accounts.list()).filter((account) => account.authType === "codex") })) },
3131
+ { path: "/accounts/siwc", methods: ["GET"], handler: "listSiwcAccounts", invoke: invoke(async () => jsonResponse({ accounts: (await this.accounts.list()).filter((account) => account.authType === "siwc") })) },
3132
+ { path: "/accounts/usage", methods: ["GET"], handler: "getAccountUsage", invoke: invoke(async (context) => {
3133
+ const id = this.accountRefQuery(context.request);
3134
+ return jsonResponse(await this.usage.get(id, context.requestSignal));
3135
+ }) },
2346
3136
  { path: "/accounts/usage/reset", methods: ["POST"], handler: "resetAccountUsage", invoke: invoke(async (context) => {
2347
3137
  const body = await this.readJson(context);
2348
3138
  exactKeys(body, ["accountRef", "redeemRequestId", "creditId"]);
2349
3139
  const accountRef = requiredText(body.accountRef, 128);
3140
+ await this.requireKind(accountRef, "codex", "unsupported_operation");
2350
3141
  const redeemRequestId = requiredText(body.redeemRequestId, 128);
2351
3142
  if (!/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(redeemRequestId))
2352
3143
  throw new ControlError("invalid_input");
@@ -2373,14 +3164,18 @@ class ChatgptControl {
2373
3164
  throw new ControlError("invalid_input");
2374
3165
  return jsonResponse({ account: accountListItem(await this.accounts.setAutoResetCredits(requiredText(body.accountRef, 128), body.enabled)) });
2375
3166
  }) },
2376
- { path: "/accounts/draft", methods: ["POST"], handler: "createDraft", invoke: invoke(async (context) => {
3167
+ { path: "/accounts/draft", methods: ["POST"], handler: "createDraft", invoke: invoke((context) => this.draft(context, "codex")) },
3168
+ { path: "/accounts/siwc/draft", methods: ["POST"], handler: "createSiwcDraft", invoke: invoke((context) => this.draft(context, "siwc")) },
3169
+ { path: "/login/siwc", methods: ["POST"], handler: "startSiwcLogin", invoke: invoke(async (context) => {
2377
3170
  const body = await this.readJson(context);
2378
3171
  exactKeys(body, ["accountRef"]);
2379
- const account = await this.accounts.get(requiredText(body.accountRef, 128));
2380
- if (!accountListItem(account).available)
2381
- throw new ControlError(account.status === "active" ? "reauth_required" : account.status);
2382
- const draft = { target: CHATGPT_TARGET, bindingOptions: { accountRef: account.id } };
2383
- return jsonResponse(draft);
3172
+ const accountRef = optionalText3(body.accountRef, 128);
3173
+ const account = accountRef === undefined ? undefined : await this.requireKind(accountRef, "siwc");
3174
+ const hostId = await getSiwcHostId(this.host.secretStore, () => !this.disposed && !this.host.signal.aborted);
3175
+ this.assertAlive(context.requestSignal);
3176
+ const fence = await this.loginFence(accountRef);
3177
+ this.assertAlive(context.requestSignal);
3178
+ return jsonResponse(this.sessions.startSiwc(hostId, accountRef, account?.siwc?.clientId, fence));
2384
3179
  }) },
2385
3180
  { path: "/login/device", methods: ["POST"], handler: "startDeviceLogin", invoke: invoke(async (context) => {
2386
3181
  const body = await this.readJson(context);
@@ -2391,6 +3186,8 @@ class ChatgptControl {
2391
3186
  const body = await this.readJson(context);
2392
3187
  exactKeys(body, ["accountRef"]);
2393
3188
  const accountRef = optionalText3(body.accountRef, 128);
3189
+ if (accountRef !== undefined)
3190
+ await this.requireKind(accountRef, "codex");
2394
3191
  const started = this.sessions.startPkce(accountRef);
2395
3192
  try {
2396
3193
  if (accountRef !== undefined)
@@ -2415,7 +3212,8 @@ class ChatgptControl {
2415
3212
  const status = this.sessions.status(sessionId);
2416
3213
  if (status.state !== "pending")
2417
3214
  return jsonResponse(status);
2418
- const result = await this.sessions.completePkce(sessionId, requiredText(body.callbackUrl, 4096), (token, info, guard) => this.commitLogin(token, info, guard));
3215
+ const complete = status.kind === "siwc" ? this.sessions.completeSiwc.bind(this.sessions) : this.sessions.completePkce.bind(this.sessions);
3216
+ const result = await complete(sessionId, requiredText(body.callbackUrl, 4096), (token, info, guard) => this.commitLogin(token, info, guard));
2419
3217
  return jsonResponse({ account: result.result });
2420
3218
  }) },
2421
3219
  { path: "/login/device/complete", methods: ["POST"], handler: "completeDeviceLogin", invoke: invoke(async (context) => {
@@ -2457,9 +3255,9 @@ class ChatgptControl {
2457
3255
  const body = await this.readJson(context);
2458
3256
  exactKeys(body, ["accountRef"]);
2459
3257
  const accountRef = requiredText(body.accountRef, 128);
2460
- const account = await this.accounts.setStatus(accountRef, "revoked");
3258
+ await this.accounts.remove(accountRef);
2461
3259
  this.usage.invalidateAccount(accountRef);
2462
- return jsonResponse({ account: accountListItem(account) });
3260
+ return jsonResponse({ deleted: true });
2463
3261
  }) }
2464
3262
  ];
2465
3263
  }
@@ -2468,7 +3266,7 @@ class ChatgptControl {
2468
3266
  { name: "getCredential", handler: "getCredential", invoke: async (_payload, context) => {
2469
3267
  try {
2470
3268
  this.assertAlive(context.attempt.signal);
2471
- return await this.credential(accountRefFromBinding(context), context.attempt.signal);
3269
+ return await this.credential(await this.bindingAccount(context), context.attempt.signal);
2472
3270
  } catch (error) {
2473
3271
  throw safeError(error);
2474
3272
  }
@@ -2478,7 +3276,7 @@ class ChatgptControl {
2478
3276
  this.assertAlive(context.attempt.signal);
2479
3277
  const body = record(payload);
2480
3278
  exactKeys(body, ["version"]);
2481
- return { rejected: await this.accounts.rejectAccess(accountRefFromBinding(context), requiredVersion(body.version)) };
3279
+ return { rejected: await this.accounts.rejectAccess(await this.bindingAccount(context), requiredVersion(body.version)) };
2482
3280
  } catch (error) {
2483
3281
  throw safeError(error);
2484
3282
  }
@@ -2504,10 +3302,14 @@ function createControl(context, dependencies = {}) {
2504
3302
  }
2505
3303
  var api = Object.freeze([
2506
3304
  { path: "/accounts", methods: ["GET"], handler: "listAccounts" },
3305
+ { path: "/accounts/codex", methods: ["GET"], handler: "listCodexAccounts" },
3306
+ { path: "/accounts/siwc", methods: ["GET"], handler: "listSiwcAccounts" },
2507
3307
  { path: "/accounts/usage", methods: ["GET"], handler: "getAccountUsage" },
2508
3308
  { path: "/accounts/usage/reset", methods: ["POST"], handler: "resetAccountUsage" },
2509
3309
  { path: "/accounts/auto-reset", methods: ["POST"], handler: "setAutoResetCredits" },
2510
3310
  { path: "/accounts/draft", methods: ["POST"], handler: "createDraft" },
3311
+ { path: "/accounts/siwc/draft", methods: ["POST"], handler: "createSiwcDraft" },
3312
+ { path: "/login/siwc", methods: ["POST"], handler: "startSiwcLogin" },
2511
3313
  { path: "/login/device", methods: ["POST"], handler: "startDeviceLogin" },
2512
3314
  { path: "/login/pkce", methods: ["POST"], handler: "startPkceLogin" },
2513
3315
  { path: "/login/status", methods: ["GET"], handler: "getLoginStatus" },
@@ -2527,6 +3329,7 @@ export {
2527
3329
  CHATGPT_TARGET,
2528
3330
  CONTROL_HOST_CONTRACT_GAP,
2529
3331
  ControlError,
3332
+ SIWC_TARGET,
2530
3333
  api,
2531
3334
  controlApi,
2532
3335
  controlRpc,
@@ -2535,4 +3338,4 @@ export {
2535
3338
  rpc
2536
3339
  };
2537
3340
 
2538
- //# debugId=8E7B0629542A407F64756E2164756E21
3341
+ //# debugId=08B71A7DC584C3AB64756E2164756E21