@codemem/server 0.31.4 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
- import { CODEMEM_CONFIG_ENV_OVERRIDES, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, MemoryStore, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, applyReplicationOps, buildBaseUrl, buildFilterClausesWithContext, buildRawEventEnvelopeFromHook, canonicalMemoryKind, canonicalWorkspaceIdentity, cleanupNonces, coordinatorArchiveGroupAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, createCoordinatorReciprocalApproval, deleteProjectScopeSettingsMapping, ensureDeviceIdentity, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorGroupPreference, getSemanticIndexDiagnostics, getSyncResetState, isSummaryLikeMemory, listCoordinatorJoinRequests, listInboundScopeRejections, listMaintenanceJobs, listObserverProviderOptions, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, negotiateSyncCapability, normalizeSyncCapability, parseStrictInteger, parseSyncScopeRequest, personalScopeGrantStatusForPeer, probeAvailableCredentials, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recordNonce, rejectInboundScopeFailures, requestJson, resolveDbPath, runSyncPass, schema, stripJsonComments, stripPrivateObj, stripTrailingCommas, summarizeInboundScopeRejections, syncScopeResetRequiredPayload, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, verifySignature, writeCodememConfigFile } from "@codemem/core";
3
+ import { CODEMEM_CONFIG_ENV_OVERRIDES, DEFAULT_TIME_WINDOW_S, LEGACY_SHARED_REVIEW_SCOPE_ID, LOCAL_DEFAULT_SCOPE_ID, LOCAL_SYNC_CAPABILITY, MemoryStore, SYNC_SCOPE_QUERY_PARAM, VERSION, VERSION as VERSION$1, addSyncScopeToBoundary, analyzeProjectScopeMappingChangeGuardrails, applyReplicationOps, buildBaseUrl, buildFilterClausesWithContext, buildRawEventEnvelopeFromHook, canonicalMemoryKind, canonicalWorkspaceIdentity, cleanupNonces, coordinatorArchiveGroupAction, coordinatorCreateGroupAction, coordinatorCreateInviteAction, coordinatorCreateScopeAction, coordinatorDisableDeviceAction, coordinatorEnableDeviceAction, coordinatorGrantScopeMembershipAction, coordinatorImportInviteAction, coordinatorListBootstrapGrantsAction, coordinatorListDevicesAction, coordinatorListGroupsAction, coordinatorListJoinRequestsAction, coordinatorListScopeMembershipsAction, coordinatorListScopesAction, coordinatorRemoveDeviceAction, coordinatorRenameDeviceAction, coordinatorRenameGroupAction, coordinatorReviewJoinRequestAction, coordinatorRevokeBootstrapGrantAction, coordinatorRevokeScopeMembershipAction, coordinatorStatusSnapshot, coordinatorUnarchiveGroupAction, coordinatorUpdateScopeAction, createCoordinatorReciprocalApproval, defaultSpaceScopeIdForGroup, deleteProjectScopeSettingsMapping, ensureDeviceIdentity, extractReplicationOps, filterReplicationOpsForSyncWithStatus, fingerprintPublicKey, fromJson, getCodememConfigPath, getCodememEnvOverrides, getCoordinatorGroupPreference, getSemanticIndexDiagnostics, getSyncResetState, isSummaryLikeMemory, listCoordinatorJoinRequests, listInboundScopeRejections, listMaintenanceJobs, listObserverProviderOptions, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, negotiateSyncCapability, normalizeSyncCapability, parseStrictInteger, parseSyncScopeRequest, personalScopeGrantStatusForPeer, probeAvailableCredentials, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recordNonce, rejectInboundScopeFailures, requestJson, resolveDbPath, runSyncPass, schema, stripJsonComments, stripPrivateObj, stripTrailingCommas, summarizeInboundScopeRejections, syncScopeResetRequiredPayload, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, verifySignature, writeCodememConfigFile } from "@codemem/core";
4
4
  import { serveStatic } from "@hono/node-server/serve-static";
5
5
  import { Hono } from "hono";
6
6
  import { createMiddleware } from "hono/factory";
@@ -1707,6 +1707,75 @@ function coordinatorAdminStatusPayload(config = readCoordinatorSyncConfig()) {
1707
1707
  has_groups: hasGroups
1708
1708
  };
1709
1709
  }
1710
+ async function ensureDefaultSpaceForTeam(opts) {
1711
+ const coordinatorId = opts.config.syncCoordinatorUrl || null;
1712
+ if (!coordinatorId) throw new Error("coordinator_not_configured");
1713
+ const scopeId = defaultSpaceScopeIdForGroup(opts.groupId);
1714
+ const label = opts.displayName || opts.groupId;
1715
+ const scope = (await coordinatorListScopesAction({
1716
+ groupId: opts.groupId,
1717
+ includeInactive: false,
1718
+ remoteUrl: opts.config.syncCoordinatorUrl || null,
1719
+ adminSecret: opts.config.syncCoordinatorAdminSecret || null
1720
+ })).find((scope) => scope.scope_id === scopeId) ?? null ?? await coordinatorCreateScopeAction({
1721
+ groupId: opts.groupId,
1722
+ scopeId,
1723
+ label,
1724
+ kind: "team_default",
1725
+ authorityType: "coordinator",
1726
+ coordinatorId,
1727
+ membershipEpoch: 1,
1728
+ status: "active",
1729
+ remoteUrl: opts.config.syncCoordinatorUrl || null,
1730
+ adminSecret: opts.config.syncCoordinatorAdminSecret || null
1731
+ });
1732
+ const [deviceId] = ensureDeviceIdentity(opts.store.db);
1733
+ return {
1734
+ scope,
1735
+ membership: await coordinatorGrantScopeMembershipAction({
1736
+ groupId: opts.groupId,
1737
+ scopeId,
1738
+ deviceId,
1739
+ role: "admin",
1740
+ membershipEpoch: 1,
1741
+ coordinatorId,
1742
+ remoteUrl: opts.config.syncCoordinatorUrl || null,
1743
+ adminSecret: opts.config.syncCoordinatorAdminSecret || null
1744
+ }),
1745
+ preferences: upsertCoordinatorGroupPreference(opts.store.db, {
1746
+ coordinator_id: coordinatorId,
1747
+ group_id: opts.groupId,
1748
+ default_space_scope_id: scopeId,
1749
+ auto_grant_default_space_on_join: true
1750
+ })
1751
+ };
1752
+ }
1753
+ async function maybeGrantDefaultSpaceOnJoin(opts) {
1754
+ const coordinatorId = opts.config.syncCoordinatorUrl || null;
1755
+ if (!coordinatorId) return null;
1756
+ const preferences = getCoordinatorGroupPreference(opts.store.db, coordinatorId, opts.groupId);
1757
+ if (!preferences?.auto_grant_default_space_on_join) return null;
1758
+ const scopeId = preferences.default_space_scope_id?.trim();
1759
+ if (!scopeId) return null;
1760
+ if (scopeId !== defaultSpaceScopeIdForGroup(opts.groupId)) return null;
1761
+ const defaultScope = (await coordinatorListScopesAction({
1762
+ groupId: opts.groupId,
1763
+ includeInactive: false,
1764
+ remoteUrl: opts.config.syncCoordinatorUrl || null,
1765
+ adminSecret: opts.config.syncCoordinatorAdminSecret || null
1766
+ })).find((scope) => scope.scope_id === scopeId) ?? null;
1767
+ if (!defaultScope || defaultScope.kind !== "team_default") return null;
1768
+ return await coordinatorGrantScopeMembershipAction({
1769
+ groupId: opts.groupId,
1770
+ scopeId,
1771
+ deviceId: opts.deviceId,
1772
+ role: "member",
1773
+ membershipEpoch: 1,
1774
+ coordinatorId,
1775
+ remoteUrl: opts.config.syncCoordinatorUrl || null,
1776
+ adminSecret: opts.config.syncCoordinatorAdminSecret || null
1777
+ });
1778
+ }
1710
1779
  function removeConfiguredCoordinatorGroup(groupId) {
1711
1780
  const targetGroup = groupId.trim();
1712
1781
  if (!targetGroup) return [];
@@ -3921,11 +3990,31 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
3921
3990
  remoteUrl: config.syncCoordinatorUrl || null,
3922
3991
  adminSecret: config.syncCoordinatorAdminSecret || null
3923
3992
  });
3924
- return c.json({
3925
- ok: true,
3926
- group,
3927
- status
3928
- });
3993
+ try {
3994
+ const defaultSpace = await ensureDefaultSpaceForTeam({
3995
+ store: getStore(),
3996
+ config,
3997
+ groupId,
3998
+ displayName: displayName || group.display_name || null
3999
+ });
4000
+ return c.json({
4001
+ ok: true,
4002
+ group,
4003
+ default_space: defaultSpace,
4004
+ status
4005
+ });
4006
+ } catch (setupError) {
4007
+ return c.json({
4008
+ ok: true,
4009
+ group,
4010
+ default_space: null,
4011
+ setup_warning: {
4012
+ step: "default_space",
4013
+ error: setupError instanceof Error ? setupError.message : String(setupError)
4014
+ },
4015
+ status
4016
+ });
4017
+ }
3929
4018
  } catch (error) {
3930
4019
  return c.json({
3931
4020
  error: error instanceof Error ? error.message : String(error),
@@ -4487,9 +4576,26 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
4487
4576
  error: "join request not found",
4488
4577
  status
4489
4578
  }, 404);
4579
+ let defaultSpaceMembership = null;
4580
+ let setupWarning = null;
4581
+ try {
4582
+ defaultSpaceMembership = await maybeGrantDefaultSpaceOnJoin({
4583
+ store: getStore(),
4584
+ config,
4585
+ groupId: result.group_id,
4586
+ deviceId: result.device_id
4587
+ });
4588
+ } catch (grantError) {
4589
+ setupWarning = {
4590
+ step: "default_space_grant",
4591
+ error: grantError instanceof Error ? grantError.message : String(grantError)
4592
+ };
4593
+ }
4490
4594
  return c.json({
4491
4595
  ok: true,
4492
4596
  request: result,
4597
+ default_space_membership: defaultSpaceMembership,
4598
+ setup_warning: setupWarning,
4493
4599
  status
4494
4600
  });
4495
4601
  } catch (error) {
@@ -4859,6 +4965,8 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
4859
4965
  projects_include: null,
4860
4966
  projects_exclude: null,
4861
4967
  auto_seed_scope: true,
4968
+ default_space_scope_id: null,
4969
+ auto_grant_default_space_on_join: false,
4862
4970
  updated_at: null
4863
4971
  },
4864
4972
  status
@@ -4897,6 +5005,12 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
4897
5005
  return cleaned.length === 0 ? null : cleaned;
4898
5006
  };
4899
5007
  const autoSeed = typeof body.auto_seed_scope === "boolean" ? body.auto_seed_scope : void 0;
5008
+ const autoGrantDefaultSpace = typeof body.auto_grant_default_space_on_join === "boolean" ? body.auto_grant_default_space_on_join : void 0;
5009
+ const defaultSpaceScopeId = body.default_space_scope_id === void 0 ? void 0 : optionalViewerString(body, "default_space_scope_id");
5010
+ if (autoGrantDefaultSpace === true && !defaultSpaceScopeId) return c.json({
5011
+ error: "default_space_scope_id_required_for_auto_grant",
5012
+ status
5013
+ }, 400);
4900
5014
  const store = getStore();
4901
5015
  try {
4902
5016
  const preferences = upsertCoordinatorGroupPreference(store.db, {
@@ -4904,7 +5018,9 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
4904
5018
  group_id: groupId,
4905
5019
  projects_include: normalizeList(body.projects_include),
4906
5020
  projects_exclude: normalizeList(body.projects_exclude),
4907
- auto_seed_scope: autoSeed
5021
+ auto_seed_scope: autoSeed,
5022
+ default_space_scope_id: defaultSpaceScopeId,
5023
+ auto_grant_default_space_on_join: autoGrantDefaultSpace
4908
5024
  });
4909
5025
  return c.json({
4910
5026
  preferences,