pinokiod 7.5.13 → 7.5.15
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/gpu/amd.js +30 -43
- package/kernel/gpu/amd_gfx_targets.json +67 -0
- package/kernel/gpu/nvidia.js +90 -4
- package/kernel/index.js +4 -3
- package/kernel/sysinfo.js +16 -25
- package/package.json +2 -1
- package/script/update-amd-gfx-targets.js +119 -0
- package/server/lib/app_log_report.js +1 -2
- package/server/public/logs.js +230 -30
- package/server/public/style.css +28 -12
- package/server/views/index.ejs +7 -0
- package/test/amd-gpu-target.test.js +169 -0
- package/test/gpu-target-template-variable.test.js +69 -0
- package/test/logs-ask-ai.test.js +65 -10
- package/test/nvidia-gpu-target.test.js +98 -0
- package/test/sysinfo-gpu-driver.test.js +134 -1
- package/kernel/gpu/apple.js +0 -8
- package/kernel/gpu/intel.js +0 -47
- package/release_notes/8.0.0/assets/app-ask-ai-drawer.png +0 -0
- package/release_notes/8.0.0/assets/app-detail-header-actions.png +0 -0
- package/release_notes/8.0.0/assets/autolaunch-dependencies.png +0 -0
- package/release_notes/8.0.0/assets/connect-huggingface-login.png +0 -0
- package/release_notes/8.0.0/assets/home-app-actions.png +0 -0
- package/release_notes/8.0.0/assets/home-sidebar-apps.png +0 -0
- package/release_notes/8.0.0/assets/logs-terminal-reporting.png +0 -0
- package/release_notes/8.0.0/assets/mobile-home-footer-nav.png +0 -0
- package/release_notes/8.0.0/assets/network-phone-access.png +0 -0
- package/release_notes/8.0.0/assets/plugins-agent-tools.png +0 -0
- package/release_notes/8.0.0/assets/skills-management.png +0 -0
- package/release_notes/8.0.0/assets/tools-runtime-management.png +0 -0
- package/release_notes/8.0.0/ui.md +0 -91
|
@@ -0,0 +1,69 @@
|
|
|
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 Kernel = require('../kernel')
|
|
8
|
+
|
|
9
|
+
test('Kernel propagates sysinfo gpu_target to template state and public info', async () => {
|
|
10
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-gpu-target-info-'))
|
|
11
|
+
const apiRoot = path.join(root, 'api')
|
|
12
|
+
await fs.mkdir(apiRoot, { recursive: true })
|
|
13
|
+
|
|
14
|
+
const templateUpdates = []
|
|
15
|
+
const kernel = {
|
|
16
|
+
version: 'test',
|
|
17
|
+
platform: 'linux',
|
|
18
|
+
arch: 'x64',
|
|
19
|
+
homedir: root,
|
|
20
|
+
shell: { shells: [] },
|
|
21
|
+
vars: {},
|
|
22
|
+
memory: {
|
|
23
|
+
local: {},
|
|
24
|
+
global: {},
|
|
25
|
+
key: {},
|
|
26
|
+
rpc: {},
|
|
27
|
+
input: {},
|
|
28
|
+
args: {}
|
|
29
|
+
},
|
|
30
|
+
procs: {},
|
|
31
|
+
api: {
|
|
32
|
+
running: {},
|
|
33
|
+
proxies: {},
|
|
34
|
+
userdir: apiRoot,
|
|
35
|
+
meta: async () => null
|
|
36
|
+
},
|
|
37
|
+
bin: { installed: {} },
|
|
38
|
+
template: {
|
|
39
|
+
update: (info) => templateUpdates.push(info)
|
|
40
|
+
},
|
|
41
|
+
sys: {
|
|
42
|
+
info: {
|
|
43
|
+
gpu: 'amd',
|
|
44
|
+
gpu_model: 'amd radeon rx 6800 xt',
|
|
45
|
+
gpu_driver: '31.0.1',
|
|
46
|
+
gpu_target: 'gfx1030',
|
|
47
|
+
gpus: [],
|
|
48
|
+
vram: 16,
|
|
49
|
+
ram: 64
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
path: (...parts) => path.join(root, ...parts),
|
|
53
|
+
dns: async () => {}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
await Kernel.prototype.update_sysinfo.call(kernel)
|
|
58
|
+
await Kernel.prototype.getInfo.call(kernel, false)
|
|
59
|
+
|
|
60
|
+
assert.equal(kernel.gpu, 'amd')
|
|
61
|
+
assert.equal(kernel.gpu_model, 'amd radeon rx 6800 xt')
|
|
62
|
+
assert.equal(kernel.gpu_target, 'gfx1030')
|
|
63
|
+
assert.equal(templateUpdates.length, 1)
|
|
64
|
+
assert.equal(templateUpdates[0].gpu_target, 'gfx1030')
|
|
65
|
+
assert.equal(kernel.i.gpu_target, 'gfx1030')
|
|
66
|
+
} finally {
|
|
67
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
68
|
+
}
|
|
69
|
+
})
|
package/test/logs-ask-ai.test.js
CHANGED
|
@@ -37,14 +37,24 @@ function createReportFixture() {
|
|
|
37
37
|
|
|
38
38
|
function createPluginMenuFixture() {
|
|
39
39
|
return {
|
|
40
|
-
menu: [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
menu: [
|
|
41
|
+
{
|
|
42
|
+
title: "Claude Desktop",
|
|
43
|
+
href: "/pinokio/run/plugin/claude-desktop/pinokio.js",
|
|
44
|
+
image: "/pinokio/asset/plugin/claude-desktop/icon.png",
|
|
45
|
+
category: "ide",
|
|
46
|
+
categoryTitle: "Desktop Plugin",
|
|
47
|
+
pluginPath: "/pinokio/run/plugin/claude-desktop/pinokio.js"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
title: "OpenAI Codex Auto",
|
|
51
|
+
href: "/pinokio/run/plugin/codex-auto/pinokio.js",
|
|
52
|
+
image: "/pinokio/asset/plugin/codex-auto/openai.webp",
|
|
53
|
+
category: "cli",
|
|
54
|
+
categoryTitle: "Terminal Plugin",
|
|
55
|
+
pluginPath: "/pinokio/run/plugin/codex-auto/pinokio.js"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
48
58
|
}
|
|
49
59
|
}
|
|
50
60
|
|
|
@@ -165,14 +175,59 @@ test("log Ask AI modal enables Run after assigning the default prompt", async ()
|
|
|
165
175
|
|
|
166
176
|
await waitFor(() => document.querySelector(".logs-ask-ai-launcher:not([hidden])"), "Ask AI modal")
|
|
167
177
|
const textarea = document.querySelector(".logs-ask-ai-launcher-textarea")
|
|
168
|
-
const
|
|
178
|
+
const trigger = document.querySelector(".logs-ask-ai-tool-trigger")
|
|
179
|
+
const selectedOption = document.querySelector(".logs-ask-ai-tool-sheet-body .universal-launcher-tool.selected")
|
|
169
180
|
const runButton = document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary")
|
|
170
181
|
|
|
171
182
|
assert.match(textarea.value, /Investigate what went wrong/)
|
|
172
|
-
assert.equal(
|
|
183
|
+
assert.equal(document.querySelector(".logs-ask-ai-tool-select"), null)
|
|
184
|
+
assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-label").textContent, "OpenAI Codex Auto")
|
|
185
|
+
assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-meta").textContent, "Terminal")
|
|
186
|
+
assert.equal(selectedOption.querySelector(".universal-launcher-tool-label").textContent, "OpenAI Codex Auto")
|
|
173
187
|
assert.equal(runButton.disabled, false)
|
|
174
188
|
})
|
|
175
189
|
|
|
190
|
+
test("log Ask AI modal uses custom plugin sheet and closes it before the modal", async () => {
|
|
191
|
+
const { dom } = await createLogsDom()
|
|
192
|
+
const { window } = dom
|
|
193
|
+
const { document } = window
|
|
194
|
+
|
|
195
|
+
document.getElementById("logs-ask-ai").click()
|
|
196
|
+
await waitFor(() => document.querySelector(".logs-ask-ai-launcher:not([hidden])"), "Ask AI modal")
|
|
197
|
+
|
|
198
|
+
const trigger = document.querySelector(".logs-ask-ai-tool-trigger")
|
|
199
|
+
const sheet = document.querySelector(".logs-ask-ai-tool-sheet-layer")
|
|
200
|
+
assert.equal(sheet.hidden, true)
|
|
201
|
+
assert.equal(document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary").disabled, true)
|
|
202
|
+
|
|
203
|
+
trigger.click()
|
|
204
|
+
assert.equal(sheet.hidden, false)
|
|
205
|
+
assert.equal(trigger.getAttribute("aria-expanded"), "true")
|
|
206
|
+
assert.deepEqual(
|
|
207
|
+
Array.from(document.querySelectorAll(".logs-ask-ai-tool-sheet-body .universal-launcher-tool-group-title")).map((node) => node.textContent),
|
|
208
|
+
["Terminal", "Desktop"]
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
const claudeOption = Array.from(document.querySelectorAll(".logs-ask-ai-tool-sheet-body .universal-launcher-tool"))
|
|
212
|
+
.find((option) => option.querySelector(".universal-launcher-tool-label").textContent === "Claude Desktop")
|
|
213
|
+
assert.ok(claudeOption)
|
|
214
|
+
claudeOption.click()
|
|
215
|
+
|
|
216
|
+
assert.equal(sheet.hidden, true)
|
|
217
|
+
assert.equal(trigger.querySelector(".universal-launcher-tool-trigger-label").textContent, "Claude Desktop")
|
|
218
|
+
assert.equal(window.localStorage.getItem("pinokio.universalLauncher.tool"), "pinokio/run/plugin/claude-desktop")
|
|
219
|
+
assert.equal(document.querySelector(".logs-ask-ai-launcher .universal-launcher-button-primary").disabled, false)
|
|
220
|
+
|
|
221
|
+
trigger.click()
|
|
222
|
+
assert.equal(sheet.hidden, false)
|
|
223
|
+
document.querySelector(".logs-ask-ai-launcher").dispatchEvent(new window.KeyboardEvent("keydown", {
|
|
224
|
+
key: "Escape",
|
|
225
|
+
bubbles: true
|
|
226
|
+
}))
|
|
227
|
+
assert.equal(sheet.hidden, true)
|
|
228
|
+
assert.equal(document.querySelector(".logs-ask-ai-launcher").hidden, false)
|
|
229
|
+
})
|
|
230
|
+
|
|
176
231
|
test("log Ask AI launch calls a same-origin parent drawer before postMessage fallback", async () => {
|
|
177
232
|
const opened = []
|
|
178
233
|
const posted = []
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const childProcess = require('node:child_process')
|
|
3
|
+
const test = require('node:test')
|
|
4
|
+
|
|
5
|
+
const nvidia = require('../kernel/gpu/nvidia')
|
|
6
|
+
|
|
7
|
+
test('NVIDIA compute capability parser emits CUDA SM targets', () => {
|
|
8
|
+
assert.deepEqual(
|
|
9
|
+
nvidia.parse_nvidia_smi_compute_caps([
|
|
10
|
+
'00000000:01:00.0, 8.6',
|
|
11
|
+
'00000000:02:00.0, 8.9',
|
|
12
|
+
'00000000:03:00.0, 12.0',
|
|
13
|
+
'00000000:04:00.0, N/A'
|
|
14
|
+
].join('\n')),
|
|
15
|
+
[
|
|
16
|
+
{ pci_bus: '01:00.0', target: 'sm_86' },
|
|
17
|
+
{ pci_bus: '02:00.0', target: 'sm_89' },
|
|
18
|
+
{ pci_bus: '03:00.0', target: 'sm_120' }
|
|
19
|
+
]
|
|
20
|
+
)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('NVIDIA target selection matches the selected controller by PCI bus', () => {
|
|
24
|
+
const records = [
|
|
25
|
+
{ pci_bus: '01:00.0', target: 'sm_86' },
|
|
26
|
+
{ pci_bus: '02:00.0', target: 'sm_89' }
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
assert.equal(nvidia.select_cuda_sm_target({ pciBus: '00000000:02:00.0' }, records), 'sm_89')
|
|
30
|
+
assert.equal(nvidia.select_cuda_sm_target({ busAddress: '01:00.0' }, records), 'sm_86')
|
|
31
|
+
assert.equal(nvidia.select_cuda_sm_target({ pciBus: '03:00.0' }, records), null)
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test('NVIDIA target selection uses the only record when the controller has no PCI bus', () => {
|
|
35
|
+
assert.equal(
|
|
36
|
+
nvidia.select_cuda_sm_target({}, [{ pci_bus: '01:00.0', target: 'sm_86' }]),
|
|
37
|
+
'sm_86'
|
|
38
|
+
)
|
|
39
|
+
assert.equal(
|
|
40
|
+
nvidia.select_cuda_sm_target({}, [
|
|
41
|
+
{ pci_bus: '01:00.0', target: 'sm_86' },
|
|
42
|
+
{ pci_bus: '02:00.0', target: 'sm_89' }
|
|
43
|
+
]),
|
|
44
|
+
null
|
|
45
|
+
)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('NVIDIA nvidia-smi query failure resolves to no targets', async () => {
|
|
49
|
+
const records = await nvidia.query_cuda_sm_targets((_cmd, _args, _options, done) => {
|
|
50
|
+
done(new Error('missing nvidia-smi'))
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
assert.deepEqual(records, [])
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('NVIDIA nvidia-smi query uses compute capability command and parses stdout', async () => {
|
|
57
|
+
const calls = []
|
|
58
|
+
const records = await nvidia.query_cuda_sm_targets((cmd, args, options, done) => {
|
|
59
|
+
calls.push({ cmd, args, options })
|
|
60
|
+
done(null, [
|
|
61
|
+
'00000000:01:00.0, 8.6',
|
|
62
|
+
'00000000:02:00.0, 8.9'
|
|
63
|
+
].join('\n'))
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
assert.deepEqual(calls, [{
|
|
67
|
+
cmd: 'nvidia-smi',
|
|
68
|
+
args: [
|
|
69
|
+
'--query-gpu=pci.bus_id,compute_cap',
|
|
70
|
+
'--format=csv,noheader,nounits'
|
|
71
|
+
],
|
|
72
|
+
options: { windowsHide: true, timeout: 5000 }
|
|
73
|
+
}])
|
|
74
|
+
assert.deepEqual(records, [
|
|
75
|
+
{ pci_bus: '01:00.0', target: 'sm_86' },
|
|
76
|
+
{ pci_bus: '02:00.0', target: 'sm_89' }
|
|
77
|
+
])
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('NVIDIA gpu_target caches the nvidia-smi compute capability query', async (t) => {
|
|
81
|
+
const modulePath = require.resolve('../kernel/gpu/nvidia')
|
|
82
|
+
delete require.cache[modulePath]
|
|
83
|
+
|
|
84
|
+
let calls = 0
|
|
85
|
+
t.mock.method(childProcess, 'execFile', (_cmd, _args, _options, done) => {
|
|
86
|
+
calls += 1
|
|
87
|
+
done(null, '00000000:01:00.0, 8.6\n')
|
|
88
|
+
})
|
|
89
|
+
t.after(() => {
|
|
90
|
+
delete require.cache[modulePath]
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const freshNvidia = require('../kernel/gpu/nvidia')
|
|
94
|
+
|
|
95
|
+
assert.equal(await freshNvidia.resolve_cuda_sm_target({ pciBus: '00000000:01:00.0' }), 'sm_86')
|
|
96
|
+
assert.equal(await freshNvidia.resolve_cuda_sm_target({ pciBus: '00000000:01:00.0' }), 'sm_86')
|
|
97
|
+
assert.equal(calls, 1)
|
|
98
|
+
})
|
|
@@ -2,6 +2,7 @@ const assert = require('node:assert/strict')
|
|
|
2
2
|
const test = require('node:test')
|
|
3
3
|
|
|
4
4
|
const system = require('systeminformation')
|
|
5
|
+
const nvidia = require('../kernel/gpu/nvidia')
|
|
5
6
|
const Sysinfo = require('../kernel/sysinfo')
|
|
6
7
|
|
|
7
8
|
test('GPU sysinfo exposes per-controller drivers and primary NVIDIA driver', async (t) => {
|
|
@@ -17,11 +18,16 @@ test('GPU sysinfo exposes per-controller drivers and primary NVIDIA driver', asy
|
|
|
17
18
|
vendor: 'NVIDIA',
|
|
18
19
|
model: 'NVIDIA RTX A4500',
|
|
19
20
|
vram: 20480,
|
|
20
|
-
driverVersion: '565.90'
|
|
21
|
+
driverVersion: '565.90',
|
|
22
|
+
pciBus: '00000000:02:00.0'
|
|
21
23
|
}
|
|
22
24
|
],
|
|
23
25
|
displays: []
|
|
24
26
|
}))
|
|
27
|
+
t.mock.method(nvidia, 'resolve_cuda_sm_target', async (controller) => {
|
|
28
|
+
assert.equal(controller.pciBus, '00000000:02:00.0')
|
|
29
|
+
return 'sm_86'
|
|
30
|
+
})
|
|
25
31
|
|
|
26
32
|
const sys = new Sysinfo()
|
|
27
33
|
sys.info = {}
|
|
@@ -31,6 +37,8 @@ test('GPU sysinfo exposes per-controller drivers and primary NVIDIA driver', asy
|
|
|
31
37
|
assert.equal(sys.info.gpu, 'nvidia')
|
|
32
38
|
assert.equal(sys.info.gpu_model, 'nvidia rtx a4500')
|
|
33
39
|
assert.equal(sys.info.gpu_driver, '565.90')
|
|
40
|
+
assert.equal(sys.info.gpu_target, 'sm_86')
|
|
41
|
+
assert.equal(sys.info.vram, 20)
|
|
34
42
|
assert.deepEqual(sys.info.gpus, [
|
|
35
43
|
{
|
|
36
44
|
name: 'intel',
|
|
@@ -72,6 +80,8 @@ test('GPU sysinfo uses selected highest-VRAM AMD controller for gpu_driver', asy
|
|
|
72
80
|
assert.equal(sys.info.gpu, 'amd')
|
|
73
81
|
assert.equal(sys.info.gpu_model, 'amd radeon rx 7900 xtx')
|
|
74
82
|
assert.equal(sys.info.gpu_driver, '31.0.2')
|
|
83
|
+
assert.equal(sys.info.gpu_target, 'gfx1100')
|
|
84
|
+
assert.equal(sys.info.vram, 24)
|
|
75
85
|
assert.deepEqual(sys.info.gpus, [
|
|
76
86
|
{
|
|
77
87
|
name: 'advanced micro devices',
|
|
@@ -85,3 +95,126 @@ test('GPU sysinfo uses selected highest-VRAM AMD controller for gpu_driver', asy
|
|
|
85
95
|
}
|
|
86
96
|
])
|
|
87
97
|
})
|
|
98
|
+
|
|
99
|
+
test('GPU sysinfo leaves gpu_target null for Intel targets', async (t) => {
|
|
100
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
101
|
+
controllers: [
|
|
102
|
+
{
|
|
103
|
+
vendor: 'Intel',
|
|
104
|
+
model: 'Intel Arc A770',
|
|
105
|
+
vram: 16384,
|
|
106
|
+
driverVersion: '31.0.101.5590'
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
displays: []
|
|
110
|
+
}))
|
|
111
|
+
|
|
112
|
+
const sys = new Sysinfo()
|
|
113
|
+
sys.info = {}
|
|
114
|
+
|
|
115
|
+
await sys.gpus()
|
|
116
|
+
|
|
117
|
+
assert.equal(sys.info.gpu, 'intel')
|
|
118
|
+
assert.equal(sys.info.gpu_model, 'intel arc a770')
|
|
119
|
+
assert.equal(sys.info.gpu_driver, '31.0.101.5590')
|
|
120
|
+
assert.equal(sys.info.gpu_target, null)
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
test('GPU sysinfo resolves gpu_target for the selected NVIDIA controller when AMD is also present', async (t) => {
|
|
124
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
125
|
+
controllers: [
|
|
126
|
+
{
|
|
127
|
+
vendor: 'AMD',
|
|
128
|
+
model: 'AMD Radeon RX 7900 XTX',
|
|
129
|
+
vram: 24576,
|
|
130
|
+
driverVersion: '31.0.2'
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
vendor: 'NVIDIA',
|
|
134
|
+
model: 'NVIDIA GeForce RTX 4090',
|
|
135
|
+
vram: 24576,
|
|
136
|
+
driverVersion: '565.90',
|
|
137
|
+
pciBus: '00000000:02:00.0'
|
|
138
|
+
}
|
|
139
|
+
],
|
|
140
|
+
displays: []
|
|
141
|
+
}))
|
|
142
|
+
t.mock.method(nvidia, 'resolve_cuda_sm_target', async (controller) => {
|
|
143
|
+
assert.equal(controller.model, 'NVIDIA GeForce RTX 4090')
|
|
144
|
+
return 'sm_89'
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
const sys = new Sysinfo()
|
|
148
|
+
sys.info = {}
|
|
149
|
+
|
|
150
|
+
await sys.gpus()
|
|
151
|
+
|
|
152
|
+
assert.equal(sys.info.gpu, 'nvidia')
|
|
153
|
+
assert.equal(sys.info.gpu_model, 'nvidia geforce rtx 4090')
|
|
154
|
+
assert.equal(sys.info.gpu_target, 'sm_89')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
test('GPU sysinfo leaves gpu_target null for Apple targets', async (t) => {
|
|
158
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
159
|
+
controllers: [
|
|
160
|
+
{
|
|
161
|
+
vendor: 'Apple',
|
|
162
|
+
model: 'Apple M3',
|
|
163
|
+
vram: 0,
|
|
164
|
+
driverVersion: null
|
|
165
|
+
}
|
|
166
|
+
],
|
|
167
|
+
displays: []
|
|
168
|
+
}))
|
|
169
|
+
|
|
170
|
+
const sys = new Sysinfo()
|
|
171
|
+
sys.info = {}
|
|
172
|
+
|
|
173
|
+
await sys.gpus()
|
|
174
|
+
|
|
175
|
+
assert.equal(sys.info.gpu, 'apple')
|
|
176
|
+
assert.equal(sys.info.gpu_model, 'apple m3')
|
|
177
|
+
assert.equal(sys.info.gpu_target, null)
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
test('GPU sysinfo leaves gpu_target null for unknown GPU vendors', async (t) => {
|
|
181
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
182
|
+
controllers: [
|
|
183
|
+
{
|
|
184
|
+
vendor: 'Qualcomm',
|
|
185
|
+
model: 'Adreno X1',
|
|
186
|
+
vram: 16384,
|
|
187
|
+
driverVersion: '1.2.3'
|
|
188
|
+
}
|
|
189
|
+
],
|
|
190
|
+
displays: []
|
|
191
|
+
}))
|
|
192
|
+
|
|
193
|
+
const sys = new Sysinfo()
|
|
194
|
+
sys.info = {}
|
|
195
|
+
|
|
196
|
+
await sys.gpus()
|
|
197
|
+
|
|
198
|
+
assert.equal(sys.info.gpu, 'qualcomm')
|
|
199
|
+
assert.equal(sys.info.gpu_model, 'adreno x1')
|
|
200
|
+
assert.equal(sys.info.gpu_driver, '1.2.3')
|
|
201
|
+
assert.equal(sys.info.gpu_target, null)
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('GPU sysinfo leaves gpu_target null when no GPU controller is detected', async (t) => {
|
|
205
|
+
t.mock.method(system, 'graphics', async () => ({
|
|
206
|
+
controllers: [],
|
|
207
|
+
displays: []
|
|
208
|
+
}))
|
|
209
|
+
|
|
210
|
+
const sys = new Sysinfo()
|
|
211
|
+
sys.info = {}
|
|
212
|
+
|
|
213
|
+
await sys.gpus()
|
|
214
|
+
|
|
215
|
+
assert.equal(sys.info.gpu, 'none')
|
|
216
|
+
assert.equal(sys.info.gpu_model, undefined)
|
|
217
|
+
assert.equal(sys.info.gpu_target, null)
|
|
218
|
+
assert.deepEqual(sys.info.gpus, [])
|
|
219
|
+
assert.equal(sys.info.vram, 0)
|
|
220
|
+
})
|
package/kernel/gpu/apple.js
DELETED
package/kernel/gpu/intel.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
const { normalize_model } = require("./common")
|
|
2
|
-
|
|
3
|
-
// Resolve CPU brand lazily so clear GPU model matches do not trigger CPU probing.
|
|
4
|
-
const resolve_cpu_brand = async (cpu_brand) => {
|
|
5
|
-
if (typeof cpu_brand === "function") {
|
|
6
|
-
return await cpu_brand()
|
|
7
|
-
} else {
|
|
8
|
-
return cpu_brand
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
// Match Intel GPU model names that Pinokio should route to PyTorch XPU wheels.
|
|
13
|
-
const matches_xpu_torch_model = (model) => {
|
|
14
|
-
let normalized = normalize_model(model)
|
|
15
|
-
return (
|
|
16
|
-
// Intel Arc A-Series / B-Series, Arc Pro, and Core Ultra iGPUs when the
|
|
17
|
-
// GPU model itself includes Arc.
|
|
18
|
-
/\barc\b/.test(normalized) ||
|
|
19
|
-
// Intel Data Center GPU Max Series. Do not match generic Data Center GPU
|
|
20
|
-
// names because Data Center GPU Flex is not in current PyTorch XPU docs.
|
|
21
|
-
/\b(data center gpu max|gpu max|ponte vecchio)\b/.test(normalized)
|
|
22
|
-
)
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Detect uninformative Intel iGPU names that need CPU-brand fallback matching.
|
|
26
|
-
const is_generic_gpu_model = (model) => {
|
|
27
|
-
let normalized = normalize_model(model)
|
|
28
|
-
return /^(intel )?graphics$/.test(normalized)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// Decide Intel XPU install intent, using CPU brand only for generic iGPU names.
|
|
32
|
-
const supports_torch_backend = async (model, cpu_brand) => {
|
|
33
|
-
if (matches_xpu_torch_model(model)) {
|
|
34
|
-
return true
|
|
35
|
-
}
|
|
36
|
-
if (!is_generic_gpu_model(model)) {
|
|
37
|
-
return false
|
|
38
|
-
}
|
|
39
|
-
let brand = normalize_model(await resolve_cpu_brand(cpu_brand))
|
|
40
|
-
return /\bcore\s*ultra\b/.test(brand)
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
module.exports = {
|
|
44
|
-
is_generic_gpu_model,
|
|
45
|
-
matches_xpu_torch_model,
|
|
46
|
-
supports_torch_backend
|
|
47
|
-
}
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
# Pinokio 8.0.0 UI Updates
|
|
2
|
-
|
|
3
|
-
This release brings Pinokio's main surfaces into a quieter, more consistent workspace: app discovery, app launch controls, logs, tools, plugins, skills, autolaunch, setup, and mobile navigation now share the same visual language and action patterns.
|
|
4
|
-
|
|
5
|
-
The notes below are grouped by what users will notice, not by commit history. Screenshots were captured from the live local UI after comparing `de83785` to the current `HEAD`.
|
|
6
|
-
|
|
7
|
-
## Cleaner App Discovery
|
|
8
|
-
|
|
9
|
-
Pinokio's home screen now behaves more like a compact app manager. The sidebar is unified with the rest of the product, the app list is easier to scan, search and sort stay prominent, and each app row exposes quick actions without forcing users into the full app page first.
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
The new action panel gives installed apps a focused command surface for common launch, install, update, reset, file, and dev actions. Related commands are grouped in one drawer, which reduces row clutter while keeping the next action close to the app.
|
|
14
|
-
|
|
15
|
-

|
|
16
|
-
|
|
17
|
-
## App Workspace And Assistance
|
|
18
|
-
|
|
19
|
-
App pages have been rebuilt around a more durable workspace shell. The page chrome, left command rail, right utility panels, app title area, status indicators, resource labels, and launch actions have been tightened so app-specific work feels connected to the rest of Pinokio.
|
|
20
|
-
|
|
21
|
-

|
|
22
|
-
|
|
23
|
-
Ask AI now opens as an in-app assistance drawer with searchable terminal and desktop agents. Instead of a generic help entry point, users can choose from bundled agents such as Codex, Claude Code, Qwen Code, Antigravity, Cursor, VS Code, and desktop integrations directly inside the app context.
|
|
24
|
-
|
|
25
|
-

|
|
26
|
-
|
|
27
|
-
## Plugins, Agents, And Skills
|
|
28
|
-
|
|
29
|
-
The Plugins page now separates terminal plugins from desktop plugins and shows install/manage affordances in a single list. This makes the new bundled native tools easier to understand: terminal agents launch inside Pinokio, while desktop tools launch externally.
|
|
30
|
-
|
|
31
|
-

|
|
32
|
-
|
|
33
|
-
Pinokio also has a new Skills management surface. Users can see where managed skills are stored, where they sync, whether each skill is valid, and whether it is currently enabled. The page also supports downloading skills from a Git URL.
|
|
34
|
-
|
|
35
|
-

|
|
36
|
-
|
|
37
|
-
## Startup And Launch Requirements
|
|
38
|
-
|
|
39
|
-
Autolaunch is now a first-class configuration page. Users can search installed apps, choose which launch script should run at startup, see how many apps are enabled, and toggle startup behavior without digging through app internals.
|
|
40
|
-
|
|
41
|
-

|
|
42
|
-
|
|
43
|
-
Setup and requirements screens now present blocked routes as a clear checklist. When a destination such as Local network needs a package update, Pinokio shows what is installed, what needs attention, and the action required before continuing.
|
|
44
|
-
|
|
45
|
-

|
|
46
|
-
|
|
47
|
-
## Logs, Diagnostics, And Issue Reporting
|
|
48
|
-
|
|
49
|
-
Logs have been redesigned around a file tree and live reader. Users can inspect logs from the machine, stream a selected file, clear the view, keep auto-scroll on, and generate a diagnostic zip from the same screen.
|
|
50
|
-
|
|
51
|
-

|
|
52
|
-
|
|
53
|
-
Behind that surface, the issue-reporting flow is more capable: recent logs can be summarized, redacted, copied, handed to Ask AI, or prepared as a registry draft. Those states require an app failure or report context, so they are not shown in this static screenshot set.
|
|
54
|
-
|
|
55
|
-
## Tools And Runtime Readiness
|
|
56
|
-
|
|
57
|
-
The Tools page now groups package bundles with readiness counts, per-bundle install state, and package-level status. Users can see which runtime bundles are ready, which need installation, and which package manager modules are present without reading terminal output first.
|
|
58
|
-
|
|
59
|
-

|
|
60
|
-
|
|
61
|
-
## Login And Connected Services
|
|
62
|
-
|
|
63
|
-
The Hugging Face login flow has been redesigned as a clear device-authorization page. It states the connection state, the credential provider, the reason to log in, and the next action without mixing auth details into unrelated setup UI.
|
|
64
|
-
|
|
65
|
-

|
|
66
|
-
|
|
67
|
-
GitHub and git-related modals also received layout cleanup: modal tabs, footers, repository status controls, checkpoint rows, and history/diff areas were tightened so longer repository workflows fit better in the available space.
|
|
68
|
-
|
|
69
|
-
## Mobile And Remote Access
|
|
70
|
-
|
|
71
|
-
Mobile layouts now prioritize the app list and move primary navigation to a bottom bar. Search, sort, app cards, favorites, and action buttons remain reachable on a phone-width viewport without the desktop sidebar competing for space.
|
|
72
|
-
|
|
73
|
-

|
|
74
|
-
|
|
75
|
-
Phone and LAN access also received targeted polish. QR and network access entry points are gated behind setup readiness, install terminals attach correctly over LAN, and the old mobile "Tap to connect" curtain has been replaced with a quieter first-interaction audio primer.
|
|
76
|
-
|
|
77
|
-
## Smaller Visual Polish
|
|
78
|
-
|
|
79
|
-
Several changes are intentionally broad but subtle:
|
|
80
|
-
|
|
81
|
-
- Shared sidebar styling now covers home, app, tools, plugins, skills, logs, settings, setup, network, and auth pages.
|
|
82
|
-
- App navigation is more responsive, especially where action labels previously wrapped or crowded the header.
|
|
83
|
-
- Screenshot/capture modals, footer status indicators, popovers, tabs, and terminal controls use flatter, tighter styling.
|
|
84
|
-
- Checkpoints rows, git history dialogs, network troubleshoot panels, and auth pages were normalized to match the newer page shell.
|
|
85
|
-
- Hidden or unused toolbar buttons were removed from the visible chrome so users see fewer inactive controls.
|
|
86
|
-
|
|
87
|
-
## Screenshot Coverage Notes
|
|
88
|
-
|
|
89
|
-
Captured screenshots cover the major visible themes: home discovery, app actions, app workspace chrome, Ask AI, plugins, skills, autolaunch, setup requirements, logs, tools, connected-service login, and mobile navigation.
|
|
90
|
-
|
|
91
|
-
Some UI states were not practical to screenshot without changing runtime state or forcing failures: failed-terminal fallback, report-draft creation, live dependency orchestration while an app is launching, completed peer-network routing, and git/checkpoint modal edge cases. Those changes are still included above because they are user-visible when the relevant workflow is active.
|