@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,128 +0,0 @@
1
- /**
2
- * JsonMemoryStore — JSON-based persistence for agent memory (key-value store per project).
3
- *
4
- * File layout:
5
- * {dataDir}/{projectId}/memory.json
6
- *
7
- * File format:
8
- * {
9
- * "key1": { "key": "key1", "value": "...", "updatedAt": 1700000000000 },
10
- * "key2": { "key": "key2", "value": "...", "updatedAt": 1700000001000 }
11
- * }
12
- *
13
- * All mutations use atomic write-then-rename for durability.
14
- */
15
-
16
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
17
- import { join } from 'node:path';
18
- import type { IMemoryStore, MemoryEntry } from './types.js';
19
- import { sanitizeId } from './JsonlStore.js';
20
-
21
- export class JsonMemoryStore implements IMemoryStore {
22
- constructor(private readonly dataDir: string) {}
23
-
24
- // ── Path helpers ──────────────────────────────────────────────────────
25
-
26
- private memoryPath(projectId: string): string {
27
- return join(this.dataDir, sanitizeId(projectId), 'memory.json');
28
- }
29
-
30
- // ── Private I/O ───────────────────────────────────────────────────────
31
-
32
- /**
33
- * Read and parse memory.json for a project.
34
- * Returns a null-prototype object on missing or corrupt file (never throws).
35
- * Using Object.create(null) prevents prototype pollution from keys like __proto__.
36
- */
37
- private load(projectId: string): Record<string, MemoryEntry> {
38
- const path = this.memoryPath(projectId);
39
- try {
40
- const raw = readFileSync(path, 'utf-8');
41
- const parsed = JSON.parse(raw) as Record<string, MemoryEntry>;
42
- // Copy into a null-prototype object to prevent prototype pollution
43
- const safe: Record<string, MemoryEntry> = Object.create(null);
44
- for (const key of Object.keys(parsed)) {
45
- safe[key] = parsed[key];
46
- }
47
- return safe;
48
- } catch {
49
- return Object.create(null);
50
- }
51
- }
52
-
53
- /**
54
- * Atomically write memory data to disk using tmp + rename strategy.
55
- * Logs error on failure without throwing.
56
- */
57
- private save(projectId: string, data: Record<string, MemoryEntry>): void {
58
- const path = this.memoryPath(projectId);
59
- const tmpPath = `${path}.tmp`;
60
-
61
- // Ensure the project directory exists
62
- const dir = join(this.dataDir, sanitizeId(projectId));
63
- try {
64
- mkdirSync(dir, { recursive: true });
65
- } catch (err) {
66
- console.error(`[JsonMemoryStore] failed to create directory ${dir}:`, err);
67
- return;
68
- }
69
-
70
- try {
71
- writeFileSync(tmpPath, JSON.stringify(data, null, 2), 'utf-8');
72
- renameSync(tmpPath, path);
73
- } catch (err) {
74
- console.error(`[JsonMemoryStore] failed to write ${path}:`, err);
75
- }
76
- }
77
-
78
- // ── IMemoryStore implementation ───────────────────────────────────────
79
-
80
- /**
81
- * Get a memory entry by key.
82
- * Returns undefined if the key does not exist or memory.json is missing.
83
- */
84
- get(projectId: string, key: string): MemoryEntry | undefined {
85
- const data = this.load(projectId);
86
- return data[key];
87
- }
88
-
89
- /**
90
- * Write or update a memory entry.
91
- * Sets updatedAt to the current Unix ms timestamp.
92
- * Returns the new/updated MemoryEntry.
93
- */
94
- set(projectId: string, key: string, value: string): MemoryEntry {
95
- const data = this.load(projectId);
96
- const entry: MemoryEntry = {
97
- key,
98
- value,
99
- updatedAt: Date.now(),
100
- };
101
- data[key] = entry;
102
- this.save(projectId, data);
103
- return entry;
104
- }
105
-
106
- /**
107
- * Delete a memory entry by key.
108
- * Returns true if the key existed and was removed, false otherwise.
109
- */
110
- delete(projectId: string, key: string): boolean {
111
- const data = this.load(projectId);
112
- if (!(key in data)) {
113
- return false;
114
- }
115
- delete data[key];
116
- this.save(projectId, data);
117
- return true;
118
- }
119
-
120
- /**
121
- * List all memory entries for a project, sorted by updatedAt descending.
122
- * Returns an empty array if memory.json does not exist.
123
- */
124
- list(projectId: string): MemoryEntry[] {
125
- const data = this.load(projectId);
126
- return Object.values(data).sort((a, b) => b.updatedAt - a.updatedAt);
127
- }
128
- }
@@ -1,212 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
2
- import { existsSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
3
- import { tmpdir } from 'node:os';
4
- import { join } from 'node:path';
5
- import { JsonTaskStore } from './JsonTaskStore.js';
6
- import type { Task } from '@harness-fe/protocol';
7
-
8
- function makeTempDir() {
9
- return mkdtempSync(join(tmpdir(), 'json-task-store-test-'));
10
- }
11
-
12
- function cleanup(dir: string) {
13
- try { rmSync(dir, { recursive: true, force: true }); } catch { /* ignore */ }
14
- }
15
-
16
- function makeTask(overrides: Partial<Task> = {}): Task {
17
- return {
18
- id: 'task-1',
19
- tabId: 'tab-abc',
20
- projectId: 'proj',
21
- url: 'http://localhost:5173',
22
- status: 'pending',
23
- question: 'What does this button do?',
24
- selector: { css: '#submit-btn' },
25
- element: { outerHTML: '<button id="submit-btn">Submit</button>', rect: { x: 0, y: 0, width: 100, height: 40 } },
26
- createdAt: Date.now(),
27
- ...overrides,
28
- };
29
- }
30
-
31
- describe('JsonTaskStore', () => {
32
- let dir: string;
33
- let store: JsonTaskStore;
34
-
35
- beforeEach(() => {
36
- dir = makeTempDir();
37
- store = new JsonTaskStore(dir);
38
- });
39
-
40
- afterEach(() => {
41
- cleanup(dir);
42
- });
43
-
44
- // ── Load from missing file → empty array ─────────────────────────────
45
-
46
- it('returns empty array when tasks.json does not exist', () => {
47
- const tasks = store.loadTasks('proj');
48
- expect(tasks).toEqual([]);
49
- });
50
-
51
- it('returns empty array for a project that has never been saved', () => {
52
- const tasks = store.loadTasks('brand-new-project');
53
- expect(tasks).toEqual([]);
54
- });
55
-
56
- // ── Load from corrupt JSON → empty array ─────────────────────────────
57
-
58
- it('returns empty array when tasks.json contains invalid JSON', () => {
59
- // Manually create the project directory and write corrupt JSON
60
- const { mkdirSync } = require('node:fs');
61
- const projDir = join(dir, 'proj');
62
- mkdirSync(projDir, { recursive: true });
63
- writeFileSync(join(projDir, 'tasks.json'), '{ this is not valid json !!!', 'utf-8');
64
-
65
- const tasks = store.loadTasks('proj');
66
- expect(tasks).toEqual([]);
67
- });
68
-
69
- it('returns empty array when tasks.json has valid JSON but wrong shape (no tasks array)', () => {
70
- const { mkdirSync } = require('node:fs');
71
- const projDir = join(dir, 'proj');
72
- mkdirSync(projDir, { recursive: true });
73
- writeFileSync(join(projDir, 'tasks.json'), JSON.stringify({ version: 1, data: [] }), 'utf-8');
74
-
75
- const tasks = store.loadTasks('proj');
76
- expect(tasks).toEqual([]);
77
- });
78
-
79
- it('returns empty array when tasks.json is empty', () => {
80
- const { mkdirSync } = require('node:fs');
81
- const projDir = join(dir, 'proj');
82
- mkdirSync(projDir, { recursive: true });
83
- writeFileSync(join(projDir, 'tasks.json'), '', 'utf-8');
84
-
85
- const tasks = store.loadTasks('proj');
86
- expect(tasks).toEqual([]);
87
- });
88
-
89
- // ── Save tasks → atomic write (tmp + rename) ─────────────────────────
90
-
91
- it('saves tasks and the file exists afterwards', () => {
92
- const tasks = [makeTask()];
93
- store.saveTasks('proj', tasks);
94
-
95
- const tasksPath = join(dir, 'proj', 'tasks.json');
96
- expect(existsSync(tasksPath)).toBe(true);
97
- });
98
-
99
- it('saves tasks and no .tmp file remains after save', () => {
100
- const tasks = [makeTask()];
101
- store.saveTasks('proj', tasks);
102
-
103
- const tmpPath = join(dir, 'proj', 'tasks.json.tmp');
104
- expect(existsSync(tmpPath)).toBe(false);
105
- });
106
-
107
- it('saved file contains the correct JSON structure', () => {
108
- const { readFileSync } = require('node:fs');
109
- const tasks = [makeTask({ id: 'task-abc' })];
110
- store.saveTasks('proj', tasks);
111
-
112
- const raw = readFileSync(join(dir, 'proj', 'tasks.json'), 'utf-8');
113
- const parsed = JSON.parse(raw);
114
- expect(parsed.version).toBe(1);
115
- expect(Array.isArray(parsed.tasks)).toBe(true);
116
- expect(parsed.tasks).toHaveLength(1);
117
- expect(parsed.tasks[0].id).toBe('task-abc');
118
- });
119
-
120
- it('creates the project directory if it does not exist', () => {
121
- const tasks = [makeTask()];
122
- store.saveTasks('new-project', tasks);
123
-
124
- const projDir = join(dir, 'new-project');
125
- expect(existsSync(projDir)).toBe(true);
126
- });
127
-
128
- // ── Round-trip: save then load ────────────────────────────────────────
129
-
130
- it('round-trip: saved tasks can be loaded back and match the original', () => {
131
- const tasks: Task[] = [
132
- makeTask({ id: 'task-1', status: 'pending' }),
133
- makeTask({ id: 'task-2', status: 'claimed', claimedAt: Date.now() }),
134
- makeTask({ id: 'task-3', status: 'resolved', resolvedAt: Date.now(), note: 'Done!' }),
135
- ];
136
-
137
- store.saveTasks('proj', tasks);
138
- const loaded = store.loadTasks('proj');
139
-
140
- expect(loaded).toHaveLength(3);
141
- expect(loaded[0].id).toBe('task-1');
142
- expect(loaded[1].id).toBe('task-2');
143
- expect(loaded[2].id).toBe('task-3');
144
- expect(loaded[2].note).toBe('Done!');
145
- });
146
-
147
- it('round-trip: saving an empty array and loading returns empty array', () => {
148
- store.saveTasks('proj', []);
149
- const loaded = store.loadTasks('proj');
150
- expect(loaded).toEqual([]);
151
- });
152
-
153
- it('round-trip: overwriting tasks replaces the previous data', () => {
154
- const initial = [makeTask({ id: 'task-1' }), makeTask({ id: 'task-2' })];
155
- store.saveTasks('proj', initial);
156
-
157
- const updated = [makeTask({ id: 'task-3' })];
158
- store.saveTasks('proj', updated);
159
-
160
- const loaded = store.loadTasks('proj');
161
- expect(loaded).toHaveLength(1);
162
- expect(loaded[0].id).toBe('task-3');
163
- });
164
-
165
- it('round-trip: multiple saves preserve the last written state', () => {
166
- for (let i = 0; i < 5; i++) {
167
- store.saveTasks('proj', [makeTask({ id: `task-${i}` })]);
168
- }
169
-
170
- const loaded = store.loadTasks('proj');
171
- expect(loaded).toHaveLength(1);
172
- expect(loaded[0].id).toBe('task-4');
173
- });
174
-
175
- // ── Purge does not delete tasks.json ─────────────────────────────────
176
- // JsonTaskStore has no purge method — verify that saveTasks/loadTasks
177
- // continue to work correctly after multiple saves (simulating what purge
178
- // would need to preserve).
179
-
180
- it('tasks.json is preserved across multiple save operations', () => {
181
- const tasks = [makeTask({ id: 'task-keep' })];
182
- store.saveTasks('proj', tasks);
183
-
184
- // Simulate additional saves (as would happen during normal operation)
185
- store.saveTasks('proj', [...tasks, makeTask({ id: 'task-new' })]);
186
-
187
- const tasksPath = join(dir, 'proj', 'tasks.json');
188
- expect(existsSync(tasksPath)).toBe(true);
189
-
190
- const loaded = store.loadTasks('proj');
191
- expect(loaded).toHaveLength(2);
192
- expect(loaded.map((t) => t.id)).toContain('task-keep');
193
- expect(loaded.map((t) => t.id)).toContain('task-new');
194
- });
195
-
196
- it('tasks.json is not affected by operations on other projects', () => {
197
- const projATasks = [makeTask({ id: 'task-a', projectId: 'proj-a' })];
198
- const projBTasks = [makeTask({ id: 'task-b', projectId: 'proj-b' })];
199
-
200
- store.saveTasks('proj-a', projATasks);
201
- store.saveTasks('proj-b', projBTasks);
202
-
203
- // Loading proj-a should not be affected by proj-b
204
- const loadedA = store.loadTasks('proj-a');
205
- expect(loadedA).toHaveLength(1);
206
- expect(loadedA[0].id).toBe('task-a');
207
-
208
- const loadedB = store.loadTasks('proj-b');
209
- expect(loadedB).toHaveLength(1);
210
- expect(loadedB[0].id).toBe('task-b');
211
- });
212
- });
@@ -1,59 +0,0 @@
1
- /**
2
- * JsonTaskStore — JSON-based persistence for annotation tasks.
3
- *
4
- * File format: {dataDir}/{sanitizeId(projectId)}/tasks.json
5
- * ```json
6
- * { "version": 1, "tasks": Task[] }
7
- * ```
8
- *
9
- * Writes are atomic: write to a .tmp file then rename to the final path.
10
- * On read failure (missing or corrupt file), returns an empty array.
11
- * On write failure, logs the error without throwing.
12
- */
13
-
14
- import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
15
- import { join } from 'node:path';
16
- import type { Task } from '@harness-fe/protocol';
17
- import type { ITaskStore } from './types.js';
18
- import { sanitizeId } from './JsonlStore.js';
19
-
20
- interface TasksFile {
21
- version: number;
22
- tasks: Task[];
23
- }
24
-
25
- export class JsonTaskStore implements ITaskStore {
26
- constructor(private readonly dataDir: string) {}
27
-
28
- private tasksPath(projectId: string): string {
29
- return join(this.dataDir, sanitizeId(projectId), 'tasks.json');
30
- }
31
-
32
- loadTasks(projectId: string): Task[] {
33
- const path = this.tasksPath(projectId);
34
- if (!existsSync(path)) return [];
35
- try {
36
- const raw = readFileSync(path, 'utf-8');
37
- const parsed = JSON.parse(raw) as TasksFile;
38
- if (!Array.isArray(parsed?.tasks)) return [];
39
- return parsed.tasks;
40
- } catch {
41
- return [];
42
- }
43
- }
44
-
45
- saveTasks(projectId: string, tasks: Task[]): void {
46
- const path = this.tasksPath(projectId);
47
- const dir = join(this.dataDir, sanitizeId(projectId));
48
-
49
- try {
50
- mkdirSync(dir, { recursive: true });
51
- const tmp = `${path}.tmp`;
52
- const data: TasksFile = { version: 1, tasks };
53
- writeFileSync(tmp, JSON.stringify(data, null, 2), 'utf-8');
54
- renameSync(tmp, path);
55
- } catch (err) {
56
- console.error(`[JsonTaskStore] saveTasks failed for project "${projectId}":`, err);
57
- }
58
- }
59
- }