agentel 0.2.0 → 0.2.3
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 +161 -63
- package/agentlog-spec.md +42 -35
- package/bin/agentlog-recall.js +2 -0
- package/bin/agentlog.js +12 -0
- package/docs/code-reference.md +120 -34
- package/docs/history-source-handling.md +236 -81
- package/docs/release.md +8 -8
- package/package.json +5 -4
- package/src/archive.js +279 -20
- package/src/cli.js +3457 -511
- package/src/config.js +42 -1
- package/src/doctor.js +167 -10
- package/src/importers/gemini.js +369 -7
- package/src/importers.js +1893 -133
- package/src/mcp.js +4 -1
- package/src/parser-versions.js +37 -22
- package/src/paths.js +4 -2
- package/src/redaction.js +140 -17
- package/src/search.js +671 -52
- package/src/supervisor.js +206 -57
- package/src/sync.js +459 -12
package/src/supervisor.js
CHANGED
|
@@ -6,18 +6,21 @@ const { spawn } = require("child_process");
|
|
|
6
6
|
const { ensureBaseDirs, paths, readJson, writeJson } = require("./paths");
|
|
7
7
|
const { effectiveImportSources, loadConfig } = require("./config");
|
|
8
8
|
const { startCollector } = require("./collector");
|
|
9
|
-
const {
|
|
10
|
-
const { hasRemoteTarget, syncArchiveIfConfigured } = require("./sync");
|
|
9
|
+
const { hasRemoteTarget } = require("./sync");
|
|
11
10
|
|
|
12
11
|
let tickRunning = false;
|
|
12
|
+
const CHILD_MAX_OUTPUT_BYTES = 10 * 1024 * 1024;
|
|
13
|
+
const CHILD_TIMEOUT_MS = 10 * 60 * 1000;
|
|
14
|
+
const CHILD_KILL_GRACE_MS = 2000;
|
|
13
15
|
|
|
14
|
-
function startSupervisorDetached() {
|
|
15
|
-
const p = paths();
|
|
16
|
+
function startSupervisorDetached(env = process.env) {
|
|
17
|
+
const p = paths(env);
|
|
16
18
|
ensureBaseDirs(p);
|
|
17
19
|
const out = fs.openSync(path.join(p.logs, "supervisor.log"), "a");
|
|
18
20
|
const err = fs.openSync(path.join(p.logs, "supervisor.err.log"), "a");
|
|
19
21
|
const child = spawn(process.execPath, [path.resolve(__dirname, "..", "bin", "agentlog.js"), "start", "--foreground"], {
|
|
20
22
|
detached: true,
|
|
23
|
+
env,
|
|
21
24
|
stdio: ["ignore", out, err]
|
|
22
25
|
});
|
|
23
26
|
child.unref();
|
|
@@ -39,11 +42,7 @@ async function runSupervisorForeground(env = process.env) {
|
|
|
39
42
|
if (stopping) return;
|
|
40
43
|
stopping = true;
|
|
41
44
|
log("supervisor stopping", env);
|
|
42
|
-
|
|
43
|
-
fs.unlinkSync(p.pid);
|
|
44
|
-
} catch {
|
|
45
|
-
// Already gone.
|
|
46
|
-
}
|
|
45
|
+
removeSupervisorPidIfOwned(process.pid, env);
|
|
47
46
|
process.exit(0);
|
|
48
47
|
};
|
|
49
48
|
process.on("SIGTERM", stop);
|
|
@@ -64,49 +63,200 @@ async function runSupervisorForeground(env = process.env) {
|
|
|
64
63
|
async function tick(env) {
|
|
65
64
|
tickRunning = true;
|
|
66
65
|
try {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
66
|
+
try {
|
|
67
|
+
const cfg = loadConfig(env);
|
|
68
|
+
const sources = effectiveImportSources(cfg);
|
|
69
|
+
const importSources = Array.isArray(sources) && sources.length ? sources : ["all"];
|
|
70
|
+
for (const source of importSources) {
|
|
71
|
+
try {
|
|
72
|
+
const imports = await importSourceInChild(source, cfg, env);
|
|
73
|
+
for (const result of imports) {
|
|
74
|
+
const summary = supervisorImportResultLog(result, source);
|
|
75
|
+
if (summary) log(summary, env);
|
|
76
|
+
if (result.errors?.length) log(`${result.provider} import errors from ${source}: ${result.errors.length}`, env);
|
|
77
|
+
}
|
|
78
|
+
} catch (error) {
|
|
79
|
+
log(`${source} history import skipped: ${error.message}`, env);
|
|
78
80
|
}
|
|
79
|
-
} catch (error) {
|
|
80
|
-
log(`${source} history import skipped: ${error.message}`, env);
|
|
81
81
|
}
|
|
82
|
+
} catch (error) {
|
|
83
|
+
log(`history import skipped: ${error.message}`, env);
|
|
82
84
|
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
log(`history import skipped: ${error.message}`, env);
|
|
85
|
-
}
|
|
86
85
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
86
|
+
try {
|
|
87
|
+
const result = await reindexInChild(env);
|
|
88
|
+
if (result.index?.docCount != null) log(`index ready (${result.index.docCount} chunk(s))`, env);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
log(`index skipped: ${error.message}`, env);
|
|
91
|
+
}
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
93
|
+
try {
|
|
94
|
+
const cfg = loadConfig(env);
|
|
95
|
+
if (hasRemoteTarget(cfg, env) && shouldRunScheduledSync(cfg, env)) {
|
|
96
|
+
markScheduledSyncAttempt(env);
|
|
97
|
+
const result = await syncInChild(env);
|
|
98
|
+
if (result.configured !== false && (result.uploaded || result.errors?.length)) {
|
|
99
|
+
log(`remote sync uploaded=${result.uploaded} current=${result.skipped} retried=${result.retried || 0} errors=${result.errors.length}`, env);
|
|
100
|
+
}
|
|
101
101
|
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
log(`remote sync skipped: ${error.message}`, env);
|
|
102
104
|
}
|
|
103
|
-
} catch (error) {
|
|
104
|
-
log(`remote sync skipped: ${error.message}`, env);
|
|
105
105
|
} finally {
|
|
106
106
|
tickRunning = false;
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
function importSourceInChild(source, config, env = process.env) {
|
|
111
|
+
const options = supervisorImportOptionsForSource(source, config);
|
|
112
|
+
const title = childProcessTitle("import", source);
|
|
113
|
+
const script = `
|
|
114
|
+
process.title = ${JSON.stringify(title)};
|
|
115
|
+
try {
|
|
116
|
+
const { importCliHistory } = require(${JSON.stringify(path.join(__dirname, "importers"))});
|
|
117
|
+
const results = importCliHistory(${JSON.stringify(options)}, process.env);
|
|
118
|
+
process.stdout.write(JSON.stringify(results));
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(error && error.message ? error.message : String(error));
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
`;
|
|
124
|
+
return runJsonChild(script, env, `${source} import`, title);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function reindexInChild(env = process.env) {
|
|
128
|
+
const title = childProcessTitle("index");
|
|
129
|
+
const script = `
|
|
130
|
+
process.title = ${JSON.stringify(title)};
|
|
131
|
+
try {
|
|
132
|
+
const { reindexIfNeeded } = require(${JSON.stringify(path.join(__dirname, "search"))});
|
|
133
|
+
const result = reindexIfNeeded(process.env, { rebuildInProcess: true });
|
|
134
|
+
process.stdout.write(JSON.stringify({
|
|
135
|
+
paused: Boolean(result.paused),
|
|
136
|
+
rebuilt: Boolean(result.rebuilt),
|
|
137
|
+
index: result.index
|
|
138
|
+
? {
|
|
139
|
+
version: result.index.version,
|
|
140
|
+
builtAt: result.index.builtAt,
|
|
141
|
+
docCount: result.index.docCount,
|
|
142
|
+
avgDocLength: result.index.avgDocLength,
|
|
143
|
+
summaryOnly: true
|
|
144
|
+
}
|
|
145
|
+
: null
|
|
146
|
+
}));
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error(error && error.message ? error.message : String(error));
|
|
149
|
+
process.exit(1);
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
152
|
+
return runJsonChild(script, env, "index", title);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function syncInChild(env = process.env) {
|
|
156
|
+
const title = childProcessTitle("sync");
|
|
157
|
+
const script = `
|
|
158
|
+
process.title = ${JSON.stringify(title)};
|
|
159
|
+
(async () => {
|
|
160
|
+
const { syncArchiveIfConfigured } = require(${JSON.stringify(path.join(__dirname, "sync"))});
|
|
161
|
+
const result = await syncArchiveIfConfigured(process.env);
|
|
162
|
+
process.stdout.write(JSON.stringify(result));
|
|
163
|
+
})().catch((error) => {
|
|
164
|
+
console.error(error && error.message ? error.message : String(error));
|
|
165
|
+
process.exitCode = 1;
|
|
166
|
+
});
|
|
167
|
+
`;
|
|
168
|
+
return runJsonChild(script, env, "remote sync", title);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function runJsonChild(script, env = process.env, label = "child process", title = childProcessTitle("worker"), options = {}) {
|
|
172
|
+
return new Promise((resolve, reject) => {
|
|
173
|
+
const child = spawn(process.execPath, ["-e", script], {
|
|
174
|
+
argv0: title,
|
|
175
|
+
env,
|
|
176
|
+
stdio: ["ignore", "pipe", "pipe"]
|
|
177
|
+
});
|
|
178
|
+
const stdout = [];
|
|
179
|
+
const stderr = [];
|
|
180
|
+
let stdoutBytes = 0;
|
|
181
|
+
let stderrBytes = 0;
|
|
182
|
+
let settled = false;
|
|
183
|
+
let timedOut = false;
|
|
184
|
+
let killTimer = null;
|
|
185
|
+
const timeoutMs = normalizeChildTimeout(options.timeoutMs);
|
|
186
|
+
const timeoutTimer = setTimeout(() => {
|
|
187
|
+
timedOut = true;
|
|
188
|
+
child.kill("SIGTERM");
|
|
189
|
+
killTimer = setTimeout(() => child.kill("SIGKILL"), CHILD_KILL_GRACE_MS);
|
|
190
|
+
if (typeof killTimer.unref === "function") killTimer.unref();
|
|
191
|
+
}, timeoutMs);
|
|
192
|
+
if (typeof timeoutTimer.unref === "function") timeoutTimer.unref();
|
|
193
|
+
|
|
194
|
+
function clearTimers() {
|
|
195
|
+
clearTimeout(timeoutTimer);
|
|
196
|
+
if (killTimer) clearTimeout(killTimer);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function fail(error) {
|
|
200
|
+
if (settled) return;
|
|
201
|
+
settled = true;
|
|
202
|
+
clearTimers();
|
|
203
|
+
reject(error);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
child.stdout.on("data", (chunk) => {
|
|
207
|
+
stdoutBytes += chunk.length;
|
|
208
|
+
if (stdoutBytes > CHILD_MAX_OUTPUT_BYTES) {
|
|
209
|
+
child.kill("SIGTERM");
|
|
210
|
+
fail(new Error(`${label} output exceeded ${CHILD_MAX_OUTPUT_BYTES} bytes`));
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
stdout.push(chunk);
|
|
214
|
+
});
|
|
215
|
+
child.stderr.on("data", (chunk) => {
|
|
216
|
+
stderrBytes += chunk.length;
|
|
217
|
+
if (stderrBytes <= CHILD_MAX_OUTPUT_BYTES) stderr.push(chunk);
|
|
218
|
+
});
|
|
219
|
+
child.on("error", fail);
|
|
220
|
+
child.on("close", (code, signal) => {
|
|
221
|
+
if (settled) return;
|
|
222
|
+
settled = true;
|
|
223
|
+
clearTimers();
|
|
224
|
+
if (timedOut) {
|
|
225
|
+
reject(new Error(`${label} timed out after ${formatDuration(timeoutMs)}`));
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
const output = Buffer.concat(stdout).toString("utf8").trim();
|
|
229
|
+
const errorOutput = Buffer.concat(stderr).toString("utf8").trim();
|
|
230
|
+
if (code !== 0) {
|
|
231
|
+
reject(new Error(errorOutput || `${label} exited with ${signal || `status ${code}`}`));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
resolve(output ? JSON.parse(output) : null);
|
|
236
|
+
} catch (error) {
|
|
237
|
+
reject(new Error(`${label} returned invalid JSON: ${error.message}`));
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
function normalizeChildTimeout(value) {
|
|
244
|
+
const timeout = Number(value || CHILD_TIMEOUT_MS);
|
|
245
|
+
return Number.isFinite(timeout) && timeout > 0 ? timeout : CHILD_TIMEOUT_MS;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function formatDuration(ms) {
|
|
249
|
+
const seconds = Math.round(ms / 1000);
|
|
250
|
+
if (seconds < 60) return `${seconds}s`;
|
|
251
|
+
const minutes = Math.round(seconds / 60);
|
|
252
|
+
return `${minutes}m`;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function childProcessTitle(kind, detail = "") {
|
|
256
|
+
const suffix = detail ? `-${String(detail).replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "")}` : "";
|
|
257
|
+
return `agentlog-${kind}${suffix}`.slice(0, 64);
|
|
258
|
+
}
|
|
259
|
+
|
|
110
260
|
function shouldRunScheduledSync(config, env = process.env, now = new Date()) {
|
|
111
261
|
const intervalMinutes = Number(config?.sync?.intervalMinutes ?? 30);
|
|
112
262
|
if (!Number.isFinite(intervalMinutes) || intervalMinutes <= 0) return false;
|
|
@@ -143,7 +293,6 @@ function supervisorImportResultLog(result, source) {
|
|
|
143
293
|
}
|
|
144
294
|
|
|
145
295
|
function stopSupervisor(env = process.env) {
|
|
146
|
-
const p = paths(env);
|
|
147
296
|
const pid = readPid(env);
|
|
148
297
|
if (!pid) return false;
|
|
149
298
|
try {
|
|
@@ -151,11 +300,7 @@ function stopSupervisor(env = process.env) {
|
|
|
151
300
|
return true;
|
|
152
301
|
} catch (error) {
|
|
153
302
|
if (error?.code !== "EPERM") {
|
|
154
|
-
|
|
155
|
-
fs.unlinkSync(p.pid);
|
|
156
|
-
} catch {
|
|
157
|
-
// No-op.
|
|
158
|
-
}
|
|
303
|
+
removeSupervisorPidIfOwned(pid, env);
|
|
159
304
|
}
|
|
160
305
|
return false;
|
|
161
306
|
}
|
|
@@ -178,6 +323,16 @@ function readPid(env = process.env) {
|
|
|
178
323
|
}
|
|
179
324
|
}
|
|
180
325
|
|
|
326
|
+
function removeSupervisorPidIfOwned(pid, env = process.env) {
|
|
327
|
+
if (!pid || readPid(env) !== Number(pid)) return false;
|
|
328
|
+
try {
|
|
329
|
+
fs.unlinkSync(paths(env).pid);
|
|
330
|
+
return true;
|
|
331
|
+
} catch {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
181
336
|
function isAlive(pid) {
|
|
182
337
|
try {
|
|
183
338
|
process.kill(pid, 0);
|
|
@@ -188,17 +343,6 @@ function isAlive(pid) {
|
|
|
188
343
|
}
|
|
189
344
|
}
|
|
190
345
|
|
|
191
|
-
function loadImportCliHistory() {
|
|
192
|
-
const modulePath = require.resolve("./importers");
|
|
193
|
-
const loaded = require(modulePath);
|
|
194
|
-
if (typeof loaded.importCliHistory === "function") return loaded.importCliHistory;
|
|
195
|
-
|
|
196
|
-
delete require.cache[modulePath];
|
|
197
|
-
const reloaded = require(modulePath);
|
|
198
|
-
if (typeof reloaded.importCliHistory === "function") return reloaded.importCliHistory;
|
|
199
|
-
throw new Error("importer module did not expose importCliHistory; restart agentlog or reinstall");
|
|
200
|
-
}
|
|
201
|
-
|
|
202
346
|
function log(message, env = process.env) {
|
|
203
347
|
const p = paths(env);
|
|
204
348
|
ensureBaseDirs(p);
|
|
@@ -212,6 +356,11 @@ module.exports = {
|
|
|
212
356
|
supervisorImportResultLog,
|
|
213
357
|
supervisorImportOptionsForSource,
|
|
214
358
|
supervisorStatus,
|
|
359
|
+
removeSupervisorPidIfOwned,
|
|
215
360
|
shouldRunScheduledSync,
|
|
361
|
+
importSourceInChild,
|
|
362
|
+
reindexInChild,
|
|
363
|
+
runJsonChild,
|
|
364
|
+
syncInChild,
|
|
216
365
|
tick
|
|
217
366
|
};
|