pinokiod 8.0.39 → 8.0.41

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 (37) hide show
  1. package/kernel/api/hf/index.js +2 -2
  2. package/kernel/api/index.js +12 -3
  3. package/kernel/connect/index.js +2 -2
  4. package/kernel/connect/providers/huggingface/index.js +14 -5
  5. package/kernel/index.js +14 -2
  6. package/kernel/shell.js +3 -2
  7. package/kernel/shells.js +6 -0
  8. package/kernel/vault/constants.js +13 -0
  9. package/kernel/vault/hash_worker.js +9 -2
  10. package/kernel/vault/index.js +1856 -316
  11. package/kernel/vault/registry.js +526 -52
  12. package/kernel/vault/snapshot.js +29 -0
  13. package/kernel/vault/sweeper.js +480 -210
  14. package/kernel/vault/walker.js +142 -0
  15. package/package.json +1 -1
  16. package/server/index.js +79 -90
  17. package/server/lib/privacy_filter_cache.js +4 -1
  18. package/server/public/storage-size.js +12 -0
  19. package/server/public/style.css +13 -0
  20. package/server/public/tab-link-popover.js +3 -0
  21. package/server/public/vault-nav.js +96 -0
  22. package/server/public/vault.css +1079 -0
  23. package/server/public/vault.js +1835 -0
  24. package/server/views/app.ejs +93 -16
  25. package/server/views/connect/huggingface.ejs +7 -9
  26. package/server/views/partials/main_sidebar.ejs +6 -1
  27. package/server/views/partials/vault_workspace.ejs +55 -0
  28. package/server/views/vault.ejs +5 -1532
  29. package/server/views/vault_app.ejs +22 -0
  30. package/test/hf-api.test.js +4 -2
  31. package/test/huggingface-connect.test.js +7 -11
  32. package/test/huggingface-token-validity.test.js +135 -0
  33. package/test/plugin-action-functions.test.js +19 -0
  34. package/test/privacy-filter-cache.test.js +1 -0
  35. package/test/vault-engine.test.js +1276 -26
  36. package/test/vault-sweep.test.js +1043 -35
  37. package/test/vault-ui.test.js +2184 -65
@@ -214,7 +214,7 @@ class HF {
214
214
 
215
215
  if (!force) {
216
216
  const existing = await connect.keys("huggingface", authContext)
217
- if (existing && existing.access_token) {
217
+ if (existing && existing.access_token && await connect.connected("huggingface", { timeout: 5000 }, authContext)) {
218
218
  return {
219
219
  status: "success",
220
220
  already_logged_in: true,
@@ -273,7 +273,7 @@ class HF {
273
273
  const startedAt = Date.now()
274
274
  while (Date.now() - startedAt < timeout) {
275
275
  const keys = await connect.keys("huggingface", authContext)
276
- if (keys && keys.access_token) {
276
+ if (keys && keys.access_token && await connect.connected("huggingface", { timeout: 5000 }, authContext)) {
277
277
  if (useModal) {
278
278
  await this.closeLoginModal(req, ondata, kernel)
279
279
  }
@@ -31,6 +31,7 @@ class Api {
31
31
  this.listeners = {}
32
32
  this.counter = 0;
33
33
  this.running = {}
34
+ this.running_paths = {}
34
35
  this.done = {}
35
36
  this.waiter = {}
36
37
  this.proxies = {}
@@ -419,12 +420,19 @@ class Api {
419
420
  delete this.resolved_actions[id]
420
421
  }
421
422
  }
423
+ clearRunning(requestOrId) {
424
+ const id = typeof requestOrId === "string" ? requestOrId : this.requestId(requestOrId)
425
+ if (!id) return
426
+ delete this.running[id]
427
+ delete this.running_paths[id]
428
+ }
422
429
  setRunning(request, done) {
423
430
  const id = this.requestId(request)
424
431
  if (!id) {
425
432
  return
426
433
  }
427
434
  this.running[id] = true
435
+ this.running_paths[id] = request.path || null
428
436
  this.done[id] = done
429
437
  if (this.kernel && typeof this.kernel.markAppLaunchStarted === "function") {
430
438
  this.kernel.markAppLaunchStarted(request.path)
@@ -634,7 +642,7 @@ class Api {
634
642
  if (req.params.id) stoppedKeys.add(req.params.id)
635
643
  stoppedKeys.add(requestPath)
636
644
  for (const key of stoppedKeys) {
637
- delete this.running[key]
645
+ this.clearRunning(key)
638
646
  delete this.kernel.memory.local[key]
639
647
  }
640
648
  if (this.kernel && typeof this.kernel.markAppLaunchStopped === "function") {
@@ -1994,7 +2002,7 @@ class Api {
1994
2002
  })
1995
2003
  } catch (e) {
1996
2004
  this.clearResolvedAction(request)
1997
- delete this.running[this.requestId(request)]
2005
+ this.clearRunning(request)
1998
2006
  if (this.kernel && typeof this.kernel.markAppLaunchFailed === "function") {
1999
2007
  this.kernel.markAppLaunchFailed(request.path, e)
2000
2008
  }
@@ -2008,7 +2016,7 @@ class Api {
2008
2016
 
2009
2017
  if (!Array.isArray(steps) || steps.length === 0) {
2010
2018
  this.clearResolvedAction(request)
2011
- delete this.running[this.requestId(request)]
2019
+ this.clearRunning(request)
2012
2020
  if (this.kernel && typeof this.kernel.markAppLaunchFailed === "function") {
2013
2021
  this.kernel.markAppLaunchFailed(request.path, new Error(`missing or invalid attribute: ${actionKey}`))
2014
2022
  }
@@ -2070,6 +2078,7 @@ class Api {
2070
2078
  let id = request.id ? request.id : request.method
2071
2079
  if (request.id) {
2072
2080
  this.running[request.id] = true
2081
+ delete this.running_paths[request.id]
2073
2082
  }
2074
2083
  try {
2075
2084
  resolved.dirname = resolved.dirname
@@ -42,9 +42,9 @@ class Connect {
42
42
  let res = await this.clients[provider].keys(context)
43
43
  return res
44
44
  }
45
- async connected(provider, options) {
45
+ async connected(provider, options, context) {
46
46
  if (this.clients[provider] && this.clients[provider].connected) {
47
- return await this.clients[provider].connected(options)
47
+ return await this.clients[provider].connected(options, context)
48
48
  }
49
49
  return false
50
50
  }
@@ -32,10 +32,11 @@ class Huggingface {
32
32
  try {
33
33
  const stat = await fs.promises.lstat(tokenPath)
34
34
  if (stat.isDirectory()) {
35
- await fs.promises.rm(tokenPath, { recursive: true, force: true })
35
+ await fs.promises.rmdir(tokenPath)
36
36
  }
37
37
  } catch (error) {
38
- if (!error || error.code !== "ENOENT") {
38
+ if (error.code !== "ENOENT") {
39
+ error.tokenPath = tokenPath
39
40
  throw error
40
41
  }
41
42
  }
@@ -45,6 +46,7 @@ class Huggingface {
45
46
  let env
46
47
  if (context.parentPath) {
47
48
  env = await Environment.get2(context.parentPath, this.kernel)
49
+ if (env.HF_TOKEN_PATH === this.kernel.path("cache", "HF_AUTH", "anonymous")) env.HF_TOKEN_PATH = this.defaultTokenPath()
48
50
  } else {
49
51
  let systemEnv = {}
50
52
  try {
@@ -96,6 +98,7 @@ class Huggingface {
96
98
  maxBuffer: 1024 * 1024,
97
99
  }, (error, stdout, stderr) => {
98
100
  if (error) {
101
+ error.tokenPath = env.HF_TOKEN_PATH
99
102
  error.stdout = stdout
100
103
  error.stderr = stderr
101
104
  reject(error)
@@ -244,11 +247,17 @@ class Huggingface {
244
247
  return null
245
248
  }
246
249
  }
247
- async connected(options = {}) {
250
+ async connected(options = {}, context = {}) {
248
251
  try {
249
- await this.runHf(["auth", "whoami", "--format", "quiet"], options)
252
+ const result = await this.runHf(["auth", "whoami", "--format", "quiet"], options, context)
253
+ if (this.kernel.envs && result.env.HF_TOKEN_PATH === this.defaultTokenPath()) {
254
+ this.kernel.envs.HF_TOKEN_PATH = result.env.HF_TOKEN_PATH
255
+ }
250
256
  return true
251
- } catch (_) {
257
+ } catch (error) {
258
+ if (this.kernel.envs && error.tokenPath === this.defaultTokenPath()) {
259
+ this.kernel.envs.HF_TOKEN_PATH = this.kernel.path("cache", "HF_AUTH", "anonymous")
260
+ }
252
261
  return false
253
262
  }
254
263
  }
package/kernel/index.js CHANGED
@@ -1221,10 +1221,22 @@ class Kernel {
1221
1221
  // discovery walk (spec/requirements/shared-model-store.md, trigger 5).
1222
1222
  this.vault = new Vault(this)
1223
1223
  if (this.homedir) {
1224
- this.vault.init().then((result) => {
1225
- if (result && result.enabled) return this.vault.verify()
1224
+ this.vault.ready = this.vault.init({ existingOnly: true }).then(async (result) => {
1225
+ if (result && result.enabled && this.vault.initialized) {
1226
+ this.vault.verificationPending = true
1227
+ await this.vault.runExclusive(async () => {
1228
+ await this.vault.verify()
1229
+ await this.vault.registry.flush()
1230
+ })
1231
+ this.vault.verificationPending = false
1232
+ }
1233
+ return result
1226
1234
  }).catch((err) => {
1235
+ // Keep an enabled Vault retryable after transient registry or
1236
+ // filesystem errors. A genuinely disabled Vault already has
1237
+ // enabled=false from its environment check.
1227
1238
  console.warn("Vault init error:", err && err.message ? err.message : err)
1239
+ return { enabled: false, error: err && err.message ? err.message : String(err) }
1228
1240
  })
1229
1241
  }
1230
1242
 
package/kernel/shell.js CHANGED
@@ -291,7 +291,7 @@ class Shell {
291
291
  }
292
292
 
293
293
  setDefaultEnvValue(this.env, "HF_HUB_DISABLE_UPDATE_CHECK", "1")
294
- setDefaultEnvValue(this.env, "HF_TOKEN_PATH", path.resolve(this.kernel.homedir, "cache", "HF_AUTH", "token"))
294
+ setDefaultEnvValue(this.env, "HF_TOKEN_PATH", this.kernel.envs?.HF_TOKEN_PATH || path.resolve(this.kernel.homedir, "cache", "HF_AUTH", "token"))
295
295
 
296
296
  if (this.platform === "win32") {
297
297
  const managedSslDir = path.resolve(this.kernel.homedir, "bin", "miniforge", "Library", "ssl")
@@ -1618,7 +1618,8 @@ class Shell {
1618
1618
 
1619
1619
 
1620
1620
  if (this.kernel.api.running[this.id]) {
1621
- delete this.kernel.api.running[this.id]
1621
+ if (typeof this.kernel.api.clearRunning === "function") this.kernel.api.clearRunning(this.id)
1622
+ else delete this.kernel.api.running[this.id]
1622
1623
  }
1623
1624
  if (this.kernel.memory.local[this.id]) {
1624
1625
  delete this.kernel.memory.local[this.id]
package/kernel/shells.js CHANGED
@@ -47,6 +47,9 @@ class Shells {
47
47
  }
48
48
  */
49
49
  async init() {
50
+ const hfDisconnected = !this.kernel.envs?.HF_TOKEN_PATH &&
51
+ !(await this.kernel.connect.connected("huggingface", { timeout: 5000 }))
52
+
50
53
  // iterate through all the envs
51
54
  let sh = new Shell(this.kernel)
52
55
  let m
@@ -58,6 +61,9 @@ class Shells {
58
61
  await sh.init_env({
59
62
  env: this.kernel.bin.envs({}),
60
63
  })
64
+ if (hfDisconnected && sh.env.HF_TOKEN_PATH === this.kernel.path("cache", "HF_AUTH", "token")) {
65
+ sh.env.HF_TOKEN_PATH = this.kernel.path("cache", "HF_AUTH", "anonymous")
66
+ }
61
67
 
62
68
  await this.ensureBracketedPasteSupport(sh.shell)
63
69
  if (this.kernel.bracketedPasteSupport) {
@@ -0,0 +1,13 @@
1
+ const MB = 1000 * 1000
2
+ const CANDIDATE_SIZE_OPTIONS = [10, 50, 100, 500, 1000].map((value) => value * MB)
3
+
4
+ module.exports = {
5
+ SIZE_THRESHOLD: 100 * MB,
6
+ CANDIDATE_SIZE_OPTIONS,
7
+ TMP_SUFFIX: ".pinokio-dedup-tmp",
8
+ SHA256_RE: /^[0-9a-f]{64}$/,
9
+ ENTRY_BATCH_SIZE: 256,
10
+ DIR_CONCURRENCY: 8,
11
+ STAT_CONCURRENCY: 32,
12
+ HASH_QUEUE_LIMIT: 256
13
+ }
@@ -10,13 +10,20 @@ const HASH_READ_SIZE = 1024 * 1024
10
10
  parentPort.on('message', ({ id, filePath }) => {
11
11
  const hash = crypto.createHash('sha256')
12
12
  let size = 0
13
- const stream = fs.createReadStream(filePath, { highWaterMark: HASH_READ_SIZE })
13
+ const flags = fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW || 0)
14
+ const stream = fs.createReadStream(filePath, { flags, highWaterMark: HASH_READ_SIZE })
14
15
  stream.on('data', (chunk) => {
15
16
  size += chunk.length
16
17
  hash.update(chunk)
17
18
  })
18
19
  stream.on('error', (error) => {
19
- parentPort.postMessage({ id, error: error.message })
20
+ parentPort.postMessage({
21
+ id,
22
+ error: {
23
+ message: error && error.message ? error.message : String(error),
24
+ code: error && error.code ? error.code : null
25
+ }
26
+ })
20
27
  })
21
28
  stream.on('end', () => {
22
29
  parentPort.postMessage({ id, hash: hash.digest('hex'), size })