open-ready 0.1.2 β 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/open-when-ready.mjs +25 -41
- package/package.json +1 -2
package/open-when-ready.mjs
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @fileoverview CLI tool that spawns a dev server command, monitors its log
|
|
5
|
+
* output for a "ready" signal, and automatically opens the local URL in the
|
|
6
|
+
* browser. If an error is detected first, it opens an AI assistant with the
|
|
7
|
+
* error context for troubleshooting.
|
|
8
|
+
*/
|
|
9
|
+
|
|
2
10
|
import fsPromises from "fs/promises";
|
|
3
11
|
import fs from "fs";
|
|
4
12
|
import path from "path";
|
|
@@ -20,18 +28,12 @@ const logPath = fs.existsSync(nextDir)
|
|
|
20
28
|
? path.join(".next", "port.log")
|
|
21
29
|
: "open-when-ready.log";
|
|
22
30
|
|
|
23
|
-
console.log(`π Log: ${logPath} | Poll: ${pollDelay}ms`);
|
|
24
31
|
|
|
25
32
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* -
|
|
30
|
-
* - **Versatile**: Supports multiple browsers
|
|
31
|
-
*
|
|
32
|
-
* ```js
|
|
33
|
-
* console.log("Ready!");
|
|
34
|
-
* ```
|
|
33
|
+
* Scans log output for error lines and extracts surrounding context
|
|
34
|
+
* as a URL-encoded string suitable for an AI search query.
|
|
35
|
+
* @param {string} log - Raw log output from the dev server
|
|
36
|
+
* @returns {string} URL-encoded error context, or empty string if no error found
|
|
35
37
|
*/
|
|
36
38
|
function getErrorContext(log) {
|
|
37
39
|
const lines = log.split(/\n/);
|
|
@@ -53,31 +55,31 @@ function getErrorContext(log) {
|
|
|
53
55
|
return "";
|
|
54
56
|
}
|
|
55
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Parses log output to find the local dev server URL (e.g. http://localhost:3000).
|
|
60
|
+
* @param {string} log - Raw log output from the dev server
|
|
61
|
+
* @returns {string|null} The localhost URL, or null if not found
|
|
62
|
+
*/
|
|
56
63
|
function extractUrl(log) {
|
|
57
64
|
// Better regex for Local: line
|
|
58
65
|
const localMatch = log.match(/Local:\s+(http:\/\/localhost:\d+)/i);
|
|
59
|
-
if (localMatch)
|
|
60
|
-
console.log("β
EXTRACTED URL:", localMatch[1]);
|
|
61
|
-
return localMatch[1];
|
|
62
|
-
}
|
|
66
|
+
if (localMatch) return localMatch[1];
|
|
63
67
|
|
|
64
68
|
// Fallback port
|
|
65
69
|
const portMatch = log.match(/localhost:(\d+)/);
|
|
66
|
-
if (portMatch) {
|
|
67
|
-
const url = `http://localhost:${portMatch[1]}`;
|
|
68
|
-
console.log("β
FALLBACK URL:", url);
|
|
69
|
-
return url;
|
|
70
|
-
}
|
|
70
|
+
if (portMatch) return `http://localhost:${portMatch[1]}`;
|
|
71
71
|
|
|
72
|
-
console.log("β NO URL FOUND");
|
|
73
72
|
return null;
|
|
74
73
|
}
|
|
75
74
|
|
|
75
|
+
/**
|
|
76
|
+
* Main entry point. Spawns the dev server command, pipes its output to a log
|
|
77
|
+
* file, and polls the log for ready/error signals to open the browser or AI helper.
|
|
78
|
+
*/
|
|
76
79
|
async function run() {
|
|
77
80
|
try {
|
|
78
81
|
await fsPromises.rm(logPath, { force: true });
|
|
79
82
|
} catch {}
|
|
80
|
-
console.log("π Starting", cmdArgs.join(" "));
|
|
81
83
|
|
|
82
84
|
const proc = spawn(cmdArgs.join(" "), [], {
|
|
83
85
|
stdio: ["ignore", "pipe", "pipe", "ipc"],
|
|
@@ -107,14 +109,9 @@ async function run() {
|
|
|
107
109
|
if (logChanged && stats.size > 100) {
|
|
108
110
|
stableCount = 0;
|
|
109
111
|
lastLogSize = stats.size;
|
|
110
|
-
console.log(
|
|
111
|
-
`π ${stats.size}B CHANGED | LOG:\n${currentLog.slice(0, 500)}...`,
|
|
112
|
-
);
|
|
113
112
|
} else if (stats.size > 100) {
|
|
114
113
|
stableCount++;
|
|
115
|
-
console.log(`π ${stats.size}B STABLE (${stableCount}/5)`);
|
|
116
114
|
if (stableCount >= 5) {
|
|
117
|
-
console.log("π Log stable 5 polls - stopping");
|
|
118
115
|
clearInterval(poll);
|
|
119
116
|
return;
|
|
120
117
|
}
|
|
@@ -123,7 +120,6 @@ async function run() {
|
|
|
123
120
|
// Error first
|
|
124
121
|
const errorRegex = /β¨―|[Ss]yntax[Ee]rror|error|failed|exception/i;
|
|
125
122
|
if (errorRegex.test(currentLog) && !noAi && !errorOpened) {
|
|
126
|
-
console.log("π― ERROR!");
|
|
127
123
|
const err = getErrorContext(currentLog);
|
|
128
124
|
const customPrompt = "Explain what the error is, how to fix it, then give a shell script with the best recommended solution.";
|
|
129
125
|
const prompt = encodeURIComponent(customPrompt + " ");
|
|
@@ -138,33 +134,21 @@ async function run() {
|
|
|
138
134
|
if (isReadyNow && !lastReadySeen && !opened) {
|
|
139
135
|
opened = true;
|
|
140
136
|
lastReadySeen = true;
|
|
141
|
-
console.log("π READY DETECTED!");
|
|
142
137
|
const url = extractUrl(currentLog);
|
|
143
138
|
if (url && !noOpen) {
|
|
144
|
-
console.log("π Testing", url);
|
|
145
139
|
try {
|
|
146
140
|
await waitOn(url, { timeout: 10000, http: true });
|
|
147
|
-
|
|
148
|
-
} catch (e) {
|
|
149
|
-
console.log(
|
|
150
|
-
"β HTTP not ready, but opening:",
|
|
151
|
-
e.message.slice(0, 50),
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
console.log("π OPENING BROWSER:", url);
|
|
141
|
+
} catch {}
|
|
155
142
|
opener(url);
|
|
156
143
|
}
|
|
157
144
|
clearInterval(poll);
|
|
158
145
|
return;
|
|
159
146
|
}
|
|
160
147
|
lastReadySeen = isReadyNow;
|
|
161
|
-
} catch
|
|
162
|
-
console.log("Poll error:", e.message);
|
|
163
|
-
}
|
|
148
|
+
} catch {}
|
|
164
149
|
}, pollDelay);
|
|
165
150
|
|
|
166
151
|
process.on("SIGINT", () => {
|
|
167
|
-
console.log("\nπ Exiting (dev server alive)");
|
|
168
152
|
clearInterval(poll);
|
|
169
153
|
process.exit(0);
|
|
170
154
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "open-ready",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Smart dev server launcher: errorβAI search, successβauto-open browser. Any CLI tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"minimist": "^1.2.8",
|
|
23
|
-
"open": "^10.1.0",
|
|
24
23
|
"opener": "^1.5.2",
|
|
25
24
|
"wait-on": "^8.0.1"
|
|
26
25
|
},
|