instar 1.3.1236 → 1.3.1237

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 (37) hide show
  1. package/dashboard/subscriptions.js +87 -8
  2. package/dist/coordination/AccountFollowMeMandateDelivery.d.ts +6 -0
  3. package/dist/coordination/AccountFollowMeMandateDelivery.d.ts.map +1 -1
  4. package/dist/coordination/AccountFollowMeMandateDelivery.js +16 -2
  5. package/dist/coordination/AccountFollowMeMandateDelivery.js.map +1 -1
  6. package/dist/coordination/DeliveredMandateStore.d.ts +4 -0
  7. package/dist/coordination/DeliveredMandateStore.d.ts.map +1 -1
  8. package/dist/coordination/DeliveredMandateStore.js +16 -2
  9. package/dist/coordination/DeliveredMandateStore.js.map +1 -1
  10. package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
  11. package/dist/core/PostUpdateMigrator.js +12 -0
  12. package/dist/core/PostUpdateMigrator.js.map +1 -1
  13. package/dist/data/standards-guard-index.json +1 -1
  14. package/dist/data/standards-guard-index.meta.json +2 -2
  15. package/dist/data/standards-registry.meta.json +1 -1
  16. package/dist/scaffold/templates.d.ts.map +1 -1
  17. package/dist/scaffold/templates.js +1 -0
  18. package/dist/scaffold/templates.js.map +1 -1
  19. package/dist/server/AgentServer.d.ts.map +1 -1
  20. package/dist/server/AgentServer.js +12 -1
  21. package/dist/server/AgentServer.js.map +1 -1
  22. package/dist/server/CapabilityIndex.d.ts.map +1 -1
  23. package/dist/server/CapabilityIndex.js +2 -0
  24. package/dist/server/CapabilityIndex.js.map +1 -1
  25. package/dist/server/routes.d.ts +5 -0
  26. package/dist/server/routes.d.ts.map +1 -1
  27. package/dist/server/routes.js +240 -7
  28. package/dist/server/routes.js.map +1 -1
  29. package/package.json +2 -1
  30. package/src/data/builtin-manifest.json +64 -64
  31. package/src/data/standards-guard-index.json +1 -1
  32. package/src/data/standards-guard-index.meta.json +2 -2
  33. package/src/data/standards-registry.meta.json +1 -1
  34. package/src/scaffold/templates.ts +1 -0
  35. package/upgrades/1.3.1237.md +27 -0
  36. package/upgrades/relogin-pool-one-click.eli16.md +9 -0
  37. package/upgrades/side-effects/relogin-pool-one-click.md +86 -0
@@ -29426,8 +29426,8 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
29426
29426
  });
29427
29427
  // Assisted subscription re-login: one verified approval, then the service
29428
29428
  // owns the bounded autonomous flow. The store exposes closed metadata only.
29429
- router.get('/subscription-relogin', (req, res) => {
29430
- if (!ctx.subscriptionRelogin) {
29429
+ router.get('/subscription-relogin', async (req, res) => {
29430
+ if (!ctx.subscriptionRelogin && req.query.scope !== 'pool') {
29431
29431
  res.status(503).json({ enabled: false, error: 'subscription re-login is not configured' });
29432
29432
  return;
29433
29433
  }
@@ -29435,10 +29435,66 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
29435
29435
  const state = typeof req.query.state === 'string' ? req.query.state : undefined;
29436
29436
  const accountId = typeof req.query.accountId === 'string' ? req.query.accountId : undefined;
29437
29437
  const limit = typeof req.query.limit === 'string' ? Number(req.query.limit) : undefined;
29438
- res.json({ enabled: true, episodes: ctx.subscriptionRelogin.store.list({
29439
- state: state,
29440
- accountId, limit,
29441
- }) });
29438
+ const localEpisodes = ctx.subscriptionRelogin?.store.list({
29439
+ state: state,
29440
+ accountId, limit,
29441
+ }) ?? [];
29442
+ if (req.query.scope !== 'pool') {
29443
+ res.json({ enabled: true, episodes: localEpisodes });
29444
+ return;
29445
+ }
29446
+ const selfMachineId = ctx.meshSelfId ?? ctx.config.machineId ?? 'local';
29447
+ const selfMachineNickname = ctx.machinePoolRegistry?.getCapacity(selfMachineId)?.nickname;
29448
+ const failed = [];
29449
+ const unavailable = [];
29450
+ const episodes = localEpisodes
29451
+ .filter((episode) => {
29452
+ if (episode.machineId === selfMachineId)
29453
+ return true;
29454
+ failed.push({ machineId: selfMachineId, error: 'episode-machine-mismatch' });
29455
+ return false;
29456
+ })
29457
+ .map((episode) => ({ ...episode, machineNickname: selfMachineNickname, remote: false }));
29458
+ if (!ctx.subscriptionRelogin)
29459
+ unavailable.push({ machineId: selfMachineId, reason: 'feature-unavailable' });
29460
+ const query = new URLSearchParams();
29461
+ if (state)
29462
+ query.set('state', state);
29463
+ if (accountId)
29464
+ query.set('accountId', accountId);
29465
+ if (limit !== undefined)
29466
+ query.set('limit', String(limit));
29467
+ const suffix = query.size > 0 ? `?${query.toString()}` : '';
29468
+ await Promise.all((ctx.resolvePeerUrls?.() ?? []).map(async (peer) => {
29469
+ try {
29470
+ const response = await fetch(`${peer.url}/subscription-relogin${suffix}`, {
29471
+ headers: { Authorization: `Bearer ${ctx.config.authToken}` },
29472
+ signal: AbortSignal.timeout(5_000),
29473
+ });
29474
+ if (!response.ok) {
29475
+ if (response.status === 503) {
29476
+ unavailable.push({ machineId: peer.machineId, reason: 'feature-unavailable' });
29477
+ return;
29478
+ }
29479
+ failed.push({ machineId: peer.machineId, error: response.status === 401 || response.status === 403 ? 'unauthorized' : 'error' });
29480
+ return;
29481
+ }
29482
+ const body = await response.json();
29483
+ if (body.enabled === false) {
29484
+ unavailable.push({ machineId: peer.machineId, reason: 'feature-unavailable' });
29485
+ return;
29486
+ }
29487
+ const nickname = ctx.machinePoolRegistry?.getCapacity(peer.machineId)?.nickname;
29488
+ for (const episode of body.episodes ?? []) {
29489
+ episodes.push({ ...episode, machineId: peer.machineId, machineNickname: nickname, remote: true });
29490
+ }
29491
+ }
29492
+ catch (error) {
29493
+ const name = error instanceof Error ? error.name : '';
29494
+ failed.push({ machineId: peer.machineId, error: name === 'TimeoutError' || name === 'AbortError' ? 'timeout' : 'unreachable' });
29495
+ }
29496
+ }));
29497
+ res.json({ enabled: true, episodes, scope: 'pool', pool: { failed, unavailable } });
29442
29498
  }
29443
29499
  catch (error) {
29444
29500
  res.status(400).json({ error: error instanceof Error ? error.message : 'invalid re-login query' });
@@ -29492,6 +29548,169 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
29492
29548
  res.status(status).json({ error: message });
29493
29549
  }
29494
29550
  });
29551
+ // Target-local point of use for a one-click account×machine repair. The
29552
+ // fronting dashboard has already verified recent operator presence and issued
29553
+ // the same exact-bounds re-mint mandate used by Account Follow-Me. This route
29554
+ // independently re-verifies that mandate for this account and this machine.
29555
+ router.post('/subscription-relogin/:episodeId/approve-with-mandate', async (req, res) => {
29556
+ if (!ctx.subscriptionRelogin || !ctx.coordination?.gate) {
29557
+ res.status(503).json({ enabled: false, error: 'subscription re-login or mandate gate is not configured' });
29558
+ return;
29559
+ }
29560
+ const mandateId = typeof req.body?.mandateId === 'string' ? req.body.mandateId : '';
29561
+ const accountId = typeof req.body?.accountId === 'string' ? req.body.accountId : '';
29562
+ const repairAction = typeof req.body?.action === 'string' ? req.body.action : 'approve';
29563
+ const targetMachineId = ctx.meshSelfId ?? ctx.config.machineId ?? 'local';
29564
+ if (!mandateId || !accountId || !['approve', 'retry', 'cancel'].includes(repairAction)) {
29565
+ res.status(400).json({ error: 'mandateId, accountId, and a valid action are required' });
29566
+ return;
29567
+ }
29568
+ const episode = ctx.subscriptionRelogin.store.get(req.params.episodeId);
29569
+ if (!episode) {
29570
+ res.status(404).json({ error: 're-login episode not found' });
29571
+ return;
29572
+ }
29573
+ if (episode.accountId !== accountId || episode.machineId !== targetMachineId) {
29574
+ res.status(409).json({ error: 're-login episode does not match the authorized account and machine' });
29575
+ return;
29576
+ }
29577
+ const actionAllowed = repairAction === 'approve' ? episode.state === 'suggested'
29578
+ : repairAction === 'retry' ? episode.state === 'failed'
29579
+ : !['succeeded', 'refused', 'cancelled'].includes(episode.state);
29580
+ if (!actionAllowed) {
29581
+ res.status(409).json({ error: 're-login episode is not eligible for this action' });
29582
+ return;
29583
+ }
29584
+ const verdict = ctx.coordination.gate.evaluate({
29585
+ action: 'account-follow-me',
29586
+ params: { accountId, targetMachineId, mechanism: 're-mint', episodeId: episode.id,
29587
+ inputDigest: episode.inputDigest, repairAction },
29588
+ agentFp: resolveAgentFingerprint(ctx), mandateId,
29589
+ });
29590
+ if (verdict.decision !== 'allow') {
29591
+ const delivered = ctx.verifyDeliveredMandate?.(mandateId);
29592
+ if (!delivered || delivered.accountId !== accountId || delivered.targetMachineId !== targetMachineId ||
29593
+ delivered.mechanism !== 're-mint' || delivered.episodeId !== episode.id ||
29594
+ delivered.inputDigest !== episode.inputDigest || delivered.repairAction !== repairAction) {
29595
+ res.status(403).json({ error: 'subscription re-login not authorized', reason: verdict.reason });
29596
+ return;
29597
+ }
29598
+ if (!ctx.consumeDeliveredMandate?.(mandateId)) {
29599
+ res.status(403).json({ error: 'subscription re-login authorization was already consumed' });
29600
+ return;
29601
+ }
29602
+ }
29603
+ else {
29604
+ const consumed = ctx.coordination.store.revoke(mandateId, 'consumed by subscription re-login');
29605
+ if (!consumed?.revoked) {
29606
+ res.status(403).json({ error: 'subscription re-login authorization could not be consumed' });
29607
+ return;
29608
+ }
29609
+ }
29610
+ try {
29611
+ const result = repairAction === 'approve' ? await ctx.subscriptionRelogin.approve(episode.id)
29612
+ : repairAction === 'retry' ? await ctx.subscriptionRelogin.retry?.(episode.id)
29613
+ : await ctx.subscriptionRelogin.cancel(episode.id);
29614
+ if (!result)
29615
+ throw new Error('re-login action is not configured');
29616
+ res.status(repairAction === 'cancel' ? 200 : 202).json({ enabled: true, accepted: true, episode: result });
29617
+ }
29618
+ catch (error) {
29619
+ const message = error instanceof Error ? error.message : 're-login approval failed';
29620
+ res.status(/mismatch|approvable|conflict|closed|expired/.test(message) ? 409 : 500).json({ error: message });
29621
+ }
29622
+ });
29623
+ // Fronting one-click route: recent dashboard unlock → exact account/machine
29624
+ // mandate → signed mesh delivery → target-local point-of-use verification.
29625
+ router.post('/subscription-relogin/repair-cell', async (req, res) => {
29626
+ if (!ctx.coordination) {
29627
+ res.status(503).json({ error: 'repair coordination is not configured' });
29628
+ return;
29629
+ }
29630
+ if (!requireReloginOperatorPin(req, res))
29631
+ return;
29632
+ const accountId = typeof req.body?.accountId === 'string' ? req.body.accountId.trim() : '';
29633
+ const machineId = typeof req.body?.machineId === 'string' ? req.body.machineId.trim() : '';
29634
+ const episodeId = typeof req.body?.episodeId === 'string' ? req.body.episodeId.trim() : '';
29635
+ const repairAction = typeof req.body?.action === 'string' ? req.body.action : 'approve';
29636
+ if (!accountId || !machineId || !episodeId || !['approve', 'retry', 'cancel'].includes(repairAction)) {
29637
+ res.status(400).json({ error: 'accountId, machineId, episodeId, and a valid action are required' });
29638
+ return;
29639
+ }
29640
+ const selfMachineId = ctx.meshSelfId ?? ctx.config.machineId ?? 'local';
29641
+ const isSelf = machineId === selfMachineId;
29642
+ const peer = isSelf ? null : (ctx.resolvePeerUrls?.() ?? []).find((candidate) => candidate.machineId === machineId);
29643
+ if (!isSelf && !peer) {
29644
+ res.status(502).json({ error: 'target machine is not reachable right now', retryable: true });
29645
+ return;
29646
+ }
29647
+ const base = isSelf ? `http://${ctx.config.host || '127.0.0.1'}:${ctx.config.port}` : peer.url;
29648
+ let targetEpisode = null;
29649
+ if (isSelf) {
29650
+ targetEpisode = ctx.subscriptionRelogin?.store.get(episodeId) ?? null;
29651
+ }
29652
+ else {
29653
+ try {
29654
+ const response = await fetch(`${base}/subscription-relogin/${encodeURIComponent(episodeId)}/events`, {
29655
+ headers: { Authorization: `Bearer ${ctx.config.authToken}` }, signal: AbortSignal.timeout(5_000),
29656
+ });
29657
+ if (response.ok)
29658
+ targetEpisode = (await response.json()).episode ?? null;
29659
+ }
29660
+ catch { /* typed unreachable response below */ }
29661
+ }
29662
+ if (!targetEpisode) {
29663
+ res.status(isSelf && !ctx.subscriptionRelogin ? 503 : 404).json({ error: 'target repair episode is unavailable' });
29664
+ return;
29665
+ }
29666
+ const targetActionAllowed = repairAction === 'approve' ? targetEpisode.state === 'suggested'
29667
+ : repairAction === 'retry' ? targetEpisode.state === 'failed'
29668
+ : !['succeeded', 'refused', 'cancelled'].includes(targetEpisode.state);
29669
+ if (targetEpisode.accountId !== accountId || targetEpisode.machineId !== machineId || !targetActionAllowed) {
29670
+ res.status(409).json({ error: 'target repair episode does not match this one-click approval' });
29671
+ return;
29672
+ }
29673
+ let mandateId;
29674
+ try {
29675
+ const mandate = ctx.coordination.store.issue({
29676
+ scope: 'subscription-relogin',
29677
+ agents: [resolveAgentFingerprint(ctx), resolveAgentFingerprint(ctx)],
29678
+ authorities: [{ action: 'account-follow-me', bounds: {
29679
+ accountId, targetMachineId: machineId, mechanism: 're-mint', episodeId,
29680
+ inputDigest: targetEpisode.inputDigest, repairAction,
29681
+ } }],
29682
+ author: 'justin', expiresAt: new Date(Date.now() + 15 * 60_000).toISOString(),
29683
+ });
29684
+ mandateId = mandate.id;
29685
+ if (!isSelf) {
29686
+ if (!ctx.packageMandateForDelivery || !ctx.deliverMandateToMachine) {
29687
+ res.status(503).json({ error: 'cross-machine authorization delivery unavailable' });
29688
+ return;
29689
+ }
29690
+ const outcome = await ctx.deliverMandateToMachine({ targetMachineId: machineId, portable: ctx.packageMandateForDelivery(mandate) });
29691
+ if (!outcome.ok) {
29692
+ res.status(502).json({ error: 'could not deliver repair authorization to the target machine', retryable: true });
29693
+ return;
29694
+ }
29695
+ }
29696
+ }
29697
+ catch (error) {
29698
+ res.status(500).json({ error: error instanceof Error ? error.message : 'repair authorization failed' });
29699
+ return;
29700
+ }
29701
+ try {
29702
+ const response = await fetch(`${base}/subscription-relogin/${encodeURIComponent(episodeId)}/approve-with-mandate`, {
29703
+ method: 'POST',
29704
+ headers: { Authorization: `Bearer ${ctx.config.authToken}`, 'Content-Type': 'application/json' },
29705
+ body: JSON.stringify({ mandateId, accountId, action: repairAction }), signal: AbortSignal.timeout(15_000),
29706
+ });
29707
+ const body = await response.json().catch(() => ({}));
29708
+ res.status(response.status).json(body);
29709
+ }
29710
+ catch {
29711
+ res.status(502).json({ error: 'could not reach the machine doing the repair', retryable: true });
29712
+ }
29713
+ });
29495
29714
  router.post('/subscription-relogin/:episodeId/cancel', async (req, res) => {
29496
29715
  if (!ctx.subscriptionRelogin) {
29497
29716
  res.status(503).json({ enabled: false, error: 'subscription re-login is not configured' });
@@ -30042,6 +30261,17 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
30042
30261
  }
30043
30262
  const { mandateId, accountId } = b;
30044
30263
  const targetMachineId = ctx.meshSelfId ?? ctx.config.machineId ?? 'local';
30264
+ // Authority separation: assisted-relogin mandates intentionally reuse the
30265
+ // signed delivery transport, but they MUST NOT authorize generic enrollment.
30266
+ // Reject any local mandate carrying the repair-only binding fields before
30267
+ // consulting the generic gate (which evaluates only the generic subset).
30268
+ const localMandate = ctx.coordination.store?.get?.(mandateId);
30269
+ const localAuthority = localMandate?.authorities?.find((authority) => authority.action === 'account-follow-me');
30270
+ const localBounds = (localAuthority?.bounds ?? {});
30271
+ if (localBounds.episodeId !== undefined || localBounds.inputDigest !== undefined || localBounds.repairAction !== undefined) {
30272
+ res.status(403).json({ error: 'repair-only mandate cannot authorize account enrollment' });
30273
+ return;
30274
+ }
30045
30275
  // THE AUTHORIZATION (deny-by-default): consult the operator mandate for THIS exact
30046
30276
  // (account, this machine, mechanism). A non-allow verdict STOPS — UNLESS a cross-machine
30047
30277
  // DELIVERED mandate authorizes it (the one-dashboard path, R4a). Never work around a deny.
@@ -30062,7 +30292,10 @@ document.getElementById('mcpForm').addEventListener('submit', async function (e)
30062
30292
  const deliveredOk = !!deliveredBounds &&
30063
30293
  deliveredBounds.accountId === accountId &&
30064
30294
  deliveredBounds.targetMachineId === targetMachineId &&
30065
- deliveredBounds.mechanism === 're-mint';
30295
+ deliveredBounds.mechanism === 're-mint' &&
30296
+ deliveredBounds.episodeId === undefined &&
30297
+ deliveredBounds.inputDigest === undefined &&
30298
+ deliveredBounds.repairAction === undefined;
30066
30299
  if (!deliveredOk) {
30067
30300
  res.status(403).json({ error: 'account follow-me not authorized', reason: verdict.reason });
30068
30301
  return;