@programinglive/dev-workflow-mcp-server 1.3.12 → 1.4.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/.github/workflows/npm-publish.yml +3 -0
- package/CHANGELOG.md +108 -0
- package/README.md +1 -1
- package/docs/PLESK_DEPLOYMENT.md +170 -0
- package/docs/WORKFLOW-BEST-PRACTICES.md +14 -0
- package/docs/release-notes/RELEASE_NOTES.md +26 -0
- package/package.json +7 -6
- package/release-wrapper.js +68 -33
- package/tests/build.test.js +3 -3
- package/tests/handlers.test.js +78 -0
- package/tests/release-wrapper.test.js +2 -0
- package/tests/workflowState.test.js +7 -4
- package/tools/definitions.js +2 -2
- package/tools/handlers.js +2 -2
- package/vite.web.config.js +23 -0
- package/web/README.md +36 -0
- package/web/app/api/docs/route.ts +32 -0
- package/web/app/api/history/route.ts +19 -0
- package/web/app/api/npm-downloads/route.ts +21 -0
- package/web/app/api/summary/route.ts +15 -0
- package/web/app/api/version/route.ts +15 -0
- package/web/app/docs/page.tsx +202 -0
- package/web/app/favicon.ico +0 -0
- package/web/app/globals.css +91 -0
- package/web/app/layout.tsx +36 -0
- package/web/app/page.tsx +15 -0
- package/web/components/ApiReference.tsx +20 -0
- package/web/components/Features.tsx +61 -0
- package/web/components/Footer.tsx +9 -0
- package/web/components/Hero.tsx +91 -0
- package/web/components/History.tsx +143 -0
- package/web/components/Navbar.tsx +64 -0
- package/web/components/Terminal.tsx +114 -0
- package/web/eslint.config.mjs +18 -0
- package/web/lib/db.js +131 -0
- package/web/lib/docs.js +73 -0
- package/web/next.config.ts +8 -0
- package/web/package-lock.json +6971 -0
- package/web/package.json +29 -0
- package/web/postcss.config.mjs +9 -0
- package/web/public/assets/android-chrome-192x192.png +0 -0
- package/web/public/assets/android-chrome-512x512.png +0 -0
- package/web/public/assets/apple-touch-icon.png +0 -0
- package/web/public/assets/favicon-16x16.png +0 -0
- package/web/public/assets/favicon-32x32.png +0 -0
- package/web/public/assets/favicon.ico +0 -0
- package/web/public/assets/favicon.png +0 -0
- package/web/public/assets/logo.png +0 -0
- package/web/public/assets/og-preview.png +0 -0
- package/web/public/assets/site.webmanifest +1 -0
- package/web/public/file.svg +1 -0
- package/web/public/globe.svg +1 -0
- package/web/public/next.svg +1 -0
- package/web/public/vercel.svg +1 -0
- package/web/public/window.svg +1 -0
- package/web/server.js +30 -158
- package/web/tailwind.config.ts +19 -0
- package/web/tsconfig.json +42 -0
- package/web-legacy/public/assets/android-chrome-192x192.png +0 -0
- package/web-legacy/public/assets/android-chrome-512x512.png +0 -0
- package/web-legacy/public/assets/apple-touch-icon.png +0 -0
- package/web-legacy/public/assets/favicon-16x16.png +0 -0
- package/web-legacy/public/assets/favicon-32x32.png +0 -0
- package/web-legacy/public/assets/favicon.ico +0 -0
- package/web-legacy/public/assets/favicon.png +0 -0
- package/web-legacy/public/assets/logo.png +0 -0
- package/web-legacy/public/assets/og-preview.png +0 -0
- package/web-legacy/public/assets/site.webmanifest +1 -0
- package/{dist → web-legacy/public}/docs/EXAMPLES.html +87 -2
- package/{dist → web-legacy/public}/docs/GETTING-STARTED.html +87 -2
- package/{dist → web-legacy/public}/docs/README.html +87 -2
- package/{dist → web-legacy/public}/docs/RELEASE_NOTES.html +92 -2
- package/{dist → web-legacy/public}/docs/TROUBLESHOOTING.html +87 -2
- package/{dist → web-legacy/public}/docs/WORKFLOW-TEMPLATES.html +87 -2
- package/{dist → web-legacy/public}/docs/web-dashboard.html +87 -2
- package/web-legacy/public/index.html +223 -0
- package/web-legacy/public/input.css +80 -0
- package/web-legacy/public/main.js +167 -0
- package/web-legacy/public/output.css +1559 -0
- package/web-legacy/server.js +207 -0
- package/workflow-state.js +11 -25
- package/web/public/index.html +0 -1046
- package/web/public/input.css +0 -3
- package/web/public/output.css +0 -1
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { createServer } from "http";
|
|
2
|
+
import { readFile, access } from "fs/promises";
|
|
3
|
+
import { fileURLToPath } from "url";
|
|
4
|
+
import { dirname, join, extname } from "path";
|
|
5
|
+
import getPort from "get-port";
|
|
6
|
+
import { getHistoryForUser, getSummaryForUser, getHistorySummary } from "../db/index.js";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = dirname(__filename);
|
|
10
|
+
const projectRoot = join(__dirname, "..");
|
|
11
|
+
|
|
12
|
+
const docSources = {
|
|
13
|
+
"README.md": {
|
|
14
|
+
markdown: join(projectRoot, "README.md"),
|
|
15
|
+
html: join(projectRoot, "dist", "docs", "README.html"),
|
|
16
|
+
},
|
|
17
|
+
"web-dashboard.md": {
|
|
18
|
+
markdown: join(projectRoot, "docs", "web-dashboard.md"),
|
|
19
|
+
html: join(projectRoot, "dist", "docs", "web-dashboard.html"),
|
|
20
|
+
},
|
|
21
|
+
"GETTING-STARTED.md": {
|
|
22
|
+
markdown: join(projectRoot, "docs", "antigravity", "GETTING-STARTED.md"),
|
|
23
|
+
html: join(projectRoot, "dist", "docs", "GETTING-STARTED.html"),
|
|
24
|
+
},
|
|
25
|
+
"EXAMPLES.md": {
|
|
26
|
+
markdown: join(projectRoot, "docs", "antigravity", "EXAMPLES.md"),
|
|
27
|
+
html: join(projectRoot, "dist", "docs", "EXAMPLES.html"),
|
|
28
|
+
},
|
|
29
|
+
"WORKFLOW-TEMPLATES.md": {
|
|
30
|
+
markdown: join(projectRoot, "docs", "antigravity", "WORKFLOW-TEMPLATES.md"),
|
|
31
|
+
html: join(projectRoot, "dist", "docs", "WORKFLOW-TEMPLATES.html"),
|
|
32
|
+
},
|
|
33
|
+
"TROUBLESHOOTING.md": {
|
|
34
|
+
markdown: join(projectRoot, "docs", "antigravity", "TROUBLESHOOTING.md"),
|
|
35
|
+
html: join(projectRoot, "dist", "docs", "TROUBLESHOOTING.html"),
|
|
36
|
+
},
|
|
37
|
+
"RELEASE_NOTES.md": {
|
|
38
|
+
markdown: join(projectRoot, "docs", "release-notes", "RELEASE_NOTES.md"),
|
|
39
|
+
html: join(projectRoot, "dist", "docs", "RELEASE_NOTES.html"),
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
async function resolveDocContent(docKey) {
|
|
44
|
+
const entry = docSources[docKey];
|
|
45
|
+
if (!entry) {
|
|
46
|
+
return { exists: false };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Try HTML first, fall back to Markdown in development or if HTML missing.
|
|
50
|
+
const candidates = [entry.html, entry.markdown];
|
|
51
|
+
for (const candidate of candidates) {
|
|
52
|
+
try {
|
|
53
|
+
await access(candidate);
|
|
54
|
+
const content = await readFile(candidate, "utf-8");
|
|
55
|
+
const format = candidate.endsWith(".html") ? "html" : "markdown";
|
|
56
|
+
return { exists: true, content, format };
|
|
57
|
+
} catch {
|
|
58
|
+
// try next candidate
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return { exists: false };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getPreferredPort() {
|
|
66
|
+
const fromEnv = process.env.PORT || process.env.DEV_WORKFLOW_WEB_PORT;
|
|
67
|
+
|
|
68
|
+
if (!fromEnv) {
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const parsed = Number(fromEnv);
|
|
73
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function start() {
|
|
77
|
+
const preferred = getPreferredPort();
|
|
78
|
+
const PORT = preferred ?? await getPort({ port: 3111 });
|
|
79
|
+
|
|
80
|
+
const server = createServer(async (req, res) => {
|
|
81
|
+
try {
|
|
82
|
+
const url = new URL(req.url, `http://${req.headers.host}`);
|
|
83
|
+
const userId = url.searchParams.get("user") || "default";
|
|
84
|
+
|
|
85
|
+
const noCacheHeaders = {
|
|
86
|
+
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
87
|
+
"Pragma": "no-cache",
|
|
88
|
+
"Expires": "0"
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// Serve index.html for root path
|
|
92
|
+
if (req.method === "GET" && (url.pathname === "/" || url.pathname === "/index.html")) {
|
|
93
|
+
try {
|
|
94
|
+
const html = await readFile(join(__dirname, "public", "index.html"), "utf-8");
|
|
95
|
+
res.writeHead(200, { "Content-Type": "text/html", ...noCacheHeaders });
|
|
96
|
+
res.end(html);
|
|
97
|
+
} catch (err) {
|
|
98
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
99
|
+
res.end("Error loading page");
|
|
100
|
+
}
|
|
101
|
+
} else if (req.method === "GET" && url.pathname === "/input.css") {
|
|
102
|
+
try {
|
|
103
|
+
// Serve the built CSS file
|
|
104
|
+
const css = await readFile(join(__dirname, "public", "output.css"), "utf-8");
|
|
105
|
+
res.writeHead(200, { "Content-Type": "text/css", ...noCacheHeaders });
|
|
106
|
+
res.end(css);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
109
|
+
res.end("CSS not found");
|
|
110
|
+
}
|
|
111
|
+
} else if (req.method === "GET" && url.pathname === "/main.js") {
|
|
112
|
+
try {
|
|
113
|
+
const js = await readFile(join(__dirname, "public", "main.js"), "utf-8");
|
|
114
|
+
res.writeHead(200, { "Content-Type": "application/javascript", ...noCacheHeaders });
|
|
115
|
+
res.end(js);
|
|
116
|
+
} catch (err) {
|
|
117
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
118
|
+
res.end("JS not found");
|
|
119
|
+
}
|
|
120
|
+
} else if (req.method === "GET" && url.pathname.startsWith("/assets/")) {
|
|
121
|
+
try {
|
|
122
|
+
const assetPath = join(__dirname, "public", url.pathname);
|
|
123
|
+
const content = await readFile(assetPath);
|
|
124
|
+
const ext = extname(assetPath);
|
|
125
|
+
const mimeTypes = {
|
|
126
|
+
".png": "image/png",
|
|
127
|
+
".jpg": "image/jpeg",
|
|
128
|
+
".svg": "image/svg+xml",
|
|
129
|
+
".ico": "image/x-icon",
|
|
130
|
+
};
|
|
131
|
+
res.writeHead(200, { "Content-Type": mimeTypes[ext] || "application/octet-stream" });
|
|
132
|
+
res.end(content);
|
|
133
|
+
} catch (err) {
|
|
134
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
135
|
+
res.end("Asset not found");
|
|
136
|
+
}
|
|
137
|
+
} else if (req.method === "GET" && url.pathname === "/") {
|
|
138
|
+
const html = await readFile(join(__dirname, "public", "index.html"), "utf-8");
|
|
139
|
+
res.writeHead(200, { "Content-Type": "text/html" });
|
|
140
|
+
res.end(html);
|
|
141
|
+
} else if (req.method === "GET" && url.pathname === "/api/summary") {
|
|
142
|
+
const summary = getSummaryForUser(userId);
|
|
143
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
144
|
+
res.end(JSON.stringify({ summary }));
|
|
145
|
+
} else if (req.method === "GET" && url.pathname === "/api/history") {
|
|
146
|
+
const page = Number(url.searchParams.get("page") || "1");
|
|
147
|
+
const pageSize = Number(url.searchParams.get("pageSize") || "20");
|
|
148
|
+
const startDate = url.searchParams.get("startDate") || undefined;
|
|
149
|
+
const endDate = url.searchParams.get("endDate") || undefined;
|
|
150
|
+
const history = getHistoryForUser(userId, { page, pageSize, startDate, endDate });
|
|
151
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
152
|
+
res.end(JSON.stringify(history));
|
|
153
|
+
} else if (req.method === "GET" && url.pathname === "/api/history-summary") {
|
|
154
|
+
const startDate = url.searchParams.get("startDate") || undefined;
|
|
155
|
+
const endDate = url.searchParams.get("endDate") || undefined;
|
|
156
|
+
const frequency = url.searchParams.get("frequency") || "daily";
|
|
157
|
+
const summary = getHistorySummary(userId, { startDate, endDate, frequency });
|
|
158
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
159
|
+
res.end(JSON.stringify({ summary }));
|
|
160
|
+
} else if (req.method === "GET" && url.pathname === "/api/docs") {
|
|
161
|
+
const rawDocKey = url.searchParams.get("doc");
|
|
162
|
+
const availableDocs = Object.keys(docSources);
|
|
163
|
+
|
|
164
|
+
if (!rawDocKey) {
|
|
165
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
166
|
+
res.end(JSON.stringify({ docs: availableDocs }));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const docKey = rawDocKey.trim();
|
|
171
|
+
const resolvedKey = availableDocs.find((key) => key.toLowerCase() === docKey.toLowerCase());
|
|
172
|
+
if (!resolvedKey) {
|
|
173
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
174
|
+
res.end(JSON.stringify({ error: "Document not found", requested: docKey, available: availableDocs }));
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const result = await resolveDocContent(resolvedKey);
|
|
179
|
+
if (!result.exists) {
|
|
180
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
181
|
+
res.end(JSON.stringify({ error: "Document asset is missing" }));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
186
|
+
res.end(JSON.stringify({ doc: resolvedKey, content: result.content, format: result.format }));
|
|
187
|
+
} else if (req.method === "GET" && url.pathname === "/api/version") {
|
|
188
|
+
const packageJson = await readFile(join(projectRoot, "package.json"), "utf-8");
|
|
189
|
+
const { version } = JSON.parse(packageJson);
|
|
190
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
191
|
+
res.end(JSON.stringify({ version }));
|
|
192
|
+
} else {
|
|
193
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
194
|
+
res.end("Not found");
|
|
195
|
+
}
|
|
196
|
+
} catch (err) {
|
|
197
|
+
res.writeHead(500, { "Content-Type": "text/plain" });
|
|
198
|
+
res.end(err.message);
|
|
199
|
+
}
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
server.listen(PORT, () => {
|
|
203
|
+
console.log(`🌐 Dev Workflow Dashboard running at http://localhost:${PORT}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
start();
|
package/workflow-state.js
CHANGED
|
@@ -398,40 +398,26 @@ export function resolveStateFile() {
|
|
|
398
398
|
return path.resolve(customPath);
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
const initCwd = process.env.INIT_CWD || process.cwd();
|
|
402
|
-
const resolvedInitCwd = path.resolve(initCwd);
|
|
403
401
|
const moduleRoot = path.resolve(__dirname, "..");
|
|
404
402
|
const cwdResolved = path.resolve(process.cwd());
|
|
403
|
+
const initCwd = process.env.INIT_CWD ? path.resolve(process.env.INIT_CWD) : null;
|
|
405
404
|
|
|
406
|
-
|
|
405
|
+
// 1. Prefer the project rooted at the current working directory
|
|
406
|
+
let projectRoot = findProjectRoot(cwdResolved);
|
|
407
407
|
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
normalizeAndStoreCandidate(candidateRoots, initProjectRoot);
|
|
413
|
-
normalizeAndStoreCandidate(candidateRoots, cwdProjectRoot);
|
|
414
|
-
if (!isRootPath(resolvedInitCwd)) {
|
|
415
|
-
normalizeAndStoreCandidate(candidateRoots, resolvedInitCwd);
|
|
416
|
-
addAncestorCandidates(candidateRoots, resolvedInitCwd);
|
|
417
|
-
}
|
|
418
|
-
if (!isRootPath(cwdResolved)) {
|
|
419
|
-
normalizeAndStoreCandidate(candidateRoots, cwdResolved);
|
|
420
|
-
addAncestorCandidates(candidateRoots, cwdResolved);
|
|
408
|
+
// 2. Fall back to the project rooted at INIT_CWD if different
|
|
409
|
+
if (!projectRoot && initCwd) {
|
|
410
|
+
projectRoot = findProjectRoot(initCwd);
|
|
421
411
|
}
|
|
422
412
|
|
|
423
|
-
|
|
424
|
-
if (!
|
|
425
|
-
|
|
426
|
-
}
|
|
427
|
-
if (!isRootPath(__dirname)) {
|
|
428
|
-
normalizeAndStoreCandidate(candidateRoots, __dirname);
|
|
413
|
+
// 3. Fall back to the project containing this module
|
|
414
|
+
if (!projectRoot) {
|
|
415
|
+
projectRoot = findProjectRoot(moduleRoot);
|
|
429
416
|
}
|
|
430
417
|
|
|
431
|
-
|
|
432
|
-
|
|
418
|
+
// 4. Final fallback: use a non-root working directory or the module root
|
|
433
419
|
if (!projectRoot) {
|
|
434
|
-
projectRoot =
|
|
420
|
+
projectRoot = !isRootPath(cwdResolved) ? cwdResolved : moduleRoot;
|
|
435
421
|
}
|
|
436
422
|
|
|
437
423
|
const stateDir = path.join(projectRoot, STATE_DIR);
|