Cordis 플러그인 개발 튜토리얼

첫 Cordis 플러그인 만들기

실행 가능한 TypeScript 플러그인을 만들고 DeepSeek Harness Web UI에 로드하며 자동 정리와 서비스 의존성을 이해합니다.

난이도초급예상 시간15–20분완성 결과로컬 Hello 플러그인

출처 안내

현재 공식 개발자 프리뷰 문서를 바탕으로 한 독립 튜토리얼입니다. 프로덕션 사용 전 업스트림 API를 확인하세요.

공식 튜토리얼소스 저장소

00 · 시작하기 전에

시작하기 전에

공식 튜토리얼은 소스에서 DeepSeek Harness를 실행할 수 있다고 가정합니다. 저장소 루트에서 작업하세요.

  • deepseek-ai/deepseek-harness 로컬 체크아웃
  • 저장소 패키지 관리자로 의존성 설치 완료
  • 저장소 루트에 열린 터미널
  • 체크아웃과 호환되는 Node.js 및 pnpm

DeepSeek Harness와 플러그인 계약은 개발자 프리뷰입니다. 개발 revision을 고정하세요.

1단계

로컬 플러그인 프로젝트 생성

Harness 저장소 안에 임시 프로젝트를 만들어 로드 경로를 명확하고 쉽게 제거할 수 있게 합니다.

shell
mkdir -p scratch-plugin/src

2단계

플러그인 모듈 작성

Harness 플러그인은 apply 함수를 내보내는 TypeScript 모듈입니다. Cordis Context를 통해 기능을 등록합니다.

scratch-plugin/src/my-plugin.ts를 만들고 로그로 로드 성공을 확인합니다.

typescript
import type { Context } from '@deepseek-ai/cordis'

export const name = 'hello-plugin'

export function apply(ctx: Context) {
  // Required dependencies are ready before apply runs.
  console.log('[hello-plugin] plugin loaded!')
}

3단계

cordis.yml에 등록

저장소 루트에서 pwd를 실행하고 scratch-plugin/cordis.yml을 만듭니다. 예제 경로를 실제 절대 경로로 바꾸세요.

yaml
- insert:
    - id: hello
      name: '/absolute/path/to/deepseek-harness/scratch-plugin/src/my-plugin.ts'
플러그인 경로는 절대 경로여야 합니다. Patch는 설정만 추가하며 로더의 Profile 디렉터리를 바꾸지 않습니다.

4단계

오버레이로 Web UI 시작

저장소 루트에서 Patch 파일로 Web UI를 시작하고 http://127.0.0.1:3080을 엽니다.

shell
pnpm dsh web --patch ./scratch-plugin/cordis.yml
성공하면 시작 중 [hello-plugin] plugin loaded!가 터미널에 출력됩니다.

05 · Cordis

수명 주기와 서비스 의존성

타이머, 연결, 도구 또는 공유 기능을 소유하기 전에 Cordis의 정리와 의존성 메커니즘을 이해하세요.

ctx.effect()로 부작용 정리

Context 등록은 플러그인과 함께 제거됩니다. 타이머나 연결처럼 명시적 해제가 필요한 자원은 ctx.effect()에서 정리 함수를 반환합니다.

typescript
import type { Context } from '@deepseek-ai/cordis'

export function apply(ctx: Context) {
  ctx.effect(() => {
    const timer = setInterval(() => {
      console.log('heartbeat')
    }, 5000)

    // Runs automatically when the plugin unloads.
    return () => clearInterval(timer)
  })
}

inject로 서비스 의존성 선언

tools, llm 또는 다른 서비스가 필요하면 inject에 선언합니다. 서비스 준비 후 apply가 호출됩니다.

typescript
import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-tool-plugin'
export const inject = ['tools']

export function apply(ctx: Context) {
  // ctx.tools is ready here.
  ctx.tools.register(/* ... */)
}

06 · API

가장 단순한 플러그인 형태 선택

함수, 객체와 클래스 형태를 지원합니다. 함수로 시작하고 다른 플러그인에 서비스를 제공할 때만 Service 클래스를 사용하세요.

함수

집중된 기능에 적합하며 읽고 테스트하고 언로드하기 쉽습니다.

객체

name, inject, apply와 메타데이터를 함께 구성할 때 유용합니다.

Service 클래스

명명된 Cordis 서비스를 제공하고 수명 주기를 소유할 때 사용합니다.

객체

typescript
export default {
  name: 'my-plugin',
  inject: ['tools'],
  apply(ctx: Context) {
    // Register capabilities here.
  },
}

Service 클래스

typescript
import { Service, type Context } from '@deepseek-ai/cordis'

export default class MyService extends Service {
  static inject = ['tools']

  constructor(ctx: Context) {
    super(ctx, 'myService')
    // Perform synchronous initialization here.
  }
}

07 · Verify

검증 체크리스트

프로세스 시작만 확인하지 말고 로드 경로, 수명 주기와 제거 동작을 확인하세요.

  • 127.0.0.1:3080에서 Web UI가 열립니다.
  • hello-plugin 로드 메시지가 한 번 출력됩니다.
  • 잘못된 절대 경로에서 이해 가능한 오류가 발생합니다.
  • 언로드 후 타이머와 효과가 정리됩니다.
  • Patch 제거 후 원래 Web Profile 동작으로 돌아갑니다.

자주 발생하는 문제

모듈을 찾을 수 없음+

cordis.yml 경로가 절대 경로이며 현재 .ts 파일을 가리키는지 확인하세요.

로드 로그 없음+

저장소 루트에서 실행하고 --patch 경로를 확인하세요.

서비스가 undefined+

서비스 이름을 inject에 넣고 apply 이후에 접근하세요.

3080 포트 사용 중+

해당 프로세스를 중지하거나 현재 업스트림 옵션을 확인하세요.

다음 개발 단계

다음 개발 단계

최소 플러그인으로 로드와 수명 주기를 확인했습니다. 다음에는 실제 Tool, 검증된 설정과 패키징을 추가하세요.