pinokiod 7.1.69 → 7.1.70

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.1.69",
3
+ "version": "7.1.70",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -79,6 +79,8 @@ 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")
83
+ const { ensureSecureRouterReady } = require("./lib/secure_router_ready")
82
84
  const AppRegistryService = require("./lib/app_registry")
83
85
  const AppLogService = require("./lib/app_logs")
84
86
  const AppSearchService = require("./lib/app_search")
@@ -253,6 +255,7 @@ class Server {
253
255
  updated_at: new Date().toISOString()
254
256
  }
255
257
  this.startup_status_run_id = 0
258
+ this.secure_router_debug = createSecureRouterDebugStore()
256
259
  this.installFatalHandlers()
257
260
  }
258
261
  setStartupStatus(patch = {}) {
@@ -5518,42 +5521,7 @@ class Server {
5518
5521
  return { success: true }
5519
5522
  }
5520
5523
  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")
5524
+ return ensureSecureRouterReady(this, timeout, interval)
5557
5525
  }
5558
5526
  async start(options) {
5559
5527
  this.debug = false
@@ -15483,6 +15451,9 @@ class Server {
15483
15451
  this.app.get("/pinokio/startup_status", ex((req, res) => {
15484
15452
  res.json(this.getStartupStatus())
15485
15453
  }))
15454
+ this.app.get("/pinokio/secure_router_debug", ex(async (req, res) => {
15455
+ res.json(await buildSecureRouterDebugSnapshot(this, this.secure_router_debug))
15456
+ }))
15486
15457
  this.app.get("/pinokio/requirements_ready", ex((req, res) => {
15487
15458
  res.json(this.getStartupStatus())
15488
15459
  }))
@@ -0,0 +1,116 @@
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 state = store && store.state ? clone(store.state) : {}
76
+ const events = store && Array.isArray(store.events) ? clone(store.events) : []
77
+ return {
78
+ wait: state,
79
+ flags: {
80
+ peer_active: peer ? !!peer.peer_active : null,
81
+ https_active: peer ? !!peer.https_active : null,
82
+ active: peer ? !!peer.active : null,
83
+ },
84
+ caddy: {
85
+ installed: caddyInstalled,
86
+ running: caddyRunning,
87
+ start_attempted: !!state.caddy_start_attempted,
88
+ start_finished: !!state.caddy_start_finished,
89
+ start_error: state.caddy_start_error || null,
90
+ },
91
+ peer: {
92
+ host: peer && peer.host ? peer.host : null,
93
+ name: peer && peer.name ? peer.name : null,
94
+ has_info: !!(peer && peer.info),
95
+ has_host_info: !!(peer && peer.info && peer.host && peer.info[peer.host]),
96
+ refreshing: !!(peer && peer.refreshing),
97
+ },
98
+ router: {
99
+ published_count: routerDialKeys.length,
100
+ published_dials: routerDialKeys.slice(0, 10),
101
+ has_pinokio_localhost: hasPinokioLocalhost,
102
+ refresh_attempted: !!state.refresh_attempted,
103
+ refresh_finished: !!state.refresh_finished,
104
+ refresh_error: state.refresh_error || null,
105
+ last_status: state.last_status || null,
106
+ },
107
+ startup: startupStatus,
108
+ events
109
+ }
110
+ }
111
+
112
+ module.exports = {
113
+ buildSecureRouterDebugSnapshot,
114
+ createSecureRouterDebugStore,
115
+ recordSecureRouterDebug,
116
+ }
@@ -0,0 +1,96 @@
1
+ const { createSecureRouterDebugStore, recordSecureRouterDebug } = require("./secure_router_debug")
2
+
3
+ async function ensureSecureRouterReady(server, timeout = 120000, interval = 500) {
4
+ if (!(server.kernel && server.kernel.peer && server.kernel.peer.https_active)) {
5
+ return { success: true, stage: "disabled" }
6
+ }
7
+ server.secure_router_debug = createSecureRouterDebugStore()
8
+ recordSecureRouterDebug(server.secure_router_debug, "wait started", {
9
+ active: true,
10
+ started_at: new Date().toISOString(),
11
+ last_status: null,
12
+ caddy_start_attempted: false,
13
+ caddy_start_finished: false,
14
+ caddy_start_error: null,
15
+ refresh_attempted: false,
16
+ refresh_finished: false,
17
+ refresh_error: null,
18
+ })
19
+ const caddy = server.kernel && server.kernel.bin && server.kernel.bin.mod
20
+ ? server.kernel.bin.mod.caddy
21
+ : null
22
+ const deadline = Date.now() + timeout
23
+ let startedCaddyManually = false
24
+ let refreshedRouter = false
25
+ let lastStatus = null
26
+ let lastStatusSummary = null
27
+ while (Date.now() < deadline) {
28
+ lastStatus = await server.check_router_up()
29
+ const statusSummary = lastStatus && lastStatus.success
30
+ ? "success"
31
+ : (lastStatus && lastStatus.error ? lastStatus.error : JSON.stringify(lastStatus))
32
+ if (statusSummary !== lastStatusSummary) {
33
+ lastStatusSummary = statusSummary
34
+ recordSecureRouterDebug(server.secure_router_debug, `check_router_up -> ${statusSummary}`, {
35
+ last_status: lastStatus
36
+ })
37
+ }
38
+ if (lastStatus && lastStatus.success) {
39
+ recordSecureRouterDebug(server.secure_router_debug, "wait success", {
40
+ active: false
41
+ })
42
+ return lastStatus
43
+ }
44
+ if (
45
+ lastStatus &&
46
+ lastStatus.error === "caddy admin unavailable" &&
47
+ !startedCaddyManually &&
48
+ caddy &&
49
+ typeof caddy.start === "function"
50
+ ) {
51
+ startedCaddyManually = true
52
+ recordSecureRouterDebug(server.secure_router_debug, "manual caddy.start() attempted", {
53
+ caddy_start_attempted: true
54
+ })
55
+ try {
56
+ await caddy.start()
57
+ recordSecureRouterDebug(server.secure_router_debug, "manual caddy.start() finished", {
58
+ caddy_start_finished: true
59
+ })
60
+ } catch (e) {
61
+ recordSecureRouterDebug(server.secure_router_debug, "manual caddy.start() failed", {
62
+ caddy_start_error: e && e.message ? e.message : String(e)
63
+ })
64
+ throw e
65
+ }
66
+ continue
67
+ }
68
+ if (!refreshedRouter) {
69
+ refreshedRouter = true
70
+ recordSecureRouterDebug(server.secure_router_debug, "kernel.refresh(true) attempted", {
71
+ refresh_attempted: true
72
+ })
73
+ await server.kernel.refresh(true).then(() => {
74
+ recordSecureRouterDebug(server.secure_router_debug, "kernel.refresh(true) finished", {
75
+ refresh_finished: true
76
+ })
77
+ }).catch((e) => {
78
+ recordSecureRouterDebug(server.secure_router_debug, "kernel.refresh(true) failed", {
79
+ refresh_error: e && e.message ? e.message : String(e)
80
+ })
81
+ })
82
+ continue
83
+ }
84
+ await new Promise((resolve) => {
85
+ setTimeout(resolve, interval)
86
+ })
87
+ }
88
+ recordSecureRouterDebug(server.secure_router_debug, "wait timeout", {
89
+ active: false
90
+ })
91
+ throw new Error(lastStatus && lastStatus.error ? lastStatus.error : "secure router did not come up")
92
+ }
93
+
94
+ module.exports = {
95
+ ensureSecureRouterReady,
96
+ }
@@ -0,0 +1,108 @@
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
+ lines.push(`Peer: ${JSON.stringify(serverDebug.peer || null)}`)
80
+ lines.push(`Router: ${JSON.stringify(serverDebug.router || null)}`)
81
+ lines.push(`Wait: ${JSON.stringify(serverDebug.wait || null)}`)
82
+ }
83
+ if (state.events.length > 0) {
84
+ lines.push("")
85
+ lines.push("Recent page events:")
86
+ lines.push(...state.events)
87
+ }
88
+ if (serverDebug && Array.isArray(serverDebug.events) && serverDebug.events.length > 0) {
89
+ lines.push("")
90
+ lines.push("Recent server events:")
91
+ for (const entry of serverDebug.events) {
92
+ lines.push(`[${entry.at}] ${entry.message}`)
93
+ }
94
+ }
95
+ body.textContent = lines.join("\n")
96
+ }
97
+
98
+ return {
99
+ event,
100
+ render,
101
+ track,
102
+ }
103
+ }
104
+
105
+ window.PinokioSetupWaitDebug = {
106
+ create
107
+ }
108
+ })();
@@ -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)