agent-tail-core 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -1
- package/dist/cli.mjs +1 -1
- package/dist/{commands-CzU-rrPM.mjs → commands-LpvDep8T.mjs} +21 -11
- package/dist/index.mjs +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
CLI and shared core for [agent-tail](https://agent-tail.vercel.app/) — pipes server output and browser console logs to log files your AI coding agents can read and `grep`.
|
|
4
4
|
|
|
5
|
+
> **Tip:** Install the umbrella [`agent-tail`](https://www.npmjs.com/package/agent-tail) package to get the CLI, Vite plugin, and Next.js plugin in one install: `npm install -D agent-tail`
|
|
6
|
+
|
|
5
7
|
## Quick start
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
npx agent-tail
|
|
10
|
+
npx agent-tail run 'fe: npm run dev' 'api: uvicorn main:app'
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
Each service gets its own log file (`fe.log`, `api.log`) plus a `combined.log` with all output interleaved. A `latest` symlink always points to the current session:
|
package/dist/cli.mjs
CHANGED
|
@@ -49,7 +49,11 @@ var LogManager = class {
|
|
|
49
49
|
fs.unlinkSync(latest_link);
|
|
50
50
|
} catch {}
|
|
51
51
|
const relative_target = path.relative(log_dir, session_dir);
|
|
52
|
-
|
|
52
|
+
try {
|
|
53
|
+
fs.symlinkSync(relative_target, latest_link);
|
|
54
|
+
} catch {
|
|
55
|
+
fs.writeFileSync(latest_link, session_dir);
|
|
56
|
+
}
|
|
53
57
|
}
|
|
54
58
|
prune_sessions(log_dir) {
|
|
55
59
|
try {
|
|
@@ -73,8 +77,12 @@ var LogManager = class {
|
|
|
73
77
|
const log_dir = path.resolve(project_root, this.options.logDir);
|
|
74
78
|
const latest_link = path.join(log_dir, "latest");
|
|
75
79
|
try {
|
|
76
|
-
const
|
|
77
|
-
|
|
80
|
+
const stat = fs.lstatSync(latest_link);
|
|
81
|
+
let target;
|
|
82
|
+
if (stat.isSymbolicLink()) target = fs.realpathSync(latest_link);
|
|
83
|
+
else if (stat.isFile()) target = fs.readFileSync(latest_link, "utf-8").trim();
|
|
84
|
+
else throw new Error("unexpected latest type");
|
|
85
|
+
if (fs.existsSync(target) && fs.statSync(target).isDirectory()) return target;
|
|
78
86
|
} catch {}
|
|
79
87
|
const log_path = this.initialize(project_root);
|
|
80
88
|
return path.dirname(log_path);
|
|
@@ -82,8 +90,8 @@ var LogManager = class {
|
|
|
82
90
|
check_gitignore(project_root) {
|
|
83
91
|
const gitignore_path = path.join(project_root, ".gitignore");
|
|
84
92
|
try {
|
|
85
|
-
const lines = fs.readFileSync(gitignore_path, "utf-8").split(
|
|
86
|
-
const log_dir = this.options.logDir;
|
|
93
|
+
const lines = fs.readFileSync(gitignore_path, "utf-8").split(/\r?\n/).map((l) => l.trim());
|
|
94
|
+
const log_dir = this.options.logDir.replace(/\\/g, "/");
|
|
87
95
|
const parts = log_dir.split("/");
|
|
88
96
|
let covered = false;
|
|
89
97
|
for (let i = 1; i <= parts.length; i++) {
|
|
@@ -171,7 +179,7 @@ function parse_service_configs(args) {
|
|
|
171
179
|
* Write data to a log stream and optionally to combined.log with a prefix.
|
|
172
180
|
*/
|
|
173
181
|
function write_to_logs(chunk, name, log_stream, combined_stream, excludes = []) {
|
|
174
|
-
const lines = chunk.toString().split(
|
|
182
|
+
const lines = chunk.toString().split(/\r?\n/);
|
|
175
183
|
for (let i = 0; i < lines.length; i++) if (lines[i].length > 0) {
|
|
176
184
|
if (excludes.length && should_exclude(lines[i], excludes)) continue;
|
|
177
185
|
log_stream.write(lines[i] + "\n");
|
|
@@ -197,13 +205,14 @@ function cmd_wrap(project_root, name, command, options = DEFAULT_CLI_OPTIONS) {
|
|
|
197
205
|
combined_stream = fs.createWriteStream(combined_file, { flags: "a" });
|
|
198
206
|
}
|
|
199
207
|
console.log(`${PREFIX} ${name} → ${log_file}`);
|
|
200
|
-
const child = spawn(
|
|
208
|
+
const child = spawn(command.join(" "), {
|
|
201
209
|
stdio: [
|
|
202
210
|
"inherit",
|
|
203
211
|
"pipe",
|
|
204
212
|
"pipe"
|
|
205
213
|
],
|
|
206
|
-
env: { ...process.env }
|
|
214
|
+
env: { ...process.env },
|
|
215
|
+
shell: true
|
|
207
216
|
});
|
|
208
217
|
child.stdout?.on("data", (chunk) => {
|
|
209
218
|
process.stdout.write(chunk);
|
|
@@ -261,16 +270,17 @@ function cmd_run(project_root, service_args, options = DEFAULT_CLI_OPTIONS) {
|
|
|
261
270
|
const tag = `${COLORS[i % COLORS.length]}[${svc.name}]${RESET}`;
|
|
262
271
|
const log_file = path.join(session_dir, `${svc.name}.log`);
|
|
263
272
|
const log_stream = fs.createWriteStream(log_file, { flags: "a" });
|
|
264
|
-
const child = spawn(
|
|
273
|
+
const child = spawn(svc.command, {
|
|
265
274
|
stdio: [
|
|
266
275
|
"inherit",
|
|
267
276
|
"pipe",
|
|
268
277
|
"pipe"
|
|
269
278
|
],
|
|
270
|
-
env: { ...process.env }
|
|
279
|
+
env: { ...process.env },
|
|
280
|
+
shell: true
|
|
271
281
|
});
|
|
272
282
|
function handle(target, chunk) {
|
|
273
|
-
const lines = chunk.toString().split(
|
|
283
|
+
const lines = chunk.toString().split(/\r?\n/);
|
|
274
284
|
for (let j = 0; j < lines.length; j++) if (lines[j].length > 0) {
|
|
275
285
|
if (options.excludes.length && should_exclude(lines[j], options.excludes)) continue;
|
|
276
286
|
log_stream.write(lines[j] + "\n");
|
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { a as resolve_session_dir, c as LogManager, i as parse_service_configs, l as should_exclude, n as cmd_run, o as DEFAULT_OPTIONS, r as cmd_wrap, s as resolve_options, t as cmd_init } from "./commands-
|
|
1
|
+
import { a as resolve_session_dir, c as LogManager, i as parse_service_configs, l as should_exclude, n as cmd_run, o as DEFAULT_OPTIONS, r as cmd_wrap, s as resolve_options, t as cmd_init } from "./commands-LpvDep8T.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/formatter.ts
|
|
4
4
|
function format_log_line(entry) {
|
|
5
|
-
return `[${entry.timestamp}] [${entry.level.toUpperCase().padEnd(7)}] ${entry.args.join(" ")}${entry.url ? ` (${entry.url})` : ""}${entry.stack ? `\n ${entry.stack.split(
|
|
5
|
+
return `[${entry.timestamp}] [${entry.level.toUpperCase().padEnd(7)}] ${entry.args.join(" ")}${entry.url ? ` (${entry.url})` : ""}${entry.stack ? `\n ${entry.stack.split(/\r?\n/).join("\n ")}` : ""}\n`;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
//#endregion
|