@linxin666/dsh-remote-web-ui 0.3.3 → 0.3.4
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/assets/mobile-service-worker.js +1 -2
- package/lib/client.js +4 -4
- package/lib/client.js.map +1 -1
- package/lib/index.js +118 -8
- package/lib/mobile.js +2 -6
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts +2 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +2 -2
- package/lib/types/dsh-home.d.ts.map +1 -1
- package/lib/types/dsh-home.js +9 -3
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +20 -2
- package/lib/types/mobile/api.d.ts.map +1 -1
- package/lib/types/mobile/api.js +2 -3
- package/lib/types/mobile-api.d.ts +14 -0
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +53 -1
- package/lib/types/mobile-routes.d.ts.map +1 -1
- package/lib/types/mobile-routes.js +25 -2
- package/package.json +3 -1
- package/src/client/PluginSettingsCard.tsx +4 -2
- package/src/client/settings-card.module.css +5 -0
- package/src/dsh-home.ts +9 -3
- package/src/index.ts +20 -2
- package/src/mobile/api.ts +2 -3
- package/src/mobile-api.ts +59 -0
- package/src/mobile-routes.ts +24 -2
package/lib/index.js
CHANGED
|
@@ -6,9 +6,11 @@ import z from "schemastery";
|
|
|
6
6
|
import { randomBytes } from "node:crypto";
|
|
7
7
|
import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
8
8
|
import { homedir, networkInterfaces } from "node:os";
|
|
9
|
+
import { isAbsolute as isAbsolute$1, join as join$1 } from "node:path/posix";
|
|
9
10
|
import { z as z$1 } from "zod";
|
|
10
11
|
import { readFile } from "node:fs/promises";
|
|
11
12
|
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { gzipSync } from "node:zlib";
|
|
12
14
|
import { RpcId } from "@deepseek-ai/dsh-host-apiproxy/api/rpc";
|
|
13
15
|
import http, { request } from "node:http";
|
|
14
16
|
import { connect } from "node:net";
|
|
@@ -650,8 +652,9 @@ function sanitizeUserAgent(raw) {
|
|
|
650
652
|
*/
|
|
651
653
|
/** Expand a leading ~ (or ~user) in a path, platform-style. */
|
|
652
654
|
function expandHome(path, home = homedir()) {
|
|
655
|
+
const j = home.startsWith("/") ? join$1 : join;
|
|
653
656
|
if (path === "~") return home;
|
|
654
|
-
if (path.startsWith("~/") || path.startsWith("~\\")) return
|
|
657
|
+
if (path.startsWith("~/") || path.startsWith("~\\")) return j(home, path.slice(2));
|
|
655
658
|
return path;
|
|
656
659
|
}
|
|
657
660
|
/**
|
|
@@ -661,12 +664,15 @@ function expandHome(path, home = homedir()) {
|
|
|
661
664
|
* @returns the absolute DSH home path.
|
|
662
665
|
*/
|
|
663
666
|
function resolveDshHome(env = process.env, home = homedir()) {
|
|
667
|
+
const isPosix = home.startsWith("/");
|
|
668
|
+
const j = isPosix ? join$1 : join;
|
|
669
|
+
const isAbs = isPosix ? isAbsolute$1 : isAbsolute;
|
|
664
670
|
const raw = env.DSH_HOME;
|
|
665
671
|
if (raw !== void 0 && raw.trim() !== "") {
|
|
666
672
|
const expanded = expandHome(raw.trim(), home);
|
|
667
|
-
return
|
|
673
|
+
return isAbs(expanded) ? expanded : j(process.cwd(), expanded);
|
|
668
674
|
}
|
|
669
|
-
return
|
|
675
|
+
return j(home, ".dsh");
|
|
670
676
|
}
|
|
671
677
|
/** Resolve the DSH home directory from the live environment. */
|
|
672
678
|
function dshHome() {
|
|
@@ -1442,7 +1448,8 @@ function makeMobileRoutes() {
|
|
|
1442
1448
|
writeStatic(res, 200, "text/html", offlineHtml());
|
|
1443
1449
|
};
|
|
1444
1450
|
let bundleBody;
|
|
1445
|
-
|
|
1451
|
+
let bundleGzip;
|
|
1452
|
+
const handleBundle = async (req, res) => {
|
|
1446
1453
|
if (bundleBody === void 0) {
|
|
1447
1454
|
const path = mobileBundlePath();
|
|
1448
1455
|
if (!existsSync(path)) {
|
|
@@ -1451,12 +1458,31 @@ function makeMobileRoutes() {
|
|
|
1451
1458
|
}
|
|
1452
1459
|
try {
|
|
1453
1460
|
bundleBody = await readFile(path, "utf8");
|
|
1461
|
+
bundleGzip = gzipSync(Buffer.from(bundleBody, "utf8"));
|
|
1454
1462
|
} catch {
|
|
1455
1463
|
writeStatic(res, 500, "text/plain", "failed to read the mobile bundle");
|
|
1456
1464
|
return;
|
|
1457
1465
|
}
|
|
1458
1466
|
}
|
|
1459
|
-
|
|
1467
|
+
const acceptEncoding = req.headers["accept-encoding"] ?? "";
|
|
1468
|
+
if (typeof acceptEncoding === "string" && acceptEncoding.includes("gzip") && bundleGzip !== void 0) {
|
|
1469
|
+
res.writeHead(200, {
|
|
1470
|
+
"content-type": "text/javascript; charset=utf-8",
|
|
1471
|
+
"content-encoding": "gzip",
|
|
1472
|
+
"cache-control": "no-cache",
|
|
1473
|
+
vary: "Accept-Encoding",
|
|
1474
|
+
"referrer-policy": "no-referrer"
|
|
1475
|
+
});
|
|
1476
|
+
res.end(bundleGzip);
|
|
1477
|
+
} else {
|
|
1478
|
+
res.writeHead(200, {
|
|
1479
|
+
"content-type": "text/javascript; charset=utf-8",
|
|
1480
|
+
"cache-control": "no-cache",
|
|
1481
|
+
vary: "Accept-Encoding",
|
|
1482
|
+
"referrer-policy": "no-referrer"
|
|
1483
|
+
});
|
|
1484
|
+
res.end(bundleBody);
|
|
1485
|
+
}
|
|
1460
1486
|
};
|
|
1461
1487
|
let workerBody;
|
|
1462
1488
|
const handleWorker = async (_req, res) => {
|
|
@@ -1568,6 +1594,8 @@ const MOBILE_ALLOWLIST = /* @__PURE__ */ new Set([
|
|
|
1568
1594
|
const MOBILE_PREFERENCES_METHOD = "mobile.preferences";
|
|
1569
1595
|
const MOBILE_PENDING_METHOD = "mobile.pending";
|
|
1570
1596
|
const MOBILE_RESPOND_METHOD = "mobile.respond";
|
|
1597
|
+
/** Execute a slash command through the host command registry (#1125). */
|
|
1598
|
+
const MOBILE_COMMAND_METHOD = "mobile.command";
|
|
1571
1599
|
/** One session.list page (thin phones load incrementally). */
|
|
1572
1600
|
const SESSION_PAGE_SIZE = 20;
|
|
1573
1601
|
/** SSE keep-alive ping cadence for the live mux stream (single connection). */
|
|
@@ -1650,7 +1678,7 @@ function makeMobileApiRoutes(deps) {
|
|
|
1650
1678
|
return;
|
|
1651
1679
|
}
|
|
1652
1680
|
const method = pathname.slice(MOBILE_API_METHOD_PREFIX.length);
|
|
1653
|
-
const local = method === MOBILE_PREFERENCES_METHOD || method === MOBILE_PENDING_METHOD || method === MOBILE_RESPOND_METHOD;
|
|
1681
|
+
const local = method === MOBILE_PREFERENCES_METHOD || method === MOBILE_PENDING_METHOD || method === MOBILE_RESPOND_METHOD || method === MOBILE_COMMAND_METHOD;
|
|
1654
1682
|
if (!MOBILE_ALLOWLIST.has(method) && !local) {
|
|
1655
1683
|
writeJson(res, 403, {
|
|
1656
1684
|
ok: false,
|
|
@@ -1736,6 +1764,73 @@ function makeMobileApiRoutes(deps) {
|
|
|
1736
1764
|
}
|
|
1737
1765
|
});
|
|
1738
1766
|
}
|
|
1767
|
+
} else if (method === MOBILE_COMMAND_METHOD) {
|
|
1768
|
+
const payload = parsed.payload;
|
|
1769
|
+
const sessionId = payload?.sessionId;
|
|
1770
|
+
const line = payload?.line;
|
|
1771
|
+
if (typeof sessionId !== "string" || typeof line !== "string") writeJson(res, 200, {
|
|
1772
|
+
type: "server-response",
|
|
1773
|
+
rpcId,
|
|
1774
|
+
result: {
|
|
1775
|
+
ok: false,
|
|
1776
|
+
error: {
|
|
1777
|
+
code: "bad-request",
|
|
1778
|
+
message: "missing sessionId or line"
|
|
1779
|
+
}
|
|
1780
|
+
}
|
|
1781
|
+
});
|
|
1782
|
+
else if (deps.commandDispatcher === void 0) writeJson(res, 200, {
|
|
1783
|
+
type: "server-response",
|
|
1784
|
+
rpcId,
|
|
1785
|
+
result: {
|
|
1786
|
+
ok: false,
|
|
1787
|
+
error: {
|
|
1788
|
+
code: "unavailable",
|
|
1789
|
+
message: "command dispatcher is not available"
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
});
|
|
1793
|
+
else try {
|
|
1794
|
+
const abort = new AbortController();
|
|
1795
|
+
res.on("close", () => {
|
|
1796
|
+
if (!res.writableEnded) abort.abort();
|
|
1797
|
+
});
|
|
1798
|
+
const outcome = await deps.commandDispatcher.execute(sessionId, line, abort.signal);
|
|
1799
|
+
if ("error" in outcome) writeJson(res, 200, {
|
|
1800
|
+
type: "server-response",
|
|
1801
|
+
rpcId,
|
|
1802
|
+
result: {
|
|
1803
|
+
ok: false,
|
|
1804
|
+
error: {
|
|
1805
|
+
code: outcome.error,
|
|
1806
|
+
message: outcome.error
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
});
|
|
1810
|
+
else writeJson(res, 200, {
|
|
1811
|
+
type: "server-response",
|
|
1812
|
+
rpcId,
|
|
1813
|
+
result: {
|
|
1814
|
+
ok: true,
|
|
1815
|
+
value: {
|
|
1816
|
+
matched: true,
|
|
1817
|
+
result: outcome.result
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
});
|
|
1821
|
+
} catch (error) {
|
|
1822
|
+
writeJson(res, 200, {
|
|
1823
|
+
type: "server-response",
|
|
1824
|
+
rpcId,
|
|
1825
|
+
result: {
|
|
1826
|
+
ok: false,
|
|
1827
|
+
error: {
|
|
1828
|
+
code: "internal",
|
|
1829
|
+
message: error instanceof Error ? error.message : String(error)
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
});
|
|
1833
|
+
}
|
|
1739
1834
|
}
|
|
1740
1835
|
return;
|
|
1741
1836
|
}
|
|
@@ -3712,7 +3807,12 @@ const UUID_POLYFILL_SCRIPT = [
|
|
|
3712
3807
|
/** Stable cordis plugin name. */
|
|
3713
3808
|
const name = "remote-web-ui";
|
|
3714
3809
|
/** Services required before the pairing surfaces can mount. */
|
|
3715
|
-
const inject = [
|
|
3810
|
+
const inject = [
|
|
3811
|
+
"webServer",
|
|
3812
|
+
"apiProxy",
|
|
3813
|
+
"commands",
|
|
3814
|
+
"agents"
|
|
3815
|
+
];
|
|
3716
3816
|
/**
|
|
3717
3817
|
* Settings namespace of the remote-control capability — the section the web
|
|
3718
3818
|
* settings surface edits. Spelled here rather than imported: the browser
|
|
@@ -3917,7 +4017,17 @@ function applyImpl(ctx, config) {
|
|
|
3917
4017
|
service,
|
|
3918
4018
|
apiProxy,
|
|
3919
4019
|
pendingTracker: new PendingTracker(),
|
|
3920
|
-
mobileEnterToSend: () => resolve().mobileEnterToSend
|
|
4020
|
+
mobileEnterToSend: () => resolve().mobileEnterToSend,
|
|
4021
|
+
commandDispatcher: ctx.commands !== void 0 && ctx.agents !== void 0 ? { async execute(sessionId, line, signal) {
|
|
4022
|
+
const agent = ctx.agents.get(sessionId);
|
|
4023
|
+
if (agent === void 0) return { error: "session-not-found" };
|
|
4024
|
+
const execution = await ctx.commands.execute(agent, line, [], signal);
|
|
4025
|
+
if (execution === void 0) return { error: "unknown-command" };
|
|
4026
|
+
return {
|
|
4027
|
+
ok: true,
|
|
4028
|
+
result: execution.result
|
|
4029
|
+
};
|
|
4030
|
+
} } : void 0
|
|
3921
4031
|
}) : [],
|
|
3922
4032
|
...apiProxy !== void 0 ? makePairedModelCatalogRoutes({
|
|
3923
4033
|
service,
|
package/lib/mobile.js
CHANGED
|
@@ -7610,13 +7610,9 @@ async function prompt(sessionId, text) {
|
|
|
7610
7610
|
}
|
|
7611
7611
|
/** Send one slash command line (e.g. `/permission workspace-write`). */
|
|
7612
7612
|
async function sendCommand(sessionId, line) {
|
|
7613
|
-
return await callUnary("
|
|
7613
|
+
return await callUnary("mobile.command", {
|
|
7614
7614
|
sessionId,
|
|
7615
|
-
|
|
7616
|
-
content: [{
|
|
7617
|
-
type: "text",
|
|
7618
|
-
text: line
|
|
7619
|
-
}]
|
|
7615
|
+
line
|
|
7620
7616
|
});
|
|
7621
7617
|
}
|
|
7622
7618
|
/**
|