dsh-openrouter-provider-advisor 0.1.2 → 0.1.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/CHANGELOG.md CHANGED
@@ -2,6 +2,48 @@
2
2
 
3
3
  All notable changes to this project are documented here.
4
4
 
5
+ ## [0.1.4] - 2026-09-04
6
+
7
+ ### Fixed
8
+
9
+ - Compatible with DSH web `>= 0.1.2-rc.1` again after the breaking client/Host
10
+ API changes in that release. The previous build failed to load on current
11
+ DSH because it imported `settingsNamespace` / `installSettingsSection` from
12
+ `@deepseek-ai/dsh-settings` (both removed in 0.1.2) and injected the removed
13
+ `apiProxy` service.
14
+ - Host: the OpenRouter settings namespace is now registered through
15
+ `ctx.settings.register(...)` and read through its live owner scope
16
+ (`dsh >= 0.1.2` settings API), replacing the removed helper functions.
17
+ - Host: session model catalog reads and session model selections now go
18
+ through `ctx.sessionController.modelCatalog()` / `selectModel(...)`
19
+ (the Session business API that replaced the `apiProxy` RPC service).
20
+ - Client: dropped every import from the removed `@deepseek-ai/dsh-client-*`
21
+ type packages (including `dsh-client-runtime`); the bundle now types the
22
+ runtime structurally, like current first-party/external plugins.
23
+ - Client: `sidebar.footer.action` / `shell.overlay` seats no longer inject a
24
+ `useSessions` render prop, so the trigger and overlay read the current
25
+ session from the root `sessions` service's list store instead.
26
+ - Manifest: `dsh.plugin.json` engine floor raised to `>= 0.1.2-rc.1`, and the
27
+ client module injection list narrowed to the modules current DSH serves.
28
+
29
+ ### Validation
30
+
31
+ - 80 automated tests passing (adapter specs updated to the
32
+ `sessionController` seam).
33
+ - TypeScript typecheck and production build passing.
34
+ - `npm audit --omit=dev` reports zero vulnerabilities.
35
+
36
+ ## [0.1.3] - 2026-08-25
37
+
38
+ ### Changed
39
+
40
+ - Updated npm `repository`, `homepage`, and `bugs` metadata after the GitHub account was renamed from `xuanfengtechx` to `xuan666-lab`, allowing DSH Store to associate the published package with the current repository URL.
41
+
42
+ ### Validation
43
+
44
+ - Automated tests, TypeScript typecheck, and production build passing.
45
+ - `npm audit --omit=dev` reports zero vulnerabilities.
46
+
5
47
  ## [0.1.2] - 2026-08-24
6
48
 
7
49
  ### Added
@@ -51,6 +93,7 @@ All notable changes to this project are documented here.
51
93
 
52
94
  - Initial public release.
53
95
 
54
- [0.1.2]: https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor/compare/v0.1.1...v0.1.2
55
- [0.1.1]: https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor/compare/v0.1.0...v0.1.1
56
- [0.1.0]: https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor/releases/tag/v0.1.0
96
+ [0.1.3]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.2...v0.1.3
97
+ [0.1.2]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.1...v0.1.2
98
+ [0.1.1]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/compare/v0.1.0...v0.1.1
99
+ [0.1.0]: https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/releases/tag/v0.1.0
package/README.md CHANGED
@@ -6,6 +6,17 @@
6
6
 
7
7
  > **核心价值:在不更换模型的前提下,减少不必要的调用成本,并在价格、速度和可靠性之间找到更适合当前工作负载的线路。**
8
8
 
9
+ 插件推荐有机会找到比模型官网 API 明显更便宜的同模型线路。以 2026-08-25 面板中的 StreamLake `deepseek-v4-flash-0731` 报价为例,按 `1 USD ≈ 6.7225 CNY` 换算,与当时 DeepSeek 官网高峰期 Flash 报价对比如下:
10
+
11
+ | 每百万 tokens | StreamLake | DeepSeek 官网高峰期 | 预计降低 |
12
+ | --- | ---: | ---: | ---: |
13
+ | 输入(缓存命中) | $0.0028 ≈ ¥0.0188 | ¥0.10 | **81.2%** |
14
+ | 输入(缓存未命中) | $0.088 ≈ ¥0.5916 | ¥3.00 | **80.3%** |
15
+ | 输出 | $0.064 ≈ ¥0.4302 | ¥9.00 | **95.2%** |
16
+ | Code Agent 加权估算(2% / 8% / 90%) | ≈ ¥0.0632 | ≈ ¥0.8700 | **92.7%** |
17
+
18
+ > 以上仅是特定时间点的价格示例,不构成持续低价承诺;OpenRouter 供应商报价、DeepSeek 官网定价和汇率都会变化,请以插件实时结果与 [DeepSeek 官方价格页](https://api-docs.deepseek.com/zh-cn/quick_start/pricing/) 为准。汇率参考:[USD/CNY 2026-08-25](https://www.investing.com/currencies/usd-cny-historical-data)。
19
+
9
20
  🌏 **中文** · [English](./README_EN.md)
10
21
 
11
22
  ![OpenRouter 供应商推荐与实时切换](./docs/images/provider-panel.png)
@@ -36,7 +47,7 @@
36
47
 
37
48
  ## 安装
38
49
 
39
- 前置条件:DSH `0.1.1-rc.2` 或更新版本、Node.js `^22.19.0` 或 `>=24`,并已在 **DSH 设置 → 模型** 中配置 OpenRouter provider/API Key。
50
+ 前置条件:DSH `0.1.2-rc.1` 或更新版本(插件适配了 0.1.2 移除 `settingsNamespace`/`installSettingsSection` 与 `apiProxy` 服务后的 Host/Client API)、Node.js `^22.19.0` 或 `>=24`,并已在 **DSH 设置 → 模型** 中配置 OpenRouter provider/API Key。
40
51
 
41
52
  ```bash
42
53
  dsh plugin --profile web add dsh-openrouter-provider-advisor@latest
@@ -85,20 +96,21 @@ session.selectModel 切换当前会话
85
96
  价格成本 = 输入价 × 2% + 输出价 × 8% + 缓存读取价 × 90%
86
97
  ```
87
98
 
88
- ### 可用率安全带
99
+ ### 可用率降权
89
100
 
90
- `status != 0` 的 endpoint 直接排除。其余 endpoint 使用温和的通用降权:
101
+ `status != 0` 的 endpoint 直接排除。其余 endpoint 使用连续、温和的通用降权,避免短时小幅波动造成排名断崖:
91
102
 
92
103
  | OpenRouter 近 30 分钟 uptime | 保留分数 |
93
104
  | --- | ---: |
94
105
  | ≥99.5% | 100% |
95
- | 99–99.5% | 85% |
96
- | 97–99% | 60% |
97
- | 90–97% | 30% |
98
- | <90% | 5% |
99
- | 无数据 | 70% |
106
+ | 99–99.5% | 97%–100%(连续插值) |
107
+ | 98–99% | 90%–97%(连续插值) |
108
+ | 95–98% | 75%–90%(连续插值) |
109
+ | 90–95% | 50%–75%(连续插值) |
110
+ | <90% | 10%–50%(连续插值) |
111
+ | 无数据 | 85% |
100
112
 
101
- 用户设置可以让惩罚更严格,但不能绕过安全带。
113
+ 用户设置中的阈值和系数会在阈值以下 5 个百分点内渐进生效,并与通用降权取更严格者。
102
114
 
103
115
  ## Agent 工具
104
116
 
@@ -136,13 +148,14 @@ POST /api/v1/presets/{slug}/chat/completions
136
148
  | 插件已安装但界面没变化 | 硬刷新浏览器;Host 有改动时重启 DSH。 |
137
149
  | 未检测到 OpenRouter 凭据 | 在 DSH 设置 → 模型中配置 OpenRouter provider/API Key。 |
138
150
  | 某供应商分数高但请求 429 | uptime 有统计延迟,临时切换到其他供应商。 |
151
+ | 面板默认选中的模型不是当前会话选的那一个 | DSH `>= 0.1.2` 的会话级模型选择由前端模型目录维护,Host 目录 API 只提供部署默认;在面板下拉里选择要分析的模型即可,推荐与切换都会按所选模型执行。 |
139
152
  | `minimum release age` | 等待 profile 安全策略允许该版本后重试。 |
140
153
  | DSH 拒绝 `.env` 中的 `DEEPSEEK_BASE_URL` | 从项目 `.env` 移除,并在启动 DSH 的终端中 `export`。 |
141
154
 
142
155
  ## 本地源码开发
143
156
 
144
157
  ```bash
145
- git clone https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor.git
158
+ git clone https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git
146
159
  cd dsh-openrouter-provider-advisor
147
160
  npm install
148
161
  npm run verify
package/README_EN.md CHANGED
@@ -6,6 +6,17 @@ It combines quantization, speed, cache-heavy pricing, context, and uptime into a
6
6
 
7
7
  > **Core value: reduce avoidable inference spend without changing models, while finding a practical balance between cost, speed, and reliability.**
8
8
 
9
+ The recommendations can surface same-model routes priced well below the model vendor's own API. For example, the StreamLake `deepseek-v4-flash-0731` quote shown on 2026-08-25 compares with DeepSeek's peak-hour Flash pricing as follows, using `1 USD ≈ 6.7225 CNY`:
10
+
11
+ | Per 1M tokens | StreamLake | DeepSeek peak hours | Estimated reduction |
12
+ | --- | ---: | ---: | ---: |
13
+ | Input (cache hit) | $0.0028 ≈ ¥0.0188 | ¥0.10 | **81.2%** |
14
+ | Input (cache miss) | $0.088 ≈ ¥0.5916 | ¥3.00 | **80.3%** |
15
+ | Output | $0.064 ≈ ¥0.4302 | ¥9.00 | **95.2%** |
16
+ | Code Agent weighted estimate (2% / 8% / 90%) | ≈ ¥0.0632 | ≈ ¥0.8700 | **92.7%** |
17
+
18
+ > This is a point-in-time example, not a guarantee of persistent savings. OpenRouter provider quotes, DeepSeek pricing, and FX rates can all change; check the live recommendations and [DeepSeek's official pricing page](https://api-docs.deepseek.com/quick_start/pricing/). FX reference: [USD/CNY on 2026-08-25](https://www.investing.com/currencies/usd-cny-historical-data).
19
+
9
20
  [中文](./README.md) · **English**
10
21
 
11
22
  ![OpenRouter provider ranking](./docs/images/provider-panel.png)
@@ -17,7 +28,7 @@ It combines quantization, speed, cache-heavy pricing, context, and uptime into a
17
28
  - Model-aware ranking without a hard-coded model catalog.
18
29
  - Best overall, price-first, speed-first, and context-first strategies.
19
30
  - Code Agent price blend: 2% input, 8% output, 90% cache-read by default.
20
- - Gentle 30-minute uptime safety bands and hard filtering for unavailable endpoints.
31
+ - Continuous, gentle 30-minute uptime penalties and hard filtering for unavailable endpoints.
21
32
  - One-click switching for the active session and deployment default.
22
33
  - Linked percentage sliders with locks; each group always totals 100%.
23
34
  - Reuses DSH credential references; API keys never reach the browser.
@@ -78,13 +89,14 @@ cost = input price × 2% + output price × 8% + cache-read price × 90%
78
89
  | OpenRouter trailing 30-minute uptime | Score retained |
79
90
  | --- | ---: |
80
91
  | ≥99.5% | 100% |
81
- | 99–99.5% | 85% |
82
- | 97–99% | 60% |
83
- | 90–97% | 30% |
84
- | <90% | 5% |
85
- | Missing | 70% |
92
+ | 99–99.5% | 97%–100% (continuous) |
93
+ | 98–99% | 90%–97% (continuous) |
94
+ | 95–98% | 75%–90% (continuous) |
95
+ | 90–95% | 50%–75% (continuous) |
96
+ | <90% | 10%–50% (continuous) |
97
+ | Missing | 85% |
86
98
 
87
- Endpoints with nonzero status are excluded. User settings may make penalties stricter, never looser.
99
+ Endpoints with nonzero status are excluded. Configured penalties ramp in across five percentage points below the threshold and combine with the stricter general curve.
88
100
 
89
101
  ## Credentials and security
90
102
 
@@ -102,7 +114,7 @@ Only official OpenRouter APIs are used: `/models`, `/models/{id}/endpoints`, and
102
114
  ## Development
103
115
 
104
116
  ```bash
105
- git clone https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor.git
117
+ git clone https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git
106
118
  cd dsh-openrouter-provider-advisor
107
119
  npm install
108
120
  npm run verify
package/dsh.plugin.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "id": "dsh-external/dsh-openrouter-provider-advisor",
3
- "version": "0.1.0",
3
+ "version": "0.1.4",
4
4
  "main": "./lib/index.js",
5
- "description": "OpenRouter 供应商推荐与安全切换:按质量、速度、缓存价格、上下文和可用率排序",
5
+ "description": "OpenRouter 供应商推荐与安全切换:按质量、速度、缓存价格、上下文和可用率排序(兼容 DSH >= 0.1.2-rc.1)",
6
6
  "engines": {
7
- "dsh": ">=0.1.1-rc.2"
7
+ "dsh": ">=0.1.2-rc.1"
8
8
  },
9
9
  "contributes": {
10
10
  "tools": ["recommend_openrouter_providers", "switch_openrouter_provider"],
package/lib/client.js CHANGED
@@ -670,13 +670,9 @@ function consume(operation) {
670
670
  }
671
671
  function ProviderSidebarTrigger(props) {
672
672
  const i18n = useUiI18n();
673
- const current = props.useSessions((state) => state.current);
674
- const currentAddress = props.useSessions((state) => state.currentAddress);
673
+ const sessionState = (0, react.useSyncExternalStore)(props.sessions.subscribe, props.sessions.getSnapshot, props.sessions.getSnapshot);
675
674
  const panel = (0, react.useSyncExternalStore)(props.store.subscribe, props.store.getSnapshot, props.store.getSnapshot);
676
- const target = providerTriggerState({
677
- current,
678
- currentAddress
679
- });
675
+ const target = providerTriggerState(sessionState);
680
676
  return (0, react.createElement)(react.Fragment, null, (0, react.createElement)("style", null, "div:has(> div[data-slot=\"sidebar.footer.action\"] > button[data-openrouter-provider-trigger]){flex-wrap:wrap;row-gap:4px}"), (0, react.createElement)("button", {
681
677
  type: "button",
682
678
  disabled: target.disabled,
@@ -724,13 +720,9 @@ function freshnessLabel(updatedAt, now, i18n = createI18n("zh-CN")) {
724
720
  const providerGridColumns = "minmax(110px,1fr) minmax(0,2fr) minmax(54px,auto)";
725
721
  function ProviderOverlay(props) {
726
722
  const i18n = useUiI18n();
727
- const current = props.useSessions((state$1) => state$1.current);
728
- const currentAddress = props.useSessions((state$1) => state$1.currentAddress);
723
+ const sessionState = (0, react.useSyncExternalStore)(props.sessions.subscribe, props.sessions.getSnapshot, props.sessions.getSnapshot);
729
724
  const state = (0, react.useSyncExternalStore)(props.store.subscribe, props.store.getSnapshot, props.store.getSnapshot);
730
- const target = providerTriggerState({
731
- current,
732
- currentAddress
733
- });
725
+ const target = providerTriggerState(sessionState);
734
726
  const dialogRef = (0, react.useRef)(null);
735
727
  const [now, setNow] = (0, react.useState)(() => Date.now());
736
728
  (0, react.useEffect)(() => {
@@ -1708,15 +1700,14 @@ function OpenRouterProvidersSettingsCard({ scope }) {
1708
1700
  const inject = [
1709
1701
  "sessions",
1710
1702
  "slots",
1711
- "settingsScope",
1712
- "locale"
1703
+ "locale",
1704
+ "settingsScope"
1713
1705
  ];
1714
1706
  function apply(ctx) {
1715
- const client = ctx;
1716
- attachLocale(client.locale);
1707
+ attachLocale(ctx.locale);
1717
1708
  ctx.effect(() => {
1718
- const offZh = client.locale.register(LOCALE_NS, "zh", zh);
1719
- const offEn = client.locale.register(LOCALE_NS, "en", en);
1709
+ const offZh = ctx.locale.register(LOCALE_NS, "zh", zh);
1710
+ const offEn = ctx.locale.register(LOCALE_NS, "en", en);
1720
1711
  return () => {
1721
1712
  offZh();
1722
1713
  offEn();
@@ -1724,30 +1715,27 @@ function apply(ctx) {
1724
1715
  };
1725
1716
  }, "openrouter-providers: locale dictionaries");
1726
1717
  const panelStore = createProviderPanelStore();
1718
+ const sessions = ctx.sessions.list;
1727
1719
  const BoundTrigger = (props) => (0, react.createElement)(ProviderSidebarTrigger, {
1728
1720
  ...props,
1729
- store: panelStore
1721
+ store: panelStore,
1722
+ sessions
1730
1723
  });
1731
- const BoundOverlay = (props) => (0, react.createElement)(ProviderOverlay, {
1732
- ...props,
1733
- store: panelStore
1724
+ const BoundOverlay = () => (0, react.createElement)(ProviderOverlay, {
1725
+ store: panelStore,
1726
+ sessions
1734
1727
  });
1735
- client.slots.inject("sidebar.footer.action", () => client.slots.register({
1728
+ ctx.slots.inject("sidebar.footer.action", () => ctx.slots.register({
1736
1729
  name: "sidebar.footer.action",
1737
1730
  id: "openrouter-providers-trigger",
1738
1731
  order: 20
1739
1732
  }, BoundTrigger));
1740
- client.slots.inject("shell.overlay", () => client.slots.register({
1741
- name: "shell.overlay",
1742
- id: "openrouter-providers-overlay",
1743
- order: 20
1744
- }, BoundOverlay));
1733
+ ctx.slots.inject("shell.overlay", () => ctx.slots.register({ name: "shell.overlay" }, BoundOverlay));
1745
1734
  ctx.effect(() => () => panelStore.dispose(), "openrouter-providers: provider panel store");
1746
- const scope = client.settingsScope.bind({ namespace: "openrouter-providers" });
1747
- client.slots.inject("settings.plugin.item", () => client.slots.register({
1735
+ const scope = ctx.settingsScope.bind({ namespace: "openrouter-providers" });
1736
+ ctx.slots.inject("settings.plugin.item", () => ctx.slots.register({
1748
1737
  name: "settings.plugin.item",
1749
1738
  key: "openrouter-providers",
1750
- locale: "openrouter-providers",
1751
1739
  inject: () => ({ scope })
1752
1740
  }, OpenRouterProvidersSettingsCard));
1753
1741
  }
package/lib/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { credentialRef } from "@deepseek-ai/dsh-credentials";
2
- import { installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
3
2
  import { defineTool } from "@deepseek-ai/dsh-tools";
4
3
  import z from "@deepseek-ai/schemastery";
5
4
 
@@ -622,10 +621,6 @@ var OpenRouterClient = class {
622
621
 
623
622
  //#endregion
624
623
  //#region src/host-adapter.ts
625
- function unwrap(operation, response) {
626
- if (!response.result.ok) throw new Error(`${operation} failed: ${response.result.error.code}: ${response.result.error.message}`);
627
- return response.result.value;
628
- }
629
624
  function createHostController(ctx, config, credentialRef$1) {
630
625
  const clients = /* @__PURE__ */ new Map();
631
626
  const client = (credential) => {
@@ -644,6 +639,7 @@ function createHostController(ctx, config, credentialRef$1) {
644
639
  };
645
640
  const profiles = () => ctx.settings.get("llm-pi-ai")?.providers ?? {};
646
641
  const fallbackRef = "OPENROUTER_API_KEY";
642
+ const catalog = async () => ctx.sessionController.modelCatalog();
647
643
  return new OpenRouterProviderController({
648
644
  config,
649
645
  credential: async (provider) => {
@@ -670,11 +666,11 @@ function createHostController(ctx, config, credentialRef$1) {
670
666
  source: providerRef === void 0 ? "fallback" : "provider"
671
667
  };
672
668
  },
673
- sessionSelection: async (sessionId) => unwrap("session.models", await ctx.apiProxy.sessions.models({ payload: { sessionId } })).current,
674
- sessionDirectory: async (sessionId) => {
675
- const value = unwrap("session.models", await ctx.apiProxy.sessions.models({ payload: { sessionId } }));
669
+ sessionSelection: async () => (await catalog()).default,
670
+ sessionDirectory: async () => {
671
+ const value = await catalog();
676
672
  return {
677
- current: value.current,
673
+ current: value.default,
678
674
  groups: value.groups ?? []
679
675
  };
680
676
  },
@@ -693,7 +689,7 @@ function createHostController(ctx, config, credentialRef$1) {
693
689
  value: models
694
690
  }]),
695
691
  selectModel: async (selection) => {
696
- unwrap("session.selectModel", await ctx.apiProxy.sessions.selectModel({ payload: selection }));
692
+ await ctx.sessionController.selectModel(selection);
697
693
  },
698
694
  saveDefault: (selection) => ctx.agentDefaultModel.saveSelection(selection)
699
695
  });
@@ -904,15 +900,28 @@ function createProviderTools(defineTool$1, controller) {
904
900
  //#endregion
905
901
  //#region src/index.ts
906
902
  const name = "openrouter-providers";
903
+ /**
904
+ * Required host services.
905
+ *
906
+ * `sessionController` (Session Remote owner, provided by dsh-api-session-controller)
907
+ * replaced the pre-0.1.2 `apiProxy` RPC service that was removed from DSH: the
908
+ * panel now reads the shared model catalog and installs session model
909
+ * selections directly through the Host Session business API.
910
+ */
907
911
  const inject = [
908
912
  "tools",
909
913
  "settings",
910
914
  "credentials",
911
915
  "agentDefaultModel",
912
- "apiProxy",
916
+ "sessionController",
913
917
  "webServer"
914
918
  ];
915
- const NS = settingsNamespace("openrouter-providers");
919
+ /**
920
+ * Settings namespace this plugin owns. Registered through the current
921
+ * `ctx.settings.register` API; the older `settingsNamespace()` helper was
922
+ * removed in DSH 0.1.2.
923
+ */
924
+ const NS = "openrouter-providers";
916
925
  const weights = z.object({
917
926
  quantization: z.number().min(0).max(1).default(.3),
918
927
  speed: z.number().min(0).max(1).default(.3),
@@ -933,16 +942,13 @@ const Config = z.object({
933
942
  uptimePenaltyFactor: z.number().min(0).max(1).default(.85)
934
943
  });
935
944
  function apply(ctx, initial = DEFAULT_CONFIG) {
936
- let source = () => initial;
937
- installSettingsSection(ctx, NS, Config, initial, {
938
- validate: validateConfig,
939
- setSource: (current) => {
940
- source = current;
941
- },
942
- onChange: () => {}
945
+ const scope = ctx.settings.register(NS, Config, {
946
+ base: initial,
947
+ validate: validateConfig
943
948
  });
949
+ const readConfig = () => scope.get();
944
950
  const host = ctx;
945
- const controller = createHostController(host, () => source(), credentialRef);
951
+ const controller = createHostController(host, readConfig, credentialRef);
946
952
  ctx.effect(() => ctx.on("tools/pre-execute", providerToolApproval), "openrouter-providers: approve provider switch");
947
953
  ctx.effect(() => host.webServer.register({
948
954
  kind: "prefix",
@@ -1,4 +1,33 @@
1
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import { type SessionsListStoreLike } from './provider-panel.js';
2
+ import { type SettingsScopeLike } from './settings-card.js';
3
+ import { type LocaleServiceLike } from './i18n.js';
4
+ /**
5
+ * Structural client-context subset — mirrors how current external plugins
6
+ * (dsh-market, dsh-web-updater) type the runtime: the removed
7
+ * `@deepseek-ai/dsh-client-runtime` type package no longer exists in
8
+ * DSH >= 0.1.2, so the plugin imports no client type package and types only
9
+ * the services it touches.
10
+ */
11
+ export interface OrpClientContext {
12
+ effect(dispose: () => unknown, label?: string): void;
13
+ locale: LocaleServiceLike & {
14
+ register(namespace: string, locale: string, messages: Record<string, string>): () => void;
15
+ };
16
+ /** Root sessions service (list store carrying the current session selection). */
17
+ sessions: {
18
+ list: SessionsListStoreLike;
19
+ };
20
+ slots: {
21
+ inject(name: string, register: () => unknown): void;
22
+ register(options: Record<string, unknown>, component: unknown): unknown;
23
+ };
24
+ /** Settings-namespace binder provided by the settings section (dsh web >= 0.1.0-rc.7). */
25
+ settingsScope: {
26
+ bind(spec: {
27
+ namespace: string;
28
+ }): SettingsScopeLike;
29
+ };
30
+ }
2
31
  export declare const inject: string[];
3
- export declare function apply(ctx: ClientContext): void;
32
+ export declare function apply(ctx: OrpClientContext): void;
4
33
  //# sourceMappingURL=index.d.ts.map
@@ -3,12 +3,19 @@ import type { RecommendationResponse } from '../directory.js';
3
3
  import type { RankedProvider } from '../types.js';
4
4
  import type { RankingStrategy } from '../config.js';
5
5
  import type { ProviderPanelStore } from './provider-panel-store.js';
6
- interface SessionListLike {
6
+ export interface SessionListLike {
7
7
  current?: string | undefined;
8
8
  currentAddress?: unknown | undefined;
9
9
  }
10
- interface GlobalSlotProps {
11
- useSessions<T>(selector: (state: SessionListLike) => T): T;
10
+ /**
11
+ * Observable snapshot of the root sessions list store. DSH >= 0.1.2 no longer
12
+ * injects a `useSessions` render prop into `sidebar.footer.action` /
13
+ * `shell.overlay` occupants, so the components subscribe to the root sessions
14
+ * service directly (the same list store the useSessions hook reads).
15
+ */
16
+ export interface SessionsListStoreLike {
17
+ getSnapshot(): SessionListLike;
18
+ subscribe(listener: () => void): () => void;
12
19
  }
13
20
  export interface ProviderRowView {
14
21
  group: string;
@@ -52,17 +59,18 @@ export declare function providerTriggerState(state: SessionListLike): {
52
59
  disabled: boolean;
53
60
  };
54
61
  export declare function providerTriggerLayout(wide: boolean): CSSProperties;
55
- export declare function ProviderSidebarTrigger(props: GlobalSlotProps & {
62
+ export declare function ProviderSidebarTrigger(props: {
56
63
  wide: boolean;
57
64
  store: ProviderPanelStore;
65
+ sessions: SessionsListStoreLike;
58
66
  }): ReactNode;
59
67
  export declare function freshnessLabel(updatedAt: number, now: number, i18n?: {
60
68
  locale: import("./i18n.js").SupportedLocale;
61
69
  t(key: import("./i18n.js").TranslationKey, params?: Record<string, string | number>): string;
62
70
  }): string;
63
71
  export declare const providerGridColumns = "minmax(110px,1fr) minmax(0,2fr) minmax(54px,auto)";
64
- export declare function ProviderOverlay(props: GlobalSlotProps & {
72
+ export declare function ProviderOverlay(props: {
65
73
  store: ProviderPanelStore;
74
+ sessions: SessionsListStoreLike;
66
75
  }): ReactNode;
67
- export {};
68
76
  //# sourceMappingURL=provider-panel.d.ts.map
@@ -1,16 +1,21 @@
1
1
  import type { Config } from './config.js';
2
2
  import { OpenRouterProviderController, type SessionSelection } from './controller.js';
3
- interface RpcResult<T> {
4
- result: {
5
- ok: true;
6
- value: T;
7
- } | {
8
- ok: false;
9
- error: {
10
- code: string;
11
- message: string;
12
- };
13
- };
3
+ /**
4
+ * Session model catalog surfaced by `ctx.sessionController.modelCatalog()`
5
+ * (structural subset — the full wire type carries more fields).
6
+ */
7
+ interface SessionModelCatalog {
8
+ default: SessionSelection;
9
+ groups?: Array<{
10
+ id: string;
11
+ name: string;
12
+ models: Array<{
13
+ id: string;
14
+ name: string;
15
+ contextWindow?: number;
16
+ [key: string]: unknown;
17
+ }>;
18
+ }>;
14
19
  }
15
20
  export interface HostContextLike {
16
21
  credentials: {
@@ -26,37 +31,22 @@ export interface HostContextLike {
26
31
  value: unknown;
27
32
  }>): Promise<void>;
28
33
  };
29
- apiProxy: {
30
- sessions: {
31
- models(input: {
32
- payload: {
33
- sessionId: string;
34
- };
35
- }): Promise<RpcResult<{
36
- current: SessionSelection;
37
- groups?: Array<{
38
- id: string;
39
- name: string;
40
- models: Array<{
41
- id: string;
42
- name: string;
43
- }>;
44
- }>;
45
- }>>;
46
- selectModel(input: {
47
- payload: {
48
- sessionId: string;
49
- provider: string;
50
- model: string;
51
- reasoningEffort?: string;
52
- };
53
- }): Promise<RpcResult<{
54
- selected: SessionSelection;
55
- }>>;
56
- };
34
+ /**
35
+ * Host Session business API (dsh >= 0.1.2, provided by dsh-api-session-controller).
36
+ * Replaces the removed `apiProxy` RPC service: catalog reads and session model
37
+ * selections now go through this in-process service instead of an HTTP remote.
38
+ */
39
+ sessionController: {
40
+ modelCatalog(): Promise<SessionModelCatalog>;
41
+ selectModel(request: {
42
+ sessionId: string;
43
+ provider: string;
44
+ model: string;
45
+ reasoningEffort?: string;
46
+ }): Promise<unknown>;
57
47
  };
58
48
  agentDefaultModel: {
59
- saveSelection(selection: Omit<SessionSelection, never>): Promise<void>;
49
+ saveSelection(selection: SessionSelection): Promise<void>;
60
50
  };
61
51
  }
62
52
  export declare function createHostController(ctx: HostContextLike, config: () => Config, credentialRef: (name: string) => unknown): OpenRouterProviderController;
@@ -2,8 +2,21 @@ import type { Context } from '@deepseek-ai/cordis';
2
2
  import z from '@deepseek-ai/schemastery';
3
3
  import { type Config as ProviderConfig } from './config.js';
4
4
  export declare const name = "openrouter-providers";
5
+ /**
6
+ * Required host services.
7
+ *
8
+ * `sessionController` (Session Remote owner, provided by dsh-api-session-controller)
9
+ * replaced the pre-0.1.2 `apiProxy` RPC service that was removed from DSH: the
10
+ * panel now reads the shared model catalog and installs session model
11
+ * selections directly through the Host Session business API.
12
+ */
5
13
  export declare const inject: string[];
6
- export declare const NS: import("@deepseek-ai/dsh-settings").SettingsNamespace;
14
+ /**
15
+ * Settings namespace this plugin owns. Registered through the current
16
+ * `ctx.settings.register` API; the older `settingsNamespace()` helper was
17
+ * removed in DSH 0.1.2.
18
+ */
19
+ export declare const NS: "openrouter-providers";
7
20
  export declare const Config: z<ProviderConfig>;
8
21
  export declare function apply(ctx: Context, initial?: ProviderConfig): void;
9
22
  export { OpenRouterProviderDirectory } from './directory.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-openrouter-provider-advisor",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "DSH web plugin that ranks OpenRouter upstream providers by quality, speed, cache-heavy price, context, and uptime, then safely switches the active route.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -12,11 +12,11 @@
12
12
  ],
13
13
  "repository": {
14
14
  "type": "git",
15
- "url": "git+https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor.git"
15
+ "url": "git+https://github.com/xuan666-lab/dsh-openrouter-provider-advisor.git"
16
16
  },
17
- "homepage": "https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor#readme",
17
+ "homepage": "https://github.com/xuan666-lab/dsh-openrouter-provider-advisor#readme",
18
18
  "bugs": {
19
- "url": "https://github.com/xuanfengtechx/dsh-openrouter-provider-advisor/issues"
19
+ "url": "https://github.com/xuan666-lab/dsh-openrouter-provider-advisor/issues"
20
20
  },
21
21
  "publishConfig": {
22
22
  "access": "public"
@@ -44,11 +44,7 @@
44
44
  "platform": "web",
45
45
  "inject": [
46
46
  "@deepseek-ai/dsh-client-runtime",
47
- "@deepseek-ai/dsh-client-locale",
48
- "@deepseek-ai/dsh-client-ui-layout",
49
- "@deepseek-ai/dsh-client-ui-sidebar",
50
- "@deepseek-ai/dsh-client-ui-settings",
51
- "@deepseek-ai/dsh-client-ui-settings-plugins"
47
+ "@deepseek-ai/dsh-client-ui-slots"
52
48
  ]
53
49
  }
54
50
  },
@@ -62,28 +58,14 @@
62
58
  },
63
59
  "peerDependencies": {
64
60
  "@deepseek-ai/cordis": "^4.0.1",
65
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
66
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
67
- "@deepseek-ai/dsh-client-ui-layout": "^0.1.0-rc.8",
68
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
69
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
70
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.0-rc.8",
71
61
  "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
72
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
73
62
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
74
63
  "@deepseek-ai/schemastery": "^3.18.1",
75
64
  "react": ">=18.2.0 <20"
76
65
  },
77
66
  "devDependencies": {
78
67
  "@deepseek-ai/cordis": "^4.0.1",
79
- "@deepseek-ai/dsh-client-locale": "^0.1.0-rc.8",
80
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.8",
81
- "@deepseek-ai/dsh-client-ui-layout": "^0.1.0-rc.8",
82
- "@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.8",
83
- "@deepseek-ai/dsh-client-ui-settings-plugins": "^0.1.0-rc.8",
84
- "@deepseek-ai/dsh-client-ui-sidebar": "^0.1.0-rc.8",
85
68
  "@deepseek-ai/dsh-credentials": "^0.1.0-rc.8",
86
- "@deepseek-ai/dsh-settings": "^0.1.0-rc.8",
87
69
  "@deepseek-ai/dsh-tools": "^0.1.0-rc.8",
88
70
  "@deepseek-ai/schemastery": "^3.18.1",
89
71
  "@types/node": "^22.20.0",