@opengeni/db 1.0.1 → 3.1.0-canary.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/dist/attached-browser-devices.d.ts +7 -0
- package/dist/automations.d.ts +154 -0
- package/dist/canonical-human-identities.js +3 -3
- package/dist/capability-integrations.d.ts +13 -0
- package/dist/child-lifecycle-notices.d.ts +68 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-EULAN2NP.js} +2 -2
- package/dist/{chunk-CUPM3FGB.js → chunk-NJVZPWXY.js} +3 -3
- package/dist/{chunk-4SR4HKKA.js → chunk-NMUBV7Z5.js} +19 -11
- package/dist/chunk-NMUBV7Z5.js.map +1 -0
- package/dist/chunk-OF5OBB76.js +4457 -0
- package/dist/chunk-OF5OBB76.js.map +1 -0
- package/dist/{chunk-6ZYKCRFO.js → chunk-PR2WMLOQ.js} +7580 -4417
- package/dist/chunk-PR2WMLOQ.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-SCPUJS53.js} +41 -4
- package/dist/chunk-SCPUJS53.js.map +1 -0
- package/dist/{chunk-SK7YYHBI.js → chunk-SIBYBA2D.js} +46 -1
- package/dist/chunk-SIBYBA2D.js.map +1 -0
- package/dist/chunk-TIKF3KTI.js +383 -0
- package/dist/chunk-TIKF3KTI.js.map +1 -0
- package/dist/chunk-ZZ5LJEFX.js +516 -0
- package/dist/chunk-ZZ5LJEFX.js.map +1 -0
- package/dist/codemode-operations.d.ts +1 -6
- package/dist/company-brain-context-selection-schema.d.ts +705 -0
- package/dist/company-brain-context-selection.d.ts +94 -0
- package/dist/company-brain-governed-writes.d.ts +24 -0
- package/dist/company-brain.d.ts +55 -0
- package/dist/company-profile-agent-admin.d.ts +15 -0
- package/dist/company-profile-schema.d.ts +619 -2
- package/dist/company-profile.d.ts +9 -4
- package/dist/connection-authority.d.ts +95 -0
- package/dist/connection-scopes.d.ts +8 -0
- package/dist/connection-tenancy-backfill.d.ts +41 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +19 -0
- package/dist/editable-artifact-durable-export.js +2 -2
- package/dist/editable-artifacts.d.ts +2 -2
- package/dist/editable-artifacts.js +3 -3
- package/dist/governed-learning-activation-schema.d.ts +984 -0
- package/dist/governed-learning-activation.d.ts +98 -0
- package/dist/governed-learning-evaluator-schema.d.ts +633 -0
- package/dist/governed-learning-evaluator.d.ts +28 -0
- package/dist/index.d.ts +1595 -81
- package/dist/index.js +21571 -8126
- package/dist/index.js.map +1 -1
- package/dist/insights.d.ts +18 -0
- package/dist/integration-bindings.d.ts +1 -0
- package/dist/integration-facets.d.ts +30 -0
- package/dist/interaction-placement-loss.d.ts +22 -0
- package/dist/managed-human-provisioning.d.ts +25 -0
- package/dist/memory-domain.d.ts +3 -3
- package/dist/migrate.d.ts +11 -1
- package/dist/migrate.js +1 -1
- package/dist/new-session-drafts.d.ts +43 -5
- package/dist/organization-membership-backfill.d.ts +174 -0
- package/dist/organization-membership-lifecycle.d.ts +162 -0
- package/dist/pr-review.d.ts +107 -0
- package/dist/preference-registry-schema.d.ts +2 -0
- package/dist/preference-registry.d.ts +2 -0
- package/dist/provision-roles.js +1 -1
- package/dist/runtime-posture.d.ts +22 -8
- package/dist/schema.d.ts +24914 -13703
- package/dist/schema.js +99 -1
- package/dist/scoped-knowledge-schema.d.ts +52 -1
- package/dist/session-background-commands.d.ts +125 -0
- package/dist/session-background-commands.js +34 -0
- package/dist/session-background-commands.js.map +1 -0
- package/dist/session-control.d.ts +156 -3
- package/dist/session-goal-pacing.d.ts +68 -0
- package/dist/session-queue-commands.d.ts +41 -4
- package/dist/session-realtime-ledger.d.ts +2 -0
- package/dist/session-tenancy.d.ts +71 -1
- package/dist/session-tenancy.js +25 -3
- package/dist/session-tool-call-settlement.d.ts +9 -4
- package/dist/slack-routing-personal-workspace.d.ts +23 -0
- package/dist/slack-routing.d.ts +153 -0
- package/dist/slack-task-policy-schema.d.ts +34 -0
- package/dist/slack-task-policy.d.ts +8 -0
- package/dist/slack-user-link-access.d.ts +1 -0
- package/dist/task-notes-schema.d.ts +594 -0
- package/dist/task-notes.d.ts +39 -0
- package/dist/turn-initiator.d.ts +2 -0
- package/dist/user-resource-authority.d.ts +109 -0
- package/dist/video-generation.js +3 -3
- package/dist/workspace-authority.d.ts +81 -0
- package/dist/workspace-instruction-policies-schema.d.ts +279 -0
- package/dist/workspace-instruction-policies.d.ts +62 -0
- package/dist/workspace-learning-policy.d.ts +1 -0
- package/dist/workspace-membership-permissions.d.ts +2 -0
- package/drizzle/0240_enrollment_connection_authority.sql +39 -0
- package/drizzle/0241_atomic_personal_resource_delegation.sql +1000 -0
- package/drizzle/0241_enrollment_agent_runtime.sql +70 -0
- package/drizzle/0244_slack_app_home_refresh_queue.sql +120 -0
- package/drizzle/0245_model_context_contribution_facts.sql +88 -0
- package/drizzle/0246_integration_personal_instance_authority.sql +70 -0
- package/drizzle/0247_terraform_stacks_provenance_repair.sql +428 -0
- package/drizzle/0248_terraform_stacks_component_resolution_fence.sql +83 -0
- package/drizzle/0249_personal_resource_delegation_authority_correction.sql +662 -0
- package/drizzle/0250_direct_retained_process_owner_liveness.sql +199 -0
- package/drizzle/0251_connected_machine_operation_policy.sql +45 -0
- package/drizzle/0252_scheduled_personal_resource_delegation.sql +1867 -0
- package/drizzle/0253_common_user_resource_authority_lifecycle.sql +1555 -0
- package/drizzle/0254_scoped_variable_set_authority.sql +1255 -0
- package/drizzle/0255_company_brain_governed_write_proposals.sql +524 -0
- package/drizzle/0256_connection_authority_delegation.sql +703 -0
- package/drizzle/0257_goal_revision_decisions_and_root_constraints.sql +436 -0
- package/drizzle/0258_three_scope_document_knowledge_authority.sql +1273 -0
- package/drizzle/0259_company_brain_context_selection_receipts.sql +869 -0
- package/drizzle/0260_task_note_knowledge_promotion.sql +820 -0
- package/drizzle/0261_preference_knowledge_proposal_actor_binding.sql +471 -0
- package/drizzle/0262_scoped_connected_machines_and_rigs.sql +1700 -0
- package/drizzle/0263_organization_membership_lifecycle.sql +3284 -0
- package/drizzle/0264_connection_authority_runtime_activation.sql +1084 -0
- package/drizzle/0266_company_brain_context_receipt_inspection.sql +156 -0
- package/drizzle/0268_governed_learning_decision_receipts.sql +825 -0
- package/drizzle/0269_governed_learning_activation_controller.sql +1874 -0
- package/drizzle/0270_governed_learning_history_inspection.sql +170 -0
- package/drizzle/0271_company_brain_retrieval_only_default.sql +524 -0
- package/drizzle/0272_human_confirmed_learning_activation.sql +591 -0
- package/drizzle/0273_scheduled_variable_set_materialization.sql +268 -0
- package/drizzle/0274_human_confirmed_knowledge_review.sql +310 -0
- package/drizzle/0275_scheduled_connection_authority.sql +5527 -0
- package/drizzle/0276_onboarding_proposal_initiating_human_guc.sql +198 -0
- package/drizzle/0277_workspace_writer_authority_attribution.sql +393 -0
- package/drizzle/0278_workspace_membership_removal_fencing.sql +1174 -0
- package/drizzle/0279_workspace_connection_use_lane.sql +343 -0
- package/drizzle/0280_connection_and_variable_set_audit_attribution.sql +773 -0
- package/drizzle/0281_viewer_holder_authority_claims.sql +52 -0
- package/drizzle/0282_variable_set_session_attach_attribution.sql +178 -0
- package/drizzle/0283_editable_spreadsheet_authored_state.sql +82 -0
- package/drizzle/0284_truthful_human_confirmed_review_reason.sql +930 -0
- package/drizzle/0285_organization_tenancy_inventory.sql +329 -0
- package/drizzle/0286_widen_task_note_expiry_ceiling.sql +391 -0
- package/drizzle/0287_open_suffix_pending_tool_calls.sql +18 -0
- package/drizzle/0288_attached_browser_reenrollment.sql +64 -0
- package/drizzle/0289_session_composer_policy_authority.sql +597 -0
- package/drizzle/0290_organization_membership_backfill.sql +268 -0
- package/drizzle/0291_resource_authority_classification_assertion.sql +371 -0
- package/drizzle/0292_truthful_tenancy_inventory_counters.sql +291 -0
- package/drizzle/0293_confirm_time_rule_rebaseline.sql +615 -0
- package/drizzle/0294_preference_activation_authority.sql +237 -0
- package/drizzle/0295_retire_legacy_standing_memory_mode.sql +476 -0
- package/drizzle/0296_force_rls_backfill_noop_repair.sql +215 -0
- package/drizzle/0297_session_ownership_classification_and_backfill.sql +752 -0
- package/drizzle/0298_organization_tenancy_parity.sql +609 -0
- package/drizzle/0299_organization_membership_lock_order.sql +291 -0
- package/drizzle/0300_tenancy_backfill_ledger.sql +459 -0
- package/drizzle/0301_session_snapshot_and_pin_visibility.sql +181 -0
- package/drizzle/0302_personal_workspace_session_ownership.sql +303 -0
- package/drizzle/0303_session_tenancy_product_activation.sql +919 -0
- package/drizzle/0304_personal_workspace_private_session_reads.sql +97 -0
- package/drizzle/0305_personal_resource_grant_management.sql +584 -0
- package/drizzle/0306_atomic_personal_resource_attachments.sql +1374 -0
- package/drizzle/0307_session_attention_state.sql +51 -0
- package/drizzle/0308_session_archives.sql +35 -0
- package/drizzle/0309_channel_project_pins.sql +6 -0
- package/drizzle/0310_channel_project_order.sql +27 -0
- package/drizzle/0311_company_scope_and_private_session_create.sql +656 -0
- package/drizzle/0312_quiescent_session_tree_deletion.sql +39 -0
- package/drizzle/0313_private_child_session_authority.sql +365 -0
- package/drizzle/0314_unregistered_organization_invitations.sql +606 -0
- package/drizzle/0315_personal_github_repository_selection.sql +624 -0
- package/drizzle/0316_human_confirmed_activation_resumed_generation.sql +869 -0
- package/drizzle/0317_session_goal_continuation_hold.sql +64 -0
- package/drizzle/0318_session_turn_startup_milestones.sql +115 -0
- package/drizzle/0319_event_triggered_automations.sql +254 -0
- package/drizzle/0320_pr_review_pack.sql +113 -0
- package/drizzle/0321_slack_bot_environment_display_name.sql +15 -0
- package/drizzle/0322_session_turns_unclaimed_prompt_trigger_index.sql +5 -0
- package/drizzle/0323_organization_private_session_enablement.sql +489 -0
- package/drizzle/0324_human_confirmed_company_profile_agent_admin.sql +856 -0
- package/drizzle/0325_child_lifecycle_notices.sql +44 -0
- package/drizzle/0326_interaction_operation_error_codes.sql +62 -0
- package/drizzle/0327_slack_first_task_hint.sql +29 -0
- package/drizzle/0328_session_background_commands.sql +590 -0
- package/drizzle/0329_slack_orchestration_delivery_events.sql +91 -0
- package/drizzle/0330_api_key_descriptions.sql +20 -0
- package/drizzle/0331_managed_organization_creation.sql +163 -0
- package/drizzle/0332_organization_shared_workspace_control_plane.sql +539 -0
- package/drizzle/0333_session_turn_prompt_routing.sql +45 -0
- package/drizzle/0334_connected_machine_workspace_root.sql +99 -0
- package/drizzle/0335_slack_workspace_routing.sql +460 -0
- package/drizzle/0336_atomic_session_fork_visibility.sql +667 -0
- package/drizzle/0337_slack_routed_action_handles.sql +189 -0
- package/drizzle/0338_atomic_connected_machine_attachments.sql +410 -0
- package/drizzle/0339_document_authority_reclassification.sql +1153 -0
- package/drizzle/0340_tenancy_backfill_activation_evidence.sql +1347 -0
- package/drizzle/0341_slack_routing_probe_organization_fence.sql +109 -0
- package/package.json +10 -6
- package/src/attached-browser-devices.ts +233 -5
- package/src/automations.ts +911 -0
- package/src/browser-sessions.ts +10 -0
- package/src/capability-integrations.ts +175 -1
- package/src/child-lifecycle-notices.ts +241 -0
- package/src/codemode-operations.ts +1 -35
- package/src/company-brain-context-selection-schema.ts +100 -0
- package/src/company-brain-context-selection.ts +359 -0
- package/src/company-brain-governed-writes.ts +1170 -0
- package/src/company-brain.ts +309 -0
- package/src/company-profile-agent-admin.ts +225 -0
- package/src/company-profile-schema.ts +121 -1
- package/src/company-profile.ts +21 -0
- package/src/computer-sessions.ts +10 -1
- package/src/connection-authority.ts +290 -0
- package/src/connection-scopes.ts +20 -0
- package/src/connection-tenancy-backfill.ts +81 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +75 -2
- package/src/editable-artifacts-schema.ts +10 -5
- package/src/editable-artifacts.ts +21 -11
- package/src/governed-learning-activation-schema.ts +121 -0
- package/src/governed-learning-activation.ts +588 -0
- package/src/governed-learning-evaluator-schema.ts +72 -0
- package/src/governed-learning-evaluator.ts +217 -0
- package/src/index.ts +11651 -1548
- package/src/insights.ts +107 -0
- package/src/integration-bindings.ts +88 -6
- package/src/integration-facets.ts +287 -84
- package/src/interaction-placement-loss.ts +234 -0
- package/src/managed-human-provisioning.ts +116 -0
- package/src/memory-domain.ts +27 -32
- package/src/migrate.ts +77 -1
- package/src/new-session-drafts.ts +229 -35
- package/src/organization-membership-backfill.ts +735 -0
- package/src/organization-membership-lifecycle.ts +985 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +877 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1291 -76
- package/src/schema.ts +3303 -428
- package/src/scoped-knowledge-schema.ts +16 -8
- package/src/scoped-knowledge.ts +1 -1
- package/src/session-background-commands.ts +759 -0
- package/src/session-control.ts +640 -74
- package/src/session-goal-pacing.ts +126 -0
- package/src/session-queue-commands.ts +437 -84
- package/src/session-realtime-context.ts +3 -3
- package/src/session-realtime-ledger.ts +22 -11
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +403 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +469 -0
- package/src/slack-task-policy-schema.ts +5 -0
- package/src/slack-task-policy.ts +18 -0
- package/src/slack-user-link-access.ts +17 -1
- package/src/task-notes-schema.ts +74 -0
- package/src/task-notes.ts +124 -2
- package/src/turn-initiator.ts +8 -0
- package/src/user-resource-authority.ts +377 -0
- package/src/workspace-authority.ts +160 -0
- package/src/workspace-instruction-policies-schema.ts +53 -1
- package/src/workspace-instruction-policies.ts +528 -77
- package/src/workspace-learning-policy.ts +21 -5
- package/src/workspace-membership-permissions.ts +9 -0
- package/dist/chunk-4SR4HKKA.js.map +0 -1
- package/dist/chunk-6ZYKCRFO.js.map +0 -1
- package/dist/chunk-NXAFLCQX.js +0 -2468
- package/dist/chunk-NXAFLCQX.js.map +0 -1
- package/dist/chunk-SK7YYHBI.js.map +0 -1
- package/dist/chunk-WD5ROSLL.js +0 -117
- package/dist/chunk-WD5ROSLL.js.map +0 -1
- package/dist/chunk-ZWREYEWA.js.map +0 -1
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-EULAN2NP.js.map} +0 -0
- /package/dist/{chunk-CUPM3FGB.js.map → chunk-NJVZPWXY.js.map} +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
-- deployment-mode: rolling
|
|
2
|
+
-- Fence both Slack routing tenancy probes on the organization the caller is
|
|
3
|
+
-- acting for.
|
|
4
|
+
--
|
|
5
|
+
-- Neither probe carried an account predicate. Both are SECURITY DEFINER and
|
|
6
|
+
-- both return tenancy ids, so a caller holding another organization's
|
|
7
|
+
-- connection UUID plus a route key or a handle id learned that organization's
|
|
8
|
+
-- account, workspace and interaction ids. Exploitability is low today, because
|
|
9
|
+
-- connection ids are never caller-supplied and the Slack signature check
|
|
10
|
+
-- already fences the team, but the predicate is one line and every sibling
|
|
11
|
+
-- authority routine carries it.
|
|
12
|
+
--
|
|
13
|
+
-- Rolling by expand-and-contract: the fenced three-argument routines are added
|
|
14
|
+
-- alongside the existing two-argument ones rather than replacing them, so an
|
|
15
|
+
-- old image keeps resolving thread continuation and button clicks through the
|
|
16
|
+
-- signature it knows. A later migration drops the unfenced pair once the fleet
|
|
17
|
+
-- is on an image that calls the fenced one; dropping them here would break
|
|
18
|
+
-- every running old worker the moment this commits.
|
|
19
|
+
|
|
20
|
+
SET LOCAL lock_timeout = '5s';
|
|
21
|
+
SET LOCAL statement_timeout = '10min';
|
|
22
|
+
|
|
23
|
+
DO $slack_fenced_interaction_probe$
|
|
24
|
+
DECLARE data_schema text := current_schema();
|
|
25
|
+
BEGIN
|
|
26
|
+
EXECUTE format($ddl$
|
|
27
|
+
CREATE FUNCTION opengeni_private.resolve_slack_interaction_tenancy(
|
|
28
|
+
p_account_id uuid,
|
|
29
|
+
p_connection_id uuid,
|
|
30
|
+
p_route_key text
|
|
31
|
+
)
|
|
32
|
+
RETURNS TABLE (account_id uuid, workspace_id uuid, interaction_id uuid)
|
|
33
|
+
LANGUAGE plpgsql
|
|
34
|
+
SECURITY DEFINER
|
|
35
|
+
SET search_path = %1$I, pg_catalog
|
|
36
|
+
AS $body$
|
|
37
|
+
BEGIN
|
|
38
|
+
IF p_account_id IS NULL OR p_connection_id IS NULL OR p_route_key IS NULL THEN
|
|
39
|
+
RETURN;
|
|
40
|
+
END IF;
|
|
41
|
+
INSERT INTO opengeni_private.slack_routing_runtime_capabilities (
|
|
42
|
+
backend_pid, transaction_id, capability_kind
|
|
43
|
+
) VALUES (
|
|
44
|
+
pg_catalog.pg_backend_pid(), pg_catalog.pg_current_xact_id(), 'interaction_tenancy'
|
|
45
|
+
) ON CONFLICT DO NOTHING;
|
|
46
|
+
BEGIN
|
|
47
|
+
RETURN QUERY
|
|
48
|
+
SELECT I.account_id, I.workspace_id, I.id
|
|
49
|
+
FROM slack_interactions I
|
|
50
|
+
WHERE I.account_id = p_account_id
|
|
51
|
+
AND I.connection_id = p_connection_id
|
|
52
|
+
AND I.route_key = p_route_key
|
|
53
|
+
LIMIT 1;
|
|
54
|
+
DELETE FROM opengeni_private.slack_routing_runtime_capabilities
|
|
55
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
56
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
57
|
+
AND capability_kind = 'interaction_tenancy';
|
|
58
|
+
RETURN;
|
|
59
|
+
EXCEPTION WHEN OTHERS THEN
|
|
60
|
+
DELETE FROM opengeni_private.slack_routing_runtime_capabilities
|
|
61
|
+
WHERE backend_pid = pg_catalog.pg_backend_pid()
|
|
62
|
+
AND transaction_id = pg_catalog.pg_current_xact_id_if_assigned()
|
|
63
|
+
AND capability_kind = 'interaction_tenancy';
|
|
64
|
+
RAISE;
|
|
65
|
+
END;
|
|
66
|
+
END;
|
|
67
|
+
$body$
|
|
68
|
+
$ddl$, data_schema);
|
|
69
|
+
END
|
|
70
|
+
$slack_fenced_interaction_probe$;
|
|
71
|
+
|
|
72
|
+
REVOKE ALL ON FUNCTION opengeni_private.resolve_slack_interaction_tenancy(uuid, uuid, text)
|
|
73
|
+
FROM PUBLIC;
|
|
74
|
+
|
|
75
|
+
CREATE FUNCTION opengeni_private.resolve_slack_action_handle_tenancy(
|
|
76
|
+
p_account_id uuid,
|
|
77
|
+
p_connection_id uuid,
|
|
78
|
+
p_handle_id uuid
|
|
79
|
+
)
|
|
80
|
+
RETURNS TABLE (account_id uuid, workspace_id uuid)
|
|
81
|
+
LANGUAGE sql
|
|
82
|
+
STABLE
|
|
83
|
+
SECURITY DEFINER
|
|
84
|
+
SET search_path = opengeni_private, pg_catalog
|
|
85
|
+
AS $$
|
|
86
|
+
SELECT T.account_id, T.workspace_id
|
|
87
|
+
FROM slack_action_handle_tenancy T
|
|
88
|
+
WHERE T.account_id = p_account_id
|
|
89
|
+
AND T.handle_id = p_handle_id
|
|
90
|
+
AND T.connection_id = p_connection_id
|
|
91
|
+
LIMIT 1
|
|
92
|
+
$$;
|
|
93
|
+
|
|
94
|
+
REVOKE ALL ON FUNCTION
|
|
95
|
+
opengeni_private.resolve_slack_action_handle_tenancy(uuid, uuid, uuid) FROM PUBLIC;
|
|
96
|
+
|
|
97
|
+
DO $grants$
|
|
98
|
+
BEGIN
|
|
99
|
+
IF EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'opengeni_app') THEN
|
|
100
|
+
GRANT EXECUTE ON FUNCTION
|
|
101
|
+
opengeni_private.resolve_slack_interaction_tenancy(uuid, uuid, text) TO opengeni_app;
|
|
102
|
+
GRANT EXECUTE ON FUNCTION
|
|
103
|
+
opengeni_private.resolve_slack_action_handle_tenancy(uuid, uuid, uuid) TO opengeni_app;
|
|
104
|
+
END IF;
|
|
105
|
+
END
|
|
106
|
+
$grants$;
|
|
107
|
+
|
|
108
|
+
RESET statement_timeout;
|
|
109
|
+
RESET lock_timeout;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/db",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "3.1.0-canary.0",
|
|
4
4
|
"description": "OpenGeni persistence: Drizzle schema, RLS-scoped query layer, the SQL migration runner, and role provisioning.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -54,6 +54,10 @@
|
|
|
54
54
|
"./session-tenancy": {
|
|
55
55
|
"types": "./dist/session-tenancy.d.ts",
|
|
56
56
|
"import": "./dist/session-tenancy.js"
|
|
57
|
+
},
|
|
58
|
+
"./session-background-commands": {
|
|
59
|
+
"types": "./dist/session-background-commands.d.ts",
|
|
60
|
+
"import": "./dist/session-background-commands.js"
|
|
57
61
|
}
|
|
58
62
|
},
|
|
59
63
|
"publishConfig": {
|
|
@@ -70,11 +74,11 @@
|
|
|
70
74
|
"prepublishOnly": "bash ../../scripts/prepublish-guard"
|
|
71
75
|
},
|
|
72
76
|
"dependencies": {
|
|
73
|
-
"@opengeni/codemode": "^0.4.
|
|
74
|
-
"@opengeni/codex": "^0.2.
|
|
75
|
-
"@opengeni/config": "^0.
|
|
76
|
-
"@opengeni/contracts": "^
|
|
77
|
-
"@opengeni/network": "^0.2.2",
|
|
77
|
+
"@opengeni/codemode": "^0.4.12-canary.0",
|
|
78
|
+
"@opengeni/codex": "^0.2.18-canary.0",
|
|
79
|
+
"@opengeni/config": "^0.19.0-canary.0",
|
|
80
|
+
"@opengeni/contracts": "^2.2.0-canary.0",
|
|
81
|
+
"@opengeni/network": "^0.2.2-canary.0",
|
|
78
82
|
"drizzle-orm": "^0.45.2",
|
|
79
83
|
"postgres": "^3.4.7"
|
|
80
84
|
},
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
type AttachedBrowserDeviceListResponse as AttachedBrowserDeviceListResponseValue,
|
|
9
9
|
type AttachedBrowserInventorySnapshot as AttachedBrowserInventorySnapshotValue,
|
|
10
10
|
} from "@opengeni/contracts";
|
|
11
|
-
import { and, asc, desc, eq, inArray, isNull } from "drizzle-orm";
|
|
11
|
+
import { and, asc, desc, eq, inArray, isNull, sql } from "drizzle-orm";
|
|
12
12
|
import { type Database, withRlsContext } from "./database";
|
|
13
13
|
import {
|
|
14
14
|
advanceWorkspaceInteractionRevision,
|
|
@@ -16,6 +16,12 @@ import {
|
|
|
16
16
|
} from "./interaction-revisions";
|
|
17
17
|
import * as schema from "./schema";
|
|
18
18
|
|
|
19
|
+
const ATTACHED_DEVICE_LIVE_LIFECYCLES = ["starting", "active", "suspending", "restoring"] as const;
|
|
20
|
+
// `ending` stays off this list so an in-flight `/end` can finish physical
|
|
21
|
+
// teardown (`stopCapture` + helper exit) after Chrome reconnects.
|
|
22
|
+
|
|
23
|
+
const CONTROLLER_TRANSITION_EXPIRED = "controller_transition_expired";
|
|
24
|
+
|
|
19
25
|
type DeviceRow = typeof schema.attachedBrowserDevices.$inferSelect;
|
|
20
26
|
|
|
21
27
|
const CONSISTENT_READ = {
|
|
@@ -156,13 +162,29 @@ export async function reconcileAttachedBrowserInventory(
|
|
|
156
162
|
.where(eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId))
|
|
157
163
|
.for("update");
|
|
158
164
|
const byId = new Map(rows.map((row) => [row.id, row]));
|
|
165
|
+
const generationChangedDeviceIds = new Set<string>();
|
|
159
166
|
let changed = false;
|
|
160
167
|
for (const device of snapshot.devices) {
|
|
161
168
|
const existing = byId.get(device.id);
|
|
169
|
+
let reclaimingRevokedEnrollment = false;
|
|
162
170
|
if (existing && existing.enrollmentId !== input.enrollmentId) {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
const [previousEnrollment] = await tx
|
|
172
|
+
.select({ status: schema.enrollments.status })
|
|
173
|
+
.from(schema.enrollments)
|
|
174
|
+
.where(
|
|
175
|
+
and(
|
|
176
|
+
eq(schema.enrollments.id, existing.enrollmentId),
|
|
177
|
+
eq(schema.enrollments.accountId, input.accountId),
|
|
178
|
+
eq(schema.enrollments.workspaceId, input.workspaceId),
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
.limit(1);
|
|
182
|
+
if (previousEnrollment?.status !== "revoked") {
|
|
183
|
+
throw new AttachedBrowserInventoryConflictError(
|
|
184
|
+
"attached browser device id is already owned by another active enrollment",
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
reclaimingRevokedEnrollment = true;
|
|
166
188
|
}
|
|
167
189
|
if (!existing) {
|
|
168
190
|
await tx.insert(schema.attachedBrowserDevices).values({
|
|
@@ -189,10 +211,15 @@ export async function reconcileAttachedBrowserInventory(
|
|
|
189
211
|
changed = true;
|
|
190
212
|
continue;
|
|
191
213
|
}
|
|
192
|
-
|
|
214
|
+
if (existing.connectionGeneration !== device.connectionGeneration) {
|
|
215
|
+
generationChangedDeviceIds.add(device.id);
|
|
216
|
+
}
|
|
217
|
+
const materialChanged =
|
|
218
|
+
reclaimingRevokedEnrollment || !sameAnnouncement(existing, device);
|
|
193
219
|
await tx
|
|
194
220
|
.update(schema.attachedBrowserDevices)
|
|
195
221
|
.set({
|
|
222
|
+
enrollmentId: input.enrollmentId,
|
|
196
223
|
name: device.name,
|
|
197
224
|
profileLabel: device.profileLabel,
|
|
198
225
|
browserName: device.browserName,
|
|
@@ -217,6 +244,15 @@ export async function reconcileAttachedBrowserInventory(
|
|
|
217
244
|
changed ||= materialChanged;
|
|
218
245
|
}
|
|
219
246
|
|
|
247
|
+
if (
|
|
248
|
+
await terminalizeStaleAttachedDeviceSessions(tx, {
|
|
249
|
+
workspaceId: input.workspaceId,
|
|
250
|
+
deviceIds: [...generationChangedDeviceIds],
|
|
251
|
+
})
|
|
252
|
+
) {
|
|
253
|
+
changed = true;
|
|
254
|
+
}
|
|
255
|
+
|
|
220
256
|
const announced = new Set(snapshot.devices.map((device) => device.id));
|
|
221
257
|
const disconnectedIds = rows
|
|
222
258
|
.filter(
|
|
@@ -275,6 +311,153 @@ export async function reconcileAttachedBrowserInventory(
|
|
|
275
311
|
);
|
|
276
312
|
}
|
|
277
313
|
|
|
314
|
+
/** A new physical Chrome generation cannot inherit a prior controller token.
|
|
315
|
+
* Mark every still-live BrowserSession/ComputerSession on that exact device
|
|
316
|
+
* lost in place. Never rewrite placementInstanceId onto the successor. */
|
|
317
|
+
async function terminalizeStaleAttachedDeviceSessions(
|
|
318
|
+
tx: Database,
|
|
319
|
+
input: { workspaceId: string; deviceIds: readonly string[] },
|
|
320
|
+
): Promise<boolean> {
|
|
321
|
+
if (input.deviceIds.length === 0) return false;
|
|
322
|
+
|
|
323
|
+
const browserRows = await tx
|
|
324
|
+
.select({ id: schema.browserSessions.id })
|
|
325
|
+
.from(schema.browserSessions)
|
|
326
|
+
.where(
|
|
327
|
+
and(
|
|
328
|
+
eq(schema.browserSessions.workspaceId, input.workspaceId),
|
|
329
|
+
eq(schema.browserSessions.placementKind, "attached_device"),
|
|
330
|
+
inArray(schema.browserSessions.deviceId, [...input.deviceIds]),
|
|
331
|
+
inArray(schema.browserSessions.lifecycle, [...ATTACHED_DEVICE_LIVE_LIFECYCLES]),
|
|
332
|
+
),
|
|
333
|
+
);
|
|
334
|
+
const computerRows = await tx
|
|
335
|
+
.select({ id: schema.computerSessions.id })
|
|
336
|
+
.from(schema.computerSessions)
|
|
337
|
+
.where(
|
|
338
|
+
and(
|
|
339
|
+
eq(schema.computerSessions.workspaceId, input.workspaceId),
|
|
340
|
+
eq(schema.computerSessions.placementKind, "attached_device"),
|
|
341
|
+
inArray(schema.computerSessions.deviceId, [...input.deviceIds]),
|
|
342
|
+
inArray(schema.computerSessions.lifecycle, [...ATTACHED_DEVICE_LIVE_LIFECYCLES]),
|
|
343
|
+
),
|
|
344
|
+
);
|
|
345
|
+
if (browserRows.length === 0 && computerRows.length === 0) return false;
|
|
346
|
+
|
|
347
|
+
const browserIds = browserRows.map((row) => row.id).sort();
|
|
348
|
+
const computerIds = computerRows.map((row) => row.id).sort();
|
|
349
|
+
const resourceIds = [...browserIds, ...computerIds];
|
|
350
|
+
const operations = await tx
|
|
351
|
+
.select({ operationId: schema.interactionOperations.operationId })
|
|
352
|
+
.from(schema.interactionOperations)
|
|
353
|
+
.where(
|
|
354
|
+
and(
|
|
355
|
+
eq(schema.interactionOperations.workspaceId, input.workspaceId),
|
|
356
|
+
inArray(schema.interactionOperations.resourceId, resourceIds),
|
|
357
|
+
inArray(schema.interactionOperations.state, ["prepared", "dispatched"]),
|
|
358
|
+
),
|
|
359
|
+
);
|
|
360
|
+
const operationIds = operations.map((row) => row.operationId).sort();
|
|
361
|
+
for (const operationId of operationIds) {
|
|
362
|
+
await tx.execute(sql`
|
|
363
|
+
select operation_id from interaction_operations
|
|
364
|
+
where workspace_id = ${input.workspaceId} and operation_id = ${operationId}
|
|
365
|
+
for update
|
|
366
|
+
`);
|
|
367
|
+
}
|
|
368
|
+
for (const browserSessionId of browserIds) {
|
|
369
|
+
await tx.execute(sql`
|
|
370
|
+
select id from browser_sessions
|
|
371
|
+
where workspace_id = ${input.workspaceId} and id = ${browserSessionId}
|
|
372
|
+
for update
|
|
373
|
+
`);
|
|
374
|
+
}
|
|
375
|
+
for (const computerSessionId of computerIds) {
|
|
376
|
+
await tx.execute(sql`
|
|
377
|
+
select id from computer_sessions
|
|
378
|
+
where workspace_id = ${input.workspaceId} and id = ${computerSessionId}
|
|
379
|
+
for update
|
|
380
|
+
`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
const now = new Date();
|
|
384
|
+
if (operationIds.length > 0) {
|
|
385
|
+
await tx
|
|
386
|
+
.update(schema.interactionOperations)
|
|
387
|
+
.set({
|
|
388
|
+
state: "outcome_unknown",
|
|
389
|
+
errorCode: "outcome_unknown",
|
|
390
|
+
errorMessage: "Attached Chrome connection generation changed",
|
|
391
|
+
errorRetryable: false,
|
|
392
|
+
errorDetails: { reason: "connection_generation_changed" },
|
|
393
|
+
settledAt: now,
|
|
394
|
+
updatedAt: now,
|
|
395
|
+
})
|
|
396
|
+
.where(
|
|
397
|
+
and(
|
|
398
|
+
eq(schema.interactionOperations.workspaceId, input.workspaceId),
|
|
399
|
+
inArray(schema.interactionOperations.operationId, operationIds),
|
|
400
|
+
inArray(schema.interactionOperations.state, ["prepared", "dispatched"]),
|
|
401
|
+
),
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
if (browserIds.length > 0) {
|
|
405
|
+
await tx
|
|
406
|
+
.update(schema.browserSessions)
|
|
407
|
+
.set({
|
|
408
|
+
lifecycle: "lost",
|
|
409
|
+
failureCode: CONTROLLER_TRANSITION_EXPIRED,
|
|
410
|
+
updatedAt: now,
|
|
411
|
+
})
|
|
412
|
+
.where(
|
|
413
|
+
and(
|
|
414
|
+
eq(schema.browserSessions.workspaceId, input.workspaceId),
|
|
415
|
+
inArray(schema.browserSessions.id, browserIds),
|
|
416
|
+
inArray(schema.browserSessions.lifecycle, [...ATTACHED_DEVICE_LIVE_LIFECYCLES]),
|
|
417
|
+
),
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
if (computerIds.length > 0) {
|
|
421
|
+
await tx
|
|
422
|
+
.update(schema.computerSessions)
|
|
423
|
+
.set({
|
|
424
|
+
lifecycle: "lost",
|
|
425
|
+
failureCode: CONTROLLER_TRANSITION_EXPIRED,
|
|
426
|
+
updatedAt: now,
|
|
427
|
+
})
|
|
428
|
+
.where(
|
|
429
|
+
and(
|
|
430
|
+
eq(schema.computerSessions.workspaceId, input.workspaceId),
|
|
431
|
+
inArray(schema.computerSessions.id, computerIds),
|
|
432
|
+
inArray(schema.computerSessions.lifecycle, [...ATTACHED_DEVICE_LIVE_LIFECYCLES]),
|
|
433
|
+
),
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/** Lock the device row first (same order as inventory) and prove the live
|
|
440
|
+
* physical generation still matches this controller placement. */
|
|
441
|
+
export async function attachedDeviceGenerationMatches(
|
|
442
|
+
tx: Database,
|
|
443
|
+
input: { workspaceId: string; deviceId: string; placementInstanceId: string },
|
|
444
|
+
): Promise<boolean> {
|
|
445
|
+
const [device] = await tx
|
|
446
|
+
.select({
|
|
447
|
+
connectionGeneration: schema.attachedBrowserDevices.connectionGeneration,
|
|
448
|
+
})
|
|
449
|
+
.from(schema.attachedBrowserDevices)
|
|
450
|
+
.where(
|
|
451
|
+
and(
|
|
452
|
+
eq(schema.attachedBrowserDevices.workspaceId, input.workspaceId),
|
|
453
|
+
eq(schema.attachedBrowserDevices.id, input.deviceId),
|
|
454
|
+
),
|
|
455
|
+
)
|
|
456
|
+
.for("update")
|
|
457
|
+
.limit(1);
|
|
458
|
+
return device?.connectionGeneration === input.placementInstanceId;
|
|
459
|
+
}
|
|
460
|
+
|
|
278
461
|
/** Immediately mark every live Chrome profile on a cleanly disconnected agent
|
|
279
462
|
* offline. Historical endpoint rows remain discoverable and reconnect in place. */
|
|
280
463
|
export async function disconnectAttachedBrowserDevices(
|
|
@@ -336,8 +519,53 @@ export async function listAttachedBrowserDevices(
|
|
|
336
519
|
asc(schema.attachedBrowserDevices.name),
|
|
337
520
|
asc(schema.attachedBrowserDevices.id),
|
|
338
521
|
);
|
|
522
|
+
const inventories = await scopedDb
|
|
523
|
+
.select({
|
|
524
|
+
enrollmentId: schema.attachedBrowserInventories.enrollmentId,
|
|
525
|
+
bridgeGeneration: schema.attachedBrowserInventories.bridgeGeneration,
|
|
526
|
+
revision: schema.attachedBrowserInventories.revision,
|
|
527
|
+
observedAt: schema.attachedBrowserInventories.observedAt,
|
|
528
|
+
enrollmentStatus: schema.enrollments.status,
|
|
529
|
+
connectionLeaseExpiresAt: schema.enrollments.connectionLeaseExpiresAt,
|
|
530
|
+
wentOfflineAt: schema.enrollments.wentOfflineAt,
|
|
531
|
+
})
|
|
532
|
+
.from(schema.attachedBrowserInventories)
|
|
533
|
+
.innerJoin(
|
|
534
|
+
schema.enrollments,
|
|
535
|
+
and(
|
|
536
|
+
eq(schema.enrollments.id, schema.attachedBrowserInventories.enrollmentId),
|
|
537
|
+
eq(schema.enrollments.workspaceId, schema.attachedBrowserInventories.workspaceId),
|
|
538
|
+
),
|
|
539
|
+
)
|
|
540
|
+
.where(eq(schema.attachedBrowserInventories.workspaceId, input.workspaceId))
|
|
541
|
+
.orderBy(
|
|
542
|
+
desc(schema.attachedBrowserInventories.observedAt),
|
|
543
|
+
asc(schema.attachedBrowserInventories.enrollmentId),
|
|
544
|
+
);
|
|
545
|
+
const connectedProfilesByEnrollment = new Map<string, number>();
|
|
546
|
+
for (const row of rows) {
|
|
547
|
+
if (row.disconnectedAt !== null) continue;
|
|
548
|
+
connectedProfilesByEnrollment.set(
|
|
549
|
+
row.enrollmentId,
|
|
550
|
+
(connectedProfilesByEnrollment.get(row.enrollmentId) ?? 0) + 1,
|
|
551
|
+
);
|
|
552
|
+
}
|
|
339
553
|
return AttachedBrowserDeviceListResponse.parse({
|
|
340
554
|
revision: await readWorkspaceInteractionRevision(scopedDb, input.workspaceId),
|
|
555
|
+
bridges: inventories.map((inventory) => ({
|
|
556
|
+
enrollmentId: inventory.enrollmentId,
|
|
557
|
+
state:
|
|
558
|
+
inventory.enrollmentStatus === "active" &&
|
|
559
|
+
inventory.wentOfflineAt === null &&
|
|
560
|
+
inventory.connectionLeaseExpiresAt !== null &&
|
|
561
|
+
inventory.connectionLeaseExpiresAt.getTime() > Date.now()
|
|
562
|
+
? "online"
|
|
563
|
+
: "offline",
|
|
564
|
+
bridgeGeneration: inventory.bridgeGeneration,
|
|
565
|
+
inventoryRevision: inventory.revision,
|
|
566
|
+
connectedProfileCount: connectedProfilesByEnrollment.get(inventory.enrollmentId) ?? 0,
|
|
567
|
+
lastSeenAt: inventory.observedAt.toISOString(),
|
|
568
|
+
})),
|
|
341
569
|
devices: rows.map(deviceFromRow),
|
|
342
570
|
});
|
|
343
571
|
},
|