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.
Files changed (53) hide show
  1. package/dashboard/index.html +4 -0
  2. package/dashboard/subscriptions.js +103 -12
  3. package/dist/commands/server.d.ts.map +1 -1
  4. package/dist/commands/server.js +14 -1
  5. package/dist/commands/server.js.map +1 -1
  6. package/dist/coordination/AccountFollowMeMandateDelivery.d.ts +6 -0
  7. package/dist/coordination/AccountFollowMeMandateDelivery.d.ts.map +1 -1
  8. package/dist/coordination/AccountFollowMeMandateDelivery.js +16 -2
  9. package/dist/coordination/AccountFollowMeMandateDelivery.js.map +1 -1
  10. package/dist/coordination/DeliveredMandateStore.d.ts +4 -0
  11. package/dist/coordination/DeliveredMandateStore.d.ts.map +1 -1
  12. package/dist/coordination/DeliveredMandateStore.js +16 -2
  13. package/dist/coordination/DeliveredMandateStore.js.map +1 -1
  14. package/dist/core/EnrollmentWizard.d.ts +8 -2
  15. package/dist/core/EnrollmentWizard.d.ts.map +1 -1
  16. package/dist/core/EnrollmentWizard.js +77 -18
  17. package/dist/core/EnrollmentWizard.js.map +1 -1
  18. package/dist/core/PendingLoginStore.d.ts +13 -0
  19. package/dist/core/PendingLoginStore.d.ts.map +1 -1
  20. package/dist/core/PendingLoginStore.js +20 -0
  21. package/dist/core/PendingLoginStore.js.map +1 -1
  22. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  23. package/dist/core/PostUpdateMigrator.js +12 -0
  24. package/dist/core/PostUpdateMigrator.js.map +1 -1
  25. package/dist/data/standards-guard-index.json +2 -2
  26. package/dist/data/standards-guard-index.meta.json +3 -3
  27. package/dist/data/standards-registry.md +37 -37
  28. package/dist/data/standards-registry.meta.json +2 -2
  29. package/dist/scaffold/templates.d.ts.map +1 -1
  30. package/dist/scaffold/templates.js +1 -0
  31. package/dist/scaffold/templates.js.map +1 -1
  32. package/dist/server/AgentServer.d.ts.map +1 -1
  33. package/dist/server/AgentServer.js +12 -1
  34. package/dist/server/AgentServer.js.map +1 -1
  35. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  36. package/dist/server/CapabilityIndex.js +2 -0
  37. package/dist/server/CapabilityIndex.js.map +1 -1
  38. package/dist/server/routes.d.ts +5 -0
  39. package/dist/server/routes.d.ts.map +1 -1
  40. package/dist/server/routes.js +260 -17
  41. package/dist/server/routes.js.map +1 -1
  42. package/package.json +2 -1
  43. package/src/data/builtin-manifest.json +64 -64
  44. package/src/data/standards-guard-index.json +2 -2
  45. package/src/data/standards-guard-index.meta.json +3 -3
  46. package/src/data/standards-registry.md +37 -37
  47. package/src/data/standards-registry.meta.json +2 -2
  48. package/src/scaffold/templates.ts +1 -0
  49. package/upgrades/1.3.1237.md +27 -0
  50. package/upgrades/1.3.1238.md +26 -0
  51. package/upgrades/relogin-pool-one-click.eli16.md +9 -0
  52. package/upgrades/side-effects/relogin-pool-one-click.md +86 -0
  53. package/upgrades/side-effects/subscription-relogin-completion-witness.md +125 -0
@@ -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 (c.state === 'empty' || c.state === 'needs-reauth' || c.state === 'held' || c.state === 'cant-resolve' || c.state === 'email-missing' || c.state === 'expired' || c.state === 'broken') {
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) { status.textContent = 'Unlock the dashboard again, then retry.'; return; }
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
- ? 'Unlock the dashboard again, then retry.'
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
- renderAccounts(doc, els.accounts, accounts, now(), inUseAccountId, state.reloginEpisodes);
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
- button.textContent = 'Unlock the dashboard again';
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.relogin}/${encodeURIComponent(episodeId)}/${action}`, {
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
- button.textContent = response.status === 401 ? 'Unlock the dashboard again' : 'Couldn’t start — try again';
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":"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"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"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"}
@@ -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).