pterm 0.0.23 → 0.0.25
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/README.md +217 -15
- package/endpoint.js +163 -0
- package/index.js +71 -16
- package/package.json +1 -1
- package/rpc.js +26 -8
- package/script.js +114 -59
- package/target.js +437 -0
- package/util.js +197 -17
package/util.js
CHANGED
|
@@ -1,16 +1,54 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
1
2
|
const os = require('os')
|
|
2
3
|
const axios = require('axios')
|
|
3
4
|
const path = require('path')
|
|
4
5
|
const RPC = require('./rpc')
|
|
6
|
+
const { resolveHttpBaseUrl, resolveWsBaseUrl } = require('./endpoint')
|
|
7
|
+
const { isPinokioRef, parsePinokioRef, buildPinokioRef } = require('./target')
|
|
5
8
|
class Util {
|
|
9
|
+
resolveLocalPath(value) {
|
|
10
|
+
const raw = String(value)
|
|
11
|
+
if (raw.startsWith('~/')) {
|
|
12
|
+
return path.resolve(os.homedir(), raw.slice(2))
|
|
13
|
+
}
|
|
14
|
+
return path.resolve(process.cwd(), raw)
|
|
15
|
+
}
|
|
6
16
|
printJson(payload) {
|
|
7
17
|
process.stdout.write(JSON.stringify(payload, null, 2))
|
|
8
18
|
process.stdout.write("\n")
|
|
9
19
|
}
|
|
20
|
+
formatRpcError(payload) {
|
|
21
|
+
if (typeof payload !== "string") {
|
|
22
|
+
return JSON.stringify(payload)
|
|
23
|
+
}
|
|
24
|
+
const [firstLine] = payload.split(/\r?\n/)
|
|
25
|
+
return firstLine || payload
|
|
26
|
+
}
|
|
10
27
|
registryBase() {
|
|
11
28
|
const value = String(process.env.PINOKIO_REGISTRY_API_BASE || "https://api.pinokio.co").trim()
|
|
12
29
|
return value.replace(/\/$/, "")
|
|
13
30
|
}
|
|
31
|
+
async resolveResourceRequestTarget(value) {
|
|
32
|
+
const raw = String(value || '').trim()
|
|
33
|
+
if (isPinokioRef(raw)) {
|
|
34
|
+
const parsedRef = parsePinokioRef(raw)
|
|
35
|
+
if (!parsedRef.valid) {
|
|
36
|
+
throw new Error(parsedRef.error || 'Invalid ref')
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
baseUrl: await resolveHttpBaseUrl(),
|
|
40
|
+
ref: buildPinokioRef(parsedRef),
|
|
41
|
+
legacyId: null,
|
|
42
|
+
parsedRef
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
baseUrl: await resolveHttpBaseUrl(),
|
|
47
|
+
ref: null,
|
|
48
|
+
legacyId: raw,
|
|
49
|
+
parsedRef: null
|
|
50
|
+
}
|
|
51
|
+
}
|
|
14
52
|
async search(argv) {
|
|
15
53
|
const query = (argv._.slice(1).join(" ") || argv.q || "").trim()
|
|
16
54
|
const params = { q: query }
|
|
@@ -32,7 +70,8 @@ class Util {
|
|
|
32
70
|
params.limit = String(limit)
|
|
33
71
|
}
|
|
34
72
|
}
|
|
35
|
-
const
|
|
73
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
74
|
+
const response = await axios.get(`${baseUrl}/apps/search`, {
|
|
36
75
|
params
|
|
37
76
|
})
|
|
38
77
|
this.printJson(response.data)
|
|
@@ -71,15 +110,20 @@ class Util {
|
|
|
71
110
|
console.error("required argument: <app_id>")
|
|
72
111
|
return
|
|
73
112
|
}
|
|
74
|
-
const
|
|
113
|
+
const target = await this.resolveResourceRequestTarget(argv._[1])
|
|
75
114
|
const probe = argv.probe ? "1" : "0"
|
|
76
115
|
const timeout = argv.timeout ? Number.parseInt(String(argv.timeout), 10) : null
|
|
77
116
|
const params = new URLSearchParams()
|
|
117
|
+
if (target.ref) {
|
|
118
|
+
params.set("ref", target.ref)
|
|
119
|
+
}
|
|
78
120
|
params.set("probe", probe)
|
|
79
121
|
if (Number.isFinite(timeout) && timeout > 0) {
|
|
80
122
|
params.set("timeout", String(timeout))
|
|
81
123
|
}
|
|
82
|
-
const url =
|
|
124
|
+
const url = target.ref
|
|
125
|
+
? `${target.baseUrl}/pinokio/resource/status?${params.toString()}`
|
|
126
|
+
: `${target.baseUrl}/apps/status/${encodeURIComponent(target.legacyId)}?${params.toString()}`
|
|
83
127
|
const response = await axios.get(url)
|
|
84
128
|
this.printJson(response.data)
|
|
85
129
|
}
|
|
@@ -88,8 +132,11 @@ class Util {
|
|
|
88
132
|
console.error("required argument: <app_id>")
|
|
89
133
|
return
|
|
90
134
|
}
|
|
91
|
-
const
|
|
135
|
+
const target = await this.resolveResourceRequestTarget(argv._[1])
|
|
92
136
|
const params = new URLSearchParams()
|
|
137
|
+
if (target.ref) {
|
|
138
|
+
params.set("ref", target.ref)
|
|
139
|
+
}
|
|
93
140
|
if (argv.script) {
|
|
94
141
|
params.set("script", String(argv.script))
|
|
95
142
|
}
|
|
@@ -100,15 +147,71 @@ class Util {
|
|
|
100
147
|
}
|
|
101
148
|
}
|
|
102
149
|
const suffix = params.toString() ? `?${params.toString()}` : ""
|
|
103
|
-
const url =
|
|
150
|
+
const url = target.ref
|
|
151
|
+
? `${target.baseUrl}/pinokio/resource/logs${suffix}`
|
|
152
|
+
: `${target.baseUrl}/apps/logs/${encodeURIComponent(target.legacyId)}${suffix}`
|
|
104
153
|
const response = await axios.get(url)
|
|
105
154
|
this.printJson(response.data)
|
|
106
155
|
}
|
|
156
|
+
async upload(argv) {
|
|
157
|
+
if (argv._.length <= 2) {
|
|
158
|
+
console.error("required arguments: <app_id> <file...>")
|
|
159
|
+
process.exitCode = 1
|
|
160
|
+
return
|
|
161
|
+
}
|
|
162
|
+
const target = await this.resolveResourceRequestTarget(argv._[1])
|
|
163
|
+
if (!target.ref && !target.legacyId) {
|
|
164
|
+
console.error("required argument: <app_id>")
|
|
165
|
+
process.exitCode = 1
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
const fileArgs = argv._.slice(2)
|
|
169
|
+
const form = new FormData()
|
|
170
|
+
for (const rawPath of fileArgs) {
|
|
171
|
+
const resolvedPath = this.resolveLocalPath(rawPath)
|
|
172
|
+
let stat
|
|
173
|
+
try {
|
|
174
|
+
stat = await fs.promises.stat(resolvedPath)
|
|
175
|
+
} catch (_) {
|
|
176
|
+
console.error(`file not found: ${resolvedPath}`)
|
|
177
|
+
process.exitCode = 1
|
|
178
|
+
return
|
|
179
|
+
}
|
|
180
|
+
if (!stat.isFile()) {
|
|
181
|
+
console.error(`not a file: ${resolvedPath}`)
|
|
182
|
+
process.exitCode = 1
|
|
183
|
+
return
|
|
184
|
+
}
|
|
185
|
+
const buffer = await fs.promises.readFile(resolvedPath)
|
|
186
|
+
form.append('files', new Blob([buffer]), path.basename(resolvedPath))
|
|
187
|
+
}
|
|
188
|
+
const uploadUrl = target.ref
|
|
189
|
+
? `${target.baseUrl}/pinokio/resource/upload?ref=${encodeURIComponent(target.ref)}`
|
|
190
|
+
: `${target.baseUrl}/apps/${encodeURIComponent(target.legacyId)}/upload`
|
|
191
|
+
const response = await fetch(uploadUrl, {
|
|
192
|
+
method: 'POST',
|
|
193
|
+
body: form
|
|
194
|
+
})
|
|
195
|
+
const raw = await response.text()
|
|
196
|
+
let payload
|
|
197
|
+
try {
|
|
198
|
+
payload = raw ? JSON.parse(raw) : {}
|
|
199
|
+
} catch (_) {
|
|
200
|
+
payload = { error: raw || `Upload failed (${response.status})` }
|
|
201
|
+
}
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
this.printJson(payload)
|
|
204
|
+
process.exitCode = 1
|
|
205
|
+
return
|
|
206
|
+
}
|
|
207
|
+
this.printJson(payload)
|
|
208
|
+
}
|
|
107
209
|
async stars(argv) {
|
|
108
210
|
const query = (argv._.slice(1).join(" ") || argv.q || "").trim().toLowerCase()
|
|
211
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
109
212
|
const [preferenceResponse, appResponse] = await Promise.all([
|
|
110
|
-
axios.get(
|
|
111
|
-
axios.get(
|
|
213
|
+
axios.get(`${baseUrl}/apps/preferences`),
|
|
214
|
+
axios.get(`${baseUrl}/info/apps`)
|
|
112
215
|
])
|
|
113
216
|
const preferenceItems = preferenceResponse && preferenceResponse.data && preferenceResponse.data.items
|
|
114
217
|
? preferenceResponse.data.items
|
|
@@ -158,7 +261,8 @@ class Util {
|
|
|
158
261
|
return
|
|
159
262
|
}
|
|
160
263
|
const appId = argv._[1]
|
|
161
|
-
const
|
|
264
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
265
|
+
const response = await axios.put(`${baseUrl}/apps/preferences/${encodeURIComponent(appId)}`, {
|
|
162
266
|
starred: Boolean(starred)
|
|
163
267
|
})
|
|
164
268
|
this.printJson(response.data)
|
|
@@ -174,7 +278,8 @@ class Util {
|
|
|
174
278
|
return
|
|
175
279
|
}
|
|
176
280
|
try {
|
|
177
|
-
const
|
|
281
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
282
|
+
const response = await axios.get(`${baseUrl}/pinokio/path/${encodeURIComponent(command)}`)
|
|
178
283
|
if (argv.json) {
|
|
179
284
|
this.printJson(response.data)
|
|
180
285
|
} else if (response.data && response.data.path) {
|
|
@@ -191,7 +296,8 @@ class Util {
|
|
|
191
296
|
}
|
|
192
297
|
}
|
|
193
298
|
async home(argv) {
|
|
194
|
-
const
|
|
299
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
300
|
+
const response = await axios.get(`${baseUrl}/pinokio/home`)
|
|
195
301
|
if (argv.json) {
|
|
196
302
|
this.printJson(response.data)
|
|
197
303
|
} else if (response.data && response.data.path) {
|
|
@@ -200,9 +306,9 @@ class Util {
|
|
|
200
306
|
}
|
|
201
307
|
}
|
|
202
308
|
async filepicker(argv) {
|
|
203
|
-
const rpc = new RPC(
|
|
309
|
+
const rpc = new RPC(await resolveWsBaseUrl())
|
|
204
310
|
if (argv.path) {
|
|
205
|
-
argv.path =
|
|
311
|
+
argv.path = this.resolveLocalPath(argv.path)
|
|
206
312
|
}
|
|
207
313
|
await rpc.run({
|
|
208
314
|
method: "kernel.bin.filepicker",
|
|
@@ -229,7 +335,8 @@ class Util {
|
|
|
229
335
|
if (argv._.length > 2) {
|
|
230
336
|
payload.text = argv._[2]
|
|
231
337
|
}
|
|
232
|
-
|
|
338
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
339
|
+
let response = await axios.post(`${baseUrl}/clipboard`, payload)
|
|
233
340
|
if (response.data && response.data.text) {
|
|
234
341
|
console.log(response.data.text)
|
|
235
342
|
}
|
|
@@ -240,19 +347,92 @@ class Util {
|
|
|
240
347
|
argv.message = argv._[1]
|
|
241
348
|
}
|
|
242
349
|
if (argv.image && !path.isAbsolute(argv.image)) {
|
|
243
|
-
argv.image =
|
|
350
|
+
argv.image = this.resolveLocalPath(argv.image)
|
|
244
351
|
}
|
|
245
|
-
|
|
352
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
353
|
+
let response = await axios.post(`${baseUrl}/push`, argv)
|
|
246
354
|
return response
|
|
247
355
|
}
|
|
248
356
|
async open_url(url) {
|
|
249
|
-
|
|
357
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
358
|
+
let response = await axios.post(`${baseUrl}/go`, { url })
|
|
250
359
|
return response
|
|
251
360
|
}
|
|
361
|
+
async open(argv) {
|
|
362
|
+
if (argv._.length <= 1) {
|
|
363
|
+
console.error("required argument: <url>")
|
|
364
|
+
process.exitCode = 1
|
|
365
|
+
return
|
|
366
|
+
}
|
|
367
|
+
const url = String(argv._[1] || '').trim()
|
|
368
|
+
if (!url) {
|
|
369
|
+
console.error("required argument: <url>")
|
|
370
|
+
process.exitCode = 1
|
|
371
|
+
return
|
|
372
|
+
}
|
|
373
|
+
const payload = { url }
|
|
374
|
+
if (typeof argv.peer === 'string' && argv.peer.trim()) {
|
|
375
|
+
payload.peer = argv.peer.trim()
|
|
376
|
+
}
|
|
377
|
+
if (typeof argv.surface === 'string' && argv.surface.trim()) {
|
|
378
|
+
payload.surface = argv.surface.trim()
|
|
379
|
+
}
|
|
380
|
+
if (typeof argv.preset === 'string' && argv.preset.trim()) {
|
|
381
|
+
payload.preset = argv.preset.trim()
|
|
382
|
+
}
|
|
383
|
+
const baseUrl = await resolveHttpBaseUrl()
|
|
384
|
+
const response = await axios.post(`${baseUrl}/pinokio/open`, payload)
|
|
385
|
+
this.printJson(response.data)
|
|
386
|
+
}
|
|
387
|
+
async appDownload(argv) {
|
|
388
|
+
if (argv._.length <= 1) {
|
|
389
|
+
console.error("required argument: <uri>")
|
|
390
|
+
process.exitCode = 1
|
|
391
|
+
return
|
|
392
|
+
}
|
|
393
|
+
const uri = String(argv._[1]).trim()
|
|
394
|
+
const name = argv._.length > 2 ? String(argv._[2]).trim() : ""
|
|
395
|
+
const branch = typeof argv.b === "string"
|
|
396
|
+
? argv.b.trim()
|
|
397
|
+
: (typeof argv.branch === "string" ? argv.branch.trim() : "")
|
|
398
|
+
const rpc = new RPC(await resolveWsBaseUrl())
|
|
399
|
+
let exitCode = 0
|
|
400
|
+
await rpc.run({
|
|
401
|
+
method: "app.download",
|
|
402
|
+
params: {
|
|
403
|
+
uri,
|
|
404
|
+
...(name ? { name } : {}),
|
|
405
|
+
...(branch ? { branch } : {})
|
|
406
|
+
}
|
|
407
|
+
}, (packet) => {
|
|
408
|
+
if (packet.type === "result") {
|
|
409
|
+
if (!packet.data || packet.data.ok === false) {
|
|
410
|
+
exitCode = 1
|
|
411
|
+
const message = packet.data && packet.data.error ? packet.data.error : "download failed"
|
|
412
|
+
if (packet.data && packet.data.path) {
|
|
413
|
+
console.error(`${message}: ${packet.data.path}`)
|
|
414
|
+
} else {
|
|
415
|
+
console.error(message)
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
rpc.close()
|
|
419
|
+
} else if (packet.type === "stream") {
|
|
420
|
+
process.stdout.write(packet.data.raw)
|
|
421
|
+
} else if (packet.type === "error") {
|
|
422
|
+
exitCode = 1
|
|
423
|
+
console.error(this.formatRpcError(packet.data))
|
|
424
|
+
rpc.close()
|
|
425
|
+
}
|
|
426
|
+
})
|
|
427
|
+
if (exitCode !== 0) {
|
|
428
|
+
process.exitCode = exitCode
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
// Keep the legacy URL download flow for `pterm run <url>`.
|
|
252
432
|
async download(argv) {
|
|
253
433
|
if (argv._.length > 1) {
|
|
254
434
|
let uri = argv._[1]
|
|
255
|
-
const rpc = new RPC(
|
|
435
|
+
const rpc = new RPC(await resolveWsBaseUrl())
|
|
256
436
|
await rpc.run({
|
|
257
437
|
method: "kernel.bin.install2",
|
|
258
438
|
params: {}
|