pinomad 0.10.0 → 0.11.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 +11 -9
- package/dist/{chunk-v414b5pp.js → chunk-44pnts98.js} +2 -2
- package/dist/{chunk-5791mb0b.js → chunk-4dcs8sbj.js} +11 -9
- package/dist/{chunk-czj9nfrz.js → chunk-67gq282t.js} +15 -13
- package/dist/{chunk-73efzaf1.js → chunk-6d16c2fv.js} +21 -11
- package/dist/{chunk-p978qj9m.js → chunk-9h5vawnm.js} +351 -35
- package/dist/{chunk-c5p9xkd8.js → chunk-as6pyf00.js} +3 -3
- package/dist/chunk-b457q0tm.js +70 -0
- package/dist/chunk-etf7ef97.js +55 -0
- package/dist/chunk-greb3dp6.js +22 -0
- package/dist/chunk-gtdpjqa3.js +317 -0
- package/dist/chunk-h5q2y57m.js +20 -0
- package/dist/{chunk-4sepcfpy.js → chunk-jt34g8st.js} +2 -2
- package/dist/chunk-n1m1tjrk.js +214 -0
- package/dist/{chunk-4h7pkkzr.js → chunk-ncbqa8jk.js} +3 -3
- package/dist/{chunk-sygeb9qb.js → chunk-pqe7q1s7.js} +1 -1
- package/dist/{chunk-zehwx009.js → chunk-sd5fkxjj.js} +1 -1
- package/dist/{chunk-8gywfw5n.js → chunk-t91zt5f0.js} +2 -51
- package/dist/{chunk-vax4zspw.js → chunk-tj57aqav.js} +3 -3
- package/dist/{chunk-w2m9xkks.js → chunk-vvj9b1gc.js} +29 -24
- package/dist/{chunk-jgd38rna.js → chunk-xqycs43g.js} +171 -171
- package/dist/{chunk-cx8k63by.js → chunk-y127fe1a.js} +1 -1
- package/dist/pinomad.js +17 -11
- package/package.json +1 -1
- package/web/assets/index-BhHrKpoo.js +395 -0
- package/web/index.html +1 -1
- package/dist/chunk-bhtx2c8a.js +0 -16
- package/dist/chunk-c05g401k.js +0 -170
- package/web/assets/index-BdBjP1qJ.js +0 -395
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
generateKeyPair,
|
|
4
4
|
keyPairFromPrivate,
|
|
5
5
|
toBase64Url
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-pqe7q1s7.js";
|
|
7
7
|
|
|
8
8
|
// apps/host/src/devices.ts
|
|
9
9
|
import { link, readFile, unlink, writeFile } from "node:fs/promises";
|
|
@@ -140,53 +140,4 @@ function pairingOffers(ttlMs = 5 * 60 * 1000) {
|
|
|
140
140
|
};
|
|
141
141
|
}
|
|
142
142
|
|
|
143
|
-
|
|
144
|
-
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
145
|
-
import { readFile as readFile2, rename, rm, writeFile as writeFile2 } from "node:fs/promises";
|
|
146
|
-
import { join as join2 } from "node:path";
|
|
147
|
-
var SERVER_CONFIG_FILE = "server.json";
|
|
148
|
-
var normalizeOrigin = (origin) => origin.replace(/\/+$/, "");
|
|
149
|
-
async function readServerConfig(dataDir) {
|
|
150
|
-
let text;
|
|
151
|
-
try {
|
|
152
|
-
text = await readFile2(join2(dataDir, SERVER_CONFIG_FILE), "utf8");
|
|
153
|
-
} catch (error) {
|
|
154
|
-
if (error.code === "ENOENT")
|
|
155
|
-
return;
|
|
156
|
-
throw error;
|
|
157
|
-
}
|
|
158
|
-
let value;
|
|
159
|
-
try {
|
|
160
|
-
value = JSON.parse(text);
|
|
161
|
-
} catch {
|
|
162
|
-
throw new Error(`${join2(dataDir, SERVER_CONFIG_FILE)} is not valid JSON — delete it or run \`pinomad leave\``);
|
|
163
|
-
}
|
|
164
|
-
const origin = value?.origin;
|
|
165
|
-
if (typeof origin !== "string" || origin.trim() === "") {
|
|
166
|
-
throw new Error(`${join2(dataDir, SERVER_CONFIG_FILE)} is missing a string "origin"`);
|
|
167
|
-
}
|
|
168
|
-
const binding = value.binding;
|
|
169
|
-
return {
|
|
170
|
-
origin,
|
|
171
|
-
...typeof binding === "string" && binding !== "" ? { binding } : {}
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
async function writeServerConfig(dataDir, config) {
|
|
175
|
-
const path = join2(dataDir, SERVER_CONFIG_FILE);
|
|
176
|
-
const tmp = `${path}.tmp-${process.pid}`;
|
|
177
|
-
await writeFile2(tmp, `${JSON.stringify({ origin: normalizeOrigin(config.origin), binding: randomBytes2(16).toString("base64url") })}
|
|
178
|
-
`, { mode: 384 });
|
|
179
|
-
await rename(tmp, path);
|
|
180
|
-
}
|
|
181
|
-
var removeServerConfig = (dataDir) => rm(join2(dataDir, SERVER_CONFIG_FILE), { force: true });
|
|
182
|
-
async function readServerBinding(dataDir) {
|
|
183
|
-
return (await readServerConfig(dataDir))?.binding;
|
|
184
|
-
}
|
|
185
|
-
function resolveRelayOrigin(flag, config) {
|
|
186
|
-
if (flag !== undefined && config !== undefined && normalizeOrigin(flag) !== normalizeOrigin(config.origin)) {
|
|
187
|
-
throw new Error(`--relay ${flag} disagrees with ${SERVER_CONFIG_FILE} (${config.origin}) — remove one`);
|
|
188
|
-
}
|
|
189
|
-
return flag ?? config?.origin;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
export { DevicesDoc, ensureDevices, RevocationsDoc, admitDevice, loadHostKey, loadRelayKey, pairingOffers, normalizeOrigin, readServerConfig, writeServerConfig, removeServerConfig, readServerBinding, resolveRelayOrigin };
|
|
143
|
+
export { DevicesDoc, ensureDevices, RevocationsDoc, admitDevice, loadHostKey, loadRelayKey, pairingOffers };
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
connectRemoteDurable
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import"./chunk-
|
|
3
|
+
} from "./chunk-jt34g8st.js";
|
|
4
|
+
import"./chunk-xqycs43g.js";
|
|
5
5
|
import {
|
|
6
6
|
isLoopbackUrl
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-pqe7q1s7.js";
|
|
8
8
|
import {
|
|
9
9
|
hostAddress
|
|
10
10
|
} from "./chunk-ngf1zatm.js";
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
isParseArgsError
|
|
3
|
-
} from "./chunk-kzbzacz9.js";
|
|
4
|
-
import {
|
|
5
|
-
commandHint,
|
|
6
|
-
hostEntry,
|
|
7
|
-
packagedVersion
|
|
8
|
-
} from "./chunk-zehwx009.js";
|
|
9
1
|
import {
|
|
10
2
|
ProtocolMismatchError,
|
|
11
3
|
connectRemoteDurable
|
|
12
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-jt34g8st.js";
|
|
5
|
+
import {
|
|
6
|
+
isParseArgsError
|
|
7
|
+
} from "./chunk-kzbzacz9.js";
|
|
13
8
|
import {
|
|
14
9
|
DEFAULT_DATA_DIR,
|
|
15
10
|
hostAddress
|
|
16
11
|
} from "./chunk-ngf1zatm.js";
|
|
12
|
+
import {
|
|
13
|
+
commandHint,
|
|
14
|
+
hostEntry,
|
|
15
|
+
packagedVersion
|
|
16
|
+
} from "./chunk-sd5fkxjj.js";
|
|
17
17
|
|
|
18
18
|
// apps/host/src/cli/service.ts
|
|
19
19
|
import { execFile as execFile2 } from "node:child_process";
|
|
@@ -81,20 +81,21 @@ function argValue(args, flag) {
|
|
|
81
81
|
}
|
|
82
82
|
return value;
|
|
83
83
|
}
|
|
84
|
-
function
|
|
84
|
+
function withoutArg(args, flag) {
|
|
85
85
|
const out = [];
|
|
86
86
|
for (let i = 0;i < args.length; i += 1) {
|
|
87
87
|
const arg = args[i];
|
|
88
|
-
if (arg ===
|
|
88
|
+
if (arg === flag) {
|
|
89
89
|
i += 1;
|
|
90
90
|
continue;
|
|
91
91
|
}
|
|
92
|
-
if (arg.startsWith(
|
|
92
|
+
if (arg.startsWith(`${flag}=`))
|
|
93
93
|
continue;
|
|
94
94
|
out.push(arg);
|
|
95
95
|
}
|
|
96
96
|
return out;
|
|
97
97
|
}
|
|
98
|
+
var withoutRelayArgs = (args) => withoutArg(args, "--relay");
|
|
98
99
|
var unescapeExec = (value) => value.replace(/\\(.)/g, "$1");
|
|
99
100
|
var unspecify = (value) => value.replace(/%%/g, "%").replace(/\$\$/g, "$");
|
|
100
101
|
var xmlUnescape = (value) => value.replace(/"/g, '"').replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&");
|
|
@@ -409,15 +410,13 @@ async function install(argv) {
|
|
|
409
410
|
console.log(`data dir: ${spec.dataDir}`);
|
|
410
411
|
console.log(`browser link: ${commandHint("link")} pairing QR: ${commandHint("pair")}`);
|
|
411
412
|
}
|
|
412
|
-
async function
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
console.log(`version: ${packagedVersion}`);
|
|
420
|
-
}
|
|
413
|
+
async function versionLine() {
|
|
414
|
+
if (packagedVersion !== undefined)
|
|
415
|
+
return `version: ${packagedVersion}`;
|
|
416
|
+
const head = await run2("git", ["rev-parse", "--short", "HEAD"], { cwd: repoRoot }).then(({ stdout }) => stdout.trim(), () => "unknown");
|
|
417
|
+
return `checkout: ${head}`;
|
|
418
|
+
}
|
|
419
|
+
async function reachabilityLine(argv) {
|
|
421
420
|
try {
|
|
422
421
|
const { url, token } = await hostAddress(argv);
|
|
423
422
|
const remote = await connectToHost(url, token);
|
|
@@ -425,14 +424,20 @@ async function status(argv) {
|
|
|
425
424
|
await remote.controller.toggleTasks();
|
|
426
425
|
const tasks = remote.view.current().tasks;
|
|
427
426
|
const count = tasks === undefined ? 0 : Object.keys(tasks.tasks).length;
|
|
428
|
-
console.log(`host: reachable at ${url}, ${count} live task${count === 1 ? "" : "s"}`);
|
|
429
427
|
remote.close();
|
|
428
|
+
return `host: reachable at ${url}, ${count} live task${count === 1 ? "" : "s"}`;
|
|
430
429
|
} catch (error) {
|
|
431
430
|
if (isParseArgsError(error))
|
|
432
431
|
throw error;
|
|
433
|
-
|
|
432
|
+
return `host: unreachable (${message(error)})`;
|
|
434
433
|
}
|
|
435
434
|
}
|
|
435
|
+
async function status(argv) {
|
|
436
|
+
const state = await serviceManager().state();
|
|
437
|
+
console.log(`service: ${state.installed ? state.detail : "not installed"}`);
|
|
438
|
+
console.log(await versionLine());
|
|
439
|
+
console.log(await reachabilityLine(argv));
|
|
440
|
+
}
|
|
436
441
|
async function restartService({
|
|
437
442
|
force,
|
|
438
443
|
argv,
|
|
@@ -536,4 +541,4 @@ async function main(argv) {
|
|
|
536
541
|
}
|
|
537
542
|
if (false) {}
|
|
538
543
|
|
|
539
|
-
export { installSpec, argValue, withoutRelayArgs, serviceManager, restartService, main };
|
|
544
|
+
export { installSpec, argValue, withoutArg, withoutRelayArgs, serviceManager, versionLine, reachabilityLine, restartService, main };
|
|
@@ -2,7 +2,176 @@ import {
|
|
|
2
2
|
HOST_SETTING_KEYS,
|
|
3
3
|
isQueueMode,
|
|
4
4
|
isThinkingLevel
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-pqe7q1s7.js";
|
|
6
|
+
|
|
7
|
+
// packages/protocol/src/rtc.ts
|
|
8
|
+
function rtcAddressScope(address) {
|
|
9
|
+
const ip = parseRtcAddress(address);
|
|
10
|
+
if (ip === undefined)
|
|
11
|
+
return "unknown";
|
|
12
|
+
if (ip.family === 4)
|
|
13
|
+
return ipv4Scope(ip.octets);
|
|
14
|
+
const octets = mappedIpv4(ip.segments);
|
|
15
|
+
return octets === undefined ? ipv6Scope(ip.segments) : ipv4Scope(octets);
|
|
16
|
+
}
|
|
17
|
+
function rtcCandidateEnd(info) {
|
|
18
|
+
return {
|
|
19
|
+
...info.type === undefined ? {} : { type: info.type },
|
|
20
|
+
...info.address === undefined ? {} : { address: info.address },
|
|
21
|
+
scope: rtcAddressScope(info.address)
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
function rtcSelectedPair(local, remote) {
|
|
25
|
+
return { local: rtcCandidateEnd(local), remote: rtcCandidateEnd(remote) };
|
|
26
|
+
}
|
|
27
|
+
function parseRtcAddress(address) {
|
|
28
|
+
if (address === undefined)
|
|
29
|
+
return;
|
|
30
|
+
const text = address.trim();
|
|
31
|
+
if (text === "")
|
|
32
|
+
return;
|
|
33
|
+
let host = text;
|
|
34
|
+
if (text.startsWith("[")) {
|
|
35
|
+
const close = text.indexOf("]");
|
|
36
|
+
if (close === -1)
|
|
37
|
+
return;
|
|
38
|
+
const tail = text.slice(close + 1);
|
|
39
|
+
if (tail !== "" && !/^:\d+$/.test(tail))
|
|
40
|
+
return;
|
|
41
|
+
host = text.slice(1, close);
|
|
42
|
+
} else if (text.split(":").length === 2) {
|
|
43
|
+
const [candidate, port] = text.split(":");
|
|
44
|
+
if (/^\d+$/.test(port))
|
|
45
|
+
host = candidate;
|
|
46
|
+
}
|
|
47
|
+
const zone = host.indexOf("%");
|
|
48
|
+
if (zone !== -1)
|
|
49
|
+
host = host.slice(0, zone);
|
|
50
|
+
const ipv4 = parseIpv4(host);
|
|
51
|
+
if (ipv4 !== undefined)
|
|
52
|
+
return { family: 4, octets: ipv4 };
|
|
53
|
+
const ipv6 = parseIpv6(host);
|
|
54
|
+
return ipv6 === undefined ? undefined : { family: 6, segments: ipv6 };
|
|
55
|
+
}
|
|
56
|
+
function parseIpv4(text) {
|
|
57
|
+
const parts = text.split(".");
|
|
58
|
+
if (parts.length !== 4)
|
|
59
|
+
return;
|
|
60
|
+
const octets = [];
|
|
61
|
+
for (const part of parts) {
|
|
62
|
+
if (!/^(?:0|[1-9]\d{0,2})$/.test(part))
|
|
63
|
+
return;
|
|
64
|
+
const value = Number(part);
|
|
65
|
+
if (value > 255)
|
|
66
|
+
return;
|
|
67
|
+
octets.push(value);
|
|
68
|
+
}
|
|
69
|
+
return octets;
|
|
70
|
+
}
|
|
71
|
+
function parseIpv6(text) {
|
|
72
|
+
const halves = text.split("::");
|
|
73
|
+
if (halves.length > 2)
|
|
74
|
+
return;
|
|
75
|
+
const parsePart = (part, allowIpv4) => {
|
|
76
|
+
if (part === "")
|
|
77
|
+
return [];
|
|
78
|
+
const tokens = part.split(":");
|
|
79
|
+
const groups = [];
|
|
80
|
+
for (let index = 0;index < tokens.length; index++) {
|
|
81
|
+
const token = tokens[index];
|
|
82
|
+
if (allowIpv4 && index === tokens.length - 1 && token.includes(".")) {
|
|
83
|
+
const v4 = parseIpv4(token);
|
|
84
|
+
if (v4 === undefined)
|
|
85
|
+
return;
|
|
86
|
+
groups.push(v4[0] << 8 | v4[1], v4[2] << 8 | v4[3]);
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
if (!/^[0-9a-fA-F]{1,4}$/.test(token))
|
|
90
|
+
return;
|
|
91
|
+
groups.push(Number.parseInt(token, 16));
|
|
92
|
+
}
|
|
93
|
+
return groups;
|
|
94
|
+
};
|
|
95
|
+
if (halves.length === 1) {
|
|
96
|
+
const groups = parsePart(text, true);
|
|
97
|
+
return groups?.length === 8 ? groups : undefined;
|
|
98
|
+
}
|
|
99
|
+
const left = parsePart(halves[0], false);
|
|
100
|
+
const right = parsePart(halves[1], true);
|
|
101
|
+
if (left === undefined || right === undefined)
|
|
102
|
+
return;
|
|
103
|
+
const zeros = 8 - left.length - right.length;
|
|
104
|
+
if (zeros < 1)
|
|
105
|
+
return;
|
|
106
|
+
return [...left, ...new Array(zeros).fill(0), ...right];
|
|
107
|
+
}
|
|
108
|
+
function mappedIpv4(segments) {
|
|
109
|
+
if (!segments.slice(0, 5).every((group) => group === 0) || segments[5] !== 65535)
|
|
110
|
+
return;
|
|
111
|
+
return [segments[6] >> 8, segments[6] & 255, segments[7] >> 8, segments[7] & 255];
|
|
112
|
+
}
|
|
113
|
+
function ipv4Scope(octets) {
|
|
114
|
+
const [a, b] = octets;
|
|
115
|
+
if (a === 127)
|
|
116
|
+
return "loopback";
|
|
117
|
+
if (a === 100 && b >= 64 && b <= 127)
|
|
118
|
+
return "tailnet";
|
|
119
|
+
if (a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254) {
|
|
120
|
+
return "lan";
|
|
121
|
+
}
|
|
122
|
+
return "public";
|
|
123
|
+
}
|
|
124
|
+
function ipv6Scope(segments) {
|
|
125
|
+
if (segments[0] === 64890 && segments[1] === 4444 && segments[2] === 41440)
|
|
126
|
+
return "tailnet";
|
|
127
|
+
if ((segments[0] & 65024) === 64512 || (segments[0] & 65472) === 65152)
|
|
128
|
+
return "lan";
|
|
129
|
+
if (segments.slice(0, 7).every((group) => group === 0) && segments[7] === 1)
|
|
130
|
+
return "loopback";
|
|
131
|
+
return "public";
|
|
132
|
+
}
|
|
133
|
+
var RTC_DEFAULT_STUN = ["stun:stun.cloudflare.com:3478"];
|
|
134
|
+
var RTC_DATA_CHANNEL_LABEL = "pinomad";
|
|
135
|
+
var RTC_MAX_RECORD_BYTES = 65535;
|
|
136
|
+
var RTC_DEFAULT_MAX_MESSAGE_SIZE = 65536;
|
|
137
|
+
var RTC_MIN_MAX_MESSAGE_SIZE = RTC_MAX_RECORD_BYTES;
|
|
138
|
+
function isRtcHello(value) {
|
|
139
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
140
|
+
return false;
|
|
141
|
+
const hello = value;
|
|
142
|
+
return Array.isArray(hello.stun) && hello.stun.every((server) => typeof server === "string" && server.length > 0) && hello.signalOnly === true;
|
|
143
|
+
}
|
|
144
|
+
function rtcCandidateType(candidate) {
|
|
145
|
+
const parts = candidate.trim().split(/\s+/);
|
|
146
|
+
const at = parts.indexOf("typ");
|
|
147
|
+
return at === -1 ? undefined : parts[at + 1];
|
|
148
|
+
}
|
|
149
|
+
function sdpCandidates(sdp) {
|
|
150
|
+
return sdp.split(/\r?\n/).filter((line) => line.startsWith("a=candidate:")).map((line) => line.slice("a=".length));
|
|
151
|
+
}
|
|
152
|
+
function withRtcCandidates(sdp, candidates) {
|
|
153
|
+
const lines = sdp.split(/\r?\n/).filter((line) => line !== "" && !line.startsWith("a=candidate:") && line !== "a=end-of-candidates");
|
|
154
|
+
const unique = [...new Set(candidates.map((candidate) => candidate.replace(/^a=/, "")))];
|
|
155
|
+
return [...lines, ...unique.map((candidate) => `a=${candidate}`), "a=end-of-candidates", ""].join(`\r
|
|
156
|
+
`);
|
|
157
|
+
}
|
|
158
|
+
function parseRtcMaxMessageSize(sdp) {
|
|
159
|
+
const line = sdp.split(/\r?\n/).find((entry) => entry.startsWith("a=max-message-size:"));
|
|
160
|
+
if (line === undefined)
|
|
161
|
+
return { kind: "bounded", bytes: RTC_DEFAULT_MAX_MESSAGE_SIZE };
|
|
162
|
+
const raw = line.slice("a=max-message-size:".length).trim();
|
|
163
|
+
if (!/^\d+$/.test(raw))
|
|
164
|
+
return { kind: "invalid", reason: `malformed a=max-message-size: ${raw}` };
|
|
165
|
+
const bytes = Number(raw);
|
|
166
|
+
if (!Number.isSafeInteger(bytes))
|
|
167
|
+
return { kind: "invalid", reason: `a=max-message-size is not a safe integer: ${raw}` };
|
|
168
|
+
if (bytes === 0)
|
|
169
|
+
return { kind: "unlimited" };
|
|
170
|
+
if (bytes < RTC_MIN_MAX_MESSAGE_SIZE) {
|
|
171
|
+
return { kind: "invalid", reason: `a=max-message-size ${bytes} cannot carry a ${RTC_MIN_MAX_MESSAGE_SIZE}-byte record` };
|
|
172
|
+
}
|
|
173
|
+
return { kind: "bounded", bytes };
|
|
174
|
+
}
|
|
6
175
|
|
|
7
176
|
// packages/protocol/src/auth.ts
|
|
8
177
|
var CUSTOM_PROVIDER_APIS = ["openai-completions", "openai-responses", "anthropic-messages", "google-generative-ai"];
|
|
@@ -218,175 +387,6 @@ function nonempty(value) {
|
|
|
218
387
|
return typeof value === "string" && value.length > 0;
|
|
219
388
|
}
|
|
220
389
|
|
|
221
|
-
// packages/protocol/src/rtc.ts
|
|
222
|
-
function rtcAddressScope(address) {
|
|
223
|
-
const ip = parseRtcAddress(address);
|
|
224
|
-
if (ip === undefined)
|
|
225
|
-
return "unknown";
|
|
226
|
-
if (ip.family === 4)
|
|
227
|
-
return ipv4Scope(ip.octets);
|
|
228
|
-
const octets = mappedIpv4(ip.segments);
|
|
229
|
-
return octets === undefined ? ipv6Scope(ip.segments) : ipv4Scope(octets);
|
|
230
|
-
}
|
|
231
|
-
function rtcCandidateEnd(info) {
|
|
232
|
-
return {
|
|
233
|
-
...info.type === undefined ? {} : { type: info.type },
|
|
234
|
-
...info.address === undefined ? {} : { address: info.address },
|
|
235
|
-
scope: rtcAddressScope(info.address)
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
function rtcSelectedPair(local, remote) {
|
|
239
|
-
return { local: rtcCandidateEnd(local), remote: rtcCandidateEnd(remote) };
|
|
240
|
-
}
|
|
241
|
-
function parseRtcAddress(address) {
|
|
242
|
-
if (address === undefined)
|
|
243
|
-
return;
|
|
244
|
-
const text = address.trim();
|
|
245
|
-
if (text === "")
|
|
246
|
-
return;
|
|
247
|
-
let host = text;
|
|
248
|
-
if (text.startsWith("[")) {
|
|
249
|
-
const close = text.indexOf("]");
|
|
250
|
-
if (close === -1)
|
|
251
|
-
return;
|
|
252
|
-
const tail = text.slice(close + 1);
|
|
253
|
-
if (tail !== "" && !/^:\d+$/.test(tail))
|
|
254
|
-
return;
|
|
255
|
-
host = text.slice(1, close);
|
|
256
|
-
} else if (text.split(":").length === 2) {
|
|
257
|
-
const [candidate, port] = text.split(":");
|
|
258
|
-
if (/^\d+$/.test(port))
|
|
259
|
-
host = candidate;
|
|
260
|
-
}
|
|
261
|
-
const zone = host.indexOf("%");
|
|
262
|
-
if (zone !== -1)
|
|
263
|
-
host = host.slice(0, zone);
|
|
264
|
-
const ipv4 = parseIpv4(host);
|
|
265
|
-
if (ipv4 !== undefined)
|
|
266
|
-
return { family: 4, octets: ipv4 };
|
|
267
|
-
const ipv6 = parseIpv6(host);
|
|
268
|
-
return ipv6 === undefined ? undefined : { family: 6, segments: ipv6 };
|
|
269
|
-
}
|
|
270
|
-
function parseIpv4(text) {
|
|
271
|
-
const parts = text.split(".");
|
|
272
|
-
if (parts.length !== 4)
|
|
273
|
-
return;
|
|
274
|
-
const octets = [];
|
|
275
|
-
for (const part of parts) {
|
|
276
|
-
if (!/^(?:0|[1-9]\d{0,2})$/.test(part))
|
|
277
|
-
return;
|
|
278
|
-
const value = Number(part);
|
|
279
|
-
if (value > 255)
|
|
280
|
-
return;
|
|
281
|
-
octets.push(value);
|
|
282
|
-
}
|
|
283
|
-
return octets;
|
|
284
|
-
}
|
|
285
|
-
function parseIpv6(text) {
|
|
286
|
-
const halves = text.split("::");
|
|
287
|
-
if (halves.length > 2)
|
|
288
|
-
return;
|
|
289
|
-
const parsePart = (part, allowIpv4) => {
|
|
290
|
-
if (part === "")
|
|
291
|
-
return [];
|
|
292
|
-
const tokens = part.split(":");
|
|
293
|
-
const groups = [];
|
|
294
|
-
for (let index = 0;index < tokens.length; index++) {
|
|
295
|
-
const token = tokens[index];
|
|
296
|
-
if (allowIpv4 && index === tokens.length - 1 && token.includes(".")) {
|
|
297
|
-
const v4 = parseIpv4(token);
|
|
298
|
-
if (v4 === undefined)
|
|
299
|
-
return;
|
|
300
|
-
groups.push(v4[0] << 8 | v4[1], v4[2] << 8 | v4[3]);
|
|
301
|
-
continue;
|
|
302
|
-
}
|
|
303
|
-
if (!/^[0-9a-fA-F]{1,4}$/.test(token))
|
|
304
|
-
return;
|
|
305
|
-
groups.push(Number.parseInt(token, 16));
|
|
306
|
-
}
|
|
307
|
-
return groups;
|
|
308
|
-
};
|
|
309
|
-
if (halves.length === 1) {
|
|
310
|
-
const groups = parsePart(text, true);
|
|
311
|
-
return groups?.length === 8 ? groups : undefined;
|
|
312
|
-
}
|
|
313
|
-
const left = parsePart(halves[0], false);
|
|
314
|
-
const right = parsePart(halves[1], true);
|
|
315
|
-
if (left === undefined || right === undefined)
|
|
316
|
-
return;
|
|
317
|
-
const zeros = 8 - left.length - right.length;
|
|
318
|
-
if (zeros < 1)
|
|
319
|
-
return;
|
|
320
|
-
return [...left, ...new Array(zeros).fill(0), ...right];
|
|
321
|
-
}
|
|
322
|
-
function mappedIpv4(segments) {
|
|
323
|
-
if (!segments.slice(0, 5).every((group) => group === 0) || segments[5] !== 65535)
|
|
324
|
-
return;
|
|
325
|
-
return [segments[6] >> 8, segments[6] & 255, segments[7] >> 8, segments[7] & 255];
|
|
326
|
-
}
|
|
327
|
-
function ipv4Scope(octets) {
|
|
328
|
-
const [a, b] = octets;
|
|
329
|
-
if (a === 127)
|
|
330
|
-
return "loopback";
|
|
331
|
-
if (a === 100 && b >= 64 && b <= 127)
|
|
332
|
-
return "tailnet";
|
|
333
|
-
if (a === 10 || a === 172 && b >= 16 && b <= 31 || a === 192 && b === 168 || a === 169 && b === 254) {
|
|
334
|
-
return "lan";
|
|
335
|
-
}
|
|
336
|
-
return "public";
|
|
337
|
-
}
|
|
338
|
-
function ipv6Scope(segments) {
|
|
339
|
-
if (segments[0] === 64890 && segments[1] === 4444 && segments[2] === 41440)
|
|
340
|
-
return "tailnet";
|
|
341
|
-
if ((segments[0] & 65024) === 64512 || (segments[0] & 65472) === 65152)
|
|
342
|
-
return "lan";
|
|
343
|
-
if (segments.slice(0, 7).every((group) => group === 0) && segments[7] === 1)
|
|
344
|
-
return "loopback";
|
|
345
|
-
return "public";
|
|
346
|
-
}
|
|
347
|
-
var RTC_DEFAULT_STUN = ["stun:stun.cloudflare.com:3478"];
|
|
348
|
-
var RTC_DATA_CHANNEL_LABEL = "pinomad";
|
|
349
|
-
var RTC_MAX_RECORD_BYTES = 65535;
|
|
350
|
-
var RTC_DEFAULT_MAX_MESSAGE_SIZE = 65536;
|
|
351
|
-
var RTC_MIN_MAX_MESSAGE_SIZE = RTC_MAX_RECORD_BYTES;
|
|
352
|
-
function isRtcHello(value) {
|
|
353
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
354
|
-
return false;
|
|
355
|
-
const hello = value;
|
|
356
|
-
return Array.isArray(hello.stun) && hello.stun.every((server) => typeof server === "string" && server.length > 0) && hello.signalOnly === true;
|
|
357
|
-
}
|
|
358
|
-
function rtcCandidateType(candidate) {
|
|
359
|
-
const parts = candidate.trim().split(/\s+/);
|
|
360
|
-
const at = parts.indexOf("typ");
|
|
361
|
-
return at === -1 ? undefined : parts[at + 1];
|
|
362
|
-
}
|
|
363
|
-
function sdpCandidates(sdp) {
|
|
364
|
-
return sdp.split(/\r?\n/).filter((line) => line.startsWith("a=candidate:")).map((line) => line.slice("a=".length));
|
|
365
|
-
}
|
|
366
|
-
function withRtcCandidates(sdp, candidates) {
|
|
367
|
-
const lines = sdp.split(/\r?\n/).filter((line) => line !== "" && !line.startsWith("a=candidate:") && line !== "a=end-of-candidates");
|
|
368
|
-
const unique = [...new Set(candidates.map((candidate) => candidate.replace(/^a=/, "")))];
|
|
369
|
-
return [...lines, ...unique.map((candidate) => `a=${candidate}`), "a=end-of-candidates", ""].join(`\r
|
|
370
|
-
`);
|
|
371
|
-
}
|
|
372
|
-
function parseRtcMaxMessageSize(sdp) {
|
|
373
|
-
const line = sdp.split(/\r?\n/).find((entry) => entry.startsWith("a=max-message-size:"));
|
|
374
|
-
if (line === undefined)
|
|
375
|
-
return { kind: "bounded", bytes: RTC_DEFAULT_MAX_MESSAGE_SIZE };
|
|
376
|
-
const raw = line.slice("a=max-message-size:".length).trim();
|
|
377
|
-
if (!/^\d+$/.test(raw))
|
|
378
|
-
return { kind: "invalid", reason: `malformed a=max-message-size: ${raw}` };
|
|
379
|
-
const bytes = Number(raw);
|
|
380
|
-
if (!Number.isSafeInteger(bytes))
|
|
381
|
-
return { kind: "invalid", reason: `a=max-message-size is not a safe integer: ${raw}` };
|
|
382
|
-
if (bytes === 0)
|
|
383
|
-
return { kind: "unlimited" };
|
|
384
|
-
if (bytes < RTC_MIN_MAX_MESSAGE_SIZE) {
|
|
385
|
-
return { kind: "invalid", reason: `a=max-message-size ${bytes} cannot carry a ${RTC_MIN_MAX_MESSAGE_SIZE}-byte record` };
|
|
386
|
-
}
|
|
387
|
-
return { kind: "bounded", bytes };
|
|
388
|
-
}
|
|
389
|
-
|
|
390
390
|
// packages/protocol/src/attachments.ts
|
|
391
391
|
var MAX_ATTACHMENT_BYTES = 10 * 1024 * 1024;
|
|
392
392
|
var MAX_ATTACHMENTS_PER_MESSAGE = 10;
|
|
@@ -619,4 +619,4 @@ function homeOf(index, summaries, id) {
|
|
|
619
619
|
return;
|
|
620
620
|
}
|
|
621
621
|
|
|
622
|
-
export {
|
|
622
|
+
export { rtcSelectedPair, RTC_DEFAULT_STUN, RTC_DATA_CHANNEL_LABEL, RTC_MAX_RECORD_BYTES, isRtcHello, rtcCandidateType, sdpCandidates, withRtcCandidates, parseRtcMaxMessageSize, isCustomProviderApi, HOST_STREAM_NAMES, hostStreams, conversationStream, docStream, UNAUTHORIZED_CLOSE_CODE, INVALID_FRAME_CLOSE_CODE, PROTOCOL, hostSupportsCall, hostSupportsFeature, parseClientFrame, MAX_ATTACHMENTS_PER_MESSAGE, MAX_PENDING_ATTACHMENTS, MAX_PENDING_ATTACHMENT_BYTES, ATTACHMENT_TTL_MS, MAX_ATTACHMENT_NAME_LENGTH, isAttachmentId, base64DecodedLength, attachmentBeginProblem, attachmentChunkProblem, attachmentBlock, parseAttachmentBlock, organize, checkoutOf, homeOf };
|
package/dist/pinomad.js
CHANGED
|
@@ -5,27 +5,33 @@ import {
|
|
|
5
5
|
} from "./chunk-kzbzacz9.js";
|
|
6
6
|
import {
|
|
7
7
|
packagedVersion
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-sd5fkxjj.js";
|
|
9
9
|
import {
|
|
10
10
|
__require
|
|
11
11
|
} from "./chunk-df32avw6.js";
|
|
12
12
|
|
|
13
13
|
// apps/cli/src/pinomad.ts
|
|
14
14
|
var COMMANDS = {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
up: () => import("./chunk-n1m1tjrk.js"),
|
|
16
|
+
status: () => import("./chunk-b457q0tm.js"),
|
|
17
|
+
host: () => import("./chunk-67gq282t.js"),
|
|
18
|
+
server: () => import("./chunk-9h5vawnm.js"),
|
|
19
|
+
relay: () => import("./chunk-9h5vawnm.js"),
|
|
20
|
+
service: () => import("./chunk-h5q2y57m.js"),
|
|
21
|
+
upgrade: () => import("./chunk-6d16c2fv.js"),
|
|
22
|
+
link: () => import("./chunk-as6pyf00.js"),
|
|
23
|
+
pair: () => import("./chunk-tj57aqav.js"),
|
|
24
|
+
join: () => import("./chunk-greb3dp6.js"),
|
|
25
|
+
leave: () => import("./chunk-4dcs8sbj.js")
|
|
24
26
|
};
|
|
25
27
|
var USAGE = `pinomad — PiNomad host, server, and utilities
|
|
26
28
|
|
|
27
29
|
usage: pinomad <command> [args…]
|
|
28
30
|
|
|
31
|
+
up [--server] [--lan] [--yes] set this host up: account, LAN, service
|
|
32
|
+
up --origin URL --remote-port N server to join (app.pinomad.si), LAN port (7422)
|
|
33
|
+
up --no-server|--no-lan [--data-dir D] turn a path off; with no flags, up asks
|
|
34
|
+
status [--data-dir D] joined server and account, LAN, service, host
|
|
29
35
|
host [--data-dir DIR] [--port 7420] [--project DIR]… run the host in the foreground
|
|
30
36
|
host --remote-port PORT [--public-url URL] remote access, LAN (ADR-0008)
|
|
31
37
|
host --relay URL remote access via relay (ADR-0015)
|
|
@@ -37,7 +43,7 @@ usage: pinomad <command> [args…]
|
|
|
37
43
|
upgrade [--force] [address flags] upgrade the package and restart the service
|
|
38
44
|
link [--data-dir D|--url U …] print the browser link
|
|
39
45
|
pair [--data-dir D|--url U …] print a pairing QR
|
|
40
|
-
join <origin>
|
|
46
|
+
join <origin> [token] [--data-dir D] join this host to a server account
|
|
41
47
|
leave [--data-dir D] leave the server account
|
|
42
48
|
--version, -v print the version
|
|
43
49
|
`;
|