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.
Files changed (82) hide show
  1. package/kernel/api/browser/index.js +3 -1
  2. package/kernel/api/cloudflare/index.js +3 -3
  3. package/kernel/api/index.js +187 -51
  4. package/kernel/api/loading/index.js +15 -0
  5. package/kernel/api/process/index.js +7 -0
  6. package/kernel/api/shell/index.js +0 -2
  7. package/kernel/bin/browserless.js +22 -0
  8. package/kernel/bin/caddy.js +36 -4
  9. package/kernel/bin/index.js +4 -1
  10. package/kernel/bin/setup.js +38 -5
  11. package/kernel/connect/backend.js +110 -0
  12. package/kernel/connect/config.js +171 -0
  13. package/kernel/connect/index.js +18 -7
  14. package/kernel/connect/providers/huggingface/index.js +98 -0
  15. package/kernel/connect/providers/x/index.js +0 -1
  16. package/kernel/environment.js +91 -19
  17. package/kernel/git.js +46 -3
  18. package/kernel/index.js +119 -39
  19. package/kernel/peer.js +40 -5
  20. package/kernel/plugin.js +3 -2
  21. package/kernel/procs.js +27 -20
  22. package/kernel/prototype.js +30 -16
  23. package/kernel/router/common.js +1 -1
  24. package/kernel/router/connector.js +1 -3
  25. package/kernel/router/index.js +38 -4
  26. package/kernel/router/localhost_home_router.js +5 -1
  27. package/kernel/router/localhost_port_router.js +27 -1
  28. package/kernel/router/localhost_static_router.js +93 -0
  29. package/kernel/router/localhost_variable_router.js +14 -9
  30. package/kernel/router/peer_peer_router.js +3 -0
  31. package/kernel/router/peer_static_router.js +43 -0
  32. package/kernel/router/peer_variable_router.js +15 -14
  33. package/kernel/router/processor.js +26 -1
  34. package/kernel/router/rewriter.js +59 -0
  35. package/kernel/scripts/git/commit +11 -1
  36. package/kernel/shell.js +8 -3
  37. package/kernel/util.js +65 -6
  38. package/package.json +2 -1
  39. package/server/index.js +1037 -964
  40. package/server/public/common.js +382 -1
  41. package/server/public/fscreator.js +0 -1
  42. package/server/public/loading.js +17 -0
  43. package/server/public/notifyinput.js +0 -1
  44. package/server/public/opener.js +4 -2
  45. package/server/public/style.css +311 -11
  46. package/server/socket.js +7 -1
  47. package/server/views/app.ejs +1747 -351
  48. package/server/views/columns.ejs +338 -0
  49. package/server/views/connect/huggingface.ejs +353 -0
  50. package/server/views/connect/index.ejs +410 -0
  51. package/server/views/connect/x.ejs +43 -9
  52. package/server/views/connect.ejs +709 -49
  53. package/server/views/container.ejs +357 -0
  54. package/server/views/d.ejs +251 -62
  55. package/server/views/download.ejs +54 -10
  56. package/server/views/editor.ejs +11 -0
  57. package/server/views/explore.ejs +40 -15
  58. package/server/views/file_explorer.ejs +25 -246
  59. package/server/views/form.ejs +44 -1
  60. package/server/views/frame.ejs +39 -1
  61. package/server/views/github.ejs +48 -11
  62. package/server/views/help.ejs +48 -7
  63. package/server/views/index.ejs +119 -58
  64. package/server/views/index2.ejs +3 -4
  65. package/server/views/init/index.ejs +651 -197
  66. package/server/views/install.ejs +1 -1
  67. package/server/views/mini.ejs +47 -18
  68. package/server/views/net.ejs +199 -67
  69. package/server/views/network.ejs +220 -94
  70. package/server/views/network2.ejs +3 -4
  71. package/server/views/old_network.ejs +3 -3
  72. package/server/views/prototype/index.ejs +48 -11
  73. package/server/views/review.ejs +1005 -0
  74. package/server/views/rows.ejs +341 -0
  75. package/server/views/screenshots.ejs +1020 -0
  76. package/server/views/settings.ejs +160 -23
  77. package/server/views/setup.ejs +49 -7
  78. package/server/views/setup_home.ejs +43 -10
  79. package/server/views/shell.ejs +7 -1
  80. package/server/views/start.ejs +14 -9
  81. package/server/views/terminal.ejs +13 -2
  82. 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
+ }
@@ -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.x = new X(kernel)
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 request(provider, method, req) {
9
- let res = await this[provider].request(method, req)
10
- return res
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
@@ -44,7 +44,6 @@ class X {
44
44
  // check if auth has expired
45
45
  // if expired, refresh and return
46
46
  if (Date.now() < this.auth.expires_at) {
47
- console.log("authentication valid")
48
47
  return
49
48
  }
50
49
 
@@ -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 env_path = path.resolve(homedir, "ENVIRONMENT")
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 env_path = path.resolve(homedir, "ENVIRONMENT")
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
- module.exports = { ENV, get, get2, init_folders, requirements }
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 }