dsh-codex-subscription 3.0.0-beta.7 → 3.0.0-beta.8
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 +6 -6
- package/README.md +6 -6
- package/compatibility.json +3 -1
- package/lib/client.js +318 -191
- package/lib/index.js +256 -33
- package/package.json +18 -18
package/lib/index.js
CHANGED
|
@@ -1199,6 +1199,7 @@ const RPC_ENDPOINTS = Object.freeze([
|
|
|
1199
1199
|
"account/select",
|
|
1200
1200
|
"account/remove",
|
|
1201
1201
|
"usage",
|
|
1202
|
+
"usage/accounts",
|
|
1202
1203
|
"usage/history",
|
|
1203
1204
|
"usage/profile",
|
|
1204
1205
|
"diagnostics",
|
|
@@ -1400,7 +1401,9 @@ const SUPPORTED_RUNTIME_VERSIONS = Object.freeze([
|
|
|
1400
1401
|
"0.2.0-rc.2",
|
|
1401
1402
|
"0.2.1-alpha.1",
|
|
1402
1403
|
"0.2.1-alpha.2",
|
|
1403
|
-
"0.2.1-alpha.3"
|
|
1404
|
+
"0.2.1-alpha.3",
|
|
1405
|
+
"0.2.1-alpha.4",
|
|
1406
|
+
"0.2.1-alpha.5"
|
|
1404
1407
|
]);
|
|
1405
1408
|
const require = createRequire(import.meta.url);
|
|
1406
1409
|
const execute = promisify(execFile);
|
|
@@ -2158,9 +2161,36 @@ function inheritedOriginalImageRef(session, assetId) {
|
|
|
2158
2161
|
}
|
|
2159
2162
|
//#endregion
|
|
2160
2163
|
//#region src/version.js
|
|
2161
|
-
const PACKAGE_VERSION = "3.0.0-beta.
|
|
2164
|
+
const PACKAGE_VERSION = "3.0.0-beta.8";
|
|
2162
2165
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
2163
2166
|
//#endregion
|
|
2167
|
+
//#region src/http-failure.js
|
|
2168
|
+
const SIGN_IN_RENEW = "ChatGPT sign-in needs to be renewed";
|
|
2169
|
+
const REQUEST_BLOCKED = "ChatGPT blocked this request before it reached your account (HTTP 403); your sign-in is fine, try again later or check your network, proxy or VPN";
|
|
2170
|
+
const CHALLENGE_MARKERS = /just a moment|cf-chl|_cf_chl_opt|attention required|enable javascript and cookies|challenge-platform/iu;
|
|
2171
|
+
/** Whether error text is a Cloudflare / network-protection page (pi-ai keeps only the body of a failed request, not its status). */
|
|
2172
|
+
const isChallengePage = (text) => typeof text === "string" && /^\s*<(?:!doctype|html)/iu.test(text) && CHALLENGE_MARKERS.test(text);
|
|
2173
|
+
/** Whether a 403 response is a Cloudflare / network-protection page rather than an answer from the ChatGPT backend. */
|
|
2174
|
+
async function isBlockedByNetwork(response) {
|
|
2175
|
+
if (response?.status !== 403) return false;
|
|
2176
|
+
try {
|
|
2177
|
+
const header = (name) => String(response.headers?.get?.(name) ?? "");
|
|
2178
|
+
if (/challenge/iu.test(header("cf-mitigated"))) return true;
|
|
2179
|
+
if (!/html/iu.test(header("content-type"))) return false;
|
|
2180
|
+
if (/cloudflare/iu.test(header("server"))) return true;
|
|
2181
|
+
const text = typeof response.clone === "function" ? (await response.clone().text()).slice(0, 8192) : "";
|
|
2182
|
+
return CHALLENGE_MARKERS.test(text);
|
|
2183
|
+
} catch {
|
|
2184
|
+
return false;
|
|
2185
|
+
}
|
|
2186
|
+
}
|
|
2187
|
+
/** The error message for a 401 or 403 answer, or undefined for any other status. */
|
|
2188
|
+
async function authFailureMessage(response) {
|
|
2189
|
+
if (response?.status === 401) return SIGN_IN_RENEW;
|
|
2190
|
+
if (response?.status !== 403) return void 0;
|
|
2191
|
+
return await isBlockedByNetwork(response) ? REQUEST_BLOCKED : SIGN_IN_RENEW;
|
|
2192
|
+
}
|
|
2193
|
+
//#endregion
|
|
2164
2194
|
//#region src/codex-images.js
|
|
2165
2195
|
const CODEX_IMAGE_TOOL_NAME = "codex_image_generate";
|
|
2166
2196
|
const CODEX_IMAGE_GENERATION_URL = "https://chatgpt.com/backend-api/codex/images/generations";
|
|
@@ -2663,7 +2693,8 @@ function createCodexImageTool(options) {
|
|
|
2663
2693
|
throw new Error(`Codex image ${editing ? "edit" : "generation"} request failed${requestFailureReason(error)}`, { cause: error });
|
|
2664
2694
|
}
|
|
2665
2695
|
if (!response.ok) {
|
|
2666
|
-
|
|
2696
|
+
const authFailure = await authFailureMessage(response);
|
|
2697
|
+
if (authFailure !== void 0) throw new Error(authFailure);
|
|
2667
2698
|
const requestId = supportId(response);
|
|
2668
2699
|
if (response.status === 429) throw new Error(`${await limitMessage(response)}${requestId}`);
|
|
2669
2700
|
throw new Error(`Codex image ${editing ? "edit" : "generation"} failed (HTTP ${response.status})${requestId}`);
|
|
@@ -4314,6 +4345,16 @@ function createDshRemoteControl({ controller, agents, permissions = () => void 0
|
|
|
4314
4345
|
reasoningEffort: agent.options.reasoningEffort
|
|
4315
4346
|
} : void 0;
|
|
4316
4347
|
};
|
|
4348
|
+
const directoryOf = (session) => {
|
|
4349
|
+
try {
|
|
4350
|
+
const events = typeof session?.snapshotEvents === "function" ? session.snapshotEvents() : session?.eventsSnapshot;
|
|
4351
|
+
for (let index = (events?.length ?? 0) - 1; index >= 0; index -= 1) {
|
|
4352
|
+
const cwd = events[index]?.type === "working-directory/change" ? events[index].data?.cwd : void 0;
|
|
4353
|
+
if (typeof cwd === "string" && cwd !== "") return cwd;
|
|
4354
|
+
}
|
|
4355
|
+
} catch {}
|
|
4356
|
+
return session?.meta?.cwd;
|
|
4357
|
+
};
|
|
4317
4358
|
const sameSelection = (a, b) => a?.provider === b?.provider && a?.model === b?.model && a?.reasoningEffort === b?.reasoningEffort;
|
|
4318
4359
|
const selection = (agent) => {
|
|
4319
4360
|
const saved = recorded(agent);
|
|
@@ -4435,16 +4476,17 @@ function createDshRemoteControl({ controller, agents, permissions = () => void 0
|
|
|
4435
4476
|
const messages = session.deriveMessages();
|
|
4436
4477
|
const first = messages.find((message) => typedText(message) !== "");
|
|
4437
4478
|
const imageUrls = await imageUrlsOf(messages);
|
|
4479
|
+
const cwd = directoryOf(session);
|
|
4438
4480
|
return {
|
|
4439
4481
|
agent,
|
|
4440
4482
|
thread: thread({
|
|
4441
4483
|
id,
|
|
4442
|
-
cwd
|
|
4484
|
+
cwd,
|
|
4443
4485
|
createdAt: session.meta?.createdAt,
|
|
4444
4486
|
running: active.has(id),
|
|
4445
4487
|
projectId: projectIdOf(id),
|
|
4446
4488
|
preview: first ? visibleText(first.content).slice(0, 200) : "",
|
|
4447
|
-
turns: withLiveTurn(id, projectTurns(messages, session.meta?.createdAt, imageUrls,
|
|
4489
|
+
turns: withLiveTurn(id, projectTurns(messages, session.meta?.createdAt, imageUrls, cwd))
|
|
4448
4490
|
})
|
|
4449
4491
|
};
|
|
4450
4492
|
};
|
|
@@ -5341,7 +5383,7 @@ function createDshRemoteControl({ controller, agents, permissions = () => void 0
|
|
|
5341
5383
|
}) : askPhone(request, "item/commandExecution/requestApproval", {
|
|
5342
5384
|
...common,
|
|
5343
5385
|
command: commandText(session, request),
|
|
5344
|
-
cwd: session
|
|
5386
|
+
cwd: directoryOf(session) ?? null,
|
|
5345
5387
|
reason,
|
|
5346
5388
|
commandActions: [],
|
|
5347
5389
|
availableDecisions: [
|
|
@@ -5475,7 +5517,7 @@ function createDshRemoteControl({ controller, agents, permissions = () => void 0
|
|
|
5475
5517
|
type: "text",
|
|
5476
5518
|
text: "x"
|
|
5477
5519
|
}]
|
|
5478
|
-
}, message], void 0, void 0, session
|
|
5520
|
+
}, message], void 0, void 0, directoryOf(session)).flatMap((turn) => turn.items.slice(1));
|
|
5479
5521
|
const stream = streams.get(session.id);
|
|
5480
5522
|
streams.delete(session.id);
|
|
5481
5523
|
let reuse = stream?.item;
|
|
@@ -5772,6 +5814,42 @@ var DshOAuthAccountVault = class {
|
|
|
5772
5814
|
return clone$1(payload?.accounts.find((account) => account.id === payload.activeId)?.credential);
|
|
5773
5815
|
});
|
|
5774
5816
|
}
|
|
5817
|
+
/** One account's credential, whether or not it is the active one. */
|
|
5818
|
+
readAccount(id) {
|
|
5819
|
+
return this.#enqueue(async () => {
|
|
5820
|
+
const payload = await this.#ensurePayload();
|
|
5821
|
+
return clone$1(payload?.accounts.find((account) => account.id === id)?.credential);
|
|
5822
|
+
});
|
|
5823
|
+
}
|
|
5824
|
+
/** Update one account's credential (a token refresh) without changing which account is active. */
|
|
5825
|
+
modifyAccount(id, update) {
|
|
5826
|
+
return this.#enqueue(async () => {
|
|
5827
|
+
let result;
|
|
5828
|
+
await this.#modifyPayload(async (current) => {
|
|
5829
|
+
const index = current.accounts.findIndex((account) => account.id === id);
|
|
5830
|
+
if (index < 0) throw new Error("Unknown Codex account");
|
|
5831
|
+
const previous = clone$1(current.accounts[index].credential);
|
|
5832
|
+
const next = await update(previous);
|
|
5833
|
+
if (next === void 0) {
|
|
5834
|
+
result = previous;
|
|
5835
|
+
return current;
|
|
5836
|
+
}
|
|
5837
|
+
const credential = sanitizeOAuthCredential(next);
|
|
5838
|
+
if (credential.email === void 0 && previous.email !== void 0) credential.email = previous.email;
|
|
5839
|
+
const accounts = [...current.accounts];
|
|
5840
|
+
accounts[index] = {
|
|
5841
|
+
...accounts[index],
|
|
5842
|
+
credential
|
|
5843
|
+
};
|
|
5844
|
+
result = clone$1(credential);
|
|
5845
|
+
return {
|
|
5846
|
+
...current,
|
|
5847
|
+
accounts
|
|
5848
|
+
};
|
|
5849
|
+
});
|
|
5850
|
+
return result;
|
|
5851
|
+
});
|
|
5852
|
+
}
|
|
5775
5853
|
activeId() {
|
|
5776
5854
|
return this.#enqueue(async () => (await this.#ensurePayload())?.activeId);
|
|
5777
5855
|
}
|
|
@@ -5871,11 +5949,11 @@ var DshOAuthAccountVault = class {
|
|
|
5871
5949
|
};
|
|
5872
5950
|
//#endregion
|
|
5873
5951
|
//#region src/credential-store.js
|
|
5874
|
-
const PROVIDER$
|
|
5952
|
+
const PROVIDER$2 = "openai-codex";
|
|
5875
5953
|
const abortIfNeeded = (options) => options?.signal?.throwIfAborted();
|
|
5876
5954
|
const clone = (value) => value === void 0 ? void 0 : structuredClone(value);
|
|
5877
|
-
function assertProvider(providerId) {
|
|
5878
|
-
if (providerId !== PROVIDER$
|
|
5955
|
+
function assertProvider$1(providerId) {
|
|
5956
|
+
if (providerId !== PROVIDER$2) throw new Error(`Codex credential store does not own provider ${JSON.stringify(providerId)}`);
|
|
5879
5957
|
}
|
|
5880
5958
|
function assertOAuthCredential(value) {
|
|
5881
5959
|
if (value === void 0) return void 0;
|
|
@@ -5908,7 +5986,7 @@ var DshOAuthCredentialStore = class {
|
|
|
5908
5986
|
this.vault = options.vault;
|
|
5909
5987
|
}
|
|
5910
5988
|
#enqueue(providerId, operation, options) {
|
|
5911
|
-
assertProvider(providerId);
|
|
5989
|
+
assertProvider$1(providerId);
|
|
5912
5990
|
const current = (this.#chains.get(providerId) ?? Promise.resolve()).catch(() => void 0).then(async () => {
|
|
5913
5991
|
abortIfNeeded(options);
|
|
5914
5992
|
return operation();
|
|
@@ -5921,7 +5999,7 @@ var DshOAuthCredentialStore = class {
|
|
|
5921
5999
|
return current;
|
|
5922
6000
|
}
|
|
5923
6001
|
async #read(providerId, options) {
|
|
5924
|
-
assertProvider(providerId);
|
|
6002
|
+
assertProvider$1(providerId);
|
|
5925
6003
|
abortIfNeeded(options);
|
|
5926
6004
|
if (this.vault !== void 0) {
|
|
5927
6005
|
const current = await this.vault.readActive();
|
|
@@ -5954,8 +6032,8 @@ var DshOAuthCredentialStore = class {
|
|
|
5954
6032
|
}
|
|
5955
6033
|
async list(options) {
|
|
5956
6034
|
abortIfNeeded(options);
|
|
5957
|
-
return await this.read(PROVIDER$
|
|
5958
|
-
providerId: PROVIDER$
|
|
6035
|
+
return await this.read(PROVIDER$2, options) === void 0 ? [] : [{
|
|
6036
|
+
providerId: PROVIDER$2,
|
|
5959
6037
|
type: "oauth"
|
|
5960
6038
|
}];
|
|
5961
6039
|
}
|
|
@@ -6005,16 +6083,16 @@ function createCodexAuthService(models, store, options = {}) {
|
|
|
6005
6083
|
const createPendingStore = options.createPendingStore ?? (() => new PendingOAuthCredentialStore());
|
|
6006
6084
|
return Object.freeze({
|
|
6007
6085
|
async status(options) {
|
|
6008
|
-
const current = await store.read(PROVIDER$
|
|
6086
|
+
const current = await store.read(PROVIDER$2, options);
|
|
6009
6087
|
const accounts = await accountVault?.list();
|
|
6010
6088
|
if (current === void 0) return {
|
|
6011
6089
|
authenticated: false,
|
|
6012
|
-
provider: PROVIDER$
|
|
6090
|
+
provider: PROVIDER$2,
|
|
6013
6091
|
...accounts === void 0 ? {} : { accounts }
|
|
6014
6092
|
};
|
|
6015
6093
|
return {
|
|
6016
6094
|
authenticated: true,
|
|
6017
|
-
provider: PROVIDER$
|
|
6095
|
+
provider: PROVIDER$2,
|
|
6018
6096
|
type: "oauth",
|
|
6019
6097
|
expiresAt: current.expires,
|
|
6020
6098
|
...accounts === void 0 ? {} : { accounts }
|
|
@@ -6025,13 +6103,13 @@ function createCodexAuthService(models, store, options = {}) {
|
|
|
6025
6103
|
if (accountVault === void 0 || createLoginModels === void 0) throw new Error("Codex multi-account is unavailable");
|
|
6026
6104
|
return runLogin(async () => {
|
|
6027
6105
|
const pending = createPendingStore();
|
|
6028
|
-
await createLoginModels(pending).login(PROVIDER$
|
|
6106
|
+
await createLoginModels(pending).login(PROVIDER$2, "oauth", interaction);
|
|
6029
6107
|
const credential = pending.credential();
|
|
6030
6108
|
if (credential === void 0) throw new Error("Codex login did not return credentials");
|
|
6031
6109
|
await accountVault.add(input.label, credential);
|
|
6032
6110
|
});
|
|
6033
6111
|
}
|
|
6034
|
-
return runLogin(() => models.login(PROVIDER$
|
|
6112
|
+
return runLogin(() => models.login(PROVIDER$2, "oauth", interaction));
|
|
6035
6113
|
},
|
|
6036
6114
|
async select(id) {
|
|
6037
6115
|
if (accountVault === void 0) throw new Error("Codex multi-account is unavailable");
|
|
@@ -6044,7 +6122,7 @@ function createCodexAuthService(models, store, options = {}) {
|
|
|
6044
6122
|
return this.status();
|
|
6045
6123
|
},
|
|
6046
6124
|
logout(options) {
|
|
6047
|
-
return models.logout(PROVIDER$
|
|
6125
|
+
return models.logout(PROVIDER$2, options);
|
|
6048
6126
|
}
|
|
6049
6127
|
});
|
|
6050
6128
|
}
|
|
@@ -6580,6 +6658,13 @@ function normalizeTransportEvent(event, signal) {
|
|
|
6580
6658
|
if (signal?.aborted || event?.type !== "error" || event.reason !== "error") return event;
|
|
6581
6659
|
const message = event.error?.errorMessage;
|
|
6582
6660
|
if (typeof message !== "string") return event;
|
|
6661
|
+
if (isChallengePage(message)) return {
|
|
6662
|
+
...event,
|
|
6663
|
+
error: {
|
|
6664
|
+
...event.error,
|
|
6665
|
+
errorMessage: REQUEST_BLOCKED
|
|
6666
|
+
}
|
|
6667
|
+
};
|
|
6583
6668
|
if (!TRANSIENT_WEBSOCKET_CLOSE.test(message) && !GATEWAY_REPLAY_BUFFER_EXCEEDED.test(message)) return event;
|
|
6584
6669
|
return {
|
|
6585
6670
|
...event,
|
|
@@ -6896,7 +6981,8 @@ Object.freeze([
|
|
|
6896
6981
|
"0.85.1",
|
|
6897
6982
|
"0.87.1",
|
|
6898
6983
|
"1.1.0",
|
|
6899
|
-
"1.2.0"
|
|
6984
|
+
"1.2.0",
|
|
6985
|
+
"1.3.0"
|
|
6900
6986
|
]);
|
|
6901
6987
|
//#endregion
|
|
6902
6988
|
//#region src/model-catalog.js
|
|
@@ -7303,7 +7389,10 @@ function createCodexSearchProvider(options) {
|
|
|
7303
7389
|
if (signal?.aborted || error?.name === "AbortError") throw new WebError("Codex search aborted", "WEB_ABORTED", { cause: error });
|
|
7304
7390
|
throw new WebError("Codex search request failed", "WEB_PROVIDER_ERROR", { cause: error });
|
|
7305
7391
|
}
|
|
7306
|
-
if (!response.ok)
|
|
7392
|
+
if (!response.ok) {
|
|
7393
|
+
const authFailure = await authFailureMessage(response);
|
|
7394
|
+
throw authFailure === "ChatGPT sign-in needs to be renewed" ? new WebError(authFailure, "WEB_PROVIDER_CREDENTIAL_MISSING") : authFailure !== void 0 ? new WebError(authFailure, "WEB_PROVIDER_ERROR") : new WebError(`Codex search request failed (HTTP ${response.status})`, "WEB_PROVIDER_ERROR");
|
|
7395
|
+
}
|
|
7307
7396
|
let value;
|
|
7308
7397
|
try {
|
|
7309
7398
|
value = await response.json();
|
|
@@ -8302,7 +8391,7 @@ function createCodexUsageReader(options) {
|
|
|
8302
8391
|
signal: requestSignal$1(signal, timeoutMs)
|
|
8303
8392
|
});
|
|
8304
8393
|
if (!response.ok) {
|
|
8305
|
-
const error =
|
|
8394
|
+
const error = new Error(await authFailureMessage(response) ?? `ChatGPT usage request failed (HTTP ${response.status})`);
|
|
8306
8395
|
Object.defineProperty(error, "retryAfterMs", { value: retryAfterMs(response, now, maxRetryAfterMs) });
|
|
8307
8396
|
throw error;
|
|
8308
8397
|
}
|
|
@@ -8353,6 +8442,102 @@ function createCodexUsageReader(options) {
|
|
|
8353
8442
|
});
|
|
8354
8443
|
}
|
|
8355
8444
|
//#endregion
|
|
8445
|
+
//#region src/account-usage.js
|
|
8446
|
+
const PROVIDER$1 = "openai-codex";
|
|
8447
|
+
const MAX_PARALLEL = 4;
|
|
8448
|
+
const assertProvider = (providerId) => {
|
|
8449
|
+
if (providerId !== PROVIDER$1) throw new Error("Codex account usage received an unknown provider");
|
|
8450
|
+
};
|
|
8451
|
+
/** pi-ai's OAuth credential store, bound to one account of the vault. */
|
|
8452
|
+
function accountCredentialStore(vault, id, expirySkewMs = 0) {
|
|
8453
|
+
const shift = (credential) => credential === void 0 || expirySkewMs === 0 ? credential : {
|
|
8454
|
+
...credential,
|
|
8455
|
+
expires: credential.expires - expirySkewMs
|
|
8456
|
+
};
|
|
8457
|
+
return {
|
|
8458
|
+
async read(providerId) {
|
|
8459
|
+
assertProvider(providerId);
|
|
8460
|
+
return shift(await vault.readAccount(id));
|
|
8461
|
+
},
|
|
8462
|
+
async list() {
|
|
8463
|
+
return await vault.readAccount(id) === void 0 ? [] : [{
|
|
8464
|
+
providerId: PROVIDER$1,
|
|
8465
|
+
type: "oauth"
|
|
8466
|
+
}];
|
|
8467
|
+
},
|
|
8468
|
+
async modify(providerId, update) {
|
|
8469
|
+
assertProvider(providerId);
|
|
8470
|
+
return vault.modifyAccount(id, async (current) => update(shift(current)));
|
|
8471
|
+
},
|
|
8472
|
+
async delete() {
|
|
8473
|
+
throw new Error("An account usage read cannot remove a sign-in");
|
|
8474
|
+
}
|
|
8475
|
+
};
|
|
8476
|
+
}
|
|
8477
|
+
/**
|
|
8478
|
+
* @param {object} options
|
|
8479
|
+
* @param {{ list(): Promise<object[]>, readAccount(id: string): Promise<object|undefined>, modifyAccount: Function }} options.vault
|
|
8480
|
+
* @param {(credentials: object) => object} options.createModels builds a pi-ai Models over a credential store, with the Codex provider set
|
|
8481
|
+
* @param {typeof fetch} options.fetch
|
|
8482
|
+
*/
|
|
8483
|
+
function createAccountUsageReader({ vault, createModels, fetch, expirySkewMs = 0, now = Date.now, activeReader }) {
|
|
8484
|
+
const readers = /* @__PURE__ */ new Map();
|
|
8485
|
+
const readerFor = (id) => {
|
|
8486
|
+
let reader = readers.get(id);
|
|
8487
|
+
if (reader === void 0) {
|
|
8488
|
+
const models = createModels(accountCredentialStore(vault, id, expirySkewMs));
|
|
8489
|
+
reader = createCodexUsageReader({
|
|
8490
|
+
getAuth: () => models.getAuth(PROVIDER$1),
|
|
8491
|
+
readCredential: () => vault.readAccount(id).then((credential) => credential),
|
|
8492
|
+
fetch,
|
|
8493
|
+
now
|
|
8494
|
+
});
|
|
8495
|
+
readers.set(id, reader);
|
|
8496
|
+
}
|
|
8497
|
+
return reader;
|
|
8498
|
+
};
|
|
8499
|
+
const summarize = (error, signal) => {
|
|
8500
|
+
if (signal?.aborted) throw error;
|
|
8501
|
+
const message = error instanceof Error ? error.message : "";
|
|
8502
|
+
return ["ChatGPT subscription is not signed in", "ChatGPT sign-in needs to be renewed"].includes(message) || message.includes("blocked this request") ? message : "Could not read ChatGPT usage";
|
|
8503
|
+
};
|
|
8504
|
+
return Object.freeze({
|
|
8505
|
+
/** One entry per account: its usage reading, or the reason it could not be read. */
|
|
8506
|
+
async readAll({ force = false, signal } = {}) {
|
|
8507
|
+
const accounts = await vault.list();
|
|
8508
|
+
for (const id of [...readers.keys()]) if (!accounts.some((account) => account.id === id)) readers.delete(id);
|
|
8509
|
+
const entries = new Array(accounts.length);
|
|
8510
|
+
let next = 0;
|
|
8511
|
+
const worker = async () => {
|
|
8512
|
+
while (next < accounts.length) {
|
|
8513
|
+
const index = next++;
|
|
8514
|
+
const account = accounts[index];
|
|
8515
|
+
try {
|
|
8516
|
+
const reader = account.active && activeReader !== void 0 ? activeReader : readerFor(account.id);
|
|
8517
|
+
entries[index] = {
|
|
8518
|
+
id: account.id,
|
|
8519
|
+
usage: await reader.read({
|
|
8520
|
+
force,
|
|
8521
|
+
signal
|
|
8522
|
+
})
|
|
8523
|
+
};
|
|
8524
|
+
} catch (error) {
|
|
8525
|
+
entries[index] = {
|
|
8526
|
+
id: account.id,
|
|
8527
|
+
error: summarize(error, signal)
|
|
8528
|
+
};
|
|
8529
|
+
}
|
|
8530
|
+
}
|
|
8531
|
+
};
|
|
8532
|
+
await Promise.all(Array.from({ length: Math.min(MAX_PARALLEL, accounts.length) }, worker));
|
|
8533
|
+
return entries;
|
|
8534
|
+
},
|
|
8535
|
+
clear() {
|
|
8536
|
+
readers.clear();
|
|
8537
|
+
}
|
|
8538
|
+
});
|
|
8539
|
+
}
|
|
8540
|
+
//#endregion
|
|
8356
8541
|
//#region src/plan-history.js
|
|
8357
8542
|
const CODEX_PLAN_HISTORY_URL = "https://chatgpt.com/backend-api/wham/usage/plan_limit_history?days=7";
|
|
8358
8543
|
const DIMENSIONS = Object.freeze([
|
|
@@ -8432,7 +8617,7 @@ function createReportReader({ url, parse, label, getAuth, readCredential, fetch:
|
|
|
8432
8617
|
available: false,
|
|
8433
8618
|
fetchedAt: now()
|
|
8434
8619
|
};
|
|
8435
|
-
if (!response.ok) throw new Error(response.status === 401 ?
|
|
8620
|
+
if (!response.ok) throw new Error(response.status === 401 ? SIGN_IN_RENEW : response.status === 403 ? await isBlockedByNetwork(response) ? REQUEST_BLOCKED : `ChatGPT does not offer the ${label} to this account` : `ChatGPT ${label} request failed (HTTP ${response.status})`);
|
|
8436
8621
|
let body;
|
|
8437
8622
|
try {
|
|
8438
8623
|
body = await response.json();
|
|
@@ -8613,8 +8798,8 @@ function createConnectorsClient({ getAuth, readCredential, fetch: fetchRpc = fet
|
|
|
8613
8798
|
}),
|
|
8614
8799
|
signal: signal === void 0 ? timeout : AbortSignal.any([signal, timeout])
|
|
8615
8800
|
});
|
|
8616
|
-
if (response.status === 401) throw new Error(
|
|
8617
|
-
if (response.status === 403) throw new Error("ChatGPT does not offer connectors to this account");
|
|
8801
|
+
if (response.status === 401) throw new Error(SIGN_IN_RENEW);
|
|
8802
|
+
if (response.status === 403) throw new Error(await isBlockedByNetwork(response) ? REQUEST_BLOCKED : "ChatGPT does not offer connectors to this account");
|
|
8618
8803
|
if (!response.ok) throw new Error(`ChatGPT connectors request failed (HTTP ${response.status})`);
|
|
8619
8804
|
const message = await readRpc(response);
|
|
8620
8805
|
if (record$1(message.error)) throw new Error(`Connector error: ${clip(oneLine(String(message.error.message ?? message.error.code ?? "failed")), 200)}`);
|
|
@@ -9545,7 +9730,7 @@ function createCodexResetCreditService(options) {
|
|
|
9545
9730
|
},
|
|
9546
9731
|
signal: requestSignal(signal, timeoutMs)
|
|
9547
9732
|
});
|
|
9548
|
-
if (!response.ok) throw new Error(
|
|
9733
|
+
if (!response.ok) throw new Error(await authFailureMessage(response) ?? `ChatGPT quota reset request failed (HTTP ${response.status})`);
|
|
9549
9734
|
let raw;
|
|
9550
9735
|
try {
|
|
9551
9736
|
raw = await response.json();
|
|
@@ -9679,7 +9864,7 @@ function createCodexResetCreditService(options) {
|
|
|
9679
9864
|
throw new Error(UNCERTAIN_RESET_RESULT);
|
|
9680
9865
|
}
|
|
9681
9866
|
if (response.status !== 401 && response.status !== 403) retryable = false;
|
|
9682
|
-
throw new Error(
|
|
9867
|
+
throw new Error(await authFailureMessage(response) ?? `ChatGPT quota reset request failed (HTTP ${response.status})`);
|
|
9683
9868
|
}
|
|
9684
9869
|
let raw;
|
|
9685
9870
|
try {
|
|
@@ -9735,7 +9920,7 @@ async function settlesLive(preferences, patch, signal, { attempts = 8, delayMs =
|
|
|
9735
9920
|
}
|
|
9736
9921
|
return live();
|
|
9737
9922
|
}
|
|
9738
|
-
function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryReader, usageProfileReader, resetCreditService, preferences, defaultModel, runtimeManagement, remoteControl, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged, onCleanupFailure }) {
|
|
9923
|
+
function createSubscriptionRpcHandler({ authHandler, usageReader, accountUsageReader, planHistoryReader, usageProfileReader, resetCreditService, preferences, defaultModel, runtimeManagement, remoteControl, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged, onCleanupFailure }) {
|
|
9739
9924
|
return async (endpoint, payload, signal) => {
|
|
9740
9925
|
if (endpoint === "default-model/status" || endpoint === "default-model/select") try {
|
|
9741
9926
|
signal.throwIfAborted();
|
|
@@ -9904,7 +10089,11 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryRea
|
|
|
9904
10089
|
};
|
|
9905
10090
|
} catch (error) {
|
|
9906
10091
|
if (signal.aborted) throw error;
|
|
9907
|
-
const known = /* @__PURE__ */ new Set([
|
|
10092
|
+
const known = /* @__PURE__ */ new Set([
|
|
10093
|
+
"ChatGPT subscription is not signed in",
|
|
10094
|
+
"ChatGPT sign-in needs to be renewed",
|
|
10095
|
+
REQUEST_BLOCKED
|
|
10096
|
+
]);
|
|
9908
10097
|
const message = error instanceof Error && known.has(error.message) ? error.message : "Could not read ChatGPT usage";
|
|
9909
10098
|
return publicError("internal", message);
|
|
9910
10099
|
}
|
|
@@ -9919,9 +10108,26 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryRea
|
|
|
9919
10108
|
};
|
|
9920
10109
|
} catch (error) {
|
|
9921
10110
|
if (signal.aborted) throw error;
|
|
9922
|
-
const known = /* @__PURE__ */ new Set([
|
|
10111
|
+
const known = /* @__PURE__ */ new Set([
|
|
10112
|
+
"ChatGPT subscription is not signed in",
|
|
10113
|
+
"ChatGPT sign-in needs to be renewed",
|
|
10114
|
+
REQUEST_BLOCKED
|
|
10115
|
+
]);
|
|
9923
10116
|
return publicError("internal", error instanceof Error && known.has(error.message) ? error.message : "Could not read ChatGPT usage profile");
|
|
9924
10117
|
}
|
|
10118
|
+
if (endpoint === "usage/accounts") try {
|
|
10119
|
+
signal.throwIfAborted();
|
|
10120
|
+
return {
|
|
10121
|
+
ok: true,
|
|
10122
|
+
value: { accounts: await accountUsageReader.readAll({
|
|
10123
|
+
force: payload?.force === true,
|
|
10124
|
+
signal
|
|
10125
|
+
}) }
|
|
10126
|
+
};
|
|
10127
|
+
} catch (error) {
|
|
10128
|
+
if (signal.aborted) throw error;
|
|
10129
|
+
return publicError("internal", "Could not read the usage of the signed-in accounts");
|
|
10130
|
+
}
|
|
9925
10131
|
if (endpoint === "usage/history") try {
|
|
9926
10132
|
signal.throwIfAborted();
|
|
9927
10133
|
return {
|
|
@@ -9933,7 +10139,11 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryRea
|
|
|
9933
10139
|
};
|
|
9934
10140
|
} catch (error) {
|
|
9935
10141
|
if (signal.aborted) throw error;
|
|
9936
|
-
const known = /* @__PURE__ */ new Set([
|
|
10142
|
+
const known = /* @__PURE__ */ new Set([
|
|
10143
|
+
"ChatGPT subscription is not signed in",
|
|
10144
|
+
"ChatGPT sign-in needs to be renewed",
|
|
10145
|
+
REQUEST_BLOCKED
|
|
10146
|
+
]);
|
|
9937
10147
|
return publicError("internal", error instanceof Error && known.has(error.message) ? error.message : "Could not read ChatGPT usage history");
|
|
9938
10148
|
}
|
|
9939
10149
|
if (endpoint === "reset-credit/inspect" || endpoint === "reset-credit/prepare" || endpoint === "reset-credit/consume") try {
|
|
@@ -9954,6 +10164,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryRea
|
|
|
9954
10164
|
const known = /* @__PURE__ */ new Set([
|
|
9955
10165
|
"ChatGPT subscription is not signed in",
|
|
9956
10166
|
"ChatGPT sign-in needs to be renewed",
|
|
10167
|
+
REQUEST_BLOCKED,
|
|
9957
10168
|
"No quota reset is available",
|
|
9958
10169
|
"No usable quota reset is available",
|
|
9959
10170
|
"The available quota reset expires too soon",
|
|
@@ -9978,7 +10189,7 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, planHistoryRea
|
|
|
9978
10189
|
() => modelCatalog?.clear(),
|
|
9979
10190
|
() => onAccountChanged?.()
|
|
9980
10191
|
];
|
|
9981
|
-
if (endpoint === "logout") tasks.push(() => usageReader.clear());
|
|
10192
|
+
if (endpoint === "logout") tasks.push(() => usageReader.clear(), () => accountUsageReader?.clear());
|
|
9982
10193
|
if (endpoint === "account/remove" && typeof payload?.id === "string") tasks.push(() => usageReader.clearScope(payload.id));
|
|
9983
10194
|
if ((await Promise.allSettled(tasks.map((task) => Promise.resolve().then(task)))).some((value) => value.status === "rejected")) try {
|
|
9984
10195
|
onCleanupFailure?.();
|
|
@@ -10533,9 +10744,21 @@ function apply(ctx, config = {}) {
|
|
|
10533
10744
|
sketchBridge.dispose();
|
|
10534
10745
|
};
|
|
10535
10746
|
}, "codex-subscription: native sketch tool");
|
|
10747
|
+
const accountUsageReader = createAccountUsageReader({
|
|
10748
|
+
vault: accountVault,
|
|
10749
|
+
createModels: (credentials) => {
|
|
10750
|
+
const accountModels = createModels({ credentials });
|
|
10751
|
+
accountModels.setProvider(provider);
|
|
10752
|
+
return accountModels;
|
|
10753
|
+
},
|
|
10754
|
+
fetch: (input, init) => network.fetch("quota", input, init),
|
|
10755
|
+
expirySkewMs: OAUTH_EXPIRY_SKEW_MS,
|
|
10756
|
+
activeReader: baseUsageReader
|
|
10757
|
+
});
|
|
10536
10758
|
const subscriptionHandler = createSubscriptionRpcHandler({
|
|
10537
10759
|
authHandler: createCodexRpcHandler(coordinator, { openExternal: openCodexAuthUrl }),
|
|
10538
10760
|
usageReader,
|
|
10761
|
+
accountUsageReader,
|
|
10539
10762
|
planHistoryReader,
|
|
10540
10763
|
usageProfileReader,
|
|
10541
10764
|
resetCreditService,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "3.0.0-beta.
|
|
3
|
+
"version": "3.0.0-beta.8",
|
|
4
4
|
"description": "Use ChatGPT and Codex subscriptions in DeepSeek Harness with OAuth, quota, safe resets, web search, images, and Fast mode",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./lib/index.js",
|
|
@@ -77,24 +77,24 @@
|
|
|
77
77
|
},
|
|
78
78
|
"peerDependencies": {
|
|
79
79
|
"@deepseek-ai/cordis": "4.0.4 || 4.0.5-alpha.1 || 4.0.5-alpha.2",
|
|
80
|
-
"@deepseek-ai/dsh-api-remotes": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
81
|
-
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
82
|
-
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
83
|
-
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
84
|
-
"@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
85
|
-
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
86
|
-
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
87
|
-
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
88
|
-
"@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
89
|
-
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
90
|
-
"@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
91
|
-
"@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
92
|
-
"@deepseek-ai/dsh-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
93
|
-
"@deepseek-ai/dsh-subagent-codex": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
94
|
-
"@deepseek-ai/dsh-tools": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
95
|
-
"@deepseek-ai/dsh-web": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3",
|
|
80
|
+
"@deepseek-ai/dsh-api-remotes": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
81
|
+
"@deepseek-ai/dsh-client-connection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
82
|
+
"@deepseek-ai/dsh-client-locale": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
83
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-model-selection": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
85
|
+
"@deepseek-ai/dsh-client-ui-primitives": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
86
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
87
|
+
"@deepseek-ai/dsh-client-ui-slots": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
88
|
+
"@deepseek-ai/dsh-client-ui-tool": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
89
|
+
"@deepseek-ai/dsh-credentials": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
90
|
+
"@deepseek-ai/dsh-llm": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
91
|
+
"@deepseek-ai/dsh-llm-pi-ai": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
92
|
+
"@deepseek-ai/dsh-settings": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
93
|
+
"@deepseek-ai/dsh-subagent-codex": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
94
|
+
"@deepseek-ai/dsh-tools": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
95
|
+
"@deepseek-ai/dsh-web": "0.1.7-rc.2 || 0.2.0-rc.1 || 0.2.0-rc.2 || 0.2.1-alpha.1 || 0.2.1-alpha.2 || 0.2.1-alpha.3 || 0.2.1-alpha.4 || 0.2.1-alpha.5",
|
|
96
96
|
"@deepseek-ai/schemastery": "3.18.1 || ^3.18.2",
|
|
97
|
-
"@earendil-works/pi-ai": "0.85.1 || 0.87.1 || 1.1.0 || 1.2.0",
|
|
97
|
+
"@earendil-works/pi-ai": "0.85.1 || 0.87.1 || 1.1.0 || 1.2.0 || 1.3.0",
|
|
98
98
|
"react": "^18.2.0",
|
|
99
99
|
"react-dom": "^18.2.0"
|
|
100
100
|
},
|