@studio-foundation/api 0.3.0-beta.1 → 0.3.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -4
- package/ARCHITECTURE.md +0 -52
- package/src/.gitkeep +0 -0
- package/src/api.ts +0 -8
- package/src/bootstrap.ts +0 -259
- package/src/event-bus.ts +0 -64
- package/src/index.ts +0 -58
- package/src/integration-runtime.ts +0 -180
- package/src/integration-store.ts +0 -125
- package/src/integrations/linear/failure-handler.ts +0 -93
- package/src/integrations/linear/webhook-handler.ts +0 -156
- package/src/integrations/registry.ts +0 -12
- package/src/integrations/types.ts +0 -37
- package/src/launcher.ts +0 -214
- package/src/logger.ts +0 -50
- package/src/plans.ts +0 -43
- package/src/routes/agents.ts +0 -131
- package/src/routes/config.ts +0 -154
- package/src/routes/contracts.ts +0 -205
- package/src/routes/pipelines.ts +0 -146
- package/src/routes/projects.ts +0 -237
- package/src/routes/runs.ts +0 -468
- package/src/routes/skills.ts +0 -136
- package/src/routes/tools.ts +0 -222
- package/src/routes/users.ts +0 -169
- package/src/routes/validate.ts +0 -196
- package/src/routes/webhooks.ts +0 -120
- package/src/server.ts +0 -155
- package/src/spawners/http-api-spawner.ts +0 -96
- package/src/user-store-pg.ts +0 -138
- package/src/user-store.ts +0 -125
- package/src/utils/repo-resolver.ts +0 -3
- package/src/webhook-dispatcher.ts +0 -142
- package/src/webhook-store.ts +0 -141
- package/tests/agents.test.ts +0 -164
- package/tests/cancel.test.ts +0 -120
- package/tests/config.test.ts +0 -196
- package/tests/contracts.test.ts +0 -302
- package/tests/event-bus.test.ts +0 -53
- package/tests/http-api-spawner.test.ts +0 -158
- package/tests/integration-runtime.test.ts +0 -199
- package/tests/integration-store.test.ts +0 -66
- package/tests/integrations/linear/failure-handler.test.ts +0 -113
- package/tests/integrations/linear/webhook-handler.test.ts +0 -191
- package/tests/launcher.test.ts +0 -380
- package/tests/linear-webhook.test.ts +0 -390
- package/tests/pipelines.test.ts +0 -166
- package/tests/projects.test.ts +0 -283
- package/tests/runs.test.ts +0 -379
- package/tests/server.test.ts +0 -208
- package/tests/skills.test.ts +0 -149
- package/tests/sse.test.ts +0 -129
- package/tests/tools.test.ts +0 -233
- package/tests/user-store.test.ts +0 -93
- package/tests/users.test.ts +0 -189
- package/tests/utils/repo-resolver.test.ts +0 -105
- package/tests/validate.test.ts +0 -233
- package/tests/webhook-dispatcher.test.ts +0 -214
- package/tests/webhook-store.test.ts +0 -98
- package/tests/webhooks.test.ts +0 -176
- package/tsconfig.json +0 -24
- package/vitest.config.ts +0 -7
package/tests/webhooks.test.ts
DELETED
|
@@ -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
|
-
}
|