@jamesaphoenix/tx-api-server 0.1.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/dist/__tests__/api.test.d.ts +7 -0
- package/dist/__tests__/api.test.d.ts.map +1 -0
- package/dist/__tests__/api.test.js +184 -0
- package/dist/__tests__/api.test.js.map +1 -0
- package/dist/__tests__/auth.test.d.ts +7 -0
- package/dist/__tests__/auth.test.d.ts.map +1 -0
- package/dist/__tests__/auth.test.js +174 -0
- package/dist/__tests__/auth.test.js.map +1 -0
- package/dist/__tests__/body-limit.test.d.ts +7 -0
- package/dist/__tests__/body-limit.test.d.ts.map +1 -0
- package/dist/__tests__/body-limit.test.js +60 -0
- package/dist/__tests__/body-limit.test.js.map +1 -0
- package/dist/__tests__/health.test.d.ts.map +1 -0
- package/dist/__tests__/health.test.js.map +1 -0
- package/dist/__tests__/log-reader.test.d.ts +8 -0
- package/dist/__tests__/log-reader.test.d.ts.map +1 -0
- package/dist/__tests__/log-reader.test.js +55 -0
- package/dist/__tests__/log-reader.test.js.map +1 -0
- package/dist/__tests__/rate-limit.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit.test.js.map +1 -0
- package/dist/__tests__/server-lib.test.d.ts +10 -0
- package/dist/__tests__/server-lib.test.d.ts.map +1 -0
- package/dist/__tests__/server-lib.test.js +145 -0
- package/dist/__tests__/server-lib.test.js.map +1 -0
- package/dist/api.d.ts +1616 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +566 -0
- package/dist/api.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +31 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/auth.js +65 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/body-limit.d.ts +33 -0
- package/dist/middleware/body-limit.d.ts.map +1 -0
- package/dist/middleware/body-limit.js +50 -0
- package/dist/middleware/body-limit.js.map +1 -0
- package/dist/middleware/cors.d.ts +15 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/cors.js +31 -0
- package/dist/middleware/cors.js.map +1 -0
- package/dist/middleware/error.d.ts +26 -0
- package/dist/middleware/error.d.ts.map +1 -0
- package/dist/middleware/error.js +84 -0
- package/dist/middleware/error.js.map +1 -0
- package/dist/middleware/rate-limit.d.ts +59 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/rate-limit.js +296 -0
- package/dist/middleware/rate-limit.js.map +1 -0
- package/dist/routes/attempts.d.ts +9 -0
- package/dist/routes/attempts.d.ts.map +1 -0
- package/dist/routes/attempts.js +26 -0
- package/dist/routes/attempts.js.map +1 -0
- package/dist/routes/claims.d.ts +9 -0
- package/dist/routes/claims.d.ts.map +1 -0
- package/dist/routes/claims.js +42 -0
- package/dist/routes/claims.js.map +1 -0
- package/dist/routes/docs.d.ts +9 -0
- package/dist/routes/docs.d.ts.map +1 -0
- package/dist/routes/docs.js +80 -0
- package/dist/routes/docs.js.map +1 -0
- package/dist/routes/health.d.ts +8 -0
- package/dist/routes/health.d.ts.map +1 -0
- package/dist/routes/health.js +101 -0
- package/dist/routes/health.js.map +1 -0
- package/dist/routes/invariants.d.ts +9 -0
- package/dist/routes/invariants.d.ts.map +1 -0
- package/dist/routes/invariants.js +64 -0
- package/dist/routes/invariants.js.map +1 -0
- package/dist/routes/learnings.d.ts +8 -0
- package/dist/routes/learnings.d.ts.map +1 -0
- package/dist/routes/learnings.js +93 -0
- package/dist/routes/learnings.js.map +1 -0
- package/dist/routes/runs.d.ts +8 -0
- package/dist/routes/runs.d.ts.map +1 -0
- package/dist/routes/runs.js +195 -0
- package/dist/routes/runs.js.map +1 -0
- package/dist/routes/sync.d.ts +8 -0
- package/dist/routes/sync.d.ts.map +1 -0
- package/dist/routes/sync.js +67 -0
- package/dist/routes/sync.js.map +1 -0
- package/dist/routes/tasks.d.ts +9 -0
- package/dist/routes/tasks.d.ts.map +1 -0
- package/dist/routes/tasks.js +167 -0
- package/dist/routes/tasks.js.map +1 -0
- package/dist/runtime.d.ts +10 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +10 -0
- package/dist/runtime.js.map +1 -0
- package/dist/server-lib.d.ts +26 -0
- package/dist/server-lib.d.ts.map +1 -0
- package/dist/server-lib.js +123 -0
- package/dist/server-lib.js.map +1 -0
- package/dist/server.d.ts +14 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +15 -0
- package/dist/server.js.map +1 -0
- package/dist/utils/log-reader.d.ts +26 -0
- package/dist/utils/log-reader.d.ts.map +1 -0
- package/dist/utils/log-reader.js +52 -0
- package/dist/utils/log-reader.js.map +1 -0
- package/dist/utils/transcript-parser.d.ts +40 -0
- package/dist/utils/transcript-parser.d.ts.map +1 -0
- package/dist/utils/transcript-parser.js +216 -0
- package/dist/utils/transcript-parser.js.map +1 -0
- package/package.json +75 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rate Limiting Middleware
|
|
3
|
+
*
|
|
4
|
+
* Provides configurable rate limiting to protect the API from abuse.
|
|
5
|
+
* Uses a sliding window algorithm with in-memory storage.
|
|
6
|
+
*
|
|
7
|
+
* Rate limiting is disabled by default; enable by setting TX_API_RATE_LIMIT env var.
|
|
8
|
+
*
|
|
9
|
+
* SECURITY NOTE: By default, proxy headers (X-Forwarded-For, X-Real-IP) are NOT trusted.
|
|
10
|
+
* Set TX_API_TRUST_PROXY=true ONLY when running behind a trusted reverse proxy.
|
|
11
|
+
* Otherwise, attackers can bypass rate limiting by spoofing these headers.
|
|
12
|
+
*/
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
/**
|
|
15
|
+
* IPv4 address pattern (basic validation).
|
|
16
|
+
*/
|
|
17
|
+
const IPV4_PATTERN = /^(\d{1,3}\.){3}\d{1,3}$/;
|
|
18
|
+
/**
|
|
19
|
+
* IPv6 address pattern (basic validation - covers common formats).
|
|
20
|
+
*/
|
|
21
|
+
const IPV6_PATTERN = /^([0-9a-fA-F]{0,4}:){2,7}[0-9a-fA-F]{0,4}$|^::1$|^::$/;
|
|
22
|
+
/**
|
|
23
|
+
* Validate that a string looks like a valid IP address.
|
|
24
|
+
* This is a basic check to prevent obviously invalid or malicious values.
|
|
25
|
+
*/
|
|
26
|
+
const isValidIpFormat = (ip) => {
|
|
27
|
+
const trimmed = ip.trim();
|
|
28
|
+
return IPV4_PATTERN.test(trimmed) || IPV6_PATTERN.test(trimmed);
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* In-memory store for rate limit tracking.
|
|
32
|
+
* Uses client IP as the key.
|
|
33
|
+
*/
|
|
34
|
+
const store = new Map();
|
|
35
|
+
/**
|
|
36
|
+
* Cleanup interval for expired entries (5 minutes).
|
|
37
|
+
*/
|
|
38
|
+
const CLEANUP_INTERVAL_MS = 5 * 60 * 1000;
|
|
39
|
+
/**
|
|
40
|
+
* Start cleanup interval on first use.
|
|
41
|
+
*/
|
|
42
|
+
let cleanupStarted = false;
|
|
43
|
+
const startCleanup = () => {
|
|
44
|
+
if (cleanupStarted)
|
|
45
|
+
return;
|
|
46
|
+
cleanupStarted = true;
|
|
47
|
+
setInterval(() => {
|
|
48
|
+
const now = Date.now();
|
|
49
|
+
for (const [key, entry] of store.entries()) {
|
|
50
|
+
if (entry.expiresAt < now) {
|
|
51
|
+
store.delete(key);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}, CLEANUP_INTERVAL_MS).unref();
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Check if rate limiting is enabled.
|
|
58
|
+
*/
|
|
59
|
+
export const isRateLimitEnabled = () => {
|
|
60
|
+
return !!process.env.TX_API_RATE_LIMIT;
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Check if proxy headers should be trusted.
|
|
64
|
+
* Only enable this when running behind a trusted reverse proxy.
|
|
65
|
+
*/
|
|
66
|
+
export const isTrustProxyEnabled = () => {
|
|
67
|
+
return process.env.TX_API_TRUST_PROXY === "true";
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Get rate limit configuration from environment.
|
|
71
|
+
*/
|
|
72
|
+
const getConfig = () => {
|
|
73
|
+
const envValue = process.env.TX_API_RATE_LIMIT;
|
|
74
|
+
const max = envValue ? parseInt(envValue, 10) : 100;
|
|
75
|
+
return {
|
|
76
|
+
max: isNaN(max) ? 100 : max,
|
|
77
|
+
windowSec: parseInt(process.env.TX_API_RATE_LIMIT_WINDOW ?? "60", 10) || 60,
|
|
78
|
+
message: process.env.TX_API_RATE_LIMIT_MESSAGE ?? "Too many requests, please try again later",
|
|
79
|
+
skipHealthEndpoints: process.env.TX_API_RATE_LIMIT_SKIP_HEALTH !== "false",
|
|
80
|
+
trustProxy: isTrustProxyEnabled()
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Extract client IP from proxy headers (only when trustProxy is enabled).
|
|
85
|
+
* Validates IP format to prevent malicious header values.
|
|
86
|
+
*/
|
|
87
|
+
const getIpFromProxyHeaders = (c) => {
|
|
88
|
+
// Check X-Forwarded-For header
|
|
89
|
+
const forwarded = c.req.header("X-Forwarded-For");
|
|
90
|
+
if (forwarded) {
|
|
91
|
+
// Take the first IP in the chain (original client)
|
|
92
|
+
const ip = forwarded.split(",")[0].trim();
|
|
93
|
+
if (isValidIpFormat(ip)) {
|
|
94
|
+
return ip;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Check X-Real-IP header
|
|
98
|
+
const realIp = c.req.header("X-Real-IP");
|
|
99
|
+
if (realIp) {
|
|
100
|
+
const ip = realIp.trim();
|
|
101
|
+
if (isValidIpFormat(ip)) {
|
|
102
|
+
return ip;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return null;
|
|
106
|
+
};
|
|
107
|
+
/**
|
|
108
|
+
* Extract client IP from the connection (socket level).
|
|
109
|
+
* Works with @hono/node-server when HttpBindings are available.
|
|
110
|
+
*/
|
|
111
|
+
const getConnectionIp = (c) => {
|
|
112
|
+
try {
|
|
113
|
+
// @hono/node-server exposes socket info via c.env.incoming
|
|
114
|
+
const incoming = c.env?.incoming;
|
|
115
|
+
if (incoming?.socket?.remoteAddress) {
|
|
116
|
+
const ip = incoming.socket.remoteAddress;
|
|
117
|
+
// Handle IPv6-mapped IPv4 addresses (::ffff:127.0.0.1 -> 127.0.0.1)
|
|
118
|
+
if (ip.startsWith("::ffff:")) {
|
|
119
|
+
return ip.slice(7);
|
|
120
|
+
}
|
|
121
|
+
return ip;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
// Connection info not available in this runtime
|
|
126
|
+
}
|
|
127
|
+
return null;
|
|
128
|
+
};
|
|
129
|
+
/**
|
|
130
|
+
* Extract client identifier from request.
|
|
131
|
+
*
|
|
132
|
+
* SECURITY: Only trusts proxy headers (X-Forwarded-For, X-Real-IP) when
|
|
133
|
+
* TX_API_TRUST_PROXY=true. Otherwise, uses the actual connection IP.
|
|
134
|
+
*
|
|
135
|
+
* When running behind a trusted reverse proxy (nginx, cloud load balancer),
|
|
136
|
+
* set TX_API_TRUST_PROXY=true to get the real client IP from headers.
|
|
137
|
+
*
|
|
138
|
+
* When NOT behind a proxy, leave TX_API_TRUST_PROXY unset (default: false)
|
|
139
|
+
* to prevent attackers from bypassing rate limits by spoofing headers.
|
|
140
|
+
*/
|
|
141
|
+
const getClientId = (c) => {
|
|
142
|
+
const config = getConfig();
|
|
143
|
+
// Only trust proxy headers when explicitly enabled
|
|
144
|
+
if (config.trustProxy) {
|
|
145
|
+
const proxyIp = getIpFromProxyHeaders(c);
|
|
146
|
+
if (proxyIp) {
|
|
147
|
+
return proxyIp;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// Try to get the actual connection IP
|
|
151
|
+
const connectionIp = getConnectionIp(c);
|
|
152
|
+
if (connectionIp) {
|
|
153
|
+
return connectionIp;
|
|
154
|
+
}
|
|
155
|
+
// Fallback when connection info is not available
|
|
156
|
+
// Generate unique ID per request to prevent shared bucket DoS attacks
|
|
157
|
+
// SECURITY NOTE: This means rate limiting is ineffective for unidentifiable clients,
|
|
158
|
+
// but it's better than a shared bucket that allows attackers to block all anonymous users.
|
|
159
|
+
// In production with @hono/node-server, connection IP should be available.
|
|
160
|
+
console.warn("[rate-limit] Unable to identify client IP - rate limiting degraded for this request. " +
|
|
161
|
+
"Ensure your server runtime exposes socket info or configure a trusted reverse proxy.");
|
|
162
|
+
return `anon-${randomUUID()}`;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Check if the request should be rate limited.
|
|
166
|
+
* Returns the number of remaining requests, or -1 if rate limited.
|
|
167
|
+
*/
|
|
168
|
+
const checkRateLimit = (clientId, config) => {
|
|
169
|
+
const now = Date.now();
|
|
170
|
+
const windowMs = config.windowSec * 1000;
|
|
171
|
+
const windowStart = now - windowMs;
|
|
172
|
+
// Get or create entry for this client
|
|
173
|
+
let entry = store.get(clientId);
|
|
174
|
+
if (!entry) {
|
|
175
|
+
entry = {
|
|
176
|
+
timestamps: [],
|
|
177
|
+
expiresAt: now + windowMs * 2 // Keep entries for 2 windows for sliding window accuracy
|
|
178
|
+
};
|
|
179
|
+
store.set(clientId, entry);
|
|
180
|
+
}
|
|
181
|
+
// Filter out timestamps outside the current window
|
|
182
|
+
entry.timestamps = entry.timestamps.filter((ts) => ts > windowStart);
|
|
183
|
+
// Update expiration
|
|
184
|
+
entry.expiresAt = now + windowMs * 2;
|
|
185
|
+
// Calculate remaining requests
|
|
186
|
+
const remaining = Math.max(0, config.max - entry.timestamps.length);
|
|
187
|
+
// Calculate reset time (when oldest request in window expires)
|
|
188
|
+
const resetAt = entry.timestamps.length > 0
|
|
189
|
+
? Math.ceil((entry.timestamps[0] + windowMs) / 1000)
|
|
190
|
+
: Math.ceil((now + windowMs) / 1000);
|
|
191
|
+
if (entry.timestamps.length >= config.max) {
|
|
192
|
+
return { remaining: -1, resetAt };
|
|
193
|
+
}
|
|
194
|
+
// Record this request
|
|
195
|
+
entry.timestamps.push(now);
|
|
196
|
+
return { remaining: remaining - 1, resetAt };
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Rate limiting middleware.
|
|
200
|
+
* Only applies rate limiting if TX_API_RATE_LIMIT environment variable is set.
|
|
201
|
+
* If not set, all requests are allowed through.
|
|
202
|
+
*/
|
|
203
|
+
export const rateLimitMiddleware = async (c, next) => {
|
|
204
|
+
// Skip if rate limiting is not enabled
|
|
205
|
+
if (!isRateLimitEnabled()) {
|
|
206
|
+
return next();
|
|
207
|
+
}
|
|
208
|
+
const config = getConfig();
|
|
209
|
+
// Skip health endpoints if configured
|
|
210
|
+
if (config.skipHealthEndpoints) {
|
|
211
|
+
const path = c.req.path;
|
|
212
|
+
if (path === "/health" || path === "/api/health" || path === "/healthz") {
|
|
213
|
+
return next();
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Start cleanup interval
|
|
217
|
+
startCleanup();
|
|
218
|
+
const clientId = getClientId(c);
|
|
219
|
+
const { remaining, resetAt } = checkRateLimit(clientId, config);
|
|
220
|
+
// Set rate limit headers
|
|
221
|
+
c.header("X-RateLimit-Limit", String(config.max));
|
|
222
|
+
c.header("X-RateLimit-Remaining", String(Math.max(0, remaining)));
|
|
223
|
+
c.header("X-RateLimit-Reset", String(resetAt));
|
|
224
|
+
if (remaining < 0) {
|
|
225
|
+
const retryAfter = Math.max(1, resetAt - Math.floor(Date.now() / 1000));
|
|
226
|
+
c.header("Retry-After", String(retryAfter));
|
|
227
|
+
return c.json({
|
|
228
|
+
error: {
|
|
229
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
230
|
+
message: config.message
|
|
231
|
+
}
|
|
232
|
+
}, 429);
|
|
233
|
+
}
|
|
234
|
+
return next();
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* Create rate limit middleware with custom configuration.
|
|
238
|
+
* Useful for applying different limits to specific routes.
|
|
239
|
+
*/
|
|
240
|
+
export const createRateLimitMiddleware = (customConfig) => {
|
|
241
|
+
const store = new Map();
|
|
242
|
+
return async (c, next) => {
|
|
243
|
+
const defaultConfig = getConfig();
|
|
244
|
+
const config = { ...defaultConfig, ...customConfig };
|
|
245
|
+
// Start cleanup interval
|
|
246
|
+
startCleanup();
|
|
247
|
+
const clientId = getClientId(c);
|
|
248
|
+
const now = Date.now();
|
|
249
|
+
const windowMs = config.windowSec * 1000;
|
|
250
|
+
const windowStart = now - windowMs;
|
|
251
|
+
let entry = store.get(clientId);
|
|
252
|
+
if (!entry) {
|
|
253
|
+
entry = {
|
|
254
|
+
timestamps: [],
|
|
255
|
+
expiresAt: now + windowMs * 2
|
|
256
|
+
};
|
|
257
|
+
store.set(clientId, entry);
|
|
258
|
+
}
|
|
259
|
+
entry.timestamps = entry.timestamps.filter((ts) => ts > windowStart);
|
|
260
|
+
entry.expiresAt = now + windowMs * 2;
|
|
261
|
+
const remaining = Math.max(0, config.max - entry.timestamps.length);
|
|
262
|
+
const resetAt = entry.timestamps.length > 0
|
|
263
|
+
? Math.ceil((entry.timestamps[0] + windowMs) / 1000)
|
|
264
|
+
: Math.ceil((now + windowMs) / 1000);
|
|
265
|
+
c.header("X-RateLimit-Limit", String(config.max));
|
|
266
|
+
c.header("X-RateLimit-Remaining", String(Math.max(0, remaining - 1)));
|
|
267
|
+
c.header("X-RateLimit-Reset", String(resetAt));
|
|
268
|
+
if (entry.timestamps.length >= config.max) {
|
|
269
|
+
const retryAfter = Math.max(1, resetAt - Math.floor(Date.now() / 1000));
|
|
270
|
+
c.header("Retry-After", String(retryAfter));
|
|
271
|
+
return c.json({
|
|
272
|
+
error: {
|
|
273
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
274
|
+
message: config.message
|
|
275
|
+
}
|
|
276
|
+
}, 429);
|
|
277
|
+
}
|
|
278
|
+
entry.timestamps.push(now);
|
|
279
|
+
return next();
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
/**
|
|
283
|
+
* Reset rate limit for a specific client.
|
|
284
|
+
* Useful for testing or administrative purposes.
|
|
285
|
+
*/
|
|
286
|
+
export const resetRateLimit = (clientId) => {
|
|
287
|
+
store.delete(clientId);
|
|
288
|
+
};
|
|
289
|
+
/**
|
|
290
|
+
* Clear all rate limit entries.
|
|
291
|
+
* Useful for testing.
|
|
292
|
+
*/
|
|
293
|
+
export const clearAllRateLimits = () => {
|
|
294
|
+
store.clear();
|
|
295
|
+
};
|
|
296
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements attempt endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Tracks task approach outcomes (failed/succeeded).
|
|
6
|
+
*/
|
|
7
|
+
import { AttemptService } from "@jamesaphoenix/tx-core";
|
|
8
|
+
export declare const AttemptsLive: import("effect/Layer").Layer<import("@effect/platform/HttpApiGroup").ApiGroup<"tx", "attempts">, never, AttemptService>;
|
|
9
|
+
//# sourceMappingURL=attempts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempts.d.ts","sourceRoot":"","sources":["../../src/routes/attempts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAOvD,eAAO,MAAM,YAAY,yHAsBxB,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Attempt Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements attempt endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Tracks task approach outcomes (failed/succeeded).
|
|
6
|
+
*/
|
|
7
|
+
import { HttpApiBuilder } from "@effect/platform";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
import { serializeAttempt, assertTaskId, assertAttemptOutcome } from "@jamesaphoenix/tx-types";
|
|
10
|
+
import { AttemptService } from "@jamesaphoenix/tx-core";
|
|
11
|
+
import { TxApi, mapCoreError } from "../api.js";
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
// Handler Layer
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
export const AttemptsLive = HttpApiBuilder.group(TxApi, "attempts", (handlers) => handlers
|
|
16
|
+
.handle("listAttempts", ({ path }) => Effect.gen(function* () {
|
|
17
|
+
const attemptService = yield* AttemptService;
|
|
18
|
+
const attempts = yield* attemptService.listForTask(assertTaskId(path.id));
|
|
19
|
+
return { attempts: attempts.map(serializeAttempt) };
|
|
20
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
21
|
+
.handle("createAttempt", ({ path, payload }) => Effect.gen(function* () {
|
|
22
|
+
const attemptService = yield* AttemptService;
|
|
23
|
+
const attempt = yield* attemptService.create(assertTaskId(path.id), payload.approach, assertAttemptOutcome(payload.outcome), payload.reason ?? null);
|
|
24
|
+
return serializeAttempt(attempt);
|
|
25
|
+
}).pipe(Effect.mapError(mapCoreError))));
|
|
26
|
+
//# sourceMappingURL=attempts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attempts.js","sourceRoot":"","sources":["../../src/routes/attempts.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,gBAAgB,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAA;AAC9F,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE/C,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,YAAY,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,UAAU,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC/E,QAAQ;KACL,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACnC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,cAAc,CAAA;IAC5C,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAA;IACzE,OAAO,EAAE,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,gBAAgB,CAAC,EAAE,CAAA;AACrD,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC;KAEA,MAAM,CAAC,eAAe,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,CAC7C,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,cAAc,CAAA;IAC5C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC,MAAM,CAC1C,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,EACrB,OAAO,CAAC,QAAQ,EAChB,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,EACrC,OAAO,CAAC,MAAM,IAAI,IAAI,CACvB,CAAA;IACD,OAAO,gBAAgB,CAAC,OAAO,CAAC,CAAA;AAClC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC,CACJ,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements claim endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Manages task claim lifecycle with lease-based expiration.
|
|
6
|
+
*/
|
|
7
|
+
import { ClaimService } from "@jamesaphoenix/tx-core";
|
|
8
|
+
export declare const ClaimsLive: import("effect/Layer").Layer<import("@effect/platform/HttpApiGroup").ApiGroup<"tx", "claims">, never, ClaimService>;
|
|
9
|
+
//# sourceMappingURL=claims.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.d.ts","sourceRoot":"","sources":["../../src/routes/claims.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAsBrD,eAAO,MAAM,UAAU,qHA6BtB,CAAA"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements claim endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Manages task claim lifecycle with lease-based expiration.
|
|
6
|
+
*/
|
|
7
|
+
import { HttpApiBuilder } from "@effect/platform";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
import { ClaimService } from "@jamesaphoenix/tx-core";
|
|
10
|
+
import { TxApi, mapCoreError } from "../api.js";
|
|
11
|
+
// -----------------------------------------------------------------------------
|
|
12
|
+
// Serialization Helper
|
|
13
|
+
// -----------------------------------------------------------------------------
|
|
14
|
+
const serializeClaim = (claim) => ({
|
|
15
|
+
id: claim.id,
|
|
16
|
+
taskId: claim.taskId,
|
|
17
|
+
workerId: claim.workerId,
|
|
18
|
+
claimedAt: claim.claimedAt.toISOString(),
|
|
19
|
+
leaseExpiresAt: claim.leaseExpiresAt.toISOString(),
|
|
20
|
+
renewedCount: claim.renewedCount,
|
|
21
|
+
status: claim.status,
|
|
22
|
+
});
|
|
23
|
+
// -----------------------------------------------------------------------------
|
|
24
|
+
// Handler Layer
|
|
25
|
+
// -----------------------------------------------------------------------------
|
|
26
|
+
export const ClaimsLive = HttpApiBuilder.group(TxApi, "claims", (handlers) => handlers
|
|
27
|
+
.handle("createClaim", ({ payload }) => Effect.gen(function* () {
|
|
28
|
+
const claimService = yield* ClaimService;
|
|
29
|
+
const claim = yield* claimService.claim(payload.taskId, payload.workerId, payload.leaseDurationMinutes);
|
|
30
|
+
return serializeClaim(claim);
|
|
31
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
32
|
+
.handle("releaseClaim", ({ path }) => Effect.gen(function* () {
|
|
33
|
+
const claimService = yield* ClaimService;
|
|
34
|
+
yield* claimService.release(path.taskId, path.workerId);
|
|
35
|
+
return { success: true };
|
|
36
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
37
|
+
.handle("renewClaim", ({ path }) => Effect.gen(function* () {
|
|
38
|
+
const claimService = yield* ClaimService;
|
|
39
|
+
const claim = yield* claimService.renew(path.taskId, path.workerId);
|
|
40
|
+
return serializeClaim(claim);
|
|
41
|
+
}).pipe(Effect.mapError(mapCoreError))));
|
|
42
|
+
//# sourceMappingURL=claims.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claims.js","sourceRoot":"","sources":["../../src/routes/claims.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAA;AAErD,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAE/C,gFAAgF;AAChF,uBAAuB;AACvB,gFAAgF;AAEhF,MAAM,cAAc,GAAG,CAAC,KAAgB,EAAE,EAAE,CAAC,CAAC;IAC5C,EAAE,EAAE,KAAK,CAAC,EAAE;IACZ,MAAM,EAAE,KAAK,CAAC,MAAM;IACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;IACxB,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,WAAW,EAAE;IACxC,cAAc,EAAE,KAAK,CAAC,cAAc,CAAC,WAAW,EAAE;IAClD,YAAY,EAAE,KAAK,CAAC,YAAY;IAChC,MAAM,EAAE,KAAK,CAAC,MAAM;CACrB,CAAC,CAAA;AAEF,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC3E,QAAQ;KACL,MAAM,CAAC,aAAa,EAAE,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CACrC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,YAAY,CAAA;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,KAAK,CACrC,OAAO,CAAC,MAAM,EACd,OAAO,CAAC,QAAQ,EAChB,OAAO,CAAC,oBAAoB,CAC7B,CAAA;IACD,OAAO,cAAc,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC;KAEA,MAAM,CAAC,cAAc,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACnC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,YAAY,CAAA;IACxC,KAAK,CAAC,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IACvD,OAAO,EAAE,OAAO,EAAE,IAAa,EAAE,CAAA;AACnC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC;KAEA,MAAM,CAAC,YAAY,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CACjC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,YAAY,GAAG,KAAK,CAAC,CAAC,YAAY,CAAA;IACxC,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAA;IACnE,OAAO,cAAc,CAAC,KAAK,CAAC,CAAA;AAC9B,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC,CACJ,CAAA"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doc Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements doc endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Manages doc lifecycle, rendering, and linking.
|
|
6
|
+
*/
|
|
7
|
+
import { DocService } from "@jamesaphoenix/tx-core";
|
|
8
|
+
export declare const DocsLive: import("effect/Layer").Layer<import("@effect/platform/HttpApiGroup").ApiGroup<"tx", "docs">, never, DocService>;
|
|
9
|
+
//# sourceMappingURL=docs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../src/routes/docs.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AA2BnD,eAAO,MAAM,QAAQ,iHA0EpB,CAAA"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doc Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements doc endpoint handlers using Effect HttpApiBuilder.
|
|
5
|
+
* Manages doc lifecycle, rendering, and linking.
|
|
6
|
+
*/
|
|
7
|
+
import { HttpApiBuilder } from "@effect/platform";
|
|
8
|
+
import { Effect } from "effect";
|
|
9
|
+
import { DocService } from "@jamesaphoenix/tx-core";
|
|
10
|
+
import { assertDocKind, assertDocStatus, assertDocLinkType } from "@jamesaphoenix/tx-types";
|
|
11
|
+
import { TxApi, mapCoreError } from "../api.js";
|
|
12
|
+
// -----------------------------------------------------------------------------
|
|
13
|
+
// Serialization Helper
|
|
14
|
+
// -----------------------------------------------------------------------------
|
|
15
|
+
const serializeDoc = (doc) => ({
|
|
16
|
+
id: doc.id,
|
|
17
|
+
kind: doc.kind,
|
|
18
|
+
name: doc.name,
|
|
19
|
+
title: doc.title,
|
|
20
|
+
version: doc.version,
|
|
21
|
+
status: doc.status,
|
|
22
|
+
filePath: doc.filePath ?? null,
|
|
23
|
+
hash: doc.hash,
|
|
24
|
+
createdAt: doc.createdAt.toISOString(),
|
|
25
|
+
updatedAt: null,
|
|
26
|
+
lockedAt: doc.lockedAt?.toISOString() ?? null,
|
|
27
|
+
});
|
|
28
|
+
// -----------------------------------------------------------------------------
|
|
29
|
+
// Handler Layer
|
|
30
|
+
// -----------------------------------------------------------------------------
|
|
31
|
+
export const DocsLive = HttpApiBuilder.group(TxApi, "docs", (handlers) => handlers
|
|
32
|
+
.handle("listDocs", ({ urlParams }) => Effect.gen(function* () {
|
|
33
|
+
const docService = yield* DocService;
|
|
34
|
+
const filter = {};
|
|
35
|
+
if (urlParams.kind) {
|
|
36
|
+
filter.kind = assertDocKind(urlParams.kind);
|
|
37
|
+
}
|
|
38
|
+
if (urlParams.status) {
|
|
39
|
+
filter.status = assertDocStatus(urlParams.status);
|
|
40
|
+
}
|
|
41
|
+
const docs = yield* docService.list(filter);
|
|
42
|
+
return { docs: docs.map(serializeDoc) };
|
|
43
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
44
|
+
.handle("getDoc", ({ path }) => Effect.gen(function* () {
|
|
45
|
+
const docService = yield* DocService;
|
|
46
|
+
const doc = yield* docService.get(path.name);
|
|
47
|
+
return serializeDoc(doc);
|
|
48
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
49
|
+
.handle("createDoc", ({ payload }) => Effect.gen(function* () {
|
|
50
|
+
const docService = yield* DocService;
|
|
51
|
+
const doc = yield* docService.create({
|
|
52
|
+
kind: payload.kind,
|
|
53
|
+
name: payload.name,
|
|
54
|
+
title: payload.title,
|
|
55
|
+
yamlContent: payload.yamlContent,
|
|
56
|
+
metadata: payload.metadata,
|
|
57
|
+
});
|
|
58
|
+
return serializeDoc(doc);
|
|
59
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
60
|
+
.handle("updateDoc", ({ path, payload }) => Effect.gen(function* () {
|
|
61
|
+
const docService = yield* DocService;
|
|
62
|
+
const doc = yield* docService.update(path.name, payload.yamlContent);
|
|
63
|
+
return serializeDoc(doc);
|
|
64
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
65
|
+
.handle("lockDoc", ({ path }) => Effect.gen(function* () {
|
|
66
|
+
const docService = yield* DocService;
|
|
67
|
+
const doc = yield* docService.lock(path.name);
|
|
68
|
+
return serializeDoc(doc);
|
|
69
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
70
|
+
.handle("linkDoc", ({ path, payload }) => Effect.gen(function* () {
|
|
71
|
+
const docService = yield* DocService;
|
|
72
|
+
yield* docService.linkDocs(path.name, payload.toName, payload.linkType ? assertDocLinkType(payload.linkType) : undefined);
|
|
73
|
+
return { success: true };
|
|
74
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
75
|
+
.handle("renderDoc", ({ path }) => Effect.gen(function* () {
|
|
76
|
+
const docService = yield* DocService;
|
|
77
|
+
const rendered = yield* docService.render(path.name);
|
|
78
|
+
return { rendered };
|
|
79
|
+
}).pipe(Effect.mapError(mapCoreError))));
|
|
80
|
+
//# sourceMappingURL=docs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docs.js","sourceRoot":"","sources":["../../src/routes/docs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements health check and stats endpoint handlers.
|
|
5
|
+
*/
|
|
6
|
+
import { TaskService, LearningService, RunRepository } from "@jamesaphoenix/tx-core";
|
|
7
|
+
export declare const HealthLive: import("effect/Layer").Layer<import("@effect/platform/HttpApiGroup").ApiGroup<"tx", "health">, never, TaskService | LearningService | RunRepository>;
|
|
8
|
+
//# sourceMappingURL=health.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.d.ts","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAQpF,eAAO,MAAM,UAAU,sJAkHtB,CAAA"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Route Handlers
|
|
3
|
+
*
|
|
4
|
+
* Implements health check and stats endpoint handlers.
|
|
5
|
+
*/
|
|
6
|
+
import { HttpApiBuilder, HttpServerRequest } from "@effect/platform";
|
|
7
|
+
import { Effect } from "effect";
|
|
8
|
+
import { TaskService, LearningService, RunRepository } from "@jamesaphoenix/tx-core";
|
|
9
|
+
import { TxApi, mapCoreError } from "../api.js";
|
|
10
|
+
import { extractApiKey, timingSafeEqual } from "../middleware/auth.js";
|
|
11
|
+
// -----------------------------------------------------------------------------
|
|
12
|
+
// Handler Layer
|
|
13
|
+
// -----------------------------------------------------------------------------
|
|
14
|
+
export const HealthLive = HttpApiBuilder.group(TxApi, "health", (handlers) => handlers
|
|
15
|
+
.handle("health", () => Effect.gen(function* () {
|
|
16
|
+
let dbConnected = true;
|
|
17
|
+
const result = yield* Effect.gen(function* () {
|
|
18
|
+
const taskService = yield* TaskService;
|
|
19
|
+
yield* taskService.listWithDeps({ limit: 1 });
|
|
20
|
+
}).pipe(Effect.either);
|
|
21
|
+
if (result._tag === "Left") {
|
|
22
|
+
dbConnected = false;
|
|
23
|
+
}
|
|
24
|
+
// Only expose database path when auth is enabled AND request is authenticated
|
|
25
|
+
const showSensitive = yield* Effect.gen(function* () {
|
|
26
|
+
const apiKey = process.env.TX_API_KEY;
|
|
27
|
+
if (!apiKey)
|
|
28
|
+
return false;
|
|
29
|
+
const request = yield* HttpServerRequest.HttpServerRequest;
|
|
30
|
+
const providedKey = extractApiKey(request.headers);
|
|
31
|
+
if (!providedKey)
|
|
32
|
+
return false;
|
|
33
|
+
return timingSafeEqual(providedKey, apiKey);
|
|
34
|
+
});
|
|
35
|
+
const dbPath = showSensitive
|
|
36
|
+
? (process.env.TX_DB_PATH ?? ".tx/tasks.db")
|
|
37
|
+
: null;
|
|
38
|
+
return {
|
|
39
|
+
status: dbConnected ? "healthy" : "degraded",
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
version: "0.1.0",
|
|
42
|
+
database: {
|
|
43
|
+
connected: dbConnected,
|
|
44
|
+
path: dbPath,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
}))
|
|
48
|
+
.handle("stats", () => Effect.gen(function* () {
|
|
49
|
+
const taskService = yield* TaskService;
|
|
50
|
+
const learningService = yield* LearningService;
|
|
51
|
+
const runRepo = yield* RunRepository;
|
|
52
|
+
const allTasks = yield* taskService.listWithDeps({});
|
|
53
|
+
const learningsCount = yield* learningService.count();
|
|
54
|
+
const runCounts = yield* runRepo.countByStatus();
|
|
55
|
+
let done = 0;
|
|
56
|
+
let ready = 0;
|
|
57
|
+
for (const task of allTasks) {
|
|
58
|
+
if (task.status === "done")
|
|
59
|
+
done++;
|
|
60
|
+
if (task.isReady)
|
|
61
|
+
ready++;
|
|
62
|
+
}
|
|
63
|
+
const runsTotal = Object.values(runCounts).reduce((a, b) => a + b, 0);
|
|
64
|
+
return {
|
|
65
|
+
tasks: allTasks.length,
|
|
66
|
+
done,
|
|
67
|
+
ready,
|
|
68
|
+
learnings: learningsCount,
|
|
69
|
+
runsRunning: runCounts.running ?? 0,
|
|
70
|
+
runsTotal,
|
|
71
|
+
};
|
|
72
|
+
}).pipe(Effect.mapError(mapCoreError)))
|
|
73
|
+
.handle("ralph", () => Effect.gen(function* () {
|
|
74
|
+
const fs = yield* Effect.promise(() => import("node:fs"));
|
|
75
|
+
const path = yield* Effect.promise(() => import("node:path"));
|
|
76
|
+
const stateFile = path.join(process.cwd(), ".tx", "ralph-state");
|
|
77
|
+
let running = false;
|
|
78
|
+
let pid = null;
|
|
79
|
+
let currentIteration = 0;
|
|
80
|
+
let currentTask = null;
|
|
81
|
+
try {
|
|
82
|
+
if (fs.existsSync(stateFile)) {
|
|
83
|
+
const state = JSON.parse(fs.readFileSync(stateFile, "utf-8"));
|
|
84
|
+
running = state.running ?? false;
|
|
85
|
+
pid = state.pid ?? null;
|
|
86
|
+
currentIteration = state.iteration ?? 0;
|
|
87
|
+
currentTask = state.currentTask ?? null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
console.warn(`[health] Failed to parse RALPH state file at ${stateFile}:`, error instanceof Error ? error.message : String(error));
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
running,
|
|
95
|
+
pid,
|
|
96
|
+
currentIteration,
|
|
97
|
+
currentTask,
|
|
98
|
+
recentActivity: [],
|
|
99
|
+
};
|
|
100
|
+
})));
|
|
101
|
+
//# sourceMappingURL=health.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health.js","sourceRoot":"","sources":["../../src/routes/health.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,cAAc,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC/B,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACpF,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAC/C,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEtE,gFAAgF;AAChF,gBAAgB;AAChB,gFAAgF;AAEhF,MAAM,CAAC,MAAM,UAAU,GAAG,cAAc,CAAC,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,CAAC,QAAQ,EAAE,EAAE,CAC3E,QAAQ;KACL,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,CACrB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,IAAI,WAAW,GAAG,IAAI,CAAA;IAEtB,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACxC,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAA;QACtC,KAAK,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC,CAAA;IAC/C,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAEtB,IAAI,MAAM,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;QAC3B,WAAW,GAAG,KAAK,CAAA;IACrB,CAAC;IAED,8EAA8E;IAC9E,MAAM,aAAa,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC/C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,CAAA;QACrC,IAAI,CAAC,MAAM;YAAE,OAAO,KAAK,CAAA;QAEzB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,iBAAiB,CAAA;QAC1D,MAAM,WAAW,GAAG,aAAa,CAAC,OAAO,CAAC,OAAwD,CAAC,CAAA;QACnG,IAAI,CAAC,WAAW;YAAE,OAAO,KAAK,CAAA;QAE9B,OAAO,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IACF,MAAM,MAAM,GAAG,aAAa;QAC1B,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc,CAAC;QAC5C,CAAC,CAAC,IAAI,CAAA;IAER,OAAO;QACL,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,SAAkB,CAAC,CAAC,CAAC,UAAmB;QAC9D,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACnC,OAAO,EAAE,OAAO;QAChB,QAAQ,EAAE;YACR,SAAS,EAAE,WAAW;YACtB,IAAI,EAAE,MAAM;SACb;KACF,CAAA;AACH,CAAC,CAAC,CACH;KAEA,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,CACpB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,WAAW,CAAA;IACtC,MAAM,eAAe,GAAG,KAAK,CAAC,CAAC,eAAe,CAAA;IAC9C,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,aAAa,CAAA;IAEpC,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;IACpD,MAAM,cAAc,GAAG,KAAK,CAAC,CAAC,eAAe,CAAC,KAAK,EAAE,CAAA;IACrD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,OAAO,CAAC,aAAa,EAAE,CAAA;IAEhD,IAAI,IAAI,GAAG,CAAC,CAAA;IACZ,IAAI,KAAK,GAAG,CAAC,CAAA;IACb,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,IAAI,IAAI,CAAC,MAAM,KAAK,MAAM;YAAE,IAAI,EAAE,CAAA;QAClC,IAAI,IAAI,CAAC,OAAO;YAAE,KAAK,EAAE,CAAA;IAC3B,CAAC;IAED,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAA;IAErE,OAAO;QACL,KAAK,EAAE,QAAQ,CAAC,MAAM;QACtB,IAAI;QACJ,KAAK;QACL,SAAS,EAAE,cAAc;QACzB,WAAW,EAAE,SAAS,CAAC,OAAO,IAAI,CAAC;QACnC,SAAS;KACV,CAAA;AACH,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC,CACvC;KAEA,MAAM,CAAC,OAAO,EAAE,GAAG,EAAE,CACpB,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;IAClB,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAA;IACzD,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAA;IAE7D,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;IAChE,IAAI,OAAO,GAAG,KAAK,CAAA;IACnB,IAAI,GAAG,GAAkB,IAAI,CAAA;IAC7B,IAAI,gBAAgB,GAAG,CAAC,CAAA;IACxB,IAAI,WAAW,GAAkB,IAAI,CAAA;IAErC,IAAI,CAAC;QACH,IAAI,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC7B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAA;YAC7D,OAAO,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAA;YAChC,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,IAAI,CAAA;YACvB,gBAAgB,GAAG,KAAK,CAAC,SAAS,IAAI,CAAC,CAAA;YACvC,WAAW,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAA;QACzC,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CACV,gDAAgD,SAAS,GAAG,EAC5D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CACvD,CAAA;IACH,CAAC;IAED,OAAO;QACL,OAAO;QACP,GAAG;QACH,gBAAgB;QAChB,WAAW;QACX,cAAc,EAAE,EAOd;KACH,CAAA;AACH,CAAC,CAAC,CACH,CACJ,CAAA"}
|