diff --git a/.githooks/pre-push b/.githooks/pre-push new file mode 100755 index 0000000..67148e2 --- /dev/null +++ b/.githooks/pre-push @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +# Pre-push hook: prevent commercial code from leaking to the community remote. +# +# Git calls pre-push with the remote name and URL as arguments, +# and feeds (local_ref local_sha remote_ref remote_sha) lines on stdin. + +remote="$1" +url="$2" + +# Only guard pushes to the community origin (DexterFromLab) +if ! echo "$url" | grep -qi "DexterFromLab"; then + exit 0 +fi + +echo "[pre-push] Scanning commits for commercial code before push to community remote..." + +COMMERCIAL_PATTERNS="agor-pro/|src/lib/commercial/" + +while read -r local_ref local_sha remote_ref remote_sha; do + # Skip delete pushes + if [ "$local_sha" = "0000000000000000000000000000000000000000" ]; then + continue + fi + + # For new branches, diff against remote HEAD; for updates, diff against remote_sha + if [ "$remote_sha" = "0000000000000000000000000000000000000000" ]; then + range="$local_sha" + else + range="$remote_sha..$local_sha" + fi + + # Check file paths in the commits being pushed + leaked_files=$(git diff --name-only "$range" 2>/dev/null | grep -E "$COMMERCIAL_PATTERNS" || true) + + if [ -n "$leaked_files" ]; then + echo "" + echo "==========================================" + echo " PUSH BLOCKED: Commercial code detected!" + echo "==========================================" + echo "" + echo "The following commercial files were found in commits being pushed:" + echo "$leaked_files" | sed 's/^/ - /' + echo "" + echo "You are pushing to the community remote ($url)." + echo "Commercial code must NOT be pushed to this remote." + echo "" + echo "To fix: remove commercial files from these commits or push to the commercial remote instead." + echo "==========================================" + exit 1 + fi +done + +exit 0 diff --git a/.github/workflows/commercial-build.yml b/.github/workflows/commercial-build.yml new file mode 100644 index 0000000..3f1c43f --- /dev/null +++ b/.github/workflows/commercial-build.yml @@ -0,0 +1,71 @@ +name: Commercial Build + +on: + push: + branches: + - 'commercial/**' + workflow_dispatch: + +permissions: + contents: read + +env: + CARGO_TERM_COLOR: always + +jobs: + commercial-build: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v4 + + - name: Install system dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + libwebkit2gtk-4.1-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + libssl-dev \ + build-essential \ + pkg-config + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Cache Cargo + uses: actions/cache@v4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + target + key: ${{ runner.os }}-cargo-pro-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo-pro- + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install npm dependencies + run: npm ci + + - name: Cargo check (pro features) + run: cargo check --features pro + + - name: Cargo test (pro features) + run: cargo test --features pro + + - name: Vitest (frontend) + run: npm run test + + - name: Commercial tests + run: | + if [ -d "tests/commercial/" ] && ls tests/commercial/*.test.* 2>/dev/null; then + npx vitest run tests/commercial/ + else + echo "No commercial tests found, skipping." + fi diff --git a/.github/workflows/leak-check.yml b/.github/workflows/leak-check.yml new file mode 100644 index 0000000..f8f13ff --- /dev/null +++ b/.github/workflows/leak-check.yml @@ -0,0 +1,69 @@ +name: Leak Check + +on: + push: + branches: [main] + pull_request: + branches: [main] + +permissions: + contents: read + +jobs: + leak-check: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Check for commercial directories + run: | + failed=0 + + # Check agor-pro/ exists + if [ -d "agor-pro/" ]; then + echo "::error::Commercial directory 'agor-pro/' found in community repo" + failed=1 + fi + + # Check src/lib/commercial/ has actual content (beyond .gitkeep) + if [ -d "src/lib/commercial/" ]; then + content_count=$(find src/lib/commercial/ -type f ! -name '.gitkeep' | wc -l) + if [ "$content_count" -gt 0 ]; then + echo "::error::Commercial code found in 'src/lib/commercial/' ($content_count files beyond .gitkeep)" + find src/lib/commercial/ -type f ! -name '.gitkeep' + failed=1 + fi + fi + + # Check tests/commercial/ has actual content (beyond .gitkeep) + if [ -d "tests/commercial/" ]; then + content_count=$(find tests/commercial/ -type f ! -name '.gitkeep' | wc -l) + if [ "$content_count" -gt 0 ]; then + echo "::error::Commercial test code found in 'tests/commercial/' ($content_count files beyond .gitkeep)" + find tests/commercial/ -type f ! -name '.gitkeep' + failed=1 + fi + fi + + if [ "$failed" -eq 1 ]; then + exit 1 + fi + echo "No commercial directories with content found." + + - name: Grep for commercial references in source + run: | + failed=0 + + for pattern in "LicenseRef-Commercial" "agor-pro" "agor_pro"; do + if grep -r --include="*.ts" --include="*.svelte" --include="*.rs" --include="*.toml" \ + "$pattern" src/ src-tauri/src/ 2>/dev/null; then + echo "::error::Found '$pattern' reference in source code" + failed=1 + fi + done + + if [ "$failed" -eq 1 ]; then + echo "::error::Commercial references detected in community source. See above for details." + exit 1 + fi + echo "No commercial references found in source." diff --git a/.github/workflows/pat-health.yml b/.github/workflows/pat-health.yml new file mode 100644 index 0000000..a9abb0b --- /dev/null +++ b/.github/workflows/pat-health.yml @@ -0,0 +1,75 @@ +name: PAT Health Check + +on: + schedule: + - cron: '0 9 * * 1' # Every Monday at 9am UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + +jobs: + check-pat: + runs-on: ubuntu-latest + steps: + - name: Check COMMUNITY_PAT validity + env: + COMMUNITY_PAT: ${{ secrets.COMMUNITY_PAT }} + run: | + if [ -z "$COMMUNITY_PAT" ]; then + echo "::error::COMMUNITY_PAT secret is not set" + echo "pat_valid=false" >> "$GITHUB_ENV" + exit 0 + fi + + status=$(curl -s -o /dev/null -w "%{http_code}" \ + -H "Authorization: token $COMMUNITY_PAT" \ + -H "Accept: application/vnd.github+json" \ + https://api.github.com/user) + + if [ "$status" -eq 200 ]; then + echo "COMMUNITY_PAT is valid (HTTP $status)" + echo "pat_valid=true" >> "$GITHUB_ENV" + else + echo "::error::COMMUNITY_PAT returned HTTP $status" + echo "pat_valid=false" >> "$GITHUB_ENV" + fi + + - name: Create issue if PAT is invalid + if: env.pat_valid == 'false' + uses: actions/github-script@v7 + with: + script: | + const title = 'COMMUNITY_PAT is invalid or missing'; + const body = [ + '## PAT Health Check Failed', + '', + 'The weekly PAT health check detected that `COMMUNITY_PAT` is either missing or returning an error from the GitHub API.', + '', + '**Action required:** Rotate or re-create the PAT and update the repository secret.', + '', + `Run: ${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`, + ].join('\n'); + + // Avoid duplicate issues + const existing = await github.rest.issues.listForRepo({ + owner: context.repo.owner, + repo: context.repo.repo, + state: 'open', + labels: 'pat-health', + per_page: 1, + }); + + if (existing.data.length > 0) { + console.log('Open PAT health issue already exists, skipping creation.'); + return; + } + + await github.rest.issues.create({ + owner: context.repo.owner, + repo: context.repo.repo, + title, + body, + labels: ['pat-health'], + }); diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..9e0725a --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,122 @@ +# Contributing to Agent Orchestrator + +## Dual-Repository Model + +This project uses a dual-repo structure: + +| Repository | License | Purpose | +|------------|---------|---------| +| `DexterFromLab/agent-orchestrator` | MIT | Community edition (open source) | +| `agents-orchestrator/agents-orchestrator` | MIT + Commercial | Commercial edition (this repo) | + +Community contributions target the community repo. Commercial development happens +exclusively in this repo. The two share a common `main` branch that is synced +periodically. + +## Community Contributions + +All community contributions go to **DexterFromLab/agent-orchestrator**. Do not open +PRs against this repo for community features. + +### Contributor License Agreement (CLA) + +Every community contributor must sign the CLA before their first PR is merged. +CLA signing is automated via [CLA-assistant.io](https://cla-assistant.io/) on +the community repository. The bot will prompt you on your first PR. + +The CLA grants the project maintainers a perpetual, irrevocable license to use +your contribution in both the community and commercial editions. + +## Commercial Development + +Commercial features are developed only in this repository. Access is restricted +to authorized team members. + +### SPDX License Headers + +All commercial source files must include the following header as the first line: + +``` +// SPDX-License-Identifier: LicenseRef-Commercial +``` + +For CSS/HTML files: + +``` +/* SPDX-License-Identifier: LicenseRef-Commercial */ +``` + +For Rust files: + +```rust +// SPDX-License-Identifier: LicenseRef-Commercial +``` + +Community-shared code uses the MIT identifier: + +``` +// SPDX-License-Identifier: MIT +``` + +### Commercial Directories + +Files under these paths are always commercial-only: + +- `agor-pro/` +- `src/lib/commercial/` + +## Branch Model + +| Branch | Purpose | +|--------|---------| +| `main` | Shared with community edition. Never commit commercial code here. | +| `commercial/*` | Commercial-only features. Merged into the commercial release branch. | +| `feature/*`, `fix/*` | Standard development branches. | + +### Sync Flow + +The community repo's `main` is merged into this repo's `main` periodically: + +``` +community/main --> origin/main --> commercial branches +``` + +Use `make sync` to pull community changes. Never force-push `main`. + +## Commit Conventions + +Use [Conventional Commits](https://www.conventionalcommits.org/): + +``` +type(scope): description + +feat(dashboard): add team analytics panel +fix(sidecar): handle timeout on agent restart +docs(api): update webhook payload reference +chore(deps): bump tauri to 2.3.1 +``` + +Types: `feat`, `fix`, `docs`, `style`, `refactor`, `perf`, `test`, `chore`, `ci`, `build`. + +Breaking changes use `type!:` prefix or include `BREAKING CHANGE:` in the footer. + +## Testing + +Both editions must pass their respective test suites before merge: + +```bash +# Community tests (must always pass on main) +npm run test:all + +# Commercial tests (includes commercial-specific tests) +npm run test:all:commercial +``` + +Do not merge a PR if either suite is red. If a community sync introduces +failures in commercial tests, fix them before merging. + +## Code Review + +- All PRs require at least one approval. +- Commercial PRs must be reviewed by a team member with commercial repo access. +- Verify no commercial code leaks into community-bound branches before approving. diff --git a/LICENSE-COMMERCIAL b/LICENSE-COMMERCIAL new file mode 100644 index 0000000..7a36be1 --- /dev/null +++ b/LICENSE-COMMERCIAL @@ -0,0 +1,50 @@ +Commercial License + +Copyright (c) 2025-2026 Agents Orchestrator. All rights reserved. + +TERMS AND CONDITIONS + +1. GRANT OF LICENSE + + This software and associated documentation files (the "Software") located + under the directories `agor-pro/` and `src/lib/commercial/`, and any other + files bearing the SPDX header `LicenseRef-Commercial`, are licensed to + authorized users under the terms of a separate commercial agreement. + +2. RESTRICTIONS + + Unless you have a valid commercial license agreement, you may not: + + a. Copy, modify, merge, publish, distribute, sublicense, or sell copies + of the Software. + b. Reverse engineer, decompile, or disassemble the Software. + c. Remove or alter any proprietary notices, labels, or marks on the + Software. + d. Use the Software to provide a competing product or service. + +3. OWNERSHIP + + The Software is the intellectual property of Agents Orchestrator and is + protected by copyright law. This license does not transfer ownership. + +4. NO WARRANTY + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING + FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + +5. TERMINATION + + This license terminates automatically if you breach any of its terms. Upon + termination, you must destroy all copies of the Software in your possession. + +6. GOVERNING LAW + + This license shall be governed by and construed in accordance with the laws + of the jurisdiction in which Agents Orchestrator is incorporated. + +For licensing inquiries, contact the Agents Orchestrator organization. diff --git a/MAINTENANCE.md b/MAINTENANCE.md new file mode 100644 index 0000000..6a7a2de --- /dev/null +++ b/MAINTENANCE.md @@ -0,0 +1,128 @@ +# Maintenance Guide + +Operational procedures for the commercial edition of Agent Orchestrator. + +## PAT Rotation + +The `COMMUNITY_PAT` personal access token is used by CI to sync with the +community repository. Rotate it every 90 days. + +### Rotation Procedure + +1. Generate a new fine-grained PAT on GitHub with scope: + - Repository: `DexterFromLab/agent-orchestrator` + - Permissions: `Contents: Read-only` +2. Update the secret in this repo's GitHub Settings > Secrets > Actions: + - Name: `COMMUNITY_PAT` + - Value: the new token +3. Run the sync workflow manually to verify: Actions > Community Sync > Run workflow. +4. Record the rotation date. Next rotation due in 90 days. + +### Token Audit + +Check token expiry dates monthly. Set a calendar reminder. + +## Community Sync + +### Automated + +```bash +make sync +``` + +This fetches `community/main`, merges it into `origin/main`, and runs the test +suite. Conflicts must be resolved manually. + +### Manual + +```bash +git remote add community https://github.com/DexterFromLab/agent-orchestrator.git 2>/dev/null +git fetch community main +git checkout main +git merge community/main --no-edit +npm run test:all +``` + +If tests fail after sync, fix before pushing. + +## Pre-Release Checklist: Community Edition + +Before publishing a community release from `main`: + +- [ ] `git diff main..commercial/main -- agor-pro/ src/lib/commercial/` shows no commercial code on `main` +- [ ] Run `grep -r "LicenseRef-Commercial" --include="*.ts" --include="*.rs" --include="*.svelte" src/ src-tauri/` on `main` returns nothing +- [ ] Run `npm run test:all` passes +- [ ] Run `cargo test --workspace` passes +- [ ] CHANGELOG.md updated with release notes +- [ ] Tag follows semver: `v{major}.{minor}.{patch}` +- [ ] No secrets, API keys, or internal URLs in the diff since last release + +## Pre-Release Checklist: Commercial Edition + +Before publishing a commercial release: + +- [ ] All commercial branches merged into the release branch +- [ ] `npm run test:all:commercial` passes +- [ ] `cargo test --workspace` passes +- [ ] License headers present on all commercial files (`grep -rL "SPDX-License-Identifier" agor-pro/ src/lib/commercial/` returns nothing) +- [ ] No hardcoded credentials or internal endpoints +- [ ] Database migrations tested against fresh install and upgrade from previous version +- [ ] Release notes written for commercial changelog + +## Database Migration Notes + +The commercial edition uses a separate data directory to avoid conflicts: + +| Edition | Data Directory | +|---------|---------------| +| Community | `~/.local/share/bterminal/` | +| Commercial | `~/.local/share/agor/` | + +### Migration Rules + +- Schema migrations run automatically on startup (WAL mode SQLite). +- Never modify existing migration SQL. Add new migrations with incrementing version numbers. +- Test migrations against: (a) fresh install, (b) upgrade from N-1, (c) upgrade from N-2. +- Back up `~/.local/share/agor/` before testing destructive migrations locally. + +### Edition Switching in Development + +When switching between community and commercial editions locally: + +```bash +make clean +``` + +This clears build artifacts and resets configuration to avoid cross-contamination. +The two editions use separate data directories, so user data is not affected. +Rebuild after switching: + +```bash +npm install && npm run tauri dev +``` + +## Quarterly Maintenance + +Perform these tasks every quarter: + +### Security + +- [ ] Rotate `COMMUNITY_PAT` (if due within the quarter) +- [ ] Run `npm audit` and `cargo audit` on both editions +- [ ] Review GitHub Dependabot alerts +- [ ] Verify no secrets in git history: `git log --all --diff-filter=A -- '*.env' '*.pem' '*.key'` + +### Dependencies + +- [ ] Update Rust toolchain (`rustup update`) +- [ ] Update Node.js to latest LTS if applicable +- [ ] Review and update pinned dependency versions +- [ ] Run full test suite after updates + +### Repository Health + +- [ ] Prune stale branches (`git branch --merged main | grep -v main`) +- [ ] Verify CI workflows are green on main +- [ ] Review and close stale issues/PRs +- [ ] Sync community changes if not done recently +- [ ] Verify backup procedures for commercial data diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..1460e59 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +.PHONY: setup build build-pro test test-pro sync clean + +# --- Community --- + +setup: + git config core.hooksPath .githooks + npm install + @echo "Git hooks configured and dependencies installed." + +build: + cargo build + npm run build + +test: + npm run test:all + +# --- Commercial --- + +build-pro: + cargo build --features pro + npm run build + @echo "For Tauri release: cargo tauri build -- --features pro --config src-tauri/tauri.conf.commercial.json" + +test-pro: + cargo test --features pro + AGOR_EDITION=pro npx vitest run + +# --- Maintenance --- + +sync: + git fetch origin + git merge origin/main + +clean: + cargo clean + rm -rf node_modules/.vite diff --git a/agor-pro/Cargo.toml b/agor-pro/Cargo.toml new file mode 100644 index 0000000..5ec3b27 --- /dev/null +++ b/agor-pro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "agor-pro" +version = "0.1.0" +edition = "2021" +description = "Commercial plugin for Agents Orchestrator" +license = "LicenseRef-Commercial" + +[dependencies] +agor-core = { path = "../agor-core" } +tauri = { version = "2.10.3", features = [] } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" +log = "0.4" diff --git a/agor-pro/src/lib.rs b/agor-pro/src/lib.rs new file mode 100644 index 0000000..c74a7f4 --- /dev/null +++ b/agor-pro/src/lib.rs @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: LicenseRef-Commercial +// +// agor-pro — Commercial plugin for Agents Orchestrator. +// This crate is NOT open-source. It is distributed only via the +// agents-orchestrator/agents-orchestrator private repository. + +use tauri::{ + plugin::{Builder, TauriPlugin}, + Runtime, +}; + +/// Initialize the agor-pro Tauri plugin. +/// Registers all commercial commands and managed state. +pub fn init() -> TauriPlugin { + Builder::new("agor-pro") + .invoke_handler(tauri::generate_handler![ + pro_status, + ]) + .build() +} + +#[tauri::command] +fn pro_status() -> String { + "active".to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_pro_status() { + assert_eq!(pro_status(), "active"); + } +} diff --git a/src-tauri/tauri.conf.commercial.json b/src-tauri/tauri.conf.commercial.json new file mode 100644 index 0000000..a874c85 --- /dev/null +++ b/src-tauri/tauri.conf.commercial.json @@ -0,0 +1,16 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "agents-orchestrator-pro", + "identifier": "com.agentsorchestrator.pro", + "plugins": { + "updater": { + "endpoints": [ + "https://github.com/agents-orchestrator/agents-orchestrator/releases/latest/download/latest.json" + ] + } + }, + "bundle": { + "shortDescription": "Agents Orchestrator Pro — commercial multi-agent dashboard", + "longDescription": "Agents Orchestrator Pro is the commercial edition with advanced features including enterprise agent management, priority support, and extended integrations." + } +} diff --git a/src/lib/commercial/.gitkeep b/src/lib/commercial/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/commercial/.gitkeep b/tests/commercial/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/tests/commercial/pro-edition.test.ts b/tests/commercial/pro-edition.test.ts new file mode 100644 index 0000000..7c2c322 --- /dev/null +++ b/tests/commercial/pro-edition.test.ts @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: LicenseRef-Commercial +// Commercial-only tests — excluded from community test runs. + +import { describe, it, expect } from 'vitest'; + +describe('Pro Edition', () => { + it('commercial test suite is reachable', () => { + expect(true).toBe(true); + }); + + it('AGOR_EDITION env var is set to pro when running commercial tests', () => { + expect(process.env.AGOR_EDITION).toBe('pro'); + }); +});