pterm 0.0.22 → 0.0.24

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.
Files changed (4) hide show
  1. package/README.md +71 -0
  2. package/index.js +8 -1
  3. package/package.json +1 -1
  4. package/util.js +85 -0
package/README.md CHANGED
@@ -149,6 +149,42 @@ Launch a script directly with query parameters. Query parameters are passed thro
149
149
  pterm start 'run.js?mode=Default'
150
150
  ```
151
151
 
152
+ ## download
153
+
154
+ Clone an app repo into Pinokio's app directory without launching it.
155
+
156
+ ### syntax
157
+
158
+ ```
159
+ pterm download <uri> [name] [--branch=<branch>]
160
+ pterm download <uri> [name] -b <branch>
161
+ ```
162
+
163
+ - `uri`: required git repository URI
164
+ - `name`: (optional) target folder name under `PINOKIO_HOME/api`
165
+ - `--branch` / `-b`: (optional) clone a specific branch
166
+
167
+ Behavior:
168
+
169
+ - if `name` is omitted, Pinokio uses the same default destination folder naming as `git clone <uri>`
170
+ - if `name` is provided, Pinokio clones into `PINOKIO_HOME/api/<name>`
171
+ - if the target folder already exists, the command fails with `already exists`
172
+ - the command does not dedupe by repo URL
173
+
174
+ ### examples
175
+
176
+ ```
177
+ pterm download https://github.com/example/my-launcher.git
178
+ ```
179
+
180
+ ```
181
+ pterm download https://github.com/example/my-launcher.git my-launcher-dev
182
+ ```
183
+
184
+ ```
185
+ pterm download https://github.com/example/my-launcher.git my-launcher-dev --branch=feature-x
186
+ ```
187
+
152
188
  ## search
153
189
 
154
190
  Search installed or available apps.
@@ -179,6 +215,41 @@ pterm search --q="text generation"
179
215
  pterm search "tts speech synthesis" --mode=balanced --min-match=2 --limit=8
180
216
  ```
181
217
 
218
+ ## registry search
219
+
220
+ Search the remote Pinokio registry.
221
+
222
+ ### syntax
223
+
224
+ ```
225
+ pterm registry search [query words...]
226
+ pterm registry search --q="<query>" [--limit=<n>] [--sort=relevance|popular|trending|latest|created|checkins|name] [--platform=mac|windows|linux] [--gpu=nvidia|amd|apple]
227
+ ```
228
+
229
+ - `--limit`: (optional) max number of app results to return.
230
+ - `--sort`: (optional) result ordering. Default is `relevance`.
231
+ - `--platform`: (optional) filter by observed platform support from public check-ins.
232
+ - `--gpu`: (optional) filter by observed GPU support from public check-ins.
233
+
234
+ By default, this command queries `https://api.pinokio.co/v1/search`. Override with `PINOKIO_REGISTRY_API_BASE`.
235
+
236
+ ### examples
237
+
238
+ ```
239
+ pterm registry search tts
240
+ ```
241
+
242
+ ```
243
+ pterm registry search "speech synthesis" --limit=5
244
+ ```
245
+
246
+ PINOKIO_REGISTRY_API_BASE=https://api.pinokio.co pterm registry search comfyui --platform=mac --gpu=apple
247
+ ```
248
+
249
+ ```
250
+ PINOKIO_REGISTRY_API_BASE=https://api.pinokio.co pterm registry search comfyui --sort=popular
251
+ ```
252
+
182
253
  ## which
183
254
 
184
255
  Resolve the executable path for a command name through Pinokio's environment.
package/index.js CHANGED
@@ -58,7 +58,14 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
58
58
  } else if (cmd === "filepicker") {
59
59
  await util.filepicker(argv)
60
60
  } else if (cmd === "download") {
61
- await util.download(argv)
61
+ await util.appDownload(argv)
62
+ } else if (cmd === "registry") {
63
+ const subcmd = argv._.length > 1 ? String(argv._[1]).toLowerCase() : ""
64
+ if (subcmd === "search") {
65
+ await util.registrySearch(argv)
66
+ } else {
67
+ console.error("supported subcommands: search")
68
+ }
62
69
  } else if (cmd === "search") {
63
70
  await util.search(argv)
64
71
  } else if (cmd === "stars") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pterm",
3
- "version": "0.0.22",
3
+ "version": "0.0.24",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/util.js CHANGED
@@ -7,6 +7,17 @@ class Util {
7
7
  process.stdout.write(JSON.stringify(payload, null, 2))
8
8
  process.stdout.write("\n")
9
9
  }
10
+ formatRpcError(payload) {
11
+ if (typeof payload !== "string") {
12
+ return JSON.stringify(payload)
13
+ }
14
+ const [firstLine] = payload.split(/\r?\n/)
15
+ return firstLine || payload
16
+ }
17
+ registryBase() {
18
+ const value = String(process.env.PINOKIO_REGISTRY_API_BASE || "https://api.pinokio.co").trim()
19
+ return value.replace(/\/$/, "")
20
+ }
10
21
  async search(argv) {
11
22
  const query = (argv._.slice(1).join(" ") || argv.q || "").trim()
12
23
  const params = { q: query }
@@ -33,6 +44,35 @@ class Util {
33
44
  })
34
45
  this.printJson(response.data)
35
46
  }
47
+ async registrySearch(argv) {
48
+ const query = (argv._.slice(2).join(" ") || argv.q || "").trim()
49
+ if (!query) {
50
+ console.error("required argument: <query>")
51
+ return
52
+ }
53
+ const params = { q: query }
54
+ const limitRaw = argv.limit
55
+ const sortRaw = typeof argv.sort === "string" ? argv.sort.trim().toLowerCase() : ""
56
+ const platformRaw = typeof argv.platform === "string" ? argv.platform.trim().toLowerCase() : ""
57
+ const gpuRaw = typeof argv.gpu === "string" ? argv.gpu.trim().toLowerCase() : ""
58
+ if (limitRaw !== undefined && limitRaw !== null && limitRaw !== "") {
59
+ const limit = Number.parseInt(String(limitRaw), 10)
60
+ if (Number.isFinite(limit) && limit > 0) {
61
+ params.limit = String(limit)
62
+ }
63
+ }
64
+ if (["relevance", "popular", "trending", "latest", "created", "checkins", "name"].includes(sortRaw)) {
65
+ params.sort = sortRaw
66
+ }
67
+ if (platformRaw === "mac" || platformRaw === "windows" || platformRaw === "linux") {
68
+ params.platform = platformRaw
69
+ }
70
+ if (gpuRaw === "nvidia" || gpuRaw === "amd" || gpuRaw === "apple") {
71
+ params.gpu = gpuRaw
72
+ }
73
+ const response = await axios.get(`${this.registryBase()}/v1/search`, { params })
74
+ this.printJson(response.data)
75
+ }
36
76
  async status(argv) {
37
77
  if (argv._.length <= 1) {
38
78
  console.error("required argument: <app_id>")
@@ -216,6 +256,51 @@ class Util {
216
256
  let response = await axios.post("http://localhost:42000/go", { url })
217
257
  return response
218
258
  }
259
+ async appDownload(argv) {
260
+ if (argv._.length <= 1) {
261
+ console.error("required argument: <uri>")
262
+ process.exitCode = 1
263
+ return
264
+ }
265
+ const uri = String(argv._[1]).trim()
266
+ const name = argv._.length > 2 ? String(argv._[2]).trim() : ""
267
+ const branch = typeof argv.b === "string"
268
+ ? argv.b.trim()
269
+ : (typeof argv.branch === "string" ? argv.branch.trim() : "")
270
+ const rpc = new RPC("ws://localhost:42000")
271
+ let exitCode = 0
272
+ await rpc.run({
273
+ method: "app.download",
274
+ params: {
275
+ uri,
276
+ ...(name ? { name } : {}),
277
+ ...(branch ? { branch } : {})
278
+ }
279
+ }, (packet) => {
280
+ if (packet.type === "result") {
281
+ if (!packet.data || packet.data.ok === false) {
282
+ exitCode = 1
283
+ const message = packet.data && packet.data.error ? packet.data.error : "download failed"
284
+ if (packet.data && packet.data.path) {
285
+ console.error(`${message}: ${packet.data.path}`)
286
+ } else {
287
+ console.error(message)
288
+ }
289
+ }
290
+ rpc.close()
291
+ } else if (packet.type === "stream") {
292
+ process.stdout.write(packet.data.raw)
293
+ } else if (packet.type === "error") {
294
+ exitCode = 1
295
+ console.error(this.formatRpcError(packet.data))
296
+ rpc.close()
297
+ }
298
+ })
299
+ if (exitCode !== 0) {
300
+ process.exitCode = exitCode
301
+ }
302
+ }
303
+ // Keep the legacy URL download flow for `pterm run <url>`.
219
304
  async download(argv) {
220
305
  if (argv._.length > 1) {
221
306
  let uri = argv._[1]