@codemem/server 0.22.0 → 0.22.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 +8 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +182 -18
- package/dist/index.js.map +1 -1
- package/dist/request-rate-limit.d.ts +8 -0
- package/dist/request-rate-limit.d.ts.map +1 -0
- package/dist/routes/config.d.ts.map +1 -1
- package/dist/routes/stats.d.ts +1 -1
- package/dist/routes/stats.d.ts.map +1 -1
- package/dist/routes/sync.d.ts +11 -1
- package/dist/routes/sync.d.ts.map +1 -1
- package/package.json +3 -2
- package/static/app.js +80 -40
- package/static/index.html +8 -0
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface InMemoryRequestRateLimiter {
|
|
2
|
+
check(key: string, limit: number): {
|
|
3
|
+
allowed: boolean;
|
|
4
|
+
retryAfterS: number;
|
|
5
|
+
};
|
|
6
|
+
}
|
|
7
|
+
export declare function createInMemoryRequestRateLimiter(windowMs?: number): InMemoryRequestRateLimiter;
|
|
8
|
+
//# sourceMappingURL=request-rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-rate-limit.d.ts","sourceRoot":"","sources":["../src/request-rate-limit.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,0BAA0B;IAC1C,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7E;AAED,wBAAgB,gCAAgC,CAAC,QAAQ,SAAS,GAAG,0BAA0B,CA2C9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAKN,KAAK,eAAe,EAKpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/routes/config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,EAKN,KAAK,eAAe,EAKpB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA6E5B,MAAM,WAAW,kBAAkB;IAClC,UAAU,CAAC,EAAE,MAAM,eAAe,GAAG,IAAI,CAAC;CAC1C;AAmOD,wBAAgB,YAAY,CAAC,IAAI,GAAE,kBAAuB,8EA6GzD"}
|
package/dist/routes/stats.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/routes/stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/routes/stats.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,WAAW,EAAW,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B;;;GAGG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,WAAW,8EA2HtD"}
|
package/dist/routes/sync.d.ts
CHANGED
|
@@ -3,11 +3,21 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { MemoryStore } from "@codemem/core";
|
|
5
5
|
import { Hono } from "hono";
|
|
6
|
+
import type { InMemoryRequestRateLimiter } from "../request-rate-limit.js";
|
|
6
7
|
type StoreFactory = () => MemoryStore;
|
|
7
8
|
type SyncRuntimeStatus = {
|
|
8
9
|
phase: "starting" | "running" | "stopping" | "error" | "disabled" | "rebootstrapping" | "needs_attention" | null;
|
|
9
10
|
detail?: string | null;
|
|
10
11
|
};
|
|
12
|
+
interface SyncProtocolRouteOptions {
|
|
13
|
+
routeRateLimit?: {
|
|
14
|
+
limiter: InMemoryRequestRateLimiter;
|
|
15
|
+
readLimit: number;
|
|
16
|
+
mutationLimit: number;
|
|
17
|
+
unauthenticatedReadLimit: number;
|
|
18
|
+
unauthenticatedMutationLimit: number;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
11
21
|
/**
|
|
12
22
|
* Peer-to-peer sync protocol routes (/v1/*).
|
|
13
23
|
*
|
|
@@ -15,7 +25,7 @@ type SyncRuntimeStatus = {
|
|
|
15
25
|
* network-accessible. All requests are auth-gated via signature
|
|
16
26
|
* verification so unauthenticated callers are rejected.
|
|
17
27
|
*/
|
|
18
|
-
export declare function syncProtocolRoutes(getStore: StoreFactory): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
28
|
+
export declare function syncProtocolRoutes(getStore: StoreFactory, opts?: SyncProtocolRouteOptions): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
19
29
|
/**
|
|
20
30
|
* Viewer-facing sync management routes (/api/sync/*).
|
|
21
31
|
*
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/routes/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AA2BhE,OAAO,
|
|
1
|
+
{"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../src/routes/sync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,eAAe,CAAC;AA2BhE,OAAO,EAAgB,IAAI,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAE3E,KAAK,YAAY,GAAG,MAAM,WAAW,CAAC;AACtC,KAAK,iBAAiB,GAAG;IACxB,KAAK,EACF,UAAU,GACV,SAAS,GACT,UAAU,GACV,OAAO,GACP,UAAU,GACV,iBAAiB,GACjB,iBAAiB,GACjB,IAAI,CAAC;IACR,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC;AAEF,UAAU,wBAAwB;IACjC,cAAc,CAAC,EAAE;QAChB,OAAO,EAAE,0BAA0B,CAAC;QACpC,SAAS,EAAE,MAAM,CAAC;QAClB,aAAa,EAAE,MAAM,CAAC;QACtB,wBAAwB,EAAE,MAAM,CAAC;QACjC,4BAA4B,EAAE,MAAM,CAAC;KACrC,CAAC;CACF;AA8pBD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,EAAE,IAAI,GAAE,wBAA6B,8EA+O7F;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CACzB,QAAQ,EAAE,YAAY,EACtB,oBAAoB,CAAC,EAAE,MAAM,iBAAiB,GAAG,IAAI,8EAyzBrD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codemem/server",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"exports": {
|
|
@@ -18,7 +18,8 @@
|
|
|
18
18
|
"@hono/node-server": "^1.14.3",
|
|
19
19
|
"drizzle-orm": "^0.45.1",
|
|
20
20
|
"hono": "^4.7.10",
|
|
21
|
-
"
|
|
21
|
+
"limiter": "^3.0.0",
|
|
22
|
+
"@codemem/core": "^0.22.1"
|
|
22
23
|
},
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/better-sqlite3": "^7.6.13",
|
package/static/app.js
CHANGED
|
@@ -252,6 +252,9 @@
|
|
|
252
252
|
async function loadStats() {
|
|
253
253
|
return fetchJson("/api/stats");
|
|
254
254
|
}
|
|
255
|
+
async function loadRuntimeInfo() {
|
|
256
|
+
return fetchJson("/api/runtime");
|
|
257
|
+
}
|
|
255
258
|
async function loadUsage(project) {
|
|
256
259
|
return fetchJson(`/api/usage?project=${encodeURIComponent(project)}`);
|
|
257
260
|
}
|
|
@@ -2366,6 +2369,10 @@
|
|
|
2366
2369
|
if (peer?.status?.fresh) return "connected";
|
|
2367
2370
|
return "waiting";
|
|
2368
2371
|
}
|
|
2372
|
+
function isOfflineTeamDevice(device) {
|
|
2373
|
+
if (!device.discovered?.stale) return false;
|
|
2374
|
+
return device.peer ? derivePeerUiStatus(device.peer) !== "connected" : true;
|
|
2375
|
+
}
|
|
2369
2376
|
function peerErrorText(peer) {
|
|
2370
2377
|
return cleanText(peer?.last_error).toLowerCase();
|
|
2371
2378
|
}
|
|
@@ -2627,7 +2634,7 @@
|
|
|
2627
2634
|
summary: {
|
|
2628
2635
|
connectedDeviceCount: peers.filter((peer) => derivePeerUiStatus(peer) === "connected").length,
|
|
2629
2636
|
seenOnTeamCount: discoveredDevices.length,
|
|
2630
|
-
offlineTeamDeviceCount:
|
|
2637
|
+
offlineTeamDeviceCount: mergedDevices.filter((device) => isOfflineTeamDevice(device)).length
|
|
2631
2638
|
},
|
|
2632
2639
|
duplicatePeople,
|
|
2633
2640
|
attentionItems: attentionItems.sort((a, b) => a.priority - b.priority || a.title.localeCompare(b.title))
|
|
@@ -4145,11 +4152,6 @@
|
|
|
4145
4152
|
function assignedActorCount(actorId) {
|
|
4146
4153
|
return (Array.isArray(state.lastSyncPeers) ? state.lastSyncPeers : []).filter((peer) => String(peer?.actor_id || "") === actorId).length;
|
|
4147
4154
|
}
|
|
4148
|
-
function assignmentNote(actorId) {
|
|
4149
|
-
if (!actorId) return "Unassigned devices keep legacy fallback attribution until you choose a person.";
|
|
4150
|
-
if ((Array.isArray(state.lastSyncActors) ? state.lastSyncActors : []).find((item) => String(item?.actor_id || "") === actorId)?.is_local) return "Assigning this device to you keeps it in your identity across your devices, including private sync.";
|
|
4151
|
-
return "This person receives memories from allowed projects by default. Use Only me on a memory when it should stay local.";
|
|
4152
|
-
}
|
|
4153
4155
|
function visibleSyncActors() {
|
|
4154
4156
|
return deriveVisiblePeopleActors({
|
|
4155
4157
|
actors: state.lastSyncActors,
|
|
@@ -4763,7 +4765,10 @@
|
|
|
4763
4765
|
children: [/* @__PURE__ */ u("div", {
|
|
4764
4766
|
className: "actor-title",
|
|
4765
4767
|
children: [
|
|
4766
|
-
/* @__PURE__ */ u("strong", {
|
|
4768
|
+
/* @__PURE__ */ u("strong", {
|
|
4769
|
+
title: row.displayTitle || void 0,
|
|
4770
|
+
children: row.displayName
|
|
4771
|
+
}),
|
|
4767
4772
|
/* @__PURE__ */ u("span", {
|
|
4768
4773
|
className: `badge actor-badge${row.availabilityLabel === "Offline" ? "" : " local"}`,
|
|
4769
4774
|
children: row.availabilityLabel
|
|
@@ -9957,7 +9962,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
9957
9962
|
ensureJoinPanelInSetupSection();
|
|
9958
9963
|
const coordinator = state.lastSyncCoordinator;
|
|
9959
9964
|
const syncView = state.lastSyncViewModel || {
|
|
9960
|
-
summary: {
|
|
9965
|
+
summary: {
|
|
9966
|
+
connectedDeviceCount: 0,
|
|
9967
|
+
seenOnTeamCount: 0,
|
|
9968
|
+
offlineTeamDeviceCount: 0
|
|
9969
|
+
},
|
|
9970
|
+
duplicatePeople: [],
|
|
9961
9971
|
attentionItems: []
|
|
9962
9972
|
};
|
|
9963
9973
|
const focusAttentionTarget = (item) => {
|
|
@@ -10080,7 +10090,11 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10080
10090
|
const attentionItems = Array.isArray(syncView.attentionItems) ? syncView.attentionItems : [];
|
|
10081
10091
|
const discoveredRows = (Array.isArray(coordinator.discovered_devices) ? coordinator.discovered_devices : []).map((device) => {
|
|
10082
10092
|
const deviceId = String(device.device_id || "").trim();
|
|
10083
|
-
const displayName =
|
|
10093
|
+
const displayName = resolveFriendlyDeviceName({
|
|
10094
|
+
coordinatorName: String(device.display_name || "").trim(),
|
|
10095
|
+
deviceId
|
|
10096
|
+
}) || "Discovered device";
|
|
10097
|
+
const displayTitle = deviceId && displayName !== deviceId ? deviceId : null;
|
|
10084
10098
|
const fingerprint = String(device.fingerprint || "").trim();
|
|
10085
10099
|
const hasAmbiguousCoordinatorGroup = (Array.isArray(device.groups) ? device.groups.map((value) => String(value || "").trim()).filter(Boolean) : []).length > 1;
|
|
10086
10100
|
const pairedPeer = localPeers.find((peer) => String(peer?.peer_device_id || "") === deviceId);
|
|
@@ -10092,7 +10106,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10092
10106
|
const hasConflict = Boolean(pairedPeer) && Boolean(fingerprint) && Boolean(pairedFingerprint) && pairedFingerprint !== fingerprint;
|
|
10093
10107
|
const canAccept = Boolean(deviceId) && Boolean(fingerprint) && !pairedPeer && !device.stale && !hasAmbiguousCoordinatorGroup;
|
|
10094
10108
|
const addresses = Array.isArray(device.addresses) ? device.addresses : [];
|
|
10095
|
-
const noteParts = [
|
|
10109
|
+
const noteParts = [addresses.length ? addresses.map((address) => isSyncRedactionEnabled() ? redactAddress(String(address || "")) : String(address || "")).filter(Boolean).join(" · ") : "No fresh addresses"];
|
|
10110
|
+
if (displayTitle) noteParts.push(`device id: ${deviceId}`);
|
|
10096
10111
|
let actionMessage = null;
|
|
10097
10112
|
let mode = canAccept ? "accept" : "none";
|
|
10098
10113
|
let pairedMessage = null;
|
|
@@ -10127,6 +10142,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10127
10142
|
connectionLabel: hasConflict ? SYNC_TERMINOLOGY.conflicts : pairedPeer ? SYNC_TERMINOLOGY.pairedLocally : "Not connected on this device",
|
|
10128
10143
|
deviceId,
|
|
10129
10144
|
displayName,
|
|
10145
|
+
displayTitle,
|
|
10130
10146
|
fingerprint,
|
|
10131
10147
|
mode,
|
|
10132
10148
|
note: noteParts.join(" · "),
|
|
@@ -10544,9 +10560,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10544
10560
|
function listText(value) {
|
|
10545
10561
|
return Array.isArray(value) ? value.map((item) => String(item || "").trim()).filter(Boolean) : [];
|
|
10546
10562
|
}
|
|
10547
|
-
function summaryText(prefix, values, emptyLabel) {
|
|
10548
|
-
return `${prefix}: ${values.join(", ") || emptyLabel}`;
|
|
10549
|
-
}
|
|
10550
10563
|
function ExistingElementSlot({ element }) {
|
|
10551
10564
|
const hostRef = A$1(null);
|
|
10552
10565
|
_(() => {
|
|
@@ -10569,9 +10582,9 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10569
10582
|
const scope = peer.project_scope || {};
|
|
10570
10583
|
const includeList = listText(scope.include);
|
|
10571
10584
|
const excludeList = listText(scope.exclude);
|
|
10572
|
-
|
|
10573
|
-
|
|
10574
|
-
|
|
10585
|
+
listText(scope.effective_include);
|
|
10586
|
+
listText(scope.effective_exclude);
|
|
10587
|
+
Boolean(scope.inherits_global);
|
|
10575
10588
|
const primaryAddress = pickPrimaryAddress(peer.addresses);
|
|
10576
10589
|
const peerAddresses = Array.isArray(peer.addresses) ? Array.from(new Set(peer.addresses.filter(Boolean).map((value) => String(value)))) : [];
|
|
10577
10590
|
const addressLine = peerAddresses.length ? peerAddresses.map((address) => isSyncRedactionEnabled() ? redactAddress(address) : address).join(" · ") : "No addresses";
|
|
@@ -10876,18 +10889,6 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
10876
10889
|
children: applyActorLabel
|
|
10877
10890
|
})]
|
|
10878
10891
|
}),
|
|
10879
|
-
/* @__PURE__ */ u("div", {
|
|
10880
|
-
className: "peer-scope-effective",
|
|
10881
|
-
children: assignmentNote(selectedActorId)
|
|
10882
|
-
}),
|
|
10883
|
-
/* @__PURE__ */ u("div", {
|
|
10884
|
-
className: "peer-scope-summary",
|
|
10885
|
-
children: inheritsGlobal ? "Using global sync scope" : `Device override · include: ${includeList.join(", ") || "all"} · exclude: ${excludeList.join(", ") || "none"}`
|
|
10886
|
-
}),
|
|
10887
|
-
/* @__PURE__ */ u("div", {
|
|
10888
|
-
className: "peer-scope-effective",
|
|
10889
|
-
children: `Effective scope · ${summaryText("include", effectiveInclude, "all")} · ${summaryText("exclude", effectiveExclude, "none")}`
|
|
10890
|
-
}),
|
|
10891
10892
|
/* @__PURE__ */ u("div", { ref: setScopeHost })
|
|
10892
10893
|
]
|
|
10893
10894
|
})
|
|
@@ -11157,6 +11158,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11157
11158
|
//#region src/tabs/sync/index.ts
|
|
11158
11159
|
var lastSyncHash = "";
|
|
11159
11160
|
var cachedSyncStatus = null;
|
|
11161
|
+
var latestSyncLoadRequestId = 0;
|
|
11160
11162
|
var HEALTH_SYNC_STATUS_CACHE_TTL_MS = 15e3;
|
|
11161
11163
|
function syncStatusCacheKey(project) {
|
|
11162
11164
|
return `project:${project || ""}|includeJoinRequests:false`;
|
|
@@ -11183,20 +11185,22 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11183
11185
|
};
|
|
11184
11186
|
}
|
|
11185
11187
|
async function loadSyncData() {
|
|
11188
|
+
const requestId = ++latestSyncLoadRequestId;
|
|
11186
11189
|
try {
|
|
11187
11190
|
const project = state.currentProject || "";
|
|
11188
11191
|
const includeJoinRequests = state.activeTab === "sync";
|
|
11189
11192
|
const useCache = state.activeTab === "health";
|
|
11193
|
+
let fetchedFreshSyncStatus = false;
|
|
11190
11194
|
let payload;
|
|
11191
11195
|
if (useCache) {
|
|
11192
11196
|
payload = readCachedSyncStatus(project);
|
|
11193
11197
|
if (!payload) {
|
|
11194
11198
|
payload = await loadSyncStatus(true, project, { includeJoinRequests: false });
|
|
11195
|
-
|
|
11199
|
+
fetchedFreshSyncStatus = true;
|
|
11196
11200
|
}
|
|
11197
11201
|
} else {
|
|
11198
11202
|
payload = await loadSyncStatus(true, project, { includeJoinRequests });
|
|
11199
|
-
|
|
11203
|
+
fetchedFreshSyncStatus = true;
|
|
11200
11204
|
}
|
|
11201
11205
|
let actorsPayload = null;
|
|
11202
11206
|
let actorLoadError = false;
|
|
@@ -11206,6 +11210,8 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11206
11210
|
} catch {
|
|
11207
11211
|
actorLoadError = true;
|
|
11208
11212
|
}
|
|
11213
|
+
if (requestId !== latestSyncLoadRequestId) return;
|
|
11214
|
+
if (fetchedFreshSyncStatus) writeCachedSyncStatus(project, normalizeSyncStatusForCache(payload));
|
|
11209
11215
|
const hash = JSON.stringify([
|
|
11210
11216
|
payload,
|
|
11211
11217
|
actorsPayload,
|
|
@@ -11250,6 +11256,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11250
11256
|
if (actorMeta) actorMeta.textContent = "People controls are temporarily unavailable. Device status and sync health still loaded.";
|
|
11251
11257
|
}
|
|
11252
11258
|
} catch {
|
|
11259
|
+
if (requestId !== latestSyncLoadRequestId) return;
|
|
11253
11260
|
hideSkeleton("syncTeamSkeleton");
|
|
11254
11261
|
hideSkeleton("syncActorsSkeleton");
|
|
11255
11262
|
hideSkeleton("syncPeersSkeleton");
|
|
@@ -11285,6 +11292,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11285
11292
|
var settingsEnvOverrides = {};
|
|
11286
11293
|
var settingsTouchedKeys = /* @__PURE__ */ new Set();
|
|
11287
11294
|
var settingsShellMounted = false;
|
|
11295
|
+
var settingsProtectedKeys = /* @__PURE__ */ new Set();
|
|
11288
11296
|
var settingsStartPolling = null;
|
|
11289
11297
|
var settingsRefresh = null;
|
|
11290
11298
|
var SETTINGS_ADVANCED_KEY = "codemem-settings-advanced";
|
|
@@ -11317,6 +11325,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11317
11325
|
syncCoordinatorTimeout: "sync_coordinator_timeout_s",
|
|
11318
11326
|
syncCoordinatorPresenceTtl: "sync_coordinator_presence_ttl_s"
|
|
11319
11327
|
};
|
|
11328
|
+
var PROTECTED_VIEWER_CONFIG_KEYS = new Set([
|
|
11329
|
+
"claude_command",
|
|
11330
|
+
"observer_base_url",
|
|
11331
|
+
"observer_auth_file",
|
|
11332
|
+
"observer_auth_command",
|
|
11333
|
+
"observer_headers",
|
|
11334
|
+
"sync_coordinator_url"
|
|
11335
|
+
]);
|
|
11320
11336
|
var settingsRenderState = {
|
|
11321
11337
|
effectiveText: "",
|
|
11322
11338
|
isSaving: false,
|
|
@@ -11802,6 +11818,12 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11802
11818
|
type: hasWarning ? "warning" : "success"
|
|
11803
11819
|
};
|
|
11804
11820
|
}
|
|
11821
|
+
function isProtectedConfigKey(key) {
|
|
11822
|
+
return settingsProtectedKeys.has(key) || PROTECTED_VIEWER_CONFIG_KEYS.has(key);
|
|
11823
|
+
}
|
|
11824
|
+
function protectedConfigHelp(key) {
|
|
11825
|
+
return `${key} is read-only in the viewer for security. Edit the config file or environment instead.`;
|
|
11826
|
+
}
|
|
11805
11827
|
function formStateFromPayload(payload) {
|
|
11806
11828
|
const config = payload.config || {};
|
|
11807
11829
|
const effective = payload.effective || {};
|
|
@@ -11842,8 +11864,10 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11842
11864
|
const defaults = payload.defaults || {};
|
|
11843
11865
|
const config = payload.config || {};
|
|
11844
11866
|
const envOverrides = payload.env_overrides && typeof payload.env_overrides === "object" ? payload.env_overrides : {};
|
|
11867
|
+
const protectedKeys = Array.isArray(payload.protected_keys) ? payload.protected_keys.filter((value) => typeof value === "string" && value.trim().length > 0) : [];
|
|
11845
11868
|
const values = formStateFromPayload(payload);
|
|
11846
11869
|
settingsEnvOverrides = envOverrides;
|
|
11870
|
+
settingsProtectedKeys = new Set(protectedKeys);
|
|
11847
11871
|
state.configDefaults = defaults;
|
|
11848
11872
|
state.configPath = payload.path || "";
|
|
11849
11873
|
updateRenderState({
|
|
@@ -11996,6 +12020,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
11996
12020
|
const current = collectSettingsPayload({ allowUntouchedParseErrors: true });
|
|
11997
12021
|
const changed = {};
|
|
11998
12022
|
Object.entries(current).forEach(([key, value]) => {
|
|
12023
|
+
if (isProtectedConfigKey(key)) return;
|
|
11999
12024
|
if (hasOwn(settingsEnvOverrides, key) && !settingsTouchedKeys.has(key)) return;
|
|
12000
12025
|
if (!isEqualValue(value, settingsBaseline[key])) changed[key] = value;
|
|
12001
12026
|
});
|
|
@@ -12398,15 +12423,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12398
12423
|
children: "Claude command (JSON argv)"
|
|
12399
12424
|
}),
|
|
12400
12425
|
/* @__PURE__ */ u("textarea", {
|
|
12426
|
+
disabled: true,
|
|
12401
12427
|
id: "claudeCommand",
|
|
12402
|
-
onInput: onTextInput("claudeCommand"),
|
|
12403
12428
|
placeholder: "[\"claude\"]",
|
|
12404
12429
|
rows: 2,
|
|
12405
12430
|
value: values.claudeCommand
|
|
12406
12431
|
}),
|
|
12407
12432
|
/* @__PURE__ */ u("div", {
|
|
12408
12433
|
className: "small",
|
|
12409
|
-
children: "
|
|
12434
|
+
children: protectedConfigHelp("claude_command")
|
|
12410
12435
|
})
|
|
12411
12436
|
]
|
|
12412
12437
|
}),
|
|
@@ -12496,14 +12521,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12496
12521
|
children: "Token file path"
|
|
12497
12522
|
}),
|
|
12498
12523
|
/* @__PURE__ */ u("input", {
|
|
12524
|
+
disabled: true,
|
|
12499
12525
|
id: "observerAuthFile",
|
|
12500
|
-
onInput: onTextInput("observerAuthFile"),
|
|
12501
12526
|
placeholder: "~/.codemem/work-token.txt",
|
|
12502
12527
|
value: values.observerAuthFile
|
|
12503
12528
|
}),
|
|
12504
12529
|
/* @__PURE__ */ u("div", {
|
|
12505
12530
|
className: "small",
|
|
12506
|
-
children: "
|
|
12531
|
+
children: protectedConfigHelp("observer_auth_file")
|
|
12507
12532
|
})
|
|
12508
12533
|
]
|
|
12509
12534
|
}),
|
|
@@ -12525,15 +12550,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12525
12550
|
})]
|
|
12526
12551
|
}),
|
|
12527
12552
|
/* @__PURE__ */ u("textarea", {
|
|
12553
|
+
disabled: true,
|
|
12528
12554
|
id: "observerAuthCommand",
|
|
12529
|
-
onInput: onTextInput("observerAuthCommand"),
|
|
12530
12555
|
placeholder: "[\"iap-auth\", \"--audience\", \"gateway\"]",
|
|
12531
12556
|
rows: 3,
|
|
12532
12557
|
value: values.observerAuthCommand
|
|
12533
12558
|
}),
|
|
12534
12559
|
/* @__PURE__ */ u("div", {
|
|
12535
12560
|
className: "small",
|
|
12536
|
-
children: "
|
|
12561
|
+
children: protectedConfigHelp("observer_auth_command")
|
|
12537
12562
|
})
|
|
12538
12563
|
]
|
|
12539
12564
|
}),
|
|
@@ -12589,15 +12614,15 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12589
12614
|
})]
|
|
12590
12615
|
}),
|
|
12591
12616
|
/* @__PURE__ */ u("textarea", {
|
|
12617
|
+
disabled: true,
|
|
12592
12618
|
id: "observerHeaders",
|
|
12593
|
-
onInput: onTextInput("observerHeaders"),
|
|
12594
12619
|
placeholder: "{\"Authorization\":\"Bearer ${auth.token}\"}",
|
|
12595
12620
|
rows: 4,
|
|
12596
12621
|
value: values.observerHeaders
|
|
12597
12622
|
}),
|
|
12598
12623
|
/* @__PURE__ */ u("div", {
|
|
12599
12624
|
className: "small",
|
|
12600
|
-
children:
|
|
12625
|
+
children: protectedConfigHelp("observer_headers")
|
|
12601
12626
|
})
|
|
12602
12627
|
]
|
|
12603
12628
|
})
|
|
@@ -12781,14 +12806,14 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12781
12806
|
children: "Coordinator URL"
|
|
12782
12807
|
}),
|
|
12783
12808
|
/* @__PURE__ */ u("input", {
|
|
12809
|
+
disabled: true,
|
|
12784
12810
|
id: "syncCoordinatorUrl",
|
|
12785
|
-
onInput: onTextInput("syncCoordinatorUrl"),
|
|
12786
12811
|
placeholder: "https://coord.example.com",
|
|
12787
12812
|
value: values.syncCoordinatorUrl
|
|
12788
12813
|
}),
|
|
12789
12814
|
/* @__PURE__ */ u("div", {
|
|
12790
12815
|
className: "small",
|
|
12791
|
-
children: "
|
|
12816
|
+
children: protectedConfigHelp("sync_coordinator_url")
|
|
12792
12817
|
})
|
|
12793
12818
|
]
|
|
12794
12819
|
}),
|
|
@@ -12888,6 +12913,20 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
12888
12913
|
}
|
|
12889
12914
|
//#endregion
|
|
12890
12915
|
//#region src/app.ts
|
|
12916
|
+
function setRuntimeLabel(version, commit) {
|
|
12917
|
+
const el = $$2("runtimeLabel");
|
|
12918
|
+
if (!el) return;
|
|
12919
|
+
el.textContent = commit ? `v${version} (${commit})` : `v${version}`;
|
|
12920
|
+
el.title = commit ? `codemem ${version} (${commit})` : `codemem ${version}`;
|
|
12921
|
+
el.hidden = false;
|
|
12922
|
+
}
|
|
12923
|
+
async function loadRuntimeLabel() {
|
|
12924
|
+
try {
|
|
12925
|
+
const runtime = await loadRuntimeInfo();
|
|
12926
|
+
if (!runtime?.version) return;
|
|
12927
|
+
setRuntimeLabel(runtime.version, "4a76585");
|
|
12928
|
+
} catch {}
|
|
12929
|
+
}
|
|
12891
12930
|
var lastAnnouncedRefreshState = null;
|
|
12892
12931
|
function setRefreshStatus(rs, detail) {
|
|
12893
12932
|
state.refreshState = rs;
|
|
@@ -13022,6 +13061,7 @@ For more information, see https://radix-ui.com/primitives/docs/components/${titl
|
|
|
13022
13061
|
initSyncTab(() => refresh());
|
|
13023
13062
|
initSettings(stopPolling, startPolling, () => refresh());
|
|
13024
13063
|
loadProjects();
|
|
13064
|
+
loadRuntimeLabel();
|
|
13025
13065
|
refresh();
|
|
13026
13066
|
startPolling();
|
|
13027
13067
|
//#endregion
|
package/static/index.html
CHANGED
|
@@ -346,6 +346,13 @@
|
|
|
346
346
|
max-width: 300px;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
.runtime-label {
|
|
350
|
+
font-size: 11px;
|
|
351
|
+
color: var(--text-tertiary);
|
|
352
|
+
opacity: 0.8;
|
|
353
|
+
white-space: nowrap;
|
|
354
|
+
}
|
|
355
|
+
|
|
349
356
|
/* ── Tab bar ───────────────────────────────────────────── */
|
|
350
357
|
|
|
351
358
|
.tab-bar {
|
|
@@ -1625,6 +1632,7 @@
|
|
|
1625
1632
|
<span class="sr-only" id="refreshAnnouncer" aria-live="polite"></span>
|
|
1626
1633
|
</div>
|
|
1627
1634
|
<div class="header-right">
|
|
1635
|
+
<span class="runtime-label" id="runtimeLabel" hidden></span>
|
|
1628
1636
|
<span class="meta-line" id="metaLine"></span>
|
|
1629
1637
|
<select class="project-filter" id="projectFilter" aria-label="Project filter">
|
|
1630
1638
|
<option value="">All Projects</option>
|