sealkeep 0.9.0 → 0.11.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/ARCHITECTURE.md +163 -14
- package/CHANGELOG.md +223 -1
- package/CONTROL_PLANE.md +2 -2
- package/LICENSE +1 -1
- package/README.md +108 -22
- package/THIRD_PARTY.md +2 -2
- package/THREAT_MODEL.md +23 -4
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/chunk-access.d.ts +26 -4
- package/dist/packages/sealkeep-crypto/src/chunk-access.js +219 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.d.ts +11 -1
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/envelope.js +58 -21
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.d.ts +2 -2
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.d.ts +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/index.js +1 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.js +4 -4
- package/dist/packages/sealkeep-crypto/src/squeeze.d.ts +6 -0
- package/dist/packages/sealkeep-crypto/src/squeeze.js +39 -0
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.d.ts +36 -3
- package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/stream.js +196 -37
- package/dist/site/index.html +1808 -1904
- package/dist/site/llms.txt +67 -0
- package/dist/site/trust/architecture-data-flow.html +53 -0
- package/dist/site/trust/audit-roadmap.html +37 -0
- package/dist/site/trust/deployment-responsibility.html +11 -0
- package/dist/site/trust/dpa-sample.html +30 -0
- package/dist/site/trust/release-provenance.html +21 -0
- package/dist/site/trust/subprocessors.html +15 -0
- package/dist/site/trust/threat-model.html +34 -0
- package/dist/site/trust/toms.html +41 -0
- package/dist/site/trust-document.css +32 -0
- package/dist/site/trust.html +73 -0
- package/dist/site/visual/assets/index-8Nxnkc7o.js +207 -0
- package/dist/site/visual/assets/index-oxLy1bpy.css +1 -0
- package/dist/site/visual/index.html +18 -0
- package/dist/site.zip +0 -0
- package/dist/src/activity.d.ts +9 -0
- package/dist/src/activity.js +90 -1
- package/dist/src/adapters.d.ts +175 -5
- package/dist/src/adapters.js +961 -73
- package/dist/src/agent-context.d.ts +135 -0
- package/dist/src/agent-context.js +1059 -0
- package/dist/src/archive-copies.d.ts +47 -0
- package/dist/src/archive-copies.js +179 -0
- package/dist/src/audit.d.ts +1 -1
- package/dist/src/audit.js +29 -4
- package/dist/src/autopilot.d.ts +52 -7
- package/dist/src/autopilot.js +143 -25
- package/dist/src/background-bandwidth.d.ts +46 -0
- package/dist/src/background-bandwidth.js +301 -0
- package/dist/src/background-cpu.d.ts +82 -0
- package/dist/src/background-cpu.js +212 -0
- package/dist/src/background-worker-error.d.ts +12 -0
- package/dist/src/background-worker-error.js +18 -0
- package/dist/src/branding.d.ts +24 -1
- package/dist/src/branding.js +31 -1
- package/dist/src/bridge.d.ts +233 -0
- package/dist/src/bridge.js +604 -0
- package/dist/src/byte-stream.d.ts +91 -0
- package/dist/src/byte-stream.js +385 -0
- package/dist/src/chunk-store.d.ts +41 -8
- package/dist/src/chunk-store.js +161 -65
- package/dist/src/cli.js +1698 -163
- package/dist/src/cloud.d.ts +841 -31
- package/dist/src/cloud.js +3196 -277
- package/dist/src/context-background.d.ts +37 -0
- package/dist/src/context-background.js +309 -0
- package/dist/src/context-drain-child.d.ts +1 -0
- package/dist/src/context-drain-child.js +98 -0
- package/dist/src/context-reader.d.ts +118 -0
- package/dist/src/context-reader.js +447 -0
- package/dist/src/control-plane/auth.d.ts +32 -4
- package/dist/src/control-plane/auth.js +85 -24
- package/dist/src/control-plane/server.js +19 -6
- package/dist/src/control-plane.d.ts +17 -1
- package/dist/src/control-plane.js +32 -6
- package/dist/src/crypto.d.ts +1 -1
- package/dist/src/crypto.js +5 -5
- package/dist/src/daemon-lease.d.ts +70 -0
- package/dist/src/daemon-lease.js +420 -0
- package/dist/src/daemon.d.ts +94 -1
- package/dist/src/daemon.js +1082 -105
- package/dist/src/darwin-service-policy.d.ts +41 -0
- package/dist/src/darwin-service-policy.js +60 -0
- package/dist/src/dashboard-cli.js +15 -15
- package/dist/src/device-authorization.d.ts +37 -0
- package/dist/src/device-authorization.js +199 -0
- package/dist/src/device-enrollment.d.ts +91 -0
- package/dist/src/device-enrollment.js +349 -0
- package/dist/src/disk.d.ts +17 -12
- package/dist/src/disk.js +43 -17
- package/dist/src/doctor.d.ts +35 -1
- package/dist/src/doctor.js +316 -41
- package/dist/src/durable-ticket-lock.d.ts +24 -0
- package/dist/src/durable-ticket-lock.js +232 -0
- package/dist/src/enroll.d.ts +1 -1
- package/dist/src/enroll.js +13 -7
- package/dist/src/env.d.ts +10 -1
- package/dist/src/env.js +11 -3
- package/dist/src/errors.d.ts +8 -8
- package/dist/src/errors.js +6 -6
- package/dist/src/flush.d.ts +12 -0
- package/dist/src/flush.js +37 -0
- package/dist/src/heartbeat.d.ts +86 -12
- package/dist/src/heartbeat.js +415 -29
- package/dist/src/index-background-watchdog.d.ts +1 -0
- package/dist/src/index-background-watchdog.js +94 -0
- package/dist/src/index-background-work.d.ts +21 -0
- package/dist/src/index-background-work.js +25 -0
- package/dist/src/index-background.d.ts +64 -0
- package/dist/src/index-background.js +394 -0
- package/dist/src/index-build-child.d.ts +1 -0
- package/dist/src/index-build-child.js +109 -0
- package/dist/src/index-manifest.d.ts +52 -0
- package/dist/src/index-manifest.js +444 -0
- package/dist/src/index-publication-proof.d.ts +84 -0
- package/dist/src/index-publication-proof.js +380 -0
- package/dist/src/index-publication-state.d.ts +149 -0
- package/dist/src/index-publication-state.js +696 -0
- package/dist/src/index-publication-verifier.d.ts +89 -0
- package/dist/src/index-publication-verifier.js +341 -0
- package/dist/src/index-publish.d.ts +62 -0
- package/dist/src/index-publish.js +540 -0
- package/dist/src/index-scratch-cleanup.d.ts +19 -0
- package/dist/src/index-scratch-cleanup.js +166 -0
- package/dist/src/index-segment-types.d.ts +132 -0
- package/dist/src/index-segment-types.js +21 -0
- package/dist/src/index-segments.d.ts +9 -0
- package/dist/src/index-segments.js +516 -0
- package/dist/src/index-store.d.ts +123 -0
- package/dist/src/index-store.js +495 -0
- package/dist/src/index-sync.d.ts +91 -15
- package/dist/src/index-sync.js +286 -53
- package/dist/src/index-upgrade-publication.d.ts +30 -0
- package/dist/src/index-upgrade-publication.js +179 -0
- package/dist/src/integration-manager.d.ts +32 -0
- package/dist/src/integration-manager.js +394 -0
- package/dist/src/leakscan.js +1 -1
- package/dist/src/local-api.d.ts +56 -15
- package/dist/src/local-api.js +4979 -541
- package/dist/src/machine-settings.d.ts +51 -0
- package/dist/src/machine-settings.js +166 -0
- package/dist/src/managed-chunks.d.ts +5 -2
- package/dist/src/managed-chunks.js +14 -14
- package/dist/src/mcp-install.d.ts +11 -9
- package/dist/src/mcp-install.js +73 -28
- package/dist/src/mcp-workspace.d.ts +18 -0
- package/dist/src/mcp-workspace.js +50 -0
- package/dist/src/mcp.js +294 -25
- package/dist/src/migrate.js +27 -21
- package/dist/src/notify.d.ts +1 -1
- package/dist/src/notify.js +5 -5
- package/dist/src/offload.d.ts +201 -14
- package/dist/src/offload.js +1848 -140
- package/dist/src/onboarding.d.ts +8 -1
- package/dist/src/onboarding.js +4 -4
- package/dist/src/packages.d.ts +2 -2
- package/dist/src/packages.js +10 -2
- package/dist/src/passkey.d.ts +0 -1
- package/dist/src/passkey.js +2 -7
- package/dist/src/password-lock.d.ts +2 -2
- package/dist/src/password-lock.js +6 -6
- package/dist/src/paths.d.ts +2 -0
- package/dist/src/paths.js +2 -0
- package/dist/src/presence.d.ts +86 -0
- package/dist/src/presence.js +240 -0
- package/dist/src/progress-deadline.d.ts +21 -0
- package/dist/src/progress-deadline.js +91 -0
- package/dist/src/project-repair.d.ts +55 -0
- package/dist/src/project-repair.js +131 -0
- package/dist/src/providers/gcs.d.ts +28 -7
- package/dist/src/providers/gcs.js +35 -24
- package/dist/src/providers/gdrive.d.ts +71 -8
- package/dist/src/providers/gdrive.js +223 -50
- package/dist/src/providers/index.d.ts +11 -3
- package/dist/src/providers/index.js +30 -10
- package/dist/src/providers/s3.d.ts +30 -8
- package/dist/src/providers/s3.js +41 -30
- package/dist/src/providers/safe-storage-fetch.d.ts +12 -0
- package/dist/src/providers/safe-storage-fetch.js +72 -0
- package/dist/src/queue.d.ts +136 -19
- package/dist/src/queue.js +862 -96
- package/dist/src/reclaim-transaction.d.ts +156 -0
- package/dist/src/reclaim-transaction.js +1027 -0
- package/dist/src/recovery-codes.d.ts +32 -0
- package/dist/src/recovery-codes.js +338 -0
- package/dist/src/recovery.js +12 -9
- package/dist/src/rehydrate.d.ts +25 -22
- package/dist/src/rehydrate.js +319 -23
- package/dist/src/restore.d.ts +57 -4
- package/dist/src/restore.js +272 -36
- package/dist/src/resume-stub.d.ts +92 -0
- package/dist/src/resume-stub.js +417 -0
- package/dist/src/retention.d.ts +98 -7
- package/dist/src/retention.js +1066 -62
- package/dist/src/rotate.js +3 -3
- package/dist/src/search.d.ts +566 -8
- package/dist/src/search.js +5897 -290
- package/dist/src/secrets.d.ts +51 -7
- package/dist/src/secrets.js +316 -24
- package/dist/src/service.d.ts +49 -11
- package/dist/src/service.js +776 -35
- package/dist/src/share.js +3 -3
- package/dist/src/shared-spaces.d.ts +98 -0
- package/dist/src/shared-spaces.js +214 -0
- package/dist/src/source-reader.d.ts +73 -0
- package/dist/src/source-reader.js +715 -0
- package/dist/src/spool.d.ts +1 -1
- package/dist/src/spool.js +1 -1
- package/dist/src/start-tui.js +2 -1
- package/dist/src/start.js +2 -2
- package/dist/src/storage-endpoint.d.ts +21 -0
- package/dist/src/storage-endpoint.js +122 -0
- package/dist/src/storage-setup.js +12 -12
- package/dist/src/storage-targets.d.ts +109 -6
- package/dist/src/storage-targets.js +975 -67
- package/dist/src/stream-to-cloud.d.ts +5 -1
- package/dist/src/stream-to-cloud.js +34 -14
- package/dist/src/sync-rules.d.ts +31 -6
- package/dist/src/sync-rules.js +153 -14
- package/dist/src/team-backfill-scheduling.d.ts +8 -0
- package/dist/src/team-backfill-scheduling.js +33 -0
- package/dist/src/team-backfill.d.ts +116 -0
- package/dist/src/team-backfill.js +1429 -0
- package/dist/src/team-index-cache.d.ts +16 -0
- package/dist/src/team-index-cache.js +152 -0
- package/dist/src/team-offboarding.d.ts +38 -0
- package/dist/src/team-offboarding.js +1043 -0
- package/dist/src/team-presence.d.ts +127 -0
- package/dist/src/team-presence.js +904 -0
- package/dist/src/team-publication-policy.d.ts +20 -0
- package/dist/src/team-publication-policy.js +140 -0
- package/dist/src/team-realtime.d.ts +68 -0
- package/dist/src/team-realtime.js +816 -0
- package/dist/src/team-source-facts-cache.d.ts +23 -0
- package/dist/src/team-source-facts-cache.js +255 -0
- package/dist/src/trash.d.ts +1 -1
- package/dist/src/trash.js +2 -2
- package/dist/src/tui.js +11 -12
- package/dist/src/types.d.ts +173 -7
- package/dist/src/types.js +20 -0
- package/dist/src/ui-server.d.ts +163 -35
- package/dist/src/ui-server.js +712 -72
- package/dist/src/ui.d.ts +1 -2
- package/dist/src/ui.js +1 -2
- package/dist/src/upload.d.ts +27 -0
- package/dist/src/upload.js +383 -43
- package/dist/src/vault.d.ts +226 -30
- package/dist/src/vault.js +1776 -192
- package/dist/src/watcher.d.ts +7 -1
- package/dist/src/watcher.js +198 -55
- package/dist/src/worker.d.ts +27 -3
- package/dist/src/worker.js +274 -55
- package/package.json +33 -12
- package/scripts/native-reboot-rehearsal.mjs +90 -0
- package/web/app.js +6032 -343
- package/web/bootstrap.js +17 -0
- package/web/index.html +255 -57
- package/web/rail.js +317 -40
- package/web/retention.html +2 -2
- package/web/rules-view.js +188 -16
- package/web/sessions-view.js +485 -62
- package/web/sessions.html +2 -2
- package/web/setup-api.js +152 -29
- package/web/setup-logic.js +68 -9
- package/web/setup.html +113 -44
- package/web/setup.js +604 -71
- package/web/style.css +513 -98
- package/dist/packages/vaultline-crypto/src/chunk-access.js +0 -93
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/aead.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/format.js +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/recipients.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.d.ts +0 -0
- /package/dist/packages/{vaultline-crypto → sealkeep-crypto}/src/sha256-stream.js +0 -0
package/web/rules-view.js
CHANGED
|
@@ -15,6 +15,16 @@
|
|
|
15
15
|
const policy = () => document.querySelector('input[name=policy]:checked').value;
|
|
16
16
|
|
|
17
17
|
const MB = 1024 * 1024;
|
|
18
|
+
const COPIES_CACHE_KEY = "sealkeep.cloudCopiesView.v1";
|
|
19
|
+
// A large vault can contain thousands of stored copies. Keep the complete
|
|
20
|
+
// catalog in memory for filtering, but only create the rows a person has
|
|
21
|
+
// asked to see; each row carries controls and listeners, so eagerly painting
|
|
22
|
+
// the whole catalog can otherwise freeze the local dashboard.
|
|
23
|
+
const COPY_PAGE_SIZE = 100;
|
|
24
|
+
let copiesRefreshTimer = null;
|
|
25
|
+
let storedCopies = [];
|
|
26
|
+
let copiesVisibleLimit = COPY_PAGE_SIZE;
|
|
27
|
+
let rulesLoaded = false;
|
|
18
28
|
|
|
19
29
|
const bytes = (value) => {
|
|
20
30
|
if (!Number.isFinite(value) || value <= 0) return "0 B";
|
|
@@ -50,14 +60,14 @@
|
|
|
50
60
|
const eligible = evaluation.candidates.filter((candidate) => candidate.eligible).length;
|
|
51
61
|
$("effect").textContent = eligible === 0
|
|
52
62
|
? `Nothing is reclaimable under these rules right now — ${evaluation.candidates.length} archived session${evaluation.candidates.length === 1 ? " is" : "s are"} being tracked.`
|
|
53
|
-
: `${bytes(evaluation.reclaimableBytes)} across ${eligible} session${eligible === 1 ? "" : "s"} could be
|
|
63
|
+
: `${bytes(evaluation.reclaimableBytes)} across ${eligible} session${eligible === 1 ? "" : "s"} could be freed from this machine under these rules.`;
|
|
54
64
|
}
|
|
55
65
|
|
|
56
66
|
function fill(evaluation) {
|
|
57
67
|
// Never type over someone's hands. A refresh landing while a retention
|
|
58
68
|
// number is being edited used to revert it silently, and the next click on
|
|
59
69
|
// "Save these rules" then saved the old policy — the one that decides when
|
|
60
|
-
// original session files may be
|
|
70
|
+
// original session files may be permanently reclaimed. loadCopies already
|
|
61
71
|
// guards its one field this way; every field here needs the same.
|
|
62
72
|
const set = (id, value) => {
|
|
63
73
|
const node = $(id);
|
|
@@ -85,11 +95,31 @@
|
|
|
85
95
|
return days === 0 ? "today" : days === 1 ? "1 day" : `${days} days`;
|
|
86
96
|
};
|
|
87
97
|
|
|
98
|
+
const copyProviderName = (provider) => ({
|
|
99
|
+
vaultline: "Sealkeep Cloud",
|
|
100
|
+
gdrive: "Google Drive",
|
|
101
|
+
s3: "S3",
|
|
102
|
+
r2: "Cloudflare R2",
|
|
103
|
+
b2: "Backblaze B2",
|
|
104
|
+
gcs: "Google Cloud Storage"
|
|
105
|
+
})[provider] || provider || "unknown storage";
|
|
106
|
+
|
|
107
|
+
const copySearchText = (copy) => [copy.ref, copy.provider, copyProviderName(copy.provider)]
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
.join(" ")
|
|
110
|
+
.toLocaleLowerCase();
|
|
111
|
+
|
|
88
112
|
function copyRow(copy) {
|
|
89
113
|
const row = document.createElement("tr");
|
|
90
114
|
const ref = document.createElement("td");
|
|
91
115
|
ref.className = "path";
|
|
92
116
|
ref.textContent = copy.ref;
|
|
117
|
+
if (copy.provider) {
|
|
118
|
+
const place = document.createElement("span");
|
|
119
|
+
place.className = "lastmark";
|
|
120
|
+
place.textContent = copyProviderName(copy.provider);
|
|
121
|
+
ref.append(place);
|
|
122
|
+
}
|
|
93
123
|
if (copy.lastCopy) {
|
|
94
124
|
const mark = document.createElement("span");
|
|
95
125
|
mark.className = "lastmark";
|
|
@@ -116,6 +146,110 @@
|
|
|
116
146
|
return row;
|
|
117
147
|
}
|
|
118
148
|
|
|
149
|
+
function renderCopiesPage({ reset = false } = {}) {
|
|
150
|
+
if (reset) copiesVisibleLimit = COPY_PAGE_SIZE;
|
|
151
|
+
const filter = ($("copies-filter")?.value || "").trim().toLocaleLowerCase();
|
|
152
|
+
const matching = filter
|
|
153
|
+
? storedCopies.filter((copy) => copySearchText(copy).includes(filter))
|
|
154
|
+
: storedCopies;
|
|
155
|
+
const visible = matching.slice(0, copiesVisibleLimit);
|
|
156
|
+
const body = $("copies-body");
|
|
157
|
+
body.textContent = "";
|
|
158
|
+
for (const copy of visible) body.append(copyRow(copy));
|
|
159
|
+
|
|
160
|
+
const table = $("copies-table");
|
|
161
|
+
const controls = $("copies-controls");
|
|
162
|
+
const count = $("copies-visible");
|
|
163
|
+
const more = $("copies-more");
|
|
164
|
+
controls.hidden = storedCopies.length === 0;
|
|
165
|
+
table.hidden = visible.length === 0;
|
|
166
|
+
if (matching.length === 0 && storedCopies.length > 0) {
|
|
167
|
+
count.textContent = `No stored copies match “${$("copies-filter").value.trim()}”.`;
|
|
168
|
+
} else if (visible.length < matching.length) {
|
|
169
|
+
count.textContent = `Showing ${visible.length.toLocaleString()} of ${matching.length.toLocaleString()} ${filter ? "matching " : ""}copies.`;
|
|
170
|
+
} else {
|
|
171
|
+
count.textContent = `${matching.length.toLocaleString()} ${filter ? "matching " : ""}${matching.length === 1 ? "copy" : "copies"}.`;
|
|
172
|
+
}
|
|
173
|
+
const remaining = Math.max(0, matching.length - visible.length);
|
|
174
|
+
more.hidden = remaining === 0;
|
|
175
|
+
more.textContent = remaining > COPY_PAGE_SIZE
|
|
176
|
+
? `Show ${COPY_PAGE_SIZE.toLocaleString()} more`
|
|
177
|
+
: `Show remaining ${remaining.toLocaleString()}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const copyIdentity = (copy) => copy.copyId || `${copy.provider || "unknown"}\0${copy.ref}`;
|
|
181
|
+
|
|
182
|
+
function readCopiesCache(accountScope) {
|
|
183
|
+
if (!accountScope || typeof cacheBindingForToken !== "function") return null;
|
|
184
|
+
try {
|
|
185
|
+
const cached = JSON.parse(localStorage.getItem(COPIES_CACHE_KEY) || "null");
|
|
186
|
+
if (cached?.version !== 1 || cached.authBinding !== cacheBindingForToken(token)
|
|
187
|
+
|| cached.accountScope !== accountScope || !Array.isArray(cached.copies)) return null;
|
|
188
|
+
return cached;
|
|
189
|
+
} catch { return null; }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function writeCopiesCache(data) {
|
|
193
|
+
const accountScope = data.catalog?.accountScope;
|
|
194
|
+
if (!accountScope || typeof cacheBindingForToken !== "function") return;
|
|
195
|
+
try {
|
|
196
|
+
localStorage.setItem(COPIES_CACHE_KEY, JSON.stringify({
|
|
197
|
+
version: 1,
|
|
198
|
+
authBinding: cacheBindingForToken(token),
|
|
199
|
+
accountScope,
|
|
200
|
+
copies: data.copies,
|
|
201
|
+
mode: data.mode,
|
|
202
|
+
rollingCapGb: data.rollingCapGb,
|
|
203
|
+
loadedAt: new Date().toISOString(),
|
|
204
|
+
}));
|
|
205
|
+
} catch {
|
|
206
|
+
// The live view remains correct when browser storage is disabled/full;
|
|
207
|
+
// only the warm last-complete fallback is unavailable on the next poll.
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function mergeWithLastComplete(data) {
|
|
212
|
+
if (data.catalog?.complete) {
|
|
213
|
+
writeCopiesCache(data);
|
|
214
|
+
return data;
|
|
215
|
+
}
|
|
216
|
+
const cached = readCopiesCache(data.catalog?.accountScope);
|
|
217
|
+
if (!cached) return data;
|
|
218
|
+
const copies = [...data.copies];
|
|
219
|
+
const seen = new Set(copies.map(copyIdentity));
|
|
220
|
+
for (const copy of cached.copies) {
|
|
221
|
+
const identity = copyIdentity(copy);
|
|
222
|
+
if (!seen.has(identity)) { seen.add(identity); copies.push(copy); }
|
|
223
|
+
}
|
|
224
|
+
const providers = new Set(copies.map((copy) => copy.provider).filter(Boolean));
|
|
225
|
+
const mode = providers.size > 1
|
|
226
|
+
? "mixed"
|
|
227
|
+
: providers.has("vaultline")
|
|
228
|
+
? "managed"
|
|
229
|
+
: providers.size === 1 ? "own" : (data.mode === "none" ? cached.mode : data.mode);
|
|
230
|
+
return {
|
|
231
|
+
...data,
|
|
232
|
+
copies,
|
|
233
|
+
mode,
|
|
234
|
+
catalog: { ...data.catalog, stale: true },
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function scheduleCopiesRefresh() {
|
|
239
|
+
if (copiesRefreshTimer) clearTimeout(copiesRefreshTimer);
|
|
240
|
+
copiesRefreshTimer = setTimeout(() => {
|
|
241
|
+
copiesRefreshTimer = null;
|
|
242
|
+
// Re-entering the view calls loadCopies itself. Do not keep polling a
|
|
243
|
+
// hidden settings surface after the person has moved elsewhere.
|
|
244
|
+
if (!$('view-rules')?.hidden) void loadCopies();
|
|
245
|
+
}, 3200);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function copiesSummary(copies) {
|
|
249
|
+
const total = copies.reduce((sum, copy) => sum + copy.bytes, 0);
|
|
250
|
+
return `${copies.length} verified ${copies.length === 1 ? "copy" : "copies"} · ${bytes(total)}`;
|
|
251
|
+
}
|
|
252
|
+
|
|
119
253
|
async function deleteCopy(copy, button, message) {
|
|
120
254
|
// The design system's destructive pattern: the first click arms, the second
|
|
121
255
|
// acts, and an armed button left alone stands down after four seconds so
|
|
@@ -133,7 +267,10 @@
|
|
|
133
267
|
button.disabled = true;
|
|
134
268
|
message.textContent = "";
|
|
135
269
|
try {
|
|
136
|
-
|
|
270
|
+
const endpoint = copy.archiveId && copy.copyId
|
|
271
|
+
? `/v1/archives/${encodeURIComponent(copy.archiveId)}/copies/${encodeURIComponent(copy.copyId)}`
|
|
272
|
+
: `/v1/cloud/copies/${encodeURIComponent(copy.ref)}`;
|
|
273
|
+
await api(endpoint, {
|
|
137
274
|
method: "DELETE",
|
|
138
275
|
body: JSON.stringify({ confirmLastCopy: button.dataset.last === "yes" })
|
|
139
276
|
});
|
|
@@ -161,29 +298,52 @@
|
|
|
161
298
|
$("copies-line").textContent = token
|
|
162
299
|
? `Could not read your stored copies: ${error.message}`
|
|
163
300
|
: "Connect the dashboard to see what is stored off this machine.";
|
|
301
|
+
scheduleCopiesRefresh();
|
|
164
302
|
return;
|
|
165
303
|
}
|
|
166
|
-
|
|
304
|
+
// Old servers exposed in-flight rows here. Be defensive during upgrades:
|
|
305
|
+
// only provider-verified durable rows can count as stored copies or bytes.
|
|
306
|
+
data = {
|
|
307
|
+
...data,
|
|
308
|
+
copies: Array.isArray(data.copies) ? data.copies.filter((copy) => copy?.state === "durable") : [],
|
|
309
|
+
};
|
|
310
|
+
data = mergeWithLastComplete(data);
|
|
311
|
+
const { copies, mode, catalog = { complete: true, status: "complete", updating: false, stale: false, error: null } } = data;
|
|
167
312
|
if (mode === "none") {
|
|
313
|
+
if (copiesRefreshTimer) { clearTimeout(copiesRefreshTimer); copiesRefreshTimer = null; }
|
|
168
314
|
$("copies-line").textContent = "No storage is connected to this vault, so nothing is stored off this machine. Everything you have archived lives here only.";
|
|
169
|
-
|
|
315
|
+
storedCopies = [];
|
|
316
|
+
renderCopiesPage({ reset: true });
|
|
170
317
|
return;
|
|
171
318
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
? "
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
319
|
+
if (!catalog.complete) {
|
|
320
|
+
const visible = copies.length ? `${copiesSummary(copies)} visible` : "No verified copy total is available yet";
|
|
321
|
+
const kept = catalog.stale ? "Showing the last complete view." : "This is not the complete storage total.";
|
|
322
|
+
if (catalog.status === "error") {
|
|
323
|
+
$("copies-line").textContent = `${visible}. ${kept} Could not refresh Sealkeep Cloud${catalog.error ? `: ${catalog.error}.` : "."} Retrying automatically.`;
|
|
324
|
+
} else if (catalog.status === "incomplete") {
|
|
325
|
+
$("copies-line").textContent = `${visible}. ${kept} Sealkeep Cloud returned only part of the catalog; retrying automatically.`;
|
|
326
|
+
} else {
|
|
327
|
+
$("copies-line").textContent = `${visible}. ${kept} Updating Sealkeep Cloud copies…`;
|
|
328
|
+
}
|
|
329
|
+
scheduleCopiesRefresh();
|
|
330
|
+
} else {
|
|
331
|
+
if (copiesRefreshTimer) { clearTimeout(copiesRefreshTimer); copiesRefreshTimer = null; }
|
|
332
|
+
$("copies-line").textContent = copies.length === 0
|
|
333
|
+
? "Nothing is stored off this machine yet."
|
|
334
|
+
: `${copies.length} ${copies.length === 1 ? "copy" : "copies"} · ${bytes(copies.reduce((sum, copy) => sum + copy.bytes, 0))} in your storage.`;
|
|
335
|
+
}
|
|
336
|
+
storedCopies = copies;
|
|
337
|
+
renderCopiesPage();
|
|
180
338
|
if ($("cap") !== document.activeElement) $("cap").value = data.rollingCapGb == null ? "" : data.rollingCapGb;
|
|
181
339
|
}
|
|
182
340
|
|
|
183
341
|
function loadRules() {
|
|
342
|
+
$('save').disabled = !rulesLoaded;
|
|
343
|
+
if (!rulesLoaded) $('effect').textContent = 'Loading your saved cleanup rules. Nothing can be saved until they arrive.';
|
|
184
344
|
// Show what is actually configured rather than the markup's defaults, so
|
|
185
345
|
// this view never quietly disagrees with the vault it is editing.
|
|
186
|
-
api("/v1/retention").then(fill).catch((error) => {
|
|
346
|
+
api("/v1/retention").then((data) => { fill(data); rulesLoaded = true; $('save').disabled = false; }).catch((error) => {
|
|
187
347
|
$("effect").textContent = token
|
|
188
348
|
? `Could not read the current rules: ${error.message}`
|
|
189
349
|
: "Connect the dashboard so it can read this vault's current rules.";
|
|
@@ -191,7 +351,14 @@
|
|
|
191
351
|
}
|
|
192
352
|
|
|
193
353
|
function wire() {
|
|
354
|
+
$("copies-filter").addEventListener("input", () => renderCopiesPage({ reset: true }));
|
|
355
|
+
$("copies-more").addEventListener("click", () => {
|
|
356
|
+
copiesVisibleLimit += COPY_PAGE_SIZE;
|
|
357
|
+
renderCopiesPage();
|
|
358
|
+
});
|
|
359
|
+
|
|
194
360
|
$("save").onclick = async () => {
|
|
361
|
+
if (!rulesLoaded) { $('saved').textContent = 'Saved rules have not loaded. Refresh before making changes.'; return; }
|
|
195
362
|
$("save").disabled = true;
|
|
196
363
|
$("saved").textContent = "Saving…";
|
|
197
364
|
try {
|
|
@@ -210,7 +377,12 @@
|
|
|
210
377
|
})
|
|
211
378
|
});
|
|
212
379
|
showEffect(evaluation);
|
|
213
|
-
|
|
380
|
+
const canReclaim = evaluation.policy === "archive-and-reclaim" || evaluation.policy === "manual-approval";
|
|
381
|
+
$("saved").textContent = !canReclaim
|
|
382
|
+
? "Saved. Automatic reclaim is off; original sessions stay on this machine."
|
|
383
|
+
: evaluation.automaticReclaimEnabled
|
|
384
|
+
? "Saved. This machine uses these rules from its next background pass."
|
|
385
|
+
: "Saved. These rules describe what is eligible, but automatic reclaim remains off on this machine. Turn it on in Settings when you are ready.";
|
|
214
386
|
} catch (error) {
|
|
215
387
|
$("saved").textContent = `Not saved: ${error.message}`;
|
|
216
388
|
} finally {
|
|
@@ -223,7 +395,7 @@
|
|
|
223
395
|
|
|
224
396
|
// The rolling cap is a Pro setting: the account is what enforces it, so a
|
|
225
397
|
// vault without that account has nowhere real to write it.
|
|
226
|
-
|
|
398
|
+
$("rolling").hidden = true; // Account-wide storage caps are managed online.
|
|
227
399
|
|
|
228
400
|
$("cap-save").onclick = async () => {
|
|
229
401
|
const raw = $("cap").value.trim();
|