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.
- package/Dockerfile +0 -2
- package/kernel/api/index.js +14 -1
- package/kernel/api/process/index.js +99 -44
- package/kernel/api/uri/index.js +51 -0
- package/kernel/git.js +0 -13
- package/kernel/index.js +4 -0
- package/kernel/plugin.js +6 -58
- package/kernel/plugin_sources.js +236 -0
- package/kernel/util.js +60 -0
- package/package.json +1 -1
- package/server/features/drafts/public/drafts.js +35 -12
- package/server/index.js +110 -142
- package/server/lib/content_validation.js +28 -25
- package/server/public/common.js +95 -29
- package/server/public/create-launcher.js +4 -31
- package/server/public/electron.css +1 -1
- package/server/public/style.css +337 -0
- package/server/public/task-launcher.js +5 -32
- package/server/public/universal-launcher.js +3 -26
- package/server/views/app.ejs +8 -29
- package/server/views/d.ejs +0 -33
- package/server/views/editor.ejs +25 -4
- package/server/views/shell.ejs +11 -3
- package/server/views/terminal.ejs +15 -3
- package/spec/INSTRUCTION_SYNC.md +5 -5
- package/system/plugin/antigravity/antigravity.png +0 -0
- package/system/plugin/antigravity/pinokio.js +37 -0
- package/system/plugin/claude/claude.png +0 -0
- package/system/plugin/claude/pinokio.js +63 -0
- package/system/plugin/claude-auto/claude.png +0 -0
- package/system/plugin/claude-auto/pinokio.js +74 -0
- package/system/plugin/claude-desktop/icon.jpeg +0 -0
- package/system/plugin/claude-desktop/pinokio.js +39 -0
- package/system/plugin/codex/openai.webp +0 -0
- package/system/plugin/codex/pinokio.js +58 -0
- package/system/plugin/codex-auto/openai.webp +0 -0
- package/system/plugin/codex-auto/pinokio.js +65 -0
- package/system/plugin/codex-desktop/icon.png +0 -0
- package/system/plugin/codex-desktop/pinokio.js +39 -0
- package/system/plugin/crush/crush.png +0 -0
- package/system/plugin/crush/pinokio.js +31 -0
- package/system/plugin/cursor/cursor.jpeg +0 -0
- package/system/plugin/cursor/pinokio.js +39 -0
- package/system/plugin/gemini/gemini.jpeg +0 -0
- package/system/plugin/gemini/pinokio.js +40 -0
- package/system/plugin/gemini-auto/gemini.jpeg +0 -0
- package/system/plugin/gemini-auto/pinokio.js +43 -0
- package/system/plugin/qwen/pinokio.js +50 -0
- package/system/plugin/qwen/qwen.png +0 -0
- package/system/plugin/vscode/pinokio.js +36 -0
- package/system/plugin/vscode/vscode.png +0 -0
- package/system/plugin/windsurf/pinokio.js +39 -0
- 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
|
@@ -269,10 +269,10 @@
|
|
|
269
269
|
justify-content: center;
|
|
270
270
|
gap: 6px;
|
|
271
271
|
min-height: 30px;
|
|
272
|
-
border: 1px solid
|
|
272
|
+
border: 1px solid var(--pinokio-chrome-accent-bg-light);
|
|
273
273
|
border-radius: 6px;
|
|
274
|
-
background:
|
|
275
|
-
color:
|
|
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:
|
|
286
|
-
background:
|
|
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:
|
|
290
|
-
background:
|
|
291
|
-
color: #
|
|
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:
|
|
296
|
-
background:
|
|
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 || "
|
|
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", "
|
|
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 (
|
|
499
|
-
let
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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:
|
|
7001
|
-
manageable: Boolean(pluginFileExists && pluginDirExists
|
|
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
|
-
|
|
7055
|
-
|
|
7056
|
-
if (localState.ownership === "
|
|
7054
|
+
const buildPluginShareState = async (plugin) => {
|
|
7055
|
+
const localState = await resolvePluginLocalState(plugin)
|
|
7056
|
+
if (localState.ownership === "system") {
|
|
7057
7057
|
return {
|
|
7058
|
-
ownership: "
|
|
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: "
|
|
7099
|
+
ownership: "bundled",
|
|
7108
7100
|
manageable: false,
|
|
7109
|
-
canOpenFolder:
|
|
7110
|
-
dir:
|
|
7111
|
-
localLabel:
|
|
7112
|
-
relativeDir:
|
|
7113
|
-
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:
|
|
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
|
-
|
|
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: "
|
|
7199
|
-
tone: "
|
|
7195
|
+
label: "Built-in plugin",
|
|
7196
|
+
tone: "neutral"
|
|
7200
7197
|
})
|
|
7201
|
-
|
|
7202
|
-
|
|
7203
|
-
|
|
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 === "
|
|
7249
|
-
sourceLabel = "
|
|
7250
|
-
sourceValue = shareState.localLabel
|
|
7251
|
-
statusValue =
|
|
7252
|
-
githubPanelTitle = "
|
|
7253
|
-
githubPanelCopy = "This plugin
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|