pinokiod 8.2.10 → 8.2.12
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/anywhere/cli.js +100 -0
- package/kernel/anywhere/index.js +237 -161
- package/kernel/anywhere/ports.js +6 -9
- package/kernel/anywhere/serve.js +17 -7
- package/kernel/anywhere/state.js +14 -0
- package/kernel/bin/conda-meta.js +19 -1
- package/kernel/bin/conda-pins.js +7 -3
- package/kernel/bin/conda.js +67 -15
- package/kernel/bin/index.js +5 -2
- package/kernel/bin/setup.js +3 -17
- package/kernel/bin/tailscaled.js +23 -13
- package/kernel/index.js +11 -9
- package/kernel/shell.js +14 -6
- package/kernel/util.js +1 -0
- package/package.json +1 -1
- package/server/index.js +15 -19
- package/server/public/universal-launcher.js +5 -0
- package/server/views/anywhere.ejs +13 -13
- package/server/views/partials/main_sidebar.ejs +0 -1
- package/server/views/remote.ejs +12 -4
- package/system/pip_seed.json +6 -0
- package/system/repair_pip_seed.py +100 -0
- package/test/anywhere-remote.test.js +182 -8
- package/test/anywhere.test.js +1063 -131
- package/test/conda-bin.test.js +254 -4
- package/test/pip_seed_test.py +183 -0
- package/test/shell-run-template.test.js +147 -140
- package/test/tailscale-cli.test.js +239 -0
- package/kernel/anywhere/localapi.js +0 -147
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const { execFile } = require('child_process')
|
|
2
|
+
const net = require('net')
|
|
3
|
+
const { isDeepStrictEqual } = require('util')
|
|
4
|
+
const { FIRST_PORT, LAST_PORT } = require('./ports')
|
|
5
|
+
|
|
6
|
+
// All control goes through the pinned, bundled CLI and this explicit endpoint.
|
|
7
|
+
class TailscaleCLI {
|
|
8
|
+
constructor({ binPath, socketPath }) {
|
|
9
|
+
this.binPath = binPath
|
|
10
|
+
this.socketPath = socketPath
|
|
11
|
+
this.children = new Set()
|
|
12
|
+
}
|
|
13
|
+
run(args, { timeout = 10000, onOutput } = {}) {
|
|
14
|
+
return new Promise((resolve, reject) => {
|
|
15
|
+
const child = execFile(this.binPath, ['--socket=' + this.socketPath, ...args], {
|
|
16
|
+
shell: false, windowsHide: true, timeout, maxBuffer: 4 * 1024 * 1024,
|
|
17
|
+
}, (error, stdout, stderr) => {
|
|
18
|
+
this.children.delete(child)
|
|
19
|
+
if (error) {
|
|
20
|
+
error.message = `tailscale ${args[0]}: ${stderr.trim() || stdout.trim() || error.message}`
|
|
21
|
+
error.cancelled = !!child.cancelled
|
|
22
|
+
reject(error)
|
|
23
|
+
} else resolve(stdout)
|
|
24
|
+
})
|
|
25
|
+
this.children.add(child)
|
|
26
|
+
if (onOutput) {
|
|
27
|
+
let output = ''
|
|
28
|
+
child.stdout.on('data', (chunk) => {
|
|
29
|
+
output += chunk
|
|
30
|
+
try { if (onOutput) onOutput(output) }
|
|
31
|
+
catch (error) { onOutput = null; child.kill(); reject(error) }
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
cancel() {
|
|
37
|
+
for (const child of this.children) { child.cancelled = true; child.kill() }
|
|
38
|
+
}
|
|
39
|
+
async json(args, options) {
|
|
40
|
+
return JSON.parse(await this.run(args, options))
|
|
41
|
+
}
|
|
42
|
+
status(timeout = 10000) { return this.json(['status', '--json'], { timeout }) }
|
|
43
|
+
async ping() {
|
|
44
|
+
try {
|
|
45
|
+
await this.status(2000)
|
|
46
|
+
return true
|
|
47
|
+
} catch (error) {
|
|
48
|
+
// A CLI failure alone says nothing about liveness. Only a missing/refused
|
|
49
|
+
// endpoint confirms absence; this connection probe sends no API requests.
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
const socket = net.createConnection(this.socketPath)
|
|
52
|
+
socket.once('connect', () => { socket.destroy(); reject(error) })
|
|
53
|
+
socket.once('error', (cause) => {
|
|
54
|
+
if (cause.code === 'ENOENT' || cause.code === 'ECONNREFUSED') resolve(false)
|
|
55
|
+
else reject(error)
|
|
56
|
+
})
|
|
57
|
+
socket.setTimeout(2000, () => { socket.destroy(); reject(error) })
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
setPrefs(hostname) {
|
|
62
|
+
return this.run(['set', '--hostname=' + hostname, '--accept-dns=false', '--accept-routes=false', '--exit-node='])
|
|
63
|
+
}
|
|
64
|
+
up(onUpdate, force = false, hostname = 'pinokio') {
|
|
65
|
+
let emitted = 0
|
|
66
|
+
return this.run(['up', '--json', '--timeout=5m', '--hostname=' + hostname,
|
|
67
|
+
'--accept-dns=false', '--accept-routes=false', '--exit-node=', ...(force ? ['--force-reauth'] : [])], {
|
|
68
|
+
timeout: 305000,
|
|
69
|
+
onOutput(output) {
|
|
70
|
+
// The pinned CLI emits pretty-printed top-level JSON objects.
|
|
71
|
+
for (const block of (output.match(/^\{[\s\S]*?^\}/gm) || []).slice(emitted)) {
|
|
72
|
+
emitted += 1
|
|
73
|
+
onUpdate(JSON.parse(block))
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
logout() { return this.run(['logout']) }
|
|
79
|
+
queryFeature() { return this.json(['debug', 'localapi', 'POST', '/localapi/v0/query-feature?feature=serve']) }
|
|
80
|
+
serveConfig() { return this.json(['serve', 'status', '--json']) }
|
|
81
|
+
async setServeConfig(desired, current) {
|
|
82
|
+
current = current || {}
|
|
83
|
+
for (const [hostPort, web] of Object.entries(desired.Web)) {
|
|
84
|
+
const port = hostPort.slice(hostPort.lastIndexOf(':') + 1)
|
|
85
|
+
if (isDeepStrictEqual(current.Web && current.Web[hostPort], web) &&
|
|
86
|
+
isDeepStrictEqual(current.TCP && current.TCP[port], desired.TCP[port])) continue
|
|
87
|
+
// Each command reads the latest config and writes with its ETag. The CLI
|
|
88
|
+
// reports conflicts; never use set-raw/reset, which lose that protection.
|
|
89
|
+
await this.run(['serve', '--bg', '--https=' + port, web.Handlers['/'].Proxy])
|
|
90
|
+
}
|
|
91
|
+
for (const port of Object.keys(current.TCP || {})) {
|
|
92
|
+
if (!desired.TCP[port] && Number(port) >= FIRST_PORT && Number(port) <= LAST_PORT) {
|
|
93
|
+
await this.run(['serve', '--https=' + port, 'off'])
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return this.serveConfig()
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
module.exports = TailscaleCLI
|