[ai] fix stub endpoint

This commit is contained in:
Devin Zuczek
2026-08-20 18:27:42 -04:00
parent 038e3b3c50
commit e47ce58db3
3 changed files with 41 additions and 30 deletions
+20 -16
View File
@@ -235,28 +235,32 @@ const app = new Hono<App>()
}
)
// Whether the caller may use Maker AI at all. Always false: no model runs behind this
// worker, so the honest answer is that the feature isn't available — and false is what
// leaves the creation UI in its normal state rather than offering a tool that can't
// work. (The balances below are still served: the client reads its usage meter
// separately, and a server that bills nothing has spent nothing.)
// Whether the caller may use Maker AI at all. Granted, like the Roomie budget reads and
// unlike the Game AI checks: this is a gate, not a model call, and refusing it hides the
// feature outright. (The balances below are still zeroed: the client reads its usage
// meter separately, and a server that bills nothing has spent nothing.)
//
// The body is a BARE JSON `false` — not an envelope, unlike the Game AI refusal and the
// Roomie access check on either side of it. `econ`'s
// `/api/makerai/checkfreetrialeligibility` answers the same bare shape.
// The envelope is its own shape again — PascalCase `Success`/`Error` beside a snake_case
// `error_id`, which is neither the Game AI refusal's all-lowercase body nor the Roomie
// check's `{ success, error_id, error, value }`. Reproduced as the reference sends it;
// the mixed casing is not a typo to tidy up.
.get(
'/makerai/user/access',
describeRoute({
tags: ['Maker AI'],
summary: 'May the caller use Maker AI?',
description: [
'Asked before the client offers Maker AI. Always `false` — no model runs behind this',
'worker. The body is a bare JSON boolean, not the `{ success, error, value }` envelope',
'the neighbouring checks answer with.',
'Asked before the client offers Maker AI. Always granted — the gate is about',
'entitlement, not capacity, and nothing here meters what Maker AI would cost.',
'',
'`roomInstanceSpecificCheck` (the client sends .NETs `False`) is accepted and ignored:',
'it asks whether the check is about the instance the player is standing in rather than',
'the account, and the answer is the same either way. The token is still validated first.',
'The envelope carries PascalCase `Success`/`Error` next to a snake_case `error_id`,',
'which matches neither neighbour on this worker. That mix is what the reference sends;',
'it is not an inconsistency to clean up.',
'',
'`roomInstanceSpecificCheck` (the client sends .NETs `True`/`False`) is accepted and',
'ignored: it asks whether the check is about the instance the player is standing in',
'rather than the account, and the answer is the same either way. The token is still',
'validated first.',
].join(' '),
security: AUTHED,
parameters: [
@@ -266,7 +270,7 @@ const app = new Hono<App>()
),
],
responses: {
200: json(MakerAiAccessResponse, 'Always `false`'),
200: json(MakerAiAccessResponse, 'Always granted'),
401: UNAUTHORIZED_RESPONSE,
},
}),
@@ -274,7 +278,7 @@ const app = new Hono<App>()
const id = await authedId(c)
if (id === null) return unauthorized(c)
return c.json(false)
return c.json({ Success: true, Error: null, error_id: null })
}
)
+10 -5
View File
@@ -113,12 +113,17 @@ export const GameAiSpendSummaryDenied = GameAiAccessDenied.extend({
})
/**
* `GET /makerai/user/access` — a BARE JSON boolean, not an envelope and not a `{ value }`
* wrapper. The whole body is the answer.
* `GET /makerai/user/access` — always granted, in an envelope that belongs to this endpoint
* alone: PascalCase `Success`/`Error` beside a snake_case `error_id`, and no `value` slot.
* It is neither the Game AI refusal's all-lowercase body nor the Roomie check's
* `{ success, error_id, error, value }`. Reproduced as the reference sends it — the casing
* mix is not a typo to normalise.
*/
export const MakerAiAccessResponse = z
.boolean()
.describe('Whether the caller may use Maker AI; always false — no model runs here')
export const MakerAiAccessResponse = z.object({
Success: z.boolean().describe('Whether the caller may use Maker AI. Always true'),
Error: z.null().describe('The failure message. Null — the check always passes'),
error_id: z.null().describe('The failure code. Null — the check always passes'),
})
/**
* Maker AI's dollar balances. A FLAT body — no `{ success, error, value }` envelope — and
+11 -9
View File
@@ -175,29 +175,31 @@ describe('GET /roomieai/user/facts', () => {
})
describe('GET /makerai/user/access', () => {
// Always false — nothing here runs a model. The body is the boolean itself, not an
// envelope, matching econ's `/api/makerai/checkfreetrialeligibility`.
it('refuses access with a bare false', async () => {
const res = await SELF.fetch(`${ORIGIN}/makerai/user/access?roomInstanceSpecificCheck=False`, {
// Granted, and pinned whole: the casing is mixed on purpose (PascalCase `Success`/`Error`
// beside a snake_case `error_id`) and matches neither neighbour on this worker, so a
// "consistency" edit has to fail here rather than on the client.
it('grants access', async () => {
const res = await SELF.fetch(`${ORIGIN}/makerai/user/access?roomInstanceSpecificCheck=True`, {
headers: await bearer(),
})
expect(res.status).toBe(200)
expect(res.headers.get('content-type')).toContain('application/json')
expect(await res.text()).toBe('false')
expect(await res.json()).toEqual({ Success: true, Error: null, error_id: null })
})
it('answers the same without the query param', async () => {
// `roomInstanceSpecificCheck` is ignored, so its presence, absence and value change
// nothing.
const granted = { Success: true, Error: null, error_id: null }
const res = await SELF.fetch(`${ORIGIN}/makerai/user/access`, { headers: await bearer() })
expect(await res.text()).toBe('false')
const trueCheck = await SELF.fetch(
`${ORIGIN}/makerai/user/access?roomInstanceSpecificCheck=True`,
expect(await res.json()).toEqual(granted)
const falseCheck = await SELF.fetch(
`${ORIGIN}/makerai/user/access?roomInstanceSpecificCheck=False`,
{
headers: await bearer(),
}
)
expect(await trueCheck.text()).toBe('false')
expect(await falseCheck.json()).toEqual(granted)
})
it('401s without a bearer token', async () => {