pinokiod 7.1.60 → 7.1.62

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.60",
3
+ "version": "7.1.62",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -11367,7 +11367,8 @@ class Server {
11367
11367
  name: "x",
11368
11368
  title: "x.com",
11369
11369
  description: "Connect with X.com",
11370
- url: "/connect/x"
11370
+ url: "/connect/x",
11371
+ hidden: true
11371
11372
  }]
11372
11373
  let github_hosts = await this.get_github_hosts()
11373
11374
  for(let i=0; i<items.length; i++) {
@@ -11408,7 +11409,7 @@ class Server {
11408
11409
  logo: this.logo,
11409
11410
  theme: this.theme,
11410
11411
  agent: req.agent,
11411
- items,
11412
+ items: items.filter((item) => !item.hidden),
11412
11413
  })
11413
11414
  }))
11414
11415
  /*
@@ -15492,6 +15493,7 @@ class Server {
15492
15493
  if (this.onrestart) {
15493
15494
  console.log("onrestart exists")
15494
15495
  this.onrestart()
15496
+ res.json({ success: true })
15495
15497
  } else {
15496
15498
  await this.start({ debug: this.debug, browser: this.browser })
15497
15499
  res.json({ success: true })
@@ -15499,7 +15501,10 @@ class Server {
15499
15501
  }))
15500
15502
  this.app.post("/restart", ex(async (req, res) => {
15501
15503
  console.log("post /restart")
15502
- this.start({ debug: this.debug, browser: this.browser })
15504
+ this.start({ debug: this.debug, browser: this.browser }).catch((error) => {
15505
+ console.error("[Pinokiod] restart failed", error && error.stack ? error.stack : error)
15506
+ })
15507
+ res.json({ success: true })
15503
15508
  }))
15504
15509
  this.app.post("/network", ex(async (req, res) => {
15505
15510
  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';
@@ -596,11 +591,24 @@
596
591
  .replace(/=/g, '');
597
592
  }
598
593
 
594
+ async function openAuthInBrowser(authUrl) {
595
+ const response = await fetch('/go', {
596
+ method: 'POST',
597
+ headers: { 'Content-Type': 'application/json' },
598
+ body: JSON.stringify({ url: authUrl })
599
+ });
600
+ if (!response.ok) {
601
+ throw new Error(`Browser launch failed with status ${response.status}`);
602
+ }
603
+ const payload = await response.json().catch(() => ({}));
604
+ if (payload && payload.error) {
605
+ throw new Error(payload.error);
606
+ }
607
+ }
608
+
599
609
  // OAuth functions
600
610
  async function login() {
601
611
  try {
602
- showLoader('Connecting...');
603
- setStatus(`Redirecting to ${name}...`, 'warning');
604
612
  // Clear existing data
605
613
  localStorage.removeItem('oauth_state');
606
614
  localStorage.removeItem('code_verifier');
@@ -626,8 +634,21 @@
626
634
  });
627
635
 
628
636
  const authUrl = OAUTH_URL + '?' + params.toString();
629
-
630
- // Redirect
637
+
638
+ if (CONNECT_AGENT === 'electron') {
639
+ startSecureConnectPolling();
640
+ setStatus(`Continue the ${name} login in your browser.`, 'warning');
641
+ try {
642
+ await openAuthInBrowser(authUrl);
643
+ } catch (openError) {
644
+ stopSecureConnectPolling(`Could not open the browser automatically. Please open ${authUrl} manually.`, 'error');
645
+ return;
646
+ }
647
+ return;
648
+ }
649
+
650
+ showLoader('Connecting...');
651
+ setStatus(`Redirecting to ${name}...`, 'warning');
631
652
  window.location.href = authUrl;
632
653
 
633
654
  } catch (error) {
@@ -637,6 +658,48 @@
637
658
  }
638
659
  }
639
660
 
661
+ function stopSecureConnectPolling(message = '', type = 'warning') {
662
+ if (secureConnectPollTimer) {
663
+ clearInterval(secureConnectPollTimer);
664
+ secureConnectPollTimer = null;
665
+ }
666
+ if (secureConnectTimeoutHandle) {
667
+ clearTimeout(secureConnectTimeoutHandle);
668
+ secureConnectTimeoutHandle = null;
669
+ }
670
+ if (message) {
671
+ hideLoader();
672
+ setStatus(message, type);
673
+ }
674
+ }
675
+
676
+ async function pollSecureConnectStatus() {
677
+ const token = await ensureValidToken();
678
+ if (!token) {
679
+ return false;
680
+ }
681
+ stopSecureConnectPolling();
682
+ localStorage.removeItem('oauth_state');
683
+ localStorage.removeItem('code_verifier');
684
+ await fetchUserInfo(token);
685
+ setStatus('Connected.', 'success');
686
+ return true;
687
+ }
688
+
689
+ function startSecureConnectPolling() {
690
+ stopSecureConnectPolling();
691
+ showLoader('Waiting for authentication in the browser...', {
692
+ cancellable: true,
693
+ onCancel: () => stopSecureConnectPolling('Connection cancelled.', 'warning')
694
+ });
695
+ setStatus(`Waiting for ${name} authentication to finish...`, 'warning');
696
+ pollSecureConnectStatus();
697
+ secureConnectPollTimer = setInterval(pollSecureConnectStatus, SECURE_CONNECT_POLL_INTERVAL);
698
+ secureConnectTimeoutHandle = setTimeout(() => {
699
+ stopSecureConnectPolling('Timed out waiting for the browser login to finish. Please try again.', 'error');
700
+ }, SECURE_CONNECT_TIMEOUT);
701
+ }
702
+
640
703
  async function handleOAuthCallback(code) {
641
704
  showLoader('Finishing connection...');
642
705
  try {
@@ -670,6 +733,7 @@
670
733
  console.log('Token response:', response)
671
734
  // Store tokens
672
735
  if (response.access_token) {
736
+ stopSecureConnectPolling();
673
737
  // Cleanup
674
738
  localStorage.removeItem('oauth_state');
675
739
  localStorage.removeItem('code_verifier');
@@ -721,11 +785,13 @@
721
785
  await handleOAuthCallback(code);
722
786
  return;
723
787
  }
724
-
788
+
725
789
  // Check existing session with automatic refresh
726
790
  const token = await ensureValidToken();
727
791
  if (token) {
728
792
  await fetchUserInfo(token);
793
+ } else if (CONNECT_AGENT === 'electron' && localStorage.getItem('oauth_state')) {
794
+ startSecureConnectPolling();
729
795
  } else {
730
796
  setStatus('Not authenticated', 'warning');
731
797
  }
@@ -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) => {