sealkeep 0.11.5 → 0.11.6
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/CHANGELOG.md +7 -0
- package/package.json +1 -1
- package/web/app.js +24 -6
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,13 @@
|
|
|
3
3
|
Notable changes, by published version. Sealkeep is pre-1.0: minor versions
|
|
4
4
|
may change behavior, and say so here when they do.
|
|
5
5
|
|
|
6
|
+
## 0.11.6 — 2026-09-25 — reconnect and Recovery Kit limits are separate
|
|
7
|
+
|
|
8
|
+
- After a machine reconnects successfully, the Recovery Kit page says so even
|
|
9
|
+
if a later cloud check is paused by a service limit. It shows when that
|
|
10
|
+
check can be retried and no longer asks for another reconnect that cannot
|
|
11
|
+
lift the limit. The local recovery phrase and existing vault are unchanged.
|
|
12
|
+
|
|
6
13
|
## 0.11.5 — 2026-09-23 — compacting is safe, and a lost destination says so
|
|
7
14
|
|
|
8
15
|
- **An agent that has just been compacted is told where the work stands.**
|
package/package.json
CHANGED
package/web/app.js
CHANGED
|
@@ -435,7 +435,7 @@ async function api(path, options = {}, preparationAttempt = 0) {
|
|
|
435
435
|
error.status = response.status;
|
|
436
436
|
// Structured refusals (lastCopy, forever) ride to the caller so a button
|
|
437
437
|
// can change its own words instead of string-matching prose.
|
|
438
|
-
if (body?.error && typeof body.error === "object") Object.assign(error, { code: body.error.code, status: response.status, forever: body.error.forever, lastCopy: body.error.lastCopy });
|
|
438
|
+
if (body?.error && typeof body.error === "object") Object.assign(error, { code: body.error.code, status: response.status, forever: body.error.forever, lastCopy: body.error.lastCopy, retryAt: body.error.retryAt });
|
|
439
439
|
throw error;
|
|
440
440
|
}
|
|
441
441
|
if (path === "/v1/archives" && Array.isArray(body)) {
|
|
@@ -3683,7 +3683,14 @@ async function renderAccountCard(status, options) {
|
|
|
3683
3683
|
}
|
|
3684
3684
|
}
|
|
3685
3685
|
|
|
3686
|
-
let recoveryAccessState = { vaultExists: false, phraseHeld: null, kit: null };
|
|
3686
|
+
let recoveryAccessState = { vaultExists: false, phraseHeld: null, kit: null, kitCheckPausedUntil: null, accountReconnected: false };
|
|
3687
|
+
|
|
3688
|
+
function recoveryKitPauseMessage() {
|
|
3689
|
+
const retryAt = recoveryAccessState.kitCheckPausedUntil;
|
|
3690
|
+
const when = retryAt && Number.isFinite(Date.parse(retryAt))
|
|
3691
|
+
? ` until ${new Date(retryAt).toLocaleString()}` : " until the service limit resets";
|
|
3692
|
+
return `${recoveryAccessState.accountReconnected ? 'This computer reconnected successfully. ' : ''}Cloud Recovery Kit check is paused${when}. Reconnecting again will not lift the service limit.`;
|
|
3693
|
+
}
|
|
3687
3694
|
|
|
3688
3695
|
function paintRecoveryAccess() {
|
|
3689
3696
|
const panel = $("recovery-access-panel");
|
|
@@ -3697,7 +3704,9 @@ function paintRecoveryAccess() {
|
|
|
3697
3704
|
? "Keychain: this machine does not hold its recovery phrase."
|
|
3698
3705
|
: "Keychain: recovery phrase status is not available yet.";
|
|
3699
3706
|
const kit = recoveryAccessState.kit;
|
|
3700
|
-
$("recovery-access-cloud").textContent =
|
|
3707
|
+
$("recovery-access-cloud").textContent = recoveryAccessState.kitCheckPausedUntil
|
|
3708
|
+
? recoveryKitPauseMessage()
|
|
3709
|
+
: kit?.eligible === true
|
|
3701
3710
|
? kit.available
|
|
3702
3711
|
? `Cloud Recovery Kit: ${kit.remaining} unused backup code${kit.remaining === 1 ? " remains" : "s remain"}.`
|
|
3703
3712
|
: "Cloud Recovery Kit: no unused backup codes are available."
|
|
@@ -3764,6 +3773,7 @@ async function pollAccountReconnect() {
|
|
|
3764
3773
|
return;
|
|
3765
3774
|
}
|
|
3766
3775
|
accountReconnect = null; button.hidden = true; button.href = '#recovery-kit'; button.textContent = 'Reconnect account';
|
|
3776
|
+
recoveryAccessState = { ...recoveryAccessState, accountReconnected: true };
|
|
3767
3777
|
message.textContent = 'This machine is reconnected. Checking your existing Recovery Kit…';
|
|
3768
3778
|
overviewDetailCheckedAt = 0;
|
|
3769
3779
|
try {
|
|
@@ -3812,6 +3822,7 @@ $('recovery-kit-account').onclick = async (event) => {
|
|
|
3812
3822
|
try {
|
|
3813
3823
|
const renewed = await api('/v1/account/reconnect', {method:'POST',body:'{}'});
|
|
3814
3824
|
if (renewed.reconnected) {
|
|
3825
|
+
recoveryAccessState = { ...recoveryAccessState, accountReconnected: true };
|
|
3815
3826
|
try { popup?.close(); } catch {}
|
|
3816
3827
|
message.textContent = 'This computer is reconnected using its existing device seat. Checking recovery access…';
|
|
3817
3828
|
overviewDetailCheckedAt = 0;
|
|
@@ -3850,7 +3861,7 @@ async function renderRecoveryKit() {
|
|
|
3850
3861
|
try {
|
|
3851
3862
|
const kit = await api("/v1/recovery-kit");
|
|
3852
3863
|
if (accountReconnect) return;
|
|
3853
|
-
recoveryAccessState = { ...recoveryAccessState, kit };
|
|
3864
|
+
recoveryAccessState = { ...recoveryAccessState, kit, kitCheckPausedUntil: null };
|
|
3854
3865
|
paintRecoveryAccess();
|
|
3855
3866
|
account.hidden = true;
|
|
3856
3867
|
if (kit.eligible == null) {
|
|
@@ -3873,9 +3884,14 @@ async function renderRecoveryKit() {
|
|
|
3873
3884
|
button.textContent = kit.available ? "Replace backup codes" : "Create Recovery Kit";
|
|
3874
3885
|
} catch (error) {
|
|
3875
3886
|
if (accountReconnect) return;
|
|
3876
|
-
recoveryAccessState = { ...recoveryAccessState, kit: null };
|
|
3887
|
+
recoveryAccessState = { ...recoveryAccessState, kit: null, kitCheckPausedUntil: error.code === 'rate_limited' ? (error.retryAt || 'pending') : null };
|
|
3877
3888
|
paintRecoveryAccess();
|
|
3878
3889
|
button.hidden = true;
|
|
3890
|
+
if (error.code === 'rate_limited') {
|
|
3891
|
+
account.hidden = true;
|
|
3892
|
+
state.textContent = recoveryKitPauseMessage();
|
|
3893
|
+
return;
|
|
3894
|
+
}
|
|
3879
3895
|
const needsAccount = /JWT|credential|sign|account|unauthorized/i.test(error.message);
|
|
3880
3896
|
account.hidden = !needsAccount;
|
|
3881
3897
|
state.textContent = needsAccount
|
|
@@ -3923,7 +3939,9 @@ function recoveryAccessSummary() {
|
|
|
3923
3939
|
if (kit?.eligible === true) {
|
|
3924
3940
|
return { message: "This machine can open your memories, but no usable Cloud Recovery Kit exists. Create one below for lost-machine recovery.", tone: "warn" };
|
|
3925
3941
|
}
|
|
3926
|
-
return { message:
|
|
3942
|
+
return { message: recoveryAccessState.kitCheckPausedUntil
|
|
3943
|
+
? `This machine can open your memories, but off-machine recovery cannot be confirmed right now. ${recoveryKitPauseMessage()}`
|
|
3944
|
+
: "This machine can open your memories, but Sealkeep could not confirm off-machine recovery. Reconnect the account and check again.", tone: "warn" };
|
|
3927
3945
|
}
|
|
3928
3946
|
|
|
3929
3947
|
async function recoverMachine() {
|