@codemem/server 0.47.0-beta.2 → 0.47.0-beta.4
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/account/context.d.ts +2 -0
- package/dist/account/context.d.ts.map +1 -1
- package/dist/account/link-registry.d.ts +5 -0
- package/dist/account/link-registry.d.ts.map +1 -1
- package/dist/account/owner-recovery-context.d.ts +20 -0
- package/dist/account/owner-recovery-context.d.ts.map +1 -0
- package/dist/account/owner-recovery-evidence.d.ts +24 -0
- package/dist/account/owner-recovery-evidence.d.ts.map +1 -0
- package/dist/account/owner-recovery-service.d.ts +38 -0
- package/dist/account/owner-recovery-service.d.ts.map +1 -0
- package/dist/account/owner-recovery-types.d.ts +65 -0
- package/dist/account/owner-recovery-types.d.ts.map +1 -0
- package/dist/account/owner-status.d.ts +8 -0
- package/dist/account/owner-status.d.ts.map +1 -0
- package/dist/account/status.d.ts +17 -2
- package/dist/account/status.d.ts.map +1 -1
- package/dist/index.d.ts +9 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2890 -259
- package/dist/index.js.map +1 -1
- package/dist/routes/account-owner-recovery.d.ts +7 -0
- package/dist/routes/account-owner-recovery.d.ts.map +1 -0
- package/dist/routes/device-rename.d.ts +7 -7
- package/dist/routes/device-rename.d.ts.map +1 -1
- package/dist/routes/file-context.d.ts +4 -0
- package/dist/routes/file-context.d.ts.map +1 -0
- package/dist/routes/memory-tools.d.ts +1 -2
- package/dist/routes/memory-tools.d.ts.map +1 -1
- package/dist/routes/memory.d.ts.map +1 -1
- package/dist/routes/pack.d.ts.map +1 -1
- package/package.json +3 -3
- package/static/app.js +43 -43
- package/static/app.js.br +0 -0
- package/static/app.js.gz +0 -0
- package/static/index.html +24 -2
package/dist/index.js
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
2
|
-
import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
3
|
-
import { ACCESS_CLEANUP_OP_TYPE, CodememConfigMutationError, CoordinatorAccountLinkError, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MEMORY_FILTER_FIELD_TYPES, MEMORY_FILTER_NAMES, MEMORY_KIND_DESCRIPTIONS, MemoryStore, ObserverClient, PROMPT_TRANSPORT_PROTOCOL_RANGE, REMEMBER_MEMORY_KINDS, RICH_TIER_ANTHROPIC_DEFAULTS, RICH_TIER_DEFAULTS, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SIMPLE_TIER_ANTHROPIC_DEFAULTS, SIMPLE_TIER_DEFAULTS, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, analyzeProjectScopeMappingChangesGuardrails, analyzeProjectScopeMappingDeletionGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterClausesWithContext, buildPiFlushSignalFromEvent, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalRepositoryProjectIdentity, canonicalWorkspaceIdentity, changeCoordinatorAccount, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, classifyRecordedSyncFailure, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, dedupeOrderedIds, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, generateWithOpenCodeV2, getCodememConfigPath, getCodememEnvOverrideValues, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getEffectiveCachedScopeAuthorization, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, linkCoordinatorAccount, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listOpenCodeV2Models, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadPublicKey, loadReplicationOpsForPeer, loadRuntimeSigningPublicKey, lookupCoordinatorPeers, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, mutateCodememConfigFile, negotiateSyncCapability, normalizeAddress, normalizeCoordinatorAccountLinkOrigin, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parseBootstrapExpiry, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readAccountLinkDevice, readCodememConfigFile, readCoordinatorAccountChangeCapabilities, readCoordinatorAccountLinkStatus, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, repositoryIdentitiesByWorkspace, repositoryIdentityForWorkspace, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveCodememConfigPath, resolveDbPath, resolveObserverDefaultModel, resolveObserverRuntime, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, resolveVisibleScopeIds, runSyncPass, schema, scopeAuthorizationFailureReason, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMappings, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, validateRawEvents, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, wakeRecipientPoliciesForIdentities, wakeRecipientPoliciesForProjectIdentities } from "@codemem/core";
|
|
1
|
+
import { closeSync, existsSync, fstatSync, fsyncSync, linkSync, lstatSync, mkdirSync, openSync, readFileSync, statSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
3
|
+
import { ACCESS_CLEANUP_OP_TYPE, CodememConfigMutationError, CoordinatorAccountLinkError, CoordinatorReciprocalApprovalRequestChangedError, DEFAULT_TIME_WINDOW_S, FILE_CONTEXT_TRANSPORT_MAX_BYTES, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, LOCAL_SYNC_FEATURES, LegacyTeamSetupAdditiveConvergenceError, LegacyTeamSetupRosterCapacityError, MEMORY_FILTER_FIELD_TYPES, MEMORY_FILTER_NAMES, MEMORY_KIND_DESCRIPTIONS, MemoryStore, ObserverClient, PROMPT_TRANSPORT_PROTOCOL_RANGE, REMEMBER_MEMORY_KINDS, RICH_TIER_ANTHROPIC_DEFAULTS, RICH_TIER_DEFAULTS, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, SIMPLE_TIER_ANTHROPIC_DEFAULTS, SIMPLE_TIER_DEFAULTS, SYNC_AUTHORIZATION_REFRESH_HEADER, SYNC_CAPABILITY_HEADER, SYNC_FEATURES_HEADER, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, VIEWER_IDENTITY_TARGET_KEYS, VIEWER_SERVICE_DISCRIMINATOR, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, analyzeProjectScopeMappingChangesGuardrails, analyzeProjectScopeMappingDeletionGuardrails, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterBranchesWithContext, buildFilterClausesWithContext, buildPiFlushSignalFromEvent, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalRepositoryProjectIdentity, canonicalWorkspaceIdentity, captureAccountOwnerProfilePins, changeCoordinatorAccount, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, classifyRecordedSyncFailure, cleanupNonces, clearLegacyTeamSetupDeviceDecision, clonePromptPackAttempt, coerceObserverCommand, commitDeviceIdentityBindings, commitRecipientPolicyEdges, containLegacyTeamSetupCompletionConflict, coordinatorArchiveGroupAction, coordinatorAuthControllerReviewAction, coordinatorCreateAddDeviceInviteAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateLegacyTeamCompletionAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGetLegacyTeamCompletionAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListConsumedTeamInvitesAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListLegacyTeamCompletionsAction, coordinatorListReviewedRecipientInviteEvidenceAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, countShareableProjectMemories, createCoordinatorReciprocalApproval, decodeInvitePayload, dedupeOrderedIds, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, deriveLegacyTeamSetupCompletionManifest, detectInstallKind, deterministicPolicyTeamId, diagnoseStalePeerReceivedRows, discoverLegacyTeamCandidates, dispatchFileContextTransport, ensureDeviceIdentity, executeShareProvisioning, extractInvitePayload, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, formatHostPort, friendlyDeviceName, fromJson, generateWithOpenCodeV2, getCodememConfigPath, getCodememEnvOverrideValues, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getEffectiveCachedScopeAuthorization, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, linkCoordinatorAccount, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listOpenCodeV2Models, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadPublicKey, loadReplicationOpsForPeer, loadRuntimeSigningPublicKey, lookupCoordinatorPeers, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, mutateCodememConfigFile, negotiateSyncCapability, normalizeAddress, normalizeCoordinatorAccountLinkOrigin, normalizeCoordinatorLegacyTeamCompletionManifest, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parseBootstrapExpiry, parseFileContextTransportRequest, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readAccountLinkDevice, readAccountOwnerAssociation, readCodememConfigFile, readCoordinatorAccountChangeCapabilities, readCoordinatorAccountLinkStatus, readCoordinatorResolvedAccountOwnerReceiptStatus, readCoordinatorResolvedAccountOwnerStatus, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, repositoryIdentitiesByWorkspace, repositoryIdentityForWorkspace, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveCodememConfigPath, resolveDbPath, resolveObserverDefaultModel, resolveObserverRuntime, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, resolveVisibleScopeIds, runSyncPass, schema, scopeAuthorizationFailureReason, searchPiSessions, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMappings, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, validateRawEvents, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature, wakeRecipientPoliciesForIdentities, wakeRecipientPoliciesForProjectIdentities } from "@codemem/core";
|
|
4
4
|
import { serveStatic } from "@hono/node-server/serve-static";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
6
|
import { createCipheriv, createDecipheriv, createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
7
7
|
import { createMiddleware } from "hono/factory";
|
|
8
8
|
import { RateLimiter } from "limiter";
|
|
9
|
-
import { and, count, desc, eq, inArray, max, ne } from "drizzle-orm";
|
|
9
|
+
import { and, count, desc, eq, inArray, max, ne, sql } from "drizzle-orm";
|
|
10
10
|
import { drizzle } from "drizzle-orm/better-sqlite3";
|
|
11
11
|
import { homedir, hostname, networkInterfaces } from "node:os";
|
|
12
12
|
import * as z from "zod";
|
|
13
13
|
import { chmod, link, mkdir, open, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
14
|
+
import "better-sqlite3";
|
|
15
|
+
import "sqlite-vec";
|
|
16
|
+
import { index, integer, primaryKey, real, sqliteTable, text, uniqueIndex } from "drizzle-orm/sqlite-core";
|
|
14
17
|
//#region src/routes/account.ts
|
|
15
18
|
/** Mounted only alongside the account request middleware. */
|
|
16
19
|
function accountRoutes(getSession) {
|
|
@@ -329,6 +332,7 @@ var InProcessAccountLinkRegistry = class {
|
|
|
329
332
|
#shuttingDown = false;
|
|
330
333
|
#closing = null;
|
|
331
334
|
#uncertainOwner = null;
|
|
335
|
+
#recoveryOwner = null;
|
|
332
336
|
constructor(options) {
|
|
333
337
|
this.#options = options;
|
|
334
338
|
this.#now = options.now ?? Date.now;
|
|
@@ -484,6 +488,22 @@ var InProcessAccountLinkRegistry = class {
|
|
|
484
488
|
busySlot.lifecycle ??= Symbol();
|
|
485
489
|
return busySlot.lifecycle;
|
|
486
490
|
}
|
|
491
|
+
tryReserveRecovery() {
|
|
492
|
+
if (this.#shuttingDown || this.#inProgress()) return null;
|
|
493
|
+
if (this.#shuttingDown || busySlot.owner) return null;
|
|
494
|
+
const owner = Symbol();
|
|
495
|
+
busySlot.owner = owner;
|
|
496
|
+
busySlot.lifecycle = Symbol();
|
|
497
|
+
this.#recoveryOwner = owner;
|
|
498
|
+
return {
|
|
499
|
+
isCurrent: () => !this.#shuttingDown && this.#recoveryOwner === owner && busySlot.owner === owner,
|
|
500
|
+
release: () => {
|
|
501
|
+
if (this.#recoveryOwner !== owner) return;
|
|
502
|
+
this.#recoveryOwner = null;
|
|
503
|
+
releaseBusyOwner(owner);
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
}
|
|
487
507
|
get(operationId) {
|
|
488
508
|
this.#cleanup();
|
|
489
509
|
const job = this.#jobs.get(operationId);
|
|
@@ -529,6 +549,10 @@ var InProcessAccountLinkRegistry = class {
|
|
|
529
549
|
close() {
|
|
530
550
|
if (this.#closing) return this.#closing;
|
|
531
551
|
this.#shuttingDown = true;
|
|
552
|
+
if (this.#recoveryOwner) {
|
|
553
|
+
releaseBusyOwner(this.#recoveryOwner);
|
|
554
|
+
this.#recoveryOwner = null;
|
|
555
|
+
}
|
|
532
556
|
for (const job of this.#jobs.values()) {
|
|
533
557
|
this.#clearUrl(job);
|
|
534
558
|
if (!terminal(job)) {
|
|
@@ -554,17 +578,129 @@ function createAccountLinkRegistry(options) {
|
|
|
554
578
|
return new InProcessAccountLinkRegistry(options);
|
|
555
579
|
}
|
|
556
580
|
//#endregion
|
|
581
|
+
//#region src/account/owner-status.ts
|
|
582
|
+
/** The coordinator ID comes from the first signed actor-status read, never the origin. */
|
|
583
|
+
function readOwnerAssociation(context, coordinatorId) {
|
|
584
|
+
if (context.publicKeyHash === void 0) return null;
|
|
585
|
+
const before = statSync(context.dbPath, { bigint: true });
|
|
586
|
+
const device = readAccountLinkDevice(context.dbPath);
|
|
587
|
+
const profile = {
|
|
588
|
+
...context,
|
|
589
|
+
publicKey: device.publicKey,
|
|
590
|
+
actorId: context.identityId,
|
|
591
|
+
coordinatorId
|
|
592
|
+
};
|
|
593
|
+
const expectedProfilePins = captureAccountOwnerProfilePins(profile);
|
|
594
|
+
const association = readAccountOwnerAssociation(profile, { expectedProfilePins }) ?? readAccountOwnerAssociation(profile);
|
|
595
|
+
const after = statSync(context.dbPath, { bigint: true });
|
|
596
|
+
if (!before.isFile() || before.dev !== after.dev || before.ino !== after.ino || after.dev.toString() !== context.fileIdentity.dev || after.ino.toString() !== context.fileIdentity.ino || device.deviceId !== context.deviceId || createHash("sha256").update(device.publicKey).digest("hex") !== context.publicKeyHash || device.fingerprint !== context.fingerprint) throw new Error("account_owner_association_invalid");
|
|
597
|
+
return association;
|
|
598
|
+
}
|
|
599
|
+
function ownerUnavailable(view, code) {
|
|
600
|
+
return {
|
|
601
|
+
...withoutOwnerCapabilities(view),
|
|
602
|
+
remote: {
|
|
603
|
+
kind: "unavailable",
|
|
604
|
+
code
|
|
605
|
+
},
|
|
606
|
+
canLink: false,
|
|
607
|
+
blockedReason: code === "context_changed" ? "status_unavailable" : "review_required"
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function withoutOwnerCapabilities(view) {
|
|
611
|
+
const { managementActions, unlinkTarget, accountOwner, ownerProjection, ...local } = view;
|
|
612
|
+
return local;
|
|
613
|
+
}
|
|
614
|
+
function ownerProfile(value) {
|
|
615
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
616
|
+
const source = value;
|
|
617
|
+
const profile = {};
|
|
618
|
+
for (const [field, limit] of [
|
|
619
|
+
["displayName", 256],
|
|
620
|
+
["email", 320],
|
|
621
|
+
["pictureUrl", 2048]
|
|
622
|
+
]) {
|
|
623
|
+
const text = source[field];
|
|
624
|
+
if (text === void 0) continue;
|
|
625
|
+
if (typeof text !== "string" || !text.trim() || text.length > limit || /[\p{Cc}\p{Cf}\p{Cs}]/u.test(text)) return null;
|
|
626
|
+
if (field === "pictureUrl" && !safeOwnerPicture(text)) return null;
|
|
627
|
+
profile[field] = text;
|
|
628
|
+
}
|
|
629
|
+
return profile;
|
|
630
|
+
}
|
|
631
|
+
function safeOwnerPicture(text) {
|
|
632
|
+
try {
|
|
633
|
+
const url = new URL(text);
|
|
634
|
+
return url.protocol === "https:" && url.hostname === "lh3.googleusercontent.com" && !url.username && !url.password && !url.port;
|
|
635
|
+
} catch {
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
function associatedOwnerView(view, context, association, remote) {
|
|
640
|
+
const contextId = createHash("sha256").update(JSON.stringify({
|
|
641
|
+
version: "account-owner-view-v1",
|
|
642
|
+
context,
|
|
643
|
+
association
|
|
644
|
+
})).digest("hex");
|
|
645
|
+
const base = {
|
|
646
|
+
...withoutOwnerCapabilities(view),
|
|
647
|
+
contextId
|
|
648
|
+
};
|
|
649
|
+
if (remote?.kind !== "owner_status" || !remote.status || remote.status.version !== 1 || remote.status.coordinatorId !== association.coordinatorId || remote.status.groupId !== context.groupId || remote.status.deviceId !== context.deviceId || remote.status.fingerprint !== context.fingerprint || remote.status.ownerIdentity !== association.ownerIdentityId || remote.status.state !== "owner_confirmed") return ownerUnavailable(base, "account_owner_association_invalid");
|
|
650
|
+
const status = remote.status;
|
|
651
|
+
const profile = status.profileState === "linked" ? ownerProfile(status.profile) : null;
|
|
652
|
+
if (status.profileState === "linked" && profile === null || status.profileState !== "linked" && (status.profileState !== "account_unlinked" && status.profileState !== "unavailable" || status.profile !== null)) return ownerUnavailable(base, "account_owner_association_invalid");
|
|
653
|
+
const subject = {
|
|
654
|
+
version: 1,
|
|
655
|
+
coordinatorId: status.coordinatorId,
|
|
656
|
+
groupId: status.groupId,
|
|
657
|
+
deviceId: status.deviceId,
|
|
658
|
+
fingerprint: status.fingerprint,
|
|
659
|
+
state: "owner_confirmed",
|
|
660
|
+
ownerIdentity: status.ownerIdentity
|
|
661
|
+
};
|
|
662
|
+
let confirmed;
|
|
663
|
+
if (status.profileState === "linked" && profile) confirmed = {
|
|
664
|
+
...subject,
|
|
665
|
+
profileState: "linked",
|
|
666
|
+
profile
|
|
667
|
+
};
|
|
668
|
+
else if (status.profileState === "account_unlinked" || status.profileState === "unavailable") confirmed = {
|
|
669
|
+
...subject,
|
|
670
|
+
profileState: status.profileState,
|
|
671
|
+
profile: null
|
|
672
|
+
};
|
|
673
|
+
else return ownerUnavailable(base, "account_owner_association_invalid");
|
|
674
|
+
return {
|
|
675
|
+
...base,
|
|
676
|
+
accountOwner: {
|
|
677
|
+
identityId: association.ownerIdentityId,
|
|
678
|
+
memoryActorId: context.identityId
|
|
679
|
+
},
|
|
680
|
+
remote: { kind: "not_checked" },
|
|
681
|
+
ownerProjection: {
|
|
682
|
+
kind: "resolved_account_owner",
|
|
683
|
+
status: confirmed
|
|
684
|
+
},
|
|
685
|
+
canLink: false,
|
|
686
|
+
blockedReason: profile ? "already_linked" : "status_unavailable"
|
|
687
|
+
};
|
|
688
|
+
}
|
|
689
|
+
//#endregion
|
|
557
690
|
//#region src/account/status.ts
|
|
558
|
-
function attachOperation(view, registry) {
|
|
559
|
-
const operation =
|
|
691
|
+
function attachOperation(view, registry, operationContextId = view.contextId) {
|
|
692
|
+
const operation = operationContextId ? registry?.forContext(operationContextId) ?? null : null;
|
|
693
|
+
const operationContext = operation && operationContextId && operationContextId !== view.contextId ? { operationContextId } : {};
|
|
560
694
|
if (registry?.hasActive()) return {
|
|
561
695
|
...view,
|
|
696
|
+
...operationContext,
|
|
562
697
|
operation,
|
|
563
698
|
canLink: false,
|
|
564
699
|
blockedReason: "operation_active"
|
|
565
700
|
};
|
|
566
701
|
return {
|
|
567
702
|
...view,
|
|
703
|
+
...operationContext,
|
|
568
704
|
operation
|
|
569
705
|
};
|
|
570
706
|
}
|
|
@@ -668,10 +804,57 @@ async function readAccountStatus(options, groupId, signal, readOptions = {}) {
|
|
|
668
804
|
},
|
|
669
805
|
blockedReason: "status_unavailable"
|
|
670
806
|
}, options.registry);
|
|
807
|
+
return finishAccountStatus(options, initial.context, fresh, remote, signal);
|
|
808
|
+
}
|
|
809
|
+
async function finishAccountStatus(options, context, fresh, remote, signal) {
|
|
671
810
|
const currentView = applyRemote(accountLocalView(fresh), remote);
|
|
672
|
-
|
|
811
|
+
if (remote.kind === "status") {
|
|
812
|
+
const associated = await readAssociatedOwner(options, context, currentView, remote, signal);
|
|
813
|
+
if (associated) return attachOperation(associated, options.registry, context.contextId);
|
|
814
|
+
}
|
|
815
|
+
await attachManagementActions(options, context, currentView, signal);
|
|
816
|
+
if (remote.kind === "status") try {
|
|
817
|
+
if (readOwnerAssociation(context, remote.status.coordinatorId)) return attachOperation(ownerUnavailable(accountLocalView(fresh), "context_changed"), options.registry);
|
|
818
|
+
} catch {
|
|
819
|
+
return attachOperation(ownerUnavailable(accountLocalView(fresh), "account_owner_association_invalid"), options.registry);
|
|
820
|
+
}
|
|
673
821
|
return attachOperation(currentView, options.registry);
|
|
674
822
|
}
|
|
823
|
+
async function readAssociatedOwner(options, context, view, actorRemote, signal) {
|
|
824
|
+
const local = {
|
|
825
|
+
...view,
|
|
826
|
+
remote: { kind: "not_checked" },
|
|
827
|
+
canLink: false
|
|
828
|
+
};
|
|
829
|
+
try {
|
|
830
|
+
const association = readOwnerAssociation(context, actorRemote.status.coordinatorId);
|
|
831
|
+
if (!association) return null;
|
|
832
|
+
const before = options.readContext(context.groupId);
|
|
833
|
+
if ("error" in before || !before.context || !sameAuthority(context, before.context)) return ownerUnavailable(local, "context_changed");
|
|
834
|
+
const { dbPath, keysDir, groupId, coordinatorUrl } = context;
|
|
835
|
+
let remote;
|
|
836
|
+
try {
|
|
837
|
+
remote = await (options.readRemoteOwnerStatus ?? readCoordinatorResolvedAccountOwnerStatus)({
|
|
838
|
+
dbPath,
|
|
839
|
+
keysDir,
|
|
840
|
+
groupId,
|
|
841
|
+
coordinatorUrl,
|
|
842
|
+
ownerIdentityId: association.ownerIdentityId,
|
|
843
|
+
signal
|
|
844
|
+
});
|
|
845
|
+
} catch {
|
|
846
|
+
remote = {
|
|
847
|
+
kind: "unavailable",
|
|
848
|
+
code: "coordinator_unreachable"
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
const after = options.readContext(context.groupId);
|
|
852
|
+
if (signal.aborted || JSON.stringify(before) !== JSON.stringify(after) || JSON.stringify(association) !== JSON.stringify(readOwnerAssociation(context, association.coordinatorId))) return ownerUnavailable(local, "context_changed");
|
|
853
|
+
return associatedOwnerView(local, context, association, remote);
|
|
854
|
+
} catch {
|
|
855
|
+
return ownerUnavailable(local, "account_owner_association_invalid");
|
|
856
|
+
}
|
|
857
|
+
}
|
|
675
858
|
async function attachManagementActions(options, context, view, signal) {
|
|
676
859
|
if (view.remote.kind !== "status" || view.remote.state !== "linked" || !view.remote.identityMatches) return;
|
|
677
860
|
const read = options.readRemoteCapabilities ?? (options.readRemoteStatus ? null : readCoordinatorAccountChangeCapabilities);
|
|
@@ -711,7 +894,7 @@ async function attachManagementActions(options, context, view, signal) {
|
|
|
711
894
|
}
|
|
712
895
|
//#endregion
|
|
713
896
|
//#region src/routes/account-operations.ts
|
|
714
|
-
var ROOT = "/api/account/link-operations";
|
|
897
|
+
var ROOT$1 = "/api/account/link-operations";
|
|
715
898
|
function validString(value) {
|
|
716
899
|
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !/[\p{Cc}\p{Cf}\p{Cs}]/u.test(value);
|
|
717
900
|
}
|
|
@@ -720,7 +903,7 @@ function noBody(request) {
|
|
|
720
903
|
return request.body === null && (length === null || length === "0") && !request.headers.has("Transfer-Encoding");
|
|
721
904
|
}
|
|
722
905
|
/** Cap both bytes and elapsed time; cancellation must not wait on an uncooperative stream. */
|
|
723
|
-
async function readBody(request, keys, optional = []) {
|
|
906
|
+
async function readBody$1(request, keys, optional = []) {
|
|
724
907
|
if (!/^application\/json(?:\s*;\s*charset\s*=\s*(?:utf-8|"utf-8"))?\s*$/i.test(request.headers.get("Content-Type") ?? "")) return null;
|
|
725
908
|
const reader = request.body?.getReader();
|
|
726
909
|
if (!reader) return null;
|
|
@@ -767,7 +950,7 @@ function unlinkTargetChanged(body, view) {
|
|
|
767
950
|
return body.intent === "disconnect" && body.expectedLinkId !== view.unlinkTarget;
|
|
768
951
|
}
|
|
769
952
|
async function startOperation(c, options) {
|
|
770
|
-
const body = await readBody(c.req.raw, [
|
|
953
|
+
const body = await readBody$1(c.req.raw, [
|
|
771
954
|
"groupId",
|
|
772
955
|
"contextId",
|
|
773
956
|
"authority",
|
|
@@ -823,7 +1006,7 @@ function beginCurrentOperation(c, options, body, view, expectedCoordinatorId, ch
|
|
|
823
1006
|
}
|
|
824
1007
|
async function performOperation(c, options) {
|
|
825
1008
|
const { id, action } = c.req.param();
|
|
826
|
-
if (!validString(id) || action !== "browser-url" && action !== "cancel" || !await readBody(c.req.raw, [])) return c.json({ error: "account_invalid_request" }, 400);
|
|
1009
|
+
if (!validString(id) || action !== "browser-url" && action !== "cancel" || !await readBody$1(c.req.raw, [])) return c.json({ error: "account_invalid_request" }, 400);
|
|
827
1010
|
const operation = options.registry.get(id);
|
|
828
1011
|
if (!operation) return c.json({ error: "account_operation_not_found" }, 404);
|
|
829
1012
|
if (action === "cancel") {
|
|
@@ -849,10 +1032,10 @@ function accountOperationRoutes(options) {
|
|
|
849
1032
|
if ([...new URL(c.req.url).searchParams].length) return c.json({ error: "account_invalid_request" }, 400);
|
|
850
1033
|
await next();
|
|
851
1034
|
};
|
|
852
|
-
app.use(ROOT, rejectQuery);
|
|
853
|
-
app.use(`${ROOT}/*`, rejectQuery);
|
|
854
|
-
app.post(ROOT, (c) => startOperation(c, options));
|
|
855
|
-
app.get(`${ROOT}/:id`, (c) => {
|
|
1035
|
+
app.use(ROOT$1, rejectQuery);
|
|
1036
|
+
app.use(`${ROOT$1}/*`, rejectQuery);
|
|
1037
|
+
app.post(ROOT$1, (c) => startOperation(c, options));
|
|
1038
|
+
app.get(`${ROOT$1}/:id`, (c) => {
|
|
856
1039
|
if (!noBody(c.req.raw) || !validString(c.req.param("id"))) return c.json({ error: "account_invalid_request" }, 400);
|
|
857
1040
|
const operation = options.registry.get(c.req.param("id"));
|
|
858
1041
|
if (!operation) return c.json({ error: "account_operation_not_found" }, 404);
|
|
@@ -862,7 +1045,338 @@ function accountOperationRoutes(options) {
|
|
|
862
1045
|
current: isCurrent(options, operation)
|
|
863
1046
|
});
|
|
864
1047
|
});
|
|
865
|
-
app.post(`${ROOT}/:id/:action`, (c) => performOperation(c, options));
|
|
1048
|
+
app.post(`${ROOT$1}/:id/:action`, (c) => performOperation(c, options));
|
|
1049
|
+
return app;
|
|
1050
|
+
}
|
|
1051
|
+
//#endregion
|
|
1052
|
+
//#region src/account/owner-recovery-evidence.ts
|
|
1053
|
+
var RECOVERY_SCOPE = "historical_included_owner_labels_only";
|
|
1054
|
+
var RECOVERY_REASON = /^[a-z][a-z0-9_]{0,119}(?:: [\x20-\x7e]{1,240})?$/;
|
|
1055
|
+
var AccountOwnerRecoveryError = class extends Error {
|
|
1056
|
+
code;
|
|
1057
|
+
constructor(code = "invalid") {
|
|
1058
|
+
super("account_owner_recovery_invalid");
|
|
1059
|
+
this.code = code;
|
|
1060
|
+
}
|
|
1061
|
+
};
|
|
1062
|
+
function requireRecovery(condition, code = "invalid") {
|
|
1063
|
+
if (!condition) throw new AccountOwnerRecoveryError(code);
|
|
1064
|
+
}
|
|
1065
|
+
function recoveryId(value) {
|
|
1066
|
+
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !/[\p{Cc}\p{Cf}\p{Cs}]/u.test(value);
|
|
1067
|
+
}
|
|
1068
|
+
function recoveryHash(value) {
|
|
1069
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
1070
|
+
}
|
|
1071
|
+
function recoveryRecord(value) {
|
|
1072
|
+
requireRecovery(value && typeof value === "object" && !Array.isArray(value));
|
|
1073
|
+
return value;
|
|
1074
|
+
}
|
|
1075
|
+
function exactRecoveryFields(row, fields) {
|
|
1076
|
+
requireRecovery(Object.keys(row).length === fields.length && fields.every((key) => Object.hasOwn(row, key)));
|
|
1077
|
+
}
|
|
1078
|
+
function timestamp(value) {
|
|
1079
|
+
return typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
1080
|
+
}
|
|
1081
|
+
function readWeakLabel(value) {
|
|
1082
|
+
const row = recoveryRecord(value);
|
|
1083
|
+
exactRecoveryFields(row, [
|
|
1084
|
+
"label",
|
|
1085
|
+
"kind",
|
|
1086
|
+
"group_id",
|
|
1087
|
+
"device_id",
|
|
1088
|
+
"evidence_ref",
|
|
1089
|
+
"effect"
|
|
1090
|
+
]);
|
|
1091
|
+
requireRecovery(recoveryId(row.label) && recoveryId(row.group_id) && recoveryId(row.device_id) && recoveryId(row.evidence_ref));
|
|
1092
|
+
requireRecovery(row.kind === "legacy_included_owner_label" || row.kind === "consumed_project_actor_label");
|
|
1093
|
+
requireRecovery(row.effect === "account_owner_display_only");
|
|
1094
|
+
return {
|
|
1095
|
+
label: row.label,
|
|
1096
|
+
kind: row.kind,
|
|
1097
|
+
group_id: row.group_id,
|
|
1098
|
+
device_id: row.device_id,
|
|
1099
|
+
evidence_ref: row.evidence_ref,
|
|
1100
|
+
effect: row.effect
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
function historyRows(value, selection, labels) {
|
|
1104
|
+
const row = recoveryRecord(value);
|
|
1105
|
+
requireRecovery(recoveryId(row.group_id) && recoveryId(row.candidate_ref) && timestamp(row.created_at));
|
|
1106
|
+
const manifest = normalizeCoordinatorLegacyTeamCompletionManifest(row.manifest);
|
|
1107
|
+
requireRecovery(manifest.coordinator_id === selection.profile.coordinatorId && manifest.candidate_ref === row.candidate_ref && manifest.version === row.manifest_version && manifest.completed_at === row.completed_at && legacyTeamCandidateId(manifest.coordinator_id, row.group_id) === manifest.candidate_ref && canonicalCoordinatorLegacyTeamCompletionManifestJson(manifest) === row.manifest_json);
|
|
1108
|
+
const groupId = row.group_id;
|
|
1109
|
+
return manifest.device_decisions.map((decision) => {
|
|
1110
|
+
let relation = "other";
|
|
1111
|
+
if (decision.device_id === selection.profile.deviceId) relation = "exact_subject";
|
|
1112
|
+
else if (labels.some((label) => label.kind === "legacy_included_owner_label" && label.group_id === groupId && label.device_id === decision.device_id && label.label === decision.identity_id && label.evidence_ref === manifest.source_digest)) relation = "alias";
|
|
1113
|
+
return {
|
|
1114
|
+
id: `${manifest.candidate_ref}:${decision.device_id}`,
|
|
1115
|
+
groupId,
|
|
1116
|
+
deviceId: decision.device_id,
|
|
1117
|
+
fingerprint: decision.key_fingerprint,
|
|
1118
|
+
identityId: decision.identity_id ?? "",
|
|
1119
|
+
assignment: decision.decision,
|
|
1120
|
+
relation,
|
|
1121
|
+
reason: "Retained canonical Team decision; display metadata only."
|
|
1122
|
+
};
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
function receiptPart(value, idField) {
|
|
1126
|
+
const row = recoveryRecord(value);
|
|
1127
|
+
exactRecoveryFields(row, [
|
|
1128
|
+
"disposition",
|
|
1129
|
+
idField,
|
|
1130
|
+
"evidence_digest",
|
|
1131
|
+
"created_at"
|
|
1132
|
+
]);
|
|
1133
|
+
const id = row[idField];
|
|
1134
|
+
requireRecovery(recoveryId(id) && recoveryHash(row.evidence_digest) && timestamp(row.created_at) && (row.disposition === "created" || row.disposition === "reused"));
|
|
1135
|
+
return {
|
|
1136
|
+
id,
|
|
1137
|
+
digest: row.evidence_digest
|
|
1138
|
+
};
|
|
1139
|
+
}
|
|
1140
|
+
function reviewReceipt(row) {
|
|
1141
|
+
requireRecovery(recoveryId(row.attestation_id));
|
|
1142
|
+
const source = receiptPart(row.recovery_receipt, "review_receipt_id");
|
|
1143
|
+
const resolution = receiptPart(row.owner_resolution, "resolution_id");
|
|
1144
|
+
return {
|
|
1145
|
+
attestationId: row.attestation_id,
|
|
1146
|
+
reviewReceiptId: source.id,
|
|
1147
|
+
reviewEvidenceDigest: source.digest,
|
|
1148
|
+
resolutionId: resolution.id,
|
|
1149
|
+
resolutionEvidenceDigest: resolution.digest
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
function validateRecoveryReview(value, selection, groupId) {
|
|
1153
|
+
const row = recoveryRecord(value);
|
|
1154
|
+
const profile = selection.profile;
|
|
1155
|
+
requireRecovery(row.purpose === "existing_account_owner" && row.resolution_scope === "historical_included_owner_labels_only" && row.coordinator_id === profile.coordinatorId && row.group_id === groupId && row.device_id === profile.deviceId && row.fingerprint === profile.fingerprint && row.identity_id === selection.existingOwnerId);
|
|
1156
|
+
requireRecovery(Array.isArray(row.legacy_history) && row.legacy_history.length <= 500 && Array.isArray(row.reviewed_weak_labels) && row.reviewed_weak_labels.length <= 256 && Array.isArray(row.reasons) && row.reasons.every((reason) => typeof reason === "string"));
|
|
1157
|
+
const labels = row.reviewed_weak_labels.map(readWeakLabel);
|
|
1158
|
+
requireRecovery(new Set(labels.map((label) => JSON.stringify(label))).size === labels.length);
|
|
1159
|
+
const rows = row.legacy_history.flatMap((history) => historyRows(history, selection, labels));
|
|
1160
|
+
requireRecovery(row.reasons.length <= 8 && row.reasons.every((reason) => typeof reason === "string" && RECOVERY_REASON.test(reason)));
|
|
1161
|
+
const reasons = row.reasons;
|
|
1162
|
+
if (row.state === "needs_review") {
|
|
1163
|
+
requireRecovery(row.reasons.length > 0 && !Object.hasOwn(row, "owner_resolution_digest"));
|
|
1164
|
+
return {
|
|
1165
|
+
state: row.state,
|
|
1166
|
+
rows,
|
|
1167
|
+
labels,
|
|
1168
|
+
reasons
|
|
1169
|
+
};
|
|
1170
|
+
}
|
|
1171
|
+
requireRecovery((row.state === "ready" || row.state === "restored") && recoveryHash(row.owner_resolution_digest) && row.reasons.length === 0);
|
|
1172
|
+
const target = recoveryRecord(row.account_owner_target);
|
|
1173
|
+
requireRecovery(target.identity_id === selection.existingOwnerId && recoveryId(target.link_id) && recoveryId(target.auth_config_revision) && typeof target.created_at_ms === "number" && Number.isSafeInteger(target.created_at_ms));
|
|
1174
|
+
let receipt;
|
|
1175
|
+
if (row.state === "restored" || Object.hasOwn(row, "owner_resolution")) receipt = reviewReceipt(row);
|
|
1176
|
+
return {
|
|
1177
|
+
state: row.state,
|
|
1178
|
+
digest: row.owner_resolution_digest,
|
|
1179
|
+
receipt,
|
|
1180
|
+
rows,
|
|
1181
|
+
labels,
|
|
1182
|
+
reasons
|
|
1183
|
+
};
|
|
1184
|
+
}
|
|
1185
|
+
function replyGroups(row, selection) {
|
|
1186
|
+
exactRecoveryFields(row, [
|
|
1187
|
+
"version",
|
|
1188
|
+
"purpose",
|
|
1189
|
+
"scope",
|
|
1190
|
+
"coordinatorOrigin",
|
|
1191
|
+
"coordinatorId",
|
|
1192
|
+
"deviceId",
|
|
1193
|
+
"fingerprint",
|
|
1194
|
+
"ownerIdentityId",
|
|
1195
|
+
"groups"
|
|
1196
|
+
]);
|
|
1197
|
+
const p = selection.profile;
|
|
1198
|
+
requireRecovery(row.version === 1 && row.purpose === "existing_account_owner" && row.scope === "historical_included_owner_labels_only" && row.coordinatorOrigin === p.coordinatorOrigin && row.coordinatorId === p.coordinatorId && row.deviceId === p.deviceId && row.fingerprint === p.fingerprint && row.ownerIdentityId === selection.existingOwnerId && Array.isArray(row.groups) && row.groups.length === selection.groups.length);
|
|
1199
|
+
return row.groups;
|
|
1200
|
+
}
|
|
1201
|
+
function parseRecoveryReply(text, selection) {
|
|
1202
|
+
requireRecovery(typeof text === "string" && Buffer.byteLength(text) <= 32768);
|
|
1203
|
+
const groups = replyGroups(recoveryRecord(JSON.parse(text)), selection);
|
|
1204
|
+
const receipts = /* @__PURE__ */ new Map();
|
|
1205
|
+
for (const input of groups) {
|
|
1206
|
+
const group = recoveryRecord(input);
|
|
1207
|
+
exactRecoveryFields(group, [
|
|
1208
|
+
"groupId",
|
|
1209
|
+
"attestationId",
|
|
1210
|
+
"reviewReceiptId",
|
|
1211
|
+
"reviewEvidenceDigest",
|
|
1212
|
+
"resolutionId",
|
|
1213
|
+
"resolutionEvidenceDigest"
|
|
1214
|
+
]);
|
|
1215
|
+
requireRecovery(typeof group.groupId === "string" && selection.groups.includes(group.groupId) && !receipts.has(group.groupId));
|
|
1216
|
+
requireRecovery(recoveryId(group.attestationId) && recoveryId(group.reviewReceiptId) && recoveryId(group.resolutionId) && recoveryHash(group.reviewEvidenceDigest) && recoveryHash(group.resolutionEvidenceDigest));
|
|
1217
|
+
receipts.set(group.groupId, {
|
|
1218
|
+
attestationId: group.attestationId,
|
|
1219
|
+
reviewReceiptId: group.reviewReceiptId,
|
|
1220
|
+
reviewEvidenceDigest: group.reviewEvidenceDigest,
|
|
1221
|
+
resolutionId: group.resolutionId,
|
|
1222
|
+
resolutionEvidenceDigest: group.resolutionEvidenceDigest
|
|
1223
|
+
});
|
|
1224
|
+
}
|
|
1225
|
+
return receipts;
|
|
1226
|
+
}
|
|
1227
|
+
//#endregion
|
|
1228
|
+
//#region src/routes/account-owner-recovery.ts
|
|
1229
|
+
var ROOT = "/api/account/owner-recovery";
|
|
1230
|
+
var LIMITS = {
|
|
1231
|
+
preview: 65536,
|
|
1232
|
+
confirm: 1048576,
|
|
1233
|
+
import: 65536
|
|
1234
|
+
};
|
|
1235
|
+
function bodyLength(request) {
|
|
1236
|
+
requireRecovery(!request.headers.has("Transfer-Encoding"));
|
|
1237
|
+
const length = request.headers.get("Content-Length");
|
|
1238
|
+
if (length === null) return null;
|
|
1239
|
+
requireRecovery(/^(0|[1-9][0-9]*)$/.test(length));
|
|
1240
|
+
const parsed = Number(length);
|
|
1241
|
+
requireRecovery(Number.isSafeInteger(parsed));
|
|
1242
|
+
return parsed;
|
|
1243
|
+
}
|
|
1244
|
+
/** Limit actual UTF-8 bytes and elapsed time, regardless of transport metadata. */
|
|
1245
|
+
async function readBody(request, cap) {
|
|
1246
|
+
const declared = bodyLength(request);
|
|
1247
|
+
requireRecovery(declared === null || declared <= cap);
|
|
1248
|
+
requireRecovery(/^application\/json(?:\s*;\s*charset\s*=\s*(?:utf-8|"utf-8"))?\s*$/i.test(request.headers.get("Content-Type") ?? ""));
|
|
1249
|
+
const reader = request.body?.getReader();
|
|
1250
|
+
requireRecovery(reader);
|
|
1251
|
+
let timer;
|
|
1252
|
+
let stop = () => {};
|
|
1253
|
+
try {
|
|
1254
|
+
const expired = new Promise((_, reject) => {
|
|
1255
|
+
stop = () => reject(new AccountOwnerRecoveryError());
|
|
1256
|
+
timer = setTimeout(stop, 1e4);
|
|
1257
|
+
request.signal.addEventListener("abort", stop, { once: true });
|
|
1258
|
+
if (request.signal.aborted) stop();
|
|
1259
|
+
});
|
|
1260
|
+
const bytes = new Uint8Array(cap);
|
|
1261
|
+
let length = 0;
|
|
1262
|
+
while (true) {
|
|
1263
|
+
const chunk = await Promise.race([reader.read(), expired]);
|
|
1264
|
+
if (chunk.done) break;
|
|
1265
|
+
requireRecovery(length + chunk.value.byteLength <= cap);
|
|
1266
|
+
bytes.set(chunk.value, length);
|
|
1267
|
+
length += chunk.value.byteLength;
|
|
1268
|
+
}
|
|
1269
|
+
requireRecovery(!request.signal.aborted && (declared === null || declared === length));
|
|
1270
|
+
return recoveryRecord(JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes.subarray(0, length))));
|
|
1271
|
+
} catch {
|
|
1272
|
+
throw new AccountOwnerRecoveryError();
|
|
1273
|
+
} finally {
|
|
1274
|
+
clearTimeout(timer);
|
|
1275
|
+
request.signal.removeEventListener("abort", stop);
|
|
1276
|
+
reader.cancel().catch(() => {});
|
|
1277
|
+
reader.releaseLock();
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
function stringList(value) {
|
|
1281
|
+
requireRecovery(Array.isArray(value) && value.every(recoveryId));
|
|
1282
|
+
return value;
|
|
1283
|
+
}
|
|
1284
|
+
function profile(value) {
|
|
1285
|
+
const row = recoveryRecord(value);
|
|
1286
|
+
exactRecoveryFields(row, [
|
|
1287
|
+
"contextId",
|
|
1288
|
+
"memoryActorId",
|
|
1289
|
+
"deviceId",
|
|
1290
|
+
"fingerprint",
|
|
1291
|
+
"coordinatorOrigin",
|
|
1292
|
+
"coordinatorId",
|
|
1293
|
+
"configuredGroups"
|
|
1294
|
+
]);
|
|
1295
|
+
const { contextId, memoryActorId, deviceId, fingerprint, coordinatorOrigin, coordinatorId } = row;
|
|
1296
|
+
requireRecovery(recoveryId(contextId) && recoveryId(memoryActorId) && recoveryId(deviceId) && recoveryId(fingerprint) && recoveryId(coordinatorOrigin) && recoveryId(coordinatorId));
|
|
1297
|
+
return {
|
|
1298
|
+
contextId,
|
|
1299
|
+
memoryActorId,
|
|
1300
|
+
deviceId,
|
|
1301
|
+
fingerprint,
|
|
1302
|
+
coordinatorOrigin,
|
|
1303
|
+
coordinatorId,
|
|
1304
|
+
configuredGroups: stringList(row.configuredGroups)
|
|
1305
|
+
};
|
|
1306
|
+
}
|
|
1307
|
+
function selection(row) {
|
|
1308
|
+
requireRecovery(recoveryId(row.existingOwnerId));
|
|
1309
|
+
return {
|
|
1310
|
+
profile: profile(row.profile),
|
|
1311
|
+
existingOwnerId: row.existingOwnerId,
|
|
1312
|
+
groups: stringList(row.groups)
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
async function dispatch(c, service) {
|
|
1316
|
+
const request = c.req.raw;
|
|
1317
|
+
requireRecovery(!new URL(request.url).search && !request.signal.aborted);
|
|
1318
|
+
if (c.req.path === ROOT && request.method === "GET") {
|
|
1319
|
+
const length = bodyLength(request);
|
|
1320
|
+
requireRecovery(request.body === null && (length === null || length === 0));
|
|
1321
|
+
return c.json(await service.getProfile());
|
|
1322
|
+
}
|
|
1323
|
+
if (request.method !== "POST") return c.json({ error: "account_owner_recovery_invalid" }, 405);
|
|
1324
|
+
if (c.req.path === `${ROOT}/preview`) {
|
|
1325
|
+
const body = await readBody(request, LIMITS.preview);
|
|
1326
|
+
exactRecoveryFields(body, [
|
|
1327
|
+
"profile",
|
|
1328
|
+
"existingOwnerId",
|
|
1329
|
+
"groups",
|
|
1330
|
+
"requestId",
|
|
1331
|
+
"purpose"
|
|
1332
|
+
]);
|
|
1333
|
+
requireRecovery(recoveryId(body.requestId) && (body.purpose === "review" || body.purpose === "check_status"));
|
|
1334
|
+
return c.json(await service.preview({
|
|
1335
|
+
...selection(body),
|
|
1336
|
+
requestId: body.requestId,
|
|
1337
|
+
purpose: body.purpose
|
|
1338
|
+
}));
|
|
1339
|
+
}
|
|
1340
|
+
if (c.req.path === `${ROOT}/confirm`) {
|
|
1341
|
+
const body = await readBody(request, LIMITS.confirm);
|
|
1342
|
+
exactRecoveryFields(body, ["preview", "approvalDigest"]);
|
|
1343
|
+
requireRecovery(recoveryId(body.approvalDigest));
|
|
1344
|
+
return c.json(await service.confirm({
|
|
1345
|
+
preview: body.preview,
|
|
1346
|
+
approvalDigest: body.approvalDigest
|
|
1347
|
+
}));
|
|
1348
|
+
}
|
|
1349
|
+
if (c.req.path === `${ROOT}/import`) {
|
|
1350
|
+
const body = await readBody(request, LIMITS.import);
|
|
1351
|
+
exactRecoveryFields(body, ["selection", "replyText"]);
|
|
1352
|
+
const selected = recoveryRecord(body.selection);
|
|
1353
|
+
exactRecoveryFields(selected, [
|
|
1354
|
+
"profile",
|
|
1355
|
+
"existingOwnerId",
|
|
1356
|
+
"groups"
|
|
1357
|
+
]);
|
|
1358
|
+
requireRecovery(typeof body.replyText === "string");
|
|
1359
|
+
return c.json(await service.importReply({
|
|
1360
|
+
selection: selection(selected),
|
|
1361
|
+
replyText: body.replyText
|
|
1362
|
+
}));
|
|
1363
|
+
}
|
|
1364
|
+
return c.json({ error: "account_owner_recovery_invalid" }, 404);
|
|
1365
|
+
}
|
|
1366
|
+
/** Mount only after createAccountGuard: this router grants no independent authority. */
|
|
1367
|
+
function accountOwnerRecoveryRoutes(service) {
|
|
1368
|
+
const app = new Hono();
|
|
1369
|
+
app.onError((error, c) => {
|
|
1370
|
+
if (!(error instanceof AccountOwnerRecoveryError)) return c.json({ error: "account_owner_recovery_unavailable" }, 503);
|
|
1371
|
+
return c.json({ error: `account_owner_recovery_${error.code}` }, {
|
|
1372
|
+
invalid: 400,
|
|
1373
|
+
busy: 409,
|
|
1374
|
+
context_changed: 409,
|
|
1375
|
+
unavailable: 503
|
|
1376
|
+
}[error.code]);
|
|
1377
|
+
});
|
|
1378
|
+
app.all(ROOT, (c) => dispatch(c, service));
|
|
1379
|
+
app.all(`${ROOT}/*`, (c) => dispatch(c, service));
|
|
866
1380
|
return app;
|
|
867
1381
|
}
|
|
868
1382
|
//#endregion
|
|
@@ -1751,38 +2265,6 @@ function diagnosticsRoutes(getStore) {
|
|
|
1751
2265
|
return app;
|
|
1752
2266
|
}
|
|
1753
2267
|
//#endregion
|
|
1754
|
-
//#region src/routes/health.ts
|
|
1755
|
-
function databaseReachable(getStore) {
|
|
1756
|
-
try {
|
|
1757
|
-
getStore().db.pragma("schema_version", { simple: true });
|
|
1758
|
-
return true;
|
|
1759
|
-
} catch {
|
|
1760
|
-
return false;
|
|
1761
|
-
}
|
|
1762
|
-
}
|
|
1763
|
-
function healthRoutes(getStore) {
|
|
1764
|
-
const app = new Hono();
|
|
1765
|
-
app.get("/api/viewer-status", (c) => {
|
|
1766
|
-
const store = getStore();
|
|
1767
|
-
store.db.pragma("schema_version", { simple: true });
|
|
1768
|
-
c.header("Cache-Control", "no-store");
|
|
1769
|
-
return c.json({ identity: { actor_id: store.actorId } });
|
|
1770
|
-
});
|
|
1771
|
-
app.get("/api/health", (c) => {
|
|
1772
|
-
const reachable = databaseReachable(getStore);
|
|
1773
|
-
c.header("Cache-Control", "no-store");
|
|
1774
|
-
return c.json({
|
|
1775
|
-
service: VIEWER_SERVICE_DISCRIMINATOR,
|
|
1776
|
-
version: VERSION$1,
|
|
1777
|
-
pid: process.pid,
|
|
1778
|
-
uptime_ms: Math.max(0, Math.floor(process.uptime() * 1e3)),
|
|
1779
|
-
ready: reachable,
|
|
1780
|
-
database: { reachable }
|
|
1781
|
-
});
|
|
1782
|
-
});
|
|
1783
|
-
return app;
|
|
1784
|
-
}
|
|
1785
|
-
//#endregion
|
|
1786
2268
|
//#region src/helpers.ts
|
|
1787
2269
|
/**
|
|
1788
2270
|
* Shared helpers for viewer-server routes.
|
|
@@ -1876,44 +2358,219 @@ async function parseJsonObjectBody(c, maxBytes) {
|
|
|
1876
2358
|
return parsed;
|
|
1877
2359
|
}
|
|
1878
2360
|
//#endregion
|
|
1879
|
-
//#region src/routes/
|
|
1880
|
-
var
|
|
1881
|
-
var
|
|
1882
|
-
|
|
1883
|
-
|
|
2361
|
+
//#region src/routes/target-validation.ts
|
|
2362
|
+
var IDENTITY_TARGET_KEYS = new Set(VIEWER_IDENTITY_TARGET_KEYS);
|
|
2363
|
+
var BOOLEAN_IDENTITY_TARGET_KEYS = /* @__PURE__ */ new Set([
|
|
2364
|
+
"actor_id_present",
|
|
2365
|
+
"embedding_disabled",
|
|
2366
|
+
"embedding_offline"
|
|
2367
|
+
]);
|
|
2368
|
+
function invalidRequest$1(message) {
|
|
2369
|
+
return {
|
|
2370
|
+
ok: false,
|
|
2371
|
+
status: 400,
|
|
2372
|
+
body: { error: {
|
|
2373
|
+
code: "invalid_request",
|
|
2374
|
+
message
|
|
2375
|
+
} }
|
|
2376
|
+
};
|
|
1884
2377
|
}
|
|
1885
|
-
function
|
|
1886
|
-
return
|
|
2378
|
+
function conflict(code, message) {
|
|
2379
|
+
return {
|
|
2380
|
+
ok: false,
|
|
2381
|
+
status: 409,
|
|
2382
|
+
body: { error: {
|
|
2383
|
+
code,
|
|
2384
|
+
message
|
|
2385
|
+
} }
|
|
2386
|
+
};
|
|
1887
2387
|
}
|
|
1888
|
-
function
|
|
1889
|
-
|
|
1890
|
-
store.db.function("codemem_casefold", { deterministic: true }, casefold);
|
|
1891
|
-
store.db.function("codemem_project_basename", { deterministic: true }, (value) => projectBasename(String(value ?? "").trim()));
|
|
1892
|
-
searchFunctionsRegisteredStores.add(store);
|
|
2388
|
+
function isRecord$1(value) {
|
|
2389
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
1893
2390
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
const direct = String(item[key] ?? "").trim();
|
|
1897
|
-
if (direct) return direct;
|
|
1898
|
-
const rawMetadata = item.metadata ?? item.metadata_json;
|
|
1899
|
-
const metadata = typeof rawMetadata === "string" ? fromJson(rawMetadata) : rawMetadata;
|
|
1900
|
-
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return "";
|
|
1901
|
-
return String(metadata[key] ?? "").trim();
|
|
2391
|
+
function currentIdentityTarget() {
|
|
2392
|
+
return buildViewerIdentityTarget();
|
|
1902
2393
|
}
|
|
1903
|
-
function
|
|
1904
|
-
|
|
2394
|
+
function requestedDbMatches(store, value) {
|
|
2395
|
+
if (value == null) return true;
|
|
2396
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
2397
|
+
return resolve(resolveDbPath(value.trim())) === resolve(store.dbPath);
|
|
1905
2398
|
}
|
|
1906
|
-
function
|
|
1907
|
-
const
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
}
|
|
1913
|
-
return rows;
|
|
2399
|
+
function implicitConfigPath() {
|
|
2400
|
+
const { resolved, fallbackChain } = resolveCodememConfigPath();
|
|
2401
|
+
if (resolved.source !== "env-codemem-config") return resolve(resolved.path);
|
|
2402
|
+
const candidates = fallbackChain.filter((candidate) => candidate.valid);
|
|
2403
|
+
const implicit = candidates.find((candidate) => candidate.exists) ?? candidates[0];
|
|
2404
|
+
return implicit ? resolve(implicit.path) : null;
|
|
1914
2405
|
}
|
|
1915
|
-
function
|
|
1916
|
-
if (
|
|
2406
|
+
function identityFieldMatches(key, requested, expected) {
|
|
2407
|
+
if (requested === expected) return true;
|
|
2408
|
+
if (key !== "config_path" || requested !== null && expected !== null) return false;
|
|
2409
|
+
return (requested ?? expected) === implicitConfigPath();
|
|
2410
|
+
}
|
|
2411
|
+
function requestedIdentityMatches(value) {
|
|
2412
|
+
if (value == null) return true;
|
|
2413
|
+
if (!isRecord$1(value)) return null;
|
|
2414
|
+
if (Object.keys(value).length !== IDENTITY_TARGET_KEYS.size || Object.keys(value).some((key) => !IDENTITY_TARGET_KEYS.has(key))) return "unsupported";
|
|
2415
|
+
const expected = currentIdentityTarget();
|
|
2416
|
+
for (const key of VIEWER_IDENTITY_TARGET_KEYS) {
|
|
2417
|
+
const requested = value[key];
|
|
2418
|
+
if (BOOLEAN_IDENTITY_TARGET_KEYS.has(key)) {
|
|
2419
|
+
if (typeof requested !== "boolean") return null;
|
|
2420
|
+
} else if (requested !== null && typeof requested !== "string") return null;
|
|
2421
|
+
if (!identityFieldMatches(key, requested, expected[key])) return false;
|
|
2422
|
+
}
|
|
2423
|
+
return true;
|
|
2424
|
+
}
|
|
2425
|
+
function validateViewerTarget(store, payload, options = {}) {
|
|
2426
|
+
if (options.requirePairedTargets) {
|
|
2427
|
+
const hasDbPath = Object.hasOwn(payload, "db_path");
|
|
2428
|
+
if (hasDbPath !== Object.hasOwn(payload, "identity_target")) return invalidRequest$1("db_path and identity_target must be provided together");
|
|
2429
|
+
if (hasDbPath && (payload.db_path == null || payload.identity_target == null)) return invalidRequest$1("db_path and identity_target must be valid when provided");
|
|
2430
|
+
}
|
|
2431
|
+
const dbMatches = requestedDbMatches(store, payload.db_path);
|
|
2432
|
+
if (dbMatches == null) return invalidRequest$1("db_path must be a non-empty string");
|
|
2433
|
+
if (!dbMatches) return conflict("viewer_db_mismatch", "viewer database does not match request");
|
|
2434
|
+
const identityMatches = requestedIdentityMatches(payload.identity_target);
|
|
2435
|
+
if (identityMatches === "unsupported") return conflict("viewer_contract_unsupported", "viewer request contract is incompatible");
|
|
2436
|
+
if (identityMatches == null) return invalidRequest$1("identity_target is invalid");
|
|
2437
|
+
if (!identityMatches) return conflict("viewer_identity_mismatch", "viewer identity does not match request");
|
|
2438
|
+
if ((options.requireCurrentIdentity || payload.identity_target != null) && !store.hasCurrentIdentity()) return conflict("viewer_identity_mismatch", "viewer identity does not match request");
|
|
2439
|
+
return { ok: true };
|
|
2440
|
+
}
|
|
2441
|
+
/**
|
|
2442
|
+
* Middleware read of db_path / identity_target from the query string.
|
|
2443
|
+
* Native tools (pi extension) send targets as query params so a pre-handler
|
|
2444
|
+
* guard can reject a foreign viewer without consuming POST bodies. Lenient:
|
|
2445
|
+
* clients that omit targets (UI, MCP, curl) are unaffected.
|
|
2446
|
+
*/
|
|
2447
|
+
function viewerTargetQueryGuard(getStore) {
|
|
2448
|
+
return async (c, next) => {
|
|
2449
|
+
const dbPath = c.req.query("db_path");
|
|
2450
|
+
const rawIdentity = c.req.query("identity_target");
|
|
2451
|
+
if (dbPath == null && rawIdentity == null) return next();
|
|
2452
|
+
let identityTarget;
|
|
2453
|
+
if (rawIdentity != null) try {
|
|
2454
|
+
identityTarget = JSON.parse(rawIdentity);
|
|
2455
|
+
} catch {
|
|
2456
|
+
return c.json({ error: {
|
|
2457
|
+
code: "invalid_request",
|
|
2458
|
+
message: "identity_target must be JSON"
|
|
2459
|
+
} }, 400);
|
|
2460
|
+
}
|
|
2461
|
+
const target = validateViewerTarget(getStore(), {
|
|
2462
|
+
db_path: dbPath ?? void 0,
|
|
2463
|
+
identity_target: identityTarget
|
|
2464
|
+
});
|
|
2465
|
+
if (!target.ok) return c.json(target.body, target.status);
|
|
2466
|
+
return next();
|
|
2467
|
+
};
|
|
2468
|
+
}
|
|
2469
|
+
//#endregion
|
|
2470
|
+
//#region src/routes/file-context.ts
|
|
2471
|
+
function fileContextRoutes(getStore) {
|
|
2472
|
+
const app = new Hono();
|
|
2473
|
+
app.post("/api/file-context", async (c) => {
|
|
2474
|
+
const body = await parseJsonObjectBody(c, FILE_CONTEXT_TRANSPORT_MAX_BYTES);
|
|
2475
|
+
if (body instanceof Response) return body;
|
|
2476
|
+
const parsed = parseFileContextTransportRequest(body);
|
|
2477
|
+
if (!parsed.success) return c.json({ error: {
|
|
2478
|
+
code: "invalid_request",
|
|
2479
|
+
message: "invalid file-context request"
|
|
2480
|
+
} }, 400);
|
|
2481
|
+
try {
|
|
2482
|
+
const store = getStore();
|
|
2483
|
+
const target = validateViewerTarget(store, body, {
|
|
2484
|
+
requireCurrentIdentity: true,
|
|
2485
|
+
requirePairedTargets: true
|
|
2486
|
+
});
|
|
2487
|
+
if (!target.ok) return c.json(target.body, target.status);
|
|
2488
|
+
const response = dispatchFileContextTransport(store, parsed.data);
|
|
2489
|
+
if (!response.ok) return c.json({ error: {
|
|
2490
|
+
code: "invalid_request",
|
|
2491
|
+
message: "delivery does not match attempt"
|
|
2492
|
+
} }, 400);
|
|
2493
|
+
return c.json(response);
|
|
2494
|
+
} catch {
|
|
2495
|
+
return c.json({ error: {
|
|
2496
|
+
code: "file_context_failed",
|
|
2497
|
+
message: "file context unavailable"
|
|
2498
|
+
} }, 503);
|
|
2499
|
+
}
|
|
2500
|
+
});
|
|
2501
|
+
return app;
|
|
2502
|
+
}
|
|
2503
|
+
//#endregion
|
|
2504
|
+
//#region src/routes/health.ts
|
|
2505
|
+
function databaseReachable(getStore) {
|
|
2506
|
+
try {
|
|
2507
|
+
getStore().db.pragma("schema_version", { simple: true });
|
|
2508
|
+
return true;
|
|
2509
|
+
} catch {
|
|
2510
|
+
return false;
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
function healthRoutes(getStore) {
|
|
2514
|
+
const app = new Hono();
|
|
2515
|
+
app.get("/api/viewer-status", (c) => {
|
|
2516
|
+
const store = getStore();
|
|
2517
|
+
store.db.pragma("schema_version", { simple: true });
|
|
2518
|
+
c.header("Cache-Control", "no-store");
|
|
2519
|
+
return c.json({ identity: { actor_id: store.actorId } });
|
|
2520
|
+
});
|
|
2521
|
+
app.get("/api/health", (c) => {
|
|
2522
|
+
const reachable = databaseReachable(getStore);
|
|
2523
|
+
c.header("Cache-Control", "no-store");
|
|
2524
|
+
return c.json({
|
|
2525
|
+
service: VIEWER_SERVICE_DISCRIMINATOR,
|
|
2526
|
+
version: VERSION$1,
|
|
2527
|
+
pid: process.pid,
|
|
2528
|
+
uptime_ms: Math.max(0, Math.floor(process.uptime() * 1e3)),
|
|
2529
|
+
ready: reachable,
|
|
2530
|
+
database: { reachable }
|
|
2531
|
+
});
|
|
2532
|
+
});
|
|
2533
|
+
return app;
|
|
2534
|
+
}
|
|
2535
|
+
//#endregion
|
|
2536
|
+
//#region src/routes/memory.ts
|
|
2537
|
+
var MAX_FEED_QUERY_CODE_UNITS = 256;
|
|
2538
|
+
var searchFunctionsRegisteredStores = /* @__PURE__ */ new WeakSet();
|
|
2539
|
+
function casefold(value) {
|
|
2540
|
+
return String(value ?? "").toLowerCase();
|
|
2541
|
+
}
|
|
2542
|
+
function normalizeMemorySearchQuery(value) {
|
|
2543
|
+
return casefold(value?.trim()).slice(0, MAX_FEED_QUERY_CODE_UNITS) || void 0;
|
|
2544
|
+
}
|
|
2545
|
+
function ensureMemorySearchFunctions(store) {
|
|
2546
|
+
if (searchFunctionsRegisteredStores.has(store)) return;
|
|
2547
|
+
store.db.function("codemem_casefold", { deterministic: true }, casefold);
|
|
2548
|
+
store.db.function("codemem_project_basename", { deterministic: true }, (value) => projectBasename(String(value ?? "").trim()));
|
|
2549
|
+
searchFunctionsRegisteredStores.add(store);
|
|
2550
|
+
}
|
|
2551
|
+
var IDENTITY_LOOKUP_BATCH_SIZE = 500;
|
|
2552
|
+
function identityValue(item, key) {
|
|
2553
|
+
const direct = String(item[key] ?? "").trim();
|
|
2554
|
+
if (direct) return direct;
|
|
2555
|
+
const rawMetadata = item.metadata ?? item.metadata_json;
|
|
2556
|
+
const metadata = typeof rawMetadata === "string" ? fromJson(rawMetadata) : rawMetadata;
|
|
2557
|
+
if (!metadata || typeof metadata !== "object" || Array.isArray(metadata)) return "";
|
|
2558
|
+
return String(metadata[key] ?? "").trim();
|
|
2559
|
+
}
|
|
2560
|
+
function uniqueStrings(items, key) {
|
|
2561
|
+
return [...new Set(items.map((item) => identityValue(item, key)).filter(Boolean))];
|
|
2562
|
+
}
|
|
2563
|
+
function batchedLookup(store, values, query) {
|
|
2564
|
+
const rows = [];
|
|
2565
|
+
for (let offset = 0; offset < values.length; offset += IDENTITY_LOOKUP_BATCH_SIZE) {
|
|
2566
|
+
const batch = values.slice(offset, offset + IDENTITY_LOOKUP_BATCH_SIZE);
|
|
2567
|
+
const placeholders = batch.map(() => "?").join(", ");
|
|
2568
|
+
rows.push(...store.db.prepare(query(placeholders)).all(...batch));
|
|
2569
|
+
}
|
|
2570
|
+
return rows;
|
|
2571
|
+
}
|
|
2572
|
+
function humanName(value) {
|
|
2573
|
+
if (typeof value !== "string") return void 0;
|
|
1917
2574
|
try {
|
|
1918
2575
|
return normalizeHumanPresentationName(value, "display_name");
|
|
1919
2576
|
} catch {
|
|
@@ -2082,16 +2739,19 @@ function memorySearchSql(query) {
|
|
|
2082
2739
|
params: [query]
|
|
2083
2740
|
};
|
|
2084
2741
|
}
|
|
2085
|
-
function
|
|
2086
|
-
const
|
|
2087
|
-
const
|
|
2088
|
-
"memory_items.
|
|
2089
|
-
`
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
const
|
|
2093
|
-
|
|
2094
|
-
return
|
|
2742
|
+
function countVisibleMemoryClassifications(store, filters) {
|
|
2743
|
+
const branches = buildFilterBranchesWithContext(filters, store.ownershipFilterContext());
|
|
2744
|
+
const source = branches.map((branch) => {
|
|
2745
|
+
const from = branch.joinSessions ? "memory_items JOIN sessions ON sessions.id = memory_items.session_id" : "memory_items";
|
|
2746
|
+
return `SELECT ${summaryLikeSqlPredicate()} AS is_summary FROM ${from}
|
|
2747
|
+
WHERE memory_items.active = 1 AND ${branch.clauses.join(" AND ") || "1 = 1"}`;
|
|
2748
|
+
}).join(" UNION ALL ");
|
|
2749
|
+
const row = store.db.prepare(`SELECT COUNT(*) AS memories,
|
|
2750
|
+
COUNT(CASE WHEN NOT is_summary THEN 1 END) AS observations FROM (${source})`).get(...branches.flatMap((branch) => branch.params));
|
|
2751
|
+
return {
|
|
2752
|
+
memories: Number(row?.memories ?? 0),
|
|
2753
|
+
observations: Number(row?.observations ?? 0)
|
|
2754
|
+
};
|
|
2095
2755
|
}
|
|
2096
2756
|
function sessionAllowsArtifactAccess(store, sessionId) {
|
|
2097
2757
|
const visibleCount = countVisibleMemoryRows(store, { session_id: sessionId });
|
|
@@ -2160,6 +2820,23 @@ function queryMemoryPage(store, options) {
|
|
|
2160
2820
|
const ownedBySelf = store.buildOwnershipPredicate();
|
|
2161
2821
|
return rows.map((row) => serializeMemoryRow(ownedBySelf, row));
|
|
2162
2822
|
}
|
|
2823
|
+
async function handleLegacyPackRequest(c, getStore) {
|
|
2824
|
+
const store = getStore();
|
|
2825
|
+
const context = c.req.query("context") || "";
|
|
2826
|
+
if (!context) return c.json({ error: "context required" }, 400);
|
|
2827
|
+
const limit = queryInt(c.req.query("limit"), 10);
|
|
2828
|
+
const tokenBudgetStr = c.req.query("token_budget");
|
|
2829
|
+
let tokenBudget;
|
|
2830
|
+
if (tokenBudgetStr) {
|
|
2831
|
+
tokenBudget = parseStrictInteger(tokenBudgetStr) ?? void 0;
|
|
2832
|
+
if (tokenBudget === void 0) return c.json({ error: "token_budget must be int" }, 400);
|
|
2833
|
+
}
|
|
2834
|
+
const project = c.req.query("project") || void 0;
|
|
2835
|
+
const filters = {};
|
|
2836
|
+
if (project) filters.project = project;
|
|
2837
|
+
const pack = await store.buildMemoryPackAsync(context, limit, tokenBudget ?? null, filters, void 0, void 0, { signal: c.req.raw.signal });
|
|
2838
|
+
return c.json(pack);
|
|
2839
|
+
}
|
|
2163
2840
|
function memoryRoutes(getStore) {
|
|
2164
2841
|
const app = new Hono();
|
|
2165
2842
|
app.get("/api/sessions", (c) => {
|
|
@@ -2267,18 +2944,13 @@ function memoryRoutes(getStore) {
|
|
|
2267
2944
|
const project = c.req.query("project") || null;
|
|
2268
2945
|
let prompts;
|
|
2269
2946
|
let artifacts;
|
|
2270
|
-
|
|
2271
|
-
let observations;
|
|
2947
|
+
const { memories, observations } = countVisibleMemoryClassifications(store, project ? { project } : void 0);
|
|
2272
2948
|
if (project) {
|
|
2273
2949
|
prompts = countVisiblePromptRows(store, project);
|
|
2274
2950
|
artifacts = countVisibleArtifactRows(store, project);
|
|
2275
|
-
memories = countVisibleMemoryRows(store, { project });
|
|
2276
|
-
observations = countVisibleObservationRows(store, { project });
|
|
2277
2951
|
} else {
|
|
2278
2952
|
prompts = countVisiblePromptRows(store);
|
|
2279
2953
|
artifacts = countVisibleArtifactRows(store);
|
|
2280
|
-
memories = countVisibleMemoryRows(store);
|
|
2281
|
-
observations = countVisibleObservationRows(store);
|
|
2282
2954
|
}
|
|
2283
2955
|
const total = prompts + artifacts + memories;
|
|
2284
2956
|
return c.json({
|
|
@@ -2290,25 +2962,7 @@ function memoryRoutes(getStore) {
|
|
|
2290
2962
|
});
|
|
2291
2963
|
}
|
|
2292
2964
|
});
|
|
2293
|
-
app.get("/api/pack",
|
|
2294
|
-
const store = getStore();
|
|
2295
|
-
{
|
|
2296
|
-
const context = c.req.query("context") || "";
|
|
2297
|
-
if (!context) return c.json({ error: "context required" }, 400);
|
|
2298
|
-
const limit = queryInt(c.req.query("limit"), 10);
|
|
2299
|
-
const tokenBudgetStr = c.req.query("token_budget");
|
|
2300
|
-
let tokenBudget;
|
|
2301
|
-
if (tokenBudgetStr) {
|
|
2302
|
-
tokenBudget = parseStrictInteger(tokenBudgetStr) ?? void 0;
|
|
2303
|
-
if (tokenBudget === void 0) return c.json({ error: "token_budget must be int" }, 400);
|
|
2304
|
-
}
|
|
2305
|
-
const project = c.req.query("project") || void 0;
|
|
2306
|
-
const filters = {};
|
|
2307
|
-
if (project) filters.project = project;
|
|
2308
|
-
const pack = await store.buildMemoryPackAsync(context, limit, tokenBudget ?? null, filters);
|
|
2309
|
-
return c.json(pack);
|
|
2310
|
-
}
|
|
2311
|
-
});
|
|
2965
|
+
app.get("/api/pack", (c) => handleLegacyPackRequest(c, getStore));
|
|
2312
2966
|
app.post("/api/pack/trace", async (c) => {
|
|
2313
2967
|
const store = getStore();
|
|
2314
2968
|
const parsed = await c.req.json().catch(() => Symbol.for("invalid-json"));
|
|
@@ -2330,7 +2984,11 @@ function memoryRoutes(getStore) {
|
|
|
2330
2984
|
const filters = {};
|
|
2331
2985
|
if (project) filters.project = project;
|
|
2332
2986
|
if (workingSetFiles.length > 0) filters.working_set_paths = workingSetFiles;
|
|
2333
|
-
const trace = await store.buildMemoryPackTraceAsync(context, limit, tokenBudget, filters
|
|
2987
|
+
const trace = await store.buildMemoryPackTraceAsync(context, limit, tokenBudget, filters, ...[
|
|
2988
|
+
void 0,
|
|
2989
|
+
void 0,
|
|
2990
|
+
{ signal: c.req.raw.signal }
|
|
2991
|
+
]);
|
|
2334
2992
|
return c.json(trace);
|
|
2335
2993
|
});
|
|
2336
2994
|
app.get("/api/memory", (c) => {
|
|
@@ -2752,6 +3410,22 @@ function expandMemories(store, args) {
|
|
|
2752
3410
|
}
|
|
2753
3411
|
};
|
|
2754
3412
|
}
|
|
3413
|
+
function piSessionSearchRoutes(getStore) {
|
|
3414
|
+
const searchApp = new Hono();
|
|
3415
|
+
searchApp.get("/api/pi/sessions/search", (c) => {
|
|
3416
|
+
const store = getStore();
|
|
3417
|
+
const query = c.req.query("query") ?? "";
|
|
3418
|
+
if (!query.trim()) return c.json({ error: "query required" }, 400);
|
|
3419
|
+
const result = searchPiSessions(store.db, query, {
|
|
3420
|
+
project: c.req.query("project"),
|
|
3421
|
+
session_id: c.req.query("session_id"),
|
|
3422
|
+
limit: queryInt(c.req.query("limit"), 10),
|
|
3423
|
+
snippet_chars: queryInt(c.req.query("snippet_chars"), 1200)
|
|
3424
|
+
});
|
|
3425
|
+
return c.json(result);
|
|
3426
|
+
});
|
|
3427
|
+
return searchApp;
|
|
3428
|
+
}
|
|
2755
3429
|
function memoryToolRoutes(getStore) {
|
|
2756
3430
|
const app = new Hono();
|
|
2757
3431
|
app.post("/api/memories/remember", async (c) => {
|
|
@@ -2950,7 +3624,7 @@ function memoryToolRoutes(getStore) {
|
|
|
2950
3624
|
return c.json({ error: msg }, 400);
|
|
2951
3625
|
}
|
|
2952
3626
|
});
|
|
2953
|
-
return app;
|
|
3627
|
+
return app.route("/", piSessionSearchRoutes(getStore));
|
|
2954
3628
|
}
|
|
2955
3629
|
//#endregion
|
|
2956
3630
|
//#region src/routes/observer-status.ts
|
|
@@ -3025,136 +3699,27 @@ function observerStatusRoutes(deps) {
|
|
|
3025
3699
|
return app;
|
|
3026
3700
|
}
|
|
3027
3701
|
//#endregion
|
|
3028
|
-
//#region src/routes/
|
|
3029
|
-
var
|
|
3030
|
-
var
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3702
|
+
//#region src/routes/pack.ts
|
|
3703
|
+
var MAX_LEDGER_PAYLOAD_BYTES = 16384;
|
|
3704
|
+
var MAX_METADATA_FIELD_CHARS = 512;
|
|
3705
|
+
var MAX_WORKING_SET_FILES = 50;
|
|
3706
|
+
var MAX_WORKING_SET_PATH_CHARS = 512;
|
|
3707
|
+
var INVALID_JSON = Symbol("invalid-json");
|
|
3708
|
+
var FORBIDDEN_LEDGER_KEYS = /* @__PURE__ */ new Set([
|
|
3709
|
+
"body",
|
|
3710
|
+
"context",
|
|
3711
|
+
"pack",
|
|
3712
|
+
"pack_text",
|
|
3713
|
+
"path",
|
|
3714
|
+
"preview",
|
|
3715
|
+
"prompt",
|
|
3716
|
+
"query",
|
|
3717
|
+
"raw_prompt",
|
|
3718
|
+
"title"
|
|
3034
3719
|
]);
|
|
3035
|
-
function
|
|
3036
|
-
|
|
3037
|
-
|
|
3038
|
-
status: 400,
|
|
3039
|
-
body: { error: {
|
|
3040
|
-
code: "invalid_request",
|
|
3041
|
-
message
|
|
3042
|
-
} }
|
|
3043
|
-
};
|
|
3044
|
-
}
|
|
3045
|
-
function conflict(code, message) {
|
|
3046
|
-
return {
|
|
3047
|
-
ok: false,
|
|
3048
|
-
status: 409,
|
|
3049
|
-
body: { error: {
|
|
3050
|
-
code,
|
|
3051
|
-
message
|
|
3052
|
-
} }
|
|
3053
|
-
};
|
|
3054
|
-
}
|
|
3055
|
-
function isRecord$1(value) {
|
|
3056
|
-
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
3057
|
-
}
|
|
3058
|
-
function currentIdentityTarget() {
|
|
3059
|
-
return buildViewerIdentityTarget();
|
|
3060
|
-
}
|
|
3061
|
-
function requestedDbMatches(store, value) {
|
|
3062
|
-
if (value == null) return true;
|
|
3063
|
-
if (typeof value !== "string" || !value.trim()) return null;
|
|
3064
|
-
return resolve(resolveDbPath(value.trim())) === resolve(store.dbPath);
|
|
3065
|
-
}
|
|
3066
|
-
function implicitConfigPath() {
|
|
3067
|
-
const { resolved, fallbackChain } = resolveCodememConfigPath();
|
|
3068
|
-
if (resolved.source !== "env-codemem-config") return resolve(resolved.path);
|
|
3069
|
-
const candidates = fallbackChain.filter((candidate) => candidate.valid);
|
|
3070
|
-
const implicit = candidates.find((candidate) => candidate.exists) ?? candidates[0];
|
|
3071
|
-
return implicit ? resolve(implicit.path) : null;
|
|
3072
|
-
}
|
|
3073
|
-
function identityFieldMatches(key, requested, expected) {
|
|
3074
|
-
if (requested === expected) return true;
|
|
3075
|
-
if (key !== "config_path" || requested !== null && expected !== null) return false;
|
|
3076
|
-
return (requested ?? expected) === implicitConfigPath();
|
|
3077
|
-
}
|
|
3078
|
-
function requestedIdentityMatches(value) {
|
|
3079
|
-
if (value == null) return true;
|
|
3080
|
-
if (!isRecord$1(value)) return null;
|
|
3081
|
-
if (Object.keys(value).length !== IDENTITY_TARGET_KEYS.size || Object.keys(value).some((key) => !IDENTITY_TARGET_KEYS.has(key))) return "unsupported";
|
|
3082
|
-
const expected = currentIdentityTarget();
|
|
3083
|
-
for (const key of VIEWER_IDENTITY_TARGET_KEYS) {
|
|
3084
|
-
const requested = value[key];
|
|
3085
|
-
if (BOOLEAN_IDENTITY_TARGET_KEYS.has(key)) {
|
|
3086
|
-
if (typeof requested !== "boolean") return null;
|
|
3087
|
-
} else if (requested !== null && typeof requested !== "string") return null;
|
|
3088
|
-
if (!identityFieldMatches(key, requested, expected[key])) return false;
|
|
3089
|
-
}
|
|
3090
|
-
return true;
|
|
3091
|
-
}
|
|
3092
|
-
function validateViewerTarget(store, payload, options = {}) {
|
|
3093
|
-
if (options.requirePairedTargets) {
|
|
3094
|
-
const hasDbPath = Object.hasOwn(payload, "db_path");
|
|
3095
|
-
if (hasDbPath !== Object.hasOwn(payload, "identity_target")) return invalidRequest$1("db_path and identity_target must be provided together");
|
|
3096
|
-
if (hasDbPath && (payload.db_path == null || payload.identity_target == null)) return invalidRequest$1("db_path and identity_target must be valid when provided");
|
|
3097
|
-
}
|
|
3098
|
-
const dbMatches = requestedDbMatches(store, payload.db_path);
|
|
3099
|
-
if (dbMatches == null) return invalidRequest$1("db_path must be a non-empty string");
|
|
3100
|
-
if (!dbMatches) return conflict("viewer_db_mismatch", "viewer database does not match request");
|
|
3101
|
-
const identityMatches = requestedIdentityMatches(payload.identity_target);
|
|
3102
|
-
if (identityMatches === "unsupported") return conflict("viewer_contract_unsupported", "viewer request contract is incompatible");
|
|
3103
|
-
if (identityMatches == null) return invalidRequest$1("identity_target is invalid");
|
|
3104
|
-
if (!identityMatches) return conflict("viewer_identity_mismatch", "viewer identity does not match request");
|
|
3105
|
-
if ((options.requireCurrentIdentity || payload.identity_target != null) && !store.hasCurrentIdentity()) return conflict("viewer_identity_mismatch", "viewer identity does not match request");
|
|
3106
|
-
return { ok: true };
|
|
3107
|
-
}
|
|
3108
|
-
/**
|
|
3109
|
-
* Middleware read of db_path / identity_target from the query string.
|
|
3110
|
-
* Native tools (pi extension) send targets as query params so a pre-handler
|
|
3111
|
-
* guard can reject a foreign viewer without consuming POST bodies. Lenient:
|
|
3112
|
-
* clients that omit targets (UI, MCP, curl) are unaffected.
|
|
3113
|
-
*/
|
|
3114
|
-
function viewerTargetQueryGuard(getStore) {
|
|
3115
|
-
return async (c, next) => {
|
|
3116
|
-
const dbPath = c.req.query("db_path");
|
|
3117
|
-
const rawIdentity = c.req.query("identity_target");
|
|
3118
|
-
if (dbPath == null && rawIdentity == null) return next();
|
|
3119
|
-
let identityTarget;
|
|
3120
|
-
if (rawIdentity != null) try {
|
|
3121
|
-
identityTarget = JSON.parse(rawIdentity);
|
|
3122
|
-
} catch {
|
|
3123
|
-
return c.json({ error: {
|
|
3124
|
-
code: "invalid_request",
|
|
3125
|
-
message: "identity_target must be JSON"
|
|
3126
|
-
} }, 400);
|
|
3127
|
-
}
|
|
3128
|
-
const target = validateViewerTarget(getStore(), {
|
|
3129
|
-
db_path: dbPath ?? void 0,
|
|
3130
|
-
identity_target: identityTarget
|
|
3131
|
-
});
|
|
3132
|
-
if (!target.ok) return c.json(target.body, target.status);
|
|
3133
|
-
return next();
|
|
3134
|
-
};
|
|
3135
|
-
}
|
|
3136
|
-
//#endregion
|
|
3137
|
-
//#region src/routes/pack.ts
|
|
3138
|
-
var MAX_LEDGER_PAYLOAD_BYTES = 16384;
|
|
3139
|
-
var MAX_METADATA_FIELD_CHARS = 512;
|
|
3140
|
-
var MAX_WORKING_SET_FILES = 50;
|
|
3141
|
-
var MAX_WORKING_SET_PATH_CHARS = 512;
|
|
3142
|
-
var INVALID_JSON = Symbol("invalid-json");
|
|
3143
|
-
var FORBIDDEN_LEDGER_KEYS = /* @__PURE__ */ new Set([
|
|
3144
|
-
"body",
|
|
3145
|
-
"context",
|
|
3146
|
-
"pack",
|
|
3147
|
-
"pack_text",
|
|
3148
|
-
"path",
|
|
3149
|
-
"preview",
|
|
3150
|
-
"prompt",
|
|
3151
|
-
"query",
|
|
3152
|
-
"raw_prompt",
|
|
3153
|
-
"title"
|
|
3154
|
-
]);
|
|
3155
|
-
function optionalMetadataString(field) {
|
|
3156
|
-
const error = `ledger metadata field ${field} is invalid`;
|
|
3157
|
-
return z.string({ error }).max(MAX_METADATA_FIELD_CHARS, { error }).nullish();
|
|
3720
|
+
function optionalMetadataString(field) {
|
|
3721
|
+
const error = `ledger metadata field ${field} is invalid`;
|
|
3722
|
+
return z.string({ error }).max(MAX_METADATA_FIELD_CHARS, { error }).nullish();
|
|
3158
3723
|
}
|
|
3159
3724
|
var attemptMetadataShape = {
|
|
3160
3725
|
attempt_id: z.string({ error: "ledger metadata requires attempt_id" }).min(1, { error: "ledger metadata requires attempt_id" }).max(MAX_METADATA_FIELD_CHARS, { error: "ledger metadata field attempt_id is invalid" }),
|
|
@@ -3301,14 +3866,15 @@ function packRenderOptions(value) {
|
|
|
3301
3866
|
...value.compact_detail_count != null ? { compactDetailCount: value.compact_detail_count } : {}
|
|
3302
3867
|
};
|
|
3303
3868
|
}
|
|
3304
|
-
function packRequestArgs(request) {
|
|
3869
|
+
function packRequestArgs(request, signal) {
|
|
3305
3870
|
return [
|
|
3306
3871
|
request.context,
|
|
3307
3872
|
request.limit,
|
|
3308
3873
|
request.tokenBudget,
|
|
3309
3874
|
request.filters,
|
|
3310
3875
|
request.renderOptions,
|
|
3311
|
-
request.automaticContext
|
|
3876
|
+
request.automaticContext,
|
|
3877
|
+
{ signal }
|
|
3312
3878
|
];
|
|
3313
3879
|
}
|
|
3314
3880
|
function packSchemaError(error) {
|
|
@@ -3496,11 +4062,9 @@ function packTransportRoutes(getStore) {
|
|
|
3496
4062
|
const store = getStore();
|
|
3497
4063
|
const target = validateViewerTarget(store, parsed, { requireCurrentIdentity: true });
|
|
3498
4064
|
if (!target.ok) return c.json(target.body, target.status);
|
|
3499
|
-
if (!request.attempt)
|
|
3500
|
-
|
|
3501
|
-
|
|
3502
|
-
}
|
|
3503
|
-
const artifacts = await store.buildMemoryPackWithTraceAsync(...packRequestArgs(request));
|
|
4065
|
+
if (!request.attempt) return await handleUntracedPackRequest(c, store, request);
|
|
4066
|
+
const artifacts = await store.buildMemoryPackWithTraceAsync(...packRequestArgs(request, c.req.raw.signal));
|
|
4067
|
+
c.req.raw.signal.throwIfAborted();
|
|
3504
4068
|
let ledgerArtifactFingerprint;
|
|
3505
4069
|
try {
|
|
3506
4070
|
ledgerArtifactFingerprint = promptPackArtifactFingerprint(store.db, request.context, request.filters, artifacts);
|
|
@@ -3524,6 +4088,11 @@ function packTransportRoutes(getStore) {
|
|
|
3524
4088
|
app.post("/api/prompt-pack-ledger", (c) => handleLedgerRequest(c, getStore));
|
|
3525
4089
|
return app;
|
|
3526
4090
|
}
|
|
4091
|
+
async function handleUntracedPackRequest(c, store, request) {
|
|
4092
|
+
const pack = await store.buildMemoryPackAsync(...packRequestArgs(request, c.req.raw.signal));
|
|
4093
|
+
c.req.raw.signal.throwIfAborted();
|
|
4094
|
+
return c.json(pack);
|
|
4095
|
+
}
|
|
3527
4096
|
//#endregion
|
|
3528
4097
|
//#region src/raw-event-processing.ts
|
|
3529
4098
|
function nudgeRawEventSessions(sweeper, sessions) {
|
|
@@ -3611,7 +4180,7 @@ async function writeDurableFile(path, contents) {
|
|
|
3611
4180
|
await handle.close();
|
|
3612
4181
|
}
|
|
3613
4182
|
}
|
|
3614
|
-
async function syncDirectory(path) {
|
|
4183
|
+
async function syncDirectory$1(path) {
|
|
3615
4184
|
let handle;
|
|
3616
4185
|
try {
|
|
3617
4186
|
handle = await open(path, "r");
|
|
@@ -3639,7 +4208,7 @@ async function ensurePrivateDirectory(path) {
|
|
|
3639
4208
|
const boundary = dirname(firstCreated);
|
|
3640
4209
|
let current = path;
|
|
3641
4210
|
while (true) {
|
|
3642
|
-
await syncDirectory(current);
|
|
4211
|
+
await syncDirectory$1(current);
|
|
3643
4212
|
if (current === boundary) return;
|
|
3644
4213
|
current = dirname(current);
|
|
3645
4214
|
}
|
|
@@ -3719,7 +4288,7 @@ var FileRawEventInbox = class {
|
|
|
3719
4288
|
const existingName = parseInboxFileName(existingEntry.name);
|
|
3720
4289
|
if (entryContentId(parsedExisting.entry) !== id || existingName?.enqueueOrder !== parsedExisting.enqueueOrder) throw new Error("raw-event inbox entry conflict");
|
|
3721
4290
|
await chmod(join(this.directory, existingEntry.name), 384);
|
|
3722
|
-
await syncDirectory(this.directory);
|
|
4291
|
+
await syncDirectory$1(this.directory);
|
|
3723
4292
|
this.scheduleDrain();
|
|
3724
4293
|
return;
|
|
3725
4294
|
}
|
|
@@ -3745,7 +4314,7 @@ var FileRawEventInbox = class {
|
|
|
3745
4314
|
} finally {
|
|
3746
4315
|
await rm(temporary, { force: true }).catch(() => {});
|
|
3747
4316
|
}
|
|
3748
|
-
await syncDirectory(this.directory);
|
|
4317
|
+
await syncDirectory$1(this.directory);
|
|
3749
4318
|
this.scheduleDrain();
|
|
3750
4319
|
}
|
|
3751
4320
|
async allocateEnqueueOrder(entries) {
|
|
@@ -13596,6 +14165,7 @@ function readyContext(options, groupId, coordinatorUrl, raw, groups) {
|
|
|
13596
14165
|
groupId,
|
|
13597
14166
|
deviceId: device.deviceId,
|
|
13598
14167
|
fingerprint: device.fingerprint,
|
|
14168
|
+
publicKeyHash: createHash("sha256").update(device.publicKey).digest("hex"),
|
|
13599
14169
|
identityId: actor,
|
|
13600
14170
|
identityLabel: label(profile.identityLabel),
|
|
13601
14171
|
deviceLabel: label(profile.deviceLabel),
|
|
@@ -13675,6 +14245,2052 @@ function createAccountContextReader(options) {
|
|
|
13675
14245
|
};
|
|
13676
14246
|
}
|
|
13677
14247
|
//#endregion
|
|
14248
|
+
//#region ../core/src/coordinator-ed25519-key-id.ts
|
|
14249
|
+
function decodeWire(encoded) {
|
|
14250
|
+
try {
|
|
14251
|
+
const binary = atob(encoded);
|
|
14252
|
+
return Uint8Array.from(binary, (character) => character.charCodeAt(0));
|
|
14253
|
+
} catch {
|
|
14254
|
+
return null;
|
|
14255
|
+
}
|
|
14256
|
+
}
|
|
14257
|
+
function readRawKey(wire) {
|
|
14258
|
+
if (wire.length < 4) return null;
|
|
14259
|
+
const view = new DataView(wire.buffer, wire.byteOffset, wire.byteLength);
|
|
14260
|
+
const typeEnd = 4 + view.getUint32(0);
|
|
14261
|
+
if (wire.length < typeEnd + 4) return null;
|
|
14262
|
+
const keyLength = view.getUint32(typeEnd);
|
|
14263
|
+
const keyStart = typeEnd + 4;
|
|
14264
|
+
if (keyLength !== 32 || wire.length < keyStart + keyLength) return null;
|
|
14265
|
+
return wire.subarray(keyStart, keyStart + keyLength);
|
|
14266
|
+
}
|
|
14267
|
+
function canonicalBlob(rawKey) {
|
|
14268
|
+
const blob = /* @__PURE__ */ new Uint8Array(51);
|
|
14269
|
+
const view = new DataView(blob.buffer);
|
|
14270
|
+
view.setUint32(0, 11);
|
|
14271
|
+
blob.set(new TextEncoder().encode("ssh-ed25519"), 4);
|
|
14272
|
+
view.setUint32(15, 32);
|
|
14273
|
+
blob.set(rawKey, 19);
|
|
14274
|
+
return blob;
|
|
14275
|
+
}
|
|
14276
|
+
/**
|
|
14277
|
+
* Internal string contract, not an authentication acceptance check. Like the
|
|
14278
|
+
* Worker verifier, skip the inner type bytes and ignore trailing wire bytes.
|
|
14279
|
+
* atob rejects some encodings accepted by Node's Buffer base64 decoder.
|
|
14280
|
+
*/
|
|
14281
|
+
function parseSshEd25519PublicKey(publicKey) {
|
|
14282
|
+
return parseSshEd25519PublicKeyWithDecoder(publicKey, decodeWire);
|
|
14283
|
+
}
|
|
14284
|
+
/** Internal trusted decoder seam; callers must not use it to decide authentication acceptance. */
|
|
14285
|
+
function parseSshEd25519PublicKeyWithDecoder(publicKey, decode) {
|
|
14286
|
+
if (typeof publicKey !== "string") return { kind: "other" };
|
|
14287
|
+
const [keyType, keyData] = publicKey.trim().split(/\s+/);
|
|
14288
|
+
if (keyType !== "ssh-ed25519") return { kind: "other" };
|
|
14289
|
+
if (!keyData) return { kind: "malformed_ed25519" };
|
|
14290
|
+
let wire;
|
|
14291
|
+
try {
|
|
14292
|
+
wire = decode(keyData);
|
|
14293
|
+
} catch {
|
|
14294
|
+
return { kind: "malformed_ed25519" };
|
|
14295
|
+
}
|
|
14296
|
+
if (!wire) return { kind: "malformed_ed25519" };
|
|
14297
|
+
const rawKey = readRawKey(wire);
|
|
14298
|
+
if (!rawKey) return { kind: "malformed_ed25519" };
|
|
14299
|
+
return {
|
|
14300
|
+
kind: "ed25519",
|
|
14301
|
+
blob: canonicalBlob(rawKey)
|
|
14302
|
+
};
|
|
14303
|
+
}
|
|
14304
|
+
//#endregion
|
|
14305
|
+
//#region ../core/src/coordinator-auth-controller-history.ts
|
|
14306
|
+
var MAX_BYTES = 75e4;
|
|
14307
|
+
var CONTROLLER_HISTORY_CTE = `history_input AS MATERIALIZED (
|
|
14308
|
+
SELECT json_extract(value, '$.legacyHistory') AS value FROM snapshot
|
|
14309
|
+
), history_key_sources AS NOT MATERIALIZED (
|
|
14310
|
+
SELECT public_key, fingerprint FROM enrolled_devices
|
|
14311
|
+
UNION
|
|
14312
|
+
SELECT a.public_key, a.fingerprint FROM coordinator_auth_controller_attestations a
|
|
14313
|
+
CROSS JOIN history_input h
|
|
14314
|
+
WHERE a.coordinator_id = json_extract(h.value, '$.coordinatorId')
|
|
14315
|
+
), history_rows AS NOT MATERIALIZED (
|
|
14316
|
+
SELECT c.*, CASE WHEN json_valid(c.manifest_json) THEN c.manifest_json ELSE '{}' END AS parsed
|
|
14317
|
+
FROM coordinator_legacy_team_completions c
|
|
14318
|
+
), live_history AS MATERIALIZED (
|
|
14319
|
+
SELECT c.group_id, c.candidate_ref, c.manifest_version, c.completed_at, c.created_at,
|
|
14320
|
+
CASE WHEN length(CAST(c.manifest_json AS BLOB)) <= ${MAX_BYTES} THEN c.manifest_json ELSE NULL END AS manifest_json
|
|
14321
|
+
FROM history_rows c CROSS JOIN history_input h
|
|
14322
|
+
WHERE h.value IS NOT NULL
|
|
14323
|
+
AND (json_extract(c.parsed, '$.coordinator_id') = json_extract(h.value, '$.coordinatorId')
|
|
14324
|
+
OR json_extract(c.parsed, '$.coordinator_id') IS NULL)
|
|
14325
|
+
AND (EXISTS (
|
|
14326
|
+
SELECT 1 FROM json_each(c.parsed, '$.device_decisions') d
|
|
14327
|
+
WHERE json_extract(CASE WHEN d.type = 'object' THEN d.value ELSE '{}' END, '$.device_id') = json_extract(h.value, '$.deviceId')
|
|
14328
|
+
OR json_extract(CASE WHEN d.type = 'object' THEN d.value ELSE '{}' END, '$.key_fingerprint')
|
|
14329
|
+
IN (SELECT value FROM json_each(h.value, '$.fingerprints'))
|
|
14330
|
+
OR ((d.type <> 'object'
|
|
14331
|
+
OR json_type(CASE WHEN d.type = 'object' THEN d.value ELSE '{}' END, '$.device_id') IS NOT 'text'
|
|
14332
|
+
OR json_type(CASE WHEN d.type = 'object' THEN d.value ELSE '{}' END, '$.key_fingerprint') IS NOT 'text')
|
|
14333
|
+
AND (instr(CASE WHEN d.type = 'text' THEN json_quote(d.value) ELSE d.value END,
|
|
14334
|
+
json_quote(json_extract(h.value, '$.deviceId'))) > 0
|
|
14335
|
+
OR EXISTS (SELECT 1 FROM json_each(h.value, '$.fingerprints') f
|
|
14336
|
+
WHERE instr(d.value, f.value) > 0)))
|
|
14337
|
+
) OR ((json_valid(c.manifest_json) = 0 OR json_type(c.parsed, '$.device_decisions') IS NOT 'array') AND (
|
|
14338
|
+
instr(c.manifest_json, json_quote(json_extract(h.value, '$.deviceId'))) > 0
|
|
14339
|
+
OR EXISTS (SELECT 1 FROM json_each(h.value, '$.fingerprints') f
|
|
14340
|
+
WHERE instr(c.manifest_json, f.value) > 0)
|
|
14341
|
+
))) LIMIT 501
|
|
14342
|
+
), snapshot_history AS MATERIALIZED (
|
|
14343
|
+
SELECT json_extract(j.value, '$.group_id') AS group_id,
|
|
14344
|
+
json_extract(j.value, '$.candidate_ref') AS candidate_ref,
|
|
14345
|
+
json_extract(j.value, '$.manifest_version') AS manifest_version,
|
|
14346
|
+
json_extract(j.value, '$.completed_at') AS completed_at,
|
|
14347
|
+
json_extract(j.value, '$.created_at') AS created_at,
|
|
14348
|
+
json_extract(j.value, '$.manifest_json') AS manifest_json
|
|
14349
|
+
FROM history_input h, json_each(h.value, '$.records') j
|
|
14350
|
+
), snapshot_history_keys AS MATERIALIZED (
|
|
14351
|
+
SELECT json_extract(j.value, '$.public_key') AS public_key,
|
|
14352
|
+
json_extract(j.value, '$.fingerprint') AS fingerprint
|
|
14353
|
+
FROM history_input h, json_each(h.value, '$.keys') j
|
|
14354
|
+
), snapshot_discovery_keys AS MATERIALIZED (
|
|
14355
|
+
SELECT j.value AS public_key FROM history_input h, json_each(h.value, '$.discoveryKeys') j
|
|
14356
|
+
), discovery_lines AS NOT MATERIALIZED (
|
|
14357
|
+
SELECT public_key,
|
|
14358
|
+
ltrim(replace(replace(replace(replace(replace(public_key,
|
|
14359
|
+
char(9), ' '), char(10), ' '), char(11), ' '), char(12), ' '), char(13), ' '), ' ') AS line
|
|
14360
|
+
FROM history_key_sources
|
|
14361
|
+
), discovery_tails AS NOT MATERIALIZED (
|
|
14362
|
+
SELECT public_key, CASE WHEN substr(line, 1, 12) = 'ssh-ed25519 '
|
|
14363
|
+
THEN ltrim(substr(line, 13), ' ') ELSE NULL END AS tail FROM discovery_lines
|
|
14364
|
+
), discovery_tokens AS NOT MATERIALIZED (
|
|
14365
|
+
SELECT public_key, CASE WHEN tail IS NOT NULL
|
|
14366
|
+
THEN substr(tail, 1, instr(tail || ' ', ' ') - 1) ELSE NULL END AS token
|
|
14367
|
+
FROM discovery_tails
|
|
14368
|
+
), live_discovery_keys AS MATERIALIZED (
|
|
14369
|
+
SELECT DISTINCT e.public_key FROM discovery_tokens e CROSS JOIN history_input h
|
|
14370
|
+
WHERE e.public_key = json_extract(h.value, '$.publicKey')
|
|
14371
|
+
OR instr(e.public_key, char(0)) > 0
|
|
14372
|
+
OR e.token IS NULL
|
|
14373
|
+
OR EXISTS (SELECT 1 FROM json_each(h.value, '$.discoveryFragments') f
|
|
14374
|
+
WHERE instr(replace(replace(e.token, '-', '+'), '_', '/'), f.value) > 0)
|
|
14375
|
+
OR e.token GLOB '*[^A-Za-z0-9+/=_-]*'
|
|
14376
|
+
LIMIT 8193
|
|
14377
|
+
), live_history_keys AS MATERIALIZED (
|
|
14378
|
+
SELECT DISTINCT e.public_key, e.fingerprint FROM history_key_sources e
|
|
14379
|
+
WHERE e.public_key IN (SELECT public_key FROM snapshot_history_keys)
|
|
14380
|
+
LIMIT 4097
|
|
14381
|
+
)`;
|
|
14382
|
+
`${CONTROLLER_HISTORY_CTE}`;
|
|
14383
|
+
`${CONTROLLER_HISTORY_CTE}`;
|
|
14384
|
+
`${CONTROLLER_HISTORY_CTE}${MAX_BYTES}`;
|
|
14385
|
+
var CONTROLLER_HISTORY_GUARD = `AND (
|
|
14386
|
+
(SELECT value FROM history_input) IS NULL OR (
|
|
14387
|
+
json_extract((SELECT value FROM history_input), '$.deviceId') = e.device_id
|
|
14388
|
+
AND json_extract((SELECT value FROM history_input), '$.publicKey') = e.public_key
|
|
14389
|
+
AND json_extract((SELECT value FROM history_input), '$.fingerprint') = e.fingerprint
|
|
14390
|
+
AND (SELECT count(*) FROM live_history) = (SELECT count(*) FROM snapshot_history)
|
|
14391
|
+
AND NOT EXISTS (SELECT * FROM live_history EXCEPT SELECT * FROM snapshot_history)
|
|
14392
|
+
AND NOT EXISTS (SELECT * FROM live_history_keys EXCEPT SELECT * FROM snapshot_history_keys)
|
|
14393
|
+
AND NOT EXISTS (SELECT * FROM snapshot_history_keys EXCEPT SELECT * FROM live_history_keys)
|
|
14394
|
+
AND NOT EXISTS (SELECT * FROM live_discovery_keys EXCEPT SELECT * FROM snapshot_discovery_keys)
|
|
14395
|
+
AND NOT EXISTS (SELECT * FROM snapshot_discovery_keys EXCEPT SELECT * FROM live_discovery_keys)
|
|
14396
|
+
))`;
|
|
14397
|
+
//#endregion
|
|
14398
|
+
//#region ../core/src/coordinator-account-connection-storage.ts
|
|
14399
|
+
/**
|
|
14400
|
+
* Storage constraints, not an unlink/relink authorization API.
|
|
14401
|
+
* The explicit account-schema migration installs this DDL. Management writers
|
|
14402
|
+
* compose the winning proof, archive move and successor CAS in one backend.batch;
|
|
14403
|
+
* an Identity identifier alone never authorizes these effects.
|
|
14404
|
+
*/
|
|
14405
|
+
var CONNECTION_BIRTH_COLUMNS = [
|
|
14406
|
+
"coordinator_id",
|
|
14407
|
+
"link_id",
|
|
14408
|
+
"issuer",
|
|
14409
|
+
"subject",
|
|
14410
|
+
"identity_id",
|
|
14411
|
+
"attempt_id",
|
|
14412
|
+
"controller_attestation_id",
|
|
14413
|
+
"auth_config_revision",
|
|
14414
|
+
"created_at_ms",
|
|
14415
|
+
"revoked_at_ms",
|
|
14416
|
+
"authority_kind",
|
|
14417
|
+
"device_claim_id"
|
|
14418
|
+
];
|
|
14419
|
+
var current = "coordinator_auth_account_links";
|
|
14420
|
+
var history = "coordinator_auth_account_connection_history";
|
|
14421
|
+
var orders = "coordinator_auth_account_connection_orders";
|
|
14422
|
+
var birthEqual = CONNECTION_BIRTH_COLUMNS.map((c) => `c.${c} IS NEW.${c}`).join(" AND ");
|
|
14423
|
+
var immutableBirth = CONNECTION_BIRTH_COLUMNS.filter((c) => c !== "revoked_at_ms").map((c) => `NEW.${c} IS NOT OLD.${c}`).join(" OR ");
|
|
14424
|
+
`${CONNECTION_BIRTH_COLUMNS.map((column) => `h.${column} IS c.${column}`).join(" AND ")}`;
|
|
14425
|
+
`${current}${history}`;
|
|
14426
|
+
/** Alias l must be the pinned current target, never a caller-selected Identity. */
|
|
14427
|
+
var CONNECTION_NOT_REVOKED_SQL = `l.revoked_at_ms IS NULL AND NOT EXISTS (
|
|
14428
|
+
SELECT 1 FROM ${history} h WHERE h.coordinator_id = l.coordinator_id
|
|
14429
|
+
AND h.identity_id = l.identity_id AND h.revoked_at_ms IS NOT NULL)`;
|
|
14430
|
+
`${history}${current}${history}`;
|
|
14431
|
+
`${orders}`;
|
|
14432
|
+
`${orders}`;
|
|
14433
|
+
`${history}`, `${history}${history}${history}${history}${current}${birthEqual}`, `${history}`, `${history}${immutableBirth}${history}${current}${history}`, `${history}`, `${current}${history}`, `${history}`, `${orders}`, `${orders}${orders}`, `${orders}`, `${orders}`;
|
|
14434
|
+
`${CONNECTION_BIRTH_COLUMNS.join(",")}${CONNECTION_BIRTH_COLUMNS.join(",")}`;
|
|
14435
|
+
function rootBirths(coordinator, link) {
|
|
14436
|
+
return `SELECT ${CONNECTION_BIRTH_COLUMNS.join(",")} FROM coordinator_auth_account_links WHERE coordinator_id = ${coordinator} AND link_id = ${link}
|
|
14437
|
+
UNION ALL SELECT ${CONNECTION_BIRTH_COLUMNS.join(",")} FROM coordinator_auth_account_connection_history WHERE coordinator_id = ${coordinator} AND link_id = ${link}`;
|
|
14438
|
+
}
|
|
14439
|
+
/** Trusted SQL aliases only. Historical ownership is independent of current enrollment.
|
|
14440
|
+
* The immutable proof's key ID was derived at commit. Signed-device receipts also
|
|
14441
|
+
* pass a freshly derived canonical key ID; account sessions need no current key.
|
|
14442
|
+
* Link revocation is deliberately checked by the caller, including post-revoke audit.
|
|
14443
|
+
*/
|
|
14444
|
+
function authDeviceClaimProvenanceSql(options) {
|
|
14445
|
+
if (options.connections) return connectionClaimProvenance(options);
|
|
14446
|
+
return genesisClaimProvenance(options.link, "coordinator_auth_account_links", options.canonicalKeyId);
|
|
14447
|
+
}
|
|
14448
|
+
function genesisClaimProvenance(link, births, canonicalKeyId) {
|
|
14449
|
+
const l = "pl";
|
|
14450
|
+
return `EXISTS (WITH committed_claim AS MATERIALIZED (
|
|
14451
|
+
SELECT ct.coordinator_id, ct.link_id, cd.key_id FROM coordinator_auth_link_attempts ct
|
|
14452
|
+
JOIN ${births} pl ON pl.coordinator_id = ct.coordinator_id AND pl.link_id = ct.link_id
|
|
14453
|
+
JOIN coordinator_auth_device_claims cd ON cd.coordinator_id = ct.coordinator_id AND cd.claim_id = ${l}.device_claim_id
|
|
14454
|
+
JOIN coordinator_auth_link_audit_log ca ON ca.coordinator_id = ct.coordinator_id AND ca.link_id = ct.link_id AND ca.action = 'link_created'
|
|
14455
|
+
WHERE ct.coordinator_id = ${link}.coordinator_id AND ct.link_id = ${link}.link_id
|
|
14456
|
+
AND ct.attempt_id = ${l}.attempt_id
|
|
14457
|
+
AND ct.authority_kind = 'device_claim' AND ct.controller_attestation_id IS NULL
|
|
14458
|
+
AND ct.controller_review_receipt_id IS NULL AND ct.controller_revision IS NULL AND ct.state IN ('finalized','session_redeemed')
|
|
14459
|
+
AND ${l}.authority_kind = 'device_claim' AND ${l}.controller_attestation_id IS NULL
|
|
14460
|
+
AND ct.identity_id = ${l}.identity_id AND ct.issuer = ${l}.issuer AND ct.account_subject = ${l}.subject
|
|
14461
|
+
AND ct.auth_config_revision = ${l}.auth_config_revision AND ct.finalized_at_ms = ${l}.created_at_ms
|
|
14462
|
+
AND ct.browser_transaction_hash IS NOT NULL AND ct.browser_start_hash IS NOT NULL
|
|
14463
|
+
AND ct.runtime_verifier_hash IS NOT NULL AND ct.completion_secret_hash IS NOT NULL
|
|
14464
|
+
AND ct.created_at_ms <= ct.claimed_at_ms AND ct.claimed_at_ms <= ct.oidc_verified_at_ms
|
|
14465
|
+
AND ct.oidc_verified_at_ms <= ct.confirmed_at_ms AND ct.confirmed_at_ms <= ct.finalized_at_ms AND ct.finalized_at_ms < ct.expires_at_ms
|
|
14466
|
+
AND cd.attempt_id = ct.attempt_id AND cd.identity_id = ct.identity_id AND cd.group_id = ct.group_id AND cd.device_id = ct.device_id
|
|
14467
|
+
AND cd.public_key = ct.public_key AND cd.fingerprint = ct.fingerprint
|
|
14468
|
+
AND cd.issuer = ct.issuer AND cd.auth_config_revision = ct.auth_config_revision AND cd.created_at_ms = ct.finalized_at_ms
|
|
14469
|
+
AND ca.authority_kind = 'device_claim' AND ca.controller_attestation_id IS NULL AND ca.device_claim_id IS ${l}.device_claim_id
|
|
14470
|
+
AND ca.attempt_id = ct.attempt_id AND ca.identity_id = ct.identity_id AND ca.group_id = ct.group_id AND ca.device_id = ct.device_id
|
|
14471
|
+
AND ca.fingerprint = ct.fingerprint AND ca.auth_config_revision = ct.auth_config_revision AND ca.created_at_ms = ct.finalized_at_ms)
|
|
14472
|
+
SELECT 1 FROM committed_claim cp WHERE cp.coordinator_id = ${link}.coordinator_id AND cp.link_id = ${link}.link_id
|
|
14473
|
+
${canonicalKeyId ? `AND cp.key_id = ${canonicalKeyId}` : ""})`;
|
|
14474
|
+
}
|
|
14475
|
+
function connectionClaimProvenance(options) {
|
|
14476
|
+
const l = options.link;
|
|
14477
|
+
return `EXISTS (WITH connection_births AS MATERIALIZED (${rootBirths(`${l}.coordinator_id`, `${l}.device_claim_id`)}), continuation AS MATERIALIZED (
|
|
14478
|
+
SELECT t.coordinator_id,t.link_id,d.key_id FROM coordinator_auth_link_attempts t
|
|
14479
|
+
JOIN coordinator_auth_link_audit_log a ON a.coordinator_id = t.coordinator_id AND a.link_id = t.link_id AND a.action = 'link_created'
|
|
14480
|
+
JOIN coordinator_auth_device_claims d ON d.coordinator_id = t.coordinator_id AND d.claim_id = ${l}.device_claim_id
|
|
14481
|
+
WHERE t.coordinator_id = ${l}.coordinator_id AND t.link_id = ${l}.link_id AND t.attempt_id = ${l}.attempt_id
|
|
14482
|
+
AND (${l}.authority_kind = 'device_claim' AND ${l}.controller_attestation_id IS NULL AND t.authority_kind = 'device_claim' AND t.controller_attestation_id IS NULL AND t.controller_review_receipt_id IS NULL AND t.controller_revision IS NULL)
|
|
14483
|
+
AND (t.state IN ('finalized','session_redeemed') AND t.identity_id = ${l}.identity_id AND t.issuer = ${l}.issuer AND t.account_subject = ${l}.subject AND t.auth_config_revision = ${l}.auth_config_revision AND t.finalized_at_ms = ${l}.created_at_ms)
|
|
14484
|
+
AND (t.browser_start_hash IS NOT NULL AND t.browser_transaction_hash IS NOT NULL AND t.runtime_verifier_hash IS NOT NULL AND t.completion_secret_hash IS NOT NULL AND t.created_at_ms <= t.claimed_at_ms AND t.claimed_at_ms <= t.oidc_verified_at_ms AND t.oidc_verified_at_ms <= t.confirmed_at_ms AND t.confirmed_at_ms <= t.finalized_at_ms AND t.finalized_at_ms < t.expires_at_ms)
|
|
14485
|
+
AND (d.identity_id = t.identity_id AND d.issuer = t.issuer AND d.group_id = t.group_id AND d.device_id = t.device_id AND d.public_key = t.public_key AND d.fingerprint = t.fingerprint)
|
|
14486
|
+
AND (a.attempt_id = t.attempt_id AND a.identity_id = t.identity_id AND a.group_id = t.group_id AND a.device_id = t.device_id AND a.fingerprint = t.fingerprint AND a.auth_config_revision = t.auth_config_revision AND a.created_at_ms = t.finalized_at_ms AND a.authority_kind = 'device_claim' AND a.controller_attestation_id IS NULL AND a.device_claim_id = d.claim_id)
|
|
14487
|
+
AND ((t.prior_link_id IS NULL AND t.link_id = d.claim_id) OR EXISTS (SELECT 1 FROM coordinator_auth_account_connection_history h WHERE h.coordinator_id = t.coordinator_id AND h.link_id = t.prior_link_id AND h.identity_id = t.identity_id AND h.successor_link_id = t.link_id))
|
|
14488
|
+
AND EXISTS (SELECT 1 FROM connection_births gl WHERE gl.coordinator_id = d.coordinator_id AND gl.link_id = d.claim_id AND ${genesisClaimProvenance("gl", "connection_births")}))
|
|
14489
|
+
SELECT 1 FROM continuation cp ${options.canonicalKeyId ? "WHERE cp.key_id = ?" : ""})`;
|
|
14490
|
+
}
|
|
14491
|
+
/** Account management only; privileged enrollment/grant proofs remain attested-only. */
|
|
14492
|
+
function controllerBirthProvenance(link) {
|
|
14493
|
+
return `EXISTS (WITH controller_receipt AS MATERIALIZED (SELECT t.coordinator_id,t.link_id FROM coordinator_auth_link_attempts t
|
|
14494
|
+
JOIN coordinator_auth_controller_attestations origin ON origin.coordinator_id=t.coordinator_id AND origin.attestation_id=t.controller_attestation_id
|
|
14495
|
+
JOIN coordinator_auth_link_audit_log a ON a.coordinator_id=t.coordinator_id AND a.link_id=t.link_id AND a.action='link_created'
|
|
14496
|
+
WHERE (t.coordinator_id=${link}.coordinator_id AND t.link_id=${link}.link_id AND t.attempt_id=${link}.attempt_id AND t.authority_kind='controller_attestation' AND t.controller_attestation_id=${link}.controller_attestation_id)
|
|
14497
|
+
AND (t.identity_id=${link}.identity_id AND t.issuer=${link}.issuer AND t.account_subject=${link}.subject AND t.auth_config_revision=${link}.auth_config_revision AND t.finalized_at_ms=${link}.created_at_ms AND t.state IN ('finalized','session_redeemed'))
|
|
14498
|
+
AND (t.browser_transaction_hash IS NOT NULL AND t.runtime_verifier_hash IS NOT NULL AND t.completion_secret_hash IS NOT NULL AND t.created_at_ms<=t.claimed_at_ms AND t.claimed_at_ms<=t.oidc_verified_at_ms AND t.oidc_verified_at_ms<=t.confirmed_at_ms AND t.confirmed_at_ms<=t.finalized_at_ms AND t.finalized_at_ms<t.expires_at_ms)
|
|
14499
|
+
AND (origin.identity_id=t.identity_id AND origin.group_id=t.group_id AND origin.device_id=t.device_id AND origin.public_key=t.public_key AND origin.fingerprint=t.fingerprint AND origin.review_receipt_id=t.controller_review_receipt_id AND origin.revision=t.controller_revision)
|
|
14500
|
+
AND (t.prior_link_id IS NULL OR EXISTS (SELECT 1 FROM coordinator_auth_account_connection_history h WHERE h.coordinator_id=t.coordinator_id AND h.identity_id=t.identity_id AND h.link_id=t.prior_link_id AND h.successor_link_id=t.link_id))
|
|
14501
|
+
AND (a.attempt_id=t.attempt_id AND a.identity_id=t.identity_id AND a.group_id=t.group_id AND a.device_id=t.device_id AND a.fingerprint=t.fingerprint AND a.controller_attestation_id=t.controller_attestation_id AND a.authority_kind=t.authority_kind AND a.device_claim_id IS NULL AND a.auth_config_revision=t.auth_config_revision AND a.created_at_ms=t.finalized_at_ms)) SELECT 1 FROM controller_receipt)`;
|
|
14502
|
+
}
|
|
14503
|
+
function authAccountLinkProvenanceSql(link, options = {}) {
|
|
14504
|
+
return `((${link}.authority_kind = 'controller_attestation' AND ${link}.controller_attestation_id IS NOT NULL AND ${link}.device_claim_id IS NULL${options.connections ? ` AND ${controllerBirthProvenance(link)}` : ""})
|
|
14505
|
+
OR (${authDeviceClaimProvenanceSql({
|
|
14506
|
+
link,
|
|
14507
|
+
connections: options.connections
|
|
14508
|
+
})}))`;
|
|
14509
|
+
}
|
|
14510
|
+
`${CONNECTION_NOT_REVOKED_SQL}${authAccountLinkProvenanceSql("l", { connections: true })}`;
|
|
14511
|
+
//#endregion
|
|
14512
|
+
//#region ../core/src/coordinator-auth-controller.ts
|
|
14513
|
+
function isAuthControllerId(value) {
|
|
14514
|
+
return typeof value === "string" && value.length > 0 && value.length <= 256 && value.trim() === value && !/[\p{Cc}\p{Cf}\p{Cs}]/u.test(value);
|
|
14515
|
+
}
|
|
14516
|
+
var AUTH_CONTROLLER_SNAPSHOT_CTE = `
|
|
14517
|
+
WITH snapshot(value) AS (SELECT ?), snapshot_invites AS MATERIALIZED (
|
|
14518
|
+
SELECT json_extract(j.value, '$.inviteId') AS invite_id,
|
|
14519
|
+
json_extract(j.value, '$.kind') AS invite_kind,
|
|
14520
|
+
json_extract(j.value, '$.actorId') AS recipient_actor_id,
|
|
14521
|
+
json_extract(j.value, '$.assignedIdentityId') AS assigned_identity_id,
|
|
14522
|
+
json_extract(j.value, '$.targetIdentityId') AS target_identity_id,
|
|
14523
|
+
json_extract(j.value, '$.digest') AS reviewed_preview_digest
|
|
14524
|
+
FROM json_each((SELECT value FROM snapshot), '$.invites') j
|
|
14525
|
+
), live_invites AS MATERIALIZED (
|
|
14526
|
+
SELECT i.invite_id, i.invite_kind, i.recipient_actor_id, i.assigned_identity_id,
|
|
14527
|
+
i.target_identity_id, i.reviewed_preview_digest FROM coordinator_invites i
|
|
14528
|
+
WHERE i.group_id = ? AND i.bound_device_id = ? AND i.bound_public_key = ?
|
|
14529
|
+
AND i.bound_fingerprint = ? AND i.revoked_at IS NULL
|
|
14530
|
+
AND i.consumed_at IS NOT NULL AND i.consumed_at != ''
|
|
14531
|
+
AND i.invite_kind IN ('team_member', 'add_device')
|
|
14532
|
+
), ${CONTROLLER_HISTORY_CTE}`;
|
|
14533
|
+
var AUTH_CONTROLLER_SNAPSHOT_GUARD = `
|
|
14534
|
+
AND ((SELECT value FROM snapshot) IS NULL OR (
|
|
14535
|
+
e.identity_id IS json_extract((SELECT value FROM snapshot), '$.enrollmentIdentityId')
|
|
14536
|
+
AND (SELECT count(*) FROM live_invites) =
|
|
14537
|
+
(SELECT count(*) FROM snapshot_invites)
|
|
14538
|
+
AND NOT EXISTS (
|
|
14539
|
+
SELECT * FROM live_invites
|
|
14540
|
+
EXCEPT
|
|
14541
|
+
SELECT * FROM snapshot_invites
|
|
14542
|
+
)
|
|
14543
|
+
)) ${CONTROLLER_HISTORY_GUARD}`;
|
|
14544
|
+
var AUTH_CONTROLLER_AUTHORITY_GUARD = `
|
|
14545
|
+
AND e.group_id = ? AND e.device_id = ? AND e.public_key = ?
|
|
14546
|
+
AND e.fingerprint = ? AND e.identity_id IS ?
|
|
14547
|
+
AND NOT EXISTS (SELECT 1 FROM coordinator_device_revocations WHERE
|
|
14548
|
+
(subject_kind = 'device_id' AND subject_value = ?) OR
|
|
14549
|
+
(subject_kind = 'ed25519_key' AND subject_value = ?))`;
|
|
14550
|
+
`${AUTH_CONTROLLER_SNAPSHOT_CTE}${AUTH_CONTROLLER_SNAPSHOT_GUARD}${AUTH_CONTROLLER_AUTHORITY_GUARD}`;
|
|
14551
|
+
`${AUTH_CONTROLLER_SNAPSHOT_CTE}${AUTH_CONTROLLER_SNAPSHOT_GUARD}${AUTH_CONTROLLER_AUTHORITY_GUARD}`;
|
|
14552
|
+
var AUTH_CONTROLLER_CURRENT_SQL = `
|
|
14553
|
+
SELECT a.* FROM coordinator_auth_controller_attestations a
|
|
14554
|
+
JOIN enrolled_devices e ON e.group_id = a.group_id AND e.device_id = a.device_id
|
|
14555
|
+
JOIN groups g ON g.group_id = a.group_id
|
|
14556
|
+
WHERE a.coordinator_id = ? AND a.attestation_id = ? AND a.revoked_at IS NULL
|
|
14557
|
+
AND e.enabled = 1 AND g.archived_at IS NULL AND e.public_key = a.public_key
|
|
14558
|
+
AND e.fingerprint = a.fingerprint
|
|
14559
|
+
AND (e.identity_id IS NULL OR e.identity_id = a.identity_id)`;
|
|
14560
|
+
AUTH_CONTROLLER_CURRENT_SQL.replace("SELECT a.*", "SELECT e.*");
|
|
14561
|
+
`${AUTH_CONTROLLER_SNAPSHOT_CTE}`, `${AUTH_CONTROLLER_CURRENT_SQL}${AUTH_CONTROLLER_AUTHORITY_GUARD}`, `${AUTH_CONTROLLER_SNAPSHOT_GUARD}`;
|
|
14562
|
+
function ownerProfileConflict() {
|
|
14563
|
+
throw new Error("Owner enrollment stopped for review. Keep the profile and saved request unchanged; no identity or access repair is automatic.");
|
|
14564
|
+
}
|
|
14565
|
+
[{
|
|
14566
|
+
name: "trg_identity_devices_assignment_version",
|
|
14567
|
+
create: `CREATE TRIGGER trg_identity_devices_assignment_version
|
|
14568
|
+
AFTER UPDATE OF identity_id ON identity_devices
|
|
14569
|
+
WHEN NEW.identity_id <> OLD.identity_id
|
|
14570
|
+
BEGIN
|
|
14571
|
+
UPDATE identity_devices
|
|
14572
|
+
SET assignment_version = OLD.assignment_version + 1
|
|
14573
|
+
WHERE device_id = NEW.device_id;
|
|
14574
|
+
END`
|
|
14575
|
+
}, {
|
|
14576
|
+
name: "trg_identity_devices_purge_decisions",
|
|
14577
|
+
create: `CREATE TRIGGER trg_identity_devices_purge_decisions
|
|
14578
|
+
AFTER DELETE ON identity_devices
|
|
14579
|
+
BEGIN
|
|
14580
|
+
DELETE FROM policy_team_device_decisions WHERE device_id = OLD.device_id;
|
|
14581
|
+
END`
|
|
14582
|
+
}].map(({ name, create }) => `DROP TRIGGER IF EXISTS ${name};\n${create};`).join("\n");
|
|
14583
|
+
[
|
|
14584
|
+
[
|
|
14585
|
+
"insert",
|
|
14586
|
+
"AFTER INSERT",
|
|
14587
|
+
""
|
|
14588
|
+
],
|
|
14589
|
+
[
|
|
14590
|
+
"delete",
|
|
14591
|
+
"AFTER DELETE",
|
|
14592
|
+
""
|
|
14593
|
+
],
|
|
14594
|
+
[
|
|
14595
|
+
"update",
|
|
14596
|
+
"AFTER UPDATE",
|
|
14597
|
+
"WHEN OLD.id IS NOT NEW.id OR OLD.title IS NOT NEW.title OR OLD.body_text IS NOT NEW.body_text OR OLD.active IS NOT NEW.active"
|
|
14598
|
+
]
|
|
14599
|
+
].map(([suffix, event, condition]) => ({
|
|
14600
|
+
name: `vector_corpus_epoch_${suffix}`,
|
|
14601
|
+
sql: `CREATE TRIGGER vector_corpus_epoch_${suffix} ${event} ON memory_items ${condition}
|
|
14602
|
+
BEGIN UPDATE vector_corpus_epoch SET epoch = epoch + 1 WHERE id = 1; END`
|
|
14603
|
+
}));
|
|
14604
|
+
join(homedir(), ".codemem", "mem.sqlite");
|
|
14605
|
+
join(homedir(), ".codemem.sqlite"), join(homedir(), ".opencode-mem.sqlite");
|
|
14606
|
+
//#endregion
|
|
14607
|
+
//#region ../core/src/schema.ts
|
|
14608
|
+
/**
|
|
14609
|
+
* Drizzle ORM schema for the codemem SQLite database.
|
|
14610
|
+
*/
|
|
14611
|
+
var sessions = sqliteTable("sessions", {
|
|
14612
|
+
id: integer("id").primaryKey(),
|
|
14613
|
+
started_at: text("started_at").notNull(),
|
|
14614
|
+
ended_at: text("ended_at"),
|
|
14615
|
+
cwd: text("cwd"),
|
|
14616
|
+
project: text("project"),
|
|
14617
|
+
git_remote: text("git_remote"),
|
|
14618
|
+
git_branch: text("git_branch"),
|
|
14619
|
+
user: text("user"),
|
|
14620
|
+
tool_version: text("tool_version"),
|
|
14621
|
+
metadata_json: text("metadata_json"),
|
|
14622
|
+
import_key: text("import_key")
|
|
14623
|
+
}, (table) => [index("idx_sessions_started_id").on(table.started_at, table.id)]);
|
|
14624
|
+
sqliteTable("replication_scopes", {
|
|
14625
|
+
scope_id: text("scope_id").primaryKey(),
|
|
14626
|
+
label: text("label").notNull(),
|
|
14627
|
+
kind: text("kind").notNull().default("user"),
|
|
14628
|
+
authority_type: text("authority_type").notNull().default("local"),
|
|
14629
|
+
coordinator_id: text("coordinator_id"),
|
|
14630
|
+
group_id: text("group_id"),
|
|
14631
|
+
manifest_issuer_device_id: text("manifest_issuer_device_id"),
|
|
14632
|
+
membership_epoch: integer("membership_epoch").notNull().default(0),
|
|
14633
|
+
manifest_hash: text("manifest_hash"),
|
|
14634
|
+
status: text("status").notNull().default("active"),
|
|
14635
|
+
created_at: text("created_at").notNull(),
|
|
14636
|
+
updated_at: text("updated_at").notNull()
|
|
14637
|
+
}, (table) => [index("idx_replication_scopes_status").on(table.status), index("idx_replication_scopes_authority_group").on(table.coordinator_id, table.group_id)]);
|
|
14638
|
+
sqliteTable("project_scope_mappings", {
|
|
14639
|
+
id: integer("id").primaryKey(),
|
|
14640
|
+
workspace_identity: text("workspace_identity"),
|
|
14641
|
+
project_pattern: text("project_pattern").notNull(),
|
|
14642
|
+
scope_id: text("scope_id").notNull(),
|
|
14643
|
+
priority: integer("priority").notNull().default(0),
|
|
14644
|
+
source: text("source").notNull().default("user"),
|
|
14645
|
+
created_at: text("created_at").notNull(),
|
|
14646
|
+
updated_at: text("updated_at").notNull()
|
|
14647
|
+
}, (table) => [
|
|
14648
|
+
index("idx_project_scope_mappings_workspace_priority").on(table.workspace_identity, table.priority),
|
|
14649
|
+
index("idx_project_scope_mappings_pattern_priority").on(table.project_pattern, table.priority),
|
|
14650
|
+
index("idx_project_scope_mappings_scope").on(table.scope_id)
|
|
14651
|
+
]);
|
|
14652
|
+
sqliteTable("scope_memberships", {
|
|
14653
|
+
scope_id: text("scope_id").notNull(),
|
|
14654
|
+
device_id: text("device_id").notNull(),
|
|
14655
|
+
role: text("role").notNull().default("member"),
|
|
14656
|
+
status: text("status").notNull().default("active"),
|
|
14657
|
+
membership_epoch: integer("membership_epoch").notNull().default(0),
|
|
14658
|
+
coordinator_id: text("coordinator_id"),
|
|
14659
|
+
group_id: text("group_id"),
|
|
14660
|
+
manifest_issuer_device_id: text("manifest_issuer_device_id"),
|
|
14661
|
+
manifest_hash: text("manifest_hash"),
|
|
14662
|
+
signed_manifest_json: text("signed_manifest_json"),
|
|
14663
|
+
updated_at: text("updated_at").notNull()
|
|
14664
|
+
}, (table) => [
|
|
14665
|
+
primaryKey({ columns: [table.scope_id, table.device_id] }),
|
|
14666
|
+
index("idx_scope_memberships_device_status").on(table.device_id, table.status),
|
|
14667
|
+
index("idx_scope_memberships_scope_status").on(table.scope_id, table.status),
|
|
14668
|
+
index("idx_scope_memberships_authority_group").on(table.coordinator_id, table.group_id)
|
|
14669
|
+
]);
|
|
14670
|
+
sqliteTable("artifacts", {
|
|
14671
|
+
id: integer("id").primaryKey(),
|
|
14672
|
+
session_id: integer("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
14673
|
+
kind: text("kind").notNull(),
|
|
14674
|
+
path: text("path"),
|
|
14675
|
+
content_text: text("content_text"),
|
|
14676
|
+
content_hash: text("content_hash"),
|
|
14677
|
+
created_at: text("created_at").notNull(),
|
|
14678
|
+
metadata_json: text("metadata_json")
|
|
14679
|
+
}, (table) => [index("idx_artifacts_session_kind").on(table.session_id, table.kind)]);
|
|
14680
|
+
var memoryItems = sqliteTable("memory_items", {
|
|
14681
|
+
id: integer("id").primaryKey(),
|
|
14682
|
+
session_id: integer("session_id").notNull().references(() => sessions.id, { onDelete: "cascade" }),
|
|
14683
|
+
kind: text("kind").notNull(),
|
|
14684
|
+
title: text("title").notNull(),
|
|
14685
|
+
subtitle: text("subtitle"),
|
|
14686
|
+
body_text: text("body_text").notNull(),
|
|
14687
|
+
confidence: real("confidence").default(.5),
|
|
14688
|
+
tags_text: text("tags_text").default(""),
|
|
14689
|
+
active: integer("active").default(1),
|
|
14690
|
+
created_at: text("created_at").notNull(),
|
|
14691
|
+
updated_at: text("updated_at").notNull(),
|
|
14692
|
+
metadata_json: text("metadata_json"),
|
|
14693
|
+
actor_id: text("actor_id"),
|
|
14694
|
+
actor_display_name: text("actor_display_name"),
|
|
14695
|
+
visibility: text("visibility"),
|
|
14696
|
+
workspace_id: text("workspace_id"),
|
|
14697
|
+
workspace_kind: text("workspace_kind"),
|
|
14698
|
+
origin_device_id: text("origin_device_id"),
|
|
14699
|
+
origin_source: text("origin_source"),
|
|
14700
|
+
trust_state: text("trust_state"),
|
|
14701
|
+
facts: text("facts"),
|
|
14702
|
+
narrative: text("narrative"),
|
|
14703
|
+
concepts: text("concepts"),
|
|
14704
|
+
files_read: text("files_read"),
|
|
14705
|
+
files_modified: text("files_modified"),
|
|
14706
|
+
user_prompt_id: integer("user_prompt_id"),
|
|
14707
|
+
prompt_number: integer("prompt_number"),
|
|
14708
|
+
deleted_at: text("deleted_at"),
|
|
14709
|
+
rev: integer("rev").default(0),
|
|
14710
|
+
dedup_key: text("dedup_key"),
|
|
14711
|
+
import_key: text("import_key"),
|
|
14712
|
+
scope_id: text("scope_id"),
|
|
14713
|
+
project: text("project")
|
|
14714
|
+
}, (table) => [
|
|
14715
|
+
index("idx_memory_items_active_created").on(table.active, table.created_at),
|
|
14716
|
+
index("idx_memory_items_created_id").on(table.created_at, table.id),
|
|
14717
|
+
index("idx_memory_items_origin_device_active").on(table.origin_device_id, table.active),
|
|
14718
|
+
index("idx_memory_items_session").on(table.session_id),
|
|
14719
|
+
index("idx_memory_items_project").on(table.project),
|
|
14720
|
+
index("idx_memory_items_scope_visibility_created").on(table.scope_id, table.visibility, table.created_at),
|
|
14721
|
+
index("idx_memory_items_scope_backfill_pending").on(table.id).where(sql`scope_id IS NULL OR scope_id = ''`),
|
|
14722
|
+
index("idx_memory_items_dedup_key_active_created").on(table.dedup_key, table.active, table.created_at),
|
|
14723
|
+
uniqueIndex("idx_memory_items_same_session_dedup_unique").on(table.session_id, table.kind, table.visibility, table.workspace_id, table.dedup_key).where(sql`active = 1 AND dedup_key IS NOT NULL`)
|
|
14724
|
+
]);
|
|
14725
|
+
sqliteTable("memory_file_refs", {
|
|
14726
|
+
memory_id: integer("memory_id").notNull().references(() => memoryItems.id, { onDelete: "cascade" }),
|
|
14727
|
+
file_path: text("file_path").notNull(),
|
|
14728
|
+
relation: text("relation").notNull()
|
|
14729
|
+
}, (table) => [primaryKey({ columns: [
|
|
14730
|
+
table.memory_id,
|
|
14731
|
+
table.file_path,
|
|
14732
|
+
table.relation
|
|
14733
|
+
] }), index("idx_memory_file_refs_path").on(table.file_path)]);
|
|
14734
|
+
sqliteTable("memory_concept_refs", {
|
|
14735
|
+
memory_id: integer("memory_id").notNull().references(() => memoryItems.id, { onDelete: "cascade" }),
|
|
14736
|
+
concept: text("concept").notNull()
|
|
14737
|
+
}, (table) => [primaryKey({ columns: [table.memory_id, table.concept] }), index("idx_memory_concept_refs_concept").on(table.concept)]);
|
|
14738
|
+
sqliteTable("usage_events", {
|
|
14739
|
+
id: integer("id").primaryKey(),
|
|
14740
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "set null" }),
|
|
14741
|
+
event: text("event").notNull(),
|
|
14742
|
+
tokens_read: integer("tokens_read").default(0),
|
|
14743
|
+
tokens_written: integer("tokens_written").default(0),
|
|
14744
|
+
tokens_saved: integer("tokens_saved").default(0),
|
|
14745
|
+
created_at: text("created_at").notNull(),
|
|
14746
|
+
metadata_json: text("metadata_json")
|
|
14747
|
+
}, (table) => [index("idx_usage_events_event_created").on(table.event, table.created_at), index("idx_usage_events_session").on(table.session_id)]);
|
|
14748
|
+
var retrievalAttempts = sqliteTable("retrieval_attempts", {
|
|
14749
|
+
attempt_id: text("attempt_id").primaryKey(),
|
|
14750
|
+
contract_version: integer("contract_version").notNull(),
|
|
14751
|
+
surface: text("surface").notNull(),
|
|
14752
|
+
trigger: text("trigger").notNull(),
|
|
14753
|
+
started_at: text("started_at").notNull(),
|
|
14754
|
+
completed_at: text("completed_at"),
|
|
14755
|
+
retrieval_status: text("retrieval_status").notNull(),
|
|
14756
|
+
delivery_status: text("delivery_status").notNull(),
|
|
14757
|
+
candidate_count: integer("candidate_count").notNull(),
|
|
14758
|
+
selected_count: integer("selected_count").notNull(),
|
|
14759
|
+
persisted_candidate_count: integer("persisted_candidate_count").notNull(),
|
|
14760
|
+
recorder_version: text("recorder_version").notNull(),
|
|
14761
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "cascade" }),
|
|
14762
|
+
source: text("source"),
|
|
14763
|
+
stream_id: text("stream_id"),
|
|
14764
|
+
source_session_id: text("source_session_id"),
|
|
14765
|
+
prompt_number: integer("prompt_number"),
|
|
14766
|
+
request_id: text("request_id"),
|
|
14767
|
+
raw_event_start_seq: integer("raw_event_start_seq"),
|
|
14768
|
+
raw_event_end_seq: integer("raw_event_end_seq"),
|
|
14769
|
+
experiment_id: text("experiment_id"),
|
|
14770
|
+
experiment_cell_id: text("experiment_cell_id"),
|
|
14771
|
+
evaluation_checkout_id: text("evaluation_checkout_id"),
|
|
14772
|
+
evaluation_fixture_id: text("evaluation_fixture_id"),
|
|
14773
|
+
evaluation_seed: integer("evaluation_seed"),
|
|
14774
|
+
latency_ms: integer("latency_ms"),
|
|
14775
|
+
project: text("project"),
|
|
14776
|
+
scope_id: text("scope_id"),
|
|
14777
|
+
mode: text("mode"),
|
|
14778
|
+
limit_requested: integer("limit_requested"),
|
|
14779
|
+
token_budget: integer("token_budget"),
|
|
14780
|
+
output_tokens: integer("output_tokens"),
|
|
14781
|
+
working_set_file_count: integer("working_set_file_count"),
|
|
14782
|
+
working_set_files_json: text("working_set_files_json"),
|
|
14783
|
+
query_hash_sha256: text("query_hash_sha256"),
|
|
14784
|
+
query_char_count: integer("query_char_count"),
|
|
14785
|
+
query_token_estimate: integer("query_token_estimate"),
|
|
14786
|
+
filter_summary_json: text("filter_summary_json"),
|
|
14787
|
+
failure_code: text("failure_code"),
|
|
14788
|
+
failure_stage: text("failure_stage"),
|
|
14789
|
+
trace_version: integer("trace_version"),
|
|
14790
|
+
automatic_recall_json: text("automatic_recall_json"),
|
|
14791
|
+
automatic_recall_key: text("automatic_recall_key"),
|
|
14792
|
+
retention_until: text("retention_until"),
|
|
14793
|
+
retention_pinned: integer("retention_pinned").notNull().default(0),
|
|
14794
|
+
retention_finalized_at: text("retention_finalized_at")
|
|
14795
|
+
}, (table) => [
|
|
14796
|
+
index("idx_retrieval_attempts_session_started").on(table.session_id, table.started_at),
|
|
14797
|
+
uniqueIndex("idx_retrieval_attempts_automatic_recall_key").on(table.automatic_recall_key),
|
|
14798
|
+
index("idx_retrieval_attempts_source_stream_started").on(table.source, table.stream_id, table.started_at),
|
|
14799
|
+
index("idx_retrieval_attempts_retention").on(table.retention_pinned, table.retention_until),
|
|
14800
|
+
index("idx_retrieval_attempts_started").on(table.started_at, table.attempt_id),
|
|
14801
|
+
index("idx_retrieval_attempts_surface_started").on(table.surface, table.started_at),
|
|
14802
|
+
uniqueIndex("idx_retrieval_attempts_request_identity").on(table.source, table.surface, table.request_id).where(sql`request_id IS NOT NULL`),
|
|
14803
|
+
index("idx_retrieval_attempts_experiment_cell").on(table.experiment_id, table.experiment_cell_id)
|
|
14804
|
+
]);
|
|
14805
|
+
var retrievalExposures = sqliteTable("retrieval_exposures", {
|
|
14806
|
+
exposure_id: integer("exposure_id").primaryKey({ autoIncrement: true }),
|
|
14807
|
+
attempt_id: text("attempt_id").notNull().references(() => retrievalAttempts.attempt_id, { onDelete: "cascade" }),
|
|
14808
|
+
memory_id: integer("memory_id").references(() => memoryItems.id, { onDelete: "set null" }),
|
|
14809
|
+
memory_import_key: text("memory_import_key"),
|
|
14810
|
+
origin_device_id: text("origin_device_id"),
|
|
14811
|
+
rank: integer("rank").notNull(),
|
|
14812
|
+
disposition: text("disposition").notNull(),
|
|
14813
|
+
section: text("section"),
|
|
14814
|
+
handoff_status: text("handoff_status").notNull(),
|
|
14815
|
+
memory_rev: integer("memory_rev"),
|
|
14816
|
+
memory_updated_at: text("memory_updated_at"),
|
|
14817
|
+
memory_scope_id: text("memory_scope_id"),
|
|
14818
|
+
memory_kind: text("memory_kind"),
|
|
14819
|
+
memory_active: integer("memory_active"),
|
|
14820
|
+
memory_deleted_at: text("memory_deleted_at"),
|
|
14821
|
+
score_summary_json: text("score_summary_json"),
|
|
14822
|
+
reason_codes_json: text("reason_codes_json")
|
|
14823
|
+
}, (table) => [uniqueIndex("idx_retrieval_exposures_attempt_rank").on(table.attempt_id, table.rank), index("idx_retrieval_exposures_memory").on(table.memory_id)]);
|
|
14824
|
+
var outcomeEvidence = sqliteTable("outcome_evidence", {
|
|
14825
|
+
evidence_id: text("evidence_id").primaryKey(),
|
|
14826
|
+
contract_version: integer("contract_version").notNull(),
|
|
14827
|
+
dimension: text("dimension").notNull(),
|
|
14828
|
+
evidence_type: text("evidence_type").notNull(),
|
|
14829
|
+
source_class: text("source_class").notNull(),
|
|
14830
|
+
observed_at: text("observed_at").notNull(),
|
|
14831
|
+
producer: text("producer").notNull(),
|
|
14832
|
+
producer_version: text("producer_version").notNull(),
|
|
14833
|
+
status: text("status").notNull(),
|
|
14834
|
+
value_type: text("value_type"),
|
|
14835
|
+
value_integer: integer("value_integer"),
|
|
14836
|
+
value_real: real("value_real"),
|
|
14837
|
+
value_unit: text("value_unit"),
|
|
14838
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "cascade" }),
|
|
14839
|
+
source: text("source"),
|
|
14840
|
+
stream_id: text("stream_id"),
|
|
14841
|
+
source_session_id: text("source_session_id"),
|
|
14842
|
+
prompt_number: integer("prompt_number"),
|
|
14843
|
+
raw_event_start_seq: integer("raw_event_start_seq"),
|
|
14844
|
+
raw_event_end_seq: integer("raw_event_end_seq"),
|
|
14845
|
+
experiment_id: text("experiment_id"),
|
|
14846
|
+
experiment_cell_id: text("experiment_cell_id"),
|
|
14847
|
+
window_start_at: text("window_start_at"),
|
|
14848
|
+
window_end_at: text("window_end_at"),
|
|
14849
|
+
references_json: text("references_json"),
|
|
14850
|
+
retention_until: text("retention_until"),
|
|
14851
|
+
retention_pinned: integer("retention_pinned").notNull().default(0),
|
|
14852
|
+
retention_finalized_at: text("retention_finalized_at")
|
|
14853
|
+
}, (table) => [
|
|
14854
|
+
index("idx_outcome_evidence_observed_id").on(table.observed_at, table.evidence_id),
|
|
14855
|
+
index("idx_outcome_evidence_session_observed").on(table.session_id, table.observed_at),
|
|
14856
|
+
index("idx_outcome_evidence_source_stream_observed").on(table.source, table.stream_id, table.observed_at),
|
|
14857
|
+
index("idx_outcome_evidence_type_observed").on(table.evidence_type, table.observed_at),
|
|
14858
|
+
index("idx_outcome_evidence_retention").on(table.retention_pinned, table.retention_until)
|
|
14859
|
+
]);
|
|
14860
|
+
var attributionAssessments = sqliteTable("attribution_assessments", {
|
|
14861
|
+
assessment_id: text("assessment_id").primaryKey(),
|
|
14862
|
+
contract_version: integer("contract_version").notNull(),
|
|
14863
|
+
subject_type: text("subject_type").notNull(),
|
|
14864
|
+
attempt_id: text("attempt_id").notNull().references(() => retrievalAttempts.attempt_id, { onDelete: "cascade" }),
|
|
14865
|
+
exposure_id: integer("exposure_id").references(() => retrievalExposures.exposure_id, { onDelete: "cascade" }),
|
|
14866
|
+
dimension: text("dimension").notNull(),
|
|
14867
|
+
impact_label: text("impact_label").notNull(),
|
|
14868
|
+
basis: text("basis").notNull(),
|
|
14869
|
+
confidence_level: text("confidence_level").notNull(),
|
|
14870
|
+
method: text("method").notNull(),
|
|
14871
|
+
method_version: text("method_version").notNull(),
|
|
14872
|
+
created_at: text("created_at").notNull(),
|
|
14873
|
+
claim_type: text("claim_type").notNull().default("observational")
|
|
14874
|
+
}, (table) => [
|
|
14875
|
+
index("idx_attribution_assessments_attempt_created").on(table.attempt_id, table.created_at),
|
|
14876
|
+
index("idx_attribution_assessments_label_created").on(table.impact_label, table.created_at),
|
|
14877
|
+
index("idx_attribution_assessments_exposure").on(table.exposure_id)
|
|
14878
|
+
]);
|
|
14879
|
+
sqliteTable("attribution_assessment_evidence", {
|
|
14880
|
+
assessment_id: text("assessment_id").notNull().references(() => attributionAssessments.assessment_id, { onDelete: "cascade" }),
|
|
14881
|
+
evidence_id: text("evidence_id").notNull().references(() => outcomeEvidence.evidence_id, { onDelete: "cascade" })
|
|
14882
|
+
}, (table) => [primaryKey({ columns: [table.assessment_id, table.evidence_id] }), index("idx_attribution_assessment_evidence_evidence").on(table.evidence_id, table.assessment_id)]);
|
|
14883
|
+
sqliteTable("maintenance_jobs", {
|
|
14884
|
+
kind: text("kind").primaryKey(),
|
|
14885
|
+
title: text("title").notNull(),
|
|
14886
|
+
status: text("status").notNull(),
|
|
14887
|
+
message: text("message"),
|
|
14888
|
+
progress_current: integer("progress_current").notNull().default(0),
|
|
14889
|
+
progress_total: integer("progress_total"),
|
|
14890
|
+
progress_unit: text("progress_unit").notNull().default("items"),
|
|
14891
|
+
metadata_json: text("metadata_json"),
|
|
14892
|
+
started_at: text("started_at"),
|
|
14893
|
+
updated_at: text("updated_at").notNull(),
|
|
14894
|
+
finished_at: text("finished_at"),
|
|
14895
|
+
error: text("error")
|
|
14896
|
+
}, (table) => [index("idx_maintenance_jobs_status_updated").on(table.status, table.updated_at)]);
|
|
14897
|
+
sqliteTable("raw_events", {
|
|
14898
|
+
id: integer("id").primaryKey(),
|
|
14899
|
+
source: text("source").notNull().default("opencode"),
|
|
14900
|
+
stream_id: text("stream_id").notNull().default(""),
|
|
14901
|
+
opencode_session_id: text("opencode_session_id").notNull(),
|
|
14902
|
+
event_id: text("event_id"),
|
|
14903
|
+
event_seq: integer("event_seq").notNull(),
|
|
14904
|
+
event_type: text("event_type").notNull(),
|
|
14905
|
+
ts_wall_ms: integer("ts_wall_ms"),
|
|
14906
|
+
ts_mono_ms: real("ts_mono_ms"),
|
|
14907
|
+
payload_json: text("payload_json").notNull(),
|
|
14908
|
+
capture_context_json: text("capture_context_json"),
|
|
14909
|
+
created_at: text("created_at").notNull()
|
|
14910
|
+
}, (table) => [
|
|
14911
|
+
uniqueIndex("idx_raw_events_source_stream_seq").on(table.source, table.stream_id, table.event_seq),
|
|
14912
|
+
uniqueIndex("idx_raw_events_source_stream_event_id").on(table.source, table.stream_id, table.event_id),
|
|
14913
|
+
index("idx_raw_events_session_seq").on(table.opencode_session_id, table.event_seq),
|
|
14914
|
+
index("idx_raw_events_created").on(table.created_at),
|
|
14915
|
+
index("idx_raw_events_capture_context").on(table.source, table.stream_id, table.event_seq).where(sql`${table.capture_context_json} IS NOT NULL`)
|
|
14916
|
+
]);
|
|
14917
|
+
sqliteTable("raw_event_sessions", {
|
|
14918
|
+
source: text("source").notNull().default("opencode"),
|
|
14919
|
+
stream_id: text("stream_id").notNull().default(""),
|
|
14920
|
+
opencode_session_id: text("opencode_session_id").notNull(),
|
|
14921
|
+
cwd: text("cwd"),
|
|
14922
|
+
project: text("project"),
|
|
14923
|
+
started_at: text("started_at"),
|
|
14924
|
+
last_seen_ts_wall_ms: integer("last_seen_ts_wall_ms"),
|
|
14925
|
+
last_received_event_seq: integer("last_received_event_seq").notNull().default(-1),
|
|
14926
|
+
last_flushed_event_seq: integer("last_flushed_event_seq").notNull().default(-1),
|
|
14927
|
+
updated_at: text("updated_at").notNull()
|
|
14928
|
+
}, (table) => [primaryKey({ columns: [table.source, table.stream_id] }), index("idx_raw_event_sessions_pending_updated").on(table.updated_at).where(sql`${table.last_received_event_seq} > ${table.last_flushed_event_seq}`)]);
|
|
14929
|
+
sqliteTable("pi_import_state", {
|
|
14930
|
+
file_path: text("file_path").primaryKey(),
|
|
14931
|
+
size_bytes: integer("size_bytes").notNull(),
|
|
14932
|
+
mtime_ms: real("mtime_ms").notNull(),
|
|
14933
|
+
updated_at: text("updated_at").notNull()
|
|
14934
|
+
});
|
|
14935
|
+
sqliteTable("opencode_sessions", {
|
|
14936
|
+
source: text("source").notNull().default("opencode"),
|
|
14937
|
+
stream_id: text("stream_id").notNull().default(""),
|
|
14938
|
+
opencode_session_id: text("opencode_session_id").notNull(),
|
|
14939
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "cascade" }),
|
|
14940
|
+
created_at: text("created_at").notNull()
|
|
14941
|
+
}, (table) => [primaryKey({ columns: [table.source, table.stream_id] }), index("idx_opencode_sessions_session").on(table.session_id)]);
|
|
14942
|
+
sqliteTable("raw_event_flush_batches", {
|
|
14943
|
+
id: integer("id").primaryKey(),
|
|
14944
|
+
source: text("source").notNull().default("opencode"),
|
|
14945
|
+
stream_id: text("stream_id").notNull().default(""),
|
|
14946
|
+
opencode_session_id: text("opencode_session_id").notNull(),
|
|
14947
|
+
start_event_seq: integer("start_event_seq").notNull(),
|
|
14948
|
+
end_event_seq: integer("end_event_seq").notNull(),
|
|
14949
|
+
extractor_version: text("extractor_version").notNull(),
|
|
14950
|
+
status: text("status").notNull(),
|
|
14951
|
+
error_message: text("error_message"),
|
|
14952
|
+
error_type: text("error_type"),
|
|
14953
|
+
observer_provider: text("observer_provider"),
|
|
14954
|
+
observer_model: text("observer_model"),
|
|
14955
|
+
observer_runtime: text("observer_runtime"),
|
|
14956
|
+
observer_auth_source: text("observer_auth_source"),
|
|
14957
|
+
observer_auth_type: text("observer_auth_type"),
|
|
14958
|
+
observer_error_code: text("observer_error_code"),
|
|
14959
|
+
observer_error_message: text("observer_error_message"),
|
|
14960
|
+
attempt_count: integer("attempt_count").notNull().default(0),
|
|
14961
|
+
created_at: text("created_at").notNull(),
|
|
14962
|
+
updated_at: text("updated_at").notNull()
|
|
14963
|
+
}, (table) => [
|
|
14964
|
+
uniqueIndex("idx_flush_batches_source_stream_seq_ver").on(table.source, table.stream_id, table.start_event_seq, table.end_event_seq, table.extractor_version),
|
|
14965
|
+
index("idx_flush_batches_session_created").on(table.opencode_session_id, table.created_at),
|
|
14966
|
+
index("idx_flush_batches_status_updated").on(table.status, table.updated_at),
|
|
14967
|
+
index("idx_flush_batches_observer_status_updated").on(table.status, table.updated_at).where(sql`${table.observer_provider} IS NOT NULL`),
|
|
14968
|
+
index("idx_flush_batches_capture_status_updated").on(table.status, table.updated_at).where(sql`${table.observer_provider} IS NULL`)
|
|
14969
|
+
]);
|
|
14970
|
+
sqliteTable("user_prompts", {
|
|
14971
|
+
id: integer("id").primaryKey(),
|
|
14972
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "cascade" }),
|
|
14973
|
+
project: text("project"),
|
|
14974
|
+
prompt_text: text("prompt_text").notNull(),
|
|
14975
|
+
prompt_number: integer("prompt_number"),
|
|
14976
|
+
created_at: text("created_at").notNull(),
|
|
14977
|
+
created_at_epoch: integer("created_at_epoch").notNull(),
|
|
14978
|
+
metadata_json: text("metadata_json"),
|
|
14979
|
+
import_key: text("import_key")
|
|
14980
|
+
}, (table) => [
|
|
14981
|
+
index("idx_user_prompts_session").on(table.session_id),
|
|
14982
|
+
index("idx_user_prompts_project").on(table.project),
|
|
14983
|
+
index("idx_user_prompts_epoch").on(table.created_at_epoch)
|
|
14984
|
+
]);
|
|
14985
|
+
sqliteTable("session_summaries", {
|
|
14986
|
+
id: integer("id").primaryKey(),
|
|
14987
|
+
session_id: integer("session_id").references(() => sessions.id, { onDelete: "cascade" }),
|
|
14988
|
+
project: text("project"),
|
|
14989
|
+
request: text("request"),
|
|
14990
|
+
investigated: text("investigated"),
|
|
14991
|
+
learned: text("learned"),
|
|
14992
|
+
completed: text("completed"),
|
|
14993
|
+
next_steps: text("next_steps"),
|
|
14994
|
+
notes: text("notes"),
|
|
14995
|
+
files_read: text("files_read"),
|
|
14996
|
+
files_edited: text("files_edited"),
|
|
14997
|
+
prompt_number: integer("prompt_number"),
|
|
14998
|
+
created_at: text("created_at").notNull(),
|
|
14999
|
+
created_at_epoch: integer("created_at_epoch").notNull(),
|
|
15000
|
+
metadata_json: text("metadata_json"),
|
|
15001
|
+
import_key: text("import_key")
|
|
15002
|
+
}, (table) => [
|
|
15003
|
+
index("idx_session_summaries_session").on(table.session_id),
|
|
15004
|
+
index("idx_session_summaries_project").on(table.project),
|
|
15005
|
+
index("idx_session_summaries_epoch").on(table.created_at_epoch)
|
|
15006
|
+
]);
|
|
15007
|
+
sqliteTable("replication_ops", {
|
|
15008
|
+
op_id: text("op_id").primaryKey(),
|
|
15009
|
+
entity_type: text("entity_type").notNull(),
|
|
15010
|
+
entity_id: text("entity_id").notNull(),
|
|
15011
|
+
op_type: text("op_type").notNull(),
|
|
15012
|
+
payload_json: text("payload_json"),
|
|
15013
|
+
clock_rev: integer("clock_rev").notNull(),
|
|
15014
|
+
clock_updated_at: text("clock_updated_at").notNull(),
|
|
15015
|
+
clock_device_id: text("clock_device_id").notNull(),
|
|
15016
|
+
device_id: text("device_id").notNull(),
|
|
15017
|
+
created_at: text("created_at").notNull(),
|
|
15018
|
+
scope_id: text("scope_id")
|
|
15019
|
+
}, (table) => [
|
|
15020
|
+
index("idx_replication_ops_created").on(table.created_at, table.op_id),
|
|
15021
|
+
index("idx_replication_ops_entity").on(table.entity_type, table.entity_id),
|
|
15022
|
+
index("idx_replication_ops_scope_created").on(table.scope_id, table.created_at, table.op_id)
|
|
15023
|
+
]);
|
|
15024
|
+
sqliteTable("replication_cursors", {
|
|
15025
|
+
peer_device_id: text("peer_device_id").primaryKey(),
|
|
15026
|
+
last_applied_cursor: text("last_applied_cursor"),
|
|
15027
|
+
last_acked_cursor: text("last_acked_cursor"),
|
|
15028
|
+
updated_at: text("updated_at").notNull()
|
|
15029
|
+
});
|
|
15030
|
+
sqliteTable("replication_cursors_v2", {
|
|
15031
|
+
peer_device_id: text("peer_device_id").notNull(),
|
|
15032
|
+
scope_id: text("scope_id").notNull(),
|
|
15033
|
+
last_applied_cursor: text("last_applied_cursor"),
|
|
15034
|
+
last_acked_cursor: text("last_acked_cursor"),
|
|
15035
|
+
updated_at: text("updated_at").notNull()
|
|
15036
|
+
}, (table) => [primaryKey({ columns: [table.peer_device_id, table.scope_id] }), index("idx_replication_cursors_v2_scope").on(table.scope_id)]);
|
|
15037
|
+
sqliteTable("sync_peers", {
|
|
15038
|
+
peer_device_id: text("peer_device_id").primaryKey(),
|
|
15039
|
+
name: text("name"),
|
|
15040
|
+
pinned_fingerprint: text("pinned_fingerprint"),
|
|
15041
|
+
public_key: text("public_key"),
|
|
15042
|
+
addresses_json: text("addresses_json"),
|
|
15043
|
+
manual_addresses_json: text("manual_addresses_json"),
|
|
15044
|
+
last_success_address: text("last_success_address"),
|
|
15045
|
+
claimed_local_actor: integer("claimed_local_actor").notNull().default(0),
|
|
15046
|
+
actor_id: text("actor_id"),
|
|
15047
|
+
projects_include_json: text("projects_include_json"),
|
|
15048
|
+
projects_exclude_json: text("projects_exclude_json"),
|
|
15049
|
+
created_at: text("created_at").notNull(),
|
|
15050
|
+
last_seen_at: text("last_seen_at"),
|
|
15051
|
+
last_sync_at: text("last_sync_at"),
|
|
15052
|
+
last_error: text("last_error"),
|
|
15053
|
+
runtime_version: text("runtime_version"),
|
|
15054
|
+
runtime_version_observed_at: text("runtime_version_observed_at"),
|
|
15055
|
+
highest_observed_direct_signature_version: integer("highest_observed_direct_signature_version"),
|
|
15056
|
+
discovered_via_coordinator_id: text("discovered_via_coordinator_id"),
|
|
15057
|
+
discovered_via_group_id: text("discovered_via_group_id"),
|
|
15058
|
+
trust_provenance: text("trust_provenance"),
|
|
15059
|
+
pending_bootstrap_grant_id: text("pending_bootstrap_grant_id")
|
|
15060
|
+
});
|
|
15061
|
+
sqliteTable("sync_peer_signature_state", {
|
|
15062
|
+
peer_device_id: text("peer_device_id").primaryKey(),
|
|
15063
|
+
highest_observed_direct_signature_version: integer("highest_observed_direct_signature_version").notNull()
|
|
15064
|
+
});
|
|
15065
|
+
sqliteTable("sync_nonces", {
|
|
15066
|
+
nonce: text("nonce").primaryKey(),
|
|
15067
|
+
device_id: text("device_id").notNull(),
|
|
15068
|
+
created_at: text("created_at").notNull()
|
|
15069
|
+
});
|
|
15070
|
+
sqliteTable("sync_device", {
|
|
15071
|
+
device_id: text("device_id").primaryKey(),
|
|
15072
|
+
public_key: text("public_key").notNull(),
|
|
15073
|
+
fingerprint: text("fingerprint").notNull(),
|
|
15074
|
+
created_at: text("created_at").notNull()
|
|
15075
|
+
});
|
|
15076
|
+
sqliteTable("sync_attempts", {
|
|
15077
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
15078
|
+
peer_device_id: text("peer_device_id").notNull(),
|
|
15079
|
+
started_at: text("started_at").notNull(),
|
|
15080
|
+
finished_at: text("finished_at"),
|
|
15081
|
+
ok: integer("ok").notNull(),
|
|
15082
|
+
ops_in: integer("ops_in").notNull(),
|
|
15083
|
+
ops_out: integer("ops_out").notNull(),
|
|
15084
|
+
error: text("error"),
|
|
15085
|
+
local_sync_capability: text("local_sync_capability"),
|
|
15086
|
+
peer_sync_capability: text("peer_sync_capability"),
|
|
15087
|
+
negotiated_sync_capability: text("negotiated_sync_capability"),
|
|
15088
|
+
failure_category: text("failure_category")
|
|
15089
|
+
}, (table) => [
|
|
15090
|
+
index("idx_sync_attempts_peer_started").on(table.peer_device_id, table.started_at),
|
|
15091
|
+
index("idx_sync_attempts_occurred").on(sql`CASE WHEN ${table.finished_at} IS NULL THEN ${table.started_at} ELSE ${table.finished_at} END`, table.id),
|
|
15092
|
+
index("idx_sync_attempts_error_occurred").on(sql`CASE WHEN ${table.finished_at} IS NULL THEN ${table.started_at} ELSE ${table.finished_at} END`, table.id).where(sql`${table.ok} = 0`),
|
|
15093
|
+
index("idx_sync_attempts_success_occurred").on(sql`CASE WHEN ${table.finished_at} IS NULL THEN ${table.started_at} ELSE ${table.finished_at} END`, table.id).where(sql`${table.ok} <> 0`)
|
|
15094
|
+
]);
|
|
15095
|
+
sqliteTable("sync_scope_rejections", {
|
|
15096
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
15097
|
+
peer_device_id: text("peer_device_id"),
|
|
15098
|
+
op_id: text("op_id").notNull(),
|
|
15099
|
+
entity_type: text("entity_type").notNull(),
|
|
15100
|
+
entity_id: text("entity_id").notNull(),
|
|
15101
|
+
scope_id: text("scope_id"),
|
|
15102
|
+
reason: text("reason").notNull(),
|
|
15103
|
+
created_at: text("created_at").notNull()
|
|
15104
|
+
}, (table) => [index("idx_sync_scope_rejections_peer_created").on(table.peer_device_id, table.created_at), index("idx_sync_scope_rejections_scope_created").on(table.scope_id, table.created_at)]);
|
|
15105
|
+
sqliteTable("sync_daemon_state", {
|
|
15106
|
+
id: integer("id").primaryKey(),
|
|
15107
|
+
last_error: text("last_error"),
|
|
15108
|
+
last_traceback: text("last_traceback"),
|
|
15109
|
+
last_error_at: text("last_error_at"),
|
|
15110
|
+
last_ok_at: text("last_ok_at"),
|
|
15111
|
+
phase: text("phase")
|
|
15112
|
+
});
|
|
15113
|
+
sqliteTable("sync_reset_state", {
|
|
15114
|
+
id: integer("id").primaryKey(),
|
|
15115
|
+
generation: integer("generation").notNull(),
|
|
15116
|
+
snapshot_id: text("snapshot_id").notNull(),
|
|
15117
|
+
baseline_cursor: text("baseline_cursor"),
|
|
15118
|
+
retained_floor_cursor: text("retained_floor_cursor"),
|
|
15119
|
+
updated_at: text("updated_at").notNull()
|
|
15120
|
+
});
|
|
15121
|
+
sqliteTable("sync_reset_state_v2", {
|
|
15122
|
+
scope_id: text("scope_id").primaryKey(),
|
|
15123
|
+
generation: integer("generation").notNull(),
|
|
15124
|
+
snapshot_id: text("snapshot_id").notNull(),
|
|
15125
|
+
baseline_cursor: text("baseline_cursor"),
|
|
15126
|
+
retained_floor_cursor: text("retained_floor_cursor"),
|
|
15127
|
+
updated_at: text("updated_at").notNull()
|
|
15128
|
+
});
|
|
15129
|
+
sqliteTable("sync_retention_state", {
|
|
15130
|
+
id: integer("id").primaryKey(),
|
|
15131
|
+
last_run_at: text("last_run_at"),
|
|
15132
|
+
last_duration_ms: integer("last_duration_ms"),
|
|
15133
|
+
last_deleted_ops: integer("last_deleted_ops").notNull().default(0),
|
|
15134
|
+
last_estimated_bytes_before: integer("last_estimated_bytes_before"),
|
|
15135
|
+
last_estimated_bytes_after: integer("last_estimated_bytes_after"),
|
|
15136
|
+
retained_floor_cursor: text("retained_floor_cursor"),
|
|
15137
|
+
last_error: text("last_error"),
|
|
15138
|
+
last_error_at: text("last_error_at")
|
|
15139
|
+
});
|
|
15140
|
+
sqliteTable("sync_retention_state_v2", {
|
|
15141
|
+
scope_id: text("scope_id").primaryKey(),
|
|
15142
|
+
last_run_at: text("last_run_at"),
|
|
15143
|
+
last_duration_ms: integer("last_duration_ms"),
|
|
15144
|
+
last_deleted_ops: integer("last_deleted_ops").notNull().default(0),
|
|
15145
|
+
last_estimated_bytes_before: integer("last_estimated_bytes_before"),
|
|
15146
|
+
last_estimated_bytes_after: integer("last_estimated_bytes_after"),
|
|
15147
|
+
retained_floor_cursor: text("retained_floor_cursor"),
|
|
15148
|
+
last_error: text("last_error"),
|
|
15149
|
+
last_error_at: text("last_error_at")
|
|
15150
|
+
});
|
|
15151
|
+
sqliteTable("raw_event_ingest_samples", {
|
|
15152
|
+
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
15153
|
+
created_at: text("created_at").notNull(),
|
|
15154
|
+
inserted_events: integer("inserted_events").notNull().default(0),
|
|
15155
|
+
skipped_invalid: integer("skipped_invalid").notNull().default(0),
|
|
15156
|
+
skipped_duplicate: integer("skipped_duplicate").notNull().default(0),
|
|
15157
|
+
skipped_conflict: integer("skipped_conflict").notNull().default(0)
|
|
15158
|
+
});
|
|
15159
|
+
sqliteTable("raw_event_ingest_stats", {
|
|
15160
|
+
id: integer("id").primaryKey(),
|
|
15161
|
+
inserted_events: integer("inserted_events").notNull().default(0),
|
|
15162
|
+
skipped_events: integer("skipped_events").notNull().default(0),
|
|
15163
|
+
skipped_invalid: integer("skipped_invalid").notNull().default(0),
|
|
15164
|
+
skipped_duplicate: integer("skipped_duplicate").notNull().default(0),
|
|
15165
|
+
skipped_conflict: integer("skipped_conflict").notNull().default(0),
|
|
15166
|
+
updated_at: text("updated_at").notNull()
|
|
15167
|
+
});
|
|
15168
|
+
sqliteTable("coordinator_group_preferences", {
|
|
15169
|
+
coordinator_id: text("coordinator_id").notNull(),
|
|
15170
|
+
group_id: text("group_id").notNull(),
|
|
15171
|
+
projects_include_json: text("projects_include_json"),
|
|
15172
|
+
projects_exclude_json: text("projects_exclude_json"),
|
|
15173
|
+
auto_seed_scope: integer("auto_seed_scope").notNull().default(1),
|
|
15174
|
+
default_space_scope_id: text("default_space_scope_id"),
|
|
15175
|
+
auto_grant_default_space_on_join: integer("auto_grant_default_space_on_join").notNull().default(0),
|
|
15176
|
+
updated_at: text("updated_at").notNull()
|
|
15177
|
+
}, (table) => ({ pk: primaryKey({ columns: [table.coordinator_id, table.group_id] }) }));
|
|
15178
|
+
sqliteTable("actors", {
|
|
15179
|
+
actor_id: text("actor_id").primaryKey(),
|
|
15180
|
+
display_name: text("display_name").notNull(),
|
|
15181
|
+
is_local: integer("is_local").notNull().default(0),
|
|
15182
|
+
status: text("status").notNull().default("active"),
|
|
15183
|
+
merged_into_actor_id: text("merged_into_actor_id"),
|
|
15184
|
+
created_at: text("created_at").notNull(),
|
|
15185
|
+
updated_at: text("updated_at").notNull()
|
|
15186
|
+
}, (table) => ({
|
|
15187
|
+
isLocalIdx: index("idx_actors_is_local").on(table.is_local),
|
|
15188
|
+
statusIdx: index("idx_actors_status").on(table.status)
|
|
15189
|
+
}));
|
|
15190
|
+
sqliteTable("recipient_policy_review_resolutions", {
|
|
15191
|
+
review_item_id: text("review_item_id").notNull(),
|
|
15192
|
+
source_fingerprint: text("source_fingerprint").notNull(),
|
|
15193
|
+
decision: text("decision").notNull(),
|
|
15194
|
+
decision_input_json: text("decision_input_json").notNull(),
|
|
15195
|
+
preview_json: text("preview_json").notNull(),
|
|
15196
|
+
decided_by_identity_id: text("decided_by_identity_id").notNull(),
|
|
15197
|
+
decided_by_device_id: text("decided_by_device_id").notNull(),
|
|
15198
|
+
resolved_at: text("resolved_at").notNull()
|
|
15199
|
+
}, (table) => [primaryKey({ columns: [table.review_item_id, table.source_fingerprint] })]);
|
|
15200
|
+
sqliteTable("coordinator_enrollment_reconciliation_issues", {
|
|
15201
|
+
coordinator_id: text("coordinator_id").notNull(),
|
|
15202
|
+
group_id: text("group_id").notNull(),
|
|
15203
|
+
kind: text("kind").notNull(),
|
|
15204
|
+
reference_id: text("reference_id").notNull(),
|
|
15205
|
+
code: text("code").notNull(),
|
|
15206
|
+
status: text("status").notNull().default("open"),
|
|
15207
|
+
first_seen_at: text("first_seen_at").notNull(),
|
|
15208
|
+
last_seen_at: text("last_seen_at").notNull(),
|
|
15209
|
+
resolved_at: text("resolved_at"),
|
|
15210
|
+
occurrence_count: integer("occurrence_count").notNull().default(1),
|
|
15211
|
+
updated_at: text("updated_at").notNull()
|
|
15212
|
+
}, (table) => [
|
|
15213
|
+
primaryKey({ columns: [
|
|
15214
|
+
table.coordinator_id,
|
|
15215
|
+
table.group_id,
|
|
15216
|
+
table.kind,
|
|
15217
|
+
table.reference_id,
|
|
15218
|
+
table.code
|
|
15219
|
+
] }),
|
|
15220
|
+
index("idx_coordinator_enrollment_issues_boundary_status").on(table.coordinator_id, table.group_id, table.status),
|
|
15221
|
+
index("idx_coordinator_enrollment_issues_status_recent").on(table.status, table.last_seen_at, table.resolved_at)
|
|
15222
|
+
]);
|
|
15223
|
+
var policyTeams = sqliteTable("policy_teams", {
|
|
15224
|
+
team_id: text("team_id").primaryKey(),
|
|
15225
|
+
display_name: text("display_name").notNull(),
|
|
15226
|
+
status: text("status").notNull(),
|
|
15227
|
+
device_eligibility_mode: text("device_eligibility_mode").notNull().default("person_all_devices"),
|
|
15228
|
+
provenance: text("provenance").notNull(),
|
|
15229
|
+
revision: text("revision").notNull(),
|
|
15230
|
+
migration_state: text("migration_state").notNull(),
|
|
15231
|
+
source_fingerprint: text("source_fingerprint"),
|
|
15232
|
+
idempotency_key: text("idempotency_key").notNull().unique(),
|
|
15233
|
+
created_at: text("created_at").notNull(),
|
|
15234
|
+
updated_at: text("updated_at").notNull()
|
|
15235
|
+
});
|
|
15236
|
+
sqliteTable("policy_team_memberships", {
|
|
15237
|
+
team_id: text("team_id").notNull(),
|
|
15238
|
+
identity_id: text("identity_id").notNull(),
|
|
15239
|
+
role: text("role").notNull(),
|
|
15240
|
+
status: text("status").notNull(),
|
|
15241
|
+
provenance: text("provenance").notNull(),
|
|
15242
|
+
revision: text("revision").notNull(),
|
|
15243
|
+
migration_state: text("migration_state").notNull(),
|
|
15244
|
+
source_fingerprint: text("source_fingerprint"),
|
|
15245
|
+
idempotency_key: text("idempotency_key").notNull().unique(),
|
|
15246
|
+
created_at: text("created_at").notNull(),
|
|
15247
|
+
updated_at: text("updated_at").notNull()
|
|
15248
|
+
}, (table) => ({
|
|
15249
|
+
pk: primaryKey({ columns: [table.team_id, table.identity_id] }),
|
|
15250
|
+
identityStatusIdx: index("idx_policy_team_memberships_identity_status").on(table.identity_id, table.status)
|
|
15251
|
+
}));
|
|
15252
|
+
sqliteTable("policy_team_device_decisions", {
|
|
15253
|
+
team_id: text("team_id").notNull().references(() => policyTeams.team_id, { onDelete: "cascade" }),
|
|
15254
|
+
device_id: text("device_id").notNull(),
|
|
15255
|
+
decision: text("decision").notNull(),
|
|
15256
|
+
assignment_version: integer("assignment_version").notNull().default(0),
|
|
15257
|
+
provenance: text("provenance").notNull(),
|
|
15258
|
+
revision: text("revision").notNull(),
|
|
15259
|
+
created_at: text("created_at").notNull(),
|
|
15260
|
+
updated_at: text("updated_at").notNull()
|
|
15261
|
+
}, (table) => [primaryKey({ columns: [table.team_id, table.device_id] }), index("idx_policy_team_device_decisions_device").on(table.device_id)]);
|
|
15262
|
+
sqliteTable("identity_devices", {
|
|
15263
|
+
device_id: text("device_id").primaryKey(),
|
|
15264
|
+
identity_id: text("identity_id").notNull(),
|
|
15265
|
+
display_name: text("display_name").notNull(),
|
|
15266
|
+
status: text("status").notNull(),
|
|
15267
|
+
provenance: text("provenance").notNull(),
|
|
15268
|
+
revision: text("revision").notNull(),
|
|
15269
|
+
migration_state: text("migration_state").notNull(),
|
|
15270
|
+
assignment_version: integer("assignment_version").notNull().default(0),
|
|
15271
|
+
source_fingerprint: text("source_fingerprint"),
|
|
15272
|
+
idempotency_key: text("idempotency_key").notNull().unique(),
|
|
15273
|
+
created_at: text("created_at").notNull(),
|
|
15274
|
+
updated_at: text("updated_at").notNull()
|
|
15275
|
+
}, (table) => ({ identityStatusIdx: index("idx_identity_devices_identity_status").on(table.identity_id, table.status) }));
|
|
15276
|
+
var deviceIdentityBindingCommits = sqliteTable("device_identity_binding_commits", {
|
|
15277
|
+
commit_digest: text("commit_digest").primaryKey(),
|
|
15278
|
+
reviewed_inventory_digest: text("reviewed_inventory_digest").notNull(),
|
|
15279
|
+
request_json: text("request_json").notNull(),
|
|
15280
|
+
outcomes_json: text("outcomes_json").notNull(),
|
|
15281
|
+
write_count: integer("write_count").notNull(),
|
|
15282
|
+
decided_by_identity_id: text("decided_by_identity_id").notNull(),
|
|
15283
|
+
decided_by_device_id: text("decided_by_device_id").notNull(),
|
|
15284
|
+
created_at: text("created_at").notNull()
|
|
15285
|
+
});
|
|
15286
|
+
sqliteTable("device_identity_binding_audit", {
|
|
15287
|
+
event_id: text("event_id").primaryKey(),
|
|
15288
|
+
commit_digest: text("commit_digest").notNull().references(() => deviceIdentityBindingCommits.commit_digest),
|
|
15289
|
+
device_id: text("device_id").notNull(),
|
|
15290
|
+
previous_identity_id: text("previous_identity_id"),
|
|
15291
|
+
target_identity_id: text("target_identity_id").notNull(),
|
|
15292
|
+
action: text("action").notNull(),
|
|
15293
|
+
previous_assignment_version: integer("previous_assignment_version"),
|
|
15294
|
+
resulting_assignment_version: integer("resulting_assignment_version").notNull(),
|
|
15295
|
+
decided_by_identity_id: text("decided_by_identity_id").notNull(),
|
|
15296
|
+
decided_by_device_id: text("decided_by_device_id").notNull(),
|
|
15297
|
+
created_at: text("created_at").notNull()
|
|
15298
|
+
}, (table) => [uniqueIndex("idx_device_identity_binding_audit_commit_device").on(table.commit_digest, table.device_id), index("idx_device_identity_binding_audit_device_created").on(table.device_id, table.created_at, table.event_id)]);
|
|
15299
|
+
sqliteTable("project_recipients", {
|
|
15300
|
+
canonical_project_identity: text("canonical_project_identity").notNull(),
|
|
15301
|
+
recipient_kind: text("recipient_kind").notNull(),
|
|
15302
|
+
recipient_id: text("recipient_id").notNull(),
|
|
15303
|
+
status: text("status").notNull(),
|
|
15304
|
+
provenance: text("provenance").notNull(),
|
|
15305
|
+
policy_revision: text("policy_revision").notNull(),
|
|
15306
|
+
migration_state: text("migration_state").notNull(),
|
|
15307
|
+
source_fingerprint: text("source_fingerprint"),
|
|
15308
|
+
idempotency_key: text("idempotency_key").notNull().unique(),
|
|
15309
|
+
created_at: text("created_at").notNull(),
|
|
15310
|
+
updated_at: text("updated_at").notNull()
|
|
15311
|
+
}, (table) => ({
|
|
15312
|
+
pk: primaryKey({ columns: [
|
|
15313
|
+
table.canonical_project_identity,
|
|
15314
|
+
table.recipient_kind,
|
|
15315
|
+
table.recipient_id
|
|
15316
|
+
] }),
|
|
15317
|
+
projectStatusIdx: index("idx_project_recipients_project_status").on(table.canonical_project_identity, table.status),
|
|
15318
|
+
recipientStatusIdx: index("idx_project_recipients_recipient_status").on(table.recipient_kind, table.recipient_id, table.status)
|
|
15319
|
+
}));
|
|
15320
|
+
var legacyTeamSetupDrafts = sqliteTable("legacy_team_setup_drafts", {
|
|
15321
|
+
attempt_id: text("attempt_id").primaryKey(),
|
|
15322
|
+
candidate_id: text("candidate_id").notNull(),
|
|
15323
|
+
coordinator_id: text("coordinator_id").notNull(),
|
|
15324
|
+
group_id: text("group_id").notNull(),
|
|
15325
|
+
state: text("state").notNull().default("needs_setup"),
|
|
15326
|
+
display_name: text("display_name").notNull(),
|
|
15327
|
+
roster_fingerprint: text("roster_fingerprint").notNull(),
|
|
15328
|
+
projection_fingerprint: text("projection_fingerprint").notNull(),
|
|
15329
|
+
finish_digest: text("finish_digest"),
|
|
15330
|
+
safe_error_code: text("safe_error_code"),
|
|
15331
|
+
completed_team_id: text("completed_team_id"),
|
|
15332
|
+
created_at: text("created_at").notNull(),
|
|
15333
|
+
updated_at: text("updated_at").notNull(),
|
|
15334
|
+
completed_at: text("completed_at"),
|
|
15335
|
+
superseded_at: text("superseded_at")
|
|
15336
|
+
}, (table) => [
|
|
15337
|
+
index("idx_legacy_team_setup_drafts_candidate_state").on(table.candidate_id, table.state, table.created_at),
|
|
15338
|
+
index("idx_legacy_team_setup_drafts_state_updated").on(table.state, table.updated_at),
|
|
15339
|
+
index("idx_legacy_team_setup_drafts_finish_digest").on(table.finish_digest)
|
|
15340
|
+
]);
|
|
15341
|
+
sqliteTable("legacy_team_setup_completions", {
|
|
15342
|
+
attempt_id: text("attempt_id").notNull(),
|
|
15343
|
+
finish_digest: text("finish_digest").notNull(),
|
|
15344
|
+
candidate_ref: text("candidate_ref").notNull(),
|
|
15345
|
+
confirmed_access_delta_digest: text("confirmed_access_delta_digest").notNull(),
|
|
15346
|
+
completed_team_id: text("completed_team_id").notNull(),
|
|
15347
|
+
response_json: text("response_json").notNull(),
|
|
15348
|
+
completed_at: text("completed_at").notNull(),
|
|
15349
|
+
created_at: text("created_at").notNull()
|
|
15350
|
+
}, (table) => [uniqueIndex("idx_legacy_team_setup_completions_attempt_finish").on(table.attempt_id, table.finish_digest), index("idx_legacy_team_setup_completions_exact_replay").on(table.candidate_ref, table.attempt_id, table.finish_digest, table.confirmed_access_delta_digest)]);
|
|
15351
|
+
sqliteTable("legacy_team_setup_draft_devices", {
|
|
15352
|
+
attempt_id: text("attempt_id").notNull().references(() => legacyTeamSetupDrafts.attempt_id, { onDelete: "cascade" }),
|
|
15353
|
+
device_id: text("device_id").notNull(),
|
|
15354
|
+
device_ref: text("device_ref").notNull(),
|
|
15355
|
+
key_fingerprint: text("key_fingerprint").notNull(),
|
|
15356
|
+
display_name: text("display_name").notNull(),
|
|
15357
|
+
enabled: integer("enabled", { mode: "boolean" }).notNull(),
|
|
15358
|
+
existing_identity_id: text("existing_identity_id"),
|
|
15359
|
+
existing_assignment_version: integer("existing_assignment_version"),
|
|
15360
|
+
verified_evidence_kind: text("verified_evidence_kind"),
|
|
15361
|
+
decision: text("decision").notNull().default("unresolved"),
|
|
15362
|
+
target_identity_id: text("target_identity_id"),
|
|
15363
|
+
expected_assignment_kind: text("expected_assignment_kind"),
|
|
15364
|
+
expected_assignment_version: integer("expected_assignment_version"),
|
|
15365
|
+
updated_at: text("updated_at").notNull()
|
|
15366
|
+
}, (table) => [
|
|
15367
|
+
primaryKey({ columns: [table.attempt_id, table.device_id] }),
|
|
15368
|
+
uniqueIndex("idx_legacy_team_setup_devices_attempt_ref").on(table.attempt_id, table.device_ref),
|
|
15369
|
+
index("idx_legacy_team_setup_devices_attempt_decision").on(table.attempt_id, table.decision)
|
|
15370
|
+
]);
|
|
15371
|
+
sqliteTable("legacy_team_setup_draft_projects", {
|
|
15372
|
+
attempt_id: text("attempt_id").notNull().references(() => legacyTeamSetupDrafts.attempt_id, { onDelete: "cascade" }),
|
|
15373
|
+
project_ref: text("project_ref").notNull(),
|
|
15374
|
+
source_project_identity: text("source_project_identity").notNull(),
|
|
15375
|
+
display_name: text("display_name").notNull(),
|
|
15376
|
+
source_fingerprint: text("source_fingerprint").notNull(),
|
|
15377
|
+
resolution_kind: text("resolution_kind").notNull().default("unresolved"),
|
|
15378
|
+
resolved_project_identity: text("resolved_project_identity"),
|
|
15379
|
+
target_scope_id: text("target_scope_id"),
|
|
15380
|
+
updated_at: text("updated_at").notNull()
|
|
15381
|
+
}, (table) => [primaryKey({ columns: [table.attempt_id, table.project_ref] }), index("idx_legacy_team_setup_projects_attempt_resolution").on(table.attempt_id, table.resolution_kind)]);
|
|
15382
|
+
sqliteTable("recipient_managed_project_projections", {
|
|
15383
|
+
canonical_project_identity: text("canonical_project_identity").notNull(),
|
|
15384
|
+
display_name: text("display_name").notNull(),
|
|
15385
|
+
managed_scope_id: text("managed_scope_id").notNull(),
|
|
15386
|
+
coordinator_id: text("coordinator_id").notNull(),
|
|
15387
|
+
group_id: text("group_id").notNull(),
|
|
15388
|
+
recipient_identity_id: text("recipient_identity_id").notNull(),
|
|
15389
|
+
accepting_device_id: text("accepting_device_id").notNull(),
|
|
15390
|
+
source_operation_id: text("source_operation_id").notNull(),
|
|
15391
|
+
reviewed_project_set_digest: text("reviewed_project_set_digest").notNull(),
|
|
15392
|
+
status: text("status").notNull().default("active"),
|
|
15393
|
+
accepted_at: text("accepted_at").notNull(),
|
|
15394
|
+
revoked_at: text("revoked_at"),
|
|
15395
|
+
created_at: text("created_at").notNull(),
|
|
15396
|
+
updated_at: text("updated_at").notNull()
|
|
15397
|
+
}, (table) => [
|
|
15398
|
+
primaryKey({ columns: [table.source_operation_id, table.canonical_project_identity] }),
|
|
15399
|
+
index("idx_recipient_managed_projects_identity_status").on(table.recipient_identity_id, table.status),
|
|
15400
|
+
index("idx_recipient_managed_projects_scope_authority").on(table.managed_scope_id, table.coordinator_id, table.group_id, table.status)
|
|
15401
|
+
]);
|
|
15402
|
+
sqliteTable("recipient_policy_authority_states", {
|
|
15403
|
+
canonical_project_identity: text("canonical_project_identity").primaryKey(),
|
|
15404
|
+
authority_state: text("authority_state").notNull().default("legacy"),
|
|
15405
|
+
generation: integer("generation").notNull().default(0),
|
|
15406
|
+
desired_devices_digest: text("desired_devices_digest"),
|
|
15407
|
+
current_devices_digest: text("current_devices_digest"),
|
|
15408
|
+
stable_parity_evidence_digest: text("stable_parity_evidence_digest"),
|
|
15409
|
+
stable_parity_passed_at: text("stable_parity_passed_at"),
|
|
15410
|
+
fresh_snapshot_fingerprint: text("fresh_snapshot_fingerprint"),
|
|
15411
|
+
fresh_snapshot_observed_at: text("fresh_snapshot_observed_at"),
|
|
15412
|
+
safe_error_code: text("safe_error_code"),
|
|
15413
|
+
state_changed_at: text("state_changed_at").notNull(),
|
|
15414
|
+
last_error_at: text("last_error_at"),
|
|
15415
|
+
attempt_count: integer("attempt_count").notNull().default(0),
|
|
15416
|
+
last_attempt_at: text("last_attempt_at"),
|
|
15417
|
+
wake_epoch: integer("wake_epoch").notNull().default(0),
|
|
15418
|
+
last_completed_at: text("last_completed_at"),
|
|
15419
|
+
lease_owner: text("lease_owner"),
|
|
15420
|
+
lease_acquired_at: text("lease_acquired_at"),
|
|
15421
|
+
lease_expires_at: text("lease_expires_at"),
|
|
15422
|
+
created_at: text("created_at").notNull(),
|
|
15423
|
+
updated_at: text("updated_at").notNull()
|
|
15424
|
+
});
|
|
15425
|
+
sqliteTable("recipient_policy_reconciliation_steps", {
|
|
15426
|
+
canonical_project_identity: text("canonical_project_identity").notNull(),
|
|
15427
|
+
generation: integer("generation").notNull(),
|
|
15428
|
+
step_key: text("step_key").notNull(),
|
|
15429
|
+
effect_id: text("effect_id").notNull(),
|
|
15430
|
+
payload_digest: text("payload_digest").notNull(),
|
|
15431
|
+
status: text("status").notNull().default("pending"),
|
|
15432
|
+
attempt_count: integer("attempt_count").notNull().default(0),
|
|
15433
|
+
started_at: text("started_at"),
|
|
15434
|
+
completed_at: text("completed_at"),
|
|
15435
|
+
last_attempt_at: text("last_attempt_at"),
|
|
15436
|
+
safe_error_code: text("safe_error_code"),
|
|
15437
|
+
error_at: text("error_at"),
|
|
15438
|
+
lease_owner: text("lease_owner"),
|
|
15439
|
+
lease_acquired_at: text("lease_acquired_at"),
|
|
15440
|
+
lease_expires_at: text("lease_expires_at"),
|
|
15441
|
+
created_at: text("created_at").notNull(),
|
|
15442
|
+
updated_at: text("updated_at").notNull()
|
|
15443
|
+
}, (table) => [
|
|
15444
|
+
primaryKey({ columns: [
|
|
15445
|
+
table.canonical_project_identity,
|
|
15446
|
+
table.generation,
|
|
15447
|
+
table.step_key
|
|
15448
|
+
] }),
|
|
15449
|
+
uniqueIndex("idx_recipient_policy_reconciliation_steps_effect").on(table.effect_id),
|
|
15450
|
+
index("idx_recipient_policy_reconciliation_steps_status").on(table.canonical_project_identity, table.status),
|
|
15451
|
+
index("idx_recipient_policy_reconciliation_steps_pending_refresh").on(table.canonical_project_identity, table.generation, table.step_key).where(sql`${table.status} IN ('pending', 'running', 'failed') AND ${table.step_key} GLOB 'refresh-after-revocations-v2:*'`)
|
|
15452
|
+
]);
|
|
15453
|
+
sqliteTable("recipient_policy_deny_overlays", {
|
|
15454
|
+
canonical_project_identity: text("canonical_project_identity").notNull(),
|
|
15455
|
+
scope_id: text("scope_id").notNull(),
|
|
15456
|
+
device_id: text("device_id").notNull(),
|
|
15457
|
+
generation: integer("generation").notNull(),
|
|
15458
|
+
reason_code: text("reason_code").notNull(),
|
|
15459
|
+
created_at: text("created_at").notNull(),
|
|
15460
|
+
updated_at: text("updated_at").notNull()
|
|
15461
|
+
}, (table) => [primaryKey({ columns: [
|
|
15462
|
+
table.canonical_project_identity,
|
|
15463
|
+
table.scope_id,
|
|
15464
|
+
table.device_id
|
|
15465
|
+
] }), index("idx_recipient_policy_deny_overlays_scope_device").on(table.scope_id, table.device_id)]);
|
|
15466
|
+
//#endregion
|
|
15467
|
+
//#region ../core/src/coordinator-owner-profile-files.ts
|
|
15468
|
+
function ownerDatabaseFile(path) {
|
|
15469
|
+
const node = lstatSync(path, { bigint: true });
|
|
15470
|
+
if (!node.isFile() || node.isSymbolicLink()) return ownerProfileConflict();
|
|
15471
|
+
return `${node.dev}:${node.ino}`;
|
|
15472
|
+
}
|
|
15473
|
+
function ownerObject(value) {
|
|
15474
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return ownerProfileConflict();
|
|
15475
|
+
return value;
|
|
15476
|
+
}
|
|
15477
|
+
function assertFile(path, options = {}) {
|
|
15478
|
+
const stat = lstatSync(path);
|
|
15479
|
+
if (!stat.isFile() || stat.isSymbolicLink() || options.private && (stat.mode & 511) !== 384 || stat.size > 1048576) ownerProfileConflict();
|
|
15480
|
+
}
|
|
15481
|
+
function ownerFileSnapshot(path) {
|
|
15482
|
+
assertFile(path);
|
|
15483
|
+
const node = lstatSync(path, { bigint: true });
|
|
15484
|
+
return {
|
|
15485
|
+
text: readFileSync(path, "utf8"),
|
|
15486
|
+
dev: String(node.dev),
|
|
15487
|
+
ino: String(node.ino),
|
|
15488
|
+
ctime: String(node.ctimeNs)
|
|
15489
|
+
};
|
|
15490
|
+
}
|
|
15491
|
+
function readOwnerState(path) {
|
|
15492
|
+
assertFile(path, { private: true });
|
|
15493
|
+
return ownerObject(JSON.parse(readFileSync(path, "utf8")));
|
|
15494
|
+
}
|
|
15495
|
+
function syncDirectory(path) {
|
|
15496
|
+
const fd = openSync(path, "r");
|
|
15497
|
+
try {
|
|
15498
|
+
fsyncSync(fd);
|
|
15499
|
+
} finally {
|
|
15500
|
+
closeSync(fd);
|
|
15501
|
+
}
|
|
15502
|
+
}
|
|
15503
|
+
/** Sync an existing POSIX directory without changing its permissions or contents. */
|
|
15504
|
+
function syncOwnerProfileDirectory(path) {
|
|
15505
|
+
syncDirectory(path);
|
|
15506
|
+
}
|
|
15507
|
+
/** Durable, create-only publication; never overwrite a retained final request. */
|
|
15508
|
+
function retainOwnerState(path, value) {
|
|
15509
|
+
const text = JSON.stringify(value);
|
|
15510
|
+
if (text === void 0) ownerProfileConflict();
|
|
15511
|
+
const staged = stageOwnerState(path, text);
|
|
15512
|
+
linkSync(staged.path, path);
|
|
15513
|
+
syncDirectory(dirname(path));
|
|
15514
|
+
assertPublishedConfigLock(path, staged.snapshot);
|
|
15515
|
+
assertPublishedConfigLock(staged.path, staged.snapshot);
|
|
15516
|
+
unlinkSync(staged.path);
|
|
15517
|
+
syncDirectory(dirname(path));
|
|
15518
|
+
}
|
|
15519
|
+
function stageOwnerState(path, text) {
|
|
15520
|
+
const temporary = `${path}.tmp-${randomUUID()}`;
|
|
15521
|
+
const fd = openSync(temporary, "wx", 384);
|
|
15522
|
+
let identity;
|
|
15523
|
+
let closeAttempted = false;
|
|
15524
|
+
try {
|
|
15525
|
+
const node = fstatSync(fd, { bigint: true });
|
|
15526
|
+
identity = {
|
|
15527
|
+
dev: String(node.dev),
|
|
15528
|
+
ino: String(node.ino)
|
|
15529
|
+
};
|
|
15530
|
+
writeFileSync(fd, text);
|
|
15531
|
+
fsyncSync(fd);
|
|
15532
|
+
const snapshot = ownerFileSnapshot(temporary);
|
|
15533
|
+
if (snapshot.dev !== identity.dev || snapshot.ino !== identity.ino || snapshot.text !== text) ownerProfileConflict();
|
|
15534
|
+
syncDirectory(dirname(path));
|
|
15535
|
+
closeAttempted = true;
|
|
15536
|
+
closeSync(fd);
|
|
15537
|
+
return {
|
|
15538
|
+
path: temporary,
|
|
15539
|
+
snapshot
|
|
15540
|
+
};
|
|
15541
|
+
} catch (error) {
|
|
15542
|
+
if (identity) cleanupUnpublishedConfigLock(temporary, identity);
|
|
15543
|
+
if (!closeAttempted) closeSync(fd);
|
|
15544
|
+
throw error;
|
|
15545
|
+
}
|
|
15546
|
+
}
|
|
15547
|
+
function cleanupUnpublishedConfigLock(path, identity) {
|
|
15548
|
+
try {
|
|
15549
|
+
const node = lstatSync(path, { bigint: true });
|
|
15550
|
+
if (!node.isFile() || node.isSymbolicLink() || node.nlink !== 1n || String(node.dev) !== identity.dev || String(node.ino) !== identity.ino) return;
|
|
15551
|
+
unlinkSync(path);
|
|
15552
|
+
} catch {}
|
|
15553
|
+
}
|
|
15554
|
+
function assertPublishedConfigLock(path, staged) {
|
|
15555
|
+
const current = ownerFileSnapshot(path);
|
|
15556
|
+
if (current.dev !== staged.dev || current.ino !== staged.ino || current.text !== staged.text) ownerProfileConflict();
|
|
15557
|
+
return current;
|
|
15558
|
+
}
|
|
15559
|
+
//#endregion
|
|
15560
|
+
//#region ../core/src/account-owner-association-directory.ts
|
|
15561
|
+
function accountOwnerAssociationDirectory() {
|
|
15562
|
+
return join(homedir(), ".codemem", "account-owner-associations");
|
|
15563
|
+
}
|
|
15564
|
+
function checkDirectory(path, options = {}) {
|
|
15565
|
+
try {
|
|
15566
|
+
const stat = lstatSync(path);
|
|
15567
|
+
const unsafeMode = options.private ? (stat.mode & 511) !== 448 : (stat.mode & 18) !== 0;
|
|
15568
|
+
if (!stat.isDirectory() || stat.isSymbolicLink() || unsafeMode || typeof process.getuid === "function" && stat.uid !== process.getuid()) throw new Error("account_owner_association_invalid");
|
|
15569
|
+
return true;
|
|
15570
|
+
} catch (error) {
|
|
15571
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return false;
|
|
15572
|
+
throw error;
|
|
15573
|
+
}
|
|
15574
|
+
}
|
|
15575
|
+
/** Missing directories stay missing during reads. Never follow a state-directory symlink. */
|
|
15576
|
+
function readAccountOwnerAssociationDirectory(path) {
|
|
15577
|
+
return checkDirectory(dirname(path)) && checkDirectory(path, { private: true });
|
|
15578
|
+
}
|
|
15579
|
+
function createDirectory(path) {
|
|
15580
|
+
try {
|
|
15581
|
+
mkdirSync(path, { mode: 448 });
|
|
15582
|
+
} catch (error) {
|
|
15583
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") throw error;
|
|
15584
|
+
}
|
|
15585
|
+
}
|
|
15586
|
+
/** Sync ancestors on every retry: an earlier mkdir may have become visible without its fsync. */
|
|
15587
|
+
function retainAccountOwnerAssociationDirectory(path) {
|
|
15588
|
+
const parent = dirname(path);
|
|
15589
|
+
createDirectory(parent);
|
|
15590
|
+
if (!checkDirectory(parent)) throw new Error("account_owner_association_invalid");
|
|
15591
|
+
syncOwnerProfileDirectory(dirname(parent));
|
|
15592
|
+
createDirectory(path);
|
|
15593
|
+
if (!checkDirectory(path, { private: true })) throw new Error("account_owner_association_invalid");
|
|
15594
|
+
syncOwnerProfileDirectory(parent);
|
|
15595
|
+
}
|
|
15596
|
+
//#endregion
|
|
15597
|
+
//#region ../core/src/coordinator-auth-browser-style.ts
|
|
15598
|
+
/** Standalone auth pages mirror the Viewer's tokens without loading remote assets.
|
|
15599
|
+
* Keep these palette values aligned with ui/static/{tokens,themes}.css.
|
|
15600
|
+
* Separate origins cannot read the Viewer's saved theme; use the OS preference.
|
|
15601
|
+
*/
|
|
15602
|
+
var COORDINATOR_AUTH_STYLE = `
|
|
15603
|
+
:root{color-scheme:dark;--surface-0:#0b1016;--surface-1:#0f1720;--surface-2:#111c27;--text-primary:#e7f0f7;--text-secondary:#b7c5d3;--accent:#7ef0c2;--accent-hover:#9eff87;--border:rgba(126,240,194,.12);--border-strong:rgba(126,240,194,.28);--font-sans:"Geist",system-ui,-apple-system,sans-serif;--font-mono:"JetBrains Mono",ui-monospace,monospace;font-family:var(--font-sans);color:var(--text-primary);background:var(--surface-0)}
|
|
15604
|
+
*{box-sizing:border-box}body{margin:0;padding:clamp(1rem,5vw,4rem) 1rem;line-height:1.6}main{max-width:34rem;margin:0 auto;padding:clamp(1.25rem,4vw,2rem);border:1px solid var(--border-strong);border-radius:20px;background:var(--surface-1)}
|
|
15605
|
+
.brand{display:flex;align-items:center;gap:.6rem;margin:0 0 1.5rem;color:var(--accent);font-size:.875rem;font-weight:700;letter-spacing:.02em}.brand::before{content:"";width:.65rem;height:.65rem;border-radius:3px;background:var(--accent)}
|
|
15606
|
+
h1,h2{font-family:inherit;font-weight:600;line-height:1.25;letter-spacing:-.025em}h1{font-size:1.6rem;margin:0 0 1rem}h2{font-size:1.05rem;margin:0 0 .5rem}p{margin:.5rem 0}section{padding:1.25rem 0;border-bottom:1px solid var(--border)}.eyebrow{font-size:.75rem;letter-spacing:.08em;text-transform:uppercase;color:var(--text-secondary)}
|
|
15607
|
+
.profile{display:flex;align-items:center;gap:1rem}.profile-text{min-width:0;overflow-wrap:anywhere}.avatar{position:relative;display:inline-flex;align-items:center;justify-content:center;flex:0 0 3rem;width:3rem;height:3rem;border-radius:50%;background:var(--surface-2);color:var(--accent);overflow:hidden}.initials{font-weight:700}.avatar img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.note{font-size:.875rem;color:var(--text-secondary)}
|
|
15608
|
+
dl{margin:0}dt{font-size:.8rem;font-weight:600;color:var(--text-secondary);margin-top:1rem}dd{margin:.25rem 0 0}code{font-family:var(--font-mono);font-size:.8rem;overflow-wrap:anywhere;white-space:pre-wrap}
|
|
15609
|
+
.actions{display:flex;flex-wrap:wrap;gap:.75rem;margin:1.5rem 0 0}form{margin:0}button,.link{font:inherit;font-size:.875rem;font-weight:600;display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:.65rem 1rem;border:1px solid var(--border-strong);border-radius:10px;background:var(--accent);color:var(--surface-0);cursor:pointer;text-decoration:none}button.secondary{background:transparent;color:var(--accent)}a{color:var(--accent)}button:hover,.link:hover{background:var(--accent-hover);color:var(--surface-0)}:focus-visible{outline:3px solid var(--accent);outline-offset:4px}
|
|
15610
|
+
@media(prefers-color-scheme:light){:root{color-scheme:light;--surface-0:#f7faf8;--surface-1:#fff;--surface-2:#eef5f1;--text-primary:#0b1016;--text-secondary:#3f4d5c;--accent:#0f8a68;--accent-hover:#0a6f53;--border:rgba(15,138,104,.16);--border-strong:rgba(15,138,104,.3)}button,.link,button:hover,.link:hover{color:#fff}button.secondary{color:var(--accent)}button.secondary:hover{color:#fff}}
|
|
15611
|
+
@media(max-width:30rem){body{padding:1rem}.actions{flex-direction:column}.actions form,.actions button,.actions .link{width:100%}h1{font-size:1.4rem}}
|
|
15612
|
+
`;
|
|
15613
|
+
//#endregion
|
|
15614
|
+
//#region ../core/src/coordinator-account-link-receiver.ts
|
|
15615
|
+
function normalizeCoordinatorAccountLinkOrigin$1(value) {
|
|
15616
|
+
if (typeof value !== "string") return null;
|
|
15617
|
+
try {
|
|
15618
|
+
const url = new URL(value);
|
|
15619
|
+
if (!(url.protocol === "http:" && (url.hostname === "127.0.0.1" || url.hostname === "[::1]")) && url.protocol !== "https:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash || value.includes("\\") || /[\s\p{Cc}]/u.test(value) || value.includes("?") || value.includes("#") || value !== url.origin && value !== `${url.origin}/`) return null;
|
|
15620
|
+
return url.origin;
|
|
15621
|
+
} catch {
|
|
15622
|
+
return null;
|
|
15623
|
+
}
|
|
15624
|
+
}
|
|
15625
|
+
`${createHash("sha256").update(COORDINATOR_AUTH_STYLE).digest("base64")}`;
|
|
15626
|
+
//#endregion
|
|
15627
|
+
//#region ../core/src/account-owner-association.ts
|
|
15628
|
+
var PIN_FIELDS = [
|
|
15629
|
+
"databaseFile",
|
|
15630
|
+
"deviceId",
|
|
15631
|
+
"keyId",
|
|
15632
|
+
"fingerprint",
|
|
15633
|
+
"actorId",
|
|
15634
|
+
"coordinatorUrl",
|
|
15635
|
+
"coordinatorId",
|
|
15636
|
+
"groupId"
|
|
15637
|
+
];
|
|
15638
|
+
var REVIEW_FIELDS = [
|
|
15639
|
+
"ownerIdentityId",
|
|
15640
|
+
"attestationId",
|
|
15641
|
+
"reviewDigest"
|
|
15642
|
+
];
|
|
15643
|
+
function invalidAssociation() {
|
|
15644
|
+
throw new Error("account_owner_association_invalid");
|
|
15645
|
+
}
|
|
15646
|
+
function hash(value) {
|
|
15647
|
+
return createHash("sha256").update(value).digest("hex");
|
|
15648
|
+
}
|
|
15649
|
+
/** Local context only, not authority. Does not inspect the running actor or its config. */
|
|
15650
|
+
function captureAccountOwnerProfilePins$1(profile) {
|
|
15651
|
+
const origin = normalizeCoordinatorAccountLinkOrigin$1(profile.coordinatorUrl);
|
|
15652
|
+
const key = parseSshEd25519PublicKey(profile.publicKey);
|
|
15653
|
+
if (!origin || key.kind !== "ed25519" || !isAuthControllerId(profile.actorId) || !isAuthControllerId(profile.deviceId) || !isAuthControllerId(profile.groupId) || !isAuthControllerId(profile.coordinatorId) || typeof profile.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(profile.fingerprint)) return invalidAssociation();
|
|
15654
|
+
return {
|
|
15655
|
+
databaseFile: ownerDatabaseFile(profile.dbPath),
|
|
15656
|
+
deviceId: profile.deviceId,
|
|
15657
|
+
keyId: hash(key.blob),
|
|
15658
|
+
fingerprint: profile.fingerprint,
|
|
15659
|
+
actorId: profile.actorId,
|
|
15660
|
+
coordinatorUrl: origin,
|
|
15661
|
+
coordinatorId: profile.coordinatorId,
|
|
15662
|
+
groupId: profile.groupId
|
|
15663
|
+
};
|
|
15664
|
+
}
|
|
15665
|
+
var profilePins = captureAccountOwnerProfilePins$1;
|
|
15666
|
+
function ownData(value, fields) {
|
|
15667
|
+
if (!value || typeof value !== "object") return invalidAssociation();
|
|
15668
|
+
const prototype = Object.getPrototypeOf(value);
|
|
15669
|
+
if (prototype !== Object.prototype && prototype !== null) return invalidAssociation();
|
|
15670
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
15671
|
+
if (Reflect.ownKeys(descriptors).length !== fields.length) return invalidAssociation();
|
|
15672
|
+
return Object.fromEntries(fields.map((field) => {
|
|
15673
|
+
const descriptor = descriptors[field];
|
|
15674
|
+
if (!Object.hasOwn(descriptors, field) || !descriptor || !("value" in descriptor)) return invalidAssociation();
|
|
15675
|
+
return [field, descriptor.value];
|
|
15676
|
+
}));
|
|
15677
|
+
}
|
|
15678
|
+
function captureStoredPins(value) {
|
|
15679
|
+
if (typeof value.databaseFile !== "string" || !/^(0|[1-9][0-9]*):(0|[1-9][0-9]*)$/.test(value.databaseFile) || !isAuthControllerId(value.deviceId) || !isAuthControllerId(value.actorId) || !isAuthControllerId(value.coordinatorId) || !isAuthControllerId(value.groupId) || typeof value.keyId !== "string" || !/^[a-f0-9]{64}$/.test(value.keyId) || typeof value.fingerprint !== "string" || !/^[a-f0-9]{64}$/.test(value.fingerprint) || typeof value.coordinatorUrl !== "string" || normalizeCoordinatorAccountLinkOrigin$1(value.coordinatorUrl) !== value.coordinatorUrl) return invalidAssociation();
|
|
15680
|
+
return {
|
|
15681
|
+
databaseFile: value.databaseFile,
|
|
15682
|
+
deviceId: value.deviceId,
|
|
15683
|
+
keyId: value.keyId,
|
|
15684
|
+
fingerprint: value.fingerprint,
|
|
15685
|
+
actorId: value.actorId,
|
|
15686
|
+
coordinatorUrl: value.coordinatorUrl,
|
|
15687
|
+
coordinatorId: value.coordinatorId,
|
|
15688
|
+
groupId: value.groupId
|
|
15689
|
+
};
|
|
15690
|
+
}
|
|
15691
|
+
function pinsMatch(value, expected) {
|
|
15692
|
+
return PIN_FIELDS.every((field) => value[field] === expected[field]);
|
|
15693
|
+
}
|
|
15694
|
+
function captureStoredAssociation(input) {
|
|
15695
|
+
const value = ownData(input, [
|
|
15696
|
+
"version",
|
|
15697
|
+
...PIN_FIELDS,
|
|
15698
|
+
...REVIEW_FIELDS
|
|
15699
|
+
]);
|
|
15700
|
+
const pins = captureStoredPins(value);
|
|
15701
|
+
if (value.version !== 1 || !isAuthControllerId(value.ownerIdentityId) || !isAuthControllerId(value.attestationId) || typeof value.reviewDigest !== "string" || !/^[a-f0-9]{64}$/.test(value.reviewDigest)) return invalidAssociation();
|
|
15702
|
+
return {
|
|
15703
|
+
version: 1,
|
|
15704
|
+
...pins,
|
|
15705
|
+
ownerIdentityId: value.ownerIdentityId,
|
|
15706
|
+
attestationId: value.attestationId,
|
|
15707
|
+
reviewDigest: value.reviewDigest
|
|
15708
|
+
};
|
|
15709
|
+
}
|
|
15710
|
+
function associationPath(profile) {
|
|
15711
|
+
const origin = normalizeCoordinatorAccountLinkOrigin$1(profile.coordinatorUrl);
|
|
15712
|
+
if (!origin || !isAuthControllerId(profile.groupId)) return invalidAssociation();
|
|
15713
|
+
const scope = hash(JSON.stringify([
|
|
15714
|
+
origin,
|
|
15715
|
+
profile.coordinatorId,
|
|
15716
|
+
profile.groupId
|
|
15717
|
+
])).slice(0, 32);
|
|
15718
|
+
return `${resolve(profile.dbPath)}.account-owner-${scope}.json`;
|
|
15719
|
+
}
|
|
15720
|
+
function captureAssociation(value, profile) {
|
|
15721
|
+
const record = captureStoredAssociation(value);
|
|
15722
|
+
if (!pinsMatch(record, profilePins(profile))) return invalidAssociation();
|
|
15723
|
+
return record;
|
|
15724
|
+
}
|
|
15725
|
+
function captureGeneration(profile, expected) {
|
|
15726
|
+
const expectedPins = captureStoredPins(ownData(expected, PIN_FIELDS));
|
|
15727
|
+
const dbPath = resolve(profile.dbPath);
|
|
15728
|
+
const pins = profilePins({
|
|
15729
|
+
...profile,
|
|
15730
|
+
dbPath
|
|
15731
|
+
});
|
|
15732
|
+
if (!pinsMatch(pins, expectedPins)) return invalidAssociation();
|
|
15733
|
+
const scope = hash(JSON.stringify([
|
|
15734
|
+
pins.coordinatorUrl,
|
|
15735
|
+
pins.coordinatorId,
|
|
15736
|
+
pins.groupId
|
|
15737
|
+
])).slice(0, 32);
|
|
15738
|
+
const base = `${dbPath}.account-owner-${scope}`;
|
|
15739
|
+
const generation = hash(JSON.stringify(pins));
|
|
15740
|
+
const basenameBase = join(dirname(dbPath), `${hash(basename(dbPath))}.account-owner-${scope}`);
|
|
15741
|
+
return {
|
|
15742
|
+
dbPath,
|
|
15743
|
+
pins,
|
|
15744
|
+
legacyPath: `${base}.json`,
|
|
15745
|
+
previousPaths: [`${base}-${generation}.json`, `${basenameBase}-${generation}.json`],
|
|
15746
|
+
path: join(accountOwnerAssociationDirectory(), `.account-owner-${generation}.json`)
|
|
15747
|
+
};
|
|
15748
|
+
}
|
|
15749
|
+
function assertGenerationCurrent(profile, context) {
|
|
15750
|
+
try {
|
|
15751
|
+
if (resolve(profile.dbPath) === context.dbPath && pinsMatch(profilePins(profile), context.pins)) return;
|
|
15752
|
+
} catch {}
|
|
15753
|
+
throw new Error("account_owner_association_context_changed_outcome_unknown");
|
|
15754
|
+
}
|
|
15755
|
+
function readStoredAssociation(path, options = {}) {
|
|
15756
|
+
try {
|
|
15757
|
+
lstatSync(path);
|
|
15758
|
+
} catch (error) {
|
|
15759
|
+
if (error && typeof error === "object" && "code" in error) {
|
|
15760
|
+
if (error.code === "ENOENT") return null;
|
|
15761
|
+
if (options.historicalPath && error.code === "ENAMETOOLONG") return null;
|
|
15762
|
+
}
|
|
15763
|
+
return invalidAssociation();
|
|
15764
|
+
}
|
|
15765
|
+
try {
|
|
15766
|
+
const stat = lstatSync(path);
|
|
15767
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid()) return invalidAssociation();
|
|
15768
|
+
return captureStoredAssociation(readOwnerState(path));
|
|
15769
|
+
} catch {
|
|
15770
|
+
return invalidAssociation();
|
|
15771
|
+
}
|
|
15772
|
+
}
|
|
15773
|
+
function readGeneration(context) {
|
|
15774
|
+
const legacy = readStoredAssociation(context.legacyPath, { historicalPath: true });
|
|
15775
|
+
let found = null;
|
|
15776
|
+
const paths = [...context.previousPaths];
|
|
15777
|
+
if (readAccountOwnerAssociationDirectory(dirname(context.path))) paths.push(context.path);
|
|
15778
|
+
for (const path of paths) {
|
|
15779
|
+
const record = readStoredAssociation(path, { historicalPath: path !== context.path });
|
|
15780
|
+
if (!record) continue;
|
|
15781
|
+
if (!pinsMatch(record, context.pins)) return invalidAssociation();
|
|
15782
|
+
if (found && JSON.stringify(found.record) !== JSON.stringify(record)) return invalidAssociation();
|
|
15783
|
+
found = {
|
|
15784
|
+
record,
|
|
15785
|
+
path
|
|
15786
|
+
};
|
|
15787
|
+
}
|
|
15788
|
+
if (found) return found;
|
|
15789
|
+
if (legacy && pinsMatch(legacy, context.pins)) return {
|
|
15790
|
+
record: legacy,
|
|
15791
|
+
path: context.legacyPath
|
|
15792
|
+
};
|
|
15793
|
+
return null;
|
|
15794
|
+
}
|
|
15795
|
+
/**
|
|
15796
|
+
* Read without initializing the database, changing the actor, or granting access.
|
|
15797
|
+
* Expected pins opt trusted local callers into exact generations; omitted pins keep legacy behavior.
|
|
15798
|
+
*/
|
|
15799
|
+
function readAccountOwnerAssociation$1(profile, options) {
|
|
15800
|
+
if (options?.expectedProfilePins !== void 0) {
|
|
15801
|
+
const context = captureGeneration(profile, options.expectedProfilePins);
|
|
15802
|
+
const record = readGeneration(context);
|
|
15803
|
+
assertGenerationCurrent(profile, context);
|
|
15804
|
+
return record?.record ?? null;
|
|
15805
|
+
}
|
|
15806
|
+
const path = associationPath(profile);
|
|
15807
|
+
try {
|
|
15808
|
+
lstatSync(path);
|
|
15809
|
+
} catch (error) {
|
|
15810
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") return null;
|
|
15811
|
+
return invalidAssociation();
|
|
15812
|
+
}
|
|
15813
|
+
try {
|
|
15814
|
+
const stat = lstatSync(path);
|
|
15815
|
+
if (typeof process.getuid === "function" && stat.uid !== process.getuid()) return invalidAssociation();
|
|
15816
|
+
return captureAssociation(readOwnerState(path), profile);
|
|
15817
|
+
} catch {
|
|
15818
|
+
return invalidAssociation();
|
|
15819
|
+
}
|
|
15820
|
+
}
|
|
15821
|
+
/**
|
|
15822
|
+
* Create-only publication after review; an exact retry never replaces prior owner evidence.
|
|
15823
|
+
* Generation checks cover this profile object and the DB file, not runtime actor/config state.
|
|
15824
|
+
* A future recovery service must check that state before and after calling this helper.
|
|
15825
|
+
* Pathname replacement and restoration between checks is not detected atomically.
|
|
15826
|
+
*/
|
|
15827
|
+
function retainAccountOwnerAssociation(profile, input, options) {
|
|
15828
|
+
const review = ownData(input, REVIEW_FIELDS);
|
|
15829
|
+
if (options?.expectedProfilePins !== void 0) return retainGeneration(profile, review, captureGeneration(profile, options.expectedProfilePins));
|
|
15830
|
+
const record = captureAssociation({
|
|
15831
|
+
version: 1,
|
|
15832
|
+
...profilePins(profile),
|
|
15833
|
+
...review
|
|
15834
|
+
}, profile);
|
|
15835
|
+
const existing = readAccountOwnerAssociation$1(profile);
|
|
15836
|
+
if (existing) {
|
|
15837
|
+
if (JSON.stringify(existing) !== JSON.stringify(record)) return invalidAssociation();
|
|
15838
|
+
syncOwnerProfileDirectory(dirname(associationPath(profile)));
|
|
15839
|
+
return existing;
|
|
15840
|
+
}
|
|
15841
|
+
try {
|
|
15842
|
+
retainOwnerState(associationPath(profile), record);
|
|
15843
|
+
} catch (error) {
|
|
15844
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") return invalidAssociation();
|
|
15845
|
+
}
|
|
15846
|
+
const retained = readAccountOwnerAssociation$1(profile);
|
|
15847
|
+
if (!retained || JSON.stringify(retained) !== JSON.stringify(record)) return invalidAssociation();
|
|
15848
|
+
syncOwnerProfileDirectory(dirname(associationPath(profile)));
|
|
15849
|
+
return retained;
|
|
15850
|
+
}
|
|
15851
|
+
function retainGeneration(profile, review, context) {
|
|
15852
|
+
const record = captureStoredAssociation({
|
|
15853
|
+
version: 1,
|
|
15854
|
+
...context.pins,
|
|
15855
|
+
...review
|
|
15856
|
+
});
|
|
15857
|
+
const existing = readGeneration(context);
|
|
15858
|
+
assertGenerationCurrent(profile, context);
|
|
15859
|
+
if (existing && JSON.stringify(existing.record) !== JSON.stringify(record)) return invalidAssociation();
|
|
15860
|
+
retainAccountOwnerAssociationDirectory(dirname(context.path));
|
|
15861
|
+
assertGenerationCurrent(profile, context);
|
|
15862
|
+
if (existing?.path === context.path) {
|
|
15863
|
+
syncOwnerProfileDirectory(dirname(context.path));
|
|
15864
|
+
assertGenerationCurrent(profile, context);
|
|
15865
|
+
return existing.record;
|
|
15866
|
+
}
|
|
15867
|
+
try {
|
|
15868
|
+
retainOwnerState(context.path, record);
|
|
15869
|
+
} catch (error) {
|
|
15870
|
+
if (!error || typeof error !== "object" || !("code" in error) || error.code !== "EEXIST") return invalidAssociation();
|
|
15871
|
+
}
|
|
15872
|
+
assertGenerationCurrent(profile, context);
|
|
15873
|
+
const retained = readGeneration(context);
|
|
15874
|
+
if (!retained || JSON.stringify(retained.record) !== JSON.stringify(record)) return invalidAssociation();
|
|
15875
|
+
syncOwnerProfileDirectory(dirname(context.path));
|
|
15876
|
+
assertGenerationCurrent(profile, context);
|
|
15877
|
+
return retained.record;
|
|
15878
|
+
}
|
|
15879
|
+
//#endregion
|
|
15880
|
+
//#region src/account/owner-recovery-context.ts
|
|
15881
|
+
function recoverySha(value) {
|
|
15882
|
+
return createHash("sha256").update(value).digest("hex");
|
|
15883
|
+
}
|
|
15884
|
+
function captureDevice(context) {
|
|
15885
|
+
const before = statSync(context.dbPath, { bigint: true });
|
|
15886
|
+
const device = readAccountLinkDevice(context.dbPath);
|
|
15887
|
+
const publicKey = loadPublicKey(context.keysDir);
|
|
15888
|
+
const after = statSync(context.dbPath, { bigint: true });
|
|
15889
|
+
requireRecovery(before.isFile() && before.dev === after.dev && before.ino === after.ino && after.dev.toString() === context.fileIdentity.dev && after.ino.toString() === context.fileIdentity.ino && device.deviceId === context.deviceId && device.fingerprint === context.fingerprint && recoverySha(device.publicKey) === context.publicKeyHash && publicKey === device.publicKey);
|
|
15890
|
+
return device.publicKey;
|
|
15891
|
+
}
|
|
15892
|
+
function captureRecoveryContext(readContext) {
|
|
15893
|
+
let result = readContext();
|
|
15894
|
+
requireRecovery(!("error" in result) && result.groups.length > 0 && result.groups.length <= 25);
|
|
15895
|
+
if (!result.context) result = readContext(result.groups[0]);
|
|
15896
|
+
requireRecovery(!("error" in result) && result.setup === "ready" && result.context);
|
|
15897
|
+
const context = structuredClone(result.context);
|
|
15898
|
+
const groups = [...result.groups].sort();
|
|
15899
|
+
requireRecovery(groups.every(recoveryId) && new Set(groups).size === groups.length && recoveryId(context.identityId));
|
|
15900
|
+
return {
|
|
15901
|
+
context,
|
|
15902
|
+
groups,
|
|
15903
|
+
publicKey: captureDevice(context),
|
|
15904
|
+
semanticId: recoverySha(JSON.stringify([
|
|
15905
|
+
resolve(context.dbPath),
|
|
15906
|
+
context.keysDir === void 0 ? null : resolve(context.keysDir),
|
|
15907
|
+
context.configPath === void 0 ? null : resolve(context.configPath),
|
|
15908
|
+
context.fileIdentity,
|
|
15909
|
+
context.identityId,
|
|
15910
|
+
context.deviceId,
|
|
15911
|
+
context.publicKeyHash,
|
|
15912
|
+
context.fingerprint,
|
|
15913
|
+
context.coordinatorUrl,
|
|
15914
|
+
groups
|
|
15915
|
+
]))
|
|
15916
|
+
};
|
|
15917
|
+
}
|
|
15918
|
+
function recoveryProfile(capture, coordinatorId) {
|
|
15919
|
+
const context = capture.context;
|
|
15920
|
+
return {
|
|
15921
|
+
contextId: recoverySha(JSON.stringify([capture.semanticId, coordinatorId])),
|
|
15922
|
+
memoryActorId: context.identityId,
|
|
15923
|
+
deviceId: context.deviceId,
|
|
15924
|
+
fingerprint: context.fingerprint,
|
|
15925
|
+
coordinatorOrigin: context.coordinatorUrl,
|
|
15926
|
+
coordinatorId,
|
|
15927
|
+
configuredGroups: [...capture.groups]
|
|
15928
|
+
};
|
|
15929
|
+
}
|
|
15930
|
+
function assertRecoverySelection(selection, expected) {
|
|
15931
|
+
requireRecovery(JSON.stringify(selection.profile) === JSON.stringify(expected) && recoveryId(selection.existingOwnerId) && Array.isArray(selection.groups) && selection.groups.length > 0 && selection.groups.length <= 25 && new Set(selection.groups).size === selection.groups.length && selection.groups.every((group) => expected.configuredGroups.includes(group)));
|
|
15932
|
+
}
|
|
15933
|
+
function recoveryDestinations(capture, coordinatorId) {
|
|
15934
|
+
return new Map(capture.groups.map((groupId) => {
|
|
15935
|
+
const profile = {
|
|
15936
|
+
dbPath: capture.context.dbPath,
|
|
15937
|
+
deviceId: capture.context.deviceId,
|
|
15938
|
+
publicKey: capture.publicKey,
|
|
15939
|
+
fingerprint: capture.context.fingerprint,
|
|
15940
|
+
actorId: capture.context.identityId,
|
|
15941
|
+
coordinatorUrl: capture.context.coordinatorUrl,
|
|
15942
|
+
coordinatorId,
|
|
15943
|
+
groupId
|
|
15944
|
+
};
|
|
15945
|
+
return [groupId, {
|
|
15946
|
+
profile,
|
|
15947
|
+
pins: captureAccountOwnerProfilePins(profile)
|
|
15948
|
+
}];
|
|
15949
|
+
}));
|
|
15950
|
+
}
|
|
15951
|
+
function assertNoCompetingOwner(destinations, ownerIdentityId) {
|
|
15952
|
+
for (const { profile, pins } of destinations.values()) {
|
|
15953
|
+
const existing = readAccountOwnerAssociation(profile, { expectedProfilePins: pins });
|
|
15954
|
+
requireRecovery(!existing || existing.ownerIdentityId === ownerIdentityId);
|
|
15955
|
+
}
|
|
15956
|
+
}
|
|
15957
|
+
//#endregion
|
|
15958
|
+
//#region src/account/owner-recovery-service.ts
|
|
15959
|
+
function groupAction(state) {
|
|
15960
|
+
if (state === "ready") return "new";
|
|
15961
|
+
if (state === "restored") return "reuse";
|
|
15962
|
+
return "blocked";
|
|
15963
|
+
}
|
|
15964
|
+
function uniqueRows(rows) {
|
|
15965
|
+
return [...new Map(rows.map((row) => [JSON.stringify(row), row])).values()];
|
|
15966
|
+
}
|
|
15967
|
+
function staleDenial(error) {
|
|
15968
|
+
return error instanceof Error && "status" in error && error.status === 409 && "code" in error && error.code === "review_stale";
|
|
15969
|
+
}
|
|
15970
|
+
/** No routes or configuration composition: these callbacks are private server capabilities. */
|
|
15971
|
+
var AccountOwnerRecoveryService = class {
|
|
15972
|
+
#options;
|
|
15973
|
+
#handles = /* @__PURE__ */ new Map();
|
|
15974
|
+
#reservations = /* @__PURE__ */ new Set();
|
|
15975
|
+
#controller = new AbortController();
|
|
15976
|
+
#closed = false;
|
|
15977
|
+
constructor(options) {
|
|
15978
|
+
this.#options = options;
|
|
15979
|
+
}
|
|
15980
|
+
#now() {
|
|
15981
|
+
return (this.#options.now ?? Date.now)();
|
|
15982
|
+
}
|
|
15983
|
+
#assertCurrent(capture, reservation) {
|
|
15984
|
+
requireRecovery(!this.#closed, "unavailable");
|
|
15985
|
+
requireRecovery(!reservation || reservation.isCurrent(), "context_changed");
|
|
15986
|
+
requireRecovery(captureRecoveryContext(this.#options.readContext).semanticId === capture.semanticId && !this.#closed && (!reservation || reservation.isCurrent()), "context_changed");
|
|
15987
|
+
}
|
|
15988
|
+
#capture() {
|
|
15989
|
+
requireRecovery(!this.#closed, "unavailable");
|
|
15990
|
+
const capture = captureRecoveryContext(this.#options.readContext);
|
|
15991
|
+
this.#assertCurrent(capture);
|
|
15992
|
+
return capture;
|
|
15993
|
+
}
|
|
15994
|
+
async #discover(capture) {
|
|
15995
|
+
this.#assertCurrent(capture);
|
|
15996
|
+
const c = capture.context;
|
|
15997
|
+
const response = await (this.#options.readRemoteStatus ?? readCoordinatorAccountLinkStatus)({
|
|
15998
|
+
dbPath: c.dbPath,
|
|
15999
|
+
keysDir: c.keysDir,
|
|
16000
|
+
coordinatorUrl: c.coordinatorUrl,
|
|
16001
|
+
groupId: c.groupId,
|
|
16002
|
+
identityId: c.identityId,
|
|
16003
|
+
signal: this.#controller.signal
|
|
16004
|
+
});
|
|
16005
|
+
this.#assertCurrent(capture);
|
|
16006
|
+
requireRecovery(response.kind === "status" && recoveryId(response.status.coordinatorId) && response.status.deviceId === c.deviceId && response.status.fingerprint === c.fingerprint && response.status.groupId === c.groupId, "unavailable");
|
|
16007
|
+
return response.status.coordinatorId;
|
|
16008
|
+
}
|
|
16009
|
+
async getProfile() {
|
|
16010
|
+
const capture = this.#capture();
|
|
16011
|
+
const coordinatorId = await this.#discover(capture);
|
|
16012
|
+
this.#assertCurrent(capture);
|
|
16013
|
+
const reviewAccess = this.#options.readAdminSecret() ? "operator" : "request";
|
|
16014
|
+
this.#assertCurrent(capture);
|
|
16015
|
+
return {
|
|
16016
|
+
version: 1,
|
|
16017
|
+
profile: recoveryProfile(capture, coordinatorId),
|
|
16018
|
+
reviewAccess,
|
|
16019
|
+
candidates: []
|
|
16020
|
+
};
|
|
16021
|
+
}
|
|
16022
|
+
#reviewInput(capture, selection, groupId) {
|
|
16023
|
+
const adminSecret = this.#options.readAdminSecret();
|
|
16024
|
+
requireRecovery(typeof adminSecret === "string" && adminSecret.length > 0);
|
|
16025
|
+
this.#assertCurrent(capture);
|
|
16026
|
+
return {
|
|
16027
|
+
remoteUrl: capture.context.coordinatorUrl,
|
|
16028
|
+
adminSecret,
|
|
16029
|
+
purpose: "existing_account_owner",
|
|
16030
|
+
resolutionScope: RECOVERY_SCOPE,
|
|
16031
|
+
groupId,
|
|
16032
|
+
deviceId: capture.context.deviceId,
|
|
16033
|
+
fingerprint: capture.context.fingerprint,
|
|
16034
|
+
identityId: selection.existingOwnerId
|
|
16035
|
+
};
|
|
16036
|
+
}
|
|
16037
|
+
async #previewReview(entry, groupId) {
|
|
16038
|
+
const input = this.#reviewInput(entry.capture, entry.preview, groupId);
|
|
16039
|
+
this.#assertCurrent(entry.capture);
|
|
16040
|
+
const raw = await (this.#options.reviewOwner ?? coordinatorAuthControllerReviewAction)(input);
|
|
16041
|
+
this.#assertCurrent(entry.capture);
|
|
16042
|
+
const evidence = structuredClone(raw);
|
|
16043
|
+
entry.reviews.set(groupId, validateRecoveryReview(evidence, entry.preview, groupId));
|
|
16044
|
+
entry.evidence.set(groupId, evidence);
|
|
16045
|
+
}
|
|
16046
|
+
#newReview(request, capture) {
|
|
16047
|
+
return {
|
|
16048
|
+
capture,
|
|
16049
|
+
destinations: recoveryDestinations(capture, request.profile.coordinatorId),
|
|
16050
|
+
purpose: request.purpose,
|
|
16051
|
+
createdAt: this.#now(),
|
|
16052
|
+
reviews: /* @__PURE__ */ new Map(),
|
|
16053
|
+
evidence: /* @__PURE__ */ new Map(),
|
|
16054
|
+
preview: {
|
|
16055
|
+
...structuredClone(request),
|
|
16056
|
+
scope: RECOVERY_SCOPE,
|
|
16057
|
+
approvalDigest: randomBytes(32).toString("hex"),
|
|
16058
|
+
groupPreviews: [],
|
|
16059
|
+
history: {
|
|
16060
|
+
complete: false,
|
|
16061
|
+
rows: [],
|
|
16062
|
+
operatorHelp: "Ask the coordinator operator to review the existing account owner and provide receipt references."
|
|
16063
|
+
},
|
|
16064
|
+
reviewedWeakLabels: []
|
|
16065
|
+
}
|
|
16066
|
+
};
|
|
16067
|
+
}
|
|
16068
|
+
#projectReviews(entry) {
|
|
16069
|
+
const reviews = [...entry.reviews.values()];
|
|
16070
|
+
entry.preview.history = {
|
|
16071
|
+
complete: true,
|
|
16072
|
+
rows: uniqueRows(reviews.flatMap((review) => review.rows))
|
|
16073
|
+
};
|
|
16074
|
+
entry.preview.reviewedWeakLabels = uniqueRows(reviews.flatMap((review) => review.labels));
|
|
16075
|
+
entry.preview.groupPreviews = entry.preview.groups.map((groupId) => {
|
|
16076
|
+
const review = entry.reviews.get(groupId);
|
|
16077
|
+
requireRecovery(review);
|
|
16078
|
+
return {
|
|
16079
|
+
groupId,
|
|
16080
|
+
action: groupAction(review.state),
|
|
16081
|
+
reason: review.state === "needs_review" ? review.reasons.join("; ") : "Fresh coordinator owner review."
|
|
16082
|
+
};
|
|
16083
|
+
});
|
|
16084
|
+
}
|
|
16085
|
+
#retainHandle(entry) {
|
|
16086
|
+
for (const [key, value] of this.#handles) if (this.#now() - value.createdAt >= 3e5) this.#handles.delete(key);
|
|
16087
|
+
while (this.#handles.size >= 16) {
|
|
16088
|
+
const oldest = this.#handles.keys().next().value;
|
|
16089
|
+
requireRecovery(oldest);
|
|
16090
|
+
this.#handles.delete(oldest);
|
|
16091
|
+
}
|
|
16092
|
+
this.#handles.set(entry.preview.approvalDigest, entry);
|
|
16093
|
+
return structuredClone(entry.preview);
|
|
16094
|
+
}
|
|
16095
|
+
async preview(request) {
|
|
16096
|
+
const capture = this.#capture();
|
|
16097
|
+
const coordinatorId = await this.#discover(capture);
|
|
16098
|
+
this.#assertCurrent(capture);
|
|
16099
|
+
assertRecoverySelection(request, recoveryProfile(capture, coordinatorId));
|
|
16100
|
+
requireRecovery(recoveryId(request.requestId) && (request.purpose === "review" || request.purpose === "check_status"));
|
|
16101
|
+
const entry = this.#newReview(request, capture);
|
|
16102
|
+
const admin = this.#options.readAdminSecret();
|
|
16103
|
+
this.#assertCurrent(capture);
|
|
16104
|
+
if (admin) {
|
|
16105
|
+
for (const groupId of entry.preview.groups) await this.#previewReview(entry, groupId);
|
|
16106
|
+
this.#projectReviews(entry);
|
|
16107
|
+
} else await this.#requestAssistance(entry);
|
|
16108
|
+
this.#assertCurrent(capture);
|
|
16109
|
+
return this.#retainHandle(entry);
|
|
16110
|
+
}
|
|
16111
|
+
async #requestAssistance(entry) {
|
|
16112
|
+
for (const groupId of entry.preview.groups) {
|
|
16113
|
+
this.#assertCurrent(entry.capture);
|
|
16114
|
+
await (this.#options.readRemoteOwnerStatus ?? readCoordinatorResolvedAccountOwnerStatus)({
|
|
16115
|
+
...entry.capture.context,
|
|
16116
|
+
groupId,
|
|
16117
|
+
ownerIdentityId: entry.preview.existingOwnerId,
|
|
16118
|
+
signal: this.#controller.signal
|
|
16119
|
+
});
|
|
16120
|
+
this.#assertCurrent(entry.capture);
|
|
16121
|
+
}
|
|
16122
|
+
entry.preview.groupPreviews = entry.preview.groups.map((groupId) => ({
|
|
16123
|
+
groupId,
|
|
16124
|
+
action: "blocked",
|
|
16125
|
+
reason: "An operator must review the full retained history."
|
|
16126
|
+
}));
|
|
16127
|
+
}
|
|
16128
|
+
#reserve() {
|
|
16129
|
+
requireRecovery(!this.#closed, "unavailable");
|
|
16130
|
+
const reservation = this.#options.registry.tryReserveRecovery?.();
|
|
16131
|
+
requireRecovery(reservation, "busy");
|
|
16132
|
+
requireRecovery(reservation.isCurrent(), "context_changed");
|
|
16133
|
+
this.#reservations.add(reservation);
|
|
16134
|
+
return reservation;
|
|
16135
|
+
}
|
|
16136
|
+
#release(reservation) {
|
|
16137
|
+
reservation.release();
|
|
16138
|
+
this.#reservations.delete(reservation);
|
|
16139
|
+
}
|
|
16140
|
+
async confirm(input) {
|
|
16141
|
+
requireRecovery(!this.#closed, "unavailable");
|
|
16142
|
+
const entry = this.#handles.get(input.approvalDigest);
|
|
16143
|
+
requireRecovery(entry && JSON.stringify(entry.preview) === JSON.stringify(input.preview));
|
|
16144
|
+
if (entry.outcome) return structuredClone(await entry.outcome);
|
|
16145
|
+
requireRecovery(this.#now() - entry.createdAt < 3e5 && entry.purpose === "review" && entry.preview.history.complete && entry.preview.groupPreviews.every((group) => group.action !== "blocked"));
|
|
16146
|
+
const reservation = this.#reserve();
|
|
16147
|
+
entry.outcome = this.#applyReview(entry, reservation).finally(() => this.#release(reservation));
|
|
16148
|
+
return structuredClone(await entry.outcome);
|
|
16149
|
+
}
|
|
16150
|
+
async #applyReview(entry, reservation) {
|
|
16151
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16152
|
+
assertNoCompetingOwner(entry.destinations, entry.preview.existingOwnerId);
|
|
16153
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16154
|
+
const groups = [];
|
|
16155
|
+
let verifiedReceipt;
|
|
16156
|
+
for (const groupId of entry.preview.groups) {
|
|
16157
|
+
const applied = await this.#applyGroup(entry, groupId, reservation, { verifiedReceipt });
|
|
16158
|
+
groups.push(applied.outcome);
|
|
16159
|
+
verifiedReceipt ??= applied.verifiedReceipt;
|
|
16160
|
+
}
|
|
16161
|
+
return {
|
|
16162
|
+
preview: structuredClone(entry.preview),
|
|
16163
|
+
groups
|
|
16164
|
+
};
|
|
16165
|
+
}
|
|
16166
|
+
async #confirmNew(entry, groupId, reservation) {
|
|
16167
|
+
const review = entry.reviews.get(groupId);
|
|
16168
|
+
requireRecovery(review?.digest);
|
|
16169
|
+
const input = this.#reviewInput(entry.capture, entry.preview, groupId);
|
|
16170
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16171
|
+
const raw = await (this.#options.reviewOwner ?? coordinatorAuthControllerReviewAction)({
|
|
16172
|
+
...input,
|
|
16173
|
+
confirmOwnerResolutionDigest: review.digest,
|
|
16174
|
+
assertBeforeOwnerResolutionConfirm: () => this.#assertCurrent(entry.capture, reservation)
|
|
16175
|
+
});
|
|
16176
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16177
|
+
const confirmed = validateRecoveryReview(structuredClone(raw), entry.preview, groupId);
|
|
16178
|
+
requireRecovery(confirmed.state === "ready" && confirmed.digest === review.digest && confirmed.receipt);
|
|
16179
|
+
return confirmed.receipt;
|
|
16180
|
+
}
|
|
16181
|
+
async #applyGroup(entry, groupId, reservation, options) {
|
|
16182
|
+
try {
|
|
16183
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16184
|
+
const review = entry.reviews.get(groupId);
|
|
16185
|
+
requireRecovery(review && review.state !== "needs_review");
|
|
16186
|
+
let receipt = options.verifiedReceipt ?? review.receipt;
|
|
16187
|
+
if (!options.verifiedReceipt && review.state === "ready") receipt = await this.#confirmNew(entry, groupId, reservation);
|
|
16188
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16189
|
+
requireRecovery(receipt);
|
|
16190
|
+
const outcome = await this.#verifyAndRetain(entry, groupId, receipt, reservation, { afterRemoteConfirmation: Boolean(options.verifiedReceipt) || review.state === "ready" });
|
|
16191
|
+
if (outcome.status !== "confirmed") return { outcome };
|
|
16192
|
+
return {
|
|
16193
|
+
outcome,
|
|
16194
|
+
verifiedReceipt: structuredClone(receipt)
|
|
16195
|
+
};
|
|
16196
|
+
} catch (error) {
|
|
16197
|
+
return { outcome: {
|
|
16198
|
+
groupId,
|
|
16199
|
+
status: staleDenial(error) ? "failed" : "unknown",
|
|
16200
|
+
message: "Check coordinator status or import original receipts before trying again."
|
|
16201
|
+
} };
|
|
16202
|
+
}
|
|
16203
|
+
}
|
|
16204
|
+
async #verifyAndRetain(entry, groupId, receipt, reservation, options = {}) {
|
|
16205
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16206
|
+
const response = await (this.#options.readRemoteReceiptStatus ?? readCoordinatorResolvedAccountOwnerReceiptStatus)({
|
|
16207
|
+
dbPath: entry.capture.context.dbPath,
|
|
16208
|
+
keysDir: entry.capture.context.keysDir,
|
|
16209
|
+
coordinatorUrl: entry.capture.context.coordinatorUrl,
|
|
16210
|
+
groupId,
|
|
16211
|
+
ownerIdentityId: entry.preview.existingOwnerId,
|
|
16212
|
+
receipt: structuredClone(receipt),
|
|
16213
|
+
signal: this.#controller.signal
|
|
16214
|
+
});
|
|
16215
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16216
|
+
if (!this.#validOwnerProof(response, entry, groupId)) return {
|
|
16217
|
+
groupId,
|
|
16218
|
+
status: options.afterRemoteConfirmation ? "unknown" : "failed",
|
|
16219
|
+
message: "The original owner receipt could not be verified with a linked account."
|
|
16220
|
+
};
|
|
16221
|
+
this.#publish(entry, groupId, receipt, reservation);
|
|
16222
|
+
return {
|
|
16223
|
+
groupId,
|
|
16224
|
+
status: "confirmed",
|
|
16225
|
+
message: "Verified existing owner context retained; memory authorship is unchanged."
|
|
16226
|
+
};
|
|
16227
|
+
}
|
|
16228
|
+
#validOwnerProof(response, entry, groupId) {
|
|
16229
|
+
if (response.kind !== "owner_status") return false;
|
|
16230
|
+
const status = response.status, p = entry.preview.profile;
|
|
16231
|
+
return status.version === 1 && status.coordinatorId === p.coordinatorId && status.groupId === groupId && status.deviceId === p.deviceId && status.fingerprint === p.fingerprint && status.state === "owner_confirmed" && status.ownerIdentity === entry.preview.existingOwnerId && status.profileState === "linked" && status.profile !== null && typeof status.profile === "object" && !Array.isArray(status.profile);
|
|
16232
|
+
}
|
|
16233
|
+
#publish(entry, groupId, receipt, reservation) {
|
|
16234
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16235
|
+
assertNoCompetingOwner(entry.destinations, entry.preview.existingOwnerId);
|
|
16236
|
+
const destination = entry.destinations.get(groupId);
|
|
16237
|
+
requireRecovery(destination);
|
|
16238
|
+
const { profile, pins } = destination;
|
|
16239
|
+
const existing = readAccountOwnerAssociation(profile, { expectedProfilePins: pins });
|
|
16240
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16241
|
+
if (!existing) retainAccountOwnerAssociation(profile, {
|
|
16242
|
+
ownerIdentityId: entry.preview.existingOwnerId,
|
|
16243
|
+
attestationId: receipt.attestationId,
|
|
16244
|
+
reviewDigest: receipt.reviewEvidenceDigest
|
|
16245
|
+
}, { expectedProfilePins: pins });
|
|
16246
|
+
this.#assertCurrent(entry.capture, reservation);
|
|
16247
|
+
}
|
|
16248
|
+
async importReply(input) {
|
|
16249
|
+
const reservation = this.#reserve();
|
|
16250
|
+
try {
|
|
16251
|
+
const capture = this.#capture();
|
|
16252
|
+
const coordinatorId = await this.#discover(capture);
|
|
16253
|
+
this.#assertCurrent(capture, reservation);
|
|
16254
|
+
assertRecoverySelection(input.selection, recoveryProfile(capture, coordinatorId));
|
|
16255
|
+
const receipts = parseRecoveryReply(input.replyText, input.selection);
|
|
16256
|
+
const entry = this.#newReview({
|
|
16257
|
+
...input.selection,
|
|
16258
|
+
requestId: randomBytes(16).toString("hex"),
|
|
16259
|
+
purpose: "review"
|
|
16260
|
+
}, capture);
|
|
16261
|
+
assertNoCompetingOwner(entry.destinations, entry.preview.existingOwnerId);
|
|
16262
|
+
const groups = [];
|
|
16263
|
+
for (const [groupId, receipt] of receipts) groups.push(await this.#importGroup(entry, groupId, receipt, reservation));
|
|
16264
|
+
return {
|
|
16265
|
+
preview: entry.preview,
|
|
16266
|
+
groups
|
|
16267
|
+
};
|
|
16268
|
+
} finally {
|
|
16269
|
+
this.#release(reservation);
|
|
16270
|
+
}
|
|
16271
|
+
}
|
|
16272
|
+
async #importGroup(entry, groupId, receipt, reservation) {
|
|
16273
|
+
try {
|
|
16274
|
+
return await this.#verifyAndRetain(entry, groupId, receipt, reservation);
|
|
16275
|
+
} catch {
|
|
16276
|
+
return {
|
|
16277
|
+
groupId,
|
|
16278
|
+
status: "unknown",
|
|
16279
|
+
message: "Context or proof outcome changed; check status before importing again."
|
|
16280
|
+
};
|
|
16281
|
+
}
|
|
16282
|
+
}
|
|
16283
|
+
async close() {
|
|
16284
|
+
this.#closed = true;
|
|
16285
|
+
this.#controller.abort();
|
|
16286
|
+
this.#handles.clear();
|
|
16287
|
+
for (const reservation of this.#reservations) this.#release(reservation);
|
|
16288
|
+
}
|
|
16289
|
+
};
|
|
16290
|
+
function createAccountOwnerRecoveryService(options) {
|
|
16291
|
+
return new AccountOwnerRecoveryService(options);
|
|
16292
|
+
}
|
|
16293
|
+
//#endregion
|
|
13678
16294
|
//#region src/index.ts
|
|
13679
16295
|
/**
|
|
13680
16296
|
* @codemem/server — HTTP server (viewer, sync, API).
|
|
@@ -13687,16 +16303,28 @@ function createAccountContextReader(options) {
|
|
|
13687
16303
|
*/
|
|
13688
16304
|
/** Shared store instance — SQLite WAL mode handles concurrent reads safely. */
|
|
13689
16305
|
var sharedStore = null;
|
|
16306
|
+
var sharedStoreClosePromise = null;
|
|
13690
16307
|
/** Get (or create) the shared store instance. Exported so the sweeper can share it. */
|
|
13691
16308
|
function getStore() {
|
|
13692
16309
|
if (!sharedStore) sharedStore = new MemoryStore(resolveDbPath());
|
|
13693
16310
|
return sharedStore;
|
|
13694
16311
|
}
|
|
13695
|
-
/** Close
|
|
16312
|
+
/** Close a synchronous-only shared store. Async pack owners use closeStoreAsync(). */
|
|
13696
16313
|
function closeStore() {
|
|
13697
16314
|
sharedStore?.close();
|
|
13698
16315
|
sharedStore = null;
|
|
13699
16316
|
}
|
|
16317
|
+
/** Drain semantic work before closing SQLite and releasing the shared store. */
|
|
16318
|
+
function closeStoreAsync() {
|
|
16319
|
+
if (sharedStoreClosePromise) return sharedStoreClosePromise;
|
|
16320
|
+
if (!sharedStore) return Promise.resolve();
|
|
16321
|
+
const store = sharedStore;
|
|
16322
|
+
sharedStoreClosePromise = store.closeAsync().then(() => {
|
|
16323
|
+
if (sharedStore === store) sharedStore = null;
|
|
16324
|
+
sharedStoreClosePromise = null;
|
|
16325
|
+
});
|
|
16326
|
+
return sharedStoreClosePromise;
|
|
16327
|
+
}
|
|
13700
16328
|
function registerStaticRoutes(app) {
|
|
13701
16329
|
const staticRoot = process.env.CODEMEM_VIEWER_STATIC_DIR ?? join(import.meta.dirname ?? ".", "../static");
|
|
13702
16330
|
app.use("/assets/*", async (c, next) => {
|
|
@@ -13729,6 +16357,7 @@ function registerAccountRoutes(app, options) {
|
|
|
13729
16357
|
const account = createAccountGuard(options);
|
|
13730
16358
|
app.use("*", account.middleware);
|
|
13731
16359
|
app.route("/", account.routes);
|
|
16360
|
+
if (options.ownerRecovery) app.route("/", accountOwnerRecoveryRoutes(options.ownerRecovery));
|
|
13732
16361
|
if (options.readContext) app.route("/", accountStatusRoutes({
|
|
13733
16362
|
...options,
|
|
13734
16363
|
readContext: options.readContext
|
|
@@ -13752,12 +16381,14 @@ function createApp(opts) {
|
|
|
13752
16381
|
const viewerTargetGuard = viewerTargetQueryGuard(storeFactory);
|
|
13753
16382
|
app.use("/api/memories/*", viewerTargetGuard);
|
|
13754
16383
|
app.use("/api/memory", viewerTargetGuard);
|
|
16384
|
+
app.use("/api/pi/sessions/search", viewerTargetGuard);
|
|
13755
16385
|
app.route("/", healthRoutes(storeFactory));
|
|
13756
16386
|
app.route("/", diagnosticsRoutes(storeFactory));
|
|
13757
16387
|
app.route("/", statsRoutes(storeFactory));
|
|
13758
16388
|
app.route("/", memoryRoutes(storeFactory));
|
|
13759
16389
|
app.route("/", memoryToolRoutes(storeFactory));
|
|
13760
16390
|
app.route("/", packTransportRoutes(storeFactory));
|
|
16391
|
+
app.route("/", fileContextRoutes(storeFactory));
|
|
13761
16392
|
app.route("/", observerStatusRoutes({
|
|
13762
16393
|
getStore: storeFactory,
|
|
13763
16394
|
getSweeper: () => sweeper,
|
|
@@ -13812,6 +16443,6 @@ function createSyncApp(opts) {
|
|
|
13812
16443
|
return app;
|
|
13813
16444
|
}
|
|
13814
16445
|
//#endregion
|
|
13815
|
-
export { FileRawEventInbox, VERSION, advancePendingProjectShares, advancePendingProjectSharesOperation, advanceProjectShareOperation, closeStore, createAccountContextReader, createAccountLinkRegistry, createApp, createRecipientPolicyReconcilerEffects, createSyncApp, createViewerRawEventInbox, getStore, listRecipientPolicyReconciliationStatus, recipientPolicyCapabilityFromStatus, reconcileConfiguredCoordinatorEnrollment, reconcileRecipientPolicyProjects, reconcileRecipientPolicyProjectsOperation, resolveRawEventInboxDirectory };
|
|
16446
|
+
export { FileRawEventInbox, VERSION, advancePendingProjectShares, advancePendingProjectSharesOperation, advanceProjectShareOperation, closeStore, closeStoreAsync, createAccountContextReader, createAccountLinkRegistry, createAccountOwnerRecoveryService, createApp, createRecipientPolicyReconcilerEffects, createSyncApp, createViewerRawEventInbox, getStore, listRecipientPolicyReconciliationStatus, recipientPolicyCapabilityFromStatus, reconcileConfiguredCoordinatorEnrollment, reconcileRecipientPolicyProjects, reconcileRecipientPolicyProjectsOperation, resolveRawEventInboxDirectory };
|
|
13816
16447
|
|
|
13817
16448
|
//# sourceMappingURL=index.js.map
|