agent-relay-server 0.4.7 → 0.4.9

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 CHANGED
@@ -8,7 +8,7 @@ A lightweight HTTP message bus that lets AI coding agents talk to each other acr
8
8
 
9
9
  Built for [Claude Code](https://docs.anthropic.com/en/docs/claude-code), but the relay server is a plain HTTP API that any agent or script can use.
10
10
 
11
- ![Agent Relay Dashboard](docs/dashboard.png)
11
+ ![Agent Relay Dashboard](https://raw.githubusercontent.com/edimuj/agent-relay/main/docs/dashboard.png)
12
12
 
13
13
  ## Why
14
14
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Agent Relay integration for Codex sessions",
5
5
  "author": {
6
6
  "name": "Edin Mujkanovic"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-relay-server",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Lightweight HTTP message relay for inter-agent communication across machines",
5
5
  "module": "src/index.ts",
6
6
  "type": "module",
package/src/index.ts CHANGED
@@ -7,7 +7,6 @@ import {
7
7
  REAP_INTERVAL_MS,
8
8
  STALE_TTL_MS,
9
9
  OFFLINE_PRUNE_MS,
10
- MAX_BODY_BYTES,
11
10
  DAY_MS,
12
11
  VERSION,
13
12
  } from "./config";
@@ -57,68 +56,66 @@ function startServer(): void {
57
56
  if (pruned > 0) console.log(`pruned ${pruned} old message(s)`);
58
57
  }, DAY_MS);
59
58
 
60
- const publicDir = resolve(import.meta.dir, "../public");
61
- const publicDirPrefix = publicDir + sep;
62
-
63
59
  Bun.serve({
64
60
  port: PORT,
65
61
  hostname: HOST,
66
- async fetch(req) {
67
- const url = new URL(req.url);
62
+ fetch: createFetchHandler({ logRequests: LOG_REQUESTS }),
63
+ });
68
64
 
69
- if (req.method === "OPTIONS") {
70
- return corsPreflight(req);
71
- }
72
- if (!isOriginAllowed(req)) {
73
- return Response.json({ error: "origin not allowed" }, { status: 403 });
74
- }
65
+ console.log(`agent-relay ${VERSION} running on http://${HOST}:${PORT}`);
66
+ }
75
67
 
76
- // Body size guard for write methods
77
- if (req.method === "POST" || req.method === "PATCH" || req.method === "PUT") {
78
- const len = Number(req.headers.get("content-length") ?? 0);
79
- if (len > MAX_BODY_BYTES) {
80
- return Response.json(
81
- { error: `request body exceeds ${MAX_BODY_BYTES} bytes` },
82
- { status: 413 },
83
- );
84
- }
85
- }
68
+ export function createFetchHandler(
69
+ opts: { publicDir?: string; logRequests?: boolean } = {},
70
+ ): (req: Request) => Promise<Response> {
71
+ const publicDir = opts.publicDir ?? resolve(import.meta.dir, "../public");
72
+ const publicDirPrefix = publicDir + sep;
73
+ const logRequests = opts.logRequests ?? false;
86
74
 
87
- // API routes
88
- const matched = matchRoute(req.method, url.pathname);
89
- if (matched) {
90
- const integrationAuth = getIntegrationAuth(req);
91
- if (!isAuthorized(req)) {
92
- if (!integrationAuth || !url.pathname.startsWith("/api/integrations/")) {
93
- return unauthorized(req);
94
- }
95
- }
96
- const response = await matched.handler(req, matched.params);
97
- applyCors(req, response);
98
- if (LOG_REQUESTS && url.pathname.startsWith("/api/")) {
99
- console.log(`${req.method} ${url.pathname} → ${response.status}`);
75
+ return async function fetch(req: Request): Promise<Response> {
76
+ const url = new URL(req.url);
77
+
78
+ if (req.method === "OPTIONS") {
79
+ return corsPreflight(req);
80
+ }
81
+ if (!isOriginAllowed(req)) {
82
+ return Response.json({ error: "origin not allowed" }, { status: 403 });
83
+ }
84
+
85
+ // API routes
86
+ const matched = matchRoute(req.method, url.pathname);
87
+ if (matched) {
88
+ const integrationAuth = getIntegrationAuth(req);
89
+ if (!isAuthorized(req)) {
90
+ if (!integrationAuth || !url.pathname.startsWith("/api/integrations/")) {
91
+ return unauthorized(req);
100
92
  }
101
- return response;
102
93
  }
103
-
104
- // Dashboard — serve static files, rejecting path traversal and directory requests
105
- let requested = url.pathname === "/" ? "/index.html" : url.pathname;
106
- if (requested.endsWith("/")) requested += "index.html";
107
- const resolved = resolve(publicDir, `.${requested}`);
108
- if (!resolved.startsWith(publicDirPrefix)) {
109
- return Response.json({ error: "not found" }, { status: 404 });
94
+ const response = await matched.handler(req, matched.params);
95
+ applyCors(req, response);
96
+ if (logRequests && url.pathname.startsWith("/api/")) {
97
+ console.log(`${req.method} ${url.pathname} ${response.status}`);
110
98
  }
111
- const file = Bun.file(resolved);
112
- if (await file.exists()) return new Response(file);
99
+ return response;
100
+ }
113
101
 
102
+ // Dashboard — serve static files, rejecting path traversal and directory requests
103
+ let requested = url.pathname === "/" ? "/index.html" : url.pathname;
104
+ if (requested.endsWith("/")) requested += "index.html";
105
+ const resolved = resolve(publicDir, `.${requested}`);
106
+ if (!resolved.startsWith(publicDirPrefix)) {
114
107
  return Response.json({ error: "not found" }, { status: 404 });
115
- },
116
- });
108
+ }
109
+ const file = Bun.file(resolved);
110
+ if (await file.exists()) return new Response(file);
117
111
 
118
- console.log(`agent-relay ${VERSION} running on http://${HOST}:${PORT}`);
112
+ return Response.json({ error: "not found" }, { status: 404 });
113
+ };
119
114
  }
120
115
 
121
- main().catch((error) => {
122
- console.error(error instanceof Error ? error.message : String(error));
123
- process.exit(1);
124
- });
116
+ if (import.meta.main) {
117
+ main().catch((error) => {
118
+ console.error(error instanceof Error ? error.message : String(error));
119
+ process.exit(1);
120
+ });
121
+ }