pinomad 0.11.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 (96) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-b457q0tm.js → chunk-41q0saz0.js} +11 -8
  3. package/dist/chunk-42nqtx5r.js +22 -0
  4. package/dist/{chunk-wb10efyq.js → chunk-445cevpm.js} +1 -1
  5. package/dist/{chunk-xqycs43g.js → chunk-47ff1d1t.js} +35 -7
  6. package/dist/{chunk-n1m1tjrk.js → chunk-5qf1grkk.js} +19 -13
  7. package/dist/{chunk-ncbqa8jk.js → chunk-7mn38n4f.js} +1 -1
  8. package/dist/{chunk-sd5fkxjj.js → chunk-7wj0jh3t.js} +1 -1
  9. package/dist/{chunk-4dcs8sbj.js → chunk-agq4tn9r.js} +43 -21
  10. package/dist/{chunk-as6pyf00.js → chunk-aqj9wfmb.js} +1 -1
  11. package/dist/{chunk-y127fe1a.js → chunk-g3ahp7b9.js} +11 -0
  12. package/dist/{chunk-gtdpjqa3.js → chunk-gg2dcqbg.js} +186 -45
  13. package/dist/{chunk-6d16c2fv.js → chunk-jeqsdcj1.js} +13 -6
  14. package/dist/{chunk-67gq282t.js → chunk-ksfxqd8s.js} +491 -34
  15. package/dist/{chunk-9h5vawnm.js → chunk-mnmqyq7d.js} +237 -27
  16. package/dist/{chunk-jt34g8st.js → chunk-np4jd070.js} +154 -2
  17. package/dist/{chunk-44pnts98.js → chunk-nppgxh6c.js} +1 -1
  18. package/dist/chunk-nq57fdnq.js +106 -0
  19. package/dist/{chunk-vvj9b1gc.js → chunk-t5abqsva.js} +174 -52
  20. package/dist/{chunk-tj57aqav.js → chunk-vqkb70d7.js} +2 -2
  21. package/dist/{chunk-h5q2y57m.js → chunk-vx7dy07y.js} +10 -4
  22. package/dist/pinomad.js +15 -14
  23. package/package.json +2 -1
  24. package/web/assets/astro-DOly5024.js +1 -0
  25. package/web/assets/c-BIGW1oBm.js +1 -0
  26. package/web/assets/core-DnpzcQzs.js +4 -0
  27. package/web/assets/cpp-DddhVWhK.js +1 -0
  28. package/web/assets/csharp-B_TYUkTI.js +1 -0
  29. package/web/assets/css-Z72CF80r.js +1 -0
  30. package/web/assets/dart-CtKP3W-j.js +1 -0
  31. package/web/assets/diff-D97Zzqfu.js +1 -0
  32. package/web/assets/dockerfile-BcOcwvcX.js +1 -0
  33. package/web/assets/elixir-DkJLSxtU.js +1 -0
  34. package/web/assets/engine-javascript-BHPwbUfN.js +141 -0
  35. package/web/assets/engine-oniguruma-CQjQPVHy.js +1 -0
  36. package/web/assets/fish-BvzEVeQv.js +1 -0
  37. package/web/assets/go-C27-OAKa.js +1 -0
  38. package/web/assets/graphql-ChdNCCLP.js +1 -0
  39. package/web/assets/haskell-Df6bDoY_.js +1 -0
  40. package/web/assets/hcl-BWvSN4gD.js +1 -0
  41. package/web/assets/highlighted-diff-Dn5ifsvK.js +175 -0
  42. package/web/assets/highlights-DnHv6NBM.js +4 -0
  43. package/web/assets/highlights-themes-CUdrlZk3.js +1 -0
  44. package/web/assets/html-BlS82ilI.js +1 -0
  45. package/web/assets/{index-DoyHMPwf.css → index-BV0ecolo.css} +1 -1
  46. package/web/assets/index-DNsTTgPE.js +396 -0
  47. package/web/assets/ini-BEwlwnbL.js +1 -0
  48. package/web/assets/java-CylS5w8V.js +1 -0
  49. package/web/assets/javascript-wDzz0qaB.js +1 -0
  50. package/web/assets/json-Cp-IABpG.js +1 -0
  51. package/web/assets/jsonc-Des-eS-w.js +1 -0
  52. package/web/assets/jsx-g9-lgVsj.js +1 -0
  53. package/web/assets/kotlin-BdnUsdx6.js +1 -0
  54. package/web/assets/less-B1dDrJ26.js +1 -0
  55. package/web/assets/lua-BaeVxFsk.js +1 -0
  56. package/web/assets/make-CHLpvVh8.js +1 -0
  57. package/web/assets/markdown-Cvjx9yec.js +1 -0
  58. package/web/assets/nix-CwoSXNpI.js +1 -0
  59. package/web/assets/objective-c-DXmwc3jG.js +1 -0
  60. package/web/assets/php-DoruYMF0.js +1 -0
  61. package/web/assets/pierre-dark-CyvmCCZW.js +1 -0
  62. package/web/assets/pierre-dark-VKo95ZgA.js +1 -0
  63. package/web/assets/pierre-light-480U9XYS.js +1 -0
  64. package/web/assets/pierre-light-BVSW-Xev.js +1 -0
  65. package/web/assets/postcss-DnzrXTVv.js +1 -0
  66. package/web/assets/powershell-Dd0kTxKP.js +1 -0
  67. package/web/assets/prisma-Vru482bI.js +1 -0
  68. package/web/assets/proto-C7zT0LnQ.js +1 -0
  69. package/web/assets/python-B6aJPvgy.js +1 -0
  70. package/web/assets/ruby-B43yHlf9.js +1 -0
  71. package/web/assets/rust-DPvrY3IQ.js +1 -0
  72. package/web/assets/scala-CqE71os6.js +1 -0
  73. package/web/assets/scss-Cw6XMfVs.js +1 -0
  74. package/web/assets/shellscript-Yzrsuije.js +1 -0
  75. package/web/assets/shiki-BuPP5HCg.js +2 -0
  76. package/web/assets/shiki-langs-FJk0v0Ga.js +2 -0
  77. package/web/assets/shiki-wasm-CgFRq91U.js +1 -0
  78. package/web/assets/sql-DkvoVjww.js +1 -0
  79. package/web/assets/stream-tokenizer-Bl6bisIa.js +4 -0
  80. package/web/assets/svelte-DoIXUgJc.js +1 -0
  81. package/web/assets/swift-C2oV4EkX.js +1 -0
  82. package/web/assets/terraform-BETggiCN.js +1 -0
  83. package/web/assets/theming-themes-BL_CC-Mm.js +1 -0
  84. package/web/assets/toml-vGWfd6FD.js +1 -0
  85. package/web/assets/tsx-COt5Ahok.js +1 -0
  86. package/web/assets/typescript-BPQ3VLAy.js +1 -0
  87. package/web/assets/vue-CwSeJimo.js +1 -0
  88. package/web/assets/xml-sdJ4AIDG.js +1 -0
  89. package/web/assets/xterm-view-6GBZ9nXN.css +32 -0
  90. package/web/assets/xterm-view-ZsuzJb2C.js +38 -0
  91. package/web/assets/yaml-Buea-lGh.js +1 -0
  92. package/web/assets/zig-VOosw3JB.js +1 -0
  93. package/web/index.html +2 -2
  94. package/dist/chunk-etf7ef97.js +0 -55
  95. package/dist/chunk-greb3dp6.js +0 -22
  96. package/web/assets/index-BhHrKpoo.js +0 -395
package/README.md CHANGED
@@ -17,7 +17,7 @@ A self-hosted coding agent built on [Pi Durable](https://www.npmjs.com/package/@
17
17
  ## Quick start
18
18
 
19
19
  1. **Create an account** at [app.pinomad.si](https://app.pinomad.si/) (sign-up is by invite).
20
- 2. **Add your host.** On the machine that should run the agent (macOS or Linux), run the two lines below. `pinomad up` prints a code; enter it on the page it opens, signed in, to add the host to your account. It then runs as a service.
20
+ 2. **Add your host.** On the machine that should run the agent (macOS or Linux), run the two lines below. `pinomad up` opens a page in your browser; sign in and confirm to add the host to your account. It then runs as a service. Without a browser on that machine (over SSH, say), it prints a code to enter on any signed-in device instead.
21
21
 
22
22
  ```sh
23
23
  curl -fsSL https://pinomad.si/install.sh | sh
@@ -1,23 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ readRemovalNotice,
3
4
  readServerConfig
4
- } from "./chunk-etf7ef97.js";
5
+ } from "./chunk-nq57fdnq.js";
5
6
  import {
6
7
  argValue,
7
8
  reachabilityLine,
8
9
  serviceManager,
9
10
  versionLine
10
- } from "./chunk-vvj9b1gc.js";
11
- import"./chunk-jt34g8st.js";
11
+ } from "./chunk-t5abqsva.js";
12
+ import"./chunk-np4jd070.js";
12
13
  import"./chunk-kzbzacz9.js";
13
- import"./chunk-xqycs43g.js";
14
+ import"./chunk-47ff1d1t.js";
14
15
  import"./chunk-pqe7q1s7.js";
15
16
  import {
16
17
  DEFAULT_DATA_DIR
17
18
  } from "./chunk-ngf1zatm.js";
18
19
  import {
19
20
  commandHint
20
- } from "./chunk-sd5fkxjj.js";
21
+ } from "./chunk-7wj0jh3t.js";
21
22
  import"./chunk-df32avw6.js";
22
23
 
23
24
  // apps/host/src/cli/status.ts
@@ -27,6 +28,7 @@ async function statusReport(args, deps) {
27
28
  const dataDir = resolve(args.dataDir);
28
29
  const manager = deps?.manager ?? serviceManager();
29
30
  const server = await readServerConfig(dataDir);
31
+ const removed = server === undefined ? await readRemovalNotice(dataDir) : undefined;
30
32
  const service = await manager.state();
31
33
  const spec = service.installed ? await manager.read() : undefined;
32
34
  const own = spec !== undefined && resolve(spec.dataDir) === dataDir;
@@ -34,6 +36,7 @@ async function statusReport(args, deps) {
34
36
  const port = own ? argValue(spec.hostArgs, "--port") : undefined;
35
37
  return {
36
38
  ...server === undefined ? {} : { server },
39
+ ...removed === undefined ? {} : { removed },
37
40
  ...lanPort === undefined ? {} : { lanPort },
38
41
  service,
39
42
  ...port === undefined ? {} : { port },
@@ -45,10 +48,10 @@ async function main(argv) {
45
48
  const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
46
49
  try {
47
50
  const report = await statusReport({ dataDir });
48
- const { server } = report;
49
- console.log(server === undefined ? `server: not joined (\`${commandHint("up")}\` to join)` : `server: ${server.username === undefined ? "joined" : server.username} at ${server.origin}`);
51
+ const { server, removed } = report;
52
+ console.log(server !== undefined ? `server: ${server.username === undefined ? "joined" : server.username} at ${server.origin}` : removed !== undefined ? `server: removed from ${removed.username === undefined ? "its account" : `${removed.username}'s account`} at ${removed.origin} (\`${commandHint("up")}\` to join again)` : `server: not joined (\`${commandHint("up")}\` to join)`);
50
53
  console.log(`LAN and tailnet: ${report.lanPort === undefined ? "off" : `port ${report.lanPort}`}`);
51
- console.log(`service: ${report.service.installed ? report.service.detail : "not installed"}`);
54
+ console.log(`service: ${!report.service.installed ? "not installed" : report.service.stopped ? `stopped (\`${commandHint("service")} start\` to turn it back on)` : report.service.detail}`);
52
55
  if (report.otherDataDir !== undefined)
53
56
  console.log(`note: the installed service runs ${report.otherDataDir}, not ${dataDir}`);
54
57
  console.log(await versionLine());
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ joinServer,
4
+ main
5
+ } from "./chunk-gg2dcqbg.js";
6
+ import"./chunk-7mn38n4f.js";
7
+ import"./chunk-nppgxh6c.js";
8
+ import"./chunk-t91zt5f0.js";
9
+ import"./chunk-nq57fdnq.js";
10
+ import"./chunk-g3ahp7b9.js";
11
+ import"./chunk-t5abqsva.js";
12
+ import"./chunk-np4jd070.js";
13
+ import"./chunk-kzbzacz9.js";
14
+ import"./chunk-47ff1d1t.js";
15
+ import"./chunk-pqe7q1s7.js";
16
+ import"./chunk-ngf1zatm.js";
17
+ import"./chunk-7wj0jh3t.js";
18
+ import"./chunk-df32avw6.js";
19
+ export {
20
+ main,
21
+ joinServer
22
+ };
@@ -16,7 +16,7 @@ var NOT_BUILT = "Web client not built: run bun run build";
16
16
  var SECURITY_HEADERS = {
17
17
  "content-security-policy": [
18
18
  "default-src 'self'",
19
- "script-src 'self'",
19
+ "script-src 'self' 'wasm-unsafe-eval'",
20
20
  "style-src 'self' 'unsafe-inline'",
21
21
  "img-src 'self' data:",
22
22
  "connect-src 'self' ws: wss:",
@@ -187,7 +187,7 @@ var conversationStream = (id) => `conversation:${id}`;
187
187
  var docStream = (kind, id) => `doc:${kind}:${id}`;
188
188
  var UNAUTHORIZED_CLOSE_CODE = 4401;
189
189
  var INVALID_FRAME_CLOSE_CODE = 4400;
190
- var PROTOCOL = { major: 5, minor: 25 };
190
+ var PROTOCOL = { major: 5, minor: 27 };
191
191
  var CALL_SINCE = {
192
192
  addProject: 0,
193
193
  removeProject: 0,
@@ -229,7 +229,13 @@ var CALL_SINCE = {
229
229
  beginAttachment: 19,
230
230
  appendAttachment: 19,
231
231
  rtcOffer: 23,
232
- ping: 23
232
+ ping: 23,
233
+ openTerminal: 27,
234
+ terminalInput: 27,
235
+ resizeTerminal: 27,
236
+ renameTerminal: 27,
237
+ restartTerminal: 27,
238
+ closeTerminal: 27
233
239
  };
234
240
  function hostSupportsCall(hostMinor, method) {
235
241
  return hostMinor >= CALL_SINCE[method];
@@ -246,7 +252,8 @@ var FEATURE_SINCE = {
246
252
  attachments: 19,
247
253
  imageVariants: 20,
248
254
  rtc: 23,
249
- submitEntry: 25
255
+ submitEntry: 25,
256
+ terminals: 27
250
257
  };
251
258
  function hostSupportsFeature(hostMinor, feature) {
252
259
  return hostMinor >= FEATURE_SINCE[feature];
@@ -259,8 +266,16 @@ function parseClientFrame(value) {
259
266
  if (typeof stream !== "string")
260
267
  return { kind: "malformed" };
261
268
  if (isStreamName(stream)) {
262
- const refs = value.type === "subscribe" && value.images === "ref";
263
- return { kind: "frame", frame: refs ? { type: "subscribe", stream, images: "ref" } : { type: value.type, stream } };
269
+ if (value.type === "unsubscribe")
270
+ return { kind: "frame", frame: { type: "unsubscribe", stream } };
271
+ if (stream.startsWith("terminal:") && value.since !== undefined) {
272
+ const since = value.since;
273
+ if (typeof since !== "number" || !Number.isSafeInteger(since) || since < 0)
274
+ return { kind: "malformed" };
275
+ return { kind: "frame", frame: { type: "subscribe", stream, since } };
276
+ }
277
+ const refs = value.images === "ref";
278
+ return { kind: "frame", frame: refs ? { type: "subscribe", stream, images: "ref" } : { type: "subscribe", stream } };
264
279
  }
265
280
  const prefix = stream.slice(0, stream.indexOf(":") === -1 ? undefined : stream.indexOf(":"));
266
281
  if (STREAM_KINDS.has(prefix))
@@ -277,9 +292,9 @@ function parseClientFrame(value) {
277
292
  return { kind: "unsupportedCall", id, method };
278
293
  return validCallArgs(method, args) ? { kind: "frame", frame: { type: "call", id, method, args } } : { kind: "malformed" };
279
294
  }
280
- var STREAM_KINDS = new Set(["conversation", "doc", "tasks", ...HOST_STREAM_NAMES]);
295
+ var STREAM_KINDS = new Set(["conversation", "doc", "tasks", "terminals", "terminal", ...HOST_STREAM_NAMES]);
281
296
  function isStreamName(stream) {
282
- return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc:.+:)[1-9]\d*$/.test(stream);
297
+ return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc:.+:|terminals:)[1-9]\d*$/.test(stream) || /^terminal:[\w-]+$/.test(stream);
283
298
  }
284
299
  var isCallMethod = (method) => CALL_METHODS.includes(method);
285
300
  var CALL_METHODS = Object.keys(CALL_SINCE);
@@ -299,7 +314,20 @@ function validCallArgs(method, args) {
299
314
  const skills = () => args.skills === undefined || Array.isArray(args.skills) && args.skills.every(nonempty);
300
315
  const attachments = () => args.attachments === undefined || Array.isArray(args.attachments) && args.attachments.every(nonempty);
301
316
  const role = () => record(args.role) && typeof args.role.name === "string" && typeof args.role.description === "string" && typeof args.role.prompt === "string" && (args.role.model === undefined || typeof args.role.model === "string") && (args.role.thinkingLevel === undefined || typeof args.role.thinkingLevel === "string");
317
+ const terminalId = () => nonempty(args.terminalId);
318
+ const size = () => [args.cols, args.rows].every((n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 1);
302
319
  switch (method) {
320
+ case "openTerminal":
321
+ return conversationId() && size();
322
+ case "terminalInput":
323
+ return terminalId() && typeof args.data === "string";
324
+ case "resizeTerminal":
325
+ case "restartTerminal":
326
+ return terminalId() && size();
327
+ case "renameTerminal":
328
+ return terminalId() && (args.name === null || typeof args.name === "string");
329
+ case "closeTerminal":
330
+ return terminalId();
303
331
  case "submit":
304
332
  return conversationId() && typeof args.text === "string" && (args.whenBusy === "steer" || args.whenBusy === "followUp") && nonempty(args.requestId) && images() && skills() && attachments();
305
333
  case "submission":
@@ -3,31 +3,32 @@ import {
3
3
  joinServer,
4
4
  openServiceSlot,
5
5
  writeService
6
- } from "./chunk-gtdpjqa3.js";
7
- import"./chunk-ncbqa8jk.js";
8
- import"./chunk-44pnts98.js";
6
+ } from "./chunk-gg2dcqbg.js";
7
+ import"./chunk-7mn38n4f.js";
8
+ import"./chunk-nppgxh6c.js";
9
9
  import"./chunk-t91zt5f0.js";
10
10
  import {
11
11
  normalizeOrigin,
12
+ readRemovalNotice,
12
13
  readServerConfig
13
- } from "./chunk-etf7ef97.js";
14
- import"./chunk-y127fe1a.js";
14
+ } from "./chunk-nq57fdnq.js";
15
+ import"./chunk-g3ahp7b9.js";
15
16
  import {
16
17
  argValue,
17
18
  serviceManager,
18
19
  withoutArg,
19
20
  withoutRelayArgs
20
- } from "./chunk-vvj9b1gc.js";
21
- import"./chunk-jt34g8st.js";
21
+ } from "./chunk-t5abqsva.js";
22
+ import"./chunk-np4jd070.js";
22
23
  import"./chunk-kzbzacz9.js";
23
- import"./chunk-xqycs43g.js";
24
+ import"./chunk-47ff1d1t.js";
24
25
  import"./chunk-pqe7q1s7.js";
25
26
  import {
26
27
  DEFAULT_DATA_DIR
27
28
  } from "./chunk-ngf1zatm.js";
28
29
  import {
29
30
  commandHint
30
- } from "./chunk-sd5fkxjj.js";
31
+ } from "./chunk-7wj0jh3t.js";
31
32
  import"./chunk-df32avw6.js";
32
33
 
33
34
  // apps/host/src/cli/up.ts
@@ -44,6 +45,9 @@ function resolveReach(flags, current) {
44
45
  return { server: flags.server ?? current.server, lan: flags.lan ?? current.lan };
45
46
  return { server: flags.server ?? !flags.lan, lan: flags.lan ?? !flags.server };
46
47
  }
48
+ var lastRemovedOrigin = async (dataDir) => (await readRemovalNotice(dataDir).catch(() => {
49
+ return;
50
+ }))?.origin;
47
51
  async function currentReach(dataDir, deps) {
48
52
  const slot = await openServiceSlot(dataDir, deps?.manager ?? serviceManager());
49
53
  const config = await readServerConfig(dataDir);
@@ -73,7 +77,7 @@ async function upHost(args, deps) {
73
77
  const manager = deps?.manager ?? serviceManager();
74
78
  const slot = await openServiceSlot(dataDir, manager);
75
79
  const config = await readServerConfig(dataDir);
76
- const origin = normalizeOrigin(args.origin ?? config?.origin ?? OFFICIAL_SERVER_ORIGIN);
80
+ const origin = normalizeOrigin(args.origin ?? config?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
77
81
  if (reach.server && config !== undefined && normalizeOrigin(config.origin) !== origin) {
78
82
  throw new Error(`this host is joined to ${config.origin} — run \`${commandHint("leave")}\` first to join ${origin}, ` + `or leave out --origin to stay`);
79
83
  }
@@ -81,7 +85,7 @@ async function upHost(args, deps) {
81
85
  const lan = (hostArgs) => withLan(hostArgs, lanPort);
82
86
  const notes = [];
83
87
  if (reach.server && config === undefined) {
84
- const joined = await joinServer({ origin, dataDir, editHostArgs: lan }, { ...deps, manager });
88
+ const joined = await joinServer({ origin, dataDir, code: args.code, editHostArgs: lan }, { ...deps, manager });
85
89
  return {
86
90
  joined: { origin: joined.origin, username: joined.username, fresh: true },
87
91
  ...lanPort === undefined ? {} : { lanPort },
@@ -145,6 +149,7 @@ async function main(argv, deps) {
145
149
  lan: { type: "boolean" },
146
150
  yes: { type: "boolean", short: "y" },
147
151
  origin: { type: "string" },
152
+ code: { type: "boolean" },
148
153
  "remote-port": { type: "string" },
149
154
  "data-dir": { type: "string" }
150
155
  }
@@ -152,7 +157,7 @@ async function main(argv, deps) {
152
157
  const log = deps?.log ?? ((line) => console.log(line));
153
158
  try {
154
159
  const dataDir = resolve(values["data-dir"] ?? DEFAULT_DATA_DIR);
155
- const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? OFFICIAL_SERVER_ORIGIN);
160
+ const origin = normalizeOrigin(values.origin ?? (await readServerConfig(dataDir))?.origin ?? await lastRemovedOrigin(dataDir) ?? OFFICIAL_SERVER_ORIGIN);
156
161
  const current = await currentReach(dataDir, deps);
157
162
  let reach = resolveReach({ server: values.server, lan: values.lan }, current);
158
163
  if (reach === undefined) {
@@ -176,7 +181,8 @@ async function main(argv, deps) {
176
181
  dataDir,
177
182
  origin,
178
183
  reach,
179
- ...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] }
184
+ ...values["remote-port"] === undefined ? {} : { remotePort: values["remote-port"] },
185
+ ...values.code === true ? { code: true } : {}
180
186
  }, deps);
181
187
  if (outcome.joined !== undefined) {
182
188
  const who = outcome.joined.username ?? "an account";
@@ -6,7 +6,7 @@ import {
6
6
  relayWsBase,
7
7
  signRelayChallenge,
8
8
  signRelayChallengeV2
9
- } from "./chunk-y127fe1a.js";
9
+ } from "./chunk-g3ahp7b9.js";
10
10
  import {
11
11
  toBase64Url
12
12
  } from "./chunk-pqe7q1s7.js";
@@ -1,6 +1,6 @@
1
1
  // apps/host/src/distribution.ts
2
2
  import { fileURLToPath } from "node:url";
3
- var packagedVersion = "0.11.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,32 +3,35 @@ import {
3
3
  assertSecureServerOrigin,
4
4
  probeServerAccounts,
5
5
  runServerOperation
6
- } from "./chunk-ncbqa8jk.js";
6
+ } from "./chunk-7mn38n4f.js";
7
7
  import {
8
8
  loadHostKey,
9
9
  loadRelayKey
10
10
  } from "./chunk-t91zt5f0.js";
11
11
  import {
12
+ readRemovalNotice,
12
13
  readServerConfig,
13
14
  removeServerConfig
14
- } from "./chunk-etf7ef97.js";
15
+ } from "./chunk-nq57fdnq.js";
15
16
  import {
16
17
  relayHostId
17
- } from "./chunk-y127fe1a.js";
18
+ } from "./chunk-g3ahp7b9.js";
18
19
  import {
19
20
  argValue,
20
21
  restartService,
21
22
  serviceManager,
22
23
  withoutRelayArgs
23
- } from "./chunk-vvj9b1gc.js";
24
- import"./chunk-jt34g8st.js";
24
+ } from "./chunk-t5abqsva.js";
25
+ import"./chunk-np4jd070.js";
25
26
  import"./chunk-kzbzacz9.js";
26
- import"./chunk-xqycs43g.js";
27
+ import"./chunk-47ff1d1t.js";
27
28
  import"./chunk-pqe7q1s7.js";
28
29
  import {
29
30
  DEFAULT_DATA_DIR
30
31
  } from "./chunk-ngf1zatm.js";
31
- import"./chunk-sd5fkxjj.js";
32
+ import {
33
+ commandHint
34
+ } from "./chunk-7wj0jh3t.js";
32
35
  import"./chunk-df32avw6.js";
33
36
 
34
37
  // apps/host/src/cli/leave.ts
@@ -39,8 +42,13 @@ var message = (error) => error instanceof Error ? error.message : String(error);
39
42
  async function leaveServer(args, deps) {
40
43
  const dataDir = resolve(args.dataDir);
41
44
  const config = await readServerConfig(dataDir);
42
- if (config === undefined)
43
- 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
+ }
44
52
  const origin = config.origin;
45
53
  const warnings = [];
46
54
  const notes = [];
@@ -78,7 +86,10 @@ async function leaveServer(args, deps) {
78
86
  await removeServerConfig(dataDir);
79
87
  const manager = deps?.manager ?? serviceManager();
80
88
  try {
81
- 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;
82
93
  const previous = installed ? await manager.read() : undefined;
83
94
  if (!installed) {
84
95
  notes.push("no service installed — restart any running host so it stops dialing the server");
@@ -87,19 +98,30 @@ async function leaveServer(args, deps) {
87
98
  } else {
88
99
  const stripped = previous === undefined ? undefined : { ...previous, hostArgs: withoutRelayArgs(previous.hostArgs) };
89
100
  const rewriting = stripped !== undefined && stripped.hostArgs.length !== previous.hostArgs.length;
90
- const port = argValue(previous?.hostArgs ?? [], "--port");
91
- const code = await restartService({
92
- force: false,
93
- argv: ["--data-dir", dataDir, ...port === undefined ? [] : ["--url", `ws://127.0.0.1:${port}`]],
94
- manager,
95
- ...rewriting ? {
96
- restart: async () => {
101
+ if (state.stopped) {
102
+ if (rewriting) {
103
+ try {
97
104
  await manager.install(stripped);
105
+ } finally {
106
+ await manager.stop();
98
107
  }
99
- } : {}
100
- });
101
- if (code !== 0)
102
- 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
+ }
103
125
  }
104
126
  } catch (error) {
105
127
  warnings.push(`could not update the service definition: ${message(error)} — remove --relay from it and restart manually`);
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-ngf1zatm.js";
4
4
  import {
5
5
  packagedVersion
6
- } from "./chunk-sd5fkxjj.js";
6
+ } from "./chunk-7wj0jh3t.js";
7
7
  import"./chunk-df32avw6.js";
8
8
 
9
9
  // apps/host/src/cli/web-link.ts
@@ -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" });