pinokiod 6.0.90 → 6.0.92

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.
@@ -5,56 +5,66 @@ const { detectCommandLineTools } = require('./xcode-tools')
5
5
  class Brew {
6
6
  description = "Wait for an install pop-up, then approve."
7
7
  async install(req, ondata) {
8
- const installer_url = "https://github.com/cocktailpeanut/bin/releases/download/homebrew/homebrew.zip"
9
- //const installer_url = "https://github.com/Homebrew/brew/tarball/master"
10
- const installer = "Homebrew.zip"
11
-
12
- ondata({ raw: `downloading installer: ${installer_url}...\r\n` })
13
- await this.kernel.bin.download(installer_url, installer, ondata)
14
- console.log("## DOWNLOADED")
15
-
16
- console.log("homebrewpath", this.kernel.bin.path("homebrew"))
17
- await this.kernel.bin.rm("homebrew", ondata)
18
-
19
- // 2. run the script
20
- ondata({ raw: `unzipping installer: ${installer}...\r\n` })
21
- await this.kernel.bin.unzip("Homebrew.zip", this.kernel.bin.path(), null, ondata)
22
- await this.kernel.bin.rm("Homebrew.zip", ondata)
23
-
24
8
  if (this.kernel.platform === "darwin") {
25
9
  const checkingMsg = "> checking xcode command line tools...\r\n"
26
10
  console.log(checkingMsg)
27
11
  ondata({ raw: checkingMsg })
28
12
 
29
- const cltStatus = await detectCommandLineTools({
30
- exec: (params) => this.kernel.bin.exec(params, () => {})
31
- })
13
+ const cltStatus = await detectCommandLineTools()
32
14
 
33
15
  if (cltStatus.valid) {
34
16
  const msg = `> command line tools detected at ${cltStatus.path} (pkg ${cltStatus.pkgVersion}, xcode-select ${cltStatus.xcodeSelectVersion}). skipping...\r\n`
35
17
  console.log(msg)
36
18
  ondata({ raw: msg })
19
+ } else if (needsManualXcodeAction(cltStatus.reason)) {
20
+ const instruction = manualXcodeActionMessage(cltStatus.reason)
21
+ const msg = `> ${instruction}\r\n`
22
+ console.log(msg)
23
+ ondata({ raw: msg })
24
+ ondata({
25
+ blocker: true,
26
+ html: `<b><i class="fa-solid fa-circle-exclamation"></i> Xcode action required</b><div style="padding-top:8px">${instruction}</div>`
27
+ }, "notify3")
28
+ throw new Error(`__PINOKIO_BLOCKER__ ${instruction}`)
37
29
  } else {
38
30
  const msg = `> ${cltStatus.reason || "command line tools not installed yet."} install the latest xcode build tools...\r\n`
39
31
  console.log(msg)
40
32
  ondata({ raw: msg })
41
- await this._install(req, ondata)
33
+ await this._install(req, ondata, shouldRemoveCommandLineTools(cltStatus.reason))
42
34
  }
43
35
  }
36
+
37
+ const installer_url = "https://github.com/cocktailpeanut/bin/releases/download/homebrew/homebrew.zip"
38
+ //const installer_url = "https://github.com/Homebrew/brew/tarball/master"
39
+ const installer = "Homebrew.zip"
40
+
41
+ ondata({ raw: `downloading installer: ${installer_url}...\r\n` })
42
+ await this.kernel.bin.download(installer_url, installer, ondata)
43
+ console.log("## DOWNLOADED")
44
+
45
+ console.log("homebrewpath", this.kernel.bin.path("homebrew"))
46
+ await this.kernel.bin.rm("homebrew", ondata)
47
+
48
+ // 2. run the script
49
+ ondata({ raw: `unzipping installer: ${installer}...\r\n` })
50
+ await this.kernel.bin.unzip("Homebrew.zip", this.kernel.bin.path(), null, ondata)
51
+ await this.kernel.bin.rm("Homebrew.zip", ondata)
44
52
  //
45
53
  ondata({ raw: "installing gettext\r\n" })
46
54
  await this.kernel.bin.exec({ message: "brew install gettext --force-bottle", conda: { skip: true } }, (stream) => { ondata(stream) })
47
55
  //
48
56
  ondata({ raw: `Install finished\r\n` })
49
57
  }
50
- async _install(req, ondata) {
58
+ async _install(req, ondata, removeExisting = false) {
51
59
  // command line tools installed
52
60
  const msg = "> installing the latest xcode build tools...\r\n"
53
61
  console.log(msg)
54
62
  ondata({ raw: msg })
55
63
  // not installed or not installed properly
56
64
  // install xcode build tools
57
- await this.kernel.bin.exec({ sudo: true, conda: { skip: true }, message: "rm -rf /Library/Developer/CommandLineTools" }, (stream) => { ondata(stream) })
65
+ if (removeExisting) {
66
+ await this.kernel.bin.exec({ sudo: true, conda: { skip: true }, message: "rm -rf /Library/Developer/CommandLineTools" }, (stream) => { ondata(stream) })
67
+ }
58
68
  let sh_path = path.resolve(this.kernel.homedir, "xcode.sh")
59
69
  let src_path = path.resolve(__dirname, "xcode.sh")
60
70
  console.log({ sh_path, src_path })
@@ -68,16 +78,10 @@ class Brew {
68
78
  }
69
79
 
70
80
  async installed() {
81
+ const homebrewExists = await this.kernel.bin.exists("homebrew")
82
+ const cltStatus = homebrewExists ? await detectCommandLineTools() : { valid: false }
83
+ this.kernel.bin.brew_installed = homebrewExists && cltStatus.valid
71
84
  return this.kernel.bin.brew_installed
72
- /*
73
- let e = await this.kernel.bin.exists("homebrew")
74
-
75
- let { stdout }= await this.kernel.bin.exec({ message: "xcode-select -p" }, (stream) => { })
76
- let e2 = /(.*Library.*Developer.*CommandLineTools.*|.*Xcode.*Developer.*)/gi.test(stdout)
77
- console.log({ e, e2, stdout })
78
-
79
- return e && e2
80
- */
81
85
  }
82
86
 
83
87
  uninstall(req, ondata) {
@@ -96,4 +100,20 @@ class Brew {
96
100
  }
97
101
  }
98
102
  }
103
+
104
+ function needsManualXcodeAction(reason) {
105
+ return reason === "xcode license not accepted" || reason === "xcode first launch is incomplete"
106
+ }
107
+
108
+ function manualXcodeActionMessage(reason) {
109
+ if (reason === "xcode license not accepted") {
110
+ return "xcode license not accepted. open Xcode once and accept the license, then retry"
111
+ }
112
+ return "xcode first launch is incomplete. open Xcode once to finish setup, then retry"
113
+ }
114
+
115
+ function shouldRemoveCommandLineTools(reason) {
116
+ return typeof reason === "string" && reason.startsWith("command line tools version ") && reason.includes(" is below required ")
117
+ }
118
+
99
119
  module.exports = Brew
@@ -21,7 +21,6 @@ const LLVM = require('./llvm')
21
21
  const VS = require("./vs")
22
22
  const Cuda = require("./cuda")
23
23
  const Torch = require("./torch")
24
- const { detectCommandLineTools } = require('./xcode-tools')
25
24
  const { buildCondaListFromMeta } = require('./conda-meta')
26
25
  const { glob } = require('glob')
27
26
  const fakeUa = require('fake-useragent');
@@ -460,14 +459,7 @@ class Bin {
460
459
  this.installed.brew = new Set(brew)
461
460
 
462
461
 
463
- // check brew_installed
464
- let e = await this.kernel.bin.exists("homebrew")
465
- const cltStatus = await detectCommandLineTools({
466
- exec: (params) => this.exec(params, () => {})
467
- })
468
- console.log({ cltStatus })
469
- this.brew_installed = e && cltStatus.valid
470
-
462
+ this.brew_installed = await this.kernel.bin.exists("homebrew")
471
463
  console.log("brew_installed", this.brew_installed)
472
464
 
473
465
  }
@@ -1036,7 +1028,8 @@ class Bin {
1036
1028
  let r = requirements[i]
1037
1029
  let fingerprint = JSON.stringify(r)
1038
1030
  let installed
1039
- if (fingerprint in this.requirements_cache) {
1031
+ const canUseCache = r.name !== "brew" || r.type
1032
+ if (canUseCache && fingerprint in this.requirements_cache) {
1040
1033
  let relevant = this.relevant(r)
1041
1034
  requirements[i].relevant = relevant
1042
1035
  if (relevant) {
@@ -1063,7 +1056,9 @@ class Bin {
1063
1056
  requirements[i].dependencies = dependencies
1064
1057
  }
1065
1058
  installed = await this.check_installed(r, dependencies)
1066
- this.requirements_cache[fingerprint] = installed
1059
+ if (canUseCache) {
1060
+ this.requirements_cache[fingerprint] = installed
1061
+ }
1067
1062
  //if (installed) {
1068
1063
  // // cache if true
1069
1064
  // this.requirements_cache[fingerprint] = true
@@ -1,4 +1,5 @@
1
1
  const fs = require('fs')
2
+ const { execFile } = require('child_process')
2
3
  const semver = require('semver')
3
4
 
4
5
  const MIN_CLT_VERSION = '13.0'
@@ -13,19 +14,17 @@ const PACKAGE_MATCHERS = [
13
14
  /^com\.apple\.pkg\.Xcode$/
14
15
  ]
15
16
 
16
- async function detectCommandLineTools({ exec }) {
17
- if (typeof exec !== 'function') {
18
- throw new Error('detectCommandLineTools requires an exec function')
19
- }
20
-
21
- const run = (message) => exec({ message, conda: { skip: true } })
17
+ async function detectCommandLineTools({ exec } = {}) {
18
+ const run = (typeof exec === 'function')
19
+ ? (message) => exec({ message, conda: { skip: true } })
20
+ : (message) => execCommand(message)
22
21
 
23
22
  let selectResult
24
23
  try {
25
24
  selectResult = await run('xcode-select -p')
26
25
  } catch (err) {
27
26
  console.log('[CLT] xcode-select -p failed', err)
28
- return { valid: false, reason: 'xcode-select -p failed' }
27
+ return invalidFromError('xcode-select -p', err, 'xcode-select -p failed')
29
28
  }
30
29
 
31
30
  const developerPath = (selectResult && selectResult.stdout ? selectResult.stdout : '')
@@ -56,7 +55,7 @@ async function detectCommandLineTools({ exec }) {
56
55
  clangResult = await run('xcrun --find clang')
57
56
  } catch (err) {
58
57
  console.log('[CLT] xcrun --find clang failed', err)
59
- return { valid: false, reason: 'unable to locate clang via xcrun' }
58
+ return invalidFromError('xcrun --find clang', err, 'unable to locate clang via xcrun')
60
59
  }
61
60
 
62
61
  const clangStdout = clangResult && clangResult.stdout ? clangResult.stdout : ''
@@ -109,6 +108,42 @@ async function detectCommandLineTools({ exec }) {
109
108
  return status
110
109
  }
111
110
 
111
+ async function execCommand(message) {
112
+ const tokens = String(message || '').trim().split(/\s+/).filter(Boolean)
113
+ const file = tokens.shift()
114
+ if (!file) {
115
+ throw new Error('missing command')
116
+ }
117
+ return new Promise((resolve, reject) => {
118
+ execFile(file, tokens, {
119
+ timeout: 8000,
120
+ maxBuffer: 1024 * 1024,
121
+ }, (error, stdout, stderr) => {
122
+ if (error) {
123
+ error.stdout = stdout
124
+ error.stderr = stderr
125
+ reject(error)
126
+ return
127
+ }
128
+ resolve({ stdout })
129
+ })
130
+ })
131
+ }
132
+
133
+ function invalidFromError(command, error, fallbackReason) {
134
+ const stderr = error && typeof error.stderr === 'string' ? error.stderr : ''
135
+ if (error && error.killed && error.signal === 'SIGTERM') {
136
+ return { valid: false, reason: `${command} timed out` }
137
+ }
138
+ if (/license agreements/i.test(stderr)) {
139
+ return { valid: false, reason: 'xcode license not accepted' }
140
+ }
141
+ if (/runfirstlaunch|first launch/i.test(stderr)) {
142
+ return { valid: false, reason: 'xcode first launch is incomplete' }
143
+ }
144
+ return { valid: false, reason: fallbackReason }
145
+ }
146
+
112
147
  async function pkgInfoFor(run, pkgId) {
113
148
  try {
114
149
  const result = await run(`pkgutil --pkg-info=${pkgId}`)
package/kernel/index.js CHANGED
@@ -1063,9 +1063,6 @@ class Kernel {
1063
1063
  console.warn("Git init error:", err && err.message ? err.message : err)
1064
1064
  })
1065
1065
  this.shell.init().then(async () => {
1066
- this.bin.check({
1067
- bin: this.bin.preset("dev"),
1068
- })
1069
1066
  if (this.envs) {
1070
1067
  this.template.update({
1071
1068
  env: this.envs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "6.0.90",
3
+ "version": "6.0.92",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -5062,7 +5062,9 @@ class Server {
5062
5062
 
5063
5063
 
5064
5064
  await this.kernel.init({ port: this.port})
5065
- await Environment.init({}, this.kernel)
5065
+ if (this.kernel.homedir) {
5066
+ await Environment.init({}, this.kernel)
5067
+ }
5066
5068
  this.kernel.server_port = this.port
5067
5069
  this.kernel.peer.start(this.kernel)
5068
5070
 
@@ -260,6 +260,9 @@ document.addEventListener("DOMContentLoaded", async () => {
260
260
  } else if (packet.type === "result") {
261
261
  resolve()
262
262
  } else if (packet.type === "error") {
263
+ if (typeof packet.data === "string" && packet.data.includes("__PINOKIO_BLOCKER__")) {
264
+ return
265
+ }
263
266
  //term.write("\r\n" + packet.data + "\r\n")
264
267
  ModalInput({
265
268
  title: "Error",
@@ -279,12 +282,16 @@ document.addEventListener("DOMContentLoaded", async () => {
279
282
  document.querySelector("#status-screen").innerHTML = packet.data.html
280
283
  let audio = new Audio("/chime.mp3")
281
284
  audio.play()
282
- ModalInput({
285
+ await ModalInput({
283
286
  title: "Notice",
284
287
  type: "modal",
285
288
  description: packet.data.html,
286
289
  confirm: "OK",
287
290
  })
291
+ if (packet.data.blocker) {
292
+ location.href = document.querySelector("#callback").value
293
+ return
294
+ }
288
295
  }
289
296
  } else if (packet.type === "notify2") {
290
297
  console.log("HTML", packet.data)
@@ -8232,11 +8232,28 @@ body.dark .swal2-popup.pinokio-diff-modal .pinokio-modal-footer--commit .pinokio
8232
8232
  if (!column) {
8233
8233
  return
8234
8234
  }
8235
- const terminalId = normalizeTerminalId(column.dataset.sessionTerminalId || "")
8236
- if (!terminalId) {
8235
+ if (type === "terminal-input") {
8237
8236
  return
8238
8237
  }
8239
- if (type === "terminal-input") {
8238
+ if (type === "idle") {
8239
+ const workspacePath = normalizeIndex(column.dataset.sessionCwd || "")
8240
+ if (workspacePath) {
8241
+ fetchWorkspaceGitStatus(workspacePath, { force: true }).catch(() => {
8242
+ })
8243
+ }
8244
+ refreshSessionItems({
8245
+ sync: true,
8246
+ query: currentSessionQuery,
8247
+ workspaceKey: workspaceViewState.mode === "sessions" ? workspaceViewState.key : ""
8248
+ }).then((loaded) => {
8249
+ if (loaded || hasAttemptedSessionLoad) {
8250
+ refreshChooserRows({ autoLoad: false })
8251
+ }
8252
+ })
8253
+ return
8254
+ }
8255
+ const terminalId = normalizeTerminalId(column.dataset.sessionTerminalId || "")
8256
+ if (!terminalId) {
8240
8257
  return
8241
8258
  }
8242
8259
  if (type === "stream" || type === "start") {
@@ -8261,18 +8278,6 @@ body.dark .swal2-popup.pinokio-diff-modal .pinokio-modal-footer--commit .pinokio
8261
8278
  }
8262
8279
  return
8263
8280
  }
8264
- if (type === "idle") {
8265
- void refreshSessionItems({
8266
- sync: true,
8267
- query: currentSessionQuery,
8268
- workspaceKey: workspaceViewState.mode === "sessions" ? workspaceViewState.key : ""
8269
- }).then((loaded) => {
8270
- if (loaded || hasAttemptedSessionLoad) {
8271
- refreshChooserRows({ autoLoad: false })
8272
- }
8273
- })
8274
- return
8275
- }
8276
8281
  if (type === "disconnect") {
8277
8282
  const removedTemporary = removeTemporarySessionByTerminalId(terminalId)
8278
8283
  if (removedTemporary) {
@@ -9689,7 +9694,9 @@ body.dark .swal2-popup.pinokio-diff-modal .pinokio-modal-footer--commit .pinokio
9689
9694
  setColumnLayout()
9690
9695
  ensureLayout()
9691
9696
  refreshChooserRows()
9692
- void refreshSessionItems({
9697
+ fetchWorkspaceGitStatus(targetWorkspaceKey, { force: true }).catch(() => {
9698
+ })
9699
+ refreshSessionItems({
9693
9700
  query: "",
9694
9701
  workspaceKey: targetWorkspaceKey
9695
9702
  }).then((loaded) => {
@@ -10020,6 +10027,10 @@ body.dark .swal2-popup.pinokio-diff-modal .pinokio-modal-footer--commit .pinokio
10020
10027
  )
10021
10028
  topSessionRefreshButton.disabled = true
10022
10029
  try {
10030
+ if (workspaceKey) {
10031
+ fetchWorkspaceGitStatus(workspaceKey, { force: true }).catch(() => {
10032
+ })
10033
+ }
10023
10034
  const refreshed = await refreshSessionItems({
10024
10035
  sync: true,
10025
10036
  limit: getCurrentSessionRefreshLimit(),