connectbase-client 1.4.1 → 1.4.2

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.
Files changed (3) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/dist/cli.js +72 -15
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,20 @@
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
+ ## [1.4.2] - 2026-04-18
7
+
8
+ `npx connectbase docs` 명령이 init/deploy/tunnel 과 달리 brower auth 흐름을 거치지 않아, Public Key 가 없는 사용자가 키 발급 경로를 모른 채 prompt 만 보던 UX 문제 해결.
9
+
10
+ ### Fixed
11
+
12
+ - **`docs` 가 키 없을 때 자동 발급 흐름을 트리거**: `.connectbaserc.publicKey` → `secretKey` 가 있으면 앱 선택만, 없으면 `browserAuthFlow()` → 앱 선택/생성 → Public Key 신규 발급 → `.connectbaserc` 저장까지 자동 수행. 한 번 실행하면 다음부터는 추가 입력 없이 통과 (`cli.ts` `ensureDocsPublicKey`).
13
+ - **`tunnelAppId` 캐시 공유**: docs 와 tunnel 이 동일한 `.connectbaserc.tunnelAppId` 를 사용하므로 한쪽에서 앱을 선택하면 다른 쪽에서도 재사용된다.
14
+ - **죽은 fallback 정리**: `config.publicKey ?? config.publicKey` (자기 자신 fallback), `!config.publicKey && !config.publicKey` (동일 변수 두 번 검사) 제거.
15
+
16
+ ### Internal
17
+
18
+ - `resolveAppForTunnel` → `resolveApp` 으로 일반화. 새 앱 생성 시 백엔드(`POST /v1/public/cli/apps`) 응답에 포함된 `public_key` 를 같이 반환하도록 시그니처 확장. tunnel 호출부는 동작 동일.
19
+
6
20
  ## [1.4.1] - 2026-04-18
7
21
 
8
22
  `connectbase tunnel` CLI 가 공개 URL 호출에 필요한 **proxy token** 을 표시하지 않아, 사용자가 토큰을 알 방법이 없어 모든 요청이 `401 invalid or missing proxy token` 으로 막히던 문제 해결.
package/dist/cli.js CHANGED
@@ -845,6 +845,70 @@ function detectMonorepo(gitRoot) {
845
845
  }
846
846
  return result;
847
847
  }
848
+ async function ensureDocsPublicKey(config) {
849
+ if (config.publicKey) return config.publicKey;
850
+ const rcPath = path2.join(process.cwd(), ".connectbaserc");
851
+ const baseUrl = config.baseUrl || DEFAULT_BASE_URL;
852
+ const readRc = () => {
853
+ if (!fs2.existsSync(rcPath)) return {};
854
+ try {
855
+ return JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
856
+ } catch {
857
+ return {};
858
+ }
859
+ };
860
+ const writeRc = (data) => {
861
+ fs2.writeFileSync(rcPath, JSON.stringify(data, null, 2) + "\n");
862
+ addToGitignore(".connectbaserc");
863
+ };
864
+ let secretKey = config.secretKey;
865
+ if (!secretKey) {
866
+ info("Public Key \uBC1C\uAE09\uC744 \uC704\uD574 \uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4...");
867
+ secretKey = await browserAuthFlow();
868
+ const rc2 = readRc();
869
+ rc2.secretKey = secretKey;
870
+ writeRc(rc2);
871
+ config.secretKey = secretKey;
872
+ }
873
+ const savedAppId = readRc().tunnelAppId || "";
874
+ const resolved = await resolveApp(secretKey, baseUrl, savedAppId);
875
+ let publicKey = resolved.publicKey;
876
+ if (!publicKey) {
877
+ info("Public Key \uBC1C\uAE09 \uC911...");
878
+ const res = await makeRequest(
879
+ `${baseUrl}/v1/public/cli/apps/${resolved.appId}/public-keys`,
880
+ "POST",
881
+ { "X-Public-Key": secretKey },
882
+ JSON.stringify({ name: "CLI Docs Key" })
883
+ );
884
+ if (res.status !== 201) {
885
+ const data2 = res.data;
886
+ const detail = data2?.error || data2?.message || `HTTP ${res.status}`;
887
+ error(`Public Key \uBC1C\uAE09 \uC2E4\uD328: ${detail}`);
888
+ process.exit(1);
889
+ }
890
+ const data = res.data;
891
+ if (!data.key) {
892
+ error("Public Key \uBC1C\uAE09 \uC751\uB2F5\uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4");
893
+ process.exit(1);
894
+ }
895
+ publicKey = data.key;
896
+ success("Public Key \uBC1C\uAE09 \uC644\uB8CC");
897
+ }
898
+ const rc = readRc();
899
+ let changed = false;
900
+ if (rc.publicKey !== publicKey) {
901
+ rc.publicKey = publicKey;
902
+ changed = true;
903
+ }
904
+ if (rc.tunnelAppId !== resolved.appId) {
905
+ rc.tunnelAppId = resolved.appId;
906
+ changed = true;
907
+ }
908
+ if (changed) writeRc(rc);
909
+ config.publicKey = publicKey;
910
+ return publicKey;
911
+ }
848
912
  async function downloadDocs(publicKey, templates, baseDir) {
849
913
  if (!baseDir) {
850
914
  baseDir = getProjectRoot();
@@ -1391,9 +1455,9 @@ function getTunnelServerUrl(baseUrl) {
1391
1455
  }
1392
1456
  return baseUrl.replace(/:\d+/, ":8090");
1393
1457
  }
1394
- async function resolveAppForTunnel(secretKey, baseUrl, appIdOption) {
1458
+ async function resolveApp(secretKey, baseUrl, appIdOption) {
1395
1459
  if (appIdOption) {
1396
- return appIdOption;
1460
+ return { appId: appIdOption };
1397
1461
  }
1398
1462
  let apps = [];
1399
1463
  try {
@@ -1418,7 +1482,7 @@ async function resolveAppForTunnel(secretKey, baseUrl, appIdOption) {
1418
1482
  }
1419
1483
  if (apps.length === 1) {
1420
1484
  success(`\uC571 \uC790\uB3D9 \uC120\uD0DD: ${apps[0].name}`);
1421
- return apps[0].id;
1485
+ return { appId: apps[0].id };
1422
1486
  }
1423
1487
  if (apps.length > 0) {
1424
1488
  log(`
@@ -1434,7 +1498,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
1434
1498
  const num = parseInt(choice, 10);
1435
1499
  if (num > 0 && num <= apps.length) {
1436
1500
  success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
1437
- return apps[num - 1].id;
1501
+ return { appId: apps[num - 1].id };
1438
1502
  }
1439
1503
  const projectName = path2.basename(process.cwd());
1440
1504
  const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
@@ -1456,7 +1520,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
1456
1520
  }
1457
1521
  const createData = createRes.data;
1458
1522
  success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
1459
- return createData.app_id;
1523
+ return { appId: createData.app_id, publicKey: createData.public_key };
1460
1524
  }
1461
1525
  function acquireTunnelLock2(appID, port, force) {
1462
1526
  const result = acquireTunnelLock(appID, port, force, VERSION);
@@ -1505,7 +1569,7 @@ async function startTunnel(port, config, tunnelOpts) {
1505
1569
  } catch {
1506
1570
  }
1507
1571
  }
1508
- const appId = await resolveAppForTunnel(tunnelKey, config.baseUrl, savedAppId);
1572
+ const { appId } = await resolveApp(tunnelKey, config.baseUrl, savedAppId);
1509
1573
  try {
1510
1574
  let rcData = {};
1511
1575
  if (fs2.existsSync(rcPath)) {
@@ -1978,20 +2042,13 @@ async function main() {
1978
2042
  setupRoot: parsed.options.setupRoot === "true"
1979
2043
  });
1980
2044
  } else if (parsed.command === "docs") {
1981
- let docsPublicKey = config.publicKey ?? config.publicKey;
1982
- if (!docsPublicKey) {
1983
- docsPublicKey = await prompt(`${colors.blue}?${colors.reset} Public Key: `);
1984
- if (!docsPublicKey) {
1985
- error("Public Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
1986
- process.exit(1);
1987
- }
1988
- }
2045
+ const docsPublicKey = await ensureDocsPublicKey(config);
1989
2046
  await downloadDocs(docsPublicKey);
1990
2047
  } else if (parsed.command === "mcp") {
1991
2048
  await setupMcp();
1992
2049
  } else if (parsed.command === "deploy") {
1993
2050
  const directory = parsed.args[0] || fileConfig.deployDir || ".";
1994
- if (!config.publicKey && !config.publicKey) {
2051
+ if (!config.publicKey) {
1995
2052
  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');
1996
2053
  process.exit(1);
1997
2054
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "1.4.1",
3
+ "version": "1.4.2",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",