dsh-codex-share 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,7 +24,7 @@ For development from this checkout, from its directory:
24
24
  dsh plugin --profile web add .
25
25
  ```
26
26
 
27
- Restart the profile after installation or update. The bundle mounts one provider route, `openai-codex-local`, with the model catalog supplied by the Harness's pi-ai 1.x, including GPT-6 Astra, Sol, and Luna, and GPT-6.1 Sol (`gpt-6.1-sol`). GPT-6.1 Luna is not listed in the current OpenAI or Codex catalogs; use `gpt-6-luna` for Luna. This route replaces the plugin's earlier `openai-codex` route, which collides with pi-ai's configurable provider directory; update any saved model selection to `openai-codex-local`. The plugin appears in **Settings → Plugins → Plugin list**. Open **Settings → Models** and use **Edit model IDs** in the OpenAI Codex row to add subscription model IDs (one per line). The IDs are saved as `additionalModelIds` on the `llm-codex-share` entry in the profile's `cordis.patch.yml` and take effect without a restart. The installed catalog remains available; clearing the additional IDs restores it. For example, enter a model ID that your account can use but the catalog does not list yet. An unrecognized ID can be listed, but requests to it will fail if the Codex endpoint rejects it. Select a listed model in the model picker or agent preset. For a local Codex home outside the default location, set the absolute `CODEX_HOME` path in the environment that launches the harness. Direct Cordis composition can configure `codexHome` on the `dsh-codex-share` plugin instead.
27
+ Restart the profile after installation or update. The bundle discovers home directories whose names start with `.codex` and contain an `auth.json` file (including symlinked directories). Each gets a separate provider group labeled with its directory name, such as **OpenAI Codex (.codex)** and **OpenAI Codex (.codex-p)**, in the model picker. The primary account retains `openai-codex-local`; other accounts use stable routes such as `openai-codex-local-.codex-p`. Each route offers the same catalog and additional model IDs, and reads only that account’s current credentials for each request. Invalid logins never fall back to another account. Discovery runs when the plugin starts; restart the profile after adding or removing account directories. Directory labels distinguish homes without exposing account email addresses. The primary route comes with the model catalog supplied by the Harness's pi-ai 1.x, including GPT-6 Astra, Sol, and Luna, and GPT-6.1 Sol (`gpt-6.1-sol`). GPT-6.1 Luna is not listed in the current OpenAI or Codex catalogs; use `gpt-6-luna` for Luna. This route replaces the plugin's earlier `openai-codex` route, which collides with pi-ai's configurable provider directory; update any saved model selection to `openai-codex-local`. The plugin appears in **Settings → Plugins → Plugin list**. Open **Settings → Models** and use **Edit model IDs** in the OpenAI Codex row to add subscription model IDs (one per line). The IDs are saved as `additionalModelIds` on the `llm-codex-share` entry in the profile's `cordis.patch.yml` and take effect without a restart. The installed catalog remains available; clearing the additional IDs restores it. For example, enter a model ID that your account can use but the catalog does not list yet. An unrecognized ID can be listed, but requests to it will fail if the Codex endpoint rejects it. Select a listed model in the model picker or agent preset. To select the primary Codex home (while still discovering other home directories), set the absolute `CODEX_HOME` path in the environment that launches the harness. Direct Cordis composition can configure `codexHome` on the `dsh-codex-share` plugin instead.
28
28
 
29
29
  The plugin intentionally does not refresh a stale token. When Codex writes a replacement, the next harness request reads it. When Codex's current access token has expired, harness calls fail until Codex refreshes its own login. An API-key-only Codex login is rejected; this route never falls back to `OPENAI_API_KEY`.
30
30
 
package/dist/index.d.mts CHANGED
@@ -19,7 +19,7 @@ declare const name = "llm-codex-share";
19
19
  declare const inject: string[];
20
20
  /** Settings namespace: the Loader entry id this bundle's patch inserts. */
21
21
  declare const SETTINGS_NS = "llm-codex-share";
22
- /** Plugin configuration. Omit codexHome to follow CODEX_HOME or ~/.codex. */
22
+ /** codexHome/CODEX_HOME selects the primary route; other ~/.codex* homes are discovered. */
23
23
  interface Config {
24
24
  codexHome?: string;
25
25
  /** Extra subscription model IDs alongside the installed pi-ai catalog; edits apply without a restart. */
package/dist/index.mjs CHANGED
@@ -5,9 +5,10 @@ import { envApiKeyAuth } from "@earendil-works/pi-ai";
5
5
  import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
6
6
  import { zaiProvider } from "@earendil-works/pi-ai/providers/zai";
7
7
  import { launchEnvironmentOf } from "@deepseek-ai/dsh-launch-environment";
8
+ import { readdirSync, statSync } from "node:fs";
8
9
  import { readFile } from "node:fs/promises";
9
10
  import { homedir } from "node:os";
10
- import { isAbsolute, join } from "node:path";
11
+ import { basename, dirname, isAbsolute, join } from "node:path";
11
12
  //#region src/auth.ts
12
13
  /** Read the exact ChatGPT access token currently stored by Codex CLI. */
13
14
  /** Resolve Codex's current auth.json path without retaining a token. */
@@ -38,6 +39,24 @@ async function readCodexAccessToken(codexHome) {
38
39
  if (auth === null || auth.auth_mode !== void 0 && auth.auth_mode !== "chatgpt" || typeof auth.OPENAI_API_KEY === "string" && auth.OPENAI_API_KEY.length > 0 || typeof auth.tokens?.access_token !== "string" || auth.tokens.access_token.length === 0) throw new LlmError(`Codex login at ${path} has no ChatGPT access token; run codex login with ChatGPT`, "MISSING_CREDENTIAL");
39
40
  return auth.tokens.access_token;
40
41
  }
42
+ /** Discover account homes at plugin startup; retain only paths, never credentials. */
43
+ function discoverCodexHomes(codexHome) {
44
+ const primary = dirname(codexAuthPath(codexHome));
45
+ const homes = /* @__PURE__ */ new Set([primary]);
46
+ try {
47
+ for (const entry of readdirSync(homedir()).sort()) {
48
+ if (!entry.startsWith(".codex")) continue;
49
+ const home = join(homedir(), entry);
50
+ try {
51
+ if (statSync(home).isDirectory() && statSync(join(home, "auth.json")).isFile()) homes.add(home);
52
+ } catch {}
53
+ }
54
+ } catch {}
55
+ return [...homes].map((home) => ({
56
+ home,
57
+ label: basename(home)
58
+ }));
59
+ }
41
60
  //#endregion
42
61
  //#region src/index.ts
43
62
  /** Harness route for subscription-backed Codex models. */
@@ -84,6 +103,12 @@ function apply(ctx, config = Config({})) {
84
103
  if (this === ctx.fiber) validateModelIds(Config(raw).additionalModelIds.get());
85
104
  return raw;
86
105
  });
106
+ const accounts = discoverCodexHomes(config.codexHome).map((account, index) => ({
107
+ ...account,
108
+ provider: index === 0 ? PROVIDER : `${PROVIDER}-${encodeURIComponent(account.label)}`,
109
+ displayName: `OpenAI Codex (${account.label})`
110
+ }));
111
+ const accountHomes = new Map(accounts.map((account) => [account.provider, account.home]));
87
112
  let lastIds;
88
113
  let profiles;
89
114
  const upstream = openaiCodexProvider();
@@ -158,17 +183,18 @@ function apply(ctx, config = Config({})) {
158
183
  name: id
159
184
  };
160
185
  })];
161
- const provider = routeProvider(upstream, PROVIDER, upstream.name, "Codex CLI login", models);
162
- profiles = new Map([[PROVIDER, {
186
+ profiles = new Map([...accounts.map((account) => [account.provider, {
163
187
  ...profileBase,
164
- piProvider: provider
165
- }], ...glmProfile === void 0 ? [] : [[GLM_PROVIDER, glmProfile]]]);
188
+ provider: account.provider,
189
+ displayName: account.displayName,
190
+ piProvider: routeProvider(upstream, account.provider, account.displayName, "Codex CLI login", models)
191
+ }]), ...glmProfile === void 0 ? [] : [[GLM_PROVIDER, glmProfile]]]);
166
192
  lastIds = configured;
167
193
  return profiles;
168
194
  };
169
195
  const adapter = new PiAiAdapter({
170
196
  profiles: resolveProfiles,
171
- resolveApiKey: async (provider) => provider === "glm-coding-global" ? glmKey : readCodexAccessToken(config.codexHome),
197
+ resolveApiKey: async (provider) => provider === "glm-coding-global" ? glmKey : accountHomes.has(provider) ? readCodexAccessToken(accountHomes.get(provider)) : Promise.reject(new LlmError("Unknown Codex account route", "NO_ADAPTER")),
172
198
  resolveAttachments: () => ctx.get("attachments"),
173
199
  resolveImageAccess: (attachments, ref) => resolveImageAttachmentAccess(attachments, (hostPath) => ctx.get("fs")?.processPathFromHostPath(hostPath), ref),
174
200
  auth: {
@@ -188,13 +214,13 @@ function apply(ctx, config = Config({})) {
188
214
  }
189
215
  }
190
216
  });
191
- ctx.llm.registerConfigurableProviders([{
192
- provider: PROVIDER,
193
- displayName: "OpenAI Codex (local login)",
217
+ ctx.llm.registerConfigurableProviders([...accounts.map(({ provider, displayName }) => ({
218
+ provider,
219
+ displayName,
194
220
  settingsNs: SETTINGS_NS,
195
221
  settingsPath: []
196
- }]);
197
- ctx.llm.registerAdapter(glmProfile === void 0 ? [PROVIDER] : [PROVIDER, GLM_PROVIDER], adapter);
222
+ }))]);
223
+ ctx.llm.registerAdapter([...accountHomes.keys(), ...glmProfile === void 0 ? [] : [GLM_PROVIDER]], adapter);
198
224
  }
199
225
  //#endregion
200
226
  export { Config, GLM_PROVIDER, PROVIDER, SETTINGS_NS, apply, codexAuthPath, inject, name, readCodexAccessToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-share",
3
- "version": "0.5.2",
3
+ "version": "0.6.0",
4
4
  "description": "Use the local Codex CLI ChatGPT subscription login as a DeepSeek Harness LLM provider",
5
5
  "type": "module",
6
6
  "license": "MIT",