@stacknet/stacks 0.1.2 → 0.2.2

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 (67) hide show
  1. package/README.md +136 -0
  2. package/dist/{billing-BqscteyZ.d.cts → billing-cj0eSVrp.d.cts} +61 -1
  3. package/dist/{billing-BqscteyZ.d.ts → billing-cj0eSVrp.d.ts} +61 -1
  4. package/dist/clients/index.cjs +4 -4
  5. package/dist/clients/index.d.cts +27 -1
  6. package/dist/clients/index.d.ts +27 -1
  7. package/dist/clients/index.js +4 -4
  8. package/dist/{index-DVzKiF_0.d.cts → index-B_dUFmAg.d.cts} +31 -6
  9. package/dist/{index-DVzKiF_0.d.ts → index-B_dUFmAg.d.ts} +31 -6
  10. package/dist/index.cjs +12 -16
  11. package/dist/index.d.cts +4 -4
  12. package/dist/index.d.ts +4 -4
  13. package/dist/index.js +12 -16
  14. package/dist/proxy/index.cjs +2 -2
  15. package/dist/proxy/index.d.cts +1 -1
  16. package/dist/proxy/index.d.ts +1 -1
  17. package/dist/proxy/index.js +2 -2
  18. package/dist/streaming/index.cjs +8 -12
  19. package/dist/streaming/index.js +8 -12
  20. package/dist/types/index.d.cts +1 -1
  21. package/dist/types/index.d.ts +1 -1
  22. package/package.json +15 -13
  23. package/src/clients/agents.ts +0 -233
  24. package/src/clients/billing.ts +0 -197
  25. package/src/clients/coder.ts +0 -655
  26. package/src/clients/files.ts +0 -86
  27. package/src/clients/index.ts +0 -93
  28. package/src/clients/magma.ts +0 -299
  29. package/src/clients/mcp.ts +0 -208
  30. package/src/clients/network.ts +0 -118
  31. package/src/clients/points.ts +0 -403
  32. package/src/clients/skills.ts +0 -236
  33. package/src/clients/social.ts +0 -286
  34. package/src/clients/stack-management.ts +0 -279
  35. package/src/clients/task-network.ts +0 -303
  36. package/src/clients/user.ts +0 -84
  37. package/src/clients/widgets.ts +0 -171
  38. package/src/index.ts +0 -387
  39. package/src/managers/index.ts +0 -10
  40. package/src/managers/task-manager.ts +0 -310
  41. package/src/proxy/forwarder.ts +0 -146
  42. package/src/proxy/index.ts +0 -32
  43. package/src/proxy/route-handlers.ts +0 -950
  44. package/src/streaming/component-stream.ts +0 -319
  45. package/src/streaming/index.ts +0 -21
  46. package/src/streaming/sse.ts +0 -241
  47. package/src/types/agent.ts +0 -106
  48. package/src/types/billing.ts +0 -121
  49. package/src/types/chat.ts +0 -58
  50. package/src/types/coder.ts +0 -345
  51. package/src/types/credential.ts +0 -111
  52. package/src/types/file.ts +0 -15
  53. package/src/types/imagination.ts +0 -50
  54. package/src/types/index.ts +0 -20
  55. package/src/types/mcp.ts +0 -35
  56. package/src/types/network.ts +0 -97
  57. package/src/types/points.ts +0 -250
  58. package/src/types/skill.ts +0 -107
  59. package/src/types/social.ts +0 -109
  60. package/src/types/stack.ts +0 -269
  61. package/src/types/task.ts +0 -41
  62. package/src/types/user.ts +0 -29
  63. package/src/types/widget.ts +0 -57
  64. package/src/utils/constants.ts +0 -26
  65. package/src/utils/errors.ts +0 -169
  66. package/src/utils/helpers.ts +0 -85
  67. package/src/utils/index.ts +0 -7
@@ -1,950 +0,0 @@
1
- /**
2
- * Next.js API Route Handler Factories
3
- *
4
- * Create pre-configured route handlers for common patterns
5
- */
6
-
7
- import { forwardRequest, forwardJSON, type ForwarderConfig } from './forwarder';
8
-
9
- export interface RouteHandlerConfig extends ForwarderConfig {
10
- requireAuth?: boolean;
11
- enrichResponse?: (data: unknown) => Promise<unknown>;
12
- }
13
-
14
- export type RouteHandler = (request: Request, context?: { params?: Record<string, string> }) => Promise<Response>;
15
-
16
- export interface CRUDRouteHandlers {
17
- GET: RouteHandler;
18
- POST: RouteHandler;
19
- PUT?: RouteHandler;
20
- DELETE?: RouteHandler;
21
- }
22
-
23
- /**
24
- * Create agent route handlers
25
- */
26
- export function createAgentRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
27
- const baseUrl = config.baseUrl;
28
-
29
- return {
30
- GET: async (request: Request) => {
31
- const url = new URL(request.url);
32
- const userMid = url.searchParams.get('userMid');
33
- const visibility = url.searchParams.get('visibility');
34
-
35
- const path = userMid
36
- ? `/agents?creator_mid=${encodeURIComponent(userMid)}`
37
- : visibility
38
- ? `/agents?visibility=${visibility}`
39
- : '/agents';
40
-
41
- const { data, status } = await forwardJSON(path, {}, { baseUrl });
42
-
43
- // Optional response enrichment
44
- let responseData = data;
45
- if (config.enrichResponse) {
46
- responseData = await config.enrichResponse(data);
47
- }
48
-
49
- return Response.json(responseData, { status });
50
- },
51
-
52
- POST: async (request: Request) => {
53
- const body = await request.json();
54
- const { data, status } = await forwardJSON(
55
- '/agents',
56
- { method: 'POST', body },
57
- { baseUrl }
58
- );
59
- return Response.json(data, { status });
60
- },
61
- };
62
- }
63
-
64
- /**
65
- * Create agent detail route handlers (for [id] routes)
66
- */
67
- export function createAgentDetailRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
68
- const baseUrl = config.baseUrl;
69
-
70
- return {
71
- GET: async (request: Request, context?: { params?: { id?: string } }) => {
72
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
73
- const { data, status } = await forwardJSON(`/agents/${id}`, {}, { baseUrl });
74
-
75
- let responseData = data;
76
- if (config.enrichResponse) {
77
- responseData = await config.enrichResponse(data);
78
- }
79
-
80
- return Response.json(responseData, { status });
81
- },
82
-
83
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
84
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
85
- const body = await request.json();
86
- const { data, status } = await forwardJSON(
87
- `/agents/${id}`,
88
- { method: 'POST', body },
89
- { baseUrl }
90
- );
91
- return Response.json(data, { status });
92
- },
93
-
94
- PUT: async (request: Request, context?: { params?: { id?: string } }) => {
95
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
96
- const body = await request.json();
97
- const { data, status } = await forwardJSON(
98
- `/agents/${id}`,
99
- { method: 'PUT', body },
100
- { baseUrl }
101
- );
102
- return Response.json(data, { status });
103
- },
104
-
105
- DELETE: async (request: Request, context?: { params?: { id?: string } }) => {
106
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
107
- const { data, status } = await forwardJSON(
108
- `/agents/${id}`,
109
- { method: 'DELETE' },
110
- { baseUrl }
111
- );
112
- return Response.json(data, { status });
113
- },
114
- };
115
- }
116
-
117
- /**
118
- * Create agent execute route handler
119
- */
120
- export function createAgentExecuteRoute(config: RouteHandlerConfig = {}): { POST: RouteHandler } {
121
- const baseUrl = config.baseUrl;
122
-
123
- return {
124
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
125
- const id = context?.params?.id || new URL(request.url).pathname.split('/').slice(-2)[0];
126
- const body = await request.json();
127
-
128
- const response = await forwardRequest(
129
- `/agents/${id}/execute`,
130
- { method: 'POST', body, stream: true },
131
- { baseUrl }
132
- );
133
-
134
- // Check if streaming
135
- const contentType = response.headers.get('content-type') || '';
136
- if (contentType.includes('text/event-stream') && response.body) {
137
- return new Response(response.body, {
138
- status: response.status,
139
- headers: {
140
- 'Content-Type': 'text/event-stream',
141
- 'Cache-Control': 'no-cache',
142
- 'Connection': 'keep-alive',
143
- },
144
- });
145
- }
146
-
147
- const data = await response.json();
148
- return Response.json(data, { status: response.status });
149
- },
150
- };
151
- }
152
-
153
- /**
154
- * Create agent enable/disable route handlers
155
- */
156
- export function createAgentToggleRoutes(config: RouteHandlerConfig = {}): {
157
- enable: { POST: RouteHandler };
158
- disable: { POST: RouteHandler };
159
- } {
160
- const baseUrl = config.baseUrl;
161
-
162
- return {
163
- enable: {
164
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
165
- const id = context?.params?.id || new URL(request.url).pathname.split('/').slice(-2)[0];
166
- const { data, status } = await forwardJSON(
167
- `/agents/${id}/enable`,
168
- { method: 'POST' },
169
- { baseUrl }
170
- );
171
- return Response.json(data, { status });
172
- },
173
- },
174
- disable: {
175
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
176
- const id = context?.params?.id || new URL(request.url).pathname.split('/').slice(-2)[0];
177
- const { data, status } = await forwardJSON(
178
- `/agents/${id}/disable`,
179
- { method: 'POST' },
180
- { baseUrl }
181
- );
182
- return Response.json(data, { status });
183
- },
184
- },
185
- };
186
- }
187
-
188
- /**
189
- * Create skill route handlers
190
- */
191
- export function createSkillRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
192
- const baseUrl = config.baseUrl;
193
-
194
- return {
195
- GET: async (request: Request) => {
196
- const url = new URL(request.url);
197
- const scope = url.searchParams.get('scope');
198
- const creatorMid = url.searchParams.get('creator_mid');
199
-
200
- let path = '/skills';
201
- if (scope) path += `?scope=${encodeURIComponent(scope)}`;
202
- else if (creatorMid) path += `?creator_mid=${encodeURIComponent(creatorMid)}`;
203
-
204
- const { data, status } = await forwardJSON(path, {}, { baseUrl });
205
- return Response.json(data, { status });
206
- },
207
-
208
- POST: async (request: Request) => {
209
- const body = await request.json();
210
- const { data, status } = await forwardJSON(
211
- '/skills',
212
- { method: 'POST', body },
213
- { baseUrl }
214
- );
215
- return Response.json(data, { status });
216
- },
217
- };
218
- }
219
-
220
- /**
221
- * Create skill detail route handlers
222
- */
223
- export function createSkillDetailRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
224
- const baseUrl = config.baseUrl;
225
-
226
- return {
227
- GET: async (request: Request, context?: { params?: { id?: string } }) => {
228
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
229
- const { data, status } = await forwardJSON(`/skills/${id}`, {}, { baseUrl });
230
- return Response.json(data, { status });
231
- },
232
-
233
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
234
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
235
- const body = await request.json();
236
- const { data, status } = await forwardJSON(
237
- `/skills/${id}`,
238
- { method: 'POST', body },
239
- { baseUrl }
240
- );
241
- return Response.json(data, { status });
242
- },
243
-
244
- PUT: async (request: Request, context?: { params?: { id?: string } }) => {
245
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
246
- const body = await request.json();
247
- const { data, status } = await forwardJSON(
248
- `/skills/${id}`,
249
- { method: 'PUT', body },
250
- { baseUrl }
251
- );
252
- return Response.json(data, { status });
253
- },
254
-
255
- DELETE: async (request: Request, context?: { params?: { id?: string } }) => {
256
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
257
- const { data, status } = await forwardJSON(
258
- `/skills/${id}`,
259
- { method: 'DELETE' },
260
- { baseUrl }
261
- );
262
- return Response.json(data, { status });
263
- },
264
- };
265
- }
266
-
267
- /**
268
- * Create widget route handlers (same pattern as skills)
269
- */
270
- export function createWidgetRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
271
- const baseUrl = config.baseUrl;
272
-
273
- return {
274
- GET: async (request: Request) => {
275
- const url = new URL(request.url);
276
- const scope = url.searchParams.get('scope');
277
- const creatorMid = url.searchParams.get('creator_mid');
278
-
279
- let path = '/widgets';
280
- if (scope) path += `?scope=${encodeURIComponent(scope)}`;
281
- else if (creatorMid) path += `?creator_mid=${encodeURIComponent(creatorMid)}`;
282
-
283
- const { data, status } = await forwardJSON(path, {}, { baseUrl });
284
- return Response.json(data, { status });
285
- },
286
-
287
- POST: async (request: Request) => {
288
- const body = await request.json();
289
- const { data, status } = await forwardJSON(
290
- '/widgets',
291
- { method: 'POST', body },
292
- { baseUrl }
293
- );
294
- return Response.json(data, { status });
295
- },
296
- };
297
- }
298
-
299
- /**
300
- * Create widget detail route handlers
301
- */
302
- export function createWidgetDetailRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
303
- const baseUrl = config.baseUrl;
304
-
305
- return {
306
- GET: async (request: Request, context?: { params?: { id?: string } }) => {
307
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
308
- const { data, status } = await forwardJSON(`/widgets/${id}`, {}, { baseUrl });
309
- return Response.json(data, { status });
310
- },
311
-
312
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
313
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
314
- const body = await request.json();
315
- const { data, status } = await forwardJSON(
316
- `/widgets/${id}`,
317
- { method: 'POST', body },
318
- { baseUrl }
319
- );
320
- return Response.json(data, { status });
321
- },
322
-
323
- PUT: async (request: Request, context?: { params?: { id?: string } }) => {
324
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
325
- const body = await request.json();
326
- const { data, status } = await forwardJSON(
327
- `/widgets/${id}`,
328
- { method: 'PUT', body },
329
- { baseUrl }
330
- );
331
- return Response.json(data, { status });
332
- },
333
-
334
- DELETE: async (request: Request, context?: { params?: { id?: string } }) => {
335
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
336
- const { data, status } = await forwardJSON(
337
- `/widgets/${id}`,
338
- { method: 'DELETE' },
339
- { baseUrl }
340
- );
341
- return Response.json(data, { status });
342
- },
343
- };
344
- }
345
-
346
- /**
347
- * Create imagination route handlers
348
- */
349
- export function createImaginationRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
350
- const baseUrl = config.baseUrl;
351
-
352
- return {
353
- GET: async (request: Request) => {
354
- const url = new URL(request.url);
355
- const id = url.searchParams.get('id');
356
- const creatorMid = url.searchParams.get('creator_mid');
357
-
358
- let path = '/imaginations';
359
- if (id) path = `/imaginations/${id}`;
360
- else if (creatorMid) path += `?creator_mid=${encodeURIComponent(creatorMid)}`;
361
-
362
- const { data, status } = await forwardJSON(path, {}, { baseUrl });
363
- return Response.json(data, { status });
364
- },
365
-
366
- POST: async (request: Request) => {
367
- const body = await request.json();
368
- const { data, status } = await forwardJSON(
369
- '/imaginations',
370
- { method: 'POST', body },
371
- { baseUrl }
372
- );
373
- return Response.json(data, { status });
374
- },
375
- };
376
- }
377
-
378
- // ============================================================================
379
- // Coder Route Handlers
380
- // ============================================================================
381
-
382
- /**
383
- * Create coder session route handlers
384
- */
385
- export function createCoderSessionRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers {
386
- const baseUrl = config.baseUrl;
387
-
388
- return {
389
- GET: async (request: Request) => {
390
- const url = new URL(request.url);
391
- const status = url.searchParams.get('status');
392
- const limit = url.searchParams.get('limit');
393
-
394
- let path = '/coder/sessions';
395
- const params: string[] = [];
396
- if (status) params.push(`status=${encodeURIComponent(status)}`);
397
- if (limit) params.push(`limit=${encodeURIComponent(limit)}`);
398
- if (params.length) path += `?${params.join('&')}`;
399
-
400
- const { data, status: resStatus } = await forwardJSON(path, {}, { baseUrl });
401
- return Response.json(data, { status: resStatus });
402
- },
403
-
404
- POST: async (request: Request) => {
405
- const body = await request.json();
406
- const { data, status } = await forwardJSON(
407
- '/coder/sessions',
408
- { method: 'POST', body },
409
- { baseUrl }
410
- );
411
- return Response.json(data, { status });
412
- },
413
- };
414
- }
415
-
416
- /**
417
- * Create coder session detail route handlers
418
- */
419
- export function createCoderSessionDetailRoutes(config: RouteHandlerConfig = {}): CRUDRouteHandlers & {
420
- abort: { POST: RouteHandler };
421
- } {
422
- const baseUrl = config.baseUrl;
423
-
424
- return {
425
- GET: async (request: Request, context?: { params?: { id?: string } }) => {
426
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
427
- const { data, status } = await forwardJSON(`/coder/sessions/${id}`, {}, { baseUrl });
428
- return Response.json(data, { status });
429
- },
430
-
431
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
432
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
433
- const body = await request.json();
434
- const { data, status } = await forwardJSON(
435
- `/coder/sessions/${id}`,
436
- { method: 'POST', body },
437
- { baseUrl }
438
- );
439
- return Response.json(data, { status });
440
- },
441
-
442
- DELETE: async (request: Request, context?: { params?: { id?: string } }) => {
443
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
444
- const { data, status } = await forwardJSON(
445
- `/coder/sessions/${id}`,
446
- { method: 'DELETE' },
447
- { baseUrl }
448
- );
449
- return Response.json(data, { status });
450
- },
451
-
452
- abort: {
453
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
454
- const id = context?.params?.id || new URL(request.url).pathname.split('/').slice(-2)[0];
455
- const { data, status } = await forwardJSON(
456
- `/coder/sessions/${id}/abort`,
457
- { method: 'POST' },
458
- { baseUrl }
459
- );
460
- return Response.json(data, { status });
461
- },
462
- },
463
- };
464
- }
465
-
466
- /**
467
- * Create coder execute route handler (with streaming support)
468
- */
469
- export function createCoderExecuteRoute(config: RouteHandlerConfig = {}): { POST: RouteHandler } {
470
- const baseUrl = config.baseUrl;
471
-
472
- return {
473
- POST: async (request: Request) => {
474
- const body = await request.json();
475
-
476
- const response = await forwardRequest(
477
- '/coder/execute',
478
- { method: 'POST', body, stream: body.stream },
479
- { baseUrl }
480
- );
481
-
482
- // Check if streaming
483
- const contentType = response.headers.get('content-type') || '';
484
- if (contentType.includes('text/event-stream') && response.body) {
485
- return new Response(response.body, {
486
- status: response.status,
487
- headers: {
488
- 'Content-Type': 'text/event-stream',
489
- 'Cache-Control': 'no-cache',
490
- 'Connection': 'keep-alive',
491
- },
492
- });
493
- }
494
-
495
- const data = await response.json();
496
- return Response.json(data, { status: response.status });
497
- },
498
- };
499
- }
500
-
501
- /**
502
- * Create coder tools route handlers
503
- */
504
- export function createCoderToolsRoutes(config: RouteHandlerConfig = {}): {
505
- GET: RouteHandler;
506
- call: { POST: RouteHandler };
507
- } {
508
- const baseUrl = config.baseUrl;
509
-
510
- return {
511
- GET: async () => {
512
- const { data, status } = await forwardJSON('/coder/tools', {}, { baseUrl });
513
- return Response.json(data, { status });
514
- },
515
-
516
- call: {
517
- POST: async (request: Request, context?: { params?: { tool?: string } }) => {
518
- const tool = context?.params?.tool || new URL(request.url).pathname.split('/').pop();
519
- const body = await request.json();
520
- const { data, status } = await forwardJSON(
521
- `/coder/tools/${tool}`,
522
- { method: 'POST', body },
523
- { baseUrl }
524
- );
525
- return Response.json(data, { status });
526
- },
527
- },
528
- };
529
- }
530
-
531
- /**
532
- * Create coder file operation route handlers
533
- */
534
- export function createCoderFilesRoutes(config: RouteHandlerConfig = {}): {
535
- read: { GET: RouteHandler };
536
- write: { POST: RouteHandler };
537
- list: { GET: RouteHandler };
538
- search: { GET: RouteHandler };
539
- diff: { POST: RouteHandler };
540
- } {
541
- const baseUrl = config.baseUrl;
542
-
543
- return {
544
- read: {
545
- GET: async (request: Request) => {
546
- const url = new URL(request.url);
547
- const path = url.searchParams.get('path') || '';
548
- const sessionId = url.searchParams.get('sessionId');
549
- const sandboxId = url.searchParams.get('sandboxId');
550
-
551
- let apiPath = `/coder/files/read?path=${encodeURIComponent(path)}`;
552
- if (sessionId) apiPath += `&sessionId=${encodeURIComponent(sessionId)}`;
553
- if (sandboxId) apiPath += `&sandboxId=${encodeURIComponent(sandboxId)}`;
554
-
555
- const { data, status } = await forwardJSON(apiPath, {}, { baseUrl });
556
- return Response.json(data, { status });
557
- },
558
- },
559
-
560
- write: {
561
- POST: async (request: Request) => {
562
- const body = await request.json();
563
- const { data, status } = await forwardJSON(
564
- '/coder/files/write',
565
- { method: 'POST', body },
566
- { baseUrl }
567
- );
568
- return Response.json(data, { status });
569
- },
570
- },
571
-
572
- list: {
573
- GET: async (request: Request) => {
574
- const url = new URL(request.url);
575
- const path = url.searchParams.get('path') || '.';
576
- const recursive = url.searchParams.get('recursive');
577
- const maxDepth = url.searchParams.get('maxDepth');
578
- const sessionId = url.searchParams.get('sessionId');
579
- const sandboxId = url.searchParams.get('sandboxId');
580
-
581
- let apiPath = `/coder/files/list?path=${encodeURIComponent(path)}`;
582
- if (recursive) apiPath += `&recursive=${recursive}`;
583
- if (maxDepth) apiPath += `&maxDepth=${maxDepth}`;
584
- if (sessionId) apiPath += `&sessionId=${encodeURIComponent(sessionId)}`;
585
- if (sandboxId) apiPath += `&sandboxId=${encodeURIComponent(sandboxId)}`;
586
-
587
- const { data, status } = await forwardJSON(apiPath, {}, { baseUrl });
588
- return Response.json(data, { status });
589
- },
590
- },
591
-
592
- search: {
593
- GET: async (request: Request) => {
594
- const url = new URL(request.url);
595
- const pattern = url.searchParams.get('pattern') || '';
596
- const path = url.searchParams.get('path');
597
- const filePattern = url.searchParams.get('filePattern');
598
- const sessionId = url.searchParams.get('sessionId');
599
- const sandboxId = url.searchParams.get('sandboxId');
600
-
601
- let apiPath = `/coder/files/search?pattern=${encodeURIComponent(pattern)}`;
602
- if (path) apiPath += `&path=${encodeURIComponent(path)}`;
603
- if (filePattern) apiPath += `&filePattern=${encodeURIComponent(filePattern)}`;
604
- if (sessionId) apiPath += `&sessionId=${encodeURIComponent(sessionId)}`;
605
- if (sandboxId) apiPath += `&sandboxId=${encodeURIComponent(sandboxId)}`;
606
-
607
- const { data, status } = await forwardJSON(apiPath, {}, { baseUrl });
608
- return Response.json(data, { status });
609
- },
610
- },
611
-
612
- diff: {
613
- POST: async (request: Request) => {
614
- const body = await request.json();
615
- const { data, status } = await forwardJSON(
616
- '/coder/files/diff',
617
- { method: 'POST', body },
618
- { baseUrl }
619
- );
620
- return Response.json(data, { status });
621
- },
622
- },
623
- };
624
- }
625
-
626
- /**
627
- * Create coder sandbox route handlers
628
- */
629
- export function createCoderSandboxRoutes(config: RouteHandlerConfig = {}): {
630
- create: { POST: RouteHandler };
631
- get: { GET: RouteHandler };
632
- exec: { POST: RouteHandler };
633
- destroy: { DELETE: RouteHandler };
634
- } {
635
- const baseUrl = config.baseUrl;
636
-
637
- return {
638
- create: {
639
- POST: async (request: Request) => {
640
- const body = await request.json();
641
- const { data, status } = await forwardJSON(
642
- '/coder/sandbox',
643
- { method: 'POST', body },
644
- { baseUrl }
645
- );
646
- return Response.json(data, { status });
647
- },
648
- },
649
-
650
- get: {
651
- GET: async (request: Request, context?: { params?: { id?: string } }) => {
652
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
653
- const { data, status } = await forwardJSON(`/coder/sandbox/${id}`, {}, { baseUrl });
654
- return Response.json(data, { status });
655
- },
656
- },
657
-
658
- exec: {
659
- POST: async (request: Request, context?: { params?: { id?: string } }) => {
660
- const id = context?.params?.id || new URL(request.url).pathname.split('/').slice(-2)[0];
661
- const body = await request.json();
662
-
663
- const response = await forwardRequest(
664
- `/coder/sandbox/${id}/exec`,
665
- { method: 'POST', body, stream: body.stream },
666
- { baseUrl }
667
- );
668
-
669
- // Check if streaming
670
- const contentType = response.headers.get('content-type') || '';
671
- if (contentType.includes('text/event-stream') && response.body) {
672
- return new Response(response.body, {
673
- status: response.status,
674
- headers: {
675
- 'Content-Type': 'text/event-stream',
676
- 'Cache-Control': 'no-cache',
677
- 'Connection': 'keep-alive',
678
- },
679
- });
680
- }
681
-
682
- const data = await response.json();
683
- return Response.json(data, { status: response.status });
684
- },
685
- },
686
-
687
- destroy: {
688
- DELETE: async (request: Request, context?: { params?: { id?: string } }) => {
689
- const id = context?.params?.id || new URL(request.url).pathname.split('/').pop();
690
- const { data, status } = await forwardJSON(
691
- `/coder/sandbox/${id}`,
692
- { method: 'DELETE' },
693
- { baseUrl }
694
- );
695
- return Response.json(data, { status });
696
- },
697
- },
698
- };
699
- }
700
-
701
- /**
702
- * Create coder command execution route handlers
703
- */
704
- export function createCoderExecRoutes(config: RouteHandlerConfig = {}): {
705
- POST: RouteHandler;
706
- stream: { POST: RouteHandler };
707
- } {
708
- const baseUrl = config.baseUrl;
709
-
710
- return {
711
- POST: async (request: Request) => {
712
- const body = await request.json();
713
- const { data, status } = await forwardJSON(
714
- '/coder/exec',
715
- { method: 'POST', body },
716
- { baseUrl }
717
- );
718
- return Response.json(data, { status });
719
- },
720
-
721
- stream: {
722
- POST: async (request: Request) => {
723
- const body = await request.json();
724
-
725
- const response = await forwardRequest(
726
- '/coder/exec/stream',
727
- { method: 'POST', body, stream: true },
728
- { baseUrl }
729
- );
730
-
731
- if (response.body) {
732
- return new Response(response.body, {
733
- status: response.status,
734
- headers: {
735
- 'Content-Type': 'text/event-stream',
736
- 'Cache-Control': 'no-cache',
737
- 'Connection': 'keep-alive',
738
- },
739
- });
740
- }
741
-
742
- const data = await response.json();
743
- return Response.json(data, { status: response.status });
744
- },
745
- },
746
- };
747
- }
748
-
749
- // ============================================================================
750
- // Stack Management Route Handlers
751
- // ============================================================================
752
-
753
- export interface StackRouteHandlerConfig extends RouteHandlerConfig {
754
- /** Extract JWT from incoming request and forward as Authorization header */
755
- extractAuth?: (request: Request) => string | null;
756
- }
757
-
758
- /** Validate that an ID parameter is a safe alphanumeric/dash/underscore string */
759
- function validateId(id: string | null | undefined, name: string): string {
760
- if (!id || typeof id !== 'string') {
761
- throw new Error(`Missing required parameter: ${name}`);
762
- }
763
- // Allow alphanumeric, dashes, underscores, dots — block path traversal
764
- if (!/^[\w.\-]+$/.test(id)) {
765
- throw new Error(`Invalid ${name}: contains disallowed characters`);
766
- }
767
- return id;
768
- }
769
-
770
- function authHeaders(config: StackRouteHandlerConfig, request: Request): Record<string, string> {
771
- const headers: Record<string, string> = {};
772
- if (config.extractAuth) {
773
- const token = config.extractAuth(request);
774
- if (token) headers['Authorization'] = `Bearer ${token}`;
775
- } else {
776
- const auth = request.headers.get('Authorization');
777
- if (auth) headers['Authorization'] = auth;
778
- }
779
- return headers;
780
- }
781
-
782
- /**
783
- * Create stack CRUD route handlers (list + create)
784
- */
785
- export function createStackRoutes(config: StackRouteHandlerConfig = {}): CRUDRouteHandlers {
786
- const baseUrl = config.baseUrl;
787
-
788
- return {
789
- GET: async (request: Request) => {
790
- const { data, status } = await forwardJSON(
791
- '/api/v2/stacks',
792
- { headers: authHeaders(config, request) },
793
- { baseUrl }
794
- );
795
- return Response.json(data, { status });
796
- },
797
-
798
- POST: async (request: Request) => {
799
- const body = await request.json();
800
- const { data, status } = await forwardJSON(
801
- '/api/v2/stacks',
802
- { method: 'POST', body, headers: authHeaders(config, request) },
803
- { baseUrl }
804
- );
805
- return Response.json(data, { status });
806
- },
807
- };
808
- }
809
-
810
- /**
811
- * Create stack detail route handlers (get, update, delete by [stackId])
812
- */
813
- export function createStackDetailRoutes(config: StackRouteHandlerConfig = {}): CRUDRouteHandlers {
814
- const baseUrl = config.baseUrl;
815
-
816
- return {
817
- GET: async (request: Request, context?: { params?: { stackId?: string } }) => {
818
- const stackId = validateId(context?.params?.stackId, 'stackId');
819
- const { data, status } = await forwardJSON(
820
- `/api/v2/stacks/${stackId}`,
821
- { headers: authHeaders(config, request) },
822
- { baseUrl }
823
- );
824
- return Response.json(data, { status });
825
- },
826
-
827
- POST: async () => Response.json({ error: 'Method not allowed' }, { status: 405 }),
828
-
829
- PUT: async (request: Request, context?: { params?: { stackId?: string } }) => {
830
- const stackId = validateId(context?.params?.stackId, 'stackId');
831
- const body = await request.json();
832
- const { data, status } = await forwardJSON(
833
- `/api/v2/stacks/${stackId}`,
834
- { method: 'PATCH', body, headers: authHeaders(config, request) },
835
- { baseUrl }
836
- );
837
- return Response.json(data, { status });
838
- },
839
-
840
- DELETE: async (request: Request, context?: { params?: { stackId?: string } }) => {
841
- const stackId = validateId(context?.params?.stackId, 'stackId');
842
- const { data, status } = await forwardJSON(
843
- `/api/v2/stacks/${stackId}`,
844
- { method: 'DELETE', headers: authHeaders(config, request) },
845
- { baseUrl }
846
- );
847
- return Response.json(data, { status });
848
- },
849
- };
850
- }
851
-
852
- /**
853
- * Create stack keys route handlers (list + create + revoke)
854
- */
855
- export function createStackKeysRoutes(config: StackRouteHandlerConfig = {}): CRUDRouteHandlers {
856
- const baseUrl = config.baseUrl;
857
-
858
- return {
859
- GET: async (request: Request, context?: { params?: { stackId?: string } }) => {
860
- const stackId = validateId(context?.params?.stackId, 'stackId');
861
- const { data, status } = await forwardJSON(
862
- `/api/v2/stacks/${stackId}/keys`,
863
- { headers: authHeaders(config, request) },
864
- { baseUrl }
865
- );
866
- return Response.json(data, { status });
867
- },
868
-
869
- POST: async (request: Request, context?: { params?: { stackId?: string } }) => {
870
- const stackId = validateId(context?.params?.stackId, 'stackId');
871
- const body = await request.json();
872
- const { data, status } = await forwardJSON(
873
- `/api/v2/stacks/${stackId}/keys`,
874
- { method: 'POST', body, headers: authHeaders(config, request) },
875
- { baseUrl }
876
- );
877
- return Response.json(data, { status });
878
- },
879
-
880
- DELETE: async (request: Request, context?: { params?: { stackId?: string; keyId?: string } }) => {
881
- const stackId = validateId(context?.params?.stackId, 'stackId');
882
- const keyId = validateId(context?.params?.keyId, 'keyId');
883
- const { data, status } = await forwardJSON(
884
- `/api/v2/stacks/${stackId}/keys/${keyId}`,
885
- { method: 'DELETE', headers: authHeaders(config, request) },
886
- { baseUrl }
887
- );
888
- return Response.json(data, { status });
889
- },
890
- };
891
- }
892
-
893
- /**
894
- * Create stack members route handlers
895
- */
896
- export function createStackMembersRoutes(config: StackRouteHandlerConfig = {}): {
897
- GET: RouteHandler;
898
- stats: { GET: RouteHandler };
899
- updateRole: { PATCH: RouteHandler };
900
- } {
901
- const baseUrl = config.baseUrl;
902
-
903
- return {
904
- GET: async (request: Request, context?: { params?: { stackId?: string } }) => {
905
- const stackId = validateId(context?.params?.stackId, 'stackId');
906
- const url = new URL(request.url);
907
- const params = new URLSearchParams();
908
- const limit = url.searchParams.get('limit');
909
- const offset = url.searchParams.get('offset');
910
- const role = url.searchParams.get('role');
911
- if (limit) params.set('limit', limit);
912
- if (offset) params.set('offset', offset);
913
- if (role) params.set('role', role);
914
- const qs = params.toString() ? `?${params}` : '';
915
-
916
- const { data, status } = await forwardJSON(
917
- `/api/v2/stacks/${stackId}/members${qs}`,
918
- { headers: authHeaders(config, request) },
919
- { baseUrl }
920
- );
921
- return Response.json(data, { status });
922
- },
923
-
924
- stats: {
925
- GET: async (request: Request, context?: { params?: { stackId?: string } }) => {
926
- const stackId = validateId(context?.params?.stackId, 'stackId');
927
- const { data, status } = await forwardJSON(
928
- `/api/v2/stacks/${stackId}/members/stats`,
929
- { headers: authHeaders(config, request) },
930
- { baseUrl }
931
- );
932
- return Response.json(data, { status });
933
- },
934
- },
935
-
936
- updateRole: {
937
- PATCH: async (request: Request, context?: { params?: { stackId?: string; userId?: string } }) => {
938
- const stackId = validateId(context?.params?.stackId, 'stackId');
939
- const userId = validateId(context?.params?.userId, 'userId');
940
- const body = await request.json();
941
- const { data, status } = await forwardJSON(
942
- `/api/v2/stacks/${stackId}/members/${encodeURIComponent(userId)}/role`,
943
- { method: 'PATCH', body, headers: authHeaders(config, request) },
944
- { baseUrl }
945
- );
946
- return Response.json(data, { status });
947
- },
948
- },
949
- };
950
- }