@langify-org/dsh-code-server 0.1.1 → 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.ja.md +1 -0
- package/README.md +1 -0
- package/README.zh.md +1 -0
- package/dist/index.mjs +52 -2
- package/extension/dsh-code-bridge.vsix +0 -0
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -41,6 +41,7 @@ DSH の Web 版や Desktop 版では **Plugins** ページから追加できま
|
|
|
41
41
|
|
|
42
42
|
- プラグインは必要になったときに DSH 用の code-server を1つ起動します。待ち受けは本人だけが開ける Unix socket(`0700` のディレクトリ内の `0600`)で、code-server 自身の認証は無効にしています。ネットワークのポートは開きません。
|
|
43
43
|
- DSH は自分のオリジンの `/code-server` で code-server を配信します。すべてのリクエストと WebSocket は、先に DSH の認証とオリジンの検査を通ります。そのため code-server には、DSH に届く場所(Tailscale 経由など)からだけ届きます。
|
|
44
|
+
- DSH Desktop のページは `dsh-app://app` にあり、code-server はその宛先に WebSocket をつなごうとします。中継は code-server のページに小さなスクリプトを差し込み、WebSocket を Desktop が示す Host の宛先(`__DSH_TRANSPORT__.streamBaseUrl`)に付け替えます。Desktop はそこへの接続に DSH の認証情報を付けます。ブラウザーではこのスクリプトは何もしません。
|
|
44
45
|
- 起動のたびに、同梱の VS Code 拡張 **DSH Code Bridge**(`langify-org.dsh-code-bridge`)を code-server に入れ、古いものを置き換えます。
|
|
45
46
|
- Code ページごとに専用の接続(binding)を作ります。ページは binding をエディターの URL の断片(サーバーには送られない部分)に入れ、そのエディター画面の拡張がそれを読み取って、別の非公開 socket でプラグインに接続します。同じフォルダーを複数のページで開いていても、ファイルはその画面にだけ開きます。
|
|
46
47
|
|
package/README.md
CHANGED
|
@@ -41,6 +41,7 @@ With the defaults, the editor shares settings and extensions with your own code-
|
|
|
41
41
|
|
|
42
42
|
- The plugin starts one code-server for DSH on demand, listening on a Unix socket that only you can open (mode `0600` in a `0700` directory), with code-server's own authentication off. It never opens a network port.
|
|
43
43
|
- DSH serves code-server under `/code-server` on its own origin. Every request and WebSocket passes DSH's authentication and origin check first, so code-server is reachable wherever DSH is, for example over Tailscale, and nowhere else.
|
|
44
|
+
- In DSH Desktop the page lives at `dsh-app://app`, and code-server would dial its WebSockets at that host. The relay adds a small script to code-server's page that sends them to the address Desktop gives its Host (`__DSH_TRANSPORT__.streamBaseUrl`), where Desktop adds DSH's credentials. In a browser the script changes nothing.
|
|
44
45
|
- Before each start, the plugin installs its bundled VS Code extension, **DSH Code Bridge** (`langify-org.dsh-code-bridge`), into code-server, replacing any older copy.
|
|
45
46
|
- Each Code page gets its own connection ("binding"). The page puts the binding in the editor's URL fragment, which never reaches a server; the extension in that editor window reads it back and connects to the plugin over another private socket. Files open in that window only, even when several pages show the same folder.
|
|
46
47
|
|
package/README.zh.md
CHANGED
|
@@ -41,6 +41,7 @@ dsh plugin --profile <profile> add @langify-org/dsh-code-server
|
|
|
41
41
|
|
|
42
42
|
- 插件在需要时为 DSH 启动一个 code-server,监听只有你能打开的 Unix 套接字(位于 `0700` 目录中,权限为 `0600`),并关闭 code-server 自身的认证。它不会打开任何网络端口。
|
|
43
43
|
- DSH 在自己的源下通过 `/code-server` 提供 code-server。每个请求和 WebSocket 都会先经过 DSH 的认证和来源检查,因此只有能访问 DSH 的地方(例如通过 Tailscale)才能访问 code-server。
|
|
44
|
+
- 在 DSH Desktop 中,页面位于 `dsh-app://app`,code-server 会向该地址建立 WebSocket。中继会向 code-server 的页面注入一段小脚本,把 WebSocket 改为发往 Desktop 提供的 Host 地址(`__DSH_TRANSPORT__.streamBaseUrl`),Desktop 会为发往该地址的连接附加 DSH 的凭据。在浏览器中,该脚本不做任何改动。
|
|
44
45
|
- 每次启动前,插件都会把内置的 VS Code 扩展 **DSH Code Bridge**(`langify-org.dsh-code-bridge`)安装到 code-server 中,替换旧版本。
|
|
45
46
|
- 每个 Code 页面都有自己的连接(binding)。页面把 binding 放在编辑器 URL 的片段中(这一部分不会发送到服务器),该编辑器窗口中的扩展读取它,并通过另一个私有套接字连接到插件。即使多个页面打开了同一个文件夹,文件也只会在对应的窗口中打开。
|
|
46
47
|
|
package/dist/index.mjs
CHANGED
|
@@ -11,6 +11,7 @@ import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
|
11
11
|
import { clientRequestSchema } from "@deepseek-ai/dsh-client-connection";
|
|
12
12
|
import { SessionId } from "@deepseek-ai/dsh-session/types";
|
|
13
13
|
import { parseFileAddress } from "@deepseek-ai/dsh-util-workspace-path";
|
|
14
|
+
import { buffer } from "node:stream/consumers";
|
|
14
15
|
// src/rpc.ts
|
|
15
16
|
/** DSH's shared RPC channel, and the prefix of the endpoints this plugin owns on it. */
|
|
16
17
|
const RPC_CHANNEL = "/api";
|
|
@@ -584,9 +585,36 @@ function healthy(socketPath) {
|
|
|
584
585
|
}).on("error", () => resolve(false));
|
|
585
586
|
});
|
|
586
587
|
}
|
|
588
|
+
// src/host/desktop-websocket.ts
|
|
589
|
+
/** Self-contained: it is serialized into {@link DESKTOP_WEBSOCKET_SCRIPT}. */
|
|
590
|
+
function retargetWebSockets(page) {
|
|
591
|
+
let base;
|
|
592
|
+
try {
|
|
593
|
+
base = page.parent.__DSH_TRANSPORT__?.streamBaseUrl;
|
|
594
|
+
} catch {
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
if (typeof base !== "string") return;
|
|
598
|
+
const host = new URL(base);
|
|
599
|
+
const scheme = host.protocol === "https:" ? "wss:" : "ws:";
|
|
600
|
+
const Native = page.WebSocket;
|
|
601
|
+
page.WebSocket = class extends Native {
|
|
602
|
+
constructor(url, protocols) {
|
|
603
|
+
const target = new URL(url, page.location.href);
|
|
604
|
+
if (target.hostname === page.location.hostname) {
|
|
605
|
+
target.protocol = scheme;
|
|
606
|
+
target.host = host.host;
|
|
607
|
+
}
|
|
608
|
+
super(target, protocols);
|
|
609
|
+
}
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
const DESKTOP_WEBSOCKET_SCRIPT = `(${retargetWebSockets.toString()})(window)\n`;
|
|
587
613
|
// src/host/relay.ts
|
|
588
614
|
/** Path prefix code-server is served under, relative to the DSH page. */
|
|
589
615
|
const RELAY_PREFIX = "/code-server";
|
|
616
|
+
/** Served by the relay itself, below the prefix; code-server has no such route. */
|
|
617
|
+
const SCRIPT_PATH = "/_dsh/desktop-websocket.js";
|
|
590
618
|
/** DSH's credentials are not code-server's business. */
|
|
591
619
|
const PRIVATE_HEADERS = /* @__PURE__ */ new Set(["cookie", "authorization"]);
|
|
592
620
|
function upstreamHeaders(headers) {
|
|
@@ -612,12 +640,28 @@ function relayRequests(admission, start) {
|
|
|
612
640
|
res.end(error instanceof Error ? error.message : "code-server is unavailable");
|
|
613
641
|
return;
|
|
614
642
|
}
|
|
643
|
+
const path = upstreamPath(req.url);
|
|
644
|
+
const pathname = new URL(path, "http://code-server").pathname;
|
|
645
|
+
if (req.method === "GET" && pathname === SCRIPT_PATH) {
|
|
646
|
+
res.writeHead(200, { "content-type": "text/javascript; charset=utf-8" });
|
|
647
|
+
res.end(DESKTOP_WEBSOCKET_SCRIPT);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
const page = req.method === "GET" && pathname === "/";
|
|
651
|
+
const headers = upstreamHeaders(req.headers);
|
|
615
652
|
const upstream = request({
|
|
616
653
|
socketPath: server.socketPath,
|
|
617
654
|
method: req.method,
|
|
618
|
-
path
|
|
619
|
-
headers:
|
|
655
|
+
path,
|
|
656
|
+
headers: page ? {
|
|
657
|
+
...headers,
|
|
658
|
+
"accept-encoding": "identity"
|
|
659
|
+
} : headers
|
|
620
660
|
}, (response) => {
|
|
661
|
+
if (page && response.headers["content-type"]?.startsWith("text/html") === true) {
|
|
662
|
+
sendPage(response, res).catch(() => res.destroy());
|
|
663
|
+
return;
|
|
664
|
+
}
|
|
621
665
|
res.writeHead(response.statusCode ?? 502, response.statusMessage, response.headers);
|
|
622
666
|
response.pipe(res);
|
|
623
667
|
});
|
|
@@ -629,6 +673,12 @@ function relayRequests(admission, start) {
|
|
|
629
673
|
req.pipe(upstream);
|
|
630
674
|
};
|
|
631
675
|
}
|
|
676
|
+
/** Send code-server's page with the script ahead of code-server's own. */
|
|
677
|
+
async function sendPage(response, res) {
|
|
678
|
+
const html = (await buffer(response)).toString("utf8").replace(/<head[^>]*>/i, (head) => `${head}<script src=".${SCRIPT_PATH}"><\/script>`);
|
|
679
|
+
const { "content-length": _length, ...headers } = response.headers;
|
|
680
|
+
res.writeHead(response.statusCode ?? 502, response.statusMessage, headers).end(html);
|
|
681
|
+
}
|
|
632
682
|
/** Handler for code-server's WebSocket upgrades, which arrive on one exact path. */
|
|
633
683
|
function relayUpgrades(admission, socketPath) {
|
|
634
684
|
return (req, socket, head) => {
|
|
Binary file
|
package/package.json
CHANGED