pinokiod 3.86.0 → 3.87.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 +61 -0
- package/docker-entrypoint.sh +75 -0
- package/kernel/api/hf/index.js +1 -1
- package/kernel/api/index.js +1 -1
- package/kernel/api/shell/index.js +6 -0
- package/kernel/api/terminal/index.js +166 -0
- package/kernel/bin/conda.js +3 -2
- package/kernel/bin/index.js +53 -2
- package/kernel/bin/setup.js +32 -0
- package/kernel/bin/vs.js +11 -2
- package/kernel/index.js +42 -2
- package/kernel/info.js +36 -0
- package/kernel/peer.js +42 -15
- package/kernel/router/index.js +23 -15
- package/kernel/router/localhost_static_router.js +0 -3
- package/kernel/router/pinokio_domain_router.js +333 -0
- package/kernel/shells.js +21 -1
- package/kernel/util.js +2 -2
- package/package.json +2 -1
- package/script/install-mode.js +33 -0
- package/script/pinokio.json +7 -0
- package/server/index.js +513 -173
- package/server/public/Socket.js +48 -0
- package/server/public/common.js +1441 -276
- package/server/public/fseditor.js +71 -12
- package/server/public/install.js +1 -1
- package/server/public/layout.js +740 -0
- package/server/public/modalinput.js +0 -1
- package/server/public/style.css +97 -105
- package/server/public/tab-idle-notifier.js +629 -0
- package/server/public/terminal_input_tracker.js +63 -0
- package/server/public/urldropdown.css +319 -53
- package/server/public/urldropdown.js +615 -159
- package/server/public/window_storage.js +97 -28
- package/server/socket.js +40 -9
- package/server/views/500.ejs +2 -2
- package/server/views/app.ejs +3136 -1367
- package/server/views/bookmarklet.ejs +1 -1
- package/server/views/bootstrap.ejs +1 -1
- package/server/views/columns.ejs +2 -13
- package/server/views/connect.ejs +3 -4
- package/server/views/container.ejs +1 -2
- package/server/views/d.ejs +223 -53
- package/server/views/editor.ejs +1 -1
- package/server/views/file_explorer.ejs +1 -1
- package/server/views/index.ejs +12 -11
- package/server/views/index2.ejs +4 -4
- package/server/views/init/index.ejs +4 -5
- package/server/views/install.ejs +1 -1
- package/server/views/layout.ejs +105 -0
- package/server/views/net.ejs +39 -7
- package/server/views/network.ejs +20 -6
- package/server/views/network2.ejs +1 -1
- package/server/views/old_network.ejs +2 -2
- package/server/views/partials/dynamic.ejs +3 -5
- package/server/views/partials/menu.ejs +3 -5
- package/server/views/partials/running.ejs +1 -1
- package/server/views/pro.ejs +1 -1
- package/server/views/prototype/index.ejs +1 -1
- package/server/views/review.ejs +11 -23
- package/server/views/rows.ejs +2 -13
- package/server/views/screenshots.ejs +293 -138
- package/server/views/settings.ejs +3 -4
- package/server/views/setup.ejs +1 -2
- package/server/views/shell.ejs +277 -26
- package/server/views/terminal.ejs +322 -49
- package/server/views/tools.ejs +448 -4
package/Dockerfile
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Production image for pinokiod with required system tooling
|
|
2
|
+
FROM node:20 AS build
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
RUN apt-get update \
|
|
6
|
+
&& apt-get install -y --no-install-recommends python3 make g++ \
|
|
7
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
8
|
+
|
|
9
|
+
COPY package*.json ./
|
|
10
|
+
RUN npm ci --omit=dev && npm cache clean --force
|
|
11
|
+
|
|
12
|
+
FROM node:20 AS runtime
|
|
13
|
+
ENV NODE_ENV=production
|
|
14
|
+
WORKDIR /app
|
|
15
|
+
|
|
16
|
+
RUN apt-get update \
|
|
17
|
+
&& apt-get install -y --no-install-recommends \
|
|
18
|
+
bluez \
|
|
19
|
+
curl \
|
|
20
|
+
git \
|
|
21
|
+
lsof \
|
|
22
|
+
net-tools \
|
|
23
|
+
openssh-client \
|
|
24
|
+
p7zip-full \
|
|
25
|
+
pv \
|
|
26
|
+
&& rm -rf /var/lib/apt/lists/*
|
|
27
|
+
|
|
28
|
+
COPY --from=build /app/node_modules ./node_modules
|
|
29
|
+
COPY . .
|
|
30
|
+
|
|
31
|
+
# Pre-seed the Pinokio dev preset into the image as a compressed archive
|
|
32
|
+
RUN mkdir -p /app/.pinokio-seed \
|
|
33
|
+
&& PINOKIO_HOME=/app/.pinokio-seed PINOKIO_SETUP_MODE=prod_dev node script/install-mode.js \
|
|
34
|
+
&& rm -rf /app/.pinokio-seed/network \
|
|
35
|
+
&& mkdir -p /app/.pinokio-seed/network \
|
|
36
|
+
&& git clone --depth 1 https://github.com/pinokiocomputer/network /app/.pinokio-seed/network/system \
|
|
37
|
+
&& rm -rf /app/.pinokio-seed/network/system/.git \
|
|
38
|
+
&& rm -rf /app/.pinokio-seed/plugin \
|
|
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 \
|
|
42
|
+
&& rm -rf /app/.pinokio-seed/prototype/system \
|
|
43
|
+
&& mkdir -p /app/.pinokio-seed/prototype \
|
|
44
|
+
&& git clone --depth 1 https://github.com/pinokiocomputer/proto /app/.pinokio-seed/prototype/system \
|
|
45
|
+
&& rm -rf /app/.pinokio-seed/prototype/system/.git \
|
|
46
|
+
&& curl -fsSL https://raw.githubusercontent.com/pinokiocomputer/home/refs/heads/main/docs/README.md -o /app/.pinokio-seed/prototype/PINOKIO.md \
|
|
47
|
+
&& curl -fsSL https://raw.githubusercontent.com/pinokiocomputer/pterm/refs/heads/main/README.md -o /app/.pinokio-seed/prototype/PTERM.md \
|
|
48
|
+
&& tar -C /app/.pinokio-seed -czf /app/.pinokio-seed.tgz . \
|
|
49
|
+
&& rm -rf /app/.pinokio-seed
|
|
50
|
+
|
|
51
|
+
COPY docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
|
|
52
|
+
RUN chmod +x /usr/local/bin/docker-entrypoint.sh
|
|
53
|
+
|
|
54
|
+
ENV PINOKIO_HOME=/data/pinokio \
|
|
55
|
+
PINOKIO_HTTPS_ACTIVE=1 \
|
|
56
|
+
PINOKIO_NETWORK_ACTIVE=1
|
|
57
|
+
VOLUME ["/data/pinokio"]
|
|
58
|
+
|
|
59
|
+
EXPOSE 8080
|
|
60
|
+
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
|
|
61
|
+
CMD ["npm", "start"]
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
if [ -z "${PINOKIO_STDOUT_STREAMED:-}" ] && command -v stdbuf >/dev/null 2>&1; then
|
|
5
|
+
export PINOKIO_STDOUT_STREAMED=1
|
|
6
|
+
exec 1> >(stdbuf -oL cat)
|
|
7
|
+
exec 2> >(stdbuf -oL cat >&2)
|
|
8
|
+
fi
|
|
9
|
+
|
|
10
|
+
PINOKIO_HOME="${PINOKIO_HOME:-/data/pinokio}"
|
|
11
|
+
export PINOKIO_HOME
|
|
12
|
+
|
|
13
|
+
if [ ! -d "$PINOKIO_HOME/bin" ]; then
|
|
14
|
+
mkdir -p "$PINOKIO_HOME"
|
|
15
|
+
if [ -f "/app/.pinokio-seed.tgz" ]; then
|
|
16
|
+
echo "[entrypoint] Bootstrapping Pinokio home at $PINOKIO_HOME"
|
|
17
|
+
echo "[entrypoint] Extracting Pinokio seed archive (this may take a moment)"
|
|
18
|
+
if command -v pv >/dev/null 2>&1; then
|
|
19
|
+
step=${PINOKIO_PROGRESS_STEP:-5}
|
|
20
|
+
interval=${PINOKIO_PROGRESS_INTERVAL:-1}
|
|
21
|
+
progress_fifo_raw=$(mktemp -t pinokio-progress-raw.XXXXXX)
|
|
22
|
+
progress_fifo=$(mktemp -t pinokio-progress.XXXXXX)
|
|
23
|
+
rm -f "$progress_fifo_raw" "$progress_fifo"
|
|
24
|
+
mkfifo "$progress_fifo_raw" "$progress_fifo"
|
|
25
|
+
(
|
|
26
|
+
trap 'rm -f "$progress_fifo_raw" "$progress_fifo"; exit 0' EXIT INT TERM
|
|
27
|
+
tr '\r' '\n' <"$progress_fifo_raw" >"$progress_fifo"
|
|
28
|
+
) &
|
|
29
|
+
progress_transform_pid=$!
|
|
30
|
+
(
|
|
31
|
+
last=-1
|
|
32
|
+
while IFS= read -r line; do
|
|
33
|
+
progress=$(printf '%s\n' "$line" | grep -oE '[0-9]+%' | tail -n 1 || true)
|
|
34
|
+
if [ -n "$progress" ]; then
|
|
35
|
+
pct=${progress%%%}
|
|
36
|
+
if [ "$pct" -gt "$last" ]; then
|
|
37
|
+
delta=$((pct - last))
|
|
38
|
+
if [ "$pct" -eq 100 ] || [ "$delta" -ge "$step" ] || [ "$last" -lt 0 ]; then
|
|
39
|
+
last=$pct
|
|
40
|
+
printf '[entrypoint] Extracting: %d%%\n' "$pct"
|
|
41
|
+
fi
|
|
42
|
+
fi
|
|
43
|
+
fi
|
|
44
|
+
done <"$progress_fifo"
|
|
45
|
+
if [ "$last" -lt 100 ]; then
|
|
46
|
+
printf '[entrypoint] Extracting: 100%%\n'
|
|
47
|
+
fi
|
|
48
|
+
) &
|
|
49
|
+
progress_reader_pid=$!
|
|
50
|
+
pv -f -i "$interval" /app/.pinokio-seed.tgz 2>"$progress_fifo_raw" | tar -C "$PINOKIO_HOME" -xz -f -
|
|
51
|
+
status=$?
|
|
52
|
+
wait "$progress_reader_pid" || true
|
|
53
|
+
wait "$progress_transform_pid" || true
|
|
54
|
+
rm -f "$progress_fifo_raw" "$progress_fifo"
|
|
55
|
+
if [ $status -ne 0 ]; then
|
|
56
|
+
exit $status
|
|
57
|
+
fi
|
|
58
|
+
else
|
|
59
|
+
tar -C "$PINOKIO_HOME" -xzf /app/.pinokio-seed.tgz
|
|
60
|
+
fi
|
|
61
|
+
echo "[entrypoint] Seed archive extraction complete"
|
|
62
|
+
elif [ -d "/app/.pinokio-seed" ]; then
|
|
63
|
+
echo "[entrypoint] Bootstrapping Pinokio home at $PINOKIO_HOME"
|
|
64
|
+
echo "[entrypoint] Copying Pinokio seed directory"
|
|
65
|
+
cp -a /app/.pinokio-seed/. "$PINOKIO_HOME"/
|
|
66
|
+
echo "[entrypoint] Seed directory copy complete"
|
|
67
|
+
fi
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
if [ -e "/app/.pinokio-seed" ] && [ ! -L "/app/.pinokio-seed" ]; then
|
|
71
|
+
rm -rf /app/.pinokio-seed
|
|
72
|
+
fi
|
|
73
|
+
ln -sfn "$PINOKIO_HOME" /app/.pinokio-seed
|
|
74
|
+
|
|
75
|
+
exec "$@"
|
package/kernel/api/hf/index.js
CHANGED
|
@@ -26,7 +26,7 @@ class HF {
|
|
|
26
26
|
delete params.path
|
|
27
27
|
let chunks = unparse(params)
|
|
28
28
|
let message = [
|
|
29
|
-
`
|
|
29
|
+
`hf download ${chunks.join(" ")}` + (kernel.platform === "win32" ? " && dir" : " ; ls")
|
|
30
30
|
]
|
|
31
31
|
console.log({ message, before: req.params.message })
|
|
32
32
|
req.params.message = message
|
package/kernel/api/index.js
CHANGED
|
@@ -53,6 +53,9 @@ class Shell {
|
|
|
53
53
|
} else if (req.parent && req.parent.path) {
|
|
54
54
|
options.group = req.parent.path
|
|
55
55
|
}
|
|
56
|
+
if (req.parent && req.parent.body && req.parent.body.title) {
|
|
57
|
+
options.title = req.parent.body.title
|
|
58
|
+
}
|
|
56
59
|
let id = await kernel.shell.start(req.params, options, ondata)
|
|
57
60
|
return id
|
|
58
61
|
//let id = await kernel.shell.start(req.params, options)
|
|
@@ -130,6 +133,9 @@ class Shell {
|
|
|
130
133
|
} else if (req.parent && req.parent.path) {
|
|
131
134
|
options.group = req.parent.path
|
|
132
135
|
}
|
|
136
|
+
if (req.parent && req.parent.body && req.parent.body.title) {
|
|
137
|
+
options.title = req.parent.body.title
|
|
138
|
+
}
|
|
133
139
|
if (req.client) {
|
|
134
140
|
req.params.rows = req.client.rows
|
|
135
141
|
req.params.cols = req.client.cols
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const sanitize = require('sanitize-filename')
|
|
4
|
+
|
|
5
|
+
class Terminal {
|
|
6
|
+
async upload(req, ondata, kernel) {
|
|
7
|
+
const params = req.params || {}
|
|
8
|
+
const files = Array.isArray(params.files) ? params.files : []
|
|
9
|
+
const buffers = params.buffers || {}
|
|
10
|
+
|
|
11
|
+
if (files.length === 0) {
|
|
12
|
+
if (ondata) {
|
|
13
|
+
ondata({ raw: "\r\nNo files provided for upload.\r\n" })
|
|
14
|
+
}
|
|
15
|
+
return { files: [] }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const shellInstance = this.resolveShellInstance(params, kernel)
|
|
19
|
+
const shellCwd = this.resolveShellCwd(params, kernel)
|
|
20
|
+
const baseCwd = shellInstance && typeof shellInstance.path === 'string' && shellInstance.path.trim().length > 0
|
|
21
|
+
? shellInstance.path
|
|
22
|
+
: shellCwd
|
|
23
|
+
|
|
24
|
+
const uploadRoot = baseCwd
|
|
25
|
+
? path.join(baseCwd, '.pinokio-temp')
|
|
26
|
+
: kernel.path('temp', 'web-terminal')
|
|
27
|
+
|
|
28
|
+
await fs.promises.mkdir(uploadRoot, { recursive: true })
|
|
29
|
+
|
|
30
|
+
const saved = []
|
|
31
|
+
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const key = file && file.key
|
|
34
|
+
if (!key || !buffers[key]) {
|
|
35
|
+
continue
|
|
36
|
+
}
|
|
37
|
+
const sourceBuffer = buffers[key]
|
|
38
|
+
const originalName = typeof file.name === 'string' && file.name.length > 0
|
|
39
|
+
? file.name
|
|
40
|
+
: 'upload'
|
|
41
|
+
const sanitized = sanitize(originalName) || `upload-${Date.now()}`
|
|
42
|
+
const targetName = await this.uniqueFilename(uploadRoot, sanitized)
|
|
43
|
+
const targetPath = path.join(uploadRoot, targetName)
|
|
44
|
+
|
|
45
|
+
await fs.promises.writeFile(targetPath, sourceBuffer)
|
|
46
|
+
delete buffers[key]
|
|
47
|
+
|
|
48
|
+
const homeRelativePath = path.relative(kernel.homedir, targetPath)
|
|
49
|
+
const normalizedHomeRelativePath = homeRelativePath.split(path.sep).join('/')
|
|
50
|
+
const cliPath = targetPath
|
|
51
|
+
const cliBase = baseCwd || kernel.homedir
|
|
52
|
+
const cliRelative = cliBase ? path.relative(cliBase, targetPath) : null
|
|
53
|
+
const cliRelativePath = cliRelative ? cliRelative.split(path.sep).join('/') : null
|
|
54
|
+
|
|
55
|
+
saved.push({
|
|
56
|
+
originalName,
|
|
57
|
+
storedAs: targetName,
|
|
58
|
+
path: targetPath,
|
|
59
|
+
size: typeof file.size === 'number' ? file.size : sourceBuffer.length,
|
|
60
|
+
mimeType: typeof file.type === 'string' ? file.type : '',
|
|
61
|
+
homeRelativePath: normalizedHomeRelativePath,
|
|
62
|
+
displayPath: `~/${normalizedHomeRelativePath}`,
|
|
63
|
+
cliPath,
|
|
64
|
+
cliRelativePath
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (saved.length === 0) {
|
|
70
|
+
if (ondata) {
|
|
71
|
+
ondata({ raw: "\r\nNo files were saved.\r\n" })
|
|
72
|
+
}
|
|
73
|
+
return { files: saved }
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const marker = '[attachment] '
|
|
77
|
+
if (params && params.id && kernel && kernel.shell && typeof kernel.shell.emit === 'function') {
|
|
78
|
+
try {
|
|
79
|
+
kernel.shell.emit({
|
|
80
|
+
id: params.id,
|
|
81
|
+
emit: marker,
|
|
82
|
+
paste: true
|
|
83
|
+
})
|
|
84
|
+
} catch (error) {
|
|
85
|
+
if (ondata) {
|
|
86
|
+
ondata({ raw: marker })
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
} else if (ondata) {
|
|
90
|
+
ondata({ raw: marker })
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Break references to potentially large buffers once handled.
|
|
94
|
+
if (req.params) {
|
|
95
|
+
req.params.buffers = {}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { files: saved }
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
resolveShellInstance(params, kernel) {
|
|
102
|
+
if (!params || typeof params.id !== 'string') {
|
|
103
|
+
return null
|
|
104
|
+
}
|
|
105
|
+
if (!kernel || !kernel.shell || typeof kernel.shell.get !== 'function') {
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
try {
|
|
109
|
+
return kernel.shell.get(params.id)
|
|
110
|
+
} catch (error) {
|
|
111
|
+
return null
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
resolveShellCwd(params, kernel) {
|
|
116
|
+
if (!params || typeof params.cwd !== 'string') {
|
|
117
|
+
return null
|
|
118
|
+
}
|
|
119
|
+
let cwd = params.cwd.trim()
|
|
120
|
+
if (!cwd) {
|
|
121
|
+
return null
|
|
122
|
+
}
|
|
123
|
+
const queryIndex = cwd.indexOf('?')
|
|
124
|
+
if (queryIndex !== -1) {
|
|
125
|
+
cwd = cwd.slice(0, queryIndex)
|
|
126
|
+
}
|
|
127
|
+
const hashIndex = cwd.indexOf('#')
|
|
128
|
+
if (hashIndex !== -1) {
|
|
129
|
+
cwd = cwd.slice(0, hashIndex)
|
|
130
|
+
}
|
|
131
|
+
if (!cwd) {
|
|
132
|
+
return null
|
|
133
|
+
}
|
|
134
|
+
if (cwd.startsWith('~/')) {
|
|
135
|
+
return path.resolve(kernel.homedir, cwd.slice(2))
|
|
136
|
+
}
|
|
137
|
+
if (path.isAbsolute(cwd)) {
|
|
138
|
+
return cwd
|
|
139
|
+
}
|
|
140
|
+
return path.resolve(kernel.homedir, cwd)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async uniqueFilename(dir, candidate) {
|
|
144
|
+
const parsed = path.parse(candidate)
|
|
145
|
+
const baseName = parsed.name && parsed.name.trim().length > 0 ? parsed.name : 'upload'
|
|
146
|
+
const extension = parsed.ext || ''
|
|
147
|
+
let attemptIndex = 0
|
|
148
|
+
|
|
149
|
+
// Ensure we do not end up in an infinite loop if fs.access throws non-ENOENT errors.
|
|
150
|
+
while (true) {
|
|
151
|
+
const attemptName = attemptIndex === 0 ? `${baseName}${extension}` : `${baseName}_${attemptIndex}${extension}`
|
|
152
|
+
const attemptPath = path.join(dir, attemptName)
|
|
153
|
+
try {
|
|
154
|
+
await fs.promises.access(attemptPath)
|
|
155
|
+
attemptIndex += 1
|
|
156
|
+
} catch (error) {
|
|
157
|
+
if (error && error.code === 'ENOENT') {
|
|
158
|
+
return attemptName
|
|
159
|
+
}
|
|
160
|
+
throw error
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
module.exports = Terminal
|
package/kernel/bin/conda.js
CHANGED
|
@@ -148,7 +148,8 @@ report_errors: false`)
|
|
|
148
148
|
//if (String(version) === "24.7.0") {
|
|
149
149
|
let channel = chunks[3]
|
|
150
150
|
let coerced = semver.coerce(version)
|
|
151
|
-
let mamba_requirement = ">=24.11.1"
|
|
151
|
+
//let mamba_requirement = ">=24.11.1"
|
|
152
|
+
let mamba_requirement = ">=25.4.0"
|
|
152
153
|
//if (semver.satisfies(coerced, mamba_requirement) && channel === "conda-forge") {
|
|
153
154
|
if (semver.satisfies(coerced, mamba_requirement)) {
|
|
154
155
|
conda_check.mamba = true
|
|
@@ -266,7 +267,7 @@ report_errors: false`)
|
|
|
266
267
|
let cmds = [
|
|
267
268
|
//"conda clean -y --index-cache",
|
|
268
269
|
"conda clean -y --all",
|
|
269
|
-
`conda install -y -c conda-forge sqlite=3.47.2 ${mods}`,
|
|
270
|
+
`conda install -y -c conda-forge sqlite=3.47.2 conda-libmamba-solver>=25.4.0 ${mods}`,
|
|
270
271
|
|
|
271
272
|
// `conda config --file ${this.kernel.path('condarc')} --set remote_connect_timeout_secs 20`,
|
|
272
273
|
// `conda config --file ${this.kernel.path('condarc')} --set remote_read_timeout_secs 300`,
|
package/kernel/bin/index.js
CHANGED
|
@@ -368,7 +368,7 @@ class Bin {
|
|
|
368
368
|
//if (String(version) === "24.7.0") {
|
|
369
369
|
let channel = chunks[3]
|
|
370
370
|
let coerced = semver.coerce(version)
|
|
371
|
-
let mamba_requirement = ">=
|
|
371
|
+
let mamba_requirement = ">=25.4.0"
|
|
372
372
|
//if (semver.satisfies(coerced, mamba_requirement) && channel === "conda-forge") {
|
|
373
373
|
if (semver.satisfies(coerced, mamba_requirement)) {
|
|
374
374
|
conda_check.mamba = true
|
|
@@ -631,6 +631,57 @@ class Bin {
|
|
|
631
631
|
return res
|
|
632
632
|
}
|
|
633
633
|
}
|
|
634
|
+
async resolveInstallRequirements(req, ondata) {
|
|
635
|
+
let params = req.params
|
|
636
|
+
let mode = req.mode
|
|
637
|
+
if (typeof params === 'string') {
|
|
638
|
+
let trimmed = params.trim()
|
|
639
|
+
if (trimmed.length === 0) {
|
|
640
|
+
params = null
|
|
641
|
+
} else if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
642
|
+
try {
|
|
643
|
+
params = JSON.parse(trimmed)
|
|
644
|
+
} catch (e) {
|
|
645
|
+
params = trimmed
|
|
646
|
+
}
|
|
647
|
+
} else {
|
|
648
|
+
params = trimmed
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
if (Array.isArray(params)) {
|
|
652
|
+
return params
|
|
653
|
+
}
|
|
654
|
+
if (params && typeof params === 'object') {
|
|
655
|
+
if (Array.isArray(params.requirements)) {
|
|
656
|
+
return params.requirements
|
|
657
|
+
}
|
|
658
|
+
if (typeof params.mode === 'string' && params.mode.trim().length > 0) {
|
|
659
|
+
mode = params.mode.trim()
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
if (!mode && typeof params === 'string' && params.length > 0) {
|
|
663
|
+
mode = params
|
|
664
|
+
}
|
|
665
|
+
if (!mode && req.params && typeof req.params.mode === 'string' && req.params.mode.trim().length > 0) {
|
|
666
|
+
mode = req.params.mode.trim()
|
|
667
|
+
}
|
|
668
|
+
if (!mode && typeof req.mode === 'string' && req.mode.trim().length > 0) {
|
|
669
|
+
mode = req.mode.trim()
|
|
670
|
+
}
|
|
671
|
+
if (!mode) {
|
|
672
|
+
throw new Error('kernel.bin.install requires `requirements` array or `mode` string in params')
|
|
673
|
+
}
|
|
674
|
+
const preset = this.preset(mode)
|
|
675
|
+
if (!preset) {
|
|
676
|
+
const available = Object.keys(Setup).sort().join(', ')
|
|
677
|
+
throw new Error(`Unknown setup mode "${mode}". Available modes: ${available}`)
|
|
678
|
+
}
|
|
679
|
+
if (ondata) {
|
|
680
|
+
ondata({ html: `<b>Resolving setup preset "${mode}"</b>` }, 'notify2')
|
|
681
|
+
}
|
|
682
|
+
const { requirements } = await this.check({ bin: preset })
|
|
683
|
+
return Array.isArray(requirements) ? requirements : []
|
|
684
|
+
}
|
|
634
685
|
// async init_launcher(req, ondata) {
|
|
635
686
|
// console.log("init_launcher", req)
|
|
636
687
|
// try {
|
|
@@ -685,7 +736,7 @@ class Bin {
|
|
|
685
736
|
} else {
|
|
686
737
|
this.client = null
|
|
687
738
|
}
|
|
688
|
-
let requirements =
|
|
739
|
+
let requirements = await this.resolveInstallRequirements(req, ondata)
|
|
689
740
|
for(let x=0; x<10; x++) {
|
|
690
741
|
console.log(`## Install Attempt ${x}`)
|
|
691
742
|
let i = 0;
|
package/kernel/bin/setup.js
CHANGED
|
@@ -111,6 +111,38 @@ module.exports = {
|
|
|
111
111
|
]
|
|
112
112
|
}
|
|
113
113
|
},
|
|
114
|
+
prod_dev: (kernel) => {
|
|
115
|
+
let requirements = [
|
|
116
|
+
{ name: "conda", },
|
|
117
|
+
{ name: "zip", },
|
|
118
|
+
]
|
|
119
|
+
if (platform === "darwin") {
|
|
120
|
+
requirements.push({ name: "brew" })
|
|
121
|
+
}
|
|
122
|
+
requirements = requirements.concat([
|
|
123
|
+
{ name: "git", },
|
|
124
|
+
{ name: "node", },
|
|
125
|
+
{ name: "cli", },
|
|
126
|
+
{ name: "uv", },
|
|
127
|
+
{ name: "caddy", },
|
|
128
|
+
{ name: "py", },
|
|
129
|
+
{ name: "browserless" },
|
|
130
|
+
])
|
|
131
|
+
let conda_requirements = [
|
|
132
|
+
zip_cmd,
|
|
133
|
+
"uv",
|
|
134
|
+
"node",
|
|
135
|
+
"git",
|
|
136
|
+
"caddy"
|
|
137
|
+
]
|
|
138
|
+
return {
|
|
139
|
+
icon: "fa-solid fa-laptop-code",
|
|
140
|
+
title: "Coding (Essential)",
|
|
141
|
+
description: "Install common modules required for development (Node.js, python, Visual Studio Developer Tools (Windows), Xcode build tools (Mac)",
|
|
142
|
+
requirements,
|
|
143
|
+
conda_requirements,
|
|
144
|
+
}
|
|
145
|
+
},
|
|
114
146
|
dev: (kernel) => {
|
|
115
147
|
let requirements = [
|
|
116
148
|
{ name: "conda", },
|
package/kernel/bin/vs.js
CHANGED
|
@@ -125,6 +125,15 @@ class VS {
|
|
|
125
125
|
|
|
126
126
|
}
|
|
127
127
|
async init() {
|
|
128
|
+
if (!this.kernel || this.kernel.platform !== "win32") {
|
|
129
|
+
this._env = {
|
|
130
|
+
MSVC_PATH: [],
|
|
131
|
+
BUILD_PATH: [],
|
|
132
|
+
CMAKE_PATH: [],
|
|
133
|
+
CL_PATH: [],
|
|
134
|
+
}
|
|
135
|
+
return
|
|
136
|
+
}
|
|
128
137
|
if (this.kernel.platform === "win32") {
|
|
129
138
|
/*
|
|
130
139
|
{
|
|
@@ -151,12 +160,12 @@ class VS {
|
|
|
151
160
|
}
|
|
152
161
|
}
|
|
153
162
|
env () {
|
|
154
|
-
if (this.kernel.platform === "win32") {
|
|
163
|
+
if (this.kernel && this.kernel.platform === "win32") {
|
|
155
164
|
return this._env
|
|
156
165
|
}
|
|
157
166
|
}
|
|
158
167
|
async installed() {
|
|
159
|
-
if (this.kernel.platform === "win32") {
|
|
168
|
+
if (this.kernel && this.kernel.platform === "win32") {
|
|
160
169
|
await this.init()
|
|
161
170
|
console.log("VS INSTALLED CHECK", this._env)
|
|
162
171
|
return this._env.MSVC_PATH && this._env.MSVC_PATH.length > 0 && this._env.BUILD_PATH && this._env.BUILD_PATH.length > 0 && this._env.CMAKE_PATH && this._env.CMAKE_PATH.length > 0 && this._env.VCVARSALL_PATH && this._env.VCVARSALL_PATH.length > 0
|
package/kernel/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const Store = require('./store')
|
|
|
33
33
|
const Proto = require('./prototype')
|
|
34
34
|
const Plugin = require('./plugin')
|
|
35
35
|
const Router = require("./router")
|
|
36
|
+
const PinokioDomainRouter = require("./router/pinokio_domain_router")
|
|
36
37
|
const Procs = require('./procs')
|
|
37
38
|
const Peer = require('./peer')
|
|
38
39
|
const Git = require('./git')
|
|
@@ -388,11 +389,43 @@ class Kernel {
|
|
|
388
389
|
return false
|
|
389
390
|
}
|
|
390
391
|
}
|
|
392
|
+
async resolvePinokioDomain() {
|
|
393
|
+
const envDomain = (process.env.PINOKIO_DOMAIN || '').trim()
|
|
394
|
+
if (envDomain.length > 0) {
|
|
395
|
+
return envDomain
|
|
396
|
+
}
|
|
397
|
+
if (!this.homedir) {
|
|
398
|
+
return ''
|
|
399
|
+
}
|
|
400
|
+
try {
|
|
401
|
+
const env = await Environment.get(this.homedir, this)
|
|
402
|
+
const value = (env.PINOKIO_DOMAIN || '').trim()
|
|
403
|
+
return value
|
|
404
|
+
} catch (e) {
|
|
405
|
+
return ''
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async ensureRouterMode() {
|
|
409
|
+
const domain = await this.resolvePinokioDomain()
|
|
410
|
+
const shouldUseCustom = domain.length > 0
|
|
411
|
+
if (shouldUseCustom && this.router_kind !== 'custom-domain') {
|
|
412
|
+
console.log('[router] switching to custom-domain router mode')
|
|
413
|
+
this.router = new PinokioDomainRouter(this)
|
|
414
|
+
this.router_kind = 'custom-domain'
|
|
415
|
+
} else if (!shouldUseCustom && this.router_kind !== 'default') {
|
|
416
|
+
console.log('[router] switching to default router mode')
|
|
417
|
+
this.router = new Router(this)
|
|
418
|
+
this.router_kind = 'default'
|
|
419
|
+
}
|
|
420
|
+
this.pinokio_domain_value = domain
|
|
421
|
+
}
|
|
391
422
|
async refresh(notify_peers) {
|
|
392
423
|
const ts = Date.now()
|
|
393
424
|
|
|
394
425
|
await this.peer.check(this)
|
|
395
426
|
|
|
427
|
+
await this.ensureRouterMode()
|
|
428
|
+
|
|
396
429
|
if (this.peer.peer_active) {
|
|
397
430
|
// 1. get the process list
|
|
398
431
|
await this.processes.refresh()
|
|
@@ -769,7 +802,7 @@ class Kernel {
|
|
|
769
802
|
/// }
|
|
770
803
|
async init(options) {
|
|
771
804
|
|
|
772
|
-
let home = this.store.get("home")
|
|
805
|
+
let home = this.store.get("home") || process.env.PINOKIO_HOME
|
|
773
806
|
|
|
774
807
|
// reset shells if they exist
|
|
775
808
|
if (this.shell) {
|
|
@@ -834,7 +867,14 @@ class Kernel {
|
|
|
834
867
|
this.api = new Api(this)
|
|
835
868
|
this.python = new Python(this)
|
|
836
869
|
this.shell = new Shells(this)
|
|
837
|
-
this.
|
|
870
|
+
this.pinokio_domain_value = (process.env.PINOKIO_DOMAIN || '').trim()
|
|
871
|
+
if (this.pinokio_domain_value) {
|
|
872
|
+
this.router = new PinokioDomainRouter(this)
|
|
873
|
+
this.router_kind = 'custom-domain'
|
|
874
|
+
} else {
|
|
875
|
+
this.router = new Router(this)
|
|
876
|
+
this.router_kind = 'default'
|
|
877
|
+
}
|
|
838
878
|
this.connect = new Connect(this)
|
|
839
879
|
this.system = system
|
|
840
880
|
this.keys = {}
|
package/kernel/info.js
CHANGED
|
@@ -68,6 +68,42 @@ class Info {
|
|
|
68
68
|
let resolved_path = path.resolve(cwd, ...args)
|
|
69
69
|
return this.kernel.memory.global[resolved_path] || {}
|
|
70
70
|
}
|
|
71
|
+
scriptsByApi() {
|
|
72
|
+
if (!this.kernel || !this.kernel.memory || !this.kernel.memory.local) {
|
|
73
|
+
return {}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const apiRoot = this.kernel.path("api")
|
|
77
|
+
const scriptsByApi = {}
|
|
78
|
+
|
|
79
|
+
for (const [id, localVariables] of Object.entries(this.kernel.memory.local)) {
|
|
80
|
+
if (!id) continue
|
|
81
|
+
|
|
82
|
+
const scriptPath = id.split("?")[0]
|
|
83
|
+
if (!scriptPath || !this.isSubpath(apiRoot, scriptPath)) continue
|
|
84
|
+
|
|
85
|
+
const apiName = Util.api_name(scriptPath, this.kernel)
|
|
86
|
+
if (!apiName || apiName === '.' || apiName.startsWith('..')) continue
|
|
87
|
+
|
|
88
|
+
if (!scriptsByApi[apiName]) {
|
|
89
|
+
scriptsByApi[apiName] = []
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
scriptsByApi[apiName].push({
|
|
93
|
+
uri: scriptPath,
|
|
94
|
+
local: localVariables || {}
|
|
95
|
+
})
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return scriptsByApi
|
|
99
|
+
}
|
|
100
|
+
isSubpath(parent, child) {
|
|
101
|
+
if (!parent || !child) {
|
|
102
|
+
return false
|
|
103
|
+
}
|
|
104
|
+
const relative = path.relative(parent, child)
|
|
105
|
+
return !!relative && !relative.startsWith('..') && !path.isAbsolute(relative)
|
|
106
|
+
}
|
|
71
107
|
}
|
|
72
108
|
|
|
73
109
|
module.exports = Info
|