pinomad 0.4.0 → 0.5.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-fgx2sh7d.js → chunk-1d9sppj0.js} +1 -1
- package/dist/{chunk-5785kwej.js → chunk-26d04z02.js} +3 -5
- package/dist/{chunk-vw5yb5pd.js → chunk-4by4x5c4.js} +290 -37
- package/dist/{chunk-prhvksh3.js → chunk-5ng86fmy.js} +4 -4
- package/dist/{chunk-b3f0ced1.js → chunk-9d59jjmt.js} +11 -14
- package/dist/{chunk-akgxwg8f.js → chunk-aqj4wg33.js} +2 -2
- package/dist/{chunk-17p6bfvy.js → chunk-crv3dqzp.js} +4 -4
- package/dist/{chunk-z5pmcscx.js → chunk-d4wdt5qs.js} +594 -212
- package/dist/{chunk-837gh8nr.js → chunk-ebfaf3k2.js} +69 -17
- package/dist/{chunk-xhwkx9bd.js → chunk-f0v3sht0.js} +11 -14
- package/dist/{chunk-n73t5xzw.js → chunk-gqr7ahv4.js} +1 -1
- package/dist/{chunk-myjp910s.js → chunk-hsp12cjt.js} +10 -6
- package/dist/{chunk-7gmkgyaz.js → chunk-kmbh44dt.js} +286 -2
- package/dist/{chunk-374zqx5h.js → chunk-m4q9wafw.js} +14 -1
- package/dist/{chunk-d4y99pfy.js → chunk-zttf1hvh.js} +2 -2
- package/dist/pinomad.js +14 -14
- package/package.json +1 -1
- package/web/apple-touch-icon.png +0 -0
- package/web/assets/index-BkF0-im5.css +1 -0
- package/web/assets/{index-8mNNPXPT.js → index-D4lgm3Y4.js} +89 -89
- package/web/favicon.svg +1 -0
- package/web/index.html +4 -2
- package/dist/chunk-302y6by9.js +0 -50
- package/dist/chunk-knaw1a9g.js +0 -182
- package/dist/chunk-xrvmnymc.js +0 -33
- package/web/assets/index-BFJJgGGo.css +0 -1
package/web/favicon.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><style>.bg{fill:#F7F5F0}.body{fill:#FFF;stroke:#151515}.ink{fill:#151515}@media(prefers-color-scheme:dark){.bg,.body{fill:#181818}.body{stroke:#FFF}.ink{fill:#FFF}}</style><rect class="bg" width="1024" height="1024" rx="224"/><g transform="translate(128 170) scale(2.7) translate(-148 -148)"><path class="body" d="M 368 244 C 384 278 372 326 348 353 C 322 382 287 398 255 396 C 234 395 221 387 228 368 C 230 362 223 365 215 366 C 185 369 165 359 159 346 C 154 337 157 337 168 335 C 197 332 211 308 221 282 C 231 254 253 244 283 233 C 318 219 350 212 362 232 Z" stroke-width="13" stroke-linejoin="round"/><path class="ink" d="M 214 234 C 213 199 224 176 246 165 C 267 153 282 147 296 149 C 317 151 333 172 343 194 C 300 201 254 226 214 234 Z"/><path class="ink" d="M 195 239 C 229 241 262 226 306 209 C 347 193 385 177 410 185 C 437 191 438 210 425 229 C 414 244 397 257 377 267 C 373 246 365 227 350 223 C 331 217 311 226 285 236 C 255 246 238 251 227 271 C 216 291 215 305 200 312 C 172 311 153 302 149 287 C 142 264 164 244 195 239 Z"/><ellipse class="ink" cx="304" cy="272" rx="9.5" ry="15.5"/><ellipse class="ink" cx="343" cy="269" rx="9.5" ry="15.5"/></g></svg>
|
package/web/index.html
CHANGED
|
@@ -3,9 +3,11 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
7
|
+
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
|
6
8
|
<title>PiNomad</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-D4lgm3Y4.js"></script>
|
|
10
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BkF0-im5.css">
|
|
9
11
|
</head>
|
|
10
12
|
<body>
|
|
11
13
|
<div id="root"></div>
|
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-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
|
-
};
|