@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.
Files changed (109) hide show
  1. package/dist/__tests__/api.test.d.ts +7 -0
  2. package/dist/__tests__/api.test.d.ts.map +1 -0
  3. package/dist/__tests__/api.test.js +184 -0
  4. package/dist/__tests__/api.test.js.map +1 -0
  5. package/dist/__tests__/auth.test.d.ts +7 -0
  6. package/dist/__tests__/auth.test.d.ts.map +1 -0
  7. package/dist/__tests__/auth.test.js +174 -0
  8. package/dist/__tests__/auth.test.js.map +1 -0
  9. package/dist/__tests__/body-limit.test.d.ts +7 -0
  10. package/dist/__tests__/body-limit.test.d.ts.map +1 -0
  11. package/dist/__tests__/body-limit.test.js +60 -0
  12. package/dist/__tests__/body-limit.test.js.map +1 -0
  13. package/dist/__tests__/health.test.d.ts.map +1 -0
  14. package/dist/__tests__/health.test.js.map +1 -0
  15. package/dist/__tests__/log-reader.test.d.ts +8 -0
  16. package/dist/__tests__/log-reader.test.d.ts.map +1 -0
  17. package/dist/__tests__/log-reader.test.js +55 -0
  18. package/dist/__tests__/log-reader.test.js.map +1 -0
  19. package/dist/__tests__/rate-limit.test.d.ts.map +1 -0
  20. package/dist/__tests__/rate-limit.test.js.map +1 -0
  21. package/dist/__tests__/server-lib.test.d.ts +10 -0
  22. package/dist/__tests__/server-lib.test.d.ts.map +1 -0
  23. package/dist/__tests__/server-lib.test.js +145 -0
  24. package/dist/__tests__/server-lib.test.js.map +1 -0
  25. package/dist/api.d.ts +1616 -0
  26. package/dist/api.d.ts.map +1 -0
  27. package/dist/api.js +566 -0
  28. package/dist/api.js.map +1 -0
  29. package/dist/index.d.ts +16 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +19 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/middleware/auth.d.ts +31 -0
  34. package/dist/middleware/auth.d.ts.map +1 -0
  35. package/dist/middleware/auth.js +65 -0
  36. package/dist/middleware/auth.js.map +1 -0
  37. package/dist/middleware/body-limit.d.ts +33 -0
  38. package/dist/middleware/body-limit.d.ts.map +1 -0
  39. package/dist/middleware/body-limit.js +50 -0
  40. package/dist/middleware/body-limit.js.map +1 -0
  41. package/dist/middleware/cors.d.ts +15 -0
  42. package/dist/middleware/cors.d.ts.map +1 -0
  43. package/dist/middleware/cors.js +31 -0
  44. package/dist/middleware/cors.js.map +1 -0
  45. package/dist/middleware/error.d.ts +26 -0
  46. package/dist/middleware/error.d.ts.map +1 -0
  47. package/dist/middleware/error.js +84 -0
  48. package/dist/middleware/error.js.map +1 -0
  49. package/dist/middleware/rate-limit.d.ts +59 -0
  50. package/dist/middleware/rate-limit.d.ts.map +1 -0
  51. package/dist/middleware/rate-limit.js +296 -0
  52. package/dist/middleware/rate-limit.js.map +1 -0
  53. package/dist/routes/attempts.d.ts +9 -0
  54. package/dist/routes/attempts.d.ts.map +1 -0
  55. package/dist/routes/attempts.js +26 -0
  56. package/dist/routes/attempts.js.map +1 -0
  57. package/dist/routes/claims.d.ts +9 -0
  58. package/dist/routes/claims.d.ts.map +1 -0
  59. package/dist/routes/claims.js +42 -0
  60. package/dist/routes/claims.js.map +1 -0
  61. package/dist/routes/docs.d.ts +9 -0
  62. package/dist/routes/docs.d.ts.map +1 -0
  63. package/dist/routes/docs.js +80 -0
  64. package/dist/routes/docs.js.map +1 -0
  65. package/dist/routes/health.d.ts +8 -0
  66. package/dist/routes/health.d.ts.map +1 -0
  67. package/dist/routes/health.js +101 -0
  68. package/dist/routes/health.js.map +1 -0
  69. package/dist/routes/invariants.d.ts +9 -0
  70. package/dist/routes/invariants.d.ts.map +1 -0
  71. package/dist/routes/invariants.js +64 -0
  72. package/dist/routes/invariants.js.map +1 -0
  73. package/dist/routes/learnings.d.ts +8 -0
  74. package/dist/routes/learnings.d.ts.map +1 -0
  75. package/dist/routes/learnings.js +93 -0
  76. package/dist/routes/learnings.js.map +1 -0
  77. package/dist/routes/runs.d.ts +8 -0
  78. package/dist/routes/runs.d.ts.map +1 -0
  79. package/dist/routes/runs.js +195 -0
  80. package/dist/routes/runs.js.map +1 -0
  81. package/dist/routes/sync.d.ts +8 -0
  82. package/dist/routes/sync.d.ts.map +1 -0
  83. package/dist/routes/sync.js +67 -0
  84. package/dist/routes/sync.js.map +1 -0
  85. package/dist/routes/tasks.d.ts +9 -0
  86. package/dist/routes/tasks.d.ts.map +1 -0
  87. package/dist/routes/tasks.js +167 -0
  88. package/dist/routes/tasks.js.map +1 -0
  89. package/dist/runtime.d.ts +10 -0
  90. package/dist/runtime.d.ts.map +1 -0
  91. package/dist/runtime.js +10 -0
  92. package/dist/runtime.js.map +1 -0
  93. package/dist/server-lib.d.ts +26 -0
  94. package/dist/server-lib.d.ts.map +1 -0
  95. package/dist/server-lib.js +123 -0
  96. package/dist/server-lib.js.map +1 -0
  97. package/dist/server.d.ts +14 -0
  98. package/dist/server.d.ts.map +1 -0
  99. package/dist/server.js +15 -0
  100. package/dist/server.js.map +1 -0
  101. package/dist/utils/log-reader.d.ts +26 -0
  102. package/dist/utils/log-reader.d.ts.map +1 -0
  103. package/dist/utils/log-reader.js +52 -0
  104. package/dist/utils/log-reader.js.map +1 -0
  105. package/dist/utils/transcript-parser.d.ts +40 -0
  106. package/dist/utils/transcript-parser.d.ts.map +1 -0
  107. package/dist/utils/transcript-parser.js +216 -0
  108. package/dist/utils/transcript-parser.js.map +1 -0
  109. 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":"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"}