@codemem/server 0.46.0-alpha.2 → 0.46.0-beta.1
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.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +220 -64
- package/dist/index.js.map +1 -1
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/memory-ownership.d.ts +5 -0
- package/dist/routes/memory-ownership.d.ts.map +1 -0
- package/dist/routes/observer-status.d.ts.map +1 -1
- package/dist/routes/sync.d.ts +22 -8
- package/dist/routes/sync.d.ts.map +1 -1
- package/dist/routes/target-validation.d.ts +13 -0
- package/dist/routes/target-validation.d.ts.map +1 -1
- package/dist/snapshot-source.d.ts +12 -0
- package/dist/snapshot-source.d.ts.map +1 -0
- package/package.json +2 -2
- package/static/app.js +76 -57
- package/static/app.js.br +0 -0
- package/static/app.js.gz +0 -0
- package/static/index.html +827 -280
package/dist/index.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { Hono } from "hono";
|
|
|
13
13
|
import type { RawEventInbox } from "./raw-event-inbox.js";
|
|
14
14
|
import { type InMemoryRequestRateLimiter } from "./request-rate-limit.js";
|
|
15
15
|
import { type SyncRoutesOptions } from "./routes/sync.js";
|
|
16
|
-
import type
|
|
16
|
+
import { type ViewerTargetStore } from "./routes/target-validation.js";
|
|
17
17
|
import { type LegacyTeamCompletionDependencies, type LegacyTeamConfiguredGroupSnapshotLoader } from "./routes/team-setup.js";
|
|
18
18
|
export type { AdvancePendingProjectSharesResult, AdvanceProjectShareOperationResult, ReconcileRecipientPolicyProjectsResult, } from "./application/coordinator-maintenance.js";
|
|
19
19
|
export { advancePendingProjectSharesOperation, reconcileRecipientPolicyProjectsOperation, } from "./application/coordinator-maintenance.js";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EACX,iCAAiC,EACjC,sBAAsB,EACtB,cAAc,EACd,YAAY,EACZ,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAiB,OAAO,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,yBAAyB,CAAC;AAUjC,OAAO,EAAE,KAAK,iBAAiB,EAAkC,MAAM,kBAAkB,CAAC;AAC1F,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, join, posix, resolve, win32 } from "node:path";
|
|
3
|
-
import { ACCESS_CLEANUP_OP_TYPE,
|
|
3
|
+
import { ACCESS_CLEANUP_OP_TYPE, CodememConfigMutationError, 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, RawEventIngestValidationError, RecipientPolicyEdgeRequestError, RecipientPolicyTeamRenameError, RemoteCoordinatorRequestError, SCOPE_MEMBERSHIP_REVOCATION_LIMITATION, 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, applyLegacyTeamSetupCompletionManifest, applyLegacyTeamSetupCompletionManifestAndReturnActivation, applyReplicationOps, areLegacyTeamSetupCompletionPolicyFactsAdditivelyCompatible, automaticRecallHealth, buildAuthHeaders, buildBaseUrl, buildDirectPeerAuthHeaders, buildDistillReport, buildFilterClausesWithContext, buildPiFlushSignalFromEvent, buildRawEventEnvelopeFromCodexHook, buildRawEventEnvelopeFromHook, buildRawEventEnvelopeFromPiEvent, buildViewerIdentityTarget, canonicalCoordinatorLegacyTeamCompletionManifestJson, canonicalMemoryKind, canonicalWorkspaceIdentity, claimRecipientPolicyActorMutations, claimRecipientPolicyPublicationMutation, 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, getCodememConfigPath, getCodememEnvOverrideValues, getCodememEnvOverrides, getCoordinatorEnrollmentReconciliationIssueSummary, getCoordinatorGroupPreference, getLegacyTeamSetupDraft, getRecipientPolicyAuthorityState, getSemanticIndexDiagnostics, getSyncResetState, getUpdateStatus, ingestRawEvents, inspectFreshLegacyTeamSetupActivation, inspectLegacyTeamSetupActivation, inviteTokenDigest, isAutomaticRecallMeasurement, isLegacyTeamCandidateSelectable, isLegacyTeamSetupProjectMappingIdentity, isPeerTrustBindingCompatible, isProjectSyncEnablementError, isScopedSyncCapability, judgeDistillReport, legacyTeamCandidateId, legacyTeamCandidateProjectInventory, legacyTeamCanonicalProjectRef, legacyTeamDeviceRef, legacyTeamResolvedProjectRef, legacyTeamSetupApiErrorCode, listAuthorizedScopesForPeer, listCoordinatorJoinRequests, listDeviceIdentityInventory, listInboundScopeRejections, listLegacyRecipientPolicyProjections, listMaintenanceJobs, listObserverProviderOptions, listPerPeerScopeSyncState, listProjectScopeCandidates, listProjectScopeInventory, listProjectScopeSettingsMappings, listRecipientPolicyIntent, listRecipientPolicyReview, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, memoryFilterValueMatchesType, mergeAddresses, migrateRecipientPolicyIntent, mutateCodememConfigFile, negotiateSyncCapability, normalizeAddress, normalizeHumanPresentationName, normalizeSyncCapability, normalizeSyncFeatures, normalizeTeammateName, parseAcceptedProjectIntent, parsePositiveMemoryId, parseReassignScopePayload, parseRecipientPolicyEdgeCommitRequest, parseStrictInteger, parseSyncScopeRequest, persistShareOperation, personalScopeGrantStatusForPeer, planShareOperation, previewDeviceIdentityBindings, previewLegacyTeamSetupActivation, previewRecipientPolicyEdges, previewRecipientPolicyOnboarding, previewRecipientPolicyOnboardingFromReviewedIntent, probeAvailableCredentials, projectMatchesFilter, projectShareLifecycle, promptPackArtifactFingerprint, readCodememConfigFile, readCoordinatorSyncConfig, reassignProjectScopeInventoryProject, recipientInviteAuthoritativeIdentityId, recipientPolicyDigest, recipientReviewedIntentDigest, reconcileCoordinatorEnrollmentSnapshot, reconcileRecipientPolicyProject, reconcileShareOperationAcceptance, reconstructLegacyTeamSetupCompletionManifest, recordAutomaticRecall, recordHighestObservedDirectSignatureVersion, recordNonce, recordPromptPackArtifacts, recordPromptPackTerminal, refreshAuthorizedCoordinatorPeerTrust, refreshConfiguredScopeMembershipCache, refreshLegacyTeamCandidate, rejectInboundScopeFailures, renameRecipientPolicyTeam, replayLegacyTeamSetupActivation, requestJson, requireLegacyTeamSetupAccessDeltaWithinLimit, resolveDbPath, resolveObserverRuntime, resolveProject, resolveProjectRoot, resolveRecipientPolicyReview, resolveRecipientPolicyReviewBulk, runSyncPass, schema, serializeRecipientPolicyCoordinatorGroupMutation, serializeRecipientPolicyPublicationMutation, serializeRecipientPolicyTeamMutation, setLegacyTeamSetupDeviceAssignment, setLegacyTeamSetupDeviceDecision, setLegacyTeamSetupProjectMapping, storeVectors, summarizeInboundScopeRejections, summaryLikeSqlPredicate, supportsSyncFeature, syncScopeResetRequiredPayload, toJson, tryUpdateRetrievalDelivery, updatePeerAddresses, upsertCoordinatorGroupPreference, upsertProjectScopeSettingsMapping, validateLegacyTeamSetupCompletionManifest, validateLegacyTeamSetupCompletionManifestBinding, validateRawEvents, verifyDirectPeerSignature, verifyRecipientReviewedIntent, verifySignature } 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";
|
|
@@ -284,7 +284,7 @@ var DEFAULTS = {
|
|
|
284
284
|
sync_host: "0.0.0.0",
|
|
285
285
|
sync_port: 7337,
|
|
286
286
|
sync_interval_s: 120,
|
|
287
|
-
sync_mdns:
|
|
287
|
+
sync_mdns: false,
|
|
288
288
|
sync_coordinator_timeout_s: 3,
|
|
289
289
|
sync_coordinator_presence_ttl_s: 180,
|
|
290
290
|
raw_events_sweeper_interval_s: 30
|
|
@@ -303,14 +303,18 @@ function getEffectiveConfig(configData) {
|
|
|
303
303
|
...configData
|
|
304
304
|
};
|
|
305
305
|
for (const key of ["claude_command", "codex_command"]) effective[key] = coerceObserverCommand(effective[key]) ?? DEFAULTS[key];
|
|
306
|
-
|
|
307
|
-
const val = process.env[envVar];
|
|
308
|
-
if (val == null || val === "") continue;
|
|
309
|
-
if (key === "claude_command" || key === "codex_command") effective[key] = coerceObserverCommand(val) ?? effective[key];
|
|
310
|
-
else effective[key] = val;
|
|
311
|
-
}
|
|
306
|
+
Object.assign(effective, getCodememEnvOverrideValues());
|
|
312
307
|
return effective;
|
|
313
308
|
}
|
|
309
|
+
function observerRuntimeMetadata(configData) {
|
|
310
|
+
return {
|
|
311
|
+
resolved_observer_runtime: resolveObserverRuntime(configData),
|
|
312
|
+
observer_runtime_by_auth_source: Object.fromEntries([...AUTH_SOURCES].map((source) => [source, resolveObserverRuntime({
|
|
313
|
+
...configData,
|
|
314
|
+
observer_auth_source: source
|
|
315
|
+
})]))
|
|
316
|
+
};
|
|
317
|
+
}
|
|
314
318
|
function redactConfigValue(key, value) {
|
|
315
319
|
if (value == null || !SECRET_CONFIG_KEYS.has(key)) return value;
|
|
316
320
|
if (Array.isArray(value)) return value.length > 0 ? REDACTED_VALUE : [];
|
|
@@ -530,6 +534,7 @@ function viewerConfigSavePayload(saved, opts) {
|
|
|
530
534
|
path: savedPath,
|
|
531
535
|
config: sanitizeConfigForResponse(nextConfig),
|
|
532
536
|
effective: sanitizeConfigForResponse(afterEffective),
|
|
537
|
+
...observerRuntimeMetadata(nextConfig),
|
|
533
538
|
protected_keys: [...PROTECTED_WRITE_KEYS].sort(),
|
|
534
539
|
effects: {
|
|
535
540
|
saved_keys: savedChangedKeys,
|
|
@@ -563,6 +568,7 @@ function configRoutes(opts = {}) {
|
|
|
563
568
|
config: sanitizeConfigForResponse(configData),
|
|
564
569
|
defaults: DEFAULTS,
|
|
565
570
|
effective: sanitizeConfigForResponse(effective),
|
|
571
|
+
...observerRuntimeMetadata(configData),
|
|
566
572
|
env_overrides: getCodememEnvOverrides(),
|
|
567
573
|
protected_keys: [...PROTECTED_WRITE_KEYS].sort(),
|
|
568
574
|
providers: loadProviderOptions()
|
|
@@ -1594,7 +1600,7 @@ function memoryRoutes(getStore) {
|
|
|
1594
1600
|
}
|
|
1595
1601
|
const memoryId = parseStrictInteger(typeof body.memory_id === "string" ? body.memory_id : String(body.memory_id ?? ""));
|
|
1596
1602
|
if (memoryId == null || memoryId <= 0) return c.json({ error: "memory_id must be int" }, 400);
|
|
1597
|
-
if (!store
|
|
1603
|
+
if (!forgetTargetExists(store, memoryId, forgetSafetyFilters(body))) return c.json(...forgetNotFound(forgetSafetyFilters(body)));
|
|
1598
1604
|
const row = drizzle(store.db, { schema }).select().from(schema.memoryItems).where(eq(schema.memoryItems.id, memoryId)).get();
|
|
1599
1605
|
if (!row) return c.json({ error: "memory not found" }, 404);
|
|
1600
1606
|
if (!store.memoryOwnedBySelf(row)) return c.json({ error: "memory not owned by this device" }, 403);
|
|
@@ -1612,6 +1618,19 @@ function memoryRoutes(getStore) {
|
|
|
1612
1618
|
});
|
|
1613
1619
|
return app;
|
|
1614
1620
|
}
|
|
1621
|
+
function forgetNotFound(filters) {
|
|
1622
|
+
return [{ error: Object.keys(filters).length > 0 ? "not_found" : "memory not found" }, 404];
|
|
1623
|
+
}
|
|
1624
|
+
function forgetSafetyFilters(body) {
|
|
1625
|
+
const filters = {};
|
|
1626
|
+
if (typeof body.kind === "string" && body.kind.trim()) filters.kind = body.kind.trim();
|
|
1627
|
+
if (typeof body.project === "string" && body.project.trim()) filters.project = body.project.trim();
|
|
1628
|
+
return filters;
|
|
1629
|
+
}
|
|
1630
|
+
function forgetTargetExists(store, memoryId, filters) {
|
|
1631
|
+
if (Object.keys(filters).length > 0) return store.timeline(null, memoryId, 0, 0, filters).some((row) => row.id === memoryId);
|
|
1632
|
+
return store.get(memoryId) != null;
|
|
1633
|
+
}
|
|
1615
1634
|
//#endregion
|
|
1616
1635
|
//#region src/routes/memory-tools.ts
|
|
1617
1636
|
/**
|
|
@@ -2129,6 +2148,15 @@ function buildFailureImpact(latestFailure, queueTotals, authBackoff) {
|
|
|
2129
2148
|
if (queueTotals.pending > 0) return `${queueTotals.pending} queued raw events across ${queueTotals.sessions} session(s) are waiting on a successful flush.`;
|
|
2130
2149
|
return "Failed flush batches are pending retry.";
|
|
2131
2150
|
}
|
|
2151
|
+
function readCaptureEnabled() {
|
|
2152
|
+
const value = process.env.CODEMEM_RAW_EVENTS?.trim().toLowerCase();
|
|
2153
|
+
if (!value) return null;
|
|
2154
|
+
return ![
|
|
2155
|
+
"0",
|
|
2156
|
+
"false",
|
|
2157
|
+
"off"
|
|
2158
|
+
].includes(value);
|
|
2159
|
+
}
|
|
2132
2160
|
function observerStatusRoutes(deps) {
|
|
2133
2161
|
const app = new Hono();
|
|
2134
2162
|
app.get("/api/observer-status", (c) => {
|
|
@@ -2138,6 +2166,7 @@ function observerStatusRoutes(deps) {
|
|
|
2138
2166
|
if (!store || typeof store.rawEventBacklogTotals !== "function") return c.json({
|
|
2139
2167
|
active: null,
|
|
2140
2168
|
available_credentials: {},
|
|
2169
|
+
capture_enabled: readCaptureEnabled(),
|
|
2141
2170
|
latest_failure: null,
|
|
2142
2171
|
queue: {
|
|
2143
2172
|
pending: 0,
|
|
@@ -2161,6 +2190,7 @@ function observerStatusRoutes(deps) {
|
|
|
2161
2190
|
return c.json({
|
|
2162
2191
|
active,
|
|
2163
2192
|
available_credentials: availableCredentials,
|
|
2193
|
+
capture_enabled: readCaptureEnabled(),
|
|
2164
2194
|
latest_failure: failureWithImpact,
|
|
2165
2195
|
queue: {
|
|
2166
2196
|
...queueTotals,
|
|
@@ -2240,6 +2270,34 @@ function validateViewerTarget(store, payload, options = {}) {
|
|
|
2240
2270
|
if ((options.requireCurrentIdentity || payload.identity_target != null) && !store.hasCurrentIdentity()) return conflict("viewer_identity_mismatch", "viewer identity does not match request");
|
|
2241
2271
|
return { ok: true };
|
|
2242
2272
|
}
|
|
2273
|
+
/**
|
|
2274
|
+
* Middleware read of db_path / identity_target from the query string.
|
|
2275
|
+
* Native tools (pi extension) send targets as query params so a pre-handler
|
|
2276
|
+
* guard can reject a foreign viewer without consuming POST bodies. Lenient:
|
|
2277
|
+
* clients that omit targets (UI, MCP, curl) are unaffected.
|
|
2278
|
+
*/
|
|
2279
|
+
function viewerTargetQueryGuard(getStore) {
|
|
2280
|
+
return async (c, next) => {
|
|
2281
|
+
const dbPath = c.req.query("db_path");
|
|
2282
|
+
const rawIdentity = c.req.query("identity_target");
|
|
2283
|
+
if (dbPath == null && rawIdentity == null) return next();
|
|
2284
|
+
let identityTarget;
|
|
2285
|
+
if (rawIdentity != null) try {
|
|
2286
|
+
identityTarget = JSON.parse(rawIdentity);
|
|
2287
|
+
} catch {
|
|
2288
|
+
return c.json({ error: {
|
|
2289
|
+
code: "invalid_request",
|
|
2290
|
+
message: "identity_target must be JSON"
|
|
2291
|
+
} }, 400);
|
|
2292
|
+
}
|
|
2293
|
+
const target = validateViewerTarget(getStore(), {
|
|
2294
|
+
db_path: dbPath ?? void 0,
|
|
2295
|
+
identity_target: identityTarget
|
|
2296
|
+
});
|
|
2297
|
+
if (!target.ok) return c.json(target.body, target.status);
|
|
2298
|
+
return next();
|
|
2299
|
+
};
|
|
2300
|
+
}
|
|
2243
2301
|
//#endregion
|
|
2244
2302
|
//#region src/routes/pack.ts
|
|
2245
2303
|
var MAX_LEDGER_PAYLOAD_BYTES = 16384;
|
|
@@ -4319,6 +4377,40 @@ var PEERS_QUERY$1 = `
|
|
|
4319
4377
|
ORDER BY name, peer_device_id
|
|
4320
4378
|
`;
|
|
4321
4379
|
//#endregion
|
|
4380
|
+
//#region src/snapshot-source.ts
|
|
4381
|
+
function snapshotGeneration(raw) {
|
|
4382
|
+
if (!raw?.trim()) return null;
|
|
4383
|
+
return Number.parseInt(raw, 10);
|
|
4384
|
+
}
|
|
4385
|
+
function snapshotSourceDevice(requested, localDeviceId) {
|
|
4386
|
+
if (requested !== void 0 && requested !== localDeviceId) throw new Error("retirement_snapshot_source_required");
|
|
4387
|
+
return requested;
|
|
4388
|
+
}
|
|
4389
|
+
function snapshotErrorStatus(message) {
|
|
4390
|
+
if ([
|
|
4391
|
+
"retirement_snapshot_source_required",
|
|
4392
|
+
"missing_generation",
|
|
4393
|
+
"missing_snapshot_id"
|
|
4394
|
+
].includes(message)) return 400;
|
|
4395
|
+
if (message === "generation_mismatch" || message === "boundary_mismatch") return 409;
|
|
4396
|
+
return null;
|
|
4397
|
+
}
|
|
4398
|
+
function snapshotPageRequest(query, localDeviceId) {
|
|
4399
|
+
const generation = snapshotGeneration(query("generation"));
|
|
4400
|
+
const snapshotId = query("snapshot_id");
|
|
4401
|
+
if (generation === null || !Number.isFinite(generation)) throw new Error("missing_generation");
|
|
4402
|
+
if (!snapshotId) throw new Error("missing_snapshot_id");
|
|
4403
|
+
const rawLimit = Number.parseInt(query("limit") ?? "200", 10);
|
|
4404
|
+
return {
|
|
4405
|
+
generation,
|
|
4406
|
+
snapshotId,
|
|
4407
|
+
baselineCursor: query("baseline_cursor") ?? null,
|
|
4408
|
+
pageToken: query("page_token") ?? null,
|
|
4409
|
+
sourceDeviceId: snapshotSourceDevice(query("source_device_id"), localDeviceId),
|
|
4410
|
+
limit: Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 5e3)) : 200
|
|
4411
|
+
};
|
|
4412
|
+
}
|
|
4413
|
+
//#endregion
|
|
4322
4414
|
//#region src/routes/team-setup-view.ts
|
|
4323
4415
|
var enabled = {
|
|
4324
4416
|
enabled: true,
|
|
@@ -6264,6 +6356,26 @@ function tryParsePairingPayload(value) {
|
|
|
6264
6356
|
addresses
|
|
6265
6357
|
};
|
|
6266
6358
|
}
|
|
6359
|
+
async function inspectPairingInvite(c, next) {
|
|
6360
|
+
const body = await parseViewerJsonBody(c);
|
|
6361
|
+
if (typeof body?.invite !== "string") return c.json({ error: "invite_invalid" }, 400);
|
|
6362
|
+
try {
|
|
6363
|
+
const pairing = tryParsePairingPayload(body.invite.match(/echo\s+['"]([A-Za-z0-9+/=]+)['"]\s*\|\s*base64\s+-d\s*\|\s*codemem/)?.[1]?.trim() || body.invite.trim());
|
|
6364
|
+
if (pairing?.kind === "invalid-pair") return c.json({ error: pairing.error }, 400);
|
|
6365
|
+
if (pairing?.kind !== "pair") {
|
|
6366
|
+
decodeInvitePayload(extractInvitePayload(body.invite));
|
|
6367
|
+
return next();
|
|
6368
|
+
}
|
|
6369
|
+
return c.json({
|
|
6370
|
+
kind: "pair",
|
|
6371
|
+
device_id: pairing.device_id,
|
|
6372
|
+
fingerprint: pairing.fingerprint,
|
|
6373
|
+
addresses: pairing.addresses
|
|
6374
|
+
});
|
|
6375
|
+
} catch (error) {
|
|
6376
|
+
return c.json({ error: safeRecipientInviteError(error) }, 400);
|
|
6377
|
+
}
|
|
6378
|
+
}
|
|
6267
6379
|
function coordinatorAdminStatusPayload(config = readCoordinatorSyncConfig()) {
|
|
6268
6380
|
const groups = config.syncCoordinatorGroups;
|
|
6269
6381
|
const activeGroup = groups[0] ?? null;
|
|
@@ -8838,6 +8950,42 @@ function negotiatedSyncCapability(c) {
|
|
|
8838
8950
|
* network-accessible. All requests are auth-gated via signature
|
|
8839
8951
|
* verification so unauthenticated callers are rejected.
|
|
8840
8952
|
*/
|
|
8953
|
+
function serveAuthorizedSnapshot(c, store, peerDeviceId) {
|
|
8954
|
+
try {
|
|
8955
|
+
const rawScopeId = c.req.query(SYNC_SCOPE_QUERY_PARAM);
|
|
8956
|
+
const [localDeviceId] = ensureDeviceIdentity(store.db, { keysDir: syncKeysDir() });
|
|
8957
|
+
const scopeRequest = parseSyncScopeRequest(rawScopeId, rawScopeId !== void 0, {
|
|
8958
|
+
db: store.db,
|
|
8959
|
+
localDeviceId,
|
|
8960
|
+
negotiatedCapability: negotiatedSyncCapability(c),
|
|
8961
|
+
peerDeviceId
|
|
8962
|
+
});
|
|
8963
|
+
if (!scopeRequest.ok) return c.json(syncScopeResetRequiredPayload(getSyncResetState(store.db), scopeRequest.reason, LOCAL_SYNC_CAPABILITY, null), 409);
|
|
8964
|
+
const pageRequest = snapshotPageRequest((name) => c.req.query(name), localDeviceId);
|
|
8965
|
+
const result = loadMemorySnapshotPageForPeer(store.db, {
|
|
8966
|
+
...pageRequest,
|
|
8967
|
+
peerDeviceId,
|
|
8968
|
+
scopeId: scopeRequest.mode === "scoped" ? scopeRequest.scope_id : void 0
|
|
8969
|
+
});
|
|
8970
|
+
return c.json({
|
|
8971
|
+
source_device_id: pageRequest.sourceDeviceId,
|
|
8972
|
+
scope_id: scopeRequest.scope_id,
|
|
8973
|
+
generation: result.boundary.generation,
|
|
8974
|
+
snapshot_id: result.boundary.snapshot_id,
|
|
8975
|
+
baseline_cursor: result.boundary.baseline_cursor,
|
|
8976
|
+
retained_floor_cursor: result.boundary.retained_floor_cursor,
|
|
8977
|
+
sync_capability: LOCAL_SYNC_CAPABILITY,
|
|
8978
|
+
items: result.items,
|
|
8979
|
+
next_page_token: result.nextPageToken,
|
|
8980
|
+
has_more: result.hasMore
|
|
8981
|
+
});
|
|
8982
|
+
} catch (err) {
|
|
8983
|
+
const message = err instanceof Error ? err.message : "";
|
|
8984
|
+
const status = snapshotErrorStatus(message);
|
|
8985
|
+
if (status) return c.json({ error: message }, status);
|
|
8986
|
+
return c.json({ error: "internal_error" }, 500);
|
|
8987
|
+
}
|
|
8988
|
+
}
|
|
8841
8989
|
function syncProtocolRoutes(getStore, opts = {}) {
|
|
8842
8990
|
const app = new Hono();
|
|
8843
8991
|
const routeRateLimit = opts.routeRateLimit ?? null;
|
|
@@ -9004,51 +9152,7 @@ function syncProtocolRoutes(getStore, opts = {}) {
|
|
|
9004
9152
|
}
|
|
9005
9153
|
const limited = rateLimitedResponse(c, auth.deviceId, true);
|
|
9006
9154
|
if (limited) return limited;
|
|
9007
|
-
|
|
9008
|
-
const rawScopeId = c.req.query(SYNC_SCOPE_QUERY_PARAM);
|
|
9009
|
-
const negotiated = negotiatedSyncCapability(c);
|
|
9010
|
-
const [localDeviceId] = ensureDeviceIdentity(store.db, { keysDir: syncKeysDir() });
|
|
9011
|
-
const scopeRequest = parseSyncScopeRequest(rawScopeId, rawScopeId !== void 0, {
|
|
9012
|
-
db: store.db,
|
|
9013
|
-
localDeviceId,
|
|
9014
|
-
negotiatedCapability: negotiated,
|
|
9015
|
-
peerDeviceId: auth.deviceId
|
|
9016
|
-
});
|
|
9017
|
-
if (!scopeRequest.ok) return c.json(syncScopeResetRequiredPayload(getSyncResetState(store.db), scopeRequest.reason, LOCAL_SYNC_CAPABILITY, null), 409);
|
|
9018
|
-
const rawGeneration = c.req.query("generation");
|
|
9019
|
-
const generation = rawGeneration != null && rawGeneration.trim().length > 0 ? Number.parseInt(rawGeneration, 10) : null;
|
|
9020
|
-
const snapshotId = c.req.query("snapshot_id") ?? null;
|
|
9021
|
-
const baselineCursor = c.req.query("baseline_cursor") ?? null;
|
|
9022
|
-
const pageToken = c.req.query("page_token") ?? null;
|
|
9023
|
-
const rawLimit = Number.parseInt(c.req.query("limit") ?? "200", 10);
|
|
9024
|
-
const limit = Number.isFinite(rawLimit) ? Math.max(1, Math.min(rawLimit, 5e3)) : 200;
|
|
9025
|
-
if (generation == null || !Number.isFinite(generation)) return c.json({ error: "missing_generation" }, 400);
|
|
9026
|
-
if (!snapshotId) return c.json({ error: "missing_snapshot_id" }, 400);
|
|
9027
|
-
const result = loadMemorySnapshotPageForPeer(store.db, {
|
|
9028
|
-
generation,
|
|
9029
|
-
snapshotId,
|
|
9030
|
-
baselineCursor,
|
|
9031
|
-
pageToken,
|
|
9032
|
-
limit,
|
|
9033
|
-
peerDeviceId: auth.deviceId,
|
|
9034
|
-
scopeId: scopeRequest.mode === "scoped" ? scopeRequest.scope_id : void 0
|
|
9035
|
-
});
|
|
9036
|
-
return c.json({
|
|
9037
|
-
scope_id: scopeRequest.scope_id,
|
|
9038
|
-
generation: result.boundary.generation,
|
|
9039
|
-
snapshot_id: result.boundary.snapshot_id,
|
|
9040
|
-
baseline_cursor: result.boundary.baseline_cursor,
|
|
9041
|
-
retained_floor_cursor: result.boundary.retained_floor_cursor,
|
|
9042
|
-
sync_capability: LOCAL_SYNC_CAPABILITY,
|
|
9043
|
-
items: result.items,
|
|
9044
|
-
next_page_token: result.nextPageToken,
|
|
9045
|
-
has_more: result.hasMore
|
|
9046
|
-
});
|
|
9047
|
-
} catch (err) {
|
|
9048
|
-
const message = err instanceof Error ? err.message : "";
|
|
9049
|
-
if (message === "generation_mismatch" || message === "boundary_mismatch") return c.json({ error: message }, 409);
|
|
9050
|
-
return c.json({ error: "internal_error" }, 500);
|
|
9051
|
-
}
|
|
9155
|
+
return serveAuthorizedSnapshot(c, store, auth.deviceId);
|
|
9052
9156
|
})();
|
|
9053
9157
|
});
|
|
9054
9158
|
app.post("/v1/ops", async (c) => {
|
|
@@ -9126,7 +9230,7 @@ function syncProtocolRoutes(getStore, opts = {}) {
|
|
|
9126
9230
|
}
|
|
9127
9231
|
async function loadConfiguredDeviceIdentityCoordinatorEvidence() {
|
|
9128
9232
|
const config = readCoordinatorSyncConfig();
|
|
9129
|
-
const hasCoordinatorConfiguration = config.syncCoordinatorUrl.length > 0 || config.syncCoordinatorGroups.length > 0;
|
|
9233
|
+
const hasCoordinatorConfiguration = config.syncCoordinatorUrl.length > 0 || config.syncCoordinatorGroups.length > 0 || config.syncCoordinatorAdminSecret.length > 0;
|
|
9130
9234
|
const remoteUrl = config.syncCoordinatorUrl.trim();
|
|
9131
9235
|
const adminSecret = config.syncCoordinatorAdminSecret.trim();
|
|
9132
9236
|
const groupIds = [...new Set(config.syncCoordinatorGroups.map((groupId) => groupId.trim()))].filter(Boolean);
|
|
@@ -9137,7 +9241,7 @@ async function loadConfiguredDeviceIdentityCoordinatorEvidence() {
|
|
|
9137
9241
|
};
|
|
9138
9242
|
if (!remoteUrl || !adminSecret || groupIds.length === 0) return {
|
|
9139
9243
|
availability: "unavailable",
|
|
9140
|
-
safeErrorCode: "
|
|
9244
|
+
safeErrorCode: "coordinator_configuration_required",
|
|
9141
9245
|
enrollments: []
|
|
9142
9246
|
};
|
|
9143
9247
|
if (groupIds.length > 25) return {
|
|
@@ -9170,6 +9274,61 @@ async function loadConfiguredDeviceIdentityCoordinatorEvidence() {
|
|
|
9170
9274
|
};
|
|
9171
9275
|
}
|
|
9172
9276
|
}
|
|
9277
|
+
var ORIGIN_DEVICE_NAME_LOOKUP_BATCH_SIZE = 400;
|
|
9278
|
+
function originDeviceNameKey(value) {
|
|
9279
|
+
return value.normalize("NFKC").toLowerCase().toUpperCase().toLowerCase();
|
|
9280
|
+
}
|
|
9281
|
+
function originDeviceDisplayName(value, deviceId) {
|
|
9282
|
+
if (typeof value !== "string") return null;
|
|
9283
|
+
try {
|
|
9284
|
+
const displayName = normalizeHumanPresentationName(value, "display_name");
|
|
9285
|
+
let normalizedDeviceId = deviceId;
|
|
9286
|
+
try {
|
|
9287
|
+
normalizedDeviceId = normalizeHumanPresentationName(deviceId, "device_id");
|
|
9288
|
+
} catch {}
|
|
9289
|
+
return originDeviceNameKey(displayName) === originDeviceNameKey(normalizedDeviceId) ? null : displayName;
|
|
9290
|
+
} catch {
|
|
9291
|
+
return null;
|
|
9292
|
+
}
|
|
9293
|
+
}
|
|
9294
|
+
function resolveOriginDeviceDisplayNames(store, projects) {
|
|
9295
|
+
const deviceIds = [...new Set(projects.flatMap((project) => project.origin_devices.map((device) => device.device_id)))];
|
|
9296
|
+
const names = /* @__PURE__ */ new Map();
|
|
9297
|
+
for (let offset = 0; offset < deviceIds.length; offset += ORIGIN_DEVICE_NAME_LOOKUP_BATCH_SIZE) {
|
|
9298
|
+
const batch = deviceIds.slice(offset, offset + ORIGIN_DEVICE_NAME_LOOKUP_BATCH_SIZE);
|
|
9299
|
+
const placeholders = batch.map(() => "?").join(", ");
|
|
9300
|
+
const peers = store.db.prepare(`SELECT peer_device_id AS device_id, name AS display_name
|
|
9301
|
+
FROM sync_peers
|
|
9302
|
+
WHERE peer_device_id IN (${placeholders})
|
|
9303
|
+
AND TRIM(COALESCE(pinned_fingerprint, '')) <> ''`).all(...batch);
|
|
9304
|
+
for (const peer of peers) {
|
|
9305
|
+
const displayName = originDeviceDisplayName(peer.display_name, peer.device_id);
|
|
9306
|
+
if (displayName) names.set(peer.device_id, displayName);
|
|
9307
|
+
}
|
|
9308
|
+
const devices = store.db.prepare(`SELECT device_id, display_name
|
|
9309
|
+
FROM identity_devices
|
|
9310
|
+
WHERE device_id IN (${placeholders}) AND status = 'active'`).all(...batch);
|
|
9311
|
+
for (const device of devices) {
|
|
9312
|
+
const displayName = originDeviceDisplayName(device.display_name, device.device_id);
|
|
9313
|
+
if (displayName) names.set(device.device_id, displayName);
|
|
9314
|
+
}
|
|
9315
|
+
}
|
|
9316
|
+
return names;
|
|
9317
|
+
}
|
|
9318
|
+
function serializeProjectScopeInventory(store, inventory, sharingByProject) {
|
|
9319
|
+
const originDeviceNames = resolveOriginDeviceDisplayNames(store, inventory.projects);
|
|
9320
|
+
return {
|
|
9321
|
+
...inventory,
|
|
9322
|
+
projects: inventory.projects.map((project) => ({
|
|
9323
|
+
...project,
|
|
9324
|
+
origin_devices: project.origin_devices.map(({ device_id }) => ({
|
|
9325
|
+
device_id,
|
|
9326
|
+
display_name: originDeviceNames.get(device_id) ?? null
|
|
9327
|
+
})),
|
|
9328
|
+
sharing: sharingByProject.get(project.workspace_identity) ?? []
|
|
9329
|
+
}))
|
|
9330
|
+
};
|
|
9331
|
+
}
|
|
9173
9332
|
/**
|
|
9174
9333
|
* Viewer-facing sync management routes (/api/sync/*).
|
|
9175
9334
|
*
|
|
@@ -9178,7 +9337,7 @@ async function loadConfiguredDeviceIdentityCoordinatorEvidence() {
|
|
|
9178
9337
|
* local viewer.
|
|
9179
9338
|
*/
|
|
9180
9339
|
function syncRoutes(getStore, getSyncRuntimeStatus, options = {}) {
|
|
9181
|
-
const app = new Hono();
|
|
9340
|
+
const app = new Hono().post("/api/sync/invites/inspect", inspectPairingInvite);
|
|
9182
9341
|
const readCoordinatorConfig = options.readCoordinatorConfig ?? readCoordinatorSyncConfig;
|
|
9183
9342
|
const renameCoordinatorGroup = options.renameCoordinatorGroup ?? coordinatorRenameGroupAction;
|
|
9184
9343
|
const loadCoordinatorEvidence = options.loadDeviceIdentityCoordinatorEvidence ?? loadConfiguredDeviceIdentityCoordinatorEvidence;
|
|
@@ -9823,13 +9982,7 @@ function syncRoutes(getStore, getSyncRuntimeStatus, options = {}) {
|
|
|
9823
9982
|
});
|
|
9824
9983
|
sharingByProject.set(item.canonical_project_identity, current);
|
|
9825
9984
|
}
|
|
9826
|
-
return c.json(
|
|
9827
|
-
...inventory,
|
|
9828
|
-
projects: inventory.projects.map((project) => ({
|
|
9829
|
-
...project,
|
|
9830
|
-
sharing: sharingByProject.get(project.workspace_identity) ?? []
|
|
9831
|
-
}))
|
|
9832
|
-
});
|
|
9985
|
+
return c.json(serializeProjectScopeInventory(store, inventory, sharingByProject));
|
|
9833
9986
|
});
|
|
9834
9987
|
app.post("/api/sync/projects/reassign-project", async (c) => {
|
|
9835
9988
|
const store = getStore();
|
|
@@ -12028,6 +12181,9 @@ function createApp(opts) {
|
|
|
12028
12181
|
const app = new Hono();
|
|
12029
12182
|
app.use("*", preflightHandler());
|
|
12030
12183
|
app.use("*", originGuard({ unsafeGetPathPrefixes: [TEAM_SETUP_ROUTE_PREFIX] }));
|
|
12184
|
+
const viewerTargetGuard = viewerTargetQueryGuard(storeFactory);
|
|
12185
|
+
app.use("/api/memories/*", viewerTargetGuard);
|
|
12186
|
+
app.use("/api/memory", viewerTargetGuard);
|
|
12031
12187
|
app.route("/", healthRoutes(storeFactory));
|
|
12032
12188
|
app.route("/", diagnosticsRoutes(storeFactory));
|
|
12033
12189
|
app.route("/", statsRoutes(storeFactory));
|