diff --git a/bitvid-working-webtorrent/.devcontainer/devcontainer.json b/bitvid-working-webtorrent/.devcontainer/devcontainer.json new file mode 100644 index 00000000..007b07f4 --- /dev/null +++ b/bitvid-working-webtorrent/.devcontainer/devcontainer.json @@ -0,0 +1,17 @@ +{ + "name": "bitvid", + "image": "mcr.microsoft.com/devcontainers/javascript-node:22", + "features": { + "ghcr.io/devcontainers/features/github-cli:1": {} + }, + "postCreateCommand": "npm ci && npm run build && npx playwright install", + "customizations": { + "vscode": { + "extensions": [ + "esbenp.prettier-vscode", + "bradlc.vscode-tailwindcss", + "stylelint.vscode-stylelint" + ] + } + } +} diff --git a/bitvid-working-webtorrent/.dockerignore b/bitvid-working-webtorrent/.dockerignore new file mode 100644 index 00000000..b5cc6ace --- /dev/null +++ b/bitvid-working-webtorrent/.dockerignore @@ -0,0 +1,7 @@ +node_modules +.git +.gitignore +artifacts +Dockerfile +scripts +test-results diff --git a/bitvid-working-webtorrent/.gitattributes b/bitvid-working-webtorrent/.gitattributes new file mode 100644 index 00000000..dfe07704 --- /dev/null +++ b/bitvid-working-webtorrent/.gitattributes @@ -0,0 +1,2 @@ +# Auto detect text files and perform LF normalization +* text=auto diff --git a/bitvid-working-webtorrent/.github/workflows/agent-pr-review.yml b/bitvid-working-webtorrent/.github/workflows/agent-pr-review.yml new file mode 100644 index 00000000..b010a79b --- /dev/null +++ b/bitvid-working-webtorrent/.github/workflows/agent-pr-review.yml @@ -0,0 +1,52 @@ +name: Automated Agent PR Review + +on: + pull_request: + types: [opened, synchronize, reopened] + +permissions: + contents: write + pull-requests: write + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + agent-review: + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@v4 + with: + ref: ${{ github.event.pull_request.head.ref }} + repository: ${{ github.event.pull_request.head.repo.full_name }} + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Add upstream remote + run: | + git remote add upstream https://github.com/PR0M3TH3AN/bitvid.git || true + git fetch upstream + + - name: Verify upstream branch and diff + run: | + if git ls-remote --heads upstream "${{ github.base_ref }}" | grep "${{ github.base_ref }}" > /dev/null; then + git fetch upstream "${{ github.base_ref }}" + git diff --name-only upstream/${{ github.base_ref }}...HEAD + else + echo "::warning::Branch upstream/${{ github.base_ref }} not found. Skipping diff." + fi + + - name: Run Agent Review + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + PR_NUMBER: ${{ github.event.pull_request.number }} + GITHUB_BASE_REF: ${{ github.base_ref }} + GITHUB_BASE_REPO_URL: ${{ github.event.pull_request.base.repo.clone_url }} + run: node scripts/automated-pr-review.mjs diff --git a/bitvid-working-webtorrent/.github/workflows/ci.yml b/bitvid-working-webtorrent/.github/workflows/ci.yml new file mode 100644 index 00000000..61f888f9 --- /dev/null +++ b/bitvid-working-webtorrent/.github/workflows/ci.yml @@ -0,0 +1,247 @@ +name: CI + +on: + push: + branches: + - main + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + matrix: + node-version: [20] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Install ripgrep + run: sudo apt-get update && sudo apt-get install -y ripgrep + + - name: Run lint suite + run: npm run lint + + - name: Build assets + run: npm run build + + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run visual regression tests + id: visual + run: npm run test:visual + continue-on-error: true + + - name: Upload Playwright artifacts + if: steps.visual.outcome == 'failure' + uses: actions/upload-artifact@v4 + with: + name: playwright-visual-diffs + path: | + playwright-report + test-results + if-no-files-found: ignore + + - name: Fail if visual regressions detected + if: steps.visual.outcome == 'failure' + run: exit 1 + + unit-tests: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + fail-fast: false + matrix: + node-version: [20] + shard: [1, 2, 3] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Run unit tests (shard ${{ matrix.shard }}/3) + env: + UNIT_TEST_TIMEOUT_MS: 300000 + run: npm run test:unit:shard${{ matrix.shard }} + + dm-unit-tests: + runs-on: ubuntu-latest + timeout-minutes: 20 + strategy: + matrix: + node-version: [20] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Run DM unit tests + run: npm run test:dm:unit + + dm-integration-tests: + runs-on: ubuntu-latest + timeout-minutes: 30 + strategy: + matrix: + node-version: [20] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Run DM integration tests + run: npm run test:dm:integration + + e2e-headless: + runs-on: ubuntu-latest + timeout-minutes: 45 + strategy: + matrix: + node-version: [20] + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache npm directory + uses: actions/cache@v4 + with: + path: ~/.npm + key: ${{ runner.os }}-node-${{ matrix.node-version }}-npm-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-npm- + + - name: Cache node_modules + id: node-modules-cache + uses: actions/cache@v4 + with: + path: node_modules + key: ${{ runner.os }}-node-${{ matrix.node-version }}-modules-${{ hashFiles('package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node-${{ matrix.node-version }}-modules- + + - name: Install dependencies + if: steps.node-modules-cache.outputs.cache-hit != 'true' + run: npm ci + + - name: Install Playwright browsers + run: npx playwright install --with-deps chromium + + - name: Run headless E2E tests + run: npm run test:e2e diff --git a/bitvid-working-webtorrent/.github/workflows/cleanup-stuck-runs.yml b/bitvid-working-webtorrent/.github/workflows/cleanup-stuck-runs.yml new file mode 100644 index 00000000..3da93cd9 --- /dev/null +++ b/bitvid-working-webtorrent/.github/workflows/cleanup-stuck-runs.yml @@ -0,0 +1,72 @@ +name: Cleanup stuck workflow runs + +on: + schedule: + - cron: '*/30 * * * *' + workflow_dispatch: + +permissions: + actions: write + +jobs: + cleanup: + runs-on: ubuntu-latest + steps: + - name: Cancel stuck workflow runs + uses: actions/github-script@v7 + env: + STALE_THRESHOLD_HOURS: '2' + with: + script: | + const thresholdHours = Number(process.env.STALE_THRESHOLD_HOURS || 2); + const thresholdMs = thresholdHours * 60 * 60 * 1000; + const now = Date.now(); + + const statuses = ['queued', 'in_progress']; + const owner = context.repo.owner; + const repo = context.repo.repo; + const staleRuns = []; + + for (const status of statuses) { + const runs = await github.paginate(github.rest.actions.listWorkflowRunsForRepo, { + owner, + repo, + status, + per_page: 100, + }); + + for (const run of runs) { + const createdAt = Date.parse(run.created_at); + if (Number.isNaN(createdAt)) { + core.warning(`Skipping run ${run.id} (${run.name}) due to invalid created_at.`); + continue; + } + + const ageMs = now - createdAt; + if (ageMs > thresholdMs) { + staleRuns.push({ + id: run.id, + name: run.name, + status: run.status, + created_at: run.created_at, + html_url: run.html_url, + }); + } + } + } + + if (staleRuns.length === 0) { + core.info(`No runs older than ${thresholdHours} hours found in queued/in_progress.`); + return; + } + + core.info(`Found ${staleRuns.length} stale runs older than ${thresholdHours} hours.`); + + for (const run of staleRuns) { + core.info(`Canceling run ${run.id} (${run.name}) status=${run.status} created_at=${run.created_at} url=${run.html_url}`); + await github.request('POST /repos/{owner}/{repo}/actions/runs/{run_id}/force-cancel', { + owner, + repo, + run_id: run.id, + }); + } diff --git a/bitvid-working-webtorrent/.github/workflows/daily-design-system-audit.yml b/bitvid-working-webtorrent/.github/workflows/daily-design-system-audit.yml new file mode 100644 index 00000000..c16cffa3 --- /dev/null +++ b/bitvid-working-webtorrent/.github/workflows/daily-design-system-audit.yml @@ -0,0 +1,75 @@ +name: Daily Design System Audit + +on: + schedule: + - cron: '0 9 * * *' # Run daily at 09:00 UTC + workflow_dispatch: + +permissions: + contents: write + issues: write + pull-requests: write + +jobs: + audit: + name: Audit Design System + runs-on: ubuntu-latest + steps: + - name: Checkout unstable branch + uses: actions/checkout@v4 + with: + ref: unstable + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'npm' + + - name: Verify gh CLI availability + run: gh --version + + - name: Install dependencies + run: npm ci + + - name: Run Design System Audit (with fix) + id: audit + run: node scripts/daily-design-system-audit.mjs --fix + continue-on-error: true + + - name: Check for changes + id: git-check + run: | + if [ -n "$(git status --porcelain)" ]; then + echo "changes=true" >> $GITHUB_OUTPUT + else + echo "changes=false" >> $GITHUB_OUTPUT + fi + + - name: Create Pull Request + if: steps.git-check.outputs.changes == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b "ds-audit/$(date +%Y-%m-%d)" + git add . + git commit -m "chore: Apply daily design system remediation" + git push origin HEAD + gh pr create \ + --title "Daily Design System Remediation" \ + --body-file REMEDIATION_REPORT.md \ + --base unstable \ + --head "ds-audit/$(date +%Y-%m-%d)" \ + --label "design-system" + + - name: Create Issue on Failure (No Fixes) + if: steps.audit.outcome == 'failure' && steps.git-check.outputs.changes == 'false' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh issue create \ + --title "⚠� Design System Violations Detected" \ + --body-file REMEDIATION_REPORT.md \ + --label "design-system" diff --git a/bitvid-working-webtorrent/.gitignore b/bitvid-working-webtorrent/.gitignore new file mode 100644 index 00000000..f0d7d671 --- /dev/null +++ b/bitvid-working-webtorrent/.gitignore @@ -0,0 +1,12 @@ +saved_config.yaml +repo-context.txt +node_modules/ +src/webtorrent-docs/ +playwright-report/ +test-results/ +tests/visual/__snapshots__/ +artifacts/ +css/tailwind.generated.css +ai/reports/ +*.log +coverage/ diff --git a/bitvid-working-webtorrent/.vscode/settings.json b/bitvid-working-webtorrent/.vscode/settings.json new file mode 100644 index 00000000..d2ac1bee --- /dev/null +++ b/bitvid-working-webtorrent/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "liveServer.settings.root": "./src" +} diff --git a/bitvid-working-webtorrent/AGENTS.md b/bitvid-working-webtorrent/AGENTS.md new file mode 100644 index 00000000..c55a1d28 --- /dev/null +++ b/bitvid-working-webtorrent/AGENTS.md @@ -0,0 +1,202 @@ +# bitvid — AI Agent Guide + +This guide tells AI agents how to keep bitvid aligned with the current product direction. Follow it whenever you touch code, content, or documentation inside this repository. + +**Important:** Before starting any development or debugging, please review `KNOWN_ISSUES.md`. This file documents pre-existing test failures, environmental quirks, and architectural limitations that you should be aware of to avoid unnecessary investigation. + +For any nostr related work, please review the nip documentation located in /docs/nips before begining work. + +**Comment publishing guardrail:** Session actors are limited to passive telemetry (e.g., view counters). Always require the logged-in Nostr signer when sending video comments through [`js/nostr/commentEvents.js`](js/nostr/commentEvents.js). See the reminder in [`docs/nostr-event-schemas.md`](docs/nostr-event-schemas.md#event-catalogue) for context. + +--- + +## 1. Release Channels: Main vs. Unstable + +* **Main** is the production track. Anything merged here must preserve today’s UX and magnet safety guarantees. Rollbacks should be painless: keep commits atomic, avoid destructive migrations, and leave feature flags in a known-good default (`false` unless product explicitly flips them). +* **Unstable** is our experimentation lane. Gate risky behavior behind feature flags defined in `js/constants.js` and document the toggle/rollback plan in PR descriptions. +* **Emergency response:** If a change regresses URL-first playback or breaks magnet parsing, revert immediately, call this out in the PR, and annotate the AGENTS.md changelog with remediation tips. **Note:** These regressions usually surface in dev logs and tests around playback/magnet helpers—reference them when triaging so future agents don’t repeat the mistake. + +### Browser logging policy + +* Route every browser log through `js/utils/logger.js`. Do **not** call `console.*` directly. +* Use `logger.dev.*` for diagnostics that should disappear in production (flagged by `IS_DEV_MODE`/`isDevMode`). Use `logger.user.*` for operator-facing warnings and errors that must reach production consoles. +* Before promoting a build, flip `IS_DEV_MODE` in `config/instance-config.js` to match the target environment so `window.__BITVID_DEV_MODE__` and the logger channels behave correctly. +* Review and follow [`docs/logging.md`](docs/logging.md) whenever you add new logging or touch inline scripts. + +## 2. Mission: URL‑First Playback with WebTorrent Fallback + +* **Goal:** Always deliver smooth playback while keeping hosting costs low. +* **Primary transport:** A hosted video URL (MP4/WebM/HLS/DASH) that the `