@dan-ai-studio/dshopencodego 0.1.91 → 0.1.92
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 +3 -1
- package/README.md +3 -1
- package/lib/client.js +180 -4
- package/lib/index.js +260 -21
- package/lib/types/catalog/go-doc.d.ts +6 -5
- package/lib/types/desktop/contract.d.ts +25 -0
- package/lib/types/desktop/feed.d.ts +96 -0
- package/lib/types/desktop/index.d.ts +10 -0
- package/lib/types/desktop/service.d.ts +30 -0
- package/package.json +1 -1
package/README.en.md
CHANGED
|
@@ -82,6 +82,8 @@ Why: Electron owns the Desktop profile, and **the CLI cannot boot or mutate it**
|
|
|
82
82
|
|
|
83
83
|
The version constraint matches the CLI: the DSH bundled with the Desktop app must stay inside the declared and verified band (`>=0.2.0-rc.1 <0.3.0`, 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.
|
|
84
84
|
|
|
85
|
+
The settings page also carries a **Desktop app** row: **Check download address** reads the official feed at `https://download.deepseek.com/dsh-desk/feeds/win-x64/nightly.yml` and shows the newest installer's version, publication date, size, staged-rollout share, SHA-512, and address — the address can be copied or opened in a browser. This **only reads the feed and shows the address**: the plugin never downloads, verifies, or installs anything. The Host caches the reading for six hours, and an unreachable feed says so instead of pretending nothing was published.
|
|
86
|
+
|
|
85
87
|
## Configuration
|
|
86
88
|
|
|
87
89
|
Configuration lives in the profile's `cordis.patch.yml`:
|
|
@@ -291,7 +293,7 @@ npm run seed # refresh the bundled catalog seed before a release (see "Re
|
|
|
291
293
|
|
|
292
294
|
**Order matters**: `npm run build` must come before `npm test` — `tests/pack-contents.spec.ts` walks every `exports` target, and whether `lib/` exists decides whether that check runs or is silently skipped (it does not fail, it just stops protecting you). `package.json` no longer carries `prepare`, so `npm ci` will not build for you; all three CI workflows run `npm run build` explicitly.
|
|
293
295
|
|
|
294
|
-
The suite covers configuration validation, the protocol ladder, gateway and online metadata, the catalog projection, the adapter on the wire (all three protocols), Remote contracts and services, session-header invariants, usage windows and the meter, the client controller and usage pill, both halves' mounting and registration, the error mapping for stream idle timeouts and aborts, the exit-code contract of the verification scripts, and several **structural invariants** (inject declarations match the source's reads, peer coverage holds both ways, the READMEs match the manifest, and the READMEs' claims about the upstream match the upstream actually installed). That is **
|
|
296
|
+
The suite covers configuration validation, the protocol ladder, gateway and online metadata, the catalog projection, the adapter on the wire (all three protocols), Remote contracts and services, session-header invariants, usage windows and the meter, the client controller and usage pill, both halves' mounting and registration, the error mapping for stream idle timeouts and aborts, the exit-code contract of the verification scripts, and several **structural invariants** (inject declarations match the source's reads, peer coverage holds both ways, the READMEs match the manifest, and the READMEs' claims about the upstream match the upstream actually installed). That is **64 test files** — a number `tests/readme-consistency.spec.ts` checks against the `tests/` directory, so it cannot drift.
|
|
295
297
|
|
|
296
298
|
## Releasing (maintainers)
|
|
297
299
|
|
package/README.md
CHANGED
|
@@ -82,6 +82,8 @@ DSH 桌面端是 Electron 壳 + 完整的 dsh Web 应用,本插件对它同样
|
|
|
82
82
|
|
|
83
83
|
版本约束与 CLI 一致:桌面端内置的 DSH 要落在已声明并验证过的版本带内(`>=0.2.0-rc.1 <0.3.0`,见「兼容性」)。若插件导致启动失败,桌面端的原生恢复对话框可以一键**禁用第三方插件、备份 `cordis.patch.yml` 后重启**,不会把应用锁死。
|
|
84
84
|
|
|
85
|
+
设置页里另有一栏**「桌面版」**:点「检查下载地址」会读取官方发布源 `https://download.deepseek.com/dsh-desk/feeds/win-x64/nightly.yml`,显示当前最新安装包的版本、发布时间、大小、灰度比例、SHA-512 与下载地址,地址可一键复制或用浏览器打开。这里**只读发布源、只显示地址**——插件不会下载、校验或安装任何东西。结果在 Host 侧缓存 6 小时,发布源没响应时会明确说「下载站没有响应」,而不是假装「没有新版本」。
|
|
86
|
+
|
|
85
87
|
## 配置
|
|
86
88
|
|
|
87
89
|
配置写在 profile 的 `cordis.patch.yml`:
|
|
@@ -290,7 +292,7 @@ npm run seed # 发版前刷新捆绑的目录种子(见「发布」)
|
|
|
290
292
|
|
|
291
293
|
**顺序有讲究**:`npm run build` 要排在 `npm test` 之前——`tests/pack-contents.spec.ts` 会遍历 `exports` 的每个目标,`lib/` 存在与否决定这条检查是执行还是被跳过(不报错,但会少一层保护)。`package.json` 已移除 `prepare`,因此 `npm ci` 不会再替你隐式构建;三个 CI workflow 都显式跑了 `npm run build`。
|
|
292
294
|
|
|
293
|
-
测试覆盖配置校验、协议阶梯、网关与在线元数据、目录投影、适配器(三种协议的线上请求)、Remote 契约与服务、会话头不变量、用量窗口与计量、客户端控制器与用量组件、两半的挂载与登记、流空闲超时与中止的错误映射、验证脚本的退出码契约、以及几条**结构性不变量**(inject 声明与源码读取一致、peer 覆盖双向、README 与 manifest 一致、README 对上游的断言与实装的上游一致)。共 **
|
|
295
|
+
测试覆盖配置校验、协议阶梯、网关与在线元数据、目录投影、适配器(三种协议的线上请求)、Remote 契约与服务、会话头不变量、用量窗口与计量、客户端控制器与用量组件、两半的挂载与登记、流空闲超时与中止的错误映射、验证脚本的退出码契约、以及几条**结构性不变量**(inject 声明与源码读取一致、peer 覆盖双向、README 与 manifest 一致、README 对上游的断言与实装的上游一致)。共 **64 个测试文件**——这个数字由 `tests/readme-consistency.spec.ts` 对着 `tests/` 目录校验,不会漂移。
|
|
294
296
|
|
|
295
297
|
## 发布(维护者)
|
|
296
298
|
|
package/lib/client.js
CHANGED
|
@@ -717,7 +717,23 @@ var en = {
|
|
|
717
717
|
updateInspectRefused: "The published target could not be verified, so the installed version was left untouched:",
|
|
718
718
|
updateStopProfile: "The running version cannot be removed without a restart; restart DSH first, then retry.",
|
|
719
719
|
updateIncompatible: "The new version is incompatible with this DSH build; finish the update from the plugin manager page (a version exemption needs your explicit approval).",
|
|
720
|
-
updatePendingBuilds: "Installation needs build-script approval; approve it on the plugin manager page, then retry."
|
|
720
|
+
updatePendingBuilds: "Installation needs build-script approval; approve it on the plugin manager page, then retry.",
|
|
721
|
+
desktopTitle: "Desktop app",
|
|
722
|
+
desktopHint: "The newest installer published for this platform. The address is shown, never downloaded \u2014 copy it or open it in a browser.",
|
|
723
|
+
desktopCheck: "Check download address",
|
|
724
|
+
desktopChecking: "Checking the download host\u2026",
|
|
725
|
+
desktopVersion: "Newest build:",
|
|
726
|
+
desktopPublished: "published",
|
|
727
|
+
desktopSize: "size",
|
|
728
|
+
desktopStaging: "staged rollout",
|
|
729
|
+
desktopCopy: "Copy address",
|
|
730
|
+
desktopCopied: "Copied",
|
|
731
|
+
desktopCopyFailed: "The address could not be copied; select it and copy by hand.",
|
|
732
|
+
desktopOpen: "Open download",
|
|
733
|
+
desktopUnavailable: "The download host did not answer, so no installer address was read; choose Check download address to retry.",
|
|
734
|
+
desktopFailed: "The check failed:",
|
|
735
|
+
desktopRemoteMissing: "The desktop download service is not ready yet; choose Check download address to retry.",
|
|
736
|
+
desktopNoRelease: "The download host answered, but published no installer for this platform."
|
|
721
737
|
};
|
|
722
738
|
var zh = {
|
|
723
739
|
nav: "OpenCode Go",
|
|
@@ -929,7 +945,23 @@ var zh = {
|
|
|
929
945
|
updateInspectRefused: "\u65E0\u6CD5\u9A8C\u8BC1\u53D1\u5E03\u7684\u76EE\u6807\uFF0C\u5DF2\u4FDD\u7559\u5F53\u524D\u7248\u672C\u672A\u505A\u6539\u52A8\uFF1A",
|
|
930
946
|
updateStopProfile: "\u8FD0\u884C\u4E2D\u7684\u7248\u672C\u9700\u91CD\u542F\u540E\u624D\u80FD\u79FB\u9664\uFF1B\u8BF7\u5148\u91CD\u542F DSH \u518D\u91CD\u8BD5\u3002",
|
|
931
947
|
updateIncompatible: "\u65B0\u7248\u672C\u4E0E\u5F53\u524D DSH \u4E0D\u517C\u5BB9\uFF0C\u8BF7\u524D\u5F80\u63D2\u4EF6\u7BA1\u7406\u9875\u5B8C\u6210\u66F4\u65B0\uFF08\u7248\u672C\u8C41\u514D\u9700\u660E\u786E\u6279\u51C6\uFF09\u3002",
|
|
932
|
-
updatePendingBuilds: "\u5B89\u88C5\u9700\u8981\u6279\u51C6\u6784\u5EFA\u811A\u672C\uFF1B\u8BF7\u524D\u5F80\u63D2\u4EF6\u7BA1\u7406\u9875\u6279\u51C6\u540E\u518D\u91CD\u8BD5\u3002"
|
|
948
|
+
updatePendingBuilds: "\u5B89\u88C5\u9700\u8981\u6279\u51C6\u6784\u5EFA\u811A\u672C\uFF1B\u8BF7\u524D\u5F80\u63D2\u4EF6\u7BA1\u7406\u9875\u6279\u51C6\u540E\u518D\u91CD\u8BD5\u3002",
|
|
949
|
+
desktopTitle: "\u684C\u9762\u7248",
|
|
950
|
+
desktopHint: "\u5F53\u524D\u5E73\u53F0\u6700\u65B0\u53D1\u5E03\u7684\u5B89\u88C5\u5305\u3002\u8FD9\u91CC\u53EA\u663E\u793A\u5730\u5740\uFF0C\u4E0D\u4F1A\u4E0B\u8F7D\u2014\u2014\u53EF\u4EE5\u590D\u5236\uFF0C\u6216\u7528\u6D4F\u89C8\u5668\u6253\u5F00\u3002",
|
|
951
|
+
desktopCheck: "\u68C0\u67E5\u4E0B\u8F7D\u5730\u5740",
|
|
952
|
+
desktopChecking: "\u6B63\u5728\u67E5\u8BE2\u4E0B\u8F7D\u7AD9\u2026",
|
|
953
|
+
desktopVersion: "\u6700\u65B0\u7248\u672C\uFF1A",
|
|
954
|
+
desktopPublished: "\u53D1\u5E03\u4E8E",
|
|
955
|
+
desktopSize: "\u5927\u5C0F",
|
|
956
|
+
desktopStaging: "\u7070\u5EA6\u6BD4\u4F8B",
|
|
957
|
+
desktopCopy: "\u590D\u5236\u5730\u5740",
|
|
958
|
+
desktopCopied: "\u5DF2\u590D\u5236",
|
|
959
|
+
desktopCopyFailed: "\u5730\u5740\u590D\u5236\u5931\u8D25\uFF1B\u8BF7\u624B\u52A8\u9009\u4E2D\u540E\u590D\u5236\u3002",
|
|
960
|
+
desktopOpen: "\u6253\u5F00\u4E0B\u8F7D",
|
|
961
|
+
desktopUnavailable: "\u4E0B\u8F7D\u7AD9\u6CA1\u6709\u54CD\u5E94\uFF0C\u672A\u83B7\u53D6\u5230\u5B89\u88C5\u5305\u5730\u5740\uFF1B\u8BF7\u70B9\u51FB\u201C\u68C0\u67E5\u4E0B\u8F7D\u5730\u5740\u201D\u91CD\u8BD5\u3002",
|
|
962
|
+
desktopFailed: "\u68C0\u67E5\u5931\u8D25\uFF1A",
|
|
963
|
+
desktopRemoteMissing: "\u684C\u9762\u7248\u4E0B\u8F7D\u670D\u52A1\u5C1A\u672A\u5C31\u7EEA\uFF1B\u8BF7\u70B9\u51FB\u201C\u68C0\u67E5\u4E0B\u8F7D\u5730\u5740\u201D\u91CD\u8BD5\u3002",
|
|
964
|
+
desktopNoRelease: "\u4E0B\u8F7D\u7AD9\u6709\u54CD\u5E94\uFF0C\u4F46\u6CA1\u6709\u4E3A\u8BE5\u5E73\u53F0\u53D1\u5E03\u5B89\u88C5\u5305\u3002"
|
|
933
965
|
};
|
|
934
966
|
|
|
935
967
|
// src/client/Section.tsx
|
|
@@ -1113,7 +1145,8 @@ var INITIAL = {
|
|
|
1113
1145
|
writable: false,
|
|
1114
1146
|
saving: false,
|
|
1115
1147
|
filter: INITIAL_FILTER,
|
|
1116
|
-
update: { phase: "idle", status: void 0, message: void 0 }
|
|
1148
|
+
update: { phase: "idle", status: void 0, message: void 0 },
|
|
1149
|
+
desktop: { phase: "idle", reading: void 0, message: void 0, copied: false }
|
|
1117
1150
|
};
|
|
1118
1151
|
function settingsOf(value) {
|
|
1119
1152
|
if (value === null || typeof value !== "object") return void 0;
|
|
@@ -1166,6 +1199,7 @@ var SectionController = class _SectionController {
|
|
|
1166
1199
|
void this.loadKey();
|
|
1167
1200
|
void this.loadNetwork();
|
|
1168
1201
|
void this.checkUpdate(false);
|
|
1202
|
+
void this.checkDesktop(false);
|
|
1169
1203
|
}
|
|
1170
1204
|
subscribe = (listener) => {
|
|
1171
1205
|
this.listeners.add(listener);
|
|
@@ -1375,6 +1409,71 @@ var SectionController = class _SectionController {
|
|
|
1375
1409
|
});
|
|
1376
1410
|
}
|
|
1377
1411
|
}
|
|
1412
|
+
/**
|
|
1413
|
+
* The desktop download Remote.
|
|
1414
|
+
*
|
|
1415
|
+
* Declared in `client/index.ts`'s inject list, so the namespace is mounted by
|
|
1416
|
+
* the time this controller exists; the guard is a defensive assertion, not a
|
|
1417
|
+
* wait, for the same reason the update Remote's is.
|
|
1418
|
+
* @returns the mounted desktop Remote.
|
|
1419
|
+
* @throws {Error} `remote-not-mounted` when the namespace is absent.
|
|
1420
|
+
*/
|
|
1421
|
+
desktopRemote() {
|
|
1422
|
+
const remote = this.services.remote.opencodeGoDesktop;
|
|
1423
|
+
if (remote === void 0) throw new Error("remote-not-mounted");
|
|
1424
|
+
return remote;
|
|
1425
|
+
}
|
|
1426
|
+
/**
|
|
1427
|
+
* Ask the Host which installer the desktop download host publishes.
|
|
1428
|
+
*
|
|
1429
|
+
* The Host answers from a cached feed reading and fails silent, so an
|
|
1430
|
+
* unreachable download host still answers "no installer known" rather than
|
|
1431
|
+
* erroring the page. Runs once with the page and again only on explicit
|
|
1432
|
+
* refresh — same shape as the update check, and for the same reason: both
|
|
1433
|
+
* wire methods take zero arguments.
|
|
1434
|
+
*/
|
|
1435
|
+
async checkDesktop(force) {
|
|
1436
|
+
const previous = this.state.desktop;
|
|
1437
|
+
this.set({ desktop: { ...previous, phase: "checking", message: void 0, copied: false } });
|
|
1438
|
+
try {
|
|
1439
|
+
const remote = this.desktopRemote();
|
|
1440
|
+
const result = force ? await remote.refreshDesktop() : await remote.readDesktop();
|
|
1441
|
+
if (!result.ok) throw result.error;
|
|
1442
|
+
const reading = result.value;
|
|
1443
|
+
this.set({
|
|
1444
|
+
desktop: {
|
|
1445
|
+
// An unanswered feed is not "nothing published": rendering the two
|
|
1446
|
+
// alike would report a dead download host as an empty channel.
|
|
1447
|
+
phase: !reading.checked ? "failed" : reading.release === void 0 ? "empty" : "ready",
|
|
1448
|
+
reading,
|
|
1449
|
+
message: reading.checked ? void 0 : "feed-unreachable",
|
|
1450
|
+
copied: false
|
|
1451
|
+
}
|
|
1452
|
+
});
|
|
1453
|
+
} catch (error) {
|
|
1454
|
+
this.set({
|
|
1455
|
+
desktop: {
|
|
1456
|
+
phase: "failed",
|
|
1457
|
+
reading: previous.reading,
|
|
1458
|
+
message: error instanceof Error ? error.message : "the download check failed",
|
|
1459
|
+
copied: false
|
|
1460
|
+
}
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
}
|
|
1464
|
+
/** Copy the installer address to the clipboard; the address stays on screen either way. */
|
|
1465
|
+
async copyDesktopAddress() {
|
|
1466
|
+
const url = this.state.desktop.reading?.release?.url;
|
|
1467
|
+
if (url === void 0) return;
|
|
1468
|
+
try {
|
|
1469
|
+
const clipboard = globalThis.navigator?.clipboard;
|
|
1470
|
+
if (clipboard === void 0) throw new Error("no clipboard");
|
|
1471
|
+
await clipboard.writeText(url);
|
|
1472
|
+
this.set({ desktop: { ...this.state.desktop, copied: true, message: void 0 } });
|
|
1473
|
+
} catch {
|
|
1474
|
+
this.set({ desktop: { ...this.state.desktop, copied: false, message: "copy-failed" } });
|
|
1475
|
+
}
|
|
1476
|
+
}
|
|
1378
1477
|
/** Arm the confirm step; the destructive two-step runs only from `applyUpdate`. */
|
|
1379
1478
|
armUpdate() {
|
|
1380
1479
|
if (this.state.update.phase === "ready") {
|
|
@@ -1804,6 +1903,82 @@ function updateFailureText(message, t) {
|
|
|
1804
1903
|
if (message === "pending-builds") return t("updatePendingBuilds");
|
|
1805
1904
|
return message;
|
|
1806
1905
|
}
|
|
1906
|
+
function formatBytes(bytes2) {
|
|
1907
|
+
const units = ["B", "KB", "MB", "GB", "TB"];
|
|
1908
|
+
let value = bytes2;
|
|
1909
|
+
let unit = 0;
|
|
1910
|
+
while (value >= 1024 && unit < units.length - 1) {
|
|
1911
|
+
value /= 1024;
|
|
1912
|
+
unit += 1;
|
|
1913
|
+
}
|
|
1914
|
+
const rounded = unit === 0 || value >= 10 ? Math.round(value) : Math.round(value * 10) / 10;
|
|
1915
|
+
return `${rounded} ${units[unit] ?? "B"}`;
|
|
1916
|
+
}
|
|
1917
|
+
function formatReleaseDate(value) {
|
|
1918
|
+
const at = new Date(value);
|
|
1919
|
+
if (Number.isNaN(at.getTime())) return value;
|
|
1920
|
+
return at.toLocaleString(void 0, { dateStyle: "medium", timeStyle: "short" });
|
|
1921
|
+
}
|
|
1922
|
+
function DesktopBanner({ controller, t, busy, act, desktop }) {
|
|
1923
|
+
if (desktop.phase === "idle") return null;
|
|
1924
|
+
const release = desktop.reading?.release;
|
|
1925
|
+
const check = () => {
|
|
1926
|
+
void act(async () => {
|
|
1927
|
+
await controller.checkDesktop(true);
|
|
1928
|
+
});
|
|
1929
|
+
};
|
|
1930
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.block, children: [
|
|
1931
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
1932
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("desktopTitle") }),
|
|
1933
|
+
desktop.phase === "checking" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: t("desktopChecking") }),
|
|
1934
|
+
desktop.phase === "ready" && release !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1935
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: `${t("desktopVersion")} ${release.version}` }),
|
|
1936
|
+
release.releaseDate !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: `${t("desktopPublished")} ${formatReleaseDate(release.releaseDate)}` }),
|
|
1937
|
+
release.size !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: `${t("desktopSize")} ${formatBytes(release.size)}` }),
|
|
1938
|
+
release.stagingPercentage !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: `${t("desktopStaging")} ${release.stagingPercentage}%` }),
|
|
1939
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: section_default.button, disabled: busy, onClick: check, children: t("desktopCheck") })
|
|
1940
|
+
] }),
|
|
1941
|
+
desktop.phase === "empty" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1942
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: t("desktopNoRelease") }),
|
|
1943
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: section_default.button, disabled: busy, onClick: check, children: t("desktopCheck") })
|
|
1944
|
+
] }),
|
|
1945
|
+
desktop.phase === "failed" && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1946
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.warn, children: t("desktopFailed") }),
|
|
1947
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.hint, children: desktopFailureText(desktop.message, t) }),
|
|
1948
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: section_default.button, disabled: busy, onClick: check, children: t("desktopCheck") })
|
|
1949
|
+
] })
|
|
1950
|
+
] }),
|
|
1951
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: section_default.hint, children: t("desktopHint") }),
|
|
1952
|
+
desktop.phase === "ready" && release !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1953
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
1954
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: section_default.mono, children: release.url }),
|
|
1955
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1956
|
+
"button",
|
|
1957
|
+
{
|
|
1958
|
+
type: "button",
|
|
1959
|
+
className: section_default.button,
|
|
1960
|
+
disabled: busy,
|
|
1961
|
+
onClick: () => {
|
|
1962
|
+
void act(async () => {
|
|
1963
|
+
await controller.copyDesktopAddress();
|
|
1964
|
+
});
|
|
1965
|
+
},
|
|
1966
|
+
children: desktop.copied ? t("desktopCopied") : t("desktopCopy")
|
|
1967
|
+
}
|
|
1968
|
+
),
|
|
1969
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { className: section_default.button, href: release.url, target: "_blank", rel: "noreferrer", children: t("desktopOpen") })
|
|
1970
|
+
] }),
|
|
1971
|
+
desktop.message === "copy-failed" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: section_default.warn, children: t("desktopCopyFailed") }),
|
|
1972
|
+
release.sha512 !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: section_default.hint, children: `SHA-512 ${release.sha512}` })
|
|
1973
|
+
] })
|
|
1974
|
+
] });
|
|
1975
|
+
}
|
|
1976
|
+
function desktopFailureText(message, t) {
|
|
1977
|
+
if (message === void 0 || message.length === 0) return "";
|
|
1978
|
+
if (message === "remote-not-mounted") return t("desktopRemoteMissing");
|
|
1979
|
+
if (message === "feed-unreachable") return t("desktopUnavailable");
|
|
1980
|
+
return message;
|
|
1981
|
+
}
|
|
1807
1982
|
function Section({ controller, t, getLocale, openRequestLog }) {
|
|
1808
1983
|
const state = (0, import_react.useSyncExternalStore)(controller.subscribe, controller.snapshot, controller.snapshot);
|
|
1809
1984
|
const [keyDraft, setKeyDraft] = (0, import_react.useState)("");
|
|
@@ -1834,6 +2009,7 @@ function Section({ controller, t, getLocale, openRequestLog }) {
|
|
|
1834
2009
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { className: section_default.title, children: t("nav") }),
|
|
1835
2010
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: section_default.hint, children: t("intro") }),
|
|
1836
2011
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(UpdateBanner, { controller, t, busy, act, update: state.update }),
|
|
2012
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DesktopBanner, { controller, t, busy, act, desktop: state.desktop }),
|
|
1837
2013
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.block, children: [
|
|
1838
2014
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: section_default.row, children: [
|
|
1839
2015
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { children: t("keyTitle") }),
|
|
@@ -3292,7 +3468,7 @@ function apply(ctx) {
|
|
|
3292
3468
|
void dispose?.();
|
|
3293
3469
|
};
|
|
3294
3470
|
}, "dshopencodego: usage Remote");
|
|
3295
|
-
ctx.inject(["configForms", "remote.opencodeGoCatalog", "remote.opencodeGoUpdate", "remote.opencodeGoNetwork", "remote.opencodeGoRequestLog", "remote.credentials"], (scope) => {
|
|
3471
|
+
ctx.inject(["configForms", "remote.opencodeGoCatalog", "remote.opencodeGoUpdate", "remote.opencodeGoNetwork", "remote.opencodeGoDesktop", "remote.opencodeGoRequestLog", "remote.credentials"], (scope) => {
|
|
3296
3472
|
const forms = scope.get("configForms");
|
|
3297
3473
|
const pluginManager = {};
|
|
3298
3474
|
const profile = forms?.get("dshopencodego");
|
package/lib/index.js
CHANGED
|
@@ -74427,8 +74427,8 @@ function writeDocCache(cacheDir, entry) {
|
|
|
74427
74427
|
|
|
74428
74428
|
// src/catalog/go-doc.ts
|
|
74429
74429
|
var GO_DOC_URLS = [
|
|
74430
|
-
"https://raw.githubusercontent.com/anomalyco/opencode/dev/services/
|
|
74431
|
-
"https://cdn.jsdelivr.net/gh/anomalyco/opencode@dev/services/
|
|
74430
|
+
"https://raw.githubusercontent.com/anomalyco/opencode/dev/services/site/src/docs/content/console/go.mdx",
|
|
74431
|
+
"https://cdn.jsdelivr.net/gh/anomalyco/opencode@dev/services/site/src/docs/content/console/go.mdx"
|
|
74432
74432
|
];
|
|
74433
74433
|
var GO_DOC_MAX_BYTES = 1024 * 1024;
|
|
74434
74434
|
var ENDPOINT_PROTOCOLS = {
|
|
@@ -79124,6 +79124,75 @@ var catalogRemote = {
|
|
|
79124
79124
|
]
|
|
79125
79125
|
};
|
|
79126
79126
|
|
|
79127
|
+
// src/desktop/contract.ts
|
|
79128
|
+
function parseRelease(value) {
|
|
79129
|
+
if (value === null || typeof value !== "object") throw new Error("invalid desktop release");
|
|
79130
|
+
const row = value;
|
|
79131
|
+
const version = row["version"];
|
|
79132
|
+
const url = row["url"];
|
|
79133
|
+
if (typeof version !== "string" || version.length === 0) throw new Error("invalid desktop release");
|
|
79134
|
+
if (typeof url !== "string" || url.length === 0) throw new Error("invalid desktop release");
|
|
79135
|
+
const size = row["size"];
|
|
79136
|
+
const sha512 = row["sha512"];
|
|
79137
|
+
const stagingPercentage = row["stagingPercentage"];
|
|
79138
|
+
const releaseDate = row["releaseDate"];
|
|
79139
|
+
return {
|
|
79140
|
+
version,
|
|
79141
|
+
url,
|
|
79142
|
+
...typeof size === "number" && Number.isFinite(size) && size >= 0 ? { size } : {},
|
|
79143
|
+
...typeof sha512 === "string" && sha512.length > 0 ? { sha512 } : {},
|
|
79144
|
+
...typeof stagingPercentage === "number" && Number.isFinite(stagingPercentage) ? { stagingPercentage } : {},
|
|
79145
|
+
...typeof releaseDate === "string" && releaseDate.length > 0 ? { releaseDate } : {}
|
|
79146
|
+
};
|
|
79147
|
+
}
|
|
79148
|
+
function parseDesktopReading(value) {
|
|
79149
|
+
if (value === null || typeof value !== "object") throw new Error("invalid desktop reading");
|
|
79150
|
+
const row = value;
|
|
79151
|
+
if (typeof row["checked"] !== "boolean") throw new Error("invalid desktop reading");
|
|
79152
|
+
if (typeof row["checkedAt"] !== "number" || !Number.isFinite(row["checkedAt"])) {
|
|
79153
|
+
throw new Error("invalid desktop reading");
|
|
79154
|
+
}
|
|
79155
|
+
if (typeof row["platform"] !== "string") throw new Error("invalid desktop reading");
|
|
79156
|
+
if (typeof row["channel"] !== "string") throw new Error("invalid desktop reading");
|
|
79157
|
+
const release = row["release"];
|
|
79158
|
+
return {
|
|
79159
|
+
checked: row["checked"],
|
|
79160
|
+
checkedAt: row["checkedAt"],
|
|
79161
|
+
platform: row["platform"],
|
|
79162
|
+
channel: row["channel"],
|
|
79163
|
+
...release === void 0 ? {} : { release: parseRelease(release) }
|
|
79164
|
+
};
|
|
79165
|
+
}
|
|
79166
|
+
var codec2 = {
|
|
79167
|
+
mode: "strict",
|
|
79168
|
+
typeSymbol: "@dan-ai-studio/dshopencodego#DesktopReading",
|
|
79169
|
+
schema: { parse: parseDesktopReading },
|
|
79170
|
+
create: () => ({ parse: parseDesktopReading })
|
|
79171
|
+
};
|
|
79172
|
+
var desktopRemote = {
|
|
79173
|
+
package: "@dan-ai-studio/dshopencodego",
|
|
79174
|
+
descriptors: [
|
|
79175
|
+
{
|
|
79176
|
+
id: "@dan-ai-studio/dshopencodego#opencodeGoDesktop/readDesktop",
|
|
79177
|
+
service: "opencodeGoDesktop",
|
|
79178
|
+
namespace: "opencodeGoDesktop",
|
|
79179
|
+
method: "readDesktop",
|
|
79180
|
+
invocation: { kind: "direct" },
|
|
79181
|
+
parameters: [],
|
|
79182
|
+
result: codec2
|
|
79183
|
+
},
|
|
79184
|
+
{
|
|
79185
|
+
id: "@dan-ai-studio/dshopencodego#opencodeGoDesktop/refreshDesktop",
|
|
79186
|
+
service: "opencodeGoDesktop",
|
|
79187
|
+
namespace: "opencodeGoDesktop",
|
|
79188
|
+
method: "refreshDesktop",
|
|
79189
|
+
invocation: { kind: "direct" },
|
|
79190
|
+
parameters: [],
|
|
79191
|
+
result: codec2
|
|
79192
|
+
}
|
|
79193
|
+
]
|
|
79194
|
+
};
|
|
79195
|
+
|
|
79127
79196
|
// src/network/contract.ts
|
|
79128
79197
|
function parseNetworkReading(value) {
|
|
79129
79198
|
if (value === null || typeof value !== "object") throw new Error("invalid network reading");
|
|
@@ -79138,7 +79207,7 @@ function parseNetworkReading(value) {
|
|
|
79138
79207
|
...typeof failed === "boolean" ? { failed } : {}
|
|
79139
79208
|
};
|
|
79140
79209
|
}
|
|
79141
|
-
var
|
|
79210
|
+
var codec3 = {
|
|
79142
79211
|
mode: "strict",
|
|
79143
79212
|
typeSymbol: "@dan-ai-studio/dshopencodego#NetworkReading",
|
|
79144
79213
|
schema: { parse: parseNetworkReading },
|
|
@@ -79154,7 +79223,7 @@ var networkRemote = {
|
|
|
79154
79223
|
method: "readNetwork",
|
|
79155
79224
|
invocation: { kind: "direct" },
|
|
79156
79225
|
parameters: [],
|
|
79157
|
-
result:
|
|
79226
|
+
result: codec3
|
|
79158
79227
|
}
|
|
79159
79228
|
]
|
|
79160
79229
|
};
|
|
@@ -79512,7 +79581,7 @@ function parseRequestLogSnapshot(value) {
|
|
|
79512
79581
|
...typeof row["poolKey"] === "string" ? { poolKey: row["poolKey"] } : {}
|
|
79513
79582
|
};
|
|
79514
79583
|
}
|
|
79515
|
-
var
|
|
79584
|
+
var codec4 = {
|
|
79516
79585
|
mode: "strict",
|
|
79517
79586
|
typeSymbol: "@dan-ai-studio/dshopencodego#RequestLogSnapshot",
|
|
79518
79587
|
schema: { parse: parseRequestLogSnapshot },
|
|
@@ -79528,7 +79597,7 @@ var requestLogRemote = {
|
|
|
79528
79597
|
method: "readLog",
|
|
79529
79598
|
invocation: { kind: "direct" },
|
|
79530
79599
|
parameters: [],
|
|
79531
|
-
result:
|
|
79600
|
+
result: codec4
|
|
79532
79601
|
}
|
|
79533
79602
|
]
|
|
79534
79603
|
};
|
|
@@ -79552,7 +79621,7 @@ function parseUpdateStatus(value) {
|
|
|
79552
79621
|
checked: row["checked"]
|
|
79553
79622
|
};
|
|
79554
79623
|
}
|
|
79555
|
-
var
|
|
79624
|
+
var codec5 = (typeSymbol, parse) => ({
|
|
79556
79625
|
mode: "strict",
|
|
79557
79626
|
typeSymbol,
|
|
79558
79627
|
schema: { parse },
|
|
@@ -79568,7 +79637,7 @@ var updateRemote = {
|
|
|
79568
79637
|
method: "readUpdate",
|
|
79569
79638
|
invocation: { kind: "direct" },
|
|
79570
79639
|
parameters: [],
|
|
79571
|
-
result:
|
|
79640
|
+
result: codec5("@dan-ai-studio/dshopencodego#UpdateStatus", parseUpdateStatus)
|
|
79572
79641
|
},
|
|
79573
79642
|
{
|
|
79574
79643
|
id: "@dan-ai-studio/dshopencodego#opencodeGoUpdate/refreshUpdate",
|
|
@@ -79577,7 +79646,7 @@ var updateRemote = {
|
|
|
79577
79646
|
method: "refreshUpdate",
|
|
79578
79647
|
invocation: { kind: "direct" },
|
|
79579
79648
|
parameters: [],
|
|
79580
|
-
result:
|
|
79649
|
+
result: codec5("@dan-ai-studio/dshopencodego#UpdateStatus", parseUpdateStatus)
|
|
79581
79650
|
}
|
|
79582
79651
|
]
|
|
79583
79652
|
};
|
|
@@ -79643,7 +79712,7 @@ function parseGoMeter(value) {
|
|
|
79643
79712
|
})
|
|
79644
79713
|
};
|
|
79645
79714
|
}
|
|
79646
|
-
var
|
|
79715
|
+
var codec6 = (typeSymbol, parse) => ({
|
|
79647
79716
|
mode: "strict",
|
|
79648
79717
|
typeSymbol,
|
|
79649
79718
|
schema: { parse },
|
|
@@ -79659,7 +79728,7 @@ var usageRemote = {
|
|
|
79659
79728
|
method: "readWindows",
|
|
79660
79729
|
invocation: { kind: "direct" },
|
|
79661
79730
|
parameters: [],
|
|
79662
|
-
result:
|
|
79731
|
+
result: codec6("@dan-ai-studio/dshopencodego#GoUsageWindows", parseUsageWindows)
|
|
79663
79732
|
},
|
|
79664
79733
|
{
|
|
79665
79734
|
id: "@dan-ai-studio/dshopencodego#opencodeGoUsage/readMeter",
|
|
@@ -79668,7 +79737,7 @@ var usageRemote = {
|
|
|
79668
79737
|
method: "readMeter",
|
|
79669
79738
|
invocation: { kind: "direct" },
|
|
79670
79739
|
parameters: [],
|
|
79671
|
-
result:
|
|
79740
|
+
result: codec6("@dan-ai-studio/dshopencodego#GoMeter", parseGoMeter)
|
|
79672
79741
|
}
|
|
79673
79742
|
]
|
|
79674
79743
|
};
|
|
@@ -79681,6 +79750,7 @@ var contribution = {
|
|
|
79681
79750
|
...catalogRemote.descriptors,
|
|
79682
79751
|
...updateRemote.descriptors,
|
|
79683
79752
|
...networkRemote.descriptors,
|
|
79753
|
+
...desktopRemote.descriptors,
|
|
79684
79754
|
...requestLogRemote.descriptors
|
|
79685
79755
|
]
|
|
79686
79756
|
};
|
|
@@ -79816,6 +79886,174 @@ function catalogFailure(error) {
|
|
|
79816
79886
|
}, { cause: error });
|
|
79817
79887
|
}
|
|
79818
79888
|
|
|
79889
|
+
// src/desktop/feed.ts
|
|
79890
|
+
var DESKTOP_FEED_PLATFORM = "win-x64";
|
|
79891
|
+
var DESKTOP_FEED_CHANNEL = "nightly";
|
|
79892
|
+
var DESKTOP_FEED_URL = `https://download.deepseek.com/dsh-desk/feeds/${DESKTOP_FEED_PLATFORM}/${DESKTOP_FEED_CHANNEL}.yml`;
|
|
79893
|
+
var DESKTOP_CACHE_MS = 6 * 60 * 60 * 1e3;
|
|
79894
|
+
var DESKTOP_TIMEOUT_MS = 1e4;
|
|
79895
|
+
function indentOf(line) {
|
|
79896
|
+
return line.length - line.trimStart().length;
|
|
79897
|
+
}
|
|
79898
|
+
function unquote(raw) {
|
|
79899
|
+
const value = raw.trim();
|
|
79900
|
+
const quoted = value.length >= 2 && (value.startsWith("'") && value.endsWith("'") || value.startsWith('"') && value.endsWith('"'));
|
|
79901
|
+
return quoted ? value.slice(1, -1) : value;
|
|
79902
|
+
}
|
|
79903
|
+
function toNumber(value) {
|
|
79904
|
+
if (!/^\d+$/.test(value)) return void 0;
|
|
79905
|
+
const parsed = Number(value);
|
|
79906
|
+
return Number.isFinite(parsed) ? parsed : void 0;
|
|
79907
|
+
}
|
|
79908
|
+
function foldBlocks(source) {
|
|
79909
|
+
const lines = source.split(/\r?\n/);
|
|
79910
|
+
const out = [];
|
|
79911
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
79912
|
+
const line = lines[index] ?? "";
|
|
79913
|
+
const open = /^([ \t]*)([A-Za-z][\w-]*):[ \t]*(>-?|>|\|-?|\|)[ \t]*$/.exec(line);
|
|
79914
|
+
if (open === null) {
|
|
79915
|
+
out.push({ indent: indentOf(line), text: line.trim() });
|
|
79916
|
+
continue;
|
|
79917
|
+
}
|
|
79918
|
+
const [, leading = "", key = "", marker = ""] = open;
|
|
79919
|
+
const indent = leading.length;
|
|
79920
|
+
const parts = [];
|
|
79921
|
+
let cursor = index + 1;
|
|
79922
|
+
for (; cursor < lines.length; cursor += 1) {
|
|
79923
|
+
const next = lines[cursor] ?? "";
|
|
79924
|
+
if (next.trim().length === 0) continue;
|
|
79925
|
+
if (indentOf(next) <= indent) break;
|
|
79926
|
+
parts.push(next.trim());
|
|
79927
|
+
}
|
|
79928
|
+
out.push({ indent, text: `${key}: ${parts.join(marker.startsWith("|") ? "\n" : " ")}` });
|
|
79929
|
+
index = cursor - 1;
|
|
79930
|
+
}
|
|
79931
|
+
return out;
|
|
79932
|
+
}
|
|
79933
|
+
function assignFile(target, text2) {
|
|
79934
|
+
const match = /^([A-Za-z][\w-]*):[ \t]*(.*)$/.exec(text2);
|
|
79935
|
+
if (match === null) return;
|
|
79936
|
+
const [, key = "", raw = ""] = match;
|
|
79937
|
+
const value = unquote(raw);
|
|
79938
|
+
if (key === "url") target.url = value;
|
|
79939
|
+
else if (key === "sha512") target.sha512 = value;
|
|
79940
|
+
else if (key === "size") {
|
|
79941
|
+
const size = toNumber(value);
|
|
79942
|
+
if (size !== void 0) target.size = size;
|
|
79943
|
+
}
|
|
79944
|
+
}
|
|
79945
|
+
function parseDesktopFeed(source) {
|
|
79946
|
+
let version;
|
|
79947
|
+
let path5;
|
|
79948
|
+
let digest;
|
|
79949
|
+
let stagingPercentage;
|
|
79950
|
+
let releaseDate;
|
|
79951
|
+
const files = [];
|
|
79952
|
+
let section = "top";
|
|
79953
|
+
for (const line of foldBlocks(source)) {
|
|
79954
|
+
const text2 = line.text;
|
|
79955
|
+
if (text2.length === 0 || text2.startsWith("#")) continue;
|
|
79956
|
+
if (line.indent === 0) {
|
|
79957
|
+
const match = /^([A-Za-z][\w-]*):[ \t]*(.*)$/.exec(text2);
|
|
79958
|
+
if (match === null) continue;
|
|
79959
|
+
const [, key = "", raw = ""] = match;
|
|
79960
|
+
section = key === "files" ? "files" : "top";
|
|
79961
|
+
if (section === "files") continue;
|
|
79962
|
+
const value = unquote(raw);
|
|
79963
|
+
if (key === "version") version = value;
|
|
79964
|
+
else if (key === "path") path5 = value;
|
|
79965
|
+
else if (key === "sha512") digest = value;
|
|
79966
|
+
else if (key === "releaseDate") releaseDate = value;
|
|
79967
|
+
else if (key === "stagingPercentage") stagingPercentage = toNumber(value);
|
|
79968
|
+
continue;
|
|
79969
|
+
}
|
|
79970
|
+
if (section !== "files") continue;
|
|
79971
|
+
if (text2.startsWith("-")) {
|
|
79972
|
+
files.push({});
|
|
79973
|
+
const rest = text2.slice(1).trim();
|
|
79974
|
+
const target2 = files[files.length - 1];
|
|
79975
|
+
if (target2 !== void 0 && rest.length > 0) assignFile(target2, rest);
|
|
79976
|
+
continue;
|
|
79977
|
+
}
|
|
79978
|
+
const target = files[files.length - 1];
|
|
79979
|
+
if (target !== void 0) assignFile(target, text2);
|
|
79980
|
+
}
|
|
79981
|
+
const first = files[0];
|
|
79982
|
+
const url = path5 ?? first?.url;
|
|
79983
|
+
if (version === void 0 || version.length === 0) return void 0;
|
|
79984
|
+
if (url === void 0 || url.length === 0) return void 0;
|
|
79985
|
+
const sha512 = digest ?? first?.sha512;
|
|
79986
|
+
return {
|
|
79987
|
+
version,
|
|
79988
|
+
url,
|
|
79989
|
+
...first?.size === void 0 ? {} : { size: first.size },
|
|
79990
|
+
...sha512 === void 0 ? {} : { sha512 },
|
|
79991
|
+
...stagingPercentage === void 0 ? {} : { stagingPercentage },
|
|
79992
|
+
...releaseDate === void 0 ? {} : { releaseDate }
|
|
79993
|
+
};
|
|
79994
|
+
}
|
|
79995
|
+
function createDesktopFeedReader(deps = {}) {
|
|
79996
|
+
const now = deps.now ?? Date.now;
|
|
79997
|
+
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
79998
|
+
let cached;
|
|
79999
|
+
let inflight;
|
|
80000
|
+
async function lookup() {
|
|
80001
|
+
const checkedAt = now();
|
|
80002
|
+
const identity = { checkedAt, platform: DESKTOP_FEED_PLATFORM, channel: DESKTOP_FEED_CHANNEL };
|
|
80003
|
+
try {
|
|
80004
|
+
const response = await fetchImpl(DESKTOP_FEED_URL, {
|
|
80005
|
+
signal: AbortSignal.timeout(DESKTOP_TIMEOUT_MS),
|
|
80006
|
+
headers: { accept: "text/yaml, text/plain, */*" }
|
|
80007
|
+
});
|
|
80008
|
+
if (!response.ok) throw new Error(`feed answered ${response.status}`);
|
|
80009
|
+
const release = parseDesktopFeed(await response.text());
|
|
80010
|
+
if (release === void 0) throw new Error("feed carried no release");
|
|
80011
|
+
return { ...identity, checked: true, release };
|
|
80012
|
+
} catch {
|
|
80013
|
+
return { ...identity, checked: false };
|
|
80014
|
+
}
|
|
80015
|
+
}
|
|
80016
|
+
return {
|
|
80017
|
+
read(force = false) {
|
|
80018
|
+
if (!force && cached !== void 0 && now() - cached.checkedAt < DESKTOP_CACHE_MS) {
|
|
80019
|
+
return Promise.resolve(cached);
|
|
80020
|
+
}
|
|
80021
|
+
inflight ??= lookup().then((reading) => {
|
|
80022
|
+
cached = reading;
|
|
80023
|
+
inflight = void 0;
|
|
80024
|
+
return reading;
|
|
80025
|
+
});
|
|
80026
|
+
return inflight;
|
|
80027
|
+
}
|
|
80028
|
+
};
|
|
80029
|
+
}
|
|
80030
|
+
|
|
80031
|
+
// src/desktop/service.ts
|
|
80032
|
+
import { TypertRemoteService as TypertRemoteService2 } from "@deepseek-ai/dsh-typert-protocol";
|
|
80033
|
+
var OpencodeGoDesktopService = class extends TypertRemoteService2 {
|
|
80034
|
+
read;
|
|
80035
|
+
constructor(ctx) {
|
|
80036
|
+
super(ctx, "opencodeGoDesktop");
|
|
80037
|
+
const reader = createDesktopFeedReader();
|
|
80038
|
+
this.read = (force) => reader.read(force);
|
|
80039
|
+
}
|
|
80040
|
+
/**
|
|
80041
|
+
* The newest published installer (cached reading).
|
|
80042
|
+
*
|
|
80043
|
+
* The wire method takes no arguments — the gateway client rejects any call
|
|
80044
|
+
* whose positional arity differs from the descriptor's `parameters`, so a
|
|
80045
|
+
* `force` flag cannot ride this call. Forced revalidation is the separate
|
|
80046
|
+
* `refreshDesktop` method below, mirroring the update Remote's pair.
|
|
80047
|
+
*/
|
|
80048
|
+
readDesktop() {
|
|
80049
|
+
return this.read(false);
|
|
80050
|
+
}
|
|
80051
|
+
/** Revalidate against the feed, bypassing the cache. */
|
|
80052
|
+
refreshDesktop() {
|
|
80053
|
+
return this.read(true);
|
|
80054
|
+
}
|
|
80055
|
+
};
|
|
80056
|
+
|
|
79819
80057
|
// src/network/pinning.ts
|
|
79820
80058
|
var import_undici = __toESM(require_undici(), 1);
|
|
79821
80059
|
import { isIP } from "node:net";
|
|
@@ -79972,8 +80210,8 @@ function gatewayFetch(pin, keepAliveTimeoutMs) {
|
|
|
79972
80210
|
}
|
|
79973
80211
|
|
|
79974
80212
|
// src/network/service.ts
|
|
79975
|
-
import { TypertRemoteService as
|
|
79976
|
-
var OpencodeGoNetworkService = class extends
|
|
80213
|
+
import { TypertRemoteService as TypertRemoteService3 } from "@deepseek-ai/dsh-typert-protocol";
|
|
80214
|
+
var OpencodeGoNetworkService = class extends TypertRemoteService3 {
|
|
79977
80215
|
options;
|
|
79978
80216
|
constructor(ctx, options) {
|
|
79979
80217
|
super(ctx, "opencodeGoNetwork");
|
|
@@ -79991,8 +80229,8 @@ var OpencodeGoNetworkService = class extends TypertRemoteService2 {
|
|
|
79991
80229
|
};
|
|
79992
80230
|
|
|
79993
80231
|
// src/request-log/service.ts
|
|
79994
|
-
import { TypertRemoteService as
|
|
79995
|
-
var OpencodeGoRequestLogService = class extends
|
|
80232
|
+
import { TypertRemoteService as TypertRemoteService4 } from "@deepseek-ai/dsh-typert-protocol";
|
|
80233
|
+
var OpencodeGoRequestLogService = class extends TypertRemoteService4 {
|
|
79996
80234
|
options;
|
|
79997
80235
|
constructor(ctx, options) {
|
|
79998
80236
|
super(ctx, "opencodeGoRequestLog");
|
|
@@ -80098,11 +80336,11 @@ function createUpdateChecker(deps) {
|
|
|
80098
80336
|
}
|
|
80099
80337
|
|
|
80100
80338
|
// src/update/version.ts
|
|
80101
|
-
var PLUGIN_VERSION = true ? "0.1.
|
|
80339
|
+
var PLUGIN_VERSION = true ? "0.1.92" : "0.0.0-dev";
|
|
80102
80340
|
|
|
80103
80341
|
// src/update/service.ts
|
|
80104
|
-
import { TypertRemoteService as
|
|
80105
|
-
var OpencodeGoUpdateService = class extends
|
|
80342
|
+
import { TypertRemoteService as TypertRemoteService5 } from "@deepseek-ai/dsh-typert-protocol";
|
|
80343
|
+
var OpencodeGoUpdateService = class extends TypertRemoteService5 {
|
|
80106
80344
|
check;
|
|
80107
80345
|
constructor(ctx, options) {
|
|
80108
80346
|
super(ctx, "opencodeGoUpdate");
|
|
@@ -80182,7 +80420,7 @@ async function readUsageWindows(options) {
|
|
|
80182
80420
|
|
|
80183
80421
|
// src/usage/service.ts
|
|
80184
80422
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
80185
|
-
import { RemoteError as RemoteError2, TypertRemoteService as
|
|
80423
|
+
import { RemoteError as RemoteError2, TypertRemoteService as TypertRemoteService6 } from "@deepseek-ai/dsh-typert-protocol";
|
|
80186
80424
|
import { LlmError as LlmError13 } from "@deepseek-ai/dsh-llm";
|
|
80187
80425
|
function usageFailure(error, source) {
|
|
80188
80426
|
const missing = error instanceof LlmError13 && error.code === "MISSING_CREDENTIAL";
|
|
@@ -80193,7 +80431,7 @@ function usageFailure(error, source) {
|
|
|
80193
80431
|
{ cause: error }
|
|
80194
80432
|
);
|
|
80195
80433
|
}
|
|
80196
|
-
var OpencodeGoUsageService = class extends
|
|
80434
|
+
var OpencodeGoUsageService = class extends TypertRemoteService6 {
|
|
80197
80435
|
identity;
|
|
80198
80436
|
options;
|
|
80199
80437
|
constructor(ctx, options) {
|
|
@@ -80345,6 +80583,7 @@ function apply(ctx, raw) {
|
|
|
80345
80583
|
}
|
|
80346
80584
|
});
|
|
80347
80585
|
ctx.plugin(OpencodeGoUpdateService, { currentVersion: PLUGIN_VERSION });
|
|
80586
|
+
ctx.plugin(OpencodeGoDesktopService);
|
|
80348
80587
|
ctx.effect(() => {
|
|
80349
80588
|
const refresh = () => {
|
|
80350
80589
|
void adapter.catalogOf(current2()).snapshot().catch(() => void 0);
|
|
@@ -22,11 +22,12 @@ import type { WireProtocol } from './protocol.ts';
|
|
|
22
22
|
* same path and is tried when GitHub itself is unreachable.
|
|
23
23
|
*
|
|
24
24
|
* The path is the provider's own, and it moves: `go.mdx` used to live at
|
|
25
|
-
* `packages/web/src/content/docs/go.mdx
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
25
|
+
* `packages/web/src/content/docs/go.mdx`, then at
|
|
26
|
+
* `services/www/src/docs/content/console/go.mdx`, and the `www` tree was
|
|
27
|
+
* renamed to `site` — after which both mirrors answered 404 again. A document
|
|
28
|
+
* that cannot be read costs the endpoint table — the provider's own statement
|
|
29
|
+
* of each model's protocol — so the path is asserted against the live
|
|
30
|
+
* repository by `tests/go-doc-url.spec.ts` rather than left to rot unnoticed.
|
|
30
31
|
*/
|
|
31
32
|
export declare const GO_DOC_URLS: readonly string[];
|
|
32
33
|
/** What one parse of the document yields. */
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wire contract for the desktop Remote.
|
|
3
|
+
*
|
|
4
|
+
* `readDesktop` answers which installer the desktop download host publishes
|
|
5
|
+
* right now, and where it lives. The reading is advisory: the page shows the
|
|
6
|
+
* address so a human can copy it or open it in a browser, and nothing here
|
|
7
|
+
* downloads or installs anything.
|
|
8
|
+
*
|
|
9
|
+
* @module @dan-ai-studio/dshopencodego/desktop/contract
|
|
10
|
+
*/
|
|
11
|
+
import type { RemoteResult, TypertRemoteContribution } from '@deepseek-ai/dsh-typert-protocol';
|
|
12
|
+
import type { DesktopReading } from './feed.ts';
|
|
13
|
+
export type { DesktopReading, DesktopRelease } from './feed.ts';
|
|
14
|
+
/** Validate a desktop reading crossing the wire. */
|
|
15
|
+
export declare function parseDesktopReading(value: unknown): DesktopReading;
|
|
16
|
+
declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
17
|
+
interface TypertRemoteNamespaceMap {
|
|
18
|
+
opencodeGoDesktop: {
|
|
19
|
+
readDesktop(): Promise<RemoteResult<DesktopReading>>;
|
|
20
|
+
refreshDesktop(): Promise<RemoteResult<DesktopReading>>;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/** Remote methods this package owns, mounted together by the plugin. */
|
|
25
|
+
export declare const desktopRemote: TypertRemoteContribution;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The desktop app's release feed.
|
|
3
|
+
*
|
|
4
|
+
* The Harness desktop build publishes one small YAML document per platform and
|
|
5
|
+
* channel under `download.deepseek.com/dsh-desk/feeds/`. Reading it answers a
|
|
6
|
+
* single question — which installer is the newest one published right now, and
|
|
7
|
+
* where does it live? — without asking the user to curl the feed by hand.
|
|
8
|
+
*
|
|
9
|
+
* This module only *reads*. It never downloads, verifies, or runs the
|
|
10
|
+
* installer: the address is surfaced so the page can show, copy, or open it,
|
|
11
|
+
* and the decision to install stays with the human. Verifying the published
|
|
12
|
+
* `sha512` would mean fetching a ~286 MB payload, which is not something a
|
|
13
|
+
* settings-page click may do unasked.
|
|
14
|
+
*
|
|
15
|
+
* Only `win-x64/nightly` exists today — every other platform directory answers
|
|
16
|
+
* 404 — so platform and channel are constants rather than a matrix. A feed that
|
|
17
|
+
* starts publishing elsewhere gets its own constant here.
|
|
18
|
+
*
|
|
19
|
+
* @module @dan-ai-studio/dshopencodego/desktop/feed
|
|
20
|
+
*/
|
|
21
|
+
/** Platform directory of the published feed. */
|
|
22
|
+
export declare const DESKTOP_FEED_PLATFORM = "win-x64";
|
|
23
|
+
/** Release channel of the published feed. */
|
|
24
|
+
export declare const DESKTOP_FEED_CHANNEL = "nightly";
|
|
25
|
+
/** Document answering the newest installer for that platform and channel. */
|
|
26
|
+
export declare const DESKTOP_FEED_URL = "https://download.deepseek.com/dsh-desk/feeds/win-x64/nightly.yml";
|
|
27
|
+
/** How long one reading answers for before the feed is asked again. */
|
|
28
|
+
export declare const DESKTOP_CACHE_MS: number;
|
|
29
|
+
/** Bound on one feed round trip; expiry fails silent, never throws. */
|
|
30
|
+
export declare const DESKTOP_TIMEOUT_MS = 10000;
|
|
31
|
+
/** One published installer, as the feed describes it. */
|
|
32
|
+
export interface DesktopRelease {
|
|
33
|
+
/** Version the installer carries, e.g. `0.2.2-rc.3`. */
|
|
34
|
+
readonly version: string;
|
|
35
|
+
/** Absolute address of the installer. */
|
|
36
|
+
readonly url: string;
|
|
37
|
+
/** Size in bytes, when the feed states one. */
|
|
38
|
+
readonly size?: number;
|
|
39
|
+
/** Base64 SHA-512 of the payload, when the feed states one. */
|
|
40
|
+
readonly sha512?: string;
|
|
41
|
+
/** Share of clients this build is offered to, when the feed states one. */
|
|
42
|
+
readonly stagingPercentage?: number;
|
|
43
|
+
/** ISO timestamp of the publication, when the feed states one. */
|
|
44
|
+
readonly releaseDate?: string;
|
|
45
|
+
}
|
|
46
|
+
/** What the settings page renders. */
|
|
47
|
+
export interface DesktopReading {
|
|
48
|
+
/**
|
|
49
|
+
* Whether the feed actually answered.
|
|
50
|
+
*
|
|
51
|
+
* `false` means the reading is a placeholder for a failed lookup: "no
|
|
52
|
+
* installer known", which is not the same fact as "nothing published". The
|
|
53
|
+
* page must not render the two alike.
|
|
54
|
+
*/
|
|
55
|
+
readonly checked: boolean;
|
|
56
|
+
/** Milliseconds since the epoch of the lookup backing this reading. */
|
|
57
|
+
readonly checkedAt: number;
|
|
58
|
+
/** Platform the reading describes. */
|
|
59
|
+
readonly platform: string;
|
|
60
|
+
/** Channel the reading describes. */
|
|
61
|
+
readonly channel: string;
|
|
62
|
+
/** The newest installer, when the feed answered with one. */
|
|
63
|
+
readonly release?: DesktopRelease;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Read the newest installer out of one feed document.
|
|
67
|
+
*
|
|
68
|
+
* The top-level `path`/`sha512` pair is what the updater itself follows, so it
|
|
69
|
+
* wins over the `files:` list; that list is the fallback for a feed that only
|
|
70
|
+
* spells the payload out there. A document without both a version and an
|
|
71
|
+
* address is not a release this page can offer, and reads as `undefined`
|
|
72
|
+
* rather than as a half-filled row.
|
|
73
|
+
*
|
|
74
|
+
* @param source - the feed document as served.
|
|
75
|
+
* @returns the release it describes, or `undefined` when it describes none.
|
|
76
|
+
*/
|
|
77
|
+
export declare function parseDesktopFeed(source: string): DesktopRelease | undefined;
|
|
78
|
+
/** Dependencies the reader needs, injectable for tests. */
|
|
79
|
+
interface DesktopFeedDeps {
|
|
80
|
+
/** Fetch implementation; defaults to the global one. */
|
|
81
|
+
readonly fetchImpl?: typeof fetch;
|
|
82
|
+
/** Clock; defaults to `Date.now`. */
|
|
83
|
+
readonly now?: () => number;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Ask the feed for the newest installer, cached per process.
|
|
87
|
+
*
|
|
88
|
+
* Concurrent callers share one in-flight lookup. A lookup that fails for any
|
|
89
|
+
* reason — network, timeout, a 404, a document without a release — resolves as
|
|
90
|
+
* "no installer known" rather than rejecting, so the settings page never
|
|
91
|
+
* breaks because the download host is unreachable.
|
|
92
|
+
*/
|
|
93
|
+
export declare function createDesktopFeedReader(deps?: DesktopFeedDeps): {
|
|
94
|
+
read(force?: boolean): Promise<DesktopReading>;
|
|
95
|
+
};
|
|
96
|
+
export {};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The desktop app's download feed (advisory only).
|
|
3
|
+
*
|
|
4
|
+
* @module @dan-ai-studio/dshopencodego/desktop
|
|
5
|
+
*/
|
|
6
|
+
export { DESKTOP_CACHE_MS, DESKTOP_FEED_CHANNEL, DESKTOP_FEED_PLATFORM, DESKTOP_FEED_URL, DESKTOP_TIMEOUT_MS, } from './feed.ts';
|
|
7
|
+
export type { DesktopReading, DesktopRelease } from './feed.ts';
|
|
8
|
+
export { createDesktopFeedReader, parseDesktopFeed } from './feed.ts';
|
|
9
|
+
export { OpencodeGoDesktopService } from './service.ts';
|
|
10
|
+
export { desktopRemote, parseDesktopReading } from './contract.ts';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Host half of the desktop Remote.
|
|
3
|
+
*
|
|
4
|
+
* One question: which installer does the desktop download host publish right
|
|
5
|
+
* now? The service answers from the cached feed reader in `./feed.ts`, so the
|
|
6
|
+
* settings page learns the address without the model path ever touching the
|
|
7
|
+
* network. Downloading and installing are intentionally absent — this only
|
|
8
|
+
* reports where the installer lives.
|
|
9
|
+
*
|
|
10
|
+
* @module @dan-ai-studio/dshopencodego/desktop/service
|
|
11
|
+
*/
|
|
12
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
13
|
+
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
14
|
+
import type { DesktopReading } from './feed.ts';
|
|
15
|
+
/** Desktop Remote: the cached feed reading for the settings page. */
|
|
16
|
+
export declare class OpencodeGoDesktopService extends TypertRemoteService {
|
|
17
|
+
private readonly read;
|
|
18
|
+
constructor(ctx: Context);
|
|
19
|
+
/**
|
|
20
|
+
* The newest published installer (cached reading).
|
|
21
|
+
*
|
|
22
|
+
* The wire method takes no arguments — the gateway client rejects any call
|
|
23
|
+
* whose positional arity differs from the descriptor's `parameters`, so a
|
|
24
|
+
* `force` flag cannot ride this call. Forced revalidation is the separate
|
|
25
|
+
* `refreshDesktop` method below, mirroring the update Remote's pair.
|
|
26
|
+
*/
|
|
27
|
+
readDesktop(): Promise<DesktopReading>;
|
|
28
|
+
/** Revalidate against the feed, bypassing the cache. */
|
|
29
|
+
refreshDesktop(): Promise<DesktopReading>;
|
|
30
|
+
}
|
package/package.json
CHANGED