nolo-cli 0.8.0-alpha.2 → 0.8.0-alpha.20
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/README.md +1 -1
- package/_executeModel-WRUCQIV5.js +205 -0
- package/action-R4M4ORGA.js +28 -0
- package/addContentAction-UYFFTEWJ.js +26 -0
- package/addDialogAgentAction-OT4XZAVP.js +68 -0
- package/agentDeleteCommand-5DD2IM3J.js +327 -0
- package/agentEmailCommands-IASM2YBQ.js +628 -0
- package/agentListCommands-I6KZYCIG.js +223 -0
- package/agentMachineCommands-OUP3YQP7.js +425 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-QKGE2JTA.js +96 -0
- package/agentRunCommand-TVZXUBQ5.js +114 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-ZYEIPTY4.js +104 -0
- package/agentSlice-T4PMW7W6.js +42 -0
- package/authCommand-EB5JSOX5.js +581 -0
- package/authCommands-XPEHTKSM.js +35 -0
- package/chunk-2EHX7F5G.js +1948 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4FBRSEV4.js +573 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4QEWT7KD.js +933 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-4ZDM73SD.js +34 -0
- package/chunk-5CS33IBL.js +301 -0
- package/chunk-5KLGBAAR.js +386 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-5W3VUFOX.js +34 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7DAJ4AI4.js +191 -0
- package/chunk-7KCUSYT2.js +354 -0
- package/chunk-7T5PWAZN.js +171 -0
- package/chunk-AFON4QUU.js +112 -0
- package/chunk-AGFH73SK.js +239 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BBYVOSU2.js +792 -0
- package/chunk-BCIOM4NZ.js +1078 -0
- package/chunk-C3RUNRHT.js +51 -0
- package/chunk-C4FTUXVF.js +146 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-CMKUBIGW.js +1550 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DCOA4QSN.js +105 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-FM5IVZXO.js +4723 -0
- package/chunk-FVYY6MCS.js +1447 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GLYVN7EQ.js +238 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-HNW7IJSK.js +71 -0
- package/chunk-I42VC5KR.js +15 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-IHBCLYEE.js +22310 -0
- package/chunk-IJGXKGXW.js +421 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IWX4UW2E.js +548 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JEXVIMYZ.js +55 -0
- package/chunk-JRKKFRAV.js +537 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-K6BPOQLF.js +2052 -0
- package/chunk-K777STIO.js +478 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-LTRXGFMS.js +83 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MNJDFJND.js +222 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-MTU75CZ2.js +91 -0
- package/chunk-N54XAP77.js +19 -0
- package/chunk-O2ZNTTET.js +3272 -0
- package/chunk-O4BU24WZ.js +3212 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OPZYGHUC.js +89 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-P3PKED2I.js +962 -0
- package/chunk-PFTAG6DY.js +1939 -0
- package/chunk-PMKQGGGM.js +221 -0
- package/chunk-PP4S6RTL.js +175 -0
- package/chunk-QJ7FZNYN.js +939 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-RGIM3WLT.js +113 -0
- package/chunk-RL776AGR.js +190 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SDJDLO5W.js +628 -0
- package/chunk-SOTZ6RDN.js +69 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-TIMLUWQA.js +231 -0
- package/chunk-TISCADJH.js +807 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-VC6OZXO5.js +298 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-VVX233PH.js +482 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WI4XWASA.js +208 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-WZSRQNQG.js +2587 -0
- package/chunk-XBROXIGL.js +3066 -0
- package/chunk-XENYHRUE.js +104 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YN4XDB7B.js +9036 -0
- package/chunk-YPRZSPF3.js +148 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-Z6EKEXJW.js +138 -0
- package/chunk-ZDRZMLKO.js +27339 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/cliExecutor-K3F7DMRO.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-CCABEOM6.js +866 -0
- package/createDialogAction-YPKF5ITI.js +55 -0
- package/createPageAction-YEZ7EK5F.js +172 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-QPVZHBOE.js +60 -0
- package/deleteFile-SV5MS23W.js +41 -0
- package/dialogCommands-4IX4XIZE.js +63 -0
- package/dialogSlice-OVTO7QGW.js +148 -0
- package/docCreateCommands-SIL3NPGT.js +220 -0
- package/docDeleteCommands-7IOMLUS4.js +181 -0
- package/docReadCommands-FNFXUA5L.js +134 -0
- package/docStore-ZAYLW2HN.js +98 -0
- package/docUpdateCommands-X2HNDVUC.js +318 -0
- package/fetchMessages-KTNM7QAR.js +17 -0
- package/getPricing-3E6UNPKL.js +30 -0
- package/handleSendMessageAction-TZMZ4EP5.js +185 -0
- package/index.js +188 -112958
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-2VRI24F4.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-M4ZJZIMS.js +781 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-TPJAUGLP.js +112 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-4OPOOLFS.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +4 -23
- package/read-EBMWX4Z6.js +17 -0
- package/readDocTool-2MWHSTQE.js +16 -0
- package/readlineWorkspace-OMCIU3WT.js +5682 -0
- package/removeDialogAgentAction-SNZ662NP.js +73 -0
- package/runAgentClientLoop-H6NGOUAM.js +293 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-XDI2KM2N.js +60 -0
- package/setPrimaryDialogAgentAction-PYEC4N4Z.js +80 -0
- package/settingSlice-NOWHY2FK.js +199 -0
- package/skillDiscovery-UGSQGX2U.js +16 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-E7VOF4LO.js +450 -0
- package/spaceSlice-QHBPICA2.js +149 -0
- package/streamAgentChatTurn-3XTBA2HN.js +4510 -0
- package/tableCommands-R5UCM63M.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-GHLZ3XD6.js +87 -0
- package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
- package/understanding-3CUQQ6SV.js +271 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-XA2OZTLP.js +188 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
import {
|
|
2
|
+
isCompiledBinary,
|
|
3
|
+
resolveCliEntrypointPath
|
|
4
|
+
} from "./chunk-ISNCLGUZ.js";
|
|
5
|
+
|
|
6
|
+
// packages/cli/agentRunControl.ts
|
|
7
|
+
import { homedir as nodeHomedir } from "node:os";
|
|
8
|
+
import { isAbsolute, join, resolve } from "node:path";
|
|
9
|
+
import * as nodeFs from "node:fs";
|
|
10
|
+
import { spawn as nodeSpawn } from "node:child_process";
|
|
11
|
+
function resolveNoloHome(env, homedir = nodeHomedir) {
|
|
12
|
+
const fromEnv = env?.NOLO_HOME;
|
|
13
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv;
|
|
14
|
+
return join(homedir(), ".nolo");
|
|
15
|
+
}
|
|
16
|
+
function resolveRunsDir(env, homedir = nodeHomedir) {
|
|
17
|
+
return join(resolveNoloHome(env, homedir), "runs");
|
|
18
|
+
}
|
|
19
|
+
function resolveRunRecordPath(runId, env, homedir = nodeHomedir) {
|
|
20
|
+
return join(resolveRunsDir(env, homedir), `${runId}.json`);
|
|
21
|
+
}
|
|
22
|
+
function resolveRunLogPath(runId, env, homedir = nodeHomedir) {
|
|
23
|
+
return join(resolveRunsDir(env, homedir), `${runId}.log`);
|
|
24
|
+
}
|
|
25
|
+
function defaultGenerateRunId() {
|
|
26
|
+
const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
27
|
+
const random = Math.random().toString(36).slice(2, 8);
|
|
28
|
+
return `run-${timestamp}-${random}`;
|
|
29
|
+
}
|
|
30
|
+
function writeRunRecord(record, deps = {}) {
|
|
31
|
+
const fs = deps.fs ?? nodeFs;
|
|
32
|
+
const path = resolveRunRecordPath(record.runId, deps.env, deps.homedir);
|
|
33
|
+
fs.mkdirSync(resolveRunsDir(deps.env, deps.homedir), { recursive: true });
|
|
34
|
+
fs.writeFileSync(path, JSON.stringify(record, null, 2));
|
|
35
|
+
}
|
|
36
|
+
var FILE_EDIT_TOOL_NAMES = /* @__PURE__ */ new Set(["writeFile", "editFile"]);
|
|
37
|
+
var DEFAULT_ACTIVITY_WRITE_INTERVAL_MS = 2e3;
|
|
38
|
+
function createRunActivityTracker(runId, deps = {}, options = {}) {
|
|
39
|
+
const fs = deps.fs ?? nodeFs;
|
|
40
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
41
|
+
const minWriteIntervalMs = options.minWriteIntervalMs ?? DEFAULT_ACTIVITY_WRITE_INTERVAL_MS;
|
|
42
|
+
let lastEventAt = now().toISOString();
|
|
43
|
+
let inFlight = null;
|
|
44
|
+
const counters = { llmCalls: 0, toolCalls: 0, fileEdits: 0 };
|
|
45
|
+
let writeTimer;
|
|
46
|
+
let lastWriteAt = 0;
|
|
47
|
+
function serializeActivity() {
|
|
48
|
+
const nowDate = now();
|
|
49
|
+
const nowMs = nowDate.getTime();
|
|
50
|
+
return {
|
|
51
|
+
lastEventAt,
|
|
52
|
+
inFlight: inFlight ? {
|
|
53
|
+
kind: inFlight.kind,
|
|
54
|
+
name: inFlight.name,
|
|
55
|
+
sinceMs: Math.max(0, nowMs - inFlight.startMs)
|
|
56
|
+
} : null,
|
|
57
|
+
counters: { ...counters },
|
|
58
|
+
updatedAt: nowDate.toISOString()
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function doWrite() {
|
|
62
|
+
writeTimer = void 0;
|
|
63
|
+
const record = readRunRecord(runId, deps);
|
|
64
|
+
if (!record) return;
|
|
65
|
+
const activity = serializeActivity();
|
|
66
|
+
writeRunRecord({ ...record, activity }, deps);
|
|
67
|
+
lastWriteAt = now().getTime();
|
|
68
|
+
}
|
|
69
|
+
function scheduleWrite() {
|
|
70
|
+
if (writeTimer !== void 0) return;
|
|
71
|
+
const elapsed = now().getTime() - lastWriteAt;
|
|
72
|
+
const delay = Math.max(0, minWriteIntervalMs - elapsed);
|
|
73
|
+
writeTimer = setTimeout(doWrite, delay);
|
|
74
|
+
}
|
|
75
|
+
function onLoopEvent(event) {
|
|
76
|
+
lastEventAt = new Date(event.atMs).toISOString();
|
|
77
|
+
switch (event.kind) {
|
|
78
|
+
case "llm-start":
|
|
79
|
+
inFlight = { kind: "llm", name: "llm", startMs: event.atMs };
|
|
80
|
+
break;
|
|
81
|
+
case "llm-end":
|
|
82
|
+
inFlight = null;
|
|
83
|
+
counters.llmCalls += 1;
|
|
84
|
+
break;
|
|
85
|
+
case "tool-start":
|
|
86
|
+
inFlight = { kind: "tool", name: event.name, startMs: event.atMs };
|
|
87
|
+
break;
|
|
88
|
+
case "tool-end":
|
|
89
|
+
inFlight = null;
|
|
90
|
+
counters.toolCalls += 1;
|
|
91
|
+
if (FILE_EDIT_TOOL_NAMES.has(event.name)) {
|
|
92
|
+
counters.fileEdits += 1;
|
|
93
|
+
}
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
scheduleWrite();
|
|
97
|
+
}
|
|
98
|
+
function getActivity() {
|
|
99
|
+
return serializeActivity();
|
|
100
|
+
}
|
|
101
|
+
function flush() {
|
|
102
|
+
doWrite();
|
|
103
|
+
}
|
|
104
|
+
function dispose() {
|
|
105
|
+
if (writeTimer !== void 0) {
|
|
106
|
+
clearTimeout(writeTimer);
|
|
107
|
+
writeTimer = void 0;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
return { onLoopEvent, getActivity, flush, dispose };
|
|
111
|
+
}
|
|
112
|
+
function readRunRecord(runId, deps = {}) {
|
|
113
|
+
const fs = deps.fs ?? nodeFs;
|
|
114
|
+
const path = resolveRunRecordPath(runId, deps.env, deps.homedir);
|
|
115
|
+
try {
|
|
116
|
+
return JSON.parse(fs.readFileSync(path, "utf8"));
|
|
117
|
+
} catch {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function listRunRecords(deps = {}) {
|
|
122
|
+
const fs = deps.fs ?? nodeFs;
|
|
123
|
+
const dir = resolveRunsDir(deps.env, deps.homedir);
|
|
124
|
+
let entries = [];
|
|
125
|
+
try {
|
|
126
|
+
entries = fs.readdirSync(dir);
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
const records = [];
|
|
131
|
+
for (const entry of entries) {
|
|
132
|
+
if (!entry.endsWith(".json")) continue;
|
|
133
|
+
const runId = entry.slice(0, -".json".length);
|
|
134
|
+
const record = readRunRecord(runId, deps);
|
|
135
|
+
if (record) records.push(record);
|
|
136
|
+
}
|
|
137
|
+
return records.sort(
|
|
138
|
+
(a, b) => new Date(b.startedAt).getTime() - new Date(a.startedAt).getTime()
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
function findRunRecordByPid(pid, deps = {}) {
|
|
142
|
+
return listRunRecords(deps).find((record) => record.pid === pid);
|
|
143
|
+
}
|
|
144
|
+
function findRunRecord(target, deps = {}) {
|
|
145
|
+
if (/^\d+$/.test(target)) {
|
|
146
|
+
const pid = Number(target);
|
|
147
|
+
const byPid = findRunRecordByPid(pid, deps);
|
|
148
|
+
if (byPid) return byPid;
|
|
149
|
+
}
|
|
150
|
+
return readRunRecord(target, deps) ?? void 0;
|
|
151
|
+
}
|
|
152
|
+
function stripBackgroundFlag(args) {
|
|
153
|
+
const result = [];
|
|
154
|
+
for (const arg of args) {
|
|
155
|
+
if (arg === "--bg" || arg.startsWith("--bg=")) continue;
|
|
156
|
+
result.push(arg);
|
|
157
|
+
}
|
|
158
|
+
return result;
|
|
159
|
+
}
|
|
160
|
+
function absolutizeSkillArgs(args, baseCwd = process.cwd()) {
|
|
161
|
+
const result = [];
|
|
162
|
+
const resolveIfPath = (value) => {
|
|
163
|
+
if (isAbsolute(value)) return value;
|
|
164
|
+
const looksLikePath = value.includes("/") || value.endsWith(".md");
|
|
165
|
+
return looksLikePath ? resolve(baseCwd, value) : value;
|
|
166
|
+
};
|
|
167
|
+
for (let i = 0; i < args.length; i++) {
|
|
168
|
+
const arg = args[i];
|
|
169
|
+
if (arg === "--skill" && typeof args[i + 1] === "string") {
|
|
170
|
+
result.push(arg, resolveIfPath(args[i + 1]));
|
|
171
|
+
i++;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
const eqMatch = arg.match(/^--skill=(.+)$/);
|
|
175
|
+
if (eqMatch) {
|
|
176
|
+
result.push(`--skill=${resolveIfPath(eqMatch[1])}`);
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
result.push(arg);
|
|
180
|
+
}
|
|
181
|
+
return result;
|
|
182
|
+
}
|
|
183
|
+
function rewriteMsgFileArg(args, messagePath) {
|
|
184
|
+
const result = [];
|
|
185
|
+
for (let i = 0; i < args.length; i++) {
|
|
186
|
+
const arg = args[i];
|
|
187
|
+
if (arg === "--msg-file" && typeof args[i + 1] === "string") {
|
|
188
|
+
result.push(arg, messagePath);
|
|
189
|
+
i++;
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (arg.startsWith("--msg-file=")) {
|
|
193
|
+
result.push(`--msg-file=${messagePath}`);
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
result.push(arg);
|
|
197
|
+
}
|
|
198
|
+
return result;
|
|
199
|
+
}
|
|
200
|
+
function buildAgentRunChildCommand(options) {
|
|
201
|
+
const execPath = process.execPath;
|
|
202
|
+
const entrypoint = options.cliEntrypointPath || resolveCliEntrypointPath();
|
|
203
|
+
const commandParts = options.commandPath ?? [];
|
|
204
|
+
let strippedArgs = stripBackgroundFlag(options.rawArgs);
|
|
205
|
+
if (options.messagePath) {
|
|
206
|
+
strippedArgs = rewriteMsgFileArg(strippedArgs, options.messagePath);
|
|
207
|
+
}
|
|
208
|
+
strippedArgs = absolutizeSkillArgs(strippedArgs);
|
|
209
|
+
if (isCompiledBinary() || entrypoint === execPath) {
|
|
210
|
+
return { execPath, childArgs: [...commandParts, ...strippedArgs] };
|
|
211
|
+
}
|
|
212
|
+
return { execPath, childArgs: [entrypoint, ...commandParts, ...strippedArgs] };
|
|
213
|
+
}
|
|
214
|
+
async function spawnLocalBackgroundRun(input, deps = {}) {
|
|
215
|
+
const env = deps.env ?? process.env;
|
|
216
|
+
const homedir = deps.homedir ?? nodeHomedir;
|
|
217
|
+
const fs = deps.fs ?? nodeFs;
|
|
218
|
+
const spawn = deps.spawn ?? nodeSpawn;
|
|
219
|
+
const generateRunId = deps.generateRunId ?? defaultGenerateRunId;
|
|
220
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
221
|
+
const runId = generateRunId();
|
|
222
|
+
const logPath = resolveRunLogPath(runId, env, homedir);
|
|
223
|
+
const recordPath = resolveRunRecordPath(runId, env, homedir);
|
|
224
|
+
const runsDir = resolveRunsDir(env, homedir);
|
|
225
|
+
fs.mkdirSync(runsDir, { recursive: true });
|
|
226
|
+
let messagePath;
|
|
227
|
+
if (typeof input.message === "string") {
|
|
228
|
+
messagePath = join(runsDir, `${runId}.msg.md`);
|
|
229
|
+
fs.writeFileSync(messagePath, input.message);
|
|
230
|
+
}
|
|
231
|
+
const record = {
|
|
232
|
+
runId,
|
|
233
|
+
agentKey: input.agentKey,
|
|
234
|
+
cwd: input.cwd,
|
|
235
|
+
...messagePath ? { msgFile: messagePath } : input.msgFile ? { msgFile: input.msgFile } : {},
|
|
236
|
+
startedAt: now().toISOString(),
|
|
237
|
+
...typeof input.timeoutMs === "number" ? { timeoutMs: input.timeoutMs } : {},
|
|
238
|
+
status: "running",
|
|
239
|
+
logPath
|
|
240
|
+
};
|
|
241
|
+
fs.writeFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
242
|
+
const { execPath, childArgs } = buildAgentRunChildCommand({
|
|
243
|
+
rawArgs: input.rawArgs,
|
|
244
|
+
commandPath: input.commandPath,
|
|
245
|
+
cliEntrypointPath: input.cliEntrypointPath,
|
|
246
|
+
messagePath
|
|
247
|
+
});
|
|
248
|
+
const childEnv = {
|
|
249
|
+
...env,
|
|
250
|
+
NOLO_AGENT_RUN_CHILD: "1",
|
|
251
|
+
NOLO_AGENT_RUN_ID: runId
|
|
252
|
+
};
|
|
253
|
+
const logFd = fs.openSync(logPath, "a");
|
|
254
|
+
const proc = spawn(execPath, childArgs, {
|
|
255
|
+
cwd: input.cwd,
|
|
256
|
+
env: childEnv,
|
|
257
|
+
detached: true,
|
|
258
|
+
stdio: ["ignore", logFd, logFd]
|
|
259
|
+
});
|
|
260
|
+
proc.unref();
|
|
261
|
+
if (typeof proc.pid === "number") {
|
|
262
|
+
record.pid = proc.pid;
|
|
263
|
+
fs.writeFileSync(recordPath, JSON.stringify(record, null, 2));
|
|
264
|
+
}
|
|
265
|
+
return { runId, pid: proc.pid, logPath };
|
|
266
|
+
}
|
|
267
|
+
function finalizeRunRecord(runId, update, deps = {}) {
|
|
268
|
+
const record = readRunRecord(runId, deps);
|
|
269
|
+
if (!record) return;
|
|
270
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
271
|
+
record.status = update.status;
|
|
272
|
+
if (typeof update.exitCode === "number") record.exitCode = update.exitCode;
|
|
273
|
+
if (update.dialogId) record.dialogId = update.dialogId;
|
|
274
|
+
record.endedAt = now().toISOString();
|
|
275
|
+
writeRunRecord(record, deps);
|
|
276
|
+
}
|
|
277
|
+
function isPidGone(pid, deps = {}) {
|
|
278
|
+
const kill = deps.kill ?? ((p, s) => process.kill(p, s));
|
|
279
|
+
try {
|
|
280
|
+
kill(pid, "0");
|
|
281
|
+
return false;
|
|
282
|
+
} catch (error) {
|
|
283
|
+
const code = error.code;
|
|
284
|
+
return code === "ESRCH";
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function checkStaleRun(runId, deps = {}) {
|
|
288
|
+
const record = readRunRecord(runId, deps);
|
|
289
|
+
if (!record) return null;
|
|
290
|
+
if (record.status !== "running") return record;
|
|
291
|
+
if (typeof record.pid !== "number") return record;
|
|
292
|
+
if (!isPidGone(record.pid, deps)) return record;
|
|
293
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
294
|
+
record.status = "failed";
|
|
295
|
+
record.note = "process gone: pid no longer exists";
|
|
296
|
+
record.endedAt = now().toISOString();
|
|
297
|
+
writeRunRecord(record, deps);
|
|
298
|
+
return record;
|
|
299
|
+
}
|
|
300
|
+
function formatDuration(startedAt, endedAt) {
|
|
301
|
+
const start = new Date(startedAt).getTime();
|
|
302
|
+
const end = endedAt ? new Date(endedAt).getTime() : Date.now();
|
|
303
|
+
const elapsedMs = Math.max(0, end - start);
|
|
304
|
+
const seconds = Math.floor(elapsedMs / 1e3) % 60;
|
|
305
|
+
const minutes = Math.floor(elapsedMs / 6e4) % 60;
|
|
306
|
+
const hours = Math.floor(elapsedMs / 36e5);
|
|
307
|
+
if (hours > 0) {
|
|
308
|
+
return `${hours}h ${minutes}m ${seconds}s`;
|
|
309
|
+
}
|
|
310
|
+
if (minutes > 0) {
|
|
311
|
+
return `${minutes}m ${seconds}s`;
|
|
312
|
+
}
|
|
313
|
+
return `${seconds}s`;
|
|
314
|
+
}
|
|
315
|
+
function readLastLogLines(logPath, count, deps) {
|
|
316
|
+
const fs = deps.fs ?? nodeFs;
|
|
317
|
+
try {
|
|
318
|
+
const content = fs.readFileSync(logPath, "utf8");
|
|
319
|
+
const lines = content.split("\n");
|
|
320
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
321
|
+
return lines.slice(-count);
|
|
322
|
+
} catch {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function readLogContent(logPath, tailCount, deps) {
|
|
327
|
+
const fs = deps.fs ?? nodeFs;
|
|
328
|
+
try {
|
|
329
|
+
const content = fs.readFileSync(logPath, "utf8");
|
|
330
|
+
if (typeof tailCount === "number" && tailCount > 0) {
|
|
331
|
+
const lines = content.split("\n");
|
|
332
|
+
if (lines.length > 0 && lines[lines.length - 1] === "") lines.pop();
|
|
333
|
+
return `${lines.slice(-tailCount).join("\n")}
|
|
334
|
+
`;
|
|
335
|
+
}
|
|
336
|
+
return content;
|
|
337
|
+
} catch {
|
|
338
|
+
return "";
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
var RUNNING_STATUSES = /* @__PURE__ */ new Set(["running"]);
|
|
342
|
+
function isRunningStatus(status) {
|
|
343
|
+
return RUNNING_STATUSES.has(status);
|
|
344
|
+
}
|
|
345
|
+
function parseJsonFlag(args) {
|
|
346
|
+
let json = false;
|
|
347
|
+
const rest = [];
|
|
348
|
+
for (const arg of args) {
|
|
349
|
+
if (arg === "--json") {
|
|
350
|
+
json = true;
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
if (arg.startsWith("--json=")) {
|
|
354
|
+
const value = arg.slice("--json=".length);
|
|
355
|
+
json = value === "" || value === "true" || value === "1";
|
|
356
|
+
continue;
|
|
357
|
+
}
|
|
358
|
+
rest.push(arg);
|
|
359
|
+
}
|
|
360
|
+
return { json, rest };
|
|
361
|
+
}
|
|
362
|
+
function defaultSleep(ms) {
|
|
363
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
364
|
+
}
|
|
365
|
+
async function runAgentPsCommand(args, deps) {
|
|
366
|
+
const { json } = parseJsonFlag(args);
|
|
367
|
+
const records = listRunRecords(deps);
|
|
368
|
+
for (const record of records) {
|
|
369
|
+
if (record.status === "running" && typeof record.pid === "number") {
|
|
370
|
+
checkStaleRun(record.runId, deps);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
const refreshed = records.map((r) => readRunRecord(r.runId, deps)).filter(Boolean);
|
|
374
|
+
if (json) {
|
|
375
|
+
deps.output.write(JSON.stringify(refreshed) + "\n");
|
|
376
|
+
return 0;
|
|
377
|
+
}
|
|
378
|
+
if (refreshed.length === 0) {
|
|
379
|
+
deps.output.write("No local runs found.\n");
|
|
380
|
+
return 0;
|
|
381
|
+
}
|
|
382
|
+
deps.output.write("RUN ID STATUS PID AGENT\n");
|
|
383
|
+
for (const record of refreshed) {
|
|
384
|
+
const pid = record.pid?.toString() ?? "-";
|
|
385
|
+
deps.output.write(
|
|
386
|
+
`${record.runId.padEnd(32)} ${record.status.padEnd(8)} ${pid.padEnd(8)} ${record.agentKey}
|
|
387
|
+
`
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
return 0;
|
|
391
|
+
}
|
|
392
|
+
function parseStatusArgs(args) {
|
|
393
|
+
let target = "";
|
|
394
|
+
let json = false;
|
|
395
|
+
let watch = false;
|
|
396
|
+
let intervalMs = 2e3;
|
|
397
|
+
for (let i = 0; i < args.length; i++) {
|
|
398
|
+
const arg = args[i];
|
|
399
|
+
if (arg === "--json") {
|
|
400
|
+
json = true;
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
if (arg.startsWith("--json=")) {
|
|
404
|
+
const value = arg.slice("--json=".length);
|
|
405
|
+
json = value === "" || value === "true" || value === "1";
|
|
406
|
+
continue;
|
|
407
|
+
}
|
|
408
|
+
if (arg === "--watch") {
|
|
409
|
+
watch = true;
|
|
410
|
+
continue;
|
|
411
|
+
}
|
|
412
|
+
if (arg.startsWith("--watch=")) {
|
|
413
|
+
const value = arg.slice("--watch=".length);
|
|
414
|
+
watch = value === "" || value === "true" || value === "1";
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
417
|
+
if (arg === "--interval-ms") {
|
|
418
|
+
const next = args[i + 1];
|
|
419
|
+
if (next && /^\d+$/.test(next)) {
|
|
420
|
+
intervalMs = Number(next);
|
|
421
|
+
i += 1;
|
|
422
|
+
}
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
if (arg.startsWith("--interval-ms=")) {
|
|
426
|
+
const value = arg.slice("--interval-ms=".length);
|
|
427
|
+
if (/^\d+$/.test(value)) intervalMs = Number(value);
|
|
428
|
+
continue;
|
|
429
|
+
}
|
|
430
|
+
if (!arg.startsWith("-") && !target) {
|
|
431
|
+
target = arg;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
return { target, json, watch, intervalMs };
|
|
435
|
+
}
|
|
436
|
+
function printStatusTick(record, deps) {
|
|
437
|
+
const elapsed = formatDuration(record.startedAt, record.endedAt);
|
|
438
|
+
const note = record.note ? ` (${record.note})` : "";
|
|
439
|
+
deps.output.write(
|
|
440
|
+
`[${(/* @__PURE__ */ new Date()).toISOString()}] ${record.runId} status=${record.status} elapsed=${elapsed}${note}
|
|
441
|
+
`
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
async function runAgentStatusCommand(args, deps) {
|
|
445
|
+
const { target, json, watch, intervalMs } = parseStatusArgs(args);
|
|
446
|
+
if (!target) {
|
|
447
|
+
deps.output.write("Usage: nolo agent status <runId|pid> [--json] [--watch] [--interval-ms N]\n");
|
|
448
|
+
return 1;
|
|
449
|
+
}
|
|
450
|
+
const initial = findRunRecord(target, deps);
|
|
451
|
+
if (!initial) {
|
|
452
|
+
deps.output.write(`Run not found: ${target}
|
|
453
|
+
`);
|
|
454
|
+
return 1;
|
|
455
|
+
}
|
|
456
|
+
const reconciled = checkStaleRun(initial.runId, deps) ?? initial;
|
|
457
|
+
if (json) {
|
|
458
|
+
const record = readRunRecord(reconciled.runId, deps) ?? reconciled;
|
|
459
|
+
deps.output.write(JSON.stringify(record) + "\n");
|
|
460
|
+
return 0;
|
|
461
|
+
}
|
|
462
|
+
if (!watch) {
|
|
463
|
+
const record = readRunRecord(reconciled.runId, deps) ?? reconciled;
|
|
464
|
+
return printStatusOnce(record, deps);
|
|
465
|
+
}
|
|
466
|
+
return runStatusWatch(reconciled.runId, intervalMs, deps);
|
|
467
|
+
}
|
|
468
|
+
function formatActivitySummary(activity) {
|
|
469
|
+
if (!activity) return void 0;
|
|
470
|
+
const { counters, inFlight, lastEventAt } = activity;
|
|
471
|
+
const parts = [`${counters.fileEdits} edits, ${counters.toolCalls} tools`];
|
|
472
|
+
if (inFlight) {
|
|
473
|
+
const elapsedSec = Math.floor(
|
|
474
|
+
(Date.now() - new Date(lastEventAt).getTime()) / 1e3
|
|
475
|
+
);
|
|
476
|
+
parts.push(`in-flight ${inFlight.kind}${inFlight.name ? ` ${inFlight.name}` : ""} ${elapsedSec}s`);
|
|
477
|
+
}
|
|
478
|
+
return `activity: ${parts.join(", ")}`;
|
|
479
|
+
}
|
|
480
|
+
function printStatusOnce(record, deps) {
|
|
481
|
+
deps.output.write(`runId: ${record.runId}
|
|
482
|
+
`);
|
|
483
|
+
deps.output.write(`status: ${record.status}
|
|
484
|
+
`);
|
|
485
|
+
deps.output.write(`pid: ${record.pid ?? "-"}
|
|
486
|
+
`);
|
|
487
|
+
deps.output.write(`agent: ${record.agentKey}
|
|
488
|
+
`);
|
|
489
|
+
deps.output.write(`cwd: ${record.cwd ?? "-"}
|
|
490
|
+
`);
|
|
491
|
+
deps.output.write(`started: ${record.startedAt}
|
|
492
|
+
`);
|
|
493
|
+
deps.output.write(`elapsed: ${formatDuration(record.startedAt, record.endedAt)}
|
|
494
|
+
`);
|
|
495
|
+
if (record.endedAt) deps.output.write(`ended: ${record.endedAt}
|
|
496
|
+
`);
|
|
497
|
+
if (typeof record.exitCode === "number") deps.output.write(`exitCode: ${record.exitCode}
|
|
498
|
+
`);
|
|
499
|
+
if (record.dialogId) deps.output.write(`dialog: ${record.dialogId}
|
|
500
|
+
`);
|
|
501
|
+
if (record.note) deps.output.write(`note: ${record.note}
|
|
502
|
+
`);
|
|
503
|
+
const activitySummary = formatActivitySummary(record.activity);
|
|
504
|
+
if (activitySummary) deps.output.write(`${activitySummary}
|
|
505
|
+
`);
|
|
506
|
+
deps.output.write(`log: ${record.logPath}
|
|
507
|
+
`);
|
|
508
|
+
const logLines = readLastLogLines(record.logPath, 20, deps);
|
|
509
|
+
if (logLines.length > 0) {
|
|
510
|
+
deps.output.write("\n--- last log lines ---\n");
|
|
511
|
+
for (const line of logLines) {
|
|
512
|
+
deps.output.write(`${line}
|
|
513
|
+
`);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
return 0;
|
|
517
|
+
}
|
|
518
|
+
async function runStatusWatch(runId, intervalMs, deps) {
|
|
519
|
+
const sleep = deps.sleep ?? defaultSleep;
|
|
520
|
+
const now = deps.now ?? (() => /* @__PURE__ */ new Date());
|
|
521
|
+
let stopped = false;
|
|
522
|
+
const setSignalHandler = deps.setSignalHandler ?? ((handler) => {
|
|
523
|
+
process.once("SIGINT", handler);
|
|
524
|
+
});
|
|
525
|
+
const clearSignalHandler = deps.clearSignalHandler ?? (() => {
|
|
526
|
+
process.removeAllListeners("SIGINT");
|
|
527
|
+
});
|
|
528
|
+
setSignalHandler(() => {
|
|
529
|
+
stopped = true;
|
|
530
|
+
});
|
|
531
|
+
try {
|
|
532
|
+
let record = readRunRecord(runId, deps);
|
|
533
|
+
if (!record) {
|
|
534
|
+
deps.output.write(`Run not found: ${runId}
|
|
535
|
+
`);
|
|
536
|
+
return 1;
|
|
537
|
+
}
|
|
538
|
+
printStatusTick(record, deps);
|
|
539
|
+
while (!stopped && isRunningStatus(record.status)) {
|
|
540
|
+
await sleep(intervalMs);
|
|
541
|
+
if (stopped) break;
|
|
542
|
+
record = checkStaleRun(runId, deps);
|
|
543
|
+
if (!record) {
|
|
544
|
+
deps.output.write(`Run not found: ${runId}
|
|
545
|
+
`);
|
|
546
|
+
return 1;
|
|
547
|
+
}
|
|
548
|
+
printStatusTick(record, deps);
|
|
549
|
+
}
|
|
550
|
+
if (stopped) {
|
|
551
|
+
deps.output.write(`watch stopped by signal
|
|
552
|
+
`);
|
|
553
|
+
}
|
|
554
|
+
return 0;
|
|
555
|
+
} finally {
|
|
556
|
+
clearSignalHandler();
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
function parseLogsArgs(args, onTail) {
|
|
560
|
+
let runId = "";
|
|
561
|
+
for (let i = 0; i < args.length; i++) {
|
|
562
|
+
const arg = args[i];
|
|
563
|
+
if (arg === "--tail") {
|
|
564
|
+
const next = args[i + 1];
|
|
565
|
+
if (next && /^\d+$/.test(next)) {
|
|
566
|
+
onTail(Number(next));
|
|
567
|
+
i += 1;
|
|
568
|
+
}
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
if (arg.startsWith("--tail=")) {
|
|
572
|
+
const value = arg.slice("--tail=".length);
|
|
573
|
+
if (/^\d+$/.test(value)) onTail(Number(value));
|
|
574
|
+
continue;
|
|
575
|
+
}
|
|
576
|
+
if (!arg.startsWith("-") && !runId) {
|
|
577
|
+
runId = arg;
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return runId;
|
|
581
|
+
}
|
|
582
|
+
async function runAgentLogsCommand(args, deps) {
|
|
583
|
+
let tailCount;
|
|
584
|
+
const runId = parseLogsArgs(args, (count) => {
|
|
585
|
+
tailCount = count;
|
|
586
|
+
});
|
|
587
|
+
if (!runId) {
|
|
588
|
+
deps.output.write("Usage: nolo agent logs <runId> [--tail N]\n");
|
|
589
|
+
return 1;
|
|
590
|
+
}
|
|
591
|
+
const record = readRunRecord(runId, deps) ?? findRunRecordByPid(Number(runId), deps);
|
|
592
|
+
if (!record) {
|
|
593
|
+
deps.output.write(`Run not found: ${runId}
|
|
594
|
+
`);
|
|
595
|
+
return 1;
|
|
596
|
+
}
|
|
597
|
+
const fs = deps.fs ?? nodeFs;
|
|
598
|
+
if (!fs.existsSync(record.logPath)) {
|
|
599
|
+
deps.output.write(`Log not found: ${record.logPath}
|
|
600
|
+
`);
|
|
601
|
+
return 1;
|
|
602
|
+
}
|
|
603
|
+
const content = readLogContent(record.logPath, tailCount, deps);
|
|
604
|
+
deps.output.write(content);
|
|
605
|
+
return 0;
|
|
606
|
+
}
|
|
607
|
+
async function runSignalCommand(args, signal, verb, deps) {
|
|
608
|
+
const target = args[0];
|
|
609
|
+
if (!target) {
|
|
610
|
+
deps.output.write(`Usage: nolo agent ${verb} <runId|pid>
|
|
611
|
+
`);
|
|
612
|
+
return 1;
|
|
613
|
+
}
|
|
614
|
+
const record = findRunRecord(target, deps);
|
|
615
|
+
if (!record) {
|
|
616
|
+
deps.output.write(`Run not found: ${target}
|
|
617
|
+
`);
|
|
618
|
+
return 1;
|
|
619
|
+
}
|
|
620
|
+
if (typeof record.pid !== "number") {
|
|
621
|
+
deps.output.write(`Run has no pid: ${record.runId}
|
|
622
|
+
`);
|
|
623
|
+
return 1;
|
|
624
|
+
}
|
|
625
|
+
const kill = deps.kill ?? ((pid, sig) => process.kill(pid, sig));
|
|
626
|
+
try {
|
|
627
|
+
kill(record.pid, signal);
|
|
628
|
+
} catch (error) {
|
|
629
|
+
const code = error.code;
|
|
630
|
+
if (code === "ESRCH") {
|
|
631
|
+
deps.output.write(`Process ${record.pid} already exited.
|
|
632
|
+
`);
|
|
633
|
+
} else {
|
|
634
|
+
deps.output.write(`Failed to ${verb} ${record.runId}: ${error}
|
|
635
|
+
`);
|
|
636
|
+
return 1;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
finalizeRunRecord(record.runId, { status: "killed" }, deps);
|
|
640
|
+
deps.output.write(`Sent ${signal} to ${record.runId} (pid ${record.pid}).
|
|
641
|
+
`);
|
|
642
|
+
return 0;
|
|
643
|
+
}
|
|
644
|
+
async function runAgentStopCommand(args, deps) {
|
|
645
|
+
return runSignalCommand(args, "SIGTERM", "stop", deps);
|
|
646
|
+
}
|
|
647
|
+
async function runAgentKillCommand(args, deps) {
|
|
648
|
+
return runSignalCommand(args, "SIGKILL", "kill", deps);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
export {
|
|
652
|
+
resolveNoloHome,
|
|
653
|
+
resolveRunsDir,
|
|
654
|
+
resolveRunRecordPath,
|
|
655
|
+
resolveRunLogPath,
|
|
656
|
+
defaultGenerateRunId,
|
|
657
|
+
writeRunRecord,
|
|
658
|
+
createRunActivityTracker,
|
|
659
|
+
readRunRecord,
|
|
660
|
+
listRunRecords,
|
|
661
|
+
findRunRecordByPid,
|
|
662
|
+
findRunRecord,
|
|
663
|
+
stripBackgroundFlag,
|
|
664
|
+
absolutizeSkillArgs,
|
|
665
|
+
rewriteMsgFileArg,
|
|
666
|
+
spawnLocalBackgroundRun,
|
|
667
|
+
finalizeRunRecord,
|
|
668
|
+
isPidGone,
|
|
669
|
+
checkStaleRun,
|
|
670
|
+
runAgentPsCommand,
|
|
671
|
+
runAgentStatusCommand,
|
|
672
|
+
runAgentLogsCommand,
|
|
673
|
+
runAgentStopCommand,
|
|
674
|
+
runAgentKillCommand
|
|
675
|
+
};
|