pinokiod 7.3.5 → 7.3.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/index.js +3 -2
- package/kernel/api/script/index.js +1 -0
- package/kernel/resource_usage/gpu.js +1078 -270
- package/kernel/resource_usage/index.js +9 -4
- package/package.json +2 -1
- package/server/index.js +14 -2
- package/server/public/nav.js +1 -1
- package/server/public/style.css +298 -191
- package/server/public/task-launcher.css +16 -20
- package/server/public/universal-launcher.css +0 -113
- package/server/public/universal-launcher.js +1 -1
- package/server/views/app.ejs +592 -298
- package/server/views/autolaunch.ejs +1 -1
- package/server/views/checkpoints.ejs +2 -6
- package/server/views/connect.ejs +1 -1
- package/server/views/explore.ejs +2 -1
- package/server/views/index.ejs +89 -60
- package/server/views/install.ejs +5 -7
- package/server/views/invalid_content.ejs +1 -1
- package/server/views/layout.ejs +8 -2
- package/server/views/logs.ejs +5 -27
- package/server/views/net.ejs +1 -1
- package/server/views/network.ejs +1 -1
- package/server/views/partials/fs_status.ejs +0 -8
- package/server/views/partials/main_sidebar.ejs +108 -44
- package/server/views/plugin_detail.ejs +1 -1
- package/server/views/plugins.ejs +1 -28
- package/server/views/screenshots.ejs +1 -1
- package/server/views/settings.ejs +2 -1
- package/server/views/setup.ejs +15 -1
- package/server/views/skills.ejs +1 -1
- package/server/views/task_builder.ejs +1 -1
- package/server/views/task_install.ejs +1 -1
- package/server/views/task_launch.ejs +1 -1
- package/server/views/task_list.ejs +1 -1
- package/server/views/tools.ejs +1 -1
- package/test/resource-usage-gpu.test.js +320 -70
- package/test/script-api.test.js +90 -0
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
const assert = require("node:assert/strict")
|
|
2
|
+
const fs = require("node:fs")
|
|
3
|
+
const os = require("node:os")
|
|
4
|
+
const path = require("node:path")
|
|
2
5
|
const test = require("node:test")
|
|
3
6
|
|
|
4
7
|
const {
|
|
5
8
|
GpuSampler,
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
NvmlGpuMemoryClient,
|
|
10
|
+
WindowsPdhGpuMemoryClient,
|
|
11
|
+
collectLinuxDrmFdinfoProcesses,
|
|
12
|
+
decodeWindowsMultiSz,
|
|
13
|
+
extractPidFromWindowsGpuInstance,
|
|
14
|
+
isDedicatedDrmMemoryRegion,
|
|
15
|
+
parseLinuxDrmFdinfo
|
|
8
16
|
} = require("../kernel/resource_usage/gpu")
|
|
9
17
|
|
|
10
18
|
const MIB = 1024 * 1024
|
|
@@ -16,111 +24,353 @@ function gpuProcess(pid, bytes) {
|
|
|
16
24
|
}
|
|
17
25
|
}
|
|
18
26
|
|
|
19
|
-
test("
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
27
|
+
test("extractPidFromWindowsGpuInstance handles full PDH counter paths", () => {
|
|
28
|
+
assert.equal(extractPidFromWindowsGpuInstance("app_pid_3456_phys_0"), 3456)
|
|
29
|
+
assert.equal(extractPidFromWindowsGpuInstance("\\\\HOST\\GPU Process Memory(pid_1234_luid_0x00000000_phys_0)\\Dedicated Usage"), 1234)
|
|
30
|
+
assert.equal(extractPidFromWindowsGpuInstance("\\\\HOST\\GPU Process Memory(_total)\\Dedicated Usage"), null)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test("decodeWindowsMultiSz decodes double-null UTF-16 string lists", () => {
|
|
34
|
+
const text = "one\u0000two\u0000\u0000"
|
|
35
|
+
const buffer = Buffer.from(text, "utf16le")
|
|
36
|
+
|
|
37
|
+
assert.deepEqual(decodeWindowsMultiSz(buffer, text.length), ["one", "two"])
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test("Windows PDH binds formatted counter value with exported symbol name", () => {
|
|
41
|
+
const signatures = []
|
|
42
|
+
const fakeKoffi = {
|
|
43
|
+
struct: (name, fields) => ({ name, fields }),
|
|
44
|
+
load: (library) => {
|
|
45
|
+
assert.equal(library, "pdh.dll")
|
|
46
|
+
return {
|
|
47
|
+
func: (signature) => {
|
|
48
|
+
signatures.push(signature)
|
|
49
|
+
return () => 0
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const client = new WindowsPdhGpuMemoryClient({ koffi: fakeKoffi })
|
|
56
|
+
client.init()
|
|
57
|
+
|
|
58
|
+
assert(signatures.some((signature) => signature.includes("PdhGetFormattedCounterValue(")))
|
|
59
|
+
assert(!signatures.some((signature) => signature.includes("PdhGetFormattedCounterValueW(")))
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
test("parseLinuxDrmFdinfo counts dedicated DRM memory regions only", () => {
|
|
63
|
+
const amdgpu = parseLinuxDrmFdinfo([
|
|
64
|
+
"drm-driver:\tamdgpu",
|
|
65
|
+
"drm-pdev:\t0000:03:00.0",
|
|
66
|
+
"drm-client-id:\t17",
|
|
67
|
+
"drm-memory-vram:\t4 MiB",
|
|
68
|
+
"drm-memory-gtt:\t128 MiB"
|
|
69
|
+
].join("\n"))
|
|
70
|
+
|
|
71
|
+
const intel = parseLinuxDrmFdinfo([
|
|
72
|
+
"drm-driver:\ti915",
|
|
73
|
+
"drm-pdev:\t0000:00:02.0",
|
|
74
|
+
"drm-client-id:\t9",
|
|
75
|
+
"drm-resident-local0:\t8 MiB",
|
|
76
|
+
"drm-resident-system:\t64 MiB"
|
|
24
77
|
].join("\n"))
|
|
25
78
|
|
|
26
|
-
assert.equal(
|
|
27
|
-
assert.equal(
|
|
79
|
+
assert.equal(amdgpu.dedicatedBytes, 4 * MIB)
|
|
80
|
+
assert.equal(intel.dedicatedBytes, 8 * MIB)
|
|
81
|
+
assert.equal(isDedicatedDrmMemoryRegion("vram"), true)
|
|
82
|
+
assert.equal(isDedicatedDrmMemoryRegion("local0"), true)
|
|
83
|
+
assert.equal(isDedicatedDrmMemoryRegion("gtt"), false)
|
|
84
|
+
assert.equal(isDedicatedDrmMemoryRegion("system"), false)
|
|
28
85
|
})
|
|
29
86
|
|
|
30
|
-
test("
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
"
|
|
35
|
-
|
|
87
|
+
test("collectLinuxDrmFdinfoProcesses deduplicates DRM client fds", async () => {
|
|
88
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pinokio-drm-fdinfo-"))
|
|
89
|
+
try {
|
|
90
|
+
await fs.promises.mkdir(path.join(root, "1234", "fdinfo"), { recursive: true })
|
|
91
|
+
await fs.promises.mkdir(path.join(root, "5678", "fdinfo"), { recursive: true })
|
|
92
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "3"), [
|
|
93
|
+
"drm-driver:\tamdgpu",
|
|
94
|
+
"drm-pdev:\t0000:03:00.0",
|
|
95
|
+
"drm-client-id:\t17",
|
|
96
|
+
"drm-resident-vram:\t12 MiB"
|
|
97
|
+
].join("\n"))
|
|
98
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "4"), [
|
|
99
|
+
"drm-driver:\tamdgpu",
|
|
100
|
+
"drm-pdev:\t0000:03:00.0",
|
|
101
|
+
"drm-client-id:\t17",
|
|
102
|
+
"drm-resident-vram:\t11 MiB"
|
|
103
|
+
].join("\n"))
|
|
104
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "5"), [
|
|
105
|
+
"drm-driver:\tamdgpu",
|
|
106
|
+
"drm-pdev:\t0000:03:00.0",
|
|
107
|
+
"drm-client-id:\t18",
|
|
108
|
+
"drm-resident-vram:\t5 MiB"
|
|
109
|
+
].join("\n"))
|
|
110
|
+
await fs.promises.writeFile(path.join(root, "5678", "fdinfo", "9"), [
|
|
111
|
+
"drm-driver:\tamdgpu",
|
|
112
|
+
"drm-pdev:\t0000:03:00.0",
|
|
113
|
+
"drm-client-id:\t22",
|
|
114
|
+
"drm-resident-gtt:\t64 MiB"
|
|
115
|
+
].join("\n"))
|
|
36
116
|
|
|
37
|
-
|
|
38
|
-
|
|
117
|
+
const processes = await collectLinuxDrmFdinfoProcesses([1234, 5678], { procRoot: root })
|
|
118
|
+
|
|
119
|
+
assert.equal(processes.get(1234).usedGpuMemoryBytes, 17 * MIB)
|
|
120
|
+
assert.equal(processes.has(5678), false)
|
|
121
|
+
} finally {
|
|
122
|
+
await fs.promises.rm(root, { recursive: true, force: true })
|
|
123
|
+
}
|
|
39
124
|
})
|
|
40
125
|
|
|
41
|
-
test("GpuSampler uses Windows
|
|
42
|
-
const sampler = new GpuSampler({
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
provider: "windows-gpu-process-memory",
|
|
47
|
-
processes: new Map([
|
|
126
|
+
test("GpuSampler uses Windows PDH only on Windows", async () => {
|
|
127
|
+
const sampler = new GpuSampler({
|
|
128
|
+
platform: "win32",
|
|
129
|
+
windowsPdhClient: {
|
|
130
|
+
collect: () => new Map([
|
|
48
131
|
[1234, gpuProcess(1234, 500 * MIB)]
|
|
49
|
-
])
|
|
50
|
-
error: null
|
|
132
|
+
])
|
|
51
133
|
}
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
137
|
+
|
|
138
|
+
assert.equal(snapshot.available, true)
|
|
139
|
+
assert.deepEqual(snapshot.providers, ["windows-pdh"])
|
|
140
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 500 * MIB)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test("GpuSampler reports Windows VRAM unavailable when PDH fails", async () => {
|
|
144
|
+
const originalWarn = console.warn
|
|
145
|
+
console.warn = () => {}
|
|
146
|
+
|
|
147
|
+
try {
|
|
148
|
+
const sampler = new GpuSampler({
|
|
149
|
+
platform: "win32",
|
|
150
|
+
windowsPdhClient: {
|
|
151
|
+
collect: () => {
|
|
152
|
+
throw new Error("pdh unavailable")
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
158
|
+
|
|
159
|
+
assert.equal(snapshot.available, false)
|
|
160
|
+
assert.deepEqual(snapshot.providers, ["windows-pdh"])
|
|
161
|
+
assert.equal(snapshot.processes.size, 0)
|
|
162
|
+
assert.equal(snapshot.errors[0].provider, "windows-pdh")
|
|
163
|
+
} finally {
|
|
164
|
+
console.warn = originalWarn
|
|
52
165
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
error: null
|
|
61
|
-
}
|
|
166
|
+
})
|
|
167
|
+
|
|
168
|
+
test("GpuSampler logs provider failures once per backoff window", async () => {
|
|
169
|
+
const originalWarn = console.warn
|
|
170
|
+
const warnings = []
|
|
171
|
+
console.warn = (...args) => {
|
|
172
|
+
warnings.push(args)
|
|
62
173
|
}
|
|
63
|
-
sampler.collectAmd = async () => null
|
|
64
174
|
|
|
65
|
-
|
|
175
|
+
try {
|
|
176
|
+
const sampler = new GpuSampler({
|
|
177
|
+
platform: "win32",
|
|
178
|
+
windowsPdhClient: {
|
|
179
|
+
collect: () => {
|
|
180
|
+
const error = new Error("pdh unavailable")
|
|
181
|
+
error.code = "PDH_TEST"
|
|
182
|
+
throw error
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
})
|
|
66
186
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
187
|
+
await sampler.collect(new Set([1234]))
|
|
188
|
+
await sampler.collect(new Set([1234]))
|
|
189
|
+
|
|
190
|
+
assert.equal(warnings.length, 1)
|
|
191
|
+
assert.equal(warnings[0][0], "[resource-usage:gpu] provider failed")
|
|
192
|
+
assert.deepEqual(warnings[0][1], {
|
|
193
|
+
provider: "windows-pdh",
|
|
194
|
+
platform: "win32",
|
|
195
|
+
pid_count: 1,
|
|
196
|
+
error: "pdh unavailable",
|
|
197
|
+
code: "PDH_TEST"
|
|
198
|
+
})
|
|
199
|
+
} finally {
|
|
200
|
+
console.warn = originalWarn
|
|
201
|
+
}
|
|
70
202
|
})
|
|
71
203
|
|
|
72
|
-
test("GpuSampler
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
204
|
+
test("GpuSampler uses Linux DRM fdinfo before native library providers", async () => {
|
|
205
|
+
const root = await fs.promises.mkdtemp(path.join(os.tmpdir(), "pinokio-drm-sampler-"))
|
|
206
|
+
try {
|
|
207
|
+
await fs.promises.mkdir(path.join(root, "1234", "fdinfo"), { recursive: true })
|
|
208
|
+
await fs.promises.writeFile(path.join(root, "1234", "fdinfo", "3"), [
|
|
209
|
+
"drm-driver:\ti915",
|
|
210
|
+
"drm-pdev:\t0000:00:02.0",
|
|
211
|
+
"drm-client-id:\t4",
|
|
212
|
+
"drm-resident-local0:\t32 MiB"
|
|
213
|
+
].join("\n"))
|
|
214
|
+
|
|
215
|
+
const sampler = new GpuSampler({ platform: "linux", procRoot: root })
|
|
216
|
+
let nvmlCalls = 0
|
|
217
|
+
let amdCalls = 0
|
|
218
|
+
let rocmCalls = 0
|
|
219
|
+
sampler.collectNvml = async () => {
|
|
220
|
+
nvmlCalls += 1
|
|
221
|
+
return null
|
|
222
|
+
}
|
|
223
|
+
sampler.collectAmdSmi = async () => {
|
|
224
|
+
amdCalls += 1
|
|
225
|
+
return null
|
|
226
|
+
}
|
|
227
|
+
sampler.collectRocmSmi = async () => {
|
|
228
|
+
rocmCalls += 1
|
|
229
|
+
return null
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
233
|
+
|
|
234
|
+
assert.equal(nvmlCalls, 0)
|
|
235
|
+
assert.equal(amdCalls, 0)
|
|
236
|
+
assert.equal(rocmCalls, 0)
|
|
237
|
+
assert.deepEqual(snapshot.providers, ["linux-drm-fdinfo"])
|
|
238
|
+
assert.equal(snapshot.available, true)
|
|
239
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 32 * MIB)
|
|
240
|
+
} finally {
|
|
241
|
+
await fs.promises.rm(root, { recursive: true, force: true })
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test("GpuSampler uses NVML when fdinfo does not cover target PID", async () => {
|
|
246
|
+
const sampler = new GpuSampler({
|
|
247
|
+
platform: "linux",
|
|
248
|
+
nvmlClient: {
|
|
249
|
+
collect: () => new Map([
|
|
250
|
+
[1234, gpuProcess(1234, 700 * MIB)]
|
|
251
|
+
])
|
|
252
|
+
}
|
|
79
253
|
})
|
|
80
|
-
sampler.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
254
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
255
|
+
let amdCalls = 0
|
|
256
|
+
sampler.collectAmdSmi = async () => {
|
|
257
|
+
amdCalls += 1
|
|
258
|
+
return null
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const snapshot = await sampler.collect(new Set([1234]))
|
|
262
|
+
|
|
263
|
+
assert.equal(amdCalls, 0)
|
|
264
|
+
assert.deepEqual(snapshot.providers, ["linux-nvml"])
|
|
265
|
+
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 700 * MIB)
|
|
266
|
+
})
|
|
267
|
+
|
|
268
|
+
test("NvmlGpuMemoryClient sums the same PID across devices", () => {
|
|
269
|
+
const client = new NvmlGpuMemoryClient({ koffi: { sizeof: () => 1 } })
|
|
270
|
+
const compute = { name: "compute" }
|
|
271
|
+
const graphics = { name: "graphics" }
|
|
272
|
+
client.init = () => {}
|
|
273
|
+
client.getDeviceHandles = () => ["gpu0", "gpu1"]
|
|
274
|
+
client.functions = { compute, graphics, mps: null }
|
|
275
|
+
client.collectProcessList = (device, entry) => {
|
|
276
|
+
const samples = {
|
|
277
|
+
"gpu0:compute": [{ pid: 1234, usedGpuMemory: 300 * MIB }],
|
|
278
|
+
"gpu0:graphics": [{ pid: 1234, usedGpuMemory: 250 * MIB }],
|
|
279
|
+
"gpu1:compute": [{ pid: 1234, usedGpuMemory: 400 * MIB }],
|
|
280
|
+
"gpu1:graphics": [{ pid: 1234, usedGpuMemory: 100 * MIB }]
|
|
88
281
|
}
|
|
282
|
+
return samples[`${device}:${entry && entry.name}`] || []
|
|
89
283
|
}
|
|
90
|
-
sampler.collectAmd = async () => null
|
|
91
284
|
|
|
92
|
-
const
|
|
285
|
+
const processes = client.collect([1234])
|
|
93
286
|
|
|
94
|
-
assert.equal(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
287
|
+
assert.equal(processes.get(1234).usedGpuMemoryBytes, 700 * MIB)
|
|
288
|
+
})
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
test("GpuSampler uses AMD SMI after fdinfo and NVML miss", async () => {
|
|
292
|
+
const sampler = new GpuSampler({
|
|
293
|
+
platform: "linux",
|
|
294
|
+
amdSmiClient: {
|
|
295
|
+
collect: () => new Map([
|
|
296
|
+
[2222, gpuProcess(2222, 300 * MIB)]
|
|
297
|
+
])
|
|
298
|
+
}
|
|
299
|
+
})
|
|
300
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
301
|
+
sampler.collectNvml = async () => null
|
|
302
|
+
let rocmCalls = 0
|
|
303
|
+
sampler.collectRocmSmi = async () => {
|
|
304
|
+
rocmCalls += 1
|
|
305
|
+
return null
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const snapshot = await sampler.collect(new Set([2222]))
|
|
309
|
+
|
|
310
|
+
assert.equal(rocmCalls, 0)
|
|
311
|
+
assert.deepEqual(snapshot.providers, ["linux-amdsmi"])
|
|
312
|
+
assert.equal(snapshot.processes.get(2222).usedGpuMemoryBytes, 300 * MIB)
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
test("GpuSampler uses ROCm SMI after AMD SMI misses", async () => {
|
|
316
|
+
const sampler = new GpuSampler({
|
|
317
|
+
platform: "linux",
|
|
318
|
+
rocmSmiClient: {
|
|
319
|
+
collect: () => new Map([
|
|
320
|
+
[3333, gpuProcess(3333, 200 * MIB)]
|
|
321
|
+
])
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
sampler.collectLinuxDrmFdinfo = async () => null
|
|
325
|
+
sampler.collectNvml = async () => null
|
|
326
|
+
sampler.collectAmdSmi = async () => null
|
|
327
|
+
|
|
328
|
+
const snapshot = await sampler.collect(new Set([3333]))
|
|
329
|
+
|
|
330
|
+
assert.deepEqual(snapshot.providers, ["linux-rocm-smi"])
|
|
331
|
+
assert.equal(snapshot.processes.get(3333).usedGpuMemoryBytes, 200 * MIB)
|
|
98
332
|
})
|
|
99
333
|
|
|
100
334
|
test("GpuSampler merges overlapping provider samples by PID without double-counting", async () => {
|
|
101
335
|
const sampler = new GpuSampler({ platform: "linux" })
|
|
102
|
-
sampler.
|
|
103
|
-
provider: "
|
|
336
|
+
sampler.collectLinuxDrmFdinfo = async () => ({
|
|
337
|
+
provider: "linux-drm-fdinfo",
|
|
104
338
|
processes: new Map([
|
|
105
|
-
[1234, gpuProcess(1234, 300 * MIB)]
|
|
106
|
-
[3333, gpuProcess(3333, 200 * MIB)]
|
|
339
|
+
[1234, gpuProcess(1234, 300 * MIB)]
|
|
107
340
|
]),
|
|
108
341
|
error: null
|
|
109
342
|
})
|
|
110
|
-
sampler.
|
|
111
|
-
provider: "
|
|
343
|
+
sampler.collectNvml = async () => ({
|
|
344
|
+
provider: "linux-nvml",
|
|
112
345
|
processes: new Map([
|
|
113
346
|
[1234, gpuProcess(1234, 500 * MIB)],
|
|
114
|
-
[
|
|
347
|
+
[3333, gpuProcess(3333, 200 * MIB)]
|
|
115
348
|
]),
|
|
116
349
|
error: null
|
|
117
350
|
})
|
|
351
|
+
sampler.collectAmdSmi = async () => null
|
|
352
|
+
sampler.collectRocmSmi = async () => null
|
|
118
353
|
|
|
119
354
|
const snapshot = await sampler.collect()
|
|
120
355
|
|
|
121
356
|
assert.equal(snapshot.available, true)
|
|
122
|
-
assert.deepEqual(snapshot.providers, ["
|
|
357
|
+
assert.deepEqual(snapshot.providers, ["linux-drm-fdinfo", "linux-nvml"])
|
|
123
358
|
assert.equal(snapshot.processes.get(1234).usedGpuMemoryBytes, 500 * MIB)
|
|
124
|
-
assert.equal(snapshot.processes.get(2222).usedGpuMemoryBytes, 100 * MIB)
|
|
125
359
|
assert.equal(snapshot.processes.get(3333).usedGpuMemoryBytes, 200 * MIB)
|
|
126
360
|
})
|
|
361
|
+
|
|
362
|
+
test("GpuSampler does not collect VRAM providers on macOS", async () => {
|
|
363
|
+
const sampler = new GpuSampler({ platform: "darwin" })
|
|
364
|
+
sampler.collectNvml = async () => {
|
|
365
|
+
throw new Error("NVML should not be queried on macOS")
|
|
366
|
+
}
|
|
367
|
+
sampler.collectAmdSmi = async () => {
|
|
368
|
+
throw new Error("AMD SMI should not be queried on macOS")
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const snapshot = await sampler.collect()
|
|
372
|
+
|
|
373
|
+
assert.equal(snapshot.available, false)
|
|
374
|
+
assert.deepEqual(snapshot.providers, [])
|
|
375
|
+
assert.equal(snapshot.processes.size, 0)
|
|
376
|
+
})
|
package/test/script-api.test.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs')
|
|
3
|
+
const os = require('node:os')
|
|
2
4
|
const path = require('node:path')
|
|
3
5
|
const test = require('node:test')
|
|
4
6
|
|
|
7
|
+
const Api = require('../kernel/api')
|
|
5
8
|
const Script = require('../kernel/api/script')
|
|
6
9
|
|
|
7
10
|
test('script.restart stops explicit target and schedules start with explicit params', async () => {
|
|
@@ -95,3 +98,90 @@ test('script.restart self restart preserves session id and parent args', async (
|
|
|
95
98
|
params: { prompt: 'again' }
|
|
96
99
|
})
|
|
97
100
|
})
|
|
101
|
+
|
|
102
|
+
test('script.run refreshes git mapping before resolving remote dependency uri', async () => {
|
|
103
|
+
const script = new Script()
|
|
104
|
+
const calls = []
|
|
105
|
+
let initialized = false
|
|
106
|
+
const remoteUri = 'https://github.com/example/dependency.pinokio.git/start.js'
|
|
107
|
+
const resolvedPath = '/pinokio/api/github_com_example_dependency_pinokio_git/start.js'
|
|
108
|
+
const kernel = {
|
|
109
|
+
path: (...parts) => path.join('/pinokio', ...parts),
|
|
110
|
+
bin: {
|
|
111
|
+
sh: async () => {
|
|
112
|
+
throw new Error('should not clone when init maps existing dependency')
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
api: {
|
|
116
|
+
userdir: '/pinokio/api',
|
|
117
|
+
running: {},
|
|
118
|
+
init: async () => {
|
|
119
|
+
calls.push('init')
|
|
120
|
+
initialized = true
|
|
121
|
+
},
|
|
122
|
+
filePath: (uri) => {
|
|
123
|
+
calls.push('filePath')
|
|
124
|
+
assert.equal(initialized, true)
|
|
125
|
+
assert.equal(uri, remoteUri)
|
|
126
|
+
return resolvedPath
|
|
127
|
+
},
|
|
128
|
+
resolveGitURI: (uri) => {
|
|
129
|
+
calls.push('resolveGitURI')
|
|
130
|
+
assert.equal(initialized, true)
|
|
131
|
+
assert.equal(uri, remoteUri)
|
|
132
|
+
return resolvedPath
|
|
133
|
+
},
|
|
134
|
+
getGitURI: () => 'https://github.com/example/dependency.pinokio.git',
|
|
135
|
+
process: (request, done) => {
|
|
136
|
+
calls.push('process')
|
|
137
|
+
assert.equal(request.uri, remoteUri)
|
|
138
|
+
done({ input: { ok: true } })
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const result = await script.run({
|
|
144
|
+
params: {
|
|
145
|
+
uri: remoteUri,
|
|
146
|
+
params: { prompt: 'go' }
|
|
147
|
+
}
|
|
148
|
+
}, () => {}, kernel)
|
|
149
|
+
|
|
150
|
+
assert.deepEqual(result, { ok: true })
|
|
151
|
+
assert.deepEqual(calls, ['init', 'filePath', 'resolveGitURI', 'init', 'process'])
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
test('api.linkGit keeps previous git mapping until refresh completes', async () => {
|
|
155
|
+
const root = fs.mkdtempSync(path.join(os.tmpdir(), 'pinokio-linkgit-'))
|
|
156
|
+
const userdir = path.join(root, 'api')
|
|
157
|
+
const repo = path.join(userdir, 'demo')
|
|
158
|
+
fs.mkdirSync(path.join(repo, '.git'), { recursive: true })
|
|
159
|
+
fs.writeFileSync(path.join(repo, '.git', 'config'), [
|
|
160
|
+
'[remote "origin"]',
|
|
161
|
+
'\turl = https://github.com/example/demo.git',
|
|
162
|
+
'\tfetch = +refs/heads/*:refs/remotes/origin/*',
|
|
163
|
+
''
|
|
164
|
+
].join('\n'))
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
const api = new Api({
|
|
168
|
+
homedir: root,
|
|
169
|
+
path: (...parts) => path.join(root, ...parts)
|
|
170
|
+
})
|
|
171
|
+
api.userdir = userdir
|
|
172
|
+
api.gitPath = {
|
|
173
|
+
'https://github.com/example/old.git': path.join(userdir, 'old')
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const refresh = api.linkGit()
|
|
177
|
+
assert.deepEqual(api.gitPath, {
|
|
178
|
+
'https://github.com/example/old.git': path.join(userdir, 'old')
|
|
179
|
+
})
|
|
180
|
+
await refresh
|
|
181
|
+
assert.deepEqual(api.gitPath, {
|
|
182
|
+
'https://github.com/example/demo.git': repo
|
|
183
|
+
})
|
|
184
|
+
} finally {
|
|
185
|
+
fs.rmSync(root, { recursive: true, force: true })
|
|
186
|
+
}
|
|
187
|
+
})
|