pinokiod 6.0.104 → 6.0.107

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.
@@ -123,6 +123,141 @@ class AppAPI {
123
123
  const launcher = this.ensureService(kernel)
124
124
  return launcher.refresh(req && req.params ? req.params : {})
125
125
  }
126
+
127
+ async download(req, ondata, kernel) {
128
+ /*
129
+ {
130
+ "method": "app.download",
131
+ "params": {
132
+ "uri": <required git uri>,
133
+ "name": <optional target folder name>,
134
+ "branch": <optional git branch>
135
+ }
136
+ }
137
+ */
138
+ const params = req && req.params ? req.params : {}
139
+ const uri = typeof params.uri === 'string' ? params.uri.trim() : ''
140
+ const providedName = typeof params.name === 'string' ? params.name.trim() : ''
141
+ const branch = typeof params.branch === 'string' ? params.branch.trim() : ''
142
+ if (!uri) {
143
+ throw new Error('app.download requires params.uri')
144
+ }
145
+
146
+ const name = providedName || this.deriveDownloadName(uri)
147
+ if (!name) {
148
+ return {
149
+ ok: false,
150
+ code: 'INVALID_URI',
151
+ error: 'invalid uri',
152
+ uri
153
+ }
154
+ }
155
+ if (!this.isValidDownloadName(name)) {
156
+ return {
157
+ ok: false,
158
+ code: 'INVALID_NAME',
159
+ error: 'invalid name',
160
+ name,
161
+ uri
162
+ }
163
+ }
164
+
165
+ const targetPath = kernel.path('api', name)
166
+ if (await this.pathExists(targetPath)) {
167
+ return {
168
+ ok: false,
169
+ code: 'APP_EXISTS',
170
+ error: 'already exists',
171
+ name,
172
+ path: targetPath,
173
+ uri
174
+ }
175
+ }
176
+
177
+ await kernel.bin.install2({ params: {} }, ondata)
178
+
179
+ await kernel.bin.sh({
180
+ message: this.buildGitCloneCommand({
181
+ uri,
182
+ name: providedName ? name : null,
183
+ branch
184
+ }),
185
+ path: kernel.api.userdir
186
+ }, ondata)
187
+
188
+ await kernel.api.init()
189
+ if (kernel.appLauncher && typeof kernel.appLauncher.refresh === 'function') {
190
+ try {
191
+ await kernel.appLauncher.refresh({ force: true })
192
+ } catch (_) {
193
+ }
194
+ }
195
+
196
+ const result = {
197
+ ok: true,
198
+ name,
199
+ path: targetPath,
200
+ uri
201
+ }
202
+ if (branch) {
203
+ result.branch = branch
204
+ }
205
+ return result
206
+ }
207
+ }
208
+
209
+ AppAPI.prototype.pathExists = async function pathExists(targetPath) {
210
+ try {
211
+ await fs.promises.access(targetPath)
212
+ return true
213
+ } catch (_) {
214
+ return false
215
+ }
216
+ }
217
+
218
+ AppAPI.prototype.isValidDownloadName = function isValidDownloadName(name) {
219
+ return typeof name === 'string'
220
+ && name.length > 0
221
+ && name !== '.'
222
+ && name !== '..'
223
+ && !/[\\/]/.test(name)
224
+ }
225
+
226
+ AppAPI.prototype.deriveDownloadName = function deriveDownloadName(uri) {
227
+ const trimmed = String(uri || '').trim().replace(/[\\/]+$/, '')
228
+ if (!trimmed) {
229
+ return ''
230
+ }
231
+ let lastSegment = ''
232
+ try {
233
+ const parsed = new URL(trimmed)
234
+ const pathname = parsed.pathname.replace(/\/+$/, '')
235
+ lastSegment = pathname.split('/').filter(Boolean).pop() || ''
236
+ } catch (_) {
237
+ }
238
+ if (!lastSegment) {
239
+ lastSegment = trimmed.split(/[/:]/).filter(Boolean).pop() || ''
240
+ }
241
+ if (lastSegment.toLowerCase().endsWith('.git')) {
242
+ lastSegment = lastSegment.slice(0, -4)
243
+ }
244
+ return lastSegment
245
+ }
246
+
247
+ AppAPI.prototype.shellQuote = function shellQuote(value) {
248
+ return JSON.stringify(String(value))
249
+ }
250
+
251
+ AppAPI.prototype.buildGitCloneCommand = function buildGitCloneCommand({ uri, name, branch }) {
252
+ const parts = ['git', 'clone']
253
+ if (branch) {
254
+ parts.push('--branch', this.shellQuote(branch))
255
+ }
256
+ parts.push(this.shellQuote(uri))
257
+ if (name) {
258
+ parts.push(this.shellQuote(name))
259
+ }
260
+ return parts.join(' ')
126
261
  }
127
262
 
128
263
  AppAPI.prototype.sleep = function sleep(ms) {
package/kernel/bin/cli.js CHANGED
@@ -2,7 +2,7 @@ const path = require('path')
2
2
  const semver = require('semver')
3
3
  const Util = require('../util')
4
4
  class CLI {
5
- version = ">=0.0.22"
5
+ version = ">=0.0.24"
6
6
  async install(req, ondata) {
7
7
  await this.kernel.exec({
8
8
  message: "npm install -g pterm@latest --force",
@@ -4,7 +4,8 @@ const { rimraf } = require('rimraf')
4
4
  const decompress = require('decompress');
5
5
  class Node {
6
6
  cmd() {
7
- return "nodejs=22.21.1 pnpm"
7
+ //return "nodejs=22.21.1 pnpm"
8
+ return "nodejs=25.8.1 pnpm"
8
9
  }
9
10
  async install(req, ondata) {
10
11
  await this.kernel.bin.exec({
@@ -18,7 +19,8 @@ class Node {
18
19
  async installed() {
19
20
  if (this.kernel.bin.installed.conda && this.kernel.bin.installed.conda_versions) {
20
21
  let version = this.kernel.bin.installed.conda_versions.nodejs
21
- if (version !== "22.21.1") {
22
+ //if (version !== "22.21.1") {
23
+ if (version !== "25.8.1") {
22
24
  return false
23
25
  }
24
26
  }
@@ -544,7 +544,8 @@ const init = async (options, kernel) => {
544
544
  const home = os.homedir()
545
545
  const targetDirs = [
546
546
  path.resolve(home, ".agents", "skills", skillName),
547
- path.resolve(home, ".claude", "skills", skillName)
547
+ path.resolve(home, ".claude", "skills", skillName),
548
+ path.resolve(home, ".hermes", "skills", skillName)
548
549
  ]
549
550
  for (let i = 0; i < targetDirs.length; i++) {
550
551
  const skillDir = targetDirs[i]
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "6.0.104",
3
+ "version": "6.0.107",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: pinokio
3
- description: Discover, launch, and use locally installed apps for the current task.
3
+ description: Discover, launch, and use apps and tools for the current task.
4
4
  ---
5
5
 
6
6
  # Pinokio Runtime Skill (pterm-first)
@@ -45,158 +45,159 @@ Use direct `pterm` commands for control-plane operations:
45
45
  2. `pterm status <app_id>`
46
46
  3. `pterm run <app_path> [--default <selector>]...`
47
47
  4. `pterm logs <app_id> --tail 200`
48
- 5. `pterm stars` (optional: inspect user-pinned favorites)
49
- 6. `pterm star <app_id>` / `pterm unstar <app_id>` (only when user explicitly asks to change preference)
50
-
51
- Do not run install/update commands from this skill.
52
- Do not execute bundled app binaries or internal app CLIs from the app repo unless the user explicitly asks for CLI mode.
53
- Once a Pinokio-managed app is selected, treat the launcher and its exposed interfaces as the source of truth for lifecycle and execution.
54
- Do not switch to alternate repo-local execution paths unless the user explicitly requests that mode.
55
-
56
- Permission handling:
57
- - If a probe or `pterm` command fails with loopback permission errors (`EPERM`/`EACCES` to `127.0.0.1:42000`), ask for permission first when the client supports permission prompts or escalation.
58
- - After permission is granted, rerun the same command.
59
- - Continue normal flow if rerun succeeds.
60
- - Fail only if permission/escalation is denied, unavailable, or rerun still cannot reach Pinokio.
61
- - When failing after those retries, say "Pinokio may be running, but this client cannot reach the local control plane" rather than "Pinokio is not installed/running" unless you have confirmed both conditions.
62
-
63
- ## Workflow
64
-
65
- 1. Resolve app target.
66
- - Resolve by `pterm search`.
67
- - Build one primary query from user intent:
68
- - explicit app name/vendor if user provided one
69
- - otherwise 2-4 high-signal capability tokens (example: `tts speech synthesis`)
70
- - Query hygiene:
71
- - remove duplicate/filler words (`to`, `for`, `use`, `app`, `tool`, `service`)
72
- - do not send full sentences
73
- - Run primary lookup:
74
- - if query has 3+ terms: `pterm search "<query>" --mode balanced --min-match 2 --limit 8`
75
- - if query has 1-2 terms: `pterm search "<query>" --mode balanced --min-match 1 --limit 8`
76
- - If user provided a git URL, extract owner/repo tokens and run `pterm search` with those tokens first.
77
- - Useful-hit threshold:
78
- - for 3+ term queries: candidate has `matched_terms_count >= 2` (if available)
79
- - for 1-2 term queries: candidate has `matched_terms_count >= 1` or clear top score
80
- - If no useful hits, run one fallback:
81
- - `pterm search "<query>" --mode broad --limit 8`
82
- - Deterministic ranking:
83
- - First, rank by runtime tier:
84
- - relevant apps with `ready=true`
85
- - otherwise relevant apps with `running=true`
86
- - otherwise relevant offline apps
87
- - Within the selected runtime tier, rank by user preference:
88
- - exact `app_id`/title match (for explicit app requests)
89
- - `starred=true`
90
- - Within that same tier, use the remaining tiebreakers:
91
- - higher `matched_terms_count` (if available)
92
- - higher `launch_count_total` (if available)
93
- - more recent `last_launch_at` (if available)
94
- - higher `score`
95
- - Do not choose an offline app over a relevant `ready` or `running` app.
96
- - Do not choose a non-starred app over a relevant starred app in the same runtime tier unless the starred app is clearly not a useful match.
97
- - If the top candidate is not clearly better than alternatives, ask user once with top 3 candidates.
98
-
99
- 2. Check runtime state with `pterm status`.
100
- - Poll every 2s.
101
- - Use status fields from pterm output:
102
- - `path`: absolute app path to use with `pterm run`
103
- - `running`: script is running
104
- - `ready`: app is reachable/ready
105
- - `ready_url`: base URL for API calls when available
106
- - `state`: `offline | starting | online`
107
- - Use `--probe` only for readiness confirmation before first API call (or when status is uncertain).
108
- - Use `--timeout=<ms>` only when you need a non-default probe timeout.
109
- - Treat `offline` as expected before first run.
110
-
111
- 3. If app is offline or not ready, run it.
112
- - Run `pterm run <app_path>`.
113
- - If the launcher has no explicit default item or the launch action depends on current menu state, infer one or more ordered selectors from the launcher's current menu and pass them via repeated `--default`.
114
- - Prefer stable launcher selectors such as `run.js?mode=Default`, then broader fallbacks like `run.js`, then installation fallback like `install.js`.
115
- - Continue polling with `pterm status <app_id>`.
116
- - Default startup timeout: 180s.
117
- - Do not keep searching indefinitely once an app is selected; start it.
118
-
119
- 4. Success criteria.
120
- - `state=online` and `ready=true`.
121
- - If `ready_url` exists, use it as API base URL.
122
- - Treat `ready_url` plus a generated or reused client as the default execution path for app functionality.
123
-
124
- 5. Failure criteria.
125
- - Timeout before success.
126
- - App drops back to `offline` during startup after a run attempt.
127
- - `pterm run` terminates and status never reaches ready.
128
- - On failure, fetch `pterm logs <app_id> --tail 200` and return:
129
- - raw log tail
130
- - short diagnosis
131
-
132
- 6. API call strategy (generated once, reused).
133
- - Resolve path roots before writing agent-owned files:
134
- - prefer the current working directory when it is the active writable task/workspace folder
135
- - resolve `PINOKIO_HOME` with `pterm home` when fallback global storage is needed
136
- - Generated client location:
137
- - local default: `<current_working_directory>/pinokio_agent/clients/<app_id>/<operation>.<ext>`
138
- - fallback: `<PINOKIO_HOME>/agents/clients/<app_id>/<operation>.<ext>`
139
- - Output location:
140
- - local default: `<current_working_directory>/pinokio_agent/output/<app_id>/...`
141
- - fallback: `<PINOKIO_HOME>/agents/output/<app_id>/...`
142
- - First run for `<app_id>/<operation>`:
143
- - inspect docs/code to infer endpoint + payload
144
- - generate minimal HTTP client file (`js`/`py`/`sh`)
145
- - Later runs:
146
- - reuse existing generated client file directly
147
- - Regenerate only if request indicates contract mismatch:
148
- - 404/405 endpoint mismatch
149
- - 400/422 payload/schema mismatch
150
- - auth/header mismatch
151
- - Prefer documented/public app APIs exposed by the running launcher.
152
- - Do not execute the app's internal Python/Node/bundled CLI as a fallback when `pterm` has already selected a launcher-managed app.
153
- - If no automatable API exists after the app is running, report that clearly instead of bypassing the launcher with an internal CLI.
48
+ 5. `pterm which <command>`
49
+ 6. `pterm stars` (optional: inspect user-pinned favorites)
50
+ 7. `pterm star <app_id>` / `pterm unstar <app_id>` (only when user explicitly asks to change preference)
51
+ 8. `pterm registry search "<query>"` (only after no suitable local result and user approval)
52
+ 9. `pterm download <uri> [name]` (only after a registry result is selected)
53
+
54
+ Do not run update commands from this skill.
55
+ Use `pterm download` only after local search fails, the user approves registry search, and a registry app is selected.
56
+ Once a Pinokio-managed app is selected, treat `pterm` and the launcher-managed interfaces as the source of truth for lifecycle and execution. Do not switch to repo-local CLIs or bundled app binaries unless the user explicitly asks for CLI mode.
57
+
58
+ ## How to use
59
+
60
+ Follow these sections in order:
61
+ 1. Use Search App first.
62
+ 2. Only use Registry Fallback if Search App found no suitable installed app and the user approved it.
63
+ 3. Then use Run App.
64
+ 4. Then use API Call Strategy if the app exposes an automatable API.
65
+
66
+ ### 1. Search App
67
+
68
+ - Resolve by `pterm search`.
69
+ - Build one primary query from user intent:
70
+ - explicit app name/vendor if user provided one
71
+ - otherwise 2-4 high-signal capability tokens (example: `tts speech synthesis`)
72
+ - Query hygiene:
73
+ - remove duplicate/filler words (`to`, `for`, `use`, `app`, `tool`, `service`)
74
+ - do not send full sentences
75
+ - Run primary lookup:
76
+ - if query has 3+ terms: `pterm search "<query>" --mode balanced --min-match 2 --limit 8`
77
+ - if query has 1-2 terms: `pterm search "<query>" --mode balanced --min-match 1 --limit 8`
78
+ - If user provided a git URL, extract owner/repo tokens and run `pterm search` with those tokens first.
79
+ - Useful-hit threshold:
80
+ - for 3+ term queries: candidate has `matched_terms_count >= 2` (if available)
81
+ - for 1-2 term queries: candidate has `matched_terms_count >= 1` or clear top score
82
+ - If no useful hits, run one fallback:
83
+ - `pterm search "<query>" --mode broad --limit 8`
84
+ - Deterministic ranking:
85
+ - First, rank by runtime tier:
86
+ - relevant apps with `ready=true`
87
+ - otherwise relevant apps with `running=true`
88
+ - otherwise relevant offline apps
89
+ - Within the selected runtime tier, rank by user preference:
90
+ - exact `app_id`/title match (for explicit app requests)
91
+ - `starred=true`
92
+ - Within that same tier, use the remaining tiebreakers:
93
+ - higher `matched_terms_count` (if available)
94
+ - higher `launch_count_total` (if available)
95
+ - more recent `last_launch_at` (if available)
96
+ - higher `score`
97
+ - Do not choose an offline app over a relevant `ready` or `running` app.
98
+ - Do not choose a non-starred app over a relevant starred app in the same runtime tier unless the starred app is clearly not a useful match.
99
+ - If the top candidate is not clearly better than alternatives, ask user once with top 3 candidates.
100
+ - If a suitable installed app is found, select it and continue to Run App.
101
+
102
+ ### 2. Registry Fallback
103
+
104
+ - Only use this section if Search App found no suitable installed app.
105
+ - Ask the user once whether to search the Pinokio registry for installable apps.
106
+ - Only after the user says yes:
107
+ - run `pterm registry search "<query>"`
108
+ - present the best candidates
109
+ - after the user selects one, run `pterm download <uri>`
110
+ - if `pterm download <uri>` fails with `already exists`, ask the user for a local folder name and retry with `pterm download <uri> <name>`
111
+ - if the user wants a specific local folder name or another copy of the same repo, use `pterm download <uri> <name>`
112
+ - then run the downloaded app with `pterm run <local_app_path_or_name>`
113
+ - Do not use `pterm registry search` automatically.
114
+ - Do not use `pterm run <url>` for the registry flow.
115
+
116
+ ### 3. Run App
117
+
118
+ - Once you have a selected local app, use `pterm status`.
119
+ - Poll every 2s.
120
+ - Use status fields from pterm output:
121
+ - `path`: absolute app path to use with `pterm run`
122
+ - `running`: script is running
123
+ - `ready`: app is reachable/ready
124
+ - `ready_url`: base URL for API calls when available
125
+ - `state`: `offline | starting | online`
126
+ - Use `--probe` only for readiness confirmation before first API call (or when status is uncertain).
127
+ - Use `--timeout=<ms>` only when you need a non-default probe timeout.
128
+ - Treat `offline` as expected before first run.
129
+ - If app is offline or not ready, run it:
130
+ - Run `pterm run <app_path>`.
131
+ - If the launcher has no explicit default item or the launch action depends on current menu state, infer one or more ordered selectors from the launcher's current menu and pass them via repeated `--default`.
132
+ - Prefer stable launcher selectors such as `run.js?mode=Default`, then broader fallbacks like `run.js`, then installation fallback like `install.js`.
133
+ - Continue polling with `pterm status <app_id>`.
134
+ - Default startup timeout: 180s.
135
+ - Success criteria:
136
+ - `state=online` and `ready=true`
137
+ - if `ready_url` exists, use it as API base URL
138
+ - treat `ready_url` plus a generated or reused client as the default execution path for app functionality
139
+ - Failure criteria:
140
+ - timeout before success
141
+ - app drops back to `offline` during startup after a run attempt
142
+ - `pterm run` terminates and status never reaches ready
143
+ - on failure, fetch `pterm logs <app_id> --tail 200` and return:
144
+ - raw log tail
145
+ - short diagnosis
146
+
147
+ ### 4. API Call Strategy (generated once, reused)
148
+ - Resolve path roots before writing agent-owned files:
149
+ - prefer the current working directory when it is the active writable task/workspace folder
150
+ - resolve `PINOKIO_HOME` with `pterm home` when fallback global storage is needed
151
+ - Generated client location:
152
+ - local default: `<current_working_directory>/pinokio_agent/clients/<app_id>/<operation>.<ext>`
153
+ - fallback: `<PINOKIO_HOME>/agents/clients/<app_id>/<operation>.<ext>`
154
+ - Output location:
155
+ - local default: `<current_working_directory>/pinokio_agent/output/<app_id>/...`
156
+ - fallback: `<PINOKIO_HOME>/agents/output/<app_id>/...`
157
+ - First run for `<app_id>/<operation>`:
158
+ - inspect docs/code to infer endpoint + payload
159
+ - generate minimal HTTP client file (`js`/`py`/`sh`)
160
+ - Later runs:
161
+ - reuse existing generated client file directly
162
+ - Regenerate only if request indicates contract mismatch:
163
+ - 404/405 endpoint mismatch
164
+ - 400/422 payload/schema mismatch
165
+ - auth/header mismatch
166
+ - Prefer documented/public app APIs exposed by the running launcher.
167
+ - Do not execute the app's internal Python/Node/bundled CLI as a fallback when `pterm` has already selected a launcher-managed app.
168
+ - If no automatable API exists after the app is running, report that clearly instead of bypassing the launcher with an internal CLI.
154
169
 
155
170
  ## Behavior Rules
156
171
 
157
172
  - Do not add app-specific hardcoding when user gave only capability (for example "tts").
158
173
  - Do not guess hidden endpoints when docs/code are unclear; ask one targeted question.
159
174
  - Do not rewrite launcher files unless user explicitly asked.
175
+ - When a task needs a local executable such as `python`, prefer resolving it with `pterm which <command>` before falling back to generic shell discovery.
160
176
  - Prefer returning full logs over brittle deterministic error parsing.
161
177
  - REST endpoints may be used for diagnostics only when pterm is unavailable; do not claim full install/launch lifecycle completion without compatible pterm commands.
162
178
  - Do not keep searching after app selection; move to status/run.
163
179
  - Do not conflate loopback access failure, sandbox denial, or missing permission with "Pinokio is not running" or "`pterm` is not installed."
164
180
  - On localhost permission failure, prefer asking for permission over asking the user to manually run commands.
165
181
  - If `127.0.0.1:42000` is blocked but local `pterm` exists, explicitly tell the user this looks like a client permission/sandbox issue.
166
- - After app selection, do not execute `python`, `node`, shell entrypoints, or repo-local binaries inside the selected app unless the user explicitly asks for CLI mode.
167
- - Do not inspect bundled CLI help or probe repo-local executables as an alternative execution path when the task is to operate the Pinokio-managed app.
168
- - "Launch the app server and run it" means `pterm status` -> `pterm run` -> wait for `ready=true` -> use `ready_url` with a generated/reused client.
169
- - If `ready_url` is absent, use `pterm` only for lifecycle control and ask one targeted question or report that the app lacks a clear automatable API. Do not silently fall back to internal CLI execution.
170
182
 
171
183
  ## Example A (Capability Only)
172
184
 
173
185
  User: "Generate TTS from this text: hello world"
174
186
 
175
- 1. `pterm search "tts speech synthesis" --mode balanced --min-match 2 --limit 8`
176
- 2. Pick best match using deterministic ranking (or ask once if ambiguous).
177
- 3. `pterm status <app_id>`
178
- 4. If not ready: `pterm run <app_path>`, keep polling status.
179
- 5. Before first API call: `pterm status <app_id> --probe`
180
- 6. When ready: generate/reuse `text_to_speech` client and execute.
181
- 7. Return output (audio path/bytes) or failure with `pterm logs`.
187
+ 1. Use the Search App workflow with `pterm search "tts speech synthesis" --mode balanced --min-match 2 --limit 8`.
188
+ 2. If a suitable installed app is found, continue to Run App.
189
+ 3. Otherwise, use the Registry Fallback workflow:
190
+ - ask before `pterm registry search`
191
+ - after selection: `pterm download <uri>`
192
+ - if needed after `already exists`, ask for a local folder name and retry with `pterm download <uri> <name>`
193
+ - then `pterm run <local_app_path_or_name>`
194
+ 4. Continue with Run App and then API Call Strategy.
182
195
 
183
- ## Example B (Explicit App)
184
-
185
- User: "Use Qwen-TTS to generate speech from this text: hello world"
186
-
187
- 1. `pterm search "qwen tts" --mode balanced --min-match 1 --limit 8`.
188
- 2. If exact app_id/title match exists, pick it.
189
- 3. `pterm status <app_id>`
190
- 4. Before first API call: `pterm status <app_id> --probe`
191
- 5. If not ready: `pterm run <app_path>`, keep polling.
192
- 6. When ready: generate/reuse `text_to_speech` client and execute.
193
- 7. Return output or failure with `pterm logs`.
194
-
195
- ## Example C (No Launcher Default)
196
+ ## Example B (No Launcher Default)
196
197
 
197
198
  User: "Launch FaceFusion"
198
199
 
199
- 1. Resolve app target and status as usual.
200
+ 1. Use Search App and then Run App as usual.
200
201
  2. If launcher menu has no explicit default item, infer ordered selectors from the current launcher menu.
201
202
  3. Run:
202
203
  `pterm run <app_path> --default 'run.js?mode=Default' --default run.js --default install.js`