errase 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 ADDED
@@ -0,0 +1,21 @@
1
+ # ERRase CLI
2
+
3
+ AI-powered browser debugging. Detects runtime errors, reads your source files, and applies fixes automatically.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx errase /path/to/your/project
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ 1. Install the [ERRase Chrome Extension](#)
14
+ 2. Get a free [Groq API key](https://console.groq.com)
15
+ 3. Enter your key in the extension popup
16
+ 4. Run `npx errase` in your project directory
17
+
18
+ ## Requirements
19
+
20
+ - Node.js 18+
21
+ - Next.js project (more frameworks coming soon)
package/dist/index.js ADDED
@@ -0,0 +1,185 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const express_1 = __importDefault(require("express"));
8
+ const ws_1 = require("ws");
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const source_map_js_1 = require("source-map-js");
12
+ const projectRoot = process.argv[2]
13
+ ? path_1.default.resolve(process.argv[2])
14
+ : process.cwd();
15
+ const app = (0, express_1.default)();
16
+ const server = app.listen(7891, () => [
17
+ console.log("[ERRase CLI] Running on port 7891"),
18
+ console.log("[ERRase CLI] Project root:", projectRoot)
19
+ ]);
20
+ const wss = new ws_1.WebSocketServer({ server });
21
+ // Extracts all bundled JS URLs + line/col from a raw browser stack trace
22
+ function parseBundledStack(stack) {
23
+ const frames = [];
24
+ for (const line of stack.split(/\r?\n/)) {
25
+ const match = line.match(/\(?(https?:\/\/.+?\.js):(\d+):(\d+)\)?/);
26
+ if (match)
27
+ frames.push({ bundledUrl: match[1], line: parseInt(match[2]), column: parseInt(match[3]) });
28
+ }
29
+ return frames;
30
+ }
31
+ // Iterates all stack frames, resolves source maps, and returns the first frame
32
+ // that maps to a project file (skipping node_modules frames).
33
+ async function resolveToProjectFile(stack) {
34
+ const frames = parseBundledStack(stack);
35
+ for (const frame of frames) {
36
+ let rawMap;
37
+ try {
38
+ rawMap = await fetchSourceMap(frame.bundledUrl);
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ const consumer = new source_map_js_1.SourceMapConsumer(rawMap);
44
+ const original = consumer.originalPositionFor({ line: frame.line, column: frame.column });
45
+ if (!original.source || original.line == null)
46
+ continue;
47
+ if (original.source.includes("node_modules"))
48
+ continue;
49
+ const filename = stripSourcePrefix(original.source);
50
+ if (filename.includes("node_modules"))
51
+ continue;
52
+ return { filename, lineNumber: original.line };
53
+ }
54
+ return null;
55
+ }
56
+ async function fetchSourceMap(bundledUrl) {
57
+ const res = await fetch(bundledUrl + ".map");
58
+ if (!res.ok)
59
+ throw new Error(`Source map not found: ${bundledUrl}.map (${res.status})`);
60
+ return res.json();
61
+ }
62
+ // Strips webpack:// or file:// prefixes from source map source paths, returning
63
+ // a path relative to projectRoot suitable for disk reads.
64
+ // e.g. webpack://_N_E/./app/page.tsx → app/page.tsx
65
+ // file:///C:/path/to/project/app/page.tsx → app/page.tsx
66
+ function stripSourcePrefix(source) {
67
+ const webpackMatch = source.match(/^webpack:\/\/[^/]*\/\.\/(.+)$/) ?? source.match(/^webpack:\/\/[^/]*\/(.+)$/);
68
+ if (webpackMatch)
69
+ return webpackMatch[1];
70
+ if (source.startsWith("file://")) {
71
+ const decoded = decodeURIComponent(source);
72
+ // file:///C:/... on Windows, file:///... on Unix
73
+ const absolute = decoded.replace(/^file:\/\/\//, "").replace(/^file:\/\//, "");
74
+ return path_1.default.relative(projectRoot, absolute);
75
+ }
76
+ return source;
77
+ }
78
+ function readContextLines(filename, lineNumber, context = 15) {
79
+ const fullPath = path_1.default.join(projectRoot, filename);
80
+ if (!fs_1.default.existsSync(fullPath))
81
+ throw new Error(`File not found: ${fullPath}`);
82
+ const lines = fs_1.default.readFileSync(fullPath, "utf-8").split("\n");
83
+ const start = Math.max(0, lineNumber - 1 - context);
84
+ const end = Math.min(lines.length, lineNumber + context);
85
+ return lines.slice(start, end).map((l, i) => `${start + i + 1}: ${l}`).join("\n");
86
+ }
87
+ wss.on("connection", (socket) => {
88
+ console.log("[ERRase CLI] Extension connected");
89
+ socket.on("message", async (data) => {
90
+ let message;
91
+ try {
92
+ message = JSON.parse(data.toString());
93
+ }
94
+ catch {
95
+ console.log("[ERRase CLI] Could not parse message, skipping");
96
+ return;
97
+ }
98
+ if (message.type === "ERROR_DETECTED") {
99
+ console.log("[ERRase CLI] Error received:");
100
+ console.log(" Message:", message.message);
101
+ console.log(" URL:", message.url);
102
+ let resolved = null;
103
+ try {
104
+ resolved = await resolveToProjectFile(message.stack);
105
+ }
106
+ catch (err) {
107
+ console.log("[ERRase CLI] Source map resolution failed:", err);
108
+ return;
109
+ }
110
+ if (!resolved) {
111
+ console.log("[ERRase CLI] Could not resolve stack to a project file (all frames are node_modules or unmapped)");
112
+ return;
113
+ }
114
+ const { filename, lineNumber } = resolved;
115
+ console.log(`[ERRase CLI] Mapped to: ${filename}:${lineNumber}`);
116
+ try {
117
+ const snippet = readContextLines(filename, lineNumber);
118
+ console.log(`[ERRase CLI] Context (±30 lines around line ${lineNumber}):\n${snippet}`);
119
+ socket.send(JSON.stringify({
120
+ type: "ERROR_CONTEXT",
121
+ filename,
122
+ lineNumber,
123
+ message: message.message,
124
+ stack: message.stack,
125
+ fileContent: snippet
126
+ }));
127
+ }
128
+ catch (err) {
129
+ console.log("[ERRase CLI] Could not read source file:", err);
130
+ }
131
+ }
132
+ if (message.type === "ANALYZE_REQUEST") {
133
+ console.log("[ERRase CLI] Analyze request received:");
134
+ console.log(" File:", message.filename);
135
+ try {
136
+ const response = await fetch("https://errase-production.up.railway.app/analyze", {
137
+ method: "POST",
138
+ headers: { "Content-Type": "application/json" },
139
+ body: JSON.stringify({
140
+ message: message.message,
141
+ filename: message.filename,
142
+ lineNumber: message.lineNumber,
143
+ fileContents: message.fileContents,
144
+ groqApiKey: message.groqApiKey
145
+ })
146
+ });
147
+ const fix = await response.json();
148
+ console.log("[ERRase Backend response]:", JSON.stringify(fix, null, 2));
149
+ socket.send(JSON.stringify({
150
+ type: "FIX_READY",
151
+ explanation: fix.explanation,
152
+ confidence: fix.confidence,
153
+ originalLine: fix.originalLine,
154
+ fixedLine: fix.fixedLine,
155
+ diff: fix.diff,
156
+ }));
157
+ }
158
+ catch (error) {
159
+ console.error("[ERRase CLI] Failed to reach backend:", error);
160
+ }
161
+ }
162
+ if (message.type === "APPLY_FIX") {
163
+ console.log("[ERRase CLI] Applying fix to:", message.filename);
164
+ try {
165
+ const fullPath = path_1.default.join(projectRoot, message.filename);
166
+ const fileContent = fs_1.default.readFileSync(fullPath, "utf-8");
167
+ if (!fileContent.includes(message.originalLine)) {
168
+ console.error("[ERRase CLI] originalLine not found in file");
169
+ console.error("[ERRase CLI] Searched for:", JSON.stringify(message.originalLine));
170
+ console.error("[ERRase CLI] File start:", fileContent.slice(0, 500));
171
+ return;
172
+ }
173
+ const updated = fileContent.replace(message.originalLine, message.fixedLine);
174
+ fs_1.default.writeFileSync(fullPath, updated, "utf-8");
175
+ console.log("[ERRase CLI] Fix applied to:", fullPath);
176
+ }
177
+ catch (error) {
178
+ console.error("[ERRase CLI] Failed to apply fix:", error);
179
+ }
180
+ }
181
+ });
182
+ socket.on("close", () => {
183
+ console.log("[ERRase CLI] Extension disconnected");
184
+ });
185
+ });
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "errase",
3
+ "version": "1.0.0",
4
+ "main": "dist/index.js",
5
+ "bin": {
6
+ "errase": "dist/index.js"
7
+ },
8
+ "scripts": {
9
+ "build": "tsc",
10
+ "start": "node dist/index.js",
11
+ "dev": "nodemon --exec ts-node src/index.ts",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "debugging",
16
+ "ai",
17
+ "chrome-extension",
18
+ "developer-tools",
19
+ "nextjs"
20
+ ],
21
+ "author": "Craig Rosario",
22
+ "license": "ISC",
23
+ "description": "AI-powered browser debugging assistant. Detects runtime errors, reads your source files, and applies AI-generated fixes without leaving the tab.",
24
+ "dependencies": {
25
+ "diff": "^9.0.0",
26
+ "express": "^5.2.1",
27
+ "source-map-js": "^1.2.1",
28
+ "ws": "^8.20.1"
29
+ },
30
+ "devDependencies": {
31
+ "@types/diff": "^7.0.2",
32
+ "@types/express": "^5.0.6",
33
+ "@types/node": "^25.9.1",
34
+ "@types/ws": "^8.18.1",
35
+ "nodemon": "^3.1.14",
36
+ "ts-node": "^10.9.2",
37
+ "typescript": "^6.0.3"
38
+ },
39
+ "homepage": "https://errase.vercel.app",
40
+ "repository": {
41
+ "type": "git",
42
+ "url": "https://github.com/Craig-Rosario/ERRase"
43
+ },
44
+ "bugs": {
45
+ "url": "https://github.com/Craig-Rosario/ERRase/issues"
46
+ }
47
+ }
package/src/index.ts ADDED
@@ -0,0 +1,191 @@
1
+ #!/usr/bin/env node
2
+ import express from "express"
3
+ import { WebSocketServer } from "ws"
4
+ import fs from "fs"
5
+ import path from "path"
6
+ import { SourceMapConsumer } from "source-map-js"
7
+
8
+ const projectRoot = process.argv[2]
9
+ ? path.resolve(process.argv[2])
10
+ : process.cwd()
11
+
12
+ const app = express();
13
+
14
+ const server = app.listen(7891, () => [
15
+ console.log("[ERRase CLI] Running on port 7891"),
16
+ console.log("[ERRase CLI] Project root:", projectRoot)
17
+ ])
18
+
19
+ const wss = new WebSocketServer({ server });
20
+
21
+ // Extracts all bundled JS URLs + line/col from a raw browser stack trace
22
+ function parseBundledStack(stack: string): Array<{ bundledUrl: string; line: number; column: number }> {
23
+ const frames: Array<{ bundledUrl: string; line: number; column: number }> = []
24
+ for (const line of stack.split(/\r?\n/)) {
25
+ const match = line.match(/\(?(https?:\/\/.+?\.js):(\d+):(\d+)\)?/)
26
+ if (match) frames.push({ bundledUrl: match[1], line: parseInt(match[2]), column: parseInt(match[3]) })
27
+ }
28
+ return frames
29
+ }
30
+
31
+ // Iterates all stack frames, resolves source maps, and returns the first frame
32
+ // that maps to a project file (skipping node_modules frames).
33
+ async function resolveToProjectFile(stack: string): Promise<{ filename: string; lineNumber: number } | null> {
34
+ const frames = parseBundledStack(stack)
35
+ for (const frame of frames) {
36
+ let rawMap: any
37
+ try {
38
+ rawMap = await fetchSourceMap(frame.bundledUrl)
39
+ } catch {
40
+ continue
41
+ }
42
+ const consumer = new SourceMapConsumer(rawMap)
43
+ const original = consumer.originalPositionFor({ line: frame.line, column: frame.column })
44
+ if (!original.source || original.line == null) continue
45
+ if (original.source.includes("node_modules")) continue
46
+ const filename = stripSourcePrefix(original.source)
47
+ if (filename.includes("node_modules")) continue
48
+ return { filename, lineNumber: original.line }
49
+ }
50
+ return null
51
+ }
52
+
53
+ async function fetchSourceMap(bundledUrl: string): Promise<any> {
54
+ const res = await fetch(bundledUrl + ".map")
55
+ if (!res.ok) throw new Error(`Source map not found: ${bundledUrl}.map (${res.status})`)
56
+ return res.json()
57
+ }
58
+
59
+ // Strips webpack:// or file:// prefixes from source map source paths, returning
60
+ // a path relative to projectRoot suitable for disk reads.
61
+ // e.g. webpack://_N_E/./app/page.tsx → app/page.tsx
62
+ // file:///C:/path/to/project/app/page.tsx → app/page.tsx
63
+ function stripSourcePrefix(source: string): string {
64
+ const webpackMatch = source.match(/^webpack:\/\/[^/]*\/\.\/(.+)$/) ?? source.match(/^webpack:\/\/[^/]*\/(.+)$/)
65
+ if (webpackMatch) return webpackMatch[1]
66
+
67
+ if (source.startsWith("file://")) {
68
+ const decoded = decodeURIComponent(source)
69
+ // file:///C:/... on Windows, file:///... on Unix
70
+ const absolute = decoded.replace(/^file:\/\/\//, "").replace(/^file:\/\//, "")
71
+ return path.relative(projectRoot, absolute)
72
+ }
73
+
74
+ return source
75
+ }
76
+
77
+ function readContextLines(filename: string, lineNumber: number, context = 15): string {
78
+ const fullPath = path.join(projectRoot, filename)
79
+ if (!fs.existsSync(fullPath)) throw new Error(`File not found: ${fullPath}`)
80
+ const lines = fs.readFileSync(fullPath, "utf-8").split("\n")
81
+ const start = Math.max(0, lineNumber - 1 - context)
82
+ const end = Math.min(lines.length, lineNumber + context)
83
+ return lines.slice(start, end).map((l, i) => `${start + i + 1}: ${l}`).join("\n")
84
+ }
85
+
86
+ wss.on("connection", (socket) => {
87
+ console.log("[ERRase CLI] Extension connected")
88
+
89
+ socket.on("message", async (data) => {
90
+ let message: any
91
+ try {
92
+ message = JSON.parse(data.toString())
93
+ } catch {
94
+ console.log("[ERRase CLI] Could not parse message, skipping")
95
+ return
96
+ }
97
+
98
+ if (message.type === "ERROR_DETECTED") {
99
+ console.log("[ERRase CLI] Error received:")
100
+ console.log(" Message:", message.message)
101
+ console.log(" URL:", message.url)
102
+
103
+ let resolved: { filename: string; lineNumber: number } | null = null
104
+ try {
105
+ resolved = await resolveToProjectFile(message.stack)
106
+ } catch (err) {
107
+ console.log("[ERRase CLI] Source map resolution failed:", err)
108
+ return
109
+ }
110
+
111
+ if (!resolved) {
112
+ console.log("[ERRase CLI] Could not resolve stack to a project file (all frames are node_modules or unmapped)")
113
+ return
114
+ }
115
+
116
+ const { filename, lineNumber } = resolved
117
+ console.log(`[ERRase CLI] Mapped to: ${filename}:${lineNumber}`)
118
+
119
+ try {
120
+ const snippet = readContextLines(filename, lineNumber)
121
+ console.log(`[ERRase CLI] Context (±30 lines around line ${lineNumber}):\n${snippet}`)
122
+
123
+ socket.send(JSON.stringify({
124
+ type: "ERROR_CONTEXT",
125
+ filename,
126
+ lineNumber,
127
+ message: message.message,
128
+ stack: message.stack,
129
+ fileContent: snippet
130
+ }))
131
+ } catch (err) {
132
+ console.log("[ERRase CLI] Could not read source file:", err)
133
+ }
134
+ }
135
+
136
+ if (message.type === "ANALYZE_REQUEST") {
137
+ console.log("[ERRase CLI] Analyze request received:")
138
+ console.log(" File:", message.filename)
139
+
140
+ try {
141
+ const response = await fetch("https://errase-production.up.railway.app/analyze", {
142
+ method: "POST",
143
+ headers: { "Content-Type": "application/json" },
144
+ body: JSON.stringify({
145
+ message: message.message,
146
+ filename: message.filename,
147
+ lineNumber: message.lineNumber,
148
+ fileContents: message.fileContents,
149
+ groqApiKey: message.groqApiKey
150
+ })
151
+ })
152
+ const fix = await response.json() as any;
153
+ console.log("[ERRase Backend response]:", JSON.stringify(fix, null, 2))
154
+ socket.send(JSON.stringify({
155
+ type: "FIX_READY",
156
+ explanation: fix.explanation,
157
+ confidence: fix.confidence,
158
+ originalLine: fix.originalLine,
159
+ fixedLine: fix.fixedLine,
160
+ diff: fix.diff,
161
+ }))
162
+
163
+ } catch (error) {
164
+ console.error("[ERRase CLI] Failed to reach backend:", error)
165
+ }
166
+ }
167
+
168
+ if (message.type === "APPLY_FIX") {
169
+ console.log("[ERRase CLI] Applying fix to:", message.filename)
170
+ try {
171
+ const fullPath = path.join(projectRoot, message.filename)
172
+ const fileContent = fs.readFileSync(fullPath, "utf-8")
173
+ if (!fileContent.includes(message.originalLine)) {
174
+ console.error("[ERRase CLI] originalLine not found in file")
175
+ console.error("[ERRase CLI] Searched for:", JSON.stringify(message.originalLine))
176
+ console.error("[ERRase CLI] File start:", fileContent.slice(0, 500))
177
+ return
178
+ }
179
+ const updated = fileContent.replace(message.originalLine, message.fixedLine)
180
+ fs.writeFileSync(fullPath, updated, "utf-8")
181
+ console.log("[ERRase CLI] Fix applied to:", fullPath)
182
+ } catch (error) {
183
+ console.error("[ERRase CLI] Failed to apply fix:", error)
184
+ }
185
+ }
186
+ })
187
+
188
+ socket.on("close", () => {
189
+ console.log("[ERRase CLI] Extension disconnected");
190
+ })
191
+ })
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "outDir": "./dist",
6
+ "rootDir": "./src",
7
+ "strict": false,
8
+ "esModuleInterop": true
9
+ }
10
+ }