dsh-plugin-capabilities 0.1.0 → 0.1.2
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 +16 -11
- package/lib/client.js +60 -6
- package/lib/client.js.map +2 -2
- package/lib/index.js +72 -3
- package/lib/index.js.map +3 -3
- package/package.json +1 -1
- package/src/client/McpTab.tsx +44 -6
- package/src/client/index.ts +8 -1
- package/src/client/locales.ts +10 -0
- package/src/mcp.test.ts +8 -0
- package/src/mcp.ts +7 -4
- package/src/restart.test.ts +52 -0
- package/src/restart.ts +94 -0
- package/src/routes.ts +24 -0
package/lib/index.js
CHANGED
|
@@ -796,6 +796,52 @@ function sendJson(response, status, body) {
|
|
|
796
796
|
response.end(payload);
|
|
797
797
|
}
|
|
798
798
|
|
|
799
|
+
// src/restart.ts
|
|
800
|
+
import { spawn } from "node:child_process";
|
|
801
|
+
import { openSync } from "node:fs";
|
|
802
|
+
import { tmpdir } from "node:os";
|
|
803
|
+
import { dirname, resolve } from "node:path";
|
|
804
|
+
function dshLaunch(argv = process.argv, execArgv = process.execArgv) {
|
|
805
|
+
const entry = argv[1];
|
|
806
|
+
if (entry !== void 0 && /[\\/](?:bin\.(?:js|ts)|dsh)$/.test(entry)) {
|
|
807
|
+
const abs = resolve(entry);
|
|
808
|
+
return { file: process.execPath, args: [...execArgv, abs, ...argv.slice(2)], cwd: dirname(abs), viaShell: false };
|
|
809
|
+
}
|
|
810
|
+
return { file: "dsh", args: [...argv.slice(2)], cwd: void 0, viaShell: process.platform === "win32" };
|
|
811
|
+
}
|
|
812
|
+
function scheduleRestart(launch) {
|
|
813
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-").slice(0, 19);
|
|
814
|
+
const logOut = `${tmpdir()}${tmpdir().endsWith("/") ? "" : "\\"}dsh-plugin-capabilities-restart-${stamp}.out.log`;
|
|
815
|
+
const logErr = logOut.replace(".out.log", ".err.log");
|
|
816
|
+
const child = spawn(launch.file, launch.args, {
|
|
817
|
+
cwd: launch.cwd,
|
|
818
|
+
stdio: ["ignore", openSync(logOut, "a"), openSync(logErr, "a")],
|
|
819
|
+
env: process.env,
|
|
820
|
+
shell: launch.viaShell,
|
|
821
|
+
windowsHide: true
|
|
822
|
+
});
|
|
823
|
+
child.unref();
|
|
824
|
+
setTimeout(() => process.kill(process.pid, "SIGTERM"), 500);
|
|
825
|
+
return { pid: process.pid, replacementPid: child.pid, logOut, logErr };
|
|
826
|
+
}
|
|
827
|
+
function trustedRestartRequest(request, socketAddress) {
|
|
828
|
+
const address = socketAddress ?? (request.socket.remoteAddress ?? "");
|
|
829
|
+
if (address !== "127.0.0.1" && address !== "::1" && address !== "::ffff:127.0.0.1") return false;
|
|
830
|
+
if (request.headers.forwarded !== void 0 || request.headers["x-forwarded-for"] !== void 0 || request.headers["x-real-ip"] !== void 0) return false;
|
|
831
|
+
const origin = request.headers.origin;
|
|
832
|
+
const host = request.headers.host;
|
|
833
|
+
if (origin === void 0 || host === void 0) return false;
|
|
834
|
+
try {
|
|
835
|
+
const parsed = new URL(origin);
|
|
836
|
+
return (parsed.protocol === "http:" || parsed.protocol === "https:") && parsed.host === host;
|
|
837
|
+
} catch {
|
|
838
|
+
return false;
|
|
839
|
+
}
|
|
840
|
+
}
|
|
841
|
+
function restartOwnedByShell(env = process.env) {
|
|
842
|
+
return env.DSH_DESKTOP === "1";
|
|
843
|
+
}
|
|
844
|
+
|
|
799
845
|
// src/skills.ts
|
|
800
846
|
import { existsSync as existsSync2, mkdirSync, rmSync, statSync, writeFileSync } from "node:fs";
|
|
801
847
|
import { homedir as homedir3 } from "node:os";
|
|
@@ -899,7 +945,8 @@ function listMcp(profileDirPath) {
|
|
|
899
945
|
}
|
|
900
946
|
function validateMcpInput(input) {
|
|
901
947
|
if (!SERVER_NAME_RE.test(input.serverName)) return "serverName must be 1-32 chars of A-Z a-z 0-9 _ -";
|
|
902
|
-
|
|
948
|
+
const id = input.id ?? "";
|
|
949
|
+
if (id.includes("/") || id.includes("..")) return "invalid id";
|
|
903
950
|
if (input.transport === "stdio") {
|
|
904
951
|
if (input.command === void 0 || input.command.trim() === "") return "stdio transport requires a command";
|
|
905
952
|
} else if (input.url === void 0 || !/^https?:\/\//.test(input.url)) {
|
|
@@ -908,10 +955,11 @@ function validateMcpInput(input) {
|
|
|
908
955
|
return null;
|
|
909
956
|
}
|
|
910
957
|
function upsertMcp(profileDirPath, input) {
|
|
958
|
+
const inputId = input.id ?? "";
|
|
911
959
|
const doc = loadPatch(profileDirPath);
|
|
912
960
|
const seq = rowSeq(doc);
|
|
913
|
-
const existing =
|
|
914
|
-
let id =
|
|
961
|
+
const existing = inputId !== "" ? mcpRows(doc).find((item) => item.get("id") === inputId) : void 0;
|
|
962
|
+
let id = inputId !== "" ? inputId : `mcp-${input.serverName}`;
|
|
915
963
|
if (existing === void 0) {
|
|
916
964
|
const taken = new Set(
|
|
917
965
|
(seq.items ?? []).map((item) => String(item.get("id") ?? "")).filter((id2) => id2 !== "")
|
|
@@ -1215,6 +1263,27 @@ function mountCapabilitiesRoutes(host, config) {
|
|
|
1215
1263
|
sendJson(response, 500, { error: error instanceof Error ? error.message : String(error) });
|
|
1216
1264
|
}
|
|
1217
1265
|
}
|
|
1266
|
+
}),
|
|
1267
|
+
host.webServer.register({
|
|
1268
|
+
kind: "exact",
|
|
1269
|
+
path: "/dsh-plugin-capabilities/restart",
|
|
1270
|
+
handler: (request, response) => {
|
|
1271
|
+
if (request.method !== "POST") {
|
|
1272
|
+
response.writeHead(405, { allow: "POST" });
|
|
1273
|
+
response.end();
|
|
1274
|
+
return;
|
|
1275
|
+
}
|
|
1276
|
+
if (!trustedRestartRequest(request)) {
|
|
1277
|
+
sendJson(response, 403, { error: "untrusted origin" });
|
|
1278
|
+
return;
|
|
1279
|
+
}
|
|
1280
|
+
if (restartOwnedByShell()) {
|
|
1281
|
+
sendJson(response, 409, { error: "restart is owned by the desktop shell" });
|
|
1282
|
+
return;
|
|
1283
|
+
}
|
|
1284
|
+
const { pid, replacementPid, logOut } = scheduleRestart(dshLaunch());
|
|
1285
|
+
sendJson(response, 200, { ok: true, pid, replacementPid, logOut });
|
|
1286
|
+
}
|
|
1218
1287
|
})
|
|
1219
1288
|
];
|
|
1220
1289
|
return () => {
|