diff --git a/.github/workflows/chained_e2e.yml b/.github/workflows/chained_e2e.yml index 158a88382..24f006477 100644 --- a/.github/workflows/chained_e2e.yml +++ b/.github/workflows/chained_e2e.yml @@ -1,10 +1,6 @@ name: 'Testing: E2E (Chained)' on: - push: - branches: - - 'main' - merge_group: workflow_run: workflows: ['Trigger E2E'] types: ['completed'] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index dd9e83ea4..551bd7e8b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,487 +1,44 @@ -name: 'Testing: CI' +name: 'Build' on: - push: - branches: - - 'main' - - 'release/**' - pull_request: - branches: - - 'main' - - 'release/**' - merge_group: workflow_dispatch: inputs: - branch_ref: - description: 'Branch to run on' - required: true + ref: + description: 'Branch, tag, or SHA to build.' + required: false default: 'main' type: 'string' -concurrency: - group: '${{ github.workflow }}-${{ github.head_ref || github.ref }}' - cancel-in-progress: |- - ${{ github.ref != 'refs/heads/main' && !startsWith(github.ref, 'refs/heads/release/') }} - permissions: - checks: 'write' contents: 'read' - statuses: 'write' -defaults: - run: - shell: 'bash' +concurrency: + group: '${{ github.workflow }}-${{ github.event.inputs.ref || github.ref }}' + cancel-in-progress: true jobs: - merge_queue_skipper: - permissions: 'read-all' - name: 'Merge Queue Skipper' - runs-on: 'jiminy-cli-ubuntu-16-core' - if: "github.repository == 'Plaer1/jiminy-cli'" - outputs: - skip: '${{ steps.merge-queue-ci-skipper.outputs.skip-check }}' - steps: - - id: 'merge-queue-ci-skipper' - uses: 'cariad-tech/merge-queue-ci-skipper@1032489e59437862c90a08a2c92809c903883772' # ratchet:cariad-tech/merge-queue-ci-skipper@main - with: - secret: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT }}' - - lint: - name: 'Lint' - runs-on: 'jiminy-cli-ubuntu-16-core' - needs: 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && needs.merge_queue_skipper.outputs.skip == 'false'" - env: - GEMINI_LINT_TEMP_DIR: '${{ github.workspace }}/.jiminy-linters' - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - with: - ref: '${{ github.event.inputs.branch_ref || github.ref }}' - fetch-depth: 0 - - - name: 'Set up Node.js' - uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4.4.0 - with: - node-version-file: '.nvmrc' - cache: 'npm' - - - name: 'Cache Linters' - uses: 'actions/cache@v4' - with: - path: '${{ env.GEMINI_LINT_TEMP_DIR }}' - key: "${{ runner.os }}-${{ runner.arch }}-linters-${{ hashFiles('scripts/lint.js') }}" - - - name: 'Install dependencies' - run: 'npm ci' - - - name: 'Cache ESLint' - uses: 'actions/cache@v4' - with: - path: '.eslintcache' - key: "${{ runner.os }}-eslint-${{ hashFiles('package-lock.json', 'eslint.config.js') }}" - - - name: 'Validate NOTICES.txt' - run: 'git diff --exit-code packages/vscode-ide-companion/NOTICES.txt' - - - name: 'Check lockfile' - run: 'npm run check:lockfile' - - - name: 'Install linters' - run: 'node scripts/lint.js --setup' - - - name: 'Run ESLint' - run: 'node scripts/lint.js --eslint' - - - name: 'Run actionlint' - run: 'node scripts/lint.js --actionlint' - - - name: 'Run shellcheck' - run: 'node scripts/lint.js --shellcheck' - - - name: 'Run yamllint' - run: 'node scripts/lint.js --yamllint' - - - name: 'Run Prettier' - run: 'node scripts/lint.js --prettier' - - - name: 'Build docs prerequisites' - run: 'npm run predocs:settings' - - - name: 'Verify settings docs' - run: 'npm run docs:settings -- --check' - - - name: 'Run sensitive keyword linter' - run: 'node scripts/lint.js --sensitive-keywords' - - link_checker: - name: 'Link Checker' + build: runs-on: 'ubuntu-latest' - if: "github.repository == 'Plaer1/jiminy-cli'" - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - - name: 'Link Checker' - uses: 'lycheeverse/lychee-action@885c65f3dc543b57c898c8099f4e08c8afd178a2' # ratchet: lycheeverse/lychee-action@v2.6.1 - with: - args: '--verbose --accept 200,503 ./**/*.md' - fail: true - test_linux: - name: 'Test (Linux) - ${{ matrix.node-version }}, ${{ matrix.shard }}' - runs-on: 'jiminy-cli-ubuntu-16-core' - needs: - - 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && needs.merge_queue_skipper.outputs.skip == 'false'" - permissions: - contents: 'read' - checks: 'write' - pull-requests: 'write' - strategy: - matrix: - node-version: - - '20.x' - - '22.x' - - '24.x' - shard: - - 'cli' - - 'others' steps: - name: 'Checkout' uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - - - name: 'Set up Node.js ${{ matrix.node-version }}' - uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4 - with: - node-version: '${{ matrix.node-version }}' - cache: 'npm' - - - name: 'Build project' - run: 'npm run build' - - - name: 'Install dependencies for testing' - run: 'npm ci' - - - name: 'Run tests and generate reports' - env: - NO_COLOR: true - run: | - if [[ "${{ matrix.shard }}" == "cli" ]]; then - npm run test:ci --workspace @plaer1/jiminy-cli - else - # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @plaer1/jiminy-cli-core --workspace @plaer1/jiminy-cli-a2a-server --workspace jiminy-cli-vscode-ide-companion --workspace @plaer1/jiminy-cli-test-utils --if-present -- --coverage.enabled=false - npm run test:scripts - fi - - - name: 'Bundle' - run: 'npm run bundle' - - - name: 'Smoke test bundle' - run: 'node ./bundle/jiminy.js --version' - - - name: 'Smoke test npx installation' - run: | - # 1. Package the project into a tarball - TARBALL=$(npm pack | tail -n 1) - - # 2. Move to a fresh directory for isolation - mkdir -p ../smoke-test-dir - mv "$TARBALL" ../smoke-test-dir/ - cd ../smoke-test-dir - - # 3. Run npx from the tarball - npx "./$TARBALL" --version - - - name: 'Wait for file system sync' - run: 'sleep 2' - - - name: 'Publish Test Report (for non-forks)' - if: |- - ${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }} - uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2 with: - name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})' - path: 'packages/*/junit.xml' - reporter: 'java-junit' - fail-on-error: 'false' + ref: '${{ github.event.inputs.ref || github.ref }}' - - name: 'Upload Test Results Artifact (for forks)' - if: |- - ${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} - uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4 - with: - name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}' - path: 'packages/*/junit.xml' - - test_mac: - name: 'Test (Mac) - ${{ matrix.node-version }}, ${{ matrix.shard }}' - runs-on: 'macos-latest' - needs: - - 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && needs.merge_queue_skipper.outputs.skip == 'false'" - permissions: - contents: 'read' - checks: 'write' - pull-requests: 'write' - continue-on-error: true - strategy: - matrix: - node-version: - - '20.x' - - '22.x' - - '24.x' - shard: - - 'cli' - - 'others' - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - - - name: 'Set up Node.js ${{ matrix.node-version }}' - uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4 - with: - node-version: '${{ matrix.node-version }}' - cache: 'npm' - - - name: 'Build project' - run: 'npm run build' - - - name: 'Install dependencies for testing' - run: 'npm ci' - - - name: 'Run tests and generate reports' - env: - NO_COLOR: true - run: | - if [[ "${{ matrix.shard }}" == "cli" ]]; then - npm run test:ci --workspace @plaer1/jiminy-cli -- --coverage.enabled=false - else - # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @plaer1/jiminy-cli-core --workspace @plaer1/jiminy-cli-a2a-server --workspace jiminy-cli-vscode-ide-companion --workspace @plaer1/jiminy-cli-test-utils --if-present -- --coverage.enabled=false - npm run test:scripts - fi - - - name: 'Bundle' - run: 'npm run bundle' - - - name: 'Smoke test bundle' - run: 'node ./bundle/jiminy.js --version' - - - name: 'Smoke test npx installation' - run: | - # 1. Package the project into a tarball - TARBALL=$(npm pack | tail -n 1) - - # 2. Move to a fresh directory for isolation - mkdir -p ../smoke-test-dir - mv "$TARBALL" ../smoke-test-dir/ - cd ../smoke-test-dir - - # 3. Run npx from the tarball - npx "./$TARBALL" --version - - - name: 'Wait for file system sync' - run: 'sleep 2' - - - name: 'Publish Test Report (for non-forks)' - if: |- - ${{ always() && (github.event.pull_request.head.repo.full_name == github.repository) }} - uses: 'dorny/test-reporter@dc3a92680fcc15842eef52e8c4606ea7ce6bd3f3' # ratchet:dorny/test-reporter@v2 - with: - name: 'Test Results (Node ${{ runner.os }}, ${{ matrix.node-version }}, ${{ matrix.shard }})' - path: 'packages/*/junit.xml' - reporter: 'java-junit' - fail-on-error: 'false' - - - name: 'Upload Test Results Artifact (for forks)' - if: |- - ${{ always() && (github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository) }} - uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4 - with: - name: 'test-results-fork-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}' - path: 'packages/*/junit.xml' - - - name: 'Upload coverage reports' - if: |- - ${{ always() }} - uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4 - with: - name: 'coverage-reports-${{ runner.os }}-${{ matrix.node-version }}-${{ matrix.shard }}' - path: 'packages/*/coverage' - - codeql: - name: 'CodeQL' - runs-on: 'jiminy-cli-ubuntu-16-core' - needs: 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && needs.merge_queue_skipper.outputs.skip == 'false'" - permissions: - actions: 'read' - contents: 'read' - security-events: 'write' - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - with: - ref: '${{ github.event.inputs.branch_ref || github.ref }}' - - - name: 'Initialize CodeQL' - uses: 'github/codeql-action/init@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/init@v3 - with: - languages: 'javascript' - - - name: 'Perform CodeQL Analysis' - uses: 'github/codeql-action/analyze@df559355d593797519d70b90fc8edd5db049e7a2' # ratchet:github/codeql-action/analyze@v3 - - # Check for changes in bundle size. - bundle_size: - name: 'Check Bundle Size' - needs: 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && github.event_name == 'pull_request' && needs.merge_queue_skipper.outputs.skip == 'false'" - runs-on: 'jiminy-cli-ubuntu-16-core' - permissions: - contents: 'read' # For checkout - pull-requests: 'write' # For commenting - - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - with: - ref: '${{ github.event.inputs.branch_ref || github.ref }}' - fetch-depth: 1 - - - uses: 'preactjs/compressed-size-action@946a292cd35bd1088e0d7eb92b69d1a8d5b5d76a' - with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' - pattern: './bundle/**/*.{js,sb}' - minimum-change-threshold: '1000' - compression: 'none' - clean-script: 'clean' - - test_windows: - name: 'Slow Test - Win - ${{ matrix.shard }}' - runs-on: 'jiminy-cli-windows-16-core' - needs: 'merge_queue_skipper' - if: "github.repository == 'Plaer1/jiminy-cli' && needs.merge_queue_skipper.outputs.skip == 'false'" - timeout-minutes: 60 - strategy: - matrix: - shard: - - 'cli' - - 'others' - - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5 - with: - ref: '${{ github.event.inputs.branch_ref || github.ref }}' - - - name: 'Set up Node.js 20.x' + - name: 'Set up Node.js' uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4 with: - node-version: '20.x' + node-version-file: '.nvmrc' cache: 'npm' - - name: 'Configure Windows Defender exclusions' - run: | - Add-MpPreference -ExclusionPath $env:GITHUB_WORKSPACE -Force - Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\node_modules" -Force - Add-MpPreference -ExclusionPath "$env:GITHUB_WORKSPACE\packages" -Force - Add-MpPreference -ExclusionPath "$env:TEMP" -Force - shell: 'pwsh' - - - name: 'Configure npm for Windows performance' - run: | - npm config set progress false - npm config set audit false - npm config set fund false - npm config set loglevel error - npm config set maxsockets 32 - npm config set registry https://registry.npmjs.org/ - shell: 'pwsh' - - name: 'Install dependencies' run: 'npm ci' - shell: 'pwsh' - - name: 'Build project' + - name: 'Build workspaces' run: 'npm run build' - shell: 'pwsh' - env: - NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256' - UV_THREADPOOL_SIZE: '32' - NODE_ENV: 'production' - - - name: 'Run tests and generate reports' - env: - GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}' - NO_COLOR: true - NODE_OPTIONS: '--max-old-space-size=32768 --max-semi-space-size=256' - UV_THREADPOOL_SIZE: '32' - NODE_ENV: 'test' - run: | - if ("${{ matrix.shard }}" -eq "cli") { - npm run test:ci --workspace @plaer1/jiminy-cli -- --coverage.enabled=false - } else { - # Explicitly list non-cli packages to ensure they are sharded correctly - npm run test:ci --workspace @plaer1/jiminy-cli-core --workspace @plaer1/jiminy-cli-a2a-server --workspace jiminy-cli-vscode-ide-companion --workspace @plaer1/jiminy-cli-test-utils --if-present -- --coverage.enabled=false - npm run test:scripts - } - shell: 'pwsh' - - name: 'Bundle' + - name: 'Bundle CLI' run: 'npm run bundle' - shell: 'pwsh' - name: 'Smoke test bundle' run: 'node ./bundle/jiminy.js --version' - shell: 'pwsh' - - - name: 'Smoke test npx installation' - run: | - # 1. Package the project into a tarball - $PACK_OUTPUT = npm pack - $TARBALL = $PACK_OUTPUT[-1] - - # 2. Move to a fresh directory for isolation - New-Item -ItemType Directory -Force -Path ../smoke-test-dir - Move-Item $TARBALL ../smoke-test-dir/ - Set-Location ../smoke-test-dir - - # 3. Run npx from the tarball - npx "./$TARBALL" --version - shell: 'pwsh' - - ci: - name: 'CI' - if: "github.repository == 'Plaer1/jiminy-cli' && always()" - needs: - - 'lint' - - 'link_checker' - - 'test_linux' - - 'test_mac' - - 'test_windows' - - 'codeql' - - 'bundle_size' - runs-on: 'jiminy-cli-ubuntu-16-core' - steps: - - name: 'Check all job results' - run: | - if [[ (${NEEDS_LINT_RESULT} != 'success' && ${NEEDS_LINT_RESULT} != 'skipped') || \ - (${NEEDS_LINK_CHECKER_RESULT} != 'success' && ${NEEDS_LINK_CHECKER_RESULT} != 'skipped') || \ - (${NEEDS_TEST_LINUX_RESULT} != 'success' && ${NEEDS_TEST_LINUX_RESULT} != 'skipped') || \ - (${NEEDS_TEST_MAC_RESULT} != 'success' && ${NEEDS_TEST_MAC_RESULT} != 'skipped') || \ - (${NEEDS_TEST_WINDOWS_RESULT} != 'success' && ${NEEDS_TEST_WINDOWS_RESULT} != 'skipped') || \ - (${NEEDS_CODEQL_RESULT} != 'success' && ${NEEDS_CODEQL_RESULT} != 'skipped') || \ - (${NEEDS_BUNDLE_SIZE_RESULT} != 'success' && ${NEEDS_BUNDLE_SIZE_RESULT} != 'skipped') ]]; then - echo "One or more CI jobs failed." - exit 1 - fi - echo "All CI jobs passed!" - env: - NEEDS_LINT_RESULT: '${{ needs.lint.result }}' - NEEDS_LINK_CHECKER_RESULT: '${{ needs.link_checker.result }}' - NEEDS_TEST_LINUX_RESULT: '${{ needs.test_linux.result }}' - NEEDS_TEST_MAC_RESULT: '${{ needs.test_mac.result }}' - NEEDS_TEST_WINDOWS_RESULT: '${{ needs.test_windows.result }}' - NEEDS_CODEQL_RESULT: '${{ needs.codeql.result }}' - NEEDS_BUNDLE_SIZE_RESULT: '${{ needs.bundle_size.result }}' diff --git a/.github/workflows/docs-page-action.yml b/.github/workflows/docs-page-action.yml index 9dc26bafc..4987ee5ac 100644 --- a/.github/workflows/docs-page-action.yml +++ b/.github/workflows/docs-page-action.yml @@ -1,8 +1,6 @@ name: 'Deploy GitHub Pages' on: - push: - tags: 'v*' workflow_dispatch: permissions: diff --git a/.github/workflows/evals-nightly.yml b/.github/workflows/evals-nightly.yml index d236289b8..692b90711 100644 --- a/.github/workflows/evals-nightly.yml +++ b/.github/workflows/evals-nightly.yml @@ -1,8 +1,6 @@ name: 'Evals: Nightly' on: - schedule: - - cron: '0 1 * * *' # Runs at 1 AM every day workflow_dispatch: inputs: run_all: diff --git a/.github/workflows/jiminy-scheduled-stale-issue-closer.yml b/.github/workflows/jiminy-scheduled-stale-issue-closer.yml deleted file mode 100644 index 72b77212e..000000000 --- a/.github/workflows/jiminy-scheduled-stale-issue-closer.yml +++ /dev/null @@ -1,159 +0,0 @@ -name: '🔒 Jiminy Scheduled Stale Issue Closer' - -on: - schedule: - - cron: '0 0 * * 0' # Every Sunday at midnight UTC - workflow_dispatch: - inputs: - dry_run: - description: 'Run in dry-run mode (no changes applied)' - required: false - default: false - type: 'boolean' - -concurrency: - group: '${{ github.workflow }}' - cancel-in-progress: true - -defaults: - run: - shell: 'bash' - -jobs: - close-stale-issues: - if: "github.repository == 'Plaer1/jiminy-cli'" - runs-on: 'ubuntu-latest' - permissions: - issues: 'write' - steps: - - name: 'Generate GitHub App Token' - id: 'generate_token' - uses: 'actions/create-github-app-token@v2' - with: - app-id: '${{ secrets.APP_ID }}' - private-key: '${{ secrets.PRIVATE_KEY }}' - permission-issues: 'write' - - - name: 'Process Stale Issues' - uses: 'actions/github-script@v7' - env: - DRY_RUN: '${{ inputs.dry_run }}' - with: - github-token: '${{ steps.generate_token.outputs.token }}' - script: | - const dryRun = process.env.DRY_RUN === 'true'; - if (dryRun) { - core.info('DRY RUN MODE ENABLED: No changes will be applied.'); - } - const batchLabel = 'Stale'; - - const threeMonthsAgo = new Date(); - threeMonthsAgo.setMonth(threeMonthsAgo.getMonth() - 3); - - const tenDaysAgo = new Date(); - tenDaysAgo.setDate(tenDaysAgo.getDate() - 10); - - core.info(`Cutoff date for creation: ${threeMonthsAgo.toISOString()}`); - core.info(`Cutoff date for updates: ${tenDaysAgo.toISOString()}`); - - const query = `repo:${context.repo.owner}/${context.repo.repo} is:issue is:open created:<${threeMonthsAgo.toISOString()}`; - core.info(`Searching with query: ${query}`); - - const itemsToCheck = await github.paginate(github.rest.search.issuesAndPullRequests, { - q: query, - sort: 'created', - order: 'asc', - per_page: 100 - }); - - core.info(`Found ${itemsToCheck.length} open issues to check.`); - - let processedCount = 0; - - for (const issue of itemsToCheck) { - const createdAt = new Date(issue.created_at); - const updatedAt = new Date(issue.updated_at); - const reactionCount = issue.reactions.total_count; - - // Basic thresholds - if (reactionCount >= 5) { - continue; - } - - // Skip if it has a maintainer, help wanted, or Public Roadmap label - const rawLabels = issue.labels.map((l) => l.name); - const lowercaseLabels = rawLabels.map((l) => l.toLowerCase()); - if ( - lowercaseLabels.some((l) => l.includes('maintainer')) || - lowercaseLabels.includes('help wanted') || - rawLabels.includes('🗓️ Public Roadmap') - ) { - continue; - } - - let isStale = updatedAt < tenDaysAgo; - - // If apparently active, check if it's only bot activity - if (!isStale) { - try { - const comments = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - per_page: 100, - sort: 'created', - direction: 'desc' - }); - - const lastHumanComment = comments.data.find(comment => comment.user.type !== 'Bot'); - if (lastHumanComment) { - isStale = new Date(lastHumanComment.created_at) < tenDaysAgo; - } else { - // No human comments. Check if creator is human. - if (issue.user.type !== 'Bot') { - isStale = createdAt < tenDaysAgo; - } else { - isStale = true; // Bot created, only bot comments - } - } - } catch (error) { - core.warning(`Failed to fetch comments for issue #${issue.number}: ${error.message}`); - continue; - } - } - - if (isStale) { - processedCount++; - const message = `Closing stale issue #${issue.number}: "${issue.title}" (${issue.html_url})`; - core.info(message); - - if (!dryRun) { - // Add label - await github.rest.issues.addLabels({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - labels: [batchLabel] - }); - - // Add comment - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - body: 'Hello! As part of our effort to keep our backlog manageable and focus on the most active issues, we are tidying up older reports.\n\nIt looks like this issue hasn\'t been active for a while, so we are closing it for now. However, if you are still experiencing this bug on the latest stable build, please feel free to comment on this issue or create a new one with updated details.\n\nThank you for your contribution!' - }); - - // Close issue - await github.rest.issues.update({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issue.number, - state: 'closed', - state_reason: 'not_planned' - }); - } - } - } - - core.info(`\nTotal issues processed: ${processedCount}`); diff --git a/.github/workflows/jiminy-scheduled-stale-pr-closer.yml b/.github/workflows/jiminy-scheduled-stale-pr-closer.yml deleted file mode 100644 index c8e8fa4f7..000000000 --- a/.github/workflows/jiminy-scheduled-stale-pr-closer.yml +++ /dev/null @@ -1,254 +0,0 @@ -name: 'Jiminy Scheduled Stale PR Closer' - -on: - schedule: - - cron: '0 2 * * *' # Every day at 2 AM UTC - pull_request: - types: ['opened', 'edited'] - workflow_dispatch: - inputs: - dry_run: - description: 'Run in dry-run mode' - required: false - default: false - type: 'boolean' - -jobs: - close-stale-prs: - if: "github.repository == 'Plaer1/jiminy-cli'" - runs-on: 'ubuntu-latest' - permissions: - pull-requests: 'write' - issues: 'write' - steps: - - name: 'Generate GitHub App Token' - id: 'generate_token' - env: - APP_ID: '${{ secrets.APP_ID }}' - if: |- - ${{ env.APP_ID != '' }} - uses: 'actions/create-github-app-token@v2' - with: - app-id: '${{ secrets.APP_ID }}' - private-key: '${{ secrets.PRIVATE_KEY }}' - - - name: 'Process Stale PRs' - uses: 'actions/github-script@v7' - env: - DRY_RUN: '${{ inputs.dry_run }}' - with: - github-token: '${{ steps.generate_token.outputs.token || secrets.GITHUB_TOKEN }}' - script: | - const dryRun = process.env.DRY_RUN === 'true'; - const fourteenDaysAgo = new Date(); - fourteenDaysAgo.setDate(fourteenDaysAgo.getDate() - 14); - const thirtyDaysAgo = new Date(); - thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30); - - // 1. Fetch maintainers for verification - let maintainerLogins = new Set(); - const teams = ['jiminy-cli-maintainers', 'jiminy-cli-askmode-approvers', 'jiminy-cli-docs']; - - for (const team_slug of teams) { - try { - const members = await github.paginate(github.rest.teams.listMembersInOrg, { - org: context.repo.owner, - team_slug: team_slug - }); - for (const m of members) maintainerLogins.add(m.login.toLowerCase()); - core.info(`Successfully fetched ${members.length} team members from ${team_slug}`); - } catch (e) { - // Silently skip if permissions are insufficient; we will rely on author_association - core.debug(`Skipped team fetch for ${team_slug}: ${e.message}`); - } - } - - const isMaintainer = async (login, assoc) => { - // Reliably identify maintainers using authorAssociation (provided by GitHub) - // and organization membership (if available). - const isTeamMember = maintainerLogins.has(login.toLowerCase()); - const isRepoMaintainer = ['OWNER', 'MEMBER', 'COLLABORATOR'].includes(assoc); - - if (isTeamMember || isRepoMaintainer) return true; - - // Fallback: Check if user belongs to the 'google' or 'googlers' orgs (requires permission) - try { - const orgs = ['googlers', 'google']; - for (const org of orgs) { - try { - await github.rest.orgs.checkMembershipForUser({ org: org, username: login }); - return true; - } catch (e) { - if (e.status !== 404) throw e; - } - } - } catch (e) { - // Gracefully ignore failures here - } - - return false; - }; - - // 2. Fetch all open PRs - let prs = []; - if (context.eventName === 'pull_request') { - const { data: pr } = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.payload.pull_request.number - }); - prs = [pr]; - } else { - prs = await github.paginate(github.rest.pulls.list, { - owner: context.repo.owner, - repo: context.repo.repo, - state: 'open', - per_page: 100 - }); - } - - for (const pr of prs) { - const maintainerPr = await isMaintainer(pr.user.login, pr.author_association); - const isBot = pr.user.type === 'Bot' || pr.user.login.endsWith('[bot]'); - if (maintainerPr || isBot) continue; - - // Helper: Fetch labels and linked issues via GraphQL - const prDetailsQuery = `query($owner:String!, $repo:String!, $number:Int!) { - repository(owner:$owner, name:$repo) { - pullRequest(number:$number) { - closingIssuesReferences(first: 10) { - nodes { - number - labels(first: 20) { - nodes { name } - } - } - } - } - } - }`; - - let linkedIssues = []; - try { - const res = await github.graphql(prDetailsQuery, { - owner: context.repo.owner, repo: context.repo.repo, number: pr.number - }); - linkedIssues = res.repository.pullRequest.closingIssuesReferences.nodes; - } catch (e) { - core.warning(`GraphQL fetch failed for PR #${pr.number}: ${e.message}`); - } - - // Check for mentions in body as fallback (regex) - const body = pr.body || ''; - const mentionRegex = /(?:#|https:\/\/github\.com\/[^\/]+\/[^\/]+\/issues\/)(\d+)/i; - const matches = body.match(mentionRegex); - if (matches && linkedIssues.length === 0) { - const issueNumber = parseInt(matches[1]); - try { - const { data: issue } = await github.rest.issues.get({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: issueNumber - }); - linkedIssues = [{ number: issueNumber, labels: { nodes: issue.labels.map(l => ({ name: l.name })) } }]; - } catch (e) {} - } - - // 3. Enforcement Logic - const prLabels = pr.labels.map(l => l.name.toLowerCase()); - const hasHelpWanted = prLabels.includes('help wanted') || - linkedIssues.some(issue => issue.labels.nodes.some(l => l.name.toLowerCase() === 'help wanted')); - - const hasMaintainerOnly = prLabels.includes('🔒 maintainer only') || - linkedIssues.some(issue => issue.labels.nodes.some(l => l.name.toLowerCase() === '🔒 maintainer only')); - - const hasLinkedIssue = linkedIssues.length > 0; - - // Closure Policy: No help-wanted label = Close after 14 days - if (pr.state === 'open' && !hasHelpWanted && !hasMaintainerOnly) { - const prCreatedAt = new Date(pr.created_at); - - // We give a 14-day grace period for non-help-wanted PRs to be manually reviewed/labeled by an EM - if (prCreatedAt > fourteenDaysAgo) { - core.info(`PR #${pr.number} is new and lacks 'help wanted'. Giving 14-day grace period for EM review.`); - continue; - } - - core.info(`PR #${pr.number} is older than 14 days and lacks 'help wanted' association. Closing.`); - if (!dryRun) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: "Hi there! Thank you for your interest in contributing to Jiminy CLI. \n\nTo ensure we maintain high code quality and focus on our prioritized roadmap, we have updated our contribution policy (see [Discussion #17383](https://github.com/Plaer1/jiminy-cli/discussions/17383)). \n\n**We only *guarantee* review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.** All other community pull requests are subject to closure after 14 days if they do not align with our current focus areas. For this reason, we strongly recommend that contributors only submit pull requests against issues explicitly labeled as **'help-wanted'**. \n\nThis pull request is being closed as it has been open for 14 days without a 'help wanted' designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding and for being part of our community!" - }); - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.number, - state: 'closed' - }); - } - continue; - } - - // Also check for linked issue even if it has help wanted (redundant but safe) - if (pr.state === 'open' && !hasLinkedIssue) { - // Already covered by hasHelpWanted check above, but good for future-proofing - continue; - } - - // 4. Staleness Check (Scheduled only) - if (pr.state === 'open' && context.eventName !== 'pull_request') { - // Skip PRs that were created less than 30 days ago - they cannot be stale yet - const prCreatedAt = new Date(pr.created_at); - if (prCreatedAt > thirtyDaysAgo) continue; - - let lastActivity = new Date(pr.created_at); - try { - const reviews = await github.paginate(github.rest.pulls.listReviews, { - owner: context.repo.owner, repo: context.repo.repo, pull_number: pr.number - }); - for (const r of reviews) { - if (await isMaintainer(r.user.login, r.author_association)) { - const d = new Date(r.submitted_at || r.updated_at); - if (d > lastActivity) lastActivity = d; - } - } - const comments = await github.paginate(github.rest.issues.listComments, { - owner: context.repo.owner, repo: context.repo.repo, issue_number: pr.number - }); - for (const c of comments) { - if (await isMaintainer(c.user.login, c.author_association)) { - const d = new Date(c.updated_at); - if (d > lastActivity) lastActivity = d; - } - } - } catch (e) {} - - if (lastActivity < thirtyDaysAgo) { - const labels = pr.labels.map(l => l.name.toLowerCase()); - const isProtected = labels.includes('help wanted') || labels.includes('🔒 maintainer only'); - if (isProtected) { - core.info(`PR #${pr.number} is stale but has a protected label. Skipping closure.`); - continue; - } - - core.info(`PR #${pr.number} is stale (no maintainer activity for 30+ days). Closing.`); - if (!dryRun) { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: pr.number, - body: "Hi there! Thank you for your contribution. To keep our backlog manageable, we are closing pull requests that haven't seen maintainer activity for 30 days. If you're still working on this, please let us know!" - }); - await github.rest.pulls.update({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: pr.number, - state: 'closed' - }); - } - } - } - } diff --git a/.github/workflows/no-response.yml b/.github/workflows/no-response.yml deleted file mode 100644 index b4f0f4740..000000000 --- a/.github/workflows/no-response.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: 'No Response' - -# Run as a daily cron at 1:45 AM -on: - schedule: - - cron: '45 1 * * *' - workflow_dispatch: - -jobs: - no-response: - runs-on: 'ubuntu-latest' - if: |- - ${{ github.repository == 'Plaer1/jiminy-cli' }} - permissions: - issues: 'write' - pull-requests: 'write' - concurrency: - group: '${{ github.workflow }}-no-response' - cancel-in-progress: true - steps: - - uses: 'actions/stale@5bef64f19d7facfb25b37b414482c7164d639639' # ratchet:actions/stale@v9 - with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' - days-before-stale: -1 - days-before-close: 14 - stale-issue-label: 'status/need-information' - close-issue-message: >- - This issue was marked as needing more information and has not received a response in 14 days. - Closing it for now. If you still face this problem, feel free to reopen with more details. Thank you! - stale-pr-label: 'status/need-information' - close-pr-message: >- - This pull request was marked as needing more information and has had no updates in 14 days. - Closing it for now. You are welcome to reopen with the required info. Thanks for contributing! diff --git a/.github/workflows/release-nightly.yml b/.github/workflows/release-nightly.yml index f64266a45..4889c9414 100644 --- a/.github/workflows/release-nightly.yml +++ b/.github/workflows/release-nightly.yml @@ -1,8 +1,6 @@ name: 'Release: Nightly' on: - schedule: - - cron: '0 0 * * *' workflow_dispatch: inputs: dry_run: @@ -35,7 +33,7 @@ jobs: environment: "${{ github.event.inputs.environment || 'prod' }}" runs-on: 'ubuntu-latest' outputs: - dry_run: "${{ github.event_name == 'schedule' && 'false' || github.event.inputs.dry_run }}" + dry_run: '${{ github.event.inputs.dry_run }}' release_branch: '${{ steps.publish_release.outputs.release-branch }}' release_tag: '${{ steps.nightly_version.outputs.RELEASE_TAG }}' release_version: '${{ steps.nightly_version.outputs.RELEASE_VERSION }}' @@ -62,7 +60,7 @@ jobs: run: 'npm ci' - name: 'Run Tests' - if: "${{ github.event_name == 'schedule' || github.event.inputs.force_skip_tests == 'false' }}" + if: "${{ github.event.inputs.force_skip_tests == 'false' }}" uses: './.github/actions/run-tests' with: jiminy_api_key: '${{ secrets.GEMINI_API_KEY }}' @@ -101,7 +99,7 @@ jobs: release-tag: '${{ steps.nightly_version.outputs.RELEASE_TAG }}' release-channel: '${{ steps.nightly_version.outputs.RELEASE_CHANNEL }}' npm-tag: '${{ steps.nightly_version.outputs.RELEASE_CHANNEL }}' - dry-run: "${{ github.event_name == 'schedule' && 'false' || github.event.inputs.dry_run }}" + dry-run: '${{ github.event.inputs.dry_run }}' publish-npm: 'true' github-token: '${{ secrets.GEMINI_CLI_ROBOT_GITHUB_PAT || secrets.GITHUB_TOKEN }}' wombat-token-core: '${{ secrets.WOMBAT_TOKEN_CORE }}' diff --git a/.github/workflows/release-patch-2-trigger.yml b/.github/workflows/release-patch-2-trigger.yml index 5976816db..f212cf5a2 100644 --- a/.github/workflows/release-patch-2-trigger.yml +++ b/.github/workflows/release-patch-2-trigger.yml @@ -2,15 +2,10 @@ name: 'Release: Patch (2) Trigger' run-name: >- Release Patch (2) Trigger | - ${{ github.event.pull_request.number && format('PR #{0}', github.event.pull_request.number) || 'Manual' }} | - ${{ github.event.pull_request.head.ref || github.event.inputs.ref }} + Manual | + ${{ github.event.inputs.ref }} on: - pull_request: - types: - - 'closed' - branches: - - 'release/**' workflow_dispatch: inputs: ref: @@ -53,7 +48,7 @@ on: jobs: trigger-patch-release: - if: "(github.event_name == 'pull_request' && github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'hotfix/')) || github.event_name == 'workflow_dispatch'" + if: "github.event_name == 'workflow_dispatch'" runs-on: 'ubuntu-latest' environment: "${{ github.event.inputs.environment || 'prod' }}" permissions: @@ -79,8 +74,8 @@ jobs: - name: 'Trigger Patch Release' env: GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - HEAD_REF: "${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.event.inputs.ref }}" - PR_BODY: "${{ github.event_name == 'pull_request' && github.event.pull_request.body || '' }}" + HEAD_REF: '${{ github.event.inputs.ref }}' + PR_BODY: '' WORKFLOW_ID: '${{ github.event.inputs.workflow_id }}' GITHUB_REPOSITORY_OWNER: '${{ github.repository_owner }}' GITHUB_REPOSITORY_NAME: '${{ github.event.repository.name }}' diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml deleted file mode 100644 index 8f74e3b4e..000000000 --- a/.github/workflows/smoke-test.yml +++ /dev/null @@ -1,51 +0,0 @@ -name: 'On Merge Smoke Test' - -on: - push: - branches: - - 'main' - - 'release/**' - workflow_dispatch: - inputs: - ref: - description: 'The branch, tag, or SHA to test on.' - required: false - type: 'string' - default: 'main' - dry-run: - description: 'Run a dry-run of the smoke test; No bug will be created' - required: true - type: 'boolean' - default: true - -jobs: - smoke-test: - if: "github.repository == 'Plaer1/jiminy-cli'" - runs-on: 'ubuntu-latest' - permissions: - contents: 'write' - packages: 'write' - issues: 'write' - steps: - - name: 'Checkout' - uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' - with: - ref: '${{ github.event.inputs.ref || github.sha }}' - fetch-depth: 0 - - name: 'Install Dependencies' - run: 'npm ci' - - name: 'Build bundle' - run: 'npm run bundle' - - name: 'Smoke test bundle' - run: 'node ./bundle/jiminy.js --version' - - name: 'Create Issue on Failure' - if: '${{ failure() && github.event.inputs.dry-run == false }}' - env: - GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' - DETAILS_URL: '${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}' - REF: '${{ github.event.inputs.ref }}' - run: | - gh issue create \ - --title 'Smoke test failed on ${REF} @ $(date +'%Y-%m-%d')' \ - --body 'Smoke test build failed. See the full run for details: ${DETAILS_URL}' \ - --label 'priority/p0' diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml deleted file mode 100644 index 6523aeee7..000000000 --- a/.github/workflows/stale.yml +++ /dev/null @@ -1,44 +0,0 @@ -name: 'Mark stale issues and pull requests' - -# Run as a daily cron at 1:30 AM -on: - schedule: - - cron: '30 1 * * *' - workflow_dispatch: - -jobs: - stale: - strategy: - fail-fast: false - matrix: - runner: - - 'ubuntu-latest' # GitHub-hosted - runs-on: '${{ matrix.runner }}' - if: |- - ${{ github.repository == 'Plaer1/jiminy-cli' }} - permissions: - issues: 'write' - pull-requests: 'write' - concurrency: - group: '${{ github.workflow }}-stale' - cancel-in-progress: true - steps: - - uses: 'actions/stale@5bef64f19d7facfb25b37b414482c7164d639639' # ratchet:actions/stale@v9 - with: - repo-token: '${{ secrets.GITHUB_TOKEN }}' - stale-issue-message: >- - This issue has been automatically marked as stale due to 60 days of inactivity. - It will be closed in 14 days if no further activity occurs. - stale-pr-message: >- - This pull request has been automatically marked as stale due to 60 days of inactivity. - It will be closed in 14 days if no further activity occurs. - close-issue-message: >- - This issue has been closed due to 14 additional days of inactivity after being marked as stale. - If you believe this is still relevant, feel free to comment or reopen the issue. Thank you! - close-pr-message: >- - This pull request has been closed due to 14 additional days of inactivity after being marked as stale. - If this is still relevant, you are welcome to reopen or leave a comment. Thanks for contributing! - days-before-stale: 60 - days-before-close: 14 - exempt-issue-labels: 'pinned,security,🔒 maintainer only,help wanted,🗓️ Public Roadmap' - exempt-pr-labels: 'pinned,security,🔒 maintainer only,help wanted,🗓️ Public Roadmap' diff --git a/.github/workflows/trigger_e2e.yml b/.github/workflows/trigger_e2e.yml index 1c20c2d00..56325baef 100644 --- a/.github/workflows/trigger_e2e.yml +++ b/.github/workflows/trigger_e2e.yml @@ -5,13 +5,12 @@ on: inputs: repo_name: description: 'Repository name (e.g., owner/repo)' - required: false + required: true type: 'string' head_sha: description: 'SHA of the commit to test' - required: false + required: true type: 'string' - pull_request: jobs: save_repo_name: diff --git a/package-lock.json b/package-lock.json index a55ec0748..a2abf9aee 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,12 +11,23 @@ "packages/*" ], "dependencies": { + "clipboardy": "^4.0.0", + "command-exists": "^1.2.9", + "comment-json": "^4.2.5", + "dotenv": "^17.2.4", "ink": "npm:@jrichman/ink@6.6.3", + "ink-gradient": "^3.0.0", + "ink-spinner": "^5.0.0", "latest-version": "^9.0.0", + "lowlight": "^3.3.0", "node-fetch-native": "^1.6.7", + "prompts": "^2.4.2", "proper-lockfile": "^4.1.2", "punycode": "^2.3.1", - "simple-git": "^3.28.0" + "simple-git": "^3.28.0", + "tar": "^7.5.8", + "tinycolor2": "^1.6.0", + "tinygradient": "^1.1.5" }, "bin": { "jiminy": "bundle/jiminy.js" @@ -24,6 +35,7 @@ "devDependencies": { "@agentclientprotocol/sdk": "^0.16.1", "@octokit/rest": "^22.0.0", + "@types/command-exists": "^1.2.3", "@types/marked": "^5.0.2", "@types/mime-types": "^3.0.1", "@types/minimatch": "^5.1.2", @@ -32,8 +44,10 @@ "@types/proper-lockfile": "^4.1.4", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", + "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", "@types/ws": "^8.18.1", + "@types/yargs": "^17.0.33", "@vitest/coverage-v8": "^3.1.1", "@vitest/eslint-plugin": "^1.3.4", "cross-env": "^7.0.3", @@ -3790,6 +3804,13 @@ "assertion-error": "^2.0.1" } }, + "node_modules/@types/command-exists": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@types/command-exists/-/command-exists-1.2.3.tgz", + "integrity": "sha512-PpbaE2XWLaWYboXD6k70TcXO/OdOyyRFq5TVpmlUELNxdkkmXU9fkImNosmXU1DtsNrqdUgWd/nJQYXgwmtdXQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", @@ -3879,6 +3900,24 @@ "@types/node": "*" } }, + "node_modules/@types/gradient-string": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@types/gradient-string/-/gradient-string-1.1.6.tgz", + "integrity": "sha512-LkaYxluY4G5wR1M4AKQUal2q61Di1yVVCw42ImFTuaIoQVgmV0WP1xUaLB8zwb47mp82vWTpePI9JmrjEnJ7nQ==", + "license": "MIT", + "dependencies": { + "@types/tinycolor2": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz", + "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/html-to-text": { "version": "9.0.4", "resolved": "https://registry.npmjs.org/@types/html-to-text/-/html-to-text-9.0.4.tgz", @@ -4135,6 +4174,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/send": { "version": "0.17.5", "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz", @@ -4217,6 +4263,12 @@ "node": ">=8" } }, + "node_modules/@types/tinycolor2": { + "version": "1.4.6", + "resolved": "https://registry.npmjs.org/@types/tinycolor2/-/tinycolor2-1.4.6.tgz", + "integrity": "sha512-iEN8J0BoMnsWBqjVbWH/c0G0Hh7O21lpR2/+PrvAVgWdzL7eexIFm4JN/Wn10PTcmNdtS6U67r499mlWMXOxNw==", + "license": "MIT" + }, "node_modules/@types/tough-cookie": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.5.tgz", @@ -4229,6 +4281,12 @@ "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", "license": "MIT" }, + "node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", @@ -4239,6 +4297,23 @@ "@types/node": "*" } }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", @@ -5357,6 +5432,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "license": "MIT" + }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", @@ -6027,7 +6108,6 @@ "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -6044,7 +6124,6 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" @@ -6213,6 +6292,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cli-truncate": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-4.0.0.tgz", @@ -6302,6 +6393,145 @@ "node": ">= 12" } }, + "node_modules/clipboardy": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-4.0.0.tgz", + "integrity": "sha512-5mOlNS0mhX0707P2I0aZ2V/cmHUEO/fL7VFLqszkhUsxt7RwnmrInf/eEQKlf5GzvYeHIjT+Ov1HRfNmymlG0w==", + "license": "MIT", + "dependencies": { + "execa": "^8.0.1", + "is-wsl": "^3.1.0", + "is64bit": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/execa": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-8.0.1.tgz", + "integrity": "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.3", + "get-stream": "^8.0.1", + "human-signals": "^5.0.0", + "is-stream": "^3.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^5.1.0", + "onetime": "^6.0.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^3.0.0" + }, + "engines": { + "node": ">=16.17" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/clipboardy/node_modules/get-stream": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-8.0.1.tgz", + "integrity": "sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==", + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/human-signals": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-5.0.0.tgz", + "integrity": "sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.17.0" + } + }, + "node_modules/clipboardy/node_modules/is-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz", + "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==", + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/mimic-fn": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz", + "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/npm-run-path": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.3.0.tgz", + "integrity": "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/onetime": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz", + "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==", + "license": "MIT", + "dependencies": { + "mimic-fn": "^4.0.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clipboardy/node_modules/strip-final-newline": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz", + "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -6479,6 +6709,12 @@ "node": ">= 0.8" } }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==", + "license": "MIT" + }, "node_modules/commander": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", @@ -6489,6 +6725,19 @@ "node": ">=18" } }, + "node_modules/comment-json": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.6.2.tgz", + "integrity": "sha512-R2rze/hDX30uul4NZoIZ76ImSJLFxn/1/ZxtKC1L77y2X1k+yYu1joKbAtMA2Fg3hZrTOiw0I5mwVMo0cf250w==", + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/component-emitter": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.1.tgz", @@ -7177,6 +7426,15 @@ "dev": true, "license": "MIT" }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", @@ -7187,6 +7445,19 @@ "node": ">=0.10.0" } }, + "node_modules/devlop": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", + "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/devtools-protocol": { "version": "0.0.1581282", "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1581282.tgz", @@ -7295,6 +7566,18 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dotenv": { + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/dotenv-expand": { "version": "12.0.3", "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-12.0.3.tgz", @@ -9414,6 +9697,19 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/gradient-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/gradient-string/-/gradient-string-2.0.2.tgz", + "integrity": "sha512-rEDCuqUQ4tbD78TpzsMtt5OIf0cBCSDWSJtUDaF6JsAh+k0v9r++NzxNEG87oDZx9ZwGhD8DaezR2L/yrw0Jdw==", + "license": "MIT", + "dependencies": { + "chalk": "^4.1.2", + "tinygradient": "^1.1.5" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -9461,7 +9757,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -9541,6 +9836,15 @@ "devOptional": true, "license": "MIT" }, + "node_modules/highlight.js": { + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=12.0.0" + } + }, "node_modules/homedir-polyfill": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", @@ -9880,6 +10184,43 @@ } } }, + "node_modules/ink-gradient": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ink-gradient/-/ink-gradient-3.0.0.tgz", + "integrity": "sha512-OVyPBovBxE1tFcBhSamb+P1puqDP6pG3xFe2W9NiLgwUZd9RbcjBeR7twLbliUT9navrUstEf1ZcPKKvx71BsQ==", + "license": "MIT", + "dependencies": { + "@types/gradient-string": "^1.1.2", + "gradient-string": "^2.0.2", + "prop-types": "^15.8.1", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + }, + "peerDependencies": { + "ink": ">=4" + } + }, + "node_modules/ink-spinner": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/ink-spinner/-/ink-spinner-5.0.0.tgz", + "integrity": "sha512-EYEasbEjkqLGyPOUc8hBJZNuC5GvXGMLu0w5gdTNskPc7Izc5vO3tdQEYnzvshucyGCBXc86ig0ujXPMWaQCdA==", + "license": "MIT", + "dependencies": { + "cli-spinners": "^2.7.0" + }, + "engines": { + "node": ">=14.16" + }, + "peerDependencies": { + "ink": ">=4.0.0", + "react": ">=18.0.0" + } + }, "node_modules/ink/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", @@ -10524,6 +10865,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/is64bit": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is64bit/-/is64bit-2.0.0.tgz", + "integrity": "sha512-jv+8jaWCl0g2lSBkNSVXdzfBA0npK1HGC2KtWM9FumFRoGS94g3NbCCLVnCYHLjp4GrW2KZeeSTMo5ddtznmGw==", + "license": "MIT", + "dependencies": { + "system-architecture": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", @@ -10947,7 +11303,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -11383,7 +11738,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -11396,7 +11750,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/loupe": { @@ -11417,6 +11770,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lowlight": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz", + "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "devlop": "^1.0.0", + "highlight.js": "~11.11.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", @@ -11558,6 +11926,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -13134,11 +13508,23 @@ "node": ">=0.4.0" } }, + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", + "license": "MIT", + "dependencies": { + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -13564,7 +13950,6 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-reconciler": { @@ -14477,6 +14862,12 @@ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", "license": "MIT" }, + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", + "license": "MIT" + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -15147,6 +15538,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/system-architecture": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/system-architecture/-/system-architecture-0.1.0.tgz", + "integrity": "sha512-ulAk51I9UVUyJgxlv9M6lFot2WP3e7t8Kz9+IS6D4rVba1tR9kON+Ey69f+1R4Q8cd45Lod6a4IcJIxnzGc/zA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/systeminformation": { "version": "5.31.4", "resolved": "https://registry.npmjs.org/systeminformation/-/systeminformation-5.31.4.tgz", @@ -15277,6 +15680,22 @@ "node": ">=8" } }, + "node_modules/tar": { + "version": "7.5.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.13.tgz", + "integrity": "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/tar-fs": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", @@ -15460,6 +15879,12 @@ "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "license": "MIT" }, + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, "node_modules/tinyexec": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", @@ -15511,6 +15936,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinygradient": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/tinygradient/-/tinygradient-1.1.5.tgz", + "integrity": "sha512-8nIfc2vgQ4TeLnk2lFj4tRLvvJwEfQuabdsmvDdQPT0xlk9TaNtpGd6nNRxXoK6vQhN6RSzj+Cnp5tTQmpxmbw==", + "license": "MIT", + "dependencies": { + "@types/tinycolor2": "^1.4.0", + "tinycolor2": "^1.0.0" + } + }, "node_modules/tinypool": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", @@ -17650,18 +18085,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "packages/core/node_modules/dotenv": { - "version": "17.3.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", - "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://dotenvx.com" - } - }, "packages/core/node_modules/fdir": { "version": "6.4.6", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", diff --git a/package.json b/package.json index 48ad36e15..d957bfe10 100644 --- a/package.json +++ b/package.json @@ -93,13 +93,16 @@ "@types/marked": "^5.0.2", "@types/mime-types": "^3.0.1", "@types/minimatch": "^5.1.2", + "@types/command-exists": "^1.2.3", "@types/mock-fs": "^4.13.4", "@types/prompts": "^2.4.9", "@types/proper-lockfile": "^4.1.4", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", + "@types/semver": "^7.7.1", "@types/shell-quote": "^1.7.5", "@types/ws": "^8.18.1", + "@types/yargs": "^17.0.33", "@vitest/coverage-v8": "^3.1.1", "@vitest/eslint-plugin": "^1.3.4", "cross-env": "^7.0.3", @@ -136,12 +139,23 @@ "yargs": "^17.7.2" }, "dependencies": { + "clipboardy": "^4.0.0", + "command-exists": "^1.2.9", + "comment-json": "^4.2.5", + "dotenv": "^17.2.4", "ink": "npm:@jrichman/ink@6.6.3", + "ink-gradient": "^3.0.0", + "ink-spinner": "^5.0.0", "latest-version": "^9.0.0", + "lowlight": "^3.3.0", "node-fetch-native": "^1.6.7", + "prompts": "^2.4.2", "proper-lockfile": "^4.1.2", "punycode": "^2.3.1", - "simple-git": "^3.28.0" + "simple-git": "^3.28.0", + "tar": "^7.5.8", + "tinycolor2": "^1.6.0", + "tinygradient": "^1.1.5" }, "optionalDependencies": { "@lydell/node-pty": "1.1.0", diff --git a/packages/cli/src/commands/extensions.test.tsx b/packages/cli/src/commands/extensions.test.tsx index 5a0d5c944..604143e3a 100644 --- a/packages/cli/src/commands/extensions.test.tsx +++ b/packages/cli/src/commands/extensions.test.tsx @@ -50,8 +50,14 @@ describe('extensionsCommand', () => { version: vi.fn().mockReturnThis(), }; - // @ts-expect-error - Mocking yargs - extensionsCommand.builder(mockYargs); + const builder = extensionsCommand.builder; + + expect(typeof builder).toBe('function'); + if (typeof builder !== 'function') { + throw new Error('Expected extensionsCommand.builder to be a function'); + } + + builder(mockYargs as never); expect(mockYargs.middleware).toHaveBeenCalled(); expect(mockYargs.command).toHaveBeenCalledWith( @@ -86,7 +92,6 @@ describe('extensionsCommand', () => { }); it('should have a handler that does nothing', () => { - // @ts-expect-error - Handler doesn't take arguments in this case - expect(extensionsCommand.handler()).toBeUndefined(); + expect(extensionsCommand.handler({} as never)).toBeUndefined(); }); }); diff --git a/packages/cli/src/config/sandboxConfig.test.ts b/packages/cli/src/config/sandboxConfig.test.ts index e21da54c8..8c13b427a 100644 --- a/packages/cli/src/config/sandboxConfig.test.ts +++ b/packages/cli/src/config/sandboxConfig.test.ts @@ -142,7 +142,7 @@ describe('loadSandboxConfig', () => { it('should use sandbox-exec on darwin if available', async () => { mockedOsPlatform.mockReturnValue('darwin'); mockedCommandExistsSync.mockImplementation( - (cmd) => cmd === 'sandbox-exec', + (cmd: string) => cmd === 'sandbox-exec', ); const config = await loadSandboxConfig({}, { sandbox: true }); expect(config).toEqual({ @@ -169,7 +169,9 @@ describe('loadSandboxConfig', () => { it('should use docker if available and sandbox is true', async () => { mockedOsPlatform.mockReturnValue('linux'); - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'docker'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'docker', + ); const config = await loadSandboxConfig({ tools: { sandbox: true } }, {}); expect(config).toEqual({ enabled: true, @@ -182,7 +184,9 @@ describe('loadSandboxConfig', () => { it('should use podman if available and docker is not', async () => { mockedOsPlatform.mockReturnValue('linux'); - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'podman'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'podman', + ); const config = await loadSandboxConfig({}, { sandbox: true }); expect(config).toEqual({ enabled: true, @@ -275,7 +279,9 @@ describe('loadSandboxConfig', () => { describe('truthy/falsy sandbox values', () => { beforeEach(() => { mockedOsPlatform.mockReturnValue('linux'); - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'docker'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'docker', + ); }); it.each([true, 'true', '1'])( @@ -305,7 +311,9 @@ describe('loadSandboxConfig', () => { describe('with SandboxConfig object in settings', () => { beforeEach(() => { mockedOsPlatform.mockReturnValue('linux'); - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'docker'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'docker', + ); }); it('should support object structure with enabled: true', async () => { @@ -331,7 +339,9 @@ describe('loadSandboxConfig', () => { }); it('should support object structure with explicit command', async () => { - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'podman'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'podman', + ); const config = await loadSandboxConfig( { tools: { @@ -491,7 +501,9 @@ describe('loadSandboxConfig', () => { }); it('should throw if Docker not available (runsc requires Docker)', async () => { - mockedCommandExistsSync.mockImplementation((cmd) => cmd === 'runsc'); + mockedCommandExistsSync.mockImplementation( + (cmd: string) => cmd === 'runsc', + ); await expect(loadSandboxConfig({}, { sandbox: 'runsc' })).rejects.toThrow( "runsc (gVisor) requires Docker. Install Docker, or use sandbox: 'docker'.", @@ -500,7 +512,7 @@ describe('loadSandboxConfig', () => { it('should NOT auto-detect runsc when both runsc and docker available', async () => { mockedCommandExistsSync.mockImplementation( - (cmd) => cmd === 'runsc' || cmd === 'docker', + (cmd: string) => cmd === 'runsc' || cmd === 'docker', ); const config = await loadSandboxConfig({}, { sandbox: true });