devharness 0.9.8 → 0.9.10

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 (161) hide show
  1. package/README.md +16 -1
  2. package/build/block-events.d.ts +3 -5
  3. package/build/block-events.d.ts.map +1 -1
  4. package/build/block-events.js +6 -24
  5. package/build/block-events.js.map +1 -1
  6. package/build/block-events.test.js +10 -7
  7. package/build/block-events.test.js.map +1 -1
  8. package/build/cdp-manager.d.ts +7 -0
  9. package/build/cdp-manager.d.ts.map +1 -1
  10. package/build/cdp-manager.js +9 -0
  11. package/build/cdp-manager.js.map +1 -1
  12. package/build/cli/index.d.ts +26 -0
  13. package/build/cli/index.d.ts.map +1 -0
  14. package/build/cli/index.js +290 -0
  15. package/build/cli/index.js.map +1 -0
  16. package/build/cli/process-tree.d.ts +21 -0
  17. package/build/cli/process-tree.d.ts.map +1 -0
  18. package/build/cli/process-tree.js +51 -0
  19. package/build/cli/process-tree.js.map +1 -0
  20. package/build/cli/session-match.d.ts +37 -0
  21. package/build/cli/session-match.d.ts.map +1 -0
  22. package/build/cli/session-match.js +55 -0
  23. package/build/cli/session-match.js.map +1 -0
  24. package/build/cli/session-match.test.d.ts +17 -0
  25. package/build/cli/session-match.test.d.ts.map +1 -0
  26. package/build/cli/session-match.test.js +125 -0
  27. package/build/cli/session-match.test.js.map +1 -0
  28. package/build/config.d.ts +2 -2
  29. package/build/config.d.ts.map +1 -1
  30. package/build/config.js +2 -1
  31. package/build/config.js.map +1 -1
  32. package/build/config.test.js +2 -2
  33. package/build/config.test.js.map +1 -1
  34. package/build/connection-manager.d.ts.map +1 -1
  35. package/build/connection-manager.js +6 -1
  36. package/build/connection-manager.js.map +1 -1
  37. package/build/dashboard/dashboard-hub.js +2 -2
  38. package/build/index.js +60 -7
  39. package/build/index.js.map +1 -1
  40. package/build/interaction-recorder.js +2 -2
  41. package/build/interaction-recorder.js.map +1 -1
  42. package/build/issue-tracker.test.js +1 -1
  43. package/build/issue-tracker.test.js.map +1 -1
  44. package/build/mcp-supervisor.js +43 -0
  45. package/build/mcp-supervisor.js.map +1 -1
  46. package/build/network-monitor.d.ts +13 -2
  47. package/build/network-monitor.d.ts.map +1 -1
  48. package/build/network-monitor.js +38 -2
  49. package/build/network-monitor.js.map +1 -1
  50. package/build/network-monitor.test.js +50 -0
  51. package/build/network-monitor.test.js.map +1 -1
  52. package/build/persistent-profiles.test.js +2 -2
  53. package/build/persistent-profiles.test.js.map +1 -1
  54. package/build/plugin-session-start-hook.test.d.ts +10 -0
  55. package/build/plugin-session-start-hook.test.d.ts.map +1 -0
  56. package/build/plugin-session-start-hook.test.js +187 -0
  57. package/build/plugin-session-start-hook.test.js.map +1 -0
  58. package/build/runners/docker-compose-runner.js +1 -1
  59. package/build/runners/docker-compose-runner.js.map +1 -1
  60. package/build/runners/docker-runner.js +1 -1
  61. package/build/runners/docker-runner.js.map +1 -1
  62. package/build/runners/types.d.ts +1 -1
  63. package/build/runners/types.d.ts.map +1 -1
  64. package/build/server-manager.d.ts +1 -1
  65. package/build/server-manager.js +3 -3
  66. package/build/server-manager.js.map +1 -1
  67. package/build/server-manager.test.d.ts +1 -1
  68. package/build/server-manager.test.js +1 -1
  69. package/build/server-ownership.test.js +2 -2
  70. package/build/server-ownership.test.js.map +1 -1
  71. package/build/session-endpoint.d.ts +72 -0
  72. package/build/session-endpoint.d.ts.map +1 -0
  73. package/build/session-endpoint.js +220 -0
  74. package/build/session-endpoint.js.map +1 -0
  75. package/build/session-endpoint.test.d.ts +10 -0
  76. package/build/session-endpoint.test.d.ts.map +1 -0
  77. package/build/session-endpoint.test.js +202 -0
  78. package/build/session-endpoint.test.js.map +1 -0
  79. package/build/session-events.d.ts +28 -0
  80. package/build/session-events.d.ts.map +1 -0
  81. package/build/session-events.js +38 -0
  82. package/build/session-events.js.map +1 -0
  83. package/build/session-events.test.d.ts +8 -0
  84. package/build/session-events.test.d.ts.map +1 -0
  85. package/build/session-events.test.js +56 -0
  86. package/build/session-events.test.js.map +1 -0
  87. package/build/session-identity.d.ts +56 -0
  88. package/build/session-identity.d.ts.map +1 -0
  89. package/build/session-identity.js +96 -0
  90. package/build/session-identity.js.map +1 -0
  91. package/build/session-identity.test.d.ts +9 -0
  92. package/build/session-identity.test.d.ts.map +1 -0
  93. package/build/session-identity.test.js +126 -0
  94. package/build/session-identity.test.js.map +1 -0
  95. package/build/session-messages.d.ts +70 -0
  96. package/build/session-messages.d.ts.map +1 -0
  97. package/build/session-messages.js +139 -0
  98. package/build/session-messages.js.map +1 -0
  99. package/build/supervisor/pidfile.test.js +1 -1
  100. package/build/supervisor/pidfile.test.js.map +1 -1
  101. package/build/tool-response.d.ts +40 -1
  102. package/build/tool-response.d.ts.map +1 -1
  103. package/build/tool-response.js.map +1 -1
  104. package/build/tools/config-tools.d.ts +1 -1
  105. package/build/tools/config-tools.js +4 -4
  106. package/build/tools/config-tools.js.map +1 -1
  107. package/build/tools/console-tools.d.ts +4 -0
  108. package/build/tools/console-tools.d.ts.map +1 -1
  109. package/build/tools/console-tools.js +55 -1
  110. package/build/tools/console-tools.js.map +1 -1
  111. package/build/tools/dashboard-tools.d.ts +1 -1
  112. package/build/tools/dashboard-tools.js +2 -2
  113. package/build/tools/dashboard-tools.js.map +1 -1
  114. package/build/tools/inspect-worker-meta.test.d.ts +9 -0
  115. package/build/tools/inspect-worker-meta.test.d.ts.map +1 -0
  116. package/build/tools/inspect-worker-meta.test.js +122 -0
  117. package/build/tools/inspect-worker-meta.test.js.map +1 -0
  118. package/build/tools/inspection-tools.d.ts +12 -5
  119. package/build/tools/inspection-tools.d.ts.map +1 -1
  120. package/build/tools/inspection-tools.js +86 -5
  121. package/build/tools/inspection-tools.js.map +1 -1
  122. package/build/tools/issues-abort-hygiene.test.js +1 -1
  123. package/build/tools/issues-abort-hygiene.test.js.map +1 -1
  124. package/build/tools/issues-tools.d.ts +3 -3
  125. package/build/tools/issues-tools.test.js +1 -1
  126. package/build/tools/issues-tools.test.js.map +1 -1
  127. package/build/tools/message-tools.d.ts +51 -0
  128. package/build/tools/message-tools.d.ts.map +1 -0
  129. package/build/tools/message-tools.js +251 -0
  130. package/build/tools/message-tools.js.map +1 -0
  131. package/build/tools/message-tools.test.d.ts +9 -0
  132. package/build/tools/message-tools.test.d.ts.map +1 -0
  133. package/build/tools/message-tools.test.js +332 -0
  134. package/build/tools/message-tools.test.js.map +1 -0
  135. package/build/tools/network-tools.js +1 -1
  136. package/build/tools/network-tools.js.map +1 -1
  137. package/build/tools/replay-tools.d.ts +3 -3
  138. package/build/tools/replay-tools.js +3 -3
  139. package/build/tools/replay-tools.js.map +1 -1
  140. package/build/tools/server-tools.d.ts +3 -3
  141. package/build/worker-targets.d.ts +75 -0
  142. package/build/worker-targets.d.ts.map +1 -0
  143. package/build/worker-targets.js +171 -0
  144. package/build/worker-targets.js.map +1 -0
  145. package/build/worker-targets.test.d.ts +8 -0
  146. package/build/worker-targets.test.d.ts.map +1 -0
  147. package/build/worker-targets.test.js +118 -0
  148. package/build/worker-targets.test.js.map +1 -0
  149. package/docs/README.md +2 -2
  150. package/docs/automation.md +1 -1
  151. package/docs/installation.md +4 -4
  152. package/docs/instructions.md +26 -15
  153. package/docs/message-system.md +1 -1
  154. package/docs/messages.md +153 -3
  155. package/docs/parser-plugins.md +1 -1
  156. package/docs/replay.md +1 -1
  157. package/docs/troubleshooting.md +3 -3
  158. package/package.json +2 -1
  159. package/plugin/skills/devharness/SKILL.md +36 -8
  160. package/plugin/skills/devharness/references/sequences.md +2 -2
  161. package/plugin/skills/devharness/references/tool-categories.md +10 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-targets.d.ts","sourceRoot":"","sources":["../src/worker-targets.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,+DAA+D;AAC/D,QAAA,MAAM,YAAY,wDAAyD,CAAC;AAE5E,MAAM,MAAM,gBAAgB,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAE7D,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,gEAAgE;IAChE,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAC/B,GAAG,EAAE,MAAM;IAAS,SAAS,EAAE,YAAY,EAAE;gBAA7C,GAAG,EAAE,MAAM,EAAS,SAAS,EAAE,YAAY,EAAE;CAIjE;AAED,qBAAa,0BAA2B,SAAQ,KAAK;IAChC,GAAG,EAAE,MAAM;IAAS,OAAO,EAAE,YAAY,EAAE;gBAA3C,GAAG,EAAE,MAAM,EAAS,OAAO,EAAE,YAAY,EAAE;CAI/D;AAED,qBAAa,mBAAoB,SAAQ,KAAK;IACR,OAAO,CAAC,EAAE,MAAM;gBAAxC,OAAO,EAAE,MAAM,EAAS,OAAO,CAAC,EAAE,MAAM,YAAA;CAIrD;AAUD;;;GAGG;AACH,qBAAa,oBAAoB;IAI7B,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,IAAI;IACZ,OAAO,CAAC,WAAW;IACnB,OAAO,CAAC,OAAO;IANjB,OAAO,CAAC,QAAQ,CAA0C;gBAGhD,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,EACZ,WAAW,GAAE,CAAC,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,GAAG,EAAE,CAAY,EACnF,OAAO,GAAE,CAAC,OAAO,EAAE,GAAG,KAAK,OAAO,CAAC,GAAG,CAAO;IAGjD,IAAI,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC;IAYrC;;;;OAIG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAUjD;;;OAGG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,UAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAkBlF,oEAAoE;IAC9D,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,oBAAoB,EAAE,CAAC;YAM9C,MAAM;IAsBpB,sEAAsE;IAChE,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAW/B;AASD,0EAA0E;AAC1E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,oBAAoB,CAOxF;AAED,yEAAyE;AACzE,wBAAsB,2BAA2B,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3F"}
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Worker targets - service workers, dedicated workers, shared workers.
3
+ *
4
+ * Every debugging tool runs against the page target, so code inside a worker
5
+ * has been unreachable: no evaluation, no console. A worker is its own CDP
6
+ * target with its own websocket, listed by the browser alongside the page, so
7
+ * a client per worker reaches it without touching the page's debugger state.
8
+ */
9
+ import CDP from 'chrome-remote-interface';
10
+ /** The three target types that run script outside the page. */
11
+ const WORKER_TYPES = ['service_worker', 'worker', 'shared_worker'];
12
+ export class WorkerTargetNotFoundError extends Error {
13
+ ref;
14
+ available;
15
+ constructor(ref, available) {
16
+ super(`No worker target matches "${ref}"`);
17
+ this.ref = ref;
18
+ this.available = available;
19
+ this.name = 'WorkerTargetNotFoundError';
20
+ }
21
+ }
22
+ export class WorkerTargetAmbiguousError extends Error {
23
+ ref;
24
+ matches;
25
+ constructor(ref, matches) {
26
+ super(`"${ref}" matches ${matches.length} worker targets`);
27
+ this.ref = ref;
28
+ this.matches = matches;
29
+ this.name = 'WorkerTargetAmbiguousError';
30
+ }
31
+ }
32
+ export class WorkerEvaluateError extends Error {
33
+ details;
34
+ constructor(message, details) {
35
+ super(message);
36
+ this.details = details;
37
+ this.name = 'WorkerEvaluateError';
38
+ }
39
+ }
40
+ /** Console output is capped per target; a chatty worker cannot grow unbounded. */
41
+ const MAX_MESSAGES = 200;
42
+ /**
43
+ * One browser's worker targets. Keyed by host and port, since that pair is
44
+ * what locates a target's websocket.
45
+ */
46
+ export class WorkerTargetRegistry {
47
+ host;
48
+ port;
49
+ listTargets;
50
+ connect;
51
+ attached = new Map();
52
+ constructor(host, port, listTargets = CDP.List, connect = CDP) {
53
+ this.host = host;
54
+ this.port = port;
55
+ this.listTargets = listTargets;
56
+ this.connect = connect;
57
+ }
58
+ async list() {
59
+ const targets = await this.listTargets({ host: this.host, port: this.port });
60
+ return targets
61
+ .filter((t) => WORKER_TYPES.includes(t.type))
62
+ .map((t) => ({
63
+ targetId: t.id,
64
+ type: t.type,
65
+ url: t.url,
66
+ attached: this.attached.has(t.id),
67
+ }));
68
+ }
69
+ /**
70
+ * A target id, or a substring of a target's URL. A substring matching more
71
+ * than one target is refused with both named - picking the first would
72
+ * evaluate in a worker the caller did not choose.
73
+ */
74
+ async resolve(ref) {
75
+ const targets = await this.list();
76
+ const byId = targets.find((t) => t.targetId === ref);
77
+ if (byId)
78
+ return byId;
79
+ const matches = targets.filter((t) => t.url.includes(ref));
80
+ if (matches.length === 1)
81
+ return matches[0];
82
+ if (matches.length === 0)
83
+ throw new WorkerTargetNotFoundError(ref, targets);
84
+ throw new WorkerTargetAmbiguousError(ref, matches);
85
+ }
86
+ /**
87
+ * Evaluate inside the worker. Console output from the target is recorded
88
+ * from the moment of first attach, so a log emitted before that is not held.
89
+ */
90
+ async evaluate(ref, expression, awaitPromise = true) {
91
+ const target = await this.resolve(ref);
92
+ const worker = await this.attach(target);
93
+ const result = await worker.client.Runtime.evaluate({
94
+ expression,
95
+ returnByValue: true,
96
+ awaitPromise,
97
+ });
98
+ if (result.exceptionDetails) {
99
+ const thrown = result.exceptionDetails.exception;
100
+ throw new WorkerEvaluateError(thrown?.description || thrown?.value || result.exceptionDetails.text, result.exceptionDetails.text);
101
+ }
102
+ return result.result?.value;
103
+ }
104
+ /** Console output recorded since this target was first attached. */
105
+ async messages(ref) {
106
+ const target = await this.resolve(ref);
107
+ const worker = await this.attach(target);
108
+ return [...worker.messages];
109
+ }
110
+ async attach(target) {
111
+ const existing = this.attached.get(target.targetId);
112
+ if (existing)
113
+ return existing;
114
+ const client = await this.connect({ host: this.host, port: this.port, target: target.targetId });
115
+ const worker = { client, messages: [] };
116
+ const record = (type, text) => {
117
+ worker.messages.push({ type, text, timestamp: Date.now() });
118
+ if (worker.messages.length > MAX_MESSAGES)
119
+ worker.messages.shift();
120
+ };
121
+ client.Runtime.consoleAPICalled((e) => {
122
+ record(e.type, (e.args || []).map(describeArg).join(' '));
123
+ });
124
+ client.Runtime.exceptionThrown((e) => {
125
+ const thrown = e.exceptionDetails?.exception;
126
+ record('error', thrown?.description || e.exceptionDetails?.text || 'exception');
127
+ });
128
+ await client.Runtime.enable();
129
+ this.attached.set(target.targetId, worker);
130
+ return worker;
131
+ }
132
+ /** Closes every client. A client left open holds its worker alive. */
133
+ async dispose() {
134
+ const workers = [...this.attached.values()];
135
+ this.attached.clear();
136
+ for (const worker of workers) {
137
+ try {
138
+ await worker.client.close();
139
+ }
140
+ catch {
141
+ // Its target is already gone; the socket went with it.
142
+ }
143
+ }
144
+ }
145
+ }
146
+ function describeArg(arg) {
147
+ if (arg.value !== undefined)
148
+ return typeof arg.value === 'string' ? arg.value : JSON.stringify(arg.value);
149
+ return arg.description || arg.className || arg.type || '';
150
+ }
151
+ const registries = new Map();
152
+ /** One registry per browser, so a second call reuses the open clients. */
153
+ export function getWorkerTargetRegistry(host, port) {
154
+ const key = `${host}:${port}`;
155
+ const existing = registries.get(key);
156
+ if (existing)
157
+ return existing;
158
+ const registry = new WorkerTargetRegistry(host, port);
159
+ registries.set(key, registry);
160
+ return registry;
161
+ }
162
+ /** Called when a connection closes: its browser's clients go with it. */
163
+ export async function disposeWorkerTargetRegistry(host, port) {
164
+ const key = `${host}:${port}`;
165
+ const registry = registries.get(key);
166
+ if (!registry)
167
+ return;
168
+ registries.delete(key);
169
+ await registry.dispose();
170
+ }
171
+ //# sourceMappingURL=worker-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-targets.js","sourceRoot":"","sources":["../src/worker-targets.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * A worker's script runs in its own target, so a reference has to name exactly
3
+ * one. A substring matching two targets resolved to the first would evaluate
4
+ * inside a worker the caller did not choose, and a client left open holds its
5
+ * worker alive past the browser that owned it - these pin both.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=worker-targets.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-targets.test.d.ts","sourceRoot":"","sources":["../src/worker-targets.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,118 @@
1
+ /**
2
+ * A worker's script runs in its own target, so a reference has to name exactly
3
+ * one. A substring matching two targets resolved to the first would evaluate
4
+ * inside a worker the caller did not choose, and a client left open holds its
5
+ * worker alive past the browser that owned it - these pin both.
6
+ */
7
+ import { describe, it, expect } from 'vitest';
8
+ import { WorkerTargetRegistry, WorkerTargetNotFoundError, WorkerTargetAmbiguousError, WorkerEvaluateError, } from './worker-targets.js';
9
+ const TARGETS = [
10
+ { id: 'page-1', type: 'page', url: 'http://localhost:5173/' },
11
+ { id: 'sw-1', type: 'service_worker', url: 'http://localhost:5173/sw.js' },
12
+ { id: 'w-1', type: 'worker', url: 'http://localhost:5173/worker.js' },
13
+ { id: 'w-2', type: 'worker', url: 'http://localhost:5173/second-worker.js' },
14
+ ];
15
+ function fakeClient(evaluate) {
16
+ const client = {
17
+ closed: false,
18
+ handlers: {},
19
+ Runtime: {
20
+ enable: async () => { },
21
+ evaluate: async (params) => (evaluate ? evaluate(params) : { result: { value: 'ok' } }),
22
+ consoleAPICalled: (h) => { client.handlers.console = h; },
23
+ exceptionThrown: (h) => { client.handlers.exception = h; },
24
+ },
25
+ close: async () => { client.closed = true; },
26
+ };
27
+ return client;
28
+ }
29
+ function registry(options = {}) {
30
+ const clients = [];
31
+ const connect = async () => {
32
+ const client = fakeClient(options.evaluate);
33
+ clients.push(client);
34
+ return client;
35
+ };
36
+ const instance = new WorkerTargetRegistry('localhost', 9222, async () => options.targets ?? TARGETS, connect);
37
+ return { instance, clients };
38
+ }
39
+ describe('WorkerTargetRegistry.list', () => {
40
+ it('lists only targets that run script outside the page', async () => {
41
+ const { instance } = registry();
42
+ const types = (await instance.list()).map((t) => t.type);
43
+ expect(types).toEqual(['service_worker', 'worker', 'worker']);
44
+ });
45
+ it('marks a target attached once a client is open on it', async () => {
46
+ const { instance } = registry();
47
+ await instance.evaluate('sw-1', '1');
48
+ const sw = (await instance.list()).find((t) => t.targetId === 'sw-1');
49
+ expect(sw?.attached).toBe(true);
50
+ });
51
+ });
52
+ describe('WorkerTargetRegistry.resolve', () => {
53
+ it('takes a target id', async () => {
54
+ const { instance } = registry();
55
+ expect((await instance.resolve('sw-1')).url).toBe('http://localhost:5173/sw.js');
56
+ });
57
+ it('takes a URL substring that matches one target', async () => {
58
+ const { instance } = registry();
59
+ expect((await instance.resolve('sw.js')).targetId).toBe('sw-1');
60
+ });
61
+ it('refuses a substring matching more than one, naming both', async () => {
62
+ const { instance } = registry();
63
+ await expect(instance.resolve('worker.js')).rejects.toBeInstanceOf(WorkerTargetAmbiguousError);
64
+ const error = await instance.resolve('worker.js').catch((e) => e);
65
+ expect(error.matches.map((t) => t.targetId)).toEqual(['w-1', 'w-2']);
66
+ });
67
+ it('refuses a reference matching nothing, listing what there is', async () => {
68
+ const { instance } = registry();
69
+ const error = await instance.resolve('absent.js').catch((e) => e);
70
+ expect(error).toBeInstanceOf(WorkerTargetNotFoundError);
71
+ expect(error.available.map((t) => t.targetId)).toEqual(['sw-1', 'w-1', 'w-2']);
72
+ });
73
+ });
74
+ describe('WorkerTargetRegistry.evaluate', () => {
75
+ it('returns the by-value result', async () => {
76
+ const { instance } = registry({ evaluate: () => ({ result: { value: 'ServiceWorkerGlobalScope' } }) });
77
+ expect(await instance.evaluate('sw-1', 'self.constructor.name')).toBe('ServiceWorkerGlobalScope');
78
+ });
79
+ it('raises what the worker threw', async () => {
80
+ const { instance } = registry({
81
+ evaluate: () => ({ exceptionDetails: { text: 'Uncaught', exception: { description: 'TypeError: nope' } } }),
82
+ });
83
+ await expect(instance.evaluate('sw-1', 'nope()')).rejects.toBeInstanceOf(WorkerEvaluateError);
84
+ });
85
+ it('opens one client per target, however many evaluations', async () => {
86
+ const { instance, clients } = registry();
87
+ await instance.evaluate('sw-1', '1');
88
+ await instance.evaluate('sw-1', '2');
89
+ expect(clients).toHaveLength(1);
90
+ });
91
+ });
92
+ describe('WorkerTargetRegistry console', () => {
93
+ it('records console output from the target', async () => {
94
+ const { instance, clients } = registry();
95
+ await instance.evaluate('sw-1', '1');
96
+ clients[0].handlers.console({ type: 'log', args: [{ value: 'inside' }, { value: 42 }] });
97
+ const messages = await instance.messages('sw-1');
98
+ expect(messages.map((m) => m.text)).toEqual(['inside 42']);
99
+ });
100
+ it('records an exception thrown in the target as an error', async () => {
101
+ const { instance, clients } = registry();
102
+ await instance.evaluate('sw-1', '1');
103
+ clients[0].handlers.exception({ exceptionDetails: { exception: { description: 'TypeError: nope' } } });
104
+ const messages = await instance.messages('sw-1');
105
+ expect(messages[0]).toMatchObject({ type: 'error', text: 'TypeError: nope' });
106
+ });
107
+ });
108
+ describe('WorkerTargetRegistry.dispose', () => {
109
+ it('closes every client it opened', async () => {
110
+ const { instance, clients } = registry();
111
+ await instance.evaluate('sw-1', '1');
112
+ await instance.evaluate('w-1', '1');
113
+ await instance.dispose();
114
+ expect(clients.map((c) => c.closed)).toEqual([true, true]);
115
+ expect((await instance.list()).every((t) => !t.attached)).toBe(true);
116
+ });
117
+ });
118
+ //# sourceMappingURL=worker-targets.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker-targets.test.js","sourceRoot":"","sources":["../src/worker-targets.test.ts"],"names":[],"mappings":"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"}
package/docs/README.md CHANGED
@@ -33,7 +33,7 @@ You don't have to set this up by hand: on startup, the server checks whether the
33
33
 
34
34
  ```bash
35
35
  # Claude Code CLI
36
- claude mcp add cdp-tools -- npx -y devharness
36
+ claude mcp add devharness -- npx -y devharness
37
37
  ```
38
38
 
39
39
  ```javascript
@@ -61,7 +61,7 @@ devharness supports multiple simultaneous connections:
61
61
 
62
62
  ### Smart Element Caching
63
63
 
64
- When you navigate to a page, cdp-tools automatically:
64
+ When you navigate to a page, devharness automatically:
65
65
 
66
66
  1. **Caches all clickable elements** - Links, buttons, and inputs are discovered and stored
67
67
  2. **Records viewport positions** - Tracks which elements are visible
@@ -500,7 +500,7 @@ server({ action: 'stop', serverId: 'my-app', global: true })
500
500
 
501
501
  ## Configuration Management
502
502
 
503
- Manage cdp-tools configuration across projects:
503
+ Manage devharness configuration across projects:
504
504
 
505
505
  ```javascript
506
506
  // Check where config is loaded from
@@ -3,7 +3,7 @@
3
3
  ## Claude Code CLI
4
4
 
5
5
  ```bash
6
- claude mcp add cdp-tools -- npx -y devharness
6
+ claude mcp add devharness -- npx -y devharness
7
7
  ```
8
8
 
9
9
  ## Claude Desktop
@@ -17,7 +17,7 @@ Add to your config file:
17
17
  ```json
18
18
  {
19
19
  "mcpServers": {
20
- "cdp-tools": {
20
+ "devharness": {
21
21
  "command": "npx",
22
22
  "args": ["-y", "devharness"]
23
23
  }
@@ -71,7 +71,7 @@ The `defaultDebugPort` sets the starting port for Chrome debugging. If the port
71
71
 
72
72
  ### Idle Sessions
73
73
 
74
- An editor window left open for days keeps its cdp-tools server alive, holding
74
+ An editor window left open for days keeps its devharness server alive, holding
75
75
  Chrome instances, dev servers and monitor buffers nobody is using. Two things
76
76
  stop that:
77
77
 
@@ -90,7 +90,7 @@ stop that:
90
90
  - **Abandoned dev servers are collected.** A window closed for good used to
91
91
  leave its dev servers running until reboot. The next session in that
92
92
  directory now stops any whose owning sessions are all gone.
93
- - **Orphan reaping.** When the client that launched cdp-tools is gone, the
93
+ - **Orphan reaping.** When the client that launched devharness is gone, the
94
94
  server tree shuts itself down, rather than waiting for a stdin EOF that never
95
95
  arrives when an `npm exec` wrapper is holding the pipe open.
96
96
 
@@ -1,4 +1,4 @@
1
- # cdp-tools Debugger Usage
1
+ # devharness Debugger Usage
2
2
 
3
3
  Chrome DevTools Protocol debugging for JavaScript/TypeScript in Chrome, Node.js, or CDP-compatible environments.
4
4
 
@@ -59,6 +59,9 @@ Chrome DevTools Protocol debugging for JavaScript/TypeScript in Chrome, Node.js,
59
59
  - Note: DOM breakpoints use nodeIds which are invalidated on page reload
60
60
 
61
61
  **Code search:**
62
+ - `inspect({ action: 'listTargets' })`: List worker targets (service, dedicated, shared)
63
+ - `inspect({ action: 'evaluateExpression', target })`: Evaluate inside a worker, addressed by target id or a URL substring
64
+ - `console({ action: 'list', target })`: Read a worker's console, which reaches no page listener
62
65
  - `inspect({ action: 'searchCode' })`: Find patterns
63
66
  - `inspect({ action: 'searchFunctions' })`: Locate definitions
64
67
  - `getSourceCode`: View context
@@ -120,28 +123,31 @@ Chrome DevTools Protocol debugging for JavaScript/TypeScript in Chrome, Node.js,
120
123
  - **Missing/invalid parameters**: the error includes a `continuationToken` and `missingParameters` (name/type/description/enum). Retry with just `{ continuationToken, <missing/bad field(s)> }` - don't resend everything. Expires after 5 min.
121
124
  - **A validated call gets blocked by a guard** (port failure, dead server, breakpoint pause): the response footer shows `**Repeat:** replay({ action: 'repeat', indices: [N] })`. Acknowledge the guard (e.g. `server({ action: 'acknowledgePort' })`), then use that hint to resume the exact call. Don't reuse a `continuationToken` here - that's for fixing bad input, not for retrying an already-valid call.
122
125
 
123
- ## Getting notified when a guard blocks
126
+ ## The event stream
124
127
 
125
- Guards only surface on the *next* tool call, so a server that dies while you're editing files stays invisible until you call back into devharness. Every new block also appends one JSON line to `.devharness/logs/blocks.jsonl`:
128
+ Everything devharness pushes - a guard block, a message from another session - appends one JSON line to `~/.devharness/events/<sessionId>.jsonl`. One file per session, so one watch covers every kind, including kinds added later.
126
129
 
127
- ```json
128
- {"ts":"2026-08-08T03:21:45.588Z","guard":"pendingStartup","tool":"navigate","detail":"died before port detected: \"web\"","resolve":"server({ action: 'acknowledgeStartup', serverId: 'web' })"}
129
- ```
130
-
131
- `guard` is one of `port`, `breakpoint`, `pendingStartup`, `bug`, `duplicateSession`. Lines are deduplicated: one per *new* block, not one per blocked call. The same block re-firing on later calls stays quiet until a call clears every guard and the block recurs.
132
-
133
- In Claude Code, tail it with a persistent Monitor so blocks arrive as notifications while you work on something else:
130
+ Installed as a plugin, a `SessionStart` hook (`plugin/hooks/session-start.mjs`) creates that file and prints the `Monitor` call as session context before the first turn. In Claude Code, arm it:
134
131
 
135
132
  ```
136
133
  Monitor({
137
- command: "mkdir -p .devharness/logs && touch .devharness/logs/blocks.jsonl && tail -f -n0 .devharness/logs/blocks.jsonl",
138
- description: "devharness guard blocks",
134
+ command: "mkdir -p ~/.devharness/events && touch <streamPath> && tail -f -n0 <streamPath>",
135
+ description: "devharness events",
139
136
  persistent: true,
140
137
  timeout_ms: 3600000
141
138
  })
142
139
  ```
143
140
 
144
- Any other client can watch the same file. `-n0` skips existing history so you only see blocks from that point on.
141
+ Nothing is lost without a watch - blocks and messages still surface on the next tool call. The watch is what makes them arrive while the session is doing something else.
142
+
143
+ Line kinds:
144
+
145
+ ```json
146
+ {"ts":"...","kind":"block","guard":"pendingStartup","tool":"navigate","detail":"died before port detected: \"web\"","resolve":"server({ action: 'acknowledgeStartup', serverId: 'web' })"}
147
+ {"ts":"...","kind":"message","from":"66ba2d65","id":"d94924a8-...","detail":"Message from 66ba2d65: ...","resolve":"message({ action: 'read' })"}
148
+ ```
149
+
150
+ `guard` is one of `port`, `breakpoint`, `pendingStartup`, `bug`, `duplicateSession`. Blocks are deduplicated: one line per *new* block, not one per blocked call. Any client can tail the file.
145
151
 
146
152
  ## Restarting devharness
147
153
 
@@ -218,9 +224,14 @@ runs against (see Quick Start).
218
224
  - **GitHub** (via the `gh` CLI; only `publish` and `sync` use the network): `publish` shows a draft and posts nothing until `confirm: true`; `sync` reconciles both ways and reports a conflict rather than overwriting when both sides changed; `import` materialises a GitHub-only issue locally; `link` stamps an existing number with no network call; `pullSequence` writes a sequence out of an issue body to disk (one authored by another GitHub account needs a person to read it and pass `confirm: true`)
219
225
  - A sequence pulled from an issue is **never run automatically**, and one using `execution`, `saveToDisk`, `server`, `request` or `download` is refused unless you pass `allowPrivilegedSteps: true`. Read it first
220
226
 
227
+ **Messages**: `message` (actions: sessions, send, read, reply)
228
+ - Text between two devharness sessions on this machine - a session hitting a devharness bug talking to the session working on devharness itself. `sessions` lists reachable mailboxes and this session's own mailbox path
229
+ - `send({ to, text, waitForReplyMs })` holds the call open until something lands in this session's mailbox (max 300000ms) and returns it; without `waitForReplyMs` it returns as soon as the line is written. `reply({ replyTo, text })` routes back to the sender of that message. The wait returns on ANY arrival, not only a tagged reply, so two sessions blocking at the same moment both release
230
+ - The mailbox is one append-only JSONL file per session under `~/.devharness/messages/`, holding the conversation and the read cursor; the global directory is what makes it reach across project roots. Nothing watches it. An arrival announces itself on that session's event stream, `~/.devharness/events/<id>.jsonl`, so one watch covers messages and guard blocks alike; without one, messages surface on the next `message({ action: 'read' })`
231
+
221
232
  **Server**: `server` (actions: start, stop, restart, list, logs, stopAll, setAutoRun, clearLogs, remove, monitorPort, unmonitorPort, listMonitored, acknowledgePort, acknowledgeStartup, extendStartup, cancelPendingRestart)
222
233
  - Use `global: true` to access servers started from a different working directory
223
- - `start({ watch: true, watchPaths?: [...] })`: cdp-tools watches the given paths (default: cwd) and auto-restarts the server on file changes, instead of relying on `--watch`/nodemon. Pause-aware: if a breakpoint debugger is paused on that server's inspector port, the restart queues instead of firing immediately - `cancelPendingRestart` discards a queued restart to keep debugging without it firing on resume
234
+ - `start({ watch: true, watchPaths?: [...] })`: devharness watches the given paths (default: cwd) and auto-restarts the server on file changes, instead of relying on `--watch`/nodemon. Pause-aware: if a breakpoint debugger is paused on that server's inspector port, the restart queues instead of firing immediately - `cancelPendingRestart` discards a queued restart to keep debugging without it firing on resume
224
235
 
225
236
  **Replay**: `replay` (actions: history, create, list, get, delete, export, load, listSaved, deleteSaved, run, runAll, step, finish, insert, addConditional, status, cancel, repeat, runFromLog, recordInteraction)
226
237
  - `recordInteraction`: record mouse, keyboard, and navigation events with a visual overlay
@@ -247,6 +258,6 @@ runs against (see Quick Start).
247
258
  - `show`: Display current configuration
248
259
  - `listTools`: List all toggleable tools with status and dependency conflicts
249
260
  - `reload`: Re-read config.json now (also happens automatically on file edits, ~250ms debounce). Doesn't apply `tools.enabled`/`tools.disabled` - those need `restart`
250
- - `restart`: Restart cdp-tools itself via the mcp-supervisor (see "Restarting devharness" above) - use when the server seems stuck/broken, or to apply `tools.enabled`/`tools.disabled` changes
261
+ - `restart`: Restart devharness itself via the mcp-supervisor (see "Restarting devharness" above) - use when the server seems stuck/broken, or to apply `tools.enabled`/`tools.disabled` changes
251
262
  - `listProfiles`: List named persistent Chrome profiles and the root they live under
252
263
  - `resetProfile`: Wipe and recreate a named profile (`config({ action: 'resetProfile', profile: 'device-a' })`). Refused while a live Chrome holds that profile - nothing is deleted in that case
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- The cdp-tools debugger uses a centralized message system to provide consistent, markdown-formatted responses to all tool calls. This guide explains how to use the system and when to use different patterns.
5
+ The devharness debugger uses a centralized message system to provide consistent, markdown-formatted responses to all tool calls. This guide explains how to use the system and when to use different patterns.
6
6
 
7
7
  ## Architecture
8
8