diff --git a/docs/ko/ci.md b/docs/ko/ci.md index 9a0dadc26..aee9d3ca8 100644 --- a/docs/ko/ci.md +++ b/docs/ko/ci.md @@ -1,107 +1,101 @@ --- read_when: - - 특정 CI 작업이 왜 실행되었는지 또는 왜 실행되지 않았는지 이해해야 합니다. - - 실패한 GitHub Actions 체크를 디버깅하고 있습니다. -summary: CI 작업 그래프, 범위 게이트, 그리고 이에 대응하는 로컬 명령어들 + - CI 작업이 실행되었거나 실행되지 않은 이유를 파악해야 합니다. + - 실패한 GitHub Actions 검사를 디버깅하고 있습니다. +summary: CI 작업 그래프, 범위 게이트, 및 로컬 명령어 대응 항목 title: CI 파이프라인 x-i18n: - generated_at: "2026-04-23T13:59:33Z" + generated_at: "2026-04-23T14:55:17Z" model: gpt-5.4 provider: openai - source_hash: c5a8ea0d8e428826169b0e6aced1caeb993106fe79904002125ace86b48cae1f + source_hash: e9a03440ae28a15167fc08d9c66bb1fd719ddfa1517aaecb119c80f2ad826c0d source_path: ci.md workflow: 15 --- # CI 파이프라인 -CI는 `main`에 대한 모든 push와 모든 pull request에서 실행됩니다. 관련 없는 영역만 변경된 경우 비용이 큰 작업을 건너뛰도록 스마트 스코핑을 사용합니다. +CI는 `main`에 대한 모든 푸시와 모든 pull request에서 실행됩니다. 스마트 범위 지정을 사용해 변경된 영역이 관련 없는 경우 비용이 큰 작업을 건너뜁니다. -QA Lab에는 메인 스마트 스코프 워크플로 밖에 전용 CI 레인이 있습니다. -`Parity gate` 워크플로는 일치하는 PR 변경과 수동 디스패치에서 실행되며, -비공개 QA 런타임을 빌드하고 mock GPT-5.4 및 Opus 4.6 -agentic pack을 비교합니다. `QA-Lab - All Lanes` 워크플로는 `main`에서 매일 밤 실행되고 -수동 디스패치로도 실행되며, mock parity gate, live Matrix 레인, live -Telegram 레인을 병렬 작업으로 팬아웃합니다. live 작업은 `qa-live-shared` -environment를 사용하고, Telegram 레인은 Convex lease를 사용합니다. `OpenClaw Release -Checks`도 릴리스 승인 전에 동일한 QA Lab 레인들을 실행합니다. +QA Lab에는 메인 스마트 범위 워크플로 밖에 전용 CI 레인이 있습니다. `Parity gate` 워크플로는 일치하는 PR 변경 사항과 수동 실행에서 동작하며, 비공개 QA 런타임을 빌드하고 mock GPT-5.4 및 Opus 4.6 agentic pack을 비교합니다. `QA-Lab - All Lanes` 워크플로는 `main`에서 매일 밤 실행되고 수동 실행도 가능하며, mock parity gate, live Matrix lane, live Telegram lane을 병렬 작업으로 fan-out합니다. live 작업은 `qa-live-shared` environment를 사용하고, Telegram lane은 Convex lease를 사용합니다. `OpenClaw Release Checks`도 릴리스 승인 전에 동일한 QA Lab 레인을 실행합니다. ## 작업 개요 -| 작업 | 목적 | 실행 시점 | -| -------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------ | -| `preflight` | docs-only 변경, 변경된 범위, 변경된 extension을 감지하고 CI manifest를 빌드 | 초안이 아닌 모든 push 및 PR에서 항상 | -| `security-scm-fast` | `zizmor`를 통한 private key 감지 및 워크플로 감사 | 초안이 아닌 모든 push 및 PR에서 항상 | -| `security-dependency-audit` | npm advisory에 대한 dependency-free 프로덕션 lockfile 감사 | 초안이 아닌 모든 push 및 PR에서 항상 | -| `security-fast` | 빠른 보안 작업을 위한 필수 aggregate | 초안이 아닌 모든 push 및 PR에서 항상 | -| `build-artifacts` | `dist/`, Control UI, 빌드 산출물 검사, 재사용 가능한 다운스트림 산출물 빌드 | Node 관련 변경 시 | -| `checks-fast-core` | bundled/plugin-contract/protocol 검사 같은 빠른 Linux 정확성 레인 | Node 관련 변경 시 | -| `checks-fast-contracts-channels` | 안정적인 aggregate 체크 결과를 위한 샤딩된 채널 contract 검사 | Node 관련 변경 시 | -| `checks-node-extensions` | extension 스위트 전반에 걸친 전체 bundled-plugin 테스트 샤드 | Node 관련 변경 시 | -| `checks-node-core-test` | 채널, bundled, contract, extension 레인을 제외한 코어 Node 테스트 샤드 | Node 관련 변경 시 | -| `extension-fast` | 변경된 bundled plugin만을 대상으로 한 집중 테스트 | extension 변경이 있는 pull request | -| `check` | 샤딩된 메인 로컬 게이트 대응: 프로덕션 타입, lint, 가드, 테스트 타입, 엄격한 smoke | Node 관련 변경 시 | -| `check-additional` | 아키텍처, 경계, extension-surface 가드, package-boundary, gateway-watch 샤드 | Node 관련 변경 시 | -| `build-smoke` | 빌드된 CLI smoke 테스트 및 시작 메모리 smoke | Node 관련 변경 시 | -| `checks` | 빌드 산출물 채널 테스트 및 push 전용 Node 22 호환성 검증기 | Node 관련 변경 시 | -| `check-docs` | 문서 포맷팅, lint, 깨진 링크 검사 | docs 변경 시 | -| `skills-python` | Python 기반 Skills에 대한 Ruff + pytest | Python skill 관련 변경 시 | -| `checks-windows` | Windows 전용 테스트 레인 | Windows 관련 변경 시 | -| `macos-node` | 공유 빌드 산출물을 사용하는 macOS TypeScript 테스트 레인 | macOS 관련 변경 시 | -| `macos-swift` | macOS 앱에 대한 Swift lint, 빌드, 테스트 | macOS 관련 변경 시 | -| `android` | 두 flavor 모두에 대한 Android 단위 테스트 및 디버그 APK 하나 빌드 | Android 관련 변경 시 | +| 작업 | 목적 | 실행 시점 | +| --------------------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------- | +| `preflight` | docs-only 변경, 변경된 범위, 변경된 extension을 감지하고 CI manifest를 빌드 | 초안이 아닌 모든 push 및 PR에서 항상 실행 | +| `security-scm-fast` | `zizmor`를 통한 private key 탐지 및 워크플로 감사 | 초안이 아닌 모든 push 및 PR에서 항상 실행 | +| `security-dependency-audit` | npm advisory에 대한 dependency-free 프로덕션 lockfile 감사 | 초안이 아닌 모든 push 및 PR에서 항상 실행 | +| `security-fast` | 빠른 보안 작업용 필수 aggregate | 초안이 아닌 모든 push 및 PR에서 항상 실행 | +| `build-artifacts` | `dist/`, Control UI, built-artifact 검사, 재사용 가능한 downstream artifact 빌드 | Node 관련 변경 | +| `checks-fast-core` | bundled/plugin-contract/protocol 검사 같은 빠른 Linux 정확성 레인 | Node 관련 변경 | +| `checks-fast-contracts-channels` | 안정적인 aggregate check 결과를 갖는 shard된 channel contract 검사 | Node 관련 변경 | +| `checks-node-extensions` | extension 전체에 대한 전체 bundled-plugin 테스트 shard | Node 관련 변경 | +| `checks-node-core-test` | channel, bundled, contract, extension 레인을 제외한 core Node 테스트 shard | Node 관련 변경 | +| `extension-fast` | 변경된 bundled plugin만 대상으로 하는 집중 테스트 | extension 변경이 있는 pull request | +| `check` | shard된 메인 로컬 게이트 대응 항목: 프로덕션 타입, lint, guard, 테스트 타입, strict smoke | Node 관련 변경 | +| `check-additional` | 아키텍처, 경계, extension-surface guard, package-boundary, gateway-watch shard | Node 관련 변경 | +| `build-smoke` | built-CLI smoke 테스트 및 startup-memory smoke | Node 관련 변경 | +| `checks` | built-artifact channel 테스트와 push 전용 Node 22 호환성에 대한 verifier | Node 관련 변경 | +| `check-docs` | 문서 포맷팅, lint, 깨진 링크 검사 | 문서 변경 | +| `skills-python` | Python 기반 Skills용 Ruff + pytest | Python Skills 관련 변경 | +| `checks-windows` | Windows 전용 테스트 레인 | Windows 관련 변경 | +| `macos-node` | 공유 built artifact를 사용하는 macOS TypeScript 테스트 레인 | macOS 관련 변경 | +| `macos-swift` | macOS 앱용 Swift lint, 빌드, 테스트 | macOS 관련 변경 | +| `android` | 두 flavor 모두에 대한 Android 단위 테스트와 하나의 debug APK 빌드 | Android 관련 변경 | ## Fail-Fast 순서 -작업은 저렴한 검사가 비용이 큰 작업보다 먼저 실패하도록 정렬되어 있습니다. +작업은 비용이 큰 작업이 실행되기 전에 저렴한 검사가 먼저 실패하도록 순서가 지정되어 있습니다. -1. `preflight`가 어떤 레인이 아예 존재할지를 결정합니다. `docs-scope`와 `changed-scope` 로직은 독립 작업이 아니라 이 작업 내부의 step입니다. -2. `security-scm-fast`, `security-dependency-audit`, `security-fast`, `check`, `check-additional`, `check-docs`, `skills-python`은 더 무거운 산출물 및 플랫폼 매트릭스 작업을 기다리지 않고 빠르게 실패합니다. -3. `build-artifacts`는 빠른 Linux 레인과 겹쳐 실행되므로 공유 빌드가 준비되는 즉시 다운스트림 소비자가 시작할 수 있습니다. -4. 그다음 더 무거운 플랫폼 및 런타임 레인이 팬아웃합니다: `checks-fast-core`, `checks-fast-contracts-channels`, `checks-node-extensions`, `checks-node-core-test`, PR 전용 `extension-fast`, `checks`, `checks-windows`, `macos-node`, `macos-swift`, `android`. +1. `preflight`가 어떤 레인이 실제로 존재할지 결정합니다. `docs-scope`와 `changed-scope` 로직은 독립적인 작업이 아니라 이 작업 내부의 단계입니다. +2. `security-scm-fast`, `security-dependency-audit`, `security-fast`, `check`, `check-additional`, `check-docs`, `skills-python`은 더 무거운 artifact 및 플랫폼 매트릭스 작업을 기다리지 않고 빠르게 실패합니다. +3. `build-artifacts`는 빠른 Linux 레인과 겹쳐 실행되어 downstream consumer가 공유 빌드가 준비되는 즉시 시작할 수 있도록 합니다. +4. 그다음 더 무거운 플랫폼 및 런타임 레인이 fan-out됩니다: `checks-fast-core`, `checks-fast-contracts-channels`, `checks-node-extensions`, `checks-node-core-test`, PR 전용 `extension-fast`, `checks`, `checks-windows`, `macos-node`, `macos-swift`, `android`. 범위 로직은 `scripts/ci-changed-scope.mjs`에 있으며 `src/scripts/ci-changed-scope.test.ts`의 단위 테스트로 검증됩니다. -CI 워크플로 편집은 Node CI 그래프와 워크플로 linting은 검증하지만, 그 자체로 Windows, Android, macOS 네이티브 빌드를 강제하지는 않습니다. 이러한 플랫폼 레인은 계속 플랫폼 소스 변경에만 스코프됩니다. -Windows Node 체크는 Windows 전용 process/path wrapper, npm/pnpm/UI runner helper, package manager 구성, 그리고 해당 레인을 실행하는 CI 워크플로 surface에 스코프됩니다. 관련 없는 소스, plugin, install-smoke, test-only 변경은 일반 테스트 샤드에서 이미 커버되는 검증을 위해 16-vCPU Windows worker를 점유하지 않도록 Linux Node 레인에 남겨 둡니다. -별도의 `install-smoke` 워크플로는 자체 `preflight` 작업을 통해 같은 범위 스크립트를 재사용합니다. 더 좁은 changed-smoke 신호에서 `run_install_smoke`를 계산하므로, Docker/install smoke는 install, packaging, container 관련 변경, bundled extension 프로덕션 변경, 그리고 Docker smoke 작업이 실행하는 코어 plugin/channel/gateway/Plugin SDK surface에 대해 실행됩니다. test-only 및 docs-only 수정은 Docker worker를 점유하지 않습니다. 해당 QR package smoke는 BuildKit pnpm store 캐시는 유지하면서 Docker `pnpm install` 레이어를 강제로 다시 실행하므로, 매 실행마다 dependency를 다시 다운로드하지 않으면서도 설치를 계속 검증합니다. 해당 gateway-network e2e는 작업 초반에 빌드한 런타임 이미지를 재사용하므로, Docker 빌드를 하나 더 추가하지 않고도 실제 컨테이너 간 WebSocket 커버리지를 더합니다. 로컬 `test:docker:all`은 공유 live-test 이미지 하나와 공유 `scripts/e2e/Dockerfile` built-app 이미지 하나를 미리 빌드한 다음, `OPENCLAW_SKIP_DOCKER_BUILD=1`로 live/E2E smoke 레인을 병렬 실행합니다. 기본 동시성 4는 `OPENCLAW_DOCKER_ALL_PARALLELISM`으로 조정하세요. 로컬 aggregate는 기본적으로 첫 실패 이후 새 pooled 레인 스케줄링을 중단하며, 각 레인에는 120분 타임아웃이 있고 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`로 재정의할 수 있습니다. 시작 또는 provider에 민감한 레인은 병렬 풀 이후에 독점적으로 실행됩니다. 재사용 가능한 live/E2E 워크플로도 공유 이미지 패턴을 따르며, Docker 매트릭스 전에 SHA 태그가 붙은 GHCR Docker E2E 이미지를 하나 빌드 및 푸시한 다음 `OPENCLAW_SKIP_DOCKER_BUILD=1`로 매트릭스를 실행합니다. 예약된 live/E2E 워크플로는 전체 릴리스 경로 Docker 스위트를 매일 실행합니다. QR 및 installer Docker 테스트는 설치 중심의 자체 Dockerfile을 유지합니다. 별도의 `docker-e2e-fast` 작업은 120초 명령 타임아웃 하에서 범위가 제한된 bundled-plugin Docker 프로파일을 실행합니다: setup-entry dependency 복구와 synthetic bundled-loader 실패 격리입니다. 전체 bundled update/channel 매트릭스는 반복적인 실제 npm update 및 doctor repair 패스를 수행하므로 수동/전체 스위트로 유지됩니다. +CI 워크플로 편집은 Node CI 그래프와 워크플로 linting은 검증하지만, Windows, Android, macOS 네이티브 빌드를 그것만으로 강제하지는 않습니다. 해당 플랫폼 레인은 계속해서 플랫폼 소스 변경에만 범위가 지정됩니다. +Windows Node 검사는 Windows 전용 process/path wrapper, npm/pnpm/UI runner helper, 패키지 관리자 구성, 그리고 해당 레인을 실행하는 CI 워크플로 표면으로 범위가 지정됩니다. 관련 없는 소스, plugin, install-smoke, test-only 변경은 일반 테스트 shard로 이미 검증되는 범위를 위해 16-vCPU Windows worker를 예약하지 않도록 Linux Node 레인에 남습니다. +별도의 `install-smoke` 워크플로는 자체 `preflight` 작업을 통해 동일한 범위 스크립트를 재사용합니다. 더 좁은 changed-smoke 신호에서 `run_install_smoke`를 계산하므로, Docker/install smoke는 install, packaging, container 관련 변경, bundled extension 프로덕션 변경, 그리고 Docker smoke 작업이 실행하는 core plugin/channel/gateway/Plugin SDK 표면에 대해 실행됩니다. test-only 및 docs-only 편집은 Docker worker를 예약하지 않습니다. 해당 QR package smoke는 Docker `pnpm install` 레이어를 강제로 다시 실행하면서 BuildKit pnpm store 캐시는 유지하므로, 매 실행마다 dependency를 다시 다운로드하지 않으면서도 설치를 계속 검증합니다. gateway-network e2e는 작업 초기에 빌드된 런타임 이미지를 재사용하므로, 또 다른 Docker 빌드를 추가하지 않고 실제 container-to-container WebSocket 범위를 추가합니다. 로컬 `test:docker:all`은 공유 live-test 이미지 하나와 공유 `scripts/e2e/Dockerfile` built-app 이미지 하나를 미리 빌드한 뒤, `OPENCLAW_SKIP_DOCKER_BUILD=1`로 live/E2E smoke 레인을 병렬 실행합니다. 기본 동시성 4는 `OPENCLAW_DOCKER_ALL_PARALLELISM`으로 조정할 수 있습니다. 로컬 aggregate는 기본적으로 첫 번째 실패 후 새로운 pooled lane 예약을 중단하며, 각 lane에는 `OPENCLAW_DOCKER_ALL_LANE_TIMEOUT_MS`로 재정의 가능한 120분 타임아웃이 있습니다. startup 또는 provider 민감 레인은 병렬 풀 이후에 독점 실행됩니다. 재사용 가능한 live/E2E 워크플로는 Docker 매트릭스 전에 SHA 태그가 붙은 GHCR Docker E2E 이미지를 하나 빌드하고 푸시한 뒤, `OPENCLAW_SKIP_DOCKER_BUILD=1`로 매트릭스를 실행하는 동일한 shared-image 패턴을 따릅니다. 예약된 live/E2E 워크플로는 매일 전체 release-path Docker 제품군을 실행합니다. QR 및 installer Docker 테스트는 각자의 install 중심 Dockerfile을 유지합니다. 별도의 `docker-e2e-fast` 작업은 120초 명령 타임아웃 아래에서 제한된 bundled-plugin Docker 프로필을 실행합니다: setup-entry dependency repair 및 synthetic bundled-loader failure isolation입니다. 전체 bundled update/channel 매트릭스는 반복적인 실제 npm update 및 doctor repair 패스를 수행하므로 수동/전체 제품군으로 유지됩니다. -로컬 changed-lane 로직은 `scripts/changed-lanes.mjs`에 있으며 `scripts/check-changed.mjs`로 실행됩니다. 이 로컬 게이트는 넓은 CI 플랫폼 범위보다 아키텍처 경계에 더 엄격합니다. 코어 프로덕션 변경은 코어 프로덕션 typecheck와 코어 테스트를 실행하고, 코어 test-only 변경은 코어 테스트 typecheck/테스트만 실행하며, extension 프로덕션 변경은 extension 프로덕션 typecheck와 extension 테스트를 실행하고, extension test-only 변경은 extension 테스트 typecheck/테스트만 실행합니다. 공개 Plugin SDK 또는 plugin-contract 변경은 extension이 해당 코어 contract에 의존하므로 extension 검증으로 확장됩니다. 릴리스 메타데이터만 포함된 버전 증가 변경은 대상이 좁은 version/config/root-dependency 검사를 실행합니다. 알 수 없는 root/config 변경은 안전을 위해 모든 레인으로 실패 처리됩니다. +로컬 changed-lane 로직은 `scripts/changed-lanes.mjs`에 있으며 `scripts/check-changed.mjs`에 의해 실행됩니다. 이 로컬 게이트는 광범위한 CI 플랫폼 범위보다 아키텍처 경계에 더 엄격합니다. core 프로덕션 변경은 core 프로덕션 typecheck와 core 테스트를 실행하고, core test-only 변경은 core 테스트 typecheck/테스트만 실행하며, extension 프로덕션 변경은 extension 프로덕션 typecheck와 extension 테스트를 실행하고, extension test-only 변경은 extension 테스트 typecheck/테스트만 실행합니다. public Plugin SDK 또는 plugin-contract 변경은 extension이 이러한 core contract에 의존하므로 extension 검증까지 확장됩니다. 릴리스 메타데이터 전용 버전 범프는 대상이 좁은 version/config/root-dependency 검사를 실행합니다. 알 수 없는 root/config 변경은 안전하게 모든 레인으로 확장됩니다. -push에서는 `checks` 매트릭스에 push 전용 `compat-node22` 레인이 추가됩니다. pull request에서는 이 레인이 건너뛰어지고, 매트릭스는 일반 테스트/채널 레인에 집중합니다. +푸시에서는 `checks` 매트릭스가 push 전용 `compat-node22` 레인을 추가합니다. pull request에서는 해당 레인이 건너뛰어지고 매트릭스는 일반 테스트/channel 레인에 집중된 상태를 유지합니다. -가장 느린 Node 테스트 계열은 각 작업이 작게 유지되도록 분할 또는 균형 조정됩니다. 채널 contract는 registry와 코어 커버리지를 총 여섯 개의 가중 샤드로 분할하고, bundled plugin 테스트는 여섯 개의 extension worker에 균형 있게 배분되며, auto-reply는 여섯 개의 작은 worker 대신 균형 잡힌 세 개의 worker로 실행되고, agentic gateway/plugin config는 빌드 산출물을 기다리는 대신 기존 소스 전용 agentic Node 작업에 분산됩니다. 광범위한 browser, QA, media, 기타 plugin 테스트는 공유 plugin catch-all 대신 전용 Vitest config를 사용합니다. 광범위한 agents 레인은 단일 느린 테스트 파일이 소유하는 형태가 아니라 import/스케줄링 지배형이므로 공유 Vitest 파일 병렬 스케줄러를 사용합니다. `runtime-config`는 공유 런타임 샤드가 꼬리를 소유하지 않도록 infra core-runtime 샤드와 함께 실행됩니다. `check-additional`은 package-boundary compile/canary 작업을 함께 유지하고 런타임 topology 아키텍처를 gateway watch 커버리지와 분리합니다. boundary guard 샤드는 그 안의 작은 독립 가드를 하나의 작업 내부에서 동시에 실행합니다. Gateway watch, 채널 테스트, 코어 support-boundary 샤드는 `dist/`와 `dist-runtime/`이 이미 빌드된 뒤 `build-artifacts` 내부에서 동시에 실행되며, 두 개의 추가 Blacksmith worker와 두 번째 산출물 소비자 큐를 피하면서도 기존 체크 이름을 가벼운 verifier 작업으로 유지합니다. -Android CI는 `testPlayDebugUnitTest`와 `testThirdPartyDebugUnitTest`를 모두 실행한 다음 Play 디버그 APK를 빌드합니다. third-party flavor에는 별도의 소스 세트나 manifest가 없지만, 해당 단위 테스트 레인은 여전히 SMS/call-log BuildConfig 플래그로 그 flavor를 컴파일하면서도 모든 Android 관련 push마다 중복 디버그 APK 패키징 작업은 피합니다. -`extension-fast`는 push 실행이 이미 전체 bundled plugin 샤드를 수행하므로 PR 전용입니다. 이렇게 하면 리뷰 중 변경 plugin에 대한 피드백은 유지하면서도 `checks-node-extensions`에 이미 존재하는 커버리지를 위해 `main`에서 추가 Blacksmith worker를 예약하지 않게 됩니다. +가장 느린 Node 테스트 계열은 각 작업이 너무 커지지 않도록 분할 또는 균형 조정되어 있으며, 동시에 runner를 과도하게 예약하지 않도록 되어 있습니다. channel contract는 가중치 기반 shard 세 개로 실행되고, bundled plugin 테스트는 여섯 개의 extension worker에 걸쳐 균형 분산되며, 작은 core unit 레인은 짝지어지고, auto-reply는 여섯 개의 작은 worker 대신 균형 잡힌 세 개의 worker로 실행되며, agentic gateway/plugin config는 built artifact를 기다리지 않고 기존 source-only agentic Node 작업에 분산됩니다. 광범위한 browser, QA, media, 기타 plugin 테스트는 공유 plugin catch-all 대신 전용 Vitest 구성을 사용합니다. 광범위한 agents lane은 단일 느린 테스트 파일이 아니라 import/스케줄링이 지배적이므로 공유 Vitest 파일 병렬 스케줄러를 사용합니다. `runtime-config`는 공유 런타임 shard가 tail을 맡지 않도록 infra core-runtime shard와 함께 실행됩니다. `check-additional`은 package-boundary compile/canary 작업을 함께 유지하고, runtime topology architecture와 gateway watch 범위를 분리합니다. boundary guard shard는 하나의 작업 내에서 작은 독립 guard를 동시에 실행합니다. Gateway watch, channel 테스트, core support-boundary shard는 `dist/`와 `dist-runtime/`가 이미 빌드된 뒤 `build-artifacts` 내부에서 동시에 실행되며, 이전 check 이름은 가벼운 verifier 작업으로 유지하면서 추가 Blacksmith worker 두 개와 두 번째 artifact-consumer 큐는 피합니다. +Android CI는 `testPlayDebugUnitTest`와 `testThirdPartyDebugUnitTest`를 모두 실행한 뒤 Play debug APK를 빌드합니다. third-party flavor에는 별도의 소스 세트나 manifest가 없지만, 해당 단위 테스트 레인은 SMS/call-log BuildConfig 플래그를 사용해 그 flavor도 컴파일하면서 Android 관련 모든 푸시마다 중복 debug APK 패키징 작업이 실행되는 것은 피합니다. +`extension-fast`는 푸시 실행에서 이미 전체 bundled plugin shard를 수행하므로 PR 전용입니다. 이렇게 하면 리뷰를 위한 changed-plugin 피드백은 유지하면서, `checks-node-extensions`에 이미 포함된 범위를 위해 `main`에서 추가 Blacksmith worker를 예약하지 않아도 됩니다. -GitHub는 같은 PR 또는 `main` ref에 새 push가 들어오면 더 이상 최신이 아닌 작업을 `cancelled`로 표시할 수 있습니다. 같은 ref의 최신 실행도 실패 중이 아닌 한, 이를 CI 노이즈로 취급하세요. aggregate 샤드 체크는 `!cancelled() && always()`를 사용하므로 정상적인 샤드 실패는 계속 보고하지만, 전체 워크플로 자체가 이미 더 최신 실행으로 대체된 경우에는 큐에 들어가지 않습니다. -CI concurrency key는 버전이 지정되어 있습니다(`CI-v7-*`). 따라서 오래된 큐 그룹의 GitHub 측 zombie가 최신 main 실행을 무기한 차단할 수 없습니다. +GitHub는 같은 PR 또는 `main` ref에 더 새로운 푸시가 들어오면 대체된 작업을 `cancelled`로 표시할 수 있습니다. 같은 ref에 대한 최신 실행도 실패하는 경우가 아니라면 이를 CI 노이즈로 취급하세요. aggregate shard 검사는 `!cancelled() && always()`를 사용하므로 정상적인 shard 실패는 계속 보고하지만, 전체 워크플로가 이미 대체된 뒤에는 대기열에 들어가지 않습니다. +CI concurrency key는 버전 관리됩니다(`CI-v7-*`). 따라서 오래된 큐 그룹의 GitHub 측 zombie가 더 새로운 main 실행을 무기한 차단할 수 없습니다. ## 러너 -| 러너 | 작업 | -| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `ubuntu-24.04` | `preflight`, 빠른 보안 작업과 aggregate(`security-scm-fast`, `security-dependency-audit`, `security-fast`), 빠른 protocol/contract/bundled 검사, 샤딩된 채널 contract 검사, lint를 제외한 `check` 샤드, `check-additional` 샤드와 aggregate, Node 테스트 aggregate verifier, 문서 검사, Python Skills, workflow-sanity, labeler, auto-response. install-smoke preflight도 GitHub 호스팅 Ubuntu를 사용하므로 Blacksmith 매트릭스가 더 일찍 큐에 들어갈 수 있습니다 | -| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`, build-smoke, Linux Node 테스트 샤드, bundled plugin 테스트 샤드, `android` | -| `blacksmith-16vcpu-ubuntu-2404` | `check-lint` — 이 작업은 여전히 CPU 민감도가 높아 8 vCPU가 절약하는 것보다 더 많은 비용이 들었습니다. install-smoke Docker 빌드 — 여기서는 32-vCPU의 큐 대기 비용이 절약 효과보다 더 컸습니다 | -| `blacksmith-16vcpu-windows-2025` | `checks-windows` | -| `blacksmith-6vcpu-macos-latest` | `openclaw/openclaw`에서의 `macos-node`; fork에서는 `macos-latest`로 폴백 | -| `blacksmith-12vcpu-macos-latest` | `openclaw/openclaw`에서의 `macos-swift`; fork에서는 `macos-latest`로 폴백 | +| 러너 | 작업 | +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `ubuntu-24.04` | `preflight`, 빠른 보안 작업 및 aggregate (`security-scm-fast`, `security-dependency-audit`, `security-fast`), 빠른 protocol/contract/bundled 검사, shard된 channel contract 검사, lint를 제외한 `check` shard, `check-additional` shard 및 aggregate, Node 테스트 aggregate verifier, docs 검사, Python Skills, workflow-sanity, labeler, auto-response; install-smoke preflight도 Blacksmith 매트릭스가 더 일찍 대기열에 들어갈 수 있도록 GitHub 호스팅 Ubuntu를 사용합니다 | +| `blacksmith-8vcpu-ubuntu-2404` | `build-artifacts`, build-smoke, Linux Node 테스트 shard, bundled plugin 테스트 shard, `android` | +| `blacksmith-16vcpu-ubuntu-2404` | `check-lint`. 이 작업은 여전히 CPU 민감도가 높아서 8 vCPU는 절약 효과보다 비용이 더 컸습니다. 또한 install-smoke Docker 빌드도 여기에 해당하며, 32-vCPU는 절약 효과보다 대기열 시간이 더 컸습니다 | +| `blacksmith-16vcpu-windows-2025` | `checks-windows` | +| `blacksmith-6vcpu-macos-latest` | `openclaw/openclaw`에서의 `macos-node`; fork는 `macos-latest`로 대체됩니다 | +| `blacksmith-12vcpu-macos-latest` | `openclaw/openclaw`에서의 `macos-swift`; fork는 `macos-latest`로 대체됩니다 | -## 로컬 대응 명령어 +## 로컬 대응 항목 ```bash -pnpm changed:lanes # origin/main...HEAD에 대한 로컬 changed-lane 분류기를 확인 -pnpm check:changed # 스마트 로컬 게이트: 경계 레인별 변경 typecheck/lint/테스트 -pnpm check # 빠른 로컬 게이트: 프로덕션 tsgo + 샤딩된 lint + 병렬 빠른 가드 +pnpm changed:lanes # origin/main...HEAD에 대한 로컬 changed-lane 분류기 확인 +pnpm check:changed # 스마트 로컬 게이트: 경계 레인별 변경된 typecheck/lint/테스트 +pnpm check # 빠른 로컬 게이트: 프로덕션 tsgo + shard된 lint + 병렬 빠른 guard pnpm check:test-types pnpm check:timed # 단계별 시간 측정이 포함된 동일한 게이트 pnpm build:strict-smoke pnpm check:architecture pnpm test:gateway:watch-regression -pnpm test # Vitest 테스트 +pnpm test # vitest 테스트 pnpm test:channels pnpm test:contracts:channels -pnpm check:docs # 문서 포맷 + lint + 깨진 링크 +pnpm check:docs # docs 형식 + lint + 깨진 링크 pnpm build # CI artifact/build-smoke 레인이 중요할 때 dist 빌드 -node scripts/ci-run-timings.mjs # 총 소요 시간, 큐 대기 시간, 가장 느린 작업 요약 +node scripts/ci-run-timings.mjs # 전체 시간, 대기열 시간, 가장 느린 작업 요약 +node scripts/ci-run-timings.mjs --recent 10 # 최근 성공한 main CI 실행 비교 ``` diff --git a/docs/ko/gateway/authentication.md b/docs/ko/gateway/authentication.md index c37e4f0a8..bd8b27a1a 100644 --- a/docs/ko/gateway/authentication.md +++ b/docs/ko/gateway/authentication.md @@ -1,43 +1,43 @@ --- read_when: - - 모델 인증 또는 OAuth 만료를 디버깅할 때 - - 인증 또는 자격 증명 저장소를 문서화할 때 -summary: '모델 인증: OAuth, API 키, Claude CLI 재사용, 그리고 Anthropic setup-token' + - 모델 인증 또는 OAuth 만료 디버깅 + - 인증 또는 자격 증명 저장 문서화 +summary: '모델 인증: OAuth, API 키, Claude CLI 재사용, 및 Anthropic 설정 토큰' title: 인증 x-i18n: - generated_at: "2026-04-07T05:55:51Z" + generated_at: "2026-04-23T14:55:31Z" model: gpt-5.4 provider: openai - source_hash: 9db0ad9eccd7e3e3ca328adaad260bc4288a8ccdbe2dc0c24d9fd049b7ab9231 + source_hash: 37a7c20872b915d1d079f0578c933e43cbdb97eca1c60d8c4e6e5137ca83f8b2 source_path: gateway/authentication.md workflow: 15 --- -# 인증 (모델 providers) +# 인증 (모델 제공자) -이 페이지는 **모델 provider** 인증(API 키, OAuth, Claude CLI 재사용, Anthropic setup-token)을 다룹니다. **Gateway 연결** 인증(token, password, trusted-proxy)은 [구성](/ko/gateway/configuration) 및 [Trusted Proxy Auth](/ko/gateway/trusted-proxy-auth)를 참조하세요. +이 페이지는 **모델 제공자** 인증(API 키, OAuth, Claude CLI 재사용, Anthropic 설정 토큰)을 다룹니다. **Gateway 연결** 인증(토큰, 비밀번호, trusted-proxy)은 [구성](/ko/gateway/configuration) 및 [Trusted Proxy Auth](/ko/gateway/trusted-proxy-auth)를 참조하세요. -OpenClaw는 모델 providers에 대해 OAuth와 API 키를 지원합니다. 항상 실행되는 Gateway -호스트에서는 일반적으로 API 키가 가장 예측 가능한 선택입니다. 구독/OAuth -흐름도 provider 계정 모델과 일치하는 경우 지원됩니다. +OpenClaw는 모델 제공자에 대해 OAuth와 API 키를 지원합니다. 항상 실행되는 Gateway +호스트에서는 일반적으로 API 키가 가장 예측 가능한 옵션입니다. 제공자 계정 모델에 맞는 경우 구독/OAuth +흐름도 지원됩니다. 전체 OAuth 흐름과 저장소 -구조는 [/concepts/oauth](/ko/concepts/oauth)를 참조하세요. -SecretRef 기반 인증(`env`/`file`/`exec` providers)은 [Secrets Management](/ko/gateway/secrets)를 참조하세요. -`models status --probe`에서 사용하는 자격 증명 적격성/사유 코드 규칙은 -[Auth Credential Semantics](/ko/auth-credential-semantics)를 참조하세요. +레이아웃은 [/concepts/oauth](/ko/concepts/oauth)를 참조하세요. +SecretRef 기반 인증(`env`/`file`/`exec` 제공자)은 [Secrets Management](/ko/gateway/secrets)를 참조하세요. +`models status --probe`에서 사용하는 자격 증명 적격성/이유 코드 규칙은 +[인증 자격 증명 시맨틱](/ko/auth-credential-semantics)을 참조하세요. -## 권장 설정(API 키, 모든 provider) +## 권장 설정(API 키, 모든 제공자) -장시간 실행되는 Gateway를 운영하는 경우, 선택한 -provider의 API 키로 시작하세요. -Anthropic의 경우 특히 API 키 인증이 여전히 가장 예측 가능한 서버 +장기간 실행되는 Gateway를 사용 중이라면, 선택한 +제공자용 API 키부터 시작하세요. +특히 Anthropic의 경우에도 API 키 인증이 여전히 가장 예측 가능한 서버 설정이지만, OpenClaw는 로컬 Claude CLI 로그인 재사용도 지원합니다. -1. provider 콘솔에서 API 키를 생성합니다. -2. 이를 **Gateway 호스트**(`openclaw gateway`를 실행하는 머신)에 배치합니다. +1. 제공자 콘솔에서 API 키를 만듭니다. +2. 이를 **Gateway 호스트**(`openclaw gateway`를 실행하는 머신)에 설정합니다. ```bash export _API_KEY="..." @@ -45,7 +45,7 @@ openclaw models status ``` 3. Gateway가 systemd/launchd 아래에서 실행되는 경우, 데몬이 읽을 수 있도록 - 키를 `~/.openclaw/.env`에 넣는 것을 권장합니다: + `~/.openclaw/.env`에 키를 두는 것을 권장합니다. ```bash cat >> ~/.openclaw/.env <<'EOF' @@ -53,43 +53,60 @@ cat >> ~/.openclaw/.env <<'EOF' EOF ``` -그런 다음 데몬을 재시작(또는 Gateway 프로세스를 재시작)하고 다시 확인하세요: +그런 다음 데몬을 다시 시작하고(또는 Gateway 프로세스를 다시 시작하고) 다시 확인하세요. ```bash openclaw models status openclaw doctor ``` -환경 변수를 직접 관리하고 싶지 않다면, 온보딩이 데몬 사용을 위해 -API 키를 저장할 수 있습니다: `openclaw onboard`. +직접 env var를 관리하고 싶지 않다면, 온보딩에서 +데몬용 API 키를 저장할 수 있습니다: `openclaw onboard`. `env.shellEnv`, -`~/.openclaw/.env`, systemd/launchd의 환경 상속에 대한 자세한 내용은 [도움말](/ko/help)을 참조하세요. +`~/.openclaw/.env`, systemd/launchd의 env 상속에 대한 자세한 내용은 [도움말](/ko/help)을 참조하세요. ## Anthropic: Claude CLI 및 토큰 호환성 -Anthropic setup-token 인증은 OpenClaw에서 여전히 지원되는 토큰 -경로로 제공됩니다. 이후 Anthropic 담당자가 OpenClaw 스타일의 Claude CLI 사용이 -다시 허용된다고 알려주었으므로, Anthropic이 새 정책을 발표하지 않는 한 -OpenClaw는 Claude CLI 재사용과 `claude -p` 사용을 이 통합에 대해 -허용된 방식으로 취급합니다. 호스트에서 Claude CLI 재사용이 가능하다면, -이제 그것이 선호되는 경로입니다. +Anthropic 설정 토큰 인증은 여전히 OpenClaw에서 지원되는 토큰 +경로로 사용할 수 있습니다. 이후 Anthropic 직원이 OpenClaw 스타일 Claude CLI 사용이 +다시 허용된다고 알려왔기 때문에, Anthropic이 새로운 정책을 발표하지 않는 한 +OpenClaw는 이 통합에 대해 Claude CLI 재사용과 `claude -p` 사용을 허용된 방식으로 취급합니다. 호스트에서 +Claude CLI 재사용이 가능한 경우, 이제 이것이 권장 경로입니다. -장시간 실행되는 Gateway 호스트에서는 Anthropic API 키가 여전히 가장 예측 가능한 +장기간 실행되는 Gateway 호스트의 경우, Anthropic API 키가 여전히 가장 예측 가능한 설정입니다. 같은 호스트에서 기존 Claude 로그인을 재사용하려면, 온보딩/구성에서 Anthropic Claude CLI 경로를 사용하세요. -수동 토큰 입력(모든 provider, `auth-profiles.json` 기록 + config 업데이트): +Claude CLI 재사용을 위한 권장 호스트 설정: + +```bash +# Gateway 호스트에서 실행 +claude auth login +claude auth status --text +openclaw models auth login --provider anthropic --method cli --set-default +``` + +이것은 2단계 설정입니다. + +1. Gateway 호스트에서 Claude Code 자체를 Anthropic에 로그인시킵니다. +2. OpenClaw에 Anthropic 모델 선택을 로컬 `claude-cli` + 백엔드로 전환하고, 그에 맞는 OpenClaw 인증 프로필을 저장하도록 지시합니다. + +`claude`가 `PATH`에 없다면, 먼저 Claude Code를 설치하거나 +`agents.defaults.cliBackends.claude-cli.command`를 실제 바이너리 경로로 설정하세요. + +수동 토큰 입력(모든 제공자; `auth-profiles.json` 작성 + config 업데이트): ```bash openclaw models auth paste-token --provider openrouter ``` -정적 자격 증명에는 인증 프로필 참조도 지원됩니다: +정적 자격 증명에 대해서도 인증 프로필 참조가 지원됩니다. -- `api_key` 자격 증명은 `keyRef: { source, provider, id }`를 사용할 수 있습니다 -- `token` 자격 증명은 `tokenRef: { source, provider, id }`를 사용할 수 있습니다 -- OAuth 모드 프로필은 SecretRef 자격 증명을 지원하지 않습니다. `auth.profiles..mode`가 `"oauth"`로 설정된 경우, 해당 프로필의 SecretRef 기반 `keyRef`/`tokenRef` 입력은 거부됩니다. +- `api_key` 자격 증명은 `keyRef: { source, provider, id }`를 사용할 수 있습니다. +- `token` 자격 증명은 `tokenRef: { source, provider, id }`를 사용할 수 있습니다. +- OAuth 모드 프로필은 SecretRef 자격 증명을 지원하지 않습니다. `auth.profiles..mode`가 `"oauth"`로 설정된 경우, 해당 프로필에 대한 SecretRef 기반 `keyRef`/`tokenRef` 입력은 거부됩니다. 자동화 친화적 확인(만료/누락 시 종료 코드 `1`, 만료 임박 시 `2`): @@ -97,7 +114,7 @@ openclaw models auth paste-token --provider openrouter openclaw models status --check ``` -실시간 인증 프로브: +라이브 인증 프로브: ```bash openclaw models status --probe @@ -105,25 +122,26 @@ openclaw models status --probe 참고: -- 프로브 행은 인증 프로필, 환경 자격 증명, 또는 `models.json`에서 올 수 있습니다. +- 프로브 행은 인증 프로필, env 자격 증명 또는 `models.json`에서 올 수 있습니다. - 명시적 `auth.order.`가 저장된 프로필을 생략하면, 프로브는 해당 프로필을 시도하는 대신 `excluded_by_auth_order`를 보고합니다. -- 인증이 존재하지만 OpenClaw가 해당 provider에 대해 프로브 가능한 모델 후보를 - 확인할 수 없으면, 프로브는 `status: no_model`을 보고합니다. +- 인증이 존재하지만 OpenClaw가 해당 제공자에 대해 프로브 가능한 모델 후보를 확인할 수 없는 경우, + 프로브는 `status: no_model`을 보고합니다. - rate-limit cooldown은 모델 범위일 수 있습니다. 한 - 모델에 대해 cooldown 상태인 프로필도 같은 provider의 다른 형제 모델에는 여전히 사용할 수 있습니다. + 모델에 대해 cooldown 중인 프로필이 동일 제공자의 다른 형제 모델에서는 여전히 사용 가능할 수 있습니다. -선택적 운영 스크립트(systemd/Termux)는 여기에서 문서화되어 있습니다: +선택적 운영 스크립트(systemd/Termux)는 여기 문서화되어 있습니다: [인증 모니터링 스크립트](/ko/help/scripts#auth-monitoring-scripts) ## Anthropic 참고 Anthropic `claude-cli` 백엔드는 다시 지원됩니다. -- Anthropic 담당자는 이 OpenClaw 통합 경로가 다시 허용된다고 알려주었습니다. -- 따라서 Anthropic이 새로운 정책을 발표하지 않는 한, OpenClaw는 Anthropic 기반 실행에 대해 Claude CLI 재사용과 `claude -p` 사용을 허용된 방식으로 취급합니다. -- Anthropic API 키는 장시간 실행되는 Gateway - 호스트와 명시적인 서버 측 과금 제어를 위한 가장 예측 가능한 선택으로 남아 있습니다. +- Anthropic 직원이 이 OpenClaw 통합 경로가 다시 허용된다고 알려왔습니다. +- 따라서 Anthropic이 새로운 정책을 발표하지 않는 한, OpenClaw는 + Anthropic 기반 실행에 대해 Claude CLI 재사용과 `claude -p` 사용을 허용된 방식으로 취급합니다. +- Anthropic API 키는 장기간 실행되는 Gateway + 호스트와 명시적인 서버 측 과금 제어를 위해 여전히 가장 예측 가능한 선택입니다. ## 모델 인증 상태 확인 @@ -132,36 +150,36 @@ openclaw models status openclaw doctor ``` -## API 키 교체 동작(Gateway) +## API 키 순환 동작(Gateway) -일부 providers는 API 호출이 provider rate limit에 -도달했을 때 대체 키로 요청을 다시 시도하는 것을 지원합니다. +일부 제공자는 API 호출이 제공자 rate limit에 +도달했을 때 대체 키로 요청을 재시도하는 것을 지원합니다. - 우선순위: - - `OPENCLAW_LIVE__KEY` (단일 override) + - `OPENCLAW_LIVE__KEY`(단일 재정의) - `_API_KEYS` - `_API_KEY` - `_API_KEY_*` -- Google providers는 추가 fallback으로 `GOOGLE_API_KEY`도 포함합니다. +- Google 제공자는 추가 폴백으로 `GOOGLE_API_KEY`도 포함합니다. - 동일한 키 목록은 사용 전에 중복 제거됩니다. - OpenClaw는 rate-limit 오류에 대해서만 다음 키로 재시도합니다(예: `429`, `rate_limit`, `quota`, `resource exhausted`, `Too many concurrent requests`, `ThrottlingException`, `concurrency limit reached`, 또는 `workers_ai ... quota limit exceeded`). - rate-limit이 아닌 오류는 대체 키로 재시도하지 않습니다. -- 모든 키가 실패하면 마지막 시도의 최종 오류가 반환됩니다. +- 모든 키가 실패하면, 마지막 시도의 최종 오류가 반환됩니다. -## 어떤 자격 증명을 사용할지 제어하기 +## 어떤 자격 증명을 사용할지 제어 ### 세션별(채팅 명령) -현재 세션에서 특정 provider 자격 증명을 고정하려면 `/model @`를 사용하세요(예시 프로필 ID: `anthropic:default`, `anthropic:work`). +현재 세션에 특정 제공자 자격 증명을 고정하려면 `/model @`를 사용하세요(프로필 ID 예시: `anthropic:default`, `anthropic:work`). -간단한 선택기는 `/model`(또는 `/model list`)을 사용하고, 전체 보기(후보 + 다음 인증 프로필, 설정된 경우 provider 엔드포인트 세부 정보 포함)는 `/model status`를 사용하세요. +간단한 선택기는 `/model`(또는 `/model list`)을 사용하고, 전체 보기(후보 + 다음 인증 프로필, 구성된 경우 제공자 엔드포인트 세부 정보 포함)는 `/model status`를 사용하세요. -### 에이전트별(CLI override) +### 에이전트별(CLI 재정의) -에이전트에 대한 명시적 인증 프로필 순서 override를 설정합니다(해당 에이전트의 `auth-state.json`에 저장됨): +에이전트에 대한 명시적 인증 프로필 순서 재정의를 설정합니다(해당 에이전트의 `auth-state.json`에 저장됨). ```bash openclaw models auth order get --provider anthropic @@ -169,17 +187,18 @@ openclaw models auth order set --provider anthropic anthropic:default openclaw models auth order clear --provider anthropic ``` -특정 에이전트를 대상으로 하려면 `--agent `를 사용하고, 구성된 기본 에이전트를 사용하려면 이를 생략하세요. -순서 문제를 디버깅할 때 `openclaw models status --probe`는 생략된 +특정 에이전트를 대상으로 하려면 `--agent `를 사용하고, 구성된 기본 에이전트를 사용하려면 생략하세요. +순서 문제를 디버깅할 때는 `openclaw models status --probe`가 생략된 저장 프로필을 조용히 건너뛰는 대신 `excluded_by_auth_order`로 표시합니다. cooldown 문제를 디버깅할 때는 rate-limit cooldown이 -전체 provider 프로필이 아니라 하나의 모델 ID에 연결될 수 있다는 점을 기억하세요. +전체 제공자 프로필이 아니라 하나의 모델 ID에 연결될 수 있다는 점을 기억하세요. ## 문제 해결 -### "No credentials found" +### "자격 증명을 찾을 수 없음" -Anthropic 프로필이 없으면 **Gateway 호스트**에 Anthropic API 키를 구성하거나 Anthropic setup-token 경로를 설정한 다음 다시 확인하세요: +Anthropic 프로필이 없으면, +**Gateway 호스트**에 Anthropic API 키를 구성하거나 Anthropic 설정 토큰 경로를 설정한 다음 다시 확인하세요. ```bash openclaw models status @@ -187,4 +206,6 @@ openclaw models status ### 토큰 만료 임박/만료됨 -어떤 프로필이 만료 중인지 확인하려면 `openclaw models status`를 실행하세요. Anthropic 토큰 프로필이 없거나 만료된 경우, setup-token을 통해 해당 설정을 새로 고치거나 Anthropic API 키로 마이그레이션하세요. +어떤 프로필이 만료 중인지 확인하려면 `openclaw models status`를 실행하세요. Anthropic 토큰 프로필이 없거나 만료된 경우, +setup-token으로 해당 설정을 새로 고치거나 Anthropic API 키로 +마이그레이션하세요. diff --git a/docs/ko/gateway/cli-backends.md b/docs/ko/gateway/cli-backends.md index 1bc1b91e3..99de2d97b 100644 --- a/docs/ko/gateway/cli-backends.md +++ b/docs/ko/gateway/cli-backends.md @@ -1,46 +1,41 @@ --- read_when: - - API provider가 실패할 때 신뢰할 수 있는 대체 경로를 원합니다 - - Codex CLI 또는 기타 로컬 AI CLI를 실행 중이며 이를 재사용하려고 합니다 - - CLI 백엔드 도구 액세스를 위한 MCP loopback 브리지를 이해하려고 합니다 -summary: 'CLI 백엔드: 선택적 MCP 도구 브리지가 포함된 로컬 AI CLI 대체 경로' + - API 제공자가 실패할 때를 대비해 신뢰할 수 있는 대체 경로가 필요합니다 + - Codex CLI 또는 다른 로컬 AI CLI를 실행 중이며 이를 재사용하려고 합니다 + - CLI 백엔드 도구 액세스를 위한 MCP loopback 브리지를 이해하고 싶습니다 +summary: 'CLI 백엔드: 선택적 MCP 도구 브리지를 사용하는 로컬 AI CLI 대체 경로' title: CLI 백엔드 x-i18n: - generated_at: "2026-04-23T14:03:00Z" + generated_at: "2026-04-23T14:55:25Z" model: gpt-5.4 provider: openai - source_hash: 475923b36e4580d3e4e57014ff2e6b89e9eb52c11b0a0ab1fc8241655b07836e + source_hash: ff7458d18b8a5b716930579241177917fd3edffcf7f6e211c7d570cf76519316 source_path: gateway/cli-backends.md workflow: 15 --- # CLI 백엔드(대체 런타임) -OpenClaw은 API provider가 다운되었거나, -rate limit에 걸렸거나, 일시적으로 오동작할 때 **로컬 AI CLI**를 **텍스트 전용 대체 경로**로 실행할 수 있습니다. 이 경로는 의도적으로 보수적으로 설계되었습니다: +OpenClaw는 API 제공자가 다운되었거나, 속도 제한에 걸렸거나, 일시적으로 오작동할 때 **로컬 AI CLI**를 **텍스트 전용 대체 경로**로 실행할 수 있습니다. 이는 의도적으로 보수적으로 설계되었습니다. -- **OpenClaw 도구는 직접 주입되지 않지만**, `bundleMcp: true`인 백엔드는 loopback MCP 브리지를 통해 gateway 도구를 받을 수 있습니다. -- 이를 지원하는 CLI를 위한 **JSONL 스트리밍**. -- **세션 지원**(후속 턴의 일관성 유지). -- CLI가 이미지 경로를 받는다면 **이미지 전달 가능**. +- **OpenClaw 도구는 직접 주입되지 않지만**, `bundleMcp: true`가 설정된 백엔드는 loopback MCP 브리지를 통해 gateway 도구를 받을 수 있습니다. +- 이를 지원하는 CLI를 위한 **JSONL 스트리밍** +- **세션이 지원되므로** 후속 턴의 일관성이 유지됩니다. +- CLI가 이미지 경로를 받을 수 있다면 **이미지를 그대로 전달할 수 있습니다**. -이 기능은 기본 경로라기보다 **안전망**으로 설계되었습니다. 외부 API에 의존하지 않고 -“항상 동작하는” 텍스트 응답을 원할 때 사용하세요. +이 기능은 주 경로라기보다 **안전망**으로 설계되었습니다. 외부 API에 의존하지 않고 “항상 작동하는” 텍스트 응답이 필요할 때 사용하세요. -ACP 세션 제어, 백그라운드 작업, -스레드/대화 바인딩, 영속적인 외부 코딩 세션이 있는 완전한 harness 런타임이 필요하다면 -대신 [ACP Agents](/ko/tools/acp-agents)를 사용하세요. CLI 백엔드는 ACP가 아닙니다. +ACP 세션 제어, 백그라운드 작업, 스레드/대화 바인딩, 영구적인 외부 코딩 세션을 갖춘 전체 하네스 런타임이 필요하다면 대신 [ACP Agents](/ko/tools/acp-agents)를 사용하세요. CLI 백엔드는 ACP가 아닙니다. ## 초보자용 빠른 시작 -Codex CLI는 **아무 구성 없이도** 사용할 수 있습니다(번들된 OpenAI plugin이 -기본 백엔드를 등록합니다): +설정 없이도 Codex CLI를 사용할 수 있습니다(번들 OpenAI Plugin이 기본 백엔드를 등록합니다). ```bash openclaw agent --message "hi" --model codex-cli/gpt-5.4 ``` -gateway가 launchd/systemd 아래에서 실행되고 PATH가 최소 구성이라면, 명령 경로만 추가하세요: +gateway가 launchd/systemd 아래에서 실행되고 PATH가 최소화되어 있다면, 명령 경로만 추가하세요. ```json5 { @@ -56,15 +51,13 @@ gateway가 launchd/systemd 아래에서 실행되고 PATH가 최소 구성이라 } ``` -이것으로 충분합니다. CLI 자체 외에 별도의 키나 인증 구성은 필요하지 않습니다. +이것으로 충분합니다. CLI 자체에 필요한 것 외에는 키도, 추가 인증 설정도 필요하지 않습니다. -번들된 CLI 백엔드를 gateway 호스트에서 **기본 메시지 provider**로 사용하는 경우, -이제 OpenClaw은 구성에서 model ref 또는 -`agents.defaults.cliBackends` 아래에 해당 백엔드를 명시적으로 참조하면 소유 번들 plugin을 자동으로 로드합니다. +gateway 호스트에서 번들 CLI 백엔드를 **기본 메시지 제공자**로 사용하는 경우, 이제 OpenClaw는 모델 ref 또는 `agents.defaults.cliBackends` 아래에서 해당 백엔드를 설정이 명시적으로 참조하면 소유 번들 Plugin을 자동으로 로드합니다. ## 대체 경로로 사용하기 -CLI 백엔드를 대체 목록에 추가하면 기본 모델이 실패할 때만 실행됩니다: +기본 모델이 실패할 때만 실행되도록 대체 목록에 CLI 백엔드를 추가하세요. ```json5 { @@ -85,20 +78,19 @@ CLI 백엔드를 대체 목록에 추가하면 기본 모델이 실패할 때만 참고: -- `agents.defaults.models`(허용 목록)을 사용하는 경우, CLI 백엔드 모델도 여기에 포함해야 합니다. -- 기본 provider가 실패하면(인증, rate limit, timeout), OpenClaw은 - 다음으로 CLI 백엔드를 시도합니다. +- `agents.defaults.models`(허용 목록)를 사용하는 경우, 여기에 CLI 백엔드 모델도 포함해야 합니다. +- 기본 제공자가 실패하면(인증, 속도 제한, 타임아웃), OpenClaw가 다음으로 CLI 백엔드를 시도합니다. ## 구성 개요 -모든 CLI 백엔드는 다음 아래에 있습니다: +모든 CLI 백엔드는 다음 아래에 있습니다. ``` agents.defaults.cliBackends ``` 각 항목은 **provider id**(예: `codex-cli`, `my-cli`)를 키로 사용합니다. -provider id는 model ref의 왼쪽 부분이 됩니다: +provider id는 모델 ref의 왼쪽 부분이 됩니다. ``` / @@ -142,92 +134,78 @@ provider id는 model ref의 왼쪽 부분이 됩니다: } ``` -## 동작 방식 +## 작동 방식 -1. provider 접두사(`codex-cli/...`)를 기준으로 **백엔드를 선택**합니다. -2. 동일한 OpenClaw 프롬프트 + 워크스페이스 컨텍스트를 사용해 **시스템 프롬프트를 구성**합니다. -3. CLI가 지원하면 세션 id와 함께 **CLI를 실행**하여 기록의 일관성을 유지합니다. - 번들된 `claude-cli` 백엔드는 OpenClaw 세션별로 Claude stdio 프로세스를 - 유지하고 후속 턴을 stream-json stdin으로 보냅니다. -4. **출력을 파싱**(JSON 또는 일반 텍스트)하고 최종 텍스트를 반환합니다. -5. 백엔드별로 **세션 id를 영속화**하여 후속 요청이 같은 CLI 세션을 재사용하게 합니다. +1. provider 접두사(`codex-cli/...`)를 기준으로 **백엔드를 선택합니다**. +2. 동일한 OpenClaw 프롬프트 + 작업공간 컨텍스트를 사용해 **시스템 프롬프트를 구성합니다**. +3. 지원되는 경우 세션 id와 함께 **CLI를 실행하여** 기록의 일관성을 유지합니다. + 번들 `claude-cli` 백엔드는 OpenClaw 세션마다 Claude stdio 프로세스를 유지하고 후속 턴을 stream-json stdin을 통해 전송합니다. +4. **출력을 파싱하고**(JSON 또는 일반 텍스트) 최종 텍스트를 반환합니다. +5. 백엔드별로 **세션 id를 유지**하므로 후속 요청에서 동일한 CLI 세션을 재사용합니다. -번들된 Anthropic `claude-cli` 백엔드가 다시 지원됩니다. Anthropic 직원이 -OpenClaw 스타일 Claude CLI 사용이 다시 허용된다고 알려주었기 때문에, Anthropic이 새 정책을 게시하기 전까지 OpenClaw은 이 통합에 대해 -`claude -p` 사용을 허용된 것으로 취급합니다. +번들 Anthropic `claude-cli` 백엔드가 다시 지원됩니다. Anthropic 직원이 OpenClaw 스타일 Claude CLI 사용이 다시 허용된다고 알려왔기 때문에, Anthropic이 새로운 정책을 발표하지 않는 한 OpenClaw는 이 통합에 대해 `claude -p` 사용을 승인된 것으로 취급합니다. -번들된 OpenAI `codex-cli` 백엔드는 OpenClaw의 시스템 프롬프트를 -Codex의 `model_instructions_file` 구성 override(`-c -model_instructions_file="..."`)를 통해 전달합니다. Codex는 Claude 스타일의 -`--append-system-prompt` 플래그를 제공하지 않으므로, OpenClaw은 새 Codex CLI 세션마다 조합된 프롬프트를 임시 파일에 기록합니다. +번들 OpenAI `codex-cli` 백엔드는 OpenClaw의 시스템 프롬프트를 Codex의 `model_instructions_file` 설정 오버라이드(`-c +model_instructions_file="..."`)를 통해 전달합니다. Codex는 Claude 스타일의 `--append-system-prompt` 플래그를 제공하지 않으므로, OpenClaw는 새 Codex CLI 세션마다 조합된 프롬프트를 임시 파일에 기록합니다. -번들된 Anthropic `claude-cli` 백엔드는 OpenClaw Skills 스냅샷을 -두 가지 방식으로 받습니다: 추가된 시스템 프롬프트의 압축된 OpenClaw Skills 카탈로그, 그리고 -`--plugin-dir`로 전달되는 임시 Claude Code plugin입니다. 이 plugin에는 해당 에이전트/세션에 적합한 Skills만 포함되므로, Claude Code의 네이티브 skill resolver는 OpenClaw이 프롬프트에서 광고했을 것과 동일한 필터링된 집합을 보게 됩니다. Skill env/API key override는 여전히 OpenClaw이 실행을 위해 child process 환경에 적용합니다. +번들 Anthropic `claude-cli` 백엔드는 OpenClaw Skills 스냅샷을 두 가지 방식으로 받습니다. 하나는 추가된 시스템 프롬프트의 간결한 OpenClaw Skills 카탈로그이고, 다른 하나는 `--plugin-dir`로 전달되는 임시 Claude Code Plugin입니다. 이 Plugin에는 해당 에이전트/세션에 적합한 Skills만 포함되므로, Claude Code의 네이티브 skill resolver는 OpenClaw가 프롬프트에서 광고했을 것과 동일한 필터링된 집합을 보게 됩니다. Skill env/API 키 오버라이드는 여전히 OpenClaw가 실행 시 자식 프로세스 환경에 적용합니다. + +OpenClaw가 번들 `claude-cli` 백엔드를 사용하려면, Claude Code 자체가 이미 같은 호스트에서 로그인되어 있어야 합니다. + +```bash +claude auth login +claude auth status --text +openclaw models auth login --provider anthropic --method cli --set-default +``` + +`claude` 바이너리가 이미 `PATH`에 없을 때만 `agents.defaults.cliBackends.claude-cli.command`를 사용하세요. ## 세션 -- CLI가 세션을 지원하면 `sessionArg`(예: `--session-id`) 또는 - ID를 여러 플래그에 삽입해야 할 때 `sessionArgs`(placeholder `{sessionId}`)를 설정하세요. -- CLI가 다른 플래그를 사용하는 **resume 하위 명령**을 쓴다면 - `resumeArgs`(재개 시 `args`를 대체)와 선택적으로 `resumeOutput` - (JSON이 아닌 재개용)를 설정하세요. +- CLI가 세션을 지원하면 `sessionArg`(예: `--session-id`) 또는, ID를 여러 플래그에 삽입해야 할 때 `sessionArgs`(플레이스홀더 `{sessionId}`)를 설정하세요. +- CLI가 서로 다른 플래그를 가진 **resume 하위 명령**을 사용한다면, `resumeArgs`(`args`를 대체)를 설정하고 필요하다면 `resumeOutput`(JSON이 아닌 resume용)을 설정하세요. - `sessionMode`: - - `always`: 항상 세션 id를 전송(저장된 값이 없으면 새 UUID). - - `existing`: 이전에 저장된 세션 id가 있을 때만 전송. - - `none`: 세션 id를 전송하지 않음. -- `claude-cli`의 기본값은 `liveSession: "claude-stdio"`, `output: "jsonl"`, - `input: "stdin"`이므로 후속 턴은 활성 상태일 때 라이브 Claude 프로세스를 재사용합니다. - 이제 warm stdio가 기본값이며, transport 필드를 생략한 사용자 지정 구성에도 적용됩니다. - Gateway가 재시작되거나 유휴 프로세스가 종료되면, - OpenClaw은 저장된 Claude 세션 id로 재개합니다. 저장된 세션 - id는 재개 전에 읽을 수 있는 기존 프로젝트 transcript와 대조 검증되므로, - phantom 바인딩은 `--resume` 아래에서 조용히 새 Claude CLI 세션을 시작하는 대신 `reason=transcript-missing`과 함께 제거됩니다. -- 저장된 CLI 세션은 provider 소유 연속성입니다. 암시적인 일일 세션 - 재설정은 이를 끊지 않으며, `/reset`과 명시적인 `session.reset` 정책은 끊습니다. + - `always`: 항상 세션 id를 전송합니다(저장된 값이 없으면 새 UUID 생성). + - `existing`: 이전에 저장된 세션 id가 있을 때만 전송합니다. + - `none`: 세션 id를 전송하지 않습니다. +- `claude-cli`는 기본값으로 `liveSession: "claude-stdio"`, `output: "jsonl"`, `input: "stdin"`을 사용하므로 후속 턴에서 활성 상태의 Claude 프로세스를 재사용합니다. 이제 따뜻한 stdio가 기본값이며, 전송 필드를 생략한 사용자 정의 구성에도 적용됩니다. Gateway가 재시작되거나 유휴 프로세스가 종료되면 OpenClaw는 저장된 Claude 세션 id에서 재개합니다. 저장된 세션 id는 재개 전에 기존의 읽을 수 있는 프로젝트 transcript와 대조하여 검증되므로, 허상 바인딩은 `--resume`으로 조용히 새 Claude CLI 세션을 시작하는 대신 `reason=transcript-missing`과 함께 정리됩니다. +- 저장된 CLI 세션은 provider가 소유하는 연속성입니다. 암묵적인 일일 세션 재설정은 이를 끊지 않으며, `/reset` 및 명시적 `session.reset` 정책만 끊습니다. -직렬화 참고: +직렬화 관련 참고: -- `serialize: true`는 같은 lane의 실행 순서를 유지합니다. +- `serialize: true`는 동일 lane의 실행 순서를 유지합니다. - 대부분의 CLI는 하나의 provider lane에서 직렬화합니다. -- OpenClaw은 선택된 인증 식별자가 바뀌면 저장된 CLI 세션 재사용을 중단합니다. - 여기에는 변경된 auth profile id, 정적 API key, 정적 토큰, - 또는 CLI가 이를 노출하는 경우 OAuth 계정 식별자가 포함됩니다. OAuth access 및 refresh token - 회전은 저장된 CLI 세션을 끊지 않습니다. CLI가 안정적인 OAuth 계정 id를 노출하지 않는다면, OpenClaw은 해당 CLI가 재개 권한을 강제하도록 둡니다. +- OpenClaw는 선택된 인증 ID가 변경되면 저장된 CLI 세션 재사용을 중단합니다. 여기에는 auth profile id 변경, 정적 API 키, 정적 토큰, 또는 CLI가 이를 노출하는 경우 OAuth 계정 ID 변경이 포함됩니다. OAuth 액세스 및 리프레시 토큰 순환은 저장된 CLI 세션을 끊지 않습니다. CLI가 안정적인 OAuth 계정 id를 노출하지 않으면, OpenClaw는 해당 CLI가 resume 권한을 강제하도록 둡니다. -## 이미지(전달) +## 이미지(패스스루) -CLI가 이미지 경로를 받을 수 있다면 `imageArg`를 설정하세요: +CLI가 이미지 경로를 받을 수 있다면 `imageArg`를 설정하세요. ```json5 imageArg: "--image", imageMode: "repeat" ``` -OpenClaw은 base64 이미지를 임시 파일로 기록합니다. `imageArg`가 설정되어 있으면 해당 -경로가 CLI 인수로 전달됩니다. `imageArg`가 없으면 OpenClaw은 -파일 경로를 프롬프트에 추가(path injection)하며, 이는 일반 경로에서 로컬 파일을 자동 로드하는 CLI에는 충분합니다. +OpenClaw는 base64 이미지를 임시 파일에 기록합니다. `imageArg`가 설정되어 있으면 해당 경로가 CLI 인수로 전달됩니다. `imageArg`가 없으면 OpenClaw는 파일 경로를 프롬프트에 추가(path injection)하며, 이는 일반 경로로부터 로컬 파일을 자동 로드하는 CLI에는 충분합니다. ## 입력 / 출력 - `output: "json"`(기본값)은 JSON을 파싱하고 텍스트 + 세션 id를 추출하려고 시도합니다. -- Gemini CLI JSON 출력의 경우, OpenClaw은 `usage`가 없거나 비어 있을 때 - `response`에서 답글 텍스트를, `stats`에서 사용량을 읽습니다. -- `output: "jsonl"`은 JSONL 스트림(예: Codex CLI `--json`)을 파싱하고 최종 agent 메시지와 - 존재하는 경우 세션 식별자를 추출합니다. +- Gemini CLI JSON 출력의 경우, `usage`가 없거나 비어 있으면 OpenClaw는 `response`에서 응답 텍스트를, `stats`에서 사용량을 읽습니다. +- `output: "jsonl"`은 JSONL 스트림(예: Codex CLI `--json`)을 파싱하고 존재할 경우 최종 에이전트 메시지와 세션 식별자를 추출합니다. - `output: "text"`는 stdout을 최종 응답으로 취급합니다. 입력 모드: - `input: "arg"`(기본값)은 프롬프트를 마지막 CLI 인수로 전달합니다. -- `input: "stdin"`은 프롬프트를 stdin으로 보냅니다. +- `input: "stdin"`은 프롬프트를 stdin으로 전송합니다. - 프롬프트가 매우 길고 `maxPromptArgChars`가 설정되어 있으면 stdin이 사용됩니다. -## 기본값(plugin 소유) +## 기본값(Plugin 소유) -번들된 OpenAI plugin은 `codex-cli`에 대한 기본값도 등록합니다: +번들 OpenAI Plugin은 `codex-cli`에 대한 기본값도 등록합니다. - `command: "codex"` - `args: ["exec","--json","--color","never","--sandbox","workspace-write","--skip-git-repo-check"]` @@ -238,7 +216,7 @@ OpenClaw은 base64 이미지를 임시 파일로 기록합니다. `imageArg`가 - `imageArg: "--image"` - `sessionMode: "existing"` -번들된 Google plugin도 `google-gemini-cli`에 대한 기본값을 등록합니다: +번들 Google Plugin은 `google-gemini-cli`에 대한 기본값도 등록합니다. - `command: "gemini"` - `args: ["--output-format", "json", "--prompt", "{prompt}"]` @@ -249,31 +227,27 @@ OpenClaw은 base64 이미지를 임시 파일로 기록합니다. `imageArg`가 - `sessionMode: "existing"` - `sessionIdFields: ["session_id", "sessionId"]` -전제 조건: 로컬 Gemini CLI가 설치되어 있어야 하며 -PATH에서 `gemini`로 사용할 수 있어야 합니다(`brew install gemini-cli` 또는 -`npm install -g @google/gemini-cli`). +전제 조건: 로컬 Gemini CLI가 설치되어 있고 `PATH`에서 `gemini`로 사용할 수 있어야 합니다(`brew install gemini-cli` 또는 `npm install -g @google/gemini-cli`). Gemini CLI JSON 참고: -- 답글 텍스트는 JSON `response` 필드에서 읽습니다. -- `usage`가 없거나 비어 있으면 사용량은 `stats`로 대체됩니다. +- 응답 텍스트는 JSON `response` 필드에서 읽습니다. +- 사용량은 `usage`가 없거나 비어 있을 때 `stats`로 대체됩니다. - `stats.cached`는 OpenClaw `cacheRead`로 정규화됩니다. -- `stats.input`이 없으면 OpenClaw은 - `stats.input_tokens - stats.cached`에서 입력 토큰을 계산합니다. +- `stats.input`이 없으면 OpenClaw는 `stats.input_tokens - stats.cached`에서 입력 토큰 수를 도출합니다. -필요한 경우에만 override하세요(일반적인 경우: 절대 `command` 경로). +필요한 경우에만 오버라이드하세요(일반적으로는 절대 `command` 경로). -## plugin 소유 기본값 +## Plugin 소유 기본값 -CLI 백엔드 기본값은 이제 plugin 표면의 일부입니다: +이제 CLI 백엔드 기본값은 Plugin 표면의 일부입니다. - Plugins는 `api.registerCliBackend(...)`로 이를 등록합니다. -- 백엔드 `id`는 model ref의 provider 접두사가 됩니다. -- `agents.defaults.cliBackends.` 아래의 사용자 구성은 여전히 plugin 기본값을 override합니다. -- 백엔드별 구성 정리는 선택적 - `normalizeConfig` hook을 통해 plugin 소유로 유지됩니다. +- 백엔드 `id`는 모델 ref의 provider 접두사가 됩니다. +- `agents.defaults.cliBackends.`의 사용자 구성은 여전히 Plugin 기본값을 오버라이드합니다. +- 백엔드별 구성 정리는 선택적 `normalizeConfig` 훅을 통해 Plugin이 계속 소유합니다. -작은 프롬프트/메시지 호환성 shim이 필요한 plugins는 provider나 CLI 백엔드를 교체하지 않고도 양방향 텍스트 변환을 선언할 수 있습니다: +작은 프롬프트/메시지 호환성 shim이 필요한 Plugins는 provider나 CLI 백엔드를 교체하지 않고 양방향 텍스트 변환을 선언할 수 있습니다. ```typescript api.registerTextTransforms({ @@ -290,49 +264,41 @@ api.registerTextTransforms({ }); ``` -`input`은 CLI에 전달되는 시스템 프롬프트와 사용자 프롬프트를 재작성합니다. `output`은 OpenClaw이 자체 제어 마커와 채널 전달을 처리하기 전에 스트리밍된 assistant delta와 파싱된 최종 텍스트를 재작성합니다. +`input`은 CLI에 전달되는 시스템 프롬프트와 사용자 프롬프트를 다시 씁니다. `output`은 OpenClaw가 자체 제어 마커와 채널 전달을 처리하기 전에 스트리밍된 어시스턴트 델타와 파싱된 최종 텍스트를 다시 씁니다. -Claude Code stream-json 호환 JSONL을 출력하는 CLI의 경우, -해당 백엔드 구성에 `jsonlDialect: "claude-stream-json"`를 설정하세요. +Claude Code stream-json 호환 JSONL을 출력하는 CLI의 경우, 해당 백엔드 구성에 `jsonlDialect: "claude-stream-json"`를 설정하세요. ## 번들 MCP 오버레이 -CLI 백엔드는 OpenClaw 도구 호출을 **직접** 받지 않지만, 백엔드는 -`bundleMcp: true`로 생성된 MCP 구성 오버레이에 옵트인할 수 있습니다. +CLI 백엔드는 OpenClaw 도구 호출을 **직접** 받지 않지만, 백엔드는 `bundleMcp: true`로 생성된 MCP 구성 오버레이를 선택적으로 사용할 수 있습니다. 현재 번들 동작: -- `claude-cli`: 생성된 strict MCP 구성 파일 -- `codex-cli`: `mcp_servers`용 인라인 구성 override -- `google-gemini-cli`: 생성된 Gemini 시스템 설정 파일 +- `claude-cli`: 생성된 strict MCP config 파일 +- `codex-cli`: `mcp_servers`에 대한 인라인 config 오버라이드 +- `google-gemini-cli`: 생성된 Gemini 시스템 settings 파일 -bundle MCP가 활성화되면 OpenClaw은: +bundle MCP가 활성화되면, OpenClaw는 다음을 수행합니다. -- gateway 도구를 CLI 프로세스에 노출하는 loopback HTTP MCP 서버를 실행합니다 -- 세션별 토큰(`OPENCLAW_MCP_TOKEN`)으로 브리지를 인증합니다 -- 현재 세션, 계정, 채널 컨텍스트로 도구 액세스를 제한합니다 -- 현재 워크스페이스에 대해 활성화된 bundle-MCP 서버를 로드합니다 -- 이를 기존 백엔드 MCP 구성/설정 형태와 병합합니다 -- 소유 extension의 백엔드 소유 통합 모드를 사용해 실행 구성을 재작성합니다 +- CLI 프로세스에 gateway 도구를 노출하는 loopback HTTP MCP 서버를 시작합니다. +- 세션별 토큰(`OPENCLAW_MCP_TOKEN`)으로 브리지를 인증합니다. +- 도구 액세스를 현재 세션, 계정, 채널 컨텍스트로 제한합니다. +- 현재 작업공간에 대해 활성화된 bundle-MCP 서버를 로드합니다. +- 기존 백엔드 MCP config/settings 형태와 병합합니다. +- 소유 extension의 백엔드 소유 통합 모드를 사용해 실행 구성을 다시 씁니다. -활성화된 MCP 서버가 없더라도, 백엔드가 bundle MCP에 옵트인한 경우 -백그라운드 실행이 격리된 상태를 유지하도록 OpenClaw은 여전히 strict 구성을 주입합니다. +활성화된 MCP 서버가 없더라도, 백엔드가 bundle MCP를 선택하면 OpenClaw는 백그라운드 실행이 격리된 상태를 유지하도록 여전히 strict config를 주입합니다. ## 제한 사항 -- **직접적인 OpenClaw 도구 호출은 없습니다.** OpenClaw은 CLI 백엔드 프로토콜에 - 도구 호출을 주입하지 않습니다. 백엔드는 `bundleMcp: true`에 옵트인한 경우에만 - gateway 도구를 볼 수 있습니다. -- **스트리밍은 백엔드별입니다.** 일부 백엔드는 JSONL을 스트리밍하고, 다른 백엔드는 - 종료 시점까지 버퍼링합니다. +- **직접적인 OpenClaw 도구 호출은 없습니다.** OpenClaw는 CLI 백엔드 프로토콜에 도구 호출을 주입하지 않습니다. 백엔드는 `bundleMcp: true`를 선택한 경우에만 gateway 도구를 볼 수 있습니다. +- **스트리밍은 백엔드마다 다릅니다.** 일부 백엔드는 JSONL을 스트리밍하고, 다른 백엔드는 종료될 때까지 버퍼링합니다. - **구조화된 출력**은 CLI의 JSON 형식에 따라 달라집니다. -- **Codex CLI 세션**은 텍스트 출력으로 재개되며(JSONL 아님), 이는 초기 `--json` 실행보다 - 구조화 수준이 낮습니다. OpenClaw 세션 자체는 여전히 정상적으로 동작합니다. +- **Codex CLI 세션**은 텍스트 출력으로 재개되며(JSONL 없음), 이는 초기 `--json` 실행보다 구조화 수준이 낮습니다. OpenClaw 세션은 여전히 정상적으로 동작합니다. ## 문제 해결 - **CLI를 찾을 수 없음**: `command`를 전체 경로로 설정하세요. - **잘못된 모델 이름**: `modelAliases`를 사용해 `provider/model` → CLI 모델로 매핑하세요. -- **세션 연속성이 없음**: `sessionArg`가 설정되어 있고 `sessionMode`가 - `none`이 아닌지 확인하세요(Codex CLI는 현재 JSON 출력으로 재개할 수 없습니다). -- **이미지가 무시됨**: `imageArg`를 설정하세요(그리고 CLI가 파일 경로를 지원하는지 확인하세요). +- **세션 연속성이 없음**: `sessionArg`가 설정되어 있고 `sessionMode`가 `none`이 아닌지 확인하세요(Codex CLI는 현재 JSON 출력으로 재개할 수 없습니다). +- **이미지가 무시됨**: `imageArg`를 설정하고(그리고 CLI가 파일 경로를 지원하는지 확인하세요). diff --git a/docs/ko/help/testing.md b/docs/ko/help/testing.md index 32f15998c..ebe4d652c 100644 --- a/docs/ko/help/testing.md +++ b/docs/ko/help/testing.md @@ -1,15 +1,15 @@ --- read_when: - 로컬 또는 CI에서 테스트 실행하기 - - 모델/provider 버그에 대한 회귀 테스트 추가하기 - - Gateway + 에이전트 동작 디버깅 + - 모델/프로바이더 버그에 대한 회귀 테스트 추가하기 + - Gateway + 에이전트 동작 디버깅하기 summary: '테스트 키트: unit/e2e/live 스위트, Docker 러너, 그리고 각 테스트가 다루는 내용' title: 테스트 x-i18n: - generated_at: "2026-04-23T14:03:59Z" + generated_at: "2026-04-23T14:55:15Z" model: gpt-5.4 provider: openai - source_hash: fe0e9bdea78cba7e512358d2e4d428da04a2071188e74af2d5419d2c85eafe15 + source_hash: fbec4996699577321116c94f60c01d205d7594ed41aca27c821f1c3d65a7dca3 source_path: help/testing.md workflow: 15 --- @@ -18,167 +18,167 @@ x-i18n: OpenClaw에는 세 가지 Vitest 스위트(unit/integration, e2e, live)와 소수의 Docker 러너가 있습니다. -이 문서는 “어떻게 테스트하는가”에 대한 가이드입니다. +이 문서는 “우리가 테스트하는 방법” 가이드입니다: -- 각 스위트가 무엇을 다루는지(그리고 의도적으로 무엇을 다루지 않는지) -- 일반적인 워크플로(로컬, 푸시 전, 디버깅)에서 어떤 명령을 실행할지 -- live 테스트가 자격 증명을 어떻게 찾고 모델/provider를 어떻게 선택하는지 -- 실제 모델/provider 문제에 대한 회귀 테스트를 추가하는 방법 +- 각 스위트가 다루는 범위(그리고 의도적으로 _다루지 않는_ 범위) +- 일반적인 워크플로(로컬, 푸시 전, 디버깅)에 어떤 명령을 실행해야 하는지 +- live 테스트가 자격 증명을 어떻게 찾고 모델/프로바이더를 어떻게 선택하는지 +- 실제 모델/프로바이더 이슈에 대한 회귀 테스트를 어떻게 추가하는지 ## 빠른 시작 대부분의 날에는: -- 전체 게이트(푸시 전 예상): `pnpm build && pnpm check && pnpm check:test-types && pnpm test` +- 전체 게이트(푸시 전에 예상됨): `pnpm build && pnpm check && pnpm check:test-types && pnpm test` - 여유 있는 머신에서 더 빠른 로컬 전체 스위트 실행: `pnpm test:max` -- 직접 Vitest watch 루프: `pnpm test:watch` -- 직접 파일 타기팅은 이제 extension/channel 경로도 라우팅합니다: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` -- 단일 실패를 반복하는 중이라면 먼저 대상 범위를 좁힌 실행을 선호하세요. +- 직접 Vitest watch 루프 실행: `pnpm test:watch` +- 이제 직접 파일 지정 시 extension/channel 경로도 라우팅됨: `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts` +- 단일 실패를 반복 작업 중이라면 먼저 대상 범위를 좁혀 실행하는 방식을 권장합니다. - Docker 기반 QA 사이트: `pnpm qa:lab:up` - Linux VM 기반 QA 레인: `pnpm openclaw qa suite --runner multipass --scenario channel-chat-baseline` -테스트를 건드렸거나 더 높은 신뢰가 필요할 때: +테스트를 수정했거나 추가 확신이 필요할 때: - 커버리지 게이트: `pnpm test:coverage` - E2E 스위트: `pnpm test:e2e` -실제 provider/모델을 디버깅할 때(실제 자격 증명 필요): +실제 프로바이더/모델을 디버깅할 때(실제 자격 증명 필요): -- live 스위트(모델 + Gateway 도구/이미지 프로브): `pnpm test:live` -- 하나의 live 파일만 조용히 타기팅: `pnpm test:live -- src/agents/models.profiles.live.test.ts` +- Live 스위트(모델 + Gateway 도구/이미지 프로브): `pnpm test:live` +- 하나의 live 파일만 조용히 대상으로 지정: `pnpm test:live -- src/agents/models.profiles.live.test.ts` - Docker live 모델 스윕: `pnpm test:docker:live-models` + - 이제 선택된 각 모델은 텍스트 턴 하나와 작은 파일 읽기 스타일 프로브를 실행합니다. + 메타데이터에서 `image` 입력을 광고하는 모델은 작은 이미지 턴도 실행합니다. + 프로바이더 실패를 격리할 때는 `OPENCLAW_LIVE_MODEL_FILE_PROBE=0` 또는 + `OPENCLAW_LIVE_MODEL_IMAGE_PROBE=0`으로 추가 프로브를 비활성화하세요. - CI 커버리지: 일일 `OpenClaw Scheduled Live And E2E Checks`와 수동 - `OpenClaw Release Checks`는 둘 다 재사용 가능한 live/E2E 워크플로를 - `include_live_suites: true`와 함께 호출하며, 여기에는 provider별로 샤딩된 - 별도의 Docker live 모델 매트릭스 작업이 포함됩니다. - - 집중된 CI 재실행의 경우 `OpenClaw Live And E2E Checks (Reusable)`를 - `include_live_suites: true`와 `live_models_only: true`로 디스패치하세요. - - 새 고신호 provider 시크릿은 `scripts/ci-hydrate-live-auth.sh`와 + `OpenClaw Release Checks`는 모두 `include_live_suites: true`로 재사용 가능한 live/E2E 워크플로를 호출하며, + 여기에는 프로바이더별로 샤딩된 별도 Docker live 모델 + 매트릭스 작업이 포함됩니다. + - 집중된 CI 재실행을 위해서는 `OpenClaw Live And E2E Checks (Reusable)`를 + `include_live_suites: true` 및 `live_models_only: true`로 디스패치하세요. + - 새로운 고신호 프로바이더 시크릿을 추가할 때는 `scripts/ci-hydrate-live-auth.sh`와 `.github/workflows/openclaw-live-and-e2e-checks-reusable.yml` 및 그 - scheduled/release 호출자에 추가하세요. -- Moonshot/Kimi 비용 스모크: `MOONSHOT_API_KEY`를 설정한 상태에서 - `openclaw models list --provider moonshot --json`를 실행한 뒤, `moonshot/kimi-k2.6`에 대해 - 격리된 `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json` + scheduled/release 호출자도 함께 추가하세요. +- Moonshot/Kimi 비용 스모크: `MOONSHOT_API_KEY`가 설정된 상태에서 + `openclaw models list --provider moonshot --json`를 실행한 다음, `moonshot/kimi-k2.6`에 대해 + 분리된 + `openclaw agent --local --session-id live-kimi-cost --message 'Reply exactly: KIMI_LIVE_OK' --thinking off --json` 를 실행하세요. JSON이 Moonshot/K2.6을 보고하는지, 그리고 - 어시스턴트 transcript가 정규화된 `usage.cost`를 저장하는지 확인하세요. + assistant transcript에 정규화된 `usage.cost`가 저장되는지 확인하세요. -팁: 하나의 실패 사례만 필요할 때는 아래에 설명된 allowlist env vars로 live 테스트 범위를 좁히는 방식을 선호하세요. +팁: 실패하는 케이스 하나만 필요하다면, 아래에 설명된 allowlist 환경 변수를 통해 live 테스트 범위를 좁히는 방식을 권장합니다. ## QA 전용 러너 -이 명령들은 QA-lab 수준의 현실성이 필요할 때 메인 테스트 스위트 옆에 있습니다. +이 명령들은 QA-lab 수준의 현실성이 필요할 때 메인 테스트 스위트 옆에서 사용합니다: -CI는 전용 워크플로에서 QA Lab을 실행합니다. `Parity gate`는 일치하는 PR과 -수동 디스패치에서 mock provider로 실행됩니다. `QA-Lab - All Lanes`는 매일 밤 `main`에서, -그리고 수동 디스패치에서는 mock parity gate, live Matrix 레인, Convex 관리 live Telegram 레인을 -병렬 작업으로 실행합니다. `OpenClaw Release Checks`는 릴리스 승인 전에 동일한 레인을 실행합니다. +CI는 전용 워크플로에서 QA Lab을 실행합니다. `Parity gate`는 일치하는 PR에서 실행되며, +mock 프로바이더를 사용한 수동 디스패치에서도 실행됩니다. `QA-Lab - All Lanes`는 +`main`에서 야간 실행되며, mock parity gate, live Matrix 레인, 그리고 +Convex로 관리되는 live Telegram 레인을 병렬 작업으로 포함한 수동 디스패치에서도 실행됩니다. `OpenClaw Release Checks`는 릴리스 승인 전에 동일한 레인들을 실행합니다. - `pnpm openclaw qa suite` - - 저장소 기반 QA 시나리오를 호스트에서 직접 실행합니다. - - 기본적으로 격리된 Gateway 워커와 함께 여러 선택된 시나리오를 병렬로 실행합니다. - `qa-channel`은 기본 동시성 4를 사용합니다(선택된 시나리오 수에 의해 제한됨). - 워커 수를 조정하려면 `--concurrency `를, 이전 직렬 레인을 원하면 `--concurrency 1`을 사용하세요. - - 어떤 시나리오라도 실패하면 0이 아닌 종료 코드를 반환합니다. 실패 종료 코드 없이 아티팩트를 원하면 `--allow-failures`를 사용하세요. - - provider 모드 `live-frontier`, `mock-openai`, `aimock`를 지원합니다. - `aimock`은 시나리오 인식 `mock-openai` 레인을 대체하지 않으면서, - 실험적인 픽스처 및 프로토콜 mock 커버리지를 위해 로컬 AIMock 기반 provider 서버를 시작합니다. + - 호스트에서 직접 repo 기반 QA 시나리오를 실행합니다. + - 기본적으로 여러 선택된 시나리오를 격리된 + Gateway 워커와 함께 병렬로 실행합니다. `qa-channel`은 기본적으로 동시성 4를 사용합니다 + (선택된 시나리오 수에 의해 제한됨). 워커 수를 조정하려면 `--concurrency `를 사용하고, + 이전 직렬 레인을 사용하려면 `--concurrency 1`을 사용하세요. + - 어떤 시나리오라도 실패하면 0이 아닌 코드로 종료됩니다. 실패 종료 코드 없이 아티팩트가 필요하면 `--allow-failures`를 사용하세요. + - `live-frontier`, `mock-openai`, `aimock` 프로바이더 모드를 지원합니다. + `aimock`은 실험적 + 픽스처 및 프로토콜 목 커버리지를 위해 로컬 AIMock 기반 프로바이더 서버를 시작하지만, + 시나리오 인지형 `mock-openai` 레인을 대체하지는 않습니다. - `pnpm openclaw qa suite --runner multipass` - - 동일한 QA 스위트를 일회용 Multipass Linux VM 안에서 실행합니다. + - 동일한 QA 스위트를 일회용 Multipass Linux VM 내부에서 실행합니다. - 호스트의 `qa suite`와 동일한 시나리오 선택 동작을 유지합니다. - - `qa suite`와 동일한 provider/모델 선택 플래그를 재사용합니다. - - live 실행은 게스트에서 실용적인 지원 QA 인증 입력을 전달합니다: - env 기반 provider 키, QA live provider config 경로, 그리고 존재할 경우 `CODEX_HOME`. - - 게스트가 마운트된 워크스페이스를 통해 다시 쓸 수 있도록 출력 디렉터리는 저장소 루트 아래에 있어야 합니다. - - 일반 QA 보고서 + 요약과 함께 Multipass 로그를 `.artifacts/qa-e2e/...` 아래에 기록합니다. + - `qa suite`와 동일한 프로바이더/모델 선택 플래그를 재사용합니다. + - Live 실행은 게스트에 전달하기 실용적인 지원 QA 인증 입력을 전달합니다: + 환경 변수 기반 프로바이더 키, QA live 프로바이더 설정 경로, 그리고 존재하는 경우 `CODEX_HOME`. + - 게스트가 마운트된 워크스페이스를 통해 다시 쓸 수 있도록 출력 디렉터리는 반드시 repo 루트 아래에 있어야 합니다. + - `.artifacts/qa-e2e/...` 아래에 일반 QA 리포트 + 요약과 Multipass 로그를 기록합니다. - `pnpm qa:lab:up` - 운영자 스타일 QA 작업을 위한 Docker 기반 QA 사이트를 시작합니다. - `pnpm test:docker:npm-onboard-channel-agent` - - 현재 체크아웃에서 npm tarball을 빌드하고, 이를 Docker에 전역 설치한 다음, + - 현재 체크아웃에서 npm tarball을 빌드하고, 이를 Docker에서 전역 설치한 뒤, 비대화형 OpenAI API 키 온보딩을 실행하고, 기본적으로 Telegram을 구성하며, - plugin 활성화가 필요 시 런타임 의존성을 주문형으로 설치하는지 검증하고, - doctor를 실행한 뒤 mock OpenAI 엔드포인트에 대해 하나의 로컬 에이전트 턴을 실행합니다. - - Discord로 동일한 패키지 설치 레인을 실행하려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용하세요. + plugin 활성화가 필요 시 런타임 의존성을 즉시 설치하는지 검증하고, + doctor를 실행하며, 목된 OpenAI 엔드포인트를 대상으로 하나의 로컬 agent 턴을 실행합니다. + - 동일한 패키지 설치 레인을 Discord로 실행하려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용하세요. - `pnpm test:docker:bundled-channel-deps` - - 현재 OpenClaw 빌드를 패키징해 Docker에 설치하고, OpenAI가 구성된 상태로 Gateway를 시작한 다음, - config 편집을 통해 번들 채널/plugin을 활성화합니다. - - 설정 검색이 구성되지 않은 plugin 런타임 의존성을 그대로 두는지, - 첫 번째 구성된 Gateway 또는 doctor 실행이 각 번들 plugin의 런타임 의존성을 주문형으로 설치하는지, - 그리고 두 번째 재시작이 이미 활성화된 의존성을 재설치하지 않는지 검증합니다. - - 또한 알려진 이전 npm 기준선을 설치하고, `openclaw update --tag ` 실행 전에 Telegram을 활성화한 뒤, - 후보 버전의 업데이트 후 doctor가 하네스 측 postinstall 복구 없이 - 번들 채널 런타임 의존성을 복구하는지 검증합니다. + - 현재 OpenClaw 빌드를 Docker에서 pack 및 설치하고, OpenAI가 구성된 상태로 Gateway를 시작한 다음, + 설정 편집을 통해 번들 channel/plugins를 활성화합니다. + - 설정 탐지가 미구성 plugin 런타임 의존성을 설치하지 않은 상태로 남겨두는지, + 처음 구성된 Gateway 또는 doctor 실행 시 각 번들 plugin의 런타임 의존성을 필요 시 설치하는지, + 그리고 두 번째 재시작 시 이미 활성화된 의존성을 다시 설치하지 않는지 검증합니다. + - 또한 알려진 더 오래된 npm 기준 버전을 설치하고, Telegram을 활성화한 뒤 + `openclaw update --tag `를 실행하며, 후보 버전의 + post-update doctor가 하네스 측 postinstall 복구 없이 번들 channel 런타임 의존성을 복구하는지 검증합니다. - `pnpm openclaw qa aimock` - - 직접 프로토콜 스모크 테스트를 위해 로컬 AIMock provider 서버만 시작합니다. + - 직접 프로토콜 스모크 테스트를 위해 로컬 AIMock 프로바이더 서버만 시작합니다. - `pnpm openclaw qa matrix` - 일회용 Docker 기반 Tuwunel homeserver를 대상으로 Matrix live QA 레인을 실행합니다. - - 이 QA 호스트는 현재 저장소/개발 전용입니다. 패키지형 OpenClaw 설치에는 - `qa-lab`이 포함되지 않으므로 `openclaw qa`를 노출하지 않습니다. - - 저장소 체크아웃은 번들 러너를 직접 로드하며, 별도 plugin 설치 단계가 필요 없습니다. - - 세 개의 임시 Matrix 사용자(`driver`, `sut`, `observer`)와 하나의 비공개 방을 프로비저닝한 뒤, - 실제 Matrix plugin을 SUT 전송으로 사용하는 QA Gateway 자식 프로세스를 시작합니다. - - 기본적으로 고정된 안정 Tuwunel 이미지 `ghcr.io/matrix-construct/tuwunel:v1.5.1`를 사용합니다. - 다른 이미지를 테스트해야 할 때는 `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE`로 재정의하세요. - - Matrix는 일회용 사용자를 로컬에서 프로비저닝하므로 공유 자격 증명 소스 플래그를 노출하지 않습니다. - - Matrix QA 보고서, 요약, observed-events 아티팩트, stdout/stderr 결합 출력 로그를 - `.artifacts/qa-e2e/...` 아래에 기록합니다. + - 이 QA 호스트는 현재 repo/dev 전용입니다. 패키지된 OpenClaw 설치에는 + `qa-lab`이 포함되지 않으므로 `openclaw qa`가 노출되지 않습니다. + - Repo 체크아웃은 번들 러너를 직접 로드하므로 별도의 plugin 설치 단계가 필요하지 않습니다. + - 임시 Matrix 사용자 세 명(`driver`, `sut`, `observer`)과 비공개 room 하나를 프로비저닝한 다음, 실제 Matrix plugin을 SUT 전송 계층으로 사용하는 QA gateway child를 시작합니다. + - 기본적으로 고정된 안정 Tuwunel 이미지 `ghcr.io/matrix-construct/tuwunel:v1.5.1`를 사용합니다. 다른 이미지를 테스트해야 하면 `OPENCLAW_QA_MATRIX_TUWUNEL_IMAGE`로 재정의하세요. + - Matrix는 레인이 로컬에서 일회용 사용자를 프로비저닝하므로 공유 자격 증명 소스 플래그를 노출하지 않습니다. + - `.artifacts/qa-e2e/...` 아래에 Matrix QA 리포트, 요약, observed-events 아티팩트, 그리고 결합된 stdout/stderr 출력 로그를 기록합니다. - `pnpm openclaw qa telegram` - - env에 있는 driver 및 SUT 봇 토큰을 사용해 실제 비공개 그룹을 대상으로 Telegram live QA 레인을 실행합니다. - - `OPENCLAW_QA_TELEGRAM_GROUP_ID`, `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, - `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`가 필요합니다. 그룹 ID는 숫자형 Telegram 채팅 ID여야 합니다. - - 공유 풀 자격 증명에는 `--credential-source convex`를 지원합니다. - 기본은 env 모드를 사용하고, 풀 lease를 옵트인하려면 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`를 설정하세요. - - 어떤 시나리오라도 실패하면 0이 아닌 종료 코드를 반환합니다. 실패 종료 코드 없이 아티팩트를 원하면 `--allow-failures`를 사용하세요. - - 동일한 비공개 그룹 안에 서로 다른 두 봇이 필요하며, SUT 봇은 Telegram 사용자 이름을 노출해야 합니다. - - 안정적인 봇 대 봇 관찰을 위해 두 봇 모두에서 `@BotFather`의 Bot-to-Bot Communication Mode를 활성화하고, - driver 봇이 그룹 내 봇 트래픽을 관찰할 수 있도록 하세요. - - Telegram QA 보고서, 요약, observed-messages 아티팩트를 `.artifacts/qa-e2e/...` 아래에 기록합니다. - 답장 시나리오에는 driver 전송 요청부터 관찰된 SUT 답장까지의 RTT가 포함됩니다. + - 환경 변수의 driver 및 SUT bot 토큰을 사용해 실제 비공개 그룹을 대상으로 Telegram live QA 레인을 실행합니다. + - `OPENCLAW_QA_TELEGRAM_GROUP_ID`, `OPENCLAW_QA_TELEGRAM_DRIVER_BOT_TOKEN`, `OPENCLAW_QA_TELEGRAM_SUT_BOT_TOKEN`이 필요합니다. group id는 숫자형 Telegram chat id여야 합니다. + - 공유 풀링 자격 증명에는 `--credential-source convex`를 지원합니다. 기본적으로는 env 모드를 사용하고, 풀링된 lease를 사용하려면 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`를 설정하세요. + - 어떤 시나리오라도 실패하면 0이 아닌 코드로 종료됩니다. 실패 종료 코드 없이 아티팩트가 필요하면 `--allow-failures`를 사용하세요. + - 동일한 비공개 그룹에 속한 서로 다른 두 bot이 필요하며, SUT bot은 Telegram username을 노출해야 합니다. + - 안정적인 bot 간 관찰을 위해 두 bot 모두에서 `@BotFather`의 Bot-to-Bot Communication Mode를 활성화하고, driver bot이 그룹 bot 트래픽을 관찰할 수 있도록 하세요. + - `.artifacts/qa-e2e/...` 아래에 Telegram QA 리포트, 요약, 그리고 observed-messages 아티팩트를 기록합니다. 응답 시나리오에는 driver 전송 요청부터 관찰된 SUT 응답까지의 RTT가 포함됩니다. -live 전송 레인은 새 전송이 드리프트하지 않도록 하나의 표준 계약을 공유합니다. +Live 전송 레인은 새로운 전송 방식이 드리프트하지 않도록 하나의 표준 계약을 공유합니다: -`qa-channel`은 광범위한 합성 QA 스위트로 유지되며 live 전송 커버리지 매트릭스의 일부는 아닙니다. +`qa-channel`은 여전히 광범위한 합성 QA 스위트이며 live 전송 커버리지 매트릭스의 일부는 아닙니다. -| Lane | Canary | Mention gating | Allowlist block | Top-level reply | Restart resume | Thread follow-up | Thread isolation | Reaction observation | Help command | -| -------- | ------ | -------------- | --------------- | --------------- | -------------- | ---------------- | ---------------- | -------------------- | ------------ | -| Matrix | x | x | x | x | x | x | x | x | | -| Telegram | x | | | | | | | | x | +| 레인 | Canary | 멘션 게이팅 | Allowlist 차단 | 최상위 답장 | 재시작 재개 | 스레드 후속 응답 | 스레드 격리 | 반응 관찰 | 도움말 명령 | +| -------- | ------ | ----------- | -------------- | ----------- | ----------- | ---------------- | ----------- | --------- | ----------- | +| Matrix | x | x | x | x | x | x | x | x | | +| Telegram | x | | | | | | | | x | -### Convex를 통한 공유 Telegram 자격 증명(v1) +### Convex를 통한 공유 Telegram 자격 증명 (v1) `openclaw qa telegram`에 대해 `--credential-source convex`(또는 `OPENCLAW_QA_CREDENTIAL_SOURCE=convex`)가 활성화되면, -QA lab은 Convex 기반 풀에서 독점 lease를 획득하고, 레인이 실행되는 동안 해당 lease에 Heartbeat를 보내며, -종료 시 lease를 해제합니다. +QA lab은 Convex 기반 풀에서 독점 lease를 획득하고, +레인이 실행되는 동안 해당 lease에 Heartbeat를 보내며, 종료 시 lease를 해제합니다. -참조용 Convex 프로젝트 스캐폴드: +참고용 Convex 프로젝트 스캐폴드: - `qa/convex-credential-broker/` -필수 env vars: +필수 환경 변수: -- `OPENCLAW_QA_CONVEX_SITE_URL`(예: `https://your-deployment.convex.site`) -- 선택된 역할용 시크릿 하나: - - `maintainer`에는 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER` - - `ci`에는 `OPENCLAW_QA_CONVEX_SECRET_CI` +- `OPENCLAW_QA_CONVEX_SITE_URL` (예: `https://your-deployment.convex.site`) +- 선택한 역할에 대한 시크릿 하나: + - `maintainer`용 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER` + - `ci`용 `OPENCLAW_QA_CONVEX_SECRET_CI` - 자격 증명 역할 선택: - CLI: `--credential-role maintainer|ci` - - env 기본값: `OPENCLAW_QA_CREDENTIAL_ROLE`(CI에서는 기본 `ci`, 그 외에는 `maintainer`) + - 환경 변수 기본값: `OPENCLAW_QA_CREDENTIAL_ROLE` (CI에서는 기본값 `ci`, 그 외에는 `maintainer`) -선택적 env vars: +선택적 환경 변수: -- `OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS`(기본값 `1200000`) -- `OPENCLAW_QA_CREDENTIAL_HEARTBEAT_INTERVAL_MS`(기본값 `30000`) -- `OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS`(기본값 `90000`) -- `OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS`(기본값 `15000`) -- `OPENCLAW_QA_CONVEX_ENDPOINT_PREFIX`(기본값 `/qa-credentials/v1`) -- `OPENCLAW_QA_CREDENTIAL_OWNER_ID`(선택적 추적 ID) +- `OPENCLAW_QA_CREDENTIAL_LEASE_TTL_MS` (기본값 `1200000`) +- `OPENCLAW_QA_CREDENTIAL_HEARTBEAT_INTERVAL_MS` (기본값 `30000`) +- `OPENCLAW_QA_CREDENTIAL_ACQUIRE_TIMEOUT_MS` (기본값 `90000`) +- `OPENCLAW_QA_CREDENTIAL_HTTP_TIMEOUT_MS` (기본값 `15000`) +- `OPENCLAW_QA_CONVEX_ENDPOINT_PREFIX` (기본값 `/qa-credentials/v1`) +- `OPENCLAW_QA_CREDENTIAL_OWNER_ID` (선택적 추적 id) - `OPENCLAW_QA_ALLOW_INSECURE_HTTP=1`은 로컬 전용 개발을 위해 loopback `http://` Convex URL을 허용합니다. -일반 운영에서는 `OPENCLAW_QA_CONVEX_SITE_URL`에 `https://`를 사용해야 합니다. +정상 운영에서는 `OPENCLAW_QA_CONVEX_SITE_URL`이 `https://`를 사용해야 합니다. -유지관리자 관리자 명령(pool add/remove/list)에는 -특히 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`가 필요합니다. +메인테이너 관리자 명령(pool 추가/제거/목록)은 +반드시 `OPENCLAW_QA_CONVEX_SECRET_MAINTAINER`를 사용해야 합니다. -유지관리자용 CLI 도우미: +메인테이너용 CLI 헬퍼: ```bash pnpm openclaw qa credentials add --kind telegram --payload-file qa/telegram-credential.json @@ -186,9 +186,9 @@ pnpm openclaw qa credentials list --kind telegram pnpm openclaw qa credentials remove --credential-id ``` -스크립트와 CI 유틸리티에서 기계가 읽기 쉬운 출력을 원하면 `--json`을 사용하세요. +스크립트와 CI 유틸리티에서 기계 판독 가능한 출력을 원하면 `--json`을 사용하세요. -기본 엔드포인트 계약(`OPENCLAW_QA_CONVEX_SITE_URL` + `/qa-credentials/v1`): +기본 엔드포인트 계약 (`OPENCLAW_QA_CONVEX_SITE_URL` + `/qa-credentials/v1`): - `POST /acquire` - 요청: `{ kind, ownerId, actorRole, leaseTtlMs, heartbeatIntervalMs }` @@ -196,77 +196,77 @@ pnpm openclaw qa credentials remove --credential-id - 소진/재시도 가능: `{ status: "error", code: "POOL_EXHAUSTED" | "NO_CREDENTIAL_AVAILABLE", ... }` - `POST /heartbeat` - 요청: `{ kind, ownerId, actorRole, credentialId, leaseToken, leaseTtlMs }` - - 성공: `{ status: "ok" }`(또는 빈 `2xx`) + - 성공: `{ status: "ok" }` (또는 빈 `2xx`) - `POST /release` - 요청: `{ kind, ownerId, actorRole, credentialId, leaseToken }` - - 성공: `{ status: "ok" }`(또는 빈 `2xx`) -- `POST /admin/add`(유지관리자 시크릿 전용) + - 성공: `{ status: "ok" }` (또는 빈 `2xx`) +- `POST /admin/add` (메인테이너 시크릿 전용) - 요청: `{ kind, actorId, payload, note?, status? }` - 성공: `{ status: "ok", credential }` -- `POST /admin/remove`(유지관리자 시크릿 전용) +- `POST /admin/remove` (메인테이너 시크릿 전용) - 요청: `{ credentialId, actorId }` - 성공: `{ status: "ok", changed, credential }` - 활성 lease 가드: `{ status: "error", code: "LEASE_ACTIVE", ... }` -- `POST /admin/list`(유지관리자 시크릿 전용) +- `POST /admin/list` (메인테이너 시크릿 전용) - 요청: `{ kind?, status?, includePayload?, limit? }` - 성공: `{ status: "ok", credentials, count }` -Telegram kind용 payload 형태: +Telegram kind용 페이로드 형태: - `{ groupId: string, driverToken: string, sutToken: string }` -- `groupId`는 숫자형 Telegram 채팅 ID 문자열이어야 합니다. -- `admin/add`는 `kind: "telegram"`에 대해 이 형태를 검증하고 잘못된 payload를 거부합니다. +- `groupId`는 숫자형 Telegram chat id 문자열이어야 합니다. +- `admin/add`는 `kind: "telegram"`에 대해 이 형태를 검증하고, 잘못된 형식의 페이로드는 거부합니다. -### QA에 채널 추가하기 +### QA에 channel 추가하기 -markdown QA 시스템에 채널을 추가하려면 정확히 두 가지가 필요합니다. +마크다운 QA 시스템에 channel을 추가하려면 정확히 두 가지가 필요합니다: -1. 해당 채널용 전송 어댑터 -2. 채널 계약을 실행하는 시나리오 팩 +1. 해당 channel용 전송 어댑터 +2. channel 계약을 검증하는 시나리오 팩 -공유 `qa-lab` 호스트가 흐름을 소유할 수 있는데도 새 최상위 QA 명령 루트를 추가하지 마세요. +공유 `qa-lab` 호스트가 흐름을 소유할 수 있는 경우, 새로운 최상위 QA 명령 루트를 추가하지 마세요. -`qa-lab`은 공유 호스트 메커니즘을 소유합니다. +`qa-lab`은 공유 호스트 메커니즘을 소유합니다: - `openclaw qa` 명령 루트 -- 스위트 시작과 종료 +- 스위트 시작 및 종료 - 워커 동시성 - 아티팩트 기록 -- 보고서 생성 +- 리포트 생성 - 시나리오 실행 - 이전 `qa-channel` 시나리오용 호환성 별칭 -러너 plugin은 전송 계약을 소유합니다. +러너 Plugin은 전송 계약을 소유합니다: -- 공유 `qa` 루트 아래에 `openclaw qa `를 어떻게 마운트할지 -- 해당 전송용으로 Gateway를 어떻게 구성할지 -- 준비 상태를 어떻게 확인할지 -- 인바운드 이벤트를 어떻게 주입할지 -- 아웃바운드 메시지를 어떻게 관찰할지 -- transcript와 정규화된 전송 상태를 어떻게 노출할지 -- 전송 기반 작업을 어떻게 실행할지 -- 전송별 리셋 또는 정리를 어떻게 처리할지 +- 공유 `qa` 루트 아래에 `openclaw qa `를 어떻게 마운트하는지 +- 해당 전송에 맞게 Gateway를 어떻게 구성하는지 +- 준비 상태를 어떻게 확인하는지 +- 인바운드 이벤트를 어떻게 주입하는지 +- 아웃바운드 메시지를 어떻게 관찰하는지 +- transcript와 정규화된 전송 상태를 어떻게 노출하는지 +- 전송 기반 작업을 어떻게 실행하는지 +- 전송별 초기화 또는 정리를 어떻게 처리하는지 -새 채널 채택의 최소 기준은 다음과 같습니다. +새 channel에 대한 최소 도입 기준은 다음과 같습니다: -1. 공유 `qa` 루트의 소유자는 `qa-lab`으로 유지합니다. -2. 공유 `qa-lab` 호스트 seam에서 전송 러너를 구현합니다. -3. 전송별 메커니즘은 러너 plugin 또는 채널 하네스 내부에 둡니다. +1. 공유 `qa` 루트의 소유자는 계속 `qa-lab`으로 유지합니다. +2. 공유 `qa-lab` 호스트 시임에서 전송 러너를 구현합니다. +3. 전송별 메커니즘은 러너 Plugin 또는 channel 하네스 내부에 유지합니다. 4. 경쟁하는 루트 명령을 등록하는 대신 러너를 `openclaw qa `로 마운트합니다. - 러너 plugin은 `openclaw.plugin.json`에 `qaRunners`를 선언하고 `runtime-api.ts`에서 일치하는 `qaRunnerCliRegistrations` 배열을 export해야 합니다. - `runtime-api.ts`는 가볍게 유지하세요. 지연 CLI와 러너 실행은 별도의 entrypoint 뒤에 남겨 두어야 합니다. -5. 테마별 `qa/scenarios/` 디렉터리 아래에 markdown 시나리오를 작성하거나 조정합니다. -6. 새 시나리오에는 일반 시나리오 도우미를 사용합니다. -7. 저장소가 의도적인 마이그레이션 중이 아닌 한 기존 호환성 별칭은 계속 동작하게 유지합니다. + 러너 Plugin은 `openclaw.plugin.json`에 `qaRunners`를 선언하고 `runtime-api.ts`에서 일치하는 `qaRunnerCliRegistrations` 배열을 export해야 합니다. + `runtime-api.ts`는 가볍게 유지하세요. 지연 CLI 및 러너 실행은 별도의 엔트리포인트 뒤에 두어야 합니다. +5. 테마별 `qa/scenarios/` 디렉터리 아래에 마크다운 시나리오를 작성하거나 조정합니다. +6. 새 시나리오에는 일반 시나리오 헬퍼를 사용합니다. +7. repo가 의도적인 마이그레이션을 수행 중인 경우가 아니라면 기존 호환성 별칭이 계속 동작하도록 유지합니다. -판단 규칙은 엄격합니다. +결정 규칙은 엄격합니다: -- 동작을 `qa-lab`에서 한 번만 표현할 수 있다면 `qa-lab`에 넣습니다. -- 동작이 하나의 채널 전송에 의존한다면 그 러너 plugin 또는 plugin 하네스에 둡니다. -- 시나리오가 둘 이상의 채널이 사용할 수 있는 새 기능을 필요로 한다면 `suite.ts`에 채널별 분기를 넣는 대신 일반 도우미를 추가합니다. -- 동작이 하나의 전송에서만 의미가 있다면 시나리오를 전송 전용으로 유지하고 시나리오 계약에서 이를 명시합니다. +- 동작을 `qa-lab`에 한 번만 표현할 수 있다면 `qa-lab`에 두세요. +- 동작이 하나의 channel 전송에 의존한다면 해당 러너 Plugin 또는 Plugin 하네스에 두세요. +- 하나 이상의 channel이 사용할 수 있는 새 기능이 시나리오에 필요하다면 `suite.ts`에 channel별 분기를 추가하는 대신 일반 헬퍼를 추가하세요. +- 동작이 하나의 전송에서만 의미가 있다면 시나리오를 전송 전용으로 유지하고, 시나리오 계약에서 이를 명시하세요. -새 시나리오에 권장되는 일반 도우미 이름은 다음과 같습니다. +새 시나리오에 권장되는 일반 헬퍼 이름은 다음과 같습니다: - `waitForTransportReady` - `waitForChannelReady` @@ -281,7 +281,7 @@ markdown QA 시스템에 채널을 추가하려면 정확히 두 가지가 필 - `formatTransportTranscript` - `resetTransport` -기존 시나리오용 호환성 별칭도 계속 사용할 수 있습니다. +기존 시나리오를 위한 호환성 별칭도 계속 사용할 수 있으며, 여기에는 다음이 포함됩니다: - `waitForQaChannelReady` - `waitForOutboundMessage` @@ -289,261 +289,264 @@ markdown QA 시스템에 채널을 추가하려면 정확히 두 가지가 필 - `formatConversationTranscript` - `resetBus` -새 채널 작업에는 일반 도우미 이름을 사용해야 합니다. -호환성 별칭은 일괄 전환 없는 마이그레이션을 피하기 위해 존재하는 것이지, +새 channel 작업에는 일반 헬퍼 이름을 사용해야 합니다. +호환성 별칭은 플래그 데이 마이그레이션을 피하기 위해 존재하는 것이지, 새 시나리오 작성의 모델이 아닙니다. -## 테스트 스위트(어디에서 무엇이 실행되는지) +## 테스트 스위트(어디서 무엇이 실행되는가) -스위트는 “현실성이 증가하는 순서”(그리고 불안정성/비용도 증가하는 순서)로 생각하세요. +스위트는 “현실성이 점점 높아지는 단계”(그리고 불안정성/비용도 증가하는 단계)로 생각하면 됩니다: ### Unit / integration(기본) - 명령: `pnpm test` -- 구성: 대상이 없는 실행은 `vitest.full-*.config.ts` 샤드 세트를 사용하며 병렬 스케줄링을 위해 다중 프로젝트 샤드를 프로젝트별 구성으로 확장할 수 있습니다 -- 파일: `src/**/*.test.ts`, `packages/**/*.test.ts`, `test/**/*.test.ts` 아래의 core/unit 인벤토리와 `vitest.unit.config.ts`가 다루는 화이트리스트 `ui` Node 테스트 +- 구성: 대상이 지정되지 않은 실행은 `vitest.full-*.config.ts` 샤드 세트를 사용하며, 병렬 스케줄링을 위해 다중 프로젝트 샤드를 프로젝트별 config로 확장할 수 있습니다. +- 파일: `src/**/*.test.ts`, `packages/**/*.test.ts`, `test/**/*.test.ts` 아래의 core/unit 인벤토리와 `vitest.unit.config.ts`가 다루는 화이트리스트된 `ui` node 테스트 - 범위: - 순수 unit 테스트 - - 인프로세스 integration 테스트(Gateway 인증, 라우팅, 도구화, 파싱, 구성) + - 프로세스 내 integration 테스트(gateway auth, routing, tooling, parsing, config) - 알려진 버그에 대한 결정적 회귀 테스트 - 기대 사항: - CI에서 실행됨 - - 실제 키 필요 없음 + - 실제 키 불필요 - 빠르고 안정적이어야 함 - 프로젝트 참고: - - 대상이 없는 `pnpm test`는 이제 거대한 하나의 네이티브 루트 프로젝트 프로세스 대신 열두 개의 더 작은 샤드 구성(`core-unit-fast`, `core-unit-src`, `core-unit-security`, `core-unit-ui`, `core-unit-support`, `core-support-boundary`, `core-contracts`, `core-bundled`, `core-runtime`, `agentic`, `auto-reply`, `extensions`)을 실행합니다. 이렇게 하면 로드된 머신에서 최대 RSS를 줄이고 auto-reply/extension 작업이 관련 없는 스위트를 굶기지 않게 합니다. + - 대상이 지정되지 않은 `pnpm test`는 이제 하나의 거대한 네이티브 루트 프로젝트 프로세스 대신 열두 개의 더 작은 샤드 config(`core-unit-fast`, `core-unit-src`, `core-unit-security`, `core-unit-ui`, `core-unit-support`, `core-support-boundary`, `core-contracts`, `core-bundled`, `core-runtime`, `agentic`, `auto-reply`, `extensions`)를 실행합니다. 이렇게 하면 부하가 큰 머신에서 최대 RSS를 줄이고 auto-reply/extension 작업이 관련 없는 스위트를 굶기지 않도록 합니다. - `pnpm test --watch`는 다중 샤드 watch 루프가 실용적이지 않기 때문에 여전히 네이티브 루트 `vitest.config.ts` 프로젝트 그래프를 사용합니다. - - `pnpm test`, `pnpm test:watch`, `pnpm test:perf:imports`는 이제 명시적인 파일/디렉터리 대상을 먼저 범위 지정된 레인으로 라우팅하므로 `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`는 전체 루트 프로젝트 시작 비용을 지불하지 않습니다. - - `pnpm test:changed`는 변경된 git 경로가 라우팅 가능한 더 작은 스위트에 깔끔하게 매핑될 때 동일한 범위 지정 레인을 통해 라우팅합니다. config/setup 편집은 여전히 넓은 루트 프로젝트 재실행으로 폴백합니다. - - `pnpm check:changed`는 좁은 작업을 위한 일반적인 스마트 로컬 게이트입니다. diff를 core, core 테스트, extensions, extension 테스트, apps, docs, 릴리스 메타데이터, tooling으로 분류한 뒤, 일치하는 typecheck/lint/test 레인을 실행합니다. 공개 Plugin SDK와 plugin-contract 변경은 extension이 해당 core 계약에 의존하므로 extension 검증을 포함합니다. 릴리스 메타데이터 전용 버전 범프는 전체 스위트 대신 대상 버전/config/root-dependency 검사를 실행하며, 최상위 버전 필드 외의 package 변경을 거부하는 가드를 둡니다. - - agents, commands, plugins, auto-reply helpers, `plugin-sdk` 및 유사한 순수 유틸리티 영역의 import-light unit 테스트는 `test/setup-openclaw-runtime.ts`를 건너뛰는 `unit-fast` 레인을 통해 라우팅됩니다. 상태 보유/런타임 중심 파일은 기존 레인에 남습니다. - - 선택된 `plugin-sdk` 및 `commands` helper 소스 파일도 변경 모드 실행을 그 경량 레인의 명시적 sibling 테스트에 매핑하므로 helper 편집 시 해당 디렉터리의 전체 무거운 스위트를 다시 실행하지 않아도 됩니다. - - `auto-reply`는 이제 세 개의 전용 버킷을 가집니다: 최상위 core helpers, 최상위 `reply.*` integration 테스트, 그리고 `src/auto-reply/reply/**` 하위 트리. 이렇게 하면 가장 무거운 reply 하네스 작업이 저렴한 status/chunk/token 테스트에 섞이지 않습니다. -- 내장 러너 참고: - - 메시지 도구 검색 입력이나 Compaction 런타임 컨텍스트를 변경할 때는 두 수준의 커버리지를 모두 유지하세요. - - 순수 라우팅/정규화 경계에 대해 집중된 helper 회귀 테스트를 추가하세요. - - 또한 내장 러너 integration 스위트도 건강하게 유지하세요: + - `pnpm test`, `pnpm test:watch`, `pnpm test:perf:imports`는 명시적 파일/디렉터리 대상을 먼저 범위 지정 레인으로 라우팅하므로, `pnpm test extensions/discord/src/monitor/message-handler.preflight.test.ts`는 전체 루트 프로젝트 시작 비용을 지불하지 않아도 됩니다. + - `pnpm test:changed`는 변경 diff가 라우팅 가능한 소스/테스트 파일만 건드리는 경우 변경된 git 경로를 동일한 범위 지정 레인으로 확장합니다. config/setup 수정은 여전히 광범위한 루트 프로젝트 재실행으로 대체됩니다. + - `pnpm check:changed`는 좁은 작업에 대한 일반적인 스마트 로컬 게이트입니다. diff를 core, core tests, extensions, extension tests, apps, docs, release metadata, tooling으로 분류한 뒤, 일치하는 typecheck/lint/test 레인을 실행합니다. 공개 Plugin SDK와 plugin-contract 변경은 extensions가 해당 core 계약에 의존하므로 extension 검증을 포함합니다. 릴리스 메타데이터만의 버전 범프는 전체 스위트 대신 대상 지정된 version/config/root-dependency 검사를 실행하며, 최상위 version 필드 외의 package 변경을 거부하는 가드가 있습니다. + - 에이전트, 명령, plugins, auto-reply 헬퍼, `plugin-sdk`, 그리고 유사한 순수 유틸리티 영역의 import가 가벼운 unit 테스트는 `test/setup-openclaw-runtime.ts`를 건너뛰는 `unit-fast` 레인으로 라우팅됩니다. 상태가 있거나 런타임이 무거운 파일은 기존 레인에 남습니다. + - 선택된 `plugin-sdk` 및 `commands` 헬퍼 소스 파일은 changed-mode 실행을 이러한 가벼운 레인의 명시적 sibling 테스트에도 매핑하므로, 헬퍼 수정 시 해당 디렉터리에 대해 전체 무거운 스위트를 다시 실행하지 않아도 됩니다. + - `auto-reply`는 이제 세 가지 전용 버킷을 가집니다: 최상위 core 헬퍼, 최상위 `reply.*` integration 테스트, 그리고 `src/auto-reply/reply/**` 하위 트리. 이렇게 하면 가장 무거운 reply 하네스 작업을 저렴한 status/chunk/token 테스트에서 분리할 수 있습니다. +- Embedded runner 참고: + - 메시지 도구 탐색 입력 또는 Compaction 런타임 컨텍스트를 변경할 때는 + 두 수준의 커버리지를 모두 유지하세요. + - 순수 routing/normalization 경계에 대해서는 집중된 헬퍼 회귀 테스트를 추가하세요. + - 또한 embedded runner integration 스위트도 정상 상태로 유지하세요: `src/agents/pi-embedded-runner/compact.hooks.test.ts`, `src/agents/pi-embedded-runner/run.overflow-compaction.test.ts`, 그리고 `src/agents/pi-embedded-runner/run.overflow-compaction.loop.test.ts`. - - 이 스위트는 범위 지정된 ID와 Compaction 동작이 실제 `run.ts` / `compact.ts` 경로를 통해 계속 흐르는지 검증합니다. helper 전용 테스트는 해당 integration 경로의 충분한 대체물이 아닙니다. + - 이 스위트는 범위 지정 id와 Compaction 동작이 여전히 실제 + `run.ts` / `compact.ts` 경로를 통해 흐르는지 검증합니다. 헬퍼 전용 테스트만으로는 + 이러한 integration 경로를 충분히 대체할 수 없습니다. - 풀 참고: - - 기본 Vitest 구성은 이제 기본적으로 `threads`를 사용합니다. - - 공유 Vitest 구성은 또한 `isolate: false`를 고정하고 루트 프로젝트, e2e, live 구성 전반에서 비격리 러너를 사용합니다. - - 루트 UI 레인은 `jsdom` 설정과 optimizer를 유지하지만, 이제 공유 비격리 러너에서도 실행됩니다. - - 각 `pnpm test` 샤드는 공유 Vitest 구성에서 동일한 `threads` + `isolate: false` 기본값을 상속합니다. - - 공유 `scripts/run-vitest.mjs` 실행기는 이제 큰 로컬 실행 중 V8 컴파일 churn을 줄이기 위해 기본적으로 Vitest 자식 Node 프로세스에 `--no-maglev`도 추가합니다. 기본 V8 동작과 비교해야 한다면 `OPENCLAW_VITEST_ENABLE_MAGLEV=1`을 설정하세요. + - 기본 Vitest config는 이제 기본값으로 `threads`를 사용합니다. + - 공유 Vitest config는 또한 `isolate: false`를 고정하고 루트 프로젝트, e2e, live config 전반에서 비격리 러너를 사용합니다. + - 루트 UI 레인은 `jsdom` 설정과 optimizer를 유지하지만 이제 공유 비격리 러너에서도 실행됩니다. + - 각 `pnpm test` 샤드는 공유 Vitest config에서 동일한 `threads` + `isolate: false` 기본값을 상속합니다. + - 공유 `scripts/run-vitest.mjs` 런처는 이제 대규모 로컬 실행 중 V8 컴파일 churn을 줄이기 위해 기본적으로 Vitest child Node 프로세스에 `--no-maglev`도 추가합니다. 기본 V8 동작과 비교해야 한다면 `OPENCLAW_VITEST_ENABLE_MAGLEV=1`을 설정하세요. - 빠른 로컬 반복 참고: - `pnpm changed:lanes`는 diff가 어떤 아키텍처 레인을 트리거하는지 보여줍니다. - - pre-commit hook은 staged formatting/linting 후 `pnpm check:changed --staged`를 실행하므로 core 전용 커밋은 공개 extension 대상 계약을 건드리지 않는 한 extension 테스트 비용을 치르지 않습니다. 릴리스 메타데이터 전용 커밋은 대상 버전/config/root-dependency 레인에 남습니다. - - 정확한 staged 변경 세트가 이미 동등하거나 더 강한 게이트로 검증되었다면 `scripts/committer --fast "" `를 사용해 변경 범위 hook 재실행만 건너뛰세요. staged format/lint는 여전히 실행됩니다. 핸드오프에 완료한 게이트를 언급하세요. 이는 격리된 flaky hook 실패를 scoped proof로 재실행해 통과한 뒤에도 허용됩니다. - - `pnpm test:changed`는 변경된 경로가 더 작은 스위트에 깔끔하게 매핑될 때 범위 지정된 레인을 통해 라우팅합니다. - - `pnpm test:max`와 `pnpm test:changed:max`는 더 높은 워커 상한만 제외하면 동일한 라우팅 동작을 유지합니다. - - 로컬 워커 자동 스케일링은 이제 의도적으로 보수적이며 호스트 load average가 이미 높을 때도 물러나므로, 여러 동시 Vitest 실행이 기본적으로 덜 큰 피해를 줍니다. - - 기본 Vitest 구성은 프로젝트/구성 파일을 `forceRerunTriggers`로 표시하므로 테스트 wiring이 바뀔 때 변경 모드 재실행의 정확성을 유지합니다. - - 이 구성은 지원되는 호스트에서 `OPENCLAW_VITEST_FS_MODULE_CACHE`를 계속 활성화합니다. 직접 프로파일링을 위한 명시적 캐시 위치 하나를 원하면 `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path`를 설정하세요. + - pre-commit hook은 staged formatting/linting 후 `pnpm check:changed --staged`를 실행하므로, core 전용 커밋은 공개 extension 대상 계약을 건드리지 않는 한 extension 테스트 비용을 지불하지 않습니다. 릴리스 메타데이터 전용 커밋은 대상 지정된 version/config/root-dependency 레인에 머뭅니다. + - 정확한 staged 변경 집합이 이미 동등하거나 더 강한 게이트로 검증되었다면 `scripts/committer --fast "" `를 사용해 변경 범위 hook 재실행만 건너뛰세요. staged format/lint는 여전히 실행됩니다. 인계 시 완료한 게이트를 언급하세요. 고립된 flaky hook 실패를 재실행하여 범위 지정된 증거와 함께 통과한 경우에도 이것은 허용됩니다. + - `pnpm test:changed`는 변경된 경로가 더 작은 스위트에 깔끔하게 매핑될 때 범위 지정 레인을 통해 라우팅됩니다. + - `pnpm test:max`와 `pnpm test:changed:max`도 동일한 라우팅 동작을 유지하며, 단지 더 높은 워커 상한을 사용할 뿐입니다. + - 로컬 워커 자동 스케일링은 이제 의도적으로 보수적으로 동작하며, 호스트 load average가 이미 높을 때도 백오프하므로, 여러 Vitest 실행이 동시에 돌아가도 기본적으로 피해를 덜 줍니다. + - 기본 Vitest config는 프로젝트/config 파일을 `forceRerunTriggers`로 표시하므로 테스트 wiring이 변경될 때 changed-mode 재실행의 정확성을 유지합니다. + - config는 지원되는 호스트에서 `OPENCLAW_VITEST_FS_MODULE_CACHE`를 계속 활성화합니다. 직접 프로파일링을 위해 명시적인 하나의 캐시 위치를 원한다면 `OPENCLAW_VITEST_FS_MODULE_CACHE_PATH=/abs/path`를 설정하세요. - 성능 디버그 참고: - - `pnpm test:perf:imports`는 Vitest import-duration 보고와 import-breakdown 출력을 활성화합니다. + - `pnpm test:perf:imports`는 Vitest import-duration 리포팅과 import-breakdown 출력을 활성화합니다. - `pnpm test:perf:imports:changed`는 동일한 프로파일링 보기를 `origin/main` 이후 변경된 파일로 범위 지정합니다. -- `pnpm test:perf:changed:bench -- --ref `는 해당 커밋 diff에 대해 라우팅된 `test:changed`를 네이티브 루트 프로젝트 경로와 비교하고 wall time과 macOS max RSS를 출력합니다. -- `pnpm test:perf:changed:bench -- --worktree`는 변경된 파일 목록을 `scripts/test-projects.mjs`와 루트 Vitest 구성에 라우팅해 현재 dirty tree를 벤치마크합니다. +- `pnpm test:perf:changed:bench -- --ref `는 해당 커밋 diff에 대해 라우팅된 `test:changed`와 네이티브 루트 프로젝트 경로를 비교하고 wall time과 macOS 최대 RSS를 출력합니다. +- `pnpm test:perf:changed:bench -- --worktree`는 변경된 파일 목록을 `scripts/test-projects.mjs`와 루트 Vitest config를 통해 라우팅하여 현재 dirty tree를 벤치마크합니다. - `pnpm test:perf:profile:main`은 Vitest/Vite 시작 및 transform 오버헤드에 대한 메인 스레드 CPU 프로파일을 기록합니다. - - `pnpm test:perf:profile:runner`는 파일 병렬화를 비활성화한 상태에서 unit 스위트의 러너 CPU+힙 프로파일을 기록합니다. + - `pnpm test:perf:profile:runner`는 파일 병렬화를 비활성화한 상태로 unit 스위트의 러너 CPU+heap 프로파일을 기록합니다. ### 안정성(Gateway) - 명령: `pnpm test:stability:gateway` -- 구성: `vitest.gateway.config.ts`, 강제로 워커 1개 사용 +- 구성: `vitest.gateway.config.ts`, 워커 하나로 강제 - 범위: - - 기본적으로 진단 기능이 활성화된 실제 loopback Gateway 시작 - - 진단 이벤트 경로를 통해 합성 Gateway 메시지, 메모리, 대형 payload churn 구동 - - Gateway WS RPC를 통해 `diagnostics.stability` 조회 - - 진단 안정성 번들 영속성 helper 커버 - - recorder가 bounded 상태를 유지하고, 합성 RSS 샘플이 압력 예산 아래에 머무르며, 세션별 queue depth가 다시 0으로 배출되는지 단언 + - 기본적으로 diagnostics를 활성화한 실제 loopback Gateway를 시작 + - 합성 gateway 메시지, 메모리, 대용량 페이로드 churn을 diagnostic event 경로를 통해 구동 + - Gateway WS RPC를 통해 `diagnostics.stability` 질의 + - diagnostic stability bundle persistence 헬퍼 커버 + - 레코더가 제한된 상태를 유지하고, 합성 RSS 샘플이 pressure 예산 아래에 머물며, 세션별 queue depth가 다시 0으로 배출되는지 검증 - 기대 사항: - - CI 안전, 키 필요 없음 - - 안정성 회귀 후속 조치를 위한 좁은 레인이지 전체 Gateway 스위트의 대체품은 아님 + - CI 안전하며 키 불필요 + - 안정성 회귀 후속 조치를 위한 좁은 레인이며, 전체 Gateway 스위트를 대체하지는 않음 ### E2E(Gateway 스모크) - 명령: `pnpm test:e2e` - 구성: `vitest.e2e.config.ts` -- 파일: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts`, 그리고 `extensions/` 아래 번들 plugin E2E 테스트 +- 파일: `src/**/*.e2e.test.ts`, `test/**/*.e2e.test.ts`, 그리고 `extensions/` 아래의 번들 Plugin E2E 테스트 - 런타임 기본값: - - 저장소의 나머지와 동일하게 Vitest `threads`와 `isolate: false`를 사용합니다. - - 적응형 워커를 사용합니다(CI: 최대 2, 로컬: 기본값 1). + - repo의 나머지와 맞춰 Vitest `threads`와 `isolate: false`를 사용합니다. + - 적응형 워커 사용(CI: 최대 2, 로컬: 기본 1). - 콘솔 I/O 오버헤드를 줄이기 위해 기본적으로 silent 모드로 실행됩니다. - 유용한 재정의: - - 워커 수를 강제로 지정하려면 `OPENCLAW_E2E_WORKERS=`(최대 16) - - 자세한 콘솔 출력을 다시 활성화하려면 `OPENCLAW_E2E_VERBOSE=1` + - 워커 수를 강제하려면 `OPENCLAW_E2E_WORKERS=` 사용(최대 16). + - 자세한 콘솔 출력을 다시 활성화하려면 `OPENCLAW_E2E_VERBOSE=1` 사용. - 범위: - - 다중 인스턴스 Gateway 엔드투엔드 동작 - - WebSocket/HTTP 표면, Node 페어링, 더 무거운 네트워킹 + - 다중 인스턴스 gateway end-to-end 동작 + - WebSocket/HTTP 표면, node pairing, 그리고 더 무거운 네트워킹 - 기대 사항: - - 파이프라인에서 활성화되면 CI에서 실행됨 - - 실제 키 필요 없음 - - unit 테스트보다 더 많은 가동 부품이 있음(더 느릴 수 있음) + - CI에서 실행됨(파이프라인에서 활성화된 경우) + - 실제 키 불필요 + - unit 테스트보다 더 많은 가동 요소가 있음(더 느릴 수 있음) ### E2E: OpenShell 백엔드 스모크 - 명령: `pnpm test:e2e:openshell` - 파일: `extensions/openshell/src/backend.e2e.test.ts` - 범위: - - Docker를 통해 호스트에서 격리된 OpenShell Gateway를 시작 - - 임시 로컬 Dockerfile에서 샌드박스 생성 - - 실제 `sandbox ssh-config` + SSH exec을 통해 OpenClaw의 OpenShell 백엔드 실행 - - 샌드박스 fs bridge를 통해 원격 canonical 파일시스템 동작 검증 + - 호스트에서 Docker를 통해 격리된 OpenShell Gateway를 시작합니다. + - 임시 로컬 Dockerfile에서 sandbox를 생성합니다. + - 실제 `sandbox ssh-config` + SSH exec를 통해 OpenClaw의 OpenShell 백엔드를 검증합니다. + - sandbox fs bridge를 통해 원격 표준 파일시스템 동작을 검증합니다. - 기대 사항: - - 옵트인 전용이며 기본 `pnpm test:e2e` 실행에는 포함되지 않음 - - 로컬 `openshell` CLI와 동작하는 Docker 데몬 필요 - - 격리된 `HOME` / `XDG_CONFIG_HOME`을 사용한 뒤 테스트 Gateway와 샌드박스를 제거 + - 옵트인 전용이며, 기본 `pnpm test:e2e` 실행에는 포함되지 않습니다. + - 로컬 `openshell` CLI와 정상 동작하는 Docker 데몬이 필요합니다. + - 격리된 `HOME` / `XDG_CONFIG_HOME`을 사용한 뒤, 테스트 Gateway와 sandbox를 제거합니다. - 유용한 재정의: - 더 넓은 e2e 스위트를 수동 실행할 때 테스트를 활성화하려면 `OPENCLAW_E2E_OPENSHELL=1` - 기본이 아닌 CLI 바이너리 또는 래퍼 스크립트를 가리키려면 `OPENCLAW_E2E_OPENSHELL_COMMAND=/path/to/openshell` -### live(실제 provider + 실제 모델) +### Live(실제 프로바이더 + 실제 모델) - 명령: `pnpm test:live` - 구성: `vitest.live.config.ts` -- 파일: `src/**/*.live.test.ts`, `test/**/*.live.test.ts`, 그리고 `extensions/` 아래 번들 plugin live 테스트 -- 기본값: `pnpm test:live`로 **활성화됨**(`OPENCLAW_LIVE_TEST=1` 설정) +- 파일: `src/**/*.live.test.ts`, `test/**/*.live.test.ts`, 그리고 `extensions/` 아래의 번들 Plugin live 테스트 +- 기본값: `pnpm test:live`에 의해 **활성화됨**(`OPENCLAW_LIVE_TEST=1` 설정) - 범위: - - “이 provider/모델이 _오늘_ 실제 자격 증명으로 실제로 동작하는가?” - - provider 포맷 변경, 도구 호출 특이점, 인증 문제, 속도 제한 동작 포착 + - “이 프로바이더/모델이 _오늘_ 실제 자격 증명으로 실제로 동작하는가?” + - 프로바이더 형식 변경, tool-calling 특이사항, 인증 이슈, rate limit 동작을 포착 - 기대 사항: - - 설계상 CI에서 안정적이지 않음(실제 네트워크, 실제 provider 정책, 쿼터, 장애) - - 비용이 들거나 속도 제한을 사용함 - - “모든 것”보다 좁힌 하위 집합 실행을 선호 -- live 실행은 누락된 API 키를 가져오기 위해 `~/.profile`을 source합니다. -- 기본적으로 live 실행은 여전히 `HOME`을 격리하고 구성/인증 자료를 임시 테스트 홈에 복사하므로 unit 픽스처가 실제 `~/.openclaw`를 변경할 수 없습니다. -- live 테스트가 실제 홈 디렉터리를 사용해야 하는 경우에만 `OPENCLAW_LIVE_USE_REAL_HOME=1`을 설정하세요. -- `pnpm test:live`는 이제 더 조용한 모드를 기본으로 사용합니다. `[live] ...` 진행 출력은 유지하지만 추가 `~/.profile` 안내를 숨기고 Gateway bootstrap 로그/Bonjour chatter를 음소거합니다. 전체 시작 로그를 다시 보려면 `OPENCLAW_LIVE_TEST_QUIET=0`을 설정하세요. -- API 키 교체(provider별): 쉼표/세미콜론 형식의 `*_API_KEYS` 또는 `*_API_KEY_1`, `*_API_KEY_2`를 설정하세요(예: `OPENAI_API_KEYS`, `ANTHROPIC_API_KEYS`, `GEMINI_API_KEYS`). 또는 live 전용 재정의로 `OPENCLAW_LIVE_*_KEY`를 사용하세요. 테스트는 속도 제한 응답에서 재시도합니다. -- 진행/Heartbeat 출력: - - live 스위트는 이제 진행 줄을 stderr로 출력하므로 Vitest 콘솔 캡처가 조용해도 긴 provider 호출이 진행 중임을 볼 수 있습니다. - - `vitest.live.config.ts`는 Vitest 콘솔 가로채기를 비활성화하므로 provider/Gateway 진행 줄이 live 실행 중 즉시 스트리밍됩니다. - - 직접 모델 Heartbeat는 `OPENCLAW_LIVE_HEARTBEAT_MS`로 조정하세요. - - Gateway/프로브 Heartbeat는 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS`로 조정하세요. + - 설계상 CI에서 안정적이지 않음(실제 네트워크, 실제 프로바이더 정책, quota, 장애) + - 비용이 발생하거나 rate limit를 사용함 + - “모든 것”보다 범위를 좁힌 부분집합 실행을 권장함 +- Live 실행은 누락된 API 키를 가져오기 위해 `~/.profile`을 source합니다. +- 기본적으로 live 실행은 여전히 `HOME`을 격리하고 config/auth 자료를 임시 테스트 홈에 복사하므로 unit 픽스처가 실제 `~/.openclaw`를 변경할 수 없습니다. +- live 테스트가 실제 홈 디렉터리를 사용하도록 의도적으로 해야 할 때만 `OPENCLAW_LIVE_USE_REAL_HOME=1`을 설정하세요. +- `pnpm test:live`는 이제 더 조용한 모드를 기본값으로 사용합니다. `[live] ...` 진행 상황 출력은 유지하지만, 추가 `~/.profile` 알림은 숨기고 gateway bootstrap 로그/Bonjour chatter는 음소거합니다. 전체 시작 로그를 다시 보려면 `OPENCLAW_LIVE_TEST_QUIET=0`을 설정하세요. +- API 키 순환(프로바이더별): 쉼표/세미콜론 형식의 `*_API_KEYS` 또는 `*_API_KEY_1`, `*_API_KEY_2`를 설정하세요(예: `OPENAI_API_KEYS`, `ANTHROPIC_API_KEYS`, `GEMINI_API_KEYS`). 또는 live 전용 재정의로 `OPENCLAW_LIVE_*_KEY`를 사용하세요. 테스트는 rate limit 응답 시 재시도합니다. +- 진행 상황/Heartbeat 출력: + - Live 스위트는 이제 진행 상황 줄을 stderr에 출력하므로, Vitest 콘솔 캡처가 조용한 경우에도 긴 프로바이더 호출이 눈에 띄게 활성 상태로 보입니다. + - `vitest.live.config.ts`는 Vitest 콘솔 가로채기를 비활성화하므로, 프로바이더/gateway 진행 상황 줄이 live 실행 중 즉시 스트리밍됩니다. + - 직접 모델 Heartbeat는 `OPENCLAW_LIVE_HEARTBEAT_MS`로 조정합니다. + - gateway/프로브 Heartbeat는 `OPENCLAW_LIVE_GATEWAY_HEARTBEAT_MS`로 조정합니다. ## 어떤 스위트를 실행해야 하나요? -다음 결정 표를 사용하세요. +다음 결정 표를 사용하세요: -- 로직/테스트 편집: `pnpm test` 실행(많이 바꿨다면 `pnpm test:coverage`도) -- Gateway 네트워킹 / WS 프로토콜 / 페어링 수정: `pnpm test:e2e` 추가 -- “내 봇이 다운됨” / provider별 실패 / 도구 호출 디버깅: 범위를 좁힌 `pnpm test:live` 실행 +- 로직/테스트 편집: `pnpm test` 실행(많이 변경했다면 `pnpm test:coverage`도 실행) +- gateway 네트워킹 / WS 프로토콜 / pairing 변경: `pnpm test:e2e` 추가 +- “내 봇이 죽었어요” / 프로바이더별 실패 / tool calling 디버깅: 범위를 좁힌 `pnpm test:live` 실행 -## live: Android Node 기능 스윕 +## Live: Android Node 기능 스윕 - 테스트: `src/gateway/android-node.capabilities.live.test.ts` - 스크립트: `pnpm android:test:integration` -- 목표: 연결된 Android Node가 현재 광고하는 **모든 명령을 호출**하고 명령 계약 동작을 단언 +- 목표: 연결된 Android Node가 현재 광고하는 **모든 명령을 호출**하고 명령 계약 동작을 검증합니다. - 범위: - - 사전 조건/수동 설정 기반(스위트는 앱을 설치/실행/페어링하지 않음) - - 선택된 Android Node에 대한 명령별 Gateway `node.invoke` 검증 -- 필요한 사전 설정: - - Android 앱이 이미 Gateway에 연결되고 페어링되어 있어야 함 - - 앱이 전면에 유지되어야 함 - - 통과를 기대하는 기능에 대해 권한/캡처 동의가 부여되어 있어야 함 + - 사전 조건이 갖춰진 수동 설정(이 스위트는 앱을 설치/실행/pairing하지 않음) + - 선택된 Android Node에 대한 명령별 gateway `node.invoke` 검증 +- 필수 사전 설정: + - Android 앱이 이미 Gateway에 연결되고 pairing되어 있어야 합니다. + - 앱이 foreground 상태로 유지되어야 합니다. + - 통과를 기대하는 기능에 필요한 권한/캡처 동의가 부여되어 있어야 합니다. - 선택적 대상 재정의: - `OPENCLAW_ANDROID_NODE_ID` 또는 `OPENCLAW_ANDROID_NODE_NAME` - `OPENCLAW_ANDROID_GATEWAY_URL` / `OPENCLAW_ANDROID_GATEWAY_TOKEN` / `OPENCLAW_ANDROID_GATEWAY_PASSWORD` -- 전체 Android 설정 세부 정보: [Android App](/ko/platforms/android) +- 전체 Android 설정 상세 정보: [Android App](/ko/platforms/android) -## live: 모델 스모크(profile 키) +## Live: 모델 스모크(profile 키) -live 테스트는 실패를 격리할 수 있도록 두 계층으로 나뉩니다. +Live 테스트는 실패를 격리할 수 있도록 두 계층으로 나뉩니다: -- “직접 모델”은 주어진 키로 provider/모델이 실제로 응답할 수 있는지를 알려줍니다. -- “Gateway 스모크”는 해당 모델에 대해 전체 Gateway+에이전트 파이프라인이 동작하는지를 알려줍니다(세션, 히스토리, 도구, 샌드박스 정책 등). +- “직접 모델”은 주어진 키로 프로바이더/모델이 응답할 수 있는지 알려줍니다. +- “Gateway 스모크”는 해당 모델에 대해 전체 gateway+agent 파이프라인이 동작하는지 알려줍니다(세션, 이력, 도구, sandbox 정책 등). -### 계층 1: 직접 모델 완료(Gateway 없음) +### 계층 1: 직접 모델 completion(Gateway 없음) - 테스트: `src/agents/models.profiles.live.test.ts` - 목표: - - 검색된 모델 나열 + - 탐지된 모델을 열거 - `getApiKeyForModel`을 사용해 자격 증명이 있는 모델 선택 - - 모델별 작은 완료 실행(필요한 경우 대상 회귀 포함) + - 모델별 작은 completion 실행(그리고 필요한 경우 대상 회귀 테스트) - 활성화 방법: - `pnpm test:live`(또는 Vitest를 직접 호출할 경우 `OPENCLAW_LIVE_TEST=1`) -- 이 스위트를 실제로 실행하려면 `OPENCLAW_LIVE_MODELS=modern`(또는 `all`, modern의 별칭)을 설정하세요. 그렇지 않으면 `pnpm test:live`를 Gateway 스모크에 집중시키기 위해 건너뜁니다. +- 실제로 이 스위트를 실행하려면 `OPENCLAW_LIVE_MODELS=modern`(또는 modern의 별칭인 `all`)을 설정해야 합니다. 그렇지 않으면 `pnpm test:live`를 gateway 스모크에 집중시키기 위해 건너뜁니다. - 모델 선택 방법: - modern allowlist를 실행하려면 `OPENCLAW_LIVE_MODELS=modern`(Opus/Sonnet 4.6+, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4) - - `OPENCLAW_LIVE_MODELS=all`은 modern allowlist의 별칭 - - 또는 `OPENCLAW_LIVE_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6,..."`(쉼표 allowlist) - - modern/all 스윕은 기본적으로 선별된 고신호 상한을 사용합니다. exhaustive modern 스윕에는 `OPENCLAW_LIVE_MAX_MODELS=0`, 더 작은 상한에는 양수를 설정하세요. -- provider 선택 방법: + - `OPENCLAW_LIVE_MODELS=all`은 modern allowlist의 별칭입니다. + - 또는 `OPENCLAW_LIVE_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6,..."`(쉼표로 구분한 allowlist) + - Modern/all 스윕은 기본적으로 선별된 고신호 상한을 사용합니다. 전체 modern 스윕을 하려면 `OPENCLAW_LIVE_MAX_MODELS=0`, 더 작은 상한을 원하면 양수를 설정하세요. +- 프로바이더 선택 방법: - `OPENCLAW_LIVE_PROVIDERS="google,google-antigravity,google-gemini-cli"`(쉼표 allowlist) - 키 출처: - - 기본값: profile 저장소와 env 폴백 - - **profile 저장소만** 강제하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` -- 존재 이유: - - “provider API가 깨짐 / 키가 잘못됨”과 “Gateway 에이전트 파이프라인이 깨짐”을 분리 - - 작고 격리된 회귀를 담기 위함(예: OpenAI Responses/Codex Responses reasoning replay + tool-call 흐름) + - 기본값: profile 저장소와 env fallback + - **profile 저장소**만 강제하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` 설정 +- 이 계층이 존재하는 이유: + - “프로바이더 API가 깨졌는가 / 키가 유효하지 않은가”와 “gateway agent 파이프라인이 깨졌는가”를 분리 + - 작고 격리된 회귀 테스트를 포함(예: OpenAI Responses/Codex Responses reasoning replay + tool-call 흐름) -### 계층 2: Gateway + dev 에이전트 스모크(`@openclaw`가 실제로 하는 일) +### 계층 2: Gateway + dev agent 스모크(`@openclaw`가 실제로 하는 일) - 테스트: `src/gateway/gateway-models.profiles.live.test.ts` - 목표: - - 인프로세스 Gateway 시작 + - 프로세스 내 Gateway를 시작 - `agent:dev:*` 세션 생성/패치(실행별 모델 재정의) - - 키가 있는 모델을 반복하며 다음을 단언: + - 키가 있는 모델들을 순회하며 다음을 검증: - “의미 있는” 응답(도구 없음) - - 실제 도구 호출이 동작함(read probe) + - 실제 도구 호출 동작(read probe) - 선택적 추가 도구 프로브(exec+read probe) - - OpenAI 회귀 경로(tool-call-only → follow-up)가 계속 동작함 -- 프로브 세부 정보(실패를 빠르게 설명할 수 있도록): - - `read` 프로브: 테스트가 워크스페이스에 nonce 파일을 쓰고 에이전트에게 이를 `read`하고 nonce를 다시 에코하라고 요청합니다. - - `exec+read` 프로브: 테스트가 에이전트에게 temp 파일에 nonce를 `exec`로 기록한 뒤 다시 `read`하라고 요청합니다. - - 이미지 프로브: 테스트가 생성된 PNG(고양이 + 랜덤 코드)를 첨부하고 모델이 `cat `를 반환하기를 기대합니다. - - 구현 참고: `src/gateway/gateway-models.profiles.live.test.ts` 및 `src/gateway/live-image-probe.ts` + - OpenAI 회귀 경로(tool-call-only → 후속 응답)가 계속 동작함 +- 프로브 상세 정보(실패를 빠르게 설명할 수 있도록): + - `read` 프로브: 테스트가 워크스페이스에 nonce 파일을 쓰고 agent에게 이를 `read`해서 nonce를 다시 출력하도록 요청합니다. + - `exec+read` 프로브: 테스트가 agent에게 `exec`로 임시 파일에 nonce를 쓰고 다시 `read`하도록 요청합니다. + - image 프로브: 테스트가 생성된 PNG(cat + 무작위 코드)를 첨부하고 모델이 `cat `를 반환할 것으로 기대합니다. + - 구현 참조: `src/gateway/gateway-models.profiles.live.test.ts` 및 `src/gateway/live-image-probe.ts`. - 활성화 방법: - `pnpm test:live`(또는 Vitest를 직접 호출할 경우 `OPENCLAW_LIVE_TEST=1`) - 모델 선택 방법: - 기본값: modern allowlist(Opus/Sonnet 4.6+, GPT-5.x + Codex, Gemini 3, GLM 4.7, MiniMax M2.7, Grok 4) - - `OPENCLAW_LIVE_GATEWAY_MODELS=all`은 modern allowlist의 별칭 - - 또는 `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"`(또는 쉼표 목록)로 좁히기 - - modern/all Gateway 스윕은 기본적으로 선별된 고신호 상한을 사용합니다. exhaustive modern 스윕에는 `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0`, 더 작은 상한에는 양수를 설정하세요. -- provider 선택 방법(“OpenRouter 전체” 피하기): + - `OPENCLAW_LIVE_GATEWAY_MODELS=all`은 modern allowlist의 별칭입니다. + - 또는 범위를 좁히려면 `OPENCLAW_LIVE_GATEWAY_MODELS="provider/model"`(또는 쉼표 목록)을 설정하세요. + - Modern/all gateway 스윕은 기본적으로 선별된 고신호 상한을 사용합니다. 전체 modern 스윕을 하려면 `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=0`, 더 작은 상한을 원하면 양수를 설정하세요. +- 프로바이더 선택 방법(“OpenRouter 전체” 방지): - `OPENCLAW_LIVE_GATEWAY_PROVIDERS="google,google-antigravity,google-gemini-cli,openai,anthropic,zai,minimax"`(쉼표 allowlist) -- 도구 + 이미지 프로브는 이 live 테스트에서 항상 켜져 있습니다: +- 도구 + 이미지 프로브는 이 live 테스트에서 항상 활성화됩니다: - `read` 프로브 + `exec+read` 프로브(도구 스트레스) - - 이미지 프로브는 모델이 이미지 입력 지원을 광고할 때 실행 + - image 프로브는 모델이 이미지 입력 지원을 광고할 때 실행됩니다. - 흐름(상위 수준): - - 테스트는 “CAT” + 랜덤 코드가 있는 작은 PNG를 생성합니다(`src/gateway/live-image-probe.ts`) - - 이를 `agent` `attachments: [{ mimeType: "image/png", content: "" }]`로 전송합니다 - - Gateway는 첨부파일을 `images[]`로 파싱합니다(`src/gateway/server-methods/agent.ts` + `src/gateway/chat-attachments.ts`) - - 내장 에이전트는 멀티모달 사용자 메시지를 모델로 전달합니다 - - 단언: 답변에 `cat` + 코드가 포함되어야 합니다(OCR 허용 범위: 사소한 실수 허용) + - 테스트가 “CAT” + 무작위 코드가 들어간 작은 PNG를 생성합니다(`src/gateway/live-image-probe.ts`) + - 이를 `agent`의 `attachments: [{ mimeType: "image/png", content: "" }]`를 통해 전송합니다. + - Gateway가 첨부를 `images[]`로 파싱합니다(`src/gateway/server-methods/agent.ts` + `src/gateway/chat-attachments.ts`) + - Embedded agent가 멀티모달 사용자 메시지를 모델에 전달합니다. + - 검증: 응답에 `cat` + 코드가 포함됨(OCR 허용 범위: 사소한 오류 허용) -팁: 머신에서 무엇을 테스트할 수 있는지(정확한 `provider/model` ID 포함) 보려면 다음을 실행하세요. +팁: 현재 머신에서 무엇을 테스트할 수 있는지(그리고 정확한 `provider/model` id가 무엇인지) 보려면 다음을 실행하세요: ```bash openclaw models list openclaw models list --json ``` -## live: CLI 백엔드 스모크(Claude, Codex, Gemini 또는 기타 로컬 CLI) +## Live: CLI 백엔드 스모크(Claude, Codex, Gemini 또는 기타 로컬 CLI) - 테스트: `src/gateway/gateway-cli-backend.live.test.ts` -- 목표: 기본 구성을 건드리지 않고 로컬 CLI 백엔드를 사용해 Gateway + 에이전트 파이프라인을 검증 -- 백엔드별 스모크 기본값은 소유 extension의 `cli-backend.ts` 정의에 있습니다. +- 목표: 기본 config를 건드리지 않고 로컬 CLI 백엔드를 사용해 Gateway + agent 파이프라인을 검증합니다. +- 백엔드별 스모크 기본값은 해당 extension의 `cli-backend.ts` 정의에 있습니다. - 활성화: - `pnpm test:live`(또는 Vitest를 직접 호출할 경우 `OPENCLAW_LIVE_TEST=1`) - `OPENCLAW_LIVE_CLI_BACKEND=1` - 기본값: - - 기본 provider/모델: `claude-cli/claude-sonnet-4-6` - - 명령/인수/이미지 동작은 소유 CLI 백엔드 plugin 메타데이터에서 가져옵니다. + - 기본 프로바이더/모델: `claude-cli/claude-sonnet-4-6` + - 명령/인수/이미지 동작은 해당 CLI 백엔드 Plugin 메타데이터에서 가져옵니다. - 재정의(선택 사항): - `OPENCLAW_LIVE_CLI_BACKEND_MODEL="codex-cli/gpt-5.4"` - `OPENCLAW_LIVE_CLI_BACKEND_COMMAND="/full/path/to/codex"` - `OPENCLAW_LIVE_CLI_BACKEND_ARGS='["exec","--json","--color","never","--sandbox","read-only","--skip-git-repo-check"]'` - 실제 이미지 첨부를 보내려면 `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_PROBE=1`(경로는 프롬프트에 주입됨) - - 프롬프트 주입 대신 이미지 파일 경로를 CLI 인수로 전달하려면 `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` - - `IMAGE_ARG`가 설정되었을 때 이미지 인수 전달 방식을 제어하려면 `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"`(또는 `"list"`) + - 프롬프트 주입 대신 CLI 인수로 이미지 파일 경로를 전달하려면 `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_ARG="--image"` + - `IMAGE_ARG`가 설정되었을 때 이미지 인수를 어떻게 전달할지 제어하려면 `OPENCLAW_LIVE_CLI_BACKEND_IMAGE_MODE="repeat"`(또는 `"list"`) - 두 번째 턴을 보내고 resume 흐름을 검증하려면 `OPENCLAW_LIVE_CLI_BACKEND_RESUME_PROBE=1` - - 기본 Claude Sonnet -> Opus 동일 세션 연속성 프로브를 비활성화하려면 `OPENCLAW_LIVE_CLI_BACKEND_MODEL_SWITCH_PROBE=0`(선택된 모델이 switch 대상을 지원할 때 강제로 켜려면 `1`) - + - 기본 Claude Sonnet -> Opus 동일 세션 연속성 프로브를 비활성화하려면 `OPENCLAW_LIVE_CLI_BACKEND_MODEL_SWITCH_PROBE=0`(선택한 모델이 전환 대상 지원 시 강제로 켜려면 `1`) + 예시: ```bash @@ -558,7 +561,7 @@ Docker 레시피: pnpm test:docker:live-cli-backend ``` -단일 provider Docker 레시피: +단일 프로바이더 Docker 레시피: ```bash pnpm test:docker:live-cli-backend:claude @@ -570,27 +573,27 @@ pnpm test:docker:live-cli-backend:gemini 참고: - Docker 러너는 `scripts/test-live-cli-backend-docker.sh`에 있습니다. -- 저장소 Docker 이미지 내부에서 비루트 `node` 사용자로 live CLI 백엔드 스모크를 실행합니다. -- 소유 extension에서 CLI 스모크 메타데이터를 확인한 뒤, 일치하는 Linux CLI 패키지(`@anthropic-ai/claude-code`, `@openai/codex`, 또는 `@google/gemini-cli`)를 캐시 가능한 쓰기 가능 prefix `OPENCLAW_DOCKER_CLI_TOOLS_DIR`(기본값: `~/.cache/openclaw/docker-cli-tools`)에 설치합니다. -- `pnpm test:docker:live-cli-backend:claude-subscription`에는 `~/.claude/.credentials.json`의 `claudeAiOauth.subscriptionType` 또는 `claude setup-token`의 `CLAUDE_CODE_OAUTH_TOKEN`을 통한 이동 가능한 Claude Code 구독 OAuth가 필요합니다. 먼저 Docker에서 직접 `claude -p`를 입증한 뒤, Anthropic API 키 env vars를 유지하지 않은 상태로 두 번의 Gateway CLI 백엔드 턴을 실행합니다. 이 구독 레인은 Claude가 현재 제3자 앱 사용을 일반 구독 플랜 제한 대신 추가 사용량 과금으로 라우팅하기 때문에 기본적으로 Claude MCP/tool 및 이미지 프로브를 비활성화합니다. -- live CLI 백엔드 스모크는 이제 Claude, Codex, Gemini에 대해 동일한 엔드투엔드 흐름을 실행합니다: 텍스트 턴, 이미지 분류 턴, 그리고 Gateway CLI를 통해 검증되는 MCP `cron` 도구 호출. -- Claude의 기본 스모크는 세션을 Sonnet에서 Opus로 패치하고, 재개된 세션이 여전히 이전 메모를 기억하는지도 검증합니다. +- repo Docker 이미지 안에서 비-root `node` 사용자로 live CLI 백엔드 스모크를 실행합니다. +- 해당 extension에서 CLI 스모크 메타데이터를 확인한 뒤, 일치하는 Linux CLI 패키지(`@anthropic-ai/claude-code`, `@openai/codex`, 또는 `@google/gemini-cli`)를 `OPENCLAW_DOCKER_CLI_TOOLS_DIR`(기본값: `~/.cache/openclaw/docker-cli-tools`)의 캐시 가능한 쓰기 가능 prefix에 설치합니다. +- `pnpm test:docker:live-cli-backend:claude-subscription`은 `~/.claude/.credentials.json`의 `claudeAiOauth.subscriptionType` 또는 `claude setup-token`의 `CLAUDE_CODE_OAUTH_TOKEN`을 통한 portable Claude Code subscription OAuth가 필요합니다. 먼저 Docker에서 직접 `claude -p`를 검증한 뒤, Anthropic API 키 env 변수를 유지하지 않고 두 번의 Gateway CLI 백엔드 턴을 실행합니다. 이 subscription 레인은 현재 Claude가 일반 subscription 플랜 한도 대신 추가 사용량 과금을 통해 서드파티 앱 사용을 라우팅하므로 Claude MCP/tool 및 image 프로브를 기본적으로 비활성화합니다. +- 이제 live CLI 백엔드 스모크는 Claude, Codex, Gemini에 대해 동일한 end-to-end 흐름을 검증합니다: 텍스트 턴, 이미지 분류 턴, 그리고 gateway CLI를 통해 검증되는 MCP `cron` 도구 호출. +- Claude의 기본 스모크는 Sonnet에서 Opus로 세션을 패치하고, 재개된 세션이 이전 메모를 여전히 기억하는지도 검증합니다. -## live: ACP bind 스모크(`/acp spawn ... --bind here`) +## Live: ACP 바인드 스모크(` /acp spawn ... --bind here`) - 테스트: `src/gateway/gateway-acp-bind.live.test.ts` -- 목표: live ACP 에이전트와 실제 ACP 대화 바인드 흐름 검증: +- 목표: live ACP 에이전트를 사용해 실제 ACP 대화 바인드 흐름을 검증합니다: - `/acp spawn --bind here` 전송 - - 합성 message-channel 대화를 제자리에서 바인드 + - 합성 message-channel 대화를 해당 위치에 바인드 - 같은 대화에서 일반 후속 메시지 전송 - 후속 메시지가 바인드된 ACP 세션 transcript에 기록되는지 검증 - 활성화: - `pnpm test:live src/gateway/gateway-acp-bind.live.test.ts` - `OPENCLAW_LIVE_ACP_BIND=1` - 기본값: - - Docker의 ACP 에이전트: `claude,codex,gemini` + - Docker 내 ACP 에이전트: `claude,codex,gemini` - 직접 `pnpm test:live ...`용 ACP 에이전트: `claude` - - 합성 채널: Slack DM 스타일 대화 컨텍스트 + - 합성 channel: Slack DM 스타일 대화 컨텍스트 - ACP 백엔드: `acpx` - 재정의: - `OPENCLAW_LIVE_ACP_BIND_AGENT=claude` @@ -600,8 +603,8 @@ pnpm test:docker:live-cli-backend:gemini - `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND='npx -y @agentclientprotocol/claude-agent-acp@'` - `OPENCLAW_LIVE_ACP_BIND_CODEX_MODEL=gpt-5.4` - 참고: - - 이 레인은 관리자 전용 합성 originating-route 필드가 있는 Gateway `chat.send` 표면을 사용하므로, 테스트가 외부로 실제 전달하는 척하지 않고 message-channel 컨텍스트를 붙일 수 있습니다. - - `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND`가 설정되지 않으면, 테스트는 선택된 ACP 하네스 에이전트에 대해 내장 `acpx` plugin의 내장 에이전트 레지스트리를 사용합니다. + - 이 레인은 admin 전용 합성 originating-route 필드가 있는 gateway `chat.send` 표면을 사용하므로, 테스트가 외부 전송을 가장하지 않고도 message-channel 컨텍스트를 첨부할 수 있습니다. + - `OPENCLAW_LIVE_ACP_BIND_AGENT_COMMAND`가 설정되지 않은 경우, 테스트는 선택된 ACP 하네스 에이전트에 대해 내장된 `acpx` Plugin의 내장 에이전트 레지스트리를 사용합니다. 예시: @@ -628,29 +631,34 @@ pnpm test:docker:live-acp-bind:gemini Docker 참고: - Docker 러너는 `scripts/test-live-acp-bind-docker.sh`에 있습니다. -- 기본적으로 지원되는 모든 live CLI 에이전트 `claude`, `codex`, `gemini`를 순서대로 대상으로 ACP bind 스모크를 실행합니다. -- 매트릭스를 좁히려면 `OPENCLAW_LIVE_ACP_BIND_AGENTS=claude`, `OPENCLAW_LIVE_ACP_BIND_AGENTS=codex`, 또는 `OPENCLAW_LIVE_ACP_BIND_AGENTS=gemini`를 사용하세요. -- `~/.profile`을 source하고, 일치하는 CLI 인증 자료를 컨테이너에 staging한 뒤, 쓰기 가능한 npm prefix에 `acpx`를 설치하고, 요청된 live CLI(`@anthropic-ai/claude-code`, `@openai/codex`, 또는 `@google/gemini-cli`)가 없으면 설치합니다. -- Docker 내부에서 러너는 `OPENCLAW_LIVE_ACP_BIND_ACPX_COMMAND=$HOME/.npm-global/bin/acpx`를 설정하므로, sourced profile의 provider env vars를 자식 하네스 CLI가 계속 사용할 수 있습니다. +- 기본적으로 지원되는 모든 live CLI 에이전트 `claude`, `codex`, `gemini`에 대해 순서대로 ACP bind 스모크를 실행합니다. +- 매트릭스 범위를 좁히려면 `OPENCLAW_LIVE_ACP_BIND_AGENTS=claude`, `OPENCLAW_LIVE_ACP_BIND_AGENTS=codex`, 또는 `OPENCLAW_LIVE_ACP_BIND_AGENTS=gemini`를 사용하세요. +- `~/.profile`을 source하고, 일치하는 CLI 인증 자료를 컨테이너에 스테이징하며, 쓰기 가능한 npm prefix에 `acpx`를 설치한 뒤, 필요 시 요청한 live CLI(`@anthropic-ai/claude-code`, `@openai/codex`, 또는 `@google/gemini-cli`)를 설치합니다. +- Docker 내부에서 러너는 `OPENCLAW_LIVE_ACP_BIND_ACPX_COMMAND=$HOME/.npm-global/bin/acpx`를 설정하여, source된 profile의 프로바이더 env 변수를 acpx가 자식 하네스 CLI까지 사용할 수 있도록 합니다. -## live: Codex app-server 하네스 스모크 +## Live: Codex app-server 하네스 스모크 -- 목표: 일반 Gateway `agent` 메서드를 통해 plugin 소유 Codex 하네스를 검증: - - 번들 `codex` plugin 로드 +- 목표: 일반 gateway + `agent` 메서드를 통해 Plugin 소유 Codex 하네스를 검증합니다: + - 번들 `codex` Plugin 로드 - `OPENCLAW_AGENT_RUNTIME=codex` 선택 - - `codex/gpt-5.4`에 첫 Gateway 에이전트 턴 전송 - - 동일한 OpenClaw 세션에 두 번째 턴을 보내 app-server 스레드가 재개될 수 있는지 검증 - - 동일한 Gateway 명령 경로를 통해 `/codex status`와 `/codex models` 실행 - - 선택적으로 Guardian 검토가 있는 두 개의 권한 상승 셸 프로브 실행: - 승인되어야 하는 무해한 명령 하나와, 거부되어 에이전트가 다시 물어봐야 하는 가짜 시크릿 업로드 하나 + - `codex/gpt-5.4`에 첫 번째 gateway agent 턴 전송 + - 동일한 OpenClaw 세션에 두 번째 턴을 전송하고 app-server + 스레드가 resume할 수 있는지 검증 + - 동일한 gateway 명령 + 경로를 통해 `/codex status` 및 `/codex models` 실행 + - 선택적으로 Guardian 검토 대상 상승된 셸 프로브 두 개를 실행: 승인되어야 하는 무해한 + 명령 하나와, 에이전트가 다시 확인하도록 거부되어야 하는 가짜 시크릿 업로드 하나 - 테스트: `src/gateway/gateway-codex-harness.live.test.ts` - 활성화: `OPENCLAW_LIVE_CODEX_HARNESS=1` - 기본 모델: `codex/gpt-5.4` -- 선택적 이미지 프로브: `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=1` +- 선택적 image 프로브: `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=1` - 선택적 MCP/tool 프로브: `OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=1` - 선택적 Guardian 프로브: `OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=1` -- 이 스모크는 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`을 설정하므로, 깨진 Codex 하네스가 PI로 조용히 폴백되어 통과할 수 없습니다. -- 인증: 셸/profile의 `OPENAI_API_KEY`, 그리고 선택적으로 복사된 `~/.codex/auth.json` 및 `~/.codex/config.toml` +- 이 스모크는 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`을 설정하므로, 깨진 Codex + 하네스가 조용히 PI로 fallback되어 통과할 수 없습니다. +- 인증: 셸/profile의 `OPENAI_API_KEY`, 그리고 선택적으로 복사된 + `~/.codex/auth.json` 및 `~/.codex/config.toml` 로컬 레시피: @@ -674,13 +682,20 @@ pnpm test:docker:live-codex-harness Docker 참고: - Docker 러너는 `scripts/test-live-codex-harness-docker.sh`에 있습니다. -- 마운트된 `~/.profile`을 source하고, `OPENAI_API_KEY`를 전달하며, Codex CLI 인증 파일이 있으면 복사하고, 쓰기 가능한 마운트된 npm prefix에 `@openai/codex`를 설치한 뒤, 소스 트리를 staging하고, Codex 하네스 live 테스트만 실행합니다. -- Docker는 기본적으로 이미지, MCP/tool, Guardian 프로브를 활성화합니다. 더 좁은 디버그 실행이 필요하면 `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=0` 또는 `OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=0` 또는 `OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=0`을 설정하세요. -- Docker는 또한 live 테스트 구성과 일치하게 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`을 export하므로 `openai-codex/*` 또는 PI 폴백이 Codex 하네스 회귀를 숨길 수 없습니다. +- 마운트된 `~/.profile`을 source하고, `OPENAI_API_KEY`를 전달하며, Codex CLI + 인증 파일이 있으면 복사하고, `@openai/codex`를 쓰기 가능한 마운트된 npm + prefix에 설치하고, 소스 트리를 스테이징한 뒤, Codex 하네스 live 테스트만 실행합니다. +- Docker는 image, MCP/tool, Guardian 프로브를 기본적으로 활성화합니다. 더 좁은 디버그 + 실행이 필요하면 `OPENCLAW_LIVE_CODEX_HARNESS_IMAGE_PROBE=0` 또는 + `OPENCLAW_LIVE_CODEX_HARNESS_MCP_PROBE=0` 또는 + `OPENCLAW_LIVE_CODEX_HARNESS_GUARDIAN_PROBE=0`를 설정하세요. +- Docker도 live + 테스트 config와 동일하게 `OPENCLAW_AGENT_HARNESS_FALLBACK=none`을 export하므로, `openai-codex/*` 또는 PI fallback이 Codex 하네스 + 회귀를 숨길 수 없습니다. ### 권장 live 레시피 -좁고 명시적인 allowlist가 가장 빠르고 가장 덜 불안정합니다. +범위를 좁힌 명시적 allowlist가 가장 빠르고 가장 덜 불안정합니다: - 단일 모델, 직접 실행(Gateway 없음): - `OPENCLAW_LIVE_MODELS="openai/gpt-5.4" pnpm test:live src/agents/models.profiles.live.test.ts` @@ -688,29 +703,29 @@ Docker 참고: - 단일 모델, Gateway 스모크: - `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.4" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts` -- 여러 provider에 걸친 도구 호출: +- 여러 프로바이더에 걸친 tool calling: - `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.4,anthropic/claude-opus-4-6,google/gemini-3-flash-preview,zai/glm-4.7,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts` -- Google 집중(Gemini API 키 + Antigravity): +- Google 중심(Gemini API 키 + Antigravity): - Gemini(API 키): `OPENCLAW_LIVE_GATEWAY_MODELS="google/gemini-3-flash-preview" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts` - Antigravity(OAuth): `OPENCLAW_LIVE_GATEWAY_MODELS="google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-pro-high" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts` 참고: - `google/...`는 Gemini API(API 키)를 사용합니다. -- `google-antigravity/...`는 Antigravity OAuth bridge(Cloud Code Assist 스타일 에이전트 엔드포인트)를 사용합니다. -- `google-gemini-cli/...`는 사용자의 머신에 있는 로컬 Gemini CLI를 사용합니다(별도의 인증 + 도구 동작 특이점). -- Gemini API vs Gemini CLI: - - API: OpenClaw가 HTTP를 통해 Google의 호스팅 Gemini API를 호출합니다(API 키 / profile 인증). 대부분 사용자가 “Gemini”라고 말할 때 의미하는 것이 이것입니다. - - CLI: OpenClaw가 로컬 `gemini` 바이너리를 셸 실행합니다. 자체 인증이 있으며 동작이 다를 수 있습니다(스트리밍/도구 지원/버전 차이). +- `google-antigravity/...`는 Antigravity OAuth 브리지(Cloud Code Assist 스타일 에이전트 엔드포인트)를 사용합니다. +- `google-gemini-cli/...`는 머신의 로컬 Gemini CLI를 사용합니다(별도 인증 + 도구 특이사항). +- Gemini API와 Gemini CLI의 차이: + - API: OpenClaw가 Google의 호스팅된 Gemini API를 HTTP(API 키 / profile 인증)로 호출합니다. 대부분의 사용자가 “Gemini”라고 할 때 의미하는 것은 이것입니다. + - CLI: OpenClaw가 로컬 `gemini` 바이너리를 셸 실행합니다. 자체 인증이 있으며 동작이 다를 수 있습니다(스트리밍/도구 지원/버전 불일치). -## live: 모델 매트릭스(무엇을 커버하는지) +## Live: 모델 매트릭스(무엇을 커버하는가) -고정된 “CI 모델 목록”은 없습니다(live는 옵트인). 하지만 실제 키가 있는 개발자 머신에서 정기적으로 커버하기를 권장하는 모델은 다음과 같습니다. +고정된 “CI 모델 목록”은 없습니다(live는 옵트인). 하지만 키가 있는 개발 머신에서 정기적으로 커버하기를 **권장하는** 모델은 다음과 같습니다. -### 현대적 스모크 세트(도구 호출 + 이미지) +### Modern 스모크 세트(tool calling + image) -이것은 계속 동작해야 한다고 기대하는 “일반 모델” 실행입니다. +이것은 계속 동작해야 한다고 기대하는 “공통 모델” 실행입니다: - OpenAI(non-Codex): `openai/gpt-5.4`(선택 사항: `openai/gpt-5.4-mini`) - OpenAI Codex: `openai-codex/gpt-5.4` @@ -720,12 +735,12 @@ Docker 참고: - Z.AI(GLM): `zai/glm-4.7` - MiniMax: `minimax/MiniMax-M2.7` -도구 + 이미지와 함께 Gateway 스모크 실행: +도구 + 이미지가 포함된 Gateway 스모크 실행: `OPENCLAW_LIVE_GATEWAY_MODELS="openai/gpt-5.4,openai-codex/gpt-5.4,anthropic/claude-opus-4-6,google/gemini-3.1-pro-preview,google/gemini-3-flash-preview,google-antigravity/claude-opus-4-6-thinking,google-antigravity/gemini-3-flash,zai/glm-4.7,minimax/MiniMax-M2.7" pnpm test:live src/gateway/gateway-models.profiles.live.test.ts` -### 기준선: 도구 호출(Read + 선택적 Exec) +### 기준선: tool calling(Read + 선택적 Exec) -provider 계열별로 최소 하나는 선택하세요. +프로바이더 계열마다 최소 하나를 선택하세요: - OpenAI: `openai/gpt-5.4`(또는 `openai/gpt-5.4-mini`) - Anthropic: `anthropic/claude-opus-4-6`(또는 `anthropic/claude-sonnet-4-6`) @@ -736,48 +751,48 @@ provider 계열별로 최소 하나는 선택하세요. 선택적 추가 커버리지(있으면 좋음): - xAI: `xai/grok-4`(또는 최신 사용 가능 버전) -- Mistral: `mistral/...`(활성화된 “tools” 가능 모델 하나 선택) -- Cerebras: `cerebras/...`(접근 권한이 있는 경우) -- LM Studio: `lmstudio/...`(로컬, 도구 호출은 API 모드에 따라 다름) +- Mistral: `mistral/`…(활성화한 “tools” 지원 모델 하나 선택) +- Cerebras: `cerebras/`…(접근 권한이 있을 경우) +- LM Studio: `lmstudio/`…(로컬; tool calling은 API 모드에 따라 다름) -### 비전: 이미지 전송(첨부파일 → 멀티모달 메시지) +### Vision: 이미지 전송(첨부 → 멀티모달 메시지) -이미지 프로브를 실행하려면 `OPENCLAW_LIVE_GATEWAY_MODELS`에 최소 하나의 이미지 지원 모델(Claude/Gemini/OpenAI의 vision 지원 변형 등)을 포함하세요. +이미지 프로브를 검증하려면 `OPENCLAW_LIVE_GATEWAY_MODELS`에 이미지 지원 모델 하나 이상(Claude/Gemini/OpenAI의 vision 지원 변형 등)을 포함하세요. ### 집계기 / 대체 Gateway -키가 활성화되어 있다면 다음 경로를 통한 테스트도 지원합니다. +키가 활성화되어 있으면 다음을 통한 테스트도 지원합니다: -- OpenRouter: `openrouter/...`(수백 개 모델, 도구+이미지 가능한 후보는 `openclaw models scan` 사용) -- OpenCode: Zen에는 `opencode/...`, Go에는 `opencode-go/...`(`OPENCODE_API_KEY` / `OPENCODE_ZEN_API_KEY`로 인증) +- OpenRouter: `openrouter/...`(수백 개 모델; 도구+이미지 지원 후보를 찾으려면 `openclaw models scan` 사용) +- OpenCode: Zen용 `opencode/...`, Go용 `opencode-go/...`(인증은 `OPENCODE_API_KEY` / `OPENCODE_ZEN_API_KEY`) -자격 증명/구성이 있다면 live 매트릭스에 포함할 수 있는 provider 추가 목록: +자격 증명/config가 있다면 live 매트릭스에 포함할 수 있는 추가 프로바이더: - 내장: `openai`, `openai-codex`, `anthropic`, `google`, `google-vertex`, `google-antigravity`, `google-gemini-cli`, `zai`, `openrouter`, `opencode`, `opencode-go`, `xai`, `groq`, `cerebras`, `mistral`, `github-copilot` -- `models.providers`를 통해(사용자 지정 엔드포인트): `minimax`(클라우드/API), 그리고 모든 OpenAI/Anthropic 호환 프록시(LM Studio, vLLM, LiteLLM 등) +- `models.providers`를 통해(custom endpoints): `minimax`(cloud/API), 그리고 OpenAI/Anthropic 호환 프록시(LM Studio, vLLM, LiteLLM 등) -팁: 문서에 “모든 모델”을 하드코딩하려고 하지 마세요. 권위 있는 목록은 사용자의 머신에서 `discoverModels(...)`가 반환하는 것 + 사용 가능한 키의 조합입니다. +팁: 문서에 “모든 모델”을 하드코딩하려고 하지 마세요. 권위 있는 목록은 여러분의 머신에서 `discoverModels(...)`가 반환하는 것과 사용 가능한 키가 무엇인지에 따라 달라집니다. ## 자격 증명(절대 커밋 금지) -live 테스트는 CLI와 같은 방식으로 자격 증명을 찾습니다. 실질적 의미는 다음과 같습니다. +Live 테스트는 CLI와 동일한 방식으로 자격 증명을 찾습니다. 실질적인 의미는 다음과 같습니다: - CLI가 동작하면 live 테스트도 같은 키를 찾아야 합니다. -- live 테스트가 “자격 증명 없음”이라고 하면 `openclaw models list` / 모델 선택을 디버깅하듯이 디버그하세요. +- live 테스트가 “자격 증명 없음”이라고 하면 `openclaw models list` / 모델 선택을 디버깅하는 방식과 동일하게 디버깅하세요. -- 에이전트별 인증 profile: `~/.openclaw/agents//agent/auth-profiles.json`(live 테스트에서 “profile 키”가 의미하는 것) -- 구성: `~/.openclaw/openclaw.json`(또는 `OPENCLAW_CONFIG_PATH`) -- 레거시 상태 디렉터리: `~/.openclaw/credentials/`(존재할 경우 staging된 live 홈에 복사되지만, 메인 profile-key 저장소는 아님) -- 로컬 live 실행은 기본적으로 활성 구성, 에이전트별 `auth-profiles.json` 파일, 레거시 `credentials/`, 지원되는 외부 CLI 인증 디렉터리를 임시 테스트 홈에 복사합니다. staging된 live 홈은 `workspace/`와 `sandboxes/`를 건너뛰며, probe가 실제 호스트 워크스페이스에 닿지 않도록 `agents.*.workspace` / `agentDir` 경로 재정의를 제거합니다. +- 에이전트별 auth profile: `~/.openclaw/agents//agent/auth-profiles.json`(이것이 live 테스트에서 말하는 “profile keys”입니다) +- Config: `~/.openclaw/openclaw.json`(또는 `OPENCLAW_CONFIG_PATH`) +- 레거시 state 디렉터리: `~/.openclaw/credentials/`(존재하면 스테이징된 live 홈으로 복사되지만, 주 profile-key 저장소는 아님) +- 기본적으로 live 로컬 실행은 활성 config, 에이전트별 `auth-profiles.json` 파일, 레거시 `credentials/`, 그리고 지원되는 외부 CLI auth 디렉터리를 임시 테스트 홈으로 복사합니다. 스테이징된 live 홈은 `workspace/`와 `sandboxes/`를 건너뛰며, 프로브가 실제 호스트 워크스페이스를 사용하지 않도록 `agents.*.workspace` / `agentDir` 경로 재정의가 제거됩니다. -env 키(예: `~/.profile`에 export된 키)에 의존하려면 로컬 테스트 전에 `source ~/.profile`을 실행하거나 아래의 Docker 러너를 사용하세요(컨테이너에 `~/.profile`을 마운트할 수 있음). +env 키에 의존하려면(예: `~/.profile`에 export되어 있는 경우) 로컬 테스트 전에 `source ~/.profile`을 실행하거나, 아래 Docker 러너를 사용하세요(`~/.profile`을 컨테이너에 마운트할 수 있음). ## Deepgram live(오디오 전사) - 테스트: `extensions/deepgram/audio.live.test.ts` - 활성화: `DEEPGRAM_API_KEY=... DEEPGRAM_LIVE_TEST=1 pnpm test:live extensions/deepgram/audio.live.test.ts` -## BytePlus coding plan live +## BytePlus 코딩 계획 live - 테스트: `extensions/byteplus/live.test.ts` - 활성화: `BYTEPLUS_API_KEY=... BYTEPLUS_LIVE_TEST=1 pnpm test:live extensions/byteplus/live.test.ts` @@ -788,9 +803,9 @@ env 키(예: `~/.profile`에 export된 키)에 의존하려면 로컬 테스트 - 테스트: `extensions/comfy/comfy.live.test.ts` - 활성화: `OPENCLAW_LIVE_TEST=1 COMFY_LIVE_TEST=1 pnpm test:live -- extensions/comfy/comfy.live.test.ts` - 범위: - - 번들 comfy 이미지, 비디오, `music_generate` 경로 실행 - - `models.providers.comfy.`가 구성되지 않으면 각 기능을 건너뜀 - - comfy 워크플로 제출, 폴링, 다운로드, plugin 등록을 변경한 뒤 유용함 + - 번들 comfy 이미지, 비디오, `music_generate` 경로를 검증 + - `models.providers.comfy.`가 구성되지 않은 기능은 각각 건너뜀 + - comfy 워크플로 제출, 폴링, 다운로드, 또는 Plugin 등록을 변경한 뒤 유용함 ## 이미지 생성 live @@ -798,28 +813,28 @@ env 키(예: `~/.profile`에 export된 키)에 의존하려면 로컬 테스트 - 명령: `pnpm test:live test/image-generation.runtime.live.test.ts` - 하네스: `pnpm test:live:media image` - 범위: - - 등록된 모든 이미지 생성 provider plugin 나열 - - 프로브 전에 로그인 셸(`~/.profile`)에서 누락된 provider env vars 로드 - - 기본적으로 저장된 인증 profile보다 live/env API 키를 우선 사용하므로 `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않음 - - 사용 가능한 인증/profile/모델이 없는 provider는 건너뜀 - - 공유 런타임 기능을 통해 기본 이미지 생성 변형 실행: + - 등록된 모든 이미지 생성 프로바이더 Plugin을 열거합니다. + - 프로브 전에 로그인 셸(`~/.profile`)에서 누락된 프로바이더 env 변수를 로드합니다. + - 기본적으로 저장된 auth profile보다 live/env API 키를 우선 사용하므로, `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않도록 합니다. + - 사용 가능한 인증/profile/모델이 없는 프로바이더는 건너뜁니다. + - 공유 런타임 기능을 통해 기본 이미지 생성 변형을 실행합니다: - `google:flash-generate` - `google:pro-generate` - `google:pro-edit` - `openai:default-generate` -- 현재 커버되는 번들 provider: +- 현재 커버되는 번들 프로바이더: - `fal` - `google` - `minimax` - `openai` - `vydra` - `xai` -- 선택적 범위 좁히기: +- 선택적 범위 축소: - `OPENCLAW_LIVE_IMAGE_GENERATION_PROVIDERS="openai,google,xai"` - `OPENCLAW_LIVE_IMAGE_GENERATION_MODELS="openai/gpt-image-2,google/gemini-3.1-flash-image-preview,xai/grok-imagine-image"` - `OPENCLAW_LIVE_IMAGE_GENERATION_CASES="google:flash-generate,google:pro-edit,xai:default-generate,xai:default-edit"` - 선택적 인증 동작: - - profile 저장소 인증을 강제하고 env 전용 재정의를 무시하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` + - env 전용 재정의를 무시하고 profile 저장소 인증을 강제하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` ## 음악 생성 live @@ -827,23 +842,23 @@ env 키(예: `~/.profile`에 export된 키)에 의존하려면 로컬 테스트 - 활성화: `OPENCLAW_LIVE_TEST=1 pnpm test:live -- extensions/music-generation-providers.live.test.ts` - 하네스: `pnpm test:live:media music` - 범위: - - 공유 번들 음악 생성 provider 경로 실행 - - 현재 Google과 MiniMax를 다룸 - - 프로브 전에 로그인 셸(`~/.profile`)에서 provider env vars 로드 - - 기본적으로 저장된 인증 profile보다 live/env API 키를 우선 사용하므로 `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않음 - - 사용 가능한 인증/profile/모델이 없는 provider는 건너뜀 - - 가능할 때 선언된 런타임 모드를 둘 다 실행: + - 공유 번들 음악 생성 프로바이더 경로를 검증합니다. + - 현재 Google과 MiniMax를 커버합니다. + - 프로브 전에 로그인 셸(`~/.profile`)에서 프로바이더 env 변수를 로드합니다. + - 기본적으로 저장된 auth profile보다 live/env API 키를 우선 사용하므로, `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않도록 합니다. + - 사용 가능한 인증/profile/모델이 없는 프로바이더는 건너뜁니다. + - 사용 가능한 경우 선언된 두 런타임 모드를 모두 실행합니다: - 프롬프트 전용 입력의 `generate` - - provider가 `capabilities.edit.enabled`를 선언한 경우 `edit` + - 프로바이더가 `capabilities.edit.enabled`를 선언한 경우의 `edit` - 현재 공유 레인 커버리지: - `google`: `generate`, `edit` - `minimax`: `generate` - - `comfy`: 이 공유 스윕이 아닌 별도 Comfy live 파일 -- 선택적 범위 좁히기: + - `comfy`: 별도 Comfy live 파일이며, 이 공유 스윕에는 포함되지 않음 +- 선택적 범위 축소: - `OPENCLAW_LIVE_MUSIC_GENERATION_PROVIDERS="google,minimax"` - `OPENCLAW_LIVE_MUSIC_GENERATION_MODELS="google/lyria-3-clip-preview,minimax/music-2.5+"` - 선택적 인증 동작: - - profile 저장소 인증을 강제하고 env 전용 재정의를 무시하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` + - env 전용 재정의를 무시하고 profile 저장소 인증을 강제하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` ## 비디오 생성 live @@ -851,250 +866,255 @@ env 키(예: `~/.profile`에 export된 키)에 의존하려면 로컬 테스트 - 활성화: `OPENCLAW_LIVE_TEST=1 pnpm test:live -- extensions/video-generation-providers.live.test.ts` - 하네스: `pnpm test:live:media video` - 범위: - - 공유 번들 비디오 생성 provider 경로 실행 - - 기본적으로 릴리스 안전 스모크 경로를 사용: FAL이 아닌 provider, provider별 텍스트-비디오 요청 1회, 1초 랍스터 프롬프트, `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS`의 provider별 작업 상한(기본값 `180000`) - - provider 측 큐 지연이 릴리스 시간을 지배할 수 있으므로 기본적으로 FAL을 건너뜁니다. 명시적으로 실행하려면 `--video-providers fal` 또는 `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="fal"`을 전달하세요. - - 프로브 전에 로그인 셸(`~/.profile`)에서 provider env vars 로드 - - 기본적으로 저장된 인증 profile보다 live/env API 키를 우선 사용하므로 `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않음 - - 사용 가능한 인증/profile/모델이 없는 provider는 건너뜀 - - 기본적으로 `generate`만 실행 - - 가능할 때 선언된 transform 모드도 실행하려면 `OPENCLAW_LIVE_VIDEO_GENERATION_FULL_MODES=1` 설정: - - provider가 `capabilities.imageToVideo.enabled`를 선언하고 선택된 provider/모델이 공유 스윕에서 버퍼 기반 로컬 이미지 입력을 허용할 때 `imageToVideo` - - provider가 `capabilities.videoToVideo.enabled`를 선언하고 선택된 provider/모델이 공유 스윕에서 버퍼 기반 로컬 비디오 입력을 허용할 때 `videoToVideo` - - 현재 공유 스윕에서 선언되었지만 건너뛰는 `imageToVideo` provider: + - 공유 번들 비디오 생성 프로바이더 경로를 검증합니다. + - 기본적으로 릴리스에 안전한 스모크 경로를 사용합니다: 비-FAL 프로바이더, 프로바이더당 하나의 text-to-video 요청, 1초짜리 lobster 프롬프트, 그리고 `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS`에서 가져오는 프로바이더별 작업 상한(기본값 `180000`) + - 프로바이더 측 큐 지연이 릴리스 시간을 지배할 수 있으므로 기본적으로 FAL은 건너뜁니다. 명시적으로 실행하려면 `--video-providers fal` 또는 `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="fal"`을 전달하세요. + - 프로브 전에 로그인 셸(`~/.profile`)에서 프로바이더 env 변수를 로드합니다. + - 기본적으로 저장된 auth profile보다 live/env API 키를 우선 사용하므로, `auth-profiles.json`의 오래된 테스트 키가 실제 셸 자격 증명을 가리지 않도록 합니다. + - 사용 가능한 인증/profile/모델이 없는 프로바이더는 건너뜁니다. + - 기본적으로 `generate`만 실행합니다. + - 사용 가능한 경우 선언된 transform 모드도 실행하려면 `OPENCLAW_LIVE_VIDEO_GENERATION_FULL_MODES=1`을 설정하세요: + - 프로바이더가 `capabilities.imageToVideo.enabled`를 선언하고 선택된 프로바이더/모델이 공유 스윕에서 버퍼 기반 로컬 이미지 입력을 수용하는 경우의 `imageToVideo` + - 프로바이더가 `capabilities.videoToVideo.enabled`를 선언하고 선택된 프로바이더/모델이 공유 스윕에서 버퍼 기반 로컬 비디오 입력을 수용하는 경우의 `videoToVideo` + - 현재 공유 스윕에서 선언되었지만 건너뛰는 `imageToVideo` 프로바이더: - 번들 `veo3`는 텍스트 전용이고 번들 `kling`은 원격 이미지 URL이 필요하므로 `vydra` - - provider별 Vydra 커버리지: + - 프로바이더별 Vydra 커버리지: - `OPENCLAW_LIVE_TEST=1 OPENCLAW_LIVE_VYDRA_VIDEO=1 pnpm test:live -- extensions/vydra/vydra.live.test.ts` - - 이 파일은 기본적으로 원격 이미지 URL 픽스처를 사용하는 `kling` 레인과 함께 `veo3` 텍스트-비디오를 실행합니다 + - 이 파일은 기본적으로 원격 이미지 URL 픽스처를 사용하는 `kling` 레인과 함께 `veo3` text-to-video를 실행합니다. - 현재 `videoToVideo` live 커버리지: - 선택된 모델이 `runway/gen4_aleph`일 때만 `runway` - - 현재 공유 스윕에서 선언되었지만 건너뛰는 `videoToVideo` provider: - - 현재 그 경로들이 원격 `http(s)` / MP4 참조 URL을 필요로 하므로 `alibaba`, `qwen`, `xai` - - 현재 공유 Gemini/Veo 레인이 로컬 버퍼 기반 입력을 사용하고 그 경로가 공유 스윕에서 허용되지 않으므로 `google` - - 현재 공유 레인에 org별 비디오 inpaint/remix 접근 보장이 없으므로 `openai` -- 선택적 범위 좁히기: + - 현재 공유 스윕에서 선언되었지만 건너뛰는 `videoToVideo` 프로바이더: + - 현재 이 경로들에 원격 `http(s)` / MP4 참조 URL이 필요하므로 `alibaba`, `qwen`, `xai` + - 현재 공유 Gemini/Veo 레인이 로컬 버퍼 기반 입력을 사용하고 그 경로가 공유 스윕에서는 허용되지 않으므로 `google` + - 현재 공유 레인에는 조직별 비디오 inpaint/remix 접근 보장이 없으므로 `openai` +- 선택적 범위 축소: - `OPENCLAW_LIVE_VIDEO_GENERATION_PROVIDERS="google,openai,runway"` - `OPENCLAW_LIVE_VIDEO_GENERATION_MODELS="google/veo-3.1-fast-generate-preview,openai/sora-2,runway/gen4_aleph"` - - 기본 스윕에 FAL을 포함한 모든 provider를 포함하려면 `OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS=""` - - 공격적인 스모크 실행을 위해 provider별 작업 상한을 줄이려면 `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS=60000` + - 기본 스윕에 FAL을 포함한 모든 프로바이더를 포함하려면 `OPENCLAW_LIVE_VIDEO_GENERATION_SKIP_PROVIDERS=""` + - 공격적인 스모크 실행을 위해 프로바이더별 작업 상한을 줄이려면 `OPENCLAW_LIVE_VIDEO_GENERATION_TIMEOUT_MS=60000` - 선택적 인증 동작: - - profile 저장소 인증을 강제하고 env 전용 재정의를 무시하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` + - env 전용 재정의를 무시하고 profile 저장소 인증을 강제하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` ## 미디어 live 하네스 - 명령: `pnpm test:live:media` - 목적: - - 공유 이미지, 음악, 비디오 live 스위트를 하나의 저장소 네이티브 entrypoint로 실행 - - `~/.profile`에서 누락된 provider env vars 자동 로드 - - 기본적으로 현재 사용 가능한 인증이 있는 provider로 각 스위트를 자동 축소 - - `scripts/test-live.mjs`를 재사용하므로 Heartbeat 및 quiet-mode 동작이 일관됨 + - 공유 이미지, 음악, 비디오 live 스위트를 repo 네이티브 단일 엔트리포인트를 통해 실행합니다. + - `~/.profile`에서 누락된 프로바이더 env 변수를 자동 로드합니다. + - 기본적으로 현재 사용 가능한 인증이 있는 프로바이더로 각 스위트 범위를 자동 축소합니다. + - `scripts/test-live.mjs`를 재사용하므로 Heartbeat와 quiet 모드 동작이 일관되게 유지됩니다. - 예시: - `pnpm test:live:media` - `pnpm test:live:media image video --providers openai,google,minimax` - `pnpm test:live:media video --video-providers openai,runway --all-providers` - `pnpm test:live:media music --quiet` -## Docker 러너(선택적 “Linux에서 동작함” 검사) +## Docker 러너(선택적 “Linux에서 동작함” 확인) -이 Docker 러너는 두 버킷으로 나뉩니다. +이 Docker 러너들은 두 가지 버킷으로 나뉩니다: -- live 모델 러너: `test:docker:live-models`와 `test:docker:live-gateway`는 일치하는 profile-key live 파일만 저장소 Docker 이미지 내부에서 실행합니다(`src/agents/models.profiles.live.test.ts`와 `src/gateway/gateway-models.profiles.live.test.ts`). 로컬 config 디렉터리와 워크스페이스를 마운트하고(마운트되면 `~/.profile`도 source) 실행합니다. 일치하는 로컬 entrypoint는 `test:live:models-profiles`와 `test:live:gateway-profiles`입니다. -- Docker live 러너는 전체 Docker 스윕을 실용적으로 유지하기 위해 더 작은 스모크 상한을 기본으로 사용합니다: +- Live 모델 러너: `test:docker:live-models`와 `test:docker:live-gateway`는 각각 대응하는 profile-key live 파일만 repo Docker 이미지 내부에서 실행합니다(`src/agents/models.profiles.live.test.ts` 및 `src/gateway/gateway-models.profiles.live.test.ts`). 로컬 config 디렉터리와 workspace를 마운트하며(마운트된 경우 `~/.profile`도 source함), 대응하는 로컬 엔트리포인트는 `test:live:models-profiles`와 `test:live:gateway-profiles`입니다. +- Docker live 러너는 기본적으로 더 작은 스모크 상한을 사용하므로 전체 Docker 스윕도 실용적으로 유지됩니다: `test:docker:live-models`는 기본적으로 `OPENCLAW_LIVE_MAX_MODELS=12`를 사용하고, `test:docker:live-gateway`는 기본적으로 `OPENCLAW_LIVE_GATEWAY_SMOKE=1`, `OPENCLAW_LIVE_GATEWAY_MAX_MODELS=8`, `OPENCLAW_LIVE_GATEWAY_STEP_TIMEOUT_MS=45000`, 그리고 - `OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`를 사용합니다. 명시적으로 더 큰 exhaustive 스캔을 원할 때는 이 env vars를 재정의하세요. -- `test:docker:all`은 `test:docker:live-build`를 통해 live Docker 이미지를 한 번 빌드한 뒤 두 live Docker 레인에 재사용합니다. 또한 `test:docker:e2e-build`를 통해 공유 `scripts/e2e/Dockerfile` 이미지 하나를 빌드하고, 빌드된 앱을 실행하는 E2E 컨테이너 스모크 러너에 재사용합니다. + `OPENCLAW_LIVE_GATEWAY_MODEL_TIMEOUT_MS=90000`를 사용합니다. 명시적으로 더 큰 전체 스캔을 원할 때는 이러한 env 변수를 재정의하세요. +- `test:docker:all`은 먼저 `test:docker:live-build`를 통해 live Docker 이미지를 한 번 빌드한 뒤, 이를 두 live Docker 레인에서 재사용합니다. 또한 `test:docker:e2e-build`를 통해 공유 `scripts/e2e/Dockerfile` 이미지 하나를 빌드하고, 빌드된 앱을 검증하는 E2E 컨테이너 스모크 러너에서 이를 재사용합니다. - 컨테이너 스모크 러너: `test:docker:openwebui`, `test:docker:onboard`, `test:docker:npm-onboard-channel-agent`, `test:docker:gateway-network`, `test:docker:mcp-channels`, `test:docker:pi-bundle-mcp-tools`, `test:docker:cron-mcp-cleanup`, `test:docker:plugins`, `test:docker:plugin-update`, `test:docker:config-reload`는 하나 이상의 실제 컨테이너를 부팅하고 더 높은 수준의 integration 경로를 검증합니다. -live 모델 Docker 러너는 필요한 CLI 인증 홈만 bind-mount하거나(실행 범위를 좁히지 않은 경우 지원되는 모든 인증 홈), -실행 전에 이를 컨테이너 홈으로 복사하므로 외부 CLI OAuth가 호스트 인증 저장소를 변경하지 않고 토큰을 새로고침할 수 있습니다. +또한 live 모델 Docker 러너는 필요한 CLI auth 홈만(또는 실행이 축소되지 않은 경우 지원되는 모든 홈을) 바인드 마운트한 뒤, 실행 전에 이를 컨테이너 홈으로 복사하여 외부 CLI OAuth가 호스트 auth 저장소를 변경하지 않고 토큰을 갱신할 수 있게 합니다: -- 직접 모델: `pnpm test:docker:live-models`(스크립트: `scripts/test-live-models-docker.sh`) -- ACP bind 스모크: `pnpm test:docker:live-acp-bind`(스크립트: `scripts/test-live-acp-bind-docker.sh`) -- CLI 백엔드 스모크: `pnpm test:docker:live-cli-backend`(스크립트: `scripts/test-live-cli-backend-docker.sh`) -- Codex app-server 하네스 스모크: `pnpm test:docker:live-codex-harness`(스크립트: `scripts/test-live-codex-harness-docker.sh`) -- Gateway + dev 에이전트: `pnpm test:docker:live-gateway`(스크립트: `scripts/test-live-gateway-models-docker.sh`) -- Open WebUI live 스모크: `pnpm test:docker:openwebui`(스크립트: `scripts/e2e/openwebui-docker.sh`) -- 온보딩 마법사(TTY, 전체 스캐폴딩): `pnpm test:docker:onboard`(스크립트: `scripts/e2e/onboard-docker.sh`) -- npm tarball 온보딩/채널/에이전트 스모크: `pnpm test:docker:npm-onboard-channel-agent`는 패키징된 OpenClaw tarball을 Docker에 전역 설치하고, env-ref 온보딩과 기본 Telegram으로 OpenAI를 구성하며, plugin 활성화가 런타임 의존성을 주문형으로 설치하는지 검증하고, doctor를 실행한 뒤, mock OpenAI 에이전트 턴 하나를 실행합니다. 미리 빌드한 tarball을 재사용하려면 `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, 호스트 재빌드를 건너뛰려면 `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, 채널을 바꾸려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용하세요. -- Gateway 네트워킹(두 컨테이너, WS 인증 + 상태 확인): `pnpm test:docker:gateway-network`(스크립트: `scripts/e2e/gateway-network-docker.sh`) -- OpenAI Responses `web_search` 최소 reasoning 회귀: `pnpm test:docker:openai-web-search-minimal`(스크립트: `scripts/e2e/openai-web-search-minimal-docker.sh`)는 mock OpenAI 서버를 Gateway를 통해 실행하고, `web_search`가 `reasoning.effort`를 `minimal`에서 `low`로 올리는지 검증한 다음, provider schema 거부를 강제하고 원시 세부 정보가 Gateway 로그에 나타나는지 확인합니다. -- MCP 채널 브리지(seeded Gateway + stdio bridge + 원시 Claude notification-frame 스모크): `pnpm test:docker:mcp-channels`(스크립트: `scripts/e2e/mcp-channels-docker.sh`) -- Pi 번들 MCP 도구(실제 stdio MCP 서버 + 내장 Pi profile allow/deny 스모크): `pnpm test:docker:pi-bundle-mcp-tools`(스크립트: `scripts/e2e/pi-bundle-mcp-tools-docker.sh`) -- Cron/subagent MCP 정리(실제 Gateway + stdio MCP 자식 teardown, 격리된 Cron 및 원샷 하위 에이전트 실행 후): `pnpm test:docker:cron-mcp-cleanup`(스크립트: `scripts/e2e/cron-mcp-cleanup-docker.sh`) -- Plugins(설치 스모크 + `/plugin` 별칭 + Claude 번들 재시작 동작): `pnpm test:docker:plugins`(스크립트: `scripts/e2e/plugins-docker.sh`) -- Plugin update 변경 없음 스모크: `pnpm test:docker:plugin-update`(스크립트: `scripts/e2e/plugin-update-unchanged-docker.sh`) -- Config reload 메타데이터 스모크: `pnpm test:docker:config-reload`(스크립트: `scripts/e2e/config-reload-source-docker.sh`) -- 번들 plugin 런타임 의존성: `pnpm test:docker:bundled-channel-deps`는 기본적으로 작은 Docker 러너 이미지를 빌드하고, 호스트에서 OpenClaw를 한 번 빌드/패키징한 뒤, 각 Linux 설치 시나리오에 그 tarball을 마운트합니다. 이미지를 재사용하려면 `OPENCLAW_SKIP_DOCKER_BUILD=1`, 새 로컬 빌드 후 호스트 재빌드를 건너뛰려면 `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, 기존 tarball을 가리키려면 `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz`를 사용하세요. -- 반복 작업 중에는 관련 없는 시나리오를 비활성화해 번들 plugin 런타임 의존성 범위를 좁히세요. 예: - `OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps` +- 직접 모델: `pnpm test:docker:live-models` (스크립트: `scripts/test-live-models-docker.sh`) +- ACP bind 스모크: `pnpm test:docker:live-acp-bind` (스크립트: `scripts/test-live-acp-bind-docker.sh`) +- CLI 백엔드 스모크: `pnpm test:docker:live-cli-backend` (스크립트: `scripts/test-live-cli-backend-docker.sh`) +- Codex app-server 하네스 스모크: `pnpm test:docker:live-codex-harness` (스크립트: `scripts/test-live-codex-harness-docker.sh`) +- Gateway + dev agent: `pnpm test:docker:live-gateway` (스크립트: `scripts/test-live-gateway-models-docker.sh`) +- Open WebUI live 스모크: `pnpm test:docker:openwebui` (스크립트: `scripts/e2e/openwebui-docker.sh`) +- 온보딩 마법사(TTY, 전체 스캐폴딩): `pnpm test:docker:onboard` (스크립트: `scripts/e2e/onboard-docker.sh`) +- Npm tarball 온보딩/channel/agent 스모크: `pnpm test:docker:npm-onboard-channel-agent`는 패킹된 OpenClaw tarball을 Docker에 전역 설치하고, env-ref 온보딩을 통해 OpenAI를 구성한 뒤 기본적으로 Telegram을 설정하고, Plugin 활성화 시 런타임 의존성이 필요 시 설치되는지 검증하고, doctor를 실행하며, 목된 OpenAI agent 턴 하나를 실행합니다. 미리 빌드된 tarball을 재사용하려면 `OPENCLAW_NPM_ONBOARD_PACKAGE_TGZ=/path/to/openclaw-*.tgz`, 호스트 재빌드를 건너뛰려면 `OPENCLAW_NPM_ONBOARD_HOST_BUILD=0`, channel을 전환하려면 `OPENCLAW_NPM_ONBOARD_CHANNEL=discord`를 사용하세요. +- Gateway 네트워킹(컨테이너 두 개, WS auth + health): `pnpm test:docker:gateway-network` (스크립트: `scripts/e2e/gateway-network-docker.sh`) +- OpenAI Responses `web_search` 최소 reasoning 회귀: `pnpm test:docker:openai-web-search-minimal` (스크립트: `scripts/e2e/openai-web-search-minimal-docker.sh`)는 목된 OpenAI 서버를 Gateway를 통해 실행하고, `web_search`가 `reasoning.effort`를 `minimal`에서 `low`로 올리는지 검증한 다음, 프로바이더 스키마 거부를 강제하고 raw 상세 정보가 Gateway 로그에 나타나는지 확인합니다. +- MCP channel bridge(seeded Gateway + stdio bridge + raw Claude notification-frame 스모크): `pnpm test:docker:mcp-channels` (스크립트: `scripts/e2e/mcp-channels-docker.sh`) +- Pi 번들 MCP 도구(실제 stdio MCP 서버 + embedded Pi profile allow/deny 스모크): `pnpm test:docker:pi-bundle-mcp-tools` (스크립트: `scripts/e2e/pi-bundle-mcp-tools-docker.sh`) +- Cron/subagent MCP 정리(실제 Gateway + stdio MCP child teardown, 격리된 cron 및 one-shot subagent 실행 후): `pnpm test:docker:cron-mcp-cleanup` (스크립트: `scripts/e2e/cron-mcp-cleanup-docker.sh`) +- Plugins(설치 스모크 + `/plugin` 별칭 + Claude 번들 재시작 시맨틱): `pnpm test:docker:plugins` (스크립트: `scripts/e2e/plugins-docker.sh`) +- Plugin update unchanged 스모크: `pnpm test:docker:plugin-update` (스크립트: `scripts/e2e/plugin-update-unchanged-docker.sh`) +- Config reload 메타데이터 스모크: `pnpm test:docker:config-reload` (스크립트: `scripts/e2e/config-reload-source-docker.sh`) +- 번들 Plugin 런타임 의존성: `pnpm test:docker:bundled-channel-deps`는 기본적으로 작은 Docker 러너 이미지를 빌드하고, 호스트에서 OpenClaw를 한 번 빌드 및 pack한 뒤, 그 tarball을 각 Linux 설치 시나리오에 마운트합니다. 이미지를 재사용하려면 `OPENCLAW_SKIP_DOCKER_BUILD=1`, 새 로컬 빌드 후 호스트 재빌드를 건너뛰려면 `OPENCLAW_BUNDLED_CHANNEL_HOST_BUILD=0`, 기존 tarball을 지정하려면 `OPENCLAW_BUNDLED_CHANNEL_PACKAGE_TGZ=/path/to/openclaw-*.tgz`를 사용하세요. +- 반복 작업 중 관련 없는 시나리오를 비활성화하여 번들 Plugin 런타임 의존성 범위를 좁히세요. 예: + `OPENCLAW_BUNDLED_CHANNEL_SCENARIOS=0 OPENCLAW_BUNDLED_CHANNEL_UPDATE_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_ROOT_OWNED_SCENARIO=0 OPENCLAW_BUNDLED_CHANNEL_SETUP_ENTRY_SCENARIO=0 pnpm test:docker:bundled-channel-deps`. -공유 built-app 이미지를 수동으로 사전 빌드하고 재사용하려면: +공유 빌드 앱 이미지를 수동으로 미리 빌드하고 재사용하려면: ```bash OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local pnpm test:docker:e2e-build OPENCLAW_DOCKER_E2E_IMAGE=openclaw-docker-e2e:local OPENCLAW_SKIP_DOCKER_BUILD=1 pnpm test:docker:mcp-channels ``` -`OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 같은 스위트별 이미지 재정의는 설정되어 있으면 여전히 우선합니다. `OPENCLAW_SKIP_DOCKER_BUILD=1`이 원격 공유 이미지를 가리킬 때, 스크립트는 해당 이미지가 로컬에 없으면 pull합니다. QR 및 설치 프로그램 Docker 테스트는 공유 built-app 런타임이 아니라 패키지/설치 동작을 검증하기 때문에 자체 Dockerfile을 유지합니다. +`OPENCLAW_GATEWAY_NETWORK_E2E_IMAGE` 같은 스위트별 이미지 재정의는 설정되어 있으면 여전히 우선합니다. `OPENCLAW_SKIP_DOCKER_BUILD=1`가 원격 공유 이미지를 가리키는 경우, 스크립트는 해당 이미지가 아직 로컬에 없으면 pull합니다. QR 및 installer Docker 테스트는 공유 빌드 앱 런타임이 아니라 패키지/설치 동작을 검증하므로 자체 Dockerfile을 유지합니다. -live 모델 Docker 러너는 현재 체크아웃도 읽기 전용으로 bind-mount하고, -컨테이너 내부의 임시 workdir로 staging합니다. 이렇게 하면 런타임 -이미지를 슬림하게 유지하면서도 정확히 사용자의 로컬 소스/구성에 대해 Vitest를 실행할 수 있습니다. -staging 단계는 `.pnpm-store`, `.worktrees`, `__openclaw_vitest__`, 그리고 앱 로컬 `.build` 또는 -Gradle 출력 디렉터리 같은 대형 로컬 전용 캐시와 앱 빌드 출력을 건너뛰므로, -Docker live 실행이 머신별 아티팩트를 복사하느라 몇 분씩 쓰지 않습니다. -또한 `OPENCLAW_SKIP_CHANNELS=1`을 설정하므로 Gateway live 프로브가 -컨테이너 내부에서 실제 Telegram/Discord 등의 채널 워커를 시작하지 않습니다. +또한 live 모델 Docker 러너는 현재 체크아웃을 읽기 전용으로 바인드 마운트하고, +이를 컨테이너 내부의 임시 workdir로 스테이징합니다. 이렇게 하면 런타임 +이미지는 슬림하게 유지하면서도 정확한 로컬 소스/config를 대상으로 Vitest를 실행할 수 있습니다. +스테이징 단계에서는 +`.pnpm-store`, `.worktrees`, `__openclaw_vitest__`, 그리고 앱 로컬 `.build` 또는 +Gradle 출력 디렉터리 같은 대형 로컬 전용 캐시 및 앱 빌드 출력을 건너뛰므로, Docker live 실행이 +머신별 아티팩트를 복사하느라 몇 분씩 소비하지 않습니다. +또한 `OPENCLAW_SKIP_CHANNELS=1`을 설정하므로 gateway live 프로브가 +컨테이너 내부에서 실제 Telegram/Discord 등의 channel 워커를 시작하지 않습니다. `test:docker:live-models`는 여전히 `pnpm test:live`를 실행하므로, -해당 Docker 레인에서 Gateway live 커버리지를 좁히거나 제외해야 할 때는 +해당 Docker 레인에서 gateway live 커버리지를 좁히거나 제외해야 할 때는 `OPENCLAW_LIVE_GATEWAY_*`도 함께 전달하세요. -`test:docker:openwebui`는 더 높은 수준의 호환성 스모크입니다. OpenAI 호환 HTTP 엔드포인트가 활성화된 -OpenClaw Gateway 컨테이너를 시작하고, 해당 Gateway를 대상으로 고정된 Open WebUI 컨테이너를 시작하며, -Open WebUI를 통해 로그인하고, `/api/models`가 `openclaw/default`를 노출하는지 검증한 뒤, +`test:docker:openwebui`는 더 높은 수준의 호환성 스모크입니다. 이 레인은 +OpenAI 호환 HTTP 엔드포인트가 활성화된 OpenClaw gateway 컨테이너를 시작하고, +그 gateway를 대상으로 고정된 Open WebUI 컨테이너를 시작하며, Open WebUI를 통해 로그인하고, +`/api/models`가 `openclaw/default`를 노출하는지 확인한 다음, Open WebUI의 `/api/chat/completions` 프록시를 통해 실제 채팅 요청을 보냅니다. -첫 실행은 Docker가 Open WebUI 이미지를 pull해야 하거나 Open WebUI가 -자체 콜드 스타트 설정을 끝내야 할 수 있으므로 눈에 띄게 느릴 수 있습니다. -이 레인은 사용 가능한 live 모델 키를 기대하며, Dockerized 실행에서 이를 제공하는 주요 방법은 +첫 실행은 Docker가 +Open WebUI 이미지를 pull해야 할 수 있고 Open WebUI가 자체 cold-start 설정을 완료해야 할 수도 있으므로 눈에 띄게 더 느릴 수 있습니다. +이 레인은 사용 가능한 live 모델 키를 기대하며, Dockerized 실행에서 이를 제공하는 주된 방법은 `OPENCLAW_PROFILE_FILE`(기본값 `~/.profile`)입니다. 성공적인 실행은 `{ "ok": true, "model": -"openclaw/default", ... }` 같은 작은 JSON payload를 출력합니다. -`test:docker:mcp-channels`는 의도적으로 결정적이며 실제 Telegram, Discord, iMessage 계정이 필요하지 않습니다. -seeded Gateway 컨테이너를 부팅하고, -`openclaw mcp serve`를 spawn하는 두 번째 컨테이너를 시작한 뒤, -실제 stdio MCP bridge를 통해 라우팅된 대화 검색, transcript 읽기, 첨부파일 메타데이터, -live 이벤트 큐 동작, 아웃바운드 전송 라우팅, Claude 스타일 채널 + -권한 알림을 검증합니다. 알림 검사는 원시 stdio MCP 프레임을 직접 검사하므로 -특정 client SDK가 우연히 노출하는 내용이 아니라 bridge가 실제로 방출하는 것을 검증합니다. +"openclaw/default", ... }`와 같은 작은 JSON 페이로드를 출력합니다. +`test:docker:mcp-channels`는 의도적으로 결정적이며 실제 +Telegram, Discord, 또는 iMessage 계정이 필요하지 않습니다. seeded Gateway +컨테이너를 부팅하고, `openclaw mcp serve`를 실행하는 두 번째 컨테이너를 시작한 다음, +실제 stdio MCP bridge를 통해 라우팅된 대화 탐지, transcript 읽기, 첨부 메타데이터, +live 이벤트 큐 동작, 아웃바운드 전송 라우팅, 그리고 Claude 스타일 channel + +권한 알림을 검증합니다. 알림 검사는 +원시 stdio MCP 프레임을 직접 검사하므로, 이 스모크는 특정 클라이언트 SDK가 우연히 노출하는 내용이 아니라 +bridge가 실제로 방출하는 것을 검증합니다. `test:docker:pi-bundle-mcp-tools`는 결정적이며 live -모델 키가 필요하지 않습니다. 저장소 Docker 이미지를 빌드하고, 컨테이너 내부에서 실제 stdio MCP 프로브 서버를 시작하며, -해당 서버를 내장 Pi 번들 MCP 런타임을 통해 materialize하고, 도구를 실행한 다음, -`coding`과 `messaging`은 `bundle-mcp` 도구를 유지하는 반면 `minimal`과 `tools.deny: ["bundle-mcp"]`는 이를 필터링하는지 검증합니다. +모델 키가 필요하지 않습니다. repo Docker 이미지를 빌드하고, 컨테이너 내부에서 실제 stdio MCP probe 서버를 시작하고, +내장 Pi 번들 MCP 런타임을 통해 해당 서버를 구체화하고, +도구를 실행한 다음, `coding`과 `messaging`은 +`bundle-mcp` 도구를 유지하고 `minimal` 및 `tools.deny: ["bundle-mcp"]`는 이를 필터링하는지 검증합니다. `test:docker:cron-mcp-cleanup`도 결정적이며 live 모델 -키가 필요하지 않습니다. 실제 stdio MCP 프로브 서버가 있는 seeded Gateway를 시작하고, -격리된 Cron 턴과 `/subagents spawn` 원샷 자식 턴을 실행한 뒤, -각 실행 후 MCP 자식 프로세스가 종료되는지 검증합니다. +키가 필요하지 않습니다. 실제 stdio MCP probe 서버가 포함된 seeded Gateway를 시작하고, +격리된 cron 턴과 `/subagents spawn` one-shot child 턴을 실행한 다음, +각 실행 후 MCP child 프로세스가 종료되는지 검증합니다. -수동 ACP 평문 스레드 스모크(CI 아님): +수동 ACP plain-language 스레드 스모크(CI 아님): - `bun scripts/dev/discord-acp-plain-language-smoke.ts --channel ...` -- 이 스크립트는 회귀/디버그 워크플로용으로 유지하세요. ACP 스레드 라우팅 검증에 다시 필요할 수 있으므로 삭제하지 마세요. +- 이 스크립트는 회귀/디버그 워크플로를 위해 유지하세요. ACP 스레드 라우팅 검증에 다시 필요할 수 있으므로 삭제하지 마세요. -유용한 env vars: +유용한 env 변수: -- `OPENCLAW_CONFIG_DIR=...`(기본값: `~/.openclaw`) → `/home/node/.openclaw`에 마운트 -- `OPENCLAW_WORKSPACE_DIR=...`(기본값: `~/.openclaw/workspace`) → `/home/node/.openclaw/workspace`에 마운트 -- `OPENCLAW_PROFILE_FILE=...`(기본값: `~/.profile`) → `/home/node/.profile`에 마운트되며 테스트 실행 전에 source됨 -- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1`은 `OPENCLAW_PROFILE_FILE`에서 source된 env vars만 검증하며, 임시 config/workspace 디렉터리를 사용하고 외부 CLI 인증 마운트는 사용하지 않음 -- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...`(기본값: `~/.cache/openclaw/docker-cli-tools`) → Docker 내부 캐시된 CLI 설치를 위해 `/home/node/.npm-global`에 마운트 -- `$HOME` 아래 외부 CLI 인증 디렉터리/파일은 `/host-auth...` 아래 읽기 전용으로 마운트된 뒤, 테스트 시작 전에 `/home/node/...`로 복사됨 +- `OPENCLAW_CONFIG_DIR=...` (기본값: `~/.openclaw`) → `/home/node/.openclaw`에 마운트 +- `OPENCLAW_WORKSPACE_DIR=...` (기본값: `~/.openclaw/workspace`) → `/home/node/.openclaw/workspace`에 마운트 +- `OPENCLAW_PROFILE_FILE=...` (기본값: `~/.profile`) → `/home/node/.profile`에 마운트되며 테스트 실행 전 source됨 +- `OPENCLAW_DOCKER_PROFILE_ENV_ONLY=1`을 설정하면 `OPENCLAW_PROFILE_FILE`에서 source된 env 변수만 검증하며, 임시 config/workspace 디렉터리를 사용하고 외부 CLI auth 마운트는 사용하지 않습니다. +- `OPENCLAW_DOCKER_CLI_TOOLS_DIR=...` (기본값: `~/.cache/openclaw/docker-cli-tools`) → Docker 내부에서 캐시된 CLI 설치용으로 `/home/node/.npm-global`에 마운트 +- `$HOME` 아래 외부 CLI auth 디렉터리/파일은 `/host-auth...` 아래에 읽기 전용으로 마운트된 뒤, 테스트 시작 전에 `/home/node/...`로 복사됩니다. - 기본 디렉터리: `.minimax` - 기본 파일: `~/.codex/auth.json`, `~/.codex/config.toml`, `.claude.json`, `~/.claude/.credentials.json`, `~/.claude/settings.json`, `~/.claude/settings.local.json` - - 범위를 좁힌 provider 실행은 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`에서 추론된 필요한 디렉터리/파일만 마운트 - - `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none`, 또는 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex` 같은 쉼표 목록으로 수동 재정의 가능 + - 범위를 좁힌 프로바이더 실행은 `OPENCLAW_LIVE_PROVIDERS` / `OPENCLAW_LIVE_GATEWAY_PROVIDERS`에서 추론된 필요한 디렉터리/파일만 마운트합니다. + - `OPENCLAW_DOCKER_AUTH_DIRS=all`, `OPENCLAW_DOCKER_AUTH_DIRS=none`, 또는 `OPENCLAW_DOCKER_AUTH_DIRS=.claude,.codex` 같은 쉼표 목록으로 수동 재정의할 수 있습니다. - 실행 범위를 좁히려면 `OPENCLAW_LIVE_GATEWAY_MODELS=...` / `OPENCLAW_LIVE_MODELS=...` -- 컨테이너 내부 provider를 필터링하려면 `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` +- 컨테이너 내부 프로바이더를 필터링하려면 `OPENCLAW_LIVE_GATEWAY_PROVIDERS=...` / `OPENCLAW_LIVE_PROVIDERS=...` - 재빌드가 필요 없는 재실행에서 기존 `openclaw:local-live` 이미지를 재사용하려면 `OPENCLAW_SKIP_DOCKER_BUILD=1` -- 자격 증명이 profile 저장소에서 오도록 보장하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1`(env 아님) -- Open WebUI 스모크를 위해 Gateway가 노출하는 모델을 선택하려면 `OPENCLAW_OPENWEBUI_MODEL=...` -- Open WebUI 스모크가 사용하는 nonce-check 프롬프트를 재정의하려면 `OPENCLAW_OPENWEBUI_PROMPT=...` +- 자격 증명이 env가 아니라 profile 저장소에서 오도록 보장하려면 `OPENCLAW_LIVE_REQUIRE_PROFILE_KEYS=1` +- Open WebUI 스모크에 대해 gateway가 노출하는 모델을 선택하려면 `OPENCLAW_OPENWEBUI_MODEL=...` +- Open WebUI 스모크가 사용하는 nonce 검사 프롬프트를 재정의하려면 `OPENCLAW_OPENWEBUI_PROMPT=...` - 고정된 Open WebUI 이미지 태그를 재정의하려면 `OPENWEBUI_IMAGE=...` -## 문서 sanity +## 문서 점검 -문서 편집 후 문서 검사를 실행하세요: `pnpm check:docs`. -페이지 내 heading 검사까지 필요할 때는 전체 Mintlify 앵커 검증을 실행하세요: `pnpm docs:check-links:anchors`. +문서를 수정한 뒤에는 문서 검사를 실행하세요: `pnpm check:docs`. +페이지 내 heading 검사까지 필요할 때는 전체 Mintlify anchor 검증을 실행하세요: `pnpm docs:check-links:anchors`. ## 오프라인 회귀(CI 안전) -이것들은 실제 provider 없이도 “실제 파이프라인” 회귀를 검증합니다. +실제 프로바이더 없이 “실제 파이프라인” 회귀를 검증하는 항목들입니다: -- Gateway 도구 호출(mock OpenAI, 실제 Gateway + 에이전트 루프): `src/gateway/gateway.test.ts`(케이스: "runs a mock OpenAI tool call end-to-end via gateway agent loop") -- Gateway 마법사(WS `wizard.start`/`wizard.next`, config + auth 강제 기록): `src/gateway/gateway.test.ts`(케이스: "runs wizard over ws and writes auth token config") +- Gateway tool calling(mock OpenAI, 실제 gateway + agent loop): `src/gateway/gateway.test.ts` (케이스: "runs a mock OpenAI tool call end-to-end via gateway agent loop") +- Gateway wizard(WS `wizard.start`/`wizard.next`, config + auth 작성 강제): `src/gateway/gateway.test.ts` (케이스: "runs wizard over ws and writes auth token config") ## 에이전트 신뢰성 평가(Skills) -이미 CI 안전한 테스트 몇 개가 있으며, 이들은 “에이전트 신뢰성 평가”처럼 동작합니다. +우리는 이미 “에이전트 신뢰성 평가”처럼 동작하는 몇 가지 CI 안전 테스트를 갖고 있습니다: -- 실제 Gateway + 에이전트 루프를 통한 mock 도구 호출(`src/gateway/gateway.test.ts`) -- 세션 wiring과 config 효과를 검증하는 엔드투엔드 마법사 흐름(`src/gateway/gateway.test.ts`) +- 실제 gateway + agent loop를 통한 mock tool-calling (`src/gateway/gateway.test.ts`) +- 세션 wiring 및 config 효과를 검증하는 end-to-end wizard 흐름 (`src/gateway/gateway.test.ts`) -Skills 관련해서 여전히 부족한 점([Skills](/ko/tools/skills) 참고): +Skills 관련해서 여전히 부족한 부분([Skills](/ko/tools/skills) 참고): -- **의사결정:** 프롬프트에 skill이 나열되어 있을 때 에이전트가 올바른 skill을 선택하는가(또는 관련 없는 skill은 피하는가)? -- **준수:** 에이전트가 사용 전에 `SKILL.md`를 읽고 필요한 단계/인수를 따르는가? -- **워크플로 계약:** 도구 순서, 세션 히스토리 유지, 샌드박스 경계를 단언하는 다중 턴 시나리오 +- **의사결정:** Skills가 프롬프트에 나열되었을 때, 에이전트가 올바른 skill을 선택하는가(또는 관련 없는 skill을 피하는가)? +- **준수:** 에이전트가 사용 전에 `SKILL.md`를 읽고 필수 단계/인수를 따르는가? +- **워크플로 계약:** 도구 순서, 세션 이력 이어받기, sandbox 경계를 검증하는 다중 턴 시나리오 -향후 평가는 우선 결정적이어야 합니다. +향후 평가는 우선 결정적이어야 합니다: -- mock provider를 사용해 도구 호출 + 순서, skill 파일 읽기, 세션 wiring을 단언하는 시나리오 러너 -- skill 중심 시나리오의 작은 스위트(사용 vs 회피, 게이팅, 프롬프트 인젝션) -- CI 안전 스위트가 자리 잡은 뒤에만 선택적인 live 평가(옵트인, env 게이트) +- mock 프로바이더를 사용해 도구 호출 + 순서, skill 파일 읽기, 세션 wiring을 검증하는 시나리오 러너 +- skill 중심 시나리오의 소규모 스위트(사용 vs 회피, 게이팅, 프롬프트 인젝션) +- CI 안전 스위트가 마련된 이후에만 선택적으로 opt-in, env-gated live 평가 -## 계약 테스트(plugin 및 channel 형태) +## 계약 테스트(Plugin 및 channel 형태) -계약 테스트는 등록된 모든 plugin과 채널이 자신의 -인터페이스 계약을 준수하는지 검증합니다. 검색된 모든 plugin을 순회하며 형태와 동작 단언 스위트를 실행합니다. 기본 `pnpm test` unit 레인은 의도적으로 이 공유 seam 및 스모크 파일을 건너뛰므로, 공유 채널 또는 provider 표면을 건드렸다면 계약 명령을 명시적으로 실행하세요. +계약 테스트는 등록된 모든 Plugin과 channel이 +인터페이스 계약을 준수하는지 검증합니다. 발견된 모든 Plugin을 순회하고 +형태 및 동작 검증 스위트를 실행합니다. 기본 `pnpm test` unit 레인은 +의도적으로 이러한 공유 시임 및 스모크 파일을 건너뛰므로, 공유 channel 또는 provider 표면을 건드렸다면 계약 명령을 명시적으로 실행하세요. ### 명령 - 모든 계약: `pnpm test:contracts` -- 채널 계약만: `pnpm test:contracts:channels` +- channel 계약만: `pnpm test:contracts:channels` - provider 계약만: `pnpm test:contracts:plugins` -### 채널 계약 +### Channel 계약 -위치: `src/channels/plugins/contracts/*.contract.test.ts` +`src/channels/plugins/contracts/*.contract.test.ts`에 위치: -- **plugin** - 기본 plugin 형태(id, name, capabilities) +- **plugin** - 기본 Plugin 형태(id, name, capabilities) - **setup** - 설정 마법사 계약 - **session-binding** - 세션 바인딩 동작 -- **outbound-payload** - 메시지 payload 구조 +- **outbound-payload** - 메시지 페이로드 구조 - **inbound** - 인바운드 메시지 처리 -- **actions** - 채널 action 핸들러 +- **actions** - channel action 핸들러 - **threading** - 스레드 ID 처리 -- **directory** - 디렉터리/로스터 API +- **directory** - 디렉터리/roster API - **group-policy** - 그룹 정책 강제 -### provider 상태 계약 +### Provider 상태 계약 -위치: `src/plugins/contracts/*.contract.test.ts` +`src/plugins/contracts/*.contract.test.ts`에 위치합니다. -- **status** - 채널 상태 프로브 -- **registry** - plugin 레지스트리 형태 +- **status** - channel 상태 프로브 +- **registry** - Plugin 레지스트리 형태 -### provider 계약 +### Provider 계약 -위치: `src/plugins/contracts/*.contract.test.ts` +`src/plugins/contracts/*.contract.test.ts`에 위치: - **auth** - 인증 흐름 계약 - **auth-choice** - 인증 선택/선정 - **catalog** - 모델 카탈로그 API -- **discovery** - plugin 검색 -- **loader** - plugin 로딩 -- **runtime** - provider 런타임 -- **shape** - plugin 형태/인터페이스 +- **discovery** - Plugin 발견 +- **loader** - Plugin 로딩 +- **runtime** - 프로바이더 런타임 +- **shape** - Plugin 형태/인터페이스 - **wizard** - 설정 마법사 -### 실행 시점 +### 언제 실행해야 하는가 -- plugin-sdk export 또는 하위 경로를 변경한 뒤 -- 채널 또는 provider plugin을 추가하거나 수정한 뒤 -- plugin 등록 또는 검색을 리팩터링한 뒤 +- plugin-sdk export 또는 subpath를 변경한 후 +- channel 또는 provider Plugin을 추가하거나 수정한 후 +- Plugin 등록 또는 발견을 리팩터링한 후 계약 테스트는 CI에서 실행되며 실제 API 키가 필요하지 않습니다. -## 회귀 추가(가이드) +## 회귀 테스트 추가하기(가이드) -live에서 발견된 provider/모델 문제를 수정할 때: +live에서 발견된 프로바이더/모델 이슈를 수정할 때: -- 가능하다면 CI 안전 회귀를 추가하세요(mock/stub provider 또는 정확한 요청 형태 변환을 포착) -- 본질적으로 live 전용인 경우(속도 제한, 인증 정책), live 테스트는 좁고 env vars로 옵트인되게 유지하세요 -- 버그를 잡는 가장 작은 계층을 타기팅하는 것을 선호하세요: - - provider 요청 변환/replay 버그 → 직접 모델 테스트 - - Gateway 세션/히스토리/도구 파이프라인 버그 → Gateway live 스모크 또는 CI 안전 Gateway mock 테스트 +- 가능하다면 CI 안전 회귀 테스트를 추가하세요(mock/stub 프로바이더를 사용하거나 정확한 요청 형태 변환을 캡처) +- 본질적으로 live 전용이라면(rate limit, 인증 정책) live 테스트를 좁게 유지하고 env 변수를 통해 opt-in하도록 하세요. +- 버그를 포착하는 가장 작은 계층을 대상으로 하는 것을 우선하세요: + - 프로바이더 요청 변환/재생 버그 → 직접 모델 테스트 + - gateway 세션/이력/도구 파이프라인 버그 → gateway live 스모크 또는 CI 안전 gateway mock 테스트 - SecretRef 순회 가드레일: - - `src/secrets/exec-secret-ref-id-parity.test.ts`는 레지스트리 메타데이터(`listSecretTargetRegistryEntries()`)에서 SecretRef 클래스별 샘플 대상 하나를 도출한 뒤, 순회 세그먼트 exec ID가 거부되는지 단언합니다. - - `src/secrets/target-registry-data.ts`에 새 `includeInPlan` SecretRef 대상 계열을 추가하면, 해당 테스트의 `classifyTargetClass`를 업데이트하세요. 테스트는 분류되지 않은 대상 ID에서 의도적으로 실패하므로 새 클래스가 조용히 건너뛰어질 수 없습니다. + - `src/secrets/exec-secret-ref-id-parity.test.ts`는 레지스트리 메타데이터(`listSecretTargetRegistryEntries()`)에서 SecretRef 클래스별 샘플 대상 하나를 도출한 뒤, traversal-segment exec id가 거부되는지 검증합니다. + - `src/secrets/target-registry-data.ts`에 새로운 `includeInPlan` SecretRef 대상 계열을 추가한다면, 해당 테스트의 `classifyTargetClass`를 업데이트하세요. 이 테스트는 분류되지 않은 대상 id에서 의도적으로 실패하므로 새로운 클래스가 조용히 건너뛰어질 수 없습니다.