@open-mercato/cli 0.6.8-develop.7099.1.7ba2771d06 → 0.7.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 (185) hide show
  1. package/.turbo/turbo-build.log +1 -7
  2. package/dist/agentic/guides/module-facts.json +598 -5508
  3. package/dist/agentic/guides/module-facts.v2.json +598 -5594
  4. package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
  5. package/dist/agentic/guides/modules/api_docs/index.md +1 -1
  6. package/dist/agentic/guides/modules/api_keys/exact-override-targets.md +0 -1
  7. package/dist/agentic/guides/modules/api_keys/index.md +2 -4
  8. package/dist/agentic/guides/modules/attachments/di-registrations-rich.md +5 -6
  9. package/dist/agentic/guides/modules/attachments/di-service-tokens.md +4 -5
  10. package/dist/agentic/guides/modules/attachments/exact-override-targets.md +5 -6
  11. package/dist/agentic/guides/modules/attachments/index.md +2 -2
  12. package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
  13. package/dist/agentic/guides/modules/auth/di-registrations-rich.md +2 -3
  14. package/dist/agentic/guides/modules/auth/di-service-tokens.md +2 -3
  15. package/dist/agentic/guides/modules/auth/exact-override-targets.md +2 -3
  16. package/dist/agentic/guides/modules/auth/index.md +1 -1
  17. package/dist/agentic/guides/modules/business_rules/index.md +1 -1
  18. package/dist/agentic/guides/modules/catalog/incoming-installed-contributions.md +0 -3
  19. package/dist/agentic/guides/modules/catalog/index.md +2 -2
  20. package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
  21. package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
  22. package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
  23. package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
  24. package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
  25. package/dist/agentic/guides/modules/checkout/index.md +1 -1
  26. package/dist/agentic/guides/modules/communication_channels/di-registrations-rich.md +8 -9
  27. package/dist/agentic/guides/modules/communication_channels/exact-override-targets.md +8 -9
  28. package/dist/agentic/guides/modules/communication_channels/incoming-installed-contributions.md +0 -2
  29. package/dist/agentic/guides/modules/communication_channels/index.md +3 -3
  30. package/dist/agentic/guides/modules/configs/index.md +1 -1
  31. package/dist/agentic/guides/modules/content/index.md +1 -1
  32. package/dist/agentic/guides/modules/currencies/index.md +1 -1
  33. package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
  34. package/dist/agentic/guides/modules/customers/active-extension-bindings.md +3 -6
  35. package/dist/agentic/guides/modules/customers/contribution-resolutions.md +0 -1
  36. package/dist/agentic/guides/modules/customers/events.md +1 -2
  37. package/dist/agentic/guides/modules/customers/incoming-installed-contributions.md +0 -7
  38. package/dist/agentic/guides/modules/customers/index.md +4 -4
  39. package/dist/agentic/guides/modules/customers/umes-contributions.md +0 -1
  40. package/dist/agentic/guides/modules/customers/umes-hosts.md +0 -1
  41. package/dist/agentic/guides/modules/dashboards/index.md +1 -1
  42. package/dist/agentic/guides/modules/data_sync/exact-override-targets.md +2 -2
  43. package/dist/agentic/guides/modules/data_sync/index.md +1 -1
  44. package/dist/agentic/guides/modules/data_sync/workers.md +2 -2
  45. package/dist/agentic/guides/modules/design_system/index.md +1 -1
  46. package/dist/agentic/guides/modules/devices/index.md +1 -1
  47. package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
  48. package/dist/agentic/guides/modules/directory/exact-override-targets.md +0 -2
  49. package/dist/agentic/guides/modules/directory/index.md +3 -5
  50. package/dist/agentic/guides/modules/entities/index.md +1 -1
  51. package/dist/agentic/guides/modules/eudr/index.md +1 -1
  52. package/dist/agentic/guides/modules/events/index.md +1 -1
  53. package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
  54. package/dist/agentic/guides/modules/gateway_stripe/exact-override-targets.md +1 -1
  55. package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
  56. package/dist/agentic/guides/modules/gateway_stripe/workers.md +1 -1
  57. package/dist/agentic/guides/modules/generators/index.md +1 -1
  58. package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
  59. package/dist/agentic/guides/modules/integrations/index.md +1 -1
  60. package/dist/agentic/guides/modules/messages/index.md +1 -1
  61. package/dist/agentic/guides/modules/notifications/index.md +1 -1
  62. package/dist/agentic/guides/modules/onboarding/index.md +1 -1
  63. package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
  64. package/dist/agentic/guides/modules/perspectives/index.md +1 -1
  65. package/dist/agentic/guides/modules/planner/index.md +1 -1
  66. package/dist/agentic/guides/modules/portal/index.md +1 -1
  67. package/dist/agentic/guides/modules/progress/index.md +1 -1
  68. package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
  69. package/dist/agentic/guides/modules/query_index/index.md +1 -1
  70. package/dist/agentic/guides/modules/record_locks/index.md +1 -1
  71. package/dist/agentic/guides/modules/resources/index.md +1 -1
  72. package/dist/agentic/guides/modules/sales/active-extension-bindings.md +0 -1
  73. package/dist/agentic/guides/modules/sales/contribution-resolutions.md +0 -1
  74. package/dist/agentic/guides/modules/sales/exact-override-targets.md +4 -5
  75. package/dist/agentic/guides/modules/sales/host-extension-points.md +1 -1
  76. package/dist/agentic/guides/modules/sales/incoming-installed-contributions.md +0 -2
  77. package/dist/agentic/guides/modules/sales/index.md +3 -3
  78. package/dist/agentic/guides/modules/sales/setup.md +1 -1
  79. package/dist/agentic/guides/modules/sales/umes-contributions.md +0 -1
  80. package/dist/agentic/guides/modules/sales/umes-diagnostics.md +0 -1
  81. package/dist/agentic/guides/modules/sales/umes-hosts.md +0 -9
  82. package/dist/agentic/guides/modules/scheduler/cli-commands.md +0 -1
  83. package/dist/agentic/guides/modules/scheduler/exact-override-targets.md +1 -3
  84. package/dist/agentic/guides/modules/scheduler/index.md +2 -2
  85. package/dist/agentic/guides/modules/scheduler/workers.md +1 -2
  86. package/dist/agentic/guides/modules/search/index.md +1 -1
  87. package/dist/agentic/guides/modules/security/index.md +1 -1
  88. package/dist/agentic/guides/modules/security/owned-contract-module-metadata.md +1 -1
  89. package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
  90. package/dist/agentic/guides/modules/sso/index.md +1 -1
  91. package/dist/agentic/guides/modules/staff/index.md +1 -1
  92. package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
  93. package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
  94. package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
  95. package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
  96. package/dist/agentic/guides/modules/translations/index.md +1 -1
  97. package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
  98. package/dist/agentic/guides/modules/webhooks/index.md +1 -1
  99. package/dist/agentic/guides/modules/wms/index.md +1 -1
  100. package/dist/agentic/guides/modules/workflows/index.md +1 -1
  101. package/dist/agentic/guides/reference-module-facts.json +2 -2
  102. package/dist/agentic/guides/reference-modules/example/index.md +1 -1
  103. package/dist/agentic/guides/upstream/manifest.json +1 -1
  104. package/dist/agentic/shared/AGENTS.md.template +3 -3
  105. package/dist/agentic/shared/ai/harness/README.md +2 -2
  106. package/dist/agentic/shared/ai/harness/RELEASE.md +6 -8
  107. package/dist/agentic/shared/ai/harness/cases.json +0 -83
  108. package/dist/agentic/shared/ai/harness/cases.schema.json +4 -4
  109. package/dist/agentic/shared/ai/harness/validators.json +1 -1
  110. package/dist/agentic/shared/ai/skills/om-auto-upgrade-0.6.7-to-0.7.0/SKILL.md +12 -3
  111. package/dist/agentic/shared/scripts/run-agent-harness-release.mjs +11 -37
  112. package/dist/lib/generators/ast/index.js +1 -4
  113. package/dist/lib/generators/ast/index.js.map +2 -2
  114. package/dist/lib/generators/ast/source-file.js +1 -8
  115. package/dist/lib/generators/ast/source-file.js.map +2 -2
  116. package/dist/lib/generators/ast/writers.js +1 -9
  117. package/dist/lib/generators/ast/writers.js.map +2 -2
  118. package/dist/lib/generators/module-registry.js +189 -424
  119. package/dist/lib/generators/module-registry.js.map +2 -2
  120. package/dist/lib/generators/route-manifest-shards.js +1 -4
  121. package/dist/lib/generators/route-manifest-shards.js.map +2 -2
  122. package/dist/lib/spawn.js +2 -3
  123. package/dist/lib/spawn.js.map +2 -2
  124. package/dist/lib/testing/integration.js +24 -162
  125. package/dist/lib/testing/integration.js.map +2 -2
  126. package/dist/mercato.js +0 -14
  127. package/dist/mercato.js.map +2 -2
  128. package/package.json +7 -8
  129. package/src/lib/__tests__/spawn.test.ts +0 -23
  130. package/src/lib/generators/__tests__/metadata-extraction.test.ts +0 -44
  131. package/src/lib/generators/__tests__/module-facts.bc-guard.test.ts +8 -32
  132. package/src/lib/generators/__tests__/module-facts.customers.fixture.test.ts +1 -4
  133. package/src/lib/generators/__tests__/module-facts.extension-hosts.test.ts +1 -1
  134. package/src/lib/generators/__tests__/module-subset.test.ts +72 -96
  135. package/src/lib/generators/__tests__/output-snapshots.test.ts +1 -1
  136. package/src/lib/generators/__tests__/route-manifest-shards.test.ts +0 -15
  137. package/src/lib/generators/__tests__/structural-contracts.test.ts +3 -7
  138. package/src/lib/generators/ast/index.ts +1 -2
  139. package/src/lib/generators/ast/source-file.ts +0 -13
  140. package/src/lib/generators/ast/writers.ts +1 -26
  141. package/src/lib/generators/module-registry.ts +227 -534
  142. package/src/lib/generators/route-manifest-shards.ts +1 -8
  143. package/src/lib/spawn.ts +4 -5
  144. package/src/lib/testing/__tests__/integration.test.ts +0 -435
  145. package/src/lib/testing/integration.ts +32 -254
  146. package/src/mercato.ts +0 -27
  147. package/dist/agentic/guides/modules/api_keys/di-registrations-rich.md +0 -11
  148. package/dist/agentic/guides/modules/api_keys/di-service-tokens.md +0 -11
  149. package/dist/agentic/guides/modules/channel_discord/acl-features.md +0 -13
  150. package/dist/agentic/guides/modules/channel_discord/ai-agents.md +0 -11
  151. package/dist/agentic/guides/modules/channel_discord/backend-pages.md +0 -11
  152. package/dist/agentic/guides/modules/channel_discord/cli-commands.md +0 -13
  153. package/dist/agentic/guides/modules/channel_discord/contribution-resolutions.md +0 -14
  154. package/dist/agentic/guides/modules/channel_discord/di-registrations-rich.md +0 -12
  155. package/dist/agentic/guides/modules/channel_discord/exact-override-targets.md +0 -26
  156. package/dist/agentic/guides/modules/channel_discord/index.md +0 -22
  157. package/dist/agentic/guides/modules/channel_discord/owned-contract-module-metadata.md +0 -11
  158. package/dist/agentic/guides/modules/channel_discord/setup.md +0 -11
  159. package/dist/agentic/guides/modules/channel_discord/umes-contributions.md +0 -14
  160. package/dist/agentic/guides/modules/channel_discord/workers.md +0 -12
  161. package/dist/agentic/guides/modules/directory/di-registrations-rich.md +0 -12
  162. package/dist/agentic/guides/modules/directory/di-service-tokens.md +0 -12
  163. package/dist/agentic/guides/modules/documents/acl-features.md +0 -17
  164. package/dist/agentic/guides/modules/documents/backend-pages.md +0 -13
  165. package/dist/agentic/guides/modules/documents/contribution-resolutions.md +0 -27
  166. package/dist/agentic/guides/modules/documents/di-registrations-rich.md +0 -21
  167. package/dist/agentic/guides/modules/documents/domain-commands.md +0 -37
  168. package/dist/agentic/guides/modules/documents/encryption.md +0 -11
  169. package/dist/agentic/guides/modules/documents/entities.md +0 -21
  170. package/dist/agentic/guides/modules/documents/events.md +0 -25
  171. package/dist/agentic/guides/modules/documents/exact-override-targets.md +0 -39
  172. package/dist/agentic/guides/modules/documents/index.md +0 -25
  173. package/dist/agentic/guides/modules/documents/notifications.md +0 -13
  174. package/dist/agentic/guides/modules/documents/owned-contract-module-metadata.md +0 -11
  175. package/dist/agentic/guides/modules/documents/setup.md +0 -11
  176. package/dist/agentic/guides/modules/documents/umes-contributions.md +0 -27
  177. package/dist/agentic/guides/modules/documents/umes-hosts.md +0 -36
  178. package/src/__tests__/resolve-queue-abandon-hook.test.ts +0 -39
  179. package/src/lib/generators/__tests__/__snapshots__/bootstrap-registrations.test.ts.snap +0 -14
  180. package/src/lib/generators/__tests__/__snapshots__/command-loaders.test.ts.snap +0 -23
  181. package/src/lib/generators/__tests__/ast-writers-escaping.test.ts +0 -64
  182. package/src/lib/generators/__tests__/bootstrap-registrations.test.ts +0 -176
  183. package/src/lib/generators/__tests__/command-loaders.test.ts +0 -239
  184. package/src/lib/generators/__tests__/generated-registry-imports.test.ts +0 -128
  185. package/src/lib/generators/__tests__/registry-variant-parity.test.ts +0 -258
@@ -1,5 +1,4 @@
1
1
  import type { ChildProcess, StdioOptions } from 'node:child_process'
2
- import { spawnSync } from 'node:child_process'
3
2
  import { createServer } from 'node:net'
4
3
  import { existsSync, readFileSync } from 'node:fs'
5
4
  import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises'
@@ -763,56 +762,23 @@ function runNpxCommand(args: string[], environment: NodeJS.ProcessEnv): Promise<
763
762
  })
764
763
  }
765
764
 
766
- export type CapturedOutputProcess = ChildProcess & {
767
- readCapturedOutput?: () => string
768
- }
769
-
770
- export const CAPTURED_OUTPUT_MAX_LENGTH = 64 * 1024
771
-
772
- export function createBoundedOutputBuffer(): { append: (chunk: Buffer | string) => void; read: () => string } {
773
- let buffered = ''
774
- return {
775
- append: (chunk: Buffer | string) => {
776
- buffered += chunk.toString()
777
- if (buffered.length > CAPTURED_OUTPUT_MAX_LENGTH) {
778
- buffered = `…(truncated)…${buffered.slice(-CAPTURED_OUTPUT_MAX_LENGTH)}`
779
- }
780
- },
781
- read: () => buffered,
782
- }
783
- }
784
-
785
- // Both streams are returned when both carry output: a single stderr deprecation notice must not
786
- // hide the stdout tail that usually holds the real startup failure. The 20-line tail in
787
- // `exitError()` does the trimming.
788
- export function formatCapturedOutput(stderrText: string, stdoutText: string): string {
789
- if (stderrText && stdoutText) {
790
- return `--- stderr ---\n${stderrText}\n--- stdout ---\n${stdoutText}`
791
- }
792
- return stderrText || stdoutText
793
- }
794
-
795
765
  function startYarnRawCommand(
796
766
  commandArgs: string[],
797
767
  environment: NodeJS.ProcessEnv,
798
- opts: { silent?: boolean; detached?: boolean } = {},
768
+ opts: { silent?: boolean } = {},
799
769
  cwd: string = projectRootDirectory,
800
- ): CapturedOutputProcess {
770
+ ): ChildProcess {
801
771
  const outputMode: StdioOptions = opts.silent ? ['ignore', 'pipe', 'pipe'] : 'inherit'
802
- const resolvedSpawn = resolveSpawnCommand(resolveYarnBinary(), commandArgs, { detached: opts.detached })
803
- const processHandle: CapturedOutputProcess = spawn(resolvedSpawn.command, resolvedSpawn.args, {
772
+ const resolvedSpawn = resolveSpawnCommand(resolveYarnBinary(), commandArgs)
773
+ const processHandle: ChildProcess = spawn(resolvedSpawn.command, resolvedSpawn.args, {
804
774
  cwd,
805
775
  env: environment,
806
776
  stdio: outputMode,
807
777
  ...resolvedSpawn.spawnOptions,
808
778
  })
809
779
  if (opts.silent) {
810
- const stdoutBuffer = createBoundedOutputBuffer()
811
- const stderrBuffer = createBoundedOutputBuffer()
812
- processHandle.stdout?.on('data', (chunk: Buffer | string) => stdoutBuffer.append(chunk))
813
- processHandle.stderr?.on('data', (chunk: Buffer | string) => stderrBuffer.append(chunk))
814
- processHandle.readCapturedOutput = () =>
815
- formatCapturedOutput(stderrBuffer.read().trim(), stdoutBuffer.read().trim())
780
+ processHandle.stdout?.on('data', () => {})
781
+ processHandle.stderr?.on('data', () => {})
816
782
  }
817
783
  return processHandle
818
784
  }
@@ -820,9 +786,9 @@ function startYarnRawCommand(
820
786
  function startYarnCommand(
821
787
  args: string[],
822
788
  environment: NodeJS.ProcessEnv,
823
- opts: { silent?: boolean; detached?: boolean } = {},
789
+ opts: { silent?: boolean } = {},
824
790
  cwd: string = projectRootDirectory,
825
- ): CapturedOutputProcess {
791
+ ): ChildProcess {
826
792
  return startYarnRawCommand(['run', ...args], environment, opts, cwd)
827
793
  }
828
794
 
@@ -1802,172 +1768,6 @@ function isProcessRunning(processId: number): boolean {
1802
1768
  }
1803
1769
  }
1804
1770
 
1805
- export type ProcessTreeKillDependencies = {
1806
- platform?: NodeJS.Platform
1807
- killPosixProcessGroup?: (pid: number, signal: NodeJS.Signals) => void
1808
- killWindowsProcessTree?: (pid: number, options: { forced: boolean }) => void
1809
- gracePeriodMs?: number
1810
- }
1811
-
1812
- function defaultKillPosixProcessGroup(pid: number, signal: NodeJS.Signals): void {
1813
- process.kill(-pid, signal)
1814
- }
1815
-
1816
- // `/f` is added only on escalation so the Windows path mirrors the POSIX SIGTERM → grace → SIGKILL
1817
- // sequence: the first attempt lets the tree shut down cleanly, the second forces it.
1818
- function defaultKillWindowsProcessTree(pid: number, options: { forced: boolean }): void {
1819
- spawnSync('taskkill', ['/pid', String(pid), '/t', ...(options.forced ? ['/f'] : [])])
1820
- }
1821
-
1822
- export function killProcessTree(
1823
- pid: number,
1824
- signal: NodeJS.Signals,
1825
- dependencies: ProcessTreeKillDependencies = {},
1826
- ): void {
1827
- const platform = dependencies.platform ?? process.platform
1828
- if (platform === 'win32') {
1829
- const killWindowsProcessTree = dependencies.killWindowsProcessTree ?? defaultKillWindowsProcessTree
1830
- killWindowsProcessTree(pid, { forced: signal === 'SIGKILL' })
1831
- return
1832
- }
1833
- const killPosixProcessGroup = dependencies.killPosixProcessGroup ?? defaultKillPosixProcessGroup
1834
- killPosixProcessGroup(pid, signal)
1835
- }
1836
-
1837
- const PROCESS_TREE_KILL_GRACE_PERIOD_MS = 3_000
1838
-
1839
- function killProcessTreeIfRunning(
1840
- pid: number,
1841
- signal: NodeJS.Signals,
1842
- dependencies: ProcessTreeKillDependencies,
1843
- ): void {
1844
- try {
1845
- killProcessTree(pid, signal, dependencies)
1846
- } catch (error) {
1847
- if ((error as NodeJS.ErrnoException).code !== 'ESRCH') {
1848
- throw error
1849
- }
1850
- }
1851
- }
1852
-
1853
- // `getProcessExitPromise` only observes *future* events, so a process that already exited would
1854
- // never settle it and the caller would stall for the whole grace period. This waiter detaches both
1855
- // listeners and clears the timer whichever way it settles, so nothing is left pending and a late
1856
- // `'error'` cannot surface as an unhandled rejection.
1857
- function waitForProcessExitWithin(childProcess: ChildProcess, timeoutMs: number): Promise<boolean> {
1858
- return new Promise((resolve) => {
1859
- const settle = (exited: boolean) => {
1860
- clearTimeout(gracePeriodTimer)
1861
- childProcess.off('exit', onExit)
1862
- childProcess.off('error', onError)
1863
- resolve(exited)
1864
- }
1865
- const onExit = () => settle(true)
1866
- const onError = () => settle(false)
1867
- const gracePeriodTimer = setTimeout(() => settle(false), timeoutMs)
1868
- childProcess.on('exit', onExit)
1869
- childProcess.on('error', onError)
1870
- })
1871
- }
1872
-
1873
- // A `ChildProcess` reports `null` on both fields while it is alive, so a non-null value means the
1874
- // exit has already happened and its `'exit'` event will never fire again.
1875
- function hasProcessAlreadyExited(childProcess: ChildProcess): boolean {
1876
- return (childProcess.exitCode ?? null) !== null || (childProcess.signalCode ?? null) !== null
1877
- }
1878
-
1879
- export async function terminateProcessTree(
1880
- childProcess: CapturedOutputProcess,
1881
- dependencies: ProcessTreeKillDependencies = {},
1882
- ): Promise<void> {
1883
- const pid = childProcess.pid
1884
- if (!pid) return
1885
- // A reaped leader answers only "waiting for its `'exit'` event can never settle" — without this the
1886
- // crash path would stall the whole teardown for the grace period waiting for an event that already
1887
- // fired. It does *not* mean the process group is empty: on the readiness-failure path the `yarn`
1888
- // wrapper exits non-zero while the `mercato start`/Next/worker descendants it spawned stay in its
1889
- // group, so the group still has to be signalled or #5333's orphan survives verbatim. POSIX keeps
1890
- // the group id reserved while any member holds it, so signalling it after the leader is reaped is
1891
- // both safe and effective. Skip the wait, never the signal — and go straight to SIGKILL, since
1892
- // there is no leader left to coordinate a graceful shutdown or to report the exit we would await.
1893
- if (hasProcessAlreadyExited(childProcess)) {
1894
- killProcessTreeIfRunning(pid, 'SIGKILL', dependencies)
1895
- return
1896
- }
1897
-
1898
- killProcessTreeIfRunning(pid, 'SIGTERM', dependencies)
1899
-
1900
- const gracePeriodMs = dependencies.gracePeriodMs ?? PROCESS_TREE_KILL_GRACE_PERIOD_MS
1901
- const exitedBeforeGracePeriod = await waitForProcessExitWithin(childProcess, gracePeriodMs)
1902
-
1903
- if (!exitedBeforeGracePeriod && isProcessRunning(pid)) {
1904
- killProcessTreeIfRunning(pid, 'SIGKILL', dependencies)
1905
- }
1906
- }
1907
-
1908
- const EPHEMERAL_SHUTDOWN_SIGNALS: NodeJS.Signals[] = ['SIGINT', 'SIGTERM']
1909
-
1910
- export type ShutdownProcessRef = Pick<NodeJS.Process, 'once' | 'off' | 'removeAllListeners' | 'kill' | 'pid'>
1911
-
1912
- export type EphemeralShutdownHandlers = { dispose: () => void }
1913
-
1914
- // The application is spawned `detached: true`, which puts its whole tree in a new session. A
1915
- // terminal Ctrl+C only reaches the foreground process group of the controlling terminal, so the
1916
- // tree never sees the signal and would outlive the run holding `server-start.lock` — exactly the
1917
- // orphan this harness exists to prevent. Node also skips `'exit'` listeners when it dies from a
1918
- // signal, so the crash sweep cannot cover this path either. The runner therefore forwards the
1919
- // interrupt itself: stop the environment, then re-raise the signal so the process still exits with
1920
- // the conventional 130/143 instead of a synthetic success.
1921
- export function registerEphemeralShutdownHandlers(options: {
1922
- stop: () => Promise<void>
1923
- killApplicationTree: () => void
1924
- onSignal?: (signal: NodeJS.Signals) => void
1925
- processRef?: ShutdownProcessRef
1926
- }): EphemeralShutdownHandlers {
1927
- const processRef = options.processRef ?? process
1928
- const onProcessExit = () => options.killApplicationTree()
1929
- const signalHandlers = new Map<NodeJS.Signals, () => void>()
1930
-
1931
- // Registered per `startEphemeralEnvironment()` call, and the environment is restarted on retry,
1932
- // so the handlers must come back off in `stop()` or a retried run stacks dead closures until it
1933
- // trips MaxListenersExceededWarning.
1934
- const dispose = () => {
1935
- processRef.off('exit', onProcessExit)
1936
- for (const [signal, handler] of signalHandlers) {
1937
- processRef.off(signal, handler)
1938
- }
1939
- signalHandlers.clear()
1940
- }
1941
-
1942
- for (const signal of EPHEMERAL_SHUTDOWN_SIGNALS) {
1943
- const handler = () => {
1944
- void (async () => {
1945
- options.onSignal?.(signal)
1946
- try {
1947
- await options.stop()
1948
- } catch (error) {
1949
- // A teardown failure must not become an unhandled rejection, and must not swallow the
1950
- // signal: report it and still re-raise so the runner exits the way the shell expects.
1951
- console.error(`Failed to stop the ephemeral environment on ${signal}:`, error)
1952
- } finally {
1953
- dispose()
1954
- // Load-bearing, not cleanup: `dispose()` already detached this module's `once` handler, so
1955
- // the only listeners left belong to somebody else (testcontainers, Playwright, a host CLI).
1956
- // Any survivor would swallow the re-raised signal and turn the interrupt back into the
1957
- // synthetic success this whole path exists to avoid, so they come off before the re-raise.
1958
- processRef.removeAllListeners(signal)
1959
- processRef.kill(processRef.pid as number, signal)
1960
- }
1961
- })()
1962
- }
1963
- signalHandlers.set(signal, handler)
1964
- processRef.once(signal, handler)
1965
- }
1966
-
1967
- processRef.once('exit', onProcessExit)
1968
- return { dispose }
1969
- }
1970
-
1971
1771
  async function getPathAgeMilliseconds(targetPath: string): Promise<number | null> {
1972
1772
  try {
1973
1773
  const stats = await stat(targetPath)
@@ -2294,7 +2094,7 @@ export async function tryReuseExistingEnvironment(options: EphemeralRuntimeOptio
2294
2094
 
2295
2095
  export async function waitForApplicationReadiness(
2296
2096
  baseUrl: string,
2297
- appProcess: CapturedOutputProcess,
2097
+ appProcess: ChildProcess,
2298
2098
  options: { timeoutMs: number; intervalMs?: number; stabilizationMs?: number },
2299
2099
  ): Promise<void> {
2300
2100
  const startTimestamp = Date.now()
@@ -2315,15 +2115,8 @@ export async function waitForApplicationReadiness(
2315
2115
  return { exited: true as const }
2316
2116
  },
2317
2117
  )
2318
- const exitError = () => {
2319
- const capturedOutput = appProcess.readCapturedOutput?.().trim()
2320
- const capturedOutputTail = capturedOutput
2321
- ? `\nCaptured output:\n${capturedOutput.split('\n').slice(-20).join('\n')}`
2322
- : ''
2323
- return new Error(
2324
- `Application process exited before readiness check (exit ${exitCode ?? 'unknown'})${capturedOutputTail}`,
2325
- )
2326
- }
2118
+ const exitError = () =>
2119
+ new Error(`Application process exited before readiness check (exit ${exitCode ?? 'unknown'})`)
2327
2120
 
2328
2121
  while (Date.now() - startTimestamp < options.timeoutMs) {
2329
2122
  // Run one probe cycle to completion before starting the next. Overlapping cycles (the previous
@@ -3627,45 +3420,21 @@ export async function startEphemeralEnvironment(options: EphemeralRuntimeOptions
3627
3420
  })
3628
3421
 
3629
3422
  const runtimeLock = await acquireEphemeralRuntimeLock(options.logPrefix)
3630
- let applicationProcess: CapturedOutputProcess | null = null
3423
+ let applicationProcess: ChildProcess | null = null
3631
3424
  let isStopped = false
3632
- let shutdownHandlers: EphemeralShutdownHandlers | null = null
3633
3425
  const stop = async (): Promise<void> => {
3634
3426
  if (isStopped) return
3635
3427
  isStopped = true
3636
3428
  try {
3637
3429
  if (applicationProcess && !applicationProcess.killed) {
3638
- try {
3639
- await terminateProcessTree(applicationProcess)
3640
- } catch (error) {
3641
- // `killProcessTreeIfRunning` rethrows anything that is not `ESRCH`, and this call sits
3642
- // ahead of the container and state cleanup. A kill that fails must not strand the
3643
- // Postgres container and the state file too — report it and finish tearing down.
3644
- console.error(`[${options.logPrefix}] Failed to terminate the application process tree:`, error)
3645
- }
3430
+ applicationProcess.kill('SIGTERM')
3646
3431
  }
3647
3432
  await databaseContainer.stop()
3648
3433
  await clearEphemeralEnvironmentState()
3649
3434
  } finally {
3650
3435
  await runtimeLock.release()
3651
- shutdownHandlers?.dispose()
3652
- }
3653
- }
3654
- shutdownHandlers = registerEphemeralShutdownHandlers({
3655
- stop,
3656
- onSignal: (signal) =>
3657
- console.log(`[${options.logPrefix}] Received ${signal}, stopping ephemeral environment...`),
3658
- // Deliberately not guarded on `isStopped`: `startEphemeralEnvironment`'s own catch already ran
3659
- // `stop()` before rethrowing, so the guard made the sweep dead on exactly the paths that need
3660
- // it. After a successful `stop()` the extra group kill is a harmless `ESRCH` this swallows.
3661
- killApplicationTree: () => {
3662
- const pid = applicationProcess?.pid
3663
- if (!pid) return
3664
- try {
3665
- killProcessTree(pid, 'SIGKILL')
3666
- } catch {}
3667
- },
3668
- })
3436
+ }
3437
+ }
3669
3438
 
3670
3439
  try {
3671
3440
  const appReadyTimeoutMs = resolveAppReadyTimeoutMs(options.logPrefix)
@@ -3752,7 +3521,6 @@ export async function startEphemeralEnvironment(options: EphemeralRuntimeOptions
3752
3521
  console.log(`[${options.logPrefix}] Starting application on ${applicationBaseUrl}...`)
3753
3522
  const startedAppProcess = startYarnCommand(['start'], commandEnvironment, {
3754
3523
  silent: !options.verbose,
3755
- detached: true,
3756
3524
  }, appDirectory)
3757
3525
  applicationProcess = startedAppProcess
3758
3526
 
@@ -3791,11 +3559,15 @@ export async function startEphemeralEnvironment(options: EphemeralRuntimeOptions
3791
3559
  }
3792
3560
  }
3793
3561
 
3794
- // Interrupt handling belongs to `startEphemeralEnvironment`, which owns the detached tree and now
3795
- // registers SIGINT/SIGTERM handlers for every run — not just the `--keep` ones. Registering a
3796
- // second pair here would race them: both fire on the same signal, and this one's `process.exit()`
3797
- // would cut the environment's teardown short mid-await.
3798
- async function keepEnvironmentRunningForever(): Promise<void> {
3562
+ async function keepEnvironmentRunningForever(options: { logPrefix: string; stop: () => Promise<void> }): Promise<void> {
3563
+ const onSignal = async (signal: string): Promise<void> => {
3564
+ console.log(`[${options.logPrefix}] Received ${signal}, stopping ephemeral environment...`)
3565
+ await options.stop()
3566
+ process.exit(0)
3567
+ }
3568
+
3569
+ process.once('SIGINT', () => void onSignal('SIGINT'))
3570
+ process.once('SIGTERM', () => void onSignal('SIGTERM'))
3799
3571
  await new Promise<void>(() => {})
3800
3572
  }
3801
3573
 
@@ -3845,7 +3617,10 @@ export async function runIntegrationTestsInEphemeralEnvironment(rawArgs: string[
3845
3617
 
3846
3618
  if (options.keep) {
3847
3619
  console.log('[integration] --keep enabled: leaving app and database running. Press Ctrl+C to stop.')
3848
- await keepEnvironmentRunningForever()
3620
+ await keepEnvironmentRunningForever({
3621
+ logPrefix: 'integration',
3622
+ stop: environment.stop,
3623
+ })
3849
3624
  }
3850
3625
  } finally {
3851
3626
  if (!options.keep) {
@@ -3873,7 +3648,10 @@ export async function runEphemeralAppForQa(rawArgs: string[]): Promise<void> {
3873
3648
  console.log('[ephemeral] Reused existing environment. Press Ctrl+C to exit without stopping the shared runtime.')
3874
3649
  }
3875
3650
 
3876
- await keepEnvironmentRunningForever()
3651
+ await keepEnvironmentRunningForever({
3652
+ logPrefix: 'ephemeral',
3653
+ stop: environment.stop,
3654
+ })
3877
3655
  }
3878
3656
 
3879
3657
  export async function runInteractiveIntegrationInEphemeralEnvironment(rawArgs: string[]): Promise<void> {
package/src/mercato.ts CHANGED
@@ -78,29 +78,6 @@ function getRegisteredCliWorkers(modules: Module[] = getCliModules()): ModuleWor
78
78
  return allWorkers
79
79
  }
80
80
 
81
- /**
82
- * Picks the abandoned-job callback for a queue from its workers.
83
- *
84
- * The numeric queue options merge across a queue's workers with `Math.max`; two callbacks cannot
85
- * merge, so the first declared one wins. Silence there would make a genuine wiring mistake — two
86
- * handlers on one queue each expecting to report its abandoned jobs — look like it works while one of
87
- * them never runs.
88
- */
89
- export function resolveQueueAbandonHook(
90
- queueName: string,
91
- queueWorkers: Array<Pick<ModuleWorker, 'id' | 'onJobAbandoned'>>,
92
- warn: (message: string) => void = console.warn,
93
- ): ModuleWorker['onJobAbandoned'] {
94
- const declaring = queueWorkers.filter((worker) => worker.onJobAbandoned)
95
- if (declaring.length > 1) {
96
- warn(
97
- `[worker] Queue "${queueName}" has ${declaring.length} workers declaring onJobAbandoned `
98
- + `(${declaring.map((worker) => worker.id).join(', ')}); using the first and ignoring the rest.`,
99
- )
100
- }
101
- return declaring[0]?.onJobAbandoned
102
- }
103
-
104
81
  function shouldEmbedLocalSchedulerInSharedWorker(env: NodeJS.ProcessEnv = process.env): boolean {
105
82
  return parseBooleanToken(env.OM_DEV_EMBED_SCHEDULER_IN_SHARED_WORKER) === true
106
83
  }
@@ -1701,7 +1678,6 @@ export async function run(argv = process.argv) {
1701
1678
  Math.max(...queueWorkers.map((w) => w.concurrency), 1)
1702
1679
  const maxStalledCount = Math.max(...queueWorkers.map((w) => w.maxStalledCount ?? 1), 1)
1703
1680
  const lockDuration = Math.max(...queueWorkers.map((w) => w.lockDuration ?? 0), 0) || undefined
1704
- const onJobAbandoned = resolveQueueAbandonHook(queue, queueWorkers)
1705
1681
 
1706
1682
  console.log(`[worker] Starting "${queue}" with ${queueWorkers.length} handler(s), concurrency: ${concurrency}`)
1707
1683
 
@@ -1712,7 +1688,6 @@ export async function run(argv = process.argv) {
1712
1688
  concurrency,
1713
1689
  lockDuration,
1714
1690
  maxStalledCount,
1715
- onJobAbandoned,
1716
1691
  background: true,
1717
1692
  handler: createPerJobWorkerHandler(queueWorkers, createRequestContainer),
1718
1693
  })
@@ -1758,7 +1733,6 @@ export async function run(argv = process.argv) {
1758
1733
  const concurrency = budgetPlan.entries[0]?.effective ?? requested
1759
1734
  const maxStalledCount = Math.max(...queueWorkers.map((w) => w.maxStalledCount ?? 1), 1)
1760
1735
  const lockDuration = Math.max(...queueWorkers.map((w) => w.lockDuration ?? 0), 0) || undefined
1761
- const onJobAbandoned = resolveQueueAbandonHook(queueName!, queueWorkers)
1762
1736
 
1763
1737
  console.log(`[worker] Found ${queueWorkers.length} worker(s) for queue "${queueName}"`)
1764
1738
 
@@ -1769,7 +1743,6 @@ export async function run(argv = process.argv) {
1769
1743
  concurrency,
1770
1744
  lockDuration,
1771
1745
  maxStalledCount,
1772
- onJobAbandoned,
1773
1746
  handler: createPerJobWorkerHandler(queueWorkers, createRequestContainer),
1774
1747
  })
1775
1748
  } else {
@@ -1,11 +0,0 @@
1
- # api_keys — DI registrations (rich)
2
-
3
- [Back to module index](index.md)
4
-
5
- ## DI registrations (rich)
6
-
7
- | Token | Kind | Lifetime | Injection | Provider | Source |
8
- |---|---|---|---|---|---|
9
- | apiKeyPrincipalService | class | scoped | — | DefaultApiKeyPrincipalService | [di.ts:6](../../../../node_modules/@open-mercato/core/src/modules/api_keys/di.ts#L6) |
10
-
11
- <!-- end module facts section: api_keys/di-registrations-rich -->
@@ -1,11 +0,0 @@
1
- # api_keys — DI service tokens
2
-
3
- [Back to module index](index.md)
4
-
5
- ## DI service tokens
6
-
7
- | Token | Source |
8
- |---|---|
9
- | apiKeyPrincipalService | [di.ts:6](../../../../node_modules/@open-mercato/core/src/modules/api_keys/di.ts#L6) |
10
-
11
- <!-- end module facts section: api_keys/di-service-tokens -->
@@ -1,13 +0,0 @@
1
- # channel_discord — ACL features
2
-
3
- [Back to module index](index.md)
4
-
5
- ## ACL features (3)
6
-
7
- | Feature | Source |
8
- |---|---|
9
- | channel_discord.view | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
10
- | channel_discord.configure | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
11
- | channel_discord.ai_auto_reply.run | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
12
-
13
- <!-- end module facts section: channel_discord/acl-features -->
@@ -1,11 +0,0 @@
1
- # channel_discord — AI agents
2
-
3
- [Back to module index](index.md)
4
-
5
- ## AI agents
6
-
7
- | ID / path | Source |
8
- |---|---|
9
- | channel_discord.auto_reply | [ai-agents.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/ai-agents.ts) |
10
-
11
- <!-- end module facts section: channel_discord/ai-agents -->
@@ -1,11 +0,0 @@
1
- # channel_discord — Backend pages
2
-
3
- [Back to module index](index.md)
4
-
5
- ## Backend pages
6
-
7
- | ID / path | Source |
8
- |---|---|
9
- | /backend/channel_discord/channels/[id]/ai-auto-reply | [backend/channel_discord/channels/[id]/ai-auto-reply/page.tsx](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/backend/channel_discord/channels/[id]/ai-auto-reply/page.tsx) |
10
-
11
- <!-- end module facts section: channel_discord/backend-pages -->
@@ -1,13 +0,0 @@
1
- # channel_discord — CLI commands
2
-
3
- [Back to module index](index.md)
4
-
5
- ## CLI commands
6
-
7
- | ID / path | Source |
8
- |---|---|
9
- | start-gateway | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
10
- | configure-from-env | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
11
- | register-slash-commands | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
12
-
13
- <!-- end module facts section: channel_discord/cli-commands -->
@@ -1,14 +0,0 @@
1
- # channel_discord — Contribution resolutions
2
-
3
- [Back to module index](index.md)
4
-
5
- ## Contribution resolutions
6
-
7
- | Contribution | Target | Resolution | Activations | Source |
8
- |---|---|---|---|---|
9
- | ai:channel_discord.auto_reply | module:channel_discord.auto_reply | capability-only | — | [ai-agents.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/ai-agents.ts) |
10
- | channel_discord:ai-auto-reply | event:communication_channels.message.received @communication_channels | capability-only | — | [subscribers/ai-auto-reply.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/subscribers/ai-auto-reply.ts) |
11
- | channel_discord.injection.connect@profile:communication-channels:connect | widget-spot:profile:communication-channels:connect @communication_channels | bound | widget-spot:profile:communication-channels:connect:widget-injection-consumer | [widgets/injection-table.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/widgets/injection-table.ts) |
12
- | integration:channel_discord | module:channel_discord | capability-only | — | [integration.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/integration.ts) |
13
-
14
- <!-- end module facts section: channel_discord/contribution-resolutions -->
@@ -1,12 +0,0 @@
1
- # channel_discord — DI registrations (rich)
2
-
3
- [Back to module index](index.md)
4
-
5
- ## DI registrations (rich)
6
-
7
- | Token | Kind | Lifetime | Injection | Provider | Source |
8
- |---|---|---|---|---|---|
9
- | channelDiscordAdapter | value | — | — | — | [di.ts:20](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/di.ts#L20) |
10
- | channelDiscordHealthCheck | value | — | — | — | [di.ts:24](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/di.ts#L24) |
11
-
12
- <!-- end module facts section: channel_discord/di-registrations-rich -->
@@ -1,26 +0,0 @@
1
- # channel_discord — Exact override targets
2
-
3
- [Back to module index](index.md)
4
-
5
- ## Exact override targets
6
-
7
- | Domain | Path / key | Supported modes | Referenced fact | Source |
8
- |---|---|---|---|---|
9
- | acl | `overrides.acl.features["channel_discord.ai_auto_reply.run"]` | disable-replace | aclFeatures:channel_discord.ai_auto_reply.run | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
10
- | acl | `overrides.acl.features["channel_discord.configure"]` | disable-replace | aclFeatures:channel_discord.configure | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
11
- | acl | `overrides.acl.features["channel_discord.view"]` | disable-replace | aclFeatures:channel_discord.view | [acl.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/acl.ts) |
12
- | ai | `overrides.ai.agents["channel_discord.auto_reply"]` | disable-replace | aiAgents:channel_discord.auto_reply | [ai-agents.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/ai-agents.ts) |
13
- | cli | `overrides.cli["configure-from-env"]` | disable-replace | cliCommands:configure-from-env | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
14
- | cli | `overrides.cli["register-slash-commands"]` | disable-replace | cliCommands:register-slash-commands | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
15
- | cli | `overrides.cli["start-gateway"]` | disable-replace | cliCommands:start-gateway | [cli.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/cli.ts) |
16
- | di | `overrides.di["channelDiscordAdapter"]` | disable-replace | ownedContracts.di-registration:channelDiscordAdapter | [di.ts:20](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/di.ts#L20) |
17
- | di | `overrides.di["channelDiscordHealthCheck"]` | disable-replace | ownedContracts.di-registration:channelDiscordHealthCheck | [di.ts:24](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/di.ts#L24) |
18
- | events | `overrides.events.subscribers["channel_discord:ai-auto-reply"]` | disable-replace | extensionSurfaces.contributions:channel_discord:ai-auto-reply | [subscribers/ai-auto-reply.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/subscribers/ai-auto-reply.ts) |
19
- | routes | `overrides.routes.pages["backend:/backend/channel_discord/channels/[id]/ai-auto-reply"]` | disable-replace | backendPages:/backend/channel_discord/channels/[id]/ai-auto-reply | [backend/channel_discord/channels/[id]/ai-auto-reply/page.tsx](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/backend/channel_discord/channels/[id]/ai-auto-reply/page.tsx) |
20
- | setup | `overrides.setup.defaultRoleFeatures` | replace | ownedContracts.setup:channel_discord:setup | [setup.ts:33](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/setup.ts#L33) |
21
- | setup | `overrides.setup.onTenantCreated` | replace | ownedContracts.setup:channel_discord:setup | [setup.ts:33](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/setup.ts#L33) |
22
- | widgets | `overrides.widgets.injection["channel_discord.injection.connect"]` | disable-replace | extensionSurfaces.contributions:channel_discord.injection.connect@profile:communication-channels:connect | [widgets/injection-table.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/widgets/injection-table.ts) |
23
- | workers | `overrides.workers["channel_discord:gateway"]` | disable-replace | ownedContracts.worker:channel_discord:gateway | [workers/discord-gateway.ts:50](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/workers/discord-gateway.ts#L50) |
24
- | workers | `overrides.workers["channel_discord:interactions"]` | disable-replace | ownedContracts.worker:channel_discord:interactions | [workers/discord-interactions.ts:39](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/workers/discord-interactions.ts#L39) |
25
-
26
- <!-- end module facts section: channel_discord/exact-override-targets -->
@@ -1,22 +0,0 @@
1
- # channel_discord — module facts (generated, do not edit)
2
- <!-- generated from @open-mercato/channel-discord 0.6.8-develop.7099.1.7ba2771d06; core 0.6.8-develop.7099.1.7ba2771d06 — R1 staleness stamp -->
3
- Source root: node_modules/@open-mercato/channel-discord/src/modules/channel_discord
4
-
5
- ## Sections
6
-
7
- - [ACL features](acl-features.md) — ~1 KB — Generated acl features facts for channel_discord.
8
- - [Backend pages](backend-pages.md) — ~1 KB — Generated backend pages facts for channel_discord.
9
- - [UMES contributions](umes-contributions.md) — ~2 KB — Generated umes contributions facts for channel_discord.
10
- - [Contribution resolutions](contribution-resolutions.md) — ~2 KB — Generated contribution resolutions facts for channel_discord.
11
- - [CLI commands](cli-commands.md) — ~1 KB — Generated cli commands facts for channel_discord.
12
- - [AI agents](ai-agents.md) — ~1 KB — Generated ai agents facts for channel_discord.
13
- - [Exact override targets](exact-override-targets.md) — ~5 KB — Generated exact override targets facts for channel_discord.
14
- - [Owned contract — module metadata](owned-contract-module-metadata.md) — ~1 KB — Generated owned contract — module metadata facts for channel_discord.
15
- - [Workers](workers.md) — ~1 KB — Generated workers facts for channel_discord.
16
- - [Setup](setup.md) — ~1 KB — Generated setup facts for channel_discord.
17
- - [DI registrations (rich)](di-registrations-rich.md) — ~1 KB — Generated di registrations (rich) facts for channel_discord.
18
-
19
- Read only the section files needed for the task; every section ends with a completion marker.
20
- A section absent from this list has no facts for channel_discord; this index is complete only when the read reaches the marker below.
21
-
22
- <!-- end module facts: channel_discord — 11 sections -->
@@ -1,11 +0,0 @@
1
- # channel_discord — Owned contract — module metadata
2
-
3
- [Back to module index](index.md)
4
-
5
- ## Owned contract — module metadata
6
-
7
- | ID | Metadata | Source |
8
- |---|---|---|
9
- | channel_discord | — | [index.ts:1](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/index.ts#L1) |
10
-
11
- <!-- end module facts section: channel_discord/owned-contract-module-metadata -->
@@ -1,11 +0,0 @@
1
- # channel_discord — Setup
2
-
3
- [Back to module index](index.md)
4
-
5
- ## Setup
6
-
7
- | ID | Metadata | Source |
8
- |---|---|---|
9
- | channel_discord:setup | hooks=onTenantCreated; roles=admin, superadmin | [setup.ts:33](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/setup.ts#L33) |
10
-
11
- <!-- end module facts section: channel_discord/setup -->
@@ -1,14 +0,0 @@
1
- # channel_discord — UMES contributions
2
-
3
- [Back to module index](index.md)
4
-
5
- ## UMES contributions
6
-
7
- | ID | Kind | Target | Phase / operations | Contract | Resolution | Source |
8
- |---|---|---|---|---|---|---|
9
- | ai:channel_discord.auto_reply | specialized-registry | channel_discord.auto_reply | — | registry=ai; registryId=channel_discord.auto_reply; specialistRoute=aiAgents | fact-ref | [ai-agents.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/ai-agents.ts) |
10
- | integration:channel_discord | specialized-registry | channel_discord | — | registry=integration; registryId=channel_discord; specialistRoute=integrations | fact-ref | [integration.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/integration.ts) |
11
- | channel_discord:ai-auto-reply | subscriber | communication_channels.message.received | async-delivery | event=communication_channels.message.received; persistent=true; subscriberId=channel_discord:ai-auto-reply; sync=false | fact-ref | [subscribers/ai-auto-reply.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/subscribers/ai-auto-reply.ts) |
12
- | channel_discord.injection.connect@profile:communication-channels:connect | widget | profile:communication-channels:connect | — | executionGuard=host; payload=render; registryKey=channel_discord.injection.connect | exact | [widgets/injection-table.ts](../../../../node_modules/@open-mercato/channel-discord/src/modules/channel_discord/widgets/injection-table.ts) |
13
-
14
- <!-- end module facts section: channel_discord/umes-contributions -->