pinokiod 7.5.7 → 7.5.8
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/kernel/api/shell/index.js +7 -2
- package/kernel/bin/node.js +13 -8
- package/kernel/shell.js +20 -0
- package/kernel/shell_conda_runtime_guard.js +647 -0
- package/kernel/shells.js +7 -1
- package/package.json +1 -1
- package/server/index.js +3 -0
- package/server/public/common.js +11 -4
- package/server/public/logs.js +597 -4
- package/server/public/style.css +203 -42
- package/server/views/app.ejs +39 -31
- package/server/views/editor.ejs +1 -0
- package/server/views/install.ejs +1 -0
- package/server/views/logs.ejs +9 -5
- package/server/views/terminal.ejs +34 -5
- package/test/node-bin.test.js +39 -0
- package/test/shell-api.test.js +99 -9
- package/test/shell-conda-runtime-guard.test.js +558 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const test = require('node:test')
|
|
3
|
+
|
|
4
|
+
const Node = require('../kernel/bin/node')
|
|
5
|
+
|
|
6
|
+
function createNode({ nodeVersion = '24.18.0', pnpmVersion = '11.9.0', hasNode = true, hasPnpm = true } = {}) {
|
|
7
|
+
const node = new Node()
|
|
8
|
+
const conda = new Set()
|
|
9
|
+
if (hasNode) conda.add('nodejs')
|
|
10
|
+
if (hasPnpm) conda.add('pnpm')
|
|
11
|
+
node.kernel = {
|
|
12
|
+
bin: {
|
|
13
|
+
installed: {
|
|
14
|
+
conda,
|
|
15
|
+
conda_versions: {
|
|
16
|
+
nodejs: nodeVersion,
|
|
17
|
+
pnpm: pnpmVersion,
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
}
|
|
22
|
+
return node
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test('Node bin pins Node.js 24 LTS and matching pnpm', () => {
|
|
26
|
+
assert.equal(createNode().cmd(), 'nodejs=24.18.0 pnpm=11.9.0')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test('Node.installed accepts exactly the pinned Node.js and pnpm versions', async () => {
|
|
30
|
+
assert.equal(await createNode().installed(), true)
|
|
31
|
+
assert.equal(await createNode({ nodeVersion: '24.18.0-h654b19f_0' }).installed(), true)
|
|
32
|
+
assert.equal(await createNode({ pnpmVersion: '11.9.0-h7c87c79_0' }).installed(), true)
|
|
33
|
+
assert.equal(await createNode({ nodeVersion: '22.21.1' }).installed(), false)
|
|
34
|
+
assert.equal(await createNode({ nodeVersion: '26.4.0' }).installed(), false)
|
|
35
|
+
assert.equal(await createNode({ pnpmVersion: '10.29.3' }).installed(), false)
|
|
36
|
+
assert.equal(await createNode({ pnpmVersion: '11.8.0' }).installed(), false)
|
|
37
|
+
assert.equal(await createNode({ hasNode: false }).installed(), false)
|
|
38
|
+
assert.equal(await createNode({ hasPnpm: false }).installed(), false)
|
|
39
|
+
})
|
package/test/shell-api.test.js
CHANGED
|
@@ -2,6 +2,9 @@ const test = require('node:test')
|
|
|
2
2
|
const assert = require('node:assert/strict')
|
|
3
3
|
const path = require('node:path')
|
|
4
4
|
const ShellAPI = require('../kernel/api/shell')
|
|
5
|
+
const Shell = require('../kernel/shell')
|
|
6
|
+
const Shells = require('../kernel/shells')
|
|
7
|
+
const CondaRuntimeGuard = require('../kernel/shell_conda_runtime_guard')
|
|
5
8
|
|
|
6
9
|
function createKernel () {
|
|
7
10
|
const calls = []
|
|
@@ -10,23 +13,23 @@ function createKernel () {
|
|
|
10
13
|
homedir: '/tmp/pinokio-home',
|
|
11
14
|
shell: {
|
|
12
15
|
start: async (params, options) => {
|
|
13
|
-
calls.push({ method: 'start', params: structuredClone(params), options: structuredClone(options) })
|
|
16
|
+
calls.push({ method: 'start', guard: params[CondaRuntimeGuard.SHELL_RUN_GUARD] === true, params: structuredClone(params), options: structuredClone(options) })
|
|
14
17
|
return 'shell-id'
|
|
15
18
|
},
|
|
16
19
|
enter: async (params) => {
|
|
17
|
-
calls.push({ method: 'enter', params: structuredClone(params) })
|
|
20
|
+
calls.push({ method: 'enter', guard: params[CondaRuntimeGuard.SHELL_RUN_GUARD] === true, params: structuredClone(params) })
|
|
18
21
|
return 'entered'
|
|
19
22
|
},
|
|
20
23
|
write: async (params) => {
|
|
21
|
-
calls.push({ method: 'write', params: structuredClone(params) })
|
|
24
|
+
calls.push({ method: 'write', guard: params[CondaRuntimeGuard.SHELL_RUN_GUARD] === true, params: structuredClone(params) })
|
|
22
25
|
return 'written'
|
|
23
26
|
},
|
|
24
27
|
run: async (params, options) => {
|
|
25
|
-
calls.push({ method: 'run', params: structuredClone(params), options: structuredClone(options) })
|
|
28
|
+
calls.push({ method: 'run', guard: params[CondaRuntimeGuard.SHELL_RUN_GUARD] === true, params: structuredClone(params), options: structuredClone(options) })
|
|
26
29
|
return { stdout: 'ok' }
|
|
27
30
|
},
|
|
28
31
|
kill: async (params) => {
|
|
29
|
-
calls.push({ method: 'kill', params: structuredClone(params) })
|
|
32
|
+
calls.push({ method: 'kill', guard: params[CondaRuntimeGuard.SHELL_RUN_GUARD] === true, params: structuredClone(params) })
|
|
30
33
|
}
|
|
31
34
|
}
|
|
32
35
|
}
|
|
@@ -51,6 +54,7 @@ test('shell.start forwards defaults, client size, parent, and shell options', as
|
|
|
51
54
|
assert.equal(result, 'shell-id')
|
|
52
55
|
assert.deepEqual(kernel.calls, [{
|
|
53
56
|
method: 'start',
|
|
57
|
+
guard: false,
|
|
54
58
|
params: {
|
|
55
59
|
id: cwd,
|
|
56
60
|
path: cwd,
|
|
@@ -76,25 +80,28 @@ test('shell.enter, shell.write, and shell.stop target current cwd by default', a
|
|
|
76
80
|
const cwd = path.join('/tmp', 'pinokio-shell-api')
|
|
77
81
|
const parent = { path: path.join(cwd, 'script.js') }
|
|
78
82
|
|
|
79
|
-
assert.equal(await api.enter({ cwd, parent, params: { message: '
|
|
80
|
-
assert.equal(await api.write({ cwd, params: { message: '
|
|
83
|
+
assert.equal(await api.enter({ cwd, parent, params: { message: 'conda install python=3.12 -y' } }, () => {}, kernel), 'entered')
|
|
84
|
+
assert.equal(await api.write({ cwd, params: { message: 'conda update --all' } }, () => {}, kernel), 'written')
|
|
81
85
|
await api.stop({ cwd, params: {} }, () => {}, kernel)
|
|
82
86
|
|
|
83
87
|
assert.deepEqual(kernel.calls, [{
|
|
84
88
|
method: 'enter',
|
|
89
|
+
guard: false,
|
|
85
90
|
params: {
|
|
86
|
-
message: '
|
|
91
|
+
message: 'conda install python=3.12 -y',
|
|
87
92
|
id: cwd,
|
|
88
93
|
$parent: parent
|
|
89
94
|
}
|
|
90
95
|
}, {
|
|
91
96
|
method: 'write',
|
|
97
|
+
guard: false,
|
|
92
98
|
params: {
|
|
93
|
-
message: '
|
|
99
|
+
message: 'conda update --all',
|
|
94
100
|
id: cwd
|
|
95
101
|
}
|
|
96
102
|
}, {
|
|
97
103
|
method: 'kill',
|
|
104
|
+
guard: false,
|
|
98
105
|
params: {
|
|
99
106
|
id: cwd
|
|
100
107
|
}
|
|
@@ -118,6 +125,7 @@ test('shell.run applies deterministic defaults and forwards execution options',
|
|
|
118
125
|
assert.deepEqual(result, { stdout: 'ok' })
|
|
119
126
|
assert.deepEqual(kernel.calls, [{
|
|
120
127
|
method: 'run',
|
|
128
|
+
guard: true,
|
|
121
129
|
params: {
|
|
122
130
|
message: 'echo ok',
|
|
123
131
|
path: cwd,
|
|
@@ -132,3 +140,85 @@ test('shell.run applies deterministic defaults and forwards execution options',
|
|
|
132
140
|
}
|
|
133
141
|
}])
|
|
134
142
|
})
|
|
143
|
+
|
|
144
|
+
test('shell.run forwards typed stream events', async () => {
|
|
145
|
+
const api = new ShellAPI()
|
|
146
|
+
const events = []
|
|
147
|
+
const kernel = createKernel()
|
|
148
|
+
kernel.shell.run = async (params, options, ondata) => {
|
|
149
|
+
ondata({ html: 'notice', type: 'warning' }, 'notify')
|
|
150
|
+
return { stdout: 'ok' }
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const result = await api.run({
|
|
154
|
+
cwd: path.join('/tmp', 'pinokio-shell-api'),
|
|
155
|
+
params: {
|
|
156
|
+
message: 'echo ok'
|
|
157
|
+
}
|
|
158
|
+
}, (stream, type) => {
|
|
159
|
+
events.push({ stream, type })
|
|
160
|
+
}, kernel)
|
|
161
|
+
|
|
162
|
+
assert.deepEqual(result, { stdout: 'ok' })
|
|
163
|
+
assert.deepEqual(events, [{
|
|
164
|
+
stream: {
|
|
165
|
+
html: 'notice',
|
|
166
|
+
type: 'warning'
|
|
167
|
+
},
|
|
168
|
+
type: 'notify'
|
|
169
|
+
}])
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
test('Shells.launch forwards typed stream events without live-output parsing', async () => {
|
|
173
|
+
const events = []
|
|
174
|
+
const originalStart = Shell.prototype.start
|
|
175
|
+
Shell.prototype.start = async function (_params, ondata) {
|
|
176
|
+
this.id = 'stub-shell'
|
|
177
|
+
await ondata({ html: 'notice', type: 'warning' }, 'notify')
|
|
178
|
+
return ''
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
try {
|
|
182
|
+
const root = path.join('/tmp', 'pinokio-shells-api')
|
|
183
|
+
const shells = new Shells({
|
|
184
|
+
homedir: root,
|
|
185
|
+
platform: 'darwin',
|
|
186
|
+
bracketedPasteSupport: { bash: true },
|
|
187
|
+
which: () => null,
|
|
188
|
+
path: (...parts) => path.join(root, ...parts),
|
|
189
|
+
bin: {
|
|
190
|
+
envs: (env = {}) => env
|
|
191
|
+
},
|
|
192
|
+
api: {
|
|
193
|
+
resolvePath: (cwd, target) => path.resolve(cwd, target),
|
|
194
|
+
running: {}
|
|
195
|
+
},
|
|
196
|
+
git: {
|
|
197
|
+
repos: async () => [],
|
|
198
|
+
restoreNewReposForActiveSnapshot: async () => {}
|
|
199
|
+
},
|
|
200
|
+
template: {
|
|
201
|
+
render: (value) => value
|
|
202
|
+
}
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
await shells.launch({
|
|
206
|
+
path: path.join(root, 'api', 'demo'),
|
|
207
|
+
message: 'echo ok'
|
|
208
|
+
}, {
|
|
209
|
+
cwd: root
|
|
210
|
+
}, (stream, type) => {
|
|
211
|
+
events.push({ stream, type })
|
|
212
|
+
})
|
|
213
|
+
|
|
214
|
+
assert.deepEqual(events, [{
|
|
215
|
+
stream: {
|
|
216
|
+
html: 'notice',
|
|
217
|
+
type: 'warning'
|
|
218
|
+
},
|
|
219
|
+
type: 'notify'
|
|
220
|
+
}])
|
|
221
|
+
} finally {
|
|
222
|
+
Shell.prototype.start = originalStart
|
|
223
|
+
}
|
|
224
|
+
})
|