pinokiod 8.0.38 → 8.0.40
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/hf/index.js +2 -2
- package/kernel/connect/index.js +2 -2
- package/kernel/connect/providers/huggingface/index.js +14 -5
- package/kernel/index.js +0 -12
- package/kernel/shell.js +1 -1
- package/kernel/shells.js +6 -0
- package/package.json +1 -1
- package/server/index.js +0 -113
- package/server/views/connect/huggingface.ejs +7 -9
- package/server/views/partials/main_sidebar.ejs +0 -1
- package/test/hf-api.test.js +4 -2
- package/test/huggingface-connect.test.js +7 -11
- package/test/huggingface-token-validity.test.js +135 -0
- package/kernel/vault/hash_worker.js +0 -19
- package/kernel/vault/index.js +0 -820
- package/kernel/vault/registry.js +0 -169
- package/kernel/vault/sweeper.js +0 -354
- package/server/views/vault.ejs +0 -1483
- package/test/vault-engine.test.js +0 -360
- package/test/vault-sweep.test.js +0 -286
- package/test/vault-ui.test.js +0 -301
package/kernel/api/hf/index.js
CHANGED
|
@@ -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
|
}
|
package/kernel/connect/index.js
CHANGED
|
@@ -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.
|
|
35
|
+
await fs.promises.rmdir(tokenPath)
|
|
36
36
|
}
|
|
37
37
|
} catch (error) {
|
|
38
|
-
if (
|
|
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
|
@@ -37,7 +37,6 @@ const PinokioDomainRouter = require("./router/pinokio_domain_router")
|
|
|
37
37
|
const Procs = require('./procs')
|
|
38
38
|
const Peer = require('./peer')
|
|
39
39
|
const Git = require('./git')
|
|
40
|
-
const Vault = require('./vault')
|
|
41
40
|
const Connect = require('./connect')
|
|
42
41
|
const Favicon = require('./favicon')
|
|
43
42
|
const AppLauncher = require('./app_launcher')
|
|
@@ -1217,17 +1216,6 @@ class Kernel {
|
|
|
1217
1216
|
await this.git.loadCheckpoints()
|
|
1218
1217
|
console.timeEnd("git.loadCheckpoints")
|
|
1219
1218
|
|
|
1220
|
-
// Shared model store: registry verification only at startup — never a
|
|
1221
|
-
// discovery walk (spec/requirements/shared-model-store.md, trigger 5).
|
|
1222
|
-
this.vault = new Vault(this)
|
|
1223
|
-
if (this.homedir) {
|
|
1224
|
-
this.vault.init().then((result) => {
|
|
1225
|
-
if (result && result.enabled) return this.vault.verify()
|
|
1226
|
-
}).catch((err) => {
|
|
1227
|
-
console.warn("Vault init error:", err && err.message ? err.message : err)
|
|
1228
|
-
})
|
|
1229
|
-
}
|
|
1230
|
-
|
|
1231
1219
|
// let contents = await fs.promises.readdir(this.homedir)
|
|
1232
1220
|
//await this.bin.init()
|
|
1233
1221
|
let ts = Date.now()
|
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")
|
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) {
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -15732,119 +15732,6 @@ class Server {
|
|
|
15732
15732
|
// }))
|
|
15733
15733
|
|
|
15734
15734
|
|
|
15735
|
-
// Vault dashboard data: everything from memory, never a walk. The scan
|
|
15736
|
-
// itself runs ONLY via the explicit scan action below.
|
|
15737
|
-
this.app.get("/info/dedup", ex(async (req, res) => {
|
|
15738
|
-
const vault = this.kernel.vault
|
|
15739
|
-
if (!vault || !vault.enabled) {
|
|
15740
|
-
res.json({ enabled: false })
|
|
15741
|
-
return
|
|
15742
|
-
}
|
|
15743
|
-
if (req.query && req.query.progress === "1") {
|
|
15744
|
-
res.json(vault.progressStatus())
|
|
15745
|
-
return
|
|
15746
|
-
}
|
|
15747
|
-
res.json(await vault.status())
|
|
15748
|
-
}))
|
|
15749
|
-
this.app.get("/vault", ex(async (req, res) => {
|
|
15750
|
-
res.render("vault", { theme: this.theme, agent: req.agent })
|
|
15751
|
-
}))
|
|
15752
|
-
this.app.post("/vault/action", ex(async (req, res) => {
|
|
15753
|
-
const vault = this.kernel.vault
|
|
15754
|
-
if (!vault || !vault.enabled) {
|
|
15755
|
-
res.json({ error: "vault is not enabled" })
|
|
15756
|
-
return
|
|
15757
|
-
}
|
|
15758
|
-
const body = req.body || {}
|
|
15759
|
-
try {
|
|
15760
|
-
switch (body.action) {
|
|
15761
|
-
case "add_source": {
|
|
15762
|
-
const result = await vault.addExternalSource(body.path)
|
|
15763
|
-
res.json({
|
|
15764
|
-
created: result.created,
|
|
15765
|
-
source: {
|
|
15766
|
-
id: result.source.id,
|
|
15767
|
-
label: result.source.label,
|
|
15768
|
-
target_path: result.source.root,
|
|
15769
|
-
shareable: !!result.source.shareable
|
|
15770
|
-
}
|
|
15771
|
-
})
|
|
15772
|
-
return
|
|
15773
|
-
}
|
|
15774
|
-
case "deduplicate": {
|
|
15775
|
-
const scopeId = typeof body.scope_id === "string" ? body.scope_id : null
|
|
15776
|
-
if (scopeId) {
|
|
15777
|
-
const source = vault.sources().find((item) => item.id === scopeId && item.kind !== "virtual")
|
|
15778
|
-
if (!source) {
|
|
15779
|
-
res.json({ error: "That location is no longer available. Scan again to refresh it." })
|
|
15780
|
-
return
|
|
15781
|
-
}
|
|
15782
|
-
if (!source.shareable) {
|
|
15783
|
-
res.json({ error: "This location is on a disk that cannot share space with this vault." })
|
|
15784
|
-
return
|
|
15785
|
-
}
|
|
15786
|
-
if (source.kind === "app") {
|
|
15787
|
-
const appRoot = this.kernel.path("api", source.app) + path.sep
|
|
15788
|
-
const running = this.kernel.api && this.kernel.api.running ? this.kernel.api.running : {}
|
|
15789
|
-
const busy = Object.keys(running).some((k) => running[k] && k.startsWith(appRoot))
|
|
15790
|
-
if (busy) {
|
|
15791
|
-
res.json({ error: "This app has a running script. Stop it first, then deduplicate." })
|
|
15792
|
-
return
|
|
15793
|
-
}
|
|
15794
|
-
}
|
|
15795
|
-
res.json(await vault.sweeper.convertPending(undefined, {
|
|
15796
|
-
scope_id: scopeId, batch_id: `batch-${Date.now()}`
|
|
15797
|
-
}))
|
|
15798
|
-
return
|
|
15799
|
-
}
|
|
15800
|
-
const app = Object.prototype.hasOwnProperty.call(body, "app") ? (body.app || null) : undefined
|
|
15801
|
-
if (app) {
|
|
15802
|
-
const appRoot = this.kernel.path("api", app) + path.sep
|
|
15803
|
-
const running = this.kernel.api && this.kernel.api.running ? this.kernel.api.running : {}
|
|
15804
|
-
const busy = Object.keys(running).some((k) => running[k] && k.startsWith(appRoot))
|
|
15805
|
-
if (busy) {
|
|
15806
|
-
res.json({ error: "This app has a running script. Stop it first, then deduplicate." })
|
|
15807
|
-
return
|
|
15808
|
-
}
|
|
15809
|
-
}
|
|
15810
|
-
res.json(await vault.sweeper.convertPending(app, { batch_id: `batch-${Date.now()}` }))
|
|
15811
|
-
return
|
|
15812
|
-
}
|
|
15813
|
-
case "reclaim":
|
|
15814
|
-
res.json(await vault.reclaim(body.hash))
|
|
15815
|
-
return
|
|
15816
|
-
case "reclaim_all":
|
|
15817
|
-
res.json(await vault.reclaimAll())
|
|
15818
|
-
return
|
|
15819
|
-
case "scan":
|
|
15820
|
-
vault.sweeper.scan().catch(() => {})
|
|
15821
|
-
res.json({ started: true })
|
|
15822
|
-
return
|
|
15823
|
-
case "repair":
|
|
15824
|
-
case "rebuild":
|
|
15825
|
-
if (vault.sweeper && vault.sweeper.state.active) {
|
|
15826
|
-
res.json({ error: "Wait for the current scan to finish before repairing the index." })
|
|
15827
|
-
return
|
|
15828
|
-
}
|
|
15829
|
-
await vault.rebuild()
|
|
15830
|
-
res.json({ done: true })
|
|
15831
|
-
return
|
|
15832
|
-
case "undo":
|
|
15833
|
-
res.json(await vault.undoBatch(body.batch_id))
|
|
15834
|
-
return
|
|
15835
|
-
case "detach":
|
|
15836
|
-
res.json(await vault.detach(body.path))
|
|
15837
|
-
return
|
|
15838
|
-
case "reshare":
|
|
15839
|
-
res.json(await vault.reshare(body.path))
|
|
15840
|
-
return
|
|
15841
|
-
default:
|
|
15842
|
-
res.json({ error: "unknown action" })
|
|
15843
|
-
}
|
|
15844
|
-
} catch (e) {
|
|
15845
|
-
res.json({ error: e && e.message ? e.message : String(e) })
|
|
15846
|
-
}
|
|
15847
|
-
}))
|
|
15848
15735
|
this.app.get("/info/scripts", ex(async (req, res) => {
|
|
15849
15736
|
/*
|
|
15850
15737
|
returns something like this by using the this.kernel.memory.local variable, extracting the api name and adding all running scripts in each associated array, setting the uri as the script path, and the local variables as the local attribute
|
|
@@ -168,9 +168,9 @@
|
|
|
168
168
|
<script>
|
|
169
169
|
const LOGIN_URL = "/connect/huggingface/login"
|
|
170
170
|
const LOGOUT_URL = "/connect/huggingface/logout"
|
|
171
|
-
const
|
|
171
|
+
const STATUS_URL = "/connect/status/huggingface"
|
|
172
172
|
const PROFILE_URL = "/connect/huggingface/profile"
|
|
173
|
-
const POLL_INTERVAL =
|
|
173
|
+
const POLL_INTERVAL = 5000
|
|
174
174
|
const POLL_TIMEOUT = 120000
|
|
175
175
|
|
|
176
176
|
const badgeElement = document.getElementById("hf-badge")
|
|
@@ -261,9 +261,9 @@
|
|
|
261
261
|
return res.json()
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
-
async function
|
|
265
|
-
const json = await
|
|
266
|
-
return json && json.
|
|
264
|
+
async function isConnected() {
|
|
265
|
+
const json = await fetch(STATUS_URL, { cache: "no-store" }).then((res) => res.json())
|
|
266
|
+
return Boolean(json && json.connected)
|
|
267
267
|
}
|
|
268
268
|
|
|
269
269
|
async function showConnected() {
|
|
@@ -326,8 +326,7 @@
|
|
|
326
326
|
}
|
|
327
327
|
|
|
328
328
|
async function pollLoginStatus() {
|
|
329
|
-
|
|
330
|
-
if (token) {
|
|
329
|
+
if (await isConnected()) {
|
|
331
330
|
await showConnected()
|
|
332
331
|
}
|
|
333
332
|
}
|
|
@@ -415,8 +414,7 @@
|
|
|
415
414
|
|
|
416
415
|
window.addEventListener("load", async () => {
|
|
417
416
|
try {
|
|
418
|
-
|
|
419
|
-
if (token) {
|
|
417
|
+
if (await isConnected()) {
|
|
420
418
|
await showConnected()
|
|
421
419
|
return
|
|
422
420
|
}
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
<div class='main-sidebar-section' aria-label="Manage">
|
|
27
27
|
<div class="main-sidebar-section-title">Manage</div>
|
|
28
28
|
<a class="tab <%= sidebarSelected === 'checkpoints' ? 'selected' : '' %>" href="/checkpoints"><i class="fa-solid fa-clock-rotate-left"></i><div class='caption'>Checkpoints</div></a>
|
|
29
|
-
<a class="tab <%= sidebarSelected === 'vault' ? 'selected' : '' %>" href="/vault"><i class="fa-solid fa-hard-drive"></i><div class='caption'>Vault</div></a>
|
|
30
29
|
<a class="tab <%= sidebarSelected === 'tools' ? 'selected' : '' %>" href="/tools"><i class="fa-solid fa-toolbox"></i><div class='caption'>Tools</div></a>
|
|
31
30
|
<a class="tab <%= sidebarSelected === 'plugins' ? 'selected' : '' %>" href="/plugins"><i class="fa-solid fa-plug-circle-bolt"></i><div class='caption'>Plugins</div></a>
|
|
32
31
|
<a class="tab <%= sidebarSelected === 'tasks' ? 'selected' : '' %>" href="/tasks"><i class="fa-solid fa-list-check"></i><div class='caption'>Tasks</div></a>
|
package/test/hf-api.test.js
CHANGED
|
@@ -41,7 +41,8 @@ test('hf.login shows a modal, waits for the user to open Hugging Face, then clos
|
|
|
41
41
|
login,
|
|
42
42
|
token_path: '/tmp/pinokio/hf-token'
|
|
43
43
|
}
|
|
44
|
-
}
|
|
44
|
+
},
|
|
45
|
+
connected: async () => true
|
|
45
46
|
},
|
|
46
47
|
api: {
|
|
47
48
|
async wait(key) {
|
|
@@ -161,7 +162,8 @@ test('hf.login returns success without opening anything when already logged in',
|
|
|
161
162
|
access_token: 'hf_secret',
|
|
162
163
|
token_path: '/tmp/pinokio/hf-token'
|
|
163
164
|
}
|
|
164
|
-
}
|
|
165
|
+
},
|
|
166
|
+
connected: async () => true
|
|
165
167
|
}
|
|
166
168
|
}
|
|
167
169
|
|
|
@@ -240,7 +240,7 @@ test('Hugging Face connect leaves existing token and refresh files unchanged', a
|
|
|
240
240
|
}
|
|
241
241
|
})
|
|
242
242
|
|
|
243
|
-
test('Hugging Face connect
|
|
243
|
+
test('Hugging Face connect preserves a non-empty token directory', async () => {
|
|
244
244
|
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-connect-invalid-dir-'))
|
|
245
245
|
const authDir = path.join(root, 'cache', 'HF_AUTH')
|
|
246
246
|
const tokenPath = path.join(authDir, 'token')
|
|
@@ -248,16 +248,12 @@ test('Hugging Face connect removes a non-empty token directory', async () => {
|
|
|
248
248
|
await fs.writeFile(path.join(tokenPath, 'leftover.txt'), 'leftover')
|
|
249
249
|
await fs.writeFile(path.join(authDir, 'stored_tokens'), 'refresh state')
|
|
250
250
|
try {
|
|
251
|
-
const
|
|
252
|
-
|
|
253
|
-
provider
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
])
|
|
258
|
-
|
|
259
|
-
assert.equal(env.HF_TOKEN_PATH, tokenPath)
|
|
260
|
-
assert.equal(await fs.readFile(tokenPath, 'utf8'), 'hf_replacement_token')
|
|
251
|
+
const kernel = createKernel(root)
|
|
252
|
+
kernel.envs = { HF_TOKEN_PATH: tokenPath }
|
|
253
|
+
const provider = new HuggingfaceConnect(kernel, {})
|
|
254
|
+
assert.equal(await provider.connected(), false)
|
|
255
|
+
assert.equal(kernel.envs.HF_TOKEN_PATH, path.join(authDir, 'anonymous'))
|
|
256
|
+
assert.equal(await fs.readFile(path.join(tokenPath, 'leftover.txt'), 'utf8'), 'leftover')
|
|
261
257
|
assert.equal(await fs.readFile(path.join(authDir, 'stored_tokens'), 'utf8'), 'refresh state')
|
|
262
258
|
} finally {
|
|
263
259
|
await fs.rm(root, { recursive: true, force: true })
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
const assert = require('node:assert/strict')
|
|
2
|
+
const fs = require('node:fs/promises')
|
|
3
|
+
const os = require('node:os')
|
|
4
|
+
const path = require('node:path')
|
|
5
|
+
const test = require('node:test')
|
|
6
|
+
|
|
7
|
+
const HF = require('../kernel/api/hf')
|
|
8
|
+
const HuggingfaceConnect = require('../kernel/connect/providers/huggingface')
|
|
9
|
+
const Shell = require('../kernel/shell')
|
|
10
|
+
const Shells = require('../kernel/shells')
|
|
11
|
+
|
|
12
|
+
function createKernel(root) {
|
|
13
|
+
return {
|
|
14
|
+
homedir: root,
|
|
15
|
+
platform: process.platform,
|
|
16
|
+
path: (...parts) => path.join(root, ...parts),
|
|
17
|
+
exists: async (target) => fs.access(target).then(() => true, () => false)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test('hf.login does not accept an invalid saved token', async () => {
|
|
22
|
+
const context = {
|
|
23
|
+
parentPath: '/pinokio/api/test/login.js',
|
|
24
|
+
cwd: '/pinokio/api/test',
|
|
25
|
+
env: { HF_TOKEN_PATH: './token' }
|
|
26
|
+
}
|
|
27
|
+
const kernel = {
|
|
28
|
+
connect: {
|
|
29
|
+
keys: async () => ({ access_token: 'invalid' }),
|
|
30
|
+
async connected(provider, options, receivedContext) {
|
|
31
|
+
assert.equal(provider, 'huggingface')
|
|
32
|
+
assert.deepEqual(options, { timeout: 5000 })
|
|
33
|
+
assert.deepEqual(receivedContext, context)
|
|
34
|
+
return false
|
|
35
|
+
},
|
|
36
|
+
login: async () => ({ status: 'error', error: 'login started' })
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const result = await new HF().login({
|
|
41
|
+
parent: { path: context.parentPath },
|
|
42
|
+
cwd: context.cwd,
|
|
43
|
+
params: { env: context.env }
|
|
44
|
+
}, () => {}, kernel)
|
|
45
|
+
|
|
46
|
+
assert.deepEqual(result, { status: 'error', error: 'login started' })
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('whoami updates only the managed shared token path', async () => {
|
|
50
|
+
const root = path.join(os.tmpdir(), 'pinokio-hf-state')
|
|
51
|
+
const tokenPath = path.join(root, 'cache', 'HF_AUTH', 'token')
|
|
52
|
+
const anonymousPath = path.join(root, 'cache', 'HF_AUTH', 'anonymous')
|
|
53
|
+
const kernel = createKernel(root)
|
|
54
|
+
kernel.envs = { HF_TOKEN_PATH: anonymousPath }
|
|
55
|
+
const provider = new HuggingfaceConnect(kernel, {})
|
|
56
|
+
const customContext = { env: { HF_TOKEN_PATH: '/tmp/custom-hf-token' } }
|
|
57
|
+
|
|
58
|
+
provider.runHf = async (_args, _options, receivedContext) => {
|
|
59
|
+
assert.equal(receivedContext, customContext)
|
|
60
|
+
return { env: { HF_TOKEN_PATH: customContext.env.HF_TOKEN_PATH } }
|
|
61
|
+
}
|
|
62
|
+
assert.equal(await provider.connected({}, customContext), true)
|
|
63
|
+
assert.equal(kernel.envs.HF_TOKEN_PATH, anonymousPath)
|
|
64
|
+
|
|
65
|
+
provider.runHf = async () => ({ env: { HF_TOKEN_PATH: tokenPath } })
|
|
66
|
+
assert.equal(await provider.connected(), true)
|
|
67
|
+
assert.equal(kernel.envs.HF_TOKEN_PATH, tokenPath)
|
|
68
|
+
|
|
69
|
+
provider.runHf = async () => {
|
|
70
|
+
const error = new Error('invalid token')
|
|
71
|
+
error.tokenPath = tokenPath
|
|
72
|
+
throw error
|
|
73
|
+
}
|
|
74
|
+
assert.equal(await provider.connected(), false)
|
|
75
|
+
assert.equal(kernel.envs.HF_TOKEN_PATH, anonymousPath)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
test('provider login ignores only Pinokio\'s inherited anonymous path', async () => {
|
|
79
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-context-'))
|
|
80
|
+
const appDir = path.join(root, 'api', 'demo')
|
|
81
|
+
const scriptPath = path.join(appDir, 'start.js')
|
|
82
|
+
await fs.mkdir(appDir, { recursive: true })
|
|
83
|
+
await fs.writeFile(path.join(root, 'ENVIRONMENT'), '')
|
|
84
|
+
await fs.writeFile(path.join(appDir, 'ENVIRONMENT'), '')
|
|
85
|
+
await fs.writeFile(scriptPath, '')
|
|
86
|
+
try {
|
|
87
|
+
const kernel = createKernel(root)
|
|
88
|
+
kernel.envs = { HF_TOKEN_PATH: path.join(root, 'cache', 'HF_AUTH', 'anonymous') }
|
|
89
|
+
const provider = new HuggingfaceConnect(kernel, {})
|
|
90
|
+
const context = { parentPath: scriptPath, cwd: appDir }
|
|
91
|
+
|
|
92
|
+
assert.equal((await provider.authEnv(context)).HF_TOKEN_PATH, provider.defaultTokenPath())
|
|
93
|
+
assert.equal(
|
|
94
|
+
(await provider.authEnv({ ...context, env: { HF_TOKEN_PATH: './custom-token' } })).HF_TOKEN_PATH,
|
|
95
|
+
path.join(appDir, 'custom-token')
|
|
96
|
+
)
|
|
97
|
+
} finally {
|
|
98
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('invalid managed tokens are replaced once without overriding apps', async () => {
|
|
103
|
+
const root = await fs.mkdtemp(path.join(os.tmpdir(), 'pinokio-hf-shells-'))
|
|
104
|
+
await fs.writeFile(path.join(root, 'ENVIRONMENT'), '')
|
|
105
|
+
let checks = 0
|
|
106
|
+
const kernel = createKernel(root)
|
|
107
|
+
kernel.bin = { envs: () => ({}) }
|
|
108
|
+
kernel.bracketedPasteSupport = {}
|
|
109
|
+
kernel.which = () => null
|
|
110
|
+
kernel.connect = {
|
|
111
|
+
connected: async () => {
|
|
112
|
+
checks++
|
|
113
|
+
return false
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
const shells = new Shells(kernel)
|
|
117
|
+
shells.ensureBracketedPasteSupport = async () => {}
|
|
118
|
+
try {
|
|
119
|
+
await shells.init()
|
|
120
|
+
const anonymousPath = path.join(root, 'cache', 'HF_AUTH', 'anonymous')
|
|
121
|
+
assert.equal(kernel.envs.HF_TOKEN_PATH, anonymousPath)
|
|
122
|
+
await shells.init()
|
|
123
|
+
assert.equal(checks, 1)
|
|
124
|
+
|
|
125
|
+
const inheritedShell = new Shell(kernel)
|
|
126
|
+
await inheritedShell.init_env({ env: {} })
|
|
127
|
+
assert.equal(inheritedShell.env.HF_TOKEN_PATH, anonymousPath)
|
|
128
|
+
|
|
129
|
+
const appShell = new Shell(kernel)
|
|
130
|
+
await appShell.init_env({ env: { HF_TOKEN_PATH: '/tmp/app-hf-token' } })
|
|
131
|
+
assert.equal(appShell.env.HF_TOKEN_PATH, '/tmp/app-hf-token')
|
|
132
|
+
} finally {
|
|
133
|
+
await fs.rm(root, { recursive: true, force: true })
|
|
134
|
+
}
|
|
135
|
+
})
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const { parentPort } = require('worker_threads')
|
|
2
|
-
const crypto = require('crypto')
|
|
3
|
-
const fs = require('fs')
|
|
4
|
-
|
|
5
|
-
parentPort.on('message', ({ id, filePath }) => {
|
|
6
|
-
const hash = crypto.createHash('sha256')
|
|
7
|
-
let size = 0
|
|
8
|
-
const stream = fs.createReadStream(filePath)
|
|
9
|
-
stream.on('data', (chunk) => {
|
|
10
|
-
size += chunk.length
|
|
11
|
-
hash.update(chunk)
|
|
12
|
-
})
|
|
13
|
-
stream.on('error', (error) => {
|
|
14
|
-
parentPort.postMessage({ id, error: error.message })
|
|
15
|
-
})
|
|
16
|
-
stream.on('end', () => {
|
|
17
|
-
parentPort.postMessage({ id, hash: hash.digest('hex'), size })
|
|
18
|
-
})
|
|
19
|
-
})
|