pinokiod 7.2.11 → 7.2.13

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 (54) hide show
  1. package/Dockerfile +0 -2
  2. package/kernel/api/index.js +14 -1
  3. package/kernel/api/process/index.js +99 -44
  4. package/kernel/api/uri/index.js +51 -0
  5. package/kernel/environment.js +22 -3
  6. package/kernel/git.js +0 -13
  7. package/kernel/index.js +61 -1
  8. package/kernel/plugin.js +6 -58
  9. package/kernel/plugin_sources.js +236 -0
  10. package/kernel/util.js +60 -0
  11. package/package.json +1 -1
  12. package/server/features/drafts/public/drafts.js +35 -12
  13. package/server/index.js +120 -142
  14. package/server/lib/content_validation.js +28 -25
  15. package/server/public/common.js +95 -29
  16. package/server/public/create-launcher.js +4 -31
  17. package/server/public/electron.css +1 -1
  18. package/server/public/style.css +337 -0
  19. package/server/public/task-launcher.js +5 -32
  20. package/server/public/universal-launcher.js +3 -26
  21. package/server/views/app.ejs +8 -29
  22. package/server/views/d.ejs +0 -33
  23. package/server/views/editor.ejs +25 -4
  24. package/server/views/shell.ejs +11 -3
  25. package/server/views/terminal.ejs +15 -3
  26. package/spec/INSTRUCTION_SYNC.md +5 -5
  27. package/system/plugin/antigravity/antigravity.png +0 -0
  28. package/system/plugin/antigravity/pinokio.js +37 -0
  29. package/system/plugin/claude/claude.png +0 -0
  30. package/system/plugin/claude/pinokio.js +63 -0
  31. package/system/plugin/claude-auto/claude.png +0 -0
  32. package/system/plugin/claude-auto/pinokio.js +74 -0
  33. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  34. package/system/plugin/claude-desktop/pinokio.js +39 -0
  35. package/system/plugin/codex/openai.webp +0 -0
  36. package/system/plugin/codex/pinokio.js +58 -0
  37. package/system/plugin/codex-auto/openai.webp +0 -0
  38. package/system/plugin/codex-auto/pinokio.js +65 -0
  39. package/system/plugin/codex-desktop/icon.png +0 -0
  40. package/system/plugin/codex-desktop/pinokio.js +39 -0
  41. package/system/plugin/crush/crush.png +0 -0
  42. package/system/plugin/crush/pinokio.js +31 -0
  43. package/system/plugin/cursor/cursor.jpeg +0 -0
  44. package/system/plugin/cursor/pinokio.js +39 -0
  45. package/system/plugin/gemini/gemini.jpeg +0 -0
  46. package/system/plugin/gemini/pinokio.js +40 -0
  47. package/system/plugin/gemini-auto/gemini.jpeg +0 -0
  48. package/system/plugin/gemini-auto/pinokio.js +43 -0
  49. package/system/plugin/qwen/pinokio.js +50 -0
  50. package/system/plugin/qwen/qwen.png +0 -0
  51. package/system/plugin/vscode/pinokio.js +36 -0
  52. package/system/plugin/vscode/vscode.png +0 -0
  53. package/system/plugin/windsurf/pinokio.js +39 -0
  54. package/system/plugin/windsurf/windsurf.png +0 -0
@@ -0,0 +1,236 @@
1
+ const fs = require("fs")
2
+ const path = require("path")
3
+ const { glob } = require("glob")
4
+
5
+ const LOCAL_RUN_PREFIX = "/run"
6
+ const LOCAL_ASSET_PREFIX = "/asset"
7
+ const SYSTEM_RUN_PREFIX = "/pinokio/run"
8
+ const SYSTEM_ASSET_PREFIX = "/pinokio/asset"
9
+ const LOCAL_PLUGIN_RUN_PREFIX = `${LOCAL_RUN_PREFIX}/plugin`
10
+ const LOCAL_PLUGIN_ASSET_PREFIX = `${LOCAL_ASSET_PREFIX}/plugin`
11
+ const SYSTEM_PLUGIN_RUN_PREFIX = `${SYSTEM_RUN_PREFIX}/plugin`
12
+ const SYSTEM_PLUGIN_ASSET_PREFIX = `${SYSTEM_ASSET_PREFIX}/plugin`
13
+
14
+ const toPathname = (value) => {
15
+ const raw = typeof value === "string" ? value.trim() : ""
16
+ if (!raw) return ""
17
+ try {
18
+ if (/^https?:\/\//i.test(raw)) return new URL(raw).pathname
19
+ if (raw.startsWith("/")) return new URL(`http://localhost${raw}`).pathname
20
+ } catch (_) {
21
+ }
22
+ return raw.split("?")[0].split("#")[0]
23
+ }
24
+
25
+ const normalizeSlashes = (value) => String(value || "").replace(/\\/g, "/").replace(/\/{2,}/g, "/")
26
+
27
+ const systemRoot = (kernel) => {
28
+ if (kernel && typeof kernel.systemPath === "function") {
29
+ return kernel.systemPath()
30
+ }
31
+ return path.resolve(__dirname, "..", "system")
32
+ }
33
+
34
+ const systemPluginRoot = (kernel) => {
35
+ if (kernel && typeof kernel.systemPath === "function") {
36
+ return kernel.systemPath("plugin")
37
+ }
38
+ return path.resolve(__dirname, "..", "system", "plugin")
39
+ }
40
+
41
+ const isSystemRunPath = (value) => toPathname(value).startsWith(`${SYSTEM_RUN_PREFIX}/`)
42
+ const isLocalRunPath = (value) => toPathname(value).startsWith(`${LOCAL_RUN_PREFIX}/`)
43
+ const isRunPath = (value) => isLocalRunPath(value) || isSystemRunPath(value)
44
+ const isSystemPluginPath = (value) => normalizeSlashes(value).startsWith(`${SYSTEM_PLUGIN_RUN_PREFIX}/`)
45
+ const isLegacyPluginCodePath = (value) => {
46
+ const normalized = normalizeSlashes(value).replace(/^\/+/, "")
47
+ return normalized.startsWith("plugin/code/") || normalized.startsWith("code/")
48
+ }
49
+
50
+ const normalizePluginPath = (value) => {
51
+ let normalized = toPathname(value)
52
+ if (!normalized) return ""
53
+ normalized = normalizeSlashes(normalized)
54
+ if (!normalized.startsWith(`${SYSTEM_RUN_PREFIX}/`)) {
55
+ normalized = normalized.replace(/^\/run(?=\/)/, "")
56
+ }
57
+ if (!normalized.startsWith("/")) {
58
+ normalized = `/${normalized}`
59
+ }
60
+ normalized = normalized.replace(/\/+$/, "")
61
+ if (!normalized.endsWith("/pinokio.js")) {
62
+ normalized = `${normalized}/pinokio.js`
63
+ }
64
+ return normalized
65
+ }
66
+
67
+ const pluginSelectionMatches = (src, selectedValue) => {
68
+ const selectedPlugin = normalizeSlashes(typeof selectedValue === "string" ? selectedValue.trim() : "")
69
+ if (!selectedPlugin || !src) return false
70
+ const selectedRunPath = selectedPlugin.startsWith(`${SYSTEM_RUN_PREFIX}/`)
71
+ ? selectedPlugin
72
+ : `${LOCAL_RUN_PREFIX}${selectedPlugin}`
73
+ return src === selectedPlugin || src.startsWith(selectedRunPath)
74
+ }
75
+
76
+ const resolveRunPath = (kernel, value) => {
77
+ const pathname = toPathname(value)
78
+ if (isSystemRunPath(pathname)) {
79
+ const parts = pathname.split("/").filter(Boolean).slice(2)
80
+ return kernel.systemPath(...parts)
81
+ }
82
+ if (isLocalRunPath(pathname)) {
83
+ const parts = pathname.split("/").filter(Boolean).slice(1)
84
+ return kernel.path(...parts)
85
+ }
86
+ return ""
87
+ }
88
+
89
+ const resolvePinokioPath = (kernel, value) => {
90
+ const runPath = resolveRunPath(kernel, value)
91
+ if (runPath) return runPath
92
+ const pathname = toPathname(value)
93
+ return pathname ? kernel.path(pathname.replace(/^\/+/, "")) : ""
94
+ }
95
+
96
+ const systemRelativeFromPluginPath = (normalizedPath) => {
97
+ return normalizeSlashes(normalizedPath).replace(/^\/pinokio\/run\/+/, "")
98
+ }
99
+
100
+ const pluginPathToAbsolute = (kernel, normalizedPath) => {
101
+ if (isSystemPluginPath(normalizedPath)) {
102
+ return kernel.systemPath(systemRelativeFromPluginPath(normalizedPath))
103
+ }
104
+ return kernel.path(normalizeSlashes(normalizedPath).replace(/^\/+/, ""))
105
+ }
106
+
107
+ const pluginRunHrefForPath = (normalizedPath) => {
108
+ if (typeof normalizedPath !== "string" || !normalizedPath) return ""
109
+ if (isSystemPluginPath(normalizedPath)) return normalizedPath
110
+ return `${LOCAL_RUN_PREFIX}${normalizedPath}`
111
+ }
112
+
113
+ const pluginAssetHrefForIcon = (normalizedPath, icon) => {
114
+ const trimmedIcon = typeof icon === "string" ? icon.trim() : ""
115
+ if (!trimmedIcon) return ""
116
+ if (isSystemPluginPath(normalizedPath)) {
117
+ const relativeDir = path.posix.dirname(systemRelativeFromPluginPath(normalizedPath))
118
+ return `${SYSTEM_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
119
+ }
120
+ if (normalizeSlashes(normalizedPath).startsWith("/plugin/")) {
121
+ const relativeDir = path.posix.dirname(normalizeSlashes(normalizedPath).slice(1))
122
+ return `${LOCAL_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
123
+ }
124
+ return ""
125
+ }
126
+
127
+ const normalizeLauncherTool = (toolValue) => {
128
+ let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
129
+ normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
130
+ normalizedTool = normalizedTool.replace(/^\/+|\/+$/g, "")
131
+ if (!normalizedTool || normalizedTool.includes("..") || !/^[A-Za-z0-9._/-]+$/.test(normalizedTool)) {
132
+ const error = new Error("Invalid plugin.")
133
+ error.status = 400
134
+ throw error
135
+ }
136
+ if (!normalizedTool.startsWith("pinokio/run/")) {
137
+ normalizedTool = normalizedTool.replace(/^run\//, "")
138
+ }
139
+ if (isLegacyPluginCodePath(normalizedTool)) {
140
+ const error = new Error("The managed plugin/code path is no longer used.")
141
+ error.status = 400
142
+ throw error
143
+ }
144
+ return normalizedTool
145
+ }
146
+
147
+ const resolveLauncherPluginHref = (toolValue) => {
148
+ const normalizedTool = normalizeLauncherTool(toolValue)
149
+ if (normalizedTool.startsWith("pinokio/run/")) {
150
+ const scriptPath = normalizedTool.endsWith(".js")
151
+ ? normalizedTool
152
+ : `${normalizedTool}/pinokio.js`
153
+ return `/${scriptPath}`
154
+ }
155
+ if (normalizedTool.startsWith("plugin/") || normalizedTool.startsWith("api/")) {
156
+ const scriptPath = normalizedTool.endsWith(".js")
157
+ ? normalizedTool
158
+ : `${normalizedTool}/pinokio.js`
159
+ return `${LOCAL_RUN_PREFIX}/${scriptPath}`
160
+ }
161
+ return `${LOCAL_PLUGIN_RUN_PREFIX}/${normalizedTool}/pinokio.js`
162
+ }
163
+
164
+ const loadPluginsFromRoot = async ({ kernel, root, runPrefix, assetPrefix, source, ignore = [] }) => {
165
+ const exists = await fs.promises.stat(root).then((stat) => stat.isDirectory()).catch(() => false)
166
+ if (!exists) return []
167
+
168
+ const pluginPaths = await glob("**/pinokio.js", { cwd: root, ignore })
169
+ const plugins = []
170
+ for (const pluginPath of pluginPaths) {
171
+ const normalizedPluginPath = normalizeSlashes(pluginPath)
172
+ const config = await kernel.require(path.resolve(root, pluginPath))
173
+ if (!config || !Array.isArray(config.run)) continue
174
+ const invalid = Object.keys(config).some((key) => typeof config[key] === "function")
175
+ if (invalid) continue
176
+
177
+ const cwd = normalizedPluginPath.split("/").slice(0, -1).join("/")
178
+ const href = `${runPrefix}/${normalizedPluginPath}`
179
+ const image = config.icon ? `${assetPrefix}/${cwd}/${config.icon}` : config.image
180
+ plugins.push({
181
+ ...config,
182
+ href,
183
+ src: href,
184
+ image,
185
+ source,
186
+ system: source === "system",
187
+ })
188
+ }
189
+ return plugins
190
+ }
191
+
192
+ const loadPluginMenu = async (kernel) => {
193
+ const systemPlugins = await loadPluginsFromRoot({
194
+ kernel,
195
+ root: systemPluginRoot(kernel),
196
+ runPrefix: SYSTEM_PLUGIN_RUN_PREFIX,
197
+ assetPrefix: SYSTEM_PLUGIN_ASSET_PREFIX,
198
+ source: "system",
199
+ })
200
+ const localPlugins = await loadPluginsFromRoot({
201
+ kernel,
202
+ root: path.resolve(kernel.homedir, "plugin"),
203
+ runPrefix: LOCAL_PLUGIN_RUN_PREFIX,
204
+ assetPrefix: LOCAL_PLUGIN_ASSET_PREFIX,
205
+ source: "local",
206
+ ignore: ["code/**"],
207
+ })
208
+ return systemPlugins.concat(localPlugins)
209
+ }
210
+
211
+ module.exports = {
212
+ LOCAL_RUN_PREFIX,
213
+ LOCAL_ASSET_PREFIX,
214
+ SYSTEM_RUN_PREFIX,
215
+ SYSTEM_ASSET_PREFIX,
216
+ LOCAL_PLUGIN_RUN_PREFIX,
217
+ SYSTEM_PLUGIN_RUN_PREFIX,
218
+ SYSTEM_PLUGIN_ASSET_PREFIX,
219
+ systemRoot,
220
+ systemPluginRoot,
221
+ toPathname,
222
+ isSystemRunPath,
223
+ isLocalRunPath,
224
+ isRunPath,
225
+ isSystemPluginPath,
226
+ isLegacyPluginCodePath,
227
+ normalizePluginPath,
228
+ pluginSelectionMatches,
229
+ resolveRunPath,
230
+ resolvePinokioPath,
231
+ pluginPathToAbsolute,
232
+ pluginRunHrefForPath,
233
+ pluginAssetHrefForIcon,
234
+ resolveLauncherPluginHref,
235
+ loadPluginMenu,
236
+ }
package/kernel/util.js CHANGED
@@ -434,6 +434,65 @@ const openURL = (url) => {
434
434
  console.warn('[Util.openURL] exec failed:', err && err.message ? err.message : err)
435
435
  }
436
436
  }
437
+ const openURI = async (uri) => {
438
+ const platform = os.platform()
439
+ const launch = (command, args) => {
440
+ return new Promise((resolve, reject) => {
441
+ let settled = false
442
+ const settle = (fn, value) => {
443
+ if (settled) return
444
+ settled = true
445
+ clearTimeout(timer)
446
+ fn(value)
447
+ }
448
+ const timer = setTimeout(() => {
449
+ settle(resolve, {
450
+ ok: true,
451
+ command,
452
+ status: 'started'
453
+ })
454
+ }, 3000)
455
+ const child = spawn(command, args, {
456
+ detached: true,
457
+ stdio: 'ignore'
458
+ })
459
+ child.on('error', (error) => {
460
+ settle(reject, error)
461
+ })
462
+ child.on('close', (code) => {
463
+ if (code) {
464
+ settle(reject, new Error(`${command} exited with code ${code}`))
465
+ } else {
466
+ settle(resolve, {
467
+ ok: true,
468
+ command,
469
+ status: 'exited'
470
+ })
471
+ }
472
+ })
473
+ child.unref()
474
+ })
475
+ }
476
+ try {
477
+ if (platform === 'darwin') {
478
+ return await launch('open', [uri])
479
+ } else if (platform === 'win32') {
480
+ try {
481
+ return await launch('rundll32.exe', ['url.dll,FileProtocolHandler', uri])
482
+ } catch (error) {
483
+ console.warn('[Util.openURI] rundll32 failed, falling back to explorer:', error && error.message ? error.message : error)
484
+ return await launch('explorer.exe', [uri])
485
+ }
486
+ }
487
+ return await launch('xdg-open', [uri])
488
+ } catch (err) {
489
+ console.warn('[Util.openURI] spawn failed:', err && err.message ? err.message : err)
490
+ return {
491
+ ok: false,
492
+ error: err && err.message ? err.message : String(err)
493
+ }
494
+ }
495
+ }
437
496
  const openfs = (dirPath, options, kernel) => {
438
497
  let command = '';
439
498
  const platform = os.platform()
@@ -1197,6 +1256,7 @@ module.exports = {
1197
1256
  find_venv,
1198
1257
  fill_object,
1199
1258
  run,
1259
+ openURI,
1200
1260
  openURL,
1201
1261
  u2p,
1202
1262
  p2u,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.2.11",
3
+ "version": "7.2.13",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -269,10 +269,10 @@
269
269
  justify-content: center;
270
270
  gap: 6px;
271
271
  min-height: 30px;
272
- border: 1px solid rgba(15, 23, 42, 0.14);
272
+ border: 1px solid var(--pinokio-chrome-accent-bg-light);
273
273
  border-radius: 6px;
274
- background: rgba(255, 255, 255, 0.88);
275
- color: #1f2937;
274
+ background: var(--pinokio-chrome-accent-bg-light);
275
+ color: var(--pinokio-chrome-accent-fg-light);
276
276
  cursor: pointer;
277
277
  font-size: 12px;
278
278
  font-weight: 700;
@@ -282,37 +282,60 @@
282
282
  }
283
283
  .pinokio-draft-button:hover,
284
284
  .pinokio-draft-button:focus {
285
- border-color: rgba(15, 23, 42, 0.24);
286
- background: rgba(15, 23, 42, 0.04);
285
+ border-color: color-mix(in srgb, var(--pinokio-chrome-accent-bg-light) 88%, #ffffff);
286
+ background: color-mix(in srgb, var(--pinokio-chrome-accent-bg-light) 88%, #ffffff);
287
287
  }
288
288
  body.dark .pinokio-draft-button {
289
- border-color: rgba(148, 163, 184, 0.28);
290
- background: rgba(30, 41, 59, 0.92);
291
- color: #f8fafc;
289
+ border-color: #fbbf24;
290
+ background: #fbbf24;
291
+ color: var(--universal-launcher-surface-solid, #0a0a0b);
292
292
  }
293
293
  body.dark .pinokio-draft-button:hover,
294
294
  body.dark .pinokio-draft-button:focus {
295
- border-color: rgba(251, 191, 36, 0.56);
296
- background: rgba(120, 53, 15, 0.32);
295
+ border-color: color-mix(in srgb, #fbbf24 90%, #ffffff);
296
+ background: color-mix(in srgb, #fbbf24 90%, #ffffff);
297
+ color: var(--universal-launcher-surface-solid, #0a0a0b);
297
298
  }
298
299
  .pinokio-draft-button.secondary {
300
+ border-color: rgba(15, 23, 42, 0.14);
299
301
  color: #374151;
300
302
  background: rgba(255, 255, 255, 0.72);
301
303
  }
304
+ .pinokio-draft-button.secondary:hover,
305
+ .pinokio-draft-button.secondary:focus {
306
+ border-color: rgba(15, 23, 42, 0.24);
307
+ background: rgba(15, 23, 42, 0.04);
308
+ }
302
309
  body.dark .pinokio-draft-button.secondary {
310
+ border-color: rgba(148, 163, 184, 0.28);
303
311
  color: #dbeafe;
304
312
  background: transparent;
305
313
  }
314
+ body.dark .pinokio-draft-button.secondary:hover,
315
+ body.dark .pinokio-draft-button.secondary:focus {
316
+ border-color: rgba(148, 163, 184, 0.38);
317
+ background: rgba(255, 255, 255, 0.06);
318
+ }
306
319
  .pinokio-draft-drawer .pinokio-draft-button.secondary {
307
320
  border-color: rgba(15, 23, 42, 0.18);
308
321
  color: #1f2937;
309
322
  background: #ffffff;
310
323
  }
324
+ .pinokio-draft-drawer .pinokio-draft-button.secondary:hover,
325
+ .pinokio-draft-drawer .pinokio-draft-button.secondary:focus {
326
+ border-color: rgba(15, 23, 42, 0.28);
327
+ background: rgba(15, 23, 42, 0.05);
328
+ }
311
329
  body.dark .pinokio-draft-drawer .pinokio-draft-button.secondary {
312
330
  border-color: rgba(148, 163, 184, 0.28);
313
331
  color: #dbeafe;
314
332
  background: transparent;
315
333
  }
334
+ body.dark .pinokio-draft-drawer .pinokio-draft-button.secondary:hover,
335
+ body.dark .pinokio-draft-drawer .pinokio-draft-button.secondary:focus {
336
+ border-color: rgba(148, 163, 184, 0.38);
337
+ background: rgba(255, 255, 255, 0.06);
338
+ }
316
339
  .pinokio-draft-button:disabled {
317
340
  cursor: default;
318
341
  opacity: 0.68;
@@ -1223,7 +1246,7 @@
1223
1246
  } finally {
1224
1247
  if (button) {
1225
1248
  button.disabled = false;
1226
- setActionButtonLabel(button, originalText || "Open draft");
1249
+ setActionButtonLabel(button, originalText || "File explorer");
1227
1250
  }
1228
1251
  }
1229
1252
  }
@@ -1361,7 +1384,7 @@
1361
1384
  tabs.appendChild(createDrawerTab("Markdown", "markdown"));
1362
1385
  tabs.appendChild(createDrawerTab("Media", "media"));
1363
1386
  const actions = createElement("div", "pinokio-draft-drawer-actions");
1364
- const openButton = createActionButton("pinokio-draft-button secondary", "Open draft", "fa-solid fa-folder-open");
1387
+ const openButton = createActionButton("pinokio-draft-button secondary", "File explorer", "fa-solid fa-folder-open");
1365
1388
  openButton.type = "button";
1366
1389
  openButton.addEventListener("click", () => {
1367
1390
  void openDraft(item, openButton);