@codelifter/sdk
v0.4.7
Published
Codelifter SDK — programmatic API for UI extraction and code generation (built-in adapters: react/vue/svelte/react-native/flutter, custom adapter API, optional internal escape hatch)
Readme
@codelifter/sdk
Surface 4/8 — the builder's lane. High-level API to embed Codelifter in your tool.
The SDK is the surface for builders — Raycast extension authors, Figma plugin developers, VS Code extension authors, and SaaS teams embedding "URL → component code" into their own product. v0.3.0 ships the high-level API (extract, streamExtract, detectLibraries) plus the Custom Adapter API (registerAdapter for React Native, Solid, Flutter, …); low-level escape hatches live behind @codelifter/sdk/internal.
VSCode/Figma/Raycast 익스텐션은 v1.0.3 에 함께 출시되었으며 — 이 SDK 가 그 셋의 공통 토대입니다. 이 패키지는 그 셋의 외부 빌더에게도 동일하게 열려 있습니다.
Why SDK?
Codelifter has 8 user-facing surfaces today (browser, AI editor MCP, terminal CLI, Node.js SDK, Claude Code skill, VSCode/Cursor extension, Figma plugin, Raycast extension). The SDK is the builders' lane — the one external builders use when they want to plug Codelifter's extraction engine into their own tool, without owning a Chromium install or a WebSocket protocol. (We also use it ourselves as the backbone of surfaces 6–8.)
Personas the SDK targets:
- Raycast extension authors — Cmd+Space, type a URL, get React on clipboard.
- Figma plugin developers — accept a URL, return Figma node tree from the detected library.
- VS Code / Cursor / JetBrains extension authors — command palette → URL → editor.
- SaaS embedders — your own dashboard offers "import from URL"; you call
extract()server-side.
The SDK guarantees:
- Same engine as the browser extension (Tier 2 library detection — shadcn/ui, Radix, MUI, etc).
- Automatic fallback — extension bridge → headless Playwright → CDP, no code change needed.
- AbortSignal + onProgress — your UI gets first-class progress, your user can cancel.
- Streaming via async iterator — for editors with long-lived progress UI.
Install
npm install @codelifter/sdkQuickstart
extract — single-shot
import { extract } from '@codelifter/sdk';
const result = await extract('https://stripe.com/payments', {
framework: 'react',
cssMode: 'tailwind',
});
console.log(result.pipelineMode);
// 'extension' | 'headless-bundled' | 'headless-remote'
console.log(result.extractionResult);streamExtract — async iterator with progress
import { streamExtract } from '@codelifter/sdk';
for await (const event of streamExtract('https://stripe.com/payments')) {
if (event.type === 'progress') {
statusBar.show(event.event.message ?? event.event.phase);
} else if (event.type === 'library-detected') {
console.log('Detected:', event.library.name, event.library.confidence);
} else if (event.type === 'done') {
return event.result;
}
}detectLibraries — Tier 2 only
import { detectLibraries } from '@codelifter/sdk';
const libs = await detectLibraries('https://ui.shadcn.com');
// [{ name: 'shadcn/ui', category: 'component-framework', confidence: 1 }, ...]Behind-auth pages (useMyChrome)
// 먼저 별도 터미널에서:
// npx codelifter connect-my-chrome
// (CDP 9222 포트로 사용자 Chrome 실행, 기존 세션 유지)
const result = await extract('https://app.example.com/dashboard', {
useMyChrome: true,
chromePort: 9222,
});Cancellation + progress
import { extract } from '@codelifter/sdk';
const ac = new AbortController();
setTimeout(() => ac.abort(), 30_000); // 30s timeout
try {
const result = await extract('https://example.com', {
signal: ac.signal,
onProgress: (e) => console.log(e.phase, e.message),
});
} catch (err) {
if (ac.signal.aborted) console.log('user cancelled');
}surface option — funnel attribution (권장)
surface 옵션으로 호출 진입점을 명시하면 백엔드가 surface별 funnel을 분리 측정합니다.
// 직접 SDK 호출 (기본값)
await extract(url, { surface: 'sdk' });
// Raycast extension 내부에서 호출
await extract(url, { surface: 'raycast' });
// VSCode extension 내부에서 호출
await extract(url, { surface: 'vscode' });⚠️ Deprecated notice:
surface미지정 시 런타임 경고가 표시됩니다. 다음 메이저 버전에서 필수 파라미터로 변경될 예정입니다. 상위 통합 빌더는 해당 surface 식별자를 명시해주세요.
Builder scenarios
다음 시나리오 셋은 빌더가 SDK 를 자기 도구에 어떻게 박는가의 ready-to-adapt 예시입니다. Codelifter 가 v1.0.3 에서 직접 출시한 IDE/Figma/Raycast 익스텐션의 청사진이기도 합니다.
1. Embed in a Raycast extension
// raycast-extension/src/lift.tsx
import { Detail, popToRoot, showToast, Toast } from '@raycast/api';
import { extract } from '@codelifter/sdk';
export default async function Command(props: { arguments: { url: string } }) {
const toast = await showToast({ style: Toast.Style.Animated, title: 'Lifting...' });
const result = await extract(props.arguments.url, {
onProgress: (e) => (toast.message = e.message),
});
await navigator.clipboard.writeText(result.generatedFiles?.[0]?.content ?? '');
toast.style = Toast.Style.Success;
toast.title = 'Copied React to clipboard';
await popToRoot();
}Raycast 의 Toast 가 onProgress 와 자연스럽게 묶입니다. 사용자는 Cmd+Space → lift <url> → 클립보드에 React 코드, 그게 전부.[^1]
2. Embed in a Figma plugin
// figma-plugin/src/code.ts (백엔드 — Figma sandbox 외부에서 동작)
import { detectLibraries, extract } from '@codelifter/sdk';
figma.ui.onmessage = async (msg) => {
if (msg.type === 'lift-url') {
// 1) 라이브러리 빠르게 감지 → UI 에 어떤 디자인 시스템인지 즉시 표시
const libs = await detectLibraries(msg.url);
figma.ui.postMessage({ type: 'libraries', libs });
// 2) 풀 추출 → DOM/CSS 를 Figma node 트리로 매핑
const result = await extract(msg.url, { framework: 'react' });
figma.ui.postMessage({ type: 'extracted', result });
}
};Figma plugin sandbox 가 Node API 를 제한하므로 실제 SDK 호출은 별도 backend 에서 합니다. detectLibraries 가 가벼워 UI 진행률을 단계별로 채우기 좋습니다.[^1]
3. Embed in a VSCode extension
// vscode-extension/src/extension.ts
import * as vscode from 'vscode';
import { streamExtract } from '@codelifter/sdk';
export function activate(ctx: vscode.ExtensionContext) {
ctx.subscriptions.push(
vscode.commands.registerCommand('codelifter.lift', async () => {
const url = await vscode.window.showInputBox({ prompt: 'URL to lift' });
if (!url) return;
await vscode.window.withProgress(
{ location: vscode.ProgressLocation.Notification, title: 'Codelifter', cancellable: true },
async (progress, token) => {
const ac = new AbortController();
token.onCancellationRequested(() => ac.abort());
for await (const ev of streamExtract(url, { signal: ac.signal })) {
if (ev.type === 'progress') {
progress.report({ message: ev.event.message });
} else if (ev.type === 'done') {
const file = ev.result.generatedFiles?.[0];
if (file) {
const doc = await vscode.workspace.openTextDocument({
content: file.content,
language: 'typescriptreact',
});
await vscode.window.showTextDocument(doc);
}
}
}
}
);
})
);
}VS Code 의 withProgress + cancellation token 이 streamExtract + AbortSignal 과 1:1 로 맵 됩니다. command palette → URL → 활성 에디터에 새 .tsx 가 열리는 흐름.[^1]
[^1]: 위 시나리오 셋의 first-party 출시는 Codelifter 가 v1.0.3 에서 직접 진행했습니다 (packages/vscode-extension, packages/figma-plugin, packages/raycast-extension). SDK 는 외부 빌더에게도 동일하게 열려 있어, 이 코드를 그대로 자기 익스텐션에 가져가도 됩니다.
How to get the pairing token
SDK 가 확장 브리지(extension 파이프라인, 100% Tier 2)를 사용할 때는 페어링 토큰이 필요합니다. 토큰 없이도 headless-bundled 모드는 동작합니다 (~95% Tier 2). 100% 보존을 원하면:
Browser Extension 열기 — 툴바의 Codelifter 아이콘 클릭, 사이드 패널의 Settings 탭으로 이동.
Developer integrations 섹션 펼치기 — "MCP server" 카드.
"Enable MCP server" 토글 ON — 토큰이 자동 생성됩니다.
64자 토큰 표시 — "Show pairing token" 버튼으로 마스킹 해제. 옆의 복사 버튼으로 클립보드에 복사.
SDK 호출 환경에 전달 — 다음 두 방법 중 하나:
# 방법 1: 환경변수 export CODELIFTER_MCP_TOKEN=<pasted-64-char-token> node my-script.mjs # 방법 2: config 파일 (영구) mkdir -p ~/.config/codelifter echo "<pasted-64-char-token>" > ~/.config/codelifter/pairing.token chmod 600 ~/.config/codelifter/pairing.token
Security: 토큰은 Codelifter 서버에 절대 전송되지 않습니다. 로컬 WebSocket 핸드쉐이크 (
x-codelifter-token헤더 +crypto.timingSafeEqual비교) 에만 사용되며, 3 회 실패 시 10 분 차단됩니다.
API reference
extract(url, options?)
| 옵션 | 타입 | 기본값 | 설명 |
|------|------|--------|------|
| selector | string | — | CSS 선택자 (특정 요소만) |
| framework | 'react' \| 'vue' \| 'svelte' \| 'react-native' \| 'flutter' | 'react' | 출력 프레임워크 (RN/Flutter 는 클라이언트 사이드 변환) |
| cssMode | 'tailwind' | 'tailwind' | CSS 모드 |
| useMyChrome | boolean | false | 사용자 Chrome (CDP) 사용 |
| chromePort | number | 9222 | CDP 포트 |
| onProgress | (e: ExtractProgressEvent) => void | — | 진행률 콜백 |
| signal | AbortSignal | — | 취소 신호 |
streamExtract(url, options?)
extract 와 동일 옵션 + AsyncGenerator<ExtractStreamEvent> 반환. yield 하는 이벤트:
{ type: 'progress', event }— onProgress 와 동일 페이로드{ type: 'library-detected', library }— Tier 2 시그니처 매치 시{ type: 'done', result }— 마지막. 호출자가 result 를 사용.
detectLibraries(url, options?)
Tier 2 라이브러리만 빠르게 감지 (코드 생성 비용 회피). 옵션은 extract 의 부분집합 (selector, useMyChrome, chromePort, signal).
Built-in mobile adapters (v0.4.0+)
React Native 와 Flutter adapter 가 SDK 에 기본 동봉됩니다 — registerAdapter 호출 없이 바로 사용:
import { extract } from '@codelifter/sdk';
// React Native — .tsx + StyleSheet.create({...})
const rn = await extract('https://stripe.com/payments', { framework: 'react-native' });
console.log(rn.generatedFiles?.[0]?.path); // 'LiftedScreen.tsx'
// Flutter — .dart + Material widgets
const ft = await extract('https://stripe.com/payments', { framework: 'flutter' });
console.log(ft.generatedFiles?.[0]?.path); // 'liftedscreen.dart'매핑 정책 — 100% 정확도가 목표가 아님. 빌더가 자기 컴포넌트 라이브러리(Paper, NativeBase, gluestack / Material, Cupertino) 컨벤션으로 추가 가공함을 전제로 한 "의도 보존" 수준의 변환:
| HTML | React Native | Flutter |
|------|-------------|---------|
| div | View | Container |
| p / span / h1~h6 | Text | Text |
| img | Image | Image.network |
| a / button | Pressable | InkWell + onTap |
| input / textarea | TextInput | TextField |
| ul / ol | View | Column |
CSS 변환 — padding/margin 분해, color (#rgb/#rrggbb/rgb()/keyword) → Color(0xFFRRGGBB), font-size/weight, border-radius, width/height. 미지원 속성은 무시 (빌더 후가공).
예제: examples/react-native.ts, examples/flutter.ts.
백엔드 영향 — RN/Flutter 변환은 클라이언트 사이드만 — 백엔드는 항상 react 로 호출. v1.0.2 호환성 락 유지.
Custom Adapter API (v0.3.0+)
framework: 'react'|'vue'|'svelte'|'react-native'|'flutter' 외 자기 프레임워크 (Solid, SwiftUI, Compose 등) 를 지원하려면 adapter 를 등록한다.
import { registerAdapter, extract, type CustomAdapter } from '@codelifter/sdk';
const reactNativeAdapter: CustomAdapter = {
name: 'react-native',
mapNode(node, ctx) { /* HTML 태그 → RN 컴포넌트 매핑 */ },
formatOutput(tree, ctx) { /* 트리 → .tsx 파일 */ },
getImports(libraries, ctx) { /* detected library → import 선언 */ },
};
registerAdapter(reactNativeAdapter);
const result = await extract(url, { framework: 'react-native' });또는 registerAdapter 없이 객체를 직접 전달:
const result = await extract(url, { framework: reactNativeAdapter });전체 예제: examples/custom-adapter.ts.
streamExtract 는 adapter 가 등록된 경우 'node-mapped' 이벤트를 추가로 yield 하며, 모든 추출 에러는 'error' 이벤트로 들어온다 (이전엔 그냥 throw 였음).
streamExtract 이벤트 (v0.3.0+)
| type | 발생 조건 |
|------|----------|
| progress | 단계 변경 (connecting → extracting-dom → transforming-code → done) |
| library-detected | Tier 2 시그니처 매치 |
| node-mapped | custom adapter 의 mapNode 가 노드 처리 완료 |
| done | 추출 + adapter 처리 완료 (마지막 이벤트) |
| error | 어느 단계든 실패 (yield 후 throw) |
Timeout
await extract(url, {
timeoutMs: 30_000, // 30초 후 자동 abort
signal: ac.signal, // signal 과 함께 쓰면 둘 중 먼저 발생한 abort 적용
});@codelifter/sdk/internal (escape hatch — 직접 사용 비권장)
import {
extractViaExtensionBridge,
isExtensionBridgeAvailable,
extractViaPlaywright,
extractViaRemoteChrome,
streamTransform,
loadConfig,
saveConfig,
getApiKey,
getApiBaseUrl,
requireApiKey,
} from '@codelifter/sdk/internal';이 entry 의 시그니처는 minor 업데이트에서도 깨질 수 있습니다. SDK semver 는 high-level API(/) 에만 적용됩니다.
Related surfaces (8개 모두 Live)
- Browser Extension (Surface 1/8) — Tier 2 100% 보존의 원천. SDK 는 가능하면 이 확장의 WebSocket 브리지를 우선 시도.
- MCP Server (Surface 2/8) —
codelifter mcp-stdio. SDK 는 이를 직접 부르지 않지만 동일 페어링 토큰을 공유. - CLI (
@codelifter/cli, Surface 3/8) — SDK 가 internal dependency 로 wrapping. 동일 동작 보장. - Claude Code Skill (Surface 5/8) —
/codelifter슬래시. 클립보드/MCP 결과를.tsx로 변환. - VSCode / Cursor Extension (Surface 6/8) — Command Palette 의
Codelifter: Extract from URL. SDK 위에 박힌 IDE 진입점. - Figma Plugin (Surface 7/8) — 라이브 URL 을 Figma frame 으로 lift. SDK backend 호출.
- Raycast Extension (Surface 8/8) — Cmd+Space →
lift <url>→ 클립보드 React. SDK 의extract+onProgress사용.
