프로바이더란
호출자는 프로바이더(Claude)를 이름으로 부를 뿐, 어댑터를 직접 다루지 않습니다. 이 구분은 의도적입니다. 어댑터는 협력 객체와 상태를 가진 동작하는 객체인 반면, 프로바이더는 CLI의 정체성 — “claude와 대화한다”, “codex로 전환한다”라고 말할 때 가리키는 그것입니다.
command.provider = Codex; // 기계를 넘겨주는 것이 아니라 값 대입프로바이더는 command가 실제로 어댑터를 필요로 하는 시점, 즉 send() 시점에 어댑터를 만듭니다. 그 전이 아닙니다.
정체성과 기계
섹션 제목: “정체성과 기계”두 단어는 실제로 서로 다른 것을 가리키며, 이 분리가 위의 대입문을 정직하게 만듭니다.
| 프로바이더 | 어댑터 | |
|---|---|---|
| 무엇인가 | CLI의 정체성 | 동작하는 객체 |
| 무엇을 가지는가 | 이름, 그리고 어댑터를 만드는 방법 | 협력 객체와 상태 — argv 빌더, PATH 보강, job spawner, 이벤트 파서 |
| 몇 개 존재하는가 | CLI당 하나, 싱글턴으로 export | 세션마다 새로 하나 |
| 호출자가 무엇을 하는가 | 이름으로 부른다 | 결코 손대지 않는다 |
Claude는 인스턴스화할 클래스가 아니라 싱글턴으로 export됩니다. claude CLI는 하나뿐이기 때문입니다. Command.open(Claude, …)는 무언가를 생성하는 문장이 아니라 그것을 지목하는 문장으로 읽혀야 합니다.
import { Claude, Command } from 'activecli';
const command = Command.open(Claude, { workingDir: '/proj' });대입이 공짜인 이유
섹션 제목: “대입이 공짜인 이유”command.provider = Codex가 기계를 넘기는 일이었다면 대가가 있어야 합니다 — 시작할 프로세스, 찾아야 할 실행 파일, 조립할 환경. 그런 대가는 없습니다. 프로바이더는 그중 아무것도 지니고 있지 않기 때문입니다.
이것은 Command가 ActiveRecord의 Relation에서 빌려 온 바로 그 지연입니다. 세터는 의도를 쌓고, toArgv()는 무엇이 실행될지 보여주며, send()가 실행합니다. 커넥션을 바꾸는 일은 실행 전까지 아무 비용도 들지 않습니다.
| ActiveRecord | ActiveCLI |
|---|---|
| 커넥션 전환은 실행 전까지 비용이 없다 | command.provider = Codex는 다음 send() 전까지 비용이 없다 |
command를 다른 CLI로 향하게 하는 것은 대입하는 순간이 아니라 다음 send()에서 효력을 가집니다. CLI 프로세스는 다른 프로세스가 될 수 없으므로, 전환한다는 것은 프로세스를 교체한다는 뜻입니다 — 그리고 실행할 것이 생기기 전까지 그 비용을 치를 이유가 없습니다.
프로바이더가 어댑터를 만든다
섹션 제목: “프로바이더가 어댑터를 만든다”AbstractProvider는 하위 클래스가 채워야 할 멤버 두 개를 선언하며, 그 외에는 아무것도 요구하지 않습니다.
abstract get name(): string;abstract createAdapter(): AbstractAdapter;createAdapter()는 새 어댑터를 반환합니다. 세션은 자신만의 파이프와 상태를 가진 실행 중인 프로세스이므로 자신만의 어댑터를 가집니다. 프로바이더는 두 세션 모두가 그 이름으로 불린 대상일 뿐입니다.
프로바이더를 공짜로 교체할 수 있는 이유도 여기에 있습니다. send()가 어댑터를 요구하기 전까지 Codex에 관한 그 무엇도 살아 있지 않으므로, 그것을 지목하는 일은 그저 지목하는 일입니다.
이 구분이 사는 것
섹션 제목: “이 구분이 사는 것”공통 계약 없이 CLI를 여럿 붙이면, 같은 동사가 각각 다른 파라미터를 받게 됩니다. 한쪽은 세션 id를 받고, 다른 쪽은 스레드 id에 더해 엔드포인트와 서비스 티어를 받는 식입니다. 그러면 호출자는 페이로드를 만들기 위해 지금 상대가 어느 CLI인지 알아야 합니다. 그것은 통합이 아니라 한 지붕 아래의 분리 수납입니다.
정체성을 기계와 분리해 두는 것이 다른 방식을 가능하게 합니다. 호출자는 어느 CLI인지를 한 단어로 말하고, 그 단어가 함축하는 것은 호출자가 아니라 어댑터의 문제입니다.
다음으로 볼 것
섹션 제목: “다음으로 볼 것”- AbstractProvider — 모든 프로바이더가 드러내는 표면, 그리고
ask()가 거기 있는 이유. - Claude 프로바이더 — 현재 존재하는 유일한 구현.
- 프로바이더 작성하기 — 직접 CLI를 붙이기.