@agent-relay/factory 0.0.0 → 0.1.2

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 (235) hide show
  1. package/README.md +182 -23
  2. package/bin/factory.mjs +7 -0
  3. package/dist/cli/fleet.d.ts +81 -0
  4. package/dist/cli/fleet.d.ts.map +1 -0
  5. package/dist/cli/fleet.js +571 -0
  6. package/dist/cli/fleet.js.map +1 -0
  7. package/dist/config/schema.d.ts +1313 -0
  8. package/dist/config/schema.d.ts.map +1 -0
  9. package/dist/config/schema.js +320 -0
  10. package/dist/config/schema.js.map +1 -0
  11. package/dist/constants/linear.d.ts +6 -0
  12. package/dist/constants/linear.d.ts.map +1 -0
  13. package/dist/constants/linear.js +21 -0
  14. package/dist/constants/linear.js.map +1 -0
  15. package/dist/constants/slack.d.ts +3 -0
  16. package/dist/constants/slack.d.ts.map +1 -0
  17. package/dist/constants/slack.js +3 -0
  18. package/dist/constants/slack.js.map +1 -0
  19. package/dist/dispatch/templates.d.ts +52 -0
  20. package/dist/dispatch/templates.d.ts.map +1 -0
  21. package/dist/dispatch/templates.js +118 -0
  22. package/dist/dispatch/templates.js.map +1 -0
  23. package/dist/fleet/create-fleet.d.ts +14 -0
  24. package/dist/fleet/create-fleet.d.ts.map +1 -0
  25. package/dist/fleet/create-fleet.js +15 -0
  26. package/dist/fleet/create-fleet.js.map +1 -0
  27. package/dist/fleet/ensure-relay-broker.d.ts +24 -0
  28. package/dist/fleet/ensure-relay-broker.d.ts.map +1 -0
  29. package/dist/fleet/ensure-relay-broker.js +56 -0
  30. package/dist/fleet/ensure-relay-broker.js.map +1 -0
  31. package/dist/fleet/internal-fleet-client.d.ts +90 -0
  32. package/dist/fleet/internal-fleet-client.d.ts.map +1 -0
  33. package/dist/fleet/internal-fleet-client.js +602 -0
  34. package/dist/fleet/internal-fleet-client.js.map +1 -0
  35. package/dist/fleet/relay-fleet-client.d.ts +105 -0
  36. package/dist/fleet/relay-fleet-client.d.ts.map +1 -0
  37. package/dist/fleet/relay-fleet-client.js +509 -0
  38. package/dist/fleet/relay-fleet-client.js.map +1 -0
  39. package/dist/github/index.d.ts +5 -0
  40. package/dist/github/index.d.ts.map +1 -0
  41. package/dist/github/index.js +3 -0
  42. package/dist/github/index.js.map +1 -0
  43. package/dist/github/merge-gate.d.ts +47 -0
  44. package/dist/github/merge-gate.d.ts.map +1 -0
  45. package/dist/github/merge-gate.js +147 -0
  46. package/dist/github/merge-gate.js.map +1 -0
  47. package/dist/github/probe-closer.d.ts +15 -0
  48. package/dist/github/probe-closer.d.ts.map +1 -0
  49. package/dist/github/probe-closer.js +67 -0
  50. package/dist/github/probe-closer.js.map +1 -0
  51. package/dist/index.d.ts +36 -0
  52. package/dist/index.d.ts.map +1 -0
  53. package/dist/index.js +20 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/issue-key-match.d.ts +3 -0
  56. package/dist/issue-key-match.d.ts.map +1 -0
  57. package/dist/issue-key-match.js +22 -0
  58. package/dist/issue-key-match.js.map +1 -0
  59. package/dist/linear/state-resolver.d.ts +26 -0
  60. package/dist/linear/state-resolver.d.ts.map +1 -0
  61. package/dist/linear/state-resolver.js +285 -0
  62. package/dist/linear/state-resolver.js.map +1 -0
  63. package/dist/mount/local-mount-preflight.d.ts +9 -0
  64. package/dist/mount/local-mount-preflight.d.ts.map +1 -0
  65. package/dist/mount/local-mount-preflight.js +149 -0
  66. package/dist/mount/local-mount-preflight.js.map +1 -0
  67. package/dist/mount/relayfile-binary.d.ts +9 -0
  68. package/dist/mount/relayfile-binary.d.ts.map +1 -0
  69. package/dist/mount/relayfile-binary.js +177 -0
  70. package/dist/mount/relayfile-binary.js.map +1 -0
  71. package/dist/mount/relayfile-cloud-mount-client.d.ts +123 -0
  72. package/dist/mount/relayfile-cloud-mount-client.d.ts.map +1 -0
  73. package/dist/mount/relayfile-cloud-mount-client.js +427 -0
  74. package/dist/mount/relayfile-cloud-mount-client.js.map +1 -0
  75. package/dist/node/factory-node.d.ts +59 -0
  76. package/dist/node/factory-node.d.ts.map +1 -0
  77. package/dist/node/factory-node.js +351 -0
  78. package/dist/node/factory-node.js.map +1 -0
  79. package/dist/node/factory.node.d.ts +3 -0
  80. package/dist/node/factory.node.d.ts.map +1 -0
  81. package/dist/node/factory.node.js +5 -0
  82. package/dist/node/factory.node.js.map +1 -0
  83. package/dist/orchestrator/batch-tracker.d.ts +42 -0
  84. package/dist/orchestrator/batch-tracker.d.ts.map +1 -0
  85. package/dist/orchestrator/batch-tracker.js +125 -0
  86. package/dist/orchestrator/batch-tracker.js.map +1 -0
  87. package/dist/orchestrator/factory.d.ts +58 -0
  88. package/dist/orchestrator/factory.d.ts.map +1 -0
  89. package/dist/orchestrator/factory.js +4372 -0
  90. package/dist/orchestrator/factory.js.map +1 -0
  91. package/dist/orchestrator/index.d.ts +5 -0
  92. package/dist/orchestrator/index.d.ts.map +1 -0
  93. package/dist/orchestrator/index.js +4 -0
  94. package/dist/orchestrator/index.js.map +1 -0
  95. package/dist/orchestrator/process-identity.d.ts +23 -0
  96. package/dist/orchestrator/process-identity.d.ts.map +1 -0
  97. package/dist/orchestrator/process-identity.js +122 -0
  98. package/dist/orchestrator/process-identity.js.map +1 -0
  99. package/dist/orchestrator/reaper.d.ts +60 -0
  100. package/dist/orchestrator/reaper.d.ts.map +1 -0
  101. package/dist/orchestrator/reaper.js +293 -0
  102. package/dist/orchestrator/reaper.js.map +1 -0
  103. package/dist/ports/fleet.d.ts +101 -0
  104. package/dist/ports/fleet.d.ts.map +1 -0
  105. package/dist/ports/fleet.js +2 -0
  106. package/dist/ports/fleet.js.map +1 -0
  107. package/dist/ports/index.d.ts +5 -0
  108. package/dist/ports/index.d.ts.map +1 -0
  109. package/dist/ports/index.js +2 -0
  110. package/dist/ports/index.js.map +1 -0
  111. package/dist/ports/mount.d.ts +56 -0
  112. package/dist/ports/mount.d.ts.map +1 -0
  113. package/dist/ports/mount.js +2 -0
  114. package/dist/ports/mount.js.map +1 -0
  115. package/dist/ports/state.d.ts +61 -0
  116. package/dist/ports/state.d.ts.map +1 -0
  117. package/dist/ports/state.js +2 -0
  118. package/dist/ports/state.js.map +1 -0
  119. package/dist/ports/system.d.ts +15 -0
  120. package/dist/ports/system.d.ts.map +1 -0
  121. package/dist/ports/system.js +2 -0
  122. package/dist/ports/system.js.map +1 -0
  123. package/dist/ports/writeback.d.ts +25 -0
  124. package/dist/ports/writeback.d.ts.map +1 -0
  125. package/dist/ports/writeback.js +2 -0
  126. package/dist/ports/writeback.js.map +1 -0
  127. package/dist/safety/factory-scope.d.ts +16 -0
  128. package/dist/safety/factory-scope.d.ts.map +1 -0
  129. package/dist/safety/factory-scope.js +107 -0
  130. package/dist/safety/factory-scope.js.map +1 -0
  131. package/dist/state/in-memory-state-store.d.ts +31 -0
  132. package/dist/state/in-memory-state-store.d.ts.map +1 -0
  133. package/dist/state/in-memory-state-store.js +115 -0
  134. package/dist/state/in-memory-state-store.js.map +1 -0
  135. package/dist/state/index.d.ts +3 -0
  136. package/dist/state/index.d.ts.map +1 -0
  137. package/dist/state/index.js +2 -0
  138. package/dist/state/index.js.map +1 -0
  139. package/dist/state/store.d.ts +29 -0
  140. package/dist/state/store.d.ts.map +1 -0
  141. package/dist/state/store.js +26 -0
  142. package/dist/state/store.js.map +1 -0
  143. package/dist/subscriptions/common.d.ts +7 -0
  144. package/dist/subscriptions/common.d.ts.map +1 -0
  145. package/dist/subscriptions/common.js +59 -0
  146. package/dist/subscriptions/common.js.map +1 -0
  147. package/dist/subscriptions/event-client.d.ts +40 -0
  148. package/dist/subscriptions/event-client.d.ts.map +1 -0
  149. package/dist/subscriptions/event-client.js +234 -0
  150. package/dist/subscriptions/event-client.js.map +1 -0
  151. package/dist/subscriptions/globs.d.ts +5 -0
  152. package/dist/subscriptions/globs.d.ts.map +1 -0
  153. package/dist/subscriptions/globs.js +50 -0
  154. package/dist/subscriptions/globs.js.map +1 -0
  155. package/dist/subscriptions/index.d.ts +8 -0
  156. package/dist/subscriptions/index.d.ts.map +1 -0
  157. package/dist/subscriptions/index.js +5 -0
  158. package/dist/subscriptions/index.js.map +1 -0
  159. package/dist/subscriptions/linear-filter.d.ts +26 -0
  160. package/dist/subscriptions/linear-filter.d.ts.map +1 -0
  161. package/dist/subscriptions/linear-filter.js +143 -0
  162. package/dist/subscriptions/linear-filter.js.map +1 -0
  163. package/dist/subscriptions/specs.d.ts +44 -0
  164. package/dist/subscriptions/specs.d.ts.map +1 -0
  165. package/dist/subscriptions/specs.js +195 -0
  166. package/dist/subscriptions/specs.js.map +1 -0
  167. package/dist/testing/fakes.d.ts +114 -0
  168. package/dist/testing/fakes.d.ts.map +1 -0
  169. package/dist/testing/fakes.js +219 -0
  170. package/dist/testing/fakes.js.map +1 -0
  171. package/dist/testing/index.d.ts +2 -0
  172. package/dist/testing/index.d.ts.map +1 -0
  173. package/dist/testing/index.js +2 -0
  174. package/dist/testing/index.js.map +1 -0
  175. package/dist/triage/heuristic.d.ts +42 -0
  176. package/dist/triage/heuristic.d.ts.map +1 -0
  177. package/dist/triage/heuristic.js +326 -0
  178. package/dist/triage/heuristic.js.map +1 -0
  179. package/dist/triage/index.d.ts +7 -0
  180. package/dist/triage/index.d.ts.map +1 -0
  181. package/dist/triage/index.js +5 -0
  182. package/dist/triage/index.js.map +1 -0
  183. package/dist/triage/llm.d.ts +11 -0
  184. package/dist/triage/llm.d.ts.map +1 -0
  185. package/dist/triage/llm.js +48 -0
  186. package/dist/triage/llm.js.map +1 -0
  187. package/dist/triage/schema.d.ts +344 -0
  188. package/dist/triage/schema.d.ts.map +1 -0
  189. package/dist/triage/schema.js +37 -0
  190. package/dist/triage/schema.js.map +1 -0
  191. package/dist/triage/tiered.d.ts +8 -0
  192. package/dist/triage/tiered.d.ts.map +1 -0
  193. package/dist/triage/tiered.js +47 -0
  194. package/dist/triage/tiered.js.map +1 -0
  195. package/dist/types.d.ts +212 -0
  196. package/dist/types.d.ts.map +1 -0
  197. package/dist/types.js +2 -0
  198. package/dist/types.js.map +1 -0
  199. package/dist/webhook/handler.d.ts +22 -0
  200. package/dist/webhook/handler.d.ts.map +1 -0
  201. package/dist/webhook/handler.js +57 -0
  202. package/dist/webhook/handler.js.map +1 -0
  203. package/dist/webhook/index.d.ts +5 -0
  204. package/dist/webhook/index.d.ts.map +1 -0
  205. package/dist/webhook/index.js +3 -0
  206. package/dist/webhook/index.js.map +1 -0
  207. package/dist/webhook/registrar.d.ts +16 -0
  208. package/dist/webhook/registrar.d.ts.map +1 -0
  209. package/dist/webhook/registrar.js +50 -0
  210. package/dist/webhook/registrar.js.map +1 -0
  211. package/dist/writeback/github.d.ts +6 -0
  212. package/dist/writeback/github.d.ts.map +1 -0
  213. package/dist/writeback/github.js +48 -0
  214. package/dist/writeback/github.js.map +1 -0
  215. package/dist/writeback/index.d.ts +6 -0
  216. package/dist/writeback/index.d.ts.map +1 -0
  217. package/dist/writeback/index.js +4 -0
  218. package/dist/writeback/index.js.map +1 -0
  219. package/dist/writeback/linear.d.ts +41 -0
  220. package/dist/writeback/linear.d.ts.map +1 -0
  221. package/dist/writeback/linear.js +286 -0
  222. package/dist/writeback/linear.js.map +1 -0
  223. package/dist/writeback/shared.d.ts +7 -0
  224. package/dist/writeback/shared.d.ts.map +1 -0
  225. package/dist/writeback/shared.js +29 -0
  226. package/dist/writeback/shared.js.map +1 -0
  227. package/dist/writeback/slack.d.ts +16 -0
  228. package/dist/writeback/slack.d.ts.map +1 -0
  229. package/dist/writeback/slack.js +104 -0
  230. package/dist/writeback/slack.js.map +1 -0
  231. package/package.json +54 -9
  232. package/scripts/com.agentrelay.factory-canary.plist.example +53 -0
  233. package/scripts/factory-canary.sh +89 -0
  234. package/index.d.ts +0 -2
  235. package/index.js +0 -7
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,22 @@
1
+ export interface WebhookEvent {
2
+ type: string;
3
+ path: string;
4
+ revision: string;
5
+ eventId: string;
6
+ contentHash?: string;
7
+ provider?: string;
8
+ correlationId?: string;
9
+ timestamp: string;
10
+ }
11
+ export interface WebhookHandlerConfig {
12
+ secret: string;
13
+ onLinearEvent: (event: WebhookEvent) => Promise<void>;
14
+ onSlackEvent: (event: WebhookEvent) => Promise<void>;
15
+ onGithubEvent: (event: WebhookEvent) => Promise<void>;
16
+ }
17
+ export declare class WebhookHandler {
18
+ #private;
19
+ constructor(config: WebhookHandlerConfig);
20
+ handle(request: Request): Promise<Response>;
21
+ }
22
+ //# sourceMappingURL=handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,oBAAoB;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACrD,YAAY,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpD,aAAa,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACtD;AAED,qBAAa,cAAc;;gBAIb,MAAM,EAAE,oBAAoB;IAIlC,MAAM,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC;CA2ClD"}
@@ -0,0 +1,57 @@
1
+ import { createHmac, timingSafeEqual as cryptoTimingSafeEqual } from 'node:crypto';
2
+ export class WebhookHandler {
3
+ #config;
4
+ #seen = new Set();
5
+ constructor(config) {
6
+ this.#config = config;
7
+ }
8
+ async handle(request) {
9
+ const timestamp = request.headers.get('x-relay-timestamp') ?? '';
10
+ const signature = request.headers.get('x-relay-signature') ?? '';
11
+ const headerEventId = request.headers.get('x-relay-event-id') ?? '';
12
+ const rawBody = await request.text();
13
+ const expected = createHmac('sha256', this.#config.secret)
14
+ .update(`${timestamp}.${rawBody}`)
15
+ .digest('hex');
16
+ if (!timingSafeHexEqual(expected, signature)) {
17
+ return new Response('forbidden', { status: 403 });
18
+ }
19
+ let event;
20
+ try {
21
+ event = JSON.parse(rawBody);
22
+ }
23
+ catch {
24
+ return new Response('bad_request', { status: 400 });
25
+ }
26
+ const eventId = event.eventId || headerEventId;
27
+ if (eventId && this.#seen.has(eventId)) {
28
+ return new Response('ok', { status: 200 });
29
+ }
30
+ try {
31
+ if (event.path.startsWith('/linear/issues/')) {
32
+ await this.#config.onLinearEvent(event);
33
+ }
34
+ else if (event.path.startsWith('/slack/channels/')) {
35
+ await this.#config.onSlackEvent(event);
36
+ }
37
+ else if (event.path.startsWith('/github/repos/')) {
38
+ await this.#config.onGithubEvent(event);
39
+ }
40
+ }
41
+ catch {
42
+ return new Response('internal_error', { status: 500 });
43
+ }
44
+ if (eventId) {
45
+ this.#seen.add(eventId);
46
+ }
47
+ return new Response('ok', { status: 200 });
48
+ }
49
+ }
50
+ function timingSafeHexEqual(a, b) {
51
+ if (a.length !== b.length)
52
+ return false;
53
+ const bufA = Buffer.from(a, 'hex');
54
+ const bufB = Buffer.from(b, 'hex');
55
+ return bufA.length === bufB.length && cryptoTimingSafeEqual(bufA, bufB);
56
+ }
57
+ //# sourceMappingURL=handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handler.js","sourceRoot":"","sources":["../../src/webhook/handler.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { WebhookHandler } from './handler.js';
2
+ export type { WebhookHandlerConfig, WebhookEvent } from './handler.js';
3
+ export { WebhookRegistrar } from './registrar.js';
4
+ export type { WebhookRegistrarConfig } from './registrar.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AACtE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { WebhookHandler } from './handler.js';
2
+ export { WebhookRegistrar } from './registrar.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/webhook/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,16 @@
1
+ import type { RelayFileClient } from '@relayfile/sdk';
2
+ export interface WebhookRegistrarConfig {
3
+ client: RelayFileClient;
4
+ workspaceId: string;
5
+ receiverUrl: string;
6
+ secret: string;
7
+ pathGlobs?: string[];
8
+ }
9
+ export declare class WebhookRegistrar {
10
+ #private;
11
+ constructor(config: WebhookRegistrarConfig);
12
+ register(): Promise<void>;
13
+ unregister(): Promise<void>;
14
+ get subscriptionId(): string | undefined;
15
+ }
16
+ //# sourceMappingURL=registrar.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registrar.d.ts","sourceRoot":"","sources":["../../src/webhook/registrar.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAuB,MAAM,gBAAgB,CAAA;AAQ1E,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,CAAA;IACvB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,qBAAa,gBAAgB;;gBAIf,MAAM,EAAE,sBAAsB;IAIpC,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAmBzB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IASjC,IAAI,cAAc,IAAI,MAAM,GAAG,SAAS,CAEvC;CASF"}
@@ -0,0 +1,50 @@
1
+ const DEFAULT_PATH_GLOBS = [
2
+ '/linear/issues/**',
3
+ '/slack/channels/**',
4
+ '/github/repos/**',
5
+ ];
6
+ export class WebhookRegistrar {
7
+ #config;
8
+ #subscriptionId;
9
+ constructor(config) {
10
+ this.#config = config;
11
+ }
12
+ async register() {
13
+ if (this.#subscriptionId)
14
+ return;
15
+ const pathGlobs = this.#config.pathGlobs ?? DEFAULT_PATH_GLOBS;
16
+ const existing = await this.#findExistingSubscription(pathGlobs);
17
+ if (existing) {
18
+ this.#subscriptionId = existing.id;
19
+ return;
20
+ }
21
+ const subscription = await this.#config.client.registerWebhook({
22
+ workspaceId: this.#config.workspaceId,
23
+ url: this.#config.receiverUrl,
24
+ pathGlobs,
25
+ secret: this.#config.secret,
26
+ });
27
+ this.#subscriptionId = subscription.subscriptionId;
28
+ }
29
+ async unregister() {
30
+ if (!this.#subscriptionId)
31
+ return;
32
+ await this.#config.client.deleteWebhook(this.#config.workspaceId, this.#subscriptionId);
33
+ this.#subscriptionId = undefined;
34
+ }
35
+ get subscriptionId() {
36
+ return this.#subscriptionId;
37
+ }
38
+ async #findExistingSubscription(pathGlobs) {
39
+ const subscriptions = await this.#config.client.listWebhooks(this.#config.workspaceId);
40
+ return subscriptions.find((subscription) => (subscription.url === this.#config.receiverUrl
41
+ && sameStringSet(subscription.pathGlobs, pathGlobs)));
42
+ }
43
+ }
44
+ function sameStringSet(a, b) {
45
+ if (a.length !== b.length)
46
+ return false;
47
+ const bSet = new Set(b);
48
+ return a.every((value) => bSet.has(value));
49
+ }
50
+ //# sourceMappingURL=registrar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registrar.js","sourceRoot":"","sources":["../../src/webhook/registrar.ts"],"names":[],"mappings":"AAEA,MAAM,kBAAkB,GAAG;IACzB,mBAAmB;IACnB,oBAAoB;IACpB,kBAAkB;CACnB,CAAA;AAUD,MAAM,OAAO,gBAAgB;IAClB,OAAO,CAAwB;IACxC,eAAe,CAAS;IAExB,YAAY,MAA8B;QACxC,IAAI,CAAC,OAAO,GAAG,MAAM,CAAA;IACvB,CAAC;IAED,KAAK,CAAC,QAAQ;QACZ,IAAI,IAAI,CAAC,eAAe;YAAE,OAAM;QAEhC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,IAAI,kBAAkB,CAAA;QAC9D,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,yBAAyB,CAAC,SAAS,CAAC,CAAA;QAChE,IAAI,QAAQ,EAAE,CAAC;YACb,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAC,EAAE,CAAA;YAClC,OAAM;QACR,CAAC;QAED,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,eAAe,CAAC;YAC7D,WAAW,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YACrC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,WAAW;YAC7B,SAAS;YACT,MAAM,EAAE,IAAI,CAAC,OAAO,CAAC,MAAM;SAC5B,CAAC,CAAA;QACF,IAAI,CAAC,eAAe,GAAG,YAAY,CAAC,cAAc,CAAA;IACpD,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC,IAAI,CAAC,eAAe;YAAE,OAAM;QACjC,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CACrC,IAAI,CAAC,OAAO,CAAC,WAAW,EACxB,IAAI,CAAC,eAAe,CACrB,CAAA;QACD,IAAI,CAAC,eAAe,GAAG,SAAS,CAAA;IAClC,CAAC;IAED,IAAI,cAAc;QAChB,OAAO,IAAI,CAAC,eAAe,CAAA;IAC7B,CAAC;IAED,KAAK,CAAC,yBAAyB,CAAC,SAAmB;QACjD,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAA;QACtF,OAAO,aAAa,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,EAAE,CAAC,CAC1C,YAAY,CAAC,GAAG,KAAK,IAAI,CAAC,OAAO,CAAC,WAAW;eAC1C,aAAa,CAAC,YAAY,CAAC,SAAS,EAAE,SAAS,CAAC,CACpD,CAAC,CAAA;IACJ,CAAC;CACF;AAED,SAAS,aAAa,CAAC,CAAW,EAAE,CAAW;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAA;IACvC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC,CAAC,CAAA;IACvB,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAC5C,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { MountClient } from '../ports/index.js';
2
+ import type { PrSummary } from '../types.js';
3
+ export declare const MountGithubRead: (mount: MountClient) => {
4
+ getPr(repo: string, number: number): Promise<PrSummary>;
5
+ };
6
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/writeback/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,UAAU,CAAA;AAmBzC,eAAO,MAAM,eAAe,GAAI,OAAO,WAAW;gBAC9B,MAAM,UAAU,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC;CAgB7D,CAAA"}
@@ -0,0 +1,48 @@
1
+ import { asRecord, wrappedPayload } from './shared.js';
2
+ const repoDir = (repo) => {
3
+ if (repo.includes('__')) {
4
+ return repo;
5
+ }
6
+ const [owner, name] = repo.split('/');
7
+ if (!owner || !name) {
8
+ throw new Error(`GitHub repo must be owner/repo or owner__repo: ${repo}`);
9
+ }
10
+ return `${owner}__${name}`;
11
+ };
12
+ const prPath = (repo, number) => `/github/repos/${repoDir(repo)}/pulls/by-id/${number}.json`;
13
+ export const MountGithubRead = (mount) => ({
14
+ async getPr(repo, number) {
15
+ const { content } = await mount.readFile(prPath(repo, number));
16
+ const payload = wrappedPayload(content);
17
+ return {
18
+ repo,
19
+ number: numberValue(payload.number) ?? number,
20
+ title: typeof payload.title === 'string' ? payload.title : undefined,
21
+ url: typeof payload.url === 'string' ? payload.url : undefined,
22
+ state: typeof payload.state === 'string' ? payload.state : undefined,
23
+ headRef: refName(payload.headRef) ?? refName(payload.head) ?? stringValue(payload.head_ref),
24
+ baseRef: refName(payload.baseRef) ?? refName(payload.base) ?? stringValue(payload.base_ref),
25
+ author: refName(payload.author) ?? stringValue(payload.user),
26
+ filesChanged: filesChanged(payload.files_changed ?? payload.filesChanged ?? payload.files),
27
+ };
28
+ },
29
+ });
30
+ const stringValue = (value) => typeof value === 'string' ? value : undefined;
31
+ const numberValue = (value) => typeof value === 'number' ? value : undefined;
32
+ const refName = (value) => {
33
+ if (typeof value === 'string') {
34
+ return value;
35
+ }
36
+ const record = asRecord(value);
37
+ return stringValue(record?.name) ?? stringValue(record?.ref) ?? stringValue(record?.login);
38
+ };
39
+ const filesChanged = (value) => {
40
+ if (!Array.isArray(value)) {
41
+ return undefined;
42
+ }
43
+ const files = value
44
+ .map((entry) => typeof entry === 'string' ? entry : stringValue(asRecord(entry)?.path) ?? stringValue(asRecord(entry)?.filename))
45
+ .filter((entry) => Boolean(entry));
46
+ return files.length > 0 ? files : undefined;
47
+ };
48
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/writeback/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export { linearCommentName, MountLinearWriteback, } from './linear.js';
2
+ export type { LinearCommentPayload, LinearCreateIssuePayload, LinearStateIds, MountLinearWritebackConfig, } from './linear.js';
3
+ export { MountSlackWriteback, } from './slack.js';
4
+ export type { MountSlackWritebackConfig, } from './slack.js';
5
+ export { MountGithubRead, } from './github.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/writeback/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,UAAU,CAAA;AACjB,YAAY,EACV,oBAAoB,EACpB,wBAAwB,EACxB,cAAc,EACd,0BAA0B,GAC3B,MAAM,UAAU,CAAA;AACjB,OAAO,EACL,mBAAmB,GACpB,MAAM,SAAS,CAAA;AAChB,YAAY,EACV,yBAAyB,GAC1B,MAAM,SAAS,CAAA;AAChB,OAAO,EACL,eAAe,GAChB,MAAM,UAAU,CAAA"}
@@ -0,0 +1,4 @@
1
+ export { linearCommentName, MountLinearWriteback, } from './linear.js';
2
+ export { MountSlackWriteback, } from './slack.js';
3
+ export { MountGithubRead, } from './github.js';
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/writeback/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,oBAAoB,GACrB,MAAM,UAAU,CAAA;AAOjB,OAAO,EACL,mBAAmB,GACpB,MAAM,SAAS,CAAA;AAIhB,OAAO,EACL,eAAe,GAChB,MAAM,UAAU,CAAA"}
@@ -0,0 +1,41 @@
1
+ import type { MountClient } from '../ports/index.js';
2
+ import type { Logger } from '../ports/system.js';
3
+ import type { LinearIssue } from '../types.js';
4
+ export interface LinearStateIds {
5
+ [name: string]: string;
6
+ }
7
+ export interface LinearCommentPayload {
8
+ body: string;
9
+ issueId: string;
10
+ }
11
+ export interface MountLinearWritebackConfig {
12
+ stateIds?: LinearStateIds;
13
+ safety?: {
14
+ requireTitlePrefix?: string;
15
+ requireLabel?: string;
16
+ requireTeamKey?: string;
17
+ };
18
+ logger?: Pick<Logger, 'warn'>;
19
+ }
20
+ export interface LinearCreateIssuePayload extends Record<string, unknown> {
21
+ id?: string;
22
+ identifier?: string;
23
+ title?: string;
24
+ teamId?: string;
25
+ team?: unknown;
26
+ labels?: unknown;
27
+ source?: unknown;
28
+ }
29
+ export declare const linearCommentName: (issue: LinearIssue, body: string) => string;
30
+ export declare const MountLinearWriteback: (mount: MountClient, configOrStateIds?: LinearStateIds | MountLinearWritebackConfig) => {
31
+ setState(issue: LinearIssue, stateId: string): Promise<void>;
32
+ postComment(issue: LinearIssue, body: string): Promise<void>;
33
+ createIssue(payload: LinearCreateIssuePayload): Promise<{
34
+ path: string;
35
+ }>;
36
+ verify(issue: LinearIssue, expect: {
37
+ stateId?: string;
38
+ commentName?: string;
39
+ }): Promise<boolean>;
40
+ };
41
+ //# sourceMappingURL=linear.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"linear.d.ts","sourceRoot":"","sources":["../../src/writeback/linear.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAC3C,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AAE7C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAG3C,MAAM,WAAW,cAAc;IAC7B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;CACvB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,MAAM,CAAC,EAAE;QACP,kBAAkB,CAAC,EAAE,MAAM,CAAA;QAC3B,YAAY,CAAC,EAAE,MAAM,CAAA;QACrB,cAAc,CAAC,EAAE,MAAM,CAAA;KACxB,CAAA;IACD,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED,MAAM,WAAW,wBAAyB,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACvE,EAAE,CAAC,EAAE,MAAM,CAAA;IACX,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AAKD,eAAO,MAAM,iBAAiB,GAAI,OAAO,WAAW,EAAE,MAAM,MAAM,KAAG,MACxB,CAAA;AAwI7C,eAAO,MAAM,oBAAoB,GAC/B,OAAO,WAAW,EAClB,mBAAmB,cAAc,GAAG,0BAA0B;oBAsEtC,WAAW,WAAW,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;uBAYzC,WAAW,QAAQ,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;yBASvC,wBAAwB,GAAG,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;kBAiBtE,WAAW,UACV;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACjD,OAAO,CAAC,OAAO,CAAC;CAkBtB,CAAA"}
@@ -0,0 +1,286 @@
1
+ import { linearByIdPath, linearByUuidPath, linearCommentPath, linearIssuePath } from '../constants/linear.js';
2
+ import { assertInFactoryScope, isInFactoryScope } from '../safety/factory-scope.js';
3
+ import { asRecord, safePathSegment, stableHash, wrappedPayload } from './shared.js';
4
+ const issuePath = (issue) => issue.path || linearIssuePath(issue.key, issue.uuid);
5
+ export const linearCommentName = (issue, body) => `${issue.key}__factory-${stableHash(body)}`;
6
+ const linearCommentPayload = (issue, body) => ({
7
+ body,
8
+ issueId: issue.uuid,
9
+ });
10
+ const safetyFromConfig = (configOrStateIds) => {
11
+ const safety = asRecord(asRecord(configOrStateIds)?.safety);
12
+ if (safety) {
13
+ return {
14
+ requireTitlePrefix: typeof safety.requireTitlePrefix === 'string' && safety.requireTitlePrefix
15
+ ? safety.requireTitlePrefix
16
+ : '[factory-e2e]',
17
+ requireLabel: typeof safety.requireLabel === 'string'
18
+ ? safety.requireLabel
19
+ : 'factory',
20
+ requireTeamKey: typeof safety.requireTeamKey === 'string' && safety.requireTeamKey
21
+ ? safety.requireTeamKey
22
+ : 'AR',
23
+ };
24
+ }
25
+ return { requireTitlePrefix: '[factory-e2e]', requireLabel: 'factory', requireTeamKey: 'AR' };
26
+ };
27
+ const payloadInFactoryScope = (payload, safety) => {
28
+ return isInFactoryScope(scopeIssueFromPayload(payload, 'createIssue payload'), safety);
29
+ };
30
+ const hasGuardFields = (payload) => typeof payload.title === 'string' || Array.isArray(payload.labels) || asRecord(payload.team) !== undefined;
31
+ const readIssuePayloadForGuard = async (mount, issue) => {
32
+ // The primary <key>__<uuid>.json may be a change-event STUB (no title/labels/
33
+ // team — the sparse-sync case); fall back to the canonical by-id/by-uuid
34
+ // records so the factory-scope guard sees the real fields and doesn't refuse a
35
+ // legitimately-[factory] issue.
36
+ const candidates = [
37
+ issuePath(issue),
38
+ ...(issue.key ? [linearByIdPath(issue.key)] : []),
39
+ ...(issue.uuid ? [linearByUuidPath(issue.uuid)] : []),
40
+ ];
41
+ let primaryPayload;
42
+ let lastError;
43
+ for (const path of candidates) {
44
+ try {
45
+ const payload = wrappedPayload((await mount.readFile(path)).content);
46
+ if (primaryPayload === undefined) {
47
+ primaryPayload = payload;
48
+ }
49
+ if (hasGuardFields(payload)) {
50
+ return payload;
51
+ }
52
+ }
53
+ catch (error) {
54
+ lastError = error;
55
+ }
56
+ }
57
+ // No record carried guard fields. Preserve prior behavior: return the primary
58
+ // payload (the scope guard then decides) rather than failing the read outright.
59
+ if (primaryPayload !== undefined) {
60
+ return primaryPayload;
61
+ }
62
+ throw new Error(`Refusing Linear writeback for ${issue.key}: unable to read guard fields` +
63
+ (lastError instanceof Error ? ` (${lastError.message})` : ''));
64
+ };
65
+ const createIssuePath = (payload) => {
66
+ const id = typeof payload.id === 'string' && payload.id ? payload.id : undefined;
67
+ if (id)
68
+ return `/linear/issues/factory-create-${safePathSegment(id)}.json`;
69
+ const identifier = typeof payload.identifier === 'string' && payload.identifier ? payload.identifier : undefined;
70
+ if (identifier && !looksLikeProviderIssueIdentifier(identifier)) {
71
+ return `/linear/issues/${safePathSegment(identifier)}.json`;
72
+ }
73
+ throw new Error('Linear createIssue payload must include a non-provider id/clientId');
74
+ };
75
+ const looksLikeProviderIssueIdentifier = (value) => /^[A-Z][A-Z0-9]*-/u.test(value);
76
+ const READBACK_CONFIRM_ATTEMPTS = 3;
77
+ const READBACK_CONFIRM_DELAY_MS = 250;
78
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
79
+ const confirmWriteback = async (mount, path, verify, logger) => {
80
+ await assertWritebackAcked(mount, path);
81
+ // getOp can return a FAKED success on a busy/wedged mount ("workspace write
82
+ // path is busy"), so the read-back is the source of truth. Retry to absorb
83
+ // eventual-consistency lag; if it never confirms, the write did NOT land —
84
+ // throw instead of silently faking success (which previously left issues
85
+ // un-advanced while the factory believed they had advanced).
86
+ for (let attempt = 0; attempt < READBACK_CONFIRM_ATTEMPTS; attempt += 1) {
87
+ let confirmed = false;
88
+ try {
89
+ confirmed = await verify();
90
+ }
91
+ catch {
92
+ confirmed = false;
93
+ }
94
+ if (confirmed) {
95
+ return;
96
+ }
97
+ if (attempt < READBACK_CONFIRM_ATTEMPTS - 1) {
98
+ logger.warn?.(`[factory-sdk] Linear writeback read-back for ${path} not yet confirmed (attempt ${attempt + 1}/${READBACK_CONFIRM_ATTEMPTS}); retrying`);
99
+ await delay(READBACK_CONFIRM_DELAY_MS);
100
+ }
101
+ }
102
+ throw new Error(`[factory-sdk] Linear writeback for ${path} acked but the read-back never confirmed it landed; the write did not propagate`);
103
+ };
104
+ const assertWritebackAcked = async (mount, path) => {
105
+ const confirmation = await mount.confirmWrite(path, { timeoutMs: 90_000 });
106
+ if (confirmation !== 'acked') {
107
+ throw new Error(`Writeback not acked for ${path}: ${confirmation}`);
108
+ }
109
+ };
110
+ export const MountLinearWriteback = (mount, configOrStateIds) => {
111
+ const safety = safetyFromConfig(configOrStateIds);
112
+ const logger = asRecord(configOrStateIds)?.logger ?? console;
113
+ const canonicalByPath = new Map();
114
+ const seedCanonical = (path, payload) => {
115
+ if (!payloadInFactoryScope(payload, safety))
116
+ return undefined;
117
+ const canonical = {
118
+ payload: { ...payload },
119
+ writable: createIssueWritePayload(payload),
120
+ };
121
+ canonicalByPath.set(path, canonical);
122
+ return canonical;
123
+ };
124
+ const rawIssuePayload = (issue) => wrappedPayload(issue.raw);
125
+ const canonicalForIssue = async (issue) => {
126
+ const path = issuePath(issue);
127
+ const rawPayload = rawIssuePayload(issue);
128
+ let canonical = canonicalByPath.get(path);
129
+ if (payloadInFactoryScope(rawPayload, safety)) {
130
+ canonical = seedCanonical(path, rawPayload);
131
+ }
132
+ else if (!canonical) {
133
+ throw new Error(`Refusing Linear writeback for ${issue.key}: missing title-bearing canonical issue payload`);
134
+ }
135
+ if (!canonical) {
136
+ throw new Error(`Refusing Linear writeback for ${issue.key}: missing title-bearing canonical issue payload`);
137
+ }
138
+ const livePayload = await readIssuePayloadForGuard(mount, issue);
139
+ if (payloadInFactoryScope(livePayload, safety)) {
140
+ seedCanonical(path, livePayload);
141
+ return canonical;
142
+ }
143
+ if (isStateOnlyDraft(livePayload)) {
144
+ const cached = canonicalByPath.get(path);
145
+ if (cached)
146
+ return cached;
147
+ throw new Error(`Refusing Linear writeback for ${issue.key}: missing title-bearing canonical issue payload`);
148
+ }
149
+ assertInFactoryScope(scopeIssueFromPayload(livePayload, issue.key), safety);
150
+ throw new Error(`Refusing Linear writeback for ${issue.key}: missing title-bearing canonical issue payload`);
151
+ };
152
+ const updateCanonicalState = (path, issue, canonical, stateId) => {
153
+ const payload = { ...canonical.payload, stateId };
154
+ canonicalByPath.set(path, {
155
+ payload,
156
+ writable: { ...canonical.writable, stateId },
157
+ });
158
+ issue.stateId = stateId;
159
+ const rawPayload = asRecord(issue.raw.payload);
160
+ issue.raw = rawPayload
161
+ ? { ...issue.raw, payload: { ...rawPayload, stateId } }
162
+ : { ...issue.raw, stateId };
163
+ };
164
+ const adapter = {
165
+ async setState(issue, stateId) {
166
+ const path = issuePath(issue);
167
+ const canonical = await canonicalForIssue(issue);
168
+ assertInFactoryScope(scopeIssueFromPayload(canonical.payload, issue.key), safety);
169
+ await mount.writeFile(path, {
170
+ ...canonical.writable,
171
+ stateId,
172
+ }, { guarded: true });
173
+ updateCanonicalState(path, issue, canonical, stateId);
174
+ await confirmWriteback(mount, path, () => verifyStateReadback(mount, issue, stateId), logger);
175
+ },
176
+ async postComment(issue, body) {
177
+ const canonical = await canonicalForIssue(issue);
178
+ assertInFactoryScope(scopeIssueFromPayload(canonical.payload, issue.key), safety);
179
+ const name = linearCommentName(issue, body);
180
+ const path = linearCommentPath(issuePath(issue), name);
181
+ await mount.writeFile(path, linearCommentPayload(issue, body), { guarded: true });
182
+ await confirmWriteback(mount, path, () => verifyCommentReadback(mount, issue, name), logger);
183
+ },
184
+ async createIssue(payload) {
185
+ assertInFactoryScope(scopeIssueFromPayload(payload, 'createIssue payload'), safety, 'createIssue payload');
186
+ const path = createIssuePath(payload);
187
+ seedCanonical(path, payload);
188
+ await mount.writeFile(path, createIssueDraftPayload(payload), { guarded: true });
189
+ await confirmWriteback(mount, path, async () => {
190
+ try {
191
+ const written = wrappedPayload((await mount.readFile(path)).content);
192
+ return payloadInFactoryScope(written, safety);
193
+ }
194
+ catch {
195
+ return false;
196
+ }
197
+ }, logger);
198
+ return { path };
199
+ },
200
+ async verify(issue, expect) {
201
+ if (expect.stateId) {
202
+ const path = issuePath(issue);
203
+ await assertWritebackAcked(mount, path);
204
+ return verifyStateReadback(mount, issue, expect.stateId);
205
+ }
206
+ if (expect.commentName) {
207
+ const path = linearCommentPath(issuePath(issue), expect.commentName);
208
+ await assertWritebackAcked(mount, path);
209
+ return verifyCommentReadback(mount, issue, expect.commentName);
210
+ }
211
+ return false;
212
+ },
213
+ };
214
+ return adapter;
215
+ };
216
+ const isStateOnlyDraft = (payload) => {
217
+ const keys = Object.keys(payload);
218
+ return keys.length === 1 && keys[0] === 'stateId' && typeof payload.stateId === 'string';
219
+ };
220
+ const verifyStateReadback = async (mount, issue, stateId) => {
221
+ try {
222
+ const { content } = await mount.readFile(issuePath(issue));
223
+ const payload = wrappedPayload(content);
224
+ return payload.stateId === stateId;
225
+ }
226
+ catch {
227
+ return false;
228
+ }
229
+ };
230
+ const verifyCommentReadback = async (mount, issue, commentName) => {
231
+ try {
232
+ const { content } = await mount.readFile(linearCommentPath(issuePath(issue), commentName));
233
+ const payload = wrappedPayload(content);
234
+ return payload.issueId === issue.uuid || payload.issue_id === issue.uuid;
235
+ }
236
+ catch {
237
+ return false;
238
+ }
239
+ };
240
+ const createIssueWritePayload = (payload) => {
241
+ const writable = {};
242
+ for (const key of [
243
+ 'title',
244
+ 'teamId',
245
+ 'stateId',
246
+ 'description',
247
+ 'priority',
248
+ 'assigneeId',
249
+ 'labelIds',
250
+ 'labels',
251
+ 'source',
252
+ 'parentId',
253
+ 'projectId',
254
+ 'estimate',
255
+ ]) {
256
+ const value = payload[key];
257
+ if (value !== undefined)
258
+ writable[key] = value;
259
+ }
260
+ const teamId = typeof payload.teamId === 'string'
261
+ ? payload.teamId
262
+ : typeof asRecord(payload.team)?.id === 'string'
263
+ ? asRecord(payload.team)?.id
264
+ : undefined;
265
+ if (teamId)
266
+ writable.teamId = teamId;
267
+ return writable;
268
+ };
269
+ // createIssue drafts (e.g. GitHub mirrors) only carry a team key, no teamId. The
270
+ // shared writable strips that, leaving the draft teamless. Re-attach the team
271
+ // object for creates only — existing issues already have their team assigned, so
272
+ // setState/postComment must not re-send it.
273
+ const createIssueDraftPayload = (payload) => {
274
+ const writable = createIssueWritePayload(payload);
275
+ if (writable.teamId === undefined && asRecord(payload.team)) {
276
+ writable.team = payload.team;
277
+ }
278
+ return writable;
279
+ };
280
+ const scopeIssueFromPayload = (payload, key) => ({
281
+ key,
282
+ title: typeof payload.title === 'string' ? payload.title : '',
283
+ team: typeof asRecord(payload.team)?.key === 'string' ? asRecord(payload.team)?.key : undefined,
284
+ raw: { payload },
285
+ });
286
+ //# sourceMappingURL=linear.js.map