@lobu/cli 6.0.1 → 7.0.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 (265) hide show
  1. package/README.md +20 -27
  2. package/dist/bundled-skills/lobu/SKILL.md +11 -11
  3. package/dist/commands/_lib/apply/apply-cmd.d.ts +38 -0
  4. package/dist/commands/_lib/apply/apply-cmd.d.ts.map +1 -1
  5. package/dist/commands/_lib/apply/apply-cmd.js +574 -40
  6. package/dist/commands/_lib/apply/apply-cmd.js.map +1 -1
  7. package/dist/commands/_lib/apply/client.d.ts +180 -1
  8. package/dist/commands/_lib/apply/client.d.ts.map +1 -1
  9. package/dist/commands/_lib/apply/client.js +308 -28
  10. package/dist/commands/_lib/apply/client.js.map +1 -1
  11. package/dist/commands/_lib/apply/desired-state.d.ts +134 -3
  12. package/dist/commands/_lib/apply/desired-state.d.ts.map +1 -1
  13. package/dist/commands/_lib/apply/desired-state.js +703 -89
  14. package/dist/commands/_lib/apply/desired-state.js.map +1 -1
  15. package/dist/commands/_lib/apply/diff.d.ts +61 -3
  16. package/dist/commands/_lib/apply/diff.d.ts.map +1 -1
  17. package/dist/commands/_lib/apply/diff.js +382 -92
  18. package/dist/commands/_lib/apply/diff.js.map +1 -1
  19. package/dist/commands/_lib/apply/prompt.d.ts +6 -0
  20. package/dist/commands/_lib/apply/prompt.d.ts.map +1 -1
  21. package/dist/commands/_lib/apply/prompt.js +16 -0
  22. package/dist/commands/_lib/apply/prompt.js.map +1 -1
  23. package/dist/commands/_lib/apply/render.d.ts +9 -0
  24. package/dist/commands/_lib/apply/render.d.ts.map +1 -1
  25. package/dist/commands/_lib/apply/render.js +80 -3
  26. package/dist/commands/_lib/apply/render.js.map +1 -1
  27. package/dist/commands/agent.d.ts +7 -0
  28. package/dist/commands/agent.d.ts.map +1 -1
  29. package/dist/commands/agent.js +65 -1
  30. package/dist/commands/agent.js.map +1 -1
  31. package/dist/commands/chat.d.ts +12 -9
  32. package/dist/commands/chat.d.ts.map +1 -1
  33. package/dist/commands/chat.js +125 -57
  34. package/dist/commands/chat.js.map +1 -1
  35. package/dist/commands/dev.d.ts +23 -7
  36. package/dist/commands/dev.d.ts.map +1 -1
  37. package/dist/commands/dev.js +197 -49
  38. package/dist/commands/dev.js.map +1 -1
  39. package/dist/commands/doctor.d.ts +1 -0
  40. package/dist/commands/doctor.d.ts.map +1 -1
  41. package/dist/commands/doctor.js +136 -0
  42. package/dist/commands/doctor.js.map +1 -1
  43. package/dist/commands/eval.d.ts +8 -0
  44. package/dist/commands/eval.d.ts.map +1 -1
  45. package/dist/commands/eval.js +72 -6
  46. package/dist/commands/eval.js.map +1 -1
  47. package/dist/commands/init.d.ts +22 -5
  48. package/dist/commands/init.d.ts.map +1 -1
  49. package/dist/commands/init.js +355 -182
  50. package/dist/commands/init.js.map +1 -1
  51. package/dist/commands/link.d.ts +11 -0
  52. package/dist/commands/link.d.ts.map +1 -0
  53. package/dist/commands/link.js +28 -0
  54. package/dist/commands/link.js.map +1 -0
  55. package/dist/commands/login.d.ts.map +1 -1
  56. package/dist/commands/login.js +14 -2
  57. package/dist/commands/login.js.map +1 -1
  58. package/dist/commands/memory/_lib/browser-auth-cmd.d.ts.map +1 -1
  59. package/dist/commands/memory/_lib/browser-auth-cmd.js +3 -3
  60. package/dist/commands/memory/_lib/browser-auth-cmd.js.map +1 -1
  61. package/dist/commands/memory/_lib/mcp.d.ts +2 -2
  62. package/dist/commands/memory/_lib/mcp.d.ts.map +1 -1
  63. package/dist/commands/memory/_lib/mcp.js +24 -12
  64. package/dist/commands/memory/_lib/mcp.js.map +1 -1
  65. package/dist/commands/memory/_lib/openclaw-auth.d.ts +1 -0
  66. package/dist/commands/memory/_lib/openclaw-auth.d.ts.map +1 -1
  67. package/dist/commands/memory/_lib/openclaw-auth.js +14 -3
  68. package/dist/commands/memory/_lib/openclaw-auth.js.map +1 -1
  69. package/dist/commands/memory/_lib/openclaw-cmd.js +1 -1
  70. package/dist/commands/memory/_lib/openclaw-cmd.js.map +1 -1
  71. package/dist/commands/memory/_lib/schema.d.ts +29 -2
  72. package/dist/commands/memory/_lib/schema.d.ts.map +1 -1
  73. package/dist/commands/memory/_lib/schema.js +121 -5
  74. package/dist/commands/memory/_lib/schema.js.map +1 -1
  75. package/dist/commands/memory/_lib/seed-cmd.d.ts.map +1 -1
  76. package/dist/commands/memory/_lib/seed-cmd.js +46 -24
  77. package/dist/commands/memory/_lib/seed-cmd.js.map +1 -1
  78. package/dist/commands/memory/run.d.ts.map +1 -1
  79. package/dist/commands/memory/run.js +2 -2
  80. package/dist/commands/memory/run.js.map +1 -1
  81. package/dist/commands/org.d.ts +4 -0
  82. package/dist/commands/org.d.ts.map +1 -1
  83. package/dist/commands/org.js +10 -0
  84. package/dist/commands/org.js.map +1 -1
  85. package/dist/commands/platforms/platform-prompts.d.ts +0 -1
  86. package/dist/commands/platforms/platform-prompts.d.ts.map +1 -1
  87. package/dist/commands/platforms/platform-prompts.js +54 -8
  88. package/dist/commands/platforms/platform-prompts.js.map +1 -1
  89. package/dist/commands/telemetry.d.ts +10 -0
  90. package/dist/commands/telemetry.d.ts.map +1 -0
  91. package/dist/commands/telemetry.js +68 -0
  92. package/dist/commands/telemetry.js.map +1 -0
  93. package/dist/commands/token.d.ts +9 -0
  94. package/dist/commands/token.d.ts.map +1 -1
  95. package/dist/commands/token.js +54 -0
  96. package/dist/commands/token.js.map +1 -1
  97. package/dist/commands/whoami.d.ts.map +1 -1
  98. package/dist/commands/whoami.js +1 -1
  99. package/dist/commands/whoami.js.map +1 -1
  100. package/dist/connectors/README.md +534 -0
  101. package/dist/connectors/__tests__/browser-scraper-utils.test.ts +186 -0
  102. package/dist/connectors/apple_health.ts +138 -0
  103. package/dist/connectors/apple_screen_time.ts +82 -0
  104. package/dist/connectors/browser-scraper-utils.ts +246 -0
  105. package/dist/connectors/capterra.ts +277 -0
  106. package/dist/connectors/g2.ts +290 -0
  107. package/dist/connectors/github.ts +1530 -0
  108. package/dist/connectors/glassdoor.ts +295 -0
  109. package/dist/connectors/gmaps.ts +197 -0
  110. package/dist/connectors/google_calendar.ts +641 -0
  111. package/dist/connectors/google_gmail.ts +754 -0
  112. package/dist/connectors/google_photos.ts +776 -0
  113. package/dist/connectors/google_play.ts +349 -0
  114. package/dist/connectors/hackernews.ts +471 -0
  115. package/dist/connectors/index.ts +28 -0
  116. package/dist/connectors/ios_appstore.ts +226 -0
  117. package/dist/connectors/linkedin.ts +494 -0
  118. package/dist/connectors/local_directory.ts +91 -0
  119. package/dist/connectors/microsoft_outlook.ts +410 -0
  120. package/dist/connectors/producthunt.ts +471 -0
  121. package/dist/connectors/reddit.ts +600 -0
  122. package/dist/connectors/revolut.ts +572 -0
  123. package/dist/connectors/rss.ts +448 -0
  124. package/dist/connectors/spotify.ts +590 -0
  125. package/dist/connectors/trustpilot.ts +203 -0
  126. package/dist/connectors/website.ts +629 -0
  127. package/dist/connectors/whatsapp.ts +1081 -0
  128. package/dist/connectors/whatsapp_local.ts +125 -0
  129. package/dist/connectors/x.ts +536 -0
  130. package/dist/connectors/youtube.ts +666 -0
  131. package/dist/db/migrations/00000000000000_baseline.sql +4867 -0
  132. package/dist/db/migrations/20260405193000_add_mcp_sessions.sql +33 -0
  133. package/dist/db/migrations/20260408120000_remove_system_connectors.sql +48 -0
  134. package/dist/db/migrations/20260408120001_optional_compiled_code.sql +6 -0
  135. package/dist/db/migrations/20260409110000_add_active_watcher_run_index.sql +9 -0
  136. package/dist/db/migrations/20260409130000_connector_default_config.sql +5 -0
  137. package/dist/db/migrations/20260410120000_add_agent_secrets.sql +25 -0
  138. package/dist/db/migrations/20260413170000_add_watcher_group_id.sql +67 -0
  139. package/dist/db/migrations/20260416120000_add_entity_wa_jid_index.sql +14 -0
  140. package/dist/db/migrations/20260417100000_add_entity_identities.sql +77 -0
  141. package/dist/db/migrations/20260418100000_add_auth_runs.sql +83 -0
  142. package/dist/db/migrations/20260418110000_add_runs_created_by_user.sql +18 -0
  143. package/dist/db/migrations/20260419120000_add_event_identity_indexes.sql +56 -0
  144. package/dist/db/migrations/20260420120000_extend_reserved_org_slugs.sql +56 -0
  145. package/dist/db/migrations/20260424030000_add_watcher_run_correlation.sql +52 -0
  146. package/dist/db/migrations/20260424130000_relax_events_client_id_fk.sql +47 -0
  147. package/dist/db/migrations/20260425100000_normalize_watcher_feedback.sql +91 -0
  148. package/dist/db/migrations/20260425120000_add_run_diagnostics.sql +20 -0
  149. package/dist/db/migrations/20260425130000_add_repair_agent_plumbing.sql +46 -0
  150. package/dist/db/migrations/20260426120000_entities_entity_type_fk.sql +101 -0
  151. package/dist/db/migrations/20260426130000_db_integrity_cleanup.sql +104 -0
  152. package/dist/db/migrations/20260426130001_db_integrity_cleanup_concurrent.sql +187 -0
  153. package/dist/db/migrations/20260427133000_events_created_by_nullable.sql +74 -0
  154. package/dist/db/migrations/20260427140000_identity_engine_indexes.sql +140 -0
  155. package/dist/db/migrations/20260427150000_drop_events_source_id.sql +177 -0
  156. package/dist/db/migrations/20260427160000_drop_dead_schema.sql +76 -0
  157. package/dist/db/migrations/20260427170000_market_founder_to_member.sql +364 -0
  158. package/dist/db/migrations/20260428040000_cascade_events_watchers_org_fk.sql +66 -0
  159. package/dist/db/migrations/20260428050000_add_runs_approved_input.sql +9 -0
  160. package/dist/db/migrations/20260429010000_auth_profile_tenant_scoped_fk.sql +79 -0
  161. package/dist/db/migrations/20260429060000_extend_runs_for_lobu_queue.sql +108 -0
  162. package/dist/db/migrations/20260429120000_agent_changed_notify.sql +97 -0
  163. package/dist/db/migrations/20260429120100_user_auth_profiles_and_model_prefs.sql +36 -0
  164. package/dist/db/migrations/20260429120200_fix_notify_old_keys.sql +130 -0
  165. package/dist/db/migrations/20260429130000_oauth_states_cli_sessions_rate_limits.sql +83 -0
  166. package/dist/db/migrations/20260429140000_phase8_grants_chat_connections_mcp_sessions.sql +84 -0
  167. package/dist/db/migrations/20260429140100_runs_priority_expires_at_retry_delay.sql +44 -0
  168. package/dist/db/migrations/20260429180000_drop_invalidatable_cache_triggers.sql +25 -0
  169. package/dist/db/migrations/20260430005614_agents_apply_fields.sql +21 -0
  170. package/dist/db/migrations/20260430022231_fix_connection_config_encryption.sql +69 -0
  171. package/dist/db/migrations/20260430151215_add_task_run_type.sql +77 -0
  172. package/dist/db/migrations/20260501000000_drop_cli_sessions.sql +27 -0
  173. package/dist/db/migrations/20260501133000_lobu_memory_mcp_id.sql +117 -0
  174. package/dist/db/migrations/20260502000000_drop_chat_connections.sql +60 -0
  175. package/dist/db/migrations/20260503000000_agent_secrets_org_scope.sql +56 -0
  176. package/dist/db/migrations/20260504000000_flatten_agents_drop_sandbox_model.sql +48 -0
  177. package/dist/db/migrations/20260510220000_connector_required_capability.sql +47 -0
  178. package/dist/db/migrations/20260512000000_device_worker_connection_binding.sql +113 -0
  179. package/dist/db/migrations/20260512131703_connections_slug.sql +131 -0
  180. package/dist/db/migrations/20260513000000_chat_user_identities.sql +24 -0
  181. package/dist/db/migrations/20260513120000_auth_profiles_device_binding.sql +50 -0
  182. package/dist/db/migrations/20260513150000_auth_profiles_cdp_url.sql +43 -0
  183. package/dist/db/migrations/20260513200000_notifications_as_events.sql +86 -0
  184. package/dist/db/migrations/20260514000000_scheduled_jobs.sql +97 -0
  185. package/dist/db/migrations/20260514120000_auth_profiles_connector_key_nullable.sql +42 -0
  186. package/dist/eval/types.d.ts +2 -0
  187. package/dist/eval/types.d.ts.map +1 -1
  188. package/dist/index.d.ts +11 -0
  189. package/dist/index.d.ts.map +1 -1
  190. package/dist/index.js +210 -132
  191. package/dist/index.js.map +1 -1
  192. package/dist/internal/api-client.d.ts +4 -8
  193. package/dist/internal/api-client.d.ts.map +1 -1
  194. package/dist/internal/api-client.js +1 -1
  195. package/dist/internal/api-client.js.map +1 -1
  196. package/dist/internal/context.js +2 -2
  197. package/dist/internal/context.js.map +1 -1
  198. package/dist/internal/credentials.d.ts.map +1 -1
  199. package/dist/internal/credentials.js +6 -1
  200. package/dist/internal/credentials.js.map +1 -1
  201. package/dist/internal/gateway-url.d.ts +14 -0
  202. package/dist/internal/gateway-url.d.ts.map +1 -1
  203. package/dist/internal/gateway-url.js +19 -0
  204. package/dist/internal/gateway-url.js.map +1 -1
  205. package/dist/internal/index.d.ts +3 -4
  206. package/dist/internal/index.d.ts.map +1 -1
  207. package/dist/internal/index.js +3 -3
  208. package/dist/internal/index.js.map +1 -1
  209. package/dist/internal/oauth.d.ts +6 -5
  210. package/dist/internal/oauth.d.ts.map +1 -1
  211. package/dist/internal/oauth.js +2 -2
  212. package/dist/internal/project-link.d.ts +10 -0
  213. package/dist/internal/project-link.d.ts.map +1 -0
  214. package/dist/internal/project-link.js +48 -0
  215. package/dist/internal/project-link.js.map +1 -0
  216. package/dist/providers.json +2 -2
  217. package/dist/server.bundle.mjs +31654 -30866
  218. package/dist/start-local.bundle.mjs +74409 -0
  219. package/dist/templates/README.md.tmpl +10 -11
  220. package/dist/templates/TESTING.md.tmpl +9 -9
  221. package/package.json +15 -13
  222. package/dist/__tests__/chat.integration.test.d.ts +0 -2
  223. package/dist/__tests__/chat.integration.test.d.ts.map +0 -1
  224. package/dist/__tests__/chat.integration.test.js +0 -337
  225. package/dist/__tests__/chat.integration.test.js.map +0 -1
  226. package/dist/__tests__/dev.test.d.ts +0 -2
  227. package/dist/__tests__/dev.test.d.ts.map +0 -1
  228. package/dist/__tests__/dev.test.js +0 -25
  229. package/dist/__tests__/dev.test.js.map +0 -1
  230. package/dist/__tests__/init-memory.test.d.ts +0 -2
  231. package/dist/__tests__/init-memory.test.d.ts.map +0 -1
  232. package/dist/__tests__/init-memory.test.js +0 -45
  233. package/dist/__tests__/init-memory.test.js.map +0 -1
  234. package/dist/__tests__/token.test.d.ts +0 -2
  235. package/dist/__tests__/token.test.d.ts.map +0 -1
  236. package/dist/__tests__/token.test.js +0 -52
  237. package/dist/__tests__/token.test.js.map +0 -1
  238. package/dist/commands/_lib/apply/__tests__/client.test.d.ts +0 -2
  239. package/dist/commands/_lib/apply/__tests__/client.test.d.ts.map +0 -1
  240. package/dist/commands/_lib/apply/__tests__/client.test.js +0 -23
  241. package/dist/commands/_lib/apply/__tests__/client.test.js.map +0 -1
  242. package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts +0 -2
  243. package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts.map +0 -1
  244. package/dist/commands/_lib/apply/__tests__/desired-state.test.js +0 -140
  245. package/dist/commands/_lib/apply/__tests__/desired-state.test.js.map +0 -1
  246. package/dist/commands/_lib/apply/__tests__/diff.test.d.ts +0 -2
  247. package/dist/commands/_lib/apply/__tests__/diff.test.d.ts.map +0 -1
  248. package/dist/commands/_lib/apply/__tests__/diff.test.js +0 -378
  249. package/dist/commands/_lib/apply/__tests__/diff.test.js.map +0 -1
  250. package/dist/commands/apply.d.ts +0 -3
  251. package/dist/commands/apply.d.ts.map +0 -1
  252. package/dist/commands/apply.js +0 -5
  253. package/dist/commands/apply.js.map +0 -1
  254. package/dist/commands/memory/_lib/openclaw-auth.test.d.ts +0 -2
  255. package/dist/commands/memory/_lib/openclaw-auth.test.d.ts.map +0 -1
  256. package/dist/commands/memory/_lib/openclaw-auth.test.js +0 -9
  257. package/dist/commands/memory/_lib/openclaw-auth.test.js.map +0 -1
  258. package/dist/internal/__tests__/api-client.test.d.ts +0 -2
  259. package/dist/internal/__tests__/api-client.test.d.ts.map +0 -1
  260. package/dist/internal/__tests__/api-client.test.js +0 -95
  261. package/dist/internal/__tests__/api-client.test.js.map +0 -1
  262. package/dist/internal/__tests__/context.test.d.ts +0 -2
  263. package/dist/internal/__tests__/context.test.d.ts.map +0 -1
  264. package/dist/internal/__tests__/context.test.js +0 -77
  265. package/dist/internal/__tests__/context.test.js.map +0 -1
@@ -0,0 +1,33 @@
1
+ -- migrate:up
2
+
3
+ CREATE TABLE public.mcp_sessions (
4
+ session_id text PRIMARY KEY,
5
+ user_id text,
6
+ client_id text,
7
+ organization_id text,
8
+ member_role text,
9
+ requested_agent_id text,
10
+ is_authenticated boolean DEFAULT false NOT NULL,
11
+ scoped_to_org boolean DEFAULT false NOT NULL,
12
+ last_accessed_at timestamp with time zone DEFAULT now() NOT NULL,
13
+ expires_at timestamp with time zone NOT NULL
14
+ );
15
+
16
+ CREATE INDEX mcp_sessions_client_id_idx ON public.mcp_sessions USING btree (client_id);
17
+ CREATE INDEX mcp_sessions_expires_at_idx ON public.mcp_sessions USING btree (expires_at);
18
+ CREATE INDEX mcp_sessions_user_id_idx ON public.mcp_sessions USING btree (user_id);
19
+
20
+ ALTER TABLE ONLY public.mcp_sessions
21
+ ADD CONSTRAINT mcp_sessions_client_id_fkey FOREIGN KEY (client_id) REFERENCES public.oauth_clients(id) ON DELETE CASCADE;
22
+
23
+ ALTER TABLE ONLY public.mcp_sessions
24
+ ADD CONSTRAINT mcp_sessions_organization_id_fkey FOREIGN KEY (organization_id) REFERENCES public.organization(id) ON DELETE CASCADE;
25
+
26
+ ALTER TABLE ONLY public.mcp_sessions
27
+ ADD CONSTRAINT mcp_sessions_user_id_fkey FOREIGN KEY (user_id) REFERENCES public."user"(id) ON DELETE CASCADE;
28
+
29
+ COMMENT ON TABLE public.mcp_sessions IS 'Persisted MCP streamable HTTP sessions for restart and cross-replica recovery';
30
+
31
+ -- migrate:down
32
+
33
+ DROP TABLE IF EXISTS public.mcp_sessions;
@@ -0,0 +1,48 @@
1
+ -- migrate:up
2
+
3
+ -- Migrate system connector definitions to org-scoped.
4
+ -- For every org, copy each active system connector definition that
5
+ -- the org doesn't already have (regardless of whether connections exist).
6
+
7
+ INSERT INTO connector_definitions (
8
+ organization_id, key, name, description, version,
9
+ auth_schema, feeds_schema, actions_schema, options_schema,
10
+ mcp_config, openapi_config, favicon_domain, status, login_enabled
11
+ )
12
+ SELECT
13
+ o.id,
14
+ cd.key,
15
+ cd.name,
16
+ cd.description,
17
+ cd.version,
18
+ cd.auth_schema,
19
+ cd.feeds_schema,
20
+ cd.actions_schema,
21
+ cd.options_schema,
22
+ cd.mcp_config,
23
+ cd.openapi_config,
24
+ cd.favicon_domain,
25
+ cd.status,
26
+ cd.login_enabled
27
+ FROM connector_definitions cd
28
+ CROSS JOIN "organization" o
29
+ WHERE cd.organization_id IS NULL
30
+ AND cd.status = 'active'
31
+ AND NOT EXISTS (
32
+ SELECT 1
33
+ FROM connector_definitions existing
34
+ WHERE existing.organization_id = o.id
35
+ AND existing.key = cd.key
36
+ AND existing.status = 'active'
37
+ );
38
+
39
+ -- Archive all system-level connector definitions
40
+ UPDATE connector_definitions
41
+ SET status = 'archived', updated_at = NOW()
42
+ WHERE organization_id IS NULL
43
+ AND status = 'active';
44
+
45
+ -- Drop orphaned index that only covered system-level (org IS NULL) definitions
46
+ DROP INDEX IF EXISTS idx_connector_defs_system_key;
47
+
48
+ -- migrate:down
@@ -0,0 +1,6 @@
1
+ -- migrate:up
2
+ ALTER TABLE connector_versions ALTER COLUMN compiled_code DROP NOT NULL;
3
+
4
+ -- migrate:down
5
+ UPDATE connector_versions SET compiled_code = '' WHERE compiled_code IS NULL;
6
+ ALTER TABLE connector_versions ALTER COLUMN compiled_code SET NOT NULL;
@@ -0,0 +1,9 @@
1
+ -- migrate:up
2
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_runs_active_watcher_per_watcher
3
+ ON runs (watcher_id)
4
+ WHERE run_type = 'watcher'
5
+ AND watcher_id IS NOT NULL
6
+ AND status IN ('pending', 'claimed', 'running');
7
+
8
+ -- migrate:down
9
+ DROP INDEX IF EXISTS idx_runs_active_watcher_per_watcher;
@@ -0,0 +1,5 @@
1
+ -- migrate:up
2
+ ALTER TABLE connector_definitions ADD COLUMN IF NOT EXISTS default_connection_config jsonb;
3
+
4
+ -- migrate:down
5
+ ALTER TABLE connector_definitions DROP COLUMN IF EXISTS default_connection_config;
@@ -0,0 +1,25 @@
1
+ -- migrate:up
2
+
3
+ CREATE TABLE public.agent_secrets (
4
+ name text PRIMARY KEY,
5
+ ciphertext text NOT NULL,
6
+ expires_at timestamp with time zone,
7
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
8
+ updated_at timestamp with time zone DEFAULT now() NOT NULL
9
+ );
10
+
11
+ -- text_pattern_ops index enables efficient prefix scans used by list(prefix)
12
+ -- to support cascade deletes (deleteSecretsByPrefix in @lobu/gateway).
13
+ CREATE INDEX agent_secrets_name_prefix_idx
14
+ ON public.agent_secrets USING btree (name text_pattern_ops);
15
+
16
+ CREATE INDEX agent_secrets_expires_at_idx
17
+ ON public.agent_secrets USING btree (expires_at)
18
+ WHERE expires_at IS NOT NULL;
19
+
20
+ COMMENT ON TABLE public.agent_secrets IS
21
+ 'Encrypted secret values referenced via secret:// refs. Backs the PostgresSecretStore implementation of @lobu/gateway WritableSecretStore.';
22
+
23
+ -- migrate:down
24
+
25
+ DROP TABLE IF EXISTS public.agent_secrets;
@@ -0,0 +1,67 @@
1
+ -- migrate:up
2
+
3
+ ALTER TABLE public.watchers
4
+ ADD COLUMN IF NOT EXISTS source_watcher_id integer,
5
+ ADD COLUMN IF NOT EXISTS watcher_group_id integer;
6
+
7
+ -- Backfill source watcher links from cloned watcher versions.
8
+ WITH derived_source AS (
9
+ SELECT DISTINCT ON (w.id)
10
+ w.id AS watcher_id,
11
+ source_versions.watcher_id AS source_watcher_id
12
+ FROM public.watchers w
13
+ JOIN public.watcher_versions wv ON wv.watcher_id = w.id
14
+ JOIN public.watcher_versions source_versions
15
+ ON source_versions.id = substring(wv.change_notes FROM 'Created from version ([0-9]+)')::integer
16
+ WHERE wv.change_notes ~ 'Created from version [0-9]+'
17
+ ORDER BY w.id, wv.version ASC, wv.id ASC
18
+ )
19
+ UPDATE public.watchers w
20
+ SET source_watcher_id = ds.source_watcher_id
21
+ FROM derived_source ds
22
+ WHERE w.id = ds.watcher_id
23
+ AND w.source_watcher_id IS NULL;
24
+
25
+ -- Build stable watcher group ids by walking source watcher ancestry to the root.
26
+ WITH RECURSIVE roots AS (
27
+ SELECT w.id AS watcher_id, w.source_watcher_id, w.id AS root_id
28
+ FROM public.watchers w
29
+ WHERE w.source_watcher_id IS NULL
30
+
31
+ UNION ALL
32
+
33
+ SELECT child.id AS watcher_id, child.source_watcher_id, roots.root_id
34
+ FROM public.watchers child
35
+ JOIN roots ON child.source_watcher_id = roots.watcher_id
36
+ )
37
+ UPDATE public.watchers w
38
+ SET watcher_group_id = roots.root_id
39
+ FROM roots
40
+ WHERE w.id = roots.watcher_id;
41
+
42
+ -- Fallback for any rows that did not match the recursive backfill.
43
+ UPDATE public.watchers
44
+ SET watcher_group_id = id
45
+ WHERE watcher_group_id IS NULL;
46
+
47
+ -- `watchers.current_version_id` has a DEFERRABLE FK. The backfill updates above queue
48
+ -- deferred trigger events, and PostgreSQL refuses ALTER TABLE while those are pending.
49
+ SET CONSTRAINTS ALL IMMEDIATE;
50
+
51
+ ALTER TABLE public.watchers
52
+ ALTER COLUMN watcher_group_id SET NOT NULL;
53
+
54
+ CREATE INDEX IF NOT EXISTS idx_watchers_watcher_group_id
55
+ ON public.watchers USING btree (watcher_group_id);
56
+
57
+ CREATE INDEX IF NOT EXISTS idx_watchers_org_group
58
+ ON public.watchers USING btree (organization_id, watcher_group_id);
59
+
60
+ -- migrate:down
61
+
62
+ DROP INDEX IF EXISTS idx_watchers_org_group;
63
+ DROP INDEX IF EXISTS idx_watchers_watcher_group_id;
64
+
65
+ ALTER TABLE public.watchers
66
+ DROP COLUMN IF EXISTS watcher_group_id,
67
+ DROP COLUMN IF EXISTS source_watcher_id;
@@ -0,0 +1,14 @@
1
+ -- migrate:up
2
+
3
+ -- Speeds up the event↔entity join declared by the WhatsApp connector's
4
+ -- early entityLinks rule: events JOIN entities ON
5
+ -- entities.metadata->>'wa_jid' = events.metadata->>'chat_jid'.
6
+ -- Superseded by the entity_identities table (2026-04-17 migration) which
7
+ -- drops this index; this file is kept so dbmate's history is preserved.
8
+ CREATE INDEX IF NOT EXISTS idx_entities_wa_jid
9
+ ON public.entities (entity_type, (metadata->>'wa_jid'))
10
+ WHERE metadata ? 'wa_jid' AND deleted_at IS NULL;
11
+
12
+ -- migrate:down
13
+
14
+ DROP INDEX IF EXISTS public.idx_entities_wa_jid;
@@ -0,0 +1,77 @@
1
+ -- migrate:up
2
+
3
+ -- Normalized identifier store. One row per (organization_id, namespace, identifier).
4
+ -- Replaces the earlier scheme of storing identifiers inside entities.metadata JSONB
5
+ -- arrays. The UNIQUE constraint is the foundation of the whole design:
6
+ -- * creation races collapse on the UNIQUE (one batch wins, the other links)
7
+ -- * cross-entity contamination is constraint-blocked
8
+ -- * accrete is just INSERT ... ON CONFLICT DO NOTHING
9
+ -- * lookup is a plain BTREE seek (no GIN-on-jsonb)
10
+ CREATE TABLE public.entity_identities (
11
+ id bigserial PRIMARY KEY,
12
+ organization_id text NOT NULL REFERENCES public.organization(id) ON DELETE CASCADE,
13
+ entity_id bigint NOT NULL REFERENCES public.entities(id) ON DELETE CASCADE,
14
+ namespace text NOT NULL,
15
+ identifier text NOT NULL,
16
+ source_connector text,
17
+ created_at timestamp with time zone DEFAULT now() NOT NULL,
18
+ updated_at timestamp with time zone DEFAULT now() NOT NULL,
19
+ deleted_at timestamp with time zone
20
+ );
21
+
22
+ -- Relaxed uniqueness: only live rows are constrained, so soft-delete + re-claim
23
+ -- is a supported repair path (mis-attribution recovery).
24
+ CREATE UNIQUE INDEX idx_entity_identities_live_unique
25
+ ON public.entity_identities (organization_id, namespace, identifier)
26
+ WHERE deleted_at IS NULL;
27
+
28
+ -- Primary ingestion lookup path: "does anyone in this org own this identifier?"
29
+ CREATE INDEX idx_entity_identities_lookup
30
+ ON public.entity_identities (organization_id, namespace, identifier)
31
+ WHERE deleted_at IS NULL;
32
+
33
+ -- Reverse lookup: "what identifiers does this entity have?"
34
+ CREATE INDEX idx_entity_identities_by_entity
35
+ ON public.entity_identities (entity_id)
36
+ WHERE deleted_at IS NULL;
37
+
38
+ COMMENT ON TABLE public.entity_identities IS
39
+ 'Normalized identifier claims per entity. See docs/identity-linking.md for the full pattern.';
40
+ COMMENT ON COLUMN public.entity_identities.namespace IS
41
+ 'Identifier kind. Standard values: phone, email, wa_jid, slack_user_id, github_login, auth_user_id, google_contact_id. Custom namespaces allowed but connectors sharing a namespace must agree on its format.';
42
+ COMMENT ON COLUMN public.entity_identities.identifier IS
43
+ 'Normalized identifier value (E.164 digits for phone, lowercase for email, etc.). Normalizers in @lobu/connector-sdk own the canonical form.';
44
+ COMMENT ON COLUMN public.entity_identities.source_connector IS
45
+ 'Who claimed this identifier: "connector:whatsapp", "manual", or null when seeded by migration.';
46
+
47
+
48
+ -- Per-install override surface. A JSONB keyed by entityType; shallow-merged
49
+ -- onto the connector's declared entityLinks at rule-resolve time. Lets an
50
+ -- org retarget, disable rules, flip autoCreate, or mask specific identities
51
+ -- without forking the connector. Null column = use connector defaults verbatim.
52
+ ALTER TABLE public.connector_definitions
53
+ ADD COLUMN IF NOT EXISTS entity_link_overrides jsonb;
54
+
55
+ COMMENT ON COLUMN public.connector_definitions.entity_link_overrides IS
56
+ 'Per-install override of connector entityLinks rules. See resolveEntityLinkRules() for merge semantics.';
57
+
58
+
59
+ -- The old scalar-JSONB index was built for a shape (entities.metadata->>wa_jid)
60
+ -- we no longer use — identifiers now live in entity_identities. Drop to avoid
61
+ -- carrying an unused index and a misleading comment on future reads.
62
+ DROP INDEX IF EXISTS public.idx_entities_wa_jid;
63
+
64
+
65
+ -- migrate:down
66
+
67
+ DROP INDEX IF EXISTS public.idx_entity_identities_by_entity;
68
+ DROP INDEX IF EXISTS public.idx_entity_identities_lookup;
69
+ DROP INDEX IF EXISTS public.idx_entity_identities_live_unique;
70
+ DROP TABLE IF EXISTS public.entity_identities;
71
+
72
+ ALTER TABLE public.connector_definitions
73
+ DROP COLUMN IF EXISTS entity_link_overrides;
74
+
75
+ CREATE INDEX IF NOT EXISTS idx_entities_wa_jid
76
+ ON public.entities (entity_type, (metadata->>'wa_jid'))
77
+ WHERE metadata ? 'wa_jid' AND deleted_at IS NULL;
@@ -0,0 +1,83 @@
1
+ -- migrate:up
2
+
3
+ -- Adds the 'auth' run lifecycle: workers run interactive connector.authenticate()
4
+ -- flows (WhatsApp QR, OAuth redirect, credential prompt) and stream artifacts
5
+ -- back to the UI via runs.checkpoint. The UI signals back (OAuth callback,
6
+ -- form submit, cancel) via runs.auth_signal.
7
+
8
+ ALTER TABLE public.runs DROP CONSTRAINT IF EXISTS runs_run_type_check;
9
+ ALTER TABLE public.runs ADD CONSTRAINT runs_run_type_check CHECK (
10
+ run_type = ANY (ARRAY[
11
+ 'sync'::text,
12
+ 'action'::text,
13
+ 'code'::text,
14
+ 'insight'::text,
15
+ 'watcher'::text,
16
+ 'embed_backfill'::text,
17
+ 'auth'::text
18
+ ])
19
+ );
20
+
21
+ -- Target auth profile for 'auth' runs (null for other run types).
22
+ ALTER TABLE public.runs ADD COLUMN IF NOT EXISTS auth_profile_id bigint
23
+ REFERENCES public.auth_profiles(id) ON DELETE CASCADE;
24
+
25
+ -- Reverse channel: UI → connector. The connector pauses on ctx.awaitSignal(name)
26
+ -- and polls this column; the API writes here when the UI posts a signal.
27
+ ALTER TABLE public.runs ADD COLUMN IF NOT EXISTS auth_signal jsonb;
28
+
29
+ -- One active auth run per auth profile at a time.
30
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_runs_active_auth_per_profile
31
+ ON public.runs (auth_profile_id)
32
+ WHERE run_type = 'auth'
33
+ AND auth_profile_id IS NOT NULL
34
+ AND status = ANY (ARRAY['pending'::text, 'claimed'::text, 'running'::text]);
35
+
36
+ -- 'interactive' profile kind: credentials produced by a connector.authenticate()
37
+ -- run. Examples: WhatsApp Baileys session, connector-managed OAuth.
38
+ ALTER TABLE public.auth_profiles DROP CONSTRAINT IF EXISTS auth_profiles_profile_kind_check;
39
+ ALTER TABLE public.auth_profiles ADD CONSTRAINT auth_profiles_profile_kind_check CHECK (
40
+ profile_kind = ANY (ARRAY[
41
+ 'env'::text,
42
+ 'oauth_app'::text,
43
+ 'oauth_account'::text,
44
+ 'browser_session'::text,
45
+ 'interactive'::text
46
+ ])
47
+ );
48
+
49
+ -- Structured display metadata produced by connector.authenticate() — account_id,
50
+ -- display_name, paired_at, expires_at, etc. Surfaced in UI next to the connection.
51
+ ALTER TABLE public.auth_profiles ADD COLUMN IF NOT EXISTS metadata jsonb DEFAULT '{}'::jsonb NOT NULL;
52
+
53
+
54
+ -- migrate:down
55
+
56
+ ALTER TABLE public.auth_profiles DROP COLUMN IF EXISTS metadata;
57
+
58
+ ALTER TABLE public.auth_profiles DROP CONSTRAINT IF EXISTS auth_profiles_profile_kind_check;
59
+ ALTER TABLE public.auth_profiles ADD CONSTRAINT auth_profiles_profile_kind_check CHECK (
60
+ profile_kind = ANY (ARRAY[
61
+ 'env'::text,
62
+ 'oauth_app'::text,
63
+ 'oauth_account'::text,
64
+ 'browser_session'::text
65
+ ])
66
+ );
67
+
68
+ DROP INDEX IF EXISTS public.idx_runs_active_auth_per_profile;
69
+
70
+ ALTER TABLE public.runs DROP COLUMN IF EXISTS auth_signal;
71
+ ALTER TABLE public.runs DROP COLUMN IF EXISTS auth_profile_id;
72
+
73
+ ALTER TABLE public.runs DROP CONSTRAINT IF EXISTS runs_run_type_check;
74
+ ALTER TABLE public.runs ADD CONSTRAINT runs_run_type_check CHECK (
75
+ run_type = ANY (ARRAY[
76
+ 'sync'::text,
77
+ 'action'::text,
78
+ 'code'::text,
79
+ 'insight'::text,
80
+ 'watcher'::text,
81
+ 'embed_backfill'::text
82
+ ])
83
+ );
@@ -0,0 +1,18 @@
1
+ -- migrate:up
2
+
3
+ -- Track which user initiated an interactive auth run so that only that user
4
+ -- can view the QR / credential artifact. Sensitive artifacts (WhatsApp QR,
5
+ -- OTP codes, OAuth consent URLs) must never be viewable by other org members.
6
+
7
+ ALTER TABLE public.runs ADD COLUMN IF NOT EXISTS created_by_user_id text
8
+ REFERENCES public."user"(id) ON DELETE SET NULL;
9
+
10
+ CREATE INDEX IF NOT EXISTS idx_runs_created_by_user
11
+ ON public.runs (created_by_user_id)
12
+ WHERE created_by_user_id IS NOT NULL;
13
+
14
+
15
+ -- migrate:down
16
+
17
+ DROP INDEX IF EXISTS public.idx_runs_created_by_user;
18
+ ALTER TABLE public.runs DROP COLUMN IF EXISTS created_by_user_id;
@@ -0,0 +1,56 @@
1
+ -- migrate:up
2
+
3
+ -- Read-time JOIN support for the entity_identities graph.
4
+ --
5
+ -- applyEntityLinks (src/utils/entity-link-upsert.ts) stamps normalized
6
+ -- identifiers into events.metadata under the namespace key (metadata.email,
7
+ -- metadata.wa_jid, metadata.phone, …). Entity-scoped content queries then
8
+ -- JOIN:
9
+ --
10
+ -- events f
11
+ -- EXISTS (SELECT 1 FROM entity_identities ei
12
+ -- WHERE ei.entity_id = $X
13
+ -- AND ei.deleted_at IS NULL
14
+ -- AND f.metadata ? ei.namespace
15
+ -- AND f.metadata->>ei.namespace = ei.identifier)
16
+ --
17
+ -- Without these indexes, the plan degenerates into a seq scan of events for
18
+ -- every entity-scoped content listing. One partial BTREE per namespace in use
19
+ -- keeps the JOIN on an index.
20
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_email
21
+ ON public.events ((metadata->>'email'))
22
+ WHERE metadata ? 'email';
23
+
24
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_wa_jid
25
+ ON public.events ((metadata->>'wa_jid'))
26
+ WHERE metadata ? 'wa_jid';
27
+
28
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_phone
29
+ ON public.events ((metadata->>'phone'))
30
+ WHERE metadata ? 'phone';
31
+
32
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_slack_user_id
33
+ ON public.events ((metadata->>'slack_user_id'))
34
+ WHERE metadata ? 'slack_user_id';
35
+
36
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_github_login
37
+ ON public.events ((metadata->>'github_login'))
38
+ WHERE metadata ? 'github_login';
39
+
40
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_auth_user_id
41
+ ON public.events ((metadata->>'auth_user_id'))
42
+ WHERE metadata ? 'auth_user_id';
43
+
44
+ CREATE INDEX IF NOT EXISTS idx_events_metadata_google_contact_id
45
+ ON public.events ((metadata->>'google_contact_id'))
46
+ WHERE metadata ? 'google_contact_id';
47
+
48
+ -- migrate:down
49
+
50
+ DROP INDEX IF EXISTS public.idx_events_metadata_google_contact_id;
51
+ DROP INDEX IF EXISTS public.idx_events_metadata_auth_user_id;
52
+ DROP INDEX IF EXISTS public.idx_events_metadata_github_login;
53
+ DROP INDEX IF EXISTS public.idx_events_metadata_slack_user_id;
54
+ DROP INDEX IF EXISTS public.idx_events_metadata_phone;
55
+ DROP INDEX IF EXISTS public.idx_events_metadata_wa_jid;
56
+ DROP INDEX IF EXISTS public.idx_events_metadata_email;
@@ -0,0 +1,56 @@
1
+ -- migrate:up
2
+
3
+ -- Extend reserved org slugs to cover infrastructure subdomains.
4
+ -- RESERVED_SUBDOMAINS in packages/server/src/index.ts already
5
+ -- treats www/mcp/static/cdn/... as non-org at the routing layer; this
6
+ -- mirrors it at the DB layer so those names can never be claimed.
7
+ --
8
+ -- `app` is intentionally NOT reserved — `app.lobu.ai` hosts the auth
9
+ -- org itself, whose DB row uses slug='app'.
10
+
11
+ ALTER TABLE public.organization DROP CONSTRAINT IF EXISTS org_slug_not_reserved;
12
+
13
+ ALTER TABLE public.organization ADD CONSTRAINT org_slug_not_reserved CHECK (
14
+ slug <> ALL (ARRAY[
15
+ 'settings',
16
+ 'auth',
17
+ 'api',
18
+ 'templates',
19
+ 'help',
20
+ 'account',
21
+ 'admin',
22
+ 'health',
23
+ 'login',
24
+ 'logout',
25
+ 'signup',
26
+ 'register',
27
+ 'www',
28
+ 'mcp',
29
+ 'static',
30
+ 'assets',
31
+ 'cdn',
32
+ 'docs',
33
+ 'mail'
34
+ ]::text[])
35
+ );
36
+
37
+ -- migrate:down
38
+
39
+ ALTER TABLE public.organization DROP CONSTRAINT IF EXISTS org_slug_not_reserved;
40
+
41
+ ALTER TABLE public.organization ADD CONSTRAINT org_slug_not_reserved CHECK (
42
+ slug <> ALL (ARRAY[
43
+ 'settings',
44
+ 'auth',
45
+ 'api',
46
+ 'templates',
47
+ 'help',
48
+ 'account',
49
+ 'admin',
50
+ 'health',
51
+ 'login',
52
+ 'logout',
53
+ 'signup',
54
+ 'register'
55
+ ]::text[])
56
+ );
@@ -0,0 +1,52 @@
1
+ -- migrate:up
2
+
3
+ -- Durable correlation between a dispatched watcher run and the agent message
4
+ -- that executes it, and between a completed watcher window and the run that
5
+ -- produced it. Replaces payload-based matching in reconcileWatcherRuns.
6
+
7
+ ALTER TABLE public.runs
8
+ ADD COLUMN IF NOT EXISTS dispatched_message_id text;
9
+
10
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_runs_dispatched_message_id
11
+ ON public.runs (dispatched_message_id)
12
+ WHERE dispatched_message_id IS NOT NULL;
13
+
14
+ ALTER TABLE public.watcher_windows
15
+ ADD COLUMN IF NOT EXISTS run_id bigint
16
+ REFERENCES public.runs(id) ON DELETE SET NULL;
17
+
18
+ -- Rollout backfill: older windows already stored watcher_run_id in run_metadata,
19
+ -- but pre-migration rows have NULL run_id. Populate the durable FK before the
20
+ -- new reconciler/reset path runs so successful pre-deploy executions are not
21
+ -- re-dispatched on first tick.
22
+ WITH correlated_windows AS (
23
+ SELECT ww.id,
24
+ (btrim(ww.run_metadata->>'watcher_run_id'))::bigint AS correlated_run_id
25
+ FROM public.watcher_windows ww
26
+ WHERE ww.run_id IS NULL
27
+ AND ww.run_metadata ? 'watcher_run_id'
28
+ AND jsonb_typeof(ww.run_metadata->'watcher_run_id') IN ('number', 'string')
29
+ AND btrim(ww.run_metadata->>'watcher_run_id') ~ '^[0-9]+$'
30
+ )
31
+ UPDATE public.watcher_windows ww
32
+ SET run_id = cw.correlated_run_id
33
+ FROM correlated_windows cw
34
+ WHERE ww.id = cw.id
35
+ AND EXISTS (
36
+ SELECT 1
37
+ FROM public.runs r
38
+ WHERE r.id = cw.correlated_run_id
39
+ AND r.run_type = 'watcher'
40
+ );
41
+
42
+ CREATE INDEX IF NOT EXISTS idx_watcher_windows_run_id
43
+ ON public.watcher_windows (run_id)
44
+ WHERE run_id IS NOT NULL;
45
+
46
+
47
+ -- migrate:down
48
+
49
+ DROP INDEX IF EXISTS public.idx_watcher_windows_run_id;
50
+ ALTER TABLE public.watcher_windows DROP COLUMN IF EXISTS run_id;
51
+ DROP INDEX IF EXISTS public.idx_runs_dispatched_message_id;
52
+ ALTER TABLE public.runs DROP COLUMN IF EXISTS dispatched_message_id;
@@ -0,0 +1,47 @@
1
+ -- migrate:up
2
+
3
+ -- The events table tags each row with the OAuth client that produced it via
4
+ -- `events.client_id -> oauth_clients.id`. The original FK had no ON DELETE
5
+ -- behaviour, so when an oauth_client row was removed (manual cleanup, e2e
6
+ -- teardown, expired registration) any in-flight token still issuing inserts
7
+ -- failed with `events_client_id_fkey` violations (Sentry: LOBU-34).
8
+ --
9
+ -- Match the relaxation already applied to other event-side FKs
10
+ -- (connection_id, feed_id, run_id) and let stale client references reset
11
+ -- to NULL instead of breaking inserts.
12
+ --
13
+ -- Add and validate the replacement constraint before the quick final swap so
14
+ -- existing traffic stays protected and the ACCESS EXCLUSIVE window is short.
15
+
16
+ ALTER TABLE public.events
17
+ ADD CONSTRAINT events_client_id_fkey_v2
18
+ FOREIGN KEY (client_id)
19
+ REFERENCES public.oauth_clients(id)
20
+ ON DELETE SET NULL
21
+ NOT VALID;
22
+
23
+ ALTER TABLE public.events
24
+ VALIDATE CONSTRAINT events_client_id_fkey_v2;
25
+
26
+ ALTER TABLE public.events
27
+ DROP CONSTRAINT IF EXISTS events_client_id_fkey;
28
+
29
+ ALTER TABLE public.events
30
+ RENAME CONSTRAINT events_client_id_fkey_v2 TO events_client_id_fkey;
31
+
32
+ -- migrate:down
33
+
34
+ ALTER TABLE public.events
35
+ ADD CONSTRAINT events_client_id_fkey_v2
36
+ FOREIGN KEY (client_id)
37
+ REFERENCES public.oauth_clients(id)
38
+ NOT VALID;
39
+
40
+ ALTER TABLE public.events
41
+ VALIDATE CONSTRAINT events_client_id_fkey_v2;
42
+
43
+ ALTER TABLE public.events
44
+ DROP CONSTRAINT IF EXISTS events_client_id_fkey;
45
+
46
+ ALTER TABLE public.events
47
+ RENAME CONSTRAINT events_client_id_fkey_v2 TO events_client_id_fkey;