mirror of
https://github.com/djdevin/recflare.git
synced 2026-09-08 06:31:27 -07:00
[ai] fix stub endpoint
This commit is contained in:
+20
-16
@@ -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 .NET’s `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 .NET’s `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
@@ -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
|
||||
|
||||
@@ -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 () => {
|
||||
|
||||
Reference in New Issue
Block a user