pterm 0.0.15 → 0.0.17

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 CHANGED
@@ -108,9 +108,11 @@ Run a launcher. Equivalent to the user visiting a launcher page. Will run whiche
108
108
  ### syntax
109
109
 
110
110
  ```
111
- pterm run <launcher_path>
111
+ pterm run <launcher_path_or_uri> [--open]
112
112
  ```
113
113
 
114
+ - `--open`: (optional) open URL results in the browser. Default behavior is to print the URL to stdout without opening a browser.
115
+
114
116
  ### examples
115
117
 
116
118
  Launch the launcher in the current path
@@ -125,6 +127,141 @@ Launch from absolute path
125
127
  pterm run /pinokio/api/test
126
128
  ```
127
129
 
130
+ Run from a launcher URI and auto-open the resulting URL in browser
131
+
132
+ ```
133
+ pterm run https://github.com/example/my-launcher --open
134
+ ```
135
+
136
+ ## search
137
+
138
+ Search installed or available apps.
139
+
140
+ ### syntax
141
+
142
+ ```
143
+ pterm search [query words...]
144
+ pterm search --q="<query>"
145
+ pterm search "<query>" [--mode=balanced|broad|strict] [--min-match=<n>] [--limit=<n>]
146
+ ```
147
+
148
+ - `--mode`: (optional) search strategy. `broad` (default), `balanced`, or `strict`.
149
+ - `--min-match`: (optional) minimum number of query terms an app should match.
150
+ - `--limit`: (optional) max number of app results to return.
151
+
152
+ ### examples
153
+
154
+ ```
155
+ pterm search comfyui
156
+ ```
157
+
158
+ ```
159
+ pterm search --q="text generation"
160
+ ```
161
+
162
+ ```
163
+ pterm search "tts speech synthesis" --mode=balanced --min-match=2 --limit=8
164
+ ```
165
+
166
+ ## status
167
+
168
+ Get app status by app id.
169
+
170
+ ### syntax
171
+
172
+ ```
173
+ pterm status <app_id> [--probe] [--timeout=<ms>]
174
+ ```
175
+
176
+ - `--probe`: (optional) actively probe app health.
177
+ - `--timeout`: (optional) probe timeout in milliseconds.
178
+
179
+ ### examples
180
+
181
+ ```
182
+ pterm status comfyanonymous-comfyui
183
+ ```
184
+
185
+ ```
186
+ pterm status comfyanonymous-comfyui --probe --timeout=5000
187
+ ```
188
+
189
+ ## stars
190
+
191
+ List starred apps.
192
+
193
+ ### syntax
194
+
195
+ ```
196
+ pterm stars [query words...]
197
+ pterm stars --q="<query>"
198
+ ```
199
+
200
+ ### examples
201
+
202
+ ```
203
+ pterm stars
204
+ ```
205
+
206
+ ```
207
+ pterm stars tts
208
+ ```
209
+
210
+ ## star
211
+
212
+ Star an app so it is preferred on Pinokio home/search ranking.
213
+
214
+ ### syntax
215
+
216
+ ```
217
+ pterm star <app_id>
218
+ ```
219
+
220
+ ### example
221
+
222
+ ```
223
+ pterm star comfyanonymous-comfyui
224
+ ```
225
+
226
+ ## unstar
227
+
228
+ Remove star from an app.
229
+
230
+ ### syntax
231
+
232
+ ```
233
+ pterm unstar <app_id>
234
+ ```
235
+
236
+ ### example
237
+
238
+ ```
239
+ pterm unstar comfyanonymous-comfyui
240
+ ```
241
+
242
+ ## logs
243
+
244
+ Get app logs by app id.
245
+
246
+ ### syntax
247
+
248
+ ```
249
+ pterm logs <app_id> [--script=<name>] [--tail=<lines>]
250
+ ```
251
+
252
+ - `--script`: (optional) filter to a script.
253
+ - `--tail`: (optional) return only the last N lines.
254
+
255
+ ### examples
256
+
257
+ ```
258
+ pterm logs comfyanonymous-comfyui
259
+ ```
260
+
261
+ ```
262
+ pterm logs comfyanonymous-comfyui --script=start --tail=200
263
+ ```
264
+
128
265
  ## filepicker
129
266
 
130
267
  Display a file picker dialog, which lets the user select one or more file or folder paths, powered by tkinter.
@@ -268,4 +405,3 @@ pterm push 'this is a notification' --sound
268
405
  ```
269
406
  pterm push 'this is an image notification' --image=icon.png
270
407
  ```
271
-
package/index.js CHANGED
@@ -56,6 +56,12 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
56
56
  await util.download(argv)
57
57
  } else if (cmd === "search") {
58
58
  await util.search(argv)
59
+ } else if (cmd === "stars") {
60
+ await util.stars(argv)
61
+ } else if (cmd === "star") {
62
+ await util.setStar(argv, true)
63
+ } else if (cmd === "unstar") {
64
+ await util.setStar(argv, false)
59
65
  } else if (cmd === "status") {
60
66
  await util.status(argv)
61
67
  } else if (cmd === "logs") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pterm",
3
- "version": "0.0.15",
3
+ "version": "0.0.17",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {
package/rpc.js CHANGED
@@ -2,6 +2,11 @@ const { WebSocket } = require('unws')
2
2
  class RPC {
3
3
  constructor(url) {
4
4
  this.url = url
5
+ this.wsOptions = {
6
+ headers: {
7
+ "x-pinokio-client": "pterm"
8
+ }
9
+ }
5
10
  }
6
11
  async status(rpc) {
7
12
  let res = await new Promise((resolve, reject) => {
@@ -38,7 +43,7 @@ class RPC {
38
43
  if (this.ws) {
39
44
  this.ws.send(JSON.stringify(rpc))
40
45
  } else {
41
- this.ws = new WebSocket(this.url)
46
+ this.ws = new WebSocket(this.url, this.wsOptions)
42
47
  this.ws.addEventListener('open', () => {
43
48
  this.ws.send(JSON.stringify(rpc))
44
49
  });
package/script.js CHANGED
@@ -52,7 +52,14 @@ class Script {
52
52
  stop()
53
53
  });
54
54
  let default_uri = await new Promise((resolve, reject) => {
55
- rpc.run({ uri, mode: "open" }, (packet) => {
55
+ rpc.run({
56
+ uri,
57
+ mode: "open",
58
+ source: "pterm",
59
+ client: {
60
+ source: "pterm"
61
+ }
62
+ }, (packet) => {
56
63
  if (packet.data && packet.data.uri) {
57
64
  // start
58
65
  //rpc.stop({ uri })
@@ -134,9 +141,11 @@ class Script {
134
141
  });
135
142
  await rpc.run({
136
143
  uri,
144
+ source: "pterm",
137
145
  client: {
138
146
  cols,
139
147
  rows,
148
+ source: "pterm"
140
149
  }
141
150
  }, (packet) => {
142
151
  if (packet.type === "stop") {
package/util.js CHANGED
@@ -9,8 +9,27 @@ class Util {
9
9
  }
10
10
  async search(argv) {
11
11
  const query = (argv._.slice(1).join(" ") || argv.q || "").trim()
12
+ const params = { q: query }
13
+ const mode = typeof argv.mode === "string" ? argv.mode.trim().toLowerCase() : ""
14
+ const minMatchRaw = argv.minMatch ?? argv["min-match"] ?? argv.min_match
15
+ const limitRaw = argv.limit
16
+ if (mode === "broad" || mode === "balanced" || mode === "strict") {
17
+ params.mode = mode
18
+ }
19
+ if (minMatchRaw !== undefined && minMatchRaw !== null && minMatchRaw !== "") {
20
+ const minMatch = Number.parseInt(String(minMatchRaw), 10)
21
+ if (Number.isFinite(minMatch) && minMatch > 0) {
22
+ params.min_match = String(minMatch)
23
+ }
24
+ }
25
+ if (limitRaw !== undefined && limitRaw !== null && limitRaw !== "") {
26
+ const limit = Number.parseInt(String(limitRaw), 10)
27
+ if (Number.isFinite(limit) && limit > 0) {
28
+ params.limit = String(limit)
29
+ }
30
+ }
12
31
  const response = await axios.get("http://localhost:42000/apps/search", {
13
- params: { q: query }
32
+ params
14
33
  })
15
34
  this.printJson(response.data)
16
35
  }
@@ -52,6 +71,65 @@ class Util {
52
71
  const response = await axios.get(url)
53
72
  this.printJson(response.data)
54
73
  }
74
+ async stars(argv) {
75
+ const query = (argv._.slice(1).join(" ") || argv.q || "").trim().toLowerCase()
76
+ const [preferenceResponse, appResponse] = await Promise.all([
77
+ axios.get("http://localhost:42000/apps/preferences"),
78
+ axios.get("http://localhost:42000/info/apps")
79
+ ])
80
+ const preferenceItems = preferenceResponse && preferenceResponse.data && preferenceResponse.data.items
81
+ ? preferenceResponse.data.items
82
+ : {}
83
+ const apps = appResponse && appResponse.data && Array.isArray(appResponse.data.apps)
84
+ ? appResponse.data.apps
85
+ : []
86
+ const appsById = new Map()
87
+ for (const app of apps) {
88
+ if (!app || !app.name) continue
89
+ appsById.set(app.name, app)
90
+ }
91
+ const starredApps = Object.entries(preferenceItems)
92
+ .filter(([, preference]) => preference && preference.starred)
93
+ .map(([appId, preference]) => {
94
+ const app = appsById.get(appId) || {}
95
+ return {
96
+ app_id: appId,
97
+ title: app.title || appId,
98
+ description: app.description || "",
99
+ icon: app.icon || "/pinokio-black.png",
100
+ ...preference
101
+ }
102
+ })
103
+ .sort((a, b) => {
104
+ const aLast = typeof a.last_launch_at === "string" ? Date.parse(a.last_launch_at) || 0 : 0
105
+ const bLast = typeof b.last_launch_at === "string" ? Date.parse(b.last_launch_at) || 0 : 0
106
+ if (aLast !== bLast) {
107
+ return bLast - aLast
108
+ }
109
+ return String(a.title || a.app_id).localeCompare(String(b.title || b.app_id))
110
+ })
111
+ .filter((app) => {
112
+ if (!query) return true
113
+ const haystack = `${app.app_id || ""}\n${app.title || ""}\n${app.description || ""}`.toLowerCase()
114
+ return haystack.includes(query)
115
+ })
116
+ this.printJson({
117
+ q: query,
118
+ count: starredApps.length,
119
+ apps: starredApps
120
+ })
121
+ }
122
+ async setStar(argv, starred) {
123
+ if (argv._.length <= 1) {
124
+ console.error("required argument: <app_id>")
125
+ return
126
+ }
127
+ const appId = argv._[1]
128
+ const response = await axios.put(`http://localhost:42000/apps/preferences/${encodeURIComponent(appId)}`, {
129
+ starred: Boolean(starred)
130
+ })
131
+ this.printJson(response.data)
132
+ }
55
133
  async filepicker(argv) {
56
134
  const rpc = new RPC("ws://localhost:42000")
57
135
  if (argv.path) {