debughub 1.0.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 +49 -0
- package/dist/cli.js +54 -0
- package/dist/commands/clear.js +65 -0
- package/dist/commands/doctor.js +96 -0
- package/dist/commands/install.js +88 -0
- package/dist/commands/search.js +106 -0
- package/dist/commands/server.js +94 -0
- package/dist/commands/start.js +123 -0
- package/dist/commands/stop.js +68 -0
- package/dist/commands/tail.js +97 -0
- package/dist/commands/verify.js +91 -0
- package/dist/vendor/1.0.0/MANIFEST.json +13 -0
- package/dist/vendor/1.0.0/README.md +26 -0
- package/dist/vendor/1.0.0/csharp/DebugProbe.cs +97 -0
- package/dist/vendor/1.0.0/go/debug_probe.go +81 -0
- package/dist/vendor/1.0.0/http/EVENT_CONTRACT.md +54 -0
- package/dist/vendor/1.0.0/http/event.example.json +13 -0
- package/dist/vendor/1.0.0/java/DebugProbe.java +114 -0
- package/dist/vendor/1.0.0/php/DebugProbe.php +68 -0
- package/dist/vendor/1.0.0/python/debugProbe.py +54 -0
- package/dist/vendor/1.0.0/rust/debug_probe.rs +194 -0
- package/dist/vendor/1.0.0/ts/debugProbe.browser.ts +45 -0
- package/dist/vendor/1.0.0/ts/debugProbe.ts +63 -0
- package/docs/DEBUG_MODE_PROMPT.md +24 -0
- package/docs/INSTALLING_INTO_A_REPO.md +52 -0
- package/docs/RUNTIME_PREREQS.md +67 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# DebugHub
|
|
2
|
+
|
|
3
|
+
A cross-platform CLI tool providing a Cursor-like "Debug Mode" loop for any coding agent (Codex, Claude, etc.), without requiring MCP or complex remote execution setups.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
- **Runtime Setup Guide**: See `docs/RUNTIME_PREREQS.md` for installing Java/Go/Python/Rust/PHP/.NET required by helper E2E tests.
|
|
7
|
+
- **Agent-Agnostic**: Works with any coding assistant capable of modifying files and running basic OS commands.
|
|
8
|
+
- **Vendored Runtime Helpers**: Small, standalone helpers for TypeScript/Node, Browser TS/JS, Java, Python, Rust, PHP, Go, and C# are installed in `.debughub/vendor`.
|
|
9
|
+
- **Shared HTTP Contract**: All helpers follow one event contract (`.debughub/vendor/<version>/http/EVENT_CONTRACT.md` after `debughub install`) and submit best-effort HTTP events to the local collector.
|
|
10
|
+
- **Deterministic**: A `MANIFEST.json` and strict integrity verification ensures files are not tampered with.
|
|
11
|
+
- **Safe and Local**: By default, the collector server only binds to `127.0.0.1`.
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
1. Install inside any repository:
|
|
16
|
+
```bash
|
|
17
|
+
npx debughub install
|
|
18
|
+
```
|
|
19
|
+
2. Start the local collector:
|
|
20
|
+
```bash
|
|
21
|
+
npx debughub start
|
|
22
|
+
```
|
|
23
|
+
3. Read the given `sessionId` and endpoint. Follow the instructions to configure your environment variables:
|
|
24
|
+
- `DEBUGHUB_ENABLED=1`
|
|
25
|
+
- `DEBUGHUB_SESSION=<sessionId>`
|
|
26
|
+
- `DEBUGHUB_ENDPOINT=http://127.0.0.1:<port>`
|
|
27
|
+
|
|
28
|
+
4. Use the helper inside your app code (see `.debughub/vendor/<version>`).
|
|
29
|
+
5. Run your app and reproduce the issue.
|
|
30
|
+
6. Check logs:
|
|
31
|
+
```bash
|
|
32
|
+
npx debughub tail --n 50
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Security Model
|
|
36
|
+
- No network traffic leaves your local machine.
|
|
37
|
+
- No remote code is downloaded at runtime.
|
|
38
|
+
- No `eval` or shell spawning in helpers.
|
|
39
|
+
- The `debughub verify` command validates the helper code matches the official SHA-256 signatures.
|
|
40
|
+
|
|
41
|
+
## Commands
|
|
42
|
+
- `debughub install`: Installs support files to `.debughub/`.
|
|
43
|
+
- `debughub start`: Starts the collector server in the background.
|
|
44
|
+
- `debughub stop`: Stops the background collector.
|
|
45
|
+
- `debughub tail`: Print the latest recorded events.
|
|
46
|
+
- `debughub search "<query>"`: Search events by text or label.
|
|
47
|
+
- `debughub clear`: Clears the output log file.
|
|
48
|
+
- `debughub verify`: Verifies integrity of the vendor code in `.debughub/`.
|
|
49
|
+
- `debughub doctor`: Checks installation and system status.
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const install_1 = require("./commands/install");
|
|
6
|
+
const start_1 = require("./commands/start");
|
|
7
|
+
const stop_1 = require("./commands/stop");
|
|
8
|
+
const tail_1 = require("./commands/tail");
|
|
9
|
+
const search_1 = require("./commands/search");
|
|
10
|
+
const clear_1 = require("./commands/clear");
|
|
11
|
+
const verify_1 = require("./commands/verify");
|
|
12
|
+
const doctor_1 = require("./commands/doctor");
|
|
13
|
+
const server_1 = require("./commands/server");
|
|
14
|
+
const program = new commander_1.Command();
|
|
15
|
+
const pkg = require('../package.json');
|
|
16
|
+
program
|
|
17
|
+
.name('debughub')
|
|
18
|
+
.description('DebugHub: Agent-agnostic local debug loop')
|
|
19
|
+
.version(pkg.version);
|
|
20
|
+
program.command('install')
|
|
21
|
+
.description('Installs vendored DebugHub runtime helpers into the current repo')
|
|
22
|
+
.action(install_1.install);
|
|
23
|
+
program.command('start')
|
|
24
|
+
.description('Starts the local collector server')
|
|
25
|
+
.action(start_1.start);
|
|
26
|
+
program.command('stop')
|
|
27
|
+
.description('Stops the local collector server')
|
|
28
|
+
.action(stop_1.stop);
|
|
29
|
+
program.command('tail')
|
|
30
|
+
.description('Tails the last N events')
|
|
31
|
+
.option('-n, --n <number>', 'Number of events', '200')
|
|
32
|
+
.option('--session <id>', 'Session ID')
|
|
33
|
+
.option('--json', 'Print raw JSON instead of pretty output', false)
|
|
34
|
+
.action(tail_1.tail);
|
|
35
|
+
program.command('search <query>')
|
|
36
|
+
.description('Searches the JSONL file for substring matches')
|
|
37
|
+
.option('--label <label>', 'Filter by label')
|
|
38
|
+
.option('--hypothesis <id>', 'Filter by hypothesisId')
|
|
39
|
+
.option('--level <level>', 'Filter by level')
|
|
40
|
+
.option('--session <id>', 'Session ID')
|
|
41
|
+
.action(search_1.search);
|
|
42
|
+
program.command('clear')
|
|
43
|
+
.description('Clears the current session output file')
|
|
44
|
+
.option('--session <id>', 'Session ID')
|
|
45
|
+
.action(clear_1.clear);
|
|
46
|
+
program.command('verify')
|
|
47
|
+
.description('Verifies integrity of installed vendor files')
|
|
48
|
+
.action(verify_1.verify);
|
|
49
|
+
program.command('doctor')
|
|
50
|
+
.description('Prints quick diagnostics')
|
|
51
|
+
.action(doctor_1.doctor);
|
|
52
|
+
program.command('_server <sessionId> <port>', { hidden: true })
|
|
53
|
+
.action(server_1._server);
|
|
54
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.clear = clear;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function clear(options) {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
const debughubDir = path.join(cwd, '.debughub');
|
|
42
|
+
let sessionId = options.session;
|
|
43
|
+
if (!sessionId) {
|
|
44
|
+
const stateFile = path.join(debughubDir, 'state.json');
|
|
45
|
+
if (fs.existsSync(stateFile)) {
|
|
46
|
+
try {
|
|
47
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
48
|
+
sessionId = state.session;
|
|
49
|
+
}
|
|
50
|
+
catch (e) { }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!sessionId) {
|
|
54
|
+
console.error('Could not determine active session ID.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const outFile = path.join(debughubDir, 'out', `${sessionId}.jsonl`);
|
|
58
|
+
if (fs.existsSync(outFile)) {
|
|
59
|
+
fs.truncateSync(outFile, 0);
|
|
60
|
+
console.log(`Cleared log file for session ${sessionId}`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
console.log(`Session ${sessionId} has no log file yet.`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.doctor = doctor;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const http = __importStar(require("http"));
|
|
40
|
+
function doctor() {
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
const debughubDir = path.join(cwd, '.debughub');
|
|
43
|
+
const stateFile = path.join(debughubDir, 'state.json');
|
|
44
|
+
const currentLink = path.join(debughubDir, 'vendor', 'current');
|
|
45
|
+
console.log('🩺 DebugHub Doctor');
|
|
46
|
+
console.log('==================');
|
|
47
|
+
// Check 1: Installation
|
|
48
|
+
const installed = fs.existsSync(currentLink);
|
|
49
|
+
console.log(`[${installed ? 'x' : ' '}] Installation: .debughub/vendor directory exists`);
|
|
50
|
+
if (installed) {
|
|
51
|
+
const version = fs.readFileSync(currentLink, 'utf-8').trim();
|
|
52
|
+
console.log(` Version linked: ${version}`);
|
|
53
|
+
}
|
|
54
|
+
// Check 2: Collector status
|
|
55
|
+
const isRunning = fs.existsSync(stateFile);
|
|
56
|
+
console.log(`[${isRunning ? 'x' : ' '}] Collector : Running state found`);
|
|
57
|
+
if (isRunning) {
|
|
58
|
+
try {
|
|
59
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
60
|
+
console.log(` Session ID: ${state.session}`);
|
|
61
|
+
console.log(` PID : ${state.pid}`);
|
|
62
|
+
console.log(` Port : ${state.port}`);
|
|
63
|
+
// Check writable output
|
|
64
|
+
const outFile = path.join(debughubDir, 'out', `${state.session}.jsonl`);
|
|
65
|
+
try {
|
|
66
|
+
fs.accessSync(path.dirname(outFile), fs.constants.W_OK);
|
|
67
|
+
console.log(`[x] Output Path : ${outFile} is reachable/writable`);
|
|
68
|
+
}
|
|
69
|
+
catch (e) {
|
|
70
|
+
console.log(`[ ] Output Path : ${outFile} is NOT writable`);
|
|
71
|
+
}
|
|
72
|
+
// Check Reachability
|
|
73
|
+
const req = http.request({
|
|
74
|
+
hostname: '127.0.0.1',
|
|
75
|
+
port: state.port,
|
|
76
|
+
path: '/event',
|
|
77
|
+
method: 'OPTIONS',
|
|
78
|
+
}, (res) => {
|
|
79
|
+
if (res.statusCode === 204) {
|
|
80
|
+
console.log(`[x] Reachability: Endpoint http://127.0.0.1:${state.port}/event is responding`);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
console.log(`[ ] Reachability: Endpoint responded with ${res.statusCode}`);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
req.on('error', (e) => {
|
|
87
|
+
console.log(`[ ] Reachability: Error connecting to endpoint: ${e.message}`);
|
|
88
|
+
});
|
|
89
|
+
req.end();
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
console.log(` State file is unreadable or corrupt.`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.install = install;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const verify_1 = require("./verify");
|
|
40
|
+
function install() {
|
|
41
|
+
const pkg = require('../../package.json');
|
|
42
|
+
const version = pkg.version;
|
|
43
|
+
const cwd = process.cwd();
|
|
44
|
+
const debughubDir = path.join(cwd, '.debughub');
|
|
45
|
+
const targetVendorDir = path.join(debughubDir, 'vendor', version);
|
|
46
|
+
const currentLinkFile = path.join(debughubDir, 'vendor', 'current');
|
|
47
|
+
// Find source bundle
|
|
48
|
+
const distVendorPath = path.resolve(__dirname, '..', 'vendor', version);
|
|
49
|
+
if (!fs.existsSync(distVendorPath)) {
|
|
50
|
+
console.error(`Error: Cannot find DebugHub distribution bundle for v${version} at ${distVendorPath}`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
// Create target structure
|
|
54
|
+
fs.mkdirSync(targetVendorDir, { recursive: true });
|
|
55
|
+
fs.mkdirSync(path.join(debughubDir, 'out'), { recursive: true });
|
|
56
|
+
// Copy files
|
|
57
|
+
copyRecursiveSync(distVendorPath, targetVendorDir);
|
|
58
|
+
// Update "current" pointer (use a text file to avoid symlink issues on windows)
|
|
59
|
+
fs.writeFileSync(currentLinkFile, version, 'utf-8');
|
|
60
|
+
// Write default config
|
|
61
|
+
const configFile = path.join(debughubDir, 'debughub.json');
|
|
62
|
+
if (!fs.existsSync(configFile)) {
|
|
63
|
+
fs.writeFileSync(configFile, JSON.stringify({
|
|
64
|
+
bindHost: "127.0.0.1",
|
|
65
|
+
allowRemote: false
|
|
66
|
+
}, null, 2), 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
console.log(`DebugHub v${version} vendor files installed to .debughub/vendor/${version}`);
|
|
69
|
+
console.log(`Verifying installation...`);
|
|
70
|
+
const isValid = (0, verify_1.verifyVendorFiles)(cwd);
|
|
71
|
+
if (!isValid) {
|
|
72
|
+
console.error('Verify failed immediately after install. Installation might be corrupted.');
|
|
73
|
+
process.exit(1);
|
|
74
|
+
}
|
|
75
|
+
console.log('Installation verified successfully ✨');
|
|
76
|
+
}
|
|
77
|
+
function copyRecursiveSync(src, dest) {
|
|
78
|
+
const stat = fs.statSync(src);
|
|
79
|
+
if (stat.isDirectory()) {
|
|
80
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
81
|
+
for (const child of fs.readdirSync(src)) {
|
|
82
|
+
copyRecursiveSync(path.join(src, child), path.join(dest, child));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
fs.copyFileSync(src, dest);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.search = search;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
function search(query, options) {
|
|
40
|
+
const cwd = process.cwd();
|
|
41
|
+
const debughubDir = path.join(cwd, '.debughub');
|
|
42
|
+
let sessionId = options.session;
|
|
43
|
+
if (!sessionId) {
|
|
44
|
+
const stateFile = path.join(debughubDir, 'state.json');
|
|
45
|
+
if (fs.existsSync(stateFile)) {
|
|
46
|
+
try {
|
|
47
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
48
|
+
sessionId = state.session;
|
|
49
|
+
}
|
|
50
|
+
catch (e) { }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!sessionId) {
|
|
54
|
+
console.error('Could not determine active session ID. Provide --session or run `debughub start`.');
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const outFile = path.join(debughubDir, 'out', `${sessionId}.jsonl`);
|
|
58
|
+
if (!fs.existsSync(outFile)) {
|
|
59
|
+
console.log(`No events yet in session ${sessionId}`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const content = fs.readFileSync(outFile, 'utf-8');
|
|
63
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
64
|
+
let matches = 0;
|
|
65
|
+
for (const l of lines) {
|
|
66
|
+
try {
|
|
67
|
+
const parsed = JSON.parse(l);
|
|
68
|
+
// Filters
|
|
69
|
+
if (options.label && parsed.label !== options.label)
|
|
70
|
+
continue;
|
|
71
|
+
if (options.hypothesis && parsed.hypothesisId !== options.hypothesis)
|
|
72
|
+
continue;
|
|
73
|
+
if (options.level && parsed.level !== options.level)
|
|
74
|
+
continue;
|
|
75
|
+
// Substring match
|
|
76
|
+
if (query && !l.includes(query))
|
|
77
|
+
continue;
|
|
78
|
+
matches++;
|
|
79
|
+
const ts = new Date(parsed.ts).toLocaleTimeString();
|
|
80
|
+
const levelColorStr = parsed.level === 'error' ? '\x1b[31m[ERROR]\x1b[0m' :
|
|
81
|
+
parsed.level === 'warn' ? '\x1b[33m[WARN]\x1b[0m' :
|
|
82
|
+
'\x1b[36m[INFO]\x1b[0m';
|
|
83
|
+
let meta = '';
|
|
84
|
+
if (parsed.hypothesisId)
|
|
85
|
+
meta += ` [H:${parsed.hypothesisId}]`;
|
|
86
|
+
if (parsed.loc)
|
|
87
|
+
meta += ` [L:${parsed.loc}]`;
|
|
88
|
+
console.log(`[${ts}] ${levelColorStr} ${parsed.label}${meta}`);
|
|
89
|
+
if (parsed.data !== null) {
|
|
90
|
+
console.dir(parsed.data, { depth: 4, colors: true });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
if (query && l.includes(query)) {
|
|
95
|
+
matches++;
|
|
96
|
+
console.log(l);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
if (matches === 0) {
|
|
101
|
+
console.log(`No matches found for query: "${query}"`);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
console.log(`\nFound ${matches} match(es).`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports._server = _server;
|
|
37
|
+
const http = __importStar(require("http"));
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
function _server(sessionId, portStr) {
|
|
41
|
+
const cwd = process.cwd();
|
|
42
|
+
const stateFile = path.join(cwd, '.debughub', 'state.json');
|
|
43
|
+
const outFile = path.join(cwd, '.debughub', 'out', `${sessionId}.jsonl`);
|
|
44
|
+
const server = http.createServer((req, res) => {
|
|
45
|
+
// Basic CORS
|
|
46
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
47
|
+
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
|
|
48
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
49
|
+
if (req.method === 'OPTIONS') {
|
|
50
|
+
res.writeHead(204);
|
|
51
|
+
res.end();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (req.method === 'POST' && req.url === '/event') {
|
|
55
|
+
let body = '';
|
|
56
|
+
req.on('data', chunk => {
|
|
57
|
+
body += chunk.toString();
|
|
58
|
+
});
|
|
59
|
+
req.on('end', () => {
|
|
60
|
+
try {
|
|
61
|
+
fs.appendFileSync(outFile, body + '\n', 'utf-8');
|
|
62
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
63
|
+
res.end(JSON.stringify({ ok: true }));
|
|
64
|
+
}
|
|
65
|
+
catch (e) {
|
|
66
|
+
res.writeHead(500);
|
|
67
|
+
res.end();
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
res.writeHead(404);
|
|
73
|
+
res.end();
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
server.listen(parseInt(portStr, 10), '127.0.0.1', () => {
|
|
77
|
+
const address = server.address();
|
|
78
|
+
const port = address.port;
|
|
79
|
+
fs.writeFileSync(stateFile, JSON.stringify({
|
|
80
|
+
pid: process.pid,
|
|
81
|
+
session: sessionId,
|
|
82
|
+
port: port
|
|
83
|
+
}, null, 2));
|
|
84
|
+
// Cleanup on exit
|
|
85
|
+
const cleanup = () => {
|
|
86
|
+
if (fs.existsSync(stateFile)) {
|
|
87
|
+
fs.unlinkSync(stateFile);
|
|
88
|
+
}
|
|
89
|
+
process.exit();
|
|
90
|
+
};
|
|
91
|
+
process.on('SIGINT', cleanup);
|
|
92
|
+
process.on('SIGTERM', cleanup);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.start = start;
|
|
37
|
+
const fs = __importStar(require("fs"));
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const crypto = __importStar(require("crypto"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
function start() {
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
const debughubDir = path.join(cwd, '.debughub');
|
|
44
|
+
const stateFile = path.join(debughubDir, 'state.json');
|
|
45
|
+
if (!fs.existsSync(debughubDir)) {
|
|
46
|
+
console.error('Error: .debughub directory not found. Did you run `debughub install`?');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
// Check if already running
|
|
50
|
+
if (fs.existsSync(stateFile)) {
|
|
51
|
+
let state = null;
|
|
52
|
+
try {
|
|
53
|
+
state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
54
|
+
}
|
|
55
|
+
catch (e) { }
|
|
56
|
+
if (state?.pid) {
|
|
57
|
+
let isRunning = false;
|
|
58
|
+
try {
|
|
59
|
+
process.kill(state.pid, 0); // test if running
|
|
60
|
+
isRunning = true;
|
|
61
|
+
}
|
|
62
|
+
catch (e) {
|
|
63
|
+
// Process not actually running, clear state
|
|
64
|
+
fs.unlinkSync(stateFile);
|
|
65
|
+
}
|
|
66
|
+
if (isRunning) {
|
|
67
|
+
console.error(`Collector is already running with PID ${state.pid} (Session: ${state.session})`);
|
|
68
|
+
console.error(`Run \`debughub stop\` to stop it first.`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const sessionId = crypto.randomUUID();
|
|
74
|
+
const outDir = path.join(debughubDir, 'out');
|
|
75
|
+
fs.mkdirSync(outDir, { recursive: true });
|
|
76
|
+
const port = process.env.DEBUGHUB_PORT ? parseInt(process.env.DEBUGHUB_PORT, 10) : 0; // 0 means pick random
|
|
77
|
+
// Spawn the server in detached mode
|
|
78
|
+
const cliPath = path.resolve(__dirname, '..', 'cli.js');
|
|
79
|
+
const outLog = fs.openSync(path.join(debughubDir, 'server.log'), 'a');
|
|
80
|
+
const errLog = fs.openSync(path.join(debughubDir, 'error.log'), 'a');
|
|
81
|
+
const child = (0, child_process_1.spawn)(process.execPath, [cliPath, '_server', sessionId, port.toString()], {
|
|
82
|
+
detached: true,
|
|
83
|
+
stdio: ['ignore', outLog, errLog]
|
|
84
|
+
});
|
|
85
|
+
child.unref();
|
|
86
|
+
// We need to wait a tiny bit to read the port from state file that the child writes,
|
|
87
|
+
// or the child can write it and we just tell the user to check `.debughub/state.json`.
|
|
88
|
+
// To be user-friendly, let's wait up to 2 seconds for state.json to appear with our sessionId
|
|
89
|
+
let attempts = 0;
|
|
90
|
+
let finalPort = port;
|
|
91
|
+
while (attempts < 20) {
|
|
92
|
+
if (fs.existsSync(stateFile)) {
|
|
93
|
+
try {
|
|
94
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
95
|
+
if (state.session === sessionId && state.port) {
|
|
96
|
+
finalPort = state.port;
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
catch (e) { }
|
|
101
|
+
}
|
|
102
|
+
// sleep
|
|
103
|
+
const startTm = Date.now();
|
|
104
|
+
while (Date.now() - startTm < 100) {
|
|
105
|
+
// intentional busy-wait to keep implementation synchronous
|
|
106
|
+
}
|
|
107
|
+
attempts++;
|
|
108
|
+
}
|
|
109
|
+
if (finalPort === 0) {
|
|
110
|
+
console.log("Started collector but couldn't confirm port. Check .debughub/state.json");
|
|
111
|
+
// Fallback display
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
console.log(`\nDebugHub Collector Started!`);
|
|
115
|
+
console.log(`Session ID : ${sessionId}`);
|
|
116
|
+
console.log(`Endpoint : http://127.0.0.1:${finalPort}/event`);
|
|
117
|
+
console.log(`Output File : .debughub/out/${sessionId}.jsonl`);
|
|
118
|
+
console.log(`\nEnvironment variables to set:`);
|
|
119
|
+
console.log(` DEBUGHUB_ENABLED=1`);
|
|
120
|
+
console.log(` DEBUGHUB_SESSION=${sessionId}`);
|
|
121
|
+
console.log(` DEBUGHUB_ENDPOINT=http://127.0.0.1:${finalPort}\n`);
|
|
122
|
+
}
|
|
123
|
+
}
|