pinomad 0.4.0 → 0.6.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/README.md +4 -2
- package/dist/{chunk-837gh8nr.js → chunk-0xh6cy9v.js} +69 -17
- package/dist/{chunk-n73t5xzw.js → chunk-367n9je8.js} +2 -2
- package/dist/{chunk-xhwkx9bd.js → chunk-3dnnaz4j.js} +14 -15
- package/dist/{chunk-z5pmcscx.js → chunk-4btj08we.js} +676 -377
- package/dist/{chunk-7gmkgyaz.js → chunk-7qb9tq54.js} +324 -4
- package/dist/{chunk-d4y99pfy.js → chunk-7s1f4z4g.js} +4 -3
- package/dist/chunk-b1h374jr.js +159 -0
- package/dist/{chunk-fgx2sh7d.js → chunk-c8nxym2z.js} +1 -1
- package/dist/{chunk-akgxwg8f.js → chunk-d5ypmmr7.js} +29 -12
- package/dist/{chunk-374zqx5h.js → chunk-d93kvxvc.js} +18 -2
- package/dist/chunk-ggb1taqy.js +16 -0
- package/dist/chunk-jn7y815x.js +79 -0
- package/dist/{chunk-b3f0ced1.js → chunk-k2s1hfq5.js} +39 -16
- package/dist/chunk-kzbzacz9.js +12 -0
- package/dist/{chunk-myjp910s.js → chunk-n4tjhcb7.js} +25 -21
- package/dist/{chunk-cjfmfyjb.js → chunk-ngf1zatm.js} +7 -6
- package/dist/{chunk-vw5yb5pd.js → chunk-wqvp1fwh.js} +304 -38
- package/dist/{chunk-5785kwej.js → chunk-ywmm63ks.js} +9 -7
- package/dist/chunk-znhn8af7.js +33 -0
- package/dist/pinomad.js +40 -16
- package/package.json +1 -1
- package/web/apple-touch-icon.png +0 -0
- package/web/assets/index-C_aU_ZiV.js +383 -0
- package/web/assets/index-DaPcXZPM.css +1 -0
- package/web/favicon.svg +1 -0
- package/web/index.html +4 -2
- package/dist/chunk-17p6bfvy.js +0 -63
- package/dist/chunk-302y6by9.js +0 -50
- package/dist/chunk-knaw1a9g.js +0 -182
- package/dist/chunk-prhvksh3.js +0 -14
- package/dist/chunk-xrvmnymc.js +0 -33
- package/web/assets/index-8mNNPXPT.js +0 -383
- package/web/assets/index-BFJJgGGo.css +0 -1
package/dist/chunk-302y6by9.js
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
// apps/host/src/server-config.ts
|
|
2
|
-
import { randomBytes } from "node:crypto";
|
|
3
|
-
import { readFile, rename, rm, writeFile } from "node:fs/promises";
|
|
4
|
-
import { join } from "node:path";
|
|
5
|
-
var SERVER_CONFIG_FILE = "server.json";
|
|
6
|
-
var normalizeOrigin = (origin) => origin.replace(/\/+$/, "");
|
|
7
|
-
async function readServerConfig(dataDir) {
|
|
8
|
-
let text;
|
|
9
|
-
try {
|
|
10
|
-
text = await readFile(join(dataDir, SERVER_CONFIG_FILE), "utf8");
|
|
11
|
-
} catch (error) {
|
|
12
|
-
if (error.code === "ENOENT")
|
|
13
|
-
return;
|
|
14
|
-
throw error;
|
|
15
|
-
}
|
|
16
|
-
let value;
|
|
17
|
-
try {
|
|
18
|
-
value = JSON.parse(text);
|
|
19
|
-
} catch {
|
|
20
|
-
throw new Error(`${join(dataDir, SERVER_CONFIG_FILE)} is not valid JSON — delete it or run \`pinomad leave\``);
|
|
21
|
-
}
|
|
22
|
-
const origin = value?.origin;
|
|
23
|
-
if (typeof origin !== "string" || origin.trim() === "") {
|
|
24
|
-
throw new Error(`${join(dataDir, SERVER_CONFIG_FILE)} is missing a string "origin"`);
|
|
25
|
-
}
|
|
26
|
-
const binding = value.binding;
|
|
27
|
-
return {
|
|
28
|
-
origin,
|
|
29
|
-
...typeof binding === "string" && binding !== "" ? { binding } : {}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
async function writeServerConfig(dataDir, config) {
|
|
33
|
-
const path = join(dataDir, SERVER_CONFIG_FILE);
|
|
34
|
-
const tmp = `${path}.tmp-${process.pid}`;
|
|
35
|
-
await writeFile(tmp, `${JSON.stringify({ origin: normalizeOrigin(config.origin), binding: randomBytes(16).toString("base64url") })}
|
|
36
|
-
`, { mode: 384 });
|
|
37
|
-
await rename(tmp, path);
|
|
38
|
-
}
|
|
39
|
-
var removeServerConfig = (dataDir) => rm(join(dataDir, SERVER_CONFIG_FILE), { force: true });
|
|
40
|
-
async function readServerBinding(dataDir) {
|
|
41
|
-
return (await readServerConfig(dataDir))?.binding;
|
|
42
|
-
}
|
|
43
|
-
function resolveRelayOrigin(flag, config) {
|
|
44
|
-
if (flag !== undefined && config !== undefined && normalizeOrigin(flag) !== normalizeOrigin(config.origin)) {
|
|
45
|
-
throw new Error(`--relay ${flag} disagrees with ${SERVER_CONFIG_FILE} (${config.origin}) — remove one`);
|
|
46
|
-
}
|
|
47
|
-
return flag ?? config?.origin;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export { normalizeOrigin, readServerConfig, writeServerConfig, removeServerConfig, readServerBinding, resolveRelayOrigin };
|
package/dist/chunk-knaw1a9g.js
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
RELAY_CLOSE,
|
|
3
|
-
decodeRelayToHost,
|
|
4
|
-
encodeRelayFrame,
|
|
5
|
-
relayHostId,
|
|
6
|
-
signRelayChallenge,
|
|
7
|
-
signRelayChallengeV2,
|
|
8
|
-
toBase64Url
|
|
9
|
-
} from "./chunk-7gmkgyaz.js";
|
|
10
|
-
|
|
11
|
-
// apps/host/src/relay-link.ts
|
|
12
|
-
import { WebSocket } from "ws";
|
|
13
|
-
function relayWsBase(origin) {
|
|
14
|
-
const normalized = origin.replace(/\/+$/, "");
|
|
15
|
-
if (normalized.startsWith("https://"))
|
|
16
|
-
return `wss://${normalized.slice("https://".length)}`;
|
|
17
|
-
if (normalized.startsWith("http://"))
|
|
18
|
-
return `ws://${normalized.slice("http://".length)}`;
|
|
19
|
-
throw new Error(`relay origin must be an http(s) URL: ${origin}`);
|
|
20
|
-
}
|
|
21
|
-
var REJECTION_REASONS = {
|
|
22
|
-
[RELAY_CLOSE.badSignature]: () => `the relay rejected this host's registration signature — a version mismatch?`,
|
|
23
|
-
[RELAY_CLOSE.hostNotAllowed]: (hostId) => `the server does not allow this host; join it to an account (\`pinomad join\`) or add --allow-host ${hostId} to the server's arguments`,
|
|
24
|
-
[RELAY_CLOSE.replaced]: () => `the relay replaced this control connection — another process registered the same hostId`
|
|
25
|
-
};
|
|
26
|
-
function startRelayLink(options) {
|
|
27
|
-
const origin = options.origin.replace(/\/+$/, "");
|
|
28
|
-
const wsBase = relayWsBase(origin);
|
|
29
|
-
const hostId = relayHostId(options.signingKey.publicKey);
|
|
30
|
-
const minDelay = options.reconnectDelayMs?.min ?? 1000;
|
|
31
|
-
const maxDelay = options.reconnectDelayMs?.max ?? 30000;
|
|
32
|
-
const maxPendingAccepts = options.maxPendingAccepts ?? 16;
|
|
33
|
-
let state = { status: "connecting" };
|
|
34
|
-
let stopped = false;
|
|
35
|
-
let control;
|
|
36
|
-
let delay = minDelay;
|
|
37
|
-
let retryTimer;
|
|
38
|
-
let pendingAccepts = 0;
|
|
39
|
-
function setState(next) {
|
|
40
|
-
const changed = state.status !== next.status || state.status === "rejected" && next.status === "rejected" && state.reason !== next.reason;
|
|
41
|
-
state = next;
|
|
42
|
-
if (changed)
|
|
43
|
-
options.onStateChange?.(next);
|
|
44
|
-
}
|
|
45
|
-
const watched = new Set;
|
|
46
|
-
const ponged = new WeakMap;
|
|
47
|
-
const pingIntervalMs = options.pingIntervalMs ?? 30000;
|
|
48
|
-
const pingTimer = setInterval(() => {
|
|
49
|
-
for (const socket of watched) {
|
|
50
|
-
if (socket.readyState !== WebSocket.OPEN)
|
|
51
|
-
continue;
|
|
52
|
-
if (ponged.get(socket) === true) {
|
|
53
|
-
ponged.set(socket, false);
|
|
54
|
-
socket.ping();
|
|
55
|
-
} else {
|
|
56
|
-
socket.terminate();
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}, pingIntervalMs);
|
|
60
|
-
function watch(socket) {
|
|
61
|
-
watched.add(socket);
|
|
62
|
-
ponged.set(socket, true);
|
|
63
|
-
socket.on("pong", () => ponged.set(socket, true));
|
|
64
|
-
socket.once("close", () => watched.delete(socket));
|
|
65
|
-
socket.on("error", () => socket.terminate());
|
|
66
|
-
}
|
|
67
|
-
function connectControl() {
|
|
68
|
-
if (stopped)
|
|
69
|
-
return;
|
|
70
|
-
const socket = new WebSocket(`${wsBase}/host`, { handshakeTimeout: pingIntervalMs });
|
|
71
|
-
control = socket;
|
|
72
|
-
watch(socket);
|
|
73
|
-
socket.on("message", (data, isBinary) => onControlMessage(socket, data, isBinary));
|
|
74
|
-
socket.once("close", onControlClosed);
|
|
75
|
-
}
|
|
76
|
-
function reconnect(at) {
|
|
77
|
-
if (stopped || retryTimer !== undefined)
|
|
78
|
-
return;
|
|
79
|
-
retryTimer = setTimeout(() => {
|
|
80
|
-
retryTimer = undefined;
|
|
81
|
-
connectControl();
|
|
82
|
-
}, at);
|
|
83
|
-
}
|
|
84
|
-
function onControlMessage(socket, data, isBinary) {
|
|
85
|
-
let frame;
|
|
86
|
-
try {
|
|
87
|
-
if (isBinary)
|
|
88
|
-
throw new Error("binary control frame");
|
|
89
|
-
frame = decodeRelayToHost(data.toString());
|
|
90
|
-
} catch {
|
|
91
|
-
socket.close();
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
switch (frame.t) {
|
|
95
|
-
case "challenge": {
|
|
96
|
-
const noisePublicKey = toBase64Url(options.noisePublicKey);
|
|
97
|
-
const challenge = { relayOrigin: origin, hostId, nonce: frame.nonce };
|
|
98
|
-
socket.send(encodeRelayFrame({
|
|
99
|
-
t: "register",
|
|
100
|
-
hostId,
|
|
101
|
-
signature: signRelayChallenge(options.signingKey.secretKey, challenge),
|
|
102
|
-
signatureV2: signRelayChallengeV2(options.signingKey.secretKey, { ...challenge, noisePublicKey }),
|
|
103
|
-
noisePublicKey,
|
|
104
|
-
...options.accountOffer === undefined ? {} : { features: ["pairing"] }
|
|
105
|
-
}));
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
case "registered":
|
|
109
|
-
delay = minDelay;
|
|
110
|
-
setState({ status: "registered" });
|
|
111
|
-
break;
|
|
112
|
-
case "pairing-request": {
|
|
113
|
-
const accountOffer = options.accountOffer;
|
|
114
|
-
const refused = (reason) => {
|
|
115
|
-
socket.send(encodeRelayFrame({ t: "pairing-refused", requestId: frame.requestId, reason }));
|
|
116
|
-
};
|
|
117
|
-
if (accountOffer === undefined) {
|
|
118
|
-
refused("unavailable");
|
|
119
|
-
break;
|
|
120
|
-
}
|
|
121
|
-
accountOffer().then((result) => {
|
|
122
|
-
if (socket !== control || socket.readyState !== WebSocket.OPEN)
|
|
123
|
-
return;
|
|
124
|
-
socket.send(encodeRelayFrame(result.ok ? { t: "pairing", requestId: frame.requestId, secret: result.secret, expiresAt: result.expiresAt } : { t: "pairing-refused", requestId: frame.requestId, reason: result.reason }));
|
|
125
|
-
}, () => {
|
|
126
|
-
if (socket === control && socket.readyState === WebSocket.OPEN)
|
|
127
|
-
refused("error");
|
|
128
|
-
});
|
|
129
|
-
break;
|
|
130
|
-
}
|
|
131
|
-
case "incoming": {
|
|
132
|
-
if (pendingAccepts >= maxPendingAccepts)
|
|
133
|
-
break;
|
|
134
|
-
pendingAccepts += 1;
|
|
135
|
-
const data2 = new WebSocket(`${wsBase}/accept/${frame.connId}`, { handshakeTimeout: pingIntervalMs });
|
|
136
|
-
watch(data2);
|
|
137
|
-
const release = () => {
|
|
138
|
-
pendingAccepts -= 1;
|
|
139
|
-
clearTimeout(ageOut);
|
|
140
|
-
data2.off("close", release);
|
|
141
|
-
};
|
|
142
|
-
const ageOut = setTimeout(release, 1e4);
|
|
143
|
-
data2.once("close", release);
|
|
144
|
-
options.accept(data2);
|
|
145
|
-
break;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function onControlClosed(code) {
|
|
150
|
-
control = undefined;
|
|
151
|
-
if (stopped)
|
|
152
|
-
return;
|
|
153
|
-
const rejection = REJECTION_REASONS[code];
|
|
154
|
-
if (rejection !== undefined) {
|
|
155
|
-
const reason = `relay ${origin}: ${rejection(hostId)} (${code})`;
|
|
156
|
-
setState({ status: "rejected", reason });
|
|
157
|
-
delay = maxDelay;
|
|
158
|
-
reconnect(maxDelay);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
setState({ status: "connecting" });
|
|
162
|
-
reconnect(delay);
|
|
163
|
-
delay = Math.min(maxDelay, delay * 2);
|
|
164
|
-
}
|
|
165
|
-
connectControl();
|
|
166
|
-
return {
|
|
167
|
-
hostId,
|
|
168
|
-
origin,
|
|
169
|
-
state: () => state,
|
|
170
|
-
close() {
|
|
171
|
-
stopped = true;
|
|
172
|
-
if (retryTimer !== undefined)
|
|
173
|
-
clearTimeout(retryTimer);
|
|
174
|
-
clearInterval(pingTimer);
|
|
175
|
-
for (const socket of watched)
|
|
176
|
-
socket.terminate();
|
|
177
|
-
return Promise.resolve();
|
|
178
|
-
}
|
|
179
|
-
};
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
export { relayWsBase, startRelayLink };
|
package/dist/chunk-prhvksh3.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
main,
|
|
4
|
-
restartService
|
|
5
|
-
} from "./chunk-akgxwg8f.js";
|
|
6
|
-
import"./chunk-fgx2sh7d.js";
|
|
7
|
-
import"./chunk-374zqx5h.js";
|
|
8
|
-
import"./chunk-myjp910s.js";
|
|
9
|
-
import"./chunk-cjfmfyjb.js";
|
|
10
|
-
import"./chunk-df32avw6.js";
|
|
11
|
-
export {
|
|
12
|
-
restartService,
|
|
13
|
-
main
|
|
14
|
-
};
|
package/dist/chunk-xrvmnymc.js
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
loadRelayKey
|
|
3
|
-
} from "./chunk-837gh8nr.js";
|
|
4
|
-
import {
|
|
5
|
-
relayHostId
|
|
6
|
-
} from "./chunk-7gmkgyaz.js";
|
|
7
|
-
import {
|
|
8
|
-
DEFAULT_DATA_DIR
|
|
9
|
-
} from "./chunk-cjfmfyjb.js";
|
|
10
|
-
import"./chunk-df32avw6.js";
|
|
11
|
-
|
|
12
|
-
// apps/host/src/cli/relay-id.ts
|
|
13
|
-
import { mkdir } from "node:fs/promises";
|
|
14
|
-
import { resolve } from "node:path";
|
|
15
|
-
import { parseArgs } from "node:util";
|
|
16
|
-
async function relayId(argv) {
|
|
17
|
-
const { values } = parseArgs({
|
|
18
|
-
args: [...argv],
|
|
19
|
-
options: { "data-dir": { type: "string" } }
|
|
20
|
-
});
|
|
21
|
-
const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
|
|
22
|
-
await mkdir(dataDir, { recursive: true });
|
|
23
|
-
return relayHostId((await loadRelayKey(dataDir)).publicKey);
|
|
24
|
-
}
|
|
25
|
-
async function main(argv) {
|
|
26
|
-
console.log(await relayId(argv));
|
|
27
|
-
}
|
|
28
|
-
if (false)
|
|
29
|
-
;
|
|
30
|
-
export {
|
|
31
|
-
relayId,
|
|
32
|
-
main
|
|
33
|
-
};
|