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.
- package/Dockerfile +2 -0
- package/kernel/api/index.js +1 -42
- package/kernel/api/process/index.js +44 -99
- package/kernel/bin/conda-python.js +30 -0
- package/kernel/bin/conda.js +22 -3
- package/kernel/bin/index.js +11 -1
- package/kernel/environment.js +2 -182
- package/kernel/git.js +13 -0
- package/kernel/index.js +1 -64
- package/kernel/plugin.js +58 -6
- package/kernel/shell.js +2 -21
- package/kernel/util.js +0 -60
- package/package.json +1 -1
- package/server/index.js +149 -176
- package/server/lib/content_validation.js +25 -28
- package/server/public/common.js +29 -103
- package/server/public/create-launcher.js +31 -4
- package/server/public/electron.css +6 -0
- package/server/public/style.css +0 -337
- package/server/public/task-launcher.js +32 -5
- package/server/public/universal-launcher.js +26 -3
- package/server/socket.js +11 -22
- package/server/views/app.ejs +30 -167
- package/server/views/d.ejs +33 -0
- package/server/views/editor.ejs +4 -25
- package/server/views/partials/main_sidebar.ejs +0 -1
- package/server/views/shell.ejs +3 -11
- package/server/views/terminal.ejs +3 -23
- package/server/views/terminals.ejs +0 -1
- package/spec/INSTRUCTION_SYNC.md +5 -5
- package/kernel/api/shell_run_template.js +0 -273
- package/kernel/api/uri/index.js +0 -51
- package/kernel/plugin_sources.js +0 -236
- package/kernel/watch/context.js +0 -42
- package/kernel/watch/drivers/fs.js +0 -71
- package/kernel/watch/drivers/poll.js +0 -33
- package/kernel/watch/index.js +0 -158
- package/server/features/drafts/index.js +0 -41
- package/server/features/drafts/parser.js +0 -169
- package/server/features/drafts/public/drafts.js +0 -1504
- package/server/features/drafts/registry_import.js +0 -412
- package/server/features/drafts/routes.js +0 -68
- package/server/features/drafts/service.js +0 -261
- package/server/features/drafts/watcher.js +0 -76
- package/server/features/index.js +0 -13
- package/server/lib/workspace_catalog.js +0 -151
- package/server/lib/workspace_runtime.js +0 -390
- package/server/routes/workspaces.js +0 -44
- package/server/views/partials/workspace_row.ejs +0 -61
- package/server/views/workspaces.ejs +0 -812
- package/system/plugin/antigravity/antigravity.png +0 -0
- package/system/plugin/antigravity/pinokio.js +0 -37
- package/system/plugin/claude/claude.png +0 -0
- package/system/plugin/claude/pinokio.js +0 -63
- package/system/plugin/claude-auto/claude.png +0 -0
- package/system/plugin/claude-auto/pinokio.js +0 -74
- package/system/plugin/claude-desktop/icon.jpeg +0 -0
- package/system/plugin/claude-desktop/pinokio.js +0 -39
- package/system/plugin/codex/openai.webp +0 -0
- package/system/plugin/codex/pinokio.js +0 -58
- package/system/plugin/codex-auto/openai.webp +0 -0
- package/system/plugin/codex-auto/pinokio.js +0 -65
- package/system/plugin/codex-desktop/icon.png +0 -0
- package/system/plugin/codex-desktop/pinokio.js +0 -39
- package/system/plugin/crush/crush.png +0 -0
- package/system/plugin/crush/pinokio.js +0 -31
- package/system/plugin/cursor/cursor.jpeg +0 -0
- package/system/plugin/cursor/pinokio.js +0 -39
- package/system/plugin/gemini/gemini.jpeg +0 -0
- package/system/plugin/gemini/pinokio.js +0 -40
- package/system/plugin/gemini-auto/gemini.jpeg +0 -0
- package/system/plugin/gemini-auto/pinokio.js +0 -43
- package/system/plugin/qwen/pinokio.js +0 -50
- package/system/plugin/qwen/qwen.png +0 -0
- package/system/plugin/vscode/pinokio.js +0 -36
- package/system/plugin/vscode/vscode.png +0 -0
- package/system/plugin/windsurf/pinokio.js +0 -39
- package/system/plugin/windsurf/windsurf.png +0 -0
|
@@ -1,273 +0,0 @@
|
|
|
1
|
-
function isCmdShellName(shellName) {
|
|
2
|
-
const name = (shellName || '').toLowerCase()
|
|
3
|
-
return name.includes('cmd.exe') || name === 'cmd'
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
function isPowerShellName(shellName) {
|
|
7
|
-
const name = (shellName || '').toLowerCase()
|
|
8
|
-
return name.includes('powershell') || name.includes('pwsh')
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const ENV_ARG_MARKER_RE = /__PINOKIO_ENVARG_(\d+)__/g
|
|
12
|
-
|
|
13
|
-
function envArgMarker(index) {
|
|
14
|
-
return `__PINOKIO_ENVARG_${index}__`
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function isPinokioEnvArgKey(key) {
|
|
18
|
-
return /^PINOKIO_ARG_\d+$/.test(key || "")
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function hasEnvArgMarker(value) {
|
|
22
|
-
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
23
|
-
return ENV_ARG_MARKER_RE.test(String(value))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function quotePosixLiteral(value) {
|
|
27
|
-
const input = value == null ? "" : String(value)
|
|
28
|
-
return `'${input.split("'").join("'\"'\"'")}'`
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function quotePowerShellComposite(value) {
|
|
32
|
-
const input = value == null ? "" : String(value)
|
|
33
|
-
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
34
|
-
let output = '"'
|
|
35
|
-
let lastIndex = 0
|
|
36
|
-
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
37
|
-
const literal = input.slice(lastIndex, match.index)
|
|
38
|
-
output += literal.replace(/[`"$]/g, (char) => "`" + char)
|
|
39
|
-
output += "${env:PINOKIO_ARG_" + match[1] + "}"
|
|
40
|
-
lastIndex = match.index + match[0].length
|
|
41
|
-
}
|
|
42
|
-
output += input.slice(lastIndex).replace(/[`"$]/g, (char) => "`" + char)
|
|
43
|
-
output += '"'
|
|
44
|
-
return output
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function quoteCmdComposite(value) {
|
|
48
|
-
const input = value == null ? "" : String(value)
|
|
49
|
-
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
50
|
-
let output = '"'
|
|
51
|
-
let lastIndex = 0
|
|
52
|
-
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
53
|
-
const literal = input.slice(lastIndex, match.index)
|
|
54
|
-
output += literal.replace(/([()%!^"<>&|])/g, '^$1')
|
|
55
|
-
output += "!PINOKIO_ARG_" + match[1] + "!"
|
|
56
|
-
lastIndex = match.index + match[0].length
|
|
57
|
-
}
|
|
58
|
-
output += input.slice(lastIndex).replace(/([()%!^"<>&|])/g, '^$1')
|
|
59
|
-
output += '"'
|
|
60
|
-
return output
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function quoteEnvArgComposite(value, shellName) {
|
|
64
|
-
const input = value == null ? "" : String(value)
|
|
65
|
-
if (isCmdShellName(shellName)) {
|
|
66
|
-
return quoteCmdComposite(input)
|
|
67
|
-
}
|
|
68
|
-
if (isPowerShellName(shellName)) {
|
|
69
|
-
return quotePowerShellComposite(input)
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
ENV_ARG_MARKER_RE.lastIndex = 0
|
|
73
|
-
const parts = []
|
|
74
|
-
let lastIndex = 0
|
|
75
|
-
for (const match of input.matchAll(ENV_ARG_MARKER_RE)) {
|
|
76
|
-
const literal = input.slice(lastIndex, match.index)
|
|
77
|
-
if (literal) {
|
|
78
|
-
parts.push(quotePosixLiteral(literal))
|
|
79
|
-
}
|
|
80
|
-
parts.push('"$PINOKIO_ARG_' + match[1] + '"')
|
|
81
|
-
lastIndex = match.index + match[0].length
|
|
82
|
-
}
|
|
83
|
-
const tail = input.slice(lastIndex)
|
|
84
|
-
if (tail) {
|
|
85
|
-
parts.push(quotePosixLiteral(tail))
|
|
86
|
-
}
|
|
87
|
-
return parts.length > 0 ? parts.join("") : "''"
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
function shellNameFor(kernel, params) {
|
|
91
|
-
let shellName = kernel && kernel.platform === "win32" ? "cmd.exe" : "bash"
|
|
92
|
-
if (params && typeof params.shell === "string" && params.shell.trim()) {
|
|
93
|
-
shellName = params.shell
|
|
94
|
-
}
|
|
95
|
-
return shellName
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function isPlainObject(value) {
|
|
99
|
-
return value && value.constructor === Object
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function hasMultiline(value) {
|
|
103
|
-
return typeof value === "string" && /[\r\n]/.test(value)
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
function isStructuredArgvMessage(value) {
|
|
107
|
-
return isPlainObject(value) && Array.isArray(value._)
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function hasStructuredArgvMessage(value) {
|
|
111
|
-
if (isStructuredArgvMessage(value)) {
|
|
112
|
-
return true
|
|
113
|
-
}
|
|
114
|
-
if (Array.isArray(value)) {
|
|
115
|
-
return value.some((item) => isStructuredArgvMessage(item))
|
|
116
|
-
}
|
|
117
|
-
return false
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
function protectStructuredString(value, state) {
|
|
121
|
-
if (!hasMultiline(value)) {
|
|
122
|
-
return value
|
|
123
|
-
}
|
|
124
|
-
const name = `PINOKIO_ARG_${state.args.length}`
|
|
125
|
-
state.args.push({
|
|
126
|
-
name,
|
|
127
|
-
value: value == null ? "" : String(value)
|
|
128
|
-
})
|
|
129
|
-
return envArgMarker(state.args.length - 1)
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
function protectStructuredValue(value, state) {
|
|
133
|
-
if (typeof value === "string") {
|
|
134
|
-
return protectStructuredString(value, state)
|
|
135
|
-
}
|
|
136
|
-
if (Array.isArray(value)) {
|
|
137
|
-
return value.map((item) => protectStructuredValue(item, state))
|
|
138
|
-
}
|
|
139
|
-
if (isPlainObject(value)) {
|
|
140
|
-
const rendered = {}
|
|
141
|
-
for (const [key, item] of Object.entries(value)) {
|
|
142
|
-
rendered[key] = protectStructuredValue(item, state)
|
|
143
|
-
}
|
|
144
|
-
return rendered
|
|
145
|
-
}
|
|
146
|
-
return value
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
function protectStructuredMessage(value, state) {
|
|
150
|
-
if (isStructuredArgvMessage(value)) {
|
|
151
|
-
return protectStructuredValue(value, state)
|
|
152
|
-
}
|
|
153
|
-
if (Array.isArray(value)) {
|
|
154
|
-
return value.map((item) => isStructuredArgvMessage(item) ? protectStructuredValue(item, state) : item)
|
|
155
|
-
}
|
|
156
|
-
return value
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function renderEnvArgs(kernel, rpc, memory) {
|
|
160
|
-
if (!rpc || rpc.method !== "shell.run" || !rpc.params || !hasStructuredArgvMessage(rpc.params.message)) {
|
|
161
|
-
return rpc
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const shellName = shellNameFor(kernel, rpc.params)
|
|
165
|
-
const state = { args: [] }
|
|
166
|
-
const message = protectStructuredMessage(rpc.params.message, state)
|
|
167
|
-
|
|
168
|
-
if (state.args.length === 0) {
|
|
169
|
-
return rpc
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
const env = Object.assign({}, rpc.params.env || {})
|
|
173
|
-
for (const arg of state.args) {
|
|
174
|
-
env[arg.name] = arg.value
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
return {
|
|
178
|
-
...rpc,
|
|
179
|
-
params: {
|
|
180
|
-
...rpc.params,
|
|
181
|
-
message,
|
|
182
|
-
env,
|
|
183
|
-
_pinokio_env_args: state.args,
|
|
184
|
-
_pinokio_cmd_delayed_expansion: isCmdShellName(shellName)
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function envArgDetails(value) {
|
|
190
|
-
const normalized = String(value == null ? "" : value)
|
|
191
|
-
.replace(/\r\n/g, "\n")
|
|
192
|
-
.replace(/\r/g, "\n")
|
|
193
|
-
.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, "?")
|
|
194
|
-
const lines = normalized.split("\n")
|
|
195
|
-
const previewLines = []
|
|
196
|
-
const maxLines = 8
|
|
197
|
-
const maxChars = 800
|
|
198
|
-
let used = 0
|
|
199
|
-
|
|
200
|
-
for (const line of lines.slice(0, maxLines)) {
|
|
201
|
-
const remaining = maxChars - used
|
|
202
|
-
if (remaining <= 0) {
|
|
203
|
-
break
|
|
204
|
-
}
|
|
205
|
-
if (line.length > remaining) {
|
|
206
|
-
previewLines.push(line.slice(0, remaining) + "...")
|
|
207
|
-
used = maxChars
|
|
208
|
-
break
|
|
209
|
-
}
|
|
210
|
-
previewLines.push(line)
|
|
211
|
-
used += line.length + 1
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const preview = previewLines.join("\n")
|
|
215
|
-
const truncated = lines.length > previewLines.length || normalized.length > preview.length
|
|
216
|
-
if (truncated && previewLines[previewLines.length - 1] !== "...") {
|
|
217
|
-
previewLines.push("...")
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
return {
|
|
221
|
-
lineCount: normalized.length === 0 ? 0 : lines.length,
|
|
222
|
-
previewLines,
|
|
223
|
-
truncated
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
function formatEnvArgsPreview(args) {
|
|
228
|
-
if (!Array.isArray(args) || args.length === 0) {
|
|
229
|
-
return ""
|
|
230
|
-
}
|
|
231
|
-
const lines = ["\r\nPinokio shell args", ""]
|
|
232
|
-
for (const arg of args) {
|
|
233
|
-
const details = envArgDetails(arg.value)
|
|
234
|
-
lines.push(`${arg.name} ${details.lineCount} lines`)
|
|
235
|
-
for (const previewLine of details.previewLines) {
|
|
236
|
-
lines.push(` ${previewLine}`)
|
|
237
|
-
}
|
|
238
|
-
lines.push("")
|
|
239
|
-
}
|
|
240
|
-
return lines.join("\r\n") + "\r\n"
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
function envArgSummary(value) {
|
|
244
|
-
const details = envArgDetails(value)
|
|
245
|
-
return {
|
|
246
|
-
type: "pinokio env arg",
|
|
247
|
-
lines: details.lineCount,
|
|
248
|
-
preview: details.previewLines.join("\n"),
|
|
249
|
-
truncated: details.truncated
|
|
250
|
-
}
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
function redactEnvArgs(env) {
|
|
254
|
-
if (!env || typeof env !== "object") {
|
|
255
|
-
return env
|
|
256
|
-
}
|
|
257
|
-
const redacted = { ...env }
|
|
258
|
-
for (const key of Object.keys(redacted)) {
|
|
259
|
-
if (isPinokioEnvArgKey(key)) {
|
|
260
|
-
redacted[key] = envArgSummary(redacted[key])
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
return redacted
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
module.exports = {
|
|
267
|
-
renderEnvArgs,
|
|
268
|
-
quoteEnvArgComposite,
|
|
269
|
-
hasEnvArgMarker,
|
|
270
|
-
isPinokioEnvArgKey,
|
|
271
|
-
formatEnvArgsPreview,
|
|
272
|
-
redactEnvArgs
|
|
273
|
-
}
|
package/kernel/api/uri/index.js
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
const Util = require('../../util')
|
|
2
|
-
|
|
3
|
-
const appendQueryParams = (uri, params) => {
|
|
4
|
-
if (!params || typeof params !== 'object' || Array.isArray(params)) {
|
|
5
|
-
return uri
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const entries = []
|
|
9
|
-
for (const [key, value] of Object.entries(params)) {
|
|
10
|
-
const values = Array.isArray(value) ? value : [value]
|
|
11
|
-
for (const item of values) {
|
|
12
|
-
if (item === undefined || item === null) {
|
|
13
|
-
continue
|
|
14
|
-
}
|
|
15
|
-
const serialized = typeof item === 'object' ? JSON.stringify(item) : String(item)
|
|
16
|
-
entries.push(`${encodeURIComponent(key)}=${encodeURIComponent(serialized)}`)
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (entries.length === 0) {
|
|
21
|
-
return uri
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
const hashIndex = uri.indexOf('#')
|
|
25
|
-
const base = hashIndex === -1 ? uri : uri.slice(0, hashIndex)
|
|
26
|
-
const hash = hashIndex === -1 ? '' : uri.slice(hashIndex)
|
|
27
|
-
const separator = base.includes('?')
|
|
28
|
-
? (base.endsWith('?') || base.endsWith('&') ? '' : '&')
|
|
29
|
-
: '?'
|
|
30
|
-
|
|
31
|
-
return `${base}${separator}${entries.join('&')}${hash}`
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
class URI {
|
|
35
|
-
build(params = {}) {
|
|
36
|
-
const uri = typeof params.uri === 'string' ? params.uri.trim() : ''
|
|
37
|
-
if (!uri) {
|
|
38
|
-
throw new Error('uri.open requires params.uri')
|
|
39
|
-
}
|
|
40
|
-
return appendQueryParams(uri, params.params)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async open(req, ondata, kernel) {
|
|
44
|
-
const uri = this.build(req.params)
|
|
45
|
-
ondata({ raw: `\r\nopening uri: ${uri}\r\n` })
|
|
46
|
-
const result = await Util.openURI(uri)
|
|
47
|
-
return { uri, result }
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
module.exports = URI
|
package/kernel/plugin_sources.js
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
const fs = require("fs")
|
|
2
|
-
const path = require("path")
|
|
3
|
-
const { glob } = require("glob")
|
|
4
|
-
|
|
5
|
-
const LOCAL_RUN_PREFIX = "/run"
|
|
6
|
-
const LOCAL_ASSET_PREFIX = "/asset"
|
|
7
|
-
const SYSTEM_RUN_PREFIX = "/pinokio/run"
|
|
8
|
-
const SYSTEM_ASSET_PREFIX = "/pinokio/asset"
|
|
9
|
-
const LOCAL_PLUGIN_RUN_PREFIX = `${LOCAL_RUN_PREFIX}/plugin`
|
|
10
|
-
const LOCAL_PLUGIN_ASSET_PREFIX = `${LOCAL_ASSET_PREFIX}/plugin`
|
|
11
|
-
const SYSTEM_PLUGIN_RUN_PREFIX = `${SYSTEM_RUN_PREFIX}/plugin`
|
|
12
|
-
const SYSTEM_PLUGIN_ASSET_PREFIX = `${SYSTEM_ASSET_PREFIX}/plugin`
|
|
13
|
-
|
|
14
|
-
const toPathname = (value) => {
|
|
15
|
-
const raw = typeof value === "string" ? value.trim() : ""
|
|
16
|
-
if (!raw) return ""
|
|
17
|
-
try {
|
|
18
|
-
if (/^https?:\/\//i.test(raw)) return new URL(raw).pathname
|
|
19
|
-
if (raw.startsWith("/")) return new URL(`http://localhost${raw}`).pathname
|
|
20
|
-
} catch (_) {
|
|
21
|
-
}
|
|
22
|
-
return raw.split("?")[0].split("#")[0]
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const normalizeSlashes = (value) => String(value || "").replace(/\\/g, "/").replace(/\/{2,}/g, "/")
|
|
26
|
-
|
|
27
|
-
const systemRoot = (kernel) => {
|
|
28
|
-
if (kernel && typeof kernel.systemPath === "function") {
|
|
29
|
-
return kernel.systemPath()
|
|
30
|
-
}
|
|
31
|
-
return path.resolve(__dirname, "..", "system")
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const systemPluginRoot = (kernel) => {
|
|
35
|
-
if (kernel && typeof kernel.systemPath === "function") {
|
|
36
|
-
return kernel.systemPath("plugin")
|
|
37
|
-
}
|
|
38
|
-
return path.resolve(__dirname, "..", "system", "plugin")
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const isSystemRunPath = (value) => toPathname(value).startsWith(`${SYSTEM_RUN_PREFIX}/`)
|
|
42
|
-
const isLocalRunPath = (value) => toPathname(value).startsWith(`${LOCAL_RUN_PREFIX}/`)
|
|
43
|
-
const isRunPath = (value) => isLocalRunPath(value) || isSystemRunPath(value)
|
|
44
|
-
const isSystemPluginPath = (value) => normalizeSlashes(value).startsWith(`${SYSTEM_PLUGIN_RUN_PREFIX}/`)
|
|
45
|
-
const isLegacyPluginCodePath = (value) => {
|
|
46
|
-
const normalized = normalizeSlashes(value).replace(/^\/+/, "")
|
|
47
|
-
return normalized.startsWith("plugin/code/") || normalized.startsWith("code/")
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const normalizePluginPath = (value) => {
|
|
51
|
-
let normalized = toPathname(value)
|
|
52
|
-
if (!normalized) return ""
|
|
53
|
-
normalized = normalizeSlashes(normalized)
|
|
54
|
-
if (!normalized.startsWith(`${SYSTEM_RUN_PREFIX}/`)) {
|
|
55
|
-
normalized = normalized.replace(/^\/run(?=\/)/, "")
|
|
56
|
-
}
|
|
57
|
-
if (!normalized.startsWith("/")) {
|
|
58
|
-
normalized = `/${normalized}`
|
|
59
|
-
}
|
|
60
|
-
normalized = normalized.replace(/\/+$/, "")
|
|
61
|
-
if (!normalized.endsWith("/pinokio.js")) {
|
|
62
|
-
normalized = `${normalized}/pinokio.js`
|
|
63
|
-
}
|
|
64
|
-
return normalized
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const pluginSelectionMatches = (src, selectedValue) => {
|
|
68
|
-
const selectedPlugin = normalizeSlashes(typeof selectedValue === "string" ? selectedValue.trim() : "")
|
|
69
|
-
if (!selectedPlugin || !src) return false
|
|
70
|
-
const selectedRunPath = selectedPlugin.startsWith(`${SYSTEM_RUN_PREFIX}/`)
|
|
71
|
-
? selectedPlugin
|
|
72
|
-
: `${LOCAL_RUN_PREFIX}${selectedPlugin}`
|
|
73
|
-
return src === selectedPlugin || src.startsWith(selectedRunPath)
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const resolveRunPath = (kernel, value) => {
|
|
77
|
-
const pathname = toPathname(value)
|
|
78
|
-
if (isSystemRunPath(pathname)) {
|
|
79
|
-
const parts = pathname.split("/").filter(Boolean).slice(2)
|
|
80
|
-
return kernel.systemPath(...parts)
|
|
81
|
-
}
|
|
82
|
-
if (isLocalRunPath(pathname)) {
|
|
83
|
-
const parts = pathname.split("/").filter(Boolean).slice(1)
|
|
84
|
-
return kernel.path(...parts)
|
|
85
|
-
}
|
|
86
|
-
return ""
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
const resolvePinokioPath = (kernel, value) => {
|
|
90
|
-
const runPath = resolveRunPath(kernel, value)
|
|
91
|
-
if (runPath) return runPath
|
|
92
|
-
const pathname = toPathname(value)
|
|
93
|
-
return pathname ? kernel.path(pathname.replace(/^\/+/, "")) : ""
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const systemRelativeFromPluginPath = (normalizedPath) => {
|
|
97
|
-
return normalizeSlashes(normalizedPath).replace(/^\/pinokio\/run\/+/, "")
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const pluginPathToAbsolute = (kernel, normalizedPath) => {
|
|
101
|
-
if (isSystemPluginPath(normalizedPath)) {
|
|
102
|
-
return kernel.systemPath(systemRelativeFromPluginPath(normalizedPath))
|
|
103
|
-
}
|
|
104
|
-
return kernel.path(normalizeSlashes(normalizedPath).replace(/^\/+/, ""))
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
const pluginRunHrefForPath = (normalizedPath) => {
|
|
108
|
-
if (typeof normalizedPath !== "string" || !normalizedPath) return ""
|
|
109
|
-
if (isSystemPluginPath(normalizedPath)) return normalizedPath
|
|
110
|
-
return `${LOCAL_RUN_PREFIX}${normalizedPath}`
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const pluginAssetHrefForIcon = (normalizedPath, icon) => {
|
|
114
|
-
const trimmedIcon = typeof icon === "string" ? icon.trim() : ""
|
|
115
|
-
if (!trimmedIcon) return ""
|
|
116
|
-
if (isSystemPluginPath(normalizedPath)) {
|
|
117
|
-
const relativeDir = path.posix.dirname(systemRelativeFromPluginPath(normalizedPath))
|
|
118
|
-
return `${SYSTEM_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
|
|
119
|
-
}
|
|
120
|
-
if (normalizeSlashes(normalizedPath).startsWith("/plugin/")) {
|
|
121
|
-
const relativeDir = path.posix.dirname(normalizeSlashes(normalizedPath).slice(1))
|
|
122
|
-
return `${LOCAL_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
|
|
123
|
-
}
|
|
124
|
-
return ""
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const normalizeLauncherTool = (toolValue) => {
|
|
128
|
-
let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
|
|
129
|
-
normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
|
|
130
|
-
normalizedTool = normalizedTool.replace(/^\/+|\/+$/g, "")
|
|
131
|
-
if (!normalizedTool || normalizedTool.includes("..") || !/^[A-Za-z0-9._/-]+$/.test(normalizedTool)) {
|
|
132
|
-
const error = new Error("Invalid plugin.")
|
|
133
|
-
error.status = 400
|
|
134
|
-
throw error
|
|
135
|
-
}
|
|
136
|
-
if (!normalizedTool.startsWith("pinokio/run/")) {
|
|
137
|
-
normalizedTool = normalizedTool.replace(/^run\//, "")
|
|
138
|
-
}
|
|
139
|
-
if (isLegacyPluginCodePath(normalizedTool)) {
|
|
140
|
-
const error = new Error("The managed plugin/code path is no longer used.")
|
|
141
|
-
error.status = 400
|
|
142
|
-
throw error
|
|
143
|
-
}
|
|
144
|
-
return normalizedTool
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
const resolveLauncherPluginHref = (toolValue) => {
|
|
148
|
-
const normalizedTool = normalizeLauncherTool(toolValue)
|
|
149
|
-
if (normalizedTool.startsWith("pinokio/run/")) {
|
|
150
|
-
const scriptPath = normalizedTool.endsWith(".js")
|
|
151
|
-
? normalizedTool
|
|
152
|
-
: `${normalizedTool}/pinokio.js`
|
|
153
|
-
return `/${scriptPath}`
|
|
154
|
-
}
|
|
155
|
-
if (normalizedTool.startsWith("plugin/") || normalizedTool.startsWith("api/")) {
|
|
156
|
-
const scriptPath = normalizedTool.endsWith(".js")
|
|
157
|
-
? normalizedTool
|
|
158
|
-
: `${normalizedTool}/pinokio.js`
|
|
159
|
-
return `${LOCAL_RUN_PREFIX}/${scriptPath}`
|
|
160
|
-
}
|
|
161
|
-
return `${LOCAL_PLUGIN_RUN_PREFIX}/${normalizedTool}/pinokio.js`
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
const loadPluginsFromRoot = async ({ kernel, root, runPrefix, assetPrefix, source, ignore = [] }) => {
|
|
165
|
-
const exists = await fs.promises.stat(root).then((stat) => stat.isDirectory()).catch(() => false)
|
|
166
|
-
if (!exists) return []
|
|
167
|
-
|
|
168
|
-
const pluginPaths = await glob("**/pinokio.js", { cwd: root, ignore })
|
|
169
|
-
const plugins = []
|
|
170
|
-
for (const pluginPath of pluginPaths) {
|
|
171
|
-
const normalizedPluginPath = normalizeSlashes(pluginPath)
|
|
172
|
-
const config = await kernel.require(path.resolve(root, pluginPath))
|
|
173
|
-
if (!config || !Array.isArray(config.run)) continue
|
|
174
|
-
const invalid = Object.keys(config).some((key) => typeof config[key] === "function")
|
|
175
|
-
if (invalid) continue
|
|
176
|
-
|
|
177
|
-
const cwd = normalizedPluginPath.split("/").slice(0, -1).join("/")
|
|
178
|
-
const href = `${runPrefix}/${normalizedPluginPath}`
|
|
179
|
-
const image = config.icon ? `${assetPrefix}/${cwd}/${config.icon}` : config.image
|
|
180
|
-
plugins.push({
|
|
181
|
-
...config,
|
|
182
|
-
href,
|
|
183
|
-
src: href,
|
|
184
|
-
image,
|
|
185
|
-
source,
|
|
186
|
-
system: source === "system",
|
|
187
|
-
})
|
|
188
|
-
}
|
|
189
|
-
return plugins
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
const loadPluginMenu = async (kernel) => {
|
|
193
|
-
const systemPlugins = await loadPluginsFromRoot({
|
|
194
|
-
kernel,
|
|
195
|
-
root: systemPluginRoot(kernel),
|
|
196
|
-
runPrefix: SYSTEM_PLUGIN_RUN_PREFIX,
|
|
197
|
-
assetPrefix: SYSTEM_PLUGIN_ASSET_PREFIX,
|
|
198
|
-
source: "system",
|
|
199
|
-
})
|
|
200
|
-
const localPlugins = await loadPluginsFromRoot({
|
|
201
|
-
kernel,
|
|
202
|
-
root: path.resolve(kernel.homedir, "plugin"),
|
|
203
|
-
runPrefix: LOCAL_PLUGIN_RUN_PREFIX,
|
|
204
|
-
assetPrefix: LOCAL_PLUGIN_ASSET_PREFIX,
|
|
205
|
-
source: "local",
|
|
206
|
-
ignore: ["code/**"],
|
|
207
|
-
})
|
|
208
|
-
return systemPlugins.concat(localPlugins)
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
module.exports = {
|
|
212
|
-
LOCAL_RUN_PREFIX,
|
|
213
|
-
LOCAL_ASSET_PREFIX,
|
|
214
|
-
SYSTEM_RUN_PREFIX,
|
|
215
|
-
SYSTEM_ASSET_PREFIX,
|
|
216
|
-
LOCAL_PLUGIN_RUN_PREFIX,
|
|
217
|
-
SYSTEM_PLUGIN_RUN_PREFIX,
|
|
218
|
-
SYSTEM_PLUGIN_ASSET_PREFIX,
|
|
219
|
-
systemRoot,
|
|
220
|
-
systemPluginRoot,
|
|
221
|
-
toPathname,
|
|
222
|
-
isSystemRunPath,
|
|
223
|
-
isLocalRunPath,
|
|
224
|
-
isRunPath,
|
|
225
|
-
isSystemPluginPath,
|
|
226
|
-
isLegacyPluginCodePath,
|
|
227
|
-
normalizePluginPath,
|
|
228
|
-
pluginSelectionMatches,
|
|
229
|
-
resolveRunPath,
|
|
230
|
-
resolvePinokioPath,
|
|
231
|
-
pluginPathToAbsolute,
|
|
232
|
-
pluginRunHrefForPath,
|
|
233
|
-
pluginAssetHrefForIcon,
|
|
234
|
-
resolveLauncherPluginHref,
|
|
235
|
-
loadPluginMenu,
|
|
236
|
-
}
|
package/kernel/watch/context.js
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
const path = require("path")
|
|
2
|
-
const { watchFs } = require("./drivers/fs")
|
|
3
|
-
const { poll } = require("./drivers/poll")
|
|
4
|
-
|
|
5
|
-
class WatchContext {
|
|
6
|
-
constructor(options = {}) {
|
|
7
|
-
this.kernel = options.kernel
|
|
8
|
-
this.manager = options.manager
|
|
9
|
-
this.id = options.id
|
|
10
|
-
this.cwd = path.resolve(options.cwd)
|
|
11
|
-
this.dirname = path.resolve(options.dirname || options.cwd)
|
|
12
|
-
this.request = options.request
|
|
13
|
-
this.script = options.script
|
|
14
|
-
this.declaration = options.declaration
|
|
15
|
-
this.input = options.input || {}
|
|
16
|
-
this.args = options.args || this.input
|
|
17
|
-
this.watch = {
|
|
18
|
-
fs: (targetPath, callback, watchOptions = {}) => {
|
|
19
|
-
return watchFs(this.resolve(targetPath), callback, {
|
|
20
|
-
...watchOptions,
|
|
21
|
-
onError: watchOptions.onError || ((error) => {
|
|
22
|
-
console.warn("[watch.fs]", error && error.message ? error.message : error)
|
|
23
|
-
})
|
|
24
|
-
})
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
resolve(targetPath) {
|
|
30
|
-
return this.kernel.api.resolvePath(this.cwd, String(targetPath || "."))
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
resolveModule(targetPath) {
|
|
34
|
-
return this.kernel.api.resolvePath(this.dirname, String(targetPath || "."))
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
poll(interval, callback, options = {}) {
|
|
38
|
-
return poll(interval, callback, options)
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
module.exports = WatchContext
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
const fs = require("fs")
|
|
2
|
-
const path = require("path")
|
|
3
|
-
const ParcelWatcher = require("@parcel/watcher")
|
|
4
|
-
|
|
5
|
-
const DEFAULT_IGNORE = [
|
|
6
|
-
"**/.git/**",
|
|
7
|
-
"**/node_modules/**",
|
|
8
|
-
"**/__pycache__/**",
|
|
9
|
-
"**/.venv/**",
|
|
10
|
-
"**/venv/**",
|
|
11
|
-
"**/env/**"
|
|
12
|
-
]
|
|
13
|
-
|
|
14
|
-
function isInside(candidate, parent) {
|
|
15
|
-
const relative = path.relative(parent, candidate)
|
|
16
|
-
return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative))
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async function nearestExistingDirectory(targetPath) {
|
|
20
|
-
let current = path.resolve(targetPath)
|
|
21
|
-
while (current && current !== path.dirname(current)) {
|
|
22
|
-
const stats = await fs.promises.stat(current).catch(() => null)
|
|
23
|
-
if (stats && stats.isDirectory()) {
|
|
24
|
-
return current
|
|
25
|
-
}
|
|
26
|
-
current = path.dirname(current)
|
|
27
|
-
}
|
|
28
|
-
return current || path.parse(path.resolve(targetPath)).root
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function watchFs(targetPath, callback, options = {}) {
|
|
32
|
-
const resolvedTarget = path.resolve(targetPath)
|
|
33
|
-
const targetStats = await fs.promises.stat(resolvedTarget).catch(() => null)
|
|
34
|
-
const watchRoot = targetStats && targetStats.isDirectory()
|
|
35
|
-
? resolvedTarget
|
|
36
|
-
: await nearestExistingDirectory(resolvedTarget)
|
|
37
|
-
const filterToTarget = watchRoot !== resolvedTarget
|
|
38
|
-
|
|
39
|
-
const subscription = await ParcelWatcher.subscribe(
|
|
40
|
-
watchRoot,
|
|
41
|
-
(error, events) => {
|
|
42
|
-
if (error) {
|
|
43
|
-
if (typeof options.onError === "function") {
|
|
44
|
-
options.onError(error)
|
|
45
|
-
}
|
|
46
|
-
return
|
|
47
|
-
}
|
|
48
|
-
const normalizedEvents = Array.isArray(events) ? events : []
|
|
49
|
-
const filteredEvents = filterToTarget
|
|
50
|
-
? normalizedEvents.filter((event) => event && event.path && isInside(path.resolve(event.path), resolvedTarget))
|
|
51
|
-
: normalizedEvents
|
|
52
|
-
if (filteredEvents.length === 0) {
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
callback(filteredEvents)
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
ignore: Array.isArray(options.ignore) ? options.ignore : DEFAULT_IGNORE
|
|
59
|
-
}
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
return async () => {
|
|
63
|
-
if (subscription && typeof subscription.unsubscribe === "function") {
|
|
64
|
-
await subscription.unsubscribe().catch(() => {})
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
module.exports = {
|
|
70
|
-
watchFs
|
|
71
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
function poll(interval, callback, options = {}) {
|
|
2
|
-
const delay = Math.max(100, Number(interval || options.interval || 1000))
|
|
3
|
-
let stopped = false
|
|
4
|
-
let running = false
|
|
5
|
-
|
|
6
|
-
const tick = async () => {
|
|
7
|
-
if (stopped || running) return
|
|
8
|
-
running = true
|
|
9
|
-
try {
|
|
10
|
-
await callback()
|
|
11
|
-
} catch (error) {
|
|
12
|
-
if (typeof options.onError === "function") {
|
|
13
|
-
options.onError(error)
|
|
14
|
-
}
|
|
15
|
-
} finally {
|
|
16
|
-
running = false
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (options.immediate !== false) {
|
|
21
|
-
setTimeout(tick, 0)
|
|
22
|
-
}
|
|
23
|
-
const timer = setInterval(tick, delay)
|
|
24
|
-
|
|
25
|
-
return async () => {
|
|
26
|
-
stopped = true
|
|
27
|
-
clearInterval(timer)
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
module.exports = {
|
|
32
|
-
poll
|
|
33
|
-
}
|