dsh-any-background 0.2.9 → 0.3.0
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.md +48 -27
- package/README.zh.md +48 -26
- package/lib/client.js +1294 -271
- package/lib/client.js.map +1 -1
- package/lib/index.js +200 -42
- package/package.json +23 -11
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { access, mkdir, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
2
2
|
import { createReadStream, createWriteStream } from "node:fs";
|
|
3
|
+
import { basename, dirname, join } from "node:path";
|
|
3
4
|
import { Readable } from "node:stream";
|
|
4
5
|
import { dshHomePath } from "@deepseek-ai/dsh-home-paths";
|
|
5
6
|
//#region src/index.ts
|
|
@@ -100,7 +101,8 @@ const DEFAULT_CONFIG = {
|
|
|
100
101
|
trajectory: 30,
|
|
101
102
|
input: 30,
|
|
102
103
|
panel: 30,
|
|
103
|
-
produced: 30
|
|
104
|
+
produced: 30,
|
|
105
|
+
header: 30
|
|
104
106
|
},
|
|
105
107
|
strokes: {
|
|
106
108
|
bg: {
|
|
@@ -147,6 +149,11 @@ const DEFAULT_CONFIG = {
|
|
|
147
149
|
width: 0,
|
|
148
150
|
color: "auto",
|
|
149
151
|
customColor: "#808080"
|
|
152
|
+
},
|
|
153
|
+
header: {
|
|
154
|
+
width: 0,
|
|
155
|
+
color: "auto",
|
|
156
|
+
customColor: "#808080"
|
|
150
157
|
}
|
|
151
158
|
},
|
|
152
159
|
settingsOpacity: .5,
|
|
@@ -177,6 +184,7 @@ const DEFAULT_CONFIG = {
|
|
|
177
184
|
trajectoryOpacity: .5,
|
|
178
185
|
panelOpacity: .5,
|
|
179
186
|
producedOpacity: .5,
|
|
187
|
+
headerOpacity: .5,
|
|
180
188
|
profiles: [],
|
|
181
189
|
rotation: {
|
|
182
190
|
enabled: false,
|
|
@@ -197,6 +205,55 @@ const DEFAULT_CONFIG = {
|
|
|
197
205
|
schemeOverride: "auto",
|
|
198
206
|
activeProfile: null
|
|
199
207
|
};
|
|
208
|
+
const VERSION_RE = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/;
|
|
209
|
+
/** True when a string looks like a release version (a bare `~/.dsh` basename does not). */
|
|
210
|
+
function looksLikeVersion(value) {
|
|
211
|
+
return VERSION_RE.test(value);
|
|
212
|
+
}
|
|
213
|
+
/** Read the installed `@deepseek-ai/dsh` manifest version for a launcher dir,
|
|
214
|
+
* or null when the layout does not apply (plain `~/.dsh`, non-launcher hosts). */
|
|
215
|
+
async function readInstalledVersion(homeDir) {
|
|
216
|
+
const root = dirname(dirname(homeDir));
|
|
217
|
+
const versionSeg = basename(homeDir);
|
|
218
|
+
const manifest = join(root, "versions", versionSeg, "node_modules", "@deepseek-ai", "dsh", "package.json");
|
|
219
|
+
try {
|
|
220
|
+
const raw = JSON.parse(await readFile(manifest, "utf8"));
|
|
221
|
+
if (typeof raw.version === "string" && raw.version.trim() !== "") return raw.version.trim();
|
|
222
|
+
} catch {}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
/** Cached verdict: the probe touches the filesystem, and the host version never
|
|
226
|
+
* changes within a process lifetime. */
|
|
227
|
+
let hostInfoCache;
|
|
228
|
+
/** Map a release string onto the generation bucket the plugin gates features by.
|
|
229
|
+
* The published lines are `0.1.5`, `0.1.6` and `0.1.7`, so the bucket is the
|
|
230
|
+
* `major.minor.patch` prefix — every rc/alpha within a line shares it. */
|
|
231
|
+
function generationOf(version) {
|
|
232
|
+
if (version === null) return "unknown";
|
|
233
|
+
const m = /^(\d+\.\d+\.\d+)/.exec(version);
|
|
234
|
+
if (m === null) return "unknown";
|
|
235
|
+
const key = m[1];
|
|
236
|
+
return key === "0.1.5" || key === "0.1.6" || key === "0.1.7" ? key : "unknown";
|
|
237
|
+
}
|
|
238
|
+
/** Resolve (and cache) the host release + generation. */
|
|
239
|
+
async function resolveHostInfo() {
|
|
240
|
+
if (hostInfoCache !== void 0) return hostInfoCache;
|
|
241
|
+
const homeDir = dshHomePath();
|
|
242
|
+
let version = null;
|
|
243
|
+
try {
|
|
244
|
+
version = await readInstalledVersion(homeDir);
|
|
245
|
+
} catch {}
|
|
246
|
+
if (version === null) {
|
|
247
|
+
const seg = basename(homeDir);
|
|
248
|
+
if (looksLikeVersion(seg)) version = seg;
|
|
249
|
+
}
|
|
250
|
+
if (version !== null && !looksLikeVersion(version)) version = null;
|
|
251
|
+
hostInfoCache = {
|
|
252
|
+
version,
|
|
253
|
+
generation: generationOf(version)
|
|
254
|
+
};
|
|
255
|
+
return hostInfoCache;
|
|
256
|
+
}
|
|
200
257
|
const dataDir = () => dshHomePath(DATA_DIR);
|
|
201
258
|
const configPath = () => dshHomePath(DATA_DIR, CONFIG_FILE);
|
|
202
259
|
const wallpaperPath = () => dshHomePath(DATA_DIR, WALLPAPER_FILE);
|
|
@@ -297,7 +354,8 @@ const STROKE_GROUPS = [
|
|
|
297
354
|
"trajectory",
|
|
298
355
|
"input",
|
|
299
356
|
"panel",
|
|
300
|
-
"produced"
|
|
357
|
+
"produced",
|
|
358
|
+
"header"
|
|
301
359
|
];
|
|
302
360
|
const STROKE_COLOR_KEYS = [
|
|
303
361
|
"auto",
|
|
@@ -359,7 +417,8 @@ function normalizeConfig(raw) {
|
|
|
359
417
|
"trajectory",
|
|
360
418
|
"input",
|
|
361
419
|
"panel",
|
|
362
|
-
"produced"
|
|
420
|
+
"produced",
|
|
421
|
+
"header"
|
|
363
422
|
]) blurs[k] = clamp(bl[k], 0, 60, DEFAULT_CONFIG.blurs[k]);
|
|
364
423
|
return {
|
|
365
424
|
color,
|
|
@@ -387,6 +446,7 @@ function normalizeConfig(raw) {
|
|
|
387
446
|
trajectoryOpacity: clamp(r.trajectoryOpacity, 0, 1, DEFAULT_CONFIG.trajectoryOpacity),
|
|
388
447
|
panelOpacity: clamp(r.panelOpacity, 0, 1, DEFAULT_CONFIG.panelOpacity),
|
|
389
448
|
producedOpacity: clamp(r.producedOpacity, 0, 1, DEFAULT_CONFIG.producedOpacity),
|
|
449
|
+
headerOpacity: clamp(r.headerOpacity, 0, 1, DEFAULT_CONFIG.headerOpacity),
|
|
390
450
|
profiles: normalizeProfiles(r.profiles),
|
|
391
451
|
rotation: normalizeRotation(r.rotation),
|
|
392
452
|
schedule: normalizeSchedule(r.schedule),
|
|
@@ -471,7 +531,8 @@ function normalizeProfileAppearance(raw) {
|
|
|
471
531
|
chatTextOpacity: clamp(a.chatTextOpacity, 0, 1, DEFAULT_CONFIG.chatTextOpacity),
|
|
472
532
|
trajectoryOpacity: clamp(a.trajectoryOpacity, 0, 1, DEFAULT_CONFIG.trajectoryOpacity),
|
|
473
533
|
panelOpacity: clamp(a.panelOpacity, 0, 1, DEFAULT_CONFIG.panelOpacity),
|
|
474
|
-
producedOpacity: clamp(a.producedOpacity, 0, 1, DEFAULT_CONFIG.producedOpacity)
|
|
534
|
+
producedOpacity: clamp(a.producedOpacity, 0, 1, DEFAULT_CONFIG.producedOpacity),
|
|
535
|
+
headerOpacity: clamp(a.headerOpacity, 0, 1, DEFAULT_CONFIG.headerOpacity)
|
|
475
536
|
};
|
|
476
537
|
}
|
|
477
538
|
function normalizeProfiles(raw) {
|
|
@@ -536,12 +597,41 @@ async function ensureDir() {
|
|
|
536
597
|
console.warn(`dsh-any-background: cannot create data dir "${dataDir()}"`, e);
|
|
537
598
|
}
|
|
538
599
|
}
|
|
600
|
+
let configCacheKey = null;
|
|
601
|
+
let configCacheValue = null;
|
|
602
|
+
function dropConfigCache() {
|
|
603
|
+
configCacheKey = null;
|
|
604
|
+
configCacheValue = null;
|
|
605
|
+
}
|
|
539
606
|
async function readConfig() {
|
|
540
607
|
await ensureDir();
|
|
608
|
+
const file = configPath();
|
|
609
|
+
let text;
|
|
610
|
+
let key;
|
|
541
611
|
try {
|
|
542
|
-
const
|
|
543
|
-
|
|
612
|
+
const st = await stat(file);
|
|
613
|
+
key = {
|
|
614
|
+
mtimeMs: st.mtimeMs,
|
|
615
|
+
size: st.size
|
|
616
|
+
};
|
|
617
|
+
if (configCacheKey !== null && configCacheValue !== null && configCacheKey.mtimeMs === key.mtimeMs && configCacheKey.size === key.size) return configCacheValue;
|
|
618
|
+
text = await readFile(file, "utf8");
|
|
544
619
|
} catch {
|
|
620
|
+
dropConfigCache();
|
|
621
|
+
return { ...DEFAULT_CONFIG };
|
|
622
|
+
}
|
|
623
|
+
try {
|
|
624
|
+
const parsed = normalizeConfig(JSON.parse(text));
|
|
625
|
+
configCacheKey = key;
|
|
626
|
+
configCacheValue = parsed;
|
|
627
|
+
return parsed;
|
|
628
|
+
} catch (e) {
|
|
629
|
+
try {
|
|
630
|
+
const into = `${file}.corrupt-${Date.now()}`;
|
|
631
|
+
await rename(file, into);
|
|
632
|
+
console.warn(`dsh-any-background: theme-config.json was corrupt, archived to "${into}" and reset to defaults`, e);
|
|
633
|
+
} catch {}
|
|
634
|
+
dropConfigCache();
|
|
545
635
|
return { ...DEFAULT_CONFIG };
|
|
546
636
|
}
|
|
547
637
|
}
|
|
@@ -573,13 +663,19 @@ function warnUnknownConfigKeys(raw, normalized) {
|
|
|
573
663
|
}
|
|
574
664
|
async function writeConfig(config) {
|
|
575
665
|
await ensureDir();
|
|
666
|
+
const tmp = `${configPath()}.tmp`;
|
|
576
667
|
try {
|
|
577
668
|
const normalized = normalizeConfig(config);
|
|
578
669
|
warnUnknownConfigKeys(config, normalized);
|
|
579
|
-
await writeFile(
|
|
670
|
+
await writeFile(tmp, JSON.stringify(normalized, null, 2), "utf8");
|
|
671
|
+
await rename(tmp, configPath());
|
|
672
|
+
dropConfigCache();
|
|
580
673
|
return true;
|
|
581
674
|
} catch (e) {
|
|
582
675
|
console.error(`dsh-any-background: failed to write "${CONFIG_FILE}"`, e);
|
|
676
|
+
try {
|
|
677
|
+
await rm(tmp, { force: true });
|
|
678
|
+
} catch {}
|
|
583
679
|
return false;
|
|
584
680
|
}
|
|
585
681
|
}
|
|
@@ -618,6 +714,17 @@ function sniffImageMime(buf) {
|
|
|
618
714
|
if (buf.length >= 12 && buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) return "image/webp";
|
|
619
715
|
return "image/jpeg";
|
|
620
716
|
}
|
|
717
|
+
/** True when the leading bytes are a container the serve route knows how to
|
|
718
|
+
* sniff. The Content-Type only reflects what the server claims; validating the
|
|
719
|
+
* bytes themselves rejects a mislabeled or hostile payload with a clear error
|
|
720
|
+
* instead of persisting a file that renders as a broken image. */
|
|
721
|
+
function isImageBytes(buf) {
|
|
722
|
+
if (buf.length >= 4 && buf[0] === 137 && buf[1] === 80 && buf[2] === 78 && buf[3] === 71) return true;
|
|
723
|
+
if (buf.length >= 3 && buf[0] === 255 && buf[1] === 216 && buf[2] === 255) return true;
|
|
724
|
+
if (buf.length >= 6 && buf[0] === 71 && buf[1] === 73 && buf[2] === 70) return true;
|
|
725
|
+
if (buf.length >= 12 && buf[0] === 82 && buf[1] === 73 && buf[2] === 70 && buf[3] === 70 && buf[8] === 87 && buf[9] === 69 && buf[10] === 66 && buf[11] === 80) return true;
|
|
726
|
+
return false;
|
|
727
|
+
}
|
|
621
728
|
/** Download a wallpaper from a network URL and persist it into the local
|
|
622
729
|
* wallpaper.jpg slot (replacing whatever was stored), so type switches and
|
|
623
730
|
* rotation keep working through the single active slot. The response carries
|
|
@@ -690,6 +797,10 @@ async function writeWallpaperFromUrl(url) {
|
|
|
690
797
|
error: "read failed"
|
|
691
798
|
};
|
|
692
799
|
}
|
|
800
|
+
if (!isImageBytes(buf)) return {
|
|
801
|
+
ok: false,
|
|
802
|
+
error: "not an image"
|
|
803
|
+
};
|
|
693
804
|
await ensureDir();
|
|
694
805
|
try {
|
|
695
806
|
await writeFile(wallpaperPath(), buf);
|
|
@@ -911,21 +1022,28 @@ async function writeVideoFromUrl(url) {
|
|
|
911
1022
|
const out = createWriteStream(tmp);
|
|
912
1023
|
let received = 0;
|
|
913
1024
|
let failed = false;
|
|
914
|
-
const fail = () => {
|
|
915
|
-
if (failed) return;
|
|
916
|
-
failed = true;
|
|
917
|
-
out.destroy();
|
|
918
|
-
rm(tmp, { force: true });
|
|
919
|
-
};
|
|
920
1025
|
const nodeStream = Readable.fromWeb(res.body);
|
|
921
1026
|
let idle = null;
|
|
1027
|
+
const clearIdle = () => {
|
|
1028
|
+
if (idle !== null) {
|
|
1029
|
+
clearTimeout(idle);
|
|
1030
|
+
idle = null;
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
922
1033
|
const pokeIdle = () => {
|
|
923
|
-
|
|
1034
|
+
clearIdle();
|
|
924
1035
|
idle = setTimeout(() => {
|
|
925
1036
|
nodeStream.destroy();
|
|
926
1037
|
fail();
|
|
927
1038
|
}, VIDEO_FETCH_IDLE_TIMEOUT);
|
|
928
1039
|
};
|
|
1040
|
+
const fail = () => {
|
|
1041
|
+
if (failed) return;
|
|
1042
|
+
failed = true;
|
|
1043
|
+
clearIdle();
|
|
1044
|
+
out.destroy();
|
|
1045
|
+
rmWhenClosed(out, tmp);
|
|
1046
|
+
};
|
|
929
1047
|
nodeStream.on("data", (chunk) => {
|
|
930
1048
|
pokeIdle();
|
|
931
1049
|
received += chunk.byteLength;
|
|
@@ -934,10 +1052,7 @@ async function writeVideoFromUrl(url) {
|
|
|
934
1052
|
fail();
|
|
935
1053
|
}
|
|
936
1054
|
});
|
|
937
|
-
nodeStream.on("end",
|
|
938
|
-
if (idle) clearTimeout(idle);
|
|
939
|
-
idle = null;
|
|
940
|
-
});
|
|
1055
|
+
nodeStream.on("end", clearIdle);
|
|
941
1056
|
nodeStream.on("aborted", fail);
|
|
942
1057
|
nodeStream.on("error", fail);
|
|
943
1058
|
out.on("error", fail);
|
|
@@ -1001,6 +1116,37 @@ async function writeVideo(dataUrl) {
|
|
|
1001
1116
|
return false;
|
|
1002
1117
|
}
|
|
1003
1118
|
}
|
|
1119
|
+
/** Drop a partial upload's temp file once its sink is really closed. Windows
|
|
1120
|
+
* refuses to unlink a file that still has an open handle, so removing it in the
|
|
1121
|
+
* same tick as `out.destroy()` silently fails and leaves the aborted transfer
|
|
1122
|
+
* on disk (up to the full limit) until some later upload overwrites it. */
|
|
1123
|
+
function rmWhenClosed(out, tmp) {
|
|
1124
|
+
const drop = () => {
|
|
1125
|
+
rm(tmp, { force: true });
|
|
1126
|
+
};
|
|
1127
|
+
if (out.closed === true) drop();
|
|
1128
|
+
else out.once("close", drop);
|
|
1129
|
+
}
|
|
1130
|
+
/** Reject an oversized upload with a real status and tear the transfer down.
|
|
1131
|
+
* The bare req.destroy()+fail() path left the client holding a network error
|
|
1132
|
+
* with no way to tell "too large" from "connection died". The body is machine
|
|
1133
|
+
* readable (`error` + the enforced `limit`) so the panel can phrase the
|
|
1134
|
+
* refusal in the user's own language, with `message` kept for logs. */
|
|
1135
|
+
function rejectOversizedUpload(req, res, fail, limit) {
|
|
1136
|
+
try {
|
|
1137
|
+
res.writeHead(413, { "Content-Type": "application/json" });
|
|
1138
|
+
res.end(JSON.stringify({
|
|
1139
|
+
ok: false,
|
|
1140
|
+
error: "too large",
|
|
1141
|
+
limit,
|
|
1142
|
+
message: `too large (limit ${limit})`
|
|
1143
|
+
}));
|
|
1144
|
+
} catch {}
|
|
1145
|
+
fail();
|
|
1146
|
+
try {
|
|
1147
|
+
req.destroy();
|
|
1148
|
+
} catch {}
|
|
1149
|
+
}
|
|
1004
1150
|
/** Stream the stored video: correct MIME, no caching, Range answers so the
|
|
1005
1151
|
* browser can seek (snapshot capture does). */
|
|
1006
1152
|
async function serveVideo(req, res) {
|
|
@@ -1104,16 +1250,14 @@ async function handleVideoUpload(req, res) {
|
|
|
1104
1250
|
if (failed) return;
|
|
1105
1251
|
failed = true;
|
|
1106
1252
|
out.destroy();
|
|
1107
|
-
|
|
1253
|
+
rmWhenClosed(out, tmp);
|
|
1108
1254
|
};
|
|
1109
1255
|
req.on("aborted", fail);
|
|
1110
1256
|
req.on("error", fail);
|
|
1111
1257
|
req.on("data", (chunk) => {
|
|
1258
|
+
if (failed) return;
|
|
1112
1259
|
received += chunk.byteLength;
|
|
1113
|
-
if (received > VIDEO_UPLOAD_MAX)
|
|
1114
|
-
req.destroy();
|
|
1115
|
-
fail();
|
|
1116
|
-
}
|
|
1260
|
+
if (received > VIDEO_UPLOAD_MAX) rejectOversizedUpload(req, res, fail, "2 GB");
|
|
1117
1261
|
});
|
|
1118
1262
|
out.on("error", () => {
|
|
1119
1263
|
fail();
|
|
@@ -1226,7 +1370,7 @@ async function handleWallpaperUpload(req, res) {
|
|
|
1226
1370
|
if (failed) return;
|
|
1227
1371
|
failed = true;
|
|
1228
1372
|
out.destroy();
|
|
1229
|
-
|
|
1373
|
+
rmWhenClosed(out, tmp);
|
|
1230
1374
|
};
|
|
1231
1375
|
req.on("aborted", fail);
|
|
1232
1376
|
req.on("error", fail);
|
|
@@ -1238,11 +1382,9 @@ async function handleWallpaperUpload(req, res) {
|
|
|
1238
1382
|
} catch {}
|
|
1239
1383
|
});
|
|
1240
1384
|
req.on("data", (chunk) => {
|
|
1385
|
+
if (failed) return;
|
|
1241
1386
|
received += chunk.byteLength;
|
|
1242
|
-
if (received > WALLPAPER_UPLOAD_MAX)
|
|
1243
|
-
req.destroy();
|
|
1244
|
-
fail();
|
|
1245
|
-
}
|
|
1387
|
+
if (received > WALLPAPER_UPLOAD_MAX) rejectOversizedUpload(req, res, fail, "100 MB");
|
|
1246
1388
|
});
|
|
1247
1389
|
req.pipe(out);
|
|
1248
1390
|
out.on("finish", async () => {
|
|
@@ -1338,7 +1480,7 @@ async function handleFontUpload(req, res) {
|
|
|
1338
1480
|
if (failed) return;
|
|
1339
1481
|
failed = true;
|
|
1340
1482
|
out.destroy();
|
|
1341
|
-
|
|
1483
|
+
rmWhenClosed(out, tmp);
|
|
1342
1484
|
};
|
|
1343
1485
|
req.on("aborted", fail);
|
|
1344
1486
|
req.on("error", fail);
|
|
@@ -1350,11 +1492,9 @@ async function handleFontUpload(req, res) {
|
|
|
1350
1492
|
} catch {}
|
|
1351
1493
|
});
|
|
1352
1494
|
req.on("data", (chunk) => {
|
|
1495
|
+
if (failed) return;
|
|
1353
1496
|
received += chunk.byteLength;
|
|
1354
|
-
if (received > FONT_UPLOAD_MAX)
|
|
1355
|
-
req.destroy();
|
|
1356
|
-
fail();
|
|
1357
|
-
}
|
|
1497
|
+
if (received > FONT_UPLOAD_MAX) rejectOversizedUpload(req, res, fail, "100 MB");
|
|
1358
1498
|
});
|
|
1359
1499
|
req.pipe(out);
|
|
1360
1500
|
out.on("finish", async () => {
|
|
@@ -1458,8 +1598,7 @@ function isoWeekKey(d) {
|
|
|
1458
1598
|
* old one never reaches the screen. The client's maybeRotate stays as a
|
|
1459
1599
|
* fallback and skips when this already advanced (the `rotated` flag). */
|
|
1460
1600
|
async function advanceRotationIfDue() {
|
|
1461
|
-
const
|
|
1462
|
-
const rot = cfg.rotation;
|
|
1601
|
+
const rot = (await readConfig()).rotation;
|
|
1463
1602
|
if (!rot.enabled || rot.items.length === 0) return false;
|
|
1464
1603
|
const now = /* @__PURE__ */ new Date();
|
|
1465
1604
|
const last = rot.lastRotate !== null ? new Date(rot.lastRotate) : null;
|
|
@@ -1477,12 +1616,14 @@ async function advanceRotationIfDue() {
|
|
|
1477
1616
|
console.error("dsh-any-background: failed to advance the rotation pool", e);
|
|
1478
1617
|
return false;
|
|
1479
1618
|
}
|
|
1480
|
-
|
|
1481
|
-
...
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1619
|
+
if (!await writeConfig({
|
|
1620
|
+
...await readConfig(),
|
|
1621
|
+
rotation: {
|
|
1622
|
+
...rot,
|
|
1623
|
+
current: idx,
|
|
1624
|
+
lastRotate: now.toISOString()
|
|
1625
|
+
}
|
|
1626
|
+
})) return false;
|
|
1486
1627
|
return true;
|
|
1487
1628
|
}
|
|
1488
1629
|
/** Dispatch one decoded RPC method to the matching persistence routine and
|
|
@@ -1504,7 +1645,8 @@ async function handleRpcMethod(endpoint, payload) {
|
|
|
1504
1645
|
videoUrl: await videoUrl(),
|
|
1505
1646
|
fontUrl: await fontUrl(),
|
|
1506
1647
|
rotated,
|
|
1507
|
-
firstRun
|
|
1648
|
+
firstRun,
|
|
1649
|
+
host: await resolveHostInfo()
|
|
1508
1650
|
}
|
|
1509
1651
|
};
|
|
1510
1652
|
}
|
|
@@ -1588,6 +1730,22 @@ function apply(ctx) {
|
|
|
1588
1730
|
}));
|
|
1589
1731
|
return;
|
|
1590
1732
|
}
|
|
1733
|
+
const declaredLen = Number(req.headers["content-length"] ?? "");
|
|
1734
|
+
if (Number.isFinite(declaredLen) && declaredLen > RPC_BODY_MAX) {
|
|
1735
|
+
res.writeHead(413, {
|
|
1736
|
+
"Content-Type": "application/json",
|
|
1737
|
+
connection: "close"
|
|
1738
|
+
});
|
|
1739
|
+
res.end(JSON.stringify({
|
|
1740
|
+
ok: false,
|
|
1741
|
+
error: {
|
|
1742
|
+
code: "dsh-any-background/too-large",
|
|
1743
|
+
message: `body exceeds ${RPC_BODY_MAX} bytes; uploads must use the binary routes`,
|
|
1744
|
+
details: {}
|
|
1745
|
+
}
|
|
1746
|
+
}));
|
|
1747
|
+
return;
|
|
1748
|
+
}
|
|
1591
1749
|
const pathname = new URL(req.url ?? "/", "http://dsh.internal").pathname;
|
|
1592
1750
|
const endpoint = pathname.startsWith(`${RPC_CHANNEL}/`) ? pathname.slice(20) : void 0;
|
|
1593
1751
|
if (endpoint === void 0 || endpoint.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-any-background",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Appearance plugin for DeepSeek Harness: custom theme color (PS-style color wheel), background wallpaper with opacity/blur controls, and separate main/settings interface opacity.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"deepseek-harness",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"author": "Tkingxiao",
|
|
24
24
|
"type": "module",
|
|
25
25
|
"engines": {
|
|
26
|
-
"dsh": "
|
|
26
|
+
"dsh": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1"
|
|
27
27
|
},
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
@@ -67,15 +67,23 @@
|
|
|
67
67
|
},
|
|
68
68
|
"peerDependencies": {
|
|
69
69
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
70
|
-
"@deepseek-ai/dsh-client-locale": "
|
|
71
|
-
"@deepseek-ai/dsh-client-store": "
|
|
72
|
-
"@deepseek-ai/dsh-client-connection": "
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-settings": "
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-
|
|
75
|
-
"@deepseek-ai/dsh-
|
|
76
|
-
"
|
|
70
|
+
"@deepseek-ai/dsh-client-locale": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
71
|
+
"@deepseek-ai/dsh-client-store": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
72
|
+
"@deepseek-ai/dsh-client-connection": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-settings": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-theme": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
76
|
+
"@deepseek-ai/dsh-invariants": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
77
|
+
"react": "^18.2.0",
|
|
78
|
+
"dsh-better-sidebar": ">=0.4.0"
|
|
77
79
|
},
|
|
78
80
|
"peerDependenciesMeta": {
|
|
81
|
+
"dsh-better-sidebar": {
|
|
82
|
+
"optional": true
|
|
83
|
+
},
|
|
84
|
+
"@deepseek-ai/dsh-client-ui-sidebar-right": {
|
|
85
|
+
"optional": true
|
|
86
|
+
},
|
|
79
87
|
"react": {
|
|
80
88
|
"optional": true
|
|
81
89
|
},
|
|
@@ -103,9 +111,13 @@
|
|
|
103
111
|
},
|
|
104
112
|
"dsh": {
|
|
105
113
|
"compatibility": {
|
|
106
|
-
"dsh": "0.1.5-rc.2",
|
|
114
|
+
"dsh": "0.1.5-rc.2 || 0.1.5-rc.3 || 0.1.6-alpha.1 || 0.1.6-alpha.2 || 0.1.7-alpha.1",
|
|
107
115
|
"dshReleases": {
|
|
108
|
-
"0.1.5-rc.2": "compatible"
|
|
116
|
+
"0.1.5-rc.2": "compatible",
|
|
117
|
+
"0.1.5-rc.3": "compatible",
|
|
118
|
+
"0.1.6-alpha.1": "compatible",
|
|
119
|
+
"0.1.6-alpha.2": "compatible",
|
|
120
|
+
"0.1.7-alpha.1": "compatible"
|
|
109
121
|
}
|
|
110
122
|
},
|
|
111
123
|
"bundle": {
|