@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.
- package/README.md +20 -27
- package/dist/bundled-skills/lobu/SKILL.md +11 -11
- package/dist/commands/_lib/apply/apply-cmd.d.ts +38 -0
- package/dist/commands/_lib/apply/apply-cmd.d.ts.map +1 -1
- package/dist/commands/_lib/apply/apply-cmd.js +574 -40
- package/dist/commands/_lib/apply/apply-cmd.js.map +1 -1
- package/dist/commands/_lib/apply/client.d.ts +180 -1
- package/dist/commands/_lib/apply/client.d.ts.map +1 -1
- package/dist/commands/_lib/apply/client.js +308 -28
- package/dist/commands/_lib/apply/client.js.map +1 -1
- package/dist/commands/_lib/apply/desired-state.d.ts +134 -3
- package/dist/commands/_lib/apply/desired-state.d.ts.map +1 -1
- package/dist/commands/_lib/apply/desired-state.js +703 -89
- package/dist/commands/_lib/apply/desired-state.js.map +1 -1
- package/dist/commands/_lib/apply/diff.d.ts +61 -3
- package/dist/commands/_lib/apply/diff.d.ts.map +1 -1
- package/dist/commands/_lib/apply/diff.js +382 -92
- package/dist/commands/_lib/apply/diff.js.map +1 -1
- package/dist/commands/_lib/apply/prompt.d.ts +6 -0
- package/dist/commands/_lib/apply/prompt.d.ts.map +1 -1
- package/dist/commands/_lib/apply/prompt.js +16 -0
- package/dist/commands/_lib/apply/prompt.js.map +1 -1
- package/dist/commands/_lib/apply/render.d.ts +9 -0
- package/dist/commands/_lib/apply/render.d.ts.map +1 -1
- package/dist/commands/_lib/apply/render.js +80 -3
- package/dist/commands/_lib/apply/render.js.map +1 -1
- package/dist/commands/agent.d.ts +7 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +65 -1
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/chat.d.ts +12 -9
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +125 -57
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/dev.d.ts +23 -7
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +197 -49
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +136 -0
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/eval.d.ts +8 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +72 -6
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/init.d.ts +22 -5
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +355 -182
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/link.d.ts +11 -0
- package/dist/commands/link.d.ts.map +1 -0
- package/dist/commands/link.js +28 -0
- package/dist/commands/link.js.map +1 -0
- package/dist/commands/login.d.ts.map +1 -1
- package/dist/commands/login.js +14 -2
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/memory/_lib/browser-auth-cmd.d.ts.map +1 -1
- package/dist/commands/memory/_lib/browser-auth-cmd.js +3 -3
- package/dist/commands/memory/_lib/browser-auth-cmd.js.map +1 -1
- package/dist/commands/memory/_lib/mcp.d.ts +2 -2
- package/dist/commands/memory/_lib/mcp.d.ts.map +1 -1
- package/dist/commands/memory/_lib/mcp.js +24 -12
- package/dist/commands/memory/_lib/mcp.js.map +1 -1
- package/dist/commands/memory/_lib/openclaw-auth.d.ts +1 -0
- package/dist/commands/memory/_lib/openclaw-auth.d.ts.map +1 -1
- package/dist/commands/memory/_lib/openclaw-auth.js +14 -3
- package/dist/commands/memory/_lib/openclaw-auth.js.map +1 -1
- package/dist/commands/memory/_lib/openclaw-cmd.js +1 -1
- package/dist/commands/memory/_lib/openclaw-cmd.js.map +1 -1
- package/dist/commands/memory/_lib/schema.d.ts +29 -2
- package/dist/commands/memory/_lib/schema.d.ts.map +1 -1
- package/dist/commands/memory/_lib/schema.js +121 -5
- package/dist/commands/memory/_lib/schema.js.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.d.ts.map +1 -1
- package/dist/commands/memory/_lib/seed-cmd.js +46 -24
- package/dist/commands/memory/_lib/seed-cmd.js.map +1 -1
- package/dist/commands/memory/run.d.ts.map +1 -1
- package/dist/commands/memory/run.js +2 -2
- package/dist/commands/memory/run.js.map +1 -1
- package/dist/commands/org.d.ts +4 -0
- package/dist/commands/org.d.ts.map +1 -1
- package/dist/commands/org.js +10 -0
- package/dist/commands/org.js.map +1 -1
- package/dist/commands/platforms/platform-prompts.d.ts +0 -1
- package/dist/commands/platforms/platform-prompts.d.ts.map +1 -1
- package/dist/commands/platforms/platform-prompts.js +54 -8
- package/dist/commands/platforms/platform-prompts.js.map +1 -1
- package/dist/commands/telemetry.d.ts +10 -0
- package/dist/commands/telemetry.d.ts.map +1 -0
- package/dist/commands/telemetry.js +68 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/commands/token.d.ts +9 -0
- package/dist/commands/token.d.ts.map +1 -1
- package/dist/commands/token.js +54 -0
- package/dist/commands/token.js.map +1 -1
- package/dist/commands/whoami.d.ts.map +1 -1
- package/dist/commands/whoami.js +1 -1
- package/dist/commands/whoami.js.map +1 -1
- package/dist/connectors/README.md +534 -0
- package/dist/connectors/__tests__/browser-scraper-utils.test.ts +186 -0
- package/dist/connectors/apple_health.ts +138 -0
- package/dist/connectors/apple_screen_time.ts +82 -0
- package/dist/connectors/browser-scraper-utils.ts +246 -0
- package/dist/connectors/capterra.ts +277 -0
- package/dist/connectors/g2.ts +290 -0
- package/dist/connectors/github.ts +1530 -0
- package/dist/connectors/glassdoor.ts +295 -0
- package/dist/connectors/gmaps.ts +197 -0
- package/dist/connectors/google_calendar.ts +641 -0
- package/dist/connectors/google_gmail.ts +754 -0
- package/dist/connectors/google_photos.ts +776 -0
- package/dist/connectors/google_play.ts +349 -0
- package/dist/connectors/hackernews.ts +471 -0
- package/dist/connectors/index.ts +28 -0
- package/dist/connectors/ios_appstore.ts +226 -0
- package/dist/connectors/linkedin.ts +494 -0
- package/dist/connectors/local_directory.ts +91 -0
- package/dist/connectors/microsoft_outlook.ts +410 -0
- package/dist/connectors/producthunt.ts +471 -0
- package/dist/connectors/reddit.ts +600 -0
- package/dist/connectors/revolut.ts +572 -0
- package/dist/connectors/rss.ts +448 -0
- package/dist/connectors/spotify.ts +590 -0
- package/dist/connectors/trustpilot.ts +203 -0
- package/dist/connectors/website.ts +629 -0
- package/dist/connectors/whatsapp.ts +1081 -0
- package/dist/connectors/whatsapp_local.ts +125 -0
- package/dist/connectors/x.ts +536 -0
- package/dist/connectors/youtube.ts +666 -0
- package/dist/db/migrations/00000000000000_baseline.sql +4867 -0
- package/dist/db/migrations/20260405193000_add_mcp_sessions.sql +33 -0
- package/dist/db/migrations/20260408120000_remove_system_connectors.sql +48 -0
- package/dist/db/migrations/20260408120001_optional_compiled_code.sql +6 -0
- package/dist/db/migrations/20260409110000_add_active_watcher_run_index.sql +9 -0
- package/dist/db/migrations/20260409130000_connector_default_config.sql +5 -0
- package/dist/db/migrations/20260410120000_add_agent_secrets.sql +25 -0
- package/dist/db/migrations/20260413170000_add_watcher_group_id.sql +67 -0
- package/dist/db/migrations/20260416120000_add_entity_wa_jid_index.sql +14 -0
- package/dist/db/migrations/20260417100000_add_entity_identities.sql +77 -0
- package/dist/db/migrations/20260418100000_add_auth_runs.sql +83 -0
- package/dist/db/migrations/20260418110000_add_runs_created_by_user.sql +18 -0
- package/dist/db/migrations/20260419120000_add_event_identity_indexes.sql +56 -0
- package/dist/db/migrations/20260420120000_extend_reserved_org_slugs.sql +56 -0
- package/dist/db/migrations/20260424030000_add_watcher_run_correlation.sql +52 -0
- package/dist/db/migrations/20260424130000_relax_events_client_id_fk.sql +47 -0
- package/dist/db/migrations/20260425100000_normalize_watcher_feedback.sql +91 -0
- package/dist/db/migrations/20260425120000_add_run_diagnostics.sql +20 -0
- package/dist/db/migrations/20260425130000_add_repair_agent_plumbing.sql +46 -0
- package/dist/db/migrations/20260426120000_entities_entity_type_fk.sql +101 -0
- package/dist/db/migrations/20260426130000_db_integrity_cleanup.sql +104 -0
- package/dist/db/migrations/20260426130001_db_integrity_cleanup_concurrent.sql +187 -0
- package/dist/db/migrations/20260427133000_events_created_by_nullable.sql +74 -0
- package/dist/db/migrations/20260427140000_identity_engine_indexes.sql +140 -0
- package/dist/db/migrations/20260427150000_drop_events_source_id.sql +177 -0
- package/dist/db/migrations/20260427160000_drop_dead_schema.sql +76 -0
- package/dist/db/migrations/20260427170000_market_founder_to_member.sql +364 -0
- package/dist/db/migrations/20260428040000_cascade_events_watchers_org_fk.sql +66 -0
- package/dist/db/migrations/20260428050000_add_runs_approved_input.sql +9 -0
- package/dist/db/migrations/20260429010000_auth_profile_tenant_scoped_fk.sql +79 -0
- package/dist/db/migrations/20260429060000_extend_runs_for_lobu_queue.sql +108 -0
- package/dist/db/migrations/20260429120000_agent_changed_notify.sql +97 -0
- package/dist/db/migrations/20260429120100_user_auth_profiles_and_model_prefs.sql +36 -0
- package/dist/db/migrations/20260429120200_fix_notify_old_keys.sql +130 -0
- package/dist/db/migrations/20260429130000_oauth_states_cli_sessions_rate_limits.sql +83 -0
- package/dist/db/migrations/20260429140000_phase8_grants_chat_connections_mcp_sessions.sql +84 -0
- package/dist/db/migrations/20260429140100_runs_priority_expires_at_retry_delay.sql +44 -0
- package/dist/db/migrations/20260429180000_drop_invalidatable_cache_triggers.sql +25 -0
- package/dist/db/migrations/20260430005614_agents_apply_fields.sql +21 -0
- package/dist/db/migrations/20260430022231_fix_connection_config_encryption.sql +69 -0
- package/dist/db/migrations/20260430151215_add_task_run_type.sql +77 -0
- package/dist/db/migrations/20260501000000_drop_cli_sessions.sql +27 -0
- package/dist/db/migrations/20260501133000_lobu_memory_mcp_id.sql +117 -0
- package/dist/db/migrations/20260502000000_drop_chat_connections.sql +60 -0
- package/dist/db/migrations/20260503000000_agent_secrets_org_scope.sql +56 -0
- package/dist/db/migrations/20260504000000_flatten_agents_drop_sandbox_model.sql +48 -0
- package/dist/db/migrations/20260510220000_connector_required_capability.sql +47 -0
- package/dist/db/migrations/20260512000000_device_worker_connection_binding.sql +113 -0
- package/dist/db/migrations/20260512131703_connections_slug.sql +131 -0
- package/dist/db/migrations/20260513000000_chat_user_identities.sql +24 -0
- package/dist/db/migrations/20260513120000_auth_profiles_device_binding.sql +50 -0
- package/dist/db/migrations/20260513150000_auth_profiles_cdp_url.sql +43 -0
- package/dist/db/migrations/20260513200000_notifications_as_events.sql +86 -0
- package/dist/db/migrations/20260514000000_scheduled_jobs.sql +97 -0
- package/dist/db/migrations/20260514120000_auth_profiles_connector_key_nullable.sql +42 -0
- package/dist/eval/types.d.ts +2 -0
- package/dist/eval/types.d.ts.map +1 -1
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +210 -132
- package/dist/index.js.map +1 -1
- package/dist/internal/api-client.d.ts +4 -8
- package/dist/internal/api-client.d.ts.map +1 -1
- package/dist/internal/api-client.js +1 -1
- package/dist/internal/api-client.js.map +1 -1
- package/dist/internal/context.js +2 -2
- package/dist/internal/context.js.map +1 -1
- package/dist/internal/credentials.d.ts.map +1 -1
- package/dist/internal/credentials.js +6 -1
- package/dist/internal/credentials.js.map +1 -1
- package/dist/internal/gateway-url.d.ts +14 -0
- package/dist/internal/gateway-url.d.ts.map +1 -1
- package/dist/internal/gateway-url.js +19 -0
- package/dist/internal/gateway-url.js.map +1 -1
- package/dist/internal/index.d.ts +3 -4
- package/dist/internal/index.d.ts.map +1 -1
- package/dist/internal/index.js +3 -3
- package/dist/internal/index.js.map +1 -1
- package/dist/internal/oauth.d.ts +6 -5
- package/dist/internal/oauth.d.ts.map +1 -1
- package/dist/internal/oauth.js +2 -2
- package/dist/internal/project-link.d.ts +10 -0
- package/dist/internal/project-link.d.ts.map +1 -0
- package/dist/internal/project-link.js +48 -0
- package/dist/internal/project-link.js.map +1 -0
- package/dist/providers.json +2 -2
- package/dist/server.bundle.mjs +31654 -30866
- package/dist/start-local.bundle.mjs +74409 -0
- package/dist/templates/README.md.tmpl +10 -11
- package/dist/templates/TESTING.md.tmpl +9 -9
- package/package.json +15 -13
- package/dist/__tests__/chat.integration.test.d.ts +0 -2
- package/dist/__tests__/chat.integration.test.d.ts.map +0 -1
- package/dist/__tests__/chat.integration.test.js +0 -337
- package/dist/__tests__/chat.integration.test.js.map +0 -1
- package/dist/__tests__/dev.test.d.ts +0 -2
- package/dist/__tests__/dev.test.d.ts.map +0 -1
- package/dist/__tests__/dev.test.js +0 -25
- package/dist/__tests__/dev.test.js.map +0 -1
- package/dist/__tests__/init-memory.test.d.ts +0 -2
- package/dist/__tests__/init-memory.test.d.ts.map +0 -1
- package/dist/__tests__/init-memory.test.js +0 -45
- package/dist/__tests__/init-memory.test.js.map +0 -1
- package/dist/__tests__/token.test.d.ts +0 -2
- package/dist/__tests__/token.test.d.ts.map +0 -1
- package/dist/__tests__/token.test.js +0 -52
- package/dist/__tests__/token.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/client.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/client.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/client.test.js +0 -23
- package/dist/commands/_lib/apply/__tests__/client.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/desired-state.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/desired-state.test.js +0 -140
- package/dist/commands/_lib/apply/__tests__/desired-state.test.js.map +0 -1
- package/dist/commands/_lib/apply/__tests__/diff.test.d.ts +0 -2
- package/dist/commands/_lib/apply/__tests__/diff.test.d.ts.map +0 -1
- package/dist/commands/_lib/apply/__tests__/diff.test.js +0 -378
- package/dist/commands/_lib/apply/__tests__/diff.test.js.map +0 -1
- package/dist/commands/apply.d.ts +0 -3
- package/dist/commands/apply.d.ts.map +0 -1
- package/dist/commands/apply.js +0 -5
- package/dist/commands/apply.js.map +0 -1
- package/dist/commands/memory/_lib/openclaw-auth.test.d.ts +0 -2
- package/dist/commands/memory/_lib/openclaw-auth.test.d.ts.map +0 -1
- package/dist/commands/memory/_lib/openclaw-auth.test.js +0 -9
- package/dist/commands/memory/_lib/openclaw-auth.test.js.map +0 -1
- package/dist/internal/__tests__/api-client.test.d.ts +0 -2
- package/dist/internal/__tests__/api-client.test.d.ts.map +0 -1
- package/dist/internal/__tests__/api-client.test.js +0 -95
- package/dist/internal/__tests__/api-client.test.js.map +0 -1
- package/dist/internal/__tests__/context.test.d.ts +0 -2
- package/dist/internal/__tests__/context.test.d.ts.map +0 -1
- package/dist/internal/__tests__/context.test.js +0 -77
- package/dist/internal/__tests__/context.test.js.map +0 -1
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- The bespoke `lobu login` device-code flow that backed `cli_sessions` has
|
|
4
|
+
-- been replaced with standard OAuth 2.0 device-code (issued by the existing
|
|
5
|
+
-- Lobu IdP). The CLI now mints OAuth bearer tokens, so `cli_sessions` is
|
|
6
|
+
-- dead.
|
|
7
|
+
DROP INDEX IF EXISTS public.cli_sessions_user_id_idx;
|
|
8
|
+
DROP INDEX IF EXISTS public.cli_sessions_expires_at_idx;
|
|
9
|
+
DROP TABLE IF EXISTS public.cli_sessions;
|
|
10
|
+
|
|
11
|
+
-- migrate:down
|
|
12
|
+
|
|
13
|
+
CREATE TABLE IF NOT EXISTS public.cli_sessions (
|
|
14
|
+
session_id text PRIMARY KEY,
|
|
15
|
+
user_id text NOT NULL,
|
|
16
|
+
email text,
|
|
17
|
+
name text,
|
|
18
|
+
refresh_token_id text NOT NULL,
|
|
19
|
+
expires_at timestamptz NOT NULL,
|
|
20
|
+
created_at timestamptz NOT NULL DEFAULT now()
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
CREATE INDEX IF NOT EXISTS cli_sessions_user_id_idx
|
|
24
|
+
ON public.cli_sessions (user_id);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS cli_sessions_expires_at_idx
|
|
27
|
+
ON public.cli_sessions (expires_at);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
UPDATE public.agents
|
|
4
|
+
SET mcp_servers = (mcp_servers - 'lobu') || jsonb_build_object('lobu-memory', mcp_servers->'lobu'),
|
|
5
|
+
updated_at = now()
|
|
6
|
+
WHERE mcp_servers ? 'lobu'
|
|
7
|
+
AND NOT (mcp_servers ? 'lobu-memory');
|
|
8
|
+
|
|
9
|
+
UPDATE public.agents
|
|
10
|
+
SET mcp_servers = mcp_servers - 'lobu',
|
|
11
|
+
updated_at = now()
|
|
12
|
+
WHERE mcp_servers ? 'lobu';
|
|
13
|
+
|
|
14
|
+
UPDATE public.agents a
|
|
15
|
+
SET pre_approved_tools = COALESCE((
|
|
16
|
+
SELECT jsonb_agg(DISTINCT mapped.value)
|
|
17
|
+
FROM (
|
|
18
|
+
SELECT CASE
|
|
19
|
+
WHEN tool.value #>> '{}' LIKE '/mcp/lobu/tools/%'
|
|
20
|
+
OR tool.value #>> '{}' LIKE '/mcp/lobu-memory/tools/%'
|
|
21
|
+
THEN to_jsonb('/mcp/lobu-memory/tools/*'::text)
|
|
22
|
+
ELSE tool.value
|
|
23
|
+
END AS value
|
|
24
|
+
FROM jsonb_array_elements(COALESCE(a.pre_approved_tools, '[]'::jsonb)) AS tool(value)
|
|
25
|
+
|
|
26
|
+
UNION ALL
|
|
27
|
+
|
|
28
|
+
SELECT to_jsonb('/mcp/lobu-memory/tools/*'::text) AS value
|
|
29
|
+
WHERE a.mcp_servers ? 'lobu-memory'
|
|
30
|
+
) AS mapped
|
|
31
|
+
), '[]'::jsonb),
|
|
32
|
+
updated_at = now()
|
|
33
|
+
WHERE a.mcp_servers ? 'lobu-memory'
|
|
34
|
+
OR EXISTS (
|
|
35
|
+
SELECT 1
|
|
36
|
+
FROM jsonb_array_elements(COALESCE(a.pre_approved_tools, '[]'::jsonb)) AS tool(value)
|
|
37
|
+
WHERE tool.value #>> '{}' LIKE '/mcp/lobu/tools/%'
|
|
38
|
+
OR tool.value #>> '{}' LIKE '/mcp/lobu-memory/tools/%'
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
INSERT INTO public.grants (agent_id, kind, pattern, expires_at, granted_at, denied)
|
|
42
|
+
SELECT g.agent_id,
|
|
43
|
+
g.kind,
|
|
44
|
+
'/mcp/lobu-memory/tools/*',
|
|
45
|
+
CASE
|
|
46
|
+
WHEN bool_or(g.expires_at IS NULL) THEN NULL::timestamptz
|
|
47
|
+
ELSE max(g.expires_at)
|
|
48
|
+
END,
|
|
49
|
+
now(),
|
|
50
|
+
bool_or(g.denied)
|
|
51
|
+
FROM public.grants g
|
|
52
|
+
WHERE g.kind = 'mcp_tool'
|
|
53
|
+
AND (g.pattern LIKE '/mcp/lobu/tools/%' OR g.pattern LIKE '/mcp/lobu-memory/tools/%')
|
|
54
|
+
GROUP BY g.agent_id, g.kind
|
|
55
|
+
ON CONFLICT (agent_id, kind, pattern) DO UPDATE SET
|
|
56
|
+
expires_at = CASE
|
|
57
|
+
WHEN public.grants.expires_at IS NULL THEN EXCLUDED.expires_at
|
|
58
|
+
WHEN EXCLUDED.expires_at IS NULL THEN public.grants.expires_at
|
|
59
|
+
ELSE LEAST(public.grants.expires_at, EXCLUDED.expires_at)
|
|
60
|
+
END,
|
|
61
|
+
granted_at = EXCLUDED.granted_at,
|
|
62
|
+
denied = public.grants.denied OR EXCLUDED.denied;
|
|
63
|
+
|
|
64
|
+
DELETE FROM public.grants
|
|
65
|
+
WHERE kind = 'mcp_tool'
|
|
66
|
+
AND pattern LIKE '/mcp/lobu/tools/%';
|
|
67
|
+
|
|
68
|
+
-- migrate:down
|
|
69
|
+
|
|
70
|
+
UPDATE public.agents
|
|
71
|
+
SET mcp_servers = (mcp_servers - 'lobu-memory') || jsonb_build_object('lobu', mcp_servers->'lobu-memory'),
|
|
72
|
+
updated_at = now()
|
|
73
|
+
WHERE mcp_servers ? 'lobu-memory'
|
|
74
|
+
AND NOT (mcp_servers ? 'lobu');
|
|
75
|
+
|
|
76
|
+
UPDATE public.agents a
|
|
77
|
+
SET pre_approved_tools = COALESCE((
|
|
78
|
+
SELECT jsonb_agg(DISTINCT CASE
|
|
79
|
+
WHEN tool.value #>> '{}' LIKE '/mcp/lobu-memory/tools/%'
|
|
80
|
+
THEN to_jsonb('/mcp/lobu/tools/*'::text)
|
|
81
|
+
ELSE tool.value
|
|
82
|
+
END)
|
|
83
|
+
FROM jsonb_array_elements(COALESCE(a.pre_approved_tools, '[]'::jsonb)) AS tool(value)
|
|
84
|
+
), '[]'::jsonb),
|
|
85
|
+
updated_at = now()
|
|
86
|
+
WHERE EXISTS (
|
|
87
|
+
SELECT 1
|
|
88
|
+
FROM jsonb_array_elements(COALESCE(a.pre_approved_tools, '[]'::jsonb)) AS tool(value)
|
|
89
|
+
WHERE tool.value #>> '{}' LIKE '/mcp/lobu-memory/tools/%'
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
INSERT INTO public.grants (agent_id, kind, pattern, expires_at, granted_at, denied)
|
|
93
|
+
SELECT g.agent_id,
|
|
94
|
+
g.kind,
|
|
95
|
+
'/mcp/lobu/tools/*',
|
|
96
|
+
CASE
|
|
97
|
+
WHEN bool_or(g.expires_at IS NULL) THEN NULL::timestamptz
|
|
98
|
+
ELSE max(g.expires_at)
|
|
99
|
+
END,
|
|
100
|
+
now(),
|
|
101
|
+
bool_or(g.denied)
|
|
102
|
+
FROM public.grants g
|
|
103
|
+
WHERE g.kind = 'mcp_tool'
|
|
104
|
+
AND g.pattern LIKE '/mcp/lobu-memory/tools/%'
|
|
105
|
+
GROUP BY g.agent_id, g.kind
|
|
106
|
+
ON CONFLICT (agent_id, kind, pattern) DO UPDATE SET
|
|
107
|
+
expires_at = CASE
|
|
108
|
+
WHEN public.grants.expires_at IS NULL THEN EXCLUDED.expires_at
|
|
109
|
+
WHEN EXCLUDED.expires_at IS NULL THEN public.grants.expires_at
|
|
110
|
+
ELSE LEAST(public.grants.expires_at, EXCLUDED.expires_at)
|
|
111
|
+
END,
|
|
112
|
+
granted_at = EXCLUDED.granted_at,
|
|
113
|
+
denied = public.grants.denied OR EXCLUDED.denied;
|
|
114
|
+
|
|
115
|
+
DELETE FROM public.grants
|
|
116
|
+
WHERE kind = 'mcp_tool'
|
|
117
|
+
AND pattern LIKE '/mcp/lobu-memory/tools/%';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
-- Drop chat_connections table. Connection state is now unified in
|
|
3
|
+
-- agent_connections, which ChatInstanceManager reads/writes directly.
|
|
4
|
+
-- Secret fields (botToken, signingSecret, etc.) live as `secret://`
|
|
5
|
+
-- refs inside the row's `config` JSON and resolve at runtime through
|
|
6
|
+
-- SecretStoreRegistry — backed by Postgres by default, pluggable to
|
|
7
|
+
-- AWS Secrets Manager / Vault / k8s for ops who need it.
|
|
8
|
+
|
|
9
|
+
-- Copy any existing chat_connections rows into agent_connections so a
|
|
10
|
+
-- live deployment with provisioned chat bots doesn't lose them. Configs
|
|
11
|
+
-- carrying the legacy `enc:v1:` ciphertext are handled at read time by
|
|
12
|
+
-- decryptLegacyEncryptedConfig in postgres-stores.ts; refs pass through
|
|
13
|
+
-- unchanged. ON CONFLICT DO NOTHING covers the case where rows have
|
|
14
|
+
-- already been mirrored by an in-flight write through the manager.
|
|
15
|
+
-- agent_connections.agent_id is NOT NULL, but chat_connections.template_agent_id
|
|
16
|
+
-- was nullable. Skip orphaned rows (no parent agent) — they could not start
|
|
17
|
+
-- in the current model anyway.
|
|
18
|
+
-- Guarded by to_regclass: deployments that never had chat_connections
|
|
19
|
+
-- (table created with IF NOT EXISTS in 20260429140000 and never used) skip
|
|
20
|
+
-- the copy entirely instead of erroring on the missing relation.
|
|
21
|
+
DO $$
|
|
22
|
+
BEGIN
|
|
23
|
+
IF to_regclass('public.chat_connections') IS NOT NULL THEN
|
|
24
|
+
INSERT INTO public.agent_connections (
|
|
25
|
+
id, agent_id, platform, config, settings, metadata,
|
|
26
|
+
status, error_message, created_at, updated_at
|
|
27
|
+
)
|
|
28
|
+
SELECT
|
|
29
|
+
id, template_agent_id, platform, config, settings, metadata,
|
|
30
|
+
status, error_message, created_at, updated_at
|
|
31
|
+
FROM public.chat_connections
|
|
32
|
+
WHERE template_agent_id IS NOT NULL
|
|
33
|
+
ON CONFLICT (id) DO NOTHING;
|
|
34
|
+
END IF;
|
|
35
|
+
END $$;
|
|
36
|
+
|
|
37
|
+
DROP TABLE IF EXISTS public.chat_connections;
|
|
38
|
+
|
|
39
|
+
-- migrate:down
|
|
40
|
+
-- Recreate chat_connections for rollback. Data would need to be re-seeded.
|
|
41
|
+
|
|
42
|
+
CREATE TABLE IF NOT EXISTS public.chat_connections (
|
|
43
|
+
id text PRIMARY KEY,
|
|
44
|
+
platform text NOT NULL,
|
|
45
|
+
template_agent_id text REFERENCES public.agents(id) ON DELETE CASCADE,
|
|
46
|
+
config jsonb NOT NULL,
|
|
47
|
+
settings jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
48
|
+
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
49
|
+
status text NOT NULL DEFAULT 'active',
|
|
50
|
+
error_message text,
|
|
51
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
52
|
+
updated_at timestamptz NOT NULL DEFAULT now()
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
CREATE INDEX IF NOT EXISTS chat_connections_template_agent_id_idx
|
|
56
|
+
ON public.chat_connections (template_agent_id)
|
|
57
|
+
WHERE template_agent_id IS NOT NULL;
|
|
58
|
+
|
|
59
|
+
CREATE INDEX IF NOT EXISTS chat_connections_platform_idx
|
|
60
|
+
ON public.chat_connections (platform);
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- agent_secrets used a global `(name)` namespace, so two organizations
|
|
4
|
+
-- storing the same key (e.g. `ANTHROPIC_API_KEY`) would silently overwrite
|
|
5
|
+
-- each other. Scope rows by organization while keeping legacy rows
|
|
6
|
+
-- addressable: empty-string organization_id means "global" (used by
|
|
7
|
+
-- system env-store and any pre-existing rows from the global era).
|
|
8
|
+
--
|
|
9
|
+
-- ROLLOUT NOTE: this migration replaces the primary key on `(name)` with
|
|
10
|
+
-- `(organization_id, name)` non-atomically. Lobu's deployment model is a
|
|
11
|
+
-- single embedded Node process with atomic swap (see "Embedded-only
|
|
12
|
+
-- deployment" in AGENTS.md), so old + new processes never run concurrently
|
|
13
|
+
-- against the same database. If you're running Lobu under a rolling-deploy
|
|
14
|
+
-- supervisor, stop traffic before applying this migration — old pods using
|
|
15
|
+
-- `ON CONFLICT (name)` will fail writes against the new schema.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE public.agent_secrets
|
|
18
|
+
ADD COLUMN IF NOT EXISTS organization_id text NOT NULL DEFAULT '';
|
|
19
|
+
|
|
20
|
+
ALTER TABLE public.agent_secrets
|
|
21
|
+
DROP CONSTRAINT IF EXISTS agent_secrets_pkey;
|
|
22
|
+
|
|
23
|
+
ALTER TABLE public.agent_secrets
|
|
24
|
+
ADD CONSTRAINT agent_secrets_pkey PRIMARY KEY (organization_id, name);
|
|
25
|
+
|
|
26
|
+
CREATE INDEX IF NOT EXISTS agent_secrets_org_id_idx
|
|
27
|
+
ON public.agent_secrets (organization_id);
|
|
28
|
+
|
|
29
|
+
-- migrate:down
|
|
30
|
+
|
|
31
|
+
ALTER TABLE public.agent_secrets
|
|
32
|
+
DROP CONSTRAINT IF EXISTS agent_secrets_pkey;
|
|
33
|
+
|
|
34
|
+
DROP INDEX IF EXISTS public.agent_secrets_org_id_idx;
|
|
35
|
+
|
|
36
|
+
-- Down-step is destructive by design: collapses per-org rows back to the
|
|
37
|
+
-- global namespace. The two DELETEs below pick a survivor non-deterministically
|
|
38
|
+
-- when multiple orgs hold the same name (global wins first; otherwise the
|
|
39
|
+
-- lexicographically-smaller org_id survives). Acceptable because rollback
|
|
40
|
+
-- after multi-org writes is already lossy — there is no "right" winner.
|
|
41
|
+
DELETE FROM public.agent_secrets a
|
|
42
|
+
USING public.agent_secrets b
|
|
43
|
+
WHERE a.name = b.name
|
|
44
|
+
AND a.organization_id <> ''
|
|
45
|
+
AND b.organization_id = '';
|
|
46
|
+
|
|
47
|
+
DELETE FROM public.agent_secrets a
|
|
48
|
+
USING public.agent_secrets b
|
|
49
|
+
WHERE a.name = b.name
|
|
50
|
+
AND a.organization_id > b.organization_id;
|
|
51
|
+
|
|
52
|
+
ALTER TABLE public.agent_secrets
|
|
53
|
+
ADD CONSTRAINT agent_secrets_pkey PRIMARY KEY (name);
|
|
54
|
+
|
|
55
|
+
ALTER TABLE public.agent_secrets
|
|
56
|
+
DROP COLUMN IF EXISTS organization_id;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Flatten the agents table: there is no longer a template/sandbox split.
|
|
4
|
+
-- One agents row = one logical agent. Definitions, providers, settings all
|
|
5
|
+
-- live on this row and are agent-only (not per-user).
|
|
6
|
+
--
|
|
7
|
+
-- Per-user state has its own homes:
|
|
8
|
+
-- - user_auth_profiles (per-user-per-agent OAuth tokens) — already canonical
|
|
9
|
+
-- - agent_channel_bindings (where the agent operates)
|
|
10
|
+
-- - agent_users (who has interacted)
|
|
11
|
+
--
|
|
12
|
+
-- ROLLOUT NOTE: this migration deletes every sandbox row. There is no
|
|
13
|
+
-- pre-cutover production data to preserve (per buremba). If that ever
|
|
14
|
+
-- changes, the rollback below restores the columns but NOT the deleted
|
|
15
|
+
-- rows.
|
|
16
|
+
|
|
17
|
+
-- 1. Drop sandbox rows. Anything with template_agent_id or parent_connection_id
|
|
18
|
+
-- set was a per-user / per-connection sandbox.
|
|
19
|
+
DELETE FROM public.agents
|
|
20
|
+
WHERE template_agent_id IS NOT NULL
|
|
21
|
+
OR parent_connection_id IS NOT NULL;
|
|
22
|
+
|
|
23
|
+
-- 2. Drop indexes on the going-away columns.
|
|
24
|
+
DROP INDEX IF EXISTS public.agents_template_agent_id_idx;
|
|
25
|
+
DROP INDEX IF EXISTS public.agents_parent_connection_id_idx;
|
|
26
|
+
|
|
27
|
+
-- 3. Drop the columns themselves.
|
|
28
|
+
ALTER TABLE public.agents DROP COLUMN IF EXISTS template_agent_id;
|
|
29
|
+
ALTER TABLE public.agents DROP COLUMN IF EXISTS parent_connection_id;
|
|
30
|
+
|
|
31
|
+
-- 4. Drop legacy / dead-code columns.
|
|
32
|
+
-- auth_profiles: superseded by user_auth_profiles table (per-user-per-agent).
|
|
33
|
+
-- mcp_install_notified: per-user UI dismissal state, never used at runtime.
|
|
34
|
+
ALTER TABLE public.agents DROP COLUMN IF EXISTS auth_profiles;
|
|
35
|
+
ALTER TABLE public.agents DROP COLUMN IF EXISTS mcp_install_notified;
|
|
36
|
+
|
|
37
|
+
-- migrate:down
|
|
38
|
+
|
|
39
|
+
-- Restore columns (data is gone — the DELETE is irreversible).
|
|
40
|
+
ALTER TABLE public.agents ADD COLUMN IF NOT EXISTS template_agent_id text;
|
|
41
|
+
ALTER TABLE public.agents ADD COLUMN IF NOT EXISTS parent_connection_id text;
|
|
42
|
+
ALTER TABLE public.agents ADD COLUMN IF NOT EXISTS auth_profiles jsonb DEFAULT '[]'::jsonb;
|
|
43
|
+
ALTER TABLE public.agents ADD COLUMN IF NOT EXISTS mcp_install_notified jsonb DEFAULT '{}'::jsonb;
|
|
44
|
+
|
|
45
|
+
CREATE INDEX IF NOT EXISTS agents_template_agent_id_idx
|
|
46
|
+
ON public.agents (template_agent_id);
|
|
47
|
+
CREATE INDEX IF NOT EXISTS agents_parent_connection_id_idx
|
|
48
|
+
ON public.agents (parent_connection_id);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Add a per-connector capability gate for worker dispatch. Workers advertise
|
|
4
|
+
-- their capabilities on poll; the runs scheduler only assigns connector runs
|
|
5
|
+
-- to workers whose capabilities include the connector's required_capability.
|
|
6
|
+
-- NULL means "no special capability required" (the default for API/browser
|
|
7
|
+
-- connectors that the existing fleet can run).
|
|
8
|
+
--
|
|
9
|
+
-- `runtime` carries platform metadata for device-bound connectors (e.g.
|
|
10
|
+
-- `{"platforms": ["macos"]}` for apple.screen_time / local.directory, which
|
|
11
|
+
-- only run inside Lobu for Mac — that data is unreachable from a server-side
|
|
12
|
+
-- worker). NULL = cloud connector.
|
|
13
|
+
--
|
|
14
|
+
-- Initial use case: apple.screen_time and local.directory, served by Lobu for
|
|
15
|
+
-- Mac polling /api/workers/* as a user-scoped device worker.
|
|
16
|
+
|
|
17
|
+
ALTER TABLE public.connector_definitions
|
|
18
|
+
ADD COLUMN IF NOT EXISTS required_capability text,
|
|
19
|
+
ADD COLUMN IF NOT EXISTS runtime jsonb;
|
|
20
|
+
|
|
21
|
+
CREATE INDEX IF NOT EXISTS connector_definitions_required_capability_idx
|
|
22
|
+
ON public.connector_definitions (required_capability)
|
|
23
|
+
WHERE required_capability IS NOT NULL;
|
|
24
|
+
|
|
25
|
+
CREATE TABLE IF NOT EXISTS public.device_workers (
|
|
26
|
+
user_id text NOT NULL,
|
|
27
|
+
worker_id text NOT NULL,
|
|
28
|
+
platform text,
|
|
29
|
+
app_version text,
|
|
30
|
+
capabilities jsonb NOT NULL DEFAULT '[]'::jsonb,
|
|
31
|
+
label text,
|
|
32
|
+
first_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
33
|
+
last_seen_at timestamptz NOT NULL DEFAULT now(),
|
|
34
|
+
PRIMARY KEY (user_id, worker_id)
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
CREATE INDEX IF NOT EXISTS device_workers_user_id_idx
|
|
38
|
+
ON public.device_workers (user_id);
|
|
39
|
+
|
|
40
|
+
-- migrate:down
|
|
41
|
+
|
|
42
|
+
DROP INDEX IF EXISTS public.device_workers_user_id_idx;
|
|
43
|
+
DROP TABLE IF EXISTS public.device_workers;
|
|
44
|
+
DROP INDEX IF EXISTS public.connector_definitions_required_capability_idx;
|
|
45
|
+
ALTER TABLE public.connector_definitions
|
|
46
|
+
DROP COLUMN IF EXISTS runtime,
|
|
47
|
+
DROP COLUMN IF EXISTS required_capability;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Make a connection's execution target explicit, and give every device worker
|
|
4
|
+
-- a home organization.
|
|
5
|
+
--
|
|
6
|
+
-- connections.device_worker_id (nullable) is the binding:
|
|
7
|
+
-- NULL -> runs on the cloud connector-worker pool (today's behavior)
|
|
8
|
+
-- set -> runs are pinned to that device worker
|
|
9
|
+
-- For device-type connectors the binding is mandatory; for cloud connectors
|
|
10
|
+
-- it's an optional override. A connection can only be pinned to a device that
|
|
11
|
+
-- is attached to that connection's organization.
|
|
12
|
+
--
|
|
13
|
+
-- device_workers.organization_id is the device's home org — chosen at setup,
|
|
14
|
+
-- defaulting to the owner's personal workspace. The device's connectors live
|
|
15
|
+
-- there; re-attaching the device to a different org (a member of which the
|
|
16
|
+
-- owner must be) is the only knob. There is no per-connection device→org grant.
|
|
17
|
+
|
|
18
|
+
-- Surrogate key for device_workers so connections / UI can reference a device
|
|
19
|
+
-- by a single stable id. The (user_id, worker_id) primary key stays.
|
|
20
|
+
ALTER TABLE public.device_workers
|
|
21
|
+
ADD COLUMN IF NOT EXISTS id uuid NOT NULL DEFAULT gen_random_uuid(),
|
|
22
|
+
ADD COLUMN IF NOT EXISTS organization_id text;
|
|
23
|
+
|
|
24
|
+
CREATE UNIQUE INDEX IF NOT EXISTS device_workers_id_key
|
|
25
|
+
ON public.device_workers (id);
|
|
26
|
+
|
|
27
|
+
CREATE INDEX IF NOT EXISTS idx_device_workers_organization_id
|
|
28
|
+
ON public.device_workers (organization_id)
|
|
29
|
+
WHERE organization_id IS NOT NULL;
|
|
30
|
+
|
|
31
|
+
ALTER TABLE public.connections
|
|
32
|
+
ADD COLUMN IF NOT EXISTS device_worker_id uuid;
|
|
33
|
+
|
|
34
|
+
DO $$
|
|
35
|
+
BEGIN
|
|
36
|
+
IF NOT EXISTS (
|
|
37
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'connections_device_worker_id_fkey'
|
|
38
|
+
) THEN
|
|
39
|
+
ALTER TABLE public.connections
|
|
40
|
+
ADD CONSTRAINT connections_device_worker_id_fkey
|
|
41
|
+
FOREIGN KEY (device_worker_id)
|
|
42
|
+
REFERENCES public.device_workers (id)
|
|
43
|
+
ON DELETE SET NULL;
|
|
44
|
+
END IF;
|
|
45
|
+
END$$;
|
|
46
|
+
|
|
47
|
+
CREATE INDEX IF NOT EXISTS idx_connections_device_worker_id
|
|
48
|
+
ON public.connections (device_worker_id)
|
|
49
|
+
WHERE device_worker_id IS NOT NULL;
|
|
50
|
+
|
|
51
|
+
-- Attach existing devices to their owner's personal workspace (no-op on a
|
|
52
|
+
-- fresh database — there are no users yet; the device heartbeat sets this for
|
|
53
|
+
-- new devices either way).
|
|
54
|
+
UPDATE public.device_workers dw
|
|
55
|
+
SET organization_id = (
|
|
56
|
+
SELECT o.id FROM public.organization o
|
|
57
|
+
WHERE (o.metadata::jsonb)->>'personal_org_for_user_id' = dw.user_id
|
|
58
|
+
LIMIT 1
|
|
59
|
+
)
|
|
60
|
+
WHERE dw.organization_id IS NULL;
|
|
61
|
+
|
|
62
|
+
-- Backfill: existing auto-wired personal-org device connections (created_by
|
|
63
|
+
-- set, no auth profile) whose owner has exactly one device get pinned to that
|
|
64
|
+
-- device — but at most one per (org, connector_key, owner) so the unique index
|
|
65
|
+
-- created below can never be violated. Ambiguous ones stay NULL and the UI
|
|
66
|
+
-- prompts for a device.
|
|
67
|
+
UPDATE public.connections c
|
|
68
|
+
SET device_worker_id = dw.id
|
|
69
|
+
FROM (
|
|
70
|
+
-- Users with exactly one device worker (no min(uuid) needed — and Postgres
|
|
71
|
+
-- has no aggregate for uuid anyway).
|
|
72
|
+
SELECT dw1.user_id, dw1.id
|
|
73
|
+
FROM public.device_workers dw1
|
|
74
|
+
WHERE NOT EXISTS (
|
|
75
|
+
SELECT 1 FROM public.device_workers dw2
|
|
76
|
+
WHERE dw2.user_id = dw1.user_id AND dw2.id <> dw1.id
|
|
77
|
+
)
|
|
78
|
+
) dw
|
|
79
|
+
WHERE c.created_by = dw.user_id
|
|
80
|
+
AND c.device_worker_id IS NULL
|
|
81
|
+
AND c.deleted_at IS NULL
|
|
82
|
+
AND c.auth_profile_id IS NULL
|
|
83
|
+
AND c.connector_key IN (
|
|
84
|
+
SELECT key FROM public.connector_definitions WHERE required_capability IS NOT NULL
|
|
85
|
+
)
|
|
86
|
+
AND c.id = (
|
|
87
|
+
SELECT min(c2.id) FROM public.connections c2
|
|
88
|
+
WHERE c2.organization_id = c.organization_id
|
|
89
|
+
AND c2.connector_key = c.connector_key
|
|
90
|
+
AND c2.created_by = c.created_by
|
|
91
|
+
AND c2.deleted_at IS NULL
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
-- One active connection per (org, connector, device). A second device backing
|
|
95
|
+
-- the same connector is a second connection. Doubles as DB-level idempotency
|
|
96
|
+
-- for the create-vs-auto-wire race. Created AFTER the backfill above.
|
|
97
|
+
DROP INDEX IF EXISTS public.idx_connections_org_connector_device_live;
|
|
98
|
+
CREATE UNIQUE INDEX idx_connections_org_connector_device_live
|
|
99
|
+
ON public.connections (organization_id, connector_key, device_worker_id)
|
|
100
|
+
WHERE deleted_at IS NULL AND device_worker_id IS NOT NULL;
|
|
101
|
+
|
|
102
|
+
-- migrate:down
|
|
103
|
+
|
|
104
|
+
DROP INDEX IF EXISTS public.idx_connections_org_connector_device_live;
|
|
105
|
+
DROP INDEX IF EXISTS public.idx_connections_device_worker_id;
|
|
106
|
+
ALTER TABLE public.connections
|
|
107
|
+
DROP CONSTRAINT IF EXISTS connections_device_worker_id_fkey,
|
|
108
|
+
DROP COLUMN IF EXISTS device_worker_id;
|
|
109
|
+
DROP INDEX IF EXISTS public.device_workers_id_key;
|
|
110
|
+
DROP INDEX IF EXISTS public.idx_device_workers_organization_id;
|
|
111
|
+
ALTER TABLE public.device_workers
|
|
112
|
+
DROP COLUMN IF EXISTS id,
|
|
113
|
+
DROP COLUMN IF EXISTS organization_id;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Add a stable `slug` identity to `connections` so `lobu apply` can diff
|
|
4
|
+
-- connections by an immutable key instead of the mutable `display_name`.
|
|
5
|
+
--
|
|
6
|
+
-- Mirrors the existing `auth_profiles.slug` design: text slug, unique per org
|
|
7
|
+
-- among live rows (partial index on `deleted_at IS NULL`), generated from the
|
|
8
|
+
-- display name when not supplied explicitly.
|
|
9
|
+
--
|
|
10
|
+
-- Backfill MUST produce exactly what `ensureUniqueConnectionSlug` /
|
|
11
|
+
-- `slugifyConnectionName` in packages/server/src/utils/connections.ts would
|
|
12
|
+
-- generate (that file is the source of truth):
|
|
13
|
+
-- 1. base = slugify(display_name); if empty, slugify(connector_key); if still
|
|
14
|
+
-- empty, the literal 'connection'. slugify = lowercase, every run of
|
|
15
|
+
-- non-alphanumerics -> '-', trim leading/trailing '-'.
|
|
16
|
+
-- 2. Collisions per (organization_id) among live (`deleted_at IS NULL`) rows
|
|
17
|
+
-- are resolved with a deterministic numeric suffix loop: base, base-2,
|
|
18
|
+
-- base-3, ... assigned in ascending id order. Soft-deleted rows do not
|
|
19
|
+
-- participate in the unique index, so they keep their base slug freely.
|
|
20
|
+
|
|
21
|
+
ALTER TABLE public.connections
|
|
22
|
+
ADD COLUMN IF NOT EXISTS slug text;
|
|
23
|
+
|
|
24
|
+
-- slugify(display_name) -> slugify(connector_key) -> 'connection'
|
|
25
|
+
WITH base AS (
|
|
26
|
+
SELECT
|
|
27
|
+
c.id,
|
|
28
|
+
coalesce(
|
|
29
|
+
NULLIF(
|
|
30
|
+
regexp_replace(
|
|
31
|
+
regexp_replace(lower(coalesce(c.display_name, '')), '[^a-z0-9]+', '-', 'g'),
|
|
32
|
+
'(^-+|-+$)', '', 'g'
|
|
33
|
+
),
|
|
34
|
+
''
|
|
35
|
+
),
|
|
36
|
+
NULLIF(
|
|
37
|
+
regexp_replace(
|
|
38
|
+
regexp_replace(lower(coalesce(c.connector_key, '')), '[^a-z0-9]+', '-', 'g'),
|
|
39
|
+
'(^-+|-+$)', '', 'g'
|
|
40
|
+
),
|
|
41
|
+
''
|
|
42
|
+
),
|
|
43
|
+
'connection'
|
|
44
|
+
) AS base_slug
|
|
45
|
+
FROM public.connections c
|
|
46
|
+
)
|
|
47
|
+
UPDATE public.connections c
|
|
48
|
+
SET slug = b.base_slug
|
|
49
|
+
FROM base b
|
|
50
|
+
WHERE b.id = c.id
|
|
51
|
+
AND c.slug IS NULL;
|
|
52
|
+
|
|
53
|
+
-- Resolve collisions to base / base-2 / base-3 / ... in ascending id order.
|
|
54
|
+
-- Loops until no live (deleted_at IS NULL) duplicates remain — a re-assigned
|
|
55
|
+
-- `base-N` could itself collide with another row whose base slug is already
|
|
56
|
+
-- `base-N`, so a single pass is not enough.
|
|
57
|
+
--
|
|
58
|
+
-- This produces a deterministic, collision-free assignment with the same
|
|
59
|
+
-- semantics as the runtime (slugified connector_key fallback, numeric `-N`
|
|
60
|
+
-- suffixing). It is NOT guaranteed to be byte-identical to what
|
|
61
|
+
-- `ensureUniqueConnectionSlug` would pick for pathological mixed-name sets
|
|
62
|
+
-- (the runtime resolves in row-creation order against live DB state, which
|
|
63
|
+
-- pure SQL can't replay) — `packages/server/src/utils/connections.ts` is the
|
|
64
|
+
-- source of truth for new rows.
|
|
65
|
+
DO $$
|
|
66
|
+
DECLARE
|
|
67
|
+
v_changed integer;
|
|
68
|
+
BEGIN
|
|
69
|
+
LOOP
|
|
70
|
+
WITH ranked AS (
|
|
71
|
+
SELECT
|
|
72
|
+
id,
|
|
73
|
+
organization_id,
|
|
74
|
+
slug,
|
|
75
|
+
-- strip any suffix we may have appended on a prior pass so the
|
|
76
|
+
-- base groups stay stable across iterations
|
|
77
|
+
regexp_replace(slug, '-[0-9]+$', '') AS base_slug,
|
|
78
|
+
row_number() OVER (
|
|
79
|
+
PARTITION BY organization_id, regexp_replace(slug, '-[0-9]+$', '')
|
|
80
|
+
ORDER BY id
|
|
81
|
+
) AS rn
|
|
82
|
+
FROM public.connections
|
|
83
|
+
WHERE deleted_at IS NULL
|
|
84
|
+
),
|
|
85
|
+
target AS (
|
|
86
|
+
SELECT
|
|
87
|
+
id,
|
|
88
|
+
CASE WHEN rn = 1 THEN base_slug ELSE base_slug || '-' || rn::text END AS desired_slug
|
|
89
|
+
FROM ranked
|
|
90
|
+
)
|
|
91
|
+
UPDATE public.connections c
|
|
92
|
+
SET slug = t.desired_slug
|
|
93
|
+
FROM target t
|
|
94
|
+
WHERE t.id = c.id
|
|
95
|
+
AND c.slug IS DISTINCT FROM t.desired_slug;
|
|
96
|
+
|
|
97
|
+
GET DIAGNOSTICS v_changed = ROW_COUNT;
|
|
98
|
+
EXIT WHEN v_changed = 0;
|
|
99
|
+
END LOOP;
|
|
100
|
+
END $$;
|
|
101
|
+
|
|
102
|
+
-- Guard: there must be no live-slug duplicate per org before the unique index.
|
|
103
|
+
DO $$
|
|
104
|
+
DECLARE
|
|
105
|
+
v_dups integer;
|
|
106
|
+
BEGIN
|
|
107
|
+
SELECT count(*) INTO v_dups
|
|
108
|
+
FROM (
|
|
109
|
+
SELECT organization_id, slug
|
|
110
|
+
FROM public.connections
|
|
111
|
+
WHERE deleted_at IS NULL
|
|
112
|
+
GROUP BY organization_id, slug
|
|
113
|
+
HAVING count(*) > 1
|
|
114
|
+
) d;
|
|
115
|
+
IF v_dups > 0 THEN
|
|
116
|
+
RAISE EXCEPTION 'connections.slug backfill left % duplicate (organization_id, slug) group(s) among live rows', v_dups;
|
|
117
|
+
END IF;
|
|
118
|
+
END $$;
|
|
119
|
+
|
|
120
|
+
ALTER TABLE public.connections
|
|
121
|
+
ALTER COLUMN slug SET NOT NULL;
|
|
122
|
+
|
|
123
|
+
CREATE UNIQUE INDEX IF NOT EXISTS connections_org_slug_unique
|
|
124
|
+
ON public.connections (organization_id, slug)
|
|
125
|
+
WHERE deleted_at IS NULL;
|
|
126
|
+
|
|
127
|
+
-- migrate:down
|
|
128
|
+
|
|
129
|
+
DROP INDEX IF EXISTS public.connections_org_slug_unique;
|
|
130
|
+
ALTER TABLE public.connections
|
|
131
|
+
DROP COLUMN IF EXISTS slug;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Maps a chat-platform user (Slack `U…`, …) to a Lobu account. Recorded as a
|
|
4
|
+
-- side effect of `/lobu link <code>` — the code is minted by an authenticated
|
|
5
|
+
-- `lobu run`, so `oauth_states.payload.createdBy` is the Lobu user. Once a user
|
|
6
|
+
-- is linked here, they can re-bind any chat to an agent they can manage via
|
|
7
|
+
-- `/lobu link <agentId>` without minting a fresh code.
|
|
8
|
+
|
|
9
|
+
CREATE TABLE IF NOT EXISTS public.chat_user_identities (
|
|
10
|
+
platform text NOT NULL,
|
|
11
|
+
team_id text NOT NULL DEFAULT '', -- workspace id; '' for platforms without one
|
|
12
|
+
platform_user_id text NOT NULL,
|
|
13
|
+
lobu_user_id text NOT NULL REFERENCES public."user"(id) ON DELETE CASCADE,
|
|
14
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
15
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
16
|
+
PRIMARY KEY (platform, team_id, platform_user_id)
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
CREATE INDEX IF NOT EXISTS chat_user_identities_lobu_user_idx
|
|
20
|
+
ON public.chat_user_identities (lobu_user_id);
|
|
21
|
+
|
|
22
|
+
-- migrate:down
|
|
23
|
+
|
|
24
|
+
DROP TABLE IF EXISTS public.chat_user_identities;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- migrate:up
|
|
2
|
+
|
|
3
|
+
-- Let an auth_profile of kind 'browser_session' live on a specific device worker
|
|
4
|
+
-- instead of holding cookies in auth_data. When device_worker_id is set, cookies
|
|
5
|
+
-- live on disk inside the Mac app's managed --user-data-dir at user_data_dir;
|
|
6
|
+
-- the server never sees them. Cloud/fleet path (device_worker_id NULL,
|
|
7
|
+
-- auth_data populated) is unchanged.
|
|
8
|
+
|
|
9
|
+
ALTER TABLE public.auth_profiles
|
|
10
|
+
ADD COLUMN IF NOT EXISTS device_worker_id uuid,
|
|
11
|
+
ADD COLUMN IF NOT EXISTS browser_kind text,
|
|
12
|
+
ADD COLUMN IF NOT EXISTS user_data_dir text;
|
|
13
|
+
|
|
14
|
+
DO $$
|
|
15
|
+
BEGIN
|
|
16
|
+
IF NOT EXISTS (
|
|
17
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'auth_profiles_device_worker_id_fkey'
|
|
18
|
+
) THEN
|
|
19
|
+
ALTER TABLE public.auth_profiles
|
|
20
|
+
ADD CONSTRAINT auth_profiles_device_worker_id_fkey
|
|
21
|
+
FOREIGN KEY (device_worker_id)
|
|
22
|
+
REFERENCES public.device_workers (id)
|
|
23
|
+
ON DELETE CASCADE;
|
|
24
|
+
END IF;
|
|
25
|
+
END$$;
|
|
26
|
+
|
|
27
|
+
DO $$
|
|
28
|
+
BEGIN
|
|
29
|
+
IF NOT EXISTS (
|
|
30
|
+
SELECT 1 FROM pg_constraint WHERE conname = 'auth_profiles_browser_kind_check'
|
|
31
|
+
) THEN
|
|
32
|
+
ALTER TABLE public.auth_profiles
|
|
33
|
+
ADD CONSTRAINT auth_profiles_browser_kind_check
|
|
34
|
+
CHECK (browser_kind IS NULL OR browser_kind = ANY (ARRAY['chrome','brave','arc','edge']));
|
|
35
|
+
END IF;
|
|
36
|
+
END$$;
|
|
37
|
+
|
|
38
|
+
CREATE INDEX IF NOT EXISTS auth_profiles_device_worker_idx
|
|
39
|
+
ON public.auth_profiles (device_worker_id)
|
|
40
|
+
WHERE device_worker_id IS NOT NULL;
|
|
41
|
+
|
|
42
|
+
-- migrate:down
|
|
43
|
+
|
|
44
|
+
DROP INDEX IF EXISTS public.auth_profiles_device_worker_idx;
|
|
45
|
+
ALTER TABLE public.auth_profiles
|
|
46
|
+
DROP CONSTRAINT IF EXISTS auth_profiles_browser_kind_check,
|
|
47
|
+
DROP CONSTRAINT IF EXISTS auth_profiles_device_worker_id_fkey,
|
|
48
|
+
DROP COLUMN IF EXISTS user_data_dir,
|
|
49
|
+
DROP COLUMN IF EXISTS browser_kind,
|
|
50
|
+
DROP COLUMN IF EXISTS device_worker_id;
|