@parall/daemon 1.32.0 → 1.32.1

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.
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Bun binary resolution — shared by the clip process manager (which spawns
3
+ * `bun run <entry> --ipc`) and the clip installer (which runs `bun install`).
4
+ *
5
+ * Resolution order, embedded-first:
6
+ * 1. Embedded bun shipped alongside the daemon's node binary. In a packaged
7
+ * desktop app the daemon is launched via `Frameworks/parall-node`, so
8
+ * `dirname(process.execPath)` is the `Frameworks/` dir and the embedded
9
+ * bun sits right beside it. electron-builder stages it under the CANONICAL
10
+ * name `Frameworks/bun` (node stays `parall-node` since the daemon only
11
+ * invokes node by absolute path, whereas bun must also be discoverable by
12
+ * name on the child PATH for clip postinstall hooks / clips that shell out
13
+ * to `bun`). This is the reliable path for users who never installed Bun.
14
+ * 2. User-level installs (`~/.bun/bin`, Homebrew) — for dev / CLI daemons.
15
+ * 3. PATH lookup (`which` / `where.exe`).
16
+ *
17
+ * A non-packaged or npm-CLI daemon runs from the system node, so the sibling
18
+ * `bun` does not exist and resolution falls through to the install candidates.
19
+ * The launchd-managed daemon inherits a minimal PATH
20
+ * (`/usr/bin:/bin:/usr/sbin:/sbin`) that contains neither `bun` nor `npm`,
21
+ * which is exactly why resolving an absolute path (not relying on PATH) matters.
22
+ */
23
+ export declare function findBunBinary(): string;
24
+ //# sourceMappingURL=bun-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bun-resolver.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/bun-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAMH,wBAAgB,aAAa,IAAI,MAAM,CAgCtC"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Bun binary resolution — shared by the clip process manager (which spawns
3
+ * `bun run <entry> --ipc`) and the clip installer (which runs `bun install`).
4
+ *
5
+ * Resolution order, embedded-first:
6
+ * 1. Embedded bun shipped alongside the daemon's node binary. In a packaged
7
+ * desktop app the daemon is launched via `Frameworks/parall-node`, so
8
+ * `dirname(process.execPath)` is the `Frameworks/` dir and the embedded
9
+ * bun sits right beside it. electron-builder stages it under the CANONICAL
10
+ * name `Frameworks/bun` (node stays `parall-node` since the daemon only
11
+ * invokes node by absolute path, whereas bun must also be discoverable by
12
+ * name on the child PATH for clip postinstall hooks / clips that shell out
13
+ * to `bun`). This is the reliable path for users who never installed Bun.
14
+ * 2. User-level installs (`~/.bun/bin`, Homebrew) — for dev / CLI daemons.
15
+ * 3. PATH lookup (`which` / `where.exe`).
16
+ *
17
+ * A non-packaged or npm-CLI daemon runs from the system node, so the sibling
18
+ * `bun` does not exist and resolution falls through to the install candidates.
19
+ * The launchd-managed daemon inherits a minimal PATH
20
+ * (`/usr/bin:/bin:/usr/sbin:/sbin`) that contains neither `bun` nor `npm`,
21
+ * which is exactly why resolving an absolute path (not relying on PATH) matters.
22
+ */
23
+ import * as fs from 'node:fs';
24
+ import * as path from 'node:path';
25
+ import { execFileSync } from 'node:child_process';
26
+ export function findBunBinary() {
27
+ const isWindows = process.platform === 'win32';
28
+ const binName = isWindows ? 'bun.exe' : 'bun';
29
+ // 1. Embedded bun — sibling of the daemon's node binary in a packaged app
30
+ // (staged as parall-bun-${arch}, landed as Frameworks/bun).
31
+ const embedded = path.join(path.dirname(process.execPath), binName);
32
+ if (fs.existsSync(embedded))
33
+ return embedded;
34
+ if (!isWindows) {
35
+ const candidates = [
36
+ path.join(process.env.HOME || '', '.bun', 'bin', 'bun'),
37
+ '/usr/local/bin/bun',
38
+ '/opt/homebrew/bin/bun',
39
+ ];
40
+ for (const candidate of candidates) {
41
+ if (fs.existsSync(candidate))
42
+ return candidate;
43
+ }
44
+ }
45
+ // PATH lookup — platform-aware
46
+ const lookupCmd = isWindows ? 'where.exe' : 'which';
47
+ const lookupArg = isWindows ? 'bun.exe' : 'bun';
48
+ try {
49
+ const result = execFileSync(lookupCmd, [lookupArg], { encoding: 'utf-8' }).trim();
50
+ const firstLine = result.split('\n')[0]?.trim();
51
+ if (firstLine)
52
+ return firstLine;
53
+ }
54
+ catch {
55
+ // not found in PATH
56
+ }
57
+ throw new Error('bun binary not found — install Bun (https://bun.sh) or set bunPath option');
58
+ }
@@ -1 +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"}
1
+ {"version":3,"file":"clip-installer.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-installer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAeH,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;AAyeD,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"}
@@ -11,6 +11,7 @@ import * as path from 'node:path';
11
11
  import * as crypto from 'node:crypto';
12
12
  import { execFileSync } from 'node:child_process';
13
13
  import { pipeline } from 'node:stream/promises';
14
+ import { findBunBinary } from './bun-resolver.js';
14
15
  const DEFAULT_REGISTRY_URL = 'https://api.pinixai.com';
15
16
  // ---------------------------------------------------------------------------
16
17
  // Source parsing
@@ -406,28 +407,68 @@ function installDeps(clipDir) {
406
407
  catch {
407
408
  /* ignore parse error, try install anyway */
408
409
  }
409
- const runners = [
410
- { cmd: 'bun', args: ['install', '--frozen-lockfile'] },
411
- { cmd: 'bun', args: ['install'] },
412
- { cmd: 'npm', args: ['install', '--production'] },
413
- ];
414
- for (const runner of runners) {
415
- try {
416
- execFileSync(runner.cmd, runner.args, {
417
- cwd: clipDir,
418
- stdio: 'pipe',
419
- timeout: 120_000,
420
- });
421
- return;
422
- }
423
- catch {
424
- // try next runner
410
+ // Reasons each attempt failed — surfaced in the thrown error so a clip that
411
+ // genuinely needs deps reports *why* installation failed, not just "no
412
+ // package manager succeeded".
413
+ const failures = [];
414
+ // Bun is the reliable path. Resolve it the same way the process manager
415
+ // resolves the runtime bun (embedded-first, then ~/.bun / Homebrew / PATH)
416
+ // a bare `bun` would not be found under the daemon's minimal launchd PATH
417
+ // (/usr/bin:/bin:/usr/sbin:/sbin). We invoke bun by absolute path below, and
418
+ // also prepend its directory to the child PATH so a dependency's postinstall
419
+ // hook that shells out to `bun` by name resolves it. The embedded binary is
420
+ // landed under the canonical name `bun` (Frameworks/bun) precisely so this
421
+ // by-name lookup works — a `parall-bun` filename would defeat it.
422
+ let bunPath = null;
423
+ try {
424
+ bunPath = findBunBinary();
425
+ }
426
+ catch (err) {
427
+ failures.push(`resolve bun: ${err instanceof Error ? err.message : String(err)}`);
428
+ }
429
+ if (bunPath) {
430
+ const bunEnv = {
431
+ ...process.env,
432
+ PATH: `${path.dirname(bunPath)}${path.delimiter}${process.env.PATH ?? ''}`,
433
+ };
434
+ // Prefer a reproducible install when a lockfile is present, then fall back
435
+ // to a plain install (no lockfile / lockfile drift).
436
+ for (const args of [['install', '--frozen-lockfile'], ['install']]) {
437
+ try {
438
+ execFileSync(bunPath, args, {
439
+ cwd: clipDir,
440
+ stdio: 'pipe',
441
+ timeout: 120_000,
442
+ env: bunEnv,
443
+ });
444
+ return;
445
+ }
446
+ catch (err) {
447
+ failures.push(`bun ${args.join(' ')}: ${err instanceof Error ? err.message : String(err)}`);
448
+ }
425
449
  }
426
450
  }
451
+ // Best-effort npm fallback. The packaged desktop app embeds bun but NOT npm,
452
+ // so this only succeeds on dev/CLI hosts that already have npm on PATH; it is
453
+ // never the reliable path. Kept so a developer running the bare daemon with
454
+ // npm-but-no-bun still resolves dependencies.
455
+ try {
456
+ execFileSync('npm', ['install', '--production'], {
457
+ cwd: clipDir,
458
+ stdio: 'pipe',
459
+ timeout: 120_000,
460
+ });
461
+ return;
462
+ }
463
+ catch (err) {
464
+ failures.push(`npm install: ${err instanceof Error ? err.message : String(err)}`);
465
+ }
427
466
  if (hasDeps) {
428
- throw new Error(`failed to install dependencies in ${clipDir} — clip declares dependencies but no package manager succeeded`);
467
+ throw new Error(`failed to install dependencies in ${clipDir} — clip declares dependencies but no ` +
468
+ `package manager succeeded:\n ${failures.join('\n ')}`);
429
469
  }
430
- console.warn(`[clip-installer] could not run package manager in ${clipDir} (no dependencies declared, continuing)`);
470
+ console.warn(`[clip-installer] could not run a package manager in ${clipDir} ` +
471
+ `(no dependencies declared, continuing): ${failures.join('; ')}`);
431
472
  }
432
473
  // ---------------------------------------------------------------------------
433
474
  // Public API
@@ -19,7 +19,7 @@ export interface ClipProviderOptions {
19
19
  serviceUrl: string;
20
20
  /** mck_xxx machine key for authorization. */
21
21
  authKey: string;
22
- /** Org to register for. */
22
+ /** Machine org, used for local logging; Hub derives org from auth. */
23
23
  orgId: string;
24
24
  /** Unique provider name (typically machine ID). */
25
25
  providerName: string;
@@ -72,5 +72,7 @@ export declare class ClipProvider {
72
72
  private buildClipRegistrations;
73
73
  private clipConfigToRegistration;
74
74
  private commandDetailToInfo;
75
+ private dependencyNames;
76
+ private stringEntities;
75
77
  }
76
78
  //# sourceMappingURL=clip-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"clip-provider.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA4H/D,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,2BAA2B;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,WAAW,EAAE,kBAAkB,CAAC;IAChC,mBAAmB;IACnB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACrF;AAED,qBAAa,YAAY;IAiBX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAhBjC,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,mBAAmB,CAA6B;IACxD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAU;IACzC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAU;gBAEjB,IAAI,EAAE,mBAAmB;IAEtD,mFAAmF;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,wCAAwC;IAClC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,WAAW,IAAI,OAAO;YAQR,UAAU;IAoExB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,gBAAgB;IAWxB,OAAO,CAAC,gBAAgB;YAeV,mBAAmB;YAsDnB,YAAY;YAWZ,mBAAmB;IAiBjC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,mBAAmB;CAU5B"}
1
+ {"version":3,"file":"clip-provider.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/clip-provider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AA0I/D,MAAM,WAAW,mBAAmB;IAClC,sDAAsD;IACtD,UAAU,EAAE,MAAM,CAAC;IACnB,6CAA6C;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,YAAY,EAAE,MAAM,CAAC;IACrB,yDAAyD;IACzD,WAAW,EAAE,kBAAkB,CAAC;IAChC,mBAAmB;IACnB,GAAG,EAAE;QAAE,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,CAAC;CACrF;AAED,qBAAa,YAAY;IAiBX,OAAO,CAAC,QAAQ,CAAC,IAAI;IAhBjC,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,MAAM,CAAwC;IACtD,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,gBAAgB,CAAK;IAC7B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,mBAAmB,CAA6B;IACxD,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,gBAAgB,CAAS;IAEjC,OAAO,CAAC,MAAM,CAAC,iBAAiB,CAAS;IACzC,OAAO,CAAC,MAAM,CAAC,gBAAgB,CAAU;IACzC,OAAO,CAAC,MAAM,CAAC,qBAAqB,CAAU;gBAEjB,IAAI,EAAE,mBAAmB;IAEtD,mFAAmF;IAC7E,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAS9B,wCAAwC;IAClC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAUjC,WAAW,IAAI,OAAO;YAQR,UAAU;IAoExB,OAAO,CAAC,gBAAgB;IAQxB,OAAO,CAAC,iBAAiB;IAiBzB,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,mBAAmB;IAO3B,OAAO,CAAC,cAAc;IAyBtB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,wBAAwB;IAWhC,OAAO,CAAC,gBAAgB;IAaxB,OAAO,CAAC,gBAAgB;YAeV,mBAAmB;YAyDnB,YAAY;YAUZ,mBAAmB;IAiBjC,OAAO,CAAC,sBAAsB;IAK9B,OAAO,CAAC,wBAAwB;IAuBhC,OAAO,CAAC,mBAAmB;IAS3B,OAAO,CAAC,eAAe;IAUvB,OAAO,CAAC,cAAc;CAUvB"}
@@ -149,7 +149,7 @@ export class ClipProvider {
149
149
  this.stream.on('close', () => {
150
150
  this.handleDisconnect();
151
151
  });
152
- // Send ProviderRegister as the first message
152
+ // Send RegisterRequest as the first message.
153
153
  await this.sendRegister();
154
154
  }
155
155
  catch (err) {
@@ -208,7 +208,7 @@ export class ClipProvider {
208
208
  void this.openStream();
209
209
  return;
210
210
  }
211
- this.sendProviderMessage({ heartbeat: {} }).catch((err) => {
211
+ this.sendProviderMessage({ ping: { sentAtUnixMs: Date.now() } }).catch((err) => {
212
212
  this.opts.log.warn(`[clip-provider] heartbeat send failed: ${String(err)}`);
213
213
  });
214
214
  }, ClipProvider.HEARTBEAT_INTERVAL_MS);
@@ -242,16 +242,16 @@ export class ClipProvider {
242
242
  subscribeStatusChanges() {
243
243
  const mgr = this.opts.clipManager;
244
244
  // Forward clip status changes to the Hub
245
- this.statusUnsubscribe = mgr.addStatusListener((name, status, _message) => {
245
+ this.statusUnsubscribe = mgr.addStatusListener((name, status, message) => {
246
246
  if (!this.connected)
247
247
  return;
248
248
  const protoStatus = status === 'running'
249
- ? 'CLIP_ONLINE_STATUS_ONLINE'
249
+ ? 'CLIP_STATUS_RUNNING'
250
250
  : status === 'error'
251
- ? 'CLIP_ONLINE_STATUS_ERROR'
252
- : 'CLIP_ONLINE_STATUS_OFFLINE';
251
+ ? 'CLIP_STATUS_ERROR'
252
+ : 'CLIP_STATUS_SLEEPING';
253
253
  this.sendProviderMessage({
254
- clipStatusChanged: { alias: name, status: protoStatus },
254
+ clipStatusChanged: { name, status: protoStatus, message },
255
255
  }).catch((err) => {
256
256
  this.opts.log.warn(`[clip-provider] status change send failed: ${String(err)}`);
257
257
  });
@@ -271,20 +271,22 @@ export class ClipProvider {
271
271
  // Message handling
272
272
  // ---------------------------------------------------------------------------
273
273
  handleHubMessage(msg) {
274
- if (msg.registered) {
275
- this.handleRegistered(msg.registered);
274
+ if (msg.registerResponse) {
275
+ this.handleRegistered(msg.registerResponse);
276
276
  }
277
277
  else if (msg.invokeCommand) {
278
278
  void this.handleInvokeCommand(msg.invokeCommand);
279
279
  }
280
- else if (msg.heartbeat !== undefined) {
281
- // Respond to hub heartbeat with provider heartbeat
282
- this.sendProviderMessage({ heartbeat: {} }).catch(() => { });
280
+ else if (msg.pong !== undefined) {
281
+ // Pong is the hub's keepalive ack. Do NOT echo a ping here — the
282
+ // heartbeat timer (startHeartbeat) is the sole ping driver. Replying to
283
+ // every pong with a ping turns the 30s interval into an unbounded
284
+ // ping↔pong loop the moment the first timer tick fires.
283
285
  }
284
286
  }
285
287
  handleRegistered(reg) {
286
288
  if (reg.accepted) {
287
- this.opts.log.info(`[clip-provider] registered with hub (org=${reg.orgId})`);
289
+ this.opts.log.info(`[clip-provider] registered with hub (org=${this.opts.orgId})`);
288
290
  this.connected = true;
289
291
  this.reconnectAttempt = 0;
290
292
  this.startHeartbeat();
@@ -298,7 +300,7 @@ export class ClipProvider {
298
300
  }
299
301
  }
300
302
  async handleInvokeCommand(cmd) {
301
- const { requestId, clipAlias, commandName } = cmd;
303
+ const { requestId, clipName, command } = cmd;
302
304
  const timeoutMs = cmd.timeoutMs ?? 30_000;
303
305
  try {
304
306
  let input;
@@ -313,7 +315,7 @@ export class ClipProvider {
313
315
  }
314
316
  let timer;
315
317
  const output = await Promise.race([
316
- this.opts.clipManager.invoke(clipAlias, commandName, input),
318
+ this.opts.clipManager.invoke(clipName, command, input),
317
319
  new Promise((_, reject) => {
318
320
  timer = setTimeout(() => reject(new Error('invoke timeout')), timeoutMs);
319
321
  }),
@@ -336,7 +338,10 @@ export class ClipProvider {
336
338
  await this.sendProviderMessage({
337
339
  invokeResult: {
338
340
  requestId,
339
- error: isCommandError ? err.message : String(err),
341
+ error: {
342
+ code: isCommandError ? 'CLIP_COMMAND_ERROR' : 'INVOKE_FAILED',
343
+ message: isCommandError ? err.message : String(err),
344
+ },
340
345
  errorIsCommand: isCommandError,
341
346
  done: true,
342
347
  },
@@ -352,7 +357,6 @@ export class ClipProvider {
352
357
  register: {
353
358
  providerName: this.opts.providerName,
354
359
  clips,
355
- orgId: this.opts.orgId,
356
360
  },
357
361
  });
358
362
  }
@@ -382,21 +386,45 @@ export class ClipProvider {
382
386
  const commands = manifest
383
387
  ? manifest.commandDetails.map((cmd) => this.commandDetailToInfo(cmd))
384
388
  : [];
389
+ const pkg = manifest?.package ?? clip.package ?? clip.name;
385
390
  return {
386
391
  alias: clip.name,
387
- name: manifest?.package ?? clip.package ?? clip.name,
392
+ package: pkg,
388
393
  version: manifest?.version ?? clip.version ?? '',
394
+ domain: manifest?.domain,
389
395
  commands,
396
+ hasWeb: manifest?.hasWeb ?? false,
397
+ dependencies: this.dependencyNames(manifest?.dependencies),
398
+ tokenProtected: Boolean(clip.token),
399
+ name: pkg,
400
+ patterns: manifest?.patterns,
401
+ entities: this.stringEntities(manifest?.entities),
402
+ description: manifest?.description,
390
403
  };
391
404
  }
392
405
  commandDetailToInfo(detail) {
393
406
  return {
394
407
  name: detail.name,
395
408
  description: detail.description ?? '',
396
- inputSchema: detail.input ? Buffer.from(detail.input, 'utf-8').toString('base64') : undefined,
397
- outputSchema: detail.output
398
- ? Buffer.from(detail.output, 'utf-8').toString('base64')
399
- : undefined,
409
+ input: detail.input,
410
+ output: detail.output,
400
411
  };
401
412
  }
413
+ dependencyNames(deps) {
414
+ if (!deps)
415
+ return undefined;
416
+ const names = Object.entries(deps)
417
+ .map(([slot, dep]) => dep.package ?? slot)
418
+ .filter((name) => name.length > 0);
419
+ return names.length > 0 ? names : undefined;
420
+ }
421
+ stringEntities(entities) {
422
+ if (!entities)
423
+ return undefined;
424
+ const out = {};
425
+ for (const [key, value] of Object.entries(entities)) {
426
+ out[key] = typeof value === 'string' ? value : JSON.stringify(value);
427
+ }
428
+ return out;
429
+ }
402
430
  }
@@ -1 +1 @@
1
- {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/process-manager.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQ/D,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAkC;IACnD,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,eAAe,CAEhB;IACP,OAAO,CAAC,iBAAiB,CAAyC;gBAEtD,IAAI,EAAE,yBAAyB;IAM3C,+EAA+E;IAC/E,IAAI,cAAc,CAAC,EAAE,EACjB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GACpE,SAAS,EAQZ;IAED,yEAAyE;IACzE,iBAAiB,CACf,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACrE,MAAM,IAAI;IAQb,mBAAmB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ/D,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,UAAU;IAOlB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAOhC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAarC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9B;;;;OAIG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBxE,YAAY,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACjC,OAAO,CAAC,OAAO,CAAC;IAgBb,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAOxD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAMxE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKhC,kBAAkB,IAAI,UAAU,EAAE;IAIlC;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;YA0D3B,aAAa;YAwBb,YAAY;IA0C1B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,SAAS;CAUlB"}
1
+ {"version":3,"file":"process-manager.d.ts","sourceRoot":"","sources":["../../src/clip-runtime/process-manager.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,KAAK,iBAAiB,EAEvB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAQ/D,MAAM,WAAW,yBAAyB;IACxC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,SAAS,CAAkC;IACnD,OAAO,CAAC,UAAU,CAA2C;IAC7D,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,YAAY,CAAqB;IACzC,OAAO,CAAC,eAAe,CAEhB;IACP,OAAO,CAAC,iBAAiB,CAAyC;gBAEtD,IAAI,EAAE,yBAAyB;IAM3C,+EAA+E;IAC/E,IAAI,cAAc,CAAC,EAAE,EACjB,CAAC,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC,GACpE,SAAS,EAQZ;IAED,yEAAyE;IACzE,iBAAiB,CACf,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,GACrE,MAAM,IAAI;IAQb,mBAAmB,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,GAAG,MAAM,IAAI;IAQ/D,OAAO,CAAC,oBAAoB;IAU5B,OAAO,CAAC,UAAU;IAOlB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;IAOhC,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB3C,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAItC,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAarC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAkB9B;;;;OAIG;IACG,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAgBxE,YAAY,CAChB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,OAAO,EACf,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,GACjC,OAAO,CAAC,OAAO,CAAC;IAgBb,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAOxD,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG;QAAE,MAAM,EAAE,iBAAiB,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE;IAMxE,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAKhC,kBAAkB,IAAI,UAAU,EAAE;IAIlC;;;OAGG;IACG,kBAAkB,IAAI,OAAO,CAAC,IAAI,CAAC;YA0D3B,aAAa;YAwBb,YAAY;IAgD1B,OAAO,CAAC,iBAAiB;IAMzB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,SAAS;CAUlB"}
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'node:fs';
2
2
  import * as path from 'node:path';
3
- import { execFileSync } from 'node:child_process';
4
3
  import { ClipProcess, ClipCommandError, } from './process.js';
4
+ import { findBunBinary } from './bun-resolver.js';
5
5
  export class ClipProcessManager {
6
6
  bunPath;
7
7
  clipsDir;
@@ -269,8 +269,14 @@ export class ClipProcessManager {
269
269
  async spawnProcess(config) {
270
270
  const proc = new ClipProcess(config);
271
271
  const clipDataDir = this.ensureClipDataDir(config);
272
- proc.spawn(this.resolveBun(), {
272
+ const bunPath = this.resolveBun();
273
+ proc.spawn(bunPath, {
273
274
  PINIX_DATA_DIR: clipDataDir,
275
+ // Expose the embedded bun's directory on the clip's PATH so a clip that
276
+ // shells out to `bun` by name resolves it — the daemon's launchd PATH
277
+ // does not include Frameworks/. Symmetric with installDeps(); relies on
278
+ // the embedded binary being named `bun` (Frameworks/bun).
279
+ PATH: `${path.dirname(bunPath)}${path.delimiter}${process.env.PATH ?? ''}`,
274
280
  });
275
281
  this.setStatus(config.name, 'running', 'starting');
276
282
  try {
@@ -325,30 +331,3 @@ export class ClipProcessManager {
325
331
  }
326
332
  }
327
333
  }
328
- function findBunBinary() {
329
- const isWindows = process.platform === 'win32';
330
- if (!isWindows) {
331
- const candidates = [
332
- path.join(process.env.HOME || '', '.bun', 'bin', 'bun'),
333
- '/usr/local/bin/bun',
334
- '/opt/homebrew/bin/bun',
335
- ];
336
- for (const candidate of candidates) {
337
- if (fs.existsSync(candidate))
338
- return candidate;
339
- }
340
- }
341
- // PATH lookup — platform-aware
342
- const lookupCmd = isWindows ? 'where.exe' : 'which';
343
- const lookupArg = isWindows ? 'bun.exe' : 'bun';
344
- try {
345
- const result = execFileSync(lookupCmd, [lookupArg], { encoding: 'utf-8' }).trim();
346
- const firstLine = result.split('\n')[0]?.trim();
347
- if (firstLine)
348
- return firstLine;
349
- }
350
- catch {
351
- // not found in PATH
352
- }
353
- throw new Error('bun binary not found — install Bun (https://bun.sh) or set bunPath option');
354
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@parall/daemon",
3
- "version": "1.32.0",
3
+ "version": "1.32.1",
4
4
  "description": "Parall local agent runtime — daemon supervisor + bridge runtimes, bundled as standalone JS files",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -29,11 +29,11 @@
29
29
  "dist"
30
30
  ],
31
31
  "dependencies": {
32
- "@parall/agent-core": "1.32.0",
33
- "@parall/claude-agent": "1.32.0",
34
- "@parall/codex-agent": "1.32.0",
35
- "@parall/openclaw-agent": "1.32.0",
36
- "@parall/sdk": "1.32.0"
32
+ "@parall/codex-agent": "1.32.1",
33
+ "@parall/claude-agent": "1.32.1",
34
+ "@parall/sdk": "1.32.1",
35
+ "@parall/openclaw-agent": "1.32.1",
36
+ "@parall/agent-core": "1.32.1"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/node": "^22.0.0",