pinokiod 7.2.17 → 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 (78) hide show
  1. package/Dockerfile +2 -0
  2. package/kernel/api/index.js +1 -42
  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/index.js +11 -1
  7. package/kernel/environment.js +2 -182
  8. package/kernel/git.js +13 -0
  9. package/kernel/index.js +1 -64
  10. package/kernel/plugin.js +58 -6
  11. package/kernel/shell.js +2 -21
  12. package/kernel/util.js +0 -60
  13. package/package.json +1 -1
  14. package/server/index.js +149 -176
  15. package/server/lib/content_validation.js +25 -28
  16. package/server/public/common.js +29 -103
  17. package/server/public/create-launcher.js +31 -4
  18. package/server/public/electron.css +6 -0
  19. package/server/public/style.css +0 -337
  20. package/server/public/task-launcher.js +32 -5
  21. package/server/public/universal-launcher.js +26 -3
  22. package/server/socket.js +11 -22
  23. package/server/views/app.ejs +30 -167
  24. package/server/views/d.ejs +33 -0
  25. package/server/views/editor.ejs +4 -25
  26. package/server/views/partials/main_sidebar.ejs +0 -1
  27. package/server/views/shell.ejs +3 -11
  28. package/server/views/terminal.ejs +3 -23
  29. package/server/views/terminals.ejs +0 -1
  30. package/spec/INSTRUCTION_SYNC.md +5 -5
  31. package/kernel/api/shell_run_template.js +0 -273
  32. package/kernel/api/uri/index.js +0 -51
  33. package/kernel/plugin_sources.js +0 -236
  34. package/kernel/watch/context.js +0 -42
  35. package/kernel/watch/drivers/fs.js +0 -71
  36. package/kernel/watch/drivers/poll.js +0 -33
  37. package/kernel/watch/index.js +0 -158
  38. package/server/features/drafts/index.js +0 -41
  39. package/server/features/drafts/parser.js +0 -169
  40. package/server/features/drafts/public/drafts.js +0 -1504
  41. package/server/features/drafts/registry_import.js +0 -412
  42. package/server/features/drafts/routes.js +0 -68
  43. package/server/features/drafts/service.js +0 -261
  44. package/server/features/drafts/watcher.js +0 -76
  45. package/server/features/index.js +0 -13
  46. package/server/lib/workspace_catalog.js +0 -151
  47. package/server/lib/workspace_runtime.js +0 -390
  48. package/server/routes/workspaces.js +0 -44
  49. package/server/views/partials/workspace_row.ejs +0 -61
  50. package/server/views/workspaces.ejs +0 -812
  51. package/system/plugin/antigravity/antigravity.png +0 -0
  52. package/system/plugin/antigravity/pinokio.js +0 -37
  53. package/system/plugin/claude/claude.png +0 -0
  54. package/system/plugin/claude/pinokio.js +0 -63
  55. package/system/plugin/claude-auto/claude.png +0 -0
  56. package/system/plugin/claude-auto/pinokio.js +0 -74
  57. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  58. package/system/plugin/claude-desktop/pinokio.js +0 -39
  59. package/system/plugin/codex/openai.webp +0 -0
  60. package/system/plugin/codex/pinokio.js +0 -58
  61. package/system/plugin/codex-auto/openai.webp +0 -0
  62. package/system/plugin/codex-auto/pinokio.js +0 -65
  63. package/system/plugin/codex-desktop/icon.png +0 -0
  64. package/system/plugin/codex-desktop/pinokio.js +0 -39
  65. package/system/plugin/crush/crush.png +0 -0
  66. package/system/plugin/crush/pinokio.js +0 -31
  67. package/system/plugin/cursor/cursor.jpeg +0 -0
  68. package/system/plugin/cursor/pinokio.js +0 -39
  69. package/system/plugin/gemini/gemini.jpeg +0 -0
  70. package/system/plugin/gemini/pinokio.js +0 -40
  71. package/system/plugin/gemini-auto/gemini.jpeg +0 -0
  72. package/system/plugin/gemini-auto/pinokio.js +0 -43
  73. package/system/plugin/qwen/pinokio.js +0 -50
  74. package/system/plugin/qwen/qwen.png +0 -0
  75. package/system/plugin/vscode/pinokio.js +0 -36
  76. package/system/plugin/vscode/vscode.png +0 -0
  77. package/system/plugin/windsurf/pinokio.js +0 -39
  78. package/system/plugin/windsurf/windsurf.png +0 -0
package/Dockerfile CHANGED
@@ -37,6 +37,8 @@ RUN mkdir -p /app/.pinokio-seed \
37
37
  && rm -rf /app/.pinokio-seed/network/system/.git \
38
38
  && rm -rf /app/.pinokio-seed/plugin \
39
39
  && mkdir -p /app/.pinokio-seed/plugin \
40
+ && git clone --depth 1 https://github.com/pinokiocomputer/code /app/.pinokio-seed/plugin/code \
41
+ && rm -rf /app/.pinokio-seed/plugin/code/.git \
40
42
  && rm -rf /app/.pinokio-seed/prototype/system \
41
43
  && mkdir -p /app/.pinokio-seed/prototype \
42
44
  && git clone --depth 1 https://github.com/pinokiocomputer/proto /app/.pinokio-seed/prototype/system \
@@ -11,7 +11,6 @@ const fastq = require('fastq')
11
11
  const Loader = require("../loader")
12
12
  const Environment = require("../environment")
13
13
  const Util = require('../util')
14
- const ShellRunTemplate = require('./shell_run_template')
15
14
 
16
15
  class Api {
17
16
  constructor(kernel) {
@@ -29,19 +28,6 @@ class Api {
29
28
  this.child_procs = {}
30
29
  this.lproxy = new Lproxy()
31
30
  }
32
- startData(rpc) {
33
- if (rpc && rpc.method === "process.wait" && rpc.params && typeof rpc.params === "object" && !Array.isArray(rpc.params)) {
34
- let data = rpc
35
- if (typeof data.title === "undefined" && typeof rpc.params.title !== "undefined") {
36
- data = { ...data, title: rpc.params.title }
37
- }
38
- if (typeof data.description === "undefined" && typeof rpc.params.description !== "undefined") {
39
- data = { ...data, description: rpc.params.description }
40
- }
41
- return data
42
- }
43
- return rpc
44
- }
45
31
  async launcher_path(name) {
46
32
  let root_path = this.kernel.path("api", name)
47
33
  let primary_path = path.resolve(root_path, "pinokio.js")
@@ -430,12 +416,6 @@ class Api {
430
416
  await this.process(script.on.stop)
431
417
  }
432
418
  }
433
- if (this.kernel.watch && typeof this.kernel.watch.stop === "function") {
434
- if (req.params.id) {
435
- await this.kernel.watch.stop(req.params.id)
436
- }
437
- await this.kernel.watch.stop(requestPath)
438
- }
439
419
  // reset modules
440
420
  let modpath = this.resolvePath(cwd, req.params.uri)
441
421
  if (this.child_procs[modpath]) {
@@ -1030,7 +1010,6 @@ class Api {
1030
1010
  }
1031
1011
  // replace {{{ }}} with {{ }}
1032
1012
  rpc = this.kernel.template.flatten(rpc)
1033
- rpc = ShellRunTemplate.renderEnvArgs(this.kernel, rpc, memory)
1034
1013
 
1035
1014
  // 6. rpc must have method names
1036
1015
  if (rpc.method) {
@@ -1199,7 +1178,7 @@ class Api {
1199
1178
  this.ondata({
1200
1179
  id: request.id || request.path,
1201
1180
  type: "start",
1202
- data: this.startData(rpc)
1181
+ data: rpc
1203
1182
  })
1204
1183
 
1205
1184
  // DEPRECATED APIS
@@ -1475,25 +1454,6 @@ class Api {
1475
1454
  }
1476
1455
  return false
1477
1456
  }
1478
- async startWatchersForRequest(request, script, scriptDir, input) {
1479
- if (!this.kernel.watch || typeof this.kernel.watch.startForScript !== "function") {
1480
- return
1481
- }
1482
- const id = request.id || request.path
1483
- if (!id) {
1484
- return
1485
- }
1486
- const cwd = request.cwd || scriptDir
1487
- await this.kernel.watch.startForScript({
1488
- id,
1489
- request,
1490
- script,
1491
- cwd,
1492
- dirname: scriptDir,
1493
- input,
1494
- args: input
1495
- })
1496
- }
1497
1457
  createQueue(queue_id, concurrency) {
1498
1458
  this.queues[queue_id] = fastq.promise(async ({ request, rawrpc, input, step, total, cwd, args }) => {
1499
1459
  try {
@@ -1735,7 +1695,6 @@ class Api {
1735
1695
  }
1736
1696
 
1737
1697
  const initialPayload = typeof request.input === "undefined" ? {} : request.input
1738
- await this.startWatchersForRequest(request, script, cwd, initialPayload)
1739
1698
  this.queue(request, steps[0], initialPayload, 0, steps.length, cwd, initialPayload)
1740
1699
 
1741
1700
  } else {
@@ -34,16 +34,6 @@ class Process {
34
34
  constructor() {
35
35
  this.appApi = new AppAPI()
36
36
  }
37
- async waitIndefinitely(req, kernel) {
38
- await new Promise((resolve, reject) => {
39
- this.resolve = resolve
40
-
41
- // register the process with the root uri so it can be manually resolved (with this.resolve()) later
42
- if (kernel && kernel.procs && req && req.parent && req.parent.path) {
43
- kernel.procs[req.parent.path] = this
44
- }
45
- })
46
- }
47
37
  // async start(req, ondata, kernel) {
48
38
  // /*
49
39
  // req := {
@@ -169,8 +159,6 @@ class Process {
169
159
  /*
170
160
  params := {
171
161
  sec: <SECONDS>,
172
- title: (optional) Title to display in the footer while waiting,
173
- description: (optional) Description to display in the footer while waiting,
174
162
  message: (optional) Description to display while waiting,
175
163
  menu: (optional) menu to display in the modal while waiting,
176
164
  // ok: (optional) <ok button text> (if not specified, no ok button),
@@ -181,8 +169,6 @@ class Process {
181
169
 
182
170
  params := {
183
171
  min: <MINUTES>,
184
- title: (optional) Title to display in the footer while waiting,
185
- description: (optional) Description to display in the footer while waiting,
186
172
  message: (optional) Description to display while waiting,
187
173
  menu: (optional) menu to display in the modal while waiting,
188
174
  // ok: (optional) <ok button text> (if not specified, no ok button),
@@ -194,8 +180,6 @@ class Process {
194
180
  params := {
195
181
  url: <URL>,
196
182
  interval: (optional) how often to retry checking (in seconds)
197
- title: (optional) Title to display in the footer while waiting,
198
- description: (optional) Description to display in the footer while waiting,
199
183
  message: (optional) the message to display while retrying (default: no message)
200
184
  }
201
185
 
@@ -214,21 +198,9 @@ class Process {
214
198
 
215
199
  or
216
200
 
217
- params := {
218
- title: (optional) Title to display in the footer while waiting,
219
- description: (optional) Description to display in the footer while waiting,
220
- }
221
-
222
- This waits indefinitely until the script is stopped or the wait is manually
223
- resolved.
224
-
225
- or
226
-
227
201
 
228
202
  params := {
229
203
  on: <wait-on condition https://github.com/jeffbski/wait-on>,
230
- title: (optional) Title to display in the footer while waiting,
231
- description: (optional) Description to display in the footer while waiting,
232
204
  message: (optional) Description to display while waiting,
233
205
  menu: (optional) menu to display in the modal while waiting,
234
206
  // ok: (optional) <ok button text> (if not specified, no ok button),
@@ -240,84 +212,57 @@ class Process {
240
212
  if 'cancel' is pressed before the condition is met, stops the script
241
213
 
242
214
  */
243
- let ms
244
- const waitPath = req && req.parent && req.parent.path
245
- if (waitPath && kernel) {
246
- if (!kernel.activeProcessWaits) {
247
- kernel.activeProcessWaits = {}
215
+ let ms
216
+ if (req.params) {
217
+ if (req.params.app || req.params.id || req.params.name) {
218
+ await this.appApi.waitForAppPresence(req, ondata, kernel)
219
+ return
248
220
  }
249
- kernel.activeProcessWaits[waitPath] = {
250
- path: waitPath,
251
- params: req.params || {},
252
- title: req.params && req.params.title,
253
- description: req.params && req.params.description,
254
- message: req.params && req.params.message,
255
- started: Date.now()
256
- }
257
- }
258
- const showFooter = req.params && (req.params.title || req.params.description)
259
- if (showFooter && typeof ondata === "function") {
260
- ondata(req.params, "process.wait.start")
261
- }
262
- try {
263
- if (req.params) {
264
- if (req.params.app || req.params.id || req.params.name) {
265
- await this.appApi.waitForAppPresence(req, ondata, kernel)
266
- return
221
+ // Display modal
222
+ if (req.params.sec || req.params.min) {
223
+ // Wait
224
+ if (req.params.sec) {
225
+ ms = req.params.sec * 1000
226
+ } else if (req.params.min) {
227
+ ms = req.params.min * 60 * 1000
267
228
  }
268
- // Display modal
269
- if (req.params.sec || req.params.min) {
270
- // Wait
271
- if (req.params.sec) {
272
- ms = req.params.sec * 1000
273
- } else if (req.params.min) {
274
- ms = req.params.min * 60 * 1000
275
- }
276
- await new Promise((resolve, reject) => {
277
- this.resolve = resolve
229
+ await new Promise((resolve, reject) => {
230
+ this.resolve = resolve
278
231
 
279
- // register the process with the root uri so it can be manually resolved (with this.resolve()) later
280
- kernel.procs[req.parent.path] = this
232
+ // register the process with the root uri so it can be manually resolved (with this.resolve()) later
233
+ kernel.procs[req.parent.path] = this
281
234
 
282
- setTimeout(() => {
283
- this.resolve()
284
- }, ms)
235
+ setTimeout(() => {
236
+ this.resolve()
237
+ }, ms)
238
+ })
239
+ } else if (req.params.uri) {
240
+ let interval = req.params.interval ? req.params.interval * 1000 : 1000
241
+ ondata(req.params, "loading.start")
242
+ await waitForUrl(req.params.uri, req.params.message, interval, ondata)
243
+ ondata(req.params, "loading.end")
244
+ } else if (req.params.url) {
245
+ let interval = req.params.interval ? req.params.interval * 1000 : 1000
246
+ ondata(req.params, "loading.start")
247
+ await waitForUrl(req.params.url, req.params.message, interval, ondata)
248
+ ondata(req.params, "loading.end")
249
+ } else if (req.params.on) {
250
+ // Wait
251
+ if (req.params.message) {
252
+ ondata({
253
+ raw: `\r\nWaiting: ${JSON.stringify(req.params.on)}\r\n`
285
254
  })
286
- } else if (req.params.uri) {
287
- let interval = req.params.interval ? req.params.interval * 1000 : 1000
288
- ondata(req.params, "loading.start")
289
- await waitForUrl(req.params.uri, req.params.message, interval, ondata)
290
- ondata(req.params, "loading.end")
291
- } else if (req.params.url) {
292
- let interval = req.params.interval ? req.params.interval * 1000 : 1000
293
- ondata(req.params, "loading.start")
294
- await waitForUrl(req.params.url, req.params.message, interval, ondata)
295
- ondata(req.params, "loading.end")
296
- } else if (req.params.on) {
297
- // Wait
298
- if (req.params.message) {
299
- ondata({
300
- raw: `\r\nWaiting: ${JSON.stringify(req.params.on)}\r\n`
301
- })
302
- ondata(req.params, "wait")
303
- }
304
- console.log("Wait", req.params.on)
305
- await waitOn(req.params.on)
306
- console.log("Wait finished")
307
- ondata(req.params, "wait.end")
308
- } else {
309
- await this.waitIndefinitely(req, kernel)
255
+ ondata(req.params, "wait")
310
256
  }
311
- } else {
312
- await this.waitIndefinitely(req, kernel)
313
- }
314
- } finally {
315
- if (waitPath && kernel && kernel.activeProcessWaits) {
316
- delete kernel.activeProcessWaits[waitPath]
317
- }
318
- if (showFooter && typeof ondata === "function") {
319
- ondata(req.params, "process.wait.end")
257
+ console.log("Wait", req.params.on)
258
+ await waitOn(req.params.on)
259
+ console.log("Wait finished")
260
+ ondata(req.params, "wait.end")
320
261
  }
262
+ } else {
263
+ await new Promise((resolve, reject) => {
264
+ this.resolve = resolve
265
+ })
321
266
  }
322
267
  }
323
268
  }
@@ -0,0 +1,30 @@
1
+ const semver = require('semver')
2
+
3
+ const WINDOWS_PYTHON_SSL_FIX_SPEC = "python=3.10.20=*_1_cpython"
4
+ const WINDOWS_PYTHON_SSL_FIX_VERSION = "3.10.20"
5
+
6
+ const condaBuildNumber = (build) => {
7
+ const chunks = String(build || "").split("_").reverse()
8
+ const buildNumber = chunks.find((chunk) => /^\d+$/.test(chunk))
9
+ return buildNumber ? Number(buildNumber) : null
10
+ }
11
+
12
+ const isWindowsPythonSslFixed = (version, build) => {
13
+ const coerced = semver.coerce(version)
14
+ if (!coerced) {
15
+ return false
16
+ }
17
+ if (!semver.satisfies(coerced, ">=3.10.20 <3.11.0")) {
18
+ return false
19
+ }
20
+ if (semver.eq(coerced, WINDOWS_PYTHON_SSL_FIX_VERSION)) {
21
+ const buildNumber = condaBuildNumber(build)
22
+ return typeof buildNumber === "number" && buildNumber >= 1
23
+ }
24
+ return true
25
+ }
26
+
27
+ module.exports = {
28
+ WINDOWS_PYTHON_SSL_FIX_SPEC,
29
+ isWindowsPythonSslFixed,
30
+ }
@@ -4,6 +4,11 @@ const fetch = require('cross-fetch')
4
4
  const { glob } = require('glob')
5
5
  const semver = require('semver')
6
6
  const { buildCondaListFromMeta } = require('./conda-meta')
7
+ const {
8
+ WINDOWS_PYTHON_SSL_FIX_SPEC,
9
+ isWindowsPythonSslFixed,
10
+ } = require('./conda-python')
11
+
7
12
  class Conda {
8
13
  description = "Pinokio uses Conda to install various useful programs in an isolated manner."
9
14
  urls = {
@@ -160,6 +165,7 @@ report_errors: false`)
160
165
  let conda_check = {}
161
166
  let conda = new Set()
162
167
  let conda_versions = {}
168
+ let conda_builds = {}
163
169
  let start = false
164
170
  for(let line of lines) {
165
171
  if (start) {
@@ -167,8 +173,10 @@ report_errors: false`)
167
173
  if (chunks.length > 2) {
168
174
  let name = chunks[0]
169
175
  let version = chunks[1]
176
+ let build = chunks[2]
170
177
  conda.add(name)
171
178
  conda_versions[name] = version
179
+ conda_builds[name] = build
172
180
  if (name === "conda") {
173
181
  conda_check.conda = true
174
182
  // //if (String(version) === "24.11.1") {
@@ -206,6 +214,9 @@ report_errors: false`)
206
214
  // console.log("semver NOT satisfied")
207
215
  //}
208
216
  }
217
+ if (name === "python") {
218
+ conda_check.python = this.kernel.platform !== "win32" || isWindowsPythonSslFixed(version, build)
219
+ }
209
220
  }
210
221
  } else {
211
222
  if (/.*name.*version.*build.*channel/i.test(line)) {
@@ -215,7 +226,8 @@ report_errors: false`)
215
226
  }
216
227
  this.kernel.bin.installed.conda = conda
217
228
  this.kernel.bin.installed.conda_versions = conda_versions
218
- return conda_check.conda && conda_check.mamba && conda_check.sqlite
229
+ this.kernel.bin.installed.conda_builds = conda_builds
230
+ return conda_check.conda && conda_check.mamba && conda_check.sqlite && (this.kernel.platform !== "win32" || conda_check.python)
219
231
  //return conda_check.conda && conda_check.mamba
220
232
  }
221
233
  async install(req, ondata) {
@@ -300,10 +312,18 @@ report_errors: false`)
300
312
  }).join(" ")
301
313
  console.log("Conda dependencies to install", { mods })
302
314
 
315
+ let condaPackages = [
316
+ `"sqlite=3.47.2"`,
317
+ `"conda-libmamba-solver>=25.4.0"`,
318
+ ]
319
+ if (this.kernel.platform === "win32") {
320
+ condaPackages.unshift(`"${WINDOWS_PYTHON_SSL_FIX_SPEC}"`)
321
+ }
322
+
303
323
  let cmds = [
304
324
  //"conda clean -y --index-cache",
305
325
  "conda clean -y --all",
306
- `conda install -y -c conda-forge "sqlite=3.47.2" "conda-libmamba-solver>=25.4.0" ${mods}`,
326
+ `conda install -y -c conda-forge ${condaPackages.join(" ")} ${mods}`.trim(),
307
327
 
308
328
  // `conda config --file ${this.kernel.path('condarc')} --set remote_connect_timeout_secs 20`,
309
329
  // `conda config --file ${this.kernel.path('condarc')} --set remote_read_timeout_secs 300`,
@@ -339,7 +359,6 @@ report_errors: false`)
339
359
  //if (this.kernel.platform === "win32" || this.kernel.platform === "darwin") {
340
360
  // cmds.push("conda install -y conda-libmamba-solver=24.7.0 conda=24.7.1 --freeze-installed")
341
361
  //}
342
-
343
362
  await this.kernel.bin.exec({
344
363
  message: cmds,
345
364
  env: {
@@ -22,6 +22,7 @@ const VS = require("./vs")
22
22
  const Cuda = require("./cuda")
23
23
  const Torch = require("./torch")
24
24
  const { buildCondaListFromMeta } = require('./conda-meta')
25
+ const { isWindowsPythonSslFixed } = require('./conda-python')
25
26
  const { glob } = require('glob')
26
27
  const fakeUa = require('fake-useragent');
27
28
  const fse = require('fs-extra')
@@ -348,6 +349,8 @@ class Bin {
348
349
  let conda_check = {}
349
350
  let conda = new Set()
350
351
  let conda_versions = {}
352
+ let conda_builds = {}
353
+ this.correct_conda = false
351
354
 
352
355
  //////////////////////////////////////////////////////////////////
353
356
  // exception handling
@@ -386,8 +389,10 @@ class Bin {
386
389
  if (chunks.length > 2) {
387
390
  let name = chunks[0]
388
391
  let version = chunks[1]
392
+ let build = chunks[2]
389
393
  conda.add(name)
390
394
  conda_versions[name] = version
395
+ conda_builds[name] = build
391
396
  if (name === "conda") {
392
397
  conda_check.conda = true
393
398
  }
@@ -408,6 +413,9 @@ class Bin {
408
413
  conda_check.sqlite = true
409
414
  }
410
415
  }
416
+ if (name === "python") {
417
+ conda_check.python = this.platform !== "win32" || isWindowsPythonSslFixed(version, build)
418
+ }
411
419
  }
412
420
  } else {
413
421
  if (/.*name.*version.*build.*channel/i.test(line)) {
@@ -416,13 +424,14 @@ class Bin {
416
424
  }
417
425
  }
418
426
 
419
- if (conda_check.conda && conda_check.mamba && conda_check.sqlite) {
427
+ if (conda_check.conda && conda_check.mamba && conda_check.sqlite && (this.platform !== "win32" || conda_check.python)) {
420
428
  //if (conda_check.conda && conda_check.mamba) {
421
429
  this.correct_conda = true
422
430
  }
423
431
  }
424
432
  this.installed.conda = conda
425
433
  this.installed.conda_versions = conda_versions
434
+ this.installed.conda_builds = conda_builds
426
435
  }
427
436
  async refreshInstalled() {
428
437
 
@@ -430,6 +439,7 @@ class Bin {
430
439
 
431
440
  this.installed_initialized = false
432
441
  this.requirements_cache = {}
442
+ this.correct_conda = false
433
443
 
434
444
  //this.installed = {}
435
445
 
@@ -3,187 +3,7 @@ const portfinder = require('portfinder-cp')
3
3
  const os = require('os')
4
4
  const fs = require('fs')
5
5
  const Util = require('./util')
6
- const TEMP_ENV_KEYS = ["TMP", "TEMP", "TMPDIR", "PIP_TMPDIR"]
7
- const CACHE_ENV_KEYS = ["UV_CACHE_DIR", "PIP_CACHE_DIR"]
8
- const CACHE_PREFLIGHT_KEYS = TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)
9
-
10
- const formatCachePreflightError = (error) => {
11
- if (!error) {
12
- return ""
13
- }
14
- const parts = []
15
- if (error.code) {
16
- parts.push(`code=${error.code}`)
17
- }
18
- if (typeof error.errno !== "undefined") {
19
- parts.push(`errno=${error.errno}`)
20
- }
21
- if (error.syscall) {
22
- parts.push(`syscall=${error.syscall}`)
23
- }
24
- if (error.path) {
25
- parts.push(`path=${error.path}`)
26
- }
27
- if (error.dest) {
28
- parts.push(`dest=${error.dest}`)
29
- }
30
- if (error.message) {
31
- parts.push(`message=${error.message}`)
32
- }
33
- return parts.join(" ")
34
- }
35
-
36
- const logCachePreflight = (message) => {
37
- console.log(`[Pinokio cache preflight] ${message}`)
38
- }
39
-
40
- const probeCacheDir = async (dirPath) => {
41
- const probeDir = path.resolve(
42
- dirPath,
43
- `.pinokio-cache-probe-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`
44
- )
45
- const probeFile = path.resolve(probeDir, "probe.tmp")
46
- const renamedFile = path.resolve(probeDir, "probe-renamed.tmp")
47
- const steps = [
48
- ["create probe directory", () => fs.promises.mkdir(probeDir, { recursive: false })],
49
- ["write probe file", () => fs.promises.writeFile(probeFile, "pinokio")],
50
- ["append probe file", () => fs.promises.appendFile(probeFile, "-cache-probe")],
51
- ["rename probe file", () => fs.promises.rename(probeFile, renamedFile)],
52
- ["delete probe file", () => fs.promises.unlink(renamedFile)],
53
- ["remove probe directory", () => fs.promises.rmdir(probeDir)]
54
- ]
55
-
56
- for (const [step, run] of steps) {
57
- try {
58
- await run()
59
- } catch (error) {
60
- await fs.promises.rm(probeDir, { recursive: true, force: true }).catch(() => {})
61
- return { ok: false, step, error }
62
- }
63
- }
64
- return { ok: true }
65
- }
66
-
67
- const managedCacheEnvDefaults = () => {
68
- const defaults = {}
69
- for (const key of CACHE_PREFLIGHT_KEYS) {
70
- defaults[key] = `./cache/${key}`
71
- }
72
- return defaults
73
- }
74
-
75
- const ensureCachePreflightDir = async (key, targetPath, options = {}) => {
76
- logCachePreflight(`${key}: target=${targetPath}`)
77
- try {
78
- await fs.promises.mkdir(targetPath, { recursive: true })
79
- logCachePreflight(`${key}: mkdir ok`)
80
- } catch (error) {
81
- logCachePreflight(`${key}: mkdir failed ${formatCachePreflightError(error)}`)
82
- }
83
-
84
- const firstProbe = await probeCacheDir(targetPath)
85
- if (firstProbe.ok) {
86
- logCachePreflight(`${key}: probe ok`)
87
- return { key, path: targetPath, repaired: false, ok: true }
88
- }
89
-
90
- logCachePreflight(`${key}: probe failed step="${firstProbe.step}" ${formatCachePreflightError(firstProbe.error)}`)
91
- logCachePreflight(`${key}: repair delete start path=${targetPath}`)
92
-
93
- try {
94
- await fs.promises.rm(targetPath, { recursive: true, force: true })
95
- logCachePreflight(`${key}: repair delete ok`)
96
- } catch (error) {
97
- logCachePreflight(`${key}: repair delete failed ${formatCachePreflightError(error)}`)
98
- if (typeof options.elevatedRepair !== "function") {
99
- return { key, path: targetPath, repaired: false, ok: false, step: "repair delete", error }
100
- }
101
- logCachePreflight(`${key}: elevated repair start path=${targetPath}`)
102
- let elevatedRepair
103
- try {
104
- elevatedRepair = await options.elevatedRepair(targetPath, logCachePreflight)
105
- } catch (repairError) {
106
- elevatedRepair = { ok: false, error: repairError }
107
- }
108
- if (!elevatedRepair || !elevatedRepair.ok) {
109
- return { key, path: targetPath, repaired: false, elevated: true, ok: false, step: "elevated repair", error: elevatedRepair && elevatedRepair.error ? elevatedRepair.error : error }
110
- }
111
- const elevatedProbe = await probeCacheDir(targetPath)
112
- if (elevatedProbe.ok) {
113
- logCachePreflight(`${key}: elevated repair probe ok`)
114
- return { key, path: targetPath, repaired: true, elevated: true, ok: true }
115
- }
116
- logCachePreflight(`${key}: elevated repair probe failed step="${elevatedProbe.step}" ${formatCachePreflightError(elevatedProbe.error)}`)
117
- return { key, path: targetPath, repaired: true, elevated: true, ok: false, step: elevatedProbe.step, error: elevatedProbe.error }
118
- }
119
-
120
- try {
121
- await fs.promises.mkdir(targetPath, { recursive: true })
122
- logCachePreflight(`${key}: repair mkdir ok`)
123
- } catch (error) {
124
- logCachePreflight(`${key}: repair mkdir failed ${formatCachePreflightError(error)}`)
125
- return { key, path: targetPath, repaired: true, ok: false, step: "repair mkdir", error }
126
- }
127
-
128
- const secondProbe = await probeCacheDir(targetPath)
129
- if (secondProbe.ok) {
130
- logCachePreflight(`${key}: repair probe ok`)
131
- return { key, path: targetPath, repaired: true, ok: true }
132
- }
133
-
134
- logCachePreflight(`${key}: repair probe failed step="${secondProbe.step}" ${formatCachePreflightError(secondProbe.error)}`)
135
- return { key, path: targetPath, repaired: true, ok: false, step: secondProbe.step, error: secondProbe.error }
136
- }
137
-
138
- const ensurePinokioCacheDirs = async (kernel, options = {}) => {
139
- if (!kernel || !kernel.homedir) {
140
- return {}
141
- }
142
- const throwOnFailure = !!options.throwOnFailure
143
- const root = path.resolve(kernel.homedir)
144
- const cacheRoot = path.resolve(root, "cache")
145
- const envPath = path.resolve(root, "ENVIRONMENT")
146
- const defaults = managedCacheEnvDefaults()
147
- logCachePreflight(`start root=${root}`)
148
- await Util.update_env(envPath, defaults)
149
- logCachePreflight(`ENVIRONMENT updated keys=${CACHE_PREFLIGHT_KEYS.join(",")}`)
150
- try {
151
- await fs.promises.mkdir(cacheRoot, { recursive: true })
152
- logCachePreflight(`cache root mkdir ok path=${cacheRoot}`)
153
- } catch (error) {
154
- logCachePreflight(`cache root mkdir failed path=${cacheRoot} ${formatCachePreflightError(error)}`)
155
- }
156
-
157
- const errors = []
158
- const results = []
159
-
160
- for (const key of CACHE_PREFLIGHT_KEYS) {
161
- const targetPath = path.resolve(cacheRoot, key)
162
- const result = await ensureCachePreflightDir(key, targetPath, options)
163
- results.push(result)
164
- if (!result.ok) {
165
- errors.push(result)
166
- }
167
- }
168
-
169
- if (errors.length > 0) {
170
- kernel.cacheDirErrors = errors
171
- const message = errors
172
- .map((error) => `${error.key}: ${error.path} (${error.step || "unknown"} ${formatCachePreflightError(error.error)})`)
173
- .join(", ")
174
- logCachePreflight(`failed ${message}`)
175
- if (throwOnFailure) {
176
- throw new Error(`Pinokio could not create writable cache directories: ${message}`)
177
- }
178
- } else {
179
- kernel.cacheDirErrors = []
180
- logCachePreflight(`complete ok checked=${results.length} repaired=${results.filter((result) => result.repaired).length}`)
181
- }
182
-
183
- kernel.cacheDirPreflight = results
184
- const env = await get(root, kernel)
185
- return { env, errors, results }
186
- }
6
+ const platform = os.platform()
187
7
  const ENVS = async () => {
188
8
  // const primary_port = 80
189
9
  // const secondary_port = 42000
@@ -1008,4 +828,4 @@ const init = async (options, kernel) => {
1008
828
  env_path: current
1009
829
  }
1010
830
  }
1011
- module.exports = { ENV, get, get2, init_folders, ensurePinokioCacheDirs, requirements, init, get_root }
831
+ module.exports = { ENV, get, get2, init_folders, requirements, init, get_root }
package/kernel/git.js CHANGED
@@ -1378,6 +1378,19 @@ class Git {
1378
1378
  const absoluteTarget = path.resolve(targetPath)
1379
1379
  const home = path.resolve(this.kernel.homedir)
1380
1380
  const managedTargets = [
1381
+ {
1382
+ kind: "plugin",
1383
+ root: path.resolve(home, "plugin/code"),
1384
+ matches: (root, target) => target === root || target.startsWith(`${root}${path.sep}`),
1385
+ bootstrap: async () => {
1386
+ await fs.promises.rm(path.resolve(home, "plugin/code"), { recursive: true, force: true })
1387
+ this.dirs.delete(path.resolve(home, "plugin/code"))
1388
+ if (this.kernel.plugin && typeof this.kernel.plugin.init === "function") {
1389
+ await this.kernel.plugin.init()
1390
+ }
1391
+ },
1392
+ exists: async () => this.kernel.exists("plugin/code")
1393
+ },
1381
1394
  {
1382
1395
  kind: "prototype",
1383
1396
  root: path.resolve(home, "prototype/system"),