mirror of
https://github.com/moeru-ai/airi.git
synced 2026-08-14 00:48:06 +00:00
fix(minecraft): auth failure misleading err close #2071
This commit is contained in:
@@ -29,6 +29,12 @@ BOT_PORT=''
|
||||
# BOT_AUTH='microsoft'
|
||||
BOT_VERSION=''
|
||||
|
||||
# AIRI desktop server channel
|
||||
# Copy the Auth Token from AIRI desktop under Settings > Connection.
|
||||
AIRI_WS_BASEURL='ws://localhost:6121/ws'
|
||||
AIRI_WS_TOKEN=''
|
||||
AIRI_CLIENT_NAME='minecraft-bot'
|
||||
|
||||
# ==============================================================================
|
||||
# Debug and Development Tools
|
||||
# ==============================================================================
|
||||
|
||||
@@ -30,9 +30,21 @@ Treat this service as a local-development and trusted-server tool only.
|
||||
cp integrations/minecraft/.env integrations/minecraft/.env.local
|
||||
```
|
||||
|
||||
3. Edit `integrations/minecraft/.env.local`.
|
||||
3. Open **Settings > Connection** in the AIRI desktop app.
|
||||
|
||||
4. Start the service:
|
||||
4. Copy the **Auth Token**.
|
||||
|
||||
5. Edit `integrations/minecraft/.env.local` and set the token:
|
||||
|
||||
```dotenv
|
||||
AIRI_WS_TOKEN='paste-the-desktop-auth-token-here'
|
||||
```
|
||||
|
||||
The desktop app generates this token by default. Keep the token private.
|
||||
|
||||
The default `AIRI_WS_BASEURL` is `ws://localhost:6121/ws`. Change itwhen the desktop server uses another address.
|
||||
|
||||
6. Start the service:
|
||||
|
||||
```bash
|
||||
pnpm -F @proj-airi/minecraft-bot dev
|
||||
@@ -44,7 +56,7 @@ Treat this service as a local-development and trusted-server tool only.
|
||||
pnpm dev
|
||||
```
|
||||
|
||||
5. The bot should automatically connect to both AIRI and the Minecraft server.
|
||||
7. The bot should automatically connect to both AIRI and the Minecraft server.
|
||||
|
||||
## Cognitive Architecture
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"@modelcontextprotocol/sdk": "catalog:",
|
||||
"@moeru/std": "catalog:",
|
||||
"@proj-airi/server-sdk": "workspace:*",
|
||||
"@proj-airi/server-shared": "workspace:*",
|
||||
"@xsai/generate-text": "catalog:",
|
||||
"@xsai/shared-chat": "catalog:",
|
||||
"alien-signals": "catalog:",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { errorMessageFrom } from '@moeru/std'
|
||||
import { parseServerErrorMessage } from '@proj-airi/server-shared'
|
||||
|
||||
interface AiriClientLike {
|
||||
connect: () => Promise<void>
|
||||
@@ -10,49 +11,75 @@ interface LoggerLike {
|
||||
withFields: (fields: Record<string, unknown>) => LoggerLike
|
||||
}
|
||||
|
||||
type ReportedFailureKind = 'authentication-retrying' | 'authentication-terminal' | 'connection'
|
||||
|
||||
export function startAiriClientConnection(client: AiriClientLike, deps: {
|
||||
logger: LoggerLike
|
||||
url: string
|
||||
}) {
|
||||
let unavailableReported = false
|
||||
let connectionInterrupted = false
|
||||
let reportedFailureKind: ReportedFailureKind | undefined
|
||||
|
||||
const reportUnavailable = (error: unknown) => {
|
||||
if (unavailableReported)
|
||||
const reportError = (error: unknown) => {
|
||||
const errorMessage = errorMessageFrom(error) ?? 'Unknown error'
|
||||
const serverError = parseServerErrorMessage(errorMessage)
|
||||
let failureKind: ReportedFailureKind = 'connection'
|
||||
if (serverError.authentication) {
|
||||
failureKind = serverError.terminal ? 'authentication-terminal' : 'authentication-retrying'
|
||||
}
|
||||
|
||||
if (reportedFailureKind === failureKind)
|
||||
return
|
||||
|
||||
unavailableReported = true
|
||||
connectionInterrupted = true
|
||||
reportedFailureKind = failureKind
|
||||
|
||||
if (serverError.authentication) {
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
error: errorMessage,
|
||||
retrying: !serverError.terminal,
|
||||
}).warn(
|
||||
serverError.terminal
|
||||
? 'AIRI server authentication failed. Check AIRI_WS_TOKEN before restarting the service'
|
||||
: 'AIRI server authentication failed. Set AIRI_WS_TOKEN to the desktop server Auth Token. The client will retry in background',
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
error: errorMessageFrom(error) ?? 'Unknown error',
|
||||
}).warn('AIRI server is unavailable; continuing startup without AIRI and retrying in background')
|
||||
error: errorMessage,
|
||||
retrying: true,
|
||||
}).warn('AIRI server is unavailable. The service will continue startup and retry in background')
|
||||
}
|
||||
|
||||
const reportDisconnected = () => {
|
||||
connectionInterrupted = true
|
||||
reportedFailureKind = 'connection'
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
}).warn('AIRI server connection closed; retrying in background')
|
||||
retrying: true,
|
||||
}).warn('AIRI server connection closed. The client will retry in background')
|
||||
}
|
||||
|
||||
void client.connect()
|
||||
.then(() => {
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
}).log(
|
||||
unavailableReported
|
||||
? 'Connected to AIRI server after background retry'
|
||||
: 'Connected to AIRI server',
|
||||
)
|
||||
unavailableReported = false
|
||||
})
|
||||
.catch((error) => {
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
error: errorMessageFrom(error) ?? 'Unknown error',
|
||||
}).warn('AIRI client stopped retrying')
|
||||
})
|
||||
const reportConnected = () => {
|
||||
deps.logger.withFields({
|
||||
url: deps.url,
|
||||
}).log(
|
||||
connectionInterrupted
|
||||
? 'Connected to AIRI server after background retry'
|
||||
: 'Connected to AIRI server',
|
||||
)
|
||||
connectionInterrupted = false
|
||||
reportedFailureKind = undefined
|
||||
}
|
||||
|
||||
void client.connect().catch(reportError)
|
||||
|
||||
return {
|
||||
reportUnavailable,
|
||||
reportConnected,
|
||||
reportDisconnected,
|
||||
reportError,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +86,9 @@ async function main() {
|
||||
// the connection recovers immediately once the loop frees up.
|
||||
// Root cause (the periodic event-loop stall itself) is tracked separately.
|
||||
heartbeat: { readTimeout: 120_000, pingInterval: 20_000 },
|
||||
onError: error => airiConnectionLifecycle?.reportUnavailable(error),
|
||||
onError: error => airiConnectionLifecycle?.reportError(error),
|
||||
onClose: () => airiConnectionLifecycle?.reportDisconnected(),
|
||||
onReady: () => airiConnectionLifecycle?.reportConnected(),
|
||||
})
|
||||
airiConnectionLifecycle = startAiriClientConnection(airiClient, {
|
||||
logger,
|
||||
|
||||
Generated
+3
@@ -3100,6 +3100,9 @@ importers:
|
||||
'@proj-airi/server-sdk':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/server-sdk
|
||||
'@proj-airi/server-shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/server-shared
|
||||
'@xsai/generate-text':
|
||||
specifier: 'catalog:'
|
||||
version: 0.5.0-beta.2(patch_hash=306bfb723913596b334140f0d6fa48063f336e3b44024efc1d72bf60d54b15e6)
|
||||
|
||||
Reference in New Issue
Block a user