mirror of
https://github.com/tinyhumansai/openhuman.git
synced 2026-07-27 21:08:00 +00:00
refactor: consolidate CLI binaries and streamline CI workflows (#44)
* chore: update dependencies and remove unused packages - Removed several unused dependencies from package.json, including `@tauri-apps/plugin-shell`, `@types/react-router-dom`, `immer`, `qrcode.react`, and `@testing-library/user-event`. - Updated the Vite configuration to exclude `telegram` from the optimizeDeps include list. - Cleaned up yarn.lock by removing references to deleted packages. - Deleted outdated GitHub Actions workflows for macOS ARM64 build, package and publish, and update changelog. * refactor: consolidate CLI binaries and update configurations - Renamed the main binary from `openhuman-core` to `openhuman` for consistency across the project. - Removed the `openhuman-cli` and `openhuman-core` binaries, consolidating functionality into the new `openhuman` binary. - Updated build configurations in GitHub Actions and Tauri to reflect the new binary name and paths. - Adjusted the Tailwind and Vite configurations to point to the correct HTML and source directories. - Introduced a new method in the Rust core server for handling JSON-RPC calls, enhancing the API structure. * chore: update subproject commit reference in skills * chore: update Rust test workflow and improve CLI command formatting - Modified the GitHub Actions workflow to include a new job for running Rust tests in the workspace. - Updated the `test:rust` command in `package.json` to run tests for the entire workspace. - Refactored CLI command definitions in `openhuman.rs` for improved readability by consolidating struct fields into single lines. * chore: enhance E2E testing scripts and workflows - Updated `.prettierignore` to exclude the `rust-core` directory. - Added new E2E testing commands in `package.json` for running all flows and improved formatting commands to include `cargo fmt`. - Enhanced GitHub Actions workflows to streamline E2E testing, including new scripts for running individual specs and all flows sequentially. - Refactored existing E2E scripts to utilize a common function for running tests, improving maintainability and readability. - Introduced a new script for resolving Node.js and Appium dependencies, ensuring compatibility for E2E tests. - Cleaned up and simplified existing E2E scripts by removing redundant code and consolidating functionality. * chore: update GitHub Actions workflows for Tauri build process - Modified build and release workflows to reference the correct target directory for Rust binaries, ensuring compatibility with the Cargo workspace structure. - Adjusted caching configuration in the test workflow for consistency and improved readability. - Standardized environment variable formatting in the test workflow to enhance clarity. * fix: standardize string formatting in GitHub Actions workflows - Updated the test workflow to use single quotes for cache configuration and environment variables, enhancing consistency across the file.
This commit is contained in:
@@ -73,13 +73,14 @@ jobs:
|
||||
run: cd skills && yarn install --frozen-lockfile
|
||||
|
||||
- name: Build sidecar core binary
|
||||
run: cargo build --manifest-path rust-core/Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman-core
|
||||
run: cargo build --manifest-path rust-core/Cargo.toml --release --target x86_64-unknown-linux-gnu --bin openhuman
|
||||
|
||||
- name: Stage sidecar for Tauri bundler
|
||||
run: |
|
||||
mkdir -p src-tauri/binaries
|
||||
cp rust-core/target/x86_64-unknown-linux-gnu/release/openhuman-core src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu
|
||||
chmod +x src-tauri/binaries/openhuman-core-x86_64-unknown-linux-gnu
|
||||
# Workspace root target/ (not rust-core/target/) — see root Cargo.toml [workspace]
|
||||
cp target/x86_64-unknown-linux-gnu/release/openhuman src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
|
||||
chmod +x src-tauri/binaries/openhuman-x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Build Tauri app
|
||||
run: |
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
# Standalone macOS Apple Silicon (aarch64) Tauri build — no prepare-release / tagging.
|
||||
#
|
||||
# GitHub: workflow_dispatch checks out the ref. Local act: ACT=true skips checkout (use -b bind + run
|
||||
# ./scripts/run-macos-arm64-build.sh so submodules are initialized before act copies the tree).
|
||||
name: macOS ARM64 build
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build-macos-arm64:
|
||||
name: Build macOS aarch64 (signed)
|
||||
runs-on: macos-latest
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.XGH_TOKEN || github.token }}
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
if: ${{ env.ACT != 'true' }}
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set Xcode version
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
|
||||
- name: Setup Node.js 24.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.x
|
||||
cache: yarn
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin
|
||||
|
||||
- name: Cargo.lock fingerprint (deps only)
|
||||
id: cargo-lock-fingerprint
|
||||
shell: bash
|
||||
run: |
|
||||
echo "hash=$(tail -n +8 src-tauri/Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cache Cargo registry and git sources
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-registry-${{ steps.cargo-lock-fingerprint.outputs.hash }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-registry-
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Install skills dependencies
|
||||
run: cd skills && yarn install --frozen-lockfile
|
||||
|
||||
- name: Build skills
|
||||
run: cd skills && yarn build
|
||||
|
||||
- name: Define Tauri configuration overrides
|
||||
id: config-overrides
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
BASE_URL: ${{ vars.BASE_URL }}
|
||||
UPDATER_PUBLIC_KEY: ${{ secrets.UPDATER_PUBLIC_KEY }}
|
||||
WITH_UPDATER: 'true'
|
||||
with:
|
||||
github-token: ${{ secrets.XGH_TOKEN || github.token }}
|
||||
script: |
|
||||
const workspacePath = process.env.GITHUB_WORKSPACE.replace(/\\/g, '/');
|
||||
const prefix = workspacePath.startsWith('/') ? 'file://' : 'file:///';
|
||||
const moduleUrl = `${prefix}${workspacePath}/scripts/prepareTauriConfig.js`;
|
||||
const { default: prepareTauriConfig } = await import(moduleUrl);
|
||||
const config = prepareTauriConfig();
|
||||
core.setOutput('json', JSON.stringify(config));
|
||||
|
||||
- name: Build frontend
|
||||
run: yarn build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
VITE_BACKEND_URL: ${{ vars.VITE_BACKEND_URL }}
|
||||
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
|
||||
VITE_DEBUG: ${{ vars.VITE_DEBUG }}
|
||||
|
||||
- name: Build sidecar core binary
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build \
|
||||
--manifest-path rust-core/Cargo.toml \
|
||||
--release \
|
||||
--target aarch64-apple-darwin \
|
||||
--bin openhuman-core
|
||||
|
||||
- name: Stage sidecar for Tauri bundler
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p src-tauri/binaries
|
||||
cp rust-core/target/aarch64-apple-darwin/release/openhuman-core \
|
||||
src-tauri/binaries/openhuman-core-aarch64-apple-darwin
|
||||
chmod +x src-tauri/binaries/openhuman-core-aarch64-apple-darwin
|
||||
|
||||
- name: Build, package (no GitHub release upload)
|
||||
uses: tauri-apps/tauri-action@v0.6.2
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
BASE_URL: ${{ vars.BASE_URL }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
WITH_UPDATER: 'true'
|
||||
MACOSX_DEPLOYMENT_TARGET: '10.15'
|
||||
with:
|
||||
args: -c ${{ steps.config-overrides.outputs.json }} --target aarch64-apple-darwin -- -- --bin OpenHuman
|
||||
includeDebug: false
|
||||
includeRelease: true
|
||||
includeUpdaterJson: false
|
||||
@@ -1,499 +0,0 @@
|
||||
# Terms:
|
||||
# "build" - Compile web project using webpack.
|
||||
# "package" - Produce a distributive package for a specific platform as a workflow artifact.
|
||||
# "publish" - Send a package to corresponding store and GitHub release page.
|
||||
# "release" - build + package + publish
|
||||
#
|
||||
# Jobs in this workflow will skip the "publish" step when `SHOULD_PUBLISH` is not set.
|
||||
|
||||
name: Package and publish (Legacy)
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
# pull_request:
|
||||
# branches:
|
||||
# - main
|
||||
|
||||
env:
|
||||
IS_PR: ${{ github.event_name == 'pull_request' }}
|
||||
SHOULD_PUBLISH: ${{ github.event_name != 'pull_request' && github.ref == 'refs/heads/main' }}
|
||||
PUBLISH_REPO: openhumanxyz/openhuman
|
||||
XGH_TOKEN: ${{ secrets.XGH_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
jobs:
|
||||
get-version:
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
package-version: ${{ steps.extract-version.outputs.package-version }}
|
||||
tag-name: ${{ steps.extract-version.outputs.tag-name }}
|
||||
should-publish: ${{ steps.extract-version.outputs.should-publish }}
|
||||
release-name: ${{ steps.extract-version.outputs.release-name }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
|
||||
token: ${{ secrets.XGH_TOKEN_READ }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 20.x
|
||||
|
||||
- name: Extract version and tag
|
||||
id: extract-version
|
||||
run: |
|
||||
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
||||
TAG_NAME="v${PACKAGE_VERSION}"
|
||||
RELEASE_NAME="OpenHuman v${PACKAGE_VERSION}"
|
||||
echo "package-version=$PACKAGE_VERSION" >> $GITHUB_OUTPUT
|
||||
echo "tag-name=$TAG_NAME" >> $GITHUB_OUTPUT
|
||||
echo "release-name=$RELEASE_NAME" >> $GITHUB_OUTPUT
|
||||
echo "should-publish=$SHOULD_PUBLISH" >> $GITHUB_OUTPUT
|
||||
echo "Extracted version: $PACKAGE_VERSION"
|
||||
echo "Generated tag: $TAG_NAME"
|
||||
echo "Generated release name: $RELEASE_NAME"
|
||||
|
||||
check-version:
|
||||
runs-on: ubuntu-latest
|
||||
needs: get-version
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || '' }}
|
||||
outputs:
|
||||
should-skip: ${{ steps.check-release.outputs.should-skip }}
|
||||
steps:
|
||||
- name: Check if release already exists
|
||||
id: check-release
|
||||
env:
|
||||
PACKAGE_VERSION: ${{ needs.get-version.outputs.package-version }}
|
||||
TAG_NAME: ${{ needs.get-version.outputs.tag-name }}
|
||||
run: |
|
||||
# For non-main branches or when publishing is disabled, always continue
|
||||
if [ -z "$SHOULD_PUBLISH" ]; then
|
||||
echo "🚧 Publishing disabled (non-main branch)"
|
||||
echo "should-skip=false" >> $GITHUB_OUTPUT
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Checking if release already exists for tag: $TAG_NAME"
|
||||
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases/tags/$TAG_NAME")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
|
||||
if [ "$HTTP_CODE" = "404" ]; then
|
||||
echo "🆕 No release found for version $PACKAGE_VERSION, will create new release"
|
||||
echo "should-skip=false" >> $GITHUB_OUTPUT
|
||||
elif [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "⚠️ Warning: Failed to check release (HTTP $HTTP_CODE). Response: $BODY"
|
||||
echo "Continuing anyway..."
|
||||
echo "should-skip=false" >> $GITHUB_OUTPUT
|
||||
elif echo "$BODY" | jq -e '.tag_name' > /dev/null; then
|
||||
IS_DRAFT=$(echo "$BODY" | jq -r '.draft')
|
||||
if [ "$IS_DRAFT" = "false" ]; then
|
||||
echo "✅ Published release already exists for version $PACKAGE_VERSION"
|
||||
echo "should-skip=true" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "📝 Draft release exists for version $PACKAGE_VERSION, will continue"
|
||||
echo "should-skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
else
|
||||
echo "🆕 No release found for version $PACKAGE_VERSION, will create new release"
|
||||
echo "should-skip=false" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
create-release:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version, check-version]
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || '' }}
|
||||
if: github.ref == 'refs/heads/main' && needs.get-version.outputs.should-publish == 'true' && needs.check-version.outputs.should-skip != 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
outputs:
|
||||
releaseId: ${{ steps.create-release.outputs.releaseId }}
|
||||
steps:
|
||||
- name: Create draft release
|
||||
id: create-release
|
||||
env:
|
||||
PACKAGE_VERSION: ${{ needs.get-version.outputs.package-version }}
|
||||
TAG_NAME: ${{ needs.get-version.outputs.tag-name }}
|
||||
RELEASE_NAME: ${{ needs.get-version.outputs.release-name }}
|
||||
XGH_TOKEN: ${{ secrets.XGH_TOKEN }}
|
||||
run: |
|
||||
echo "Creating draft release for tag: $TAG_NAME"
|
||||
echo "Repository: $PUBLISH_REPO"
|
||||
RELEASE_BODY="See the assets below to download this version."
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
||||
-H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d '{"tag_name": "'"$TAG_NAME"'", "name": "'"$RELEASE_NAME"'", "draft": true, "body": "'"$RELEASE_BODY"'"}' \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
echo "HTTP Status Code: $HTTP_CODE"
|
||||
echo "Response body: $BODY"
|
||||
if [ "$HTTP_CODE" != "201" ]; then
|
||||
echo "Error: Failed to create release. HTTP $HTTP_CODE"
|
||||
echo "Response: $BODY"
|
||||
exit 1
|
||||
fi
|
||||
RELEASE_ID=$(echo "$BODY" | jq -r '.id')
|
||||
echo "Extracted Release ID: $RELEASE_ID"
|
||||
if [ "$RELEASE_ID" = "null" ] || [ -z "$RELEASE_ID" ]; then
|
||||
echo "Error: Failed to extract release ID. Response was: $BODY"
|
||||
exit 1
|
||||
fi
|
||||
echo "releaseId=$RELEASE_ID" >> $GITHUB_OUTPUT
|
||||
|
||||
package-tauri:
|
||||
name: Build, package and publish Tauri
|
||||
needs: [get-version, check-version, create-release]
|
||||
if: ${{ !failure() && !cancelled() && needs.check-version.outputs.should-skip != 'true' }}
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || '' }}
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.XGH_TOKEN }}
|
||||
permissions:
|
||||
contents: write
|
||||
strategy:
|
||||
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 x86_64-unknown-linux-gnu'
|
||||
target: 'x86_64-unknown-linux-gnu'
|
||||
runs-on: ${{ matrix.settings.platform }}
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
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: Set Xcode version
|
||||
if: matrix.settings.platform == 'macos-latest'
|
||||
uses: maxim-lobanov/setup-xcode@v1
|
||||
with:
|
||||
xcode-version: latest-stable
|
||||
|
||||
- name: Setup Node.js 24.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.x
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.settings.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
|
||||
- name: Install Tauri dependencies (ubuntu only)
|
||||
if: matrix.settings.platform == 'ubuntu-22.04'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
# OpenMP is now disabled via Cargo.toml (default-features = false for llama-cpp-2)
|
||||
# This avoids cross-compilation issues where Homebrew ARM64 OpenMP can't be used for x86_64 builds
|
||||
|
||||
- name: Cache Cargo registry
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-cargo-${{ hashFiles('src-tauri/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-
|
||||
|
||||
- name: Cache Cargo build artifacts
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: src-tauri/target
|
||||
key: ${{ runner.os }}-cargo-target-${{ matrix.settings.platform }}-${{ hashFiles('src-tauri/Cargo.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-cargo-target-${{ matrix.settings.platform }}-
|
||||
|
||||
- name: Cache node modules
|
||||
id: yarn-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: ${{ runner.os }}-build-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-build-
|
||||
|
||||
- name: Install dependencies
|
||||
if: steps.yarn-cache.outputs.cache-hit != 'true'
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Cache skills node modules
|
||||
id: skills-yarn-cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: skills/node_modules
|
||||
key: ${{ runner.os }}-skills-${{ hashFiles('skills/yarn.lock') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-skills-
|
||||
|
||||
- name: Install skills dependencies
|
||||
if: steps.skills-yarn-cache.outputs.cache-hit != 'true'
|
||||
run: cd skills && yarn install --frozen-lockfile
|
||||
|
||||
- name: Build skills
|
||||
run: cd skills && yarn build
|
||||
|
||||
- name: Extract repository owner and name
|
||||
id: repository-info
|
||||
if: needs.get-version.outputs.should-publish != ''
|
||||
shell: bash
|
||||
run: |
|
||||
echo "owner=${PUBLISH_REPO%%/*}" >> $GITHUB_OUTPUT
|
||||
echo "repo=${PUBLISH_REPO#*/}" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Define Tauri configuration overrides
|
||||
id: config-overrides
|
||||
uses: actions/github-script@v7
|
||||
env:
|
||||
BASE_URL: ${{ vars.BASE_URL }}
|
||||
UPDATER_PUBLIC_KEY: ${{ secrets.UPDATER_PUBLIC_KEY }}
|
||||
WITH_UPDATER: ${{ needs.get-version.outputs.should-publish != '' && 'true' || 'false' }}
|
||||
with:
|
||||
script: |
|
||||
const workspacePath = process.env.GITHUB_WORKSPACE.replace(/\\/g, '/');
|
||||
const prefix = workspacePath.startsWith('/') ? 'file://' : 'file:///';
|
||||
const moduleUrl = `${prefix}${workspacePath}/scripts/prepareTauriConfig.js`;
|
||||
const { default: prepareTauriConfig } = await import(moduleUrl)
|
||||
const config = prepareTauriConfig();
|
||||
|
||||
const configJson = JSON.stringify(config);
|
||||
console.log(configJson);
|
||||
|
||||
core.setOutput("json", configJson);
|
||||
|
||||
- name: Build frontend
|
||||
run: yarn build
|
||||
env:
|
||||
NODE_ENV: production
|
||||
VITE_BACKEND_URL: ${{ vars.VITE_BACKEND_URL }}
|
||||
VITE_SENTRY_DSN: ${{ secrets.VITE_SENTRY_DSN }}
|
||||
VITE_DEBUG: ${{ vars.VITE_DEBUG }}
|
||||
|
||||
- name: Build sidecar core binary
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build \
|
||||
--manifest-path rust-core/Cargo.toml \
|
||||
--release \
|
||||
--target "$MATRIX_TARGET" \
|
||||
--bin openhuman-core
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
|
||||
- name: Stage sidecar for Tauri bundler
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p src-tauri/binaries
|
||||
SOURCE="rust-core/target/$MATRIX_TARGET/release/openhuman-core"
|
||||
DEST="src-tauri/binaries/openhuman-core-$MATRIX_TARGET"
|
||||
cp "$SOURCE" "$DEST"
|
||||
chmod +x "$DEST"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
|
||||
- name: Build, package and publish
|
||||
uses: tauri-apps/tauri-action@v0.6.2
|
||||
id: build-tauri
|
||||
env:
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE_BASE64 }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
|
||||
BASE_URL: ${{ vars.BASE_URL }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.UPDATER_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.UPDATER_PRIVATE_KEY_PASSWORD }}
|
||||
WITH_UPDATER: ${{ needs.get-version.outputs.should-publish != '' && 'true' || 'false' }}
|
||||
# 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 }} -- -- --bin OpenHuman'
|
||||
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
|
||||
# No post-build scripts needed - Tauri bundler handles copying and signing
|
||||
releaseId: ${{ needs.create-release.outputs.releaseId }}
|
||||
owner: openhumanxyz
|
||||
repo: openhuman
|
||||
|
||||
- name: Get file info
|
||||
id: file-info
|
||||
shell: bash
|
||||
run: |
|
||||
FULL_PATH=$(echo "${{ fromJSON(steps.build-tauri.outputs.artifactPaths)[0] }}")
|
||||
FILENAME=$(basename "$FULL_PATH")
|
||||
NAME_WITH_EXT="${FILENAME%.*}"
|
||||
VERSION="${{ needs.get-version.outputs.package-version }}"
|
||||
TAG_NAME="${{ needs.get-version.outputs.tag-name }}"
|
||||
|
||||
# Remove version/tag patterns from name
|
||||
# Patterns to remove: v0.14.0-1, 0.14.0-1, v0.14.0, 0.14.0, etc.
|
||||
# Package managers add release numbers like -1, -2, etc.
|
||||
NAME="$NAME_WITH_EXT"
|
||||
|
||||
# Remove version-release pattern (0.14.0-1, 0.14.0-2, etc.) - escape dots for regex
|
||||
VERSION_ESCAPED=$(echo "$VERSION" | sed 's/\./\\./g')
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?${VERSION_ESCAPED}-[0-9]+[_-]?//g")
|
||||
|
||||
# Remove tag-release pattern (v0.14.0-1, v0.14.0-2, etc.)
|
||||
TAG_ESCAPED=$(echo "$TAG_NAME" | sed 's/\./\\./g')
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?${TAG_ESCAPED}-[0-9]+[_-]?//g")
|
||||
|
||||
# Remove tag name (v0.14.0) with various separators
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?${TAG_ESCAPED}[_-]?//g")
|
||||
|
||||
# Remove version (0.14.0) with various separators
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?${VERSION_ESCAPED}[_-]?//g")
|
||||
|
||||
# Remove version with dots replaced by separators (0_14_0, 0-14-0)
|
||||
VERSION_DASH=$(echo "$VERSION" | tr '.' '-')
|
||||
VERSION_UNDERSCORE=$(echo "$VERSION" | tr '.' '_')
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?v?${VERSION_DASH}[_-]?//g")
|
||||
NAME=$(echo "$NAME" | sed -E "s/[_-]?v?${VERSION_UNDERSCORE}[_-]?//g")
|
||||
|
||||
# Clean up any double separators or trailing/leading separators
|
||||
NAME=$(echo "$NAME" | sed -E 's/[_-]{2,}/_/g' | sed 's/^[_-]//' | sed 's/[_-]$//')
|
||||
|
||||
FILE_PATH=$(cd "$(dirname "$FULL_PATH")" && pwd)
|
||||
# Extract architecture from target (e.g., aarch64-apple-darwin -> aarch64)
|
||||
ARCHITECTURE=$(echo "${{ matrix.settings.target }}" | cut -d'-' -f1)
|
||||
echo "name=$NAME" >> $GITHUB_OUTPUT
|
||||
echo "filename=$FILENAME" >> $GITHUB_OUTPUT
|
||||
echo "architecture=$ARCHITECTURE" >> $GITHUB_OUTPUT
|
||||
echo "path=$FILE_PATH" >> $GITHUB_OUTPUT
|
||||
|
||||
# MacOS release — upload the DMG after TDLib bundling and re-signing
|
||||
- name: Upload release asset (MacOS)
|
||||
if: matrix.settings.platform == 'macos-latest' && needs.get-version.outputs.should-publish != ''
|
||||
shell: bash
|
||||
run: |
|
||||
SANITIZED_FILENAME=$(echo "${{ steps.file-info.outputs.name }}" | sed 's/ /./g')
|
||||
PUBLISH_FILE_NAME="$SANITIZED_FILENAME-${{ steps.file-info.outputs.architecture }}.dmg"
|
||||
FILE_PATH="${{ steps.file-info.outputs.path }}/${{ steps.file-info.outputs.filename }}"
|
||||
RELEASE_ID="${{ needs.create-release.outputs.releaseId }}"
|
||||
curl -X POST -H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Content-Type: application/octet-stream" \
|
||||
--data-binary "@$FILE_PATH" \
|
||||
"https://uploads.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID/assets?name=$PUBLISH_FILE_NAME"
|
||||
|
||||
- name: Upload artifact (MacOS)
|
||||
if: matrix.settings.platform == 'macos-latest'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.file-info.outputs.name }}-${{ steps.file-info.outputs.architecture }}.dmg
|
||||
path: ${{ steps.file-info.outputs.path }}/${{ steps.file-info.outputs.filename }}
|
||||
|
||||
# Linux release
|
||||
- name: Upload Linux artifact (Linux)
|
||||
if: matrix.settings.platform == 'ubuntu-22.04'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ steps.file-info.outputs.filename }}
|
||||
path: ${{ steps.file-info.outputs.path }}/${{ steps.file-info.outputs.filename }}
|
||||
|
||||
publish-release:
|
||||
runs-on: ubuntu-latest
|
||||
# needs: [get-version, check-version, create-release, package-tauri, package-android]
|
||||
needs: [get-version, check-version, create-release, package-tauri]
|
||||
environment: ${{ github.ref == 'refs/heads/main' && 'Production' || '' }}
|
||||
if: github.ref == 'refs/heads/main' && needs.get-version.outputs.should-publish == 'true' && needs.check-version.outputs.should-skip != 'true'
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
RELEASE_ID: ${{ needs.create-release.outputs.releaseId }}
|
||||
XGH_TOKEN: ${{ secrets.XGH_TOKEN }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
ref: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref }}
|
||||
|
||||
- name: Publish release
|
||||
run: |
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X PATCH \
|
||||
-H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d '{"draft": false}' \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
if [ "$HTTP_CODE" != "200" ]; then
|
||||
echo "Error: Failed to publish release. HTTP $HTTP_CODE"
|
||||
echo "Response: $BODY"
|
||||
exit 1
|
||||
fi
|
||||
echo "Release published successfully"
|
||||
|
||||
- name: Update Gist with JSON
|
||||
run: |
|
||||
ASSET_ID=$(curl -s -H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID/assets" | jq -r '.[] | select(.name == "latest.json") | .id')
|
||||
JSON_CONTENT=$(curl -sSL -H "Accept: application/octet-stream" \
|
||||
-H "Authorization: Bearer $XGH_TOKEN" \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases/assets/$ASSET_ID")
|
||||
GIST_CONTENT=$(jq -n --arg json "$JSON_CONTENT" '{"files":{"updater.json":{"content":$json}}}')
|
||||
curl -X PATCH -H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
-d "$GIST_CONTENT" \
|
||||
"https://api.github.com/gists/$UPDATER_GIST_ID"
|
||||
|
||||
delete-release-on-failure:
|
||||
name: Delete draft release on build/publish failure
|
||||
runs-on: ubuntu-latest
|
||||
needs: [get-version, create-release, package-tauri, publish-release]
|
||||
if: failure() && needs.create-release.result == 'success' && needs.create-release.outputs.releaseId != ''
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
RELEASE_ID: ${{ needs.create-release.outputs.releaseId }}
|
||||
TAG_NAME: ${{ needs.get-version.outputs.tag-name }}
|
||||
XGH_TOKEN: ${{ secrets.XGH_TOKEN }}
|
||||
steps:
|
||||
- name: Delete draft release
|
||||
run: |
|
||||
echo "Deleting draft release $RELEASE_ID after build or publish failure"
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X DELETE \
|
||||
-H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/releases/$RELEASE_ID")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
BODY=$(echo "$RESPONSE" | sed '$d')
|
||||
if [ "$HTTP_CODE" = "204" ] || [ "$HTTP_CODE" = "404" ]; then
|
||||
echo "Release deleted (or already gone)"
|
||||
else
|
||||
echo "Warning: Delete release returned HTTP $HTTP_CODE. Response: $BODY"
|
||||
fi
|
||||
- name: Delete tag
|
||||
run: |
|
||||
echo "Deleting tag $TAG_NAME"
|
||||
RESPONSE=$(curl -s -w "\n%{http_code}" -X DELETE \
|
||||
-H "Authorization: Bearer $XGH_TOKEN" \
|
||||
-H "Accept: application/vnd.github.v3+json" \
|
||||
"https://api.github.com/repos/$PUBLISH_REPO/git/refs/tags/$TAG_NAME")
|
||||
HTTP_CODE=$(echo "$RESPONSE" | tail -n1)
|
||||
if [ "$HTTP_CODE" = "204" ] || [ "$HTTP_CODE" = "404" ]; then
|
||||
echo "Tag deleted (or already gone)"
|
||||
else
|
||||
echo "Warning: Delete tag returned HTTP $HTTP_CODE"
|
||||
fi
|
||||
@@ -305,27 +305,57 @@ jobs:
|
||||
VITE_SENTRY_DSN: ${{ vars.VITE_SENTRY_DSN }}
|
||||
VITE_DEBUG: ${{ vars.VITE_DEBUG }}
|
||||
|
||||
- name: Resolve core manifest and binary names
|
||||
id: core-paths
|
||||
shell: bash
|
||||
run: |
|
||||
if [ -f "openhuman_core/Cargo.toml" ]; then
|
||||
CORE_DIR="openhuman_core"
|
||||
elif [ -f "rust-core/Cargo.toml" ]; then
|
||||
CORE_DIR="rust-core"
|
||||
else
|
||||
echo "No core Cargo manifest found (expected openhuman_core/Cargo.toml or rust-core/Cargo.toml)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SIDE_CAR_BASE="$(node -e "const fs=require('fs');const c=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8'));const b=(c.bundle&&Array.isArray(c.bundle.externalBin)&&c.bundle.externalBin[0])||'binaries/openhuman';process.stdout.write(String(b).split('/').pop());")"
|
||||
CORE_BIN_NAME="${SIDE_CAR_BASE}"
|
||||
|
||||
echo "core_dir=$CORE_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "core_manifest=$CORE_DIR/Cargo.toml" >> "$GITHUB_OUTPUT"
|
||||
# Cargo workspace: release artifacts are under repo root target/, not <member>/target/
|
||||
echo "core_target_dir=target/$MATRIX_TARGET/release" >> "$GITHUB_OUTPUT"
|
||||
echo "core_bin_name=$CORE_BIN_NAME" >> "$GITHUB_OUTPUT"
|
||||
echo "sidecar_base=$SIDE_CAR_BASE" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
|
||||
- name: Build sidecar core binary
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build \
|
||||
--manifest-path rust-core/Cargo.toml \
|
||||
--manifest-path "$CORE_MANIFEST" \
|
||||
--release \
|
||||
--target "$MATRIX_TARGET" \
|
||||
--bin openhuman-core
|
||||
--bin "$CORE_BIN_NAME"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
CORE_MANIFEST: ${{ steps.core-paths.outputs.core_manifest }}
|
||||
CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }}
|
||||
|
||||
- name: Stage sidecar for Tauri bundler
|
||||
shell: bash
|
||||
run: |
|
||||
mkdir -p src-tauri/binaries
|
||||
SOURCE="rust-core/target/$MATRIX_TARGET/release/openhuman-core"
|
||||
DEST="src-tauri/binaries/openhuman-core-$MATRIX_TARGET"
|
||||
SOURCE="$CORE_TARGET_DIR/$CORE_BIN_NAME"
|
||||
DEST="src-tauri/binaries/$SIDECAR_BASE-$MATRIX_TARGET"
|
||||
cp "$SOURCE" "$DEST"
|
||||
chmod +x "$DEST"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
CORE_TARGET_DIR: ${{ steps.core-paths.outputs.core_target_dir }}
|
||||
CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }}
|
||||
SIDECAR_BASE: ${{ steps.core-paths.outputs.sidecar_base }}
|
||||
|
||||
- name: Build, package and upload to release
|
||||
uses: tauri-apps/tauri-action@v0.6.2
|
||||
@@ -364,32 +394,36 @@ jobs:
|
||||
APP_PATH="src-tauri/target/${{ matrix.settings.target }}/release/bundle/macos/OpenHuman.app"
|
||||
echo "Inspecting bundle at: $APP_PATH"
|
||||
ls -la "$APP_PATH/Contents/MacOS"
|
||||
ls -la "$APP_PATH/Contents/Resources" | grep openhuman-core || true
|
||||
if [ -f "$APP_PATH/Contents/MacOS/openhuman-core" ]; then
|
||||
ls -la "$APP_PATH/Contents/Resources" | grep openhuman || true
|
||||
if [ -f "$APP_PATH/Contents/MacOS/openhuman" ]; then
|
||||
echo "Unexpected standalone core binary found in MacOS dir"
|
||||
exit 1
|
||||
fi
|
||||
if ! ls "$APP_PATH/Contents/Resources"/openhuman-core-* >/dev/null 2>&1; then
|
||||
if ! ls "$APP_PATH/Contents/Resources"/openhuman-* >/dev/null 2>&1; then
|
||||
echo "Sidecar core binary missing from app resources"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Build standalone CLI binary
|
||||
- name: Build standalone core CLI binary
|
||||
shell: bash
|
||||
run: |
|
||||
cargo build --manifest-path rust-core/Cargo.toml --release --target "$MATRIX_TARGET" --bin openhuman-cli
|
||||
cargo build --manifest-path "$CORE_MANIFEST" --release --target "$MATRIX_TARGET" --bin "$CORE_BIN_NAME"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
CORE_MANIFEST: ${{ steps.core-paths.outputs.core_manifest }}
|
||||
CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }}
|
||||
|
||||
- name: Resolve standalone CLI artifact path
|
||||
- name: Resolve standalone core CLI artifact path
|
||||
id: cli-paths
|
||||
shell: bash
|
||||
run: |
|
||||
BASE_DIR="rust-core/target/$MATRIX_TARGET/release"
|
||||
BASE_DIR="$CORE_TARGET_DIR"
|
||||
echo "base_dir=$BASE_DIR" >> "$GITHUB_OUTPUT"
|
||||
echo "cli_path=$BASE_DIR/openhuman-cli" >> "$GITHUB_OUTPUT"
|
||||
echo "cli_path=$BASE_DIR/$CORE_BIN_NAME" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
MATRIX_TARGET: ${{ matrix.settings.target }}
|
||||
CORE_TARGET_DIR: ${{ steps.core-paths.outputs.core_target_dir }}
|
||||
CORE_BIN_NAME: ${{ steps.core-paths.outputs.core_bin_name }}
|
||||
|
||||
- name: Upload standalone CLI artifacts
|
||||
uses: actions/upload-artifact@v4
|
||||
|
||||
@@ -54,3 +54,87 @@ jobs:
|
||||
name: coverage-report
|
||||
path: coverage
|
||||
retention-days: 7
|
||||
|
||||
rust-tests:
|
||||
name: Run Rust Tests
|
||||
runs-on: ubuntu-22.04
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: x86_64-unknown-linux-gnu
|
||||
|
||||
- name: Install Tauri build dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
# Match build.yml: lockfile body only (skip workspace version header noise)
|
||||
- name: Cargo.lock fingerprint (deps only)
|
||||
id: cargo-lock-fingerprint
|
||||
shell: bash
|
||||
run: |
|
||||
echo "hash=$(tail -n +8 Cargo.lock | openssl dgst -sha256 | awk '{print $2}')" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Cache Cargo registry and git sources
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.cargo/registry
|
||||
~/.cargo/git
|
||||
key: ${{ runner.os }}-rust-test-cargo-${{ steps.cargo-lock-fingerprint.outputs.hash }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-rust-test-cargo-
|
||||
|
||||
- name: Test rust-core (openhuman-core)
|
||||
run: cargo test -p openhuman-core
|
||||
|
||||
- name: Test src-tauri (OpenHuman)
|
||||
run: cargo test -p OpenHuman
|
||||
|
||||
e2e-macos:
|
||||
name: E2E (macOS / Appium)
|
||||
runs-on: macos-latest
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
submodules: recursive
|
||||
|
||||
- name: Setup Node.js 24.x
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24.x
|
||||
cache: 'yarn'
|
||||
|
||||
- name: Install Rust stable
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
|
||||
- name: Install dependencies
|
||||
run: yarn install --frozen-lockfile
|
||||
|
||||
- name: Install skills dependencies
|
||||
run: cd skills && yarn install --frozen-lockfile
|
||||
|
||||
- name: Ensure .env exists for E2E build
|
||||
run: touch .env
|
||||
|
||||
- name: Install Appium and mac2 driver
|
||||
run: |
|
||||
npm install -g appium
|
||||
appium driver install mac2
|
||||
|
||||
- name: Build E2E app bundle
|
||||
run: yarn test:e2e:build
|
||||
env:
|
||||
E2E_SKIP_CARGO_CLEAN: '1'
|
||||
|
||||
- name: Run all E2E flows
|
||||
run: yarn test:e2e:all:flows
|
||||
|
||||
@@ -1,197 +0,0 @@
|
||||
name: Update Changelog
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
concurrency:
|
||||
group: update-changelog
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
update-changelog:
|
||||
runs-on: ubuntu-latest
|
||||
# Skip if commit message contains [skip ci] or is a version bump commit
|
||||
if: "!contains(github.event.head_commit.message, '[skip ci]') && !contains(github.event.head_commit.message, 'chore: bump version')"
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Configure Git
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Fetch tags
|
||||
run: |
|
||||
git fetch origin --tags
|
||||
|
||||
- name: Calculate next version
|
||||
id: next-version
|
||||
run: |
|
||||
# Get the latest version tag (format: v1.2.3)
|
||||
LATEST_TAG=$(git tag -l "v*" | sort -V | tail -n 1 || echo "")
|
||||
|
||||
if [ -z "$LATEST_TAG" ]; then
|
||||
# No tags found, start with v1.0.0
|
||||
NEXT_VERSION="v1.0.0"
|
||||
LATEST_TAG="(none)"
|
||||
else
|
||||
# Extract version number (remove 'v' prefix)
|
||||
VERSION="${LATEST_TAG#v}"
|
||||
|
||||
# Split into major.minor.patch
|
||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION"
|
||||
|
||||
# Ensure we have valid numbers (default to 0 if empty)
|
||||
MAJOR=${MAJOR:-0}
|
||||
MINOR=${MINOR:-0}
|
||||
PATCH=${PATCH:-0}
|
||||
|
||||
# Increment minor version and reset patch to 0
|
||||
MINOR=$((MINOR + 1))
|
||||
PATCH=0
|
||||
|
||||
NEXT_VERSION="v${MAJOR}.${MINOR}.${PATCH}"
|
||||
fi
|
||||
|
||||
echo "Latest tag: $LATEST_TAG"
|
||||
echo "Predicted next version: $NEXT_VERSION"
|
||||
echo "version=$NEXT_VERSION" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Get commits since last tag
|
||||
id: get-commits
|
||||
run: |
|
||||
# Get the latest actual tag (not the predicted one)
|
||||
LATEST_TAG=$(git tag -l "v*" | sort -V | tail -n 1 || echo "")
|
||||
|
||||
# Get commits since the last tag (or all commits if no tag exists)
|
||||
if [ -n "$LATEST_TAG" ] && git rev-parse "$LATEST_TAG" >/dev/null 2>&1; then
|
||||
# Tag exists, get commits since that tag
|
||||
git log "$LATEST_TAG"..HEAD --pretty=format:"- %s (%h)" --no-merges > commits.txt || echo "- $(git log -1 --pretty=format:'%s (%h)')" > commits.txt
|
||||
else
|
||||
# No tag exists, get all commits
|
||||
git log --pretty=format:"- %s (%h)" --no-merges > commits.txt || echo "- $(git log -1 --pretty=format:'%s (%h)')" > commits.txt
|
||||
fi
|
||||
|
||||
# If file is empty, use current commit
|
||||
if [ ! -s commits.txt ]; then
|
||||
echo "- $(git log -1 --pretty=format:'%s (%h)')" > commits.txt
|
||||
fi
|
||||
|
||||
- name: Get current date
|
||||
id: date
|
||||
run: |
|
||||
echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Update CHANGELOG.md
|
||||
run: |
|
||||
CHANGELOG_PATH="publish/CHANGELOG.md"
|
||||
NEXT_VERSION="${{ steps.next-version.outputs.version }}"
|
||||
RELEASE_DATE="${{ steps.date.outputs.date }}"
|
||||
|
||||
# Create changelog directory if it doesn't exist
|
||||
mkdir -p publish
|
||||
|
||||
# Check if changelog exists and has content
|
||||
if [ ! -f "$CHANGELOG_PATH" ] || [ ! -s "$CHANGELOG_PATH" ]; then
|
||||
# Create new changelog with header
|
||||
cat > "$CHANGELOG_PATH" <<'EOF'
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Added
|
||||
- Initial changelog
|
||||
|
||||
EOF
|
||||
fi
|
||||
|
||||
# Check if this version already exists in changelog
|
||||
if grep -q "## \[$NEXT_VERSION\]" "$CHANGELOG_PATH"; then
|
||||
echo "Version $NEXT_VERSION already exists in CHANGELOG.md. Skipping update."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Use Python to update the changelog
|
||||
python3 <<PYTHON_SCRIPT
|
||||
changelog_path = "$CHANGELOG_PATH"
|
||||
next_version = "$NEXT_VERSION"
|
||||
release_date = "$RELEASE_DATE"
|
||||
|
||||
# Read commits
|
||||
with open("commits.txt", "r") as f:
|
||||
commits = f.read().strip()
|
||||
|
||||
# Read existing changelog
|
||||
with open(changelog_path, "r") as f:
|
||||
lines = f.readlines()
|
||||
|
||||
# Create new version entry
|
||||
new_entry = f"""## [{next_version}] - {release_date}
|
||||
|
||||
### Changed
|
||||
{commits}
|
||||
|
||||
"""
|
||||
|
||||
# Find [Unreleased] section and insert after it
|
||||
output_lines = []
|
||||
inserted = False
|
||||
i = 0
|
||||
while i < len(lines):
|
||||
line = lines[i]
|
||||
output_lines.append(line)
|
||||
|
||||
# Check if this is the [Unreleased] header line
|
||||
if line.strip() == "## [Unreleased]":
|
||||
# Find the end of the [Unreleased] section (next ## header or end of file)
|
||||
i += 1
|
||||
while i < len(lines) and not lines[i].startswith("##"):
|
||||
output_lines.append(lines[i])
|
||||
i += 1
|
||||
# Insert new entry after the [Unreleased] section
|
||||
output_lines.append("\n")
|
||||
output_lines.append(new_entry)
|
||||
inserted = True
|
||||
# Continue with remaining lines
|
||||
continue
|
||||
|
||||
i += 1
|
||||
|
||||
# If [Unreleased] not found, prepend
|
||||
if not inserted:
|
||||
output_lines = [new_entry, "\n"] + lines
|
||||
|
||||
# Write updated changelog
|
||||
with open(changelog_path, "w") as f:
|
||||
f.writelines(output_lines)
|
||||
|
||||
print(f"Updated CHANGELOG.md with version {next_version}")
|
||||
PYTHON_SCRIPT
|
||||
|
||||
- name: Commit and push changes
|
||||
run: |
|
||||
git add publish/CHANGELOG.md
|
||||
|
||||
# Check if there are changes to commit
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git commit -m "chore: update changelog for ${{ steps.next-version.outputs.version }} [skip ci]"
|
||||
git push origin develop
|
||||
+1
-1
@@ -32,6 +32,6 @@ set -e
|
||||
|
||||
# Exit with error if any command still fails after fixes
|
||||
if [ $FORMAT_EXIT -ne 0 ] || [ $LINT_EXIT -ne 0 ] || [ $COMPILE_EXIT -ne 0 ]; then
|
||||
echo "Pre-push checks failed. Please fix format, lint, and/or TypeScript errors before pushing."
|
||||
echo "Pre-push checks failed. Please fix format (Prettier + cargo fmt), lint, and/or TypeScript errors before pushing."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -2,6 +2,7 @@ node_modules
|
||||
dist
|
||||
coverage
|
||||
src-tauri
|
||||
rust-core
|
||||
skills
|
||||
*.config.js
|
||||
*.config.ts
|
||||
|
||||
+6
-11
@@ -25,14 +25,16 @@
|
||||
"test:unit:watch": "vitest --config test/vitest.config.ts",
|
||||
"test:watch": "vitest --config test/vitest.config.ts",
|
||||
"test:coverage": "vitest run --config test/vitest.config.ts --coverage",
|
||||
"test:rust": "source $HOME/.cargo/env 2>/dev/null; cargo test --manifest-path src-tauri/Cargo.toml",
|
||||
"test:rust": "source $HOME/.cargo/env 2>/dev/null; cargo test --workspace",
|
||||
"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:e2e:all:flows": "bash scripts/e2e-run-all-flows.sh",
|
||||
"test:e2e:all": "yarn test:e2e:build && yarn test:e2e:all:flows",
|
||||
"test:all": "yarn test:coverage && yarn test:rust && yarn test:e2e",
|
||||
"format": "prettier --write .",
|
||||
"format:check": "prettier --check .",
|
||||
"format": "prettier --write . && cargo fmt --all",
|
||||
"format:check": "prettier --check . && cargo fmt --all --check",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"lint:fix": "eslint . --ext .ts,.tsx --fix",
|
||||
"skills:build": "cd skills && yarn build",
|
||||
@@ -51,16 +53,12 @@
|
||||
"@tauri-apps/plugin-deep-link": "^2",
|
||||
"@tauri-apps/plugin-opener": "^2",
|
||||
"@tauri-apps/plugin-os": "^2.3.2",
|
||||
"@tauri-apps/plugin-shell": "~2",
|
||||
"@types/react-router-dom": "^5.3.3",
|
||||
"@types/three": "^0.183.1",
|
||||
"buffer": "^6.0.3",
|
||||
"debug": "^4.4.3",
|
||||
"immer": "^11.1.3",
|
||||
"lottie-react": "^2.4.1",
|
||||
"os-browserify": "^0.3.0",
|
||||
"process": "^0.11.10",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "^19.1.0",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
@@ -69,10 +67,8 @@
|
||||
"redux-logger": "^3.0.6",
|
||||
"redux-persist": "^6.0.0",
|
||||
"socket.io-client": "^4.8.3",
|
||||
"telegram": "^2.26.22",
|
||||
"three": "^0.183.2",
|
||||
"util": "^0.12.5",
|
||||
"zustand": "^5.0.10"
|
||||
"util": "^0.12.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.39.2",
|
||||
@@ -82,7 +78,6 @@
|
||||
"@testing-library/dom": "^10.4.1",
|
||||
"@testing-library/jest-dom": "^6.9.1",
|
||||
"@testing-library/react": "^16.3.2",
|
||||
"@testing-library/user-event": "^14.6.1",
|
||||
"@trivago/prettier-plugin-sort-imports": "^6.0.2",
|
||||
"@types/debug": "^4.1.12",
|
||||
"@types/node": "^25.0.10",
|
||||
|
||||
@@ -1,388 +0,0 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Debug, Serialize)]
|
||||
struct RpcRequest {
|
||||
jsonrpc: &'static str,
|
||||
id: u64,
|
||||
method: String,
|
||||
params: serde_json::Value,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RpcResponse {
|
||||
result: Option<serde_json::Value>,
|
||||
error: Option<RpcError>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct RpcError {
|
||||
code: i64,
|
||||
message: String,
|
||||
data: Option<serde_json::Value>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "openhuman-cli")]
|
||||
#[command(about = "CLI for the OpenHuman core RPC server")]
|
||||
struct Cli {
|
||||
/// Core RPC endpoint URL
|
||||
#[arg(long, global = true)]
|
||||
rpc_url: Option<String>,
|
||||
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
/// Check core health
|
||||
Ping,
|
||||
/// Print core version
|
||||
Version,
|
||||
/// Get health snapshot
|
||||
Health,
|
||||
/// Get runtime flags
|
||||
RuntimeFlags,
|
||||
|
||||
/// Config operations
|
||||
Config {
|
||||
#[command(subcommand)]
|
||||
command: ConfigCommand,
|
||||
},
|
||||
|
||||
/// Service operations
|
||||
Service {
|
||||
#[command(subcommand)]
|
||||
command: ServiceCommand,
|
||||
},
|
||||
|
||||
/// Doctor operations
|
||||
Doctor {
|
||||
#[command(subcommand)]
|
||||
command: DoctorCommand,
|
||||
},
|
||||
|
||||
/// Integrations operations
|
||||
Integrations {
|
||||
#[command(subcommand)]
|
||||
command: IntegrationsCommand,
|
||||
},
|
||||
|
||||
/// Send one-shot agent message
|
||||
AgentChat(AgentChatArgs),
|
||||
|
||||
/// Hardware operations
|
||||
Hardware {
|
||||
#[command(subcommand)]
|
||||
command: HardwareCommand,
|
||||
},
|
||||
|
||||
/// Encrypt a secret
|
||||
Encrypt { plaintext: String },
|
||||
|
||||
/// Decrypt a secret
|
||||
Decrypt { ciphertext: String },
|
||||
|
||||
/// Toggle browser allow-all runtime flag
|
||||
BrowserAllowAll {
|
||||
#[arg(long)]
|
||||
enabled: bool,
|
||||
},
|
||||
|
||||
/// Refresh model catalog
|
||||
ModelsRefresh {
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long, default_value_t = false)]
|
||||
force: bool,
|
||||
},
|
||||
|
||||
/// Migrate OpenClaw memory
|
||||
MigrateOpenclaw {
|
||||
#[arg(long)]
|
||||
source_workspace: Option<String>,
|
||||
#[arg(long, default_value_t = true)]
|
||||
dry_run: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum ConfigCommand {
|
||||
/// Get full config snapshot
|
||||
Get,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum ServiceCommand {
|
||||
Install,
|
||||
Start,
|
||||
Stop,
|
||||
Status,
|
||||
Reinstall,
|
||||
Uninstall,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum DoctorCommand {
|
||||
/// Run doctor checks
|
||||
Report,
|
||||
/// Probe model catalog
|
||||
Models {
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long, default_value_t = true)]
|
||||
use_cache: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum IntegrationsCommand {
|
||||
/// List integrations
|
||||
List,
|
||||
/// Get one integration info
|
||||
Info {
|
||||
#[arg(long)]
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum HardwareCommand {
|
||||
/// Discover connected hardware
|
||||
Discover,
|
||||
/// Introspect one device path
|
||||
Introspect {
|
||||
#[arg(long)]
|
||||
path: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct AgentChatArgs {
|
||||
message: String,
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long)]
|
||||
model: Option<String>,
|
||||
#[arg(long)]
|
||||
temperature: Option<f64>,
|
||||
}
|
||||
|
||||
fn endpoint(cli: &Cli) -> String {
|
||||
if let Some(url) = &cli.rpc_url {
|
||||
return url.clone();
|
||||
}
|
||||
std::env::var("OPENHUMAN_CORE_RPC_URL")
|
||||
.unwrap_or_else(|_| "http://127.0.0.1:7788/rpc".to_string())
|
||||
}
|
||||
|
||||
fn call(url: &str, method: &str, params: serde_json::Value) -> Result<serde_json::Value, String> {
|
||||
let req = RpcRequest {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: method.to_string(),
|
||||
params,
|
||||
};
|
||||
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let resp = client
|
||||
.post(url)
|
||||
.json(&req)
|
||||
.send()
|
||||
.map_err(|e| format!("request failed: {e}"))?;
|
||||
|
||||
let payload: RpcResponse = resp.json().map_err(|e| format!("invalid response: {e}"))?;
|
||||
|
||||
if let Some(err) = payload.error {
|
||||
return Err(format!(
|
||||
"rpc error {}: {}{}",
|
||||
err.code,
|
||||
err.message,
|
||||
err.data.map(|d| format!(" ({d})")).unwrap_or_default()
|
||||
));
|
||||
}
|
||||
|
||||
Ok(payload.result.unwrap_or(serde_json::Value::Null))
|
||||
}
|
||||
|
||||
fn execute(cli: Cli) -> Result<serde_json::Value, String> {
|
||||
let url = endpoint(&cli);
|
||||
|
||||
match cli.command {
|
||||
Command::Ping => call(&url, "core.ping", serde_json::json!({})),
|
||||
Command::Version => call(&url, "core.version", serde_json::json!({})),
|
||||
Command::Health => call(&url, "openhuman.health_snapshot", serde_json::json!({})),
|
||||
Command::RuntimeFlags => call(&url, "openhuman.get_runtime_flags", serde_json::json!({})),
|
||||
Command::Config { command } => match command {
|
||||
ConfigCommand::Get => call(&url, "openhuman.get_config", serde_json::json!({})),
|
||||
},
|
||||
Command::Service { command } => match command {
|
||||
ServiceCommand::Install => local_service_install(),
|
||||
ServiceCommand::Start => local_service_start(),
|
||||
ServiceCommand::Stop => local_service_stop(),
|
||||
ServiceCommand::Status => local_service_status(),
|
||||
ServiceCommand::Reinstall => {
|
||||
let _ = local_service_uninstall()?;
|
||||
local_service_install()
|
||||
}
|
||||
ServiceCommand::Uninstall => local_service_uninstall(),
|
||||
},
|
||||
Command::Doctor { command } => match command {
|
||||
DoctorCommand::Report => call(&url, "openhuman.doctor_report", serde_json::json!({})),
|
||||
DoctorCommand::Models {
|
||||
provider,
|
||||
use_cache,
|
||||
} => call(
|
||||
&url,
|
||||
"openhuman.doctor_models",
|
||||
serde_json::json!({
|
||||
"provider_override": provider,
|
||||
"use_cache": use_cache,
|
||||
}),
|
||||
),
|
||||
},
|
||||
Command::Integrations { command } => match command {
|
||||
IntegrationsCommand::List => {
|
||||
call(&url, "openhuman.list_integrations", serde_json::json!({}))
|
||||
}
|
||||
IntegrationsCommand::Info { name } => call(
|
||||
&url,
|
||||
"openhuman.get_integration_info",
|
||||
serde_json::json!({ "name": name }),
|
||||
),
|
||||
},
|
||||
Command::AgentChat(args) => call(
|
||||
&url,
|
||||
"openhuman.agent_chat",
|
||||
serde_json::json!({
|
||||
"message": args.message,
|
||||
"provider_override": args.provider,
|
||||
"model_override": args.model,
|
||||
"temperature": args.temperature,
|
||||
}),
|
||||
),
|
||||
Command::Hardware { command } => match command {
|
||||
HardwareCommand::Discover => {
|
||||
call(&url, "openhuman.hardware_discover", serde_json::json!({}))
|
||||
}
|
||||
HardwareCommand::Introspect { path } => call(
|
||||
&url,
|
||||
"openhuman.hardware_introspect",
|
||||
serde_json::json!({ "path": path }),
|
||||
),
|
||||
},
|
||||
Command::Encrypt { plaintext } => call(
|
||||
&url,
|
||||
"openhuman.encrypt_secret",
|
||||
serde_json::json!({ "plaintext": plaintext }),
|
||||
),
|
||||
Command::Decrypt { ciphertext } => call(
|
||||
&url,
|
||||
"openhuman.decrypt_secret",
|
||||
serde_json::json!({ "ciphertext": ciphertext }),
|
||||
),
|
||||
Command::BrowserAllowAll { enabled } => call(
|
||||
&url,
|
||||
"openhuman.set_browser_allow_all",
|
||||
serde_json::json!({ "enabled": enabled }),
|
||||
),
|
||||
Command::ModelsRefresh { provider, force } => call(
|
||||
&url,
|
||||
"openhuman.models_refresh",
|
||||
serde_json::json!({
|
||||
"provider_override": provider,
|
||||
"force": force,
|
||||
}),
|
||||
),
|
||||
Command::MigrateOpenclaw {
|
||||
source_workspace,
|
||||
dry_run,
|
||||
} => call(
|
||||
&url,
|
||||
"openhuman.migrate_openclaw",
|
||||
serde_json::json!({
|
||||
"source_workspace": source_workspace,
|
||||
"dry_run": dry_run,
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config() -> Result<openhuman_core::openhuman::config::Config, String> {
|
||||
let runtime = tokio::runtime::Builder::new_current_thread()
|
||||
.enable_all()
|
||||
.build()
|
||||
.map_err(|e| format!("failed to build runtime: {e}"))?;
|
||||
runtime
|
||||
.block_on(openhuman_core::openhuman::config::Config::load_or_init())
|
||||
.map_err(|e| format!("failed to load config: {e}"))
|
||||
}
|
||||
|
||||
fn status_value(
|
||||
status: openhuman_core::openhuman::service::ServiceStatus,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
serde_json::to_value(status).map_err(|e| format!("failed to serialize service status: {e}"))
|
||||
}
|
||||
|
||||
fn command_response_json(
|
||||
status: openhuman_core::openhuman::service::ServiceStatus,
|
||||
log: &str,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
Ok(json!({
|
||||
"result": status_value(status)?,
|
||||
"logs": [log],
|
||||
}))
|
||||
}
|
||||
|
||||
fn local_service_install() -> Result<serde_json::Value, String> {
|
||||
let config = load_config()?;
|
||||
let status = openhuman_core::openhuman::service::install(&config)
|
||||
.map_err(|e| format!("service install failed: {e}"))?;
|
||||
command_response_json(status, "service install completed")
|
||||
}
|
||||
|
||||
fn local_service_start() -> Result<serde_json::Value, String> {
|
||||
let config = load_config()?;
|
||||
let status = openhuman_core::openhuman::service::start(&config)
|
||||
.map_err(|e| format!("service start failed: {e}"))?;
|
||||
command_response_json(status, "service start completed")
|
||||
}
|
||||
|
||||
fn local_service_stop() -> Result<serde_json::Value, String> {
|
||||
let config = load_config()?;
|
||||
let status = openhuman_core::openhuman::service::stop(&config)
|
||||
.map_err(|e| format!("service stop failed: {e}"))?;
|
||||
command_response_json(status, "service stop completed")
|
||||
}
|
||||
|
||||
fn local_service_status() -> Result<serde_json::Value, String> {
|
||||
let config = load_config()?;
|
||||
let status = openhuman_core::openhuman::service::status(&config)
|
||||
.map_err(|e| format!("service status failed: {e}"))?;
|
||||
command_response_json(status, "service status fetched")
|
||||
}
|
||||
|
||||
fn local_service_uninstall() -> Result<serde_json::Value, String> {
|
||||
let config = load_config()?;
|
||||
let status = openhuman_core::openhuman::service::uninstall(&config)
|
||||
.map_err(|e| format!("service uninstall failed: {e}"))?;
|
||||
command_response_json(status, "service uninstall completed")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
match execute(cli) {
|
||||
Ok(value) => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value).unwrap_or_else(|_| "null".to_string())
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
fn main() {
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
if let Err(err) = openhuman_core::core_server::run_from_cli_args(&args) {
|
||||
eprintln!("openhuman-core failed: {err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,334 @@
|
||||
use clap::{Args, Parser, Subcommand};
|
||||
use serde_json::json;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(name = "openhuman")]
|
||||
#[command(about = "OpenHuman core CLI")]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
/// Run JSON-RPC server
|
||||
Serve {
|
||||
#[arg(long)]
|
||||
port: Option<u16>,
|
||||
},
|
||||
/// Check core health
|
||||
Ping,
|
||||
/// Print core version
|
||||
Version,
|
||||
/// Get health snapshot
|
||||
Health,
|
||||
/// Get runtime flags
|
||||
RuntimeFlags,
|
||||
/// Get security policy info
|
||||
SecurityPolicy,
|
||||
/// Generic JSON-RPC style method call
|
||||
Call {
|
||||
#[arg(long)]
|
||||
method: String,
|
||||
#[arg(long, default_value = "{}")]
|
||||
params: String,
|
||||
},
|
||||
/// Config operations
|
||||
Config {
|
||||
#[command(subcommand)]
|
||||
command: ConfigCommand,
|
||||
},
|
||||
/// Service operations
|
||||
Service {
|
||||
#[command(subcommand)]
|
||||
command: ServiceCommand,
|
||||
},
|
||||
/// Doctor operations
|
||||
Doctor {
|
||||
#[command(subcommand)]
|
||||
command: DoctorCommand,
|
||||
},
|
||||
/// Integrations operations
|
||||
Integrations {
|
||||
#[command(subcommand)]
|
||||
command: IntegrationsCommand,
|
||||
},
|
||||
/// Send one-shot agent message
|
||||
AgentChat(AgentChatArgs),
|
||||
/// Hardware operations
|
||||
Hardware {
|
||||
#[command(subcommand)]
|
||||
command: HardwareCommand,
|
||||
},
|
||||
/// Encrypt a secret
|
||||
Encrypt { plaintext: String },
|
||||
/// Decrypt a secret
|
||||
Decrypt { ciphertext: String },
|
||||
/// Toggle browser allow-all runtime flag
|
||||
BrowserAllowAll {
|
||||
#[arg(long)]
|
||||
enabled: bool,
|
||||
},
|
||||
/// Refresh model catalog
|
||||
ModelsRefresh {
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long, default_value_t = false)]
|
||||
force: bool,
|
||||
},
|
||||
/// Migrate OpenClaw memory
|
||||
MigrateOpenclaw {
|
||||
#[arg(long)]
|
||||
source_workspace: Option<String>,
|
||||
#[arg(long, default_value_t = true)]
|
||||
dry_run: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum ConfigCommand {
|
||||
/// Get full config snapshot
|
||||
Get,
|
||||
/// Update model settings with a JSON object
|
||||
UpdateModel {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
/// Update memory settings with a JSON object
|
||||
UpdateMemory {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
/// Update gateway settings with a JSON object
|
||||
UpdateGateway {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
/// Update runtime settings with a JSON object
|
||||
UpdateRuntime {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
/// Update browser settings with a JSON object
|
||||
UpdateBrowser {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
/// Replace tunnel settings with a JSON object
|
||||
UpdateTunnel {
|
||||
#[arg(long)]
|
||||
json: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum ServiceCommand {
|
||||
Install,
|
||||
Start,
|
||||
Stop,
|
||||
Status,
|
||||
Reinstall,
|
||||
Uninstall,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum DoctorCommand {
|
||||
/// Run doctor checks
|
||||
Report,
|
||||
/// Probe model catalog
|
||||
Models {
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long, default_value_t = true)]
|
||||
use_cache: bool,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum IntegrationsCommand {
|
||||
/// List integrations
|
||||
List,
|
||||
/// Get one integration info
|
||||
Info {
|
||||
#[arg(long)]
|
||||
name: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum HardwareCommand {
|
||||
/// Discover connected hardware
|
||||
Discover,
|
||||
/// Introspect one device path
|
||||
Introspect {
|
||||
#[arg(long)]
|
||||
path: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
struct AgentChatArgs {
|
||||
message: String,
|
||||
#[arg(long)]
|
||||
provider: Option<String>,
|
||||
#[arg(long)]
|
||||
model: Option<String>,
|
||||
#[arg(long)]
|
||||
temperature: Option<f64>,
|
||||
}
|
||||
|
||||
fn parse_json_arg(raw: &str) -> Result<serde_json::Value, String> {
|
||||
serde_json::from_str(raw).map_err(|e| format!("invalid JSON for --json/--params: {e}"))
|
||||
}
|
||||
|
||||
async fn call_local(method: &str, params: serde_json::Value) -> Result<serde_json::Value, String> {
|
||||
openhuman_core::core_server::call_method(method, params).await
|
||||
}
|
||||
|
||||
async fn execute(cli: Cli) -> Result<serde_json::Value, String> {
|
||||
match cli.command {
|
||||
Command::Serve { port } => openhuman_core::core_server::run_server(port)
|
||||
.await
|
||||
.map(|_| serde_json::Value::Null)
|
||||
.map_err(|e| format!("serve failed: {e}")),
|
||||
Command::Ping => call_local("core.ping", json!({})).await,
|
||||
Command::Version => call_local("core.version", json!({})).await,
|
||||
Command::Health => call_local("openhuman.health_snapshot", json!({})).await,
|
||||
Command::RuntimeFlags => call_local("openhuman.get_runtime_flags", json!({})).await,
|
||||
Command::SecurityPolicy => call_local("openhuman.security_policy_info", json!({})).await,
|
||||
Command::Call { method, params } => call_local(&method, parse_json_arg(¶ms)?).await,
|
||||
Command::Config { command } => match command {
|
||||
ConfigCommand::Get => call_local("openhuman.get_config", json!({})).await,
|
||||
ConfigCommand::UpdateModel { json } => {
|
||||
call_local("openhuman.update_model_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
ConfigCommand::UpdateMemory { json } => {
|
||||
call_local("openhuman.update_memory_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
ConfigCommand::UpdateGateway { json } => {
|
||||
call_local("openhuman.update_gateway_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
ConfigCommand::UpdateRuntime { json } => {
|
||||
call_local("openhuman.update_runtime_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
ConfigCommand::UpdateBrowser { json } => {
|
||||
call_local("openhuman.update_browser_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
ConfigCommand::UpdateTunnel { json } => {
|
||||
call_local("openhuman.update_tunnel_settings", parse_json_arg(&json)?).await
|
||||
}
|
||||
},
|
||||
Command::Service { command } => match command {
|
||||
ServiceCommand::Install => call_local("openhuman.service_install", json!({})).await,
|
||||
ServiceCommand::Start => call_local("openhuman.service_start", json!({})).await,
|
||||
ServiceCommand::Stop => call_local("openhuman.service_stop", json!({})).await,
|
||||
ServiceCommand::Status => call_local("openhuman.service_status", json!({})).await,
|
||||
ServiceCommand::Reinstall => {
|
||||
call_local("openhuman.service_uninstall", json!({})).await?;
|
||||
call_local("openhuman.service_install", json!({})).await
|
||||
}
|
||||
ServiceCommand::Uninstall => call_local("openhuman.service_uninstall", json!({})).await,
|
||||
},
|
||||
Command::Doctor { command } => match command {
|
||||
DoctorCommand::Report => call_local("openhuman.doctor_report", json!({})).await,
|
||||
DoctorCommand::Models {
|
||||
provider,
|
||||
use_cache,
|
||||
} => {
|
||||
call_local(
|
||||
"openhuman.doctor_models",
|
||||
json!({
|
||||
"provider_override": provider,
|
||||
"use_cache": use_cache,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
},
|
||||
Command::Integrations { command } => match command {
|
||||
IntegrationsCommand::List => call_local("openhuman.list_integrations", json!({})).await,
|
||||
IntegrationsCommand::Info { name } => {
|
||||
call_local("openhuman.get_integration_info", json!({ "name": name })).await
|
||||
}
|
||||
},
|
||||
Command::AgentChat(args) => {
|
||||
call_local(
|
||||
"openhuman.agent_chat",
|
||||
json!({
|
||||
"message": args.message,
|
||||
"provider_override": args.provider,
|
||||
"model_override": args.model,
|
||||
"temperature": args.temperature,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Command::Hardware { command } => match command {
|
||||
HardwareCommand::Discover => call_local("openhuman.hardware_discover", json!({})).await,
|
||||
HardwareCommand::Introspect { path } => {
|
||||
call_local("openhuman.hardware_introspect", json!({ "path": path })).await
|
||||
}
|
||||
},
|
||||
Command::Encrypt { plaintext } => {
|
||||
call_local(
|
||||
"openhuman.encrypt_secret",
|
||||
json!({ "plaintext": plaintext }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Command::Decrypt { ciphertext } => {
|
||||
call_local(
|
||||
"openhuman.decrypt_secret",
|
||||
json!({ "ciphertext": ciphertext }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Command::BrowserAllowAll { enabled } => {
|
||||
call_local(
|
||||
"openhuman.set_browser_allow_all",
|
||||
json!({ "enabled": enabled }),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Command::ModelsRefresh { provider, force } => {
|
||||
call_local(
|
||||
"openhuman.models_refresh",
|
||||
json!({
|
||||
"provider_override": provider,
|
||||
"force": force,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
Command::MigrateOpenclaw {
|
||||
source_workspace,
|
||||
dry_run,
|
||||
} => {
|
||||
call_local(
|
||||
"openhuman.migrate_openclaw",
|
||||
json!({
|
||||
"source_workspace": source_workspace,
|
||||
"dry_run": dry_run,
|
||||
}),
|
||||
)
|
||||
.await
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let cli = Cli::parse();
|
||||
match execute(cli).await {
|
||||
Ok(value) => {
|
||||
println!(
|
||||
"{}",
|
||||
serde_json::to_string_pretty(&value).unwrap_or_else(|_| "null".to_string())
|
||||
);
|
||||
}
|
||||
Err(err) => {
|
||||
eprintln!("{err}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,6 +640,20 @@ async fn dispatch(
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn call_method(
|
||||
method: &str,
|
||||
params: serde_json::Value,
|
||||
) -> Result<serde_json::Value, String> {
|
||||
dispatch(
|
||||
AppState {
|
||||
core_version: env!("CARGO_PKG_VERSION").to_string(),
|
||||
},
|
||||
method,
|
||||
params,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn health_handler() -> impl IntoResponse {
|
||||
(StatusCode::OK, Json(json!({ "ok": true })))
|
||||
}
|
||||
|
||||
@@ -16,13 +16,14 @@ fn windows_task_name() -> &'static str {
|
||||
}
|
||||
|
||||
fn daemon_program_args(exe: &std::path::Path) -> Vec<String> {
|
||||
let file_name = exe
|
||||
.file_name()
|
||||
.and_then(|n| n.to_str())
|
||||
.unwrap_or_default()
|
||||
.to_ascii_lowercase();
|
||||
let raw_file_name = exe.file_name().and_then(|n| n.to_str()).unwrap_or_default();
|
||||
let file_name = raw_file_name.to_ascii_lowercase();
|
||||
let standalone_core_binary = file_name.contains("openhuman-core")
|
||||
|| file_name.starts_with("openhuman-")
|
||||
|| raw_file_name == "openhuman"
|
||||
|| raw_file_name == "openhuman.exe";
|
||||
|
||||
if file_name.contains("openhuman-core") {
|
||||
if standalone_core_binary {
|
||||
vec!["serve".to_string()]
|
||||
} else {
|
||||
vec!["core".to_string(), "serve".to_string()]
|
||||
|
||||
@@ -119,7 +119,7 @@ mod tests {
|
||||
assert_eq!(result.unwrap(), "test");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn test_safe_async_execute_with_runtime() {
|
||||
let result = safe_async_execute(async { "test".to_string() });
|
||||
assert!(result.is_ok());
|
||||
|
||||
+2
-88
@@ -1,90 +1,4 @@
|
||||
#!/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
|
||||
#
|
||||
# Run E2E auth & access control tests only. See scripts/e2e-run-spec.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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 test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/auth-access-control.spec.ts" "auth"
|
||||
|
||||
+13
-7
@@ -14,16 +14,22 @@ 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
|
||||
if [ -z "${E2E_SKIP_CARGO_CLEAN:-}" ]; then
|
||||
cargo clean --manifest-path src-tauri/Cargo.toml
|
||||
else
|
||||
echo "Skipping cargo clean (E2E_SKIP_CARGO_CLEAN is set)."
|
||||
fi
|
||||
|
||||
# Load .env for other vars (Telegram API keys, etc.)
|
||||
source scripts/load-dotenv.sh
|
||||
if [ -f .env ]; then
|
||||
# shellcheck source=/dev/null
|
||||
source scripts/load-dotenv.sh
|
||||
else
|
||||
echo "No .env file — skipping load-dotenv (optional for CI)."
|
||||
fi
|
||||
|
||||
# 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
|
||||
# Use npx so CI does not require a global Tauri CLI
|
||||
npx tauri build --bundles app --debug
|
||||
|
||||
echo "E2E build complete."
|
||||
|
||||
@@ -1,90 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run E2E cryptocurrency payment flow tests only.
|
||||
#
|
||||
# Starts Appium, cleans app caches, runs the crypto-payment-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-crypto-payment.sh
|
||||
# APPIUM_PORT=4723 ./scripts/e2e-crypto-payment.sh
|
||||
#
|
||||
# Run E2E crypto payment flow tests only. See scripts/e2e-run-spec.sh.
|
||||
set -euo pipefail
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
SPEC="test/e2e/specs/crypto-payment-flow.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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-crypto-payment.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 crypto payment flow tests ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment"
|
||||
|
||||
+2
-88
@@ -1,90 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run E2E Gmail integration flow tests only.
|
||||
#
|
||||
# Starts Appium, cleans app caches, runs the gmail-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-gmail.sh
|
||||
# APPIUM_PORT=4723 ./scripts/e2e-gmail.sh
|
||||
#
|
||||
# Run E2E Gmail integration flow tests only. See scripts/e2e-run-spec.sh.
|
||||
set -euo pipefail
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
SPEC="test/e2e/specs/gmail-flow.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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-gmail.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 Gmail integration flow tests ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/gmail-flow.spec.ts" "gmail"
|
||||
|
||||
+2
-91
@@ -1,93 +1,4 @@
|
||||
#!/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
|
||||
#
|
||||
# Run E2E login flow tests only. See scripts/e2e-run-spec.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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 test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/login-flow.spec.ts" "login"
|
||||
|
||||
+2
-88
@@ -1,90 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run E2E Notion integration flow tests only.
|
||||
#
|
||||
# Starts Appium, cleans app caches, runs the notion-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-notion.sh
|
||||
# APPIUM_PORT=4723 ./scripts/e2e-notion.sh
|
||||
#
|
||||
# Run E2E Notion integration flow tests only. See scripts/e2e-run-spec.sh.
|
||||
set -euo pipefail
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
SPEC="test/e2e/specs/notion-flow.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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-notion.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 Notion integration flow tests ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/notion-flow.spec.ts" "notion"
|
||||
|
||||
+2
-88
@@ -1,90 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run E2E card payment flow tests only.
|
||||
#
|
||||
# Starts Appium, cleans app caches, runs the card-payment-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-payment.sh
|
||||
# APPIUM_PORT=4723 ./scripts/e2e-payment.sh
|
||||
#
|
||||
# Run E2E card payment flow tests only. See scripts/e2e-run-spec.sh.
|
||||
set -euo pipefail
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
SPEC="test/e2e/specs/card-payment-flow.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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-payment.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 card payment flow tests ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/card-payment-flow.spec.ts" "card-payment"
|
||||
|
||||
Executable
+38
@@ -0,0 +1,38 @@
|
||||
#!/usr/bin/env bash
|
||||
# Resolve Node 24+ and Appium for E2E scripts (local nvm or CI PATH).
|
||||
# shellcheck disable=SC2034
|
||||
# Outputs: NODE24, APPIUM_BIN (export for callers)
|
||||
|
||||
NODE24="$(command -v node 2>/dev/null || true)"
|
||||
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
|
||||
if [ -s "$NVM_DIR/nvm.sh" ]; then
|
||||
# shellcheck source=/dev/null
|
||||
. "$NVM_DIR/nvm.sh"
|
||||
NVM_NODE="$(nvm which 24 2>/dev/null || true)"
|
||||
if [ -n "${NVM_NODE:-}" ] && [ -x "$NVM_NODE" ]; then
|
||||
NODE24="$NVM_NODE"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "${NODE24:-}" ] || [ ! -x "$NODE24" ]; then
|
||||
echo "ERROR: Node.js is required (Node 24+ for Appium v3)." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NODE_MAJOR="$("$NODE24" --version | sed 's/^v//' | cut -d. -f1)"
|
||||
if [ "${NODE_MAJOR:-0}" -lt 24 ]; then
|
||||
echo "ERROR: Node 24+ is required for Appium v3 (found $($NODE24 --version))." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
APPIUM_BIN="$(command -v appium 2>/dev/null || true)"
|
||||
if [ -z "${APPIUM_BIN:-}" ] || [ ! -x "$APPIUM_BIN" ]; then
|
||||
APPIUM_BIN="$(dirname "$NODE24")/appium"
|
||||
fi
|
||||
if [ ! -x "$APPIUM_BIN" ]; then
|
||||
echo "ERROR: appium not found. Install with: npm install -g appium" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
export NODE24
|
||||
export APPIUM_BIN
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run all E2E WDIO specs sequentially (Appium restarted per spec).
|
||||
# Requires a prior E2E app build: yarn test:e2e:build
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
cd "$ROOT"
|
||||
|
||||
run() {
|
||||
"$ROOT/scripts/e2e-run-spec.sh" "$1" "$2"
|
||||
}
|
||||
|
||||
run "test/e2e/specs/login-flow.spec.ts" "login"
|
||||
run "test/e2e/specs/auth-access-control.spec.ts" "auth"
|
||||
run "test/e2e/specs/telegram-flow.spec.ts" "telegram"
|
||||
run "test/e2e/specs/gmail-flow.spec.ts" "gmail"
|
||||
run "test/e2e/specs/notion-flow.spec.ts" "notion"
|
||||
run "test/e2e/specs/card-payment-flow.spec.ts" "card-payment"
|
||||
run "test/e2e/specs/crypto-payment-flow.spec.ts" "crypto-payment"
|
||||
run "test/e2e/specs/navigation.spec.ts" "navigation"
|
||||
run "test/e2e/specs/smoke.spec.ts" "smoke"
|
||||
run "test/e2e/specs/tauri-commands.spec.ts" "tauri-commands"
|
||||
|
||||
echo "All E2E flows completed."
|
||||
Executable
+68
@@ -0,0 +1,68 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run a single WebDriverIO E2E spec (Appium mac2 + mock server in spec).
|
||||
#
|
||||
# Usage:
|
||||
# ./scripts/e2e-run-spec.sh test/e2e/specs/login-flow.spec.ts [log-suffix]
|
||||
#
|
||||
set -euo pipefail
|
||||
|
||||
SPEC="${1:?spec path required}"
|
||||
LOG_SUFFIX="${2:-$(basename "$SPEC" .spec.ts)}"
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=/dev/null
|
||||
source "$SCRIPT_DIR/e2e-resolve-node-appium.sh"
|
||||
|
||||
export VITE_BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}"
|
||||
export BACKEND_URL="http://127.0.0.1:${E2E_MOCK_PORT}"
|
||||
|
||||
echo "Cleaning cached app data..."
|
||||
rm -rf ~/Library/WebKit/com.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.app"
|
||||
|
||||
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."
|
||||
|
||||
APPIUM_LOG="/tmp/appium-e2e-${LOG_SUFFIX}.log"
|
||||
NODE_VER=$("$NODE24" --version)
|
||||
echo "Starting Appium on port $APPIUM_PORT (Node $NODE_VER)..."
|
||||
echo " Appium logs: $APPIUM_LOG"
|
||||
"$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
|
||||
|
||||
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
|
||||
|
||||
echo "Running E2E spec ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
+2
-88
@@ -1,90 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
#
|
||||
# Run E2E Telegram integration flow tests only.
|
||||
#
|
||||
# Starts Appium, cleans app caches, runs the telegram-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-telegram.sh
|
||||
# APPIUM_PORT=4723 ./scripts/e2e-telegram.sh
|
||||
#
|
||||
# Run E2E Telegram integration flow tests only. See scripts/e2e-run-spec.sh.
|
||||
set -euo pipefail
|
||||
|
||||
APPIUM_PORT="${APPIUM_PORT:-4723}"
|
||||
E2E_MOCK_PORT="${E2E_MOCK_PORT:-18473}"
|
||||
SPEC="test/e2e/specs/telegram-flow.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.openhuman.app
|
||||
rm -rf ~/Library/Caches/com.openhuman.app
|
||||
rm -rf "$HOME/Library/Application Support/com.openhuman.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-telegram.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 Telegram integration flow tests ($SPEC)..."
|
||||
npx wdio run test/wdio.conf.ts --spec "$SPEC"
|
||||
exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/e2e-run-spec.sh" "test/e2e/specs/telegram-flow.spec.ts" "telegram"
|
||||
|
||||
+1
-1
Submodule skills updated: 4ef9e9b368...e856b8e756
@@ -117,16 +117,25 @@ pub fn default_core_bin() -> Option<PathBuf> {
|
||||
let exe_dir = exe.parent()?;
|
||||
|
||||
#[cfg(windows)]
|
||||
let standalone = exe_dir.join("openhuman-core.exe");
|
||||
let standalone = exe_dir.join("openhuman.exe");
|
||||
#[cfg(not(windows))]
|
||||
let standalone = exe_dir.join("openhuman-core");
|
||||
let standalone = exe_dir.join("openhuman");
|
||||
|
||||
if standalone.exists() {
|
||||
return Some(standalone);
|
||||
}
|
||||
|
||||
// Sidecar layout: bundle.externalBin("binaries/openhuman-core") is emitted as
|
||||
// openhuman-core-<target-triple>(.exe) under app resources.
|
||||
#[cfg(windows)]
|
||||
let legacy_standalone = exe_dir.join("openhuman-core.exe");
|
||||
#[cfg(not(windows))]
|
||||
let legacy_standalone = exe_dir.join("openhuman-core");
|
||||
|
||||
if legacy_standalone.exists() {
|
||||
return Some(legacy_standalone);
|
||||
}
|
||||
|
||||
// Sidecar layout: bundle.externalBin("binaries/openhuman") is emitted as
|
||||
// openhuman-<target-triple>(.exe) under app resources.
|
||||
let mut search_dirs = vec![exe_dir.to_path_buf()];
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
@@ -149,9 +158,11 @@ pub fn default_core_bin() -> Option<PathBuf> {
|
||||
};
|
||||
|
||||
#[cfg(windows)]
|
||||
let matches = file_name.starts_with("openhuman-core-") && file_name.ends_with(".exe");
|
||||
let matches = (file_name.starts_with("openhuman-") && file_name.ends_with(".exe"))
|
||||
|| (file_name.starts_with("openhuman-core-") && file_name.ends_with(".exe"));
|
||||
#[cfg(not(windows))]
|
||||
let matches = file_name.starts_with("openhuman-core-");
|
||||
let matches =
|
||||
file_name.starts_with("openhuman-") || file_name.starts_with("openhuman-core-");
|
||||
|
||||
if matches {
|
||||
return Some(path);
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
"../rust-core/ai"
|
||||
],
|
||||
"externalBin": [
|
||||
"binaries/openhuman-core"
|
||||
"binaries/openhuman"
|
||||
],
|
||||
"createUpdaterArtifacts": true,
|
||||
"macOS": {
|
||||
|
||||
@@ -9,6 +9,6 @@
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
<script type="module" src="/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/* eslint-disable @typescript-eslint/no-explicit-any -- intentional global polyfill assignments */
|
||||
// Polyfill Node.js globals for browser (required by telegram package)
|
||||
// Polyfill Node.js globals for browser dependencies
|
||||
// This must be imported FIRST before any other imports that use Node.js APIs
|
||||
import * as util from 'util';
|
||||
import { Buffer } from 'buffer';
|
||||
|
||||
@@ -30,8 +30,6 @@ vi.mock('@tauri-apps/plugin-opener', () => ({ open: vi.fn() }));
|
||||
|
||||
vi.mock('@tauri-apps/plugin-os', () => ({ platform: vi.fn().mockResolvedValue('macos') }));
|
||||
|
||||
vi.mock('@tauri-apps/plugin-shell', () => ({ Command: vi.fn(), open: vi.fn() }));
|
||||
|
||||
// Mock tauriCommands to prevent Tauri API calls in tests
|
||||
vi.mock('../utils/tauriCommands', () => ({
|
||||
isTauri: () => false,
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
module.exports = {
|
||||
darkMode: 'class',
|
||||
content: [
|
||||
"./index.html",
|
||||
"./src/index.html",
|
||||
"./src/**/*.{js,ts,jsx,tsx}",
|
||||
],
|
||||
theme: {
|
||||
|
||||
+7
-1
@@ -7,6 +7,12 @@ const host = process.env.TAURI_DEV_HOST;
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig(async () => ({
|
||||
root: "src",
|
||||
publicDir: "../public",
|
||||
build: {
|
||||
outDir: "../dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [
|
||||
nodePolyfills({
|
||||
include: ["buffer", "process", "util", "os", "crypto", "stream"],
|
||||
@@ -52,6 +58,6 @@ export default defineConfig(async () => ({
|
||||
},
|
||||
},
|
||||
optimizeDeps: {
|
||||
include: ["buffer", "process", "util", "os-browserify", "telegram"],
|
||||
include: ["buffer", "process", "util", "os-browserify"],
|
||||
},
|
||||
}));
|
||||
|
||||
@@ -281,11 +281,6 @@
|
||||
resolved "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz"
|
||||
integrity sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==
|
||||
|
||||
"@cryptography/aes@^0.1.1":
|
||||
version "0.1.1"
|
||||
resolved "https://registry.npmjs.org/@cryptography/aes/-/aes-0.1.1.tgz"
|
||||
integrity sha512-PcYz4FDGblO6tM2kSC+VzhhK62vml6k6/YAkiWtyPvrgJVfnDRoHGDtKn5UiaRRUrvUTTocBpvc2rRgTCqxjsg==
|
||||
|
||||
"@csstools/color-helpers@^6.0.1":
|
||||
version "6.0.1"
|
||||
resolved "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.1.tgz"
|
||||
@@ -1402,13 +1397,6 @@
|
||||
dependencies:
|
||||
"@tauri-apps/api" "^2.8.0"
|
||||
|
||||
"@tauri-apps/plugin-shell@~2":
|
||||
version "2.3.4"
|
||||
resolved "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.4.tgz"
|
||||
integrity sha512-ktsRWf8wHLD17aZEyqE8c5x98eNAuTizR1FSX475zQ4TxaiJnhwksLygQz+AGwckJL5bfEP13nWrlTNQJUpKpA==
|
||||
dependencies:
|
||||
"@tauri-apps/api" "^2.8.0"
|
||||
|
||||
"@testing-library/dom@^10.4.1":
|
||||
version "10.4.1"
|
||||
resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz"
|
||||
@@ -1442,11 +1430,6 @@
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
|
||||
"@testing-library/user-event@^14.6.1":
|
||||
version "14.6.1"
|
||||
resolved "https://registry.npmjs.org/@testing-library/user-event/-/user-event-14.6.1.tgz"
|
||||
integrity sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==
|
||||
|
||||
"@tootallnate/quickjs-emscripten@^0.23.0":
|
||||
version "0.23.0"
|
||||
resolved "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz"
|
||||
@@ -1548,11 +1531,6 @@
|
||||
dependencies:
|
||||
"@types/unist" "*"
|
||||
|
||||
"@types/history@^4.7.11":
|
||||
version "4.7.11"
|
||||
resolved "https://registry.npmjs.org/@types/history/-/history-4.7.11.tgz"
|
||||
integrity sha512-qjDJRrmvBMiTx+jyLxvLfJU7UznFuokDv4f3WRuriHKERccVpFU+8XMQUAbDzoiJCsmexxRExQeMwwCdamSKDA==
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.6":
|
||||
version "2.0.6"
|
||||
resolved "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz"
|
||||
@@ -1630,24 +1608,7 @@
|
||||
resolved "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz"
|
||||
integrity sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==
|
||||
|
||||
"@types/react-router-dom@^5.3.3":
|
||||
version "5.3.3"
|
||||
resolved "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.3.3.tgz"
|
||||
integrity sha512-kpqnYK4wcdm5UaWI3fLcELopqLrHgLqNsdpHauzlQktfkHL3npOSwtj1Uz9oKBAzs7lFtVkV8j83voAz2D8fhw==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
"@types/react-router" "*"
|
||||
|
||||
"@types/react-router@*":
|
||||
version "5.1.20"
|
||||
resolved "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.20.tgz"
|
||||
integrity sha512-jGjmu/ZqS7FjSH6owMcD5qpq19+1RS9DeVRqfl1FeBMxTDQAGwlMWOcs52NDoXaNKyG3d1cYQFMs9rCrb88o9Q==
|
||||
dependencies:
|
||||
"@types/history" "^4.7.11"
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react@*", "@types/react@^19.1.8":
|
||||
"@types/react@^19.1.8":
|
||||
version "19.2.9"
|
||||
resolved "https://registry.npmjs.org/@types/react/-/react-19.2.9.tgz"
|
||||
integrity sha512-Lpo8kgb/igvMIPeNV2rsYKTgaORYdO1XGVZ4Qz3akwOj0ySGYMPlQWa8BaLn0G63D1aSaAQ5ldR06wCpChQCjA==
|
||||
@@ -2424,13 +2385,6 @@ async-function@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz"
|
||||
integrity sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==
|
||||
|
||||
async-mutex@^0.3.0:
|
||||
version "0.3.2"
|
||||
resolved "https://registry.npmjs.org/async-mutex/-/async-mutex-0.3.2.tgz"
|
||||
integrity sha512-HuTK7E7MT7jZEh1P9GtRW9+aTWiDWWi9InbZ5hjxrnRa39KS4BW04+xLBhYNS2aXhHUIKZSw3gj4Pn1pj+qGAA==
|
||||
dependencies:
|
||||
tslib "^2.3.1"
|
||||
|
||||
async@^3.2.4, async@^3.2.6:
|
||||
version "3.2.6"
|
||||
resolved "https://registry.npmjs.org/async/-/async-3.2.6.tgz"
|
||||
@@ -2533,11 +2487,6 @@ bidi-js@^1.0.3:
|
||||
dependencies:
|
||||
require-from-string "^2.0.2"
|
||||
|
||||
big-integer@^1.6.48:
|
||||
version "1.6.52"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz"
|
||||
integrity sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==
|
||||
|
||||
binary-extensions@^2.0.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
|
||||
@@ -2701,13 +2650,6 @@ buffer@^6.0.3:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.2.1"
|
||||
|
||||
bufferutil@^4.0.1, bufferutil@^4.0.3:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.npmjs.org/bufferutil/-/bufferutil-4.1.0.tgz"
|
||||
integrity sha512-ZMANVnAixE6AWWnPzlW2KpUrxhm9woycYvPOo67jWHyFowASTEd9s+QN1EIMsSDtwhIxN4sWE1jotpuDUIgyIw==
|
||||
dependencies:
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
builtin-status-codes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz"
|
||||
@@ -3132,14 +3074,6 @@ csstype@^3.2.2:
|
||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz"
|
||||
integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
|
||||
|
||||
d@1, d@^1.0.1, d@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/d/-/d-1.0.2.tgz"
|
||||
integrity sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==
|
||||
dependencies:
|
||||
es5-ext "^0.10.64"
|
||||
type "^2.7.2"
|
||||
|
||||
data-uri-to-buffer@^6.0.2:
|
||||
version "6.0.2"
|
||||
resolved "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz"
|
||||
@@ -3187,13 +3121,6 @@ debug@4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, d
|
||||
dependencies:
|
||||
ms "^2.1.3"
|
||||
|
||||
debug@^2.2.0:
|
||||
version "2.6.9"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
|
||||
integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
|
||||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@^3.2.7:
|
||||
version "3.2.7"
|
||||
resolved "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz"
|
||||
@@ -3343,15 +3270,6 @@ dom-accessibility-api@^0.6.3:
|
||||
resolved "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz"
|
||||
integrity sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==
|
||||
|
||||
dom-serializer@^1.0.1:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz"
|
||||
integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.2.0"
|
||||
entities "^2.0.0"
|
||||
|
||||
dom-serializer@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz"
|
||||
@@ -3366,18 +3284,11 @@ domain-browser@4.22.0:
|
||||
resolved "https://registry.npmjs.org/domain-browser/-/domain-browser-4.22.0.tgz"
|
||||
integrity sha512-IGBwjF7tNk3cwypFNH/7bfzBcgSCbaMOD3GsaY1AU/JRrnHnYgEM0+9kQt52iZxjNsjBtJYtao146V+f8jFZNw==
|
||||
|
||||
domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0:
|
||||
domelementtype@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz"
|
||||
integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==
|
||||
|
||||
domhandler@^4.0.0, domhandler@^4.2.0:
|
||||
version "4.3.1"
|
||||
resolved "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz"
|
||||
integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==
|
||||
dependencies:
|
||||
domelementtype "^2.2.0"
|
||||
|
||||
domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz"
|
||||
@@ -3385,15 +3296,6 @@ domhandler@^5.0.2, domhandler@^5.0.3:
|
||||
dependencies:
|
||||
domelementtype "^2.3.0"
|
||||
|
||||
domutils@^2.5.2:
|
||||
version "2.8.0"
|
||||
resolved "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz"
|
||||
integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==
|
||||
dependencies:
|
||||
dom-serializer "^1.0.1"
|
||||
domelementtype "^2.2.0"
|
||||
domhandler "^4.2.0"
|
||||
|
||||
domutils@^3.0.1, domutils@^3.2.2:
|
||||
version "3.2.2"
|
||||
resolved "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz"
|
||||
@@ -3519,11 +3421,6 @@ engine.io-parser@~5.2.1:
|
||||
resolved "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz"
|
||||
integrity sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==
|
||||
|
||||
entities@^2.0.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz"
|
||||
integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==
|
||||
|
||||
entities@^4.2.0:
|
||||
version "4.5.0"
|
||||
resolved "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz"
|
||||
@@ -3676,33 +3573,6 @@ es-to-primitive@^1.3.0:
|
||||
is-date-object "^1.0.5"
|
||||
is-symbol "^1.0.4"
|
||||
|
||||
es5-ext@^0.10.35, es5-ext@^0.10.62, es5-ext@^0.10.63, es5-ext@^0.10.64, es5-ext@~0.10.14:
|
||||
version "0.10.64"
|
||||
resolved "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.64.tgz"
|
||||
integrity sha512-p2snDhiLaXe6dahss1LddxqEm+SkuDvV8dnIQG0MWjyHpcMNfXKPE+/Cc0y+PhxJX3A4xGNeFCj5oc0BUh6deg==
|
||||
dependencies:
|
||||
es6-iterator "^2.0.3"
|
||||
es6-symbol "^3.1.3"
|
||||
esniff "^2.0.1"
|
||||
next-tick "^1.1.0"
|
||||
|
||||
es6-iterator@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz"
|
||||
integrity sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "^0.10.35"
|
||||
es6-symbol "^3.1.1"
|
||||
|
||||
es6-symbol@^3.1.1, es6-symbol@^3.1.3:
|
||||
version "3.1.4"
|
||||
resolved "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.4.tgz"
|
||||
integrity sha512-U9bFFjX8tFiATgtkJ1zg25+KviIXpgRvRHS8sau3GfhVzThRQrOeksPeT0BWW2MNZs1OEWJ1DPXOQMn0KKRkvg==
|
||||
dependencies:
|
||||
d "^1.0.2"
|
||||
ext "^1.7.0"
|
||||
|
||||
esbuild@^0.27.0:
|
||||
version "0.27.2"
|
||||
resolved "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz"
|
||||
@@ -3932,16 +3802,6 @@ eslint@^9.39.2:
|
||||
natural-compare "^1.4.0"
|
||||
optionator "^0.9.3"
|
||||
|
||||
esniff@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz"
|
||||
integrity sha512-kTUIGKQ/mDPFoJ0oVfcmyJn4iBDRptjNVIzwIFR7tqWXdVI9xfA2RMwY/gbSpJG3lkdWNEjLap/NqVHZiJsdfg==
|
||||
dependencies:
|
||||
d "^1.0.1"
|
||||
es5-ext "^0.10.62"
|
||||
event-emitter "^0.3.5"
|
||||
type "^2.7.2"
|
||||
|
||||
espree@^10.0.1, espree@^10.4.0:
|
||||
version "10.4.0"
|
||||
resolved "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz"
|
||||
@@ -3997,14 +3857,6 @@ esutils@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz"
|
||||
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
|
||||
|
||||
event-emitter@^0.3.5:
|
||||
version "0.3.5"
|
||||
resolved "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz"
|
||||
integrity sha512-D9rRn9y7kLPnJ+hMq7S/nhvoKwwvVJahBi2BPmx3bvbsEdK3W9ii8cBSGjP+72/LnM4n6fo3+dkCX5FeTQruXA==
|
||||
dependencies:
|
||||
d "1"
|
||||
es5-ext "~0.10.14"
|
||||
|
||||
event-target-shim@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz"
|
||||
@@ -4080,13 +3932,6 @@ expect@^30.2.0:
|
||||
jest-mock "30.2.0"
|
||||
jest-util "30.2.0"
|
||||
|
||||
ext@^1.7.0:
|
||||
version "1.7.0"
|
||||
resolved "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz"
|
||||
integrity sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==
|
||||
dependencies:
|
||||
type "^2.7.2"
|
||||
|
||||
extend@^3.0.0:
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz"
|
||||
@@ -4435,7 +4280,7 @@ gopd@^1.0.1, gopd@^1.2.0:
|
||||
resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
|
||||
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.2.0, graceful-fs@^4.2.11:
|
||||
graceful-fs@^4.2.0, graceful-fs@^4.2.11:
|
||||
version "4.2.11"
|
||||
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
|
||||
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
|
||||
@@ -4624,16 +4469,6 @@ htmlparser2@^10.1.0:
|
||||
domutils "^3.2.2"
|
||||
entities "^7.0.1"
|
||||
|
||||
htmlparser2@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz"
|
||||
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
dependencies:
|
||||
domelementtype "^2.0.1"
|
||||
domhandler "^4.0.0"
|
||||
domutils "^2.5.2"
|
||||
entities "^2.0.0"
|
||||
|
||||
http-proxy-agent@^7.0.0, http-proxy-agent@^7.0.1, http-proxy-agent@^7.0.2:
|
||||
version "7.0.2"
|
||||
resolved "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz"
|
||||
@@ -4699,7 +4534,7 @@ immediate@~3.0.5:
|
||||
resolved "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz"
|
||||
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||
|
||||
immer@^11.0.0, immer@^11.1.3:
|
||||
immer@^11.0.0:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.npmjs.org/immer/-/immer-11.1.3.tgz"
|
||||
integrity sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==
|
||||
@@ -5021,11 +4856,6 @@ is-typed-array@^1.1.13, is-typed-array@^1.1.14, is-typed-array@^1.1.15, is-typed
|
||||
dependencies:
|
||||
which-typed-array "^1.1.16"
|
||||
|
||||
is-typedarray@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz"
|
||||
integrity sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==
|
||||
|
||||
is-unicode-supported@^0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz"
|
||||
@@ -5852,11 +5682,6 @@ miller-rabin@^4.0.0:
|
||||
bn.js "^4.0.0"
|
||||
brorand "^1.0.1"
|
||||
|
||||
mime@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz"
|
||||
integrity sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==
|
||||
|
||||
min-indent@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz"
|
||||
@@ -5944,11 +5769,6 @@ modern-tar@^0.7.2:
|
||||
resolved "https://registry.npmjs.org/modern-tar/-/modern-tar-0.7.3.tgz"
|
||||
integrity sha512-4W79zekKGyYU4JXVmB78DOscMFaJth2gGhgfTl2alWE4rNe3nf4N2pqenQ0rEtIewrnD79M687Ouba3YGTLOvg==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
|
||||
|
||||
ms@^2.1.1, ms@^2.1.3:
|
||||
version "2.1.3"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
|
||||
@@ -6007,23 +5827,6 @@ netmask@^2.0.2:
|
||||
resolved "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz"
|
||||
integrity sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==
|
||||
|
||||
next-tick@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz"
|
||||
integrity sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==
|
||||
|
||||
node-gyp-build@^4.3.0:
|
||||
version "4.8.4"
|
||||
resolved "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz"
|
||||
integrity sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==
|
||||
|
||||
node-localstorage@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.npmjs.org/node-localstorage/-/node-localstorage-2.2.1.tgz"
|
||||
integrity sha512-vv8fJuOUCCvSPjDjBLlMqYMHob4aGjkmrkaE42/mZr0VT+ZAU10jRF8oTnX9+pgU9/vYJ8P7YT3Vd6ajkmzSCw==
|
||||
dependencies:
|
||||
write-file-atomic "^1.1.4"
|
||||
|
||||
node-releases@^2.0.27:
|
||||
version "2.0.27"
|
||||
resolved "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz"
|
||||
@@ -6277,11 +6080,6 @@ package-json-from-dist@^1.0.0:
|
||||
resolved "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz"
|
||||
integrity sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==
|
||||
|
||||
pako@^2.0.3:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz"
|
||||
integrity sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==
|
||||
|
||||
pako@~1.0.2, pako@~1.0.5:
|
||||
version "1.0.11"
|
||||
resolved "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz"
|
||||
@@ -6655,11 +6453,6 @@ punycode@^2.1.0, punycode@^2.3.1:
|
||||
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz"
|
||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||
|
||||
qrcode.react@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/qrcode.react/-/qrcode.react-4.2.0.tgz"
|
||||
integrity sha512-QpgqWi8rD9DsS9EP3z7BT+5lY5SFhsqGjpgW5DY/i3mK4M9DTBNz3ErMi8BWYEfI3L0d8GIbGmcdFAS1uIRGjA==
|
||||
|
||||
qs@^6.12.3:
|
||||
version "6.14.1"
|
||||
resolved "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz"
|
||||
@@ -6847,11 +6640,6 @@ readdirp@~3.6.0:
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
real-cancellable-promise@^1.1.1:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/real-cancellable-promise/-/real-cancellable-promise-1.2.3.tgz"
|
||||
integrity sha512-hBI5Gy/55VEeeMtImMgEirD7eq5UmqJf1J8dFZtbJZA/3rB0pYFZ7PayMGueb6v4UtUtpKpP+05L0VwyE1hI9Q==
|
||||
|
||||
recursive-readdir@^2.2.3:
|
||||
version "2.2.3"
|
||||
resolved "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz"
|
||||
@@ -7280,11 +7068,6 @@ slash@^3.0.0:
|
||||
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
|
||||
integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
|
||||
|
||||
slide@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmjs.org/slide/-/slide-1.1.6.tgz"
|
||||
integrity sha512-NwrtjCg+lZoqhFU8fOwl4ay2ei8PaqCBOUV3/ektPY9trO1yQ1oXEfmHAhKArUVUr/hOHvy5f6AdP17dCM0zMw==
|
||||
|
||||
smart-buffer@^4.2.0:
|
||||
version "4.2.0"
|
||||
resolved "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz"
|
||||
@@ -7317,7 +7100,7 @@ socks-proxy-agent@^8.0.5:
|
||||
debug "^4.3.4"
|
||||
socks "^2.8.3"
|
||||
|
||||
socks@^2.6.2, socks@^2.8.3:
|
||||
socks@^2.8.3:
|
||||
version "2.8.7"
|
||||
resolved "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz"
|
||||
integrity sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==
|
||||
@@ -7406,11 +7189,6 @@ stop-iteration-iterator@^1.1.0:
|
||||
es-errors "^1.3.0"
|
||||
internal-slot "^1.1.0"
|
||||
|
||||
store2@^2.13.0:
|
||||
version "2.14.4"
|
||||
resolved "https://registry.npmjs.org/store2/-/store2-2.14.4.tgz"
|
||||
integrity sha512-srTItn1GOvyvOycgxjAnPA63FZNwy0PTyUBFMHRM+hVFltAeoh0LmNBz9SZqUS9mMqGk8rfyWyXn3GH5ReJ8Zw==
|
||||
|
||||
stream-browserify@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.npmjs.org/stream-browserify/-/stream-browserify-3.0.0.tgz"
|
||||
@@ -7708,29 +7486,6 @@ tar-stream@^3.0.0, tar-stream@^3.1.5:
|
||||
fast-fifo "^1.2.0"
|
||||
streamx "^2.15.0"
|
||||
|
||||
telegram@^2.26.22:
|
||||
version "2.26.22"
|
||||
resolved "https://registry.npmjs.org/telegram/-/telegram-2.26.22.tgz"
|
||||
integrity sha512-EIj7Yrjiu0Yosa3FZ/7EyPg9s6UiTi/zDQrFmR/2Mg7pIUU+XjAit1n1u9OU9h2oRnRM5M+67/fxzQluZpaJJg==
|
||||
dependencies:
|
||||
"@cryptography/aes" "^0.1.1"
|
||||
async-mutex "^0.3.0"
|
||||
big-integer "^1.6.48"
|
||||
buffer "^6.0.3"
|
||||
htmlparser2 "^6.1.0"
|
||||
mime "^3.0.0"
|
||||
node-localstorage "^2.2.1"
|
||||
pako "^2.0.3"
|
||||
path-browserify "^1.0.1"
|
||||
real-cancellable-promise "^1.1.1"
|
||||
socks "^2.6.2"
|
||||
store2 "^2.13.0"
|
||||
ts-custom-error "^3.2.0"
|
||||
websocket "^1.0.34"
|
||||
optionalDependencies:
|
||||
bufferutil "^4.0.3"
|
||||
utf-8-validate "^5.0.5"
|
||||
|
||||
text-decoder@^1.1.0:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz"
|
||||
@@ -7854,11 +7609,6 @@ ts-api-utils@^2.4.0:
|
||||
resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz"
|
||||
integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==
|
||||
|
||||
ts-custom-error@^3.2.0:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.npmjs.org/ts-custom-error/-/ts-custom-error-3.3.1.tgz"
|
||||
integrity sha512-5OX1tzOjxWEgsr/YEUWSuPrQ00deKLh6D7OTWcvNHm12/7QPyRh8SYpyWvA4IZv8H/+GQWQEh/kwo95Q9OVW1A==
|
||||
|
||||
ts-interface-checker@^0.1.9:
|
||||
version "0.1.13"
|
||||
resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
|
||||
@@ -7874,7 +7624,7 @@ tsconfig-paths@^3.15.0:
|
||||
minimist "^1.2.6"
|
||||
strip-bom "^3.0.0"
|
||||
|
||||
tslib@^2.0.1, tslib@^2.1.0, tslib@^2.3.1:
|
||||
tslib@^2.0.1, tslib@^2.1.0:
|
||||
version "2.8.1"
|
||||
resolved "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz"
|
||||
integrity sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==
|
||||
@@ -7923,11 +7673,6 @@ type-fest@^5.2.0:
|
||||
dependencies:
|
||||
tagged-tag "^1.0.0"
|
||||
|
||||
type@^2.7.2:
|
||||
version "2.7.3"
|
||||
resolved "https://registry.npmjs.org/type/-/type-2.7.3.tgz"
|
||||
integrity sha512-8j+1QmAbPvLZow5Qpi6NCaN8FB60p/6x8/vfNqOk/hC+HuvFZhL4+WfekuhQLiqFZXOgQdrs3B+XxEmCc6b3FQ==
|
||||
|
||||
typed-array-buffer@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz"
|
||||
@@ -7973,13 +7718,6 @@ typed-array-length@^1.0.7:
|
||||
possible-typed-array-names "^1.0.0"
|
||||
reflect.getprototypeof "^1.0.6"
|
||||
|
||||
typedarray-to-buffer@^3.1.5:
|
||||
version "3.1.5"
|
||||
resolved "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz"
|
||||
integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==
|
||||
dependencies:
|
||||
is-typedarray "^1.0.0"
|
||||
|
||||
typescript@~5.8.3:
|
||||
version "5.8.3"
|
||||
resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz"
|
||||
@@ -8114,13 +7852,6 @@ userhome@1.0.1:
|
||||
resolved "https://registry.npmjs.org/userhome/-/userhome-1.0.1.tgz"
|
||||
integrity sha512-5cnLm4gseXjAclKowC4IjByaGsjtAoV6PrOQOljplNB54ReUYJP8HdAFq2muHinSDAh09PPX/uXDPfdxRHvuSA==
|
||||
|
||||
utf-8-validate@^5.0.2, utf-8-validate@^5.0.5:
|
||||
version "5.0.10"
|
||||
resolved "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-5.0.10.tgz"
|
||||
integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==
|
||||
dependencies:
|
||||
node-gyp-build "^4.3.0"
|
||||
|
||||
util-deprecate@^1.0.1, util-deprecate@^1.0.2, util-deprecate@~1.0.1:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
|
||||
@@ -8290,18 +8021,6 @@ webidl-conversions@^8.0.1:
|
||||
resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz"
|
||||
integrity sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==
|
||||
|
||||
websocket@^1.0.34:
|
||||
version "1.0.35"
|
||||
resolved "https://registry.npmjs.org/websocket/-/websocket-1.0.35.tgz"
|
||||
integrity sha512-/REy6amwPZl44DDzvRCkaI1q1bIiQB0mEFQLUrhz3z2EK91cp3n72rAjUlrTP0zV22HJIUOVHQGPxhFRjxjt+Q==
|
||||
dependencies:
|
||||
bufferutil "^4.0.1"
|
||||
debug "^2.2.0"
|
||||
es5-ext "^0.10.63"
|
||||
typedarray-to-buffer "^3.1.5"
|
||||
utf-8-validate "^5.0.2"
|
||||
yaeti "^0.0.6"
|
||||
|
||||
whatwg-encoding@^3.1.1:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz"
|
||||
@@ -8454,15 +8173,6 @@ wrappy@1:
|
||||
resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
|
||||
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
|
||||
|
||||
write-file-atomic@^1.1.4:
|
||||
version "1.3.4"
|
||||
resolved "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-1.3.4.tgz"
|
||||
integrity sha512-SdrHoC/yVBPpV0Xq/mUZQIpW2sWXAShb/V4pomcJXh92RuaO+f3UTWItiR3Px+pLnV2PvC2/bfn5cwr5X6Vfxw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.11"
|
||||
imurmurhash "^0.1.4"
|
||||
slide "^1.1.5"
|
||||
|
||||
ws@^8.8.0:
|
||||
version "8.19.0"
|
||||
resolved "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz"
|
||||
@@ -8503,11 +8213,6 @@ y18n@^5.0.5:
|
||||
resolved "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz"
|
||||
integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
|
||||
|
||||
yaeti@^0.0.6:
|
||||
version "0.0.6"
|
||||
resolved "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz"
|
||||
integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==
|
||||
|
||||
yallist@^3.0.2:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz"
|
||||
@@ -8606,11 +8311,6 @@ zip-stream@^6.0.1:
|
||||
resolved "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz"
|
||||
integrity sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==
|
||||
|
||||
zustand@^5.0.10:
|
||||
version "5.0.10"
|
||||
resolved "https://registry.npmjs.org/zustand/-/zustand-5.0.10.tgz"
|
||||
integrity sha512-U1AiltS1O9hSy3rul+Ub82ut2fqIAefiSuwECWt6jlMVUGejvf+5omLcRBSzqbRagSM3hQZbtzdeRc6QVScXTg==
|
||||
|
||||
zwitch@^2.0.0:
|
||||
version "2.0.4"
|
||||
resolved "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz"
|
||||
|
||||
Reference in New Issue
Block a user