@paigy/harness 0.2.0 → 0.2.4
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/dist/cli.js +40 -10
- package/dist/main.js +40 -10
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -30804,7 +30804,7 @@ function landIntents(intents) {
|
|
|
30804
30804
|
});
|
|
30805
30805
|
}
|
|
30806
30806
|
async function checkReplies(opts = {}) {
|
|
30807
|
-
const token =
|
|
30807
|
+
const token = authToken(opts.token);
|
|
30808
30808
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/pending`, {
|
|
30809
30809
|
headers: { authorization: `Bearer ${token}` }
|
|
30810
30810
|
}));
|
|
@@ -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-
|
|
32198
|
-
//
|
|
32199
|
-
|
|
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(
|
|
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 });
|
|
@@ -32371,11 +32387,12 @@ function startHost(opts) {
|
|
|
32371
32387
|
}
|
|
32372
32388
|
}
|
|
32373
32389
|
const SLOT_HARNESS = { "mcp-agent": "claude", codex: "codex" };
|
|
32390
|
+
const WAKE_PROMPT = "You were woken because Paigy work is waiting for you. Call check_replies now and handle everything it returns, then follow your paigy instructions to stay in the loop.";
|
|
32374
32391
|
async function sweepSlots() {
|
|
32375
32392
|
const workspace = listWorkspaces(opts.wsDeps)[0];
|
|
32376
32393
|
if (!workspace) return;
|
|
32377
32394
|
for (const slot of listSlots()) {
|
|
32378
|
-
const harness = SLOT_HARNESS[slot];
|
|
32395
|
+
const harness = SLOT_HARNESS[slot] ?? (slot === "Desktop" ? void 0 : "claude");
|
|
32379
32396
|
const key = `slot:${slot}`;
|
|
32380
32397
|
if (!harness || runs.has(key)) continue;
|
|
32381
32398
|
const token = readToken(slot);
|
|
@@ -32383,7 +32400,20 @@ function startHost(opts) {
|
|
|
32383
32400
|
const work = await checkReplies({ token }).catch(() => null);
|
|
32384
32401
|
if (!work || work.requests.length === 0 && work.replies.length === 0) continue;
|
|
32385
32402
|
const log = (line) => opts.log(`[${slot}] ${line}`);
|
|
32386
|
-
const run = runHarness({
|
|
32403
|
+
const run = runHarness({
|
|
32404
|
+
harness,
|
|
32405
|
+
cwd: workspace,
|
|
32406
|
+
mode: "bypass",
|
|
32407
|
+
prompt: WAKE_PROMPT,
|
|
32408
|
+
exclusive: true,
|
|
32409
|
+
token,
|
|
32410
|
+
// A dead run must not squat the slot — evict so the next wake can respawn.
|
|
32411
|
+
onExit: () => {
|
|
32412
|
+
runs.delete(key);
|
|
32413
|
+
opts.log(`\u2716 ${slot} ended`);
|
|
32414
|
+
},
|
|
32415
|
+
log
|
|
32416
|
+
});
|
|
32387
32417
|
runs.set(key, { run, label: slot });
|
|
32388
32418
|
opts.log(`\u25B6 woke ${slot} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
32389
32419
|
}
|
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
|
|
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";
|
|
@@ -12226,7 +12226,7 @@ function landIntents(intents) {
|
|
|
12226
12226
|
});
|
|
12227
12227
|
}
|
|
12228
12228
|
async function checkReplies(opts = {}) {
|
|
12229
|
-
const token =
|
|
12229
|
+
const token = authToken(opts.token);
|
|
12230
12230
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/pending`, {
|
|
12231
12231
|
headers: { authorization: `Bearer ${token}` }
|
|
12232
12232
|
}));
|
|
@@ -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-
|
|
12611
|
-
//
|
|
12612
|
-
|
|
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 });
|
|
@@ -13842,11 +13858,12 @@ function startHost(opts) {
|
|
|
13842
13858
|
}
|
|
13843
13859
|
}
|
|
13844
13860
|
const SLOT_HARNESS = { "mcp-agent": "claude", codex: "codex" };
|
|
13861
|
+
const WAKE_PROMPT = "You were woken because Paigy work is waiting for you. Call check_replies now and handle everything it returns, then follow your paigy instructions to stay in the loop.";
|
|
13845
13862
|
async function sweepSlots() {
|
|
13846
13863
|
const workspace = listWorkspaces(opts.wsDeps)[0];
|
|
13847
13864
|
if (!workspace) return;
|
|
13848
13865
|
for (const slot of listSlots()) {
|
|
13849
|
-
const harness = SLOT_HARNESS[slot];
|
|
13866
|
+
const harness = SLOT_HARNESS[slot] ?? (slot === "Desktop" ? void 0 : "claude");
|
|
13850
13867
|
const key = `slot:${slot}`;
|
|
13851
13868
|
if (!harness || runs.has(key)) continue;
|
|
13852
13869
|
const token = readToken(slot);
|
|
@@ -13854,7 +13871,20 @@ function startHost(opts) {
|
|
|
13854
13871
|
const work = await checkReplies({ token }).catch(() => null);
|
|
13855
13872
|
if (!work || work.requests.length === 0 && work.replies.length === 0) continue;
|
|
13856
13873
|
const log2 = (line) => opts.log(`[${slot}] ${line}`);
|
|
13857
|
-
const run = runHarness({
|
|
13874
|
+
const run = runHarness({
|
|
13875
|
+
harness,
|
|
13876
|
+
cwd: workspace,
|
|
13877
|
+
mode: "bypass",
|
|
13878
|
+
prompt: WAKE_PROMPT,
|
|
13879
|
+
exclusive: true,
|
|
13880
|
+
token,
|
|
13881
|
+
// A dead run must not squat the slot — evict so the next wake can respawn.
|
|
13882
|
+
onExit: () => {
|
|
13883
|
+
runs.delete(key);
|
|
13884
|
+
opts.log(`\u2716 ${slot} ended`);
|
|
13885
|
+
},
|
|
13886
|
+
log: log2
|
|
13887
|
+
});
|
|
13858
13888
|
runs.set(key, { run, label: slot });
|
|
13859
13889
|
opts.log(`\u25B6 woke ${slot} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
13860
13890
|
}
|
|
@@ -13884,7 +13914,7 @@ function startHost(opts) {
|
|
|
13884
13914
|
}
|
|
13885
13915
|
|
|
13886
13916
|
// src/main.ts
|
|
13887
|
-
var here =
|
|
13917
|
+
var here = dirname3(fileURLToPath(import.meta.url));
|
|
13888
13918
|
var wsDeps = { file: workspacesFile() };
|
|
13889
13919
|
var DEVICE_SLOT = "Desktop";
|
|
13890
13920
|
var deviceToken = () => readToken(DEVICE_SLOT) || readToken();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@paigy/harness",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.4",
|
|
4
4
|
"description": "Run Claude Code / Codex on this machine, bridged to Paigy — launchable from your phone.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"vitest": "^2.1.8",
|
|
19
19
|
"qrcode": "^1.5.4",
|
|
20
20
|
"zod": "^3.24.1",
|
|
21
|
-
"@paigy/
|
|
22
|
-
"@paigy/
|
|
21
|
+
"@paigy/schema": "0.0.0",
|
|
22
|
+
"@paigy/sdk": "0.1.0"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
25
|
"dist/cli.js",
|