@tnnevol/dsh-codex-auth 0.1.0-rc.7 → 0.1.0-rc.7.2

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
@@ -2,8 +2,6 @@
2
2
 
3
3
  一个独立的 DeepSeek Harness Codex OAuth 插件,适配 DSH `0.1.0-rc.7`。
4
4
 
5
- 作者:tnnevol
6
-
7
5
  ## 功能
8
6
 
9
7
  - 在 DSH Web 的“插件”设置中提供 ChatGPT 登录和退出登录入口。
@@ -6,5 +6,7 @@ declare const CODEX_AUTH_STATUS_PATH = "/plugins/dsh-codex-auth-plugin/auth/stat
6
6
  declare const CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-codex-auth-plugin/auth/login";
7
7
  declare const CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-codex-auth-plugin/auth/logout";
8
8
  declare const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
9
+ /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
10
+ declare const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
9
11
  //#endregion
10
- export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
12
+ export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
package/lib/auth-paths.js CHANGED
@@ -6,5 +6,7 @@ const CODEX_AUTH_STATUS_PATH = "/plugins/dsh-codex-auth-plugin/auth/status";
6
6
  const CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-codex-auth-plugin/auth/login";
7
7
  const CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-codex-auth-plugin/auth/logout";
8
8
  const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
9
+ /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
10
+ const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
9
11
  //#endregion
10
- export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
12
+ export { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH };
package/lib/client.js CHANGED
@@ -300,6 +300,8 @@ window.__ModuleLoader__.load({
300
300
  const CODEX_AUTH_LOGIN_PATH = "/plugins/dsh-codex-auth-plugin/auth/login";
301
301
  const CODEX_AUTH_LOGOUT_PATH = "/plugins/dsh-codex-auth-plugin/auth/logout";
302
302
  const CODEX_USAGE_PATH = "/plugins/dsh-codex-auth-plugin/auth/usage";
303
+ /** Same-origin settings endpoint used when DSH marks a NAS browser as remote. */
304
+ const CODEX_AUTH_SETTINGS_PATH = "/plugins/dsh-codex-auth-plugin/auth/settings";
303
305
  //#endregion
304
306
  //#region src/client/CodexAuthCard.tsx
305
307
  /** Expandable account card for the DSH Plugins settings section. */
@@ -1515,6 +1517,116 @@ window.__ModuleLoader__.load({
1515
1517
  };
1516
1518
  }
1517
1519
  //#endregion
1520
+ //#region src/client/remote-settings-scope.ts
1521
+ const INITIAL_SNAPSHOT = {
1522
+ status: "loading",
1523
+ value: void 0,
1524
+ base: void 0,
1525
+ user: void 0,
1526
+ revision: void 0,
1527
+ writable: false,
1528
+ mode: "host"
1529
+ };
1530
+ async function requestSettings(method, value) {
1531
+ const response = await fetch(CODEX_AUTH_SETTINGS_PATH, {
1532
+ method,
1533
+ headers: {
1534
+ accept: "application/json",
1535
+ ...value === void 0 ? {} : { "content-type": "application/json" }
1536
+ },
1537
+ ...value === void 0 ? {} : { body: JSON.stringify(value) },
1538
+ credentials: "same-origin"
1539
+ });
1540
+ const payload = await response.json().catch(() => void 0);
1541
+ if (!response.ok) {
1542
+ const error = typeof payload === "object" && payload !== null && "error" in payload && typeof payload.error === "string" ? payload.error : `HTTP ${String(response.status)}`;
1543
+ throw new Error(error);
1544
+ }
1545
+ const settings = decodeCodexAuthSettings(payload);
1546
+ if (settings === void 0) throw new Error("Host returned invalid Codex settings");
1547
+ return settings;
1548
+ }
1549
+ /**
1550
+ * The official settings RPC is intentionally unavailable to non-loopback
1551
+ * browser authorities. This small scope talks only to the plugin-owned,
1552
+ * same-origin endpoint and carries no settings schema or credential data.
1553
+ */
1554
+ var CodexAuthRemoteSettingsScope = class {
1555
+ snapshot = INITIAL_SNAPSHOT;
1556
+ listeners = /* @__PURE__ */ new Set();
1557
+ tail = Promise.resolve();
1558
+ disposed = false;
1559
+ getSnapshot() {
1560
+ return this.snapshot;
1561
+ }
1562
+ subscribe(listener) {
1563
+ this.listeners.add(listener);
1564
+ return () => {
1565
+ this.listeners.delete(listener);
1566
+ };
1567
+ }
1568
+ load() {
1569
+ return this.enqueue(async () => {
1570
+ try {
1571
+ const settings = await requestSettings("GET");
1572
+ if (this.disposed) return;
1573
+ this.publish({
1574
+ ...this.snapshot,
1575
+ status: "ready",
1576
+ value: settings,
1577
+ writable: true
1578
+ });
1579
+ } catch {
1580
+ if (this.disposed) return;
1581
+ this.publish({
1582
+ ...this.snapshot,
1583
+ status: "unavailable",
1584
+ writable: false
1585
+ });
1586
+ }
1587
+ });
1588
+ }
1589
+ set(field, value) {
1590
+ if (field !== "enableImageTool" && field !== "enableImageUpload") return Promise.reject(/* @__PURE__ */ new Error(`Unsupported Codex settings field: ${field}`));
1591
+ if (typeof value !== "boolean") return Promise.reject(/* @__PURE__ */ new TypeError(`Codex setting ${field} must be boolean`));
1592
+ return this.enqueue(async () => {
1593
+ const current = this.getSnapshot().value;
1594
+ if (current === void 0) throw new Error("Codex settings are not loaded");
1595
+ const accepted = await requestSettings("PUT", {
1596
+ ...current,
1597
+ [field]: value
1598
+ });
1599
+ if (this.disposed) return;
1600
+ this.publish({
1601
+ ...this.snapshot,
1602
+ status: "ready",
1603
+ value: accepted,
1604
+ writable: true
1605
+ });
1606
+ });
1607
+ }
1608
+ unset(field) {
1609
+ return this.set(field, false);
1610
+ }
1611
+ async dispose() {
1612
+ this.disposed = true;
1613
+ await this.tail;
1614
+ }
1615
+ enqueue(operation) {
1616
+ if (this.disposed) return Promise.resolve();
1617
+ const task = this.tail.then(async () => {
1618
+ if (this.disposed) return;
1619
+ await operation();
1620
+ });
1621
+ this.tail = task.catch(() => void 0);
1622
+ return task;
1623
+ }
1624
+ publish(next) {
1625
+ this.snapshot = next;
1626
+ for (const listener of [...this.listeners]) listener();
1627
+ }
1628
+ };
1629
+ //#endregion
1518
1630
  //#region src/client/locales.ts
1519
1631
  /** Browser copy for the standalone Codex authentication card. */
1520
1632
  const en = {
@@ -1639,10 +1751,17 @@ window.__ModuleLoader__.load({
1639
1751
  en
1640
1752
  }), "dsh-codex-auth-plugin: locale");
1641
1753
  const t = ctx.locale.bind(namespace);
1642
- const configScope = ctx.settingsScope.bind({
1643
- namespace: CODEX_AUTH_SETTINGS_NAMESPACE,
1754
+ const remoteScope = ctx.get("connection").isLoopback ? void 0 : new CodexAuthRemoteSettingsScope();
1755
+ const configScope = remoteScope ?? ctx.settingsScope.bind({
1756
+ namespace: "dsh-codex-auth",
1644
1757
  decode: decodeCodexAuthSettings
1645
1758
  });
1759
+ if (remoteScope !== void 0) ctx.effect(() => {
1760
+ remoteScope.load();
1761
+ return async () => {
1762
+ await remoteScope.dispose();
1763
+ };
1764
+ }, "dsh-codex-auth-plugin: remote settings scope");
1646
1765
  ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1647
1766
  name: "settings.plugin.item",
1648
1767
  key: CODEX_AUTH_SETTINGS_NAMESPACE,
package/lib/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH } from "./auth-paths.js";
2
2
  import { a as CODEX_AUTH_FILENAME, c as codexAuthPath, i as logoutCodex, n as codexAuthStatus, o as CODEX_PROVIDER, r as loginCodex, s as CodexCredentialStore, t as CodexAuthStatus } from "./auth-BBBeI24-.js";
3
3
  import { CredentialProvider } from "@deepseek-ai/dsh-credentials";
4
+ import "@deepseek-ai/dsh-settings";
4
5
  import { AttachmentStore } from "@deepseek-ai/dsh-attachment";
5
6
  import { PiAiAdapter } from "@deepseek-ai/dsh-llm-pi-ai";
6
7
  import { Context } from "@deepseek-ai/cordis";
package/lib/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as CODEX_PROVIDER, i as CODEX_AUTH_FILENAME, n as loginCodex, o as CodexCredentialStore, r as logoutCodex, s as codexAuthPath, t as codexAuthStatus } from "./auth-DQ2F4vc6.js";
2
- import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH } from "./auth-paths.js";
2
+ import { CODEX_AUTH_LOGIN_PATH, CODEX_AUTH_LOGOUT_PATH, CODEX_AUTH_SETTINGS_NAMESPACE, CODEX_AUTH_SETTINGS_PATH, CODEX_AUTH_STATUS_PATH, CODEX_USAGE_PATH } from "./auth-paths.js";
3
3
  import { createModels } from "@earendil-works/pi-ai";
4
4
  import { openaiCodexProvider } from "@earendil-works/pi-ai/providers/openai-codex";
5
5
  import { basename } from "node:path";
@@ -136,10 +136,29 @@ var CodexUsageService = class {
136
136
  }
137
137
  };
138
138
  //#endregion
139
+ //#region src/settings-contract.ts
140
+ const DEFAULT_CODEX_AUTH_SETTINGS = Object.freeze({
141
+ enableImageTool: false,
142
+ enableImageUpload: false
143
+ });
144
+ function isRecord(value) {
145
+ return typeof value === "object" && value !== null && !Array.isArray(value);
146
+ }
147
+ /** Narrow the settings wire payload before it enters React state. */
148
+ function decodeCodexAuthSettings(value) {
149
+ if (!isRecord(value) || typeof value["enableImageTool"] !== "boolean") return void 0;
150
+ return {
151
+ enableImageTool: value["enableImageTool"],
152
+ enableImageUpload: typeof value["enableImageUpload"] === "boolean" ? value["enableImageUpload"] : DEFAULT_CODEX_AUTH_SETTINGS.enableImageUpload
153
+ };
154
+ }
155
+ //#endregion
139
156
  //#region src/auth-routes.ts
140
157
  const CODEX_AUTH_URL_TIMEOUT_MS = 3e4;
141
158
  const REMOTE_WEB_ORIGIN_NOT_TRUSTED = "remote-web-origin-not-trusted";
142
159
  const CODEX_USAGE_UNAVAILABLE = "codex-usage-unavailable";
160
+ const CODEX_SETTINGS_INVALID = "codex-settings-invalid";
161
+ const CODEX_SETTINGS_BODY_LIMIT = 8192;
143
162
  function signedInStatus(expiresAt) {
144
163
  return expiresAt === void 0 ? { status: "signed-in" } : {
145
164
  status: "signed-in",
@@ -336,6 +355,71 @@ function json(res, status, value) {
336
355
  });
337
356
  res.end(JSON.stringify(value));
338
357
  }
358
+ function readJsonBody(req) {
359
+ return new Promise((resolve, reject) => {
360
+ let body = "";
361
+ let size = 0;
362
+ let finished = false;
363
+ req.setEncoding("utf8");
364
+ req.on("data", (chunk) => {
365
+ if (finished) return;
366
+ size += Buffer.byteLength(chunk);
367
+ if (size > CODEX_SETTINGS_BODY_LIMIT) {
368
+ finished = true;
369
+ reject(/* @__PURE__ */ new Error("request body is too large"));
370
+ return;
371
+ }
372
+ body += chunk;
373
+ });
374
+ req.on("end", () => {
375
+ if (finished) return;
376
+ finished = true;
377
+ try {
378
+ resolve(JSON.parse(body));
379
+ } catch {
380
+ reject(/* @__PURE__ */ new Error("request body is not valid JSON"));
381
+ }
382
+ });
383
+ req.on("error", (error) => {
384
+ if (finished) return;
385
+ finished = true;
386
+ reject(error);
387
+ });
388
+ });
389
+ }
390
+ function decodeSettingsWrite(value) {
391
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return void 0;
392
+ const record = value;
393
+ if (typeof record["enableImageTool"] !== "boolean" || typeof record["enableImageUpload"] !== "boolean") return;
394
+ return decodeCodexAuthSettings(record);
395
+ }
396
+ /** Register the plugin-owned settings endpoint for browsers opened through a NAS authority. */
397
+ function registerCodexSettingsRoute(ctx, settings) {
398
+ ctx.effect(() => {
399
+ const authorize = (req, res) => {
400
+ if (trustedRequest(req)) return true;
401
+ json(res, 403, { error: REMOTE_WEB_ORIGIN_NOT_TRUSTED });
402
+ return false;
403
+ };
404
+ return ctx.webServer.register({
405
+ kind: "exact",
406
+ path: CODEX_AUTH_SETTINGS_PATH,
407
+ handler: async (req, res) => {
408
+ if (req.method !== "GET" && req.method !== "PUT") return json(res, 405, { error: "method not allowed" });
409
+ if (!authorize(req, res)) return;
410
+ if (req.method === "GET") return json(res, 200, settings.get());
411
+ try {
412
+ const next = decodeSettingsWrite(await readJsonBody(req));
413
+ if (next === void 0) return json(res, 400, { error: CODEX_SETTINGS_INVALID });
414
+ await settings.update(next);
415
+ json(res, 200, settings.get());
416
+ } catch (error) {
417
+ json(res, 400, { error: safeMessage(error) });
418
+ }
419
+ }
420
+ });
421
+ }, "dsh-codex-auth-plugin: remote settings route");
422
+ }
339
423
  /** Register the auth endpoints when the DSH Web server is available. */
340
424
  function registerCodexAuthRoutes(ctx, store, mirror) {
341
425
  const auth = new CodexWebAuth(store, CODEX_AUTH_URL_TIMEOUT_MS, mirror);
@@ -460,12 +544,6 @@ var CodexCredentialMirror = class {
460
544
  }
461
545
  };
462
546
  //#endregion
463
- //#region src/settings-contract.ts
464
- const DEFAULT_CODEX_AUTH_SETTINGS = Object.freeze({
465
- enableImageTool: false,
466
- enableImageUpload: false
467
- });
468
- //#endregion
469
547
  //#region src/settings.ts
470
548
  /** Host settings registration that makes the standalone auth card discoverable. */
471
549
  /** Branded namespace used by the Host settings service. */
@@ -706,6 +784,7 @@ function apply(ctx) {
706
784
  };
707
785
  }, "dsh-codex-auth: credential mirror");
708
786
  registerCodexAuthRoutes(ctx, store, mirror);
787
+ registerCodexSettingsRoute(ctx, settings);
709
788
  let stopped = false;
710
789
  let imageFiber;
711
790
  let imageTail = Promise.resolve();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@tnnevol/dsh-codex-auth",
3
3
  "displayName": "Codex Auth",
4
4
  "description": "Standalone ChatGPT OAuth account management for DeepSeek Harness.",
5
- "version": "0.1.0-rc.7",
5
+ "version": "0.1.0-rc.7.2",
6
6
  "author": "tnnevol",
7
7
  "repository": {
8
8
  "type": "git",