@axiomatic-labs/claudeflow 2.32.3 → 2.32.4
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/bin/cli.js +71 -44
- package/lib/preview.js +91 -73
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -16,16 +16,27 @@
|
|
|
16
16
|
// (has `.claudeflow/`), passthrough to claude.
|
|
17
17
|
// * Otherwise, run `install` to match the npx-first-time path.
|
|
18
18
|
|
|
19
|
-
const fs = require(
|
|
20
|
-
const path = require(
|
|
19
|
+
const fs = require("fs");
|
|
20
|
+
const path = require("path");
|
|
21
21
|
|
|
22
|
-
const SUBCOMMANDS = new Set([
|
|
22
|
+
const SUBCOMMANDS = new Set([
|
|
23
|
+
"install",
|
|
24
|
+
"version",
|
|
25
|
+
"--version",
|
|
26
|
+
"-v",
|
|
27
|
+
"help",
|
|
28
|
+
"--help",
|
|
29
|
+
"-h",
|
|
30
|
+
"doctor",
|
|
31
|
+
"panel",
|
|
32
|
+
"preview",
|
|
33
|
+
]);
|
|
23
34
|
|
|
24
35
|
function inClaudeflowProject(startDir) {
|
|
25
36
|
let dir = path.resolve(startDir);
|
|
26
37
|
const root = path.parse(dir).root;
|
|
27
38
|
while (true) {
|
|
28
|
-
if (fs.existsSync(path.join(dir,
|
|
39
|
+
if (fs.existsSync(path.join(dir, ".claudeflow"))) return true;
|
|
29
40
|
if (dir === root) return false;
|
|
30
41
|
const parent = path.dirname(dir);
|
|
31
42
|
if (parent === dir) return false;
|
|
@@ -35,61 +46,77 @@ function inClaudeflowProject(startDir) {
|
|
|
35
46
|
|
|
36
47
|
async function runSubcommand(command) {
|
|
37
48
|
switch (command) {
|
|
38
|
-
case
|
|
39
|
-
const install = require(
|
|
49
|
+
case "install": {
|
|
50
|
+
const install = require("../lib/install.js");
|
|
40
51
|
await install();
|
|
41
52
|
return;
|
|
42
53
|
}
|
|
43
|
-
case
|
|
44
|
-
case
|
|
45
|
-
case
|
|
46
|
-
const version = require(
|
|
54
|
+
case "version":
|
|
55
|
+
case "--version":
|
|
56
|
+
case "-v": {
|
|
57
|
+
const version = require("../lib/version.js");
|
|
47
58
|
await version();
|
|
48
59
|
return;
|
|
49
60
|
}
|
|
50
|
-
case
|
|
51
|
-
const doctor = require(
|
|
61
|
+
case "doctor": {
|
|
62
|
+
const doctor = require("../lib/doctor.js");
|
|
52
63
|
const code = await doctor(process.argv.slice(3));
|
|
53
64
|
process.exit(code || 0);
|
|
54
65
|
}
|
|
55
|
-
case
|
|
56
|
-
const panel = require(
|
|
66
|
+
case "panel": {
|
|
67
|
+
const panel = require("../lib/panel.js");
|
|
57
68
|
await panel(process.argv.slice(3));
|
|
58
69
|
return;
|
|
59
70
|
}
|
|
60
|
-
case
|
|
61
|
-
const preview = require(
|
|
71
|
+
case "preview": {
|
|
72
|
+
const preview = require("../lib/preview.js");
|
|
62
73
|
await preview(process.argv.slice(3));
|
|
63
74
|
return;
|
|
64
75
|
}
|
|
65
|
-
case
|
|
66
|
-
case
|
|
67
|
-
case
|
|
76
|
+
case "help":
|
|
77
|
+
case "--help":
|
|
78
|
+
case "-h":
|
|
68
79
|
default: {
|
|
69
|
-
const ui = require(
|
|
80
|
+
const ui = require("../lib/ui.js");
|
|
70
81
|
ui.banner();
|
|
71
|
-
console.log(
|
|
72
|
-
console.log(
|
|
73
|
-
console.log(
|
|
74
|
-
console.log(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
console.log(
|
|
82
|
+
console.log(" Usage: claudeflow [command | claude-args...]");
|
|
83
|
+
console.log("");
|
|
84
|
+
console.log(" Claudeflow commands:");
|
|
85
|
+
console.log(
|
|
86
|
+
` ${ui.CYAN}install${ui.RESET} Install or update Claudeflow in the current project`,
|
|
87
|
+
);
|
|
88
|
+
console.log(
|
|
89
|
+
` ${ui.CYAN}doctor${ui.RESET} Diagnose local issues (CDP port, stale lockfiles); add --fix to repair`,
|
|
90
|
+
);
|
|
91
|
+
console.log(
|
|
92
|
+
` ${ui.CYAN}panel${ui.RESET} Open the local web dashboard for hooks, CLAUDE.md, and run state`,
|
|
93
|
+
);
|
|
94
|
+
console.log(
|
|
95
|
+
` ${ui.CYAN}preview${ui.RESET} Live-reload server for prototypes (.claudeflow/prototypes/) — run once, leave it`,
|
|
96
|
+
);
|
|
78
97
|
console.log(` ${ui.CYAN}version${ui.RESET} Show version info`);
|
|
79
98
|
console.log(` ${ui.CYAN}help${ui.RESET} Show this message`);
|
|
80
|
-
console.log(
|
|
81
|
-
console.log(
|
|
82
|
-
console.log(
|
|
83
|
-
console.log(
|
|
84
|
-
console.log(
|
|
85
|
-
console.log(
|
|
86
|
-
console.log(
|
|
87
|
-
|
|
99
|
+
console.log("");
|
|
100
|
+
console.log(" Any other arguments are passed through to `claude`, with");
|
|
101
|
+
console.log(" `--append-system-prompt-file ./append-system-prompt.md`");
|
|
102
|
+
console.log(" auto-injected when the project ships that file.");
|
|
103
|
+
console.log("");
|
|
104
|
+
console.log(" Examples:");
|
|
105
|
+
console.log(
|
|
106
|
+
" claudeflow # open claude in current project",
|
|
107
|
+
);
|
|
108
|
+
console.log(
|
|
109
|
+
" claudeflow --continue # continue last claude session",
|
|
110
|
+
);
|
|
88
111
|
console.log(' claudeflow -p "fix bug" # one-shot prompt');
|
|
89
|
-
console.log(
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
console.log(
|
|
112
|
+
console.log(
|
|
113
|
+
" claudeflow install # install or update claudeflow",
|
|
114
|
+
);
|
|
115
|
+
console.log("");
|
|
116
|
+
console.log(" Env:");
|
|
117
|
+
console.log(
|
|
118
|
+
" CLAUDEFLOW_NO_SYSTEM_PROMPT=1 skip the append-system-prompt flag",
|
|
119
|
+
);
|
|
93
120
|
return;
|
|
94
121
|
}
|
|
95
122
|
}
|
|
@@ -104,25 +131,25 @@ async function main() {
|
|
|
104
131
|
return;
|
|
105
132
|
}
|
|
106
133
|
|
|
107
|
-
if (first && first.startsWith(
|
|
134
|
+
if (first && first.startsWith("-")) {
|
|
108
135
|
// Flags → passthrough to claude
|
|
109
|
-
require(
|
|
136
|
+
require("../lib/claude-passthrough.js").run(args);
|
|
110
137
|
return;
|
|
111
138
|
}
|
|
112
139
|
|
|
113
140
|
if (!first) {
|
|
114
141
|
// No args: passthrough inside a claudeflow project, else install
|
|
115
142
|
if (inClaudeflowProject(process.cwd())) {
|
|
116
|
-
require(
|
|
143
|
+
require("../lib/claude-passthrough.js").run([]);
|
|
117
144
|
} else {
|
|
118
|
-
await runSubcommand(
|
|
145
|
+
await runSubcommand("install");
|
|
119
146
|
}
|
|
120
147
|
return;
|
|
121
148
|
}
|
|
122
149
|
|
|
123
150
|
// Unknown subcommand-looking arg → passthrough (claude owns its own
|
|
124
151
|
// subcommands like `claude mcp`, `claude config`, etc.)
|
|
125
|
-
require(
|
|
152
|
+
require("../lib/claude-passthrough.js").run(args);
|
|
126
153
|
}
|
|
127
154
|
|
|
128
155
|
main().catch((err) => {
|
package/lib/preview.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// `claudeflow preview` — standing, decoupled live-preview server for prototypes.
|
|
2
2
|
//
|
|
3
|
-
// Serves .claudeflow/
|
|
3
|
+
// Serves .claudeflow/prototypes/ over http://127.0.0.1:<port>, watches the
|
|
4
4
|
// files, and pushes live updates over SSE — CSS edits hot-swap (no flicker),
|
|
5
5
|
// HTML edits reload only the changed document / the content iframe (the shell
|
|
6
6
|
// stays). The user runs this ONCE and leaves it; the /claudeflow-prototype flow
|
|
@@ -13,32 +13,32 @@
|
|
|
13
13
|
// a prototype is still a static snapshot; live-reload is purely this server's job
|
|
14
14
|
// (the live client is INJECTED into served HTML, so prototype files stay pure).
|
|
15
15
|
|
|
16
|
-
const http = require(
|
|
17
|
-
const fs = require(
|
|
18
|
-
const path = require(
|
|
19
|
-
const { spawn } = require(
|
|
16
|
+
const http = require("http");
|
|
17
|
+
const fs = require("fs");
|
|
18
|
+
const path = require("path");
|
|
19
|
+
const { spawn } = require("child_process");
|
|
20
20
|
|
|
21
|
-
const ui = require(
|
|
21
|
+
const ui = require("./ui.js");
|
|
22
22
|
|
|
23
23
|
const MIME = {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
".html": "text/html; charset=utf-8",
|
|
25
|
+
".css": "text/css; charset=utf-8",
|
|
26
|
+
".js": "text/javascript; charset=utf-8",
|
|
27
|
+
".json": "application/json; charset=utf-8",
|
|
28
|
+
".svg": "image/svg+xml",
|
|
29
|
+
".png": "image/png",
|
|
30
|
+
".jpg": "image/jpeg",
|
|
31
|
+
".jpeg": "image/jpeg",
|
|
32
|
+
".gif": "image/gif",
|
|
33
|
+
".webp": "image/webp",
|
|
34
|
+
".ico": "image/x-icon",
|
|
35
|
+
".woff": "font/woff",
|
|
36
|
+
".woff2": "font/woff2",
|
|
37
|
+
".ttf": "font/ttf",
|
|
38
|
+
".map": "application/json; charset=utf-8",
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
const SSE_PATH =
|
|
41
|
+
const SSE_PATH = "/__livereload";
|
|
42
42
|
|
|
43
43
|
// Injected into every served .html. Connects to the SSE stream; CSS changes
|
|
44
44
|
// hot-swap the <link>s (flicker-free), other changes reload the changed
|
|
@@ -82,7 +82,7 @@ function listPrototypes(root) {
|
|
|
82
82
|
return entries
|
|
83
83
|
.filter((e) => e.isDirectory())
|
|
84
84
|
.map((e) => e.name)
|
|
85
|
-
.filter((name) => fs.existsSync(path.join(root, name,
|
|
85
|
+
.filter((name) => fs.existsSync(path.join(root, name, "index.html")))
|
|
86
86
|
.sort();
|
|
87
87
|
}
|
|
88
88
|
|
|
@@ -91,18 +91,18 @@ function listingPage(root) {
|
|
|
91
91
|
const items = protos.length
|
|
92
92
|
? protos
|
|
93
93
|
.map((p) => `<li><a href="/${p}/index.html">${p}</a></li>`)
|
|
94
|
-
.join(
|
|
95
|
-
: '<li class="muted">No prototypes yet — assemble one with /claudeflow-prototype (writes into .claudeflow/
|
|
94
|
+
.join("\n")
|
|
95
|
+
: '<li class="muted">No prototypes yet — assemble one with /claudeflow-prototype (writes into .claudeflow/prototypes/).</li>';
|
|
96
96
|
return `<!doctype html><html><head><meta charset="utf-8"><title>claudeflow preview</title>
|
|
97
97
|
<style>body{font:15px/1.5 -apple-system,system-ui,sans-serif;max-width:640px;margin:48px auto;padding:0 20px;color:#1c1c22}
|
|
98
98
|
h1{font-size:18px}a{color:#5050d4}.muted{color:#717180}li{margin:6px 0}</style></head>
|
|
99
|
-
<body><h1>claudeflow preview</h1><p class="muted">Serving <code>.claudeflow/
|
|
99
|
+
<body><h1>claudeflow preview</h1><p class="muted">Serving <code>.claudeflow/prototypes/</code> with live reload.</p>
|
|
100
100
|
<ul>${items}</ul></body></html>`;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
// Resolve the requested URL to a file under root, blocking path traversal.
|
|
104
104
|
function resolveFile(root, urlPath) {
|
|
105
|
-
const clean = decodeURIComponent(urlPath.split(
|
|
105
|
+
const clean = decodeURIComponent(urlPath.split("?")[0]).replace(/\/+$/, "");
|
|
106
106
|
const abs = path.normalize(path.join(root, clean));
|
|
107
107
|
if (abs !== root && !abs.startsWith(root + path.sep)) return null; // traversal guard
|
|
108
108
|
return abs;
|
|
@@ -111,7 +111,7 @@ function resolveFile(root, urlPath) {
|
|
|
111
111
|
function injectLiveClient(html) {
|
|
112
112
|
const marker = /<\/body>/i;
|
|
113
113
|
return marker.test(html)
|
|
114
|
-
? html.replace(marker, LIVE_CLIENT +
|
|
114
|
+
? html.replace(marker, LIVE_CLIENT + "\n</body>")
|
|
115
115
|
: html + LIVE_CLIENT;
|
|
116
116
|
}
|
|
117
117
|
|
|
@@ -119,7 +119,7 @@ function injectLiveClient(html) {
|
|
|
119
119
|
// supported on every platform/Node). Calls onChange(relPath) on edits.
|
|
120
120
|
function watch(root, onChange) {
|
|
121
121
|
let timer = null;
|
|
122
|
-
let lastPath =
|
|
122
|
+
let lastPath = "";
|
|
123
123
|
const fire = (rel) => {
|
|
124
124
|
lastPath = rel || lastPath;
|
|
125
125
|
clearTimeout(timer);
|
|
@@ -127,9 +127,9 @@ function watch(root, onChange) {
|
|
|
127
127
|
};
|
|
128
128
|
try {
|
|
129
129
|
const w = fs.watch(root, { recursive: true }, (_evt, filename) => {
|
|
130
|
-
if (filename) fire(String(filename).split(path.sep).join(
|
|
130
|
+
if (filename) fire(String(filename).split(path.sep).join("/"));
|
|
131
131
|
});
|
|
132
|
-
w.on(
|
|
132
|
+
w.on("error", () => {});
|
|
133
133
|
return () => w.close();
|
|
134
134
|
} catch {
|
|
135
135
|
// Polling fallback: scan mtimes every second.
|
|
@@ -143,7 +143,7 @@ function watch(root, onChange) {
|
|
|
143
143
|
}
|
|
144
144
|
for (const e of ents) {
|
|
145
145
|
const abs = path.join(dir, e.name);
|
|
146
|
-
const rel = base ? base +
|
|
146
|
+
const rel = base ? base + "/" + e.name : e.name;
|
|
147
147
|
if (e.isDirectory()) {
|
|
148
148
|
scan(abs, rel);
|
|
149
149
|
} else {
|
|
@@ -158,22 +158,22 @@ function watch(root, onChange) {
|
|
|
158
158
|
}
|
|
159
159
|
}
|
|
160
160
|
};
|
|
161
|
-
scan(root,
|
|
162
|
-
const iv = setInterval(() => scan(root,
|
|
161
|
+
scan(root, "");
|
|
162
|
+
const iv = setInterval(() => scan(root, ""), 1000);
|
|
163
163
|
return () => clearInterval(iv);
|
|
164
164
|
}
|
|
165
165
|
}
|
|
166
166
|
|
|
167
167
|
function openBrowser(url) {
|
|
168
168
|
const cmd =
|
|
169
|
-
process.platform ===
|
|
170
|
-
? { c:
|
|
171
|
-
: process.platform ===
|
|
172
|
-
? { c:
|
|
173
|
-
: { c:
|
|
169
|
+
process.platform === "darwin"
|
|
170
|
+
? { c: "open", a: [url] }
|
|
171
|
+
: process.platform === "win32"
|
|
172
|
+
? { c: "cmd", a: ["/c", "start", "", url] }
|
|
173
|
+
: { c: "xdg-open", a: [url] };
|
|
174
174
|
try {
|
|
175
|
-
const child = spawn(cmd.c, cmd.a, { stdio:
|
|
176
|
-
child.on(
|
|
175
|
+
const child = spawn(cmd.c, cmd.a, { stdio: "ignore", detached: true });
|
|
176
|
+
child.on("error", () => {});
|
|
177
177
|
child.unref();
|
|
178
178
|
} catch {
|
|
179
179
|
/* best-effort */
|
|
@@ -184,9 +184,9 @@ function parseArgs(argv) {
|
|
|
184
184
|
const out = { proto: null, port: null, open: true };
|
|
185
185
|
for (let i = 0; i < argv.length; i++) {
|
|
186
186
|
const a = argv[i];
|
|
187
|
-
if (a ===
|
|
188
|
-
else if (a ===
|
|
189
|
-
else if (!a.startsWith(
|
|
187
|
+
if (a === "--port" || a === "-p") out.port = Number(argv[++i]);
|
|
188
|
+
else if (a === "--no-open") out.open = false;
|
|
189
|
+
else if (!a.startsWith("-")) out.proto = a;
|
|
190
190
|
}
|
|
191
191
|
return out;
|
|
192
192
|
}
|
|
@@ -194,7 +194,7 @@ function parseArgs(argv) {
|
|
|
194
194
|
async function preview(argv = []) {
|
|
195
195
|
const { proto, port: wantPort, open } = parseArgs(argv);
|
|
196
196
|
const cwd = process.cwd();
|
|
197
|
-
const protosRoot = path.join(cwd,
|
|
197
|
+
const protosRoot = path.join(cwd, ".claudeflow", "prototypes");
|
|
198
198
|
|
|
199
199
|
// Root to serve: a specific prototype subdir, or the prototypes parent (listing).
|
|
200
200
|
let root = protosRoot;
|
|
@@ -216,24 +216,30 @@ async function preview(argv = []) {
|
|
|
216
216
|
|
|
217
217
|
const clients = new Set();
|
|
218
218
|
const server = http.createServer((req, res) => {
|
|
219
|
-
const urlPath = (req.url ||
|
|
219
|
+
const urlPath = (req.url || "/").split("?")[0];
|
|
220
220
|
|
|
221
221
|
if (urlPath === SSE_PATH) {
|
|
222
222
|
res.writeHead(200, {
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Connection:
|
|
223
|
+
"Content-Type": "text/event-stream",
|
|
224
|
+
"Cache-Control": "no-cache",
|
|
225
|
+
Connection: "keep-alive",
|
|
226
226
|
});
|
|
227
|
-
res.write(
|
|
227
|
+
res.write(": connected\n\n");
|
|
228
228
|
clients.add(res);
|
|
229
|
-
req.on(
|
|
229
|
+
req.on("close", () => clients.delete(res));
|
|
230
230
|
return;
|
|
231
231
|
}
|
|
232
232
|
|
|
233
233
|
// Listing at root when serving the prototypes parent and no index there.
|
|
234
|
-
if (
|
|
234
|
+
if (
|
|
235
|
+
(urlPath === "/" || urlPath === "") &&
|
|
236
|
+
!fs.existsSync(path.join(root, "index.html"))
|
|
237
|
+
) {
|
|
235
238
|
const body = listingPage(root);
|
|
236
|
-
res.writeHead(200, {
|
|
239
|
+
res.writeHead(200, {
|
|
240
|
+
"Content-Type": MIME[".html"],
|
|
241
|
+
"Cache-Control": "no-store",
|
|
242
|
+
});
|
|
237
243
|
res.end(injectLiveClient(body));
|
|
238
244
|
return;
|
|
239
245
|
}
|
|
@@ -241,11 +247,11 @@ async function preview(argv = []) {
|
|
|
241
247
|
let file = resolveFile(root, urlPath);
|
|
242
248
|
if (!file) {
|
|
243
249
|
res.writeHead(403);
|
|
244
|
-
res.end(
|
|
250
|
+
res.end("Forbidden");
|
|
245
251
|
return;
|
|
246
252
|
}
|
|
247
253
|
try {
|
|
248
|
-
if (fs.statSync(file).isDirectory()) file = path.join(file,
|
|
254
|
+
if (fs.statSync(file).isDirectory()) file = path.join(file, "index.html");
|
|
249
255
|
} catch {
|
|
250
256
|
/* fall through to 404 below */
|
|
251
257
|
}
|
|
@@ -253,17 +259,21 @@ async function preview(argv = []) {
|
|
|
253
259
|
try {
|
|
254
260
|
buf = fs.readFileSync(file);
|
|
255
261
|
} catch {
|
|
256
|
-
res.writeHead(404, {
|
|
257
|
-
res.end(
|
|
262
|
+
res.writeHead(404, { "Content-Type": MIME[".html"] });
|
|
263
|
+
res.end(
|
|
264
|
+
injectLiveClient(
|
|
265
|
+
'<!doctype html><body style="font-family:system-ui;padding:40px">404 — not found</body>',
|
|
266
|
+
),
|
|
267
|
+
);
|
|
258
268
|
return;
|
|
259
269
|
}
|
|
260
270
|
const ext = path.extname(file).toLowerCase();
|
|
261
|
-
const type = MIME[ext] ||
|
|
262
|
-
if (ext ===
|
|
263
|
-
res.writeHead(200, {
|
|
264
|
-
res.end(injectLiveClient(buf.toString(
|
|
271
|
+
const type = MIME[ext] || "application/octet-stream";
|
|
272
|
+
if (ext === ".html") {
|
|
273
|
+
res.writeHead(200, { "Content-Type": type, "Cache-Control": "no-store" });
|
|
274
|
+
res.end(injectLiveClient(buf.toString("utf8")));
|
|
265
275
|
} else {
|
|
266
|
-
res.writeHead(200, {
|
|
276
|
+
res.writeHead(200, { "Content-Type": type, "Cache-Control": "no-store" });
|
|
267
277
|
res.end(buf);
|
|
268
278
|
}
|
|
269
279
|
});
|
|
@@ -280,25 +290,33 @@ async function preview(argv = []) {
|
|
|
280
290
|
|
|
281
291
|
const onPort = (actualPort) => {
|
|
282
292
|
const url = `http://127.0.0.1:${actualPort}/`;
|
|
283
|
-
console.log(
|
|
284
|
-
console.log(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
console.log(
|
|
293
|
+
console.log("");
|
|
294
|
+
console.log(
|
|
295
|
+
` ${ui.GREEN}claudeflow preview${ui.RESET} — live reload for prototypes`,
|
|
296
|
+
);
|
|
297
|
+
console.log(
|
|
298
|
+
` ${ui.CYAN}${url}${ui.RESET} ${ui.DIM}(serving .claudeflow/prototypes/)${ui.RESET}`,
|
|
299
|
+
);
|
|
300
|
+
console.log(
|
|
301
|
+
` ${ui.DIM}CSS edits hot-swap (no flicker); HTML edits reload the changed view. Ctrl-C to stop.${ui.RESET}`,
|
|
302
|
+
);
|
|
303
|
+
console.log("");
|
|
288
304
|
if (open) openBrowser(proto ? url : url);
|
|
289
305
|
};
|
|
290
306
|
|
|
291
307
|
await new Promise((resolve) => {
|
|
292
308
|
const tryListen = (p, allowFallback) => {
|
|
293
|
-
server.once(
|
|
294
|
-
if (err && err.code ===
|
|
309
|
+
server.once("error", (err) => {
|
|
310
|
+
if (err && err.code === "EADDRINUSE" && allowFallback) {
|
|
295
311
|
tryListen(0, false); // OS-assigned free port
|
|
296
312
|
} else {
|
|
297
|
-
console.error(
|
|
313
|
+
console.error(
|
|
314
|
+
`${ui.RED}preview server error: ${err.message}${ui.RESET}`,
|
|
315
|
+
);
|
|
298
316
|
resolve();
|
|
299
317
|
}
|
|
300
318
|
});
|
|
301
|
-
server.listen(p,
|
|
319
|
+
server.listen(p, "127.0.0.1", () => {
|
|
302
320
|
onPort(server.address().port);
|
|
303
321
|
resolve();
|
|
304
322
|
});
|
|
@@ -329,8 +347,8 @@ async function preview(argv = []) {
|
|
|
329
347
|
server.close(() => process.exit(0));
|
|
330
348
|
setTimeout(() => process.exit(0), 300).unref();
|
|
331
349
|
};
|
|
332
|
-
process.on(
|
|
333
|
-
process.on(
|
|
350
|
+
process.on("SIGINT", shutdown);
|
|
351
|
+
process.on("SIGTERM", shutdown);
|
|
334
352
|
|
|
335
353
|
// Keep the process alive (standing server).
|
|
336
354
|
return new Promise(() => {});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axiomatic-labs/claudeflow",
|
|
3
|
-
"version": "2.32.
|
|
3
|
+
"version": "2.32.4",
|
|
4
4
|
"description": "Claudeflow — AI-powered development toolkit for Claude Code. Skills, agents, hooks, and quality gates that ship production apps.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"claudeflow": "./bin/cli.js"
|