@codemem/server 0.30.0 → 0.31.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.d.ts.map +1 -1
- package/dist/index.js +210 -4
- package/dist/index.js.map +1 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +2 -2
- package/static/app.js +1068 -54
- package/static/index.html +295 -1
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,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAiB,OAAO,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,yBAAyB,CAAC;AAQjC,OAAO,EAAE,OAAO,EAAE,CAAC;AAKnB,uFAAuF;AACvF,wBAAgB,QAAQ,IAAI,WAAW,CAKtC;AAED,mDAAmD;AACnD,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,YAAY,CAAC,EAAE,MAAM,WAAW,CAAC;IACjC,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,oBAAoB,CAAC,EAAE;QACtB,OAAO,CAAC,EAAE,0BAA0B,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,4BAA4B,CAAC,EAAE,MAAM,CAAC;KACtC,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM;QAC5B,KAAK,EACF,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,UAAU,GACV,iBAAiB,GACjB,iBAAiB,GACjB,IAAI,CAAC;QACR,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,GAAG,IAAI,CAAC;CACT;AAED,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,UAAU,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,KAAK,eAAe,EAAiB,OAAO,EAAE,MAAM,eAAe,CAAC;AAE1F,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,EAEN,KAAK,0BAA0B,EAC/B,MAAM,yBAAyB,CAAC;AAQjC,OAAO,EAAE,OAAO,EAAE,CAAC;AAKnB,uFAAuF;AACvF,wBAAgB,QAAQ,IAAI,WAAW,CAKtC;AAED,mDAAmD;AACnD,wBAAgB,UAAU,IAAI,IAAI,CAGjC;AAED;;;GAGG;AACH,MAAM,WAAW,UAAU;IAC1B,YAAY,CAAC,EAAE,MAAM,WAAW,CAAC;IACjC,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,cAAc,GAAG,IAAI,CAAC;IACjC,oBAAoB,CAAC,EAAE;QACtB,OAAO,CAAC,EAAE,0BAA0B,CAAC;QACrC,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,wBAAwB,CAAC,EAAE,MAAM,CAAC;QAClC,4BAA4B,CAAC,EAAE,MAAM,CAAC;KACtC,CAAC;IACF,oBAAoB,CAAC,EAAE,MAAM;QAC5B,KAAK,EACF,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,UAAU,GACV,iBAAiB,GACjB,iBAAiB,GACjB,IAAI,CAAC;QACR,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;KACvB,GAAG,IAAI,CAAC;CACT;AAED,wBAAgB,SAAS,CAAC,IAAI,CAAC,EAAE,UAAU,8EA6D1C;AAED;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,IAAI,CAAC,EAAE,UAAU,8EA8B9C"}
|
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, 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, listProjectScopeSettingsMappings, listSharingDomainSettingsScopes, loadMemorySnapshotPageForPeer, loadReplicationOpsForPeer, lookupCoordinatorPeers, mergeAddresses, negotiateSyncCapability, normalizeSyncCapability, parseStrictInteger, parseSyncScopeRequest, personalScopeGrantStatusForPeer, probeAvailableCredentials, readCodememConfigFile, readCoordinatorSyncConfig, 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, 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";
|
|
@@ -2461,11 +2461,153 @@ function legacySharedReviewSummary(store) {
|
|
|
2461
2461
|
AND active = 1
|
|
2462
2462
|
AND deleted_at IS NULL`).get(LEGACY_SHARED_REVIEW_SCOPE_ID);
|
|
2463
2463
|
const memoryCount = Number(row?.memory_count ?? 0);
|
|
2464
|
+
const candidatesByIdentity = new Map(listProjectScopeCandidates(store.db, { limit: null }).map((candidate) => [candidate.workspace_identity, candidate]));
|
|
2465
|
+
const groupRows = store.db.prepare(`SELECT s.project,
|
|
2466
|
+
s.cwd,
|
|
2467
|
+
s.git_remote,
|
|
2468
|
+
s.git_branch,
|
|
2469
|
+
m.workspace_id,
|
|
2470
|
+
COUNT(*) AS memory_count,
|
|
2471
|
+
MAX(m.updated_at) AS last_updated_at
|
|
2472
|
+
FROM memory_items m
|
|
2473
|
+
LEFT JOIN sessions s ON s.id = m.session_id
|
|
2474
|
+
WHERE m.scope_id = ?
|
|
2475
|
+
AND m.active = 1
|
|
2476
|
+
AND m.deleted_at IS NULL
|
|
2477
|
+
GROUP BY s.project, s.cwd, s.git_remote, s.git_branch, m.workspace_id
|
|
2478
|
+
ORDER BY memory_count DESC, last_updated_at DESC`).all(LEGACY_SHARED_REVIEW_SCOPE_ID);
|
|
2479
|
+
const groupsByIdentity = /* @__PURE__ */ new Map();
|
|
2480
|
+
for (const group of groupRows) {
|
|
2481
|
+
const identity = canonicalWorkspaceIdentity({
|
|
2482
|
+
cwd: group.cwd,
|
|
2483
|
+
gitBranch: group.git_branch,
|
|
2484
|
+
gitRemote: group.git_remote,
|
|
2485
|
+
project: group.project,
|
|
2486
|
+
workspaceId: group.workspace_id
|
|
2487
|
+
});
|
|
2488
|
+
const candidate = candidatesByIdentity.get(identity.value);
|
|
2489
|
+
const resolvedScope = candidate?.resolved_scope_id ?? null;
|
|
2490
|
+
const suggestedScope = resolvedScope && resolvedScope !== LOCAL_DEFAULT_SCOPE_ID && resolvedScope !== LEGACY_SHARED_REVIEW_SCOPE_ID ? resolvedScope : candidate?.suggested_scope_id;
|
|
2491
|
+
const existing = groupsByIdentity.get(identity.value);
|
|
2492
|
+
const memoryCount = Number(group.memory_count ?? 0);
|
|
2493
|
+
const lastUpdatedAt = group.last_updated_at ?? null;
|
|
2494
|
+
if (existing) {
|
|
2495
|
+
existing.memory_count += memoryCount;
|
|
2496
|
+
if (lastUpdatedAt && (!existing.last_updated_at || lastUpdatedAt > existing.last_updated_at)) existing.last_updated_at = lastUpdatedAt;
|
|
2497
|
+
continue;
|
|
2498
|
+
}
|
|
2499
|
+
groupsByIdentity.set(identity.value, {
|
|
2500
|
+
workspace_identity: identity.value,
|
|
2501
|
+
identity_source: identity.source,
|
|
2502
|
+
display_project: identity.displayProject ?? group.project ?? group.cwd ?? identity.value,
|
|
2503
|
+
memory_count: memoryCount,
|
|
2504
|
+
last_updated_at: lastUpdatedAt,
|
|
2505
|
+
suggested_scope_id: suggestedScope ?? null,
|
|
2506
|
+
suggestion_reason: suggestedScope ? candidate?.suggestion_reason ?? "Existing project mapping can be reviewed as a destination, but legacy data is not promoted automatically." : null
|
|
2507
|
+
});
|
|
2508
|
+
}
|
|
2509
|
+
const totalGroupCount = groupsByIdentity.size;
|
|
2510
|
+
const groups = [...groupsByIdentity.values()].sort((left, right) => right.memory_count - left.memory_count || (right.last_updated_at ?? "").localeCompare(left.last_updated_at ?? "") || left.workspace_identity.localeCompare(right.workspace_identity)).slice(0, 20);
|
|
2511
|
+
const targetScopes = listSharingDomainSettingsScopes(store.db).filter((scope) => scope.scope_id !== LOCAL_DEFAULT_SCOPE_ID && scope.scope_id !== LEGACY_SHARED_REVIEW_SCOPE_ID).map((scope) => ({
|
|
2512
|
+
authority_type: scope.authority_type,
|
|
2513
|
+
label: scope.label || scope.scope_id,
|
|
2514
|
+
scope_id: scope.scope_id
|
|
2515
|
+
}));
|
|
2464
2516
|
return {
|
|
2465
2517
|
scope_id: LEGACY_SHARED_REVIEW_SCOPE_ID,
|
|
2466
2518
|
memory_count: memoryCount,
|
|
2467
2519
|
has_data: memoryCount > 0,
|
|
2468
|
-
last_updated_at: row?.last_updated_at ?? null
|
|
2520
|
+
last_updated_at: row?.last_updated_at ?? null,
|
|
2521
|
+
groups,
|
|
2522
|
+
total_group_count: totalGroupCount,
|
|
2523
|
+
target_scopes: targetScopes
|
|
2524
|
+
};
|
|
2525
|
+
}
|
|
2526
|
+
function assertLocalDeviceScopeMembership(store, scopeId) {
|
|
2527
|
+
if (scopeId === LOCAL_DEFAULT_SCOPE_ID) return;
|
|
2528
|
+
if (!store.db.prepare(`SELECT 1
|
|
2529
|
+
FROM scope_memberships sm
|
|
2530
|
+
JOIN replication_scopes rs ON rs.scope_id = sm.scope_id
|
|
2531
|
+
WHERE sm.scope_id = ?
|
|
2532
|
+
AND sm.device_id = ?
|
|
2533
|
+
AND sm.status = 'active'
|
|
2534
|
+
AND rs.status = 'active'
|
|
2535
|
+
AND sm.membership_epoch >= rs.membership_epoch
|
|
2536
|
+
LIMIT 1`).get(scopeId, store.deviceId)) throw new Error(`local device is not a member of Sharing domain ${scopeId}`);
|
|
2537
|
+
}
|
|
2538
|
+
function legacySharedReviewRowsForWorkspace(store, workspaceIdentity) {
|
|
2539
|
+
return store.db.prepare(`SELECT m.id,
|
|
2540
|
+
m.active,
|
|
2541
|
+
m.deleted_at,
|
|
2542
|
+
m.scope_id,
|
|
2543
|
+
s.project,
|
|
2544
|
+
s.cwd,
|
|
2545
|
+
s.git_remote,
|
|
2546
|
+
s.git_branch,
|
|
2547
|
+
m.workspace_id,
|
|
2548
|
+
m.actor_id,
|
|
2549
|
+
m.origin_device_id,
|
|
2550
|
+
m.metadata_json
|
|
2551
|
+
FROM memory_items m
|
|
2552
|
+
LEFT JOIN sessions s ON s.id = m.session_id
|
|
2553
|
+
WHERE m.scope_id = ?
|
|
2554
|
+
AND m.active = 1
|
|
2555
|
+
AND m.deleted_at IS NULL`).all(LEGACY_SHARED_REVIEW_SCOPE_ID).filter((row) => {
|
|
2556
|
+
return canonicalWorkspaceIdentity({
|
|
2557
|
+
cwd: row.cwd,
|
|
2558
|
+
gitBranch: row.git_branch,
|
|
2559
|
+
gitRemote: row.git_remote,
|
|
2560
|
+
project: row.project,
|
|
2561
|
+
workspaceId: row.workspace_id
|
|
2562
|
+
}).value === workspaceIdentity;
|
|
2563
|
+
});
|
|
2564
|
+
}
|
|
2565
|
+
function reassignableLegacySharedReviewRows(store, rows) {
|
|
2566
|
+
const reassignableRows = rows.filter((row) => store.memoryOwnedBySelf(row));
|
|
2567
|
+
if (reassignableRows.length === 0) throw new Error("legacy shared review group has no locally owned memories to reassign");
|
|
2568
|
+
const currentRows = store.db.prepare(`SELECT id, scope_id, active, deleted_at
|
|
2569
|
+
FROM memory_items
|
|
2570
|
+
WHERE id IN (${reassignableRows.map(() => "?").join(",")})`).all(...reassignableRows.map((row) => row.id));
|
|
2571
|
+
const currentById = new Map(currentRows.map((row) => [Number(row.id), row]));
|
|
2572
|
+
for (const row of reassignableRows) {
|
|
2573
|
+
const current = currentById.get(row.id);
|
|
2574
|
+
if (current?.scope_id !== LEGACY_SHARED_REVIEW_SCOPE_ID || current.active !== 1 || current.deleted_at) throw new Error("legacy shared review group changed before reassignment; refresh and try again");
|
|
2575
|
+
}
|
|
2576
|
+
return reassignableRows;
|
|
2577
|
+
}
|
|
2578
|
+
function legacySharedReviewReassignmentPreview(store, input) {
|
|
2579
|
+
if (!input.workspaceIdentity.trim()) throw new Error("workspace_identity must be a non-empty string");
|
|
2580
|
+
if (!input.scopeId.trim()) throw new Error("scope_id must be a non-empty string");
|
|
2581
|
+
if (input.scopeId === LOCAL_DEFAULT_SCOPE_ID) throw new Error("local-default is not a valid target for legacy shared review reassignment");
|
|
2582
|
+
if (input.scopeId === LEGACY_SHARED_REVIEW_SCOPE_ID) throw new Error("legacy-shared-review is a review bucket, not an assignable Sharing domain");
|
|
2583
|
+
const targetScope = listSharingDomainSettingsScopes(store.db).find((scope) => scope.scope_id === input.scopeId);
|
|
2584
|
+
if (!targetScope) throw new Error(`scope_id ${input.scopeId} is not an active Sharing domain`);
|
|
2585
|
+
assertLocalDeviceScopeMembership(store, input.scopeId);
|
|
2586
|
+
const rows = legacySharedReviewRowsForWorkspace(store, input.workspaceIdentity);
|
|
2587
|
+
if (rows.length === 0) throw new Error("legacy shared review group not found");
|
|
2588
|
+
const reassignableRows = reassignableLegacySharedReviewRows(store, rows);
|
|
2589
|
+
const peerDeviceIds = [...new Set(rows.map((row) => String(row.origin_device_id ?? "").trim()).filter((deviceId) => deviceId && deviceId !== store.deviceId))].sort();
|
|
2590
|
+
return {
|
|
2591
|
+
affected_peer_device_count: peerDeviceIds.length,
|
|
2592
|
+
affected_peer_device_ids: peerDeviceIds.slice(0, 5),
|
|
2593
|
+
memory_count: rows.length,
|
|
2594
|
+
reassignable_memory_count: reassignableRows.length,
|
|
2595
|
+
scope_id: input.scopeId,
|
|
2596
|
+
skipped_memory_count: rows.length - reassignableRows.length,
|
|
2597
|
+
target_scope_label: targetScope.label || targetScope.scope_id,
|
|
2598
|
+
warning: "This changes future sync authorization for reassignable local memories. It does not erase data already copied to peers under legacy-shared-review.",
|
|
2599
|
+
workspace_identity: input.workspaceIdentity
|
|
2600
|
+
};
|
|
2601
|
+
}
|
|
2602
|
+
function reassignLegacySharedReviewGroup(store, input) {
|
|
2603
|
+
const preview = legacySharedReviewReassignmentPreview(store, input);
|
|
2604
|
+
const rows = reassignableLegacySharedReviewRows(store, legacySharedReviewRowsForWorkspace(store, input.workspaceIdentity));
|
|
2605
|
+
for (const row of rows) store.reassignMemoryScope(row.id, input.scopeId);
|
|
2606
|
+
return {
|
|
2607
|
+
ok: true,
|
|
2608
|
+
...preview,
|
|
2609
|
+
reassigned_memory_count: rows.length,
|
|
2610
|
+
legacy_shared_review: legacySharedReviewSummary(store)
|
|
2469
2611
|
};
|
|
2470
2612
|
}
|
|
2471
2613
|
var SYNC_DIRECTION_WINDOW_SECONDS = 1440 * 60;
|
|
@@ -3162,6 +3304,65 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
3162
3304
|
local_default_scope_id: LOCAL_DEFAULT_SCOPE_ID
|
|
3163
3305
|
});
|
|
3164
3306
|
});
|
|
3307
|
+
app.get("/api/sync/projects", (c) => {
|
|
3308
|
+
const store = getStore();
|
|
3309
|
+
const limit = Math.max(1, queryInt(c.req.query("limit"), 50));
|
|
3310
|
+
const offset = Math.max(0, queryInt(c.req.query("offset"), 0));
|
|
3311
|
+
return c.json(listProjectScopeInventory(store.db, {
|
|
3312
|
+
identitySource: c.req.query("identity_source"),
|
|
3313
|
+
limit,
|
|
3314
|
+
offset,
|
|
3315
|
+
query: c.req.query("q"),
|
|
3316
|
+
scopeId: c.req.query("scope_id"),
|
|
3317
|
+
status: c.req.query("status")
|
|
3318
|
+
}));
|
|
3319
|
+
});
|
|
3320
|
+
app.post("/api/sync/projects/reassign-project", async (c) => {
|
|
3321
|
+
const store = getStore();
|
|
3322
|
+
const body = await parseViewerJsonBody(c);
|
|
3323
|
+
if (!body) return c.json({ error: "invalid json" }, 400);
|
|
3324
|
+
try {
|
|
3325
|
+
const workspaceIdentity = optionalViewerStrictString(body, "workspace_identity") ?? "";
|
|
3326
|
+
const project = optionalViewerStrictString(body, "project") ?? "";
|
|
3327
|
+
const result = reassignProjectScopeInventoryProject(store.db, {
|
|
3328
|
+
project,
|
|
3329
|
+
workspaceIdentity
|
|
3330
|
+
});
|
|
3331
|
+
return c.json({
|
|
3332
|
+
ok: true,
|
|
3333
|
+
...result
|
|
3334
|
+
});
|
|
3335
|
+
} catch (error) {
|
|
3336
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3337
|
+
return c.json({ error: message }, message.includes("not found") ? 404 : 400);
|
|
3338
|
+
}
|
|
3339
|
+
});
|
|
3340
|
+
app.post("/api/sync/legacy-shared-review/reassign", async (c) => {
|
|
3341
|
+
const store = getStore();
|
|
3342
|
+
const body = await parseViewerJsonBody(c);
|
|
3343
|
+
if (!body) return c.json({ error: "invalid json" }, 400);
|
|
3344
|
+
try {
|
|
3345
|
+
const workspaceIdentity = optionalViewerStrictString(body, "workspace_identity") ?? "";
|
|
3346
|
+
const scopeId = optionalViewerStrictString(body, "scope_id") ?? "";
|
|
3347
|
+
const confirmedOldCopies = body.confirmed_old_copies === true;
|
|
3348
|
+
const preview = legacySharedReviewReassignmentPreview(store, {
|
|
3349
|
+
scopeId,
|
|
3350
|
+
workspaceIdentity
|
|
3351
|
+
});
|
|
3352
|
+
if (!confirmedOldCopies) return c.json({
|
|
3353
|
+
error: "legacy_review_confirmation_required",
|
|
3354
|
+
message: preview.warning,
|
|
3355
|
+
preview
|
|
3356
|
+
}, 409);
|
|
3357
|
+
return c.json(reassignLegacySharedReviewGroup(store, {
|
|
3358
|
+
scopeId,
|
|
3359
|
+
workspaceIdentity
|
|
3360
|
+
}));
|
|
3361
|
+
} catch (error) {
|
|
3362
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
3363
|
+
return c.json({ error: message }, message.includes("not found") ? 404 : 400);
|
|
3364
|
+
}
|
|
3365
|
+
});
|
|
3165
3366
|
app.put("/api/sync/sharing-domains/project-mappings", async (c) => {
|
|
3166
3367
|
const store = getStore();
|
|
3167
3368
|
const body = await parseViewerJsonBody(c);
|
|
@@ -3185,9 +3386,9 @@ function syncRoutes(getStore, getSyncRuntimeStatus) {
|
|
|
3185
3386
|
source
|
|
3186
3387
|
};
|
|
3187
3388
|
const analysis = analyzeProjectScopeMappingChangeGuardrails(store.db, mappingInput);
|
|
3188
|
-
if (analysis.requested_workspace_identity?.startsWith("unmapped:")
|
|
3389
|
+
if (analysis.requested_workspace_identity?.startsWith("unmapped:")) return c.json({
|
|
3189
3390
|
error: "unmapped_project_local_only",
|
|
3190
|
-
message: "Unmapped projects need a stable path, git remote, or workspace id before assigning a
|
|
3391
|
+
message: "Unmapped projects need a stable path, git remote, or workspace id before assigning a Sharing domain.",
|
|
3191
3392
|
guardrail_warnings: analysis.warnings
|
|
3192
3393
|
}, 400);
|
|
3193
3394
|
const missingConfirmations = missingGuardrailConfirmations(analysis.warnings, confirmedGuardrailTokens);
|
|
@@ -4706,6 +4907,10 @@ function createApp(opts) {
|
|
|
4706
4907
|
app.route("/", rawEventsRoutes(storeFactory, sweeper));
|
|
4707
4908
|
app.route("/", syncRoutes(storeFactory, getSyncRuntimeStatus));
|
|
4708
4909
|
const staticRoot = process.env.CODEMEM_VIEWER_STATIC_DIR ?? join(import.meta.dirname ?? ".", "../static");
|
|
4910
|
+
app.use("/assets/*", async (c, next) => {
|
|
4911
|
+
c.header("Cache-Control", "no-cache");
|
|
4912
|
+
await next();
|
|
4913
|
+
});
|
|
4709
4914
|
app.use("/assets/*", serveStatic({
|
|
4710
4915
|
root: staticRoot,
|
|
4711
4916
|
rewriteRequestPath: (path) => path.replace(/^\/assets/, "")
|
|
@@ -4715,6 +4920,7 @@ function createApp(opts) {
|
|
|
4715
4920
|
const indexHtml = readFileSync(indexPath, "utf-8");
|
|
4716
4921
|
app.get("*", (c) => {
|
|
4717
4922
|
if (c.req.path.startsWith("/api/")) return c.json({ error: "not found" }, 404);
|
|
4923
|
+
c.header("Cache-Control", "no-store");
|
|
4718
4924
|
return c.html(indexHtml);
|
|
4719
4925
|
});
|
|
4720
4926
|
return app;
|