devharness 0.8.0 → 0.9.0
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/README.md +12 -0
- package/build/cdp-manager.js +1 -1
- package/build/cdp-manager.js.map +1 -1
- package/build/chrome-launcher.d.ts +7 -7
- package/build/chrome-launcher.d.ts.map +1 -1
- package/build/chrome-launcher.js +9 -8
- package/build/chrome-launcher.js.map +1 -1
- package/build/command-recorder.d.ts +1 -1
- package/build/command-recorder.js +1 -1
- package/build/config.d.ts +5 -5
- package/build/config.js +15 -15
- package/build/config.js.map +1 -1
- package/build/config.test.js +4 -4
- package/build/config.test.js.map +1 -1
- package/build/dashboard/dashboard-client.js +2 -2
- package/build/dashboard/dashboard-client.js.map +1 -1
- package/build/dashboard/dashboard-hub.js +3 -2
- package/build/dashboard/dashboard-hub.js.map +1 -1
- package/build/debug-logger.d.ts +1 -1
- package/build/debug-logger.js +1 -1
- package/build/helpers/parser-plugins.d.ts +2 -2
- package/build/helpers/parser-plugins.js +2 -2
- package/build/helpers/paths-migration.test.d.ts +2 -0
- package/build/helpers/paths-migration.test.d.ts.map +1 -0
- package/build/helpers/paths-migration.test.js +65 -0
- package/build/helpers/paths-migration.test.js.map +1 -0
- package/build/helpers/paths.d.ts +28 -9
- package/build/helpers/paths.d.ts.map +1 -1
- package/build/helpers/paths.js +66 -16
- package/build/helpers/paths.js.map +1 -1
- package/build/index.js +46 -46
- package/build/index.js.map +1 -1
- package/build/interaction-recorder.js +4 -4
- package/build/interaction-recorder.js.map +1 -1
- package/build/issue-tracker.js +4 -4
- package/build/issue-tracker.js.map +1 -1
- package/build/issue-tracker.test.js +1 -1
- package/build/issue-tracker.test.js.map +1 -1
- package/build/log-processor/orchestrator.d.ts +2 -2
- package/build/log-processor/orchestrator.d.ts.map +1 -1
- package/build/log-processor/orchestrator.js +1 -1
- package/build/mcp-supervisor.js +2 -3
- package/build/mcp-supervisor.js.map +1 -1
- package/build/messages.d.ts +1 -1
- package/build/messages.js +3 -3
- package/build/messages.js.map +1 -1
- package/build/overlays.d.ts +1 -1
- package/build/overlays.js +2 -2
- package/build/overlays.js.map +1 -1
- package/build/persistent-profiles.test.js +5 -5
- package/build/persistent-profiles.test.js.map +1 -1
- package/build/runners/types.d.ts +1 -1
- package/build/runners/types.d.ts.map +1 -1
- package/build/self-restart.js +1 -1
- package/build/server-claims.js +2 -2
- package/build/server-claims.js.map +1 -1
- package/build/server-claims.test.js +3 -3
- package/build/server-claims.test.js.map +1 -1
- package/build/server-manager.d.ts +4 -4
- package/build/server-manager.d.ts.map +1 -1
- package/build/server-manager.js +7 -7
- package/build/server-manager.js.map +1 -1
- package/build/server-ownership.test.js +2 -2
- package/build/server-ownership.test.js.map +1 -1
- package/build/server-watcher.js +2 -2
- package/build/server-watcher.js.map +1 -1
- package/build/supervisor/idle-config.d.ts +1 -1
- package/build/supervisor/idle-config.d.ts.map +1 -1
- package/build/supervisor/idle-config.js +1 -1
- package/build/tools/breakpoint-tools.js +1 -1
- package/build/tools/breakpoint-tools.js.map +1 -1
- package/build/tools/config-tools.js +2 -2
- package/build/tools/config-tools.js.map +1 -1
- package/build/tools/console-tools.js +1 -1
- package/build/tools/console-tools.js.map +1 -1
- package/build/tools/content-tools.js +5 -5
- package/build/tools/content-tools.js.map +1 -1
- package/build/tools/replay-add-conditional.test.js +2 -2
- package/build/tools/replay-add-conditional.test.js.map +1 -1
- package/build/tools/replay-folder-save.test.js +1 -1
- package/build/tools/replay-folder-save.test.js.map +1 -1
- package/build/tools/replay-formatters.d.ts +1 -1
- package/build/tools/replay-formatters.js +1 -1
- package/build/tools/replay-tools.js +4 -4
- package/build/tools/replay-tools.js.map +1 -1
- package/build/tools/screenshot-tools.js +1 -1
- package/build/tools/screenshot-tools.js.map +1 -1
- package/build/tools/server-tools.js +1 -1
- package/build/tools/server-tools.js.map +1 -1
- package/build/tools/storage-tools.js +1 -1
- package/build/tools/storage-tools.js.map +1 -1
- package/docs/automation.md +3 -3
- package/docs/installation.md +3 -3
- package/docs/instructions.md +7 -7
- package/docs/mcp-instructions.md +3 -3
- package/docs/messages.md +5 -5
- package/docs/parser-plugins.md +4 -4
- package/docs/replay.md +11 -11
- package/docs/troubleshooting.md +2 -2
- package/package.json +1 -1
- package/skills/devharness/SKILL.md +3 -3
- package/build/session-detector.test.d.ts +0 -9
- package/build/session-detector.test.d.ts.map +0 -1
- package/build/session-detector.test.js +0 -45
- package/build/session-detector.test.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"paths-migration.test.js","sourceRoot":"","sources":["../../src/helpers/paths-migration.test.ts"],"names":[],"mappings":"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"}
|
package/build/helpers/paths.d.ts
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized path configuration for
|
|
2
|
+
* Centralized path configuration for devharness output directories
|
|
3
3
|
*
|
|
4
|
-
* All data defaults to working directory (<cwd>/.
|
|
5
|
-
* Use global: true to save to ~/.
|
|
4
|
+
* All data defaults to working directory (<cwd>/.devharness/).
|
|
5
|
+
* Use global: true to save to ~/.devharness/ instead.
|
|
6
6
|
* If cwd is invalid (e.g., "/"), falls back to global automatically.
|
|
7
|
+
*
|
|
8
|
+
* The directory was `.cdp-tools` before the rename in 0.9.0 and holds things
|
|
9
|
+
* users would miss: persistent Chrome profiles (logins, IndexedDB), config,
|
|
10
|
+
* recorded sequences, issues. So this migrates rather than switches - see
|
|
11
|
+
* resolveStateDir.
|
|
7
12
|
*/
|
|
8
13
|
import { z } from 'zod';
|
|
14
|
+
/**
|
|
15
|
+
* The state directory under `parent`, migrating a legacy `.cdp-tools` into
|
|
16
|
+
* place if that is what exists.
|
|
17
|
+
*
|
|
18
|
+
* Rename, not copy: it is atomic within a filesystem, so there is no window
|
|
19
|
+
* where half the profiles are in one directory and half in the other. If it
|
|
20
|
+
* fails for any reason - cross-device, permissions, a directory held open on
|
|
21
|
+
* Windows - keep using the legacy directory for this session rather than
|
|
22
|
+
* starting a fresh empty one. A user whose saved logins silently vanished
|
|
23
|
+
* would have no way to connect the two events.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveStateDir(parent: string): string;
|
|
26
|
+
/** Global state directory, migrated if needed. */
|
|
27
|
+
export declare function getGlobalBase(): string;
|
|
9
28
|
declare const pathOptionsSchema: z.ZodObject<{
|
|
10
29
|
global: z.ZodOptional<z.ZodBoolean>;
|
|
11
30
|
}, "strict", z.ZodTypeAny, {
|
|
@@ -33,12 +52,12 @@ export declare function initializePaths(): PathConfig;
|
|
|
33
52
|
*/
|
|
34
53
|
export declare function setWorkingDirOverride(dir: string): void;
|
|
35
54
|
/**
|
|
36
|
-
* Get path for
|
|
37
|
-
* Defaults to working directory, use global: true for ~/.
|
|
55
|
+
* Get path for devharness data.
|
|
56
|
+
* Defaults to working directory, use global: true for ~/.devharness/
|
|
38
57
|
*
|
|
39
58
|
* @param segments - Path segments to join (e.g., 'logs', 'debug.log')
|
|
40
|
-
* @param options - { global: true } to use ~/.
|
|
41
|
-
* @returns Full path like /project/.
|
|
59
|
+
* @param options - { global: true } to use ~/.devharness/ instead of cwd
|
|
60
|
+
* @returns Full path like /project/.devharness/logs/debug.log
|
|
42
61
|
*/
|
|
43
62
|
export declare function getOutputPath(...args: [...string[]] | [...string[], z.infer<typeof pathOptionsSchema>]): string;
|
|
44
63
|
/**
|
|
@@ -51,7 +70,7 @@ export declare function getConfigPath(): string;
|
|
|
51
70
|
/**
|
|
52
71
|
* Get path for saving new config.
|
|
53
72
|
*
|
|
54
|
-
* @param options - { global: true } to save to ~/.
|
|
73
|
+
* @param options - { global: true } to save to ~/.devharness/ (default: working directory)
|
|
55
74
|
* @returns Path to config.json
|
|
56
75
|
*/
|
|
57
76
|
export declare function getConfigSavePath(options?: {
|
|
@@ -62,7 +81,7 @@ export declare function getConfigSavePath(options?: {
|
|
|
62
81
|
* Uses system temp directory.
|
|
63
82
|
*
|
|
64
83
|
* @param segments - Path segments to join (e.g., 'a4-page.css')
|
|
65
|
-
* @returns Full path like /tmp/
|
|
84
|
+
* @returns Full path like /tmp/devharness/a4-page.css
|
|
66
85
|
*/
|
|
67
86
|
export declare function getTempPath(...segments: string[]): string;
|
|
68
87
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAyBtD;AAED,kDAAkD;AAClD,wBAAgB,aAAa,IAAI,MAAM,CAItC;AAED,QAAA,MAAM,iBAAiB;;;;;;EAEZ,CAAC;AAEZ,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAsBD;;;GAGG;AACH,wBAAgB,eAAe,IAAI,UAAU,CAW5C;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAMvD;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,GAAG,IAAI,EAAE,CAAC,GAAG,MAAM,EAAE,CAAC,GAAG,CAAC,GAAG,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC,GACxE,MAAM,CA0BR;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,IAAI,MAAM,CAatC;AAED;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE;IAAE,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,MAAM,CAUxE;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAGzD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAGD,sCAAsC;AACtC,wBAAgB,iBAAiB,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE/D;AAED,mEAAmE;AACnE,wBAAgB,aAAa,CAAC,GAAG,QAAQ,EAAE,MAAM,EAAE,GAAG,MAAM,CAE3D;AAED,sCAAsC;AACtC,eAAO,MAAM,iBAAiB,sBAAgB,CAAC"}
|
package/build/helpers/paths.js
CHANGED
|
@@ -1,15 +1,65 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Centralized path configuration for
|
|
2
|
+
* Centralized path configuration for devharness output directories
|
|
3
3
|
*
|
|
4
|
-
* All data defaults to working directory (<cwd>/.
|
|
5
|
-
* Use global: true to save to ~/.
|
|
4
|
+
* All data defaults to working directory (<cwd>/.devharness/).
|
|
5
|
+
* Use global: true to save to ~/.devharness/ instead.
|
|
6
6
|
* If cwd is invalid (e.g., "/"), falls back to global automatically.
|
|
7
|
+
*
|
|
8
|
+
* The directory was `.cdp-tools` before the rename in 0.9.0 and holds things
|
|
9
|
+
* users would miss: persistent Chrome profiles (logins, IndexedDB), config,
|
|
10
|
+
* recorded sequences, issues. So this migrates rather than switches - see
|
|
11
|
+
* resolveStateDir.
|
|
7
12
|
*/
|
|
8
13
|
import { join } from 'path';
|
|
9
14
|
import { homedir, tmpdir } from 'os';
|
|
10
|
-
import { existsSync, accessSync, constants } from 'fs';
|
|
15
|
+
import { existsSync, accessSync, constants, renameSync } from 'fs';
|
|
11
16
|
import { z } from 'zod';
|
|
12
|
-
const OUTPUT_DIR = '.
|
|
17
|
+
const OUTPUT_DIR = '.devharness';
|
|
18
|
+
const LEGACY_OUTPUT_DIR = '.cdp-tools';
|
|
19
|
+
/** Migration is announced once per process, not once per path lookup. */
|
|
20
|
+
let migrationLogged = false;
|
|
21
|
+
/**
|
|
22
|
+
* The state directory under `parent`, migrating a legacy `.cdp-tools` into
|
|
23
|
+
* place if that is what exists.
|
|
24
|
+
*
|
|
25
|
+
* Rename, not copy: it is atomic within a filesystem, so there is no window
|
|
26
|
+
* where half the profiles are in one directory and half in the other. If it
|
|
27
|
+
* fails for any reason - cross-device, permissions, a directory held open on
|
|
28
|
+
* Windows - keep using the legacy directory for this session rather than
|
|
29
|
+
* starting a fresh empty one. A user whose saved logins silently vanished
|
|
30
|
+
* would have no way to connect the two events.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveStateDir(parent) {
|
|
33
|
+
const current = join(parent, OUTPUT_DIR);
|
|
34
|
+
const legacy = join(parent, LEGACY_OUTPUT_DIR);
|
|
35
|
+
if (existsSync(current))
|
|
36
|
+
return current;
|
|
37
|
+
if (!existsSync(legacy))
|
|
38
|
+
return current;
|
|
39
|
+
try {
|
|
40
|
+
renameSync(legacy, current);
|
|
41
|
+
if (!migrationLogged) {
|
|
42
|
+
console.error(`[devharness] migrated ${legacy} -> ${current}`);
|
|
43
|
+
migrationLogged = true;
|
|
44
|
+
}
|
|
45
|
+
return current;
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (!migrationLogged) {
|
|
49
|
+
console.error(`[devharness] could not migrate ${legacy} to ${current} ` +
|
|
50
|
+
`(${error instanceof Error ? error.message : String(error)}); ` +
|
|
51
|
+
`continuing to use ${legacy}`);
|
|
52
|
+
migrationLogged = true;
|
|
53
|
+
}
|
|
54
|
+
return legacy;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/** Global state directory, migrated if needed. */
|
|
58
|
+
export function getGlobalBase() {
|
|
59
|
+
return process.env.DEVHARNESS_DIR
|
|
60
|
+
|| process.env.CDP_TOOLS_DIR // pre-0.9.0 name, still honoured
|
|
61
|
+
|| resolveStateDir(homedir());
|
|
62
|
+
}
|
|
13
63
|
const pathOptionsSchema = z.object({
|
|
14
64
|
global: z.boolean().optional(),
|
|
15
65
|
}).strict();
|
|
@@ -39,13 +89,13 @@ function isValidWorkingDirectory(dir) {
|
|
|
39
89
|
* Called automatically on first path request, but can be called explicitly for early initialization.
|
|
40
90
|
*/
|
|
41
91
|
export function initializePaths() {
|
|
42
|
-
const
|
|
43
|
-
const globalBase = envOverride || join(homedir(), OUTPUT_DIR);
|
|
92
|
+
const globalBase = getGlobalBase();
|
|
44
93
|
const cwd = process.cwd();
|
|
45
94
|
const workingDirBase = isValidWorkingDirectory(cwd)
|
|
46
|
-
?
|
|
95
|
+
? resolveStateDir(cwd)
|
|
47
96
|
: null;
|
|
48
|
-
|
|
97
|
+
// Ephemeral, so it gets the new name outright with nothing to migrate.
|
|
98
|
+
const tempBase = join(tmpdir(), 'devharness');
|
|
49
99
|
pathConfig = { globalBase, workingDirBase, tempBase };
|
|
50
100
|
return pathConfig;
|
|
51
101
|
}
|
|
@@ -63,15 +113,15 @@ export function setWorkingDirOverride(dir) {
|
|
|
63
113
|
}
|
|
64
114
|
if (!pathConfig)
|
|
65
115
|
initializePaths();
|
|
66
|
-
pathConfig.workingDirBase =
|
|
116
|
+
pathConfig.workingDirBase = resolveStateDir(dir);
|
|
67
117
|
}
|
|
68
118
|
/**
|
|
69
|
-
* Get path for
|
|
70
|
-
* Defaults to working directory, use global: true for ~/.
|
|
119
|
+
* Get path for devharness data.
|
|
120
|
+
* Defaults to working directory, use global: true for ~/.devharness/
|
|
71
121
|
*
|
|
72
122
|
* @param segments - Path segments to join (e.g., 'logs', 'debug.log')
|
|
73
|
-
* @param options - { global: true } to use ~/.
|
|
74
|
-
* @returns Full path like /project/.
|
|
123
|
+
* @param options - { global: true } to use ~/.devharness/ instead of cwd
|
|
124
|
+
* @returns Full path like /project/.devharness/logs/debug.log
|
|
75
125
|
*/
|
|
76
126
|
export function getOutputPath(...args) {
|
|
77
127
|
if (!pathConfig)
|
|
@@ -121,7 +171,7 @@ export function getConfigPath() {
|
|
|
121
171
|
/**
|
|
122
172
|
* Get path for saving new config.
|
|
123
173
|
*
|
|
124
|
-
* @param options - { global: true } to save to ~/.
|
|
174
|
+
* @param options - { global: true } to save to ~/.devharness/ (default: working directory)
|
|
125
175
|
* @returns Path to config.json
|
|
126
176
|
*/
|
|
127
177
|
export function getConfigSavePath(options) {
|
|
@@ -139,7 +189,7 @@ export function getConfigSavePath(options) {
|
|
|
139
189
|
* Uses system temp directory.
|
|
140
190
|
*
|
|
141
191
|
* @param segments - Path segments to join (e.g., 'a4-page.css')
|
|
142
|
-
* @returns Full path like /tmp/
|
|
192
|
+
* @returns Full path like /tmp/devharness/a4-page.css
|
|
143
193
|
*/
|
|
144
194
|
export function getTempPath(...segments) {
|
|
145
195
|
if (!pathConfig)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/helpers/paths.ts"],"names":[],"mappings":"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"}
|
package/build/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// Early stderr logging for debugging startup issues
|
|
3
|
-
console.error(`[
|
|
3
|
+
console.error(`[devharness] Process starting (PID: ${process.pid})`);
|
|
4
4
|
// Capture startup time immediately before any imports
|
|
5
5
|
const STARTUP_TIME = performance.now();
|
|
6
6
|
/**
|
|
@@ -63,7 +63,7 @@ import { join, dirname } from 'path';
|
|
|
63
63
|
import { fileURLToPath } from 'url';
|
|
64
64
|
import { debugLog, enableDebugLogging, disableDebugLogging, isDebugEnabled, enableHistoryLogging, setStartupMetrics } from './debug-logger.js';
|
|
65
65
|
import { validateReference, requireValidReference, deriveConnectionReference, UNNAMED_CONNECTION, InvalidReferenceError } from './reference-validator.js';
|
|
66
|
-
import { initializePaths } from './helpers/paths.js';
|
|
66
|
+
import { initializePaths, resolveStateDir } from './helpers/paths.js';
|
|
67
67
|
import { cleanupStaleTempFiles, cleanupStaleTempFilesSync } from './atomic-write.js';
|
|
68
68
|
import { createSessionDetector } from './session-detector.js';
|
|
69
69
|
import { serverClaims } from './server-claims.js';
|
|
@@ -114,13 +114,13 @@ async function findAvailablePort(startPort) {
|
|
|
114
114
|
// Explicitly bind to IPv4 localhost to match Chrome's behavior
|
|
115
115
|
server.listen(startPort, '127.0.0.1', () => {
|
|
116
116
|
const port = server.address().port;
|
|
117
|
-
console.error(`[
|
|
117
|
+
console.error(`[devharness] findAvailablePort: Port ${port} is available`);
|
|
118
118
|
server.close(() => resolve(port));
|
|
119
119
|
});
|
|
120
120
|
server.on('error', (err) => {
|
|
121
121
|
if (err.code === 'EADDRINUSE') {
|
|
122
122
|
// Port in use, try next one
|
|
123
|
-
console.error(`[
|
|
123
|
+
console.error(`[devharness] findAvailablePort: Port ${startPort} is in use, trying ${startPort + 1}`);
|
|
124
124
|
resolve(findAvailablePort(startPort + 1));
|
|
125
125
|
}
|
|
126
126
|
else {
|
|
@@ -228,7 +228,7 @@ async function loadInstructions() {
|
|
|
228
228
|
return instructions + nudge;
|
|
229
229
|
}
|
|
230
230
|
catch (error) {
|
|
231
|
-
console.error('[
|
|
231
|
+
console.error('[devharness] Failed to load instructions file:', error instanceof Error ? error.message : error);
|
|
232
232
|
return undefined;
|
|
233
233
|
}
|
|
234
234
|
}
|
|
@@ -236,7 +236,7 @@ async function loadInstructions() {
|
|
|
236
236
|
const sourceMapHandler = new SourceMapHandler();
|
|
237
237
|
const chromeLauncher = new ChromeLauncher({
|
|
238
238
|
// Resolved lazily so a live config reload of chrome.persistentProfileRoot
|
|
239
|
-
// (global ~/.
|
|
239
|
+
// (global ~/.devharness/profiles by default, or a project-local override)
|
|
240
240
|
// is picked up without restarting the server.
|
|
241
241
|
persistentProfileRoot: () => configManager.getPersistentProfileRoot(),
|
|
242
242
|
});
|
|
@@ -377,7 +377,7 @@ const connectionTools = {
|
|
|
377
377
|
reference: z.string().optional().describe('Connection reference name (3 descriptive words). If not provided, defaults to "unnamed-connection-default". Use this to identify the connection when calling other tools.'),
|
|
378
378
|
width: z.number().optional().describe('Viewport width in CSS px. Sizes the real OS window, not an emulated viewport, so the page keeps tracking window resizes. Bigger than the display is clamped and reported. Headless emulates instead.'),
|
|
379
379
|
height: z.number().optional().describe('Viewport height in CSS px. Sized like `width`.'),
|
|
380
|
-
profile: z.string().optional().describe('Named persistent Chrome profile, e.g. "device-a". Naming a profile makes it persistent: it maps to a stable user-data-dir under ~/.
|
|
380
|
+
profile: z.string().optional().describe('Named persistent Chrome profile, e.g. "device-a". Naming a profile makes it persistent: it maps to a stable user-data-dir under ~/.devharness/profiles (override per project with chrome.persistentProfileRoot) and is never deleted, so cookies, localStorage and IndexedDB - including non-extractable CryptoKeys - survive across runs. Created on first use. Does NOT pin a port; port selection is unchanged. Wipe it with config({action:"resetProfile", profile:"device-a"}). Only one live Chrome may hold a given profile at a time.'),
|
|
381
381
|
chromeArgs: z.array(z.string()).optional().describe('Extra Chrome command-line flags to pass through at launch, e.g. ["--use-fake-device-for-media-stream", "--use-fake-ui-for-media-stream"]. Merged after the managed defaults. The CDP_TOOLS_EXTRA_CHROME_ARGS env var (space-separated) is also always merged. Only applies when this call actually launches Chrome (ignored when an existing instance on the port is reused).'),
|
|
382
382
|
}).strict(), async (args) => {
|
|
383
383
|
// Validate reference FIRST, before launching Chrome
|
|
@@ -653,7 +653,7 @@ const connectionTools = {
|
|
|
653
653
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
654
654
|
}
|
|
655
655
|
catch (reloadError) {
|
|
656
|
-
console.error(`[
|
|
656
|
+
console.error(`[devharness] Warning: Page reload failed: ${reloadError.message}`);
|
|
657
657
|
}
|
|
658
658
|
}
|
|
659
659
|
}
|
|
@@ -782,7 +782,7 @@ const connectionTools = {
|
|
|
782
782
|
reason: z.string().describe('Why Chrome launcher needs to be reset'),
|
|
783
783
|
}).strict(), async (args) => {
|
|
784
784
|
// Log the reason for audit purposes
|
|
785
|
-
console.error(`[
|
|
785
|
+
console.error(`[devharness] resetChromeLauncher called - Reason: ${args.reason}`);
|
|
786
786
|
chromeLauncher.reset();
|
|
787
787
|
return createSuccessResponse('CHROME_LAUNCHER_RESET');
|
|
788
788
|
}),
|
|
@@ -805,10 +805,10 @@ const connectionTools = {
|
|
|
805
805
|
if (args.enabled) {
|
|
806
806
|
await enableDebugLogging(); // Now async to log startup metrics
|
|
807
807
|
return createSuccessResponse('DEBUG_LOGGING_ENABLED', {
|
|
808
|
-
message: 'Debug logging enabled. Logs will be written to .
|
|
808
|
+
message: 'Debug logging enabled. Logs will be written to .devharness/logs/debug.log'
|
|
809
809
|
}, {
|
|
810
810
|
enabled: true,
|
|
811
|
-
message: 'Debug logging enabled. Logs will be written to .
|
|
811
|
+
message: 'Debug logging enabled. Logs will be written to .devharness/logs/debug.log'
|
|
812
812
|
});
|
|
813
813
|
}
|
|
814
814
|
else {
|
|
@@ -826,10 +826,10 @@ const connectionTools = {
|
|
|
826
826
|
return createSuccessResponse('DEBUG_LOGGING_STATUS', {
|
|
827
827
|
status: enabled ? 'enabled' : 'disabled',
|
|
828
828
|
enabled, // Pass boolean for conditionals
|
|
829
|
-
logFile: '.
|
|
829
|
+
logFile: '.devharness/logs/debug.log'
|
|
830
830
|
}, {
|
|
831
831
|
enabled,
|
|
832
|
-
logFile: '.
|
|
832
|
+
logFile: '.devharness/logs/debug.log'
|
|
833
833
|
});
|
|
834
834
|
}),
|
|
835
835
|
connectDebugger: createTool('Connect to debugger', z.object({
|
|
@@ -923,7 +923,7 @@ const connectionTools = {
|
|
|
923
923
|
}
|
|
924
924
|
catch (reloadError) {
|
|
925
925
|
// Log warning but don't fail - page might already be loaded
|
|
926
|
-
console.error(`[
|
|
926
|
+
console.error(`[devharness] Warning: Page reload failed: ${reloadError.message}`);
|
|
927
927
|
}
|
|
928
928
|
}
|
|
929
929
|
features.push('browser-automation', 'console-monitoring', 'network-monitoring');
|
|
@@ -1002,7 +1002,7 @@ const connectionTools = {
|
|
|
1002
1002
|
reference: z.string().describe('3 descriptive words of the connection to disconnect'),
|
|
1003
1003
|
}).strict(), async (args) => {
|
|
1004
1004
|
// Log the reason for audit purposes
|
|
1005
|
-
console.error(`[
|
|
1005
|
+
console.error(`[devharness] disconnectDebugger called - Reason: ${args.reason}, Reference: ${args.reference}`);
|
|
1006
1006
|
// Find connection by reference
|
|
1007
1007
|
const connection = connectionManager.findConnectionByReference(args.reference);
|
|
1008
1008
|
if (!connection) {
|
|
@@ -1327,7 +1327,7 @@ function registerToolHandlers(server) {
|
|
|
1327
1327
|
// Check for tool dependency conflicts (blocks ALL tools except config)
|
|
1328
1328
|
if (toolName !== 'config' && configManager.hasDependencyConflicts()) {
|
|
1329
1329
|
const conflicts = configManager.getDependencyConflicts();
|
|
1330
|
-
const configPath = configManager.getStatus().loadedFrom || '.
|
|
1330
|
+
const configPath = configManager.getStatus().loadedFrom || '.devharness/config.json';
|
|
1331
1331
|
return {
|
|
1332
1332
|
content: [
|
|
1333
1333
|
{
|
|
@@ -1549,7 +1549,7 @@ async function runCliSequence(argv) {
|
|
|
1549
1549
|
catch {
|
|
1550
1550
|
attempts++;
|
|
1551
1551
|
if (attempts >= maxAttempts) {
|
|
1552
|
-
console.error(`[
|
|
1552
|
+
console.error(`[devharness] Failed to reserve a port after ${maxAttempts} attempts`);
|
|
1553
1553
|
process.exit(1);
|
|
1554
1554
|
}
|
|
1555
1555
|
process.env.MCP_DEBUG_PORT = String(port + 1);
|
|
@@ -1595,10 +1595,10 @@ async function main() {
|
|
|
1595
1595
|
await runCliSequence(process.argv.slice(3));
|
|
1596
1596
|
return;
|
|
1597
1597
|
}
|
|
1598
|
-
console.error(`[
|
|
1598
|
+
console.error(`[devharness] main() called (PID: ${process.pid})`);
|
|
1599
1599
|
// Initialize path configuration early (before any file operations)
|
|
1600
1600
|
const pathConfig = initializePaths();
|
|
1601
|
-
console.error(`[
|
|
1601
|
+
console.error(`[devharness] Path config: global=${pathConfig.globalBase}, workingDir=${pathConfig.workingDirBase ?? 'none (using global fallback)'}`);
|
|
1602
1602
|
// Clean up stale temp files from previous crashed/killed processes
|
|
1603
1603
|
// Run in background - don't block startup
|
|
1604
1604
|
Promise.all([
|
|
@@ -1607,7 +1607,7 @@ async function main() {
|
|
|
1607
1607
|
]).then(([globalResult, localResult]) => {
|
|
1608
1608
|
const totalCleaned = globalResult.cleaned.length + localResult.cleaned.length;
|
|
1609
1609
|
if (totalCleaned > 0) {
|
|
1610
|
-
console.error(`[
|
|
1610
|
+
console.error(`[devharness] Cleaned ${totalCleaned} stale temp file(s)`);
|
|
1611
1611
|
}
|
|
1612
1612
|
}).catch(() => {
|
|
1613
1613
|
// Ignore cleanup errors - best effort only
|
|
@@ -1642,7 +1642,7 @@ async function main() {
|
|
|
1642
1642
|
if (orchestratorInstance || !sessionDetectorInstance)
|
|
1643
1643
|
return;
|
|
1644
1644
|
try {
|
|
1645
|
-
const configDir = join(cwd, '
|
|
1645
|
+
const configDir = join(resolveStateDir(cwd), 'config');
|
|
1646
1646
|
mkdirSync(join(configDir, 'classifiers'), { recursive: true });
|
|
1647
1647
|
mkdirSync(join(configDir, 'extractors'), { recursive: true });
|
|
1648
1648
|
mkdirSync(join(configDir, 'state-machines'), { recursive: true });
|
|
@@ -1747,14 +1747,14 @@ async function main() {
|
|
|
1747
1747
|
// Reserve the port by binding a socket to it
|
|
1748
1748
|
try {
|
|
1749
1749
|
await portReserver.reserve(port);
|
|
1750
|
-
console.error(`[
|
|
1750
|
+
console.error(`[devharness] Reserved debug port: ${port}`);
|
|
1751
1751
|
reservationSucceeded = true;
|
|
1752
1752
|
}
|
|
1753
1753
|
catch (error) {
|
|
1754
1754
|
attempts++;
|
|
1755
|
-
console.error(`[
|
|
1755
|
+
console.error(`[devharness] Port ${port} reservation failed (attempt ${attempts}/${maxAttempts}), trying next port...`);
|
|
1756
1756
|
if (attempts >= maxAttempts) {
|
|
1757
|
-
console.error(`[
|
|
1757
|
+
console.error(`[devharness] Failed to reserve a port after ${maxAttempts} attempts`);
|
|
1758
1758
|
process.exit(1);
|
|
1759
1759
|
}
|
|
1760
1760
|
// Try the next port
|
|
@@ -1771,12 +1771,12 @@ async function main() {
|
|
|
1771
1771
|
registerToolHandlers(server);
|
|
1772
1772
|
const toolRegistrationTime = performance.now() - toolRegistrationStart;
|
|
1773
1773
|
// Connect to transport
|
|
1774
|
-
console.error(`[
|
|
1774
|
+
console.error(`[devharness] Connecting to transport (PID: ${process.pid})`);
|
|
1775
1775
|
const transportStart = performance.now();
|
|
1776
1776
|
const transport = new StdioServerTransport();
|
|
1777
1777
|
await server.connect(transport);
|
|
1778
1778
|
const transportTime = performance.now() - transportStart;
|
|
1779
|
-
console.error(`[
|
|
1779
|
+
console.error(`[devharness] Transport connected (PID: ${process.pid})`);
|
|
1780
1780
|
// Note: Config was already loaded earlier (before orchestrator startup) for debug logging
|
|
1781
1781
|
// Announce this session BEFORE the manager decides anything: collection asks
|
|
1782
1782
|
// whether another live session is working in a server's directory, and a
|
|
@@ -1786,16 +1786,16 @@ async function main() {
|
|
|
1786
1786
|
// Initialize server manager - recover running servers and start auto-run servers
|
|
1787
1787
|
const serverInitResult = await serverManager.initialize();
|
|
1788
1788
|
if (serverInitResult.recovered.length > 0) {
|
|
1789
|
-
console.error(`[
|
|
1789
|
+
console.error(`[devharness] Recovered ${serverInitResult.recovered.length} running server(s): ${serverInitResult.recovered.join(', ')}`);
|
|
1790
1790
|
}
|
|
1791
1791
|
if (serverInitResult.started.length > 0) {
|
|
1792
|
-
console.error(`[
|
|
1792
|
+
console.error(`[devharness] Auto-started ${serverInitResult.started.length} server(s): ${serverInitResult.started.join(', ')}`);
|
|
1793
1793
|
}
|
|
1794
1794
|
if (serverInitResult.failed.length > 0) {
|
|
1795
|
-
console.error(`[
|
|
1795
|
+
console.error(`[devharness] Failed to auto-start ${serverInitResult.failed.length} server(s): ${serverInitResult.failed.join(', ')}`);
|
|
1796
1796
|
}
|
|
1797
1797
|
// Dashboard is initialized in session detection callback after sessionId is known
|
|
1798
|
-
console.error(`[
|
|
1798
|
+
console.error(`[devharness] Server ready (PID: ${process.pid})`);
|
|
1799
1799
|
// Calculate total startup time and store metrics for later logging
|
|
1800
1800
|
const totalStartupTime = performance.now() - STARTUP_TIME;
|
|
1801
1801
|
setStartupMetrics({
|
|
@@ -1829,12 +1829,12 @@ async function main() {
|
|
|
1829
1829
|
// autoConnect: false and never connected, or orphaned by some other cleanup path).
|
|
1830
1830
|
const closedCount = await connectionManager.closeInactiveConnections(INACTIVITY_THRESHOLD);
|
|
1831
1831
|
if (closedCount > 0) {
|
|
1832
|
-
console.error(`[
|
|
1832
|
+
console.error(`[devharness] Closed ${closedCount} inactive connection(s)`);
|
|
1833
1833
|
await debugLog('index', `Closed ${closedCount} inactive connection(s)`);
|
|
1834
1834
|
}
|
|
1835
1835
|
for (const port of chromeLauncher.getRunningPorts()) {
|
|
1836
1836
|
if (!connectionManager.hasBrowser('localhost', port)) {
|
|
1837
|
-
console.error(`[
|
|
1837
|
+
console.error(`[devharness] Killing orphaned Chrome on port ${port} (no tracked connections)`);
|
|
1838
1838
|
await debugLog('index', `Killing orphaned Chrome on port ${port} (no tracked connections) due to inactivity`);
|
|
1839
1839
|
chromeLauncher.setPendingCloseReason(port, 'inactivity');
|
|
1840
1840
|
await chromeLauncher.kill(port);
|
|
@@ -1842,7 +1842,7 @@ async function main() {
|
|
|
1842
1842
|
}
|
|
1843
1843
|
}
|
|
1844
1844
|
catch (error) {
|
|
1845
|
-
console.error(`[
|
|
1845
|
+
console.error(`[devharness] Error during cleanup: ${error}`);
|
|
1846
1846
|
await debugLog('index', `Error during inactivity cleanup: ${error}`);
|
|
1847
1847
|
}
|
|
1848
1848
|
}, CLEANUP_INTERVAL) : null;
|
|
@@ -1864,7 +1864,7 @@ async function main() {
|
|
|
1864
1864
|
return; // Prevent multiple cleanup calls
|
|
1865
1865
|
}
|
|
1866
1866
|
isCleaningUp = true;
|
|
1867
|
-
console.error(`[
|
|
1867
|
+
console.error(`[devharness] Received ${signal}, cleaning up...`);
|
|
1868
1868
|
try {
|
|
1869
1869
|
if (cleanupInterval)
|
|
1870
1870
|
clearInterval(cleanupInterval); // Stop periodic cleanup
|
|
@@ -1876,15 +1876,15 @@ async function main() {
|
|
|
1876
1876
|
try {
|
|
1877
1877
|
const { stopped, keptForOthers } = await serverManager.stopOwnedServers();
|
|
1878
1878
|
if (stopped.length > 0) {
|
|
1879
|
-
console.error(`[
|
|
1879
|
+
console.error(`[devharness] Stopped ${stopped.length} owned dev server(s): ${stopped.join(', ')}`);
|
|
1880
1880
|
}
|
|
1881
1881
|
if (keptForOthers.length > 0) {
|
|
1882
|
-
console.error(`[
|
|
1882
|
+
console.error(`[devharness] Left ${keptForOthers.length} dev server(s) for other sessions: ${keptForOthers.join(', ')}`);
|
|
1883
1883
|
}
|
|
1884
1884
|
await serverManager.getPortMonitor().stopAll();
|
|
1885
1885
|
}
|
|
1886
1886
|
catch (error) {
|
|
1887
|
-
console.error(`[
|
|
1887
|
+
console.error(`[devharness] Error releasing dev servers: ${error}`);
|
|
1888
1888
|
}
|
|
1889
1889
|
// This session is over: nothing of ours may keep pinning a server that
|
|
1890
1890
|
// the next session would otherwise collect.
|
|
@@ -1920,12 +1920,12 @@ async function main() {
|
|
|
1920
1920
|
: { cleaned: [] };
|
|
1921
1921
|
const totalCleaned = globalCleaned.cleaned.length + localCleaned.cleaned.length;
|
|
1922
1922
|
if (totalCleaned > 0) {
|
|
1923
|
-
console.error(`[
|
|
1923
|
+
console.error(`[devharness] Cleaned ${totalCleaned} temp file(s) on shutdown`);
|
|
1924
1924
|
}
|
|
1925
|
-
console.error('[
|
|
1925
|
+
console.error('[devharness] Cleanup complete');
|
|
1926
1926
|
}
|
|
1927
1927
|
catch (error) {
|
|
1928
|
-
console.error(`[
|
|
1928
|
+
console.error(`[devharness] Cleanup error: ${error}`);
|
|
1929
1929
|
}
|
|
1930
1930
|
process.exit(0);
|
|
1931
1931
|
};
|
|
@@ -1947,7 +1947,7 @@ async function main() {
|
|
|
1947
1947
|
// Handle normal exit (catch-all)
|
|
1948
1948
|
process.on('exit', () => {
|
|
1949
1949
|
if (!isCleaningUp) {
|
|
1950
|
-
console.error('[
|
|
1950
|
+
console.error('[devharness] Process exiting');
|
|
1951
1951
|
}
|
|
1952
1952
|
});
|
|
1953
1953
|
// Catch uncaught exceptions and unhandled rejections for debugging.
|
|
@@ -1973,7 +1973,7 @@ async function main() {
|
|
|
1973
1973
|
const msg = (error && error.message) ? error.message : String(error);
|
|
1974
1974
|
if (msg === lastUncaughtMsg && now - lastUncaughtAt < 1000) {
|
|
1975
1975
|
if (++uncaughtCount > 50) {
|
|
1976
|
-
writeErr('[
|
|
1976
|
+
writeErr('[devharness] Same uncaught exception >50x in <1s — exiting to break the loop.');
|
|
1977
1977
|
process.exit(1);
|
|
1978
1978
|
}
|
|
1979
1979
|
return;
|
|
@@ -1981,21 +1981,21 @@ async function main() {
|
|
|
1981
1981
|
lastUncaughtMsg = msg;
|
|
1982
1982
|
lastUncaughtAt = now;
|
|
1983
1983
|
uncaughtCount = 0;
|
|
1984
|
-
writeErr(`[
|
|
1984
|
+
writeErr(`[devharness] UNCAUGHT EXCEPTION (${origin}): ${msg}`);
|
|
1985
1985
|
try {
|
|
1986
1986
|
const s = error?.stack;
|
|
1987
1987
|
if (s)
|
|
1988
|
-
writeErr(`[
|
|
1988
|
+
writeErr(`[devharness] Stack: ${s}`);
|
|
1989
1989
|
}
|
|
1990
1990
|
catch { /* stack getter can throw */ }
|
|
1991
1991
|
});
|
|
1992
1992
|
process.on('unhandledRejection', (reason) => {
|
|
1993
1993
|
const msg = (reason && reason.message) ? reason.message : String(reason);
|
|
1994
|
-
writeErr(`[
|
|
1994
|
+
writeErr(`[devharness] UNHANDLED REJECTION: ${msg}`);
|
|
1995
1995
|
try {
|
|
1996
1996
|
const s = reason?.stack;
|
|
1997
1997
|
if (s)
|
|
1998
|
-
writeErr(`[
|
|
1998
|
+
writeErr(`[devharness] Stack: ${s}`);
|
|
1999
1999
|
}
|
|
2000
2000
|
catch { /* stack getter can throw */ }
|
|
2001
2001
|
});
|