annotask 0.0.6 → 0.0.7
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 +13 -7
- package/dist/chunk-2PL37QTY.js +1043 -0
- package/dist/chunk-2PL37QTY.js.map +1 -0
- package/dist/chunk-4J7F2BC5.js +61 -0
- package/dist/chunk-4J7F2BC5.js.map +1 -0
- package/dist/chunk-DPJXWLPB.js +61 -0
- package/dist/chunk-DPJXWLPB.js.map +1 -0
- package/dist/chunk-DQBM2VFQ.js +465 -0
- package/dist/chunk-DQBM2VFQ.js.map +1 -0
- package/dist/chunk-FMNBQLJZ.js +61 -0
- package/dist/chunk-FMNBQLJZ.js.map +1 -0
- package/dist/chunk-LGNV6UH3.js +488 -0
- package/dist/chunk-LGNV6UH3.js.map +1 -0
- package/dist/chunk-TBBJTSMJ.js +496 -0
- package/dist/chunk-TBBJTSMJ.js.map +1 -0
- package/dist/cli.js +1 -1
- package/dist/index.js +2 -2
- package/dist/server.d.ts +1 -1
- package/dist/server.js +1 -1
- package/dist/shell/assets/index-B-SRjSQF.js +59 -0
- package/dist/shell/assets/{index-En0AXNAK.css → index-CeF02UAx.css} +1 -1
- package/dist/shell/index.html +2 -2
- package/dist/standalone.js +2 -2
- package/dist/vendor/axe-core.min.js +12 -0
- package/dist/vendor/html2canvas.min.js +20 -0
- package/dist/webpack.js +3 -3
- package/package.json +8 -3
- package/dist/shell/assets/index-Dko9s8T0.js +0 -59
- package/skills/annotask-watch/SKILL.md +0 -47
|
@@ -0,0 +1,496 @@
|
|
|
1
|
+
// src/server/api.ts
|
|
2
|
+
import fsp from "fs/promises";
|
|
3
|
+
import nodePath from "path";
|
|
4
|
+
var MAX_BODY_SIZE = 4194304;
|
|
5
|
+
var VALID_TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "applied", "review", "accepted", "denied"]);
|
|
6
|
+
var PATCHABLE_TASK_FIELDS = /* @__PURE__ */ new Set([
|
|
7
|
+
"status",
|
|
8
|
+
"description",
|
|
9
|
+
"notes",
|
|
10
|
+
"screenshot",
|
|
11
|
+
"feedback",
|
|
12
|
+
"intent",
|
|
13
|
+
"action",
|
|
14
|
+
"context",
|
|
15
|
+
"viewport",
|
|
16
|
+
"interaction_history",
|
|
17
|
+
"element_context",
|
|
18
|
+
"mfe"
|
|
19
|
+
]);
|
|
20
|
+
function readBody(req) {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
let body = "";
|
|
23
|
+
let size = 0;
|
|
24
|
+
req.on("data", (chunk) => {
|
|
25
|
+
size += chunk.length;
|
|
26
|
+
if (size > MAX_BODY_SIZE) {
|
|
27
|
+
req.destroy();
|
|
28
|
+
reject(new Error("Request body too large"));
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
body += chunk.toString();
|
|
32
|
+
});
|
|
33
|
+
req.on("end", () => resolve(body));
|
|
34
|
+
req.on("error", reject);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function parseJSON(raw) {
|
|
38
|
+
try {
|
|
39
|
+
return { ok: true, data: JSON.parse(raw) };
|
|
40
|
+
} catch {
|
|
41
|
+
return { ok: false };
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function sendError(res, status, message) {
|
|
45
|
+
res.statusCode = status;
|
|
46
|
+
res.end(JSON.stringify({ error: message }));
|
|
47
|
+
}
|
|
48
|
+
function isLocalOrigin(origin) {
|
|
49
|
+
if (!origin) return true;
|
|
50
|
+
try {
|
|
51
|
+
const url = new URL(origin);
|
|
52
|
+
const host = url.hostname;
|
|
53
|
+
return host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1";
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function getCorsOrigin(req) {
|
|
59
|
+
const origin = req.headers.origin;
|
|
60
|
+
if (!origin) return null;
|
|
61
|
+
return isLocalOrigin(origin) ? origin : null;
|
|
62
|
+
}
|
|
63
|
+
function createAPIMiddleware(options) {
|
|
64
|
+
return async (req, res, next) => {
|
|
65
|
+
if (req.url?.startsWith("/__annotask/screenshots/") && req.method === "GET") {
|
|
66
|
+
const filename = req.url.replace("/__annotask/screenshots/", "").replace(/\?.*$/, "");
|
|
67
|
+
if (!/^[a-zA-Z0-9_-]+\.png$/.test(filename)) {
|
|
68
|
+
res.statusCode = 400;
|
|
69
|
+
res.end("Invalid filename");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const filePath = nodePath.join(options.projectRoot, ".annotask", "screenshots", filename);
|
|
73
|
+
try {
|
|
74
|
+
const data = await fsp.readFile(filePath);
|
|
75
|
+
res.setHeader("Content-Type", "image/png");
|
|
76
|
+
res.setHeader("Cache-Control", "public, max-age=3600");
|
|
77
|
+
const corsOrigin2 = getCorsOrigin(req);
|
|
78
|
+
if (corsOrigin2) res.setHeader("Access-Control-Allow-Origin", corsOrigin2);
|
|
79
|
+
res.end(data);
|
|
80
|
+
} catch {
|
|
81
|
+
res.statusCode = 404;
|
|
82
|
+
res.end("Not found");
|
|
83
|
+
}
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (!req.url?.startsWith("/__annotask/api/")) return next();
|
|
87
|
+
const path3 = req.url.replace("/__annotask/api/", "");
|
|
88
|
+
const corsOrigin = getCorsOrigin(req);
|
|
89
|
+
res.setHeader("Content-Type", "application/json");
|
|
90
|
+
if (corsOrigin) {
|
|
91
|
+
res.setHeader("Access-Control-Allow-Origin", corsOrigin);
|
|
92
|
+
res.setHeader("Vary", "Origin");
|
|
93
|
+
}
|
|
94
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, OPTIONS");
|
|
95
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
96
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
97
|
+
if (req.method === "OPTIONS") {
|
|
98
|
+
res.statusCode = 200;
|
|
99
|
+
res.end();
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if ((req.method === "POST" || req.method === "PATCH") && !isLocalOrigin(req.headers.origin)) {
|
|
103
|
+
return sendError(res, 403, "Forbidden: non-local origin");
|
|
104
|
+
}
|
|
105
|
+
if (path3 === "report" && req.method === "GET") {
|
|
106
|
+
const report = options.getReport() ?? { version: "1.0", changes: [] };
|
|
107
|
+
const urlObj = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
108
|
+
const mfeFilter = urlObj.searchParams.get("mfe");
|
|
109
|
+
if (mfeFilter && report.changes) {
|
|
110
|
+
const filtered = { ...report, changes: report.changes.filter((c) => c.mfe === mfeFilter) };
|
|
111
|
+
res.end(JSON.stringify(filtered, null, 2));
|
|
112
|
+
} else {
|
|
113
|
+
res.end(JSON.stringify(report, null, 2));
|
|
114
|
+
}
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
if (path3 === "config" && req.method === "GET") {
|
|
118
|
+
res.end(JSON.stringify(options.getConfig(), null, 2));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
if (path3 === "design-spec" && req.method === "GET") {
|
|
122
|
+
res.end(JSON.stringify(options.getDesignSpec(), null, 2));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (path3.startsWith("tasks") && !path3.startsWith("tasks/") && req.method === "GET") {
|
|
126
|
+
const urlObj = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
127
|
+
const mfeFilter = urlObj.searchParams.get("mfe");
|
|
128
|
+
const taskData = options.getTasks();
|
|
129
|
+
if (mfeFilter) {
|
|
130
|
+
const filtered = { ...taskData, tasks: taskData.tasks.filter((t) => t.mfe === mfeFilter) };
|
|
131
|
+
res.end(JSON.stringify(filtered, null, 2));
|
|
132
|
+
} else {
|
|
133
|
+
res.end(JSON.stringify(taskData, null, 2));
|
|
134
|
+
}
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
if (path3 === "tasks" && req.method === "POST") {
|
|
138
|
+
let raw;
|
|
139
|
+
try {
|
|
140
|
+
raw = await readBody(req);
|
|
141
|
+
} catch {
|
|
142
|
+
return sendError(res, 413, "Request body too large");
|
|
143
|
+
}
|
|
144
|
+
const parsed = parseJSON(raw);
|
|
145
|
+
if (!parsed.ok) return sendError(res, 400, "Invalid JSON body");
|
|
146
|
+
const body = parsed.data;
|
|
147
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return sendError(res, 400, "Request body must be a JSON object");
|
|
148
|
+
if (typeof body.type !== "string" || !body.type) return sendError(res, 400, "Missing required field: type (string)");
|
|
149
|
+
if (typeof body.description !== "string") return sendError(res, 400, "Missing required field: description (string)");
|
|
150
|
+
res.end(JSON.stringify(options.addTask(body), null, 2));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (path3 === "screenshots" && req.method === "POST") {
|
|
154
|
+
let raw;
|
|
155
|
+
try {
|
|
156
|
+
raw = await readBody(req);
|
|
157
|
+
} catch {
|
|
158
|
+
return sendError(res, 413, "Request body too large");
|
|
159
|
+
}
|
|
160
|
+
const parsed = parseJSON(raw);
|
|
161
|
+
if (!parsed.ok) return sendError(res, 400, "Invalid JSON body");
|
|
162
|
+
const body = parsed.data;
|
|
163
|
+
if (!body.data || typeof body.data !== "string") return sendError(res, 400, "Missing data field");
|
|
164
|
+
const match = body.data.match(/^data:image\/png;base64,(.+)$/);
|
|
165
|
+
if (!match) return sendError(res, 400, "Invalid PNG data URL");
|
|
166
|
+
const buffer = Buffer.from(match[1], "base64");
|
|
167
|
+
if (buffer.length > 4 * 1024 * 1024) return sendError(res, 413, "Screenshot too large (max 4MB)");
|
|
168
|
+
const filename = `screenshot-${Date.now()}-${Math.random().toString(36).slice(2, 7)}.png`;
|
|
169
|
+
const dir = nodePath.join(options.projectRoot, ".annotask", "screenshots");
|
|
170
|
+
await fsp.mkdir(dir, { recursive: true });
|
|
171
|
+
await fsp.writeFile(nodePath.join(dir, filename), buffer);
|
|
172
|
+
res.end(JSON.stringify({ filename }));
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
if (path3.startsWith("tasks/") && req.method === "PATCH") {
|
|
176
|
+
const id = path3.replace("tasks/", "");
|
|
177
|
+
let raw;
|
|
178
|
+
try {
|
|
179
|
+
raw = await readBody(req);
|
|
180
|
+
} catch {
|
|
181
|
+
return sendError(res, 413, "Request body too large");
|
|
182
|
+
}
|
|
183
|
+
const parsed = parseJSON(raw);
|
|
184
|
+
if (!parsed.ok) return sendError(res, 400, "Invalid JSON body");
|
|
185
|
+
const body = parsed.data;
|
|
186
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return sendError(res, 400, "Request body must be a JSON object");
|
|
187
|
+
if (body.status !== void 0 && !VALID_TASK_STATUSES.has(body.status)) {
|
|
188
|
+
return sendError(res, 400, `Invalid status. Must be one of: ${[...VALID_TASK_STATUSES].join(", ")}`);
|
|
189
|
+
}
|
|
190
|
+
const sanitized = {};
|
|
191
|
+
for (const key of Object.keys(body)) {
|
|
192
|
+
if (PATCHABLE_TASK_FIELDS.has(key)) {
|
|
193
|
+
sanitized[key] = body[key];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
res.end(JSON.stringify(options.updateTask(id, sanitized), null, 2));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
if (path3 === "status" && req.method === "GET") {
|
|
200
|
+
res.end(JSON.stringify({ status: "ok", tool: "annotask" }));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
res.statusCode = 404;
|
|
204
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/server/ws-server.ts
|
|
209
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
210
|
+
function createWSServer() {
|
|
211
|
+
let currentReport = null;
|
|
212
|
+
const clients = /* @__PURE__ */ new Set();
|
|
213
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
214
|
+
wss.on("connection", (ws) => {
|
|
215
|
+
clients.add(ws);
|
|
216
|
+
if (currentReport) {
|
|
217
|
+
ws.send(JSON.stringify({ event: "report:current", data: currentReport, timestamp: Date.now() }));
|
|
218
|
+
}
|
|
219
|
+
ws.on("message", (raw) => {
|
|
220
|
+
try {
|
|
221
|
+
const msg = JSON.parse(raw.toString());
|
|
222
|
+
if (msg.event === "report:updated") {
|
|
223
|
+
currentReport = msg.data;
|
|
224
|
+
for (const client of clients) {
|
|
225
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
226
|
+
client.send(JSON.stringify({ event: "report:updated", data: msg.data, timestamp: Date.now() }));
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (msg.event === "changes:cleared") {
|
|
231
|
+
currentReport = null;
|
|
232
|
+
for (const client of clients) {
|
|
233
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
234
|
+
client.send(JSON.stringify({ event: "changes:cleared", data: null, timestamp: Date.now() }));
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (msg.event === "get:report") {
|
|
239
|
+
ws.send(JSON.stringify({ event: "report:current", data: currentReport, timestamp: Date.now() }));
|
|
240
|
+
}
|
|
241
|
+
} catch {
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
ws.on("close", () => {
|
|
245
|
+
clients.delete(ws);
|
|
246
|
+
});
|
|
247
|
+
});
|
|
248
|
+
return {
|
|
249
|
+
handleUpgrade(req, socket, head) {
|
|
250
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
251
|
+
wss.emit("connection", ws, req);
|
|
252
|
+
});
|
|
253
|
+
},
|
|
254
|
+
broadcast(event, data) {
|
|
255
|
+
const msg = JSON.stringify({ event, data, timestamp: Date.now() });
|
|
256
|
+
for (const client of clients) {
|
|
257
|
+
if (client.readyState === WebSocket.OPEN) client.send(msg);
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
getReport() {
|
|
261
|
+
return currentReport;
|
|
262
|
+
},
|
|
263
|
+
clients
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// src/server/serve-shell.ts
|
|
268
|
+
import fsp2 from "fs/promises";
|
|
269
|
+
import path from "path";
|
|
270
|
+
import { fileURLToPath } from "url";
|
|
271
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
272
|
+
function findShellDist() {
|
|
273
|
+
return path.resolve(__dirname, "shell");
|
|
274
|
+
}
|
|
275
|
+
function findVendorDist() {
|
|
276
|
+
return path.resolve(__dirname, "vendor");
|
|
277
|
+
}
|
|
278
|
+
function createShellMiddleware() {
|
|
279
|
+
const shellDist = findShellDist();
|
|
280
|
+
const vendorDist = findVendorDist();
|
|
281
|
+
return async (req, res, next) => {
|
|
282
|
+
if (!req.url?.startsWith("/__annotask")) return next();
|
|
283
|
+
const origin = req.headers.origin;
|
|
284
|
+
if (origin) {
|
|
285
|
+
try {
|
|
286
|
+
const host = new URL(origin).hostname;
|
|
287
|
+
if (host === "localhost" || host === "127.0.0.1" || host === "[::1]" || host === "::1") {
|
|
288
|
+
res.setHeader("Access-Control-Allow-Origin", origin);
|
|
289
|
+
res.setHeader("Vary", "Origin");
|
|
290
|
+
}
|
|
291
|
+
} catch {
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
let filePath = req.url.replace("/__annotask", "") || "/";
|
|
295
|
+
const queryIndex = filePath.indexOf("?");
|
|
296
|
+
if (queryIndex !== -1) filePath = filePath.slice(0, queryIndex);
|
|
297
|
+
if (filePath === "/" || filePath === "") filePath = "/index.html";
|
|
298
|
+
if (filePath.startsWith("/api/") || filePath === "/ws") return next();
|
|
299
|
+
if (filePath.startsWith("/vendor/")) {
|
|
300
|
+
const vendorFile = path.join(vendorDist, filePath.replace("/vendor/", ""));
|
|
301
|
+
if (!vendorFile.startsWith(vendorDist)) {
|
|
302
|
+
res.statusCode = 403;
|
|
303
|
+
res.end("Forbidden");
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
try {
|
|
307
|
+
const data = await fsp2.readFile(vendorFile);
|
|
308
|
+
res.setHeader("Content-Type", "application/javascript");
|
|
309
|
+
res.setHeader("Cache-Control", "public, max-age=86400");
|
|
310
|
+
res.end(data);
|
|
311
|
+
} catch {
|
|
312
|
+
res.statusCode = 404;
|
|
313
|
+
res.end("Vendor file not found");
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
318
|
+
const fullPath = path.join(shellDist, filePath);
|
|
319
|
+
if (!fullPath.startsWith(shellDist)) {
|
|
320
|
+
res.statusCode = 403;
|
|
321
|
+
res.end("Forbidden");
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
try {
|
|
325
|
+
const stat = await fsp2.stat(fullPath);
|
|
326
|
+
if (!stat.isFile()) throw new Error("not a file");
|
|
327
|
+
const ext = path.extname(fullPath);
|
|
328
|
+
const contentTypes = {
|
|
329
|
+
".html": "text/html",
|
|
330
|
+
".js": "application/javascript",
|
|
331
|
+
".css": "text/css",
|
|
332
|
+
".json": "application/json",
|
|
333
|
+
".svg": "image/svg+xml",
|
|
334
|
+
".png": "image/png",
|
|
335
|
+
".ico": "image/x-icon"
|
|
336
|
+
};
|
|
337
|
+
res.setHeader("Content-Type", contentTypes[ext] || "application/octet-stream");
|
|
338
|
+
const data = await fsp2.readFile(fullPath);
|
|
339
|
+
res.end(data);
|
|
340
|
+
} catch {
|
|
341
|
+
const indexPath = path.join(shellDist, "index.html");
|
|
342
|
+
try {
|
|
343
|
+
const html = await fsp2.readFile(indexPath, "utf-8");
|
|
344
|
+
res.setHeader("Content-Type", "text/html");
|
|
345
|
+
res.end(html);
|
|
346
|
+
} catch {
|
|
347
|
+
res.statusCode = 404;
|
|
348
|
+
res.end("Annotask shell not built. Run: pnpm build:shell");
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// src/server/state.ts
|
|
355
|
+
import fs from "fs";
|
|
356
|
+
import fsp3 from "fs/promises";
|
|
357
|
+
import path2 from "path";
|
|
358
|
+
var DEFAULT_DESIGN_SPEC = {
|
|
359
|
+
initialized: false,
|
|
360
|
+
version: "1.0",
|
|
361
|
+
framework: null,
|
|
362
|
+
colors: [],
|
|
363
|
+
typography: { families: [], scale: [], weights: [] },
|
|
364
|
+
spacing: [],
|
|
365
|
+
borders: { radius: [] },
|
|
366
|
+
icons: null,
|
|
367
|
+
components: null
|
|
368
|
+
};
|
|
369
|
+
async function atomicWrite(filePath, data) {
|
|
370
|
+
const dir = path2.dirname(filePath);
|
|
371
|
+
await fsp3.mkdir(dir, { recursive: true });
|
|
372
|
+
const tmpPath = filePath + `.tmp.${process.pid}.${Date.now()}`;
|
|
373
|
+
await fsp3.writeFile(tmpPath, data, "utf-8");
|
|
374
|
+
await fsp3.rename(tmpPath, filePath);
|
|
375
|
+
}
|
|
376
|
+
function createProjectState(projectRoot, broadcast) {
|
|
377
|
+
let cachedDesignSpec = null;
|
|
378
|
+
let specWatcher = null;
|
|
379
|
+
const tasksPath = path2.join(projectRoot, ".annotask", "tasks.json");
|
|
380
|
+
let taskCache = null;
|
|
381
|
+
let writeQueue = Promise.resolve();
|
|
382
|
+
function loadTasksSync() {
|
|
383
|
+
if (taskCache) return taskCache;
|
|
384
|
+
try {
|
|
385
|
+
taskCache = JSON.parse(fs.readFileSync(tasksPath, "utf-8"));
|
|
386
|
+
} catch {
|
|
387
|
+
taskCache = { version: "1.0", tasks: [] };
|
|
388
|
+
}
|
|
389
|
+
return taskCache;
|
|
390
|
+
}
|
|
391
|
+
function flushTasks() {
|
|
392
|
+
const data = taskCache;
|
|
393
|
+
if (!data) return;
|
|
394
|
+
writeQueue = writeQueue.then(() => atomicWrite(tasksPath, JSON.stringify(data, null, 2))).catch(() => {
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
function getDesignSpec() {
|
|
398
|
+
if (cachedDesignSpec !== null) return cachedDesignSpec;
|
|
399
|
+
const specPath = path2.join(projectRoot, ".annotask", "design-spec.json");
|
|
400
|
+
try {
|
|
401
|
+
cachedDesignSpec = { initialized: true, ...JSON.parse(fs.readFileSync(specPath, "utf-8")) };
|
|
402
|
+
} catch {
|
|
403
|
+
cachedDesignSpec = DEFAULT_DESIGN_SPEC;
|
|
404
|
+
}
|
|
405
|
+
if (!specWatcher) {
|
|
406
|
+
const configDir = path2.join(projectRoot, ".annotask");
|
|
407
|
+
try {
|
|
408
|
+
if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true });
|
|
409
|
+
specWatcher = fs.watch(configDir, (_, filename) => {
|
|
410
|
+
cachedDesignSpec = null;
|
|
411
|
+
if (filename === "design-spec.json") broadcast("designspec:updated", null);
|
|
412
|
+
if (filename === "tasks.json") {
|
|
413
|
+
taskCache = null;
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
} catch {
|
|
417
|
+
cachedDesignSpec = null;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return cachedDesignSpec ?? DEFAULT_DESIGN_SPEC;
|
|
421
|
+
}
|
|
422
|
+
function getConfig() {
|
|
423
|
+
const spec = getDesignSpec();
|
|
424
|
+
return { initialized: !!spec?.initialized, ...spec };
|
|
425
|
+
}
|
|
426
|
+
function addTask(task) {
|
|
427
|
+
const data = loadTasksSync();
|
|
428
|
+
const id = `task-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
429
|
+
const newTask = { id, status: "pending", createdAt: Date.now(), updatedAt: Date.now(), ...task };
|
|
430
|
+
data.tasks.push(newTask);
|
|
431
|
+
flushTasks();
|
|
432
|
+
broadcast("tasks:updated", data);
|
|
433
|
+
return newTask;
|
|
434
|
+
}
|
|
435
|
+
function updateTask(id, updates) {
|
|
436
|
+
const data = loadTasksSync();
|
|
437
|
+
const task = data.tasks.find((t) => t.id === id);
|
|
438
|
+
if (!task) return { error: "Task not found" };
|
|
439
|
+
Object.assign(task, updates, { updatedAt: Date.now() });
|
|
440
|
+
if (updates.status === "accepted") {
|
|
441
|
+
if (task.screenshot) {
|
|
442
|
+
const screenshotPath = path2.join(projectRoot, ".annotask", "screenshots", task.screenshot);
|
|
443
|
+
fsp3.unlink(screenshotPath).catch(() => {
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
data.tasks = data.tasks.filter((t) => t.id !== id);
|
|
447
|
+
}
|
|
448
|
+
flushTasks();
|
|
449
|
+
broadcast("tasks:updated", data);
|
|
450
|
+
return task;
|
|
451
|
+
}
|
|
452
|
+
function dispose() {
|
|
453
|
+
if (specWatcher) {
|
|
454
|
+
specWatcher.close();
|
|
455
|
+
specWatcher = null;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return { getDesignSpec, getConfig, getTasks: loadTasksSync, addTask, updateTask, dispose };
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/server/index.ts
|
|
462
|
+
function createAnnotaskServer(options) {
|
|
463
|
+
const wsServer = createWSServer();
|
|
464
|
+
const state = createProjectState(options.projectRoot, wsServer.broadcast);
|
|
465
|
+
const apiMiddleware = createAPIMiddleware({
|
|
466
|
+
projectRoot: options.projectRoot,
|
|
467
|
+
getReport: () => wsServer.getReport(),
|
|
468
|
+
getConfig: () => state.getConfig(),
|
|
469
|
+
getDesignSpec: () => state.getDesignSpec(),
|
|
470
|
+
getTasks: () => state.getTasks(),
|
|
471
|
+
addTask: (task) => state.addTask(task),
|
|
472
|
+
updateTask: (id, updates) => state.updateTask(id, updates)
|
|
473
|
+
});
|
|
474
|
+
const shellMiddleware = createShellMiddleware();
|
|
475
|
+
const middleware = (req, res, next) => {
|
|
476
|
+
apiMiddleware(req, res, () => {
|
|
477
|
+
shellMiddleware(req, res, next);
|
|
478
|
+
});
|
|
479
|
+
};
|
|
480
|
+
return {
|
|
481
|
+
middleware,
|
|
482
|
+
handleUpgrade: (req, socket, head) => wsServer.handleUpgrade(req, socket, head),
|
|
483
|
+
broadcast: (event, data) => wsServer.broadcast(event, data),
|
|
484
|
+
getReport: () => wsServer.getReport(),
|
|
485
|
+
dispose: () => state.dispose()
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
export {
|
|
490
|
+
createAPIMiddleware,
|
|
491
|
+
createWSServer,
|
|
492
|
+
createShellMiddleware,
|
|
493
|
+
createProjectState,
|
|
494
|
+
createAnnotaskServer
|
|
495
|
+
};
|
|
496
|
+
//# sourceMappingURL=chunk-TBBJTSMJ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/api.ts","../src/server/ws-server.ts","../src/server/serve-shell.ts","../src/server/state.ts","../src/server/index.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from 'node:http'\nimport fs from 'node:fs'\nimport fsp from 'node:fs/promises'\nimport nodePath from 'node:path'\n\nexport interface APIOptions {\n projectRoot: string\n getReport: () => unknown\n getConfig: () => unknown\n getDesignSpec: () => unknown\n getTasks: () => { version: string; tasks: any[] }\n updateTask: (id: string, updates: Record<string, unknown>) => unknown\n addTask: (task: Record<string, unknown>) => unknown\n}\n\nconst MAX_BODY_SIZE = 4_194_304\nconst VALID_TASK_STATUSES = new Set(['pending', 'applied', 'review', 'accepted', 'denied'])\n\n/** Fields that PATCH /tasks/:id is allowed to update */\nconst PATCHABLE_TASK_FIELDS = new Set([\n 'status', 'description', 'notes', 'screenshot', 'feedback',\n 'intent', 'action', 'context', 'viewport', 'interaction_history',\n 'element_context', 'mfe',\n])\n\nfunction readBody(req: IncomingMessage): Promise<string> {\n return new Promise((resolve, reject) => {\n let body = ''\n let size = 0\n req.on('data', (chunk: Buffer) => {\n size += chunk.length\n if (size > MAX_BODY_SIZE) { req.destroy(); reject(new Error('Request body too large')); return }\n body += chunk.toString()\n })\n req.on('end', () => resolve(body))\n req.on('error', reject)\n })\n}\n\nfunction parseJSON(raw: string): { ok: true; data: unknown } | { ok: false } {\n try { return { ok: true, data: JSON.parse(raw) } } catch { return { ok: false } }\n}\n\nfunction sendError(res: ServerResponse, status: number, message: string) {\n res.statusCode = status\n res.end(JSON.stringify({ error: message }))\n}\n\n/** Check if an Origin header is from localhost */\nfunction isLocalOrigin(origin: string | undefined): boolean {\n if (!origin) return true // same-origin requests (no Origin header)\n try {\n const url = new URL(origin)\n const host = url.hostname\n return host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1'\n } catch {\n return false\n }\n}\n\n/** Build CORS origin value — reflect the request origin if it's local, otherwise omit */\nfunction getCorsOrigin(req: IncomingMessage): string | null {\n const origin = req.headers.origin as string | undefined\n if (!origin) return null // same-origin, no CORS header needed\n return isLocalOrigin(origin) ? origin : null\n}\n\nexport function createAPIMiddleware(options: APIOptions) {\n return async (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n // Serve screenshots (outside /api/ path)\n if (req.url?.startsWith('/__annotask/screenshots/') && req.method === 'GET') {\n const filename = (req.url.replace('/__annotask/screenshots/', '')).replace(/\\?.*$/, '')\n if (!/^[a-zA-Z0-9_-]+\\.png$/.test(filename)) {\n res.statusCode = 400; res.end('Invalid filename'); return\n }\n const filePath = nodePath.join(options.projectRoot, '.annotask', 'screenshots', filename)\n try {\n const data = await fsp.readFile(filePath)\n res.setHeader('Content-Type', 'image/png')\n res.setHeader('Cache-Control', 'public, max-age=3600')\n const corsOrigin = getCorsOrigin(req)\n if (corsOrigin) res.setHeader('Access-Control-Allow-Origin', corsOrigin)\n res.end(data)\n } catch {\n res.statusCode = 404; res.end('Not found')\n }\n return\n }\n\n if (!req.url?.startsWith('/__annotask/api/')) return next()\n\n const path = req.url.replace('/__annotask/api/', '')\n\n // CORS — only allow localhost origins\n const corsOrigin = getCorsOrigin(req)\n res.setHeader('Content-Type', 'application/json')\n if (corsOrigin) {\n res.setHeader('Access-Control-Allow-Origin', corsOrigin)\n res.setHeader('Vary', 'Origin')\n }\n res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PATCH, OPTIONS')\n res.setHeader('Access-Control-Allow-Headers', 'Content-Type')\n res.setHeader('Cache-Control', 'no-cache')\n\n if (req.method === 'OPTIONS') { res.statusCode = 200; res.end(); return }\n\n // Block mutating requests from non-local origins\n if ((req.method === 'POST' || req.method === 'PATCH') && !isLocalOrigin(req.headers.origin as string | undefined)) {\n return sendError(res, 403, 'Forbidden: non-local origin')\n }\n\n if (path === 'report' && req.method === 'GET') {\n const report = options.getReport() as any ?? { version: '1.0', changes: [] }\n const urlObj = new URL(req.url!, `http://${req.headers.host || 'localhost'}`)\n const mfeFilter = urlObj.searchParams.get('mfe')\n if (mfeFilter && report.changes) {\n const filtered = { ...report, changes: report.changes.filter((c: any) => c.mfe === mfeFilter) }\n res.end(JSON.stringify(filtered, null, 2))\n } else {\n res.end(JSON.stringify(report, null, 2))\n }\n return\n }\n\n if (path === 'config' && req.method === 'GET') {\n res.end(JSON.stringify(options.getConfig(), null, 2))\n return\n }\n\n if (path === 'design-spec' && req.method === 'GET') {\n res.end(JSON.stringify(options.getDesignSpec(), null, 2))\n return\n }\n\n if (path.startsWith('tasks') && !path.startsWith('tasks/') && req.method === 'GET') {\n const urlObj = new URL(req.url!, `http://${req.headers.host || 'localhost'}`)\n const mfeFilter = urlObj.searchParams.get('mfe')\n const taskData = options.getTasks()\n if (mfeFilter) {\n const filtered = { ...taskData, tasks: taskData.tasks.filter((t: any) => t.mfe === mfeFilter) }\n res.end(JSON.stringify(filtered, null, 2))\n } else {\n res.end(JSON.stringify(taskData, null, 2))\n }\n return\n }\n\n if (path === 'tasks' && req.method === 'POST') {\n let raw: string\n try { raw = await readBody(req) } catch { return sendError(res, 413, 'Request body too large') }\n const parsed = parseJSON(raw)\n if (!parsed.ok) return sendError(res, 400, 'Invalid JSON body')\n const body = parsed.data as Record<string, unknown>\n if (!body || typeof body !== 'object' || Array.isArray(body)) return sendError(res, 400, 'Request body must be a JSON object')\n if (typeof body.type !== 'string' || !body.type) return sendError(res, 400, 'Missing required field: type (string)')\n if (typeof body.description !== 'string') return sendError(res, 400, 'Missing required field: description (string)')\n res.end(JSON.stringify(options.addTask(body), null, 2))\n return\n }\n\n if (path === 'screenshots' && req.method === 'POST') {\n let raw: string\n try { raw = await readBody(req) } catch { return sendError(res, 413, 'Request body too large') }\n const parsed = parseJSON(raw)\n if (!parsed.ok) return sendError(res, 400, 'Invalid JSON body')\n const body = parsed.data as { data: string }\n if (!body.data || typeof body.data !== 'string') return sendError(res, 400, 'Missing data field')\n const match = body.data.match(/^data:image\\/png;base64,(.+)$/)\n if (!match) return sendError(res, 400, 'Invalid PNG data URL')\n const buffer = Buffer.from(match[1], 'base64')\n if (buffer.length > 4 * 1024 * 1024) return sendError(res, 413, 'Screenshot too large (max 4MB)')\n const filename = `screenshot-${Date.now()}-${Math.random().toString(36).slice(2, 7)}.png`\n const dir = nodePath.join(options.projectRoot, '.annotask', 'screenshots')\n await fsp.mkdir(dir, { recursive: true })\n await fsp.writeFile(nodePath.join(dir, filename), buffer)\n res.end(JSON.stringify({ filename }))\n return\n }\n\n if (path.startsWith('tasks/') && req.method === 'PATCH') {\n const id = path.replace('tasks/', '')\n let raw: string\n try { raw = await readBody(req) } catch { return sendError(res, 413, 'Request body too large') }\n const parsed = parseJSON(raw)\n if (!parsed.ok) return sendError(res, 400, 'Invalid JSON body')\n const body = parsed.data as Record<string, unknown>\n if (!body || typeof body !== 'object' || Array.isArray(body)) return sendError(res, 400, 'Request body must be a JSON object')\n if (body.status !== undefined && !VALID_TASK_STATUSES.has(body.status as string)) {\n return sendError(res, 400, `Invalid status. Must be one of: ${[...VALID_TASK_STATUSES].join(', ')}`)\n }\n // Strip unknown fields — only allow whitelisted fields through\n const sanitized: Record<string, unknown> = {}\n for (const key of Object.keys(body)) {\n if (PATCHABLE_TASK_FIELDS.has(key)) {\n sanitized[key] = body[key]\n }\n }\n res.end(JSON.stringify(options.updateTask(id, sanitized), null, 2))\n return\n }\n\n if (path === 'status' && req.method === 'GET') {\n res.end(JSON.stringify({ status: 'ok', tool: 'annotask' }))\n return\n }\n\n res.statusCode = 404\n res.end(JSON.stringify({ error: 'Not found' }))\n }\n}\n","import { WebSocketServer, WebSocket } from 'ws'\nimport type { IncomingMessage } from 'node:http'\nimport type { Duplex } from 'node:stream'\n\nexport interface AnnotaskWSServer {\n handleUpgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => void\n broadcast: (event: string, data: unknown) => void\n getReport: () => unknown\n clients: Set<WebSocket>\n}\n\nexport function createWSServer(): AnnotaskWSServer {\n let currentReport: unknown = null\n const clients = new Set<WebSocket>()\n const wss = new WebSocketServer({ noServer: true })\n\n wss.on('connection', (ws) => {\n clients.add(ws)\n if (currentReport) {\n ws.send(JSON.stringify({ event: 'report:current', data: currentReport, timestamp: Date.now() }))\n }\n\n ws.on('message', (raw) => {\n try {\n const msg = JSON.parse(raw.toString())\n if (msg.event === 'report:updated') {\n currentReport = msg.data\n for (const client of clients) {\n if (client !== ws && client.readyState === WebSocket.OPEN) {\n client.send(JSON.stringify({ event: 'report:updated', data: msg.data, timestamp: Date.now() }))\n }\n }\n }\n if (msg.event === 'changes:cleared') {\n currentReport = null\n for (const client of clients) {\n if (client !== ws && client.readyState === WebSocket.OPEN) {\n client.send(JSON.stringify({ event: 'changes:cleared', data: null, timestamp: Date.now() }))\n }\n }\n }\n if (msg.event === 'get:report') {\n ws.send(JSON.stringify({ event: 'report:current', data: currentReport, timestamp: Date.now() }))\n }\n } catch {}\n })\n\n ws.on('close', () => { clients.delete(ws) })\n })\n\n return {\n handleUpgrade(req, socket, head) {\n wss.handleUpgrade(req, socket, head, (ws) => { wss.emit('connection', ws, req) })\n },\n broadcast(event, data) {\n const msg = JSON.stringify({ event, data, timestamp: Date.now() })\n for (const client of clients) {\n if (client.readyState === WebSocket.OPEN) client.send(msg)\n }\n },\n getReport() { return currentReport },\n clients,\n }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url))\n\nfunction findShellDist(): string {\n return path.resolve(__dirname, 'shell')\n}\n\nfunction findVendorDist(): string {\n return path.resolve(__dirname, 'vendor')\n}\n\nexport function createShellMiddleware() {\n const shellDist = findShellDist()\n const vendorDist = findVendorDist()\n\n return async (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n if (!req.url?.startsWith('/__annotask')) return next()\n\n // CORS for cross-port access (Webpack standalone server) — localhost only\n const origin = req.headers.origin as string | undefined\n if (origin) {\n try {\n const host = new URL(origin).hostname\n if (host === 'localhost' || host === '127.0.0.1' || host === '[::1]' || host === '::1') {\n res.setHeader('Access-Control-Allow-Origin', origin)\n res.setHeader('Vary', 'Origin')\n }\n } catch { /* invalid origin, skip CORS */ }\n }\n\n let filePath = req.url.replace('/__annotask', '') || '/'\n const queryIndex = filePath.indexOf('?')\n if (queryIndex !== -1) filePath = filePath.slice(0, queryIndex)\n if (filePath === '/' || filePath === '') filePath = '/index.html'\n\n // Skip API and WS paths\n if (filePath.startsWith('/api/') || filePath === '/ws') return next()\n\n // Serve vendor files (axe-core, html2canvas)\n if (filePath.startsWith('/vendor/')) {\n const vendorFile = path.join(vendorDist, filePath.replace('/vendor/', ''))\n if (!vendorFile.startsWith(vendorDist)) {\n res.statusCode = 403; res.end('Forbidden'); return\n }\n try {\n const data = await fsp.readFile(vendorFile)\n res.setHeader('Content-Type', 'application/javascript')\n res.setHeader('Cache-Control', 'public, max-age=86400')\n res.end(data)\n } catch {\n res.statusCode = 404; res.end('Vendor file not found'); return\n }\n return\n }\n\n const fullPath = path.join(shellDist, filePath)\n\n if (!fullPath.startsWith(shellDist)) {\n res.statusCode = 403\n res.end('Forbidden')\n return\n }\n\n try {\n const stat = await fsp.stat(fullPath)\n if (!stat.isFile()) throw new Error('not a file')\n const ext = path.extname(fullPath)\n const contentTypes: Record<string, string> = {\n '.html': 'text/html', '.js': 'application/javascript', '.css': 'text/css',\n '.json': 'application/json', '.svg': 'image/svg+xml', '.png': 'image/png', '.ico': 'image/x-icon',\n }\n res.setHeader('Content-Type', contentTypes[ext] || 'application/octet-stream')\n const data = await fsp.readFile(fullPath)\n res.end(data)\n } catch {\n // SPA fallback — serve index.html\n const indexPath = path.join(shellDist, 'index.html')\n try {\n const html = await fsp.readFile(indexPath, 'utf-8')\n res.setHeader('Content-Type', 'text/html')\n res.end(html)\n } catch {\n res.statusCode = 404\n res.end('Annotask shell not built. Run: pnpm build:shell')\n }\n }\n }\n}\n","import fs from 'node:fs'\nimport fsp from 'node:fs/promises'\nimport path from 'node:path'\nimport os from 'node:os'\n\nconst DEFAULT_DESIGN_SPEC = {\n initialized: false,\n version: '1.0' as const,\n framework: null,\n colors: [],\n typography: { families: [], scale: [], weights: [] },\n spacing: [],\n borders: { radius: [] },\n icons: null,\n components: null,\n}\n\nexport interface ProjectState {\n getDesignSpec: () => unknown\n getConfig: () => unknown\n getTasks: () => { version: string; tasks: any[] }\n addTask: (task: Record<string, unknown>) => unknown\n updateTask: (id: string, updates: Record<string, unknown>) => unknown\n dispose: () => void\n}\n\n/** Atomic write: write to tmp file then rename into place */\nasync function atomicWrite(filePath: string, data: string) {\n const dir = path.dirname(filePath)\n await fsp.mkdir(dir, { recursive: true })\n const tmpPath = filePath + `.tmp.${process.pid}.${Date.now()}`\n await fsp.writeFile(tmpPath, data, 'utf-8')\n await fsp.rename(tmpPath, filePath)\n}\n\nexport function createProjectState(projectRoot: string, broadcast: (event: string, data: unknown) => void): ProjectState {\n let cachedDesignSpec: unknown = null\n let specWatcher: fs.FSWatcher | null = null\n const tasksPath = path.join(projectRoot, '.annotask', 'tasks.json')\n\n // In-memory task cache — loaded once from disk, written back atomically on mutation\n let taskCache: { version: string; tasks: any[] } | null = null\n let writeQueue: Promise<void> = Promise.resolve()\n\n function loadTasksSync(): { version: string; tasks: any[] } {\n if (taskCache) return taskCache\n try {\n taskCache = JSON.parse(fs.readFileSync(tasksPath, 'utf-8'))\n } catch {\n taskCache = { version: '1.0', tasks: [] }\n }\n return taskCache!\n }\n\n /** Queue an atomic write so concurrent mutations don't race */\n function flushTasks() {\n const data = taskCache\n if (!data) return\n writeQueue = writeQueue\n .then(() => atomicWrite(tasksPath, JSON.stringify(data, null, 2)))\n .catch(() => { /* write errors are non-fatal for the in-memory state */ })\n }\n\n function getDesignSpec(): unknown {\n if (cachedDesignSpec !== null) return cachedDesignSpec\n const specPath = path.join(projectRoot, '.annotask', 'design-spec.json')\n try {\n cachedDesignSpec = { initialized: true, ...JSON.parse(fs.readFileSync(specPath, 'utf-8')) }\n } catch {\n cachedDesignSpec = DEFAULT_DESIGN_SPEC\n }\n if (!specWatcher) {\n const configDir = path.join(projectRoot, '.annotask')\n try {\n if (!fs.existsSync(configDir)) fs.mkdirSync(configDir, { recursive: true })\n specWatcher = fs.watch(configDir, (_, filename) => {\n cachedDesignSpec = null\n if (filename === 'design-spec.json') broadcast('designspec:updated', null)\n if (filename === 'tasks.json') {\n // External edit — reload from disk\n taskCache = null\n }\n })\n } catch { cachedDesignSpec = null }\n }\n return cachedDesignSpec ?? DEFAULT_DESIGN_SPEC\n }\n\n function getConfig(): unknown {\n const spec = getDesignSpec() as any\n return { initialized: !!spec?.initialized, ...spec }\n }\n\n function addTask(task: Record<string, unknown>) {\n const data = loadTasksSync()\n const id = `task-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`\n const newTask = { id, status: 'pending', createdAt: Date.now(), updatedAt: Date.now(), ...task }\n data.tasks.push(newTask)\n flushTasks()\n broadcast('tasks:updated', data)\n return newTask\n }\n\n function updateTask(id: string, updates: Record<string, unknown>) {\n const data = loadTasksSync()\n const task = data.tasks.find((t: any) => t.id === id)\n if (!task) return { error: 'Task not found' }\n Object.assign(task, updates, { updatedAt: Date.now() })\n if (updates.status === 'accepted') {\n if (task.screenshot) {\n const screenshotPath = path.join(projectRoot, '.annotask', 'screenshots', task.screenshot)\n fsp.unlink(screenshotPath).catch(() => {})\n }\n data.tasks = data.tasks.filter((t: any) => t.id !== id)\n }\n flushTasks()\n broadcast('tasks:updated', data)\n return task\n }\n\n function dispose() {\n if (specWatcher) { specWatcher.close(); specWatcher = null }\n }\n\n return { getDesignSpec, getConfig, getTasks: loadTasksSync, addTask, updateTask, dispose }\n}\n","import type { IncomingMessage, ServerResponse } from 'node:http'\nimport type { Duplex } from 'node:stream'\nimport { createAPIMiddleware } from './api.js'\nimport { createWSServer, type AnnotaskWSServer } from './ws-server.js'\nimport { createShellMiddleware } from './serve-shell.js'\nimport { createProjectState, type ProjectState } from './state.js'\n\nexport interface AnnotaskServer {\n middleware: (req: IncomingMessage, res: ServerResponse, next: () => void) => void\n handleUpgrade: (req: IncomingMessage, socket: Duplex, head: Buffer) => void\n broadcast: (event: string, data: unknown) => void\n getReport: () => unknown\n dispose: () => void\n}\n\nexport interface AnnotaskServerOptions {\n projectRoot: string\n}\n\nexport function createAnnotaskServer(options: AnnotaskServerOptions): AnnotaskServer {\n const wsServer = createWSServer()\n const state = createProjectState(options.projectRoot, wsServer.broadcast)\n\n const apiMiddleware = createAPIMiddleware({\n projectRoot: options.projectRoot,\n getReport: () => wsServer.getReport(),\n getConfig: () => state.getConfig(),\n getDesignSpec: () => state.getDesignSpec(),\n getTasks: () => state.getTasks(),\n addTask: (task) => state.addTask(task),\n updateTask: (id, updates) => state.updateTask(id, updates),\n })\n\n const shellMiddleware = createShellMiddleware()\n\n const middleware = (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n // API first, then shell (shell is SPA fallback)\n apiMiddleware(req, res, () => {\n shellMiddleware(req, res, next)\n })\n }\n\n return {\n middleware,\n handleUpgrade: (req, socket, head) => wsServer.handleUpgrade(req, socket, head),\n broadcast: (event, data) => wsServer.broadcast(event, data),\n getReport: () => wsServer.getReport(),\n dispose: () => state.dispose(),\n }\n}\n\nexport { createProjectState, type ProjectState } from './state.js'\nexport { createWSServer, type AnnotaskWSServer } from './ws-server.js'\nexport { createAPIMiddleware, type APIOptions } from './api.js'\nexport { createShellMiddleware } from './serve-shell.js'\n"],"mappings":"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","names":["corsOrigin","path","fsp","fsp","path"]}
|
package/dist/cli.js
CHANGED
|
@@ -127,7 +127,7 @@ function watchChanges() {
|
|
|
127
127
|
async function fetchReport() {
|
|
128
128
|
try {
|
|
129
129
|
const tasksUrl = mfeFilter ? `${apiUrl}/tasks?mfe=${encodeURIComponent(mfeFilter)}` : `${apiUrl}/tasks`;
|
|
130
|
-
const reportUrl = `${apiUrl}/report`;
|
|
130
|
+
const reportUrl = mfeFilter ? `${apiUrl}/report?mfe=${encodeURIComponent(mfeFilter)}` : `${apiUrl}/report`;
|
|
131
131
|
const [tasksRes, reportRes] = await Promise.all([
|
|
132
132
|
fetch(tasksUrl),
|
|
133
133
|
fetch(reportUrl)
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
bridgeClientScript
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-2PL37QTY.js";
|
|
4
4
|
import {
|
|
5
5
|
writeMfeServerInfo,
|
|
6
6
|
writeServerInfo
|
|
7
7
|
} from "./chunk-XLNGAH3S.js";
|
|
8
8
|
import {
|
|
9
9
|
createAnnotaskServer
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-TBBJTSMJ.js";
|
|
11
11
|
import {
|
|
12
12
|
transformFile,
|
|
13
13
|
transformHTML
|
package/dist/server.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ interface APIOptions {
|
|
|
37
37
|
}
|
|
38
38
|
declare function createAPIMiddleware(options: APIOptions): (req: IncomingMessage, res: ServerResponse, next: () => void) => Promise<void>;
|
|
39
39
|
|
|
40
|
-
declare function createShellMiddleware(): (req: IncomingMessage, res: ServerResponse, next: () => void) => void
|
|
40
|
+
declare function createShellMiddleware(): (req: IncomingMessage, res: ServerResponse, next: () => void) => Promise<void>;
|
|
41
41
|
|
|
42
42
|
interface AnnotaskServer {
|
|
43
43
|
middleware: (req: IncomingMessage, res: ServerResponse, next: () => void) => void;
|