pinokiod 7.1.52 → 7.1.61

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.52",
3
+ "version": "7.1.61",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -15492,6 +15492,7 @@ class Server {
15492
15492
  if (this.onrestart) {
15493
15493
  console.log("onrestart exists")
15494
15494
  this.onrestart()
15495
+ res.json({ success: true })
15495
15496
  } else {
15496
15497
  await this.start({ debug: this.debug, browser: this.browser })
15497
15498
  res.json({ success: true })
@@ -15499,7 +15500,10 @@ class Server {
15499
15500
  }))
15500
15501
  this.app.post("/restart", ex(async (req, res) => {
15501
15502
  console.log("post /restart")
15502
- this.start({ debug: this.debug, browser: this.browser })
15503
+ this.start({ debug: this.debug, browser: this.browser }).catch((error) => {
15504
+ console.error("[Pinokiod] restart failed", error && error.stack ? error.stack : error)
15505
+ })
15506
+ res.json({ success: true })
15503
15507
  }))
15504
15508
  this.app.post("/network", ex(async (req, res) => {
15505
15509
  if (this.kernel.homedir) {
@@ -44,9 +44,16 @@
44
44
  color: var(--connect-text);
45
45
  margin: 0;
46
46
  min-height: 100vh;
47
+ display: flex;
48
+ flex-direction: column;
47
49
  color-scheme: light;
48
50
  }
49
51
 
52
+ body[data-page="connect"][data-agent="electron"] {
53
+ padding-top: 26px;
54
+ box-sizing: border-box;
55
+ }
56
+
50
57
  @media (prefers-color-scheme: dark) {
51
58
  body[data-page="connect"] {
52
59
  --connect-body-bg: #010617;
@@ -101,6 +108,12 @@
101
108
  color-scheme: dark;
102
109
  }
103
110
 
111
+ body[data-page="connect"] main.connect-page-shell {
112
+ flex: 1 1 auto;
113
+ padding: 24px 20px 48px;
114
+ box-sizing: border-box;
115
+ }
116
+
104
117
  body[data-page="connect"] .container {
105
118
  background: var(--connect-card-bg);
106
119
  max-width: 600px;
@@ -261,7 +274,7 @@
261
274
  body[data-page="connect"] header.head {
262
275
  max-width: 600px;
263
276
  margin: 0 auto;
264
- padding: 50px;
277
+ padding: 16px 50px 36px;
265
278
  text-align: center;
266
279
  }
267
280
 
@@ -297,47 +310,23 @@
297
310
  <script src="/nav.js"></script>
298
311
  </head>
299
312
  <body class='<%=theme%>' data-agent="<%=agent%>" data-page="connect">
300
- <header class="navheader grabbable">
301
- <h1>
302
- <a class="home" href="/home"><img class="icon" src="/pinokio-black.png"></a>
303
- <button class="btn2" id="minimize-header" data-tippy-content="fullscreen" title="fullscreen">
304
- <div><i class="fa-solid fa-expand"></i></div>
305
- </button>
306
- <button class="btn2" id="back" data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
307
- <button class="btn2" id="forward" data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
308
- <button class="btn2" id="refresh-page" data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
309
- <button class="btn2" id="screenshot" data-tippy-content="screen capture"><i class="fa-solid fa-camera"></i></button>
310
- <button class="btn2" id="inspector" data-tippy-content="X-ray mode"><i class="fa-solid fa-eye"></i></button>
311
- <div class="flexible"></div>
312
- <a class="btn2" href="/columns" data-tippy-content="split into 2 columns">
313
- <div><i class="fa-solid fa-table-columns"></i></div>
314
- </a>
315
- <a class="btn2" href="/rows" data-tippy-content="split into 2 rows">
316
- <div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
317
- </a>
318
- <button class="btn2" id="new-window" data-tippy-content="open a new window" title="open a new window" data-agent="web">
319
- <div><i class="fa-solid fa-plus"></i></div>
320
- </button>
321
- <button class="btn2 hidden" id="close-window" data-tippy-content="close this section">
322
- <div><i class="fa-solid fa-xmark"></i></div>
323
- </button>
324
- </h1>
325
- </header>
326
- <header class='head'>
327
- <div class='logos'>
328
- <img class='logo' src="/pinokio-black.png"/>
329
- <div>+</div>
330
- <% if (name === "huggingface") { %>
331
- <div class='logo'>🤗</div>
332
- <% } else if (name === "x") { %>
333
- <i class="logo fa-brands fa-square-x-twitter"></i>
334
- <% } else if (name === "pinokio") { %>
313
+ <%- include('../partials/app_navheader', { agent }) %>
314
+ <main class="connect-page-shell">
315
+ <header class='head'>
316
+ <div class='logos'>
335
317
  <img class='logo' src="/pinokio-black.png"/>
336
- <% } %>
337
- </div>
338
- <h1><%=name%> Connect</h1>
339
- </header>
340
- <div class="container">
318
+ <div>+</div>
319
+ <% if (name === "huggingface") { %>
320
+ <div class='logo'>🤗</div>
321
+ <% } else if (name === "x") { %>
322
+ <i class="logo fa-brands fa-square-x-twitter"></i>
323
+ <% } else if (name === "pinokio") { %>
324
+ <img class='logo' src="/pinokio-black.png"/>
325
+ <% } %>
326
+ </div>
327
+ <h1><%=name%> Connect</h1>
328
+ </header>
329
+ <div class="container">
341
330
  <div id="status" class="status hidden">
342
331
  </div>
343
332
 
@@ -359,7 +348,8 @@
359
348
  </div>
360
349
  </div>
361
350
 
362
- </div>
351
+ </div>
352
+ </main>
363
353
 
364
354
  <div id="connect-loader" class="loader-overlay hidden">
365
355
  <div class="loader-spinner"></div>
@@ -369,6 +359,7 @@
369
359
 
370
360
  <script>
371
361
  const CONNECT_NAME = "<%=name%>"
362
+ const CONNECT_AGENT = document.body.getAttribute('data-agent') || 'web'
372
363
  const isSecureConnectContext = <%= protocol === "https" ? 'true' : 'false' %>
373
364
  const statusElement = document.getElementById('status')
374
365
  const loaderElement = document.getElementById('connect-loader')
@@ -565,6 +556,10 @@
565
556
  const PROFILE_URL = "<%=config.PROFILE_URL%>"
566
557
  const SCOPE = "<%=config.SCOPE%>"
567
558
  const name = "<%=name%>"
559
+ const SECURE_CONNECT_POLL_INTERVAL = 2000
560
+ const SECURE_CONNECT_TIMEOUT = 120000
561
+ let secureConnectPollTimer = null
562
+ let secureConnectTimeoutHandle = null
568
563
 
569
564
  function generateRandomString(length) {
570
565
  const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
@@ -599,8 +594,6 @@
599
594
  // OAuth functions
600
595
  async function login() {
601
596
  try {
602
- showLoader('Connecting...');
603
- setStatus(`Redirecting to ${name}...`, 'warning');
604
597
  // Clear existing data
605
598
  localStorage.removeItem('oauth_state');
606
599
  localStorage.removeItem('code_verifier');
@@ -626,8 +619,22 @@
626
619
  });
627
620
 
628
621
  const authUrl = OAUTH_URL + '?' + params.toString();
629
-
630
- // Redirect
622
+
623
+ if (CONNECT_AGENT === 'electron') {
624
+ startSecureConnectPolling();
625
+ setStatus(`Continue the ${name} login in your browser.`, 'warning');
626
+ try {
627
+ window.open(authUrl, '_blank', 'browser');
628
+ } catch (openError) {
629
+ hideLoader();
630
+ setStatus(`Could not open the browser. Please open ${authUrl} manually.`, 'error');
631
+ return;
632
+ }
633
+ return;
634
+ }
635
+
636
+ showLoader('Connecting...');
637
+ setStatus(`Redirecting to ${name}...`, 'warning');
631
638
  window.location.href = authUrl;
632
639
 
633
640
  } catch (error) {
@@ -637,6 +644,48 @@
637
644
  }
638
645
  }
639
646
 
647
+ function stopSecureConnectPolling(message = '', type = 'warning') {
648
+ if (secureConnectPollTimer) {
649
+ clearInterval(secureConnectPollTimer);
650
+ secureConnectPollTimer = null;
651
+ }
652
+ if (secureConnectTimeoutHandle) {
653
+ clearTimeout(secureConnectTimeoutHandle);
654
+ secureConnectTimeoutHandle = null;
655
+ }
656
+ if (message) {
657
+ hideLoader();
658
+ setStatus(message, type);
659
+ }
660
+ }
661
+
662
+ async function pollSecureConnectStatus() {
663
+ const token = await ensureValidToken();
664
+ if (!token) {
665
+ return false;
666
+ }
667
+ stopSecureConnectPolling();
668
+ localStorage.removeItem('oauth_state');
669
+ localStorage.removeItem('code_verifier');
670
+ await fetchUserInfo(token);
671
+ setStatus('Connected.', 'success');
672
+ return true;
673
+ }
674
+
675
+ function startSecureConnectPolling() {
676
+ stopSecureConnectPolling();
677
+ showLoader('Waiting for authentication in the browser...', {
678
+ cancellable: true,
679
+ onCancel: () => stopSecureConnectPolling('Connection cancelled.', 'warning')
680
+ });
681
+ setStatus(`Waiting for ${name} authentication to finish...`, 'warning');
682
+ pollSecureConnectStatus();
683
+ secureConnectPollTimer = setInterval(pollSecureConnectStatus, SECURE_CONNECT_POLL_INTERVAL);
684
+ secureConnectTimeoutHandle = setTimeout(() => {
685
+ stopSecureConnectPolling('Timed out waiting for the browser login to finish. Please try again.', 'error');
686
+ }, SECURE_CONNECT_TIMEOUT);
687
+ }
688
+
640
689
  async function handleOAuthCallback(code) {
641
690
  showLoader('Finishing connection...');
642
691
  try {
@@ -670,6 +719,7 @@
670
719
  console.log('Token response:', response)
671
720
  // Store tokens
672
721
  if (response.access_token) {
722
+ stopSecureConnectPolling();
673
723
  // Cleanup
674
724
  localStorage.removeItem('oauth_state');
675
725
  localStorage.removeItem('code_verifier');
@@ -721,11 +771,13 @@
721
771
  await handleOAuthCallback(code);
722
772
  return;
723
773
  }
724
-
774
+
725
775
  // Check existing session with automatic refresh
726
776
  const token = await ensureValidToken();
727
777
  if (token) {
728
778
  await fetchUserInfo(token);
779
+ } else if (CONNECT_AGENT === 'electron' && localStorage.getItem('oauth_state')) {
780
+ startSecureConnectPolling();
729
781
  } else {
730
782
  setStatus('Not authenticated', 'warning');
731
783
  }
@@ -240,38 +240,113 @@ document.addEventListener("DOMContentLoaded", async () => {
240
240
  location.href = location.href
241
241
  <% } %>
242
242
  <% if (!requirements_pending && !install_required && wait === "caddy") { %>
243
- console.log("RESTART")
244
- alert("RESTART")
243
+ const waitRoot = document.querySelector(".requirements .content")
244
+ const waitLabel = document.querySelector(".requirements .content .loading span")
245
+ const WAIT_TIMEOUT_MS = 120000
246
+ const setWaitLabel = (message) => {
247
+ if (waitLabel) {
248
+ waitLabel.textContent = message
249
+ }
250
+ }
251
+ const showWaitError = (message) => {
252
+ if (waitRoot) {
253
+ waitRoot.innerHTML = `
254
+ <div class='loading'>
255
+ <i class="fa-solid fa-circle-exclamation"></i>
256
+ <span>${message}</span>
257
+ </div>
258
+ <div class='item'>
259
+ <a class='btn' href='${location.href}'>Retry</a>
260
+ </div>
261
+ `
262
+ } else {
263
+ alert(message)
264
+ }
265
+ }
245
266
 
246
- let r = await fetch("/network", {
247
- method: "post",
248
- headers: { "Content-Type": "application/json" },
249
- body: JSON.stringify({
250
- PINOKIO_NETWORK_ACTIVE: "1",
251
- PINOKIO_HTTPS_ACTIVE: "1",
267
+ try {
268
+ setWaitLabel("Enabling secure localhost access...")
269
+ const networkResponse = await fetch("/network", {
270
+ method: "post",
271
+ headers: { "Content-Type": "application/json" },
272
+ body: JSON.stringify({
273
+ PINOKIO_NETWORK_ACTIVE: "1",
274
+ PINOKIO_HTTPS_ACTIVE: "1",
275
+ })
276
+ }).then((res) => {
277
+ return res.json()
252
278
  })
253
- }).then((res) => {
254
- return res.json()
255
- })
256
- fetch("/restart", {
257
- method: "post"
258
- }, () => {
259
- })
260
- console.log("Fetch finished")
261
- await new Promise((resolve, reject) => {
262
- let interval = setInterval(() => {
263
- fetch("/check_router_up").then((res) => {
264
- return res.json()
265
- }).then((res) => {
266
- console.log(res)
267
- if (res.success) {
268
- clearInterval(interval)
269
- resolve()
270
- }
279
+ if (networkResponse && networkResponse.error) {
280
+ throw new Error(networkResponse.error)
281
+ }
282
+
283
+ setWaitLabel("Restarting Pinokio...")
284
+ try {
285
+ await fetch("/restart", {
286
+ method: "post"
271
287
  })
272
- }, 500)
273
- })
274
- location.href = "<%=current%>"
288
+ } catch (error) {
289
+ console.warn("restart request ended early", error)
290
+ }
291
+
292
+ const startedAt = Date.now()
293
+ let lastKnownError = null
294
+ let lastKnownPhase = null
295
+
296
+ await new Promise((resolve, reject) => {
297
+ const interval = setInterval(async () => {
298
+ try {
299
+ const [routerStatus, startupStatus] = await Promise.all([
300
+ fetch("/check_router_up").then((res) => res.json()),
301
+ fetch("/pinokio/startup_status").then((res) => res.json()).catch(() => null)
302
+ ])
303
+
304
+ if (routerStatus && routerStatus.success) {
305
+ clearInterval(interval)
306
+ resolve()
307
+ return
308
+ }
309
+
310
+ if (routerStatus && routerStatus.error) {
311
+ lastKnownError = routerStatus.error
312
+ }
313
+ if (startupStatus && startupStatus.error) {
314
+ lastKnownError = startupStatus.error
315
+ }
316
+ if (startupStatus && startupStatus.phase) {
317
+ lastKnownPhase = startupStatus.phase
318
+ const phaseMessage = startupStatus.phase === "ready"
319
+ ? "Secure router is almost ready..."
320
+ : `Restarting Pinokio... (${startupStatus.phase.replace(/_/g, " ")})`
321
+ setWaitLabel(phaseMessage)
322
+ }
323
+
324
+ if (startupStatus && startupStatus.phase === "error") {
325
+ clearInterval(interval)
326
+ reject(new Error(lastKnownError || "Pinokio failed to restart cleanly"))
327
+ return
328
+ }
329
+
330
+ if ((Date.now() - startedAt) >= WAIT_TIMEOUT_MS) {
331
+ clearInterval(interval)
332
+ const detail = lastKnownError || lastKnownPhase || "secure localhost router did not come up"
333
+ reject(new Error(`Timed out waiting for restart: ${detail}`))
334
+ }
335
+ } catch (error) {
336
+ lastKnownError = error && error.message ? error.message : String(error)
337
+ if ((Date.now() - startedAt) >= WAIT_TIMEOUT_MS) {
338
+ clearInterval(interval)
339
+ reject(new Error(`Timed out waiting for restart: ${lastKnownError}`))
340
+ }
341
+ }
342
+ }, 1000)
343
+ })
344
+
345
+ location.href = "<%=current%>"
346
+ } catch (error) {
347
+ console.error("secure router setup failed", error)
348
+ showWaitError(error && error.message ? error.message : "Failed to restart Pinokio")
349
+ }
275
350
  <% } %>
276
351
  if (document.querySelector("#del-bin")) {
277
352
  document.querySelector("#del-bin").addEventListener("click", async (e) => {