@mindexec/cli 0.2.186 → 0.2.188
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/package.json +1 -1
- package/scripts/remote-registry-follower-smoke.mjs +64 -12
- package/server.js +233 -16
- package/wwwroot/_framework/MindExecution.Web.hsle161c7m.dll +0 -0
- package/wwwroot/_framework/blazor.boot.json +3 -3
- package/wwwroot/service-worker-assets.js +4 -4
- package/wwwroot/service-worker.js +1 -1
- package/wwwroot/_framework/MindExecution.Web.ubzzp2laor.dll +0 -0
package/package.json
CHANGED
|
@@ -15,6 +15,9 @@ const PAIR_TOKEN = 'remote-registry-follower-pair-token';
|
|
|
15
15
|
const USER_ID = '11111111-2222-4333-8444-555555555555';
|
|
16
16
|
const SUPABASE_KEY = 'remote-registry-follower-key';
|
|
17
17
|
const ACCESS_TOKEN = 'remote-registry-follower-access-token';
|
|
18
|
+
const EXPIRED_ACCESS_TOKEN = 'remote-registry-follower-expired-access-token';
|
|
19
|
+
const REFRESHED_ACCESS_TOKEN = 'remote-registry-follower-refreshed-access-token';
|
|
20
|
+
const REFRESH_TOKEN = 'remote-registry-follower-refresh-token';
|
|
18
21
|
const LOCAL_BRIDGE_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
19
22
|
|
|
20
23
|
function wait(ms) {
|
|
@@ -86,6 +89,14 @@ function createRegistryTarget({ endpoint, endpointCandidates, leaseId, active =
|
|
|
86
89
|
function startFakeSupabase(getTarget, setTarget = null) {
|
|
87
90
|
const requests = [];
|
|
88
91
|
const realtimeClients = new Set();
|
|
92
|
+
const validAccessTokens = new Set([ACCESS_TOKEN]);
|
|
93
|
+
const assertRegistryAuth = req => {
|
|
94
|
+
assert.equal(String(req.headers.apikey || ''), SUPABASE_KEY);
|
|
95
|
+
const authorization = String(req.headers.authorization || '');
|
|
96
|
+
assert.ok(
|
|
97
|
+
validAccessTokens.has(authorization.replace(/^Bearer\s+/i, '')),
|
|
98
|
+
`unexpected registry authorization: ${authorization}`);
|
|
99
|
+
};
|
|
89
100
|
const server = createServer(async (req, res) => {
|
|
90
101
|
const readBody = () => new Promise(resolve => {
|
|
91
102
|
let body = '';
|
|
@@ -103,8 +114,7 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
103
114
|
});
|
|
104
115
|
|
|
105
116
|
if (req.method === 'GET' && parsed.pathname === '/rest/v1/remote_host_targets') {
|
|
106
|
-
|
|
107
|
-
assert.equal(String(req.headers.authorization || ''), `Bearer ${ACCESS_TOKEN}`);
|
|
117
|
+
assertRegistryAuth(req);
|
|
108
118
|
res.writeHead(200, {
|
|
109
119
|
'Content-Type': 'application/json',
|
|
110
120
|
'Cache-Control': 'no-store'
|
|
@@ -115,8 +125,7 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
115
125
|
}
|
|
116
126
|
|
|
117
127
|
if (req.method === 'POST' && parsed.pathname === '/rest/v1/rpc/set_remote_host_target') {
|
|
118
|
-
|
|
119
|
-
assert.equal(String(req.headers.authorization || ''), `Bearer ${ACCESS_TOKEN}`);
|
|
128
|
+
assertRegistryAuth(req);
|
|
120
129
|
const body = JSON.parse(await readBody() || '{}');
|
|
121
130
|
const now = Date.now();
|
|
122
131
|
const existing = getTarget();
|
|
@@ -148,6 +157,31 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
148
157
|
return;
|
|
149
158
|
}
|
|
150
159
|
|
|
160
|
+
if (req.method === 'POST' && parsed.pathname === '/auth/v1/token') {
|
|
161
|
+
assert.equal(parsed.searchParams.get('grant_type'), 'refresh_token');
|
|
162
|
+
assert.equal(String(req.headers.apikey || ''), SUPABASE_KEY);
|
|
163
|
+
assert.equal(String(req.headers.authorization || ''), `Bearer ${SUPABASE_KEY}`);
|
|
164
|
+
const body = JSON.parse(await readBody() || '{}');
|
|
165
|
+
assert.equal(body.refresh_token, REFRESH_TOKEN);
|
|
166
|
+
validAccessTokens.add(REFRESHED_ACCESS_TOKEN);
|
|
167
|
+
res.writeHead(200, {
|
|
168
|
+
'Content-Type': 'application/json',
|
|
169
|
+
'Cache-Control': 'no-store'
|
|
170
|
+
});
|
|
171
|
+
res.end(JSON.stringify({
|
|
172
|
+
access_token: REFRESHED_ACCESS_TOKEN,
|
|
173
|
+
refresh_token: REFRESH_TOKEN,
|
|
174
|
+
token_type: 'bearer',
|
|
175
|
+
expires_in: 3600,
|
|
176
|
+
expires_at: Math.floor(Date.now() / 1000) + 3600,
|
|
177
|
+
user: {
|
|
178
|
+
id: USER_ID,
|
|
179
|
+
email: 'remote-registry-follower@example.test'
|
|
180
|
+
}
|
|
181
|
+
}));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
151
185
|
res.writeHead(404, { 'Content-Type': 'application/json' });
|
|
152
186
|
res.end(JSON.stringify({ error: 'not-found' }));
|
|
153
187
|
});
|
|
@@ -166,8 +200,8 @@ function startFakeSupabase(getTarget, setTarget = null) {
|
|
|
166
200
|
|
|
167
201
|
if (message.event === 'phx_join') {
|
|
168
202
|
assert.equal(String(req.headers.apikey || ''), SUPABASE_KEY);
|
|
169
|
-
assert.
|
|
170
|
-
assert.
|
|
203
|
+
assert.ok(validAccessTokens.has(String(req.headers.authorization || '').replace(/^Bearer\s+/i, '')));
|
|
204
|
+
assert.ok(validAccessTokens.has(String(message.payload?.access_token || '')));
|
|
171
205
|
assert.deepEqual(
|
|
172
206
|
message.payload?.config?.postgres_changes?.[0],
|
|
173
207
|
{
|
|
@@ -338,10 +372,14 @@ async function waitForBridge(bridge) {
|
|
|
338
372
|
}, 30000, `bridge startup\n${bridge.details()}`);
|
|
339
373
|
}
|
|
340
374
|
|
|
341
|
-
function createSessionPayload() {
|
|
375
|
+
function createSessionPayload(options = {}) {
|
|
376
|
+
const accessToken = options.accessToken || ACCESS_TOKEN;
|
|
377
|
+
const refreshToken = options.refreshToken || REFRESH_TOKEN;
|
|
378
|
+
const expiresAt = options.expiresAt ?? Math.floor(Date.now() / 1000) + 3600;
|
|
342
379
|
return JSON.stringify({
|
|
343
|
-
access_token:
|
|
344
|
-
|
|
380
|
+
access_token: accessToken,
|
|
381
|
+
refresh_token: refreshToken,
|
|
382
|
+
expires_at: expiresAt,
|
|
345
383
|
user: {
|
|
346
384
|
id: USER_ID,
|
|
347
385
|
email: 'remote-registry-follower@example.test'
|
|
@@ -349,9 +387,9 @@ function createSessionPayload() {
|
|
|
349
387
|
});
|
|
350
388
|
}
|
|
351
389
|
|
|
352
|
-
async function writeSession(authRoot) {
|
|
390
|
+
async function writeSession(authRoot, options = {}) {
|
|
353
391
|
await mkdir(authRoot, { recursive: true });
|
|
354
|
-
await writeFile(path.join(authRoot, 'supabase-session.json'), createSessionPayload(), 'utf8');
|
|
392
|
+
await writeFile(path.join(authRoot, 'supabase-session.json'), createSessionPayload(options), 'utf8');
|
|
355
393
|
}
|
|
356
394
|
|
|
357
395
|
async function waitForManagedAgent(bridge, managerEndpoint, label, timeoutMs = 20000) {
|
|
@@ -435,7 +473,10 @@ async function main() {
|
|
|
435
473
|
const hostBAuth = path.join(tempRoot, 'auth-host-b');
|
|
436
474
|
const clientAuth = path.join(tempRoot, 'auth-client');
|
|
437
475
|
await writeSession(renewHostAuth);
|
|
438
|
-
await writeSession(clientAuth
|
|
476
|
+
await writeSession(clientAuth, {
|
|
477
|
+
accessToken: EXPIRED_ACCESS_TOKEN,
|
|
478
|
+
expiresAt: Math.floor(Date.now() / 1000) - 30
|
|
479
|
+
});
|
|
439
480
|
|
|
440
481
|
renewHost = spawnBridge({
|
|
441
482
|
bridgePort: renewHostBridgePort,
|
|
@@ -544,6 +585,17 @@ async function main() {
|
|
|
544
585
|
assert.match(String(firstAgent.launcher || ''), /mindexec-remote-fast/i);
|
|
545
586
|
assert.equal(firstAgent.manager, hostAEndpoint);
|
|
546
587
|
assert.equal(firstAgent.managerCandidates?.[0], staleEndpoint, JSON.stringify(firstAgent.managerCandidates));
|
|
588
|
+
assert.ok(
|
|
589
|
+
fakeSupabase.requests.some(request =>
|
|
590
|
+
request.method === 'POST'
|
|
591
|
+
&& request.pathname === '/auth/v1/token'),
|
|
592
|
+
'expected LocalBridge to refresh expired persisted Supabase session without browser involvement');
|
|
593
|
+
assert.ok(
|
|
594
|
+
fakeSupabase.requests.some(request =>
|
|
595
|
+
request.method === 'GET'
|
|
596
|
+
&& request.pathname === '/rest/v1/remote_host_targets'
|
|
597
|
+
&& request.authorization === `Bearer ${REFRESHED_ACCESS_TOKEN}`),
|
|
598
|
+
'expected registry read to use refreshed access token');
|
|
547
599
|
await waitForConnectedDevice(hostA, 'host-a');
|
|
548
600
|
const firstStatus = await fetchJson(`${client.baseUrl}/api/status`);
|
|
549
601
|
assert.equal(firstStatus.payload?.remoteRegistryRealtime?.subscribed, true, JSON.stringify(firstStatus.payload?.remoteRegistryRealtime));
|
package/server.js
CHANGED
|
@@ -3388,6 +3388,7 @@ const REMOTE_REGISTRY_FOLLOWER_MISSING_SESSION_FAST_RETRY_COUNT = Math.max(
|
|
|
3388
3388
|
Number(process.env.MINDEXEC_REMOTE_REGISTRY_MISSING_SESSION_FAST_RETRY_COUNT || 5) || 5);
|
|
3389
3389
|
const REMOTE_REGISTRY_FOLLOWER_INACTIVE_STOP_COUNT = Math.max(2, Number(process.env.MINDEXEC_REMOTE_REGISTRY_INACTIVE_STOP_COUNT || 8) || 8);
|
|
3390
3390
|
const REMOTE_REGISTRY_FOLLOWER_INACTIVE_GRACE_MS = Math.max(500, Number(process.env.MINDEXEC_REMOTE_REGISTRY_INACTIVE_GRACE_MS || 60000) || 60000);
|
|
3391
|
+
const REMOTE_REGISTRY_SESSION_REFRESH_LEAD_MS = Math.max(15_000, Number(process.env.MINDEXEC_REMOTE_REGISTRY_SESSION_REFRESH_LEAD_MS || 120_000) || 120_000);
|
|
3391
3392
|
const REMOTE_REGISTRY_REALTIME_ENABLED = REMOTE_REGISTRY_FOLLOWER_ENABLED
|
|
3392
3393
|
&& !/^(0|false|no|off)$/i.test(String(process.env.MINDEXEC_REMOTE_REGISTRY_REALTIME || 'true'));
|
|
3393
3394
|
const REMOTE_REGISTRY_REALTIME_HEARTBEAT_MS = Math.max(10000, Number(process.env.MINDEXEC_REMOTE_REGISTRY_REALTIME_HEARTBEAT_MS || 25000) || 25000);
|
|
@@ -3426,6 +3427,7 @@ let remoteRegistryFollowerStarted = false;
|
|
|
3426
3427
|
let remoteRegistryFollowerConsecutiveMissingSession = 0;
|
|
3427
3428
|
let remoteRegistryFollowerConsecutiveInactiveTarget = 0;
|
|
3428
3429
|
let remoteRegistryFollowerInactiveTargetFirstAt = 0;
|
|
3430
|
+
let remoteRegistrySessionRefreshPromise = null;
|
|
3429
3431
|
let remoteRegistryRealtimeSocket = null;
|
|
3430
3432
|
let remoteRegistryRealtimeSessionKey = '';
|
|
3431
3433
|
let remoteRegistryRealtimeTopic = '';
|
|
@@ -5533,6 +5535,75 @@ function readSupabaseSessionField(source, ...keys) {
|
|
|
5533
5535
|
return '';
|
|
5534
5536
|
}
|
|
5535
5537
|
|
|
5538
|
+
function parseSupabaseSessionExpiresAtMs(value) {
|
|
5539
|
+
if (value === undefined || value === null || String(value).trim() === '') {
|
|
5540
|
+
return 0;
|
|
5541
|
+
}
|
|
5542
|
+
|
|
5543
|
+
const numeric = Number(value);
|
|
5544
|
+
if (Number.isFinite(numeric) && numeric > 0) {
|
|
5545
|
+
return numeric > 9999999999 ? numeric : numeric * 1000;
|
|
5546
|
+
}
|
|
5547
|
+
|
|
5548
|
+
const parsed = Date.parse(String(value));
|
|
5549
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
5550
|
+
}
|
|
5551
|
+
|
|
5552
|
+
function setSupabaseSessionKnownField(session, value, fallbackKey, ...keys) {
|
|
5553
|
+
let wrote = false;
|
|
5554
|
+
for (const key of keys) {
|
|
5555
|
+
if (Object.prototype.hasOwnProperty.call(session, key)) {
|
|
5556
|
+
session[key] = value;
|
|
5557
|
+
wrote = true;
|
|
5558
|
+
}
|
|
5559
|
+
}
|
|
5560
|
+
|
|
5561
|
+
if (!wrote) {
|
|
5562
|
+
session[fallbackKey] = value;
|
|
5563
|
+
}
|
|
5564
|
+
}
|
|
5565
|
+
|
|
5566
|
+
function buildRefreshedSupabaseSessionPayload(content, refreshedSession) {
|
|
5567
|
+
const existing = JSON.parse(String(content || '{}'));
|
|
5568
|
+
const accessToken = String(readSupabaseSessionField(refreshedSession, 'access_token', 'accessToken', 'AccessToken')).trim();
|
|
5569
|
+
const refreshToken = String(readSupabaseSessionField(refreshedSession, 'refresh_token', 'refreshToken', 'RefreshToken')).trim()
|
|
5570
|
+
|| String(readSupabaseSessionField(existing, 'refresh_token', 'refreshToken', 'RefreshToken')).trim();
|
|
5571
|
+
const tokenType = String(readSupabaseSessionField(refreshedSession, 'token_type', 'tokenType', 'TokenType')).trim()
|
|
5572
|
+
|| String(readSupabaseSessionField(existing, 'token_type', 'tokenType', 'TokenType')).trim()
|
|
5573
|
+
|| 'bearer';
|
|
5574
|
+
const expiresInRaw = Number(readSupabaseSessionField(refreshedSession, 'expires_in', 'expiresIn', 'ExpiresIn') || 0);
|
|
5575
|
+
const expiresAtFromResponse = readSupabaseSessionField(refreshedSession, 'expires_at', 'expiresAt', 'ExpiresAt');
|
|
5576
|
+
const expiresAtMs = parseSupabaseSessionExpiresAtMs(expiresAtFromResponse)
|
|
5577
|
+
|| (Number.isFinite(expiresInRaw) && expiresInRaw > 0 ? Date.now() + (expiresInRaw * 1000) : 0);
|
|
5578
|
+
const expiresAtSeconds = expiresAtMs > 0 ? Math.floor(expiresAtMs / 1000) : 0;
|
|
5579
|
+
|
|
5580
|
+
if (!accessToken || !refreshToken || !expiresAtSeconds) {
|
|
5581
|
+
const error = new Error('auth-refresh-response-incomplete');
|
|
5582
|
+
error.statusCode = 502;
|
|
5583
|
+
throw error;
|
|
5584
|
+
}
|
|
5585
|
+
|
|
5586
|
+
setSupabaseSessionKnownField(existing, accessToken, 'access_token', 'access_token', 'accessToken', 'AccessToken');
|
|
5587
|
+
setSupabaseSessionKnownField(existing, refreshToken, 'refresh_token', 'refresh_token', 'refreshToken', 'RefreshToken');
|
|
5588
|
+
setSupabaseSessionKnownField(existing, tokenType, 'token_type', 'token_type', 'tokenType', 'TokenType');
|
|
5589
|
+
setSupabaseSessionKnownField(existing, expiresAtSeconds, 'expires_at', 'expires_at', 'expiresAt', 'ExpiresAt');
|
|
5590
|
+
|
|
5591
|
+
if (Number.isFinite(expiresInRaw) && expiresInRaw > 0) {
|
|
5592
|
+
setSupabaseSessionKnownField(existing, Math.floor(expiresInRaw), 'expires_in', 'expires_in', 'expiresIn', 'ExpiresIn');
|
|
5593
|
+
}
|
|
5594
|
+
|
|
5595
|
+
const user = refreshedSession?.user || refreshedSession?.User;
|
|
5596
|
+
if (user && typeof user === 'object') {
|
|
5597
|
+
if (Object.prototype.hasOwnProperty.call(existing, 'User')) {
|
|
5598
|
+
existing.User = user;
|
|
5599
|
+
} else {
|
|
5600
|
+
existing.user = user;
|
|
5601
|
+
}
|
|
5602
|
+
}
|
|
5603
|
+
|
|
5604
|
+
return JSON.stringify(existing);
|
|
5605
|
+
}
|
|
5606
|
+
|
|
5536
5607
|
function parseSupabaseSessionForRegistry(content) {
|
|
5537
5608
|
let session = null;
|
|
5538
5609
|
try {
|
|
@@ -5543,13 +5614,11 @@ function parseSupabaseSessionForRegistry(content) {
|
|
|
5543
5614
|
|
|
5544
5615
|
const user = session?.user || session?.User || {};
|
|
5545
5616
|
const accessToken = String(readSupabaseSessionField(session, 'access_token', 'accessToken', 'AccessToken')).trim();
|
|
5617
|
+
const refreshToken = String(readSupabaseSessionField(session, 'refresh_token', 'refreshToken', 'RefreshToken')).trim();
|
|
5546
5618
|
const userId = String(
|
|
5547
5619
|
readSupabaseSessionField(user, 'id', 'Id')
|
|
5548
5620
|
|| readSupabaseSessionField(session, 'user_id', 'userId', 'UserId')).trim();
|
|
5549
|
-
const
|
|
5550
|
-
const expiresAtMs = Number.isFinite(expiresAtRaw) && expiresAtRaw > 0
|
|
5551
|
-
? (expiresAtRaw > 9999999999 ? expiresAtRaw : expiresAtRaw * 1000)
|
|
5552
|
-
: 0;
|
|
5621
|
+
const expiresAtMs = parseSupabaseSessionExpiresAtMs(readSupabaseSessionField(session, 'expires_at', 'expiresAt', 'ExpiresAt'));
|
|
5553
5622
|
|
|
5554
5623
|
if (!accessToken || !userId) {
|
|
5555
5624
|
return null;
|
|
@@ -5557,11 +5626,143 @@ function parseSupabaseSessionForRegistry(content) {
|
|
|
5557
5626
|
|
|
5558
5627
|
return {
|
|
5559
5628
|
accessToken,
|
|
5629
|
+
refreshToken,
|
|
5560
5630
|
userId,
|
|
5561
5631
|
expiresAtMs
|
|
5562
5632
|
};
|
|
5563
5633
|
}
|
|
5564
5634
|
|
|
5635
|
+
async function refreshSupabaseSessionForRegistry(config, sessionPayload, reason = 'registry') {
|
|
5636
|
+
const currentSession = parseSupabaseSessionForRegistry(sessionPayload?.content);
|
|
5637
|
+
if (!currentSession?.refreshToken) {
|
|
5638
|
+
return {
|
|
5639
|
+
ok: false,
|
|
5640
|
+
reason: 'refresh-token-missing'
|
|
5641
|
+
};
|
|
5642
|
+
}
|
|
5643
|
+
|
|
5644
|
+
if (remoteRegistrySessionRefreshPromise) {
|
|
5645
|
+
return await remoteRegistrySessionRefreshPromise;
|
|
5646
|
+
}
|
|
5647
|
+
|
|
5648
|
+
remoteRegistrySessionRefreshPromise = (async () => {
|
|
5649
|
+
const url = new URL('/auth/v1/token', config.url);
|
|
5650
|
+
url.searchParams.set('grant_type', 'refresh_token');
|
|
5651
|
+
const response = await fetch(url.toString(), {
|
|
5652
|
+
method: 'POST',
|
|
5653
|
+
headers: {
|
|
5654
|
+
apikey: config.key,
|
|
5655
|
+
Authorization: `Bearer ${config.key}`,
|
|
5656
|
+
Accept: 'application/json',
|
|
5657
|
+
'Content-Type': 'application/json'
|
|
5658
|
+
},
|
|
5659
|
+
body: JSON.stringify({
|
|
5660
|
+
refresh_token: currentSession.refreshToken
|
|
5661
|
+
})
|
|
5662
|
+
});
|
|
5663
|
+
|
|
5664
|
+
if (!response.ok) {
|
|
5665
|
+
const text = await response.text().catch(() => '');
|
|
5666
|
+
const error = new Error(`auth-refresh-${response.status}${text ? `:${shortenText(text, 160)}` : ''}`);
|
|
5667
|
+
error.statusCode = response.status;
|
|
5668
|
+
throw error;
|
|
5669
|
+
}
|
|
5670
|
+
|
|
5671
|
+
const refreshed = await response.json();
|
|
5672
|
+
const content = buildRefreshedSupabaseSessionPayload(sessionPayload.content, refreshed);
|
|
5673
|
+
await writeStableAuthSessionPayload(content);
|
|
5674
|
+
const session = parseSupabaseSessionForRegistry(content);
|
|
5675
|
+
if (!session) {
|
|
5676
|
+
const error = new Error('auth-refresh-session-parse-failed');
|
|
5677
|
+
error.statusCode = 502;
|
|
5678
|
+
throw error;
|
|
5679
|
+
}
|
|
5680
|
+
|
|
5681
|
+
logEvent(
|
|
5682
|
+
'remote',
|
|
5683
|
+
`registry auth refreshed ${formatKeyValue('reason', reason || 'registry')} ${formatKeyValue('expiresInMs', Math.max(0, session.expiresAtMs - Date.now()))}`,
|
|
5684
|
+
'remote');
|
|
5685
|
+
|
|
5686
|
+
return {
|
|
5687
|
+
ok: true,
|
|
5688
|
+
reason: 'refreshed',
|
|
5689
|
+
content,
|
|
5690
|
+
session
|
|
5691
|
+
};
|
|
5692
|
+
})();
|
|
5693
|
+
|
|
5694
|
+
try {
|
|
5695
|
+
return await remoteRegistrySessionRefreshPromise;
|
|
5696
|
+
} finally {
|
|
5697
|
+
remoteRegistrySessionRefreshPromise = null;
|
|
5698
|
+
}
|
|
5699
|
+
}
|
|
5700
|
+
|
|
5701
|
+
async function readFreshSupabaseSessionForRegistry(config, reason = 'registry') {
|
|
5702
|
+
const sessionPayload = await readStableAuthSessionPayload();
|
|
5703
|
+
const session = sessionPayload.found ? parseSupabaseSessionForRegistry(sessionPayload.content) : null;
|
|
5704
|
+
if (!session) {
|
|
5705
|
+
return {
|
|
5706
|
+
found: sessionPayload.found,
|
|
5707
|
+
payload: sessionPayload,
|
|
5708
|
+
session: null,
|
|
5709
|
+
refreshed: false,
|
|
5710
|
+
expired: false
|
|
5711
|
+
};
|
|
5712
|
+
}
|
|
5713
|
+
|
|
5714
|
+
const now = Date.now();
|
|
5715
|
+
const shouldRefresh = session.expiresAtMs > 0
|
|
5716
|
+
&& session.expiresAtMs <= now + REMOTE_REGISTRY_SESSION_REFRESH_LEAD_MS;
|
|
5717
|
+
if (!shouldRefresh) {
|
|
5718
|
+
return {
|
|
5719
|
+
found: true,
|
|
5720
|
+
payload: sessionPayload,
|
|
5721
|
+
session,
|
|
5722
|
+
refreshed: false,
|
|
5723
|
+
expired: session.expiresAtMs > 0 && session.expiresAtMs <= now
|
|
5724
|
+
};
|
|
5725
|
+
}
|
|
5726
|
+
|
|
5727
|
+
try {
|
|
5728
|
+
const refreshed = await refreshSupabaseSessionForRegistry(config, sessionPayload, reason);
|
|
5729
|
+
if (refreshed.ok && refreshed.session) {
|
|
5730
|
+
return {
|
|
5731
|
+
found: true,
|
|
5732
|
+
payload: {
|
|
5733
|
+
...sessionPayload,
|
|
5734
|
+
content: refreshed.content,
|
|
5735
|
+
refreshed: true
|
|
5736
|
+
},
|
|
5737
|
+
session: refreshed.session,
|
|
5738
|
+
refreshed: true,
|
|
5739
|
+
expired: false
|
|
5740
|
+
};
|
|
5741
|
+
}
|
|
5742
|
+
|
|
5743
|
+
return {
|
|
5744
|
+
found: true,
|
|
5745
|
+
payload: sessionPayload,
|
|
5746
|
+
session,
|
|
5747
|
+
refreshed: false,
|
|
5748
|
+
expired: session.expiresAtMs > 0 && session.expiresAtMs <= Date.now(),
|
|
5749
|
+
refreshError: refreshed.reason || 'auth-refresh-skipped'
|
|
5750
|
+
};
|
|
5751
|
+
} catch (error) {
|
|
5752
|
+
logWarn(
|
|
5753
|
+
'remote',
|
|
5754
|
+
`registry auth refresh failed ${formatKeyValue('reason', reason || 'registry')} ${formatKeyValue('error', error?.message || String(error || ''))}`);
|
|
5755
|
+
return {
|
|
5756
|
+
found: true,
|
|
5757
|
+
payload: sessionPayload,
|
|
5758
|
+
session,
|
|
5759
|
+
refreshed: false,
|
|
5760
|
+
expired: session.expiresAtMs > 0 && session.expiresAtMs <= Date.now(),
|
|
5761
|
+
refreshError: error?.message || String(error || '')
|
|
5762
|
+
};
|
|
5763
|
+
}
|
|
5764
|
+
}
|
|
5765
|
+
|
|
5565
5766
|
function readRegistryTargetField(target, ...keys) {
|
|
5566
5767
|
for (const key of keys) {
|
|
5567
5768
|
const value = target?.[key];
|
|
@@ -5700,13 +5901,13 @@ async function readRemoteRegistryContext() {
|
|
|
5700
5901
|
return { ok: false, reason: 'supabase-config-missing' };
|
|
5701
5902
|
}
|
|
5702
5903
|
|
|
5703
|
-
const
|
|
5704
|
-
const session =
|
|
5904
|
+
const sessionState = await readFreshSupabaseSessionForRegistry(config, 'registry-context');
|
|
5905
|
+
const session = sessionState.session;
|
|
5705
5906
|
if (!session) {
|
|
5706
5907
|
return { ok: false, reason: 'registry-not-authenticated' };
|
|
5707
5908
|
}
|
|
5708
5909
|
|
|
5709
|
-
if (session.expiresAtMs && session.expiresAtMs <= Date.now()) {
|
|
5910
|
+
if (sessionState.expired || (session.expiresAtMs && session.expiresAtMs <= Date.now())) {
|
|
5710
5911
|
return { ok: false, reason: 'session-expired' };
|
|
5711
5912
|
}
|
|
5712
5913
|
|
|
@@ -6321,8 +6522,8 @@ async function runRemoteRegistryFollowerOnce(trigger = 'timer') {
|
|
|
6321
6522
|
return serializeRemoteRegistryFollowerState();
|
|
6322
6523
|
}
|
|
6323
6524
|
|
|
6324
|
-
const
|
|
6325
|
-
const session =
|
|
6525
|
+
const sessionState = await readFreshSupabaseSessionForRegistry(config, `follower-${trigger || 'timer'}`);
|
|
6526
|
+
const session = sessionState.session;
|
|
6326
6527
|
if (!session) {
|
|
6327
6528
|
closeRemoteRegistryRealtime('registry-not-authenticated');
|
|
6328
6529
|
remoteRegistryFollowerConsecutiveMissingSession += 1;
|
|
@@ -6368,23 +6569,39 @@ async function runRemoteRegistryFollowerOnce(trigger = 'timer') {
|
|
|
6368
6569
|
}
|
|
6369
6570
|
|
|
6370
6571
|
remoteRegistryFollowerConsecutiveMissingSession = 0;
|
|
6371
|
-
if (session.expiresAtMs && session.expiresAtMs <= Date.now()) {
|
|
6572
|
+
if (sessionState.expired || (session.expiresAtMs && session.expiresAtMs <= Date.now())) {
|
|
6372
6573
|
closeRemoteRegistryRealtime('session-expired');
|
|
6574
|
+
const agentKept = isRemoteAgentRegistryOwned() && isRemoteAgentProcessRunning();
|
|
6575
|
+
const reason = agentKept
|
|
6576
|
+
? 'session-expired-agent-kept'
|
|
6577
|
+
: 'session-expired';
|
|
6578
|
+
const keptManager = agentKept ? safeRemoteAgentField(remoteAgentState.manager, 160) : '';
|
|
6579
|
+
const keptCandidates = agentKept ? normalizeRemoteManagerEndpointList(remoteAgentState.managerCandidates, remoteAgentState.manager) : [];
|
|
6373
6580
|
updateRemoteRegistryFollowerState({
|
|
6374
|
-
status: 'skipped',
|
|
6375
|
-
reason
|
|
6581
|
+
status: agentKept ? 'auth-pending' : 'skipped',
|
|
6582
|
+
reason,
|
|
6376
6583
|
authenticated: false,
|
|
6377
6584
|
lastAttemptAt: attemptedAt,
|
|
6378
|
-
lastError: ''
|
|
6585
|
+
lastError: sessionState.refreshError || '',
|
|
6586
|
+
targetEndpoint: keptManager,
|
|
6587
|
+
targetEndpointCandidates: keptCandidates,
|
|
6588
|
+
targetLeaseId: agentKept ? safeRemoteAgentField(remoteAgentState.leaseId, 128) : '',
|
|
6589
|
+
targetNodeId: agentKept ? safeRemoteAgentField(remoteAgentState.nodeId, 128) : '',
|
|
6590
|
+
agentKept
|
|
6379
6591
|
});
|
|
6380
6592
|
await reportRemoteRegistryFollowerSync({
|
|
6381
6593
|
ok: true,
|
|
6382
6594
|
skipped: true,
|
|
6383
|
-
reason
|
|
6595
|
+
reason,
|
|
6384
6596
|
trigger,
|
|
6385
|
-
authenticated: false
|
|
6597
|
+
authenticated: false,
|
|
6598
|
+
targetEndpoint: keptManager,
|
|
6599
|
+
targetEndpointCandidates: keptCandidates,
|
|
6600
|
+
targetLeaseId: agentKept ? remoteAgentState.leaseId : '',
|
|
6601
|
+
targetNodeId: agentKept ? remoteAgentState.nodeId : '',
|
|
6602
|
+
agentKept
|
|
6386
6603
|
});
|
|
6387
|
-
scheduleRemoteRegistryFollower(REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'session-expired');
|
|
6604
|
+
scheduleRemoteRegistryFollower(agentKept ? REMOTE_REGISTRY_FOLLOWER_FAST_RETRY_MS : REMOTE_REGISTRY_FOLLOWER_POLL_MS, 'session-expired');
|
|
6388
6605
|
return serializeRemoteRegistryFollowerState();
|
|
6389
6606
|
}
|
|
6390
6607
|
|
|
Binary file
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"mainAssemblyName": "MindExecution.Web",
|
|
3
3
|
"resources": {
|
|
4
|
-
"hash": "sha256-
|
|
4
|
+
"hash": "sha256-wO/y6PhLVnM+OKb9a+DZH6St+JWnCdBxHFafo2120Dc=",
|
|
5
5
|
"fingerprinting": {
|
|
6
6
|
"Google.Protobuf.9h59ukbel7.dll": "Google.Protobuf.dll",
|
|
7
7
|
"Markdig.d1j7v41cl1.dll": "Markdig.dll",
|
|
@@ -132,7 +132,7 @@
|
|
|
132
132
|
"MindExecution.Plugins.PlanMaster.xt4y5gj8yw.dll": "MindExecution.Plugins.PlanMaster.dll",
|
|
133
133
|
"MindExecution.Plugins.YouTube.kz0lju94md.dll": "MindExecution.Plugins.YouTube.dll",
|
|
134
134
|
"MindExecution.Shared.7rwzquw93z.dll": "MindExecution.Shared.dll",
|
|
135
|
-
"MindExecution.Web.
|
|
135
|
+
"MindExecution.Web.hsle161c7m.dll": "MindExecution.Web.dll",
|
|
136
136
|
"dotnet.js": "dotnet.js",
|
|
137
137
|
"dotnet.native.qc8g39g30v.js": "dotnet.native.js",
|
|
138
138
|
"dotnet.native.boem75ye5i.wasm": "dotnet.native.wasm",
|
|
@@ -284,7 +284,7 @@
|
|
|
284
284
|
"MindExecution.Plugins.Concept.3mpew6v5hz.dll": "sha256-K6GVU9mkcEQatq6shNKDO9xtahvaLBOhrIOzYda8lAI=",
|
|
285
285
|
"MindExecution.Plugins.PlanMaster.xt4y5gj8yw.dll": "sha256-Cx47VicQk4brENQ4Y/9SMJ81sawy9AkvnrxuDnqoAYI=",
|
|
286
286
|
"MindExecution.Shared.7rwzquw93z.dll": "sha256-bZ2rx8V2aQu7Ff4nl/mgbEOg3P/3YNjMmFpIXWcu3cQ=",
|
|
287
|
-
"MindExecution.Web.
|
|
287
|
+
"MindExecution.Web.hsle161c7m.dll": "sha256-0e786DmgqKdqucNj3NloG//KBor6Wqoeci7UHtgdyaA="
|
|
288
288
|
},
|
|
289
289
|
"lazyAssembly": {
|
|
290
290
|
"MindExecution.Plugins.Admin.29mytzdaun.dll": "sha256-2mHPbTPcHCi1xPuk3dNMVWxn42xZFUZ3QFhm3xIV/6E=",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
self.assetsManifest = {
|
|
2
|
-
"version": "
|
|
2
|
+
"version": "gYWza81R",
|
|
3
3
|
"assets": [
|
|
4
4
|
{
|
|
5
5
|
"hash": "sha256-+CSYMcqLNTsq3VnH11jgYyOCCdxvHzL74CBmo4sCmMU=",
|
|
@@ -446,8 +446,8 @@
|
|
|
446
446
|
"url": "_framework/MindExecution.Shared.7rwzquw93z.dll"
|
|
447
447
|
},
|
|
448
448
|
{
|
|
449
|
-
"hash": "sha256-
|
|
450
|
-
"url": "_framework/MindExecution.Web.
|
|
449
|
+
"hash": "sha256-0e786DmgqKdqucNj3NloG//KBor6Wqoeci7UHtgdyaA=",
|
|
450
|
+
"url": "_framework/MindExecution.Web.hsle161c7m.dll"
|
|
451
451
|
},
|
|
452
452
|
{
|
|
453
453
|
"hash": "sha256-IsZJ91/OW+fHzNqIgEc7Y072ns8z9dGritiSyvR9Wgc=",
|
|
@@ -770,7 +770,7 @@
|
|
|
770
770
|
"url": "_framework/Websocket.Client.vapounvmnl.dll"
|
|
771
771
|
},
|
|
772
772
|
{
|
|
773
|
-
"hash": "sha256-
|
|
773
|
+
"hash": "sha256-qmh1kbyeNhh7PgQWPBUrFh5bC+YayDSQBAYA92Yo4VA=",
|
|
774
774
|
"url": "_framework/blazor.boot.json"
|
|
775
775
|
},
|
|
776
776
|
{
|
|
Binary file
|