dsh-update-status 0.1.0 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +34 -0
- package/README.md +33 -6
- package/README.zh.md +33 -6
- package/docs/RELEASING.md +50 -16
- package/lib/client.js +446 -95
- package/lib/client.js.map +1 -1
- package/lib/index.js +57 -17
- package/package.json +14 -16
package/lib/index.js
CHANGED
|
@@ -8,12 +8,20 @@ import { dirname, join, normalize, sep } from "node:path";
|
|
|
8
8
|
*
|
|
9
9
|
* The static package uses the authenticated Connection RPC channel because
|
|
10
10
|
* `harness.handle` / `host.call` are dynamic-Cordis-only closure APIs in DSH
|
|
11
|
-
* 0.1.
|
|
11
|
+
* 0.1.5-rc.1. The endpoint vocabulary remains deliberately small and private
|
|
12
12
|
* to this plugin channel.
|
|
13
13
|
*/
|
|
14
14
|
const PLUGIN_ID = "dsh-update-status";
|
|
15
15
|
const PACKAGE_NAME = "@deepseek-ai/dsh";
|
|
16
|
-
|
|
16
|
+
/** Shared Connection RPC channel. Custom prefixes 405 on the SPA fallback. */
|
|
17
|
+
const UPDATE_STATUS_CHANNEL = "/api";
|
|
18
|
+
/**
|
|
19
|
+
* DSH releases this bundle has actually been verified against. The same list is
|
|
20
|
+
* declared in package.json's `dsh.compatibility.dshReleases`, and every other
|
|
21
|
+
* release the registry reports stays `unverified` — the plugin never claims a
|
|
22
|
+
* compatibility nobody checked.
|
|
23
|
+
*/
|
|
24
|
+
const VERIFIED_DSH_VERSIONS = ["0.1.5-rc.1", "0.1.6-alpha.1"];
|
|
17
25
|
const RELEASE_CHANNELS = [
|
|
18
26
|
"latest",
|
|
19
27
|
"next",
|
|
@@ -23,8 +31,8 @@ function isCacheTtlMinutes(value) {
|
|
|
23
31
|
return typeof value === "number" && Number.isInteger(value) && value >= 30 && value <= 1440;
|
|
24
32
|
}
|
|
25
33
|
const UPDATE_ENDPOINTS = {
|
|
26
|
-
getStatus: "get-status",
|
|
27
|
-
checkUpdate: "check-update"
|
|
34
|
+
getStatus: "dsh-update-status.get-status",
|
|
35
|
+
checkUpdate: "dsh-update-status.check-update"
|
|
28
36
|
};
|
|
29
37
|
function isReleaseChannel(value) {
|
|
30
38
|
return value === "latest" || value === "next" || value === "alpha";
|
|
@@ -176,6 +184,32 @@ function requestOf(value) {
|
|
|
176
184
|
...record.cacheTtlMinutes === void 0 ? {} : { cacheTtlMinutes: record.cacheTtlMinutes }
|
|
177
185
|
};
|
|
178
186
|
}
|
|
187
|
+
function jsonResponse(rpcId, result) {
|
|
188
|
+
return Response.json({
|
|
189
|
+
type: "server-response",
|
|
190
|
+
rpcId,
|
|
191
|
+
result
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
/** Envelope-compatible Fetch adapter for one namespaced `/api` endpoint. */
|
|
195
|
+
async function dispatchUpdateStatusFetch(endpoint, handler, request) {
|
|
196
|
+
if (request.method !== "POST") return new Response("not found", { status: 404 });
|
|
197
|
+
if (request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase() !== "application/json") return new Response("content type must be application/json", { status: 415 });
|
|
198
|
+
let body;
|
|
199
|
+
try {
|
|
200
|
+
body = await request.json();
|
|
201
|
+
} catch {
|
|
202
|
+
return new Response("body is not JSON", { status: 400 });
|
|
203
|
+
}
|
|
204
|
+
const record = body !== null && typeof body === "object" && !Array.isArray(body) ? body : {};
|
|
205
|
+
const rpcId = typeof record.rpcId === "string" ? record.rpcId : "invalid-request";
|
|
206
|
+
if (record.type !== "client-request" || record.method !== endpoint) return jsonResponse(rpcId, failure("gateway/bad-request", "invalid client-request message"));
|
|
207
|
+
try {
|
|
208
|
+
return jsonResponse(rpcId, await handler(endpoint, record.payload, request.signal));
|
|
209
|
+
} catch (error) {
|
|
210
|
+
return new Response(`handler failure: ${String(error)}`, { status: 500 });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
179
213
|
/** Endpoint dispatcher, exported to allow exact JSON-shape tests without a Host. */
|
|
180
214
|
function createUpdateStatusRpcHandler(service) {
|
|
181
215
|
return async (endpoint, payload) => {
|
|
@@ -204,21 +238,27 @@ function createUpdateStatusRpcHandler(service) {
|
|
|
204
238
|
}
|
|
205
239
|
/**
|
|
206
240
|
* Static packages do not receive dynamic Cordis's `harness.handle` closure.
|
|
207
|
-
*
|
|
208
|
-
*
|
|
241
|
+
* DSH 0.1.5 serves browser unary RPC only on the shared `/api` channel; a
|
|
242
|
+
* private prefix such as `/dsh-update-status` is answered by the SPA fallback
|
|
243
|
+
* with HTTP 405. Exact Fetch routes on `/api/<endpoint>` are dispatched by
|
|
244
|
+
* Connection before the Typert interceptor and do not need a new HTTP prefix.
|
|
209
245
|
*/
|
|
210
246
|
function installUpdateStatusRpc(ctx, service) {
|
|
211
247
|
const handler = createUpdateStatusRpcHandler(service);
|
|
212
|
-
ctx.inject(["connection"], (
|
|
213
|
-
const connection =
|
|
214
|
-
const
|
|
215
|
-
if (
|
|
216
|
-
|
|
217
|
-
const
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
248
|
+
ctx.inject(["connection"], (owned) => {
|
|
249
|
+
const connection = owned.connection;
|
|
250
|
+
const register = typeof connection.fetch?.register === "function" ? connection.fetch.register.bind(connection.fetch) : void 0;
|
|
251
|
+
if (register === void 0) return;
|
|
252
|
+
try {
|
|
253
|
+
for (const endpoint of Object.values(UPDATE_ENDPOINTS)) register({
|
|
254
|
+
path: `${UPDATE_STATUS_CHANNEL}/${endpoint}`,
|
|
255
|
+
methods: ["POST"],
|
|
256
|
+
requestBody: "buffered",
|
|
257
|
+
fetch: (request) => dispatchUpdateStatusFetch(endpoint, handler, request)
|
|
258
|
+
});
|
|
259
|
+
} catch (error) {
|
|
260
|
+
console.error("[dsh-update-status] authenticated RPC route registration failed:", error);
|
|
261
|
+
}
|
|
222
262
|
});
|
|
223
263
|
}
|
|
224
264
|
//#endregion
|
|
@@ -321,7 +361,7 @@ function dateOrNull(value) {
|
|
|
321
361
|
return Number.isFinite(time) ? new Date(time).toISOString() : null;
|
|
322
362
|
}
|
|
323
363
|
function compatibilityOf(version) {
|
|
324
|
-
return version
|
|
364
|
+
return version !== null && VERIFIED_DSH_VERSIONS.includes(version) ? "verified" : "unverified";
|
|
325
365
|
}
|
|
326
366
|
function registryReleaseOf(value) {
|
|
327
367
|
if (value === null || typeof value !== "object" || Array.isArray(value)) throw new Error("npm registry returned an invalid document");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-update-status",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Read-only DeepSeek Harness Web plugin that shows the running DSH version, discovers latest/next/alpha releases, warns about compatibility, and generates copy-only upgrade commands.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -29,15 +29,6 @@
|
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=20"
|
|
31
31
|
},
|
|
32
|
-
"scripts": {
|
|
33
|
-
"build": "tsdown",
|
|
34
|
-
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.tests.json",
|
|
35
|
-
"test": "pnpm run typecheck && vitest run",
|
|
36
|
-
"test:unit": "vitest run",
|
|
37
|
-
"clean": "rm -rf lib coverage",
|
|
38
|
-
"prepack": "pnpm run build",
|
|
39
|
-
"verify": "pnpm run test && pnpm run build && pnpm pack --dry-run"
|
|
40
|
-
},
|
|
41
32
|
"repository": {
|
|
42
33
|
"type": "git",
|
|
43
34
|
"url": "git+https://github.com/idoall/dsh-update-status.git"
|
|
@@ -59,14 +50,14 @@
|
|
|
59
50
|
"@deepseek-ai/dsh-client-connection",
|
|
60
51
|
"@deepseek-ai/dsh-client-ui-layout",
|
|
61
52
|
"@deepseek-ai/dsh-client-ui-sidebar",
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-slots",
|
|
63
53
|
"@deepseek-ai/dsh-client-ui-settings",
|
|
64
54
|
"@deepseek-ai/dsh-client-ui-settings-general"
|
|
65
55
|
]
|
|
66
56
|
},
|
|
67
57
|
"compatibility": {
|
|
68
58
|
"dshReleases": {
|
|
69
|
-
"0.1.
|
|
59
|
+
"0.1.5-rc.1": "compatible",
|
|
60
|
+
"0.1.6-alpha.1": "compatible"
|
|
70
61
|
}
|
|
71
62
|
}
|
|
72
63
|
},
|
|
@@ -84,11 +75,11 @@
|
|
|
84
75
|
},
|
|
85
76
|
"peerDependencies": {
|
|
86
77
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
87
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
78
|
+
"@deepseek-ai/dsh-settings": ">=0.1.5-rc.1"
|
|
88
79
|
},
|
|
89
80
|
"devDependencies": {
|
|
90
81
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
91
|
-
"@deepseek-ai/dsh-settings": "0.1.
|
|
82
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.1",
|
|
92
83
|
"@deepseek-ai/schemastery": "^3.18.2",
|
|
93
84
|
"@types/node": "^26.2.0",
|
|
94
85
|
"@types/react": "^18.3.31",
|
|
@@ -97,5 +88,12 @@
|
|
|
97
88
|
"typescript": "^7.0.2",
|
|
98
89
|
"vitest": "^4.1.11"
|
|
99
90
|
},
|
|
100
|
-
"
|
|
101
|
-
|
|
91
|
+
"scripts": {
|
|
92
|
+
"build": "tsdown",
|
|
93
|
+
"typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.tests.json",
|
|
94
|
+
"test": "pnpm run typecheck && vitest run",
|
|
95
|
+
"test:unit": "vitest run",
|
|
96
|
+
"clean": "rm -rf lib coverage",
|
|
97
|
+
"verify": "pnpm run test && pnpm run build && pnpm pack --dry-run"
|
|
98
|
+
}
|
|
99
|
+
}
|