connectbase-client 6.3.0 → 6.3.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 +33 -0
  2. package/dist/cli.js +116 -46
  3. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -3,6 +3,39 @@
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.2] - 2026-09-08
7
+
8
+ ### Fixed — `init` 이 pnpm / yarn / bun 프로젝트에도 npm 명령을 안내하던 문제
9
+
10
+ `init` 의 마지막 안내(`npm install`, `npm run deploy`)와 package.json 에 넣는 deploy
11
+ 스크립트(`npm run build && …`)가 npm 으로 고정돼 있었습니다. pnpm 프로젝트에서 안내대로
12
+ `npm install` 을 치면 package-lock.json 이 새로 생기고 node_modules 가 pnpm 것과 갈립니다.
13
+
14
+ 이제 package.json 의 `packageManager` 필드, 없으면 잠금 파일(pnpm-lock.yaml / yarn.lock /
15
+ bun.lock / package-lock.json — 모노레포는 git 루트까지 올라가며 탐색)로 패키지 매니저를 알아내
16
+ 그 매니저의 명령을 안내하고 스크립트에도 씁니다. pnpm 은 `pnpm deploy` 가 워크스페이스
17
+ 배포 내장 명령이라 반드시 `pnpm run deploy` 로 안내합니다. `update` 의 새 버전 안내도
18
+ npm 과 pnpm 을 나란히 찍던 것을 감지된 매니저 하나로 바꿨습니다.
19
+
20
+ ## [6.3.1] - 2026-09-08
21
+
22
+ ### Fixed — `init` 의 웹 스토리지 생성 실패가 `[object Object]` 로만 찍히던 문제
23
+
24
+ 앱에 이미 같은 이름의 웹 스토리지가 있는 상태에서 `init` 이 같은 이름으로 생성을 시도하면
25
+ 서버는 409 를 돌려주는데, CLI 가 그 응답 본문(`{ error: { code, message } }`)을 문자열로
26
+ 찍어 `생성 실패: [object Object]` 만 보였습니다. 원인이 이름 충돌이라는 사실을 알 길이
27
+ 없었고, 스토리지 선택 프롬프트에서 Enter 만 치면 정확히 이 경로로 들어갔습니다.
28
+
29
+ - 에러 응답 형식이 `{ error: "문자열" }` 이든 표준 봉투 `{ error: { code, message,
30
+ suggestion } }` 이든 사람이 읽을 메시지로 정리하는 헬퍼(`apiErrorMessage`)를 두고,
31
+ `init` / `deploy` / `promote` / 배포 이력 / presign / 앱 생성 / 인증 세션의 에러 출력을
32
+ 전부 이 헬퍼로 통일했습니다. 서버가 제안(suggestion)을 보내면 함께 보여 줍니다.
33
+ - 대화형 `init` 도 `--yes` 와 같은 규칙으로, 입력한 이름과 같은 웹 스토리지가 이미 있으면
34
+ 생성 대신 그 스토리지를 재사용합니다. 서버 요청 없이 즉시 넘어가므로 409 자체가 나지
35
+ 않습니다.
36
+ - 서버(core-server)도 같은 앱의 이름 중복을 미리 검사해 `같은 이름의 웹 스토리지가 이미
37
+ 있습니다: "<이름>"` 이라는 409 를 돌려줍니다. 구버전 CLI 로도 이제 이 문장이 보입니다.
38
+
6
39
  ## [6.3.0] - 2026-09-07
7
40
 
8
41
  ### Fixed — Secret Key 가 만료되면 `init` 을 다시 돌릴 수 없던 문제
package/dist/cli.js CHANGED
@@ -32,13 +32,16 @@ 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,
37
+ detectPackageManager: () => detectPackageManager,
36
38
  getTunnelConnectCandidates: () => getTunnelConnectCandidates,
37
39
  guessMimeType: () => guessMimeType,
38
40
  isAllowedDeployExt: () => isAllowedDeployExt,
39
41
  isBinaryDeployExt: () => isBinaryDeployExt,
40
42
  makeRequest: () => makeRequest,
41
43
  normalizeRelativePath: () => normalizeRelativePath,
44
+ packageManagerCommands: () => packageManagerCommands,
42
45
  parseArgs: () => parseArgs,
43
46
  registerEndpointBinding: () => registerEndpointBinding,
44
47
  sha256Hex: () => sha256Hex,
@@ -147,7 +150,7 @@ function handleTunnelError(msg, appId, localPort) {
147
150
  }
148
151
 
149
152
  // src/cli.ts
150
- var VERSION = "6.3.0" ? "6.3.0" : getPackageVersion();
153
+ var VERSION = "6.3.2" ? "6.3.2" : getPackageVersion();
151
154
  var DEFAULT_BASE_URL = "https://api.connectbase.world";
152
155
  var colors = {
153
156
  reset: "\x1B[0m",
@@ -420,9 +423,9 @@ async function requestDeployPresign(baseStorageUrl, headers, files, timeoutMs) {
420
423
  );
421
424
  if (res.status === 404) return null;
422
425
  if (res.status < 200 || res.status >= 300) {
423
- const data = res.data;
424
- const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
425
- throw new Error(`presign \uC694\uCCAD \uC2E4\uD328: ${msg}`);
426
+ throw new Error(
427
+ `presign \uC694\uCCAD \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
428
+ );
426
429
  }
427
430
  const parsed = res.data;
428
431
  return parsed?.urls ?? [];
@@ -580,6 +583,27 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
580
583
  }
581
584
  }
582
585
  var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
586
+ function apiErrorMessage(data, status) {
587
+ const fallback = `HTTP ${status}`;
588
+ if (typeof data === "string") return data.trim() || fallback;
589
+ if (typeof data !== "object" || data === null) return fallback;
590
+ const body = data;
591
+ const str = (v) => typeof v === "string" && v.trim() ? v.trim() : void 0;
592
+ let message;
593
+ let suggestion;
594
+ if (typeof body.error === "object" && body.error !== null) {
595
+ const detail = body.error;
596
+ message = str(detail.message) ?? str(detail.code);
597
+ suggestion = str(detail.suggestion);
598
+ } else {
599
+ message = str(body.message) ?? str(body.error);
600
+ }
601
+ if (!message) {
602
+ const raw = JSON.stringify(body);
603
+ return raw === "{}" ? fallback : `${fallback}: ${raw}`;
604
+ }
605
+ return suggestion ? `${message} (${suggestion})` : message;
606
+ }
583
607
  async function makeRequest(url, method, headers, body, reqOpts = {}) {
584
608
  return new Promise((resolve2, reject) => {
585
609
  const parsedUrl = new URL(url);
@@ -768,9 +792,9 @@ async function tryFetchManifest(baseStorageUrl, headers) {
768
792
  );
769
793
  if (res.status === 404) return null;
770
794
  if (res.status < 200 || res.status >= 300) {
771
- const data = res.data;
772
- const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
773
- throw new Error(`manifest \uC870\uD68C \uC2E4\uD328: ${msg}`);
795
+ throw new Error(
796
+ `manifest \uC870\uD68C \uC2E4\uD328: ${apiErrorMessage(res.data, res.status)}`
797
+ );
774
798
  }
775
799
  const parsed = res.data;
776
800
  if (!parsed || !Array.isArray(parsed.files)) {
@@ -920,8 +944,7 @@ ${colors.cyan}URL: ${data.url}${colors.reset}
920
944
  `);
921
945
  }
922
946
  } else {
923
- const data = response.data;
924
- const errorMsg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : typeof data === "string" ? data : `HTTP ${response.status}`;
947
+ const errorMsg = apiErrorMessage(response.data, response.status);
925
948
  if (!reportDeployAuthError(response.status, response.data)) {
926
949
  error(`\uBC30\uD3EC \uC2E4\uD328: ${errorMsg}`);
927
950
  }
@@ -950,8 +973,7 @@ async function promote(config, deploymentId) {
950
973
  process.exit(1);
951
974
  }
952
975
  if (res.status < 200 || res.status >= 300) {
953
- const data2 = res.data;
954
- const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
976
+ const msg = apiErrorMessage(res.data, res.status);
955
977
  if (!reportDeployAuthError(res.status, res.data)) {
956
978
  error(`promote \uC2E4\uD328: ${msg}`);
957
979
  }
@@ -974,8 +996,7 @@ async function listReleases(config) {
974
996
  headers
975
997
  );
976
998
  if (res.status < 200 || res.status >= 300) {
977
- const data2 = res.data;
978
- const msg = typeof data2 === "object" && data2 !== null ? data2.error || data2.message || JSON.stringify(data2) : `HTTP ${res.status}`;
999
+ const msg = apiErrorMessage(res.data, res.status);
979
1000
  if (!reportDeployAuthError(res.status, res.data)) {
980
1001
  error(`\uBC30\uD3EC \uC774\uB825 \uC870\uD68C \uC2E4\uD328: ${msg}`);
981
1002
  }
@@ -1100,9 +1121,9 @@ async function browserAuthFlow(baseUrl = DEFAULT_BASE_URL) {
1100
1121
  {}
1101
1122
  );
1102
1123
  if (startRes.status !== 200) {
1103
- const errData = startRes.data;
1104
- const detail = errData?.error || errData?.message || `HTTP ${startRes.status}`;
1105
- error(`\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${detail}`);
1124
+ error(
1125
+ `\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${apiErrorMessage(startRes.data, startRes.status)}`
1126
+ );
1106
1127
  process.exit(1);
1107
1128
  }
1108
1129
  const { session_id } = startRes.data;
@@ -1413,8 +1434,9 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1413
1434
  process.exit(1);
1414
1435
  }
1415
1436
  if (createRes.status !== 201) {
1416
- const data = createRes.data;
1417
- error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
1437
+ error(
1438
+ `\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
1439
+ );
1418
1440
  process.exit(1);
1419
1441
  }
1420
1442
  const createData = createRes.data;
@@ -1498,27 +1520,34 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1498
1520
  const name = nonInteractive ? projectName : await prompt(
1499
1521
  `${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `
1500
1522
  ) || projectName;
1501
- info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
1502
- const createRes = await makeRequest(
1503
- `${baseUrl}/v1/public/storages/webs`,
1504
- "POST",
1505
- {
1506
- "X-Public-Key": publicKeyForSdk,
1507
- "X-User-Secret-Key": secretKey,
1508
- Authorization: `Bearer ${secretKey}`
1509
- },
1510
- JSON.stringify({ name })
1511
- );
1512
- if (createRes.status !== 200) {
1513
- const data = createRes.data;
1514
- error(
1515
- `\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`
1523
+ const sameName = storages.find((s) => s.name === name);
1524
+ if (sameName) {
1525
+ storageId = sameName.id;
1526
+ success(
1527
+ `\uAC19\uC740 \uC774\uB984\uC758 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uAC00 \uC774\uBBF8 \uC788\uC5B4 \uC7AC\uC0AC\uC6A9\uD569\uB2C8\uB2E4: ${sameName.name}`
1516
1528
  );
1517
- process.exit(1);
1529
+ } else {
1530
+ info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
1531
+ const createRes = await makeRequest(
1532
+ `${baseUrl}/v1/public/storages/webs`,
1533
+ "POST",
1534
+ {
1535
+ "X-Public-Key": publicKeyForSdk,
1536
+ "X-User-Secret-Key": secretKey,
1537
+ Authorization: `Bearer ${secretKey}`
1538
+ },
1539
+ JSON.stringify({ name })
1540
+ );
1541
+ if (createRes.status !== 200) {
1542
+ error(
1543
+ `\uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`
1544
+ );
1545
+ process.exit(1);
1546
+ }
1547
+ const createData = createRes.data;
1548
+ storageId = createData.storage_web_id;
1549
+ success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
1518
1550
  }
1519
- const createData = createRes.data;
1520
- storageId = createData.storage_web_id;
1521
- success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
1522
1551
  }
1523
1552
  } else {
1524
1553
  throw new Error(`HTTP ${listRes.status}`);
@@ -1556,7 +1585,8 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1556
1585
  `);
1557
1586
  success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
1558
1587
  addToGitignore(".connectbaserc");
1559
- const { needsInstall } = addDeployScript(deployDir);
1588
+ const pm = packageManagerCommands(detectPackageManager(cwd));
1589
+ const { needsInstall } = addDeployScript(deployDir, pm);
1560
1590
  await setupClaudeCode(secretKey, projectRoot, baseUrl);
1561
1591
  log(`
1562
1592
  ${colors.green}\uCD08\uAE30\uD654 \uC644\uB8CC!${colors.reset}
@@ -1564,10 +1594,10 @@ ${colors.green}\uCD08\uAE30\uD654 \uC644\uB8CC!${colors.reset}
1564
1594
  log(`${colors.dim}\uBC30\uD3EC\uD558\uB824\uBA74:${colors.reset}`);
1565
1595
  if (needsInstall) {
1566
1596
  log(
1567
- ` ${colors.cyan}npm install${colors.reset} ${colors.dim}# connectbase-client (CLI \uD3EC\uD568) \uC124\uCE58${colors.reset}`
1597
+ ` ${colors.cyan}${pm.install}${colors.reset} ${colors.dim}# connectbase-client (CLI \uD3EC\uD568) \uC124\uCE58${colors.reset}`
1568
1598
  );
1569
1599
  }
1570
- log(` ${colors.cyan}npm run deploy${colors.reset}`);
1600
+ log(` ${colors.cyan}${pm.run("deploy")}${colors.reset}`);
1571
1601
  log(`
1572
1602
  ${colors.dim}Claude Code\uC5D0\uC11C MCP\uAC00 \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4${colors.reset}
1573
1603
  `);
@@ -1617,7 +1647,45 @@ ${entry}
1617
1647
  success(".gitignore \uC0DD\uC131 \uC644\uB8CC");
1618
1648
  }
1619
1649
  }
1620
- function addDeployScript(deployDir) {
1650
+ function detectPackageManager(startDir = process.cwd()) {
1651
+ try {
1652
+ const pkg = JSON.parse(
1653
+ fs2.readFileSync(path2.join(startDir, "package.json"), "utf-8")
1654
+ );
1655
+ const declared = typeof pkg.packageManager === "string" ? pkg.packageManager.split("@")[0] : "";
1656
+ if (declared === "npm" || declared === "pnpm" || declared === "yarn" || declared === "bun") {
1657
+ return declared;
1658
+ }
1659
+ } catch {
1660
+ }
1661
+ const lockfiles = [
1662
+ ["pnpm-lock.yaml", "pnpm"],
1663
+ ["yarn.lock", "yarn"],
1664
+ ["bun.lockb", "bun"],
1665
+ ["bun.lock", "bun"],
1666
+ ["package-lock.json", "npm"],
1667
+ ["npm-shrinkwrap.json", "npm"]
1668
+ ];
1669
+ let dir = path2.resolve(startDir);
1670
+ for (; ; ) {
1671
+ for (const [file, pm] of lockfiles) {
1672
+ if (fs2.existsSync(path2.join(dir, file))) return pm;
1673
+ }
1674
+ if (fs2.existsSync(path2.join(dir, ".git"))) break;
1675
+ const parent = path2.dirname(dir);
1676
+ if (parent === dir) break;
1677
+ dir = parent;
1678
+ }
1679
+ return "npm";
1680
+ }
1681
+ function packageManagerCommands(pm) {
1682
+ return {
1683
+ install: `${pm} install`,
1684
+ run: (script) => `${pm} run ${script}`,
1685
+ addLatest: (name) => pm === "npm" ? `npm install ${name}@latest` : `${pm} add ${name}@latest`
1686
+ };
1687
+ }
1688
+ function addDeployScript(deployDir, pm) {
1621
1689
  const pkgPath = path2.join(process.cwd(), "package.json");
1622
1690
  if (!fs2.existsSync(pkgPath)) {
1623
1691
  warn("package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4");
@@ -1639,7 +1707,7 @@ function addDeployScript(deployDir) {
1639
1707
  );
1640
1708
  } else {
1641
1709
  const deployCmd = `connectbase deploy ${deployDir}`;
1642
- pkg.scripts.deploy = pkg.scripts.build ? `npm run build && ${deployCmd}` : deployCmd;
1710
+ pkg.scripts.deploy = pkg.scripts.build ? `${pm.run("build")} && ${deployCmd}` : deployCmd;
1643
1711
  success("package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC");
1644
1712
  }
1645
1713
  fs2.writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}
@@ -2119,10 +2187,10 @@ ${colors.cyan}ConnectBase Update${colors.reset}
2119
2187
  warn(
2120
2188
  `\uC0C8 \uBC84\uC804\uC774 \uC788\uC2B5\uB2C8\uB2E4: v${VERSION} \u2192 v${colors.green}${latestVersion}${colors.reset}`
2121
2189
  );
2190
+ const pm = packageManagerCommands(detectPackageManager(projectRoot));
2122
2191
  log(
2123
- ` ${colors.cyan}npm install connectbase-client@latest${colors.reset}`
2192
+ ` ${colors.cyan}${pm.addLatest("connectbase-client")}${colors.reset}`
2124
2193
  );
2125
- log(` ${colors.cyan}pnpm add connectbase-client@latest${colors.reset}`);
2126
2194
  log("");
2127
2195
  }
2128
2196
  } else {
@@ -2664,8 +2732,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `
2664
2732
  process.exit(1);
2665
2733
  }
2666
2734
  if (createRes.status !== 201) {
2667
- const data = createRes.data;
2668
- error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
2735
+ error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${apiErrorMessage(createRes.data, createRes.status)}`);
2669
2736
  process.exit(1);
2670
2737
  }
2671
2738
  const createData = createRes.data;
@@ -3744,13 +3811,16 @@ main().catch((err) => {
3744
3811
  // Annotate the CommonJS export names for ESM import in node:
3745
3812
  0 && (module.exports = {
3746
3813
  TunnelConnectSelector,
3814
+ apiErrorMessage,
3747
3815
  computeDeployDiff,
3816
+ detectPackageManager,
3748
3817
  getTunnelConnectCandidates,
3749
3818
  guessMimeType,
3750
3819
  isAllowedDeployExt,
3751
3820
  isBinaryDeployExt,
3752
3821
  makeRequest,
3753
3822
  normalizeRelativePath,
3823
+ packageManagerCommands,
3754
3824
  parseArgs,
3755
3825
  registerEndpointBinding,
3756
3826
  sha256Hex,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "connectbase-client",
3
- "version": "6.3.0",
3
+ "version": "6.3.2",
4
4
  "description": "Connect Base JavaScript/TypeScript SDK for browser and Node.js",
5
5
  "repository": {
6
6
  "type": "git",