parawatch 0.3.17 → 0.3.18
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/adapters/codex.js +16 -1
- package/dist/adapters/jsonl.js +66 -0
- package/package.json +1 -1
package/dist/adapters/codex.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { applyStall } from "./claudeCode.js";
|
|
5
|
-
import { isObj, logActivity, oneLine, readHeadLines, readHeadText, readTailLines, str, textValues } from "./jsonl.js";
|
|
5
|
+
import { isObj, lastLineWith, logActivity, oneLine, readHeadLines, readHeadText, readTailLines, str, textValues } from "./jsonl.js";
|
|
6
6
|
const HEAD_BYTES = 128 * 1024;
|
|
7
7
|
const TAIL_BYTES = 256 * 1024;
|
|
8
8
|
// prompts can sit behind several inline base64 images on one line; read this far only when needed
|
|
@@ -85,6 +85,19 @@ export function codexSettings(lines) {
|
|
|
85
85
|
const options = [sandbox ? (SANDBOXES[sandbox] ?? sandbox) : undefined, approval ? (APPROVALS[approval] ?? approval) : undefined].filter((x) => !!x);
|
|
86
86
|
return { model: str(tc.model), effort: str(tc.effort) ?? str(tc.reasoning_effort) ?? (isObj(tc.collaboration_mode) && isObj(tc.collaboration_mode.settings) ? str(tc.collaboration_mode.settings.reasoning_effort) : undefined), options: options.length ? options : undefined };
|
|
87
87
|
}
|
|
88
|
+
/** What the last turn ran with, per log: a long log is searched once, then only where it grew. */
|
|
89
|
+
const middle = new Map();
|
|
90
|
+
/** The settings of a log whose last `turn_context` sits between its head and tail. */
|
|
91
|
+
export function settingsInMiddle(file) {
|
|
92
|
+
const size = fs.statSync(file).size;
|
|
93
|
+
const known = middle.get(file);
|
|
94
|
+
if (known && known.size === size)
|
|
95
|
+
return known.settings;
|
|
96
|
+
const found = lastLineWith(file, '"type":"turn_context"', known && known.size < size ? Math.max(0, known.size - 64) : 0);
|
|
97
|
+
const settings = found ? codexSettings([found]) : (known?.settings ?? {});
|
|
98
|
+
middle.set(file, { size, settings });
|
|
99
|
+
return settings;
|
|
100
|
+
}
|
|
88
101
|
export function readCodexLog(head, tail, rawHead) {
|
|
89
102
|
const meta = head.map(payload).find((p, i) => p && isObj(head[i]) && head[i].type === "session_meta");
|
|
90
103
|
const id = str(meta?.id) ?? str(meta?.session_id) ?? "unknown";
|
|
@@ -188,6 +201,8 @@ export const codex = {
|
|
|
188
201
|
if (fs.statSync(file).mtimeMs < opts.sinceMs)
|
|
189
202
|
continue;
|
|
190
203
|
const run = { ...readCodexLog(readHeadLines(file, HEAD_BYTES), readTailLines(file, TAIL_BYTES), () => readHeadText(file, RAW_HEAD_BYTES)), source: file };
|
|
204
|
+
if (!run.model)
|
|
205
|
+
Object.assign(run, settingsInMiddle(file));
|
|
191
206
|
run.activity = logActivity(file, opts.now);
|
|
192
207
|
applyStall(run, undefined, opts.now);
|
|
193
208
|
runs.push(run);
|
package/dist/adapters/jsonl.js
CHANGED
|
@@ -141,4 +141,70 @@ export function logActivity(file, now) {
|
|
|
141
141
|
seen.set(file, entry);
|
|
142
142
|
return activeMinutes(entry.times, now);
|
|
143
143
|
}
|
|
144
|
+
/**
|
|
145
|
+
* The last line holding `marker` in the part of a file after byte `from`, parsed, searching back
|
|
146
|
+
* from the end a megabyte at a time. For a record written once a turn that can sit anywhere in a
|
|
147
|
+
* log of many megabytes, between what the head and tail readings cover.
|
|
148
|
+
*/
|
|
149
|
+
export function lastLineWith(file, marker, from = 0, maxLine = 4 * 1024 * 1024) {
|
|
150
|
+
const size = fs.statSync(file).size;
|
|
151
|
+
const needle = Buffer.from(marker);
|
|
152
|
+
const CHUNK = 1024 * 1024;
|
|
153
|
+
const fd = fs.openSync(file, "r");
|
|
154
|
+
try {
|
|
155
|
+
let end = size;
|
|
156
|
+
while (end > from) {
|
|
157
|
+
const start = Math.max(from, end - CHUNK);
|
|
158
|
+
// overlap by the marker's length, so one cut in two is still found
|
|
159
|
+
const len = Math.min(size, end + needle.length) - start;
|
|
160
|
+
const buf = Buffer.alloc(len);
|
|
161
|
+
const n = fs.readSync(fd, buf, 0, len, start);
|
|
162
|
+
const at = buf.subarray(0, n).lastIndexOf(needle);
|
|
163
|
+
if (at >= 0)
|
|
164
|
+
return lineAt(fd, size, start + at, maxLine);
|
|
165
|
+
end = start;
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
finally {
|
|
170
|
+
fs.closeSync(fd);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/** The whole line around a byte position, parsed; undefined when it is too long or not JSON. */
|
|
174
|
+
function lineAt(fd, size, pos, maxLine) {
|
|
175
|
+
const STEP = 64 * 1024;
|
|
176
|
+
let lineStart = pos;
|
|
177
|
+
while (lineStart > 0 && pos - lineStart < maxLine) {
|
|
178
|
+
const from = Math.max(0, lineStart - STEP);
|
|
179
|
+
const buf = Buffer.alloc(lineStart - from);
|
|
180
|
+
fs.readSync(fd, buf, 0, buf.length, from);
|
|
181
|
+
const nl = buf.lastIndexOf(10);
|
|
182
|
+
if (nl >= 0) {
|
|
183
|
+
lineStart = from + nl + 1;
|
|
184
|
+
break;
|
|
185
|
+
}
|
|
186
|
+
lineStart = from;
|
|
187
|
+
}
|
|
188
|
+
let lineEnd = pos;
|
|
189
|
+
while (lineEnd < size && lineEnd - lineStart < maxLine) {
|
|
190
|
+
const buf = Buffer.alloc(Math.min(STEP, size - lineEnd));
|
|
191
|
+
fs.readSync(fd, buf, 0, buf.length, lineEnd);
|
|
192
|
+
const nl = buf.indexOf(10);
|
|
193
|
+
if (nl >= 0) {
|
|
194
|
+
lineEnd += nl;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
lineEnd += buf.length;
|
|
198
|
+
}
|
|
199
|
+
if (lineEnd - lineStart >= maxLine)
|
|
200
|
+
return undefined;
|
|
201
|
+
const buf = Buffer.alloc(lineEnd - lineStart);
|
|
202
|
+
fs.readSync(fd, buf, 0, buf.length, lineStart);
|
|
203
|
+
try {
|
|
204
|
+
return JSON.parse(buf.toString("utf8"));
|
|
205
|
+
}
|
|
206
|
+
catch {
|
|
207
|
+
return undefined;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
144
210
|
//# sourceMappingURL=jsonl.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "parawatch",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.18",
|
|
4
4
|
"description": "A control tower for people running many AI coding agents in parallel: see what is running, what needs you, and wake the right agent when you answer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ai-agents",
|