dsh-codex-subscription 2.2.2 → 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 +150 -153
- package/README.md +150 -154
- package/lib/client.js +304 -12
- package/lib/index.js +496 -71
- package/locale/en.json +6 -0
- package/locale/zh-CN.json +6 -0
- package/package.json +4 -2
package/lib/index.js
CHANGED
|
@@ -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
|
});
|
|
@@ -1316,6 +1370,21 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1316
1370
|
backgroundMode: "one-shot",
|
|
1317
1371
|
maxDepth: "provider-managed"
|
|
1318
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
|
+
};
|
|
1319
1388
|
const configure = (fiber, config) => {
|
|
1320
1389
|
if (!standard(fiber.entry, config)) return config;
|
|
1321
1390
|
if (!originals.has(fiber)) originals.set(fiber, { ...config });
|
|
@@ -1345,7 +1414,8 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1345
1414
|
fiber,
|
|
1346
1415
|
before
|
|
1347
1416
|
});
|
|
1348
|
-
|
|
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);
|
|
1349
1419
|
}
|
|
1350
1420
|
await persist?.(mode);
|
|
1351
1421
|
} catch (error) {
|
|
@@ -1364,7 +1434,7 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
|
|
|
1364
1434
|
await tail.catch(() => {});
|
|
1365
1435
|
disposed = true;
|
|
1366
1436
|
selected = "dsh";
|
|
1367
|
-
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);
|
|
1368
1438
|
originals.clear();
|
|
1369
1439
|
}
|
|
1370
1440
|
};
|
|
@@ -2860,8 +2930,28 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
|
|
|
2860
2930
|
}
|
|
2861
2931
|
Object.freeze(["0.82.1", "0.85.1"]);
|
|
2862
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
|
|
2863
2953
|
//#region src/version.js
|
|
2864
|
-
const PACKAGE_VERSION = "2.2.
|
|
2954
|
+
const PACKAGE_VERSION = "2.2.3";
|
|
2865
2955
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
2866
2956
|
//#endregion
|
|
2867
2957
|
//#region src/model-catalog.js
|
|
@@ -2986,12 +3076,8 @@ function createOfficialModelCatalog(options = {}) {
|
|
|
2986
3076
|
let timer;
|
|
2987
3077
|
const timeoutError = /* @__PURE__ */ new Error("Codex model catalog refresh timed out");
|
|
2988
3078
|
const work = (async () => {
|
|
2989
|
-
const
|
|
3079
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, requestSignal);
|
|
2990
3080
|
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
2991
|
-
const credential = await options.readCredential({ signal: requestSignal });
|
|
2992
|
-
if (currentGeneration !== generation || requestSignal.aborted) return false;
|
|
2993
|
-
const access = auth?.auth?.apiKey;
|
|
2994
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
2995
3081
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) return false;
|
|
2996
3082
|
const headers = {
|
|
2997
3083
|
authorization: `Bearer ${access}`,
|
|
@@ -3148,10 +3234,7 @@ function createCodexSearchProvider(options) {
|
|
|
3148
3234
|
signal?.throwIfAborted();
|
|
3149
3235
|
const preferences = readCapabilitySettings(options.resolvePreferences?.());
|
|
3150
3236
|
if (preferences.searchMode === "disabled") throw new WebError("Codex search is disabled in subscription settings", "WEB_PROVIDER_UNAVAILABLE");
|
|
3151
|
-
const
|
|
3152
|
-
const credential = await options.readCredential({ signal });
|
|
3153
|
-
const access = auth?.auth?.apiKey;
|
|
3154
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
3237
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, signal);
|
|
3155
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");
|
|
3156
3239
|
const model = nonEmpty$1(options.resolveModel?.()) ?? DEFAULT_MODEL;
|
|
3157
3240
|
const id = nonEmpty$1(options.resolveSessionId?.()) ?? randomUUID();
|
|
@@ -3662,10 +3745,7 @@ function createCodexImageTool(options) {
|
|
|
3662
3745
|
const prompt = nonEmpty(args.prompt);
|
|
3663
3746
|
if (prompt === void 0) throw new Error("prompt must be a non-empty string");
|
|
3664
3747
|
const imageOptions = normalizeImageOptions(args);
|
|
3665
|
-
const
|
|
3666
|
-
const credential = await options.readCredential({ signal: exec.signal });
|
|
3667
|
-
const access = auth?.auth?.apiKey;
|
|
3668
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
3748
|
+
const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, exec.signal);
|
|
3669
3749
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
3670
3750
|
if (!attachments.imageLimits.mediaTypes.includes("image/png")) throw new Error("This DSH installation does not accept PNG image attachments");
|
|
3671
3751
|
const maximumBytes = Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
|
|
@@ -3907,6 +3987,229 @@ var OriginalImageStore = class {
|
|
|
3907
3987
|
}
|
|
3908
3988
|
};
|
|
3909
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
|
|
3910
4213
|
//#region src/diagnostics.js
|
|
3911
4214
|
const requestAreas = /* @__PURE__ */ new Set([
|
|
3912
4215
|
"login",
|
|
@@ -3939,6 +4242,26 @@ const elapsedBuckets = /* @__PURE__ */ new Set([
|
|
|
3939
4242
|
"5-15s",
|
|
3940
4243
|
"over-15s"
|
|
3941
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
|
+
}
|
|
3942
4265
|
function safeRequests(network) {
|
|
3943
4266
|
const raw = network?.snapshot?.() ?? {};
|
|
3944
4267
|
const result = {};
|
|
@@ -3957,17 +4280,113 @@ function safeRequests(network) {
|
|
|
3957
4280
|
return result;
|
|
3958
4281
|
}
|
|
3959
4282
|
/** Build a support report that deliberately excludes OAuth and account metadata. */
|
|
3960
|
-
async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction }) {
|
|
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);
|
|
3961
4383
|
let account = { status: "unknown" };
|
|
3962
4384
|
const issues = [];
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
const preference = preferences.status();
|
|
3969
|
-
const catalog = modelCatalog?.status?.();
|
|
3970
|
-
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) => {
|
|
3971
4390
|
if (!value || typeof value.model !== "string" || !/^[a-z][a-z0-9._-]{0,79}$/u.test(value.model)) return [];
|
|
3972
4391
|
const fields = Object.fromEntries([
|
|
3973
4392
|
"reasoning",
|
|
@@ -3985,6 +4404,22 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3985
4404
|
if (gaps.length) issues.push({ code: "catalog-capabilities-not-adapted" });
|
|
3986
4405
|
return {
|
|
3987
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
|
+
},
|
|
3988
4423
|
package: "dsh-codex-subscription",
|
|
3989
4424
|
version: PACKAGE_VERSION,
|
|
3990
4425
|
runtime: {
|
|
@@ -3994,20 +4429,11 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
3994
4429
|
},
|
|
3995
4430
|
account,
|
|
3996
4431
|
login,
|
|
3997
|
-
requests:
|
|
3998
|
-
...
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
"deltaRequests",
|
|
4003
|
-
"websocketFailures",
|
|
4004
|
-
"sseFallbacks"
|
|
4005
|
-
]) } : {},
|
|
4006
|
-
...compaction ? { compaction: safeCounters(compaction, [
|
|
4007
|
-
"requests",
|
|
4008
|
-
"checkpointsSaved",
|
|
4009
|
-
"checkpointsReused"
|
|
4010
|
-
]) } : {},
|
|
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 } : {},
|
|
4011
4437
|
...catalog && ["fallback", "online"].includes(catalog.source) && [
|
|
4012
4438
|
"idle",
|
|
4013
4439
|
"refreshing",
|
|
@@ -4025,7 +4451,7 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
|
|
|
4025
4451
|
...typeof preference.outputVerbosity === "string" ? { outputVerbosity: preference.outputVerbosity } : {},
|
|
4026
4452
|
searchProvider: preference.searchProvider,
|
|
4027
4453
|
speedMode: preference.speedMode,
|
|
4028
|
-
writable: preference.writable
|
|
4454
|
+
...typeof preference.writable === "boolean" ? { writable: preference.writable } : {}
|
|
4029
4455
|
},
|
|
4030
4456
|
issues
|
|
4031
4457
|
};
|
|
@@ -4192,10 +4618,7 @@ function createCodexUsageReader(options) {
|
|
|
4192
4618
|
let inFlight;
|
|
4193
4619
|
let generation = 0;
|
|
4194
4620
|
const load = async (signal) => {
|
|
4195
|
-
const
|
|
4196
|
-
const credential = await readCredential({ signal });
|
|
4197
|
-
const access = auth?.auth?.apiKey;
|
|
4198
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
4621
|
+
const { access, accountId } = await readSubscriptionCredentials(getAuth, readCredential, signal);
|
|
4199
4622
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
4200
4623
|
const response = await fetchUsage(CODEX_USAGE_URL, {
|
|
4201
4624
|
method: "GET",
|
|
@@ -4851,9 +5274,7 @@ const requestSignal = (signal, timeoutMs) => {
|
|
|
4851
5274
|
function safeCopy(value) {
|
|
4852
5275
|
return typeof value === "string" && value.length > 0 ? value.slice(0, MAX_COPY_LENGTH) : void 0;
|
|
4853
5276
|
}
|
|
4854
|
-
function credentialsOf(
|
|
4855
|
-
const access = auth?.auth?.apiKey;
|
|
4856
|
-
const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
|
|
5277
|
+
function credentialsOf({ access, accountId }) {
|
|
4857
5278
|
if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
|
|
4858
5279
|
return {
|
|
4859
5280
|
access,
|
|
@@ -4938,7 +5359,7 @@ function createCodexResetCreditService(options) {
|
|
|
4938
5359
|
...credit.title === void 0 ? {} : { name: credit.title },
|
|
4939
5360
|
...credit.creditExpiresAt === void 0 ? {} : { expiresAt: credit.creditExpiresAt }
|
|
4940
5361
|
});
|
|
4941
|
-
const resolveCredentials = async (signal) => credentialsOf(await getAuth
|
|
5362
|
+
const resolveCredentials = async (signal) => credentialsOf(await readSubscriptionCredentials(getAuth, readCredential, signal));
|
|
4942
5363
|
const readDetails = async (signal, credentials) => {
|
|
4943
5364
|
const { access, accountId } = credentials ?? await resolveCredentials(signal);
|
|
4944
5365
|
const response = await fetchReset(CODEX_RESET_CREDITS_URL, {
|
|
@@ -5129,7 +5550,7 @@ const publicError = (code, message) => ({
|
|
|
5129
5550
|
details: { issues: [] }
|
|
5130
5551
|
}
|
|
5131
5552
|
});
|
|
5132
|
-
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 }) {
|
|
5133
5554
|
return async (endpoint, payload, signal) => {
|
|
5134
5555
|
if (endpoint === "storage/status" || endpoint === "storage/clear-forecast") try {
|
|
5135
5556
|
signal.throwIfAborted();
|
|
@@ -5287,23 +5708,20 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
|
|
|
5287
5708
|
return publicError("internal", message);
|
|
5288
5709
|
}
|
|
5289
5710
|
const result = await authHandler(endpoint, payload, signal);
|
|
5290
|
-
if (result.ok === true &&
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
modelCatalog?.clear();
|
|
5305
|
-
onAccountChanged?.();
|
|
5306
|
-
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(() => {});
|
|
5307
5725
|
} else if (result.ok === true && (endpoint === "status" || result.value?.authenticated === true)) modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
|
|
5308
5726
|
return result;
|
|
5309
5727
|
};
|
|
@@ -5381,6 +5799,8 @@ function apply(ctx, config = {}) {
|
|
|
5381
5799
|
const settings = createSettingsAdapter(ctx, z.object(settingsFields), config, SETTINGS_NAMESPACE);
|
|
5382
5800
|
const searchProvider = createSearchProviderSwitcher(ctx.loader);
|
|
5383
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");
|
|
5384
5804
|
const originalImages = new OriginalImageStore();
|
|
5385
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, {
|
|
5386
5806
|
key: ACCOUNT_VAULT_KEY,
|
|
@@ -5714,14 +6134,19 @@ function apply(ctx, config = {}) {
|
|
|
5714
6134
|
network,
|
|
5715
6135
|
modelCatalog,
|
|
5716
6136
|
connection,
|
|
5717
|
-
compaction
|
|
6137
|
+
compaction,
|
|
6138
|
+
operations: diagnosticOperations,
|
|
6139
|
+
runtimeManagement,
|
|
6140
|
+
storage: () => usageReader.storage(),
|
|
6141
|
+
tools: ctx.tools
|
|
5718
6142
|
}),
|
|
6143
|
+
onCleanupFailure: () => diagnosticOperations.record("account/cleanup", "failed"),
|
|
5719
6144
|
modelCatalog,
|
|
5720
6145
|
closeConnections: () => connection.dispose(),
|
|
5721
6146
|
originalImages,
|
|
5722
6147
|
resolveInheritedOriginal: (sessionId, assetId) => inheritedOriginalImageRef(ctx.get?.("sessions")?.get?.(sessionId), assetId)
|
|
5723
6148
|
});
|
|
5724
|
-
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));
|
|
5725
6150
|
ctx.effect(() => {
|
|
5726
6151
|
modelCatalog.refresh().catch((error) => ctx.logger?.debug?.("could not refresh Codex model catalog: %s", error.message));
|
|
5727
6152
|
}, "codex-subscription: official model catalog");
|