@pome-sh/cli 0.1.0 → 0.2.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 (738) hide show
  1. package/README.md +5 -6
  2. package/dist/build-info.json +3 -3
  3. package/dist/src/cli/eval.js +26 -8
  4. package/dist/src/cli/init-sdk.d.ts +0 -3
  5. package/dist/src/cli/init-sdk.js +117 -24
  6. package/dist/src/cli/main.js +23 -69
  7. package/dist/src/cli/register.d.ts +9 -0
  8. package/dist/src/cli/register.js +51 -5
  9. package/dist/src/cli/session.d.ts +7 -1
  10. package/dist/src/cli/session.js +87 -27
  11. package/dist/src/demo/runDemo.js +3 -2
  12. package/dist/src/doctor/checks.js +2 -2
  13. package/dist/src/hosted/client.d.ts +41 -5
  14. package/dist/src/hosted/client.js +333 -19
  15. package/dist/src/hosted/errors.d.ts +11 -1
  16. package/dist/src/hosted/errors.js +8 -1
  17. package/dist/src/hosted/evalResultView.d.ts +2 -0
  18. package/dist/src/hosted/evalResultView.js +22 -0
  19. package/dist/src/hosted/uploadAndFinalize.d.ts +18 -1
  20. package/dist/src/hosted/uploadAndFinalize.js +51 -3
  21. package/dist/src/recorder/inspect.js +20 -2
  22. package/dist/src/runner/mergeAdapterSignals.js +2 -2
  23. package/dist/src/runner/ports.js +3 -0
  24. package/dist/src/runner/runScenario.d.ts +79 -2
  25. package/dist/src/runner/runScenario.js +94 -36
  26. package/dist/src/runner/runScenarioHosted.d.ts +37 -0
  27. package/dist/src/runner/runScenarioHosted.js +257 -58
  28. package/dist/src/runner/runTrialGroup.d.ts +1 -3
  29. package/dist/src/runner/runTrialGroup.js +1 -3
  30. package/dist/src/scenario/parseScenario.d.ts +5 -0
  31. package/dist/src/scenario/parseScenario.js +135 -8
  32. package/dist/src/scenario/scenarioSchema.d.ts +200 -3
  33. package/dist/src/scenario/scenarioSchema.js +12 -1
  34. package/dist/src/twin/twinHarness.d.ts +10 -0
  35. package/dist/src/twin/twinHarness.js +6 -2
  36. package/dist/src/twin/twinStart.d.ts +19 -0
  37. package/dist/src/twin/twinStart.js +156 -0
  38. package/node_modules/@pome-sh/sdk/dist/db.d.ts +4 -4
  39. package/node_modules/@pome-sh/sdk/dist/db.js +93 -25
  40. package/node_modules/@pome-sh/sdk/dist/db.js.map +1 -1
  41. package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.d.ts +121 -0
  42. package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.js +204 -0
  43. package/node_modules/@pome-sh/sdk/dist/fidelity-inventory.js.map +1 -0
  44. package/node_modules/@pome-sh/sdk/dist/parity.d.ts +55 -0
  45. package/node_modules/@pome-sh/sdk/dist/parity.js +112 -0
  46. package/node_modules/@pome-sh/sdk/dist/parity.js.map +1 -0
  47. package/node_modules/@pome-sh/sdk/dist/server-helpers.d.ts +15 -0
  48. package/node_modules/@pome-sh/sdk/dist/server-helpers.js +96 -0
  49. package/node_modules/@pome-sh/sdk/dist/server-helpers.js.map +1 -1
  50. package/node_modules/@pome-sh/sdk/dist/server.d.ts +1 -1
  51. package/node_modules/@pome-sh/sdk/dist/server.js +9 -7
  52. package/node_modules/@pome-sh/sdk/dist/server.js.map +1 -1
  53. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.d.ts +39 -0
  54. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.js +33 -0
  55. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/errors.js.map +1 -0
  56. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.d.ts +13 -0
  57. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.js +6 -0
  58. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/evaluator-hooks.js.map +1 -0
  59. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.d.ts +215 -0
  60. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js +83 -0
  61. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js.map +1 -0
  62. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.d.ts +175 -0
  63. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.js +182 -0
  64. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/github-access-control.js.map +1 -0
  65. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.d.ts +90 -0
  66. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.js +72 -0
  67. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/identity.js.map +1 -0
  68. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.d.ts +27 -0
  69. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.js +34 -0
  70. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/index.js.map +1 -0
  71. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +286 -0
  72. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.js +33 -0
  73. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/event-schema.js.map +1 -0
  74. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.d.ts +65 -0
  75. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js +455 -0
  76. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/data.js.map +1 -0
  77. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.d.ts +39 -0
  78. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.js +47 -0
  79. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/fixtures/index.js.map +1 -0
  80. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.d.ts +25 -0
  81. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.js +27 -0
  82. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/index.js.map +1 -0
  83. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.d.ts +100 -0
  84. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +185 -0
  85. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -0
  86. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.d.ts +64 -0
  87. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.js +112 -0
  88. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/map-span.js.map +1 -0
  89. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.d.ts +26 -0
  90. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.js +61 -0
  91. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/nano.js.map +1 -0
  92. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.d.ts +42 -0
  93. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.js +96 -0
  94. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/project.js.map +1 -0
  95. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.d.ts +51 -0
  96. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.js +74 -0
  97. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/semconv.js.map +1 -0
  98. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.d.ts +134 -0
  99. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.js +152 -0
  100. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/otel/span-event.js.map +1 -0
  101. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +418 -0
  102. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.js +234 -0
  103. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -0
  104. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.d.ts +2 -0
  105. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.js +152 -0
  106. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/redaction.js.map +1 -0
  107. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.d.ts +198 -0
  108. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.js +225 -0
  109. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/rest.js.map +1 -0
  110. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.d.ts +201 -0
  111. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.js +166 -0
  112. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/run.js.map +1 -0
  113. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.d.ts +446 -0
  114. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.js +167 -0
  115. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/seed-state.js.map +1 -0
  116. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.d.ts +53 -0
  117. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.js +51 -0
  118. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/sessions.js.map +1 -0
  119. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.d.ts +66 -0
  120. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.js +84 -0
  121. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task-vocab.js.map +1 -0
  122. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.d.ts +483 -0
  123. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.js +60 -0
  124. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/dist/task.js.map +1 -0
  125. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/package.json +54 -0
  126. package/node_modules/@pome-sh/sdk/node_modules/@pome-sh/shared-types/trace-contract.json +41 -0
  127. package/node_modules/@pome-sh/sdk/package.json +7 -7
  128. package/node_modules/@pome-sh/shared-types/dist/errors.d.ts +39 -0
  129. package/node_modules/@pome-sh/shared-types/dist/errors.js +33 -0
  130. package/node_modules/@pome-sh/shared-types/dist/errors.js.map +1 -0
  131. package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.d.ts +252 -0
  132. package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js +122 -0
  133. package/node_modules/@pome-sh/shared-types/dist/finalize-shapes.js.map +1 -0
  134. package/node_modules/@pome-sh/shared-types/dist/identity.d.ts +90 -0
  135. package/node_modules/@pome-sh/shared-types/dist/identity.js +72 -0
  136. package/node_modules/@pome-sh/shared-types/dist/identity.js.map +1 -0
  137. package/node_modules/@pome-sh/shared-types/dist/index.d.ts +13 -1353
  138. package/node_modules/@pome-sh/shared-types/dist/index.js +14 -568
  139. package/node_modules/@pome-sh/shared-types/dist/index.js.map +1 -1
  140. package/node_modules/@pome-sh/shared-types/dist/otel/event-schema.d.ts +45 -0
  141. package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js +8 -0
  142. package/node_modules/@pome-sh/shared-types/dist/otel/legacy-shim.js.map +1 -1
  143. package/node_modules/@pome-sh/shared-types/dist/otel/semconv.js +7 -0
  144. package/node_modules/@pome-sh/shared-types/dist/otel/semconv.js.map +1 -1
  145. package/node_modules/@pome-sh/shared-types/dist/recorder-events.d.ts +62 -0
  146. package/node_modules/@pome-sh/shared-types/dist/recorder-events.js +36 -0
  147. package/node_modules/@pome-sh/shared-types/dist/recorder-events.js.map +1 -1
  148. package/node_modules/@pome-sh/shared-types/dist/rest.d.ts +245 -0
  149. package/node_modules/@pome-sh/shared-types/dist/rest.js +303 -0
  150. package/node_modules/@pome-sh/shared-types/dist/rest.js.map +1 -0
  151. package/node_modules/@pome-sh/shared-types/dist/run.d.ts +7 -0
  152. package/node_modules/@pome-sh/shared-types/dist/run.js +4 -0
  153. package/node_modules/@pome-sh/shared-types/dist/run.js.map +1 -1
  154. package/node_modules/@pome-sh/shared-types/dist/seed-envelope.d.ts +14 -0
  155. package/node_modules/@pome-sh/shared-types/dist/seed-envelope.js +46 -0
  156. package/node_modules/@pome-sh/shared-types/dist/seed-envelope.js.map +1 -0
  157. package/node_modules/@pome-sh/shared-types/dist/seed-state.d.ts +446 -0
  158. package/node_modules/@pome-sh/shared-types/dist/seed-state.js +167 -0
  159. package/node_modules/@pome-sh/shared-types/dist/seed-state.js.map +1 -0
  160. package/node_modules/@pome-sh/shared-types/dist/sessions.d.ts +53 -0
  161. package/node_modules/@pome-sh/shared-types/dist/sessions.js +51 -0
  162. package/node_modules/@pome-sh/shared-types/dist/sessions.js.map +1 -0
  163. package/node_modules/@pome-sh/shared-types/dist/task.d.ts +485 -0
  164. package/node_modules/@pome-sh/shared-types/dist/task.js +60 -0
  165. package/node_modules/@pome-sh/shared-types/dist/task.js.map +1 -0
  166. package/node_modules/@pome-sh/shared-types/package.json +2 -1
  167. package/node_modules/@pome-sh/shared-types/trace-contract.json +1 -1
  168. package/node_modules/@pome-sh/twin-github/FIDELITY.md +127 -67
  169. package/node_modules/@pome-sh/twin-github/README.md +5 -8
  170. package/node_modules/@pome-sh/twin-github/dist/examples/claude-github-agent.js +4 -20
  171. package/node_modules/@pome-sh/twin-github/dist/examples/claude-github-agent.js.map +1 -1
  172. package/node_modules/@pome-sh/twin-github/dist/src/access-control.d.ts +3 -3
  173. package/node_modules/@pome-sh/twin-github/dist/src/domain.d.ts +141 -3
  174. package/node_modules/@pome-sh/twin-github/dist/src/domain.js +0 -0
  175. package/node_modules/@pome-sh/twin-github/dist/src/domain.js.map +1 -1
  176. package/node_modules/@pome-sh/twin-github/dist/src/routes.js +2 -0
  177. package/node_modules/@pome-sh/twin-github/dist/src/routes.js.map +1 -1
  178. package/node_modules/@pome-sh/twin-github/dist/src/seed.d.ts +1 -1
  179. package/node_modules/@pome-sh/twin-github/dist/src/serializers.d.ts +2 -2
  180. package/node_modules/@pome-sh/twin-github/dist/src/server.js +7 -6
  181. package/node_modules/@pome-sh/twin-github/dist/src/server.js.map +1 -1
  182. package/node_modules/@pome-sh/twin-github/dist/src/tools.d.ts +168 -18
  183. package/node_modules/@pome-sh/twin-github/dist/src/tools.js +23 -0
  184. package/node_modules/@pome-sh/twin-github/dist/src/tools.js.map +1 -1
  185. package/node_modules/@pome-sh/twin-github/fidelity.inventory.json +773 -0
  186. package/node_modules/@pome-sh/twin-github/package.json +5 -5
  187. package/node_modules/@pome-sh/twin-slack/FIDELITY.md +121 -35
  188. package/node_modules/@pome-sh/twin-slack/README.md +2 -6
  189. package/node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.js +92 -0
  190. package/node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.js.map +1 -0
  191. package/node_modules/@pome-sh/twin-slack/dist/scripts/smoke.js +3 -3
  192. package/node_modules/@pome-sh/twin-slack/dist/scripts/smoke.js.map +1 -1
  193. package/node_modules/@pome-sh/twin-slack/dist/scripts/validate-mcp.js +1 -1
  194. package/node_modules/@pome-sh/twin-slack/dist/scripts/validate-mcp.js.map +1 -1
  195. package/node_modules/@pome-sh/twin-slack/dist/src/server.js +7 -6
  196. package/node_modules/@pome-sh/twin-slack/dist/src/server.js.map +1 -1
  197. package/node_modules/@pome-sh/twin-slack/dist/src/tools.d.ts +30 -4
  198. package/node_modules/@pome-sh/twin-slack/dist/src/tools.js +48 -1
  199. package/node_modules/@pome-sh/twin-slack/dist/src/tools.js.map +1 -1
  200. package/node_modules/@pome-sh/twin-slack/fidelity.inventory.json +377 -0
  201. package/node_modules/@pome-sh/twin-slack/package.json +5 -5
  202. package/node_modules/@pome-sh/twin-stripe/FIDELITY.md +151 -50
  203. package/node_modules/@pome-sh/twin-stripe/README.md +44 -47
  204. package/node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.js +186 -0
  205. package/node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.js.map +1 -0
  206. package/node_modules/@pome-sh/twin-stripe/dist/scripts/smoke.js +8 -4
  207. package/node_modules/@pome-sh/twin-stripe/dist/scripts/smoke.js.map +1 -1
  208. package/node_modules/@pome-sh/twin-stripe/dist/src/db.js +26 -2
  209. package/node_modules/@pome-sh/twin-stripe/dist/src/db.js.map +1 -1
  210. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.d.ts +101 -0
  211. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.js +195 -0
  212. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/billing.js.map +1 -0
  213. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.d.ts +15 -1
  214. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.js +17 -5
  215. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/charges.js.map +1 -1
  216. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.d.ts +47 -0
  217. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.js +103 -0
  218. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/customers.js.map +1 -0
  219. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/events.d.ts +1 -1
  220. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/events.js.map +1 -1
  221. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.d.ts +6 -0
  222. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.js +3 -0
  223. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/index.js.map +1 -1
  224. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.d.ts +61 -2
  225. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.js +214 -52
  226. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-intents.js.map +1 -1
  227. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.d.ts +45 -0
  228. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.js +159 -0
  229. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/payment-methods.js.map +1 -0
  230. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.d.ts +6 -0
  231. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.js +16 -0
  232. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/refunds.js.map +1 -1
  233. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.d.ts +1 -0
  234. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.js +144 -1
  235. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/schema.js.map +1 -1
  236. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.d.ts +420 -43
  237. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.js +442 -13
  238. package/node_modules/@pome-sh/twin-stripe/dist/src/domain/stripe-domain.js.map +1 -1
  239. package/node_modules/@pome-sh/twin-stripe/dist/src/errors.d.ts +19 -0
  240. package/node_modules/@pome-sh/twin-stripe/dist/src/errors.js +15 -1
  241. package/node_modules/@pome-sh/twin-stripe/dist/src/errors.js.map +1 -1
  242. package/node_modules/@pome-sh/twin-stripe/dist/src/ids.d.ts +2 -0
  243. package/node_modules/@pome-sh/twin-stripe/dist/src/ids.js +2 -0
  244. package/node_modules/@pome-sh/twin-stripe/dist/src/ids.js.map +1 -1
  245. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.d.ts +8 -0
  246. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js +105 -1
  247. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/_helpers.js.map +1 -1
  248. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.d.ts +4 -0
  249. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.js +117 -0
  250. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/billing.js.map +1 -0
  251. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.d.ts +4 -0
  252. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.js +47 -0
  253. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/customers.js.map +1 -0
  254. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/index.js +6 -0
  255. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/index.js.map +1 -1
  256. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-intents.js +28 -102
  257. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-intents.js.map +1 -1
  258. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.d.ts +4 -0
  259. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.js +33 -0
  260. package/node_modules/@pome-sh/twin-stripe/dist/src/routes/payment-methods.js.map +1 -0
  261. package/node_modules/@pome-sh/twin-stripe/dist/src/seed.d.ts +10 -8
  262. package/node_modules/@pome-sh/twin-stripe/dist/src/seed.js +3 -2
  263. package/node_modules/@pome-sh/twin-stripe/dist/src/seed.js.map +1 -1
  264. package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.d.ts +181 -21
  265. package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.js +257 -34
  266. package/node_modules/@pome-sh/twin-stripe/dist/src/serializers.js.map +1 -1
  267. package/node_modules/@pome-sh/twin-stripe/dist/src/server.js +7 -6
  268. package/node_modules/@pome-sh/twin-stripe/dist/src/server.js.map +1 -1
  269. package/node_modules/@pome-sh/twin-stripe/dist/src/tools.d.ts +107 -5
  270. package/node_modules/@pome-sh/twin-stripe/dist/src/tools.js +139 -6
  271. package/node_modules/@pome-sh/twin-stripe/dist/src/tools.js.map +1 -1
  272. package/node_modules/@pome-sh/twin-stripe/dist/src/types.d.ts +73 -0
  273. package/node_modules/@pome-sh/twin-stripe/dist/src/upstream-types.d.ts +8 -0
  274. package/node_modules/@pome-sh/twin-stripe/dist/src/x402.js +6 -1
  275. package/node_modules/@pome-sh/twin-stripe/dist/src/x402.js.map +1 -1
  276. package/node_modules/@pome-sh/twin-stripe/fidelity.inventory.json +425 -0
  277. package/node_modules/@pome-sh/twin-stripe/package.json +5 -5
  278. package/package.json +9 -10
  279. package/skills/pome-setup/SKILL.md +1 -1
  280. package/dist/src/recorder/verdictArtifact.d.ts +0 -71
  281. package/dist/src/recorder/verdictArtifact.js +0 -207
  282. package/dist/src/score/view.d.ts +0 -38
  283. package/dist/src/score/view.js +0 -59
  284. package/dist/src/twin/github/app.d.ts +0 -10
  285. package/dist/src/twin/github/app.js +0 -316
  286. package/dist/src/twin/github/db/migrations.d.ts +0 -2
  287. package/dist/src/twin/github/db/migrations.js +0 -64
  288. package/dist/src/twin/github/db/openDatabase.d.ts +0 -2
  289. package/dist/src/twin/github/db/openDatabase.js +0 -13
  290. package/dist/src/twin/github/db/queries.d.ts +0 -77
  291. package/dist/src/twin/github/db/queries.js +0 -93
  292. package/dist/src/twin/github/domain/seed.d.ts +0 -74
  293. package/dist/src/twin/github/domain/seed.js +0 -104
  294. package/dist/src/twin/github/domain/serializers.d.ts +0 -113
  295. package/dist/src/twin/github/domain/serializers.js +0 -76
  296. package/dist/src/twin/github/types.d.ts +0 -66
  297. package/dist/src/twin/github/types.js +0 -1
  298. package/dist/src/twin-github/app.d.ts +0 -9
  299. package/dist/src/twin-github/app.js +0 -327
  300. package/dist/src/twin-github/auth.d.ts +0 -16
  301. package/dist/src/twin-github/auth.js +0 -142
  302. package/dist/src/twin-github/build-info.d.ts +0 -7
  303. package/dist/src/twin-github/build-info.js +0 -52
  304. package/dist/src/twin-github/db.d.ts +0 -4
  305. package/dist/src/twin-github/db.js +0 -362
  306. package/dist/src/twin-github/domain.d.ts +0 -1649
  307. package/dist/src/twin-github/domain.js +0 -957
  308. package/dist/src/twin-github/errors.d.ts +0 -14
  309. package/dist/src/twin-github/errors.js +0 -34
  310. package/dist/src/twin-github/index.d.ts +0 -9
  311. package/dist/src/twin-github/index.js +0 -7
  312. package/dist/src/twin-github/mcp.d.ts +0 -17
  313. package/dist/src/twin-github/mcp.js +0 -281
  314. package/dist/src/twin-github/recorder.d.ts +0 -2
  315. package/dist/src/twin-github/recorder.js +0 -29
  316. package/dist/src/twin-github/seed.d.ts +0 -74
  317. package/dist/src/twin-github/seed.js +0 -107
  318. package/dist/src/twin-github/serializers.d.ts +0 -449
  319. package/dist/src/twin-github/serializers.js +0 -429
  320. package/dist/src/twin-github/server.js +0 -34
  321. package/dist/src/twin-github/tools.d.ts +0 -1545
  322. package/dist/src/twin-github/tools.js +0 -347
  323. package/dist/src/twin-github/types.d.ts +0 -198
  324. package/dist/src/twin-github/unsupported-envelope.d.ts +0 -10
  325. package/dist/src/twin-github/unsupported-envelope.js +0 -29
  326. package/dist/src/twin-github/util.d.ts +0 -13
  327. package/dist/src/twin-github/util.js +0 -61
  328. package/dist/src/twin-slack/app.d.ts +0 -17
  329. package/dist/src/twin-slack/app.js +0 -683
  330. package/dist/src/twin-slack/auth.d.ts +0 -38
  331. package/dist/src/twin-slack/auth.js +0 -219
  332. package/dist/src/twin-slack/build-info.d.ts +0 -7
  333. package/dist/src/twin-slack/build-info.js +0 -9
  334. package/dist/src/twin-slack/db.d.ts +0 -4
  335. package/dist/src/twin-slack/db.js +0 -213
  336. package/dist/src/twin-slack/domain.d.ts +0 -236
  337. package/dist/src/twin-slack/domain.js +0 -1293
  338. package/dist/src/twin-slack/errors.d.ts +0 -16
  339. package/dist/src/twin-slack/errors.js +0 -60
  340. package/dist/src/twin-slack/index.d.ts +0 -8
  341. package/dist/src/twin-slack/index.js +0 -8
  342. package/dist/src/twin-slack/mcp.d.ts +0 -17
  343. package/dist/src/twin-slack/mcp.js +0 -226
  344. package/dist/src/twin-slack/recorder.d.ts +0 -2
  345. package/dist/src/twin-slack/recorder.js +0 -15
  346. package/dist/src/twin-slack/seed.d.ts +0 -49
  347. package/dist/src/twin-slack/seed.js +0 -139
  348. package/dist/src/twin-slack/serializers.d.ts +0 -141
  349. package/dist/src/twin-slack/serializers.js +0 -377
  350. package/dist/src/twin-slack/tools.d.ts +0 -97
  351. package/dist/src/twin-slack/tools.js +0 -171
  352. package/dist/src/twin-slack/types.d.ts +0 -164
  353. package/dist/src/twin-slack/types.js +0 -1
  354. package/dist/src/twin-slack/unsupported-envelope.d.ts +0 -12
  355. package/dist/src/twin-slack/unsupported-envelope.js +0 -61
  356. package/dist/src/twin-slack/upstream-types.d.ts +0 -19
  357. package/dist/src/twin-slack/upstream-types.js +0 -1
  358. package/dist/src/twin-slack/util.d.ts +0 -31
  359. package/dist/src/twin-slack/util.js +0 -136
  360. package/node_modules/base64-js/LICENSE +0 -21
  361. package/node_modules/base64-js/README.md +0 -34
  362. package/node_modules/base64-js/base64js.min.js +0 -1
  363. package/node_modules/base64-js/index.d.ts +0 -3
  364. package/node_modules/base64-js/index.js +0 -150
  365. package/node_modules/base64-js/package.json +0 -47
  366. package/node_modules/better-sqlite3/LICENSE +0 -21
  367. package/node_modules/better-sqlite3/README.md +0 -99
  368. package/node_modules/better-sqlite3/binding.gyp +0 -38
  369. package/node_modules/better-sqlite3/build/Release/better_sqlite3.node +0 -0
  370. package/node_modules/better-sqlite3/deps/common.gypi +0 -68
  371. package/node_modules/better-sqlite3/deps/copy.js +0 -31
  372. package/node_modules/better-sqlite3/deps/defines.gypi +0 -42
  373. package/node_modules/better-sqlite3/deps/download.sh +0 -123
  374. package/node_modules/better-sqlite3/deps/patches/1208.patch +0 -15
  375. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.c +0 -269395
  376. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3.h +0 -14347
  377. package/node_modules/better-sqlite3/deps/sqlite3/sqlite3ext.h +0 -739
  378. package/node_modules/better-sqlite3/deps/sqlite3.gyp +0 -80
  379. package/node_modules/better-sqlite3/deps/test_extension.c +0 -21
  380. package/node_modules/better-sqlite3/lib/database.js +0 -90
  381. package/node_modules/better-sqlite3/lib/index.js +0 -3
  382. package/node_modules/better-sqlite3/lib/methods/aggregate.js +0 -43
  383. package/node_modules/better-sqlite3/lib/methods/backup.js +0 -67
  384. package/node_modules/better-sqlite3/lib/methods/function.js +0 -31
  385. package/node_modules/better-sqlite3/lib/methods/inspect.js +0 -7
  386. package/node_modules/better-sqlite3/lib/methods/pragma.js +0 -12
  387. package/node_modules/better-sqlite3/lib/methods/serialize.js +0 -16
  388. package/node_modules/better-sqlite3/lib/methods/table.js +0 -189
  389. package/node_modules/better-sqlite3/lib/methods/transaction.js +0 -78
  390. package/node_modules/better-sqlite3/lib/methods/wrappers.js +0 -54
  391. package/node_modules/better-sqlite3/lib/sqlite-error.js +0 -20
  392. package/node_modules/better-sqlite3/lib/util.js +0 -12
  393. package/node_modules/better-sqlite3/package.json +0 -59
  394. package/node_modules/better-sqlite3/src/addon.cpp +0 -47
  395. package/node_modules/better-sqlite3/src/better_sqlite3.cpp +0 -74
  396. package/node_modules/better-sqlite3/src/objects/backup.cpp +0 -120
  397. package/node_modules/better-sqlite3/src/objects/backup.hpp +0 -36
  398. package/node_modules/better-sqlite3/src/objects/database.cpp +0 -417
  399. package/node_modules/better-sqlite3/src/objects/database.hpp +0 -103
  400. package/node_modules/better-sqlite3/src/objects/statement-iterator.cpp +0 -113
  401. package/node_modules/better-sqlite3/src/objects/statement-iterator.hpp +0 -50
  402. package/node_modules/better-sqlite3/src/objects/statement.cpp +0 -383
  403. package/node_modules/better-sqlite3/src/objects/statement.hpp +0 -58
  404. package/node_modules/better-sqlite3/src/util/bind-map.cpp +0 -73
  405. package/node_modules/better-sqlite3/src/util/binder.cpp +0 -193
  406. package/node_modules/better-sqlite3/src/util/constants.cpp +0 -172
  407. package/node_modules/better-sqlite3/src/util/custom-aggregate.cpp +0 -121
  408. package/node_modules/better-sqlite3/src/util/custom-function.cpp +0 -59
  409. package/node_modules/better-sqlite3/src/util/custom-table.cpp +0 -409
  410. package/node_modules/better-sqlite3/src/util/data-converter.cpp +0 -17
  411. package/node_modules/better-sqlite3/src/util/data.cpp +0 -194
  412. package/node_modules/better-sqlite3/src/util/helpers.cpp +0 -109
  413. package/node_modules/better-sqlite3/src/util/macros.cpp +0 -90
  414. package/node_modules/better-sqlite3/src/util/query-macros.cpp +0 -71
  415. package/node_modules/better-sqlite3/src/util/row-builder.cpp +0 -49
  416. package/node_modules/bindings/LICENSE.md +0 -22
  417. package/node_modules/bindings/README.md +0 -98
  418. package/node_modules/bindings/bindings.js +0 -221
  419. package/node_modules/bindings/package.json +0 -28
  420. package/node_modules/bl/.travis.yml +0 -17
  421. package/node_modules/bl/BufferList.js +0 -396
  422. package/node_modules/bl/LICENSE.md +0 -13
  423. package/node_modules/bl/README.md +0 -247
  424. package/node_modules/bl/bl.js +0 -84
  425. package/node_modules/bl/package.json +0 -37
  426. package/node_modules/bl/test/convert.js +0 -21
  427. package/node_modules/bl/test/indexOf.js +0 -492
  428. package/node_modules/bl/test/isBufferList.js +0 -32
  429. package/node_modules/bl/test/test.js +0 -869
  430. package/node_modules/buffer/AUTHORS.md +0 -70
  431. package/node_modules/buffer/LICENSE +0 -21
  432. package/node_modules/buffer/README.md +0 -410
  433. package/node_modules/buffer/index.d.ts +0 -186
  434. package/node_modules/buffer/index.js +0 -1817
  435. package/node_modules/buffer/package.json +0 -96
  436. package/node_modules/chownr/LICENSE +0 -15
  437. package/node_modules/chownr/README.md +0 -3
  438. package/node_modules/chownr/chownr.js +0 -167
  439. package/node_modules/chownr/package.json +0 -29
  440. package/node_modules/decompress-response/index.d.ts +0 -22
  441. package/node_modules/decompress-response/index.js +0 -58
  442. package/node_modules/decompress-response/license +0 -9
  443. package/node_modules/decompress-response/package.json +0 -56
  444. package/node_modules/decompress-response/readme.md +0 -48
  445. package/node_modules/deep-extend/CHANGELOG.md +0 -46
  446. package/node_modules/deep-extend/LICENSE +0 -20
  447. package/node_modules/deep-extend/README.md +0 -91
  448. package/node_modules/deep-extend/index.js +0 -1
  449. package/node_modules/deep-extend/lib/deep-extend.js +0 -150
  450. package/node_modules/deep-extend/package.json +0 -62
  451. package/node_modules/detect-libc/LICENSE +0 -201
  452. package/node_modules/detect-libc/README.md +0 -163
  453. package/node_modules/detect-libc/index.d.ts +0 -14
  454. package/node_modules/detect-libc/lib/detect-libc.js +0 -313
  455. package/node_modules/detect-libc/lib/elf.js +0 -39
  456. package/node_modules/detect-libc/lib/filesystem.js +0 -51
  457. package/node_modules/detect-libc/lib/process.js +0 -24
  458. package/node_modules/detect-libc/package.json +0 -44
  459. package/node_modules/end-of-stream/LICENSE +0 -21
  460. package/node_modules/end-of-stream/README.md +0 -54
  461. package/node_modules/end-of-stream/index.js +0 -96
  462. package/node_modules/end-of-stream/package.json +0 -37
  463. package/node_modules/expand-template/.travis.yml +0 -6
  464. package/node_modules/expand-template/LICENSE +0 -21
  465. package/node_modules/expand-template/README.md +0 -43
  466. package/node_modules/expand-template/index.js +0 -26
  467. package/node_modules/expand-template/package.json +0 -29
  468. package/node_modules/expand-template/test.js +0 -67
  469. package/node_modules/file-uri-to-path/.npmignore +0 -1
  470. package/node_modules/file-uri-to-path/.travis.yml +0 -30
  471. package/node_modules/file-uri-to-path/History.md +0 -21
  472. package/node_modules/file-uri-to-path/LICENSE +0 -20
  473. package/node_modules/file-uri-to-path/README.md +0 -74
  474. package/node_modules/file-uri-to-path/index.d.ts +0 -2
  475. package/node_modules/file-uri-to-path/index.js +0 -66
  476. package/node_modules/file-uri-to-path/package.json +0 -32
  477. package/node_modules/file-uri-to-path/test/test.js +0 -24
  478. package/node_modules/file-uri-to-path/test/tests.json +0 -13
  479. package/node_modules/fs-constants/LICENSE +0 -21
  480. package/node_modules/fs-constants/README.md +0 -26
  481. package/node_modules/fs-constants/browser.js +0 -1
  482. package/node_modules/fs-constants/index.js +0 -1
  483. package/node_modules/fs-constants/package.json +0 -19
  484. package/node_modules/github-from-package/.travis.yml +0 -4
  485. package/node_modules/github-from-package/LICENSE +0 -18
  486. package/node_modules/github-from-package/example/package.json +0 -8
  487. package/node_modules/github-from-package/example/url.js +0 -3
  488. package/node_modules/github-from-package/index.js +0 -17
  489. package/node_modules/github-from-package/package.json +0 -30
  490. package/node_modules/github-from-package/readme.markdown +0 -53
  491. package/node_modules/github-from-package/test/a.json +0 -8
  492. package/node_modules/github-from-package/test/b.json +0 -5
  493. package/node_modules/github-from-package/test/c.json +0 -5
  494. package/node_modules/github-from-package/test/d.json +0 -7
  495. package/node_modules/github-from-package/test/e.json +0 -5
  496. package/node_modules/github-from-package/test/url.js +0 -19
  497. package/node_modules/ieee754/LICENSE +0 -11
  498. package/node_modules/ieee754/README.md +0 -51
  499. package/node_modules/ieee754/index.d.ts +0 -10
  500. package/node_modules/ieee754/index.js +0 -85
  501. package/node_modules/ieee754/package.json +0 -52
  502. package/node_modules/inherits/LICENSE +0 -16
  503. package/node_modules/inherits/README.md +0 -42
  504. package/node_modules/inherits/inherits.js +0 -9
  505. package/node_modules/inherits/inherits_browser.js +0 -27
  506. package/node_modules/inherits/package.json +0 -29
  507. package/node_modules/ini/LICENSE +0 -15
  508. package/node_modules/ini/README.md +0 -102
  509. package/node_modules/ini/ini.js +0 -206
  510. package/node_modules/ini/package.json +0 -33
  511. package/node_modules/mimic-response/index.d.ts +0 -17
  512. package/node_modules/mimic-response/index.js +0 -77
  513. package/node_modules/mimic-response/license +0 -9
  514. package/node_modules/mimic-response/package.json +0 -42
  515. package/node_modules/mimic-response/readme.md +0 -78
  516. package/node_modules/minimist/.eslintrc +0 -29
  517. package/node_modules/minimist/.github/FUNDING.yml +0 -12
  518. package/node_modules/minimist/.nycrc +0 -14
  519. package/node_modules/minimist/CHANGELOG.md +0 -298
  520. package/node_modules/minimist/LICENSE +0 -18
  521. package/node_modules/minimist/README.md +0 -121
  522. package/node_modules/minimist/example/parse.js +0 -4
  523. package/node_modules/minimist/index.js +0 -263
  524. package/node_modules/minimist/package.json +0 -75
  525. package/node_modules/minimist/test/all_bool.js +0 -34
  526. package/node_modules/minimist/test/bool.js +0 -177
  527. package/node_modules/minimist/test/dash.js +0 -43
  528. package/node_modules/minimist/test/default_bool.js +0 -37
  529. package/node_modules/minimist/test/dotted.js +0 -24
  530. package/node_modules/minimist/test/kv_short.js +0 -32
  531. package/node_modules/minimist/test/long.js +0 -33
  532. package/node_modules/minimist/test/num.js +0 -38
  533. package/node_modules/minimist/test/parse.js +0 -209
  534. package/node_modules/minimist/test/parse_modified.js +0 -11
  535. package/node_modules/minimist/test/proto.js +0 -64
  536. package/node_modules/minimist/test/short.js +0 -69
  537. package/node_modules/minimist/test/stop_early.js +0 -17
  538. package/node_modules/minimist/test/unknown.js +0 -104
  539. package/node_modules/minimist/test/whitespace.js +0 -10
  540. package/node_modules/mkdirp-classic/LICENSE +0 -21
  541. package/node_modules/mkdirp-classic/README.md +0 -18
  542. package/node_modules/mkdirp-classic/index.js +0 -98
  543. package/node_modules/mkdirp-classic/package.json +0 -18
  544. package/node_modules/napi-build-utils/.github/workflows/run-npm-tests.yml +0 -31
  545. package/node_modules/napi-build-utils/LICENSE +0 -21
  546. package/node_modules/napi-build-utils/README.md +0 -52
  547. package/node_modules/napi-build-utils/index.js +0 -214
  548. package/node_modules/napi-build-utils/index.md +0 -0
  549. package/node_modules/napi-build-utils/package.json +0 -42
  550. package/node_modules/node-abi/LICENSE +0 -21
  551. package/node_modules/node-abi/README.md +0 -54
  552. package/node_modules/node-abi/abi_registry.json +0 -453
  553. package/node_modules/node-abi/index.js +0 -179
  554. package/node_modules/node-abi/package.json +0 -45
  555. package/node_modules/once/LICENSE +0 -15
  556. package/node_modules/once/README.md +0 -79
  557. package/node_modules/once/once.js +0 -42
  558. package/node_modules/once/package.json +0 -33
  559. package/node_modules/prebuild-install/CHANGELOG.md +0 -131
  560. package/node_modules/prebuild-install/CONTRIBUTING.md +0 -6
  561. package/node_modules/prebuild-install/LICENSE +0 -21
  562. package/node_modules/prebuild-install/README.md +0 -163
  563. package/node_modules/prebuild-install/asset.js +0 -44
  564. package/node_modules/prebuild-install/bin.js +0 -78
  565. package/node_modules/prebuild-install/download.js +0 -142
  566. package/node_modules/prebuild-install/error.js +0 -14
  567. package/node_modules/prebuild-install/help.txt +0 -16
  568. package/node_modules/prebuild-install/index.js +0 -1
  569. package/node_modules/prebuild-install/log.js +0 -33
  570. package/node_modules/prebuild-install/package.json +0 -67
  571. package/node_modules/prebuild-install/proxy.js +0 -35
  572. package/node_modules/prebuild-install/rc.js +0 -64
  573. package/node_modules/prebuild-install/util.js +0 -143
  574. package/node_modules/pump/.github/FUNDING.yml +0 -2
  575. package/node_modules/pump/.travis.yml +0 -5
  576. package/node_modules/pump/LICENSE +0 -21
  577. package/node_modules/pump/README.md +0 -74
  578. package/node_modules/pump/SECURITY.md +0 -5
  579. package/node_modules/pump/empty.js +0 -1
  580. package/node_modules/pump/index.js +0 -86
  581. package/node_modules/pump/package.json +0 -30
  582. package/node_modules/pump/test-browser.js +0 -66
  583. package/node_modules/pump/test-node.js +0 -53
  584. package/node_modules/rc/LICENSE.APACHE2 +0 -15
  585. package/node_modules/rc/LICENSE.BSD +0 -26
  586. package/node_modules/rc/LICENSE.MIT +0 -24
  587. package/node_modules/rc/README.md +0 -227
  588. package/node_modules/rc/browser.js +0 -7
  589. package/node_modules/rc/cli.js +0 -4
  590. package/node_modules/rc/index.js +0 -53
  591. package/node_modules/rc/lib/utils.js +0 -104
  592. package/node_modules/rc/package.json +0 -29
  593. package/node_modules/rc/test/ini.js +0 -16
  594. package/node_modules/rc/test/nested-env-vars.js +0 -50
  595. package/node_modules/rc/test/test.js +0 -59
  596. package/node_modules/readable-stream/CONTRIBUTING.md +0 -38
  597. package/node_modules/readable-stream/GOVERNANCE.md +0 -136
  598. package/node_modules/readable-stream/LICENSE +0 -47
  599. package/node_modules/readable-stream/README.md +0 -106
  600. package/node_modules/readable-stream/errors-browser.js +0 -127
  601. package/node_modules/readable-stream/errors.js +0 -116
  602. package/node_modules/readable-stream/experimentalWarning.js +0 -17
  603. package/node_modules/readable-stream/lib/_stream_duplex.js +0 -126
  604. package/node_modules/readable-stream/lib/_stream_passthrough.js +0 -37
  605. package/node_modules/readable-stream/lib/_stream_readable.js +0 -1027
  606. package/node_modules/readable-stream/lib/_stream_transform.js +0 -190
  607. package/node_modules/readable-stream/lib/_stream_writable.js +0 -641
  608. package/node_modules/readable-stream/lib/internal/streams/async_iterator.js +0 -180
  609. package/node_modules/readable-stream/lib/internal/streams/buffer_list.js +0 -183
  610. package/node_modules/readable-stream/lib/internal/streams/destroy.js +0 -96
  611. package/node_modules/readable-stream/lib/internal/streams/end-of-stream.js +0 -86
  612. package/node_modules/readable-stream/lib/internal/streams/from-browser.js +0 -3
  613. package/node_modules/readable-stream/lib/internal/streams/from.js +0 -52
  614. package/node_modules/readable-stream/lib/internal/streams/pipeline.js +0 -86
  615. package/node_modules/readable-stream/lib/internal/streams/state.js +0 -22
  616. package/node_modules/readable-stream/lib/internal/streams/stream-browser.js +0 -1
  617. package/node_modules/readable-stream/lib/internal/streams/stream.js +0 -1
  618. package/node_modules/readable-stream/package.json +0 -68
  619. package/node_modules/readable-stream/readable-browser.js +0 -9
  620. package/node_modules/readable-stream/readable.js +0 -16
  621. package/node_modules/safe-buffer/LICENSE +0 -21
  622. package/node_modules/safe-buffer/README.md +0 -584
  623. package/node_modules/safe-buffer/index.d.ts +0 -187
  624. package/node_modules/safe-buffer/index.js +0 -65
  625. package/node_modules/safe-buffer/package.json +0 -51
  626. package/node_modules/semver/LICENSE +0 -15
  627. package/node_modules/semver/README.md +0 -680
  628. package/node_modules/semver/bin/semver.js +0 -195
  629. package/node_modules/semver/classes/comparator.js +0 -143
  630. package/node_modules/semver/classes/index.js +0 -7
  631. package/node_modules/semver/classes/range.js +0 -577
  632. package/node_modules/semver/classes/semver.js +0 -350
  633. package/node_modules/semver/functions/clean.js +0 -8
  634. package/node_modules/semver/functions/cmp.js +0 -54
  635. package/node_modules/semver/functions/coerce.js +0 -62
  636. package/node_modules/semver/functions/compare-build.js +0 -9
  637. package/node_modules/semver/functions/compare-loose.js +0 -5
  638. package/node_modules/semver/functions/compare.js +0 -7
  639. package/node_modules/semver/functions/diff.js +0 -60
  640. package/node_modules/semver/functions/eq.js +0 -5
  641. package/node_modules/semver/functions/gt.js +0 -5
  642. package/node_modules/semver/functions/gte.js +0 -5
  643. package/node_modules/semver/functions/inc.js +0 -21
  644. package/node_modules/semver/functions/lt.js +0 -5
  645. package/node_modules/semver/functions/lte.js +0 -5
  646. package/node_modules/semver/functions/major.js +0 -5
  647. package/node_modules/semver/functions/minor.js +0 -5
  648. package/node_modules/semver/functions/neq.js +0 -5
  649. package/node_modules/semver/functions/parse.js +0 -18
  650. package/node_modules/semver/functions/patch.js +0 -5
  651. package/node_modules/semver/functions/prerelease.js +0 -8
  652. package/node_modules/semver/functions/rcompare.js +0 -5
  653. package/node_modules/semver/functions/rsort.js +0 -5
  654. package/node_modules/semver/functions/satisfies.js +0 -12
  655. package/node_modules/semver/functions/sort.js +0 -5
  656. package/node_modules/semver/functions/truncate.js +0 -48
  657. package/node_modules/semver/functions/valid.js +0 -8
  658. package/node_modules/semver/index.js +0 -93
  659. package/node_modules/semver/internal/constants.js +0 -37
  660. package/node_modules/semver/internal/debug.js +0 -11
  661. package/node_modules/semver/internal/identifiers.js +0 -29
  662. package/node_modules/semver/internal/lrucache.js +0 -42
  663. package/node_modules/semver/internal/parse-options.js +0 -17
  664. package/node_modules/semver/internal/re.js +0 -223
  665. package/node_modules/semver/package.json +0 -78
  666. package/node_modules/semver/preload.js +0 -4
  667. package/node_modules/semver/range.bnf +0 -17
  668. package/node_modules/semver/ranges/gtr.js +0 -6
  669. package/node_modules/semver/ranges/intersects.js +0 -9
  670. package/node_modules/semver/ranges/ltr.js +0 -6
  671. package/node_modules/semver/ranges/max-satisfying.js +0 -27
  672. package/node_modules/semver/ranges/min-satisfying.js +0 -26
  673. package/node_modules/semver/ranges/min-version.js +0 -63
  674. package/node_modules/semver/ranges/outside.js +0 -82
  675. package/node_modules/semver/ranges/simplify.js +0 -49
  676. package/node_modules/semver/ranges/subset.js +0 -249
  677. package/node_modules/semver/ranges/to-comparators.js +0 -10
  678. package/node_modules/semver/ranges/valid.js +0 -13
  679. package/node_modules/simple-concat/.travis.yml +0 -3
  680. package/node_modules/simple-concat/LICENSE +0 -20
  681. package/node_modules/simple-concat/README.md +0 -44
  682. package/node_modules/simple-concat/index.js +0 -15
  683. package/node_modules/simple-concat/package.json +0 -47
  684. package/node_modules/simple-concat/test/basic.js +0 -41
  685. package/node_modules/simple-get/.github/dependabot.yml +0 -15
  686. package/node_modules/simple-get/.github/workflows/ci.yml +0 -23
  687. package/node_modules/simple-get/LICENSE +0 -20
  688. package/node_modules/simple-get/README.md +0 -333
  689. package/node_modules/simple-get/index.js +0 -108
  690. package/node_modules/simple-get/package.json +0 -67
  691. package/node_modules/string_decoder/LICENSE +0 -48
  692. package/node_modules/string_decoder/README.md +0 -47
  693. package/node_modules/string_decoder/lib/string_decoder.js +0 -296
  694. package/node_modules/string_decoder/package.json +0 -34
  695. package/node_modules/strip-json-comments/index.js +0 -70
  696. package/node_modules/strip-json-comments/license +0 -21
  697. package/node_modules/strip-json-comments/package.json +0 -42
  698. package/node_modules/strip-json-comments/readme.md +0 -64
  699. package/node_modules/tar-fs/.travis.yml +0 -6
  700. package/node_modules/tar-fs/LICENSE +0 -21
  701. package/node_modules/tar-fs/README.md +0 -165
  702. package/node_modules/tar-fs/index.js +0 -392
  703. package/node_modules/tar-fs/package.json +0 -46
  704. package/node_modules/tar-fs/test/fixtures/a/hello.txt +0 -1
  705. package/node_modules/tar-fs/test/fixtures/b/a/test.txt +0 -1
  706. package/node_modules/tar-fs/test/fixtures/d/file1 +0 -0
  707. package/node_modules/tar-fs/test/fixtures/d/file2 +0 -0
  708. package/node_modules/tar-fs/test/fixtures/d/sub-dir/file5 +0 -0
  709. package/node_modules/tar-fs/test/fixtures/d/sub-files/file3 +0 -0
  710. package/node_modules/tar-fs/test/fixtures/d/sub-files/file4 +0 -0
  711. package/node_modules/tar-fs/test/fixtures/e/directory/.ignore +0 -0
  712. package/node_modules/tar-fs/test/fixtures/e/file +0 -0
  713. package/node_modules/tar-fs/test/fixtures/invalid.tar +0 -0
  714. package/node_modules/tar-fs/test/index.js +0 -457
  715. package/node_modules/tar-stream/LICENSE +0 -21
  716. package/node_modules/tar-stream/README.md +0 -168
  717. package/node_modules/tar-stream/extract.js +0 -257
  718. package/node_modules/tar-stream/headers.js +0 -295
  719. package/node_modules/tar-stream/index.js +0 -2
  720. package/node_modules/tar-stream/pack.js +0 -255
  721. package/node_modules/tar-stream/package.json +0 -58
  722. package/node_modules/tar-stream/sandbox.js +0 -11
  723. package/node_modules/tunnel-agent/LICENSE +0 -55
  724. package/node_modules/tunnel-agent/README.md +0 -4
  725. package/node_modules/tunnel-agent/index.js +0 -244
  726. package/node_modules/tunnel-agent/package.json +0 -22
  727. package/node_modules/util-deprecate/History.md +0 -16
  728. package/node_modules/util-deprecate/LICENSE +0 -24
  729. package/node_modules/util-deprecate/README.md +0 -53
  730. package/node_modules/util-deprecate/browser.js +0 -67
  731. package/node_modules/util-deprecate/node.js +0 -6
  732. package/node_modules/util-deprecate/package.json +0 -27
  733. package/node_modules/wrappy/LICENSE +0 -15
  734. package/node_modules/wrappy/README.md +0 -36
  735. package/node_modules/wrappy/package.json +0 -29
  736. package/node_modules/wrappy/wrappy.js +0 -33
  737. /package/{dist/src/twin-github/server.d.ts → node_modules/@pome-sh/twin-slack/dist/scripts/fidelity-parity.d.ts} +0 -0
  738. /package/{dist/src/twin-github/types.js → node_modules/@pome-sh/twin-stripe/dist/scripts/fidelity-parity.d.ts} +0 -0
@@ -1,1293 +0,0 @@
1
- import { resetDatabase } from "./db.js";
2
- import { slackError, notFound } from "./errors.js";
3
- import { parseSeed } from "./seed.js";
4
- import { SLACK_TWIN_HOST, serializeBookmark, serializeChannel, serializeFile, serializeMessage, serializePin, serializeUser, serializeUserProfile, serializeWorkspace, } from "./serializers.js";
5
- import { cursorDecode, cursorEncode, nowIso, nowUnix, padTsCounter, tsBaseSeconds } from "./util.js";
6
- const NOOP = () => { };
7
- const DEFAULT_BOT_USER = "U_PRIMARY";
8
- const DEFAULT_TEAM_ID = "T_POME";
9
- export class SlackDomain {
10
- db;
11
- constructor(db) {
12
- this.db = db;
13
- }
14
- // ───────────────────────────────────────────────────────────────────────────
15
- // Lifecycle
16
- // ───────────────────────────────────────────────────────────────────────────
17
- seed(state) {
18
- resetDatabase(this.db);
19
- const team = state.team ?? {};
20
- const teamId = team.id ?? DEFAULT_TEAM_ID;
21
- const teamName = team.name ?? "Pome Twin Workspace";
22
- const teamDomain = team.domain ?? "pome-twin";
23
- const now = nowIso();
24
- this.db.transaction(() => {
25
- this.db
26
- .prepare(`INSERT INTO workspaces (id, name, domain, url, enterprise_id, created_at, entity_counter)
27
- VALUES (?, ?, ?, ?, NULL, ?, 0)`)
28
- .run(teamId, teamName, teamDomain, `${SLACK_TWIN_HOST}/`, now);
29
- // Pre-create users from seed.
30
- const userIdByHandle = new Map();
31
- const seedUsers = state.users ?? [];
32
- for (const u of seedUsers) {
33
- const id = u.id ?? this.allocId(teamId, u.is_bot ? "B" : "U");
34
- const handle = u.name;
35
- userIdByHandle.set(handle, id);
36
- userIdByHandle.set(id, id);
37
- this.db
38
- .prepare(`INSERT INTO users (id, team_id, name, real_name, display_name, email, is_bot, is_admin, deleted, tz, profile_json, created_at, updated_at)
39
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, 0, ?, ?, ?, ?)`)
40
- .run(id, teamId, handle, u.real_name ?? "", "", u.email ?? null, u.is_bot ? 1 : 0, u.is_admin ? 1 : 0, u.tz ?? "America/Los_Angeles", JSON.stringify(u.profile ?? {}), now, now);
41
- }
42
- // Ensure the pome-agent user always exists.
43
- if (!userIdByHandle.has("pome-agent") && !userIdByHandle.has(DEFAULT_BOT_USER)) {
44
- const id = DEFAULT_BOT_USER;
45
- userIdByHandle.set("pome-agent", id);
46
- userIdByHandle.set(id, id);
47
- this.db
48
- .prepare(`INSERT INTO users (id, team_id, name, real_name, display_name, email, is_bot, is_admin, deleted, tz, profile_json, created_at, updated_at)
49
- VALUES (?, ?, 'pome-agent', 'Pome Agent', '', 'pome-agent@pome-twin.slack.com', 0, 1, 0, 'America/Los_Angeles', '{}', ?, ?)`)
50
- .run(id, teamId, now, now);
51
- }
52
- // Channels + members + messages.
53
- const seedChannels = state.channels ?? [];
54
- for (const ch of seedChannels) {
55
- const channelId = ch.id ?? this.allocId(teamId, ch.is_private ? "G" : "C");
56
- const creator = this.resolveSeedUserId(ch.creator, userIdByHandle) ?? DEFAULT_BOT_USER;
57
- this.db
58
- .prepare(`INSERT INTO channels (id, team_id, name, is_channel, is_group, is_im, is_mpim, is_private, is_archived, topic, purpose, creator, created_at, ts_counter)
59
- VALUES (?, ?, ?, ?, ?, 0, 0, ?, 0, ?, ?, ?, ?, 0)`)
60
- .run(channelId, teamId, ch.name, ch.is_private ? 0 : 1, ch.is_private ? 1 : 0, ch.is_private ? 1 : 0, ch.topic ?? "", ch.purpose ?? "", creator, now);
61
- for (const member of ch.members ?? []) {
62
- const memberId = this.resolveSeedUserId(member, userIdByHandle);
63
- if (!memberId)
64
- continue;
65
- this.db
66
- .prepare(`INSERT OR IGNORE INTO channel_members (channel_id, user_id, joined_at) VALUES (?, ?, ?)`)
67
- .run(channelId, memberId, now);
68
- }
69
- // Seeded messages preserve thread relationships.
70
- const seededTsByOriginalTs = new Map();
71
- for (const m of ch.messages ?? []) {
72
- const ts = m.ts ?? this.allocMessageTs(channelId);
73
- if (m.ts)
74
- seededTsByOriginalTs.set(m.ts, ts);
75
- const userId = this.resolveSeedUserId(m.user, userIdByHandle) ?? DEFAULT_BOT_USER;
76
- const threadTs = m.thread_ts ? seededTsByOriginalTs.get(m.thread_ts) ?? m.thread_ts : null;
77
- this.db
78
- .prepare(`INSERT INTO messages (channel_id, ts, user_id, text, subtype, thread_ts, reply_count, reply_users_count, latest_reply, edited_user_id, edited_ts, blocks_json, attachments_json)
79
- VALUES (?, ?, ?, ?, NULL, ?, 0, 0, NULL, NULL, NULL, '[]', '[]')`)
80
- .run(channelId, ts, userId, m.text, threadTs);
81
- if (threadTs) {
82
- this.db
83
- .prepare(`UPDATE messages SET reply_count = reply_count + 1, latest_reply = ?,
84
- reply_users_count = (
85
- SELECT COUNT(DISTINCT user_id) FROM messages WHERE channel_id = ? AND thread_ts = ?
86
- )
87
- WHERE channel_id = ? AND ts = ?`)
88
- .run(ts, channelId, threadTs, channelId, threadTs);
89
- }
90
- for (const r of m.reactions ?? []) {
91
- const reactorId = this.resolveSeedUserId(r.user, userIdByHandle);
92
- if (!reactorId)
93
- continue;
94
- this.db
95
- .prepare(`INSERT OR IGNORE INTO reactions (channel_id, message_ts, name, user_id, added_at) VALUES (?, ?, ?, ?, ?)`)
96
- .run(channelId, ts, r.name, reactorId, now);
97
- }
98
- }
99
- }
100
- })();
101
- }
102
- resetToDefault(seedFn, onDelta = NOOP) {
103
- const beforeSummary = this.summarizeState();
104
- this.seed(seedFn());
105
- const afterSummary = this.summarizeState();
106
- onDelta({ before: beforeSummary, after: afterSummary });
107
- return { ok: true };
108
- }
109
- applySeed(input, onDelta = NOOP) {
110
- const beforeSummary = this.summarizeState();
111
- const next = parseSeed(input);
112
- this.seed(next);
113
- const afterSummary = this.summarizeState();
114
- onDelta({ before: beforeSummary, after: afterSummary });
115
- return { ok: true };
116
- }
117
- exportState() {
118
- const workspace = this.firstWorkspace();
119
- if (!workspace)
120
- return { workspace: null };
121
- return {
122
- workspace: {
123
- id: workspace.id,
124
- name: workspace.name,
125
- domain: workspace.domain,
126
- },
127
- users: this.db.prepare(`SELECT * FROM users WHERE team_id = ? ORDER BY id`).all(workspace.id),
128
- channels: this.allChannels().map((channel) => ({
129
- ...channel,
130
- members: this.db
131
- .prepare(`SELECT user_id FROM channel_members WHERE channel_id = ? ORDER BY user_id`)
132
- .all(channel.id)
133
- .map((row) => row.user_id),
134
- messages: this.db
135
- .prepare(`SELECT * FROM messages WHERE channel_id = ? ORDER BY ts`)
136
- .all(channel.id),
137
- })),
138
- reactions: this.db.prepare(`SELECT * FROM reactions ORDER BY channel_id, message_ts, name, user_id`).all(),
139
- pins: this.db.prepare(`SELECT * FROM pins ORDER BY channel_id, message_ts`).all(),
140
- files: this.db.prepare(`SELECT * FROM files ORDER BY id`).all(),
141
- bookmarks: this.db.prepare(`SELECT * FROM bookmarks ORDER BY id`).all(),
142
- scheduled_messages: this.db.prepare(`SELECT * FROM scheduled_messages ORDER BY id`).all(),
143
- };
144
- }
145
- summarizeState() {
146
- const get = (sql) => this.db.prepare(sql).get().c;
147
- return {
148
- users: get(`SELECT COUNT(*) AS c FROM users`),
149
- channels: get(`SELECT COUNT(*) AS c FROM channels`),
150
- messages: get(`SELECT COUNT(*) AS c FROM messages`),
151
- reactions: get(`SELECT COUNT(*) AS c FROM reactions`),
152
- pins: get(`SELECT COUNT(*) AS c FROM pins`),
153
- files: get(`SELECT COUNT(*) AS c FROM files`),
154
- bookmarks: get(`SELECT COUNT(*) AS c FROM bookmarks`),
155
- scheduled_messages: get(`SELECT COUNT(*) AS c FROM scheduled_messages`),
156
- };
157
- }
158
- // ───────────────────────────────────────────────────────────────────────────
159
- // Auth
160
- // ───────────────────────────────────────────────────────────────────────────
161
- authTest(actor) {
162
- const workspace = this.requireWorkspace();
163
- const userRow = this.resolveActorUser(actor);
164
- return {
165
- url: `${SLACK_TWIN_HOST}/`,
166
- team: workspace.name,
167
- user: userRow.name,
168
- team_id: workspace.id,
169
- user_id: userRow.id,
170
- bot_id: userRow.is_bot ? userRow.id : null,
171
- is_enterprise_install: false,
172
- };
173
- }
174
- // ───────────────────────────────────────────────────────────────────────────
175
- // Conversations
176
- // ───────────────────────────────────────────────────────────────────────────
177
- conversationsList(args) {
178
- const requested = (args.types ?? "public_channel")
179
- .split(",")
180
- .map((t) => t.trim())
181
- .filter(Boolean);
182
- const typeFilters = [];
183
- if (requested.includes("public_channel"))
184
- typeFilters.push(`(is_private = 0 AND is_im = 0 AND is_mpim = 0)`);
185
- if (requested.includes("private_channel"))
186
- typeFilters.push(`(is_private = 1 AND is_im = 0 AND is_mpim = 0)`);
187
- if (requested.includes("mpim"))
188
- typeFilters.push(`is_mpim = 1`);
189
- if (requested.includes("im"))
190
- typeFilters.push(`is_im = 1`);
191
- const where = [];
192
- if (typeFilters.length > 0)
193
- where.push(`(${typeFilters.join(" OR ")})`);
194
- if (args.exclude_archived)
195
- where.push(`is_archived = 0`);
196
- const offset = cursorDecode(args.cursor ?? null)?.offset ?? 0;
197
- const limit = clampLimit(args.limit, 1000, 100);
198
- const stmt = `SELECT * FROM channels${where.length ? ` WHERE ${where.join(" AND ")}` : ""} ORDER BY created_at, id LIMIT ? OFFSET ?`;
199
- const rows = this.db.prepare(stmt).all(limit + 1, offset);
200
- const hasMore = rows.length > limit;
201
- const slice = rows.slice(0, limit);
202
- const channels = slice.map((row) => {
203
- const numMembers = this.db.prepare(`SELECT COUNT(*) AS c FROM channel_members WHERE channel_id = ?`).get(row.id).c;
204
- return serializeChannel(row, { num_members: numMembers });
205
- });
206
- return {
207
- channels,
208
- response_metadata: {
209
- next_cursor: hasMore ? cursorEncode({ offset: offset + slice.length }) : "",
210
- },
211
- };
212
- }
213
- conversationsInfo(args, actor = {}) {
214
- const channel = this.requireChannel(args.channel);
215
- this.assertChannelMemberForActor(channel, actor);
216
- const numMembers = this.db.prepare(`SELECT COUNT(*) AS c FROM channel_members WHERE channel_id = ?`).get(channel.id).c;
217
- return { channel: serializeChannel(channel, { num_members: numMembers }) };
218
- }
219
- conversationsCreate(args, actor, onDelta = NOOP) {
220
- // Slack distinguishes invalid_name_required (missing), invalid_name_maxlength
221
- // (>80), invalid_name_specials (any letter outside [a-z0-9_-]), and
222
- // invalid_name_punctuation (leading/trailing dash, leading underscore,
223
- // pure digits). Match per https://docs.slack.dev/reference/methods/conversations.create
224
- if (!args.name)
225
- slackError("invalid_name_required", 400);
226
- if (args.name.length > 80)
227
- slackError("invalid_name_maxlength", 400);
228
- if (/[A-Z]/.test(args.name) || /[^a-z0-9_-]/.test(args.name)) {
229
- slackError("invalid_name_specials", 400);
230
- }
231
- if (/^[-_]/.test(args.name) || /^\d+$/.test(args.name)) {
232
- slackError("invalid_name_punctuation", 400);
233
- }
234
- const workspace = this.requireWorkspace();
235
- const creator = this.resolveActorUser(actor).id;
236
- const channelId = this.allocId(workspace.id, args.is_private ? "G" : "C");
237
- const now = nowIso();
238
- const out = this.db.transaction(() => {
239
- const existing = this.db
240
- .prepare(`SELECT id FROM channels WHERE team_id = ? AND name = ?`)
241
- .get(workspace.id, args.name);
242
- if (existing)
243
- slackError("name_taken", 409);
244
- this.db
245
- .prepare(`INSERT INTO channels (id, team_id, name, is_channel, is_group, is_im, is_mpim, is_private, is_archived, topic, purpose, creator, created_at, ts_counter)
246
- VALUES (?, ?, ?, ?, ?, 0, 0, ?, 0, '', '', ?, ?, 0)`)
247
- .run(channelId, workspace.id, args.name, args.is_private ? 0 : 1, args.is_private ? 1 : 0, args.is_private ? 1 : 0, creator, now);
248
- this.db
249
- .prepare(`INSERT OR IGNORE INTO channel_members (channel_id, user_id, joined_at) VALUES (?, ?, ?)`)
250
- .run(channelId, creator, now);
251
- const row = this.requireChannelRow(channelId);
252
- onDelta({ before: null, after: row });
253
- return serializeChannel(row, { num_members: 1, is_member: true });
254
- })();
255
- return { channel: out };
256
- }
257
- conversationsArchive(args, actor, onDelta = NOOP) {
258
- const channel = this.requireChannel(args.channel);
259
- if (channel.is_archived)
260
- slackError("already_archived", 400);
261
- if (channel.name === "general")
262
- slackError("cant_archive_general", 400);
263
- const acting = this.resolveActorUser(actor);
264
- if (!acting.is_admin)
265
- this.assertChannelMember(channel, acting.id);
266
- const out = this.db.transaction(() => {
267
- const before = this.requireChannelRow(channel.id);
268
- this.db.prepare(`UPDATE channels SET is_archived = 1 WHERE id = ?`).run(channel.id);
269
- const after = this.requireChannelRow(channel.id);
270
- onDelta({ before, after });
271
- return { ok: true };
272
- })();
273
- return out;
274
- }
275
- conversationsInvite(args, actor, onDelta = NOOP) {
276
- const channel = this.requireChannel(args.channel);
277
- if (channel.is_archived)
278
- slackError("is_archived", 400);
279
- const acting = this.resolveActorUser(actor);
280
- if (!acting.is_admin)
281
- this.assertChannelMember(channel, acting.id);
282
- const userIds = (args.users ?? "")
283
- .split(",")
284
- .map((s) => s.trim())
285
- .filter(Boolean);
286
- if (userIds.length === 0)
287
- slackError("no_user", 400);
288
- const now = nowIso();
289
- const out = this.db.transaction(() => {
290
- const beforeMembers = this.channelMemberIds(channel.id);
291
- for (const userRef of userIds) {
292
- const user = this.resolveUser(userRef);
293
- if (!user)
294
- slackError("user_not_found", 404);
295
- const exists = this.db
296
- .prepare(`SELECT 1 FROM channel_members WHERE channel_id = ? AND user_id = ?`)
297
- .get(channel.id, user.id);
298
- if (exists)
299
- slackError("already_in_channel", 400);
300
- this.db
301
- .prepare(`INSERT INTO channel_members (channel_id, user_id, joined_at) VALUES (?, ?, ?)`)
302
- .run(channel.id, user.id, now);
303
- }
304
- const afterMembers = this.channelMemberIds(channel.id);
305
- onDelta({
306
- before: { channel_id: channel.id, members: beforeMembers },
307
- after: { channel_id: channel.id, members: afterMembers },
308
- });
309
- return { channel: serializeChannel(channel, { num_members: afterMembers.length }) };
310
- })();
311
- return out;
312
- }
313
- conversationsJoin(args, actor, onDelta = NOOP) {
314
- const channel = this.requireChannel(args.channel);
315
- if (channel.is_archived)
316
- slackError("is_archived", 400);
317
- if (channel.is_private)
318
- slackError("channel_not_found", 404);
319
- const userId = this.resolveActorUser(actor).id;
320
- const now = nowIso();
321
- const out = this.db.transaction(() => {
322
- const existing = this.db
323
- .prepare(`SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?`)
324
- .get(channel.id, userId);
325
- if (existing) {
326
- return { channel: serializeChannel(channel, { is_member: true }), already_in_channel: true, warning: "already_in_channel" };
327
- }
328
- this.db
329
- .prepare(`INSERT INTO channel_members (channel_id, user_id, joined_at) VALUES (?, ?, ?)`)
330
- .run(channel.id, userId, now);
331
- const after = this.db
332
- .prepare(`SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?`)
333
- .get(channel.id, userId);
334
- onDelta({ before: null, after });
335
- return { channel: serializeChannel(channel, { is_member: true }) };
336
- })();
337
- return out;
338
- }
339
- conversationsLeave(args, actor, onDelta = NOOP) {
340
- const channel = this.requireChannel(args.channel);
341
- if (channel.name === "general")
342
- slackError("cant_leave_general", 400);
343
- const userId = this.resolveActorUser(actor).id;
344
- const out = this.db.transaction(() => {
345
- const existing = this.db
346
- .prepare(`SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?`)
347
- .get(channel.id, userId);
348
- if (!existing)
349
- slackError("not_in_channel", 400);
350
- this.db.prepare(`DELETE FROM channel_members WHERE channel_id = ? AND user_id = ?`).run(channel.id, userId);
351
- onDelta({ before: existing, after: null });
352
- return { not_in_channel: false };
353
- })();
354
- return out;
355
- }
356
- conversationsKick(args, actor, onDelta = NOOP) {
357
- const channel = this.requireChannel(args.channel);
358
- if (channel.name === "general")
359
- slackError("cant_kick_from_general", 400);
360
- const acting = this.resolveActorUser(actor);
361
- const target = this.resolveUser(args.user);
362
- if (!target)
363
- slackError("user_not_found", 404);
364
- if (target.id === acting.id)
365
- slackError("cant_kick_self", 400);
366
- const out = this.db.transaction(() => {
367
- const existing = this.db
368
- .prepare(`SELECT * FROM channel_members WHERE channel_id = ? AND user_id = ?`)
369
- .get(channel.id, target.id);
370
- if (!existing)
371
- slackError("not_in_channel", 400);
372
- this.db.prepare(`DELETE FROM channel_members WHERE channel_id = ? AND user_id = ?`).run(channel.id, target.id);
373
- onDelta({ before: existing, after: null });
374
- return {};
375
- })();
376
- return out;
377
- }
378
- conversationsMembers(args, actor = {}) {
379
- const channel = this.requireChannel(args.channel);
380
- this.assertChannelMemberForActor(channel, actor);
381
- const offset = cursorDecode(args.cursor ?? null)?.offset ?? 0;
382
- const limit = clampLimit(args.limit, 1000, 100);
383
- const rows = this.db
384
- .prepare(`SELECT user_id FROM channel_members WHERE channel_id = ? ORDER BY joined_at, user_id LIMIT ? OFFSET ?`)
385
- .all(channel.id, limit + 1, offset);
386
- const hasMore = rows.length > limit;
387
- const slice = rows.slice(0, limit);
388
- return {
389
- members: slice.map((row) => row.user_id),
390
- response_metadata: {
391
- next_cursor: hasMore ? cursorEncode({ offset: offset + slice.length }) : "",
392
- },
393
- };
394
- }
395
- conversationsHistory(args, actor = {}) {
396
- const channel = this.requireChannel(args.channel);
397
- this.assertChannelMemberForActor(channel, actor);
398
- const offset = cursorDecode(args.cursor ?? null)?.offset ?? 0;
399
- const limit = clampLimit(args.limit, 1000, 100);
400
- const params = [channel.id];
401
- const where = [`channel_id = ?`, `(thread_ts IS NULL OR ts = thread_ts)`];
402
- if (args.oldest) {
403
- where.push(args.inclusive ? `ts >= ?` : `ts > ?`);
404
- params.push(args.oldest);
405
- }
406
- if (args.latest) {
407
- where.push(args.inclusive ? `ts <= ?` : `ts < ?`);
408
- params.push(args.latest);
409
- }
410
- const sql = `SELECT * FROM messages WHERE ${where.join(" AND ")} ORDER BY ts DESC LIMIT ? OFFSET ?`;
411
- params.push(limit + 1, offset);
412
- const rows = this.db.prepare(sql).all(...params);
413
- const hasMore = rows.length > limit;
414
- const slice = rows.slice(0, limit);
415
- const workspaceId = this.requireWorkspace().id;
416
- return {
417
- messages: slice.map((row) => serializeMessage(row, { reactions: this.reactionsFor(row.channel_id, row.ts), include_team: workspaceId })),
418
- has_more: hasMore,
419
- pin_count: this.db.prepare(`SELECT COUNT(*) AS c FROM pins WHERE channel_id = ?`).get(channel.id).c,
420
- channel_actions_ts: null,
421
- channel_actions_count: 0,
422
- // Real Slack OMITS `response_metadata` on conversations.history when there is
423
- // no next page (it only carries the pagination cursor when hasMore); it does
424
- // NOT return an empty-cursor envelope here the way conversations.list does
425
- // (FDRS-473 Kind B — conditional pagination matched per endpoint).
426
- ...(hasMore
427
- ? { response_metadata: { next_cursor: cursorEncode({ offset: offset + slice.length }) } }
428
- : {}),
429
- };
430
- }
431
- conversationsReplies(args, actor = {}) {
432
- if (!args.ts)
433
- slackError("invalid_arguments", 400);
434
- const channel = this.requireChannel(args.channel);
435
- this.assertChannelMemberForActor(channel, actor);
436
- const parent = this.db
437
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
438
- .get(channel.id, args.ts);
439
- if (!parent)
440
- slackError("thread_not_found", 404);
441
- const offset = cursorDecode(args.cursor ?? null)?.offset ?? 0;
442
- const limit = clampLimit(args.limit, 1000, 100);
443
- const rows = this.db
444
- .prepare(`SELECT * FROM messages
445
- WHERE channel_id = ? AND (ts = ? OR thread_ts = ?)
446
- ORDER BY ts ASC LIMIT ? OFFSET ?`)
447
- .all(channel.id, args.ts, args.ts, limit + 1, offset);
448
- const hasMore = rows.length > limit;
449
- const slice = rows.slice(0, limit);
450
- const workspaceId = this.requireWorkspace().id;
451
- const parentAuthor = parent.user_id;
452
- const parentTs = parent.ts;
453
- return {
454
- messages: slice.map((row) => {
455
- const isParent = row.ts === parentTs && !row.thread_ts;
456
- const serialized = serializeMessage(row, {
457
- reactions: this.reactionsFor(row.channel_id, row.ts),
458
- include_team: workspaceId,
459
- parent_user_id: row.thread_ts ? parentAuthor : undefined,
460
- });
461
- if (isParent) {
462
- // Real Slack conversations.replies decorates the thread parent
463
- // with thread_ts === ts (even though the stored row has no
464
- // thread_ts), plus subscribed/is_locked. Every thread-walking
465
- // SDK consumer relies on this invariant.
466
- serialized.thread_ts = parentTs;
467
- serialized.subscribed = false;
468
- serialized.is_locked = false;
469
- }
470
- return serialized;
471
- }),
472
- has_more: hasMore,
473
- // Real Slack OMITS `response_metadata` on conversations.replies when there is
474
- // no next page (cursor present only when hasMore) — FDRS-473 Kind B.
475
- ...(hasMore
476
- ? { response_metadata: { next_cursor: cursorEncode({ offset: offset + slice.length }) } }
477
- : {}),
478
- };
479
- }
480
- conversationsOpen(args, actor, onDelta = NOOP) {
481
- const workspace = this.requireWorkspace();
482
- const acting = this.resolveActorUser(actor);
483
- if (args.channel) {
484
- // Re-open existing DM/MPIM.
485
- const existing = this.requireChannel(args.channel);
486
- onDelta({ before: existing, after: existing });
487
- return { no_op: true, already_open: true, channel: serializeChannel(existing, { is_member: true }) };
488
- }
489
- if (!args.users)
490
- slackError("users_list_not_supplied", 400);
491
- const targetIds = args.users
492
- .split(",")
493
- .map((s) => s.trim())
494
- .filter(Boolean)
495
- .map((u) => {
496
- const found = this.resolveUser(u);
497
- if (!found)
498
- slackError("user_not_found", 404);
499
- return found.id;
500
- });
501
- const allIds = [...new Set([acting.id, ...targetIds])].sort();
502
- const isMpim = allIds.length > 2;
503
- const lookupName = isMpim ? `mpdm-${allIds.join("--")}` : "";
504
- const dmSignature = allIds.join("|");
505
- const out = this.db.transaction(() => {
506
- const existingChannel = this.findDirectChannelBySignature(workspace.id, dmSignature);
507
- if (existingChannel) {
508
- onDelta({ before: existingChannel, after: existingChannel });
509
- return { no_op: true, already_open: true, channel: serializeChannel(existingChannel, { is_member: true }) };
510
- }
511
- const channelId = this.allocId(workspace.id, isMpim ? "M" : "D");
512
- const now = nowIso();
513
- this.db
514
- .prepare(`INSERT INTO channels (id, team_id, name, is_channel, is_group, is_im, is_mpim, is_private, is_archived, topic, purpose, creator, created_at, ts_counter, dm_signature)
515
- VALUES (?, ?, ?, 0, 0, ?, ?, 1, 0, '', '', ?, ?, 0, ?)`)
516
- .run(channelId, workspace.id, lookupName, isMpim ? 0 : 1, isMpim ? 1 : 0, acting.id, now, dmSignature);
517
- for (const id of allIds) {
518
- this.db
519
- .prepare(`INSERT OR IGNORE INTO channel_members (channel_id, user_id, joined_at) VALUES (?, ?, ?)`)
520
- .run(channelId, id, now);
521
- }
522
- const created = this.requireChannelRow(channelId);
523
- onDelta({ before: null, after: created });
524
- return { channel: serializeChannel(created, { is_member: true, num_members: allIds.length }) };
525
- })();
526
- return out;
527
- }
528
- // ───────────────────────────────────────────────────────────────────────────
529
- // Chat
530
- // ───────────────────────────────────────────────────────────────────────────
531
- chatPostMessage(args, actor, onDelta = NOOP) {
532
- const channel = this.requireChannel(args.channel);
533
- if (channel.is_archived)
534
- slackError("is_archived", 400);
535
- if (!args.text && !args.blocks && !args.attachments)
536
- slackError("no_text", 400);
537
- if (args.thread_ts) {
538
- const parent = this.db
539
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
540
- .get(channel.id, args.thread_ts);
541
- if (!parent)
542
- slackError("thread_not_found", 404);
543
- }
544
- const author = this.resolveActorUser(actor);
545
- if (channel.is_private || channel.is_im || channel.is_mpim) {
546
- const member = this.db
547
- .prepare(`SELECT 1 FROM channel_members WHERE channel_id = ? AND user_id = ?`)
548
- .get(channel.id, author.id);
549
- if (!member)
550
- slackError("not_in_channel", 400);
551
- }
552
- const ts = this.allocMessageTs(channel.id);
553
- const blocks = args.blocks ? sanitizeJsonString(args.blocks, "[]") : "[]";
554
- const attachments = args.attachments ? sanitizeJsonString(args.attachments, "[]") : "[]";
555
- const subtype = author.is_bot ? "bot_message" : null;
556
- // For bot authors: bot_id = the bot user's id; app_id = synthetic A_POME so
557
- // SDK consumers see a non-null app_id consistent with real Slack.
558
- const botId = author.is_bot ? author.id : null;
559
- const appId = author.is_bot ? "A_POME" : null;
560
- const username = typeof args.username === "string" && args.username.length > 0 ? args.username : null;
561
- const iconUrl = typeof args.icon_url === "string" && args.icon_url.length > 0 ? args.icon_url : null;
562
- const iconEmoji = typeof args.icon_emoji === "string" && args.icon_emoji.length > 0 ? args.icon_emoji : null;
563
- const out = this.db.transaction(() => {
564
- this.db
565
- .prepare(`INSERT INTO messages (channel_id, ts, user_id, text, subtype, thread_ts, blocks_json, attachments_json,
566
- bot_id, app_id, username, icon_url, icon_emoji)
567
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
568
- .run(channel.id, ts, author.id, args.text ?? "", subtype, args.thread_ts ?? null, blocks, attachments, botId, appId, username, iconUrl, iconEmoji);
569
- if (args.thread_ts) {
570
- this.db
571
- .prepare(`UPDATE messages SET reply_count = reply_count + 1, latest_reply = ?,
572
- reply_users_count = (
573
- SELECT COUNT(DISTINCT user_id) FROM messages WHERE channel_id = ? AND thread_ts = ?
574
- )
575
- WHERE channel_id = ? AND ts = ?`)
576
- .run(ts, channel.id, args.thread_ts, channel.id, args.thread_ts);
577
- }
578
- const row = this.requireMessageRow(channel.id, ts);
579
- onDelta({ before: null, after: row });
580
- const workspaceId = this.requireWorkspace().id;
581
- return { channel: channel.id, ts, message: serializeMessage(row, { include_team: workspaceId }) };
582
- })();
583
- return out;
584
- }
585
- chatUpdate(args, actor, onDelta = NOOP) {
586
- const channel = this.requireChannel(args.channel);
587
- if (channel.is_archived)
588
- slackError("is_archived", 400);
589
- const acting = this.resolveActorUser(actor);
590
- const before = this.db
591
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
592
- .get(channel.id, args.ts);
593
- if (!before)
594
- slackError("message_not_found", 404);
595
- // Real Slack has no admin override at the API layer — only the message's
596
- // author may update their own message. Single-tenant sandbox semantics:
597
- // the JWT's `login` claim identifies the acting user.
598
- if (before.user_id !== acting.id)
599
- slackError("cant_update_message", 403);
600
- const out = this.db.transaction(() => {
601
- const updatedTs = this.allocMessageTs(channel.id);
602
- const newText = args.text ?? before.text;
603
- const newBlocks = args.blocks ? sanitizeJsonString(args.blocks, before.blocks_json) : before.blocks_json;
604
- const newAttachments = args.attachments
605
- ? sanitizeJsonString(args.attachments, before.attachments_json)
606
- : before.attachments_json;
607
- this.db
608
- .prepare(`UPDATE messages SET text = ?, blocks_json = ?, attachments_json = ?, edited_user_id = ?, edited_ts = ? WHERE channel_id = ? AND ts = ?`)
609
- .run(newText, newBlocks, newAttachments, acting.id, updatedTs, channel.id, args.ts);
610
- const after = this.requireMessageRow(channel.id, args.ts);
611
- onDelta({ before, after });
612
- return { channel: channel.id, ts: args.ts, text: after.text, message: serializeMessage(after) };
613
- })();
614
- return out;
615
- }
616
- chatDelete(args, actor, onDelta = NOOP) {
617
- const channel = this.requireChannel(args.channel);
618
- const acting = this.resolveActorUser(actor);
619
- const before = this.db
620
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
621
- .get(channel.id, args.ts);
622
- if (!before)
623
- slackError("message_not_found", 404);
624
- // Real Slack has no admin override on chat.delete — only the author may delete.
625
- if (before.user_id !== acting.id)
626
- slackError("cant_delete_message", 403);
627
- const out = this.db.transaction(() => {
628
- this.db.prepare(`DELETE FROM messages WHERE channel_id = ? AND ts = ?`).run(channel.id, args.ts);
629
- // Decrement thread parent if this was a reply.
630
- if (before.thread_ts) {
631
- this.db
632
- .prepare(`UPDATE messages SET reply_count = MAX(reply_count - 1, 0),
633
- reply_users_count = (
634
- SELECT COUNT(DISTINCT user_id) FROM messages WHERE channel_id = ? AND thread_ts = ?
635
- ),
636
- latest_reply = (
637
- SELECT ts FROM messages WHERE channel_id = ? AND thread_ts = ? ORDER BY ts DESC LIMIT 1
638
- )
639
- WHERE channel_id = ? AND ts = ?`)
640
- .run(channel.id, before.thread_ts, channel.id, before.thread_ts, channel.id, before.thread_ts);
641
- }
642
- onDelta({ before, after: null });
643
- return { channel: channel.id, ts: args.ts };
644
- })();
645
- return out;
646
- }
647
- chatScheduleMessage(args, actor, onDelta = NOOP) {
648
- const channel = this.requireChannel(args.channel);
649
- if (channel.is_archived)
650
- slackError("is_archived", 400);
651
- if (!args.text)
652
- slackError("no_text", 400);
653
- if (!args.post_at || args.post_at <= nowUnix())
654
- slackError("time_in_past", 400);
655
- const acting = this.resolveActorUser(actor);
656
- const id = this.allocId(this.requireWorkspace().id, "Q");
657
- const now = nowUnix();
658
- const out = this.db.transaction(() => {
659
- this.db
660
- .prepare(`INSERT INTO scheduled_messages (id, channel_id, user_id, text, thread_ts, post_at, date_created, blocks_json)
661
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
662
- .run(id, channel.id, acting.id, args.text, args.thread_ts ?? null, args.post_at, now, args.blocks ? sanitizeJsonString(args.blocks, "[]") : "[]");
663
- const row = this.requireScheduledMessage(id);
664
- onDelta({ before: null, after: row });
665
- return {
666
- channel: channel.id,
667
- scheduled_message_id: id,
668
- post_at: args.post_at,
669
- message: { text: args.text, type: "delayed_message" },
670
- };
671
- })();
672
- return out;
673
- }
674
- chatDeleteScheduledMessage(args, actor, onDelta = NOOP) {
675
- void actor;
676
- const channel = this.requireChannel(args.channel);
677
- const row = this.db
678
- .prepare(`SELECT * FROM scheduled_messages WHERE id = ? AND channel_id = ?`)
679
- .get(args.scheduled_message_id, channel.id);
680
- if (!row)
681
- slackError("scheduled_message_not_found", 404);
682
- const out = this.db.transaction(() => {
683
- this.db.prepare(`DELETE FROM scheduled_messages WHERE id = ?`).run(row.id);
684
- onDelta({ before: row, after: null });
685
- return {};
686
- })();
687
- return out;
688
- }
689
- // ───────────────────────────────────────────────────────────────────────────
690
- // Reactions
691
- // ───────────────────────────────────────────────────────────────────────────
692
- reactionsAdd(args, actor, onDelta = NOOP) {
693
- if (!args.name)
694
- slackError("no_reaction", 400);
695
- const channel = this.requireChannel(args.channel);
696
- const message = this.db
697
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
698
- .get(channel.id, args.timestamp);
699
- if (!message)
700
- slackError("message_not_found", 404);
701
- const acting = this.resolveActorUser(actor);
702
- const reaction = normalizeReactionName(args.name);
703
- const out = this.db.transaction(() => {
704
- const existing = this.db
705
- .prepare(`SELECT * FROM reactions WHERE channel_id = ? AND message_ts = ? AND name = ? AND user_id = ?`)
706
- .get(channel.id, args.timestamp, reaction, acting.id);
707
- if (existing)
708
- slackError("already_reacted", 400);
709
- const now = nowIso();
710
- this.db
711
- .prepare(`INSERT INTO reactions (channel_id, message_ts, name, user_id, added_at) VALUES (?, ?, ?, ?, ?)`)
712
- .run(channel.id, args.timestamp, reaction, acting.id, now);
713
- const after = this.db
714
- .prepare(`SELECT * FROM reactions WHERE channel_id = ? AND message_ts = ? AND name = ? AND user_id = ?`)
715
- .get(channel.id, args.timestamp, reaction, acting.id);
716
- onDelta({ before: null, after });
717
- return {};
718
- })();
719
- return out;
720
- }
721
- reactionsRemove(args, actor, onDelta = NOOP) {
722
- if (!args.name)
723
- slackError("no_reaction", 400);
724
- const channel = this.requireChannel(args.channel);
725
- const acting = this.resolveActorUser(actor);
726
- const reaction = normalizeReactionName(args.name);
727
- const before = this.db
728
- .prepare(`SELECT * FROM reactions WHERE channel_id = ? AND message_ts = ? AND name = ? AND user_id = ?`)
729
- .get(channel.id, args.timestamp, reaction, acting.id);
730
- if (!before)
731
- slackError("no_reaction", 404);
732
- const out = this.db.transaction(() => {
733
- this.db
734
- .prepare(`DELETE FROM reactions WHERE channel_id = ? AND message_ts = ? AND name = ? AND user_id = ?`)
735
- .run(channel.id, args.timestamp, reaction, acting.id);
736
- onDelta({ before, after: null });
737
- return {};
738
- })();
739
- return out;
740
- }
741
- reactionsGet(args, actor = {}) {
742
- const channel = this.requireChannel(args.channel);
743
- this.assertChannelMemberForActor(channel, actor);
744
- const message = this.db
745
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
746
- .get(channel.id, args.timestamp);
747
- if (!message)
748
- slackError("message_not_found", 404);
749
- const reactions = this.reactionsFor(channel.id, args.timestamp);
750
- return {
751
- type: "message",
752
- channel: channel.id,
753
- message: { ...serializeMessage(message, { reactions }) },
754
- };
755
- }
756
- // ───────────────────────────────────────────────────────────────────────────
757
- // Users
758
- // ───────────────────────────────────────────────────────────────────────────
759
- usersList(args) {
760
- const workspace = this.requireWorkspace();
761
- const offset = cursorDecode(args.cursor ?? null)?.offset ?? 0;
762
- const limit = clampLimit(args.limit, 1000, 100);
763
- const rows = this.db
764
- .prepare(`SELECT * FROM users WHERE team_id = ? ORDER BY id LIMIT ? OFFSET ?`)
765
- .all(workspace.id, limit + 1, offset);
766
- const hasMore = rows.length > limit;
767
- const slice = rows.slice(0, limit);
768
- return {
769
- // users.list embeds each user's profile WITHOUT `email` — real Slack only
770
- // returns the email on the single-user reads (FDRS-473 Kind B).
771
- members: slice.map((row) => serializeUser(row, { include_locale: args.include_locale, omitProfileEmail: true })),
772
- cache_ts: nowUnix(),
773
- response_metadata: {
774
- next_cursor: hasMore ? cursorEncode({ offset: offset + slice.length }) : "",
775
- },
776
- };
777
- }
778
- usersInfo(args) {
779
- const user = this.resolveUser(args.user);
780
- if (!user)
781
- slackError("user_not_found", 404);
782
- return { user: serializeUser(user, { include_locale: args.include_locale }) };
783
- }
784
- usersLookupByEmail(args) {
785
- if (!args.email)
786
- slackError("users_not_found", 400);
787
- const workspace = this.requireWorkspace();
788
- const user = this.db
789
- .prepare(`SELECT * FROM users WHERE team_id = ? AND email = ?`)
790
- .get(workspace.id, args.email);
791
- if (!user)
792
- slackError("users_not_found", 404);
793
- return { user: serializeUser(user) };
794
- }
795
- usersProfileGet(args, actor) {
796
- const target = args.user ? this.resolveUser(args.user) : this.resolveActorUser(actor);
797
- if (!target)
798
- slackError("user_not_found", 404);
799
- // users.profile.get returns the bare profile WITHOUT `team` — real Slack omits
800
- // it on this endpoint (it carries it in the embedded user.profile elsewhere)
801
- // (FDRS-473 Kind B).
802
- return { profile: serializeUserProfile(target, { omitTeam: true }) };
803
- }
804
- usersProfileSet(args, actor, onDelta = NOOP) {
805
- const target = args.user ? this.resolveUser(args.user) : this.resolveActorUser(actor);
806
- if (!target)
807
- slackError("user_not_found", 404);
808
- const acting = this.resolveActorUser(actor);
809
- if (target.id !== acting.id && !acting.is_admin)
810
- slackError("cant_set_profile_for_other_user", 403);
811
- const out = this.db.transaction(() => {
812
- const before = this.db.prepare(`SELECT * FROM users WHERE id = ?`).get(target.id);
813
- let profile = safeParseJson(before.profile_json);
814
- if (args.profile) {
815
- const incoming = safeParseJson(args.profile);
816
- profile = { ...profile, ...incoming };
817
- }
818
- else if (args.name) {
819
- profile = { ...profile, [args.name]: args.value ?? "" };
820
- }
821
- this.db
822
- .prepare(`UPDATE users SET profile_json = ?, real_name = COALESCE((SELECT json_extract(?, '$.real_name')), real_name), updated_at = ? WHERE id = ?`)
823
- .run(JSON.stringify(profile), JSON.stringify(profile), nowIso(), target.id);
824
- const after = this.db.prepare(`SELECT * FROM users WHERE id = ?`).get(target.id);
825
- onDelta({ before, after });
826
- // users.profile.set echoes the bare profile, same shape as users.profile.get
827
- // (no `team`) — FDRS-473 Kind B.
828
- return { profile: serializeUserProfile(after, { omitTeam: true }) };
829
- })();
830
- return out;
831
- }
832
- // ───────────────────────────────────────────────────────────────────────────
833
- // Pins
834
- // ───────────────────────────────────────────────────────────────────────────
835
- pinsAdd(args, actor, onDelta = NOOP) {
836
- const channel = this.requireChannel(args.channel);
837
- const message = this.db
838
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
839
- .get(channel.id, args.timestamp);
840
- if (!message)
841
- slackError("message_not_found", 404);
842
- const acting = this.resolveActorUser(actor);
843
- const out = this.db.transaction(() => {
844
- const existing = this.db
845
- .prepare(`SELECT * FROM pins WHERE channel_id = ? AND message_ts = ?`)
846
- .get(channel.id, args.timestamp);
847
- if (existing)
848
- slackError("already_pinned", 400);
849
- this.db
850
- .prepare(`INSERT INTO pins (channel_id, message_ts, pinned_by, pinned_at) VALUES (?, ?, ?, ?)`)
851
- .run(channel.id, args.timestamp, acting.id, nowIso());
852
- const after = this.db
853
- .prepare(`SELECT * FROM pins WHERE channel_id = ? AND message_ts = ?`)
854
- .get(channel.id, args.timestamp);
855
- onDelta({ before: null, after });
856
- return {};
857
- })();
858
- return out;
859
- }
860
- pinsRemove(args, actor, onDelta = NOOP) {
861
- void actor;
862
- const channel = this.requireChannel(args.channel);
863
- const before = this.db
864
- .prepare(`SELECT * FROM pins WHERE channel_id = ? AND message_ts = ?`)
865
- .get(channel.id, args.timestamp);
866
- if (!before)
867
- slackError("no_pin", 404);
868
- const out = this.db.transaction(() => {
869
- this.db.prepare(`DELETE FROM pins WHERE channel_id = ? AND message_ts = ?`).run(channel.id, args.timestamp);
870
- onDelta({ before, after: null });
871
- return {};
872
- })();
873
- return out;
874
- }
875
- pinsList(args, actor = {}) {
876
- const channel = this.requireChannel(args.channel);
877
- this.assertChannelMemberForActor(channel, actor);
878
- const pins = this.db
879
- .prepare(`SELECT * FROM pins WHERE channel_id = ? ORDER BY pinned_at`)
880
- .all(channel.id);
881
- const items = pins.map((pin) => {
882
- const message = this.db
883
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
884
- .get(pin.channel_id, pin.message_ts);
885
- const serializedMessage = message ? serializeMessage(message, { reactions: this.reactionsFor(pin.channel_id, pin.message_ts) }) : { ts: pin.message_ts };
886
- return serializePin(pin, serializedMessage);
887
- });
888
- return { items };
889
- }
890
- // ───────────────────────────────────────────────────────────────────────────
891
- // Search
892
- // ───────────────────────────────────────────────────────────────────────────
893
- searchMessages(args, actor = {}) {
894
- if (!args.query || args.query.trim() === "")
895
- slackError("no_query", 400);
896
- const acting = this.resolveActorUser(actor);
897
- const count = clampLimit(args.count, 100, 20);
898
- const page = Math.max(1, Math.floor(args.page ?? 1));
899
- const offset = (page - 1) * count;
900
- const sortDir = (args.sort_dir ?? "desc").toLowerCase() === "asc" ? "ASC" : "DESC";
901
- // Simple LIKE-based search. Slack's actual full-text engine is more
902
- // sophisticated; for twin scenarios this is enough to match seeded
903
- // and posted messages on substring.
904
- const like = `%${args.query.replace(/[%_]/g, (m) => `\\${m}`)}%`;
905
- const visibilityClause = `(
906
- channel_id IN (SELECT id FROM channels WHERE is_private = 0 AND is_im = 0 AND is_mpim = 0)
907
- OR channel_id IN (SELECT channel_id FROM channel_members WHERE user_id = ?)
908
- )`;
909
- const total = this.db
910
- .prepare(`SELECT COUNT(*) AS c FROM messages WHERE text LIKE ? ESCAPE '\\' AND ${visibilityClause}`)
911
- .get(like, acting.id).c;
912
- const rows = this.db
913
- .prepare(`SELECT * FROM messages
914
- WHERE text LIKE ? ESCAPE '\\' AND ${visibilityClause}
915
- ORDER BY ts ${sortDir}
916
- LIMIT ? OFFSET ?`)
917
- .all(like, acting.id, count, offset);
918
- const matches = rows.map((row) => {
919
- const channel = this.db.prepare(`SELECT * FROM channels WHERE id = ?`).get(row.channel_id);
920
- return {
921
- iid: `${row.channel_id}-${row.ts}`,
922
- type: "message",
923
- user: row.user_id,
924
- username: this.userNameOf(row.user_id),
925
- ts: row.ts,
926
- text: row.text,
927
- permalink: `${SLACK_TWIN_HOST}/archives/${row.channel_id}/p${row.ts.replace(".", "")}`,
928
- team: this.requireWorkspace().id,
929
- channel: channel ? { id: channel.id, name: channel.name, is_private: Boolean(channel.is_private) } : { id: row.channel_id, name: "", is_private: false },
930
- };
931
- });
932
- return {
933
- query: args.query,
934
- messages: {
935
- total,
936
- pagination: {
937
- page,
938
- page_count: total === 0 ? 0 : Math.ceil(total / count),
939
- per_page: count,
940
- total_count: total,
941
- first: offset + 1,
942
- last: offset + matches.length,
943
- },
944
- paging: {
945
- count,
946
- total,
947
- page,
948
- pages: total === 0 ? 0 : Math.ceil(total / count),
949
- },
950
- matches,
951
- },
952
- };
953
- }
954
- // ───────────────────────────────────────────────────────────────────────────
955
- // Files
956
- // ───────────────────────────────────────────────────────────────────────────
957
- filesUpload(args, actor, onDelta = NOOP) {
958
- const workspace = this.requireWorkspace();
959
- const acting = this.resolveActorUser(actor);
960
- const channelsRaw = args.channels ?? args.channel ?? "";
961
- const channelIds = channelsRaw
962
- .split(",")
963
- .map((s) => s.trim())
964
- .filter(Boolean)
965
- .map((ref) => this.requireChannel(ref).id);
966
- const id = this.allocId(workspace.id, "F");
967
- const name = args.filename ?? `untitled-${id}.txt`;
968
- const title = args.title ?? name;
969
- const filetype = args.filetype ?? "text";
970
- const content = args.content ?? null;
971
- const size = content ? Buffer.byteLength(content, "utf8") : 0;
972
- const out = this.db.transaction(() => {
973
- this.db
974
- .prepare(`INSERT INTO files (id, team_id, user_id, name, title, mimetype, filetype, size, url_private, channels_json, deleted, content, created_at)
975
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, '', ?, 0, ?, ?)`)
976
- .run(id, workspace.id, acting.id, name, title, filetypeMimetype(filetype), filetype, size, JSON.stringify(channelIds), content, nowIso());
977
- const after = this.db.prepare(`SELECT * FROM files WHERE id = ?`).get(id);
978
- // Optionally post initial_comment as a message in the first channel.
979
- if (args.initial_comment && channelIds.length > 0) {
980
- const ts = this.allocMessageTs(channelIds[0]);
981
- this.db
982
- .prepare(`INSERT INTO messages (channel_id, ts, user_id, text, subtype, thread_ts, blocks_json, attachments_json)
983
- VALUES (?, ?, ?, ?, ?, ?, '[]', ?)`)
984
- .run(channelIds[0], ts, acting.id, args.initial_comment, "file_share", args.thread_ts ?? null, JSON.stringify([{ id: 1, file_id: id, title }]));
985
- }
986
- onDelta({ before: null, after });
987
- return { file: serializeFile(after) };
988
- })();
989
- return out;
990
- }
991
- filesInfo(args) {
992
- const file = this.db.prepare(`SELECT * FROM files WHERE id = ?`).get(args.file);
993
- if (!file)
994
- slackError("file_not_found", 404);
995
- return {
996
- file: serializeFile(file),
997
- comments: [],
998
- response_metadata: { next_cursor: "" },
999
- };
1000
- }
1001
- filesList(args) {
1002
- const count = clampLimit(args.count, 1000, 100);
1003
- const page = Math.max(1, Math.floor(args.page ?? 1));
1004
- const offset = (page - 1) * count;
1005
- const params = [];
1006
- const where = [`deleted = 0`];
1007
- if (args.user) {
1008
- const user = this.resolveUser(args.user);
1009
- if (!user)
1010
- slackError("user_not_found", 404);
1011
- where.push(`user_id = ?`);
1012
- params.push(user.id);
1013
- }
1014
- let sql = `SELECT * FROM files WHERE ${where.join(" AND ")} ORDER BY created_at DESC LIMIT ? OFFSET ?`;
1015
- params.push(count, offset);
1016
- let rows = this.db.prepare(sql).all(...params);
1017
- if (args.channel) {
1018
- const channelId = this.requireChannel(args.channel).id;
1019
- rows = rows.filter((row) => JSON.parse(row.channels_json).includes(channelId));
1020
- }
1021
- const total = this.db.prepare(`SELECT COUNT(*) AS c FROM files WHERE deleted = 0`).get().c;
1022
- return {
1023
- files: rows.map((row) => serializeFile(row)),
1024
- paging: {
1025
- count,
1026
- total,
1027
- page,
1028
- pages: total === 0 ? 0 : Math.ceil(total / count),
1029
- },
1030
- // files.list paginates via `paging` (page/pages), not a cursor. Real Slack
1031
- // does NOT return a `response_metadata.next_cursor` envelope on files.list,
1032
- // so the twin omits it rather than emitting an empty-cursor stub that diffs
1033
- // as a twin-only field (FDRS-473 Kind B).
1034
- };
1035
- }
1036
- filesDelete(args, actor, onDelta = NOOP) {
1037
- void actor;
1038
- const before = this.db.prepare(`SELECT * FROM files WHERE id = ?`).get(args.file);
1039
- if (!before)
1040
- slackError("file_not_found", 404);
1041
- const out = this.db.transaction(() => {
1042
- this.db.prepare(`UPDATE files SET deleted = 1 WHERE id = ?`).run(args.file);
1043
- const after = this.db.prepare(`SELECT * FROM files WHERE id = ?`).get(args.file);
1044
- onDelta({ before, after });
1045
- return {};
1046
- })();
1047
- return out;
1048
- }
1049
- // ───────────────────────────────────────────────────────────────────────────
1050
- // Bookmarks
1051
- // ───────────────────────────────────────────────────────────────────────────
1052
- bookmarksAdd(args, actor, onDelta = NOOP) {
1053
- const channel = this.requireChannel(args.channel_id);
1054
- if (!args.title)
1055
- slackError("invalid_arguments", 400);
1056
- if (!args.link && (!args.type || args.type === "link"))
1057
- slackError("invalid_arguments", 400);
1058
- const acting = this.resolveActorUser(actor);
1059
- const id = this.allocId(this.requireWorkspace().id, "Bk");
1060
- const out = this.db.transaction(() => {
1061
- this.db
1062
- .prepare(`INSERT INTO bookmarks (id, channel_id, title, link, emoji, type, created_by, created_at)
1063
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`)
1064
- .run(id, channel.id, args.title, args.link ?? "", args.emoji ?? null, args.type ?? "link", acting.id, nowIso());
1065
- const after = this.db.prepare(`SELECT * FROM bookmarks WHERE id = ?`).get(id);
1066
- onDelta({ before: null, after });
1067
- return { bookmark: serializeBookmark(after) };
1068
- })();
1069
- return out;
1070
- }
1071
- bookmarksRemove(args, actor, onDelta = NOOP) {
1072
- void actor;
1073
- const channel = this.requireChannel(args.channel_id);
1074
- const before = this.db
1075
- .prepare(`SELECT * FROM bookmarks WHERE id = ? AND channel_id = ?`)
1076
- .get(args.bookmark_id, channel.id);
1077
- if (!before)
1078
- slackError("no_bookmark_found", 404);
1079
- const out = this.db.transaction(() => {
1080
- this.db.prepare(`DELETE FROM bookmarks WHERE id = ?`).run(before.id);
1081
- onDelta({ before, after: null });
1082
- return {};
1083
- })();
1084
- return out;
1085
- }
1086
- bookmarksList(args) {
1087
- const channel = this.requireChannel(args.channel_id);
1088
- const rows = this.db
1089
- .prepare(`SELECT * FROM bookmarks WHERE channel_id = ? ORDER BY created_at`)
1090
- .all(channel.id);
1091
- return { bookmarks: rows.map((row) => serializeBookmark(row)) };
1092
- }
1093
- // ───────────────────────────────────────────────────────────────────────────
1094
- // Team
1095
- // ───────────────────────────────────────────────────────────────────────────
1096
- teamInfo(args) {
1097
- void args;
1098
- return { team: serializeWorkspace(this.requireWorkspace()) };
1099
- }
1100
- // ───────────────────────────────────────────────────────────────────────────
1101
- // Private helpers
1102
- // ───────────────────────────────────────────────────────────────────────────
1103
- firstWorkspace() {
1104
- return this.db.prepare(`SELECT * FROM workspaces LIMIT 1`).get();
1105
- }
1106
- requireWorkspace() {
1107
- const w = this.firstWorkspace();
1108
- if (!w)
1109
- slackError("team_not_found", 404);
1110
- return w;
1111
- }
1112
- allChannels() {
1113
- return this.db.prepare(`SELECT * FROM channels ORDER BY created_at, id`).all();
1114
- }
1115
- allocId(workspaceId, prefix) {
1116
- const row = this.db
1117
- .prepare(`UPDATE workspaces SET entity_counter = entity_counter + 1 WHERE id = ? RETURNING entity_counter`)
1118
- .get(workspaceId);
1119
- if (!row)
1120
- throw new Error(`workspace ${workspaceId} not found for id allocation`);
1121
- return `${prefix}${String(row.entity_counter).padStart(6, "0")}`;
1122
- }
1123
- allocMessageTs(channelId) {
1124
- // Real Slack ts is workspace-globally-unique. Increment the workspace
1125
- // counter (not the channel counter) so two channels' first messages
1126
- // get distinct ts values. The channels.ts_counter column is kept for
1127
- // back-compat but no longer drives allocation.
1128
- const channel = this.db
1129
- .prepare(`SELECT team_id FROM channels WHERE id = ?`)
1130
- .get(channelId);
1131
- if (!channel)
1132
- throw new Error(`channel ${channelId} not found for ts allocation`);
1133
- const row = this.db
1134
- .prepare(`UPDATE workspaces SET ts_counter = ts_counter + 1 WHERE id = ? RETURNING ts_counter`)
1135
- .get(channel.team_id);
1136
- if (!row)
1137
- throw new Error(`workspace ${channel.team_id} not found for ts allocation`);
1138
- return `${tsBaseSeconds()}.${padTsCounter(row.ts_counter)}`;
1139
- }
1140
- resolveSeedUserId(ref, map) {
1141
- if (!ref)
1142
- return undefined;
1143
- return map.get(ref) ?? ref;
1144
- }
1145
- resolveUser(ref) {
1146
- if (!ref)
1147
- return undefined;
1148
- const byId = this.db.prepare(`SELECT * FROM users WHERE id = ?`).get(ref);
1149
- if (byId)
1150
- return byId;
1151
- const workspace = this.firstWorkspace();
1152
- if (!workspace)
1153
- return undefined;
1154
- const byName = this.db
1155
- .prepare(`SELECT * FROM users WHERE team_id = ? AND name = ?`)
1156
- .get(workspace.id, ref);
1157
- if (byName)
1158
- return byName;
1159
- if (ref.includes("@")) {
1160
- const byEmail = this.db
1161
- .prepare(`SELECT * FROM users WHERE team_id = ? AND email = ?`)
1162
- .get(workspace.id, ref);
1163
- if (byEmail)
1164
- return byEmail;
1165
- }
1166
- return undefined;
1167
- }
1168
- resolveActorUser(actor) {
1169
- if (actor.login) {
1170
- const user = this.resolveUser(actor.login);
1171
- if (!user)
1172
- slackError("user_not_found", 404);
1173
- return user;
1174
- }
1175
- const defaultUser = this.resolveUser("pome-agent");
1176
- if (defaultUser)
1177
- return defaultUser;
1178
- const fallback = this.db.prepare(`SELECT * FROM users WHERE id = ?`).get(DEFAULT_BOT_USER);
1179
- if (fallback)
1180
- return fallback;
1181
- slackError("user_not_found", 404);
1182
- }
1183
- channelRequiresMembership(channel) {
1184
- return Boolean(channel.is_private) || Boolean(channel.is_im) || Boolean(channel.is_mpim);
1185
- }
1186
- assertChannelMember(channel, userId) {
1187
- if (!this.channelRequiresMembership(channel))
1188
- return;
1189
- const member = this.db
1190
- .prepare(`SELECT 1 FROM channel_members WHERE channel_id = ? AND user_id = ?`)
1191
- .get(channel.id, userId);
1192
- if (!member)
1193
- slackError("not_in_channel", 400);
1194
- }
1195
- assertChannelMemberForActor(channel, actor) {
1196
- this.assertChannelMember(channel, this.resolveActorUser(actor).id);
1197
- }
1198
- requireChannel(ref) {
1199
- const byId = this.db.prepare(`SELECT * FROM channels WHERE id = ?`).get(ref);
1200
- if (byId)
1201
- return byId;
1202
- const workspace = this.firstWorkspace();
1203
- if (workspace) {
1204
- const cleaned = ref.startsWith("#") ? ref.slice(1) : ref;
1205
- const byName = this.db
1206
- .prepare(`SELECT * FROM channels WHERE team_id = ? AND name = ?`)
1207
- .get(workspace.id, cleaned);
1208
- if (byName)
1209
- return byName;
1210
- }
1211
- notFound("channel_not_found");
1212
- }
1213
- requireChannelRow(id) {
1214
- const row = this.db.prepare(`SELECT * FROM channels WHERE id = ?`).get(id);
1215
- if (!row)
1216
- slackError("channel_not_found", 404);
1217
- return row;
1218
- }
1219
- requireMessageRow(channelId, ts) {
1220
- const row = this.db
1221
- .prepare(`SELECT * FROM messages WHERE channel_id = ? AND ts = ?`)
1222
- .get(channelId, ts);
1223
- if (!row)
1224
- slackError("message_not_found", 404);
1225
- return row;
1226
- }
1227
- requireScheduledMessage(id) {
1228
- const row = this.db.prepare(`SELECT * FROM scheduled_messages WHERE id = ?`).get(id);
1229
- if (!row)
1230
- slackError("scheduled_message_not_found", 404);
1231
- return row;
1232
- }
1233
- channelMemberIds(channelId) {
1234
- return this.db
1235
- .prepare(`SELECT user_id FROM channel_members WHERE channel_id = ? ORDER BY joined_at, user_id`)
1236
- .all(channelId).map((r) => r.user_id);
1237
- }
1238
- reactionsFor(channelId, ts) {
1239
- return this.db
1240
- .prepare(`SELECT * FROM reactions WHERE channel_id = ? AND message_ts = ? ORDER BY added_at, name, user_id`)
1241
- .all(channelId, ts);
1242
- }
1243
- findDirectChannelBySignature(teamId, dmSignature) {
1244
- const row = this.db
1245
- .prepare(`SELECT * FROM channels WHERE team_id = ? AND dm_signature = ?`)
1246
- .get(teamId, dmSignature);
1247
- return row;
1248
- }
1249
- userNameOf(id) {
1250
- const row = this.db.prepare(`SELECT name FROM users WHERE id = ?`).get(id);
1251
- return row?.name ?? id;
1252
- }
1253
- }
1254
- function clampLimit(value, max, fallback) {
1255
- if (!Number.isFinite(value) || value === undefined)
1256
- return fallback;
1257
- return Math.min(max, Math.max(1, Math.floor(value)));
1258
- }
1259
- function safeParseJson(raw) {
1260
- if (!raw)
1261
- return {};
1262
- try {
1263
- return JSON.parse(raw);
1264
- }
1265
- catch {
1266
- return {};
1267
- }
1268
- }
1269
- function sanitizeJsonString(raw, fallback) {
1270
- try {
1271
- const parsed = JSON.parse(raw);
1272
- return JSON.stringify(parsed);
1273
- }
1274
- catch {
1275
- return fallback;
1276
- }
1277
- }
1278
- function normalizeReactionName(raw) {
1279
- return raw.trim().replace(/^:|:$/g, "");
1280
- }
1281
- function filetypeMimetype(filetype) {
1282
- const mapping = {
1283
- text: "text/plain",
1284
- markdown: "text/markdown",
1285
- json: "application/json",
1286
- pdf: "application/pdf",
1287
- png: "image/png",
1288
- jpg: "image/jpeg",
1289
- jpeg: "image/jpeg",
1290
- gif: "image/gif",
1291
- };
1292
- return mapping[filetype] ?? "application/octet-stream";
1293
- }