pi-harness-runtime 1.1.254 → 1.1.256
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/index.ts +43 -21
- package/package.json +1 -1
- package/packages/a2a-adapter/package.json +1 -1
- package/packages/agent-policy/package.json +1 -1
- package/packages/architecture-generator/package.json +1 -1
- package/packages/auth/package.json +1 -1
- package/packages/autonomous-refactor/package.json +1 -1
- package/packages/autonomous-runtime/package.json +1 -1
- package/packages/cache-strategy/package.json +1 -1
- package/packages/capability-registry/package.json +1 -1
- package/packages/checkpoint/package.json +1 -1
- package/packages/cli-plugin-sdk/package.json +1 -1
- package/packages/clipboard/package.json +1 -1
- package/packages/clipboard-plugin/package.json +1 -1
- package/packages/code-generation/package.json +1 -1
- package/packages/code-review/package.json +1 -1
- package/packages/codex-adapter/package.json +1 -1
- package/packages/codex-watcher/dist/src/index.d.ts.map +1 -1
- package/packages/codex-watcher/dist/src/index.js +20 -11
- package/packages/codex-watcher/dist/src/index.js.map +1 -1
- package/packages/codex-watcher/package.json +1 -1
- package/packages/codex-watcher/src/index.ts +23 -12
- package/packages/codex-watcher/test/watch.test.ts +303 -0
- package/packages/config-capture/package.json +1 -1
- package/packages/context-compiler/package.json +1 -1
- package/packages/context-discovery/package.json +1 -1
- package/packages/context-manager/package.json +1 -1
- package/packages/cookie-sanitizer/package.json +1 -1
- package/packages/cost-optimizer/package.json +1 -1
- package/packages/dependency-analyzer/package.json +1 -1
- package/packages/django-plugin/package.json +1 -1
- package/packages/doc-generator/package.json +1 -1
- package/packages/e2e-tester/package.json +1 -1
- package/packages/evaluation-engine/package.json +1 -1
- package/packages/evaluation-runner/package.json +1 -1
- package/packages/event-bus/package.json +1 -1
- package/packages/event-store/package.json +1 -1
- package/packages/experience-replay/package.json +1 -1
- package/packages/feedback-collector/package.json +1 -1
- package/packages/file-copy-helper/package.json +1 -1
- package/packages/framework-detector/package.json +1 -1
- package/packages/framework-plugin-sdk/package.json +1 -1
- package/packages/frappe-plugin/package.json +1 -1
- package/packages/generic-web-plugin/package.json +1 -1
- package/packages/health-monitor/package.json +1 -1
- package/packages/intent-analyzer/package.json +1 -1
- package/packages/jev-judge/package.json +1 -1
- package/packages/knowledge-graph/package.json +1 -1
- package/packages/knowledge-retrieval/package.json +1 -1
- package/packages/laravel-plugin/package.json +1 -1
- package/packages/learning-engine/package.json +1 -1
- package/packages/mcp-adapter/package.json +1 -1
- package/packages/memory-engine/package.json +1 -1
- package/packages/milestone-manager/package.json +1 -1
- package/packages/model-registry/package.json +1 -1
- package/packages/nextjs-plugin/package.json +1 -1
- package/packages/notification/package.json +1 -1
- package/packages/observability/package.json +1 -1
- package/packages/okf-indexer/package.json +1 -1
- package/packages/paperclip-integration/package.json +1 -1
- package/packages/performance-optimizer/package.json +1 -1
- package/packages/privilege-broker/package.json +1 -1
- package/packages/project-analyzer/package.json +1 -1
- package/packages/project-bootstrap/package.json +1 -1
- package/packages/projection-engine/package.json +1 -1
- package/packages/prompt-compiler/package.json +1 -1
- package/packages/prompt-versioning/package.json +1 -1
- package/packages/provider-adapter-sdk/package.json +1 -1
- package/packages/provider-router/package.json +1 -1
- package/packages/provider-selector/package.json +1 -1
- package/packages/providers/package.json +1 -1
- package/packages/qdrant-skills/package.json +1 -1
- package/packages/quota-manager/package.json +1 -1
- package/packages/rate-limiter/package.json +1 -1
- package/packages/react-vite-plugin/package.json +1 -1
- package/packages/release-manager/package.json +1 -1
- package/packages/requirement-compiler/package.json +1 -1
- package/packages/runtime/package.json +1 -1
- package/packages/scheduler/package.json +1 -1
- package/packages/scheduler-adapter/package.json +1 -1
- package/packages/session/package.json +1 -1
- package/packages/session-api/package.json +1 -1
- package/packages/session-export/package.json +1 -1
- package/packages/shared-context/package.json +1 -1
- package/packages/skill-mcp-client/package.json +1 -1
- package/packages/skill-registry/package.json +1 -1
- package/packages/skills/package.json +1 -1
- package/packages/sprint-planner/package.json +1 -1
- package/packages/subscription-engine/package.json +1 -1
- package/packages/task-compiler/package.json +1 -1
- package/packages/telegram/package.json +1 -1
- package/packages/tencentdb-memory/package.json +1 -1
- package/packages/tencentdb-sync/package.json +1 -1
- package/packages/test-data-generator/package.json +1 -1
- package/packages/test-generator/package.json +1 -1
- package/packages/todo-bd-sync/package.json +1 -1
- package/packages/token-estimation/package.json +1 -1
- package/packages/token-optimizer/package.json +1 -1
- package/packages/trajectory/package.json +1 -1
- package/packages/tui/package.json +1 -1
- package/packages/types/package.json +1 -1
- package/packages/webmcp-tester/package.json +1 -1
- package/packages/workflow-events/package.json +1 -1
- package/packages/workspace-scanner/package.json +1 -1
- package/packages/worktree/package.json +1 -1
- package/packages/write-review/package.json +1 -1
package/index.ts
CHANGED
|
@@ -1981,13 +1981,37 @@ export default function (pi: ExtensionAPI) {
|
|
|
1981
1981
|
// Lightweight: polls ~/.codex/sessions/ every 5s, no LangChain daemon.
|
|
1982
1982
|
// On plan detected: sends Telegram alert + steer message to pi.dev.
|
|
1983
1983
|
try {
|
|
1984
|
-
//
|
|
1985
|
-
//
|
|
1986
|
-
|
|
1987
|
-
const sessionAcceptedOrdinals = new Map<string, number>();
|
|
1988
|
-
const sessionDeliveredOrdinals = new Map<string, number>();
|
|
1984
|
+
// Pending delivery queue: messages waiting to be delivered to Pi
|
|
1985
|
+
// Keyed by sessionId, value is array of {ordinal, text, delivered}
|
|
1986
|
+
const pendingDelivery = new Map<string, Array<{ordinal: number; text: string; delivered: boolean}>>();
|
|
1989
1987
|
let currentSessionId: string | null = null;
|
|
1990
1988
|
|
|
1989
|
+
// Process pending delivery queue - try to deliver queued messages
|
|
1990
|
+
function processDeliveryQueue(sessionId: string) {
|
|
1991
|
+
const queue = pendingDelivery.get(sessionId);
|
|
1992
|
+
if (!queue || queue.length === 0) return;
|
|
1993
|
+
|
|
1994
|
+
// Try to deliver pending messages
|
|
1995
|
+
for (const item of queue) {
|
|
1996
|
+
if (item.delivered) continue;
|
|
1997
|
+
|
|
1998
|
+
try {
|
|
1999
|
+
const planContent = `[Codex plan detected — ordinal ${item.ordinal}]\n\n${item.text}`;
|
|
2000
|
+
pi.sendUserMessage(planContent, { deliverAs: "steer" });
|
|
2001
|
+
item.delivered = true;
|
|
2002
|
+
logToFile("[codex]", `Delivered queued ordinal ${item.ordinal} to Pi`);
|
|
2003
|
+
} catch {
|
|
2004
|
+
// Still failing, will retry on next poll
|
|
2005
|
+
logToFile("[codex]", `Retry failed for ordinal ${item.ordinal}`);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
2009
|
+
// Clean up delivered messages from the front of the queue
|
|
2010
|
+
while (queue.length > 0 && queue[0].delivered) {
|
|
2011
|
+
queue.shift();
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
|
|
1991
2015
|
const codexHandle = startWatcher({
|
|
1992
2016
|
pollIntervalMs: 5000,
|
|
1993
2017
|
maxIdlePolls: Infinity,
|
|
@@ -1995,10 +2019,9 @@ export default function (pi: ExtensionAPI) {
|
|
|
1995
2019
|
if (session) {
|
|
1996
2020
|
logToFile("[codex]", `Session started: ${session.threadName}`);
|
|
1997
2021
|
currentSessionId = session.id;
|
|
1998
|
-
// Initialize
|
|
1999
|
-
if (!
|
|
2000
|
-
|
|
2001
|
-
sessionDeliveredOrdinals.set(session.id, 0);
|
|
2022
|
+
// Initialize queue for new session
|
|
2023
|
+
if (!pendingDelivery.has(session.id)) {
|
|
2024
|
+
pendingDelivery.set(session.id, []);
|
|
2002
2025
|
}
|
|
2003
2026
|
nc("CodexSessionStarted", {
|
|
2004
2027
|
requirement: session.threadName || "Codex session",
|
|
@@ -2009,6 +2032,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
2009
2032
|
}
|
|
2010
2033
|
},
|
|
2011
2034
|
onMessages: (msgs) => {
|
|
2035
|
+
const sessionId = currentSessionId;
|
|
2036
|
+
if (!sessionId) return;
|
|
2037
|
+
|
|
2038
|
+
// First, try to deliver any pending messages
|
|
2039
|
+
processDeliveryQueue(sessionId);
|
|
2040
|
+
|
|
2012
2041
|
for (const msg of msgs) {
|
|
2013
2042
|
// Skip user messages — only track assistant plans
|
|
2014
2043
|
if (msg.role !== "assistant") continue;
|
|
@@ -2016,14 +2045,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
2016
2045
|
const classification = classifyMessage(msg.text);
|
|
2017
2046
|
if (classification !== "plan" && classification !== "mixed") continue;
|
|
2018
2047
|
|
|
2019
|
-
// Get session-specific ordinal tracking (use currentSessionId from watcher)
|
|
2020
|
-
const sessionId = currentSessionId;
|
|
2021
|
-
if (!sessionId) continue;
|
|
2022
|
-
|
|
2023
|
-
// Check if we've already delivered this ordinal to this session
|
|
2024
|
-
const deliveredOrdinal = sessionDeliveredOrdinals.get(sessionId) ?? 0;
|
|
2025
|
-
if (msg.ordinal <= deliveredOrdinal) continue;
|
|
2026
|
-
|
|
2027
2048
|
// Emit Telegram alert with preview (include more of the plan)
|
|
2028
2049
|
const planPreview = msg.text.slice(0, 800) + (msg.text.length > 800 ? "\n... (full plan forwarded to Pi)" : "");
|
|
2029
2050
|
nc("CodexPlanDetected", {
|
|
@@ -2035,12 +2056,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
2035
2056
|
const planContent = `[Codex plan detected — ordinal ${msg.ordinal}]\n\n${msg.text}`;
|
|
2036
2057
|
try {
|
|
2037
2058
|
pi.sendUserMessage(planContent, { deliverAs: "steer" });
|
|
2038
|
-
// Mark as delivered only if Pi send succeeds
|
|
2039
|
-
sessionDeliveredOrdinals.set(sessionId, msg.ordinal);
|
|
2040
2059
|
logToFile("[codex]", `Delivered ordinal ${msg.ordinal} to Pi`);
|
|
2041
2060
|
} catch {
|
|
2042
|
-
|
|
2043
|
-
|
|
2061
|
+
// Add to pending queue for retry
|
|
2062
|
+
const queue = pendingDelivery.get(sessionId) ?? [];
|
|
2063
|
+
queue.push({ ordinal: msg.ordinal, text: msg.text, delivered: false });
|
|
2064
|
+
pendingDelivery.set(sessionId, queue);
|
|
2065
|
+
logToFile("[codex]", `Delivery failed for ordinal ${msg.ordinal}, queued for retry`);
|
|
2044
2066
|
}
|
|
2045
2067
|
}
|
|
2046
2068
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pi-harness/capability-registry",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.256",
|
|
4
4
|
"description": "Capability Registry for pi-harness-runtime — tracks model capabilities and enables capability-based routing",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAM7D,oFAAoF;AACpF,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAKH,OAAO,EAAE,eAAe,EAAoB,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,KAAK,EACX,YAAY,EACZ,YAAY,EACZ,aAAa,EACb,cAAc,EACd,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,CAAC;AAC1E,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAM7D,oFAAoF;AACpF,wBAAsB,kBAAkB,CACvC,WAAW,EAAE,MAAM,EACnB,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAO,GACpD,OAAO,CAAC,YAAY,EAAE,CAAC,CAkBzB;AAMD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAiMxE"}
|
|
@@ -67,7 +67,7 @@ export function startWatcher(options = {}) {
|
|
|
67
67
|
let currentPath = null;
|
|
68
68
|
let currentOrdinal = 0;
|
|
69
69
|
let currentBytePos = 0; // Track byte position we're at in the file
|
|
70
|
-
let
|
|
70
|
+
let pendingBytes = null; // Raw bytes buffer for UTF-8 safety
|
|
71
71
|
let pollCount = 0;
|
|
72
72
|
const maxIdlePolls = options.maxIdlePolls ?? Infinity;
|
|
73
73
|
// Track last known session to detect changes
|
|
@@ -108,11 +108,12 @@ export function startWatcher(options = {}) {
|
|
|
108
108
|
currentPath = path;
|
|
109
109
|
currentOrdinal = 0;
|
|
110
110
|
currentBytePos = 0;
|
|
111
|
-
|
|
111
|
+
pendingBytes = null; // Reset pending buffer for new file
|
|
112
112
|
return currentFd;
|
|
113
113
|
}
|
|
114
114
|
/**
|
|
115
115
|
* Read new lines from the file, preserving incomplete records.
|
|
116
|
+
* Uses raw byte buffering to safely handle UTF-8 split across reads.
|
|
116
117
|
* Returns complete lines only; incomplete trailing data is kept for next read.
|
|
117
118
|
*/
|
|
118
119
|
function readNewLines(fd) {
|
|
@@ -125,18 +126,26 @@ export function startWatcher(options = {}) {
|
|
|
125
126
|
}
|
|
126
127
|
const buf = Buffer.alloc(bytesToRead);
|
|
127
128
|
const bytesRead = readSync(fd, buf, 0, buf.length, currentBytePos);
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
// If text doesn't end with newline, last element is incomplete - save it
|
|
134
|
-
if (!text.endsWith("\n")) {
|
|
135
|
-
pendingText = lines.pop() ?? "";
|
|
129
|
+
// Buffer raw bytes for UTF-8 safety
|
|
130
|
+
if (pendingBytes) {
|
|
131
|
+
// Combine with previous incomplete buffer
|
|
132
|
+
const combined = Buffer.concat([pendingBytes, buf.subarray(0, bytesRead)]);
|
|
133
|
+
pendingBytes = combined;
|
|
136
134
|
}
|
|
137
135
|
else {
|
|
138
|
-
|
|
136
|
+
pendingBytes = buf.subarray(0, bytesRead);
|
|
139
137
|
}
|
|
138
|
+
// Decode the complete buffer
|
|
139
|
+
const text = pendingBytes.toString("utf8");
|
|
140
|
+
// Split by newline to find complete records
|
|
141
|
+
const lines = text.split("\n");
|
|
142
|
+
// If buffer doesn't end with newline, last element is incomplete
|
|
143
|
+
if (!pendingBytes.toString("utf8").endsWith("\n")) {
|
|
144
|
+
// Keep the incomplete line for next read (already in pendingBytes)
|
|
145
|
+
return { lines: [], newBytePos: currentBytePos };
|
|
146
|
+
}
|
|
147
|
+
// All lines are complete - emit them and clear buffer
|
|
148
|
+
pendingBytes = null;
|
|
140
149
|
// Filter out empty lines
|
|
141
150
|
const completeLines = lines.filter((l) => l.trim());
|
|
142
151
|
// Update our tracked position by bytes actually read
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAC7F,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAChE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAShE,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,gBAAgB,EAAE,CAAC;AAE7D,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,oFAAoF;AACpF,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACvC,WAAmB,EACnB,OAAO,GAA6C,EAAE;IAEtD,MAAM,EAAE,WAAW,GAAG,CAAC,EAAE,KAAK,EAAE,GAAG,OAAO,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,EAAE,CAAC;IAExC,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAElE,IAAI,QAAQ,GAAG,gBAAgB,CAAC,KAAK,EAAE,WAAW,CAAC,CAAC;IACpD,IAAI,KAAK;QAAE,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC;IAE7C,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG;YAC/B,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAChC,QAAQ,EAAE,IAAI;SACd,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AACjB,CAAC;AAED,8EAA8E;AAC9E,wBAAwB;AACxB,8EAA8E;AAE9E;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,YAAY,CAAC,OAAO,GAAmB,EAAE;IACxD,MAAM,EACL,cAAc,GAAG,IAAI,EACrB,QAAQ,EACR,UAAU,EACV,eAAe,GACf,GAAG,OAAO,CAAC;IAEZ,MAAM,IAAI,GAAG,QAAQ,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IACnD,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,SAAS,GAAuC,IAAI,CAAC;IACzD,IAAI,WAAW,GAAkB,IAAI,CAAC;IACtC,IAAI,cAAc,GAAG,CAAC,CAAC;IACvB,IAAI,cAAc,GAAG,CAAC,CAAC,CAAE,2CAA2C;IACpE,IAAI,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -91,7 +91,7 @@ export function startWatcher(options: WatcherOptions = {}): WatcherHandle {
|
|
|
91
91
|
let currentPath: string | null = null;
|
|
92
92
|
let currentOrdinal = 0;
|
|
93
93
|
let currentBytePos = 0; // Track byte position we're at in the file
|
|
94
|
-
let
|
|
94
|
+
let pendingBytes: Buffer | null = null; // Raw bytes buffer for UTF-8 safety
|
|
95
95
|
let pollCount = 0;
|
|
96
96
|
const maxIdlePolls = options.maxIdlePolls ?? Infinity;
|
|
97
97
|
|
|
@@ -137,12 +137,13 @@ export function startWatcher(options: WatcherOptions = {}): WatcherHandle {
|
|
|
137
137
|
currentPath = path;
|
|
138
138
|
currentOrdinal = 0;
|
|
139
139
|
currentBytePos = 0;
|
|
140
|
-
|
|
140
|
+
pendingBytes = null; // Reset pending buffer for new file
|
|
141
141
|
return currentFd;
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
/**
|
|
145
145
|
* Read new lines from the file, preserving incomplete records.
|
|
146
|
+
* Uses raw byte buffering to safely handle UTF-8 split across reads.
|
|
146
147
|
* Returns complete lines only; incomplete trailing data is kept for next read.
|
|
147
148
|
*/
|
|
148
149
|
function readNewLines(
|
|
@@ -162,21 +163,31 @@ export function startWatcher(options: WatcherOptions = {}): WatcherHandle {
|
|
|
162
163
|
|
|
163
164
|
const buf = Buffer.alloc(bytesToRead);
|
|
164
165
|
const bytesRead = readSync(fd, buf, 0, buf.length, currentBytePos);
|
|
165
|
-
const text = buf.subarray(0, bytesRead).toString("utf8");
|
|
166
166
|
|
|
167
|
-
//
|
|
168
|
-
|
|
167
|
+
// Buffer raw bytes for UTF-8 safety
|
|
168
|
+
if (pendingBytes) {
|
|
169
|
+
// Combine with previous incomplete buffer
|
|
170
|
+
const combined = Buffer.concat([pendingBytes, buf.subarray(0, bytesRead)]);
|
|
171
|
+
pendingBytes = combined;
|
|
172
|
+
} else {
|
|
173
|
+
pendingBytes = buf.subarray(0, bytesRead);
|
|
174
|
+
}
|
|
169
175
|
|
|
170
|
-
//
|
|
171
|
-
const
|
|
176
|
+
// Decode the complete buffer
|
|
177
|
+
const text = pendingBytes.toString("utf8");
|
|
172
178
|
|
|
173
|
-
//
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
179
|
+
// Split by newline to find complete records
|
|
180
|
+
const lines = text.split("\n");
|
|
181
|
+
|
|
182
|
+
// If buffer doesn't end with newline, last element is incomplete
|
|
183
|
+
if (!pendingBytes.toString("utf8").endsWith("\n")) {
|
|
184
|
+
// Keep the incomplete line for next read (already in pendingBytes)
|
|
185
|
+
return { lines: [], newBytePos: currentBytePos };
|
|
178
186
|
}
|
|
179
187
|
|
|
188
|
+
// All lines are complete - emit them and clear buffer
|
|
189
|
+
pendingBytes = null;
|
|
190
|
+
|
|
180
191
|
// Filter out empty lines
|
|
181
192
|
const completeLines = lines.filter((l) => l.trim());
|
|
182
193
|
|