dsh-codex-subscription 2.2.1 → 2.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +151 -150
- package/README.md +150 -150
- package/lib/client.js +2183 -1595
- package/lib/index.js +564 -60
- package/locale/en.json +6 -0
- package/locale/zh-CN.json +6 -0
- package/package.json +4 -2
package/lib/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import { createHash, randomBytes, randomUUID } from "node:crypto";
|
|
3
|
-
import { closeOpenAICodexWebSocketSessions, resetOpenAICodexWebSocketDebugStats } from "@earendil-works/pi-ai/api/openai-codex-responses";
|
|
3
|
+
import { closeOpenAICodexWebSocketSessions, getOpenAICodexWebSocketDebugStats, resetOpenAICodexWebSocketDebugStats } from "@earendil-works/pi-ai/api/openai-codex-responses";
|
|
4
4
|
import { execFile, spawn } from "node:child_process";
|
|
5
5
|
import { request } from "node:https";
|
|
6
6
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -369,6 +369,54 @@ const PREFERENCE_FIELDS = Object.freeze({
|
|
|
369
369
|
}
|
|
370
370
|
});
|
|
371
371
|
//#endregion
|
|
372
|
+
//#region src/diagnostic-events.js
|
|
373
|
+
const areas = /* @__PURE__ */ new Set([
|
|
374
|
+
"login",
|
|
375
|
+
"model",
|
|
376
|
+
"catalog",
|
|
377
|
+
"quota",
|
|
378
|
+
"quota-reset",
|
|
379
|
+
"search",
|
|
380
|
+
"image"
|
|
381
|
+
]);
|
|
382
|
+
const codes$1 = /* @__PURE__ */ new Set([
|
|
383
|
+
"timeout",
|
|
384
|
+
"dns",
|
|
385
|
+
"tls",
|
|
386
|
+
"connection",
|
|
387
|
+
"network",
|
|
388
|
+
"http-error"
|
|
389
|
+
]);
|
|
390
|
+
/** Process-local bounded evidence. No bodies, URLs, exception strings or identities. */
|
|
391
|
+
function createDiagnosticEvents({ limit = 32, now = Date.now } = {}) {
|
|
392
|
+
const events = [];
|
|
393
|
+
const capacity = Math.max(1, Math.min(128, Number.isInteger(limit) ? limit : 32));
|
|
394
|
+
let dropped = 0, sequence = 0;
|
|
395
|
+
return {
|
|
396
|
+
record(area, value) {
|
|
397
|
+
if (!areas.has(area) || !["ok", "failed"].includes(value?.status)) return;
|
|
398
|
+
events.push({
|
|
399
|
+
sequence: ++sequence,
|
|
400
|
+
area,
|
|
401
|
+
status: value.status,
|
|
402
|
+
observedAt: now(),
|
|
403
|
+
...codes$1.has(value.code) ? { code: value.code } : {},
|
|
404
|
+
...Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599 ? { httpStatus: value.httpStatus } : {}
|
|
405
|
+
});
|
|
406
|
+
if (events.length > capacity) {
|
|
407
|
+
events.shift();
|
|
408
|
+
dropped++;
|
|
409
|
+
}
|
|
410
|
+
},
|
|
411
|
+
snapshot: () => ({
|
|
412
|
+
scope: "plugin-process",
|
|
413
|
+
capacity,
|
|
414
|
+
dropped,
|
|
415
|
+
events: events.map((value) => ({ ...value }))
|
|
416
|
+
})
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
//#endregion
|
|
372
420
|
//#region src/oauth-network.js
|
|
373
421
|
const execFileAsync = promisify(execFile);
|
|
374
422
|
const CODEX_AUTH_HOST = "auth.openai.com";
|
|
@@ -612,6 +660,11 @@ const elapsedBucket = (elapsed) => elapsed < 1e3 ? "under-1s" : elapsed < 5e3 ?
|
|
|
612
660
|
function createCodexNetworkTransport(options = {}) {
|
|
613
661
|
const attempts = /* @__PURE__ */ new Map();
|
|
614
662
|
const now = options.now ?? Date.now;
|
|
663
|
+
const evidence = createDiagnosticEvents({ now });
|
|
664
|
+
const remember = (area, value) => {
|
|
665
|
+
attempts.set(area, value);
|
|
666
|
+
evidence.record(area, value);
|
|
667
|
+
};
|
|
615
668
|
const run = async (area, operation, connection = {}) => {
|
|
616
669
|
const startedAt = now();
|
|
617
670
|
let route = attempts.get(area)?.route ?? "direct";
|
|
@@ -625,7 +678,7 @@ function createCodexNetworkTransport(options = {}) {
|
|
|
625
678
|
routed = true;
|
|
626
679
|
}
|
|
627
680
|
});
|
|
628
|
-
if (value instanceof Response && !value.ok)
|
|
681
|
+
if (value instanceof Response && !value.ok) remember(area, {
|
|
629
682
|
status: "failed",
|
|
630
683
|
stage: "http",
|
|
631
684
|
code: "http-error",
|
|
@@ -633,14 +686,14 @@ function createCodexNetworkTransport(options = {}) {
|
|
|
633
686
|
route,
|
|
634
687
|
elapsed: elapsedBucket(now() - startedAt)
|
|
635
688
|
});
|
|
636
|
-
else if (routed || value instanceof Response)
|
|
689
|
+
else if (routed || value instanceof Response) remember(area, {
|
|
637
690
|
status: "ok",
|
|
638
691
|
route,
|
|
639
692
|
elapsed: elapsedBucket(now() - startedAt)
|
|
640
693
|
});
|
|
641
694
|
return value;
|
|
642
695
|
} catch (error) {
|
|
643
|
-
if (routed)
|
|
696
|
+
if (routed) remember(area, {
|
|
644
697
|
status: "failed",
|
|
645
698
|
stage: "transport",
|
|
646
699
|
code: classifyTransportError(error),
|
|
@@ -652,6 +705,7 @@ function createCodexNetworkTransport(options = {}) {
|
|
|
652
705
|
};
|
|
653
706
|
return Object.freeze({
|
|
654
707
|
run,
|
|
708
|
+
history: evidence.snapshot,
|
|
655
709
|
fetch: (area, input, init) => run(area, () => globalThis.fetch(input, init)),
|
|
656
710
|
snapshot: () => Object.fromEntries([...attempts].map(([area, value]) => [area, { ...value }]))
|
|
657
711
|
});
|
|
@@ -662,6 +716,21 @@ function createSubscriptionConnection({ resolveMode = () => "sse", resolveProxy
|
|
|
662
716
|
const namespace = randomUUID();
|
|
663
717
|
const sessions = /* @__PURE__ */ new Set();
|
|
664
718
|
return {
|
|
719
|
+
snapshot() {
|
|
720
|
+
const totals = Object.fromEntries([
|
|
721
|
+
"requests",
|
|
722
|
+
"connectionsCreated",
|
|
723
|
+
"connectionsReused",
|
|
724
|
+
"deltaRequests",
|
|
725
|
+
"websocketFailures",
|
|
726
|
+
"sseFallbacks"
|
|
727
|
+
].map((key) => [key, 0]));
|
|
728
|
+
for (const session of sessions) {
|
|
729
|
+
const stats = getOpenAICodexWebSocketDebugStats(session);
|
|
730
|
+
for (const key of Object.keys(totals)) if (Number.isSafeInteger(stats?.[key]) && stats[key] >= 0) totals[key] = Math.min(Number.MAX_SAFE_INTEGER, totals[key] + stats[key]);
|
|
731
|
+
}
|
|
732
|
+
return totals;
|
|
733
|
+
},
|
|
665
734
|
async prepare(options = {}) {
|
|
666
735
|
if (resolveMode() !== "websocket") return { options: {
|
|
667
736
|
...options,
|
|
@@ -716,6 +785,11 @@ const MAX_BYTES = 2 * 1024 * 1024;
|
|
|
716
785
|
const MAX_AGE_MS = 10080 * 60 * 1e3;
|
|
717
786
|
function createCompactionBridge({ enabled = () => false, threshold = () => 1e5, accountScope, diagnostic = () => {}, now = Date.now }) {
|
|
718
787
|
const scope = new AsyncLocalStorage();
|
|
788
|
+
const stats = {
|
|
789
|
+
requests: 0,
|
|
790
|
+
checkpointsSaved: 0,
|
|
791
|
+
checkpointsReused: 0
|
|
792
|
+
};
|
|
719
793
|
const wrapStream = (factory, options) => (async function* () {
|
|
720
794
|
if (!enabled() || options.provider !== "openai-codex") {
|
|
721
795
|
yield* factory(options);
|
|
@@ -775,6 +849,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
775
849
|
}
|
|
776
850
|
}
|
|
777
851
|
};
|
|
852
|
+
stats.checkpointsSaved = Math.min(Number.MAX_SAFE_INTEGER, stats.checkpointsSaved + 1);
|
|
778
853
|
}
|
|
779
854
|
yield event;
|
|
780
855
|
}
|
|
@@ -783,6 +858,7 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
783
858
|
}
|
|
784
859
|
})();
|
|
785
860
|
return {
|
|
861
|
+
snapshot: () => ({ ...stats }),
|
|
786
862
|
wrapAdapter(adapter) {
|
|
787
863
|
return new Proxy(adapter, { get(target, property) {
|
|
788
864
|
if (property === "stream") return (options) => wrapStream((o) => target.stream(o), options);
|
|
@@ -800,6 +876,11 @@ function createCompactionBridge({ enabled = () => false, threshold = () => 1e5,
|
|
|
800
876
|
preparePayload(payload, contextWindow) {
|
|
801
877
|
const state = scope.getStore();
|
|
802
878
|
if (!state) return payload;
|
|
879
|
+
if (!state.counted) {
|
|
880
|
+
state.counted = true;
|
|
881
|
+
stats.requests = Math.min(Number.MAX_SAFE_INTEGER, stats.requests + 1);
|
|
882
|
+
if (state.suffix) stats.checkpointsReused = Math.min(Number.MAX_SAFE_INTEGER, stats.checkpointsReused + 1);
|
|
883
|
+
}
|
|
803
884
|
const configured = threshold();
|
|
804
885
|
if (!Number.isSafeInteger(configured) || configured < 1e3) throw new Error("Invalid compaction threshold");
|
|
805
886
|
const limit = Number.isFinite(contextWindow) && contextWindow >= 2e3 ? Math.min(configured, Math.floor(contextWindow / 2)) : configured;
|
|
@@ -902,6 +983,8 @@ const RPC_ENDPOINTS = Object.freeze([
|
|
|
902
983
|
"runtime/install",
|
|
903
984
|
"runtime/remove",
|
|
904
985
|
"runtime/cancel",
|
|
986
|
+
"storage/status",
|
|
987
|
+
"storage/clear-forecast",
|
|
905
988
|
"reset-credit/inspect",
|
|
906
989
|
"reset-credit/prepare",
|
|
907
990
|
"reset-credit/consume",
|
|
@@ -1287,6 +1370,21 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1287
1370
|
backgroundMode: "one-shot",
|
|
1288
1371
|
maxDepth: "provider-managed"
|
|
1289
1372
|
});
|
|
1373
|
+
const ownedFields = [
|
|
1374
|
+
"provider",
|
|
1375
|
+
"modelSelectionSettings",
|
|
1376
|
+
"backgroundMode",
|
|
1377
|
+
"maxDepth"
|
|
1378
|
+
];
|
|
1379
|
+
const restore = (current, original) => {
|
|
1380
|
+
const result = { ...current }, applied = convert(original);
|
|
1381
|
+
for (const key of ownedFields) {
|
|
1382
|
+
if (current[key] !== applied[key]) continue;
|
|
1383
|
+
if (Object.hasOwn(original, key)) result[key] = original[key];
|
|
1384
|
+
else delete result[key];
|
|
1385
|
+
}
|
|
1386
|
+
return result;
|
|
1387
|
+
};
|
|
1290
1388
|
const configure = (fiber, config) => {
|
|
1291
1389
|
if (!standard(fiber.entry, config)) return config;
|
|
1292
1390
|
if (!originals.has(fiber)) originals.set(fiber, { ...config });
|
|
@@ -1316,7 +1414,8 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1316
1414
|
fiber,
|
|
1317
1415
|
before
|
|
1318
1416
|
});
|
|
1319
|
-
|
|
1417
|
+
if (mode === "codex" && before.provider !== "codex-subscription-subagent") originals.set(fiber, { ...before });
|
|
1418
|
+
await fiber.update(mode === "codex" ? convert(before) : restore(before, original), true);
|
|
1320
1419
|
}
|
|
1321
1420
|
await persist?.(mode);
|
|
1322
1421
|
} catch (error) {
|
|
@@ -1335,7 +1434,7 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1335
1434
|
await tail.catch(() => {});
|
|
1336
1435
|
disposed = true;
|
|
1337
1436
|
selected = "dsh";
|
|
1338
|
-
for (const [fiber, original] of originals) if (fiber.config?.provider === "codex-subscription-subagent") await fiber.update(original, true);
|
|
1437
|
+
for (const [fiber, original] of originals) if (fiber.config?.provider === "codex-subscription-subagent") await fiber.update(restore(fiber.config, original), true);
|
|
1339
1438
|
originals.clear();
|
|
1340
1439
|
}
|
|
1341
1440
|
};
|
|
@@ -2831,8 +2930,28 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
|
|
|
2831
2930
|
}
|
|
2832
2931
|
Object.freeze(["0.82.1", "0.85.1"]);
|
|
2833
2932
|
//#endregion
|
|
2933
|
+
//#region src/subscription-credentials.js
|
|
2934
|
+
async function readSubscriptionCredentials(getAuth, readCredential, signal) {
|
|
2935
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
2936
|
+
const auth = await getAuth({ signal });
|
|
2937
|
+
signal?.throwIfAborted();
|
|
2938
|
+
if (typeof auth?.auth?.apiKey !== "string" || !auth.auth.apiKey) break;
|
|
2939
|
+
const credential = await readCredential({ signal });
|
|
2940
|
+
signal?.throwIfAborted();
|
|
2941
|
+
if (credential?.type !== "oauth") break;
|
|
2942
|
+
if (credential.access === auth.auth.apiKey) return {
|
|
2943
|
+
access: credential.access,
|
|
2944
|
+
accountId: credential.accountId
|
|
2945
|
+
};
|
|
2946
|
+
}
|
|
2947
|
+
return {
|
|
2948
|
+
access: void 0,
|
|
2949
|
+
accountId: void 0
|
|
2950
|
+
};
|
|
2951
|
+
}
|
|
2952
|
+
//#endregion
|
|
2834
2953
|
//#region src/version.js
|
|
2835
|
-
const PACKAGE_VERSION = "2.2.
|
|
2954
|
+
const PACKAGE_VERSION = "2.2.3";
|
|
2836
2955
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
2837
2956
|
//#endregion
|
|
2838
2957
|
//#region src/model-catalog.js
|
|
@@ -2957,12 +3076,8 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
2957
3076
|
let timer;
|
|
2958
3077
|
const timeoutError = /* @__PURE__ */ new Error("Codex model catalog refresh timed out");
|
|
2959
3078
|
const work = (async () => {
|
|
2960
|
-
const
|
|
3079
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, requestSignal);
|
|
2961
3080
|
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
2962
|
-
const credential = await options.readCredential({ signal: requestSignal });
|
|
2963
|
-
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
2964
|
-
const access = auth?.auth?.apiKey;
|
|
2965
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
2966
3081
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) return false;
|
|
2967
3082
|
const headers = {
|
|
2968
3083
|
authorization: `Bearer ${access}`,
|
|
@@ -2996,6 +3111,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
2996
3111
|
etag = nonEmpty$2(response.headers.get("etag")) ?? etag;
|
|
2997
3112
|
revision += 1;
|
|
2998
3113
|
outcome = "ok";
|
|
3114
|
+
options.onUpdated?.();
|
|
2999
3115
|
return true;
|
|
3000
3116
|
})();
|
|
3001
3117
|
let rejectAborted;
|
|
@@ -3048,6 +3164,7 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
3048
3164
|
etag = void 0;
|
|
3049
3165
|
refreshStatus = "idle";
|
|
3050
3166
|
revision += 1;
|
|
3167
|
+
options.onUpdated?.();
|
|
3051
3168
|
}
|
|
3052
3169
|
});
|
|
3053
3170
|
}
|
|
@@ -3117,10 +3234,7 @@ function createCodexSearchProvider(options) {
|
|
|
3117
3234
|
signal?.throwIfAborted();
|
|
3118
3235
|
const preferences = readCapabilitySettings(options.resolvePreferences?.());
|
|
3119
3236
|
if (preferences.searchMode === "disabled") throw new WebError("Codex search is disabled in subscription settings", "WEB_PROVIDER_UNAVAILABLE");
|
|
3120
|
-
const
|
|
3121
|
-
const credential = await options.readCredential({ signal });
|
|
3122
|
-
const access = auth?.auth?.apiKey;
|
|
3123
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
3237
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, signal);
|
|
3124
3238
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new WebError("ChatGPT subscription is not signed in", "WEB_PROVIDER_CREDENTIAL_MISSING");
|
|
3125
3239
|
const model = nonEmpty$1(options.resolveModel?.()) ?? DEFAULT_MODEL;
|
|
3126
3240
|
const id = nonEmpty$1(options.resolveSessionId?.()) ?? randomUUID();
|
|
@@ -3631,10 +3745,7 @@ function createCodexImageTool(options) {
|
|
|
3631
3745
|
const prompt = nonEmpty(args.prompt);
|
|
3632
3746
|
if (prompt === void 0) throw new Error("prompt must be a non-empty string");
|
|
3633
3747
|
const imageOptions = normalizeImageOptions(args);
|
|
3634
|
-
const
|
|
3635
|
-
const credential = await options.readCredential({ signal: exec.signal });
|
|
3636
|
-
const access = auth?.auth?.apiKey;
|
|
3637
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
3748
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, exec.signal);
|
|
3638
3749
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
3639
3750
|
if (!attachments.imageLimits.mediaTypes.includes("image/png")) throw new Error("This DSH installation does not accept PNG image attachments");
|
|
3640
3751
|
const maximumBytes = Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
|
|
@@ -3700,7 +3811,7 @@ function createCodexImageTool(options) {
|
|
|
3700
3811
|
localPath: options.originalImages.originalPath(original.assetId),
|
|
3701
3812
|
...metadata.background === void 0 ? {} : { background: metadata.background },
|
|
3702
3813
|
...metadata.quality === void 0 ? {} : { quality: metadata.quality },
|
|
3703
|
-
|
|
3814
|
+
size: `${original.width}x${original.height}`
|
|
3704
3815
|
};
|
|
3705
3816
|
if (exec.parent !== void 0) exec.deferContext(createUserMessage({
|
|
3706
3817
|
content: imageContent(result),
|
|
@@ -3876,6 +3987,229 @@ var OriginalImageStore = class {
|
|
|
3876
3987
|
}
|
|
3877
3988
|
};
|
|
3878
3989
|
//#endregion
|
|
3990
|
+
//#region src/diagnostic-checks.js
|
|
3991
|
+
const states = /* @__PURE__ */ new Set([
|
|
3992
|
+
"pass",
|
|
3993
|
+
"fail",
|
|
3994
|
+
"warn",
|
|
3995
|
+
"unknown",
|
|
3996
|
+
"not-applicable"
|
|
3997
|
+
]);
|
|
3998
|
+
/** Adapters return bounded reason codes, never exceptions or raw service data. */
|
|
3999
|
+
async function collectChecks(definitions, { timeoutMs = 1500, now = Date.now, source = "server", scope = "plugin-process" } = {}) {
|
|
4000
|
+
return Promise.all(definitions.map(async ({ id, capability, run }) => {
|
|
4001
|
+
const startedAt = now();
|
|
4002
|
+
let timer;
|
|
4003
|
+
const controller = new AbortController();
|
|
4004
|
+
const base = {
|
|
4005
|
+
id,
|
|
4006
|
+
capability,
|
|
4007
|
+
source,
|
|
4008
|
+
scope
|
|
4009
|
+
};
|
|
4010
|
+
try {
|
|
4011
|
+
if (!run) return {
|
|
4012
|
+
...base,
|
|
4013
|
+
status: "unknown",
|
|
4014
|
+
reason: "not-instrumented",
|
|
4015
|
+
observedAt: now()
|
|
4016
|
+
};
|
|
4017
|
+
const result = await Promise.race([Promise.resolve().then(() => run(controller.signal)), new Promise((resolve) => {
|
|
4018
|
+
timer = setTimeout(() => {
|
|
4019
|
+
controller.abort();
|
|
4020
|
+
resolve({
|
|
4021
|
+
status: "unknown",
|
|
4022
|
+
reason: "inspection-timeout"
|
|
4023
|
+
});
|
|
4024
|
+
}, timeoutMs);
|
|
4025
|
+
})]);
|
|
4026
|
+
const valid = states.has(result?.status) && /^[a-z][a-z0-9-]{0,63}$/u.test(result?.reason ?? "");
|
|
4027
|
+
return {
|
|
4028
|
+
...base,
|
|
4029
|
+
status: valid ? result.status : "unknown",
|
|
4030
|
+
reason: valid ? result.reason : "invalid-inspection-result",
|
|
4031
|
+
observedAt: now(),
|
|
4032
|
+
elapsedMs: Math.max(0, now() - startedAt)
|
|
4033
|
+
};
|
|
4034
|
+
} catch {
|
|
4035
|
+
return {
|
|
4036
|
+
...base,
|
|
4037
|
+
status: "unknown",
|
|
4038
|
+
reason: "inspection-failed",
|
|
4039
|
+
observedAt: now(),
|
|
4040
|
+
elapsedMs: Math.max(0, now() - startedAt)
|
|
4041
|
+
};
|
|
4042
|
+
} finally {
|
|
4043
|
+
clearTimeout(timer);
|
|
4044
|
+
}
|
|
4045
|
+
}));
|
|
4046
|
+
}
|
|
4047
|
+
const diagnosticCapabilities = Object.freeze([
|
|
4048
|
+
"account",
|
|
4049
|
+
"accounts",
|
|
4050
|
+
"settings",
|
|
4051
|
+
"models",
|
|
4052
|
+
"quota",
|
|
4053
|
+
"search",
|
|
4054
|
+
"images",
|
|
4055
|
+
"sketch",
|
|
4056
|
+
"subagents",
|
|
4057
|
+
"compaction",
|
|
4058
|
+
"transport",
|
|
4059
|
+
"storage",
|
|
4060
|
+
"host-ui"
|
|
4061
|
+
]);
|
|
4062
|
+
//#endregion
|
|
4063
|
+
//#region src/diagnostic-capabilities.js
|
|
4064
|
+
function capabilityCoverage({ account, preference, runtime, catalog, tools, storage, checks, operations, now = Date.now() }) {
|
|
4065
|
+
const disabled = {
|
|
4066
|
+
images: preference.imageGeneration === false && preference.imageEditing === false,
|
|
4067
|
+
sketch: preference.imageSketch === false || preference.imageEditing === false,
|
|
4068
|
+
search: preference.searchMode === "disabled",
|
|
4069
|
+
compaction: preference.compactionMode === "dsh",
|
|
4070
|
+
subagents: preference.subagentBackend === "dsh"
|
|
4071
|
+
};
|
|
4072
|
+
return diagnosticCapabilities.map((id) => {
|
|
4073
|
+
let readiness = "unknown", reason = "not-instrumented", blockedBy;
|
|
4074
|
+
if (disabled[id]) {
|
|
4075
|
+
readiness = "not-applicable";
|
|
4076
|
+
reason = "disabled-or-host-owned";
|
|
4077
|
+
} else if ([
|
|
4078
|
+
"models",
|
|
4079
|
+
"quota",
|
|
4080
|
+
"search",
|
|
4081
|
+
"images",
|
|
4082
|
+
"compaction"
|
|
4083
|
+
].includes(id) && account.status === "signed-out") {
|
|
4084
|
+
readiness = "warn";
|
|
4085
|
+
reason = "sign-in-required";
|
|
4086
|
+
blockedBy = "account";
|
|
4087
|
+
} else if (id === "account") {
|
|
4088
|
+
readiness = account.status === "signed-in" ? "pass" : "unknown";
|
|
4089
|
+
reason = account.status === "signed-in" ? "local-sign-in-present" : "sign-in-unverified";
|
|
4090
|
+
} else if (id === "settings" && typeof preference.writable === "boolean") {
|
|
4091
|
+
readiness = preference.writable ? "pass" : "warn";
|
|
4092
|
+
reason = preference.writable ? "settings-writable" : "settings-readonly";
|
|
4093
|
+
} else if (id === "subagents" && runtime) {
|
|
4094
|
+
readiness = runtime.installed && !runtime.restartRequired ? "pass" : "warn";
|
|
4095
|
+
reason = runtime.restartRequired ? "restart-required" : runtime.installed ? "runtime-present" : "runtime-missing";
|
|
4096
|
+
} else if (id === "models" && catalog) {
|
|
4097
|
+
readiness = catalog.source === "online" ? "pass" : "warn";
|
|
4098
|
+
reason = catalog.source === "online" ? "catalog-online" : "catalog-fallback";
|
|
4099
|
+
} else if (id === "storage" && storage?.readable) {
|
|
4100
|
+
readiness = "pass";
|
|
4101
|
+
reason = "storage-readable";
|
|
4102
|
+
} else if (tools && (id === "images" || id === "sketch" && preference.imageSketchAgent === true)) {
|
|
4103
|
+
readiness = tools[id] ? "pass" : "warn";
|
|
4104
|
+
reason = tools[id] ? "tool-registered" : "tool-missing";
|
|
4105
|
+
}
|
|
4106
|
+
const recent = (operations?.events ?? []).filter((event) => event.capability === id && now >= event.observedAt && now - event.observedAt <= 15 * 6e4).at(-1);
|
|
4107
|
+
return {
|
|
4108
|
+
id,
|
|
4109
|
+
readiness,
|
|
4110
|
+
reason,
|
|
4111
|
+
...blockedBy ? { blockedBy } : {},
|
|
4112
|
+
checks: checks.filter((check) => check.capability === id).map((check) => check.id),
|
|
4113
|
+
execution: recent ? "observed" : "not-verified",
|
|
4114
|
+
...recent ? { latest: {
|
|
4115
|
+
action: recent.action,
|
|
4116
|
+
source: recent.source,
|
|
4117
|
+
status: recent.status,
|
|
4118
|
+
observedAt: recent.observedAt
|
|
4119
|
+
} } : {}
|
|
4120
|
+
};
|
|
4121
|
+
});
|
|
4122
|
+
}
|
|
4123
|
+
//#endregion
|
|
4124
|
+
//#region src/diagnostic-operations.js
|
|
4125
|
+
const actions = Object.freeze({
|
|
4126
|
+
"login/start": "account",
|
|
4127
|
+
"login/submit": "account",
|
|
4128
|
+
"login/cancel": "account",
|
|
4129
|
+
logout: "account",
|
|
4130
|
+
"account/select": "accounts",
|
|
4131
|
+
"account/remove": "accounts",
|
|
4132
|
+
"preferences/update": "settings",
|
|
4133
|
+
"account/cleanup": "accounts",
|
|
4134
|
+
usage: "quota",
|
|
4135
|
+
"preferences/models": "models",
|
|
4136
|
+
"storage/status": "storage",
|
|
4137
|
+
"storage/clear-forecast": "storage",
|
|
4138
|
+
"runtime/install": "subagents",
|
|
4139
|
+
"runtime/remove": "subagents",
|
|
4140
|
+
"runtime/cancel": "subagents",
|
|
4141
|
+
codex_image_generate: "images",
|
|
4142
|
+
codex_sketch: "sketch"
|
|
4143
|
+
});
|
|
4144
|
+
function safeOperations(raw) {
|
|
4145
|
+
if (!Array.isArray(raw?.events)) return void 0;
|
|
4146
|
+
return {
|
|
4147
|
+
scope: "plugin-process",
|
|
4148
|
+
capacity: 32,
|
|
4149
|
+
dropped: (Number.isSafeInteger(raw.dropped) && raw.dropped >= 0 ? raw.dropped : 0) + Math.max(0, raw.events.length - 32),
|
|
4150
|
+
events: raw.events.slice(-32).flatMap((value) => {
|
|
4151
|
+
if (!Object.hasOwn(actions, value?.action ?? "") || ![
|
|
4152
|
+
"completed",
|
|
4153
|
+
"failed",
|
|
4154
|
+
"cancelled"
|
|
4155
|
+
].includes(value.status) || !["rpc", "tool"].includes(value.source) || !Number.isSafeInteger(value.observedAt) || value.observedAt < 0) return [];
|
|
4156
|
+
return [{
|
|
4157
|
+
capability: actions[value.action],
|
|
4158
|
+
action: value.action,
|
|
4159
|
+
status: value.status,
|
|
4160
|
+
source: value.source,
|
|
4161
|
+
observedAt: value.observedAt
|
|
4162
|
+
}];
|
|
4163
|
+
})
|
|
4164
|
+
};
|
|
4165
|
+
}
|
|
4166
|
+
function createDiagnosticOperations({ now = Date.now } = {}) {
|
|
4167
|
+
const events = [];
|
|
4168
|
+
let sequence = 0, dropped = 0;
|
|
4169
|
+
const record = (action, status, source = "rpc") => {
|
|
4170
|
+
if (!Object.hasOwn(actions, action) || ![
|
|
4171
|
+
"completed",
|
|
4172
|
+
"failed",
|
|
4173
|
+
"cancelled"
|
|
4174
|
+
].includes(status) || !["rpc", "tool"].includes(source)) return;
|
|
4175
|
+
events.push({
|
|
4176
|
+
sequence: ++sequence,
|
|
4177
|
+
capability: actions[action],
|
|
4178
|
+
action,
|
|
4179
|
+
source,
|
|
4180
|
+
status,
|
|
4181
|
+
observedAt: now()
|
|
4182
|
+
});
|
|
4183
|
+
if (events.length > 32) {
|
|
4184
|
+
events.shift();
|
|
4185
|
+
dropped++;
|
|
4186
|
+
}
|
|
4187
|
+
};
|
|
4188
|
+
return {
|
|
4189
|
+
record,
|
|
4190
|
+
wrap: (handler) => async (endpoint, payload, signal) => {
|
|
4191
|
+
try {
|
|
4192
|
+
const result = await handler(endpoint, payload, signal);
|
|
4193
|
+
record(endpoint, result?.ok === true ? "completed" : "failed");
|
|
4194
|
+
return result;
|
|
4195
|
+
} catch (error) {
|
|
4196
|
+
record(endpoint, signal?.aborted ? "cancelled" : "failed");
|
|
4197
|
+
throw error;
|
|
4198
|
+
}
|
|
4199
|
+
},
|
|
4200
|
+
tool: (exec, result) => {
|
|
4201
|
+
if (!["codex_image_generate", "codex_sketch"].includes(exec?.name)) return;
|
|
4202
|
+
record(exec.name, exec.signal?.aborted ? "cancelled" : result?.isError === true ? "failed" : result?.isError === false ? "completed" : "failed", "tool");
|
|
4203
|
+
},
|
|
4204
|
+
snapshot: () => ({
|
|
4205
|
+
scope: "plugin-process",
|
|
4206
|
+
capacity: 32,
|
|
4207
|
+
dropped,
|
|
4208
|
+
events: events.map((value) => ({ ...value }))
|
|
4209
|
+
})
|
|
4210
|
+
};
|
|
4211
|
+
}
|
|
4212
|
+
//#endregion
|
|
3879
4213
|
//#region src/diagnostics.js
|
|
3880
4214
|
const requestAreas = /* @__PURE__ */ new Set([
|
|
3881
4215
|
"login",
|
|
@@ -3908,6 +4242,26 @@ const elapsedBuckets = /* @__PURE__ */ new Set([
|
|
|
3908
4242
|
"5-15s",
|
|
3909
4243
|
"over-15s"
|
|
3910
4244
|
]);
|
|
4245
|
+
function safeHistory(raw) {
|
|
4246
|
+
if (!raw || !Array.isArray(raw.events)) return void 0;
|
|
4247
|
+
const events = raw.events.slice(-32).flatMap((value) => {
|
|
4248
|
+
if (!value || !requestAreas.has(value.area) || !statuses.has(value.status) || !Number.isSafeInteger(value.sequence) || value.sequence < 1 || !Number.isSafeInteger(value.observedAt) || value.observedAt < 0) return [];
|
|
4249
|
+
return [{
|
|
4250
|
+
area: value.area,
|
|
4251
|
+
status: value.status,
|
|
4252
|
+
sequence: value.sequence,
|
|
4253
|
+
observedAt: value.observedAt,
|
|
4254
|
+
...codes.has(value.code) ? { code: value.code } : {},
|
|
4255
|
+
...Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599 ? { httpStatus: value.httpStatus } : {}
|
|
4256
|
+
}];
|
|
4257
|
+
});
|
|
4258
|
+
return {
|
|
4259
|
+
scope: "plugin-process",
|
|
4260
|
+
capacity: 32,
|
|
4261
|
+
dropped: (Number.isSafeInteger(raw.dropped) && raw.dropped >= 0 ? raw.dropped : 0) + Math.max(0, raw.events.length - 32),
|
|
4262
|
+
events
|
|
4263
|
+
};
|
|
4264
|
+
}
|
|
3911
4265
|
function safeRequests(network) {
|
|
3912
4266
|
const raw = network?.snapshot?.() ?? {};
|
|
3913
4267
|
const result = {};
|
|
@@ -3926,17 +4280,113 @@ function safeRequests(network) {
|
|
|
3926
4280
|
return result;
|
|
3927
4281
|
}
|
|
3928
4282
|
/** Build a support report that deliberately excludes OAuth and account metadata. */
|
|
3929
|
-
async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog }) {
|
|
4283
|
+
async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction, inspectionOptions, operations, runtimeManagement, storage, tools }) {
|
|
4284
|
+
const collected = {};
|
|
4285
|
+
const checks = await collectChecks([
|
|
4286
|
+
[
|
|
4287
|
+
"account",
|
|
4288
|
+
"account",
|
|
4289
|
+
() => auth.status()
|
|
4290
|
+
],
|
|
4291
|
+
[
|
|
4292
|
+
"preferences",
|
|
4293
|
+
"settings",
|
|
4294
|
+
() => preferences.status()
|
|
4295
|
+
],
|
|
4296
|
+
[
|
|
4297
|
+
"catalog",
|
|
4298
|
+
"models",
|
|
4299
|
+
() => modelCatalog?.status?.()
|
|
4300
|
+
],
|
|
4301
|
+
[
|
|
4302
|
+
"gaps",
|
|
4303
|
+
"models",
|
|
4304
|
+
() => modelCatalog?.capabilityGaps?.()
|
|
4305
|
+
],
|
|
4306
|
+
[
|
|
4307
|
+
"requests",
|
|
4308
|
+
"transport",
|
|
4309
|
+
() => network?.snapshot ? safeRequests(network) : void 0
|
|
4310
|
+
],
|
|
4311
|
+
[
|
|
4312
|
+
"history",
|
|
4313
|
+
"transport",
|
|
4314
|
+
() => safeHistory(network?.history?.())
|
|
4315
|
+
],
|
|
4316
|
+
[
|
|
4317
|
+
"websocket",
|
|
4318
|
+
"transport",
|
|
4319
|
+
() => connection ? safeCounters(connection, [
|
|
4320
|
+
"requests",
|
|
4321
|
+
"connectionsCreated",
|
|
4322
|
+
"connectionsReused",
|
|
4323
|
+
"deltaRequests",
|
|
4324
|
+
"websocketFailures",
|
|
4325
|
+
"sseFallbacks"
|
|
4326
|
+
]) : void 0
|
|
4327
|
+
],
|
|
4328
|
+
[
|
|
4329
|
+
"compaction",
|
|
4330
|
+
"compaction",
|
|
4331
|
+
() => compaction ? safeCounters(compaction, [
|
|
4332
|
+
"requests",
|
|
4333
|
+
"checkpointsSaved",
|
|
4334
|
+
"checkpointsReused"
|
|
4335
|
+
]) : void 0
|
|
4336
|
+
],
|
|
4337
|
+
[
|
|
4338
|
+
"operations",
|
|
4339
|
+
"transport",
|
|
4340
|
+
() => safeOperations(operations?.snapshot?.())
|
|
4341
|
+
],
|
|
4342
|
+
[
|
|
4343
|
+
"runtime",
|
|
4344
|
+
"subagents",
|
|
4345
|
+
async () => {
|
|
4346
|
+
const value = await runtimeManagement?.status?.();
|
|
4347
|
+
return value ? Object.fromEntries([
|
|
4348
|
+
"available",
|
|
4349
|
+
"installed",
|
|
4350
|
+
"restartRequired"
|
|
4351
|
+
].filter((key) => typeof value[key] === "boolean").map((key) => [key, value[key]])) : void 0;
|
|
4352
|
+
}
|
|
4353
|
+
],
|
|
4354
|
+
[
|
|
4355
|
+
"storage",
|
|
4356
|
+
"storage",
|
|
4357
|
+
async () => typeof storage === "function" ? (await storage(), { readable: true }) : void 0
|
|
4358
|
+
],
|
|
4359
|
+
[
|
|
4360
|
+
"tools",
|
|
4361
|
+
"host-ui",
|
|
4362
|
+
() => typeof tools?.get === "function" ? {
|
|
4363
|
+
images: !!tools.get("codex_image_generate"),
|
|
4364
|
+
sketch: !!tools.get("codex_sketch")
|
|
4365
|
+
} : void 0
|
|
4366
|
+
]
|
|
4367
|
+
].map(([id, capability, read]) => ({
|
|
4368
|
+
id,
|
|
4369
|
+
capability,
|
|
4370
|
+
run: read ? async (signal) => {
|
|
4371
|
+
const value = await read();
|
|
4372
|
+
if (value === void 0) return {
|
|
4373
|
+
status: "unknown",
|
|
4374
|
+
reason: "not-instrumented"
|
|
4375
|
+
};
|
|
4376
|
+
if (!signal.aborted) collected[id] = value;
|
|
4377
|
+
return {
|
|
4378
|
+
status: "pass",
|
|
4379
|
+
reason: "inspection-completed"
|
|
4380
|
+
};
|
|
4381
|
+
} : void 0
|
|
4382
|
+
})), inspectionOptions);
|
|
3930
4383
|
let account = { status: "unknown" };
|
|
3931
4384
|
const issues = [];
|
|
3932
|
-
|
|
3933
|
-
|
|
3934
|
-
|
|
3935
|
-
|
|
3936
|
-
|
|
3937
|
-
const preference = preferences.status();
|
|
3938
|
-
const catalog = modelCatalog?.status?.();
|
|
3939
|
-
const gaps = (modelCatalog?.capabilityGaps?.() ?? []).slice(0, 20).flatMap((value) => {
|
|
4385
|
+
if (collected.account) account = { status: collected.account.authenticated === true ? "signed-in" : "signed-out" };
|
|
4386
|
+
else issues.push({ code: "account-status-unavailable" });
|
|
4387
|
+
const preference = collected.preferences ?? {};
|
|
4388
|
+
const catalog = collected.catalog;
|
|
4389
|
+
const gaps = (Array.isArray(collected.gaps) ? collected.gaps : []).slice(0, 20).flatMap((value) => {
|
|
3940
4390
|
if (!value || typeof value.model !== "string" || !/^[a-z][a-z0-9._-]{0,79}$/u.test(value.model)) return [];
|
|
3941
4391
|
const fields = Object.fromEntries([
|
|
3942
4392
|
"reasoning",
|
|
@@ -3954,6 +4404,22 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3954
4404
|
if (gaps.length) issues.push({ code: "catalog-capabilities-not-adapted" });
|
|
3955
4405
|
return {
|
|
3956
4406
|
schemaVersion: 3,
|
|
4407
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4408
|
+
inspection: {
|
|
4409
|
+
schemaVersion: 1,
|
|
4410
|
+
meaning: "collection-success-is-not-feature-success",
|
|
4411
|
+
checks,
|
|
4412
|
+
capabilities: capabilityCoverage({
|
|
4413
|
+
account,
|
|
4414
|
+
preference,
|
|
4415
|
+
runtime: collected.runtime,
|
|
4416
|
+
catalog,
|
|
4417
|
+
tools: collected.tools,
|
|
4418
|
+
storage: collected.storage,
|
|
4419
|
+
checks,
|
|
4420
|
+
operations: collected.operations
|
|
4421
|
+
})
|
|
4422
|
+
},
|
|
3957
4423
|
package: "dsh-codex-subscription",
|
|
3958
4424
|
version: PACKAGE_VERSION,
|
|
3959
4425
|
runtime: {
|
|
@@ -3963,7 +4429,11 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3963
4429
|
},
|
|
3964
4430
|
account,
|
|
3965
4431
|
login,
|
|
3966
|
-
requests:
|
|
4432
|
+
requests: collected.requests ?? {},
|
|
4433
|
+
...collected.history ? { requestHistory: collected.history } : {},
|
|
4434
|
+
...collected.operations ? { operations: collected.operations } : {},
|
|
4435
|
+
...collected.websocket ? { websocket: collected.websocket } : {},
|
|
4436
|
+
...collected.compaction ? { compaction: collected.compaction } : {},
|
|
3967
4437
|
...catalog && ["fallback", "online"].includes(catalog.source) && [
|
|
3968
4438
|
"idle",
|
|
3969
4439
|
"refreshing",
|
|
@@ -3981,11 +4451,15 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3981
4451
|
...typeof preference.outputVerbosity === "string" ? { outputVerbosity: preference.outputVerbosity } : {},
|
|
3982
4452
|
searchProvider: preference.searchProvider,
|
|
3983
4453
|
speedMode: preference.speedMode,
|
|
3984
|
-
writable: preference.writable
|
|
4454
|
+
...typeof preference.writable === "boolean" ? { writable: preference.writable } : {}
|
|
3985
4455
|
},
|
|
3986
4456
|
issues
|
|
3987
4457
|
};
|
|
3988
4458
|
}
|
|
4459
|
+
function safeCounters(source, keys) {
|
|
4460
|
+
const values = source.snapshot();
|
|
4461
|
+
return Object.fromEntries(keys.filter((key) => Number.isSafeInteger(values?.[key]) && values[key] >= 0).map((key) => [key, values[key]]));
|
|
4462
|
+
}
|
|
3989
4463
|
//#endregion
|
|
3990
4464
|
//#region src/usage.js
|
|
3991
4465
|
const CODEX_USAGE_URL = "https://chatgpt.com/backend-api/wham/usage";
|
|
@@ -4144,10 +4618,7 @@ function createCodexUsageReader(options) {
|
|
|
4144
4618
|
let inFlight;
|
|
4145
4619
|
let generation = 0;
|
|
4146
4620
|
const load = async (signal) => {
|
|
4147
|
-
const
|
|
4148
|
-
const credential = await readCredential({ signal });
|
|
4149
|
-
const access = auth?.auth?.apiKey;
|
|
4150
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
4621
|
+
const { access, accountId } = await readSubscriptionCredentials(getAuth, readCredential, signal);
|
|
4151
4622
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
4152
4623
|
const response = await fetchUsage(CODEX_USAGE_URL, {
|
|
4153
4624
|
method: "GET",
|
|
@@ -4683,6 +5154,7 @@ function createQuotaForecastReader({ reader, enabled, now = Date.now, scope = ()
|
|
|
4683
5154
|
return current === generation ? forecast.usage : usage;
|
|
4684
5155
|
},
|
|
4685
5156
|
clear: () => clearHistory(),
|
|
5157
|
+
storage: () => stateStore.usage(),
|
|
4686
5158
|
clearCache() {
|
|
4687
5159
|
generation += 1;
|
|
4688
5160
|
reader.clear();
|
|
@@ -4768,6 +5240,22 @@ var QuotaForecastStateStore = class {
|
|
|
4768
5240
|
async clear() {
|
|
4769
5241
|
await rm(this.filename, { force: true });
|
|
4770
5242
|
}
|
|
5243
|
+
async usage() {
|
|
5244
|
+
try {
|
|
5245
|
+
const info = await lstat(this.filename);
|
|
5246
|
+
if (!info.isFile() || info.isSymbolicLink()) throw Error("Unexpected forecast cache entry");
|
|
5247
|
+
return {
|
|
5248
|
+
bytes: info.size,
|
|
5249
|
+
limit: this.maxBytes
|
|
5250
|
+
};
|
|
5251
|
+
} catch (error) {
|
|
5252
|
+
if (error.code === "ENOENT") return {
|
|
5253
|
+
bytes: 0,
|
|
5254
|
+
limit: this.maxBytes
|
|
5255
|
+
};
|
|
5256
|
+
throw error;
|
|
5257
|
+
}
|
|
5258
|
+
}
|
|
4771
5259
|
};
|
|
4772
5260
|
//#endregion
|
|
4773
5261
|
//#region src/reset-credits.js
|
|
@@ -4786,9 +5274,7 @@ const requestSignal = (signal, timeoutMs) => {
|
|
|
4786
5274
|
function safeCopy(value) {
|
|
4787
5275
|
return typeof value === "string" && value.length > 0 ? value.slice(0, MAX_COPY_LENGTH) : void 0;
|
|
4788
5276
|
}
|
|
4789
|
-
function credentialsOf(
|
|
4790
|
-
const access = auth?.auth?.apiKey;
|
|
4791
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
5277
|
+
function credentialsOf({ access, accountId }) {
|
|
4792
5278
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
4793
5279
|
return {
|
|
4794
5280
|
access,
|
|
@@ -4873,7 +5359,7 @@ function createCodexResetCreditService(options) {
|
|
|
4873
5359
|
...credit.title === void 0 ? {} : { name: credit.title },
|
|
4874
5360
|
...credit.creditExpiresAt === void 0 ? {} : { expiresAt: credit.creditExpiresAt }
|
|
4875
5361
|
});
|
|
4876
|
-
const resolveCredentials = async (signal) => credentialsOf(await getAuth
|
|
5362
|
+
const resolveCredentials = async (signal) => credentialsOf(await readSubscriptionCredentials(getAuth, readCredential, signal));
|
|
4877
5363
|
const readDetails = async (signal, credentials) => {
|
|
4878
5364
|
const { access, accountId } = credentials ?? await resolveCredentials(signal);
|
|
4879
5365
|
const response = await fetchReset(CODEX_RESET_CREDITS_URL, {
|
|
@@ -5064,8 +5550,19 @@ const publicError = (code, message) => ({
|
|
|
5064
5550
|
details: { issues: [] }
|
|
5065
5551
|
}
|
|
5066
5552
|
});
|
|
5067
|
-
function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged }) {
|
|
5553
|
+
function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged, onCleanupFailure }) {
|
|
5068
5554
|
return async (endpoint, payload, signal) => {
|
|
5555
|
+
if (endpoint === "storage/status" || endpoint === "storage/clear-forecast") try {
|
|
5556
|
+
signal.throwIfAborted();
|
|
5557
|
+
if (endpoint === "storage/clear-forecast") await usageReader.clear();
|
|
5558
|
+
return {
|
|
5559
|
+
ok: true,
|
|
5560
|
+
value: await usageReader.storage()
|
|
5561
|
+
};
|
|
5562
|
+
} catch (error) {
|
|
5563
|
+
if (signal.aborted) throw error;
|
|
5564
|
+
return publicError("operation-error", "Could not manage forecast cache");
|
|
5565
|
+
}
|
|
5069
5566
|
if ([
|
|
5070
5567
|
"runtime/status",
|
|
5071
5568
|
"runtime/install",
|
|
@@ -5211,23 +5708,20 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
5211
5708
|
return publicError("internal", message);
|
|
5212
5709
|
}
|
|
5213
5710
|
const result = await authHandler(endpoint, payload, signal);
|
|
5214
|
-
if (result.ok === true &&
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
|
|
5227
|
-
|
|
5228
|
-
modelCatalog?.clear();
|
|
5229
|
-
onAccountChanged?.();
|
|
5230
|
-
modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
|
|
5711
|
+
if (result.ok === true && (endpoint === "logout" || endpoint === "account/select" || endpoint === "account/remove" || endpoint === "login/status" && result.value?.authenticated === true)) {
|
|
5712
|
+
const tasks = [
|
|
5713
|
+
() => closeConnections?.(),
|
|
5714
|
+
() => usageReader.clearCache(),
|
|
5715
|
+
() => resetCreditService.clear(),
|
|
5716
|
+
() => modelCatalog?.clear(),
|
|
5717
|
+
() => onAccountChanged?.()
|
|
5718
|
+
];
|
|
5719
|
+
if (endpoint === "logout") tasks.push(() => usageReader.clear());
|
|
5720
|
+
if (endpoint === "account/remove" && typeof payload?.id === "string") tasks.push(() => usageReader.clearScope(payload.id));
|
|
5721
|
+
if ((await Promise.allSettled(tasks.map((task) => Promise.resolve().then(task)))).some((value) => value.status === "rejected")) try {
|
|
5722
|
+
onCleanupFailure?.();
|
|
5723
|
+
} catch {}
|
|
5724
|
+
if (endpoint !== "logout") Promise.resolve().then(() => modelCatalog?.refresh({ signal: void 0 })).catch(() => {});
|
|
5231
5725
|
} else if (result.ok === true && (endpoint === "status" || result.value?.authenticated === true)) modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
|
|
5232
5726
|
return result;
|
|
5233
5727
|
};
|
|
@@ -5305,6 +5799,8 @@ function apply(ctx, config = {}) {
|
|
|
5305
5799
|
const settings = createSettingsAdapter(ctx, z.object(settingsFields), config, SETTINGS_NAMESPACE);
|
|
5306
5800
|
const searchProvider = createSearchProviderSwitcher(ctx.loader);
|
|
5307
5801
|
const network = createCodexNetworkTransport();
|
|
5802
|
+
const diagnosticOperations = createDiagnosticOperations();
|
|
5803
|
+
ctx.effect(() => ctx.on?.("tools/result", (exec, result) => diagnosticOperations.tool(exec, result)), "codex-subscription: operation evidence");
|
|
5308
5804
|
const originalImages = new OriginalImageStore();
|
|
5309
5805
|
const accountVault = ACCOUNT_VAULT_KEY !== void 0 && typeof ctx.credentials.readRecord === "function" && typeof ctx.credentials.modifyRecord === "function" && typeof ctx.credentials.deleteRecord === "function" ? new DshOAuthAccountVault(ctx.credentials, {
|
|
5310
5806
|
key: ACCOUNT_VAULT_KEY,
|
|
@@ -5329,6 +5825,7 @@ function apply(ctx, config = {}) {
|
|
|
5329
5825
|
});
|
|
5330
5826
|
ctx.effect(() => ctx.on?.("plugin-manager/install-state", (value) => runtimeManagement.progress(value)));
|
|
5331
5827
|
const modelCatalog = createOfficialModelCatalog({
|
|
5828
|
+
onUpdated: () => ctx.llm.emitAdaptersUpdated?.(),
|
|
5332
5829
|
getAuth: (options) => resolveAuth(options),
|
|
5333
5830
|
readCredential: (options) => store.read(PROVIDER, options),
|
|
5334
5831
|
baseModels: () => baseProvider.getModels(),
|
|
@@ -5635,14 +6132,21 @@ function apply(ctx, config = {}) {
|
|
|
5635
6132
|
preferences,
|
|
5636
6133
|
login: coordinator.supportState(),
|
|
5637
6134
|
network,
|
|
5638
|
-
modelCatalog
|
|
6135
|
+
modelCatalog,
|
|
6136
|
+
connection,
|
|
6137
|
+
compaction,
|
|
6138
|
+
operations: diagnosticOperations,
|
|
6139
|
+
runtimeManagement,
|
|
6140
|
+
storage: () => usageReader.storage(),
|
|
6141
|
+
tools: ctx.tools
|
|
5639
6142
|
}),
|
|
6143
|
+
onCleanupFailure: () => diagnosticOperations.record("account/cleanup", "failed"),
|
|
5640
6144
|
modelCatalog,
|
|
5641
6145
|
closeConnections: () => connection.dispose(),
|
|
5642
6146
|
originalImages,
|
|
5643
6147
|
resolveInheritedOriginal: (sessionId, assetId) => inheritedOriginalImageRef(ctx.get?.("sessions")?.get?.(sessionId), assetId)
|
|
5644
6148
|
});
|
|
5645
|
-
const handler = (endpoint, payload, signal) => endpoint.startsWith("sketch/") ? sketchBridge.rpc(endpoint, payload) : subscriptionHandler(endpoint, payload, signal);
|
|
6149
|
+
const handler = diagnosticOperations.wrap((endpoint, payload, signal) => endpoint.startsWith("sketch/") ? sketchBridge.rpc(endpoint, payload) : subscriptionHandler(endpoint, payload, signal));
|
|
5646
6150
|
ctx.effect(() => {
|
|
5647
6151
|
modelCatalog.refresh().catch((error) => ctx.logger?.debug?.("could not refresh Codex model catalog: %s", error.message));
|
|
5648
6152
|
}, "codex-subscription: official model catalog");
|