@dan-ai-studio/dshopencodego 0.1.10 → 0.1.12

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.en.md CHANGED
@@ -19,6 +19,8 @@ Prerequisites: a DSH `0.1.7` release (see "Compatibility"); Node `^22.19.0 || >=
19
19
 
20
20
  > **One owner per route**: a profile can have exactly one adapter for `opencode-go`. Remove the previous plugin (`dsh plugin --profile <p> remove @dan-ai-studio/dsh-opencode-go`) or clear any `opencode-go` provider in `llm-pi-ai` first. Otherwise the plugin logs an explicit ownership diagnostic and does not register the route — everything else keeps working.
21
21
 
22
+ > **Using the Desktop app?** It does not install through the CLI — install, upgrade, and remove plugins from its in-app Plugins page. See "Desktop (Electron app)" below.
23
+
22
24
  ### Option 1: npm (recommended)
23
25
 
24
26
  ```sh
@@ -57,6 +59,20 @@ dsh plugin --profile web remove @dan-ai-studio/dshopencodego # remove
57
59
 
58
60
  > pnpm silently reuses a **same-named local tarball** (`added 0`). Rename the file when reinstalling a fresh local build.
59
61
 
62
+ ### Desktop (Electron app)
63
+
64
+ The DSH Desktop app is an Electron shell around the complete dsh Web application, and this plugin fits it the same way: the host half is a standard bundle patch, and the client half declares the `web` platform — the Desktop renders exactly that Web client, so the settings section and the in-conversation usage button come from the same client packages. The Desktop keeps its **own profile** at `$DSH_HOME/profiles/desktop`, independent of `profiles/web`: credentials, model visibility, and plugin settings must be configured again there.
65
+
66
+ Installation **must** go through the in-app UI — do not use the `dsh plugin` commands above:
67
+
68
+ 1. Open the Desktop app → **Settings → Plugins**;
69
+ 2. Enter `@dan-ai-studio/dshopencodego` as the install spec (or pin a version, e.g. `@dan-ai-studio/dshopencodego@0.1.11`), and confirm;
70
+ 3. **Restart the app** when prompted, then enter the API key on the settings page (reference name `OPENCODE_GO_API_KEY`).
71
+
72
+ Why: Electron owns the Desktop profile, and **the CLI cannot boot or mutate it** (Desktop README: *The CLI cannot boot or mutate this profile.*). Plugin management runs through the app's authenticated HTTP APIs and Desktop's bundled pnpm, so no `pnpm` on `PATH` is required. Upgrades and removals happen on the Plugins page too.
73
+
74
+ The version constraint matches the CLI: the DSH bundled with the Desktop app must stay on the `0.1.7` line (`>=0.1.7-alpha.1 <0.1.8`, see "Compatibility"). If the plugin ever breaks startup, the Desktop's native recovery dialog can **disable third-party plugins, back up `cordis.patch.yml`, and restart** in one action — it will not lock the app out.
75
+
60
76
  ## Configuration
61
77
 
62
78
  Configuration lives in the profile's `cordis.patch.yml`:
@@ -108,6 +124,7 @@ The "OpenCode Go" section shows the live gateway catalog (42+ models) with, per
108
124
 
109
125
  - **context / max input / max output**, **release date**, **price per 1M tokens** (from models.dev), and **Go allowance**;
110
126
  - **Go allowance** comes from OpenCode's own documentation (the "usage limits / estimated requests" tables) — **no API exposes it**. It is a transcribed table (source URL and date in the `src/go-limits.ts` header); pricing or promotion changes require updating it and shipping a release;
127
+ - **Capability marks**: the table's new "Capabilities" column labels **structured output / temperature control / open weights** exactly as models.dev declares them. Only a declared "yes" is marked — an unstated or declared "no" stays unmarked, the same rule the reasoning levels follow.
111
128
  - filtering (by name/id, enabled-only, show-deprecated — **deprecated hidden by default**), sorting (newest first, monthly requests, input price, context, name, enabled first), and a list/table view switch.
112
129
 
113
130
  **Default switches**: with no explicit `modelVisibility` entries at all, the **top five models by published monthly request estimate** are enabled (deprecated, unconfigurable, and training-"contributor" models never qualify). The first explicit entry switches the whole list to explicit values. The Host picker and the settings page compute this from one rule, so they never disagree.
package/README.md CHANGED
@@ -19,6 +19,8 @@
19
19
 
20
20
  > **路由互斥**:同一个 profile 里 `opencode-go` 只能由一个适配器提供。装本插件前请先卸载旧插件(`dsh plugin --profile <p> remove @dan-ai-studio/dsh-opencode-go`),或清空 `llm-pi-ai` 配置里名为 `opencode-go` 的 provider。否则插件会记录一条明确的占用诊断,路由不会注册(其余功能照常)。
21
21
 
22
+ > **桌面端用户**:DSH 桌面端不走命令行安装,安装、升级与卸载都在应用内 Plugins 页完成,见文末「桌面端(Electron 应用)」。
23
+
22
24
  ### 方式一:npm(推荐)
23
25
 
24
26
  ```sh
@@ -57,6 +59,20 @@ dsh plugin --profile web remove @dan-ai-studio/dshopencodego # 卸载
57
59
 
58
60
  > pnpm 对**同名本地 tarball** 会静默复用旧内容(`added 0`)。升级本地构建时请改文件名再装。
59
61
 
62
+ ### 桌面端(Electron 应用)
63
+
64
+ DSH 桌面端是 Electron 壳 + 完整的 dsh Web 应用,本插件对它同样适用:host 半边是标准 bundle patch,client 半边声明的平台就是 `web`——桌面端渲染的正是这套 Web 客户端,设置页分区与会话内用量按钮来自同一批客户端包。桌面端使用**自己的 profile** `$DSH_HOME/profiles/desktop`,与 `profiles/web` 互不共享:凭证、模型可见性与插件设置都要在桌面端重新配一次。
65
+
66
+ 安装**必须**走应用内界面,不要使用上面的 `dsh plugin` 命令:
67
+
68
+ 1. 打开桌面端 → **设置 → Plugins(插件)** 页;
69
+ 2. 安装规格填 `@dan-ai-studio/dshopencodego`(或指定版本,如 `@dan-ai-studio/dshopencodego@0.1.11`),确认安装;
70
+ 3. 按提示**重启应用**,再到设置页填 API Key(引用名 `OPENCODE_GO_API_KEY`)。
71
+
72
+ 原因:桌面端 profile 由 Electron 自己拥有,**CLI 不能启动也不能修改它**(桌面端 README 原文:*The CLI cannot boot or mutate this profile.*)。插件管理由应用内插件页经认证 HTTP API 完成,使用应用自带的 pnpm,不依赖 PATH 里的 `pnpm`;升级与卸载同样在 Plugins 页操作。
73
+
74
+ 版本约束与 CLI 一致:桌面端内置的 DSH 要落在 `0.1.7` 线内(`>=0.1.7-alpha.1 <0.1.8`,见「兼容性」)。若插件导致启动失败,桌面端的原生恢复对话框可以一键**禁用第三方插件、备份 `cordis.patch.yml` 后重启**,不会把应用锁死。
75
+
60
76
  ## 配置
61
77
 
62
78
  配置写在 profile 的 `cordis.patch.yml`:
@@ -108,6 +124,7 @@ API Key 通过 Harness 凭证库提供(引用名 `OPENCODE_GO_API_KEY`),
108
124
 
109
125
  - **上下文 / 输入 / 输出**(最大输入只有部分模型有官方数据)、**发布时间**、**单价 /1M**(来自 models.dev)、**Go 额度**;
110
126
  - **Go 额度**来自 OpenCode 官方文档的「使用限制 / 预估请求数」表,**没有接口提供**,是本插件转写的数据表(见 `src/go-limits.ts` 的注释:来源 URL 与转写日期);文档调整价格或促销时需要更新该表并随发版发布。
127
+ - **能力标记**:表格新增「能力」列,按 models.dev 的声明标注**结构化输出 / 温度控制 / 开源权重**。只标注声明为"支持"的项——**未声明或声明不支持都不加标记**,与推理档位同一口径:不替文档下结论。
111
128
  - 筛选(按名称/ID、仅已启用、显示已弃用——**默认隐藏已弃用**)、排序(新发布优先 / 每月预估次数 / 输入单价 / 上下文 / 名称 / 已启用优先)、列表与表格两种视图。
112
129
 
113
130
  **默认开关规则**:没人显式配置过 `modelVisibility` 时,默认启用**每月预估次数最高的 5 个**(跳过已弃用、无法配置、以及"贡献者版"这类以数据换折扣的模型);**一旦有任一显式条目,全部按显式值走**。同一规则同时作用于模型选择器与设置页,两处不会出现不同答案。
package/lib/client.js CHANGED
@@ -74,6 +74,9 @@ function parseModel(value) {
74
74
  ...row["cost"] === void 0 ? {} : { cost: parseCost(row["cost"]) },
75
75
  ...row["protocolSource"] === "builtin" || row["protocolSource"] === "online" || row["protocolSource"] === "inferred" || row["protocolSource"] === "override" ? { protocolSource: row["protocolSource"] } : {},
76
76
  ...typeof row["assumedLimits"] === "boolean" ? { assumedLimits: row["assumedLimits"] } : {},
77
+ ...typeof row["structuredOutput"] === "boolean" ? { structuredOutput: row["structuredOutput"] } : {},
78
+ ...typeof row["temperature"] === "boolean" ? { temperature: row["temperature"] } : {},
79
+ ...typeof row["openWeights"] === "boolean" ? { openWeights: row["openWeights"] } : {},
77
80
  ...typeof row["configurationMissing"] === "string" ? { configurationMissing: row["configurationMissing"] } : {}
78
81
  };
79
82
  }
@@ -265,7 +268,11 @@ var en = {
265
268
  headerIo: "In / Out",
266
269
  headerPrice: "Price /1M",
267
270
  headerQuota: "Go quota",
271
+ headerCapabilities: "Capabilities",
268
272
  headerNotes: "Notes",
273
+ capStructured: "Structured",
274
+ capTemperature: "Temp",
275
+ capOpenWeights: "Open",
269
276
  iosInput: "in",
270
277
  iosOutput: "out",
271
278
  iosContext: "ctx",
@@ -335,7 +342,11 @@ var zh = {
335
342
  headerIo: "\u8F93\u5165 / \u8F93\u51FA",
336
343
  headerPrice: "\u5355\u4EF7 /1M",
337
344
  headerQuota: "Go \u989D\u5EA6",
345
+ headerCapabilities: "\u80FD\u529B",
338
346
  headerNotes: "\u5907\u6CE8",
347
+ capStructured: "\u7ED3\u6784\u5316",
348
+ capTemperature: "\u6E29\u5EA6",
349
+ capOpenWeights: "\u5F00\u6E90",
339
350
  iosInput: "\u5165",
340
351
  iosOutput: "\u51FA",
341
352
  iosContext: "\u4E0A\u4E0B\u6587",
@@ -430,6 +441,13 @@ function visibleModels(models, filter, visibility, locale) {
430
441
  });
431
442
  return sortRows(rows, filter.sort, visibility, locale);
432
443
  }
444
+ function capabilityLabels(model, t) {
445
+ const labels = [];
446
+ if (model.structuredOutput === true) labels.push(t("capStructured"));
447
+ if (model.temperature === true) labels.push(t("capTemperature"));
448
+ if (model.openWeights === true) labels.push(t("capOpenWeights"));
449
+ return labels;
450
+ }
433
451
 
434
452
  // plugin-css:/home/runner/work/dshopencodego/dshopencodego/src/client/section.module.css
435
453
  var id = "@dan-ai-studio/dshopencodego/section.module.css";
@@ -440,7 +458,7 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id) + "]")
440
458
  style.textContent = ".d7lz0W_root{color:var(--dsw-alias-label-primary);flex-direction:column;gap:1.25rem;display:flex}.d7lz0W_title{margin:0;font-size:1.05rem}.d7lz0W_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:.8rem}.d7lz0W_warn{color:var(--dsw-alias-state-warn-primary);margin:0;font-size:.8rem}.d7lz0W_ok{color:var(--dsw-alias-state-success-primary);font-size:.8rem}.d7lz0W_block{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);border-radius:.6rem;flex-direction:column;gap:.5rem;padding:.85rem;display:flex}.d7lz0W_row{flex-wrap:wrap;align-items:center;gap:.5rem;display:flex}.d7lz0W_row>strong{margin-right:auto}.d7lz0W_input{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);min-width:10rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;flex:14rem;padding:.35rem .5rem;font-size:.85rem}.d7lz0W_button{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:.35rem;padding:.35rem .7rem;font-size:.8rem}.d7lz0W_button:disabled{color:var(--dsw-alias-state-idle-primary);cursor:default}.d7lz0W_number{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:5rem;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.d7lz0W_select{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-base);width:auto;max-width:100%;color:var(--dsw-alias-label-primary);font:inherit;border-radius:.35rem;padding:.25rem .4rem;font-size:.8rem}.d7lz0W_segmented{border:1px solid var(--dsw-alias-border-l1);border-radius:.35rem;display:inline-flex;overflow:hidden}.d7lz0W_segment,.d7lz0W_segmentActive{background:var(--dsw-alias-bg-base);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;border:0;padding:.25rem .6rem;font-size:.8rem}.d7lz0W_segmentActive{background:var(--dsw-alias-bg-layer-2);color:var(--dsw-alias-label-primary)}.d7lz0W_tableWrap{overflow-x:auto}.d7lz0W_table{border-collapse:collapse;width:100%;min-width:62rem;font-size:.78rem}.d7lz0W_table th{color:var(--dsw-alias-label-secondary);text-align:left;white-space:nowrap;padding:.25rem .45rem;font-weight:500}.d7lz0W_table td{border-top:1px solid var(--dsw-alias-border-l1);vertical-align:middle;white-space:nowrap;padding:.28rem .45rem}.d7lz0W_toggle{width:2rem}.d7lz0W_toggle input{accent-color:var(--dsw-alias-brand-primary)}.d7lz0W_list{flex-direction:column;display:flex}.d7lz0W_item{border-top:1px solid var(--dsw-alias-border-l1);align-items:flex-start;gap:.5rem;padding:.45rem .15rem;display:flex}.d7lz0W_item input[type=checkbox]{accent-color:var(--dsw-alias-brand-primary);margin-top:.15rem}.d7lz0W_itemBody{flex-direction:column;gap:.15rem;min-width:0;display:flex}.d7lz0W_itemMain{flex-wrap:wrap;align-items:baseline;gap:.6rem;display:flex}.d7lz0W_itemMeta{color:var(--dsw-alias-label-secondary);flex-wrap:wrap;gap:.75rem;font-size:.75rem;display:flex}.d7lz0W_name{color:var(--dsw-alias-label-primary)}.d7lz0W_mono{color:var(--dsw-alias-label-secondary);font-family:ui-monospace,SFMono-Regular,Menlo,monospace}.d7lz0W_badge{color:var(--dsw-alias-brand-primary)}";
441
459
  document.head.appendChild(style);
442
460
  }
443
- var section_default = { "block": "d7lz0W_block", "table": "d7lz0W_table", "ok": "d7lz0W_ok", "row": "d7lz0W_row", "button": "d7lz0W_button", "itemBody": "d7lz0W_itemBody", "mono": "d7lz0W_mono", "title": "d7lz0W_title", "badge": "d7lz0W_badge", "list": "d7lz0W_list", "segment": "d7lz0W_segment", "warn": "d7lz0W_warn", "segmentActive": "d7lz0W_segmentActive", "segmented": "d7lz0W_segmented", "tableWrap": "d7lz0W_tableWrap", "toggle": "d7lz0W_toggle", "root": "d7lz0W_root", "input": "d7lz0W_input", "select": "d7lz0W_select", "item": "d7lz0W_item", "hint": "d7lz0W_hint", "itemMain": "d7lz0W_itemMain", "itemMeta": "d7lz0W_itemMeta", "name": "d7lz0W_name", "number": "d7lz0W_number" };
461
+ var section_default = { "title": "d7lz0W_title", "itemBody": "d7lz0W_itemBody", "segment": "d7lz0W_segment", "ok": "d7lz0W_ok", "select": "d7lz0W_select", "warn": "d7lz0W_warn", "table": "d7lz0W_table", "list": "d7lz0W_list", "itemMeta": "d7lz0W_itemMeta", "mono": "d7lz0W_mono", "name": "d7lz0W_name", "number": "d7lz0W_number", "toggle": "d7lz0W_toggle", "root": "d7lz0W_root", "tableWrap": "d7lz0W_tableWrap", "row": "d7lz0W_row", "hint": "d7lz0W_hint", "button": "d7lz0W_button", "segmentActive": "d7lz0W_segmentActive", "item": "d7lz0W_item", "itemMain": "d7lz0W_itemMain", "badge": "d7lz0W_badge", "block": "d7lz0W_block", "input": "d7lz0W_input", "segmented": "d7lz0W_segmented" };
444
462
 
445
463
  // src/client/Section.tsx
446
464
  var import_jsx_runtime = require("react/jsx-runtime");
@@ -920,10 +938,12 @@ function Section({ controller, t, getLocale }) {
920
938
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerIo") }),
921
939
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerPrice") }),
922
940
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerQuota") }),
941
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerCapabilities") }),
923
942
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("th", { className: section_default.hint, children: t("headerNotes") })
924
943
  ] }) }),
925
944
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("tbody", { children: rows.map((model) => {
926
945
  const badge = badgeFor(model, t);
946
+ const capabilities = capabilityLabels(model, t).join(" \xB7 ");
927
947
  const offered = isOffered(model, visibility);
928
948
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("tr", { children: [
929
949
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.toggle, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -945,11 +965,13 @@ function Section({ controller, t, getLocale }) {
945
965
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: ioLabel(model, t) }),
946
966
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: priceLabel(model) }),
947
967
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: quotaLabel(model, t) }),
968
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: section_default.hint, children: capabilities.length === 0 ? "\u2014" : capabilities }),
948
969
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("td", { className: badge.length === 0 ? section_default.hint : section_default.badge, children: badge })
949
970
  ] }, model.id);
950
971
  }) })
951
972
  ] }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: section_default.list, children: rows.map((model) => {
952
973
  const badge = badgeFor(model, t);
974
+ const capabilities = capabilityLabels(model, t).join(" \xB7 ");
953
975
  const offered = isOffered(model, visibility);
954
976
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.item, children: [
955
977
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -969,6 +991,7 @@ function Section({ controller, t, getLocale }) {
969
991
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.name, children: model.name }),
970
992
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.mono, children: model.id }),
971
993
  model.releaseDate !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: model.releaseDate }),
994
+ capabilities.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.badge, children: capabilities }),
972
995
  badge.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.badge, children: badge })
973
996
  ] }),
974
997
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.itemMeta, children: [
@@ -996,7 +1019,7 @@ if (!document.querySelector("style[data-plugin-css=" + JSON.stringify(id2) + "]"
996
1019
  style.textContent = ".mMNkkW_root{align-items:center;display:inline-flex;position:relative}.mMNkkW_trigger{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-secondary);font:inherit;cursor:pointer;white-space:nowrap;border-radius:999px;padding:.35rem .6rem;font-size:.8rem;line-height:1}.mMNkkW_trigger:hover{color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-border-l2)}.mMNkkW_panel{z-index:20;border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-overlay);width:18rem;color:var(--dsw-alias-label-primary);border-radius:.6rem;flex-direction:column;gap:.6rem;padding:.75rem .85rem;display:flex;position:absolute;bottom:calc(100% + .5rem);right:0;box-shadow:0 8px 24px #0000002e}.mMNkkW_window{flex-direction:column;gap:.25rem;display:flex}.mMNkkW_row{justify-content:space-between;gap:.5rem;font-size:.85rem;display:flex}.mMNkkW_hint{color:var(--dsw-alias-label-secondary);margin:0;font-size:.75rem}.mMNkkW_progress{width:100%;height:.35rem;accent-color:var(--dsw-alias-brand-primary)}.mMNkkW_limited{color:var(--dsw-alias-state-warn-primary);font-size:.75rem}.mMNkkW_warning{border:1px solid var(--dsw-alias-state-warn-primary);color:var(--dsw-alias-label-primary);border-radius:.4rem;padding:.4rem .5rem;font-size:.75rem}.mMNkkW_retry{border:1px solid var(--dsw-alias-border-l1);background:var(--dsw-alias-bg-layer-1);color:var(--dsw-alias-label-primary);font:inherit;cursor:pointer;border-radius:.35rem;align-self:flex-start;padding:.25rem .5rem;font-size:.75rem}.mMNkkW_local{border-top:1px solid var(--dsw-alias-border-l1);flex-direction:column;gap:.2rem;padding-top:.5rem;display:flex}";
997
1020
  document.head.appendChild(style);
998
1021
  }
999
- var pill_default = { "root": "mMNkkW_root", "window": "mMNkkW_window", "limited": "mMNkkW_limited", "progress": "mMNkkW_progress", "row": "mMNkkW_row", "retry": "mMNkkW_retry", "local": "mMNkkW_local", "hint": "mMNkkW_hint", "panel": "mMNkkW_panel", "warning": "mMNkkW_warning", "trigger": "mMNkkW_trigger" };
1022
+ var pill_default = { "panel": "mMNkkW_panel", "warning": "mMNkkW_warning", "hint": "mMNkkW_hint", "retry": "mMNkkW_retry", "window": "mMNkkW_window", "row": "mMNkkW_row", "root": "mMNkkW_root", "trigger": "mMNkkW_trigger", "progress": "mMNkkW_progress", "limited": "mMNkkW_limited", "local": "mMNkkW_local" };
1000
1023
 
1001
1024
  // src/client/UsagePill.tsx
1002
1025
  var import_jsx_runtime2 = require("react/jsx-runtime");
package/lib/index.js CHANGED
@@ -212,6 +212,9 @@ function positiveInteger(value) {
212
212
  function nonEmptyString(value) {
213
213
  return typeof value === "string" && value.length > 0 ? value : void 0;
214
214
  }
215
+ function declaredBoolean(value) {
216
+ return typeof value === "boolean" ? value : void 0;
217
+ }
215
218
  function validReleaseDate(value) {
216
219
  if (typeof value !== "string" || !/^\d{4}-\d{2}-\d{2}$/.test(value)) return void 0;
217
220
  return Number.isFinite(Date.parse(value)) && new Date(value).toISOString().slice(0, 10) === value ? value : void 0;
@@ -343,6 +346,9 @@ function readOnlineMetadata(body, sources) {
343
346
  input,
344
347
  reasoning,
345
348
  thinkingLevelMap: reasoning ? thinkingLevels(metadata, exact?.api === api ? exact : sibling) : void 0,
349
+ structuredOutput: declaredBoolean(metadata["structured_output"]),
350
+ temperature: declaredBoolean(metadata["temperature"]),
351
+ openWeights: declaredBoolean(metadata["open_weights"]),
346
352
  compat: compatFor(api, metadata, exact, sibling),
347
353
  cost,
348
354
  deprecated: metadata["status"] === "deprecated",
@@ -536,6 +542,11 @@ var OpencodeGoCatalog = class {
536
542
  input: exact?.input ?? this.options.defaults.input,
537
543
  reasoning: exact?.reasoning ?? false,
538
544
  thinkingLevelMap: exact?.thinkingLevelMap,
545
+ // No source describes these, so the page says nothing instead of
546
+ // guessing: the installed catalog knows wire shapes, not capabilities.
547
+ structuredOutput: void 0,
548
+ temperature: void 0,
549
+ openWeights: void 0,
539
550
  compat: exact?.compat ?? {},
540
551
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
541
552
  deprecated: false,
@@ -1513,6 +1524,9 @@ function parseModel(value) {
1513
1524
  ...row["cost"] === void 0 ? {} : { cost: parseCost(row["cost"]) },
1514
1525
  ...row["protocolSource"] === "builtin" || row["protocolSource"] === "online" || row["protocolSource"] === "inferred" || row["protocolSource"] === "override" ? { protocolSource: row["protocolSource"] } : {},
1515
1526
  ...typeof row["assumedLimits"] === "boolean" ? { assumedLimits: row["assumedLimits"] } : {},
1527
+ ...typeof row["structuredOutput"] === "boolean" ? { structuredOutput: row["structuredOutput"] } : {},
1528
+ ...typeof row["temperature"] === "boolean" ? { temperature: row["temperature"] } : {},
1529
+ ...typeof row["openWeights"] === "boolean" ? { openWeights: row["openWeights"] } : {},
1516
1530
  ...typeof row["configurationMissing"] === "string" ? { configurationMissing: row["configurationMissing"] } : {}
1517
1531
  };
1518
1532
  }
@@ -1702,7 +1716,10 @@ function catalogReading(snapshot, visibility, listingFailure) {
1702
1716
  }
1703
1717
  },
1704
1718
  protocolSource: fact.protocolSource,
1705
- assumedLimits: fact.assumedLimits
1719
+ assumedLimits: fact.assumedLimits,
1720
+ ...fact.structuredOutput === void 0 ? {} : { structuredOutput: fact.structuredOutput },
1721
+ ...fact.temperature === void 0 ? {} : { temperature: fact.temperature },
1722
+ ...fact.openWeights === void 0 ? {} : { openWeights: fact.openWeights }
1706
1723
  };
1707
1724
  }),
1708
1725
  ...[...snapshot.unavailable].map(([id, reason]) => ({ id, name: id, configurationMissing: reason }))
@@ -64,6 +64,9 @@ function parseModel(value) {
64
64
  || row['protocolSource'] === 'inferred' || row['protocolSource'] === 'override'
65
65
  ? { protocolSource: row['protocolSource'] } : {},
66
66
  ...typeof row['assumedLimits'] === 'boolean' ? { assumedLimits: row['assumedLimits'] } : {},
67
+ ...typeof row['structuredOutput'] === 'boolean' ? { structuredOutput: row['structuredOutput'] } : {},
68
+ ...typeof row['temperature'] === 'boolean' ? { temperature: row['temperature'] } : {},
69
+ ...typeof row['openWeights'] === 'boolean' ? { openWeights: row['openWeights'] } : {},
67
70
  ...typeof row['configurationMissing'] === 'string' ? { configurationMissing: row['configurationMissing'] } : {},
68
71
  };
69
72
  }
@@ -208,6 +208,11 @@ export class OpencodeGoCatalog {
208
208
  input: exact?.input ?? this.options.defaults.input,
209
209
  reasoning: exact?.reasoning ?? false,
210
210
  thinkingLevelMap: exact?.thinkingLevelMap,
211
+ // No source describes these, so the page says nothing instead of
212
+ // guessing: the installed catalog knows wire shapes, not capabilities.
213
+ structuredOutput: undefined,
214
+ temperature: undefined,
215
+ openWeights: undefined,
211
216
  compat: exact?.compat ?? {},
212
217
  cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
213
218
  deprecated: false,
@@ -2,8 +2,8 @@
2
2
  * Online model metadata: how to call each advertised model.
3
3
  *
4
4
  * models.dev is the only source that knows about a model the day it ships, so
5
- * it supplies names, capacities, modalities, pricing, lifecycle, and the
6
- * protocol hint. The installed pi-ai catalog still wins where it knows the
5
+ * it supplies names, capacities, modalities, pricing, lifecycle, the declared
6
+ * capability facts, and the protocol hint. The installed pi-ai catalog still wins where it knows the
7
7
  * exact id, because its entries carry the wire quirks (`thinkingFormat`,
8
8
  * reasoning-content replay, affinity format) that a generic metadata document
9
9
  * cannot express.
@@ -32,6 +32,14 @@ export interface ModelFacts {
32
32
  readonly input: readonly ('text' | 'image')[];
33
33
  readonly reasoning: boolean;
34
34
  readonly thinkingLevelMap: ThinkingLevelMap | undefined;
35
+ /**
36
+ * Declared capability facts, exactly as models.dev states them. `undefined`
37
+ * means the document says nothing about the capability, which is not the
38
+ * same answer as `false` and must never be rendered as "unsupported".
39
+ */
40
+ readonly structuredOutput: boolean | undefined;
41
+ readonly temperature: boolean | undefined;
42
+ readonly openWeights: boolean | undefined;
35
43
  readonly compat: Model<Api>['compat'];
36
44
  readonly cost: ModelCost;
37
45
  readonly deprecated: boolean;
@@ -2,8 +2,8 @@
2
2
  * Online model metadata: how to call each advertised model.
3
3
  *
4
4
  * models.dev is the only source that knows about a model the day it ships, so
5
- * it supplies names, capacities, modalities, pricing, lifecycle, and the
6
- * protocol hint. The installed pi-ai catalog still wins where it knows the
5
+ * it supplies names, capacities, modalities, pricing, lifecycle, the declared
6
+ * capability facts, and the protocol hint. The installed pi-ai catalog still wins where it knows the
7
7
  * exact id, because its entries carry the wire quirks (`thinkingFormat`,
8
8
  * reasoning-content replay, affinity format) that a generic metadata document
9
9
  * cannot express.
@@ -27,6 +27,10 @@ function positiveInteger(value) {
27
27
  function nonEmptyString(value) {
28
28
  return typeof value === 'string' && value.length > 0 ? value : undefined;
29
29
  }
30
+ /** A capability the document states as yes or no; silence stays silence. */
31
+ function declaredBoolean(value) {
32
+ return typeof value === 'boolean' ? value : undefined;
33
+ }
30
34
  /** A calendar date models.dev states without a timezone. */
31
35
  function validReleaseDate(value) {
32
36
  if (typeof value !== 'string' || !/^\d{4}-\d{2}-\d{2}$/.test(value))
@@ -223,6 +227,9 @@ export function readOnlineMetadata(body, sources) {
223
227
  input,
224
228
  reasoning,
225
229
  thinkingLevelMap: reasoning ? thinkingLevels(metadata, exact?.api === api ? exact : sibling) : undefined,
230
+ structuredOutput: declaredBoolean(metadata['structured_output']),
231
+ temperature: declaredBoolean(metadata['temperature']),
232
+ openWeights: declaredBoolean(metadata['open_weights']),
226
233
  compat: compatFor(api, metadata, exact, sibling),
227
234
  cost,
228
235
  deprecated: metadata['status'] === 'deprecated',
@@ -43,6 +43,9 @@ export function catalogReading(snapshot, visibility, listingFailure) {
43
43
  },
44
44
  protocolSource: fact.protocolSource,
45
45
  assumedLimits: fact.assumedLimits,
46
+ ...fact.structuredOutput === undefined ? {} : { structuredOutput: fact.structuredOutput },
47
+ ...fact.temperature === undefined ? {} : { temperature: fact.temperature },
48
+ ...fact.openWeights === undefined ? {} : { openWeights: fact.openWeights },
46
49
  };
47
50
  }),
48
51
  ...[...snapshot.unavailable].map(([id, reason]) => ({ id, name: id, configurationMissing: reason })),
@@ -36,6 +36,14 @@ export interface ModelSummary {
36
36
  };
37
37
  /** Which ladder level decided this model's protocol. */
38
38
  readonly protocolSource?: ProtocolSource;
39
+ /**
40
+ * Capabilities models.dev declares, in its own words. Each is absent when the
41
+ * document is silent about it — absence is "unstated", never "unsupported",
42
+ * so the page leaves the cell empty instead of showing a negative.
43
+ */
44
+ readonly structuredOutput?: boolean;
45
+ readonly temperature?: boolean;
46
+ readonly openWeights?: boolean;
39
47
  /** True when a capacity came from the route default rather than a source. */
40
48
  readonly assumedLimits?: boolean;
41
49
  /** Advertised by the gateway but not configurable, with the reason. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dan-ai-studio/dshopencodego",
3
- "version": "0.1.10",
3
+ "version": "0.1.12",
4
4
  "description": "OpenCode Go provider for DeepSeek Harness: live gateway catalog, per-conversation session header, usage",
5
5
  "type": "module",
6
6
  "license": "MIT",