Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
996eb46
feat: Add Candy-style companion features
roseonlineownz-lab Mar 30, 2026
ecdf99c
feat: Add Candy-style character metadata and consumer pages
roseonlineownz-lab Mar 30, 2026
bf61307
feat: add nsfw companion runtime integration
roseonlineownz-lab Mar 30, 2026
b428bb6
feat: refine nsfw hermes chat routing
roseonlineownz-lab Mar 30, 2026
046903f
feat: add nsfw image prompt contracts
roseonlineownz-lab Mar 30, 2026
585e644
feat: add nsfw media queue and comfyui consumer
roseonlineownz-lab Mar 30, 2026
8ae17a6
feat: add default comfyui workflow builder
roseonlineownz-lab Mar 30, 2026
cfc1091
chore: target local comfyui checkpoint
roseonlineownz-lab Mar 30, 2026
9e2ad4a
feat: tune default pony workflow quality
roseonlineownz-lab Mar 31, 2026
939a3cd
fix: polish nsfw image runtime status handling
roseonlineownz-lab Mar 31, 2026
50b18b0
feat: add comfyui smoke test tuning
roseonlineownz-lab Mar 31, 2026
5add5e3
docs: record sd turbo smoke test status
roseonlineownz-lab Mar 31, 2026
620b9bd
fix: harden comfyui job timeouts
roseonlineownz-lab Mar 31, 2026
706b509
feat: surface nsfw gallery job status
roseonlineownz-lab Mar 31, 2026
171bae2
feat: poll nsfw gallery while jobs run
roseonlineownz-lab Mar 31, 2026
bc0cbcb
fix: harden nsfw comfy workflow fallback
roseonlineownz-lab Mar 31, 2026
27c8bb0
fix: reconcile comfyui execution errors correctly
roseonlineownz-lab Apr 1, 2026
2e2de38
feat: enrich comfy execution metadata
roseonlineownz-lab Apr 1, 2026
6ffd7cb
feat: add comfyui fallback host support
roseonlineownz-lab Apr 1, 2026
c5ccd93
chore: add .env to gitignore
roseonlineownz-lab Apr 1, 2026
bacd366
feat: validate sd-turbo cpu fallback path
roseonlineownz-lab Apr 1, 2026
bd52ee0
fix: use process.on instead of named export for Node 22 compatibility
roseonlineownz-lab Apr 3, 2026
ffa3bf2
chore: sync fork with upstream moeru-ai/airi
roseonlineownz-lab Apr 3, 2026
1591b2f
fix: update pnpm-lock.yaml for server-schema valibot dependency
roseonlineownz-lab Apr 3, 2026
238373b
fix: resolve TypeScript errors from merge
roseonlineownz-lab Apr 3, 2026
cda2e99
fix: resolve more TypeScript issues in Hermes and Auth
roseonlineownz-lab Apr 3, 2026
ae8e5e1
fix: lint fixes and sync with upstream
roseonlineownz-lab Apr 5, 2026
0f17c52
chore: add keystore files to .gitignore
roseonlineownz-lab Apr 6, 2026
a3c9d93
ci: fix invalid macos-26 runner to macos-14
roseonlineownz-lab Apr 10, 2026
52f29d6
ci: upgrade pnpm/action-setup to v5 and skip free-disk-space on arm r…
roseonlineownz-lab Apr 10, 2026
5ef02d1
fix: make OAuth credentials optional for AIRI server
roseonlineownz-lab Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions .github/workflows/release-tamagotchi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ jobs:
builder-args: --macos --x64
skip: ${{ inputs.platform != '' && inputs.platform != 'all' && inputs.platform != 'macos' }}

- os: macos-26
- os: macos-14
artifact: darwin-arm64
target: aarch64-apple-darwin
builder-args: --macos --arm64
Expand Down Expand Up @@ -102,19 +102,19 @@ jobs:
# 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'
if: matrix.os == 'ubuntu-latest'
uses: jlumbroso/free-disk-space@main

- uses: actions/checkout@v6

- name: macOS Select Xcode 26.2
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-14'
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'
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-14'
run: |
xcodebuild -version
actool --version
Expand All @@ -141,7 +141,7 @@ jobs:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Build (macOS Only) # macOS
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-26'
if: matrix.os == 'macos-15-intel' || matrix.os == 'macos-14'
run: |
echo "$CSC_CONTENT" | base64 --decode > apps/stage-tamagotchi/apple-developer-code-signing.p12
export CSC_LINK="./apple-developer-code-signing.p12"
Expand Down Expand Up @@ -197,7 +197,7 @@ jobs:
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') }}
if: ${{ github.event_name == 'schedule' && (matrix.os == 'macos-14' || 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
Expand Down Expand Up @@ -294,7 +294,7 @@ jobs:
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') }}
if: ${{ github.event_name == 'workflow_dispatch' && (matrix.os == 'macos-14' || 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
Expand Down Expand Up @@ -468,7 +468,7 @@ jobs:

# https://itch.io/docs/butler/pushing.html
- name: Upload macOS (arm64) to itch.io
if: ${{ github.event_name == 'release' && matrix.os == 'macos-26' }}
if: ${{ github.event_name == 'release' && matrix.os == 'macos-14' }}
run: butler push apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} nekomeowww/airi:osx --userversion ${{ env.VERSION }}
env:
BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
Expand All @@ -484,3 +484,5 @@ jobs:
run: butler push apps/stage-tamagotchi/bundle/${{ env.BUNDLE_NAME }} nekomeowww/airi:linux --userversion ${{ env.VERSION }}
env:
BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
env:
BUTLER_API_KEY: ${{ secrets.GAME_PUBLISHING_ITCHIO }}
Comment on lines +487 to +488
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove stray top-level env block from release workflow

The trailing env: mapping after the last upload step is indented outside the step/job structure, which makes .github/workflows/release-tamagotchi.yml invalid YAML. With this parse error, GitHub Actions cannot load the workflow, so release runs fail before any build job starts.

Useful? React with 👍 / 👎.

5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,15 @@ plugins-development

apps/stage-pocket/ios/buildServer.json
apps/stage-tamagotchi/electron.vite.config.*.mjs
.env
.docs/superpowers

# Tools - ghfs
.ghfs/

# Tools - Obsidian
.obsidian/

# Android keystore (secrets)
apps/stage-pocket/*.jks
apps/stage-pocket/*base64.txt
17 changes: 17 additions & 0 deletions apps/server/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,23 @@ services:
required: false
restart: unless-stopped

nsfw-image-consumer:
build:
context: ../..
dockerfile: apps/server/Dockerfile
command: ['pnpm', '-F', '@proj-airi/server', 'run', 'server', 'nsfw-image-consumer']
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
env_file:
- path: .env
required: false
- path: .env.local
required: false
restart: unless-stopped

volumes:
db_data:
driver: local
Expand Down
14 changes: 14 additions & 0 deletions apps/server/drizzle/0008_lazy_wallflower.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
ALTER TABLE "characters"
ADD COLUMN "visibility" text DEFAULT 'private' NOT NULL;
--> statement-breakpoint
ALTER TABLE "characters"
ADD COLUMN "nsfw_enabled" boolean DEFAULT false NOT NULL;
--> statement-breakpoint
ALTER TABLE "characters"
ADD COLUMN "nsfw_level" text DEFAULT 'none' NOT NULL;
--> statement-breakpoint
ALTER TABLE "characters"
ADD COLUMN "relationship_mode" text DEFAULT 'companion' NOT NULL;
--> statement-breakpoint
ALTER TABLE "characters"
ADD COLUMN "persona_profile" jsonb DEFAULT '{}'::jsonb NOT NULL;
8 changes: 8 additions & 0 deletions apps/server/drizzle/0009_nsfw_user_gating.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
ALTER TABLE "user"
ADD COLUMN "adult_verified" boolean DEFAULT false NOT NULL;

ALTER TABLE "user"
ADD COLUMN "allow_sensitive_content" boolean DEFAULT false NOT NULL;

ALTER TABLE "user"
ADD COLUMN "content_tier" text DEFAULT 'standard' NOT NULL;
36 changes: 36 additions & 0 deletions apps/server/drizzle/0010_nsfw_media.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
CREATE TABLE "image_jobs" (
"id" text PRIMARY KEY NOT NULL,
"user_id" text NOT NULL,
"character_id" text NOT NULL,
"route" text NOT NULL,
"status" text DEFAULT 'queued' NOT NULL,
"prompt" text NOT NULL,
"negative_prompt" text NOT NULL,
"scene_type" text,
"tags" text[] DEFAULT '{}' NOT NULL,
"params" jsonb DEFAULT '{}'::jsonb NOT NULL,
"result_media_id" text,
"error_message" text,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);

CREATE TABLE "gallery_items" (
"id" text PRIMARY KEY NOT NULL,
"user_id" text NOT NULL,
"character_id" text NOT NULL,
"image_job_id" text,
"media_id" text,
"title" text,
"prompt" text NOT NULL,
"negative_prompt" text NOT NULL,
"scene_type" text,
"tags" text[] DEFAULT '{}' NOT NULL,
"created_at" timestamp DEFAULT now() NOT NULL,
"updated_at" timestamp DEFAULT now() NOT NULL
);

ALTER TABLE "gallery_items"
ADD CONSTRAINT "gallery_items_image_job_id_image_jobs_id_fk"
FOREIGN KEY ("image_job_id") REFERENCES "public"."image_jobs"("id")
ON DELETE set null ON UPDATE no action;
14 changes: 14 additions & 0 deletions apps/server/drizzle/meta/_journal.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,20 @@
"when": 1775032828818,
"tag": "0008_gray_xavin",
"breakpoints": true
},
{
"idx": 9,
"version": "7",
"when": 1774803600000,
"tag": "0009_nsfw_user_gating",
"breakpoints": true
},
{
"idx": 10,
"version": "7",
"when": 1774807200000,
"tag": "0010_nsfw_media",
"breakpoints": true
}
]
}
1 change: 1 addition & 0 deletions apps/server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"dev": "pnpm run apply:env -- tsx --import ./instrumentation.mjs --watch src/bin/run.ts api",
"start": "pnpm run apply:env -- tsx --import ./instrumentation.mjs src/bin/run.ts api",
"server": "pnpm run apply:env -- tsx --import ./instrumentation.mjs src/bin/run.ts",
"nsfw:image-consumer": "pnpm run apply:env -- tsx --import ./instrumentation.mjs src/bin/run.ts nsfw-image-consumer",
"build": "tsc -b",
"typecheck": "tsc --noEmit",
"db:generate": "drizzle-kit generate",
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/app.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,13 @@ function createTestDeps() {
characterService: {} as any,
chatService: {} as any,
providerService: {} as any,
nsfwMediaService: {} as any,
fluxService: {} as any,
fluxTransactionService: {} as any,
stripeService: {} as any,
billingService: {} as any,
billingMq: {} as any,
nsfwImageMq: {} as any,
configKV: {
getOrThrow: vi.fn(async (key: string) => {
switch (key) {
Expand Down
34 changes: 34 additions & 0 deletions apps/server/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import type { ChatService } from './services/chats'
import type { ConfigKVService } from './services/config-kv'
import type { FluxService } from './services/flux'
import type { FluxTransactionService } from './services/flux-transaction'
import type { NsfwImageEvent } from './services/nsfw-image-events'
import type { NsfwMediaService } from './services/nsfw-media'
import type { ProviderService } from './services/providers'
import type { StripeService } from './services/stripe'
import type { HonoEnv } from './types/hono'
Expand Down Expand Up @@ -41,16 +43,20 @@ import { createCharacterRoutes } from './routes/characters'
import { createChatWsHandlers } from './routes/chat-ws'
import { createChatRoutes } from './routes/chats'
import { createFluxRoutes } from './routes/flux'
import { createNsfwMediaRoutes } from './routes/nsfw-media'
import { createV1CompletionsRoutes } from './routes/openai/v1'
import { createProviderRoutes } from './routes/providers'
import { createStripeRoutes } from './routes/stripe'
import { createUserRoutes } from './routes/users'
import { createBillingMq } from './services/billing/billing-events'
import { createBillingService } from './services/billing/billing-service'
import { createCharacterService } from './services/characters'
import { createChatService } from './services/chats'
import { createConfigKVService } from './services/config-kv'
import { createFluxService } from './services/flux'
import { createFluxTransactionService } from './services/flux-transaction'
import { createNsfwImageMq } from './services/nsfw-image-events'
import { createNsfwMediaService } from './services/nsfw-media'
import { createProviderService } from './services/providers'
import { createRequestLogService } from './services/request-log'
import { createStripeService } from './services/stripe'
Expand All @@ -63,11 +69,13 @@ interface AppDeps {
characterService: CharacterService
chatService: ChatService
providerService: ProviderService
nsfwMediaService: NsfwMediaService
fluxService: FluxService
fluxTransactionService: FluxTransactionService
stripeService: StripeService
billingService: BillingService
billingMq: MqService<BillingEvent>
nsfwImageMq: MqService<NsfwImageEvent>
configKV: ConfigKVService
redis: Redis
env: Env
Expand Down Expand Up @@ -175,6 +183,16 @@ export async function buildApp(deps: AppDeps) {
*/
.route('/api/v1/providers', createProviderRoutes(deps.providerService))

/**
* Current user settings routes.
*/
.route('/api/v1/users', createUserRoutes(deps.db))

/**
* NSFW media routes.
*/
.route('/api/v1/nsfw', createNsfwMediaRoutes(deps.nsfwMediaService, deps.nsfwImageMq))

/**
* Chat routes are handled by the chat service.
*/
Expand Down Expand Up @@ -318,6 +336,18 @@ export async function createApp() {
build: ({ dependsOn }) => createProviderService(dependsOn.db),
})

const nsfwMediaService = injeca.provide('services:nsfw-media', {
dependsOn: { db },
build: ({ dependsOn }) => createNsfwMediaService(dependsOn.db),
})

const nsfwImageMq = injeca.provide('services:nsfwImageMq', {
dependsOn: { redis, env: parsedEnv },
build: ({ dependsOn }) => createNsfwImageMq(dependsOn.redis, {
stream: dependsOn.env.NSFW_IMAGE_EVENTS_STREAM,
}),
})

const chatService = injeca.provide('services:chats', {
dependsOn: { db, otel },
build: ({ dependsOn }) => createChatService(dependsOn.db, dependsOn.otel?.engagement),
Expand Down Expand Up @@ -355,12 +385,14 @@ export async function createApp() {
characterService,
chatService,
providerService,
nsfwMediaService,
fluxService,
fluxTransactionService,
requestLogService,
stripeService,
billingService,
billingMq,
nsfwImageMq,
configKV,
redis,
env: parsedEnv,
Expand All @@ -372,11 +404,13 @@ export async function createApp() {
characterService: resolved.characterService,
chatService: resolved.chatService,
providerService: resolved.providerService,
nsfwMediaService: resolved.nsfwMediaService,
fluxService: resolved.fluxService,
fluxTransactionService: resolved.fluxTransactionService,
stripeService: resolved.stripeService,
billingService: resolved.billingService,
billingMq: resolved.billingMq,
nsfwImageMq: resolved.nsfwImageMq,
configKV: resolved.configKV,
redis: resolved.redis,
env: resolved.env,
Expand Down
Loading