@teamlearners/clawops 0.35.0 → 0.36.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -372,6 +372,77 @@ for await (const m of (await client.messages.list()).autoPagingIter()) {
372
372
  const detail = await client.messages.get('MG0123456789abcdef');
373
373
  ```
374
374
 
375
+ ### 카카오 알림톡 (Kakao)
376
+
377
+ 승인된 템플릿으로 알림톡을 보냅니다. 발송에 필요한 채널·템플릿 ID 는 SDK 로 조회합니다.
378
+
379
+ ```typescript
380
+ // 1. 연결된 카카오 채널
381
+ const channels = await client.kakao.channels.list({ status: 'connected' });
382
+ const channel = channels.data[0];
383
+
384
+ // 2. 그 채널의 템플릿 — sendable: true 인 것만 보낼 수 있습니다
385
+ const templates = await client.kakao.templates.list({ channelId: channel.id });
386
+ const template = templates.data.find((t) => t.sendable);
387
+ console.log(template.variables); // ['#{고객명}'] — 이 목록을 모두 채워야 합니다
388
+
389
+ // 3. 발송
390
+ const msg = await client.messages.create({
391
+ to: '01012345678',
392
+ from: '07052358010',
393
+ kakao: {
394
+ channelId: channel.id,
395
+ templateId: template.id,
396
+ variables: { 고객명: '홍길동' }, // '#{고객명}' 표기도 받습니다
397
+ },
398
+ fallback: { body: '주문이 접수되었습니다.' },
399
+ });
400
+ console.log(msg.type); // 'ata'
401
+ ```
402
+
403
+ **본문은 템플릿이 정합니다.** 알림톡에는 `body`·`subject`·`mediaUrl` 을 실을 수 없고(컴파일
404
+ 에러입니다), 버튼·아이템 리스트·강조 문구는 카카오 검수를 받은 그대로 나갑니다 — 발송 요청으로
405
+ 바꿀 수 없습니다. 요청에서 바꿀 수 있는 것은 `variables` 값뿐입니다.
406
+
407
+ **대체발송(`fallback`)은 별도의 메시지 1건으로 기록되고 문자 단가로 청구됩니다.** 생략하면
408
+ 템플릿 본문이 그대로 문자로 나가고, `fallback: { disabled: true }` 면 알림톡 실패가 그대로
409
+ 실패로 남습니다.
410
+
411
+ 변수를 빠뜨리면 발송 전에 `400 kakao_variable_missing` 으로 막힙니다(카카오는 이런 요청도
412
+ 접수한 뒤 조용히 실패시키므로 ClawOps 가 미리 잡습니다). 사유는 `e.code` 로 분기하세요.
413
+
414
+ #### 채널 연결
415
+
416
+ 채널 연결은 두 단계입니다 — 인증번호는 카카오 비즈니스에 등록된 **담당자 휴대전화로만** 갑니다.
417
+
418
+ ```typescript
419
+ const categories = await client.kakao.channelCategories();
420
+
421
+ const requested = await client.kakao.channels.requestToken({
422
+ searchId: '@example',
423
+ phoneNumber: '010-1234-5678',
424
+ });
425
+ console.log(requested.phoneNumberMasked); // '010-****-5678'
426
+
427
+ // 담당자가 받은 인증번호로 완료 (이미 연결된 채널이면 인증번호를 쓰지 않고 기존 연결을 돌려줍니다)
428
+ const channel = await client.kakao.channels.connect({
429
+ searchId: requested.searchId,
430
+ phoneNumber: '010-1234-5678',
431
+ categoryCode: categories.data[0].code,
432
+ token: '394812',
433
+ });
434
+ ```
435
+
436
+ ⚠️ `connect()` 가 **타임아웃되면 재호출하지 마세요.** 이미 연결에 성공했을 수 있습니다 —
437
+ `channels.retrieve(id)` 로 실제 등록 여부를 확인하세요(이 조회는 몇 번을 불러도 안전합니다).
438
+ 연결에 **실패해도 인증번호는 소모되므로** 원인을 해결한 뒤 `requestToken()` 부터 다시 시작해야 합니다.
439
+
440
+ ⚠️ `channels.disconnect(id)` 는 **되돌릴 수 없고 그 채널의 알림톡 템플릿까지 함께 삭제합니다.**
441
+ 템플릿은 카카오 검수를 다시 받아야 합니다.
442
+
443
+ `channels.list()` 는 저장된 연결 정보를 그대로 돌려줍니다(빠릅니다). 카카오 쪽 상태까지 실제로
444
+ 확인하는 것은 `channels.retrieve()` 뿐이며, 이 호출이 `status` 를 갱신합니다.
445
+
375
446
  ### 솔라피(SOLAPI) 호환 — 문자만 ClawOps 로
376
447
 
377
448
  이미 솔라피 SDK 로 작성된 코드를 **그대로 두고** 문자(SMS/LMS/MMS)만 ClawOps 로 보냅니다.
@@ -646,16 +717,33 @@ try {
646
717
  const call = await client.calls.create({ to: '01012345678', from: '07052358010', url: 'https://...' });
647
718
  } catch (e) {
648
719
  if (e instanceof BadRequestError) {
649
- console.log(`잘못된 요청: ${e.statusCode} - ${JSON.stringify(e.body)}`);
720
+ console.log(`잘못된 요청: ${e.status} - ${JSON.stringify(e.body)}`);
650
721
  } else if (e instanceof AuthenticationError) {
651
- console.log(`유효하지 않은 API 키: ${e.statusCode}`);
722
+ console.log(`유효하지 않은 API 키: ${e.status}`);
652
723
  } else if (e instanceof NotFoundError) {
653
- console.log(`리소스를 찾을 수 없음: ${e.statusCode}`);
724
+ console.log(`리소스를 찾을 수 없음: ${e.status}`);
654
725
  }
655
726
  }
656
727
  ```
657
728
 
658
- 모든 에러는 `ClawOpsError`를 상속합니다. HTTP 에러는 `statusCode`, `body` 속성을 제공합니다.
729
+ 모든 에러는 `ClawOpsError`를 상속합니다. HTTP 에러는 `status`, `code`, `body` 속성을 제공합니다.
730
+
731
+ **사유는 `code` 로 분기하세요** — 한 상태 코드가 서로 다른 사유를 담고, 한글 메시지는 바뀔 수
732
+ 있습니다. 서버가 `code` 를 싣지 않은 응답에서는 `undefined` 입니다.
733
+
734
+ ```typescript
735
+ import { UnprocessableEntityError } from '@teamlearners/clawops';
736
+
737
+ try {
738
+ await client.messages.create({ to, from, kakao: { channelId, templateId, variables } });
739
+ } catch (e) {
740
+ if (e instanceof UnprocessableEntityError && e.code === 'recipient_blocked') {
741
+ // 수신거부 — 재시도하면 안 됩니다
742
+ } else if (e instanceof BadRequestError && e.code === 'kakao_variable_missing') {
743
+ // 템플릿 변수 누락 — templates.list() 의 variables 를 다시 확인하세요
744
+ }
745
+ }
746
+ ```
659
747
 
660
748
  | 에러 | 상태 코드 |
661
749
  | -------------------------- | --------- |
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
- var chunkNT6ZS3TQ_cjs = require('../chunk-NT6ZS3TQ.cjs');
4
- var chunkTVDUOKJD_cjs = require('../chunk-TVDUOKJD.cjs');
3
+ var chunkLKFNV2GI_cjs = require('../chunk-LKFNV2GI.cjs');
4
+ var chunkIYSPOOKH_cjs = require('../chunk-IYSPOOKH.cjs');
5
5
  var chunkQD2ZY4UP_cjs = require('../chunk-QD2ZY4UP.cjs');
6
6
  var fs = require('fs');
7
7
  var path = require('path');
@@ -729,7 +729,7 @@ var MAX_ERROR_MESSAGE_LENGTH = 200;
729
729
  function getSdkInfo() {
730
730
  return {
731
731
  name: "clawops-node",
732
- version: chunkNT6ZS3TQ_cjs.VERSION,
732
+ version: chunkLKFNV2GI_cjs.VERSION,
733
733
  runtime: `node/${process.versions.node}`,
734
734
  os: `${process.platform}/${os__default.default.arch()}`
735
735
  };
@@ -1474,7 +1474,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1474
1474
  constructor(options) {
1475
1475
  this._apiKey = options.apiKey ?? process.env["CLAWOPS_API_KEY"] ?? "";
1476
1476
  this._accountId = options.accountId ?? process.env["CLAWOPS_ACCOUNT_ID"] ?? "";
1477
- this._baseUrl = options.baseUrl ?? chunkNT6ZS3TQ_cjs.DEFAULT_BASE_URL;
1477
+ this._baseUrl = options.baseUrl ?? chunkLKFNV2GI_cjs.DEFAULT_BASE_URL;
1478
1478
  this._fromNumber = options.from;
1479
1479
  this._session = options.session;
1480
1480
  this._recording = options.recording ?? false;
@@ -1498,7 +1498,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1498
1498
  }
1499
1499
  static _validateGain(name, gain) {
1500
1500
  if (typeof gain !== "number" || !Number.isFinite(gain) || gain < 0) {
1501
- throw new chunkTVDUOKJD_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1501
+ throw new chunkIYSPOOKH_cjs.AgentError(`${name}=${gain} must be a finite number >= 0`);
1502
1502
  }
1503
1503
  return gain;
1504
1504
  }
@@ -1512,7 +1512,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1512
1512
  tool(nameOrTool, description, parameters, handler) {
1513
1513
  if (typeof nameOrTool === "string") {
1514
1514
  if (!description || !parameters || !handler) {
1515
- throw new chunkTVDUOKJD_cjs.AgentError(
1515
+ throw new chunkIYSPOOKH_cjs.AgentError(
1516
1516
  "tool(name, description, parameters, handler) requires all arguments."
1517
1517
  );
1518
1518
  }
@@ -1548,10 +1548,10 @@ var ClawOpsAgent = class _ClawOpsAgent {
1548
1548
  async connect() {
1549
1549
  if (this._controlWs) return;
1550
1550
  if (!this._apiKey) {
1551
- throw new chunkTVDUOKJD_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1551
+ throw new chunkIYSPOOKH_cjs.AgentError("API key is required. Set CLAWOPS_API_KEY or pass apiKey option.");
1552
1552
  }
1553
1553
  if (!this._accountId) {
1554
- throw new chunkTVDUOKJD_cjs.AgentError(
1554
+ throw new chunkIYSPOOKH_cjs.AgentError(
1555
1555
  "Account ID is required. Set CLAWOPS_ACCOUNT_ID or pass accountId option."
1556
1556
  );
1557
1557
  }
@@ -1575,7 +1575,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1575
1575
  } catch {
1576
1576
  }
1577
1577
  } catch (err) {
1578
- throw new chunkTVDUOKJD_cjs.AgentConnectionError(
1578
+ throw new chunkIYSPOOKH_cjs.AgentConnectionError(
1579
1579
  `Failed to connect to ClawOps: ${err instanceof Error ? err.message : String(err)}`
1580
1580
  );
1581
1581
  }
@@ -1640,7 +1640,7 @@ var ClawOpsAgent = class _ClawOpsAgent {
1640
1640
  });
1641
1641
  if (resp.status !== 201) {
1642
1642
  const error = await resp.json();
1643
- throw new chunkTVDUOKJD_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1643
+ throw new chunkIYSPOOKH_cjs.AgentError(`\uBC1C\uC2E0 \uC2E4\uD328 (${resp.status}): ${error["error"] ?? ""}`);
1644
1644
  }
1645
1645
  const data = await resp.json();
1646
1646
  const callSession = new CallSession({
@@ -1,5 +1,5 @@
1
- import { DEFAULT_BASE_URL, VERSION } from '../chunk-M6BYGYJO.js';
2
- import { AgentError, AgentConnectionError } from '../chunk-36JABW7P.js';
1
+ import { DEFAULT_BASE_URL, VERSION } from '../chunk-XL2VWU36.js';
2
+ import { AgentError, AgentConnectionError } from '../chunk-VYGIWHRU.js';
3
3
  import { NOOP_LOGGER, resolveBuiltinTools, createAgentLogger, createPipelineLogger, BufferingCall, attachBuffered, ulawToPcm16, resamplePcm16, getBuiltinToolSchemas, BUILTIN_TOOL_NAMES, executeBuiltinTool, CALL_NOT_READY_RESULT, pcm16ToUlaw, applyUlawGain } from '../chunk-4YLSDMLL.js';
4
4
  export { BUILTIN_TOOL_NAMES, BuiltinTool, DECODE_TABLE, createAgentLogger, createPipelineLogger, executeBuiltinTool, getBuiltinToolSchemas, isBuiltinTool, pcm16ToUlaw, resamplePcm16, ulawToPcm16 } from '../chunk-4YLSDMLL.js';
5
5
  import * as fs from 'fs';
@@ -19,14 +19,26 @@ var APIStatusError = class extends APIError {
19
19
  status;
20
20
  body;
21
21
  headers;
22
+ /**
23
+ * 서버가 함께 보낸 기계 판독용 코드(`{ error, code }`). 없으면 undefined 다.
24
+ *
25
+ * 같은 상태 코드가 서로 다른 사유를 담는다 — 422 만 해도 수신거부(`recipient_blocked`)와
26
+ * 할당량 초과(`quota_exceeded`)가 갈리고, 한글 문구로 구분하면 문구가 바뀔 때 깨진다.
27
+ */
28
+ code;
22
29
  constructor(message, response, body, request) {
23
30
  super(message, request);
24
31
  this.name = "APIStatusError";
25
32
  this.status = response.status;
26
33
  this.body = body;
27
34
  this.headers = response.headers;
35
+ this.code = extractErrorCode(body);
28
36
  }
29
37
  };
38
+ function extractErrorCode(body) {
39
+ const code = body?.code;
40
+ return typeof code === "string" && code !== "" ? code : void 0;
41
+ }
30
42
  var BadRequestError = class extends APIStatusError {
31
43
  status = 400;
32
44
  constructor(message, response, body, request) {
@@ -175,5 +187,5 @@ exports.ServiceUnavailableError = ServiceUnavailableError;
175
187
  exports.SolapiBridgeError = SolapiBridgeError;
176
188
  exports.UnprocessableEntityError = UnprocessableEntityError;
177
189
  exports.makeStatusError = makeStatusError;
178
- //# sourceMappingURL=chunk-TVDUOKJD.cjs.map
179
- //# sourceMappingURL=chunk-TVDUOKJD.cjs.map
190
+ //# sourceMappingURL=chunk-IYSPOOKH.cjs.map
191
+ //# sourceMappingURL=chunk-IYSPOOKH.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/error.ts"],"names":[],"mappings":"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","file":"chunk-IYSPOOKH.cjs","sourcesContent":["/**\n * 서버가 에러 응답의 `code` 로 싣는 값.\n *\n * 문구가 아니라 **이 값으로 분기한다** — 한글 메시지는 바뀔 수 있다.\n *\n * ⚠️ 문자 도메인은 snake_case, 카카오 채널 도메인은 SCREAMING_CASE 다. 서버가 실제로\n * 그렇게 보내므로 SDK 가 임의로 정규화하지 않는다. 열린 유니온이라 목록에 없는 코드도\n * 그대로 실린다(자동완성만 돕는다).\n */\nexport type ClawOpsErrorCode =\n // 발송 일반\n | 'invalid_phone'\n | 'invalid_type'\n | 'invalid_input'\n | 'from_not_registered'\n | 'body_too_long'\n | 'sms_no_subject'\n | 'sms_no_media'\n | 'lms_no_media'\n | 'too_many_media'\n | 'invalid_media_ext'\n | 'media_download_failed'\n | 'type_not_supported'\n | 'messaging_blocked'\n | 'recipient_blocked'\n | 'quota_exceeded'\n | 'override_quota_exceeded'\n | 'no_active_subscription'\n // 알림톡 발송\n | 'kakao_required'\n | 'kakao_type_conflict'\n | 'kakao_body_not_allowed'\n | 'kakao_subject_not_allowed'\n | 'kakao_media_not_allowed'\n | 'kakao_channel_not_found'\n | 'kakao_template_not_found'\n | 'kakao_template_not_approved'\n | 'kakao_template_dormant'\n | 'kakao_variable_missing'\n | 'kakao_variable_unknown'\n | 'kakao_send_failed'\n | 'kakao_unavailable'\n | 'invalid_fallback_type'\n // 카카오 채널 연동\n | 'KAKAO_TOKEN_INVALID'\n | 'KAKAO_CHANNEL_ALREADY_LINKED'\n | 'KAKAO_CHANNEL_REJECTED'\n | 'KAKAO_RATE_LIMITED'\n | 'KAKAO_PROVIDER_UNAVAILABLE'\n // 공통\n | 'VALIDATION'\n | 'NOT_FOUND'\n | (string & {});\n\nexport class ClawOpsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ClawOpsError';\n }\n}\n\nexport class APIError extends ClawOpsError {\n readonly request: { method: string; url: string };\n\n constructor(message: string, request: { method: string; url: string }) {\n super(message);\n this.name = 'APIError';\n this.request = request;\n }\n}\n\nexport class APIStatusError extends APIError {\n readonly status: number;\n readonly body: unknown;\n readonly headers: Headers | undefined;\n /**\n * 서버가 함께 보낸 기계 판독용 코드(`{ error, code }`). 없으면 undefined 다.\n *\n * 같은 상태 코드가 서로 다른 사유를 담는다 — 422 만 해도 수신거부(`recipient_blocked`)와\n * 할당량 초과(`quota_exceeded`)가 갈리고, 한글 문구로 구분하면 문구가 바뀔 때 깨진다.\n */\n readonly code: ClawOpsErrorCode | undefined;\n\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, request);\n this.name = 'APIStatusError';\n this.status = response.status;\n this.body = body;\n this.headers = response.headers;\n this.code = extractErrorCode(body);\n }\n}\n\n/** 응답 본문에서 `code` 를 꺼낸다. 문자열이 아니면 없는 것으로 본다. */\nfunction extractErrorCode(body: unknown): ClawOpsErrorCode | undefined {\n const code = (body as { code?: unknown } | null | undefined)?.code;\n return typeof code === 'string' && code !== '' ? code : undefined;\n}\n\nexport class BadRequestError extends APIStatusError {\n override readonly status = 400 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends APIStatusError {\n override readonly status = 401 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class PermissionDeniedError extends APIStatusError {\n override readonly status = 403 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'PermissionDeniedError';\n }\n}\n\nexport class NotFoundError extends APIStatusError {\n override readonly status = 404 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends APIStatusError {\n override readonly status = 409 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'ConflictError';\n }\n}\n\nexport class UnprocessableEntityError extends APIStatusError {\n override readonly status = 422 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'UnprocessableEntityError';\n }\n}\n\n/**\n * HTTP 429 Too Many Requests.\n *\n * 동시 통화 한도 초과 등 일시적 제한. SDK는 자동 재시도(최대 2회, 지수 backoff)를 수행한다.\n * 즉각 피드백이 필요하면 client 생성 시 maxRetries: 0으로 재시도를 비활성화하라.\n */\nexport class RateLimitError extends APIStatusError {\n override readonly status = 429 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'RateLimitError';\n }\n}\n\nexport class InternalServerError extends APIStatusError {\n override readonly status = 500 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'InternalServerError';\n }\n}\n\nexport class ServiceUnavailableError extends APIStatusError {\n override readonly status = 503 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'ServiceUnavailableError';\n }\n}\n\nexport class APIConnectionError extends APIError {\n constructor(request: { method: string; url: string }, message = 'Connection error.') {\n super(message, request);\n this.name = 'APIConnectionError';\n }\n}\n\nexport class APITimeoutError extends APIConnectionError {\n constructor(request: { method: string; url: string }) {\n super(request, 'Request timed out.');\n this.name = 'APITimeoutError';\n }\n}\n\nexport class APIResponseValidationError extends APIError {\n readonly status: number;\n\n constructor(response: { status: number }, request: { method: string; url: string }) {\n super('API response validation failed.', request);\n this.name = 'APIResponseValidationError';\n this.status = response.status;\n }\n}\n\n/**\n * 솔라피 호환 레이어가 호출을 거절할 때 쓴다.\n * 조용히 다르게 동작하느니 여기서 멈추는 편이 낫다고 판단한 경우들이다.\n */\nexport class SolapiBridgeError extends ClawOpsError {\n constructor(message: string) {\n super(message);\n this.name = 'SolapiBridgeError';\n }\n}\n\nexport class AgentError extends ClawOpsError {\n constructor(message: string) {\n super(message);\n this.name = 'AgentError';\n }\n}\n\nexport class AgentConnectionError extends AgentError {\n constructor(message = 'Agent WebSocket connection failed.') {\n super(message);\n this.name = 'AgentConnectionError';\n }\n}\n\nconst STATUS_CODE_TO_ERROR: Record<\n number,\n new (\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) => APIStatusError\n> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 403: PermissionDeniedError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n 500: InternalServerError,\n 503: ServiceUnavailableError,\n};\n\nexport function makeStatusError(\n status: number,\n body: unknown,\n headers: Headers | undefined,\n request: { method: string; url: string },\n): APIStatusError {\n let message = '';\n if (body && typeof body === 'object' && 'error' in body) {\n message = (body as { error: string }).error;\n } else {\n message = `HTTP ${status}`;\n }\n\n const ErrorClass = STATUS_CODE_TO_ERROR[status];\n if (ErrorClass) {\n return new ErrorClass(message, { status, headers }, body, request);\n }\n if (status >= 500) {\n return new InternalServerError(message, { status, headers }, body, request);\n }\n return new APIStatusError(message, { status, headers }, body, request);\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  // src/version.ts
4
- var VERSION = "0.35.0" ;
4
+ var VERSION = "0.36.0" ;
5
5
 
6
6
  // src/constants.ts
7
7
  var DEFAULT_BASE_URL = "https://api.claw-ops.com";
@@ -16,5 +16,5 @@ exports.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
16
16
  exports.INITIAL_RETRY_DELAY = INITIAL_RETRY_DELAY;
17
17
  exports.MAX_RETRY_DELAY = MAX_RETRY_DELAY;
18
18
  exports.VERSION = VERSION;
19
- //# sourceMappingURL=chunk-NT6ZS3TQ.cjs.map
20
- //# sourceMappingURL=chunk-NT6ZS3TQ.cjs.map
19
+ //# sourceMappingURL=chunk-LKFNV2GI.cjs.map
20
+ //# sourceMappingURL=chunk-LKFNV2GI.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/constants.ts"],"names":[],"mappings":";;;AAEO,IAAM,OAAA,GACT,QAAA;;;ACHG,IAAM,gBAAA,GAAmB;AACzB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB;AACxB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB","file":"chunk-NT6ZS3TQ.cjs","sourcesContent":["declare const __PKG_VERSION__: string;\n\nexport const VERSION: string = typeof __PKG_VERSION__ !== 'undefined'\n ? __PKG_VERSION__\n : 'dev';\n","export const DEFAULT_BASE_URL = 'https://api.claw-ops.com';\nexport const DEFAULT_MAX_RETRIES = 2;\nexport const DEFAULT_TIMEOUT = 600_000; // ms\nexport const INITIAL_RETRY_DELAY = 500; // ms\nexport const MAX_RETRY_DELAY = 8_000; // ms\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/constants.ts"],"names":[],"mappings":";;;AAEO,IAAM,OAAA,GACT,QAAA;;;ACHG,IAAM,gBAAA,GAAmB;AACzB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB;AACxB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB","file":"chunk-LKFNV2GI.cjs","sourcesContent":["declare const __PKG_VERSION__: string;\n\nexport const VERSION: string = typeof __PKG_VERSION__ !== 'undefined'\n ? __PKG_VERSION__\n : 'dev';\n","export const DEFAULT_BASE_URL = 'https://api.claw-ops.com';\nexport const DEFAULT_MAX_RETRIES = 2;\nexport const DEFAULT_TIMEOUT = 600_000; // ms\nexport const INITIAL_RETRY_DELAY = 500; // ms\nexport const MAX_RETRY_DELAY = 8_000; // ms\n"]}
@@ -17,14 +17,26 @@ var APIStatusError = class extends APIError {
17
17
  status;
18
18
  body;
19
19
  headers;
20
+ /**
21
+ * 서버가 함께 보낸 기계 판독용 코드(`{ error, code }`). 없으면 undefined 다.
22
+ *
23
+ * 같은 상태 코드가 서로 다른 사유를 담는다 — 422 만 해도 수신거부(`recipient_blocked`)와
24
+ * 할당량 초과(`quota_exceeded`)가 갈리고, 한글 문구로 구분하면 문구가 바뀔 때 깨진다.
25
+ */
26
+ code;
20
27
  constructor(message, response, body, request) {
21
28
  super(message, request);
22
29
  this.name = "APIStatusError";
23
30
  this.status = response.status;
24
31
  this.body = body;
25
32
  this.headers = response.headers;
33
+ this.code = extractErrorCode(body);
26
34
  }
27
35
  };
36
+ function extractErrorCode(body) {
37
+ const code = body?.code;
38
+ return typeof code === "string" && code !== "" ? code : void 0;
39
+ }
28
40
  var BadRequestError = class extends APIStatusError {
29
41
  status = 400;
30
42
  constructor(message, response, body, request) {
@@ -155,5 +167,5 @@ function makeStatusError(status, body, headers, request) {
155
167
  }
156
168
 
157
169
  export { APIConnectionError, APIError, APIResponseValidationError, APIStatusError, APITimeoutError, AgentConnectionError, AgentError, AuthenticationError, BadRequestError, ClawOpsError, ConflictError, InternalServerError, NotFoundError, PermissionDeniedError, RateLimitError, ServiceUnavailableError, SolapiBridgeError, UnprocessableEntityError, makeStatusError };
158
- //# sourceMappingURL=chunk-36JABW7P.js.map
159
- //# sourceMappingURL=chunk-36JABW7P.js.map
170
+ //# sourceMappingURL=chunk-VYGIWHRU.js.map
171
+ //# sourceMappingURL=chunk-VYGIWHRU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/error.ts"],"names":[],"mappings":"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","file":"chunk-VYGIWHRU.js","sourcesContent":["/**\n * 서버가 에러 응답의 `code` 로 싣는 값.\n *\n * 문구가 아니라 **이 값으로 분기한다** — 한글 메시지는 바뀔 수 있다.\n *\n * ⚠️ 문자 도메인은 snake_case, 카카오 채널 도메인은 SCREAMING_CASE 다. 서버가 실제로\n * 그렇게 보내므로 SDK 가 임의로 정규화하지 않는다. 열린 유니온이라 목록에 없는 코드도\n * 그대로 실린다(자동완성만 돕는다).\n */\nexport type ClawOpsErrorCode =\n // 발송 일반\n | 'invalid_phone'\n | 'invalid_type'\n | 'invalid_input'\n | 'from_not_registered'\n | 'body_too_long'\n | 'sms_no_subject'\n | 'sms_no_media'\n | 'lms_no_media'\n | 'too_many_media'\n | 'invalid_media_ext'\n | 'media_download_failed'\n | 'type_not_supported'\n | 'messaging_blocked'\n | 'recipient_blocked'\n | 'quota_exceeded'\n | 'override_quota_exceeded'\n | 'no_active_subscription'\n // 알림톡 발송\n | 'kakao_required'\n | 'kakao_type_conflict'\n | 'kakao_body_not_allowed'\n | 'kakao_subject_not_allowed'\n | 'kakao_media_not_allowed'\n | 'kakao_channel_not_found'\n | 'kakao_template_not_found'\n | 'kakao_template_not_approved'\n | 'kakao_template_dormant'\n | 'kakao_variable_missing'\n | 'kakao_variable_unknown'\n | 'kakao_send_failed'\n | 'kakao_unavailable'\n | 'invalid_fallback_type'\n // 카카오 채널 연동\n | 'KAKAO_TOKEN_INVALID'\n | 'KAKAO_CHANNEL_ALREADY_LINKED'\n | 'KAKAO_CHANNEL_REJECTED'\n | 'KAKAO_RATE_LIMITED'\n | 'KAKAO_PROVIDER_UNAVAILABLE'\n // 공통\n | 'VALIDATION'\n | 'NOT_FOUND'\n | (string & {});\n\nexport class ClawOpsError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'ClawOpsError';\n }\n}\n\nexport class APIError extends ClawOpsError {\n readonly request: { method: string; url: string };\n\n constructor(message: string, request: { method: string; url: string }) {\n super(message);\n this.name = 'APIError';\n this.request = request;\n }\n}\n\nexport class APIStatusError extends APIError {\n readonly status: number;\n readonly body: unknown;\n readonly headers: Headers | undefined;\n /**\n * 서버가 함께 보낸 기계 판독용 코드(`{ error, code }`). 없으면 undefined 다.\n *\n * 같은 상태 코드가 서로 다른 사유를 담는다 — 422 만 해도 수신거부(`recipient_blocked`)와\n * 할당량 초과(`quota_exceeded`)가 갈리고, 한글 문구로 구분하면 문구가 바뀔 때 깨진다.\n */\n readonly code: ClawOpsErrorCode | undefined;\n\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, request);\n this.name = 'APIStatusError';\n this.status = response.status;\n this.body = body;\n this.headers = response.headers;\n this.code = extractErrorCode(body);\n }\n}\n\n/** 응답 본문에서 `code` 를 꺼낸다. 문자열이 아니면 없는 것으로 본다. */\nfunction extractErrorCode(body: unknown): ClawOpsErrorCode | undefined {\n const code = (body as { code?: unknown } | null | undefined)?.code;\n return typeof code === 'string' && code !== '' ? code : undefined;\n}\n\nexport class BadRequestError extends APIStatusError {\n override readonly status = 400 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'BadRequestError';\n }\n}\n\nexport class AuthenticationError extends APIStatusError {\n override readonly status = 401 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'AuthenticationError';\n }\n}\n\nexport class PermissionDeniedError extends APIStatusError {\n override readonly status = 403 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'PermissionDeniedError';\n }\n}\n\nexport class NotFoundError extends APIStatusError {\n override readonly status = 404 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'NotFoundError';\n }\n}\n\nexport class ConflictError extends APIStatusError {\n override readonly status = 409 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'ConflictError';\n }\n}\n\nexport class UnprocessableEntityError extends APIStatusError {\n override readonly status = 422 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'UnprocessableEntityError';\n }\n}\n\n/**\n * HTTP 429 Too Many Requests.\n *\n * 동시 통화 한도 초과 등 일시적 제한. SDK는 자동 재시도(최대 2회, 지수 backoff)를 수행한다.\n * 즉각 피드백이 필요하면 client 생성 시 maxRetries: 0으로 재시도를 비활성화하라.\n */\nexport class RateLimitError extends APIStatusError {\n override readonly status = 429 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'RateLimitError';\n }\n}\n\nexport class InternalServerError extends APIStatusError {\n override readonly status = 500 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'InternalServerError';\n }\n}\n\nexport class ServiceUnavailableError extends APIStatusError {\n override readonly status = 503 as const;\n constructor(\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) {\n super(message, response, body, request);\n this.name = 'ServiceUnavailableError';\n }\n}\n\nexport class APIConnectionError extends APIError {\n constructor(request: { method: string; url: string }, message = 'Connection error.') {\n super(message, request);\n this.name = 'APIConnectionError';\n }\n}\n\nexport class APITimeoutError extends APIConnectionError {\n constructor(request: { method: string; url: string }) {\n super(request, 'Request timed out.');\n this.name = 'APITimeoutError';\n }\n}\n\nexport class APIResponseValidationError extends APIError {\n readonly status: number;\n\n constructor(response: { status: number }, request: { method: string; url: string }) {\n super('API response validation failed.', request);\n this.name = 'APIResponseValidationError';\n this.status = response.status;\n }\n}\n\n/**\n * 솔라피 호환 레이어가 호출을 거절할 때 쓴다.\n * 조용히 다르게 동작하느니 여기서 멈추는 편이 낫다고 판단한 경우들이다.\n */\nexport class SolapiBridgeError extends ClawOpsError {\n constructor(message: string) {\n super(message);\n this.name = 'SolapiBridgeError';\n }\n}\n\nexport class AgentError extends ClawOpsError {\n constructor(message: string) {\n super(message);\n this.name = 'AgentError';\n }\n}\n\nexport class AgentConnectionError extends AgentError {\n constructor(message = 'Agent WebSocket connection failed.') {\n super(message);\n this.name = 'AgentConnectionError';\n }\n}\n\nconst STATUS_CODE_TO_ERROR: Record<\n number,\n new (\n message: string,\n response: { status: number; headers?: Headers },\n body: unknown,\n request: { method: string; url: string },\n ) => APIStatusError\n> = {\n 400: BadRequestError,\n 401: AuthenticationError,\n 403: PermissionDeniedError,\n 404: NotFoundError,\n 409: ConflictError,\n 422: UnprocessableEntityError,\n 429: RateLimitError,\n 500: InternalServerError,\n 503: ServiceUnavailableError,\n};\n\nexport function makeStatusError(\n status: number,\n body: unknown,\n headers: Headers | undefined,\n request: { method: string; url: string },\n): APIStatusError {\n let message = '';\n if (body && typeof body === 'object' && 'error' in body) {\n message = (body as { error: string }).error;\n } else {\n message = `HTTP ${status}`;\n }\n\n const ErrorClass = STATUS_CODE_TO_ERROR[status];\n if (ErrorClass) {\n return new ErrorClass(message, { status, headers }, body, request);\n }\n if (status >= 500) {\n return new InternalServerError(message, { status, headers }, body, request);\n }\n return new APIStatusError(message, { status, headers }, body, request);\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  // src/version.ts
2
- var VERSION = "0.35.0" ;
2
+ var VERSION = "0.36.0" ;
3
3
 
4
4
  // src/constants.ts
5
5
  var DEFAULT_BASE_URL = "https://api.claw-ops.com";
@@ -9,5 +9,5 @@ var INITIAL_RETRY_DELAY = 500;
9
9
  var MAX_RETRY_DELAY = 8e3;
10
10
 
11
11
  export { DEFAULT_BASE_URL, DEFAULT_MAX_RETRIES, DEFAULT_TIMEOUT, INITIAL_RETRY_DELAY, MAX_RETRY_DELAY, VERSION };
12
- //# sourceMappingURL=chunk-M6BYGYJO.js.map
13
- //# sourceMappingURL=chunk-M6BYGYJO.js.map
12
+ //# sourceMappingURL=chunk-XL2VWU36.js.map
13
+ //# sourceMappingURL=chunk-XL2VWU36.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts","../src/constants.ts"],"names":[],"mappings":";AAEO,IAAM,OAAA,GACT,QAAA;;;ACHG,IAAM,gBAAA,GAAmB;AACzB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB;AACxB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB","file":"chunk-M6BYGYJO.js","sourcesContent":["declare const __PKG_VERSION__: string;\n\nexport const VERSION: string = typeof __PKG_VERSION__ !== 'undefined'\n ? __PKG_VERSION__\n : 'dev';\n","export const DEFAULT_BASE_URL = 'https://api.claw-ops.com';\nexport const DEFAULT_MAX_RETRIES = 2;\nexport const DEFAULT_TIMEOUT = 600_000; // ms\nexport const INITIAL_RETRY_DELAY = 500; // ms\nexport const MAX_RETRY_DELAY = 8_000; // ms\n"]}
1
+ {"version":3,"sources":["../src/version.ts","../src/constants.ts"],"names":[],"mappings":";AAEO,IAAM,OAAA,GACT,QAAA;;;ACHG,IAAM,gBAAA,GAAmB;AACzB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB;AACxB,IAAM,mBAAA,GAAsB;AAC5B,IAAM,eAAA,GAAkB","file":"chunk-XL2VWU36.js","sourcesContent":["declare const __PKG_VERSION__: string;\n\nexport const VERSION: string = typeof __PKG_VERSION__ !== 'undefined'\n ? __PKG_VERSION__\n : 'dev';\n","export const DEFAULT_BASE_URL = 'https://api.claw-ops.com';\nexport const DEFAULT_MAX_RETRIES = 2;\nexport const DEFAULT_TIMEOUT = 600_000; // ms\nexport const INITIAL_RETRY_DELAY = 500; // ms\nexport const MAX_RETRY_DELAY = 8_000; // ms\n"]}