pinokiod 7.2.12 → 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 (53) 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/git.js +0 -13
  6. package/kernel/index.js +4 -0
  7. package/kernel/plugin.js +6 -58
  8. package/kernel/plugin_sources.js +236 -0
  9. package/kernel/util.js +60 -0
  10. package/package.json +1 -1
  11. package/server/features/drafts/public/drafts.js +35 -12
  12. package/server/index.js +110 -142
  13. package/server/lib/content_validation.js +28 -25
  14. package/server/public/common.js +95 -29
  15. package/server/public/create-launcher.js +4 -31
  16. package/server/public/electron.css +1 -1
  17. package/server/public/style.css +337 -0
  18. package/server/public/task-launcher.js +5 -32
  19. package/server/public/universal-launcher.js +3 -26
  20. package/server/views/app.ejs +8 -29
  21. package/server/views/d.ejs +0 -33
  22. package/server/views/editor.ejs +25 -4
  23. package/server/views/shell.ejs +11 -3
  24. package/server/views/terminal.ejs +15 -3
  25. package/spec/INSTRUCTION_SYNC.md +5 -5
  26. package/system/plugin/antigravity/antigravity.png +0 -0
  27. package/system/plugin/antigravity/pinokio.js +37 -0
  28. package/system/plugin/claude/claude.png +0 -0
  29. package/system/plugin/claude/pinokio.js +63 -0
  30. package/system/plugin/claude-auto/claude.png +0 -0
  31. package/system/plugin/claude-auto/pinokio.js +74 -0
  32. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  33. package/system/plugin/claude-desktop/pinokio.js +39 -0
  34. package/system/plugin/codex/openai.webp +0 -0
  35. package/system/plugin/codex/pinokio.js +58 -0
  36. package/system/plugin/codex-auto/openai.webp +0 -0
  37. package/system/plugin/codex-auto/pinokio.js +65 -0
  38. package/system/plugin/codex-desktop/icon.png +0 -0
  39. package/system/plugin/codex-desktop/pinokio.js +39 -0
  40. package/system/plugin/crush/crush.png +0 -0
  41. package/system/plugin/crush/pinokio.js +31 -0
  42. package/system/plugin/cursor/cursor.jpeg +0 -0
  43. package/system/plugin/cursor/pinokio.js +39 -0
  44. package/system/plugin/gemini/gemini.jpeg +0 -0
  45. package/system/plugin/gemini/pinokio.js +40 -0
  46. package/system/plugin/gemini-auto/gemini.jpeg +0 -0
  47. package/system/plugin/gemini-auto/pinokio.js +43 -0
  48. package/system/plugin/qwen/pinokio.js +50 -0
  49. package/system/plugin/qwen/qwen.png +0 -0
  50. package/system/plugin/vscode/pinokio.js +36 -0
  51. package/system/plugin/vscode/vscode.png +0 -0
  52. package/system/plugin/windsurf/pinokio.js +39 -0
  53. package/system/plugin/windsurf/windsurf.png +0 -0
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.12",
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);
package/server/index.js CHANGED
@@ -78,6 +78,7 @@ const { createLauncherInstructionBootstrap } = require("./lib/launcher_instructi
78
78
  const { createTerminalGitResetHandler } = require("./lib/terminal_git_reset")
79
79
  const { createDesktopEventRouter } = require("./lib/desktop_event_router")
80
80
  const { createInjectRouter, resolveInjectList } = require("./lib/inject_router")
81
+ const PluginSources = require("../kernel/plugin_sources")
81
82
  const { createTaskPackageService } = require("./lib/task_packages")
82
83
  const { createTaskWorkspaceLinkService } = require("./lib/task_workspace_links")
83
84
  const { createWorkspaceRuntimeService } = require("./lib/workspace_runtime")
@@ -495,14 +496,12 @@ class Server {
495
496
  assignProjectSlug(obj)
496
497
  running_dynamic.push(obj)
497
498
  }
498
- } else if (href.startsWith("/run")) {
499
- let uri_path = new URL("http://localhost" + href).pathname
500
- let _filepath = uri_path.split("/").filter(x=>x).slice(1)
501
- let filepath = this.kernel.path(..._filepath)
499
+ } else if (PluginSources.isRunPath(href)) {
500
+ let filepath = PluginSources.resolveRunPath(this.kernel, href)
502
501
  let id = `${filepath}?cwd=${cwd}`
503
502
  obj.script_id = id
504
503
  //if (this.kernel.api.running[filepath]) {
505
- if (obj.src.startsWith("/run" + selected_query.plugin)) {
504
+ if (PluginSources.pluginSelectionMatches(obj.src, selected_query && selected_query.plugin)) {
506
505
  obj.running = true
507
506
  obj.display = "indent"
508
507
  obj.default = true
@@ -2397,8 +2396,10 @@ class Server {
2397
2396
  filepath = full_filepath
2398
2397
  }
2399
2398
 
2400
- if ((req.action || req.originalUrl.startsWith("/run/")) && this.contentValidation) {
2401
- const validation = await this.contentValidation.validateRunPath(pathComponents)
2399
+ if ((req.action || PluginSources.isRunPath(req.originalUrl)) && this.contentValidation) {
2400
+ const validation = await this.contentValidation.validateRunPath(pathComponents, {
2401
+ system: req.pinokioSystem === true,
2402
+ })
2402
2403
  if (validation && !validation.valid) {
2403
2404
  await this.renderInvalidContentPage(req, res, validation, {
2404
2405
  sidebarSelected: validation.type === "plugin" ? "plugins" : validation.type === "task" ? "tasks" : "home",
@@ -2875,6 +2876,9 @@ class Server {
2875
2876
  const draftWatchCwd = draftWatchEnabled
2876
2877
  ? (req.query.cwd || path.dirname(filepath))
2877
2878
  : ""
2879
+ const activeProcessWait = this.kernel.activeProcessWaits && this.kernel.activeProcessWaits[filepath]
2880
+ ? this.kernel.activeProcessWaits[filepath]
2881
+ : null
2878
2882
  const result = {
2879
2883
  portal: this.portal,
2880
2884
  projectName: (pathComponents.length > 0 ? pathComponents[0] : ''),
@@ -2882,6 +2886,11 @@ class Server {
2882
2886
  protection_enabled: protectionPreference ? protectionPreference.protection_enabled !== false : false,
2883
2887
  draft_watch_enabled: draftWatchEnabled,
2884
2888
  draft_watch_cwd: draftWatchCwd,
2889
+ active_process_wait: activeProcessWait ? {
2890
+ title: activeProcessWait.title,
2891
+ description: activeProcessWait.description,
2892
+ message: activeProcessWait.message
2893
+ } : null,
2885
2894
  kill_message,
2886
2895
  callback,
2887
2896
  callback_target,
@@ -2896,6 +2905,7 @@ class Server {
2896
2905
  //run: true, // run mode by default
2897
2906
  run: (req.query && req.query.mode === "source" ? false : true),
2898
2907
  stop: (req.query && req.query.stop ? true : false),
2908
+ readonly: req.pinokioSystem === true,
2899
2909
  pinokioPath,
2900
2910
  action: actionKey,
2901
2911
  runnable,
@@ -3112,6 +3122,7 @@ class Server {
3112
3122
  if (pathComponents.length === 0) {
3113
3123
  const normalizedApiRoot = path.normalize(this.kernel.path("api"))
3114
3124
  const normalizedPluginRoot = path.normalize(this.kernel.path("plugin"))
3125
+ const normalizedSystemPluginRoot = path.normalize(PluginSources.systemPluginRoot(this.kernel))
3115
3126
  const isPathWithinRoot = (candidatePath, rootPath) => {
3116
3127
  if (typeof candidatePath !== "string" || typeof rootPath !== "string") {
3117
3128
  return false
@@ -3137,6 +3148,9 @@ class Server {
3137
3148
  if (isPathWithinRoot(normalizedCandidate, normalizedPluginRoot)) {
3138
3149
  return true
3139
3150
  }
3151
+ if (isPathWithinRoot(normalizedCandidate, normalizedSystemPluginRoot)) {
3152
+ return true
3153
+ }
3140
3154
  const relativeToApiRoot = path.relative(normalizedApiRoot, normalizedCandidate)
3141
3155
  if (!relativeToApiRoot || relativeToApiRoot.startsWith("..") || path.isAbsolute(relativeToApiRoot)) {
3142
3156
  return false
@@ -3950,8 +3964,7 @@ class Server {
3950
3964
  if (menuitem.href.startsWith("http")) {
3951
3965
  menuitem.src = menuitem.href
3952
3966
  } else if (menuitem.href.startsWith("/")) {
3953
- let run_path = "/run"
3954
- if (menuitem.href.startsWith(run_path)) {
3967
+ if (PluginSources.isRunPath(menuitem.href)) {
3955
3968
  menuitem.src = menuitem.href
3956
3969
  // u = new URL("http://localhost" + menuitem.href.slice(run_path.length))
3957
3970
  // cwd = u.searchParams.get("cwd")
@@ -3971,7 +3984,14 @@ class Server {
3971
3984
  }
3972
3985
 
3973
3986
  // check running
3974
- let fullpath = this.kernel.path(menuitem.src.slice(1))
3987
+ let srcPathname = menuitem.src
3988
+ try {
3989
+ srcPathname = new URL("http://localhost" + menuitem.src).pathname
3990
+ } catch (_) {
3991
+ }
3992
+ let fullpath = PluginSources.isRunPath(srcPathname)
3993
+ ? PluginSources.resolveRunPath(this.kernel, srcPathname)
3994
+ : this.kernel.path(srcPathname.slice(1))
3975
3995
  let relpath = path.relative(this.kernel.homedir, fullpath)
3976
3996
  if (relpath.startsWith("api")) {
3977
3997
  // api script
@@ -5685,7 +5705,6 @@ class Server {
5685
5705
  "prototype/PTERM.md",
5686
5706
  ]
5687
5707
  const managedRefreshTargets = [
5688
- "plugin/code",
5689
5708
  "prototype/system",
5690
5709
  "network/system",
5691
5710
  "prototype/PINOKIO.md",
@@ -5729,9 +5748,6 @@ class Server {
5729
5748
  let p2 = path.resolve(home, "prototype/system")
5730
5749
  await fse.remove(p2)
5731
5750
 
5732
- let p3 = path.resolve(home, "plugin/code")
5733
- await fse.remove(p3)
5734
-
5735
5751
  let p4 = path.resolve(home, "network/system")
5736
5752
  await fse.remove(p4)
5737
5753
 
@@ -5800,10 +5816,6 @@ class Server {
5800
5816
  await this.kernel.proto.init()
5801
5817
  }
5802
5818
  if (needsManagedRefresh) {
5803
- const pluginReady = await this.kernel.exists("plugin/code")
5804
- if (!pluginReady && this.kernel.plugin && typeof this.kernel.plugin.init === "function") {
5805
- await this.kernel.plugin.init()
5806
- }
5807
5819
  const networkReady = await this.kernel.exists("network/system")
5808
5820
  if (!networkReady && this.kernel.router && typeof this.kernel.router.init === "function") {
5809
5821
  await this.kernel.router.init()
@@ -5984,6 +5996,10 @@ class Server {
5984
5996
  })
5985
5997
  this.app.use(express.static(path.resolve(__dirname, 'public')));
5986
5998
  this.app.use("/web", express.static(path.resolve(__dirname, "..", "..", "web")))
5999
+ this.app.use(PluginSources.SYSTEM_ASSET_PREFIX, express.static(PluginSources.systemRoot(this.kernel), {
6000
+ index: false,
6001
+ fallthrough: true,
6002
+ }))
5987
6003
  this.app.set('view engine', 'ejs');
5988
6004
  this.app.use((req, res, next) => {
5989
6005
  const peerForwarded = (req.get('X-Pinokio-Peer') || '').trim().toLowerCase()
@@ -6766,24 +6782,7 @@ class Server {
6766
6782
  }
6767
6783
  }
6768
6784
  const normalizePluginPath = (value) => {
6769
- let normalized = typeof value === "string" ? value.trim() : ""
6770
- if (!normalized) {
6771
- return ""
6772
- }
6773
- normalized = normalized.replace(/\\/g, "/")
6774
- if (/^https?:\/\//i.test(normalized)) {
6775
- try {
6776
- const parsed = new URL(normalized)
6777
- normalized = parsed.pathname || ""
6778
- } catch (_) {
6779
- }
6780
- }
6781
- normalized = normalized.replace(/^\/run(?=\/)/, "")
6782
- if (!normalized.startsWith("/")) {
6783
- normalized = `/${normalized}`
6784
- }
6785
- normalized = normalized.replace(/\/{2,}/g, "/").replace(/\/+$/, "")
6786
- return normalized
6785
+ return PluginSources.normalizePluginPath(value)
6787
6786
  }
6788
6787
  const normalizePluginLookupKey = (value) => {
6789
6788
  const normalized = normalizePluginPath(value)
@@ -6930,7 +6929,7 @@ class Server {
6930
6929
  index: -1,
6931
6930
  title: context.title || "Plugin",
6932
6931
  description: typeof config.description === "string" ? config.description : "",
6933
- href: `/run${normalizedPluginPath}`,
6932
+ href: PluginSources.pluginRunHrefForPath(normalizedPluginPath),
6934
6933
  link: typeof config.link === "string" ? config.link : "",
6935
6934
  image: context.image || null,
6936
6935
  icon: null,
@@ -6955,24 +6954,31 @@ class Server {
6955
6954
  }
6956
6955
  const resolvePluginLocalState = async (plugin) => {
6957
6956
  const pluginRoot = path.resolve(this.kernel.path("plugin"))
6958
- const managedRoot = path.resolve(pluginRoot, "code")
6959
6957
  const normalizedPath = normalizePluginPath(plugin && plugin.pluginPath ? plugin.pluginPath : "")
6960
6958
  const emptyState = {
6961
6959
  ownership: "bundled",
6962
6960
  manageable: false,
6963
6961
  canOpenFolder: false,
6964
6962
  pluginRoot,
6965
- managedRoot,
6966
6963
  pluginFilePath: "",
6967
6964
  pluginDir: "",
6968
6965
  relativeDir: "",
6969
6966
  relativeFile: "",
6970
6967
  localLabel: "",
6971
- managedPrefix: "",
6968
+ }
6969
+ if (PluginSources.isSystemPluginPath(normalizedPath)) {
6970
+ return {
6971
+ ...emptyState,
6972
+ ownership: "system",
6973
+ localLabel: normalizedPath.replace(/^\/pinokio\/run\//, ""),
6974
+ }
6972
6975
  }
6973
6976
  if (!normalizedPath.startsWith("/plugin/")) {
6974
6977
  return emptyState
6975
6978
  }
6979
+ if (PluginSources.isLegacyPluginCodePath(normalizedPath)) {
6980
+ return emptyState
6981
+ }
6976
6982
  const pluginFilePath = path.resolve(this.kernel.path(normalizedPath.slice(1)))
6977
6983
  if (!isPathInsideRoot(pluginFilePath, pluginRoot)) {
6978
6984
  return emptyState
@@ -6992,22 +6998,16 @@ class Server {
6992
6998
  }
6993
6999
  const relativeFile = path.relative(pluginRoot, pluginFilePath).split(path.sep).join("/")
6994
7000
  const relativeDir = path.relative(pluginRoot, pluginDir).split(path.sep).join("/")
6995
- const isManaged = isPathInsideRoot(pluginFilePath, managedRoot)
6996
- const managedPrefix = isManaged
6997
- ? path.relative(managedRoot, pluginDir).split(path.sep).join("/")
6998
- : ""
6999
7001
  return {
7000
- ownership: isManaged ? "managed" : "local",
7001
- manageable: Boolean(pluginFileExists && pluginDirExists && !isManaged),
7002
+ ownership: "local",
7003
+ manageable: Boolean(pluginFileExists && pluginDirExists),
7002
7004
  canOpenFolder: Boolean(pluginFileExists && pluginDirExists),
7003
7005
  pluginRoot,
7004
- managedRoot,
7005
7006
  pluginFilePath,
7006
7007
  pluginDir,
7007
7008
  relativeDir,
7008
7009
  relativeFile,
7009
7010
  localLabel: relativeDir ? `plugin/${relativeDir}` : "plugin",
7010
- managedPrefix,
7011
7011
  }
7012
7012
  }
7013
7013
  const collectPluginApps = async (boundAppName = "") => {
@@ -7051,11 +7051,31 @@ class Server {
7051
7051
  })
7052
7052
  return apps
7053
7053
  }
7054
- const buildPluginShareState = async (plugin) => {
7055
- const localState = await resolvePluginLocalState(plugin)
7056
- if (localState.ownership === "bundled") {
7054
+ const buildPluginShareState = async (plugin) => {
7055
+ const localState = await resolvePluginLocalState(plugin)
7056
+ if (localState.ownership === "system") {
7057
7057
  return {
7058
- ownership: "bundled",
7058
+ ownership: "system",
7059
+ manageable: false,
7060
+ canOpenFolder: false,
7061
+ dir: "",
7062
+ localLabel: localState.localLabel || "",
7063
+ remoteUrl: "",
7064
+ remoteWebUrl: "",
7065
+ githubConnected: false,
7066
+ gitInitialized: false,
7067
+ hasCommit: false,
7068
+ changeCount: 0,
7069
+ changes: [],
7070
+ branch: "HEAD",
7071
+ commitUrl: "",
7072
+ createUrl: "",
7073
+ pushUrl: "",
7074
+ }
7075
+ }
7076
+ if (localState.ownership === "bundled") {
7077
+ return {
7078
+ ownership: "bundled",
7059
7079
  manageable: false,
7060
7080
  canOpenFolder: false,
7061
7081
  dir: "",
@@ -7075,49 +7095,21 @@ class Server {
7075
7095
  }
7076
7096
 
7077
7097
  if (localState.ownership === "managed") {
7078
- let changes = []
7079
- try {
7080
- const headStatus = await this.getRepoHeadStatusByDir(localState.managedRoot)
7081
- const allChanges = Array.isArray(headStatus && headStatus.changes) ? headStatus.changes : []
7082
- const managedPrefix = typeof localState.managedPrefix === "string" ? localState.managedPrefix.trim() : ""
7083
- changes = allChanges
7084
- .filter((change) => {
7085
- const file = change && typeof change.file === "string" ? change.file : ""
7086
- if (!managedPrefix) {
7087
- return true
7088
- }
7089
- return file === managedPrefix || file.startsWith(`${managedPrefix}/`)
7090
- })
7091
- .map((change) => {
7092
- const file = change && typeof change.file === "string" ? change.file : ""
7093
- if (!managedPrefix || !file) {
7094
- return change
7095
- }
7096
- const relativeFile = file === managedPrefix
7097
- ? path.posix.basename(managedPrefix)
7098
- : file.slice(managedPrefix.length + 1)
7099
- return {
7100
- ...change,
7101
- file: relativeFile || file
7102
- }
7103
- })
7104
- } catch (_) {
7105
- }
7106
7098
  return {
7107
- ownership: "managed",
7099
+ ownership: "bundled",
7108
7100
  manageable: false,
7109
- canOpenFolder: Boolean(localState.canOpenFolder),
7110
- dir: localState.pluginDir,
7111
- localLabel: localState.localLabel,
7112
- relativeDir: localState.relativeDir,
7113
- relativeFile: localState.relativeFile,
7101
+ canOpenFolder: false,
7102
+ dir: "",
7103
+ localLabel: "",
7104
+ relativeDir: "",
7105
+ relativeFile: "",
7114
7106
  remoteUrl: "",
7115
7107
  remoteWebUrl: "",
7116
7108
  githubConnected: false,
7117
7109
  gitInitialized: false,
7118
7110
  hasCommit: false,
7119
- changeCount: changes.length,
7120
- changes,
7111
+ changeCount: 0,
7112
+ changes: [],
7121
7113
  branch: "HEAD",
7122
7114
  commitUrl: "",
7123
7115
  createUrl: "",
@@ -7193,14 +7185,19 @@ class Server {
7193
7185
  : ""
7194
7186
  const remoteLabel = summarizeTaskRemoteLabel(remoteCandidate)
7195
7187
  const badges = []
7196
- if (ownership === "local") {
7188
+ if (ownership === "local") {
7189
+ badges.push({
7190
+ label: "Local plugin",
7191
+ tone: "accent"
7192
+ })
7193
+ } else if (ownership === "system") {
7197
7194
  badges.push({
7198
- label: "Local plugin",
7199
- tone: "accent"
7195
+ label: "Built-in plugin",
7196
+ tone: "neutral"
7200
7197
  })
7201
- } else if (ownership === "managed") {
7202
- badges.push({
7203
- label: "Managed by Pinokio",
7198
+ } else if (ownership === "managed") {
7199
+ badges.push({
7200
+ label: "Managed by Pinokio",
7204
7201
  tone: "neutral"
7205
7202
  })
7206
7203
  } else {
@@ -7245,13 +7242,12 @@ class Server {
7245
7242
  : (shareState.gitInitialized ? "No local changes" : "Not version tracked yet")
7246
7243
  githubPanelTitle = "GitHub"
7247
7244
  githubPanelCopy = ""
7248
- } else if (ownership === "managed") {
7249
- sourceLabel = "Managed folder"
7250
- sourceValue = shareState.localLabel
7251
- statusValue = changes.length > 0 ? pluralizeTaskFiles(changes.length) : "Updated with Pinokio"
7252
- githubPanelTitle = "Managed by Pinokio"
7253
- githubPanelCopy = "This plugin lives inside <code>plugin/code</code>, which Pinokio refreshes as part of its managed source. Open the folder if you need to inspect it, but don&apos;t treat it as your own publishable repo."
7254
- localChangesCopy = "These edits live inside Pinokio-managed source and may be overwritten by future Pinokio updates."
7245
+ } else if (ownership === "system") {
7246
+ sourceLabel = "System plugin"
7247
+ sourceValue = shareState.localLabel || sourceValue
7248
+ statusValue = "Read-only"
7249
+ githubPanelTitle = "Built in to Pinokio"
7250
+ githubPanelCopy = "This plugin ships with Pinokio and is not editable from the local plugin workspace."
7255
7251
  }
7256
7252
  const changePreview = changes.slice(0, 6).map((change) => ({
7257
7253
  file: change && change.file ? change.file : "",
@@ -8116,24 +8112,7 @@ class Server {
8116
8112
  }
8117
8113
  return targetPath
8118
8114
  }
8119
- const resolveUniversalLauncherPluginHref = (toolValue) => {
8120
- let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
8121
- normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
8122
- normalizedTool = normalizedTool.replace(/^\/+|\/+$/g, "")
8123
- normalizedTool = normalizedTool.replace(/^run\//, "")
8124
- if (!normalizedTool || normalizedTool.includes("..") || !/^[A-Za-z0-9._/-]+$/.test(normalizedTool)) {
8125
- const error = new Error("Invalid plugin.")
8126
- error.status = 400
8127
- throw error
8128
- }
8129
- if (normalizedTool.startsWith("plugin/") || normalizedTool.startsWith("api/")) {
8130
- const scriptPath = normalizedTool.endsWith(".js")
8131
- ? normalizedTool
8132
- : `${normalizedTool}/pinokio.js`
8133
- return `/run/${scriptPath}`
8134
- }
8135
- return `/run/plugin/${normalizedTool}/pinokio.js`
8136
- }
8115
+ const resolveUniversalLauncherPluginHref = PluginSources.resolveLauncherPluginHref
8137
8116
  const persistLauncherPromptContext = async (targetPath, options = {}) => {
8138
8117
  const prompt = typeof options.prompt === "string" ? options.prompt.trim() : ""
8139
8118
  if (!prompt) {
@@ -12815,23 +12794,6 @@ class Server {
12815
12794
  })
12816
12795
  }
12817
12796
  }))
12818
- this.app.post("/plugin/update", ex(async (req, res) => {
12819
- try {
12820
- await this.kernel.exec({
12821
- message: "git pull",
12822
- path: this.kernel.path("plugin/code")
12823
- }, (e) => {
12824
- console.log(e)
12825
- })
12826
- res.json({
12827
- success: true
12828
- })
12829
- } catch (e) {
12830
- res.json({
12831
- error: e.stack
12832
- })
12833
- }
12834
- }))
12835
12797
  this.app.post("/network/reset", ex(async (req, res) => {
12836
12798
  let caddy_path = this.kernel.path("cache/XDG_DATA_HOME/caddy")
12837
12799
  await rimraf(caddy_path)
@@ -14263,6 +14225,17 @@ class Server {
14263
14225
  res.status(404).send(e.message)
14264
14226
  }
14265
14227
  }))
14228
+ this.app.get(`${PluginSources.SYSTEM_RUN_PREFIX}/*`, ex(async (req, res) => {
14229
+ const runPath = typeof req.params[0] === "string" ? req.params[0] : ""
14230
+ let pathComponents = runPath.split("/")
14231
+ req.base = PluginSources.systemRoot(this.kernel)
14232
+ req.pinokioSystem = true
14233
+ try {
14234
+ await this.render(req, res, pathComponents)
14235
+ } catch (e) {
14236
+ res.status(404).send(e.message)
14237
+ }
14238
+ }))
14266
14239
  this.app.get("/run/*", ex(async (req, res) => {
14267
14240
  const runPath = typeof req.params[0] === "string" ? req.params[0] : ""
14268
14241
  let pathComponents = runPath.split("/")
@@ -15688,12 +15661,7 @@ class Server {
15688
15661
  }))
15689
15662
  this.app.get("/bin_ready", ex(async (req, res) => {
15690
15663
  if (this.kernel.bin && !this.kernel.bin.requirements_pending) {
15691
- let code_exists = await this.kernel.exists("plugin/code")
15692
- if (code_exists) {
15693
- res.json({ success: true })
15694
- } else {
15695
- res.json({ success: false })
15696
- }
15664
+ res.json({ success: true })
15697
15665
  } else {
15698
15666
  res.json({ success: false })
15699
15667
  }