connectbase-client 3.43.0 → 3.45.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 +32 -0
- package/dist/cli.js +488 -185
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
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.44.0] - 2026-07-03
|
|
7
|
+
|
|
8
|
+
### Added — CLI 비대화형 init + SDK 문서 수명주기 정비
|
|
9
|
+
|
|
10
|
+
AI 에이전트(Claude Code 등)가 `init` 을 직접 실행할 수 있도록 비대화형 모드를 추가하고,
|
|
11
|
+
`.claude/docs/` 문서의 신선도/정리 체계를 도입했다.
|
|
12
|
+
|
|
13
|
+
- **비대화형 init**: `connectbase init --yes --secret-key cb_sk_...` — 프롬프트 없이 진행.
|
|
14
|
+
비TTY 환경에서 `--yes` 없이 실행하면 hang 대신 필요한 플래그를 안내하고 즉시 종료.
|
|
15
|
+
`--app <앱ID>`/`--storage <스토리지ID>`/`--deploy-dir <dir>` 로 명시 지정, `--yes` 는
|
|
16
|
+
프로젝트 이름과 같은 앱/스토리지가 있으면 재사용(반복 실행에 멱등). Secret Key 는
|
|
17
|
+
`CONNECTBASE_SECRET_KEY` 환경변수로도 전달 가능.
|
|
18
|
+
- **Public Key 스프롤 방지**: `.connectbaserc` 에 `appId` 를 저장하고, 같은 앱을 다시 init
|
|
19
|
+
하면 기존 Public Key 를 재사용 (이전에는 실행마다 새 키 발급).
|
|
20
|
+
- **문서 생성 스탬프 + 매니페스트**: `.claude/docs/` 파일에 생성 CLI 버전/날짜 스탬프를 넣고
|
|
21
|
+
`.connectbase-manifest.json` 으로 추적 — 콘텐츠 해시로 신선도 비교, 서버에서 사라진 문서
|
|
22
|
+
자동 정리(레거시 fullstack 파일 포함), `deploy` 성공 후 변경이 있을 때만 조용히 자동 갱신
|
|
23
|
+
(`--skip-docs` 로 생략 가능).
|
|
24
|
+
- **CLAUDE.md @import**: 참조 블록이 `@.claude/docs/project-rules.md` import 로 규칙을 매
|
|
25
|
+
세션 자동 로드. MCP 미연결 시 `npx connectbase mcp` 안내 지시 포함. `mcp` 명령도 비대화형
|
|
26
|
+
지원(`--secret-key`/env/.connectbaserc 자동 사용).
|
|
27
|
+
|
|
28
|
+
### Changed / Security
|
|
29
|
+
|
|
30
|
+
- **키를 쿼리스트링에 싣지 않음**: 문서 다운로드가 `public_key` 쿼리 파라미터를 더 이상
|
|
31
|
+
전송하지 않는다 (서버 측 키 치환은 플레이스홀더 불일치로 오래전부터 무동작 — 액세스 로그에
|
|
32
|
+
키가 남는 위험만 있었음). `update` 의 secretKey fallback 도 제거.
|
|
33
|
+
- 모든 CLI 요청에 `User-Agent: connectbase-cli/<version>` 명시.
|
|
34
|
+
- `detectBuildDir`: Next.js 는 `output: 'export'` 미설정 시 경고 안내.
|
|
35
|
+
- 버전 비교가 prerelease(`-beta.1`) 형식에서 NaN 이 되던 문제 수정.
|
|
36
|
+
- `.gitignore` 항목 추가가 부분 문자열 오탐으로 건너뛰던 문제 수정 (라인 단위 정확 매칭).
|
|
37
|
+
|
|
6
38
|
## [3.43.0] - 2026-07-02
|
|
7
39
|
|
|
8
40
|
### Added — `--dev` 배포도 대용량 바이너리 지원
|
package/dist/cli.js
CHANGED
|
@@ -429,7 +429,10 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
429
429
|
const presignFiles = largeBinaries.map((f) => ({
|
|
430
430
|
path: f.path,
|
|
431
431
|
size: Buffer.from(f.content, "base64").length,
|
|
432
|
-
mime_type: guessMimeType(f.path)
|
|
432
|
+
mime_type: guessMimeType(f.path),
|
|
433
|
+
// 신형 서버: hash 제공 시 content-addressed blob 키({id}/blobs/{hash})로 presign 되고,
|
|
434
|
+
// 동일 blob 이 이미 있으면 already_exists 로 업로드를 생략할 수 있다. 구버전 서버는 무시.
|
|
435
|
+
hash: f.hash
|
|
433
436
|
}));
|
|
434
437
|
let presigned;
|
|
435
438
|
try {
|
|
@@ -445,22 +448,30 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
445
448
|
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
449
|
process.exit(1);
|
|
447
450
|
}
|
|
448
|
-
const
|
|
451
|
+
const entryByPath = new Map(presigned.map((p) => [p.path, p]));
|
|
449
452
|
info(`\uB300\uC6A9\uB7C9 \uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4...`);
|
|
450
453
|
for (const f of largeBinaries) {
|
|
451
|
-
const
|
|
452
|
-
if (!
|
|
453
|
-
error(`presign \uC751\uB2F5\uC5D0 \
|
|
454
|
+
const entry = entryByPath.get(f.path);
|
|
455
|
+
if (!entry) {
|
|
456
|
+
error(`presign \uC751\uB2F5\uC5D0 \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
454
457
|
process.exit(1);
|
|
455
458
|
}
|
|
456
459
|
const bytes = Buffer.from(f.content, "base64");
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
|
|
460
|
+
if (entry.already_exists) {
|
|
461
|
+
success(`\uC5C5\uB85C\uB4DC \uC0DD\uB7B5(\uAE30\uC874 blob \uC7AC\uC0AC\uC6A9): ${f.path} (${formatSize(bytes.length)})`);
|
|
462
|
+
} else {
|
|
463
|
+
if (!entry.upload_url) {
|
|
464
|
+
error(`presign \uC751\uB2F5\uC5D0 \uC5C5\uB85C\uB4DC URL \uC774 \uC5C6\uC2B5\uB2C8\uB2E4: ${f.path}`);
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
try {
|
|
468
|
+
await putToPresignedUrl(entry.upload_url, bytes, guessMimeType(f.path), timeoutMs);
|
|
469
|
+
} catch (err) {
|
|
470
|
+
error(`\uC5C5\uB85C\uB4DC \uC2E4\uD328 (${f.path}): ${err instanceof Error ? err.message : err}`);
|
|
471
|
+
process.exit(1);
|
|
472
|
+
}
|
|
473
|
+
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
462
474
|
}
|
|
463
|
-
success(`\uC9C1\uC811 \uC5C5\uB85C\uB4DC: ${f.path} (${formatSize(bytes.length)})`);
|
|
464
475
|
f.pre_uploaded = true;
|
|
465
476
|
f.size = bytes.length;
|
|
466
477
|
f.content = "";
|
|
@@ -525,6 +536,8 @@ async function makeRequest(url, method, headers, body, reqOpts = {}) {
|
|
|
525
536
|
family: 4,
|
|
526
537
|
autoSelectFamily: false,
|
|
527
538
|
headers: {
|
|
539
|
+
// 명시적 UA: 관측성 + WAF 봇 휴리스틱(비브라우저 UA 차단 규칙)과의 충돌 방지
|
|
540
|
+
"User-Agent": `connectbase-cli/${VERSION}`,
|
|
528
541
|
...headers,
|
|
529
542
|
"Content-Type": "application/json",
|
|
530
543
|
...bodyBuffer ? { "Content-Length": bodyBuffer.length } : {}
|
|
@@ -741,8 +754,25 @@ async function fullDeploy(baseStorageUrl, headers, files, envLabel, endpoint, ti
|
|
|
741
754
|
}
|
|
742
755
|
function handleDeployResponse(response, envLabel) {
|
|
743
756
|
if (response.status >= 200 && response.status < 300) {
|
|
744
|
-
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
745
757
|
const data = response.data;
|
|
758
|
+
const dep = data?.deployment ?? null;
|
|
759
|
+
if (dep?.deployment_id) {
|
|
760
|
+
if (dep.promoted) {
|
|
761
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC! (release #${dep.seq})`);
|
|
762
|
+
if (dep.production_url) log(`
|
|
763
|
+
${colors.cyan}Production: ${dep.production_url}${colors.reset}`);
|
|
764
|
+
if (dep.qa_url) log(`${colors.dim}QA: ${dep.qa_url}${colors.reset}
|
|
765
|
+
`);
|
|
766
|
+
} else {
|
|
767
|
+
success(`QA \uD658\uACBD\uC5D0 \uBC30\uD3EC\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (release #${dep.seq}) \u2014 production \uC740 \uC544\uC9C1 \uC774\uC804 \uBC84\uC804\uC785\uB2C8\uB2E4`);
|
|
768
|
+
if (dep.qa_url) log(`
|
|
769
|
+
${colors.yellow}QA URL: ${dep.qa_url}${colors.reset}`);
|
|
770
|
+
log(`${colors.dim}\uAC80\uC99D \uD6C4 \uBC18\uC601: ${colors.cyan}npx connectbase promote${colors.reset}
|
|
771
|
+
`);
|
|
772
|
+
}
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
746
776
|
if (data?.dev_url) {
|
|
747
777
|
log(`
|
|
748
778
|
${colors.yellow}Dev URL: ${data.dev_url}${colors.reset}
|
|
@@ -759,6 +789,64 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
|
|
|
759
789
|
process.exit(1);
|
|
760
790
|
}
|
|
761
791
|
}
|
|
792
|
+
async function promote(config, deploymentId) {
|
|
793
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
794
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
795
|
+
info(deploymentId ? `release ${deploymentId} \uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...` : "\uCD5C\uC2E0 \uBC30\uD3EC\uB97C production \uC73C\uB85C \uC2B9\uACA9\uD569\uB2C8\uB2E4...");
|
|
796
|
+
const body = JSON.stringify(deploymentId ? { deployment_id: deploymentId } : {});
|
|
797
|
+
const res = await makeRequest(`${baseStorageUrl}/promote`, "POST", headers, body);
|
|
798
|
+
if (res.status === 404 && !res.data?.error) {
|
|
799
|
+
error("\uC11C\uBC84\uAC00 promote \uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uAD6C\uBC84\uC804 \uC11C\uBC84) \u2014 \uC11C\uBC84 \uC5C5\uB370\uC774\uD2B8 \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
800
|
+
process.exit(1);
|
|
801
|
+
}
|
|
802
|
+
if (res.status < 200 || res.status >= 300) {
|
|
803
|
+
const data2 = res.data;
|
|
804
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
805
|
+
error(`promote \uC2E4\uD328: ${msg}`);
|
|
806
|
+
process.exit(1);
|
|
807
|
+
}
|
|
808
|
+
const data = res.data;
|
|
809
|
+
success(`production \uC2B9\uACA9 \uC644\uB8CC! (release #${data.seq})`);
|
|
810
|
+
if (data.production_url) {
|
|
811
|
+
log(`
|
|
812
|
+
${colors.cyan}Production: ${data.production_url}${colors.reset}
|
|
813
|
+
`);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
async function listReleases(config) {
|
|
817
|
+
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
818
|
+
const headers = { "X-Public-Key": config.publicKey };
|
|
819
|
+
const res = await makeRequest(`${baseStorageUrl}/deployments`, "GET", headers);
|
|
820
|
+
if (res.status < 200 || res.status >= 300) {
|
|
821
|
+
const data2 = res.data;
|
|
822
|
+
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
823
|
+
error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
824
|
+
process.exit(1);
|
|
825
|
+
}
|
|
826
|
+
const data = res.data;
|
|
827
|
+
if (!data.deployments || data.deployments.length === 0) {
|
|
828
|
+
info("\uBC30\uD3EC \uC774\uB825\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 deploy \uD558\uC138\uC694.");
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
log(`
|
|
832
|
+
${colors.cyan}\uBC30\uD3EC \uC774\uB825${colors.reset} (promote_mode: ${data.promote_mode || "auto"}, \uCD1D ${data.total}\uAC1C)
|
|
833
|
+
`);
|
|
834
|
+
for (const d of data.deployments) {
|
|
835
|
+
const markers = [];
|
|
836
|
+
if (d.is_production) markers.push(`${colors.green}production${colors.reset}`);
|
|
837
|
+
if (d.is_qa) markers.push(`${colors.yellow}qa${colors.reset}`);
|
|
838
|
+
const marker = markers.length > 0 ? ` \u2190 ${markers.join(", ")}` : "";
|
|
839
|
+
const statusColor = d.status === "ready" ? colors.green : d.status === "failed" ? colors.red : colors.yellow;
|
|
840
|
+
log(
|
|
841
|
+
` #${String(d.seq).padEnd(4)} ${statusColor}${d.status.padEnd(9)}${colors.reset} ${String(d.file_count).padStart(4)}\uAC1C ${formatSize(d.total_size).padStart(9)} ${d.created_at} ${colors.dim}${d.id}${colors.reset}${marker}`
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
log("");
|
|
845
|
+
if (data.production_url) log(`${colors.cyan}Production: ${data.production_url}${colors.reset}`);
|
|
846
|
+
if (data.qa_url) log(`${colors.yellow}QA: ${data.qa_url}${colors.reset}`);
|
|
847
|
+
log(`${colors.dim}\uB864\uBC31: npx connectbase promote <deployment_id>${colors.reset}
|
|
848
|
+
`);
|
|
849
|
+
}
|
|
762
850
|
function prompt(question) {
|
|
763
851
|
const rl = readline.createInterface({
|
|
764
852
|
input: process.stdin,
|
|
@@ -905,7 +993,7 @@ ${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC13
|
|
|
905
993
|
error("\uC778\uC99D \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
906
994
|
process.exit(1);
|
|
907
995
|
}
|
|
908
|
-
async function init() {
|
|
996
|
+
async function init(config = {}, opts = {}) {
|
|
909
997
|
log(`
|
|
910
998
|
${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.reset}
|
|
911
999
|
`);
|
|
@@ -916,49 +1004,104 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
916
1004
|
info(`MCP/\uBB38\uC11C\uB294 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0, \uBC30\uD3EC \uC124\uC815\uC740 \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4
|
|
917
1005
|
`);
|
|
918
1006
|
}
|
|
1007
|
+
const nonInteractive = opts.yes === true;
|
|
1008
|
+
if (!process.stdin.isTTY && !nonInteractive) {
|
|
1009
|
+
error("\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC785\uB2C8\uB2E4. --yes \uD50C\uB798\uADF8\uC640 \uD568\uAED8 \uD544\uC694\uD55C \uAC12\uC744 \uC9C0\uC815\uD558\uC138\uC694");
|
|
1010
|
+
log(`${colors.dim}\uC608: npx connectbase init --yes --secret-key cb_sk_... [--app <\uC571ID>] [--storage <\uC2A4\uD1A0\uB9AC\uC9C0ID>] [--deploy-dir ./dist]${colors.reset}`);
|
|
1011
|
+
log(`${colors.dim}Secret Key\uB294 CONNECTBASE_SECRET_KEY \uD658\uACBD\uBCC0\uC218\uB85C\uB3C4 \uC804\uB2EC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4${colors.reset}`);
|
|
1012
|
+
process.exit(1);
|
|
1013
|
+
}
|
|
919
1014
|
const rcPath = path2.join(cwd, ".connectbaserc");
|
|
1015
|
+
let existingRc = {};
|
|
920
1016
|
if (fs2.existsSync(rcPath)) {
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
1017
|
+
try {
|
|
1018
|
+
existingRc = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1019
|
+
} catch {
|
|
1020
|
+
}
|
|
1021
|
+
if (nonInteractive) {
|
|
1022
|
+
if (!opts.force) {
|
|
1023
|
+
error(".connectbaserc\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uB824\uBA74 --force\uB97C \uD568\uAED8 \uC0AC\uC6A9\uD558\uC138\uC694");
|
|
1024
|
+
process.exit(1);
|
|
1025
|
+
}
|
|
1026
|
+
} else {
|
|
1027
|
+
const overwrite = await prompt(`${colors.yellow}\u26A0${colors.reset} .connectbaserc\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? (y/N): `);
|
|
1028
|
+
if (overwrite.toLowerCase() !== "y") {
|
|
1029
|
+
info("\uCD08\uAE30\uD654\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4");
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
925
1032
|
}
|
|
926
1033
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
`);
|
|
932
|
-
const authChoice = await prompt(`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `);
|
|
933
|
-
let secretKey = "";
|
|
934
|
-
if (authChoice === "2") {
|
|
935
|
-
try {
|
|
936
|
-
secretKey = await browserAuthFlow();
|
|
937
|
-
} catch (err) {
|
|
938
|
-
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
939
|
-
info("\uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uAC70\uB098, Secret Key \uC9C1\uC811 \uC785\uB825(\uC635\uC158 1)\uC744 \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
1034
|
+
let secretKey = config.secretKey || "";
|
|
1035
|
+
if (secretKey) {
|
|
1036
|
+
if (!secretKey.startsWith("cb_sk_")) {
|
|
1037
|
+
error("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4 \uD615\uC2DD\uC785\uB2C8\uB2E4. cb_sk_\uB85C \uC2DC\uC791\uD558\uB294 Secret Key\uB97C \uC9C0\uC815\uD558\uC138\uC694");
|
|
940
1038
|
process.exit(1);
|
|
941
1039
|
}
|
|
1040
|
+
info("Secret Key \uD655\uC778\uB428 (\uD50C\uB798\uADF8/\uD658\uACBD\uBCC0\uC218/.connectbaserc)");
|
|
1041
|
+
} else if (nonInteractive) {
|
|
1042
|
+
error("Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: --secret-key cb_sk_... \uB610\uB294 CONNECTBASE_SECRET_KEY \uD658\uACBD\uBCC0\uC218");
|
|
1043
|
+
process.exit(1);
|
|
942
1044
|
} else {
|
|
943
|
-
log(
|
|
1045
|
+
log(`
|
|
1046
|
+
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
1047
|
+
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
1048
|
+
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
944
1049
|
`);
|
|
945
|
-
|
|
946
|
-
if (
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
}
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1050
|
+
const authChoice = await prompt(`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `);
|
|
1051
|
+
if (authChoice === "2") {
|
|
1052
|
+
try {
|
|
1053
|
+
secretKey = await browserAuthFlow();
|
|
1054
|
+
} catch (err) {
|
|
1055
|
+
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1056
|
+
info("\uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uAC70\uB098, Secret Key \uC9C1\uC811 \uC785\uB825(\uC635\uC158 1)\uC744 \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
1057
|
+
process.exit(1);
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
1061
|
+
`);
|
|
1062
|
+
secretKey = await promptSecret(`${colors.blue}?${colors.reset} Secret Key: `);
|
|
1063
|
+
if (!secretKey) {
|
|
1064
|
+
error("Secret Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1065
|
+
process.exit(1);
|
|
1066
|
+
}
|
|
1067
|
+
if (secretKey.startsWith("cb_pk_")) {
|
|
1068
|
+
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
1069
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1070
|
+
process.exit(1);
|
|
1071
|
+
}
|
|
1072
|
+
if (!secretKey.startsWith("cb_sk_")) {
|
|
1073
|
+
error("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4 \uD615\uC2DD\uC785\uB2C8\uB2E4. cb_sk_\uB85C \uC2DC\uC791\uD558\uB294 Secret Key\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
1074
|
+
process.exit(1);
|
|
1075
|
+
}
|
|
958
1076
|
}
|
|
959
1077
|
}
|
|
960
1078
|
let appId = "";
|
|
961
1079
|
let publicKey = "";
|
|
1080
|
+
const projectName = path2.basename(cwd);
|
|
1081
|
+
const reuseKeyIfSameApp = (selectedAppId) => {
|
|
1082
|
+
if (existingRc.appId === selectedAppId && existingRc.publicKey?.startsWith("cb_pk_")) {
|
|
1083
|
+
publicKey = existingRc.publicKey;
|
|
1084
|
+
success("\uAE30\uC874 Public Key \uC7AC\uC0AC\uC6A9 (.connectbaserc)");
|
|
1085
|
+
return true;
|
|
1086
|
+
}
|
|
1087
|
+
return false;
|
|
1088
|
+
};
|
|
1089
|
+
const issuePublicKey = async (targetAppId) => {
|
|
1090
|
+
info("Public Key \uC0DD\uC131 \uC911...");
|
|
1091
|
+
const newKeyRes = await makeRequest(
|
|
1092
|
+
`${DEFAULT_BASE_URL}/v1/public/cli/apps/${targetAppId}/public-keys`,
|
|
1093
|
+
"POST",
|
|
1094
|
+
{ "X-Public-Key": secretKey },
|
|
1095
|
+
JSON.stringify({})
|
|
1096
|
+
);
|
|
1097
|
+
if (newKeyRes.status === 201) {
|
|
1098
|
+
const keyData = newKeyRes.data;
|
|
1099
|
+
publicKey = keyData.key;
|
|
1100
|
+
success("Public Key \uBC1C\uAE09 \uC644\uB8CC");
|
|
1101
|
+
} else {
|
|
1102
|
+
warn("Public Key \uC790\uB3D9 \uC0DD\uC131 \uC2E4\uD328. \uC218\uB3D9\uC73C\uB85C \uC0DD\uC131\uD558\uC138\uC694");
|
|
1103
|
+
}
|
|
1104
|
+
};
|
|
962
1105
|
try {
|
|
963
1106
|
info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
964
1107
|
const appsRes = await makeRequest(
|
|
@@ -975,7 +1118,23 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
975
1118
|
}
|
|
976
1119
|
const appsData = appsRes.data;
|
|
977
1120
|
const apps = appsData.apps || [];
|
|
978
|
-
if (
|
|
1121
|
+
if (opts.appId) {
|
|
1122
|
+
const found = apps.find((a) => a.id === opts.appId);
|
|
1123
|
+
if (!found) {
|
|
1124
|
+
error(`\uC9C0\uC815\uD55C \uC571\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${opts.appId}`);
|
|
1125
|
+
process.exit(1);
|
|
1126
|
+
}
|
|
1127
|
+
appId = found.id;
|
|
1128
|
+
success(`\uC120\uD0DD\uB428: ${found.name}`);
|
|
1129
|
+
if (!reuseKeyIfSameApp(appId)) await issuePublicKey(appId);
|
|
1130
|
+
} else if (nonInteractive) {
|
|
1131
|
+
const found = apps.find((a) => a.name === projectName);
|
|
1132
|
+
if (found) {
|
|
1133
|
+
appId = found.id;
|
|
1134
|
+
success(`\uAE30\uC874 \uC571 \uC7AC\uC0AC\uC6A9: ${found.name}`);
|
|
1135
|
+
if (!reuseKeyIfSameApp(appId)) await issuePublicKey(appId);
|
|
1136
|
+
}
|
|
1137
|
+
} else if (apps.length > 0) {
|
|
979
1138
|
log(`
|
|
980
1139
|
${colors.dim}\uB0B4 \uC571 \uBAA9\uB85D:${colors.reset}`);
|
|
981
1140
|
apps.forEach((a, i) => {
|
|
@@ -989,25 +1148,11 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
989
1148
|
if (num > 0 && num <= apps.length) {
|
|
990
1149
|
appId = apps[num - 1].id;
|
|
991
1150
|
success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
|
|
992
|
-
|
|
993
|
-
const newKeyRes = await makeRequest(
|
|
994
|
-
`${DEFAULT_BASE_URL}/v1/public/cli/apps/${appId}/public-keys`,
|
|
995
|
-
"POST",
|
|
996
|
-
{ "X-Public-Key": secretKey },
|
|
997
|
-
JSON.stringify({})
|
|
998
|
-
);
|
|
999
|
-
if (newKeyRes.status === 201) {
|
|
1000
|
-
const keyData = newKeyRes.data;
|
|
1001
|
-
publicKey = keyData.key;
|
|
1002
|
-
success("Public Key \uBC1C\uAE09 \uC644\uB8CC");
|
|
1003
|
-
} else {
|
|
1004
|
-
warn("Public Key \uC790\uB3D9 \uC0DD\uC131 \uC2E4\uD328. \uC218\uB3D9\uC73C\uB85C \uC0DD\uC131\uD558\uC138\uC694");
|
|
1005
|
-
}
|
|
1151
|
+
if (!reuseKeyIfSameApp(appId)) await issuePublicKey(appId);
|
|
1006
1152
|
}
|
|
1007
1153
|
}
|
|
1008
1154
|
if (!appId) {
|
|
1009
|
-
const
|
|
1010
|
-
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1155
|
+
const appName = nonInteractive ? projectName : await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1011
1156
|
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
1012
1157
|
const createRes = await makeRequest(
|
|
1013
1158
|
`${DEFAULT_BASE_URL}/v1/public/cli/apps`,
|
|
@@ -1034,6 +1179,10 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
1034
1179
|
}
|
|
1035
1180
|
} catch (err) {
|
|
1036
1181
|
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1182
|
+
if (nonInteractive) {
|
|
1183
|
+
info("\uB124\uD2B8\uC6CC\uD06C \uBCF5\uAD6C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uAC70\uB098, \uCF58\uC194\uC5D0\uC11C \uBC1C\uAE09\uD55C \uAC12\uC73C\uB85C .connectbaserc\uB97C \uC9C1\uC811 \uC791\uC131\uD558\uC138\uC694");
|
|
1184
|
+
process.exit(1);
|
|
1185
|
+
}
|
|
1037
1186
|
log(`${colors.dim}\uC218\uB3D9\uC73C\uB85C \uC785\uB825\uD558\uC138\uC694${colors.reset}`);
|
|
1038
1187
|
appId = await prompt(`${colors.blue}?${colors.reset} App ID: `);
|
|
1039
1188
|
publicKey = await prompt(`${colors.blue}?${colors.reset} Public Key (cb_pk_): `);
|
|
@@ -1048,77 +1197,92 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
1048
1197
|
}
|
|
1049
1198
|
const publicKeyForSdk = publicKey;
|
|
1050
1199
|
let storageId = "";
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1200
|
+
if (opts.storageId) {
|
|
1201
|
+
storageId = opts.storageId;
|
|
1202
|
+
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC9C0\uC815\uB428: ${storageId}`);
|
|
1203
|
+
} else {
|
|
1204
|
+
try {
|
|
1205
|
+
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC870\uD68C \uC911...");
|
|
1206
|
+
const listRes = await makeRequest(
|
|
1207
|
+
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
1208
|
+
"GET",
|
|
1209
|
+
{ "X-Public-Key": publicKeyForSdk }
|
|
1210
|
+
);
|
|
1211
|
+
if (listRes.status === 200) {
|
|
1212
|
+
const listData = listRes.data;
|
|
1213
|
+
const storages = listData.storages || [];
|
|
1214
|
+
if (nonInteractive) {
|
|
1215
|
+
const found = storages.find((s) => s.name === projectName);
|
|
1216
|
+
if (found) {
|
|
1217
|
+
storageId = found.id;
|
|
1218
|
+
success(`\uAE30\uC874 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC7AC\uC0AC\uC6A9: ${found.name}`);
|
|
1219
|
+
}
|
|
1220
|
+
} else if (storages.length > 0) {
|
|
1221
|
+
log(`
|
|
1063
1222
|
${colors.dim}\uAE30\uC874 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0:${colors.reset}`);
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1223
|
+
storages.forEach((s, i) => {
|
|
1224
|
+
log(` ${colors.cyan}${i + 1}${colors.reset}) ${s.name} (${colors.dim}${s.id}${colors.reset})`);
|
|
1225
|
+
});
|
|
1226
|
+
log(` ${colors.cyan}0${colors.reset}) \uC0C8\uB85C \uC0DD\uC131`);
|
|
1227
|
+
const choice = await prompt(`
|
|
1069
1228
|
${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1229
|
+
const num = parseInt(choice, 10);
|
|
1230
|
+
if (num > 0 && num <= storages.length) {
|
|
1231
|
+
storageId = storages[num - 1].id;
|
|
1232
|
+
success(`\uC120\uD0DD\uB428: ${storages[num - 1].name}`);
|
|
1233
|
+
}
|
|
1074
1234
|
}
|
|
1235
|
+
if (!storageId) {
|
|
1236
|
+
const name = nonInteractive ? projectName : await prompt(`${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1237
|
+
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
1238
|
+
const createRes = await makeRequest(
|
|
1239
|
+
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
1240
|
+
"POST",
|
|
1241
|
+
{ "X-Public-Key": publicKeyForSdk },
|
|
1242
|
+
JSON.stringify({ name })
|
|
1243
|
+
);
|
|
1244
|
+
if (createRes.status !== 200) {
|
|
1245
|
+
const data = createRes.data;
|
|
1246
|
+
error(`\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`);
|
|
1247
|
+
process.exit(1);
|
|
1248
|
+
}
|
|
1249
|
+
const createData = createRes.data;
|
|
1250
|
+
storageId = createData.storage_web_id;
|
|
1251
|
+
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
1252
|
+
}
|
|
1253
|
+
} else {
|
|
1254
|
+
throw new Error(`HTTP ${listRes.status}`);
|
|
1075
1255
|
}
|
|
1256
|
+
} catch (err) {
|
|
1257
|
+
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1258
|
+
if (nonInteractive) {
|
|
1259
|
+
info("\uB124\uD2B8\uC6CC\uD06C \uBCF5\uAD6C \uD6C4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uAC70\uB098, --storage <\uC2A4\uD1A0\uB9AC\uC9C0ID>\uC640 \uD568\uAED8 \uC2E4\uD589\uD558\uC138\uC694");
|
|
1260
|
+
process.exit(1);
|
|
1261
|
+
}
|
|
1262
|
+
log(`${colors.dim}\uC218\uB3D9\uC73C\uB85C Storage ID\uB97C \uC785\uB825\uD558\uC138\uC694${colors.reset}`);
|
|
1263
|
+
storageId = await prompt(`${colors.blue}?${colors.reset} Storage ID: `);
|
|
1076
1264
|
if (!storageId) {
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
1080
|
-
const createRes = await makeRequest(
|
|
1081
|
-
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
1082
|
-
"POST",
|
|
1083
|
-
{ "X-Public-Key": publicKeyForSdk },
|
|
1084
|
-
JSON.stringify({ name })
|
|
1085
|
-
);
|
|
1086
|
-
if (createRes.status !== 200) {
|
|
1087
|
-
const data = createRes.data;
|
|
1088
|
-
error(`\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`);
|
|
1089
|
-
process.exit(1);
|
|
1090
|
-
}
|
|
1091
|
-
const createData = createRes.data;
|
|
1092
|
-
storageId = createData.storage_web_id;
|
|
1093
|
-
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
1265
|
+
error("Storage ID\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1266
|
+
process.exit(1);
|
|
1094
1267
|
}
|
|
1095
|
-
} else {
|
|
1096
|
-
throw new Error(`HTTP ${listRes.status}`);
|
|
1097
|
-
}
|
|
1098
|
-
} catch (err) {
|
|
1099
|
-
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1100
|
-
log(`${colors.dim}\uC218\uB3D9\uC73C\uB85C Storage ID\uB97C \uC785\uB825\uD558\uC138\uC694${colors.reset}`);
|
|
1101
|
-
storageId = await prompt(`${colors.blue}?${colors.reset} Storage ID: `);
|
|
1102
|
-
if (!storageId) {
|
|
1103
|
-
error("Storage ID\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1104
|
-
process.exit(1);
|
|
1105
1268
|
}
|
|
1106
1269
|
}
|
|
1107
|
-
const defaultDir = detectBuildDir();
|
|
1108
|
-
const deployDir = await prompt(`${colors.blue}?${colors.reset} \uBC30\uD3EC \uB514\uB809\uD1A0\uB9AC (${defaultDir}): `) || defaultDir;
|
|
1109
|
-
const
|
|
1270
|
+
const defaultDir = opts.deployDir || detectBuildDir();
|
|
1271
|
+
const deployDir = nonInteractive ? defaultDir : await prompt(`${colors.blue}?${colors.reset} \uBC30\uD3EC \uB514\uB809\uD1A0\uB9AC (${defaultDir}): `) || defaultDir;
|
|
1272
|
+
const rcData = {
|
|
1273
|
+
appId,
|
|
1110
1274
|
publicKey: publicKey || "",
|
|
1111
1275
|
storageId,
|
|
1112
1276
|
deployDir
|
|
1113
1277
|
};
|
|
1114
1278
|
if (secretKey) {
|
|
1115
|
-
|
|
1279
|
+
rcData.secretKey = secretKey;
|
|
1116
1280
|
}
|
|
1117
|
-
fs2.writeFileSync(rcPath, JSON.stringify(
|
|
1281
|
+
fs2.writeFileSync(rcPath, JSON.stringify(rcData, null, 2) + "\n");
|
|
1118
1282
|
success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
|
|
1119
1283
|
addToGitignore(".connectbaserc");
|
|
1120
1284
|
addDeployScript(deployDir);
|
|
1121
|
-
await setupClaudeCode(
|
|
1285
|
+
await setupClaudeCode(secretKey, projectRoot);
|
|
1122
1286
|
log(`
|
|
1123
1287
|
${colors.green}\uCD08\uAE30\uD654 \uC644\uB8CC!${colors.reset}
|
|
1124
1288
|
`);
|
|
@@ -1132,9 +1296,20 @@ function detectBuildDir() {
|
|
|
1132
1296
|
if (fs2.existsSync(path2.join(process.cwd(), "dist"))) return "./dist";
|
|
1133
1297
|
if (fs2.existsSync(path2.join(process.cwd(), "build"))) return "./build";
|
|
1134
1298
|
if (fs2.existsSync(path2.join(process.cwd(), "out"))) return "./out";
|
|
1135
|
-
|
|
1299
|
+
const nextConfig = ["next.config.js", "next.config.mjs", "next.config.ts"].map((f) => path2.join(process.cwd(), f)).find((f) => fs2.existsSync(f));
|
|
1300
|
+
if (nextConfig || fs2.existsSync(path2.join(process.cwd(), ".next"))) {
|
|
1301
|
+
if (nextConfig) {
|
|
1302
|
+
try {
|
|
1303
|
+
const src = fs2.readFileSync(nextConfig, "utf-8");
|
|
1304
|
+
if (!/output\s*:\s*['"]export['"]/.test(src)) {
|
|
1305
|
+
warn(`Next.js \uAC10\uC9C0: \uC815\uC801 \uBC30\uD3EC\uD558\uB824\uBA74 ${path2.basename(nextConfig)}\uC5D0 output: 'export' \uC124\uC815\uC774 \uD544\uC694\uD569\uB2C8\uB2E4`);
|
|
1306
|
+
}
|
|
1307
|
+
} catch {
|
|
1308
|
+
}
|
|
1309
|
+
}
|
|
1310
|
+
return "./out";
|
|
1311
|
+
}
|
|
1136
1312
|
if (fs2.existsSync(path2.join(process.cwd(), "vite.config.ts")) || fs2.existsSync(path2.join(process.cwd(), "vite.config.js"))) return "./dist";
|
|
1137
|
-
if (fs2.existsSync(path2.join(process.cwd(), "next.config.js")) || fs2.existsSync(path2.join(process.cwd(), "next.config.mjs"))) return "./out";
|
|
1138
1313
|
return "./dist";
|
|
1139
1314
|
}
|
|
1140
1315
|
function addToGitignore(entry, basePath) {
|
|
@@ -1142,7 +1317,8 @@ function addToGitignore(entry, basePath) {
|
|
|
1142
1317
|
const gitignorePath = path2.join(dir, ".gitignore");
|
|
1143
1318
|
if (fs2.existsSync(gitignorePath)) {
|
|
1144
1319
|
const content = fs2.readFileSync(gitignorePath, "utf-8");
|
|
1145
|
-
|
|
1320
|
+
const lines = content.split(/\r?\n/).map((l) => l.trim());
|
|
1321
|
+
if (!lines.includes(entry)) {
|
|
1146
1322
|
fs2.appendFileSync(gitignorePath, `
|
|
1147
1323
|
# Connect Base
|
|
1148
1324
|
${entry}
|
|
@@ -1223,13 +1399,66 @@ function detectMonorepo(gitRoot) {
|
|
|
1223
1399
|
}
|
|
1224
1400
|
return result;
|
|
1225
1401
|
}
|
|
1226
|
-
|
|
1402
|
+
var DOCS_MANIFEST_FILE = ".connectbase-manifest.json";
|
|
1403
|
+
function readDocsManifest(docsDir) {
|
|
1404
|
+
try {
|
|
1405
|
+
const parsed = JSON.parse(fs2.readFileSync(path2.join(docsDir, DOCS_MANIFEST_FILE), "utf-8"));
|
|
1406
|
+
if (parsed && Array.isArray(parsed.files)) return parsed;
|
|
1407
|
+
} catch {
|
|
1408
|
+
}
|
|
1409
|
+
return null;
|
|
1410
|
+
}
|
|
1411
|
+
var LEGACY_DOC_FILES = [
|
|
1412
|
+
"intro.md",
|
|
1413
|
+
"mcp-workflow.md",
|
|
1414
|
+
"sdk-basic.md",
|
|
1415
|
+
"sdk-database.md",
|
|
1416
|
+
"sdk-storage.md",
|
|
1417
|
+
"sdk-realtime.md",
|
|
1418
|
+
"sdk-auth.md",
|
|
1419
|
+
"sdk-payment.md",
|
|
1420
|
+
"example.md",
|
|
1421
|
+
"mcp-config.md",
|
|
1422
|
+
"mcp-tools.md",
|
|
1423
|
+
"resources.md",
|
|
1424
|
+
"sdk-game.md",
|
|
1425
|
+
"sdk-push.md",
|
|
1426
|
+
"sdk-video.md",
|
|
1427
|
+
"sdk-ads.md",
|
|
1428
|
+
"sdk-ai.md",
|
|
1429
|
+
"sdk-error-tracker.md",
|
|
1430
|
+
"sdk-webrtc.md",
|
|
1431
|
+
"sdk-subscription.md",
|
|
1432
|
+
"sdk-functions.md",
|
|
1433
|
+
"sdk-webhooks.md",
|
|
1434
|
+
"deploy-guide.md",
|
|
1435
|
+
"app-packaging.md",
|
|
1436
|
+
"best-practices.md",
|
|
1437
|
+
"code-analysis.md",
|
|
1438
|
+
"sdk-queue.md",
|
|
1439
|
+
"sdk-knowledge.md",
|
|
1440
|
+
"sdk-workflow.md",
|
|
1441
|
+
"sdk-tunnel.md",
|
|
1442
|
+
"sdk-native.md",
|
|
1443
|
+
"sdk-analytics.md",
|
|
1444
|
+
"sdk-domain.md",
|
|
1445
|
+
"sdk-public-key.md",
|
|
1446
|
+
"sdk-invoice.md",
|
|
1447
|
+
"web-performance.md",
|
|
1448
|
+
"sdk-analytics-advanced.md",
|
|
1449
|
+
"sdk-endpoint.md",
|
|
1450
|
+
"sdk-web-search.md",
|
|
1451
|
+
"sdk-oauth-provider.md",
|
|
1452
|
+
"sdk-oauth-integration.md"
|
|
1453
|
+
];
|
|
1454
|
+
async function downloadDocs(baseDir, opts = {}) {
|
|
1455
|
+
const quiet = opts.quiet === true;
|
|
1227
1456
|
if (!baseDir) {
|
|
1228
1457
|
baseDir = getProjectRoot();
|
|
1229
1458
|
}
|
|
1230
1459
|
const gitRoot = getGitRoot() || baseDir;
|
|
1231
1460
|
const monorepo = detectMonorepo(gitRoot);
|
|
1232
|
-
if (monorepo.type !== "none") {
|
|
1461
|
+
if (!quiet && monorepo.type !== "none") {
|
|
1233
1462
|
info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${gitRoot})`);
|
|
1234
1463
|
if (monorepo.isSubPackage) {
|
|
1235
1464
|
info(`\uD604\uC7AC \uC11C\uBE0C \uD328\uD0A4\uC9C0: ${monorepo.subPackageName || path2.basename(process.cwd())}`);
|
|
@@ -1240,14 +1469,10 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
1240
1469
|
if (!fs2.existsSync(docsDir)) {
|
|
1241
1470
|
fs2.mkdirSync(docsDir, { recursive: true });
|
|
1242
1471
|
}
|
|
1243
|
-
if (!
|
|
1244
|
-
templates = ["rules"];
|
|
1245
|
-
}
|
|
1246
|
-
info("SDK \uAC1C\uBC1C \uAC00\uC774\uB4DC \uB2E4\uC6B4\uB85C\uB4DC \uC911...");
|
|
1472
|
+
if (!quiet) info("SDK \uAC1C\uBC1C \uAC00\uC774\uB4DC \uB2E4\uC6B4\uB85C\uB4DC \uC911...");
|
|
1247
1473
|
try {
|
|
1248
|
-
const templateParam = templates.join(",");
|
|
1249
1474
|
const res = await makeRequest(
|
|
1250
|
-
`${DEFAULT_BASE_URL}/v1/storages/webs/claude-md?template
|
|
1475
|
+
`${DEFAULT_BASE_URL}/v1/storages/webs/claude-md?template=rules&format=sections`,
|
|
1251
1476
|
"GET",
|
|
1252
1477
|
{}
|
|
1253
1478
|
);
|
|
@@ -1256,18 +1481,59 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
1256
1481
|
}
|
|
1257
1482
|
const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
1258
1483
|
const sections = data.sections || [];
|
|
1484
|
+
const hasher = crypto.createHash("sha256");
|
|
1485
|
+
const newFiles = [];
|
|
1259
1486
|
for (const section of sections) {
|
|
1260
|
-
const
|
|
1261
|
-
|
|
1487
|
+
const safeName = path2.basename(section.filename);
|
|
1488
|
+
newFiles.push(safeName);
|
|
1489
|
+
hasher.update(safeName).update("\0").update(section.content).update("\0");
|
|
1490
|
+
}
|
|
1491
|
+
const contentHash = hasher.digest("hex");
|
|
1492
|
+
const prevManifest = readDocsManifest(docsDir);
|
|
1493
|
+
const allPresent = newFiles.every((f) => fs2.existsSync(path2.join(docsDir, f)));
|
|
1494
|
+
if (prevManifest && prevManifest.contentHash === contentHash && allPresent) {
|
|
1495
|
+
if (!quiet) success("SDK \uBB38\uC11C\uAC00 \uC774\uBBF8 \uCD5C\uC2E0\uC785\uB2C8\uB2E4");
|
|
1496
|
+
updateRootClaudeMd(rootClaudeMd, quiet);
|
|
1497
|
+
if (monorepo.isSubPackage) {
|
|
1498
|
+
createSubPackageClaudeMd(path2.join(process.cwd(), "CLAUDE.md"), gitRoot, quiet);
|
|
1499
|
+
}
|
|
1500
|
+
return "unchanged";
|
|
1262
1501
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1502
|
+
const stamp = `<!-- generated by connectbase-cli v${VERSION} (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}) \u2014 \uC790\uB3D9 \uC0DD\uC131 \uD30C\uC77C\uC785\uB2C8\uB2E4. \uC9C1\uC811 \uC218\uC815\uD558\uC9C0 \uB9D0\uACE0 'npx connectbase update'\uB85C \uAC31\uC2E0\uD558\uC138\uC694 -->
|
|
1503
|
+
|
|
1504
|
+
`;
|
|
1505
|
+
for (const section of sections) {
|
|
1506
|
+
const safeName = path2.basename(section.filename);
|
|
1507
|
+
fs2.writeFileSync(path2.join(docsDir, safeName), stamp + section.content);
|
|
1508
|
+
}
|
|
1509
|
+
const staleCandidates = prevManifest ? prevManifest.files.map((f) => path2.basename(f)) : LEGACY_DOC_FILES;
|
|
1510
|
+
for (const old of staleCandidates) {
|
|
1511
|
+
if (newFiles.includes(old)) continue;
|
|
1512
|
+
const oldPath = path2.join(docsDir, old);
|
|
1513
|
+
if (fs2.existsSync(oldPath)) {
|
|
1514
|
+
try {
|
|
1515
|
+
fs2.unlinkSync(oldPath);
|
|
1516
|
+
if (!quiet) info(`\uC2A4\uD14C\uC77C \uBB38\uC11C \uC0AD\uC81C: ${old}`);
|
|
1517
|
+
} catch {
|
|
1518
|
+
}
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
const manifest = {
|
|
1522
|
+
cliVersion: VERSION,
|
|
1523
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1524
|
+
contentHash,
|
|
1525
|
+
files: newFiles
|
|
1526
|
+
};
|
|
1527
|
+
fs2.writeFileSync(path2.join(docsDir, DOCS_MANIFEST_FILE), JSON.stringify(manifest, null, 2) + "\n");
|
|
1528
|
+
if (!quiet) success(`${gitRoot}/.claude/docs/ \uC5D0 ${sections.length}\uAC1C \uBB38\uC11C \uC800\uC7A5 \uC644\uB8CC`);
|
|
1529
|
+
updateRootClaudeMd(rootClaudeMd, quiet);
|
|
1265
1530
|
if (monorepo.isSubPackage) {
|
|
1266
|
-
|
|
1267
|
-
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
1531
|
+
createSubPackageClaudeMd(path2.join(process.cwd(), "CLAUDE.md"), gitRoot, quiet);
|
|
1268
1532
|
}
|
|
1269
|
-
log(`${colors.dim}\u203B SDK \uBB38\uC11C\uB294 MCP search_sdk_docs\uB85C \uAC80\uC0C9\uD558\uC138\uC694${colors.reset}`);
|
|
1533
|
+
if (!quiet) log(`${colors.dim}\u203B SDK \uBB38\uC11C\uB294 MCP search_sdk_docs\uB85C \uAC80\uC0C9\uD558\uC138\uC694${colors.reset}`);
|
|
1534
|
+
return "updated";
|
|
1270
1535
|
} catch {
|
|
1536
|
+
if (quiet) return "failed";
|
|
1271
1537
|
warn("SDK \uAC00\uC774\uB4DC \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328, \uAE30\uBCF8 \uBB38\uC11C\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4");
|
|
1272
1538
|
const fallbackContent = `# ConnectBase \uD504\uB85C\uC81D\uD2B8
|
|
1273
1539
|
|
|
@@ -1282,9 +1548,10 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
1282
1548
|
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
1283
1549
|
}
|
|
1284
1550
|
success(`.claude/docs/ \uC0DD\uC131 \uC644\uB8CC (\uAE30\uBCF8)`);
|
|
1551
|
+
return "failed";
|
|
1285
1552
|
}
|
|
1286
1553
|
}
|
|
1287
|
-
function updateRootClaudeMd(claudeMdPath) {
|
|
1554
|
+
function updateRootClaudeMd(claudeMdPath, quiet = false) {
|
|
1288
1555
|
const startMarker = "<!-- CONNECTBASE_START -->";
|
|
1289
1556
|
const endMarker = "<!-- CONNECTBASE_END -->";
|
|
1290
1557
|
const sdkBlock = `${startMarker}
|
|
@@ -1292,11 +1559,13 @@ function updateRootClaudeMd(claudeMdPath) {
|
|
|
1292
1559
|
|
|
1293
1560
|
\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 **ConnectBase**\uB97C \uBC31\uC5D4\uB4DC\uB85C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.
|
|
1294
1561
|
|
|
1295
|
-
|
|
1562
|
+
@.claude/docs/project-rules.md
|
|
1296
1563
|
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1564
|
+
### AI \uD544\uC218 \uC6CC\uD06C\uD50C\uB85C\uC6B0
|
|
1565
|
+
|
|
1566
|
+
1. \uC704\uC5D0 import\uB41C project-rules.md\uC758 \uAE30\uB2A5 \uB9E4\uD551\uD45C\xB7\uBCF4\uC548 \uADDC\uCE59\uC744 \uB530\uB974\uC138\uC694
|
|
1567
|
+
2. \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uAE30 \uC804 \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C SDK \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uACE0, \uAC80\uC0C9 \uACB0\uACFC\uC758 \uCF54\uB4DC \uD328\uD134\uC744 \uADF8\uB300\uB85C \uB530\uB974\uC138\uC694
|
|
1568
|
+
3. \`search_sdk_docs\` \uB3C4\uAD6C\uAC00 \uBCF4\uC774\uC9C0 \uC54A\uC73C\uBA74 MCP\uAC00 \uC5F0\uACB0\uB418\uC9C0 \uC54A\uC740 \uC0C1\uD0DC\uC785\uB2C8\uB2E4 \u2014 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \`npx connectbase mcp\` \uC2E4\uD589\uC744 \uC548\uB0B4\uD558\uC138\uC694 (Secret Key \uD544\uC694)
|
|
1300
1569
|
${endMarker}`;
|
|
1301
1570
|
if (fs2.existsSync(claudeMdPath)) {
|
|
1302
1571
|
let content = fs2.readFileSync(claudeMdPath, "utf-8");
|
|
@@ -1308,16 +1577,16 @@ ${endMarker}`;
|
|
|
1308
1577
|
content = sdkBlock + "\n\n" + content.trimStart();
|
|
1309
1578
|
}
|
|
1310
1579
|
fs2.writeFileSync(claudeMdPath, content);
|
|
1311
|
-
info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
1580
|
+
if (!quiet) info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
1312
1581
|
} else {
|
|
1313
1582
|
fs2.writeFileSync(claudeMdPath, `# \uD504\uB85C\uC81D\uD2B8
|
|
1314
1583
|
|
|
1315
1584
|
${sdkBlock}
|
|
1316
1585
|
`);
|
|
1317
|
-
success("CLAUDE.md \uC0DD\uC131 \uC644\uB8CC");
|
|
1586
|
+
if (!quiet) success("CLAUDE.md \uC0DD\uC131 \uC644\uB8CC");
|
|
1318
1587
|
}
|
|
1319
1588
|
}
|
|
1320
|
-
function createSubPackageClaudeMd(subClaudeMdPath, gitRoot) {
|
|
1589
|
+
function createSubPackageClaudeMd(subClaudeMdPath, gitRoot, quiet = false) {
|
|
1321
1590
|
const startMarker = "<!-- CONNECTBASE_SUB_START -->";
|
|
1322
1591
|
const endMarker = "<!-- CONNECTBASE_SUB_END -->";
|
|
1323
1592
|
const relPath = path2.relative(path2.dirname(subClaudeMdPath), gitRoot);
|
|
@@ -1328,9 +1597,9 @@ function createSubPackageClaudeMd(subClaudeMdPath, gitRoot) {
|
|
|
1328
1597
|
|
|
1329
1598
|
### AI \uD544\uB3C5
|
|
1330
1599
|
|
|
1331
|
-
1. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`${relPath}/.claude/docs/project-rules.md\`\
|
|
1332
|
-
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C SDK \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
1333
|
-
3. \
|
|
1600
|
+
1. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`${relPath}/.claude/docs/project-rules.md\`\uC758 \uAE30\uB2A5 \uB9E4\uD551\uD45C\xB7\uBCF4\uC548 \uADDC\uCE59\uC744 \uB530\uB974\uC138\uC694
|
|
1601
|
+
2. \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uAE30 \uC804 \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C SDK \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
1602
|
+
3. \`search_sdk_docs\` \uB3C4\uAD6C\uAC00 \uBCF4\uC774\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \`npx connectbase mcp\` \uC2E4\uD589\uC744 \uC548\uB0B4\uD558\uC138\uC694
|
|
1334
1603
|
${endMarker}`;
|
|
1335
1604
|
if (fs2.existsSync(subClaudeMdPath)) {
|
|
1336
1605
|
let content = fs2.readFileSync(subClaudeMdPath, "utf-8");
|
|
@@ -1350,7 +1619,7 @@ ${endMarker}`;
|
|
|
1350
1619
|
${subBlock}
|
|
1351
1620
|
`);
|
|
1352
1621
|
}
|
|
1353
|
-
success(`\uC11C\uBE0C \uD328\uD0A4\uC9C0 CLAUDE.md \uC0DD\uC131 \uC644\uB8CC: ${subClaudeMdPath}`);
|
|
1622
|
+
if (!quiet) success(`\uC11C\uBE0C \uD328\uD0A4\uC9C0 CLAUDE.md \uC0DD\uC131 \uC644\uB8CC: ${subClaudeMdPath}`);
|
|
1354
1623
|
}
|
|
1355
1624
|
async function setupMcp(secretKey) {
|
|
1356
1625
|
const gitRoot = getGitRoot();
|
|
@@ -1364,6 +1633,13 @@ async function setupMcp(secretKey) {
|
|
|
1364
1633
|
}
|
|
1365
1634
|
const mcpConfigPath = path2.join(root, ".mcp.json");
|
|
1366
1635
|
if (!secretKey) {
|
|
1636
|
+
if (!process.stdin.isTTY) {
|
|
1637
|
+
error("Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (\uBE44\uB300\uD654\uD615 \uD658\uACBD)");
|
|
1638
|
+
log(`${colors.dim}\uB2E4\uC74C \uC911 \uD558\uB098\uB85C \uD0A4\uB97C \uC804\uB2EC\uD558\uC138\uC694:${colors.reset}`);
|
|
1639
|
+
log(` ${colors.cyan}npx connectbase mcp --secret-key cb_sk_...${colors.reset}`);
|
|
1640
|
+
log(` ${colors.cyan}CONNECTBASE_SECRET_KEY=cb_sk_... npx connectbase mcp${colors.reset}`);
|
|
1641
|
+
process.exit(1);
|
|
1642
|
+
}
|
|
1367
1643
|
log(`
|
|
1368
1644
|
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
1369
1645
|
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
@@ -1427,9 +1703,9 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
1427
1703
|
log(`
|
|
1428
1704
|
${colors.dim}Claude Code\uC5D0\uC11C ConnectBase MCP \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.${colors.reset}`);
|
|
1429
1705
|
}
|
|
1430
|
-
async function setupClaudeCode(
|
|
1706
|
+
async function setupClaudeCode(secretKey, projectRoot) {
|
|
1431
1707
|
const root = projectRoot || getProjectRoot();
|
|
1432
|
-
await downloadDocs(
|
|
1708
|
+
await downloadDocs(root);
|
|
1433
1709
|
await setupMcp(secretKey);
|
|
1434
1710
|
}
|
|
1435
1711
|
async function fetchLatestVersion(packageName) {
|
|
@@ -1448,13 +1724,18 @@ async function fetchLatestVersion(packageName) {
|
|
|
1448
1724
|
return null;
|
|
1449
1725
|
}
|
|
1450
1726
|
function compareVersions(a, b) {
|
|
1451
|
-
const
|
|
1452
|
-
const
|
|
1727
|
+
const [aMain, aPre] = a.split("-", 2);
|
|
1728
|
+
const [bMain, bPre] = b.split("-", 2);
|
|
1729
|
+
const pa = aMain.split(".").map(Number);
|
|
1730
|
+
const pb = bMain.split(".").map(Number);
|
|
1453
1731
|
for (let i = 0; i < 3; i++) {
|
|
1454
1732
|
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
1455
1733
|
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
1456
1734
|
}
|
|
1457
|
-
return 0;
|
|
1735
|
+
if (aPre === bPre) return 0;
|
|
1736
|
+
if (!aPre) return 1;
|
|
1737
|
+
if (!bPre) return -1;
|
|
1738
|
+
return aPre < bPre ? -1 : aPre > bPre ? 1 : 0;
|
|
1458
1739
|
}
|
|
1459
1740
|
function findSubPackagesWithConfig(gitRoot) {
|
|
1460
1741
|
const results = [];
|
|
@@ -1526,36 +1807,7 @@ ${colors.cyan}ConnectBase Update${colors.reset}
|
|
|
1526
1807
|
if (!opts.skipDocs) {
|
|
1527
1808
|
log("");
|
|
1528
1809
|
info("SDK \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uC911...");
|
|
1529
|
-
|
|
1530
|
-
if (!docsKey) {
|
|
1531
|
-
const rootRcPath = path2.join(projectRoot, ".connectbaserc");
|
|
1532
|
-
if (fs2.existsSync(rootRcPath)) {
|
|
1533
|
-
try {
|
|
1534
|
-
const rc = JSON.parse(fs2.readFileSync(rootRcPath, "utf-8"));
|
|
1535
|
-
docsKey = rc.publicKey || rc.secretKey;
|
|
1536
|
-
} catch {
|
|
1537
|
-
}
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
if (!docsKey && monorepo.type !== "none") {
|
|
1541
|
-
const subPackages = findSubPackagesWithConfig(projectRoot);
|
|
1542
|
-
for (const sp of subPackages) {
|
|
1543
|
-
try {
|
|
1544
|
-
const rc = JSON.parse(fs2.readFileSync(path2.join(sp, ".connectbaserc"), "utf-8"));
|
|
1545
|
-
if (rc.publicKey || rc.secretKey) {
|
|
1546
|
-
docsKey = rc.publicKey || rc.secretKey;
|
|
1547
|
-
break;
|
|
1548
|
-
}
|
|
1549
|
-
} catch {
|
|
1550
|
-
}
|
|
1551
|
-
}
|
|
1552
|
-
}
|
|
1553
|
-
if (docsKey) {
|
|
1554
|
-
await downloadDocs(docsKey, void 0, projectRoot);
|
|
1555
|
-
} else {
|
|
1556
|
-
warn("Public Key\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4");
|
|
1557
|
-
info(".connectbaserc \uD30C\uC77C\uC774\uB098 --public-key \uC635\uC158\uC73C\uB85C \uD0A4\uB97C \uC9C0\uC815\uD558\uC138\uC694");
|
|
1558
|
-
}
|
|
1810
|
+
await downloadDocs(projectRoot);
|
|
1559
1811
|
}
|
|
1560
1812
|
if (!opts.skipMcp) {
|
|
1561
1813
|
log("");
|
|
@@ -2620,6 +2872,9 @@ ${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
|
|
|
2620
2872
|
docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2621
2873
|
mcp MCP \uC11C\uBC84 \uC124\uC815 (.mcp.json \uC0DD\uC131/\uC5C5\uB370\uC774\uD2B8, \uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2622
2874
|
deploy <directory> \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uD30C\uC77C \uBC30\uD3EC (--dev: Dev \uD658\uACBD)
|
|
2875
|
+
promote_mode=manual \uC774\uBA74 QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC\uB429\uB2C8\uB2E4
|
|
2876
|
+
promote [releaseId] QA \uAC80\uC99D \uD6C4 production \uBC18\uC601 (\uBBF8\uC9C0\uC815: \uCD5C\uC2E0 \uBC30\uD3EC, \uACFC\uAC70 id: \uB864\uBC31)
|
|
2877
|
+
releases \uBC30\uD3EC \uC774\uB825 \uC870\uD68C (release \uBC88\uD638/\uC0C1\uD0DC/production\xB7QA \uD3EC\uC778\uD130)
|
|
2623
2878
|
tunnel <port> \uB85C\uCEEC \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uB178\uCD9C
|
|
2624
2879
|
|
|
2625
2880
|
${colors.yellow}\uCF54\uB4DC \uBD84\uC11D (MCP \uD1B5\uD574 \uC0AC\uC6A9):${colors.reset}
|
|
@@ -2642,8 +2897,12 @@ ${colors.yellow}\uC635\uC158:${colors.reset}
|
|
|
2642
2897
|
--label <name> Endpoint binding \uC790\uB3D9 \uB4F1\uB85D (tunnel \uC804\uC6A9) \u2014 SDK \uC758 cb.endpoint.call(label) \uD638\uCD9C \uAC00\uB2A5
|
|
2643
2898
|
--description <text> Endpoint binding \uC124\uBA85 (--label \uB3D9\uBC18 \uC2DC\uB9CC)
|
|
2644
2899
|
-d, --dev Dev \uD658\uACBD\uC5D0 \uBC30\uD3EC (deploy \uC804\uC6A9)
|
|
2900
|
+
-y, --yes \uBE44\uB300\uD654\uD615 \uCD08\uAE30\uD654 \u2014 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uAE30\uBCF8\uAC12/\uD50C\uB798\uADF8\uB85C \uC9C4\uD589 (init \uC804\uC6A9, CI/AI \uC5D0\uC774\uC804\uD2B8\uC6A9)
|
|
2901
|
+
-a, --app <id> \uAE30\uC874 \uC571 ID \uC9C0\uC815 (init \uC804\uC6A9)
|
|
2902
|
+
--secret-key <key> Secret Key (cb_sk_) \uC9C0\uC815 (init/mcp)
|
|
2903
|
+
--deploy-dir <dir> \uBC30\uD3EC \uB514\uB809\uD1A0\uB9AC \uC9C0\uC815 (init \uC804\uC6A9, \uAE30\uBCF8: \uC790\uB3D9 \uAC10\uC9C0)
|
|
2645
2904
|
--check \uBC84\uC804\uB9CC \uD655\uC778 (update \uC804\uC6A9)
|
|
2646
|
-
--skip-docs \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uAC74\uB108\uB6F0\uAE30 (update
|
|
2905
|
+
--skip-docs \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uAC74\uB108\uB6F0\uAE30 (update/deploy)
|
|
2647
2906
|
--skip-mcp MCP \uC5C5\uB370\uC774\uD2B8 \uAC74\uB108\uB6F0\uAE30 (update \uC804\uC6A9)
|
|
2648
2907
|
--setup-root \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 (update \uC804\uC6A9)
|
|
2649
2908
|
(docs, mcp, update\uB294 \uBAA8\uB178\uB808\uD3EC\uB97C \uC790\uB3D9 \uAC10\uC9C0\uD558\uC5EC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131)
|
|
@@ -2654,6 +2913,9 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
|
2654
2913
|
${colors.dim}# 1. \uCD08\uAE30\uD654 (\uCD5C\uCD08 1\uD68C)${colors.reset}
|
|
2655
2914
|
npx connectbase init
|
|
2656
2915
|
|
|
2916
|
+
${colors.dim}# 1-1. \uBE44\uB300\uD654\uD615 \uCD08\uAE30\uD654 (CI/AI \uC5D0\uC774\uC804\uD2B8)${colors.reset}
|
|
2917
|
+
npx connectbase init --yes --secret-key cb_sk_...
|
|
2918
|
+
|
|
2657
2919
|
${colors.dim}# 2. SDK \uC804\uCCB4 \uC5C5\uB370\uC774\uD2B8 (\uBC84\uC804 \uCCB4\uD06C + \uBB38\uC11C + MCP)${colors.reset}
|
|
2658
2920
|
npx connectbase update
|
|
2659
2921
|
|
|
@@ -2669,7 +2931,12 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
|
2669
2931
|
${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
|
|
2670
2932
|
npm run deploy
|
|
2671
2933
|
|
|
2672
|
-
${colors.dim}# 4.
|
|
2934
|
+
${colors.dim}# 4-1. QA \uC6CC\uD06C\uD50C\uB85C\uC6B0 (\uCF58\uC194\uC5D0\uC11C promote_mode=manual \uC124\uC815 \uC2DC)${colors.reset}
|
|
2935
|
+
npx connectbase deploy ./dist ${colors.dim}# QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC${colors.reset}
|
|
2936
|
+
npx connectbase releases ${colors.dim}# \uBC30\uD3EC \uC774\uB825 \uD655\uC778${colors.reset}
|
|
2937
|
+
npx connectbase promote ${colors.dim}# QA \uAC80\uC99D \uD6C4 production \uBC18\uC601${colors.reset}
|
|
2938
|
+
|
|
2939
|
+
${colors.dim}# 4-2. Dev \uD658\uACBD \uBC30\uD3EC (\uB0B4\uBD80 \uD655\uC778\uC6A9)${colors.reset}
|
|
2673
2940
|
npx connectbase deploy ./dist --dev
|
|
2674
2941
|
|
|
2675
2942
|
${colors.dim}# 5. \uD130\uB110 (\uAE30\uBCF8)${colors.reset}
|
|
@@ -2724,6 +2991,10 @@ function parseArgs(args) {
|
|
|
2724
2991
|
result.options.appId = args[++i];
|
|
2725
2992
|
} else if (arg === "--force") {
|
|
2726
2993
|
result.options.force = "true";
|
|
2994
|
+
} else if (arg === "-y" || arg === "--yes") {
|
|
2995
|
+
result.options.yes = "true";
|
|
2996
|
+
} else if (arg === "--deploy-dir") {
|
|
2997
|
+
result.options.deployDir = args[++i];
|
|
2727
2998
|
} else if (arg === "--public") {
|
|
2728
2999
|
result.options.public = "true";
|
|
2729
3000
|
} else if (arg === "--show-token") {
|
|
@@ -2771,7 +3042,13 @@ async function main() {
|
|
|
2771
3042
|
baseUrl: parsed.options.baseUrl || fileConfig.baseUrl || DEFAULT_BASE_URL
|
|
2772
3043
|
};
|
|
2773
3044
|
if (parsed.command === "init") {
|
|
2774
|
-
await init(
|
|
3045
|
+
await init(config, {
|
|
3046
|
+
yes: parsed.options.yes === "true",
|
|
3047
|
+
force: parsed.options.force === "true",
|
|
3048
|
+
appId: parsed.options.appId,
|
|
3049
|
+
storageId: parsed.options.storageId,
|
|
3050
|
+
deployDir: parsed.options.deployDir
|
|
3051
|
+
});
|
|
2775
3052
|
} else if (parsed.command === "update") {
|
|
2776
3053
|
await update(config, {
|
|
2777
3054
|
checkOnly: parsed.options.check === "true",
|
|
@@ -2780,9 +3057,9 @@ async function main() {
|
|
|
2780
3057
|
setupRoot: parsed.options.setupRoot === "true"
|
|
2781
3058
|
});
|
|
2782
3059
|
} else if (parsed.command === "docs") {
|
|
2783
|
-
await downloadDocs(
|
|
3060
|
+
await downloadDocs();
|
|
2784
3061
|
} else if (parsed.command === "mcp") {
|
|
2785
|
-
await setupMcp();
|
|
3062
|
+
await setupMcp(config.secretKey);
|
|
2786
3063
|
} else if (parsed.command === "deploy") {
|
|
2787
3064
|
const directory = parsed.args[0] || fileConfig.deployDir || ".";
|
|
2788
3065
|
if (!config.publicKey) {
|
|
@@ -2801,6 +3078,32 @@ async function main() {
|
|
|
2801
3078
|
else warn(`--timeout \uAC12\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544 \uBB34\uC2DC\uB429\uB2C8\uB2E4: ${parsed.options.timeout}`);
|
|
2802
3079
|
}
|
|
2803
3080
|
await deploy(directory, config, isDev, deployOpts);
|
|
3081
|
+
if (parsed.options.skipDocs !== "true") {
|
|
3082
|
+
try {
|
|
3083
|
+
const docsRoot = getGitRoot() || getProjectRoot();
|
|
3084
|
+
if (fs2.existsSync(path2.join(docsRoot, ".claude", "docs", DOCS_MANIFEST_FILE))) {
|
|
3085
|
+
const refreshed = await downloadDocs(docsRoot, { quiet: true });
|
|
3086
|
+
if (refreshed === "updated") {
|
|
3087
|
+
info("SDK \uBB38\uC11C\uAC00 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (.claude/docs/)");
|
|
3088
|
+
}
|
|
3089
|
+
}
|
|
3090
|
+
} catch {
|
|
3091
|
+
}
|
|
3092
|
+
}
|
|
3093
|
+
} else if (parsed.command === "promote" || parsed.command === "releases") {
|
|
3094
|
+
if (!config.publicKey) {
|
|
3095
|
+
error('Public Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -k \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3096
|
+
process.exit(1);
|
|
3097
|
+
}
|
|
3098
|
+
if (!config.storageId) {
|
|
3099
|
+
error('\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
3100
|
+
process.exit(1);
|
|
3101
|
+
}
|
|
3102
|
+
if (parsed.command === "promote") {
|
|
3103
|
+
await promote(config, parsed.args[0]);
|
|
3104
|
+
} else {
|
|
3105
|
+
await listReleases(config);
|
|
3106
|
+
}
|
|
2804
3107
|
} else if (parsed.command === "tunnel") {
|
|
2805
3108
|
const portStr = parsed.args[0];
|
|
2806
3109
|
if (!portStr) {
|