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 CHANGED
@@ -10,6 +10,8 @@ npm install -g pterm
10
10
 
11
11
  # Usage
12
12
 
13
+ This README documents the stable public CLI surface.
14
+
13
15
  ## version
14
16
 
15
17
  prints the current version
@@ -20,10 +22,10 @@ prints the current version
20
22
  pterm version <type>
21
23
  ```
22
24
 
23
- - `type`: may be `terminal`, `pinokiod`, or `pinokio`
25
+ - `type`: may be `terminal`, `pinokiod`, `pinokio`, or `script`
24
26
  - `terminal`: returns the pterm version
25
27
  - `pinokiod`: returns the pinokiod version
26
- - `pinokio`: returns the pinokio version
28
+ - `pinokio`: returns the Pinokio app wrapper version
27
29
  - `script`: returns the valid script version for the current client. used for including in `pinokio.js`
28
30
 
29
31
  ### example
@@ -32,16 +34,88 @@ pterm version <type>
32
34
  pterm version terminal
33
35
  ```
34
36
 
37
+ ## refs
38
+
39
+ Pinokio resource references use this syntax:
40
+
41
+ ```
42
+ pinokio://<host>:<port>/<scope>/<id>
43
+ ```
44
+
45
+ Examples:
46
+
47
+ ```
48
+ pinokio://127.0.0.1:42000/api/cropper.git
49
+ pinokio://192.168.86.26:42000/api/facefusion-pinokio.git
50
+ ```
51
+
52
+ Currently documented scope:
53
+
54
+ - `api`: an installed Pinokio app under `PINOKIO_HOME/api`
55
+
56
+ How refs are used:
57
+
58
+ - the `host:port` in a ref identifies the target Pinokio control plane, not the app's `ready_url`
59
+ - `pterm` does not connect directly to the app endpoint described by the ref
60
+ - `pterm` talks to the local Pinokio control plane, and the local control plane resolves or forwards the ref to the target Pinokio node as needed
61
+
62
+ ## open
63
+
64
+ Open a URL on the local Pinokio node or on a connected peer node.
65
+
66
+ ### syntax
67
+
68
+ ```
69
+ pterm open <url> [--peer <peer>] [--surface browser|popup] [--preset center-small|center-medium|center-large|fullscreen]
70
+ ```
71
+
72
+ - `--peer`: optional target Pinokio node. May be a peer host, `host:port`, or peer name.
73
+ - `--surface`: optional UI surface. `popup` requests a Pinokio popup window. `browser` forces the system browser. Default is popup-preferred.
74
+ - `--preset`: optional popup size preset. Only applies when popup is used.
75
+
76
+ Behavior:
77
+
78
+ - `pterm` still talks to the local Pinokio control plane first
79
+ - if `--peer` is set, the local node forwards the open request to that peer
80
+ - if `--peer` is set, the URL is opened from that peer node's point of view, so `http://127.0.0.1:7860` means the peer's loopback
81
+ - by default, `pterm open` prefers a Pinokio popup window
82
+ - on a full Pinokio desktop node, the default opens a desktop popup
83
+ - on a server-only or minimal node, the default automatically falls back to opening the system browser
84
+ - default popup preset is `center-medium`
85
+ - `center-medium` opens centered on the active display using roughly 64% of screen width and 72% of screen height, with minimum size `900x700`
86
+
87
+ ### examples
88
+
89
+ Open a URL locally with the default popup-preferred behavior:
90
+
91
+ ```
92
+ pterm open http://127.0.0.1:7860
93
+ ```
94
+
95
+ Force the system browser instead of the default popup-preferred behavior:
96
+
97
+ ```
98
+ pterm open http://127.0.0.1:7860 --surface browser
99
+ ```
100
+
101
+ Open a URL on a peer node:
102
+
103
+ ```
104
+ pterm open http://127.0.0.1:7860 --peer 192.168.86.26
105
+ ```
106
+
35
107
  ## start
36
108
 
37
- Start a pinokio script. Arguments can be passed into the script
109
+ Start a pinokio script. `pterm` options go before `--`. Script args go after `--`.
38
110
 
39
111
  ### syntax
40
112
 
41
113
  ```
42
- pterm start <script_path> [<arg1>, <arg2>, ...]
114
+ pterm start <script_path> [--ref <pinokio_ref>] [-- --<key>=<value> ...]
43
115
  ```
44
116
 
117
+ With `--ref`, prefer relative script paths like `start.js`. `~/...` expansion is only local.
118
+
45
119
  ### examples
46
120
 
47
121
  Starting a script named `install.js`:
@@ -53,7 +127,7 @@ pterm start install.js
53
127
  Starting a script named `start.js` with parameters:
54
128
 
55
129
  ```
56
- pterm start start.js --port=3000 --model=google/gemma-3n-E4B-it
130
+ pterm start start.js -- --port=3000 --model=google/gemma-3n-E4B-it
57
131
  ```
58
132
 
59
133
  Above command starts the script `start.js` with the following args:
@@ -65,6 +139,24 @@ Above command starts the script `start.js` with the following args:
65
139
  }
66
140
  ```
67
141
 
142
+ Query parameters in the script path are also passed through as script input automatically:
143
+
144
+ ```
145
+ pterm start 'run.js?mode=Default'
146
+ ```
147
+
148
+ Starting a relative script inside a selected app:
149
+
150
+ ```
151
+ pterm start start.js --ref pinokio://127.0.0.1:42000/api/metube-pinokio.git
152
+ ```
153
+
154
+ Passing a script argument named `app` without conflicting with `pterm --ref`:
155
+
156
+ ```
157
+ pterm start start.js --ref pinokio://127.0.0.1:42000/api/metube-pinokio.git -- --app=my-script-value
158
+ ```
159
+
68
160
  Which can be accessed in the `start.js` script, for example:
69
161
 
70
162
  ```json
@@ -90,8 +182,12 @@ Stops a script if running:
90
182
 
91
183
  ```
92
184
  pterm stop <script_path>
185
+ pterm stop <script_path> --ref <pinokio_ref>
186
+ pterm stop <pinokio_ref>
93
187
  ```
94
188
 
189
+ With `--ref`, prefer relative script paths like `start.js`. `~/...` expansion is only local.
190
+
95
191
 
96
192
  ### example
97
193
 
@@ -101,6 +197,18 @@ Stop the `start.js` script if it's running:
101
197
  pterm stop start.js
102
198
  ```
103
199
 
200
+ Stop a relative script inside a selected app:
201
+
202
+ ```
203
+ pterm stop start.js --ref pinokio://127.0.0.1:42000/api/metube-pinokio.git
204
+ ```
205
+
206
+ Stop all running scripts for an app:
207
+
208
+ ```
209
+ pterm stop pinokio://127.0.0.1:42000/api/metube-pinokio.git
210
+ ```
211
+
104
212
  ## run
105
213
 
106
214
  Run a launcher. Equivalent to the user visiting a launcher page. By default it will run whichever script is the current launcher default. If the launcher exposes no explicit default, you can provide repeated `--default` selectors and Pinokio will match the first selector that exists in the launcher's current menu state.
@@ -109,6 +217,7 @@ Run a launcher. Equivalent to the user visiting a launcher page. By default it w
109
217
 
110
218
  ```
111
219
  pterm run <launcher_path_or_uri> [--default <selector>]... [--open]
220
+ pterm run <pinokio_ref> [--default <selector>]... [--open]
112
221
  ```
113
222
 
114
223
  - `--open`: (optional) open URL results in the browser. Default behavior is to print the URL to stdout without opening a browser.
@@ -143,12 +252,57 @@ pterm run ~/pinokio/api/facefusion-pinokio.git \
143
252
  --default install.js
144
253
  ```
145
254
 
146
- Launch a script directly with query parameters. Query parameters are passed through as script input automatically.
255
+ Run an installed app by Pinokio ref:
256
+
257
+ ```
258
+ pterm run pinokio://192.168.86.26:42000/api/facefusion-pinokio.git \
259
+ --default 'run.js?mode=Default' \
260
+ --default run.js \
261
+ --default install.js
262
+ ```
263
+
264
+ For direct script execution with query parameters, use `pterm start`. Query parameters are passed through as script input automatically.
147
265
 
148
266
  ```
149
267
  pterm start 'run.js?mode=Default'
150
268
  ```
151
269
 
270
+ ## download
271
+
272
+ Clone an app repo into Pinokio's app directory without launching it.
273
+
274
+ ### syntax
275
+
276
+ ```
277
+ pterm download <uri> [name] [--branch=<branch>]
278
+ pterm download <uri> [name] -b <branch>
279
+ ```
280
+
281
+ - `uri`: required git repository URI
282
+ - `name`: (optional) target folder name under `PINOKIO_HOME/api`
283
+ - `--branch` / `-b`: (optional) clone a specific branch
284
+
285
+ Behavior:
286
+
287
+ - if `name` is omitted, Pinokio uses the same default destination folder naming as `git clone <uri>`
288
+ - if `name` is provided, Pinokio clones into `PINOKIO_HOME/api/<name>`
289
+ - if the target folder already exists, the command fails with `already exists`
290
+ - the command does not dedupe by repo URL
291
+
292
+ ### examples
293
+
294
+ ```
295
+ pterm download https://github.com/example/my-launcher.git
296
+ ```
297
+
298
+ ```
299
+ pterm download https://github.com/example/my-launcher.git my-launcher-dev
300
+ ```
301
+
302
+ ```
303
+ pterm download https://github.com/example/my-launcher.git my-launcher-dev --branch=feature-x
304
+ ```
305
+
152
306
  ## search
153
307
 
154
308
  Search installed or available apps.
@@ -236,14 +390,37 @@ pterm which node
236
390
  pterm which git --json
237
391
  ```
238
392
 
393
+ ## home
394
+
395
+ Get `PINOKIO_HOME`.
396
+
397
+ ### syntax
398
+
399
+ ```
400
+ pterm home [--json]
401
+ ```
402
+
403
+ - `--json`: (optional) print the raw JSON response instead of only the path.
404
+
405
+ ### examples
406
+
407
+ ```
408
+ pterm home
409
+ ```
410
+
411
+ ```
412
+ pterm home --json
413
+ ```
414
+
239
415
  ## status
240
416
 
241
- Get app status by app id.
417
+ Get app status by app id or Pinokio ref.
242
418
 
243
419
  ### syntax
244
420
 
245
421
  ```
246
422
  pterm status <app_id> [--probe] [--timeout=<ms>]
423
+ pterm status <pinokio_ref> [--probe] [--timeout=<ms>]
247
424
  ```
248
425
 
249
426
  - `--probe`: (optional) actively probe app health.
@@ -259,6 +436,10 @@ pterm status comfyanonymous-comfyui
259
436
  pterm status comfyanonymous-comfyui --probe --timeout=5000
260
437
  ```
261
438
 
439
+ ```
440
+ pterm status pinokio://192.168.86.26:42000/api/comfyanonymous-comfyui --probe --timeout=5000
441
+ ```
442
+
262
443
  ## stars
263
444
 
264
445
  List starred apps.
@@ -314,12 +495,13 @@ pterm unstar comfyanonymous-comfyui
314
495
 
315
496
  ## logs
316
497
 
317
- Get app logs by app id.
498
+ Get app logs by app id or Pinokio ref.
318
499
 
319
500
  ### syntax
320
501
 
321
502
  ```
322
503
  pterm logs <app_id> [--script=<name>] [--tail=<lines>]
504
+ pterm logs <pinokio_ref> [--script=<name>] [--tail=<lines>]
323
505
  ```
324
506
 
325
507
  - `--script`: (optional) filter to a script.
@@ -335,11 +517,15 @@ pterm logs comfyanonymous-comfyui
335
517
  pterm logs comfyanonymous-comfyui --script=start --tail=200
336
518
  ```
337
519
 
520
+ ```
521
+ pterm logs pinokio://192.168.86.26:42000/api/comfyanonymous-comfyui --script=start --tail=200
522
+ ```
523
+
338
524
  ## filepicker
339
525
 
340
526
  Display a file picker dialog, which lets the user select one or more file or folder paths, powered by tkinter.
341
527
 
342
- This API is NOT for uploading the actual files but for submitting file paths.
528
+ This API is NOT for uploading the actual files but for submitting file paths. Use `pterm upload <pinokio_ref> <file...>` when a remote app needs real files staged onto its machine.
343
529
 
344
530
  ### syntax
345
531
 
@@ -372,12 +558,6 @@ The most basic command lets users select a single file:
372
558
  pterm filepicker
373
559
  ```
374
560
 
375
- which is equivalent to:
376
-
377
- ```
378
- pterm filepicker --type=file
379
- ```
380
-
381
561
  #### Select multiple files
382
562
 
383
563
  ```
@@ -402,6 +582,28 @@ pterm filepicker --filetype='images/*.png,*.jpg,*.jpeg'
402
582
  pterm filepicker --filetype='images/*.png,*.jpg,*.jpeg' --filetype='docs/*.pdf'
403
583
  ```
404
584
 
585
+ ## upload
586
+
587
+ Stage one or more local files onto the selected app's machine and return remote filesystem paths that can be passed to path-based tasks.
588
+
589
+ ### syntax
590
+
591
+ ```
592
+ pterm upload <app_id|pinokio_ref> <file...>
593
+ ```
594
+
595
+ Quoted `~/...` file paths are expanded locally before upload.
596
+
597
+ ### examples
598
+
599
+ ```
600
+ pterm upload facefusion-pinokio.git ./face.jpg ./video.mp4
601
+ ```
602
+
603
+ ```
604
+ pterm upload pinokio://192.168.86.26:42000/api/facefusion-pinokio.git ./face.jpg ./video.mp4
605
+ ```
606
+
405
607
  ## clipboard
406
608
 
407
609
  write to or read from clipboard
package/endpoint.js ADDED
@@ -0,0 +1,163 @@
1
+ const fs = require('fs')
2
+ const os = require('os')
3
+ const path = require('path')
4
+ const axios = require('axios')
5
+
6
+ const DEFAULT_PROTOCOL = 'http'
7
+ const DEFAULT_HOST = '127.0.0.1'
8
+ const DEFAULT_PORT = 42000
9
+ const DEFAULT_BASE_URL = `${DEFAULT_PROTOCOL}://${DEFAULT_HOST}:${DEFAULT_PORT}`
10
+ const CONFIG_PATH = path.resolve(os.homedir(), '.pinokio', 'config.json')
11
+
12
+ let resolvedHttpBaseUrlPromise = null
13
+
14
+ function formatHostForUrl(host) {
15
+ if (!host) {
16
+ return ''
17
+ }
18
+ const normalized = String(host).trim().replace(/^\[|\]$/g, '')
19
+ if (!normalized) {
20
+ return ''
21
+ }
22
+ return normalized.includes(':') ? `[${normalized}]` : normalized
23
+ }
24
+
25
+ function normalizeBaseUrl(value) {
26
+ if (typeof value !== 'string') {
27
+ return null
28
+ }
29
+ const trimmed = value.trim()
30
+ if (!trimmed) {
31
+ return null
32
+ }
33
+ try {
34
+ const url = new URL(trimmed)
35
+ if (url.protocol !== 'http:' && url.protocol !== 'https:') {
36
+ return null
37
+ }
38
+ return `${url.protocol}//${url.host}`
39
+ } catch (error) {
40
+ return null
41
+ }
42
+ }
43
+
44
+ function buildExplicitHttpBaseUrl(target) {
45
+ if (!target) {
46
+ return null
47
+ }
48
+ if (typeof target === 'string') {
49
+ const normalized = normalizeBaseUrl(target)
50
+ if (normalized) {
51
+ return normalized
52
+ }
53
+ const host = formatHostForUrl(target)
54
+ if (!host) {
55
+ return null
56
+ }
57
+ return normalizeBaseUrl(`${DEFAULT_PROTOCOL}://${host}:${DEFAULT_PORT}`)
58
+ }
59
+ if (typeof target !== 'object') {
60
+ return null
61
+ }
62
+ const protocol = target.protocol === 'https' ? 'https' : DEFAULT_PROTOCOL
63
+ const host = formatHostForUrl(target.host)
64
+ if (!host) {
65
+ return null
66
+ }
67
+ const rawPort = Number.parseInt(String(target.port), 10)
68
+ const port = Number.isFinite(rawPort) && rawPort > 0 ? rawPort : DEFAULT_PORT
69
+ return normalizeBaseUrl(`${protocol}://${host}:${port}`)
70
+ }
71
+
72
+ function readStoredAccessBaseUrl() {
73
+ try {
74
+ const raw = fs.readFileSync(CONFIG_PATH, 'utf8')
75
+ const parsed = JSON.parse(raw)
76
+ const access = parsed && parsed.access
77
+ if (!access) {
78
+ return null
79
+ }
80
+ if (typeof access === 'string') {
81
+ const value = access.trim()
82
+ if (!value) {
83
+ return null
84
+ }
85
+ if (/^https?:\/\//i.test(value)) {
86
+ return normalizeBaseUrl(value)
87
+ }
88
+ if (/^\[.*\](?::\d+)?$/.test(value) || /:\d+$/.test(value)) {
89
+ return normalizeBaseUrl(`${DEFAULT_PROTOCOL}://${value}`)
90
+ }
91
+ return normalizeBaseUrl(`${DEFAULT_PROTOCOL}://${formatHostForUrl(value)}:${DEFAULT_PORT}`)
92
+ }
93
+ if (typeof access !== 'object') {
94
+ return null
95
+ }
96
+ const protocol = access.protocol === 'https' ? 'https' : DEFAULT_PROTOCOL
97
+ const host = formatHostForUrl(access.host)
98
+ if (!host) {
99
+ return null
100
+ }
101
+ const rawPort = Number.parseInt(String(access.port), 10)
102
+ const port = Number.isFinite(rawPort) && rawPort > 0 ? rawPort : DEFAULT_PORT
103
+ return normalizeBaseUrl(`${protocol}://${host}:${port}`)
104
+ } catch (error) {
105
+ return null
106
+ }
107
+ }
108
+
109
+ async function canReachControlPlane(baseUrl) {
110
+ try {
111
+ await axios.get(`${baseUrl}/pinokio/version`, {
112
+ timeout: 1000,
113
+ headers: {
114
+ 'x-pinokio-client': 'pterm'
115
+ },
116
+ validateStatus: () => true
117
+ })
118
+ return true
119
+ } catch (error) {
120
+ return false
121
+ }
122
+ }
123
+
124
+ async function resolveHttpBaseUrl(target) {
125
+ const explicitBaseUrl = buildExplicitHttpBaseUrl(target)
126
+ if (explicitBaseUrl) {
127
+ return explicitBaseUrl
128
+ }
129
+ if (!resolvedHttpBaseUrlPromise) {
130
+ resolvedHttpBaseUrlPromise = (async () => {
131
+ if (await canReachControlPlane(DEFAULT_BASE_URL)) {
132
+ return DEFAULT_BASE_URL
133
+ }
134
+ return readStoredAccessBaseUrl() || DEFAULT_BASE_URL
135
+ })()
136
+ }
137
+ try {
138
+ return await resolvedHttpBaseUrlPromise
139
+ } catch (error) {
140
+ resolvedHttpBaseUrlPromise = null
141
+ throw error
142
+ }
143
+ }
144
+
145
+ async function resolveWsBaseUrl(target) {
146
+ const httpBaseUrl = await resolveHttpBaseUrl(target)
147
+ const normalized = normalizeBaseUrl(httpBaseUrl)
148
+ if (!normalized) {
149
+ return `ws://${DEFAULT_HOST}:${DEFAULT_PORT}`
150
+ }
151
+ const url = new URL(normalized)
152
+ url.protocol = url.protocol === 'https:' ? 'wss:' : 'ws:'
153
+ return `${url.protocol}//${url.host}`
154
+ }
155
+
156
+ module.exports = {
157
+ DEFAULT_BASE_URL,
158
+ DEFAULT_PORT,
159
+ buildExplicitHttpBaseUrl,
160
+ readStoredAccessBaseUrl,
161
+ resolveHttpBaseUrl,
162
+ resolveWsBaseUrl,
163
+ }
package/index.js CHANGED
@@ -1,18 +1,64 @@
1
1
  #!/usr/bin/env node
2
2
  const path = require('path')
3
+ const axios = require('axios')
3
4
  const yargs = require('yargs/yargs')
4
5
  const { hideBin } = require('yargs/helpers')
6
+ const { resolveHttpBaseUrl } = require('./endpoint')
7
+ const { isHttpUri, resolveAppControlTarget, resolveStartTarget } = require('./target')
8
+ const extractStartInput = (parsedArgv) => {
9
+ const rawScriptArgs = Array.isArray(parsedArgv && parsedArgv["--"]) ? parsedArgv["--"] : []
10
+ if (!rawScriptArgs.length) {
11
+ return undefined
12
+ }
13
+ const scriptArgv = yargs(rawScriptArgs)
14
+ .parserConfiguration({
15
+ 'camel-case-expansion': false,
16
+ 'dot-notation': false,
17
+ 'duplicate-arguments-array': true,
18
+ 'populate--': false
19
+ })
20
+ .help(false)
21
+ .version(false)
22
+ .parse()
23
+ const input = {}
24
+ for (const [key, value] of Object.entries(scriptArgv || {})) {
25
+ if (key === '_' || key === '$0' || value === undefined) {
26
+ continue
27
+ }
28
+ input[key] = value
29
+ }
30
+ return Object.keys(input).length > 0 ? input : undefined
31
+ }
5
32
  const argv = yargs(hideBin(process.argv))
6
33
  .option('default', {
7
34
  type: 'string',
8
35
  array: true
9
36
  })
37
+ .option('peer', {
38
+ type: 'string'
39
+ })
40
+ .option('surface', {
41
+ type: 'string'
42
+ })
43
+ .option('preset', {
44
+ type: 'string'
45
+ })
46
+ .option('ref', {
47
+ type: 'string'
48
+ })
49
+ .parserConfiguration({
50
+ 'populate--': true
51
+ })
10
52
  .parse();
11
53
  const Script = require('./script')
12
54
  const Util = require('./util')
13
55
  const script = new Script();
14
56
  const util = new Util();
15
- const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(value);
57
+ const fetchPinokioVersion = async () => {
58
+ const baseUrl = await resolveHttpBaseUrl()
59
+ const response = await axios.get(`${baseUrl}/pinokio/version`)
60
+ return response.data
61
+ }
16
62
  (async () => {
17
63
  if (argv._.length > 0) {
18
64
  let cmd = argv._[0].toLowerCase()
@@ -27,27 +73,21 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
27
73
  console.log("pterm@" + require('./package.json').version)
28
74
  } else if (app === "pinokiod") {
29
75
  try {
30
- let r = await fetch("http://localhost:42000/pinokio/version").then((res) => {
31
- return res.json()
32
- })
76
+ let r = await fetchPinokioVersion()
33
77
  console.log(`pinokiod@${r.pinokiod}`)
34
78
  } catch (e) {
35
79
  }
36
80
  } else if (app === "pinokio") {
37
81
  try {
38
- let r = await fetch("http://localhost:42000/pinokio/version").then((res) => {
39
- return res.json()
40
- })
82
+ let r = await fetchPinokioVersion()
41
83
  if (r.pinokio) {
42
- console.log(`pinokiod@${r.pinokio}`)
84
+ console.log(`pinokio@${r.pinokio}`)
43
85
  }
44
86
  } catch (e) {
45
87
  }
46
88
  } else if (app === "script") {
47
89
  try {
48
- let r = await fetch("http://localhost:42000/pinokio/version").then((res) => {
49
- return res.json()
50
- })
90
+ let r = await fetchPinokioVersion()
51
91
  if (r.script) {
52
92
  console.log(`${r.script}`)
53
93
  }
@@ -57,8 +97,10 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
57
97
  }
58
98
  } else if (cmd === "filepicker") {
59
99
  await util.filepicker(argv)
100
+ } else if (cmd === "upload") {
101
+ await util.upload(argv)
60
102
  } else if (cmd === "download") {
61
- await util.download(argv)
103
+ await util.appDownload(argv)
62
104
  } else if (cmd === "registry") {
63
105
  const subcmd = argv._.length > 1 ? String(argv._[1]).toLowerCase() : ""
64
106
  if (subcmd === "search") {
@@ -82,19 +124,27 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
82
124
  await util.status(argv)
83
125
  } else if (cmd === "logs") {
84
126
  await util.logs(argv)
127
+ } else if (cmd === "open") {
128
+ await util.open(argv)
85
129
  } else if (cmd === "stop") {
86
130
  await script.stop(argv)
87
131
  } else if (cmd === "start") {
88
132
  if (argv._.length > 1) {
89
133
  let uri = argv._[1]
90
- await script.start(uri, true)
134
+ const startTarget = await resolveStartTarget(uri, argv.ref)
135
+ const startInput = extractStartInput(argv)
136
+ const startPayload = startInput
137
+ ? { uri: startTarget.uri, input: startInput }
138
+ : startTarget.uri
139
+ await script.start(startPayload, true, null, startTarget.controlPlane)
91
140
  } else {
92
141
  console.error("required argument: <uri>")
93
142
  }
94
143
  } else if (cmd === "run") {
95
144
  if (argv._.length > 1) {
96
145
  let _uri = argv._[1]
97
- const uri = isHttpUri(_uri) ? _uri : path.resolve(process.cwd(), _uri)
146
+ const runTarget = await resolveAppControlTarget(_uri)
147
+ const uri = runTarget.uri
98
148
  // try downloading first
99
149
  if (isHttpUri(uri)) {
100
150
  await util.download({
@@ -102,16 +152,18 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
102
152
  no_exit: true
103
153
  })
104
154
  }
155
+ let launched = false
105
156
  while(true) {
106
- let default_target = await script.default_script(uri, argv.default)
157
+ let default_target = await script.default_script(uri, argv.default, runTarget.controlPlane)
107
158
  if (default_target) {
159
+ launched = true
108
160
  if (path.isAbsolute(default_target.uri)) {
109
161
  await new Promise((resolve, reject) => {
110
162
  script.start(default_target, false, (packet) => {
111
163
  if (packet.type === "result") {
112
164
  resolve()
113
165
  }
114
- })
166
+ }, runTarget.controlPlane)
115
167
  })
116
168
  if (script.killed) {
117
169
  break
@@ -133,6 +185,9 @@ const isHttpUri = (value) => typeof value === "string" && /^https?:\/\//i.test(v
133
185
  if (script.killed) {
134
186
  process.exit()
135
187
  }
188
+ if (!launched) {
189
+ process.exit(0)
190
+ }
136
191
  } else {
137
192
  console.error("required argument: <uri>")
138
193
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pterm",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "bin": {