pinokiod 7.3.10 → 7.3.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.
- package/kernel/bin/brew.js +12 -2
- package/kernel/bin/caddy.js +24 -20
- package/kernel/bin/huggingface.js +2 -2
- package/kernel/bin/setup.js +2 -3
- package/kernel/bin/uv.js +13 -6
- package/kernel/connect/index.js +5 -1
- package/kernel/connect/providers/huggingface/index.js +213 -75
- package/kernel/environment.js +16 -1
- package/kernel/router/localhost_home_router.js +7 -0
- package/kernel/shell.js +1 -5
- package/kernel/util.js +1 -0
- package/package.json +1 -1
- package/server/index.js +75 -33
- package/server/public/common.js +45 -71
- package/server/public/install.js +20 -2
- package/server/public/layout.js +1 -1
- package/server/public/style.css +1432 -511
- package/server/public/tab-link-popover.css +162 -18
- package/server/public/tab-link-popover.js +230 -21
- package/server/public/task-launcher.css +121 -84
- package/server/public/terminal-settings.js +121 -45
- package/server/public/universal-launcher.css +42 -33
- package/server/public/urldropdown.css +284 -0
- package/server/views/app.ejs +1595 -308
- package/server/views/autolaunch.ejs +4 -5
- package/server/views/checkpoints.ejs +223 -50
- package/server/views/connect/huggingface.ejs +406 -325
- package/server/views/connect.ejs +0 -1
- package/server/views/github.ejs +277 -324
- package/server/views/index.ejs +53 -1
- package/server/views/install.ejs +119 -19
- package/server/views/logs.ejs +10 -9
- package/server/views/net.ejs +341 -64
- package/server/views/network.ejs +85 -63
- package/server/views/partials/main_sidebar.ejs +237 -12
- package/server/views/plugins.ejs +141 -3
- package/server/views/settings.ejs +103 -7
- package/server/views/setup.ejs +0 -5
- package/server/views/skills.ejs +0 -1
- package/server/views/task_list.ejs +0 -1
- package/server/views/terminal.ejs +142 -1
- package/server/views/terminals.ejs +346 -6
- package/server/views/tools.ejs +828 -1691
- package/test/caddy-install.test.js +53 -0
- package/test/connect-setup.test.js +16 -0
- package/test/github-connection.test.js +1 -1
- package/test/huggingface-bin.test.js +4 -4
- package/test/huggingface-connect.test.js +73 -0
- package/test/main-sidebar.test.js +31 -0
- package/test/shell-run-template.test.js +5 -1
- package/test/uv-bin.test.js +29 -0
package/kernel/bin/brew.js
CHANGED
|
@@ -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({
|
|
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
|
}
|
package/kernel/bin/caddy.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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.
|
|
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.
|
|
21
|
+
if (coerced && semver.eq(coerced, "1.20.1")) {
|
|
22
22
|
return true
|
|
23
23
|
}
|
|
24
24
|
}
|
package/kernel/bin/setup.js
CHANGED
|
@@ -280,7 +280,7 @@ module.exports = {
|
|
|
280
280
|
{ name: "git", },
|
|
281
281
|
{ name: "uv", },
|
|
282
282
|
{ name: "node", },
|
|
283
|
-
{ name: "
|
|
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
|
|
2
|
-
|
|
3
|
-
const
|
|
4
|
-
const
|
|
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
|
|
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
|
-
|
|
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({
|
package/kernel/connect/index.js
CHANGED
|
@@ -9,7 +9,11 @@ class Connect {
|
|
|
9
9
|
this.config = config
|
|
10
10
|
this.clients = {}
|
|
11
11
|
for(let name in this.config) {
|
|
12
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
44
|
-
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
if (
|
|
51
|
-
return
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
79
|
-
|
|
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
|
package/kernel/environment.js
CHANGED
|
@@ -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
|
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