@studio-foundation/api 0.3.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (188) hide show
  1. package/ARCHITECTURE.md +52 -0
  2. package/LICENSE +663 -0
  3. package/README.md +161 -0
  4. package/dist/api.d.ts +8 -0
  5. package/dist/api.d.ts.map +1 -0
  6. package/dist/api.js +6 -0
  7. package/dist/api.js.map +1 -0
  8. package/dist/bootstrap.d.ts +66 -0
  9. package/dist/bootstrap.d.ts.map +1 -0
  10. package/dist/bootstrap.js +193 -0
  11. package/dist/bootstrap.js.map +1 -0
  12. package/dist/event-bus.d.ts +16 -0
  13. package/dist/event-bus.d.ts.map +1 -0
  14. package/dist/event-bus.js +37 -0
  15. package/dist/event-bus.js.map +1 -0
  16. package/dist/index.d.ts +3 -0
  17. package/dist/index.d.ts.map +1 -0
  18. package/dist/index.js +52 -0
  19. package/dist/index.js.map +1 -0
  20. package/dist/integration-runtime.d.ts +22 -0
  21. package/dist/integration-runtime.d.ts.map +1 -0
  22. package/dist/integration-runtime.js +150 -0
  23. package/dist/integration-runtime.js.map +1 -0
  24. package/dist/integration-store.d.ts +26 -0
  25. package/dist/integration-store.d.ts.map +1 -0
  26. package/dist/integration-store.js +80 -0
  27. package/dist/integration-store.js.map +1 -0
  28. package/dist/integrations/linear/failure-handler.d.ts +5 -0
  29. package/dist/integrations/linear/failure-handler.d.ts.map +1 -0
  30. package/dist/integrations/linear/failure-handler.js +73 -0
  31. package/dist/integrations/linear/failure-handler.js.map +1 -0
  32. package/dist/integrations/linear/webhook-handler.d.ts +6 -0
  33. package/dist/integrations/linear/webhook-handler.d.ts.map +1 -0
  34. package/dist/integrations/linear/webhook-handler.js +131 -0
  35. package/dist/integrations/linear/webhook-handler.js.map +1 -0
  36. package/dist/integrations/registry.d.ts +4 -0
  37. package/dist/integrations/registry.d.ts.map +1 -0
  38. package/dist/integrations/registry.js +9 -0
  39. package/dist/integrations/registry.js.map +1 -0
  40. package/dist/integrations/types.d.ts +33 -0
  41. package/dist/integrations/types.d.ts.map +1 -0
  42. package/dist/integrations/types.js +2 -0
  43. package/dist/integrations/types.js.map +1 -0
  44. package/dist/launcher.d.ts +45 -0
  45. package/dist/launcher.d.ts.map +1 -0
  46. package/dist/launcher.js +149 -0
  47. package/dist/launcher.js.map +1 -0
  48. package/dist/logger.d.ts +7 -0
  49. package/dist/logger.d.ts.map +1 -0
  50. package/dist/logger.js +37 -0
  51. package/dist/logger.js.map +1 -0
  52. package/dist/plans.d.ts +13 -0
  53. package/dist/plans.d.ts.map +1 -0
  54. package/dist/plans.js +33 -0
  55. package/dist/plans.js.map +1 -0
  56. package/dist/routes/agents.d.ts +6 -0
  57. package/dist/routes/agents.d.ts.map +1 -0
  58. package/dist/routes/agents.js +118 -0
  59. package/dist/routes/agents.js.map +1 -0
  60. package/dist/routes/config.d.ts +6 -0
  61. package/dist/routes/config.d.ts.map +1 -0
  62. package/dist/routes/config.js +129 -0
  63. package/dist/routes/config.js.map +1 -0
  64. package/dist/routes/contracts.d.ts +6 -0
  65. package/dist/routes/contracts.d.ts.map +1 -0
  66. package/dist/routes/contracts.js +188 -0
  67. package/dist/routes/contracts.js.map +1 -0
  68. package/dist/routes/pipelines.d.ts +6 -0
  69. package/dist/routes/pipelines.d.ts.map +1 -0
  70. package/dist/routes/pipelines.js +133 -0
  71. package/dist/routes/pipelines.js.map +1 -0
  72. package/dist/routes/projects.d.ts +6 -0
  73. package/dist/routes/projects.d.ts.map +1 -0
  74. package/dist/routes/projects.js +220 -0
  75. package/dist/routes/projects.js.map +1 -0
  76. package/dist/routes/runs.d.ts +6 -0
  77. package/dist/routes/runs.d.ts.map +1 -0
  78. package/dist/routes/runs.js +427 -0
  79. package/dist/routes/runs.js.map +1 -0
  80. package/dist/routes/skills.d.ts +6 -0
  81. package/dist/routes/skills.d.ts.map +1 -0
  82. package/dist/routes/skills.js +123 -0
  83. package/dist/routes/skills.js.map +1 -0
  84. package/dist/routes/tools.d.ts +6 -0
  85. package/dist/routes/tools.d.ts.map +1 -0
  86. package/dist/routes/tools.js +193 -0
  87. package/dist/routes/tools.js.map +1 -0
  88. package/dist/routes/users.d.ts +6 -0
  89. package/dist/routes/users.d.ts.map +1 -0
  90. package/dist/routes/users.js +152 -0
  91. package/dist/routes/users.js.map +1 -0
  92. package/dist/routes/validate.d.ts +6 -0
  93. package/dist/routes/validate.d.ts.map +1 -0
  94. package/dist/routes/validate.js +171 -0
  95. package/dist/routes/validate.js.map +1 -0
  96. package/dist/routes/webhooks.d.ts +8 -0
  97. package/dist/routes/webhooks.d.ts.map +1 -0
  98. package/dist/routes/webhooks.js +102 -0
  99. package/dist/routes/webhooks.js.map +1 -0
  100. package/dist/server.d.ts +46 -0
  101. package/dist/server.d.ts.map +1 -0
  102. package/dist/server.js +101 -0
  103. package/dist/server.js.map +1 -0
  104. package/dist/spawners/http-api-spawner.d.ts +10 -0
  105. package/dist/spawners/http-api-spawner.d.ts.map +1 -0
  106. package/dist/spawners/http-api-spawner.js +88 -0
  107. package/dist/spawners/http-api-spawner.js.map +1 -0
  108. package/dist/user-store-pg.d.ts +21 -0
  109. package/dist/user-store-pg.d.ts.map +1 -0
  110. package/dist/user-store-pg.js +97 -0
  111. package/dist/user-store-pg.js.map +1 -0
  112. package/dist/user-store.d.ts +29 -0
  113. package/dist/user-store.d.ts.map +1 -0
  114. package/dist/user-store.js +97 -0
  115. package/dist/user-store.js.map +1 -0
  116. package/dist/utils/repo-resolver.d.ts +3 -0
  117. package/dist/utils/repo-resolver.d.ts.map +1 -0
  118. package/dist/utils/repo-resolver.js +3 -0
  119. package/dist/utils/repo-resolver.js.map +1 -0
  120. package/dist/webhook-dispatcher.d.ts +13 -0
  121. package/dist/webhook-dispatcher.d.ts.map +1 -0
  122. package/dist/webhook-dispatcher.js +108 -0
  123. package/dist/webhook-dispatcher.js.map +1 -0
  124. package/dist/webhook-store.d.ts +31 -0
  125. package/dist/webhook-store.d.ts.map +1 -0
  126. package/dist/webhook-store.js +91 -0
  127. package/dist/webhook-store.js.map +1 -0
  128. package/package.json +59 -0
  129. package/src/.gitkeep +0 -0
  130. package/src/api.ts +8 -0
  131. package/src/bootstrap.ts +259 -0
  132. package/src/event-bus.ts +64 -0
  133. package/src/index.ts +58 -0
  134. package/src/integration-runtime.ts +180 -0
  135. package/src/integration-store.ts +125 -0
  136. package/src/integrations/linear/failure-handler.ts +93 -0
  137. package/src/integrations/linear/webhook-handler.ts +156 -0
  138. package/src/integrations/registry.ts +12 -0
  139. package/src/integrations/types.ts +37 -0
  140. package/src/launcher.ts +214 -0
  141. package/src/logger.ts +50 -0
  142. package/src/plans.ts +43 -0
  143. package/src/routes/agents.ts +131 -0
  144. package/src/routes/config.ts +154 -0
  145. package/src/routes/contracts.ts +205 -0
  146. package/src/routes/pipelines.ts +146 -0
  147. package/src/routes/projects.ts +237 -0
  148. package/src/routes/runs.ts +468 -0
  149. package/src/routes/skills.ts +136 -0
  150. package/src/routes/tools.ts +222 -0
  151. package/src/routes/users.ts +169 -0
  152. package/src/routes/validate.ts +196 -0
  153. package/src/routes/webhooks.ts +120 -0
  154. package/src/server.ts +155 -0
  155. package/src/spawners/http-api-spawner.ts +96 -0
  156. package/src/user-store-pg.ts +138 -0
  157. package/src/user-store.ts +125 -0
  158. package/src/utils/repo-resolver.ts +3 -0
  159. package/src/webhook-dispatcher.ts +142 -0
  160. package/src/webhook-store.ts +141 -0
  161. package/tests/agents.test.ts +164 -0
  162. package/tests/cancel.test.ts +120 -0
  163. package/tests/config.test.ts +196 -0
  164. package/tests/contracts.test.ts +302 -0
  165. package/tests/event-bus.test.ts +53 -0
  166. package/tests/http-api-spawner.test.ts +158 -0
  167. package/tests/integration-runtime.test.ts +199 -0
  168. package/tests/integration-store.test.ts +66 -0
  169. package/tests/integrations/linear/failure-handler.test.ts +113 -0
  170. package/tests/integrations/linear/webhook-handler.test.ts +191 -0
  171. package/tests/launcher.test.ts +380 -0
  172. package/tests/linear-webhook.test.ts +390 -0
  173. package/tests/pipelines.test.ts +166 -0
  174. package/tests/projects.test.ts +283 -0
  175. package/tests/runs.test.ts +379 -0
  176. package/tests/server.test.ts +208 -0
  177. package/tests/skills.test.ts +149 -0
  178. package/tests/sse.test.ts +129 -0
  179. package/tests/tools.test.ts +233 -0
  180. package/tests/user-store.test.ts +93 -0
  181. package/tests/users.test.ts +189 -0
  182. package/tests/utils/repo-resolver.test.ts +105 -0
  183. package/tests/validate.test.ts +233 -0
  184. package/tests/webhook-dispatcher.test.ts +214 -0
  185. package/tests/webhook-store.test.ts +98 -0
  186. package/tests/webhooks.test.ts +176 -0
  187. package/tsconfig.json +24 -0
  188. package/vitest.config.ts +7 -0
@@ -0,0 +1,150 @@
1
+ import { WEBHOOK_HANDLERS, FAILURE_HANDLERS } from './integrations/registry.js';
2
+ export class IntegrationRuntime {
3
+ deps;
4
+ constructor(deps) {
5
+ this.deps = deps;
6
+ }
7
+ setupEventBus(bus) {
8
+ bus.subscribeAll((runId, event) => {
9
+ if (event.type !== 'pipeline_complete')
10
+ return;
11
+ const data = event.data;
12
+ if (data.status === 'success')
13
+ return;
14
+ for (const integration of this.deps.integrations) {
15
+ if (!integration.on_failure?.handler)
16
+ continue;
17
+ const handler = FAILURE_HANDLERS[integration.on_failure.handler];
18
+ if (!handler)
19
+ continue;
20
+ const ctx = {
21
+ runId,
22
+ durationMs: data.duration_ms,
23
+ status: data.status,
24
+ meta: data.meta ?? {},
25
+ lastGroupFeedback: data.last_group_feedback,
26
+ integration,
27
+ integrationConfig: this.deps.integrationConfigs[integration.name] ?? {},
28
+ };
29
+ void handler.handleFailure(ctx);
30
+ }
31
+ });
32
+ }
33
+ registerRoutes(fastify, prefix) {
34
+ for (const integration of this.deps.integrations) {
35
+ if (!integration.webhook?.handler)
36
+ continue;
37
+ const webhookHandler = WEBHOOK_HANDLERS[integration.webhook.handler];
38
+ if (!webhookHandler)
39
+ continue;
40
+ const name = integration.name;
41
+ const { store, launcher, configsDir, projectsDir, apiConfig, integrationConfigs } = this.deps;
42
+ const integrationConfig = integrationConfigs[name] ?? {};
43
+ const errorSchema = { type: 'object', properties: { error: { type: 'string' } } };
44
+ void fastify.register(async (scope) => {
45
+ scope.addContentTypeParser('application/json', { parseAs: 'buffer' }, (_req, body, done) => done(null, body));
46
+ // GET /api/integrations/{name}
47
+ scope.get(`${prefix}/integrations/${name}`, {
48
+ schema: {
49
+ tags: ['integrations'],
50
+ summary: `Get ${name} integration config and trigger log`,
51
+ response: {
52
+ 200: {
53
+ type: 'object',
54
+ properties: {
55
+ webhook_url: { type: 'string' },
56
+ pipeline: { type: ['string', 'null'] },
57
+ active: { type: 'boolean' },
58
+ triggers: { type: 'array' },
59
+ },
60
+ },
61
+ },
62
+ },
63
+ }, async (request, reply) => {
64
+ const config = store.getConfig(name);
65
+ const triggers = store.listTriggers(name, 50);
66
+ const baseUrl = process.env['STUDIO_BASE_URL'] ?? `${request.protocol}://${request.hostname}`;
67
+ return reply.status(200).send({
68
+ webhook_url: `${baseUrl}/api/integrations/${name}/webhook`,
69
+ pipeline: config.pipeline ?? null,
70
+ active: config.active,
71
+ triggers,
72
+ });
73
+ });
74
+ // PATCH /api/integrations/{name}
75
+ scope.patch(`${prefix}/integrations/${name}`, {
76
+ schema: {
77
+ tags: ['integrations'],
78
+ summary: `Update ${name} integration config`,
79
+ body: {
80
+ type: 'object',
81
+ properties: {
82
+ pipeline: { type: 'string' },
83
+ active: { type: 'boolean' },
84
+ },
85
+ },
86
+ response: {
87
+ 200: {
88
+ type: 'object',
89
+ properties: {
90
+ webhook_url: { type: 'string' },
91
+ pipeline: { type: ['string', 'null'] },
92
+ active: { type: 'boolean' },
93
+ },
94
+ },
95
+ 400: errorSchema,
96
+ },
97
+ },
98
+ }, async (request, reply) => {
99
+ let data;
100
+ try {
101
+ data = JSON.parse(request.body.toString('utf-8'));
102
+ }
103
+ catch {
104
+ return reply.status(400).send({ error: 'Invalid JSON' });
105
+ }
106
+ store.patchConfig(name, data);
107
+ const updated = store.getConfig(name);
108
+ const baseUrl = process.env['STUDIO_BASE_URL'] ?? `${request.protocol}://${request.hostname}`;
109
+ return reply.status(200).send({
110
+ webhook_url: `${baseUrl}/api/integrations/${name}/webhook`,
111
+ pipeline: updated.pipeline ?? null,
112
+ active: updated.active,
113
+ });
114
+ });
115
+ // POST /api/integrations/{name}/webhook
116
+ scope.post(`${prefix}/integrations/${name}/webhook`, {
117
+ schema: {
118
+ tags: ['integrations'],
119
+ summary: `Receive ${name} webhook event`,
120
+ response: {
121
+ 202: {
122
+ type: 'object',
123
+ properties: { run_id: { type: 'string' }, stream_url: { type: 'string' } },
124
+ },
125
+ 200: {
126
+ type: 'object',
127
+ properties: { ignored: { type: 'boolean' }, reason: { type: 'string' } },
128
+ },
129
+ 400: errorSchema,
130
+ 401: errorSchema,
131
+ },
132
+ },
133
+ }, async (request, reply) => {
134
+ return webhookHandler.handle({
135
+ rawBody: request.body,
136
+ headers: request.headers,
137
+ integration,
138
+ store,
139
+ launcher,
140
+ configsDir,
141
+ projectsDir,
142
+ apiConfig,
143
+ integrationConfig,
144
+ }, reply);
145
+ });
146
+ });
147
+ }
148
+ }
149
+ }
150
+ //# sourceMappingURL=integration-runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-runtime.js","sourceRoot":"","sources":["../src/integration-runtime.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ export interface IntegrationConfig {
2
+ pipeline?: string;
3
+ active: boolean;
4
+ }
5
+ export interface IntegrationTriggerRecord {
6
+ id: string;
7
+ integration_name: string;
8
+ received_at: string;
9
+ external_id?: string;
10
+ external_label?: string;
11
+ external_url?: string;
12
+ pipeline: string;
13
+ run_id?: string;
14
+ status: 'success' | 'failed';
15
+ }
16
+ export declare class IntegrationStore {
17
+ private db;
18
+ constructor(dbPath: string);
19
+ private initSchema;
20
+ getConfig(integrationName: string): IntegrationConfig;
21
+ patchConfig(integrationName: string, data: Partial<IntegrationConfig>): void;
22
+ insertTrigger(trigger: IntegrationTriggerRecord): void;
23
+ listTriggers(integrationName: string, limit?: number): IntegrationTriggerRecord[];
24
+ close(): void;
25
+ }
26
+ //# sourceMappingURL=integration-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-store.d.ts","sourceRoot":"","sources":["../src/integration-store.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,CAAC;IACX,gBAAgB,EAAE,MAAM,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,GAAG,QAAQ,CAAC;CAC9B;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,EAAE,CAAoC;gBAElC,MAAM,EAAE,MAAM;IAQ1B,OAAO,CAAC,UAAU;IAuBlB,SAAS,CAAC,eAAe,EAAE,MAAM,GAAG,iBAAiB;IAYrD,WAAW,CAAC,eAAe,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,IAAI;IAU5E,aAAa,CAAC,OAAO,EAAE,wBAAwB,GAAG,IAAI;IAkBtD,YAAY,CAAC,eAAe,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,wBAAwB,EAAE;IAwB7E,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,80 @@
1
+ // IntegrationStore — generic SQLite persistence for integration configs and trigger logs
2
+ // Partitioned by integration_name — supports any integration (linear, slack, github, etc.)
3
+ // Uses the same DB file as the run store (.studio/runs/runs.db)
4
+ import { createRequire } from 'node:module';
5
+ export class IntegrationStore {
6
+ db;
7
+ constructor(dbPath) {
8
+ const _require = createRequire(import.meta.url);
9
+ const Database = _require('better-sqlite3');
10
+ this.db = new Database(dbPath);
11
+ this.db.pragma('journal_mode = WAL');
12
+ this.initSchema();
13
+ }
14
+ initSchema() {
15
+ this.db.exec(`
16
+ CREATE TABLE IF NOT EXISTS integration_config (
17
+ integration_name TEXT NOT NULL,
18
+ pipeline TEXT,
19
+ active INTEGER NOT NULL DEFAULT 0,
20
+ PRIMARY KEY (integration_name)
21
+ );
22
+
23
+ CREATE TABLE IF NOT EXISTS integration_triggers (
24
+ id TEXT PRIMARY KEY,
25
+ integration_name TEXT NOT NULL,
26
+ received_at TEXT NOT NULL,
27
+ external_id TEXT,
28
+ external_label TEXT,
29
+ external_url TEXT,
30
+ pipeline TEXT NOT NULL,
31
+ run_id TEXT,
32
+ status TEXT NOT NULL
33
+ );
34
+ `);
35
+ }
36
+ getConfig(integrationName) {
37
+ const row = this.db.prepare('SELECT pipeline, active FROM integration_config WHERE integration_name = ?').get(integrationName);
38
+ if (!row)
39
+ return { active: false };
40
+ return {
41
+ ...(row.pipeline != null ? { pipeline: row.pipeline } : {}),
42
+ active: row.active === 1,
43
+ };
44
+ }
45
+ patchConfig(integrationName, data) {
46
+ const current = this.getConfig(integrationName);
47
+ const pipeline = 'pipeline' in data ? (data.pipeline ?? null) : (current.pipeline ?? null);
48
+ const active = 'active' in data ? (data.active ? 1 : 0) : (current.active ? 1 : 0);
49
+ this.db.prepare(`
50
+ INSERT INTO integration_config (integration_name, pipeline, active) VALUES (?, ?, ?)
51
+ ON CONFLICT(integration_name) DO UPDATE SET pipeline = excluded.pipeline, active = excluded.active
52
+ `).run(integrationName, pipeline, active);
53
+ }
54
+ insertTrigger(trigger) {
55
+ this.db.prepare(`
56
+ INSERT INTO integration_triggers
57
+ (id, integration_name, received_at, external_id, external_label, external_url, pipeline, run_id, status)
58
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
59
+ `).run(trigger.id, trigger.integration_name, trigger.received_at, trigger.external_id ?? null, trigger.external_label ?? null, trigger.external_url ?? null, trigger.pipeline, trigger.run_id ?? null, trigger.status);
60
+ }
61
+ listTriggers(integrationName, limit = 50) {
62
+ const rows = this.db.prepare(`SELECT id, integration_name, received_at, external_id, external_label, external_url, pipeline, run_id, status
63
+ FROM integration_triggers WHERE integration_name = ? ORDER BY received_at DESC LIMIT ?`).all(integrationName, limit);
64
+ return rows.map(row => ({
65
+ id: row.id,
66
+ integration_name: row.integration_name,
67
+ received_at: row.received_at,
68
+ ...(row.external_id != null ? { external_id: row.external_id } : {}),
69
+ ...(row.external_label != null ? { external_label: row.external_label } : {}),
70
+ ...(row.external_url != null ? { external_url: row.external_url } : {}),
71
+ pipeline: row.pipeline,
72
+ ...(row.run_id != null ? { run_id: row.run_id } : {}),
73
+ status: row.status,
74
+ }));
75
+ }
76
+ close() {
77
+ this.db.close();
78
+ }
79
+ }
80
+ //# sourceMappingURL=integration-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"integration-store.js","sourceRoot":"","sources":["../src/integration-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { FailureHandler, FailureHandlerContext } from '../types.js';
2
+ export declare class LinearFailureHandler implements FailureHandler {
3
+ handleFailure(ctx: FailureHandlerContext): Promise<void>;
4
+ }
5
+ //# sourceMappingURL=failure-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-handler.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/failure-handler.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAqCzE,qBAAa,oBAAqB,YAAW,cAAc;IACnD,aAAa,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC;CAqD/D"}
@@ -0,0 +1,73 @@
1
+ const LINEAR_GRAPHQL_URL = 'https://api.linear.app/graphql';
2
+ async function gql(query, variables, apiKey) {
3
+ const res = await fetch(LINEAR_GRAPHQL_URL, {
4
+ method: 'POST',
5
+ headers: { 'Content-Type': 'application/json', Authorization: apiKey },
6
+ body: JSON.stringify({ query, variables }),
7
+ });
8
+ if (!res.ok)
9
+ throw new Error(`Linear GraphQL HTTP error: ${res.status} ${res.statusText}`);
10
+ return res.json();
11
+ }
12
+ function buildFailureComment(ctx) {
13
+ const iterations = ctx.lastGroupFeedback?.iteration;
14
+ const iterLabel = iterations != null ? ` après ${iterations} itérations QA` : '';
15
+ const rejectionReason = ctx.lastGroupFeedback?.rejection_reason;
16
+ const rejectionDetails = ctx.lastGroupFeedback?.rejection_details;
17
+ const lines = [];
18
+ lines.push(`❌ **Code Builder échoué** —${iterLabel ? ` QA a rejeté${iterLabel}` : ' pipeline échoué'}`);
19
+ lines.push('');
20
+ if (rejectionReason) {
21
+ lines.push('**Dernière raison de rejet :**');
22
+ if (rejectionDetails && rejectionDetails.length > 0) {
23
+ for (const detail of rejectionDetails)
24
+ lines.push(`- ${detail}`);
25
+ }
26
+ else {
27
+ lines.push(`- ${rejectionReason}`);
28
+ }
29
+ lines.push('');
30
+ }
31
+ lines.push('**Action requise :** réviser le brief ou augmenter max_iterations');
32
+ lines.push('');
33
+ lines.push(`**Run ID :** ${ctx.runId}`);
34
+ return lines.join('\n');
35
+ }
36
+ export class LinearFailureHandler {
37
+ async handleFailure(ctx) {
38
+ const issueId = typeof ctx.meta['linear_issue_id'] === 'string' ? ctx.meta['linear_issue_id'] : undefined;
39
+ if (!issueId)
40
+ return;
41
+ const apiKey = ctx.integrationConfig['LINEAR_API_KEY']
42
+ ?? process.env['LINEAR_API_KEY'];
43
+ if (!apiKey) {
44
+ console.warn('[linear-failure-handler] LINEAR_API_KEY not set — skipping failure notification');
45
+ return;
46
+ }
47
+ const comment = buildFailureComment(ctx);
48
+ try {
49
+ await gql(`mutation CreateComment($issueId: String!, $body: String!) {
50
+ commentCreate(input: { issueId: $issueId, body: $body }) { success }
51
+ }`, { issueId, body: comment }, apiKey);
52
+ const statesResult = await gql(`query GetWorkflowStates($issueId: String!) {
53
+ issue(id: $issueId) { team { states { nodes { id name } } } }
54
+ }`, { issueId }, apiKey);
55
+ const nodes = (statesResult
56
+ .data?.issue?.team?.states?.nodes) ?? [];
57
+ const backlogState = nodes.find((s) => s.name === 'Backlog');
58
+ if (backlogState) {
59
+ await gql(`mutation UpdateIssue($id: String!, $stateId: String!) {
60
+ issueUpdate(id: $id, input: { stateId: $stateId }) { success }
61
+ }`, { id: issueId, stateId: backlogState.id }, apiKey);
62
+ }
63
+ else {
64
+ console.warn(`[linear-failure-handler] "Backlog" state not found for issue ${issueId} — status not updated`);
65
+ }
66
+ console.log(`[linear-failure-handler] Failure notification posted for issue ${issueId}`);
67
+ }
68
+ catch (err) {
69
+ console.error('[linear-failure-handler] Failed to notify Linear:', err);
70
+ }
71
+ }
72
+ }
73
+ //# sourceMappingURL=failure-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"failure-handler.js","sourceRoot":"","sources":["../../../src/integrations/linear/failure-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { FastifyReply } from 'fastify';
2
+ import type { WebhookHandler, WebhookHandlerContext } from '../types.js';
3
+ export declare class LinearWebhookHandler implements WebhookHandler {
4
+ handle(ctx: WebhookHandlerContext, reply: FastifyReply): Promise<unknown>;
5
+ }
6
+ //# sourceMappingURL=webhook-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-handler.d.ts","sourceRoot":"","sources":["../../../src/integrations/linear/webhook-handler.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AA4BzE,qBAAa,oBAAqB,YAAW,cAAc;IACnD,MAAM,CAAC,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC;CAuHhF"}
@@ -0,0 +1,131 @@
1
+ // LinearWebhookHandler — plugin class implementing WebhookHandler
2
+ // Extracted from routes/linear-webhook.ts POST /integrations/linear/webhook
3
+ // Receives all deps via WebhookHandlerContext instead of Fastify plugin closure.
4
+ import { createHmac, timingSafeEqual, randomUUID } from 'node:crypto';
5
+ import { join } from 'node:path';
6
+ import { loadPipelineByName } from '@studio-foundation/engine';
7
+ import { resolveRepoPath } from '../../utils/repo-resolver.js';
8
+ function verifyHmac(rawBody, signature, secret) {
9
+ const computed = createHmac('sha256', secret).update(rawBody).digest('hex');
10
+ try {
11
+ const a = Buffer.from(computed, 'hex');
12
+ const b = Buffer.from(signature, 'hex');
13
+ if (a.length !== b.length)
14
+ return false;
15
+ return timingSafeEqual(a, b);
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ export class LinearWebhookHandler {
22
+ async handle(ctx, reply) {
23
+ const { rawBody, headers, integration, store, launcher, configsDir, projectsDir, integrationConfig } = ctx;
24
+ // HMAC verification — only when hmac config is present in the integration def
25
+ const hmacConfig = integration.webhook?.hmac;
26
+ if (hmacConfig) {
27
+ const secret = integrationConfig[hmacConfig.secret_env];
28
+ if (secret) {
29
+ const sig = headers[hmacConfig.header];
30
+ if (typeof sig !== 'string') {
31
+ return reply.status(401).send({ error: `Missing ${hmacConfig.header} header` });
32
+ }
33
+ if (!verifyHmac(rawBody, sig, secret)) {
34
+ return reply.status(401).send({ error: 'Invalid signature' });
35
+ }
36
+ }
37
+ }
38
+ let payload;
39
+ try {
40
+ payload = JSON.parse(rawBody.toString('utf-8'));
41
+ }
42
+ catch {
43
+ return reply.status(400).send({ error: 'Invalid JSON' });
44
+ }
45
+ // Only handle Issue update events
46
+ if (payload.type !== 'Issue' || payload.action !== 'update') {
47
+ return reply.status(200).send({ ignored: true, reason: 'not an issue update' });
48
+ }
49
+ const issue = payload.data ?? {};
50
+ // Only trigger on transitions to "In Progress"
51
+ if (issue.state?.name !== 'In Progress') {
52
+ return reply.status(200).send({ ignored: true, reason: `state is "${issue.state?.name ?? 'unknown'}"` });
53
+ }
54
+ // Check integration is active
55
+ const config = store.getConfig(integration.name);
56
+ if (!config.active) {
57
+ return reply.status(200).send({ ignored: true, reason: 'integration is inactive' });
58
+ }
59
+ const pipeline = config.pipeline ?? 'feature-builder';
60
+ const issueUrl = `https://linear.app/studioag/issue/${issue.identifier}`;
61
+ // Resolve repo path from pipeline YAML — mirrors CLI behaviour.
62
+ // If the pipeline file can't be loaded, fall back gracefully —
63
+ // launcher.launch() will surface the proper "pipeline not found" error afterwards.
64
+ let pipelineRepoUrl;
65
+ let pipelineRepoBranch;
66
+ try {
67
+ const pipelineDef = await loadPipelineByName(pipeline, join(configsDir, 'pipelines'));
68
+ pipelineRepoUrl = pipelineDef.repo?.url;
69
+ pipelineRepoBranch = pipelineDef.repo?.branch;
70
+ }
71
+ catch {
72
+ // Pipeline not found or unparseable — launcher.launch() will handle the error
73
+ }
74
+ let repoPath;
75
+ try {
76
+ repoPath = await resolveRepoPath({
77
+ repoUrl: pipelineRepoUrl,
78
+ rawProjectsDir: projectsDir,
79
+ pipelineName: pipeline,
80
+ branch: pipelineRepoBranch,
81
+ });
82
+ }
83
+ catch (err) {
84
+ return reply.status(400).send({ error: err instanceof Error ? err.message : String(err) });
85
+ }
86
+ // Construct structured input for the configured pipeline
87
+ const input = {
88
+ brief_summary: [issue.identifier, issue.title].filter(Boolean).join(' — '),
89
+ description: issue.description ?? '',
90
+ acceptance_criteria: [],
91
+ };
92
+ const meta = {
93
+ linear_issue_id: issue.id,
94
+ linear_issue_identifier: issue.identifier,
95
+ linear_issue_url: issueUrl,
96
+ };
97
+ const runId = randomUUID();
98
+ const triggerId = randomUUID();
99
+ const receivedAt = new Date().toISOString();
100
+ try {
101
+ await launcher.launch({ runId, pipeline, input, configsDir, repoPath, meta });
102
+ store.insertTrigger({
103
+ id: triggerId,
104
+ integration_name: integration.name,
105
+ received_at: receivedAt,
106
+ external_id: issue.id,
107
+ external_label: [issue.identifier, issue.title].filter(Boolean).join(' — '),
108
+ external_url: issueUrl,
109
+ pipeline,
110
+ run_id: runId,
111
+ status: 'success',
112
+ });
113
+ }
114
+ catch (err) {
115
+ store.insertTrigger({
116
+ id: triggerId,
117
+ integration_name: integration.name,
118
+ received_at: receivedAt,
119
+ external_id: issue.id,
120
+ external_label: [issue.identifier, issue.title].filter(Boolean).join(' — '),
121
+ external_url: issueUrl,
122
+ pipeline,
123
+ run_id: runId,
124
+ status: 'failed',
125
+ });
126
+ throw err;
127
+ }
128
+ return reply.status(202).send({ run_id: runId, stream_url: `/api/runs/${runId}/stream` });
129
+ }
130
+ }
131
+ //# sourceMappingURL=webhook-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-handler.js","sourceRoot":"","sources":["../../../src/integrations/linear/webhook-handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { WebhookHandler, FailureHandler } from './types.js';
2
+ export declare const WEBHOOK_HANDLERS: Record<string, WebhookHandler>;
3
+ export declare const FAILURE_HANDLERS: Record<string, FailureHandler>;
4
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjE,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAE3D,CAAC;AAEF,eAAO,MAAM,gBAAgB,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAE3D,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { LinearWebhookHandler } from './linear/webhook-handler.js';
2
+ import { LinearFailureHandler } from './linear/failure-handler.js';
3
+ export const WEBHOOK_HANDLERS = {
4
+ 'linear-webhook': new LinearWebhookHandler(),
5
+ };
6
+ export const FAILURE_HANDLERS = {
7
+ 'linear-failure': new LinearFailureHandler(),
8
+ };
9
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/integrations/registry.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACnE,OAAO,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AAEnE,MAAM,CAAC,MAAM,gBAAgB,GAAmC;IAC9D,gBAAgB,EAAE,IAAI,oBAAoB,EAAE;CAC7C,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAmC;IAC9D,gBAAgB,EAAE,IAAI,oBAAoB,EAAE;CAC7C,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { FastifyReply } from 'fastify';
2
+ import type { IntegrationPluginDef } from '@studio-foundation/contracts';
3
+ import type { GroupFeedbackEvent } from '@studio-foundation/engine';
4
+ import type { IntegrationStore } from '../integration-store.js';
5
+ import type { RunLauncher } from '../launcher.js';
6
+ import type { ApiConfig } from '../server.js';
7
+ export interface WebhookHandlerContext {
8
+ rawBody: Buffer;
9
+ headers: Record<string, string | string[] | undefined>;
10
+ integration: IntegrationPluginDef;
11
+ store: IntegrationStore;
12
+ launcher: RunLauncher;
13
+ configsDir: string;
14
+ projectsDir?: string;
15
+ apiConfig: ApiConfig;
16
+ integrationConfig: Record<string, unknown>;
17
+ }
18
+ export interface FailureHandlerContext {
19
+ runId: string;
20
+ durationMs: number;
21
+ status: string;
22
+ meta: Record<string, unknown>;
23
+ lastGroupFeedback?: GroupFeedbackEvent;
24
+ integration: IntegrationPluginDef;
25
+ integrationConfig: Record<string, unknown>;
26
+ }
27
+ export interface WebhookHandler {
28
+ handle(ctx: WebhookHandlerContext, reply: FastifyReply): Promise<unknown>;
29
+ }
30
+ export interface FailureHandler {
31
+ handleFailure(ctx: FailureHandlerContext): Promise<void>;
32
+ }
33
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/integrations/types.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAC5C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE9C,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,WAAW,EAAE,oBAAoB,CAAC;IAClC,KAAK,EAAE,gBAAgB,CAAC;IACxB,QAAQ,EAAE,WAAW,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,SAAS,CAAC;IACrB,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC9B,iBAAiB,CAAC,EAAE,kBAAkB,CAAC;IACvC,WAAW,EAAE,oBAAoB,CAAC;IAClC,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAC5C;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,GAAG,EAAE,qBAAqB,EAAE,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC3E;AAED,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,GAAG,EAAE,qBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/integrations/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import type { EngineConfig, EngineEvents, AnyRunStore } from '@studio-foundation/engine';
2
+ import { PipelineEngine } from '@studio-foundation/engine';
3
+ import type { RunEventBus, BusListener } from './event-bus.js';
4
+ import type { AnyUserStore } from './user-store-pg.js';
5
+ import { type PlansConfig } from './plans.js';
6
+ export interface LaunchConfig {
7
+ runId: string;
8
+ pipeline: string;
9
+ input: Record<string, unknown>;
10
+ configsDir: string;
11
+ /** Pre-resolved repo path (cloned or local). Overrides engineConfig.repoPath for this run. */
12
+ repoPath?: string;
13
+ providerOverride?: string;
14
+ depth?: number;
15
+ parentRunId?: string;
16
+ meta?: Record<string, unknown>;
17
+ userId?: string;
18
+ }
19
+ export type EngineFactory = (config: EngineConfig, events: EngineEvents) => Pick<PipelineEngine, 'run'>;
20
+ export interface RunLauncher {
21
+ launch(config: LaunchConfig): Promise<{
22
+ run_id: string;
23
+ }>;
24
+ cancel(run_id: string): Promise<void>;
25
+ subscribe(runId: string, listener: BusListener): () => void;
26
+ }
27
+ export declare class InProcessLauncher implements RunLauncher {
28
+ private engineConfig;
29
+ private store;
30
+ private runsDir;
31
+ private bus;
32
+ private engineFactory;
33
+ private userStore?;
34
+ private plans;
35
+ private active;
36
+ private activePerUser;
37
+ constructor(engineConfig: EngineConfig, store: AnyRunStore, runsDir: string, bus: RunEventBus, engineFactory?: EngineFactory, userStore?: AnyUserStore | undefined, plans?: PlansConfig);
38
+ subscribe(runId: string, listener: BusListener): () => void;
39
+ launch(config: LaunchConfig): Promise<{
40
+ run_id: string;
41
+ }>;
42
+ cancel(run_id: string): Promise<void>;
43
+ }
44
+ export { randomUUID as generateRunId } from 'node:crypto';
45
+ //# sourceMappingURL=launcher.d.ts.map