@fancyboi999/open-tag-daemon 0.1.0 → 0.3.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.
- package/dist/cli.mjs +1124 -29
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -3725,7 +3725,7 @@ try {
|
|
|
3725
3725
|
// src/daemon/index.ts
|
|
3726
3726
|
import os4 from "node:os";
|
|
3727
3727
|
import fs3 from "node:fs";
|
|
3728
|
-
import
|
|
3728
|
+
import path12 from "node:path";
|
|
3729
3729
|
|
|
3730
3730
|
// node_modules/ws/wrapper.mjs
|
|
3731
3731
|
var import_stream = __toESM(require_stream(), 1);
|
|
@@ -3791,22 +3791,31 @@ function safeJson(o) {
|
|
|
3791
3791
|
}
|
|
3792
3792
|
}
|
|
3793
3793
|
|
|
3794
|
+
// src/daemonProtocol.ts
|
|
3795
|
+
var MACHINE_REJECTED_CODE = 4001;
|
|
3796
|
+
|
|
3794
3797
|
// src/daemon/connection.ts
|
|
3798
|
+
var INITIAL_BACKOFF_MS = 1e3;
|
|
3799
|
+
var MAX_BACKOFF_MS = 3e4;
|
|
3795
3800
|
var Connection = class {
|
|
3796
|
-
constructor(url, key, onMsg, onOpen) {
|
|
3801
|
+
constructor(url, key, onMsg, onOpen, mkWs = (u) => new wrapper_default(u)) {
|
|
3797
3802
|
this.url = url;
|
|
3798
3803
|
this.key = key;
|
|
3799
3804
|
this.onMsg = onMsg;
|
|
3800
3805
|
this.onOpen = onOpen;
|
|
3806
|
+
this.mkWs = mkWs;
|
|
3801
3807
|
}
|
|
3802
3808
|
url;
|
|
3803
3809
|
key;
|
|
3804
3810
|
onMsg;
|
|
3805
3811
|
onOpen;
|
|
3812
|
+
mkWs;
|
|
3806
3813
|
ws = null;
|
|
3807
|
-
delay =
|
|
3814
|
+
delay = INITIAL_BACKOFF_MS;
|
|
3808
3815
|
timer = null;
|
|
3809
3816
|
should = true;
|
|
3817
|
+
accepted = false;
|
|
3818
|
+
// per-attempt: flips true once the server sends any frame (proof it accepted us, not rejected)
|
|
3810
3819
|
log = createLogger("daemon:conn");
|
|
3811
3820
|
connect() {
|
|
3812
3821
|
this.should = true;
|
|
@@ -3824,13 +3833,17 @@ var Connection = class {
|
|
|
3824
3833
|
if (!this.should) return;
|
|
3825
3834
|
const wsUrl = this.url.replace(/^http/, "ws") + `/daemon/connect?key=${encodeURIComponent(this.key)}`;
|
|
3826
3835
|
this.log.info("connecting", { url: this.url });
|
|
3827
|
-
this.
|
|
3836
|
+
this.accepted = false;
|
|
3837
|
+
this.ws = this.mkWs(wsUrl);
|
|
3828
3838
|
this.ws.on("open", () => {
|
|
3829
|
-
this.delay = 1e3;
|
|
3830
3839
|
this.log.info("connected");
|
|
3831
3840
|
this.onOpen();
|
|
3832
3841
|
});
|
|
3833
3842
|
this.ws.on("message", (d) => {
|
|
3843
|
+
if (!this.accepted) {
|
|
3844
|
+
this.accepted = true;
|
|
3845
|
+
this.delay = INITIAL_BACKOFF_MS;
|
|
3846
|
+
}
|
|
3834
3847
|
let m;
|
|
3835
3848
|
try {
|
|
3836
3849
|
m = JSON.parse(d.toString());
|
|
@@ -3839,8 +3852,16 @@ var Connection = class {
|
|
|
3839
3852
|
}
|
|
3840
3853
|
this.onMsg(m);
|
|
3841
3854
|
});
|
|
3842
|
-
this.ws.on("close", () => {
|
|
3843
|
-
|
|
3855
|
+
this.ws.on("close", (code, reason) => {
|
|
3856
|
+
if (code === MACHINE_REJECTED_CODE) {
|
|
3857
|
+
this.delay = MAX_BACKOFF_MS;
|
|
3858
|
+
this.log.error(
|
|
3859
|
+
"server rejected this machine: its connection key is unknown or was removed. Re-issue the connect command (workspace \u2192 Computers \u2192 this machine \u2192 Reconnect), then restart the daemon with the new key.",
|
|
3860
|
+
{ code, reason: reason?.toString?.() ?? String(reason ?? "") }
|
|
3861
|
+
);
|
|
3862
|
+
} else {
|
|
3863
|
+
this.log.warn("disconnected", { code });
|
|
3864
|
+
}
|
|
3844
3865
|
this.scheduleReconnect();
|
|
3845
3866
|
});
|
|
3846
3867
|
this.ws.on("error", (e) => this.log.error("ws error", { detail: String(e?.message ?? e) }));
|
|
@@ -3852,13 +3873,13 @@ var Connection = class {
|
|
|
3852
3873
|
this.timer = null;
|
|
3853
3874
|
this.doConnect();
|
|
3854
3875
|
}, this.delay);
|
|
3855
|
-
this.delay = Math.min(this.delay * 2,
|
|
3876
|
+
this.delay = Math.min(this.delay * 2, MAX_BACKOFF_MS);
|
|
3856
3877
|
}
|
|
3857
3878
|
};
|
|
3858
3879
|
|
|
3859
3880
|
// src/daemon/agentManager.ts
|
|
3860
3881
|
import { mkdir, writeFile, readFile, access, rm } from "node:fs/promises";
|
|
3861
|
-
import
|
|
3882
|
+
import path10 from "node:path";
|
|
3862
3883
|
import os2 from "node:os";
|
|
3863
3884
|
|
|
3864
3885
|
// src/daemon/prompt.ts
|
|
@@ -4421,6 +4442,893 @@ var codexRuntime = {
|
|
|
4421
4442
|
}
|
|
4422
4443
|
};
|
|
4423
4444
|
|
|
4445
|
+
// src/daemon/copilotRuntime.ts
|
|
4446
|
+
import { spawn as spawn3 } from "node:child_process";
|
|
4447
|
+
import { randomUUID } from "node:crypto";
|
|
4448
|
+
import { writeFileSync as writeFileSync2 } from "node:fs";
|
|
4449
|
+
import path5 from "node:path";
|
|
4450
|
+
var MAX3 = 2e3;
|
|
4451
|
+
var clip3 = (s) => String(s ?? "").slice(0, MAX3);
|
|
4452
|
+
var EFFORTS2 = /* @__PURE__ */ new Set(["none", "low", "medium", "high", "xhigh", "max"]);
|
|
4453
|
+
function reasoningEffort2(rc) {
|
|
4454
|
+
const e = rc?.reasoningEffort;
|
|
4455
|
+
return typeof e === "string" && EFFORTS2.has(e) ? e : null;
|
|
4456
|
+
}
|
|
4457
|
+
function summarizeToolArgs(tr) {
|
|
4458
|
+
let args2 = tr?.arguments;
|
|
4459
|
+
if (typeof args2 === "string") {
|
|
4460
|
+
try {
|
|
4461
|
+
args2 = JSON.parse(args2);
|
|
4462
|
+
} catch {
|
|
4463
|
+
return clip3(args2).slice(0, 160);
|
|
4464
|
+
}
|
|
4465
|
+
}
|
|
4466
|
+
if (!args2 || typeof args2 !== "object") return "";
|
|
4467
|
+
const v = args2.command ?? args2.path ?? args2.file_path ?? args2.filePath ?? args2.query ?? args2.pattern ?? args2.url ?? "";
|
|
4468
|
+
return clip3(typeof v === "string" ? v : JSON.stringify(v)).slice(0, 160);
|
|
4469
|
+
}
|
|
4470
|
+
function handleCopilotEvent(evt) {
|
|
4471
|
+
const out = { trajectory: [] };
|
|
4472
|
+
const data = evt?.data ?? {};
|
|
4473
|
+
switch (evt?.type) {
|
|
4474
|
+
case "assistant.turn_start":
|
|
4475
|
+
out.activity = { activity: "working", detail: "turn" };
|
|
4476
|
+
break;
|
|
4477
|
+
case "assistant.message": {
|
|
4478
|
+
if (data.content) out.trajectory.push({ kind: "text", text: clip3(data.content) });
|
|
4479
|
+
if (Array.isArray(data.toolRequests))
|
|
4480
|
+
for (const tr of data.toolRequests)
|
|
4481
|
+
out.trajectory.push({ kind: "tool", toolName: String(tr?.name ?? "tool"), toolInput: summarizeToolArgs(tr) });
|
|
4482
|
+
break;
|
|
4483
|
+
}
|
|
4484
|
+
case "assistant.reasoning":
|
|
4485
|
+
if (data.content) out.trajectory.push({ kind: "thinking", text: clip3(data.content) });
|
|
4486
|
+
break;
|
|
4487
|
+
case "result":
|
|
4488
|
+
if (typeof evt.sessionId === "string" && evt.sessionId) out.sessionId = evt.sessionId;
|
|
4489
|
+
if (typeof evt.exitCode === "number") out.exitCode = evt.exitCode;
|
|
4490
|
+
break;
|
|
4491
|
+
case "session.error":
|
|
4492
|
+
out.error = clip3(data.message ?? data.error ?? "copilot session error");
|
|
4493
|
+
break;
|
|
4494
|
+
}
|
|
4495
|
+
return out;
|
|
4496
|
+
}
|
|
4497
|
+
function buildArgs(prompt, sessionId, model, effort) {
|
|
4498
|
+
const args2 = ["-p", prompt, "--output-format", "json", "--allow-all", "--no-ask-user", "--session-id", sessionId];
|
|
4499
|
+
const m = model && model !== "default" ? model : "";
|
|
4500
|
+
if (m) args2.push("--model", m);
|
|
4501
|
+
if (effort) args2.push("--effort", effort);
|
|
4502
|
+
return args2;
|
|
4503
|
+
}
|
|
4504
|
+
var CopilotRun = class {
|
|
4505
|
+
constructor(opts, cb) {
|
|
4506
|
+
this.opts = opts;
|
|
4507
|
+
this.cb = cb;
|
|
4508
|
+
this.sessionId = opts.sessionId || randomUUID();
|
|
4509
|
+
this.env = { ...opts.env };
|
|
4510
|
+
delete this.env.NODE_OPTIONS;
|
|
4511
|
+
try {
|
|
4512
|
+
writeFileSync2(path5.join(opts.cwd, "AGENTS.md"), opts.systemPrompt);
|
|
4513
|
+
} catch (e) {
|
|
4514
|
+
cb.log.warn("copilot: AGENTS.md write failed", { detail: String(e) });
|
|
4515
|
+
}
|
|
4516
|
+
cb.onSession(this.sessionId);
|
|
4517
|
+
this.enqueue(opts.initialPrompt);
|
|
4518
|
+
}
|
|
4519
|
+
opts;
|
|
4520
|
+
cb;
|
|
4521
|
+
queue = [];
|
|
4522
|
+
turnBusy = false;
|
|
4523
|
+
stopped = false;
|
|
4524
|
+
proc = null;
|
|
4525
|
+
sessionId;
|
|
4526
|
+
everSucceeded = false;
|
|
4527
|
+
env;
|
|
4528
|
+
enqueue(text) {
|
|
4529
|
+
if (this.stopped) return;
|
|
4530
|
+
this.queue.push(text);
|
|
4531
|
+
this.pump();
|
|
4532
|
+
}
|
|
4533
|
+
pump() {
|
|
4534
|
+
if (this.stopped || this.turnBusy || this.queue.length === 0) return;
|
|
4535
|
+
this.runTurn(this.queue.shift());
|
|
4536
|
+
}
|
|
4537
|
+
runTurn(prompt) {
|
|
4538
|
+
this.turnBusy = true;
|
|
4539
|
+
this.cb.onActivity("working", "turn");
|
|
4540
|
+
const args2 = buildArgs(prompt, this.sessionId, this.opts.model, reasoningEffort2(this.opts.runtimeConfig));
|
|
4541
|
+
const proc = spawn3("copilot", args2, { cwd: this.opts.cwd, stdio: ["ignore", "pipe", "pipe"], env: this.env });
|
|
4542
|
+
this.proc = proc;
|
|
4543
|
+
let buf = "";
|
|
4544
|
+
let resultSeen = false;
|
|
4545
|
+
const errTail = [];
|
|
4546
|
+
let errLen = 0;
|
|
4547
|
+
const processLine = (ln) => {
|
|
4548
|
+
const t = ln.trim();
|
|
4549
|
+
if (!t) return;
|
|
4550
|
+
let evt;
|
|
4551
|
+
try {
|
|
4552
|
+
evt = JSON.parse(t);
|
|
4553
|
+
} catch {
|
|
4554
|
+
return;
|
|
4555
|
+
}
|
|
4556
|
+
const emit = handleCopilotEvent(evt);
|
|
4557
|
+
if (emit.exitCode !== void 0) resultSeen = true;
|
|
4558
|
+
if (emit.error) {
|
|
4559
|
+
this.cb.onTrajectory([{ kind: "text", text: "[copilot error] " + emit.error.slice(0, 500) }]);
|
|
4560
|
+
this.cb.onActivity("error", emit.error.slice(0, 200));
|
|
4561
|
+
}
|
|
4562
|
+
if (emit.activity) this.cb.onActivity(emit.activity.activity, emit.activity.detail);
|
|
4563
|
+
if (emit.trajectory.length) this.cb.onTrajectory(emit.trajectory);
|
|
4564
|
+
};
|
|
4565
|
+
proc.stdout?.on("data", (c) => {
|
|
4566
|
+
if (this.stopped) return;
|
|
4567
|
+
buf += c.toString();
|
|
4568
|
+
const lines = buf.split("\n");
|
|
4569
|
+
buf = lines.pop() ?? "";
|
|
4570
|
+
for (const ln of lines) processLine(ln);
|
|
4571
|
+
});
|
|
4572
|
+
proc.stderr?.on("data", (c) => {
|
|
4573
|
+
const t = c.toString();
|
|
4574
|
+
errTail.push(t);
|
|
4575
|
+
errLen += t.length;
|
|
4576
|
+
while (errLen > 4096 && errTail.length > 1) errLen -= errTail.shift().length;
|
|
4577
|
+
});
|
|
4578
|
+
proc.on("error", (e) => {
|
|
4579
|
+
this.proc = null;
|
|
4580
|
+
this.turnBusy = false;
|
|
4581
|
+
if (this.stopped) return;
|
|
4582
|
+
this.cb.log.error("copilot spawn failed", { detail: String(e?.message ?? e) });
|
|
4583
|
+
this.cb.onActivity("offline", "copilot not found");
|
|
4584
|
+
if (!this.everSucceeded) this.cb.onExit(1);
|
|
4585
|
+
else this.pump();
|
|
4586
|
+
});
|
|
4587
|
+
proc.on("exit", (code) => {
|
|
4588
|
+
if (buf.trim()) processLine(buf);
|
|
4589
|
+
buf = "";
|
|
4590
|
+
this.proc = null;
|
|
4591
|
+
this.turnBusy = false;
|
|
4592
|
+
if (this.stopped) return;
|
|
4593
|
+
if (code === 0 || resultSeen) {
|
|
4594
|
+
this.everSucceeded = true;
|
|
4595
|
+
this.cb.onActivity("online", "");
|
|
4596
|
+
this.pump();
|
|
4597
|
+
return;
|
|
4598
|
+
}
|
|
4599
|
+
const tail = errTail.join("").trim();
|
|
4600
|
+
const last = tail.split("\n").filter(Boolean).pop() || `copilot exited ${code ?? "signal"}`;
|
|
4601
|
+
this.cb.onTrajectory([{ kind: "text", text: "[copilot error] " + clip3(tail).slice(0, 500) }]);
|
|
4602
|
+
this.cb.onActivity("error", last.slice(0, 200));
|
|
4603
|
+
if (!this.everSucceeded) {
|
|
4604
|
+
this.cb.onExit(code ?? 1);
|
|
4605
|
+
return;
|
|
4606
|
+
}
|
|
4607
|
+
this.pump();
|
|
4608
|
+
});
|
|
4609
|
+
}
|
|
4610
|
+
stop() {
|
|
4611
|
+
this.stopped = true;
|
|
4612
|
+
const p = this.proc;
|
|
4613
|
+
this.proc = null;
|
|
4614
|
+
if (p) {
|
|
4615
|
+
try {
|
|
4616
|
+
p.kill("SIGTERM");
|
|
4617
|
+
} catch {
|
|
4618
|
+
}
|
|
4619
|
+
}
|
|
4620
|
+
}
|
|
4621
|
+
};
|
|
4622
|
+
var copilotRuntime = {
|
|
4623
|
+
name: "copilot",
|
|
4624
|
+
experimental: true,
|
|
4625
|
+
start(opts, cb) {
|
|
4626
|
+
const run = new CopilotRun(opts, cb);
|
|
4627
|
+
return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
|
|
4628
|
+
}
|
|
4629
|
+
};
|
|
4630
|
+
|
|
4631
|
+
// src/daemon/opencodeRuntime.ts
|
|
4632
|
+
import { spawn as spawn4 } from "node:child_process";
|
|
4633
|
+
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
4634
|
+
import path6 from "node:path";
|
|
4635
|
+
var MAX4 = 2e3;
|
|
4636
|
+
var clip4 = (s) => String(s ?? "").slice(0, MAX4);
|
|
4637
|
+
function variant(rc) {
|
|
4638
|
+
const e = rc?.reasoningEffort;
|
|
4639
|
+
return typeof e === "string" && e ? e : null;
|
|
4640
|
+
}
|
|
4641
|
+
function summarizeToolInput(input) {
|
|
4642
|
+
if (!input || typeof input !== "object") return clip4(input).slice(0, 160);
|
|
4643
|
+
const v = input.command ?? input.filePath ?? input.file_path ?? input.path ?? input.pattern ?? input.query ?? input.url ?? "";
|
|
4644
|
+
return clip4(typeof v === "string" ? v : JSON.stringify(v)).slice(0, 160);
|
|
4645
|
+
}
|
|
4646
|
+
function handleOpencodeEvent(evt) {
|
|
4647
|
+
const out = { trajectory: [] };
|
|
4648
|
+
if (typeof evt?.sessionID === "string" && evt.sessionID) out.sessionId = evt.sessionID;
|
|
4649
|
+
const part = evt?.part ?? {};
|
|
4650
|
+
switch (evt?.type) {
|
|
4651
|
+
case "step_start":
|
|
4652
|
+
out.activity = { activity: "working", detail: "turn" };
|
|
4653
|
+
break;
|
|
4654
|
+
case "text":
|
|
4655
|
+
if (part.text) out.trajectory.push({ kind: "text", text: clip4(part.text) });
|
|
4656
|
+
break;
|
|
4657
|
+
case "reasoning":
|
|
4658
|
+
if (part.text) out.trajectory.push({ kind: "thinking", text: clip4(part.text) });
|
|
4659
|
+
break;
|
|
4660
|
+
case "tool_use": {
|
|
4661
|
+
const name = String(part.tool ?? "tool");
|
|
4662
|
+
const input = part.state?.input ?? part.input;
|
|
4663
|
+
out.trajectory.push({ kind: "tool", toolName: name, toolInput: summarizeToolInput(input) });
|
|
4664
|
+
break;
|
|
4665
|
+
}
|
|
4666
|
+
case "error":
|
|
4667
|
+
out.error = String(evt.error?.data?.message ?? evt.error?.name ?? "opencode error");
|
|
4668
|
+
break;
|
|
4669
|
+
}
|
|
4670
|
+
return out;
|
|
4671
|
+
}
|
|
4672
|
+
function buildArgs2(message, opts, sessionId) {
|
|
4673
|
+
const args2 = ["run", "--format", "json", "--dangerously-skip-permissions", "--dir", opts.cwd];
|
|
4674
|
+
const model = opts.model && opts.model !== "default" ? opts.model : "";
|
|
4675
|
+
if (model) args2.push("--model", model);
|
|
4676
|
+
const v = variant(opts.runtimeConfig);
|
|
4677
|
+
if (v) args2.push("--variant", v);
|
|
4678
|
+
if (sessionId) args2.push("--session", sessionId);
|
|
4679
|
+
args2.push(message);
|
|
4680
|
+
return args2;
|
|
4681
|
+
}
|
|
4682
|
+
var OpencodeRun = class {
|
|
4683
|
+
constructor(opts, cb) {
|
|
4684
|
+
this.opts = opts;
|
|
4685
|
+
this.cb = cb;
|
|
4686
|
+
this.sessionId = opts.sessionId ?? null;
|
|
4687
|
+
this.env = { ...opts.env, PWD: opts.cwd };
|
|
4688
|
+
delete this.env.NODE_OPTIONS;
|
|
4689
|
+
try {
|
|
4690
|
+
writeFileSync3(path6.join(opts.cwd, "AGENTS.md"), opts.systemPrompt);
|
|
4691
|
+
} catch (e) {
|
|
4692
|
+
cb.log.warn("opencode: AGENTS.md write failed", { detail: String(e) });
|
|
4693
|
+
}
|
|
4694
|
+
if (this.sessionId) cb.onSession(this.sessionId);
|
|
4695
|
+
this.enqueue(opts.initialPrompt);
|
|
4696
|
+
}
|
|
4697
|
+
opts;
|
|
4698
|
+
cb;
|
|
4699
|
+
queue = [];
|
|
4700
|
+
turnBusy = false;
|
|
4701
|
+
stopped = false;
|
|
4702
|
+
proc = null;
|
|
4703
|
+
sessionId;
|
|
4704
|
+
everSucceeded = false;
|
|
4705
|
+
env;
|
|
4706
|
+
enqueue(text) {
|
|
4707
|
+
if (this.stopped) return;
|
|
4708
|
+
this.queue.push(text);
|
|
4709
|
+
this.pump();
|
|
4710
|
+
}
|
|
4711
|
+
pump() {
|
|
4712
|
+
if (this.stopped || this.turnBusy || this.queue.length === 0) return;
|
|
4713
|
+
this.runTurn(this.queue.shift());
|
|
4714
|
+
}
|
|
4715
|
+
runTurn(message) {
|
|
4716
|
+
this.turnBusy = true;
|
|
4717
|
+
this.cb.onActivity("working", "turn");
|
|
4718
|
+
const args2 = buildArgs2(message, this.opts, this.sessionId);
|
|
4719
|
+
const proc = spawn4("opencode", args2, { cwd: this.opts.cwd, stdio: ["ignore", "pipe", "pipe"], env: this.env });
|
|
4720
|
+
this.proc = proc;
|
|
4721
|
+
let buf = "";
|
|
4722
|
+
const errTail = [];
|
|
4723
|
+
let errLen = 0;
|
|
4724
|
+
const processLine = (ln) => {
|
|
4725
|
+
const t = ln.trim();
|
|
4726
|
+
if (!t) return;
|
|
4727
|
+
let evt;
|
|
4728
|
+
try {
|
|
4729
|
+
evt = JSON.parse(t);
|
|
4730
|
+
} catch {
|
|
4731
|
+
return;
|
|
4732
|
+
}
|
|
4733
|
+
const emit = handleOpencodeEvent(evt);
|
|
4734
|
+
if (emit.sessionId && emit.sessionId !== this.sessionId) {
|
|
4735
|
+
this.sessionId = emit.sessionId;
|
|
4736
|
+
this.cb.onSession(emit.sessionId);
|
|
4737
|
+
}
|
|
4738
|
+
if (emit.error) {
|
|
4739
|
+
this.cb.onTrajectory([{ kind: "text", text: "[opencode error] " + emit.error.slice(0, 500) }]);
|
|
4740
|
+
this.cb.onActivity("error", emit.error.slice(0, 200));
|
|
4741
|
+
}
|
|
4742
|
+
if (emit.activity) this.cb.onActivity(emit.activity.activity, emit.activity.detail);
|
|
4743
|
+
if (emit.trajectory.length) this.cb.onTrajectory(emit.trajectory);
|
|
4744
|
+
};
|
|
4745
|
+
proc.stdout?.on("data", (c) => {
|
|
4746
|
+
if (this.stopped) return;
|
|
4747
|
+
buf += c.toString();
|
|
4748
|
+
const lines = buf.split("\n");
|
|
4749
|
+
buf = lines.pop() ?? "";
|
|
4750
|
+
for (const ln of lines) processLine(ln);
|
|
4751
|
+
});
|
|
4752
|
+
proc.stderr?.on("data", (c) => {
|
|
4753
|
+
const t = c.toString();
|
|
4754
|
+
errTail.push(t);
|
|
4755
|
+
errLen += t.length;
|
|
4756
|
+
while (errLen > 4096 && errTail.length > 1) errLen -= errTail.shift().length;
|
|
4757
|
+
});
|
|
4758
|
+
proc.on("error", (e) => {
|
|
4759
|
+
this.proc = null;
|
|
4760
|
+
this.turnBusy = false;
|
|
4761
|
+
if (this.stopped) return;
|
|
4762
|
+
this.cb.log.error("opencode spawn failed", { detail: String(e?.message ?? e) });
|
|
4763
|
+
this.cb.onActivity("offline", "opencode not found");
|
|
4764
|
+
if (!this.everSucceeded) this.cb.onExit(1);
|
|
4765
|
+
else this.pump();
|
|
4766
|
+
});
|
|
4767
|
+
proc.on("exit", (code) => {
|
|
4768
|
+
if (buf.trim()) processLine(buf);
|
|
4769
|
+
buf = "";
|
|
4770
|
+
this.proc = null;
|
|
4771
|
+
this.turnBusy = false;
|
|
4772
|
+
if (this.stopped) return;
|
|
4773
|
+
if (code === 0) {
|
|
4774
|
+
this.everSucceeded = true;
|
|
4775
|
+
this.cb.onActivity("online", "");
|
|
4776
|
+
this.pump();
|
|
4777
|
+
return;
|
|
4778
|
+
}
|
|
4779
|
+
const tail = errTail.join("").trim();
|
|
4780
|
+
const last = tail.split("\n").filter(Boolean).pop() || `opencode exited ${code ?? "signal"}`;
|
|
4781
|
+
this.cb.onTrajectory([{ kind: "text", text: "[opencode error] " + clip4(tail).slice(0, 500) }]);
|
|
4782
|
+
this.cb.onActivity("error", last.slice(0, 200));
|
|
4783
|
+
if (!this.everSucceeded) {
|
|
4784
|
+
this.cb.onExit(code ?? 1);
|
|
4785
|
+
return;
|
|
4786
|
+
}
|
|
4787
|
+
this.pump();
|
|
4788
|
+
});
|
|
4789
|
+
}
|
|
4790
|
+
stop() {
|
|
4791
|
+
this.stopped = true;
|
|
4792
|
+
const p = this.proc;
|
|
4793
|
+
this.proc = null;
|
|
4794
|
+
if (p) {
|
|
4795
|
+
try {
|
|
4796
|
+
p.kill("SIGTERM");
|
|
4797
|
+
} catch {
|
|
4798
|
+
}
|
|
4799
|
+
}
|
|
4800
|
+
}
|
|
4801
|
+
};
|
|
4802
|
+
var opencodeRuntime = {
|
|
4803
|
+
name: "opencode",
|
|
4804
|
+
experimental: true,
|
|
4805
|
+
start(opts, cb) {
|
|
4806
|
+
const run = new OpencodeRun(opts, cb);
|
|
4807
|
+
return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
|
|
4808
|
+
}
|
|
4809
|
+
};
|
|
4810
|
+
|
|
4811
|
+
// src/daemon/kimiRuntime.ts
|
|
4812
|
+
import { spawn as spawn5 } from "node:child_process";
|
|
4813
|
+
import { writeFileSync as writeFileSync4 } from "node:fs";
|
|
4814
|
+
import path7 from "node:path";
|
|
4815
|
+
var MAX5 = 2e3;
|
|
4816
|
+
var clip5 = (s) => String(s ?? "").slice(0, MAX5);
|
|
4817
|
+
function summarizeToolArgs2(argsJson) {
|
|
4818
|
+
let a = argsJson;
|
|
4819
|
+
if (typeof a === "string") {
|
|
4820
|
+
try {
|
|
4821
|
+
a = JSON.parse(a);
|
|
4822
|
+
} catch {
|
|
4823
|
+
return clip5(a).slice(0, 160);
|
|
4824
|
+
}
|
|
4825
|
+
}
|
|
4826
|
+
if (!a || typeof a !== "object") return "";
|
|
4827
|
+
const v = a.command ?? a.filePath ?? a.file_path ?? a.path ?? a.pattern ?? a.query ?? a.url ?? "";
|
|
4828
|
+
return clip5(typeof v === "string" ? v : JSON.stringify(v)).slice(0, 160);
|
|
4829
|
+
}
|
|
4830
|
+
function handleKimiEvent(evt) {
|
|
4831
|
+
const out = { trajectory: [] };
|
|
4832
|
+
if (evt?.role === "meta" && evt.type === "session.resume_hint" && typeof evt.session_id === "string") {
|
|
4833
|
+
out.sessionId = evt.session_id;
|
|
4834
|
+
return out;
|
|
4835
|
+
}
|
|
4836
|
+
if (evt?.role === "assistant") {
|
|
4837
|
+
if (typeof evt.content === "string" && evt.content) out.trajectory.push({ kind: "text", text: clip5(evt.content) });
|
|
4838
|
+
if (Array.isArray(evt.tool_calls))
|
|
4839
|
+
for (const tc of evt.tool_calls) {
|
|
4840
|
+
const fn = tc?.function ?? {};
|
|
4841
|
+
out.trajectory.push({ kind: "tool", toolName: String(fn.name ?? "tool"), toolInput: summarizeToolArgs2(fn.arguments) });
|
|
4842
|
+
}
|
|
4843
|
+
}
|
|
4844
|
+
return out;
|
|
4845
|
+
}
|
|
4846
|
+
function buildArgs3(prompt, model, sessionId) {
|
|
4847
|
+
const args2 = ["-p", prompt, "--output-format", "stream-json"];
|
|
4848
|
+
const m = model && model !== "default" ? model : "";
|
|
4849
|
+
if (m) args2.push("-m", m);
|
|
4850
|
+
if (sessionId) args2.push("-r", sessionId);
|
|
4851
|
+
return args2;
|
|
4852
|
+
}
|
|
4853
|
+
var KimiRun = class {
|
|
4854
|
+
constructor(opts, cb) {
|
|
4855
|
+
this.opts = opts;
|
|
4856
|
+
this.cb = cb;
|
|
4857
|
+
this.sessionId = opts.sessionId ?? null;
|
|
4858
|
+
this.env = { ...opts.env, PWD: opts.cwd };
|
|
4859
|
+
delete this.env.NODE_OPTIONS;
|
|
4860
|
+
try {
|
|
4861
|
+
writeFileSync4(path7.join(opts.cwd, "AGENTS.md"), opts.systemPrompt);
|
|
4862
|
+
} catch (e) {
|
|
4863
|
+
cb.log.warn("kimi: AGENTS.md write failed", { detail: String(e) });
|
|
4864
|
+
}
|
|
4865
|
+
if (this.sessionId) cb.onSession(this.sessionId);
|
|
4866
|
+
this.enqueue(opts.initialPrompt);
|
|
4867
|
+
}
|
|
4868
|
+
opts;
|
|
4869
|
+
cb;
|
|
4870
|
+
queue = [];
|
|
4871
|
+
turnBusy = false;
|
|
4872
|
+
stopped = false;
|
|
4873
|
+
proc = null;
|
|
4874
|
+
sessionId;
|
|
4875
|
+
everSucceeded = false;
|
|
4876
|
+
env;
|
|
4877
|
+
enqueue(text) {
|
|
4878
|
+
if (this.stopped) return;
|
|
4879
|
+
this.queue.push(text);
|
|
4880
|
+
this.pump();
|
|
4881
|
+
}
|
|
4882
|
+
pump() {
|
|
4883
|
+
if (this.stopped || this.turnBusy || this.queue.length === 0) return;
|
|
4884
|
+
this.runTurn(this.queue.shift());
|
|
4885
|
+
}
|
|
4886
|
+
runTurn(prompt) {
|
|
4887
|
+
this.turnBusy = true;
|
|
4888
|
+
this.cb.onActivity("working", "turn");
|
|
4889
|
+
const args2 = buildArgs3(prompt, this.opts.model, this.sessionId);
|
|
4890
|
+
const proc = spawn5("kimi", args2, { cwd: this.opts.cwd, stdio: ["ignore", "pipe", "pipe"], env: this.env });
|
|
4891
|
+
this.proc = proc;
|
|
4892
|
+
let buf = "";
|
|
4893
|
+
const errTail = [];
|
|
4894
|
+
let errLen = 0;
|
|
4895
|
+
const processLine = (ln) => {
|
|
4896
|
+
const t = ln.trim();
|
|
4897
|
+
if (!t) return;
|
|
4898
|
+
let evt;
|
|
4899
|
+
try {
|
|
4900
|
+
evt = JSON.parse(t);
|
|
4901
|
+
} catch {
|
|
4902
|
+
return;
|
|
4903
|
+
}
|
|
4904
|
+
const emit = handleKimiEvent(evt);
|
|
4905
|
+
if (emit.sessionId && emit.sessionId !== this.sessionId) {
|
|
4906
|
+
this.sessionId = emit.sessionId;
|
|
4907
|
+
this.cb.onSession(emit.sessionId);
|
|
4908
|
+
}
|
|
4909
|
+
if (emit.activity) this.cb.onActivity(emit.activity.activity, emit.activity.detail);
|
|
4910
|
+
if (emit.trajectory.length) this.cb.onTrajectory(emit.trajectory);
|
|
4911
|
+
};
|
|
4912
|
+
proc.stdout?.on("data", (c) => {
|
|
4913
|
+
if (this.stopped) return;
|
|
4914
|
+
buf += c.toString();
|
|
4915
|
+
const lines = buf.split("\n");
|
|
4916
|
+
buf = lines.pop() ?? "";
|
|
4917
|
+
for (const ln of lines) processLine(ln);
|
|
4918
|
+
});
|
|
4919
|
+
proc.stderr?.on("data", (c) => {
|
|
4920
|
+
const t = c.toString();
|
|
4921
|
+
errTail.push(t);
|
|
4922
|
+
errLen += t.length;
|
|
4923
|
+
while (errLen > 4096 && errTail.length > 1) errLen -= errTail.shift().length;
|
|
4924
|
+
});
|
|
4925
|
+
proc.on("error", (e) => {
|
|
4926
|
+
this.proc = null;
|
|
4927
|
+
this.turnBusy = false;
|
|
4928
|
+
if (this.stopped) return;
|
|
4929
|
+
this.cb.log.error("kimi spawn failed", { detail: String(e?.message ?? e) });
|
|
4930
|
+
this.cb.onActivity("offline", "kimi not found");
|
|
4931
|
+
if (!this.everSucceeded) this.cb.onExit(1);
|
|
4932
|
+
else this.pump();
|
|
4933
|
+
});
|
|
4934
|
+
proc.on("exit", (code) => {
|
|
4935
|
+
if (buf.trim()) processLine(buf);
|
|
4936
|
+
buf = "";
|
|
4937
|
+
this.proc = null;
|
|
4938
|
+
this.turnBusy = false;
|
|
4939
|
+
if (this.stopped) return;
|
|
4940
|
+
if (code === 0) {
|
|
4941
|
+
this.everSucceeded = true;
|
|
4942
|
+
this.cb.onActivity("online", "");
|
|
4943
|
+
this.pump();
|
|
4944
|
+
return;
|
|
4945
|
+
}
|
|
4946
|
+
const tail = errTail.join("").trim();
|
|
4947
|
+
if (/not found|no session|session .* not/i.test(tail)) this.sessionId = null;
|
|
4948
|
+
const last = tail.split("\n").filter(Boolean).pop() || `kimi exited ${code ?? "signal"}`;
|
|
4949
|
+
this.cb.onTrajectory([{ kind: "text", text: "[kimi error] " + clip5(tail).slice(0, 500) }]);
|
|
4950
|
+
this.cb.onActivity("error", last.slice(0, 200));
|
|
4951
|
+
if (!this.everSucceeded) {
|
|
4952
|
+
this.cb.onExit(code ?? 1);
|
|
4953
|
+
return;
|
|
4954
|
+
}
|
|
4955
|
+
this.pump();
|
|
4956
|
+
});
|
|
4957
|
+
}
|
|
4958
|
+
stop() {
|
|
4959
|
+
this.stopped = true;
|
|
4960
|
+
const p = this.proc;
|
|
4961
|
+
this.proc = null;
|
|
4962
|
+
if (p) {
|
|
4963
|
+
try {
|
|
4964
|
+
p.kill("SIGTERM");
|
|
4965
|
+
} catch {
|
|
4966
|
+
}
|
|
4967
|
+
}
|
|
4968
|
+
}
|
|
4969
|
+
};
|
|
4970
|
+
var kimiRuntime = {
|
|
4971
|
+
name: "kimi",
|
|
4972
|
+
experimental: true,
|
|
4973
|
+
start(opts, cb) {
|
|
4974
|
+
const run = new KimiRun(opts, cb);
|
|
4975
|
+
return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
|
|
4976
|
+
}
|
|
4977
|
+
};
|
|
4978
|
+
|
|
4979
|
+
// src/daemon/piRuntime.ts
|
|
4980
|
+
import { spawn as spawn6 } from "node:child_process";
|
|
4981
|
+
import { writeFileSync as writeFileSync5 } from "node:fs";
|
|
4982
|
+
import path8 from "node:path";
|
|
4983
|
+
var MAX6 = 2e3;
|
|
4984
|
+
var clip6 = (s) => String(s ?? "").slice(0, MAX6);
|
|
4985
|
+
function summarizeToolArgs3(args2) {
|
|
4986
|
+
if (!args2 || typeof args2 !== "object") return clip6(args2).slice(0, 160);
|
|
4987
|
+
const v = args2.command ?? args2.filePath ?? args2.file_path ?? args2.path ?? args2.pattern ?? args2.query ?? args2.url ?? "";
|
|
4988
|
+
return clip6(typeof v === "string" ? v : JSON.stringify(v)).slice(0, 160);
|
|
4989
|
+
}
|
|
4990
|
+
function handlePiEvent(evt) {
|
|
4991
|
+
const out = { trajectory: [] };
|
|
4992
|
+
if (evt?.type === "session" && typeof evt.id === "string") {
|
|
4993
|
+
out.sessionId = evt.id;
|
|
4994
|
+
return out;
|
|
4995
|
+
}
|
|
4996
|
+
if (evt?.type === "message_end" && evt.message?.role === "assistant") {
|
|
4997
|
+
const m = evt.message;
|
|
4998
|
+
if (Array.isArray(m.content))
|
|
4999
|
+
for (const b of m.content) {
|
|
5000
|
+
if (b?.type === "text" && b.text) out.trajectory.push({ kind: "text", text: clip6(b.text) });
|
|
5001
|
+
else if (b?.type === "toolCall") out.trajectory.push({ kind: "tool", toolName: String(b.name ?? "tool"), toolInput: summarizeToolArgs3(b.arguments) });
|
|
5002
|
+
}
|
|
5003
|
+
if (m.stopReason === "error") out.error = String(m.errorMessage ?? "pi model error (no message)");
|
|
5004
|
+
}
|
|
5005
|
+
return out;
|
|
5006
|
+
}
|
|
5007
|
+
function buildArgs4(prompt, model, sessionId, cwd, promptFile) {
|
|
5008
|
+
const args2 = ["-p", prompt, "--mode", "json", "--append-system-prompt", promptFile, "--session-dir", cwd];
|
|
5009
|
+
const m = model && model !== "default" ? model : "";
|
|
5010
|
+
if (m) args2.push("--model", m);
|
|
5011
|
+
if (sessionId) args2.push("--session", sessionId);
|
|
5012
|
+
return args2;
|
|
5013
|
+
}
|
|
5014
|
+
var PiRun = class {
|
|
5015
|
+
constructor(opts, cb) {
|
|
5016
|
+
this.opts = opts;
|
|
5017
|
+
this.cb = cb;
|
|
5018
|
+
this.sessionId = opts.sessionId ?? null;
|
|
5019
|
+
this.promptFile = path8.join(opts.cwd, ".pi-system-prompt.md");
|
|
5020
|
+
this.env = { ...opts.env };
|
|
5021
|
+
delete this.env.NODE_OPTIONS;
|
|
5022
|
+
try {
|
|
5023
|
+
writeFileSync5(this.promptFile, opts.systemPrompt);
|
|
5024
|
+
} catch (e) {
|
|
5025
|
+
this.promptWriteFailed = true;
|
|
5026
|
+
cb.log.warn("pi: system-prompt write failed", { detail: String(e) });
|
|
5027
|
+
}
|
|
5028
|
+
if (this.sessionId) cb.onSession(this.sessionId);
|
|
5029
|
+
this.enqueue(opts.initialPrompt);
|
|
5030
|
+
}
|
|
5031
|
+
opts;
|
|
5032
|
+
cb;
|
|
5033
|
+
queue = [];
|
|
5034
|
+
turnBusy = false;
|
|
5035
|
+
stopped = false;
|
|
5036
|
+
proc = null;
|
|
5037
|
+
sessionId;
|
|
5038
|
+
everSucceeded = false;
|
|
5039
|
+
promptFile;
|
|
5040
|
+
env;
|
|
5041
|
+
promptWriteFailed = false;
|
|
5042
|
+
enqueue(text) {
|
|
5043
|
+
if (this.stopped) return;
|
|
5044
|
+
this.queue.push(text);
|
|
5045
|
+
this.pump();
|
|
5046
|
+
}
|
|
5047
|
+
pump() {
|
|
5048
|
+
if (this.stopped || this.turnBusy || this.queue.length === 0) return;
|
|
5049
|
+
this.runTurn(this.queue.shift());
|
|
5050
|
+
}
|
|
5051
|
+
runTurn(prompt) {
|
|
5052
|
+
this.turnBusy = true;
|
|
5053
|
+
if (this.promptWriteFailed) {
|
|
5054
|
+
this.cb.onTrajectory([{ kind: "text", text: "[pi error] system-prompt write failed \u2014 check cwd permissions" }]);
|
|
5055
|
+
this.cb.onActivity("error", "pi: system-prompt write failed");
|
|
5056
|
+
this.turnBusy = false;
|
|
5057
|
+
if (!this.everSucceeded) {
|
|
5058
|
+
this.cb.onExit(1);
|
|
5059
|
+
return;
|
|
5060
|
+
}
|
|
5061
|
+
return;
|
|
5062
|
+
}
|
|
5063
|
+
this.cb.onActivity("working", "turn");
|
|
5064
|
+
const args2 = buildArgs4(prompt, this.opts.model, this.sessionId, this.opts.cwd, this.promptFile);
|
|
5065
|
+
const proc = spawn6("pi", args2, { cwd: this.opts.cwd, stdio: ["ignore", "pipe", "pipe"], env: this.env });
|
|
5066
|
+
this.proc = proc;
|
|
5067
|
+
let buf = "";
|
|
5068
|
+
const errTail = [];
|
|
5069
|
+
let errLen = 0;
|
|
5070
|
+
const processLine = (ln) => {
|
|
5071
|
+
const t = ln.trim();
|
|
5072
|
+
if (!t) return;
|
|
5073
|
+
let evt;
|
|
5074
|
+
try {
|
|
5075
|
+
evt = JSON.parse(t);
|
|
5076
|
+
} catch {
|
|
5077
|
+
return;
|
|
5078
|
+
}
|
|
5079
|
+
const emit = handlePiEvent(evt);
|
|
5080
|
+
if (emit.sessionId && emit.sessionId !== this.sessionId) {
|
|
5081
|
+
this.sessionId = emit.sessionId;
|
|
5082
|
+
this.cb.onSession(emit.sessionId);
|
|
5083
|
+
}
|
|
5084
|
+
if (emit.trajectory.length) this.cb.onTrajectory(emit.trajectory);
|
|
5085
|
+
if (emit.error) {
|
|
5086
|
+
this.cb.onTrajectory([{ kind: "text", text: "[pi error] " + clip6(emit.error).slice(0, 500) }]);
|
|
5087
|
+
this.cb.onActivity("error", emit.error.slice(0, 200));
|
|
5088
|
+
}
|
|
5089
|
+
};
|
|
5090
|
+
proc.stdout?.on("data", (c) => {
|
|
5091
|
+
if (this.stopped) return;
|
|
5092
|
+
buf += c.toString();
|
|
5093
|
+
const lines = buf.split("\n");
|
|
5094
|
+
buf = lines.pop() ?? "";
|
|
5095
|
+
for (const ln of lines) processLine(ln);
|
|
5096
|
+
});
|
|
5097
|
+
proc.stderr?.on("data", (c) => {
|
|
5098
|
+
const t = c.toString();
|
|
5099
|
+
errTail.push(t);
|
|
5100
|
+
errLen += t.length;
|
|
5101
|
+
while (errLen > 4096 && errTail.length > 1) errLen -= errTail.shift().length;
|
|
5102
|
+
});
|
|
5103
|
+
proc.on("error", (e) => {
|
|
5104
|
+
this.proc = null;
|
|
5105
|
+
this.turnBusy = false;
|
|
5106
|
+
if (this.stopped) return;
|
|
5107
|
+
this.cb.log.error("pi spawn failed", { detail: String(e?.message ?? e) });
|
|
5108
|
+
this.cb.onActivity("offline", "pi not found");
|
|
5109
|
+
if (!this.everSucceeded) this.cb.onExit(1);
|
|
5110
|
+
else this.pump();
|
|
5111
|
+
});
|
|
5112
|
+
proc.on("exit", (code) => {
|
|
5113
|
+
if (buf.trim()) processLine(buf);
|
|
5114
|
+
buf = "";
|
|
5115
|
+
this.proc = null;
|
|
5116
|
+
this.turnBusy = false;
|
|
5117
|
+
if (this.stopped) return;
|
|
5118
|
+
if (code === 0) {
|
|
5119
|
+
this.everSucceeded = true;
|
|
5120
|
+
this.cb.onActivity("online", "");
|
|
5121
|
+
this.pump();
|
|
5122
|
+
return;
|
|
5123
|
+
}
|
|
5124
|
+
const tail = errTail.join("").trim();
|
|
5125
|
+
const last = tail.split("\n").filter(Boolean).pop() || `pi exited ${code ?? "signal"}`;
|
|
5126
|
+
this.cb.onTrajectory([{ kind: "text", text: "[pi error] " + clip6(tail).slice(0, 500) }]);
|
|
5127
|
+
this.cb.onActivity("error", last.slice(0, 200));
|
|
5128
|
+
if (!this.everSucceeded) {
|
|
5129
|
+
this.cb.onExit(code ?? 1);
|
|
5130
|
+
return;
|
|
5131
|
+
}
|
|
5132
|
+
this.pump();
|
|
5133
|
+
});
|
|
5134
|
+
}
|
|
5135
|
+
stop() {
|
|
5136
|
+
this.stopped = true;
|
|
5137
|
+
const p = this.proc;
|
|
5138
|
+
this.proc = null;
|
|
5139
|
+
if (p) {
|
|
5140
|
+
try {
|
|
5141
|
+
p.kill("SIGTERM");
|
|
5142
|
+
} catch {
|
|
5143
|
+
}
|
|
5144
|
+
}
|
|
5145
|
+
}
|
|
5146
|
+
};
|
|
5147
|
+
var piRuntime = {
|
|
5148
|
+
name: "pi",
|
|
5149
|
+
experimental: true,
|
|
5150
|
+
start(opts, cb) {
|
|
5151
|
+
const run = new PiRun(opts, cb);
|
|
5152
|
+
return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
|
|
5153
|
+
}
|
|
5154
|
+
};
|
|
5155
|
+
|
|
5156
|
+
// src/daemon/cursorRuntime.ts
|
|
5157
|
+
import { spawn as spawn7 } from "node:child_process";
|
|
5158
|
+
import { writeFileSync as writeFileSync6 } from "node:fs";
|
|
5159
|
+
import path9 from "node:path";
|
|
5160
|
+
var MAX7 = 2e3;
|
|
5161
|
+
var clip7 = (s) => String(s ?? "").slice(0, MAX7);
|
|
5162
|
+
function summarizeToolArgs4(args2) {
|
|
5163
|
+
if (!args2 || typeof args2 !== "object") return clip7(args2).slice(0, 160);
|
|
5164
|
+
const v = args2.command ?? args2.filePath ?? args2.file_path ?? args2.path ?? args2.pattern ?? args2.query ?? args2.url ?? "";
|
|
5165
|
+
return clip7(typeof v === "string" ? v : JSON.stringify(v)).slice(0, 160);
|
|
5166
|
+
}
|
|
5167
|
+
function handleCursorEvent(evt) {
|
|
5168
|
+
const out = { trajectory: [] };
|
|
5169
|
+
if (typeof evt?.session_id === "string" && evt.session_id) out.sessionId = evt.session_id;
|
|
5170
|
+
if (evt?.type === "tool_call" && evt.subtype === "started") {
|
|
5171
|
+
const tc = evt.tool_call;
|
|
5172
|
+
if (tc && typeof tc === "object") {
|
|
5173
|
+
const key = Object.keys(tc).find((k) => k.endsWith("ToolCall")) ?? Object.keys(tc)[0] ?? "tool";
|
|
5174
|
+
const name = key.replace(/ToolCall$/, "") || "tool";
|
|
5175
|
+
out.trajectory.push({ kind: "tool", toolName: name, toolInput: summarizeToolArgs4(tc[key]?.args) });
|
|
5176
|
+
}
|
|
5177
|
+
} else if (evt?.type === "result") {
|
|
5178
|
+
out.resultSeen = true;
|
|
5179
|
+
if (evt.is_error) out.error = clip7(evt.result) || "cursor reported an error";
|
|
5180
|
+
else if (evt.result != null) out.trajectory.push({ kind: "text", text: clip7(evt.result) });
|
|
5181
|
+
} else if (evt?.type === "system" && evt?.subtype === "error") {
|
|
5182
|
+
out.error = clip7(evt.error ?? evt.message ?? "cursor system error");
|
|
5183
|
+
}
|
|
5184
|
+
return out;
|
|
5185
|
+
}
|
|
5186
|
+
function buildArgs5(prompt, model, sessionId) {
|
|
5187
|
+
const args2 = ["-p", prompt, "--output-format", "stream-json", "-f"];
|
|
5188
|
+
const m = model && model !== "default" ? model : "";
|
|
5189
|
+
if (m) args2.push("--model", m);
|
|
5190
|
+
if (sessionId) args2.push("--resume", sessionId);
|
|
5191
|
+
return args2;
|
|
5192
|
+
}
|
|
5193
|
+
var CursorRun = class {
|
|
5194
|
+
constructor(opts, cb) {
|
|
5195
|
+
this.opts = opts;
|
|
5196
|
+
this.cb = cb;
|
|
5197
|
+
this.sessionId = opts.sessionId ?? null;
|
|
5198
|
+
this.env = { ...opts.env };
|
|
5199
|
+
delete this.env.NODE_OPTIONS;
|
|
5200
|
+
try {
|
|
5201
|
+
writeFileSync6(path9.join(opts.cwd, "AGENTS.md"), opts.systemPrompt);
|
|
5202
|
+
} catch (e) {
|
|
5203
|
+
cb.log.warn("cursor: AGENTS.md write failed", { detail: String(e) });
|
|
5204
|
+
}
|
|
5205
|
+
if (this.sessionId) cb.onSession(this.sessionId);
|
|
5206
|
+
this.enqueue(opts.initialPrompt);
|
|
5207
|
+
}
|
|
5208
|
+
opts;
|
|
5209
|
+
cb;
|
|
5210
|
+
queue = [];
|
|
5211
|
+
turnBusy = false;
|
|
5212
|
+
stopped = false;
|
|
5213
|
+
proc = null;
|
|
5214
|
+
sessionId;
|
|
5215
|
+
everSucceeded = false;
|
|
5216
|
+
env;
|
|
5217
|
+
enqueue(text) {
|
|
5218
|
+
if (this.stopped) return;
|
|
5219
|
+
this.queue.push(text);
|
|
5220
|
+
this.pump();
|
|
5221
|
+
}
|
|
5222
|
+
pump() {
|
|
5223
|
+
if (this.stopped || this.turnBusy || this.queue.length === 0) return;
|
|
5224
|
+
this.runTurn(this.queue.shift());
|
|
5225
|
+
}
|
|
5226
|
+
runTurn(prompt) {
|
|
5227
|
+
this.turnBusy = true;
|
|
5228
|
+
this.cb.onActivity("working", "turn");
|
|
5229
|
+
const args2 = buildArgs5(prompt, this.opts.model, this.sessionId);
|
|
5230
|
+
const proc = spawn7("cursor-agent", args2, { cwd: this.opts.cwd, stdio: ["ignore", "pipe", "pipe"], env: this.env });
|
|
5231
|
+
this.proc = proc;
|
|
5232
|
+
let buf = "";
|
|
5233
|
+
let resultSeen = false;
|
|
5234
|
+
let resultError = false;
|
|
5235
|
+
const errTail = [];
|
|
5236
|
+
let errLen = 0;
|
|
5237
|
+
const processLine = (ln) => {
|
|
5238
|
+
const t = ln.trim();
|
|
5239
|
+
if (!t) return;
|
|
5240
|
+
let evt;
|
|
5241
|
+
try {
|
|
5242
|
+
evt = JSON.parse(t);
|
|
5243
|
+
} catch {
|
|
5244
|
+
return;
|
|
5245
|
+
}
|
|
5246
|
+
const emit = handleCursorEvent(evt);
|
|
5247
|
+
if (emit.sessionId && emit.sessionId !== this.sessionId) {
|
|
5248
|
+
this.sessionId = emit.sessionId;
|
|
5249
|
+
this.cb.onSession(emit.sessionId);
|
|
5250
|
+
}
|
|
5251
|
+
if (emit.resultSeen) resultSeen = true;
|
|
5252
|
+
if (emit.error) {
|
|
5253
|
+
resultError = true;
|
|
5254
|
+
this.cb.onTrajectory([{ kind: "text", text: "[cursor error] " + clip7(emit.error).slice(0, 500) }]);
|
|
5255
|
+
this.cb.onActivity("error", emit.error.slice(0, 200));
|
|
5256
|
+
}
|
|
5257
|
+
if (emit.trajectory.length) this.cb.onTrajectory(emit.trajectory);
|
|
5258
|
+
};
|
|
5259
|
+
proc.stdout?.on("data", (c) => {
|
|
5260
|
+
if (this.stopped) return;
|
|
5261
|
+
buf += c.toString();
|
|
5262
|
+
const lines = buf.split("\n");
|
|
5263
|
+
buf = lines.pop() ?? "";
|
|
5264
|
+
for (const ln of lines) processLine(ln);
|
|
5265
|
+
});
|
|
5266
|
+
proc.stderr?.on("data", (c) => {
|
|
5267
|
+
const t = c.toString();
|
|
5268
|
+
errTail.push(t);
|
|
5269
|
+
errLen += t.length;
|
|
5270
|
+
while (errLen > 4096 && errTail.length > 1) errLen -= errTail.shift().length;
|
|
5271
|
+
});
|
|
5272
|
+
proc.on("error", (e) => {
|
|
5273
|
+
this.proc = null;
|
|
5274
|
+
this.turnBusy = false;
|
|
5275
|
+
if (this.stopped) return;
|
|
5276
|
+
this.cb.log.error("cursor spawn failed", { detail: String(e?.message ?? e) });
|
|
5277
|
+
this.cb.onActivity("offline", "cursor-agent not found");
|
|
5278
|
+
if (!this.everSucceeded) this.cb.onExit(1);
|
|
5279
|
+
else this.pump();
|
|
5280
|
+
});
|
|
5281
|
+
proc.on("exit", (code) => {
|
|
5282
|
+
if (buf.trim()) processLine(buf);
|
|
5283
|
+
buf = "";
|
|
5284
|
+
this.proc = null;
|
|
5285
|
+
this.turnBusy = false;
|
|
5286
|
+
if (this.stopped) return;
|
|
5287
|
+
if (code === 0) {
|
|
5288
|
+
if (!resultError) {
|
|
5289
|
+
this.everSucceeded = true;
|
|
5290
|
+
this.cb.onActivity("online", "");
|
|
5291
|
+
} else if (!this.everSucceeded) {
|
|
5292
|
+
this.cb.onExit(1);
|
|
5293
|
+
return;
|
|
5294
|
+
}
|
|
5295
|
+
this.pump();
|
|
5296
|
+
return;
|
|
5297
|
+
}
|
|
5298
|
+
if (!resultError) {
|
|
5299
|
+
const tail = errTail.join("").trim();
|
|
5300
|
+
const last = tail.split("\n").filter(Boolean).pop() || `cursor-agent exited ${code ?? "signal"}`;
|
|
5301
|
+
this.cb.onTrajectory([{ kind: "text", text: "[cursor error] " + clip7(tail).slice(0, 500) }]);
|
|
5302
|
+
this.cb.onActivity("error", last.slice(0, 200));
|
|
5303
|
+
}
|
|
5304
|
+
if (!this.everSucceeded) {
|
|
5305
|
+
this.cb.onExit(code ?? 1);
|
|
5306
|
+
return;
|
|
5307
|
+
}
|
|
5308
|
+
this.pump();
|
|
5309
|
+
});
|
|
5310
|
+
}
|
|
5311
|
+
stop() {
|
|
5312
|
+
this.stopped = true;
|
|
5313
|
+
const p = this.proc;
|
|
5314
|
+
this.proc = null;
|
|
5315
|
+
if (p) {
|
|
5316
|
+
try {
|
|
5317
|
+
p.kill("SIGTERM");
|
|
5318
|
+
} catch {
|
|
5319
|
+
}
|
|
5320
|
+
}
|
|
5321
|
+
}
|
|
5322
|
+
};
|
|
5323
|
+
var cursorRuntime = {
|
|
5324
|
+
name: "cursor",
|
|
5325
|
+
experimental: true,
|
|
5326
|
+
start(opts, cb) {
|
|
5327
|
+
const run = new CursorRun(opts, cb);
|
|
5328
|
+
return { deliver: (text) => run.enqueue(text), stop: () => run.stop() };
|
|
5329
|
+
}
|
|
5330
|
+
};
|
|
5331
|
+
|
|
4424
5332
|
// src/daemon/runtimes.ts
|
|
4425
5333
|
function has(tool) {
|
|
4426
5334
|
try {
|
|
@@ -4431,10 +5339,10 @@ function has(tool) {
|
|
|
4431
5339
|
}
|
|
4432
5340
|
}
|
|
4433
5341
|
function detectRuntimes() {
|
|
4434
|
-
const found = ["claude", "codex", "
|
|
5342
|
+
const found = ["claude", "codex", "copilot", "kimi", "opencode", "pi", "cursor-agent"].filter(has).map((t) => t === "cursor-agent" ? "cursor" : t);
|
|
4435
5343
|
return found;
|
|
4436
5344
|
}
|
|
4437
|
-
var REG = { claude: claudeRuntime, codex: codexRuntime };
|
|
5345
|
+
var REG = { claude: claudeRuntime, codex: codexRuntime, copilot: copilotRuntime, opencode: opencodeRuntime, kimi: kimiRuntime, pi: piRuntime, cursor: cursorRuntime };
|
|
4438
5346
|
function getRuntime(name) {
|
|
4439
5347
|
return REG[name] ?? null;
|
|
4440
5348
|
}
|
|
@@ -4485,7 +5393,7 @@ var AgentManager = class {
|
|
|
4485
5393
|
async reset(agentId, wipeWorkspace = false, clearMemory = false) {
|
|
4486
5394
|
this.teardown(agentId);
|
|
4487
5395
|
this.send({ type: "agent:session", agentId, sessionId: null });
|
|
4488
|
-
const dir =
|
|
5396
|
+
const dir = path10.join(DATA_DIR, agentId);
|
|
4489
5397
|
if (wipeWorkspace) {
|
|
4490
5398
|
try {
|
|
4491
5399
|
await rm(dir, { recursive: true, force: true });
|
|
@@ -4495,7 +5403,7 @@ var AgentManager = class {
|
|
|
4495
5403
|
}
|
|
4496
5404
|
} else if (clearMemory) {
|
|
4497
5405
|
try {
|
|
4498
|
-
await writeFile(
|
|
5406
|
+
await writeFile(path10.join(dir, "MEMORY.md"), "# Memory\n\n(reset)\n");
|
|
4499
5407
|
this.log.info("memory cleared", { agentId });
|
|
4500
5408
|
} catch (e) {
|
|
4501
5409
|
this.log.warn("clearMemory failed", { agentId, detail: String(e) });
|
|
@@ -4509,7 +5417,7 @@ var AgentManager = class {
|
|
|
4509
5417
|
* title + `## Role`, preserving the agent's own sections. No-op if the workspace/file doesn't exist
|
|
4510
5418
|
* yet (a not-yet-started agent gets fresh values from the DB when start() seeds it). */
|
|
4511
5419
|
async syncProfile(agentId, displayName, description) {
|
|
4512
|
-
const mem =
|
|
5420
|
+
const mem = path10.join(DATA_DIR, agentId, "MEMORY.md");
|
|
4513
5421
|
let content;
|
|
4514
5422
|
try {
|
|
4515
5423
|
content = await readFile(mem, "utf8");
|
|
@@ -4551,9 +5459,9 @@ var AgentManager = class {
|
|
|
4551
5459
|
return;
|
|
4552
5460
|
}
|
|
4553
5461
|
if (runtime.experimental) this.log.warn("experimental runtime", { runtime: runtime.name });
|
|
4554
|
-
const dir =
|
|
4555
|
-
await mkdir(
|
|
4556
|
-
const mem =
|
|
5462
|
+
const dir = path10.join(DATA_DIR, agentId);
|
|
5463
|
+
await mkdir(path10.join(dir, "notes"), { recursive: true });
|
|
5464
|
+
const mem = path10.join(dir, "MEMORY.md");
|
|
4557
5465
|
try {
|
|
4558
5466
|
await access(mem);
|
|
4559
5467
|
} catch {
|
|
@@ -4654,22 +5562,22 @@ var AgentManager = class {
|
|
|
4654
5562
|
|
|
4655
5563
|
// src/daemon/workspace.ts
|
|
4656
5564
|
import { readdir, readFile as readFile2, stat } from "node:fs/promises";
|
|
4657
|
-
import
|
|
5565
|
+
import path11 from "node:path";
|
|
4658
5566
|
import os3 from "node:os";
|
|
4659
5567
|
var DATA_DIR2 = agentsDir();
|
|
4660
5568
|
var MAX_FILE = 256 * 1024;
|
|
4661
5569
|
var SKIP = /* @__PURE__ */ new Set(["node_modules", ".git"]);
|
|
4662
5570
|
function safe(agentId, rel) {
|
|
4663
|
-
const root =
|
|
4664
|
-
const target =
|
|
4665
|
-
if (target !== root && !target.startsWith(root +
|
|
5571
|
+
const root = path11.join(DATA_DIR2, agentId);
|
|
5572
|
+
const target = path11.resolve(root, rel || ".");
|
|
5573
|
+
if (target !== root && !target.startsWith(root + path11.sep)) return null;
|
|
4666
5574
|
return target;
|
|
4667
5575
|
}
|
|
4668
5576
|
async function walk(root, rel, acc, depth) {
|
|
4669
5577
|
if (depth > 6 || acc.length > 2e3) return;
|
|
4670
5578
|
let ds;
|
|
4671
5579
|
try {
|
|
4672
|
-
ds = await readdir(
|
|
5580
|
+
ds = await readdir(path11.join(root, rel), { withFileTypes: true });
|
|
4673
5581
|
} catch {
|
|
4674
5582
|
return;
|
|
4675
5583
|
}
|
|
@@ -4679,7 +5587,7 @@ async function walk(root, rel, acc, depth) {
|
|
|
4679
5587
|
let size = 0;
|
|
4680
5588
|
let modifiedAt = null;
|
|
4681
5589
|
try {
|
|
4682
|
-
const s = await stat(
|
|
5590
|
+
const s = await stat(path11.join(root, childRel));
|
|
4683
5591
|
size = d.isFile() ? s.size : 0;
|
|
4684
5592
|
modifiedAt = s.mtime.toISOString();
|
|
4685
5593
|
} catch {
|
|
@@ -4689,7 +5597,7 @@ async function walk(root, rel, acc, depth) {
|
|
|
4689
5597
|
}
|
|
4690
5598
|
}
|
|
4691
5599
|
async function listWorkspace(agentId, _subPath = "") {
|
|
4692
|
-
const root =
|
|
5600
|
+
const root = path11.join(DATA_DIR2, agentId);
|
|
4693
5601
|
try {
|
|
4694
5602
|
const files = [];
|
|
4695
5603
|
await walk(root, "", files, 0);
|
|
@@ -4727,7 +5635,7 @@ async function readSkillsDir(dir, sourcePath) {
|
|
|
4727
5635
|
if (!e.isDirectory()) continue;
|
|
4728
5636
|
let name = e.name, description = "", userInvocable = false;
|
|
4729
5637
|
try {
|
|
4730
|
-
const txt = await readFile2(
|
|
5638
|
+
const txt = await readFile2(path11.join(dir, e.name, "SKILL.md"), "utf8");
|
|
4731
5639
|
const fm = /^---\n([\s\S]*?)\n---/.exec(txt);
|
|
4732
5640
|
if (fm) {
|
|
4733
5641
|
name = fmField(fm[1], "name") || e.name;
|
|
@@ -4742,8 +5650,8 @@ async function readSkillsDir(dir, sourcePath) {
|
|
|
4742
5650
|
return out;
|
|
4743
5651
|
}
|
|
4744
5652
|
async function listSkills(agentId) {
|
|
4745
|
-
const global = await readSkillsDir(
|
|
4746
|
-
const workspace = await readSkillsDir(
|
|
5653
|
+
const global = await readSkillsDir(path11.join(os3.homedir(), ".claude", "skills"), "~/.claude/skills");
|
|
5654
|
+
const workspace = await readSkillsDir(path11.join(DATA_DIR2, agentId, ".claude", "skills"), "<workspace>/.claude/skills");
|
|
4747
5655
|
return { global, workspace };
|
|
4748
5656
|
}
|
|
4749
5657
|
async function readWorkspaceFile(agentId, rel) {
|
|
@@ -4761,6 +5669,190 @@ async function readWorkspaceFile(agentId, rel) {
|
|
|
4761
5669
|
}
|
|
4762
5670
|
}
|
|
4763
5671
|
|
|
5672
|
+
// src/daemon/listModels.ts
|
|
5673
|
+
import { spawn as spawn8 } from "node:child_process";
|
|
5674
|
+
var titleCase = (s) => s ? s[0].toUpperCase() + s.slice(1) : s;
|
|
5675
|
+
function isModelId(s) {
|
|
5676
|
+
return /^[A-Za-z][A-Za-z0-9\-_./]*$/.test(s);
|
|
5677
|
+
}
|
|
5678
|
+
var CLAUDE_MODELS = [
|
|
5679
|
+
{ id: "sonnet", label: "Sonnet" },
|
|
5680
|
+
{ id: "opus", label: "Opus" },
|
|
5681
|
+
{ id: "haiku", label: "Haiku" }
|
|
5682
|
+
];
|
|
5683
|
+
var CLAUDE_EFFORT_LABEL = {
|
|
5684
|
+
low: "Low",
|
|
5685
|
+
medium: "Medium",
|
|
5686
|
+
high: "High",
|
|
5687
|
+
xhigh: "Extra high",
|
|
5688
|
+
max: "Max"
|
|
5689
|
+
};
|
|
5690
|
+
var CLAUDE_MODEL_EFFORT_ALLOW = {
|
|
5691
|
+
opus: /* @__PURE__ */ new Set(["low", "medium", "high", "xhigh", "max"]),
|
|
5692
|
+
sonnet: /* @__PURE__ */ new Set(["low", "medium", "high", "max"]),
|
|
5693
|
+
haiku: /* @__PURE__ */ new Set(["low", "medium", "high"])
|
|
5694
|
+
};
|
|
5695
|
+
function parseClaudeEffortLevels(helpText) {
|
|
5696
|
+
const m = /--effort\s*(?:<[^>]+>)?\s*(?:Effort level[^(]*)?\(([^)]+)\)/.exec(helpText);
|
|
5697
|
+
if (!m) return [];
|
|
5698
|
+
return m[1].split(",").map((s) => s.trim()).filter((s) => /^[a-z]+$/i.test(s));
|
|
5699
|
+
}
|
|
5700
|
+
function claudeThinkingForModel(modelId, superset) {
|
|
5701
|
+
const allow = CLAUDE_MODEL_EFFORT_ALLOW[modelId];
|
|
5702
|
+
const levels = superset.filter((v) => !allow || allow.has(v)).map((v) => ({ value: v, label: CLAUDE_EFFORT_LABEL[v] ?? titleCase(v) }));
|
|
5703
|
+
if (!levels.length) return void 0;
|
|
5704
|
+
return { levels, default: levels.some((l) => l.value === "medium") ? "medium" : levels[0].value };
|
|
5705
|
+
}
|
|
5706
|
+
function parseCodexModels(jsonStr) {
|
|
5707
|
+
let parsed;
|
|
5708
|
+
try {
|
|
5709
|
+
parsed = JSON.parse(jsonStr);
|
|
5710
|
+
} catch {
|
|
5711
|
+
return [];
|
|
5712
|
+
}
|
|
5713
|
+
const models = Array.isArray(parsed?.models) ? parsed.models : [];
|
|
5714
|
+
const out = [];
|
|
5715
|
+
for (const m of models) {
|
|
5716
|
+
if (m?.visibility !== "list") continue;
|
|
5717
|
+
const slug = typeof m?.slug === "string" ? m.slug : "";
|
|
5718
|
+
if (!slug) continue;
|
|
5719
|
+
const raw = Array.isArray(m?.supported_reasoning_levels) ? m.supported_reasoning_levels : [];
|
|
5720
|
+
const levels = raw.map((l) => ({ value: String(l?.effort ?? ""), label: titleCase(String(l?.effort ?? "")), description: typeof l?.description === "string" ? l.description : void 0 })).filter((l) => l.value);
|
|
5721
|
+
const thinking = levels.length ? { levels, default: typeof m?.default_reasoning_level === "string" ? m.default_reasoning_level : void 0 } : void 0;
|
|
5722
|
+
out.push({ id: slug, label: typeof m?.display_name === "string" && m.display_name ? m.display_name : slug, provider: "openai", ...thinking ? { thinking } : {} });
|
|
5723
|
+
}
|
|
5724
|
+
return out;
|
|
5725
|
+
}
|
|
5726
|
+
function parseOpencodeModels(stdout) {
|
|
5727
|
+
const out = [];
|
|
5728
|
+
for (const raw of stdout.split("\n")) {
|
|
5729
|
+
const line = raw.trim();
|
|
5730
|
+
if (!line) continue;
|
|
5731
|
+
if (line.startsWith("{") || line.startsWith('"') || line.startsWith("}")) continue;
|
|
5732
|
+
if (line === line.toUpperCase() && /[A-Z]/.test(line)) continue;
|
|
5733
|
+
const id = line.split(/\s+/)[0];
|
|
5734
|
+
const slash = id.indexOf("/");
|
|
5735
|
+
if (slash <= 0 || slash >= id.length - 1) continue;
|
|
5736
|
+
out.push({ id, label: id, provider: id.slice(0, slash) });
|
|
5737
|
+
}
|
|
5738
|
+
return out;
|
|
5739
|
+
}
|
|
5740
|
+
function parseCursorModels(stdout) {
|
|
5741
|
+
const out = [];
|
|
5742
|
+
for (const raw of stdout.split("\n")) {
|
|
5743
|
+
const line = raw.trim();
|
|
5744
|
+
if (!line) continue;
|
|
5745
|
+
const sep = line.indexOf(" - ");
|
|
5746
|
+
if (sep < 0) continue;
|
|
5747
|
+
const id = line.slice(0, sep).trim();
|
|
5748
|
+
if (!isModelId(id)) continue;
|
|
5749
|
+
let label = line.slice(sep + 3).trim();
|
|
5750
|
+
const isDefault = /default/i.test(label);
|
|
5751
|
+
const paren = label.indexOf("(");
|
|
5752
|
+
if (paren >= 0) label = label.slice(0, paren).trim();
|
|
5753
|
+
out.push({ id, label: label || id, provider: "cursor", ...isDefault ? { default: true } : {} });
|
|
5754
|
+
}
|
|
5755
|
+
return out;
|
|
5756
|
+
}
|
|
5757
|
+
function parsePiModels(out) {
|
|
5758
|
+
const res = [];
|
|
5759
|
+
for (const raw of out.split("\n")) {
|
|
5760
|
+
const line = raw.trim();
|
|
5761
|
+
if (!line) continue;
|
|
5762
|
+
if (isPiNoise(line)) continue;
|
|
5763
|
+
const fields = line.split(/\s+/);
|
|
5764
|
+
const first = fields[0];
|
|
5765
|
+
if (first.toLowerCase() === "provider") continue;
|
|
5766
|
+
let id;
|
|
5767
|
+
if (first.includes(":") || first.includes("/")) id = first.replace(":", "/");
|
|
5768
|
+
else if (fields.length >= 2) id = `${first}/${fields[1]}`;
|
|
5769
|
+
else continue;
|
|
5770
|
+
const slash = id.indexOf("/");
|
|
5771
|
+
if (slash <= 0 || slash >= id.length - 1) continue;
|
|
5772
|
+
res.push({ id, label: id, provider: id.slice(0, slash) });
|
|
5773
|
+
}
|
|
5774
|
+
return res;
|
|
5775
|
+
}
|
|
5776
|
+
function isPiNoise(line) {
|
|
5777
|
+
const l = line.toLowerCase();
|
|
5778
|
+
return l.includes("no models match pattern") || l.startsWith("warning:") || l.startsWith("error:") || l.startsWith("info:");
|
|
5779
|
+
}
|
|
5780
|
+
var LIST_TIMEOUT_MS = 7e3;
|
|
5781
|
+
var OUT_CAP = 256 * 1024;
|
|
5782
|
+
function runList(bin, args2, timeoutMs = LIST_TIMEOUT_MS) {
|
|
5783
|
+
return new Promise((resolve) => {
|
|
5784
|
+
const env = { ...process.env };
|
|
5785
|
+
delete env.NODE_OPTIONS;
|
|
5786
|
+
let proc;
|
|
5787
|
+
try {
|
|
5788
|
+
proc = spawn8(bin, args2, { stdio: ["ignore", "pipe", "pipe"], env });
|
|
5789
|
+
} catch (e) {
|
|
5790
|
+
return resolve({ stdout: "", stderr: String(e?.message ?? e), code: 1 });
|
|
5791
|
+
}
|
|
5792
|
+
let stdout = "";
|
|
5793
|
+
let stderr = "";
|
|
5794
|
+
proc.stdout?.on("data", (c) => {
|
|
5795
|
+
if (stdout.length < OUT_CAP) stdout += c.toString();
|
|
5796
|
+
});
|
|
5797
|
+
proc.stderr?.on("data", (c) => {
|
|
5798
|
+
if (stderr.length < OUT_CAP) stderr += c.toString();
|
|
5799
|
+
});
|
|
5800
|
+
const timer = setTimeout(() => {
|
|
5801
|
+
try {
|
|
5802
|
+
proc.kill("SIGKILL");
|
|
5803
|
+
} catch {
|
|
5804
|
+
}
|
|
5805
|
+
}, timeoutMs);
|
|
5806
|
+
proc.on("error", (e) => {
|
|
5807
|
+
clearTimeout(timer);
|
|
5808
|
+
resolve({ stdout, stderr: stderr || String(e?.message ?? e), code: 1 });
|
|
5809
|
+
});
|
|
5810
|
+
proc.on("exit", (code) => {
|
|
5811
|
+
clearTimeout(timer);
|
|
5812
|
+
resolve({ stdout, stderr, code });
|
|
5813
|
+
});
|
|
5814
|
+
});
|
|
5815
|
+
}
|
|
5816
|
+
async function listModels(runtime) {
|
|
5817
|
+
switch (runtime) {
|
|
5818
|
+
case "opencode": {
|
|
5819
|
+
let r = await runList("opencode", ["models", "--verbose"], 5e3);
|
|
5820
|
+
let models = parseOpencodeModels(r.stdout);
|
|
5821
|
+
if (!models.length) {
|
|
5822
|
+
r = await runList("opencode", ["models"], 2e3);
|
|
5823
|
+
models = parseOpencodeModels(r.stdout);
|
|
5824
|
+
}
|
|
5825
|
+
return models.length ? models : null;
|
|
5826
|
+
}
|
|
5827
|
+
case "cursor": {
|
|
5828
|
+
const r = await runList("cursor-agent", ["--list-models"]);
|
|
5829
|
+
const models = parseCursorModels(r.stdout);
|
|
5830
|
+
return models.length ? models : null;
|
|
5831
|
+
}
|
|
5832
|
+
case "pi": {
|
|
5833
|
+
const r = await runList("pi", ["--list-models"]);
|
|
5834
|
+
const models = parsePiModels(r.stdout || r.stderr);
|
|
5835
|
+
return models.length ? models : null;
|
|
5836
|
+
}
|
|
5837
|
+
case "claude": {
|
|
5838
|
+
const r = await runList("claude", ["--help"]);
|
|
5839
|
+
const superset = parseClaudeEffortLevels(r.stdout || r.stderr);
|
|
5840
|
+
if (!superset.length) return null;
|
|
5841
|
+
return CLAUDE_MODELS.map((m) => {
|
|
5842
|
+
const thinking = claudeThinkingForModel(m.id, superset);
|
|
5843
|
+
return { ...m, provider: "anthropic", ...thinking ? { thinking } : {} };
|
|
5844
|
+
});
|
|
5845
|
+
}
|
|
5846
|
+
case "codex": {
|
|
5847
|
+
const r = await runList("codex", ["debug", "models"]);
|
|
5848
|
+
const models = parseCodexModels(r.stdout);
|
|
5849
|
+
return models.length ? models : null;
|
|
5850
|
+
}
|
|
5851
|
+
default:
|
|
5852
|
+
return null;
|
|
5853
|
+
}
|
|
5854
|
+
}
|
|
5855
|
+
|
|
4764
5856
|
// src/daemon/index.ts
|
|
4765
5857
|
var log = createLogger("daemon");
|
|
4766
5858
|
var args = process.argv.slice(2);
|
|
@@ -4785,7 +5877,7 @@ var readMachineId = () => {
|
|
|
4785
5877
|
};
|
|
4786
5878
|
var saveMachineId = (id) => {
|
|
4787
5879
|
try {
|
|
4788
|
-
fs3.mkdirSync(
|
|
5880
|
+
fs3.mkdirSync(path12.dirname(MID_FILE), { recursive: true });
|
|
4789
5881
|
fs3.writeFileSync(MID_FILE, id);
|
|
4790
5882
|
} catch {
|
|
4791
5883
|
}
|
|
@@ -4826,6 +5918,9 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
|
|
|
4826
5918
|
case "agent:skills:list":
|
|
4827
5919
|
void listSkills(msg.agentId).then((r) => conn.send({ type: "skills:list", requestId: msg.requestId, agentId: msg.agentId, ...r }));
|
|
4828
5920
|
break;
|
|
5921
|
+
case "probe-models":
|
|
5922
|
+
void listModels(msg.runtime ?? "").then((models) => conn.send({ type: "models", requestId: msg.requestId, runtime: msg.runtime, models })).catch((e) => conn.send({ type: "models", requestId: msg.requestId, runtime: msg.runtime, models: null, error: String(e?.message ?? e) }));
|
|
5923
|
+
break;
|
|
4829
5924
|
case "ping":
|
|
4830
5925
|
conn.send({ type: "pong" });
|
|
4831
5926
|
break;
|
|
@@ -4840,7 +5935,7 @@ conn = new Connection(serverUrl, apiKey, (msg) => {
|
|
|
4840
5935
|
runningAgents: mgr.running(),
|
|
4841
5936
|
hostname: os4.hostname(),
|
|
4842
5937
|
os: `${os4.platform()} ${os4.arch()}`,
|
|
4843
|
-
daemonVersion: "0.
|
|
5938
|
+
daemonVersion: "0.3.0",
|
|
4844
5939
|
machineId: readMachineId()
|
|
4845
5940
|
// Stable identity: empty on first connection; server sends it back via ready:ack for persistence.
|
|
4846
5941
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fancyboi999/open-tag-daemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "open-tag compute-plane daemon — connect any machine to an open-tag server so its agents run there. No repo clone needed.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|