annotask 0.0.0 → 0.0.1
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 +40 -2
- package/dist/chunk-GHDLRMQG.js +61 -0
- package/dist/chunk-GHDLRMQG.js.map +1 -0
- package/dist/chunk-JLOSPIJ4.js +340 -0
- package/dist/chunk-JLOSPIJ4.js.map +1 -0
- package/dist/chunk-JPNMDGZN.js +61 -0
- package/dist/chunk-JPNMDGZN.js.map +1 -0
- package/dist/chunk-R6P4MMZW.js +340 -0
- package/dist/chunk-R6P4MMZW.js.map +1 -0
- package/dist/chunk-VI4NPM6C.js +888 -0
- package/dist/chunk-VI4NPM6C.js.map +1 -0
- package/dist/chunk-VU7Z7EZA.js +362 -0
- package/dist/chunk-VU7Z7EZA.js.map +1 -0
- package/dist/chunk-XLNGAH3S.js +29 -0
- package/dist/chunk-XLNGAH3S.js.map +1 -0
- package/dist/cli.js +38 -10
- package/dist/index.d.ts +11 -0
- package/dist/index.js +17 -6
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +4 -1
- package/dist/server.js +1 -1
- package/dist/shell/assets/{index-BD3nZNWX.js → index-BcsdXOsJ.js} +25 -25
- package/dist/shell/index.html +1 -1
- package/dist/standalone.js +3 -3
- package/dist/webpack-loader.js +1 -1
- package/dist/webpack.js +4 -4
- package/package.json +2 -1
- package/skills/annotask-apply/SKILL.md +8 -0
- package/skills/annotask-watch/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -100,6 +100,42 @@ import { AnnotaskWebpackPlugin } from 'annotask/webpack'
|
|
|
100
100
|
plugins: [new AnnotaskWebpackPlugin()]
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
+
### Micro-frontends (single-spa, Module Federation, etc.)
|
|
104
|
+
|
|
105
|
+
For MFE architectures where multiple apps load into a single root shell:
|
|
106
|
+
|
|
107
|
+
**MFE child** (Vite) — adds `data-annotask-mfe` attribute to all elements:
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import { annotask } from 'annotask'
|
|
111
|
+
|
|
112
|
+
export default defineConfig({
|
|
113
|
+
plugins: [
|
|
114
|
+
vue(),
|
|
115
|
+
annotask({
|
|
116
|
+
mfe: '@myorg/my-mfe', // MFE identity tag
|
|
117
|
+
server: 'http://localhost:24678', // Root's annotask server URL
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
})
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Root shell** (Webpack) — runs the annotask server, bridge, and shell UI:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { AnnotaskWebpackPlugin } from 'annotask/webpack'
|
|
127
|
+
|
|
128
|
+
plugins: [new AnnotaskWebpackPlugin({ port: 24678 })]
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When `server` is set, the MFE's local annotask server is skipped — the root handles it. When only `mfe` is set (no `server`), annotask runs normally for standalone development.
|
|
132
|
+
|
|
133
|
+
Tasks created from MFE elements carry the `mfe` field. Filter them with `GET /__annotask/api/tasks?mfe=@myorg/my-mfe` or use the CLI:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
annotask report --mfe=@myorg/my-mfe
|
|
137
|
+
```
|
|
138
|
+
|
|
103
139
|
Start your dev server, then open:
|
|
104
140
|
- **App**: `http://localhost:5173/` (Vite) or `http://localhost:8090/` (Webpack)
|
|
105
141
|
- **Annotask**: `http://localhost:5173/__annotask/`
|
|
@@ -134,10 +170,12 @@ annotask status # Check connection
|
|
|
134
170
|
annotask init-skills # Install agent skills into your project
|
|
135
171
|
```
|
|
136
172
|
|
|
173
|
+
Options: `--port=N`, `--host=H`, `--server=URL` (override server.json), `--mfe=NAME` (filter by MFE).
|
|
174
|
+
|
|
137
175
|
## API
|
|
138
176
|
|
|
139
177
|
- `GET /__annotask/api/report` — Current change report
|
|
140
|
-
- `GET /__annotask/api/tasks` — Task list
|
|
178
|
+
- `GET /__annotask/api/tasks` — Task list (supports `?mfe=NAME` filter)
|
|
141
179
|
- `POST /__annotask/api/tasks` — Create a task
|
|
142
180
|
- `PATCH /__annotask/api/tasks/:id` — Update task status
|
|
143
181
|
- `GET /__annotask/api/status` — Health check
|
|
@@ -180,7 +218,7 @@ pnpm test:e2e # Run E2E tests (all frameworks)
|
|
|
180
218
|
- `src/schema.ts` — TypeScript types for change reports
|
|
181
219
|
- `src/cli/` — CLI tool for terminal interaction
|
|
182
220
|
- `skills/` — Agent skill definitions (shipped with the npm package)
|
|
183
|
-
- `playgrounds/` — Test apps (vue-vite, vue-webpack, react-vite, svelte-vite, html-vite, astro, htmx-vite)
|
|
221
|
+
- `playgrounds/` — Test apps (vue-vite, vue-webpack, react-vite, svelte-vite, html-vite, astro, htmx-vite, mfe-vite)
|
|
184
222
|
|
|
185
223
|
## Troubleshooting
|
|
186
224
|
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
removeServerInfo,
|
|
3
|
+
writeServerInfo
|
|
4
|
+
} from "./chunk-XLNGAH3S.js";
|
|
5
|
+
import {
|
|
6
|
+
createAnnotaskServer
|
|
7
|
+
} from "./chunk-R6P4MMZW.js";
|
|
8
|
+
|
|
9
|
+
// src/server/standalone.ts
|
|
10
|
+
import http from "http";
|
|
11
|
+
async function startStandaloneServer(options) {
|
|
12
|
+
const port = options.port || 24678;
|
|
13
|
+
const uiServer = createAnnotaskServer({ projectRoot: options.projectRoot });
|
|
14
|
+
const httpServer = http.createServer((req, res) => {
|
|
15
|
+
uiServer.middleware(req, res, () => {
|
|
16
|
+
res.statusCode = 404;
|
|
17
|
+
res.end("Not found");
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
21
|
+
if (req.url === "/__annotask/ws") {
|
|
22
|
+
uiServer.handleUpgrade(req, socket, head);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
httpServer.on("error", (err) => {
|
|
27
|
+
if (err.code === "EADDRINUSE") {
|
|
28
|
+
httpServer.listen(0, () => {
|
|
29
|
+
const addr = httpServer.address();
|
|
30
|
+
writeServerInfo(options.projectRoot, addr.port);
|
|
31
|
+
resolve({
|
|
32
|
+
port: addr.port,
|
|
33
|
+
close: () => {
|
|
34
|
+
removeServerInfo(options.projectRoot);
|
|
35
|
+
uiServer.dispose();
|
|
36
|
+
httpServer.close();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
reject(err);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
httpServer.listen(port, () => {
|
|
45
|
+
writeServerInfo(options.projectRoot, port);
|
|
46
|
+
resolve({
|
|
47
|
+
port,
|
|
48
|
+
close: () => {
|
|
49
|
+
removeServerInfo(options.projectRoot);
|
|
50
|
+
uiServer.dispose();
|
|
51
|
+
httpServer.close();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
startStandaloneServer
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=chunk-GHDLRMQG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/standalone.ts"],"sourcesContent":["import http from 'node:http'\nimport { createAnnotaskServer } from './index.js'\nimport { writeServerInfo, removeServerInfo } from './discovery.js'\n\nexport interface StandaloneServerOptions {\n projectRoot: string\n port?: number\n}\n\nexport async function startStandaloneServer(options: StandaloneServerOptions): Promise<{\n port: number\n close: () => void\n}> {\n const port = options.port || 24678\n const uiServer = createAnnotaskServer({ projectRoot: options.projectRoot })\n\n const httpServer = http.createServer((req, res) => {\n uiServer.middleware(req, res, () => {\n res.statusCode = 404\n res.end('Not found')\n })\n })\n\n httpServer.on('upgrade', (req, socket, head) => {\n if (req.url === '/__annotask/ws') {\n uiServer.handleUpgrade(req, socket, head)\n }\n })\n\n return new Promise((resolve, reject) => {\n httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n httpServer.listen(0, () => {\n const addr = httpServer.address() as { port: number }\n writeServerInfo(options.projectRoot, addr.port)\n resolve({\n port: addr.port,\n close: () => { removeServerInfo(options.projectRoot); uiServer.dispose(); httpServer.close() },\n })\n })\n } else {\n reject(err)\n }\n })\n\n httpServer.listen(port, () => {\n writeServerInfo(options.projectRoot, port)\n resolve({\n port,\n close: () => { removeServerInfo(options.projectRoot); uiServer.dispose(); httpServer.close() },\n })\n })\n })\n}\n"],"mappings":";;;;;;;;;AAAA,OAAO,UAAU;AASjB,eAAsB,sBAAsB,SAGzC;AACD,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,WAAW,qBAAqB,EAAE,aAAa,QAAQ,YAAY,CAAC;AAE1E,QAAM,aAAa,KAAK,aAAa,CAAC,KAAK,QAAQ;AACjD,aAAS,WAAW,KAAK,KAAK,MAAM;AAClC,UAAI,aAAa;AACjB,UAAI,IAAI,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,CAAC;AAED,aAAW,GAAG,WAAW,CAAC,KAAK,QAAQ,SAAS;AAC9C,QAAI,IAAI,QAAQ,kBAAkB;AAChC,eAAS,cAAc,KAAK,QAAQ,IAAI;AAAA,IAC1C;AAAA,EACF,CAAC;AAED,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,eAAW,GAAG,SAAS,CAAC,QAA+B;AACrD,UAAI,IAAI,SAAS,cAAc;AAC7B,mBAAW,OAAO,GAAG,MAAM;AACzB,gBAAM,OAAO,WAAW,QAAQ;AAChC,0BAAgB,QAAQ,aAAa,KAAK,IAAI;AAC9C,kBAAQ;AAAA,YACN,MAAM,KAAK;AAAA,YACX,OAAO,MAAM;AAAE,+BAAiB,QAAQ,WAAW;AAAG,uBAAS,QAAQ;AAAG,yBAAW,MAAM;AAAA,YAAE;AAAA,UAC/F,CAAC;AAAA,QACH,CAAC;AAAA,MACH,OAAO;AACL,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAED,eAAW,OAAO,MAAM,MAAM;AAC5B,sBAAgB,QAAQ,aAAa,IAAI;AACzC,cAAQ;AAAA,QACN;AAAA,QACA,OAAO,MAAM;AAAE,2BAAiB,QAAQ,WAAW;AAAG,mBAAS,QAAQ;AAAG,qBAAW,MAAM;AAAA,QAAE;AAAA,MAC/F,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
// src/server/api.ts
|
|
2
|
+
var MAX_BODY_SIZE = 1048576;
|
|
3
|
+
var VALID_TASK_STATUSES = /* @__PURE__ */ new Set(["pending", "applied", "review", "accepted", "denied"]);
|
|
4
|
+
function readBody(req) {
|
|
5
|
+
return new Promise((resolve, reject) => {
|
|
6
|
+
let body = "";
|
|
7
|
+
let size = 0;
|
|
8
|
+
req.on("data", (chunk) => {
|
|
9
|
+
size += chunk.length;
|
|
10
|
+
if (size > MAX_BODY_SIZE) {
|
|
11
|
+
req.destroy();
|
|
12
|
+
reject(new Error("Request body too large"));
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
body += chunk.toString();
|
|
16
|
+
});
|
|
17
|
+
req.on("end", () => resolve(body));
|
|
18
|
+
req.on("error", reject);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
function parseJSON(raw) {
|
|
22
|
+
try {
|
|
23
|
+
return { ok: true, data: JSON.parse(raw) };
|
|
24
|
+
} catch {
|
|
25
|
+
return { ok: false };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function sendError(res, status, message) {
|
|
29
|
+
res.statusCode = status;
|
|
30
|
+
res.end(JSON.stringify({ error: message }));
|
|
31
|
+
}
|
|
32
|
+
function createAPIMiddleware(options) {
|
|
33
|
+
return async (req, res, next) => {
|
|
34
|
+
if (!req.url?.startsWith("/__annotask/api/")) return next();
|
|
35
|
+
const path3 = req.url.replace("/__annotask/api/", "");
|
|
36
|
+
res.setHeader("Content-Type", "application/json");
|
|
37
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
38
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, POST, PATCH, OPTIONS");
|
|
39
|
+
res.setHeader("Access-Control-Allow-Headers", "Content-Type");
|
|
40
|
+
res.setHeader("Cache-Control", "no-cache");
|
|
41
|
+
if (req.method === "OPTIONS") {
|
|
42
|
+
res.statusCode = 200;
|
|
43
|
+
res.end();
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if (path3 === "report" && req.method === "GET") {
|
|
47
|
+
res.end(JSON.stringify(options.getReport() ?? { version: "1.0", changes: [] }, null, 2));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (path3 === "config" && req.method === "GET") {
|
|
51
|
+
res.end(JSON.stringify(options.getConfig(), null, 2));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (path3 === "design-spec" && req.method === "GET") {
|
|
55
|
+
res.end(JSON.stringify(options.getDesignSpec(), null, 2));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (path3.startsWith("tasks") && !path3.startsWith("tasks/") && req.method === "GET") {
|
|
59
|
+
const urlObj = new URL(req.url, `http://${req.headers.host || "localhost"}`);
|
|
60
|
+
const mfeFilter = urlObj.searchParams.get("mfe");
|
|
61
|
+
const taskData = options.getTasks();
|
|
62
|
+
if (mfeFilter) {
|
|
63
|
+
const filtered = { ...taskData, tasks: taskData.tasks.filter((t) => t.mfe === mfeFilter) };
|
|
64
|
+
res.end(JSON.stringify(filtered, null, 2));
|
|
65
|
+
} else {
|
|
66
|
+
res.end(JSON.stringify(taskData, null, 2));
|
|
67
|
+
}
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (path3 === "tasks" && req.method === "POST") {
|
|
71
|
+
let raw;
|
|
72
|
+
try {
|
|
73
|
+
raw = await readBody(req);
|
|
74
|
+
} catch {
|
|
75
|
+
return sendError(res, 413, "Request body too large");
|
|
76
|
+
}
|
|
77
|
+
const parsed = parseJSON(raw);
|
|
78
|
+
if (!parsed.ok) return sendError(res, 400, "Invalid JSON body");
|
|
79
|
+
const body = parsed.data;
|
|
80
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return sendError(res, 400, "Request body must be a JSON object");
|
|
81
|
+
if (typeof body.type !== "string" || !body.type) return sendError(res, 400, "Missing required field: type (string)");
|
|
82
|
+
if (typeof body.description !== "string") return sendError(res, 400, "Missing required field: description (string)");
|
|
83
|
+
res.end(JSON.stringify(options.addTask(body), null, 2));
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (path3.startsWith("tasks/") && req.method === "PATCH") {
|
|
87
|
+
const id = path3.replace("tasks/", "");
|
|
88
|
+
let raw;
|
|
89
|
+
try {
|
|
90
|
+
raw = await readBody(req);
|
|
91
|
+
} catch {
|
|
92
|
+
return sendError(res, 413, "Request body too large");
|
|
93
|
+
}
|
|
94
|
+
const parsed = parseJSON(raw);
|
|
95
|
+
if (!parsed.ok) return sendError(res, 400, "Invalid JSON body");
|
|
96
|
+
const body = parsed.data;
|
|
97
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) return sendError(res, 400, "Request body must be a JSON object");
|
|
98
|
+
if (body.status !== void 0 && !VALID_TASK_STATUSES.has(body.status)) {
|
|
99
|
+
return sendError(res, 400, `Invalid status. Must be one of: ${[...VALID_TASK_STATUSES].join(", ")}`);
|
|
100
|
+
}
|
|
101
|
+
res.end(JSON.stringify(options.updateTask(id, body), null, 2));
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (path3 === "status" && req.method === "GET") {
|
|
105
|
+
res.end(JSON.stringify({ status: "ok", tool: "annotask" }));
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
res.statusCode = 404;
|
|
109
|
+
res.end(JSON.stringify({ error: "Not found" }));
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// src/server/ws-server.ts
|
|
114
|
+
import { WebSocketServer, WebSocket } from "ws";
|
|
115
|
+
function createWSServer() {
|
|
116
|
+
let currentReport = null;
|
|
117
|
+
const clients = /* @__PURE__ */ new Set();
|
|
118
|
+
const wss = new WebSocketServer({ noServer: true });
|
|
119
|
+
wss.on("connection", (ws) => {
|
|
120
|
+
clients.add(ws);
|
|
121
|
+
if (currentReport) {
|
|
122
|
+
ws.send(JSON.stringify({ event: "report:current", data: currentReport, timestamp: Date.now() }));
|
|
123
|
+
}
|
|
124
|
+
ws.on("message", (raw) => {
|
|
125
|
+
try {
|
|
126
|
+
const msg = JSON.parse(raw.toString());
|
|
127
|
+
if (msg.event === "report:updated") {
|
|
128
|
+
currentReport = msg.data;
|
|
129
|
+
for (const client of clients) {
|
|
130
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
131
|
+
client.send(JSON.stringify({ event: "report:updated", data: msg.data, timestamp: Date.now() }));
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
if (msg.event === "changes:cleared") {
|
|
136
|
+
currentReport = null;
|
|
137
|
+
for (const client of clients) {
|
|
138
|
+
if (client !== ws && client.readyState === WebSocket.OPEN) {
|
|
139
|
+
client.send(JSON.stringify({ event: "changes:cleared", data: null, timestamp: Date.now() }));
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
if (msg.event === "get:report") {
|
|
144
|
+
ws.send(JSON.stringify({ event: "report:current", data: currentReport, timestamp: Date.now() }));
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
ws.on("close", () => {
|
|
150
|
+
clients.delete(ws);
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
return {
|
|
154
|
+
handleUpgrade(req, socket, head) {
|
|
155
|
+
wss.handleUpgrade(req, socket, head, (ws) => {
|
|
156
|
+
wss.emit("connection", ws, req);
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
broadcast(event, data) {
|
|
160
|
+
const msg = JSON.stringify({ event, data, timestamp: Date.now() });
|
|
161
|
+
for (const client of clients) {
|
|
162
|
+
if (client.readyState === WebSocket.OPEN) client.send(msg);
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
getReport() {
|
|
166
|
+
return currentReport;
|
|
167
|
+
},
|
|
168
|
+
clients
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// src/server/serve-shell.ts
|
|
173
|
+
import fs from "fs";
|
|
174
|
+
import path from "path";
|
|
175
|
+
import { fileURLToPath } from "url";
|
|
176
|
+
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
177
|
+
function findShellDist() {
|
|
178
|
+
return path.resolve(__dirname, "shell");
|
|
179
|
+
}
|
|
180
|
+
function createShellMiddleware() {
|
|
181
|
+
const shellDist = findShellDist();
|
|
182
|
+
return (req, res, next) => {
|
|
183
|
+
if (!req.url?.startsWith("/__annotask")) return next();
|
|
184
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
185
|
+
let filePath = req.url.replace("/__annotask", "") || "/";
|
|
186
|
+
const queryIndex = filePath.indexOf("?");
|
|
187
|
+
if (queryIndex !== -1) filePath = filePath.slice(0, queryIndex);
|
|
188
|
+
if (filePath === "/" || filePath === "") filePath = "/index.html";
|
|
189
|
+
if (filePath.startsWith("/api/") || filePath === "/ws") return next();
|
|
190
|
+
const fullPath = path.join(shellDist, filePath);
|
|
191
|
+
if (!fullPath.startsWith(shellDist)) {
|
|
192
|
+
res.statusCode = 403;
|
|
193
|
+
res.end("Forbidden");
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {
|
|
197
|
+
const indexPath = path.join(shellDist, "index.html");
|
|
198
|
+
if (fs.existsSync(indexPath)) {
|
|
199
|
+
res.setHeader("Content-Type", "text/html");
|
|
200
|
+
res.end(fs.readFileSync(indexPath, "utf-8"));
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
res.statusCode = 404;
|
|
204
|
+
res.end("Annotask shell not built. Run: pnpm build:shell");
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
const ext = path.extname(fullPath);
|
|
208
|
+
const contentTypes = {
|
|
209
|
+
".html": "text/html",
|
|
210
|
+
".js": "application/javascript",
|
|
211
|
+
".css": "text/css",
|
|
212
|
+
".json": "application/json",
|
|
213
|
+
".svg": "image/svg+xml",
|
|
214
|
+
".png": "image/png",
|
|
215
|
+
".ico": "image/x-icon"
|
|
216
|
+
};
|
|
217
|
+
res.setHeader("Content-Type", contentTypes[ext] || "application/octet-stream");
|
|
218
|
+
res.end(fs.readFileSync(fullPath));
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/server/state.ts
|
|
223
|
+
import fs2 from "fs";
|
|
224
|
+
import path2 from "path";
|
|
225
|
+
var DEFAULT_DESIGN_SPEC = {
|
|
226
|
+
initialized: false,
|
|
227
|
+
version: "1.0",
|
|
228
|
+
framework: null,
|
|
229
|
+
colors: [],
|
|
230
|
+
typography: { families: [], scale: [], weights: [] },
|
|
231
|
+
spacing: [],
|
|
232
|
+
borders: { radius: [] },
|
|
233
|
+
icons: null,
|
|
234
|
+
components: null
|
|
235
|
+
};
|
|
236
|
+
function createProjectState(projectRoot, broadcast) {
|
|
237
|
+
let cachedDesignSpec = null;
|
|
238
|
+
let specWatcher = null;
|
|
239
|
+
const tasksPath = path2.join(projectRoot, ".annotask", "tasks.json");
|
|
240
|
+
function getDesignSpec() {
|
|
241
|
+
if (cachedDesignSpec !== null) return cachedDesignSpec;
|
|
242
|
+
const specPath = path2.join(projectRoot, ".annotask", "design-spec.json");
|
|
243
|
+
try {
|
|
244
|
+
cachedDesignSpec = { initialized: true, ...JSON.parse(fs2.readFileSync(specPath, "utf-8")) };
|
|
245
|
+
} catch {
|
|
246
|
+
cachedDesignSpec = DEFAULT_DESIGN_SPEC;
|
|
247
|
+
}
|
|
248
|
+
if (!specWatcher) {
|
|
249
|
+
const configDir = path2.join(projectRoot, ".annotask");
|
|
250
|
+
try {
|
|
251
|
+
if (!fs2.existsSync(configDir)) fs2.mkdirSync(configDir, { recursive: true });
|
|
252
|
+
specWatcher = fs2.watch(configDir, (_, filename) => {
|
|
253
|
+
cachedDesignSpec = null;
|
|
254
|
+
if (filename === "design-spec.json") broadcast("designspec:updated", null);
|
|
255
|
+
});
|
|
256
|
+
} catch {
|
|
257
|
+
cachedDesignSpec = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
return cachedDesignSpec ?? DEFAULT_DESIGN_SPEC;
|
|
261
|
+
}
|
|
262
|
+
function getConfig() {
|
|
263
|
+
const spec = getDesignSpec();
|
|
264
|
+
return { initialized: !!spec?.initialized, ...spec };
|
|
265
|
+
}
|
|
266
|
+
function readTasks() {
|
|
267
|
+
try {
|
|
268
|
+
return JSON.parse(fs2.readFileSync(tasksPath, "utf-8"));
|
|
269
|
+
} catch {
|
|
270
|
+
return { version: "1.0", tasks: [] };
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
function writeTasks(data) {
|
|
274
|
+
const dir = path2.dirname(tasksPath);
|
|
275
|
+
if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
|
|
276
|
+
fs2.writeFileSync(tasksPath, JSON.stringify(data, null, 2));
|
|
277
|
+
}
|
|
278
|
+
function addTask(task) {
|
|
279
|
+
const data = readTasks();
|
|
280
|
+
const id = `task-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`;
|
|
281
|
+
const newTask = { id, status: "pending", createdAt: Date.now(), updatedAt: Date.now(), ...task };
|
|
282
|
+
data.tasks.push(newTask);
|
|
283
|
+
writeTasks(data);
|
|
284
|
+
broadcast("tasks:updated", data);
|
|
285
|
+
return newTask;
|
|
286
|
+
}
|
|
287
|
+
function updateTask(id, updates) {
|
|
288
|
+
const data = readTasks();
|
|
289
|
+
const task = data.tasks.find((t) => t.id === id);
|
|
290
|
+
if (!task) return { error: "Task not found" };
|
|
291
|
+
Object.assign(task, updates, { updatedAt: Date.now() });
|
|
292
|
+
if (updates.status === "accepted") data.tasks = data.tasks.filter((t) => t.id !== id);
|
|
293
|
+
writeTasks(data);
|
|
294
|
+
broadcast("tasks:updated", data);
|
|
295
|
+
return task;
|
|
296
|
+
}
|
|
297
|
+
function dispose() {
|
|
298
|
+
if (specWatcher) {
|
|
299
|
+
specWatcher.close();
|
|
300
|
+
specWatcher = null;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
return { getDesignSpec, getConfig, getTasks: readTasks, addTask, updateTask, dispose };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// src/server/index.ts
|
|
307
|
+
function createAnnotaskServer(options) {
|
|
308
|
+
const wsServer = createWSServer();
|
|
309
|
+
const state = createProjectState(options.projectRoot, wsServer.broadcast);
|
|
310
|
+
const apiMiddleware = createAPIMiddleware({
|
|
311
|
+
getReport: () => wsServer.getReport(),
|
|
312
|
+
getConfig: () => state.getConfig(),
|
|
313
|
+
getDesignSpec: () => state.getDesignSpec(),
|
|
314
|
+
getTasks: () => state.getTasks(),
|
|
315
|
+
addTask: (task) => state.addTask(task),
|
|
316
|
+
updateTask: (id, updates) => state.updateTask(id, updates)
|
|
317
|
+
});
|
|
318
|
+
const shellMiddleware = createShellMiddleware();
|
|
319
|
+
const middleware = (req, res, next) => {
|
|
320
|
+
apiMiddleware(req, res, () => {
|
|
321
|
+
shellMiddleware(req, res, next);
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
return {
|
|
325
|
+
middleware,
|
|
326
|
+
handleUpgrade: (req, socket, head) => wsServer.handleUpgrade(req, socket, head),
|
|
327
|
+
broadcast: (event, data) => wsServer.broadcast(event, data),
|
|
328
|
+
getReport: () => wsServer.getReport(),
|
|
329
|
+
dispose: () => state.dispose()
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export {
|
|
334
|
+
createAPIMiddleware,
|
|
335
|
+
createWSServer,
|
|
336
|
+
createShellMiddleware,
|
|
337
|
+
createProjectState,
|
|
338
|
+
createAnnotaskServer
|
|
339
|
+
};
|
|
340
|
+
//# sourceMappingURL=chunk-JLOSPIJ4.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'\n\nexport interface APIOptions {\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 = 1_048_576\nconst VALID_TASK_STATUSES = new Set(['pending', 'applied', 'review', 'accepted', 'denied'])\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\nexport function createAPIMiddleware(options: APIOptions) {\n return async (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n if (!req.url?.startsWith('/__annotask/api/')) return next()\n\n const path = req.url.replace('/__annotask/api/', '')\n\n res.setHeader('Content-Type', 'application/json')\n res.setHeader('Access-Control-Allow-Origin', '*')\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 if (path === 'report' && req.method === 'GET') {\n res.end(JSON.stringify(options.getReport() ?? { version: '1.0', changes: [] }, null, 2))\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.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 res.end(JSON.stringify(options.updateTask(id, body), 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 fs from 'node:fs'\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\nexport function createShellMiddleware() {\n const shellDist = findShellDist()\n\n return (req: IncomingMessage, res: ServerResponse, next: () => void) => {\n if (!req.url?.startsWith('/__annotask')) return next()\n\n // CORS for cross-port access (Webpack standalone server)\n res.setHeader('Access-Control-Allow-Origin', '*')\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 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 if (!fs.existsSync(fullPath) || !fs.statSync(fullPath).isFile()) {\n const indexPath = path.join(shellDist, 'index.html')\n if (fs.existsSync(indexPath)) {\n res.setHeader('Content-Type', 'text/html')\n res.end(fs.readFileSync(indexPath, 'utf-8'))\n return\n }\n res.statusCode = 404\n res.end('Annotask shell not built. Run: pnpm build:shell')\n return\n }\n\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 res.end(fs.readFileSync(fullPath))\n }\n}\n","import fs from 'node:fs'\nimport path from 'node:path'\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\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 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 })\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 readTasks(): { version: string; tasks: any[] } {\n try { return JSON.parse(fs.readFileSync(tasksPath, 'utf-8')) } catch { return { version: '1.0', tasks: [] } }\n }\n\n function writeTasks(data: { version: string; tasks: any[] }) {\n const dir = path.dirname(tasksPath)\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })\n fs.writeFileSync(tasksPath, JSON.stringify(data, null, 2))\n }\n\n function addTask(task: Record<string, unknown>) {\n const data = readTasks()\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 writeTasks(data)\n broadcast('tasks:updated', data)\n return newTask\n }\n\n function updateTask(id: string, updates: Record<string, unknown>) {\n const data = readTasks()\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') data.tasks = data.tasks.filter((t: any) => t.id !== id)\n writeTasks(data)\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: readTasks, 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 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":["path","fs","path"]}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
removeServerInfo,
|
|
3
|
+
writeServerInfo
|
|
4
|
+
} from "./chunk-XLNGAH3S.js";
|
|
5
|
+
import {
|
|
6
|
+
createAnnotaskServer
|
|
7
|
+
} from "./chunk-JLOSPIJ4.js";
|
|
8
|
+
|
|
9
|
+
// src/server/standalone.ts
|
|
10
|
+
import http from "http";
|
|
11
|
+
async function startStandaloneServer(options) {
|
|
12
|
+
const port = options.port || 24678;
|
|
13
|
+
const uiServer = createAnnotaskServer({ projectRoot: options.projectRoot });
|
|
14
|
+
const httpServer = http.createServer((req, res) => {
|
|
15
|
+
uiServer.middleware(req, res, () => {
|
|
16
|
+
res.statusCode = 404;
|
|
17
|
+
res.end("Not found");
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
httpServer.on("upgrade", (req, socket, head) => {
|
|
21
|
+
if (req.url === "/__annotask/ws") {
|
|
22
|
+
uiServer.handleUpgrade(req, socket, head);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
return new Promise((resolve, reject) => {
|
|
26
|
+
httpServer.on("error", (err) => {
|
|
27
|
+
if (err.code === "EADDRINUSE") {
|
|
28
|
+
httpServer.listen(0, () => {
|
|
29
|
+
const addr = httpServer.address();
|
|
30
|
+
writeServerInfo(options.projectRoot, addr.port);
|
|
31
|
+
resolve({
|
|
32
|
+
port: addr.port,
|
|
33
|
+
close: () => {
|
|
34
|
+
removeServerInfo(options.projectRoot);
|
|
35
|
+
uiServer.dispose();
|
|
36
|
+
httpServer.close();
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
} else {
|
|
41
|
+
reject(err);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
httpServer.listen(port, () => {
|
|
45
|
+
writeServerInfo(options.projectRoot, port);
|
|
46
|
+
resolve({
|
|
47
|
+
port,
|
|
48
|
+
close: () => {
|
|
49
|
+
removeServerInfo(options.projectRoot);
|
|
50
|
+
uiServer.dispose();
|
|
51
|
+
httpServer.close();
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export {
|
|
59
|
+
startStandaloneServer
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=chunk-JPNMDGZN.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/server/standalone.ts"],"sourcesContent":["import http from 'node:http'\nimport { createAnnotaskServer } from './index.js'\nimport { writeServerInfo, removeServerInfo } from './discovery.js'\n\nexport interface StandaloneServerOptions {\n projectRoot: string\n port?: number\n}\n\nexport async function startStandaloneServer(options: StandaloneServerOptions): Promise<{\n port: number\n close: () => void\n}> {\n const port = options.port || 24678\n const uiServer = createAnnotaskServer({ projectRoot: options.projectRoot })\n\n const httpServer = http.createServer((req, res) => {\n uiServer.middleware(req, res, () => {\n res.statusCode = 404\n res.end('Not found')\n })\n })\n\n httpServer.on('upgrade', (req, socket, head) => {\n if (req.url === '/__annotask/ws') {\n uiServer.handleUpgrade(req, socket, head)\n }\n })\n\n return new Promise((resolve, reject) => {\n httpServer.on('error', (err: NodeJS.ErrnoException) => {\n if (err.code === 'EADDRINUSE') {\n httpServer.listen(0, () => {\n const addr = httpServer.address() as { port: number }\n writeServerInfo(options.projectRoot, addr.port)\n resolve({\n port: addr.port,\n close: () => { removeServerInfo(options.projectRoot); uiServer.dispose(); httpServer.close() },\n })\n })\n } else {\n reject(err)\n }\n })\n\n httpServer.listen(port, () => {\n writeServerInfo(options.projectRoot, port)\n resolve({\n port,\n close: () => { removeServerInfo(options.projectRoot); uiServer.dispose(); httpServer.close() },\n })\n })\n })\n}\n"],"mappings":";;;;;;;;;AAAA,OAAO,UAAU;AASjB,eAAsB,sBAAsB,SAGzC;AACD,QAAM,OAAO,QAAQ,QAAQ;AAC7B,QAAM,WAAW,qBAAqB,EAAE,aAAa,QAAQ,YAAY,CAAC;AAE1E,QAAM,aAAa,KAAK,aAAa,CAAC,KAAK,QAAQ;AACjD,aAAS,WAAW,KAAK,KAAK,MAAM;AAClC,UAAI,aAAa;AACjB,UAAI,IAAI,WAAW;AAAA,IACrB,CAAC;AAAA,EACH,CAAC;AAED,aAAW,GAAG,WAAW,CAAC,KAAK,QAAQ,SAAS;AAC9C,QAAI,IAAI,QAAQ,kBAAkB;AAChC,eAAS,cAAc,KAAK,QAAQ,IAAI;AAAA,IAC1C;AAAA,EACF,CAAC;AAED,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,eAAW,GAAG,SAAS,CAAC,QAA+B;AACrD,UAAI,IAAI,SAAS,cAAc;AAC7B,mBAAW,OAAO,GAAG,MAAM;AACzB,gBAAM,OAAO,WAAW,QAAQ;AAChC,0BAAgB,QAAQ,aAAa,KAAK,IAAI;AAC9C,kBAAQ;AAAA,YACN,MAAM,KAAK;AAAA,YACX,OAAO,MAAM;AAAE,+BAAiB,QAAQ,WAAW;AAAG,uBAAS,QAAQ;AAAG,yBAAW,MAAM;AAAA,YAAE;AAAA,UAC/F,CAAC;AAAA,QACH,CAAC;AAAA,MACH,OAAO;AACL,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAED,eAAW,OAAO,MAAM,MAAM;AAC5B,sBAAgB,QAAQ,aAAa,IAAI;AACzC,cAAQ;AAAA,QACN;AAAA,QACA,OAAO,MAAM;AAAE,2BAAiB,QAAQ,WAAW;AAAG,mBAAS,QAAQ;AAAG,qBAAW,MAAM;AAAA,QAAE;AAAA,MAC/F,CAAC;AAAA,IACH,CAAC;AAAA,EACH,CAAC;AACH;","names":[]}
|