diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e782a7e3f..c6810637a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -22,6 +22,7 @@ jobs: with: fetch-depth: 1 submodules: true + token: ${{ secrets.XGH_TOKEN_READ }} - name: Setup Node.js 24.x uses: actions/setup-node@v4 @@ -70,8 +71,8 @@ jobs: if: steps.yarn-cache.outputs.cache-hit != 'true' run: yarn install --frozen-lockfile - - name: Build frontend - run: yarn build + - name: Install skills dependencies + run: cd skills && yarn install --frozen-lockfile - name: Build Tauri app run: yarn tauri build -- --target x86_64-unknown-linux-gnu diff --git a/.github/workflows/package-and-publish.yml b/.github/workflows/package-and-publish.yml index 92e2a7601..dffeab0a0 100644 --- a/.github/workflows/package-and-publish.yml +++ b/.github/workflows/package-and-publish.yml @@ -14,7 +14,7 @@ on: # branches: # - main workflow_run: - workflows: ["Version Bump"] + workflows: ['Version Bump'] types: - completed branches: @@ -171,15 +171,15 @@ jobs: fail-fast: false matrix: settings: - - platform: "macos-latest" - args: "--target aarch64-apple-darwin" - target: "aarch64-apple-darwin" - - platform: "macos-latest" - args: "--target x86_64-apple-darwin" - target: "x86_64-apple-darwin" - - platform: "ubuntu-22.04" - args: "" - target: "" + - platform: 'macos-latest' + args: '--target aarch64-apple-darwin' + target: 'aarch64-apple-darwin' + - platform: 'macos-latest' + args: '--target x86_64-apple-darwin' + target: 'x86_64-apple-darwin' + - platform: 'ubuntu-22.04' + args: '' + target: '' runs-on: ${{ matrix.settings.platform }} steps: - name: Checkout @@ -200,7 +200,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 24.x - cache: "yarn" + cache: 'yarn' - name: Install Rust stable uses: dtolnay/rust-toolchain@stable @@ -323,7 +323,7 @@ jobs: # macOS 10.15+ required for std::filesystem used by llama.cpp MACOSX_DEPLOYMENT_TARGET: ${{ matrix.settings.platform == 'macos-latest' && '10.15' || '' }} with: - args: "-c ${{ steps.config-overrides.outputs.json }} ${{ matrix.settings.args }}" + args: '-c ${{ steps.config-overrides.outputs.json }} ${{ matrix.settings.args }}' includeDebug: ${{ needs.get-version.outputs.should-publish == '' && inputs.forceRelease != 'true' }} includeRelease: ${{ needs.get-version.outputs.should-publish != '' || inputs.forceRelease == 'true' }} # TDLib dylibs are now bundled natively via build.rs + tauri.conf.json macOS.frameworks diff --git a/.github/workflows/package-android.yml b/.github/workflows/package-android.yml index d91a00e1b..2be3dedc7 100644 --- a/.github/workflows/package-android.yml +++ b/.github/workflows/package-android.yml @@ -7,11 +7,11 @@ on: workflow_dispatch: inputs: publish: - description: "Publish to release (requires main branch)" + description: 'Publish to release (requires main branch)' type: boolean default: false workflow_run: - workflows: ["Version Bump"] + workflows: ['Version Bump'] types: - completed branches: @@ -119,13 +119,14 @@ jobs: with: fetch-depth: 1 submodules: true + token: ${{ secrets.XGH_TOKEN_READ }} ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }} - name: Setup Node.js 24.x uses: actions/setup-node@v4 with: node-version: 24.x - cache: "yarn" + cache: 'yarn' - name: Install Rust stable uses: dtolnay/rust-toolchain@stable @@ -135,8 +136,8 @@ jobs: - name: Setup Java 17 uses: actions/setup-java@v4 with: - distribution: "temurin" - java-version: "17" + distribution: 'temurin' + java-version: '17' - name: Setup Android SDK uses: android-actions/setup-android@v3 diff --git a/.github/workflows/typecheck.yml b/.github/workflows/typecheck.yml index 53b3a1869..92064cab7 100644 --- a/.github/workflows/typecheck.yml +++ b/.github/workflows/typecheck.yml @@ -26,7 +26,7 @@ jobs: uses: actions/setup-node@v4 with: node-version: 24.x - cache: "yarn" + cache: 'yarn' - name: Cache node modules id: yarn-cache diff --git a/README.md b/README.md index 102e36689..61d17872d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,108 @@ -# Tauri + React + Typescript +
+ Your most productive co-worker +
-## Recommended IDE Setup + -- [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer) + + ++ "The Tet. What a brilliant machine" Morgan Freeman as he reminisces about the Tet in the movie Oblivion +
+ +## What is AlphaHuman? + +AlphaHuman is a personal AI assistant that helps you manage high-volume communication without reading everything yourself. It connects to your messaging platforms and productivity tools, understands conversations in context, and produces clear, actionable outputs you can use immediately. + +AlphaHuman is not a chatbot, browser extension, or cloud-only service. It is a native application that runs on your device, connects to your tools, and works only when you ask it to. Think of it as a second brain that sits across your communication and productivity stack. + +## What It Does + +- **Summarize conversations** -- Understand what happened without reading everything +- **Surface signals** -- Decisions, action items, risks, and sentiment shifts extracted automatically +- **Generate responses** -- Context-aware reply suggestions informed by conversation history +- **Create workflows** -- Turn unstructured commitments into trackable actions +- **Export intelligence** -- Push summaries, action items, and structured data to Notion, Google Sheets, and connected tools +- **Run automations** -- Via a sandboxed skills engine that extends the platform without app updates + +## Download + +> **Early Beta** -- AlphaHuman is under active development. Expect rough edges. + +### macOS + +| Chip | Download | +| --------------------------- | --------------------------------------------------------------------------------------------------------------- | +| Apple Silicon (M1/M2/M3/M4) | [`.dmg` (aarch64)](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_aarch64.dmg) | +| Intel | [`.dmg` (x64)](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_x64.dmg) | + +### Windows + +| Architecture | Download | +| ------------ | ------------------------------------------------------------------------------------------------------- | +| x64 | [`.msi`](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_x64_en-US.msi) | + +### Linux + +| Format | Download | +| --------------- | ------------------------------------------------------------------------------------------------------------- | +| Debian / Ubuntu | [`.deb` (amd64)](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_amd64.deb) | +| Fedora / RHEL | [`.rpm` (x86_64)](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_x86_64.rpm) | +| Universal | [`.AppImage`](https://github.com/alphahumanxyz/alphahuman/releases/latest/download/AlphaHuman_amd64.AppImage) | + +### Mobile + +- **Android** and **iOS** -- Coming soon + +Browse all releases: [github.com/alphahumanxyz/alphahuman/releases](https://github.com/alphahumanxyz/alphahuman/releases) + +## Skills + +AlphaHuman uses a pluggable **skills** architecture. Each skill connects to an external service, syncs relevant data locally, and exposes tools that you (or the AI) can use. Skills run in a sandboxed environment with their own database, storage, and permissions. + +| Skill | Status | Description | +| --------------- | ----------- | ------------------------------------------------------------- | +| Telegram | Available | Chats, messages, contacts, search, admin tools, AI summaries | +| Notion | Available | Pages, databases, blocks, users, comments, search, local sync | +| Gmail | In Progress | Email management, labels, search, send/receive with OAuth2 | +| Google Calendar | In Progress | Calendars, events, scheduling with OAuth2 | +| Google Drive | In Progress | Files, Sheets, Docs with OAuth2 | +| Slack | In Progress | Messages, channels, real-time events | +| Web3 Wallets | Planned | EVM wallet management, balance checks, network monitoring | + +## Privacy-First + +- **Zero retention** -- Message content is processed to produce output, then discarded +- **OS-level credential storage** -- Desktop platforms use native keychains (macOS Keychain, Windows Credential Manager) +- **No training on your data** -- Your data is never used for model improvement +- **Request-only processing** -- Nothing happens without an explicit user action; no background scanning +- **Sandboxed skills** -- Each skill runs in isolation with memory and resource limits + +## Getting Started + +1. **Download** the installer for your platform from the [releases page](https://github.com/alphahumanxyz/alphahuman/releases/latest) +2. **Install** the app (drag to Applications on macOS, or use your package manager on Linux) +3. **Connect a source** -- follow the in-app onboarding to link Telegram, Notion, Gmail, or other services +4. **Run your first request** -- ask the AI to summarize what you missed, extract action items, or surface key decisions + +## Links + +- [Architecture Overview](./ARCHITECTURE.md) -- How AlphaHuman is built +- [Changelog](./CHANGELOG.md) -- Release history +- [Website](https://alphahuman.xyz) -- Learn more + +--- + ++ Made with love by a bunch of Web3 nerds +
+ ++ AlphaHuman is in early beta. Features may change, break, or disappear. Use at your own risk. +
diff --git a/docs/the-tet.png b/docs/the-tet.png new file mode 100644 index 000000000..cb5ace124 Binary files /dev/null and b/docs/the-tet.png differ diff --git a/eslint.config.js b/eslint.config.js index 87ee3d172..cd4683534 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -205,6 +205,44 @@ export default [ }, }, + // E2E test files (Appium/WebDriverIO) — use tsconfig.e2e.json for parsing + { + files: ['test/e2e/**/*.ts', 'wdio.conf.ts'], + languageOptions: { + parser: tsparser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + project: './tsconfig.e2e.json', + tsconfigRootDir: __dirname, + }, + globals: { + browser: 'readonly', + $: 'readonly', + $$: 'readonly', + describe: 'readonly', + it: 'readonly', + before: 'readonly', + after: 'readonly', + beforeEach: 'readonly', + afterEach: 'readonly', + expect: 'readonly', + }, + }, + plugins: { + '@typescript-eslint': tseslint, + }, + rules: { + 'no-unused-vars': 'off', + '@typescript-eslint/no-unused-vars': [ + 'error', + { argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' }, + ], + '@typescript-eslint/no-explicit-any': 'off', + 'no-undef': 'off', + }, + }, + // JavaScript files configuration { files: ['**/*.js', '**/*.jsx'], diff --git a/package.json b/package.json index d638c1188..6efa12d46 100644 --- a/package.json +++ b/package.json @@ -24,10 +24,11 @@ "test:unit:watch": "vitest", "test:watch": "vitest", "test:coverage": "vitest run --coverage", - "test:rust": "cargo test --manifest-path src-tauri/Cargo.toml", - "test:e2e:build": "yarn macos:build:debug", - "test:e2e:run": "bash scripts/e2e.sh", - "test:e2e": "yarn test:e2e:build && yarn test:e2e:run", + "test:rust": "source $HOME/.cargo/env 2>/dev/null; cargo test --manifest-path src-tauri/Cargo.toml", + "test:e2e:build": "bash scripts/e2e-build.sh", + "test:e2e:login": "bash scripts/e2e-login.sh", + "test:e2e:auth": "bash scripts/e2e-auth.sh", + "test:e2e": "yarn test:e2e:build && yarn test:e2e:login && yarn test:e2e:auth", "test:all": "yarn test:coverage && yarn test:rust && yarn test:e2e", "format": "prettier --write .", "format:check": "prettier --check .", @@ -55,6 +56,7 @@ "qrcode.react": "^4.2.0", "react": "^19.1.0", "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", "react-redux": "^9.2.0", "react-router-dom": "^7.13.0", "redux-logger": "^3.0.6", diff --git a/scripts/e2e-auth.sh b/scripts/e2e-auth.sh new file mode 100755 index 000000000..d004011ff --- /dev/null +++ b/scripts/e2e-auth.sh @@ -0,0 +1,90 @@ +#!/usr/bin/env bash +# +# Run E2E auth & access control tests only. +# +# Starts Appium, cleans app caches, runs the auth-access-control spec, +# then tears everything down. Each flow script is self-contained so +# specs don't pollute each other's Redux Persist state. +# +# Usage: +# ./scripts/e2e-auth.sh +# APPIUM_PORT=4723 ./scripts/e2e-auth.sh +# +set -euo pipefail + +APPIUM_PORT="${APPIUM_PORT:-4723}" +E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}" +SPEC="test/e2e/specs/auth-access-control.spec.ts" + +export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" +export BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" + +# Clean cached app data for a fresh state — Redux Persist would otherwise +# remember the JWT from a previous run and skip the login flow. +echo "Cleaning cached app data..." +rm -rf ~/Library/WebKit/com.alphahuman.app +rm -rf ~/Library/Caches/com.alphahuman.app +rm -rf "$HOME/Library/Application Support/com.alphahuman.app" + +# Verify the frontend dist has the mock server URL baked in. +DIST_JS="$(ls dist/assets/index-*.js 2>/dev/null | head -1)" +if [ -z "$DIST_JS" ]; then + echo "ERROR: No frontend bundle found at dist/assets/index-*.js." >&2 + echo " Run 'yarn test:e2e:build' to build the app before running E2E tests." >&2 + exit 1 +fi +if ! grep -q "127.0.0.1:${E2E_MOCK_PORT}" "$DIST_JS"; then + echo "ERROR: frontend bundle does NOT contain mock server URL (127.0.0.1:${E2E_MOCK_PORT})." >&2 + echo " Run 'yarn test:e2e:build' to rebuild with the mock URL." >&2 + exit 1 +fi +echo "Verified: frontend bundle contains mock server URL." + +# --- Resolve Node 24 via nvm --------------------------------------------------- +export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" +# shellcheck source=/dev/null +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + +NODE24="$(nvm which 24 2>/dev/null || true)" +if [ -z "$NODE24" ] || [ ! -x "$NODE24" ]; then + echo "ERROR: Node 24 is required for appium v3. Install it with: nvm install 24" >&2 + exit 1 +fi + +APPIUM_BIN="$(dirname "$NODE24")/appium" +if [ ! -x "$APPIUM_BIN" ]; then + echo "ERROR: appium not found at $APPIUM_BIN. Install it with: nvm use 24 && npm i -g appium" >&2 + exit 1 +fi + +# --- Start Appium in the background ------------------------------------------- +APPIUM_LOG="/tmp/appium-e2e-auth.log" +NODE_VER=$("$NODE24" --version) +echo "Starting Appium on port $APPIUM_PORT (Node $NODE_VER)..." +echo " Appium logs: $APPIUM_LOG" +"$NODE24" "$APPIUM_BIN" --port "$APPIUM_PORT" --relaxed-security > "$APPIUM_LOG" 2>&1 & +APPIUM_PID=$! + +cleanup() { + echo "Stopping Appium (pid $APPIUM_PID)..." + kill "$APPIUM_PID" 2>/dev/null || true + wait "$APPIUM_PID" 2>/dev/null || true +} +trap cleanup EXIT + +# Wait for Appium to be ready +for i in $(seq 1 30); do + if curl -sf "http://127.0.0.1:$APPIUM_PORT/status" >/dev/null 2>&1; then + echo "Appium is ready." + break + fi + if [ "$i" -eq 30 ]; then + echo "ERROR: Appium did not start within 30 seconds." >&2 + exit 1 + fi + sleep 1 +done + +# --- Run WebDriverIO ---------------------------------------------------------- +echo "Running E2E auth flow tests ($SPEC)..." +npx wdio run wdio.conf.ts --spec "$SPEC" diff --git a/scripts/e2e-build.sh b/scripts/e2e-build.sh new file mode 100755 index 000000000..3522fafcb --- /dev/null +++ b/scripts/e2e-build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# +# Build the .app bundle for E2E tests with the mock server URL baked in. +# +# This does a cargo clean first to ensure the frontend assets are re-embedded +# (Cargo's incremental build won't detect changes to dist/). +# +set -euo pipefail + +# Source Cargo environment +[ -f "$HOME/.cargo/env" ] && . "$HOME/.cargo/env" + +export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT:-18473}" + +echo "Building E2E app bundle with VITE_BACKEND_URL=$VITE_BACKEND_URL" + +# Clean Rust build cache so frontend assets get re-embedded +cargo clean --manifest-path src-tauri/Cargo.toml + +# Load .env for other vars (Telegram API keys, etc.) +source scripts/load-dotenv.sh + +# Re-export mock URL in case load-dotenv.sh clobbered it +export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT:-18473}" + +# Build .app only (skip DMG to avoid bundle_dmg.sh failures) +tauri build --bundles app --debug + +echo "E2E build complete." diff --git a/scripts/e2e-login.sh b/scripts/e2e-login.sh new file mode 100755 index 000000000..bb00464cc --- /dev/null +++ b/scripts/e2e-login.sh @@ -0,0 +1,93 @@ +#!/usr/bin/env bash +# +# Run E2E login flow tests only. +# +# Starts Appium, cleans app caches, runs the login-flow spec, +# then tears everything down. Each flow script is self-contained so +# specs don't pollute each other's Redux Persist state. +# +# Usage: +# ./scripts/e2e-login.sh +# APPIUM_PORT=4723 ./scripts/e2e-login.sh +# +set -euo pipefail + +APPIUM_PORT="${APPIUM_PORT:-4723}" +E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}" +SPEC="test/e2e/specs/login-flow.spec.ts" + +# Point the app at the local mock server (baked into the build already, but +# also exported here so the Rust backend / any env-based config picks it up). +export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" +export BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}" + +# Clean cached app data for a fresh state — Redux Persist would otherwise +# remember the JWT from a previous run and skip the login flow. +echo "Cleaning cached app data..." +rm -rf ~/Library/WebKit/com.alphahuman.app +rm -rf ~/Library/Caches/com.alphahuman.app +rm -rf "$HOME/Library/Application Support/com.alphahuman.app" + +# Verify the frontend dist has the mock server URL baked in (not production). +# Tauri compresses assets when embedding, so we check dist/ not the binary. +DIST_JS="$(ls dist/assets/index-*.js 2>/dev/null | head -1)" +if [ -z "$DIST_JS" ]; then + echo "ERROR: No frontend bundle found at dist/assets/index-*.js." >&2 + echo " Run 'yarn test:e2e:build' to build the app before running E2E tests." >&2 + exit 1 +fi +if ! grep -q "127.0.0.1:${E2E_MOCK_PORT}" "$DIST_JS"; then + echo "ERROR: frontend bundle does NOT contain mock server URL (127.0.0.1:${E2E_MOCK_PORT})." >&2 + echo " Run 'yarn test:e2e:build' to rebuild with the mock URL." >&2 + exit 1 +fi +echo "Verified: frontend bundle contains mock server URL." + +# --- Resolve Node 24 via nvm --------------------------------------------------- +export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" +# shellcheck source=/dev/null +[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" + +NODE24="$(nvm which 24 2>/dev/null || true)" +if [ -z "$NODE24" ] || [ ! -x "$NODE24" ]; then + echo "ERROR: Node 24 is required for appium v3. Install it with: nvm install 24" >&2 + exit 1 +fi + +APPIUM_BIN="$(dirname "$NODE24")/appium" +if [ ! -x "$APPIUM_BIN" ]; then + echo "ERROR: appium not found at $APPIUM_BIN. Install it with: nvm use 24 && npm i -g appium" >&2 + exit 1 +fi + +# --- Start Appium in the background ------------------------------------------- +APPIUM_LOG="/tmp/appium-e2e-login.log" +NODE_VER=$("$NODE24" --version) +echo "Starting Appium on port $APPIUM_PORT (Node $NODE_VER)..." +echo " Appium logs: $APPIUM_LOG" +"$NODE24" "$APPIUM_BIN" --port "$APPIUM_PORT" --relaxed-security > "$APPIUM_LOG" 2>&1 & +APPIUM_PID=$! + +cleanup() { + echo "Stopping Appium (pid $APPIUM_PID)..." + kill "$APPIUM_PID" 2>/dev/null || true + wait "$APPIUM_PID" 2>/dev/null || true +} +trap cleanup EXIT + +# Wait for Appium to be ready +for i in $(seq 1 30); do + if curl -sf "http://127.0.0.1:$APPIUM_PORT/status" >/dev/null 2>&1; then + echo "Appium is ready." + break + fi + if [ "$i" -eq 30 ]; then + echo "ERROR: Appium did not start within 30 seconds." >&2 + exit 1 + fi + sleep 1 +done + +# --- Run WebDriverIO ---------------------------------------------------------- +echo "Running E2E login flow tests ($SPEC)..." +npx wdio run wdio.conf.ts --spec "$SPEC" diff --git a/scripts/e2e.sh b/scripts/e2e.sh deleted file mode 100755 index fe986696c..000000000 --- a/scripts/e2e.sh +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env bash -# -# Start Appium under Node 24 (required by appium v3), run WebDriverIO E2E -# tests, then tear everything down. -# -# Usage: -# ./scripts/e2e.sh # run tests -# APPIUM_PORT=4723 ./scripts/e2e.sh # custom port -# -set -euo pipefail - -APPIUM_PORT="${APPIUM_PORT:-4723}" - -# --- Resolve Node 24 via nvm --------------------------------------------------- -export NVM_DIR="${NVM_DIR:-$HOME/.nvm}" -# shellcheck source=/dev/null -[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh" - -NODE24="$(nvm which 24 2>/dev/null || true)" -if [ -z "$NODE24" ] || [ ! -x "$NODE24" ]; then - echo "ERROR: Node 24 is required for appium v3. Install it with: nvm install 24" >&2 - exit 1 -fi - -APPIUM_BIN="$(dirname "$NODE24")/appium" -if [ ! -x "$APPIUM_BIN" ]; then - echo "ERROR: appium not found at $APPIUM_BIN. Install it with: nvm use 24 && npm i -g appium" >&2 - exit 1 -fi - -# --- Start Appium in the background ------------------------------------------- -NODE_VER=$("$NODE24" --version) -echo "Starting Appium on port $APPIUM_PORT (Node $NODE_VER)..." -"$NODE24" "$APPIUM_BIN" --port "$APPIUM_PORT" --relaxed-security & -APPIUM_PID=$! - -cleanup() { - echo "Stopping Appium (pid $APPIUM_PID)..." - kill "$APPIUM_PID" 2>/dev/null || true - wait "$APPIUM_PID" 2>/dev/null || true -} -trap cleanup EXIT - -# Wait for Appium to be ready -for i in $(seq 1 30); do - if curl -sf "http://127.0.0.1:$APPIUM_PORT/status" >/dev/null 2>&1; then - echo "Appium is ready." - break - fi - if [ "$i" -eq 30 ]; then - echo "ERROR: Appium did not start within 30 seconds." >&2 - exit 1 - fi - sleep 1 -done - -# --- Run WebDriverIO ---------------------------------------------------------- -echo "Running E2E tests..." -npx wdio run wdio.conf.ts diff --git a/skills b/skills index 272640010..0c6a11b1e 160000 --- a/skills +++ b/skills @@ -1 +1 @@ -Subproject commit 272640010a8a6bdee31b610fc7519067d4fd2e44 +Subproject commit 0c6a11b1e05aed1d6c1cb986e81501f13afcf203 diff --git a/src/AppRoutes.tsx b/src/AppRoutes.tsx index f1ef2ff2b..46a58e40b 100644 --- a/src/AppRoutes.tsx +++ b/src/AppRoutes.tsx @@ -108,6 +108,14 @@ const AppRoutes = () => { } /> +Failed to load conversations
+{error}
- ))} +No matching threads
+ +No conversations yet
+ ++ Created {formatRelativeTime(selectedThread.createdAt)} +
+Failed to load messages
+{messagesError}
+ ++ {msg.content} +
+ )} ++ {formatRelativeTime(msg.createdAt)} +
+No messages in this thread
+{sendError}
+ +No conversations yet
- +Select a conversation
- Created {formatRelativeTime(selectedThread.createdAt)} -
-{msg.content}
-- {formatRelativeTime(msg.createdAt)} -
-No messages in this thread
-{sendError}
- )} -Select a conversation
-