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.
@@ -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()
@@ -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 = {
@@ -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
@@ -924,7 +924,6 @@ class Shell {
924
924
  this.done = false
925
925
  this.ptyProcess = pty.spawn(this.shell, this.args, config)
926
926
  this.ptyProcess.onData((data) => {
927
- console.log({ data, done: this.done })
928
927
  if (!this.done) {
929
928
  this.queue.push(data)
930
929
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "3.8.1532",
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
  })