pinokiod 7.2.18 → 7.3.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 (89) hide show
  1. package/Dockerfile +2 -0
  2. package/kernel/api/index.js +13 -179
  3. package/kernel/api/process/index.js +44 -99
  4. package/kernel/bin/conda-python.js +30 -0
  5. package/kernel/bin/conda.js +22 -3
  6. package/kernel/bin/huggingface.js +1 -1
  7. package/kernel/bin/index.js +11 -1
  8. package/kernel/environment.js +11 -205
  9. package/kernel/git.js +13 -0
  10. package/kernel/index.js +1 -64
  11. package/kernel/plugin.js +58 -6
  12. package/kernel/prototype.js +0 -4
  13. package/kernel/shell.js +2 -23
  14. package/kernel/util.js +0 -60
  15. package/package.json +1 -1
  16. package/server/index.js +171 -229
  17. package/server/lib/content_validation.js +33 -47
  18. package/server/public/common.js +29 -103
  19. package/server/public/create-launcher.js +31 -4
  20. package/server/public/electron.css +6 -0
  21. package/server/public/style.css +0 -337
  22. package/server/public/task-launcher.css +3 -11
  23. package/server/public/task-launcher.js +32 -5
  24. package/server/public/universal-launcher.js +26 -3
  25. package/server/socket.js +11 -22
  26. package/server/views/app.ejs +30 -167
  27. package/server/views/d.ejs +35 -33
  28. package/server/views/editor.ejs +4 -25
  29. package/server/views/partials/main_sidebar.ejs +0 -1
  30. package/server/views/partials/menu.ejs +1 -1
  31. package/server/views/pre.ejs +1 -1
  32. package/server/views/shell.ejs +3 -11
  33. package/server/views/task_launch.ejs +10 -10
  34. package/server/views/terminal.ejs +5 -34
  35. package/spec/INSTRUCTION_SYNC.md +5 -5
  36. package/kernel/agent_instructions.js +0 -166
  37. package/kernel/api/shell_run_template.js +0 -273
  38. package/kernel/api/uri/index.js +0 -51
  39. package/kernel/plugin_sources.js +0 -289
  40. package/kernel/watch/context.js +0 -42
  41. package/kernel/watch/drivers/fs.js +0 -71
  42. package/kernel/watch/drivers/poll.js +0 -33
  43. package/kernel/watch/index.js +0 -185
  44. package/server/features/index.js +0 -13
  45. package/server/features/notes/index.js +0 -41
  46. package/server/features/notes/parser.js +0 -174
  47. package/server/features/notes/public/notes.css +0 -955
  48. package/server/features/notes/public/notes.js +0 -1149
  49. package/server/features/notes/registry_import.js +0 -412
  50. package/server/features/notes/routes.js +0 -156
  51. package/server/features/notes/service.js +0 -326
  52. package/server/features/notes/watcher.js +0 -74
  53. package/server/lib/workspace_catalog.js +0 -151
  54. package/server/lib/workspace_runtime.js +0 -390
  55. package/server/public/tasker.css +0 -336
  56. package/server/public/tasker.js +0 -407
  57. package/server/routes/workspaces.js +0 -44
  58. package/server/views/partials/workspace_row.ejs +0 -61
  59. package/server/views/tasker.ejs +0 -40
  60. package/server/views/workspaces.ejs +0 -813
  61. package/system/plugin/antigravity/antigravity.png +0 -0
  62. package/system/plugin/antigravity/pinokio.js +0 -35
  63. package/system/plugin/claude/claude.png +0 -0
  64. package/system/plugin/claude/pinokio.js +0 -61
  65. package/system/plugin/claude-auto/claude.png +0 -0
  66. package/system/plugin/claude-auto/pinokio.js +0 -72
  67. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  68. package/system/plugin/claude-desktop/pinokio.js +0 -37
  69. package/system/plugin/codex/openai.webp +0 -0
  70. package/system/plugin/codex/pinokio.js +0 -56
  71. package/system/plugin/codex-auto/openai.webp +0 -0
  72. package/system/plugin/codex-auto/pinokio.js +0 -63
  73. package/system/plugin/codex-desktop/icon.png +0 -0
  74. package/system/plugin/codex-desktop/pinokio.js +0 -37
  75. package/system/plugin/crush/crush.png +0 -0
  76. package/system/plugin/crush/pinokio.js +0 -29
  77. package/system/plugin/cursor/cursor.jpeg +0 -0
  78. package/system/plugin/cursor/pinokio.js +0 -37
  79. package/system/plugin/gemini/gemini.jpeg +0 -0
  80. package/system/plugin/gemini/pinokio.js +0 -38
  81. package/system/plugin/gemini-auto/gemini.jpeg +0 -0
  82. package/system/plugin/gemini-auto/pinokio.js +0 -41
  83. package/system/plugin/qwen/pinokio.js +0 -48
  84. package/system/plugin/qwen/qwen.png +0 -0
  85. package/system/plugin/vscode/pinokio.js +0 -34
  86. package/system/plugin/vscode/vscode.png +0 -0
  87. package/system/plugin/windsurf/pinokio.js +0 -37
  88. package/system/plugin/windsurf/windsurf.png +0 -0
  89. package/test/plugin-sources.test.js +0 -45
@@ -1,7 +1,6 @@
1
1
  const fs = require("fs")
2
2
  const path = require("path")
3
3
  const clearModule = require("clear-module")
4
- const PluginSources = require("../../kernel/plugin_sources")
5
4
 
6
5
  const {
7
6
  TASK_CONFIG_FILENAME,
@@ -118,7 +117,23 @@ function createContentValidationService({ kernel }) {
118
117
  }
119
118
 
120
119
  const normalizePluginPath = (value) => {
121
- return PluginSources.normalizePluginPath(value)
120
+ let normalized = typeof value === "string" ? value.trim() : ""
121
+ if (!normalized) {
122
+ return ""
123
+ }
124
+ normalized = normalized.replace(/\\/g, "/")
125
+ normalized = normalized.replace(/^\/run(?=\/)/, "")
126
+ if (!normalized.startsWith("/")) {
127
+ normalized = `/${normalized}`
128
+ }
129
+ normalized = normalized.replace(/\/{2,}/g, "/").replace(/\/+$/, "")
130
+ if (!normalized) {
131
+ return ""
132
+ }
133
+ if (!normalized.endsWith("/pinokio.js")) {
134
+ normalized = `${normalized}/pinokio.js`
135
+ }
136
+ return normalized
122
137
  }
123
138
 
124
139
  const normalizeBundledPluginSpec = (value) => {
@@ -147,16 +162,16 @@ function createContentValidationService({ kernel }) {
147
162
  absolutePath,
148
163
  dir: pluginDir,
149
164
  config,
150
- hasInstall: PluginSources.isAction(config && config.install),
151
- hasUpdate: PluginSources.isAction(config && config.update),
152
- hasUninstall: PluginSources.isAction(config && config.uninstall),
165
+ hasInstall: Array.isArray(config && config.install),
166
+ hasUpdate: Array.isArray(config && config.update),
167
+ hasUninstall: Array.isArray(config && config.uninstall),
153
168
  image: null,
154
169
  }
155
170
 
156
171
  if (config && typeof config.icon === "string" && config.icon.trim()) {
157
- const pluginImage = PluginSources.pluginAssetHrefForIcon(normalizedPath, config.icon)
158
- if (pluginImage) {
159
- context.image = pluginImage
172
+ if (normalizedPath.startsWith("/plugin/")) {
173
+ const relativeDir = path.posix.dirname(normalizedPath.slice(1))
174
+ context.image = `/asset/${relativeDir}/${config.icon.trim()}`
160
175
  } else if (normalizedPath.startsWith("/api/")) {
161
176
  const segments = normalizedPath.replace(/^\/+/, "").split("/")
162
177
  const appName = segments[1] || ""
@@ -177,10 +192,7 @@ function createContentValidationService({ kernel }) {
177
192
  const fallbackTitle = normalizedPath
178
193
  ? path.basename(path.posix.dirname(normalizedPath))
179
194
  : "Plugin"
180
- const isSystemPlugin = PluginSources.isSystemPluginPath(normalizedPath)
181
- const isHomePlugin = normalizedPath.startsWith("/plugin/")
182
- const isApiPlugin = normalizedPath.startsWith("/api/")
183
- if (!normalizedPath || (!isSystemPlugin && !isHomePlugin && !isApiPlugin)) {
195
+ if (!normalizedPath || (!normalizedPath.startsWith("/plugin/") && !normalizedPath.startsWith("/api/"))) {
184
196
  return buildInvalid({
185
197
  type: "plugin",
186
198
  subjectTitle: fallbackTitle,
@@ -193,21 +205,8 @@ function createContentValidationService({ kernel }) {
193
205
  detailUrl: "/plugins",
194
206
  })
195
207
  }
196
- if (PluginSources.isLegacyPluginCodePath(normalizedPath)) {
197
- return buildInvalid({
198
- type: "plugin",
199
- subjectTitle: fallbackTitle,
200
- errors: [
201
- buildError(
202
- "This managed plugin path is no longer used.",
203
- "Open the built-in Pinokio plugin instead."
204
- ),
205
- ],
206
- detailUrl: "/plugins",
207
- })
208
- }
209
208
 
210
- const absolutePath = PluginSources.pluginPathToAbsolute(kernel, normalizedPath)
209
+ const absolutePath = kernel.path(normalizedPath.replace(/^\/+/, ""))
211
210
  const folderPath = path.dirname(absolutePath)
212
211
  const loaded = await loadJsFile(absolutePath)
213
212
  if (!loaded.exists) {
@@ -252,35 +251,25 @@ function createContentValidationService({ kernel }) {
252
251
  { file: absolutePath }
253
252
  ))
254
253
  } else {
255
- if (!PluginSources.isAction(config.run)) {
254
+ if (!Array.isArray(config.run)) {
256
255
  errors.push(buildError(
257
- "Plugins must define a top-level run array or function.",
258
- "Add `run: [...]` or `run: async (ctx) => [...]` to pinokio.js.",
256
+ "Plugins must define a top-level run array.",
257
+ "Add `run: [...]` to pinokio.js.",
259
258
  { file: absolutePath }
260
259
  ))
261
260
  }
262
- const topLevelFunctionKeys = Object.keys(config).filter((key) => {
263
- return typeof config[key] === "function" && !PluginSources.ACTION_KEYS.has(key)
264
- })
261
+ const topLevelFunctionKeys = Object.keys(config).filter((key) => typeof config[key] === "function")
265
262
  if (topLevelFunctionKeys.length > 0) {
266
263
  errors.push(buildError(
267
264
  `Top-level function fields are not supported: ${topLevelFunctionKeys.join(", ")}.`,
268
- "Only action fields such as run, install, uninstall, and update may be functions.",
265
+ "Move those functions out of pinokio.js or replace them with data.",
269
266
  { file: absolutePath }
270
267
  ))
271
268
  }
272
- for (const key of PluginSources.ACTION_KEYS) {
273
- if (key in config && !PluginSources.isAction(config[key])) {
274
- errors.push(buildError(
275
- `Plugin action ${key} must be an array or function.`,
276
- `Set ${key} to an array or async function returning an array.`,
277
- { file: absolutePath }
278
- ))
279
- }
280
- }
281
269
  if (normalizedPath.startsWith("/plugin/")) {
270
+ const isManagedDefaultPlugin = normalizedPath.startsWith("/plugin/code/")
282
271
  const declaredPath = typeof config.path === "string" ? config.path.trim() : ""
283
- if (declaredPath !== "plugin") {
272
+ if (!isManagedDefaultPlugin && declaredPath !== "plugin") {
284
273
  errors.push(buildError(
285
274
  'Standalone plugins must set `path: "plugin"`.',
286
275
  'Add `path: "plugin"` to pinokio.js.',
@@ -578,7 +567,7 @@ function createContentValidationService({ kernel }) {
578
567
  })
579
568
  }
580
569
 
581
- const validateRunPath = async (pathComponents, options = {}) => {
570
+ const validateRunPath = async (pathComponents) => {
582
571
  if (!Array.isArray(pathComponents) || pathComponents.length === 0) {
583
572
  return buildValid()
584
573
  }
@@ -587,9 +576,6 @@ function createContentValidationService({ kernel }) {
587
576
  return buildValid()
588
577
  }
589
578
  if (normalized[0] === "plugin") {
590
- if (options && options.system) {
591
- return validatePluginByPath(`${PluginSources.SYSTEM_RUN_PREFIX}/${normalized.join("/")}`)
592
- }
593
579
  return validatePluginByPath(`/${normalized.join("/")}`)
594
580
  }
595
581
  if (normalized[0] === "api" && normalized.length > 1) {
@@ -16,7 +16,6 @@ const requirementsGuardedRoutePrefixes = [
16
16
  '/api/',
17
17
  '/_api/',
18
18
  '/run/',
19
- '/pinokio/run/',
20
19
  '/tools',
21
20
  '/bundle/',
22
21
  '/connect/',
@@ -105,96 +104,6 @@ function createMinimalLoadingSwal () {
105
104
  });
106
105
  return close;
107
106
  }
108
-
109
- const PINOKIO_WAIT_FOOTER_ID = 'pinokio-process-wait-footer-status'
110
- const PINOKIO_INSTALL_SPINNER_VARIANTS = ["grid-shift", "single-vacancy", "pulse-swap", "corner-chase", "flip-2x2", "stack-ripple", "pixel-orbit", "binary-shuffle"]
111
- const PINOKIO_INSTALL_STATUS_SPINNER_HTML = `
112
- <span class="install-status-spinner" aria-hidden="true">
113
- <span class="install-status-grid-anchor install-status-grid-anchor-1"></span>
114
- <span class="install-status-grid-anchor install-status-grid-anchor-2"></span>
115
- <span class="install-status-grid-anchor install-status-grid-anchor-3"></span>
116
- <span class="install-status-grid-anchor install-status-grid-anchor-4"></span>
117
- <span class="install-status-grid-tile install-status-grid-tile-1"></span>
118
- <span class="install-status-grid-tile install-status-grid-tile-2"></span>
119
- <span class="install-status-grid-tile install-status-grid-tile-3"></span>
120
- <span class="install-status-grid-tile install-status-grid-tile-4"></span>
121
- <span class="install-status-grid-chaser"></span>
122
- <span class="install-status-grid-pixel"></span>
123
- </span>`
124
- let lastPinokioWaitSpinnerVariant = null
125
-
126
- function escapePinokioStatusHtml(value) {
127
- return String(value == null ? '' : value)
128
- .replace(/&/g, '&amp;')
129
- .replace(/</g, '&lt;')
130
- .replace(/>/g, '&gt;')
131
- .replace(/"/g, '&quot;')
132
- .replace(/'/g, '&#39;')
133
- }
134
-
135
- function ensurePinokioWaitFooterStatus() {
136
- let status = document.getElementById(PINOKIO_WAIT_FOOTER_ID)
137
- if (status) {
138
- return status
139
- }
140
- status = document.createElement('div')
141
- status.id = PINOKIO_WAIT_FOOTER_ID
142
- status.className = 'pinokio-install-inline-status pinokio-process-wait-footer-status'
143
- status.hidden = true
144
- status.setAttribute('aria-live', 'polite')
145
-
146
- const anchor = document.querySelector('.terminal-container')
147
- || document.querySelector('main')
148
- || document.querySelector('#terminal')?.parentElement
149
- || document.querySelector('footer')
150
- || document.body
151
-
152
- if (anchor && anchor !== document.body) {
153
- anchor.insertAdjacentElement('afterend', status)
154
- } else {
155
- document.body.appendChild(status)
156
- }
157
- return status
158
- }
159
-
160
- function showPinokioWaitFooterStatus(data = {}) {
161
- const title = data.title || 'Waiting'
162
- const description = data.description || data.message || ''
163
- const status = ensurePinokioWaitFooterStatus()
164
- const candidates = PINOKIO_INSTALL_SPINNER_VARIANTS.filter((variant) => variant !== lastPinokioWaitSpinnerVariant)
165
- const pool = candidates.length > 0 ? candidates : PINOKIO_INSTALL_SPINNER_VARIANTS
166
- lastPinokioWaitSpinnerVariant = pool[Math.floor(Math.random() * pool.length)]
167
- document.documentElement.dataset.installSpinnerVariant = lastPinokioWaitSpinnerVariant
168
- status.className = 'pinokio-install-inline-status pinokio-process-wait-footer-status is-progress'
169
- status.innerHTML = `
170
- <div class="install-status-shell">
171
- ${PINOKIO_INSTALL_STATUS_SPINNER_HTML}
172
- <div class="install-status-copy">
173
- <div class="install-status-title">${escapePinokioStatusHtml(title)}</div>
174
- ${description ? `<div class="install-status-detail">${escapePinokioStatusHtml(description)}</div>` : ''}
175
- </div>
176
- </div>
177
- `
178
- status.hidden = false
179
- document.body.classList.add('pinokio-install-status-visible')
180
- }
181
-
182
- function hidePinokioWaitFooterStatus() {
183
- const status = document.getElementById(PINOKIO_WAIT_FOOTER_ID)
184
- if (!status) {
185
- return
186
- }
187
- status.hidden = true
188
- status.innerHTML = ''
189
- document.body.classList.remove('pinokio-install-status-visible')
190
- }
191
-
192
- if (typeof window !== 'undefined') {
193
- window.PinokioWaitFooterStatus = {
194
- show: showPinokioWaitFooterStatus,
195
- hide: hidePinokioWaitFooterStatus
196
- }
197
- }
198
107
  function check_ready (targetUrl = null, options = {}) {
199
108
  createLauncherDebugLog('check_ready start');
200
109
  return fetch("/pinokio/requirements_ready").then((res) => {
@@ -2419,14 +2328,6 @@ if (typeof hotkeys === 'function') {
2419
2328
  playNextSound();
2420
2329
  };
2421
2330
 
2422
- window.PinokioPlayNotificationSound = (sound) => {
2423
- if (sound === false || isFalseyString(sound)) {
2424
- return false;
2425
- }
2426
- enqueueSound(typeof sound === 'string' && sound ? sound : '/chime.mp3');
2427
- return true;
2428
- };
2429
-
2430
2331
  const handlePacket = (packet) => {
2431
2332
  if (!packet || packet.id !== CHANNEL_ID || packet.type !== 'notification') {
2432
2333
  return;
@@ -3581,6 +3482,32 @@ document.addEventListener("DOMContentLoaded", () => {
3581
3482
  tools: null,
3582
3483
  toolsPromise: null
3583
3484
  };
3485
+ const ASK_AI_FALLBACK_TOOLS = [
3486
+ {
3487
+ value: 'claude',
3488
+ label: 'Claude Code',
3489
+ iconSrc: '/asset/plugin/code/claude/claude.png',
3490
+ href: '/run/plugin/code/claude/pinokio.js',
3491
+ category: 'CLI',
3492
+ isDefault: true
3493
+ },
3494
+ {
3495
+ value: 'codex',
3496
+ label: 'OpenAI Codex',
3497
+ iconSrc: '/asset/plugin/code/codex/openai.webp',
3498
+ href: '/run/plugin/code/codex/pinokio.js',
3499
+ category: 'CLI',
3500
+ isDefault: false
3501
+ },
3502
+ {
3503
+ value: 'gemini',
3504
+ label: 'Google Gemini CLI',
3505
+ iconSrc: '/asset/plugin/code/gemini/gemini.jpeg',
3506
+ href: '/run/plugin/code/gemini/pinokio.js',
3507
+ category: 'CLI',
3508
+ isDefault: false
3509
+ }
3510
+ ];
3584
3511
 
3585
3512
  initializeUniversalLauncherIntegration();
3586
3513
  initializeCreateLauncherIntegration();
@@ -3993,7 +3920,6 @@ document.addEventListener("DOMContentLoaded", () => {
3993
3920
  return typeof pathname === 'string'
3994
3921
  && (
3995
3922
  pathname.startsWith('/run/plugin/')
3996
- || pathname.startsWith('/pinokio/run/plugin/')
3997
3923
  || (pathname.startsWith('/run/api/') && /\/pinokio\.js$/i.test(pathname))
3998
3924
  );
3999
3925
  }
@@ -4085,11 +4011,11 @@ document.addEventListener("DOMContentLoaded", () => {
4085
4011
  })
4086
4012
  .then((payload) => {
4087
4013
  const mapped = mapPluginMenuToAskAiTools(payload && Array.isArray(payload.menu) ? payload.menu : []);
4088
- return mapped;
4014
+ return mapped.length > 0 ? mapped : ASK_AI_FALLBACK_TOOLS.slice();
4089
4015
  })
4090
4016
  .catch((error) => {
4091
- console.warn('Failed to load Ask AI plugins', error);
4092
- return [];
4017
+ console.warn('Failed to load Ask AI plugins, using fallback list', error);
4018
+ return ASK_AI_FALLBACK_TOOLS.slice();
4093
4019
  })
4094
4020
  .finally(() => {
4095
4021
  askAiState.toolsPromise = null;
@@ -5,6 +5,33 @@
5
5
  return;
6
6
  }
7
7
 
8
+ const FALLBACK_TOOLS = [
9
+ {
10
+ value: 'claude',
11
+ label: 'Claude Code',
12
+ iconSrc: '/asset/plugin/code/claude/claude.png',
13
+ isDefault: true,
14
+ href: '/run/plugin/code/claude/pinokio.js',
15
+ category: 'CLI',
16
+ },
17
+ {
18
+ value: 'codex',
19
+ label: 'OpenAI Codex',
20
+ iconSrc: '/asset/plugin/code/codex/openai.webp',
21
+ isDefault: false,
22
+ href: '/run/plugin/code/codex/pinokio.js',
23
+ category: 'CLI',
24
+ },
25
+ {
26
+ value: 'gemini',
27
+ label: 'Google Gemini CLI',
28
+ iconSrc: '/asset/plugin/code/gemini/gemini.jpeg',
29
+ isDefault: false,
30
+ href: '/run/plugin/code/gemini/pinokio.js',
31
+ category: 'CLI',
32
+ },
33
+ ];
34
+
8
35
  const CATEGORY_ORDER = ['CLI', 'IDE'];
9
36
  const MODAL_VARIANTS = {
10
37
  CREATE: 'create',
@@ -56,7 +83,7 @@
56
83
 
57
84
  let value = '';
58
85
  if (href) {
59
- // Normalize href to a launcher tool path for the backend.
86
+ // Normalize href to a plugin-relative path for the backend (e.g., code/codex)
60
87
  const normalized = href.replace(/^\/run/, '').replace(/^\/+/, '');
61
88
  const parts = normalized.split('/').filter(Boolean);
62
89
  // Expect /plugin/<path...>/pinokio.js -> want <path...>
@@ -112,11 +139,11 @@
112
139
  .then((data) => {
113
140
  const menu = data && Array.isArray(data.menu) ? data.menu : [];
114
141
  const tools = mapPluginMenuToCreateLauncherTools(menu);
115
- return tools;
142
+ return tools.length > 0 ? tools : FALLBACK_TOOLS.slice();
116
143
  })
117
144
  .catch((error) => {
118
- console.warn('Failed to load create launcher plugins', error);
119
- return [];
145
+ console.warn('Falling back to default plugins for create launcher modal', error);
146
+ return FALLBACK_TOOLS.slice();
120
147
  })
121
148
  .finally(() => {
122
149
  loadingTools = null;
@@ -25,6 +25,12 @@ aside {
25
25
  -webkit-app-region: no-drag !important;
26
26
  cursor: auto !important;
27
27
  }
28
+ header.grabbable {
29
+ /*
30
+ padding: 32px 10px 0 !important;
31
+ padding: 26px 0px 0 !important;
32
+ */
33
+ }
28
34
  /*
29
35
  header {
30
36
  cursor: grab;