pinokiod 3.41.0 → 3.43.0
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/browser/index.js +3 -1
- package/kernel/api/cloudflare/index.js +3 -3
- package/kernel/api/index.js +187 -51
- package/kernel/api/loading/index.js +15 -0
- package/kernel/api/process/index.js +7 -0
- package/kernel/api/shell/index.js +0 -2
- package/kernel/bin/browserless.js +22 -0
- package/kernel/bin/caddy.js +36 -4
- package/kernel/bin/index.js +4 -1
- package/kernel/bin/setup.js +38 -5
- package/kernel/connect/backend.js +110 -0
- package/kernel/connect/config.js +171 -0
- package/kernel/connect/index.js +18 -7
- package/kernel/connect/providers/huggingface/index.js +98 -0
- package/kernel/connect/providers/x/index.js +0 -1
- package/kernel/environment.js +91 -19
- package/kernel/git.js +46 -3
- package/kernel/index.js +119 -39
- package/kernel/peer.js +40 -5
- package/kernel/plugin.js +3 -2
- package/kernel/procs.js +27 -20
- package/kernel/prototype.js +30 -16
- package/kernel/router/common.js +1 -1
- package/kernel/router/connector.js +1 -3
- package/kernel/router/index.js +38 -4
- package/kernel/router/localhost_home_router.js +5 -1
- package/kernel/router/localhost_port_router.js +27 -1
- package/kernel/router/localhost_static_router.js +93 -0
- package/kernel/router/localhost_variable_router.js +14 -9
- package/kernel/router/peer_peer_router.js +3 -0
- package/kernel/router/peer_static_router.js +43 -0
- package/kernel/router/peer_variable_router.js +15 -14
- package/kernel/router/processor.js +26 -1
- package/kernel/router/rewriter.js +59 -0
- package/kernel/scripts/git/commit +11 -1
- package/kernel/shell.js +8 -3
- package/kernel/util.js +65 -6
- package/package.json +2 -1
- package/server/index.js +1037 -964
- package/server/public/common.js +382 -1
- package/server/public/fscreator.js +0 -1
- package/server/public/loading.js +17 -0
- package/server/public/notifyinput.js +0 -1
- package/server/public/opener.js +4 -2
- package/server/public/style.css +311 -11
- package/server/socket.js +7 -1
- package/server/views/app.ejs +1747 -351
- package/server/views/columns.ejs +338 -0
- package/server/views/connect/huggingface.ejs +353 -0
- package/server/views/connect/index.ejs +410 -0
- package/server/views/connect/x.ejs +43 -9
- package/server/views/connect.ejs +709 -49
- package/server/views/container.ejs +357 -0
- package/server/views/d.ejs +251 -62
- package/server/views/download.ejs +54 -10
- package/server/views/editor.ejs +11 -0
- package/server/views/explore.ejs +40 -15
- package/server/views/file_explorer.ejs +25 -246
- package/server/views/form.ejs +44 -1
- package/server/views/frame.ejs +39 -1
- package/server/views/github.ejs +48 -11
- package/server/views/help.ejs +48 -7
- package/server/views/index.ejs +119 -58
- package/server/views/index2.ejs +3 -4
- package/server/views/init/index.ejs +651 -197
- package/server/views/install.ejs +1 -1
- package/server/views/mini.ejs +47 -18
- package/server/views/net.ejs +199 -67
- package/server/views/network.ejs +220 -94
- package/server/views/network2.ejs +3 -4
- package/server/views/old_network.ejs +3 -3
- package/server/views/prototype/index.ejs +48 -11
- package/server/views/review.ejs +1005 -0
- package/server/views/rows.ejs +341 -0
- package/server/views/screenshots.ejs +1020 -0
- package/server/views/settings.ejs +160 -23
- package/server/views/setup.ejs +49 -7
- package/server/views/setup_home.ejs +43 -10
- package/server/views/shell.ejs +7 -1
- package/server/views/start.ejs +14 -9
- package/server/views/terminal.ejs +13 -2
- package/server/views/tools.ejs +1015 -0
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const fetch = require('cross-fetch')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
class Backend {
|
|
5
|
+
constructor(kernel, name, config) {
|
|
6
|
+
this.kernel = kernel
|
|
7
|
+
this.name = name
|
|
8
|
+
this.config = config
|
|
9
|
+
}
|
|
10
|
+
async profile() {
|
|
11
|
+
let connectPath = this.kernel.path(`connect/${this.name}`)
|
|
12
|
+
let profilePath = path.resolve(connectPath, "profile.json")
|
|
13
|
+
this.profile_config = (await this.kernel.loader.load(profilePath)).resolved
|
|
14
|
+
if (!this.profile_config) {
|
|
15
|
+
await fs.promises.mkdir(connectPath, { recursive: true }).catch((e) => { })
|
|
16
|
+
let response = await fetch(this.config.profile.url, {
|
|
17
|
+
headers: {
|
|
18
|
+
'Authorization': 'Bearer ' + this.auth.access_token
|
|
19
|
+
}
|
|
20
|
+
}).then((res) => {
|
|
21
|
+
return res.json()
|
|
22
|
+
})
|
|
23
|
+
this.profile_config = response
|
|
24
|
+
await fs.promises.writeFile(profilePath, JSON.stringify(this.profile_config, null, 2))
|
|
25
|
+
let cwd = connectPath
|
|
26
|
+
await this.config.profile.cache(this.profile_config, cwd)
|
|
27
|
+
}
|
|
28
|
+
let rendered = this.config.profile.render(this.profile_config)
|
|
29
|
+
return rendered
|
|
30
|
+
}
|
|
31
|
+
async persist(auth) {
|
|
32
|
+
console.log("PERSIST", auth)
|
|
33
|
+
this.auth = auth
|
|
34
|
+
this.auth.expires_at = Date.now() + (this.auth.expires_in * 1000);
|
|
35
|
+
let connectPath = this.kernel.path(`connect/${this.name}`)
|
|
36
|
+
let authPath = path.resolve(connectPath, "auth.json")
|
|
37
|
+
await fs.promises.mkdir(connectPath, { recursive: true }).catch((e) => { })
|
|
38
|
+
await fs.promises.writeFile(authPath, JSON.stringify(this.auth, null, 2))
|
|
39
|
+
}
|
|
40
|
+
async destroy() {
|
|
41
|
+
await fs.promises.rm(this.kernel.path(`connect/${this.name}`), { recursive: true })
|
|
42
|
+
this.auth = null
|
|
43
|
+
}
|
|
44
|
+
async sync() {
|
|
45
|
+
// check if auth exists
|
|
46
|
+
// if not, throw error
|
|
47
|
+
let authPath = this.kernel.path(`connect/${this.name}/auth.json`)
|
|
48
|
+
this.auth = (await this.kernel.loader.load(authPath)).resolved
|
|
49
|
+
if (!this.auth) {
|
|
50
|
+
console.log("not authenticated")
|
|
51
|
+
return null
|
|
52
|
+
}
|
|
53
|
+
if (!this.auth.refresh_token) {
|
|
54
|
+
console.log("no refresh token")
|
|
55
|
+
return null
|
|
56
|
+
}
|
|
57
|
+
if (!this.auth.access_token) {
|
|
58
|
+
console.log("no access token")
|
|
59
|
+
return null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// check if auth has expired
|
|
63
|
+
// if expired, refresh and return
|
|
64
|
+
if (Date.now() < this.auth.expires_at) {
|
|
65
|
+
return
|
|
66
|
+
}
|
|
67
|
+
console.log("auth expired. refresh....", JSON.stringify({ auth: this.auth, id: this.config.CLIENT_ID }, null, 2))
|
|
68
|
+
// expired — refresh
|
|
69
|
+
const response = await fetch(this.config.TOKEN_URL, {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': this.config.CONTENT_TYPE,
|
|
73
|
+
'Accept': "application/json"
|
|
74
|
+
},
|
|
75
|
+
body: new URLSearchParams({
|
|
76
|
+
grant_type: 'refresh_token',
|
|
77
|
+
refresh_token: this.auth.refresh_token,
|
|
78
|
+
client_id: this.config.CLIENT_ID,
|
|
79
|
+
})
|
|
80
|
+
}).then((res) => {
|
|
81
|
+
return res.json()
|
|
82
|
+
});
|
|
83
|
+
await this.persist(response)
|
|
84
|
+
return this.auth
|
|
85
|
+
}
|
|
86
|
+
async keys() {
|
|
87
|
+
await this.sync()
|
|
88
|
+
return this.auth
|
|
89
|
+
}
|
|
90
|
+
async login(req) {
|
|
91
|
+
console.log("login", this.name, req, this.config.TOKEN_URL, this.config.TOKEN_URL)
|
|
92
|
+
const response = await fetch(this.config.TOKEN_URL, {
|
|
93
|
+
method: 'POST',
|
|
94
|
+
headers: {
|
|
95
|
+
'Content-Type': this.config.CONTENT_TYPE,
|
|
96
|
+
'Accept': "application/json"
|
|
97
|
+
},
|
|
98
|
+
body: JSON.stringify(req)
|
|
99
|
+
}).then((res) => {
|
|
100
|
+
return res.json()
|
|
101
|
+
});
|
|
102
|
+
console.log("RESPONSE", response)
|
|
103
|
+
await this.persist(response)
|
|
104
|
+
return this.auth
|
|
105
|
+
}
|
|
106
|
+
async logout (req) {
|
|
107
|
+
await this.destroy()
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
module.exports = Backend
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const fetch = require('cross-fetch')
|
|
4
|
+
module.exports = {
|
|
5
|
+
pinokio: {
|
|
6
|
+
CLIENT_ID: 'VmrH6TOG5Q68jTQKc9bP6hFl8oZ6LrkP',
|
|
7
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/pinokio',
|
|
8
|
+
OAUTH_URL: 'http://localhost:3001/oauth/authorize',
|
|
9
|
+
TOKEN_URL: 'http://localhost:3001/oauth/token',
|
|
10
|
+
CONTENT_TYPE: "application/json",
|
|
11
|
+
profile: {
|
|
12
|
+
url: 'http://localhost:3001/oauth/userinfo',
|
|
13
|
+
cache: async (response, cwd) => {
|
|
14
|
+
let url = response.avatar
|
|
15
|
+
let filename = url.split("/").pop()
|
|
16
|
+
const res = await fetch(url)
|
|
17
|
+
await fs.promises.writeFile(path.resolve(cwd, filename), Buffer.from(await res.arrayBuffer()))
|
|
18
|
+
},
|
|
19
|
+
render: (response) => {
|
|
20
|
+
console.log("RESPONSE", response)
|
|
21
|
+
let image = response.avatar.split("/").pop()
|
|
22
|
+
let imagePath = "/asset/connect/pinokio/" + image
|
|
23
|
+
return {
|
|
24
|
+
image: imagePath || '',
|
|
25
|
+
items: [{
|
|
26
|
+
key: "Username",
|
|
27
|
+
val: response.username || "N/A",
|
|
28
|
+
}, {
|
|
29
|
+
key: "Email",
|
|
30
|
+
val: response.email || "N/A"
|
|
31
|
+
}]
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
SCOPE: 'openid profile email read-repos write-repos manage-repos write-discussions read-billing inference-api jobs webhooks',
|
|
36
|
+
},
|
|
37
|
+
huggingface: {
|
|
38
|
+
CLIENT_ID: 'e90d4a4d-68a6-4c12-ae71-64756b5918de',
|
|
39
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/huggingface',
|
|
40
|
+
OAUTH_URL: 'https://huggingface.co/oauth/authorize',
|
|
41
|
+
TOKEN_URL: 'https://huggingface.co/oauth/token',
|
|
42
|
+
CONTENT_TYPE: "application/json",
|
|
43
|
+
profile: {
|
|
44
|
+
url: 'https://huggingface.co/api/whoami-v2',
|
|
45
|
+
cache: async (response, cwd) => {
|
|
46
|
+
let url = response.avatarUrl
|
|
47
|
+
let filename = url.split("/").pop()
|
|
48
|
+
const res = await fetch(url)
|
|
49
|
+
await fs.promises.writeFile(path.resolve(cwd, filename), Buffer.from(await res.arrayBuffer()))
|
|
50
|
+
},
|
|
51
|
+
render: (response) => {
|
|
52
|
+
let image = response.avatarUrl.split("/").pop()
|
|
53
|
+
let imagePath = "/asset/connect/huggingface/" + image
|
|
54
|
+
return {
|
|
55
|
+
image: imagePath || '',
|
|
56
|
+
items: [{
|
|
57
|
+
key: "Username",
|
|
58
|
+
val: response.name || "N/A",
|
|
59
|
+
}, {
|
|
60
|
+
key: "Full name",
|
|
61
|
+
val: response.fullname || "N/A"
|
|
62
|
+
}, {
|
|
63
|
+
key: "Email",
|
|
64
|
+
val: response.email || "N/A"
|
|
65
|
+
}]
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
SCOPE: 'openid profile email read-repos write-repos manage-repos write-discussions read-billing inference-api jobs webhooks',
|
|
70
|
+
},
|
|
71
|
+
x: {
|
|
72
|
+
CLIENT_ID: 'd2FQZ0U4NXpzYnRyS1hZeHBvbUc6MTpjaQ',
|
|
73
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/x',
|
|
74
|
+
OAUTH_URL: 'https://x.com/i/oauth2/authorize',
|
|
75
|
+
TOKEN_URL: 'https://api.twitter.com/2/oauth2/token',
|
|
76
|
+
//CONTENT_TYPE: "application/x-www-form-urlencoded",
|
|
77
|
+
CONTENT_TYPE: "application/json",
|
|
78
|
+
SCOPE: 'tweet.write tweet.read users.read bookmark.write bookmark.read like.write like.read media.write offline.access',
|
|
79
|
+
profile: {
|
|
80
|
+
url: 'https://api.twitter.com/2/users/me?user.fields=profile_image_url,username',
|
|
81
|
+
cache: async (response, cwd) => {
|
|
82
|
+
let url = response.data.profile_image_url
|
|
83
|
+
let filename = url.split("/").pop()
|
|
84
|
+
console.log("Fetching", { url, filename })
|
|
85
|
+
const res = await fetch(url)
|
|
86
|
+
await fs.promises.writeFile(path.resolve(cwd, filename), Buffer.from(await res.arrayBuffer()))
|
|
87
|
+
},
|
|
88
|
+
render: (response) => {
|
|
89
|
+
let image = response.data.profile_image_url.split("/").pop()
|
|
90
|
+
let imagePath = "/asset/connect/x/" + image
|
|
91
|
+
return {
|
|
92
|
+
image: imagePath || "",
|
|
93
|
+
items: [{
|
|
94
|
+
key: "Username",
|
|
95
|
+
val: response.data.username || "N/A"
|
|
96
|
+
}]
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
google: {
|
|
102
|
+
CLIENT_ID: '911627394513-75l6eumucknc8750pn5r5cog5sclndkr.apps.googleusercontent.com',
|
|
103
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/google',
|
|
104
|
+
OAUTH_URL: 'https://accounts.google.com/o/oauth2/v2/auth',
|
|
105
|
+
TOKEN_URL: 'https://oauth2.googleapis.com/token',
|
|
106
|
+
CONTENT_TYPE: "application/json",
|
|
107
|
+
SCOPE: 'openid profile email https://www.googleapis.com/auth/youtube https://www.googleapis.com/auth/youtube.upload https://www.googleapis.com/auth/youtube.force-ssl https://www.googleapis.com/auth/drive https://www.googleapis.com/auth/drive.file https://www.googleapis.com/auth/blogger https://www.googleapis.com/auth/photoslibrary https://www.googleapis.com/auth/photoslibrary.sharing https://www.googleapis.com/auth/calendar https://www.googleapis.com/auth/gmail.compose https://www.googleapis.com/auth/gmail.send',
|
|
108
|
+
profile: {
|
|
109
|
+
url: 'https://www.googleapis.com/oauth2/v2/userinfo',
|
|
110
|
+
render: (response) => {
|
|
111
|
+
return `<p><strong>Name:</strong> ${response.name || 'N/A'}</p>
|
|
112
|
+
<p><strong>Email:</strong> ${response.email || 'N/A'}</p>
|
|
113
|
+
<p><strong>Avatar:</strong> <img src="${response.picture || ''}" alt="Avatar" style="width: 40px; height: 40px; border-radius: 50%; vertical-align: middle;"></p>`
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
github: {
|
|
118
|
+
CLIENT_ID: 'Ov23cthkE6o0xkxngT2r',
|
|
119
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/github',
|
|
120
|
+
OAUTH_URL: 'https://github.com/login/oauth/authorize',
|
|
121
|
+
TOKEN_URL: 'https://github.com/login/oauth/access_token',
|
|
122
|
+
SCOPE: 'user:email read:user repo delete_repo admin:org admin:public_key admin:repo_hook admin:org_hook gist notifications workflow write:packages read:packages write:discussion read:discussion project admin:gpg_key codespace',
|
|
123
|
+
//CONTENT_TYPE: "application/x-www-form-urlencoded",
|
|
124
|
+
CONTENT_TYPE: "application/json",
|
|
125
|
+
profile: {
|
|
126
|
+
url: 'https://api.github.com/user',
|
|
127
|
+
render: (response) => {
|
|
128
|
+
return `<p><strong>Username:</strong> ${response.login || 'N/A'}</p>
|
|
129
|
+
<p><strong>Name:</strong> ${response.name || 'N/A'}</p>
|
|
130
|
+
<p><strong>Email:</strong> ${response.email || 'N/A'}</p>
|
|
131
|
+
<p><strong>Avatar:</strong> <img src="${response.avatar_url || ''}" alt="Avatar" style="width: 40px; height: 40px; border-radius: 50%; vertical-align: middle;"></p>`
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
spotify: {
|
|
136
|
+
CLIENT_ID: '',
|
|
137
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/spotify',
|
|
138
|
+
OAUTH_URL: 'https://accounts.spotify.com/authorize',
|
|
139
|
+
TOKEN_URL: 'https://accounts.spotify.com/api/token',
|
|
140
|
+
CONTENT_TYPE: "application/x-www-form-urlencoded",
|
|
141
|
+
SCOPE: 'user-read-private user-read-email playlist-read-private playlist-read-collaborative playlist-modify-private playlist-modify-public user-library-modify user-library-read user-follow-modify user-follow-read',
|
|
142
|
+
profile: {
|
|
143
|
+
url: 'https://api.spotify.com/v1/me',
|
|
144
|
+
render: (response) => {
|
|
145
|
+
const avatarUrl = response.images && response.images.length > 0
|
|
146
|
+
? response.images[0].url
|
|
147
|
+
: '';
|
|
148
|
+
return `<p><strong>Username:</strong> ${response.id || 'N/A'}</p>
|
|
149
|
+
<p><strong>Display Name:</strong> ${response.display_name || 'N/A'}</p>
|
|
150
|
+
<p><strong>Email:</strong> ${response.email || 'N/A'}</p>
|
|
151
|
+
<p><strong>Avatar:</strong> <img src="${avatarUrl}" alt="Avatar" style="width: 40px; height: 40px; border-radius: 50%; vertical-align: middle;"></p>`
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
microsoft: {
|
|
156
|
+
CLIENT_ID: '',
|
|
157
|
+
REDIRECT_URI: 'https://pinokio.localhost/connect/microsoft',
|
|
158
|
+
OAUTH_URL: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
|
|
159
|
+
TOKEN_URL: 'https://login.microsoftonline.com/common/oauth2/v2.0/token',
|
|
160
|
+
SCOPE: 'openid profile email User.Read User.ReadWrite Files.ReadWrite Sites.ReadWrite.All Mail.ReadWrite Mail.Send Calendars.ReadWrite Tasks.ReadWrite Notes.ReadWrite.All OnlineMeetings.ReadWrite',
|
|
161
|
+
CONTENT_TYPE: "application/x-www-form-urlencoded",
|
|
162
|
+
profile: {
|
|
163
|
+
url: 'https://graph.microsoft.com/v1.0/me',
|
|
164
|
+
render: (response) => {
|
|
165
|
+
return `<p><strong>Name:</strong> ${response.displayName || 'N/A'}</p>
|
|
166
|
+
<p><strong>Email:</strong> ${response.mail || response.userPrincipalName || 'N/A'}</p>
|
|
167
|
+
<p><strong>Username:</strong> ${response.userPrincipalName || 'N/A'}</p>`
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
package/kernel/connect/index.js
CHANGED
|
@@ -1,24 +1,35 @@
|
|
|
1
1
|
const fetch = require('cross-fetch')
|
|
2
2
|
const X = require('./providers/x')
|
|
3
|
+
const Huggingface = require('./providers/huggingface')
|
|
4
|
+
const config = require('./config')
|
|
5
|
+
const Backend = require('./backend')
|
|
3
6
|
class Connect {
|
|
4
7
|
constructor(kernel) {
|
|
5
8
|
this.kernel = kernel
|
|
6
|
-
this.
|
|
9
|
+
this.config = config
|
|
10
|
+
this.clients = {}
|
|
11
|
+
for(let name in this.config) {
|
|
12
|
+
this.clients[name] = new Backend(kernel, name, this.config[name])
|
|
13
|
+
}
|
|
7
14
|
}
|
|
8
|
-
async
|
|
9
|
-
|
|
10
|
-
|
|
15
|
+
async profile(provider, req) {
|
|
16
|
+
if (this.clients[provider] && this.clients[provider].profile) {
|
|
17
|
+
let res = await this.clients[provider].profile()
|
|
18
|
+
return res
|
|
19
|
+
} else {
|
|
20
|
+
return null
|
|
21
|
+
}
|
|
11
22
|
}
|
|
12
23
|
async login(provider, req) {
|
|
13
|
-
let res = await this[provider].login(req)
|
|
24
|
+
let res = await this.clients[provider].login(req)
|
|
14
25
|
return res
|
|
15
26
|
}
|
|
16
27
|
async logout(provider, req) {
|
|
17
|
-
let res = await this[provider].logout(req)
|
|
28
|
+
let res = await this.clients[provider].logout(req)
|
|
18
29
|
return res
|
|
19
30
|
}
|
|
20
31
|
async keys(provider) {
|
|
21
|
-
let res = await this[provider].keys()
|
|
32
|
+
let res = await this.clients[provider].keys()
|
|
22
33
|
return res
|
|
23
34
|
}
|
|
24
35
|
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const fetch = require('cross-fetch')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
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) {
|
|
11
|
+
this.kernel = kernel
|
|
12
|
+
}
|
|
13
|
+
async readme() {
|
|
14
|
+
return ""
|
|
15
|
+
}
|
|
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)
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
async destroy() {
|
|
32
|
+
await fs.promises.rm(this.kernel.path("connect/huggingface.json"))
|
|
33
|
+
this.auth = null
|
|
34
|
+
}
|
|
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) {
|
|
41
|
+
console.log("not authenticated")
|
|
42
|
+
return null
|
|
43
|
+
}
|
|
44
|
+
if (!this.auth.refresh_token) {
|
|
45
|
+
console.log("no refresh token")
|
|
46
|
+
return null
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// check if auth has expired
|
|
50
|
+
// if expired, refresh and return
|
|
51
|
+
if (Date.now() < this.auth.expires_at) {
|
|
52
|
+
return
|
|
53
|
+
}
|
|
54
|
+
console.log("auth expired. refresh....", JSON.stringify({ auth: this.auth, id: this.CLIENT_ID }, null, 2))
|
|
55
|
+
// expired — refresh
|
|
56
|
+
const response = await fetch(this.HF_TOKEN_URL, {
|
|
57
|
+
method: 'POST',
|
|
58
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded', },
|
|
59
|
+
body: new URLSearchParams({
|
|
60
|
+
grant_type: 'refresh_token',
|
|
61
|
+
refresh_token: this.auth.refresh_token,
|
|
62
|
+
client_id: this.CLIENT_ID,
|
|
63
|
+
})
|
|
64
|
+
}).then((res) => {
|
|
65
|
+
return res.json()
|
|
66
|
+
});
|
|
67
|
+
await this.persist(response)
|
|
68
|
+
return this.auth
|
|
69
|
+
}
|
|
70
|
+
async keys() {
|
|
71
|
+
await this.sync()
|
|
72
|
+
return this.auth
|
|
73
|
+
}
|
|
74
|
+
async login(req) {
|
|
75
|
+
console.log("huggingface login", req)
|
|
76
|
+
await this.persist(req)
|
|
77
|
+
return this.auth
|
|
78
|
+
}
|
|
79
|
+
// async login (req) {
|
|
80
|
+
// const authHeader = 'Basic ' + Buffer.from(`${this.id}:`).toString('base64');
|
|
81
|
+
// const response = await fetch('https://api.x.com/2/oauth2/token', {
|
|
82
|
+
// method: 'POST',
|
|
83
|
+
// headers: {
|
|
84
|
+
// 'Content-Type': 'application/json',
|
|
85
|
+
//// 'Authorization': authHeader
|
|
86
|
+
// },
|
|
87
|
+
// body: JSON.stringify(req.payload)
|
|
88
|
+
// }).then((res) => {
|
|
89
|
+
// return res.json()
|
|
90
|
+
// });
|
|
91
|
+
// await this.persist(response)
|
|
92
|
+
// return this.auth
|
|
93
|
+
// }
|
|
94
|
+
async logout (req) {
|
|
95
|
+
await this.destroy()
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
module.exports = Huggingface
|
package/kernel/environment.js
CHANGED
|
@@ -299,7 +299,7 @@ const ENVS = async () => {
|
|
|
299
299
|
//}
|
|
300
300
|
|
|
301
301
|
// type := system|app
|
|
302
|
-
const ENV = async (type, homedir) => {
|
|
302
|
+
const ENV = async (type, homedir, kernel) => {
|
|
303
303
|
const envs = await ENVS()
|
|
304
304
|
let filtered_envs = []
|
|
305
305
|
let irrelevant_keys = []
|
|
@@ -334,11 +334,9 @@ const ENV = async (type, homedir) => {
|
|
|
334
334
|
// if e.key exists on system env, use that
|
|
335
335
|
// if e.key does NOT exist on system env, use from the hardcoded default option
|
|
336
336
|
if (type === 'app') {
|
|
337
|
-
system_env = await get_raw(homedir)
|
|
337
|
+
system_env = await get_raw(homedir, kernel)
|
|
338
338
|
if (e.key in system_env) {
|
|
339
|
-
// console.log(`original ${e.key}=${val}`)
|
|
340
339
|
val = system_env[e.key]
|
|
341
|
-
// console.log(`inherited from system_env: ${e.key}=${val}`)
|
|
342
340
|
keys.add(e.key)
|
|
343
341
|
}
|
|
344
342
|
}
|
|
@@ -353,14 +351,10 @@ const ENV = async (type, homedir) => {
|
|
|
353
351
|
if (!keys.has(key)) {
|
|
354
352
|
// the key has not been processed, need to add to the lines
|
|
355
353
|
if (irrelevant_keys.includes(key)) {
|
|
356
|
-
// if the key was explicitly stated to be not included, skip
|
|
357
|
-
// console.log("irrelevant key", key)
|
|
358
354
|
} else {
|
|
359
|
-
// console.log("relevant key", key)
|
|
360
355
|
let val = system_env[key]
|
|
361
356
|
let kv = `${key}=${val}`
|
|
362
357
|
lines.push(kv)
|
|
363
|
-
// console.log(`inherited custom environment key from system_env: ${kv}`)
|
|
364
358
|
}
|
|
365
359
|
}
|
|
366
360
|
}
|
|
@@ -368,8 +362,8 @@ const ENV = async (type, homedir) => {
|
|
|
368
362
|
|
|
369
363
|
return lines.join("\n")
|
|
370
364
|
}
|
|
371
|
-
const init_folders = async (homedir) => {
|
|
372
|
-
const current_env = await get(homedir)
|
|
365
|
+
const init_folders = async (homedir, kernel) => {
|
|
366
|
+
const current_env = await get(homedir, kernel)
|
|
373
367
|
for(let key in current_env) {
|
|
374
368
|
let val = current_env[key]
|
|
375
369
|
|
|
@@ -391,8 +385,8 @@ const init_folders = async (homedir) => {
|
|
|
391
385
|
// Get the actual environment variable at specific path
|
|
392
386
|
const get2 = async (filepath, kernel) => {
|
|
393
387
|
let api_path = Util.api_path(filepath, kernel)
|
|
394
|
-
let default_env = await get(kernel.homedir)
|
|
395
|
-
let api_env = await get(api_path)
|
|
388
|
+
let default_env = await get(kernel.homedir, kernel)
|
|
389
|
+
let api_env = await get(api_path, kernel)
|
|
396
390
|
let process_env = kernel.envs || process.env
|
|
397
391
|
let current_env = Object.assign({}, process_env, default_env, api_env)
|
|
398
392
|
for(let key in current_env) {
|
|
@@ -407,8 +401,10 @@ const get2 = async (filepath, kernel) => {
|
|
|
407
401
|
// return env object
|
|
408
402
|
// 1. if the value starts with ./ => convert to absolute path
|
|
409
403
|
// 2. if the value is empty => don't return the kv pair for that value
|
|
410
|
-
const get = async (homedir) => {
|
|
411
|
-
const
|
|
404
|
+
const get = async (homedir, kernel) => {
|
|
405
|
+
const got_root = await get_root({ path: homedir }, kernel)
|
|
406
|
+
const root = got_root.root
|
|
407
|
+
const env_path = path.resolve(root, "ENVIRONMENT")
|
|
412
408
|
const current_env = await Util.parse_env(env_path)
|
|
413
409
|
for(let key in current_env) {
|
|
414
410
|
let val = current_env[key]
|
|
@@ -423,8 +419,10 @@ const get = async (homedir) => {
|
|
|
423
419
|
return current_env
|
|
424
420
|
}
|
|
425
421
|
|
|
426
|
-
const get_raw = async (homedir) => {
|
|
427
|
-
const
|
|
422
|
+
const get_raw = async (homedir, kernel) => {
|
|
423
|
+
const got_root = await get_root({ path: homedir }, kernel)
|
|
424
|
+
const root = got_root.root
|
|
425
|
+
const env_path = path.resolve(root, "ENVIRONMENT")
|
|
428
426
|
const current_env = await Util.parse_env(env_path)
|
|
429
427
|
for(let key in current_env) {
|
|
430
428
|
let val = current_env[key]
|
|
@@ -468,8 +466,6 @@ const requirements = async (script, cwd, kernel) => {
|
|
|
468
466
|
} else {
|
|
469
467
|
item.host = ""
|
|
470
468
|
}
|
|
471
|
-
console.log({ env, env_key })
|
|
472
|
-
|
|
473
469
|
if (env[env_key]) {
|
|
474
470
|
item.val = env[env_key]
|
|
475
471
|
} else {
|
|
@@ -485,4 +481,80 @@ const requirements = async (script, cwd, kernel) => {
|
|
|
485
481
|
}
|
|
486
482
|
return { items: pre_items, requires_instantiation }
|
|
487
483
|
}
|
|
488
|
-
|
|
484
|
+
const get_root = async (options, kernel) => {
|
|
485
|
+
let root
|
|
486
|
+
let relpath
|
|
487
|
+
if (options.path) {
|
|
488
|
+
let primary_path = path.resolve(options.path, "pinokio")
|
|
489
|
+
let primary_exists = await kernel.exists(primary_path)
|
|
490
|
+
if (primary_exists) {
|
|
491
|
+
root = primary_path
|
|
492
|
+
relpath = "pinokio"
|
|
493
|
+
} else {
|
|
494
|
+
root = options.path
|
|
495
|
+
relpath = ""
|
|
496
|
+
}
|
|
497
|
+
} else if (options.name) {
|
|
498
|
+
let primary_path = kernel.path("api", options.name, "pinokio")
|
|
499
|
+
let primary_exists = await kernel.exists(primary_path)
|
|
500
|
+
if (primary_exists) {
|
|
501
|
+
root = primary_path
|
|
502
|
+
relpath = "pinokio"
|
|
503
|
+
} else {
|
|
504
|
+
root = kernel.path("api", options.name)
|
|
505
|
+
relpath = ""
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
return { root, relpath }
|
|
509
|
+
}
|
|
510
|
+
const init = async (options, kernel) => {
|
|
511
|
+
/*
|
|
512
|
+
options = {
|
|
513
|
+
name,
|
|
514
|
+
no_inherit
|
|
515
|
+
}
|
|
516
|
+
*/
|
|
517
|
+
// check if pinokio folder exists
|
|
518
|
+
// 1. if it exists, it's pinokio/ENVIRONMENT
|
|
519
|
+
// 2. if not, it's ENVIRONMENT
|
|
520
|
+
let relpath, root
|
|
521
|
+
if (options.name) {
|
|
522
|
+
let got_root = await get_root(options, kernel)
|
|
523
|
+
relpath = got_root.relpath
|
|
524
|
+
root = got_root.root
|
|
525
|
+
} else {
|
|
526
|
+
root = kernel.homedir
|
|
527
|
+
}
|
|
528
|
+
let current = path.resolve(root, "ENVIRONMENT")
|
|
529
|
+
let exists = await kernel.exists(current)
|
|
530
|
+
if (exists) {
|
|
531
|
+
// if ENVIRONMENT already exists, don't do anything
|
|
532
|
+
} else {
|
|
533
|
+
// if ENVIRONMENT doesn't exist, need to create one
|
|
534
|
+
// 1. if _ENVIRONMENT exists, create ENVIRONMENT by appending _ENVIRONMENT to ENVIRONMENT
|
|
535
|
+
// 2. if _ENVIRONMENT doesn't exist, just write ENVIRONMENT
|
|
536
|
+
// if _ENVIRONMENT exists,
|
|
537
|
+
let _environment = path.resolve(root, "_ENVIRONMENT")
|
|
538
|
+
let _exists = await kernel.exists(_environment)
|
|
539
|
+
if (options && options.no_inherit) {
|
|
540
|
+
if (_exists) {
|
|
541
|
+
let _environmentStr = await fs.promises.readFile(_environment, "utf8")
|
|
542
|
+
await fs.promises.writeFile(current, _environmentStr)
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
let content = await ENV("app", kernel.homedir, kernel)
|
|
546
|
+
if (_exists) {
|
|
547
|
+
let _environmentStr = await fs.promises.readFile(_environment, "utf8")
|
|
548
|
+
await fs.promises.writeFile(current, _environmentStr + "\n\n\n" + content)
|
|
549
|
+
} else {
|
|
550
|
+
await fs.promises.writeFile(current, content)
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
return {
|
|
555
|
+
relpath,
|
|
556
|
+
root_path: root,
|
|
557
|
+
env_path: current
|
|
558
|
+
}
|
|
559
|
+
}
|
|
560
|
+
module.exports = { ENV, get, get2, init_folders, requirements, init, get_root }
|