@dungsil/dsh-plugin-cli-proxy-api 0.2.1 → 0.2.3

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/dist/client.js CHANGED
@@ -19,12 +19,13 @@ function installStyles(css) {
19
19
  return () => style.remove();
20
20
  }
21
21
  // ../../tools/ui/src/settings.tsx
22
- /** The rc.5 settings shell has no icon slot, so decorate only the matching navigation rows. */
22
+ /** Decorate matching navigation rows in both the rc.5 and 0.2.2 settings shells. */
23
23
  function installSettingsIcon(id, labels, icon) {
24
24
  const attribute = "data-dsh-settings-icon";
25
25
  const selector = `[${attribute}="${id}"]`;
26
26
  const removeStyles = installStyles(`
27
- ${selector} > svg { display: none; }
27
+ ${selector} > svg,
28
+ ${selector} > [aria-hidden="true"]:not([data-dsh-settings-icon-glyph]) { display: none; }
28
29
  ${typeof icon === "string" ? `${selector}::before { content: ''; display: block; flex: none; width: 16px; height: 16px; background: currentColor; mask: url("data:image/svg+xml,${encodeURIComponent(icon)}") center / contain no-repeat; }` : ""}
29
30
  ${selector} > [data-dsh-settings-icon-glyph] { display: inline-flex; flex: none; }
30
31
  `);
@@ -89,6 +90,22 @@ function SettingsRow({ label, children }) {
89
90
  }), children]
90
91
  });
91
92
  }
93
+ // ../../tools/ui/src/page.tsx
94
+ function PluginPage({ children, className = "", contentClassName = "" }) {
95
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
96
+ className: `dsh-plugin-page ${className}`,
97
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
98
+ className: `dsh-plugin-page-content ${contentClassName}`,
99
+ children
100
+ })
101
+ });
102
+ }
103
+ // ../../tools/ui/src/page-styles.ts
104
+ const pageStyles = `
105
+ .dsh-plugin-page { box-sizing: border-box; height: 100%; min-height: 0; overflow: auto; padding: 24px; padding-inline-start: max(24px, var(--dsh-frame-leading-clearance, 0px)); }
106
+ .dsh-plugin-page-content { max-width: 960px; margin: 0 auto; }
107
+ .dsh-plugin-page h1 { margin: 0 0 24px; font-size: 20px; line-height: 28px; font-weight: 500; }
108
+ `;
92
109
  // src/protocol.ts
93
110
  const channel = "/api";
94
111
  const endpointPrefix = "dungsil-cli-proxy-api/";
@@ -115,7 +132,7 @@ const copy = {
115
132
  quotaLoadTitle: "Accounts are not available yet",
116
133
  quotaLoadHint: "Accounts and quota load automatically with a saved management key. If loading fails, refresh accounts to try again.",
117
134
  accountsObservedAt: "Accounts checked at",
118
- quotaHint: "Accounts and quota load automatically. The memory cache is valid for 5 minutes. Expired results update when you reopen this panel. Refresh accounts to update all results now. Cooldown reset only clears proxy routing blocks; it does not refill quota. No paid inference or reset credits are used.",
135
+ quotaHint: "Accounts and quota load once and stay cached in memory when you reopen this panel. Refresh accounts to update all results. Connection changes clear the cache. Cooldown reset only clears proxy routing blocks; it does not refill quota. No paid inference or reset credits are used.",
119
136
  refreshQuotaAccounts: "Refresh accounts",
120
137
  searchAccounts: "Search name / provider",
121
138
  refreshQuota: "Refresh quota",
@@ -184,7 +201,7 @@ const copy = {
184
201
  quotaLoadTitle: "아직 계정 정보를 불러오지 못했습니다",
185
202
  quotaLoadHint: "관리 키가 저장되어 있으면 계정과 할당량을 자동으로 불러옵니다. 불러오기에 실패했다면 계정을 새로고침하세요.",
186
203
  accountsObservedAt: "계정 확인 시각",
187
- quotaHint: "계정과 할당량을 자동으로 불러옵니다. 메모리 캐시는 5분간 유효하며, 화면을 다시 열 때 만료된 결과를 갱신합니다. 계정을 새로고침하면 전체 결과를 즉시 갱신합니다. 대기 시간 초기화는 프록시의 라우팅 차단만 해제하며 할당량을 충전하지 않습니다. 유료 추론이나 초기화 크레딧을 사용하지 않습니다.",
204
+ quotaHint: "계정과 할당량은 한 번 불러온 뒤 메모리에 캐시하며, 화면을 다시 열 때 그대로 표시합니다. 계정을 새로고침하면 전체 결과를 갱신합니다. 연결이 변경되면 캐시를 비웁니다. 대기 시간 초기화는 프록시의 라우팅 차단만 해제하며 할당량을 충전하지 않습니다. 유료 추론이나 초기화 크레딧을 사용하지 않습니다.",
188
205
  refreshQuotaAccounts: "계정 새로고침",
189
206
  searchAccounts: "이름 / 공급자 검색",
190
207
  refreshQuota: "할당량 새로고침",
@@ -253,7 +270,7 @@ const copy = {
253
270
  quotaLoadTitle: "暂未加载账户信息",
254
271
  quotaLoadHint: "保存管理密钥后,账户和配额会自动加载。加载失败时,请刷新账户重试。",
255
272
  accountsObservedAt: "账户检查时间",
256
- quotaHint: "账户和配额会自动加载。内存缓存有效期为 5 分钟,重新打开此面板时会更新已过期的结果。刷新账户可立即更新全部结果。重置冷却时间仅解除代理路由限制,不会补充配额。不会使用付费推理或重置额度。",
273
+ quotaHint: "账户和配额加载一次后会缓存在内存中,重新打开此面板时直接显示。刷新账户可更新全部结果。连接变更会清除缓存。重置冷却时间仅解除代理路由限制,不会补充配额。不会使用付费推理或重置额度。",
257
274
  refreshQuotaAccounts: "刷新账户",
258
275
  searchAccounts: "搜索名称 / 提供商",
259
276
  refreshQuota: "刷新配额",
@@ -684,6 +701,7 @@ function QuotaPanel({ accounts, quotas, fetchedAt, language, text, busy, canLoad
684
701
  ]
685
702
  });
686
703
  }
704
+ // src/quota-cache.ts
687
705
  const quotaKey = (account) => JSON.stringify([account.name, account.authIndex]);
688
706
  const connectionKey = (view) => JSON.stringify([
689
707
  view.baseURL,
@@ -692,6 +710,9 @@ const connectionKey = (view) => JSON.stringify([
692
710
  ]);
693
711
  var QuotaCache = class {
694
712
  entry;
713
+ get connection() {
714
+ return this.entry?.view;
715
+ }
695
716
  read(view) {
696
717
  if (this.entry?.key !== connectionKey(view)) {
697
718
  this.clear();
@@ -702,6 +723,7 @@ var QuotaCache = class {
702
723
  write(view, snapshot) {
703
724
  this.entry = {
704
725
  key: connectionKey(view),
726
+ view,
705
727
  snapshot
706
728
  };
707
729
  }
@@ -726,10 +748,6 @@ function subscribeQuotaGeneration(generation, cache, cancel, changed) {
726
748
  changed(!!generation.getSnapshot());
727
749
  });
728
750
  }
729
- function isQuotaCacheFresh(snapshot, now = Date.now()) {
730
- const age = now - snapshot.fetchedAt;
731
- return age >= 0 && age < 3e5;
732
- }
733
751
  async function loadQuotaSnapshot(list, fetch, signal, publish) {
734
752
  signal.throwIfAborted();
735
753
  const accounts = await list();
@@ -775,13 +793,10 @@ const inject = [
775
793
  "locale",
776
794
  "slots"
777
795
  ];
778
- const styles = `${settingsStyles}${quotaStyles}
796
+ const styles = `${settingsStyles}${quotaStyles}${pageStyles}
779
797
  .dsh-cli-proxy-icon { display: inline-flex; }
780
798
  .dsh-cli-proxy-icon > svg { width: 100%; height: 100%; }
781
799
  .dsh-cli-proxy { container-type: inline-size; color: var(--dsw-alias-label-primary); font: 14px/22px var(--dsw-font-family, system-ui); }
782
- .dsh-cli-proxy-page { height: 100%; min-height: 0; box-sizing: border-box; overflow-y: auto; padding: 24px; padding-inline-start: max(24px, var(--dsh-frame-leading-clearance, 0px)); }
783
- .dsh-cli-proxy-page > .dsh-cli-proxy { max-width: 960px; margin: 0 auto; }
784
- .dsh-cli-proxy h1 { font-size: 20px; line-height: 28px; font-weight: 500; margin: 0 0 24px; }
785
800
  .dsh-cli-proxy h3 { margin: 24px 0 0; font: inherit; color: var(--dsw-alias-label-secondary); }
786
801
  .dsh-cli-proxy .controls { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; max-width: 65%; }
787
802
  .dsh-cli-proxy .controls > span:has(> input) { box-sizing: border-box; width: 280px; min-width: 0; max-width: 100%; }
@@ -807,18 +822,20 @@ function ProxyPage({ ctx, cache, settings = false }) {
807
822
  const id = (0, react.useId)();
808
823
  const [connectionVersion, setConnectionVersion] = (0, react.useState)(0);
809
824
  const connection = ctx.get("connection");
810
- const [view, setView] = (0, react.useState)();
811
- const [baseURL, setBaseURL] = (0, react.useState)("");
825
+ const cachedView = settings ? void 0 : cache.connection;
826
+ const cached = cachedView && cache.read(cachedView);
827
+ const [view, setView] = (0, react.useState)(cachedView);
828
+ const [baseURL, setBaseURL] = (0, react.useState)(cachedView?.baseURL ?? "");
812
829
  const [apiKey, setAPIKey] = (0, react.useState)("");
813
830
  const [managementKey, setManagementKey] = (0, react.useState)("");
814
831
  const [removeAPIKey, setRemoveAPIKey] = (0, react.useState)(false);
815
832
  const [removeManagementKey, setRemoveManagementKey] = (0, react.useState)(false);
816
- const [synchronized, setSynchronized] = (0, react.useState)(false);
817
- const [busy, setBusy] = (0, react.useState)(true);
833
+ const [synchronized, setSynchronized] = (0, react.useState)(!!cached);
834
+ const [busy, setBusy] = (0, react.useState)(!cached);
818
835
  const [notice, setNotice] = (0, react.useState)();
819
- const [quotaAccounts, setQuotaAccounts] = (0, react.useState)();
820
- const [quotas, setQuotas] = (0, react.useState)({});
821
- const [fetchedAt, setFetchedAt] = (0, react.useState)();
836
+ const [quotaAccounts, setQuotaAccounts] = (0, react.useState)(cached?.accounts);
837
+ const [quotas, setQuotas] = (0, react.useState)(cached?.quotas ?? {});
838
+ const [fetchedAt, setFetchedAt] = (0, react.useState)(cached?.fetchedAt);
822
839
  const [uncertain, setUncertain] = (0, react.useState)(false);
823
840
  const lock = (0, react.useRef)(true);
824
841
  const lifetime = (0, react.useRef)();
@@ -831,6 +848,7 @@ function ProxyPage({ ctx, cache, settings = false }) {
831
848
  const describe = (0, react.useCallback)(async (controller) => {
832
849
  setSynchronized(false);
833
850
  const next = await call("describe", {}, controller);
851
+ cache.read(next);
834
852
  setView(next);
835
853
  setBaseURL(next.baseURL);
836
854
  setSynchronized(true);
@@ -842,7 +860,7 @@ function ProxyPage({ ctx, cache, settings = false }) {
842
860
  setQuotas({});
843
861
  setFetchedAt(void 0);
844
862
  return next;
845
- }, [call]);
863
+ }, [call, cache]);
846
864
  const showSnapshot = (0, react.useCallback)((snapshot) => {
847
865
  setQuotaAccounts(snapshot.accounts);
848
866
  setQuotas(snapshot.quotas);
@@ -898,6 +916,20 @@ function ProxyPage({ ctx, cache, settings = false }) {
898
916
  (0, react.useEffect)(() => {
899
917
  const controller = new AbortController();
900
918
  lifetime.current = controller;
919
+ const restoreView = settings ? void 0 : cache.connection;
920
+ const restore = restoreView && cache.read(restoreView);
921
+ if (restore && restoreView) {
922
+ setView(restoreView);
923
+ setBaseURL(restoreView.baseURL);
924
+ setSynchronized(true);
925
+ showSnapshot(restore);
926
+ lock.current = false;
927
+ setBusy(false);
928
+ return () => {
929
+ controller.abort();
930
+ if (lifetime.current === controller) lifetime.current = void 0;
931
+ };
932
+ }
901
933
  lock.current = true;
902
934
  setBusy(true);
903
935
  setSynchronized(false);
@@ -911,12 +943,7 @@ function ProxyPage({ ctx, cache, settings = false }) {
911
943
  setNotice(void 0);
912
944
  describe(controller).then(async (next) => {
913
945
  if (settings) return;
914
- const cached = cache.read(next);
915
946
  if (!next.managementKeySaved) return;
916
- if (cached) {
917
- showSnapshot(cached);
918
- if (isQuotaCacheFresh(cached)) return;
919
- }
920
947
  await loadAccounts(next, controller);
921
948
  }).catch(() => {
922
949
  if (!controller.signal.aborted && lifetime.current === controller) setNotice("error");
@@ -1151,15 +1178,12 @@ function ProxyPage({ ctx, cache, settings = false }) {
1151
1178
  });
1152
1179
  }
1153
1180
  function ProxyPanel({ ctx, cache }) {
1154
- return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1155
- className: "dsh-cli-proxy-page",
1156
- children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1157
- className: "dsh-cli-proxy",
1158
- children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: "CLIProxyAPI" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProxyPage, {
1159
- ctx,
1160
- cache
1161
- })]
1162
- })
1181
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(PluginPage, {
1182
+ contentClassName: "dsh-cli-proxy",
1183
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", { children: "CLIProxyAPI" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProxyPage, {
1184
+ ctx,
1185
+ cache
1186
+ })]
1163
1187
  });
1164
1188
  }
1165
1189
  function apply(ctx) {
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { createHash, randomUUID } from "node:crypto";
2
2
  import { clientRequestSchema } from "@deepseek-ai/dsh-client-connection";
3
3
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
4
- // ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.8.6/node_modules/@deepseek-ai/cosmokit/lib/index.js
4
+ // ../../node_modules/.pnpm/@deepseek-ai+cosmokit@1.9.0/node_modules/@deepseek-ai/cosmokit/lib/index.js
5
5
  /** Return true when a value is `null` or `undefined`. */
6
6
  function isNullable(value) {
7
7
  return value === null || value === void 0;
@@ -254,7 +254,7 @@ var Time;
254
254
  }
255
255
  Time.template = template;
256
256
  })(Time || (Time = {}));
257
- // ../../node_modules/.pnpm/@deepseek-ai+schemastery@3.18.5/node_modules/@deepseek-ai/schemastery/lib/index.mjs
257
+ // ../../node_modules/.pnpm/@deepseek-ai+schemastery@3.19.0/node_modules/@deepseek-ai/schemastery/lib/index.mjs
258
258
  const kSchema = Symbol.for("schemastery");
259
259
  const kValidationError = Symbol.for("ValidationError");
260
260
  globalThis.__schemastery_index__ ??= 0;
@@ -1455,7 +1455,7 @@ function apply(ctx, config) {
1455
1455
  ctx.effect(() => () => lifetime.abort());
1456
1456
  const currentURL = () => serverURL(config.baseURL.get());
1457
1457
  const ownerEntry = () => {
1458
- const entries = ctx.configEditor.entries().filter((entry) => entry.fiber === ctx.fiber);
1458
+ const entries = ctx.configEditor.entries().filter((entry) => entry.fiber?.uid === ctx.fiber.uid);
1459
1459
  if (entries.length !== 1) throw new Error("unavailable");
1460
1460
  return entries[0];
1461
1461
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dungsil/dsh-plugin-cli-proxy-api",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "CLIProxyAPI model provider and owner-only management settings for DeepSeek Harness.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -24,39 +24,40 @@
24
24
  "access": "public"
25
25
  },
26
26
  "devDependencies": {
27
- "@deepseek-ai/cordis": "4.0.5",
28
- "@deepseek-ai/dsh-app-boot": "0.2.1-rc.5",
29
- "@deepseek-ai/dsh-client-connection": "0.2.1-rc.5",
30
- "@deepseek-ai/dsh-client-locale": "0.2.1-rc.5",
31
- "@deepseek-ai/dsh-client-ui-layout": "0.2.1-rc.5",
32
- "@deepseek-ai/dsh-client-ui-primitives": "0.2.1-rc.5",
33
- "@deepseek-ai/dsh-client-ui-renderer": "0.2.1-rc.5",
34
- "@deepseek-ai/dsh-client-ui-settings": "0.2.1-rc.5",
35
- "@deepseek-ai/dsh-client-ui-sidebar": "0.2.1-rc.5",
36
- "@deepseek-ai/dsh-config-editor": "0.2.1-rc.5",
37
- "@deepseek-ai/dsh-credentials": "0.2.1-rc.5",
38
- "@deepseek-ai/dsh-credentials-local": "0.2.1-rc.5",
39
- "@deepseek-ai/dsh-llm": "0.2.1-rc.5",
40
- "@deepseek-ai/dsh-llm-pi-ai": "0.2.1-rc.5",
41
- "@deepseek-ai/schemastery": "3.18.5",
27
+ "@deepseek-ai/cordis": "4.1.0",
28
+ "@deepseek-ai/dsh-app-boot": "0.2.2-rc.1",
29
+ "@deepseek-ai/dsh-client-connection": "0.2.2-rc.1",
30
+ "@deepseek-ai/dsh-client-locale": "0.2.2-rc.1",
31
+ "@deepseek-ai/dsh-client-ui-layout": "0.2.2-rc.1",
32
+ "@deepseek-ai/dsh-client-ui-renderer": "0.2.2-rc.1",
33
+ "@deepseek-ai/dsh-client-ui-settings": "0.2.2-rc.1",
34
+ "@deepseek-ai/dsh-client-ui-sidebar": "0.2.2-rc.1",
35
+ "@deepseek-ai/dsh-config-editor": "0.2.2-rc.1",
36
+ "@deepseek-ai/dsh-credentials": "0.2.2-rc.1",
37
+ "@deepseek-ai/dsh-credentials-local": "0.2.2-rc.1",
38
+ "@deepseek-ai/dsh-llm": "0.2.2-rc.1",
39
+ "@deepseek-ai/dsh-llm-pi-ai": "0.2.2-rc.1",
40
+ "@deepseek-ai/schemastery": "3.19.0",
42
41
  "@dungsil/dsh-tsconfig": "0.0.0",
43
42
  "@dungsil/dsh-tsdown": "0.0.0",
44
43
  "@dungsil/dsh-ui": "0.0.0",
45
44
  "@types/react": "18.3.31",
45
+ "happy-dom": "21.0.4",
46
46
  "react": "18.3.1",
47
+ "react-dom": "18.3.1",
47
48
  "tsdown": "0.23.4"
48
49
  },
49
50
  "peerDependencies": {
50
51
  "@deepseek-ai/cordis": "^4.0.5",
51
- "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.5",
52
- "@deepseek-ai/dsh-client-ui-primitives": "^0.2.1-rc.5",
53
- "@deepseek-ai/dsh-config-editor": "^0.2.1-rc.5",
54
- "@deepseek-ai/dsh-credentials": "^0.2.1-rc.5",
55
- "@deepseek-ai/dsh-llm": "^0.2.1-rc.5",
56
- "@deepseek-ai/dsh-llm-pi-ai": "^0.2.1-rc.5"
52
+ "@deepseek-ai/dsh-client-connection": "^0.2.1-rc.5 || ^0.2.2-rc.1",
53
+ "@deepseek-ai/dsh-client-ui-primitives": "^0.2.1-rc.5 || ^0.2.2-rc.1",
54
+ "@deepseek-ai/dsh-config-editor": "^0.2.1-rc.5 || ^0.2.2-rc.1",
55
+ "@deepseek-ai/dsh-credentials": "^0.2.1-rc.5 || ^0.2.2-rc.1",
56
+ "@deepseek-ai/dsh-llm": "^0.2.1-rc.5 || ^0.2.2-rc.1",
57
+ "@deepseek-ai/dsh-llm-pi-ai": "^0.2.1-rc.5 || ^0.2.2-rc.1"
57
58
  },
58
59
  "engines": {
59
- "dsh": "^0.2.1-rc.5",
60
+ "dsh": "^0.2.1-rc.5 || ^0.2.2-rc.1",
60
61
  "node": ">=24.9.0"
61
62
  },
62
63
  "dsh": {