pinokiod 3.8.1532 → 3.8.1533
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/index.js +0 -2
- package/kernel/bin/setup.js +16 -0
- package/kernel/connect/index.js +25 -0
- package/kernel/connect/providers/github/README.md +11 -0
- package/kernel/connect/providers/x/README.md +96 -0
- package/kernel/connect/providers/x/index.js +152 -0
- package/kernel/index.js +2 -45
- package/kernel/router/index.js +10 -0
- package/kernel/shell.js +0 -1
- package/package.json +3 -1
- package/script/index.js +12 -0
- package/server/index.js +179 -8
- package/server/public/common.js +28 -0
- package/server/public/highlight-js.js +81 -0
- package/server/public/highlight.css +9 -0
- package/server/public/highlight.js +1244 -0
- package/server/public/x.js +63 -0
- package/server/views/500.ejs +1 -1
- package/server/views/app.ejs +2 -2
- package/server/views/connect/x.ejs +324 -0
- package/server/views/connect.ejs +197 -0
- package/server/views/editor.ejs +1 -9
- package/server/views/explore.ejs +4 -9
- package/server/views/file_explorer.ejs +3 -38
- package/server/views/github.ejs +48 -9
- package/server/views/help.ejs +1 -7
- package/server/views/index.ejs +5 -42
- package/server/views/network.ejs +15 -5
- package/server/views/prototype.ejs +1 -1
- package/server/views/settings.ejs +13 -15
- package/server/views/setup_home.ejs +2 -1
- package/server/views/terminal.ejs +1 -11
- package/LICENSE +0 -0
package/kernel/bin/index.js
CHANGED
|
@@ -646,7 +646,6 @@ class Bin {
|
|
|
646
646
|
this.client = null
|
|
647
647
|
}
|
|
648
648
|
let requirements = JSON.parse(req.params)
|
|
649
|
-
console.log({ requirements })
|
|
650
649
|
for(let x=0; x<10; x++) {
|
|
651
650
|
console.log(`## Install Attempt ${x}`)
|
|
652
651
|
let i = 0;
|
|
@@ -717,7 +716,6 @@ class Bin {
|
|
|
717
716
|
// }
|
|
718
717
|
//}
|
|
719
718
|
if (this.kernel.shell) {
|
|
720
|
-
console.log(">>> shell reset")
|
|
721
719
|
this.kernel.shell.reset()
|
|
722
720
|
}
|
|
723
721
|
await this.init()
|
package/kernel/bin/setup.js
CHANGED
|
@@ -126,5 +126,21 @@ module.exports = {
|
|
|
126
126
|
"caddy"
|
|
127
127
|
]
|
|
128
128
|
}
|
|
129
|
+
},
|
|
130
|
+
connect: (kernel) => {
|
|
131
|
+
return {
|
|
132
|
+
icon: "fa-solid fa-wifi",
|
|
133
|
+
title: "API Connect",
|
|
134
|
+
description: "Connect with 3rd party APIs",
|
|
135
|
+
requirements: [
|
|
136
|
+
{ name: "conda", },
|
|
137
|
+
{ name: "git", },
|
|
138
|
+
{ name: "caddy", },
|
|
139
|
+
],
|
|
140
|
+
conda_requirements: [
|
|
141
|
+
"git",
|
|
142
|
+
"caddy"
|
|
143
|
+
]
|
|
144
|
+
}
|
|
129
145
|
}
|
|
130
146
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const fetch = require('cross-fetch')
|
|
2
|
+
const X = require('./providers/x')
|
|
3
|
+
class Connect {
|
|
4
|
+
constructor(kernel) {
|
|
5
|
+
this.kernel = kernel
|
|
6
|
+
this.x = new X(kernel)
|
|
7
|
+
}
|
|
8
|
+
async request(provider, method, req) {
|
|
9
|
+
let res = await this[provider].request(method, req)
|
|
10
|
+
return res
|
|
11
|
+
}
|
|
12
|
+
async login(provider, req) {
|
|
13
|
+
let res = await this[provider].login(req)
|
|
14
|
+
return res
|
|
15
|
+
}
|
|
16
|
+
async logout(provider, req) {
|
|
17
|
+
let res = await this[provider].logout(req)
|
|
18
|
+
return res
|
|
19
|
+
}
|
|
20
|
+
async keys(provider) {
|
|
21
|
+
let res = await this[provider].keys()
|
|
22
|
+
return res
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
module.exports = Connect
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
# Usage
|
|
4
|
+
|
|
5
|
+
When you log into Github here, the login will be used for the git installed within Pinokio.
|
|
6
|
+
|
|
7
|
+
Logging into Github lets you do things like:
|
|
8
|
+
|
|
9
|
+
1. Cloning from Private Repositories
|
|
10
|
+
2. Publishing to Github
|
|
11
|
+
3. Use all `gh` CLI commands in all Pinokio shells ([Learn more](https://cli.github.com/))
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
# Usage
|
|
4
|
+
|
|
5
|
+
## 1. Iniitialize
|
|
6
|
+
|
|
7
|
+
First include the script
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script src="https://pinokio.localhost/x.js"></script>
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Then initialize the X object
|
|
14
|
+
|
|
15
|
+
```javascript
|
|
16
|
+
const x = new X()
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 2. Make API Calls
|
|
20
|
+
|
|
21
|
+
Now you can make API calls to X v2 API (Without worrying about authentication). For example:
|
|
22
|
+
|
|
23
|
+
```javascript
|
|
24
|
+
await x.tweet({ text: text })
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The instantiated object `x` is equivalent to the `client.v2` object from [node-twitter-api-v2](https://github.com/PLhery/node-twitter-api-v2)
|
|
28
|
+
|
|
29
|
+
Read the full API documentation here: https://github.com/PLhery/node-twitter-api-v2/blob/master/doc/v2.md
|
|
30
|
+
|
|
31
|
+
---
|
|
32
|
+
|
|
33
|
+
# Examples
|
|
34
|
+
|
|
35
|
+
## 1. Minimal Post App
|
|
36
|
+
|
|
37
|
+
The authentication is automatically taken care of. You just need to make a POST request to the `/connect/x/api` endpoint, using ANY x.com API.
|
|
38
|
+
|
|
39
|
+
```html
|
|
40
|
+
<html>
|
|
41
|
+
<body>
|
|
42
|
+
<form>
|
|
43
|
+
<input type='text' id='post'/>
|
|
44
|
+
<input type='submit'>
|
|
45
|
+
</form>
|
|
46
|
+
<script src="https://pinokio.localhost/x.js"></script>
|
|
47
|
+
<script>
|
|
48
|
+
const x = new X()
|
|
49
|
+
document.querySelector("form").addEventListener('submit', async (e) => {
|
|
50
|
+
e.preventDefault()
|
|
51
|
+
let text = document.querySelector("#post").value
|
|
52
|
+
await x.tweet({ text: text })
|
|
53
|
+
})
|
|
54
|
+
</script>
|
|
55
|
+
</body>
|
|
56
|
+
</html>
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. Upload Media App
|
|
62
|
+
|
|
63
|
+
```html
|
|
64
|
+
<html>
|
|
65
|
+
<body>
|
|
66
|
+
<form>
|
|
67
|
+
<input type='file' id='media' name='media'>
|
|
68
|
+
<input type='submit'>
|
|
69
|
+
</form>
|
|
70
|
+
<script src="https://pinokio.localhost/x.js"></script>
|
|
71
|
+
<script>
|
|
72
|
+
const x = new X()
|
|
73
|
+
document.querySelector("form").addEventListener('submit', async (e) => {
|
|
74
|
+
e.preventDefault()
|
|
75
|
+
// 1. get the uploaded file
|
|
76
|
+
let media = document.querySelector("#media").files[0]
|
|
77
|
+
// 2. determine the media_type ("tweet_gif", "tweet_image", or "tweet_video")
|
|
78
|
+
let type = media.type; // e.g., "image/png", "video/mp4", "image/gif"
|
|
79
|
+
let media_category
|
|
80
|
+
if (type.startsWith("image/")) {
|
|
81
|
+
media_category = (type === "image/gif" ? "tweet_gif" : "tweet_image");
|
|
82
|
+
} else if (type.startsWith("video/")) {
|
|
83
|
+
media_category = "tweet_video"
|
|
84
|
+
}
|
|
85
|
+
// 3. upload media
|
|
86
|
+
let media_id = await x.uploadMedia(media, { media_category })
|
|
87
|
+
// 4. post the uploaded media
|
|
88
|
+
let res = await x.tweet({ media: { media_ids: [media_id] }})
|
|
89
|
+
console.log(res)
|
|
90
|
+
})
|
|
91
|
+
</script>
|
|
92
|
+
</body>
|
|
93
|
+
</html>
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const fetch = require('cross-fetch')
|
|
4
|
+
const FormData = require('form-data');
|
|
5
|
+
const marked = require('marked')
|
|
6
|
+
const { TwitterApi } = require('twitter-api-v2')
|
|
7
|
+
class X {
|
|
8
|
+
id = 'd2FQZ0U4NXpzYnRyS1hZeHBvbUc6MTpjaQ'
|
|
9
|
+
constructor(kernel) {
|
|
10
|
+
this.kernel = kernel
|
|
11
|
+
}
|
|
12
|
+
async readme() {
|
|
13
|
+
let md = await fs.promises.readFile(path.resolve(__dirname, "README.md"), "utf8")
|
|
14
|
+
return marked.parse(md, {
|
|
15
|
+
//baseUrl: req._parsedUrl.pathname.replace(/^\/_api/, "/raw/") + "/"
|
|
16
|
+
//baseUrl: req.originalUrl + "/"
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
async persist(auth) {
|
|
20
|
+
this.auth = auth
|
|
21
|
+
this.auth.expires_at = Date.now() + this.auth.expires_in * 1000
|
|
22
|
+
let authPath = this.kernel.path('connect/x.json')
|
|
23
|
+
await fs.promises.mkdir(this.kernel.path("connect"), { recursive: true }).catch((e) => { })
|
|
24
|
+
await fs.promises.writeFile(authPath, JSON.stringify(this.auth, null, 2))
|
|
25
|
+
}
|
|
26
|
+
async destroy() {
|
|
27
|
+
await fs.promises.rm(this.kernel.path("connect/x.json"))
|
|
28
|
+
this.auth = null
|
|
29
|
+
}
|
|
30
|
+
async sync() {
|
|
31
|
+
// check if auth exists
|
|
32
|
+
// if not, throw error
|
|
33
|
+
let authPath = this.kernel.path('connect/x.json')
|
|
34
|
+
this.auth = (await this.kernel.loader.load(authPath)).resolved
|
|
35
|
+
if (!this.auth) {
|
|
36
|
+
throw new Error("not authenticated")
|
|
37
|
+
return
|
|
38
|
+
}
|
|
39
|
+
if (!this.auth.refresh_token) {
|
|
40
|
+
throw new Error("no refresh token")
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// check if auth has expired
|
|
45
|
+
// if expired, refresh and return
|
|
46
|
+
if (Date.now() < this.auth.expires_at) {
|
|
47
|
+
console.log("authentication valid")
|
|
48
|
+
return
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
console.log("auth expired. refresh....", JSON.stringify({ auth: this.auth, id: this.id }, null, 2))
|
|
53
|
+
// expired — refresh
|
|
54
|
+
const response = await fetch('https://api.twitter.com/2/oauth2/token', {
|
|
55
|
+
method: 'POST',
|
|
56
|
+
headers: { 'Content-Type': 'application/json' },
|
|
57
|
+
body: JSON.stringify({
|
|
58
|
+
grant_type: 'refresh_token',
|
|
59
|
+
refresh_token: this.auth.refresh_token,
|
|
60
|
+
client_id: this.id
|
|
61
|
+
})
|
|
62
|
+
}).then((res) => {
|
|
63
|
+
return res.json()
|
|
64
|
+
});
|
|
65
|
+
await this.persist(response)
|
|
66
|
+
return this.auth
|
|
67
|
+
}
|
|
68
|
+
async keys() {
|
|
69
|
+
await this.sync()
|
|
70
|
+
return this.auth
|
|
71
|
+
}
|
|
72
|
+
async request(method, req) {
|
|
73
|
+
/*
|
|
74
|
+
method := "uploadMedia"
|
|
75
|
+
params := {
|
|
76
|
+
"value.0", val1,
|
|
77
|
+
"type.0": "String",
|
|
78
|
+
"value.1": val2,
|
|
79
|
+
"type.1": "Blob"
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
uploadMedia(
|
|
83
|
+
val1,
|
|
84
|
+
{
|
|
85
|
+
key2: val2,
|
|
86
|
+
key3: val3
|
|
87
|
+
}
|
|
88
|
+
)
|
|
89
|
+
*/
|
|
90
|
+
await this.sync()
|
|
91
|
+
const contentType = req.headers['content-type'];
|
|
92
|
+
let args
|
|
93
|
+
if (contentType.toLowerCase() === "application/json") {
|
|
94
|
+
// don't transform
|
|
95
|
+
args = req.body
|
|
96
|
+
let response = await this.client.v2[method](...args)
|
|
97
|
+
return response
|
|
98
|
+
} else {
|
|
99
|
+
args = []
|
|
100
|
+
let body = Object.assign({}, req.body)
|
|
101
|
+
if (req.files) {
|
|
102
|
+
for (const file of req.files) {
|
|
103
|
+
body[file.fieldname] = file.buffer
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
let types = []
|
|
107
|
+
for(let key in body) {
|
|
108
|
+
let chunks = key.split(".")
|
|
109
|
+
let index = parseInt(chunks[1])
|
|
110
|
+
if (chunks[0] === "type") {
|
|
111
|
+
types[index] = body[key]
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
for(let key in body) {
|
|
115
|
+
let chunks = key.split(".")
|
|
116
|
+
let index = parseInt(chunks[1])
|
|
117
|
+
if (chunks[0] === "value") {
|
|
118
|
+
let type = types[index]
|
|
119
|
+
if (type === "Object") {
|
|
120
|
+
args[index] = JSON.parse(body[key])
|
|
121
|
+
} else {
|
|
122
|
+
args[index] = body[key]
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
console.log({ args, method })
|
|
127
|
+
|
|
128
|
+
this.client = new TwitterApi(this.auth.access_token);
|
|
129
|
+
let response = await this.client.v2[method](...args)
|
|
130
|
+
return response
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
async login (req) {
|
|
134
|
+
const authHeader = 'Basic ' + Buffer.from(`${this.id}:`).toString('base64');
|
|
135
|
+
const response = await fetch('https://api.x.com/2/oauth2/token', {
|
|
136
|
+
method: 'POST',
|
|
137
|
+
headers: {
|
|
138
|
+
'Content-Type': 'application/json',
|
|
139
|
+
// 'Authorization': authHeader
|
|
140
|
+
},
|
|
141
|
+
body: JSON.stringify(req.payload)
|
|
142
|
+
}).then((res) => {
|
|
143
|
+
return res.json()
|
|
144
|
+
});
|
|
145
|
+
await this.persist(response)
|
|
146
|
+
return this.auth
|
|
147
|
+
}
|
|
148
|
+
async logout (req) {
|
|
149
|
+
await this.destroy()
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
module.exports = X
|
package/kernel/index.js
CHANGED
|
@@ -33,6 +33,7 @@ const Proto = require('./prototype')
|
|
|
33
33
|
const Router = require("./router")
|
|
34
34
|
const Procs = require('./procs')
|
|
35
35
|
const Peer = require('./peer')
|
|
36
|
+
const Connect = require('./connect')
|
|
36
37
|
//const kill = require('./tree-kill');
|
|
37
38
|
const kill = require('kill-sync')
|
|
38
39
|
const VARS = {
|
|
@@ -45,10 +46,6 @@ const VARS = {
|
|
|
45
46
|
|
|
46
47
|
//const memwatch = require('@airbnb/node-memwatch');
|
|
47
48
|
class Kernel {
|
|
48
|
-
//schema = ">=1.0.0"
|
|
49
|
-
//schema = "<=2.1.0"
|
|
50
|
-
//schema = "<=3.2.0"
|
|
51
|
-
//schema = "<=3.7.0"
|
|
52
49
|
schema = "<=4.0.0"
|
|
53
50
|
constructor(store) {
|
|
54
51
|
this.fetch = fetch
|
|
@@ -81,13 +78,6 @@ class Kernel {
|
|
|
81
78
|
this.jsdom = jsdom
|
|
82
79
|
this.exposed = {}
|
|
83
80
|
this.envs = {}
|
|
84
|
-
// this.info = new Info(this)
|
|
85
|
-
// this.pipe = new Pipe(this)
|
|
86
|
-
// this.proto = new Proto(this)
|
|
87
|
-
// this.processes = new Procs()
|
|
88
|
-
// this.kv = new KV(this)
|
|
89
|
-
// this.cloudflare = new Cloudflare()
|
|
90
|
-
// this.peer = new Peer()
|
|
91
81
|
this.shellpath = shellPath.sync()
|
|
92
82
|
|
|
93
83
|
|
|
@@ -292,41 +282,7 @@ class Kernel {
|
|
|
292
282
|
|
|
293
283
|
// 9. announce self to the peer network
|
|
294
284
|
this.peer.announce()
|
|
295
|
-
|
|
296
285
|
}
|
|
297
|
-
|
|
298
|
-
//if (https_installed && peer_active) {
|
|
299
|
-
|
|
300
|
-
// // 1. get the process list
|
|
301
|
-
// await this.processes.refresh()
|
|
302
|
-
|
|
303
|
-
// // 2. refresh peer info to reflect the proc info
|
|
304
|
-
// await this.peer.refresh()
|
|
305
|
-
|
|
306
|
-
// // 3. load custom routers from ~/pinokio/network
|
|
307
|
-
// await this.router.init()
|
|
308
|
-
|
|
309
|
-
// // 4. set current local host router info
|
|
310
|
-
// await this.router.local()
|
|
311
|
-
|
|
312
|
-
// // 5. refresh peer info to reflect the updated router info
|
|
313
|
-
// await this.peer.refresh()
|
|
314
|
-
|
|
315
|
-
// // 6. tell peers to refresh
|
|
316
|
-
// if (notify_peers) {
|
|
317
|
-
// await this.peer.notify_peers()
|
|
318
|
-
// }
|
|
319
|
-
|
|
320
|
-
// // 7. update remote router
|
|
321
|
-
// await this.router.remote()
|
|
322
|
-
|
|
323
|
-
// // 8. update caddy config
|
|
324
|
-
// await this.router.update()
|
|
325
|
-
|
|
326
|
-
// // 9. announce self to the peer network
|
|
327
|
-
// this.peer.announce()
|
|
328
|
-
|
|
329
|
-
//}
|
|
330
286
|
}
|
|
331
287
|
async clearLog(group) {
|
|
332
288
|
let relativePath = path.relative(this.homedir, group)
|
|
@@ -599,6 +555,7 @@ class Kernel {
|
|
|
599
555
|
this.python = new Python(this)
|
|
600
556
|
this.shell = new Shells(this)
|
|
601
557
|
this.router = new Router(this)
|
|
558
|
+
this.connect = new Connect(this)
|
|
602
559
|
this.system = system
|
|
603
560
|
this.keys = {}
|
|
604
561
|
this.memory = {
|
package/kernel/router/index.js
CHANGED
|
@@ -221,5 +221,15 @@ class Router {
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
+
async check() {
|
|
225
|
+
try {
|
|
226
|
+
let res = await axios.get(`http://localhost:2019/config/`, {
|
|
227
|
+
timeout: 2000
|
|
228
|
+
})
|
|
229
|
+
return res.data
|
|
230
|
+
} catch (e) {
|
|
231
|
+
return null
|
|
232
|
+
}
|
|
233
|
+
}
|
|
224
234
|
}
|
|
225
235
|
module.exports = Router
|
package/kernel/shell.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pinokiod",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.1533",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"express-session": "^1.17.3",
|
|
29
29
|
"fake-useragent": "^1.0.1",
|
|
30
30
|
"fastq": "^1.15.0",
|
|
31
|
+
"form-data": "^4.0.2",
|
|
31
32
|
"fs-extra": "^11.1.1",
|
|
32
33
|
"glob": "^10.3.3",
|
|
33
34
|
"glob-gitignore": "^1.0.14",
|
|
@@ -60,6 +61,7 @@
|
|
|
60
61
|
"sudo-prompt-programfiles-x86": "^9.2.10",
|
|
61
62
|
"symlink-dir": "^5.2.1",
|
|
62
63
|
"systeminformation": "^5.18.4",
|
|
64
|
+
"twitter-api-v2": "^1.23.2",
|
|
63
65
|
"uuid": "^9.0.0",
|
|
64
66
|
"wait-on": "^7.2.0",
|
|
65
67
|
"which": "^5.0.0",
|
package/script/index.js
CHANGED
|
@@ -51,6 +51,18 @@ class Store {
|
|
|
51
51
|
const server = new Server({
|
|
52
52
|
port: 41999,
|
|
53
53
|
//port: 42000,
|
|
54
|
+
newsfeed: (gitRemote) => {
|
|
55
|
+
return `https://pinokiocomputer.github.io/home/item?uri=${gitRemote}&display=feed`
|
|
56
|
+
},
|
|
57
|
+
profile: (gitRemote) => {
|
|
58
|
+
return `https://pinokiocomputer.github.io/home/item?uri=${gitRemote}&display=profile`
|
|
59
|
+
},
|
|
60
|
+
site: "https://pinokiocomputer.github.io/home",
|
|
61
|
+
discover_dark: "https://pinokiocomputer.github.io/home/app",
|
|
62
|
+
discover_light: "https://pinokiocomputer.github.io/home/app",
|
|
63
|
+
portal: "https://pinokiocomputer.github.io/home/portal",
|
|
64
|
+
docs: "https://pinokiocomputer.github.io/program.pinokio.computer",
|
|
65
|
+
install: "https://pinokiocomputer.github.io/program.pinokio.computer/#/?id=install",
|
|
54
66
|
agent: "web",
|
|
55
67
|
store: new Store()
|
|
56
68
|
})
|