@jixo/opendweb-server-binary 0.0.0 → 0.1.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/bin/dweb-server-aarch64-apple-darwin +0 -0
- package/bin/dweb-server.mjs +36 -0
- package/index.d.ts +20 -0
- package/index.js +91 -1
- package/package.json +29 -4
- package/scripts/pack.mjs +73 -0
|
Binary file
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// dweb-server CLI 入口:转发参数与信号到平台二进制
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
|
|
7
|
+
const binDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
|
|
9
|
+
const PLATFORM_BINARIES = {
|
|
10
|
+
"darwin-arm64": "dweb-server-aarch64-apple-darwin",
|
|
11
|
+
"win32-x64": "dweb-server-x86_64-pc-windows.exe",
|
|
12
|
+
};
|
|
13
|
+
const SUPPORTED = `${process.platform}-${process.arch}`;
|
|
14
|
+
const BINARY_NAME = PLATFORM_BINARIES[SUPPORTED];
|
|
15
|
+
if (!BINARY_NAME) {
|
|
16
|
+
console.error(
|
|
17
|
+
`@jixo/opendweb-server-binary: 当前平台 ${SUPPORTED} 暂不支持。v0.1 提供 ${Object.keys(PLATFORM_BINARIES).join(" / ")};其它平台请使用 docker 镜像 ghcr.io/gaubee/dweb。`,
|
|
18
|
+
);
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const binPath = path.join(binDir, BINARY_NAME);
|
|
23
|
+
const child = spawn(binPath, process.argv.slice(2), {
|
|
24
|
+
stdio: "inherit",
|
|
25
|
+
env: process.env,
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const forward = (sig) => () => {
|
|
29
|
+
if (!child.killed) child.kill(sig);
|
|
30
|
+
};
|
|
31
|
+
process.on("SIGINT", forward("SIGINT"));
|
|
32
|
+
process.on("SIGTERM", forward("SIGTERM"));
|
|
33
|
+
child.on("exit", (code, signal) => {
|
|
34
|
+
if (signal) process.kill(process.pid, signal);
|
|
35
|
+
else process.exit(code ?? 0);
|
|
36
|
+
});
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export interface StartServerOptions {
|
|
2
|
+
/** rendezvous/healthz 监听地址,默认 127.0.0.1:8787 */
|
|
3
|
+
httpBind?: string;
|
|
4
|
+
/** relay HTTP 监听地址,默认 127.0.0.1:3340 */
|
|
5
|
+
relayBind?: string;
|
|
6
|
+
/** 默认 true */
|
|
7
|
+
relayEnabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface ServerHandle {
|
|
11
|
+
pid: number;
|
|
12
|
+
httpUrl: string;
|
|
13
|
+
relayHttpUrl: string;
|
|
14
|
+
/** 发送 SIGINT 并等待退出(5s 后 SIGKILL 兜底),幂等 */
|
|
15
|
+
stop(): Promise<void>;
|
|
16
|
+
/** 进程退出码 future */
|
|
17
|
+
exited: Promise<number>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export declare function startServer(options?: StartServerOptions): Promise<ServerHandle>;
|
package/index.js
CHANGED
|
@@ -1 +1,91 @@
|
|
|
1
|
-
|
|
1
|
+
// @dweb/server-binary:以子进程方式启动 dweb-server,提供可等待的停止方式
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { createHash } from "node:crypto";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
|
|
9
|
+
const PLATFORM_BINARIES = {
|
|
10
|
+
"darwin-arm64": "dweb-server-aarch64-apple-darwin",
|
|
11
|
+
"win32-x64": "dweb-server-x86_64-pc-windows.exe",
|
|
12
|
+
};
|
|
13
|
+
const SUPPORTED = `${process.platform}-${process.arch}`;
|
|
14
|
+
const BINARY_NAME = PLATFORM_BINARIES[SUPPORTED];
|
|
15
|
+
if (!BINARY_NAME) {
|
|
16
|
+
throw new Error(
|
|
17
|
+
`@jixo/opendweb-server-binary: 当前平台 ${SUPPORTED} 暂不支持。v0.1 提供 ${Object.keys(PLATFORM_BINARIES).join(" / ")};其它平台请使用 docker 镜像 ghcr.io/gaubee/dweb。`,
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @typedef {Object} StartServerOptions
|
|
23
|
+
* @property {string} [httpBind] rendezvous/healthz 监听地址,默认 127.0.0.1:8787
|
|
24
|
+
* @property {string} [relayBind] relay HTTP 监听地址,默认 127.0.0.1:3340
|
|
25
|
+
* @property {boolean} [relayEnabled] 默认 true
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 启动 dweb-server 子进程。
|
|
30
|
+
* @param {StartServerOptions} [options]
|
|
31
|
+
* @returns {Promise<{ pid: number, httpUrl: string, relayHttpUrl: string, stop: () => Promise<void>, exited: Promise<number> }>}
|
|
32
|
+
*/
|
|
33
|
+
export async function startServer(options = {}) {
|
|
34
|
+
const binDir = path.dirname(fileURLToPath(import.meta.url));
|
|
35
|
+
const srcBin = path.join(binDir, "bin", BINARY_NAME);
|
|
36
|
+
// SMB 网络磁盘(开发机)上的原生二进制会触发 CODESIGNING Invalid Page:
|
|
37
|
+
// darwin 拷到私有 tmp 内容寻址路径执行。Windows 无此问题且 tmp 无扩展名
|
|
38
|
+
// 拷贝会被 Defender/路径解析干扰——直接执行源路径。
|
|
39
|
+
let binPath = srcBin;
|
|
40
|
+
if (process.platform === "darwin") {
|
|
41
|
+
try {
|
|
42
|
+
const buf = fs.readFileSync(srcBin);
|
|
43
|
+
const hash = createHash("sha256").update(buf).digest("hex").slice(0, 24);
|
|
44
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "opendweb-server-"));
|
|
45
|
+
const dest = path.join(dir, hash);
|
|
46
|
+
const fd = fs.openSync(dest, fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY, 0o755);
|
|
47
|
+
try {
|
|
48
|
+
fs.writeFileSync(fd, buf);
|
|
49
|
+
fs.fsyncSync(fd);
|
|
50
|
+
} finally {
|
|
51
|
+
fs.closeSync(fd);
|
|
52
|
+
}
|
|
53
|
+
binPath = dest;
|
|
54
|
+
} catch {
|
|
55
|
+
binPath = srcBin; // 退回直接执行源路径
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const httpBind = options.httpBind ?? "127.0.0.1:8787";
|
|
60
|
+
const relayBind = options.relayBind ?? "127.0.0.1:3340";
|
|
61
|
+
const env = {
|
|
62
|
+
...process.env,
|
|
63
|
+
DWEB_HTTP_BIND: httpBind,
|
|
64
|
+
DWEB_RELAY_HTTP_BIND: relayBind,
|
|
65
|
+
DWEB_RELAY_ENABLED: options.relayEnabled === false ? "false" : "true",
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const child = spawn(binPath, [], { env, stdio: ["ignore", "pipe", "pipe"] });
|
|
69
|
+
if (typeof child.pid !== "number") {
|
|
70
|
+
throw new Error("failed to spawn dweb-server");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const exited = new Promise((resolve, reject) => {
|
|
74
|
+
child.once("exit", (code) => resolve(code ?? 0));
|
|
75
|
+
child.once("error", reject);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
const stop = () =>
|
|
79
|
+
new Promise((resolve) => {
|
|
80
|
+
if (child.exitCode !== null || child.signalCode !== null) return resolve();
|
|
81
|
+
child.once("exit", () => resolve());
|
|
82
|
+
child.kill("SIGINT");
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
if (child.exitCode === null && child.signalCode === null) child.kill("SIGKILL");
|
|
85
|
+
}, 5000).unref();
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const httpUrl = `http://${httpBind}`;
|
|
89
|
+
const relayHttpUrl = `http://${relayBind}`;
|
|
90
|
+
return { pid: child.pid, httpUrl, relayHttpUrl, stop, exited };
|
|
91
|
+
}
|
package/package.json
CHANGED
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jixo/opendweb-server-binary",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "@jixo/opendweb-server-binary — dweb self-hostable server binary (darwin-arm64 + win32-x64)",
|
|
5
|
+
"license": "MIT OR Apache-2.0",
|
|
6
|
+
"bin": {
|
|
7
|
+
"dweb-server": "./bin/dweb-server.mjs"
|
|
8
|
+
},
|
|
5
9
|
"main": "index.js",
|
|
10
|
+
"types": "index.d.ts",
|
|
11
|
+
"type": "module",
|
|
12
|
+
"scripts": {
|
|
13
|
+
"pack:binary": "node scripts/pack.mjs",
|
|
14
|
+
"test": "node scripts/pack.mjs && node --test --test-force-exit",
|
|
15
|
+
"typecheck": "tsc --noEmit",
|
|
16
|
+
"build:win": "PATH=\"$HOME/.cargo/bin:$PATH\" cargo build --release --target x86_64-pc-windows-gnu -p dweb-server && cp \"$HOME/.cargo-target/dweb/x86_64-pc-windows-gnu/release/dweb-server.exe\" bin/dweb-server-x86_64-pc-windows.exe"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"typescript": "^5.6.0",
|
|
20
|
+
"vitest": "^3.0.0"
|
|
21
|
+
},
|
|
6
22
|
"files": [
|
|
7
|
-
"index.js"
|
|
8
|
-
|
|
23
|
+
"index.js",
|
|
24
|
+
"index.d.ts",
|
|
25
|
+
"bin/dweb-server.mjs",
|
|
26
|
+
"scripts/pack.mjs",
|
|
27
|
+
"bin/dweb-server-aarch64-apple-darwin",
|
|
28
|
+
"bin/dweb-server-x86_64-pc-windows-windows.exe"
|
|
29
|
+
],
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/Gaubee/dweb.git"
|
|
33
|
+
}
|
|
9
34
|
}
|
package/scripts/pack.mjs
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// 平台感知的 release 二进制打包:从 cargo target 拷贝到包内 bin/。
|
|
2
|
+
// darwin: build host target 产物 dweb-server → bin/dweb-server-aarch64-apple-darwin
|
|
3
|
+
// windows: 优先复用 CI 已构建的 --target x86_64-pc-windows-msvc 产物(.exe);
|
|
4
|
+
// staging 已放好 bin/dweb-server-x86_64-pc-windows.exe 时直接通过。
|
|
5
|
+
import { spawnSync } from "node:child_process";
|
|
6
|
+
import { cpSync, chmodSync, mkdirSync, existsSync } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { fileURLToPath } from "node:url";
|
|
9
|
+
|
|
10
|
+
const pkgDir = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
11
|
+
const binDir = path.join(pkgDir, "bin");
|
|
12
|
+
mkdirSync(binDir, { recursive: true });
|
|
13
|
+
|
|
14
|
+
function findWorkspaceRoot() {
|
|
15
|
+
if (process.env.DWEB_ROOT) return process.env.DWEB_ROOT;
|
|
16
|
+
let dir = pkgDir;
|
|
17
|
+
for (let i = 0; i < 4; i++) {
|
|
18
|
+
if (existsSync(path.join(dir, "Cargo.toml"))) return dir;
|
|
19
|
+
dir = path.dirname(dir);
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveTargetDir() {
|
|
25
|
+
const raw = process.env.CARGO_TARGET_DIR ?? path.join(process.env.HOME ?? "~", ".cargo-target", "dweb");
|
|
26
|
+
return raw.replace(/^\$HOME/, process.env.HOME ?? "").replace(/^~/, process.env.HOME ?? "");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (process.platform === "win32") {
|
|
30
|
+
const staged = path.join(binDir, "dweb-server-x86_64-pc-windows.exe");
|
|
31
|
+
if (existsSync(staged)) {
|
|
32
|
+
console.log("packed(win): bin/dweb-server-x86_64-pc-windows.exe (staged)");
|
|
33
|
+
process.exit(0);
|
|
34
|
+
}
|
|
35
|
+
// 本地 Windows 开发:构建 target 产物
|
|
36
|
+
const root = findWorkspaceRoot();
|
|
37
|
+
const targetDir = resolveTargetDir();
|
|
38
|
+
const src = path.join(targetDir, "x86_64-pc-windows-msvc", "release", "dweb-server.exe");
|
|
39
|
+
if (!existsSync(src)) {
|
|
40
|
+
if (root) {
|
|
41
|
+
const built = spawnSync("cargo", ["build", "--release", "--target", "x86_64-pc-windows-msvc", "-p", "dweb-server"], { stdio: "inherit", cwd: root });
|
|
42
|
+
if (built.status !== 0) throw new Error("cargo build failed");
|
|
43
|
+
}
|
|
44
|
+
if (!existsSync(src)) {
|
|
45
|
+
throw new Error(`windows binary not found at ${src} (no MSVC toolchain on non-windows hosts is expected; use CI staging)`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
cpSync(src, staged);
|
|
49
|
+
console.log(`packed(win): ${staged}`);
|
|
50
|
+
process.exit(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// darwin(host build)
|
|
54
|
+
const root = findWorkspaceRoot();
|
|
55
|
+
if (root) {
|
|
56
|
+
const built = spawnSync("cargo", ["build", "--release", "-p", "dweb-server"], {
|
|
57
|
+
stdio: "inherit",
|
|
58
|
+
cwd: root,
|
|
59
|
+
});
|
|
60
|
+
if (built.status !== 0) {
|
|
61
|
+
throw new Error("cargo build failed");
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const targetDir = resolveTargetDir();
|
|
66
|
+
const src = path.join(targetDir, "release", "dweb-server");
|
|
67
|
+
if (!existsSync(src)) {
|
|
68
|
+
throw new Error(`binary not found at ${src}`);
|
|
69
|
+
}
|
|
70
|
+
const dest = path.join(binDir, "dweb-server-aarch64-apple-darwin");
|
|
71
|
+
cpSync(src, dest);
|
|
72
|
+
chmodSync(dest, 0o755);
|
|
73
|
+
console.log("packed: bin/dweb-server-aarch64-apple-darwin");
|