@studio-foundation/api 0.3.0-beta.1 → 0.3.0-beta.5

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 (62) hide show
  1. package/package.json +7 -4
  2. package/ARCHITECTURE.md +0 -52
  3. package/src/.gitkeep +0 -0
  4. package/src/api.ts +0 -8
  5. package/src/bootstrap.ts +0 -259
  6. package/src/event-bus.ts +0 -64
  7. package/src/index.ts +0 -58
  8. package/src/integration-runtime.ts +0 -180
  9. package/src/integration-store.ts +0 -125
  10. package/src/integrations/linear/failure-handler.ts +0 -93
  11. package/src/integrations/linear/webhook-handler.ts +0 -156
  12. package/src/integrations/registry.ts +0 -12
  13. package/src/integrations/types.ts +0 -37
  14. package/src/launcher.ts +0 -214
  15. package/src/logger.ts +0 -50
  16. package/src/plans.ts +0 -43
  17. package/src/routes/agents.ts +0 -131
  18. package/src/routes/config.ts +0 -154
  19. package/src/routes/contracts.ts +0 -205
  20. package/src/routes/pipelines.ts +0 -146
  21. package/src/routes/projects.ts +0 -237
  22. package/src/routes/runs.ts +0 -468
  23. package/src/routes/skills.ts +0 -136
  24. package/src/routes/tools.ts +0 -222
  25. package/src/routes/users.ts +0 -169
  26. package/src/routes/validate.ts +0 -196
  27. package/src/routes/webhooks.ts +0 -120
  28. package/src/server.ts +0 -155
  29. package/src/spawners/http-api-spawner.ts +0 -96
  30. package/src/user-store-pg.ts +0 -138
  31. package/src/user-store.ts +0 -125
  32. package/src/utils/repo-resolver.ts +0 -3
  33. package/src/webhook-dispatcher.ts +0 -142
  34. package/src/webhook-store.ts +0 -141
  35. package/tests/agents.test.ts +0 -164
  36. package/tests/cancel.test.ts +0 -120
  37. package/tests/config.test.ts +0 -196
  38. package/tests/contracts.test.ts +0 -302
  39. package/tests/event-bus.test.ts +0 -53
  40. package/tests/http-api-spawner.test.ts +0 -158
  41. package/tests/integration-runtime.test.ts +0 -199
  42. package/tests/integration-store.test.ts +0 -66
  43. package/tests/integrations/linear/failure-handler.test.ts +0 -113
  44. package/tests/integrations/linear/webhook-handler.test.ts +0 -191
  45. package/tests/launcher.test.ts +0 -380
  46. package/tests/linear-webhook.test.ts +0 -390
  47. package/tests/pipelines.test.ts +0 -166
  48. package/tests/projects.test.ts +0 -283
  49. package/tests/runs.test.ts +0 -379
  50. package/tests/server.test.ts +0 -208
  51. package/tests/skills.test.ts +0 -149
  52. package/tests/sse.test.ts +0 -129
  53. package/tests/tools.test.ts +0 -233
  54. package/tests/user-store.test.ts +0 -93
  55. package/tests/users.test.ts +0 -189
  56. package/tests/utils/repo-resolver.test.ts +0 -105
  57. package/tests/validate.test.ts +0 -233
  58. package/tests/webhook-dispatcher.test.ts +0 -214
  59. package/tests/webhook-store.test.ts +0 -98
  60. package/tests/webhooks.test.ts +0 -176
  61. package/tsconfig.json +0 -24
  62. package/vitest.config.ts +0 -7
@@ -1,176 +0,0 @@
1
- import { describe, test, expect, beforeEach, afterEach } from 'vitest';
2
- import { resolve } from 'node:path';
3
- import { mkdirSync } from 'node:fs';
4
- import { buildServer } from '../src/server.js';
5
- import { InMemoryRunStore } from '@studio-foundation/engine';
6
- import { WebhookStore } from '../src/webhook-store.js';
7
- import type { IntegrationRuntime } from '../src/integration-runtime.js';
8
- import type { IntegrationStore } from '../src/integration-store.js';
9
-
10
- const nullIntegrationRuntime = { registerRoutes: () => {} } as unknown as IntegrationRuntime;
11
- const nullIntegrationStore = {} as unknown as IntegrationStore;
12
-
13
- function makeServer() {
14
- const dir = resolve('/tmp', `.studio-webhooks-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
15
- mkdirSync(dir, { recursive: true });
16
- const webhookStore = new WebhookStore(resolve(dir, 'runs.db'));
17
-
18
- const server = buildServer({
19
- store: new InMemoryRunStore(),
20
- launcher: {
21
- launch: async () => ({ run_id: 'test' }),
22
- cancel: async () => {},
23
- subscribe: () => () => {},
24
- },
25
- configsDir: dir,
26
- projectName: 'test-project',
27
- apiConfig: {},
28
- studioVersion: '0.0.0',
29
- maskedConfig: { providers: [] },
30
- webhookStore,
31
- integrationRuntime: nullIntegrationRuntime,
32
- integrationStore: nullIntegrationStore,
33
- });
34
-
35
- return { server, webhookStore, cleanup: () => webhookStore.close() };
36
- }
37
-
38
- describe('POST /api/webhooks', () => {
39
- let server: ReturnType<typeof makeServer>['server'];
40
- let cleanup: () => void;
41
-
42
- beforeEach(() => {
43
- ({ server, cleanup } = makeServer());
44
- });
45
-
46
- afterEach(() => {
47
- cleanup();
48
- });
49
-
50
- test('registers a webhook and returns 201 with id', async () => {
51
- const res = await server.inject({
52
- method: 'POST',
53
- url: '/api/webhooks',
54
- payload: {
55
- url: 'https://example.com/hook',
56
- events: ['pipeline_complete', 'stage_failed'],
57
- secret: 'mysecret',
58
- },
59
- });
60
- expect(res.statusCode).toBe(201);
61
- const body = res.json<{ id: string; url: string; events: string[]; status: string }>();
62
- expect(body.id).toBeDefined();
63
- expect(body.url).toBe('https://example.com/hook');
64
- expect(body.events).toEqual(['pipeline_complete', 'stage_failed']);
65
- expect(body.status).toBe('active');
66
- });
67
-
68
- test('registers a webhook without a secret', async () => {
69
- const res = await server.inject({
70
- method: 'POST',
71
- url: '/api/webhooks',
72
- payload: {
73
- url: 'https://example.com/hook',
74
- events: ['pipeline_complete'],
75
- },
76
- });
77
- expect(res.statusCode).toBe(201);
78
- const body = res.json<{ id: string }>();
79
- expect(body.id).toBeDefined();
80
- });
81
-
82
- test('returns 400 when url is missing', async () => {
83
- const res = await server.inject({
84
- method: 'POST',
85
- url: '/api/webhooks',
86
- payload: { events: ['pipeline_complete'] },
87
- });
88
- expect(res.statusCode).toBe(400);
89
- });
90
-
91
- test('returns 400 when events is missing', async () => {
92
- const res = await server.inject({
93
- method: 'POST',
94
- url: '/api/webhooks',
95
- payload: { url: 'https://example.com/hook' },
96
- });
97
- expect(res.statusCode).toBe(400);
98
- });
99
-
100
- test('returns 400 when events is empty', async () => {
101
- const res = await server.inject({
102
- method: 'POST',
103
- url: '/api/webhooks',
104
- payload: { url: 'https://example.com/hook', events: [] },
105
- });
106
- expect(res.statusCode).toBe(400);
107
- });
108
- });
109
-
110
- describe('GET /api/webhooks', () => {
111
- let server: ReturnType<typeof makeServer>['server'];
112
- let webhookStore: WebhookStore;
113
- let cleanup: () => void;
114
-
115
- beforeEach(() => {
116
- ({ server, webhookStore, cleanup } = makeServer());
117
- });
118
-
119
- afterEach(() => {
120
- cleanup();
121
- });
122
-
123
- test('returns empty list when no webhooks registered', async () => {
124
- const res = await server.inject({ method: 'GET', url: '/api/webhooks' });
125
- expect(res.statusCode).toBe(200);
126
- const body = res.json<{ webhooks: unknown[] }>();
127
- expect(body.webhooks).toHaveLength(0);
128
- });
129
-
130
- test('returns list of registered webhooks', async () => {
131
- webhookStore.saveWebhook({ id: 'wh-1', url: 'https://a.com/hook', events: ['pipeline_complete'], status: 'active', created_at: '2026-01-01T00:00:00.000Z' });
132
- webhookStore.saveWebhook({ id: 'wh-2', url: 'https://b.com/hook', events: ['stage_failed'], status: 'active', created_at: '2026-01-01T00:00:00.000Z' });
133
-
134
- const res = await server.inject({ method: 'GET', url: '/api/webhooks' });
135
- expect(res.statusCode).toBe(200);
136
- const body = res.json<{ webhooks: Array<{ id: string }> }>();
137
- expect(body.webhooks).toHaveLength(2);
138
- expect(body.webhooks.map(w => w.id)).toContain('wh-1');
139
- expect(body.webhooks.map(w => w.id)).toContain('wh-2');
140
- });
141
-
142
- test('does not expose secret in response', async () => {
143
- webhookStore.saveWebhook({ id: 'wh-1', url: 'https://a.com/hook', events: ['pipeline_complete'], secret: 'topsecret', status: 'active', created_at: '2026-01-01T00:00:00.000Z' });
144
-
145
- const res = await server.inject({ method: 'GET', url: '/api/webhooks' });
146
- const body = res.json<{ webhooks: Array<Record<string, unknown>> }>();
147
- expect(body.webhooks[0]['secret']).toBeUndefined();
148
- });
149
- });
150
-
151
- describe('DELETE /api/webhooks/:id', () => {
152
- let server: ReturnType<typeof makeServer>['server'];
153
- let webhookStore: WebhookStore;
154
- let cleanup: () => void;
155
-
156
- beforeEach(() => {
157
- ({ server, webhookStore, cleanup } = makeServer());
158
- });
159
-
160
- afterEach(() => {
161
- cleanup();
162
- });
163
-
164
- test('deletes an existing webhook and returns 204', async () => {
165
- webhookStore.saveWebhook({ id: 'wh-1', url: 'https://a.com/hook', events: ['pipeline_complete'], status: 'active', created_at: '2026-01-01T00:00:00.000Z' });
166
-
167
- const res = await server.inject({ method: 'DELETE', url: '/api/webhooks/wh-1' });
168
- expect(res.statusCode).toBe(204);
169
- expect(webhookStore.getWebhook('wh-1')).toBeNull();
170
- });
171
-
172
- test('returns 404 for unknown webhook id', async () => {
173
- const res = await server.inject({ method: 'DELETE', url: '/api/webhooks/nonexistent' });
174
- expect(res.statusCode).toBe(404);
175
- });
176
- });
package/tsconfig.json DELETED
@@ -1,24 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2022",
4
- "module": "ES2022",
5
- "moduleResolution": "bundler",
6
- "outDir": "./dist",
7
- "rootDir": "./src",
8
- "declaration": true,
9
- "declarationMap": true,
10
- "sourceMap": true,
11
- "strict": true,
12
- "esModuleInterop": true,
13
- "skipLibCheck": true,
14
- "forceConsistentCasingInFileNames": true,
15
- "resolveJsonModule": true,
16
- "isolatedModules": true,
17
- "noUnusedLocals": true,
18
- "noUnusedParameters": true,
19
- "noImplicitReturns": true,
20
- "noFallthroughCasesInSwitch": true
21
- },
22
- "include": ["src/**/*"],
23
- "exclude": ["node_modules", "dist", "tests"]
24
- }
package/vitest.config.ts DELETED
@@ -1,7 +0,0 @@
1
- import { defineConfig } from 'vitest/config';
2
-
3
- export default defineConfig({
4
- test: {
5
- include: ['tests/**/*.test.ts'],
6
- },
7
- });