dsh-codex-share 0.1.1 → 0.3.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
@@ -4,13 +4,13 @@ A DeepSeek Harness LLM adapter that uses the same ChatGPT subscription login as
4
4
 
5
5
  ## Requirements
6
6
 
7
- - DeepSeek Harness `0.1.5-rc.3` or a compatible later release, with Node.js `^22.19.0 || >=24`.
7
+ - DeepSeek Harness `0.1.7-rc.2` or a compatible later release (0.1.5 releases are no longer supported), with Node.js `^22.19.0 || >=24`.
8
8
  - Codex CLI signed in with a ChatGPT account and configured for **file credential storage**. Run `codex login` and check `codex login status`. Codex must be able to read and update `$CODEX_HOME/auth.json` (default `~/.codex/auth.json`).
9
9
  - The harness process must run as the same OS user, or have read access to that same credential file. A keyring-only Codex login is not supported.
10
10
 
11
11
  ## Install
12
12
 
13
- Once published, install the bundle into a harness profile:
13
+ Install the bundle into a harness profile:
14
14
 
15
15
  ```sh
16
16
  dsh plugin --profile web add dsh-codex-share
@@ -22,7 +22,7 @@ For this checkout before publication, from its directory:
22
22
  dsh plugin --profile web add .
23
23
  ```
24
24
 
25
- Restart the profile after installation or update. The bundle mounts one provider route, `openai-codex`, with the model catalog supplied by pi-ai. The Host plugin appears in **Settings → Plugins → Plugin list** under the global entries; it has no separate Plugin configuration card. Select that provider and a listed model in the harness model settings 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.
25
+ Restart the profile after installation or update. The bundle mounts one provider route, `openai-codex-local`, with the model catalog supplied by pi-ai plus GPT-6 Sol (`gpt-6-sol`) and GPT-6 Luna (`gpt-6-luna`), whose metadata is taken from pi-ai 0.87 because the harness's pi-ai adapter still runs on pi-ai 0.85. 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.
26
26
 
27
27
  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`.
28
28
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
- import { Context } from "@deepseek-ai/cordis";
1
+ import z from "@deepseek-ai/schemastery";
2
+ import { Context, Volatile } from "@deepseek-ai/cordis";
2
3
  //#region src/auth.d.ts
3
4
  /** Resolve Codex's current auth.json path without retaining a token. */
4
5
  declare function codexAuthPath(codexHome?: string): string;
@@ -11,14 +12,19 @@ declare function readCodexAccessToken(codexHome?: string): Promise<string>;
11
12
  //#endregion
12
13
  //#region src/index.d.ts
13
14
  /** Harness route for subscription-backed Codex models. */
14
- declare const PROVIDER = "openai-codex";
15
+ declare const PROVIDER = "openai-codex-local";
15
16
  declare const name = "llm-codex-share";
16
17
  declare const inject: string[];
18
+ /** Settings namespace: the Loader entry id this bundle's patch inserts. */
19
+ declare const SETTINGS_NS = "llm-codex-share";
17
20
  /** Plugin configuration. Omit codexHome to follow CODEX_HOME or ~/.codex. */
18
21
  interface Config {
19
22
  codexHome?: string;
23
+ /** Extra subscription model IDs alongside the installed pi-ai catalog; edits apply without a restart. */
24
+ additionalModelIds: Volatile<string[]>;
20
25
  }
26
+ declare const Config: z<Partial<Record<keyof Config, unknown>>, Config>;
21
27
  /** Register the Codex subscription model catalog and stream adapter. */
22
28
  declare function apply(ctx: Context, config?: Config): void;
23
29
  //#endregion
24
- export { Config, PROVIDER, apply, codexAuthPath, inject, name, readCodexAccessToken };
30
+ export { Config, PROVIDER, SETTINGS_NS, apply, codexAuthPath, inject, name, readCodexAccessToken };
package/dist/index.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import z from "@deepseek-ai/schemastery";
1
2
  import { LlmError, resolveImageAttachmentAccess } from "@deepseek-ai/dsh-llm";
2
3
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
3
4
  import { envApiKeyAuth } from "@earendil-works/pi-ai";
@@ -38,19 +39,91 @@ async function readCodexAccessToken(codexHome) {
38
39
  //#endregion
39
40
  //#region src/index.ts
40
41
  /** Harness route for subscription-backed Codex models. */
41
- const PROVIDER = "openai-codex";
42
+ const PROVIDER = "openai-codex-local";
42
43
  const name = "llm-codex-share";
43
44
  const inject = ["llm"];
45
+ /** Settings namespace: the Loader entry id this bundle's patch inserts. */
46
+ const SETTINGS_NS = "llm-codex-share";
47
+ const Config = z.object({
48
+ codexHome: z.string(),
49
+ additionalModelIds: z.array(z.string().min(1)).default([]).volatile()
50
+ });
51
+ /**
52
+ * GPT-6 Sol and Luna as pi-ai 0.87 catalogs them. pi-ai 0.87 providers take a
53
+ * different stream context than the dsh 0.1.7 adapter (built on 0.85) sends,
54
+ * so the dependency stays on 0.85 and these entries are added on top of its
55
+ * GPT-6 Astra entry. An installed catalog that already lists an ID wins.
56
+ */
57
+ const GPT6_MODELS = [{
58
+ id: "gpt-6-sol",
59
+ name: "GPT-6 Sol",
60
+ cost: {
61
+ input: 2,
62
+ output: 10,
63
+ cacheRead: .2,
64
+ cacheWrite: 2.5,
65
+ tiers: [{
66
+ inputTokensAbove: 272e3,
67
+ input: 4,
68
+ output: 15,
69
+ cacheRead: .4,
70
+ cacheWrite: 5
71
+ }]
72
+ }
73
+ }, {
74
+ id: "gpt-6-luna",
75
+ name: "GPT-6 Luna",
76
+ cost: {
77
+ input: .1,
78
+ output: .5,
79
+ cacheRead: .01,
80
+ cacheWrite: .125,
81
+ tiers: [{
82
+ inputTokensAbove: 272e3,
83
+ input: .2,
84
+ output: .75,
85
+ cacheRead: .02,
86
+ cacheWrite: .25
87
+ }]
88
+ }
89
+ }];
90
+ const GPT6_THINKING = {
91
+ off: "none",
92
+ minimal: "low",
93
+ low: "low",
94
+ medium: "medium",
95
+ high: "high",
96
+ xhigh: "xhigh",
97
+ max: "max"
98
+ };
99
+ function validateModelIds(ids) {
100
+ if (new Set(ids).size !== ids.length || ids.some((id) => id.trim() !== id)) throw new TypeError("Additional Codex model IDs must be unique, non-empty, and trimmed");
101
+ }
44
102
  /** Register the Codex subscription model catalog and stream adapter. */
45
- function apply(ctx, config = {}) {
46
- const provider = {
47
- ...openaiCodexProvider(),
48
- auth: { apiKey: envApiKeyAuth("Codex CLI login", []) }
49
- };
50
- const profiles = /* @__PURE__ */ new Map([[PROVIDER, {
103
+ function apply(ctx, config = Config({})) {
104
+ validateModelIds(config.additionalModelIds.get());
105
+ ctx.on("internal/config", function(_raw, next) {
106
+ const raw = next();
107
+ if (this === ctx.fiber) validateModelIds(Config(raw).additionalModelIds.get());
108
+ return raw;
109
+ });
110
+ let lastIds;
111
+ let profiles;
112
+ const upstream = openaiCodexProvider();
113
+ const installed = upstream.getModels();
114
+ const astra = installed.find((model) => model.id === "gpt-6-astra");
115
+ const catalog = astra === void 0 ? installed : [...installed, ...GPT6_MODELS.filter((extra) => !installed.some((model) => model.id === extra.id)).map((extra) => ({
116
+ ...astra,
117
+ ...extra,
118
+ thinkingLevelMap: GPT6_THINKING,
119
+ cost: {
120
+ ...extra.cost,
121
+ tiers: [...extra.cost.tiers]
122
+ }
123
+ }))];
124
+ const profileBase = {
51
125
  provider: PROVIDER,
52
126
  displayName: "OpenAI Codex (local login)",
53
- piProvider: provider,
54
127
  modelErrors: /* @__PURE__ */ new Map(),
55
128
  configuredMaxTokens: /* @__PURE__ */ new Map(),
56
129
  streamIdleTimeoutMs: 12e4,
@@ -66,9 +139,37 @@ function apply(ctx, config = {}) {
66
139
  maxDelayMs: 0,
67
140
  jitterRatio: 0
68
141
  }
69
- }]]);
142
+ };
143
+ const resolveProfiles = () => {
144
+ const configured = config.additionalModelIds.get();
145
+ if (configured === lastIds && profiles !== void 0) return profiles;
146
+ const ids = configured.filter((id) => !catalog.some((model) => model.id === id));
147
+ const models = [...catalog, ...ids.map((id) => {
148
+ const family = id.endsWith("-luna") ? "gpt-5.6-luna" : id.endsWith("-sol") ? "gpt-5.6-sol" : "gpt-6-astra";
149
+ return {
150
+ ...catalog.find((model) => model.id === family) ?? catalog[0],
151
+ id,
152
+ name: id
153
+ };
154
+ })].map((model) => ({
155
+ ...model,
156
+ provider: PROVIDER
157
+ }));
158
+ const provider = {
159
+ ...upstream,
160
+ id: PROVIDER,
161
+ auth: { apiKey: envApiKeyAuth("Codex CLI login", []) },
162
+ getModels: () => models
163
+ };
164
+ profiles = /* @__PURE__ */ new Map([[PROVIDER, {
165
+ ...profileBase,
166
+ piProvider: provider
167
+ }]]);
168
+ lastIds = configured;
169
+ return profiles;
170
+ };
70
171
  const adapter = new PiAiAdapter({
71
- profiles: () => profiles,
172
+ profiles: resolveProfiles,
72
173
  resolveApiKey: async () => readCodexAccessToken(config.codexHome),
73
174
  resolveAttachments: () => ctx.get("attachments"),
74
175
  resolveImageAccess: (attachments, ref) => resolveImageAttachmentAccess(attachments, (hostPath) => ctx.get("fs")?.processPathFromHostPath(hostPath), ref),
@@ -89,7 +190,13 @@ function apply(ctx, config = {}) {
89
190
  }
90
191
  }
91
192
  });
193
+ ctx.llm.registerConfigurableProviders([{
194
+ provider: PROVIDER,
195
+ displayName: "OpenAI Codex (local login)",
196
+ settingsNs: SETTINGS_NS,
197
+ settingsPath: []
198
+ }]);
92
199
  ctx.llm.registerAdapter([PROVIDER], adapter);
93
200
  }
94
201
  //#endregion
95
- export { PROVIDER, apply, codexAuthPath, inject, name, readCodexAccessToken };
202
+ export { Config, PROVIDER, SETTINGS_NS, apply, codexAuthPath, inject, name, readCodexAccessToken };
package/lib/client.js ADDED
@@ -0,0 +1,113 @@
1
+ window.__ModuleLoader__.load({ id: 'dsh-codex-share', factory: (require) => {
2
+ const module = { exports: {} }; const exports = module.exports;
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
4
+ let react = require("react");
5
+ //#region src/client/index.ts
6
+ const SETTINGS_NS = "llm-codex-share";
7
+ function CodexModelsCard(props) {
8
+ const settings = props.useSettings((snapshot) => snapshot);
9
+ const configured = settings.value?.additionalModelIds ?? [];
10
+ const [draft, setDraft] = (0, react.useState)(() => configured.join("\n"));
11
+ const [editing, setEditing] = (0, react.useState)(false);
12
+ const [busy, setBusy] = (0, react.useState)(false);
13
+ const [message, setMessage] = (0, react.useState)();
14
+ (0, react.useEffect)(() => {
15
+ if (!editing) setDraft(configured.join("\n"));
16
+ }, [settings.revision, editing]);
17
+ const submit = async () => {
18
+ const ids = draft.split(/\r?\n/).map((id) => id.trim()).filter(Boolean);
19
+ if (new Set(ids).size !== ids.length) {
20
+ setMessage("Each model ID must appear only once.");
21
+ return;
22
+ }
23
+ if (settings.revision === void 0) return;
24
+ setBusy(true);
25
+ setMessage(void 0);
26
+ try {
27
+ if (await props.save(ids, settings.revision)) {
28
+ setEditing(false);
29
+ setMessage("Codex model IDs saved.");
30
+ } else setMessage("The harness refused these model IDs; the saved list is unchanged.");
31
+ } catch (error) {
32
+ setMessage(error instanceof Error ? error.message : String(error));
33
+ } finally {
34
+ setBusy(false);
35
+ }
36
+ };
37
+ const children = editing ? [
38
+ (0, react.createElement)("label", {
39
+ key: "label",
40
+ htmlFor: "codex-share-model-ids"
41
+ }, "Additional model IDs (one per line)"),
42
+ (0, react.createElement)("textarea", {
43
+ key: "input",
44
+ id: "codex-share-model-ids",
45
+ rows: 4,
46
+ value: draft,
47
+ placeholder: "gpt-6-sol\ngpt-6-luna",
48
+ disabled: busy || !settings.writable,
49
+ onChange: (event) => setDraft(event.target.value),
50
+ style: {
51
+ display: "block",
52
+ width: "100%",
53
+ boxSizing: "border-box",
54
+ margin: "8px 0"
55
+ }
56
+ }),
57
+ (0, react.createElement)("p", { key: "hint" }, "These IDs are added to the installed Codex catalog. The subscription endpoint must accept each ID."),
58
+ (0, react.createElement)("button", {
59
+ key: "save",
60
+ type: "button",
61
+ disabled: busy || !settings.writable,
62
+ onClick: () => {
63
+ submit();
64
+ }
65
+ }, busy ? "Saving…" : "Save model IDs"),
66
+ (0, react.createElement)("button", {
67
+ key: "cancel",
68
+ type: "button",
69
+ disabled: busy,
70
+ onClick: () => {
71
+ setEditing(false);
72
+ setMessage(void 0);
73
+ },
74
+ style: { marginLeft: 8 }
75
+ }, "Cancel")
76
+ ] : [(0, react.createElement)("span", { key: "list" }, configured.length > 0 ? `Additional models: ${configured.join(", ")}` : "Using installed Codex model catalog"), (0, react.createElement)("button", {
77
+ key: "edit",
78
+ type: "button",
79
+ disabled: !settings.writable,
80
+ onClick: () => {
81
+ setDraft(configured.join("\n"));
82
+ setMessage(void 0);
83
+ setEditing(true);
84
+ },
85
+ style: { marginLeft: 8 }
86
+ }, "Edit model IDs")];
87
+ return (0, react.createElement)("div", { style: { padding: "8px 0" } }, ...children, message === void 0 ? null : (0, react.createElement)("p", { role: "status" }, message));
88
+ }
89
+ const inject = ["slots", "configForms"];
90
+ function apply(ctx) {
91
+ const form = ctx.configForms.get(SETTINGS_NS);
92
+ ctx.slots.inject("settings.models.provider-card", () => ctx.slots.register({
93
+ name: "settings.models.provider-card",
94
+ key: SETTINGS_NS,
95
+ inject: () => ({
96
+ hooks: { settings: form },
97
+ save: (ids, revision) => form.mutate([ids.length === 0 ? {
98
+ op: "unset",
99
+ path: ["additionalModelIds"]
100
+ } : {
101
+ op: "set",
102
+ path: ["additionalModelIds"],
103
+ value: ids
104
+ }], revision)
105
+ })
106
+ }, CodexModelsCard));
107
+ }
108
+ //#endregion
109
+ exports.apply = apply;
110
+ exports.inject = inject;
111
+
112
+ return module.exports;
113
+ } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-codex-share",
3
- "version": "0.1.1",
3
+ "version": "0.3.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",
@@ -11,11 +11,13 @@
11
11
  "types": "./dist/index.d.mts",
12
12
  "default": "./dist/index.mjs"
13
13
  },
14
+ "./client": "./lib/client.js",
14
15
  "./cordis.patch.yml": "./cordis.patch.yml",
15
16
  "./package.json": "./package.json"
16
17
  },
17
18
  "files": [
18
19
  "dist",
20
+ "lib/client.js",
19
21
  "cordis.patch.yml",
20
22
  "README.md",
21
23
  "LICENSE"
@@ -25,34 +27,51 @@
25
27
  },
26
28
  "packageManager": "pnpm@11.7.0",
27
29
  "scripts": {
28
- "build": "tsdown src/index.ts --dts --platform node --format esm",
30
+ "build": "tsdown src/index.ts --dts --platform node --format esm && pnpm build:client",
31
+ "build:client": "tsdown src/client/index.ts --platform browser --format cjs --out-dir lib --no-clean --external react --external @deepseek-ai/cordis --external @deepseek-ai/dsh-client-ui-slots && node scripts/wrap-client.mjs",
29
32
  "typecheck": "tsc --noEmit",
30
33
  "test": "vitest run",
31
34
  "prepack": "pnpm build"
32
35
  },
33
36
  "peerDependencies": {
34
37
  "@deepseek-ai/cordis": "^4.0.2",
35
- "@deepseek-ai/dsh-llm": "^0.1.5-rc.3",
36
- "@deepseek-ai/dsh-attachment": "^0.1.5-rc.3",
37
- "@deepseek-ai/dsh-fs": "^0.1.5-rc.3",
38
- "@deepseek-ai/dsh-llm-pi-ai": "^0.1.5-rc.3"
38
+ "@deepseek-ai/dsh-attachment": "^0.1.7-rc.2",
39
+ "@deepseek-ai/dsh-fs": "^0.1.7-rc.2",
40
+ "@deepseek-ai/dsh-llm": "^0.1.7-rc.2",
41
+ "@deepseek-ai/dsh-llm-pi-ai": "^0.1.7-rc.2",
42
+ "@deepseek-ai/schemastery": "^3.18.2"
39
43
  },
40
44
  "dependencies": {
41
45
  "@earendil-works/pi-ai": "^0.85.1"
42
46
  },
43
47
  "devDependencies": {
44
48
  "@deepseek-ai/cordis": "link:../deepseek-harness/vendor/cordis",
45
- "@deepseek-ai/dsh-llm": "link:../deepseek-harness/packages/llm/llm",
49
+ "@deepseek-ai/dsh-api-remotes": "link:../deepseek-harness/packages/api/remotes",
50
+ "@deepseek-ai/dsh-app-boot": "link:../deepseek-harness/packages/boot/app-boot",
46
51
  "@deepseek-ai/dsh-attachment": "link:../deepseek-harness/packages/attachment/attachment",
52
+ "@deepseek-ai/dsh-client-ui-renderer": "link:../deepseek-harness/packages/client/ui-renderer",
53
+ "@deepseek-ai/dsh-client-ui-settings": "link:../deepseek-harness/packages/client/ui-settings",
54
+ "@deepseek-ai/dsh-client-ui-settings-models": "link:../deepseek-harness/packages/client/ui-settings-models",
55
+ "@deepseek-ai/dsh-client-ui-slots": "link:../deepseek-harness/packages/client/ui-slots",
47
56
  "@deepseek-ai/dsh-fs": "link:../deepseek-harness/packages/fs/fs",
57
+ "@deepseek-ai/dsh-llm": "link:../deepseek-harness/packages/llm/llm",
48
58
  "@deepseek-ai/dsh-llm-pi-ai": "link:../deepseek-harness/packages/llm/llm-pi-ai",
59
+ "@deepseek-ai/dsh-settings": "link:../deepseek-harness/packages/settings/settings",
60
+ "@deepseek-ai/schemastery": "link:../deepseek-harness/vendor/schemastery",
49
61
  "@types/node": "^22.20.0",
62
+ "@types/react": "~18.3.31",
63
+ "react": "^18.3.1",
50
64
  "tsdown": "^0.22.2",
51
65
  "typescript": "^6.0.3",
52
- "vitest": "^4.1.8",
53
- "@deepseek-ai/dsh-app-boot": "link:../deepseek-harness/packages/boot/app-boot"
66
+ "vitest": "^4.1.8"
54
67
  },
55
68
  "dsh": {
69
+ "client": {
70
+ "platform": "web",
71
+ "inject": [
72
+ "@deepseek-ai/dsh-client-ui-settings-models"
73
+ ]
74
+ },
56
75
  "bundle": {
57
76
  "patch": "./cordis.patch.yml"
58
77
  }