pinokiod 8.0.30 → 8.0.32
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/bin/conda-meta.js +10 -2
- package/kernel/connect/providers/huggingface/index.js +2 -0
- package/kernel/plugin_sources.js +2 -0
- package/kernel/python_env.js +14 -0
- package/kernel/util.js +4 -1
- package/package.json +1 -1
- package/system/plugin/grok/grok.png +0 -0
- package/system/plugin/grok/pinokio.js +44 -0
- package/system/plugin/grok-auto/grok.png +0 -0
- package/system/plugin/grok-auto/pinokio.js +20 -0
- package/test/conda-bin.test.js +61 -0
- package/test/filepicker-python-env.test.js +55 -0
- package/test/grok-plugin.test.js +78 -0
- package/test/huggingface-connect.test.js +35 -0
- package/test/plugin-sources.test.js +19 -1
package/kernel/bin/conda-meta.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs')
|
|
2
2
|
const path = require('path')
|
|
3
3
|
const { execFile } = require('child_process')
|
|
4
|
+
const { managedPythonEnv } = require('../python_env')
|
|
4
5
|
|
|
5
6
|
async function buildCondaListFromMeta(minicondaPath, useCondaList) {
|
|
6
7
|
if (!useCondaList) {
|
|
@@ -59,7 +60,10 @@ async function runCondaList(minicondaPath) {
|
|
|
59
60
|
return { response: '', source: 'conda-list' }
|
|
60
61
|
}
|
|
61
62
|
return await new Promise((resolve) => {
|
|
62
|
-
execFile(condaBinary, ['list'], {
|
|
63
|
+
execFile(condaBinary, ['list'], {
|
|
64
|
+
env: managedPythonEnv(),
|
|
65
|
+
windowsHide: true,
|
|
66
|
+
}, (err, stdout) => {
|
|
63
67
|
if (err) {
|
|
64
68
|
resolve({ response: '', source: 'conda-list' })
|
|
65
69
|
} else {
|
|
@@ -75,7 +79,11 @@ async function managedCondaRuns(minicondaPath, platform = process.platform) {
|
|
|
75
79
|
return false
|
|
76
80
|
}
|
|
77
81
|
return await new Promise((resolve) => {
|
|
78
|
-
execFile(condaBinary, ['--version'], {
|
|
82
|
+
execFile(condaBinary, ['--version'], {
|
|
83
|
+
env: managedPythonEnv(),
|
|
84
|
+
windowsHide: true,
|
|
85
|
+
timeout: 15000,
|
|
86
|
+
}, (err) => {
|
|
79
87
|
resolve(!err)
|
|
80
88
|
})
|
|
81
89
|
})
|
|
@@ -3,6 +3,7 @@ const fs = require('fs')
|
|
|
3
3
|
const path = require('path')
|
|
4
4
|
const fetch = require('cross-fetch')
|
|
5
5
|
const Environment = require('../../../environment')
|
|
6
|
+
const { managedPythonEnv } = require('../../../python_env')
|
|
6
7
|
|
|
7
8
|
const stripAnsi = (value) => String(value || "").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "")
|
|
8
9
|
|
|
@@ -56,6 +57,7 @@ class Huggingface {
|
|
|
56
57
|
if (context.env && typeof context.env === "object") {
|
|
57
58
|
env = Object.assign(env, context.env)
|
|
58
59
|
}
|
|
60
|
+
env = managedPythonEnv(env)
|
|
59
61
|
if (!env.HF_TOKEN_PATH) {
|
|
60
62
|
env.HF_TOKEN_PATH = this.defaultTokenPath()
|
|
61
63
|
} else if (!path.isAbsolute(env.HF_TOKEN_PATH)) {
|
package/kernel/plugin_sources.js
CHANGED
|
@@ -16,6 +16,8 @@ const FUNCTION_KEYS = new Set([...ACTION_KEYS, ...STATUS_KEYS])
|
|
|
16
16
|
const BUILTIN_TOOL_ALIASES = {
|
|
17
17
|
claude: "pinokio/run/plugin/claude",
|
|
18
18
|
codex: "pinokio/run/plugin/codex",
|
|
19
|
+
grok: "pinokio/run/plugin/grok",
|
|
20
|
+
"grok-build": "pinokio/run/plugin/grok",
|
|
19
21
|
antigravity: "pinokio/run/plugin/antigravity-cli",
|
|
20
22
|
"antigravity-cli": "pinokio/run/plugin/antigravity-cli",
|
|
21
23
|
"code/claude": "pinokio/run/plugin/claude",
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
function managedPythonEnv(source = process.env) {
|
|
2
|
+
const env = { ...(source || {}) }
|
|
3
|
+
for (const key of Object.keys(env)) {
|
|
4
|
+
if (["PYTHONNOUSERSITE", "PYTHONPATH"].includes(key.toUpperCase())) {
|
|
5
|
+
delete env[key]
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
env.PYTHONNOUSERSITE = "1"
|
|
9
|
+
return env
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
module.exports = {
|
|
13
|
+
managedPythonEnv,
|
|
14
|
+
}
|
package/kernel/util.js
CHANGED
|
@@ -13,6 +13,7 @@ const child_process = require('node:child_process');
|
|
|
13
13
|
const {auto: normalizeEOL} = require("eol");
|
|
14
14
|
const {EOL} = require("os");
|
|
15
15
|
const { randomUUID } = require('crypto');
|
|
16
|
+
const { managedPythonEnv } = require('./python_env')
|
|
16
17
|
const fsp = fs.promises;
|
|
17
18
|
const breakPattern = /\n/g;
|
|
18
19
|
const breakReplacement = "\\n";
|
|
@@ -203,7 +204,9 @@ const filepicker = async(req, ondata, kernel) => {
|
|
|
203
204
|
} else {
|
|
204
205
|
python = kernel.path("bin/miniforge/bin/python")
|
|
205
206
|
}
|
|
206
|
-
const proc = spawn(python, [picker_path]
|
|
207
|
+
const proc = spawn(python, [picker_path], {
|
|
208
|
+
env: managedPythonEnv(),
|
|
209
|
+
})
|
|
207
210
|
|
|
208
211
|
let output = "";
|
|
209
212
|
proc.stdout.on("data", (chunk) => output += chunk);
|
package/package.json
CHANGED
|
Binary file
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
title: "Grok Build",
|
|
3
|
+
icon: "grok.png",
|
|
4
|
+
link: "https://github.com/xai-org/grok-build",
|
|
5
|
+
run: [{
|
|
6
|
+
when: "{{platform === 'win32'}}",
|
|
7
|
+
id: "run",
|
|
8
|
+
method: "shell.run",
|
|
9
|
+
params: {
|
|
10
|
+
shell: "{{kernel.path('bin/miniforge/Library/bin/bash.exe')}}",
|
|
11
|
+
conda: {
|
|
12
|
+
skip: true
|
|
13
|
+
},
|
|
14
|
+
message: {
|
|
15
|
+
_: [
|
|
16
|
+
"npx",
|
|
17
|
+
"-y",
|
|
18
|
+
"@xai-official/grok@latest",
|
|
19
|
+
"{{args.prompt || undefined}}"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
path: "{{args.cwd}}",
|
|
23
|
+
input: true,
|
|
24
|
+
buffer: 1024
|
|
25
|
+
}
|
|
26
|
+
}, {
|
|
27
|
+
when: "{{platform !== 'win32'}}",
|
|
28
|
+
id: "run",
|
|
29
|
+
method: "shell.run",
|
|
30
|
+
params: {
|
|
31
|
+
message: {
|
|
32
|
+
_: [
|
|
33
|
+
"npx",
|
|
34
|
+
"-y",
|
|
35
|
+
"@xai-official/grok@latest",
|
|
36
|
+
"{{args.prompt || undefined}}"
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
path: "{{args.cwd}}",
|
|
40
|
+
input: true,
|
|
41
|
+
buffer: 1024
|
|
42
|
+
}
|
|
43
|
+
}]
|
|
44
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
const grok = require("../grok/pinokio")
|
|
2
|
+
|
|
3
|
+
module.exports = {
|
|
4
|
+
...grok,
|
|
5
|
+
title: "Grok Build Auto",
|
|
6
|
+
description: "Grok Build with tool permission prompts automatically approved unless explicitly denied.",
|
|
7
|
+
run: grok.run.map((step) => ({
|
|
8
|
+
...step,
|
|
9
|
+
params: {
|
|
10
|
+
...step.params,
|
|
11
|
+
message: {
|
|
12
|
+
_: [
|
|
13
|
+
...step.params.message._.slice(0, 3),
|
|
14
|
+
"--always-approve",
|
|
15
|
+
...step.params.message._.slice(3)
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}))
|
|
20
|
+
}
|
package/test/conda-bin.test.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const assert = require('node:assert/strict')
|
|
2
|
+
const childProcess = require('node:child_process')
|
|
2
3
|
const fsModule = require('node:fs')
|
|
3
4
|
const fs = require('node:fs/promises')
|
|
4
5
|
const os = require('node:os')
|
|
@@ -75,6 +76,14 @@ async function createLegacyMiniconda(root) {
|
|
|
75
76
|
return createCondaRoot(root, 'miniconda')
|
|
76
77
|
}
|
|
77
78
|
|
|
79
|
+
function restoreEnv(name, value) {
|
|
80
|
+
if (typeof value === 'undefined') {
|
|
81
|
+
delete process.env[name]
|
|
82
|
+
} else {
|
|
83
|
+
process.env[name] = value
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
78
87
|
function createConda(kernel) {
|
|
79
88
|
const conda = new Conda()
|
|
80
89
|
conda.kernel = kernel
|
|
@@ -94,6 +103,58 @@ function createBin(root, platform = 'win32') {
|
|
|
94
103
|
return bin
|
|
95
104
|
}
|
|
96
105
|
|
|
106
|
+
test('managed Conda subprocesses ignore external Python packages', async (t) => {
|
|
107
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-python-env-'))
|
|
108
|
+
const platform = process.platform === 'win32' ? 'win32' : process.platform
|
|
109
|
+
const miniforge = path.join(root, 'bin', 'miniforge')
|
|
110
|
+
const versionCapture = path.join(root, 'version-env.json')
|
|
111
|
+
const listCapture = path.join(root, 'list-env.json')
|
|
112
|
+
const oldCapturePath = process.env.PINOKIO_TEST_ENV_CAPTURE
|
|
113
|
+
const oldNoUserSite = process.env.PYTHONNOUSERSITE
|
|
114
|
+
const oldPythonPath = process.env.PYTHONPATH
|
|
115
|
+
|
|
116
|
+
await writeFakeManagedConda(root, platform)
|
|
117
|
+
const condaMetaPath = require.resolve('../kernel/bin/conda-meta')
|
|
118
|
+
const realExecFile = childProcess.execFile.bind(childProcess)
|
|
119
|
+
delete require.cache[condaMetaPath]
|
|
120
|
+
t.mock.method(childProcess, 'execFile', (_binary, _args, options, done) => {
|
|
121
|
+
return realExecFile(process.execPath, [
|
|
122
|
+
'-e',
|
|
123
|
+
`require('node:fs').writeFileSync(process.env.PINOKIO_TEST_ENV_CAPTURE, JSON.stringify({
|
|
124
|
+
pythonNoUserSite: process.env.PYTHONNOUSERSITE,
|
|
125
|
+
pythonPath: process.env.PYTHONPATH || null
|
|
126
|
+
}))`,
|
|
127
|
+
], options, done)
|
|
128
|
+
})
|
|
129
|
+
t.after(() => {
|
|
130
|
+
delete require.cache[condaMetaPath]
|
|
131
|
+
})
|
|
132
|
+
const { buildCondaListFromMeta, managedCondaRuns } = require('../kernel/bin/conda-meta')
|
|
133
|
+
|
|
134
|
+
process.env.PYTHONNOUSERSITE = '0'
|
|
135
|
+
process.env.PYTHONPATH = 'external-packages'
|
|
136
|
+
try {
|
|
137
|
+
process.env.PINOKIO_TEST_ENV_CAPTURE = versionCapture
|
|
138
|
+
assert.equal(await managedCondaRuns(miniforge, platform), true)
|
|
139
|
+
assert.deepEqual(JSON.parse(await fs.readFile(versionCapture, 'utf8')), {
|
|
140
|
+
pythonNoUserSite: '1',
|
|
141
|
+
pythonPath: null,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
process.env.PINOKIO_TEST_ENV_CAPTURE = listCapture
|
|
145
|
+
await buildCondaListFromMeta(miniforge, true)
|
|
146
|
+
assert.deepEqual(JSON.parse(await fs.readFile(listCapture, 'utf8')), {
|
|
147
|
+
pythonNoUserSite: '1',
|
|
148
|
+
pythonPath: null,
|
|
149
|
+
})
|
|
150
|
+
} finally {
|
|
151
|
+
restoreEnv('PINOKIO_TEST_ENV_CAPTURE', oldCapturePath)
|
|
152
|
+
restoreEnv('PYTHONNOUSERSITE', oldNoUserSite)
|
|
153
|
+
restoreEnv('PYTHONPATH', oldPythonPath)
|
|
154
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
155
|
+
}
|
|
156
|
+
})
|
|
157
|
+
|
|
97
158
|
test('Conda uses Miniforge assets and writes conda-forge-only config', async () => {
|
|
98
159
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-conda-miniforge-config-'))
|
|
99
160
|
const conda = createConda(createKernel(root))
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs/promises')
|
|
3
|
+
const os = require('node:os')
|
|
4
|
+
const path = require('node:path')
|
|
5
|
+
const test = require('node:test')
|
|
6
|
+
|
|
7
|
+
const Util = require('../kernel/util')
|
|
8
|
+
|
|
9
|
+
function restoreEnv(name, value) {
|
|
10
|
+
if (typeof value === 'undefined') {
|
|
11
|
+
delete process.env[name]
|
|
12
|
+
} else {
|
|
13
|
+
process.env[name] = value
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('file picker isolates its managed Python from external packages', async () => {
|
|
18
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-filepicker-python-env-'))
|
|
19
|
+
const pickerPath = path.join(root, 'picker.js')
|
|
20
|
+
const oldNoUserSite = process.env.PYTHONNOUSERSITE
|
|
21
|
+
const oldPythonPath = process.env.PYTHONPATH
|
|
22
|
+
await fs.writeFile(pickerPath, `
|
|
23
|
+
let input = ''
|
|
24
|
+
process.stdin.setEncoding('utf8')
|
|
25
|
+
process.stdin.on('data', (chunk) => { input += chunk })
|
|
26
|
+
process.stdin.on('end', () => {
|
|
27
|
+
JSON.parse(input)
|
|
28
|
+
process.stdout.write(JSON.stringify({
|
|
29
|
+
paths: [process.env.PYTHONNOUSERSITE, process.env.PYTHONPATH || null]
|
|
30
|
+
}))
|
|
31
|
+
})
|
|
32
|
+
`)
|
|
33
|
+
|
|
34
|
+
process.env.PYTHONNOUSERSITE = '0'
|
|
35
|
+
process.env.PYTHONPATH = 'external-packages'
|
|
36
|
+
try {
|
|
37
|
+
const kernel = {
|
|
38
|
+
platform: 'linux',
|
|
39
|
+
path: (target) => {
|
|
40
|
+
if (target === 'bin/py/picker.py') return pickerPath
|
|
41
|
+
if (target === 'bin/miniforge/bin/python') return process.execPath
|
|
42
|
+
throw new Error(`Unexpected path: ${target}`)
|
|
43
|
+
},
|
|
44
|
+
}
|
|
45
|
+
const result = await Util.filepicker({ params: {} }, () => {}, kernel)
|
|
46
|
+
|
|
47
|
+
assert.deepEqual(result, {
|
|
48
|
+
paths: ['1', null],
|
|
49
|
+
})
|
|
50
|
+
} finally {
|
|
51
|
+
restoreEnv('PYTHONNOUSERSITE', oldNoUserSite)
|
|
52
|
+
restoreEnv('PYTHONPATH', oldPythonPath)
|
|
53
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
54
|
+
}
|
|
55
|
+
})
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
const assert = require("node:assert/strict")
|
|
2
|
+
const fs = require("node:fs")
|
|
3
|
+
const path = require("node:path")
|
|
4
|
+
const test = require("node:test")
|
|
5
|
+
|
|
6
|
+
const grok = require("../system/plugin/grok/pinokio")
|
|
7
|
+
const grokAuto = require("../system/plugin/grok-auto/pinokio")
|
|
8
|
+
|
|
9
|
+
test("Grok Build exposes a minimal npx-backed terminal plugin", () => {
|
|
10
|
+
assert.equal(grok.title, "Grok Build")
|
|
11
|
+
assert.equal(grok.icon, "grok.png")
|
|
12
|
+
assert.equal(grok.link, "https://github.com/xai-org/grok-build")
|
|
13
|
+
assert.equal(fs.existsSync(path.join(__dirname, "..", "system", "plugin", "grok", grok.icon)), true)
|
|
14
|
+
|
|
15
|
+
for (const action of ["install", "update", "uninstall", "installed"]) {
|
|
16
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grok, action), false)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
assert.equal(grok.run.length, 2)
|
|
20
|
+
for (const step of grok.run) {
|
|
21
|
+
assert.equal(step.id, "run")
|
|
22
|
+
assert.equal(step.method, "shell.run")
|
|
23
|
+
assert.deepEqual(step.params.message, {
|
|
24
|
+
_: [
|
|
25
|
+
"npx",
|
|
26
|
+
"-y",
|
|
27
|
+
"@xai-official/grok@latest",
|
|
28
|
+
"{{args.prompt || undefined}}"
|
|
29
|
+
]
|
|
30
|
+
})
|
|
31
|
+
assert.equal(step.params.path, "{{args.cwd}}")
|
|
32
|
+
assert.equal(step.params.input, true)
|
|
33
|
+
assert.equal(step.params.buffer, 1024)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
assert.match(grok.run[0].when, /win32/)
|
|
37
|
+
assert.match(grok.run[0].params.shell, /bash\.exe/)
|
|
38
|
+
assert.equal(grok.run[0].params.conda.skip, true)
|
|
39
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grok.run[1].params, "shell"), false)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
test("Grok Build Auto shares the base wrapper and enables always-approve", () => {
|
|
43
|
+
assert.equal(grokAuto.title, "Grok Build Auto")
|
|
44
|
+
assert.equal(grokAuto.icon, grok.icon)
|
|
45
|
+
assert.equal(grokAuto.link, grok.link)
|
|
46
|
+
assert.match(grokAuto.description, /automatically approved/)
|
|
47
|
+
assert.equal(fs.existsSync(path.join(__dirname, "..", "system", "plugin", "grok-auto", grokAuto.icon)), true)
|
|
48
|
+
|
|
49
|
+
for (const action of ["install", "update", "uninstall", "installed"]) {
|
|
50
|
+
assert.equal(Object.prototype.hasOwnProperty.call(grokAuto, action), false)
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
assert.equal(grokAuto.run.length, grok.run.length)
|
|
54
|
+
for (let index = 0; index < grok.run.length; index += 1) {
|
|
55
|
+
const baseStep = grok.run[index]
|
|
56
|
+
const autoStep = grokAuto.run[index]
|
|
57
|
+
|
|
58
|
+
assert.equal(autoStep.when, baseStep.when)
|
|
59
|
+
assert.equal(autoStep.id, baseStep.id)
|
|
60
|
+
assert.equal(autoStep.method, baseStep.method)
|
|
61
|
+
assert.equal(autoStep.params.path, baseStep.params.path)
|
|
62
|
+
assert.equal(autoStep.params.input, baseStep.params.input)
|
|
63
|
+
assert.equal(autoStep.params.buffer, baseStep.params.buffer)
|
|
64
|
+
assert.equal(autoStep.params.shell, baseStep.params.shell)
|
|
65
|
+
assert.deepEqual(autoStep.params.conda, baseStep.params.conda)
|
|
66
|
+
assert.deepEqual(autoStep.params.message, {
|
|
67
|
+
_: [
|
|
68
|
+
"npx",
|
|
69
|
+
"-y",
|
|
70
|
+
"@xai-official/grok@latest",
|
|
71
|
+
"--always-approve",
|
|
72
|
+
"{{args.prompt || undefined}}"
|
|
73
|
+
]
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
assert.equal(grok.run.every((step) => !step.params.message._.includes("--always-approve")), true)
|
|
78
|
+
})
|
|
@@ -79,6 +79,41 @@ test('Hugging Face connect preserves configured HF paths and ignores ambient HF_
|
|
|
79
79
|
}
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
+
test('Hugging Face connect isolates its managed Python from external packages', async () => {
|
|
83
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-python-env-'))
|
|
84
|
+
const childEnvPath = path.join(root, 'child-env.json')
|
|
85
|
+
await fs.writeFile(
|
|
86
|
+
path.join(root, 'ENVIRONMENT'),
|
|
87
|
+
'PYTHONNOUSERSITE=0\nPYTHONPATH=system-packages\n'
|
|
88
|
+
)
|
|
89
|
+
try {
|
|
90
|
+
const provider = new HuggingfaceConnect(createKernel(root), {})
|
|
91
|
+
provider.hfPath = () => process.execPath
|
|
92
|
+
const { env } = await provider.runHf([
|
|
93
|
+
'-e',
|
|
94
|
+
'require("node:fs").writeFileSync(process.argv[1], JSON.stringify({ pythonNoUserSite: process.env.PYTHONNOUSERSITE, pythonPath: process.env.PYTHONPATH || null }))',
|
|
95
|
+
childEnvPath
|
|
96
|
+
], {}, {
|
|
97
|
+
env: {
|
|
98
|
+
PythonNoUserSite: '0',
|
|
99
|
+
PythonPath: 'command-packages'
|
|
100
|
+
}
|
|
101
|
+
})
|
|
102
|
+
const childEnv = JSON.parse(await fs.readFile(childEnvPath, 'utf8'))
|
|
103
|
+
|
|
104
|
+
assert.equal(env.PYTHONNOUSERSITE, '1')
|
|
105
|
+
assert.equal(env.PYTHONPATH, undefined)
|
|
106
|
+
assert.equal(env.PythonNoUserSite, undefined)
|
|
107
|
+
assert.equal(env.PythonPath, undefined)
|
|
108
|
+
assert.deepEqual(childEnv, {
|
|
109
|
+
pythonNoUserSite: '1',
|
|
110
|
+
pythonPath: null
|
|
111
|
+
})
|
|
112
|
+
} finally {
|
|
113
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
114
|
+
}
|
|
115
|
+
})
|
|
116
|
+
|
|
82
117
|
test('Hugging Face connect uses the app token path for its managed CLI', async () => {
|
|
83
118
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-app-env-'))
|
|
84
119
|
const appDir = path.join(root, 'api', 'demo')
|
|
@@ -18,6 +18,14 @@ test("resolveLauncherPluginSelection returns app-dev plugin query paths", () =>
|
|
|
18
18
|
PluginSources.resolveLauncherPluginSelection("codex-auto"),
|
|
19
19
|
"/plugin/codex-auto/pinokio.js"
|
|
20
20
|
)
|
|
21
|
+
assert.strictEqual(
|
|
22
|
+
PluginSources.resolveLauncherPluginSelection("grok"),
|
|
23
|
+
"/pinokio/run/plugin/grok/pinokio.js"
|
|
24
|
+
)
|
|
25
|
+
assert.strictEqual(
|
|
26
|
+
PluginSources.resolveLauncherPluginSelection("grok-build"),
|
|
27
|
+
"/pinokio/run/plugin/grok/pinokio.js"
|
|
28
|
+
)
|
|
21
29
|
assert.strictEqual(
|
|
22
30
|
PluginSources.resolveLauncherPluginSelection("plugin/local-tool"),
|
|
23
31
|
"/plugin/local-tool/pinokio.js"
|
|
@@ -208,10 +216,20 @@ module.exports = {
|
|
|
208
216
|
const systemPlugins = menu.filter((item) => item.source === "system")
|
|
209
217
|
const localPlugins = menu.filter((item) => item.source === "local")
|
|
210
218
|
|
|
211
|
-
assert.strictEqual(systemPlugins.length,
|
|
219
|
+
assert.strictEqual(systemPlugins.length, 14)
|
|
212
220
|
assert.ok(systemPlugins.every((item) => item.system === true))
|
|
213
221
|
assert.ok(systemPlugins.every((item) => item.href.startsWith("/pinokio/run/plugin/")))
|
|
214
222
|
assert.ok(systemPlugins.every((item) => item.image.startsWith("/pinokio/asset/plugin/")))
|
|
223
|
+
assert.ok(systemPlugins.some((item) => (
|
|
224
|
+
item.title === "Grok Build" &&
|
|
225
|
+
item.href === "/pinokio/run/plugin/grok/pinokio.js" &&
|
|
226
|
+
item.image === "/pinokio/asset/plugin/grok/grok.png"
|
|
227
|
+
)))
|
|
228
|
+
assert.ok(systemPlugins.some((item) => (
|
|
229
|
+
item.title === "Grok Build Auto" &&
|
|
230
|
+
item.href === "/pinokio/run/plugin/grok-auto/pinokio.js" &&
|
|
231
|
+
item.image === "/pinokio/asset/plugin/grok-auto/grok.png"
|
|
232
|
+
)))
|
|
215
233
|
|
|
216
234
|
assert.deepStrictEqual(localPlugins.map((item) => item.title), ["Local Tool"])
|
|
217
235
|
assert.strictEqual(localPlugins[0].href, "/run/plugin/local-tool/pinokio.js")
|