connectbase-client 6.2.0 → 6.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 +106 -5
- package/README.md +24 -19
- package/dist/cli.js +253 -195
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,107 @@
|
|
|
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
|
+
## [6.3.1] - 2026-09-08
|
|
7
|
+
|
|
8
|
+
### Fixed — `init` 의 웹 스토리지 생성 실패가 `[object Object]` 로만 찍히던 문제
|
|
9
|
+
|
|
10
|
+
앱에 이미 같은 이름의 웹 스토리지가 있는 상태에서 `init` 이 같은 이름으로 생성을 시도하면
|
|
11
|
+
서버는 409 를 돌려주는데, CLI 가 그 응답 본문(`{ error: { code, message } }`)을 문자열로
|
|
12
|
+
찍어 `생성 실패: [object Object]` 만 보였습니다. 원인이 이름 충돌이라는 사실을 알 길이
|
|
13
|
+
없었고, 스토리지 선택 프롬프트에서 Enter 만 치면 정확히 이 경로로 들어갔습니다.
|
|
14
|
+
|
|
15
|
+
- 에러 응답 형식이 `{ error: "문자열" }` 이든 표준 봉투 `{ error: { code, message,
|
|
16
|
+
suggestion } }` 이든 사람이 읽을 메시지로 정리하는 헬퍼(`apiErrorMessage`)를 두고,
|
|
17
|
+
`init` / `deploy` / `promote` / 배포 이력 / presign / 앱 생성 / 인증 세션의 에러 출력을
|
|
18
|
+
전부 이 헬퍼로 통일했습니다. 서버가 제안(suggestion)을 보내면 함께 보여 줍니다.
|
|
19
|
+
- 대화형 `init` 도 `--yes` 와 같은 규칙으로, 입력한 이름과 같은 웹 스토리지가 이미 있으면
|
|
20
|
+
생성 대신 그 스토리지를 재사용합니다. 서버 요청 없이 즉시 넘어가므로 409 자체가 나지
|
|
21
|
+
않습니다.
|
|
22
|
+
- 서버(core-server)도 같은 앱의 이름 중복을 미리 검사해 `같은 이름의 웹 스토리지가 이미
|
|
23
|
+
있습니다: "<이름>"` 이라는 409 를 돌려줍니다. 구버전 CLI 로도 이제 이 문장이 보입니다.
|
|
24
|
+
|
|
25
|
+
## [6.3.0] - 2026-09-07
|
|
26
|
+
|
|
27
|
+
### Fixed — Secret Key 가 만료되면 `init` 을 다시 돌릴 수 없던 문제
|
|
28
|
+
|
|
29
|
+
`init` 은 `.connectbaserc` 에 저장된 Secret Key 를 그대로 집어 씁니다. 그 키가 만료되거나
|
|
30
|
+
취소되면 앱 목록 조회가 401 을 받고 **그대로 종료**했는데, 다시 실행해도 같은 죽은 키를
|
|
31
|
+
다시 집어 오므로 같은 자리에서 또 죽었습니다. `.connectbaserc` 를 손으로 지우는 것 말고는
|
|
32
|
+
빠져나올 길이 없었습니다.
|
|
33
|
+
|
|
34
|
+
이제 401 을 받으면 **그 자리에서 재인증**합니다 (브라우저 로그인 또는 직접 입력). 새 키는
|
|
35
|
+
즉시 `.connectbaserc` 에 저장되고 `.mcp.json` 에도 반영되므로, 파일을 지우는 절차가
|
|
36
|
+
사라졌습니다. 비대화형(`--yes`)에서는 프롬프트 대신 새 키를 넘기라고 명확히 안내합니다.
|
|
37
|
+
|
|
38
|
+
`connectbase mcp` 도 같습니다 — 넘겨받은 키가 살아 있는지 **먼저 확인**한 뒤 `.mcp.json`
|
|
39
|
+
에 씁니다. 종전에는 만료된 키를 그대로 적고 "설정 완료" 라고 말해서, 실패가 Claude Code 의
|
|
40
|
+
401 로만 드러났습니다. (네트워크 오류로는 막지 않습니다 — 오프라인에서도 설정은 써집니다.)
|
|
41
|
+
|
|
42
|
+
재초기화 확인 프롬프트의 기본값도 "계속" 으로 바꿨습니다. 같은 앱을 고르면 Public Key 를
|
|
43
|
+
재사용하므로 재실행은 안전한 동작입니다.
|
|
44
|
+
|
|
45
|
+
### Fixed — `npm run deploy` 가 `command not found` 로 죽던 문제
|
|
46
|
+
|
|
47
|
+
`init` 이 `package.json` 에 deploy 스크립트만 넣고 CLI 를 의존성으로 선언하지 않아서,
|
|
48
|
+
SDK 가 설치되지 않은 프로젝트에서 마지막 안내대로 `npm run deploy` 를 치면
|
|
49
|
+
`connectbase: command not found` 가 났습니다. 이제 `devDependencies` 에
|
|
50
|
+
`connectbase-client` 를 함께 선언하고, 설치가 필요하면 마지막 안내에 `npm install` 을
|
|
51
|
+
포함합니다. (`npx --yes` 를 스크립트에 박는 우회는 쓰지 않았습니다 — 빌드마다
|
|
52
|
+
레지스트리에서 최신본을 끌어오면 재현 가능한 빌드가 아닙니다.)
|
|
53
|
+
|
|
54
|
+
빌드 스크립트가 있으면 deploy 스크립트는 이제 build 명령을 **복사하지 않고**
|
|
55
|
+
`npm run build && connectbase deploy <dir>` 로 위임합니다. 복사본은 build 가 바뀌어도
|
|
56
|
+
갱신되지 않아 조용히 낡습니다.
|
|
57
|
+
|
|
58
|
+
### Fixed — `--base-url` / `CONNECTBASE_BASE_URL` 이 `init` 에서 무시되던 문제
|
|
59
|
+
|
|
60
|
+
`init` 과 브라우저 로그인, SDK 문서 다운로드, MCP 설정이 상수를 직접 참조해, 로컬이나
|
|
61
|
+
스테이징 스택을 지정해도 프로덕션 API 로 갔습니다. 브라우저 로그인 URL 도 같은 환경을
|
|
62
|
+
가리키도록 API URL 에서 파생시킵니다.
|
|
63
|
+
|
|
64
|
+
### Fixed — CLI 버전을 빌드 시점에 주입
|
|
65
|
+
|
|
66
|
+
종전에는 런타임에 `package.json` 을 찾아 읽고, 실패하면 하드코딩된 옛 버전(`3.25.1`)으로
|
|
67
|
+
폴백했습니다. 그 폴백은 조용히 낡았고, 단순히 `--version` 이 틀리는 데서 끝나지 않았습니다 —
|
|
68
|
+
`init` 이 사용자의 `package.json` 에 적는 의존성 범위에까지 그 값이 들어갑니다.
|
|
69
|
+
|
|
70
|
+
### Fixed — git 저장소가 아닌 폴더에서 `fatal:` 오류가 출력되던 문제
|
|
71
|
+
|
|
72
|
+
`init` 이 프로젝트 루트를 찾을 때 쓰는 `git rev-parse` 의 stderr 가 그대로 새어 나와,
|
|
73
|
+
정상 경로인데도 화면에 `fatal: … 깃 저장소가 아닙니다` 가 세 번 찍혔습니다.
|
|
74
|
+
|
|
75
|
+
### Changed — 문서의 `npx` 예제가 패키지 이름을 쓰도록 통일
|
|
76
|
+
|
|
77
|
+
`connectbase` 는 이 패키지가 설치하는 **bin 이름**이지 npm 패키지 이름이 아닙니다. npx 는
|
|
78
|
+
로컬 `node_modules/.bin` 을 먼저 보고, 없으면 그 이름의 **패키지**를 레지스트리에서 받으려
|
|
79
|
+
합니다. 그래서 `npx connectbase init` 은 이 패키지가 이미 설치된 프로젝트에서만 동작하고,
|
|
80
|
+
아무것도 없는 새 폴더 — 즉 `init` 을 처음 실행하는 바로 그 상황 — 에서는 npm 404 가 났습니다.
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
$ npx connectbase init
|
|
84
|
+
npm error 404 Not Found - GET https://registry.npmjs.org/connectbase
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
전역 설치(`npm i -g connectbase-client`)로 PATH 에 `connectbase` 가 있어도 마찬가지입니다.
|
|
88
|
+
npx 는 PATH 를 보지 않습니다.
|
|
89
|
+
|
|
90
|
+
규칙을 하나로 정리했습니다:
|
|
91
|
+
|
|
92
|
+
| 형태 | 이름 | 왜 |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| `npx …` | `connectbase-client` | npx 인자는 **패키지** 이름이다. 설치 여부와 무관하게 항상 동작 |
|
|
95
|
+
| 전역 설치 후 / `npm run` 스크립트 / `node_modules/.bin` | `connectbase` | 짧은 bin 이름. 0.16.1 에서 정한 용도 그대로 |
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx connectbase-client init # 설치 전 — 패키지 이름
|
|
99
|
+
npm install # init 이 devDependencies 에 넣어 둔 것을 설치
|
|
100
|
+
npm run deploy # 내부적으로 `connectbase deploy ./dist` — 짧은 이름 그대로
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`connectbase` 를 실제 패키지로도 발행하려 했으나, npm 이 2013 년의 `connect-base` 와
|
|
104
|
+
이름이 유사하다는 이유로 거절했습니다 (`@connectbase` 스코프도 타인이 사용 중). 이름을
|
|
105
|
+
확보하면 `npx connectbase` 도 함께 동작하게 할 예정입니다.
|
|
106
|
+
|
|
6
107
|
## [6.2.0] - 2026-09-06
|
|
7
108
|
|
|
8
109
|
### Added — 웹 스토리지 관리 API 에 Secret Key 전송
|
|
@@ -23,7 +124,7 @@ production 승격이 가능했습니다 — 공개된 사이트에서 키를 뽑
|
|
|
23
124
|
|
|
24
125
|
**이 버전은 아무것도 깨뜨리지 않습니다.** Secret Key 가 없으면 경고만 하고 그대로 진행합니다.
|
|
25
126
|
CLI 가 서버보다 먼저 조이면, 우리가 생성해 드리는 GitHub Actions 워크플로처럼 버전을 고정하지
|
|
26
|
-
않는(`npx --yes connectbase deploy`) 환경이 서버 변경 전에 깨지기 때문입니다.
|
|
127
|
+
않는(`npx --yes connectbase-client deploy`) 환경이 서버 변경 전에 깨지기 때문입니다.
|
|
27
128
|
|
|
28
129
|
**지금 해두실 것.** CI 를 쓰신다면 저장소 시크릿에 `CONNECTBASE_SECRET_KEY` 를 추가하고
|
|
29
130
|
워크플로 env 에 넣으세요. 워크플로 파일에 값을 직접 적으면 저장소에 커밋됩니다.
|
|
@@ -554,7 +655,7 @@ await ctx.cbAdmin.appMembers.remove(ctx.appId, memberId)
|
|
|
554
655
|
```bash
|
|
555
656
|
# 이제 dist 에 두기만 하면 함께 올라간다
|
|
556
657
|
echo "/blog /blog.html 200" > dist/_redirects
|
|
557
|
-
npx connectbase deploy ./dist
|
|
658
|
+
npx connectbase-client deploy ./dist
|
|
558
659
|
```
|
|
559
660
|
|
|
560
661
|
- `_redirects`, `_headers` 만 이름 완전일치로 허용한다. `Dockerfile`·`Makefile`·`LICENSE`
|
|
@@ -1152,7 +1253,7 @@ AI 에이전트(Claude Code 등)가 `init` 을 직접 실행할 수 있도록
|
|
|
1152
1253
|
자동 정리(레거시 fullstack 파일 포함), `deploy` 성공 후 변경이 있을 때만 조용히 자동 갱신
|
|
1153
1254
|
(`--skip-docs` 로 생략 가능).
|
|
1154
1255
|
- **CLAUDE.md @import**: 참조 블록이 `@.claude/docs/project-rules.md` import 로 규칙을 매
|
|
1155
|
-
세션 자동 로드. MCP 미연결 시 `npx connectbase mcp` 안내 지시 포함. `mcp` 명령도 비대화형
|
|
1256
|
+
세션 자동 로드. MCP 미연결 시 `npx connectbase-client mcp` 안내 지시 포함. `mcp` 명령도 비대화형
|
|
1156
1257
|
지원(`--secret-key`/env/.connectbaserc 자동 사용).
|
|
1157
1258
|
|
|
1158
1259
|
### Changed / Security
|
|
@@ -2793,7 +2894,7 @@ ComfyUI × 웹스토리지 같은 e2e 통합 패턴 (작업 제출 → 폴링
|
|
|
2793
2894
|
|
|
2794
2895
|
### Fixed
|
|
2795
2896
|
|
|
2796
|
-
- `npx connectbase docs` 가 인증 없이 곧장 문서를 받도록 수정. 백엔드 `/v1/storages/webs/claude-md`
|
|
2897
|
+
- `npx connectbase-client docs` 가 인증 없이 곧장 문서를 받도록 수정. 백엔드 `/v1/storages/webs/claude-md`
|
|
2797
2898
|
는 public 라우트인데 CLI 가 불필요하게 브라우저 인증 → 앱 선택 → Public Key 발급을 강제하던
|
|
2798
2899
|
흐름을 제거. 캐시된 publicKey 가 있으면 문서에 박아주고, 없으면 백엔드가 placeholder
|
|
2799
2900
|
(`YOUR_PUBLIC_KEY_HERE`) 로 대체해 그대로 다운로드.
|
|
@@ -3225,7 +3326,7 @@ sisun 팀(`019d85c7-...`) 의 운영 피드백 3건이 시작점이며, 백엔
|
|
|
3225
3326
|
|
|
3226
3327
|
## [1.4.2] - 2026-04-18
|
|
3227
3328
|
|
|
3228
|
-
`npx connectbase docs` 명령이 init/deploy/tunnel 과 달리 brower auth 흐름을 거치지 않아, Public Key 가 없는 사용자가 키 발급 경로를 모른 채 prompt 만 보던 UX 문제 해결.
|
|
3329
|
+
`npx connectbase-client docs` 명령이 init/deploy/tunnel 과 달리 brower auth 흐름을 거치지 않아, Public Key 가 없는 사용자가 키 발급 경로를 모른 채 prompt 만 보던 UX 문제 해결.
|
|
3229
3330
|
|
|
3230
3331
|
### Fixed
|
|
3231
3332
|
|
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Connect Base provides **two types** of Keys. Use the right key for your use case
|
|
|
27
27
|
|---------|----------|---------|
|
|
28
28
|
| Frontend SDK (`new ConnectBase()`) | **Public Key** (`cb_pk_`) | Web/app: DB queries, auth, file uploads |
|
|
29
29
|
| `.env` file (`VITE_CONNECTBASE_PUBLIC_KEY`) | **Public Key** (`cb_pk_`) | React, Vue, etc. |
|
|
30
|
-
| CLI deploy (`.connectbaserc`) | **Public Key + Secret Key** (`cb_pk_` + `cb_sk_`) | `npx connectbase deploy` |
|
|
30
|
+
| CLI deploy (`.connectbaserc`) | **Public Key + Secret Key** (`cb_pk_` + `cb_sk_`) | `npx connectbase-client deploy` |
|
|
31
31
|
| MCP server (AI tools) | **Secret Key** (`cb_sk_`) | Claude, Cursor, Windsurf |
|
|
32
32
|
| Server-side admin tasks | **Secret Key** (`cb_sk_`) | Backend full data access |
|
|
33
33
|
|
|
@@ -131,22 +131,27 @@ try {
|
|
|
131
131
|
|
|
132
132
|
Deploy your web application to Connect Base Web Storage with a single command.
|
|
133
133
|
|
|
134
|
-
>
|
|
135
|
-
>
|
|
136
|
-
>
|
|
137
|
-
>
|
|
138
|
-
>
|
|
139
|
-
>
|
|
140
|
-
> **401 `SECRET_KEY_REQUIRED`**.
|
|
134
|
+
> **After `npx`, always use the package name `connectbase-client`.** This package installs two
|
|
135
|
+
> bins — `connectbase` and `connectbase-client` — but `connectbase` is a *bin* name, not a
|
|
136
|
+
> package name. npx checks the local `node_modules/.bin` first and otherwise fetches a
|
|
137
|
+
> **package** by that name, so `npx connectbase init` only works in a project that already has
|
|
138
|
+
> this package installed; in an empty folder it fails with npm 404. A global install does not
|
|
139
|
+
> help — npx does not look at `PATH`.
|
|
141
140
|
>
|
|
142
|
-
>
|
|
143
|
-
>
|
|
141
|
+
> Use the short `connectbase` **after** installing: globally, inside `npm run` scripts, or via
|
|
142
|
+
> `node_modules/.bin`.
|
|
143
|
+
>
|
|
144
|
+
> ```bash
|
|
145
|
+
> npx connectbase-client init # nothing installed yet — package name
|
|
146
|
+
> npm install # installs what init added to devDependencies
|
|
147
|
+
> npm run deploy # runs `connectbase deploy ./dist` — short name is fine here
|
|
148
|
+
> ```
|
|
144
149
|
|
|
145
150
|
### Quick Start
|
|
146
151
|
|
|
147
152
|
```bash
|
|
148
153
|
# 1. Initialize (one-time setup)
|
|
149
|
-
npx connectbase init
|
|
154
|
+
npx connectbase-client init
|
|
150
155
|
|
|
151
156
|
# 2. Deploy
|
|
152
157
|
npm run deploy
|
|
@@ -173,14 +178,14 @@ The `init` command will:
|
|
|
173
178
|
If you prefer not to use `init`, you can pass options directly:
|
|
174
179
|
|
|
175
180
|
```bash
|
|
176
|
-
npx connectbase deploy ./dist -s <storage-id> -k <public-key> --secret-key <secret-key>
|
|
181
|
+
npx connectbase-client deploy ./dist -s <storage-id> -k <public-key> --secret-key <secret-key>
|
|
177
182
|
```
|
|
178
183
|
|
|
179
184
|
The Secret Key can also come from `CONNECTBASE_SECRET_KEY` — preferred in CI, so it never
|
|
180
185
|
appears in shell history or CI logs:
|
|
181
186
|
|
|
182
187
|
```bash
|
|
183
|
-
CONNECTBASE_SECRET_KEY=cb_sk_... npx connectbase deploy ./dist -s <storage-id> -k <public-key>
|
|
188
|
+
CONNECTBASE_SECRET_KEY=cb_sk_... npx connectbase-client deploy ./dist -s <storage-id> -k <public-key>
|
|
184
189
|
```
|
|
185
190
|
|
|
186
191
|
### Options
|
|
@@ -204,14 +209,14 @@ Expose a local server to the internet through a secure WebSocket tunnel. Useful
|
|
|
204
209
|
|
|
205
210
|
```bash
|
|
206
211
|
# Expose local port 8084 to the internet
|
|
207
|
-
npx connectbase tunnel 8084 -k <public-key>
|
|
212
|
+
npx connectbase-client tunnel 8084 -k <public-key>
|
|
208
213
|
|
|
209
214
|
# With environment variable
|
|
210
215
|
export CONNECTBASE_PUBLIC_KEY=your-public-key
|
|
211
|
-
npx connectbase tunnel 8084
|
|
216
|
+
npx connectbase-client tunnel 8084
|
|
212
217
|
|
|
213
218
|
# For GPU servers or long-running tasks (e.g., image generation)
|
|
214
|
-
npx connectbase tunnel 7860 --timeout 300 --max-body 50
|
|
219
|
+
npx connectbase-client tunnel 7860 --timeout 300 --max-body 50
|
|
215
220
|
```
|
|
216
221
|
|
|
217
222
|
The tunnel creates a public URL like `https://tunnel.connectbase.world/<tunnel-id>/` that proxies all HTTP requests to your local service.
|
|
@@ -241,7 +246,7 @@ on the server, so your SDK only needs the Public Key.
|
|
|
241
246
|
|
|
242
247
|
```bash
|
|
243
248
|
# Start ComfyUI on port 8188, expose it as endpoint label "comfyui-main"
|
|
244
|
-
npx connectbase tunnel 8188 --label comfyui-main --description "ComfyUI on my desktop"
|
|
249
|
+
npx connectbase-client tunnel 8188 --label comfyui-main --description "ComfyUI on my desktop"
|
|
245
250
|
```
|
|
246
251
|
|
|
247
252
|
Authentication uses your User Secret Key (`cb_sk_*`); the CLI calls the dual-auth
|
|
@@ -272,14 +277,14 @@ The `init` command creates `.connectbaserc` automatically. You can also create i
|
|
|
272
277
|
export CONNECTBASE_PUBLIC_KEY=cb_pk_your-public-key
|
|
273
278
|
export CONNECTBASE_SECRET_KEY=cb_sk_your-secret-key # required by deploy / promote
|
|
274
279
|
export CONNECTBASE_STORAGE_ID=your-storage-id
|
|
275
|
-
npx connectbase deploy ./dist
|
|
280
|
+
npx connectbase-client deploy ./dist
|
|
276
281
|
```
|
|
277
282
|
|
|
278
283
|
In CI, read the Secret Key from a repository secret:
|
|
279
284
|
|
|
280
285
|
```yaml
|
|
281
286
|
# GitHub Actions
|
|
282
|
-
- run: npx connectbase deploy ./dist
|
|
287
|
+
- run: npx connectbase-client deploy ./dist
|
|
283
288
|
env:
|
|
284
289
|
CONNECTBASE_PUBLIC_KEY: ${{ vars.CONNECTBASE_PUBLIC_KEY }}
|
|
285
290
|
CONNECTBASE_SECRET_KEY: ${{ secrets.CONNECTBASE_SECRET_KEY }}
|
package/dist/cli.js
CHANGED
|
@@ -32,6 +32,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
32
32
|
var cli_exports = {};
|
|
33
33
|
__export(cli_exports, {
|
|
34
34
|
TunnelConnectSelector: () => TunnelConnectSelector,
|
|
35
|
+
apiErrorMessage: () => apiErrorMessage,
|
|
35
36
|
computeDeployDiff: () => computeDeployDiff,
|
|
36
37
|
getTunnelConnectCandidates: () => getTunnelConnectCandidates,
|
|
37
38
|
guessMimeType: () => guessMimeType,
|
|
@@ -147,18 +148,7 @@ function handleTunnelError(msg, appId, localPort) {
|
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
// src/cli.ts
|
|
150
|
-
|
|
151
|
-
try {
|
|
152
|
-
const pkgPath = path2.join(__dirname, "..", "package.json");
|
|
153
|
-
if (fs2.existsSync(pkgPath)) {
|
|
154
|
-
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
155
|
-
return pkg.version || "0.0.0";
|
|
156
|
-
}
|
|
157
|
-
} catch {
|
|
158
|
-
}
|
|
159
|
-
return "3.25.1";
|
|
160
|
-
}
|
|
161
|
-
var VERSION = getPackageVersion();
|
|
151
|
+
var VERSION = "6.3.1" ? "6.3.1" : getPackageVersion();
|
|
162
152
|
var DEFAULT_BASE_URL = "https://api.connectbase.world";
|
|
163
153
|
var colors = {
|
|
164
154
|
reset: "\x1B[0m",
|
|
@@ -311,7 +301,7 @@ function storageAdminHeaders(config) {
|
|
|
311
301
|
);
|
|
312
302
|
log(" \uACE7 \uC11C\uBC84\uAC00 Secret Key \uB97C \uC694\uAD6C\uD569\uB2C8\uB2E4. \uC9C0\uAE08 \uC124\uC815\uD574 \uB450\uC138\uC694.");
|
|
313
303
|
log(
|
|
314
|
-
` ${colors.cyan}npx connectbase init${colors.reset} \uC774 \uBC1C\uAE09\uD574 .connectbaserc \uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.`
|
|
304
|
+
` ${colors.cyan}npx connectbase-client init${colors.reset} \uC774 \uBC1C\uAE09\uD574 .connectbaserc \uC5D0 \uC800\uC7A5\uD569\uB2C8\uB2E4.`
|
|
315
305
|
);
|
|
316
306
|
log(
|
|
317
307
|
` \uC774\uBBF8 \uD0A4\uAC00 \uC788\uB2E4\uBA74 ${colors.cyan}CONNECTBASE_SECRET_KEY${colors.reset} \uD658\uACBD\uBCC0\uC218\uB85C \uC9C0\uC815\uD558\uC138\uC694.`
|
|
@@ -431,9 +421,9 @@ async function requestDeployPresign(baseStorageUrl, headers, files, timeoutMs) {
|
|
|
431
421
|
);
|
|
432
422
|
if (res.status === 404) return null;
|
|
433
423
|
if (res.status < 200 || res.status >= 300) {
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
424
|
+
throw new Error(
|
|
425
|
+
`presign \uC694\uCCAD \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
|
|
426
|
+
);
|
|
437
427
|
}
|
|
438
428
|
const parsed = res.data;
|
|
439
429
|
return parsed?.urls ?? [];
|
|
@@ -591,6 +581,27 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
|
|
|
591
581
|
}
|
|
592
582
|
}
|
|
593
583
|
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
584
|
+
function apiErrorMessage(data, status) {
|
|
585
|
+
const fallback = `HTTP ${status}`;
|
|
586
|
+
if (typeof data === "string") return data.trim() || fallback;
|
|
587
|
+
if (typeof data !== "object" || data === null) return fallback;
|
|
588
|
+
const body = data;
|
|
589
|
+
const str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
|
|
590
|
+
let message;
|
|
591
|
+
let suggestion;
|
|
592
|
+
if (typeof body.error === "object" && body.error !== null) {
|
|
593
|
+
const detail = body.error;
|
|
594
|
+
message = str(detail.message) ?? str(detail.code);
|
|
595
|
+
suggestion = str(detail.suggestion);
|
|
596
|
+
} else {
|
|
597
|
+
message = str(body.message) ?? str(body.error);
|
|
598
|
+
}
|
|
599
|
+
if (!message) {
|
|
600
|
+
const raw = JSON.stringify(body);
|
|
601
|
+
return raw === "{}" ? fallback : `${fallback}: ${raw}`;
|
|
602
|
+
}
|
|
603
|
+
return suggestion ? `${message} (${suggestion})` : message;
|
|
604
|
+
}
|
|
594
605
|
async function makeRequest(url, method, headers, body, reqOpts = {}) {
|
|
595
606
|
return new Promise((resolve2, reject) => {
|
|
596
607
|
const parsedUrl = new URL(url);
|
|
@@ -667,10 +678,10 @@ async function deploy(directory, config, isDev = false, deployOpts = {}) {
|
|
|
667
678
|
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
668
679
|
if (pkg.scripts?.build) {
|
|
669
680
|
warn(
|
|
670
|
-
`\uBC30\uD3EC \uC804 \uBE4C\uB4DC\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: ${colors.cyan}npm run build && npx connectbase deploy${colors.reset}`
|
|
681
|
+
`\uBC30\uD3EC \uC804 \uBE4C\uB4DC\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: ${colors.cyan}npm run build && npx connectbase-client deploy${colors.reset}`
|
|
671
682
|
);
|
|
672
683
|
warn(
|
|
673
|
-
`\uB610\uB294 package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB4F1\uB85D\uD558\uC138\uC694: ${colors.cyan}npx connectbase init${colors.reset}`
|
|
684
|
+
`\uB610\uB294 package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB4F1\uB85D\uD558\uC138\uC694: ${colors.cyan}npx connectbase-client init${colors.reset}`
|
|
674
685
|
);
|
|
675
686
|
}
|
|
676
687
|
} catch {
|
|
@@ -702,7 +713,7 @@ async function deploy(directory, config, isDev = false, deployOpts = {}) {
|
|
|
702
713
|
const envLabel = isDev ? "QA" : "Prod";
|
|
703
714
|
if (isDev) {
|
|
704
715
|
warn(
|
|
705
|
-
`${colors.yellow}--dev \uB294 deprecated${colors.reset} \uC785\uB2C8\uB2E4 \u2014 QA \uCC44\uB110\uB85C \uBC30\uD3EC\uD569\uB2C8\uB2E4(production \uBBF8\uBC18\uC601). \uC774\uD6C4 ${colors.cyan}npx connectbase promote${colors.reset} \uB85C production \uC5D0 \uBC18\uC601\uD558\uC138\uC694. promote_mode=manual \uC774\uBA74 \uC77C\uBC18 ${colors.cyan}deploy${colors.reset} \uB3C4 \uB3D9\uC77C\uD558\uAC8C QA \uB85C \uBC30\uD3EC\uB429\uB2C8\uB2E4.`
|
|
716
|
+
`${colors.yellow}--dev \uB294 deprecated${colors.reset} \uC785\uB2C8\uB2E4 \u2014 QA \uCC44\uB110\uB85C \uBC30\uD3EC\uD569\uB2C8\uB2E4(production \uBBF8\uBC18\uC601). \uC774\uD6C4 ${colors.cyan}npx connectbase-client promote${colors.reset} \uB85C production \uC5D0 \uBC18\uC601\uD558\uC138\uC694. promote_mode=manual \uC774\uBA74 \uC77C\uBC18 ${colors.cyan}deploy${colors.reset} \uB3C4 \uB3D9\uC77C\uD558\uAC8C QA \uB85C \uBC30\uD3EC\uB429\uB2C8\uB2E4.`
|
|
706
717
|
);
|
|
707
718
|
}
|
|
708
719
|
const baseStorageUrl = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}`;
|
|
@@ -779,9 +790,9 @@ async function tryFetchManifest(baseStorageUrl, headers) {
|
|
|
779
790
|
);
|
|
780
791
|
if (res.status === 404) return null;
|
|
781
792
|
if (res.status < 200 || res.status >= 300) {
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
793
|
+
throw new Error(
|
|
794
|
+
`manifest \uC870\uD68C \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
|
|
795
|
+
);
|
|
785
796
|
}
|
|
786
797
|
const parsed = res.data;
|
|
787
798
|
if (!parsed || !Array.isArray(parsed.files)) {
|
|
@@ -918,7 +929,7 @@ ${colors.cyan}Production: ${dep.production_url}${colors.reset}`
|
|
|
918
929
|
log(`
|
|
919
930
|
${colors.yellow}QA URL: ${dep.qa_url}${colors.reset}`);
|
|
920
931
|
log(
|
|
921
|
-
`${colors.dim}\uAC80\uC99D \uD6C4 \uBC18\uC601: ${colors.cyan}npx connectbase promote${colors.reset}
|
|
932
|
+
`${colors.dim}\uAC80\uC99D \uD6C4 \uBC18\uC601: ${colors.cyan}npx connectbase-client promote${colors.reset}
|
|
922
933
|
`
|
|
923
934
|
);
|
|
924
935
|
}
|
|
@@ -931,8 +942,7 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
|
|
|
931
942
|
`);
|
|
932
943
|
}
|
|
933
944
|
} else {
|
|
934
|
-
const
|
|
935
|
-
const errorMsg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : typeof data === "string" ? data : `HTTP ${response.status}`;
|
|
945
|
+
const errorMsg = apiErrorMessage(response.data, response.status);
|
|
936
946
|
if (!reportDeployAuthError(response.status, response.data)) {
|
|
937
947
|
error(`\uBC30\uD3EC \uC2E4\uD328: ${errorMsg}`);
|
|
938
948
|
}
|
|
@@ -961,8 +971,7 @@ async function promote(config, deploymentId) {
|
|
|
961
971
|
process.exit(1);
|
|
962
972
|
}
|
|
963
973
|
if (res.status < 200 || res.status >= 300) {
|
|
964
|
-
const
|
|
965
|
-
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
974
|
+
const msg = apiErrorMessage(res.data, res.status);
|
|
966
975
|
if (!reportDeployAuthError(res.status, res.data)) {
|
|
967
976
|
error(`promote \uC2E4\uD328: ${msg}`);
|
|
968
977
|
}
|
|
@@ -985,8 +994,7 @@ async function listReleases(config) {
|
|
|
985
994
|
headers
|
|
986
995
|
);
|
|
987
996
|
if (res.status < 200 || res.status >= 300) {
|
|
988
|
-
const
|
|
989
|
-
const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
|
|
997
|
+
const msg = apiErrorMessage(res.data, res.status);
|
|
990
998
|
if (!reportDeployAuthError(res.status, res.data)) {
|
|
991
999
|
error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
992
1000
|
}
|
|
@@ -1019,7 +1027,7 @@ ${colors.cyan}\uBC30\uD3EC \uC774\uB825${colors.reset} (promote_mode: ${data.pro
|
|
|
1019
1027
|
if (data.qa_url)
|
|
1020
1028
|
log(`${colors.yellow}QA: ${data.qa_url}${colors.reset}`);
|
|
1021
1029
|
log(
|
|
1022
|
-
`${colors.dim}\uB864\uBC31: npx connectbase promote <deployment_id>${colors.reset}
|
|
1030
|
+
`${colors.dim}\uB864\uBC31: npx connectbase-client promote <deployment_id>${colors.reset}
|
|
1023
1031
|
`
|
|
1024
1032
|
);
|
|
1025
1033
|
}
|
|
@@ -1086,7 +1094,9 @@ function getProjectRoot() {
|
|
|
1086
1094
|
const gitRoot = getGitRoot();
|
|
1087
1095
|
return gitRoot || process.cwd();
|
|
1088
1096
|
}
|
|
1089
|
-
|
|
1097
|
+
function consoleUrlFor(baseUrl) {
|
|
1098
|
+
return baseUrl.replace("api.", "");
|
|
1099
|
+
}
|
|
1090
1100
|
function openBrowser(url) {
|
|
1091
1101
|
const { exec } = require("child_process");
|
|
1092
1102
|
const platform2 = process.platform;
|
|
@@ -1101,21 +1111,21 @@ function openBrowser(url) {
|
|
|
1101
1111
|
exec(command, () => {
|
|
1102
1112
|
});
|
|
1103
1113
|
}
|
|
1104
|
-
async function browserAuthFlow() {
|
|
1114
|
+
async function browserAuthFlow(baseUrl = DEFAULT_BASE_URL) {
|
|
1105
1115
|
info("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D \uC138\uC158\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
1106
1116
|
const startRes = await makeRequest(
|
|
1107
|
-
`${
|
|
1117
|
+
`${baseUrl}/v1/public/cli-auth/start`,
|
|
1108
1118
|
"POST",
|
|
1109
1119
|
{}
|
|
1110
1120
|
);
|
|
1111
1121
|
if (startRes.status !== 200) {
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1122
|
+
error(
|
|
1123
|
+
`\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${apiErrorMessage(startRes.data, startRes.status)}`
|
|
1124
|
+
);
|
|
1115
1125
|
process.exit(1);
|
|
1116
1126
|
}
|
|
1117
1127
|
const { session_id } = startRes.data;
|
|
1118
|
-
const authUrl = `${
|
|
1128
|
+
const authUrl = `${consoleUrlFor(baseUrl)}/auth/cli-auth?session=${session_id}`;
|
|
1119
1129
|
log(`
|
|
1120
1130
|
${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC138\uC694:${colors.reset}`);
|
|
1121
1131
|
log(`${colors.dim}${authUrl}${colors.reset}
|
|
@@ -1135,7 +1145,7 @@ ${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC13
|
|
|
1135
1145
|
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
1136
1146
|
try {
|
|
1137
1147
|
const pollRes = await makeRequest(
|
|
1138
|
-
`${
|
|
1148
|
+
`${baseUrl}/v1/public/cli-auth/poll/${session_id}`,
|
|
1139
1149
|
"GET",
|
|
1140
1150
|
{}
|
|
1141
1151
|
);
|
|
@@ -1182,10 +1192,73 @@ ${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC13
|
|
|
1182
1192
|
error("\uC778\uC99D \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
1183
1193
|
process.exit(1);
|
|
1184
1194
|
}
|
|
1195
|
+
async function promptForSecretKey(baseUrl = DEFAULT_BASE_URL) {
|
|
1196
|
+
log(`
|
|
1197
|
+
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
1198
|
+
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
1199
|
+
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
1200
|
+
`);
|
|
1201
|
+
const authChoice = await prompt(
|
|
1202
|
+
`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `
|
|
1203
|
+
);
|
|
1204
|
+
if (authChoice === "2") {
|
|
1205
|
+
try {
|
|
1206
|
+
return await browserAuthFlow(baseUrl);
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1209
|
+
info(
|
|
1210
|
+
"\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"
|
|
1211
|
+
);
|
|
1212
|
+
process.exit(1);
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
log(
|
|
1216
|
+
`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
1217
|
+
`
|
|
1218
|
+
);
|
|
1219
|
+
const entered = await promptSecret(
|
|
1220
|
+
`${colors.blue}?${colors.reset} Secret Key: `
|
|
1221
|
+
);
|
|
1222
|
+
if (!entered) {
|
|
1223
|
+
error("Secret Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1224
|
+
process.exit(1);
|
|
1225
|
+
}
|
|
1226
|
+
if (entered.startsWith("cb_pk_")) {
|
|
1227
|
+
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
1228
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1229
|
+
process.exit(1);
|
|
1230
|
+
}
|
|
1231
|
+
if (!entered.startsWith("cb_sk_")) {
|
|
1232
|
+
error(
|
|
1233
|
+
"\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"
|
|
1234
|
+
);
|
|
1235
|
+
process.exit(1);
|
|
1236
|
+
}
|
|
1237
|
+
return entered;
|
|
1238
|
+
}
|
|
1239
|
+
function saveSecretKeyToRc(secretKey, dir = process.cwd()) {
|
|
1240
|
+
const rcPath = path2.join(dir, ".connectbaserc");
|
|
1241
|
+
let rcData = {};
|
|
1242
|
+
if (fs2.existsSync(rcPath)) {
|
|
1243
|
+
try {
|
|
1244
|
+
rcData = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1245
|
+
} catch {
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
rcData.secretKey = secretKey;
|
|
1249
|
+
fs2.writeFileSync(rcPath, `${JSON.stringify(rcData, null, 2)}
|
|
1250
|
+
`);
|
|
1251
|
+
}
|
|
1252
|
+
async function fetchAppsWithSecretKey(baseUrl, secretKey) {
|
|
1253
|
+
return makeRequest(`${baseUrl}/v1/public/cli/apps`, "GET", {
|
|
1254
|
+
"X-Public-Key": secretKey
|
|
1255
|
+
});
|
|
1256
|
+
}
|
|
1185
1257
|
async function init(config = {}, opts = {}) {
|
|
1186
1258
|
log(`
|
|
1187
1259
|
${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.reset}
|
|
1188
1260
|
`);
|
|
1261
|
+
const baseUrl = config.baseUrl || DEFAULT_BASE_URL;
|
|
1189
1262
|
const cwd = process.cwd();
|
|
1190
1263
|
const projectRoot = getProjectRoot();
|
|
1191
1264
|
if (cwd !== projectRoot) {
|
|
@@ -1199,7 +1272,7 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
1199
1272
|
if (!process.stdin.isTTY && !nonInteractive) {
|
|
1200
1273
|
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");
|
|
1201
1274
|
log(
|
|
1202
|
-
`${colors.dim}\uC608: npx connectbase init --yes --secret-key cb_sk_... [--app <\uC571ID>] [--storage <\uC2A4\uD1A0\uB9AC\uC9C0ID>] [--deploy-dir ./dist]${colors.reset}`
|
|
1275
|
+
`${colors.dim}\uC608: npx connectbase-client init --yes --secret-key cb_sk_... [--app <\uC571ID>] [--storage <\uC2A4\uD1A0\uB9AC\uC9C0ID>] [--deploy-dir ./dist]${colors.reset}`
|
|
1203
1276
|
);
|
|
1204
1277
|
log(
|
|
1205
1278
|
`${colors.dim}Secret Key\uB294 CONNECTBASE_SECRET_KEY \uD658\uACBD\uBCC0\uC218\uB85C\uB3C4 \uC804\uB2EC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4${colors.reset}`
|
|
@@ -1221,10 +1294,14 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
1221
1294
|
process.exit(1);
|
|
1222
1295
|
}
|
|
1223
1296
|
} else {
|
|
1297
|
+
info(
|
|
1298
|
+
".connectbaserc\uAC00 \uC774\uBBF8 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uAE30\uC874 \uC571/\uD0A4 \uC124\uC815\uC744 \uC7AC\uC0AC\uC6A9\uD574 \uB2E4\uC2DC \uAD6C\uC131\uD569\uB2C8\uB2E4"
|
|
1299
|
+
);
|
|
1224
1300
|
const overwrite = await prompt(
|
|
1225
|
-
`${colors.
|
|
1301
|
+
`${colors.blue}?${colors.reset} \uACC4\uC18D\uD560\uAE4C\uC694? (Y/n): `
|
|
1226
1302
|
);
|
|
1227
|
-
|
|
1303
|
+
const answer = overwrite.toLowerCase();
|
|
1304
|
+
if (answer === "n" || answer === "no") {
|
|
1228
1305
|
info("\uCD08\uAE30\uD654\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4");
|
|
1229
1306
|
return;
|
|
1230
1307
|
}
|
|
@@ -1238,57 +1315,14 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
1238
1315
|
);
|
|
1239
1316
|
process.exit(1);
|
|
1240
1317
|
}
|
|
1241
|
-
info("Secret Key \
|
|
1318
|
+
info("\uC800\uC7A5\uB41C Secret Key \uC0AC\uC6A9 (\uD50C\uB798\uADF8/\uD658\uACBD\uBCC0\uC218/.connectbaserc)");
|
|
1242
1319
|
} else if (nonInteractive) {
|
|
1243
1320
|
error(
|
|
1244
1321
|
"Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: --secret-key cb_sk_... \uB610\uB294 CONNECTBASE_SECRET_KEY \uD658\uACBD\uBCC0\uC218"
|
|
1245
1322
|
);
|
|
1246
1323
|
process.exit(1);
|
|
1247
1324
|
} else {
|
|
1248
|
-
|
|
1249
|
-
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
1250
|
-
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
1251
|
-
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
1252
|
-
`);
|
|
1253
|
-
const authChoice = await prompt(
|
|
1254
|
-
`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `
|
|
1255
|
-
);
|
|
1256
|
-
if (authChoice === "2") {
|
|
1257
|
-
try {
|
|
1258
|
-
secretKey = await browserAuthFlow();
|
|
1259
|
-
} catch (err) {
|
|
1260
|
-
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1261
|
-
info(
|
|
1262
|
-
"\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"
|
|
1263
|
-
);
|
|
1264
|
-
process.exit(1);
|
|
1265
|
-
}
|
|
1266
|
-
} else {
|
|
1267
|
-
log(
|
|
1268
|
-
`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
1269
|
-
`
|
|
1270
|
-
);
|
|
1271
|
-
secretKey = await promptSecret(
|
|
1272
|
-
`${colors.blue}?${colors.reset} Secret Key: `
|
|
1273
|
-
);
|
|
1274
|
-
if (!secretKey) {
|
|
1275
|
-
error("Secret Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1276
|
-
process.exit(1);
|
|
1277
|
-
}
|
|
1278
|
-
if (secretKey.startsWith("cb_pk_")) {
|
|
1279
|
-
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
1280
|
-
info(
|
|
1281
|
-
"Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4"
|
|
1282
|
-
);
|
|
1283
|
-
process.exit(1);
|
|
1284
|
-
}
|
|
1285
|
-
if (!secretKey.startsWith("cb_sk_")) {
|
|
1286
|
-
error(
|
|
1287
|
-
"\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"
|
|
1288
|
-
);
|
|
1289
|
-
process.exit(1);
|
|
1290
|
-
}
|
|
1291
|
-
}
|
|
1325
|
+
secretKey = await promptForSecretKey(baseUrl);
|
|
1292
1326
|
}
|
|
1293
1327
|
let appId = "";
|
|
1294
1328
|
let publicKey = "";
|
|
@@ -1304,7 +1338,7 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
1304
1338
|
const issuePublicKey = async (targetAppId) => {
|
|
1305
1339
|
info("Public Key \uC0DD\uC131 \uC911...");
|
|
1306
1340
|
const newKeyRes = await makeRequest(
|
|
1307
|
-
`${
|
|
1341
|
+
`${baseUrl}/v1/public/cli/apps/${targetAppId}/public-keys`,
|
|
1308
1342
|
"POST",
|
|
1309
1343
|
{ "X-Public-Key": secretKey },
|
|
1310
1344
|
JSON.stringify({})
|
|
@@ -1319,14 +1353,26 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
1319
1353
|
};
|
|
1320
1354
|
try {
|
|
1321
1355
|
info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
1322
|
-
|
|
1323
|
-
`${DEFAULT_BASE_URL}/v1/public/cli/apps`,
|
|
1324
|
-
"GET",
|
|
1325
|
-
{ "X-Public-Key": secretKey }
|
|
1326
|
-
);
|
|
1356
|
+
let appsRes = await fetchAppsWithSecretKey(baseUrl, secretKey);
|
|
1327
1357
|
if (appsRes.status === 401) {
|
|
1328
|
-
|
|
1329
|
-
|
|
1358
|
+
warn("\uC800\uC7A5\uB41C Secret Key\uAC00 \uB354 \uC774\uC0C1 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uB9CC\uB8CC \uB610\uB294 \uCDE8\uC18C)");
|
|
1359
|
+
if (nonInteractive) {
|
|
1360
|
+
error(
|
|
1361
|
+
"\uC0C8 Secret Key\uB97C --secret-key cb_sk_... \uB610\uB294 CONNECTBASE_SECRET_KEY \uB85C \uC804\uB2EC\uD558\uC138\uC694"
|
|
1362
|
+
);
|
|
1363
|
+
process.exit(1);
|
|
1364
|
+
}
|
|
1365
|
+
info("\uC0C8 Secret Key\uB85C \uB2E4\uC2DC \uC778\uC99D\uD569\uB2C8\uB2E4");
|
|
1366
|
+
secretKey = await promptForSecretKey(baseUrl);
|
|
1367
|
+
appsRes = await fetchAppsWithSecretKey(baseUrl, secretKey);
|
|
1368
|
+
if (appsRes.status === 401) {
|
|
1369
|
+
error(
|
|
1370
|
+
"\uC0C8\uB85C \uBC1B\uC740 Secret Key\uB3C4 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uD0A4 \uC0C1\uD0DC\uB97C \uD655\uC778\uD558\uC138\uC694"
|
|
1371
|
+
);
|
|
1372
|
+
process.exit(1);
|
|
1373
|
+
}
|
|
1374
|
+
saveSecretKeyToRc(secretKey, cwd);
|
|
1375
|
+
success("\uC0C8 Secret Key\uB97C .connectbaserc\uC5D0 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4");
|
|
1330
1376
|
}
|
|
1331
1377
|
if (appsRes.status !== 200) {
|
|
1332
1378
|
throw new Error(`HTTP ${appsRes.status}`);
|
|
@@ -1376,7 +1422,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
|
|
|
1376
1422
|
) || projectName;
|
|
1377
1423
|
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
1378
1424
|
const createRes = await makeRequest(
|
|
1379
|
-
`${
|
|
1425
|
+
`${baseUrl}/v1/public/cli/apps`,
|
|
1380
1426
|
"POST",
|
|
1381
1427
|
{ "X-Public-Key": secretKey },
|
|
1382
1428
|
JSON.stringify({ name: appName })
|
|
@@ -1386,8 +1432,9 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
|
|
|
1386
1432
|
process.exit(1);
|
|
1387
1433
|
}
|
|
1388
1434
|
if (createRes.status !== 201) {
|
|
1389
|
-
|
|
1390
|
-
|
|
1435
|
+
error(
|
|
1436
|
+
`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
|
|
1437
|
+
);
|
|
1391
1438
|
process.exit(1);
|
|
1392
1439
|
}
|
|
1393
1440
|
const createData = createRes.data;
|
|
@@ -1431,7 +1478,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
|
|
|
1431
1478
|
try {
|
|
1432
1479
|
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC870\uD68C \uC911...");
|
|
1433
1480
|
const listRes = await makeRequest(
|
|
1434
|
-
`${
|
|
1481
|
+
`${baseUrl}/v1/public/storages/webs`,
|
|
1435
1482
|
"GET",
|
|
1436
1483
|
{
|
|
1437
1484
|
"X-Public-Key": publicKeyForSdk,
|
|
@@ -1471,27 +1518,34 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
|
|
|
1471
1518
|
const name = nonInteractive ? projectName : await prompt(
|
|
1472
1519
|
`${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `
|
|
1473
1520
|
) || projectName;
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1521
|
+
const sameName = storages.find((s) => s.name === name);
|
|
1522
|
+
if (sameName) {
|
|
1523
|
+
storageId = sameName.id;
|
|
1524
|
+
success(
|
|
1525
|
+
`\uAC19\uC740 \uC774\uB984\uC758 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uAC00 \uC774\uBBF8 \uC788\uC5B4 \uC7AC\uC0AC\uC6A9\uD569\uB2C8\uB2E4: ${sameName.name}`
|
|
1526
|
+
);
|
|
1527
|
+
} else {
|
|
1528
|
+
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
1529
|
+
const createRes = await makeRequest(
|
|
1530
|
+
`${baseUrl}/v1/public/storages/webs`,
|
|
1531
|
+
"POST",
|
|
1532
|
+
{
|
|
1533
|
+
"X-Public-Key": publicKeyForSdk,
|
|
1534
|
+
"X-User-Secret-Key": secretKey,
|
|
1535
|
+
Authorization: `Bearer ${secretKey}`
|
|
1536
|
+
},
|
|
1537
|
+
JSON.stringify({ name })
|
|
1489
1538
|
);
|
|
1490
|
-
|
|
1539
|
+
if (createRes.status !== 200) {
|
|
1540
|
+
error(
|
|
1541
|
+
`\uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
|
|
1542
|
+
);
|
|
1543
|
+
process.exit(1);
|
|
1544
|
+
}
|
|
1545
|
+
const createData = createRes.data;
|
|
1546
|
+
storageId = createData.storage_web_id;
|
|
1547
|
+
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
1491
1548
|
}
|
|
1492
|
-
const createData = createRes.data;
|
|
1493
|
-
storageId = createData.storage_web_id;
|
|
1494
|
-
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
1495
1549
|
}
|
|
1496
1550
|
} else {
|
|
1497
1551
|
throw new Error(`HTTP ${listRes.status}`);
|
|
@@ -1529,12 +1583,17 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
|
|
|
1529
1583
|
`);
|
|
1530
1584
|
success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
|
|
1531
1585
|
addToGitignore(".connectbaserc");
|
|
1532
|
-
addDeployScript(deployDir);
|
|
1533
|
-
await setupClaudeCode(secretKey, projectRoot);
|
|
1586
|
+
const { needsInstall } = addDeployScript(deployDir);
|
|
1587
|
+
await setupClaudeCode(secretKey, projectRoot, baseUrl);
|
|
1534
1588
|
log(`
|
|
1535
1589
|
${colors.green}\uCD08\uAE30\uD654 \uC644\uB8CC!${colors.reset}
|
|
1536
1590
|
`);
|
|
1537
1591
|
log(`${colors.dim}\uBC30\uD3EC\uD558\uB824\uBA74:${colors.reset}`);
|
|
1592
|
+
if (needsInstall) {
|
|
1593
|
+
log(
|
|
1594
|
+
` ${colors.cyan}npm install${colors.reset} ${colors.dim}# connectbase-client (CLI \uD3EC\uD568) \uC124\uCE58${colors.reset}`
|
|
1595
|
+
);
|
|
1596
|
+
}
|
|
1538
1597
|
log(` ${colors.cyan}npm run deploy${colors.reset}`);
|
|
1539
1598
|
log(`
|
|
1540
1599
|
${colors.dim}Claude Code\uC5D0\uC11C MCP\uAC00 \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4${colors.reset}
|
|
@@ -1589,31 +1648,46 @@ function addDeployScript(deployDir) {
|
|
|
1589
1648
|
const pkgPath = path2.join(process.cwd(), "package.json");
|
|
1590
1649
|
if (!fs2.existsSync(pkgPath)) {
|
|
1591
1650
|
warn("package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4");
|
|
1592
|
-
return;
|
|
1651
|
+
return { needsInstall: false };
|
|
1593
1652
|
}
|
|
1594
1653
|
try {
|
|
1595
1654
|
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
1596
1655
|
if (!pkg.scripts) pkg.scripts = {};
|
|
1656
|
+
const declared = (name) => Boolean(pkg.dependencies?.[name] || pkg.devDependencies?.[name]);
|
|
1657
|
+
let needsInstall = false;
|
|
1658
|
+
if (!declared("connectbase-client")) {
|
|
1659
|
+
if (!pkg.devDependencies) pkg.devDependencies = {};
|
|
1660
|
+
pkg.devDependencies["connectbase-client"] = VERSION === "0.0.0" ? "latest" : `^${VERSION}`;
|
|
1661
|
+
needsInstall = true;
|
|
1662
|
+
}
|
|
1597
1663
|
if (pkg.scripts.deploy) {
|
|
1598
1664
|
info(
|
|
1599
1665
|
`package.json\uC5D0 \uC774\uBBF8 deploy \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: "${pkg.scripts.deploy}"`
|
|
1600
1666
|
);
|
|
1601
|
-
|
|
1667
|
+
} else {
|
|
1668
|
+
const deployCmd = `connectbase deploy ${deployDir}`;
|
|
1669
|
+
pkg.scripts.deploy = pkg.scripts.build ? `npm run build && ${deployCmd}` : deployCmd;
|
|
1670
|
+
success("package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC");
|
|
1602
1671
|
}
|
|
1603
|
-
const deployCmd = `connectbase deploy ${deployDir}`;
|
|
1604
|
-
pkg.scripts.deploy = pkg.scripts.build ? `${pkg.scripts.build} && ${deployCmd}` : deployCmd;
|
|
1605
1672
|
fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
|
|
1606
1673
|
`);
|
|
1607
|
-
|
|
1674
|
+
if (needsInstall) {
|
|
1675
|
+
success(
|
|
1676
|
+
`package.json devDependencies\uC5D0 connectbase-client@${pkg.devDependencies["connectbase-client"]} \uCD94\uAC00 \uC644\uB8CC`
|
|
1677
|
+
);
|
|
1678
|
+
}
|
|
1679
|
+
return { needsInstall };
|
|
1608
1680
|
} catch {
|
|
1609
1681
|
warn("package.json \uC218\uC815\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4");
|
|
1682
|
+
return { needsInstall: false };
|
|
1610
1683
|
}
|
|
1611
1684
|
}
|
|
1612
1685
|
function getGitRoot() {
|
|
1613
1686
|
try {
|
|
1614
1687
|
const { execSync } = require("child_process");
|
|
1615
1688
|
return execSync("git rev-parse --show-toplevel", {
|
|
1616
|
-
encoding: "utf-8"
|
|
1689
|
+
encoding: "utf-8",
|
|
1690
|
+
stdio: ["ignore", "pipe", "ignore"]
|
|
1617
1691
|
}).trim();
|
|
1618
1692
|
} catch {
|
|
1619
1693
|
return null;
|
|
@@ -1715,6 +1789,7 @@ var LEGACY_DOC_FILES = [
|
|
|
1715
1789
|
];
|
|
1716
1790
|
async function downloadDocs(baseDir, opts = {}) {
|
|
1717
1791
|
const quiet = opts.quiet === true;
|
|
1792
|
+
const baseUrl = opts.baseUrl || DEFAULT_BASE_URL;
|
|
1718
1793
|
if (!baseDir) {
|
|
1719
1794
|
baseDir = getProjectRoot();
|
|
1720
1795
|
}
|
|
@@ -1736,7 +1811,7 @@ async function downloadDocs(baseDir, opts = {}) {
|
|
|
1736
1811
|
if (!quiet) info("SDK \uAC1C\uBC1C \uAC00\uC774\uB4DC \uB2E4\uC6B4\uB85C\uB4DC \uC911...");
|
|
1737
1812
|
try {
|
|
1738
1813
|
const res = await makeRequest(
|
|
1739
|
-
`${
|
|
1814
|
+
`${baseUrl}/v1/storages/webs/claude-md?template=rules&format=sections`,
|
|
1740
1815
|
"GET",
|
|
1741
1816
|
{}
|
|
1742
1817
|
);
|
|
@@ -1769,7 +1844,7 @@ async function downloadDocs(baseDir, opts = {}) {
|
|
|
1769
1844
|
}
|
|
1770
1845
|
return "unchanged";
|
|
1771
1846
|
}
|
|
1772
|
-
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 -->
|
|
1847
|
+
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-client update'\uB85C \uAC31\uC2E0\uD558\uC138\uC694 -->
|
|
1773
1848
|
|
|
1774
1849
|
`;
|
|
1775
1850
|
for (const section of sections) {
|
|
@@ -1849,7 +1924,7 @@ function updateRootClaudeMd(claudeMdPath, quiet = false) {
|
|
|
1849
1924
|
|
|
1850
1925
|
1. \uC704\uC5D0 import\uB41C project-rules.md\uC758 \uAE30\uB2A5 \uB9E4\uD551\uD45C\xB7\uBCF4\uC548 \uADDC\uCE59\uC744 \uB530\uB974\uC138\uC694
|
|
1851
1926
|
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
|
|
1852
|
-
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)
|
|
1927
|
+
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-client mcp\` \uC2E4\uD589\uC744 \uC548\uB0B4\uD558\uC138\uC694 (Secret Key \uD544\uC694)
|
|
1853
1928
|
${endMarker}`;
|
|
1854
1929
|
if (fs2.existsSync(claudeMdPath)) {
|
|
1855
1930
|
let content = fs2.readFileSync(claudeMdPath, "utf-8");
|
|
@@ -1885,7 +1960,7 @@ function createSubPackageClaudeMd(subClaudeMdPath, gitRoot, quiet = false) {
|
|
|
1885
1960
|
|
|
1886
1961
|
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
|
|
1887
1962
|
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
|
|
1888
|
-
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
|
|
1963
|
+
3. \`search_sdk_docs\` \uB3C4\uAD6C\uAC00 \uBCF4\uC774\uC9C0 \uC54A\uC73C\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \`npx connectbase-client mcp\` \uC2E4\uD589\uC744 \uC548\uB0B4\uD558\uC138\uC694
|
|
1889
1964
|
${endMarker}`;
|
|
1890
1965
|
if (fs2.existsSync(subClaudeMdPath)) {
|
|
1891
1966
|
let content = fs2.readFileSync(subClaudeMdPath, "utf-8");
|
|
@@ -1913,7 +1988,7 @@ ${subBlock}
|
|
|
1913
1988
|
}
|
|
1914
1989
|
if (!quiet) success(`\uC11C\uBE0C \uD328\uD0A4\uC9C0 CLAUDE.md \uC0DD\uC131 \uC644\uB8CC: ${subClaudeMdPath}`);
|
|
1915
1990
|
}
|
|
1916
|
-
async function setupMcp(secretKey) {
|
|
1991
|
+
async function setupMcp(secretKey, baseUrl = DEFAULT_BASE_URL) {
|
|
1917
1992
|
const gitRoot = getGitRoot();
|
|
1918
1993
|
const root = gitRoot || process.cwd();
|
|
1919
1994
|
const monorepo = detectMonorepo(root);
|
|
@@ -1924,51 +1999,31 @@ async function setupMcp(secretKey) {
|
|
|
1924
1999
|
}
|
|
1925
2000
|
}
|
|
1926
2001
|
const mcpConfigPath = path2.join(root, ".mcp.json");
|
|
2002
|
+
if (secretKey) {
|
|
2003
|
+
const check = await fetchAppsWithSecretKey(baseUrl, secretKey).catch(
|
|
2004
|
+
() => null
|
|
2005
|
+
);
|
|
2006
|
+
if (check?.status === 401) {
|
|
2007
|
+
warn("\uC800\uC7A5\uB41C Secret Key\uAC00 \uB354 \uC774\uC0C1 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4 (\uB9CC\uB8CC \uB610\uB294 \uCDE8\uC18C)");
|
|
2008
|
+
secretKey = "";
|
|
2009
|
+
}
|
|
2010
|
+
}
|
|
1927
2011
|
if (!secretKey) {
|
|
1928
2012
|
if (!process.stdin.isTTY) {
|
|
1929
|
-
error("Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (\uBE44\uB300\uD654\uD615 \uD658\uACBD)");
|
|
2013
|
+
error("\uC720\uD6A8\uD55C Secret Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4 (\uBE44\uB300\uD654\uD615 \uD658\uACBD)");
|
|
1930
2014
|
log(`${colors.dim}\uB2E4\uC74C \uC911 \uD558\uB098\uB85C \uD0A4\uB97C \uC804\uB2EC\uD558\uC138\uC694:${colors.reset}`);
|
|
1931
2015
|
log(
|
|
1932
|
-
` ${colors.cyan}npx connectbase mcp --secret-key cb_sk_...${colors.reset}`
|
|
2016
|
+
` ${colors.cyan}npx connectbase-client mcp --secret-key cb_sk_...${colors.reset}`
|
|
1933
2017
|
);
|
|
1934
2018
|
log(
|
|
1935
|
-
` ${colors.cyan}CONNECTBASE_SECRET_KEY=cb_sk_... npx connectbase mcp${colors.reset}`
|
|
2019
|
+
` ${colors.cyan}CONNECTBASE_SECRET_KEY=cb_sk_... npx connectbase-client mcp${colors.reset}`
|
|
1936
2020
|
);
|
|
1937
2021
|
process.exit(1);
|
|
1938
2022
|
}
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
`);
|
|
1944
|
-
const authChoice = await prompt(
|
|
1945
|
-
`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `
|
|
1946
|
-
);
|
|
1947
|
-
if (authChoice === "2") {
|
|
1948
|
-
try {
|
|
1949
|
-
secretKey = await browserAuthFlow();
|
|
1950
|
-
const skRcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
1951
|
-
let rcData = {};
|
|
1952
|
-
if (fs2.existsSync(skRcPath)) {
|
|
1953
|
-
try {
|
|
1954
|
-
rcData = JSON.parse(fs2.readFileSync(skRcPath, "utf-8"));
|
|
1955
|
-
} catch {
|
|
1956
|
-
}
|
|
1957
|
-
}
|
|
1958
|
-
rcData.secretKey = secretKey;
|
|
1959
|
-
fs2.writeFileSync(skRcPath, `${JSON.stringify(rcData, null, 2)}
|
|
1960
|
-
`);
|
|
1961
|
-
addToGitignore(".connectbaserc", root);
|
|
1962
|
-
success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
|
|
1963
|
-
} catch (err) {
|
|
1964
|
-
error(`\uC778\uC99D \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
1965
|
-
process.exit(1);
|
|
1966
|
-
}
|
|
1967
|
-
} else {
|
|
1968
|
-
secretKey = await promptSecret(
|
|
1969
|
-
`${colors.blue}?${colors.reset} Secret Key: `
|
|
1970
|
-
);
|
|
1971
|
-
}
|
|
2023
|
+
secretKey = await promptForSecretKey(baseUrl);
|
|
2024
|
+
saveSecretKeyToRc(secretKey);
|
|
2025
|
+
addToGitignore(".connectbaserc", root);
|
|
2026
|
+
success("Secret Key\uB97C .connectbaserc\uC5D0 \uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4");
|
|
1972
2027
|
}
|
|
1973
2028
|
const mcpEntry = {
|
|
1974
2029
|
type: "http",
|
|
@@ -2007,10 +2062,10 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
2007
2062
|
${colors.dim}Claude Code\uC5D0\uC11C ConnectBase MCP \uB3C4\uAD6C\uB97C \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.${colors.reset}`
|
|
2008
2063
|
);
|
|
2009
2064
|
}
|
|
2010
|
-
async function setupClaudeCode(secretKey, projectRoot) {
|
|
2065
|
+
async function setupClaudeCode(secretKey, projectRoot, baseUrl = DEFAULT_BASE_URL) {
|
|
2011
2066
|
const root = projectRoot || getProjectRoot();
|
|
2012
|
-
await downloadDocs(root);
|
|
2013
|
-
await setupMcp(secretKey);
|
|
2067
|
+
await downloadDocs(root, { baseUrl });
|
|
2068
|
+
await setupMcp(secretKey, baseUrl);
|
|
2014
2069
|
}
|
|
2015
2070
|
async function fetchLatestVersion(packageName) {
|
|
2016
2071
|
try {
|
|
@@ -2118,7 +2173,7 @@ ${colors.cyan}ConnectBase Update${colors.reset}
|
|
|
2118
2173
|
if (!opts.skipDocs) {
|
|
2119
2174
|
log("");
|
|
2120
2175
|
info("SDK \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uC911...");
|
|
2121
|
-
await downloadDocs(projectRoot);
|
|
2176
|
+
await downloadDocs(projectRoot, { baseUrl: config.baseUrl });
|
|
2122
2177
|
}
|
|
2123
2178
|
if (!opts.skipMcp) {
|
|
2124
2179
|
log("");
|
|
@@ -2203,15 +2258,15 @@ function setupMonorepoRoot(projectRoot, monorepo) {
|
|
|
2203
2258
|
if (!pkg.scripts) pkg.scripts = {};
|
|
2204
2259
|
let changed = false;
|
|
2205
2260
|
if (!pkg.scripts["cb:update"]) {
|
|
2206
|
-
pkg.scripts["cb:update"] = "npx connectbase update";
|
|
2261
|
+
pkg.scripts["cb:update"] = "npx connectbase-client update";
|
|
2207
2262
|
changed = true;
|
|
2208
2263
|
}
|
|
2209
2264
|
if (!pkg.scripts["cb:docs"]) {
|
|
2210
|
-
pkg.scripts["cb:docs"] = "npx connectbase docs";
|
|
2265
|
+
pkg.scripts["cb:docs"] = "npx connectbase-client docs";
|
|
2211
2266
|
changed = true;
|
|
2212
2267
|
}
|
|
2213
2268
|
if (!pkg.scripts["cb:mcp"]) {
|
|
2214
|
-
pkg.scripts["cb:mcp"] = "npx connectbase mcp";
|
|
2269
|
+
pkg.scripts["cb:mcp"] = "npx connectbase-client mcp";
|
|
2215
2270
|
changed = true;
|
|
2216
2271
|
}
|
|
2217
2272
|
if (changed) {
|
|
@@ -2636,8 +2691,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `
|
|
|
2636
2691
|
process.exit(1);
|
|
2637
2692
|
}
|
|
2638
2693
|
if (createRes.status !== 201) {
|
|
2639
|
-
|
|
2640
|
-
error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
|
|
2694
|
+
error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`);
|
|
2641
2695
|
process.exit(1);
|
|
2642
2696
|
}
|
|
2643
2697
|
const createData = createRes.data;
|
|
@@ -3403,7 +3457,7 @@ function showHelp() {
|
|
|
3403
3457
|
${colors.cyan}connectbase${colors.reset} - Connect Base SDK & CLI
|
|
3404
3458
|
|
|
3405
3459
|
${colors.yellow}\uC0AC\uC6A9\uBC95:${colors.reset}
|
|
3406
|
-
npx connectbase <command> [options]
|
|
3460
|
+
npx connectbase-client <command> [options]
|
|
3407
3461
|
|
|
3408
3462
|
${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
|
|
3409
3463
|
init \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 (\uC571 \uC0DD\uC131, MCP \uC124\uC815, SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC)
|
|
@@ -3451,36 +3505,36 @@ ${colors.yellow}\uC635\uC158:${colors.reset}
|
|
|
3451
3505
|
|
|
3452
3506
|
${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
3453
3507
|
${colors.dim}# 1. \uCD08\uAE30\uD654 (\uCD5C\uCD08 1\uD68C)${colors.reset}
|
|
3454
|
-
npx connectbase init
|
|
3508
|
+
npx connectbase-client init
|
|
3455
3509
|
|
|
3456
3510
|
${colors.dim}# 1-1. \uBE44\uB300\uD654\uD615 \uCD08\uAE30\uD654 (CI/AI \uC5D0\uC774\uC804\uD2B8)${colors.reset}
|
|
3457
|
-
npx connectbase init --yes --secret-key cb_sk_...
|
|
3511
|
+
npx connectbase-client init --yes --secret-key cb_sk_...
|
|
3458
3512
|
|
|
3459
3513
|
${colors.dim}# 2. SDK \uC804\uCCB4 \uC5C5\uB370\uC774\uD2B8 (\uBC84\uC804 \uCCB4\uD06C + \uBB38\uC11C + MCP)${colors.reset}
|
|
3460
|
-
npx connectbase update
|
|
3514
|
+
npx connectbase-client update
|
|
3461
3515
|
|
|
3462
3516
|
${colors.dim}# 2-1. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8 \uC124\uCE58${colors.reset}
|
|
3463
|
-
npx connectbase update --setup-root
|
|
3517
|
+
npx connectbase-client update --setup-root
|
|
3464
3518
|
|
|
3465
3519
|
${colors.dim}# 3. SDK \uBB38\uC11C\uB9CC \uC5C5\uB370\uC774\uD2B8${colors.reset}
|
|
3466
|
-
npx connectbase docs
|
|
3520
|
+
npx connectbase-client docs
|
|
3467
3521
|
|
|
3468
3522
|
${colors.dim}# 4. MCP \uC11C\uBC84 \uC124\uC815${colors.reset}
|
|
3469
|
-
npx connectbase mcp
|
|
3523
|
+
npx connectbase-client mcp
|
|
3470
3524
|
|
|
3471
3525
|
${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
|
|
3472
3526
|
npm run deploy
|
|
3473
3527
|
|
|
3474
3528
|
${colors.dim}# 4-1. QA \uC6CC\uD06C\uD50C\uB85C\uC6B0 (\uC2E0\uADDC \uC2A4\uD1A0\uB9AC\uC9C0 \uAE30\uBCF8 promote_mode=manual)${colors.reset}
|
|
3475
|
-
npx connectbase deploy ./dist ${colors.dim}# QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC${colors.reset}
|
|
3476
|
-
npx connectbase releases ${colors.dim}# \uBC30\uD3EC \uC774\uB825 \uD655\uC778${colors.reset}
|
|
3477
|
-
npx connectbase promote ${colors.dim}# QA \uAC80\uC99D \uD6C4 production \uBC18\uC601${colors.reset}
|
|
3529
|
+
npx connectbase-client deploy ./dist ${colors.dim}# QA \uD658\uACBD\uC5D0\uB9CC \uBC30\uD3EC${colors.reset}
|
|
3530
|
+
npx connectbase-client releases ${colors.dim}# \uBC30\uD3EC \uC774\uB825 \uD655\uC778${colors.reset}
|
|
3531
|
+
npx connectbase-client promote ${colors.dim}# QA \uAC80\uC99D \uD6C4 production \uBC18\uC601${colors.reset}
|
|
3478
3532
|
|
|
3479
3533
|
${colors.dim}# 5. \uD130\uB110 (\uAE30\uBCF8)${colors.reset}
|
|
3480
|
-
npx connectbase tunnel 3000
|
|
3534
|
+
npx connectbase-client tunnel 3000
|
|
3481
3535
|
|
|
3482
3536
|
${colors.dim}# 6. \uD130\uB110 (GPU \uC11C\uBC84 \uB4F1 \uAE34 \uC751\uB2F5 \uC2DC)${colors.reset}
|
|
3483
|
-
npx connectbase tunnel 7860 --timeout 300 --max-body 50
|
|
3537
|
+
npx connectbase-client tunnel 7860 --timeout 300 --max-body 50
|
|
3484
3538
|
|
|
3485
3539
|
${colors.yellow}\uD658\uACBD\uBCC0\uC218:${colors.reset}
|
|
3486
3540
|
CONNECTBASE_PUBLIC_KEY Public Key (cb_pk_*)
|
|
@@ -3594,20 +3648,20 @@ async function main() {
|
|
|
3594
3648
|
setupRoot: parsed.options.setupRoot === "true"
|
|
3595
3649
|
});
|
|
3596
3650
|
} else if (parsed.command === "docs") {
|
|
3597
|
-
await downloadDocs();
|
|
3651
|
+
await downloadDocs(void 0, { baseUrl: config.baseUrl });
|
|
3598
3652
|
} else if (parsed.command === "mcp") {
|
|
3599
|
-
await setupMcp(config.secretKey);
|
|
3653
|
+
await setupMcp(config.secretKey, config.baseUrl);
|
|
3600
3654
|
} else if (parsed.command === "deploy") {
|
|
3601
3655
|
const directory = parsed.args[0] || fileConfig.deployDir || ".";
|
|
3602
3656
|
if (!config.publicKey) {
|
|
3603
3657
|
error(
|
|
3604
|
-
'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'
|
|
3658
|
+
'Public Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase-client init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -k \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694'
|
|
3605
3659
|
);
|
|
3606
3660
|
process.exit(1);
|
|
3607
3661
|
}
|
|
3608
3662
|
if (!config.storageId) {
|
|
3609
3663
|
error(
|
|
3610
|
-
'\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'
|
|
3664
|
+
'\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase-client init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694'
|
|
3611
3665
|
);
|
|
3612
3666
|
process.exit(1);
|
|
3613
3667
|
}
|
|
@@ -3628,7 +3682,10 @@ async function main() {
|
|
|
3628
3682
|
if (fs2.existsSync(
|
|
3629
3683
|
path2.join(docsRoot, ".claude", "docs", DOCS_MANIFEST_FILE)
|
|
3630
3684
|
)) {
|
|
3631
|
-
const refreshed = await downloadDocs(docsRoot, {
|
|
3685
|
+
const refreshed = await downloadDocs(docsRoot, {
|
|
3686
|
+
quiet: true,
|
|
3687
|
+
baseUrl: config.baseUrl
|
|
3688
|
+
});
|
|
3632
3689
|
if (refreshed === "updated") {
|
|
3633
3690
|
info("SDK \uBB38\uC11C\uAC00 \uC0C8 \uBC84\uC804\uC73C\uB85C \uAC31\uC2E0\uB418\uC5C8\uC2B5\uB2C8\uB2E4 (.claude/docs/)");
|
|
3634
3691
|
}
|
|
@@ -3639,13 +3696,13 @@ async function main() {
|
|
|
3639
3696
|
} else if (parsed.command === "promote" || parsed.command === "releases") {
|
|
3640
3697
|
if (!config.publicKey) {
|
|
3641
3698
|
error(
|
|
3642
|
-
'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'
|
|
3699
|
+
'Public Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase-client init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -k \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694'
|
|
3643
3700
|
);
|
|
3644
3701
|
process.exit(1);
|
|
3645
3702
|
}
|
|
3646
3703
|
if (!config.storageId) {
|
|
3647
3704
|
error(
|
|
3648
|
-
'\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'
|
|
3705
|
+
'\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase-client init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694'
|
|
3649
3706
|
);
|
|
3650
3707
|
process.exit(1);
|
|
3651
3708
|
}
|
|
@@ -3657,7 +3714,7 @@ async function main() {
|
|
|
3657
3714
|
} else if (parsed.command === "tunnel") {
|
|
3658
3715
|
const portStr = parsed.args[0];
|
|
3659
3716
|
if (!portStr) {
|
|
3660
|
-
error("\uD3EC\uD2B8 \uBC88\uD638\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: npx connectbase tunnel 8084");
|
|
3717
|
+
error("\uD3EC\uD2B8 \uBC88\uD638\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: npx connectbase-client tunnel 8084");
|
|
3661
3718
|
process.exit(1);
|
|
3662
3719
|
}
|
|
3663
3720
|
const port = parseInt(portStr, 10);
|
|
@@ -3713,6 +3770,7 @@ main().catch((err) => {
|
|
|
3713
3770
|
// Annotate the CommonJS export names for ESM import in node:
|
|
3714
3771
|
0 && (module.exports = {
|
|
3715
3772
|
TunnelConnectSelector,
|
|
3773
|
+
apiErrorMessage,
|
|
3716
3774
|
computeDeployDiff,
|
|
3717
3775
|
getTunnelConnectCandidates,
|
|
3718
3776
|
guessMimeType,
|