dsh-codex-subscription 1.7.3 → 1.7.4
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/AGENTS.md +4 -4
- package/THIRD_PARTY_NOTICES.md +1 -0
- package/lib/index.js +162 -5
- package/package.json +4 -1
package/AGENTS.md
CHANGED
|
@@ -6,7 +6,7 @@ Use this guide when a user asks an Agent to install, update, verify, or remove
|
|
|
6
6
|
## Safety
|
|
7
7
|
|
|
8
8
|
- Confirm the target DSH installation and profile. Use `web` only when it is the user's target.
|
|
9
|
-
- Use the exact `1.7.
|
|
9
|
+
- Use the exact `1.7.4` package below; do not install a moving branch.
|
|
10
10
|
- Never print OAuth credentials, account IDs, authorization callbacks, or credential-store contents.
|
|
11
11
|
- Preserve the DSH profile, unrelated plugins, sessions, and saved sign-in.
|
|
12
12
|
- Do not start, stop, or restart DSH without explicit permission.
|
|
@@ -34,13 +34,13 @@ For an official DSH installation run through npm, keep the complete `npx` prefix
|
|
|
34
34
|
run command does not create a global `dsh` command:
|
|
35
35
|
|
|
36
36
|
```sh
|
|
37
|
-
npx -y @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add dsh-codex-subscription@1.7.
|
|
37
|
+
npx -y @deepseek-ai/dsh@0.1.1-rc.2 plugin --profile web add dsh-codex-subscription@1.7.4
|
|
38
38
|
```
|
|
39
39
|
|
|
40
40
|
With an existing global `dsh` command:
|
|
41
41
|
|
|
42
42
|
```sh
|
|
43
|
-
dsh plugin --profile web add dsh-codex-subscription@1.7.
|
|
43
|
+
dsh plugin --profile web add dsh-codex-subscription@1.7.4
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
Use the same `add` command to update or repair. This is the complete package-changing operation.
|
|
@@ -58,7 +58,7 @@ dsh --profile web --dump-config
|
|
|
58
58
|
|
|
59
59
|
For the official npm route, run the same checks with `npx -y @deepseek-ai/dsh@0.1.1-rc.2` in place of `dsh`.
|
|
60
60
|
|
|
61
|
-
1. `dsh-codex-subscription` version `1.7.
|
|
61
|
+
1. `dsh-codex-subscription` version `1.7.4` appears exactly once.
|
|
62
62
|
2. `codex-subscription` appears exactly once in the composed config.
|
|
63
63
|
3. No unrelated plugin or profile was changed and DSH was not restarted.
|
|
64
64
|
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -8,6 +8,7 @@ This project depends on software distributed under its own terms. The dependency
|
|
|
8
8
|
| `@earendil-works/pi-ai` 0.82.1 | OpenAI Codex OAuth, model catalog, Responses transport, and WebSocket continuation | MIT | https://github.com/earendil-works/pi |
|
|
9
9
|
| React | DSH settings component runtime | MIT | https://github.com/facebook/react |
|
|
10
10
|
| Heroicons | Composer speed icon | MIT | https://github.com/tailwindlabs/heroicons |
|
|
11
|
+
| `https-proxy-agent` 7.0.6 | HTTPS proxy transport for Codex OAuth token requests | MIT | https://github.com/TooTallNate/proxy-agents |
|
|
11
12
|
| tsdown | Development-time bundler | MIT | https://github.com/rolldown/tsdown |
|
|
12
13
|
|
|
13
14
|
No third-party project endorses this community plugin. See each installed package for its complete license text and transitive dependency notices.
|
package/lib/index.js
CHANGED
|
@@ -3,7 +3,10 @@ import { LlmError, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
|
3
3
|
import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
|
|
4
4
|
import { settingsNamespace } from "@deepseek-ai/dsh-settings";
|
|
5
5
|
import z from "@deepseek-ai/schemastery";
|
|
6
|
-
import { spawn } from "node:child_process";
|
|
6
|
+
import { execFile, spawn } from "node:child_process";
|
|
7
|
+
import { request } from "node:https";
|
|
8
|
+
import { promisify } from "node:util";
|
|
9
|
+
import { HttpsProxyAgent } from "https-proxy-agent";
|
|
7
10
|
import { openaiCodexProvider as createOpenAICodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
|
|
8
11
|
import { createModels } from "@earendil-works/pi-ai";
|
|
9
12
|
import { randomUUID } from "node:crypto";
|
|
@@ -104,7 +107,8 @@ var DshOAuthCredentialStore = class {
|
|
|
104
107
|
}
|
|
105
108
|
};
|
|
106
109
|
/** Return only account state that is safe to expose to the browser client. */
|
|
107
|
-
function createCodexAuthService(models, store) {
|
|
110
|
+
function createCodexAuthService(models, store, options = {}) {
|
|
111
|
+
const runLogin = options.runLogin ?? ((run) => run());
|
|
108
112
|
return Object.freeze({
|
|
109
113
|
async status(options) {
|
|
110
114
|
const current = await store.read(PROVIDER$1, options);
|
|
@@ -120,7 +124,7 @@ function createCodexAuthService(models, store) {
|
|
|
120
124
|
};
|
|
121
125
|
},
|
|
122
126
|
login(interaction) {
|
|
123
|
-
return models.login(PROVIDER$1, "oauth", interaction);
|
|
127
|
+
return runLogin(() => models.login(PROVIDER$1, "oauth", interaction));
|
|
124
128
|
},
|
|
125
129
|
logout(options) {
|
|
126
130
|
return models.logout(PROVIDER$1, options);
|
|
@@ -221,6 +225,7 @@ const publicPrompt = (prompt) => ({
|
|
|
221
225
|
function classifyLoginFailure(error) {
|
|
222
226
|
const message = error instanceof Error ? error.message : "";
|
|
223
227
|
if (/token exchange failed/iu.test(message)) return "token-exchange";
|
|
228
|
+
if (/fetch failed|\b(?:ECONN|ENOTFOUND|ETIMEDOUT|CERT_|socket|network)\b/iu.test(message)) return "network";
|
|
224
229
|
if (/extract accountId|account[_ -]?id/iu.test(message)) return "account-claim";
|
|
225
230
|
if (/credential|credentials-local|OAuth JSON/iu.test(message)) return "credential-store";
|
|
226
231
|
if (/Missing authorization code|State mismatch|callback/iu.test(message)) return "callback";
|
|
@@ -469,6 +474,158 @@ function createCodexRpcHandler(coordinator, options = {}) {
|
|
|
469
474
|
};
|
|
470
475
|
}
|
|
471
476
|
//#endregion
|
|
477
|
+
//#region src/oauth-network.js
|
|
478
|
+
const execFileAsync = promisify(execFile);
|
|
479
|
+
const CODEX_AUTH_HOST = "auth.openai.com";
|
|
480
|
+
const MAX_RESPONSE_BYTES = 2 * 1024 * 1024;
|
|
481
|
+
let patchQueue = Promise.resolve();
|
|
482
|
+
function normalizeProxy(raw) {
|
|
483
|
+
if (typeof raw !== "string" || raw.trim() === "") return void 0;
|
|
484
|
+
const value = raw.trim().includes("://") ? raw.trim() : `http://${raw.trim()}`;
|
|
485
|
+
try {
|
|
486
|
+
const url = new URL(value);
|
|
487
|
+
if (!["http:", "https:"].includes(url.protocol) || url.hostname === "") return void 0;
|
|
488
|
+
return url.toString();
|
|
489
|
+
} catch {
|
|
490
|
+
return;
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
function bypassesProxy(hostname, port, rawNoProxy) {
|
|
494
|
+
if (typeof rawNoProxy !== "string" || rawNoProxy.trim() === "") return false;
|
|
495
|
+
return rawNoProxy.split(/[\s,]+/u).some((raw) => {
|
|
496
|
+
const entry = raw.trim().toLowerCase();
|
|
497
|
+
if (entry === "*") return true;
|
|
498
|
+
if (entry === "") return false;
|
|
499
|
+
const match = /^(.*?)(?::(\d+))?$/u.exec(entry);
|
|
500
|
+
const host = match?.[1]?.replace(/^\./u, "");
|
|
501
|
+
const entryPort = match?.[2];
|
|
502
|
+
if (!host || entryPort && entryPort !== port) return false;
|
|
503
|
+
return hostname === host || hostname.endsWith(`.${host}`);
|
|
504
|
+
});
|
|
505
|
+
}
|
|
506
|
+
function proxyFromEnvironment(env = process.env, target = new URL(`https://${CODEX_AUTH_HOST}/`)) {
|
|
507
|
+
if (bypassesProxy(target.hostname.toLowerCase(), target.port || "443", env.NO_PROXY ?? env.no_proxy)) return void 0;
|
|
508
|
+
return normalizeProxy(env.HTTPS_PROXY ?? env.https_proxy ?? env.ALL_PROXY ?? env.all_proxy);
|
|
509
|
+
}
|
|
510
|
+
function selectWindowsProxy(value) {
|
|
511
|
+
if (typeof value !== "string") return void 0;
|
|
512
|
+
const entries = value.split(";").map((item) => item.trim()).filter(Boolean);
|
|
513
|
+
const https = entries.find((item) => /^https=/iu.test(item));
|
|
514
|
+
const http = entries.find((item) => /^http=/iu.test(item));
|
|
515
|
+
const selected = (https ?? http ?? entries.find((item) => !item.includes("=")))?.replace(/^[^=]+=/u, "");
|
|
516
|
+
return normalizeProxy(selected);
|
|
517
|
+
}
|
|
518
|
+
async function windowsSystemProxy(options = {}) {
|
|
519
|
+
const run = options.execFile ?? execFileAsync;
|
|
520
|
+
const reg = `${process.env.SystemRoot ?? "C:\\Windows"}\\System32\\reg.exe`;
|
|
521
|
+
const key = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings";
|
|
522
|
+
try {
|
|
523
|
+
const enabled = await run(reg, [
|
|
524
|
+
"query",
|
|
525
|
+
key,
|
|
526
|
+
"/v",
|
|
527
|
+
"ProxyEnable"
|
|
528
|
+
], {
|
|
529
|
+
windowsHide: true,
|
|
530
|
+
encoding: "utf8"
|
|
531
|
+
});
|
|
532
|
+
if (!/REG_DWORD\s+0x1\b/iu.test(enabled.stdout)) return void 0;
|
|
533
|
+
const configured = await run(reg, [
|
|
534
|
+
"query",
|
|
535
|
+
key,
|
|
536
|
+
"/v",
|
|
537
|
+
"ProxyServer"
|
|
538
|
+
], {
|
|
539
|
+
windowsHide: true,
|
|
540
|
+
encoding: "utf8"
|
|
541
|
+
});
|
|
542
|
+
return selectWindowsProxy(/^\s*ProxyServer\s+REG_\w+\s+(.+)$/imu.exec(configured.stdout)?.[1]);
|
|
543
|
+
} catch {
|
|
544
|
+
return;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
async function macSystemProxy(options = {}) {
|
|
548
|
+
const run = options.execFile ?? execFileAsync;
|
|
549
|
+
try {
|
|
550
|
+
const result = await run("/usr/sbin/scutil", ["--proxy"], { encoding: "utf8" });
|
|
551
|
+
if (!/^\s*HTTPSEnable\s*:\s*1\s*$/imu.test(result.stdout)) return void 0;
|
|
552
|
+
const host = /^\s*HTTPSProxy\s*:\s*(\S+)\s*$/imu.exec(result.stdout)?.[1];
|
|
553
|
+
const port = /^\s*HTTPSPort\s*:\s*(\d+)\s*$/imu.exec(result.stdout)?.[1];
|
|
554
|
+
return normalizeProxy(host && port ? `${host}:${port}` : void 0);
|
|
555
|
+
} catch {
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
async function resolveCodexOAuthProxy(options = {}) {
|
|
560
|
+
const envProxy = proxyFromEnvironment(options.env);
|
|
561
|
+
if (envProxy) return envProxy;
|
|
562
|
+
const platform = options.platform ?? process.platform;
|
|
563
|
+
if (platform === "win32") return windowsSystemProxy(options);
|
|
564
|
+
if (platform === "darwin") return macSystemProxy(options);
|
|
565
|
+
}
|
|
566
|
+
function bodyBytes(body) {
|
|
567
|
+
if (body === void 0 || body === null) return void 0;
|
|
568
|
+
if (typeof body === "string") return Buffer.from(body);
|
|
569
|
+
if (body instanceof URLSearchParams) return Buffer.from(body.toString());
|
|
570
|
+
if (body instanceof Uint8Array) return Buffer.from(body);
|
|
571
|
+
throw new TypeError("Unsupported Codex OAuth request body");
|
|
572
|
+
}
|
|
573
|
+
function fetchThroughProxy(input, init, proxyUrl) {
|
|
574
|
+
const target = new URL(typeof input === "string" || input instanceof URL ? input : input.url);
|
|
575
|
+
const body = bodyBytes(init?.body);
|
|
576
|
+
const headers = new Headers(init?.headers);
|
|
577
|
+
if (body && !headers.has("content-length")) headers.set("content-length", String(body.byteLength));
|
|
578
|
+
return new Promise((resolve, reject) => {
|
|
579
|
+
const request$1 = request(target, {
|
|
580
|
+
method: init?.method ?? "GET",
|
|
581
|
+
headers: Object.fromEntries(headers.entries()),
|
|
582
|
+
agent: new HttpsProxyAgent(proxyUrl),
|
|
583
|
+
signal: init?.signal
|
|
584
|
+
}, (response) => {
|
|
585
|
+
const chunks = [];
|
|
586
|
+
let total = 0;
|
|
587
|
+
response.on("data", (chunk) => {
|
|
588
|
+
total += chunk.length;
|
|
589
|
+
if (total > MAX_RESPONSE_BYTES) {
|
|
590
|
+
request$1.destroy(/* @__PURE__ */ new Error("Codex OAuth response exceeded the safety limit"));
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
chunks.push(chunk);
|
|
594
|
+
});
|
|
595
|
+
response.on("end", () => resolve(new Response(Buffer.concat(chunks), {
|
|
596
|
+
status: response.statusCode ?? 500,
|
|
597
|
+
statusText: response.statusMessage,
|
|
598
|
+
headers: response.headers
|
|
599
|
+
})));
|
|
600
|
+
});
|
|
601
|
+
request$1.on("error", reject);
|
|
602
|
+
if (body) request$1.write(body);
|
|
603
|
+
request$1.end();
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
async function withCodexOAuthNetwork(run, options = {}) {
|
|
607
|
+
const previous = patchQueue;
|
|
608
|
+
let release;
|
|
609
|
+
patchQueue = new Promise((resolve) => {
|
|
610
|
+
release = resolve;
|
|
611
|
+
});
|
|
612
|
+
await previous;
|
|
613
|
+
const proxyUrl = await resolveCodexOAuthProxy(options);
|
|
614
|
+
const original = globalThis.fetch;
|
|
615
|
+
const proxyFetch = options.fetchThroughProxy ?? fetchThroughProxy;
|
|
616
|
+
const wrapper = proxyUrl ? (input, init) => {
|
|
617
|
+
const target = new URL(typeof input === "string" || input instanceof URL ? input : input.url);
|
|
618
|
+
return target.protocol === "https:" && target.hostname === CODEX_AUTH_HOST ? proxyFetch(input, init, proxyUrl) : original(input, init);
|
|
619
|
+
} : original;
|
|
620
|
+
globalThis.fetch = wrapper;
|
|
621
|
+
try {
|
|
622
|
+
return await run();
|
|
623
|
+
} finally {
|
|
624
|
+
if (globalThis.fetch === wrapper) globalThis.fetch = original;
|
|
625
|
+
release();
|
|
626
|
+
}
|
|
627
|
+
}
|
|
628
|
+
//#endregion
|
|
472
629
|
//#region src/settings-contract.js
|
|
473
630
|
const SETTINGS_NAMESPACE = "codex-subscription";
|
|
474
631
|
const QUICK_QUOTA_MODE_FIELD = "quickQuotaMode";
|
|
@@ -544,7 +701,7 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0 } = {
|
|
|
544
701
|
}
|
|
545
702
|
//#endregion
|
|
546
703
|
//#region src/version.js
|
|
547
|
-
const PACKAGE_VERSION = "1.7.
|
|
704
|
+
const PACKAGE_VERSION = "1.7.4";
|
|
548
705
|
const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
|
|
549
706
|
//#endregion
|
|
550
707
|
//#region src/codex-search.js
|
|
@@ -1585,7 +1742,7 @@ function apply(ctx) {
|
|
|
1585
1742
|
select(settings.get());
|
|
1586
1743
|
return settings.watch(select);
|
|
1587
1744
|
}, "codex-subscription: search provider selection");
|
|
1588
|
-
const auth = createCodexAuthService(authModels, store);
|
|
1745
|
+
const auth = createCodexAuthService(authModels, store, { runLogin: withCodexOAuthNetwork });
|
|
1589
1746
|
const coordinator = new CodexLoginCoordinator(auth);
|
|
1590
1747
|
const usageReader = createCodexUsageReader({
|
|
1591
1748
|
getAuth: resolveAuth,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-codex-subscription",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.4",
|
|
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",
|
|
@@ -122,6 +122,9 @@
|
|
|
122
122
|
"react-dom": "18.3.1",
|
|
123
123
|
"tsdown": "0.22.2"
|
|
124
124
|
},
|
|
125
|
+
"dependencies": {
|
|
126
|
+
"https-proxy-agent": "7.0.6"
|
|
127
|
+
},
|
|
125
128
|
"scripts": {
|
|
126
129
|
"build": "tsdown --config tsdown.config.mjs",
|
|
127
130
|
"test": "node --test tests/*.test.mjs",
|