@paigy/harness 0.2.0 → 0.2.2

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 (3) hide show
  1. package/dist/cli.js +37 -8
  2. package/dist/main.js +37 -8
  3. package/package.json +13 -13
package/dist/cli.js CHANGED
@@ -31967,7 +31967,7 @@ var clip = (s) => (s.length > 120 ? `${s.slice(0, 117)}\u2026` : s) || "(no text
31967
31967
  import { spawn } from "child_process";
31968
31968
  import { existsSync as existsSync3 } from "fs";
31969
31969
  import { homedir as homedir3 } from "os";
31970
- import { resolve, delimiter } from "path";
31970
+ import { dirname, resolve, delimiter } from "path";
31971
31971
 
31972
31972
  // src/harness/acp.ts
31973
31973
  var none = { events: [], writes: [] };
@@ -32194,9 +32194,15 @@ function startSession(opts) {
32194
32194
  }
32195
32195
  const child = (opts.spawnFn ?? spawn)(opts.bin ?? ADAPTER_BIN[opts.harness], [], {
32196
32196
  cwd,
32197
- // The adapter shells out to its vendor CLI (`claude`, `codex`), and a GUI-launched
32198
- // app's PATH won't have it — same reason detection probes these dirs.
32199
- env: { ...process.env, PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter) },
32197
+ // The adapter shells out to its vendor CLI (`claude`, `codex`), and a GUI- or
32198
+ // launchd-launched process's PATH won't have it — probe dirs plus the RUNNING
32199
+ // node's own bin dir (nvm installs the adapters next to node; launchd's bare
32200
+ // PATH knows neither — live catch 2026-08-04: the first slot-wake ENOENT'd).
32201
+ env: {
32202
+ ...process.env,
32203
+ PATH: [process.env.PATH, dirname(process.execPath), ...probeDirs()].filter(Boolean).join(delimiter),
32204
+ ...opts.token ? { PAIGY_TOKEN: opts.token } : {}
32205
+ },
32200
32206
  stdio: ["pipe", "pipe", "pipe"]
32201
32207
  });
32202
32208
  const write = (frames) => {
@@ -32209,8 +32215,12 @@ function startSession(opts) {
32209
32215
  });
32210
32216
  child.on("exit", (code) => {
32211
32217
  opts.onEvent({ kind: "idle", failed: code !== 0, ...code ? { result: `exited ${code}` } : {} });
32218
+ opts.onExit?.();
32219
+ });
32220
+ child.on("error", (e) => {
32221
+ opts.onEvent({ kind: "error", message: e.message });
32222
+ opts.onExit?.();
32212
32223
  });
32213
- child.on("error", (e) => opts.onEvent({ kind: "error", message: e.message }));
32214
32224
  const driver = createAcpDriver({ cwd, mode: opts.mode, ...opts.mcp ? { mcp: opts.mcp } : {} });
32215
32225
  let buffer = "";
32216
32226
  child.stdout?.on("data", (chunk) => {
@@ -32293,7 +32303,9 @@ function runHarness(opts) {
32293
32303
  cwd: opts.cwd,
32294
32304
  mode: opts.mode,
32295
32305
  ...paigyMcp ? { mcp: paigyMcp } : {},
32306
+ ...opts.token ? { token: opts.token } : {},
32296
32307
  ...opts.bin ? { bin: opts.bin } : {},
32308
+ ...opts.onExit ? { onExit: opts.onExit } : {},
32297
32309
  onEvent: (event) => void handle(event).catch((err) => opts.log(`\u26A0 ${err.message}`))
32298
32310
  });
32299
32311
  if (opts.prompt) session.send(opts.prompt);
@@ -32317,7 +32329,7 @@ function runHarness(opts) {
32317
32329
 
32318
32330
  // src/workspaces.ts
32319
32331
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
32320
- import { dirname, join as join3, resolve as resolve2 } from "path";
32332
+ import { dirname as dirname2, join as join3, resolve as resolve2 } from "path";
32321
32333
  import { homedir as homedir4 } from "os";
32322
32334
  function workspacesFile() {
32323
32335
  return join3(homedir4(), ".paigy", "workspaces.json");
@@ -32335,7 +32347,7 @@ function addWorkspace(dir, deps) {
32335
32347
  const all = listWorkspaces(deps);
32336
32348
  if (!all.includes(next)) {
32337
32349
  all.push(next);
32338
- mkdirSync2(dirname(deps.file), { recursive: true });
32350
+ mkdirSync2(dirname2(deps.file), { recursive: true });
32339
32351
  writeFileSync2(deps.file, JSON.stringify(all, null, 2));
32340
32352
  }
32341
32353
  return all;
@@ -32364,6 +32376,10 @@ function startHost(opts) {
32364
32376
  prompt: spec.prompt ?? "",
32365
32377
  exclusive: true,
32366
32378
  token: spec.token,
32379
+ onExit: () => {
32380
+ runs.delete(spec.sessionId);
32381
+ opts.log(`\u2716 ${label} ended`);
32382
+ },
32367
32383
  log
32368
32384
  });
32369
32385
  runs.set(spec.sessionId, { run, label });
@@ -32383,7 +32399,20 @@ function startHost(opts) {
32383
32399
  const work = await checkReplies({ token }).catch(() => null);
32384
32400
  if (!work || work.requests.length === 0 && work.replies.length === 0) continue;
32385
32401
  const log = (line) => opts.log(`[${slot}] ${line}`);
32386
- const run = runHarness({ harness, cwd: workspace, mode: "bypass", prompt: "", exclusive: true, token, log });
32402
+ const run = runHarness({
32403
+ harness,
32404
+ cwd: workspace,
32405
+ mode: "bypass",
32406
+ prompt: "",
32407
+ exclusive: true,
32408
+ token,
32409
+ // A dead run must not squat the slot — evict so the next wake can respawn.
32410
+ onExit: () => {
32411
+ runs.delete(key);
32412
+ opts.log(`\u2716 ${slot} ended`);
32413
+ },
32414
+ log
32415
+ });
32387
32416
  runs.set(key, { run, label: slot });
32388
32417
  opts.log(`\u25B6 woke ${slot} (${harness}) \u2014 work was waiting in ${workspace}`);
32389
32418
  }
package/dist/main.js CHANGED
@@ -4577,7 +4577,7 @@ var require_lib = __commonJS({
4577
4577
  import { app, BrowserWindow, dialog, ipcMain } from "electron";
4578
4578
  import { spawn as spawn2 } from "child_process";
4579
4579
  import { fileURLToPath } from "url";
4580
- import { dirname as dirname2, join as join4 } from "path";
4580
+ import { dirname as dirname3, join as join4 } from "path";
4581
4581
 
4582
4582
  // ../../packages/sdk/dist/index.js
4583
4583
  import { createRequire as __sdkCreateRequire } from "module";
@@ -12380,7 +12380,7 @@ function removeWorkspace(dir, deps) {
12380
12380
  import { spawn } from "child_process";
12381
12381
  import { existsSync as existsSync4 } from "fs";
12382
12382
  import { homedir as homedir4 } from "os";
12383
- import { resolve as resolve2, delimiter } from "path";
12383
+ import { dirname as dirname2, resolve as resolve2, delimiter } from "path";
12384
12384
 
12385
12385
  // src/harness/acp.ts
12386
12386
  var none = { events: [], writes: [] };
@@ -12607,9 +12607,15 @@ function startSession(opts) {
12607
12607
  }
12608
12608
  const child = (opts.spawnFn ?? spawn)(opts.bin ?? ADAPTER_BIN[opts.harness], [], {
12609
12609
  cwd,
12610
- // The adapter shells out to its vendor CLI (`claude`, `codex`), and a GUI-launched
12611
- // app's PATH won't have it — same reason detection probes these dirs.
12612
- env: { ...process.env, PATH: [process.env.PATH, ...probeDirs()].filter(Boolean).join(delimiter) },
12610
+ // The adapter shells out to its vendor CLI (`claude`, `codex`), and a GUI- or
12611
+ // launchd-launched process's PATH won't have it — probe dirs plus the RUNNING
12612
+ // node's own bin dir (nvm installs the adapters next to node; launchd's bare
12613
+ // PATH knows neither — live catch 2026-08-04: the first slot-wake ENOENT'd).
12614
+ env: {
12615
+ ...process.env,
12616
+ PATH: [process.env.PATH, dirname2(process.execPath), ...probeDirs()].filter(Boolean).join(delimiter),
12617
+ ...opts.token ? { PAIGY_TOKEN: opts.token } : {}
12618
+ },
12613
12619
  stdio: ["pipe", "pipe", "pipe"]
12614
12620
  });
12615
12621
  const write = (frames) => {
@@ -12622,8 +12628,12 @@ function startSession(opts) {
12622
12628
  });
12623
12629
  child.on("exit", (code) => {
12624
12630
  opts.onEvent({ kind: "idle", failed: code !== 0, ...code ? { result: `exited ${code}` } : {} });
12631
+ opts.onExit?.();
12632
+ });
12633
+ child.on("error", (e) => {
12634
+ opts.onEvent({ kind: "error", message: e.message });
12635
+ opts.onExit?.();
12625
12636
  });
12626
- child.on("error", (e) => opts.onEvent({ kind: "error", message: e.message }));
12627
12637
  const driver = createAcpDriver({ cwd, mode: opts.mode, ...opts.mcp ? { mcp: opts.mcp } : {} });
12628
12638
  let buffer = "";
12629
12639
  child.stdout?.on("data", (chunk) => {
@@ -13790,7 +13800,9 @@ function runHarness(opts) {
13790
13800
  cwd: opts.cwd,
13791
13801
  mode: opts.mode,
13792
13802
  ...paigyMcp ? { mcp: paigyMcp } : {},
13803
+ ...opts.token ? { token: opts.token } : {},
13793
13804
  ...opts.bin ? { bin: opts.bin } : {},
13805
+ ...opts.onExit ? { onExit: opts.onExit } : {},
13794
13806
  onEvent: (event) => void handle(event).catch((err) => opts.log(`\u26A0 ${err.message}`))
13795
13807
  });
13796
13808
  if (opts.prompt) session.send(opts.prompt);
@@ -13835,6 +13847,10 @@ function startHost(opts) {
13835
13847
  prompt: spec.prompt ?? "",
13836
13848
  exclusive: true,
13837
13849
  token: spec.token,
13850
+ onExit: () => {
13851
+ runs.delete(spec.sessionId);
13852
+ opts.log(`\u2716 ${label} ended`);
13853
+ },
13838
13854
  log: log2
13839
13855
  });
13840
13856
  runs.set(spec.sessionId, { run, label });
@@ -13854,7 +13870,20 @@ function startHost(opts) {
13854
13870
  const work = await checkReplies({ token }).catch(() => null);
13855
13871
  if (!work || work.requests.length === 0 && work.replies.length === 0) continue;
13856
13872
  const log2 = (line) => opts.log(`[${slot}] ${line}`);
13857
- const run = runHarness({ harness, cwd: workspace, mode: "bypass", prompt: "", exclusive: true, token, log: log2 });
13873
+ const run = runHarness({
13874
+ harness,
13875
+ cwd: workspace,
13876
+ mode: "bypass",
13877
+ prompt: "",
13878
+ exclusive: true,
13879
+ token,
13880
+ // A dead run must not squat the slot — evict so the next wake can respawn.
13881
+ onExit: () => {
13882
+ runs.delete(key);
13883
+ opts.log(`\u2716 ${slot} ended`);
13884
+ },
13885
+ log: log2
13886
+ });
13858
13887
  runs.set(key, { run, label: slot });
13859
13888
  opts.log(`\u25B6 woke ${slot} (${harness}) \u2014 work was waiting in ${workspace}`);
13860
13889
  }
@@ -13884,7 +13913,7 @@ function startHost(opts) {
13884
13913
  }
13885
13914
 
13886
13915
  // src/main.ts
13887
- var here = dirname2(fileURLToPath(import.meta.url));
13916
+ var here = dirname3(fileURLToPath(import.meta.url));
13888
13917
  var wsDeps = { file: workspacesFile() };
13889
13918
  var DEVICE_SLOT = "Desktop";
13890
13919
  var deviceToken = () => readToken(DEVICE_SLOT) || readToken();
package/package.json CHANGED
@@ -1,13 +1,20 @@
1
1
  {
2
2
  "name": "@paigy/harness",
3
- "version": "0.2.0",
4
- "description": "Run Claude Code / Codex on this machine, bridged to Paigy launchable from your phone.",
3
+ "version": "0.2.2",
4
+ "description": "Run Claude Code / Codex on this machine, bridged to Paigy \u2014 launchable from your phone.",
5
5
  "license": "MIT",
6
6
  "type": "module",
7
7
  "main": "dist/main.js",
8
8
  "bin": {
9
9
  "paigy-harness": "dist/cli.js"
10
10
  },
11
+ "scripts": {
12
+ "build": "tsup",
13
+ "dev": "pnpm build && electron .",
14
+ "start": "electron .",
15
+ "typecheck": "tsc --noEmit",
16
+ "test": "vitest run"
17
+ },
11
18
  "dependencies": {},
12
19
  "devDependencies": {
13
20
  "@types/node": "^22.0.0",
@@ -16,10 +23,10 @@
16
23
  "tsup": "^8.3.5",
17
24
  "typescript": "^5.7.2",
18
25
  "vitest": "^2.1.8",
26
+ "@paigy/schema": "workspace:*",
27
+ "@paigy/sdk": "workspace:*",
19
28
  "qrcode": "^1.5.4",
20
- "zod": "^3.24.1",
21
- "@paigy/sdk": "0.1.0",
22
- "@paigy/schema": "0.0.0"
29
+ "zod": "^3.24.1"
23
30
  },
24
31
  "files": [
25
32
  "dist/cli.js",
@@ -27,12 +34,5 @@
27
34
  ],
28
35
  "publishConfig": {
29
36
  "access": "public"
30
- },
31
- "scripts": {
32
- "build": "tsup",
33
- "dev": "pnpm build && electron .",
34
- "start": "electron .",
35
- "typecheck": "tsc --noEmit",
36
- "test": "vitest run"
37
37
  }
38
- }
38
+ }