@harness-fe/mcp-server 4.0.0-next.2 → 4.0.0-next.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.
Files changed (100) hide show
  1. package/dist/bin.d.ts +2 -0
  2. package/dist/bin.js +15 -0
  3. package/dist/daemon.d.ts +3 -3
  4. package/dist/daemon.js +1 -1
  5. package/dist/index.d.ts +4 -4
  6. package/dist/index.js +3 -3
  7. package/dist/mcp.d.ts +2 -2
  8. package/dist/mcp.js +42 -16
  9. package/dist/mcpHttp.d.ts +2 -2
  10. package/dist/mcpHttp.js +8 -2
  11. package/package.json +5 -7
  12. package/src/bin.ts +19 -0
  13. package/src/daemon.ts +3 -3
  14. package/src/experimental.test.ts +2 -2
  15. package/src/index.ts +4 -4
  16. package/src/mcp.ts +44 -20
  17. package/src/mcpHttp.test.ts +3 -3
  18. package/src/mcpHttp.ts +10 -4
  19. package/src/mcpLayer.e2e.test.ts +2 -2
  20. package/src/newCapabilities.e2e.test.ts +3 -3
  21. package/dist/auth.d.ts +0 -53
  22. package/dist/auth.js +0 -212
  23. package/dist/bridge.d.ts +0 -323
  24. package/dist/bridge.js +0 -1618
  25. package/dist/cli.d.ts +0 -18
  26. package/dist/cli.js +0 -293
  27. package/dist/dashboardApi.d.ts +0 -40
  28. package/dist/dashboardApi.js +0 -142
  29. package/dist/dashboardSpa.d.ts +0 -18
  30. package/dist/dashboardSpa.js +0 -180
  31. package/dist/dashboardUrl.d.ts +0 -13
  32. package/dist/dashboardUrl.js +0 -18
  33. package/dist/eventsHandler.d.ts +0 -24
  34. package/dist/eventsHandler.js +0 -114
  35. package/dist/identity.d.ts +0 -90
  36. package/dist/identity.js +0 -123
  37. package/dist/openBrowser.d.ts +0 -33
  38. package/dist/openBrowser.js +0 -63
  39. package/dist/remoteBridge.d.ts +0 -61
  40. package/dist/remoteBridge.js +0 -307
  41. package/dist/replayCreate.d.ts +0 -36
  42. package/dist/replayCreate.js +0 -156
  43. package/dist/replayViewer.d.ts +0 -20
  44. package/dist/replayViewer.js +0 -168
  45. package/dist/sessionRouter.d.ts +0 -45
  46. package/dist/sessionRouter.js +0 -88
  47. package/dist/store/JsonMemoryStore.d.ts +0 -52
  48. package/dist/store/JsonMemoryStore.js +0 -119
  49. package/dist/store/JsonTaskStore.d.ts +0 -21
  50. package/dist/store/JsonTaskStore.js +0 -53
  51. package/dist/store/JsonlStore.d.ts +0 -128
  52. package/dist/store/JsonlStore.js +0 -1172
  53. package/dist/store/MemoryEventStore.d.ts +0 -47
  54. package/dist/store/MemoryEventStore.js +0 -111
  55. package/dist/store/WriteQueue.d.ts +0 -51
  56. package/dist/store/WriteQueue.js +0 -142
  57. package/dist/store/index.d.ts +0 -6
  58. package/dist/store/index.js +0 -5
  59. package/dist/store/types.d.ts +0 -427
  60. package/dist/store/types.js +0 -19
  61. package/dist/visitorTimeline.d.ts +0 -24
  62. package/dist/visitorTimeline.js +0 -68
  63. package/src/auth.test.ts +0 -90
  64. package/src/auth.ts +0 -248
  65. package/src/bridge-auth.test.ts +0 -196
  66. package/src/bridge.test.ts +0 -1708
  67. package/src/bridge.ts +0 -1854
  68. package/src/cli.ts +0 -338
  69. package/src/dashboardApi.test.ts +0 -235
  70. package/src/dashboardApi.ts +0 -184
  71. package/src/dashboardSpa.test.ts +0 -239
  72. package/src/dashboardSpa.ts +0 -195
  73. package/src/dashboardUrl.test.ts +0 -46
  74. package/src/dashboardUrl.ts +0 -28
  75. package/src/eventsHandler.test.ts +0 -247
  76. package/src/eventsHandler.ts +0 -136
  77. package/src/identity.test.ts +0 -109
  78. package/src/identity.ts +0 -137
  79. package/src/openBrowser.test.ts +0 -103
  80. package/src/openBrowser.ts +0 -81
  81. package/src/remoteBridge.test.ts +0 -119
  82. package/src/remoteBridge.ts +0 -404
  83. package/src/replay.test.ts +0 -271
  84. package/src/replayCreate.ts +0 -194
  85. package/src/replayViewer.ts +0 -173
  86. package/src/sessionRouter.ts +0 -119
  87. package/src/store/JsonMemoryStore.test.ts +0 -175
  88. package/src/store/JsonMemoryStore.ts +0 -128
  89. package/src/store/JsonTaskStore.test.ts +0 -212
  90. package/src/store/JsonTaskStore.ts +0 -59
  91. package/src/store/JsonlStore.test.ts +0 -1538
  92. package/src/store/JsonlStore.ts +0 -1325
  93. package/src/store/MemoryEventStore.test.ts +0 -119
  94. package/src/store/MemoryEventStore.ts +0 -151
  95. package/src/store/WriteQueue.ts +0 -165
  96. package/src/store/identityTagging.test.ts +0 -67
  97. package/src/store/index.ts +0 -29
  98. package/src/store/types.ts +0 -532
  99. package/src/visitorTimeline.test.ts +0 -197
  100. 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
- }
@@ -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
- }
@@ -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';
@@ -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';