coderaft 0.0.15 → 0.0.18
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 +10 -8
- package/android-preload.cjs +54 -0
- package/code.mjs +1 -1
- package/code.tar.zst +0 -0
- package/dist/THIRD-PARTY-LICENSES.md +58 -0
- package/dist/_chunks/_android.d.mts +1 -0
- package/dist/_chunks/libs/httpxy.mjs +561 -0
- package/dist/_chunks/rolldown-runtime.mjs +3 -0
- package/dist/_chunks/server.mjs +198 -48
- package/dist/cli.mjs +16 -12
- package/dist/index.d.mts +14 -3
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -45,9 +45,11 @@ npx coderaft -o .
|
|
|
45
45
|
### Docker
|
|
46
46
|
|
|
47
47
|
```sh
|
|
48
|
-
docker run -it --rm -p 6063:6063
|
|
48
|
+
docker run -it --rm -p 6063:6063 -v coderaft:/data ghcr.io/pithings/coderaft
|
|
49
49
|
```
|
|
50
50
|
|
|
51
|
+
Based on Alpine with Node.js LTS, bash, corepack (npm/pnpm/yarn) (~65 MB download, ~89 MB final). Data persists in `/data` volume (`/data/workspace` for project files, `/data/home` symlinked to `/root` for configs).
|
|
52
|
+
|
|
51
53
|
## Programmatic
|
|
52
54
|
|
|
53
55
|
```ts
|
|
@@ -137,13 +139,13 @@ interface CodeServerHandle {
|
|
|
137
139
|
|
|
138
140
|
### Server
|
|
139
141
|
|
|
140
|
-
| Option | Description
|
|
141
|
-
| ----------------------------- |
|
|
142
|
-
| `-p, --port <port>` | Port to listen on (default: `$PORT` or `6063`)
|
|
143
|
-
| `-H, --host <host>` | Host/interface to bind
|
|
144
|
-
| `--
|
|
145
|
-
| `--socket-path <path>` | Path to a socket file to listen on
|
|
146
|
-
| `--print-startup-performance` | Print startup timing to stdout
|
|
142
|
+
| Option | Description |
|
|
143
|
+
| ----------------------------- | --------------------------------------------------- |
|
|
144
|
+
| `-p, --port <port>` | Port to listen on (default: `$PORT` or `6063`) |
|
|
145
|
+
| `-H, --host <host>` | Host/interface to bind |
|
|
146
|
+
| `--base-url <path>` | Base URL the server is mounted under (default: `/`) |
|
|
147
|
+
| `--socket-path <path>` | Path to a socket file to listen on |
|
|
148
|
+
| `--print-startup-performance` | Print startup timing to stdout |
|
|
147
149
|
|
|
148
150
|
### Auth
|
|
149
151
|
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// Android/Termux preload — injected via NODE_OPTIONS --require into all child
|
|
2
|
+
// processes. VS Code strips LD_PRELOAD from forked processes, which breaks two
|
|
3
|
+
// things that termux-exec normally handles:
|
|
4
|
+
// 1. /proc/self/exe → process.execPath resolves to the Android linker
|
|
5
|
+
// 2. execve() of Termux binaries fails with EACCES (noexec mount / SELinux)
|
|
6
|
+
//
|
|
7
|
+
// VS Code's bundled code also explicitly `delete env.LD_PRELOAD` from the env
|
|
8
|
+
// object passed to child_process.fork/spawn, so simply restoring process.env
|
|
9
|
+
// is not enough — we must monkey-patch child_process.spawn to re-inject it.
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
Object.defineProperty(process, "platform", { value: "linux" });
|
|
13
|
+
|
|
14
|
+
// Fix process.execPath when termux-exec is unavailable
|
|
15
|
+
const execPath = process.execPath;
|
|
16
|
+
if (execPath.includes("linker64") || execPath.startsWith("/apex/")) {
|
|
17
|
+
const resolved =
|
|
18
|
+
process.env.TERMUX_EXEC__PROC_SELF_EXE || "/data/data/com.termux/files/usr/bin/node";
|
|
19
|
+
Object.defineProperty(process, "execPath", {
|
|
20
|
+
value: resolved,
|
|
21
|
+
writable: true,
|
|
22
|
+
configurable: true,
|
|
23
|
+
enumerable: true,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Ensure LD_PRELOAD with termux-exec is always present in child process envs.
|
|
28
|
+
// VS Code strips it via `delete env.LD_PRELOAD` before spawning children.
|
|
29
|
+
// We patch child_process.spawn (which fork() calls internally) to re-inject it.
|
|
30
|
+
const TERMUX_EXEC_LIB = "/data/data/com.termux/files/usr/lib/libtermux-exec.so";
|
|
31
|
+
let _termuxExecExists;
|
|
32
|
+
function termuxExecExists() {
|
|
33
|
+
if (_termuxExecExists === undefined) {
|
|
34
|
+
try {
|
|
35
|
+
require("fs").accessSync(TERMUX_EXEC_LIB);
|
|
36
|
+
_termuxExecExists = true;
|
|
37
|
+
} catch {
|
|
38
|
+
_termuxExecExists = false;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return _termuxExecExists;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (termuxExecExists()) {
|
|
45
|
+
process.env.LD_PRELOAD = TERMUX_EXEC_LIB;
|
|
46
|
+
const cp = require("child_process");
|
|
47
|
+
const _spawn = cp.spawn;
|
|
48
|
+
cp.spawn = function spawn(cmd, args, opts) {
|
|
49
|
+
if (opts && typeof opts === "object" && opts.env && !opts.env.LD_PRELOAD) {
|
|
50
|
+
opts.env.LD_PRELOAD = TERMUX_EXEC_LIB;
|
|
51
|
+
}
|
|
52
|
+
return _spawn.apply(this, arguments);
|
|
53
|
+
};
|
|
54
|
+
}
|
package/code.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
5
|
|
|
6
6
|
// Auto-updated by scripts/pack.ts
|
|
7
|
-
const codeArchiveHash = "
|
|
7
|
+
const codeArchiveHash = "c2f3188affec085e";
|
|
8
8
|
|
|
9
9
|
const archivePath = fileURLToPath(new URL("./code.tar.zst", import.meta.url));
|
|
10
10
|
|
package/code.tar.zst
CHANGED
|
Binary file
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# Licenses of Bundled Dependencies
|
|
2
|
+
|
|
3
|
+
The published artifact additionally contains code with the following licenses:
|
|
4
|
+
MIT
|
|
5
|
+
|
|
6
|
+
# Bundled Dependencies
|
|
7
|
+
|
|
8
|
+
## httpxy
|
|
9
|
+
|
|
10
|
+
License: MIT
|
|
11
|
+
Repository: https://github.com/unjs/httpxy
|
|
12
|
+
|
|
13
|
+
> MIT License
|
|
14
|
+
>
|
|
15
|
+
> Copyright (c) Pooya Parsa <pooya@pi0.io>
|
|
16
|
+
>
|
|
17
|
+
> Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
+
> of this software and associated documentation files (the "Software"), to deal
|
|
19
|
+
> in the Software without restriction, including without limitation the rights
|
|
20
|
+
> to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
+
> copies of the Software, and to permit persons to whom the Software is
|
|
22
|
+
> furnished to do so, subject to the following conditions:
|
|
23
|
+
>
|
|
24
|
+
> The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
> copies or substantial portions of the Software.
|
|
26
|
+
>
|
|
27
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
> IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
> FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
> AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
> LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
> OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
+
> SOFTWARE.
|
|
34
|
+
>
|
|
35
|
+
> ----
|
|
36
|
+
>
|
|
37
|
+
> Based on http-party/node-http-proxy (9b96cd7)
|
|
38
|
+
>
|
|
39
|
+
> Copyright (c) 2010-2016 Charlie Robbins, Jarrett Cruger & the Contributors.
|
|
40
|
+
>
|
|
41
|
+
> Permission is hereby granted, free of charge, to any person obtaining
|
|
42
|
+
> a copy of this software and associated documentation files (the
|
|
43
|
+
> "Software"), to deal in the Software without restriction, including
|
|
44
|
+
> without limitation the rights to use, copy, modify, merge, publish,
|
|
45
|
+
> distribute, sublicense, and/or sell copies of the Software, and to
|
|
46
|
+
> permit persons to whom the Software is furnished to do so, subject to
|
|
47
|
+
> the following conditions:
|
|
48
|
+
>
|
|
49
|
+
> The above copyright notice and this permission notice shall be
|
|
50
|
+
> included in all copies or substantial portions of the Software.
|
|
51
|
+
>
|
|
52
|
+
> THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
53
|
+
> EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
54
|
+
> MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
55
|
+
> NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
56
|
+
> LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
57
|
+
> OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
58
|
+
> WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,561 @@
|
|
|
1
|
+
import http from "node:http";
|
|
2
|
+
import https from "node:https";
|
|
3
|
+
import http2 from "node:http2";
|
|
4
|
+
import { EventEmitter } from "node:events";
|
|
5
|
+
const upgradeHeader = /(^|,)\s*upgrade\s*($|,)/i;
|
|
6
|
+
const defaultAgents = {
|
|
7
|
+
http: new http.Agent({
|
|
8
|
+
keepAlive: true,
|
|
9
|
+
maxSockets: 256,
|
|
10
|
+
maxFreeSockets: 64
|
|
11
|
+
}),
|
|
12
|
+
https: new https.Agent({
|
|
13
|
+
keepAlive: true,
|
|
14
|
+
maxSockets: 256,
|
|
15
|
+
maxFreeSockets: 64
|
|
16
|
+
})
|
|
17
|
+
};
|
|
18
|
+
const isSSL = /^https|wss/;
|
|
19
|
+
const HTTP2_HEADER_BLACKLIST = [
|
|
20
|
+
":method",
|
|
21
|
+
":path",
|
|
22
|
+
":scheme",
|
|
23
|
+
":authority"
|
|
24
|
+
];
|
|
25
|
+
function setupOutgoing(outgoing, options, req, forward) {
|
|
26
|
+
outgoing.port = options[forward || "target"].port || (isSSL.test(options[forward || "target"].protocol ?? "http") ? 443 : 80);
|
|
27
|
+
for (const e of [
|
|
28
|
+
"host",
|
|
29
|
+
"hostname",
|
|
30
|
+
"socketPath",
|
|
31
|
+
"pfx",
|
|
32
|
+
"key",
|
|
33
|
+
"passphrase",
|
|
34
|
+
"cert",
|
|
35
|
+
"ca",
|
|
36
|
+
"ciphers",
|
|
37
|
+
"secureProtocol"
|
|
38
|
+
]) {
|
|
39
|
+
const value = options[forward || "target"][e];
|
|
40
|
+
if (value !== void 0) outgoing[e] = value;
|
|
41
|
+
}
|
|
42
|
+
outgoing.method = options.method || req.method;
|
|
43
|
+
outgoing.headers = { ...req.headers };
|
|
44
|
+
if (req.headers?.[":authority"]) outgoing.headers.host = req.headers[":authority"];
|
|
45
|
+
if (options.headers) for (const key of Object.keys(options.headers)) outgoing.headers[key] = options.headers[key];
|
|
46
|
+
if (req.httpVersionMajor > 1) for (const header of HTTP2_HEADER_BLACKLIST) delete outgoing.headers[header];
|
|
47
|
+
if (options.auth) outgoing.auth = options.auth;
|
|
48
|
+
if (options.ca) outgoing.ca = options.ca;
|
|
49
|
+
if (isSSL.test(options[forward || "target"].protocol ?? "http")) outgoing.rejectUnauthorized = options.secure === void 0 ? true : options.secure;
|
|
50
|
+
if (options.agent !== void 0) outgoing.agent = options.agent || false;
|
|
51
|
+
else if (req.httpVersionMajor > 1) outgoing.agent = false;
|
|
52
|
+
else {
|
|
53
|
+
const targetProto = options[forward || "target"].protocol ?? "http";
|
|
54
|
+
outgoing.agent = isSSL.test(targetProto) ? defaultAgents.https : defaultAgents.http;
|
|
55
|
+
}
|
|
56
|
+
outgoing.localAddress = options.localAddress;
|
|
57
|
+
if (!outgoing.agent) {
|
|
58
|
+
outgoing.headers = outgoing.headers || {};
|
|
59
|
+
if (typeof outgoing.headers.connection !== "string" || !upgradeHeader.test(outgoing.headers.connection)) outgoing.headers.connection = "close";
|
|
60
|
+
}
|
|
61
|
+
const target = options[forward || "target"];
|
|
62
|
+
const targetPath = target && options.prependPath !== false ? target.pathname || "" : "";
|
|
63
|
+
const targetSearch = target instanceof URL && options.prependPath !== false ? target.search || "" : "";
|
|
64
|
+
const reqUrl = req.url || "";
|
|
65
|
+
const qIdx = reqUrl.indexOf("?");
|
|
66
|
+
const reqPath = qIdx === -1 ? reqUrl : reqUrl.slice(0, qIdx);
|
|
67
|
+
const reqSearch = qIdx === -1 ? "" : reqUrl.slice(qIdx);
|
|
68
|
+
const normalizedPath = reqPath ? reqPath[0] === "/" ? reqPath : "/" + reqPath : "/";
|
|
69
|
+
let outgoingPath = options.toProxy ? "/" + reqUrl : normalizedPath + reqSearch;
|
|
70
|
+
outgoingPath = options.ignorePath ? "" : outgoingPath;
|
|
71
|
+
let fullPath = joinURL(targetPath, outgoingPath);
|
|
72
|
+
if (targetSearch) fullPath = fullPath.includes("?") ? fullPath.replace("?", targetSearch + "&") : fullPath + targetSearch;
|
|
73
|
+
outgoing.path = fullPath;
|
|
74
|
+
if (options.changeOrigin) outgoing.headers.host = requiresPort(outgoing.port, options[forward || "target"].protocol) && !hasPort(outgoing.host) ? outgoing.host + ":" + outgoing.port : outgoing.host ?? void 0;
|
|
75
|
+
return outgoing;
|
|
76
|
+
}
|
|
77
|
+
function joinURL(base, path) {
|
|
78
|
+
if (!base || base === "/") return path || "/";
|
|
79
|
+
if (!path || path === "/") return base || "/";
|
|
80
|
+
const baseHasTrailing = base[base.length - 1] === "/";
|
|
81
|
+
const pathHasLeading = path[0] === "/";
|
|
82
|
+
if (baseHasTrailing && pathHasLeading) return base + path.slice(1);
|
|
83
|
+
if (!baseHasTrailing && !pathHasLeading) return base + "/" + path;
|
|
84
|
+
return base + path;
|
|
85
|
+
}
|
|
86
|
+
function setupSocket(socket) {
|
|
87
|
+
socket.setTimeout(0);
|
|
88
|
+
socket.setNoDelay(true);
|
|
89
|
+
socket.setKeepAlive(true, 0);
|
|
90
|
+
return socket;
|
|
91
|
+
}
|
|
92
|
+
function getPort(req) {
|
|
93
|
+
const hostHeader = req.headers[":authority"] || req.headers.host;
|
|
94
|
+
const res = hostHeader ? hostHeader.match(/:(\d+)/) : "";
|
|
95
|
+
if (res) return res[1];
|
|
96
|
+
return hasEncryptedConnection(req) ? "443" : "80";
|
|
97
|
+
}
|
|
98
|
+
function hasEncryptedConnection(req) {
|
|
99
|
+
const socket = req.socket;
|
|
100
|
+
return !!socket && "encrypted" in socket && socket.encrypted;
|
|
101
|
+
}
|
|
102
|
+
function rewriteCookieProperty(header, config, property) {
|
|
103
|
+
if (Array.isArray(header)) return header.map(function(headerElement) {
|
|
104
|
+
return rewriteCookieProperty(headerElement, config, property);
|
|
105
|
+
});
|
|
106
|
+
return header.replace(new RegExp(String.raw`(;\s*` + property + "=)([^;]+)", "i"), function(match, prefix, previousValue) {
|
|
107
|
+
let newValue;
|
|
108
|
+
if (previousValue in config) newValue = config[previousValue];
|
|
109
|
+
else if ("*" in config) newValue = config["*"];
|
|
110
|
+
else return match;
|
|
111
|
+
return newValue ? prefix + newValue : "";
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function hasPort(host) {
|
|
115
|
+
return host ? !!~host.indexOf(":") : false;
|
|
116
|
+
}
|
|
117
|
+
function requiresPort(_port, _protocol) {
|
|
118
|
+
const protocol = _protocol?.split(":")[0];
|
|
119
|
+
const port = +_port;
|
|
120
|
+
if (!port) return false;
|
|
121
|
+
switch (protocol) {
|
|
122
|
+
case "http":
|
|
123
|
+
case "ws": return port !== 80;
|
|
124
|
+
case "https":
|
|
125
|
+
case "wss": return port !== 443;
|
|
126
|
+
case "ftp": return port !== 21;
|
|
127
|
+
case "gopher": return port !== 70;
|
|
128
|
+
case "file": return false;
|
|
129
|
+
}
|
|
130
|
+
return port !== 0;
|
|
131
|
+
}
|
|
132
|
+
function defineProxyMiddleware(m) {
|
|
133
|
+
return m;
|
|
134
|
+
}
|
|
135
|
+
function defineProxyOutgoingMiddleware(m) {
|
|
136
|
+
return m;
|
|
137
|
+
}
|
|
138
|
+
const redirectRegex = /^201|30([12378])$/;
|
|
139
|
+
const webOutgoingMiddleware = [
|
|
140
|
+
defineProxyOutgoingMiddleware((req, res, proxyRes) => {
|
|
141
|
+
if (req.httpVersion === "1.0" || req.httpVersionMajor >= 2 || proxyRes.statusCode === 204 || proxyRes.statusCode === 304) delete proxyRes.headers["transfer-encoding"];
|
|
142
|
+
}),
|
|
143
|
+
defineProxyOutgoingMiddleware((req, res, proxyRes) => {
|
|
144
|
+
if (req.httpVersion === "1.0") proxyRes.headers.connection = req.headers.connection || "close";
|
|
145
|
+
else if (req.httpVersionMajor < 2 && !proxyRes.headers.connection) proxyRes.headers.connection = req.headers.connection || "keep-alive";
|
|
146
|
+
else if (req.httpVersionMajor >= 2) delete proxyRes.headers.connection;
|
|
147
|
+
}),
|
|
148
|
+
defineProxyOutgoingMiddleware((req, res, proxyRes, options) => {
|
|
149
|
+
if ((options.hostRewrite || options.autoRewrite || options.protocolRewrite) && proxyRes.headers.location && redirectRegex.test(String(proxyRes.statusCode))) {
|
|
150
|
+
const target = _toURL(options.target);
|
|
151
|
+
const u = new URL(proxyRes.headers.location, target);
|
|
152
|
+
if (target.host !== u.host) return;
|
|
153
|
+
if (options.hostRewrite) u.host = options.hostRewrite;
|
|
154
|
+
else if (options.autoRewrite) {
|
|
155
|
+
if (req.headers[":authority"]) u.host = req.headers[":authority"];
|
|
156
|
+
else if (req.headers.host) u.host = req.headers.host;
|
|
157
|
+
}
|
|
158
|
+
if (options.protocolRewrite) u.protocol = options.protocolRewrite;
|
|
159
|
+
proxyRes.headers.location = u.toString();
|
|
160
|
+
}
|
|
161
|
+
}),
|
|
162
|
+
defineProxyOutgoingMiddleware((req, res, proxyRes, options) => {
|
|
163
|
+
const rewriteCookieDomainConfig = typeof options.cookieDomainRewrite === "string" ? { "*": options.cookieDomainRewrite } : options.cookieDomainRewrite;
|
|
164
|
+
const rewriteCookiePathConfig = typeof options.cookiePathRewrite === "string" ? { "*": options.cookiePathRewrite } : options.cookiePathRewrite;
|
|
165
|
+
const preserveHeaderKeyCase = options.preserveHeaderKeyCase;
|
|
166
|
+
let rawHeaderKeyMap;
|
|
167
|
+
const setHeader = function(key, header) {
|
|
168
|
+
if (header === void 0 || !String(key).trim()) return;
|
|
169
|
+
if (rewriteCookieDomainConfig && key.toLowerCase() === "set-cookie") header = rewriteCookieProperty(header, rewriteCookieDomainConfig, "domain");
|
|
170
|
+
if (rewriteCookiePathConfig && key.toLowerCase() === "set-cookie") header = rewriteCookieProperty(header, rewriteCookiePathConfig, "path");
|
|
171
|
+
try {
|
|
172
|
+
res.setHeader(String(key).trim(), header);
|
|
173
|
+
} catch {}
|
|
174
|
+
};
|
|
175
|
+
if (preserveHeaderKeyCase && proxyRes.rawHeaders !== void 0) {
|
|
176
|
+
rawHeaderKeyMap = {};
|
|
177
|
+
for (let i = 0; i < proxyRes.rawHeaders.length; i += 2) {
|
|
178
|
+
const key = proxyRes.rawHeaders[i];
|
|
179
|
+
rawHeaderKeyMap[key.toLowerCase()] = key;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
for (let key of Object.keys(proxyRes.headers)) {
|
|
183
|
+
const header = proxyRes.headers[key];
|
|
184
|
+
if (preserveHeaderKeyCase && rawHeaderKeyMap) key = rawHeaderKeyMap[key] || key;
|
|
185
|
+
setHeader(key, header);
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
defineProxyOutgoingMiddleware((req, res, proxyRes) => {
|
|
189
|
+
res.statusCode = proxyRes.statusCode;
|
|
190
|
+
if (proxyRes.statusMessage && req.httpVersionMajor < 2) res.statusMessage = proxyRes.statusMessage;
|
|
191
|
+
})
|
|
192
|
+
];
|
|
193
|
+
function _toURL(target) {
|
|
194
|
+
if (target instanceof URL) return target;
|
|
195
|
+
if (typeof target === "string") return new URL(target);
|
|
196
|
+
const protocol = target.protocol || "http:";
|
|
197
|
+
const host = target.host || target.hostname || "localhost";
|
|
198
|
+
const port = target.port;
|
|
199
|
+
return new URL(`${protocol}//${host}${port ? ":" + port : ""}`);
|
|
200
|
+
}
|
|
201
|
+
const nativeAgents = {
|
|
202
|
+
http,
|
|
203
|
+
https
|
|
204
|
+
};
|
|
205
|
+
const redirectStatuses = new Set([
|
|
206
|
+
301,
|
|
207
|
+
302,
|
|
208
|
+
303,
|
|
209
|
+
307,
|
|
210
|
+
308
|
|
211
|
+
]);
|
|
212
|
+
const webIncomingMiddleware = [
|
|
213
|
+
defineProxyMiddleware((req) => {
|
|
214
|
+
if ((req.method === "DELETE" || req.method === "OPTIONS") && !req.headers["content-length"]) {
|
|
215
|
+
req.headers["content-length"] = "0";
|
|
216
|
+
delete req.headers["transfer-encoding"];
|
|
217
|
+
}
|
|
218
|
+
}),
|
|
219
|
+
defineProxyMiddleware((req, res, options) => {
|
|
220
|
+
if (options.timeout) req.socket.setTimeout(options.timeout, () => {
|
|
221
|
+
req.socket.destroy();
|
|
222
|
+
});
|
|
223
|
+
}),
|
|
224
|
+
defineProxyMiddleware((req, res, options) => {
|
|
225
|
+
if (!options.xfwd) return;
|
|
226
|
+
const encrypted = req.isSpdy || hasEncryptedConnection(req);
|
|
227
|
+
const values = {
|
|
228
|
+
for: req.connection.remoteAddress || req.socket.remoteAddress,
|
|
229
|
+
port: getPort(req),
|
|
230
|
+
proto: encrypted ? "https" : "http"
|
|
231
|
+
};
|
|
232
|
+
for (const header of [
|
|
233
|
+
"for",
|
|
234
|
+
"port",
|
|
235
|
+
"proto"
|
|
236
|
+
]) {
|
|
237
|
+
const key = "x-forwarded-" + header;
|
|
238
|
+
if (!req.headers[key]) req.headers[key] = values[header];
|
|
239
|
+
}
|
|
240
|
+
req.headers["x-forwarded-host"] = req.headers["x-forwarded-host"] || req.headers[":authority"] || req.headers.host || "";
|
|
241
|
+
}),
|
|
242
|
+
defineProxyMiddleware((req, res, options, server, head, callback) => {
|
|
243
|
+
server.emit("start", req, res, options.target || options.forward);
|
|
244
|
+
const http = nativeAgents.http;
|
|
245
|
+
const https = nativeAgents.https;
|
|
246
|
+
const maxRedirects = typeof options.followRedirects === "number" ? options.followRedirects : options.followRedirects ? 5 : 0;
|
|
247
|
+
if (options.forward) {
|
|
248
|
+
const forwardReq = (isSSL.test(options.forward.protocol || "http") ? https : http).request(setupOutgoing(options.ssl || {}, options, req, "forward"));
|
|
249
|
+
const forwardError = createErrorHandler(forwardReq, options.forward);
|
|
250
|
+
req.on("error", forwardError);
|
|
251
|
+
forwardReq.on("error", forwardError);
|
|
252
|
+
(options.buffer || req).pipe(forwardReq);
|
|
253
|
+
if (!options.target) {
|
|
254
|
+
res.end();
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const proxyReq = (isSSL.test(options.target.protocol || "http") ? https : http).request(setupOutgoing(options.ssl || {}, options, req));
|
|
259
|
+
proxyReq.on("socket", (_socket) => {
|
|
260
|
+
if (server && !proxyReq.getHeader("expect")) server.emit("proxyReq", proxyReq, req, res, options);
|
|
261
|
+
});
|
|
262
|
+
if (options.proxyTimeout) proxyReq.setTimeout(options.proxyTimeout, function() {
|
|
263
|
+
proxyReq.destroy();
|
|
264
|
+
});
|
|
265
|
+
res.on("close", function() {
|
|
266
|
+
if (!res.writableFinished) proxyReq.destroy();
|
|
267
|
+
});
|
|
268
|
+
const proxyError = createErrorHandler(proxyReq, options.target);
|
|
269
|
+
req.on("error", proxyError);
|
|
270
|
+
proxyReq.on("error", proxyError);
|
|
271
|
+
function createErrorHandler(proxyReq, url) {
|
|
272
|
+
return function proxyError(err) {
|
|
273
|
+
if (!req.socket?.writable && err.code === "ECONNRESET") {
|
|
274
|
+
server.emit("econnreset", err, req, res, url);
|
|
275
|
+
return proxyReq.destroy();
|
|
276
|
+
}
|
|
277
|
+
if (callback) callback(err, req, res, url);
|
|
278
|
+
else server.emit("error", err, req, res, url);
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
let bodyBuffer;
|
|
282
|
+
if (maxRedirects > 0) {
|
|
283
|
+
const chunks = [];
|
|
284
|
+
const source = options.buffer || req;
|
|
285
|
+
source.on("data", (chunk) => {
|
|
286
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
287
|
+
proxyReq.write(chunk);
|
|
288
|
+
});
|
|
289
|
+
source.on("end", () => {
|
|
290
|
+
bodyBuffer = Buffer.concat(chunks);
|
|
291
|
+
proxyReq.end();
|
|
292
|
+
});
|
|
293
|
+
source.on("error", (err) => {
|
|
294
|
+
proxyReq.destroy(err);
|
|
295
|
+
});
|
|
296
|
+
} else proxyReq.on("socket", (socket) => {
|
|
297
|
+
if (socket.pending) socket.on("connect", () => (options.buffer || req).pipe(proxyReq));
|
|
298
|
+
else (options.buffer || req).pipe(proxyReq);
|
|
299
|
+
});
|
|
300
|
+
function handleResponse(proxyRes, redirectCount, currentUrl) {
|
|
301
|
+
const statusCode = proxyRes.statusCode;
|
|
302
|
+
if (maxRedirects > 0 && redirectStatuses.has(statusCode) && redirectCount < maxRedirects && proxyRes.headers.location) {
|
|
303
|
+
proxyRes.resume();
|
|
304
|
+
const location = new URL(proxyRes.headers.location, currentUrl);
|
|
305
|
+
const preserveMethod = statusCode === 307 || statusCode === 308;
|
|
306
|
+
const redirectMethod = preserveMethod ? req.method || "GET" : "GET";
|
|
307
|
+
const isHTTPS = isSSL.test(location.protocol);
|
|
308
|
+
const agent = isHTTPS ? https : http;
|
|
309
|
+
const redirectHeaders = { ...req.headers };
|
|
310
|
+
if (options.headers) Object.assign(redirectHeaders, options.headers);
|
|
311
|
+
redirectHeaders.host = location.host;
|
|
312
|
+
if (location.host !== currentUrl.host) {
|
|
313
|
+
delete redirectHeaders.authorization;
|
|
314
|
+
delete redirectHeaders.cookie;
|
|
315
|
+
}
|
|
316
|
+
if (!preserveMethod) {
|
|
317
|
+
delete redirectHeaders["content-length"];
|
|
318
|
+
delete redirectHeaders["content-type"];
|
|
319
|
+
delete redirectHeaders["transfer-encoding"];
|
|
320
|
+
}
|
|
321
|
+
const redirectOpts = {
|
|
322
|
+
hostname: location.hostname,
|
|
323
|
+
port: location.port || (isHTTPS ? 443 : 80),
|
|
324
|
+
path: location.pathname + location.search,
|
|
325
|
+
method: redirectMethod,
|
|
326
|
+
headers: redirectHeaders,
|
|
327
|
+
agent: options.agent || false
|
|
328
|
+
};
|
|
329
|
+
if (isHTTPS) redirectOpts.rejectUnauthorized = options.secure === void 0 ? true : options.secure;
|
|
330
|
+
const redirectReq = agent.request(redirectOpts);
|
|
331
|
+
if (server && !redirectReq.getHeader("expect")) server.emit("proxyReq", redirectReq, req, res, options);
|
|
332
|
+
if (options.proxyTimeout) redirectReq.setTimeout(options.proxyTimeout, () => {
|
|
333
|
+
redirectReq.destroy();
|
|
334
|
+
});
|
|
335
|
+
const redirectError = createErrorHandler(redirectReq, location);
|
|
336
|
+
redirectReq.on("error", redirectError);
|
|
337
|
+
redirectReq.on("response", (nextRes) => {
|
|
338
|
+
handleResponse(nextRes, redirectCount + 1, location);
|
|
339
|
+
});
|
|
340
|
+
if (preserveMethod && bodyBuffer && bodyBuffer.length > 0) redirectReq.end(bodyBuffer);
|
|
341
|
+
else redirectReq.end();
|
|
342
|
+
return;
|
|
343
|
+
}
|
|
344
|
+
if (server) server.emit("proxyRes", proxyRes, req, res);
|
|
345
|
+
if (!res.headersSent && !options.selfHandleResponse) {
|
|
346
|
+
for (const pass of webOutgoingMiddleware) if (pass(req, res, proxyRes, options)) break;
|
|
347
|
+
}
|
|
348
|
+
if (res.finished) {
|
|
349
|
+
if (server) server.emit("end", req, res, proxyRes);
|
|
350
|
+
} else {
|
|
351
|
+
res.on("close", function() {
|
|
352
|
+
proxyRes.destroy();
|
|
353
|
+
});
|
|
354
|
+
proxyRes.on("close", function() {
|
|
355
|
+
if (!proxyRes.complete && !res.destroyed) res.destroy();
|
|
356
|
+
});
|
|
357
|
+
proxyRes.on("error", function(err) {
|
|
358
|
+
if (!res.destroyed) res.destroy(err);
|
|
359
|
+
if (server.listenerCount("error") > 0) server.emit("error", err, req, res, currentUrl);
|
|
360
|
+
});
|
|
361
|
+
proxyRes.on("end", function() {
|
|
362
|
+
if (server) server.emit("end", req, res, proxyRes);
|
|
363
|
+
});
|
|
364
|
+
if (!options.selfHandleResponse) proxyRes.pipe(res);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
proxyReq.on("response", function(proxyRes) {
|
|
368
|
+
handleResponse(proxyRes, 0, options.target);
|
|
369
|
+
});
|
|
370
|
+
})
|
|
371
|
+
];
|
|
372
|
+
const websocketIncomingMiddleware = [
|
|
373
|
+
defineProxyMiddleware((req, socket) => {
|
|
374
|
+
if (req.method !== "GET" || !req.headers.upgrade) {
|
|
375
|
+
socket.destroy();
|
|
376
|
+
return true;
|
|
377
|
+
}
|
|
378
|
+
if (req.headers.upgrade.toLowerCase() !== "websocket") {
|
|
379
|
+
socket.destroy();
|
|
380
|
+
return true;
|
|
381
|
+
}
|
|
382
|
+
}),
|
|
383
|
+
defineProxyMiddleware((req, socket, options) => {
|
|
384
|
+
if (!options.xfwd) return;
|
|
385
|
+
const values = {
|
|
386
|
+
for: req.connection.remoteAddress || req.socket.remoteAddress,
|
|
387
|
+
port: getPort(req),
|
|
388
|
+
proto: hasEncryptedConnection(req) ? "wss" : "ws"
|
|
389
|
+
};
|
|
390
|
+
for (const header of [
|
|
391
|
+
"for",
|
|
392
|
+
"port",
|
|
393
|
+
"proto"
|
|
394
|
+
]) {
|
|
395
|
+
const key = "x-forwarded-" + header;
|
|
396
|
+
if (!req.headers[key]) req.headers[key] = values[header];
|
|
397
|
+
}
|
|
398
|
+
}),
|
|
399
|
+
defineProxyMiddleware((req, socket, options, server, head, callback) => {
|
|
400
|
+
const createHttpHeader = function(line, headers) {
|
|
401
|
+
return Object.keys(headers).reduce(function(head, key) {
|
|
402
|
+
const value = headers[key];
|
|
403
|
+
if (!Array.isArray(value)) {
|
|
404
|
+
head.push(key + ": " + value);
|
|
405
|
+
return head;
|
|
406
|
+
}
|
|
407
|
+
for (const element of value) head.push(key + ": " + element);
|
|
408
|
+
return head;
|
|
409
|
+
}, [line]).join("\r\n") + "\r\n\r\n";
|
|
410
|
+
};
|
|
411
|
+
setupSocket(socket);
|
|
412
|
+
if (head && head.length > 0) socket.unshift(head);
|
|
413
|
+
socket.on("error", onSocketError);
|
|
414
|
+
const proxyReq = (isSSL.test(options.target.protocol || "http") ? https : http).request(setupOutgoing(options.ssl || {}, options, req));
|
|
415
|
+
if (server) server.emit("proxyReqWs", proxyReq, req, socket, options, head);
|
|
416
|
+
proxyReq.on("error", onOutgoingError);
|
|
417
|
+
proxyReq.on("response", function(res) {
|
|
418
|
+
if (!res.upgrade) if (!socket.destroyed && socket.writable) {
|
|
419
|
+
socket.write(createHttpHeader("HTTP/" + res.httpVersion + " " + res.statusCode + " " + res.statusMessage, res.headers));
|
|
420
|
+
res.on("error", onOutgoingError);
|
|
421
|
+
res.pipe(socket);
|
|
422
|
+
} else res.resume();
|
|
423
|
+
});
|
|
424
|
+
proxyReq.on("upgrade", function(proxyRes, proxySocket, proxyHead) {
|
|
425
|
+
proxySocket.on("error", onOutgoingError);
|
|
426
|
+
proxySocket.on("end", function() {
|
|
427
|
+
server.emit("close", proxyRes, proxySocket, proxyHead);
|
|
428
|
+
});
|
|
429
|
+
socket.removeListener("error", onSocketError);
|
|
430
|
+
socket.on("error", function() {
|
|
431
|
+
proxySocket.end();
|
|
432
|
+
});
|
|
433
|
+
setupSocket(proxySocket);
|
|
434
|
+
if (proxyHead && proxyHead.length > 0) proxySocket.unshift(proxyHead);
|
|
435
|
+
socket.write(createHttpHeader("HTTP/1.1 101 Switching Protocols", proxyRes.headers));
|
|
436
|
+
proxySocket.pipe(socket).pipe(proxySocket);
|
|
437
|
+
server.emit("open", proxySocket);
|
|
438
|
+
server.emit("proxySocket", proxySocket);
|
|
439
|
+
});
|
|
440
|
+
proxyReq.end();
|
|
441
|
+
function onSocketError(err) {
|
|
442
|
+
if (callback) callback(err, req, socket);
|
|
443
|
+
else server.emit("error", err, req, socket);
|
|
444
|
+
proxyReq.destroy();
|
|
445
|
+
}
|
|
446
|
+
function onOutgoingError(err) {
|
|
447
|
+
if (callback) callback(err, req, socket);
|
|
448
|
+
else server.emit("error", err, req, socket);
|
|
449
|
+
socket.end();
|
|
450
|
+
}
|
|
451
|
+
})
|
|
452
|
+
];
|
|
453
|
+
var ProxyServer = class extends EventEmitter {
|
|
454
|
+
_server;
|
|
455
|
+
_webPasses = [...webIncomingMiddleware];
|
|
456
|
+
_wsPasses = [...websocketIncomingMiddleware];
|
|
457
|
+
options;
|
|
458
|
+
web;
|
|
459
|
+
ws;
|
|
460
|
+
constructor(options = {}) {
|
|
461
|
+
super();
|
|
462
|
+
this.options = options || {};
|
|
463
|
+
this.options.prependPath = options.prependPath !== false;
|
|
464
|
+
this.web = _createProxyFn("web", this);
|
|
465
|
+
this.ws = _createProxyFn("ws", this);
|
|
466
|
+
}
|
|
467
|
+
listen(port, hostname) {
|
|
468
|
+
const closure = (req, res) => {
|
|
469
|
+
return this.web(req, res);
|
|
470
|
+
};
|
|
471
|
+
if (this.options.http2) {
|
|
472
|
+
if (!this.options.ssl) throw new Error("HTTP/2 requires ssl option");
|
|
473
|
+
this._server = http2.createSecureServer({
|
|
474
|
+
...this.options.ssl,
|
|
475
|
+
allowHTTP1: true
|
|
476
|
+
}, closure);
|
|
477
|
+
} else if (this.options.ssl) this._server = https.createServer(this.options.ssl, closure);
|
|
478
|
+
else this._server = http.createServer(closure);
|
|
479
|
+
if (this.options.ws) this._server.on("upgrade", (req, socket, head) => {
|
|
480
|
+
this.ws(req, socket, this.options, head).catch(() => {});
|
|
481
|
+
});
|
|
482
|
+
this._server.listen(port, hostname);
|
|
483
|
+
return this;
|
|
484
|
+
}
|
|
485
|
+
close(callback) {
|
|
486
|
+
if (this._server) this._server.close((...args) => {
|
|
487
|
+
this._server = void 0;
|
|
488
|
+
if (callback) Reflect.apply(callback, void 0, args);
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
before(type, passName, pass) {
|
|
492
|
+
if (type !== "ws" && type !== "web") throw new Error("type must be `web` or `ws`");
|
|
493
|
+
const passes = this._getPasses(type);
|
|
494
|
+
let i = false;
|
|
495
|
+
for (const [idx, v] of passes.entries()) if (v.name === passName) i = idx;
|
|
496
|
+
if (i === false) throw new Error("No such pass");
|
|
497
|
+
passes.splice(i, 0, pass);
|
|
498
|
+
}
|
|
499
|
+
after(type, passName, pass) {
|
|
500
|
+
if (type !== "ws" && type !== "web") throw new Error("type must be `web` or `ws`");
|
|
501
|
+
const passes = this._getPasses(type);
|
|
502
|
+
let i = false;
|
|
503
|
+
for (const [idx, v] of passes.entries()) if (v.name === passName) i = idx;
|
|
504
|
+
if (i === false) throw new Error("No such pass");
|
|
505
|
+
passes.splice(i++, 0, pass);
|
|
506
|
+
}
|
|
507
|
+
_getPasses(type) {
|
|
508
|
+
return type === "ws" ? this._wsPasses : this._webPasses;
|
|
509
|
+
}
|
|
510
|
+
};
|
|
511
|
+
function createProxyServer(options = {}) {
|
|
512
|
+
return new ProxyServer(options);
|
|
513
|
+
}
|
|
514
|
+
function _createProxyFn(type, server) {
|
|
515
|
+
return function(req, res, opts, head) {
|
|
516
|
+
const requestOptions = {
|
|
517
|
+
...opts,
|
|
518
|
+
...server.options
|
|
519
|
+
};
|
|
520
|
+
for (const key of ["target", "forward"]) if (typeof requestOptions[key] === "string") requestOptions[key] = new URL(requestOptions[key]);
|
|
521
|
+
if (!requestOptions.target && !requestOptions.forward) {
|
|
522
|
+
this.emit("error", /* @__PURE__ */ new Error("Must provide a proper URL as target"));
|
|
523
|
+
return Promise.resolve();
|
|
524
|
+
}
|
|
525
|
+
let _resolve;
|
|
526
|
+
let _reject;
|
|
527
|
+
const callbackPromise = new Promise((resolve, reject) => {
|
|
528
|
+
_resolve = resolve;
|
|
529
|
+
_reject = reject;
|
|
530
|
+
});
|
|
531
|
+
res.on("close", () => {
|
|
532
|
+
_resolve();
|
|
533
|
+
});
|
|
534
|
+
res.on("error", (error) => {
|
|
535
|
+
_reject(error);
|
|
536
|
+
});
|
|
537
|
+
for (const pass of server._getPasses(type)) {
|
|
538
|
+
let stop;
|
|
539
|
+
try {
|
|
540
|
+
stop = pass(req, res, requestOptions, server, head, (error) => {
|
|
541
|
+
if (server.listenerCount("error") > 0) {
|
|
542
|
+
server.emit("error", error, req, res);
|
|
543
|
+
_resolve();
|
|
544
|
+
} else _reject(error);
|
|
545
|
+
});
|
|
546
|
+
} catch (error) {
|
|
547
|
+
if (server.listenerCount("error") > 0) {
|
|
548
|
+
server.emit("error", error, req, res);
|
|
549
|
+
_resolve();
|
|
550
|
+
} else _reject(error);
|
|
551
|
+
break;
|
|
552
|
+
}
|
|
553
|
+
if (stop) {
|
|
554
|
+
_resolve();
|
|
555
|
+
break;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
return callbackPromise;
|
|
559
|
+
};
|
|
560
|
+
}
|
|
561
|
+
export { createProxyServer as t };
|
package/dist/_chunks/server.mjs
CHANGED
|
@@ -1,11 +1,88 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { t as __require } from "./rolldown-runtime.mjs";
|
|
2
|
+
import { t as createProxyServer } from "./libs/httpxy.mjs";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import { createReadStream, readFileSync, readdirSync, unlinkSync } from "node:fs";
|
|
5
|
+
import { randomBytes, randomUUID } from "node:crypto";
|
|
4
6
|
import { createServer } from "node:http";
|
|
5
7
|
import { extname, join, normalize, sep } from "node:path";
|
|
6
8
|
import { stat } from "node:fs/promises";
|
|
7
9
|
import { loadCode } from "#code";
|
|
8
|
-
|
|
10
|
+
if (process.platform === "android") {
|
|
11
|
+
Object.defineProperty(process, "platform", { value: "linux" });
|
|
12
|
+
if (process.execPath.includes("linker64") || process.execPath.startsWith("/apex/")) {
|
|
13
|
+
const resolved = process.env.TERMUX_EXEC__PROC_SELF_EXE || "/data/data/com.termux/files/usr/bin/node";
|
|
14
|
+
Object.defineProperty(process, "execPath", {
|
|
15
|
+
value: resolved,
|
|
16
|
+
writable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
enumerable: true
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
const preload = `--require "${fileURLToPath(import.meta.resolve("#android-preload"))}"`;
|
|
22
|
+
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ${preload}` : preload;
|
|
23
|
+
const _fs = process.getBuiltinModule?.("fs") ?? __require("node:fs");
|
|
24
|
+
const { syncBuiltinESMExports: _syncESM } = process.getBuiltinModule?.("module") ?? __require("node:module");
|
|
25
|
+
const TERMUX_EXEC_LIB = "/data/data/com.termux/files/usr/lib/libtermux-exec.so";
|
|
26
|
+
try {
|
|
27
|
+
_fs.accessSync(TERMUX_EXEC_LIB);
|
|
28
|
+
const _cp = process.getBuiltinModule?.("child_process") ?? __require("node:child_process");
|
|
29
|
+
const _injectLdPreload = (opts) => {
|
|
30
|
+
if (opts && typeof opts === "object" && opts.env) {
|
|
31
|
+
const env = opts.env;
|
|
32
|
+
if (!env.LD_PRELOAD) env.LD_PRELOAD = TERMUX_EXEC_LIB;
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
const _spawn = _cp.spawn;
|
|
36
|
+
_cp.spawn = function spawn(...spawnArgs) {
|
|
37
|
+
_injectLdPreload(spawnArgs[2]);
|
|
38
|
+
return _spawn.apply(this, spawnArgs);
|
|
39
|
+
};
|
|
40
|
+
const _fork = _cp.fork;
|
|
41
|
+
_cp.fork = function fork(modulePath, ...rest) {
|
|
42
|
+
for (const arg of rest) if (arg && typeof arg === "object" && !Array.isArray(arg)) {
|
|
43
|
+
_injectLdPreload(arg);
|
|
44
|
+
break;
|
|
45
|
+
}
|
|
46
|
+
return _fork.call(this, modulePath, ...rest);
|
|
47
|
+
};
|
|
48
|
+
_syncESM();
|
|
49
|
+
} catch {}
|
|
50
|
+
if (process.env.PATH) {
|
|
51
|
+
const accessible = process.env.PATH.split(":").filter((dir) => {
|
|
52
|
+
try {
|
|
53
|
+
_fs.accessSync(dir, _fs.constants.R_OK);
|
|
54
|
+
return true;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
if (accessible.length > 0) process.env.PATH = accessible.join(":");
|
|
60
|
+
}
|
|
61
|
+
const _os = process.getBuiltinModule?.("os") ?? __require("node:os");
|
|
62
|
+
const _crypto = process.getBuiltinModule?.("crypto") ?? __require("node:crypto");
|
|
63
|
+
const BLACKLISTED = new Set([
|
|
64
|
+
"00:00:00:00:00:00",
|
|
65
|
+
"ff:ff:ff:ff:ff:ff",
|
|
66
|
+
"ac:de:48:00:11:22"
|
|
67
|
+
]);
|
|
68
|
+
const original = _os.networkInterfaces;
|
|
69
|
+
_os.networkInterfaces = function networkInterfaces() {
|
|
70
|
+
const ifaces = original.call(_os);
|
|
71
|
+
for (const name in ifaces) for (const info of ifaces[name]) if (info.mac && !BLACKLISTED.has(info.mac)) return ifaces;
|
|
72
|
+
const hash = _crypto.createHash("md5").update(_os.hostname()).digest();
|
|
73
|
+
hash[0] = (hash[0] | 2) & 254;
|
|
74
|
+
ifaces._coderaft = [{
|
|
75
|
+
address: "10.0.0.1",
|
|
76
|
+
netmask: "255.255.255.0",
|
|
77
|
+
family: "IPv4",
|
|
78
|
+
mac: [...hash.subarray(0, 6)].map((b) => b.toString(16).padStart(2, "0")).join(":"),
|
|
79
|
+
internal: false,
|
|
80
|
+
cidr: "10.0.0.1/24"
|
|
81
|
+
}];
|
|
82
|
+
return ifaces;
|
|
83
|
+
};
|
|
84
|
+
_syncESM();
|
|
85
|
+
}
|
|
9
86
|
const STATIC_MIME = {
|
|
10
87
|
".js": "text/javascript",
|
|
11
88
|
".mjs": "text/javascript",
|
|
@@ -47,12 +124,7 @@ async function serveStatic(res, root, relPath) {
|
|
|
47
124
|
return false;
|
|
48
125
|
}
|
|
49
126
|
}
|
|
50
|
-
|
|
51
|
-
Object.defineProperty(process, "platform", { value: "linux" });
|
|
52
|
-
const preload = `--import "data:text/javascript,Object.defineProperty(process,'platform',{value:'linux'})"`;
|
|
53
|
-
process.env.NODE_OPTIONS = process.env.NODE_OPTIONS ? `${process.env.NODE_OPTIONS} ${preload}` : preload;
|
|
54
|
-
}
|
|
55
|
-
const _os = __require("node:os");
|
|
127
|
+
const _os = process.getBuiltinModule?.("os") ?? __require("node:os");
|
|
56
128
|
const MANIFEST_BODY = JSON.stringify({
|
|
57
129
|
name: "coderaft",
|
|
58
130
|
short_name: "coderaft",
|
|
@@ -79,12 +151,13 @@ async function createCodeServer(opts = {}) {
|
|
|
79
151
|
const withoutToken = opts.vscode?.["without-connection-token"] === true || !explicitToken && isLocal;
|
|
80
152
|
const connectionToken = withoutToken ? "" : opts.connectionToken ?? randomUUID();
|
|
81
153
|
const defaultFolder = opts.defaultFolder ?? process.cwd();
|
|
154
|
+
const baseURL = normalizeBaseURL(opts.baseURL ?? opts.vscode?.["server-base-path"]);
|
|
82
155
|
const mintKey = randomBytes(32);
|
|
83
156
|
process.env.CODE_SERVER_PARENT_PID ??= String(process.pid);
|
|
84
157
|
cleanupStaleLocks(opts.vscode?.["user-data-dir"] ?? join(_os.homedir(), ".vscode-server-oss", "data"));
|
|
158
|
+
watchChildProcessHealth();
|
|
85
159
|
const { modulesDir } = await loadCode();
|
|
86
160
|
const vsRootPath = join(modulesDir, "code-server", "lib", "vscode");
|
|
87
|
-
ensureNetworkInterface();
|
|
88
161
|
const _log = console.log;
|
|
89
162
|
console.log = (...args) => {
|
|
90
163
|
if (typeof args[0] === "string" && args[0].includes("[reconnection-grace-time]")) return;
|
|
@@ -100,17 +173,26 @@ async function createCodeServer(opts = {}) {
|
|
|
100
173
|
}
|
|
101
174
|
const vscodeServer = await (await mod.loadCodeWithNls()).createServer(null, {
|
|
102
175
|
"default-folder": defaultFolder,
|
|
176
|
+
...baseURL ? { "server-base-path": baseURL } : {},
|
|
103
177
|
...withoutToken ? { "without-connection-token": true } : { "connection-token": connectionToken },
|
|
104
|
-
"reconnection-grace-time": "
|
|
178
|
+
"reconnection-grace-time": "30",
|
|
105
179
|
"disable-getting-started-override": true,
|
|
106
180
|
...opts.vscode
|
|
107
181
|
});
|
|
108
182
|
console.log = _log;
|
|
183
|
+
const proxy = createProxyServer({});
|
|
184
|
+
proxy.on("error", (err, req, res) => {
|
|
185
|
+
if (res && "writeHead" in res) {
|
|
186
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
187
|
+
res.end(`Proxy error: ${err.message}`);
|
|
188
|
+
}
|
|
189
|
+
});
|
|
109
190
|
return {
|
|
110
191
|
connectionToken,
|
|
111
192
|
handleRequest(req, res) {
|
|
112
193
|
const method = req.method ?? "GET";
|
|
113
|
-
const
|
|
194
|
+
const strippedUrl = stripBaseURL(req.url ?? "/", baseURL);
|
|
195
|
+
const url = strippedUrl.split("?")[0];
|
|
114
196
|
if (url === "/manifest.json") {
|
|
115
197
|
res.writeHead(200, {
|
|
116
198
|
"Content-Type": "application/manifest+json",
|
|
@@ -153,7 +235,7 @@ async function createCodeServer(opts = {}) {
|
|
|
153
235
|
return;
|
|
154
236
|
}
|
|
155
237
|
if (url === "/login" || url === "/logout") {
|
|
156
|
-
res.writeHead(302, { Location:
|
|
238
|
+
res.writeHead(302, { Location: `${baseURL}/` });
|
|
157
239
|
res.end();
|
|
158
240
|
return;
|
|
159
241
|
}
|
|
@@ -163,11 +245,30 @@ async function createCodeServer(opts = {}) {
|
|
|
163
245
|
});
|
|
164
246
|
return;
|
|
165
247
|
}
|
|
248
|
+
const proxyMatch = parseProxyPath(strippedUrl);
|
|
249
|
+
if (proxyMatch) {
|
|
250
|
+
const { port: targetPort, path: targetPath } = proxyMatch;
|
|
251
|
+
proxy.web(req, res, {
|
|
252
|
+
target: `http://127.0.0.1:${targetPort}${targetPath}`,
|
|
253
|
+
ignorePath: true,
|
|
254
|
+
xfwd: true
|
|
255
|
+
});
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
166
258
|
vscodeServer.handleRequest(req, res);
|
|
167
259
|
},
|
|
168
|
-
handleUpgrade(req, socket) {
|
|
169
|
-
|
|
170
|
-
|
|
260
|
+
handleUpgrade(req, socket, _head) {
|
|
261
|
+
const proxyMatch = parseProxyPath(stripBaseURL(req.url ?? "/", baseURL));
|
|
262
|
+
if (proxyMatch) {
|
|
263
|
+
const { port: targetPort, path: targetPath } = proxyMatch;
|
|
264
|
+
proxy.ws(req, socket, {
|
|
265
|
+
target: `http://127.0.0.1:${targetPort}${targetPath}`,
|
|
266
|
+
ignorePath: true,
|
|
267
|
+
xfwd: true
|
|
268
|
+
}, _head);
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
vscodeServer.handleUpgrade(req, socket, _head);
|
|
171
272
|
},
|
|
172
273
|
async dispose() {
|
|
173
274
|
vscodeServer.dispose();
|
|
@@ -175,15 +276,16 @@ async function createCodeServer(opts = {}) {
|
|
|
175
276
|
};
|
|
176
277
|
}
|
|
177
278
|
async function startCodeServer(opts = {}) {
|
|
279
|
+
const socketPath = opts.socketPath;
|
|
178
280
|
const port = opts.port ?? (Number(process.env.PORT) || 6063);
|
|
179
281
|
const handler = await createCodeServer(opts);
|
|
180
282
|
const server = createServer((req, res) => {
|
|
181
283
|
handler.handleRequest(req, res);
|
|
182
284
|
});
|
|
183
|
-
server.on("upgrade", (req, socket) => {
|
|
184
|
-
handler.handleUpgrade(req, socket);
|
|
285
|
+
server.on("upgrade", (req, socket, head) => {
|
|
286
|
+
handler.handleUpgrade(req, socket, head);
|
|
185
287
|
});
|
|
186
|
-
const
|
|
288
|
+
const listenTcp = (p) => new Promise((resolve, reject) => {
|
|
187
289
|
server.once("error", reject);
|
|
188
290
|
const cb = () => {
|
|
189
291
|
server.removeListener("error", reject);
|
|
@@ -192,17 +294,31 @@ async function startCodeServer(opts = {}) {
|
|
|
192
294
|
if (opts.host) server.listen(p, opts.host, cb);
|
|
193
295
|
else server.listen(p, cb);
|
|
194
296
|
});
|
|
195
|
-
|
|
196
|
-
|
|
297
|
+
const listenSocket = (path) => new Promise((resolve, reject) => {
|
|
298
|
+
try {
|
|
299
|
+
unlinkSync(path);
|
|
300
|
+
} catch {}
|
|
301
|
+
server.once("error", reject);
|
|
302
|
+
server.listen(path, () => {
|
|
303
|
+
server.removeListener("error", reject);
|
|
304
|
+
resolve();
|
|
305
|
+
});
|
|
306
|
+
});
|
|
307
|
+
if (socketPath) await listenSocket(socketPath);
|
|
308
|
+
else try {
|
|
309
|
+
await listenTcp(port);
|
|
197
310
|
} catch (err) {
|
|
198
|
-
if (err?.code === "EADDRINUSE") await
|
|
311
|
+
if (err?.code === "EADDRINUSE") await listenTcp(0);
|
|
199
312
|
else throw err;
|
|
200
313
|
}
|
|
201
|
-
const
|
|
314
|
+
const address = server.address();
|
|
315
|
+
const actualPort = address && typeof address === "object" && "port" in address ? address.port : void 0;
|
|
316
|
+
const basePath = normalizeBaseURL(opts.baseURL ?? opts.vscode?.["server-base-path"]);
|
|
202
317
|
return {
|
|
203
318
|
server,
|
|
204
319
|
port: actualPort,
|
|
205
|
-
|
|
320
|
+
socketPath,
|
|
321
|
+
url: socketPath ? `unix:${socketPath}` : handler.connectionToken ? `http://localhost:${actualPort}${basePath}/?tkn=${handler.connectionToken}` : `http://localhost:${actualPort}${basePath}/`,
|
|
206
322
|
connectionToken: handler.connectionToken,
|
|
207
323
|
async close() {
|
|
208
324
|
await handler.dispose();
|
|
@@ -210,33 +326,12 @@ async function startCodeServer(opts = {}) {
|
|
|
210
326
|
await new Promise((resolve, reject) => {
|
|
211
327
|
server.close((err) => err ? reject(err) : resolve());
|
|
212
328
|
});
|
|
329
|
+
if (socketPath) try {
|
|
330
|
+
unlinkSync(socketPath);
|
|
331
|
+
} catch {}
|
|
213
332
|
}
|
|
214
333
|
};
|
|
215
334
|
}
|
|
216
|
-
function ensureNetworkInterface() {
|
|
217
|
-
const original = _os.networkInterfaces;
|
|
218
|
-
const BLACKLISTED = new Set([
|
|
219
|
-
"00:00:00:00:00:00",
|
|
220
|
-
"ff:ff:ff:ff:ff:ff",
|
|
221
|
-
"ac:de:48:00:11:22"
|
|
222
|
-
]);
|
|
223
|
-
_os.networkInterfaces = function networkInterfaces() {
|
|
224
|
-
const ifaces = original.call(_os);
|
|
225
|
-
for (const name in ifaces) for (const info of ifaces[name]) if (info.mac && !BLACKLISTED.has(info.mac)) return ifaces;
|
|
226
|
-
const { createHash } = __require("node:crypto");
|
|
227
|
-
const hash = createHash("md5").update(_os.hostname()).digest();
|
|
228
|
-
hash[0] = (hash[0] | 2) & 254;
|
|
229
|
-
ifaces._coderaft = [{
|
|
230
|
-
address: "10.0.0.1",
|
|
231
|
-
netmask: "255.255.255.0",
|
|
232
|
-
family: "IPv4",
|
|
233
|
-
mac: [...hash.subarray(0, 6)].map((b) => b.toString(16).padStart(2, "0")).join(":"),
|
|
234
|
-
internal: false,
|
|
235
|
-
cidr: "10.0.0.1/24"
|
|
236
|
-
}];
|
|
237
|
-
return ifaces;
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
335
|
function cleanupStaleLocks(userDataDir) {
|
|
241
336
|
const storageDir = join(userDataDir, "User", "workspaceStorage");
|
|
242
337
|
try {
|
|
@@ -244,10 +339,65 @@ function cleanupStaleLocks(userDataDir) {
|
|
|
244
339
|
const lockPath = join(storageDir, entry, "vscode.lock");
|
|
245
340
|
try {
|
|
246
341
|
unlinkSync(lockPath);
|
|
342
|
+
console.log(`[coderaft] Removed stale lock: ${lockPath}`);
|
|
247
343
|
} catch {}
|
|
248
344
|
}
|
|
249
345
|
} catch {}
|
|
250
346
|
}
|
|
347
|
+
function watchChildProcessHealth() {
|
|
348
|
+
if (process.platform !== "linux" && process.platform !== "android") return;
|
|
349
|
+
const stuckCounts = /* @__PURE__ */ new Map();
|
|
350
|
+
const interval = setInterval(() => {
|
|
351
|
+
try {
|
|
352
|
+
for (const pid of readdirSync("/proc").filter((d) => /^\d+$/.test(d))) {
|
|
353
|
+
let cmdline;
|
|
354
|
+
try {
|
|
355
|
+
cmdline = readFileSync(`/proc/${pid}/cmdline`, "utf8");
|
|
356
|
+
} catch {
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (!cmdline.includes("extensionHost")) continue;
|
|
360
|
+
try {
|
|
361
|
+
const wchan = readFileSync(`/proc/${pid}/task/${pid}/wchan`, "utf8").trim();
|
|
362
|
+
if (wchan === "__futex_wait") {
|
|
363
|
+
const count = (stuckCounts.get(+pid) ?? 0) + 1;
|
|
364
|
+
stuckCounts.set(+pid, count);
|
|
365
|
+
if (count === 3) console.error(`[coderaft] Extension host (pid ${pid}) main thread stuck in ${wchan} for ${count * 5}s`);
|
|
366
|
+
} else stuckCounts.delete(+pid);
|
|
367
|
+
} catch {}
|
|
368
|
+
}
|
|
369
|
+
} catch {}
|
|
370
|
+
}, 5e3);
|
|
371
|
+
interval.unref();
|
|
372
|
+
return interval;
|
|
373
|
+
}
|
|
374
|
+
const PROXY_RE = /^\/proxy\/(\d+)(\/.*)?$/;
|
|
375
|
+
function parseProxyPath(url) {
|
|
376
|
+
const qIdx = url.indexOf("?");
|
|
377
|
+
const pathname = qIdx === -1 ? url : url.slice(0, qIdx);
|
|
378
|
+
const query = qIdx === -1 ? "" : url.slice(qIdx);
|
|
379
|
+
const match = PROXY_RE.exec(pathname);
|
|
380
|
+
if (!match) return;
|
|
381
|
+
const port = Number(match[1]);
|
|
382
|
+
if (port < 1 || port > 65535) return;
|
|
383
|
+
return {
|
|
384
|
+
port,
|
|
385
|
+
path: (match[2] || "/") + query
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
function normalizeBaseURL(input) {
|
|
389
|
+
if (!input || input === "/") return "";
|
|
390
|
+
let result = input.trim();
|
|
391
|
+
if (!result.startsWith("/")) result = "/" + result;
|
|
392
|
+
while (result.endsWith("/")) result = result.slice(0, -1);
|
|
393
|
+
return result;
|
|
394
|
+
}
|
|
395
|
+
function stripBaseURL(url, baseURL) {
|
|
396
|
+
if (!baseURL) return url;
|
|
397
|
+
if (url === baseURL) return "/";
|
|
398
|
+
if (url.startsWith(baseURL + "/") || url.startsWith(baseURL + "?")) return url.slice(baseURL.length) || "/";
|
|
399
|
+
return url;
|
|
400
|
+
}
|
|
251
401
|
function sendJson(res, status, body) {
|
|
252
402
|
const payload = JSON.stringify(body);
|
|
253
403
|
res.writeHead(status, {
|
package/dist/cli.mjs
CHANGED
|
@@ -12,6 +12,7 @@ const { values, positionals } = parseArgs({
|
|
|
12
12
|
type: "string",
|
|
13
13
|
short: "H"
|
|
14
14
|
},
|
|
15
|
+
"base-url": { type: "string" },
|
|
15
16
|
"server-base-path": { type: "string" },
|
|
16
17
|
"socket-path": { type: "string" },
|
|
17
18
|
"print-startup-performance": { type: "boolean" },
|
|
@@ -92,7 +93,7 @@ if (values.help) {
|
|
|
92
93
|
Server:
|
|
93
94
|
-p, --port <port> Port to listen on (default: $PORT or 6063)
|
|
94
95
|
-H, --host <host> Host/interface to bind
|
|
95
|
-
--
|
|
96
|
+
--base-url <path> Base URL the server is mounted under (default: /)
|
|
96
97
|
--socket-path <path> Path to a socket file to listen on
|
|
97
98
|
--print-startup-performance Print startup timing to stdout
|
|
98
99
|
|
|
@@ -175,7 +176,6 @@ if (values.help) {
|
|
|
175
176
|
const vscode = {};
|
|
176
177
|
for (const key of [
|
|
177
178
|
"server-base-path",
|
|
178
|
-
"socket-path",
|
|
179
179
|
"print-startup-performance",
|
|
180
180
|
"connection-token-file",
|
|
181
181
|
"without-connection-token",
|
|
@@ -224,9 +224,22 @@ for (const key of [
|
|
|
224
224
|
if (values["logs-path"]) vscode.logsPath = values["logs-path"];
|
|
225
225
|
const dir = positionals[0];
|
|
226
226
|
if (dir) vscode["disable-workspace-trust"] = true;
|
|
227
|
-
|
|
227
|
+
let handle;
|
|
228
|
+
let shuttingDown = false;
|
|
229
|
+
const shutdown = () => {
|
|
230
|
+
if (shuttingDown) process.exit(0);
|
|
231
|
+
shuttingDown = true;
|
|
232
|
+
setTimeout(() => process.exit(0), 3e3).unref();
|
|
233
|
+
if (handle) handle.close().finally(() => process.exit(0));
|
|
234
|
+
else process.exit(0);
|
|
235
|
+
};
|
|
236
|
+
process.on("SIGINT", shutdown);
|
|
237
|
+
process.on("SIGTERM", shutdown);
|
|
238
|
+
handle = await startCodeServer({
|
|
228
239
|
port: values.port ? Number(values.port) : void 0,
|
|
229
240
|
host: values.host,
|
|
241
|
+
socketPath: values["socket-path"],
|
|
242
|
+
baseURL: values["base-url"] ?? values["server-base-path"],
|
|
230
243
|
defaultFolder: dir || values["default-folder"],
|
|
231
244
|
connectionToken: values["connection-token"] ?? values.token,
|
|
232
245
|
vscode
|
|
@@ -247,13 +260,4 @@ if (values.open) {
|
|
|
247
260
|
else if (platform === "win32") exec(`start chrome --app="${url}" || start msedge --app="${url}" || start "" "${url}"`);
|
|
248
261
|
else exec(`google-chrome-stable --app="${url}" 2>/dev/null || google-chrome --app="${url}" 2>/dev/null || chromium --app="${url}" 2>/dev/null || xdg-open "${url}"`);
|
|
249
262
|
}
|
|
250
|
-
let shuttingDown = false;
|
|
251
|
-
const shutdown = () => {
|
|
252
|
-
if (shuttingDown) process.exit(0);
|
|
253
|
-
shuttingDown = true;
|
|
254
|
-
setTimeout(() => process.exit(0), 3e3).unref();
|
|
255
|
-
handle.close().finally(() => process.exit(0));
|
|
256
|
-
};
|
|
257
|
-
process.on("SIGINT", shutdown);
|
|
258
|
-
process.on("SIGTERM", shutdown);
|
|
259
263
|
export {};
|
package/dist/index.d.mts
CHANGED
|
@@ -85,24 +85,35 @@ interface CreateCodeServerOptions {
|
|
|
85
85
|
connectionToken?: string;
|
|
86
86
|
/** Host/interface to bind (used to infer local-only access for token default). */
|
|
87
87
|
host?: string;
|
|
88
|
+
/**
|
|
89
|
+
* Base URL the server is mounted under (e.g. `/code`). Defaults to `/`.
|
|
90
|
+
* Forwarded to VS Code as `server-base-path` and honored by coderaft's own
|
|
91
|
+
* routes (`/healthz`, `/_static/*`, `/proxy/*`, `/login`, …).
|
|
92
|
+
*/
|
|
93
|
+
baseURL?: string;
|
|
88
94
|
/** Extra options forwarded to VS Code's `createServer()`. */
|
|
89
95
|
vscode?: VSCodeServerOptions;
|
|
90
96
|
}
|
|
91
97
|
interface StartCodeServerOptions extends CreateCodeServerOptions {
|
|
92
|
-
/** TCP port to listen on. Defaults to `$PORT` or `6063`. */
|
|
98
|
+
/** TCP port to listen on. Defaults to `$PORT` or `6063`. Ignored when `socketPath` is set. */
|
|
93
99
|
port?: number;
|
|
100
|
+
/** Unix socket path to listen on. When set, `port` and `host` are ignored. */
|
|
101
|
+
socketPath?: string;
|
|
94
102
|
}
|
|
95
103
|
interface CodeServerHandler {
|
|
96
104
|
/** Node-style HTTP request handler (middleware). */
|
|
97
105
|
handleRequest(req: IncomingMessage, res: ServerResponse): void;
|
|
98
106
|
/** Handle WebSocket upgrade. */
|
|
99
|
-
handleUpgrade(req: IncomingMessage, socket: Duplex): void;
|
|
107
|
+
handleUpgrade(req: IncomingMessage, socket: Duplex, head: Buffer): void;
|
|
100
108
|
connectionToken: string;
|
|
101
109
|
dispose(): Promise<void>;
|
|
102
110
|
}
|
|
103
111
|
interface CodeServerHandle {
|
|
104
112
|
server: Server;
|
|
105
|
-
port
|
|
113
|
+
/** TCP port the server is bound to, or `undefined` when listening on a unix socket. */
|
|
114
|
+
port?: number;
|
|
115
|
+
/** Unix socket path the server is bound to, or `undefined` when listening on TCP. */
|
|
116
|
+
socketPath?: string;
|
|
106
117
|
url: string;
|
|
107
118
|
connectionToken: string;
|
|
108
119
|
close(): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "coderaft",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.18",
|
|
4
4
|
"repository": "pithings/coderaft",
|
|
5
5
|
"bin": {
|
|
6
6
|
"coderaft": "./dist/cli.mjs"
|
|
@@ -11,11 +11,13 @@
|
|
|
11
11
|
"ThirdPartyNotices.txt",
|
|
12
12
|
"tar.mjs",
|
|
13
13
|
"code.mjs",
|
|
14
|
+
"android-preload.cjs",
|
|
14
15
|
"code.tar.zst"
|
|
15
16
|
],
|
|
16
17
|
"type": "module",
|
|
17
18
|
"imports": {
|
|
18
|
-
"#code": "./code.mjs"
|
|
19
|
+
"#code": "./code.mjs",
|
|
20
|
+
"#android-preload": "./android-preload.cjs"
|
|
19
21
|
},
|
|
20
22
|
"scripts": {
|
|
21
23
|
"build": "obuild"
|
|
@@ -62,6 +64,7 @@
|
|
|
62
64
|
"vscode-oniguruma": "1.7.0",
|
|
63
65
|
"vscode-regexpp": "^3.1.0",
|
|
64
66
|
"vscode-textmate": "^9.3.2",
|
|
67
|
+
"httpxy": "^0.5.0",
|
|
65
68
|
"ws": "^8.19.0",
|
|
66
69
|
"yauzl": "^3.0.0",
|
|
67
70
|
"yazl": "^2.4.3"
|