beauticode-dsh 1.0.0 → 1.0.1

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.zh-CN.md CHANGED
@@ -8,17 +8,17 @@
8
8
 
9
9
  beautiCode **不启动** DeepSeek Harness。请先自己运行 `dsh web`。
10
10
 
11
- 不需要 fork 仓库。发布到 npm 之后,一行安装:
11
+ 不需要 fork 仓库。一行安装:
12
12
 
13
13
  ```sh
14
- npx @beauticode/dsh-plugin
14
+ npx beauticode-dsh
15
15
  npx @deepseek-ai/dsh web
16
16
  ```
17
17
 
18
18
  已有 pnpm 时也可以:
19
19
 
20
20
  ```sh
21
- npx @deepseek-ai/dsh plugin --profile web add @beauticode/dsh-plugin
21
+ npx @deepseek-ai/dsh plugin --profile web add beauticode-dsh
22
22
  npx @deepseek-ai/dsh web
23
23
  ```
24
24
 
@@ -125,12 +125,29 @@ function shouldCopy(source) {
125
125
  return true;
126
126
  }
127
127
 
128
+ async function sameInstalledVersion(dest) {
129
+ try {
130
+ const incoming = JSON.parse(await fsp.readFile(path.join(here, "package.json"), "utf8"));
131
+ const installed = JSON.parse(await fsp.readFile(path.join(dest, "package.json"), "utf8"));
132
+ return (
133
+ incoming.name === installed.name &&
134
+ incoming.version === installed.version &&
135
+ fs.existsSync(path.join(dest, "index.mjs")) &&
136
+ fs.existsSync(path.join(dest, "vendor", "adapter-dsh", "index.js"))
137
+ );
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+
128
143
  async function copyPackage(dest) {
144
+ if (await sameInstalledVersion(dest)) return false;
129
145
  await fsp.rm(dest, { recursive: true, force: true });
130
146
  await fsp.cp(here, dest, {
131
147
  recursive: true,
132
148
  filter: (source) => shouldCopy(source),
133
149
  });
150
+ return true;
134
151
  }
135
152
 
136
153
  async function ensureEngine(dest) {
@@ -153,7 +170,7 @@ async function install(opts) {
153
170
  const homePatch = path.join(dshHome, "cordis.patch.yml");
154
171
 
155
172
  await fsp.mkdir(dest, { recursive: true });
156
- await copyPackage(dest);
173
+ const copied = await copyPackage(dest);
157
174
  await ensureEngine(dest);
158
175
  const indexFile = path.join(dest, "index.mjs");
159
176
  if (!fs.existsSync(indexFile)) {
@@ -161,13 +178,13 @@ async function install(opts) {
161
178
  }
162
179
 
163
180
  if (fs.existsSync(webPackage)) {
164
- await writePatch(webPatch, packageInsert());
181
+ await writePatch(webPatch, fileUriInsert(toFileUri(indexFile)));
165
182
  if (fs.existsSync(homePatch)) {
166
183
  const homeRaw = await fsp.readFile(homePatch, "utf8");
167
184
  if (hasBridge(homeRaw)) await removePatch(homePatch);
168
185
  }
169
186
  console.log(`已写入 ${webPatch}`);
170
- console.log(`插件已复制到 ${dest}`);
187
+ console.log(copied ? `插件已安装到 ${dest}` : `已复用已安装的插件 ${dest}`);
171
188
  console.log("请自己运行:npx @deepseek-ai/dsh web");
172
189
  return { dest, patch: webPatch };
173
190
  }
@@ -175,7 +192,7 @@ async function install(opts) {
175
192
  await fsp.mkdir(dshHome, { recursive: true });
176
193
  await writePatch(homePatch, fileUriInsert(toFileUri(indexFile)));
177
194
  console.log(`DSH web profile 还不存在,已写入 ${homePatch}`);
178
- console.log(`插件已复制到 ${dest}`);
195
+ console.log(copied ? `插件已安装到 ${dest}` : `已复用已安装的插件 ${dest}`);
179
196
  console.log("请自己运行:npx @deepseek-ai/dsh web");
180
197
  return { dest, patch: homePatch };
181
198
  }
package/cli.js CHANGED
@@ -125,12 +125,29 @@ function shouldCopy(source) {
125
125
  return true;
126
126
  }
127
127
 
128
+ async function sameInstalledVersion(dest) {
129
+ try {
130
+ const incoming = JSON.parse(await fsp.readFile(path.join(here, "package.json"), "utf8"));
131
+ const installed = JSON.parse(await fsp.readFile(path.join(dest, "package.json"), "utf8"));
132
+ return (
133
+ incoming.name === installed.name &&
134
+ incoming.version === installed.version &&
135
+ fs.existsSync(path.join(dest, "index.mjs")) &&
136
+ fs.existsSync(path.join(dest, "vendor", "adapter-dsh", "index.js"))
137
+ );
138
+ } catch {
139
+ return false;
140
+ }
141
+ }
142
+
128
143
  async function copyPackage(dest) {
144
+ if (await sameInstalledVersion(dest)) return false;
129
145
  await fsp.rm(dest, { recursive: true, force: true });
130
146
  await fsp.cp(here, dest, {
131
147
  recursive: true,
132
148
  filter: (source) => shouldCopy(source),
133
149
  });
150
+ return true;
134
151
  }
135
152
 
136
153
  async function ensureEngine(dest) {
@@ -153,7 +170,7 @@ async function install(opts) {
153
170
  const homePatch = path.join(dshHome, "cordis.patch.yml");
154
171
 
155
172
  await fsp.mkdir(dest, { recursive: true });
156
- await copyPackage(dest);
173
+ const copied = await copyPackage(dest);
157
174
  await ensureEngine(dest);
158
175
  const indexFile = path.join(dest, "index.mjs");
159
176
  if (!fs.existsSync(indexFile)) {
@@ -161,13 +178,13 @@ async function install(opts) {
161
178
  }
162
179
 
163
180
  if (fs.existsSync(webPackage)) {
164
- await writePatch(webPatch, packageInsert());
181
+ await writePatch(webPatch, fileUriInsert(toFileUri(indexFile)));
165
182
  if (fs.existsSync(homePatch)) {
166
183
  const homeRaw = await fsp.readFile(homePatch, "utf8");
167
184
  if (hasBridge(homeRaw)) await removePatch(homePatch);
168
185
  }
169
186
  console.log(`已写入 ${webPatch}`);
170
- console.log(`插件已复制到 ${dest}`);
187
+ console.log(copied ? `插件已安装到 ${dest}` : `已复用已安装的插件 ${dest}`);
171
188
  console.log("请自己运行:npx @deepseek-ai/dsh web");
172
189
  return { dest, patch: webPatch };
173
190
  }
@@ -175,7 +192,7 @@ async function install(opts) {
175
192
  await fsp.mkdir(dshHome, { recursive: true });
176
193
  await writePatch(homePatch, fileUriInsert(toFileUri(indexFile)));
177
194
  console.log(`DSH web profile 还不存在,已写入 ${homePatch}`);
178
- console.log(`插件已复制到 ${dest}`);
195
+ console.log(copied ? `插件已安装到 ${dest}` : `已复用已安装的插件 ${dest}`);
179
196
  console.log("请自己运行:npx @deepseek-ai/dsh web");
180
197
  return { dest, patch: homePatch };
181
198
  }
package/client.js CHANGED
@@ -189,7 +189,6 @@ html[data-bc-fish="true"] #root{opacity:0!important;visibility:hidden!important;
189
189
  function loadImage(url) {
190
190
  const image = new Image();
191
191
  image.alt = "";
192
- image.crossOrigin = "anonymous";
193
192
  image.decoding = "async";
194
193
  return new Promise((resolve, reject) => {
195
194
  image.onload = () => resolve(image);
@@ -298,7 +297,6 @@ html[data-bc-fish="true"] #root{opacity:0!important;visibility:hidden!important;
298
297
  video.playsInline = true;
299
298
  video.preload = "auto";
300
299
  video.poster = payload.imageUrl;
301
- video.crossOrigin = "anonymous";
302
300
  video.src = payload.videoUrl;
303
301
  node.replaceChildren(image, video);
304
302
  await waitForVideo(video);
package/host-apply.mjs CHANGED
@@ -10,7 +10,7 @@ import { canvasImagePath } from "./presets.mjs";
10
10
  const sessions = new Map();
11
11
 
12
12
  export const ENGINE_MISSING_MESSAGE =
13
- "beautiCode 插件未能加载本机导入引擎。请执行 npx @beauticode/dsh-plugin,或从完整安装目录加载桥接。";
13
+ "beautiCode 插件未能加载本机导入引擎。请执行 npx beauticode-dsh,或从完整安装目录加载桥接。";
14
14
 
15
15
  export function resolvePluginBaseUrl(ctx) {
16
16
  const port = ctx?.webServer?.port;
package/index.mjs CHANGED
@@ -168,12 +168,16 @@ function publicStatus(current, modes, clients, clientStates) {
168
168
  const playback =
169
169
  activeAcks.find((ack) => ack.ok === true && ack.playback?.hasVideo === true)
170
170
  ?.playback ?? null;
171
+ const failedAcks = activeAcks.filter((ack) => ack.ok !== true);
171
172
  return {
172
173
  ok: true,
173
174
  connectedClients: clients.size,
174
175
  current,
175
176
  readyClients: activeAcks.filter((ack) => ack.ok === true).length,
176
- failedClients: activeAcks.filter((ack) => ack.ok !== true).length,
177
+ failedClients: failedAcks.length,
178
+ lastRenderError:
179
+ failedAcks.find((ack) => typeof ack.error === "string" && ack.error)?.error ??
180
+ null,
177
181
  visibleClients: activeAcks.filter((ack) => ack.ok === true && ack.visible === true).length,
178
182
  modeReadyClients: modeReady.length,
179
183
  blockedClients: modeReady.filter((ack) => ack.blocked === true).length,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "beauticode-dsh",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Cordis plugin: image/video backgrounds for DeepSeek Harness web.",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",
@@ -120,9 +120,12 @@ export class DshHostApplier {
120
120
  };
121
121
  }
122
122
  if (currentMatches && last.failedClients > 0 && last.readyClients === 0) {
123
+ const detail = typeof last.lastRenderError === "string" && last.lastRenderError
124
+ ? ` (${last.lastRenderError})`
125
+ : "";
123
126
  return {
124
127
  status: "fail",
125
- reason: "DeepSeek Harness client failed to render the background.",
128
+ reason: `DeepSeek Harness client failed to render the background.${detail}`,
126
129
  details: { ...last },
127
130
  };
128
131
  }
@@ -50,12 +50,24 @@ async function hashOpenedFile(handle, size) {
50
50
  }
51
51
  return hash.digest("hex");
52
52
  }
53
- /** Exact match or app://* prefix (hub is loopback-only). */
53
+ function isLoopbackHttpOrigin(origin) {
54
+ try {
55
+ const url = new URL(origin);
56
+ return (url.protocol === "http:" &&
57
+ ["127.0.0.1", "localhost", "[::1]"].includes(url.hostname.toLowerCase()));
58
+ }
59
+ catch {
60
+ return false;
61
+ }
62
+ }
63
+ /** Exact match, loopback HTTP, or app://* prefix (hub is loopback-only). */
54
64
  function matchTrustedOrigin(origin, trusted) {
55
65
  if (!origin)
56
66
  return null;
57
67
  if (trusted.has(origin))
58
68
  return origin;
69
+ if (isLoopbackHttpOrigin(origin))
70
+ return origin;
59
71
  for (const prefix of TRUSTED_ORIGIN_PREFIXES) {
60
72
  if (origin.startsWith(prefix))
61
73
  return origin;
@@ -316,7 +328,7 @@ export class LoopbackMediaHub {
316
328
  queryToken = "";
317
329
  }
318
330
  const tokenOk = headerToken === pathToken || queryToken === pathToken;
319
- if ((origin && !trustedOrigin) || !tokenOk) {
331
+ if ((origin && !trustedOrigin && !isLoopbackHttpOrigin(origin)) || !tokenOk) {
320
332
  response.writeHead(403, { "Cache-Control": "no-store" });
321
333
  response.end();
322
334
  return;