pinokiod 7.2.11 → 7.2.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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/environment.js +22 -3
- package/kernel/git.js +0 -13
- package/kernel/index.js +61 -1
- 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 +120 -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/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",
|
|
@@ -5707,6 +5726,16 @@ class Server {
|
|
|
5707
5726
|
|
|
5708
5727
|
needsManagedRefresh = true
|
|
5709
5728
|
console.log("[TRY] Updating to the new version")
|
|
5729
|
+
let envPath = path.resolve(home, "ENVIRONMENT")
|
|
5730
|
+
let envExists = await this.kernel.exists(envPath)
|
|
5731
|
+
if (!envExists) {
|
|
5732
|
+
let str = await Environment.ENV("system", home, this.kernel)
|
|
5733
|
+
await fs.promises.writeFile(envPath, str)
|
|
5734
|
+
}
|
|
5735
|
+
await Environment.ensurePinokioCacheDirs(this.kernel, {
|
|
5736
|
+
throwOnFailure: true,
|
|
5737
|
+
elevatedRepair: this.kernel.elevatedCacheRepair.bind(this.kernel)
|
|
5738
|
+
})
|
|
5710
5739
|
this.kernel.store.set("version", this.version.pinokiod)
|
|
5711
5740
|
console.log("[DONE] Updating to the new version")
|
|
5712
5741
|
console.log("not up to date. update py.")
|
|
@@ -5719,9 +5748,6 @@ class Server {
|
|
|
5719
5748
|
let p2 = path.resolve(home, "prototype/system")
|
|
5720
5749
|
await fse.remove(p2)
|
|
5721
5750
|
|
|
5722
|
-
let p3 = path.resolve(home, "plugin/code")
|
|
5723
|
-
await fse.remove(p3)
|
|
5724
|
-
|
|
5725
5751
|
let p4 = path.resolve(home, "network/system")
|
|
5726
5752
|
await fse.remove(p4)
|
|
5727
5753
|
|
|
@@ -5790,10 +5816,6 @@ class Server {
|
|
|
5790
5816
|
await this.kernel.proto.init()
|
|
5791
5817
|
}
|
|
5792
5818
|
if (needsManagedRefresh) {
|
|
5793
|
-
const pluginReady = await this.kernel.exists("plugin/code")
|
|
5794
|
-
if (!pluginReady && this.kernel.plugin && typeof this.kernel.plugin.init === "function") {
|
|
5795
|
-
await this.kernel.plugin.init()
|
|
5796
|
-
}
|
|
5797
5819
|
const networkReady = await this.kernel.exists("network/system")
|
|
5798
5820
|
if (!networkReady && this.kernel.router && typeof this.kernel.router.init === "function") {
|
|
5799
5821
|
await this.kernel.router.init()
|
|
@@ -5974,6 +5996,10 @@ class Server {
|
|
|
5974
5996
|
})
|
|
5975
5997
|
this.app.use(express.static(path.resolve(__dirname, 'public')));
|
|
5976
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
|
+
}))
|
|
5977
6003
|
this.app.set('view engine', 'ejs');
|
|
5978
6004
|
this.app.use((req, res, next) => {
|
|
5979
6005
|
const peerForwarded = (req.get('X-Pinokio-Peer') || '').trim().toLowerCase()
|
|
@@ -6756,24 +6782,7 @@ class Server {
|
|
|
6756
6782
|
}
|
|
6757
6783
|
}
|
|
6758
6784
|
const normalizePluginPath = (value) => {
|
|
6759
|
-
|
|
6760
|
-
if (!normalized) {
|
|
6761
|
-
return ""
|
|
6762
|
-
}
|
|
6763
|
-
normalized = normalized.replace(/\\/g, "/")
|
|
6764
|
-
if (/^https?:\/\//i.test(normalized)) {
|
|
6765
|
-
try {
|
|
6766
|
-
const parsed = new URL(normalized)
|
|
6767
|
-
normalized = parsed.pathname || ""
|
|
6768
|
-
} catch (_) {
|
|
6769
|
-
}
|
|
6770
|
-
}
|
|
6771
|
-
normalized = normalized.replace(/^\/run(?=\/)/, "")
|
|
6772
|
-
if (!normalized.startsWith("/")) {
|
|
6773
|
-
normalized = `/${normalized}`
|
|
6774
|
-
}
|
|
6775
|
-
normalized = normalized.replace(/\/{2,}/g, "/").replace(/\/+$/, "")
|
|
6776
|
-
return normalized
|
|
6785
|
+
return PluginSources.normalizePluginPath(value)
|
|
6777
6786
|
}
|
|
6778
6787
|
const normalizePluginLookupKey = (value) => {
|
|
6779
6788
|
const normalized = normalizePluginPath(value)
|
|
@@ -6920,7 +6929,7 @@ class Server {
|
|
|
6920
6929
|
index: -1,
|
|
6921
6930
|
title: context.title || "Plugin",
|
|
6922
6931
|
description: typeof config.description === "string" ? config.description : "",
|
|
6923
|
-
href:
|
|
6932
|
+
href: PluginSources.pluginRunHrefForPath(normalizedPluginPath),
|
|
6924
6933
|
link: typeof config.link === "string" ? config.link : "",
|
|
6925
6934
|
image: context.image || null,
|
|
6926
6935
|
icon: null,
|
|
@@ -6945,24 +6954,31 @@ class Server {
|
|
|
6945
6954
|
}
|
|
6946
6955
|
const resolvePluginLocalState = async (plugin) => {
|
|
6947
6956
|
const pluginRoot = path.resolve(this.kernel.path("plugin"))
|
|
6948
|
-
const managedRoot = path.resolve(pluginRoot, "code")
|
|
6949
6957
|
const normalizedPath = normalizePluginPath(plugin && plugin.pluginPath ? plugin.pluginPath : "")
|
|
6950
6958
|
const emptyState = {
|
|
6951
6959
|
ownership: "bundled",
|
|
6952
6960
|
manageable: false,
|
|
6953
6961
|
canOpenFolder: false,
|
|
6954
6962
|
pluginRoot,
|
|
6955
|
-
managedRoot,
|
|
6956
6963
|
pluginFilePath: "",
|
|
6957
6964
|
pluginDir: "",
|
|
6958
6965
|
relativeDir: "",
|
|
6959
6966
|
relativeFile: "",
|
|
6960
6967
|
localLabel: "",
|
|
6961
|
-
|
|
6968
|
+
}
|
|
6969
|
+
if (PluginSources.isSystemPluginPath(normalizedPath)) {
|
|
6970
|
+
return {
|
|
6971
|
+
...emptyState,
|
|
6972
|
+
ownership: "system",
|
|
6973
|
+
localLabel: normalizedPath.replace(/^\/pinokio\/run\//, ""),
|
|
6974
|
+
}
|
|
6962
6975
|
}
|
|
6963
6976
|
if (!normalizedPath.startsWith("/plugin/")) {
|
|
6964
6977
|
return emptyState
|
|
6965
6978
|
}
|
|
6979
|
+
if (PluginSources.isLegacyPluginCodePath(normalizedPath)) {
|
|
6980
|
+
return emptyState
|
|
6981
|
+
}
|
|
6966
6982
|
const pluginFilePath = path.resolve(this.kernel.path(normalizedPath.slice(1)))
|
|
6967
6983
|
if (!isPathInsideRoot(pluginFilePath, pluginRoot)) {
|
|
6968
6984
|
return emptyState
|
|
@@ -6982,22 +6998,16 @@ class Server {
|
|
|
6982
6998
|
}
|
|
6983
6999
|
const relativeFile = path.relative(pluginRoot, pluginFilePath).split(path.sep).join("/")
|
|
6984
7000
|
const relativeDir = path.relative(pluginRoot, pluginDir).split(path.sep).join("/")
|
|
6985
|
-
const isManaged = isPathInsideRoot(pluginFilePath, managedRoot)
|
|
6986
|
-
const managedPrefix = isManaged
|
|
6987
|
-
? path.relative(managedRoot, pluginDir).split(path.sep).join("/")
|
|
6988
|
-
: ""
|
|
6989
7001
|
return {
|
|
6990
|
-
ownership:
|
|
6991
|
-
manageable: Boolean(pluginFileExists && pluginDirExists
|
|
7002
|
+
ownership: "local",
|
|
7003
|
+
manageable: Boolean(pluginFileExists && pluginDirExists),
|
|
6992
7004
|
canOpenFolder: Boolean(pluginFileExists && pluginDirExists),
|
|
6993
7005
|
pluginRoot,
|
|
6994
|
-
managedRoot,
|
|
6995
7006
|
pluginFilePath,
|
|
6996
7007
|
pluginDir,
|
|
6997
7008
|
relativeDir,
|
|
6998
7009
|
relativeFile,
|
|
6999
7010
|
localLabel: relativeDir ? `plugin/${relativeDir}` : "plugin",
|
|
7000
|
-
managedPrefix,
|
|
7001
7011
|
}
|
|
7002
7012
|
}
|
|
7003
7013
|
const collectPluginApps = async (boundAppName = "") => {
|
|
@@ -7041,11 +7051,31 @@ class Server {
|
|
|
7041
7051
|
})
|
|
7042
7052
|
return apps
|
|
7043
7053
|
}
|
|
7044
|
-
|
|
7045
|
-
|
|
7046
|
-
if (localState.ownership === "
|
|
7054
|
+
const buildPluginShareState = async (plugin) => {
|
|
7055
|
+
const localState = await resolvePluginLocalState(plugin)
|
|
7056
|
+
if (localState.ownership === "system") {
|
|
7047
7057
|
return {
|
|
7048
|
-
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",
|
|
7049
7079
|
manageable: false,
|
|
7050
7080
|
canOpenFolder: false,
|
|
7051
7081
|
dir: "",
|
|
@@ -7065,49 +7095,21 @@ class Server {
|
|
|
7065
7095
|
}
|
|
7066
7096
|
|
|
7067
7097
|
if (localState.ownership === "managed") {
|
|
7068
|
-
let changes = []
|
|
7069
|
-
try {
|
|
7070
|
-
const headStatus = await this.getRepoHeadStatusByDir(localState.managedRoot)
|
|
7071
|
-
const allChanges = Array.isArray(headStatus && headStatus.changes) ? headStatus.changes : []
|
|
7072
|
-
const managedPrefix = typeof localState.managedPrefix === "string" ? localState.managedPrefix.trim() : ""
|
|
7073
|
-
changes = allChanges
|
|
7074
|
-
.filter((change) => {
|
|
7075
|
-
const file = change && typeof change.file === "string" ? change.file : ""
|
|
7076
|
-
if (!managedPrefix) {
|
|
7077
|
-
return true
|
|
7078
|
-
}
|
|
7079
|
-
return file === managedPrefix || file.startsWith(`${managedPrefix}/`)
|
|
7080
|
-
})
|
|
7081
|
-
.map((change) => {
|
|
7082
|
-
const file = change && typeof change.file === "string" ? change.file : ""
|
|
7083
|
-
if (!managedPrefix || !file) {
|
|
7084
|
-
return change
|
|
7085
|
-
}
|
|
7086
|
-
const relativeFile = file === managedPrefix
|
|
7087
|
-
? path.posix.basename(managedPrefix)
|
|
7088
|
-
: file.slice(managedPrefix.length + 1)
|
|
7089
|
-
return {
|
|
7090
|
-
...change,
|
|
7091
|
-
file: relativeFile || file
|
|
7092
|
-
}
|
|
7093
|
-
})
|
|
7094
|
-
} catch (_) {
|
|
7095
|
-
}
|
|
7096
7098
|
return {
|
|
7097
|
-
ownership: "
|
|
7099
|
+
ownership: "bundled",
|
|
7098
7100
|
manageable: false,
|
|
7099
|
-
canOpenFolder:
|
|
7100
|
-
dir:
|
|
7101
|
-
localLabel:
|
|
7102
|
-
relativeDir:
|
|
7103
|
-
relativeFile:
|
|
7101
|
+
canOpenFolder: false,
|
|
7102
|
+
dir: "",
|
|
7103
|
+
localLabel: "",
|
|
7104
|
+
relativeDir: "",
|
|
7105
|
+
relativeFile: "",
|
|
7104
7106
|
remoteUrl: "",
|
|
7105
7107
|
remoteWebUrl: "",
|
|
7106
7108
|
githubConnected: false,
|
|
7107
7109
|
gitInitialized: false,
|
|
7108
7110
|
hasCommit: false,
|
|
7109
|
-
changeCount:
|
|
7110
|
-
changes,
|
|
7111
|
+
changeCount: 0,
|
|
7112
|
+
changes: [],
|
|
7111
7113
|
branch: "HEAD",
|
|
7112
7114
|
commitUrl: "",
|
|
7113
7115
|
createUrl: "",
|
|
@@ -7183,14 +7185,19 @@ class Server {
|
|
|
7183
7185
|
: ""
|
|
7184
7186
|
const remoteLabel = summarizeTaskRemoteLabel(remoteCandidate)
|
|
7185
7187
|
const badges = []
|
|
7186
|
-
|
|
7188
|
+
if (ownership === "local") {
|
|
7189
|
+
badges.push({
|
|
7190
|
+
label: "Local plugin",
|
|
7191
|
+
tone: "accent"
|
|
7192
|
+
})
|
|
7193
|
+
} else if (ownership === "system") {
|
|
7187
7194
|
badges.push({
|
|
7188
|
-
label: "
|
|
7189
|
-
tone: "
|
|
7195
|
+
label: "Built-in plugin",
|
|
7196
|
+
tone: "neutral"
|
|
7190
7197
|
})
|
|
7191
|
-
|
|
7192
|
-
|
|
7193
|
-
|
|
7198
|
+
} else if (ownership === "managed") {
|
|
7199
|
+
badges.push({
|
|
7200
|
+
label: "Managed by Pinokio",
|
|
7194
7201
|
tone: "neutral"
|
|
7195
7202
|
})
|
|
7196
7203
|
} else {
|
|
@@ -7235,13 +7242,12 @@ class Server {
|
|
|
7235
7242
|
: (shareState.gitInitialized ? "No local changes" : "Not version tracked yet")
|
|
7236
7243
|
githubPanelTitle = "GitHub"
|
|
7237
7244
|
githubPanelCopy = ""
|
|
7238
|
-
} else if (ownership === "
|
|
7239
|
-
sourceLabel = "
|
|
7240
|
-
sourceValue = shareState.localLabel
|
|
7241
|
-
statusValue =
|
|
7242
|
-
githubPanelTitle = "
|
|
7243
|
-
githubPanelCopy = "This plugin
|
|
7244
|
-
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."
|
|
7245
7251
|
}
|
|
7246
7252
|
const changePreview = changes.slice(0, 6).map((change) => ({
|
|
7247
7253
|
file: change && change.file ? change.file : "",
|
|
@@ -8106,24 +8112,7 @@ class Server {
|
|
|
8106
8112
|
}
|
|
8107
8113
|
return targetPath
|
|
8108
8114
|
}
|
|
8109
|
-
|
|
8110
|
-
let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
|
|
8111
|
-
normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
|
|
8112
|
-
normalizedTool = normalizedTool.replace(/^\/+|\/+$/g, "")
|
|
8113
|
-
normalizedTool = normalizedTool.replace(/^run\//, "")
|
|
8114
|
-
if (!normalizedTool || normalizedTool.includes("..") || !/^[A-Za-z0-9._/-]+$/.test(normalizedTool)) {
|
|
8115
|
-
const error = new Error("Invalid plugin.")
|
|
8116
|
-
error.status = 400
|
|
8117
|
-
throw error
|
|
8118
|
-
}
|
|
8119
|
-
if (normalizedTool.startsWith("plugin/") || normalizedTool.startsWith("api/")) {
|
|
8120
|
-
const scriptPath = normalizedTool.endsWith(".js")
|
|
8121
|
-
? normalizedTool
|
|
8122
|
-
: `${normalizedTool}/pinokio.js`
|
|
8123
|
-
return `/run/${scriptPath}`
|
|
8124
|
-
}
|
|
8125
|
-
return `/run/plugin/${normalizedTool}/pinokio.js`
|
|
8126
|
-
}
|
|
8115
|
+
const resolveUniversalLauncherPluginHref = PluginSources.resolveLauncherPluginHref
|
|
8127
8116
|
const persistLauncherPromptContext = async (targetPath, options = {}) => {
|
|
8128
8117
|
const prompt = typeof options.prompt === "string" ? options.prompt.trim() : ""
|
|
8129
8118
|
if (!prompt) {
|
|
@@ -12805,23 +12794,6 @@ class Server {
|
|
|
12805
12794
|
})
|
|
12806
12795
|
}
|
|
12807
12796
|
}))
|
|
12808
|
-
this.app.post("/plugin/update", ex(async (req, res) => {
|
|
12809
|
-
try {
|
|
12810
|
-
await this.kernel.exec({
|
|
12811
|
-
message: "git pull",
|
|
12812
|
-
path: this.kernel.path("plugin/code")
|
|
12813
|
-
}, (e) => {
|
|
12814
|
-
console.log(e)
|
|
12815
|
-
})
|
|
12816
|
-
res.json({
|
|
12817
|
-
success: true
|
|
12818
|
-
})
|
|
12819
|
-
} catch (e) {
|
|
12820
|
-
res.json({
|
|
12821
|
-
error: e.stack
|
|
12822
|
-
})
|
|
12823
|
-
}
|
|
12824
|
-
}))
|
|
12825
12797
|
this.app.post("/network/reset", ex(async (req, res) => {
|
|
12826
12798
|
let caddy_path = this.kernel.path("cache/XDG_DATA_HOME/caddy")
|
|
12827
12799
|
await rimraf(caddy_path)
|
|
@@ -14253,6 +14225,17 @@ class Server {
|
|
|
14253
14225
|
res.status(404).send(e.message)
|
|
14254
14226
|
}
|
|
14255
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
|
+
}))
|
|
14256
14239
|
this.app.get("/run/*", ex(async (req, res) => {
|
|
14257
14240
|
const runPath = typeof req.params[0] === "string" ? req.params[0] : ""
|
|
14258
14241
|
let pathComponents = runPath.split("/")
|
|
@@ -15678,12 +15661,7 @@ class Server {
|
|
|
15678
15661
|
}))
|
|
15679
15662
|
this.app.get("/bin_ready", ex(async (req, res) => {
|
|
15680
15663
|
if (this.kernel.bin && !this.kernel.bin.requirements_pending) {
|
|
15681
|
-
|
|
15682
|
-
if (code_exists) {
|
|
15683
|
-
res.json({ success: true })
|
|
15684
|
-
} else {
|
|
15685
|
-
res.json({ success: false })
|
|
15686
|
-
}
|
|
15664
|
+
res.json({ success: true })
|
|
15687
15665
|
} else {
|
|
15688
15666
|
res.json({ success: false })
|
|
15689
15667
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
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")
|
|
4
5
|
|
|
5
6
|
const {
|
|
6
7
|
TASK_CONFIG_FILENAME,
|
|
@@ -117,23 +118,7 @@ function createContentValidationService({ kernel }) {
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
const normalizePluginPath = (value) => {
|
|
120
|
-
|
|
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
|
|
121
|
+
return PluginSources.normalizePluginPath(value)
|
|
137
122
|
}
|
|
138
123
|
|
|
139
124
|
const normalizeBundledPluginSpec = (value) => {
|
|
@@ -169,9 +154,9 @@ function createContentValidationService({ kernel }) {
|
|
|
169
154
|
}
|
|
170
155
|
|
|
171
156
|
if (config && typeof config.icon === "string" && config.icon.trim()) {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
context.image =
|
|
157
|
+
const pluginImage = PluginSources.pluginAssetHrefForIcon(normalizedPath, config.icon)
|
|
158
|
+
if (pluginImage) {
|
|
159
|
+
context.image = pluginImage
|
|
175
160
|
} else if (normalizedPath.startsWith("/api/")) {
|
|
176
161
|
const segments = normalizedPath.replace(/^\/+/, "").split("/")
|
|
177
162
|
const appName = segments[1] || ""
|
|
@@ -192,7 +177,10 @@ function createContentValidationService({ kernel }) {
|
|
|
192
177
|
const fallbackTitle = normalizedPath
|
|
193
178
|
? path.basename(path.posix.dirname(normalizedPath))
|
|
194
179
|
: "Plugin"
|
|
195
|
-
|
|
180
|
+
const isSystemPlugin = PluginSources.isSystemPluginPath(normalizedPath)
|
|
181
|
+
const isHomePlugin = normalizedPath.startsWith("/plugin/")
|
|
182
|
+
const isApiPlugin = normalizedPath.startsWith("/api/")
|
|
183
|
+
if (!normalizedPath || (!isSystemPlugin && !isHomePlugin && !isApiPlugin)) {
|
|
196
184
|
return buildInvalid({
|
|
197
185
|
type: "plugin",
|
|
198
186
|
subjectTitle: fallbackTitle,
|
|
@@ -205,8 +193,21 @@ function createContentValidationService({ kernel }) {
|
|
|
205
193
|
detailUrl: "/plugins",
|
|
206
194
|
})
|
|
207
195
|
}
|
|
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
|
+
}
|
|
208
209
|
|
|
209
|
-
const absolutePath =
|
|
210
|
+
const absolutePath = PluginSources.pluginPathToAbsolute(kernel, normalizedPath)
|
|
210
211
|
const folderPath = path.dirname(absolutePath)
|
|
211
212
|
const loaded = await loadJsFile(absolutePath)
|
|
212
213
|
if (!loaded.exists) {
|
|
@@ -267,9 +268,8 @@ function createContentValidationService({ kernel }) {
|
|
|
267
268
|
))
|
|
268
269
|
}
|
|
269
270
|
if (normalizedPath.startsWith("/plugin/")) {
|
|
270
|
-
const isManagedDefaultPlugin = normalizedPath.startsWith("/plugin/code/")
|
|
271
271
|
const declaredPath = typeof config.path === "string" ? config.path.trim() : ""
|
|
272
|
-
if (
|
|
272
|
+
if (declaredPath !== "plugin") {
|
|
273
273
|
errors.push(buildError(
|
|
274
274
|
'Standalone plugins must set `path: "plugin"`.',
|
|
275
275
|
'Add `path: "plugin"` to pinokio.js.',
|
|
@@ -567,7 +567,7 @@ function createContentValidationService({ kernel }) {
|
|
|
567
567
|
})
|
|
568
568
|
}
|
|
569
569
|
|
|
570
|
-
const validateRunPath = async (pathComponents) => {
|
|
570
|
+
const validateRunPath = async (pathComponents, options = {}) => {
|
|
571
571
|
if (!Array.isArray(pathComponents) || pathComponents.length === 0) {
|
|
572
572
|
return buildValid()
|
|
573
573
|
}
|
|
@@ -576,6 +576,9 @@ function createContentValidationService({ kernel }) {
|
|
|
576
576
|
return buildValid()
|
|
577
577
|
}
|
|
578
578
|
if (normalized[0] === "plugin") {
|
|
579
|
+
if (options && options.system) {
|
|
580
|
+
return validatePluginByPath(`${PluginSources.SYSTEM_RUN_PREFIX}/${normalized.join("/")}`)
|
|
581
|
+
}
|
|
579
582
|
return validatePluginByPath(`/${normalized.join("/")}`)
|
|
580
583
|
}
|
|
581
584
|
if (normalized[0] === "api" && normalized.length > 1) {
|