@paigy/harness 0.3.0 → 0.3.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.
- package/dist/cli.js +204 -69
- package/dist/main.js +589 -496
- package/package.json +13 -12
package/dist/cli.js
CHANGED
|
@@ -23175,6 +23175,79 @@ function detectAll(deps = {}) {
|
|
|
23175
23175
|
return CATALOG.map((entry) => detect(entry, deps));
|
|
23176
23176
|
}
|
|
23177
23177
|
|
|
23178
|
+
// src/cli-args.ts
|
|
23179
|
+
var USAGE = "usage: paigy-harness setup | pair | host [--grant DIR]\u2026 | service | enable-tools [--scope user|project] | hatch NAME [--voice KEY] [--slot SLOT] | handoff | [--harness claude|codex|agy] [--mode bypass|ask] [--cwd DIR] [--identity NAME] [--grace SECONDS] [--doctor] PROMPT\u2026";
|
|
23180
|
+
function parseArgs(argv) {
|
|
23181
|
+
const args = { harness: "claude", mode: "bypass", cwd: process.cwd(), doctor: false, help: false, host: false, pair: false, service: false, setup: false, enableTools: false, scope: "user", grant: [], hatch: null, voice: null, slot: null, identity: null, graceSeconds: 90, prompt: "", handoff: false };
|
|
23182
|
+
const words = [];
|
|
23183
|
+
for (let i = 0; i < argv.length; i++) {
|
|
23184
|
+
const arg = argv[i];
|
|
23185
|
+
const next = () => argv[++i];
|
|
23186
|
+
if (arg === "--harness") {
|
|
23187
|
+
const v = next();
|
|
23188
|
+
if (v !== "claude" && v !== "codex" && v !== "agy") return { error: `--harness must be claude, codex, or agy, got ${v ?? "nothing"}` };
|
|
23189
|
+
args.harness = v;
|
|
23190
|
+
} else if (arg === "--mode") {
|
|
23191
|
+
const v = next();
|
|
23192
|
+
if (v !== "bypass" && v !== "ask") return { error: `--mode must be bypass or ask, got ${v ?? "nothing"}` };
|
|
23193
|
+
args.mode = v;
|
|
23194
|
+
} else if (arg === "--cwd") {
|
|
23195
|
+
const v = next();
|
|
23196
|
+
if (!v) return { error: "--cwd needs a directory" };
|
|
23197
|
+
args.cwd = v;
|
|
23198
|
+
} else if (arg === "--doctor") {
|
|
23199
|
+
args.doctor = true;
|
|
23200
|
+
} else if (arg === "-h" || arg === "--help" || arg === "help") {
|
|
23201
|
+
args.help = true;
|
|
23202
|
+
} else if (arg === "--scope") {
|
|
23203
|
+
const v = next();
|
|
23204
|
+
if (v !== "user" && v !== "project") return { error: `--scope must be user or project, got ${v ?? "nothing"}` };
|
|
23205
|
+
args.scope = v;
|
|
23206
|
+
} else if (arg === "--grace") {
|
|
23207
|
+
const v = Number(next());
|
|
23208
|
+
if (!Number.isFinite(v) || v < 0) return { error: "--grace needs seconds (0 = straight to phone)" };
|
|
23209
|
+
args.graceSeconds = v;
|
|
23210
|
+
} else if (arg === "host") {
|
|
23211
|
+
args.host = true;
|
|
23212
|
+
} else if (arg === "pair") {
|
|
23213
|
+
args.pair = true;
|
|
23214
|
+
} else if (arg === "setup") {
|
|
23215
|
+
args.setup = true;
|
|
23216
|
+
} else if (arg === "service") {
|
|
23217
|
+
args.service = true;
|
|
23218
|
+
} else if (arg === "--grant") {
|
|
23219
|
+
const v = next();
|
|
23220
|
+
if (!v) return { error: "--grant needs a folder to allow" };
|
|
23221
|
+
args.grant.push(v);
|
|
23222
|
+
} else if (arg === "handoff") {
|
|
23223
|
+
args.handoff = true;
|
|
23224
|
+
} else if (arg === "enable-tools") {
|
|
23225
|
+
args.enableTools = true;
|
|
23226
|
+
} else if (arg === "hatch") {
|
|
23227
|
+
const v = next();
|
|
23228
|
+
if (!v) return { error: 'hatch needs a name: paigy-harness hatch "Voice bug hunter"' };
|
|
23229
|
+
args.hatch = v;
|
|
23230
|
+
} else if (arg === "--voice") {
|
|
23231
|
+
args.voice = next() ?? null;
|
|
23232
|
+
} else if (arg === "--slot") {
|
|
23233
|
+
args.slot = next() ?? null;
|
|
23234
|
+
} else if (arg === "--identity") {
|
|
23235
|
+
const v = next();
|
|
23236
|
+
if (!v) return { error: "--identity needs a hatched agent's name" };
|
|
23237
|
+
args.identity = v;
|
|
23238
|
+
} else if (arg?.startsWith("--")) {
|
|
23239
|
+
return { error: `unknown flag ${arg}` };
|
|
23240
|
+
} else if (arg) {
|
|
23241
|
+
words.push(arg);
|
|
23242
|
+
}
|
|
23243
|
+
}
|
|
23244
|
+
args.prompt = words.join(" ");
|
|
23245
|
+
if (args.help) return args;
|
|
23246
|
+
if (!args.doctor && !args.handoff && !args.hatch && !args.host && !args.pair && !args.service && !args.setup && !args.enableTools && !args.prompt) return { error: "a prompt is required (or setup / --doctor / pair / host / service / enable-tools / hatch NAME / handoff)" };
|
|
23247
|
+
return args;
|
|
23248
|
+
}
|
|
23249
|
+
var ENABLE_TOOLS_COMMAND = "npx -y -p @paigy/mcp@latest paigy-enable-tools";
|
|
23250
|
+
|
|
23178
23251
|
// ../../packages/sdk/dist/index.js
|
|
23179
23252
|
import { createRequire as __sdkCreateRequire } from "module";
|
|
23180
23253
|
import { randomUUID } from "crypto";
|
|
@@ -29694,7 +29767,7 @@ var NotifyRequestSchema = external_exports.object({
|
|
|
29694
29767
|
if (r.ask !== void 0) {
|
|
29695
29768
|
for (const f of ["context", "select", "points"]) {
|
|
29696
29769
|
if (r[f] !== void 0)
|
|
29697
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives it
|
|
29770
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
|
|
29698
29771
|
}
|
|
29699
29772
|
return;
|
|
29700
29773
|
}
|
|
@@ -29967,7 +30040,13 @@ var NotifyPlanUnitSchema = external_exports.object({
|
|
|
29967
30040
|
proposal: external_exports.object({
|
|
29968
30041
|
select: SelectShapeSchema,
|
|
29969
30042
|
options: external_exports.array(external_exports.object({ label: external_exports.string().min(1) })).optional()
|
|
29970
|
-
}).optional()
|
|
30043
|
+
}).optional(),
|
|
30044
|
+
/** What the broker READ this unit as wanting from the human (#952 layer 2): a `decision`
|
|
30045
|
+
* between alternatives, an `approval` the agent is blocked on, or `knowledge` it just
|
|
30046
|
+
* needs to know. Reported so the agent can correct a misread the same way it ratifies a
|
|
30047
|
+
* shape — the read RAISES (a decision always asks) and never silences a question the
|
|
30048
|
+
* agent declared (#731, #923). */
|
|
30049
|
+
wants: external_exports.enum(["decision", "approval", "knowledge"]).optional()
|
|
29971
30050
|
});
|
|
29972
30051
|
var NotifyPlanSchema = external_exports.object({
|
|
29973
30052
|
units: external_exports.array(NotifyPlanUnitSchema),
|
|
@@ -30214,6 +30293,15 @@ var HistoryItemSchema = external_exports.object({
|
|
|
30214
30293
|
/** When you answered the agent's notification (agent→user only). */
|
|
30215
30294
|
humanAckedAt: external_exports.string().nullable()
|
|
30216
30295
|
});
|
|
30296
|
+
var ACTIVITY_LINES = 2;
|
|
30297
|
+
var ACTIVITY_LINE_MAX = 80;
|
|
30298
|
+
var AgentActivitySchema = external_exports.object({
|
|
30299
|
+
/** Oldest first, so the newest line is last — the one that replaces in place. */
|
|
30300
|
+
lines: external_exports.array(external_exports.string().max(ACTIVITY_LINE_MAX)).max(ACTIVITY_LINES),
|
|
30301
|
+
/** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
|
|
30302
|
+
* that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
|
|
30303
|
+
at: external_exports.string().datetime()
|
|
30304
|
+
});
|
|
30217
30305
|
var ConnectionSummarySchema = external_exports.object({
|
|
30218
30306
|
/** The connection = the agent's token id (used to address a request). */
|
|
30219
30307
|
id: external_exports.string(),
|
|
@@ -30247,6 +30335,11 @@ var ConnectionSummarySchema = external_exports.object({
|
|
|
30247
30335
|
harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
|
|
30248
30336
|
workspaces: external_exports.array(external_exports.string()).optional()
|
|
30249
30337
|
}).optional(),
|
|
30338
|
+
/** The tail of this agent's working log, when a harness is driving it — the agent page's
|
|
30339
|
+
* live strip. Absent for anything the desktop harness isn't running (a hatched identity
|
|
30340
|
+
* used straight from a terminal emits no work events; the page says so rather than
|
|
30341
|
+
* drawing an empty box). */
|
|
30342
|
+
activity: AgentActivitySchema.optional(),
|
|
30250
30343
|
/** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
|
|
30251
30344
|
* false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
|
|
30252
30345
|
managed: external_exports.boolean()
|
|
@@ -31081,10 +31174,14 @@ async function claimSessions(opts = {}) {
|
|
|
31081
31174
|
return (await res.json()).sessions;
|
|
31082
31175
|
}
|
|
31083
31176
|
async function heartbeat(runtime, opts = {}) {
|
|
31177
|
+
const body = {
|
|
31178
|
+
...runtime !== void 0 ? { runtime } : {},
|
|
31179
|
+
...opts.activity !== void 0 ? { activity: opts.activity } : {}
|
|
31180
|
+
};
|
|
31084
31181
|
const res = ensureAuthed(await reach(`${BACKEND_URL}/api/presence`, {
|
|
31085
31182
|
method: "POST",
|
|
31086
31183
|
headers: { "content-type": "application/json", authorization: `Bearer ${authToken(opts.token)}` },
|
|
31087
|
-
...
|
|
31184
|
+
...Object.keys(body).length > 0 ? { body: JSON.stringify(body) } : {}
|
|
31088
31185
|
}));
|
|
31089
31186
|
if (!res.ok) throw new Error(`heartbeat failed: ${res.status}`);
|
|
31090
31187
|
}
|
|
@@ -31299,7 +31396,7 @@ var NotifyRequestSchema2 = external_exports.object({
|
|
|
31299
31396
|
if (r.ask !== void 0) {
|
|
31300
31397
|
for (const f of ["context", "select", "points"]) {
|
|
31301
31398
|
if (r[f] !== void 0)
|
|
31302
|
-
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives it
|
|
31399
|
+
ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: [f], message: `the simplified \`ask\` form takes no ${f} \u2014 the broker derives the answer shape from your prose. Drop ${f} and say it in \`ask\` instead ("should I\u2026" for approve/deny, "which of these\u2026" for a pick), passing \`options\` when you're offering concrete alternatives.` });
|
|
31303
31400
|
}
|
|
31304
31401
|
return;
|
|
31305
31402
|
}
|
|
@@ -31534,7 +31631,13 @@ var NotifyPlanUnitSchema2 = external_exports.object({
|
|
|
31534
31631
|
proposal: external_exports.object({
|
|
31535
31632
|
select: SelectShapeSchema2,
|
|
31536
31633
|
options: external_exports.array(external_exports.object({ label: external_exports.string().min(1) })).optional()
|
|
31537
|
-
}).optional()
|
|
31634
|
+
}).optional(),
|
|
31635
|
+
/** What the broker READ this unit as wanting from the human (#952 layer 2): a `decision`
|
|
31636
|
+
* between alternatives, an `approval` the agent is blocked on, or `knowledge` it just
|
|
31637
|
+
* needs to know. Reported so the agent can correct a misread the same way it ratifies a
|
|
31638
|
+
* shape — the read RAISES (a decision always asks) and never silences a question the
|
|
31639
|
+
* agent declared (#731, #923). */
|
|
31640
|
+
wants: external_exports.enum(["decision", "approval", "knowledge"]).optional()
|
|
31538
31641
|
});
|
|
31539
31642
|
var NotifyPlanSchema2 = external_exports.object({
|
|
31540
31643
|
units: external_exports.array(NotifyPlanUnitSchema2),
|
|
@@ -31781,6 +31884,15 @@ var HistoryItemSchema2 = external_exports.object({
|
|
|
31781
31884
|
/** When you answered the agent's notification (agent→user only). */
|
|
31782
31885
|
humanAckedAt: external_exports.string().nullable()
|
|
31783
31886
|
});
|
|
31887
|
+
var ACTIVITY_LINES2 = 2;
|
|
31888
|
+
var ACTIVITY_LINE_MAX2 = 80;
|
|
31889
|
+
var AgentActivitySchema2 = external_exports.object({
|
|
31890
|
+
/** Oldest first, so the newest line is last — the one that replaces in place. */
|
|
31891
|
+
lines: external_exports.array(external_exports.string().max(ACTIVITY_LINE_MAX2)).max(ACTIVITY_LINES2),
|
|
31892
|
+
/** When the harness observed this tail. Its own timestamp, not the heartbeat's: a beat
|
|
31893
|
+
* that carries an UNCHANGED tail must not make a stalled agent look like it just moved. */
|
|
31894
|
+
at: external_exports.string().datetime()
|
|
31895
|
+
});
|
|
31784
31896
|
var ConnectionSummarySchema2 = external_exports.object({
|
|
31785
31897
|
/** The connection = the agent's token id (used to address a request). */
|
|
31786
31898
|
id: external_exports.string(),
|
|
@@ -31814,6 +31926,11 @@ var ConnectionSummarySchema2 = external_exports.object({
|
|
|
31814
31926
|
harnesses: external_exports.array(external_exports.object({ name: external_exports.string(), label: external_exports.string(), status: external_exports.string() })).optional(),
|
|
31815
31927
|
workspaces: external_exports.array(external_exports.string()).optional()
|
|
31816
31928
|
}).optional(),
|
|
31929
|
+
/** The tail of this agent's working log, when a harness is driving it — the agent page's
|
|
31930
|
+
* live strip. Absent for anything the desktop harness isn't running (a hatched identity
|
|
31931
|
+
* used straight from a terminal emits no work events; the page says so rather than
|
|
31932
|
+
* drawing an empty box). */
|
|
31933
|
+
activity: AgentActivitySchema2.optional(),
|
|
31817
31934
|
/** True = a provider-managed agent running in the provider's cloud (e.g. Anthropic CMA);
|
|
31818
31935
|
* false = a local MCP connection running on the user's computer (Claude Code/Codex/…). */
|
|
31819
31936
|
managed: external_exports.boolean()
|
|
@@ -32690,9 +32807,30 @@ function startSession(opts) {
|
|
|
32690
32807
|
};
|
|
32691
32808
|
}
|
|
32692
32809
|
|
|
32810
|
+
// src/paigy/activity.ts
|
|
32811
|
+
function shortenPaths(s) {
|
|
32812
|
+
return s.replace(/(?<![\w.@+-])(?:\/[\w.@+-]+){3,}/g, (p) => {
|
|
32813
|
+
const parts = p.split("/").filter(Boolean);
|
|
32814
|
+
return `\u2026/${parts.slice(-2).join("/")}`;
|
|
32815
|
+
});
|
|
32816
|
+
}
|
|
32817
|
+
function workLine(event) {
|
|
32818
|
+
const note = (event.note ?? "").split("\n").find((l) => l.trim()) ?? "";
|
|
32819
|
+
const line = shortenPaths([event.tool.trim(), note.trim()].filter(Boolean).join(" \u2014 ").replace(/\s+/g, " "));
|
|
32820
|
+
return line.length > ACTIVITY_LINE_MAX2 ? `${line.slice(0, ACTIVITY_LINE_MAX2 - 1)}\u2026` : line;
|
|
32821
|
+
}
|
|
32822
|
+
function pushWork(lines, line) {
|
|
32823
|
+
if (!line || lines[lines.length - 1] === line) return [...lines];
|
|
32824
|
+
return [...lines, line].slice(-ACTIVITY_LINES2);
|
|
32825
|
+
}
|
|
32826
|
+
function sameTail(a, b) {
|
|
32827
|
+
return a.length === b.length && a.every((l, i) => l === b[i]);
|
|
32828
|
+
}
|
|
32829
|
+
|
|
32693
32830
|
// src/run.ts
|
|
32694
32831
|
function runHarness(opts) {
|
|
32695
32832
|
let running = true;
|
|
32833
|
+
let tail = [];
|
|
32696
32834
|
const state = { ...opts.exclusive ? { exclusive: true } : {} };
|
|
32697
32835
|
let session = null;
|
|
32698
32836
|
const asMe = { token: opts.token };
|
|
@@ -32722,6 +32860,7 @@ function runHarness(opts) {
|
|
|
32722
32860
|
}
|
|
32723
32861
|
if (event.kind === "work") {
|
|
32724
32862
|
opts.log(`\u2699 ${event.tool}${event.note ? ` \u2014 ${event.note.split("\n")[0] ?? ""}` : ""}`);
|
|
32863
|
+
tail = pushWork(tail, workLine(event));
|
|
32725
32864
|
return;
|
|
32726
32865
|
}
|
|
32727
32866
|
const { parentId } = await mirror(event, state, deps);
|
|
@@ -32729,6 +32868,7 @@ function runHarness(opts) {
|
|
|
32729
32868
|
if (event.kind === "turn") opts.log(`${event.role}: ${event.text.split("\n")[0] ?? ""}`);
|
|
32730
32869
|
if (event.kind === "idle") {
|
|
32731
32870
|
state.resting = true;
|
|
32871
|
+
tail = [];
|
|
32732
32872
|
if (endsWithQuestion(event.result)) {
|
|
32733
32873
|
const local = await opts.localAsk?.question?.(event.result ?? "") ?? null;
|
|
32734
32874
|
if (local?.trim() && session && running) {
|
|
@@ -32766,8 +32906,10 @@ function runHarness(opts) {
|
|
|
32766
32906
|
session?.send(text);
|
|
32767
32907
|
},
|
|
32768
32908
|
working: () => running && state.resting !== true,
|
|
32909
|
+
tail: () => [...tail],
|
|
32769
32910
|
stop() {
|
|
32770
32911
|
running = false;
|
|
32912
|
+
tail = [];
|
|
32771
32913
|
cancelAsks(state);
|
|
32772
32914
|
session?.stop();
|
|
32773
32915
|
session = null;
|
|
@@ -32909,6 +33051,27 @@ function startHost(opts) {
|
|
|
32909
33051
|
opts.log(`\u25B6 woke ${label} (${harness}) \u2014 work was waiting in ${workspace}`);
|
|
32910
33052
|
}
|
|
32911
33053
|
}
|
|
33054
|
+
const ACTIVITY_MS = 2e3;
|
|
33055
|
+
const published = /* @__PURE__ */ new Map();
|
|
33056
|
+
const streamActivity = () => {
|
|
33057
|
+
const publishTail = (token, lines) => {
|
|
33058
|
+
void heartbeat(void 0, { token, activity: { lines, at: (/* @__PURE__ */ new Date()).toISOString() } }).catch(() => {
|
|
33059
|
+
});
|
|
33060
|
+
};
|
|
33061
|
+
for (const [key, r] of runs) {
|
|
33062
|
+
if (!r.token) continue;
|
|
33063
|
+
const lines = r.run.tail();
|
|
33064
|
+
const was = published.get(key);
|
|
33065
|
+
if (was && sameTail(was.lines, lines)) continue;
|
|
33066
|
+
published.set(key, { token: r.token, lines });
|
|
33067
|
+
publishTail(r.token, lines);
|
|
33068
|
+
}
|
|
33069
|
+
for (const [key, was] of published) {
|
|
33070
|
+
if (runs.has(key)) continue;
|
|
33071
|
+
published.delete(key);
|
|
33072
|
+
if (was.lines.length > 0) publishTail(was.token, []);
|
|
33073
|
+
}
|
|
33074
|
+
};
|
|
32912
33075
|
const publish = () => {
|
|
32913
33076
|
try {
|
|
32914
33077
|
writeFileSync3(HOST_FILE, JSON.stringify({ pid: process.pid, at: Date.now(), roster: api.roster() }));
|
|
@@ -32922,6 +33085,7 @@ function startHost(opts) {
|
|
|
32922
33085
|
void sweepSlots();
|
|
32923
33086
|
publish();
|
|
32924
33087
|
}, 5e3);
|
|
33088
|
+
const activityTick = setInterval(streamActivity, ACTIVITY_MS);
|
|
32925
33089
|
const stopSessions = () => {
|
|
32926
33090
|
for (const { run, label } of runs.values()) {
|
|
32927
33091
|
run.stop();
|
|
@@ -32959,6 +33123,8 @@ function startHost(opts) {
|
|
|
32959
33123
|
clearInterval(pulse);
|
|
32960
33124
|
clearInterval(spawnPoll);
|
|
32961
33125
|
stopSessions();
|
|
33126
|
+
streamActivity();
|
|
33127
|
+
clearInterval(activityTick);
|
|
32962
33128
|
try {
|
|
32963
33129
|
writeFileSync3(HOST_FILE, JSON.stringify({ pid: process.pid, at: 0, roster: [] }));
|
|
32964
33130
|
} catch {
|
|
@@ -32970,74 +33136,38 @@ function startHost(opts) {
|
|
|
32970
33136
|
}
|
|
32971
33137
|
|
|
32972
33138
|
// src/cli.ts
|
|
32973
|
-
function parseArgs(argv) {
|
|
32974
|
-
const args = { harness: "claude", mode: "bypass", cwd: process.cwd(), doctor: false, host: false, pair: false, service: false, setup: false, grant: [], hatch: null, voice: null, slot: null, identity: null, graceSeconds: 90, prompt: "", handoff: false };
|
|
32975
|
-
const words = [];
|
|
32976
|
-
for (let i = 0; i < argv.length; i++) {
|
|
32977
|
-
const arg = argv[i];
|
|
32978
|
-
const next = () => argv[++i];
|
|
32979
|
-
if (arg === "--harness") {
|
|
32980
|
-
const v = next();
|
|
32981
|
-
if (v !== "claude" && v !== "codex" && v !== "agy") return { error: `--harness must be claude, codex, or agy, got ${v ?? "nothing"}` };
|
|
32982
|
-
args.harness = v;
|
|
32983
|
-
} else if (arg === "--mode") {
|
|
32984
|
-
const v = next();
|
|
32985
|
-
if (v !== "bypass" && v !== "ask") return { error: `--mode must be bypass or ask, got ${v ?? "nothing"}` };
|
|
32986
|
-
args.mode = v;
|
|
32987
|
-
} else if (arg === "--cwd") {
|
|
32988
|
-
const v = next();
|
|
32989
|
-
if (!v) return { error: "--cwd needs a directory" };
|
|
32990
|
-
args.cwd = v;
|
|
32991
|
-
} else if (arg === "--doctor") {
|
|
32992
|
-
args.doctor = true;
|
|
32993
|
-
} else if (arg === "--grace") {
|
|
32994
|
-
const v = Number(next());
|
|
32995
|
-
if (!Number.isFinite(v) || v < 0) return { error: "--grace needs seconds (0 = straight to phone)" };
|
|
32996
|
-
args.graceSeconds = v;
|
|
32997
|
-
} else if (arg === "host") {
|
|
32998
|
-
args.host = true;
|
|
32999
|
-
} else if (arg === "pair") {
|
|
33000
|
-
args.pair = true;
|
|
33001
|
-
} else if (arg === "setup") {
|
|
33002
|
-
args.setup = true;
|
|
33003
|
-
} else if (arg === "service") {
|
|
33004
|
-
args.service = true;
|
|
33005
|
-
} else if (arg === "--grant") {
|
|
33006
|
-
const v = next();
|
|
33007
|
-
if (!v) return { error: "--grant needs a folder to allow" };
|
|
33008
|
-
args.grant.push(v);
|
|
33009
|
-
} else if (arg === "handoff") {
|
|
33010
|
-
args.handoff = true;
|
|
33011
|
-
} else if (arg === "hatch") {
|
|
33012
|
-
const v = next();
|
|
33013
|
-
if (!v) return { error: 'hatch needs a name: paigy-harness hatch "Voice bug hunter"' };
|
|
33014
|
-
args.hatch = v;
|
|
33015
|
-
} else if (arg === "--voice") {
|
|
33016
|
-
args.voice = next() ?? null;
|
|
33017
|
-
} else if (arg === "--slot") {
|
|
33018
|
-
args.slot = next() ?? null;
|
|
33019
|
-
} else if (arg === "--identity") {
|
|
33020
|
-
const v = next();
|
|
33021
|
-
if (!v) return { error: "--identity needs a hatched agent's name" };
|
|
33022
|
-
args.identity = v;
|
|
33023
|
-
} else if (arg?.startsWith("--")) {
|
|
33024
|
-
return { error: `unknown flag ${arg}` };
|
|
33025
|
-
} else if (arg) {
|
|
33026
|
-
words.push(arg);
|
|
33027
|
-
}
|
|
33028
|
-
}
|
|
33029
|
-
args.prompt = words.join(" ");
|
|
33030
|
-
if (!args.doctor && !args.handoff && !args.hatch && !args.host && !args.pair && !args.service && !args.setup && !args.prompt) return { error: "a prompt is required (or setup / --doctor / pair / host / service / hatch NAME / handoff)" };
|
|
33031
|
-
return args;
|
|
33032
|
-
}
|
|
33033
33139
|
var STATUS_MARK = { ready: "\u2713", login: "\u25D0", "adapter-missing": "\u25D0", missing: "\u2717" };
|
|
33034
33140
|
async function main() {
|
|
33035
33141
|
const parsed = parseArgs(process.argv.slice(2));
|
|
33036
33142
|
if ("error" in parsed) {
|
|
33037
33143
|
console.error(`paigy-harness: ${parsed.error}`);
|
|
33038
|
-
console.error(
|
|
33144
|
+
console.error(USAGE);
|
|
33039
33145
|
process.exit(2);
|
|
33040
33146
|
}
|
|
33147
|
+
if (parsed.help) {
|
|
33148
|
+
console.log(USAGE);
|
|
33149
|
+
console.log("");
|
|
33150
|
+
console.log(" setup pair this machine, set up every agent found here, install the host service");
|
|
33151
|
+
console.log(" pair pair this machine only (QR / code)");
|
|
33152
|
+
console.log(" host host sessions your phone launches (--grant DIR to allow a folder)");
|
|
33153
|
+
console.log(" service install the host as a login service (macOS)");
|
|
33154
|
+
console.log(" enable-tools allowlist Paigy's tools in Claude Code so they don't prompt each time");
|
|
33155
|
+
console.log(" hatch NAME mint a sibling identity from this machine's credential");
|
|
33156
|
+
console.log(" handoff pin this terminal's identity here so your phone can resume it");
|
|
33157
|
+
console.log(" --doctor report which agent CLIs are installed and ready");
|
|
33158
|
+
console.log("");
|
|
33159
|
+
console.log('Anything else is a PROMPT: paigy-harness --harness codex "fix the tests"');
|
|
33160
|
+
return;
|
|
33161
|
+
}
|
|
33162
|
+
if (parsed.enableTools) {
|
|
33163
|
+
try {
|
|
33164
|
+
execSync(`${ENABLE_TOOLS_COMMAND} --scope ${parsed.scope}`, { stdio: "inherit", shell: "/bin/sh" });
|
|
33165
|
+
} catch {
|
|
33166
|
+
console.error(`Couldn't run it \u2014 try directly: ${ENABLE_TOOLS_COMMAND} --scope ${parsed.scope}`);
|
|
33167
|
+
process.exit(1);
|
|
33168
|
+
}
|
|
33169
|
+
return;
|
|
33170
|
+
}
|
|
33041
33171
|
if (parsed.doctor) {
|
|
33042
33172
|
for (const a of detectAll()) {
|
|
33043
33173
|
console.log(`${STATUS_MARK[a.status]} ${a.label}${a.hint ? ` \u2014 ${a.hint}` : ""}${a.install ? `
|
|
@@ -33120,6 +33250,14 @@ async function main() {
|
|
|
33120
33250
|
} catch {
|
|
33121
33251
|
}
|
|
33122
33252
|
}
|
|
33253
|
+
if (which("claude")) {
|
|
33254
|
+
try {
|
|
33255
|
+
execSync(ENABLE_TOOLS_COMMAND, { stdio: "ignore", shell: "/bin/sh" });
|
|
33256
|
+
console.log("\u2713 Paigy's tools allowlisted in Claude Code (they won't prompt each time)");
|
|
33257
|
+
} catch {
|
|
33258
|
+
console.log(` allowlist it later with: paigy-harness enable-tools`);
|
|
33259
|
+
}
|
|
33260
|
+
}
|
|
33123
33261
|
try {
|
|
33124
33262
|
const settings = join5(homedir6(), ".claude", "settings.json");
|
|
33125
33263
|
if (which("claude") && existsSync5(settings)) {
|
|
@@ -33306,9 +33444,6 @@ async function main() {
|
|
|
33306
33444
|
});
|
|
33307
33445
|
}
|
|
33308
33446
|
void main();
|
|
33309
|
-
export {
|
|
33310
|
-
parseArgs
|
|
33311
|
-
};
|
|
33312
33447
|
/*! Bundled license information:
|
|
33313
33448
|
|
|
33314
33449
|
undici/lib/web/fetch/body.js:
|