pinokiod 7.1.69 → 7.1.71

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.
@@ -79,12 +79,23 @@ class Caddy {
79
79
  console.log("Caddy Installed?", installed)
80
80
  if (installed) {
81
81
  let resolved
82
+ let startup_output = []
83
+ this.kernel.caddy_startup_output = startup_output
82
84
  await new Promise((resolve, reject) => {
83
85
  this.kernel.exec({
84
86
  message: `caddy run --watch`,
85
87
  path: this.kernel.homedir,
86
88
  }, (e) => {
87
89
  process.stdout.write(e.raw)
90
+ const cleaned = e && e.cleaned ? e.cleaned : ""
91
+ const lines = cleaned.split(/\r?\n/).map((line) => line.trim()).filter(Boolean)
92
+ for (let line of lines) {
93
+ startup_output.push(line)
94
+ if (startup_output.length > 20) {
95
+ startup_output.shift()
96
+ }
97
+ }
98
+ this.kernel.caddy_startup_output = startup_output.slice()
88
99
  if (!resolved) {
89
100
  if (/endpoint started/i.test(e.cleaned)) {
90
101
  resolved = true
@@ -93,6 +104,28 @@ class Caddy {
93
104
  }
94
105
  })
95
106
  })
107
+ let admin_running = false
108
+ const admin_wait_started = Date.now()
109
+ while ((Date.now() - admin_wait_started) < 5000) {
110
+ admin_running = await this.running()
111
+ if (admin_running) {
112
+ break
113
+ }
114
+ await new Promise((resolve) => {
115
+ setTimeout(resolve, 250)
116
+ })
117
+ }
118
+ if (!admin_running) {
119
+ console.log("caddy admin unavailable after startup")
120
+ if (startup_output.length > 0) {
121
+ console.log("recent caddy startup output:")
122
+ for (let line of startup_output) {
123
+ console.log(line)
124
+ }
125
+ }
126
+ return
127
+ }
128
+ this.kernel.caddy_startup_output = startup_output.slice()
96
129
  console.log("kernel.refresh bin.caddy.start")
97
130
  this.kernel.peer.announce()
98
131
  console.log("announced to peers")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.1.69",
3
+ "version": "7.1.71",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -79,6 +79,7 @@ const { createInjectRouter, resolveInjectList } = require("./lib/inject_router")
79
79
  const { createTaskPackageService } = require("./lib/task_packages")
80
80
  const { createTaskWorkspaceLinkService } = require("./lib/task_workspace_links")
81
81
  const { createContentValidationService } = require("./lib/content_validation")
82
+ const { buildSecureRouterDebugSnapshot, createSecureRouterDebugStore } = require("./lib/secure_router_debug")
82
83
  const AppRegistryService = require("./lib/app_registry")
83
84
  const AppLogService = require("./lib/app_logs")
84
85
  const AppSearchService = require("./lib/app_search")
@@ -253,6 +254,7 @@ class Server {
253
254
  updated_at: new Date().toISOString()
254
255
  }
255
256
  this.startup_status_run_id = 0
257
+ this.secure_router_debug = createSecureRouterDebugStore()
256
258
  this.installFatalHandlers()
257
259
  }
258
260
  setStartupStatus(patch = {}) {
@@ -5517,44 +5519,6 @@ class Server {
5517
5519
 
5518
5520
  return { success: true }
5519
5521
  }
5520
- async ensureSecureRouterReady(timeout = 120000, interval = 500) {
5521
- if (!(this.kernel && this.kernel.peer && this.kernel.peer.https_active)) {
5522
- return { success: true, stage: "disabled" }
5523
- }
5524
- const caddy = this.kernel && this.kernel.bin && this.kernel.bin.mod
5525
- ? this.kernel.bin.mod.caddy
5526
- : null
5527
- const deadline = Date.now() + timeout
5528
- let startedCaddyManually = false
5529
- let refreshedRouter = false
5530
- let lastStatus = null
5531
- while (Date.now() < deadline) {
5532
- lastStatus = await this.check_router_up()
5533
- if (lastStatus && lastStatus.success) {
5534
- return lastStatus
5535
- }
5536
- if (
5537
- lastStatus &&
5538
- lastStatus.error === "caddy admin unavailable" &&
5539
- !startedCaddyManually &&
5540
- caddy &&
5541
- typeof caddy.start === "function"
5542
- ) {
5543
- startedCaddyManually = true
5544
- await caddy.start()
5545
- continue
5546
- }
5547
- if (!refreshedRouter) {
5548
- refreshedRouter = true
5549
- await this.kernel.refresh(true).catch(() => {})
5550
- continue
5551
- }
5552
- await new Promise((resolve) => {
5553
- setTimeout(resolve, interval)
5554
- })
5555
- }
5556
- throw new Error(lastStatus && lastStatus.error ? lastStatus.error : "secure router did not come up")
5557
- }
5558
5522
  async start(options) {
5559
5523
  this.debug = false
5560
5524
  if (options) {
@@ -5824,12 +5788,6 @@ class Server {
5824
5788
  phase: "initializing_environment"
5825
5789
  })
5826
5790
  await Environment.init({}, this.kernel)
5827
- if (this.kernel && this.kernel.peer && this.kernel.peer.https_active) {
5828
- setStartupStatus({
5829
- phase: "waiting_for_secure_router"
5830
- })
5831
- await this.ensureSecureRouterReady()
5832
- }
5833
5791
  setStartupStatus({
5834
5792
  sys_ready: this.getStartupStatus().sys_ready,
5835
5793
  managed_ready: true,
@@ -15483,6 +15441,9 @@ class Server {
15483
15441
  this.app.get("/pinokio/startup_status", ex((req, res) => {
15484
15442
  res.json(this.getStartupStatus())
15485
15443
  }))
15444
+ this.app.get("/pinokio/secure_router_debug", ex(async (req, res) => {
15445
+ res.json(await buildSecureRouterDebugSnapshot(this, this.secure_router_debug))
15446
+ }))
15486
15447
  this.app.get("/pinokio/requirements_ready", ex((req, res) => {
15487
15448
  res.json(this.getStartupStatus())
15488
15449
  }))
@@ -0,0 +1,120 @@
1
+ function createSecureRouterDebugStore() {
2
+ return {
3
+ events: [],
4
+ state: {
5
+ active: false,
6
+ started_at: null,
7
+ updated_at: null,
8
+ last_status: null,
9
+ caddy_start_attempted: false,
10
+ caddy_start_finished: false,
11
+ caddy_start_error: null,
12
+ refresh_attempted: false,
13
+ refresh_finished: false,
14
+ refresh_error: null,
15
+ }
16
+ }
17
+ }
18
+
19
+ function clone(value) {
20
+ return JSON.parse(JSON.stringify(value))
21
+ }
22
+
23
+ function recordSecureRouterDebug(store, message, patch = {}) {
24
+ if (!store) {
25
+ return
26
+ }
27
+ const timestamp = new Date().toISOString()
28
+ store.state = {
29
+ ...(store.state || {}),
30
+ ...patch,
31
+ updated_at: timestamp
32
+ }
33
+ store.events.push({
34
+ at: timestamp,
35
+ message
36
+ })
37
+ if (store.events.length > 20) {
38
+ store.events.shift()
39
+ }
40
+ }
41
+
42
+ async function buildSecureRouterDebugSnapshot(server, store) {
43
+ const kernel = server && server.kernel ? server.kernel : null
44
+ const peer = kernel && kernel.peer ? kernel.peer : null
45
+ const router = kernel && kernel.router && typeof kernel.router.published === "function"
46
+ ? (kernel.router.published() || {})
47
+ : {}
48
+ const routerDialKeys = Object.keys(router)
49
+ const hasPinokioLocalhost = routerDialKeys.some((dial) => {
50
+ const domains = Array.isArray(router[dial]) ? router[dial] : []
51
+ return domains.includes("pinokio.localhost")
52
+ })
53
+ const caddy = kernel && kernel.bin && kernel.bin.mod
54
+ ? kernel.bin.mod.caddy
55
+ : null
56
+ let caddyInstalled = null
57
+ let caddyRunning = null
58
+ try {
59
+ if (caddy && typeof caddy.installed === "function") {
60
+ caddyInstalled = await caddy.installed()
61
+ }
62
+ } catch (error) {
63
+ caddyInstalled = { error: error && error.message ? error.message : String(error) }
64
+ }
65
+ try {
66
+ if (caddy && typeof caddy.running === "function") {
67
+ caddyRunning = await caddy.running()
68
+ }
69
+ } catch (error) {
70
+ caddyRunning = { error: error && error.message ? error.message : String(error) }
71
+ }
72
+ const startupStatus = server && typeof server.getStartupStatus === "function"
73
+ ? server.getStartupStatus()
74
+ : null
75
+ const caddyStartupOutput = kernel && Array.isArray(kernel.caddy_startup_output)
76
+ ? clone(kernel.caddy_startup_output)
77
+ : []
78
+ const state = store && store.state ? clone(store.state) : {}
79
+ const events = store && Array.isArray(store.events) ? clone(store.events) : []
80
+ return {
81
+ wait: state,
82
+ flags: {
83
+ peer_active: peer ? !!peer.peer_active : null,
84
+ https_active: peer ? !!peer.https_active : null,
85
+ active: peer ? !!peer.active : null,
86
+ },
87
+ caddy: {
88
+ installed: caddyInstalled,
89
+ running: caddyRunning,
90
+ start_attempted: !!state.caddy_start_attempted,
91
+ start_finished: !!state.caddy_start_finished,
92
+ start_error: state.caddy_start_error || null,
93
+ startup_output: caddyStartupOutput,
94
+ },
95
+ peer: {
96
+ host: peer && peer.host ? peer.host : null,
97
+ name: peer && peer.name ? peer.name : null,
98
+ has_info: !!(peer && peer.info),
99
+ has_host_info: !!(peer && peer.info && peer.host && peer.info[peer.host]),
100
+ refreshing: !!(peer && peer.refreshing),
101
+ },
102
+ router: {
103
+ published_count: routerDialKeys.length,
104
+ published_dials: routerDialKeys.slice(0, 10),
105
+ has_pinokio_localhost: hasPinokioLocalhost,
106
+ refresh_attempted: !!state.refresh_attempted,
107
+ refresh_finished: !!state.refresh_finished,
108
+ refresh_error: state.refresh_error || null,
109
+ last_status: state.last_status || null,
110
+ },
111
+ startup: startupStatus,
112
+ events
113
+ }
114
+ }
115
+
116
+ module.exports = {
117
+ buildSecureRouterDebugSnapshot,
118
+ createSecureRouterDebugStore,
119
+ recordSecureRouterDebug,
120
+ }
@@ -0,0 +1,112 @@
1
+ (function () {
2
+ const HISTORY_LIMIT = 12
3
+
4
+ function create(waitRoot) {
5
+ const state = {
6
+ events: [],
7
+ phase: null,
8
+ router: null,
9
+ startupError: null,
10
+ }
11
+
12
+ function ensurePanel() {
13
+ if (!waitRoot) {
14
+ return null
15
+ }
16
+ let panel = waitRoot.querySelector(".wait-debug")
17
+ if (!panel) {
18
+ panel = document.createElement("div")
19
+ panel.className = "wait-debug"
20
+ panel.style.marginTop = "16px"
21
+ panel.style.padding = "12px"
22
+ panel.style.border = "1px solid rgba(255,255,255,0.12)"
23
+ panel.style.borderRadius = "8px"
24
+ panel.style.background = "rgba(255,255,255,0.04)"
25
+ panel.style.textAlign = "left"
26
+ panel.innerHTML = [
27
+ '<div style="font-size:12px;font-weight:600;letter-spacing:0.04em;text-transform:uppercase;opacity:0.75;margin-bottom:8px;">Debug</div>',
28
+ '<pre class="wait-debug-body" style="margin:0;text-align:left;white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;opacity:0.9;"></pre>'
29
+ ].join("")
30
+ waitRoot.appendChild(panel)
31
+ }
32
+ return panel.querySelector(".wait-debug-body")
33
+ }
34
+
35
+ function event(message) {
36
+ const timestamp = new Date().toLocaleTimeString()
37
+ const entry = `[${timestamp}] ${message}`
38
+ if (state.events[state.events.length - 1] !== entry) {
39
+ state.events.push(entry)
40
+ if (state.events.length > HISTORY_LIMIT) {
41
+ state.events.shift()
42
+ }
43
+ }
44
+ }
45
+
46
+ function track({ routerStatus, startupStatus } = {}) {
47
+ if (startupStatus && startupStatus.phase && startupStatus.phase !== state.phase) {
48
+ state.phase = startupStatus.phase
49
+ event(`phase -> ${startupStatus.phase}`)
50
+ }
51
+ const routerSummary = routerStatus
52
+ ? (routerStatus.success ? "success" : (routerStatus.error || JSON.stringify(routerStatus)))
53
+ : null
54
+ if (routerSummary && routerSummary !== state.router) {
55
+ state.router = routerSummary
56
+ event(`router -> ${routerSummary}`)
57
+ }
58
+ if (startupStatus && startupStatus.error && startupStatus.error !== state.startupError) {
59
+ state.startupError = startupStatus.error
60
+ event(`startup error -> ${startupStatus.error}`)
61
+ }
62
+ }
63
+
64
+ function render({ startedAt, routerStatus, startupStatus, serverDebug } = {}) {
65
+ const body = ensurePanel()
66
+ if (!body) {
67
+ return
68
+ }
69
+ const elapsedSeconds = startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1000)) : 0
70
+ const lines = [
71
+ `Elapsed: ${elapsedSeconds}s`,
72
+ `Startup phase: ${startupStatus && startupStatus.phase ? startupStatus.phase : "-"}`,
73
+ `Startup error: ${startupStatus && startupStatus.error ? startupStatus.error : "-"}`,
74
+ `Router check: ${routerStatus ? JSON.stringify(routerStatus) : "-"}`,
75
+ ]
76
+ if (serverDebug) {
77
+ lines.push(`Flags: ${JSON.stringify(serverDebug.flags || null)}`)
78
+ lines.push(`Caddy: ${JSON.stringify(serverDebug.caddy || null)}`)
79
+ if (serverDebug.caddy && Array.isArray(serverDebug.caddy.startup_output) && serverDebug.caddy.startup_output.length > 0) {
80
+ lines.push("Caddy startup output:")
81
+ lines.push(...serverDebug.caddy.startup_output)
82
+ }
83
+ lines.push(`Peer: ${JSON.stringify(serverDebug.peer || null)}`)
84
+ lines.push(`Router: ${JSON.stringify(serverDebug.router || null)}`)
85
+ lines.push(`Wait: ${JSON.stringify(serverDebug.wait || null)}`)
86
+ }
87
+ if (state.events.length > 0) {
88
+ lines.push("")
89
+ lines.push("Recent page events:")
90
+ lines.push(...state.events)
91
+ }
92
+ if (serverDebug && Array.isArray(serverDebug.events) && serverDebug.events.length > 0) {
93
+ lines.push("")
94
+ lines.push("Recent server events:")
95
+ for (const entry of serverDebug.events) {
96
+ lines.push(`[${entry.at}] ${entry.message}`)
97
+ }
98
+ }
99
+ body.textContent = lines.join("\n")
100
+ }
101
+
102
+ return {
103
+ event,
104
+ render,
105
+ track,
106
+ }
107
+ }
108
+
109
+ window.PinokioSetupWaitDebug = {
110
+ create
111
+ }
112
+ })();
@@ -13,6 +13,7 @@
13
13
  <% if (agent === "electron") { %>
14
14
  <link href="/electron.css" rel="stylesheet"/>
15
15
  <% } %>
16
+ <script src="/setup-wait-debug.js"></script>
16
17
  <style>
17
18
  html {
18
19
  scroll-behavior: smooth;
@@ -243,75 +244,44 @@ document.addEventListener("DOMContentLoaded", async () => {
243
244
  const waitRoot = document.querySelector(".requirements .content")
244
245
  const waitLabel = document.querySelector(".requirements .content .loading span")
245
246
  const WAIT_TIMEOUT_MS = 120000
246
- const WAIT_DEBUG_HISTORY_LIMIT = 12
247
- const waitDebugState = {
248
- events: [],
249
- phase: null,
250
- router: null,
251
- startupError: null,
252
- }
247
+ const waitDebug = window.PinokioSetupWaitDebug ? window.PinokioSetupWaitDebug.create(waitRoot) : null
248
+ let lastRouterStatus = null
249
+ let lastStartupStatus = null
250
+ let lastServerDebug = null
253
251
  const setWaitLabel = (message) => {
254
252
  if (waitLabel) {
255
253
  waitLabel.textContent = message
256
254
  }
257
255
  }
258
256
  const pushWaitDebugEvent = (message) => {
259
- const timestamp = new Date().toLocaleTimeString()
260
- const entry = `[${timestamp}] ${message}`
261
- if (waitDebugState.events[waitDebugState.events.length - 1] !== entry) {
262
- waitDebugState.events.push(entry)
263
- if (waitDebugState.events.length > WAIT_DEBUG_HISTORY_LIMIT) {
264
- waitDebugState.events.shift()
265
- }
257
+ if (waitDebug) {
258
+ waitDebug.event(message)
266
259
  }
267
260
  }
268
- const ensureWaitDebugPanel = () => {
269
- if (!waitRoot) {
270
- return null
261
+ const renderWaitDebug = ({ startedAt, routerStatus, startupStatus, serverDebug } = {}) => {
262
+ if (typeof routerStatus !== "undefined") {
263
+ lastRouterStatus = routerStatus
271
264
  }
272
- let panel = waitRoot.querySelector(".wait-debug")
273
- if (!panel) {
274
- panel = document.createElement("div")
275
- panel.className = "wait-debug"
276
- panel.style.marginTop = "16px"
277
- panel.style.padding = "12px"
278
- panel.style.border = "1px solid rgba(255,255,255,0.12)"
279
- panel.style.borderRadius = "8px"
280
- panel.style.background = "rgba(255,255,255,0.04)"
281
- panel.style.textAlign = "left"
282
- panel.innerHTML = `
283
- <div style="font-size:12px;font-weight:600;letter-spacing:0.04em;text-transform:uppercase;opacity:0.75;margin-bottom:8px;">Debug</div>
284
- <pre class="wait-debug-body" style="margin:0;text-align:left;white-space:pre-wrap;word-break:break-word;font-size:12px;line-height:1.5;opacity:0.9;"></pre>
285
- `
286
- waitRoot.appendChild(panel)
265
+ if (typeof startupStatus !== "undefined") {
266
+ lastStartupStatus = startupStatus
287
267
  }
288
- return panel.querySelector(".wait-debug-body")
289
- }
290
- const renderWaitDebug = ({ startedAt, routerStatus, startupStatus } = {}) => {
291
- const body = ensureWaitDebugPanel()
292
- if (!body) {
268
+ if (typeof serverDebug !== "undefined") {
269
+ lastServerDebug = serverDebug
270
+ }
271
+ if (!waitDebug) {
293
272
  return
294
273
  }
295
- const elapsedSeconds = startedAt ? Math.max(0, Math.floor((Date.now() - startedAt) / 1000)) : 0
296
- const lines = [
297
- `Elapsed: ${elapsedSeconds}s`,
298
- `Startup phase: ${startupStatus && startupStatus.phase ? startupStatus.phase : "-"}`,
299
- `Startup error: ${startupStatus && startupStatus.error ? startupStatus.error : "-"}`,
300
- `Router check: ${routerStatus ? JSON.stringify(routerStatus) : "-"}`,
301
- `Startup payload: ${startupStatus ? JSON.stringify({
302
- phase: startupStatus.phase || null,
303
- error: startupStatus.error || null,
304
- startup_pending: startupStatus.startup_pending,
305
- requirements_pending: startupStatus.requirements_pending,
306
- updated_at: startupStatus.updated_at || null
307
- }) : "-"}`,
308
- ]
309
- if (waitDebugState.events.length > 0) {
310
- lines.push("")
311
- lines.push("Recent events:")
312
- lines.push(...waitDebugState.events)
274
+ waitDebug.render({
275
+ startedAt,
276
+ routerStatus: lastRouterStatus,
277
+ startupStatus: lastStartupStatus,
278
+ serverDebug: lastServerDebug,
279
+ })
280
+ }
281
+ const trackWaitDebug = ({ routerStatus, startupStatus } = {}) => {
282
+ if (waitDebug) {
283
+ waitDebug.track({ routerStatus, startupStatus })
313
284
  }
314
- body.textContent = lines.join("\n")
315
285
  }
316
286
  const showWaitError = (message) => {
317
287
  if (waitRoot) {
@@ -369,9 +339,10 @@ document.addEventListener("DOMContentLoaded", async () => {
369
339
  await new Promise((resolve, reject) => {
370
340
  const interval = setInterval(async () => {
371
341
  try {
372
- const [routerStatus, startupStatus] = await Promise.all([
342
+ const [routerStatus, startupStatus, serverDebug] = await Promise.all([
373
343
  fetch("/check_router_up").then((res) => res.json()),
374
- fetch("/pinokio/startup_status").then((res) => res.json()).catch(() => null)
344
+ fetch("/pinokio/startup_status").then((res) => res.json()).catch(() => null),
345
+ fetch("/pinokio/secure_router_debug").then((res) => res.json()).catch(() => null)
375
346
  ])
376
347
 
377
348
  if (routerStatus && routerStatus.success) {
@@ -386,21 +357,7 @@ document.addEventListener("DOMContentLoaded", async () => {
386
357
  if (startupStatus && startupStatus.error) {
387
358
  lastKnownError = startupStatus.error
388
359
  }
389
- if (startupStatus && startupStatus.phase && startupStatus.phase !== waitDebugState.phase) {
390
- waitDebugState.phase = startupStatus.phase
391
- pushWaitDebugEvent(`phase -> ${startupStatus.phase}`)
392
- }
393
- const routerSummary = routerStatus
394
- ? (routerStatus.success ? "success" : (routerStatus.error || JSON.stringify(routerStatus)))
395
- : null
396
- if (routerSummary && routerSummary !== waitDebugState.router) {
397
- waitDebugState.router = routerSummary
398
- pushWaitDebugEvent(`router -> ${routerSummary}`)
399
- }
400
- if (startupStatus && startupStatus.error && startupStatus.error !== waitDebugState.startupError) {
401
- waitDebugState.startupError = startupStatus.error
402
- pushWaitDebugEvent(`startup error -> ${startupStatus.error}`)
403
- }
360
+ trackWaitDebug({ routerStatus, startupStatus })
404
361
  if (startupStatus && startupStatus.phase) {
405
362
  lastKnownPhase = startupStatus.phase
406
363
  const phaseMessage = startupStatus.phase === "ready"
@@ -408,7 +365,7 @@ document.addEventListener("DOMContentLoaded", async () => {
408
365
  : `Restarting Pinokio... (${startupStatus.phase.replace(/_/g, " ")})`
409
366
  setWaitLabel(phaseMessage)
410
367
  }
411
- renderWaitDebug({ startedAt, routerStatus, startupStatus })
368
+ renderWaitDebug({ startedAt, routerStatus, startupStatus, serverDebug })
412
369
 
413
370
  if (startupStatus && startupStatus.phase === "error") {
414
371
  clearInterval(interval)