connectbase-client 3.39.2 → 3.42.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/CHANGELOG.md +41 -0
- package/dist/cli.js +193 -2
- package/dist/index.d.mts +15 -3
- package/dist/index.d.ts +15 -3
- package/dist/index.js +4 -3
- package/dist/index.mjs +4 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,47 @@
|
|
|
3
3
|
본 SDK 의 모든 주요 변경사항을 [Keep a Changelog](https://keepachangelog.com/ko/1.1.0/) 형식으로 기록합니다.
|
|
4
4
|
버전은 [Semantic Versioning](https://semver.org/lang/ko/) 을 따릅니다.
|
|
5
5
|
|
|
6
|
+
## [3.42.0] - 2026-07-02
|
|
7
|
+
|
|
8
|
+
### Added — 웹 배포 대용량 바이너리 Object Storage 직접 업로드 (플랫폼 이슈 019f221b)
|
|
9
|
+
|
|
10
|
+
3.41.0 의 fail-fast 를 이어, 한도를 넘는 **바이너리**를 배포할 수 있게 했다. 파일당 6 MiB(TiDB
|
|
11
|
+
단일 엔트리 한도)를 넘는 바이너리(3D 모델·큰 동영상/폰트 등)를 배포하지 못하던 문제 해결.
|
|
12
|
+
|
|
13
|
+
- **대용량 바이너리 직접 업로드**: 한도를 넘는 바이너리는 presigned URL 로 **Object Storage 의 서빙
|
|
14
|
+
경로에 직접** 업로드하고, 배포 요청에는 `pre_uploaded` 메타데이터만 전송한다. web-proxy 가 서빙하는
|
|
15
|
+
바로 그 경로에 안착하므로 동일 origin·참조 재작성/CORS 불필요. (대응 core-server 필요. 미지원 서버면
|
|
16
|
+
3.41.0 과 동일하게 fail-fast.)
|
|
17
|
+
- **fail-fast 유지**: 오프로드 불가한 파일(대용량 **텍스트**, 또는 서버 미지원)은 업로드 전에 즉시 중단.
|
|
18
|
+
- **3D 에셋 확장자 추가**: `.glb`/`.bin`(바이너리), `.gltf`(JSON)을 허용 확장자에 추가 +
|
|
19
|
+
정확한 MIME(`model/gltf-binary` 등). 이제 3D 모델을 확장자 위장 없이 번들에 그대로 둘 수 있다.
|
|
20
|
+
|
|
21
|
+
## [3.41.0] - 2026-07-02
|
|
22
|
+
|
|
23
|
+
### Added — 웹 배포 파일당 크기 한도 사전 검증 (fail-fast)
|
|
24
|
+
|
|
25
|
+
`connectbase deploy` 가 파일을 수집한 직후, 서버로 업로드하기 **전에** 각 파일이 웹 스토리지
|
|
26
|
+
파일당 크기 한도를 넘는지 검사한다(플랫폼 이슈 019f221b). 기존에는 전량 수집·업로드를 마친
|
|
27
|
+
뒤에야 서버에서 원시 DB 오류(`Error 8025 (HY000): entry too large`)로 실패해 원인 파악이 어려웠다.
|
|
28
|
+
|
|
29
|
+
- 초과 파일이 있으면 어떤 파일이 얼마나 큰지(바이너리는 원본→base64 크기까지) 나열하고 즉시 종료한다.
|
|
30
|
+
- 검사 기준: 파일 내용은 DB(TiDB Text 컬럼)에 저장되며 단일 엔트리 한도 6 MiB 를 넘을 수 없다.
|
|
31
|
+
바이너리는 base64(약 1.33x)로 저장되므로 원본 기준 실질 상한 약 4.5 MB. 텍스트는 UTF-8 바이트로 정확히 계산.
|
|
32
|
+
|
|
33
|
+
## [3.40.0] - 2026-06-28
|
|
34
|
+
|
|
35
|
+
### Added — KB 하이브리드(시맨틱) 검색 지원
|
|
36
|
+
|
|
37
|
+
지식베이스 검색에 BM25 키워드 + 벡터 의미 검색을 RRF(Reciprocal Rank Fusion)로 융합하는
|
|
38
|
+
하이브리드 검색을 추가했다. 콘솔 > **AI** 에서 임베딩을 켠 앱(gemini / openai BYOK)에서
|
|
39
|
+
동작하며, 오타·동의어·의역에 강하다.
|
|
40
|
+
|
|
41
|
+
- **\`KnowledgeSearchRequest.hybrid?: boolean\`** — \`cb.knowledge.search()\` 에 hybrid 옵션 추가.
|
|
42
|
+
미설정(기본)이면 임베딩을 켠 앱에서 자동 적용, \`false\` 면 키워드 전용 검색을 강제.
|
|
43
|
+
- **\`AIChatRequest.hybrid?: boolean\`** — \`cb.ai.chat()\` / \`cb.ai.chatStream()\` 의 RAG 검색에도
|
|
44
|
+
동일 옵션 적용. 미설정이면 임베딩을 켠 앱에서 자동 적용된다.
|
|
45
|
+
- 임베딩 미설정 앱에서는 무시되며 기존 BM25 검색으로 동작한다(하위호환, 회귀 없음).
|
|
46
|
+
|
|
6
47
|
## [3.39.2] - 2026-06-26
|
|
7
48
|
|
|
8
49
|
### Performance — 터널 프록시 응답 지연 제거 (TCP_NODELAY / keep-alive)
|
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var cli_exports = {};
|
|
33
33
|
__export(cli_exports, {
|
|
34
34
|
computeDeployDiff: () => computeDeployDiff,
|
|
35
|
+
guessMimeType: () => guessMimeType,
|
|
36
|
+
isAllowedDeployExt: () => isAllowedDeployExt,
|
|
37
|
+
isBinaryDeployExt: () => isBinaryDeployExt,
|
|
35
38
|
makeRequest: () => makeRequest,
|
|
36
39
|
normalizeRelativePath: () => normalizeRelativePath,
|
|
37
40
|
parseArgs: () => parseArgs,
|
|
@@ -191,7 +194,11 @@ var ALLOWED_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
191
194
|
".xml",
|
|
192
195
|
".webmanifest",
|
|
193
196
|
".map",
|
|
194
|
-
".wasm"
|
|
197
|
+
".wasm",
|
|
198
|
+
// 3D 에셋: .glb/.bin 은 바이너리, .gltf 는 JSON(텍스트)
|
|
199
|
+
".glb",
|
|
200
|
+
".gltf",
|
|
201
|
+
".bin"
|
|
195
202
|
]);
|
|
196
203
|
var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
197
204
|
".png",
|
|
@@ -213,8 +220,46 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
213
220
|
".webm",
|
|
214
221
|
".pdf",
|
|
215
222
|
".zip",
|
|
216
|
-
".wasm"
|
|
223
|
+
".wasm",
|
|
224
|
+
".glb",
|
|
225
|
+
".bin"
|
|
217
226
|
]);
|
|
227
|
+
var MIME_BY_EXT = {
|
|
228
|
+
".png": "image/png",
|
|
229
|
+
".jpg": "image/jpeg",
|
|
230
|
+
".jpeg": "image/jpeg",
|
|
231
|
+
".gif": "image/gif",
|
|
232
|
+
".webp": "image/webp",
|
|
233
|
+
".avif": "image/avif",
|
|
234
|
+
".ico": "image/x-icon",
|
|
235
|
+
".svg": "image/svg+xml",
|
|
236
|
+
".woff": "font/woff",
|
|
237
|
+
".woff2": "font/woff2",
|
|
238
|
+
".ttf": "font/ttf",
|
|
239
|
+
".otf": "font/otf",
|
|
240
|
+
".eot": "application/vnd.ms-fontobject",
|
|
241
|
+
".mp3": "audio/mpeg",
|
|
242
|
+
".wav": "audio/wav",
|
|
243
|
+
".ogg": "audio/ogg",
|
|
244
|
+
".mp4": "video/mp4",
|
|
245
|
+
".webm": "video/webm",
|
|
246
|
+
".pdf": "application/pdf",
|
|
247
|
+
".zip": "application/zip",
|
|
248
|
+
".wasm": "application/wasm",
|
|
249
|
+
".glb": "model/gltf-binary",
|
|
250
|
+
".gltf": "model/gltf+json",
|
|
251
|
+
".bin": "application/octet-stream"
|
|
252
|
+
};
|
|
253
|
+
function guessMimeType(filePath) {
|
|
254
|
+
return MIME_BY_EXT[path2.extname(filePath).toLowerCase()] || "application/octet-stream";
|
|
255
|
+
}
|
|
256
|
+
function isAllowedDeployExt(filePath) {
|
|
257
|
+
return ALLOWED_EXTENSIONS.has(path2.extname(filePath).toLowerCase());
|
|
258
|
+
}
|
|
259
|
+
function isBinaryDeployExt(filePath) {
|
|
260
|
+
return BINARY_EXTENSIONS.has(path2.extname(filePath).toLowerCase());
|
|
261
|
+
}
|
|
262
|
+
var WEB_FILE_MAX_STORED_BYTES = 6 * 1024 * 1024 - 64 * 1024;
|
|
218
263
|
function sha256Hex(value) {
|
|
219
264
|
return crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
220
265
|
}
|
|
@@ -281,6 +326,146 @@ function collectFiles(dir, baseDir = dir) {
|
|
|
281
326
|
}
|
|
282
327
|
return files;
|
|
283
328
|
}
|
|
329
|
+
function formatSize(bytes) {
|
|
330
|
+
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
|
|
331
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
332
|
+
}
|
|
333
|
+
function validateFileSizes(files) {
|
|
334
|
+
const oversized = files.map((f) => {
|
|
335
|
+
const storedBytes = Buffer.byteLength(f.content, "utf8");
|
|
336
|
+
const originalBytes = f.isBinary ? Math.round(f.content.length * 3 / 4) : storedBytes;
|
|
337
|
+
return { path: f.path, isBinary: f.isBinary, storedBytes, originalBytes };
|
|
338
|
+
}).filter((f) => f.storedBytes > WEB_FILE_MAX_STORED_BYTES);
|
|
339
|
+
if (oversized.length === 0) return;
|
|
340
|
+
error(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uD30C\uC77C\uB2F9 \uD06C\uAE30 \uD55C\uB3C4(\uC57D ${formatSize(WEB_FILE_MAX_STORED_BYTES)})\uB97C \uCD08\uACFC\uD55C \uD30C\uC77C\uC774 \uC788\uC5B4 \uBC30\uD3EC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4:`);
|
|
341
|
+
for (const f of oversized) {
|
|
342
|
+
const detail = f.isBinary ? `${formatSize(f.originalBytes)} \u2192 base64 ${formatSize(f.storedBytes)}` : formatSize(f.storedBytes);
|
|
343
|
+
error(` \u2022 ${f.path} (${detail})`);
|
|
344
|
+
}
|
|
345
|
+
log("");
|
|
346
|
+
warn("\uC815\uC801 \uD638\uC2A4\uD305 \uD30C\uC77C\uC740 DB \uB2E8\uC77C \uC5D4\uD2B8\uB9AC(6 MiB)\uC5D0 \uC800\uC7A5\uB418\uBA70, \uBC14\uC774\uB108\uB9AC\uB294 base64(\uC57D 1.33x)\uB85C");
|
|
347
|
+
warn("\uBD80\uD480\uC5B4 \uC6D0\uBCF8 \uAE30\uC900 \uC57D 4.5 MB \uAC00 \uC2E4\uC9C8 \uC0C1\uD55C\uC785\uB2C8\uB2E4.");
|
|
348
|
+
warn(`\uB300\uC6A9\uB7C9 \uC5D0\uC14B(3D \uBAA8\uB378\xB7\uB3D9\uC601\uC0C1\xB7\uD070 \uD3F0\uD2B8 \uB4F1)\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(${colors.cyan}cb.storage${colors.reset}, presigned URL)\uC5D0`);
|
|
349
|
+
warn("\uC62C\uB9AC\uACE0 URL \uB85C \uCC38\uC870\uD558\uC138\uC694. \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0\uB294 \uD30C\uC77C\uB2F9 \uACE0\uC815 \uC0C1\uD55C\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.");
|
|
350
|
+
process.exit(1);
|
|
351
|
+
}
|
|
352
|
+
async function requestDeployPresign(baseStorageUrl, headers, files, timeoutMs) {
|
|
353
|
+
const body = JSON.stringify({ files });
|
|
354
|
+
const res = await makeRequest(`${baseStorageUrl}/deploy/presign`, "POST", headers, body, { timeoutMs });
|
|
355
|
+
if (res.status === 404) return null;
|
|
356
|
+
if (res.status < 200 || res.status >= 300) {
|
|
357
|
+
const data = res.data;
|
|
358
|
+
const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
|
|
359
|
+
throw new Error(`presign \uC694\uCCAD \uC2E4\uD328: ${msg}`);
|
|
360
|
+
}
|
|
361
|
+
const parsed = res.data;
|
|
362
|
+
return parsed?.urls ?? [];
|
|
363
|
+
}
|
|
364
|
+
function putToPresignedUrl(uploadUrl, body, contentType, timeoutMs) {
|
|
365
|
+
return new Promise((resolve2, reject) => {
|
|
366
|
+
let parsed;
|
|
367
|
+
try {
|
|
368
|
+
parsed = new URL(uploadUrl);
|
|
369
|
+
} catch {
|
|
370
|
+
reject(new Error("\uC798\uBABB\uB41C \uC5C5\uB85C\uB4DC URL"));
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
374
|
+
reject(new Error(`\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC5C5\uB85C\uB4DC URL \uC2A4\uD0B4: ${parsed.protocol}`));
|
|
375
|
+
return;
|
|
376
|
+
}
|
|
377
|
+
const lib = parsed.protocol === "https:" ? https : http;
|
|
378
|
+
const options = {
|
|
379
|
+
hostname: parsed.hostname,
|
|
380
|
+
port: parsed.port || (parsed.protocol === "https:" ? 443 : 80),
|
|
381
|
+
path: parsed.pathname + parsed.search,
|
|
382
|
+
method: "PUT",
|
|
383
|
+
family: 4,
|
|
384
|
+
autoSelectFamily: false,
|
|
385
|
+
headers: {
|
|
386
|
+
"Content-Type": contentType || "application/octet-stream",
|
|
387
|
+
"Content-Length": body.length
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
const req = lib.request(options, (res) => {
|
|
391
|
+
const chunks = [];
|
|
392
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
393
|
+
res.on("end", () => {
|
|
394
|
+
const status = res.statusCode || 0;
|
|
395
|
+
if (status >= 200 && status < 300) {
|
|
396
|
+
resolve2();
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
reject(
|
|
400
|
+
new Error(
|
|
401
|
+
`Object Storage \uC5C5\uB85C\uB4DC \uC2E4\uD328 (HTTP ${status}): ${Buffer.concat(chunks).toString("utf8").slice(0, 200)}`
|
|
402
|
+
)
|
|
403
|
+
);
|
|
404
|
+
});
|
|
405
|
+
});
|
|
406
|
+
req.on("socket", (socket) => {
|
|
407
|
+
socket.setKeepAlive(true, 3e4);
|
|
408
|
+
});
|
|
409
|
+
req.setTimeout(timeoutMs, () => {
|
|
410
|
+
req.destroy(new Error(`\uC5C5\uB85C\uB4DC \uC2DC\uAC04 \uCD08\uACFC (${Math.round(timeoutMs / 1e3)}\uCD08)`));
|
|
411
|
+
});
|
|
412
|
+
req.on("error", reject);
|
|
413
|
+
req.end(body);
|
|
414
|
+
});
|
|
415
|
+
}
|
|
416
|
+
async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, timeoutMs) {
|
|
417
|
+
const oversized = diff.upsert.filter((f) => Buffer.byteLength(f.content, "utf8") > WEB_FILE_MAX_STORED_BYTES);
|
|
418
|
+
if (oversized.length === 0) return;
|
|
419
|
+
const largeText = oversized.filter((f) => !f.is_binary);
|
|
420
|
+
const largeBinaries = oversized.filter((f) => f.is_binary);
|
|
421
|
+
if (largeText.length > 0) {
|
|
422
|
+
error(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uD30C\uC77C\uB2F9 \uD06C\uAE30 \uD55C\uB3C4(\uC57D ${formatSize(WEB_FILE_MAX_STORED_BYTES)})\uB97C \uCD08\uACFC\uD55C \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC788\uC5B4 \uBC30\uD3EC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4:`);
|
|
423
|
+
for (const f of largeText) error(` \u2022 ${f.path} (${formatSize(Buffer.byteLength(f.content, "utf8"))})`);
|
|
424
|
+
log("");
|
|
425
|
+
warn("\uD070 JS/CSS \uB294 \uCF54\uB4DC \uC2A4\uD50C\uB9AC\uD305\uC73C\uB85C \uBD84\uD560\uD558\uC138\uC694. (\uBC14\uC774\uB108\uB9AC\uC640 \uB2EC\uB9AC \uD14D\uC2A4\uD2B8\uB294 \uC624\uBE0C\uC81D\uD2B8 \uC2A4\uD1A0\uB9AC\uC9C0\uB85C \uC624\uD504\uB85C\uB4DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.)");
|
|
426
|
+
process.exit(1);
|
|
427
|
+
}
|
|
428
|
+
if (largeBinaries.length === 0) return;
|
|
429
|
+
const presignFiles = largeBinaries.map((f) => ({
|
|
430
|
+
path: f.path,
|
|
431
|
+
size: Buffer.from(f.content, "base64").length,
|
|
432
|
+
mime_type: guessMimeType(f.path)
|
|
433
|
+
}));
|
|
434
|
+
let presigned;
|
|
435
|
+
try {
|
|
436
|
+
presigned = await requestDeployPresign(baseStorageUrl, headers, presignFiles, timeoutMs);
|
|
437
|
+
} catch (err) {
|
|
438
|
+
error(`\uB300\uC6A9\uB7C9 \uD30C\uC77C \uC5C5\uB85C\uB4DC \uC900\uBE44 \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
if (presigned === null) {
|
|
442
|
+
error(`\uC11C\uBC84\uAC00 \uB300\uC6A9\uB7C9 \uD30C\uC77C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC544 \uB2E4\uC74C \uD30C\uC77C\uC744 \uBC30\uD3EC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4(\uC57D ${formatSize(WEB_FILE_MAX_STORED_BYTES)} \uCD08\uACFC):`);
|
|
443
|
+
for (const f of largeBinaries) error(` \u2022 ${f.path} (${formatSize(Buffer.from(f.content, "base64").length)})`);
|
|
444
|
+
log("");
|
|
445
|
+
warn(`\uB300\uC6A9\uB7C9 \uC5D0\uC14B\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(${colors.cyan}cb.storage${colors.reset})\uC5D0 \uC62C\uB9AC\uACE0 URL \uB85C \uCC38\uC870\uD558\uAC70\uB098, \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
const urlByPath = new Map(presigned.map((p) => [p.path, p.upload_url]));
|
|
449
|
+
info(`\uB300\uC6A9\uB7C9 \uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4...`);
|
|
450
|
+
for (const f of largeBinaries) {
|
|
451
|
+
const uploadUrl = urlByPath.get(f.path);
|
|
452
|
+
if (!uploadUrl) {
|
|
453
|
+
error(`presign \uC751\uB2F5\uC5D0 \uC5C5\uB85C\uB4DC URL \uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
const bytes = Buffer.from(f.content, "base64");
|
|
457
|
+
try {
|
|
458
|
+
await putToPresignedUrl(uploadUrl, bytes, guessMimeType(f.path), timeoutMs);
|
|
459
|
+
} catch (err) {
|
|
460
|
+
error(`\uC5C5\uB85C\uB4DC \uC2E4\uD328 (${f.path}): ${err instanceof Error ? err.message : err}`);
|
|
461
|
+
process.exit(1);
|
|
462
|
+
}
|
|
463
|
+
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
464
|
+
f.pre_uploaded = true;
|
|
465
|
+
f.size = bytes.length;
|
|
466
|
+
f.content = "";
|
|
467
|
+
}
|
|
468
|
+
}
|
|
284
469
|
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
285
470
|
async function makeRequest(url, method, headers, body, reqOpts = {}) {
|
|
286
471
|
return new Promise((resolve2, reject) => {
|
|
@@ -387,12 +572,14 @@ async function deploy(directory, config, isDev = false, deployOpts = {}) {
|
|
|
387
572
|
const timeoutMs = computeDeployTimeout(totalSize, deployOpts.timeoutMs);
|
|
388
573
|
log(`${colors.dim}\uD0C0\uC784\uC544\uC6C3: ${Math.round(timeoutMs / 1e3)}\uCD08${colors.reset}`);
|
|
389
574
|
if (isDev) {
|
|
575
|
+
validateFileSizes(files);
|
|
390
576
|
await fullDeploy(baseStorageUrl, headers, files, envLabel, "deploy/dev", timeoutMs);
|
|
391
577
|
return;
|
|
392
578
|
}
|
|
393
579
|
try {
|
|
394
580
|
const manifest = await tryFetchManifest(baseStorageUrl, headers);
|
|
395
581
|
if (!manifest) {
|
|
582
|
+
validateFileSizes(files);
|
|
396
583
|
await fullDeploy(baseStorageUrl, headers, files, envLabel, "deploy", timeoutMs);
|
|
397
584
|
return;
|
|
398
585
|
}
|
|
@@ -404,6 +591,7 @@ ${colors.cyan}URL: https://${config.storageId}.web.connectbase.world${colors.res
|
|
|
404
591
|
`);
|
|
405
592
|
return;
|
|
406
593
|
}
|
|
594
|
+
await offloadLargeBinaries(baseStorageUrl, headers, diff, envLabel, timeoutMs);
|
|
407
595
|
info(`\uBCC0\uACBD: ${colors.green}+${diff.upsert.length}${colors.reset} / ${colors.red}-${diff.delete.length}${colors.reset} (\uC804\uCCB4 ${files.length}\uAC1C \uC911)`);
|
|
408
596
|
const uploadSize = diff.upsert.reduce((s, f) => s + f.content.length, 0);
|
|
409
597
|
info(`\uC5C5\uB85C\uB4DC \uD06C\uAE30: ${(uploadSize / 1024).toFixed(1)} KB`);
|
|
@@ -2628,6 +2816,9 @@ main().catch((err) => {
|
|
|
2628
2816
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2629
2817
|
0 && (module.exports = {
|
|
2630
2818
|
computeDeployDiff,
|
|
2819
|
+
guessMimeType,
|
|
2820
|
+
isAllowedDeployExt,
|
|
2821
|
+
isBinaryDeployExt,
|
|
2631
2822
|
makeRequest,
|
|
2632
2823
|
normalizeRelativePath,
|
|
2633
2824
|
parseArgs,
|
package/dist/index.d.mts
CHANGED
|
@@ -2402,14 +2402,15 @@ declare class StorageAPI {
|
|
|
2402
2402
|
* 파일 목록 조회
|
|
2403
2403
|
*
|
|
2404
2404
|
* @param storageId - 파일 스토리지 ID
|
|
2405
|
-
* @param parentId - 부모 폴더 ID
|
|
2405
|
+
* @param parentId - 부모 폴더 ID. 지정 시 해당 폴더의 **직계 자식만** 반환한다.
|
|
2406
|
+
* 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
|
|
2406
2407
|
*
|
|
2407
2408
|
* @example
|
|
2408
2409
|
* ```ts
|
|
2409
|
-
* //
|
|
2410
|
+
* // 스토리지 전체 파일 목록 (flat 트리)
|
|
2410
2411
|
* const files = await cb.storage.getFiles('storage-id')
|
|
2411
2412
|
*
|
|
2412
|
-
* // 특정 폴더의
|
|
2413
|
+
* // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
|
|
2413
2414
|
* const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
|
|
2414
2415
|
* ```
|
|
2415
2416
|
*/
|
|
@@ -7370,6 +7371,12 @@ interface KnowledgeSearchRequest {
|
|
|
7370
7371
|
top_k?: number;
|
|
7371
7372
|
/** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
|
|
7372
7373
|
agentic?: boolean;
|
|
7374
|
+
/**
|
|
7375
|
+
* Hybrid 검색 — BM25 키워드 + 벡터 시맨틱 검색을 RRF 로 융합.
|
|
7376
|
+
* 미설정(기본): 콘솔 > AI 에서 embedding 을 켠 앱은 자동 적용.
|
|
7377
|
+
* `false`: 키워드 전용 검색 강제. embedding 미설정 앱에서는 무시(BM25 only).
|
|
7378
|
+
*/
|
|
7379
|
+
hybrid?: boolean;
|
|
7373
7380
|
/**
|
|
7374
7381
|
* metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
|
|
7375
7382
|
* 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
|
|
@@ -7655,6 +7662,11 @@ interface AIChatRequest {
|
|
|
7655
7662
|
knowledgeBaseId?: string;
|
|
7656
7663
|
topK?: number;
|
|
7657
7664
|
agentic?: boolean;
|
|
7665
|
+
/**
|
|
7666
|
+
* KB 검색 hybrid(BM25 + 벡터 시맨틱, RRF 융합) 제어. 미설정(기본)이면 콘솔 > AI 에서
|
|
7667
|
+
* embedding 을 켠 앱은 자동 적용, `false` 면 키워드 전용 검색 강제.
|
|
7668
|
+
*/
|
|
7669
|
+
hybrid?: boolean;
|
|
7658
7670
|
toolGroupId?: string;
|
|
7659
7671
|
/**
|
|
7660
7672
|
* 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
|
package/dist/index.d.ts
CHANGED
|
@@ -2402,14 +2402,15 @@ declare class StorageAPI {
|
|
|
2402
2402
|
* 파일 목록 조회
|
|
2403
2403
|
*
|
|
2404
2404
|
* @param storageId - 파일 스토리지 ID
|
|
2405
|
-
* @param parentId - 부모 폴더 ID
|
|
2405
|
+
* @param parentId - 부모 폴더 ID. 지정 시 해당 폴더의 **직계 자식만** 반환한다.
|
|
2406
|
+
* 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
|
|
2406
2407
|
*
|
|
2407
2408
|
* @example
|
|
2408
2409
|
* ```ts
|
|
2409
|
-
* //
|
|
2410
|
+
* // 스토리지 전체 파일 목록 (flat 트리)
|
|
2410
2411
|
* const files = await cb.storage.getFiles('storage-id')
|
|
2411
2412
|
*
|
|
2412
|
-
* // 특정 폴더의
|
|
2413
|
+
* // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
|
|
2413
2414
|
* const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
|
|
2414
2415
|
* ```
|
|
2415
2416
|
*/
|
|
@@ -7370,6 +7371,12 @@ interface KnowledgeSearchRequest {
|
|
|
7370
7371
|
top_k?: number;
|
|
7371
7372
|
/** Agentic Search 활성화 — AI 가 쿼리를 자동 생성하여 다중 검색 수행 */
|
|
7372
7373
|
agentic?: boolean;
|
|
7374
|
+
/**
|
|
7375
|
+
* Hybrid 검색 — BM25 키워드 + 벡터 시맨틱 검색을 RRF 로 융합.
|
|
7376
|
+
* 미설정(기본): 콘솔 > AI 에서 embedding 을 켠 앱은 자동 적용.
|
|
7377
|
+
* `false`: 키워드 전용 검색 강제. embedding 미설정 앱에서는 무시(BM25 only).
|
|
7378
|
+
*/
|
|
7379
|
+
hybrid?: boolean;
|
|
7373
7380
|
/**
|
|
7374
7381
|
* metadata 기반 필터 (옵셔널). 키 형식 `metadata.<path>` 또는 raw key.
|
|
7375
7382
|
* 값에 `AUTH_MEMBER_ID_TOKEN` 사용 시 서버가 인증된 AppMember ID 로 치환.
|
|
@@ -7655,6 +7662,11 @@ interface AIChatRequest {
|
|
|
7655
7662
|
knowledgeBaseId?: string;
|
|
7656
7663
|
topK?: number;
|
|
7657
7664
|
agentic?: boolean;
|
|
7665
|
+
/**
|
|
7666
|
+
* KB 검색 hybrid(BM25 + 벡터 시맨틱, RRF 융합) 제어. 미설정(기본)이면 콘솔 > AI 에서
|
|
7667
|
+
* embedding 을 켠 앱은 자동 적용, `false` 면 키워드 전용 검색 강제.
|
|
7668
|
+
*/
|
|
7669
|
+
hybrid?: boolean;
|
|
7658
7670
|
toolGroupId?: string;
|
|
7659
7671
|
/**
|
|
7660
7672
|
* 0 보다 크면, 서버측 도구 그룹 실행(`toolGroupId` 사용 시)에서 각 도구 결과를 모델에
|
package/dist/index.js
CHANGED
|
@@ -2279,14 +2279,15 @@ var StorageAPI = class {
|
|
|
2279
2279
|
* 파일 목록 조회
|
|
2280
2280
|
*
|
|
2281
2281
|
* @param storageId - 파일 스토리지 ID
|
|
2282
|
-
* @param parentId - 부모 폴더 ID
|
|
2282
|
+
* @param parentId - 부모 폴더 ID. 지정 시 해당 폴더의 **직계 자식만** 반환한다.
|
|
2283
|
+
* 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
|
|
2283
2284
|
*
|
|
2284
2285
|
* @example
|
|
2285
2286
|
* ```ts
|
|
2286
|
-
* //
|
|
2287
|
+
* // 스토리지 전체 파일 목록 (flat 트리)
|
|
2287
2288
|
* const files = await cb.storage.getFiles('storage-id')
|
|
2288
2289
|
*
|
|
2289
|
-
* // 특정 폴더의
|
|
2290
|
+
* // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
|
|
2290
2291
|
* const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
|
|
2291
2292
|
* ```
|
|
2292
2293
|
*/
|
package/dist/index.mjs
CHANGED
|
@@ -2234,14 +2234,15 @@ var StorageAPI = class {
|
|
|
2234
2234
|
* 파일 목록 조회
|
|
2235
2235
|
*
|
|
2236
2236
|
* @param storageId - 파일 스토리지 ID
|
|
2237
|
-
* @param parentId - 부모 폴더 ID
|
|
2237
|
+
* @param parentId - 부모 폴더 ID. 지정 시 해당 폴더의 **직계 자식만** 반환한다.
|
|
2238
|
+
* 미지정 시 스토리지 **전체 파일 트리**를 flat 배열로 반환한다.
|
|
2238
2239
|
*
|
|
2239
2240
|
* @example
|
|
2240
2241
|
* ```ts
|
|
2241
|
-
* //
|
|
2242
|
+
* // 스토리지 전체 파일 목록 (flat 트리)
|
|
2242
2243
|
* const files = await cb.storage.getFiles('storage-id')
|
|
2243
2244
|
*
|
|
2244
|
-
* // 특정 폴더의
|
|
2245
|
+
* // 특정 폴더의 직계 자식만 (대용량 버킷에서 전체 트리 전송 회피)
|
|
2245
2246
|
* const subFiles = await cb.storage.getFiles('storage-id', 'folder-id')
|
|
2246
2247
|
* ```
|
|
2247
2248
|
*/
|