pinomad 0.11.0 → 0.13.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 (103) hide show
  1. package/README.md +1 -1
  2. package/dist/{chunk-wb10efyq.js → chunk-445cevpm.js} +1 -1
  3. package/dist/{chunk-jt34g8st.js → chunk-4fferbxw.js} +247 -27
  4. package/dist/{chunk-67gq282t.js → chunk-53xv3fny.js} +725 -75
  5. package/dist/{chunk-44pnts98.js → chunk-563fnad9.js} +1 -1
  6. package/dist/{chunk-b457q0tm.js → chunk-6th9xv39.js} +11 -8
  7. package/dist/{chunk-ncbqa8jk.js → chunk-7mn38n4f.js} +1 -1
  8. package/dist/{chunk-as6pyf00.js → chunk-90g1cg9n.js} +1 -1
  9. package/dist/{chunk-9h5vawnm.js → chunk-aapsc9g0.js} +270 -66
  10. package/dist/{chunk-n1m1tjrk.js → chunk-d4tq62yr.js} +57 -16
  11. package/dist/{chunk-gtdpjqa3.js → chunk-d9mq5d46.js} +205 -49
  12. package/dist/{chunk-y127fe1a.js → chunk-g3ahp7b9.js} +11 -0
  13. package/dist/{chunk-xqycs43g.js → chunk-kmzzjv53.js} +83 -8
  14. package/dist/{chunk-4dcs8sbj.js → chunk-m51mq8vh.js} +43 -21
  15. package/dist/{chunk-tj57aqav.js → chunk-maqv34cs.js} +2 -2
  16. package/dist/chunk-nq57fdnq.js +106 -0
  17. package/dist/{chunk-h5q2y57m.js → chunk-ra86wjp5.js} +10 -4
  18. package/dist/{chunk-sd5fkxjj.js → chunk-s0h7c7da.js} +1 -1
  19. package/dist/chunk-sd640dt7.js +22 -0
  20. package/dist/{chunk-6d16c2fv.js → chunk-t6fw0wa9.js} +13 -6
  21. package/dist/{chunk-vvj9b1gc.js → chunk-wd7aracm.js} +188 -54
  22. package/dist/pinomad.js +15 -14
  23. package/package.json +13 -11
  24. package/web/assets/astro-DOly5024.js +1 -0
  25. package/web/assets/c-BIGW1oBm.js +1 -0
  26. package/web/assets/core-BHm2gdHL.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/geist-mono-cyrillic-ext-wght-normal-X_5orZeX.woff2 +0 -0
  38. package/web/assets/geist-mono-cyrillic-wght-normal-DiZS0aHC.woff2 +0 -0
  39. package/web/assets/geist-mono-latin-ext-wght-normal-Bwz-egvJ.woff2 +0 -0
  40. package/web/assets/geist-mono-latin-wght-normal-XN7g48iV.woff2 +0 -0
  41. package/web/assets/geist-mono-symbols2-wght-normal-CO5SzqOn.woff2 +0 -0
  42. package/web/assets/geist-mono-vietnamese-wght-normal-DadHysG0.woff2 +0 -0
  43. package/web/assets/go-C27-OAKa.js +1 -0
  44. package/web/assets/graphql-ChdNCCLP.js +1 -0
  45. package/web/assets/haskell-Df6bDoY_.js +1 -0
  46. package/web/assets/hcl-BWvSN4gD.js +1 -0
  47. package/web/assets/highlighted-diff-DrHvSLK9.js +175 -0
  48. package/web/assets/highlights-1aLwssz-.js +4 -0
  49. package/web/assets/highlights-themes-C1p4pFYS.js +1 -0
  50. package/web/assets/html-BlS82ilI.js +1 -0
  51. package/web/assets/index-A_AUJoO6.css +1 -0
  52. package/web/assets/index-BafYMKiN.js +396 -0
  53. package/web/assets/ini-BEwlwnbL.js +1 -0
  54. package/web/assets/java-CylS5w8V.js +1 -0
  55. package/web/assets/javascript-wDzz0qaB.js +1 -0
  56. package/web/assets/json-Cp-IABpG.js +1 -0
  57. package/web/assets/jsonc-Des-eS-w.js +1 -0
  58. package/web/assets/jsx-g9-lgVsj.js +1 -0
  59. package/web/assets/kotlin-BdnUsdx6.js +1 -0
  60. package/web/assets/less-B1dDrJ26.js +1 -0
  61. package/web/assets/lua-BaeVxFsk.js +1 -0
  62. package/web/assets/make-CHLpvVh8.js +1 -0
  63. package/web/assets/markdown-Cvjx9yec.js +1 -0
  64. package/web/assets/nix-CwoSXNpI.js +1 -0
  65. package/web/assets/objective-c-DXmwc3jG.js +1 -0
  66. package/web/assets/php-DoruYMF0.js +1 -0
  67. package/web/assets/pierre-dark-CyvmCCZW.js +1 -0
  68. package/web/assets/pierre-dark-VKo95ZgA.js +1 -0
  69. package/web/assets/pierre-light-480U9XYS.js +1 -0
  70. package/web/assets/pierre-light-BVSW-Xev.js +1 -0
  71. package/web/assets/postcss-DnzrXTVv.js +1 -0
  72. package/web/assets/powershell-Dd0kTxKP.js +1 -0
  73. package/web/assets/prisma-Vru482bI.js +1 -0
  74. package/web/assets/proto-C7zT0LnQ.js +1 -0
  75. package/web/assets/python-B6aJPvgy.js +1 -0
  76. package/web/assets/ruby-B43yHlf9.js +1 -0
  77. package/web/assets/rust-DPvrY3IQ.js +1 -0
  78. package/web/assets/scala-CqE71os6.js +1 -0
  79. package/web/assets/scss-Cw6XMfVs.js +1 -0
  80. package/web/assets/shellscript-Yzrsuije.js +1 -0
  81. package/web/assets/shiki-BwyCm3kZ.js +2 -0
  82. package/web/assets/shiki-langs-CMlALLSg.js +2 -0
  83. package/web/assets/shiki-wasm-CgFRq91U.js +1 -0
  84. package/web/assets/sql-DkvoVjww.js +1 -0
  85. package/web/assets/stream-tokenizer-BlgIhw0y.js +4 -0
  86. package/web/assets/svelte-DoIXUgJc.js +1 -0
  87. package/web/assets/swift-C2oV4EkX.js +1 -0
  88. package/web/assets/terraform-BETggiCN.js +1 -0
  89. package/web/assets/theming-themes-DDvoqEqT.js +1 -0
  90. package/web/assets/toml-vGWfd6FD.js +1 -0
  91. package/web/assets/tsx-COt5Ahok.js +1 -0
  92. package/web/assets/typescript-BPQ3VLAy.js +1 -0
  93. package/web/assets/vue-CwSeJimo.js +1 -0
  94. package/web/assets/xml-sdJ4AIDG.js +1 -0
  95. package/web/assets/xterm-view-6GBZ9nXN.css +32 -0
  96. package/web/assets/xterm-view-BnKOnF3C.js +38 -0
  97. package/web/assets/yaml-Buea-lGh.js +1 -0
  98. package/web/assets/zig-VOosw3JB.js +1 -0
  99. package/web/index.html +2 -2
  100. package/dist/chunk-etf7ef97.js +0 -55
  101. package/dist/chunk-greb3dp6.js +0 -22
  102. package/web/assets/index-BhHrKpoo.js +0 -395
  103. package/web/assets/index-DoyHMPwf.css +0 -1
@@ -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: 29 };
191
191
  var CALL_SINCE = {
192
192
  addProject: 0,
193
193
  removeProject: 0,
@@ -202,6 +202,7 @@ var CALL_SINCE = {
202
202
  submission: 25,
203
203
  withdrawQueued: 14,
204
204
  steerQueued: 14,
205
+ reorderQueued: 29,
205
206
  abort: 0,
206
207
  compact: 0,
207
208
  runCommand: 17,
@@ -226,10 +227,17 @@ var CALL_SINCE = {
226
227
  deleteAgent: 6,
227
228
  setPushToken: 7,
228
229
  image: 15,
230
+ earlierEntries: 28,
229
231
  beginAttachment: 19,
230
232
  appendAttachment: 19,
231
233
  rtcOffer: 23,
232
- ping: 23
234
+ ping: 23,
235
+ openTerminal: 27,
236
+ terminalInput: 27,
237
+ resizeTerminal: 27,
238
+ renameTerminal: 27,
239
+ restartTerminal: 27,
240
+ closeTerminal: 27
233
241
  };
234
242
  function hostSupportsCall(hostMinor, method) {
235
243
  return hostMinor >= CALL_SINCE[method];
@@ -246,7 +254,9 @@ var FEATURE_SINCE = {
246
254
  attachments: 19,
247
255
  imageVariants: 20,
248
256
  rtc: 23,
249
- submitEntry: 25
257
+ submitEntry: 25,
258
+ terminals: 27,
259
+ transcriptPaging: 28
250
260
  };
251
261
  function hostSupportsFeature(hostMinor, feature) {
252
262
  return hostMinor >= FEATURE_SINCE[feature];
@@ -259,8 +269,27 @@ function parseClientFrame(value) {
259
269
  if (typeof stream !== "string")
260
270
  return { kind: "malformed" };
261
271
  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 } };
272
+ if (value.type === "unsubscribe")
273
+ return { kind: "frame", frame: { type: "unsubscribe", stream } };
274
+ if (stream.startsWith("terminal:") && value.since !== undefined) {
275
+ const since = value.since;
276
+ if (typeof since !== "number" || !Number.isSafeInteger(since) || since < 0)
277
+ return { kind: "malformed" };
278
+ return { kind: "frame", frame: { type: "subscribe", stream, since } };
279
+ }
280
+ const refs = value.images === "ref";
281
+ let window;
282
+ if (stream.startsWith("conversation:") && value.window !== undefined) {
283
+ if (!record(value.window) || !positive(value.window.limit) || !(value.window.from === undefined || positive(value.window.from))) {
284
+ return { kind: "malformed" };
285
+ }
286
+ const limit = value.window.limit;
287
+ window = value.window.from === undefined ? { limit } : { limit, from: value.window.from };
288
+ }
289
+ return {
290
+ kind: "frame",
291
+ frame: { type: "subscribe", stream, ...refs ? { images: "ref" } : {}, ...window === undefined ? {} : { window } }
292
+ };
264
293
  }
265
294
  const prefix = stream.slice(0, stream.indexOf(":") === -1 ? undefined : stream.indexOf(":"));
266
295
  if (STREAM_KINDS.has(prefix))
@@ -277,9 +306,9 @@ function parseClientFrame(value) {
277
306
  return { kind: "unsupportedCall", id, method };
278
307
  return validCallArgs(method, args) ? { kind: "frame", frame: { type: "call", id, method, args } } : { kind: "malformed" };
279
308
  }
280
- var STREAM_KINDS = new Set(["conversation", "doc", "tasks", ...HOST_STREAM_NAMES]);
309
+ var STREAM_KINDS = new Set(["conversation", "doc", "tasks", "terminals", "terminal", ...HOST_STREAM_NAMES]);
281
310
  function isStreamName(stream) {
282
- return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc:.+:)[1-9]\d*$/.test(stream);
311
+ return stream === "tasks" || HOST_STREAM_NAMES.includes(stream) || /^(?:conversation:|doc:.+:|terminals:)[1-9]\d*$/.test(stream) || /^terminal:[\w-]+$/.test(stream);
283
312
  }
284
313
  var isCallMethod = (method) => CALL_METHODS.includes(method);
285
314
  var CALL_METHODS = Object.keys(CALL_SINCE);
@@ -299,7 +328,20 @@ function validCallArgs(method, args) {
299
328
  const skills = () => args.skills === undefined || Array.isArray(args.skills) && args.skills.every(nonempty);
300
329
  const attachments = () => args.attachments === undefined || Array.isArray(args.attachments) && args.attachments.every(nonempty);
301
330
  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");
331
+ const terminalId = () => nonempty(args.terminalId);
332
+ const size = () => [args.cols, args.rows].every((n) => typeof n === "number" && Number.isSafeInteger(n) && n >= 1);
302
333
  switch (method) {
334
+ case "openTerminal":
335
+ return conversationId() && size();
336
+ case "terminalInput":
337
+ return terminalId() && typeof args.data === "string";
338
+ case "resizeTerminal":
339
+ case "restartTerminal":
340
+ return terminalId() && size();
341
+ case "renameTerminal":
342
+ return terminalId() && (args.name === null || typeof args.name === "string");
343
+ case "closeTerminal":
344
+ return terminalId();
303
345
  case "submit":
304
346
  return conversationId() && typeof args.text === "string" && (args.whenBusy === "steer" || args.whenBusy === "followUp") && nonempty(args.requestId) && images() && skills() && attachments();
305
347
  case "submission":
@@ -307,6 +349,8 @@ function validCallArgs(method, args) {
307
349
  case "withdrawQueued":
308
350
  case "steerQueued":
309
351
  return conversationId() && submissionId();
352
+ case "reorderQueued":
353
+ return conversationId() && submissionId() && (args.before === undefined || typeof args.before === "number" && Number.isSafeInteger(args.before) && args.before >= 1);
310
354
  case "abort":
311
355
  return conversationId();
312
356
  case "setThinkingLevel":
@@ -327,6 +371,8 @@ function validCallArgs(method, args) {
327
371
  return conversationId() && nonempty(args.kind) && nonempty(args.requestId) && Array.isArray(args.answers) && args.answers.every((answer) => record(answer) && Array.isArray(answer.selected) && answer.selected.every((l) => typeof l === "string") && (answer.other === undefined || typeof answer.other === "string"));
328
372
  case "changes":
329
373
  return conversationId();
374
+ case "earlierEntries":
375
+ return conversationId() && positive(args.before) && positive(args.limit);
330
376
  case "image":
331
377
  return nonempty(args.ref) && (args.variant === undefined || args.variant === "thumbnail" || args.variant === "preview");
332
378
  case "beginAttachment":
@@ -383,6 +429,9 @@ function validCallArgs(method, args) {
383
429
  function record(value) {
384
430
  return value !== null && typeof value === "object" && !Array.isArray(value);
385
431
  }
432
+ function positive(value) {
433
+ return typeof value === "number" && Number.isSafeInteger(value) && value >= 1;
434
+ }
386
435
  function nonempty(value) {
387
436
  return typeof value === "string" && value.length > 0;
388
437
  }
@@ -619,4 +668,30 @@ function homeOf(index, summaries, id) {
619
668
  return;
620
669
  }
621
670
 
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 };
671
+ // packages/protocol/src/transcript.ts
672
+ var TRANSCRIPT_PAGE = 50;
673
+ var MAX_TRANSCRIPT_PAGE = 500;
674
+ function withEarlier(view, earlier) {
675
+ if (earlier === undefined)
676
+ return view;
677
+ const { before: _window, ...rest } = view;
678
+ return {
679
+ ...rest,
680
+ entries: [...earlier.entries, ...view.entries],
681
+ ...earlier.before === undefined ? {} : { before: earlier.before }
682
+ };
683
+ }
684
+ function addEarlier(shown, earlier, requested, page) {
685
+ if (shown.before !== requested)
686
+ return earlier;
687
+ return {
688
+ entries: [...page.entries, ...earlier?.entries ?? []],
689
+ ...page.before === undefined ? {} : { before: page.before }
690
+ };
691
+ }
692
+ function isBusy(view) {
693
+ const live = view.docs?.["pi.live"];
694
+ return live?.run !== undefined;
695
+ }
696
+
697
+ 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, TRANSCRIPT_PAGE, MAX_TRANSCRIPT_PAGE, withEarlier, addEarlier, isBusy };
@@ -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-wd7aracm.js";
25
+ import"./chunk-4fferbxw.js";
25
26
  import"./chunk-kzbzacz9.js";
26
- import"./chunk-xqycs43g.js";
27
+ import"./chunk-kmzzjv53.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-s0h7c7da.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`);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  connectRemoteDurable
3
- } from "./chunk-jt34g8st.js";
4
- import"./chunk-xqycs43g.js";
3
+ } from "./chunk-4fferbxw.js";
4
+ import"./chunk-kmzzjv53.js";
5
5
  import {
6
6
  isLoopbackUrl
7
7
  } from "./chunk-pqe7q1s7.js";
@@ -0,0 +1,106 @@
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 REMOVAL_NOTICE_FILE = "server-removed.json";
7
+ var normalizeOrigin = (origin) => origin.replace(/\/+$/, "");
8
+ async function readServerConfig(dataDir) {
9
+ const config = await readServerConfigAt(join(dataDir, SERVER_CONFIG_FILE));
10
+ if (config === undefined)
11
+ return;
12
+ const notice = await readRemovalNotice(dataDir).catch(() => {
13
+ return;
14
+ });
15
+ return notice !== undefined && endsJoin(notice, config) ? undefined : config;
16
+ }
17
+ var endsJoin = (notice, config) => normalizeOrigin(notice.origin) === normalizeOrigin(config.origin) && notice.binding === config.binding;
18
+ async function readServerConfigAt(path) {
19
+ let text;
20
+ try {
21
+ text = await readFile(path, "utf8");
22
+ } catch (error) {
23
+ if (error.code === "ENOENT")
24
+ return;
25
+ throw error;
26
+ }
27
+ let value;
28
+ try {
29
+ value = JSON.parse(text);
30
+ } catch {
31
+ throw new Error(`${path} is not valid JSON — delete it or run \`pinomad leave\``);
32
+ }
33
+ const origin = value?.origin;
34
+ if (typeof origin !== "string" || origin.trim() === "") {
35
+ throw new Error(`${path} is missing a string "origin"`);
36
+ }
37
+ const { binding, username } = value;
38
+ return {
39
+ origin,
40
+ ...typeof binding === "string" && binding !== "" ? { binding } : {},
41
+ ...typeof username === "string" && username !== "" ? { username } : {}
42
+ };
43
+ }
44
+ async function writeJsonFile(path, value) {
45
+ const tmp = `${path}.tmp-${process.pid}`;
46
+ await writeFile(tmp, `${JSON.stringify(value)}
47
+ `, { mode: 384 });
48
+ await rename(tmp, path);
49
+ }
50
+ async function writeServerConfig(dataDir, config) {
51
+ await writeJsonFile(join(dataDir, SERVER_CONFIG_FILE), {
52
+ origin: normalizeOrigin(config.origin),
53
+ binding: randomBytes(16).toString("base64url"),
54
+ ...config.username === undefined ? {} : { username: config.username }
55
+ });
56
+ await rm(join(dataDir, REMOVAL_NOTICE_FILE), { force: true });
57
+ }
58
+ async function markRemoved(dataDir, removed, removedAt = Date.now()) {
59
+ await writeJsonFile(join(dataDir, REMOVAL_NOTICE_FILE), {
60
+ origin: normalizeOrigin(removed.origin),
61
+ ...removed.binding === undefined ? {} : { binding: removed.binding },
62
+ ...removed.username === undefined ? {} : { username: removed.username },
63
+ removedAt
64
+ });
65
+ }
66
+ async function readRemovalNotice(dataDir) {
67
+ const path = join(dataDir, REMOVAL_NOTICE_FILE);
68
+ let text;
69
+ try {
70
+ text = await readFile(path, "utf8");
71
+ } catch (error) {
72
+ if (error.code === "ENOENT")
73
+ return;
74
+ throw error;
75
+ }
76
+ let value;
77
+ try {
78
+ value = JSON.parse(text);
79
+ } catch {
80
+ throw new Error(`${path} is not valid JSON — delete it`);
81
+ }
82
+ const { origin, binding, username, removedAt } = value ?? {};
83
+ if (typeof origin !== "string" || typeof removedAt !== "number")
84
+ throw new Error(`${path} is missing "origin" or "removedAt" — delete it`);
85
+ return {
86
+ origin,
87
+ ...typeof binding === "string" && binding !== "" ? { binding } : {},
88
+ ...typeof username === "string" && username !== "" ? { username } : {},
89
+ removedAt
90
+ };
91
+ }
92
+ async function removeServerConfig(dataDir) {
93
+ await rm(join(dataDir, SERVER_CONFIG_FILE), { force: true });
94
+ await rm(join(dataDir, REMOVAL_NOTICE_FILE), { force: true });
95
+ }
96
+ async function readServerBinding(dataDir) {
97
+ return (await readServerConfig(dataDir))?.binding;
98
+ }
99
+ function resolveRelayOrigin(flag, config) {
100
+ if (flag !== undefined && config !== undefined && normalizeOrigin(flag) !== normalizeOrigin(config.origin)) {
101
+ throw new Error(`--relay ${flag} disagrees with ${SERVER_CONFIG_FILE} (${config.origin}) — remove one`);
102
+ }
103
+ return flag ?? config?.origin;
104
+ }
105
+
106
+ export { REMOVAL_NOTICE_FILE, normalizeOrigin, readServerConfig, writeServerConfig, markRemoved, readRemovalNotice, removeServerConfig, readServerBinding, resolveRelayOrigin };
@@ -3,17 +3,23 @@ import {
3
3
  main,
4
4
  reachabilityLine,
5
5
  restartService,
6
+ serviceStatusJson,
7
+ startService,
8
+ stopService,
6
9
  versionLine
7
- } from "./chunk-vvj9b1gc.js";
8
- import"./chunk-jt34g8st.js";
10
+ } from "./chunk-wd7aracm.js";
11
+ import"./chunk-4fferbxw.js";
9
12
  import"./chunk-kzbzacz9.js";
10
- import"./chunk-xqycs43g.js";
13
+ import"./chunk-kmzzjv53.js";
11
14
  import"./chunk-pqe7q1s7.js";
12
15
  import"./chunk-ngf1zatm.js";
13
- import"./chunk-sd5fkxjj.js";
16
+ import"./chunk-s0h7c7da.js";
14
17
  import"./chunk-df32avw6.js";
15
18
  export {
16
19
  versionLine,
20
+ stopService,
21
+ startService,
22
+ serviceStatusJson,
17
23
  restartService,
18
24
  reachabilityLine,
19
25
  main
@@ -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.13.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}`;
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ joinServer,
4
+ main
5
+ } from "./chunk-d9mq5d46.js";
6
+ import"./chunk-7mn38n4f.js";
7
+ import"./chunk-563fnad9.js";
8
+ import"./chunk-t91zt5f0.js";
9
+ import"./chunk-nq57fdnq.js";
10
+ import"./chunk-g3ahp7b9.js";
11
+ import"./chunk-wd7aracm.js";
12
+ import"./chunk-4fferbxw.js";
13
+ import"./chunk-kzbzacz9.js";
14
+ import"./chunk-kmzzjv53.js";
15
+ import"./chunk-pqe7q1s7.js";
16
+ import"./chunk-ngf1zatm.js";
17
+ import"./chunk-s0h7c7da.js";
18
+ import"./chunk-df32avw6.js";
19
+ export {
20
+ main,
21
+ joinServer
22
+ };
@@ -2,19 +2,20 @@
2
2
  import {
3
3
  restartService,
4
4
  serviceManager
5
- } from "./chunk-vvj9b1gc.js";
6
- import"./chunk-jt34g8st.js";
5
+ } from "./chunk-wd7aracm.js";
6
+ import"./chunk-4fferbxw.js";
7
7
  import"./chunk-kzbzacz9.js";
8
- import"./chunk-xqycs43g.js";
8
+ import"./chunk-kmzzjv53.js";
9
9
  import"./chunk-pqe7q1s7.js";
10
10
  import {
11
11
  hostAddressOptions
12
12
  } from "./chunk-ngf1zatm.js";
13
13
  import {
14
+ commandHint,
14
15
  globalPrefixOf,
15
16
  packageDir,
16
17
  packagedVersion
17
- } from "./chunk-sd5fkxjj.js";
18
+ } from "./chunk-s0h7c7da.js";
18
19
  import"./chunk-df32avw6.js";
19
20
 
20
21
  // apps/host/src/cli/upgrade.ts
@@ -34,7 +35,7 @@ function npmBeside(execPath) {
34
35
  const cli = join(binDir, "..", "lib", "node_modules", "npm", "bin", "npm-cli.js");
35
36
  return existsSync(cli) ? { command: execPath, args: [cli], binDir } : { command: "npm", args: [] };
36
37
  }
37
- var serviceInstalled = () => Promise.resolve().then(() => serviceManager().state()).then((state) => state.installed, () => false);
38
+ var serviceState = () => Promise.resolve().then(() => serviceManager().state()).catch(() => ({ installed: false, stopped: false }));
38
39
  async function main(argv, deps = {}) {
39
40
  const { values } = parseArgs({
40
41
  args: [...argv],
@@ -75,11 +76,17 @@ async function main(argv, deps = {}) {
75
76
  return;
76
77
  }
77
78
  }
78
- if (!await (deps.serviceInstalled ?? serviceInstalled)()) {
79
+ const service = await (deps.serviceState ?? serviceState)();
80
+ if (!service.installed) {
79
81
  console.log("service not installed; upgrade done, nothing restarted");
80
82
  process.exitCode = 0;
81
83
  return;
82
84
  }
85
+ if (service.stopped) {
86
+ console.log(`service stopped; upgrade done, it stays off until \`${commandHint("service")} start\``);
87
+ process.exitCode = 0;
88
+ return;
89
+ }
83
90
  (deps.exit ?? process.exit)(await restart({ force: values.force, argv: argv.filter((arg) => arg !== "--force") }));
84
91
  }
85
92
  if (false) {}