pinokiod 5.1.5 → 5.1.11

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.
Files changed (54) hide show
  1. package/kernel/api/fs/download_worker.js +158 -0
  2. package/kernel/api/fs/index.js +95 -91
  3. package/kernel/api/index.js +3 -0
  4. package/kernel/bin/index.js +5 -2
  5. package/kernel/environment.js +19 -2
  6. package/kernel/git.js +972 -1
  7. package/kernel/index.js +65 -30
  8. package/kernel/peer.js +1 -2
  9. package/kernel/plugin.js +0 -8
  10. package/kernel/procs.js +92 -36
  11. package/kernel/prototype.js +45 -22
  12. package/kernel/shells.js +30 -6
  13. package/kernel/sysinfo.js +33 -13
  14. package/kernel/util.js +61 -24
  15. package/kernel/workspace_status.js +131 -7
  16. package/package.json +1 -1
  17. package/pipe/index.js +1 -1
  18. package/server/index.js +1173 -348
  19. package/server/public/create-launcher.js +157 -2
  20. package/server/public/install.js +135 -41
  21. package/server/public/style.css +32 -1
  22. package/server/public/tab-link-popover.js +45 -14
  23. package/server/public/terminal-settings.js +51 -35
  24. package/server/public/urldropdown.css +89 -3
  25. package/server/socket.js +12 -7
  26. package/server/views/agents.ejs +4 -3
  27. package/server/views/app.ejs +798 -30
  28. package/server/views/bootstrap.ejs +2 -1
  29. package/server/views/checkpoints.ejs +1014 -0
  30. package/server/views/checkpoints_registry_beta.ejs +260 -0
  31. package/server/views/columns.ejs +4 -4
  32. package/server/views/connect.ejs +1 -0
  33. package/server/views/d.ejs +74 -4
  34. package/server/views/download.ejs +28 -28
  35. package/server/views/editor.ejs +4 -5
  36. package/server/views/env_editor.ejs +1 -1
  37. package/server/views/file_explorer.ejs +1 -1
  38. package/server/views/index.ejs +3 -1
  39. package/server/views/init/index.ejs +2 -1
  40. package/server/views/install.ejs +2 -1
  41. package/server/views/net.ejs +9 -7
  42. package/server/views/network.ejs +15 -14
  43. package/server/views/pro.ejs +5 -2
  44. package/server/views/prototype/index.ejs +2 -1
  45. package/server/views/registry_link.ejs +76 -0
  46. package/server/views/rows.ejs +4 -4
  47. package/server/views/screenshots.ejs +1 -0
  48. package/server/views/settings.ejs +1 -0
  49. package/server/views/shell.ejs +4 -6
  50. package/server/views/terminal.ejs +528 -38
  51. package/server/views/tools.ejs +1 -0
  52. package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/1764297248545 +0 -45
  53. package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/events +0 -4
  54. package/undefined/logs/dev/plugin/cursor-agent.git/pinokio.js/latest +0 -45
package/kernel/index.js CHANGED
@@ -89,6 +89,11 @@ class Kernel {
89
89
  this.pinokio_configs = {}
90
90
  this.shellpath = shellPath.sync()
91
91
  this.favicon = new Favicon()
92
+ this.vram = 0
93
+ this.ram = 0
94
+ this.sysReady = new Promise((resolve) => {
95
+ this._resolveSysReady = resolve
96
+ })
92
97
 
93
98
 
94
99
  }
@@ -999,9 +1004,16 @@ class Kernel {
999
1004
 
1000
1005
  }
1001
1006
 
1007
+ // Load git checkpoints as soon as homedir is ready so features depending on it
1008
+ // (like the Backups page) can see prior state immediately.
1009
+ console.time("git.loadCheckpoints")
1010
+ await this.git.loadCheckpoints()
1011
+ console.timeEnd("git.loadCheckpoints")
1012
+
1002
1013
  // let contents = await fs.promises.readdir(this.homedir)
1003
1014
  //await this.bin.init()
1004
1015
  let ts = Date.now()
1016
+ // Initialize core tools
1005
1017
  this.bin.init().then(() => {
1006
1018
  if (this.homedir) {
1007
1019
  this.git.init().then(() => {
@@ -1053,6 +1065,10 @@ class Kernel {
1053
1065
  this.sysinfo = info
1054
1066
 
1055
1067
  await this.getInfo(true)
1068
+ if (this._resolveSysReady) {
1069
+ this._resolveSysReady()
1070
+ this._resolveSysReady = null
1071
+ }
1056
1072
 
1057
1073
  await fs.promises.mkdir(this.path("logs"), { recursive: true }).catch((e) => { })
1058
1074
  await fs.promises.writeFile(this.path("logs/system.json"), JSON.stringify(this.i, null, 2))
@@ -1064,39 +1080,49 @@ class Kernel {
1064
1080
  // get env
1065
1081
  if (!this.launch_complete) {
1066
1082
  let interval = setInterval(async () => {
1067
- if (this.i) {
1068
- for(let api of this.i.api) {
1069
- let env_path = path.resolve(this.api.userdir, api.path)
1070
- let e = await Environment.get(env_path, this)
1071
- if (e.PINOKIO_SCRIPT_AUTOLAUNCH && e.PINOKIO_SCRIPT_AUTOLAUNCH.trim().length > 0) {
1072
- let autolaunch_path = path.resolve(env_path, e.PINOKIO_SCRIPT_AUTOLAUNCH)
1073
- let exists = await this.exists(autolaunch_path)
1074
- if (exists) {
1075
- this.api.process({
1076
- uri: autolaunch_path,
1077
- input: {}
1083
+ try {
1084
+ if (this.i) {
1085
+ for (let api of this.i.api) {
1086
+ let env_path = path.resolve(this.api.userdir, api.path)
1087
+ let e = await Environment.get(env_path, this)
1088
+ if (e.PINOKIO_SCRIPT_AUTOLAUNCH && e.PINOKIO_SCRIPT_AUTOLAUNCH.trim().length > 0) {
1089
+ let autolaunch_path = path.resolve(env_path, e.PINOKIO_SCRIPT_AUTOLAUNCH)
1090
+ let exists = await this.exists(autolaunch_path)
1091
+ if (exists) {
1092
+ this.api.process({
1093
+ uri: autolaunch_path,
1094
+ input: {}
1078
1095
  // client: req.client,
1079
1096
  // caller: req.parent.path,
1080
- }, (r) => {
1081
- console.log({ autolaunch_path, r })
1097
+ }, (r) => {
1098
+ console.log({ autolaunch_path, r })
1082
1099
  // resolve(r.input)
1083
- })
1084
- } else {
1085
- console.log("SCRIPT DOES NOT EXIST. Ignoring.", autolaunch_path)
1100
+ }).catch((err) => {
1101
+ console.warn('[Kernel.init] autolaunch process failed:', err && err.message ? err.message : err)
1102
+ })
1103
+ } else {
1104
+ console.log("SCRIPT DOES NOT EXIST. Ignoring.", autolaunch_path)
1105
+ }
1086
1106
  }
1087
1107
  }
1108
+ clearInterval(interval)
1109
+ setTimeout(() => {
1110
+ this.launch_complete = true
1111
+ console.log("SETTING launch complete", this.launch_complete)
1112
+ }, 2000)
1088
1113
  }
1089
- clearInterval(interval)
1090
- setTimeout(() => {
1091
- this.launch_complete = true
1092
- console.log("SETTING launch complete", this.launch_complete)
1093
- }, 2000)
1114
+ } catch (err) {
1115
+ console.warn('[Kernel.init] autolaunch loop failed:', err && err.message ? err.message : err)
1094
1116
  }
1095
1117
  }, 1000)
1096
1118
  }
1097
1119
  }
1120
+ }).catch((err) => {
1121
+ console.warn("Shell init error:", err && err.message ? err.message : err)
1098
1122
  })
1099
1123
  }
1124
+ }).catch((err) => {
1125
+ console.warn("Bin init error:", err && err.message ? err.message : err)
1100
1126
  })
1101
1127
  let ts2 = Date.now()
1102
1128
  await this.api.init()
@@ -1109,6 +1135,8 @@ class Kernel {
1109
1135
  system,
1110
1136
  platform: this.platform,
1111
1137
  arch: this.arch,
1138
+ vram: this.vram,
1139
+ ram: this.ram,
1112
1140
  proxy: (port) => {
1113
1141
  return this.api.get_proxy_url("/proxy", port)
1114
1142
  },
@@ -1118,19 +1146,24 @@ class Kernel {
1118
1146
  // }, 3000)
1119
1147
 
1120
1148
  // refresh every 5 second
1121
- if (this.refresh_interval) {
1122
- clearInterval(this.refresh_interval)
1123
- }
1124
- //let network_active = await this.network_active()
1125
- //if (network_active) {
1126
- this.refresh_interval = setInterval(() => {
1149
+ const scheduleRefresh = () => {
1150
+ if (this.refresh_interval) {
1151
+ clearTimeout(this.refresh_interval)
1152
+ }
1153
+ this.refresh_interval = setTimeout(async () => {
1127
1154
  if (this.server_running) {
1128
- this.refresh(true)
1155
+ try {
1156
+ await this.refresh(true)
1157
+ } catch (err) {
1158
+ console.warn('[Kernel.refresh] background refresh failed:', err && err.message ? err.message : err)
1159
+ }
1129
1160
  } else {
1130
1161
  console.log("server not running yet. retry network refresh in 5 secs")
1131
1162
  }
1163
+ scheduleRefresh()
1132
1164
  }, 5000)
1133
- //}
1165
+ }
1166
+ scheduleRefresh()
1134
1167
 
1135
1168
  }
1136
1169
 
@@ -1148,13 +1181,15 @@ class Kernel {
1148
1181
  async update_sysinfo() {
1149
1182
  try {
1150
1183
  if (this.sys) {
1151
- await this.sys.refresh()
1184
+ // await this.sys.refresh()
1152
1185
  let info = this.sys.info
1153
1186
  this.template.update(info)
1154
1187
  this.sysinfo = info
1155
1188
  this.gpu = info.gpu
1156
1189
  this.gpu_model = info.gpu_model
1157
1190
  this.gpus = info.gpus
1191
+ this.vram = typeof info.vram === "number" ? info.vram : 0
1192
+ this.ram = typeof info.ram === "number" ? info.ram : 0
1158
1193
  }
1159
1194
  } catch (e) {
1160
1195
  console.log("sysinfo error", e)
package/kernel/peer.js CHANGED
@@ -74,7 +74,7 @@ class PeerDiscovery {
74
74
  return fallback
75
75
  }
76
76
 
77
- const peer_active = resolveFlag('PINOKIO_NETWORK_ACTIVE', true)
77
+ const peer_active = resolveFlag('PINOKIO_NETWORK_ACTIVE', false)
78
78
  const https_active = resolveFlag('PINOKIO_HTTPS_ACTIVE', false)
79
79
  // console.log("kernel.refresh", { active, notify_peers })
80
80
 
@@ -101,7 +101,6 @@ class PeerDiscovery {
101
101
  async start(kernel) {
102
102
  await this.check(kernel)
103
103
 
104
- //if (this.active) {
105
104
  if (this.peer_active) {
106
105
  // Listen for incoming pings
107
106
  this.socket = dgram.createSocket('udp4');
package/kernel/plugin.js CHANGED
@@ -64,14 +64,6 @@ class Plugin {
64
64
  return
65
65
  }
66
66
  } else {
67
- if (this.kernel.bin.installed && this.kernel.bin.installed.conda && this.kernel.bin.installed.conda.has("git")) {
68
- await this.kernel.exec({
69
- message: "git pull",
70
- path: this.kernel.path("plugin/code")
71
- }, (e) => {
72
- process.stdout.write(e.raw)
73
- })
74
- }
75
67
  await this.setConfig()
76
68
  }
77
69
  }
package/kernel/procs.js CHANGED
@@ -163,12 +163,29 @@ class Procs {
163
163
  }
164
164
  getPortPidList(cb) {
165
165
  const cmd = isWin ? 'netstat -ano -p tcp' : 'lsof -nP -iTCP -sTCP:LISTEN';
166
- let d = Date.now()
167
- exec(cmd, { maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => {
168
- this.get_pids(stdout).then((pids) => {
169
- cb(pids)
170
- })
171
- });
166
+ try {
167
+ exec(cmd, { maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => {
168
+ if (err) {
169
+ console.warn('[Procs] getPortPidList failed:', err && err.message ? err.message : err)
170
+ cb([])
171
+ return
172
+ }
173
+ try {
174
+ this.get_pids(stdout || '').then((pids) => {
175
+ cb(pids)
176
+ }).catch((e) => {
177
+ console.warn('[Procs] get_pids failed:', e && e.message ? e.message : e)
178
+ cb([])
179
+ })
180
+ } catch (e) {
181
+ console.warn('[Procs] get_pids threw:', e && e.message ? e.message : e)
182
+ cb([])
183
+ }
184
+ });
185
+ } catch (e) {
186
+ console.warn('[Procs] spawn getPortPidList failed:', e && e.message ? e.message : e)
187
+ cb([])
188
+ }
172
189
  }
173
190
  get_name(stdout) {
174
191
  const lines = stdout.trim().split('\n');
@@ -206,42 +223,81 @@ class Procs {
206
223
  return
207
224
  }
208
225
  const cmd = isWin ? 'tasklist /fo csv /nh' : 'ps -Ao pid,comm';
209
- exec(cmd, { maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => {
210
- let map = this.get_name(stdout)
211
- if (!this.port_map) {
212
- this.port_map = {}
213
- }
214
- for(let key in map) {
215
- this.port_map[key] = map[key]
216
- }
217
- cb(this.port_map)
218
- });
226
+ try {
227
+ exec(cmd, { maxBuffer: 10 * 1024 * 1024 }, async (err, stdout) => {
228
+ if (err) {
229
+ console.warn('[Procs] getPidToNameMap failed:', err && err.message ? err.message : err)
230
+ cb(this.port_map || {})
231
+ return
232
+ }
233
+ let map
234
+ try {
235
+ map = this.get_name(stdout || '')
236
+ } catch (e) {
237
+ console.warn('[Procs] get_name threw:', e && e.message ? e.message : e)
238
+ cb(this.port_map || {})
239
+ return
240
+ }
241
+ if (!this.port_map) {
242
+ this.port_map = {}
243
+ }
244
+ for(let key in map) {
245
+ this.port_map[key] = map[key]
246
+ }
247
+ cb(this.port_map)
248
+ });
249
+ } catch (e) {
250
+ console.warn('[Procs] spawn getPidToNameMap failed:', e && e.message ? e.message : e)
251
+ cb(this.port_map || {})
252
+ }
219
253
  }
220
254
  async refresh() {
255
+ if (this.kernel && typeof this.kernel.binCheckDepth === 'number' && this.kernel.binCheckDepth > 0) {
256
+ return
257
+ }
221
258
  let map = {}
222
259
  this.refreshing = true
223
- let ts = Date.now()
224
- let list = await new Promise((resolve, reject) => {
225
- this.getPortPidList((portPidList) => {
226
- this.getPidToNameMap(portPidList, (pidToName) => {
227
- let list = portPidList.map(({ port, pid, ip }) => {
228
- const fullname = pidToName[pid] || 'Unknown';
229
- const name = fullname.split(path.sep).pop()
230
- if (["caddy", "caddy.exe"].includes(name)) {
231
- this.caddy_pid = pid
232
- return null
233
- } else {
234
- map["" + pid] = fullname
235
- return { port, pid , name, fullname, ip }
260
+ try {
261
+ let list = await new Promise((resolve, reject) => {
262
+ try {
263
+ this.getPortPidList((portPidList) => {
264
+ if (!Array.isArray(portPidList) || portPidList.length === 0) {
265
+ resolve([])
266
+ return
236
267
  }
237
- }).filter((x) => { return x })
238
- resolve(list)
239
- })
268
+ this.getPidToNameMap(portPidList, (pidToName) => {
269
+ try {
270
+ let list = portPidList.map(({ port, pid, ip }) => {
271
+ const fullname = pidToName[pid] || 'Unknown';
272
+ const name = fullname.split(path.sep).pop()
273
+ if (["caddy", "caddy.exe"].includes(name)) {
274
+ this.caddy_pid = pid
275
+ return null
276
+ } else {
277
+ map["" + pid] = fullname
278
+ return { port, pid , name, fullname, ip }
279
+ }
280
+ }).filter((x) => { return x })
281
+ resolve(list)
282
+ } catch (e) {
283
+ console.warn('[Procs] refresh mapping failed:', e && e.message ? e.message : e)
284
+ resolve([])
285
+ }
286
+ })
287
+ })
288
+ } catch (e) {
289
+ reject(e)
290
+ }
240
291
  })
241
- })
242
- this.info = list
243
- this.map = map
244
- this.refreshing = false
292
+ this.info = list
293
+ this.map = map
294
+ } catch (e) {
295
+ console.warn('[Procs] refresh failed:', e && e.message ? e.message : e)
296
+ this.info = []
297
+ this.map = {}
298
+ } finally {
299
+ this.refreshing = false
300
+ }
245
301
  }
246
302
  }
247
303
  module.exports = Procs
@@ -14,14 +14,7 @@ class Proto {
14
14
 
15
15
  // if ~/pinokio/prototype doesn't exist, clone
16
16
  let exists = await this.kernel.exists("prototype/system")
17
- if (exists) {
18
- await this.kernel.exec({
19
- message: "git pull",
20
- path: this.kernel.path("prototype/system")
21
- }, (e) => {
22
- process.stdout.write(e.raw)
23
- })
24
- } else {
17
+ if (!exists) {
25
18
  console.log("prototype doesn't exist. cloning...")
26
19
  await fs.promises.mkdir(this.kernel.path("prototype"), { recursive: true }).catch((e) => { })
27
20
  await this.kernel.exec({
@@ -31,20 +24,26 @@ class Proto {
31
24
  process.stdout.write(e.raw)
32
25
  })
33
26
  }
34
- await this.kernel.download({
35
- uri: "https://raw.githubusercontent.com/pinokiocomputer/home/refs/heads/main/docs/README.md",
36
- path: this.kernel.path("prototype"),
37
- filename: "PINOKIO.md"
38
- }, (e) => {
39
- process.stdout.write(e.raw)
40
- })
41
- await this.kernel.download({
42
- uri: "https://raw.githubusercontent.com/pinokiocomputer/pterm/refs/heads/main/README.md",
43
- path: this.kernel.path("prototype"),
44
- filename: "PTERM.md"
45
- }, (e) => {
46
- process.stdout.write(e.raw)
47
- })
27
+ let pinokio_exists = await this.kernel.exists("prototype/PINOKIO.md")
28
+ if (!pinokio_exists) {
29
+ await this.kernel.download({
30
+ uri: "https://raw.githubusercontent.com/pinokiocomputer/home/refs/heads/main/docs/README.md",
31
+ path: this.kernel.path("prototype"),
32
+ filename: "PINOKIO.md"
33
+ }, (e) => {
34
+ process.stdout.write(e.raw)
35
+ })
36
+ }
37
+ let pterm_exists = await this.kernel.exists("prototype/PTERM.md")
38
+ if (!pterm_exists) {
39
+ await this.kernel.download({
40
+ uri: "https://raw.githubusercontent.com/pinokiocomputer/pterm/refs/heads/main/README.md",
41
+ path: this.kernel.path("prototype"),
42
+ filename: "PTERM.md"
43
+ }, (e) => {
44
+ process.stdout.write(e.raw)
45
+ })
46
+ }
48
47
  }
49
48
  }
50
49
  async ai() {
@@ -74,6 +73,7 @@ class Proto {
74
73
  await fs.promises.rm(this.kernel.path("prototype"), { recursive: true })
75
74
  }
76
75
  async create(req, ondata) {
76
+ let uploadTmpDir = null;
77
77
  try {
78
78
  if (req.client) {
79
79
  this.kernel.client = req.client
@@ -88,6 +88,23 @@ class Proto {
88
88
  payload.cwd = path.resolve(cwd, name)
89
89
  payload.input = req.params
90
90
 
91
+ const uploadToken = req.params && req.params.uploadToken ? String(req.params.uploadToken).trim() : ''
92
+ if (uploadToken) {
93
+ uploadTmpDir = this.kernel.path("tmp", "create", uploadToken)
94
+ const exists = await this.kernel.exists(uploadTmpDir)
95
+ if (!exists) {
96
+ throw new Error("Upload token not found or expired")
97
+ }
98
+ await fs.promises.mkdir(payload.cwd, { recursive: true })
99
+ const entries = await fs.promises.readdir(uploadTmpDir, { withFileTypes: true })
100
+ for (const entry of entries) {
101
+ if (!entry.isFile()) continue
102
+ const src = path.resolve(uploadTmpDir, entry.name)
103
+ const dest = path.resolve(payload.cwd, entry.name)
104
+ await fs.promises.copyFile(src, dest)
105
+ }
106
+ }
107
+
91
108
 
92
109
  // 1. move mkdir into each launcher
93
110
  // 2. run logic
@@ -138,6 +155,12 @@ class Proto {
138
155
  } catch (e) {
139
156
  console.log("ERROR", e)
140
157
  return { error: e.stack }
158
+ } finally {
159
+ if (uploadTmpDir) {
160
+ try {
161
+ await fs.promises.rm(uploadTmpDir, { recursive: true, force: true })
162
+ } catch (_) {}
163
+ }
141
164
  }
142
165
  }
143
166
  async readme(proto) {
package/kernel/shells.js CHANGED
@@ -158,9 +158,29 @@ class Shells {
158
158
  }
159
159
 
160
160
  let exec_path = (params.path ? params.path : ".") // use the current path if not specified
161
- let cwd = (options && options.cwd ? options.cwd : this.kernel.homedir) // if cwd exists, use it. Otherwise the cwd is pinokio home folder (~/pinokio)
161
+ let cwd = (options && options.cwd ? options.cwd : this.kernel.homedir) // if cwd exists, use it. Otherwise the cwd is pinokio home folder (~/pinokio)
162
162
  params.path = this.kernel.api.resolvePath(cwd, exec_path)
163
163
 
164
+ // If this shell runs under ~/pinokio/api/<workspace>, remember the workspace
165
+ // and the current set of known git repo roots so we can detect new repos
166
+ // created by this step and pin them to recorded commits.
167
+ let workspaceName
168
+ let workspaceRoot
169
+ let beforeDirs
170
+ if (params.path && this.kernel && this.kernel.path) {
171
+ const apiRoot = this.kernel.path("api")
172
+ const rel = path.relative(apiRoot, params.path)
173
+ if (rel && !rel.startsWith("..") && !path.isAbsolute(rel)) {
174
+ const segments = rel.split(path.sep).filter(Boolean)
175
+ if (segments.length > 0) {
176
+ workspaceName = segments[0]
177
+ workspaceRoot = this.kernel.path("api", workspaceName)
178
+ beforeDirs = new Set(this.kernel.git.dirs)
179
+ }
180
+ }
181
+ }
182
+ const parentMeta = params.$parent || null
183
+
164
184
  const plannedShell = params.shell || (this.kernel.platform === 'win32' ? 'cmd.exe' : 'bash')
165
185
  await this.ensureBracketedPasteSupport(plannedShell)
166
186
  let sh = new Shell(this.kernel)
@@ -275,6 +295,15 @@ class Shells {
275
295
  sh.kill()
276
296
  }
277
297
  })
298
+ // If this shell ran under a workspace, rescan git repos for that workspace.
299
+ // Snapshots are now always user-initiated via the backups UI; here we only
300
+ // pin new repos to specific commits when this run was started from a
301
+ // snapshot restore.
302
+ if (workspaceRoot && beforeDirs) {
303
+ try {
304
+ await this.kernel.git.restoreNewReposForActiveSnapshot(workspaceName, workspaceRoot, beforeDirs)
305
+ } catch (_) {}
306
+ }
278
307
  /*
279
308
  {
280
309
  method: "shell.run",
@@ -450,11 +479,6 @@ class Shells {
450
479
  }
451
480
  return true
452
481
  } else {
453
- const preview = typeof params.emit === 'string' ? params.emit.slice(0, 64) : ''
454
- console.warn('[shell.emit] missing session for id', params.id, {
455
- paste: !!params.paste,
456
- preview
457
- })
458
482
  return false
459
483
  }
460
484
  }
package/kernel/sysinfo.js CHANGED
@@ -10,7 +10,8 @@ class Sysinfo {
10
10
  // await this.refresh()
11
11
  }
12
12
  async static() {
13
- await Promise.all([this.gpus(), this.system(), this.cpu(), this.os(), this.audio(), this.env()])
13
+ //await Promise.all([this.gpus(), this.system(), this.cpu(), this.os(), this.audio(), this.env()])
14
+ await Promise.all([this.gpus(), this.system(), this.cpu(), this.os(), this.audio(), this.env(), this.memory()])
14
15
  // await this.gpus()
15
16
  // await this.system()
16
17
  // await this.cpu()
@@ -25,7 +26,8 @@ class Sysinfo {
25
26
  // await this.time()
26
27
 
27
28
 
28
- await Promise.all([this.memory(), this.battery(), this.proc(), this.bluetooth()])
29
+ //await Promise.all([this.memory(), this.battery(), this.proc(), this.bluetooth()])
30
+ // await Promise.all([this.memory(), this.proc()])
29
31
  // await this.memory()
30
32
  // await this.battery()
31
33
  // await this.proc()
@@ -37,12 +39,13 @@ class Sysinfo {
37
39
  }
38
40
  async gpus() {
39
41
  let g = await system.graphics()
42
+ let controllers = Array.isArray(g && g.controllers) ? g.controllers : []
40
43
  let gpus
41
- if (g && g.controllers && g.controllers.length > 0) {
42
- gpus = g.controllers.map((x) => {
44
+ if (controllers.length > 0) {
45
+ gpus = controllers.map((x) => {
43
46
  return {
44
- name: x.vendor.toLowerCase(),
45
- model: x.model.toLowerCase()
47
+ name: (x.vendor || "").toLowerCase(),
48
+ model: (x.model || "").toLowerCase()
46
49
  }
47
50
  })
48
51
  } else {
@@ -71,11 +74,25 @@ class Sysinfo {
71
74
  gpu = "none"
72
75
  }
73
76
 
77
+ let primaryController
78
+ if (is_nvidia) {
79
+ primaryController = controllers.find(x => /nvidia/i.test(x.vendor || ""))
80
+ } else if (is_amd) {
81
+ primaryController = controllers.find(x => /(amd|advanced micro devices)/i.test(x.vendor || ""))
82
+ } else if (is_apple) {
83
+ primaryController = controllers.find(x => /apple/i.test(x.vendor || ""))
84
+ } else if (controllers.length > 0) {
85
+ primaryController = controllers[0]
86
+ }
87
+
88
+ let vramMB = primaryController && primaryController.vram ? primaryController.vram : 0
89
+ let vramGB = vramMB > 0 ? Math.round(vramMB / 1024) : 0
90
+
74
91
  this.info.graphics = g
75
92
  this.info.gpus = gpus
76
93
  this.info.gpu = gpu
77
94
  this.info.gpu_model = gpu_model
78
-
95
+ this.info.vram = vramGB
79
96
  }
80
97
  // async time() {
81
98
  // this.info.time = await system.time()
@@ -88,10 +105,13 @@ class Sysinfo {
88
105
  }
89
106
  async memory() {
90
107
  this.info.mem = await system.mem()
108
+ let totalBytes = this.info.mem && this.info.mem.total ? this.info.mem.total : 0
109
+ let totalGB = totalBytes > 0 ? Math.round(totalBytes / (1024 * 1024 * 1024)) : 0
110
+ this.info.ram = totalGB
91
111
  }
92
- async battery() {
93
- this.info.battery = await system.battery()
94
- }
112
+ // async battery() {
113
+ // this.info.battery = await system.battery()
114
+ // }
95
115
  async os() {
96
116
  this.info.osInfo = await system.osInfo()
97
117
  this.info.shell = await system.shell()
@@ -113,9 +133,9 @@ class Sysinfo {
113
133
  this.info.gateway = await system.networkGatewayDefault()
114
134
  this.info.interface = await system.networkInterfaces("default")
115
135
  }
116
- async bluetooth() {
117
- this.info.bluetooth = await system.bluetoothDevices()
118
- }
136
+ // async bluetooth() {
137
+ // this.info.bluetooth = await system.bluetoothDevices()
138
+ // }
119
139
  exists(_path) {
120
140
  return new Promise(r=>fs.access(_path, fs.constants.F_OK, e => r(!e)))
121
141
  }