pinomad 0.10.0 → 0.12.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.
Files changed (98) hide show
  1. package/README.md +11 -9
  2. package/dist/chunk-41q0saz0.js +73 -0
  3. package/dist/chunk-42nqtx5r.js +22 -0
  4. package/dist/{chunk-wb10efyq.js → chunk-445cevpm.js} +1 -1
  5. package/dist/{chunk-jgd38rna.js → chunk-47ff1d1t.js} +206 -178
  6. package/dist/chunk-5qf1grkk.js +220 -0
  7. package/dist/{chunk-4h7pkkzr.js → chunk-7mn38n4f.js} +3 -3
  8. package/dist/{chunk-zehwx009.js → chunk-7wj0jh3t.js} +1 -1
  9. package/dist/{chunk-5791mb0b.js → chunk-agq4tn9r.js} +47 -23
  10. package/dist/{chunk-c5p9xkd8.js → chunk-aqj9wfmb.js} +3 -3
  11. package/dist/{chunk-cx8k63by.js → chunk-g3ahp7b9.js} +12 -1
  12. package/dist/chunk-gg2dcqbg.js +458 -0
  13. package/dist/{chunk-73efzaf1.js → chunk-jeqsdcj1.js} +30 -13
  14. package/dist/{chunk-czj9nfrz.js → chunk-ksfxqd8s.js} +500 -41
  15. package/dist/{chunk-p978qj9m.js → chunk-mnmqyq7d.js} +565 -39
  16. package/dist/{chunk-4sepcfpy.js → chunk-np4jd070.js} +155 -3
  17. package/dist/{chunk-v414b5pp.js → chunk-nppgxh6c.js} +2 -2
  18. package/dist/chunk-nq57fdnq.js +106 -0
  19. package/dist/{chunk-sygeb9qb.js → chunk-pqe7q1s7.js} +1 -1
  20. package/dist/{chunk-w2m9xkks.js → chunk-t5abqsva.js} +199 -72
  21. package/dist/{chunk-8gywfw5n.js → chunk-t91zt5f0.js} +2 -51
  22. package/dist/{chunk-vax4zspw.js → chunk-vqkb70d7.js} +3 -3
  23. package/dist/chunk-vx7dy07y.js +26 -0
  24. package/dist/pinomad.js +19 -12
  25. package/package.json +2 -1
  26. package/web/assets/astro-DOly5024.js +1 -0
  27. package/web/assets/c-BIGW1oBm.js +1 -0
  28. package/web/assets/core-DnpzcQzs.js +4 -0
  29. package/web/assets/cpp-DddhVWhK.js +1 -0
  30. package/web/assets/csharp-B_TYUkTI.js +1 -0
  31. package/web/assets/css-Z72CF80r.js +1 -0
  32. package/web/assets/dart-CtKP3W-j.js +1 -0
  33. package/web/assets/diff-D97Zzqfu.js +1 -0
  34. package/web/assets/dockerfile-BcOcwvcX.js +1 -0
  35. package/web/assets/elixir-DkJLSxtU.js +1 -0
  36. package/web/assets/engine-javascript-BHPwbUfN.js +141 -0
  37. package/web/assets/engine-oniguruma-CQjQPVHy.js +1 -0
  38. package/web/assets/fish-BvzEVeQv.js +1 -0
  39. package/web/assets/go-C27-OAKa.js +1 -0
  40. package/web/assets/graphql-ChdNCCLP.js +1 -0
  41. package/web/assets/haskell-Df6bDoY_.js +1 -0
  42. package/web/assets/hcl-BWvSN4gD.js +1 -0
  43. package/web/assets/highlighted-diff-Dn5ifsvK.js +175 -0
  44. package/web/assets/highlights-DnHv6NBM.js +4 -0
  45. package/web/assets/highlights-themes-CUdrlZk3.js +1 -0
  46. package/web/assets/html-BlS82ilI.js +1 -0
  47. package/web/assets/{index-DoyHMPwf.css → index-BV0ecolo.css} +1 -1
  48. package/web/assets/index-DNsTTgPE.js +396 -0
  49. package/web/assets/ini-BEwlwnbL.js +1 -0
  50. package/web/assets/java-CylS5w8V.js +1 -0
  51. package/web/assets/javascript-wDzz0qaB.js +1 -0
  52. package/web/assets/json-Cp-IABpG.js +1 -0
  53. package/web/assets/jsonc-Des-eS-w.js +1 -0
  54. package/web/assets/jsx-g9-lgVsj.js +1 -0
  55. package/web/assets/kotlin-BdnUsdx6.js +1 -0
  56. package/web/assets/less-B1dDrJ26.js +1 -0
  57. package/web/assets/lua-BaeVxFsk.js +1 -0
  58. package/web/assets/make-CHLpvVh8.js +1 -0
  59. package/web/assets/markdown-Cvjx9yec.js +1 -0
  60. package/web/assets/nix-CwoSXNpI.js +1 -0
  61. package/web/assets/objective-c-DXmwc3jG.js +1 -0
  62. package/web/assets/php-DoruYMF0.js +1 -0
  63. package/web/assets/pierre-dark-CyvmCCZW.js +1 -0
  64. package/web/assets/pierre-dark-VKo95ZgA.js +1 -0
  65. package/web/assets/pierre-light-480U9XYS.js +1 -0
  66. package/web/assets/pierre-light-BVSW-Xev.js +1 -0
  67. package/web/assets/postcss-DnzrXTVv.js +1 -0
  68. package/web/assets/powershell-Dd0kTxKP.js +1 -0
  69. package/web/assets/prisma-Vru482bI.js +1 -0
  70. package/web/assets/proto-C7zT0LnQ.js +1 -0
  71. package/web/assets/python-B6aJPvgy.js +1 -0
  72. package/web/assets/ruby-B43yHlf9.js +1 -0
  73. package/web/assets/rust-DPvrY3IQ.js +1 -0
  74. package/web/assets/scala-CqE71os6.js +1 -0
  75. package/web/assets/scss-Cw6XMfVs.js +1 -0
  76. package/web/assets/shellscript-Yzrsuije.js +1 -0
  77. package/web/assets/shiki-BuPP5HCg.js +2 -0
  78. package/web/assets/shiki-langs-FJk0v0Ga.js +2 -0
  79. package/web/assets/shiki-wasm-CgFRq91U.js +1 -0
  80. package/web/assets/sql-DkvoVjww.js +1 -0
  81. package/web/assets/stream-tokenizer-Bl6bisIa.js +4 -0
  82. package/web/assets/svelte-DoIXUgJc.js +1 -0
  83. package/web/assets/swift-C2oV4EkX.js +1 -0
  84. package/web/assets/terraform-BETggiCN.js +1 -0
  85. package/web/assets/theming-themes-BL_CC-Mm.js +1 -0
  86. package/web/assets/toml-vGWfd6FD.js +1 -0
  87. package/web/assets/tsx-COt5Ahok.js +1 -0
  88. package/web/assets/typescript-BPQ3VLAy.js +1 -0
  89. package/web/assets/vue-CwSeJimo.js +1 -0
  90. package/web/assets/xml-sdJ4AIDG.js +1 -0
  91. package/web/assets/xterm-view-6GBZ9nXN.css +32 -0
  92. package/web/assets/xterm-view-ZsuzJb2C.js +38 -0
  93. package/web/assets/yaml-Buea-lGh.js +1 -0
  94. package/web/assets/zig-VOosw3JB.js +1 -0
  95. package/web/index.html +2 -2
  96. package/dist/chunk-bhtx2c8a.js +0 -16
  97. package/dist/chunk-c05g401k.js +0 -170
  98. package/web/assets/index-BdBjP1qJ.js +0 -395
@@ -0,0 +1,220 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ joinServer,
4
+ openServiceSlot,
5
+ writeService
6
+ } from "./chunk-gg2dcqbg.js";
7
+ import"./chunk-7mn38n4f.js";
8
+ import"./chunk-nppgxh6c.js";
9
+ import"./chunk-t91zt5f0.js";
10
+ import {
11
+ normalizeOrigin,
12
+ readRemovalNotice,
13
+ readServerConfig
14
+ } from "./chunk-nq57fdnq.js";
15
+ import"./chunk-g3ahp7b9.js";
16
+ import {
17
+ argValue,
18
+ serviceManager,
19
+ withoutArg,
20
+ withoutRelayArgs
21
+ } from "./chunk-t5abqsva.js";
22
+ import"./chunk-np4jd070.js";
23
+ import"./chunk-kzbzacz9.js";
24
+ import"./chunk-47ff1d1t.js";
25
+ import"./chunk-pqe7q1s7.js";
26
+ import {
27
+ DEFAULT_DATA_DIR
28
+ } from "./chunk-ngf1zatm.js";
29
+ import {
30
+ commandHint
31
+ } from "./chunk-7wj0jh3t.js";
32
+ import"./chunk-df32avw6.js";
33
+
34
+ // apps/host/src/cli/up.ts
35
+ import { resolve } from "node:path";
36
+ import { createInterface } from "node:readline/promises";
37
+ import { parseArgs } from "node:util";
38
+ var OFFICIAL_SERVER_ORIGIN = "https://app.pinomad.si";
39
+ var DEFAULT_REMOTE_PORT = "7422";
40
+ var message = (error) => error instanceof Error ? error.message : String(error);
41
+ function resolveReach(flags, current) {
42
+ if (flags.server === undefined && flags.lan === undefined)
43
+ return;
44
+ if (current !== undefined)
45
+ return { server: flags.server ?? current.server, lan: flags.lan ?? current.lan };
46
+ return { server: flags.server ?? !flags.lan, lan: flags.lan ?? !flags.server };
47
+ }
48
+ var lastRemovedOrigin = async (dataDir) => (await readRemovalNotice(dataDir).catch(() => {
49
+ return;
50
+ }))?.origin;
51
+ async function currentReach(dataDir, deps) {
52
+ const slot = await openServiceSlot(dataDir, deps?.manager ?? serviceManager());
53
+ const config = await readServerConfig(dataDir);
54
+ if (config === undefined && !slot.state.installed)
55
+ return;
56
+ return {
57
+ server: config !== undefined,
58
+ lan: argValue(slot.previous?.hostArgs ?? [], "--remote-port") !== undefined
59
+ };
60
+ }
61
+ function withLan(hostArgs, port) {
62
+ if (port === undefined)
63
+ return withoutArg(withoutArg(hostArgs, "--remote-port"), "--public-url");
64
+ if (argValue(hostArgs, "--remote-port") === port)
65
+ return hostArgs;
66
+ return [...withoutArg(hostArgs, "--remote-port"), "--remote-port", port];
67
+ }
68
+ async function upHost(args, deps) {
69
+ const dataDir = resolve(args.dataDir);
70
+ const { reach } = args;
71
+ if (!reach.server && !reach.lan)
72
+ throw new Error("nothing to set up: choose the server, the LAN, or both");
73
+ const port = args.remotePort;
74
+ if (port !== undefined && !(/^\d+$/.test(port) && Number(port) >= 1 && Number(port) <= 65535)) {
75
+ throw new Error(`--remote-port must be a port number from 1 to 65535, got ${port}`);
76
+ }
77
+ const manager = deps?.manager ?? serviceManager();
78
+ const slot = await openServiceSlot(dataDir, manager);
79
+ const config = await readServerConfig(dataDir);
80
+ const origin = normalizeOrigin(args.origin ?? config?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
81
+ if (reach.server && config !== undefined && normalizeOrigin(config.origin) !== origin) {
82
+ throw new Error(`this host is joined to ${config.origin} — run \`${commandHint("leave")}\` first to join ${origin}, ` + `or leave out --origin to stay`);
83
+ }
84
+ const lanPort = reach.lan ? args.remotePort ?? argValue(slot.previous?.hostArgs ?? [], "--remote-port") ?? DEFAULT_REMOTE_PORT : undefined;
85
+ const lan = (hostArgs) => withLan(hostArgs, lanPort);
86
+ const notes = [];
87
+ if (reach.server && config === undefined) {
88
+ const joined = await joinServer({ origin, dataDir, code: args.code, editHostArgs: lan }, { ...deps, manager });
89
+ return {
90
+ joined: { origin: joined.origin, username: joined.username, fresh: true },
91
+ ...lanPort === undefined ? {} : { lanPort },
92
+ service: joined.service,
93
+ notes
94
+ };
95
+ }
96
+ const edit = reach.server ? (hostArgs) => lan(withoutRelayArgs(hostArgs)) : lan;
97
+ const service = await writeService(slot, edit, { changed: reach.server });
98
+ if (!reach.server && config !== undefined) {
99
+ notes.push(`still joined to ${config.origin} — run \`${commandHint("leave")}\` to leave it`);
100
+ }
101
+ return {
102
+ ...reach.server && config !== undefined ? { joined: { origin: config.origin, ...config.username === undefined ? {} : { username: config.username }, fresh: false } } : {},
103
+ ...lanPort === undefined ? {} : { lanPort },
104
+ service,
105
+ notes
106
+ };
107
+ }
108
+ async function askOnTerminal(question) {
109
+ const terminal = createInterface({ input: process.stdin, output: process.stdout });
110
+ try {
111
+ return await terminal.question(question);
112
+ } finally {
113
+ terminal.close();
114
+ }
115
+ }
116
+ var CHOICES = [
117
+ { server: true, lan: false },
118
+ { server: false, lan: true },
119
+ { server: true, lan: true }
120
+ ];
121
+ async function askReach(origin, fallback, ask) {
122
+ const preset = CHOICES.findIndex((choice) => choice.server === fallback.server && choice.lan === fallback.lan) + 1;
123
+ let question = `How should your other devices reach this host?
124
+ ` + ` 1) Through ${origin}: from anywhere, with push notifications
125
+ ` + ` 2) Only on your local network and tailnet
126
+ ` + ` 3) Both
127
+ ` + `Choose 1, 2 or 3 [${preset}]: `;
128
+ for (;; ) {
129
+ const answer = (await ask(question)).trim();
130
+ if (answer === "")
131
+ return CHOICES[preset - 1];
132
+ const picked = CHOICES[Number(answer) - 1];
133
+ if (/^[123]$/.test(answer) && picked !== undefined)
134
+ return picked;
135
+ question = "Choose 1, 2 or 3: ";
136
+ }
137
+ }
138
+ var FLAGS = `choose with flags:
139
+ --server [--origin URL] through a PiNomad server (default ${OFFICIAL_SERVER_ORIGIN})
140
+ --lan [--remote-port N] only on the local network and tailnet (default port ${DEFAULT_REMOTE_PORT})
141
+ --server --lan both
142
+ --yes keep what this host has, or the server on a new host`;
143
+ async function main(argv, deps) {
144
+ const { values } = parseArgs({
145
+ args: [...argv],
146
+ allowNegative: true,
147
+ options: {
148
+ server: { type: "boolean" },
149
+ lan: { type: "boolean" },
150
+ yes: { type: "boolean", short: "y" },
151
+ origin: { type: "string" },
152
+ code: { type: "boolean" },
153
+ "remote-port": { type: "string" },
154
+ "data-dir": { type: "string" }
155
+ }
156
+ });
157
+ const log = deps?.log ?? ((line) => console.log(line));
158
+ try {
159
+ const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
160
+ const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
161
+ const current = await currentReach(dataDir, deps);
162
+ let reach = resolveReach({ server: values.server, lan: values.lan }, current);
163
+ if (reach === undefined) {
164
+ const fallback = current !== undefined && (current.server || current.lan) ? current : { server: true, lan: false };
165
+ if (values.yes === true) {
166
+ reach = fallback;
167
+ } else if (!(deps?.interactive ?? (process.stdin.isTTY && process.stdout.isTTY))) {
168
+ console.error(`${commandHint("up")}: no terminal to ask how other devices reach this host — ${FLAGS}`);
169
+ process.exitCode = 2;
170
+ return;
171
+ } else {
172
+ reach = await askReach(origin, fallback, deps?.ask ?? askOnTerminal);
173
+ }
174
+ }
175
+ if (!reach.server && !reach.lan) {
176
+ console.error(`${commandHint("up")}: neither the server nor the LAN would be on — ` + `\`${commandHint("service")} install\` runs a host only this machine can reach`);
177
+ process.exitCode = 2;
178
+ return;
179
+ }
180
+ const outcome = await upHost({
181
+ dataDir,
182
+ origin,
183
+ reach,
184
+ ...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] },
185
+ ...values.code === true ? { code: true } : {}
186
+ }, deps);
187
+ if (outcome.joined !== undefined) {
188
+ const who = outcome.joined.username ?? "an account";
189
+ log(outcome.joined.fresh ? `joined ${who} at ${outcome.joined.origin}` : `already joined ${who} at ${outcome.joined.origin}`);
190
+ log(`Sign in at ${outcome.joined.origin} on your other devices; they pair with this host on their own.`);
191
+ }
192
+ if (outcome.lanPort !== undefined) {
193
+ log(`LAN and tailnet: port ${outcome.lanPort}. Pair a device with \`${commandHint("pair")}\`.`);
194
+ }
195
+ for (const note of outcome.notes)
196
+ log(`note: ${note}`);
197
+ if (outcome.service === "installed")
198
+ log("service installed and started");
199
+ else if (outcome.service === "updated")
200
+ log("service updated; host restarted when idle");
201
+ else if (outcome.service === "unchanged")
202
+ log("service already running with these settings");
203
+ else {
204
+ console.error(`the service restart did not finish — check \`${commandHint("status")}\``);
205
+ process.exitCode = 1;
206
+ }
207
+ } catch (error) {
208
+ console.error(message(error));
209
+ process.exitCode = 1;
210
+ }
211
+ }
212
+ if (false)
213
+ ;
214
+ export {
215
+ upHost,
216
+ resolveReach,
217
+ main,
218
+ OFFICIAL_SERVER_ORIGIN,
219
+ DEFAULT_REMOTE_PORT
220
+ };
@@ -6,10 +6,10 @@ import {
6
6
  relayWsBase,
7
7
  signRelayChallenge,
8
8
  signRelayChallengeV2
9
- } from "./chunk-cx8k63by.js";
9
+ } from "./chunk-g3ahp7b9.js";
10
10
  import {
11
11
  toBase64Url
12
- } from "./chunk-sygeb9qb.js";
12
+ } from "./chunk-pqe7q1s7.js";
13
13
 
14
14
  // apps/host/src/cli/server-ops.ts
15
15
  import { WebSocket } from "ws";
@@ -130,4 +130,4 @@ async function runServerOperation(options) {
130
130
  });
131
131
  }
132
132
 
133
- export { assertSecureServerOrigin, probeServerAccounts, runServerOperation };
133
+ export { isSecureServerOrigin, assertSecureServerOrigin, probeServerAccounts, runServerOperation };
@@ -1,6 +1,6 @@
1
1
  // apps/host/src/distribution.ts
2
2
  import { fileURLToPath } from "node:url";
3
- var packagedVersion = "0.10.0";
3
+ var packagedVersion = "0.12.0";
4
4
  var webRoot = fileURLToPath(new URL(packagedVersion === undefined ? "../../web/dist" : "../web/", import.meta.url));
5
5
  var hostEntry = packagedVersion === undefined ? { script: fileURLToPath(new URL("./main.ts", import.meta.url)), args: [] } : { script: fileURLToPath(new URL("./pinomad.js", import.meta.url)), args: ["host"] };
6
6
  var commandHint = (name) => packagedVersion === undefined ? `bun run ${name}` : `pinomad ${name}`;
@@ -3,30 +3,35 @@ import {
3
3
  assertSecureServerOrigin,
4
4
  probeServerAccounts,
5
5
  runServerOperation
6
- } from "./chunk-4h7pkkzr.js";
6
+ } from "./chunk-7mn38n4f.js";
7
7
  import {
8
8
  loadHostKey,
9
- loadRelayKey,
9
+ loadRelayKey
10
+ } from "./chunk-t91zt5f0.js";
11
+ import {
12
+ readRemovalNotice,
10
13
  readServerConfig,
11
14
  removeServerConfig
12
- } from "./chunk-8gywfw5n.js";
15
+ } from "./chunk-nq57fdnq.js";
13
16
  import {
14
17
  relayHostId
15
- } from "./chunk-cx8k63by.js";
18
+ } from "./chunk-g3ahp7b9.js";
16
19
  import {
17
20
  argValue,
18
21
  restartService,
19
22
  serviceManager,
20
23
  withoutRelayArgs
21
- } from "./chunk-w2m9xkks.js";
24
+ } from "./chunk-t5abqsva.js";
25
+ import"./chunk-np4jd070.js";
22
26
  import"./chunk-kzbzacz9.js";
23
- import"./chunk-zehwx009.js";
24
- import"./chunk-4sepcfpy.js";
25
- import"./chunk-jgd38rna.js";
26
- import"./chunk-sygeb9qb.js";
27
+ import"./chunk-47ff1d1t.js";
28
+ import"./chunk-pqe7q1s7.js";
27
29
  import {
28
30
  DEFAULT_DATA_DIR
29
31
  } from "./chunk-ngf1zatm.js";
32
+ import {
33
+ commandHint
34
+ } from "./chunk-7wj0jh3t.js";
30
35
  import"./chunk-df32avw6.js";
31
36
 
32
37
  // apps/host/src/cli/leave.ts
@@ -37,8 +42,13 @@ var message = (error) => error instanceof Error ? error.message : String(error);
37
42
  async function leaveServer(args, deps) {
38
43
  const dataDir = resolve(args.dataDir);
39
44
  const config = await readServerConfig(dataDir);
40
- if (config === undefined)
41
- throw new Error(`not joined — no server.json in ${dataDir}`);
45
+ if (config === undefined) {
46
+ const removed = await readRemovalNotice(dataDir);
47
+ if (removed === undefined)
48
+ throw new Error(`not joined — no server.json in ${dataDir}`);
49
+ await removeServerConfig(dataDir);
50
+ return { origin: removed.origin, warnings: [], notes: ["the account had already removed this host"] };
51
+ }
42
52
  const origin = config.origin;
43
53
  const warnings = [];
44
54
  const notes = [];
@@ -76,7 +86,10 @@ async function leaveServer(args, deps) {
76
86
  await removeServerConfig(dataDir);
77
87
  const manager = deps?.manager ?? serviceManager();
78
88
  try {
79
- const installed = await manager.state().then((state) => state.installed, () => false);
89
+ const state = await manager.state().catch(() => {
90
+ return;
91
+ });
92
+ const installed = state?.installed === true;
80
93
  const previous = installed ? await manager.read() : undefined;
81
94
  if (!installed) {
82
95
  notes.push("no service installed — restart any running host so it stops dialing the server");
@@ -85,19 +98,30 @@ async function leaveServer(args, deps) {
85
98
  } else {
86
99
  const stripped = previous === undefined ? undefined : { ...previous, hostArgs: withoutRelayArgs(previous.hostArgs) };
87
100
  const rewriting = stripped !== undefined && stripped.hostArgs.length !== previous.hostArgs.length;
88
- const port = argValue(previous?.hostArgs ?? [], "--port");
89
- const code = await restartService({
90
- force: false,
91
- argv: ["--data-dir", dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]],
92
- manager,
93
- ...rewriting ? {
94
- restart: async () => {
101
+ if (state.stopped) {
102
+ if (rewriting) {
103
+ try {
95
104
  await manager.install(stripped);
105
+ } finally {
106
+ await manager.stop();
96
107
  }
97
- } : {}
98
- });
99
- if (code !== 0)
100
- warnings.push("the service restart did not finish — check `service status`");
108
+ }
109
+ notes.push(`the service is stopped and stays off until \`${commandHint("service")} start\``);
110
+ } else {
111
+ const port = argValue(previous?.hostArgs ?? [], "--port");
112
+ const code = await restartService({
113
+ force: false,
114
+ argv: ["--data-dir", dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]],
115
+ manager,
116
+ ...rewriting ? {
117
+ restart: async () => {
118
+ await manager.install(stripped);
119
+ }
120
+ } : {}
121
+ });
122
+ if (code !== 0)
123
+ warnings.push("the service restart did not finish — check `service status`");
124
+ }
101
125
  }
102
126
  } catch (error) {
103
127
  warnings.push(`could not update the service definition: ${message(error)} — remove --relay from it and restart manually`);
@@ -1,9 +1,9 @@
1
- import {
2
- packagedVersion
3
- } from "./chunk-zehwx009.js";
4
1
  import {
5
2
  hostAddress
6
3
  } from "./chunk-ngf1zatm.js";
4
+ import {
5
+ packagedVersion
6
+ } from "./chunk-7wj0jh3t.js";
7
7
  import"./chunk-df32avw6.js";
8
8
 
9
9
  // apps/host/src/cli/web-link.ts
@@ -3,7 +3,7 @@ import {
3
3
  fromBase64Url,
4
4
  parseEndpointSet,
5
5
  toBase64Url
6
- } from "./chunk-sygeb9qb.js";
6
+ } from "./chunk-pqe7q1s7.js";
7
7
 
8
8
  // packages/protocol/src/relay.ts
9
9
  import { ed25519 } from "@noble/curves/ed25519.js";
@@ -15,6 +15,7 @@ var RELAY_CLOSE = {
15
15
  handshakeTimeout: 4608,
16
16
  replaced: 4609,
17
17
  acceptTimeout: 4610,
18
+ hostRemoved: 4611,
18
19
  tooManyConnections: 4629,
19
20
  trafficLimit: 4630
20
21
  };
@@ -467,6 +468,14 @@ function startRelayLink(options) {
467
468
  }
468
469
  if (stopped)
469
470
  return;
471
+ if (code === RELAY_CLOSE.hostRemoved) {
472
+ stopped = true;
473
+ setState({
474
+ status: "removed",
475
+ reason: `relay ${origin}: the account removed this host (${hostId}); join it again with \`pinomad up\` (${code})`
476
+ });
477
+ return;
478
+ }
470
479
  const rejection = REJECTION_REASONS[code];
471
480
  if (rejection !== undefined) {
472
481
  const reason = `relay ${origin}: ${rejection(hostId)} (${code})`;
@@ -494,6 +503,8 @@ function startRelayLink(options) {
494
503
  origin,
495
504
  state: () => state,
496
505
  sendPush(request) {
506
+ if (state.status === "removed")
507
+ return Promise.resolve({ result: "unavailable", reason: "the account removed this host" });
497
508
  const socket = control;
498
509
  if (stopped || socket === undefined || socket.readyState !== WebSocket.OPEN || state.status !== "registered") {
499
510
  return Promise.resolve({ result: "retry", reason: "control disconnected" });