pinokiod 7.3.9 → 7.3.11

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 (52) hide show
  1. package/kernel/bin/brew.js +12 -2
  2. package/kernel/bin/caddy.js +24 -20
  3. package/kernel/bin/huggingface.js +2 -2
  4. package/kernel/bin/setup.js +2 -3
  5. package/kernel/bin/uv.js +13 -6
  6. package/kernel/connect/index.js +5 -1
  7. package/kernel/connect/providers/huggingface/index.js +213 -75
  8. package/kernel/environment.js +16 -1
  9. package/kernel/router/localhost_home_router.js +7 -0
  10. package/kernel/shell.js +1 -5
  11. package/kernel/util.js +1 -0
  12. package/package.json +1 -1
  13. package/server/index.js +75 -33
  14. package/server/public/common.js +52 -88
  15. package/server/public/install.js +20 -2
  16. package/server/public/layout.js +1 -1
  17. package/server/public/nav.js +3 -1
  18. package/server/public/style.css +1455 -521
  19. package/server/public/tab-link-popover.css +162 -18
  20. package/server/public/tab-link-popover.js +230 -21
  21. package/server/public/task-launcher.css +182 -91
  22. package/server/public/terminal-settings.js +227 -50
  23. package/server/public/universal-launcher.css +42 -33
  24. package/server/public/urldropdown.css +284 -0
  25. package/server/views/app.ejs +1718 -352
  26. package/server/views/autolaunch.ejs +4 -5
  27. package/server/views/checkpoints.ejs +223 -50
  28. package/server/views/connect/huggingface.ejs +406 -325
  29. package/server/views/connect.ejs +0 -1
  30. package/server/views/github.ejs +277 -324
  31. package/server/views/index.ejs +65 -8
  32. package/server/views/install.ejs +134 -65
  33. package/server/views/logs.ejs +9 -8
  34. package/server/views/net.ejs +341 -64
  35. package/server/views/network.ejs +85 -63
  36. package/server/views/partials/main_sidebar.ejs +249 -24
  37. package/server/views/plugins.ejs +141 -3
  38. package/server/views/settings.ejs +103 -7
  39. package/server/views/setup.ejs +0 -5
  40. package/server/views/skills.ejs +0 -1
  41. package/server/views/task_list.ejs +0 -1
  42. package/server/views/terminal.ejs +285 -60
  43. package/server/views/terminals.ejs +346 -6
  44. package/server/views/tools.ejs +828 -1691
  45. package/test/caddy-install.test.js +53 -0
  46. package/test/connect-setup.test.js +16 -0
  47. package/test/github-connection.test.js +1 -1
  48. package/test/huggingface-bin.test.js +4 -4
  49. package/test/huggingface-connect.test.js +73 -0
  50. package/test/main-sidebar.test.js +31 -0
  51. package/test/shell-run-template.test.js +5 -1
  52. package/test/uv-bin.test.js +29 -0
@@ -509,9 +509,9 @@ aside .btn-tab .btn .caption {
509
509
  position: relative;
510
510
  align-items: center;
511
511
  gap: 5px;
512
- height: 30px;
513
- min-height: 30px;
514
- padding: 0 10px;
512
+ height: var(--home-filter-control-height);
513
+ min-height: var(--home-filter-control-height);
514
+ padding: 0 12px;
515
515
  border: 1px solid var(--home-sort-border);
516
516
  border-radius: 6px;
517
517
  background: var(--home-sort-bg);
@@ -581,9 +581,12 @@ aside .btn-tab .btn .caption {
581
581
  flex: 0 0 auto;
582
582
  width: 132px;
583
583
  min-width: 132px;
584
+ height: 100%;
584
585
  }
585
586
 
586
587
  .home-search-form .home-apps-sort .ts-wrapper.single .ts-control {
588
+ display: flex;
589
+ align-items: center;
587
590
  border: 0;
588
591
  outline: none;
589
592
  background: transparent;
@@ -595,6 +598,7 @@ aside .btn-tab .btn .caption {
595
598
  border-radius: 0;
596
599
  cursor: pointer;
597
600
  color: inherit;
601
+ height: 100%;
598
602
  }
599
603
 
600
604
  .home-search-form .home-apps-sort .ts-control > .item {
@@ -704,6 +708,7 @@ body.dark .home-search-form .home-apps-sort .ts-dropdown [data-selectable].optio
704
708
  color: var(--home-sort-option-hover-text);
705
709
  }
706
710
  .home-search-form {
711
+ --home-filter-control-height: 40px;
707
712
  align-items: center;
708
713
  gap: 8px;
709
714
  }
@@ -731,11 +736,11 @@ body.dark .home-search-form {
731
736
  border-bottom-color: rgba(148, 163, 184, 0.22);
732
737
  }
733
738
  .home-search-form input[type='search'].flexible {
734
- height: 30px;
735
- min-height: 30px;
739
+ height: var(--home-filter-control-height);
740
+ min-height: var(--home-filter-control-height);
736
741
  box-sizing: border-box;
737
- padding: 0 10px;
738
- border-radius: 5px;
742
+ padding: 0 12px;
743
+ border-radius: 6px;
739
744
  font-size: 12px;
740
745
  font-weight: 400;
741
746
  line-height: 1;
@@ -1150,12 +1155,64 @@ body.dark aside .current.selected {
1150
1155
  */
1151
1156
  @media only screen and (max-width: 760px) {
1152
1157
  .home-search-form {
1153
- flex-wrap: wrap;
1158
+ flex-wrap: nowrap;
1154
1159
  gap: 8px;
1155
1160
  }
1161
+ .home-search-form input[type='search'].flexible {
1162
+ min-width: 0;
1163
+ flex: 1 1 auto;
1164
+ }
1156
1165
  .home-search-form .home-apps-sort {
1166
+ width: var(--home-filter-control-height);
1167
+ min-width: var(--home-filter-control-height);
1168
+ padding: 0;
1169
+ justify-content: center;
1157
1170
  margin-left: 0;
1158
1171
  }
1172
+ .home-search-form .home-apps-sort label,
1173
+ .home-search-form .home-apps-sort .ts-control > .item {
1174
+ position: absolute;
1175
+ width: 1px;
1176
+ height: 1px;
1177
+ margin: -1px;
1178
+ padding: 0;
1179
+ overflow: hidden;
1180
+ clip: rect(0 0 0 0);
1181
+ white-space: nowrap;
1182
+ border: 0;
1183
+ }
1184
+ .home-search-form .home-apps-sort .home-apps-sort-icon {
1185
+ font-size: 14px;
1186
+ }
1187
+ .home-search-form .home-apps-sort .home-apps-sort-caret {
1188
+ position: absolute;
1189
+ right: 7px;
1190
+ bottom: 7px;
1191
+ font-size: 7px;
1192
+ }
1193
+ .home-search-form .home-apps-sort select {
1194
+ position: absolute;
1195
+ inset: 0;
1196
+ width: 100%;
1197
+ min-width: 0;
1198
+ opacity: 0;
1199
+ z-index: 1;
1200
+ }
1201
+ .home-search-form .home-apps-sort .ts-wrapper {
1202
+ position: absolute;
1203
+ inset: 0;
1204
+ width: 100%;
1205
+ min-width: 0;
1206
+ height: 100%;
1207
+ opacity: 0;
1208
+ z-index: 1;
1209
+ }
1210
+ .home-search-form .home-apps-sort .ts-dropdown {
1211
+ left: auto !important;
1212
+ right: 0 !important;
1213
+ width: 164px !important;
1214
+ min-width: 164px !important;
1215
+ }
1159
1216
  header .flexible {
1160
1217
  min-width: unset;
1161
1218
  }
@@ -17,22 +17,33 @@
17
17
  document.documentElement.dataset.installSpinnerVariant = "grid-shift"
18
18
  </script>
19
19
  <style>
20
- body {
20
+ html {
21
+ height: 100%;
22
+ }
23
+ body.install-page {
21
24
  --install-status-bg: var(--pinokio-chrome-accent-bg-light);
22
25
  --install-spinner-fg: rgba(0,0,0,0.8);
23
26
  --install-status-fg: var(--pinokio-chrome-accent-fg-light);
24
27
  --install-status-fg-soft: rgba(225, 178, 97, 0.34);
25
28
  --install-status-border: rgba(15, 23, 42, 0.12);
26
29
  --install-status-detail: rgba(0, 0, 0, 0.6);
27
- height: 100%;
30
+ height: 100vh;
31
+ min-height: 100vh;
28
32
  overflow: hidden;
29
33
  /*
30
34
  background: rgb(44, 29, 22);
31
35
  */
32
36
  display: flex;
33
- flex-direction: column;
37
+ flex-direction: column !important;
38
+ align-items: stretch;
34
39
  }
35
- body.dark {
40
+ @supports (height: 100dvh) {
41
+ body.install-page {
42
+ height: 100dvh;
43
+ min-height: 100dvh;
44
+ }
45
+ }
46
+ body.install-page.dark {
36
47
  --install-status-bg: var(--pinokio-chrome-accent-bg-dark);
37
48
  --install-spinner-fg: white;
38
49
  --install-status-fg: var(--pinokio-chrome-accent-fg-dark);
@@ -40,8 +51,18 @@ body.dark {
40
51
  --install-status-border: rgba(255, 255, 255, 0.08);
41
52
  --install-status-detail: rgba(239, 233, 214, 0.76);
42
53
  }
54
+ body.install-page > header.grabbable {
55
+ flex: 0 0 auto;
56
+ }
57
+ body.install-page > .terminal-container {
58
+ flex: 1 1 auto;
59
+ min-height: 0;
60
+ overflow: hidden;
61
+ }
43
62
  #terminal {
44
63
  flex-grow: 1;
64
+ height: 100%;
65
+ min-height: 0;
45
66
  }
46
67
  .line2 {
47
68
  display: flex;
@@ -111,7 +132,9 @@ body.dark {
111
132
  color: rgba(0,0,0,0.5);
112
133
  }
113
134
  #status-screen {
135
+ flex: 0 0 auto;
114
136
  padding: 14px 18px;
137
+ padding-bottom: calc(14px + env(safe-area-inset-bottom));
115
138
  box-sizing: border-box;
116
139
  width: 100%;
117
140
  margin: 0;
@@ -348,25 +371,6 @@ body.dark {
348
371
  [data-install-spinner-variant="binary-shuffle"] .install-status-grid-tile-4 {
349
372
  animation: pinokio-binary-shuffle-4 1.1s steps(1, end) infinite;
350
373
  }
351
- .install-loader-popup.swal2-popup.loader-popup {
352
- background: var(--install-status-bg) !important;
353
- color: var(--install-status-fg) !important;
354
- border: 1px solid var(--install-status-border) !important;
355
- border-radius: 999px !important;
356
- box-shadow: none !important;
357
- }
358
- .install-loader-dialog.loader-dialog {
359
- padding: 0 !important;
360
- }
361
- .install-loader-shell {
362
- display: inline-flex;
363
- align-items: center;
364
- gap: 10px;
365
- color: var(--install-status-fg);
366
- font-size: 13px;
367
- font-weight: 700;
368
- letter-spacing: 0.01em;
369
- }
370
374
  @keyframes pinokio-grid-shift-1 {
371
375
  0%, 100% { transform: translate(0, 0); }
372
376
  25% { transform: translate(11px, 0); }
@@ -506,7 +510,7 @@ body.dark {
506
510
  <script src="/waitmodal.js"></script>
507
511
  <script src="/notifyinput.js"></script>
508
512
  </head>
509
- <body class='<%=theme%>' data-agent="<%=agent%>">
513
+ <body class='install-page <%=theme%>' data-agent="<%=agent%>">
510
514
  <header class='grabbable'></header>
511
515
  <div class='terminal-container'>
512
516
  <div id='terminal'></div>
@@ -641,13 +645,88 @@ const setInstallStatus = (html) => {
641
645
  }
642
646
  statusScreen.innerHTML = normalizeInstallStatusHtml(html)
643
647
  }
644
- const renderInstallLoader = (msg) => {
645
- return `
646
- <span class="install-loader-shell">
648
+ const setRestartStatus = () => {
649
+ applyInstallSpinnerVariant("corner-chase")
650
+ setInstallStatus(`
651
+ <div class="install-status-shell">
647
652
  ${INSTALL_STATUS_SPINNER_HTML}
648
- <span>${msg}</span>
649
- </span>
650
- `
653
+ <div class="install-status-copy">
654
+ <div class="install-status-title">Restarting Pinokio...</div>
655
+ <div class="install-status-detail">Applying the updated system tools. Pinokio will continue automatically.</div>
656
+ </div>
657
+ </div>
658
+ `)
659
+ }
660
+ let networkSharingWarningOpen = false
661
+ const isLoopbackHost = (hostname) => {
662
+ const normalized = String(hostname || "").toLowerCase()
663
+ return normalized === "localhost" || normalized === "127.0.0.1" || normalized === "::1" || normalized === "[::1]"
664
+ }
665
+ const defaultPortForProtocol = (protocol) => {
666
+ return protocol === "https:" ? "443" : "80"
667
+ }
668
+ const normalizedPort = (url) => {
669
+ return url.port || defaultPortForProtocol(url.protocol)
670
+ }
671
+ const resolveLanCallbackHref = (href) => {
672
+ if (!href) {
673
+ return "/"
674
+ }
675
+ try {
676
+ const target = new URL(href, window.location.href)
677
+ const current = new URL(window.location.href)
678
+ if (
679
+ !isLoopbackHost(current.hostname) &&
680
+ isLoopbackHost(target.hostname) &&
681
+ target.protocol === current.protocol &&
682
+ normalizedPort(target) === normalizedPort(current)
683
+ ) {
684
+ target.hostname = current.hostname
685
+ return target.toString()
686
+ }
687
+ } catch (_) {}
688
+ return href
689
+ }
690
+ const requiresNetworkSharing = (href) => {
691
+ if (isLoopbackHost(window.location.hostname)) {
692
+ return false
693
+ }
694
+ try {
695
+ const parsed = new URL(href, window.location.href)
696
+ return parsed.protocol === "http:" && isLoopbackHost(parsed.hostname)
697
+ } catch (_) {
698
+ return false
699
+ }
700
+ }
701
+ const showNetworkSharingRequired = (href) => {
702
+ if (!requiresNetworkSharing(href)) {
703
+ return false
704
+ }
705
+ if (networkSharingWarningOpen) {
706
+ return true
707
+ }
708
+ networkSharingWarningOpen = true
709
+ Swal.fire({
710
+ title: "Turn on network sharing",
711
+ text: "Open Network to turn on local network sharing, then come back to this install.",
712
+ showCancelButton: true,
713
+ confirmButtonText: "Open Network",
714
+ cancelButtonText: "Stay here"
715
+ }).then((result) => {
716
+ networkSharingWarningOpen = false
717
+ if (result.isConfirmed) {
718
+ window.location.href = "/network"
719
+ }
720
+ })
721
+ return true
722
+ }
723
+ const navigateToCallback = () => {
724
+ const callback = document.querySelector("#callback")
725
+ const href = resolveLanCallbackHref(callback && callback.value ? callback.value : "/")
726
+ if (showNetworkSharingRequired(href)) {
727
+ return
728
+ }
729
+ location.href = href
651
730
  }
652
731
  const createTerm = async (_theme) => {
653
732
  //const theme = Object.assign({ }, xtermTheme.Terminal_Basic, {
@@ -675,10 +754,10 @@ const createTerm = async (_theme) => {
675
754
  config = window.PinokioTerminalSettings.applyToConfig(config)
676
755
  }
677
756
  const term = new Terminal(config)
757
+ term.open(document.querySelector("#terminal"))
678
758
  if (window.PinokioTerminalSettings && typeof window.PinokioTerminalSettings.register === 'function') {
679
759
  window.PinokioTerminalSettings.register(term, { baseConfig })
680
760
  }
681
- term.open(document.querySelector("#terminal"))
682
761
  const terminalContainer = document.querySelector("#terminal")
683
762
  window.PinokioTouch.bindTerminalFocus(term, terminalContainer)
684
763
 
@@ -737,18 +816,30 @@ document.addEventListener("DOMContentLoaded", async () => {
737
816
  observer.observe(document.body)
738
817
  await new Promise((resolve, reject) => {
739
818
  console.log({ cols: term.cols, rows: term.rows })
740
- socket.run({
819
+ const requirements = document.querySelector("#requirements").value
820
+ const rpc = {
741
821
  id: location.pathname,
742
822
  method: "kernel.bin.install",
743
- // params: options,
744
- params: document.querySelector("#requirements").value,
745
823
  client: {
746
824
  cols: term.cols,
747
825
  rows: term.rows,
748
826
  }
749
- }, async (packet) => {
827
+ }
828
+ if (requirements && requirements.trim().length > 0) {
829
+ rpc.params = requirements
830
+ } else {
831
+ rpc.mode = "listen"
832
+ }
833
+ socket.run(rpc, async (packet) => {
750
834
  console.log("packet", packet)
751
- if (packet.type === "stream") {
835
+ if (packet.type === "connect") {
836
+ if (packet.data && packet.data.shell) {
837
+ shell_id = packet.data.shell
838
+ }
839
+ if (packet.data && packet.data.state) {
840
+ term.write(packet.data.state)
841
+ }
842
+ } else if (packet.type === "stream") {
752
843
  if (packet.data.id) {
753
844
  shell_id = packet.data.id
754
845
  }
@@ -787,7 +878,7 @@ document.addEventListener("DOMContentLoaded", async () => {
787
878
  confirm: "OK",
788
879
  })
789
880
  if (packet.data.blocker) {
790
- location.href = document.querySelector("#callback").value
881
+ navigateToCallback()
791
882
  return
792
883
  }
793
884
  }
@@ -844,48 +935,26 @@ document.addEventListener("DOMContentLoaded", async () => {
844
935
  }
845
936
  })
846
937
  })
847
- const LoadingDialog = {
848
- start: (msg) => {
849
- Swal.fire({
850
- html: renderInstallLoader(msg),
851
- customClass: {
852
- container: "loader-container",
853
- popup: "loader-popup install-loader-popup",
854
- htmlContainer: "loader-dialog install-loader-dialog",
855
- footer: "hidden",
856
- actions: "hidden"
857
- }
858
- });
859
- },
860
- end: () => {
861
- Swal.close()
862
- }
863
- }
864
- LoadingDialog.start("Restarting...")
938
+ setRestartStatus()
865
939
  fetch("/restart", {
866
940
  method: "post"
867
- }, () => {
941
+ }).catch((error) => {
942
+ console.warn("restart request ended early", error)
868
943
  })
869
- setInterval(async () => {
944
+ const restartPoll = setInterval(async () => {
870
945
  try {
871
946
  let res = await fetch("/bin_ready").then((res) => {
872
947
  return res.json()
873
948
  })
874
949
  if (res.success) {
875
950
  //location.href = location.href
876
- LoadingDialog.end()
877
- location.href = document.querySelector("#callback").value
951
+ clearInterval(restartPoll)
952
+ navigateToCallback()
878
953
  }
879
954
  } catch (e) {
880
955
  console.log(e)
881
956
  }
882
957
  }, 1000)
883
- // let ok = confirm("Install Complete! Click OK to Proceed.")
884
- // if (ok) {
885
- // }
886
-
887
-
888
- // location.href = document.querySelector("#callback").value
889
958
  })
890
959
  </script>
891
960
  </body>
@@ -41,14 +41,16 @@
41
41
  <% if (!logsError) { %>
42
42
  <section class="logs-page" id="logs-root" data-workspace="<%= logsWorkspace ? logsWorkspace : '' %>" data-report-url="<%= logsReportUrl || '' %>" data-initial-view="<%= logsInitialView || 'raw' %>">
43
43
  <div class="logs-page-header">
44
- <div class="logs-page-switch">
44
+ <div class="logs-page-header-main">
45
+ <h1 class="logs-page-title">Logs</h1>
46
+ <p class="logs-page-description">Inspect live log files and export diagnostics from this machine.</p>
45
47
  <% if (logsReportUrl) { %>
46
- <div class="logs-view-switch" role="tablist" aria-label="Log views">
47
- <button type="button" class="logs-view-tab" data-logs-view="latest" role="tab" aria-controls="logs-latest-panel">Latest</button>
48
- <button type="button" class="logs-view-tab" data-logs-view="raw" role="tab" aria-controls="logs-raw-panel">Raw logs</button>
48
+ <div class="logs-page-switch">
49
+ <div class="logs-view-switch" role="tablist" aria-label="Log views">
50
+ <button type="button" class="logs-view-tab" data-logs-view="latest" role="tab" aria-controls="logs-latest-panel">Latest</button>
51
+ <button type="button" class="logs-view-tab" data-logs-view="raw" role="tab" aria-controls="logs-raw-panel">Raw logs</button>
52
+ </div>
49
53
  </div>
50
- <% } else { %>
51
- <div class="logs-raw-only-title"><i class="fa-solid fa-laptop-code" aria-hidden="true"></i><span>Logs</span></div>
52
54
  <% } %>
53
55
  </div>
54
56
  <div class="logs-page-controls">
@@ -144,9 +146,8 @@
144
146
  <div class="logs-sidebar" aria-label="Log folders">
145
147
  <div class="logs-sidebar-header">
146
148
  <div>
147
- <p class="logs-sidebar-eyebrow">Raw log files</p>
148
149
  <h2>Files</h2>
149
- <div class="logs-tree-hint">Select a file to stream live output.</div>
150
+ <div class="logs-tree-hint">Select a log file to stream.</div>
150
151
  </div>
151
152
  <div class="logs-zip-status" id="logs-zip-status" role="status" aria-live="polite"></div>
152
153
  </div>