instar 1.3.839 → 1.3.840

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.
@@ -3644,7 +3644,7 @@
3644
3644
  the dashboard (PIN → sign-in link → paste the code). Hidden when there's nothing to show. -->
3645
3645
  <section class="ph-section">
3646
3646
  <h3 class="ph-h">Accounts on each machine</h3>
3647
- <p class="ph-h-sub">Which subscription is set up on which machine. Tap “Set up” on an empty cell to add one — you’ll enter your PIN, open the sign-in link, and paste the code right here.</p>
3647
+ <p class="ph-h-sub">Which subscription is set up on which machine. Tap “Set up” on an empty cell, or “Sign in” on a cell that needs attention — you’ll enter your PIN, open the sign-in link, and paste the code right here.</p>
3648
3648
  <div id="subMatrix"></div>
3649
3649
  </section>
3650
3650
  <section class="ph-section">
@@ -512,7 +512,13 @@ export function buildMatrixModel(poolScope, pendingScope, transient = {}) {
512
512
  const mid = a && a.machineId;
513
513
  if (!a || !a.id || !mid || offlineMachineIds.has(mid)) continue;
514
514
  const status = effectiveAccountStatus(a);
515
- cellStatus.set(`${a.id}::${mid}`, status === 'needs-reauth' ? 'needs-reauth' : (status === 'active' ? 'active' : 'other'));
515
+ const key = `${a.id}::${mid}`;
516
+ const next = status === 'needs-reauth' ? 'needs-reauth' : (status === 'active' ? 'active' : 'other');
517
+ // Pool reads may contain more than one observation for the same account/machine.
518
+ // Safety state is monotonic within a render: a later stale Active row must never
519
+ // overwrite a live identity-drift verdict.
520
+ const prior = cellStatus.get(key);
521
+ cellStatus.set(key, prior === 'needs-reauth' || next === 'needs-reauth' ? 'needs-reauth' : next);
516
522
  }
517
523
  // (accountId, machineId) in-progress, correlated on (login.id === accountId, machineId) (FD6 r3 #2).
518
524
  // The MAP carries the pending-login RECORD so the in-progress cell can render the COMPLETE
@@ -546,20 +552,22 @@ export function buildMatrixModel(poolScope, pendingScope, transient = {}) {
546
552
  if (m.offline) state = 'offline'; // whole column offline (FD6)
547
553
  else if (t && t.state === 'held') state = 'held';
548
554
  else if (t && t.state === 'cant-resolve') state = 'cant-resolve';
549
- else if (cellStatus.get(key) === 'active') state = 'active';
550
- // just-verified BRIDGES the gap between a client-observed successful enrollment and the
551
- // next pool read that shows the account active — the cell must flip to an unmistakable
552
- // verified presentation the moment the enrollment verifies (topic 29836 D4), never blink
553
- // back to a "Set up" button while the server catches up.
554
- else if (t && t.state === 'just-verified') state = 'just-verified';
555
+ // Durable pending state wins over enrollment bookkeeping after restart: the
556
+ // full flow rehydrates into its cell even if the pool row still says Active.
555
557
  // broken (D5): the server says this attempt's sign-in pane is DEAD (record ⟂ pane
556
558
  // reconciliation) — or the client just watched a code-submit refuse with pane-dead.
557
559
  // Presenting it as submittable would be a lie; it gets an explicit needs-restart
558
560
  // presentation with a working Retry (start-cell supersedes the zombie atomically).
559
- else if ((pendingLogin && pendingLogin.paneAlive === false) || (t && t.state === 'broken')) state = 'broken';
560
- else if (pendingLogin) state = 'in-progress';
561
+ else if (cellStatus.get(key) === 'needs-reauth' && pendingLogin && pendingLogin.paneAlive === false) state = 'broken';
562
+ else if (cellStatus.get(key) === 'needs-reauth' && pendingLogin) state = 'in-progress';
561
563
  else if (t && t.state === 'expired') state = 'expired';
562
564
  else if (cellStatus.get(key) === 'needs-reauth') state = 'needs-reauth';
565
+ else if (cellStatus.get(key) === 'active') state = 'active';
566
+ else if ((pendingLogin && pendingLogin.paneAlive === false) || (t && t.state === 'broken')) state = 'broken';
567
+ else if (pendingLogin) state = 'in-progress';
568
+ // just-verified bridges the short interval before the pool row becomes active.
569
+ // Once active, keep state=active and carry this transient as the highlight detail.
570
+ else if (t && t.state === 'just-verified') state = 'just-verified';
563
571
  else state = 'empty'; // → "Set up" button
564
572
  rowCells.push({
565
573
  accountId: acct.accountId, machineId: m.machineId, state,
@@ -12353,7 +12353,7 @@ export async function startServer(options) {
12353
12353
  // a 2nd account never clobbers the 1st.
12354
12354
  const { PendingLoginStore } = await import('../core/PendingLoginStore.js');
12355
12355
  const { EnrollmentWizard } = await import('../core/EnrollmentWizard.js');
12356
- const { FrameworkLoginDriver, enrollPaneSessionName } = await import('../core/FrameworkLoginDriver.js');
12356
+ const { FrameworkLoginDriver, enrollPaneSessionName, enrollmentBrowserEnv } = await import('../core/FrameworkLoginDriver.js');
12357
12357
  const DEFAULT_ENROLL_LOGIN_COMMANDS = {
12358
12358
  'claude-code': 'claude auth login',
12359
12359
  'codex-cli': 'codex login',
@@ -12402,16 +12402,16 @@ export async function startServer(options) {
12402
12402
  }
12403
12403
  return sessionManager.captureOutput(session, 120) || '';
12404
12404
  },
12405
- spawn: async ({ framework, configHome }) => {
12405
+ spawn: async ({ framework, configHome, openBrowser }) => {
12406
12406
  const tmuxPath = detectTmuxPath();
12407
12407
  if (!tmuxPath)
12408
12408
  throw new Error('tmux not available for enrollment login');
12409
12409
  const baseCmd = enrollLoginCommands[framework] ?? `${framework} login`;
12410
12410
  // env-prefix sets the per-account config home for the login process so
12411
12411
  // the credential lands in its own slot (CLAUDE_CONFIG_DIR isolation).
12412
- const cmd = configHome
12413
- ? `env CLAUDE_CONFIG_DIR=${JSON.stringify(configHome)} ${baseCmd}`
12414
- : baseCmd;
12412
+ const env = { ...(configHome ? { CLAUDE_CONFIG_DIR: configHome } : {}), ...enrollmentBrowserEnv(openBrowser) };
12413
+ const prefix = Object.entries(env).map(([k, v]) => `${k}=${JSON.stringify(v)}`).join(' ');
12414
+ const cmd = prefix ? `env ${prefix} ${baseCmd}` : baseCmd;
12415
12415
  const session = enrollPaneSessionName(framework, configHome);
12416
12416
  try {
12417
12417
  execFileSync(tmuxPath, ['kill-session', '-t', `=${session}`], { stdio: 'ignore' });