Commit Graph
1878 Commits
Author SHA1 Message Date
RainbowBird 7841df28f8 build(deps): migrate workspace dependencies to catalogs
Move direct external dependency versions into pnpm catalogs and update workspace packages to reference catalog entries.

Signed-off-by: RainbowBird <git@luoling.moe>

Commit-Message-Assisted-by: Codex
2026-06-03 17:12:13 +08:00
6a0ec32aaf chore(i18n): update translations (#1898)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

---------

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
Co-authored-by: Garfield Lee <Garfield550@users.noreply.github.com>
2026-06-03 17:01:28 +08:00
88fac74702 fix(stage-ui): correct provider source filter categories (#1929)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: LemonNeko <self@lemonneko.moe>
2026-06-02 16:02:00 +00:00
Makito 4d496b18d7 fix(stage-ui): typecheck errors 2026-06-02 23:37:34 +09:00
RainbowBird 6e871253e7 feat(server): langfuse 2026-05-30 19:35:37 +08:00
0xSelenicDoveandautofix-ci[bot] b84d59438c fix(ci): upgrade playwright to ^1.60.0 to resolve install hang on node 24 (#1896)
### Description
Upgrades Playwright from `^1.59.1` to `^1.60.0` across the monorepo
workspace.

### Why is this needed?
In clean CI environments using Node.js 24 (the current LTS), Playwright
versions older than `1.60.0` hang indefinitely during the zip extraction
phase of `playwright install chromium`.

This is a known compatibility issue with Playwright's older extraction
utility (`yauzl`) running on Node 24+. Playwright resolved this bug in
version `1.60.0`. Upgrading ensures the CI test pipeline runs smoothly
without hanging.

### Proposed Changes
- Upgraded `"playwright"` to `^1.60.0` in:
  - Root `package.json`
  - `packages/vishot-runner-browser/package.json`
  - `packages/vishot-runner-electron/package.json`
  - `services/twitter-services/package.json`
- Regenerated `pnpm-lock.yaml` via `pnpm install`.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-29 19:07:58 +08:00
0xSelenicDove 32e4acc89f test: resolve spine typecheck and unit test failures in CI (#1895) 2026-05-28 21:16:33 +08:00
github-actions[bot]andCrowdin Bot 4ea23874d4 chore(i18n): update translations (#1878)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-27 16:25:41 +08:00
Lilia_Chenandautofix-ci[bot] 484fb1f767 fix(stage-ui-three): OrbitControl initialisation (#1877)
## Summary

- Fix the first Live2D -> VRM switch camera regression where
OrbitControls could clamp the camera into the model.
- Remove the VRM render scale control from the model settings panel.
- Remove the now-unused `settings.vrm.render-scale` locale entries.
- Ignore Godot/.NET `bin/` and `obj/` build artifacts under
`engines/stage-tamagotchi-godot`.

## Root Cause

On the first VRM load, `OrbitControls` can mount before the VRM
bootstrap writes a real `modelSize`. The previous watcher copied
`modelSize.z = 0` into `minDistance` and `maxDistance`, so later camera
updates were clamped onto the target before the bootstrap camera pose
finished applying.

## Changes

- Skip OrbitControls distance-bound updates until `modelSize.z` is
finite and non-zero.
- Keep the render scale runtime state intact, but remove the VRM
settings UI entry so it is no longer hot-updated from the panel.
- Add Godot project ignores for local .NET build output.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-27 08:35:43 +08:00
github-actions[bot]andCrowdin Bot 90d288f952 chore(i18n): update translations (#1874)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-26 13:19:25 +08:00
RainbowBird 0ac9b709a2 refactor(server): integrate better-auth admin plugin (#1873) 2026-05-26 02:04:25 +08:00
RainbowBird 7e96386ec0 fix(server): unblock FLUX checkout from Electron desktop renderer
The packaged Electron renderer loads from file://, so its Stripe checkout
and portal requests carry no Referer and an opaque/absent Origin.
resolveTrustedRequestOrigin() returned undefined and the routes threw
INVALID_ORIGIN ("Missing trusted request origin"), blocking FLUX purchases
on desktop. Web and mobile were unaffected because they send a trusted web
origin. CORS was not the blocker: file:// requests already reach the API
(balance, providers, characters all work), so only the checkout-specific
redirect-base dependency failed.

Stripe success_url/cancel_url/return_url must be http(s) URLs, so file://
can never be a valid redirect base. Replace the trusted-origin requirement
with resolveCheckoutRedirectBase(), which prefers the request's trusted
origin (web/mobile return to where they started) and falls back to a
configured canonical web app URL (WEB_APP_URL, default https://airi.moeru.ai)
for origin-less clients like the desktop app.

On the client, the desktop app opens checkout in the system browser via
window.open (routed through setWindowOpenHandler -> shell.openExternal)
instead of navigating the file:// window to Stripe, and refreshes the FLUX
balance on window focus since it never receives the success_url redirect.
2026-05-26 01:56:47 +08:00
github-actions[bot]andCrowdin Bot 223a1bfffe chore(i18n): update translations (#1864)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-25 23:50:50 +08:00
RainbowBird 6f63ce96e9 feat(server): server-controlled default streaming TTS model
The streaming speech provider settings page was seeding its model
picker with a hardcoded `volcengine/seed-tts-2.0`, which contradicts
the just-landed "streaming model catalog comes from the server"
contract. Now the default also comes from configKV.

- `UNSPEECH_UPSTREAM.streaming` gains `defaultModel?: string`.
- `GET /api/v1/audio/models/streaming` response gains `default: string
  | null` reading that field.
- admin slice apply: `streaming.defaultModel` survives key/URL
  rotation alongside `streaming.models`.
- `providerOfficialSpeechStreaming.listModels` stashes the value in a
  module-scope ref, exposed via `getDefaultStreamingModel()`.
- Streaming speech settings page reads the helper instead of the
  hardcoded id; falls back to the first server-returned model if the
  operator hasn't curated a default. `handleGenerateSpeech` no longer
  has a `seed-tts-2.0` backstop — a missing backend prefix in the
  selected model id now throws instead of silently defaulting.
2026-05-24 21:13:52 +08:00
3828d06322 fix(computer-use-mcp): bound waitForElement frame timeouts (#1856)
## Summary\n- Pass the remaining waitForElement budget into each
frame-level CU_ACTION send so unresponsive frames cannot consume the
fixed 8s sendMessage timeout.\n- Use the remaining deadline for each
poll and stop polling immediately when the budget is exhausted.\n-
Reduce the bridge-side waitForElement grace from the legacy 9.5s buffer
to a small transport grace.\n- Add a regression test covering the
hanging-extension case.\n\n## Validation\n- pnpm -C
services/computer-use-mcp exec vitest run --config ./vitest.config.ts
src/browser-dom/extension-bridge.test.ts

---------

Co-authored-by: Neko <neko@ayaka.moe>
Co-authored-by: 刘梓恒 <160735726+3361559784@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-22 15:29:52 +08:00
github-actions[bot]andCrowdin Bot 9d9cded60a chore(i18n): update translations (#1844)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-21 22:56:06 +08:00
Lilia_Chenandautofix-ci[bot] ecf234c246 feat(stage-tamagotchi-godot): camera control (#1855)
## Summary

This PR adds the first usable camera and view-state loop for the
experimental Godot stage.

The main design direction is that Godot owns the stage view state, while
Electron and the settings page act as lifecycle/transport/control
surfaces. The settings UI can now edit Godot camera state, and camera
changes from the Godot window can flow back to the UI.

## What Changed

- Added the shared Godot view-state contract for camera pose, avatar
bounds, snapshots, patches, and errors.
- Added Electron main bridge APIs for Godot status, scene input, view
snapshots, and view patches.
- Added Godot-side camera runtime:
  - initial camera bootstrap from loaded avatar bounds
  - avatar facing normalisation
  - orbit, pan, wheel movement, and WASD-style camera movement
  - idle persistence for camera position, yaw, pitch, and FOV
- Added settings-page Godot camera controls with coalesced updates,
restart-safe session handling, and stale request protection.
- Kept scene input simple: Electron does not cache the selected model
for replay. Each Godot start receives the current model selection from
the renderer.

## File Guide

Shared contracts:

- `packages/stage-shared/src/godot-stage/view-state.ts` defines the
Godot view-state contract and schema validation.
- `packages/stage-shared/src/godot-stage/index.ts` exports the Godot
shared contract surface.
- `packages/stage-shared/package.json` exposes the new shared entry.

Electron / renderer bridge:

- `apps/stage-tamagotchi/src/shared/eventa/index.ts` adds the Eventa
contracts for Godot status, scene input, view snapshot, patch, and error
events.
- `apps/stage-tamagotchi/src/main/services/airi/godot-stage/index.ts`
owns the Godot sidecar lifecycle, local WebSocket bridge, scene input
forwarding, and view-state forwarding.

Settings page:

- `apps/stage-tamagotchi/src/renderer/pages/settings/models/index.vue`
wires the settings page to Godot status, scene input, snapshots, and
patches.
-
`apps/stage-tamagotchi/src/renderer/pages/settings/models/godot-view-patch-queue.ts`
coalesces high-frequency camera edits before sending them to Godot.
-
`apps/stage-tamagotchi/src/renderer/pages/settings/models/godot-view-session.ts`
tracks renderer-local Godot session epochs so stale async results are
ignored.

Shared settings UI:

-
`packages/stage-ui/src/components/scenarios/settings/model-settings/godot.vue`
renders the Godot camera controls.
-
`packages/stage-ui/src/components/scenarios/settings/model-settings/panel.vue`
routes the model settings panel between Live2D, VRM, and Godot settings.
-
`packages/stage-ui/src/components/scenarios/settings/model-settings/runtime.ts`
resolves model-settings runtime display state and Godot camera control
range.
- `packages/stage-ui/src/components/data-pane/property-number.vue`
updates number control behavior so config changes such as min/max/range
are reflected correctly.

Godot runtime:

- `engines/stage-tamagotchi-godot/scripts/StageRoot.cs` wires scene
loading, view runtime, and transport together.
- `engines/stage-tamagotchi-godot/scripts/scene/StageSceneController.cs`
loads VRM avatars and normalizes avatar facing direction.
- `engines/stage-tamagotchi-godot/scripts/view/StageViewRuntime.cs` owns
Godot view-state updates, snapshots, patch application, and idle
persistence.
-
`engines/stage-tamagotchi-godot/scripts/view/StageCameraPoseController.cs`
computes avatar bounds, bootstraps the camera, applies camera pose, and
handles camera movement math.
-
`engines/stage-tamagotchi-godot/scripts/view/StageCameraInputController.cs`
maps mouse and keyboard input into camera operations.
- `engines/stage-tamagotchi-godot/scripts/view/StageViewStateRules.cs`
centralizes view-state defaults and validation rules.
- `engines/stage-tamagotchi-godot/scripts/view/StageViewStateStore.cs`
persists and loads Godot camera view state.
- `engines/stage-tamagotchi-godot/scripts/transport/StageViewJson.cs`
parses and serializes view-state transport payloads.
-
`engines/stage-tamagotchi-godot/scripts/transport/StageViewPayloads.cs`
defines the C# payload records used by the transport layer.

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-05-21 03:32:59 +08:00
jim139129 cd908015b3 fix(stage-ui): default artistry provider to none (#1854) 2026-05-21 03:05:55 +08:00
Neko Ayaka bd47b2c8ba chore(i18n): updated locales 2026-05-21 02:54:17 +08:00
Neko Ayaka 018ad05fd7 fix(stage-ui): useVersionedLocalStorage did not return the patched value back 2026-05-21 02:53:56 +08:00
Neko Ayaka b2dfe03d11 fix(stage-ui-live2d): better blink interval 2026-05-21 02:53:02 +08:00
Neko Ayaka 92b20b0dd2 fix(stage-*): many live2d and vrm issues, default values were missing 2026-05-21 02:25:04 +08:00
DrHuangMHT 7f9eeb715d fix(stage-*): improve cursor tracking (#1842) 2026-05-20 22:30:38 +08:00
Makito 7c76ea680c chore(pnpm): fix pnpm-workspaces issues and update catalog for vitest 2026-05-20 11:38:20 +09:00
Makito f86d86c04c feat(stage-tamagotchi,stage-shared): listen keyup for global shortcut with uiohook 2026-05-20 03:07:54 +09:00
Neko Ayaka 10f340a651 fix(stage-ui): missing and incorrect flex gap 2026-05-20 01:46:18 +08:00
RainbowBird 615e0441e8 refactor(server): proxy TTS through unspeech, drop implicit fallbacks
Frontend voice picker silently lost Azure voices because /audio/voices
was called without ?model=, so the server fell back to DEFAULT_TTS_MODEL
and only ever returned one model's catalog. /audio/models also hardcoded
a single `auto` alias, so the client could never request a specific
model id even if it wanted to. On top of that, the Azure adapter shipped
only 2 hand-typed voices in voices/azure.json — the rest of Microsoft's
catalog was unreachable.

Fixed in one pass:

- Drop voices/*.json. Every getVoiceCatalog now calls unspeech REST
  (backend=microsoft|alibaba|volcengine). Azure proxies Microsoft's live
  voices/list; cosyvoice + volcengine use unspeech's embedded catalogs.
- Drop direct upstream provider calls from send(). All three adapters
  POST to <UNSPEECH_REST_BASE_URL>/v1/audio/speech with the openai-shaped
  body unspeech expects (model: `<backend>/<id>`). Azure SSML still built
  on our side so <prosody rate> speed survives — unspeech accepts
  pre-built <speak> documents.
- Fail-fast on every voices endpoint: empty ?model= → 400 MISSING_MODEL
  (no implicit DEFAULT_TTS_MODEL fallback), only `auto` resolves;
  streaming upstream missing → 503; unspeech non-2xx / network err → 502;
  malformed body → 502 with grepable message. No silent `{voices: []}`.
- /audio/models lists every LLM_ROUTER_CONFIG.tts.models key + `auto`.
- Frontend providerOfficialSpeech.listVoices passes ?model= and throws
  on upstream failure instead of returning [].
- New UNSPEECH_REST_BASE_URL configKV entry (no default, missing → 503).
  config-sync subscriber invalidates the voice cache when this key OR
  LLM_ROUTER_CONFIG changes.
- Router voice catalog Redis cache covers all providers with per-provider
  TTL (azure 6h live, alibaba/volcengine 24h since unspeech embeds them
  at build time).

Pending billing-SKU verification: DashScope cosyvoice now goes through
unspeech's WS-internal alibaba backend (was REST `/SpeechSynthesizer`).
Functional output equivalent — confirm bill after deploy, revert via
git history if WS pricing is materially higher.
2026-05-19 23:14:16 +08:00
RainbowBird 6b0788dd60 feat(server): expose streaming TTS model catalog from config
Streaming TTS model list was hardcoded in the official provider with two
Volcengine ids. This moves it server-side so operators control the
catalog without a client release.

Catalog lives on `STREAMING_TTS_UPSTREAM.models` (same configKV entry as
the ws upstream + key envelope) rather than a separate kv key — connection
target, keys, and surfaced ids are one deployment decision, splitting
them risks drift on rotation. `LLM_ROUTER_CONFIG.tts.models` is the
router/fallback domain for HTTP TTS; streaming is single-ws and has no
router semantics, so it gets its own `streamingTtsUpstreamSchema`
instead of reusing `ttsUpstreamSchema`.

New `GET /api/v1/audio/models/streaming` returns the configured list;
empty when upstream or models is unset (UI renders "no models" instead
of 5xx). Admin slice apply preserves existing `models` across key/connection
rotation so admin POSTs that only carry upstream+keys do not wipe the
catalog. Frontend `providerOfficialSpeechStreaming.listModels` fetches
the endpoint and throws on upstream errors (no silent empty array).
2026-05-19 23:14:16 +08:00
nyueki 4542a9d290 feat(stage-ui-spine): add Spine 2D runtime integration (#1810) 2026-05-19 19:13:35 +08:00
Nashchennc 8f1d3c6ab6 feat(core-agent): add chat orchestrator runtime (#1826) 2026-05-19 19:08:36 +08:00
DrHuangMHT cb9eacdd39 fix(live2d): encode filepaths in re-created model settings (#1838) 2026-05-19 19:08:15 +08:00
Neko 6b31552a16 fix(stage-ui): normalize spark:command tool schema (#1845) 2026-05-19 19:07:29 +08:00
Iro c04f4773c5 ref(server-*): refactor and cleanup server stuffs (#1833) 2026-05-19 14:42:23 +08:00
DrHuangMHT c2bf73c95d fix(stage-*): follow-up fix for #1696 (#1809) 2026-05-19 13:54:41 +08:00
github-actions[bot]andCrowdin Bot 82b0d6a80c chore(i18n): update translations (#1831)
This PR contains updated translations from Crowdin. Created from [GitHub
Actions](https://github.com/moeru-ai/airi/blob/main/.github/workflows/crowdin-cron-sync.yml).

You can review the source of translations
[here](https://crowdin.com/project/proj-airi)

Co-authored-by: Crowdin Bot <support+bot@crowdin.com>
2026-05-19 00:08:29 +08:00
RainbowBird 201beae5ac fix(server): import 2026-05-18 23:48:11 +08:00
RainbowBird 45fb765df7 fix(server): ws conn 401 2026-05-18 23:36:08 +08:00
RainbowBird efe0f8ac19 refactor(server): streaming tts support model switch 2026-05-18 23:35:38 +08:00
RainbowBird 2854e0accc fix(server): tts streaming issue 2026-05-18 23:35:37 +08:00
RainbowBird ba9247fb47 feat(server,stage-ui): bidirectional streaming TTS + audio path refactor
Why:
- Add a real bidirectional streaming TTS path: raw LLM tokens are
  forwarded to the upstream model (Volcengine v3 via the unspeech ws
  bridge) without client-side segmentation, so the model owns sentence
  splitting and audio chunks play as they arrive.
- Move audio endpoints out of /api/v1/openai/. `/audio/voices`,
  `/audio/models`, `/audio/voices/streaming` are not real OpenAI public
  APIs, and the streaming TTS surface has nothing to do with OpenAI —
  keeping them under /openai/ mislabelled the contract.
- Introduce `capabilities.speech.transport` on ProviderDefinition so
  future streaming providers (ElevenLabs / Cartesia / OpenAI Realtime)
  opt in without touching Stage.vue or the session factory.
- Unify Stage.vue's TTS path through a single StageTtsSession so the
  chat-orchestrator hooks no longer branch on provider id.

What:
- apps/server: new ws proxy /api/v1/audio/speech/ws bridges client ↔
  unspeech with auth, pre-flight flux check, billing from upstream
  session.finished.usage, OTel spans.
- apps/server: audio routes moved from /api/v1/openai/audio/* to
  /api/v1/audio/* (hard cutover; 404 sentinel tests added).
- apps/server: new /api/v1/audio/voices/streaming proxy reads voices
  from unspeech /api/voices?provider=volcengine.
- apps/server: new STREAMING_TTS_UPSTREAM configKV entry +
  scripts/seed-streaming-tts.ts.
- stage-ui: new libs/speech/streaming-pipeline.ts opens one ws per LLM
  intent (appendText / finish / cancel + onSentence / onError / onDone).
- stage-ui: new libs/speech/tts-session.ts — StageTtsSession interface
  with segmenter and streaming adapters; factory dispatches by
  capabilities.speech.transport instead of hard-coded provider id.
- stage-ui: providerOfficialSpeechStreaming with capabilities.speech =
  { transport: 'bidirectional-ws' }; settings page with model/voice
  picker + ws-based preview.
- stage-ui: Stage.vue chat hooks collapsed to a single currentSession;
  hot-swap watcher cancels mid-session on provider/voice/model change;
  unmount cancels and drains playback.

Tests:
- 9 streaming-pipeline tests (happy path / buffered / error / cancel /
  truncation)
- 11 tts-session tests (factory branch coverage + adapter contracts)
- 4 audio-speech-ws route tests (forwarding / billing / pre-flight /
  config-missing)
- 3 legacy-path 404 sentinels in v1 route tests
- Verification doc updated to reflect automated coverage.
2026-05-18 23:34:35 +08:00
RainbowBird bf1aba757c feat(server): stream tts provider 2026-05-18 23:33:24 +08:00
RainbowBird af17e9cbc2 feat(server): llm & tts gateway (#1837) 2026-05-18 23:31:07 +08:00
RainbowBird 31376fc6f1 feat(server, stage-ui): metrics 2026-05-18 23:29:14 +08:00
RainbowBird 982da671ef fix(server): ws conn 401 2026-05-18 17:05:49 +08:00
Neko Ayaka f33d485b82 feat(stage-ui): io tracer for CALL tokens 2026-05-18 01:23:14 +08:00
Neko Ayaka 77eeb64f51 fix(stage-ui): ensure started 2026-05-18 01:22:32 +08:00
Neko Ayaka 4622b99e2a refactor(pipelines-audio): better token processing pipeline, ACT, DELAY, CALL, unified 2026-05-18 01:20:21 +08:00
Neko Ayaka c459e647d0 fix(stage-ui,stage-tamagotchi): minor animation fix, and caption auto fade out 2026-05-17 18:37:36 +08:00
RainbowBird b4360c1090 fix(server): update token retrieval for WebSocket client to ensure reactive updates 2026-05-17 13:43:19 +08:00
Neko Ayaka bc69df548e feat(plugin-*): prompt for toolset 2026-05-15 18:13:31 +08:00