instar 1.3.401 → 1.3.403

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.
@@ -14395,6 +14395,16 @@ export function createRoutes(ctx) {
14395
14395
  const accounts = ctx.subscriptionPool.list();
14396
14396
  res.json({ enabled: true, count: accounts.length, accounts });
14397
14397
  });
14398
+ // P2.1 — the "Pending Logins" surface (the phone/dashboard panel). MUST be
14399
+ // registered before GET /subscription-pool/:id, or the param route shadows the
14400
+ // literal "pending-logins" segment. 200 { enabled:false } when unwired.
14401
+ router.get('/subscription-pool/pending-logins', (_req, res) => {
14402
+ if (!ctx.enrollmentWizard) {
14403
+ res.json({ enabled: false, logins: [] });
14404
+ return;
14405
+ }
14406
+ res.json({ enabled: true, logins: ctx.enrollmentWizard.pending() });
14407
+ });
14398
14408
  router.get('/subscription-pool/:id', (req, res) => {
14399
14409
  if (!ctx.subscriptionPool) {
14400
14410
  res.status(404).json({ error: 'SubscriptionPool not configured' });
@@ -14527,6 +14537,55 @@ export function createRoutes(ctx) {
14527
14537
  res.status(500).json({ error: err instanceof Error ? err.message : 'swap failed' });
14528
14538
  }
14529
14539
  });
14540
+ // ── Subscription enrollment (P2.1 — mobile-first enrollment wizard) ──
14541
+ // Assist a new-account login from the phone: drive the framework's login,
14542
+ // surface the PUBLIC code/URL (never a token), and auto-reissue an expired
14543
+ // code. List/sweep routes answer 200 { enabled:false } when the wizard is
14544
+ // unwired (dark) — never 503.
14545
+ router.post('/subscription-pool/enroll', async (req, res) => {
14546
+ if (!ctx.enrollmentWizard) {
14547
+ res.json({ enabled: false, started: false, reason: 'enrollment wizard not configured' });
14548
+ return;
14549
+ }
14550
+ const { id, label, provider, framework, kind, configHome } = req.body ?? {};
14551
+ if (!id || !label || !provider || !framework) {
14552
+ res.status(400).json({ error: 'id, label, provider, and framework are required' });
14553
+ return;
14554
+ }
14555
+ try {
14556
+ const login = await ctx.enrollmentWizard.start({ id, label, provider, framework, kind, configHome });
14557
+ res.status(201).json({ enabled: true, login });
14558
+ }
14559
+ catch (err) {
14560
+ res.status(500).json({ error: err instanceof Error ? err.message : 'failed to start enrollment' });
14561
+ }
14562
+ });
14563
+ // Single-segment path → no collision with /enroll/:id/complete (3 segments).
14564
+ router.post('/subscription-pool/enroll/reissue-expired', async (_req, res) => {
14565
+ if (!ctx.enrollmentWizard) {
14566
+ res.json({ enabled: false, reissued: [] });
14567
+ return;
14568
+ }
14569
+ try {
14570
+ const reissued = await ctx.enrollmentWizard.reissueExpired();
14571
+ res.json({ enabled: true, reissued });
14572
+ }
14573
+ catch (err) {
14574
+ res.status(500).json({ error: err instanceof Error ? err.message : 'reissue sweep failed' });
14575
+ }
14576
+ });
14577
+ router.post('/subscription-pool/enroll/:id/complete', (req, res) => {
14578
+ if (!ctx.enrollmentWizard) {
14579
+ res.json({ enabled: false, completed: false, reason: 'enrollment wizard not configured' });
14580
+ return;
14581
+ }
14582
+ const login = ctx.enrollmentWizard.complete(req.params.id);
14583
+ if (!login) {
14584
+ res.status(404).json({ error: `pending login ${req.params.id} not found` });
14585
+ return;
14586
+ }
14587
+ res.json({ enabled: true, login });
14588
+ });
14530
14589
  // ── Episodic Memory (Activity Sentinel) ──────────────────────────
14531
14590
  router.get('/episodes/stats', (req, res) => {
14532
14591
  if (!ctx.activitySentinel) {