openmeld 0.3.134 → 0.3.136
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/{add-me-membership-BMp3t67Y.js → add-me-membership-BNNtDZYW.js} +2 -2
- package/dist/{add-me-membership-BMp3t67Y.js.map → add-me-membership-BNNtDZYW.js.map} +1 -1
- package/dist/{api-client-foundation-DNnxEYcg.js → api-client-foundation-CabWpjFe.js} +3 -3
- package/dist/{api-client-foundation-DNnxEYcg.js.map → api-client-foundation-CabWpjFe.js.map} +1 -1
- package/dist/{auth-session-BbrUVaZd.js → auth-session-Bj8n8YwV.js} +223 -84
- package/dist/auth-session-Bj8n8YwV.js.map +1 -0
- package/dist/{command-CTQlQSYV.js → command-BGqFPMwd.js} +789 -227
- package/dist/{command-CTQlQSYV.js.map → command-BGqFPMwd.js.map} +1 -1
- package/dist/{dist-Fj_YSHkO.js → dist-0qM_CVk2.js} +86 -4
- package/dist/dist-0qM_CVk2.js.map +1 -0
- package/dist/openmeld.js +6 -6
- package/dist/{runtime-transport-D4_-d5Yb.js → runtime-transport-BiQfJGnw.js} +201 -105
- package/dist/runtime-transport-BiQfJGnw.js.map +1 -0
- package/package.json +2 -2
- package/dist/auth-session-BbrUVaZd.js.map +0 -1
- package/dist/dist-Fj_YSHkO.js.map +0 -1
- package/dist/runtime-transport-D4_-d5Yb.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { $ as runWithHeldLock, K as isOpenMeldLockError, Lt as openMeldRootDir, W as acquireLock, _t as CURRENT_PROCESS_START_IDENTITY, vt as isProcessIdentityAlive, xn as __exportAll } from "./base-url-BtqW4aLS.js";
|
|
4
|
-
import { t as CURRENT_DAEMON_EXECUTION_CONTRACT_EPOCH } from "./runtime-transport-
|
|
4
|
+
import { t as CURRENT_DAEMON_EXECUTION_CONTRACT_EPOCH } from "./runtime-transport-BiQfJGnw.js";
|
|
5
5
|
import { createRequire } from "node:module";
|
|
6
6
|
import * as z$1 from "zod/v4";
|
|
7
7
|
import { join, resolve } from "node:path";
|
|
@@ -110,25 +110,40 @@ function resolveAuthSessionExpiryEligibility(expiresAt, nowMs = Date.now()) {
|
|
|
110
110
|
//#endregion
|
|
111
111
|
//#region src/config/auth-session-transition-lock.ts
|
|
112
112
|
const AUTH_SESSION_LOCK_KEY = "session-transition-mutation";
|
|
113
|
+
const AUTH_SESSION_CANDIDATE_REFRESH_LOCK_KEY = "candidate-session-refresh";
|
|
113
114
|
const AUTH_SESSION_LOCK_PROFILE = "auth-session";
|
|
114
115
|
const AUTH_SESSION_LOCK_RETRY_DELAY_MS = 20;
|
|
115
116
|
const AUTH_SESSION_LOCK_WAIT_MS = 1e4;
|
|
116
117
|
async function withAuthSessionTransitionMutationLock(operation) {
|
|
118
|
+
return await withAuthSessionLock({
|
|
119
|
+
component: "auth_session_transition",
|
|
120
|
+
key: AUTH_SESSION_LOCK_KEY,
|
|
121
|
+
operation
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
async function withAuthSessionCandidateRefreshLock(operation) {
|
|
125
|
+
return await withAuthSessionLock({
|
|
126
|
+
component: "auth_session_candidate_refresh",
|
|
127
|
+
key: AUTH_SESSION_CANDIDATE_REFRESH_LOCK_KEY,
|
|
128
|
+
operation
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
async function withAuthSessionLock(input) {
|
|
117
132
|
return await runWithHeldLock({
|
|
118
|
-
lock: await
|
|
119
|
-
run: operation,
|
|
133
|
+
lock: await acquireAuthSessionLock(input),
|
|
134
|
+
run: input.operation,
|
|
120
135
|
releaseContext: {
|
|
121
|
-
component:
|
|
122
|
-
lockKey:
|
|
136
|
+
component: input.component,
|
|
137
|
+
lockKey: input.key
|
|
123
138
|
}
|
|
124
139
|
});
|
|
125
140
|
}
|
|
126
|
-
async function
|
|
141
|
+
async function acquireAuthSessionLock(input) {
|
|
127
142
|
const deadline = Date.now() + AUTH_SESSION_LOCK_WAIT_MS;
|
|
128
143
|
while (true) try {
|
|
129
144
|
return await acquireLock({
|
|
130
|
-
key:
|
|
131
|
-
meta: { component:
|
|
145
|
+
key: input.key,
|
|
146
|
+
meta: { component: input.component },
|
|
132
147
|
openMeldProfileId: AUTH_SESSION_LOCK_PROFILE
|
|
133
148
|
});
|
|
134
149
|
} catch (error) {
|
|
@@ -731,6 +746,8 @@ var auth_session_exports = /* @__PURE__ */ __exportAll({
|
|
|
731
746
|
setAuthSession: () => setAuthSession,
|
|
732
747
|
updateAuthSessionActiveOrganization: () => updateAuthSessionActiveOrganization
|
|
733
748
|
});
|
|
749
|
+
const MAX_CANDIDATE_SERVICE_PROCESSES = 16;
|
|
750
|
+
const MAX_CANDIDATE_TOKEN_REFS = 64;
|
|
734
751
|
var AuthSessionStorageError = class extends Error {
|
|
735
752
|
reason;
|
|
736
753
|
constructor(input) {
|
|
@@ -772,6 +789,9 @@ const TRAILING_SLASHES_RE = /\/+$/u;
|
|
|
772
789
|
let cachedAuthSession = null;
|
|
773
790
|
const activeAuthSessionTransitionIds = /* @__PURE__ */ new Set();
|
|
774
791
|
async function getAuthSessionState(options = {}) {
|
|
792
|
+
return await getAuthSessionStateWithCandidateRefreshFence(options, false);
|
|
793
|
+
}
|
|
794
|
+
async function getAuthSessionStateWithCandidateRefreshFence(options, candidateRefreshFenceHeld) {
|
|
775
795
|
const localState = await getLocalAuthSessionState();
|
|
776
796
|
const localPayloadKey = cachedAuthSession?.payloadKey ?? null;
|
|
777
797
|
const shouldValidateRemote = options.validateRemote === true || options.refreshExpired === true && localState.state === "expired";
|
|
@@ -785,6 +805,7 @@ async function getAuthSessionState(options = {}) {
|
|
|
785
805
|
default: return localState;
|
|
786
806
|
}
|
|
787
807
|
if (!shouldValidateRemote) return localState;
|
|
808
|
+
if (!candidateRefreshFenceHeld && await shouldFencePendingCandidateRemoteValidation()) return await withAuthSessionCandidateRefreshLock(async () => await getAuthSessionStateWithCandidateRefreshFence(options, true));
|
|
788
809
|
const remoteValidation = await getRemoteAuthSessionState(localState.session);
|
|
789
810
|
if (remoteValidation.state === "valid") return await resolveRemotelyValidatedAuthSession({
|
|
790
811
|
localPayloadKey,
|
|
@@ -804,6 +825,10 @@ async function getAuthSessionState(options = {}) {
|
|
|
804
825
|
session: null
|
|
805
826
|
};
|
|
806
827
|
}
|
|
828
|
+
async function shouldFencePendingCandidateRemoteValidation() {
|
|
829
|
+
const journal = await readAuthSessionTransitionJournal();
|
|
830
|
+
return Boolean(journal?.state === "pending" && journal.candidateCredentialRefreshVersion === 1 && isAuthSessionTransitionCandidateVisibleToCurrentProcess(journal));
|
|
831
|
+
}
|
|
807
832
|
async function resolveRemotelyValidatedAuthSession(input) {
|
|
808
833
|
if (!input.remoteValidation.expiresAt || isExpired(input.remoteValidation.expiresAt)) return input.localState;
|
|
809
834
|
let refreshedSession;
|
|
@@ -816,7 +841,14 @@ async function resolveRemotelyValidatedAuthSession(input) {
|
|
|
816
841
|
});
|
|
817
842
|
} catch (error) {
|
|
818
843
|
if (!(error instanceof AuthSessionRecoveryInProgressError)) throw error;
|
|
819
|
-
return
|
|
844
|
+
return {
|
|
845
|
+
state: "valid",
|
|
846
|
+
session: {
|
|
847
|
+
...input.localState.session,
|
|
848
|
+
accessToken: input.remoteValidation.accessToken ?? input.localState.session.accessToken,
|
|
849
|
+
expiresAt: input.remoteValidation.expiresAt
|
|
850
|
+
}
|
|
851
|
+
};
|
|
820
852
|
}
|
|
821
853
|
if (!refreshedSession) return await getLocalAuthSessionState();
|
|
822
854
|
return {
|
|
@@ -922,6 +954,8 @@ async function beginAuthSessionTransition(input, context = {}) {
|
|
|
922
954
|
ownerProcessStartedAt: CURRENT_PROCESS_START_IDENTITY,
|
|
923
955
|
previousPayload: previousPayload?.v === 1 ? previousPayload : null,
|
|
924
956
|
candidatePayload: nextPayload,
|
|
957
|
+
candidateCredentialRefreshVersion: 1,
|
|
958
|
+
candidateTokenRefs: [tokenRef],
|
|
925
959
|
...context.computerHandoff ? { computerHandoff: { gatewayUrl: context.computerHandoff.gatewayUrl } } : {}
|
|
926
960
|
};
|
|
927
961
|
let journalClaimed = false;
|
|
@@ -957,14 +991,14 @@ async function beginAuthSessionTransition(input, context = {}) {
|
|
|
957
991
|
if (!journal.previousPayload) return currentPayload === null;
|
|
958
992
|
return currentPayload?.v === 1 && buildAuthSessionPayloadKey(currentPayload) === buildAuthSessionPayloadKey(journal.previousPayload);
|
|
959
993
|
};
|
|
960
|
-
const isCandidateSessionCurrent = (currentPayload) => currentPayload?.v === 1 && buildAuthSessionPayloadKey(currentPayload) === buildAuthSessionPayloadKey(
|
|
994
|
+
const isCandidateSessionCurrent = (currentPayload, candidatePayload = nextPayload) => currentPayload?.v === 1 && buildAuthSessionPayloadKey(currentPayload) === buildAuthSessionPayloadKey(candidatePayload);
|
|
961
995
|
const assertPreviousSessionCurrent = async () => {
|
|
962
996
|
const currentPayload = await readSessionPayloadFile();
|
|
963
997
|
if (!isPreviousSessionCurrent(currentPayload)) throw new Error("auth session changed before the setup transition completed");
|
|
964
998
|
};
|
|
965
|
-
const assertTransitionSessionCurrent = async () => {
|
|
999
|
+
const assertTransitionSessionCurrent = async (candidatePayload = nextPayload) => {
|
|
966
1000
|
const currentPayload = await readSessionPayloadFile();
|
|
967
|
-
if (!(isPreviousSessionCurrent(currentPayload) || isCandidateSessionCurrent(currentPayload))) throw new Error("auth session changed before the setup transition completed");
|
|
1001
|
+
if (!(isPreviousSessionCurrent(currentPayload) || isCandidateSessionCurrent(currentPayload, candidatePayload))) throw new Error("auth session changed before the setup transition completed");
|
|
968
1002
|
};
|
|
969
1003
|
const restoreMissingJournalTransition = async () => {
|
|
970
1004
|
const currentPayload = await readSessionPayloadFile();
|
|
@@ -1016,14 +1050,13 @@ async function beginAuthSessionTransition(input, context = {}) {
|
|
|
1016
1050
|
reason: currentJournal.status
|
|
1017
1051
|
});
|
|
1018
1052
|
}
|
|
1053
|
+
const committedPayload = currentJournal.journal.candidatePayload;
|
|
1054
|
+
const committedSession = await readStoredAuthSessionFromPayload(committedPayload);
|
|
1019
1055
|
await assertPreviousSessionCurrent();
|
|
1020
|
-
await writeSessionPayloadAtomically(authSessionPath(),
|
|
1056
|
+
await writeSessionPayloadAtomically(authSessionPath(), committedPayload);
|
|
1021
1057
|
cacheAuthSession({
|
|
1022
|
-
payload:
|
|
1023
|
-
session:
|
|
1024
|
-
accessToken,
|
|
1025
|
-
payload: nextPayload
|
|
1026
|
-
})
|
|
1058
|
+
payload: committedPayload,
|
|
1059
|
+
session: committedSession
|
|
1027
1060
|
});
|
|
1028
1061
|
const committedJournal = {
|
|
1029
1062
|
...currentJournal.journal,
|
|
@@ -1054,7 +1087,7 @@ async function beginAuthSessionTransition(input, context = {}) {
|
|
|
1054
1087
|
reason: currentJournal.status
|
|
1055
1088
|
});
|
|
1056
1089
|
}
|
|
1057
|
-
await assertTransitionSessionCurrent();
|
|
1090
|
+
await assertTransitionSessionCurrent(currentJournal.journal.candidatePayload);
|
|
1058
1091
|
await restoreTransitionPreviousPayload(currentJournal.journal, previousSession);
|
|
1059
1092
|
settlement = "rolled_back";
|
|
1060
1093
|
activeAuthSessionTransitionIds.delete(journal.id);
|
|
@@ -1066,7 +1099,7 @@ async function beginAuthSessionTransition(input, context = {}) {
|
|
|
1066
1099
|
});
|
|
1067
1100
|
return;
|
|
1068
1101
|
}
|
|
1069
|
-
await
|
|
1102
|
+
await cleanupCandidateTokenRefs(currentJournal.journal);
|
|
1070
1103
|
await removeAuthSessionTransitionJournalIfOwned(currentJournal.journal);
|
|
1071
1104
|
})
|
|
1072
1105
|
};
|
|
@@ -1164,7 +1197,7 @@ async function completeInterruptedComputerAuthSessionRecovery(id) {
|
|
|
1164
1197
|
const journal = await readRequiredComputerAuthRecoveryJournal(id);
|
|
1165
1198
|
if (!(journal.computerHandoff.serviceReconciledAt && journal.computerHandoff.remoteSessionRevokedAt)) throw new Error("computer auth recovery cannot complete before service and remote-session cleanup");
|
|
1166
1199
|
try {
|
|
1167
|
-
await
|
|
1200
|
+
await cleanupCandidateTokenRefs(journal);
|
|
1168
1201
|
await removeAuthSessionTransitionJournalIfOwned(journal);
|
|
1169
1202
|
return true;
|
|
1170
1203
|
} catch (error) {
|
|
@@ -1436,7 +1469,7 @@ function buildStoredAuthSessionIdentity(session) {
|
|
|
1436
1469
|
async function cleanupTransitionTokensNotReferencedByCurrentSession(input) {
|
|
1437
1470
|
const currentTokenRef = normalizeTokenRef(input.currentPayload?.tokenRef);
|
|
1438
1471
|
const previousTokenRef = normalizeTokenRef(input.journal.previousPayload?.tokenRef);
|
|
1439
|
-
const transitionTokenRefs = [...previousTokenRef ? [previousTokenRef] : [], input.journal
|
|
1472
|
+
const transitionTokenRefs = [...previousTokenRef ? [previousTokenRef] : [], ...getCandidateTokenRefs(input.journal)];
|
|
1440
1473
|
for (const [index, tokenRef] of transitionTokenRefs.entries()) {
|
|
1441
1474
|
if (transitionTokenRefs.slice(0, index).some((previousRef) => areStoredTokenRefsEqual(previousRef, tokenRef)) || currentTokenRef && areStoredTokenRefsEqual(currentTokenRef, tokenRef)) continue;
|
|
1442
1475
|
await deleteAccessTokenOrRetainForCleanup(tokenRef);
|
|
@@ -1458,57 +1491,134 @@ function buildAuthSessionPayloadKey(payload) {
|
|
|
1458
1491
|
function areStoredTokenRefsEqual(left, right) {
|
|
1459
1492
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
1460
1493
|
}
|
|
1494
|
+
function getUniqueTokenRefs(tokenRefs) {
|
|
1495
|
+
const uniqueTokenRefs = [];
|
|
1496
|
+
for (const tokenRef of tokenRefs) if (!uniqueTokenRefs.some((existing) => areStoredTokenRefsEqual(existing, tokenRef))) uniqueTokenRefs.push(tokenRef);
|
|
1497
|
+
return uniqueTokenRefs;
|
|
1498
|
+
}
|
|
1499
|
+
function getCandidateTokenRefs(journal) {
|
|
1500
|
+
return getUniqueTokenRefs([...journal.candidateTokenRefs ?? [], journal.candidatePayload.tokenRef]);
|
|
1501
|
+
}
|
|
1502
|
+
async function cleanupCandidateTokenRefs(journal, keepTokenRef) {
|
|
1503
|
+
for (const tokenRef of getCandidateTokenRefs(journal)) {
|
|
1504
|
+
if (keepTokenRef && areStoredTokenRefsEqual(keepTokenRef, tokenRef)) continue;
|
|
1505
|
+
await deleteAccessTokenOrRetainForCleanup(tokenRef);
|
|
1506
|
+
}
|
|
1507
|
+
}
|
|
1461
1508
|
async function persistRemoteSessionRefresh(input) {
|
|
1462
1509
|
return await withAuthSessionTransitionMutationLock(async () => {
|
|
1463
|
-
await
|
|
1464
|
-
const
|
|
1465
|
-
if (
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
const clientId = normalizeOptionalString(payload.clientId);
|
|
1470
|
-
const createdAt = normalizeOptionalString(payload.createdAt);
|
|
1471
|
-
if (!(tokenRef && tokenType && authBaseUrl && clientId && createdAt)) return null;
|
|
1472
|
-
const nextAccessToken = normalizeOptionalString(input.accessToken);
|
|
1473
|
-
if (!nextAccessToken) return null;
|
|
1474
|
-
const nextTokenRef = nextAccessToken === input.previousAccessToken ? tokenRef : await storeAccessToken({
|
|
1475
|
-
accessToken: nextAccessToken,
|
|
1476
|
-
authBaseUrl,
|
|
1477
|
-
clientId
|
|
1478
|
-
});
|
|
1479
|
-
const nextPayload = {
|
|
1480
|
-
v: 1,
|
|
1481
|
-
tokenRef: nextTokenRef,
|
|
1482
|
-
tokenType,
|
|
1483
|
-
authBaseUrl,
|
|
1484
|
-
clientId,
|
|
1485
|
-
createdAt,
|
|
1486
|
-
expiresAt: input.expiresAt,
|
|
1487
|
-
...typeof payload.scope === "string" && payload.scope.trim() ? { scope: payload.scope.trim() } : {},
|
|
1488
|
-
...buildAuthUserPayload(payload.user),
|
|
1489
|
-
...buildActiveOrganizationPayload(payload.activeOrganization)
|
|
1490
|
-
};
|
|
1491
|
-
const nextSession = buildStoredAuthSession({
|
|
1492
|
-
accessToken: nextAccessToken,
|
|
1493
|
-
payload: nextPayload
|
|
1494
|
-
});
|
|
1495
|
-
try {
|
|
1496
|
-
await assertStoredAccessTokenReloads({
|
|
1497
|
-
accessToken: nextAccessToken,
|
|
1498
|
-
tokenRef: nextTokenRef
|
|
1510
|
+
await recoverInterruptedAuthSessionTransitionWithOwnership();
|
|
1511
|
+
const transitionJournal = await readAuthSessionTransitionJournal();
|
|
1512
|
+
if (transitionJournal) {
|
|
1513
|
+
if (transitionJournal.state === "pending" && transitionJournal.candidateCredentialRefreshVersion === 1 && isAuthSessionTransitionCandidateVisibleToCurrentProcess(transitionJournal) && input.expectedPayloadKey !== null && buildAuthSessionPayloadKey(transitionJournal.candidatePayload) === input.expectedPayloadKey) return await persistPendingCandidateRemoteSessionRefresh({
|
|
1514
|
+
...input,
|
|
1515
|
+
journal: transitionJournal
|
|
1499
1516
|
});
|
|
1500
|
-
|
|
1501
|
-
} catch (error) {
|
|
1502
|
-
if (!areStoredTokenRefsEqual(tokenRef, nextTokenRef)) await deleteAccessTokenOrRetainForCleanup(nextTokenRef);
|
|
1503
|
-
throw error;
|
|
1517
|
+
throw new AuthSessionRecoveryInProgressError();
|
|
1504
1518
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1519
|
+
return await persistCommittedRemoteSessionRefresh(input);
|
|
1520
|
+
});
|
|
1521
|
+
}
|
|
1522
|
+
async function persistCommittedRemoteSessionRefresh(input) {
|
|
1523
|
+
const payload = await readSessionPayloadFile();
|
|
1524
|
+
if (payload?.v !== 1 || input.expectedPayloadKey === null || buildAuthSessionPayloadKey(payload) !== input.expectedPayloadKey) return null;
|
|
1525
|
+
const refresh = await prepareRemoteSessionRefresh({
|
|
1526
|
+
...input,
|
|
1527
|
+
payload,
|
|
1528
|
+
uniqueTokenRef: false
|
|
1529
|
+
});
|
|
1530
|
+
if (!refresh) return null;
|
|
1531
|
+
try {
|
|
1532
|
+
await assertStoredAccessTokenReloads({
|
|
1533
|
+
accessToken: refresh.nextSession.accessToken,
|
|
1534
|
+
tokenRef: refresh.nextTokenRef
|
|
1508
1535
|
});
|
|
1509
|
-
|
|
1510
|
-
|
|
1536
|
+
await writeSessionPayloadAtomically(authSessionPath(), refresh.nextPayload);
|
|
1537
|
+
} catch (error) {
|
|
1538
|
+
await cleanupFailedRemoteSessionRefresh(refresh);
|
|
1539
|
+
throw error;
|
|
1540
|
+
}
|
|
1541
|
+
cacheAuthSession({
|
|
1542
|
+
payload: refresh.nextPayload,
|
|
1543
|
+
session: refresh.nextSession
|
|
1511
1544
|
});
|
|
1545
|
+
if (!areStoredTokenRefsEqual(refresh.currentTokenRef, refresh.nextTokenRef)) await deleteAccessTokenOrRetainForCleanup(refresh.currentTokenRef);
|
|
1546
|
+
return refresh.nextSession;
|
|
1547
|
+
}
|
|
1548
|
+
async function persistPendingCandidateRemoteSessionRefresh(input) {
|
|
1549
|
+
const refresh = await prepareRemoteSessionRefresh({
|
|
1550
|
+
...input,
|
|
1551
|
+
payload: input.journal.candidatePayload,
|
|
1552
|
+
uniqueTokenRef: true
|
|
1553
|
+
});
|
|
1554
|
+
if (!refresh) return null;
|
|
1555
|
+
const candidateTokenRefs = getUniqueTokenRefs([...getCandidateTokenRefs(input.journal), refresh.nextTokenRef]);
|
|
1556
|
+
if (candidateTokenRefs.length > MAX_CANDIDATE_TOKEN_REFS) {
|
|
1557
|
+
await cleanupFailedRemoteSessionRefresh(refresh);
|
|
1558
|
+
throw new AuthSessionStorageError({
|
|
1559
|
+
message: `auth session transition has more than ${String(MAX_CANDIDATE_TOKEN_REFS)} candidate token rotations`,
|
|
1560
|
+
reason: "unreadable"
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1563
|
+
const nextJournal = {
|
|
1564
|
+
...input.journal,
|
|
1565
|
+
candidatePayload: refresh.nextPayload,
|
|
1566
|
+
candidateTokenRefs
|
|
1567
|
+
};
|
|
1568
|
+
try {
|
|
1569
|
+
await assertStoredAccessTokenReloads({
|
|
1570
|
+
accessToken: refresh.nextSession.accessToken,
|
|
1571
|
+
tokenRef: refresh.nextTokenRef
|
|
1572
|
+
});
|
|
1573
|
+
await writeAuthSessionTransitionJournalIfOwned(input.journal, nextJournal);
|
|
1574
|
+
} catch (error) {
|
|
1575
|
+
await cleanupFailedRemoteSessionRefresh(refresh);
|
|
1576
|
+
throw error;
|
|
1577
|
+
}
|
|
1578
|
+
cacheAuthSession({
|
|
1579
|
+
payload: refresh.nextPayload,
|
|
1580
|
+
session: refresh.nextSession
|
|
1581
|
+
});
|
|
1582
|
+
return refresh.nextSession;
|
|
1583
|
+
}
|
|
1584
|
+
async function prepareRemoteSessionRefresh(input) {
|
|
1585
|
+
const currentTokenRef = normalizeTokenRef(input.payload.tokenRef);
|
|
1586
|
+
const tokenType = normalizeOptionalString(input.payload.tokenType);
|
|
1587
|
+
const authBaseUrl = normalizeOptionalString(input.payload.authBaseUrl);
|
|
1588
|
+
const clientId = normalizeOptionalString(input.payload.clientId);
|
|
1589
|
+
const createdAt = normalizeOptionalString(input.payload.createdAt);
|
|
1590
|
+
const nextAccessToken = normalizeOptionalString(input.accessToken);
|
|
1591
|
+
if (!(currentTokenRef && tokenType && authBaseUrl && clientId && createdAt && nextAccessToken)) return null;
|
|
1592
|
+
const nextTokenRef = nextAccessToken === input.previousAccessToken ? currentTokenRef : await storeAccessToken({
|
|
1593
|
+
accessToken: nextAccessToken,
|
|
1594
|
+
authBaseUrl,
|
|
1595
|
+
clientId,
|
|
1596
|
+
unique: input.uniqueTokenRef
|
|
1597
|
+
});
|
|
1598
|
+
const nextPayload = {
|
|
1599
|
+
v: 1,
|
|
1600
|
+
tokenRef: nextTokenRef,
|
|
1601
|
+
tokenType,
|
|
1602
|
+
authBaseUrl,
|
|
1603
|
+
clientId,
|
|
1604
|
+
createdAt,
|
|
1605
|
+
expiresAt: input.expiresAt,
|
|
1606
|
+
...typeof input.payload.scope === "string" && input.payload.scope.trim() ? { scope: input.payload.scope.trim() } : {},
|
|
1607
|
+
...buildAuthUserPayload(input.payload.user),
|
|
1608
|
+
...buildActiveOrganizationPayload(input.payload.activeOrganization)
|
|
1609
|
+
};
|
|
1610
|
+
return {
|
|
1611
|
+
currentTokenRef,
|
|
1612
|
+
nextPayload,
|
|
1613
|
+
nextSession: buildStoredAuthSession({
|
|
1614
|
+
accessToken: nextAccessToken,
|
|
1615
|
+
payload: nextPayload
|
|
1616
|
+
}),
|
|
1617
|
+
nextTokenRef
|
|
1618
|
+
};
|
|
1619
|
+
}
|
|
1620
|
+
async function cleanupFailedRemoteSessionRefresh(refresh) {
|
|
1621
|
+
if (!areStoredTokenRefsEqual(refresh.currentTokenRef, refresh.nextTokenRef)) await deleteAccessTokenOrRetainForCleanup(refresh.nextTokenRef);
|
|
1512
1622
|
}
|
|
1513
1623
|
function normalizeTokenRef(input) {
|
|
1514
1624
|
if (!isStoredTokenRef(input)) return null;
|
|
@@ -1587,12 +1697,17 @@ function resolveRemoteAuthSessionReasonCode(state) {
|
|
|
1587
1697
|
async function readSessionPayload() {
|
|
1588
1698
|
await recoverInterruptedAuthSessionTransition();
|
|
1589
1699
|
const journal = await readAuthSessionTransitionJournal();
|
|
1590
|
-
if (journal?.state === "pending"
|
|
1700
|
+
if (journal?.state === "pending") {
|
|
1701
|
+
if (isAuthSessionTransitionCandidateVisibleToCurrentProcess(journal)) return journal.candidatePayload;
|
|
1702
|
+
}
|
|
1591
1703
|
return await readSessionPayloadFile();
|
|
1592
1704
|
}
|
|
1593
1705
|
function isAuthSessionTransitionCandidateVisibleToCurrentProcess(journal) {
|
|
1594
1706
|
if (activeAuthSessionTransitionIds.has(journal.id)) return true;
|
|
1595
|
-
return journal.candidateServiceProcess
|
|
1707
|
+
return getRegisteredCandidateServiceProcesses(journal).some((candidateServiceProcess) => isSameProcessIdentity(candidateServiceProcess, {
|
|
1708
|
+
ownerPid: process.pid,
|
|
1709
|
+
ownerProcessStartedAt: CURRENT_PROCESS_START_IDENTITY
|
|
1710
|
+
}));
|
|
1596
1711
|
}
|
|
1597
1712
|
async function readSessionPayloadFile() {
|
|
1598
1713
|
const path = authSessionPath();
|
|
@@ -1639,7 +1754,7 @@ async function recoverInterruptedAuthSessionTransitionWithOwnership() {
|
|
|
1639
1754
|
});
|
|
1640
1755
|
return;
|
|
1641
1756
|
}
|
|
1642
|
-
await
|
|
1757
|
+
await cleanupCandidateTokenRefs(journal);
|
|
1643
1758
|
await removeAuthSessionTransitionJournalIfOwned(journal);
|
|
1644
1759
|
return;
|
|
1645
1760
|
}
|
|
@@ -1664,6 +1779,7 @@ async function recoverInterruptedAuthSessionTransitionWithOwnership() {
|
|
|
1664
1779
|
async function finalizeCommittedAuthSessionTransition(journal) {
|
|
1665
1780
|
const previousTokenRef = normalizeTokenRef(journal.previousPayload?.tokenRef);
|
|
1666
1781
|
if (previousTokenRef && !areStoredTokenRefsEqual(previousTokenRef, journal.candidatePayload.tokenRef)) await deleteAccessTokenOrRetainForCleanup(previousTokenRef);
|
|
1782
|
+
await cleanupCandidateTokenRefs(journal, journal.candidatePayload.tokenRef);
|
|
1667
1783
|
await removeAuthSessionTransitionJournalIfOwned(journal);
|
|
1668
1784
|
}
|
|
1669
1785
|
async function registerCurrentCandidateServiceProcess(journal) {
|
|
@@ -1672,29 +1788,43 @@ async function registerCurrentCandidateServiceProcess(journal) {
|
|
|
1672
1788
|
ownerPid: process.pid,
|
|
1673
1789
|
ownerProcessStartedAt: CURRENT_PROCESS_START_IDENTITY
|
|
1674
1790
|
};
|
|
1675
|
-
if (journal.candidateServiceProcess
|
|
1791
|
+
if (getRegisteredCandidateServiceProcesses(journal).some((candidateServiceProcess) => isSameProcessIdentity(candidateServiceProcess, currentProcess))) return journal;
|
|
1676
1792
|
const currentJournal = await readAuthSessionTransitionJournal();
|
|
1677
1793
|
if (currentJournal?.id !== journal.id || currentJournal.state !== "pending") return currentJournal ?? journal;
|
|
1794
|
+
const candidateServiceProcesses = getRegisteredCandidateServiceProcesses(currentJournal).filter(isProcessIdentityAlive);
|
|
1795
|
+
candidateServiceProcesses.push(currentProcess);
|
|
1796
|
+
if (candidateServiceProcesses.length > MAX_CANDIDATE_SERVICE_PROCESSES) throw new Error(`auth session transition has more than ${String(MAX_CANDIDATE_SERVICE_PROCESSES)} live candidate OpenMeld Service processes`);
|
|
1678
1797
|
const updatedJournal = {
|
|
1679
1798
|
...currentJournal,
|
|
1799
|
+
candidateServiceProcesses,
|
|
1680
1800
|
candidateServiceProcess: currentProcess
|
|
1681
1801
|
};
|
|
1682
1802
|
await writeAuthSessionTransitionJournalIfOwned(currentJournal, updatedJournal);
|
|
1683
1803
|
return updatedJournal;
|
|
1684
1804
|
}
|
|
1685
1805
|
function stopRegisteredCandidateService(journal) {
|
|
1686
|
-
const candidateServiceProcess
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1806
|
+
for (const candidateServiceProcess of getRegisteredCandidateServiceProcesses(journal)) {
|
|
1807
|
+
if (!isProcessIdentityAlive(candidateServiceProcess)) continue;
|
|
1808
|
+
try {
|
|
1809
|
+
process.kill(candidateServiceProcess.ownerPid, "SIGTERM");
|
|
1810
|
+
} catch (error) {
|
|
1811
|
+
if (!isFileNotFoundErrorCode(error, "ESRCH")) throw new AuthSessionStorageError({
|
|
1812
|
+
message: `candidate OpenMeld Service process ${String(candidateServiceProcess.ownerPid)} could not be stopped: ${toErrorMessage(error)}`,
|
|
1813
|
+
reason: "unreadable",
|
|
1814
|
+
cause: error
|
|
1815
|
+
});
|
|
1816
|
+
}
|
|
1696
1817
|
}
|
|
1697
1818
|
}
|
|
1819
|
+
function getRegisteredCandidateServiceProcesses(journal) {
|
|
1820
|
+
const candidates = [...journal.candidateServiceProcesses ?? [], ...journal.candidateServiceProcess ? [journal.candidateServiceProcess] : []];
|
|
1821
|
+
const uniqueCandidates = [];
|
|
1822
|
+
for (const candidate of candidates) if (!uniqueCandidates.some((existing) => isSameProcessIdentity(existing, candidate))) uniqueCandidates.push(candidate);
|
|
1823
|
+
return uniqueCandidates;
|
|
1824
|
+
}
|
|
1825
|
+
function isSameProcessIdentity(left, right) {
|
|
1826
|
+
return left.ownerPid === right.ownerPid && left.ownerProcessStartedAt === right.ownerProcessStartedAt;
|
|
1827
|
+
}
|
|
1698
1828
|
function isCurrentProcessCandidateService() {
|
|
1699
1829
|
const runtimeMode = String(process.env.OPENMELD_DAEMON_RUNTIME_MODE ?? "").trim();
|
|
1700
1830
|
return runtimeMode === "background" || runtimeMode === "foreground";
|
|
@@ -1771,10 +1901,19 @@ async function readAuthSessionTransitionJournal() {
|
|
|
1771
1901
|
}
|
|
1772
1902
|
function isAuthSessionTransitionJournal(value) {
|
|
1773
1903
|
if (!isRecord(value)) return false;
|
|
1774
|
-
return value.v === 1 && typeof value.id === "string" && value.id.length > 0 && (value.state === "pending" || value.state === "recovery_required" || value.state === "committed") && Number.isSafeInteger(value.ownerPid) && Number(value.ownerPid) > 0 && typeof value.ownerProcessStartedAt === "string" && value.ownerProcessStartedAt.length > 0 && (value.previousPayload === null || isAuthSessionPayload(value.previousPayload)) && isAuthSessionPayload(value.candidatePayload) && isOptionalProcessIdentity(value.candidateServiceProcess) && isOptionalComputerHandoffRecovery(value.computerHandoff);
|
|
1904
|
+
return value.v === 1 && typeof value.id === "string" && value.id.length > 0 && (value.state === "pending" || value.state === "recovery_required" || value.state === "committed") && Number.isSafeInteger(value.ownerPid) && Number(value.ownerPid) > 0 && typeof value.ownerProcessStartedAt === "string" && value.ownerProcessStartedAt.length > 0 && (value.previousPayload === null || isAuthSessionPayload(value.previousPayload)) && isAuthSessionPayload(value.candidatePayload) && (value.candidateCredentialRefreshVersion === void 0 || value.candidateCredentialRefreshVersion === 1) && isOptionalStoredTokenRefList(value.candidateTokenRefs) && isOptionalProcessIdentityList(value.candidateServiceProcesses) && isOptionalProcessIdentity(value.candidateServiceProcess) && isOptionalComputerHandoffRecovery(value.computerHandoff);
|
|
1905
|
+
}
|
|
1906
|
+
function isOptionalStoredTokenRefList(value) {
|
|
1907
|
+
return value === void 0 || Array.isArray(value) && value.length <= MAX_CANDIDATE_TOKEN_REFS && value.every((tokenRef) => isStoredTokenRef(tokenRef));
|
|
1908
|
+
}
|
|
1909
|
+
function isOptionalProcessIdentityList(value) {
|
|
1910
|
+
return value === void 0 || Array.isArray(value) && value.length <= MAX_CANDIDATE_SERVICE_PROCESSES && value.every((candidate) => isProcessIdentity(candidate));
|
|
1775
1911
|
}
|
|
1776
1912
|
function isOptionalProcessIdentity(value) {
|
|
1777
|
-
return value === void 0 ||
|
|
1913
|
+
return value === void 0 || isProcessIdentity(value);
|
|
1914
|
+
}
|
|
1915
|
+
function isProcessIdentity(value) {
|
|
1916
|
+
return isRecord(value) && Number.isSafeInteger(value.ownerPid) && Number(value.ownerPid) > 0 && typeof value.ownerProcessStartedAt === "string" && value.ownerProcessStartedAt.length > 0;
|
|
1778
1917
|
}
|
|
1779
1918
|
function isOptionalComputerHandoffRecovery(value) {
|
|
1780
1919
|
if (value === void 0) return true;
|
|
@@ -1958,4 +2097,4 @@ async function removeSessionFile() {
|
|
|
1958
2097
|
//#endregion
|
|
1959
2098
|
export { HEARTBEAT_RESPONSE_TIMEOUT_MS as A, RUNTIME_AGENT_CONTROLLER_REPORT_SYNC_INTERVAL_MS as B, DAEMON_RUNTIME_CONTRACT_EPOCH as C, DEFAULT_GATEWAY_CHAIN_TIMEOUT_MS as D, DAEMON_STREAM_KEEPALIVE_PONG_TIMEOUT_MS as E, OPENCLAW_CLI_TIMEOUT_MS as F, RUNTIME_STATE_PERSIST_MIN_INTERVAL_MS as H, PROVIDER_CONVERSATION_PROOF_SYNC_INTERVAL_MS as I, PROVIDER_CONVERSATION_PROOF_SYNC_RETRY_INTERVAL_MS as L, MAX_SPACE_REQUEST_TIMEOUT_MS as M, MIN_HEARTBEAT_INTERVAL_MS as N, DEFAULT_HEARTBEAT_INTERVAL_MS as O, MIN_SPACE_REQUEST_TIMEOUT_MS as P, REGISTER_RESPONSE_TIMEOUT_MS as R, CATALOG_SYNC_SIGNAL_POLL_INTERVAL_MS as S, DAEMON_STREAM_KEEPALIVE_INTERVAL_MS as T, RUNTIME_AGENT_CONTROLLER_REPORT_SYNC_RETRY_INTERVAL_MS as V, runMacOsNativeKeychainHelperIfRequested as _, beginAuthSessionTransition as a, resolveAuthSessionExpiryEligibility as b, getAuthSessionMetadata as c, getInterruptedComputerAuthSessionRecovery as d, markInterruptedComputerAuthSessionRecovery as f, updateAuthSessionActiveOrganization as g, setAuthSession as h, auth_session_exports as i, MAX_HEARTBEAT_INTERVAL_MS as j, DEFAULT_SPACE_REQUEST_TIMEOUT_MS as k, getAuthSessionState as l, recordInterruptedComputerAuthSessionRecoveryFailure as m, AuthSessionRecoveryInProgressError as n, clearAuthSession as o, normalizeStoredActiveOrganization as p, assertAuthSessionMutationAvailable as r, completeInterruptedComputerAuthSessionRecovery as s, AuthSessionChangedBeforeTransitionError as t, getCurrentAuthOwnerUserId as u, isBinaryDistribution as v, DAEMON_STREAM_CONNECT_TIMEOUT_MS as w, CATALOG_SYNC_RETRY_INTERVAL_MS as x, resolveOpenMeldDistribution as y, ROUTE_CATALOG_LOAD_TIMEOUT_MS as z };
|
|
1960
2099
|
|
|
1961
|
-
//# sourceMappingURL=auth-session-
|
|
2100
|
+
//# sourceMappingURL=auth-session-Bj8n8YwV.js.map
|