instar 1.3.623 → 1.3.624

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.
@@ -271,6 +271,9 @@ export function renderPendingLogins(doc, target, logins, now = Date.now()) {
271
271
  }
272
272
  for (const l of logins) {
273
273
  const row = el(doc, 'div', 'sub-pending');
274
+ // Non-sensitive identifiers for the code-submit handler (never a credential).
275
+ row.setAttribute('data-login-id', sanitizeForDisplay(l && l.id, 'label'));
276
+ if (l && (l.machineId || l.machineNickname)) row.setAttribute('data-machine-id', sanitizeForDisplay(l.machineId, 'label'));
274
277
 
275
278
  // Lead with a plain-language headline naming what to do + where (machine).
276
279
  const machine = sanitizeForDisplay(l && (l.machineNickname || l.machineId), 'label');
@@ -300,6 +303,22 @@ export function renderPendingLogins(doc, target, logins, now = Date.now()) {
300
303
  row.appendChild(el(doc, 'div', 'sub-pending-code', `Code: ${sanitizeForDisplay(l.userCode, 'code')}`));
301
304
  }
302
305
 
306
+ // url-code-paste flow (Claude): after signing in, the provider hands the operator a
307
+ // CODE to paste back. Give them a field for it right here — so it goes straight to the
308
+ // machine doing the login (off-chat), not relayed by hand. (ws52-code-paste-back)
309
+ if (l && l.kind === 'url-code-paste') {
310
+ row.appendChild(el(doc, 'div', 'sub-pending-codehint', 'After you sign in, paste the code the page gives you here:'));
311
+ const input = doc.createElement('input');
312
+ input.setAttribute('type', 'text');
313
+ input.setAttribute('class', 'sub-pending-code-input');
314
+ input.setAttribute('placeholder', 'Paste your sign-in code');
315
+ input.setAttribute('autocomplete', 'off');
316
+ row.appendChild(input);
317
+ const submit = el(doc, 'button', 'sub-pending-code-submit', 'Submit code');
318
+ submit.setAttribute('data-submit-code', '1');
319
+ row.appendChild(submit);
320
+ }
321
+
303
322
  // One short secondary line: the TTL, and the flow notice only if present (trimmed).
304
323
  const ttl = l && l.ttlExpiresAt ? countdown(l.ttlExpiresAt, now) : '';
305
324
  if (ttl) row.appendChild(el(doc, 'div', 'sub-pending-ttl', `Link expires in ${ttl}`));
@@ -327,6 +346,7 @@ const URLS = {
327
346
  // operator's single dashboard (WS5.2 seam #3) — without it the device-code link never appears here.
328
347
  pending: '/subscription-pool/pending-logins?scope=pool',
329
348
  inUse: '/subscription-pool/in-use',
349
+ submitCode: '/subscription-pool/follow-me/submit-code', // POST — paste-back the sign-in code (ws52-code-paste-back)
330
350
  scan: '/subscription-pool/follow-me/scan', // POST — follow-me consent offers (one-tap card)
331
351
  issue: '/mandate/issue-for-machine', // POST (PIN-gated) — Approve issues the mandate
332
352
  };
@@ -404,6 +424,7 @@ export function createController(opts) {
404
424
  const inUseAccountId = inUseBody && inUseBody.activeAccountId ? inUseBody.activeAccountId : null;
405
425
  renderAccounts(doc, els.accounts, accounts, now(), inUseAccountId);
406
426
  renderPendingLogins(doc, els.pending, logins, now());
427
+ wireCodeSubmit();
407
428
  // The one-tap follow-me Approve card(s) — rendered into els.followMe from the scan offers
408
429
  // (ws52-operator-tap-not-text Part A). Silent when there are none. The Approve click is wired
409
430
  // once (delegated) so re-renders never stack listeners.
@@ -456,6 +477,57 @@ export function createController(opts) {
456
477
  s.textContent = text;
457
478
  }
458
479
 
480
+ // Delegated, wired ONCE: a "Submit code" tap on a pending-login row reads the pasted
481
+ // sign-in code and POSTs it (with the login's id + machineId) to the fronting relay,
482
+ // which carries it to the machine doing the login (off-chat). The code is never stored
483
+ // client-side beyond the input; it's cleared on success. (ws52-code-paste-back)
484
+ function wireCodeSubmit() {
485
+ if (state.codeSubmitWired || !els.pending) return;
486
+ state.codeSubmitWired = true;
487
+ els.pending.addEventListener('click', (ev) => {
488
+ const btn = ev.target && ev.target.closest ? ev.target.closest('[data-submit-code]') : null;
489
+ if (!btn || !els.pending.contains(btn)) return;
490
+ const row = btn.closest('.sub-pending');
491
+ if (!row) return;
492
+ const input = row.querySelector('.sub-pending-code-input');
493
+ const code = input ? input.value.trim() : '';
494
+ const id = row.getAttribute('data-login-id');
495
+ const machineId = row.getAttribute('data-machine-id') || undefined;
496
+ if (!code) { setRowStatus(row, 'Paste the code the sign-in page gave you, then tap Submit.'); return; }
497
+ if (!id) { setRowStatus(row, 'Couldn’t identify this login — please refresh.'); return; }
498
+ setRowStatus(row, 'Sending your code…');
499
+ btn.setAttribute('disabled', '1');
500
+ void (async () => {
501
+ try {
502
+ const r = await postJson(URLS.submitCode, { machineId, id, code });
503
+ if (r.ok && r.json && r.json.outcome === 'validated') {
504
+ setRowStatus(row, 'Done — this machine is set up with the account.');
505
+ if (input) input.value = '';
506
+ } else if (r.ok && r.json && r.json.outcome === 'submitted') {
507
+ setRowStatus(row, 'Code sent — finishing sign-in…');
508
+ if (input) input.value = '';
509
+ } else if (r.ok && r.json && r.json.outcome === 'held') {
510
+ setRowStatus(row, 'Signed in, but the account didn’t match what was approved — check with the operator.');
511
+ btn.removeAttribute('disabled');
512
+ } else {
513
+ const msg = (r.json && (r.json.error || r.json.reason)) ? (r.json.error || r.json.reason) : `failed (${r.status})`;
514
+ setRowStatus(row, `Couldn’t submit the code: ${msg}`);
515
+ btn.removeAttribute('disabled');
516
+ }
517
+ } catch (e) {
518
+ setRowStatus(row, 'Couldn’t reach the server — try again.');
519
+ btn.removeAttribute('disabled');
520
+ }
521
+ })();
522
+ });
523
+ }
524
+
525
+ function setRowStatus(row, text) {
526
+ let s = row.querySelector('.sub-pending-status');
527
+ if (!s) { s = el(doc, 'div', 'sub-pending-status', ''); row.appendChild(s); }
528
+ s.textContent = text;
529
+ }
530
+
459
531
  function reschedule() {
460
532
  if (!state.active) return;
461
533
  if (state.timerId != null) cancel(state.timerId);
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAkH7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAy4CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CA8eN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAk8etE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/commands/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAoCH,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAS3D,OAAO,EAAE,eAAe,EAAiC,MAAM,iCAAiC,CAAC;AAwBjG,OAAO,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAGvD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAkH7D,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAsBtD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAC1C,OAAO,CAUT;AAyID,UAAU,YAAY;IACpB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;2DACuD;IACvD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAy4CD,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,eAAe,EACzB,cAAc,EAAE,cAAc,EAC9B,YAAY,CAAC,EAAE,YAAY,EAC3B,WAAW,CAAC,EAAE,WAAW,EACzB,WAAW,CAAC,EAAE,WAAW,EACzB,iBAAiB,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,EAGvE,UAAU,CAAC,EAAE,MAAM,OAAO,8BAA8B,EAAE,WAAW,GAAG,IAAI,EAK5E,qBAAqB,CAAC,EAAE,MAAM,OAAO,gCAAgC,EAAE,kBAAkB,GAAG,IAAI,EAKhG,mBAAmB,CAAC,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACpD,IAAI,CA8eN;AA2lBD,wBAAsB,WAAW,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAi8etE;AAED,wBAAsB,UAAU,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAsDzE;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE;IAAE,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAuD5E"}
@@ -9874,7 +9874,7 @@ export async function startServer(options) {
9874
9874
  // a 2nd account never clobbers the 1st.
9875
9875
  const { PendingLoginStore } = await import('../core/PendingLoginStore.js');
9876
9876
  const { EnrollmentWizard } = await import('../core/EnrollmentWizard.js');
9877
- const { FrameworkLoginDriver } = await import('../core/FrameworkLoginDriver.js');
9877
+ const { FrameworkLoginDriver, enrollPaneSessionName } = await import('../core/FrameworkLoginDriver.js');
9878
9878
  const DEFAULT_ENROLL_LOGIN_COMMANDS = {
9879
9879
  'claude-code': 'claude auth login',
9880
9880
  'codex-cli': 'codex login',
@@ -9933,8 +9933,7 @@ export async function startServer(options) {
9933
9933
  const cmd = configHome
9934
9934
  ? `env CLAUDE_CONFIG_DIR=${JSON.stringify(configHome)} ${baseCmd}`
9935
9935
  : baseCmd;
9936
- const slug = (configHome ?? framework).replace(/[^a-zA-Z0-9]+/g, '-').slice(-24);
9937
- const session = `instar-enroll-${framework}-${slug}`;
9936
+ const session = enrollPaneSessionName(framework, configHome);
9938
9937
  try {
9939
9938
  execFileSync(tmuxPath, ['kill-session', '-t', `=${session}`], { stdio: 'ignore' });
9940
9939
  }