pinokiod 3.41.0 → 3.43.0

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 (82) hide show
  1. package/kernel/api/browser/index.js +3 -1
  2. package/kernel/api/cloudflare/index.js +3 -3
  3. package/kernel/api/index.js +187 -51
  4. package/kernel/api/loading/index.js +15 -0
  5. package/kernel/api/process/index.js +7 -0
  6. package/kernel/api/shell/index.js +0 -2
  7. package/kernel/bin/browserless.js +22 -0
  8. package/kernel/bin/caddy.js +36 -4
  9. package/kernel/bin/index.js +4 -1
  10. package/kernel/bin/setup.js +38 -5
  11. package/kernel/connect/backend.js +110 -0
  12. package/kernel/connect/config.js +171 -0
  13. package/kernel/connect/index.js +18 -7
  14. package/kernel/connect/providers/huggingface/index.js +98 -0
  15. package/kernel/connect/providers/x/index.js +0 -1
  16. package/kernel/environment.js +91 -19
  17. package/kernel/git.js +46 -3
  18. package/kernel/index.js +119 -39
  19. package/kernel/peer.js +40 -5
  20. package/kernel/plugin.js +3 -2
  21. package/kernel/procs.js +27 -20
  22. package/kernel/prototype.js +30 -16
  23. package/kernel/router/common.js +1 -1
  24. package/kernel/router/connector.js +1 -3
  25. package/kernel/router/index.js +38 -4
  26. package/kernel/router/localhost_home_router.js +5 -1
  27. package/kernel/router/localhost_port_router.js +27 -1
  28. package/kernel/router/localhost_static_router.js +93 -0
  29. package/kernel/router/localhost_variable_router.js +14 -9
  30. package/kernel/router/peer_peer_router.js +3 -0
  31. package/kernel/router/peer_static_router.js +43 -0
  32. package/kernel/router/peer_variable_router.js +15 -14
  33. package/kernel/router/processor.js +26 -1
  34. package/kernel/router/rewriter.js +59 -0
  35. package/kernel/scripts/git/commit +11 -1
  36. package/kernel/shell.js +8 -3
  37. package/kernel/util.js +65 -6
  38. package/package.json +2 -1
  39. package/server/index.js +1037 -964
  40. package/server/public/common.js +382 -1
  41. package/server/public/fscreator.js +0 -1
  42. package/server/public/loading.js +17 -0
  43. package/server/public/notifyinput.js +0 -1
  44. package/server/public/opener.js +4 -2
  45. package/server/public/style.css +311 -11
  46. package/server/socket.js +7 -1
  47. package/server/views/app.ejs +1747 -351
  48. package/server/views/columns.ejs +338 -0
  49. package/server/views/connect/huggingface.ejs +353 -0
  50. package/server/views/connect/index.ejs +410 -0
  51. package/server/views/connect/x.ejs +43 -9
  52. package/server/views/connect.ejs +709 -49
  53. package/server/views/container.ejs +357 -0
  54. package/server/views/d.ejs +251 -62
  55. package/server/views/download.ejs +54 -10
  56. package/server/views/editor.ejs +11 -0
  57. package/server/views/explore.ejs +40 -15
  58. package/server/views/file_explorer.ejs +25 -246
  59. package/server/views/form.ejs +44 -1
  60. package/server/views/frame.ejs +39 -1
  61. package/server/views/github.ejs +48 -11
  62. package/server/views/help.ejs +48 -7
  63. package/server/views/index.ejs +119 -58
  64. package/server/views/index2.ejs +3 -4
  65. package/server/views/init/index.ejs +651 -197
  66. package/server/views/install.ejs +1 -1
  67. package/server/views/mini.ejs +47 -18
  68. package/server/views/net.ejs +199 -67
  69. package/server/views/network.ejs +220 -94
  70. package/server/views/network2.ejs +3 -4
  71. package/server/views/old_network.ejs +3 -3
  72. package/server/views/prototype/index.ejs +48 -11
  73. package/server/views/review.ejs +1005 -0
  74. package/server/views/rows.ejs +341 -0
  75. package/server/views/screenshots.ejs +1020 -0
  76. package/server/views/settings.ejs +160 -23
  77. package/server/views/setup.ejs +49 -7
  78. package/server/views/setup_home.ejs +43 -10
  79. package/server/views/shell.ejs +7 -1
  80. package/server/views/start.ejs +14 -9
  81. package/server/views/terminal.ejs +13 -2
  82. package/server/views/tools.ejs +1015 -0
@@ -0,0 +1,410 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title><%=name%> OAuth</title>
7
+ <link href="/css/fontawesome.min.css" rel="stylesheet">
8
+ <link href="/css/solid.min.css" rel="stylesheet">
9
+ <link href="/css/regular.min.css" rel="stylesheet">
10
+ <link href="/css/brands.min.css" rel="stylesheet">
11
+ <style>
12
+ body {
13
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
14
+ max-width: 600px;
15
+ margin: 50px auto;
16
+ padding: 20px;
17
+ background: #f8fafc;
18
+ }
19
+ /*
20
+ .container {
21
+ background: white;
22
+ padding: 30px;
23
+ border-radius: 10px;
24
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
25
+ }
26
+ */
27
+ h1 {
28
+ text-transform: capitalize;
29
+ color: #1f2937;
30
+ margin: 0;
31
+ font-size: 40px;
32
+ margin-top: 10px;
33
+ }
34
+ .btn {
35
+ text-decoration: none;
36
+ display: inline-block;
37
+ background: black;
38
+ width: 100%;
39
+ color: white;
40
+ border: none;
41
+ padding: 12px 24px;
42
+ border-radius: 6px;
43
+ cursor: pointer;
44
+ font-size: 16px;
45
+ box-sizing: border-box;
46
+ text-align: center;
47
+ /*
48
+ margin: 10px 5px 10px 0;
49
+ */
50
+ }
51
+ .btn.secondary {
52
+ background: #6b7280;
53
+ }
54
+ #user-details {
55
+ margin-bottom: 20px;
56
+ }
57
+ .user-info {
58
+ background: #f0f9ff;
59
+ padding: 15px;
60
+ border-left: 4px solid #3b82f6;
61
+ /*
62
+ margin-top: 20px;
63
+ */
64
+ }
65
+ #login-section p {
66
+ padding: 5px 0;
67
+ }
68
+ .user-info h3 {
69
+ margin: 0 0 10px;
70
+ font-size: 30px;
71
+ }
72
+ .status {
73
+ padding: 12px;
74
+ margin: 10px 0;
75
+ border-radius: 6px;
76
+ font-weight: 500;
77
+ }
78
+ .status.success { background: #d1fae5; color: #065f46; }
79
+ .status.error { background: #fee2e2; color: #991b1b; }
80
+ .status.warning { background: #fef3c7; color: #92400e; }
81
+ .hidden { display: none; }
82
+ .profile {
83
+ width: 400px;
84
+ display: flex;
85
+ gap: 10px;
86
+ font-size: 14px;
87
+ box-sizing: border-box;
88
+ }
89
+ .profile img {
90
+ width: 100px;
91
+ flex-shrink: 0;
92
+ }
93
+ .profile td {
94
+ font-size: 14px;
95
+ padding: 0 10px 0 0;
96
+ }
97
+ header {
98
+ text-align: center;
99
+ padding: 50px;
100
+ letter-spacing: -1px;
101
+ }
102
+ .logos {
103
+ display: flex;
104
+ justify-content: center;
105
+ align-items: center;
106
+ gap: 10px;
107
+ font-size: 30px;
108
+ }
109
+ .logo {
110
+ height: 40px;
111
+ font-size: 40px;
112
+ display: flex;
113
+ justify-content: center;
114
+ align-items: center;
115
+ }
116
+ p {
117
+ margin: 5px 0;
118
+ font-size: 14px;
119
+ }
120
+ </style>
121
+ </head>
122
+ <body>
123
+ <header>
124
+ <div class='logos'>
125
+ <img class='logo' src="/pinokio-black.png"/>
126
+ <div>+</div>
127
+ <% if (name === "huggingface") { %>
128
+ <div class='logo'>🤗</div>
129
+ <% } else if (name === "x") { %>
130
+ <i class="logo fa-brands fa-square-x-twitter"></i>
131
+ <% } else if (name === "pinokio") { %>
132
+ <img class='logo' src="/pinokio-black.png"/>
133
+ <% } %>
134
+ </div>
135
+ <h1><%=name%> Connect</h1>
136
+ </header>
137
+ <div class="container">
138
+ <% if (protocol === "https") { %>
139
+ <div id="status" class="status warning">
140
+ Checking authentication status...
141
+ </div>
142
+
143
+ <!-- Login Section -->
144
+ <div id="login-section">
145
+ <p>Click below to authenticate with <%=name%>:</p>
146
+ <button class="btn" onclick="login()">Login</button>
147
+ </div>
148
+
149
+ <!-- User Section -->
150
+ <div id="user-section" class="hidden">
151
+ <div class="user-info">
152
+ <h3>Logged In</h3>
153
+ <div id="user-details"></div>
154
+ <button class="btn secondary" onclick="logout()">Logout</button>
155
+ </div>
156
+ </div>
157
+ <% } else { %>
158
+ <a class='btn' href="https://pinokio.localhost/connect/<%=name%>">Get started<a>
159
+ <% } %>
160
+
161
+ </div>
162
+
163
+ <% if (protocol === "https") { %>
164
+ <script>
165
+
166
+ // Configuration
167
+
168
+ const CLIENT_ID = "<%=config.CLIENT_ID%>"
169
+ const REDIRECT_URI = "<%=config.REDIRECT_URI%>"
170
+ const OAUTH_URL = "<%=config.OAUTH_URL%>"
171
+ const TOKEN_URL = "<%=config.TOKEN_URL%>"
172
+ const PROFILE_URL = "<%=config.PROFILE_URL%>"
173
+ const SCOPE = "<%=config.SCOPE%>"
174
+ const name = "<%=name%>"
175
+
176
+ // Utility functions
177
+ function setStatus(message, type) {
178
+ const status = document.getElementById('status');
179
+ status.textContent = message;
180
+ status.className = `status ${type}`;
181
+ }
182
+
183
+ function generateRandomString(length) {
184
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
185
+ let result = '';
186
+ for (let i = 0; i < length; i++) {
187
+ result += chars.charAt(Math.floor(Math.random() * chars.length));
188
+ }
189
+ return result;
190
+ }
191
+
192
+ // PKCE functions
193
+ function generateCodeVerifier() {
194
+ const array = new Uint8Array(32);
195
+ crypto.getRandomValues(array);
196
+ return base64URLEncode(array);
197
+ }
198
+
199
+ async function generateCodeChallenge(verifier) {
200
+ const encoder = new TextEncoder();
201
+ const data = encoder.encode(verifier);
202
+ const digest = await crypto.subtle.digest('SHA-256', data);
203
+ return base64URLEncode(new Uint8Array(digest));
204
+ }
205
+
206
+ function base64URLEncode(array) {
207
+ return btoa(String.fromCharCode.apply(null, array))
208
+ .replace(/\+/g, '-')
209
+ .replace(/\//g, '_')
210
+ .replace(/=/g, '');
211
+ }
212
+
213
+ // Token management with automatic refresh
214
+ async function ensureValidToken() {
215
+ const res = await fetch(`/connect/${name}/keys`, {
216
+ method: 'POST',
217
+ headers: { 'Content-Type': 'application/json' },
218
+ body: JSON.stringify({})
219
+ });
220
+ const json = await res.json();
221
+ console.log({ json })
222
+ if (json && json.access_token) {
223
+ return json.access_token
224
+ } else {
225
+ return null
226
+ }
227
+ }
228
+
229
+ // OAuth functions
230
+ async function login() {
231
+ try {
232
+ // Clear existing data
233
+ localStorage.removeItem('oauth_state');
234
+ localStorage.removeItem('code_verifier');
235
+
236
+ // Generate PKCE parameters
237
+ const state = generateRandomString(32);
238
+ const codeVerifier = generateCodeVerifier();
239
+ const codeChallenge = await generateCodeChallenge(codeVerifier);
240
+
241
+ // Store for later
242
+ localStorage.setItem('oauth_state', state);
243
+ localStorage.setItem('code_verifier', codeVerifier);
244
+
245
+ // Build auth URL
246
+ const params = new URLSearchParams({
247
+ client_id: CLIENT_ID,
248
+ redirect_uri: REDIRECT_URI,
249
+ response_type: 'code',
250
+ scope: SCOPE,
251
+ state: state,
252
+ code_challenge: codeChallenge,
253
+ code_challenge_method: 'S256'
254
+ });
255
+
256
+ const authUrl = OAUTH_URL + '?' + params.toString();
257
+
258
+ // Redirect
259
+ window.location.href = authUrl;
260
+
261
+ } catch (error) {
262
+ console.error('Login error:', error);
263
+ setStatus('Failed to start login', 'error');
264
+ }
265
+ }
266
+
267
+ async function handleOAuthCallback(code) {
268
+ try {
269
+ // Verify state
270
+ const urlParams = new URLSearchParams(window.location.search);
271
+ const returnedState = urlParams.get('state');
272
+ const storedState = localStorage.getItem('oauth_state');
273
+
274
+ if (returnedState !== storedState) {
275
+ throw new Error('Invalid state parameter');
276
+ }
277
+
278
+ // Get code verifier
279
+ const codeVerifier = localStorage.getItem('code_verifier');
280
+ if (!codeVerifier) {
281
+ throw new Error('Code verifier not found');
282
+ }
283
+
284
+ const res = await fetch(`/connect/${name}/login`, {
285
+ method: 'POST',
286
+ headers: { 'Content-Type': 'application/json' },
287
+ body: JSON.stringify({
288
+ client_id: CLIENT_ID,
289
+ code: code,
290
+ redirect_uri: REDIRECT_URI,
291
+ grant_type: 'authorization_code',
292
+ code_verifier: codeVerifier
293
+ })
294
+ });
295
+ const response = await res.json();
296
+ console.log('Token response:', response)
297
+ // Store tokens
298
+ if (response.access_token) {
299
+ // Cleanup
300
+ localStorage.removeItem('oauth_state');
301
+ localStorage.removeItem('code_verifier');
302
+ window.history.replaceState({}, document.title, window.location.pathname);
303
+
304
+ // Get user info
305
+ await fetchUserInfo();
306
+ } else {
307
+ throw new Error('No access token received');
308
+ }
309
+
310
+ } catch (error) {
311
+ console.error('OAuth callback error:', error);
312
+ setStatus('Authentication failed: ' + error.message, 'error');
313
+ }
314
+ }
315
+
316
+ async function fetchUserInfo() {
317
+ try {
318
+ // Use ensureValidToken to automatically refresh if needed
319
+ const token = await ensureValidToken();
320
+ if (!token) {
321
+ throw new Error('No valid token available');
322
+ }
323
+ console.log({ token })
324
+ await displayUserInfo()
325
+ } catch (error) {
326
+ console.error('Error fetching user info:', error);
327
+ setStatus('Failed to fetch user info: ' + error.message, 'error');
328
+ // logout();
329
+ }
330
+ }
331
+
332
+ async function displayUserInfo() {
333
+ const res = await fetch(`/connect/${name}/profile`)
334
+ const profile = await res.json();
335
+ const userDetails = document.getElementById('user-details');
336
+ let rows = profile.items.map((row) => {
337
+ return `<tr><td>${row.key}</td><td>${row.val}</td></tr>`
338
+ }).join("")
339
+ userDetails.innerHTML = `<div class="profile">
340
+ <img src="${profile.image}">
341
+ <div class='profile-column'>
342
+ <table>${rows}</table>
343
+ </div>
344
+ </div>`
345
+ document.getElementById('login-section').className = 'hidden';
346
+ document.getElementById('user-section').className = '';
347
+ setStatus("", "hidden")
348
+ }
349
+
350
+ async function logout() {
351
+ document.getElementById('login-section').className = '';
352
+ document.getElementById('user-section').className = 'hidden';
353
+ setStatus('Logged out', 'warning');
354
+ const res = await fetch('/connect/<%=name%>/logout', {
355
+ method: 'POST',
356
+ headers: { 'Content-Type': 'application/json' },
357
+ body: JSON.stringify({})
358
+ });
359
+ const json = await res.json();
360
+ location.href = location.href
361
+ }
362
+
363
+ // Utility function for making authenticated API calls with automatic refresh
364
+ async function makeAuthenticatedRequest(url, options = {}) {
365
+ const token = await ensureValidToken();
366
+ if (!token) {
367
+ throw new Error('No valid token available');
368
+ }
369
+
370
+ return fetch(url, {
371
+ ...options,
372
+ headers: {
373
+ ...options.headers,
374
+ 'Authorization': 'Bearer ' + token
375
+ }
376
+ });
377
+ }
378
+
379
+ // Initialize on page load
380
+ window.addEventListener('load', async () => {
381
+ const urlParams = new URLSearchParams(window.location.search);
382
+ const code = urlParams.get('code');
383
+ const error = urlParams.get('error');
384
+
385
+ if (error) {
386
+ setStatus('OAuth error: ' + error, 'error');
387
+ return;
388
+ }
389
+
390
+ if (code) {
391
+ await handleOAuthCallback(code);
392
+ return;
393
+ }
394
+
395
+ // Check existing session with automatic refresh
396
+ const token = await ensureValidToken();
397
+ if (token) {
398
+ await fetchUserInfo();
399
+ } else {
400
+ setStatus('Not authenticated', 'warning');
401
+ }
402
+ });
403
+
404
+ // Export makeAuthenticatedRequest for external use
405
+ window.makeAuthenticatedRequest = makeAuthenticatedRequest;
406
+ window.ensureValidToken = ensureValidToken;
407
+ </script>
408
+ <% } %>
409
+ </body>
410
+ </html>
@@ -181,6 +181,8 @@ pre {
181
181
  }
182
182
  </style>
183
183
  <link href="/highlight.css" rel="stylesheet" />
184
+ <script src="/popper.min.js"></script>
185
+ <script src="/tippy-bundle.umd.min.js"></script>
184
186
  <script src="/hotkeys.min.js"></script>
185
187
  <script src="/sweetalert2.js"></script>
186
188
  <script src="/common.js"></script>
@@ -192,17 +194,49 @@ pre {
192
194
  <header class='navheader grabbable'>
193
195
  <h1>
194
196
  <a class='home' href="/"><img class='icon' src="/pinokio-black.png"></a>
195
- <button class='btn2' id='back'><div><i class="fa-solid fa-chevron-left"></i></div><div>Prev</div></button>
196
- <button class='btn2' id='forward'><div><i class="fa-solid fa-chevron-right"></i></div><div>Next</div></button>
197
- <button class='btn2' id='refresh-page'><div><i class="fa-solid fa-rotate-right"></i></div><div>Refresh</div></button>
197
+ <button class='btn2' id='back' data-tippy-content="back"><div><i class="fa-solid fa-chevron-left"></i></div></button>
198
+ <button class='btn2' id='forward' data-tippy-content="forward"><div><i class="fa-solid fa-chevron-right"></i></div></button>
199
+ <button class='btn2' id='refresh-page' data-tippy-content="refresh"><div><i class="fa-solid fa-rotate-right"></i></div></button>
200
+ <button class='btn2' id='screenshot' data-tippy-content="take a screenshot"><i class="fa-solid fa-camera"></i></button>
198
201
  <div class='flexible'></div>
199
- <a href="/connect" class='btn2'><div><i class="fa-solid fa-circle-user"></i></div><div>Connect</div></a>
200
- <div class='nav-btns'>
201
- <a class='btn2' href="<%=portal%>" target="_blank"><div><i class="fa-solid fa-question"></i></div><div>Help</div></a>
202
- <a class='btn2' href="/?mode=settings"><div><i class="fa-solid fa-gear"></i></div><div>Settings</div></a>
203
- <button id='new-window' title='open a new window' class='btn2' data-agent="<%=agent%>"><div><i class="fa-solid fa-plus"></i></div><div>Window</div></button>
202
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
203
+ <div><i class="fa-solid fa-table-columns"></i></div>
204
+ </a>
205
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
206
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i></div>
207
+ </a>
208
+ <div class="dropdown-content" id="dropdown-content">
209
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
210
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
211
+ </button>
212
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
213
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
214
+ </button>
204
215
  </div>
205
- </h1>
216
+ <!--
217
+ <div class="dropdown btn2">
218
+ <button class='btn2' id='window-management'>
219
+ <div><i class="fa-regular fa-window-restore"></i></div>
220
+ </button>
221
+ <div class="dropdown-content" id="dropdown-content">
222
+ <button class='btn2' id='clone-win' data-tippy-content="clone this window">
223
+ <div><i class="fa-solid fa-clone"></i><div>clone this window</div></div>
224
+ </button>
225
+ <a class='btn2' href="/columns" data-tippy-content="split into 2 columns">
226
+ <div><i class="fa-solid fa-table-columns"></i><div>split columns</div></div>
227
+ </a>
228
+ <a class='btn2' href="/rows" data-tippy-content="split into 2 rows">
229
+ <div><i class="fa-solid fa-table-columns fa-rotate-270"></i><div>split rows</div></div>
230
+ </a>
231
+ <button id='new-window' data-tippy-content="open a new window" title='open a new window' class='btn2' data-agent="<%=agent%>">
232
+ <div><i class="fa-solid fa-plus"></i><div>new window</div></div>
233
+ </button>
234
+ </div>
235
+ </div>
236
+ -->
237
+ <button class='btn2 hidden' id='close-window' data-tippy-content='close this section'>
238
+ <div><i class="fa-solid fa-xmark"></i></div>
239
+ </button>
206
240
  </h1>
207
241
  </header>
208
242
  <main>