@parall/daemon 1.30.0 → 1.32.0

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 (53) hide show
  1. package/bundle/manifest.json +17 -11
  2. package/bundle/package.json +1 -0
  3. package/bundle/parall-claude-agent.js +27671 -337
  4. package/bundle/parall-codex-agent.js +27715 -375
  5. package/bundle/parall-daemon.js +30421 -1355
  6. package/bundle/parall-openclaw-agent.js +51 -26
  7. package/dist/cli.d.ts +1 -1
  8. package/dist/cli.d.ts.map +1 -1
  9. package/dist/cli.js +122 -72
  10. package/dist/clip-runtime/clip-installer.d.ts +44 -0
  11. package/dist/clip-runtime/clip-installer.d.ts.map +1 -0
  12. package/dist/clip-runtime/clip-installer.js +501 -0
  13. package/dist/clip-runtime/clip-provider.d.ts +76 -0
  14. package/dist/clip-runtime/clip-provider.d.ts.map +1 -0
  15. package/dist/clip-runtime/clip-provider.js +402 -0
  16. package/dist/clip-runtime/index.d.ts +7 -0
  17. package/dist/clip-runtime/index.d.ts.map +1 -0
  18. package/dist/clip-runtime/index.js +5 -0
  19. package/dist/clip-runtime/ipc.d.ts +94 -0
  20. package/dist/clip-runtime/ipc.d.ts.map +1 -0
  21. package/dist/clip-runtime/ipc.js +98 -0
  22. package/dist/clip-runtime/manifest.d.ts +74 -0
  23. package/dist/clip-runtime/manifest.d.ts.map +1 -0
  24. package/dist/clip-runtime/manifest.js +181 -0
  25. package/dist/clip-runtime/process-manager.d.ts +57 -0
  26. package/dist/clip-runtime/process-manager.d.ts.map +1 -0
  27. package/dist/clip-runtime/process-manager.js +354 -0
  28. package/dist/clip-runtime/process.d.ts +59 -0
  29. package/dist/clip-runtime/process.d.ts.map +1 -0
  30. package/dist/clip-runtime/process.js +350 -0
  31. package/dist/config.d.ts +13 -0
  32. package/dist/config.d.ts.map +1 -1
  33. package/dist/config.js +36 -19
  34. package/dist/filesystem.d.ts +1 -1
  35. package/dist/filesystem.d.ts.map +1 -1
  36. package/dist/filesystem.js +51 -53
  37. package/dist/index.js +46 -14
  38. package/dist/runtimes.d.ts +10 -8
  39. package/dist/runtimes.d.ts.map +1 -1
  40. package/dist/runtimes.js +63 -95
  41. package/dist/supervisor.d.ts +12 -3
  42. package/dist/supervisor.d.ts.map +1 -1
  43. package/dist/supervisor.js +272 -71
  44. package/dist/updater-manifest.d.ts +41 -0
  45. package/dist/updater-manifest.d.ts.map +1 -0
  46. package/dist/updater-manifest.js +94 -0
  47. package/dist/updater.d.ts +60 -0
  48. package/dist/updater.d.ts.map +1 -0
  49. package/dist/updater.js +427 -0
  50. package/dist/workspace.d.ts +2 -2
  51. package/dist/workspace.d.ts.map +1 -1
  52. package/dist/workspace.js +112 -112
  53. package/package.json +6 -6
@@ -23,7 +23,7 @@ function env(name) {
23
23
  var PRLL_API_URL = env("PRLL_API_URL");
24
24
  var PRLL_API_KEY = env("PRLL_API_KEY");
25
25
  var PRLL_ORG_ID = env("PRLL_ORG_ID");
26
- var stateDir = env("PRLL_OPENCLAW_STATE_DIR");
26
+ var stateDir = env("PRLL_STATE_DIR");
27
27
  var PRLL_WS_URL = process.env.PRLL_WS_URL?.trim() || "";
28
28
  var PRLL_SWIMLANE_NAME = process.env.PRLL_SWIMLANE_NAME?.trim() || "";
29
29
  var gatewayPort = process.env.OPENCLAW_GATEWAY_PORT?.trim() || "0";
@@ -37,13 +37,7 @@ if (fs.existsSync(pluginArchive)) {
37
37
  fs.rmSync(legacyExtDir, { recursive: true, force: true });
38
38
  log.info(`Installing Parall plugin from ${pluginArchive}...`);
39
39
  try {
40
- execFileSync("openclaw", [
41
- "plugins",
42
- "install",
43
- pluginArchive,
44
- "--force",
45
- "--dangerously-force-unsafe-install"
46
- ], {
40
+ execFileSync("openclaw", ["plugins", "install", pluginArchive, "--force", "--dangerously-force-unsafe-install"], {
47
41
  env: { ...process.env, OPENCLAW_STATE_DIR: openclawStateDir },
48
42
  stdio: "inherit",
49
43
  timeout: 6e4
@@ -136,39 +130,70 @@ async function preseedPlatformConfig() {
136
130
  }
137
131
  const ALLOWED_DEFAULTS = /* @__PURE__ */ new Set(["model", "compaction", "memorySearch"]);
138
132
  const platformDefaults = pc.agents?.defaults;
133
+ const agents = cfg.agents && typeof cfg.agents === "object" ? cfg.agents : {};
134
+ const existing = agents.defaults && typeof agents.defaults === "object" ? agents.defaults : {};
135
+ const hadModel = "model" in existing;
136
+ delete existing.model;
139
137
  if (platformDefaults && typeof platformDefaults === "object") {
140
- const agents = cfg.agents && typeof cfg.agents === "object" ? cfg.agents : {};
141
- const existing = agents.defaults && typeof agents.defaults === "object" ? agents.defaults : {};
142
138
  for (const [k, v] of Object.entries(platformDefaults)) {
143
139
  if (ALLOWED_DEFAULTS.has(k))
144
140
  existing[k] = v;
145
141
  }
142
+ }
143
+ if (hadModel || platformDefaults && typeof platformDefaults === "object") {
146
144
  agents.defaults = existing;
147
145
  cfg.agents = agents;
148
146
  }
149
147
  const ALLOWED_MODEL_KEYS = /* @__PURE__ */ new Set(["id", "name", "contextWindow", "maxTokens"]);
150
148
  const platformModels = pc.models?.providers;
151
- const platformParall = platformModels?.parall;
152
- if (platformParall && typeof platformParall === "object") {
149
+ if (platformModels && typeof platformModels === "object") {
153
150
  const models = cfg.models && typeof cfg.models === "object" ? cfg.models : {};
154
151
  const providers = models.providers && typeof models.providers === "object" ? models.providers : {};
155
- const existingParall = providers.parall && typeof providers.parall === "object" ? providers.parall : {};
156
- const merged = { ...existingParall, ...platformParall };
157
- if (Array.isArray(merged.models)) {
158
- merged.models = merged.models.filter((m) => m && typeof m === "object").map((m) => {
159
- const clean = {};
160
- for (const [k, v] of Object.entries(m)) {
161
- if (ALLOWED_MODEL_KEYS.has(k))
162
- clean[k] = v;
163
- }
164
- return clean;
165
- });
152
+ for (const providerName of ["parall", "parall-anthropic"]) {
153
+ const platformProvider = platformModels[providerName];
154
+ if (!platformProvider || typeof platformProvider !== "object") {
155
+ if (providerName !== "parall")
156
+ delete providers[providerName];
157
+ continue;
158
+ }
159
+ const existing2 = providers[providerName] && typeof providers[providerName] === "object" ? providers[providerName] : {};
160
+ const merged = { ...existing2, ...platformProvider };
161
+ if (Array.isArray(merged.models)) {
162
+ merged.models = merged.models.filter((m) => m && typeof m === "object").map((m) => {
163
+ const clean = {};
164
+ for (const [k, v] of Object.entries(m)) {
165
+ if (ALLOWED_MODEL_KEYS.has(k))
166
+ clean[k] = v;
167
+ }
168
+ return clean;
169
+ });
170
+ }
171
+ merged.apiKey = PRLL_API_KEY;
172
+ providers[providerName] = merged;
166
173
  }
167
- merged.apiKey = PRLL_API_KEY;
168
- providers.parall = merged;
169
174
  models.providers = providers;
170
175
  cfg.models = models;
171
176
  }
177
+ const agentsCfg = cfg.agents && typeof cfg.agents === "object" ? cfg.agents : {};
178
+ const defaultsCfg = agentsCfg.defaults && typeof agentsCfg.defaults === "object" ? agentsCfg.defaults : {};
179
+ if (typeof defaultsCfg.model === "string") {
180
+ const modelStr = defaultsCfg.model;
181
+ if (platformModels?.["parall-anthropic"]) {
182
+ const fwd = modelStr.match(/^parall\/(anthropic\/.+)$/);
183
+ if (fwd) {
184
+ defaultsCfg.model = `parall-anthropic/${fwd[1]}`;
185
+ agentsCfg.defaults = defaultsCfg;
186
+ cfg.agents = agentsCfg;
187
+ }
188
+ } else {
189
+ const rev = modelStr.match(/^parall-anthropic\/(anthropic\/.+)$/);
190
+ if (rev) {
191
+ defaultsCfg.model = `parall/${rev[1]}`;
192
+ agentsCfg.defaults = defaultsCfg;
193
+ cfg.agents = agentsCfg;
194
+ }
195
+ }
196
+ }
172
197
  const tmp = configPath + ".tmp";
173
198
  fs.writeFileSync(tmp, JSON.stringify(cfg, null, 2));
174
199
  fs.renameSync(tmp, configPath);
@@ -187,7 +212,7 @@ try {
187
212
  } catch {
188
213
  }
189
214
  log.info("Starting OpenClaw gateway...");
190
- var workspaceDir = process.env.PRLL_OPENCLAW_WORKSPACE_DIR?.trim() || "";
215
+ var workspaceDir = process.env.PRLL_WORKSPACE_DIR?.trim() || "";
191
216
  var gatewayEnv = {
192
217
  ...process.env,
193
218
  OPENCLAW_STATE_DIR: openclawStateDir
package/dist/cli.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export declare function runCLI(args: string[]): Promise<"run-daemon" | "handled">;
1
+ export declare function runCLI(args: string[]): Promise<'run-daemon' | 'handled'>;
2
2
  //# sourceMappingURL=cli.d.ts.map
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAiPA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAoD9E"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AA0SA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC,CAuD9E"}
package/dist/cli.js CHANGED
@@ -1,14 +1,14 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import * as os from "node:os";
4
- import * as readline from "node:readline";
5
- import { spawn, execSync } from "node:child_process";
6
- import { daemonConfigDir, daemonConfigPath } from "./config.js";
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import * as os from 'node:os';
4
+ import * as readline from 'node:readline';
5
+ import { spawn, execSync } from 'node:child_process';
6
+ import { daemonConfigDir, daemonConfigPath } from './config.js';
7
7
  const CONFIG_DIR = daemonConfigDir();
8
8
  const CONFIG_PATH = daemonConfigPath();
9
9
  function readConfig() {
10
10
  try {
11
- return JSON.parse(fs.readFileSync(CONFIG_PATH, "utf-8"));
11
+ return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
12
12
  }
13
13
  catch {
14
14
  return null;
@@ -29,28 +29,30 @@ function prompt(question) {
29
29
  });
30
30
  }
31
31
  function isMacOS() {
32
- return process.platform === "darwin";
32
+ return process.platform === 'darwin';
33
33
  }
34
34
  function isLinux() {
35
- return process.platform === "linux";
35
+ return process.platform === 'linux';
36
36
  }
37
- const PLIST_LABEL = "com.parall.daemon";
37
+ const PLIST_LABEL = 'com.parall.daemon';
38
38
  function plistPath() {
39
- return path.join(os.homedir(), "Library", "LaunchAgents", `${PLIST_LABEL}.plist`);
39
+ return path.join(os.homedir(), 'Library', 'LaunchAgents', `${PLIST_LABEL}.plist`);
40
40
  }
41
41
  function systemdUnitPath() {
42
- return path.join(os.homedir(), ".config", "systemd", "user", "parall-daemon.service");
42
+ return path.join(os.homedir(), '.config', 'systemd', 'user', 'parall-daemon.service');
43
43
  }
44
44
  function getDaemonBin() {
45
45
  try {
46
- return execSync("which parall-daemon", { encoding: "utf-8", stdio: "pipe" }).trim();
46
+ return execSync('which parall-daemon', { encoding: 'utf-8', stdio: 'pipe' }).trim();
47
47
  }
48
48
  catch {
49
- return process.argv[1] ?? "parall-daemon";
49
+ return process.argv[1] ?? 'parall-daemon';
50
50
  }
51
51
  }
52
52
  function generatePlist(daemonBin) {
53
- const logPath = path.join(os.homedir(), "Library", "Logs", "parall-daemon.log");
53
+ const logPath = path.join(os.homedir(), 'Library', 'Logs', 'parall-daemon.log');
54
+ // Use a shell wrapper so the service manager loads the self-updated overlay
55
+ // bundle when it exists, falling back to the original npm-installed binary.
54
56
  return `<?xml version="1.0" encoding="UTF-8"?>
55
57
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
56
58
  <plist version="1.0">
@@ -59,7 +61,9 @@ function generatePlist(daemonBin) {
59
61
  <string>${PLIST_LABEL}</string>
60
62
  <key>ProgramArguments</key>
61
63
  <array>
62
- <string>${daemonBin}</string>
64
+ <string>/bin/sh</string>
65
+ <string>-c</string>
66
+ <string>OVERLAY="$HOME/.parall-daemon/bundle/current/parall-daemon.js"; if [ -f "$OVERLAY" ]; then exec node "$OVERLAY"; else exec ${daemonBin}; fi</string>
63
67
  </array>
64
68
  <key>RunAtLoad</key>
65
69
  <true/>
@@ -75,6 +79,8 @@ function generatePlist(daemonBin) {
75
79
  </plist>`;
76
80
  }
77
81
  function generateSystemdUnit(daemonBin) {
82
+ // Use a shell wrapper so the service manager loads the self-updated overlay
83
+ // bundle when it exists, falling back to the original npm-installed binary.
78
84
  return `[Unit]
79
85
  Description=Parall Daemon
80
86
  After=network-online.target
@@ -82,7 +88,7 @@ Wants=network-online.target
82
88
 
83
89
  [Service]
84
90
  Type=simple
85
- ExecStart=${daemonBin}
91
+ ExecStart=/bin/sh -c 'OVERLAY="$HOME/.parall-daemon/bundle/current/parall-daemon.js"; if [ -f "$OVERLAY" ]; then exec node "$OVERLAY"; else exec ${daemonBin}; fi'
86
92
  Restart=always
87
93
  RestartSec=5
88
94
 
@@ -92,7 +98,7 @@ WantedBy=default.target`;
92
98
  function installService() {
93
99
  const config = readConfig();
94
100
  if (!config) {
95
- console.error("No config found. Run `parall-daemon init` first.");
101
+ console.error('No config found. Run `parall-daemon init` first.');
96
102
  process.exit(1);
97
103
  }
98
104
  const bin = getDaemonBin();
@@ -108,102 +114,142 @@ function installService() {
108
114
  const dir = path.dirname(systemdUnitPath());
109
115
  fs.mkdirSync(dir, { recursive: true });
110
116
  fs.writeFileSync(systemdUnitPath(), generateSystemdUnit(bin));
111
- execSync("systemctl --user daemon-reload");
112
- execSync("systemctl --user enable --now parall-daemon");
117
+ execSync('systemctl --user daemon-reload');
118
+ execSync('systemctl --user enable --now parall-daemon');
113
119
  console.log(`systemd service installed: ${systemdUnitPath()}`);
114
120
  }
115
121
  else {
116
- console.log("Unsupported platform. Run `parall-daemon` manually.");
122
+ console.log('Unsupported platform. Run `parall-daemon` manually.');
117
123
  }
118
124
  }
119
125
  async function cmdInit() {
120
126
  const existing = readConfig();
121
- const defaultUrl = existing?.api_url || "https://api.parall.com";
127
+ const defaultUrl = existing?.api_url || 'https://api.parall.com';
122
128
  const apiUrl = (await prompt(`API URL [${defaultUrl}]: `)) || defaultUrl;
123
- const apiKey = await prompt("Machine key (mck_...): ");
124
- if (!apiKey.startsWith("mck_")) {
129
+ const apiKey = await prompt('Machine key (mck_...): ');
130
+ if (!apiKey.startsWith('mck_')) {
125
131
  console.error('Error: Machine key must start with "mck_"');
126
132
  process.exit(1);
127
133
  }
128
134
  writeConfig({ api_url: apiUrl, api_key: apiKey });
129
135
  console.log(`Config written to ${CONFIG_PATH}`);
130
- const install = await prompt("Install as background service? (Y/n): ");
131
- if (install.toLowerCase() !== "n") {
136
+ const install = await prompt('Install as background service? (Y/n): ');
137
+ if (install.toLowerCase() !== 'n') {
132
138
  installService();
133
139
  }
134
140
  }
135
141
  function cmdStatus() {
136
142
  const config = readConfig();
137
- console.log(`Config: ${config ? CONFIG_PATH : "not configured"}`);
143
+ console.log(`Config: ${config ? CONFIG_PATH : 'not configured'}`);
138
144
  if (isMacOS()) {
139
145
  try {
140
146
  const output = execSync(`launchctl print gui/$(id -u)/${PLIST_LABEL} 2>&1`, {
141
- encoding: "utf-8",
147
+ encoding: 'utf-8',
142
148
  });
143
- const running = output.includes("state = running");
144
- console.log(`Service: ${running ? "running" : "stopped"}`);
149
+ const running = output.includes('state = running');
150
+ console.log(`Service: ${running ? 'running' : 'stopped'}`);
145
151
  const pidMatch = output.match(/pid\s*=\s*(\d+)/);
146
152
  if (pidMatch)
147
153
  console.log(`PID: ${pidMatch[1]}`);
148
154
  }
149
155
  catch {
150
- console.log("Service: not installed");
156
+ console.log('Service: not installed');
151
157
  }
152
158
  }
153
159
  else if (isLinux()) {
154
160
  try {
155
- execSync("systemctl --user is-active parall-daemon", { encoding: "utf-8", stdio: "pipe" });
156
- console.log("Service: running");
161
+ execSync('systemctl --user is-active parall-daemon', { encoding: 'utf-8', stdio: 'pipe' });
162
+ console.log('Service: running');
157
163
  }
158
164
  catch {
159
- console.log("Service: stopped or not installed");
165
+ console.log('Service: stopped or not installed');
160
166
  }
161
167
  }
162
168
  }
163
169
  function cmdStop() {
164
170
  if (isMacOS()) {
165
171
  execSync(`launchctl bootout gui/$(id -u) ${plistPath()} 2>/dev/null || true`, {
166
- stdio: "inherit",
172
+ stdio: 'inherit',
167
173
  });
168
174
  }
169
175
  else if (isLinux()) {
170
- execSync("systemctl --user stop parall-daemon", { stdio: "inherit" });
176
+ execSync('systemctl --user stop parall-daemon', { stdio: 'inherit' });
171
177
  }
172
- console.log("Daemon stopped.");
178
+ console.log('Daemon stopped.');
173
179
  }
174
180
  function cmdLogs(lines) {
175
181
  if (isLinux()) {
176
- const child = spawn("journalctl", ["--user-unit", "parall-daemon", "-n", lines, "-f"], {
177
- stdio: "inherit",
182
+ const child = spawn('journalctl', ['--user-unit', 'parall-daemon', '-n', lines, '-f'], {
183
+ stdio: 'inherit',
178
184
  });
179
- child.on("exit", (code) => process.exit(code ?? 0));
185
+ child.on('exit', (code) => process.exit(code ?? 0));
180
186
  return;
181
187
  }
182
- const logPath = path.join(os.homedir(), "Library", "Logs", "parall-daemon.log");
188
+ const logPath = path.join(os.homedir(), 'Library', 'Logs', 'parall-daemon.log');
183
189
  if (!fs.existsSync(logPath)) {
184
- console.log("No log file found at", logPath);
190
+ console.log('No log file found at', logPath);
185
191
  return;
186
192
  }
187
- const child = spawn("tail", ["-n", lines, "-f", logPath], { stdio: "inherit" });
188
- child.on("exit", (code) => process.exit(code ?? 0));
193
+ const child = spawn('tail', ['-n', lines, '-f', logPath], { stdio: 'inherit' });
194
+ child.on('exit', (code) => process.exit(code ?? 0));
189
195
  }
190
196
  function cmdServiceUninstall() {
191
197
  if (isMacOS()) {
192
198
  execSync(`launchctl bootout gui/$(id -u) ${plistPath()} 2>/dev/null || true`);
193
199
  if (fs.existsSync(plistPath()))
194
200
  fs.unlinkSync(plistPath());
195
- console.log("launchd agent uninstalled.");
201
+ console.log('launchd agent uninstalled.');
196
202
  }
197
203
  else if (isLinux()) {
198
- execSync("systemctl --user stop parall-daemon 2>/dev/null || true");
199
- execSync("systemctl --user disable parall-daemon 2>/dev/null || true");
204
+ execSync('systemctl --user stop parall-daemon 2>/dev/null || true');
205
+ execSync('systemctl --user disable parall-daemon 2>/dev/null || true');
200
206
  if (fs.existsSync(systemdUnitPath()))
201
207
  fs.unlinkSync(systemdUnitPath());
202
- execSync("systemctl --user daemon-reload");
203
- console.log("systemd service uninstalled.");
208
+ execSync('systemctl --user daemon-reload');
209
+ console.log('systemd service uninstalled.');
204
210
  }
205
211
  else {
206
- console.log("Unsupported platform.");
212
+ console.log('Unsupported platform.');
213
+ }
214
+ }
215
+ async function cmdUpdate(checkOnly) {
216
+ const { resolveClaudeDaemonConfig, resolveBundleDir } = await import('./config.js');
217
+ const { DaemonUpdater } = await import('./updater.js');
218
+ const config = resolveClaudeDaemonConfig(process.env);
219
+ const bundleDir = resolveBundleDir(process.env);
220
+ const signingEnabled = process.env.PRLL_DAEMON_SIGNING_DISABLED !== '1';
221
+ const updater = new DaemonUpdater(bundleDir, config.updateCdnUrl, {
222
+ info: (msg) => console.log(msg),
223
+ warn: (msg) => console.warn(msg),
224
+ error: (msg) => console.error(msg),
225
+ }, signingEnabled);
226
+ const local = updater.getLocalVersion();
227
+ console.log(`Current version: ${local ?? 'unknown'}`);
228
+ console.log(`CDN: ${config.updateCdnUrl}`);
229
+ console.log(`Bundle dir: ${bundleDir}`);
230
+ if (checkOnly) {
231
+ console.log('\nChecking for updates...');
232
+ const result = await updater.checkAvailable();
233
+ if (result.available) {
234
+ console.log(`Update available: ${result.currentVersion ?? 'unknown'} → ${result.remoteVersion}`);
235
+ }
236
+ else if (result.remoteVersion) {
237
+ console.log(`Already up to date (${result.currentVersion}).`);
238
+ }
239
+ else {
240
+ console.log('Could not check for updates.');
241
+ }
242
+ return;
243
+ }
244
+ else {
245
+ console.log('\nChecking and applying updates...');
246
+ }
247
+ const applied = await updater.checkAndApply();
248
+ if (applied) {
249
+ console.log('Update applied. Restart the daemon to use the new version.');
250
+ }
251
+ else {
252
+ console.log('Already up to date.');
207
253
  }
208
254
  }
209
255
  function printUsage() {
@@ -215,6 +261,7 @@ Usage:
215
261
  parall-daemon init Configure the daemon (interactive)
216
262
  parall-daemon status Show daemon service status
217
263
  parall-daemon stop Stop the background service
264
+ parall-daemon update [--check] Check for / apply daemon updates
218
265
  parall-daemon logs [-n LINES] Tail daemon logs
219
266
  parall-daemon service install Install as background service (launchd/systemd)
220
267
  parall-daemon service uninstall Uninstall background service
@@ -224,54 +271,57 @@ Usage:
224
271
  export async function runCLI(args) {
225
272
  const cmd = args[0];
226
273
  switch (cmd) {
227
- case "init":
274
+ case 'init':
228
275
  await cmdInit();
229
- return "handled";
230
- case "status":
276
+ return 'handled';
277
+ case 'status':
231
278
  cmdStatus();
232
- return "handled";
233
- case "stop":
279
+ return 'handled';
280
+ case 'stop':
234
281
  cmdStop();
235
- return "handled";
236
- case "logs": {
237
- let lines = "50";
238
- if (args[1] === "-n") {
282
+ return 'handled';
283
+ case 'logs': {
284
+ let lines = '50';
285
+ if (args[1] === '-n') {
239
286
  const n = Number(args[2]);
240
287
  if (!Number.isInteger(n) || n <= 0) {
241
- console.error("Error: -n requires a positive integer");
288
+ console.error('Error: -n requires a positive integer');
242
289
  process.exit(1);
243
290
  }
244
291
  lines = String(n);
245
292
  }
246
293
  cmdLogs(lines);
247
- return "handled";
294
+ return 'handled';
248
295
  }
249
- case "service": {
296
+ case 'update':
297
+ await cmdUpdate(args.includes('--check'));
298
+ return 'handled';
299
+ case 'service': {
250
300
  const sub = args[1];
251
- if (sub === "install") {
301
+ if (sub === 'install') {
252
302
  installService();
253
303
  }
254
- else if (sub === "uninstall") {
304
+ else if (sub === 'uninstall') {
255
305
  cmdServiceUninstall();
256
306
  }
257
307
  else {
258
- console.error(`Unknown service command: ${sub ?? "(none)"}`);
259
- console.log("Usage: parall-daemon service [install|uninstall]");
308
+ console.error(`Unknown service command: ${sub ?? '(none)'}`);
309
+ console.log('Usage: parall-daemon service [install|uninstall]');
260
310
  process.exit(1);
261
311
  }
262
- return "handled";
312
+ return 'handled';
263
313
  }
264
- case "help":
265
- case "--help":
266
- case "-h":
314
+ case 'help':
315
+ case '--help':
316
+ case '-h':
267
317
  printUsage();
268
- return "handled";
318
+ return 'handled';
269
319
  default:
270
320
  if (cmd) {
271
321
  console.error(`Unknown command: ${cmd}`);
272
322
  printUsage();
273
323
  process.exit(1);
274
324
  }
275
- return "run-daemon";
325
+ return 'run-daemon';
276
326
  }
277
327
  }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * ClipInstaller — Downloads and installs clips from the Pinix registry.
3
+ *
4
+ * Flow: parse source → resolve version → download tarball → verify checksum →
5
+ * extract to clipsDir/{alias}/ → install deps → return result.
6
+ *
7
+ * Uses only Node.js built-ins (no external npm packages).
8
+ */
9
+ export interface InstallOptions {
10
+ /** e.g., "@pinix/github-tools" or "@pinix/github-tools@1.0.0" */
11
+ source: string;
12
+ /** Override alias (default: derived from package name) */
13
+ alias?: string;
14
+ /** Directory to install clips into */
15
+ clipsDir: string;
16
+ /** Pinix registry REST base (package metadata + tarballs). Default: https://api.pinixai.com (env: PRLL_PINIX_REGISTRY_URL) */
17
+ registryUrl?: string;
18
+ }
19
+ export interface InstallResult {
20
+ alias: string;
21
+ name: string;
22
+ version: string;
23
+ /** Filesystem path of the installed clip */
24
+ path: string;
25
+ }
26
+ interface ParsedSource {
27
+ scope: string | null;
28
+ name: string;
29
+ /** Full package name including scope, e.g. "@pinix/github-tools" */
30
+ fullName: string;
31
+ version: string | null;
32
+ }
33
+ /**
34
+ * Parse a source string into scope, name, and optional version.
35
+ * "@scope/name" → scope="scope", name="name", version=null
36
+ * "@scope/name@1.0.0" → scope="scope", name="name", version="1.0.0"
37
+ * "name" → scope=null, name="name", version=null
38
+ * "name@1.0.0" → scope=null, name="name", version="1.0.0"
39
+ */
40
+ export declare function parseSource(source: string): ParsedSource;
41
+ export declare function installClip(opts: InstallOptions): Promise<InstallResult>;
42
+ export declare function removeClip(clipsDir: string, alias: string): Promise<void>;
43
+ export {};
44
+ //# sourceMappingURL=clip-installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clip-installer.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-installer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IACjB,8HAA8H;IAC9H,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,IAAI,EAAE,MAAM,CAAC;CACd;AAMD,UAAU,YAAY;IACpB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,oEAAoE;IACpE,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB;AAcD;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAgCxD;AAicD,wBAAsB,WAAW,CAAC,IAAI,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC,CAiE9E;AAED,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/E"}