sylas-edge-worker 0.2.21

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 (163) hide show
  1. package/README.md +293 -0
  2. package/dist/ActivityPoster.d.ts +15 -0
  3. package/dist/ActivityPoster.d.ts.map +1 -0
  4. package/dist/ActivityPoster.js +194 -0
  5. package/dist/ActivityPoster.js.map +1 -0
  6. package/dist/AgentSessionManager.d.ts +280 -0
  7. package/dist/AgentSessionManager.d.ts.map +1 -0
  8. package/dist/AgentSessionManager.js +1412 -0
  9. package/dist/AgentSessionManager.js.map +1 -0
  10. package/dist/AskUserQuestionHandler.d.ts +97 -0
  11. package/dist/AskUserQuestionHandler.d.ts.map +1 -0
  12. package/dist/AskUserQuestionHandler.js +206 -0
  13. package/dist/AskUserQuestionHandler.js.map +1 -0
  14. package/dist/AttachmentService.d.ts +69 -0
  15. package/dist/AttachmentService.d.ts.map +1 -0
  16. package/dist/AttachmentService.js +369 -0
  17. package/dist/AttachmentService.js.map +1 -0
  18. package/dist/ChatSessionHandler.d.ts +87 -0
  19. package/dist/ChatSessionHandler.d.ts.map +1 -0
  20. package/dist/ChatSessionHandler.js +231 -0
  21. package/dist/ChatSessionHandler.js.map +1 -0
  22. package/dist/ConfigManager.d.ts +91 -0
  23. package/dist/ConfigManager.d.ts.map +1 -0
  24. package/dist/ConfigManager.js +227 -0
  25. package/dist/ConfigManager.js.map +1 -0
  26. package/dist/EdgeWorker.d.ts +670 -0
  27. package/dist/EdgeWorker.d.ts.map +1 -0
  28. package/dist/EdgeWorker.js +3801 -0
  29. package/dist/EdgeWorker.js.map +1 -0
  30. package/dist/GitService.d.ts +39 -0
  31. package/dist/GitService.d.ts.map +1 -0
  32. package/dist/GitService.js +432 -0
  33. package/dist/GitService.js.map +1 -0
  34. package/dist/GlobalSessionRegistry.d.ts +142 -0
  35. package/dist/GlobalSessionRegistry.d.ts.map +1 -0
  36. package/dist/GlobalSessionRegistry.js +254 -0
  37. package/dist/GlobalSessionRegistry.js.map +1 -0
  38. package/dist/PromptBuilder.d.ts +175 -0
  39. package/dist/PromptBuilder.d.ts.map +1 -0
  40. package/dist/PromptBuilder.js +884 -0
  41. package/dist/PromptBuilder.js.map +1 -0
  42. package/dist/RepositoryRouter.d.ts +152 -0
  43. package/dist/RepositoryRouter.d.ts.map +1 -0
  44. package/dist/RepositoryRouter.js +480 -0
  45. package/dist/RepositoryRouter.js.map +1 -0
  46. package/dist/RunnerSelectionService.d.ts +62 -0
  47. package/dist/RunnerSelectionService.d.ts.map +1 -0
  48. package/dist/RunnerSelectionService.js +379 -0
  49. package/dist/RunnerSelectionService.js.map +1 -0
  50. package/dist/SharedApplicationServer.d.ts +107 -0
  51. package/dist/SharedApplicationServer.d.ts.map +1 -0
  52. package/dist/SharedApplicationServer.js +247 -0
  53. package/dist/SharedApplicationServer.js.map +1 -0
  54. package/dist/SharedWebhookServer.d.ts +39 -0
  55. package/dist/SharedWebhookServer.d.ts.map +1 -0
  56. package/dist/SharedWebhookServer.js +150 -0
  57. package/dist/SharedWebhookServer.js.map +1 -0
  58. package/dist/SlackChatAdapter.d.ts +25 -0
  59. package/dist/SlackChatAdapter.d.ts.map +1 -0
  60. package/dist/SlackChatAdapter.js +143 -0
  61. package/dist/SlackChatAdapter.js.map +1 -0
  62. package/dist/UserAccessControl.d.ts +69 -0
  63. package/dist/UserAccessControl.d.ts.map +1 -0
  64. package/dist/UserAccessControl.js +171 -0
  65. package/dist/UserAccessControl.js.map +1 -0
  66. package/dist/WorktreeIncludeService.d.ts +32 -0
  67. package/dist/WorktreeIncludeService.d.ts.map +1 -0
  68. package/dist/WorktreeIncludeService.js +123 -0
  69. package/dist/WorktreeIncludeService.js.map +1 -0
  70. package/dist/index.d.ts +22 -0
  71. package/dist/index.d.ts.map +1 -0
  72. package/dist/index.js +17 -0
  73. package/dist/index.js.map +1 -0
  74. package/dist/label-prompt-template.md +27 -0
  75. package/dist/procedures/ProcedureAnalyzer.d.ts +69 -0
  76. package/dist/procedures/ProcedureAnalyzer.d.ts.map +1 -0
  77. package/dist/procedures/ProcedureAnalyzer.js +271 -0
  78. package/dist/procedures/ProcedureAnalyzer.js.map +1 -0
  79. package/dist/procedures/index.d.ts +7 -0
  80. package/dist/procedures/index.d.ts.map +1 -0
  81. package/dist/procedures/index.js +7 -0
  82. package/dist/procedures/index.js.map +1 -0
  83. package/dist/procedures/registry.d.ts +156 -0
  84. package/dist/procedures/registry.d.ts.map +1 -0
  85. package/dist/procedures/registry.js +240 -0
  86. package/dist/procedures/registry.js.map +1 -0
  87. package/dist/procedures/types.d.ts +103 -0
  88. package/dist/procedures/types.d.ts.map +1 -0
  89. package/dist/procedures/types.js +5 -0
  90. package/dist/procedures/types.js.map +1 -0
  91. package/dist/prompt-assembly/types.d.ts +80 -0
  92. package/dist/prompt-assembly/types.d.ts.map +1 -0
  93. package/dist/prompt-assembly/types.js +8 -0
  94. package/dist/prompt-assembly/types.js.map +1 -0
  95. package/dist/prompts/builder.md +191 -0
  96. package/dist/prompts/debugger.md +128 -0
  97. package/dist/prompts/graphite-orchestrator.md +362 -0
  98. package/dist/prompts/orchestrator.md +290 -0
  99. package/dist/prompts/scoper.md +95 -0
  100. package/dist/prompts/standard-issue-assigned-user-prompt.md +33 -0
  101. package/dist/prompts/subroutines/changelog-update.md +79 -0
  102. package/dist/prompts/subroutines/coding-activity.md +12 -0
  103. package/dist/prompts/subroutines/concise-summary.md +67 -0
  104. package/dist/prompts/subroutines/debugger-fix.md +92 -0
  105. package/dist/prompts/subroutines/debugger-reproduction.md +74 -0
  106. package/dist/prompts/subroutines/full-delegation.md +68 -0
  107. package/dist/prompts/subroutines/get-approval.md +175 -0
  108. package/dist/prompts/subroutines/gh-pr.md +80 -0
  109. package/dist/prompts/subroutines/git-commit.md +37 -0
  110. package/dist/prompts/subroutines/plan-summary.md +21 -0
  111. package/dist/prompts/subroutines/preparation.md +16 -0
  112. package/dist/prompts/subroutines/question-answer.md +8 -0
  113. package/dist/prompts/subroutines/question-investigation.md +8 -0
  114. package/dist/prompts/subroutines/release-execution.md +81 -0
  115. package/dist/prompts/subroutines/release-summary.md +60 -0
  116. package/dist/prompts/subroutines/user-testing-summary.md +87 -0
  117. package/dist/prompts/subroutines/user-testing.md +48 -0
  118. package/dist/prompts/subroutines/validation-fixer.md +56 -0
  119. package/dist/prompts/subroutines/verbose-summary.md +46 -0
  120. package/dist/prompts/subroutines/verifications.md +77 -0
  121. package/dist/prompts/todolist-system-prompt-extension.md +15 -0
  122. package/dist/sinks/IActivitySink.d.ts +60 -0
  123. package/dist/sinks/IActivitySink.d.ts.map +1 -0
  124. package/dist/sinks/IActivitySink.js +2 -0
  125. package/dist/sinks/IActivitySink.js.map +1 -0
  126. package/dist/sinks/LinearActivitySink.d.ts +69 -0
  127. package/dist/sinks/LinearActivitySink.d.ts.map +1 -0
  128. package/dist/sinks/LinearActivitySink.js +111 -0
  129. package/dist/sinks/LinearActivitySink.js.map +1 -0
  130. package/dist/sinks/NoopActivitySink.d.ts +13 -0
  131. package/dist/sinks/NoopActivitySink.d.ts.map +1 -0
  132. package/dist/sinks/NoopActivitySink.js +17 -0
  133. package/dist/sinks/NoopActivitySink.js.map +1 -0
  134. package/dist/sinks/index.d.ts +9 -0
  135. package/dist/sinks/index.d.ts.map +1 -0
  136. package/dist/sinks/index.js +8 -0
  137. package/dist/sinks/index.js.map +1 -0
  138. package/dist/types.d.ts +32 -0
  139. package/dist/types.d.ts.map +1 -0
  140. package/dist/types.js +2 -0
  141. package/dist/types.js.map +1 -0
  142. package/dist/validation/ValidationLoopController.d.ts +54 -0
  143. package/dist/validation/ValidationLoopController.d.ts.map +1 -0
  144. package/dist/validation/ValidationLoopController.js +242 -0
  145. package/dist/validation/ValidationLoopController.js.map +1 -0
  146. package/dist/validation/index.d.ts +7 -0
  147. package/dist/validation/index.d.ts.map +1 -0
  148. package/dist/validation/index.js +7 -0
  149. package/dist/validation/index.js.map +1 -0
  150. package/dist/validation/types.d.ts +82 -0
  151. package/dist/validation/types.d.ts.map +1 -0
  152. package/dist/validation/types.js +29 -0
  153. package/dist/validation/types.js.map +1 -0
  154. package/label-prompt-template.md +27 -0
  155. package/package.json +56 -0
  156. package/prompt-template.md +116 -0
  157. package/prompts/builder.md +191 -0
  158. package/prompts/debugger.md +128 -0
  159. package/prompts/graphite-orchestrator.md +362 -0
  160. package/prompts/orchestrator.md +290 -0
  161. package/prompts/scoper.md +95 -0
  162. package/prompts/standard-issue-assigned-user-prompt.md +33 -0
  163. package/prompts/todolist-system-prompt-extension.md +15 -0
@@ -0,0 +1,247 @@
1
+ import Fastify from "fastify";
2
+ import { CloudflareTunnelClient } from "sylas-cloudflare-tunnel-client";
3
+ import { createLogger } from "sylas-core";
4
+ /**
5
+ * Shared application server that handles both webhooks and OAuth callbacks on a single port
6
+ * Consolidates functionality from SharedWebhookServer and CLI OAuth server
7
+ */
8
+ export class SharedApplicationServer {
9
+ app = null;
10
+ webhookHandlers = new Map();
11
+ // Legacy handlers for direct Linear webhook registration (deprecated)
12
+ linearWebhookHandlers = new Map();
13
+ oauthCallbacks = new Map();
14
+ pendingApprovals = new Map();
15
+ port;
16
+ host;
17
+ isListening = false;
18
+ tunnelClient = null;
19
+ skipTunnel;
20
+ logger;
21
+ constructor(port = 3456, host = "localhost", skipTunnel = false, logger) {
22
+ this.port = port;
23
+ this.host = host;
24
+ this.skipTunnel = skipTunnel;
25
+ this.logger =
26
+ logger ?? createLogger({ component: "SharedApplicationServer" });
27
+ }
28
+ /**
29
+ * Initialize the Fastify app instance (must be called before registering routes)
30
+ */
31
+ initializeFastify() {
32
+ if (this.app) {
33
+ return; // Already initialized
34
+ }
35
+ this.app = Fastify({
36
+ logger: false,
37
+ });
38
+ }
39
+ /**
40
+ * Start the shared application server
41
+ */
42
+ async start() {
43
+ if (this.isListening) {
44
+ return; // Already listening
45
+ }
46
+ // Initialize Fastify if not already done
47
+ this.initializeFastify();
48
+ try {
49
+ await this.app.listen({
50
+ port: this.port,
51
+ host: this.host,
52
+ });
53
+ this.isListening = true;
54
+ this.logger.info(`Shared application server listening on http://${this.host}:${this.port}`);
55
+ // Start Cloudflare tunnel if CLOUDFLARE_TOKEN is set and tunnel is not skipped
56
+ if (!this.skipTunnel && process.env.CLOUDFLARE_TOKEN) {
57
+ await this.startCloudflareTunnel(process.env.CLOUDFLARE_TOKEN);
58
+ }
59
+ }
60
+ catch (error) {
61
+ this.isListening = false;
62
+ throw error;
63
+ }
64
+ }
65
+ /**
66
+ * Start Cloudflare tunnel and wait for 4 'connected' events
67
+ */
68
+ async startCloudflareTunnel(cloudflareToken) {
69
+ return new Promise((resolve, reject) => {
70
+ let connectionCount = 0;
71
+ const requiredConnections = 4;
72
+ this.tunnelClient = new CloudflareTunnelClient(cloudflareToken, this.port);
73
+ // Listen for connection events (Cloudflare establishes 4 connections per tunnel)
74
+ this.tunnelClient.on("connected", () => {
75
+ connectionCount++;
76
+ this.logger.info(`Cloudflare tunnel connection ${connectionCount}/${requiredConnections} established`);
77
+ if (connectionCount === requiredConnections) {
78
+ this.logger.info("Cloudflare tunnel fully connected and ready");
79
+ resolve();
80
+ }
81
+ });
82
+ // Listen for ready event to get tunnel URL
83
+ this.tunnelClient.on("ready", (tunnelUrl) => {
84
+ this.logger.info(`Cloudflare tunnel URL: ${tunnelUrl}`);
85
+ });
86
+ // Listen for error events
87
+ this.tunnelClient.on("error", (error) => {
88
+ this.logger.error("Cloudflare tunnel error:", error);
89
+ reject(error);
90
+ });
91
+ // Listen for disconnect events
92
+ this.tunnelClient.on("disconnect", (reason) => {
93
+ this.logger.info(`Cloudflare tunnel disconnected: ${reason}`);
94
+ });
95
+ // Start the tunnel
96
+ this.tunnelClient.startTunnel().catch(reject);
97
+ // Timeout after 30 seconds
98
+ setTimeout(() => {
99
+ if (connectionCount < requiredConnections) {
100
+ reject(new Error(`Timeout waiting for Cloudflare tunnel (${connectionCount}/${requiredConnections} connections). This is usually caused by firewall/VPN/proxy blocking cloudflared. See troubleshooting: https://github.com/smilebank7/sylas/blob/main/docs/CLOUDFLARE_TUNNEL.md#troubleshooting`));
101
+ }
102
+ }, 30000);
103
+ });
104
+ }
105
+ /**
106
+ * Stop the shared application server
107
+ */
108
+ async stop() {
109
+ // Reject all pending approvals before shutdown
110
+ for (const [sessionId, approval] of this.pendingApprovals) {
111
+ approval.reject(new Error("Server shutting down"));
112
+ this.logger.debug(`Rejected pending approval for session ${sessionId} due to shutdown`);
113
+ }
114
+ this.pendingApprovals.clear();
115
+ // Stop Cloudflare tunnel if running
116
+ if (this.tunnelClient) {
117
+ this.tunnelClient.disconnect();
118
+ this.tunnelClient = null;
119
+ this.logger.info("Cloudflare tunnel stopped");
120
+ }
121
+ if (this.app && this.isListening) {
122
+ await this.app.close();
123
+ this.isListening = false;
124
+ this.logger.info("Shared application server stopped");
125
+ }
126
+ }
127
+ /**
128
+ * Get the port number the server is listening on
129
+ */
130
+ getPort() {
131
+ return this.port;
132
+ }
133
+ /**
134
+ * Get the Fastify instance for registering routes
135
+ * Initializes Fastify if not already done
136
+ */
137
+ getFastifyInstance() {
138
+ this.initializeFastify();
139
+ return this.app;
140
+ }
141
+ /**
142
+ * Register a webhook handler for a specific token (LEGACY - deprecated)
143
+ * Supports two signatures:
144
+ * 1. For ndjson-client: (token, secret, handler)
145
+ * 2. For legacy direct registration: (token, handler) where handler takes (req, res)
146
+ *
147
+ * NOTE: New code should use LinearEventTransport which registers routes directly with Fastify
148
+ */
149
+ registerWebhookHandler(token, secretOrHandler, handler) {
150
+ if (typeof secretOrHandler === "string" && handler) {
151
+ // ndjson-client style registration
152
+ this.webhookHandlers.set(token, { secret: secretOrHandler, handler });
153
+ this.logger.debug(`Registered webhook handler (proxy-style) for token ending in ...${token.slice(-4)}`);
154
+ }
155
+ else if (typeof secretOrHandler === "function") {
156
+ // Legacy direct registration
157
+ this.linearWebhookHandlers.set(token, secretOrHandler);
158
+ this.logger.debug(`Registered webhook handler (legacy direct-style) for token ending in ...${token.slice(-4)}`);
159
+ }
160
+ else {
161
+ throw new Error("Invalid webhook handler registration parameters");
162
+ }
163
+ }
164
+ /**
165
+ * Unregister a webhook handler
166
+ */
167
+ unregisterWebhookHandler(token) {
168
+ const hadProxyHandler = this.webhookHandlers.delete(token);
169
+ const hadDirectHandler = this.linearWebhookHandlers.delete(token);
170
+ if (hadProxyHandler || hadDirectHandler) {
171
+ this.logger.debug(`Unregistered webhook handler for token ending in ...${token.slice(-4)}`);
172
+ }
173
+ }
174
+ /**
175
+ * Start OAuth flow and return promise that resolves when callback is received
176
+ */
177
+ async startOAuthFlow(proxyUrl) {
178
+ return new Promise((resolve, reject) => {
179
+ // Generate unique ID for this flow
180
+ const flowId = Date.now().toString();
181
+ // Store callback for this flow
182
+ this.oauthCallbacks.set(flowId, { resolve, reject, id: flowId });
183
+ // Check if we should use direct Linear OAuth (when self-hosting)
184
+ const isExternalHost = process.env.SYLAS_HOST_EXTERNAL?.toLowerCase().trim() === "true";
185
+ const useDirectOAuth = isExternalHost && process.env.LINEAR_CLIENT_ID;
186
+ const callbackBaseUrl = `http://${this.host}:${this.port}`;
187
+ let authUrl;
188
+ if (useDirectOAuth) {
189
+ // Use local OAuth authorize endpoint
190
+ authUrl = `${callbackBaseUrl}/oauth/authorize?callback=${encodeURIComponent(`${callbackBaseUrl}/callback`)}`;
191
+ this.logger.info(`Using direct OAuth mode (SYLAS_HOST_EXTERNAL=true)`);
192
+ }
193
+ else {
194
+ // Use proxy OAuth endpoint
195
+ authUrl = `${proxyUrl}/oauth/authorize?callback=${encodeURIComponent(`${callbackBaseUrl}/callback`)}`;
196
+ }
197
+ this.logger.info(`Opening your browser to authorize with Linear...`);
198
+ this.logger.info(`If the browser doesn't open, visit: ${authUrl}`);
199
+ // Timeout after 5 minutes
200
+ setTimeout(() => {
201
+ if (this.oauthCallbacks.has(flowId)) {
202
+ this.oauthCallbacks.delete(flowId);
203
+ reject(new Error("OAuth timeout"));
204
+ }
205
+ }, 5 * 60 * 1000);
206
+ });
207
+ }
208
+ /**
209
+ * Get the webhook URL
210
+ */
211
+ getWebhookUrl() {
212
+ return `http://${this.host}:${this.port}/webhook`;
213
+ }
214
+ /**
215
+ * Get the OAuth callback URL for registration with proxy
216
+ */
217
+ getOAuthCallbackUrl() {
218
+ return `http://${this.host}:${this.port}/callback`;
219
+ }
220
+ /**
221
+ * Register an approval request and get approval URL
222
+ */
223
+ registerApprovalRequest(sessionId) {
224
+ // Clean up expired approvals (older than 30 minutes)
225
+ const now = Date.now();
226
+ for (const [key, approval] of this.pendingApprovals) {
227
+ if (now - approval.createdAt > 30 * 60 * 1000) {
228
+ approval.reject(new Error("Approval request expired"));
229
+ this.pendingApprovals.delete(key);
230
+ }
231
+ }
232
+ // Create promise for this approval request
233
+ const promise = new Promise((resolve, reject) => {
234
+ this.pendingApprovals.set(sessionId, {
235
+ resolve: (approved, feedback) => resolve({ approved, feedback }),
236
+ reject,
237
+ sessionId,
238
+ createdAt: now,
239
+ });
240
+ });
241
+ // Generate approval URL
242
+ const url = `http://${this.host}:${this.port}/approval?session=${encodeURIComponent(sessionId)}`;
243
+ this.logger.debug(`Registered approval request for session ${sessionId}: ${url}`);
244
+ return { promise, url };
245
+ }
246
+ }
247
+ //# sourceMappingURL=SharedApplicationServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SharedApplicationServer.js","sourceRoot":"","sources":["../src/SharedApplicationServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import { type ILogger } from "sylas-core";
2
+ /**
3
+ * Shared webhook server that can handle multiple Linear tokens
4
+ * Each token has its own webhook secret for signature verification
5
+ */
6
+ export declare class SharedWebhookServer {
7
+ private server;
8
+ private webhookHandlers;
9
+ private port;
10
+ private host;
11
+ private isListening;
12
+ private logger;
13
+ constructor(port?: number, host?: string, logger?: ILogger);
14
+ /**
15
+ * Start the shared webhook server
16
+ */
17
+ start(): Promise<void>;
18
+ /**
19
+ * Stop the shared webhook server
20
+ */
21
+ stop(): Promise<void>;
22
+ /**
23
+ * Register a webhook handler for a specific token
24
+ */
25
+ registerWebhookHandler(token: string, secret: string, handler: (body: string, signature: string, timestamp?: string) => boolean): void;
26
+ /**
27
+ * Unregister a webhook handler
28
+ */
29
+ unregisterWebhookHandler(token: string): void;
30
+ /**
31
+ * Get the webhook URL for registration with proxy
32
+ */
33
+ getWebhookUrl(): string;
34
+ /**
35
+ * Handle incoming webhook requests
36
+ */
37
+ private handleWebhookRequest;
38
+ }
39
+ //# sourceMappingURL=SharedWebhookServer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SharedWebhookServer.d.ts","sourceRoot":"","sources":["../src/SharedWebhookServer.ts"],"names":[],"mappings":"AAKA,OAAO,EAAgB,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD;;;GAGG;AACH,qBAAa,mBAAmB;IAC/B,OAAO,CAAC,MAAM,CAAgD;IAC9D,OAAO,CAAC,eAAe,CAMnB;IACJ,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,IAAI,CAAS;IACrB,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,MAAM,CAAU;gBAGvB,IAAI,GAAE,MAAa,EACnB,IAAI,GAAE,MAAoB,EAC1B,MAAM,CAAC,EAAE,OAAO;IAOjB;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAyB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B;;OAEG;IACH,sBAAsB,CACrB,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,KAAK,OAAO,GACvE,IAAI;IAOP;;OAEG;IACH,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAO7C;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;YACW,oBAAoB;CAyFlC"}
@@ -0,0 +1,150 @@
1
+ import { createServer, } from "node:http";
2
+ import { createLogger } from "sylas-core";
3
+ /**
4
+ * Shared webhook server that can handle multiple Linear tokens
5
+ * Each token has its own webhook secret for signature verification
6
+ */
7
+ export class SharedWebhookServer {
8
+ server = null;
9
+ webhookHandlers = new Map();
10
+ port;
11
+ host;
12
+ isListening = false;
13
+ logger;
14
+ constructor(port = 3456, host = "localhost", logger) {
15
+ this.port = port;
16
+ this.host = host;
17
+ this.logger = logger ?? createLogger({ component: "SharedWebhookServer" });
18
+ }
19
+ /**
20
+ * Start the shared webhook server
21
+ */
22
+ async start() {
23
+ if (this.isListening) {
24
+ return; // Already listening
25
+ }
26
+ return new Promise((resolve, reject) => {
27
+ this.server = createServer((req, res) => {
28
+ this.handleWebhookRequest(req, res);
29
+ });
30
+ this.server.listen(this.port, this.host, () => {
31
+ this.isListening = true;
32
+ this.logger.info(`Shared webhook server listening on http://${this.host}:${this.port}`);
33
+ resolve();
34
+ });
35
+ this.server.on("error", (error) => {
36
+ this.isListening = false;
37
+ reject(error);
38
+ });
39
+ });
40
+ }
41
+ /**
42
+ * Stop the shared webhook server
43
+ */
44
+ async stop() {
45
+ if (this.server && this.isListening) {
46
+ return new Promise((resolve) => {
47
+ this.server.close(() => {
48
+ this.isListening = false;
49
+ this.logger.info("Shared webhook server stopped");
50
+ resolve();
51
+ });
52
+ });
53
+ }
54
+ }
55
+ /**
56
+ * Register a webhook handler for a specific token
57
+ */
58
+ registerWebhookHandler(token, secret, handler) {
59
+ this.webhookHandlers.set(token, { secret, handler });
60
+ this.logger.info(`Registered webhook handler for token ending in ...${token.slice(-4)}`);
61
+ }
62
+ /**
63
+ * Unregister a webhook handler
64
+ */
65
+ unregisterWebhookHandler(token) {
66
+ this.webhookHandlers.delete(token);
67
+ this.logger.info(`Unregistered webhook handler for token ending in ...${token.slice(-4)}`);
68
+ }
69
+ /**
70
+ * Get the webhook URL for registration with proxy
71
+ */
72
+ getWebhookUrl() {
73
+ return `http://${this.host}:${this.port}/webhook`;
74
+ }
75
+ /**
76
+ * Handle incoming webhook requests
77
+ */
78
+ async handleWebhookRequest(req, res) {
79
+ try {
80
+ this.logger.debug(`Incoming webhook request: ${req.method} ${req.url}`);
81
+ if (req.method !== "POST") {
82
+ this.logger.debug(`Rejected non-POST request: ${req.method}`);
83
+ res.writeHead(405, { "Content-Type": "text/plain" });
84
+ res.end("Method Not Allowed");
85
+ return;
86
+ }
87
+ if (req.url !== "/webhook") {
88
+ this.logger.debug(`Rejected request to wrong URL: ${req.url}`);
89
+ res.writeHead(404, { "Content-Type": "text/plain" });
90
+ res.end("Not Found");
91
+ return;
92
+ }
93
+ // Read request body
94
+ let body = "";
95
+ req.on("data", (chunk) => {
96
+ body += chunk.toString();
97
+ });
98
+ req.on("end", () => {
99
+ try {
100
+ const signature = req.headers["x-webhook-signature"];
101
+ const timestamp = req.headers["x-webhook-timestamp"];
102
+ this.logger.debug(`Webhook received with ${body.length} bytes, ${this.webhookHandlers.size} registered handlers`);
103
+ if (!signature) {
104
+ this.logger.debug("Webhook rejected: Missing signature header");
105
+ res.writeHead(400, { "Content-Type": "text/plain" });
106
+ res.end("Missing signature");
107
+ return;
108
+ }
109
+ // Try each registered handler until one verifies the signature
110
+ let handlerAttempts = 0;
111
+ for (const [token, { handler }] of this.webhookHandlers) {
112
+ handlerAttempts++;
113
+ try {
114
+ if (handler(body, signature, timestamp)) {
115
+ // Handler verified signature and processed webhook
116
+ res.writeHead(200, { "Content-Type": "text/plain" });
117
+ res.end("OK");
118
+ this.logger.debug(`Webhook delivered to token ending in ...${token.slice(-4)} (attempt ${handlerAttempts}/${this.webhookHandlers.size})`);
119
+ return;
120
+ }
121
+ }
122
+ catch (error) {
123
+ this.logger.error(`Error in webhook handler for token ...${token.slice(-4)}:`, error);
124
+ }
125
+ }
126
+ // No handler could verify the signature
127
+ this.logger.error(`Webhook signature verification failed for all ${this.webhookHandlers.size} registered handlers`);
128
+ res.writeHead(401, { "Content-Type": "text/plain" });
129
+ res.end("Unauthorized");
130
+ }
131
+ catch (error) {
132
+ this.logger.error("Error processing webhook:", error);
133
+ res.writeHead(400, { "Content-Type": "text/plain" });
134
+ res.end("Bad Request");
135
+ }
136
+ });
137
+ req.on("error", (error) => {
138
+ this.logger.error("Request error:", error);
139
+ res.writeHead(500, { "Content-Type": "text/plain" });
140
+ res.end("Internal Server Error");
141
+ });
142
+ }
143
+ catch (error) {
144
+ this.logger.error("Webhook request error:", error);
145
+ res.writeHead(500, { "Content-Type": "text/plain" });
146
+ res.end("Internal Server Error");
147
+ }
148
+ }
149
+ }
150
+ //# sourceMappingURL=SharedWebhookServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SharedWebhookServer.js","sourceRoot":"","sources":["../src/SharedWebhookServer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { IAgentRunner, ILogger } from "sylas-core";
2
+ import { type SlackWebhookEvent } from "sylas-slack-event-transport";
3
+ import type { ChatPlatformAdapter } from "./ChatSessionHandler.js";
4
+ /**
5
+ * Slack implementation of ChatPlatformAdapter.
6
+ *
7
+ * Contains all Slack-specific logic extracted from EdgeWorker:
8
+ * text extraction, thread keys, system prompts, thread context,
9
+ * reply posting, and acknowledgement reactions.
10
+ */
11
+ export declare class SlackChatAdapter implements ChatPlatformAdapter<SlackWebhookEvent> {
12
+ readonly platformName: "slack";
13
+ private logger;
14
+ constructor(logger?: ILogger);
15
+ extractTaskInstructions(event: SlackWebhookEvent): string;
16
+ getThreadKey(event: SlackWebhookEvent): string;
17
+ getEventId(event: SlackWebhookEvent): string;
18
+ buildSystemPrompt(event: SlackWebhookEvent): string;
19
+ fetchThreadContext(event: SlackWebhookEvent): Promise<string>;
20
+ postReply(event: SlackWebhookEvent, runner: IAgentRunner): Promise<void>;
21
+ acknowledgeReceipt(event: SlackWebhookEvent): Promise<void>;
22
+ notifyBusy(event: SlackWebhookEvent): Promise<void>;
23
+ private formatThreadContext;
24
+ }
25
+ //# sourceMappingURL=SlackChatAdapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SlackChatAdapter.d.ts","sourceRoot":"","sources":["../src/SlackChatAdapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAIN,KAAK,iBAAiB,EAEtB,MAAM,6BAA6B,CAAC;AACrC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAEnE;;;;;;GAMG;AACH,qBAAa,gBACZ,YAAW,mBAAmB,CAAC,iBAAiB,CAAC;IAEjD,QAAQ,CAAC,YAAY,EAAG,OAAO,CAAU;IACzC,OAAO,CAAC,MAAM,CAAU;gBAEZ,MAAM,CAAC,EAAE,OAAO;IAI5B,uBAAuB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM;IAMzD,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM;IAK9C,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM;IAI5C,iBAAiB,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM;IAe7C,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IA2C7D,SAAS,CACd,KAAK,EAAE,iBAAiB,EACxB,MAAM,EAAE,YAAY,GAClB,OAAO,CAAC,IAAI,CAAC;IAqDV,kBAAkB,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAgB3D,UAAU,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAezD,OAAO,CAAC,mBAAmB;CAgB3B"}
@@ -0,0 +1,143 @@
1
+ import { createLogger } from "sylas-core";
2
+ import { SlackMessageService, SlackReactionService, stripMention as stripSlackMention, } from "sylas-slack-event-transport";
3
+ /**
4
+ * Slack implementation of ChatPlatformAdapter.
5
+ *
6
+ * Contains all Slack-specific logic extracted from EdgeWorker:
7
+ * text extraction, thread keys, system prompts, thread context,
8
+ * reply posting, and acknowledgement reactions.
9
+ */
10
+ export class SlackChatAdapter {
11
+ platformName = "slack";
12
+ logger;
13
+ constructor(logger) {
14
+ this.logger = logger ?? createLogger({ component: "SlackChatAdapter" });
15
+ }
16
+ extractTaskInstructions(event) {
17
+ return (stripSlackMention(event.payload.text) || "Ask the user for more context");
18
+ }
19
+ getThreadKey(event) {
20
+ const threadTs = event.payload.thread_ts || event.payload.ts;
21
+ return `${event.payload.channel}:${threadTs}`;
22
+ }
23
+ getEventId(event) {
24
+ return event.eventId;
25
+ }
26
+ buildSystemPrompt(event) {
27
+ return `You are responding to a Slack @mention.
28
+
29
+ ## Context
30
+ - **Requested by**: ${event.payload.user}
31
+ - **Channel**: ${event.payload.channel}
32
+
33
+ ## Instructions
34
+ - You are running in a transient workspace, not associated with any code repository
35
+ - Be concise in your responses as they will be posted back to Slack
36
+ - If the user's request involves code changes, help them plan the work and suggest creating an issue in their project tracker (Linear, Jira, or GitHub Issues)
37
+ - You can answer questions, provide analysis, help with planning, and assist with research
38
+ - If files need to be created or examined, they will be in your working directory`;
39
+ }
40
+ async fetchThreadContext(event) {
41
+ // Only fetch context for threaded messages
42
+ if (!event.payload.thread_ts) {
43
+ return "";
44
+ }
45
+ if (!event.slackBotToken) {
46
+ this.logger.warn("Cannot fetch Slack thread context: no slackBotToken available");
47
+ return "";
48
+ }
49
+ try {
50
+ const slackService = new SlackMessageService();
51
+ const messages = await slackService.fetchThreadMessages({
52
+ token: event.slackBotToken,
53
+ channel: event.payload.channel,
54
+ thread_ts: event.payload.thread_ts,
55
+ limit: 50,
56
+ });
57
+ // Filter out the @mention message itself and bot messages
58
+ const contextMessages = messages.filter((msg) => msg.ts !== event.payload.ts &&
59
+ !msg.bot_id &&
60
+ msg.subtype !== "bot_message");
61
+ if (contextMessages.length === 0) {
62
+ return "";
63
+ }
64
+ return this.formatThreadContext(contextMessages);
65
+ }
66
+ catch (error) {
67
+ this.logger.warn(`Failed to fetch Slack thread context: ${error instanceof Error ? error.message : String(error)}`);
68
+ return "";
69
+ }
70
+ }
71
+ async postReply(event, runner) {
72
+ try {
73
+ // Get the last assistant message from the runner as the summary
74
+ const messages = runner.getMessages();
75
+ const lastAssistantMessage = [...messages]
76
+ .reverse()
77
+ .find((m) => m.type === "assistant");
78
+ let summary = "Task completed.";
79
+ if (lastAssistantMessage &&
80
+ lastAssistantMessage.type === "assistant" &&
81
+ "message" in lastAssistantMessage) {
82
+ const msg = lastAssistantMessage;
83
+ const textBlock = msg.message.content?.find((block) => block.type === "text" && block.text);
84
+ if (textBlock?.text) {
85
+ summary = textBlock.text;
86
+ }
87
+ }
88
+ if (!event.slackBotToken) {
89
+ this.logger.warn("Cannot post Slack reply: no slackBotToken available");
90
+ return;
91
+ }
92
+ // Thread the reply under the original message
93
+ const threadTs = event.payload.thread_ts || event.payload.ts;
94
+ await new SlackMessageService().postMessage({
95
+ token: event.slackBotToken,
96
+ channel: event.payload.channel,
97
+ text: summary,
98
+ thread_ts: threadTs,
99
+ });
100
+ this.logger.info(`Posted Slack reply to channel ${event.payload.channel} (thread ${threadTs})`);
101
+ }
102
+ catch (error) {
103
+ this.logger.error("Failed to post Slack reply", error instanceof Error ? error : new Error(String(error)));
104
+ }
105
+ }
106
+ async acknowledgeReceipt(event) {
107
+ if (!event.slackBotToken) {
108
+ this.logger.warn("Cannot add Slack reaction: no slackBotToken available (SLACK_BOT_TOKEN env var not set)");
109
+ return;
110
+ }
111
+ await new SlackReactionService().addReaction({
112
+ token: event.slackBotToken,
113
+ channel: event.payload.channel,
114
+ timestamp: event.payload.ts,
115
+ name: "eyes",
116
+ });
117
+ }
118
+ async notifyBusy(event) {
119
+ if (!event.slackBotToken) {
120
+ return;
121
+ }
122
+ const threadTs = event.payload.thread_ts || event.payload.ts;
123
+ await new SlackMessageService().postMessage({
124
+ token: event.slackBotToken,
125
+ channel: event.payload.channel,
126
+ text: "I'm still working on the previous request in this thread. I'll pick up your new message once I'm done.",
127
+ thread_ts: threadTs,
128
+ });
129
+ }
130
+ formatThreadContext(messages) {
131
+ const formattedMessages = messages
132
+ .map((msg) => ` <message>
133
+ <author>${msg.user ?? "unknown"}</author>
134
+ <timestamp>${msg.ts}</timestamp>
135
+ <content>
136
+ ${msg.text}
137
+ </content>
138
+ </message>`)
139
+ .join("\n");
140
+ return `<slack_thread_context>\n${formattedMessages}\n</slack_thread_context>`;
141
+ }
142
+ }
143
+ //# sourceMappingURL=SlackChatAdapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SlackChatAdapter.js","sourceRoot":"","sources":["../src/SlackChatAdapter.ts"],"names":[],"mappings":"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"}