diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..d62a2d9 --- /dev/null +++ b/.gitattributes @@ -0,0 +1 @@ +pnpm-lock.yaml -merge linguist-vendored diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b2743fd..26d58f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,173 +1,398 @@ name: CI on: - pull_request: - # labeled/unlabeled needed so the dependency-check bypass-age-gate label re-triggers the gate - types: [opened, synchronize, reopened, labeled, unlabeled] - pull_request_target: - # Run on release-please PRs (created by GITHUB_TOKEN which doesn't trigger regular pull_request) - types: [opened, synchronize, reopened] workflow_dispatch: + inputs: + all: + description: Run all jobs regardless of changed files + type: boolean + default: false + push: + branches: [main] + pull_request: + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.run_id || github.ref }} + cancel-in-progress: true + +permissions: + contents: read + +env: + FORCE_COLOR: '1' jobs: - format: + detect-changes: runs-on: ubuntu-slim - # Run from pull_request for non-release-please branches, or pull_request_target for release-please branches only - if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'release-please--')) || github.event_name == 'workflow_dispatch' + timeout-minutes: 5 + outputs: + format: ${{ steps.result.outputs.format }} + lint: ${{ steps.result.outputs.lint }} + build: ${{ steps.result.outputs.build }} + test: ${{ steps.result.outputs.test }} steps: - - name: Checkout code - uses: actions/checkout@v6 + - uses: runloopai/checkout@main with: - # For pull_request_target, explicitly checkout the PR head (for security) - # For regular pull_request and workflow_dispatch, checkout action uses defaults - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + fetch-depth: ${{ github.event_name == 'pull_request' && 1 || 2 }} + + - name: Detect changed files + if: ${{ !inputs.all }} + id: changed + uses: tj-actions/changed-files@9426d40962ed5378910ee2e21d5f8c6fcbf2dd96 # v47.0.6 + with: + files_yaml: | + ci: + - '.github/workflows/ci.yml' + format: + - 'src/**' + - '.prettierrc' + - '.prettierignore' + - 'package.json' + - 'pnpm-lock.yaml' + lint: + - 'src/**' + - 'eslint.config.js' + - 'tsconfig.json' + - 'tsconfig.test.json' + - 'package.json' + - 'pnpm-lock.yaml' + build: + - 'src/**' + - 'tsconfig.json' + - 'package.json' + - 'pnpm-lock.yaml' + test: + - 'src/**' + - 'tests/**' + - 'jest.config.js' + - 'jest.components.config.js' + - 'tsconfig.json' + - 'tsconfig.test.json' + - 'package.json' + - 'pnpm-lock.yaml' + + - name: Detect ci.yml self-changes + if: ${{ !inputs.all && steps.changed.outputs.ci_any_modified == 'true' }} + id: self + uses: runloopai/github-script@main + with: + script: | + // Inlined from changed_lines.js: addedLines + parseDiffHunks + detectCiSelfChanges + const fs = require('fs'); + const HUNK_RE = /^@@ -\d+(?:,\d+)? \+(\d+)(?:,(\d+))? @@/; + + function* addedLines(body, includeDeletionMarkers = false) { + let newLine = null; + for (const line of body.split('\n')) { + const m = HUNK_RE.exec(line); + if (m) { + const start = parseInt(m[1], 10); + const count = m[2] !== undefined ? parseInt(m[2], 10) : 1; + if (count === 0) { + if (includeDeletionMarkers) yield start; + newLine = null; + } else { + newLine = start; + } + continue; + } + if (newLine === null) continue; + if (line.startsWith('+')) { + yield newLine++; + } else if (line.startsWith('-')) { + // removed line: don't advance new-file numbering + } else if (!line.startsWith('\\')) { + newLine++; + } + } + } + + function parseDiffHunks(diffText) { + const result = {}; + let currentFile = null; + const bodyLines = []; + function flush() { + if (currentFile && bodyLines.length > 0) { + const arr = result[currentFile] ?? (result[currentFile] = []); + for (const n of addedLines(bodyLines.join('\n'), true)) arr.push(n); + } + bodyLines.length = 0; + } + for (const line of diffText.split('\n')) { + if (line.startsWith('diff --git ')) { + flush(); + currentFile = null; + } else if (line.startsWith('+++ b/')) { + flush(); + currentFile = line.slice(6); + } else if (line.startsWith('+++ /dev/null')) { + flush(); + currentFile = null; + } else if (currentFile) { + bodyLines.push(line); + } + } + flush(); + return result; + } - - name: Setup pnpm - uses: pnpm/action-setup@v5 + const CI_FILE = '.github/workflows/ci.yml'; + let base; + if (context.eventName === 'push' || context.eventName === 'workflow_dispatch') { + base = 'HEAD~1'; + } else { + base = context.payload.pull_request.base.sha; + const { exitCode, stderr } = await exec.getExecOutput( + 'git', ['fetch', 'origin', base, '--depth=1'], + { ignoreReturnCode: true, silent: true }, + ); + if (exitCode !== 0) { + core.warning(`git fetch of base ${base} failed: ${stderr.trim()} — running all jobs`); + core.setOutput('give_up', 'true'); + return; + } + } - - name: Setup Node.js - uses: actions/setup-node@v6 + const { stdout: diffOut } = await exec.getExecOutput( + 'git', ['diff', base, 'HEAD', '--unified=0', '--', CI_FILE], + { silent: true }, + ); + const diffLines = new Set(parseDiffHunks(diffOut)[CI_FILE] ?? []); + + if (diffLines.size === 0) { + core.notice('ci group triggered but ci.yml unchanged — running all jobs'); + core.setOutput('give_up', 'true'); + return; + } + + core.startGroup('ci.yml self-change detection'); + core.info(`Changed lines: ${[...diffLines].sort((a, b) => a - b).join(', ')}`); + + const { stdout: yqOut } = await exec.getExecOutput( + 'yq', ['-r', '.jobs | keys | .[]', CI_FILE], + { silent: true }, + ); + const jobKeys = yqOut.trim().split('\n').filter(Boolean); + const checkKeys = jobKeys.filter(k => k !== 'detect-changes'); + + const fileLines = fs.readFileSync(CI_FILE, 'utf8').split('\n'); + const totalLines = fileLines.length; + const jobsLine = fileLines.findIndex(l => l.trimEnd() === 'jobs:') + 1; + + const jobStarts = {}; + fileLines.forEach((line, i) => { + for (const key of jobKeys) { + if (line.trimEnd() === ` ${key}:`) jobStarts[key] = i + 1; + } + }); + + const sortedStarts = Object.entries(jobStarts).sort(([, a], [, b]) => a - b); + const jobRanges = {}; + for (let i = 0; i < sortedStarts.length; i++) { + const [k, start] = sortedStarts[i]; + const end = i + 1 < sortedStarts.length ? sortedStarts[i + 1][1] - 1 : totalLines; + jobRanges[k] = [start, end]; + } + for (const [k, [s, e]] of Object.entries(jobRanges).sort(([, [a]], [, [b]]) => a - b)) { + core.info(` ${k}: lines ${s}–${e}`); + } + + const [dcStart, dcEnd] = jobRanges['detect-changes'] ?? [0, 0]; + const topLevel = [...diffLines].some(l => l <= jobsLine || (l >= dcStart && l <= dcEnd)); + + let selfKeys; + if (topLevel) { + core.info('Top-level YAML or detect-changes changed — triggering all jobs'); + selfKeys = checkKeys; + } else { + selfKeys = checkKeys.filter(k => { + const [s, e] = jobRanges[k] ?? [0, -1]; + for (const l of diffLines) if (l >= s && l <= e) return true; + return false; + }); + for (const k of selfKeys) core.info(` ${k}: overlapping changes detected`); + } + + // No shard jobs in rl-cli — output keys match job keys 1:1 + const jobToOutputKey = {}; + const selfOutputKeys = [...new Set(selfKeys.map(k => jobToOutputKey[k] ?? k))]; + core.setOutput('keys', selfOutputKeys.join(' ')); + if (selfOutputKeys.length > 0) { + core.notice(`ci.yml self-change triggers: ${selfOutputKeys.join(', ')}`); + } else { + core.info('No job-specific overlap found'); + } + core.endGroup(); + + - name: Set outputs + if: ${{ !inputs.all }} + id: result + uses: runloopai/github-script@main + with: + script: | + // Inlined from changed_lines.js: setDetectChangesOutputs + const CI_FILE = '.github/workflows/ci.yml'; + + const { stdout: yqOut } = await exec.getExecOutput( + 'yq', ['-r', '.jobs["detect-changes"].outputs | keys | .[]', CI_FILE], + { silent: true }, + ); + const allOutputKeys = yqOut.trim().split('\n').filter(Boolean); + + const modifiedKeys = `${{ steps.changed.outputs.modified_keys }}`; + const selfKeys = `${{ steps.self.outputs.keys }}`; + const giveUp = `${{ steps.self.outputs.give_up }}` === 'true'; + const ciModified = `${{ steps.changed.outputs.ci_any_modified }}` === 'true'; + + const modList = modifiedKeys.split(' ').filter(Boolean); + const selfList = selfKeys.split(' ').filter(Boolean); + const runSet = new Set([...modList, ...selfList]); + + core.startGroup('Setting outputs'); + const skipped = new Set(); + if (giveUp) { + core.notice('give_up: cannot determine ci.yml self-changes — all jobs will run'); + } else { + for (const k of allOutputKeys) { + if (runSet.has(k)) { + core.info(` ${k} = (run)`); + } else { + core.info(` ${k} = false (skipped)`); + core.setOutput(k, 'false'); + skipped.add(k); + } + } + } + core.endGroup(); + + const modCell = modList.map(k => `\`${k}\``).join(' ') || '_(none)_'; + const selfCell = !ciModified + ? '_(not modified)_' + : selfList.length > 0 + ? selfList.map(k => `\`${k}\``).join(' ') + : '_(no job overlap)_'; + const runList = allOutputKeys.filter(k => !skipped.has(k)); + const summary = giveUp + ? 'All jobs will run (give_up)' + : `Running: ${runList.map(k => `\`${k}\``).join(', ') || '_(none)_'}`; + core.notice(summary); + await core.summary + .addHeading('Change Detection', 3) + .addRaw(`| | |\n|---|---|\n| **Event** | \`${context.eventName}\` |\n| **Modified groups** | ${modCell} |\n| **ci.yml self-change** | ${selfCell} |\n\n${summary}`) + .write(); + + format: + runs-on: ubuntu-slim + needs: [detect-changes] + if: failure() || (success() && (github.event_name == 'pull_request' || needs.detect-changes.outputs.format != 'false')) + env: + SHOULD_RUN: ${{ needs.detect-changes.outputs.format != 'false' }} + steps: + - if: env.SHOULD_RUN == 'true' + uses: runloopai/checkout@main + + - if: env.SHOULD_RUN == 'true' + uses: runloopai/pnpm-action@master + + - if: env.SHOULD_RUN == 'true' + uses: runloopai/setup-node@main with: node-version: "20" - cache: "pnpm" + cache: pnpm - - name: Install dependencies + - if: env.SHOULD_RUN == 'true' + name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run Prettier check + - if: env.SHOULD_RUN == 'true' + name: Run Prettier check run: pnpm run format:check lint: runs-on: ubuntu-slim - # Run from pull_request for non-release-please branches, or pull_request_target for release-please branches only - if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'release-please--')) || github.event_name == 'workflow_dispatch' + needs: [detect-changes] + if: failure() || (success() && (github.event_name == 'pull_request' || needs.detect-changes.outputs.lint != 'false')) + env: + SHOULD_RUN: ${{ needs.detect-changes.outputs.lint != 'false' }} steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - # For pull_request_target, explicitly checkout the PR head (for security) - # For regular pull_request and workflow_dispatch, checkout action uses defaults - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + - if: env.SHOULD_RUN == 'true' + uses: runloopai/checkout@main - - name: Setup pnpm - uses: pnpm/action-setup@v5 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/pnpm-action@master - - name: Setup Node.js - uses: actions/setup-node@v6 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/setup-node@main with: node-version: "20" - cache: "pnpm" + cache: pnpm - - name: Install dependencies + - if: env.SHOULD_RUN == 'true' + name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run ESLint + - if: env.SHOULD_RUN == 'true' + name: Run ESLint run: pnpm run lint build: runs-on: ubuntu-slim - # Run from pull_request for non-release-please branches, or pull_request_target for release-please branches only - if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'release-please--')) || github.event_name == 'workflow_dispatch' + needs: [detect-changes] + if: failure() || (success() && (github.event_name == 'pull_request' || needs.detect-changes.outputs.build != 'false')) + env: + SHOULD_RUN: ${{ needs.detect-changes.outputs.build != 'false' }} steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - # For pull_request_target, explicitly checkout the PR head (for security) - # For regular pull_request and workflow_dispatch, checkout action uses defaults - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + - if: env.SHOULD_RUN == 'true' + uses: runloopai/checkout@main - - name: Setup pnpm - uses: pnpm/action-setup@v5 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/pnpm-action@master - - name: Setup Node.js - uses: actions/setup-node@v6 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/setup-node@main with: node-version: "20" - cache: "pnpm" + cache: pnpm - - name: Install dependencies + - if: env.SHOULD_RUN == 'true' + name: Install dependencies run: pnpm install --frozen-lockfile - - name: Build TypeScript + - if: env.SHOULD_RUN == 'true' + name: Build TypeScript run: pnpm run build test: runs-on: ubuntu-slim - needs: build - # Run from pull_request for non-release-please branches, or pull_request_target for release-please branches only - if: (github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'release-please--')) || github.event_name == 'workflow_dispatch' + needs: [detect-changes] + if: failure() || (success() && (github.event_name == 'pull_request' || needs.detect-changes.outputs.test != 'false')) + env: + SHOULD_RUN: ${{ needs.detect-changes.outputs.test != 'false' }} steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - # For pull_request_target, explicitly checkout the PR head (for security) - # For regular pull_request and workflow_dispatch, checkout action uses defaults - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} + - if: env.SHOULD_RUN == 'true' + uses: runloopai/checkout@main - - name: Setup pnpm - uses: pnpm/action-setup@v5 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/pnpm-action@master - - name: Setup Node.js - uses: actions/setup-node@v6 + - if: env.SHOULD_RUN == 'true' + uses: runloopai/setup-node@main with: node-version: "20" - cache: "pnpm" + cache: pnpm - - name: Install dependencies + - if: env.SHOULD_RUN == 'true' + name: Install dependencies run: pnpm install --frozen-lockfile - - name: Run component tests with coverage + - if: env.SHOULD_RUN == 'true' + name: Run component tests with coverage run: pnpm run test:components - name: Upload coverage report - uses: actions/upload-artifact@v7 - if: always() + uses: runloopai/upload-artifact@main + if: always() && env.SHOULD_RUN == 'true' with: name: component-coverage path: coverage/ retention-days: 7 - - dependency-check: - runs-on: ubuntu-slim - if: github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--') - steps: - - name: Checkout code - uses: actions/checkout@v6 - with: - ref: ${{ github.event.pull_request.head.sha }} - repository: ${{ github.event.pull_request.head.repo.full_name }} - - - name: Fetch base ref - run: git fetch origin ${{ github.event.pull_request.base.ref }} --depth=1 - - - name: Check dependency age (supply-chain gate) - uses: runloopai/dependency-age-check-action@main - with: - ecosystems: npm - min-age-days: "7" - warn-age-days: "14" - base-ref: origin/${{ github.event.pull_request.base.ref }} - bypass-keyword: "bypass-age-gate" - - ready-to-merge: - runs-on: ubuntu-slim - permissions: {} - needs: [format, lint, build, test, dependency-check] - # Run from pull_request for non-release-please branches, or pull_request_target for release-please branches only - if: always() && ((github.event_name == 'pull_request' && !startsWith(github.event.pull_request.head.ref, 'release-please--')) || (github.event_name == 'pull_request_target' && startsWith(github.event.pull_request.head.ref, 'release-please--')) || github.event_name == 'workflow_dispatch') - steps: - - name: Check all jobs passed - run: | - if [[ "${{ needs.format.result }}" != "success" ]] || \ - [[ "${{ needs.lint.result }}" != "success" ]] || \ - [[ "${{ needs.build.result }}" != "success" ]] || \ - [[ "${{ needs.test.result }}" != "success" ]] || \ - [[ "${{ needs.dependency-check.result }}" != "success" && "${{ needs.dependency-check.result }}" != "skipped" ]]; then - echo "One or more required jobs failed" - exit 1 - fi - echo "All required jobs passed!" diff --git a/.github/workflows/dependency-age-check.yml b/.github/workflows/dependency-age-check.yml new file mode 100644 index 0000000..e1107a1 --- /dev/null +++ b/.github/workflows/dependency-age-check.yml @@ -0,0 +1,33 @@ +name: Dependency Age Check + +on: + push: + branches: [main] + paths: + - .github/workflows/dependency-age-check.yml + - pnpm-workspace.yaml + - pnpm-lock.yaml + pull_request: + branches: [main] + # labeled/unlabeled needed so the bypass-age-gate label re-triggers the gate + types: [opened, synchronize, reopened, labeled, unlabeled] + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.run_id || github.ref }} + +jobs: + dependency-check: + name: Check dependency age + runs-on: ubuntu-slim + + steps: + - name: Check out Git repository + uses: runloopai/checkout@main + + - name: Check dependency age (supply-chain gate) + uses: runloopai/lisan-al-gaib-action@main + with: + ecosystems: npm,actions + min-age-days: '7' + warn-age-days: '14' + bypass-keyword: 'bypass-age-gate' diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index 96bffb3..5aa4326 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -20,13 +20,13 @@ jobs: runs-on: ubuntu-slim steps: - name: Checkout code - uses: actions/checkout@v6 + uses: runloopai/checkout@main - name: Setup pnpm - uses: pnpm/action-setup@v5 + uses: runloopai/pnpm-action@master - name: Setup Node.js - uses: actions/setup-node@v6 + uses: runloopai/setup-node@main with: node-version: "20" cache: "pnpm" diff --git a/.github/workflows/pr-title.yml b/.github/workflows/pr-title.yml index 16f760b..518cfcd 100644 --- a/.github/workflows/pr-title.yml +++ b/.github/workflows/pr-title.yml @@ -3,83 +3,46 @@ name: PR Title Check on: pull_request: types: [opened, edited, synchronize, reopened] - workflow_dispatch: + +permissions: + pull-requests: read jobs: pr-title-check: runs-on: ubuntu-slim steps: - name: Validate PR title - uses: actions/github-script@v8 + uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - script: | - // Fetch current PR to always get latest title, even on workflow re-runs - const prNumber = context.issue.number || context.payload.pull_request?.number; - if (!prNumber) { - core.setFailed('Unable to determine PR number'); - return; - } - - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: prNumber - }); - - const title = pr.title; - console.log(`Validating PR title: "${title}"`); - - // Define allowed types - const types = [ - 'feat', 'fix', 'docs', 'style', 'refactor', - 'perf', 'test', 'build', 'ci', 'chore', 'revert' - ]; - - // Define allowed scopes (optional) - const scopes = [ - 'cli', 'mcp', 'devbox', 'benchmark', 'secret', - 'blueprint', 'storage-object', 'network-policy', - 'main', 'snapshot', 'config', 'auth', 'deps' - ]; - - // Regex pattern: type(scope)?: description - // type is required, scope is optional, description must start with lowercase - const pattern = /^(feat|fix|docs|style|refactor|perf|test|build|ci|chore|revert)(\([a-z0-9-]+\))?:\s*[a-z].+$/; - - if (!pattern.test(title)) { - const errorMsg = [ - '❌ PR title does not follow the conventional commit format.', - '', - `Found: "${title}"`, - '', - 'Expected format: type(scope)?: description', - '', - 'Where:', - `- type: one of ${types.join(', ')}`, - `- scope (optional): one of ${scopes.join(', ')}`, - '- description: starts with a lowercase letter', - '', - 'Examples:', - '- feat: add new devbox command', - '- fix(cli): resolve argument parsing issue', - '- docs: update README with usage examples', - '- feat(network-policy): add support for gateway flags' - ].join('\n'); - - core.setFailed(errorMsg); - return; - } - - // Extract and validate scope if present - const scopeMatch = title.match(/\(([^)]+)\)/); - if (scopeMatch) { - const scope = scopeMatch[1]; - if (!scopes.includes(scope)) { - core.setFailed( - `❌ Invalid scope "${scope}".\n\nAllowed scopes: ${scopes.join(', ')}` - ); - return; - } - } - - console.log('✅ PR title is valid!'); + types: | + feat + fix + docs + style + refactor + perf + test + build + ci + chore + revert + scopes: | + cli + mcp + devbox + benchmark + secret + blueprint + storage-object + network-policy + main + snapshot + config + auth + deps + subjectPattern: '^[a-z].+$' + subjectPatternError: | + The subject "{subject}" found in the pull request title "{title}" + must start with a lowercase letter. diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9385513..50b2202 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,10 +4,6 @@ on: push: branches: [main] -permissions: - contents: write - pull-requests: write - jobs: release: runs-on: ubuntu-slim @@ -15,9 +11,18 @@ jobs: release_created: ${{ steps.release.outputs.release_created }} tag_name: ${{ steps.release.outputs.tag_name }} steps: - - uses: googleapis/release-please-action@v4 + - uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 + id: app-token + with: + client-id: ${{ secrets.DEPLOY_APP_CLIENT_ID }} + private-key: ${{ secrets.DEPLOY_APP_PRIVATE_KEY }} + permission-contents: write + permission-pull-requests: write + + - uses: googleapis/release-please-action@45996ed1f6d02564a971a2fa1b5860e934307cf7 # v5.0.0 id: release with: + token: ${{ steps.app-token.outputs.token }} config-file: release-please-config.json manifest-file: .release-please-manifest.json @@ -30,13 +35,13 @@ jobs: id-token: write # Required for OIDC steps: - name: Checkout code - uses: actions/checkout@v6 + uses: runloopai/checkout@main - name: Setup pnpm - uses: pnpm/action-setup@v5 + uses: runloopai/pnpm-action@master - name: Setup Node.js - uses: actions/setup-node@v6 + uses: runloopai/setup-node@main with: node-version: "24" registry-url: "https://registry.npmjs.org" diff --git a/.prettierignore b/.prettierignore index c141aec..c2c4f61 100644 --- a/.prettierignore +++ b/.prettierignore @@ -6,3 +6,4 @@ coverage *.log package-lock.json src/mcp/index.js +pnpm-lock.yaml diff --git a/jest.config.js b/jest.config.js index 01c4902..987bb6a 100644 --- a/jest.config.js +++ b/jest.config.js @@ -66,7 +66,7 @@ export default { // Transform ignore patterns for node_modules transformIgnorePatterns: [ - 'node_modules/(?!(conf|@runloop|ink|react|ink-big-text|ink-gradient|ink-spinner|ink-text-input|ink-select-input|ink-box|ink-text|ink-testing-library|figures|is-unicode-supported)/)' + 'node_modules/(?!(conf|@runloop|ink|react|ink-gradient|ink-spinner|ink-text-input|ink-select-input|ink-box|ink-text|ink-testing-library|figures|is-unicode-supported)/)' ], // Treat these extensions as ESM diff --git a/package.json b/package.json index 1ad1b61..97b1145 100644 --- a/package.json +++ b/package.json @@ -28,9 +28,10 @@ "test:e2e": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.e2e.config.js --forceExit", "test:router": "NODE_OPTIONS='--experimental-vm-modules' jest --config jest.router.config.js --forceExit", "docs:commands": "pnpm run build && node scripts/generate-command-docs.js", - "prepare": "husky" + "prepare": "husky", + "actions:update": "actions-up --min-age 14 --exclude 'runloopai/.+'" }, - "packageManager": "pnpm@9.15.4", + "packageManager": "pnpm@10.33.0", "keywords": [ "runloop", "cli", @@ -83,58 +84,28 @@ "figures": "6.1.0", "gradient-string": "3.0.0", "ink": "6.6.0", - "ink-big-text": "2.0.0", "ink-gradient": "3.0.0", "ink-link": "5.0.0", "ink-spinner": "5.0.0", "ink-text-input": "6.0.0", "react": "19.2.0", - "ws": "^8.20.1", "tar-stream": "3.1.7", + "ws": "^8.20.1", "yaml": "2.8.3", "zustand": "5.0.10" }, - "pnpm": { - "onlyBuiltDependencies": [ - "esbuild" - ], - "overrides": { - "tmp": "^0.2.6", - "qs": "^6.15.2", - "ws": "^8.20.1", - "hono": "4.12.24", - "@hono/node-server": "^1.19.14", - "@modelcontextprotocol/sdk>ajv": "^8.18.0", - "express-rate-limit": "^8.3.2", - "fast-uri": "3.1.2", - "ip-address": "10.1.1", - "tar": "^7.5.13", - "flatted": "^3.4.2", - "handlebars": "^4.7.9", - "eslint>minimatch": "^3.1.3", - "eslint-plugin-react>minimatch": "^3.1.3", - "glob>minimatch": "^3.1.3", - "test-exclude>minimatch": "^3.1.3", - "@typescript-eslint/typescript-estree>minimatch": "^9.0.9", - "jest-util>picomatch": "^2.3.2", - "anymatch>picomatch": "^2.3.2", - "eslint>ajv": "^6.14.0", - "minimatch>brace-expansion": "^1.1.13", - "node-forge": "^1.4.0", - "micromatch>picomatch": "^2.3.2", - "tinyglobby>picomatch": "^4.0.4", - "path-to-regexp": "^8.4.2" - } - }, "devDependencies": { "@anthropic-ai/mcpb": "2.1.2", + "@eslint/js": "9.39.2", "@types/adm-zip": "0.5.7", "@types/jest": "29.5.14", "@types/node": "22.19.7", "@types/react": "19.2.10", "@types/tar-stream": "3.1.4", + "@types/ws": "^8.5.0", "@typescript-eslint/eslint-plugin": "8.54.0", "@typescript-eslint/parser": "8.54.0", + "actions-up": "^1.14.2", "esbuild": "0.27.2", "eslint": "9.39.2", "eslint-plugin-react": "7.37.5", @@ -146,7 +117,6 @@ "prettier": "3.8.1", "ts-jest": "29.4.6", "ts-node": "10.9.2", - "@types/ws": "^8.5.0", "typescript": "5.9.3" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4e12e7b..49fa981 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,31 +5,28 @@ settings: excludeLinksFromLockfile: false overrides: - tmp: ^0.2.6 - qs: ^6.15.2 - ws: ^8.20.1 - hono: 4.12.24 - '@hono/node-server': ^1.19.14 - '@modelcontextprotocol/sdk>ajv': ^8.18.0 - express-rate-limit: ^8.3.2 - fast-uri: 3.1.2 - ip-address: 10.1.1 - tar: ^7.5.13 - flatted: ^3.4.2 - handlebars: ^4.7.9 - eslint>minimatch: ^3.1.3 - eslint-plugin-react>minimatch: ^3.1.3 - glob>minimatch: ^3.1.3 - test-exclude>minimatch: ^3.1.3 - '@typescript-eslint/typescript-estree>minimatch': ^9.0.9 - jest-util>picomatch: ^2.3.2 - anymatch>picomatch: ^2.3.2 - eslint>ajv: ^6.14.0 - minimatch>brace-expansion: ^1.1.13 - node-forge: ^1.4.0 - micromatch>picomatch: ^2.3.2 - tinyglobby>picomatch: ^4.0.4 - path-to-regexp: ^8.4.2 + path-to-regexp@>=8.0.0 <8.4.0: 8.4.0 + fast-uri@<3.1.2: 3.1.2 + qs@>=6.7.0 <6.15.2: 6.15.2 + ws@>=8.0.0 <8.20.1: 8.20.1 + tmp@<0.2.6: 0.2.6 + handlebars@>=4.0.0 <4.7.9: 4.7.9 + flatted@<3.4.2: 3.4.2 + node-forge@<1.4.0: 1.4.0 + ip-address@<10.1.1: 10.1.1 + tar@<7.5.11: 7.5.11 + express-rate-limit@>=8.2.0 <8.2.2: 8.2.2 + '@hono/node-server@<1.19.13': 1.19.13 + hono@<4.12.21: 4.12.21 + minimatch@<3.1.4: 3.1.4 + minimatch@>=9.0.0 <9.0.7: 9.0.7 + brace-expansion@<1.1.13: 1.1.13 + brace-expansion@>=2.0.0 <2.0.3: 2.0.3 + picomatch@<2.3.2: 2.3.2 + picomatch@>=4.0.0 <4.0.4: 4.0.4 + ajv@<6.14.0: 6.14.0 + ajv@>=7.0.0-alpha.0 <8.18.0: 8.18.0 + yaml@>=2.0.0 <2.8.3: 2.8.3 importers: @@ -74,9 +71,6 @@ importers: ink: specifier: 6.6.0 version: 6.6.0(@types/react@19.2.10)(react@19.2.0) - ink-big-text: - specifier: 2.0.0 - version: 2.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.0))(react@19.2.0) ink-gradient: specifier: 3.0.0 version: 3.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.0)) @@ -108,6 +102,9 @@ importers: '@anthropic-ai/mcpb': specifier: 2.1.2 version: 2.1.2 + '@eslint/js': + specifier: 9.39.2 + version: 9.39.2 '@types/adm-zip': specifier: 0.5.7 version: 0.5.7 @@ -132,6 +129,9 @@ importers: '@typescript-eslint/parser': specifier: 8.54.0 version: 8.54.0(eslint@9.39.2)(typescript@5.9.3) + actions-up: + specifier: ^1.14.2 + version: 1.14.2 esbuild: specifier: 0.27.2 version: 0.27.2 @@ -542,11 +542,11 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@hono/node-server@1.19.14': - resolution: {integrity: sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==} + '@hono/node-server@1.19.13': + resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} engines: {node: '>=18.14.1'} peerDependencies: - hono: 4.12.24 + hono: 4.12.21 '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} @@ -938,6 +938,11 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + actions-up@1.14.2: + resolution: {integrity: sha512-yQHZAU4UkWegGY2vmgg1TYAoKQDFPX5NUAI57P0BXhgB6wDGBNVhgBuXUkid8mxVviF8OMVr4yxMvax2nnY0Cw==} + engines: {node: ^18.0.0 || >=20.0.0} + hasBin: true + adm-zip@0.5.16: resolution: {integrity: sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==} engines: {node: '>=12.0'} @@ -949,7 +954,7 @@ packages: ajv-formats@3.0.1: resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} peerDependencies: - ajv: ^8.0.0 + ajv: 8.18.0 peerDependenciesMeta: ajv: optional: true @@ -960,6 +965,10 @@ packages: ajv@8.18.0: resolution: {integrity: sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==} + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + ansi-escapes@4.3.2: resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} engines: {node: '>=8'} @@ -1083,6 +1092,10 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + bare-events@2.8.2: resolution: {integrity: sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==} peerDependencies: @@ -1099,8 +1112,12 @@ packages: resolution: {integrity: sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==} engines: {node: '>=18'} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.13: + resolution: {integrity: sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} @@ -1125,6 +1142,10 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + cac@7.0.0: + resolution: {integrity: sha512-tixWYgm5ZoOD+3g6UTea91eow5z6AAHaho3g0V9CNSNb45gM8SmflpAc+GRd1InC4AqN/07Unrgp56Y94N9hJQ==} + engines: {node: '>=20.19.0'} + call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} @@ -1152,11 +1173,6 @@ packages: caniuse-lite@1.0.30001766: resolution: {integrity: sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==} - cfonts@3.3.1: - resolution: {integrity: sha512-ZGEmN3W9mViWEDjsuPo4nK4h39sfh6YtoneFYp9WLPI/rw8BaSSrfQC6jkrGW3JMvV3ZnExJB/AEqXc/nHYxkw==} - engines: {node: '>=10'} - hasBin: true - chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} @@ -1334,10 +1350,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - define-property@1.0.0: - resolution: {integrity: sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==} - engines: {node: '>=0.10.0'} - delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -1394,6 +1406,10 @@ packages: resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} engines: {node: '>= 0.8'} + enquirer@2.4.1: + resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} + engines: {node: '>=8.6'} + env-paths@3.0.0: resolution: {integrity: sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} @@ -1550,8 +1566,8 @@ packages: resolution: {integrity: sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - express-rate-limit@8.3.2: - resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} + express-rate-limit@8.2.2: + resolution: {integrity: sha512-Ybv7bqtOgA914MLwaHWVFXMpMYeR1MQu/D+z2MaLYteqBsTIp9sY3AU7mGNLMJv8eLg8uQMpE20I+L2Lv49nSg==} engines: {node: '>= 16'} peerDependencies: express: '>= 4.11' @@ -1586,7 +1602,7 @@ packages: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: - picomatch: ^3 || ^4 + picomatch: 4.0.4 peerDependenciesMeta: picomatch: optional: true @@ -1785,8 +1801,8 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} - hono@4.12.24: - resolution: {integrity: sha512-I36D1s+HgQc55KbhEr4iybfxv/9o1zdpw+XEM6dJa91LqQD0HCoSGdxpRJCZE+aavs87j4V3Ls2OJzq8C/U4iw==} + hono@4.12.21: + resolution: {integrity: sha512-uV63apnb0kyPtAUwoWgaGh9HyIFcv8lgmzPZSiTBQAFOFGIzka5EZ1dZocmGnn0XdX0+XTqJ6Tqv7selMuGLRQ==} engines: {node: '>=16.9.0'} html-escaper@2.0.2: @@ -1848,13 +1864,6 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - ink-big-text@2.0.0: - resolution: {integrity: sha512-Juzqv+rIOLGuhMJiE50VtS6dg6olWfzFdL7wsU/EARSL5Eaa5JNXMogMBm9AkjgzO2Y3UwWCOh87jbhSn8aNdw==} - engines: {node: '>=14.16'} - peerDependencies: - ink: '>=4' - react: '>=18' - ink-gradient@3.0.0: resolution: {integrity: sha512-OVyPBovBxE1tFcBhSamb+P1puqDP6pG3xFe2W9NiLgwUZd9RbcjBeR7twLbliUT9navrUstEf1ZcPKKvx71BsQ==} engines: {node: '>=16'} @@ -1915,10 +1924,6 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} - is-accessor-descriptor@1.0.1: - resolution: {integrity: sha512-YBUanLI8Yoihw923YeFUS5fs0fF2f5TSFTNiYAAzhhDscDa3lEqYuz1pDOEP5KvX94I9ey3vsqjJcLVFVU+3QA==} - engines: {node: '>= 0.10'} - is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -1938,9 +1943,6 @@ packages: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} - is-buffer@1.1.6: - resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} @@ -1949,10 +1951,6 @@ packages: resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} engines: {node: '>= 0.4'} - is-data-descriptor@1.0.1: - resolution: {integrity: sha512-bc4NlCDiCr28U4aEsQ3Qs2491gVq4V8G7MQyws968ImqjKuYtTJXrl7Vq7jsN7Ly/C3xj5KWFrY7sHNeDkAzXw==} - engines: {node: '>= 0.4'} - is-data-view@1.0.2: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} @@ -1961,10 +1959,6 @@ packages: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} - is-descriptor@1.0.3: - resolution: {integrity: sha512-JCNNGbwWZEVaSPtS45mdtrneRWJFp07LLmykxeFV5F6oBvNF8vHSfJuJgoT472pSfk+Mf8VnlrspaFBHWM8JAw==} - engines: {node: '>= 0.4'} - is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} @@ -2010,10 +2004,6 @@ packages: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} - is-number@3.0.0: - resolution: {integrity: sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==} - engines: {node: '>=0.10.0'} - is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} @@ -2283,10 +2273,6 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kind-of@3.2.2: - resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} - engines: {node: '>=0.10.0'} - kleur@3.0.3: resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==} engines: {node: '>=6'} @@ -2376,11 +2362,11 @@ packages: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} - minimatch@3.1.5: - resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + minimatch@3.1.4: + resolution: {integrity: sha512-twmL+S8+7yIsE9wsqgzU3E8/LumN3M3QELrBZ20OdmQ9jB2JvW5oZtBEmft84k/Gs5CG9mqtWc6Y9vW+JEzGxw==} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} + minimatch@9.0.7: + resolution: {integrity: sha512-MOwgjc8tfrpn5QQEvjijjmDVtMw2oL88ugTevzxQnzRLm6l3fVEF2gzU0kYeYYKD8C66+IdGX6peJ4MyUlUnPg==} engines: {node: '>=16 || 14 >=14.17'} minimist@1.2.8: @@ -2401,6 +2387,9 @@ packages: resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + nanospinner@1.2.2: + resolution: {integrity: sha512-Zt/AmG6qRU3e+WnzGGLuMCEAO/dAu45stNbHY223tUxldaDAeE+FxSPsd9Q+j+paejmm0ZbrNVs5Sraqy3dRxA==} + natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} @@ -2541,8 +2530,8 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-to-regexp@8.4.2: - resolution: {integrity: sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==} + path-to-regexp@8.4.0: + resolution: {integrity: sha512-PuseHIvAnz3bjrM2rGJtSgo1zjgxapTLZ7x2pjhzWwlp4SJQgK3f3iZIQwkpEnBaKz6seKBADpM4B4ySkuYypg==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -2710,6 +2699,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.1: + resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + engines: {node: '>=10'} + hasBin: true + send@1.2.1: resolution: {integrity: sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==} engines: {node: '>= 18'} @@ -2888,8 +2882,8 @@ packages: tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} - tar@7.5.13: - resolution: {integrity: sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==} + tar@7.5.11: + resolution: {integrity: sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==} engines: {node: '>=18'} terminal-link@5.0.0: @@ -2913,8 +2907,8 @@ packages: tinygradient@1.1.5: resolution: {integrity: sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==} - tmp@0.2.7: - resolution: {integrity: sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==} + tmp@0.2.6: + resolution: {integrity: sha512-5sJPdPjfI5Kx+qbrDesxkglRBxW//g7hCsqspEjwkewGvBMGIKMOTKzLt1hFVJzyadba3lDUN20O9qhvbQUSTA==} engines: {node: '>=14.14'} tmpl@1.0.5: @@ -3115,11 +3109,6 @@ packages: resolution: {integrity: sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA==} engines: {node: '>=18'} - window-size@1.1.1: - resolution: {integrity: sha512-5D/9vujkmVQ7pSmc0SCBmHXbkv6eaHwXEx65MywhmUMsI8sGqJ972APq1lotfcwMKPFLuCFfL8xGHLIp7jaBmA==} - engines: {node: '>= 0.10.0'} - hasBin: true - word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} @@ -3146,6 +3135,18 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} + ws@8.20.1: + resolution: {integrity: sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + ws@8.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} @@ -3174,6 +3175,11 @@ packages: engines: {node: '>= 14.6'} hasBin: true + yaml@2.9.0: + resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} + engines: {node: '>= 14.6'} + hasBin: true + yargs-parser@21.1.1: resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} engines: {node: '>=12'} @@ -3535,7 +3541,7 @@ snapshots: dependencies: '@eslint/object-schema': 2.1.7 debug: 4.4.3 - minimatch: 3.1.5 + minimatch: 3.1.4 transitivePeerDependencies: - supports-color @@ -3556,7 +3562,7 @@ snapshots: ignore: 5.3.2 import-fresh: 3.3.1 js-yaml: 4.1.1 - minimatch: 3.1.5 + minimatch: 3.1.4 strip-json-comments: 3.1.1 transitivePeerDependencies: - supports-color @@ -3570,9 +3576,9 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 - '@hono/node-server@1.19.14(hono@4.12.24)': + '@hono/node-server@1.19.13(hono@4.12.21)': dependencies: - hono: 4.12.24 + hono: 4.12.21 '@humanfs/core@0.19.1': {} @@ -3887,7 +3893,7 @@ snapshots: '@modelcontextprotocol/sdk@1.26.0(zod@4.3.6)': dependencies: - '@hono/node-server': 1.19.14(hono@4.12.24) + '@hono/node-server': 1.19.13(hono@4.12.21) ajv: 8.18.0 ajv-formats: 3.0.1(ajv@8.18.0) content-type: 1.0.5 @@ -3896,8 +3902,8 @@ snapshots: eventsource: 3.0.7 eventsource-parser: 3.0.6 express: 5.2.1 - express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.24 + express-rate-limit: 8.2.2(express@5.2.1) + hono: 4.12.21 jose: 6.1.3 json-schema-typed: 8.0.2 pkce-challenge: 5.0.1 @@ -3916,7 +3922,7 @@ snapshots: form-data-encoder: 1.7.2 formdata-node: 4.4.1 node-fetch: 2.7.0 - tar: 7.5.13 + tar: 7.5.11 uuidv7: 1.1.0 zod: 3.25.76 transitivePeerDependencies: @@ -4137,7 +4143,7 @@ snapshots: '@typescript-eslint/types': 8.54.0 '@typescript-eslint/visitor-keys': 8.54.0 debug: 4.4.3 - minimatch: 9.0.9 + minimatch: 9.0.7 semver: 7.7.4 tinyglobby: 0.2.15 ts-api-utils: 2.4.0(typescript@5.9.3) @@ -4180,6 +4186,15 @@ snapshots: acorn@8.15.0: {} + actions-up@1.14.2: + dependencies: + cac: 7.0.0 + enquirer: 2.4.1 + nanospinner: 1.2.2 + picocolors: 1.1.1 + semver: 7.8.1 + yaml: 2.9.0 + adm-zip@0.5.16: {} agentkeepalive@4.6.0: @@ -4204,6 +4219,8 @@ snapshots: json-schema-traverse: 1.0.0 require-from-string: 2.0.2 + ansi-colors@4.1.3: {} + ansi-escapes@4.3.2: dependencies: type-fest: 0.21.3 @@ -4368,6 +4385,8 @@ snapshots: balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + bare-events@2.8.2: {} baseline-browser-mapping@2.9.19: {} @@ -4386,11 +4405,15 @@ snapshots: transitivePeerDependencies: - supports-color - brace-expansion@1.1.14: + brace-expansion@1.1.13: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 @@ -4415,6 +4438,8 @@ snapshots: bytes@3.1.2: {} + cac@7.0.0: {} + call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 @@ -4440,11 +4465,6 @@ snapshots: caniuse-lite@1.0.30001766: {} - cfonts@3.3.1: - dependencies: - supports-color: 8.1.1 - window-size: 1.1.1 - chalk@4.1.2: dependencies: ansi-styles: 4.3.0 @@ -4605,10 +4625,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - define-property@1.0.0: - dependencies: - is-descriptor: 1.0.3 - delayed-stream@1.0.0: {} depd@2.0.0: {} @@ -4647,6 +4663,11 @@ snapshots: encodeurl@2.0.0: {} + enquirer@2.4.1: + dependencies: + ansi-colors: 4.1.3 + strip-ansi: 6.0.1 + env-paths@3.0.0: {} environment@1.1.0: {} @@ -4817,7 +4838,7 @@ snapshots: estraverse: 5.3.0 hasown: 2.0.2 jsx-ast-utils: 3.3.5 - minimatch: 3.1.5 + minimatch: 3.1.4 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 @@ -4869,7 +4890,7 @@ snapshots: is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 lodash.merge: 4.6.2 - minimatch: 3.1.5 + minimatch: 3.1.4 natural-compare: 1.4.0 optionator: 0.9.4 transitivePeerDependencies: @@ -4933,7 +4954,7 @@ snapshots: jest-message-util: 29.7.0 jest-util: 29.7.0 - express-rate-limit@8.3.2(express@5.2.1): + express-rate-limit@8.2.2(express@5.2.1): dependencies: express: 5.2.1 ip-address: 10.1.1 @@ -4975,7 +4996,7 @@ snapshots: dependencies: chardet: 0.7.0 iconv-lite: 0.4.24 - tmp: 0.2.7 + tmp: 0.2.6 fast-deep-equal@3.1.3: {} @@ -5144,7 +5165,7 @@ snapshots: fs.realpath: 1.0.0 inflight: 1.0.6 inherits: 2.0.4 - minimatch: 3.1.5 + minimatch: 3.1.4 once: 1.4.0 path-is-absolute: 1.0.1 @@ -5204,7 +5225,7 @@ snapshots: dependencies: function-bind: 1.1.2 - hono@4.12.24: {} + hono@4.12.21: {} html-escaper@2.0.2: {} @@ -5257,13 +5278,6 @@ snapshots: inherits@2.0.4: {} - ink-big-text@2.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.0))(react@19.2.0): - dependencies: - cfonts: 3.3.1 - ink: 6.6.0(@types/react@19.2.10)(react@19.2.0) - prop-types: 15.8.1 - react: 19.2.0 - ink-gradient@3.0.0(ink@6.6.0(@types/react@19.2.10)(react@19.2.0)): dependencies: '@types/gradient-string': 1.1.6 @@ -5318,7 +5332,7 @@ snapshots: type-fest: 4.41.0 widest-line: 5.0.0 wrap-ansi: 9.0.2 - ws: 8.21.0 + ws: 8.20.1 yoga-layout: 3.2.1 optionalDependencies: '@types/react': 19.2.10 @@ -5336,10 +5350,6 @@ snapshots: ipaddr.js@1.9.1: {} - is-accessor-descriptor@1.0.1: - dependencies: - hasown: 2.0.2 - is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -5365,18 +5375,12 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-buffer@1.1.6: {} - is-callable@1.2.7: {} is-core-module@2.16.1: dependencies: hasown: 2.0.2 - is-data-descriptor@1.0.1: - dependencies: - hasown: 2.0.2 - is-data-view@1.0.2: dependencies: call-bound: 1.0.4 @@ -5388,11 +5392,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-descriptor@1.0.3: - dependencies: - is-accessor-descriptor: 1.0.1 - is-data-descriptor: 1.0.1 - is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: @@ -5430,10 +5429,6 @@ snapshots: call-bound: 1.0.4 has-tostringtag: 1.0.2 - is-number@3.0.0: - dependencies: - kind-of: 3.2.2 - is-number@7.0.0: {} is-promise@4.0.0: {} @@ -5892,10 +5887,6 @@ snapshots: dependencies: json-buffer: 3.0.1 - kind-of@3.2.2: - dependencies: - is-buffer: 1.1.6 - kleur@3.0.3: {} leven@3.1.0: {} @@ -5966,13 +5957,13 @@ snapshots: mimic-function@5.0.1: {} - minimatch@3.1.5: + minimatch@3.1.4: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.13 - minimatch@9.0.9: + minimatch@9.0.7: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 5.0.6 minimist@1.2.8: {} @@ -5986,6 +5977,10 @@ snapshots: mute-stream@1.0.0: {} + nanospinner@1.2.2: + dependencies: + picocolors: 1.1.1 + natural-compare@1.4.0: {} negotiator@1.0.0: {} @@ -6114,7 +6109,7 @@ snapshots: path-parse@1.0.7: {} - path-to-regexp@8.4.2: {} + path-to-regexp@8.4.0: {} picocolors@1.1.1: {} @@ -6245,7 +6240,7 @@ snapshots: depd: 2.0.0 is-promise: 4.0.0 parseurl: 1.3.3 - path-to-regexp: 8.4.2 + path-to-regexp: 8.4.0 transitivePeerDependencies: - supports-color @@ -6276,6 +6271,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.1: {} + send@1.2.1: dependencies: debug: 4.4.3 @@ -6515,7 +6512,7 @@ snapshots: - bare-abort-controller - react-native-b4a - tar@7.5.13: + tar@7.5.11: dependencies: '@isaacs/fs-minipass': 4.0.1 chownr: 3.0.0 @@ -6532,7 +6529,7 @@ snapshots: dependencies: '@istanbuljs/schema': 0.1.3 glob: 7.2.3 - minimatch: 3.1.5 + minimatch: 3.1.4 text-decoder@1.2.7: dependencies: @@ -6552,7 +6549,7 @@ snapshots: '@types/tinycolor2': 1.4.6 tinycolor2: 1.6.0 - tmp@0.2.7: {} + tmp@0.2.6: {} tmpl@1.0.5: {} @@ -6768,11 +6765,6 @@ snapshots: dependencies: string-width: 7.2.0 - window-size@1.1.1: - dependencies: - define-property: 1.0.0 - is-number: 3.0.0 - word-wrap@1.2.5: {} wordwrap@1.0.0: {} @@ -6802,6 +6794,8 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 + ws@8.20.1: {} + ws@8.21.0: {} y18n@5.0.8: {} @@ -6812,6 +6806,8 @@ snapshots: yaml@2.8.3: {} + yaml@2.9.0: {} + yargs-parser@21.1.1: {} yargs@17.7.2: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..15fd586 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,30 @@ +# pnpm 10 moved these settings out of package.json#pnpm into this file. +minimumReleaseAge: 10080 +minimumReleaseAgeExclude: + - '@runloop/api-client' +onlyBuiltDependencies: + - esbuild +overrides: + # Security pins (Dependabot): key = advisory vulnerable range, value = first_patched_version + 'path-to-regexp@>=8.0.0 <8.4.0': 8.4.0 + 'fast-uri@<3.1.2': 3.1.2 + 'qs@>=6.7.0 <6.15.2': 6.15.2 + 'ws@>=8.0.0 <8.20.1': 8.20.1 + 'tmp@<0.2.6': 0.2.6 + 'handlebars@>=4.0.0 <4.7.9': 4.7.9 + 'flatted@<3.4.2': 3.4.2 + 'node-forge@<1.4.0': 1.4.0 + 'ip-address@<10.1.1': 10.1.1 + 'tar@<7.5.11': 7.5.11 + 'express-rate-limit@>=8.2.0 <8.2.2': 8.2.2 + '@hono/node-server@<1.19.13': 1.19.13 + 'hono@<4.12.21': 4.12.21 + 'minimatch@<3.1.4': 3.1.4 + 'minimatch@>=9.0.0 <9.0.7': 9.0.7 + 'brace-expansion@<1.1.13': 1.1.13 + 'brace-expansion@>=2.0.0 <2.0.3': 2.0.3 + 'picomatch@<2.3.2': 2.3.2 + 'picomatch@>=4.0.0 <4.0.4': 4.0.4 + 'ajv@<6.14.0': 6.14.0 + 'ajv@>=7.0.0-alpha.0 <8.18.0': 8.18.0 + 'yaml@>=2.0.0 <2.8.3': 2.8.3 diff --git a/src/components/Banner.tsx b/src/components/Banner.tsx index 7855889..4a3627c 100644 --- a/src/components/Banner.tsx +++ b/src/components/Banner.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect, useRef } from "react"; import { Box, Text, useStdout } from "ink"; -import BigText from "ink-big-text"; import Gradient from "ink-gradient"; import { isLightMode } from "../utils/theme.js"; @@ -88,9 +87,19 @@ const LIGHT_FRAMES = precomputeFrames( LIGHT_SHIMMER_COLORS.filter((_, i) => i % 2 === 0), ); -// Minimum width to show the full BigText banner (simple3d font needs ~80 chars for "RUNLOOP.ai") +// Pre-rendered "RUNLOOP.ai" in simple3d font (previously rendered by cfonts via ink-big-text) +const BANNER_ART = `\ + ___ + _ __ __ __ ___ /\\_ \\ ___ ___ _____ __ __ +/\\\` __\\/\\ \\/\\ \\ /' _ \`\\ \\//\\ \\ / __\`\\ / __\`\\ /\\ '__\`\\ /'__\`\\ /\\_\\ +\\ \\ \\/ \\ \\ \\_\\ \\/\\ \\/\\ \\ \\_\\ \\_ /\\ \\_\\ \\/\\ \\_\\ \\\\ \\ \\_\\ \\ /\\ \\_\\.\\_ \\/\\ \\ + \\ \\_\\ \\ \\____/\\ \\_\\ \\_\\ /\\____\\\\ \\____/\\ \\____/ \\ \\ ,__/ __ \\ \\__/.\\_\\ \\ \\ \\ + \\/_/ \\/___/ \\/_/\\/_/ \\/____/ \\/___/ \\/___/ \\ \\ \\/ /\\_\\ \\/__/\\/_/ \\/_/ + \\/_/ \\/_/ `; + +// Minimum width to show the full banner (simple3d font needs ~80 chars for "RUNLOOP.ai") const MIN_WIDTH_FOR_BIG_BANNER = 90; -// Minimum height to show the full BigText banner - require generous room (40 lines) +// Minimum height to show the full banner - require generous room (40 lines) const MIN_HEIGHT_FOR_BIG_BANNER = 40; // Animation interval in ms @@ -169,7 +178,7 @@ export const Banner = React.memo(() => { return ( - + {BANNER_ART} ); diff --git a/tests/setup-components.ts b/tests/setup-components.ts index 2ff490a..ed9b229 100644 --- a/tests/setup-components.ts +++ b/tests/setup-components.ts @@ -125,8 +125,7 @@ jest.mock("ink-spinner", () => ({ default: () => null, })); -// Mock ink-big-text and ink-gradient (these cause ESM issues) -jest.mock("ink-big-text", () => ({ __esModule: true, default: () => null })); +// Mock ink-gradient (causes ESM issues) jest.mock("ink-gradient", () => ({ __esModule: true, default: () => null })); // Note: We do NOT mock 'ink' - we use ink-testing-library which needs real ink @@ -421,7 +420,7 @@ jest.mock("../src/utils/exec.ts", () => ({ execCommand: jest.fn(), })); -// Mock Banner component (uses ink-big-text which is ESM) +// Mock Banner component (uses ink-gradient which is ESM) jest.mock("../src/components/Banner.tsx", () => ({ __esModule: true, Banner: () => null, diff --git a/tests/setup-router.ts b/tests/setup-router.ts index 954eaf7..22c5091 100644 --- a/tests/setup-router.ts +++ b/tests/setup-router.ts @@ -126,8 +126,7 @@ jest.mock("ink-spinner", () => ({ default: () => null, })); -// Mock ink-big-text and ink-gradient (these cause ESM issues) -jest.mock("ink-big-text", () => ({ __esModule: true, default: () => null })); +// Mock ink-gradient (causes ESM issues) jest.mock("ink-gradient", () => ({ __esModule: true, default: () => null })); // Note: We do NOT mock 'ink' - we use ink-testing-library which needs real ink @@ -399,7 +398,7 @@ jest.mock("../src/utils/exec.ts", () => ({ execCommand: jest.fn(), })); -// Mock Banner component (uses ink-big-text which is ESM) +// Mock Banner component (uses ink-gradient which is ESM) jest.mock("../src/components/Banner.tsx", () => ({ __esModule: true, Banner: () => null, diff --git a/tests/setup.ts b/tests/setup.ts index fad235b..6d19f0f 100644 --- a/tests/setup.ts +++ b/tests/setup.ts @@ -64,7 +64,6 @@ jest.mock("ink", () => ({ })); // Mock ESM-only Ink dependencies so Jest doesn't parse their ESM bundles -jest.mock("ink-big-text", () => ({ __esModule: true, default: () => null })); jest.mock("ink-gradient", () => ({ __esModule: true, default: () => null })); // Mock app UI components that import Ink deps, to avoid pulling in ESM from node_modules