apick-api 2.2.0 → 2.3.1
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/CHANGELOG.md +9 -0
- package/README.md +35 -2
- package/docs/guide.en.md +16 -0
- package/docs/guide.ko.md +18 -0
- package/package.json +11 -10
- package/src/index.cjs +75 -4
- package/src/index.d.ts +18 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.1 - 2026-09-05
|
|
4
|
+
|
|
5
|
+
- 이미지 편집 입력에서 마스크 파일을 제거하고 원본 이미지와 프롬프트만 받도록 계약을 단순화했습니다.
|
|
6
|
+
|
|
7
|
+
## 2.3.0 - 2026-09-05
|
|
8
|
+
|
|
9
|
+
- 이미지 생성·편집과 최대 50장 비동기 작업 메서드 8종을 추가했습니다.
|
|
10
|
+
- 이미지당 25포인트, 성공분 과금, 멱등 키, 24시간 결과 보관 계약을 문서화했습니다.
|
|
11
|
+
|
|
3
12
|
## 2.2.0 - 2026-09-03
|
|
4
13
|
|
|
5
14
|
- `maskResidenceCard()`의 개인정보 마스킹 대상을 외국인등록증·영주증·외국국적동포 국내거소신고증으로 확대했습니다.
|
package/README.md
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
# APICK API for Node.js
|
|
4
4
|
|
|
5
|
-
**에이픽
|
|
5
|
+
**에이픽 데이터·AI·이미지 API를 API 키 하나로 호출하는 공식 Node.js SDK**
|
|
6
6
|
|
|
7
|
-
**Official zero-dependency Node.js SDK for
|
|
7
|
+
**Official zero-dependency Node.js SDK for APICK data, AI, and image APIs**
|
|
8
8
|
|
|
9
9
|
[](https://www.npmjs.com/package/apick-api)
|
|
10
10
|
[](https://nodejs.org/)
|
|
@@ -76,6 +76,13 @@ Leave the allowed-IP list blank for unrestricted access. To restrict access, reg
|
|
|
76
76
|
| `jsonToExcel(data, options)` | JSON→Excel | Binary |
|
|
77
77
|
| `summarize(text)` | 텍스트 요약 / Text summarization | JSON |
|
|
78
78
|
| `polish(text)` | 텍스트 다듬기 / Text polishing | JSON |
|
|
79
|
+
| `generateImages(prompt, options)` | 이미지 생성 / Image generation | JSON |
|
|
80
|
+
| `editImages(image, prompt, options)` | 이미지 편집 / Image editing | JSON |
|
|
81
|
+
| `createImageGenerationJob(prompt, options)` | 대량 이미지 생성 작업 / Batch generation job | JSON |
|
|
82
|
+
| `createImageEditJob(image, prompt, options)` | 대량 이미지 편집 작업 / Batch edit job | JSON |
|
|
83
|
+
| `getImageJob(jobId)` · `cancelImageJob(jobId)` | 이미지 작업 조회·취소 / Job status and cancellation | JSON |
|
|
84
|
+
| `downloadImageJobImage(jobId, index)` | 개별 결과 / Individual result | Binary |
|
|
85
|
+
| `downloadImageJobArchive(jobId)` | ZIP 결과 / ZIP archive | Binary |
|
|
79
86
|
|
|
80
87
|
## JSON 결과 / JSON results
|
|
81
88
|
|
|
@@ -92,6 +99,32 @@ console.log(result.meta);
|
|
|
92
99
|
|
|
93
100
|
## 파일 입력 / File input
|
|
94
101
|
|
|
102
|
+
## 이미지 생성·편집 / Image generation and editing
|
|
103
|
+
|
|
104
|
+
이미지는 장당 25포인트이며 동기는 1~4장, 작업형 API는 최대 50장까지 지원합니다. 성공적으로 저장된 이미지에 대해서만 과금되며 결과는 완료 후 24시간 동안 반복 다운로드할 수 있습니다.
|
|
105
|
+
|
|
106
|
+
Images cost 25 points each. Synchronous calls support 1–4 images and job calls support up to 50. Only stored successful results are charged, and completed job results remain downloadable for 24 hours.
|
|
107
|
+
|
|
108
|
+
```js
|
|
109
|
+
const made = await apick.generateImages("따뜻한 조명의 미니멀 제품 사진", {
|
|
110
|
+
count: 2, size: "1024x1024", outputFormat: "webp",
|
|
111
|
+
idempotencyKey: "catalog-cover-20260905"
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const edited = await apick.editImages("./source.png", "컵 색상을 파란색으로 변경", {
|
|
115
|
+
outputFormat: "png"
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const queued = await apick.createImageGenerationJob("여행 포스터 시안", { count: 20 });
|
|
119
|
+
const job = await apick.getImageJob(queued.data.job_id);
|
|
120
|
+
const image = await apick.downloadImageJobImage(job.data.job_id, 0);
|
|
121
|
+
await image.save("./result.png");
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
PNG·JPEG·WebP 출력, 투명 배경 미리보기(PNG/WebP), 표준 및 사용자 지정 크기를 지원합니다. 입력 프롬프트는 최대 6,000자입니다. 자동 재시도는 하지 않습니다.
|
|
125
|
+
|
|
126
|
+
PNG, JPEG, and WebP outputs, transparent-background previews for PNG/WebP, and standard or custom sizes are supported. Prompts are limited to 6,000 characters. Requests are never retried automatically.
|
|
127
|
+
|
|
95
128
|
OCR은 PNG/JPEG 파일 경로, `Blob`, `ArrayBuffer`, `Uint8Array`를 받습니다. 최대 크기는 50MB입니다.
|
|
96
129
|
OCR accepts a PNG/JPEG file path, `Blob`, `ArrayBuffer`, or `Uint8Array`, up to 50MB.
|
|
97
130
|
|
package/docs/guide.en.md
CHANGED
|
@@ -123,6 +123,22 @@ const polished = await client.polish(draftText);
|
|
|
123
123
|
|
|
124
124
|
Text input is limited to 100,000 characters.
|
|
125
125
|
|
|
126
|
+
## Image AI
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const result = await client.generateImages('A clean product photo on white', {
|
|
130
|
+
count: 4, size: '1024x1024', outputFormat: 'webp',
|
|
131
|
+
idempotencyKey: 'product-draft-001'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
const job = await client.createImageGenerationJob('Landscape article cover concepts', { count: 20, size: '1536x1024' });
|
|
135
|
+
const status = await client.getImageJob(job.data.job_id);
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Synchronous generation and editing support 1–4 images; job methods support 1–50. Editing accepts one PNG, JPEG, or WebP source up to 50 MB plus a prompt; mask files are not supported. Each stored successful image costs 25 points, while reservations for failed or cancelled items are released. Results remain available for 24 hours.
|
|
139
|
+
|
|
140
|
+
Methods: `generateImages`, `editImages`, `createImageGenerationJob`, `createImageEditJob`, `getImageJob`, `cancelImageJob`, `downloadImageJobImage`, and `downloadImageJobArchive`.
|
|
141
|
+
|
|
126
142
|
## Response shape
|
|
127
143
|
|
|
128
144
|
JSON methods resolve to:
|
package/docs/guide.ko.md
CHANGED
|
@@ -123,6 +123,24 @@ const polished = await client.polish(draftText);
|
|
|
123
123
|
|
|
124
124
|
입력 텍스트는 최대 100,000자입니다.
|
|
125
125
|
|
|
126
|
+
## 이미지 AI
|
|
127
|
+
|
|
128
|
+
```js
|
|
129
|
+
const result = await client.generateImages('흰 배경의 제품 사진', {
|
|
130
|
+
count: 4,
|
|
131
|
+
size: '1024x1024',
|
|
132
|
+
outputFormat: 'webp',
|
|
133
|
+
idempotencyKey: 'product-draft-001'
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const job = await client.createImageGenerationJob('가로형 커버 시안', { count: 20, size: '1536x1024' });
|
|
137
|
+
const status = await client.getImageJob(job.data.job_id);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
동기 생성·편집은 1~4장, 작업형 생성·편집은 1~50장입니다. 편집은 50MB 이하의 PNG/JPEG/WebP 원본 이미지 한 장과 프롬프트를 받으며 마스크 파일은 지원하지 않습니다. 성공 이미지 한 장당 25포인트가 확정되며 실패·취소 수량의 예약 포인트는 해제됩니다. 결과 보관 기간은 완료 후 24시간입니다.
|
|
141
|
+
|
|
142
|
+
지원 메서드: `generateImages`, `editImages`, `createImageGenerationJob`, `createImageEditJob`, `getImageJob`, `cancelImageJob`, `downloadImageJobImage`, `downloadImageJobArchive`.
|
|
143
|
+
|
|
126
144
|
## 응답 구조
|
|
127
145
|
|
|
128
146
|
JSON 메서드:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apick-api",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Official zero-dependency Node.js client for
|
|
3
|
+
"version": "2.3.1",
|
|
4
|
+
"description": "Official zero-dependency Node.js client for APICK data, AI, and image APIs. 에이픽 데이터·AI·이미지 API 공식 Node.js SDK.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.cjs",
|
|
7
7
|
"module": "./src/index.js",
|
|
@@ -27,14 +27,14 @@
|
|
|
27
27
|
"node": ">=18"
|
|
28
28
|
},
|
|
29
29
|
"scripts": {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
"test": "node --test && npm run test:types",
|
|
31
|
+
"test:types": "tsc -p tsconfig.json",
|
|
32
|
+
"test:coverage": "node --test --experimental-test-coverage",
|
|
33
|
+
"prepublishOnly": "npm test"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"typescript": "^7.0.2"
|
|
34
37
|
},
|
|
35
|
-
"devDependencies": {
|
|
36
|
-
"typescript": "^7.0.2"
|
|
37
|
-
},
|
|
38
38
|
"repository": {
|
|
39
39
|
"type": "git",
|
|
40
40
|
"url": "git+https://github.com/lead788/apick-api.git"
|
|
@@ -65,6 +65,7 @@
|
|
|
65
65
|
"pdf",
|
|
66
66
|
"excel",
|
|
67
67
|
"tts-jobs",
|
|
68
|
-
"text-summary"
|
|
68
|
+
"text-summary",
|
|
69
|
+
"image-generation"
|
|
69
70
|
]
|
|
70
71
|
}
|
package/src/index.cjs
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
const DEFAULT_BASE_URL = 'https://apick.app';
|
|
4
4
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
5
5
|
const MAX_OCR_BYTES = 50 * 1024 * 1024;
|
|
6
|
+
const MAX_IMAGE_AI_BYTES = 50 * 1024 * 1024;
|
|
6
7
|
const TTS_VOICE_IDS = Object.freeze([
|
|
7
8
|
'narrator_m_01', 'narrator_m_02', 'narrator_m_03', 'narrator_m_04', 'narrator_m_05',
|
|
8
9
|
'narrator_f_10s_01', 'narrator_f_10s_02', 'narrator_f_10s_03',
|
|
@@ -37,7 +38,8 @@ const SERVICE_DEFINITIONS = Object.freeze({
|
|
|
37
38
|
htmlToPdf: { endpoint: '/rest/html_to_pdf', timeoutMs: 25_000, output: 'binary', filename: 'document.pdf' },
|
|
38
39
|
jsonToExcel: { endpoint: '/rest/json_to_excel', timeoutMs: 45_000, output: 'binary', filename: 'data.xlsx' },
|
|
39
40
|
summarize: { endpoint: '/rest/llm/text_summary', timeoutMs: 75_000, output: 'json' },
|
|
40
|
-
polish: { endpoint: '/rest/llm/text_polish', timeoutMs: 105_000, output: 'json' }
|
|
41
|
+
polish: { endpoint: '/rest/llm/text_polish', timeoutMs: 105_000, output: 'json' },
|
|
42
|
+
generateImages: { endpoint: '/rest/image-generation/generate', timeoutMs: 190_000, output: 'json' }
|
|
41
43
|
});
|
|
42
44
|
|
|
43
45
|
const SERVICES = Object.freeze(Object.fromEntries(
|
|
@@ -151,6 +153,7 @@ function inferImageType(filename) {
|
|
|
151
153
|
const lower = String(filename || '').toLowerCase();
|
|
152
154
|
if (lower.endsWith('.png')) return 'image/png';
|
|
153
155
|
if (lower.endsWith('.jpg') || lower.endsWith('.jpeg')) return 'image/jpeg';
|
|
156
|
+
if (lower.endsWith('.webp')) return 'image/webp';
|
|
154
157
|
return '';
|
|
155
158
|
}
|
|
156
159
|
|
|
@@ -182,10 +185,11 @@ async function normalizeImage(image, options) {
|
|
|
182
185
|
}
|
|
183
186
|
|
|
184
187
|
const contentType = config.contentType || blob.type || inferImageType(filename);
|
|
185
|
-
|
|
186
|
-
|
|
188
|
+
const allowedTypes = config.allowedTypes || ['image/png', 'image/jpeg'];
|
|
189
|
+
if (!allowedTypes.includes(contentType)) {
|
|
190
|
+
throw new TypeError(config.typeError || 'OCR supports PNG and JPEG images only.');
|
|
187
191
|
}
|
|
188
|
-
if (blob.size > MAX_OCR_BYTES) {
|
|
192
|
+
if (blob.size > (config.maxBytes || MAX_OCR_BYTES)) {
|
|
189
193
|
throw new RangeError('image must not exceed 50 MB.');
|
|
190
194
|
}
|
|
191
195
|
return { blob, filename, contentType };
|
|
@@ -522,6 +526,73 @@ class ApickClient {
|
|
|
522
526
|
polish(text) {
|
|
523
527
|
return this._call('polish', { text: requiredString('text', text, 100_000) });
|
|
524
528
|
}
|
|
529
|
+
|
|
530
|
+
_imageOptions(prompt, options, maxCount) {
|
|
531
|
+
const config = options || {};
|
|
532
|
+
for (const key of ['model', 'quality', 'n', 'input_fidelity', 'moderation', 'mask', 'maskFilename', 'maskContentType']) {
|
|
533
|
+
if (Object.prototype.hasOwnProperty.call(config, key)) throw new TypeError(`${key} is not a supported image option.`);
|
|
534
|
+
}
|
|
535
|
+
const count = positiveInteger('count', config.count, 1);
|
|
536
|
+
if (count > maxCount) throw new RangeError(`count must not exceed ${maxCount}.`);
|
|
537
|
+
const payload = {
|
|
538
|
+
prompt: requiredString('prompt', prompt, 6_000), count,
|
|
539
|
+
size: config.size || '1024x1024', output_format: config.outputFormat || 'png',
|
|
540
|
+
background: config.background || 'auto'
|
|
541
|
+
};
|
|
542
|
+
if (config.outputCompression !== undefined) payload.output_compression = config.outputCompression;
|
|
543
|
+
if (config.idempotencyKey !== undefined) {
|
|
544
|
+
payload.idempotency_key = requiredString('idempotencyKey', config.idempotencyKey, 128);
|
|
545
|
+
if (!/^[A-Za-z0-9_-]{8,128}$/.test(payload.idempotency_key)) throw new TypeError('idempotencyKey must use 8-128 letters, numbers, underscores, or hyphens.');
|
|
546
|
+
}
|
|
547
|
+
return payload;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
generateImages(prompt, options) {
|
|
551
|
+
return this._call('generateImages', this._imageOptions(prompt, options, 4));
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
async editImages(image, prompt, options) {
|
|
555
|
+
const config = options || {}, payload = this._imageOptions(prompt, config, 4);
|
|
556
|
+
const uploadOptions = Object.assign({}, config, { allowedTypes:['image/png','image/jpeg','image/webp'], maxBytes:MAX_IMAGE_AI_BYTES, typeError:'image must be PNG, JPEG, or WebP.' });
|
|
557
|
+
const source = await normalizeImage(image, uploadOptions), form = new FormData();
|
|
558
|
+
Object.entries(payload).forEach(([key,value]) => form.append(key, String(value)));
|
|
559
|
+
form.append('image', source.blob, source.filename);
|
|
560
|
+
return this._call('generateImages', null, form, { endpoint:'/rest/image-generation/edit' });
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
createImageGenerationJob(prompt, options) {
|
|
564
|
+
return this._call('generateImages', this._imageOptions(prompt, options, 50), null, { endpoint:'/rest/image-generation/jobs/generate', timeoutMs:60_000 });
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
async createImageEditJob(image, prompt, options) {
|
|
568
|
+
const config = options || {}, payload = this._imageOptions(prompt, config, 50);
|
|
569
|
+
const uploadOptions = Object.assign({}, config, { allowedTypes:['image/png','image/jpeg','image/webp'], maxBytes:MAX_IMAGE_AI_BYTES, typeError:'image must be PNG, JPEG, or WebP.' });
|
|
570
|
+
const source = await normalizeImage(image, uploadOptions), form = new FormData();
|
|
571
|
+
Object.entries(payload).forEach(([key,value]) => form.append(key, String(value)));
|
|
572
|
+
form.append('image', source.blob, source.filename);
|
|
573
|
+
return this._call('generateImages', null, form, { endpoint:'/rest/image-generation/jobs/edit', timeoutMs:60_000 });
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
getImageJob(jobId) {
|
|
577
|
+
const id = normalizeTtsJobId(jobId);
|
|
578
|
+
return this._call('generateImages', null, null, { endpoint:'/rest/image-generation/jobs/'+id, method:'GET', timeoutMs:30_000 });
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
cancelImageJob(jobId) {
|
|
582
|
+
const id = normalizeTtsJobId(jobId);
|
|
583
|
+
return this._call('generateImages', null, null, { endpoint:'/rest/image-generation/jobs/'+id+'/cancel', timeoutMs:30_000 });
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
downloadImageJobImage(jobId, index) {
|
|
587
|
+
const id=normalizeTtsJobId(jobId), value=Number(index);
|
|
588
|
+
if(!Number.isInteger(value)||value<0||value>49) throw new RangeError('index must be an integer from 0 through 49.');
|
|
589
|
+
return this._call('generateImages', null, null, { endpoint:'/rest/image-generation/jobs/'+id+'/images/'+value, method:'GET', output:'binary', filename:id+'-'+value+'.bin', timeoutMs:60_000 });
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
downloadImageJobArchive(jobId) {
|
|
593
|
+
const id=normalizeTtsJobId(jobId);
|
|
594
|
+
return this._call('generateImages', null, null, { endpoint:'/rest/image-generation/jobs/'+id+'/result', method:'GET', output:'binary', filename:id+'.zip', timeoutMs:60_000 });
|
|
595
|
+
}
|
|
525
596
|
}
|
|
526
597
|
|
|
527
598
|
module.exports = {
|
package/src/index.d.ts
CHANGED
|
@@ -27,6 +27,16 @@ export interface OcrOptions {
|
|
|
27
27
|
contentType?: 'image/png' | 'image/jpeg';
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export type ImageAiFormat = 'png' | 'jpeg' | 'webp';
|
|
31
|
+
export type ImageAiBackground = 'auto' | 'opaque' | 'transparent';
|
|
32
|
+
export type ImageAiStatus = 'waiting' | 'processing' | 'completed' | 'completed_partial' | 'cancelled' | 'failed';
|
|
33
|
+
export type ApickImageErrorCode = `APICK_IMAGE_${string}`;
|
|
34
|
+
export interface ImageAiOptions { count?: number; size?: string; outputFormat?: ImageAiFormat; background?: ImageAiBackground; outputCompression?: number; idempotencyKey?: string; }
|
|
35
|
+
export interface ImageAiEditOptions extends ImageAiOptions { filename?: string; contentType?: 'image/png'|'image/jpeg'|'image/webp'; }
|
|
36
|
+
export interface ImageAiResultImage { index:number; b64_json:string; mime_type:'image/png'|'image/jpeg'|'image/webp'; width:number; height:number; }
|
|
37
|
+
export interface ImageAiResultData { request_id:string; count:number; images:ImageAiResultImage[]; idempotent_replay?:boolean; }
|
|
38
|
+
export interface ImageAiJobData { job_id:string; status:ImageAiStatus; requested_count:number; completed_count?:number; failed_count?:number; charged_point?:number; result_available?:boolean; expires_at?:string|null; error_code?:ApickImageErrorCode|null; }
|
|
39
|
+
|
|
30
40
|
export interface MaskResidentNumberOptions extends OcrOptions {
|
|
31
41
|
type: 1 | 2 | 3;
|
|
32
42
|
}
|
|
@@ -111,6 +121,14 @@ export class ApickClient {
|
|
|
111
121
|
jsonToExcel(data: unknown[], options?: { sheetName?: string }): Promise<ApickBinaryResult>;
|
|
112
122
|
summarize(text: string): Promise<ApickResult>;
|
|
113
123
|
polish(text: string): Promise<ApickResult>;
|
|
124
|
+
generateImages(prompt:string, options?:ImageAiOptions): Promise<ApickResult<ImageAiResultData>>;
|
|
125
|
+
editImages(image:string|BinaryInput|ArrayBuffer|ArrayBufferView, prompt:string, options?:ImageAiEditOptions): Promise<ApickResult<ImageAiResultData>>;
|
|
126
|
+
createImageGenerationJob(prompt:string, options?:ImageAiOptions): Promise<ApickResult<ImageAiJobData>>;
|
|
127
|
+
createImageEditJob(image:string|BinaryInput|ArrayBuffer|ArrayBufferView, prompt:string, options?:ImageAiEditOptions): Promise<ApickResult<ImageAiJobData>>;
|
|
128
|
+
getImageJob(jobId:string): Promise<ApickResult<ImageAiJobData>>;
|
|
129
|
+
cancelImageJob(jobId:string): Promise<ApickResult<ImageAiJobData>>;
|
|
130
|
+
downloadImageJobImage(jobId:string,index:number): Promise<ApickBinaryResult>;
|
|
131
|
+
downloadImageJobArchive(jobId:string): Promise<ApickBinaryResult>;
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
export default ApickClient;
|