instar 1.3.1236 → 1.3.1238
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/dashboard/index.html +4 -0
- package/dashboard/subscriptions.js +103 -12
- package/dist/commands/server.d.ts.map +1 -1
- package/dist/commands/server.js +14 -1
- package/dist/commands/server.js.map +1 -1
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts +6 -0
- package/dist/coordination/AccountFollowMeMandateDelivery.d.ts.map +1 -1
- package/dist/coordination/AccountFollowMeMandateDelivery.js +16 -2
- package/dist/coordination/AccountFollowMeMandateDelivery.js.map +1 -1
- package/dist/coordination/DeliveredMandateStore.d.ts +4 -0
- package/dist/coordination/DeliveredMandateStore.d.ts.map +1 -1
- package/dist/coordination/DeliveredMandateStore.js +16 -2
- package/dist/coordination/DeliveredMandateStore.js.map +1 -1
- package/dist/core/EnrollmentWizard.d.ts +8 -2
- package/dist/core/EnrollmentWizard.d.ts.map +1 -1
- package/dist/core/EnrollmentWizard.js +77 -18
- package/dist/core/EnrollmentWizard.js.map +1 -1
- package/dist/core/PendingLoginStore.d.ts +13 -0
- package/dist/core/PendingLoginStore.d.ts.map +1 -1
- package/dist/core/PendingLoginStore.js +20 -0
- package/dist/core/PendingLoginStore.js.map +1 -1
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +12 -0
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/data/standards-guard-index.json +2 -2
- package/dist/data/standards-guard-index.meta.json +3 -3
- package/dist/data/standards-registry.md +37 -37
- package/dist/data/standards-registry.meta.json +2 -2
- package/dist/scaffold/templates.d.ts.map +1 -1
- package/dist/scaffold/templates.js +1 -0
- package/dist/scaffold/templates.js.map +1 -1
- package/dist/server/AgentServer.d.ts.map +1 -1
- package/dist/server/AgentServer.js +12 -1
- package/dist/server/AgentServer.js.map +1 -1
- package/dist/server/CapabilityIndex.d.ts.map +1 -1
- package/dist/server/CapabilityIndex.js +2 -0
- package/dist/server/CapabilityIndex.js.map +1 -1
- package/dist/server/routes.d.ts +5 -0
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +260 -17
- package/dist/server/routes.js.map +1 -1
- package/package.json +2 -1
- package/src/data/builtin-manifest.json +64 -64
- package/src/data/standards-guard-index.json +2 -2
- package/src/data/standards-guard-index.meta.json +3 -3
- package/src/data/standards-registry.md +37 -37
- package/src/data/standards-registry.meta.json +2 -2
- package/src/scaffold/templates.ts +1 -0
- package/upgrades/1.3.1237.md +27 -0
- package/upgrades/1.3.1238.md +26 -0
- package/upgrades/relogin-pool-one-click.eli16.md +9 -0
- package/upgrades/side-effects/relogin-pool-one-click.md +86 -0
- package/upgrades/side-effects/subscription-relogin-completion-witness.md +125 -0
package/dashboard/index.html
CHANGED
|
@@ -9857,6 +9857,10 @@
|
|
|
9857
9857
|
__subController = __subModule.createController({
|
|
9858
9858
|
doc: document, els, fetchImpl,
|
|
9859
9859
|
getOperatorSessionToken: () => operatorSessionToken,
|
|
9860
|
+
requestUnlock: () => {
|
|
9861
|
+
document.getElementById('authOverlay').style.display = 'flex';
|
|
9862
|
+
document.getElementById('pinInput').focus();
|
|
9863
|
+
},
|
|
9860
9864
|
});
|
|
9861
9865
|
}
|
|
9862
9866
|
__subController.start();
|
|
@@ -811,7 +811,7 @@ export function groupMatrixRowsByProvider(rows) {
|
|
|
811
811
|
/** Render the account × machine grid. `target` is replaced. Each empty (reachable) cell
|
|
812
812
|
* gets a "Set up" button carrying its (accountId, machineId) as data-* attributes for the
|
|
813
813
|
* controller's delegated tap handler. Offline columns are disabled; no state is fabricated. */
|
|
814
|
-
export function renderAccountMatrix(doc, target, poolScope, pendingScope, transient = {}) {
|
|
814
|
+
export function renderAccountMatrix(doc, target, poolScope, pendingScope, transient = {}, reloginEpisodes = [], reloginFailures = []) {
|
|
815
815
|
if (!target) return;
|
|
816
816
|
target.replaceChildren();
|
|
817
817
|
const model = buildMatrixModel(poolScope, pendingScope, transient);
|
|
@@ -855,12 +855,38 @@ export function renderAccountMatrix(doc, target, poolScope, pendingScope, transi
|
|
|
855
855
|
const tr = doc.createElement('tr');
|
|
856
856
|
tr.appendChild(el(doc, 'th', 'sub-matrix-acct', sanitizeForDisplay(row.account.email, 'label')));
|
|
857
857
|
for (const c of row.cells) {
|
|
858
|
+
const repair = Array.isArray(reloginEpisodes)
|
|
859
|
+
? reloginEpisodes.find((episode) => episode && episode.accountId === c.accountId && episode.machineId === c.machineId)
|
|
860
|
+
: null;
|
|
861
|
+
const repairUnavailable = Array.isArray(reloginFailures)
|
|
862
|
+
&& reloginFailures.some((failure) => failure && failure.machineId === c.machineId);
|
|
858
863
|
// just-verified rides a transient highlight on an otherwise-active cell (D4 ceremony).
|
|
859
864
|
const justVerified = c.state === 'just-verified' || (c.state === 'active' && c.detail && c.detail.state === 'just-verified');
|
|
860
865
|
const td = el(doc, 'td', `sub-matrix-cell sub-matrix-${c.state}${justVerified && c.state !== 'just-verified' ? ' sub-matrix-just-verified' : ''}`);
|
|
861
866
|
// Stable cell identity for the interaction-hold rule + targeted merge updates (F9).
|
|
862
867
|
td.setAttribute('data-cell-key', sanitizeForDisplay(`${c.accountId}::${c.machineId}`, 'url'));
|
|
863
|
-
if (
|
|
868
|
+
if (repairUnavailable && ['needs-reauth', 'held', 'cant-resolve', 'expired', 'broken'].includes(c.state)) {
|
|
869
|
+
td.appendChild(el(doc, 'div', 'sub-account-repair-status', 'Repair status is unavailable for this machine.'));
|
|
870
|
+
td.appendChild(el(doc, 'div', 'sub-matrix-held-detail', 'Wait for the machine connection to recover, then try again.'));
|
|
871
|
+
} else if (repair && ['suggested', 'approved', 'cli-starting', 'artifact-ready', 'browser-driving', 'cli-finishing', 'identity-verifying', 'auth-verifying', 'waiting-operator-only', 'failed'].includes(repair.state)) {
|
|
872
|
+
const label = repair.state === 'suggested' ? 'Sign-in repair is ready.'
|
|
873
|
+
: repair.state === 'waiting-operator-only' ? 'Sign-in needs your help.'
|
|
874
|
+
: repair.state === 'failed' ? 'Sign-in repair stopped safely.'
|
|
875
|
+
: 'Repairing sign-in…';
|
|
876
|
+
td.appendChild(el(doc, 'div', 'sub-account-repair-status', label));
|
|
877
|
+
const repairAction = repair.state === 'suggested' ? ['approve', 'Repair sign-in']
|
|
878
|
+
: repair.state === 'failed' ? ['retry', 'Try repair again']
|
|
879
|
+
: ['cancel', 'Cancel repair'];
|
|
880
|
+
if (repairAction) {
|
|
881
|
+
const button = el(doc, 'button', `sub-matrix-repair sub-matrix-repair-${repairAction[0]}`, repairAction[1]);
|
|
882
|
+
button.setAttribute('data-matrix-relogin', '1');
|
|
883
|
+
button.setAttribute('data-repair-action', repairAction[0]);
|
|
884
|
+
button.setAttribute('data-account-id', sanitizeForDisplay(c.accountId, 'label'));
|
|
885
|
+
button.setAttribute('data-machine-id', sanitizeForDisplay(c.machineId, 'label'));
|
|
886
|
+
button.setAttribute('data-episode-id', sanitizeForDisplay(repair.id, 'label'));
|
|
887
|
+
td.appendChild(button);
|
|
888
|
+
}
|
|
889
|
+
} else if (c.state === 'empty' || c.state === 'needs-reauth' || c.state === 'held' || c.state === 'cant-resolve' || c.state === 'email-missing' || c.state === 'expired' || c.state === 'broken') {
|
|
864
890
|
// An actionable cell → a button that runs the SAME in-dashboard sign-in flow (PIN → link →
|
|
865
891
|
// paste code). empty → "Set up"; needs-reauth (an existing account whose login expired) →
|
|
866
892
|
// "Sign in"; held/cant-resolve/expired/broken → "Retry". A needs-reauth account already
|
|
@@ -959,7 +985,9 @@ const URLS = {
|
|
|
959
985
|
accountsPool: '/subscription-pool?scope=pool',
|
|
960
986
|
startCell: '/subscription-pool/matrix/start-cell', // POST (PIN-gated) — start a cell's sign-in
|
|
961
987
|
cancel: '/subscription-pool/follow-me/cancel', // POST (Bearer, no PIN) — cancel an in-flight cell (relay → self/peer)
|
|
962
|
-
relogin: '/subscription-relogin',
|
|
988
|
+
relogin: '/subscription-relogin?scope=pool',
|
|
989
|
+
reloginAction: '/subscription-relogin',
|
|
990
|
+
repairCell: '/subscription-relogin/repair-cell',
|
|
963
991
|
provisionProfile: '/playwright-profiles/provision',
|
|
964
992
|
};
|
|
965
993
|
|
|
@@ -1017,6 +1045,7 @@ export function createController(opts) {
|
|
|
1017
1045
|
schedule = (fn, ms) => setTimeout(fn, ms),
|
|
1018
1046
|
cancel = (id) => clearTimeout(id),
|
|
1019
1047
|
getOperatorSessionToken = () => '',
|
|
1048
|
+
requestUnlock = () => {},
|
|
1020
1049
|
} = opts;
|
|
1021
1050
|
|
|
1022
1051
|
// matrixTransient: client-side last-attempt state per `${accountId}::${machineId}` cell (FD6 —
|
|
@@ -1027,7 +1056,7 @@ export function createController(opts) {
|
|
|
1027
1056
|
// add the D4 ceremony when the episode lands. recentOutcomes: client-observed terminal
|
|
1028
1057
|
// outcomes rendered as explicit cards in the pending panel (D4 — never a vanishing line).
|
|
1029
1058
|
const state = { timerId: null, active: false, inFlight: null, offers: [], approveWired: false,
|
|
1030
|
-
reloginWired: false, reloginEpisodes: [], profileProvisionWired: false,
|
|
1059
|
+
reloginWired: false, reloginEpisodes: [], reloginFailures: [], profileProvisionWired: false,
|
|
1031
1060
|
matrixWired: false, matrixTransient: {}, lastPoolBody: null, lastPendingBody: null,
|
|
1032
1061
|
matrixEpisodes: {}, recentOutcomes: [] };
|
|
1033
1062
|
|
|
@@ -1090,6 +1119,8 @@ export function createController(opts) {
|
|
|
1090
1119
|
}
|
|
1091
1120
|
state.offers = scanBody && Array.isArray(scanBody.offered) ? scanBody.offered : [];
|
|
1092
1121
|
state.reloginEpisodes = reloginBody && Array.isArray(reloginBody.episodes) ? reloginBody.episodes : [];
|
|
1122
|
+
state.reloginFailures = reloginBody && reloginBody.pool && Array.isArray(reloginBody.pool.failed)
|
|
1123
|
+
? reloginBody.pool.failed : [];
|
|
1093
1124
|
if (els.profileProvision && !state.profileProvisionWired) {
|
|
1094
1125
|
renderProfileProvisioner(doc, els.profileProvision);
|
|
1095
1126
|
wireProfileProvisioner();
|
|
@@ -1109,7 +1140,11 @@ export function createController(opts) {
|
|
|
1109
1140
|
const profileId = els.profileProvision.querySelector('[aria-label="Profile name"]')?.value?.trim() || '';
|
|
1110
1141
|
const loginMethod = els.profileProvision.querySelector('[aria-label="Sign-in method"]')?.value || 'session-cookie';
|
|
1111
1142
|
const status = els.profileProvision.querySelector('.sub-profile-status');
|
|
1112
|
-
if (!proof) {
|
|
1143
|
+
if (!proof) {
|
|
1144
|
+
requestUnlock();
|
|
1145
|
+
status.textContent = 'Enter your dashboard PIN, then retry.';
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1113
1148
|
if (!identity || !profileId) { status.textContent = 'Enter the Google account and a profile name.'; return; }
|
|
1114
1149
|
button.setAttribute('disabled', 'disabled');
|
|
1115
1150
|
status.textContent = 'Preparing the private profile…';
|
|
@@ -1122,8 +1157,9 @@ export function createController(opts) {
|
|
|
1122
1157
|
});
|
|
1123
1158
|
const body = await response.json().catch(() => ({}));
|
|
1124
1159
|
if (!response.ok) {
|
|
1160
|
+
if (response.status === 401) requestUnlock();
|
|
1125
1161
|
status.textContent = response.status === 401
|
|
1126
|
-
? '
|
|
1162
|
+
? 'Enter your dashboard PIN, then retry.'
|
|
1127
1163
|
: `Couldn’t create the profile: ${body.error || 'try again'}`;
|
|
1128
1164
|
button.removeAttribute('disabled');
|
|
1129
1165
|
return;
|
|
@@ -1242,14 +1278,18 @@ export function createController(opts) {
|
|
|
1242
1278
|
function rerenderMatrixFromCache() {
|
|
1243
1279
|
if (!els.matrix || !state.lastPoolBody) return;
|
|
1244
1280
|
if (hasOpenInteraction(doc, els.matrix)) return;
|
|
1245
|
-
renderAccountMatrix(doc, els.matrix, state.lastPoolBody, state.lastPendingBody, state.matrixTransient);
|
|
1281
|
+
renderAccountMatrix(doc, els.matrix, state.lastPoolBody, state.lastPendingBody, state.matrixTransient, state.reloginEpisodes, state.reloginFailures);
|
|
1246
1282
|
}
|
|
1247
1283
|
|
|
1248
1284
|
function render(accountsBody, pendingBody, inUseBody, poolBody) {
|
|
1249
1285
|
const accounts = accountsBody && Array.isArray(accountsBody.accounts) ? accountsBody.accounts : [];
|
|
1250
1286
|
const logins = pendingBody && Array.isArray(pendingBody.logins) ? pendingBody.logins : [];
|
|
1251
1287
|
const inUseAccountId = inUseBody && inUseBody.activeAccountId ? inUseBody.activeAccountId : null;
|
|
1252
|
-
|
|
1288
|
+
// The account-card pane is machine-local. Do not attach a remote machine's
|
|
1289
|
+
// episode to a same-account local card; remote repairs belong to the exact
|
|
1290
|
+
// account×machine matrix cell below.
|
|
1291
|
+
const localReloginEpisodes = state.reloginEpisodes.filter((episode) => episode && episode.remote !== true);
|
|
1292
|
+
renderAccounts(doc, els.accounts, accounts, now(), inUseAccountId, localReloginEpisodes);
|
|
1253
1293
|
wireReloginActions();
|
|
1254
1294
|
purgeTransients();
|
|
1255
1295
|
// Episode reconciliation runs FIRST so a terminal transition it derives (expired /
|
|
@@ -1273,7 +1313,7 @@ export function createController(opts) {
|
|
|
1273
1313
|
// the (already pool-scope) pending logins. Hidden when the pool-scope read is unavailable.
|
|
1274
1314
|
if (els.matrix) {
|
|
1275
1315
|
if (!hasOpenInteraction(doc, els.matrix)) {
|
|
1276
|
-
renderAccountMatrix(doc, els.matrix, poolBody, pendingBody, state.matrixTransient);
|
|
1316
|
+
renderAccountMatrix(doc, els.matrix, poolBody, pendingBody, state.matrixTransient, state.reloginEpisodes, state.reloginFailures);
|
|
1277
1317
|
} else {
|
|
1278
1318
|
updateCountdowns(doc, els.matrix, now());
|
|
1279
1319
|
}
|
|
@@ -1302,7 +1342,8 @@ export function createController(opts) {
|
|
|
1302
1342
|
if (!['approve', 'cancel', 'retry'].includes(action) || !episodeId) return;
|
|
1303
1343
|
const proof = getOperatorSessionToken();
|
|
1304
1344
|
if (!proof) {
|
|
1305
|
-
|
|
1345
|
+
requestUnlock();
|
|
1346
|
+
button.textContent = 'Enter PIN, then try again';
|
|
1306
1347
|
return;
|
|
1307
1348
|
}
|
|
1308
1349
|
button.setAttribute('disabled', 'disabled');
|
|
@@ -1312,13 +1353,14 @@ export function createController(opts) {
|
|
|
1312
1353
|
|
|
1313
1354
|
async function postReloginAction(episodeId, action, proof, button) {
|
|
1314
1355
|
try {
|
|
1315
|
-
const response = await fetchImpl(`${URLS.
|
|
1356
|
+
const response = await fetchImpl(`${URLS.reloginAction}/${encodeURIComponent(episodeId)}/${action}`, {
|
|
1316
1357
|
method: 'POST',
|
|
1317
1358
|
headers: { 'Content-Type': 'application/json', 'X-Instar-Operator-Session': proof },
|
|
1318
1359
|
body: '{}',
|
|
1319
1360
|
});
|
|
1320
1361
|
if (!response.ok) {
|
|
1321
|
-
|
|
1362
|
+
if (response.status === 401) requestUnlock();
|
|
1363
|
+
button.textContent = response.status === 401 ? 'Enter PIN, then try again' : 'Couldn’t start — try again';
|
|
1322
1364
|
button.removeAttribute('disabled');
|
|
1323
1365
|
return;
|
|
1324
1366
|
}
|
|
@@ -1466,6 +1508,8 @@ export function createController(opts) {
|
|
|
1466
1508
|
els.matrix.addEventListener('click', (ev) => {
|
|
1467
1509
|
const t = ev.target;
|
|
1468
1510
|
if (!t || typeof t.closest !== 'function') return;
|
|
1511
|
+
const repairBtn = t.closest('[data-matrix-relogin]');
|
|
1512
|
+
if (repairBtn && els.matrix.contains(repairBtn)) { onRepairTap(repairBtn); return; }
|
|
1469
1513
|
const setupBtn = t.closest('[data-matrix-setup]');
|
|
1470
1514
|
if (setupBtn && els.matrix.contains(setupBtn)) { onSetupTap(setupBtn); return; }
|
|
1471
1515
|
const confirmBtn = t.closest('[data-matrix-confirm]');
|
|
@@ -1488,6 +1532,53 @@ export function createController(opts) {
|
|
|
1488
1532
|
s.textContent = text;
|
|
1489
1533
|
}
|
|
1490
1534
|
|
|
1535
|
+
// One click after dashboard unlock authorizes exactly one account×machine
|
|
1536
|
+
// repair. The short-lived operator proof stays on the fronting machine; the
|
|
1537
|
+
// server converts it into an exact, signed mandate for the target machine.
|
|
1538
|
+
function onRepairTap(btn) {
|
|
1539
|
+
const cell = matrixCellOf(btn);
|
|
1540
|
+
if (!cell) return;
|
|
1541
|
+
const accountId = btn.getAttribute('data-account-id');
|
|
1542
|
+
const machineId = btn.getAttribute('data-machine-id');
|
|
1543
|
+
const episodeId = btn.getAttribute('data-episode-id');
|
|
1544
|
+
const action = btn.getAttribute('data-repair-action') || 'approve';
|
|
1545
|
+
const proof = getOperatorSessionToken();
|
|
1546
|
+
if (!accountId || !machineId || !episodeId) {
|
|
1547
|
+
setCellStatus(cell, 'Couldn’t prepare this repair — refresh and try again.');
|
|
1548
|
+
return;
|
|
1549
|
+
}
|
|
1550
|
+
if (!proof) {
|
|
1551
|
+
requestUnlock();
|
|
1552
|
+
setCellStatus(cell, 'Enter your dashboard PIN, then tap Repair sign-in once.');
|
|
1553
|
+
return;
|
|
1554
|
+
}
|
|
1555
|
+
btn.setAttribute('disabled', 'disabled');
|
|
1556
|
+
setCellStatus(cell, 'Starting autonomous sign-in repair…');
|
|
1557
|
+
void (async () => {
|
|
1558
|
+
try {
|
|
1559
|
+
const response = await fetchImpl(URLS.repairCell, {
|
|
1560
|
+
method: 'POST',
|
|
1561
|
+
headers: { 'Content-Type': 'application/json', 'X-Instar-Operator-Session': proof },
|
|
1562
|
+
body: JSON.stringify({ accountId, machineId, episodeId, action }),
|
|
1563
|
+
});
|
|
1564
|
+
const body = await response.json().catch(() => ({}));
|
|
1565
|
+
if (!response.ok) {
|
|
1566
|
+
if (response.status === 401) requestUnlock();
|
|
1567
|
+
setCellStatus(cell, response.status === 401
|
|
1568
|
+
? 'Enter your dashboard PIN, then tap Repair sign-in once.'
|
|
1569
|
+
: body.error || 'Couldn’t start the repair — try again.');
|
|
1570
|
+
btn.removeAttribute('disabled');
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
setCellStatus(cell, 'Repair started — Instar is handling the sign-in.');
|
|
1574
|
+
await tick();
|
|
1575
|
+
} catch {
|
|
1576
|
+
setCellStatus(cell, 'Couldn’t reach the target machine — try again.');
|
|
1577
|
+
btn.removeAttribute('disabled');
|
|
1578
|
+
}
|
|
1579
|
+
})();
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1491
1582
|
// Expand the cell into a PIN input + Confirm (replacing the "Set up"/"Retry" button).
|
|
1492
1583
|
// The cell becomes an OPEN INTERACTION the moment it expands (F9): the marker holds it
|
|
1493
1584
|
// through the poll until the flow reaches a terminal state or the operator backs out.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/server.js
CHANGED
|
@@ -9,6 +9,7 @@ import { bindUnknownProducerTelegramSender, sendDeterministicTelegramNotice } fr
|
|
|
9
9
|
* topic message → find/spawn session → inject message → session replies via [telegram:N]
|
|
10
10
|
*/
|
|
11
11
|
import { execFile, execFileSync } from 'node:child_process';
|
|
12
|
+
import { createHmac } from 'node:crypto';
|
|
12
13
|
import fs from 'node:fs';
|
|
13
14
|
import os from 'node:os';
|
|
14
15
|
import path from 'node:path';
|
|
@@ -13542,6 +13543,19 @@ export async function startServer(options) {
|
|
|
13542
13543
|
return null;
|
|
13543
13544
|
}
|
|
13544
13545
|
},
|
|
13546
|
+
// Claude's `.claude.json` is a settings file and commonly predates a
|
|
13547
|
+
// repair, so its mere existence can never prove this login completed.
|
|
13548
|
+
// Capture an HMAC revision of the machine-local keychain blob before the
|
|
13549
|
+
// artifact is issued and require that revision to change. The auth bytes
|
|
13550
|
+
// never leave memory; only the keyed digest enters pending state.
|
|
13551
|
+
authRevisionWitness: async (login) => {
|
|
13552
|
+
if (login.framework !== 'claude-code' || !login.configHome)
|
|
13553
|
+
return null;
|
|
13554
|
+
const raw = await defaultKeychainExec.readService(credSwapService(login.configHome));
|
|
13555
|
+
if (!raw)
|
|
13556
|
+
return null;
|
|
13557
|
+
return createHmac('sha256', config.authToken ?? '').update(raw).digest('hex');
|
|
13558
|
+
},
|
|
13545
13559
|
driveLogin: new FrameworkLoginDriver({
|
|
13546
13560
|
// Capture with `tmux capture-pane -J` so a hard-WRAPPED verification URL is
|
|
13547
13561
|
// joined back into one line (the 2026-06-18 "code=t" truncation bug). Falls
|
|
@@ -13610,7 +13624,6 @@ export async function startServer(options) {
|
|
|
13610
13624
|
.catch(() => { });
|
|
13611
13625
|
enrollmentWizard
|
|
13612
13626
|
.sweepFollowMeCompletions({
|
|
13613
|
-
credentialReady: (l) => !!l.configHome && fs.existsSync(path.join(l.configHome, '.claude.json')),
|
|
13614
13627
|
onValidated: (login, email) => {
|
|
13615
13628
|
// Upsert (D5): a re-auth of an EXISTING pool account updates it back to
|
|
13616
13629
|
// active; only a genuinely-new account is added (add() refuses dup ids).
|