diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 842997448..8b272f95f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -1,4 +1,4 @@
-# Start contributing to [Project AIRI](https://github.com/moeru-ai/airi)
+# Start contributing to [Project Moeka](https://github.com/cuwayo/moeka)
Hello! Thank you for your interest in contributing to this project. This guide will help you get started.
@@ -6,44 +6,31 @@ Hello! Thank you for your interest in contributing to this project. This guide w
- [Git](https://git-scm.com/downloads)
- [Node.js 23+](https://nodejs.org/en/download/)
-- [corepack](https://github.com/nodejs/corepack)
-- [pnpm](https://pnpm.io/installation)
+- [Bun 1.4+](https://bun.sh/docs/installation)
Windows setup
-0. Download [Visual Studio](https://visualstudio.microsoft.com/downloads/) and follow the instructions here: https://rust-lang.github.io/rustup/installation/windows-msvc.html#walkthrough-installing-visual-studio-2022
-
- > Make sure to install Windows SDK and C++ build tools when installing Visual Studio.
-
-1. Open PowerShell
-2. Install [`scoop`](https://scoop.sh/)
+0. Open PowerShell
+1. Install [`scoop`](https://scoop.sh/)
```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
```
-3. Install `git`, Node.js, `rustup`, `msvc` through `scoop`
+2. Install `git` and Node.js through `scoop`
```powershell
- scoop install git nodejs rustup
-
- # For Rust dependencies
- # Not required if you are not going to develop on either crates or apps/tamagotchi
- scoop install main/rust-msvc
- # Rust & Windows specific
- rustup toolchain install stable-x86_64-pc-windows-msvc
- rustup default stable-x86_64-pc-windows-msvc
+ scoop install git nodejs
```
> https://stackoverflow.com/a/64121601
-4. Install `pnpm` through `corepack`
+3. Install `bun` through the official script
```powershell
- corepack enable
- corepack prepare pnpm@latest --activate
+ powershell -c "irm bun.sh/install.ps1 | iex"
```
@@ -58,11 +45,10 @@ Hello! Thank you for your interest in contributing to this project. This guide w
brew install git node
```
-2. Install `pnpm` through `corepack`
+2. Install `bun` through `brew`
```shell
- corepack enable
- corepack prepare pnpm@latest --activate
+ brew install oven-sh/bun/bun
```
@@ -73,22 +59,10 @@ Hello! Thank you for your interest in contributing to this project. This guide w
0. Open terminal
1. Follow [nodesource/distributions: NodeSource Node.js Binary Distributions](https://github.com/nodesource/distributions?tab=readme-ov-file#table-of-contents) to install `node`
2. Follow [Git](https://git-scm.com/downloads/linux) to install `git`
-3. Install `pnpm` through `corepack`
+3. Install `bun` through the official script
```shell
- corepack enable
- corepack prepare pnpm@latest --activate
- ```
-
-4. If you would love to help to develop the desktop version, you will need those dependencies:
-
- ```shell
- sudo apt install \
- libssl-dev \
- libglib2.0-dev \
- libgtk-3-dev \
- libjavascriptcoregtk-4.1-dev \
- libwebkit2gtk-4.1-dev
+ curl -fsSL https://bun.sh/install | bash
```
@@ -121,8 +95,8 @@ Click on the **Fork** button on the top right corner of the [moeru-ai/airi](http
## Clone
```shell
-git clone https://github.com//airi.git
-cd airi
+git clone https://github.com//moeka.git
+cd moeka
```
## Create your working branch
@@ -134,12 +108,7 @@ git checkout -b
## Install dependencies
```shell
-corepack enable
-pnpm install
-
-# For Rust dependencies
-# Not required if you are not going to develop on either crates or apps/tamagotchi
-cargo fetch
+bun install
```
> [!NOTE]
@@ -147,51 +116,22 @@ cargo fetch
> We would recommend to install [@antfu/ni](https://github.com/antfu-collective/ni) to make your script simpler.
>
> ```shell
-> corepack enable
> npm i -g @antfu/ni
> ```
>
> Once installed, you can
>
-> - use `ni` for `pnpm install`, `npm install` and `yarn install`.
-> - use `nr` for `pnpm run`, `npm run` and `yarn run`.
+> - use `ni` for `bun install`, `npm install` and `yarn install`.
+> - use `nr` for `bun run`, `npm run` and `yarn run`.
>
> You don't need to care about the package manager, `ni` will help you choose the right one.
## Choose the application you want to develop on
-### Stage Tamagotchi (Desktop version)
+### Stage Web
```shell
-pnpm dev:tamagotchi
-```
-
-> [!NOTE]
->
-> For [@antfu/ni](https://github.com/antfu-collective/ni) users, you can
->
-> ```shell
-> nr dev:tamagotchi
-> ```
-
-> [!NOTE]
->
-> The `dev` and `start` scripts run `install-electron` before `electron-vite`.
->
-> Electron 42 removed the `postinstall` script. The `electron` package now downloads its binary
-> when you first run its `bin` entry. `electron-vite` reads `node_modules/electron/path.txt`
-> directly, so it never starts that download. A fresh install therefore fails with
-> `Error: Electron uninstall`.
->
-> `install-electron` runs the same code as the removed `postinstall` script. It returns
-> immediately when the binary is already present.
->
-> Remove this step after `electron-vite` supports the lazy download.
-
-### Stage Web (Browser version for [airi.moeru.ai](https://airi.moeru.ai))
-
-```shell
-pnpm dev
+bun run dev
```
> [!NOTE]
@@ -209,7 +149,7 @@ Browse the live UI component storyboard at [airi.moeru.ai/ui](https://airi.moeru
### Documentation site
```shell
-pnpm dev:docs
+bun run dev:docs
```
> [!NOTE]
@@ -240,14 +180,14 @@ Edit the credentials in `.env.local`.
Migrate the database
```shell
-pnpm -F @proj-airi/telegram-bot db:generate
-pnpm -F @proj-airi/telegram-bot db:push
+bun run --filter @proj-airi/telegram-bot db:generate
+bun run --filter @proj-airi/telegram-bot db:push
```
Run the bot
```shell
-pnpm -F @proj-airi/telegram-bot start
+bun run --filter @proj-airi/telegram-bot start
```
> [!NOTE]
@@ -275,7 +215,7 @@ Edit the credentials in `.env.local`.
Run the bot
```shell
-pnpm -F @proj-airi/discord-bot start
+bun run --filter @proj-airi/discord-bot start
```
> [!NOTE]
@@ -305,7 +245,7 @@ Edit the credentials in `.env.local`.
Run the bot
```shell
-pnpm -F @proj-airi/minecraft-bot start
+bun run --filter @proj-airi/minecraft-bot start
```
> [!NOTE]
@@ -323,13 +263,13 @@ pnpm -F @proj-airi/minecraft-bot start
Please make sure lint (static checkers) and TypeScript compilers are satisfied:
```shell
-pnpm lint && pnpm typecheck
+bun run lint && bun run typecheck
```
If you are committing images, consider using AVIF format instead of PNG, JPG etc. You can convert existing images to AVIF by running:
```shell
-pnpm to-avif ...
+bun run to-avif ...
```
> [!NOTE]
diff --git a/.github/ISSUE_TEMPLATE/ai-task.md b/.github/ISSUE_TEMPLATE/ai-task.md
index 7b3a9e6c0..2a812f530 100644
--- a/.github/ISSUE_TEMPLATE/ai-task.md
+++ b/.github/ISSUE_TEMPLATE/ai-task.md
@@ -30,13 +30,13 @@ Describe the expected behavior or evidence target.
Run the narrowest relevant command first:
```bash
-pnpm -F exec vitest run
+bun run --filter exec vitest run
```
If runtime contracts changed, also run:
```bash
-pnpm -F typecheck
+bun run --filter typecheck
```
## Output required
diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
index f9804f4fc..dc85fbf53 100644
--- a/.github/copilot-instructions.md
+++ b/.github/copilot-instructions.md
@@ -1,6 +1,6 @@
# Copilot repository instructions
-This is the `moeru-ai/airi` pnpm monorepo. Prefer the smallest safe change and keep every task inside its stated scope.
+This is the `cuwayo/moeka` Bun monorepo. Prefer the smallest safe change and keep every task inside its stated scope.
## General rules
@@ -14,7 +14,7 @@ This is the `moeru-ai/airi` pnpm monorepo. Prefer the smallest safe change and k
## Validation
-- Use package-scoped pnpm commands where possible.
+- Use package-scoped Bun commands where possible.
- Run the narrowest relevant test first.
- Run typecheck if runtime contracts or exported types changed.
- Run affected package tests if shared logic changed.
diff --git a/.github/labels.yml b/.github/labels.yml
index c5ce915db..3a250d188 100644
--- a/.github/labels.yml
+++ b/.github/labels.yml
@@ -1,9 +1,3 @@
-- name: 'apps/stage-pocket'
- description: 'Mobile App: iOS / Android'
- color: 'b8d5ff'
-- name: 'apps/stage-tamagotchi'
- description: 'Desktop App: Windows & macOS & Linux'
- color: 'a1f7ef'
- name: 'apps/stage-web'
description: 'Web App: PWA & Browser'
color: 'abedff'
@@ -137,7 +131,7 @@
description: 'Scope related to providers we support'
color: '4468eb'
- name: 'scope/server-api'
- description: 'Scope related to the server api we maintained, the public service of AIRI'
+ description: 'Scope related to the server api we maintained, the public service of Moeka'
color: '4468eb'
- name: 'scope/ui'
description: 'Scope related to UI/UX, or interface improve, perf, and bugs'
diff --git a/.github/scripts/publish-gitcode-release.sh b/.github/scripts/publish-gitcode-release.sh
index 6d96bff26..586138b57 100644
--- a/.github/scripts/publish-gitcode-release.sh
+++ b/.github/scripts/publish-gitcode-release.sh
@@ -123,16 +123,16 @@ write_expected_github_asset_metadata() {
gh api "repos/${GITHUB_REPOSITORY_NAME}/releases/tags/${encoded_tag}" \
> "${release_assets_json}"
- jq -r '.assets[].name | select(test("^(AIRI-.*|latest-.*\\.yml)$"))' "${release_assets_json}" \
+ jq -r '.assets[].name | select(test("^(Moeka-.*|latest-.*\\.yml)$"))' "${release_assets_json}" \
| sort \
> "${WORK_DIR}/expected-asset-names.txt"
- jq -r '.assets[] | select(.name | test("^(AIRI-.*|latest-.*\\.yml)$")) | [.name, (.digest // "")] | @tsv' "${release_assets_json}" \
+ jq -r '.assets[] | select(.name | test("^(Moeka-.*|latest-.*\\.yml)$")) | [.name, (.digest // "")] | @tsv' "${release_assets_json}" \
| sort \
> "${WORK_DIR}/expected-asset-digests.tsv"
if [[ ! -s "${WORK_DIR}/expected-asset-names.txt" ]]; then
- echo "::error::No GitHub release assets matched AIRI-* or latest-*.yml."
+ echo "::error::No GitHub release assets matched Moeka-* or latest-*.yml."
exit 1
fi
diff --git a/.github/workflows/autofix.yaml b/.github/workflows/autofix.yaml
index eb31c6b1e..5e79b617f 100644
--- a/.github/workflows/autofix.yaml
+++ b/.github/workflows/autofix.yaml
@@ -16,20 +16,20 @@ jobs:
- uses: actions/checkout@v6
# NOTICE:
# Pin Node.js because 26.8.0 reports 26.8.0-alpha.0.0.0.
- # sharp 0.29.3 rejects this prerelease version during pnpm install.
+ # sharp 0.29.3 rejects this prerelease version during bun install.
# Source: https://github.com/nodejs/node/blob/v26.8.0/src/node_version.h
# Remove this pin when Node.js 26 reports a stable version.
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- - run: pnpm install
- - run: pnpm prune && pnpm dedupe
- - run: pnpm run lint:fix
- - run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest --fix
- working-directory: ./apps/stage-pocket
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
+
+ - run: bun install
+ - run: bun prune && bun dedupe
+ - run: bun run lint:fix
# - name: AutoCorrect
# uses: huacnlee/autocorrect-action@main
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index bf590db8a..50c81d881 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -18,33 +18,19 @@ jobs:
- uses: actions/checkout@v6
# NOTICE:
# Pin Node.js because 26.8.0 reports 26.8.0-alpha.0.0.0.
- # sharp 0.29.3 rejects this prerelease version during pnpm install.
+ # sharp 0.29.3 rejects this prerelease version during bun install.
# Source: https://github.com/nodejs/node/blob/v26.8.0/src/node_version.h
# Remove this pin when Node.js 26 reports a stable version.
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- # Setup .NET for Godot C# linting
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
+ - uses: oven-sh/setup-bun@v2
with:
- dotnet-version: '10.0.x'
+ bun-version: 1.4.2
- # Lint Godot C# code
- - name: Lint Godot C#
- working-directory: ./engines/stage-tamagotchi-godot
- run: dotnet format stage-tamagotchi-godot.slnx --verify-no-changes
-
- - run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest
- working-directory: ./apps/stage-pocket
-
- - run: pnpm install --frozen-lockfile
- - run: pnpm run lint
- - run: docker run -v `pwd`:`pwd` -w `pwd` ghcr.io/realm/swiftlint:latest
- working-directory: ./apps/stage-pocket
+ - run: bun install --frozen-lockfile
+ - run: bun run lint
build-test:
name: Build Test (${{ matrix.app_name }})
@@ -53,19 +39,13 @@ jobs:
matrix:
include:
- app_name: stage-web
- command: pnpm -F @proj-airi/stage-web run build && pnpm -F @proj-airi/docs run build:base && mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs && pnpm -F @proj-airi/stage-ui run story:build && mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
-
- - app_name: stage-tamagotchi
- command: pnpm -F @proj-airi/stage-tamagotchi run build
-
- - app_name: stage-tamagotchi-godot
- command: cd engines/stage-tamagotchi-godot && dotnet restore && dotnet build -c ExportRelease
+ command: bun run --filter @proj-airi/stage-web build && bun run --filter @proj-airi/docs build:base && mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs && bun run --filter @proj-airi/stage-ui story:build && mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
- app_name: ui-transitions
- command: pnpm -F @proj-airi/ui-transitions run play:build
+ command: bun run --filter @proj-airi/ui-transitions play:build
- app_name: ui-loading-screens
- command: pnpm -F @proj-airi/ui-loading-screens run play:build
+ command: bun run --filter @proj-airi/ui-loading-screens play:build
runs-on: ubuntu-latest
steps:
@@ -80,40 +60,20 @@ jobs:
restore-keys: |
${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- # Setup .NET and Godot
- - name: Setup .NET
- if: matrix.app_name == 'stage-tamagotchi-godot'
- uses: actions/setup-dotnet@v4
+ - uses: oven-sh/setup-bun@v2
with:
- dotnet-version: '10.0.x'
+ bun-version: 1.4.2
- - name: Setup Godot
- if: matrix.app_name == 'stage-tamagotchi-godot'
- uses: chickensoft-games/setup-godot@v2
- with:
- version: 4.7.1
- use-dotnet: true
- include-templates: true
-
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
+ - run: bun install --frozen-lockfile
+ - run: bun run build:packages
- name: Build App
run: ${{ matrix.command }}
- - name: Export Godot Linux sidecar
- if: matrix.app_name == 'stage-tamagotchi-godot'
- working-directory: ./engines/stage-tamagotchi-godot
- run: |
- mkdir -p out/linux
- godot --headless --export-release "Linux x64" out/linux/godot-stage
-
unit-test:
name: Unit Test
runs-on: ubuntu-latest
@@ -129,34 +89,21 @@ jobs:
restore-keys: |
${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- # # Setup .NET and Godot
- # - name: Setup .NET
- # if: matrix.app_name == 'stage-tamagotchi-godot'
- # uses: actions/setup-dotnet@v4
- # with:
- # dotnet-version: '10.0.x'
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
- # - name: Setup Godot
- # if: matrix.app_name == 'stage-tamagotchi-godot'
- # uses: chickensoft-games/setup-godot@v2
- # with:
- # version: 4.7.1
- # use-dotnet: true
- # include-templates: true
-
- - run: pnpm install --frozen-lockfile
+ - run: bun install --frozen-lockfile
- name: Setup Playwright
run: npx playwright install chromium
- - run: pnpm run build:packages
- - run: pnpm run test:run
+ - run: bun run build:packages
+ - run: bun run test:run
env:
NODE_OPTIONS: --no-experimental-webstorage
@@ -168,14 +115,16 @@ jobs:
- uses: actions/checkout@v6
# Node.js
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- - run: pnpm install --frozen-lockfile
- - run: pnpm run typecheck
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
+
+ - run: bun install --frozen-lockfile
+ - run: bun run typecheck
check-provenance:
name: Check Provenance
@@ -188,7 +137,7 @@ jobs:
id: check
with:
fail-on-provenance-change: true # optional, default: false
- # lockfile: pnpm-lock.yaml # optional
+ # lockfile: bun.lock # optional
# base-ref: origin/main # optional, default: origin/main
# fail-on-downgrade: true # optional, default: true
- name: Print result
diff --git a/.github/workflows/deploy-cloudflare-auth-ui.yml b/.github/workflows/deploy-cloudflare-auth-ui.yml
deleted file mode 100644
index 73db64583..000000000
--- a/.github/workflows/deploy-cloudflare-auth-ui.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-name: Cloudflare Pages (Auth UI)
-
-on:
- push:
- branches:
- - 'main'
-
- workflow_dispatch:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- deploy:
- name: Deploy - ui-server-auth
- runs-on: ubuntu-latest
- permissions:
- contents: read
- deployments: write
-
- environment:
- name: Auth Production
- url: https://accounts.airi.build/ui/
-
- steps:
- - uses: actions/checkout@v6
- # Turborepo
- - name: Cache turbo build setup
- uses: actions/cache@v5
- with:
- path: .turbo
- key: ${{ runner.os }}-turbo-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
- # NOTICE:
- #
- # Keep the wrangler setup consistent with the stage-web deployment
- # workflow. cloudflare/wrangler-action expects a usable wrangler binary
- # when running repository-local pnpm workspaces.
- - run: pnpm i -g wrangler@4
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
- - name: Build ui-server-auth
- run: pnpm -F @proj-airi/ui-server-auth run build
- env:
- VITE_ENABLE_ANALYTICS: 'true'
- VITE_SERVER_URL: 'https://api.airi.build'
-
- - uses: cloudflare/wrangler-action@v4
- with:
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: pages deploy ./apps/ui-server-auth/dist --project-name=airi-accounts-ui --branch=main
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/deploy-cloudflare-workers-dev-server.yml b/.github/workflows/deploy-cloudflare-workers-dev-server.yml
deleted file mode 100644
index 74edf0ec5..000000000
--- a/.github/workflows/deploy-cloudflare-workers-dev-server.yml
+++ /dev/null
@@ -1,132 +0,0 @@
-name: Cloudflare Workers (server-dev)
-
-on:
- push:
- branches:
- - 'server-dev'
-
- workflow_dispatch:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- deploy-stage-web:
- name: Deploy - stage-web (server-dev)
- runs-on: ubuntu-latest
- permissions:
- contents: read
- deployments: write
-
- environment:
- name: ServerDev
- url: ${{ steps.preview-urls.outputs.preview_url }}
-
- steps:
- - uses: actions/checkout@v6
- # Turborepo
- - name: Cache turbo build setup
- uses: actions/cache@v5
- with:
- path: .turbo
- key: ${{ runner.os }}-turbo-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
- - run: pnpm i -g wrangler@4
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
- - name: Build stage-web
- run: |
- pnpm -F @proj-airi/stage-web run build
-
- pnpm -F @proj-airi/docs run build:base
- mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
- cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
-
- pnpm -F @proj-airi/stage-ui run story:build
- mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
- env:
- S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
- S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
- S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
- S3_REGION: ${{ secrets.S3_REGION }}
- WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
- VITE_SERVER_URL: 'https://airi-server-dev.up.railway.app'
-
- - name: Wrangler Upload
- id: wrangler-versions-upload
- uses: cloudflare/wrangler-action@v4
- with:
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: versions upload -c ./apps/stage-web/wrangler.toml --preview-alias server-dev --message "GitHub Actions uploaded server-dev branch preview" --tag v0.0.1-server-dev
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Prepare preview URL
- id: preview-urls
- run: |
- deployment_url="${{ steps.wrangler-versions-upload.outputs.deployment-url }}"
- if [ -z "$deployment_url" ]; then
- echo "Deployment URL from wrangler upload is empty." >&2
- exit 1
- fi
-
- host_without_scheme="${deployment_url#https://}"
- host_without_scheme="${host_without_scheme#http://}"
- preview_url="https://server-dev-${host_without_scheme#*-}"
-
- echo "preview_url=$preview_url" >> "$GITHUB_OUTPUT"
- echo "Preview URL: $preview_url"
-
- deploy-auth-ui:
- name: Deploy - ui-server-auth (server-dev)
- runs-on: ubuntu-latest
- permissions:
- contents: read
- deployments: write
-
- environment:
- name: AuthServerDev
- url: https://server-dev.airi-server-auth.pages.dev/ui/
-
- steps:
- - uses: actions/checkout@v6
- # Turborepo
- - name: Cache turbo build setup
- uses: actions/cache@v5
- with:
- path: .turbo
- key: ${{ runner.os }}-turbo-auth-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-turbo-auth-
- ${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
- - run: pnpm i -g wrangler@4
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
- - name: Build ui-server-auth
- run: pnpm -F @proj-airi/ui-server-auth run build
- env:
- VITE_SERVER_URL: 'https://airi-server-dev.up.railway.app'
-
- - name: Wrangler Pages Deploy
- uses: cloudflare/wrangler-action@v4
- with:
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: pages deploy ./apps/ui-server-auth/dist --project-name=airi-accounts-ui --branch=server-dev
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Print preview URL
- run: |
- echo "Preview URL: https://server-dev.airi-server-auth.pages.dev/ui/"
diff --git a/.github/workflows/deploy-cloudflare-workers-preview-deploy.yml b/.github/workflows/deploy-cloudflare-workers-preview-deploy.yml
deleted file mode 100644
index b1b66c74f..000000000
--- a/.github/workflows/deploy-cloudflare-workers-preview-deploy.yml
+++ /dev/null
@@ -1,260 +0,0 @@
-name: Cloudflare Workers (Preview) Deploy
-
-on:
- workflow_run:
- workflows:
- - Cloudflare Workers (Preview) Prepare
- types:
- - completed
-
-jobs:
- ask-for-approval:
- name: Ask for approval to deploy - ${{ matrix.app_name }}
- runs-on: ubuntu-latest
- permissions:
- pull-requests: write
- contents: read
-
- strategy:
- matrix:
- include:
- - app_name: stage-web
-
- steps:
- - name: Download artifact - metadata
- uses: dawidd6/action-download-artifact@v19
- with:
- workflow_conclusion: success
- run_id: ${{ github.event.workflow_run.id }}
- name: preview-meta
- path: preview-meta
- allow_forks: true
-
- - name: Parse metadata
- id: meta
- run: |
- node --input-type=module <<'NODE'
- import { appendFile, readFile } from 'node:fs/promises'
-
- const metadata = JSON.parse(await readFile('preview-meta/preview-meta.json', 'utf8'))
- if (
- !/^\d+$/.test(metadata.prNumber)
- || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(metadata.repositoryFullName)
- || !/^[a-f0-9]{40}$/i.test(metadata.headSha)
- ) {
- throw new Error('Preview metadata has an invalid shape')
- }
-
- await appendFile(process.env.GITHUB_OUTPUT, [
- `PR_NUM=${metadata.prNumber}`,
- `REPO_FULL_NAME=${metadata.repositoryFullName}`,
- `HEAD_SHA=${metadata.headSha}`,
- '',
- ].join('\n'))
- NODE
-
- - name: Find Comment
- if: ${{ always() }}
- uses: peter-evans/find-comment@v4
- id: fc
- with:
- issue-number: ${{ steps.meta.outputs.PR_NUM }}
- comment-author: 'github-actions[bot]'
- body-includes: to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*
-
- - name: Comment on require approval
- uses: peter-evans/create-or-update-comment@v5
- with:
- comment-id: ${{ steps.fc.outputs.comment-id }}
- issue-number: ${{ steps.meta.outputs.PR_NUM }}
- edit-mode: replace
- body: |
- ## ⏳ Approval required for deploying to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*.
-
- | Name | Link |
- |:------------------------|:---------------------------------------------------------------------------------------|
- | 🔭 Waiting for approval | For maintainers, approve [here](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})
-
- Hey, maintainers, kindly take some time to review and approve this deployment when you are available. Thank you! 🙏
-
- on-success:
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
- name: Deploy - ${{ matrix.app_name }}
- runs-on: ubuntu-latest
- environment: Preview
-
- permissions:
- contents: read
- pull-requests: write
-
- strategy:
- matrix:
- include:
- - app_name: stage-web
- wrangler_config_path: ./apps/stage-web/wrangler.toml
- build_directory: ./apps/stage-web/dist
- build_command: |
- pnpm -F @proj-airi/stage-web run build
-
- pnpm -F @proj-airi/docs run build:base
- mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
- cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
-
- pnpm -F @proj-airi/stage-ui run story:build
- mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
-
- steps:
- - name: Download artifact - metadata
- uses: dawidd6/action-download-artifact@v19
- with:
- workflow_conclusion: success
- run_id: ${{ github.event.workflow_run.id }}
- name: preview-meta
- path: preview-meta
- allow_forks: true
-
- - name: Parse metadata
- id: meta
- run: |
- node --input-type=module <<'NODE'
- import { appendFile, readFile } from 'node:fs/promises'
-
- const metadata = JSON.parse(await readFile('preview-meta/preview-meta.json', 'utf8'))
- if (
- !/^\d+$/.test(metadata.prNumber)
- || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(metadata.repositoryFullName)
- || !/^[a-f0-9]{40}$/i.test(metadata.headSha)
- ) {
- throw new Error('Preview metadata has an invalid shape')
- }
-
- await appendFile(process.env.GITHUB_OUTPUT, [
- `PR_NUM=${metadata.prNumber}`,
- `REPO_FULL_NAME=${metadata.repositoryFullName}`,
- `HEAD_SHA=${metadata.headSha}`,
- '',
- ].join('\n'))
- NODE
-
- - name: Checkout repository
- uses: actions/checkout@v6
- with:
- repository: ${{ steps.meta.outputs.REPO_FULL_NAME }}
- ref: ${{ steps.meta.outputs.HEAD_SHA }}
- fetch-depth: 1
- persist-credentials: false
- # https://gh.io/securely-using-pull_request_target
- allow-unsafe-pr-checkout: true
-
- - name: Setup pnpm
- uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
-
- # NOTICE:
- #
- # Here installing wrangler to global is required, or otherwise:
- # ERR_PNPM_ADDING_TO_ROOT Running this command will add the dependency to the workspace root...
- # error occurs.
- #
- # Since https://github.com/cloudflare/wrangler-action/pull/339#issuecomment-2667622947 rejected the -g support
- # by saying un-reasonable 'I'm not sure if it's common ... to install packages to the global scope, ... might be introducing some unintended side effects.'
- #
- # Clearly I think installing with ` install` brings more unintended side effects...
- #
- # As suggested by https://github.com/cloudflare/wrangler-action/issues/181#issuecomment-2127990708, we should pre-install
- # with our package manager and then use it in the action.
- - run: pnpm i -g wrangler@4
-
- - name: Install dependencies
- run: pnpm install --frozen-lockfile
-
- - name: Build packages
- run: pnpm run build:packages
-
- - name: Build ${{ matrix.app_name }}
- id: build
- run: ${{ matrix.build_command }}
- continue-on-error: true
- env:
- S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
- S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
- S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
- S3_REGION: ${{ secrets.S3_REGION }}
- WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
- STAGE_WEB_WARP_DRIVE_PREFIX: proj-airi/stage-web/pr-${{ steps.meta.outputs.PR_NUM }}/
- STAGE_UI_WARP_DRIVE_PREFIX: proj-airi/stage-ui/pr-${{ steps.meta.outputs.PR_NUM }}/
-
- - name: Find Comment
- if: ${{ always() }}
- uses: peter-evans/find-comment@v4
- id: fc
- with:
- issue-number: ${{ steps.meta.outputs.PR_NUM }}
- comment-author: 'github-actions[bot]'
- body-includes: to Cloudflare Workers (Preview) for *${{ matrix.app_name }}*
-
- - name: Comment on build failure
- if: ${{ steps.build.outcome != 'success' }}
- uses: peter-evans/create-or-update-comment@v5
- with:
- comment-id: ${{ steps.fc.outputs.comment-id }}
- issue-number: ${{ steps.meta.outputs.PR_NUM }}
- edit-mode: replace
- body: |
- ## ❌ Deploy to Cloudflare Workers (Preview) for *${{ matrix.app_name }}* failed.
-
- | Name | Link |
- |:---------------------|:---------------------------------------------------------------------------------------|
- | 🔍 Latest deploy log | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
-
- - name: Fail if build failed
- if: ${{ steps.build.outcome != 'success' }}
- run: |
- echo "Build step failed; marking workflow as failed."
- exit 1
-
- - name: Wrangler Upload
- if: ${{ steps.build.outcome == 'success' }}
- id: wrangler-versions-upload
- uses: cloudflare/wrangler-action@v4
- with:
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: versions upload -c ${{ matrix.wrangler_config_path }} --preview-alias pr-${{ steps.meta.outputs.PR_NUM }} --message "GitHub Actions uploaded preview for Pull Request ${{ steps.meta.outputs.PR_NUM }}" --tag v0.0.1-pr.${{ steps.meta.outputs.PR_NUM }}
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Prepare preview URLs
- if: ${{ steps.build.outcome == 'success' }}
- id: preview-urls
- run: |
- deployment_url="${{ steps.wrangler-versions-upload.outputs.deployment-url }}"
- if [ -z "$deployment_url" ]; then
- echo "Deployment URL from wrangler upload is empty." >&2
- exit 1
- fi
-
- host_without_scheme="${deployment_url#https://}"
- host_without_scheme="${host_without_scheme#http://}"
- preview_alias_url="https://pr-${{ steps.meta.outputs.PR_NUM }}-${host_without_scheme#*-}"
-
- echo "deploy_preview_url=$deployment_url" >> "$GITHUB_OUTPUT"
- echo "pull_request_preview_url=$preview_alias_url" >> "$GITHUB_OUTPUT"
-
- - name: Create or update comment
- if: ${{ steps.build.outcome == 'success' }}
- uses: peter-evans/create-or-update-comment@v5
- with:
- comment-id: ${{ steps.fc.outputs.comment-id }}
- issue-number: ${{ steps.meta.outputs.PR_NUM }}
- edit-mode: replace
- body: |
- ## ✅ Deploy to Cloudflare Workers (Preview) for *${{ matrix.app_name }}* ready!
-
- | Name | Link |
- |:------------------------|:--------------------------------------------------------------------------------------------|
- | 🔍 Latest deploy log | https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }} |
- | 😎 Deploy Preview | ${{ steps.preview-urls.outputs.deploy_preview_url }} |
- | 🚀 Pull Request Preview | ${{ steps.preview-urls.outputs.pull_request_preview_url }} |
diff --git a/.github/workflows/deploy-cloudflare-workers-preview-prepare.yml b/.github/workflows/deploy-cloudflare-workers-preview-prepare.yml
deleted file mode 100644
index 9d055cd9e..000000000
--- a/.github/workflows/deploy-cloudflare-workers-preview-prepare.yml
+++ /dev/null
@@ -1,37 +0,0 @@
-name: Cloudflare Workers (Preview) Prepare
-
-on:
- pull_request:
- branches:
- - main
-
- workflow_dispatch:
-
-jobs:
- prepare:
- name: Prepare preview sources
- runs-on: ubuntu-latest
-
- steps:
- - name: Persist checkout metadata
- env:
- PR_NUM: ${{ github.event.number }}
- REPO_FULL_NAME: ${{ github.event.pull_request.head.repo.full_name }}
- HEAD_SHA: ${{ github.event.pull_request.head.sha }}
- run: |
- node --input-type=module <<'NODE'
- import { writeFile } from 'node:fs/promises'
-
- await writeFile('preview-meta.json', JSON.stringify({
- prNumber: process.env.PR_NUM,
- repositoryFullName: process.env.REPO_FULL_NAME,
- headSha: process.env.HEAD_SHA,
- }))
- NODE
-
- - name: Upload metadata artifact
- uses: actions/upload-artifact@v7
- with:
- name: preview-meta
- path: ./preview-meta.json
- overwrite: true
diff --git a/.github/workflows/deploy-cloudflare-workers.yml b/.github/workflows/deploy-cloudflare-workers.yml
deleted file mode 100644
index e60b22603..000000000
--- a/.github/workflows/deploy-cloudflare-workers.yml
+++ /dev/null
@@ -1,81 +0,0 @@
-name: Cloudflare Workers
-
-on:
- push:
- branches:
- - 'main'
-
- workflow_dispatch:
-
-concurrency:
- group: ${{ github.workflow }}-${{ github.ref }}
- cancel-in-progress: true
-
-jobs:
- deploy:
- name: Deploy - stage-web
- runs-on: ubuntu-latest
- permissions:
- contents: read
- deployments: write
-
- environment:
- name: Production
- url: https://airi.moeru.ai/docs/
-
- steps:
- - uses: actions/checkout@v6
- # Turborepo
- - name: Cache turbo build setup
- uses: actions/cache@v5
- with:
- path: .turbo
- key: ${{ runner.os }}-turbo-${{ github.sha }}
- restore-keys: |
- ${{ runner.os }}-turbo-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
- # NOTICE:
- #
- # Here installing wrangler to global is required, or otherwise:
- # ERR_PNPM_ADDING_TO_ROOT Running this command will add the dependency to the workspace root...
- # error occurs.
- #
- # Since https://github.com/cloudflare/wrangler-action/pull/339#issuecomment-2667622947 rejected the -g support
- # by saying un-reasonable 'I'm not sure if it's common ... to install packages to the global scope, ... might be introducing some unintended side effects.'
- #
- # Clearly I think installing with ` install` brings more unintended side effects...
- #
- # As suggested by https://github.com/cloudflare/wrangler-action/issues/181#issuecomment-2127990708, we should pre-install
- # with our package manager and then use it in the action.
- - run: pnpm i -g wrangler@4
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
- - name: Build stage-web
- run: |
- pnpm -F @proj-airi/stage-web run build
-
- pnpm -F @proj-airi/docs run build:base
- mv ./docs/.vitepress/dist ./apps/stage-web/dist/docs
- cp ./apps/stage-web/dist/docs/sitemap.xml ./apps/stage-web/dist/sitemap.xml
-
- pnpm -F @proj-airi/stage-ui run story:build
- mv ./packages/stage-ui/.histoire/dist ./apps/stage-web/dist/ui
- env:
- S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
- S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
- S3_ENDPOINT: ${{ secrets.S3_ENDPOINT }}
- S3_REGION: ${{ secrets.S3_REGION }}
- VITE_ENABLE_ANALYTICS: 'true'
- VITE_SERVER_URL: 'https://api.airi.build'
- WARP_DRIVE_PUBLIC_BASE: ${{ secrets.WARP_DRIVE_PUBLIC_BASE }}
-
- - uses: cloudflare/wrangler-action@v4
- with:
- apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
- accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
- command: deploy -c ./apps/stage-web/wrangler.toml
- gitHubToken: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/deploy-huggingface-spaces.yml b/.github/workflows/deploy-huggingface-spaces.yml
index 6ec0e8c08..f158aa744 100644
--- a/.github/workflows/deploy-huggingface-spaces.yml
+++ b/.github/workflows/deploy-huggingface-spaces.yml
@@ -22,14 +22,16 @@ jobs:
HF_TOKEN: ${{ secrets.HF_TOKEN }}
HF_USERNAME: ${{ secrets.HF_USERNAME }}
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- - run: pnpm install --frozen-lockfile
- - run: pnpm build
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
+
+ - run: bun install --frozen-lockfile
+ - run: bun run build
env:
TARGET_HUGGINGFACE_SPACE: 'true'
VITE_APP_TARGET_HUGGINGFACE_SPACE: 'true'
diff --git a/.github/workflows/pr-triage.lock.yml b/.github/workflows/pr-triage.lock.yml
index 1b0426550..73d5c8c6e 100644
--- a/.github/workflows/pr-triage.lock.yml
+++ b/.github/workflows/pr-triage.lock.yml
@@ -351,15 +351,15 @@ jobs:
mkdir -p /tmp/gh-aw/safeoutputs
mkdir -p /tmp/gh-aw/mcp-logs/safeoutputs
cat > "${RUNNER_TEMP}/gh-aw/safeoutputs/config.json" << 'GH_AW_SAFE_OUTPUTS_CONFIG_7cd577a1928d35f4_EOF'
- {"add_labels":{"allowed":["bug","feature","pending triage","apps/stage-pocket","apps/stage-tamagotchi","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":12,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["bug","feature","pending triage","apps/stage-pocket","apps/stage-tamagotchi","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":24,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"report_incomplete":{}}
+ {"add_labels":{"allowed":["bug","feature","pending triage","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":12,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"create_report_incomplete_issue":{},"missing_data":{},"missing_tool":{},"noop":{"max":1,"report-as-issue":"true"},"remove_labels":{"allowed":["bug","feature","pending triage","apps/stage-web","env/os-all","env/os-linux","env/os-macos","env/os-windows","priority/general","priority/nice-to-have","priority/urgent","scope/agent","scope/audio-input","scope/audio-output","scope/avatar","scope/avatar/live2d","scope/avatar/vrm","scope/documentation","scope/engineering","scope/extension","scope/game-playing-ai","scope/i18n","scope/providers","scope/server-api","scope/ui"],"max":24,"target":"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}"},"report_incomplete":{}}
GH_AW_SAFE_OUTPUTS_CONFIG_7cd577a1928d35f4_EOF
- name: Write Safe Outputs Tools
env:
GH_AW_TOOLS_META_JSON: |
{
"description_suffixes": {
- "add_labels": " CONSTRAINTS: Maximum 12 label(s) can be added. Only these labels are allowed: [\"bug\" \"feature\" \"pending triage\" \"apps/stage-pocket\" \"apps/stage-tamagotchi\" \"apps/stage-web\" \"env/os-all\" \"env/os-linux\" \"env/os-macos\" \"env/os-windows\" \"priority/general\" \"priority/nice-to-have\" \"priority/urgent\" \"scope/agent\" \"scope/audio-input\" \"scope/audio-output\" \"scope/avatar\" \"scope/avatar/live2d\" \"scope/avatar/vrm\" \"scope/documentation\" \"scope/engineering\" \"scope/extension\" \"scope/game-playing-ai\" \"scope/i18n\" \"scope/providers\" \"scope/server-api\" \"scope/ui\"]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}.",
- "remove_labels": " CONSTRAINTS: Maximum 24 label(s) can be removed. Only these labels can be removed: [bug feature pending triage apps/stage-pocket apps/stage-tamagotchi apps/stage-web env/os-all env/os-linux env/os-macos env/os-windows priority/general priority/nice-to-have priority/urgent scope/agent scope/audio-input scope/audio-output scope/avatar scope/avatar/live2d scope/avatar/vrm scope/documentation scope/engineering scope/extension scope/game-playing-ai scope/i18n scope/providers scope/server-api scope/ui]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}."
+ "add_labels": " CONSTRAINTS: Maximum 12 label(s) can be added. Only these labels are allowed: [\"bug\" \"feature\" \"pending triage\" \"apps/stage-web\" \"env/os-all\" \"env/os-linux\" \"env/os-macos\" \"env/os-windows\" \"priority/general\" \"priority/nice-to-have\" \"priority/urgent\" \"scope/agent\" \"scope/audio-input\" \"scope/audio-output\" \"scope/avatar\" \"scope/avatar/live2d\" \"scope/avatar/vrm\" \"scope/documentation\" \"scope/engineering\" \"scope/extension\" \"scope/game-playing-ai\" \"scope/i18n\" \"scope/providers\" \"scope/server-api\" \"scope/ui\"]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}.",
+ "remove_labels": " CONSTRAINTS: Maximum 24 label(s) can be removed. Only these labels can be removed: [bug feature pending triage apps/stage-web env/os-all env/os-linux env/os-macos env/os-windows priority/general priority/nice-to-have priority/urgent scope/agent scope/audio-input scope/audio-output scope/avatar scope/avatar/live2d scope/avatar/vrm scope/documentation scope/engineering scope/extension scope/game-playing-ai scope/i18n scope/providers scope/server-api scope/ui]. Target: ${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}."
},
"repo_params": {},
"dynamic_tools": []
@@ -1112,7 +1112,7 @@ jobs:
GH_AW_ALLOWED_DOMAINS: "api.business.githubcopilot.com,api.enterprise.githubcopilot.com,api.github.com,api.githubcopilot.com,api.individual.githubcopilot.com,api.snapcraft.io,archive.ubuntu.com,azure.archive.ubuntu.com,crl.geotrust.com,crl.globalsign.com,crl.identrust.com,crl.sectigo.com,crl.thawte.com,crl.usertrust.com,crl.verisign.com,crl3.digicert.com,crl4.digicert.com,crls.ssl.com,github.com,host.docker.internal,json-schema.org,json.schemastore.org,keyserver.ubuntu.com,ocsp.digicert.com,ocsp.geotrust.com,ocsp.globalsign.com,ocsp.identrust.com,ocsp.sectigo.com,ocsp.ssl.com,ocsp.thawte.com,ocsp.usertrust.com,ocsp.verisign.com,packagecloud.io,packages.cloud.google.com,packages.microsoft.com,ppa.launchpad.net,raw.githubusercontent.com,registry.npmjs.org,s.symcb.com,s.symcd.com,security.ubuntu.com,telemetry.enterprise.githubcopilot.com,ts-crl.ws.symantec.com,ts-ocsp.ws.symantec.com,www.googleapis.com"
GITHUB_SERVER_URL: ${{ github.server_url }}
GITHUB_API_URL: ${{ github.api_url }}
- GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-pocket\",\"apps/stage-tamagotchi\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":12,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-pocket\",\"apps/stage-tamagotchi\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":24,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"report_incomplete\":{}}"
+ GH_AW_SAFE_OUTPUTS_HANDLER_CONFIG: "{\"add_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":12,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"create_report_incomplete_issue\":{},\"missing_data\":{},\"missing_tool\":{},\"noop\":{\"max\":1,\"report-as-issue\":\"true\"},\"remove_labels\":{\"allowed\":[\"bug\",\"feature\",\"pending triage\",\"apps/stage-web\",\"env/os-all\",\"env/os-linux\",\"env/os-macos\",\"env/os-windows\",\"priority/general\",\"priority/nice-to-have\",\"priority/urgent\",\"scope/agent\",\"scope/audio-input\",\"scope/audio-output\",\"scope/avatar\",\"scope/avatar/live2d\",\"scope/avatar/vrm\",\"scope/documentation\",\"scope/engineering\",\"scope/extension\",\"scope/game-playing-ai\",\"scope/i18n\",\"scope/providers\",\"scope/server-api\",\"scope/ui\"],\"max\":24,\"target\":\"${{ github.event.pull_request.number || github.event.inputs.pull_request_number }}\"},\"report_incomplete\":{}}"
with:
github-token: ${{ secrets.GH_AW_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
script: |
diff --git a/.github/workflows/pr-triage.md b/.github/workflows/pr-triage.md
index 30b46eb81..57d115032 100644
--- a/.github/workflows/pr-triage.md
+++ b/.github/workflows/pr-triage.md
@@ -33,8 +33,6 @@ safe-outputs:
- bug
- feature
- pending triage
- - apps/stage-pocket
- - apps/stage-tamagotchi
- apps/stage-web
- env/os-all
- env/os-linux
@@ -64,8 +62,6 @@ safe-outputs:
- bug
- feature
- pending triage
- - apps/stage-pocket
- - apps/stage-tamagotchi
- apps/stage-web
- env/os-all
- env/os-linux
@@ -105,7 +101,7 @@ The target pull request is:
Managed labels:
- Type: `bug`, `feature`
-- App surface: `apps/stage-pocket`, `apps/stage-tamagotchi`, `apps/stage-web`
+- App surface: `apps/stage-web`
- Environment: `env/os-all`, `env/os-linux`, `env/os-macos`, `env/os-windows`
- Scope: `scope/agent`, `scope/audio-input`, `scope/audio-output`, `scope/avatar`, `scope/avatar/live2d`, `scope/avatar/vrm`, `scope/documentation`, `scope/engineering`, `scope/extension`, `scope/game-playing-ai`, `scope/i18n`, `scope/providers`, `scope/server-api`, `scope/ui`
- Priority: `priority/general`, `priority/nice-to-have`, `priority/urgent`
@@ -160,9 +156,7 @@ Do not use web search. Do not use bash to modify files, create branches, post co
### App labels
- Apply `apps/stage-web` when files under `apps/stage-web/` change, or the PR text explicitly says the change is for the web app or PWA/browser surface.
-- Apply `apps/stage-tamagotchi` when files under `apps/stage-tamagotchi/` change, or the PR text explicitly says desktop/Electron/Windows/macOS/Linux app.
-- Apply `apps/stage-pocket` when files under `apps/stage-pocket/` change, or the PR text explicitly says mobile/iOS/Android app.
-- Multiple app labels are allowed when the evidence is explicit.
+- Only `apps/stage-web` exists in this fork. Never apply a desktop or mobile app label.
### Environment labels
diff --git a/.github/workflows/release-pkg.yaml b/.github/workflows/release-pkg.yaml
index 63929cf66..c6abba880 100644
--- a/.github/workflows/release-pkg.yaml
+++ b/.github/workflows/release-pkg.yaml
@@ -21,11 +21,13 @@ jobs:
with:
fetch-depth: 0
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
+
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
- name: Configure npm registry
uses: actions/setup-node@v6
@@ -39,9 +41,9 @@ jobs:
# https://github.com/e18e/ecosystem-issues/issues/201
- run: npm install -g npm@latest
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
+ - run: bun install --frozen-lockfile
+ - run: bun run build:packages
- - run: pnpm publish -r --access public --no-git-checks --dry-run
+ - run: bun run publish:packages -- --dry-run
- - run: pnpm publish -r --access public --no-git-checks
+ - run: bun run publish:packages
diff --git a/.github/workflows/release-pocket-android.yml b/.github/workflows/release-pocket-android.yml
deleted file mode 100644
index 2dc346f16..000000000
--- a/.github/workflows/release-pocket-android.yml
+++ /dev/null
@@ -1,136 +0,0 @@
-name: Release Pocket (Android)
-
-permissions:
- contents: write
- actions: read
-
-on:
- release:
- types:
- - prereleased
- workflow_dispatch:
- inputs:
- build_only:
- description: Build only (no upload to release)
- required: false
- default: false
- type: boolean
- tag:
- description: Specific tag for the release upload
- required: false
- type: string
- schedule:
- - cron: '0 1 * * *'
-
-jobs:
- build:
- name: Build Android APK
- runs-on: ubuntu-latest
- env:
- PRODUCT_NAME: 'AIRI'
- # Every current trigger path produces an installable APK artifact, so ship the analytics-enabled build consistently.
- VITE_ENABLE_ANALYTICS: 'true'
- steps:
- - uses: actions/checkout@v6
-
- - uses: actions/setup-java@v4
- with:
- distribution: temurin
- java-version: '21'
-
- - name: Setup Android SDK
- uses: android-actions/setup-android@v3
-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
-
- - name: Install Android build tools
- run: |
- sdkmanager "build-tools;36.1.0"
- echo "${ANDROID_SDK_ROOT:-$ANDROID_HOME}/build-tools/36.1.0" >> "$GITHUB_PATH"
-
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
-
- - name: Validate workflow dispatch release tag
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.tag == '' }}
- run: |
- echo "workflow_dispatch release uploads require the tag input"
- exit 1
-
- - name: Build web assets
- run: pnpm -F @proj-airi/stage-pocket run build
-
- - name: Capacitor sync
- run: pnpm -F @proj-airi/stage-pocket exec cap sync android
-
- - name: Extract android keystore
- run: echo "${{ secrets.CAPACITOR_ANDROID_KEYSTORE_BASE64 }}" | base64 -d > ${{ github.workspace }}/apps/stage-pocket/airi-pocket.jks
-
- - name: Build APK
- working-directory: apps/stage-pocket/android
- run: pnpm -F @proj-airi/stage-pocket exec cap build android
- env:
- CAPACITOR_ANDROID_KEYSTORE_PATH: ${{ github.workspace }}/apps/stage-pocket/airi-pocket.jks
- CAPACITOR_ANDROID_KEYSTORE_ALIAS: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_ALIAS }}
- CAPACITOR_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_PASSWORD }}
- CAPACITOR_ANDROID_KEYSTORE_ALIAS_PASSWORD: ${{ secrets.CAPACITOR_ANDROID_KEYSTORE_ALIAS_PASSWORD }}
-
- - name: Rename APK for distribution
- env:
- RELEASE_TAG_NAME: ${{ github.event.release.tag_name || inputs.tag }}
- run: |
- VERSION="${RELEASE_TAG_NAME#v}"
- if [ -z "$VERSION" ]; then
- VERSION="$(sed -n 's/^AIRI_VERSION_NAME=//p' apps/stage-pocket/android/app-version.properties)"
- fi
-
- APK_NAME="${PRODUCT_NAME}-${VERSION}-android.apk"
- APK_SOURCE="apps/stage-pocket/android/app/build/outputs/apk/release/app-release-signed.apk"
- APK_BUNDLE_DIR="apps/stage-pocket/android/bundle"
-
- mkdir -p "$APK_BUNDLE_DIR"
- mv "$APK_SOURCE" "$APK_BUNDLE_DIR/$APK_NAME"
-
- echo "APK_NAME=$APK_NAME" >> "$GITHUB_ENV"
- echo "VERSION=$VERSION" >> "$GITHUB_ENV"
-
- - name: Upload APK artifact
- uses: actions/upload-artifact@v4
- with:
- name: ${{ env.APK_NAME }}
- path: apps/stage-pocket/android/bundle/${{ env.APK_NAME }}
- if-no-files-found: error
-
- - name: Upload to GitHub Releases
- id: upload-github-release
- if: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only) }}
- uses: softprops/action-gh-release@v2
- with:
- files: apps/stage-pocket/android/bundle/${{ env.APK_NAME }}
- append_body: true
- tag_name: ${{ github.event.release.tag_name || inputs.tag }}
-
- - name: Setup butler
- uses: remarkablegames/setup-butler@v2
-
- # https://itch.io/docs/butler/pushing.html
- - name: Upload Android to itch.io
- if: ${{ github.event_name == 'release' }}
- run: butler push apps/stage-pocket/android/bundle/${{ env.APK_NAME }} nekomeowww/airi:android --userversion ${{ env.VERSION }}
- env:
- BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
-
- - name: Publish release assets to GitCode
- if: ${{ always() && github.repository == 'moeru-ai/airi' && steps.upload-github-release.outcome == 'success' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only)) }}
- continue-on-error: true
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
- GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
- GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
- GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- run: bash .github/scripts/publish-gitcode-release.sh
diff --git a/.github/workflows/release-pocket-ios.yml b/.github/workflows/release-pocket-ios.yml
deleted file mode 100644
index 1410d2ca6..000000000
--- a/.github/workflows/release-pocket-ios.yml
+++ /dev/null
@@ -1,180 +0,0 @@
-name: Release Pocket (iOS)
-
-permissions:
- contents: write
- actions: read
-
-on:
- release:
- types:
- - prereleased
- workflow_dispatch:
- inputs:
- testflight:
- description: Upload to TestFlight
- type: boolean
- default: false
- tag:
- description: GitHub Release tag (empty to skip)
- type: string
- required: false
- default: ''
-
-jobs:
- build:
- name: Build iOS
- runs-on: macos-26
- env:
- PRODUCT_NAME: 'AIRI'
- VITE_ENABLE_ANALYTICS: 'true'
- UPLOAD_TESTFLIGHT: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.testflight) }}
- RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- steps:
- - uses: actions/checkout@v6
-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
-
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
-
- - name: Build assets
- run: pnpm -F @proj-airi/stage-pocket run build
-
- - name: Sync assets to iOS
- run: pnpm -F @proj-airi/stage-pocket exec cap sync ios
-
- - name: Install Apple certificate
- env:
- CERT_BASE64: ${{ secrets.APPLE_DISTRIBUTION_CERT_BASE64 }}
- CERT_PASSWORD: ${{ secrets.APPLE_DISTRIBUTION_CERT_PASSWORD }}
- run: |
- CERT_PATH="$RUNNER_TEMP/certificate.p12"
- KEYCHAIN_PATH="$RUNNER_TEMP/app-signing.keychain-db"
- KEYCHAIN_PASSWORD="$(openssl rand -hex 16)"
-
- echo "$CERT_BASE64" | base64 --decode -o "$CERT_PATH"
-
- security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
- security set-keychain-settings -lut 3600 "$KEYCHAIN_PATH"
- security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
-
- security import "$CERT_PATH" -k "$KEYCHAIN_PATH" -P "$CERT_PASSWORD" \
- -T /usr/bin/codesign -T /usr/bin/security
-
- security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
- security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
-
- echo "KEYCHAIN_PATH=$KEYCHAIN_PATH" >> "$GITHUB_ENV"
-
- - name: Install provisioning profile
- env:
- PROFILE_BASE64: ${{ secrets.APPLE_PROVISIONING_PROFILE_BASE64 }}
- run: |
- PROFILE_PATH="$RUNNER_TEMP/profile.mobileprovision"
- echo "$PROFILE_BASE64" | base64 --decode -o "$PROFILE_PATH"
-
- mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
- cp "$PROFILE_PATH" ~/Library/MobileDevice/Provisioning\ Profiles/
-
- - name: Read version from Xcode project
- run: |
- PBXPROJ="apps/stage-pocket/ios/App/App.xcodeproj/project.pbxproj"
- VERSION="$(sed -n 's/.*MARKETING_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')"
- BUILD_NUMBER="$(sed -n 's/.*CURRENT_PROJECT_VERSION = \(.*\);/\1/p' "$PBXPROJ" | head -1 | tr -d ' ')"
-
- echo "VERSION=$VERSION" >> "$GITHUB_ENV"
- echo "BUILD_NUMBER=$BUILD_NUMBER" >> "$GITHUB_ENV"
-
- - name: Archive iOS
- run: |
- xcodebuild archive \
- -project apps/stage-pocket/ios/App/App.xcodeproj \
- -scheme App \
- -configuration Release \
- -destination "generic/platform=iOS" \
- -archivePath "$RUNNER_TEMP/App.xcarchive" \
- CURRENT_PROJECT_VERSION=${{ env.BUILD_NUMBER }}
-
- - name: Export IPA
- run: |
- xcodebuild -exportArchive \
- -archivePath "$RUNNER_TEMP/App.xcarchive" \
- -exportPath "$RUNNER_TEMP/output" \
- -exportOptionsPlist apps/stage-pocket/ios/ExportOptions.plist
-
- - name: Rename IPA for distribution
- run: |
- IPA_SOURCE="$(find "$RUNNER_TEMP/output" -name '*.ipa' -print -quit)"
- IPA_NAME="${PRODUCT_NAME}-${VERSION}_(${BUILD_NUMBER})-ios.ipa"
-
- mv "$IPA_SOURCE" "$RUNNER_TEMP/output/$IPA_NAME"
-
- echo "IPA_NAME=$IPA_NAME" >> "$GITHUB_ENV"
-
- - name: Upload IPA artifact
- uses: actions/upload-artifact@v4
- with:
- name: ${{ env.IPA_NAME }}
- path: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
- if-no-files-found: error
-
- - name: Install Transporter
- if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }}
- run: |
- curl -fsSL "https://itunesconnect.apple.com/WebObjects/iTunesConnect.woa/ra/resources/download/public/Transporter__OSX/bin/" -o "$RUNNER_TEMP/itmstransporter.pkg"
- sudo installer -pkg "$RUNNER_TEMP/itmstransporter.pkg" -target /
- rm -f "$RUNNER_TEMP/itmstransporter.pkg"
-
- - name: Upload to TestFlight
- if: ${{ env.UPLOAD_TESTFLIGHT == 'true' }}
- uses: apple-actions/upload-testflight-build@v4
- with:
- app-path: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
- issuer-id: ${{ secrets.APPSTORE_CONNECT_API_ISSUER_ID }}
- api-key-id: ${{ secrets.APPSTORE_CONNECT_API_KEY_ID }}
- api-private-key: ${{ secrets.APPSTORE_CONNECT_API_KEY }}
-
- - name: Upload to GitHub Releases
- if: ${{ env.RELEASE_TAG != '' }}
- uses: softprops/action-gh-release@v2
- with:
- files: ${{ runner.temp }}/output/${{ env.IPA_NAME }}
- append_body: true
- fail_on_unmatched_files: true
- tag_name: ${{ env.RELEASE_TAG }}
-
- - name: Clean up signing
- if: always()
- run: |
- if [ -n "$KEYCHAIN_PATH" ]; then
- security delete-keychain "$KEYCHAIN_PATH" 2>/dev/null || true
- fi
- rm -f ~/Library/MobileDevice/Provisioning\ Profiles/*.mobileprovision
-
- publish-gitcode-release:
- name: Publish GitCode Release Mirror
- needs: build
- if: ${{ always() && github.repository == 'moeru-ai/airi' && needs.build.result == 'success' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && inputs.tag != '')) }}
- continue-on-error: true
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Validate release mirror tools
- run: |
- gh --version
- jq --version
- curl --version
-
- - name: Publish release assets to GitCode
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
- GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
- GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
- GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- run: bash .github/scripts/publish-gitcode-release.sh
diff --git a/.github/workflows/release-tamagotchi-steam.yml b/.github/workflows/release-tamagotchi-steam.yml
deleted file mode 100644
index a928300bd..000000000
--- a/.github/workflows/release-tamagotchi-steam.yml
+++ /dev/null
@@ -1,278 +0,0 @@
-name: Release Tamagotchi to Steam
-
-permissions:
- contents: read
- actions: read
-
-on:
- release:
- types:
- - prereleased
- workflow_dispatch:
- inputs:
- upload_to_steam:
- description: Upload the completed build to Steam
- required: true
- default: false
- type: boolean
- release_branch:
- description: Steam branch to set live
- required: true
- default: internal-test
- type: string
- build_description:
- description: Optional Steam build description
- required: false
- type: string
-
-concurrency:
- group: steam-deploy-tamagotchi-${{ github.event_name == 'workflow_dispatch' && inputs.release_branch || 'internal-test' }}
- cancel-in-progress: false
-
-jobs:
- build:
- name: Build ${{ matrix.depot }} depot
- runs-on: ${{ matrix.os }}
- env:
- VITE_DISTRIBUTION: steam
- VITE_DISABLE_CUSTOM_PROVIDERS: 'true'
- VITE_DISABLE_FLUX_PURCHASE: 'true'
- VITE_ENABLE_ANALYTICS: 'false'
- strategy:
- fail-fast: false
- matrix:
- include:
- - depot: windows
- os: windows-latest
- builder-args: --windows --x64
- arch: x64
-
- - depot: macos
- os: macos-26
- builder-args: --macos --arm64
- arch: arm64
-
- - depot: linux
- os: ubuntu-latest
- builder-args: --linux --x64
- arch: x64
-
- steps:
- # The Linux desktop build needs more space than the default hosted runner provides.
- - name: Free Linux disk space
- if: matrix.depot == 'linux'
- uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
-
- - uses: actions/checkout@v6
-
- - name: Select Xcode 26.2
- if: matrix.depot == 'macos'
- run: |
- sudo xcode-select -s /Applications/Xcode_26.2.app
- xcodebuild -version
-
- - name: Show macOS toolchain
- if: matrix.depot == 'macos'
- run: |
- xcodebuild -version
- actool --version
-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
-
- - name: Install macOS system dependencies for node-canvas
- if: matrix.depot == 'macos'
- run: brew install pkg-config cairo pango jpeg giflib librsvg
-
- - run: pnpm install --frozen-lockfile
-
- - run: pnpm run build:packages
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: '10.0.x'
-
- - name: Setup Godot
- uses: chickensoft-games/setup-godot@v2
- with:
- version: '4.7.1'
- use-dotnet: true
- include-templates: true
-
- - name: Export Godot Stage for Windows
- if: matrix.depot == 'windows'
- working-directory: ./engines/stage-tamagotchi-godot
- shell: bash
- run: |
- mkdir -p out/win
- godot --headless --export-release "Windows Desktop" out/win/godot-stage.exe
-
- - name: Export Godot Stage for macOS
- if: matrix.depot == 'macos'
- working-directory: ./engines/stage-tamagotchi-godot
- run: |
- mkdir -p out/mac
- godot --headless --export-release "macOS" out/mac/godot-stage.app
-
- - name: Clean Godot Stage for Steam on macOS
- if: matrix.depot == 'macos'
- working-directory: ./engines/stage-tamagotchi-godot
- run: |
- # NOTICE:
- # SteamPipe excludes *.pdb files after Electron signs the parent app.
- # Removing Godot debug symbols and AppleDouble files before signing keeps macOS sealed resources aligned with the installed bundle.
- # Source/context: Steam internal-test BuildID 24160350 and the game-ci SteamPipe FileExclusion "*.pdb" behavior.
- # Removal condition: SteamPipe preserves these resources or the embedded Godot bundle no longer contains them.
- find out/mac/godot-stage.app -name '*.pdb' -delete
- find out/mac/godot-stage.app -name '._*' -delete
-
- - name: Export Godot Stage for Linux
- if: matrix.depot == 'linux'
- working-directory: ./engines/stage-tamagotchi-godot
- run: |
- mkdir -p out/linux
- godot --headless --export-release "Linux ${{ matrix.arch }}" out/linux/godot-stage
-
- - name: Build Windows app
- if: matrix.depot == 'windows'
- run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build macOS app
- if: matrix.depot == 'macos'
- run: |
- echo "$CSC_CONTENT" | base64 --decode > apps/stage-tamagotchi/apple-developer-code-signing.p12
- export CSC_LINK="./apple-developer-code-signing.p12"
- pnpm run -F @proj-airi/stage-tamagotchi build
- pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- CSC_CONTENT: ${{ secrets.CSC_CONTENT }}
- CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
- APPLE_ID: ${{ secrets.APPLE_DEVELOPER_APPLE_ID }}
- APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
-
- - name: Build Linux app
- if: matrix.depot == 'linux'
- run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=never
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Package Windows depot
- if: matrix.depot == 'windows'
- shell: pwsh
- run: |
- New-Item -ItemType Directory -Force steam-content/windows | Out-Null
- Copy-Item apps/stage-tamagotchi/dist/win-unpacked/* steam-content/windows -Recurse -Force
- tar -cf steam-content-windows.tar -C steam-content windows
-
- - name: Package macOS depot
- if: matrix.depot == 'macos'
- run: |
- app_path="apps/stage-tamagotchi/dist/mac-arm64/AIRI.app"
- if [[ ! -d "$app_path" ]]; then
- echo "::error::No macOS app bundle was produced."
- exit 1
- fi
-
- mkdir -p steam-content/macos
- ditto --norsrc "$app_path" steam-content/macos/AIRI.app
- find steam-content/macos -name '._*' -delete
- COPYFILE_DISABLE=1 tar -cf steam-content-macos.tar -C steam-content macos
-
- - name: Package Linux depot
- if: matrix.depot == 'linux'
- run: |
- mkdir -p steam-content/linux
- cp -a apps/stage-tamagotchi/dist/linux-unpacked/. steam-content/linux/
- tar -cf steam-content-linux.tar -C steam-content linux
-
- - name: Upload depot artifact
- uses: actions/upload-artifact@v7
- with:
- name: steam-content-${{ matrix.depot }}
- path: steam-content-${{ matrix.depot }}.tar
- if-no-files-found: error
- overwrite: true
-
- deploy:
- name: Upload to Steam
- if: ${{ github.event_name == 'release' || inputs.upload_to_steam }}
- needs: build
- runs-on: ubuntu-latest
- env:
- STEAM_APP_ID: '3885340'
- STEAM_DEBUG_BRANCH: 'false'
- STEAM_RELEASE_BRANCH: ${{ github.event_name == 'workflow_dispatch' && inputs.release_branch || 'internal-test' }}
- STEAM_WINDOWS_DEPOT_ID: '3885342'
- steps:
- - name: Validate Steam branch
- env:
- RELEASE_BRANCH: ${{ env.STEAM_RELEASE_BRANCH }}
- run: |
- if [[ ! "$RELEASE_BRANCH" =~ ^[A-Za-z0-9._-]+$ ]]; then
- echo "::error::Steam release branch may contain only letters, numbers, dots, underscores, and hyphens."
- exit 1
- fi
-
- if [[ "$RELEASE_BRANCH" == "default" ]]; then
- echo '::error::Steam release branch "default" must be promoted manually.'
- exit 1
- fi
-
- - name: Normalize Steam build description
- id: metadata
- uses: actions/github-script@v9
- env:
- INPUT_BUILD_DESCRIPTION: ${{ inputs.build_description }}
- RELEASE_TAG: ${{ github.event.release.tag_name }}
- with:
- result-encoding: string
- script: |
- const source = process.env.RELEASE_TAG || process.env.GITHUB_SHA
- const fallback = `AIRI ${source} Steam candidate`
- const normalizedDescription = (process.env.INPUT_BUILD_DESCRIPTION || fallback)
- .replace(/[\r\n\t]+/g, ' ')
- .replace(/[\u0000-\u001f\u007f]+/g, ' ')
- .replaceAll('\\', '/')
- .replaceAll('"', "'")
- .replace(/ {2,}/g, ' ')
- .trim()
- .slice(0, 512)
-
- return normalizedDescription || fallback
-
- - name: Download Steam depots
- uses: actions/download-artifact@v5
- with:
- pattern: steam-content-*
- path: steam-artifacts
- merge-multiple: true
-
- - name: Prepare Steam upload root
- run: |
- mkdir -p steam-build
- tar -xf steam-artifacts/steam-content-windows.tar -C steam-build
- tar -xf steam-artifacts/steam-content-macos.tar -C steam-build
- tar -xf steam-artifacts/steam-content-linux.tar -C steam-build
-
- - name: Deploy desktop builds to Steam
- uses: game-ci/steam-deploy@v3.2.0
- with:
- username: ${{ secrets.STEAM_USERNAME }}
- configVdf: ${{ secrets.STEAM_CONFIG_VDF }}
- appId: ${{ env.STEAM_APP_ID }}
- firstDepotIdOverride: ${{ env.STEAM_WINDOWS_DEPOT_ID }}
- buildDescription: ${{ steps.metadata.outputs.result }}
- rootPath: steam-build
- depot1Path: windows
- depot2Path: macos
- depot3Path: linux
- releaseBranch: ${{ env.STEAM_RELEASE_BRANCH }}
- debugBranch: ${{ env.STEAM_DEBUG_BRANCH }}
diff --git a/.github/workflows/release-tamagotchi.yml b/.github/workflows/release-tamagotchi.yml
deleted file mode 100644
index 95d09d79f..000000000
--- a/.github/workflows/release-tamagotchi.yml
+++ /dev/null
@@ -1,665 +0,0 @@
-name: Release Tamagotchi
-
-permissions:
- contents: write
- actions: read
-
-env:
- BUNDLE_NAME: ''
- DEB_BUNDLE_NAME: ''
- RPM_BUNDLE_NAME: ''
- FLATPAK_BUNDLE_NAME: ''
- PRODUCT_NAME: 'AIRI'
- VERSION: ''
-
-on:
- release:
- types:
- - prereleased
- workflow_dispatch:
- inputs:
- build_only:
- description: Build only
- required: false
- default: false
- type: boolean
- artifacts_only:
- description: Build and upload artifacts only
- required: false
- default: false
- type: boolean
- tag:
- description: Specific tag/commit for the release (leave empty to auto-detect latest tag)
- required: false
- type: string
- platform:
- description: Platform
- type: choice
- options:
- - all
- - windows
- - macos
- - linux
- schedule:
- - cron: '0 0 * * *'
-
-jobs:
- build:
- name: Build
- continue-on-error: ${{ matrix.skip }} # WORKAROUND: Skipping subsequent steps without failing the whole workflow
- env:
- VITE_ENABLE_ANALYTICS: ${{ ((github.event_name == 'release') || (github.event_name == 'workflow_dispatch' && !inputs.build_only)) && 'true' || 'false' }}
- strategy:
- matrix:
- include:
-
- - os: macos-15-intel
- artifact: darwin-x64
- target: x86_64-apple-darwin
- arch: x64
- builder-args: --macos --x64
- skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'macos' }}
-
- - os: macos-26
- artifact: darwin-arm64
- target: aarch64-apple-darwin
- builder-args: --macos --arm64
- arch: arm64
- skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'macos' }}
-
- - os: ubuntu-latest
- artifact: linux-x64
- target: x86_64-unknown-linux-gnu
- builder-args: --linux --x64
- arch: x64
- skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'linux' }}
-
- - os: ubuntu-24.04-arm
- artifact: linux-arm64
- target: aarch64-unknown-linux-gnu
- builder-args: --linux --arm64
- arch: arm64
- skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'linux' }}
-
- - os: windows-latest
- artifact: windows-x64-setup
- target: x86_64-pc-windows-msvc
- builder-args: --windows --x64
- arch: x64
- skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'windows' }}
-
- runs-on: ${{ matrix.os }}
- steps:
- - name: Skip the build (fail fast)
- run: |
- echo "Skipping build for ${{ matrix.os }}..."
- echo "This will fail the job, but don't panic—this is expected when not building for all."
- echo "This will be improved in the future."
- exit ${{ matrix.skip && '1' || '0' }}
-
- # Why?
- #
- # failed to build archive at `/home/runner/work/airi/airi/target/x86_64-unknown-linux-gnu/release/deps/libapp_lib.rlib`:
- # No space left on device (os error 28)
- - name: Free Disk Space
- if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
- uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
-
- - uses: actions/checkout@v6
-
- - name: macOS Select Xcode 26.2
- if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
- run: |
- sudo xcode-select -s /Applications/Xcode_26.2.app
- xcodebuild -version
-
- - name: macOS Show Toolchain
- if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
- run: |
- xcodebuild -version
- actool --version
-
- # NOTICE:
- # pnpm 11 does not ship a working standalone executable for Intel macOS.
- # Install pnpm through npm so it uses the Node.js runtime from setup-node.
- # https://github.com/pnpm/pnpm/issues/11423
- # Remove this branch when the project uses pnpm 12 or later.
- - uses: actions/setup-node@v6
- if: matrix.os == 'macos-15-intel'
- with:
- node-version: 26.7.0
-
- - name: Install pnpm with npm
- if: matrix.os == 'macos-15-intel'
- run: npm install --global pnpm@11.24.0
-
- - name: Get pnpm store path
- if: matrix.os == 'macos-15-intel'
- id: pnpm-store
- run: echo "path=$(pnpm store path --silent)" >> "$GITHUB_OUTPUT"
-
- - name: Cache pnpm store
- if: matrix.os == 'macos-15-intel'
- uses: actions/cache@v5
- with:
- path: ${{ steps.pnpm-store.outputs.path }}
- key: ${{ runner.os }}-${{ runner.arch }}-pnpm-store-${{ hashFiles('pnpm-lock.yaml') }}
- restore-keys: |
- ${{ runner.os }}-${{ runner.arch }}-pnpm-store-
-
- - uses: pnpm/setup@v2
- if: matrix.os != 'macos-15-intel'
- with:
- runtime: node@26.7.0
- cache: true
- install: false
-
- - name: Install macOS System Dependencies for node-canvas
- if: runner.os == 'macOS'
- run: |
- brew install pkg-config cairo pango jpeg giflib librsvg
-
- - run: pnpm install --frozen-lockfile
-
- # ---------
- # Build
- # ---------
-
- - run: pnpm run build:packages
-
- - name: Setup .NET
- uses: actions/setup-dotnet@v4
- with:
- dotnet-version: '10.0.x'
-
- - name: Setup Godot
- uses: chickensoft-games/setup-godot@v2
- with:
- version: '4.7.1'
- use-dotnet: true
- include-templates: true
-
- - name: Export Godot Stage (Windows)
- if: matrix.os == 'windows-latest'
- working-directory: ./engines/stage-tamagotchi-godot
- shell: bash
- run: |
- mkdir -p out/win
- godot --headless --export-release "Windows Desktop" out/win/godot-stage.exe
-
- - name: Export Godot Stage (macOS)
- if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
- working-directory: ./engines/stage-tamagotchi-godot
- shell: bash
- run: |
- mkdir -p out/mac
- godot --headless --export-release "macOS" out/mac/godot-stage.app
-
- - name: Export Godot Stage (Linux)
- if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
- working-directory: ./engines/stage-tamagotchi-godot
- shell: bash
- run: |
- mkdir -p out/linux
- godot --headless --export-release "Linux ${{ matrix.arch }}" out/linux/godot-stage
-
- - name: Build (Windows Only) # Windows
- if: matrix.os == 'windows-latest'
- run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Build (macOS Only) # macOS
- if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
- run: |
- echo "$CSC_CONTENT" | base64 --decode > apps/stage-tamagotchi/apple-developer-code-signing.p12
- export CSC_LINK="./apple-developer-code-signing.p12"
- pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- CSC_CONTENT: ${{ secrets.CSC_CONTENT }}
- CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
- APPLE_ID: ${{ secrets.APPLE_DEVELOPER_APPLE_ID }}
- APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_DEVELOPER_APPLE_APP_SPECIFIC_PASSWORD }}
- APPLE_TEAM_ID: ${{ secrets.APPLE_DEVELOPER_TEAM_ID }}
-
- - name: Build (Linux Only) # Linux
- if: matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm'
- run: pnpm run -F @proj-airi/stage-tamagotchi build && pnpm -F @proj-airi/stage-tamagotchi exec electron-builder build ${{ matrix.builder-args }} --publish=${{ (inputs.build_only || inputs.artifacts_only) && 'never' || 'onTagOrDraft' }}
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Setup Flatpak (Linux Only)
- if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }}
- run: |
- sudo apt update
- sudo apt install -y flatpak flatpak-builder elfutils
- flatpak --version
- flatpak --user remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo
-
- - name: Build Flatpak (Linux Only) # Flatpak
- if: ${{ matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- mkdir -p dist
- # Auto-install required SDK/Platform/BaseApp from Flathub in user scope
- flatpak-builder --user --install-deps-from=flathub ./flatpak ai.moeru.airi.flatpak.yml --force-clean
- flatpak build-export ./flatpak-repo ./flatpak
- export FLATPAK_OUTPUT_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-output-filename flatpak)
- flatpak build-bundle ./flatpak-repo dist/${FLATPAK_OUTPUT_NAME} ai.moeru.airi
-
- # ---------
- # Nightly (schedule) builds only
- # ---------
-
- - name: Get Artifacts Envs (Nightly + Windows Only)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name)" >> $env:GITHUB_ENV
- echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version)" >> $env:GITHUB_ENV
-
- - name: Get Artifacts Envs (Nightly + Non-Windows)
- if: ${{ github.event_name == 'schedule' && matrix.os != 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version)" >> $GITHUB_ENV
-
- - name: Get Artifacts Envs (Nightly + macOS Only)
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'macos-26' || matrix.os == 'macos-15-intel') }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name)" >> $GITHUB_ENV
-
- - name: Rename Artifacts (Nightly)
- if: ${{ github.event_name == 'schedule' }}
- run:
- pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }}
-
- - name: Get Linux Artifact Names (Nightly + Linux Only)
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "DEB_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename deb)" >> $GITHUB_ENV
- echo "RPM_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename rpm)" >> $GITHUB_ENV
- echo "FLATPAK_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename flatpak)" >> $GITHUB_ENV
-
- - name: Upload Artifacts (Nightly + Non-Linux)
- if: ${{ github.event_name == 'schedule' && (matrix.os != 'ubuntu-latest' && matrix.os != 'ubuntu-24.04-arm') }}
- id: unsigned-artifacts-nightly
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Artifacts (Nightly + Linux deb)
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.DEB_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.DEB_BUNDLE_NAME }}
-
- - name: Upload Artifacts (Nightly + Linux rpm)
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.RPM_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.RPM_BUNDLE_NAME }}
-
- - name: Upload Flatpak Artifact (Nightly + Linux Only)
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.FLATPAK_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.FLATPAK_BUNDLE_NAME }}
-
- - name: Sign Windows Artifacts with SignPath (Nightly + Windows Only)
- id: signpath-nightly-windows
- continue-on-error: true
- uses: signpath/github-action-submit-signing-request@v2
- if: ${{ github.event_name == 'schedule' && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
- with:
- api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
- organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
- project-slug: 'airi'
- signing-policy-slug: 'test-signing'
- artifact-configuration-slug: ci-github-actions-artifacts-windows
- github-artifact-id: '${{ steps.unsigned-artifacts-nightly.outputs.artifact-id }}'
- wait-for-completion: true
- wait-for-completion-timeout-in-seconds: 900 # 15 minutes
- download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
- output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
-
- - name: Warn When Windows Signing Fails (Nightly)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
- run: |
- echo "::warning::SignPath Windows signing failed for nightly build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
-
- - name: Upload Signed Artifacts (Nightly + Non-Linux)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome == 'success' }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }}
- overwrite: true
-
- - name: Upload Unsigned Windows Artifacts Fallback (Nightly)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- overwrite: true
-
- # NOTICE: Electron Builder generates latest-x64.yml during the build step, before this workflow
- # submits the Windows installer to SignPath. SignPath re-signs the .exe and changes its bytes,
- # which changes the updater hashes too. Regenerating latest-x64.yml from the signed installer keeps
- # the published Windows update metadata aligned with the final released artifact.
- - name: Regenerate Windows latest-x64.yml (Nightly + Windows Only)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome == 'success' }}
- run: |
- pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
-
- - name: Regenerate Windows latest-x64.yml From Unsigned Artifact (Nightly Fallback)
- if: ${{ github.event_name == 'schedule' && matrix.os == 'windows-latest' && steps.signpath-nightly-windows.outcome != 'success' }}
- run: |
- pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
-
- # ---------
- # Workflow Dispatch only
- # ---------
-
- - name: Get Artifacts Envs (Manual + Windows Only)
- if: ${{ github.event_name == 'workflow_dispatch' && matrix.os == 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
- echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
-
- - name: Get Artifacts Envs (Manual + Non-Windows)
- if: ${{ github.event_name == 'workflow_dispatch' && matrix.os != 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
-
- - name: Get Artifacts Envs (Manual + macOS Only)
- if: ${{ github.event_name == 'workflow_dispatch' && (matrix.os == 'macos-26' || matrix.os == 'macos-15-intel') }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
-
- - name: Rename Artifacts (Manual)
- if: ${{ github.event_name == 'workflow_dispatch' }}
- run: |
- pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }} --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }}
-
- - name: Get Linux Artifact Names (Manual + Non-Release + Linux Only)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "DEB_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename deb --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
- echo "RPM_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename rpm --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
- echo "FLATPAK_BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-filename flatpak --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $GITHUB_ENV
-
- - name: Upload Artifacts (Manual + Non-Release + Non-Linux)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os != 'ubuntu-latest' && matrix.os != 'ubuntu-24.04-arm') }}
- id: unsigned-artifacts-workflow-dispatch
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Artifacts (Manual + Non-Release + Linux deb)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.DEB_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.DEB_BUNDLE_NAME }}
-
- - name: Upload Artifacts (Manual + Non-Release + Linux rpm)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.RPM_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.RPM_BUNDLE_NAME }}
-
- - name: Upload Flatpak Artifact (Manual + Non-Release + Linux)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'ubuntu-latest' || matrix.os == 'ubuntu-24.04-arm') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.FLATPAK_BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.FLATPAK_BUNDLE_NAME }}
-
- - name: Sign Windows Artifacts with SignPath (Manual + Windows Only)
- id: signpath-manual-windows
- continue-on-error: true
- uses: signpath/github-action-submit-signing-request@v2
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
- with:
- api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
- organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
- project-slug: 'airi'
- signing-policy-slug: 'test-signing'
- artifact-configuration-slug: ci-github-actions-artifacts-windows
- github-artifact-id: '${{ steps.unsigned-artifacts-workflow-dispatch.outputs.artifact-id }}'
- wait-for-completion: true
- wait-for-completion-timeout-in-seconds: 900 # 15 minutes
- download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
- output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
-
- - name: Warn When Windows Signing Fails (Manual)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' && steps.signpath-manual-windows.outcome != 'success' }}
- run: |
- echo "::warning::SignPath Windows signing failed for workflow_dispatch artifact build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
-
- - name: Move Signed Artifacts (Manual + Release + Windows Only)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' && steps.signpath-manual-windows.outcome == 'success' }}
- run: |
- Move-Item -Force apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Signed Artifacts (Manual + Windows Only)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && (matrix.os == 'windows-latest') }}
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
- overwrite: true
-
- # NOTICE: Electron Builder already wrote latest-x64.yml before SignPath signed the installer.
- # After the signed .exe replaces the unsigned one, we must rebuild latest-x64.yml so its hashes
- # describe the final Windows artifact that users will actually download.
- - name: Regenerate Windows latest-x64.yml (Manual + Windows Only)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && inputs.artifacts_only && matrix.os == 'windows-latest' }}
- run: |
- pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
-
- - name: Upload To GitHub Releases (Manual + Release + Overwrite Release)
- if: ${{ github.event_name == 'workflow_dispatch' && !inputs.build_only && !inputs.artifacts_only }}
- uses: softprops/action-gh-release@v2
- with:
- files: |
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.exe
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.zip
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.dmg
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.deb
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.rpm
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.flatpak
- apps/stage-tamagotchi/bundle/latest-*.yml
- append_body: true
- tag_name: ${{ inputs.tag }}
-
- # ---------
- # Version push
- # ---------
-
- - name: Rename Artifacts (Automatic)
- if: ${{ github.event_name == 'release' }}
- run: |
- pnpm run -F @proj-airi/stage-tamagotchi rename-artifacts ${{ matrix.target }} --release --auto-tag
-
- - name: Get Artifacts Envs (Automatic + Windows Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
- echo "VERSION=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-version --release ${{ !inputs.build_only && !inputs.artifacts_only }} --tag ${{ inputs.tag }} --auto-tag ${{ !inputs.build_only }})" >> $env:GITHUB_ENV
-
- - name: Get Artifacts Envs (Automatic + Non-Windows)
- if: ${{ github.event_name == 'release' && matrix.os != 'windows-latest' }}
- working-directory: ./apps/stage-tamagotchi
- run: |
- echo "BUNDLE_NAME=$(pnpm exec tsx scripts/artifacts-metadata.ts ${{ matrix.target }} --get-bundle-name --release --auto-tag)" >> $GITHUB_ENV
-
- - name: Upload Artifacts (Automatic + Windows Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
- id: unsigned-artifacts-release
- uses: actions/upload-artifact@v7
- with:
- name: ${{ env.BUNDLE_NAME }}
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Sign Windows Artifacts with SignPath (Nightly + Windows Only)
- id: signpath-release-windows
- continue-on-error: true
- uses: signpath/github-action-submit-signing-request@v2
- if: ${{ github.event_name == 'release' && (matrix.os == 'windows-latest' && github.repository == 'moeru-ai/airi') }}
- with:
- api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
- organization-id: '${{ secrets.SIGNPATH_ORGANIZATION_ID }}'
- project-slug: 'airi'
- signing-policy-slug: 'test-signing'
- artifact-configuration-slug: ci-github-actions-artifacts-windows
- github-artifact-id: '${{ steps.unsigned-artifacts-release.outputs.artifact-id }}'
- wait-for-completion: true
- wait-for-completion-timeout-in-seconds: 900 # 15 minutes
- download-signed-artifact-timeout-in-seconds: 900 # 15 minutes
- output-artifact-directory: apps/stage-tamagotchi/bundle/signed/windows/
-
- - name: Warn When Windows Signing Fails (Release)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' && steps.signpath-release-windows.outcome != 'success' }}
- run: |
- echo "::warning::SignPath Windows signing failed for release build. Continuing with unsigned artifact at apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}."
-
- - name: Move Signed Artifacts (Automatic + Windows Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' && steps.signpath-release-windows.outcome == 'success' }}
- run: |
- Move-Item -Force apps/stage-tamagotchi/bundle/signed/windows/${{ env.BUNDLE_NAME }} apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- # NOTICE: The root cause here is the build/signing order: latest-x64.yml is produced by Electron
- # Builder before SignPath re-signs the installer. That post-build signing step mutates the .exe,
- # so the updater metadata becomes stale. Recomputing latest-x64.yml after the signed file is moved
- # into its final bundle path makes the release upload self-consistent again.
- - name: Regenerate Windows latest manifest (Automatic + Windows Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
- run: |
- pnpm -F @proj-airi/stage-tamagotchi run regenerate-windows-latest --input apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} --output apps/stage-tamagotchi/bundle/latest-x64.yml --version ${{ env.VERSION }}
-
- - name: Upload Distribution Artifact (Automatic + macOS arm64 Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'macos-26' }}
- uses: actions/upload-artifact@v7
- with:
- name: distribution-osx
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Distribution Artifact (Automatic + Windows Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'windows-latest' }}
- uses: actions/upload-artifact@v7
- with:
- name: distribution-win
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Distribution Artifact (Automatic + Linux x64 Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'ubuntu-latest' }}
- uses: actions/upload-artifact@v7
- with:
- name: distribution-linux-x64
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload Distribution Artifact (Automatic + Linux arm64 Only)
- if: ${{ github.event_name == 'release' && matrix.os == 'ubuntu-24.04-arm' }}
- uses: actions/upload-artifact@v7
- with:
- name: distribution-linux-arm64
- path: apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }}
-
- - name: Upload To GitHub Releases (Automatic)
- if: ${{ github.event_name == 'release' }}
- uses: softprops/action-gh-release@v2
- with:
- # Possible auto updater files:
- # Windows: latest-x64.yml
- # macOS: latest-arm64-mac.yml, latest-x64-mac.yml
- # Linux: latest-arm64-linux.yml (arm64), latest-x64-linux.yml (x64)
- files: |
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.exe
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.zip
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.dmg
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.deb
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.rpm
- apps/stage-tamagotchi/bundle/${{ env.PRODUCT_NAME }}-*.flatpak
- apps/stage-tamagotchi/bundle/latest-*.yml
- append_body: true
-
- publish-gitcode-release:
- name: Publish GitCode Release Mirror
- needs: build
- if: ${{ always() && github.repository == 'moeru-ai/airi' && (github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && !inputs.build_only && !inputs.artifacts_only && inputs.tag != '')) }}
- continue-on-error: true
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v6
-
- - name: Validate release mirror tools
- run: |
- gh --version
- jq --version
- curl --version
-
- - name: Publish release assets to GitCode
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- GITCODE_TOKEN: ${{ secrets.GITCODE_TOKEN }}
- GITCODE_OWNER: ${{ secrets.GITCODE_OWNER }}
- GITCODE_REPO: ${{ secrets.GITCODE_REPO }}
- GITCODE_RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- run: bash .github/scripts/publish-gitcode-release.sh
-
- publish-distribution:
- name: Publish Distribution
- needs: build
- if: ${{ github.event_name == 'release' }}
- runs-on: ubuntu-latest
- strategy:
- matrix:
- include:
- - artifact_name: distribution-osx
- platform: osx
- - artifact_name: distribution-win
- platform: win
- - artifact_name: distribution-linux-x64
- platform: linux
- - artifact_name: distribution-linux-arm64
- platform: linux
- steps:
- - name: Resolve release version
- run: |
- echo "VERSION=${GITHUB_REF_NAME#v}" >> $GITHUB_ENV
-
- - name: Download distribution artifact
- uses: actions/download-artifact@v5
- with:
- name: ${{ matrix.artifact_name }}
- path: dist/${{ matrix.artifact_name }}
-
- - name: Resolve artifact path
- run: |
- echo "ARTIFACT_PATH=$(find dist/${{ matrix.artifact_name }} -maxdepth 1 -mindepth 1 | head -n 1)" >> $GITHUB_ENV
-
- - name: Setup butler
- uses: remarkablegames/setup-butler@v2
-
- # https://itch.io/docs/butler/pushing.html
- - name: Publish distribution artifact to itch.io
- run: butler push "${{ env.ARTIFACT_PATH }}" nekomeowww/airi:${{ matrix.platform }} --userversion "${{ env.VERSION }}"
- env:
- BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
diff --git a/.github/workflows/release-vsix.yaml b/.github/workflows/release-vsix.yaml
index e9bbf2855..89a53f93c 100644
--- a/.github/workflows/release-vsix.yaml
+++ b/.github/workflows/release-vsix.yaml
@@ -16,11 +16,13 @@ jobs:
steps:
- uses: actions/checkout@v6
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
+
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
- name: Configure npm registry
uses: actions/setup-node@v6
@@ -29,11 +31,11 @@ jobs:
# https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
registry-url: 'https://registry.npmjs.org'
- - run: pnpm install --frozen-lockfile
- - run: pnpm run build:packages
- - run: pnpm run -F @proj-airi/airi-plugin-vscode build
- - run: pnpm run -F vscode-airi build
- - run: pnpm run publish
+ - run: bun install --frozen-lockfile
+ - run: bun run build:packages
+ - run: bun run --filter @proj-airi/airi-plugin-vscode build
+ - run: bun run --filter vscode-airi build
+ - run: bun run publish
working-directory: ./integrations/vscode/vscode-airi
env:
VSCE_TOKEN: ${{ secrets.VSCE_TOKEN }}
diff --git a/.github/workflows/sponsors-svg.yml b/.github/workflows/sponsors-svg.yml
index 42ac623a4..ecf92a8c2 100644
--- a/.github/workflows/sponsors-svg.yml
+++ b/.github/workflows/sponsors-svg.yml
@@ -21,18 +21,20 @@ jobs:
with:
ref: main
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
- - run: pnpm install --frozen-lockfile
+ node-version: 26.7.0
+
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
+ - run: bun install --frozen-lockfile
- name: Pull with rebase
run: git pull origin main --rebase --autostash
- name: Generate sponsors svg
- run: pnpm run sponsors:generate
+ run: bun run sponsors:generate
- name: Remove private SponsorKit cache
run: rm -f docs/content/public/assets/sponsors/.cache.json
diff --git a/.github/workflows/update-nix-assets-hash.yaml b/.github/workflows/update-nix-assets-hash.yaml
deleted file mode 100644
index 47bd4df47..000000000
--- a/.github/workflows/update-nix-assets-hash.yaml
+++ /dev/null
@@ -1,75 +0,0 @@
-name: Update Nix assets Hash
-
-on:
- workflow_dispatch:
- push:
- branches:
- - main
- paths:
- - 'apps/stage-web/package.json'
- - 'apps/stage-web/vite.config.ts'
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- update:
- if: github.event_name == 'workflow_dispatch' || !github.event.repository.fork
- runs-on: ubuntu-latest
- steps:
- # Why?
- #
- # failed to
- # $ nix build .#airi-pnpm-deps
- # > airi-pnpm-deps> Running phase: fixupPhase
- # > error: writing to file: No space left on device
- - name: Free Disk Space
- uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
-
- - uses: actions/checkout@v6
- with:
- ref: main # Use main regardless of workflow_dispatch branch
- # Authenticate git with PAT so that pushing to head retriggers CI
- token: ${{ secrets.HASH_UPDATE_TOKEN }}
-
- - uses: cachix/install-nix-action@v31
- with:
- extra_nix_config: experimental-features = nix-command flakes
-
- - name: Update Hash
- run: nix/update-assets-hash.sh
-
- - name: Check for changes
- id: changes
- run: |
- if git diff --quiet; then
- echo "has_changes=false" >> "$GITHUB_OUTPUT"
- else
- echo "has_changes=true" >> "$GITHUB_OUTPUT"
- fi
-
- - name: Create PR
- if: steps.changes.outputs.has_changes == 'true'
- env:
- # Create PR with PAT to trigger CI
- GH_TOKEN: ${{ secrets.HASH_UPDATE_TOKEN }}
- run: |
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
- git add nix/assets-hash.txt
- git commit -m 'chore(nix): update assets hash'
- git push -f origin HEAD:chore/update-nix-assets-hash
-
- # Create PR if it doesn't already exist
- existing_pr=$(gh pr list --head chore/update-nix-assets-hash --json number -q '.[0].number' 2>/dev/null || true)
- if [ -z "$existing_pr" ]; then
- gh pr create \
- --base main \
- --head chore/update-nix-assets-hash \
- --title 'chore(nix): update assets hash' \
- --body 'Auto-generated by CI to keep Nix asset hash up-to-date.'
- fi
-
- # Enable auto-merge so PR merges once checks pass
- gh pr merge --squash --auto --delete-branch chore/update-nix-assets-hash
diff --git a/.github/workflows/update-nix-pnpm-deps-hash.yaml b/.github/workflows/update-nix-pnpm-deps-hash.yaml
deleted file mode 100644
index fc7403212..000000000
--- a/.github/workflows/update-nix-pnpm-deps-hash.yaml
+++ /dev/null
@@ -1,74 +0,0 @@
-name: Update Nix pnpmDeps Hash
-
-on:
- workflow_dispatch:
- push:
- branches:
- - main
- paths:
- - 'pnpm-lock.yaml'
-
-permissions:
- contents: write
- pull-requests: write
-
-jobs:
- update:
- if: github.event_name == 'workflow_dispatch' || !github.event.repository.fork
- runs-on: ubuntu-latest
- steps:
- # Why?
- #
- # failed to
- # $ nix build .#airi-pnpm-deps
- # > airi-pnpm-deps> Running phase: fixupPhase
- # > error: writing to file: No space left on device
- - name: Free Disk Space
- uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1
-
- - uses: actions/checkout@v6
- with:
- ref: main # Use main regardless of workflow_dispatch branch
- # Authenticate git with PAT so that pushing to head retriggers CI
- token: ${{ secrets.HASH_UPDATE_TOKEN }}
-
- - uses: cachix/install-nix-action@v31
- with:
- extra_nix_config: experimental-features = nix-command flakes
-
- - name: Update Hash
- run: nix/update-pnpm-deps-hash.sh
-
- - name: Check for changes
- id: changes
- run: |
- if git diff --quiet; then
- echo "has_changes=false" >> "$GITHUB_OUTPUT"
- else
- echo "has_changes=true" >> "$GITHUB_OUTPUT"
- fi
-
- - name: Create PR
- if: steps.changes.outputs.has_changes == 'true'
- env:
- # Create PR with PAT to trigger CI
- GH_TOKEN: ${{ secrets.HASH_UPDATE_TOKEN }}
- run: |
- git config user.name "github-actions[bot]"
- git config user.email "github-actions[bot]@users.noreply.github.com"
- git add nix/pnpm-deps-hash.txt
- git commit -m 'chore(nix): update pnpmDeps hash'
- git push -f origin HEAD:chore/update-nix-pnpm-deps-hash
-
- # Create PR if it doesn't already exist
- existing_pr=$(gh pr list --head chore/update-nix-pnpm-deps-hash --json number -q '.[0].number' 2>/dev/null || true)
- if [ -z "$existing_pr" ]; then
- gh pr create \
- --base main \
- --head chore/update-nix-pnpm-deps-hash \
- --title 'chore(nix): update pnpmDeps hash' \
- --body 'Auto-generated by CI to keep Nix pnpmDeps hash up-to-date.'
- fi
-
- # Enable auto-merge so PR merges once checks pass
- gh pr merge --squash --auto --delete-branch chore/update-nix-pnpm-deps-hash
diff --git a/.github/workflows/update-post-release-readme-urls.yml b/.github/workflows/update-post-release-readme-urls.yml
deleted file mode 100644
index 3b63984cc..000000000
--- a/.github/workflows/update-post-release-readme-urls.yml
+++ /dev/null
@@ -1,165 +0,0 @@
-name: Update (Post Release) README URLs
-
-on:
- release:
- types:
- - published
- - released
- workflow_dispatch:
- inputs:
- tag:
- description: 'Release tag (e.g., v0.10.2)'
- required: true
- type: string
-
-permissions:
- contents: read
-
-concurrency:
- group: update-post-release-readme-urls
- cancel-in-progress: true
-
-jobs:
- update:
- # Keep prereleases from replacing the stable README download buttons.
- if: github.event_name == 'workflow_dispatch' || !github.event.release.prerelease
- runs-on: ubuntu-latest
- env:
- README_BRANCH: automation/update-readme-release-links
- RELEASE_TAG: ${{ github.event.release.tag_name || inputs.tag }}
- steps:
- - uses: actions/checkout@v6
- with:
- ref: main
- persist-credentials: false
-
- - uses: pnpm/setup@v2
- with:
- runtime: node@26.7.0
- cache: true
- install: false
- - run: pnpm install --frozen-lockfile
-
- - name: Update README download links
- run: pnpm -F @proj-airi/stage-tamagotchi exec tsx scripts/update-readme-download-links.ts "$RELEASE_TAG"
-
- - name: Pull with rebase
- run: git pull origin main --rebase --autostash
-
- - name: Prepare README release update
- id: prepare
- env:
- GH_TOKEN: ${{ secrets.PAT_SLASH_COMMAND_DISPATCH }}
- run: |
- # The updater is only allowed to publish the generated README link changes.
- if ! git diff --quiet -- . ':(exclude)README.md' ':(exclude)docs/README*.md'; then
- echo "::error::Unexpected non-README changes were generated."
- git status --short
- exit 1
- fi
-
- git add README.md 'docs/README*.md'
- if git diff --cached --quiet; then
- echo "No README release links changed."
- echo "changed=false" >> "$GITHUB_OUTPUT"
- exit 0
- fi
-
- if [ -z "$GH_TOKEN" ]; then
- echo "::error::PAT_SLASH_COMMAND_DISPATCH is required so the README update PR triggers required checks."
- exit 1
- fi
-
- git config user.name "github-actions[bot]"
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
- git remote set-url origin "https://x-access-token:${GH_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
-
- REMOTE_BRANCH="$(git ls-remote --heads origin "refs/heads/${README_BRANCH}")"
- if [ -n "$REMOTE_BRANCH" ]; then
- git fetch origin "${README_BRANCH}:refs/remotes/origin/${README_BRANCH}"
- fi
-
- git checkout -B "$README_BRANCH"
- PR_TITLE="chore: update README release artifacts to ${RELEASE_TAG}"
- git commit -m "$PR_TITLE"
- git push --force-with-lease origin "$README_BRANCH"
-
- echo "changed=true" >> "$GITHUB_OUTPUT"
- echo "pr_title=$PR_TITLE" >> "$GITHUB_OUTPUT"
-
- - name: Create or update README release PR
- if: steps.prepare.outputs.changed == 'true'
- uses: actions/github-script@v8
- env:
- PR_TITLE: ${{ steps.prepare.outputs.pr_title }}
- with:
- github-token: ${{ secrets.PAT_SLASH_COMMAND_DISPATCH }}
- script: |
- const { owner, repo } = context.repo;
- const head = `${owner}:${process.env.README_BRANCH}`;
- const body = [
- '## Description',
- '',
- `Updates stable desktop download links after the ${process.env.RELEASE_TAG} release.`,
- '',
- '## Linked Issues',
- '',
- 'None.',
- '',
- '## Additional Context',
- '',
- 'Generated by the post-release README workflow.',
- ].join('\n');
-
- const { data: openPullRequests } = await github.rest.pulls.list({
- owner,
- repo,
- state: 'open',
- base: 'main',
- head,
- per_page: 1,
- });
-
- let pullRequest;
- if (openPullRequests.length > 0) {
- const { data } = await github.rest.pulls.update({
- owner,
- repo,
- pull_number: openPullRequests[0].number,
- title: process.env.PR_TITLE,
- body,
- });
- pullRequest = data;
- core.info(`Updated pull request #${pullRequest.number}.`);
- }
- else {
- const { data } = await github.rest.pulls.create({
- owner,
- repo,
- base: 'main',
- head: process.env.README_BRANCH,
- title: process.env.PR_TITLE,
- body,
- });
- pullRequest = data;
- core.info(`Created pull request #${pullRequest.number}.`);
- }
-
- if (!pullRequest.auto_merge) {
- await github.graphql(
- `mutation EnableAutoMerge($pullRequestId: ID!) {
- enablePullRequestAutoMerge(input: {
- pullRequestId: $pullRequestId
- mergeMethod: SQUASH
- }) {
- pullRequest {
- number
- }
- }
- }`,
- {
- pullRequestId: pullRequest.node_id,
- },
- );
- core.info(`Enabled squash auto-merge for pull request #${pullRequest.number}.`);
- }
diff --git a/.github/workflows/upload-crowdin-glossary.yml b/.github/workflows/upload-crowdin-glossary.yml
index 102393afb..bd7176f72 100644
--- a/.github/workflows/upload-crowdin-glossary.yml
+++ b/.github/workflows/upload-crowdin-glossary.yml
@@ -19,18 +19,20 @@ jobs:
fi
- uses: actions/checkout@v6
- - uses: pnpm/setup@v2
+ - uses: actions/setup-node@v6
with:
- runtime: node@26.7.0
- cache: true
- install: false
+ node-version: 26.7.0
- - run: pnpm install --frozen-lockfile
+ - uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: 1.4.2
+
+ - run: bun install --frozen-lockfile
# The TBX file is generated, never committed, so it cannot drift from terms.yaml.
# The schema also rejects a malformed entry here, before anything reaches Crowdin.
- name: Generate TBX File
- run: pnpm -F @proj-airi/i18n glossary:build
+ run: bun run --filter @proj-airi/i18n glossary:build
# NOTICE: Crowdin does not document whether an import merges into the existing
# concepts or adds duplicates beside them, and a merge cannot be undone. This
diff --git a/.gitignore b/.gitignore
index f64c80a87..394e8c8b6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -133,9 +133,6 @@ plugins/development
plugins-local
plugins-development
-apps/stage-pocket/ios/buildServer.json
-apps/stage-pocket/airi-pocket.jks
-apps/stage-tamagotchi/electron.vite.config.*.mjs
docs/superpowers
docs/research
diff --git a/.tool-versions b/.tool-versions
index 44a3a1ff5..a795b4d24 100644
--- a/.tool-versions
+++ b/.tool-versions
@@ -1,3 +1,3 @@
nodejs 26.7.0
-pnpm 11.24.0
+bun 1.4.2
dotnet 10
diff --git a/.vscode/launch.json b/.vscode/launch.json
index a2941aa94..16194642f 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -1,30 +1,14 @@
{
"version": "0.2.0",
"configurations": [
- {
- "name": "Debug Electron",
- "type": "node",
- "request": "launch",
- // Where to run the command
- "runtimeExecutable": "pnpm",
- "runtimeArgs": ["-F", "@proj-airi/stage-tamagotchi", "exec", "electron-vite", "--sourcemap"],
- // Open terminal when debugging starts (Optional)
- // Useful to see console.logs
- // "console": "integratedTerminal",
- // "internalConsoleOptions": "neverOpen",
- "env": {
- "REMOTE_DEBUGGING_PORT": "9222"
- }
- },
{
"name": "Debug Minecraft Service",
"type": "node",
"request": "launch",
// Where to run the command
- // Where to run the command
- "runtimeExecutable": "pnpm",
- "runtimeArgs": ["-F", "@proj-airi/minecraft-bot", "exec", "tsx"],
- "args": ["--env-file=.env", "--env-file-if-exists=.env.local", "src/main.ts"],
+ "runtimeExecutable": "bun",
+ "runtimeArgs": ["run", "start"],
+ "cwd": "${workspaceFolder}/integrations/minecraft",
"envFile": "${workspaceFolder}/integrations/minecraft/.env",
// Open terminal when debugging starts (Optional)
// Useful to see console.logs
@@ -34,36 +18,16 @@
"/**"
]
},
- {
- "name": "Debug Renderer Process",
- "port": 9222,
- "request": "attach",
- "type": "chrome",
- "webRoot": "${workspaceFolder}/src/renderer",
- "timeout": 60000,
- "presentation": {
- "hidden": true
- }
- },
{
"name": "Debug VSCode Extension",
"type": "extensionHost",
"request": "launch",
"args": [
- "--extensionDevelopmentPath=${workspaceFolder}/plugins/airi-plugin-vscode"
+ "--extensionDevelopmentPath=${workspaceFolder}/integrations/vscode/airi-plugin-vscode"
],
"outFiles": [
- "${workspaceFolder}/plugins/airi-plugin-vscode/dist/**/*.cjs"
+ "${workspaceFolder}/integrations/vscode/airi-plugin-vscode/dist/**/*.cjs"
]
}
- ],
- "compounds": [
- {
- "name": "Debug All",
- "configurations": ["Debug Electron", "Debug Renderer Process"],
- "presentation": {
- "order": 1
- }
- }
]
}
diff --git a/AGENTS.md b/AGENTS.md
index da4df0664..f23b39101 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -1,21 +1,18 @@
-# Project AIRI Agent Guide
+# Project Moeka Agent Guide
Concise but detailed reference for contributors working across the `moeru-ai/airi` monorepo. Improve code when you touch it; avoid one-off patterns.
## Tech Stack (by surface)
-- **Desktop (stage-tamagotchi)**: Electron, Vue, Vite, TypeScript, Pinia, VueUse, Eventa (IPC/RPC), UnoCSS, Vitest, ESLint.
- **Web (stage-web)**: Vue 3 + Vue Router, Vite, TypeScript, Pinia, VueUse, UnoCSS, Vitest, ESLint. Backend: WIP.
-- **Mobile (stage-pocket)**: Vue 3 + Vue Router, Vite, TypeScript, Pinia, VueUse, UnoCSS, Vitest, ESLint, Kotlin, Swift, Capacitor.
+- **Other apps**: `apps/ui-server-auth` (auth UI for the hosted auth service), `apps/component-calling` (realtime audio demo).
- **UI/Shared Packages**:
- - `packages/stage-ui`: Core business components, composables, stores shared by stage-web & stage-tamagotchi (heart of stage work).
+ - `packages/stage-ui`: Core business components, composables, stores shared by the stage apps (heart of stage work).
- `packages/stage-ui-three`: Three.js bindings + Vue components.
- - `packages/stage-ui-pixi`: Planned Pixi bindings.
- - `packages/stage-shared`: Shared logic across stage-ui, stage-ui-three, stage-web, stage-tamagotchi.
+ - `packages/stage-shared`: Shared logic across stage-ui, stage-ui-three, and stage-web.
- `packages/ui`: Standardized primitives (inputs, textarea, buttons, layout) built on reka-ui; minimal business logic.
- `packages/i18n`: Central translations.
- Server channel: `packages/server-runtime`, `packages/server-sdk`, `packages/server-shared` (power `services/` and `plugins/`).
- - Legacy: `crates/` (old Tauri desktop; current desktop is Electron).
## Structure & Responsibilities
@@ -27,18 +24,18 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
- `server/docker-compose.yaml`: complete local backend stack.
- **Apps**
- `apps/stage-web`: Web app; composables/stores in `src/composables`, `src/stores`; pages in `src/pages`; devtools in `src/pages/devtools`; router config via `vite.config.ts`.
- - `apps/stage-tamagotchi`: Electron app; renderer pages in `src/renderer/pages`; devtools in `src/renderer/pages/devtools`; settings layout at `src/renderer/layouts/settings.vue`; router config via `electron.vite.config.ts`.
- - Settings/devtools routes rely on ` meta: layout: settings `; ensure routes/icons are registered accordingly (`apps/stage-tamagotchi/src/renderer/layouts/settings.vue`, `apps/stage-web/src/layouts/settings.vue`).
+ - `apps/ui-server-auth`: Auth UI served by the hosted auth service; pages in `src/pages`.
+ - Settings/devtools routes rely on ` meta: layout: settings `; ensure routes/icons are registered accordingly (`apps/stage-web/src/layouts/settings.vue`).
- Shared page bases: `packages/stage-pages`.
- - Stage pages: `apps/stage-web/src/pages`, `apps/stage-tamagotchi/src/renderer/pages` (plus devtools folders).
+ - Stage pages: `apps/stage-web/src/pages` (plus the devtools folder).
- **Stage UI internals** (`packages/stage-ui/src`)
- Providers: `stores/providers.ts` and `stores/providers/` (standardized provider definitions).
- - Modules: `stores/modules/` (AIRI orchestration building blocks).
+ - Modules: `stores/modules/` (Moeka orchestration building blocks).
- Composables: `composables/` (business-oriented Vue helpers).
- Components: `components/`; scenarios in `components/scenarios/` for page/use-case-specific pieces.
- Stories: `packages/stage-ui/stories`, `packages/stage-ui/histoire.config.ts` (e.g. `components/misc/Button.story.vue`).
-- **IPC/Eventa**: Always use `@moeru/eventa` for type-safe, framework/runtime-agnostic IPC/RPC. Define contracts centrally (e.g., `apps/stage-tamagotchi/src/shared`) and follow usage patterns in `apps/stage-tamagotchi/src/main/services/electron` for main/renderer integration.
-- **Dependency Injection**: Use `injeca` for services/electron modules/plugins/frontend; see `apps/stage-tamagotchi/src/main/index.ts` for composition patterns.
+- **IPC/Eventa**: Always use `@moeru/eventa` for type-safe, framework/runtime-agnostic event and RPC contracts. Define contracts next to the module that owns them (e.g., `packages/stage-shared/src/beat-sync/eventa.ts`, `packages/plugin-protocol/src/types/events.ts`, `server/packages/server-sdk-shared/src/v2.ts`).
+- **Dependency Injection**: Use `injeca` for services and server/plugin composition; see `server/apps/api/src/app.ts` and `server/apps/auth/src/server.ts` for composition patterns.
- **Build/CI/Lint**: `.github/workflows` for pipelines; `eslint.config.js` for lint rules.
- **Styles**: UnoCSS config at `uno.config.ts`; check `apps/stage-web/src/styles` for existing animations; prefer UnoCSS over Tailwind.
@@ -46,48 +43,47 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
- `packages/stage-ui`: Core stage business components/composables/stores.
- `src/stores/providers.ts` and `src/stores/providers/`: provider definitions (standardized).
- - `src/stores/modules/`: AIRI orchestration modules.
+ - `src/stores/modules/`: Moeka orchestration modules.
- `src/composables/`: reusable Vue composables (business-oriented).
- `src/components/`: business components; `src/components/scenarios/` for page/use-case-specific pieces.
- Stories: `packages/stage-ui/stories`, `packages/stage-ui/histoire.config.ts` (e.g. `components/misc/Button.story.vue`).
- `packages/stage-ui-three`: Three.js bindings + Vue components.
- `packages/stage-ui-pixi`: Planned Pixi bindings.
-- `packages/stage-shared`: Shared logic across stage-ui, stage-ui-three, stage-web, stage-tamagotchi.
+- `packages/stage-shared`: Shared logic across stage-ui, stage-ui-three, and stage-web.
- `packages/ui`: Standardized primitives (inputs/textarea/buttons/layout) built on reka-ui.
- `packages/i18n`: All translations.
- Hosted backend: `server/apps/api`, `server/apps/auth`, `server/packages`, and local tooling under `server/dev`.
- Server channel: `packages/server-runtime`, `packages/server-sdk`, `packages/server-shared` (power `services/` and `plugins/`).
-- Legacy desktop: `crates/` (old Tauri; Electron is current).
-- Pages: `packages/stage-pages` (shared bases); `apps/stage-web/src/pages` and `apps/stage-tamagotchi/src/renderer/pages` for app-specific pages; devtools live in each app’s `.../pages/devtools`.
-- Router configs: `apps/stage-web/vite.config.ts`, `apps/stage-tamagotchi/electron.vite.config.ts`.
-- Devtools/layouts: `apps/stage-tamagotchi/src/renderer/layouts/settings.vue`, `apps/stage-web/src/layouts/settings.vue`.
-- IPC/Eventa contracts/examples: `apps/stage-tamagotchi/src/shared`, `apps/stage-tamagotchi/src/main/services/electron`.
-- DI examples: `apps/stage-tamagotchi/src/main/index.ts` (injeca).
+- Pages: `packages/stage-pages` (shared bases); `apps/stage-web/src/pages` and `apps/ui-server-auth/src/pages` for app-specific pages; devtools live in `apps/stage-web/src/pages/devtools`.
+- Router config: `apps/stage-web/vite.config.ts`.
+- Devtools/layouts: `apps/stage-web/src/layouts/settings.vue`.
+- IPC/Eventa contracts/examples: `packages/stage-shared/src/beat-sync/eventa.ts`, `packages/plugin-protocol/src/types/events.ts`.
+- DI examples: `server/apps/api/src/app.ts`, `server/apps/auth/src/server.ts` (injeca).
- Styles: `uno.config.ts` (UnoCSS), `apps/stage-web/src/styles` (animations/reference).
- Build pipeline refs: `.github/workflows`; lint rules in `eslint.config.js`.
- Documented solutions: `docs/solutions/` records past fixes and workflow learnings, organized by category with YAML frontmatter (`module`, `tags`, `problem_type`); relevant when implementing, debugging, or verifying in documented areas.
- Tailwind/UnoCSS: prefer UnoCSS; if standardizing styles, add shortcuts/rules/plugins in `uno.config.ts`.
-## Commands (pnpm with filters)
+## Commands (Bun with filters)
-> Use pnpm workspace filters to scope tasks. Examples below are generic; replace the filter with the target workspace name (e.g. `@proj-airi/stage-tamagotchi`, `@proj-airi/stage-web`, `@proj-airi/stage-ui`, etc.).
+> Use Bun workspace filters to scope tasks. Examples below are generic; replace the filter with the target workspace name (e.g. `@proj-airi/stage-web`, `@proj-airi/stage-ui`, etc.).
- **Typecheck**
- - `pnpm -F typecheck`
- - Example: `pnpm -F @proj-airi/stage-tamagotchi typecheck` (runs `tsc` + `vue-tsc`).
+ - `bun run --filter typecheck`
+ - Example: `bun run --filter @proj-airi/stage-web typecheck` (runs `tsc` + `vue-tsc`).
- **Unit tests (Vitest)**
- - Targeted: `pnpm exec vitest run `
- e.g. `pnpm exec vitest run apps/stage-tamagotchi/src/renderer/stores/tools/builtin/widgets.test.ts`
- - Workspace: `pnpm -F exec vitest run`
- e.g. `pnpm -F @proj-airi/stage-tamagotchi exec vitest run`
- - Root `pnpm test:run`: runs all tests across registered projects. If no tests are found, check `vitest.config.ts` include patterns.
- - Root `vitest.config.ts` includes `apps/stage-tamagotchi` and other projects; each app/package can have its own `vitest.config`.
+ - Targeted: `bunx vitest run `
+ e.g. `bunx vitest run packages/stage-ui/src/stores/providers/provider.test.ts`
+ - Workspace: `bun run --filter exec vitest run`
+ e.g. `bun run --filter @proj-airi/stage-web exec vitest run`
+ - Root `bun run test:run`: runs all tests across registered projects. If no tests are found, check `vitest.config.ts` include patterns.
+ - Root `vitest.config.ts` lists the registered test projects; each app/package can have its own `vitest.config`.
- **Lint**
- - `pnpm lint` and `pnpm lint:fix`
- - Formatting is handled via ESLint; `pnpm lint:fix` applies formatting.
+ - `bun run lint` and `bun run lint:fix`
+ - Formatting is handled via ESLint; `bun run lint:fix` applies formatting.
- **Build**
- - `pnpm -F build`
- - Example: `pnpm -F @proj-airi/stage-tamagotchi build` (typecheck + electron-vite build).
+ - `bun run --filter build`
+ - Example: `bun run --filter @proj-airi/stage-web build` (typecheck + vite build).
## Before You Start
@@ -103,7 +99,7 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
- For testing, Vitest, regression reproduction, mocks, or test import-boundary work, always use [`enforce-rules-for-vitest` skill](.agents/skills/enforce-rules-for-vitest/SKILL.md).
- For UnoCSS, Vue styling, UI components, animations, icons, or color-mode work, always use [`enforce-rules-for-unocss` skill](.agents/skills/enforce-rules-for-unocss/SKILL.md).
- For web or Electron workflows that upload a local file through an HTML input, a dynamically created input, or a file chooser, invoke [`$use-agent-browser-with-input-file`](.agents/skills/use-agent-browser-with-input-file/SKILL.md). Also invoke `$agent-browser`, and invoke `$agent-browser-electron` when the target is Electron.
-- For AIRI Live2D, VRM, or MMD import and rendering tests across stage-web, stage-tamagotchi, or stage-pocket, invoke [`$use-agent-browser-for-airi`](.agents/skills/use-agent-browser-for-airi/SKILL.md). It invokes `$use-agent-browser-with-input-file` for the upload mechanism and adds AIRI-specific routes, state preparation, format behavior, and renderer verification.
+- For Moeka Live2D, VRM, or MMD import and rendering tests in stage-web, invoke [`$use-agent-browser-for-airi`](.agents/skills/use-agent-browser-for-airi/SKILL.md). It invokes `$use-agent-browser-with-input-file` for the upload mechanism and adds Moeka-specific routes, state preparation, format behavior, and renderer verification.
- For editing, writing, refactoring, re-writing code, submitting issues, Pull Requests, and docs, comments, invoke [`$simple-english`](./agents/skills/simple-english/SKILL.md).
## Development Practices
@@ -120,7 +116,7 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
## TypeScript / IPC / Tools
- Keep JSON Schemas provider-compliant (explicit `type: object`, required fields; avoid unbounded records).
-- For Electron, and backend related packages, use `injeca` for dependency management; avoid new class hierarchies unless extending browser APIs (classes are harder to mock/test).
+- For backend related packages, use `injeca` for dependency management; avoid new class hierarchies unless extending browser APIs (classes are harder to mock/test).
- Centralize Eventa contracts; use `@moeru/eventa` for all events.
- Import types from the module or package that owns the contract. Do not redeclare external/public contracts locally just to use a narrower subset, and do not route type imports through local runtime assembly modules when the original side-effect-free type source is available.
- Omit TypeScript and JavaScript source extensions from relative imports, dynamic imports, and re-exports. Write `./module` instead of `./module.ts` or `./module.js`; keep extensions only when the runtime or asset format requires them.
@@ -140,7 +136,7 @@ Concise but detailed reference for contributors working across the `moeru-ai/air
### Glossary
`packages/i18n/glossary/terms.yaml` gives the approved English term for each product concept.
-`pnpm -F @proj-airi/i18n glossary:build` writes the TBX file that Crowdin imports. `schema.ts`
+`bun run --filter @proj-airi/i18n glossary:build` writes the TBX file that Crowdin imports. `schema.ts`
documents each field.
- Read `terms.yaml` before you write or change a string that a user sees. Use the term it gives.
@@ -240,7 +236,7 @@ as a first language.
- Unlisted actions run in the caller renderer. Their mutations become full-state proposals when `state: true`.
- Keep synchronization and persistence as separate boundaries. Give persisted synchronized state one explicit persistence owner.
- Do not add bidirectional persistence composables or storage-event listeners to synchronized state. Use explicit persistence commands.
-- Set the leadership mode explicitly for every Electron renderer. Utility and minimal windows must use `follower-only`.
+- Set the leadership mode explicitly for every browser window or tab that runs the app. Utility and minimal windows must use `follower-only`.
- Add a multi-window regression test for synchronization changes. A remote snapshot must not produce a local synchronized-state proposal. If a watcher calls a synchronized action, verify that repeated calls converge without repeated side effects.
### Readability Refactors
@@ -267,9 +263,9 @@ as a first language.
- Rebase pulls; branch naming `username/feat/short-name`; clear commit messages (gitmoji is prohibited).
- Summarize changes, how tested (commands), and follow-ups.
- Improve legacy you touch; avoid one-off patterns.
-- Keep changes scoped; use workspace filters (`pnpm -F
-
-
-
-
-
-
-
-
-