@noodleseed/one 0.96.3 → 0.98.0

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 (173) hide show
  1. package/README.md +2 -1
  2. package/dist/commands/author-loop.d.ts.map +1 -1
  3. package/dist/commands/author-loop.js +7 -1
  4. package/dist/commands/author-loop.js.map +1 -1
  5. package/dist/commands/catalog-data-local-authoring.d.ts.map +1 -1
  6. package/dist/commands/catalog-data-local-authoring.js +2 -2
  7. package/dist/commands/catalog-data-local-authoring.js.map +1 -1
  8. package/dist/deploy.d.ts +5 -0
  9. package/dist/deploy.d.ts.map +1 -1
  10. package/dist/deploy.js +1 -1
  11. package/dist/deploy.js.map +1 -1
  12. package/dist/dev.d.ts +13 -0
  13. package/dist/dev.d.ts.map +1 -1
  14. package/dist/dev.js +116 -7
  15. package/dist/dev.js.map +1 -1
  16. package/dist/devtools-auth-routes.d.ts +4 -0
  17. package/dist/devtools-auth-routes.d.ts.map +1 -0
  18. package/dist/devtools-auth-routes.js +205 -0
  19. package/dist/devtools-auth-routes.js.map +1 -0
  20. package/dist/devtools-auth-session-helpers.d.ts +6 -0
  21. package/dist/devtools-auth-session-helpers.d.ts.map +1 -0
  22. package/dist/devtools-auth-session-helpers.js +19 -0
  23. package/dist/devtools-auth-session-helpers.js.map +1 -0
  24. package/dist/devtools-auth-session.d.ts +35 -0
  25. package/dist/devtools-auth-session.d.ts.map +1 -0
  26. package/dist/devtools-auth-session.js +433 -0
  27. package/dist/devtools-auth-session.js.map +1 -0
  28. package/dist/devtools-auth-types.d.ts +51 -0
  29. package/dist/devtools-auth-types.d.ts.map +1 -0
  30. package/dist/devtools-auth-types.js +16 -0
  31. package/dist/devtools-auth-types.js.map +1 -0
  32. package/dist/devtools-auth-ui.d.ts +6 -0
  33. package/dist/devtools-auth-ui.d.ts.map +1 -0
  34. package/dist/devtools-auth-ui.js +127 -0
  35. package/dist/devtools-auth-ui.js.map +1 -0
  36. package/dist/devtools-delegated-credential.d.ts +10 -0
  37. package/dist/devtools-delegated-credential.d.ts.map +1 -0
  38. package/dist/devtools-delegated-credential.js +32 -0
  39. package/dist/devtools-delegated-credential.js.map +1 -0
  40. package/dist/devtools-firebase-auth.d.ts +55 -0
  41. package/dist/devtools-firebase-auth.d.ts.map +1 -0
  42. package/dist/devtools-firebase-auth.js +237 -0
  43. package/dist/devtools-firebase-auth.js.map +1 -0
  44. package/dist/devtools-harness.d.ts +5 -0
  45. package/dist/devtools-harness.d.ts.map +1 -1
  46. package/dist/devtools-harness.js +60 -21
  47. package/dist/devtools-harness.js.map +1 -1
  48. package/dist/devtools-microsoft-auth.d.ts +42 -0
  49. package/dist/devtools-microsoft-auth.d.ts.map +1 -0
  50. package/dist/devtools-microsoft-auth.js +362 -0
  51. package/dist/devtools-microsoft-auth.js.map +1 -0
  52. package/dist/devtools-microsoft-errors.d.ts +7 -0
  53. package/dist/devtools-microsoft-errors.d.ts.map +1 -0
  54. package/dist/devtools-microsoft-errors.js +60 -0
  55. package/dist/devtools-microsoft-errors.js.map +1 -0
  56. package/dist/devtools-preview.d.ts +15 -13
  57. package/dist/devtools-preview.d.ts.map +1 -1
  58. package/dist/devtools-preview.js +101 -179
  59. package/dist/devtools-preview.js.map +1 -1
  60. package/dist/devtools-rpc-forwarder.d.ts +37 -0
  61. package/dist/devtools-rpc-forwarder.d.ts.map +1 -0
  62. package/dist/devtools-rpc-forwarder.js +251 -0
  63. package/dist/devtools-rpc-forwarder.js.map +1 -0
  64. package/dist/devtools-tool-safety.d.ts +20 -0
  65. package/dist/devtools-tool-safety.d.ts.map +1 -0
  66. package/dist/devtools-tool-safety.js +43 -0
  67. package/dist/devtools-tool-safety.js.map +1 -0
  68. package/dist/preview-session.d.ts.map +1 -1
  69. package/dist/preview-session.js +5 -0
  70. package/dist/preview-session.js.map +1 -1
  71. package/node_modules/@noodle-borg/agent-kit/package.json +1 -1
  72. package/node_modules/@noodle-borg/auth/dist/index.d.ts +1 -0
  73. package/node_modules/@noodle-borg/auth/dist/index.d.ts.map +1 -1
  74. package/node_modules/@noodle-borg/auth/dist/index.js +1 -0
  75. package/node_modules/@noodle-borg/auth/dist/index.js.map +1 -1
  76. package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.d.ts +56 -0
  77. package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.d.ts.map +1 -0
  78. package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.js +367 -0
  79. package/node_modules/@noodle-borg/auth/dist/mcp-oauth-client.js.map +1 -0
  80. package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.d.ts +13 -0
  81. package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.d.ts.map +1 -0
  82. package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.js +71 -0
  83. package/node_modules/@noodle-borg/service/dist/customer-verifier-boundaries.js.map +1 -0
  84. package/node_modules/@noodle-borg/service/dist/customer-verifier.d.ts +1 -1
  85. package/node_modules/@noodle-borg/service/dist/customer-verifier.d.ts.map +1 -1
  86. package/node_modules/@noodle-borg/service/dist/customer-verifier.js +43 -31
  87. package/node_modules/@noodle-borg/service/dist/customer-verifier.js.map +1 -1
  88. package/node_modules/@noodle-borg/service/dist/feedback-operations/bootstrap.d.ts +7 -0
  89. package/node_modules/@noodle-borg/service/dist/feedback-operations/bootstrap.d.ts.map +1 -0
  90. package/node_modules/@noodle-borg/service/dist/feedback-operations/bootstrap.js +23 -0
  91. package/node_modules/@noodle-borg/service/dist/feedback-operations/bootstrap.js.map +1 -0
  92. package/node_modules/@noodle-borg/service/dist/feedback-operations/config.d.ts +12 -0
  93. package/node_modules/@noodle-borg/service/dist/feedback-operations/config.d.ts.map +1 -0
  94. package/node_modules/@noodle-borg/service/dist/feedback-operations/config.js +47 -0
  95. package/node_modules/@noodle-borg/service/dist/feedback-operations/config.js.map +1 -0
  96. package/node_modules/@noodle-borg/service/dist/feedback-operations/contracts.d.ts +24 -0
  97. package/node_modules/@noodle-borg/service/dist/feedback-operations/contracts.d.ts.map +1 -0
  98. package/node_modules/@noodle-borg/service/dist/feedback-operations/contracts.js +2 -0
  99. package/node_modules/@noodle-borg/service/dist/feedback-operations/contracts.js.map +1 -0
  100. package/node_modules/@noodle-borg/service/dist/feedback-operations/delivery-worker.d.ts +29 -0
  101. package/node_modules/@noodle-borg/service/dist/feedback-operations/delivery-worker.d.ts.map +1 -0
  102. package/node_modules/@noodle-borg/service/dist/feedback-operations/delivery-worker.js +256 -0
  103. package/node_modules/@noodle-borg/service/dist/feedback-operations/delivery-worker.js.map +1 -0
  104. package/node_modules/@noodle-borg/service/dist/feedback-operations/linear-client.d.ts +39 -0
  105. package/node_modules/@noodle-borg/service/dist/feedback-operations/linear-client.d.ts.map +1 -0
  106. package/node_modules/@noodle-borg/service/dist/feedback-operations/linear-client.js +142 -0
  107. package/node_modules/@noodle-borg/service/dist/feedback-operations/linear-client.js.map +1 -0
  108. package/node_modules/@noodle-borg/service/dist/feedback-operations/reporter-email.d.ts +33 -0
  109. package/node_modules/@noodle-borg/service/dist/feedback-operations/reporter-email.d.ts.map +1 -0
  110. package/node_modules/@noodle-borg/service/dist/feedback-operations/reporter-email.js +142 -0
  111. package/node_modules/@noodle-borg/service/dist/feedback-operations/reporter-email.js.map +1 -0
  112. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-card.d.ts +138 -0
  113. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-card.d.ts.map +1 -0
  114. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-card.js +199 -0
  115. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-card.js.map +1 -0
  116. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-client.d.ts +44 -0
  117. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-client.d.ts.map +1 -0
  118. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-client.js +95 -0
  119. package/node_modules/@noodle-borg/service/dist/feedback-operations/slack-client.js.map +1 -0
  120. package/node_modules/@noodle-borg/service/dist/index.d.ts +3 -1
  121. package/node_modules/@noodle-borg/service/dist/index.d.ts.map +1 -1
  122. package/node_modules/@noodle-borg/service/dist/index.js +1 -0
  123. package/node_modules/@noodle-borg/service/dist/index.js.map +1 -1
  124. package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.d.ts +20 -0
  125. package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.d.ts.map +1 -0
  126. package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.js +107 -0
  127. package/node_modules/@noodle-borg/service/dist/local-devtools-delegated-credentials.js.map +1 -0
  128. package/node_modules/@noodle-borg/service/dist/main.js +38 -3
  129. package/node_modules/@noodle-borg/service/dist/main.js.map +1 -1
  130. package/node_modules/@noodle-borg/service/dist/oauth/microsoft.d.ts +5 -0
  131. package/node_modules/@noodle-borg/service/dist/oauth/microsoft.d.ts.map +1 -1
  132. package/node_modules/@noodle-borg/service/dist/oauth/microsoft.js +48 -28
  133. package/node_modules/@noodle-borg/service/dist/oauth/microsoft.js.map +1 -1
  134. package/node_modules/@noodle-borg/service/dist/options.d.ts +14 -0
  135. package/node_modules/@noodle-borg/service/dist/options.d.ts.map +1 -1
  136. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.d.ts +9 -0
  137. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.d.ts.map +1 -0
  138. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.js +53 -0
  139. package/node_modules/@noodle-borg/service/dist/routes/feedback-operations-dispatch.js.map +1 -0
  140. package/node_modules/@noodle-borg/service/dist/routes/feedback.d.ts +2 -0
  141. package/node_modules/@noodle-borg/service/dist/routes/feedback.d.ts.map +1 -1
  142. package/node_modules/@noodle-borg/service/dist/routes/feedback.js +22 -0
  143. package/node_modules/@noodle-borg/service/dist/routes/feedback.js.map +1 -1
  144. package/node_modules/@noodle-borg/service/dist/routes/linear-feedback-webhook.d.ts +17 -0
  145. package/node_modules/@noodle-borg/service/dist/routes/linear-feedback-webhook.d.ts.map +1 -0
  146. package/node_modules/@noodle-borg/service/dist/routes/linear-feedback-webhook.js +141 -0
  147. package/node_modules/@noodle-borg/service/dist/routes/linear-feedback-webhook.js.map +1 -0
  148. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.d.ts +13 -0
  149. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.d.ts.map +1 -0
  150. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.js +99 -0
  151. package/node_modules/@noodle-borg/service/dist/routes/slack-feedback-actions.js.map +1 -0
  152. package/node_modules/@noodle-borg/service/dist/serve-options.d.ts +13 -0
  153. package/node_modules/@noodle-borg/service/dist/serve-options.d.ts.map +1 -1
  154. package/node_modules/@noodle-borg/service/dist/serve.d.ts +3 -0
  155. package/node_modules/@noodle-borg/service/dist/serve.d.ts.map +1 -1
  156. package/node_modules/@noodle-borg/service/dist/serve.js +72 -18
  157. package/node_modules/@noodle-borg/service/dist/serve.js.map +1 -1
  158. package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.d.ts +1 -0
  159. package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.d.ts.map +1 -1
  160. package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.js +2 -0
  161. package/node_modules/@noodle-borg/service/dist/service-resource-cleanup.js.map +1 -1
  162. package/node_modules/@noodle-borg/service/dist/service.d.ts.map +1 -1
  163. package/node_modules/@noodle-borg/service/dist/service.js +6 -0
  164. package/node_modules/@noodle-borg/service/dist/service.js.map +1 -1
  165. package/node_modules/@noodle-borg/service/dist/store/feedback-operations.d.ts +186 -0
  166. package/node_modules/@noodle-borg/service/dist/store/feedback-operations.d.ts.map +1 -0
  167. package/node_modules/@noodle-borg/service/dist/store/feedback-operations.js +483 -0
  168. package/node_modules/@noodle-borg/service/dist/store/feedback-operations.js.map +1 -0
  169. package/node_modules/@noodle-borg/service/dist/store/postgres-feedback-operations.d.ts +35 -0
  170. package/node_modules/@noodle-borg/service/dist/store/postgres-feedback-operations.d.ts.map +1 -0
  171. package/node_modules/@noodle-borg/service/dist/store/postgres-feedback-operations.js +172 -0
  172. package/node_modules/@noodle-borg/service/dist/store/postgres-feedback-operations.js.map +1 -0
  173. package/package.json +1 -1
@@ -0,0 +1,141 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import { readRawBody, sendJson } from '../http-util.js';
4
+ const MAX_WEBHOOK_AGE_MS = 60_000;
5
+ const LinearWebhookSchema = z
6
+ .object({
7
+ action: z.string().min(1).max(50),
8
+ type: z.enum(['Issue', 'Attachment']),
9
+ webhookTimestamp: z.number().int().positive(),
10
+ data: z
11
+ .object({
12
+ id: z.string().min(1).optional(),
13
+ issueId: z.string().min(1).optional(),
14
+ state: z.object({ id: z.string().min(1) }).optional(),
15
+ })
16
+ .catchall(z.unknown()),
17
+ updatedFrom: z.object({ stateId: z.string().optional() }).catchall(z.unknown()).optional(),
18
+ })
19
+ .catchall(z.unknown());
20
+ export async function handleLinearFeedbackWebhook(req, res, deps) {
21
+ const body = await readRawBody(req, deps.maxBody);
22
+ if (!body.ok) {
23
+ sendJson(res, 413, { error: 'request body too large' });
24
+ return;
25
+ }
26
+ const signature = singleHeader(req.headers['linear-signature']);
27
+ if (!verifyLinearSignature(body.buffer, signature, deps.webhookSecret)) {
28
+ sendJson(res, 401, { error: 'invalid webhook' });
29
+ return;
30
+ }
31
+ let parsedJson;
32
+ try {
33
+ parsedJson = JSON.parse(body.buffer.toString('utf8'));
34
+ }
35
+ catch {
36
+ sendJson(res, 400, { error: 'malformed payload' });
37
+ return;
38
+ }
39
+ const envelope = LinearWebhookSchema.safeParse(parsedJson);
40
+ if (!envelope.success) {
41
+ sendJson(res, 400, { error: 'malformed payload' });
42
+ return;
43
+ }
44
+ if (Math.abs(deps.clock().getTime() - envelope.data.webhookTimestamp) > MAX_WEBHOOK_AGE_MS) {
45
+ sendJson(res, 401, { error: 'invalid webhook' });
46
+ return;
47
+ }
48
+ const deliveryId = singleHeader(req.headers['linear-delivery']);
49
+ if (deliveryId === undefined || deliveryId.length > 200) {
50
+ sendJson(res, 400, { error: 'missing delivery id' });
51
+ return;
52
+ }
53
+ const issueId = envelope.data.type === 'Issue' ? envelope.data.data.id : envelope.data.data.issueId;
54
+ if (issueId === undefined) {
55
+ sendJson(res, 400, { error: 'malformed payload' });
56
+ return;
57
+ }
58
+ const issue = await deps.client.getIssue(issueId);
59
+ if (issue === undefined ||
60
+ issue.teamId !== deps.teamId ||
61
+ !issue.labels.some((label) => label.toLowerCase() === 'feedback')) {
62
+ sendJson(res, 200, { ok: true, data: { outcome: 'ignored' } });
63
+ return;
64
+ }
65
+ const githubIssue = githubIssueReferenceFromAttachments(issue.attachments, deps.githubRepository);
66
+ const enteredInProgress = envelope.data.type === 'Issue' &&
67
+ envelope.data.action === 'update' &&
68
+ envelope.data.data.state?.id === deps.inProgressStatusId &&
69
+ envelope.data.updatedFrom?.stateId !== undefined &&
70
+ envelope.data.updatedFrom?.stateId !== deps.inProgressStatusId;
71
+ const result = await deps.store.applyLinearEvent({
72
+ deliveryId,
73
+ eventCreatedAt: new Date(envelope.data.webhookTimestamp).toISOString(),
74
+ enteredInProgress,
75
+ ...(githubIssue !== undefined ? { githubIssueNumber: githubIssue.number } : {}),
76
+ issue: {
77
+ id: issue.id,
78
+ identifier: issue.identifier,
79
+ url: issue.url,
80
+ title: issue.title,
81
+ description: issue.description,
82
+ status: issue.status,
83
+ ...(issue.assigneeName !== undefined ? { assigneeName: issue.assigneeName } : {}),
84
+ labels: issue.labels,
85
+ },
86
+ });
87
+ if (result.bindingConflict) {
88
+ sendJson(res, 200, { ok: true, data: { outcome: 'binding_conflict' } });
89
+ return;
90
+ }
91
+ sendJson(res, 200, {
92
+ ok: true,
93
+ data: {
94
+ outcome: result.duplicate ? 'duplicate' : 'accepted',
95
+ slackEnqueued: result.slackEnqueued,
96
+ emailEnqueued: result.emailEnqueued,
97
+ },
98
+ });
99
+ }
100
+ export function verifyLinearSignature(body, signature, secret) {
101
+ if (signature === undefined || !/^[0-9a-f]{64}$/i.test(signature))
102
+ return false;
103
+ const expected = createHmac('sha256', secret).update(body).digest();
104
+ const received = Buffer.from(signature, 'hex');
105
+ return received.length === expected.length && timingSafeEqual(received, expected);
106
+ }
107
+ function githubIssueReferenceFromAttachments(attachments, repository) {
108
+ for (const value of attachments) {
109
+ let url;
110
+ try {
111
+ url = new URL(value);
112
+ }
113
+ catch {
114
+ continue;
115
+ }
116
+ const segments = url.pathname.split('/').filter(Boolean);
117
+ if (url.protocol !== 'https:' ||
118
+ url.hostname.toLowerCase() !== 'github.com' ||
119
+ url.search !== '' ||
120
+ url.hash !== '' ||
121
+ segments.length !== 4 ||
122
+ segments[0]?.toLowerCase() !== repository.owner.toLowerCase() ||
123
+ segments[1]?.toLowerCase() !== repository.repo.toLowerCase() ||
124
+ segments[2]?.toLowerCase() !== 'issues' ||
125
+ !/^[1-9]\d*$/.test(segments[3] ?? '')) {
126
+ continue;
127
+ }
128
+ const issueNumber = Number(segments[3]);
129
+ if (Number.isSafeInteger(issueNumber)) {
130
+ return {
131
+ number: issueNumber,
132
+ url: `https://github.com/${repository.owner}/${repository.repo}/issues/${issueNumber}`,
133
+ };
134
+ }
135
+ }
136
+ return undefined;
137
+ }
138
+ function singleHeader(value) {
139
+ return typeof value === 'string' ? value : undefined;
140
+ }
141
+ //# sourceMappingURL=linear-feedback-webhook.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear-feedback-webhook.js","sourceRoot":"","sources":["../../src/routes/linear-feedback-webhook.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import type { FeedbackOperationsStore } from '../store/feedback-operations.js';
3
+ export interface SlackFeedbackActionsDeps {
4
+ readonly store: FeedbackOperationsStore;
5
+ readonly signingSecret: string;
6
+ readonly workspaceId: string;
7
+ readonly channelId: string;
8
+ readonly maxBody: number;
9
+ readonly clock: () => Date;
10
+ }
11
+ export declare function handleSlackFeedbackActions(req: IncomingMessage, res: ServerResponse, deps: SlackFeedbackActionsDeps): Promise<void>;
12
+ export declare function verifySlackSignature(raw: Buffer, timestamp: string, signature: string | undefined, secret: string): boolean;
13
+ //# sourceMappingURL=slack-feedback-actions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-feedback-actions.d.ts","sourceRoot":"","sources":["../../src/routes/slack-feedback-actions.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAIjE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAoB/E,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,KAAK,EAAE,uBAAuB,CAAC;IACxC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,KAAK,EAAE,MAAM,IAAI,CAAC;CAC5B;AAED,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,wBAAwB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA+Df;AAED,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,GACb,OAAO,CAKT"}
@@ -0,0 +1,99 @@
1
+ import { createHmac, timingSafeEqual } from 'node:crypto';
2
+ import { z } from 'zod';
3
+ import { assertSlackResponseUrl } from '../feedback-operations/slack-client.js';
4
+ import { readRawBody, sendJson } from '../http-util.js';
5
+ const MAX_REQUEST_AGE_SECONDS = 5 * 60;
6
+ const SlackActionSchema = z.object({
7
+ type: z.literal('block_actions'),
8
+ team: z.object({ id: z.string().min(1) }),
9
+ channel: z.object({ id: z.string().min(1) }),
10
+ message: z.object({ ts: z.string().min(1) }),
11
+ user: z.object({ id: z.string().min(1) }),
12
+ response_url: z.url(),
13
+ actions: z
14
+ .array(z.object({
15
+ action_id: z.literal('feedback_start_work'),
16
+ value: z.string().min(1),
17
+ }))
18
+ .length(1),
19
+ });
20
+ export async function handleSlackFeedbackActions(req, res, deps) {
21
+ const raw = await readRawBody(req, deps.maxBody);
22
+ if (!raw.ok) {
23
+ sendJson(res, 413, { error: 'request body too large' });
24
+ return;
25
+ }
26
+ const timestamp = singleHeader(req.headers['x-slack-request-timestamp']);
27
+ const signature = singleHeader(req.headers['x-slack-signature']);
28
+ if (timestamp === undefined ||
29
+ !verifySlackSignature(raw.buffer, timestamp, signature, deps.signingSecret) ||
30
+ !freshTimestamp(timestamp, deps.clock())) {
31
+ sendJson(res, 401, { error: 'invalid request' });
32
+ return;
33
+ }
34
+ const form = new URLSearchParams(raw.buffer.toString('utf8'));
35
+ const payloadValue = form.get('payload');
36
+ let payloadJson;
37
+ try {
38
+ payloadJson = payloadValue === null ? undefined : JSON.parse(payloadValue);
39
+ }
40
+ catch {
41
+ payloadJson = undefined;
42
+ }
43
+ const parsed = SlackActionSchema.safeParse(payloadJson);
44
+ if (!parsed.success) {
45
+ sendJson(res, 400, { error: 'malformed action' });
46
+ return;
47
+ }
48
+ const action = parsed.data;
49
+ if (action.team.id !== deps.workspaceId || action.channel.id !== deps.channelId) {
50
+ sendJson(res, 403, { error: 'action not allowed' });
51
+ return;
52
+ }
53
+ const record = await deps.store.getBySlackMessage(action.channel.id, action.message.ts);
54
+ const linearIssueId = action.actions[0]?.value;
55
+ if (record === undefined ||
56
+ linearIssueId === undefined ||
57
+ record.linearIssueId !== linearIssueId ||
58
+ !record.trustedReporter ||
59
+ record.reporterEmail === undefined ||
60
+ record.acceptedAt !== undefined ||
61
+ record.emailSentAt !== undefined ||
62
+ (record.linearStatus?.type !== 'backlog' && record.linearStatus?.type !== 'unstarted')) {
63
+ sendJson(res, 403, { error: 'action not allowed' });
64
+ return;
65
+ }
66
+ try {
67
+ assertSlackResponseUrl(action.response_url);
68
+ }
69
+ catch {
70
+ sendJson(res, 403, { error: 'action not allowed' });
71
+ return;
72
+ }
73
+ await deps.store.enqueueStartWork({
74
+ actionKey: `${action.team.id}/${action.channel.id}/${action.message.ts}/${linearIssueId}/in-progress`,
75
+ linearIssueId,
76
+ slackUserId: action.user.id,
77
+ responseUrl: action.response_url,
78
+ createdAt: deps.clock().toISOString(),
79
+ });
80
+ sendJson(res, 200, { response_type: 'ephemeral', text: 'Starting work in Linear…' });
81
+ }
82
+ export function verifySlackSignature(raw, timestamp, signature, secret) {
83
+ if (signature === undefined || !/^v0=[0-9a-f]{64}$/i.test(signature))
84
+ return false;
85
+ const expected = createHmac('sha256', secret).update(`v0:${timestamp}:`).update(raw).digest();
86
+ const received = Buffer.from(signature.slice(3), 'hex');
87
+ return received.length === expected.length && timingSafeEqual(received, expected);
88
+ }
89
+ function freshTimestamp(value, now) {
90
+ if (!/^\d{10,13}$/.test(value))
91
+ return false;
92
+ const seconds = Number(value);
93
+ return (Number.isSafeInteger(seconds) &&
94
+ Math.abs(Math.floor(now.getTime() / 1000) - seconds) <= MAX_REQUEST_AGE_SECONDS);
95
+ }
96
+ function singleHeader(value) {
97
+ return typeof value === 'string' ? value : undefined;
98
+ }
99
+ //# sourceMappingURL=slack-feedback-actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slack-feedback-actions.js","sourceRoot":"","sources":["../../src/routes/slack-feedback-actions.ts"],"names":[],"mappings":"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"}
@@ -8,9 +8,22 @@ import type { GoogleAuthenticator } from './oauth/google.js';
8
8
  import type { WorkOSPlatformAuthenticationMethod } from './oauth/workos.js';
9
9
  import type { ServiceOptions } from './options.js';
10
10
  import type { CloudSqlConfig } from './store/cloudsql-pool.js';
11
+ import type { TenantBridgeAuthConfig } from './store.js';
11
12
  export type ServeServiceOptions = ServiceOptions & {
12
13
  readonly port?: number;
13
14
  readonly host?: string;
15
+ /**
16
+ * Loopback-only `noodle dev` seam: verify Firebase ID tokens directly instead of requiring a hosted
17
+ * Noodle bridge token. The service rejects this option on every non-loopback bind.
18
+ */
19
+ readonly localDevtoolsDirectFirebaseAuth?: boolean;
20
+ /**
21
+ * Loopback-only `noodle dev` seam: verify Microsoft ID tokens directly instead of requiring a hosted
22
+ * Noodle bridge token. The service rejects this option on every non-loopback bind.
23
+ */
24
+ readonly localDevtoolsDirectMicrosoftAuth?: boolean;
25
+ /** Resolve managed bridge variables for the single local app before direct token verification. */
26
+ readonly localDevtoolsResolveBridgeAuth?: (auth: TenantBridgeAuthConfig, resource: string) => Promise<TenantBridgeAuthConfig>;
14
27
  /** Google OAuth client id / OIDC audience for control-plane ID-token verification (MT-2). */
15
28
  readonly googleClientId?: string;
16
29
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"serve-options.d.ts","sourceRoot":"","sources":["../src/serve-options.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"serve-options.d.ts","sourceRoot":"","sources":["../src/serve-options.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { type Server } from 'node:http';
2
+ import { type LocalDevtoolsDelegatedCredentialSink } from './local-devtools-delegated-credentials.js';
2
3
  import { type RecoverResult, ServerRegistry } from './registry.js';
3
4
  import type { ServeServiceOptions } from './serve-options.js';
4
5
  export interface RunningService {
@@ -13,6 +14,8 @@ export interface RunningService {
13
14
  readonly registry: ServerRegistry;
14
15
  /** Recovery outcome when persistence is enabled (a `dataDir` was set); absent otherwise. */
15
16
  readonly recovered?: RecoverResult;
17
+ /** Loopback/in-process only. Present solely for direct Firebase/Microsoft Devtools auth. */
18
+ readonly localDevtoolsDelegatedCredentials?: LocalDevtoolsDelegatedCredentialSink;
16
19
  close(): Promise<void>;
17
20
  }
18
21
  /** Start the deploy service on its own `node:http` server (used by the `noodle-service` bin). */
@@ -1 +1 @@
1
- {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAsDtD,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAwC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,iGAAiG;AACjG,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAqmB7F"}
1
+ {"version":3,"file":"serve.d.ts","sourceRoot":"","sources":["../src/serve.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAyCtD,OAAO,EAEL,KAAK,oCAAoC,EAC1C,MAAM,2CAA2C,CAAC;AAkBnD,OAAO,EAAE,KAAK,aAAa,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACnE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAyC9D,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC;IAClC,4FAA4F;IAC5F,QAAQ,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC;IACnC,4FAA4F;IAC5F,QAAQ,CAAC,iCAAiC,CAAC,EAAE,oCAAoC,CAAC;IAClF,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,iGAAiG;AACjG,wBAAsB,YAAY,CAAC,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CA4pB7F"}
@@ -11,10 +11,12 @@ import { parseBillingUsageHmacKey } from './billing/usage-identity.js';
11
11
  import { resolveBuildInfo } from './build-info.js';
12
12
  import { createDefaultControlPlaneGate } from './control-plane-auth-bootstrap.js';
13
13
  import { warnCustomerAuthAudienceQuarantine } from './customer-auth-audience-quarantine.js';
14
- import { createHostedCustomerVerifierFactory } from './customer-verifier.js';
14
+ import { createHostedCustomerVerifierFactory, createLocalDevtoolsCustomerVerifierFactory, } from './customer-verifier.js';
15
+ import { startConfiguredFeedbackOperationsWorker } from './feedback-operations/bootstrap.js';
15
16
  import { GITHUB_RUN_RECONCILE_INTERVAL_MS, GithubDeployRunReconciler, } from './github/deploy-run-reconciler.js';
16
17
  import { PostgresGoogleWorkloadIdentityStore } from './google-workload-identity-postgres.js';
17
18
  import { InMemoryGoogleWorkloadIdentityStore } from './google-workload-identity-store.js';
19
+ import { createLocalDevtoolsDelegatedCredentialSource, } from './local-devtools-delegated-credentials.js';
18
20
  import { ensureMcpConfirmationNonceSchema, PostgresMcpConfirmationNonceLedger, } from './mcp-confirmation-nonce-postgres.js';
19
21
  import { createHostedMcpRequestStateManager } from './mcp-protocol-runtime.js';
20
22
  import { ModuleHost } from './modules/host.js';
@@ -37,6 +39,7 @@ import { InMemoryControlPlaneStore } from './store/control-plane.js';
37
39
  import { PostgresPendingGithubInstallationStore } from './store/pending-installations-postgres.js';
38
40
  import { PostgresAlertRuleStore } from './store/postgres-alerts.js';
39
41
  import { PostgresAssistantStore } from './store/postgres-assistant.js';
42
+ import { PostgresFeedbackOperationsStore } from './store/postgres-feedback-operations.js';
40
43
  import { PostgresStateHandleStore } from './store/postgres-state-handles.js';
41
44
  import { RequestEventBuffer } from './store/request-event-buffer.js';
42
45
  import { InMemoryRequestEventStore } from './store/request-events.js';
@@ -53,7 +56,19 @@ export async function serveService(options = {}) {
53
56
  });
54
57
  if (!serviceConfigSource.ok)
55
58
  throw new Error(serviceConfigSource.error);
59
+ if (options.feedbackOperationsConfig !== undefined &&
60
+ (options.linearFeedbackClient === undefined ||
61
+ options.slackFeedbackClient === undefined ||
62
+ options.feedbackReporterEmailSender === undefined)) {
63
+ throw new Error('feedback operations require Linear, Slack, and reporter email providers together');
64
+ }
56
65
  const host = options.host ?? '127.0.0.1';
66
+ if (options.localDevtoolsDirectFirebaseAuth === true && !isLoopbackHost(host)) {
67
+ throw new Error('local Devtools Firebase verification requires a loopback service bind');
68
+ }
69
+ if (options.localDevtoolsDirectMicrosoftAuth === true && !isLoopbackHost(host)) {
70
+ throw new Error('local Devtools Microsoft verification requires a loopback service bind');
71
+ }
57
72
  let gate = options.deployGate;
58
73
  const durableStoreRequested = options.dataDir !== undefined ||
59
74
  options.databaseUrl !== undefined ||
@@ -121,6 +136,7 @@ export async function serveService(options = {}) {
121
136
  let githubConnectionStore = options.githubConnectionStore;
122
137
  let githubDeployRunStore = options.githubDeployRunStore;
123
138
  let pendingGithubInstallationStore = options.pendingGithubInstallationStore;
139
+ let feedbackOperations = options.feedbackOperations;
124
140
  let pgPool;
125
141
  let platformIdentityStore = options.platformIdentityStore;
126
142
  let googleWorkloadIdentity = options.googleWorkloadIdentity;
@@ -180,6 +196,11 @@ export async function serveService(options = {}) {
180
196
  githubDeployRunStore = githubDeployRunStore ?? postgres;
181
197
  pendingGithubInstallationStore =
182
198
  pendingGithubInstallationStore ?? new PostgresPendingGithubInstallationStore(pgPool.pool);
199
+ if (options.feedbackOperationsConfig !== undefined && feedbackOperations === undefined) {
200
+ const postgresFeedback = new PostgresFeedbackOperationsStore(pgPool.pool);
201
+ await postgresFeedback.ensureSchema();
202
+ feedbackOperations = postgresFeedback;
203
+ }
183
204
  }
184
205
  else if (options.dataDir !== undefined) {
185
206
  store = new JsonFileArtifactStore(options.dataDir);
@@ -189,6 +210,9 @@ export async function serveService(options = {}) {
189
210
  githubDeployRunStore =
190
211
  githubDeployRunStore ?? new JsonFileGithubDeployRunStore(options.dataDir);
191
212
  }
213
+ if (options.feedbackOperationsConfig !== undefined && feedbackOperations === undefined) {
214
+ throw new Error('feedback operations require PostgreSQL in the live service bootstrap');
215
+ }
192
216
  platformAuth = await resolvePlatformAuthServiceBootstrap({
193
217
  ...(pgPool === undefined ? {} : { pool: pgPool.pool }),
194
218
  releaseSha: buildInfo.gitSha,
@@ -266,6 +290,10 @@ export async function serveService(options = {}) {
266
290
  ...(options.clock !== undefined ? { clock: options.clock } : {}),
267
291
  });
268
292
  }
293
+ const feedbackOperationsTimer = startConfiguredFeedbackOperationsWorker({
294
+ options,
295
+ store: feedbackOperations,
296
+ });
269
297
  let githubRunReconcileTimer;
270
298
  if (githubConnectionStore !== undefined &&
271
299
  githubDeployRunStore !== undefined &&
@@ -351,6 +379,12 @@ export async function serveService(options = {}) {
351
379
  registry: () => registry,
352
380
  });
353
381
  const { authServerApp, verifyOwnerToken: resolvedVerifyOwnerToken, authServerIssuer: resolvedAuthServerIssuer, oauthStore, developerGrantStore, delegatedExchange, rawCustomerVerifierFactory, } = oauthBootstrap;
382
+ const localDevtoolsDelegatedCredentialSource = oauthStore === undefined &&
383
+ (options.localDevtoolsDirectFirebaseAuth === true ||
384
+ options.localDevtoolsDirectMicrosoftAuth === true)
385
+ ? createLocalDevtoolsDelegatedCredentialSource()
386
+ : undefined;
387
+ const delegatedCredentialStore = oauthStore ?? localDevtoolsDelegatedCredentialSource?.store;
354
388
  if (googleWorkloadIdentity === undefined && options.oauth !== undefined) {
355
389
  googleWorkloadIdentity = {
356
390
  issuer: options.oauth.issuer,
@@ -382,11 +416,22 @@ export async function serveService(options = {}) {
382
416
  await controlPlaneStore.createOrg({ slug: 'local', displayName: 'Local' });
383
417
  }
384
418
  registry = new ServerRegistry(store, secretBox, configStore, {
385
- customerVerifierFactory: createHostedCustomerVerifierFactory(rawCustomerVerifierFactory, resolvedVerifyOwnerToken),
419
+ customerVerifierFactory: options.localDevtoolsDirectFirebaseAuth === true ||
420
+ options.localDevtoolsDirectMicrosoftAuth === true
421
+ ? createLocalDevtoolsCustomerVerifierFactory(rawCustomerVerifierFactory, {
422
+ allowedProviders: [
423
+ ...(options.localDevtoolsDirectFirebaseAuth === true ? ['firebase'] : []),
424
+ ...(options.localDevtoolsDirectMicrosoftAuth === true ? ['microsoft'] : []),
425
+ ],
426
+ ...(options.localDevtoolsResolveBridgeAuth === undefined
427
+ ? {}
428
+ : { resolveBridgeAuth: options.localDevtoolsResolveBridgeAuth }),
429
+ })
430
+ : createHostedCustomerVerifierFactory(rawCustomerVerifierFactory, resolvedVerifyOwnerToken),
386
431
  ...(moduleHost.policyGate !== undefined ? { policyGate: moduleHost.policyGate } : {}),
387
- ...(oauthStore !== undefined
432
+ ...(delegatedCredentialStore !== undefined
388
433
  ? {
389
- delegatedCredentialStore: oauthStore,
434
+ delegatedCredentialStore,
390
435
  sealCustomerCredential: (credential) => sealCustomerCredential(secretBox, credential),
391
436
  openCustomerCredential: (credential) => openCustomerCredential(secretBox, credential),
392
437
  }
@@ -439,6 +484,7 @@ export async function serveService(options = {}) {
439
484
  ? {}
440
485
  : { billingStripeOverageSettlementTimer }),
441
486
  ...(welcomeEmailTimer === undefined ? {} : { welcomeEmailTimer }),
487
+ ...(feedbackOperationsTimer === undefined ? {} : { feedbackOperationsTimer }),
442
488
  ...(platformIdentityMaintenanceTimer === undefined
443
489
  ? {}
444
490
  : { platformIdentityMaintenanceTimer }),
@@ -530,6 +576,7 @@ export async function serveService(options = {}) {
530
576
  ...(githubConnectionStore !== undefined ? { githubConnectionStore } : {}),
531
577
  ...(githubDeployRunStore !== undefined ? { githubDeployRunStore } : {}),
532
578
  ...(pendingGithubInstallationStore !== undefined ? { pendingGithubInstallationStore } : {}),
579
+ ...(feedbackOperations !== undefined ? { feedbackOperations } : {}),
533
580
  buildInfo,
534
581
  ...(assetStore !== undefined ? { assetStore } : {}),
535
582
  ...(assetPublicBaseUrl !== undefined ? { assetPublicBaseUrl } : {}),
@@ -543,8 +590,9 @@ export async function serveService(options = {}) {
543
590
  };
544
591
  let http;
545
592
  try {
546
- http = createServer(createServiceHandler(registry, handlerOptions));
547
- await listenHttpServer(http, options.port ?? 8787, host);
593
+ const listeningHttp = createServer(createServiceHandler(registry, handlerOptions));
594
+ http = listeningHttp;
595
+ await listenHttpServer(listeningHttp, options.port ?? 8787, host);
548
596
  billingMeterRuntime?.start();
549
597
  if (platformAuth.runtime !== undefined) {
550
598
  platformIdentityMaintenanceTimer = startPlatformIdentityMaintenanceWorker({
@@ -554,6 +602,24 @@ export async function serveService(options = {}) {
554
602
  logger: options.logger ?? noopLogger,
555
603
  });
556
604
  }
605
+ const { port } = listeningHttp.address();
606
+ const urlHost = host.includes(':') ? `[${host}]` : host;
607
+ const serviceUrl = `http://${urlHost}:${port}`;
608
+ const localDevtoolsDelegatedCredentials = localDevtoolsDelegatedCredentialSource?.bind(serviceUrl);
609
+ return {
610
+ http: listeningHttp,
611
+ url: serviceUrl,
612
+ port,
613
+ registry,
614
+ ...(recovered ? { recovered } : {}),
615
+ ...(localDevtoolsDelegatedCredentials === undefined
616
+ ? {}
617
+ : { localDevtoolsDelegatedCredentials }),
618
+ close: async () => {
619
+ await closeHttpServer(listeningHttp);
620
+ await closeResources();
621
+ },
622
+ };
557
623
  }
558
624
  catch (error) {
559
625
  if (http?.listening)
@@ -561,17 +627,5 @@ export async function serveService(options = {}) {
561
627
  await closeResources();
562
628
  throw error;
563
629
  }
564
- const { port } = http.address();
565
- return {
566
- http,
567
- url: `http://${host}:${port}`,
568
- port,
569
- registry,
570
- ...(recovered ? { recovered } : {}),
571
- close: async () => {
572
- await closeHttpServer(http);
573
- await closeResources();
574
- },
575
- };
576
630
  }
577
631
  //# sourceMappingURL=serve.js.map