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