pinokiod 7.3.10 → 7.3.12

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 (51) hide show
  1. package/kernel/bin/brew.js +12 -2
  2. package/kernel/bin/caddy.js +24 -20
  3. package/kernel/bin/huggingface.js +2 -2
  4. package/kernel/bin/setup.js +2 -3
  5. package/kernel/bin/uv.js +13 -6
  6. package/kernel/connect/index.js +5 -1
  7. package/kernel/connect/providers/huggingface/index.js +213 -75
  8. package/kernel/environment.js +16 -1
  9. package/kernel/router/localhost_home_router.js +7 -0
  10. package/kernel/shell.js +1 -5
  11. package/kernel/util.js +1 -0
  12. package/package.json +1 -1
  13. package/server/index.js +75 -33
  14. package/server/public/common.js +45 -71
  15. package/server/public/install.js +20 -2
  16. package/server/public/layout.js +1 -1
  17. package/server/public/style.css +1432 -511
  18. package/server/public/tab-link-popover.css +162 -18
  19. package/server/public/tab-link-popover.js +230 -21
  20. package/server/public/task-launcher.css +121 -84
  21. package/server/public/terminal-settings.js +121 -45
  22. package/server/public/universal-launcher.css +42 -33
  23. package/server/public/urldropdown.css +284 -0
  24. package/server/views/app.ejs +1608 -321
  25. package/server/views/autolaunch.ejs +4 -5
  26. package/server/views/checkpoints.ejs +223 -50
  27. package/server/views/connect/huggingface.ejs +406 -325
  28. package/server/views/connect.ejs +0 -1
  29. package/server/views/github.ejs +277 -324
  30. package/server/views/index.ejs +53 -1
  31. package/server/views/install.ejs +119 -19
  32. package/server/views/logs.ejs +10 -9
  33. package/server/views/net.ejs +341 -64
  34. package/server/views/network.ejs +85 -63
  35. package/server/views/partials/main_sidebar.ejs +237 -12
  36. package/server/views/plugins.ejs +141 -3
  37. package/server/views/settings.ejs +103 -7
  38. package/server/views/setup.ejs +0 -5
  39. package/server/views/skills.ejs +0 -1
  40. package/server/views/task_list.ejs +0 -1
  41. package/server/views/terminal.ejs +142 -1
  42. package/server/views/terminals.ejs +346 -6
  43. package/server/views/tools.ejs +828 -1691
  44. package/test/caddy-install.test.js +53 -0
  45. package/test/connect-setup.test.js +16 -0
  46. package/test/github-connection.test.js +1 -1
  47. package/test/huggingface-bin.test.js +4 -4
  48. package/test/huggingface-connect.test.js +73 -0
  49. package/test/main-sidebar.test.js +31 -0
  50. package/test/shell-run-template.test.js +5 -1
  51. package/test/uv-bin.test.js +29 -0
@@ -51,7 +51,11 @@ class Brew {
51
51
  await this.kernel.bin.rm("Homebrew.zip", ondata)
52
52
  //
53
53
  ondata({ raw: "installing gettext\r\n" })
54
- await this.kernel.bin.exec({ message: "brew install gettext --force-bottle", conda: { skip: true } }, (stream) => { ondata(stream) })
54
+ await this.kernel.bin.exec({
55
+ message: "brew install gettext --force-bottle",
56
+ conda: { skip: true },
57
+ env: this.nonInteractiveEnv()
58
+ }, (stream) => { ondata(stream) })
55
59
  //
56
60
  ondata({ raw: `Install finished\r\n` })
57
61
  }
@@ -96,7 +100,13 @@ class Brew {
96
100
  HOMEBREW_PREFIX: this.kernel.bin.path("homebrew"),
97
101
  HOMEBREW_CELLAR: this.kernel.bin.path("homebrew", "Cellar"),
98
102
  HOMEBREW_REPOSITORY: this.kernel.bin.path("homebrew"),
99
- HOMEBREW_CACHE: this.kernel.bin.path("homebrew", "cache")
103
+ HOMEBREW_CACHE: this.kernel.bin.path("homebrew", "cache"),
104
+ ...this.nonInteractiveEnv()
105
+ }
106
+ }
107
+ nonInteractiveEnv() {
108
+ return {
109
+ HOMEBREW_NO_ASK: "1"
100
110
  }
101
111
  }
102
112
  }
@@ -5,6 +5,12 @@ const semver = require('semver')
5
5
  const kill = require('kill-sync')
6
6
  const Util = require('../util')
7
7
 
8
+ const ADMIN_READY_TIMEOUT_MS = 30000
9
+ const ADMIN_START_TIMEOUT_MS = 5000
10
+ const ADMIN_POLL_INTERVAL_MS = 250
11
+
12
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms))
13
+
8
14
  class Caddy {
9
15
  description = "Installs Caddy for automatic local HTTPS and network sharing. You may be asked to approve certificate setup."
10
16
  cmd() {
@@ -104,17 +110,7 @@ class Caddy {
104
110
  }
105
111
  })
106
112
  })
107
- let admin_running = false
108
- const admin_wait_started = Date.now()
109
- while ((Date.now() - admin_wait_started) < 5000) {
110
- admin_running = await this.running()
111
- if (admin_running) {
112
- break
113
- }
114
- await new Promise((resolve) => {
115
- setTimeout(resolve, 250)
116
- })
117
- }
113
+ const admin_running = await this.waitForAdmin(ADMIN_START_TIMEOUT_MS)
118
114
  if (!admin_running) {
119
115
  console.log("caddy admin unavailable after startup")
120
116
  if (startup_output.length > 0) {
@@ -132,6 +128,16 @@ class Caddy {
132
128
  // this.kernel.refresh(true)
133
129
  }
134
130
  }
131
+ async waitForAdmin(timeout = ADMIN_READY_TIMEOUT_MS, interval = ADMIN_POLL_INTERVAL_MS) {
132
+ const started = Date.now()
133
+ while ((Date.now() - started) < timeout) {
134
+ if (await this.running()) {
135
+ return true
136
+ }
137
+ await sleep(interval)
138
+ }
139
+ return false
140
+ }
135
141
  async install(req, ondata, kernel, id) {
136
142
  // let fullpath = path.resolve(kernel.homedir, "ENVIRONMENT")
137
143
  // await Util.update_env(fullpath, {
@@ -161,15 +167,13 @@ class Caddy {
161
167
  })
162
168
 
163
169
  // wait until running
164
- await new Promise((resolve, reject) => {
165
- ondata({ raw: "waiting until caddy server is up...\r\n" })
166
- setInterval(async () => {
167
- let running = await this.running()
168
- if (running) {
169
- resolve()
170
- }
171
- }, 2000)
172
- })
170
+ ondata({ raw: "waiting until caddy server is up...\r\n" })
171
+ const running = await this.waitForAdmin()
172
+ if (!running) {
173
+ const message = "caddy admin did not become available. Certificate trust may be waiting for system approval."
174
+ ondata({ raw: `${message}\r\n` })
175
+ throw new Error(message)
176
+ }
173
177
  ondata({ raw: "caddy is running!\r\n" })
174
178
 
175
179
  if (this.kernel.platform === "win32") {
@@ -3,7 +3,7 @@ class Huggingface {
3
3
  description = "Installs huggingface_hub for downloading models and assets from Hugging Face."
4
4
  cmd() {
5
5
  //return 'huggingface_hub "hf-xet!=1.1.10"'
6
- return 'huggingface_hub=1.0.1'
6
+ return 'huggingface_hub=1.20.1'
7
7
  }
8
8
  async install(req, ondata) {
9
9
  await this.kernel.bin.exec({
@@ -18,7 +18,7 @@ class Huggingface {
18
18
  let version = this.kernel.bin.installed.conda_versions.huggingface_hub
19
19
  if (version) {
20
20
  let coerced = semver.coerce(version)
21
- if (coerced && semver.eq(coerced, "1.0.1")) {
21
+ if (coerced && semver.eq(coerced, "1.20.1")) {
22
22
  return true
23
23
  }
24
24
  }
@@ -280,7 +280,7 @@ module.exports = {
280
280
  { name: "git", },
281
281
  { name: "uv", },
282
282
  { name: "node", },
283
- { name: "caddy", },
283
+ { name: "huggingface" },
284
284
  { name: "py", },
285
285
  { name: "bluefairy" },
286
286
  ])
@@ -293,8 +293,7 @@ module.exports = {
293
293
  zip_cmd,
294
294
  "huggingface",
295
295
  "uv",
296
- "git",
297
- "caddy"
296
+ "git"
298
297
  ]
299
298
  }
300
299
  },
package/kernel/bin/uv.js CHANGED
@@ -1,11 +1,14 @@
1
- const fs = require('fs')
2
- const fetch = require('cross-fetch')
3
- const { rimraf } = require('rimraf')
4
- const decompress = require('decompress');
1
+ const UV_VERSION = "0.11.23"
2
+
3
+ const normalizeVersion = (version) => {
4
+ const match = String(version || "").match(/^\d+\.\d+\.\d+/)
5
+ return match ? match[0] : null
6
+ }
7
+
5
8
  class UV {
6
9
  description = "Installs uv, a fast Python package and virtual environment manager."
7
10
  cmd() {
8
- return "uv"
11
+ return `uv=${UV_VERSION}`
9
12
  }
10
13
  async install(req, ondata) {
11
14
  await this.kernel.bin.exec({
@@ -16,7 +19,11 @@ class UV {
16
19
  }, ondata)
17
20
  }
18
21
  async installed() {
19
- return this.kernel.bin.installed.conda.has("uv")
22
+ if (!this.kernel.bin.installed.conda.has("uv")) {
23
+ return false
24
+ }
25
+ let version = this.kernel.bin.installed.conda_versions && this.kernel.bin.installed.conda_versions.uv
26
+ return normalizeVersion(version) === UV_VERSION
20
27
  }
21
28
  async uninstall(req, ondata) {
22
29
  await this.kernel.bin.exec({
@@ -9,7 +9,11 @@ class Connect {
9
9
  this.config = config
10
10
  this.clients = {}
11
11
  for(let name in this.config) {
12
- this.clients[name] = new Backend(kernel, name, this.config[name])
12
+ if (name === "huggingface") {
13
+ this.clients[name] = new Huggingface(kernel, this.config[name])
14
+ } else {
15
+ this.clients[name] = new Backend(kernel, name, this.config[name])
16
+ }
13
17
  }
14
18
  }
15
19
  async profile(provider, req) {
@@ -1,97 +1,235 @@
1
- const fetch = require('cross-fetch')
1
+ const { execFile, spawn } = require('child_process')
2
2
  const fs = require('fs')
3
3
  const path = require('path')
4
+ const fetch = require('cross-fetch')
5
+ const Environment = require('../../../environment')
6
+
7
+ const stripAnsi = (value) => String(value || "").replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "")
8
+
4
9
  class Huggingface {
5
- CLIENT_ID = 'e90d4a4d-68a6-4c12-ae71-64756b5918de'
6
- REDIRECT_URI = 'https://pinokio.localhost/connect/huggingface'
7
- HF_OAUTH_URL = 'https://huggingface.co/oauth/authorize'
8
- HF_TOKEN_URL = 'https://huggingface.co/oauth/token'
9
- HF_API_URL = 'https://huggingface.co/api/whoami-v2'
10
- constructor(kernel) {
10
+ constructor(kernel, config) {
11
11
  this.kernel = kernel
12
+ this.config = config
13
+ this.loginSession = null
12
14
  }
13
15
  async readme() {
14
16
  return ""
15
17
  }
16
- async persist(auth) {
17
- console.log("PERSIST", auth)
18
- this.auth = auth
19
- this.auth.expires_at = Date.now() + (this.auth.expires_in * 1000);
20
- let authPath = this.kernel.path('connect/huggingface.json')
21
- await fs.promises.mkdir(this.kernel.path("connect"), { recursive: true }).catch((e) => { })
22
- await fs.promises.writeFile(authPath, JSON.stringify(this.auth, null, 2))
23
-
24
- // huggingface-cli login
25
- await this.kernel.exec({
26
- message: `hf auth login --token ${this.auth.access_token} --add-to-git-credential`
27
- }, (stream) => {
28
- process.stdout.write(stream.raw)
18
+ defaultTokenPath() {
19
+ return this.kernel.path("cache", "HF_AUTH", "token")
20
+ }
21
+ async authEnv() {
22
+ let systemEnv = {}
23
+ try {
24
+ systemEnv = await Environment.get(this.kernel.homedir, this.kernel)
25
+ } catch (_) {
26
+ systemEnv = {}
27
+ }
28
+ const env = Object.assign({}, process.env, systemEnv)
29
+ if (!env.HF_TOKEN_PATH) {
30
+ env.HF_TOKEN_PATH = this.defaultTokenPath()
31
+ }
32
+ if (!env.HF_HUB_DISABLE_UPDATE_CHECK) {
33
+ env.HF_HUB_DISABLE_UPDATE_CHECK = "1"
34
+ }
35
+ delete env.HF_TOKEN
36
+ delete env.HUGGING_FACE_HUB_TOKEN
37
+ await fs.promises.mkdir(path.dirname(env.HF_TOKEN_PATH), { recursive: true }).catch(() => {})
38
+ return env
39
+ }
40
+ hfPath() {
41
+ const candidates = this.kernel.platform === "win32" ? [
42
+ this.kernel.path("bin", "miniconda", "Scripts", "hf.exe"),
43
+ this.kernel.path("bin", "miniconda", "Scripts", "hf"),
44
+ this.kernel.path("bin", "miniconda", "bin", "hf"),
45
+ ] : [
46
+ this.kernel.path("bin", "miniconda", "bin", "hf"),
47
+ ]
48
+ for (const candidate of candidates) {
49
+ if (fs.existsSync(candidate)) {
50
+ return candidate
51
+ }
52
+ }
53
+ return candidates[0]
54
+ }
55
+ async runHf(args) {
56
+ const env = await this.authEnv()
57
+ return new Promise((resolve, reject) => {
58
+ execFile(this.hfPath(), args, {
59
+ cwd: this.kernel.homedir,
60
+ env,
61
+ maxBuffer: 1024 * 1024,
62
+ }, (error, stdout, stderr) => {
63
+ if (error) {
64
+ error.stdout = stdout
65
+ error.stderr = stderr
66
+ reject(error)
67
+ } else {
68
+ resolve({ stdout, stderr, env })
69
+ }
70
+ })
29
71
  })
30
72
  }
31
- async destroy() {
32
- await fs.promises.rm(this.kernel.path("connect/huggingface.json"))
33
- this.auth = null
73
+ serializeLoginSession(session) {
74
+ if (!session) {
75
+ return null
76
+ }
77
+ return {
78
+ status: session.status,
79
+ login: session.login,
80
+ error: session.error,
81
+ token_path: session.tokenPath,
82
+ }
34
83
  }
35
- async sync() {
36
- // check if auth exists
37
- // if not, throw error
38
- let authPath = this.kernel.path('connect/huggingface.json')
39
- this.auth = (await this.kernel.loader.load(authPath)).resolved
40
- if (!this.auth) {
84
+ parseDeviceLogin(output) {
85
+ const clean = stripAnsi(output).replace(/\s+/g, " ")
86
+ for (const line of stripAnsi(output).split(/\r?\n/)) {
87
+ if (!line.trim()) {
88
+ continue
89
+ }
90
+ let event
91
+ try {
92
+ event = JSON.parse(line)
93
+ } catch (_) {
94
+ continue
95
+ }
96
+ if (event && event.event === "device_code" && event.user_code) {
97
+ return {
98
+ verification_uri_complete: event.verification_uri_complete || event.verification_uri,
99
+ user_code: event.user_code,
100
+ expires_in: event.expires_in || null,
101
+ interval: event.interval || null,
102
+ }
103
+ }
104
+ }
105
+ const urlMatch = clean.match(/open\s+(https?:\/\/\S+)/i)
106
+ const codeMatch = clean.match(/enter the code\s+([A-Z0-9-]+)/i)
107
+ const expiresMatch = clean.match(/expires in\s+(\d+)\s+seconds/i)
108
+ if (!urlMatch || !codeMatch) {
41
109
  return null
42
110
  }
43
- if (!this.auth.refresh_token) {
44
- console.log("no refresh token")
111
+ return {
112
+ verification_uri_complete: urlMatch[1].replace(/[).,]+$/, ""),
113
+ user_code: codeMatch[1],
114
+ expires_in: expiresMatch ? Number(expiresMatch[1]) : null,
115
+ }
116
+ }
117
+ async login() {
118
+ if (this.loginSession && this.loginSession.status === "pending") {
119
+ return this.serializeLoginSession(this.loginSession)
120
+ }
121
+ const env = await this.authEnv()
122
+ const session = {
123
+ status: "pending",
124
+ login: null,
125
+ error: null,
126
+ output: "",
127
+ tokenPath: env.HF_TOKEN_PATH,
128
+ child: null,
129
+ readyResolved: false,
130
+ }
131
+ this.loginSession = session
132
+ let resolveReady
133
+ const ready = new Promise((resolve) => {
134
+ resolveReady = resolve
135
+ })
136
+ const markReady = () => {
137
+ if (!session.readyResolved) {
138
+ session.readyResolved = true
139
+ resolveReady()
140
+ }
141
+ }
142
+ const appendOutput = (chunk) => {
143
+ session.output += stripAnsi(chunk)
144
+ const parsed = this.parseDeviceLogin(session.output)
145
+ if (parsed && !session.login) {
146
+ session.login = parsed
147
+ markReady()
148
+ }
149
+ }
150
+ const child = spawn(this.hfPath(), ["auth", "login", "--format", "agent", "--force"], {
151
+ cwd: this.kernel.homedir,
152
+ env,
153
+ windowsHide: false,
154
+ })
155
+ session.child = child
156
+ child.stdout.on("data", appendOutput)
157
+ child.stderr.on("data", appendOutput)
158
+ child.on("error", (error) => {
159
+ session.status = "error"
160
+ session.error = error.message
161
+ markReady()
162
+ })
163
+ const readyTimer = setTimeout(() => {
164
+ if (session.status === "pending" && !session.login) {
165
+ session.status = "error"
166
+ session.error = "Timed out waiting for Hugging Face login code."
167
+ child.kill()
168
+ markReady()
169
+ }
170
+ }, 15000)
171
+ child.on("close", (code) => {
172
+ clearTimeout(readyTimer)
173
+ if (code === 0) {
174
+ session.status = "success"
175
+ } else {
176
+ session.status = "error"
177
+ session.error = session.error || stripAnsi(session.output).trim() || `hf auth login exited with code ${code}`
178
+ }
179
+ markReady()
180
+ })
181
+ await ready
182
+ if (session.status === "error") {
183
+ return this.serializeLoginSession(session)
184
+ }
185
+ return this.serializeLoginSession(session)
186
+ }
187
+ async keys() {
188
+ if (this.loginSession && this.loginSession.status === "pending") {
189
+ return this.serializeLoginSession(this.loginSession)
190
+ }
191
+ try {
192
+ const { stdout, env } = await this.runHf(["auth", "token", "--format", "quiet"])
193
+ const access_token = stripAnsi(stdout).trim().split(/\r?\n/).find(Boolean)
194
+ if (!access_token) {
195
+ return null
196
+ }
197
+ return { access_token, token_path: env.HF_TOKEN_PATH }
198
+ } catch (error) {
45
199
  return null
46
200
  }
47
-
48
- // check if auth has expired
49
- // if expired, refresh and return
50
- if (Date.now() < this.auth.expires_at) {
51
- return
52
- }
53
- console.log("auth expired. refresh....", JSON.stringify({ auth: this.auth, id: this.CLIENT_ID }, null, 2))
54
- // expired — refresh
55
- const response = await fetch(this.HF_TOKEN_URL, {
56
- method: 'POST',
57
- headers: { 'Content-Type': 'application/x-www-form-urlencoded', },
58
- body: new URLSearchParams({
59
- grant_type: 'refresh_token',
60
- refresh_token: this.auth.refresh_token,
61
- client_id: this.CLIENT_ID,
62
- })
201
+ }
202
+ async profile() {
203
+ const keys = await this.keys()
204
+ if (!keys || !keys.access_token) {
205
+ return null
206
+ }
207
+ const response = await fetch(this.config.profile.url, {
208
+ headers: {
209
+ 'Authorization': 'Bearer ' + keys.access_token
210
+ }
63
211
  }).then((res) => {
212
+ if (!res.ok) {
213
+ throw new Error(`Hugging Face profile request failed with ${res.status}`)
214
+ }
64
215
  return res.json()
65
- });
66
- await this.persist(response)
67
- return this.auth
68
- }
69
- async keys() {
70
- await this.sync()
71
- return this.auth
216
+ })
217
+ const connectPath = this.kernel.path("connect", "huggingface")
218
+ await fs.promises.mkdir(connectPath, { recursive: true }).catch(() => {})
219
+ await this.config.profile.cache(response, connectPath).catch(() => {})
220
+ return this.config.profile.render(response)
72
221
  }
73
- async login(req) {
74
- console.log("huggingface login", req)
75
- await this.persist(req)
76
- return this.auth
222
+ async destroy() {
223
+ if (this.loginSession && this.loginSession.child && this.loginSession.status === "pending") {
224
+ this.loginSession.child.kill()
225
+ }
226
+ this.loginSession = null
227
+ await this.runHf(["auth", "logout"]).catch(() => {})
228
+ await fs.promises.rm(this.kernel.path("connect", "huggingface"), { recursive: true, force: true }).catch(() => {})
77
229
  }
78
- // async login (req) {
79
- // const authHeader = 'Basic ' + Buffer.from(`${this.id}:`).toString('base64');
80
- // const response = await fetch('https://api.x.com/2/oauth2/token', {
81
- // method: 'POST',
82
- // headers: {
83
- // 'Content-Type': 'application/json',
84
- //// 'Authorization': authHeader
85
- // },
86
- // body: JSON.stringify(req.payload)
87
- // }).then((res) => {
88
- // return res.json()
89
- // });
90
- // await this.persist(response)
91
- // return this.auth
92
- // }
93
- async logout (req) {
94
- await this.destroy()
230
+ async logout() {
231
+ await this.destroy()
95
232
  }
96
233
  }
234
+
97
235
  module.exports = Huggingface
@@ -401,6 +401,21 @@ const ENVS = async () => {
401
401
  "#",
402
402
  "##########################################################################",
403
403
  ],
404
+ }, {
405
+ type: ["system"],
406
+ key: "HF_TOKEN_PATH",
407
+ val: "./cache/HF_AUTH/token",
408
+ comment: [
409
+ "##########################################################################",
410
+ "#",
411
+ "# HF_TOKEN_PATH",
412
+ "#",
413
+ "# Hugging Face authentication token file",
414
+ "# Pinokio keeps this shared across apps so each app can customize HF_HOME",
415
+ "# for model/cache files without changing the logged-in Hugging Face account.",
416
+ "#",
417
+ "##########################################################################",
418
+ ],
404
419
  }, {
405
420
  type: ["system", "app"],
406
421
  key: "TORCH_HOME",
@@ -535,7 +550,7 @@ const init_folders = async (homedir, kernel) => {
535
550
  if (is_absolute || is_relative) {
536
551
 
537
552
  // skip condarc and pipconfig => special case
538
- if (["PIP_CONFIG_FILE", "CONDARC"].includes(key)) {
553
+ if (["PIP_CONFIG_FILE", "CONDARC", "HF_TOKEN_PATH"].includes(key)) {
539
554
  continue
540
555
  }
541
556
  // it's a path
@@ -20,6 +20,13 @@ class LocalhostHomeRouter {
20
20
  ]
21
21
  }
22
22
  },
23
+ "pki": {
24
+ "certificate_authorities": {
25
+ "local": {
26
+ "install_trust": false
27
+ }
28
+ }
29
+ },
23
30
 
24
31
  "http": {
25
32
  "servers": {
package/kernel/shell.js CHANGED
@@ -208,11 +208,6 @@ class Shell {
208
208
  let system_env = await Environment.get(this.kernel.homedir, this.kernel)
209
209
  this.env = Object.assign(this.env, system_env)
210
210
 
211
- let hf_keys = await this.kernel.connect.keys("huggingface")
212
- if (hf_keys && hf_keys.access_token) {
213
- this.env.HF_TOKEN = hf_keys.access_token
214
- }
215
-
216
211
  const parentPath = (params.$parent && params.$parent.path) ? path.resolve(params.$parent.path) : null
217
212
  const apiRoot = path.resolve(this.kernel.path("api")) + path.sep
218
213
  const parentStat = parentPath ? await fs.promises.stat(parentPath).catch(() => null) : null
@@ -295,6 +290,7 @@ class Shell {
295
290
  }
296
291
 
297
292
  setDefaultEnvValue(this.env, "HF_HUB_DISABLE_UPDATE_CHECK", "1")
293
+ setDefaultEnvValue(this.env, "HF_TOKEN_PATH", path.resolve(this.kernel.homedir, "cache", "HF_AUTH", "token"))
298
294
 
299
295
  if (this.platform === "win32") {
300
296
  // Hugging Face file symlinks regularly fail on non-admin Windows setups.
package/kernel/util.js CHANGED
@@ -1208,6 +1208,7 @@ const rewrite_localhost= (kernel, obj, source) => {
1208
1208
  }
1209
1209
  } else {
1210
1210
  let proxyPort = kernel.peer.info[kernel.peer.host].port_mapping["" + port]
1211
+ if (!proxyPort) return url
1211
1212
  u.hostname = sourceIp;
1212
1213
  u.port = proxyPort;
1213
1214
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.3.10",
3
+ "version": "7.3.12",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {