feat(session): a run picks its own size

POST /session takes an optional tier. The host starts the run at that
size, the allowance is checked against it, and the run is billed at it.
A run asked for without one runs at its box's size, as before.

The size belongs to the run rather than the box: a box is a game's
install on a machine, and the size is chosen each time it is played, so
playing bigger never means a second box and a second install.
This commit is contained in:
Wanjohi
2026-10-10 14:03:13 +03:00
parent 05a3fee87a
commit 8608506682
6 changed files with 50 additions and 6 deletions
+8 -1
View File
@@ -1,5 +1,6 @@
import { Actor } from '@nestri/core/actor';
import { Billing } from '@nestri/core/billing/index';
import { BoxTier } from '@nestri/core/box/box.sql';
import { Box } from '@nestri/core/box/index';
import { ErrorCodes, VisibleError } from '@nestri/core/error';
import { Examples } from '@nestri/core/examples';
@@ -154,6 +155,11 @@ export namespace SessionApi {
description:
'Which linked account is playing. Defaults to the one the caller signed in with',
example: Examples.Session.linkedAccountId
}),
tier: z.enum(BoxTier.enumValues).optional().meta({
description:
'The size to run at. Defaults to the size of the box; the allowance is checked against the size asked for',
example: 'md'
})
})
// Strict, so that naming hardware is a validation error rather
@@ -273,7 +279,7 @@ export namespace SessionApi {
payer && !trial
? await Billing.assertMayStart({
teamId: payer.teamId,
nextTier: payer.tier,
nextTier: body.tier ?? payer.tier,
nextHostClass: payer.hostClass
})
: null;
@@ -283,6 +289,7 @@ export namespace SessionApi {
boxId: box.id,
gameId: game.id,
linkedAccountId,
tier: body.tier,
trial
});
return c.json({ data: session, billing }, 201);
+2
View File
@@ -158,6 +158,7 @@ describe('POST /session', () => {
gameId: s.gameId,
linkedAccountId: s.owner.linkedAccountId,
state: 'requested',
tier: null,
ticket: null,
timeStarted: null,
timeStopped: null,
@@ -726,6 +727,7 @@ describe('POST /session/:id/ticket', () => {
'linkedAccountId',
'state',
'ticket',
'tier',
'timeStarted',
'timeStopRequested',
'timeStopped',