Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
427216e
feat(mobile): add native direct client foundation
Crain99 Jul 15, 2026
509d2be
chore: normalize generated mobile project files
Crain99 Jul 15, 2026
2ec241e
feat(relay): add authenticated blind websocket router
Crain99 Jul 15, 2026
ec4ab77
ci: mark nested mobile builds non-interactive
Crain99 Jul 15, 2026
8b8f76a
chore(relay): normalize manifest
Crain99 Jul 15, 2026
306f67d
feat(relay): add encrypted desktop bridge and mobile transport
Crain99 Jul 15, 2026
ba33083
feat(relay): add desktop pairing and mobile QR setup
Crain99 Jul 15, 2026
5780a96
fix(ci): link iOS with the Xcode Swift toolchain
Crain99 Jul 15, 2026
70f0c91
feat(relay): add single-user deployment support
Crain99 Jul 15, 2026
da3eebf
chore(relay): add hardened single-host deployment
Crain99 Jul 15, 2026
84706b4
chore(relay): allow configurable Cargo registry
Crain99 Jul 15, 2026
275293f
fix(relay): support source replacement in container builds
Crain99 Jul 15, 2026
26194f1
fix(relay): stabilize mobile reconnects and large payloads
Crain99 Jul 15, 2026
5749163
feat(mobile): harden relay pairing and android delivery
Crain99 Jul 16, 2026
e7a692b
docs(mobile): record relay upload acceptance
Crain99 Jul 16, 2026
2e3375f
fix(relay): reset reconnect backoff after healthy sessions
Crain99 Jul 16, 2026
44ac0f4
test(relay): cover revocation replay and multi-device isolation
Crain99 Jul 16, 2026
fa0d83a
docs(android): link private release candidate
Crain99 Jul 16, 2026
e29d2a6
docs(android): use stable private releases link
Crain99 Jul 16, 2026
98f5f89
fix(mobile): retry relay fast enough after network restore
Crain99 Jul 16, 2026
01e35b5
fix(mobile): refine compact workspace layouts
Crain99 Jul 16, 2026
d145876
feat(relay): support self-hosted mobile endpoints
Crain99 Jul 16, 2026
27238bc
chore(mobile): publish universal Android packages
Crain99 Jul 16, 2026
46b4535
docs(mobile): remove deployment-specific references
Crain99 Jul 16, 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
90 changes: 90 additions & 0 deletions .github/workflows/mobile-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Mobile Release

on:
workflow_dispatch:
push:
tags:
- "mobile-v*"

permissions:
contents: write

jobs:
android:
name: Signed Android APK
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
run_install: false

- uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- uses: android-actions/setup-android@v3

- name: Install Android SDK, NDK and Rust target
run: sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"

- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android,armv7-linux-androideabi,i686-linux-android,x86_64-linux-android

- uses: swatinem/rust-cache@v2
with:
workspaces: "./mobile/src-tauri -> target"
shared-key: android-mobile-release

- run: pnpm install --frozen-lockfile

- name: Restore signing keystore
env:
KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
run: printf '%s' "$KEYSTORE_BASE64" | base64 --decode > "$RUNNER_TEMP/codeg-mobile-release.jks"

- name: Build signed release APK
env:
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
CODEG_ANDROID_KEYSTORE_PATH: ${{ runner.temp }}/codeg-mobile-release.jks
CODEG_ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
CODEG_ANDROID_KEY_ALIAS: ${{ secrets.ANDROID_KEY_ALIAS }}
CODEG_ANDROID_KEY_PASSWORD: ${{ secrets.ANDROID_KEY_PASSWORD }}
run: pnpm --dir mobile tauri android build --apk --ci

- name: Verify release signature
run: |
APK=mobile/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk
apksigner verify --verbose "$APK"
if apksigner verify --print-certs "$APK" | grep -q 'CN=Android Debug'; then
echo "::error::Release APK was signed with the Android debug certificate"
exit 1
fi

- name: Stage artifact
run: |
mkdir -p dist
cp mobile/src-tauri/gen/android/app/build/outputs/apk/universal/release/app-universal-release.apk \
dist/codeg-mobile-${GITHUB_REF_NAME}-universal.apk
sha256sum dist/*.apk > dist/SHA256SUMS.txt

- uses: actions/upload-artifact@v4
with:
name: codeg-mobile-android-${{ github.ref_name }}
path: dist/*
if-no-files-found: error

- name: Attach APK to GitHub Release
if: startsWith(github.ref, 'refs/tags/mobile-v')
uses: softprops/action-gh-release@v2
with:
files: dist/*
generate_release_notes: true
145 changes: 145 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@ on:
pull_request:

env:
CI: "true"
RUST_BACKTRACE: short
# Hosted Linux runners can exhaust their disk while compiling the full
# Tauri desktop test graph. CI does not need incremental objects or debug
# symbols, and disabling both keeps the matrix comfortably below the limit.
CARGO_INCREMENTAL: "0"
CARGO_PROFILE_DEV_DEBUG: "0"
CARGO_PROFILE_TEST_DEBUG: "0"
# Refuse to silently regenerate snapshots in CI — any drift must be
# accepted locally with `cargo insta review` and committed.
INSTA_UPDATE: "no"
Expand All @@ -17,6 +24,54 @@ concurrency:
cancel-in-progress: true

jobs:
relay:
name: Relay (fmt + clippy + test)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- uses: swatinem/rust-cache@v2
with:
workspaces: "./relay -> target"
shared-key: relay

- name: Format
run: cargo fmt --manifest-path relay/Cargo.toml --check

- name: Clippy
run: cargo clippy --manifest-path relay/Cargo.toml --all-targets -- -D warnings

- name: Test
run: cargo test --manifest-path relay/Cargo.toml

relay-bridge:
name: Relay bridge (fmt + clippy + test)
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
with:
components: rustfmt, clippy

- uses: swatinem/rust-cache@v2
with:
workspaces: "./relay-bridge -> target"
shared-key: relay-bridge

- name: Format
run: cargo fmt --manifest-path relay-bridge/Cargo.toml --check

- name: Clippy
run: cargo clippy --manifest-path relay-bridge/Cargo.toml --all-targets -- -D warnings

- name: Test
run: cargo test --manifest-path relay-bridge/Cargo.toml

frontend:
name: Frontend (lint + vitest + build)
# Frontend tests are OS-independent (jsdom + pure TS), keep on Linux
Expand Down Expand Up @@ -48,6 +103,96 @@ jobs:
- name: Static export build
run: pnpm build

android-mobile:
name: Android mobile compile smoke
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- name: Setup Java 17
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: 17

- name: Setup Android SDK and NDK
uses: android-actions/setup-android@v3

- name: Install Android packages
run: sdkmanager "platforms;android-36" "build-tools;36.0.0" "ndk;28.2.13676358"

- name: Install Rust Android target
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-linux-android

- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./mobile/src-tauri -> target"
shared-key: android-mobile-aarch64

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Generate Android project
env:
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
run: pnpm --dir mobile tauri android init --ci

- name: Build aarch64 debug APK
env:
NDK_HOME: ${{ env.ANDROID_HOME }}/ndk/28.2.13676358
run: pnpm --dir mobile tauri android build --apk --debug --target aarch64 --ci

ios-mobile:
name: iOS mobile simulator compile smoke
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
run_install: false

- name: Setup Node
uses: actions/setup-node@v4
with:
node-version: 24
cache: "pnpm"

- name: Install Rust iOS simulator target
uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-apple-ios-sim

- name: Rust cache
uses: swatinem/rust-cache@v2
with:
workspaces: "./mobile/src-tauri -> target"
shared-key: ios-mobile-aarch64-sim

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Generate iOS project
run: pnpm --dir mobile tauri ios init --ci

- name: Build iOS simulator app
run: pnpm --dir mobile tauri ios build --debug --target aarch64-sim --ci

rust:
name: Rust ${{ matrix.mode }} on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
Expand Down
11 changes: 11 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,12 @@ coverage/
*.p12
certificate-base64.txt

# Android signing material. Release credentials are injected from the local
# keychain or CI and must never be committed.
*.jks
*.keystore
keystore.properties

# Agent
.claude
.docs
Expand All @@ -52,6 +58,11 @@ docs/superpowers
# The binaries are platform-specific build artifacts; ship them through
# release.yml, not git.
src-tauri/binaries/
mobile/src-tauri/target/
relay/target/
relay-bridge/target/
relay-bridge.json
relay/deploy/relay.env

# pnpm local store (pnpm 11+)
.pnpm-store/
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,38 @@ Codeg ships three Rust binaries from a single workspace:
| `codeg-server` | Standalone HTTP + WebSocket server for browser/headless deployments | `pnpm server:build` / `pnpm server:dev` |
| `codeg-mcp` | Per-launch stdio MCP companion that surfaces the `delegate_to_agent` tool to agent CLIs (multi-agent collab) | `pnpm tauri:prepare-sidecars` (auto-invoked by `tauri dev` / `tauri build`) |

### Android mobile client

Codeg Mobile is a local, signed Tauri application for controlling Agents that
continue to run on a remote Codeg desktop/server. It is not a remote webpage
wrapper and does not execute Codex, Claude Code, OpenCode, ACP, a terminal, or
Git on the phone.

1. Install the signed universal APK on Android 7.0 or newer.
2. For the recommended Relay mode, open **Settings → Mobile Access** on the
desktop, create a one-time QR code, scan it on the phone, compare the
six-digit safety code and confirm the new phone on the desktop.
3. Relay works without a public IP or inbound port. Direct mode remains
available for a reachable HTTPS Codeg server and Web Service Token.
4. Pairing roots and Direct tokens are stored with Android Keystore; neither is
written to browser local storage.

Direct mode requires the phone to reach the Codeg HTTPS endpoint. The mobile
shell automatically reconnects WebSocket sessions after temporary network
loss and does not preserve unsent drafts across app restarts. Build and signing
details are in [`mobile/README.md`](mobile/README.md).
The Chinese end-user guide is in
[`docs/mobile/android-install-zh-CN.md`](docs/mobile/android-install-zh-CN.md).
To run your own encrypted Relay, follow
[`docs/relay/self-hosting-zh-CN.md`](docs/relay/self-hosting-zh-CN.md); the
desktop accepts any compatible WSS Relay and writes that endpoint into the
one-time pairing QR code.

The iOS project remains simulator-buildable, but this contribution does not
provide an IPA or TestFlight build because no Apple Developer distribution
certificate and provisioning profile are available. Android is the only
installable mobile package included with this pull request.

`codeg-mcp` must sit next to its parent binary at runtime — installers, the Docker image, and the Tauri sidecar bundler all place it next to `codeg` / `codeg-server`. Source builds and custom layouts can override the lookup with the `CODEG_MCP_BIN=/abs/path/codeg-mcp` env var. If the companion is missing, delegation is skipped (a single warning is logged) and the rest of the agent session keeps working.

### Development
Expand Down
30 changes: 30 additions & 0 deletions docs/design/mobile-prototype/brand-spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Codeg Mobile 品牌规范

## 核心 Token(OKLch)

```css
:root {
--bg: oklch(17.2% 0.017 248);
--surface: oklch(21.7% 0.021 245);
--fg: oklch(97.6% 0.006 240);
--muted: oklch(70.8% 0.025 246);
--border: oklch(31.8% 0.034 247);
--accent: oklch(70.4% 0.144 254);
}
```

## 字体

- Display:Inter / SF Pro Display / PingFang SC / HarmonyOS Sans
- Body:Inter / SF Pro Text / PingFang SC / HarmonyOS Sans
- Mono:JetBrains Mono / SF Mono / Menlo

## 设计姿态

- 对话是默认工作面,任务状态是紧邻顶部的快速入口,而不是独立仪表盘。
- 深色表面仅用明度与细边框分层,避免玻璃、霓虹和装饰性渐变。
- 状态始终同时使用图标、文字与轮廓形状,不把颜色作为唯一信号。
- 普通执行步骤保持单行紧凑;日志只在用户主动展开时出现。
- 重要操作集中在拇指区;iOS 触控区域不小于 44pt,Android 不小于 48dp。

系统一句话:Codeg Mobile 是安静、精密、对话优先的远程 AI 编程任务指挥中心。
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading