open-ready 0.1.2 → 0.1.6
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 +64 -0
- package/open-when-ready.mjs +25 -41
- package/package.json +1 -2
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# open-ready
|
|
2
|
+
|
|
3
|
+
Smart dev server launcher that watches your server's output and automatically opens the browser when ready — or opens an AI assistant with the error context when something goes wrong.
|
|
4
|
+
|
|
5
|
+
Works with Next.js, Vite, and any CLI-based dev server.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install -g open-ready
|
|
11
|
+
# or use without installing:
|
|
12
|
+
npx open-ready <your-dev-command>
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
open-ready <command> [options]
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Examples
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
open-ready npm run dev
|
|
25
|
+
open-ready bun run dev
|
|
26
|
+
open-ready vite
|
|
27
|
+
open-ready next dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Options
|
|
31
|
+
|
|
32
|
+
| Flag | Default | Description |
|
|
33
|
+
|------|---------|-------------|
|
|
34
|
+
| `--ai-base <url>` | `https://perplexity.ai?q=` | AI assistant base URL to open on error |
|
|
35
|
+
| `--noAi` | `false` | Disable opening AI on error |
|
|
36
|
+
| `--noOpen` | `false` | Disable opening browser when ready |
|
|
37
|
+
| `--pollDelay <ms>` | `1200` | How often to poll the log for ready/error signals |
|
|
38
|
+
|
|
39
|
+
### Disable AI on error
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
open-ready npm run dev --noAi
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Use a different AI assistant
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
open-ready npm run dev --ai-base "https://chatgpt.com/?q="
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## How it works
|
|
52
|
+
|
|
53
|
+
1. Spawns your dev command and pipes its stdout/stderr to a log file
|
|
54
|
+
2. Polls the log every `pollDelay` ms looking for:
|
|
55
|
+
- **Error signal** — lines matching `error`, `failed`, `exception`, `SyntaxError`, or `⨯`
|
|
56
|
+
- **Ready signal** — lines matching `ready - started server` or `Ready in Xms`
|
|
57
|
+
3. On **error**: extracts up to ~1000 chars of surrounding context and opens your AI assistant with a pre-filled prompt explaining the error and asking for a fix
|
|
58
|
+
4. On **ready**: waits for the port to be reachable, then opens the local URL in your default browser
|
|
59
|
+
|
|
60
|
+
For Next.js projects, the log is written to `.next/port.log`; otherwise `open-when-ready.log` in the current directory.
|
|
61
|
+
|
|
62
|
+
## License
|
|
63
|
+
|
|
64
|
+
MIT
|
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.6",
|
|
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
|
},
|