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/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 response = await axios.get("http://localhost:42000/apps/search", {
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 appId = argv._[1]
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 = `http://localhost:42000/apps/status/${encodeURIComponent(appId)}?${params.toString()}`
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 appId = argv._[1]
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 = `http://localhost:42000/apps/logs/${encodeURIComponent(appId)}${suffix}`
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("http://localhost:42000/apps/preferences"),
111
- axios.get("http://localhost:42000/info/apps")
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 response = await axios.put(`http://localhost:42000/apps/preferences/${encodeURIComponent(appId)}`, {
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 response = await axios.get(`http://localhost:42000/pinokio/path/${encodeURIComponent(command)}`)
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 response = await axios.get("http://localhost:42000/pinokio/home")
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("ws://localhost:42000")
309
+ const rpc = new RPC(await resolveWsBaseUrl())
204
310
  if (argv.path) {
205
- argv.path = path.resolve(process.cwd(), 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
- let response = await axios.post("http://localhost:42000/clipboard", payload)
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 = path.resolve(process.cwd(), argv.image)
350
+ argv.image = this.resolveLocalPath(argv.image)
244
351
  }
245
- let response = await axios.post("http://localhost:42000/push", argv)
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
- let response = await axios.post("http://localhost:42000/go", { url })
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("ws://localhost:42000")
435
+ const rpc = new RPC(await resolveWsBaseUrl())
256
436
  await rpc.run({
257
437
  method: "kernel.bin.install2",
258
438
  params: {}