API 레퍼런스
deplite 패키지가 공개하는 모든 심볼을 시그니처와 함께 정리해요.
처음 보신다면 개요·Quickstart부터 읽어주세요.
진입 클래스
Deplite— External 모드 (Deplite를 호출)DepliteAgent— Embedded 모드 (내 앱이 작업 노드)
이 페이지의 모든 메소드는 4xx/5xx 응답 시 DepliteApiException, 401·403 응답 시 DepliteAuthException을 던질 수 있어요.
처리 방법은 예외 섹션을 참고해주세요.
class Deplite
class Deplite {
Deplite({
required String apiToken,
String baseUrl = 'https://api.deplite.io/v1',
http.Client? client,
});
static const String defaultBaseUrl = 'https://api.deplite.io/v1';
final String apiToken;
final String baseUrl;
late final Triggers triggers;
late final Files files;
void close();
}External 모드 클라이언트예요.
종료 시 close()를 호출하면 내부 http.Client가 정리돼요.
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
apiToken | String | ✓ | dpl_... 토큰 |
baseUrl | String | 기본 https://api.deplite.io/v1 | |
client | http.Client? | 사용자 정의 HTTP 클라이언트 |
Deplite.enroll(…)
static Future<Enrollment> enroll({
required String installCode,
required String name,
String? hostname,
String? os,
String? agentVersion,
String baseUrl = 'https://api.deplite.io/v1',
http.Client? client,
});Embedded 모드용 1회 등록.
새 키쌍을 만들고 /agent/enroll을 호출해 Agent ID와 서버 공개키를 받아와요.
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
installCode | String | ✓ | 대시보드에서 발급한 1회용 설치 코드 (en_...) |
name | String | ✓ | 에이전트 표시 이름 |
hostname | String? | 호스트네임 | |
os | String? | 'ios'·'android' 등 | |
agentVersion | String? | 에이전트 버전 | |
baseUrl | String | API 베이스 오버라이드 |
결과는 Enrollment로 받아오며, 포함된 identity와 privateKey는 SDK가 보관하지 않으므로 호출 측이 암호화 후 저장하는 것을 추천드려요.
final enrollment = await Deplite.enroll(
installCode: installCode,
name: 'kiosk-seoul-01',
);
await secureStore.save(enrollment); // identity + privateKey설치 코드는 1회용이라 enroll 성공 후 폐기돼요.
저장 실패에 대비해 반환값을 먼저 저장한 뒤 후속 로직을 시작하는 게 안전해요.
class Triggers
triggers.run(…)
Future<TriggerRunResult> run({
required String triggerId,
Object? params,
String? workflowName,
String? ref,
bool debug = false,
String? idempotencyKey,
});POST /triggers/{triggerId}/run을 호출해 트리거를 실행해요.
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
triggerId | String | ✓ | 트리거 UUID |
params | Object? | JSON 직렬화 가능한 객체 (Map·List·원시값) | |
workflowName | String? | agent-scope 트리거에서는 필수 | |
ref | String? | git ref | |
debug | bool | verbose 실행 | |
idempotencyKey | String? | 중복 실행 방지 키 |
호출 결과는 TriggerRunResult로 받아요.
final job = await deplite.triggers.run(
triggerId: triggerId,
params: {'ref': 'main', 'actor': 'sehyun'},
idempotencyKey: 'release-${DateTime.now().millisecondsSinceEpoch}',
);
print('${job.jobId} ${job.status}');responseMode: sync로 만들어진 트리거여야 응답에 exitCode·output이 와요.
async 트리거는 jobId만 즉시 반환되고, 결과는 알림이나 워크플로우 콜백으로 받아야 해요.
class Files
External 모드에서 스토리지 파일을 다뤄요.
기본 CleanupRule은 TtlCleanup(86400)(24시간)이에요.
files.upload(…)
Future<FileMeta> upload({
required File file,
CleanupRule cleanupRule = const TtlCleanup(86400),
String? filename,
String? contentType,
String? bindingId,
});presign + PUT + complete를 하나의 호출로 처리해요.
| 옵션 | 타입 | 필수 | 설명 |
|---|---|---|---|
file | File | ✓ | 업로드할 로컬 파일 |
cleanupRule | CleanupRule | 기본 24h TTL | |
filename | String? | 미지정 시 파일명 자동 추론 | |
contentType | String? | MIME 타입 | |
bindingId | String? | 토큰이 여러 binding을 허용할 때 필수 |
업로드가 끝나면 FileMeta 객체로 활성화된 파일 정보를 돌려줘요.
final uploaded = await deplite.files.upload(
file: File('/tmp/app.apk'),
cleanupRule: const TtlCleanup(3600),
contentType: 'application/vnd.android.package-archive',
);
await deplite.triggers.run(
triggerId: triggerId,
params: {'apkFileId': uploaded.id},
);files.presignUpload(…)
Future<PresignedUpload> presignUpload({
required CleanupRule cleanupRule,
String? filename,
String? contentType,
String? bindingId,
});업로드 URL만 발급받아 직접 PUT 한 뒤 completeUpload를 호출하는 저수준 흐름이에요.
응답은 PresignedUpload로 받아오고, 이걸 가지고 직접 PUT 후 completeUpload를 호출하면 끝나요.
External 모드에서는 OnJobEndCleanup을 쓸 수 없어요(잡 컨텍스트가 없으므로).
Agent Files 전용이에요.
files.completeUpload / downloadUrl / download / get / list / delete
Future<FileMeta> completeUpload({required String fileId});
Future<String> downloadUrl({required String fileId});
Future<File> download({required String fileId, required File to});
Future<FileMeta> get({required String fileId});
Future<List<FileMeta>> list({String? bindingId, String? status});
Future<void> delete({required String fileId});completeUpload는 PUT을 마친 뒤 파일을 활성 상태로 만들어요.
downloadUrl은 짧은 만료의 URL을 발급하므로 다운로드 직전에 새로 받는 게 안전해요.
download는 to 파일로 직접 저장해요.
list는 토큰이 접근 가능한 binding 내 파일만 보여줘요.
class DepliteAgent
class DepliteAgent {
DepliteAgent({
required AgentIdentity identity,
required Uint8List privateKey, // 정확히 32바이트
http.Client? client,
});
final AgentIdentity identity;
late final AgentWorkflows workflows;
late final AgentJobs jobs;
late final AgentFiles files;
void close();
}Embedded 모드 클라이언트예요.
enroll() 결과를 별도 저장소에서 꺼내 넘기면 돼요.
| 속성 | 타입 | 설명 |
|---|---|---|
identity | AgentIdentity | 등록 시 받은 비밀이 아닌 신원 |
workflows | AgentWorkflows | 워크플로우 인벤토리 보고 |
jobs | AgentJobs | Job 로그·결과 보고 |
files | AgentFiles | Job 범위 파일 매니저 |
agent.heartbeat()
Future<void> heartbeat();서명된 heartbeat를 1회 송신해요(추천 주기 30초).
agent.updateIdentity(…)
Future<void> updateIdentity({
String? hostname,
String? os,
String? agentVersion,
});호스트네임·OS·버전 변경 시 PATCH로 알려요.
agent.events()
Stream<AgentEvent> events();SSE 스트림을 Dart Stream으로 노출해요.
서버 → Agent 명령(deploy 등)의 서명 검증은 SDK가 자동 처리하므로 호출 측은 이벤트 타입만 분기하면 돼요.
이벤트 종류는 AgentEvent를 참고해주세요.
Dart 3 sealed class라 switch로 exhaustive 매칭을 추천드려요.
await for (final event in agent.events()) {
switch (event) {
case DeployEvent(payload: final p):
await handleDeploy(p);
case SyncWorkflowsEvent():
await reportWorkflows();
case RevokeEvent():
return;
case PingEvent():
continue;
case UnknownEvent(name: final n):
print('unknown event: $n');
}
}전체 시나리오는 예제 페이지에 있어요.
class AgentJobs
jobs.appendLogs(…)
Future<int> appendLogs({
required String jobId,
required List<LogItem> items,
});LogItem 배열을 한 번에 보내요.
서버가 실제로 수락한 라인 수가 돌아오고, 추천 배치 크기는 64KB 또는 1초 간격 중 빠른 쪽이에요.
jobs.reportResult(…)
Future<void> reportResult({
required String jobId,
required JobResult result,
});잡당 1회만 호출해요.
JobResult는 직접 만들기보다 팩토리(JobResult.success 등)를 추천드려요.
try {
final output = await runWorkflow(payload);
await agent.jobs.reportResult(
jobId: payload.jobId,
result: JobResult.success(output: output),
);
} catch (e) {
await agent.jobs.reportResult(
jobId: payload.jobId,
result: JobResult.failed(exitCode: 1, errorMessage: '$e'),
);
}class AgentFiles
Job 컨텍스트 안에서 산출물 파일을 다뤄요.
기본 CleanupRule은 OnJobEndCleanup()이라 잡 종료 시 자동 정리돼요.
각 메소드의 옵션과 반환값은 External Files와 동일하고, 모든 호출에 jobId가 추가로 필요한 점만 달라요.
Future<FileMeta> upload({required String jobId, required File file, ...});
Future<PresignedUpload> presignUpload({required String jobId, ...});
Future<FileMeta> complete({required String fileId});
Future<String> downloadUrl({required String fileId});
Future<File> download({required String fileId, required File to});
Future<FileMeta> get({required String fileId});
Future<void> delete({required String fileId});External의 Files와 달리 모든 호출이 서명되고 jobId로 스코프돼요.
list는 노출되지 않아요.
class AgentWorkflows
workflows.report(…)
Future<int> report(List<WorkflowReport> workflows);서버에 알려진 워크플로우 인벤토리를 WorkflowReport 리스트로 완전히 교체해요.
보고된 워크플로우 수가 돌아와요.
실제 YAML·step run 명령·시크릿 값은 보내지 않는 것을 추천드려요.
이름·메타만 보고하는 게 격리 모델의 핵심이에요.
타입 참조
Enrollment
class Enrollment {
const Enrollment({
required this.identity,
required this.privateKey, // raw 32-byte 개인키
});
final AgentIdentity identity;
final Uint8List privateKey;
}Deplite.enroll의 결과. 두 필드 모두 호출 측이 암호화 후 저장하는 것을 추천드려요.
AgentIdentity
class AgentIdentity {
const AgentIdentity({
required this.agentId,
required this.organizationId,
required this.baseUrl,
required this.serverPublicKeyPem,
});
final String agentId;
final String organizationId;
final String baseUrl;
final String serverPublicKeyPem;
Map<String, dynamic> toJson();
factory AgentIdentity.fromJson(Map<String, dynamic> json);
}비밀이 아니라 toJson()으로 직렬화해 일반 저장소에 두어도 무방해요.
AgentEvent
sealed class AgentEvent { const AgentEvent(); }
class DeployEvent extends AgentEvent {
const DeployEvent(this.payload, this.signature);
final DeployPayload payload;
final String signature;
}
class RevokeEvent extends AgentEvent { const RevokeEvent(); }
class SyncWorkflowsEvent extends AgentEvent { const SyncWorkflowsEvent(); }
class PingEvent extends AgentEvent { const PingEvent(); }
class UnknownEvent extends AgentEvent {
const UnknownEvent(this.name, this.data);
final String name;
final String data;
}| 타입 | 의미 |
|---|---|
DeployEvent | 워크플로우 실행 명령 (payload에 jobId·workflowName·params 등) |
RevokeEvent | Agent 자격 회수 |
SyncWorkflowsEvent | 워크플로우 인벤토리 재보고 요청 |
PingEvent | keep-alive |
UnknownEvent | 알 수 없는 이벤트 또는 서명 검증 실패 |
Dart 3 sealed class라 switch 표현식에서 exhaustive 매칭 가능해요.
DeployPayload
class DeployPayload {
const DeployPayload({
required this.jobId,
required this.workflowName,
this.debug = false,
this.ref,
this.params,
this.issuedAt = 0,
this.nonce = '',
this.force = false,
this.forceReason,
});
final String jobId;
final String workflowName;
final bool debug;
final String? ref;
final Object? params;
final int issuedAt;
final String nonce;
final bool force;
final String? forceReason;
}TriggerRunResult
class TriggerRunResult {
const TriggerRunResult({
required this.jobId,
required this.status,
this.idempotent = false,
this.timedOut = false,
this.exitCode,
this.errorMessage,
this.output,
this.statusUrl,
});
final String jobId;
final String status;
final bool idempotent;
final bool timedOut;
final int? exitCode;
final String? errorMessage;
final Object? output;
final String? statusUrl;
}CleanupRule
sealed class CleanupRule {
const CleanupRule();
String get wireValue;
}
class TtlCleanup extends CleanupRule {
const TtlCleanup(this.ttlSeconds); // 60s ≤ ttl ≤ 90일
final int ttlSeconds;
}
class PersistentCleanup extends CleanupRule {
const PersistentCleanup();
}
class OnJobEndCleanup extends CleanupRule { // Agent Files 전용
const OnJobEndCleanup();
}FileMeta
class FileMeta {
const FileMeta({
required this.id,
this.bindingId,
this.jobId,
this.filename,
this.contentType,
this.size,
this.status,
this.cleanupRule,
this.expiresAt,
this.createdAt,
});
final String id;
final String? bindingId;
final String? jobId;
final String? filename;
final String? contentType;
final int? size;
final String? status;
final String? cleanupRule;
final String? expiresAt;
final String? createdAt;
}PresignedUpload
class PresignedUpload {
const PresignedUpload({
required this.fileId,
required this.uploadUrl,
this.uploadHeaders,
this.expiresInSeconds,
});
final String fileId;
final Uri uploadUrl;
final Map<String, String>? uploadHeaders;
final int? expiresInSeconds;
}LogItem
enum LogStream { raw, system }
enum LogLevel { info, warn, error }
class LogItem {
const LogItem({
required this.seq,
required this.stream,
required this.content,
this.stepName,
this.level,
});
final int seq;
final LogStream stream;
final String content;
final String? stepName;
final LogLevel? level;
}JobResult
enum JobStatus { running, success, failed, timeout, rejected }
class JobResult {
final JobStatus status;
final int? exitCode;
final String? errorMessage;
final Object? output;
final Rejection? rejection;
factory JobResult.running();
factory JobResult.success({int exitCode = 0, Object? output});
factory JobResult.failed({int? exitCode, String? errorMessage});
factory JobResult.timeout({String? errorMessage});
factory JobResult.rejected(Rejection rejection);
}Rejection
class Rejection {
const Rejection({
required this.reason,
this.limitType,
this.retryAfterSeconds,
this.bypassedLimits = const <String>[],
});
final String reason;
final String? limitType;
final int? retryAfterSeconds;
final List<String> bypassedLimits;
}WorkflowReport
class WorkflowReport {
const WorkflowReport({
required this.name,
this.verboseSteps = const <String>[],
this.secretsKeys = const <String>[],
});
final String name;
final List<String> verboseSteps;
final List<String> secretsKeys;
Map<String, dynamic> toJson();
}예외
DepliteException
class DepliteException implements Exception {
DepliteException(this.message, {this.cause});
final String message;
final Object? cause;
}네트워크·키·디스크 등 SDK 내부 오류의 베이스 클래스예요.
DepliteApiException
class DepliteApiException extends DepliteException {
DepliteApiException(this.statusCode, this.body);
final int statusCode;
final String body;
}서버가 비-2xx 응답을 반환했을 때 던져져요(401·403 제외).
DepliteAuthException
class DepliteAuthException extends DepliteApiException {
DepliteAuthException(int statusCode, String body) : super(statusCode, body);
}401·403 전용. 토큰 회수·교체가 필요해요(재시도 무의미). Embedded는 재등록이 필요해요.
추천 처리 패턴
try {
await deplite.triggers.run(triggerId: triggerId, params: {});
} on DepliteAuthException {
// 토큰 회수·교체 필요
} on DepliteApiException catch (e) {
if (e.statusCode == 429 || e.statusCode >= 500) {
// 백오프 후 재시도
}
} on DepliteException catch (e) {
print('${e.message} ${e.cause}');
}