@opengeni/db 1.0.1 → 3.2.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-4SR4HKKA.js → chunk-2EH4WRHL.js} +19 -11
- package/dist/chunk-2EH4WRHL.js.map +1 -0
- package/dist/{chunk-CUPM3FGB.js → chunk-2PRS4J5Q.js} +3 -3
- package/dist/chunk-6HMM2R5T.js +390 -0
- package/dist/chunk-6HMM2R5T.js.map +1 -0
- package/dist/{chunk-ZWREYEWA.js → chunk-AZHW7R5V.js} +56 -11
- package/dist/chunk-AZHW7R5V.js.map +1 -0
- package/dist/{chunk-LTG3S6ZY.js → chunk-E6CLMKEE.js} +2 -2
- package/dist/{chunk-6ZYKCRFO.js → chunk-KRLPEMLH.js} +7591 -4417
- package/dist/chunk-KRLPEMLH.js.map +1 -0
- package/dist/chunk-NAB6FKFN.js +516 -0
- package/dist/chunk-NAB6FKFN.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-UZNXG44K.js +4508 -0
- package/dist/chunk-UZNXG44K.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 +85 -0
- package/dist/connection-token-resolver.d.ts +30 -1
- package/dist/database.d.ts +23 -4
- 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 +1606 -81
- package/dist/index.js +21898 -8147
- 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 +29 -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 +43 -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 +268 -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 +28 -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/drizzle/0342_slack_route_prompt_single_pending.sql +32 -0
- package/drizzle/0343_personal_document_force_rls_lock_repair.sql +146 -0
- package/drizzle/0344_private_session_visibility_transition_gate.sql +85 -0
- package/drizzle/0345_tenant_scoped_session_tenancy_fence.sql +2839 -0
- package/drizzle/0346_document_migration_audit_surface.sql +454 -0
- package/drizzle/0347_connection_authority_convergence_evidence.sql +396 -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 +167 -0
- package/src/connection-token-resolver.ts +263 -31
- package/src/database.ts +87 -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 +11676 -1550
- 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 +995 -0
- package/src/pr-review.ts +890 -0
- package/src/preference-registry.ts +31 -1
- package/src/provision-roles.ts +905 -6
- package/src/runtime-posture-cli.ts +2 -0
- package/src/runtime-posture.ts +1317 -72
- package/src/schema.ts +3316 -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 +441 -85
- package/src/session-realtime-context.ts +4 -6
- package/src/session-realtime-ledger.ts +28 -14
- package/src/session-realtime-mirror.ts +1 -1
- package/src/session-tenancy.ts +410 -49
- package/src/session-tool-call-settlement.ts +38 -29
- package/src/slack-routing-personal-workspace.ts +67 -0
- package/src/slack-routing.ts +867 -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 +138 -11
- 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-CUPM3FGB.js.map → chunk-2PRS4J5Q.js.map} +0 -0
- /package/dist/{chunk-LTG3S6ZY.js.map → chunk-E6CLMKEE.js.map} +0 -0
|
@@ -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
|
},
|