@harness-fe/mcp-server 4.0.0-next.2 → 4.0.0-next.4
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/bin.d.ts +2 -0
- package/dist/bin.js +15 -0
- package/dist/daemon.d.ts +3 -3
- package/dist/daemon.js +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/mcp.d.ts +2 -2
- package/dist/mcp.js +65 -19
- package/dist/mcpHttp.d.ts +2 -2
- package/dist/mcpHttp.js +88 -18
- package/package.json +5 -7
- package/src/bin.ts +19 -0
- package/src/daemon.ts +3 -3
- package/src/experimental.test.ts +2 -2
- package/src/index.ts +4 -4
- package/src/mcp.ts +67 -23
- package/src/mcpHttp.test.ts +52 -3
- package/src/mcpHttp.ts +102 -23
- package/src/mcpLayer.e2e.test.ts +2 -2
- package/src/newCapabilities.e2e.test.ts +3 -3
- package/dist/auth.d.ts +0 -53
- package/dist/auth.js +0 -212
- package/dist/bridge.d.ts +0 -323
- package/dist/bridge.js +0 -1618
- package/dist/cli.d.ts +0 -18
- package/dist/cli.js +0 -293
- package/dist/dashboardApi.d.ts +0 -40
- package/dist/dashboardApi.js +0 -142
- package/dist/dashboardSpa.d.ts +0 -18
- package/dist/dashboardSpa.js +0 -180
- package/dist/dashboardUrl.d.ts +0 -13
- package/dist/dashboardUrl.js +0 -18
- package/dist/eventsHandler.d.ts +0 -24
- package/dist/eventsHandler.js +0 -114
- package/dist/identity.d.ts +0 -90
- package/dist/identity.js +0 -123
- package/dist/openBrowser.d.ts +0 -33
- package/dist/openBrowser.js +0 -63
- package/dist/remoteBridge.d.ts +0 -61
- package/dist/remoteBridge.js +0 -307
- package/dist/replayCreate.d.ts +0 -36
- package/dist/replayCreate.js +0 -156
- package/dist/replayViewer.d.ts +0 -20
- package/dist/replayViewer.js +0 -168
- package/dist/sessionRouter.d.ts +0 -45
- package/dist/sessionRouter.js +0 -88
- package/dist/store/JsonMemoryStore.d.ts +0 -52
- package/dist/store/JsonMemoryStore.js +0 -119
- package/dist/store/JsonTaskStore.d.ts +0 -21
- package/dist/store/JsonTaskStore.js +0 -53
- package/dist/store/JsonlStore.d.ts +0 -128
- package/dist/store/JsonlStore.js +0 -1172
- package/dist/store/MemoryEventStore.d.ts +0 -47
- package/dist/store/MemoryEventStore.js +0 -111
- package/dist/store/WriteQueue.d.ts +0 -51
- package/dist/store/WriteQueue.js +0 -142
- package/dist/store/index.d.ts +0 -6
- package/dist/store/index.js +0 -5
- package/dist/store/types.d.ts +0 -427
- package/dist/store/types.js +0 -19
- package/dist/visitorTimeline.d.ts +0 -24
- package/dist/visitorTimeline.js +0 -68
- package/src/auth.test.ts +0 -90
- package/src/auth.ts +0 -248
- package/src/bridge-auth.test.ts +0 -196
- package/src/bridge.test.ts +0 -1708
- package/src/bridge.ts +0 -1854
- package/src/cli.ts +0 -338
- package/src/dashboardApi.test.ts +0 -235
- package/src/dashboardApi.ts +0 -184
- package/src/dashboardSpa.test.ts +0 -239
- package/src/dashboardSpa.ts +0 -195
- package/src/dashboardUrl.test.ts +0 -46
- package/src/dashboardUrl.ts +0 -28
- package/src/eventsHandler.test.ts +0 -247
- package/src/eventsHandler.ts +0 -136
- package/src/identity.test.ts +0 -109
- package/src/identity.ts +0 -137
- package/src/openBrowser.test.ts +0 -103
- package/src/openBrowser.ts +0 -81
- package/src/remoteBridge.test.ts +0 -119
- package/src/remoteBridge.ts +0 -404
- package/src/replay.test.ts +0 -271
- package/src/replayCreate.ts +0 -194
- package/src/replayViewer.ts +0 -173
- package/src/sessionRouter.ts +0 -119
- package/src/store/JsonMemoryStore.test.ts +0 -175
- package/src/store/JsonMemoryStore.ts +0 -128
- package/src/store/JsonTaskStore.test.ts +0 -212
- package/src/store/JsonTaskStore.ts +0 -59
- package/src/store/JsonlStore.test.ts +0 -1538
- package/src/store/JsonlStore.ts +0 -1325
- package/src/store/MemoryEventStore.test.ts +0 -119
- package/src/store/MemoryEventStore.ts +0 -151
- package/src/store/WriteQueue.ts +0 -165
- package/src/store/identityTagging.test.ts +0 -67
- package/src/store/index.ts +0 -29
- package/src/store/types.ts +0 -532
- package/src/visitorTimeline.test.ts +0 -197
- package/src/visitorTimeline.ts +0 -89
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MemoryEventStore — in-memory bounded buffer of MCP HTTP-streaming events,
|
|
3
|
-
* implements the SDK `EventStore` interface so that
|
|
4
|
-
* `StreamableHTTPServerTransport` can resume a dropped SSE connection from
|
|
5
|
-
* a client-supplied `Last-Event-ID`.
|
|
6
|
-
*
|
|
7
|
-
* Eviction is applied on every store, in this order:
|
|
8
|
-
* 1. drop per-stream events older than `maxAgeMs`
|
|
9
|
-
* 2. drop oldest per-stream events while the stream length exceeds `maxEventsPerStream`
|
|
10
|
-
* 3. drop globally-oldest events across all streams while `totalBytes` exceeds `maxBytesTotal`
|
|
11
|
-
*
|
|
12
|
-
* Event ids are `{streamId}::{padded counter}`. The counter is never reused
|
|
13
|
-
* for a given stream so a reconnect with a stale `Last-Event-ID` can be
|
|
14
|
-
* detected (the id is just absent from the buffer).
|
|
15
|
-
*/
|
|
16
|
-
import type { EventId, EventStore, StreamId } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
|
|
17
|
-
import type { JSONRPCMessage } from '@modelcontextprotocol/sdk/types.js';
|
|
18
|
-
export interface MemoryEventStoreOptions {
|
|
19
|
-
/** Max events retained per stream. Default 1000. */
|
|
20
|
-
maxEventsPerStream?: number;
|
|
21
|
-
/** Max age of a retained event, in ms. Default 5 minutes. */
|
|
22
|
-
maxAgeMs?: number;
|
|
23
|
-
/** Soft cap on total buffered bytes across all streams. Default 50 MiB. */
|
|
24
|
-
maxBytesTotal?: number;
|
|
25
|
-
/** Time source — override for tests. Default `Date.now`. */
|
|
26
|
-
now?: () => number;
|
|
27
|
-
}
|
|
28
|
-
export declare class MemoryEventStore implements EventStore {
|
|
29
|
-
private readonly maxEventsPerStream;
|
|
30
|
-
private readonly maxAgeMs;
|
|
31
|
-
private readonly maxBytesTotal;
|
|
32
|
-
private readonly now;
|
|
33
|
-
private readonly streams;
|
|
34
|
-
private readonly counters;
|
|
35
|
-
private totalBytes;
|
|
36
|
-
constructor(opts?: MemoryEventStoreOptions);
|
|
37
|
-
storeEvent(streamId: StreamId, message: JSONRPCMessage): Promise<EventId>;
|
|
38
|
-
getStreamIdForEventId(eventId: EventId): Promise<StreamId | undefined>;
|
|
39
|
-
replayEventsAfter(lastEventId: EventId, { send }: {
|
|
40
|
-
send: (eventId: EventId, message: JSONRPCMessage) => Promise<void>;
|
|
41
|
-
}): Promise<StreamId>;
|
|
42
|
-
/** Test helper — total events currently buffered. */
|
|
43
|
-
size(): number;
|
|
44
|
-
/** Test helper — current buffered bytes. */
|
|
45
|
-
bytes(): number;
|
|
46
|
-
private evict;
|
|
47
|
-
}
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* MemoryEventStore — in-memory bounded buffer of MCP HTTP-streaming events,
|
|
3
|
-
* implements the SDK `EventStore` interface so that
|
|
4
|
-
* `StreamableHTTPServerTransport` can resume a dropped SSE connection from
|
|
5
|
-
* a client-supplied `Last-Event-ID`.
|
|
6
|
-
*
|
|
7
|
-
* Eviction is applied on every store, in this order:
|
|
8
|
-
* 1. drop per-stream events older than `maxAgeMs`
|
|
9
|
-
* 2. drop oldest per-stream events while the stream length exceeds `maxEventsPerStream`
|
|
10
|
-
* 3. drop globally-oldest events across all streams while `totalBytes` exceeds `maxBytesTotal`
|
|
11
|
-
*
|
|
12
|
-
* Event ids are `{streamId}::{padded counter}`. The counter is never reused
|
|
13
|
-
* for a given stream so a reconnect with a stale `Last-Event-ID` can be
|
|
14
|
-
* detected (the id is just absent from the buffer).
|
|
15
|
-
*/
|
|
16
|
-
const SEPARATOR = '::';
|
|
17
|
-
const COUNTER_PAD = 12;
|
|
18
|
-
export class MemoryEventStore {
|
|
19
|
-
maxEventsPerStream;
|
|
20
|
-
maxAgeMs;
|
|
21
|
-
maxBytesTotal;
|
|
22
|
-
now;
|
|
23
|
-
streams = new Map();
|
|
24
|
-
counters = new Map();
|
|
25
|
-
totalBytes = 0;
|
|
26
|
-
constructor(opts = {}) {
|
|
27
|
-
this.maxEventsPerStream = opts.maxEventsPerStream ?? 1000;
|
|
28
|
-
this.maxAgeMs = opts.maxAgeMs ?? 5 * 60_000;
|
|
29
|
-
this.maxBytesTotal = opts.maxBytesTotal ?? 50 * 1024 * 1024;
|
|
30
|
-
this.now = opts.now ?? Date.now;
|
|
31
|
-
}
|
|
32
|
-
async storeEvent(streamId, message) {
|
|
33
|
-
const counter = (this.counters.get(streamId) ?? 0) + 1;
|
|
34
|
-
this.counters.set(streamId, counter);
|
|
35
|
-
const eventId = `${streamId}${SEPARATOR}${counter.toString().padStart(COUNTER_PAD, '0')}`;
|
|
36
|
-
const bytes = Buffer.byteLength(JSON.stringify(message), 'utf8');
|
|
37
|
-
const stored = { eventId, message, ts: this.now(), bytes };
|
|
38
|
-
let stream = this.streams.get(streamId);
|
|
39
|
-
if (!stream) {
|
|
40
|
-
stream = [];
|
|
41
|
-
this.streams.set(streamId, stream);
|
|
42
|
-
}
|
|
43
|
-
stream.push(stored);
|
|
44
|
-
this.totalBytes += bytes;
|
|
45
|
-
this.evict();
|
|
46
|
-
return eventId;
|
|
47
|
-
}
|
|
48
|
-
async getStreamIdForEventId(eventId) {
|
|
49
|
-
const idx = eventId.lastIndexOf(SEPARATOR);
|
|
50
|
-
return idx < 0 ? undefined : eventId.slice(0, idx);
|
|
51
|
-
}
|
|
52
|
-
async replayEventsAfter(lastEventId, { send }) {
|
|
53
|
-
const streamId = await this.getStreamIdForEventId(lastEventId);
|
|
54
|
-
if (!streamId)
|
|
55
|
-
return '';
|
|
56
|
-
const stream = this.streams.get(streamId);
|
|
57
|
-
if (!stream)
|
|
58
|
-
return streamId;
|
|
59
|
-
for (const ev of stream) {
|
|
60
|
-
if (ev.eventId > lastEventId) {
|
|
61
|
-
await send(ev.eventId, ev.message);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
return streamId;
|
|
65
|
-
}
|
|
66
|
-
/** Test helper — total events currently buffered. */
|
|
67
|
-
size() {
|
|
68
|
-
let n = 0;
|
|
69
|
-
for (const stream of this.streams.values())
|
|
70
|
-
n += stream.length;
|
|
71
|
-
return n;
|
|
72
|
-
}
|
|
73
|
-
/** Test helper — current buffered bytes. */
|
|
74
|
-
bytes() {
|
|
75
|
-
return this.totalBytes;
|
|
76
|
-
}
|
|
77
|
-
evict() {
|
|
78
|
-
const now = this.now();
|
|
79
|
-
for (const [sid, stream] of this.streams) {
|
|
80
|
-
while (stream.length > 0 &&
|
|
81
|
-
(stream.length > this.maxEventsPerStream || now - stream[0].ts > this.maxAgeMs)) {
|
|
82
|
-
const dropped = stream.shift();
|
|
83
|
-
this.totalBytes -= dropped.bytes;
|
|
84
|
-
}
|
|
85
|
-
if (stream.length === 0) {
|
|
86
|
-
this.streams.delete(sid);
|
|
87
|
-
// Keep the counter — eventIds must never be reused for a stream
|
|
88
|
-
// even if its buffer is currently empty.
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (this.totalBytes <= this.maxBytesTotal)
|
|
92
|
-
return;
|
|
93
|
-
while (this.totalBytes > this.maxBytesTotal) {
|
|
94
|
-
let oldestSid;
|
|
95
|
-
let oldestTs = Infinity;
|
|
96
|
-
for (const [sid, stream] of this.streams) {
|
|
97
|
-
if (stream.length > 0 && stream[0].ts < oldestTs) {
|
|
98
|
-
oldestTs = stream[0].ts;
|
|
99
|
-
oldestSid = sid;
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (!oldestSid)
|
|
103
|
-
break;
|
|
104
|
-
const stream = this.streams.get(oldestSid);
|
|
105
|
-
const dropped = stream.shift();
|
|
106
|
-
this.totalBytes -= dropped.bytes;
|
|
107
|
-
if (stream.length === 0)
|
|
108
|
-
this.streams.delete(oldestSid);
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WriteQueue — async batching queue for JSONL timeline writes.
|
|
3
|
-
*
|
|
4
|
-
* Key behaviors:
|
|
5
|
-
* - `seq` is assigned at enqueue time (not flush time) to preserve arrival order
|
|
6
|
-
* - `setTimeout` on first enqueue after a flush — avoids unnecessary timer overhead when idle
|
|
7
|
-
* - `flush` calls `fs.appendFile` once per file path with all buffered lines joined by `\n`
|
|
8
|
-
* - `drain` awaits the current flush and any in-flight writes before returning
|
|
9
|
-
* - On flush failure: log error, discard batch, continue — seq numbers from failed batch are not reused
|
|
10
|
-
*
|
|
11
|
-
* Requirements: 4.1, 4.2, 4.6, 4.7, 4.8
|
|
12
|
-
*/
|
|
13
|
-
export declare class WriteQueue {
|
|
14
|
-
/** filePath → pending lines to write */
|
|
15
|
-
private buffers;
|
|
16
|
-
/** Pending setTimeout handle; null when idle */
|
|
17
|
-
private timer;
|
|
18
|
-
/** sessionId → next seq number (assigned at enqueue time) */
|
|
19
|
-
private seq;
|
|
20
|
-
/** Promise for the currently in-flight flush, if any */
|
|
21
|
-
private flushPromise;
|
|
22
|
-
/**
|
|
23
|
-
* Enqueue a StoreEvent line for writing.
|
|
24
|
-
*
|
|
25
|
-
* The `line` parameter should be a JSON object string WITHOUT a `seq` field.
|
|
26
|
-
* WriteQueue assigns the seq for the given sessionId, injects it into the line,
|
|
27
|
-
* and pushes the result to the buffer for `filePath`.
|
|
28
|
-
*
|
|
29
|
-
* @param filePath Absolute path to the JSONL file to append to
|
|
30
|
-
* @param sessionId Session identifier used to track the per-session seq counter
|
|
31
|
-
* @param line Pre-serialized JSON object string (without `seq` field)
|
|
32
|
-
*/
|
|
33
|
-
enqueue(filePath: string, sessionId: string, line: string): void;
|
|
34
|
-
/**
|
|
35
|
-
* Drain all buffers: one `fs.appendFile` call per file path.
|
|
36
|
-
* On error: log + discard batch (do not retry).
|
|
37
|
-
* Seq numbers from failed batches are not reused.
|
|
38
|
-
*/
|
|
39
|
-
flush(): Promise<void>;
|
|
40
|
-
/**
|
|
41
|
-
* Flush any pending timer-scheduled writes immediately, then await
|
|
42
|
-
* the current in-flight flush (if any). Used on `close()` / SIGINT
|
|
43
|
-
* to ensure all buffered events reach disk before the process exits.
|
|
44
|
-
*/
|
|
45
|
-
drain(): Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* Get the current seq counter for a session (for testing / inspection).
|
|
48
|
-
* Returns 0 if no events have been enqueued for this session yet.
|
|
49
|
-
*/
|
|
50
|
-
getSeq(sessionId: string): number;
|
|
51
|
-
}
|
package/dist/store/WriteQueue.js
DELETED
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WriteQueue — async batching queue for JSONL timeline writes.
|
|
3
|
-
*
|
|
4
|
-
* Key behaviors:
|
|
5
|
-
* - `seq` is assigned at enqueue time (not flush time) to preserve arrival order
|
|
6
|
-
* - `setTimeout` on first enqueue after a flush — avoids unnecessary timer overhead when idle
|
|
7
|
-
* - `flush` calls `fs.appendFile` once per file path with all buffered lines joined by `\n`
|
|
8
|
-
* - `drain` awaits the current flush and any in-flight writes before returning
|
|
9
|
-
* - On flush failure: log error, discard batch, continue — seq numbers from failed batch are not reused
|
|
10
|
-
*
|
|
11
|
-
* Requirements: 4.1, 4.2, 4.6, 4.7, 4.8
|
|
12
|
-
*/
|
|
13
|
-
import { appendFile } from 'node:fs/promises';
|
|
14
|
-
/** Maximum delay (ms) before a pending flush is executed. */
|
|
15
|
-
const FLUSH_DELAY_MS = 16;
|
|
16
|
-
export class WriteQueue {
|
|
17
|
-
/** filePath → pending lines to write */
|
|
18
|
-
buffers = new Map();
|
|
19
|
-
/** Pending setTimeout handle; null when idle */
|
|
20
|
-
timer = null;
|
|
21
|
-
/** sessionId → next seq number (assigned at enqueue time) */
|
|
22
|
-
seq = new Map();
|
|
23
|
-
/** Promise for the currently in-flight flush, if any */
|
|
24
|
-
flushPromise = null;
|
|
25
|
-
/**
|
|
26
|
-
* Enqueue a StoreEvent line for writing.
|
|
27
|
-
*
|
|
28
|
-
* The `line` parameter should be a JSON object string WITHOUT a `seq` field.
|
|
29
|
-
* WriteQueue assigns the seq for the given sessionId, injects it into the line,
|
|
30
|
-
* and pushes the result to the buffer for `filePath`.
|
|
31
|
-
*
|
|
32
|
-
* @param filePath Absolute path to the JSONL file to append to
|
|
33
|
-
* @param sessionId Session identifier used to track the per-session seq counter
|
|
34
|
-
* @param line Pre-serialized JSON object string (without `seq` field)
|
|
35
|
-
*/
|
|
36
|
-
enqueue(filePath, sessionId, line) {
|
|
37
|
-
// Assign seq at enqueue time to preserve arrival order
|
|
38
|
-
const seq = this.seq.get(sessionId) ?? 0;
|
|
39
|
-
this.seq.set(sessionId, seq + 1);
|
|
40
|
-
// Inject seq into the JSON line
|
|
41
|
-
// The line is a JSON object string like '{"ts":1000,"t":"log",...}'
|
|
42
|
-
// We insert seq as the first field for readability
|
|
43
|
-
const lineWithSeq = injectSeq(line, seq);
|
|
44
|
-
// Push to buffer for this file path
|
|
45
|
-
const buf = this.buffers.get(filePath);
|
|
46
|
-
if (buf) {
|
|
47
|
-
buf.push(lineWithSeq);
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
this.buffers.set(filePath, [lineWithSeq]);
|
|
51
|
-
}
|
|
52
|
-
// Schedule a flush if not already pending
|
|
53
|
-
if (this.timer === null) {
|
|
54
|
-
this.timer = setTimeout(() => {
|
|
55
|
-
this.timer = null;
|
|
56
|
-
this.flushPromise = this.flush();
|
|
57
|
-
}, FLUSH_DELAY_MS);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
/**
|
|
61
|
-
* Drain all buffers: one `fs.appendFile` call per file path.
|
|
62
|
-
* On error: log + discard batch (do not retry).
|
|
63
|
-
* Seq numbers from failed batches are not reused.
|
|
64
|
-
*/
|
|
65
|
-
async flush() {
|
|
66
|
-
if (this.buffers.size === 0)
|
|
67
|
-
return;
|
|
68
|
-
// Snapshot and clear the buffers atomically before any async work.
|
|
69
|
-
// This ensures new enqueues during the flush go into fresh buffers.
|
|
70
|
-
const snapshot = new Map(this.buffers);
|
|
71
|
-
this.buffers.clear();
|
|
72
|
-
const writes = [];
|
|
73
|
-
for (const [filePath, lines] of snapshot) {
|
|
74
|
-
if (lines.length === 0)
|
|
75
|
-
continue;
|
|
76
|
-
// Join all lines with newline; append a trailing newline
|
|
77
|
-
const content = lines.join('\n') + '\n';
|
|
78
|
-
writes.push(appendFile(filePath, content, 'utf-8').catch((err) => {
|
|
79
|
-
console.error(`[WriteQueue] flush failed for ${filePath} (${lines.length} events discarded):`, err);
|
|
80
|
-
// Discard batch — do not retry, do not reuse seq numbers
|
|
81
|
-
}));
|
|
82
|
-
}
|
|
83
|
-
await Promise.all(writes);
|
|
84
|
-
}
|
|
85
|
-
/**
|
|
86
|
-
* Flush any pending timer-scheduled writes immediately, then await
|
|
87
|
-
* the current in-flight flush (if any). Used on `close()` / SIGINT
|
|
88
|
-
* to ensure all buffered events reach disk before the process exits.
|
|
89
|
-
*/
|
|
90
|
-
async drain() {
|
|
91
|
-
// Cancel the pending timer so we don't double-flush
|
|
92
|
-
if (this.timer !== null) {
|
|
93
|
-
clearTimeout(this.timer);
|
|
94
|
-
this.timer = null;
|
|
95
|
-
}
|
|
96
|
-
// Capture any already in-flight flush (timer may have fired just before drain)
|
|
97
|
-
const inflight = this.flushPromise;
|
|
98
|
-
// Flush whatever is currently buffered (may be empty if inflight already drained it)
|
|
99
|
-
const newFlush = this.flush();
|
|
100
|
-
this.flushPromise = newFlush;
|
|
101
|
-
// Await both: the previously in-flight flush and the new one
|
|
102
|
-
await Promise.all([inflight, newFlush]);
|
|
103
|
-
}
|
|
104
|
-
/**
|
|
105
|
-
* Get the current seq counter for a session (for testing / inspection).
|
|
106
|
-
* Returns 0 if no events have been enqueued for this session yet.
|
|
107
|
-
*/
|
|
108
|
-
getSeq(sessionId) {
|
|
109
|
-
return this.seq.get(sessionId) ?? 0;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
|
113
|
-
/**
|
|
114
|
-
* Inject a `seq` field as the first property of a JSON object string.
|
|
115
|
-
*
|
|
116
|
-
* Input: '{"ts":1000,"t":"log"}'
|
|
117
|
-
* Output: '{"seq":0,"ts":1000,"t":"log"}'
|
|
118
|
-
*
|
|
119
|
-
* Falls back to appending seq if the line is not a valid JSON object.
|
|
120
|
-
*/
|
|
121
|
-
function injectSeq(line, seq) {
|
|
122
|
-
const trimmed = line.trimStart();
|
|
123
|
-
if (trimmed.startsWith('{')) {
|
|
124
|
-
// Fast path: insert after the opening brace
|
|
125
|
-
const openBrace = line.indexOf('{');
|
|
126
|
-
const rest = line.slice(openBrace + 1).trimStart();
|
|
127
|
-
if (rest.startsWith('}')) {
|
|
128
|
-
// Empty object
|
|
129
|
-
return `{"seq":${seq}}`;
|
|
130
|
-
}
|
|
131
|
-
return `${line.slice(0, openBrace + 1)}"seq":${seq},${line.slice(openBrace + 1)}`;
|
|
132
|
-
}
|
|
133
|
-
// Fallback: parse and re-serialize (handles edge cases)
|
|
134
|
-
try {
|
|
135
|
-
const obj = JSON.parse(line);
|
|
136
|
-
return JSON.stringify({ seq, ...obj });
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
// If the line is not valid JSON, wrap it
|
|
140
|
-
return JSON.stringify({ seq, _raw: line });
|
|
141
|
-
}
|
|
142
|
-
}
|
package/dist/store/index.d.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
export { JsonlStore, sanitizeId } from './JsonlStore.js';
|
|
2
|
-
export { WriteQueue } from './WriteQueue.js';
|
|
3
|
-
export { JsonTaskStore } from './JsonTaskStore.js';
|
|
4
|
-
export { JsonMemoryStore } from './JsonMemoryStore.js';
|
|
5
|
-
export { MemoryEventStore, type MemoryEventStoreOptions } from './MemoryEventStore.js';
|
|
6
|
-
export type { IStore, ITaskStore, IMemoryStore, MemoryEntry, StoreEvent, EventType, ProjectMeta, ProjectTreeNode, BuildMeta, SessionMeta, TabMeta, SessionSummary, TailOptions, SearchOptions, RecordingChunkSummary, RecordingChunk, ReplayExportMeta, RetentionPolicy, PurgeResult, EventStore, EventId, StreamId, } from './types.js';
|
package/dist/store/index.js
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
export { JsonlStore, sanitizeId } from './JsonlStore.js';
|
|
2
|
-
export { WriteQueue } from './WriteQueue.js';
|
|
3
|
-
export { JsonTaskStore } from './JsonTaskStore.js';
|
|
4
|
-
export { JsonMemoryStore } from './JsonMemoryStore.js';
|
|
5
|
-
export { MemoryEventStore } from './MemoryEventStore.js';
|