connectbase-client 6.10.0 → 6.12.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/dist/cli.js CHANGED
@@ -31,12 +31,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/cli.ts
32
32
  var cli_exports = {};
33
33
  __export(cli_exports, {
34
+ DEPLOY_REQUEST_BUDGET_BYTES: () => DEPLOY_REQUEST_BUDGET_BYTES,
34
35
  TunnelConnectSelector: () => TunnelConnectSelector,
35
36
  ancestorCommandLines: () => ancestorCommandLines,
36
37
  apiErrorMessage: () => apiErrorMessage,
38
+ browserAuthStartBody: () => browserAuthStartBody,
39
+ browserAuthUrl: () => browserAuthUrl,
40
+ browserOpenCommand: () => browserOpenCommand,
37
41
  buildFirstHint: () => buildFirstHint,
38
42
  collectDeployFiles: () => collectDeployFiles,
39
43
  computeDeployDiff: () => computeDeployDiff,
44
+ decideBaseUrl: () => decideBaseUrl,
45
+ describeBaseUrl: () => describeBaseUrl,
40
46
  detectPackageManager: () => detectPackageManager,
41
47
  formatSkippedFilesReport: () => formatSkippedFilesReport,
42
48
  getTunnelConnectCandidates: () => getTunnelConnectCandidates,
@@ -44,21 +50,28 @@ __export(cli_exports, {
44
50
  isAllowedDeployExt: () => isAllowedDeployExt,
45
51
  isBinaryDeployExt: () => isBinaryDeployExt,
46
52
  isExtensionlessDataFile: () => isExtensionlessDataFile,
53
+ isOutsideProject: () => isOutsideProject,
54
+ isPlatformBaseUrl: () => isPlatformBaseUrl,
55
+ loadConfig: () => loadConfig,
47
56
  makeRequest: () => makeRequest,
48
57
  normalizeRelativePath: () => normalizeRelativePath,
58
+ openBrowser: () => openBrowser,
49
59
  packageManagerCommands: () => packageManagerCommands,
50
60
  packageScriptInvocationPattern: () => packageScriptInvocationPattern,
51
61
  parseArgs: () => parseArgs,
52
62
  registerEndpointBinding: () => registerEndpointBinding,
53
63
  runningInsidePackageScript: () => runningInsidePackageScript,
64
+ selectBinariesToOffload: () => selectBinariesToOffload,
54
65
  sha256Hex: () => sha256Hex,
55
- storageAdminHeaders: () => storageAdminHeaders
66
+ storageAdminHeaders: () => storageAdminHeaders,
67
+ writeSecretFile: () => writeSecretFile
56
68
  });
57
69
  module.exports = __toCommonJS(cli_exports);
58
70
  var crypto = __toESM(require("crypto"));
59
71
  var fs2 = __toESM(require("fs"));
60
72
  var http = __toESM(require("http"));
61
73
  var https = __toESM(require("https"));
74
+ var os2 = __toESM(require("os"));
62
75
  var path2 = __toESM(require("path"));
63
76
  var readline = __toESM(require("readline"));
64
77
 
@@ -157,7 +170,7 @@ function handleTunnelError(msg, appId, localPort) {
157
170
  }
158
171
 
159
172
  // src/cli.ts
160
- var VERSION = "6.10.0" ? "6.10.0" : getPackageVersion();
173
+ var VERSION = "6.12.0" ? "6.12.0" : getPackageVersion();
161
174
  var DEFAULT_BASE_URL = "https://api.connectbase.world";
162
175
  var colors = {
163
176
  reset: "\x1B[0m",
@@ -315,6 +328,32 @@ function isExtensionlessDataFile(filePath) {
315
328
  return path2.extname(base) === "" && !ALLOWED_EXTENSIONLESS_FILES.has(base);
316
329
  }
317
330
  var WEB_FILE_MAX_STORED_BYTES = 6 * 1024 * 1024 - 64 * 1024;
331
+ var DEPLOY_REQUEST_BUDGET_BYTES = 96 * 1024 * 1024;
332
+ function deployEntryBytes(f) {
333
+ return Buffer.byteLength(f.content, "utf8") + Buffer.byteLength(f.path, "utf8") + 256;
334
+ }
335
+ function selectBinariesToOffload(upsert, perFileLimit = WEB_FILE_MAX_STORED_BYTES, budget = DEPLOY_REQUEST_BUDGET_BYTES) {
336
+ const offload = new Set(
337
+ upsert.filter(
338
+ (f) => f.is_binary && Buffer.byteLength(f.content, "utf8") > perFileLimit
339
+ )
340
+ );
341
+ let inlineBytes = upsert.reduce(
342
+ (sum, f) => offload.has(f) ? sum : sum + deployEntryBytes(f),
343
+ 0
344
+ );
345
+ if (inlineBytes > budget) {
346
+ const rest = upsert.filter((f) => f.is_binary && !offload.has(f)).sort(
347
+ (a, b) => Buffer.byteLength(b.content, "utf8") - Buffer.byteLength(a.content, "utf8")
348
+ );
349
+ for (const f of rest) {
350
+ if (inlineBytes <= budget) break;
351
+ offload.add(f);
352
+ inlineBytes -= deployEntryBytes(f);
353
+ }
354
+ }
355
+ return { offload: upsert.filter((f) => offload.has(f)), inlineBytes };
356
+ }
318
357
  function sha256Hex(value) {
319
358
  return crypto.createHash("sha256").update(value, "utf8").digest("hex");
320
359
  }
@@ -359,11 +398,13 @@ function reportDeployAuthError(status, data) {
359
398
  return true;
360
399
  }
361
400
  function loadConfig() {
401
+ const envBaseUrl = process.env.CONNECTBASE_BASE_URL;
362
402
  const config = {
363
403
  publicKey: process.env.CONNECTBASE_PUBLIC_KEY,
364
404
  secretKey: process.env.CONNECTBASE_SECRET_KEY,
365
405
  storageId: process.env.CONNECTBASE_STORAGE_ID,
366
- baseUrl: process.env.CONNECTBASE_BASE_URL || DEFAULT_BASE_URL
406
+ baseUrl: envBaseUrl || DEFAULT_BASE_URL,
407
+ baseUrlFromRepo: false
367
408
  };
368
409
  const rcPath = path2.join(process.cwd(), ".connectbaserc");
369
410
  if (fs2.existsSync(rcPath)) {
@@ -372,7 +413,10 @@ function loadConfig() {
372
413
  if (rcContent.publicKey) config.publicKey = rcContent.publicKey;
373
414
  if (rcContent.secretKey) config.secretKey = rcContent.secretKey;
374
415
  if (rcContent.storageId) config.storageId = rcContent.storageId;
375
- if (rcContent.baseUrl) config.baseUrl = rcContent.baseUrl;
416
+ if (!envBaseUrl && rcContent.baseUrl && typeof rcContent.baseUrl === "string") {
417
+ config.baseUrl = rcContent.baseUrl;
418
+ config.baseUrlFromRepo = true;
419
+ }
376
420
  if (rcContent.deployDir) config.deployDir = rcContent.deployDir;
377
421
  if (rcContent.includeExtensionless === true)
378
422
  config.includeExtensionless = true;
@@ -573,12 +617,9 @@ function putToPresignedUrl(uploadUrl, body, contentType, timeoutMs) {
573
617
  });
574
618
  }
575
619
  async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, timeoutMs) {
576
- const oversized = diff.upsert.filter(
577
- (f) => Buffer.byteLength(f.content, "utf8") > WEB_FILE_MAX_STORED_BYTES
620
+ const largeText = diff.upsert.filter(
621
+ (f) => !f.is_binary && Buffer.byteLength(f.content, "utf8") > WEB_FILE_MAX_STORED_BYTES
578
622
  );
579
- if (oversized.length === 0) return;
580
- const largeText = oversized.filter((f) => !f.is_binary);
581
- const largeBinaries = oversized.filter((f) => f.is_binary);
582
623
  if (largeText.length > 0) {
583
624
  error(
584
625
  `\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uD30C\uC77C\uB2F9 \uD06C\uAE30 \uD55C\uB3C4(\uC57D ${formatSize(WEB_FILE_MAX_STORED_BYTES)})\uB97C \uCD08\uACFC\uD55C \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC774 \uC788\uC5B4 \uBC30\uD3EC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4:`
@@ -593,6 +634,18 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
593
634
  );
594
635
  process.exit(1);
595
636
  }
637
+ const { offload: largeBinaries, inlineBytes } = selectBinariesToOffload(
638
+ diff.upsert
639
+ );
640
+ if (inlineBytes > DEPLOY_REQUEST_BUDGET_BYTES) {
641
+ error(
642
+ `\uD14D\uC2A4\uD2B8 \uD30C\uC77C\uB9CC\uC73C\uB85C \uBC30\uD3EC \uC694\uCCAD \uD55C\uB3C4(\uC57D ${formatSize(DEPLOY_REQUEST_BUDGET_BYTES)})\uB97C \uB118\uC2B5\uB2C8\uB2E4. \uD14D\uC2A4\uD2B8\uB294 \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uB85C \uB3CC\uB9B4 \uC218 \uC5C6\uC5B4\uC694.`
643
+ );
644
+ warn(
645
+ "\uBCC0\uACBD\uD55C \uD14D\uC2A4\uD2B8 \uD30C\uC77C\uC744 \uB098\uB220 \uC5EC\uB7EC \uBC88 \uBC30\uD3EC\uD558\uAC70\uB098, \uD070 \uB370\uC774\uD130 \uD30C\uC77C\uC740 \uD30C\uC77C \uC2A4\uD1A0\uB9AC\uC9C0(cb.storage)\uB85C \uC62E\uAE30\uC138\uC694."
646
+ );
647
+ process.exit(1);
648
+ }
596
649
  if (largeBinaries.length === 0) return;
597
650
  const presignFiles = largeBinaries.map((f) => ({
598
651
  path: f.path,
@@ -632,7 +685,7 @@ async function offloadLargeBinaries(baseStorageUrl, headers, diff, _envLabel, ti
632
685
  }
633
686
  const entryByPath = new Map(presigned.map((p) => [p.path, p]));
634
687
  info(
635
- `\uB300\uC6A9\uB7C9 \uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4...`
688
+ `\uBC14\uC774\uB108\uB9AC ${largeBinaries.length}\uAC1C\uB97C Object Storage \uB85C \uC9C1\uC811 \uC5C5\uB85C\uB4DC\uD569\uB2C8\uB2E4(\uD30C\uC77C\uB2F9 \uD55C\uB3C4 \uCD08\uACFC\uC774\uAC70\uB098 \uC694\uCCAD \uD558\uB098\uAC00 \uC57D ${formatSize(DEPLOY_REQUEST_BUDGET_BYTES)} \uB97C \uB118\uC5B4\uC11C)...`
636
689
  );
637
690
  for (const f of largeBinaries) {
638
691
  const entry = entryByPath.get(f.path);
@@ -1197,26 +1250,59 @@ function getProjectRoot() {
1197
1250
  function consoleUrlFor(baseUrl) {
1198
1251
  return baseUrl.replace("api.", "");
1199
1252
  }
1200
- function openBrowser(url) {
1201
- const { exec } = require("child_process");
1202
- const platform2 = process.platform;
1203
- let command;
1204
- if (platform2 === "darwin") {
1205
- command = `open "${url}"`;
1206
- } else if (platform2 === "win32") {
1207
- command = `start "" "${url}"`;
1208
- } else {
1209
- command = `xdg-open "${url}"`;
1253
+ function browserOpenCommand(url, platform2 = process.platform) {
1254
+ let target;
1255
+ try {
1256
+ const parsed = new URL(url);
1257
+ if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
1258
+ return null;
1259
+ }
1260
+ target = parsed.toString();
1261
+ } catch {
1262
+ return null;
1263
+ }
1264
+ if (platform2 === "darwin") return { command: "open", args: [target] };
1265
+ if (platform2 === "win32") {
1266
+ return {
1267
+ command: "rundll32",
1268
+ args: ["url.dll,FileProtocolHandler", target]
1269
+ };
1210
1270
  }
1211
- exec(command, () => {
1271
+ return { command: "xdg-open", args: [target] };
1272
+ }
1273
+ function openBrowser(url) {
1274
+ const opener = browserOpenCommand(url);
1275
+ if (!opener) return;
1276
+ const { spawn } = require("child_process");
1277
+ const child = spawn(opener.command, opener.args, {
1278
+ stdio: "ignore",
1279
+ detached: true
1280
+ });
1281
+ child.on("error", () => {
1212
1282
  });
1283
+ child.unref();
1284
+ }
1285
+ function browserAuthStartBody() {
1286
+ return {
1287
+ flow: "user_code",
1288
+ hostname: os2.hostname(),
1289
+ os: `${process.platform} ${process.arch}`,
1290
+ cli_version: VERSION
1291
+ };
1292
+ }
1293
+ function browserAuthUrl(baseUrl, start) {
1294
+ if (!start.user_code) {
1295
+ return `${consoleUrlFor(baseUrl)}/auth/cli-auth?session=${start.session_id}`;
1296
+ }
1297
+ return `${consoleUrlFor(baseUrl)}/auth/cli-auth`;
1213
1298
  }
1214
1299
  async function browserAuthFlow(baseUrl = DEFAULT_BASE_URL) {
1215
1300
  info("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D \uC138\uC158\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4...");
1216
1301
  const startRes = await makeRequest(
1217
1302
  `${baseUrl}/v1/public/cli-auth/start`,
1218
1303
  "POST",
1219
- {}
1304
+ {},
1305
+ JSON.stringify(browserAuthStartBody())
1220
1306
  );
1221
1307
  if (startRes.status !== 200) {
1222
1308
  error(
@@ -1224,15 +1310,28 @@ async function browserAuthFlow(baseUrl = DEFAULT_BASE_URL) {
1224
1310
  );
1225
1311
  process.exit(1);
1226
1312
  }
1227
- const { session_id } = startRes.data;
1228
- const authUrl = `${consoleUrlFor(baseUrl)}/auth/cli-auth?session=${session_id}`;
1313
+ const start = startRes.data;
1314
+ const { session_id } = start;
1315
+ const authUrl = browserAuthUrl(baseUrl, start);
1229
1316
  log(`
1230
1317
  ${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC138\uC694:${colors.reset}`);
1231
1318
  log(`${colors.dim}${authUrl}${colors.reset}
1232
1319
  `);
1320
+ if (start.user_code) {
1321
+ log(`${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0 \uC774 \uCF54\uB4DC\uB97C \uC785\uB825\uD558\uC138\uC694:${colors.reset}`);
1322
+ log(`
1323
+ ${colors.green}${start.user_code}${colors.reset}
1324
+ `);
1325
+ log(
1326
+ `${colors.dim}\uC774 \uBA85\uB839\uC744 \uC9C1\uC811 \uC2E4\uD589\uD558\uC9C0 \uC54A\uC558\uB2E4\uBA74 \uC2B9\uC778\uD558\uC9C0 \uB9C8\uC138\uC694.${colors.reset}
1327
+ `
1328
+ );
1329
+ }
1233
1330
  openBrowser(authUrl);
1234
- const pollInterval = 3e3;
1235
- const maxAttempts = 100;
1331
+ const pollInterval = (start.interval ?? 3) * 1e3;
1332
+ const maxAttempts = Math.ceil(
1333
+ (start.expires_in ?? 300) * 1e3 / pollInterval
1334
+ );
1236
1335
  let attempts = 0;
1237
1336
  let consecutive404 = 0;
1238
1337
  const max404Retries = 5;
@@ -1346,7 +1445,7 @@ function saveSecretKeyToRc(secretKey, dir = process.cwd()) {
1346
1445
  }
1347
1446
  }
1348
1447
  rcData.secretKey = secretKey;
1349
- fs2.writeFileSync(rcPath, `${JSON.stringify(rcData, null, 2)}
1448
+ writeSecretFile(rcPath, `${JSON.stringify(rcData, null, 2)}
1350
1449
  `);
1351
1450
  }
1352
1451
  async function fetchAppsWithSecretKey(baseUrl, secretKey) {
@@ -1679,7 +1778,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `
1679
1778
  if (secretKey) {
1680
1779
  rcData.secretKey = secretKey;
1681
1780
  }
1682
- fs2.writeFileSync(rcPath, `${JSON.stringify(rcData, null, 2)}
1781
+ writeSecretFile(rcPath, `${JSON.stringify(rcData, null, 2)}
1683
1782
  `);
1684
1783
  success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
1685
1784
  addToGitignore(".connectbaserc");
@@ -1723,6 +1822,13 @@ function detectBuildDir() {
1723
1822
  return "./dist";
1724
1823
  return "./dist";
1725
1824
  }
1825
+ function writeSecretFile(filePath, content) {
1826
+ try {
1827
+ if (fs2.existsSync(filePath)) fs2.chmodSync(filePath, 384);
1828
+ } catch {
1829
+ }
1830
+ fs2.writeFileSync(filePath, content, { mode: 384 });
1831
+ }
1726
1832
  function addToGitignore(entry, basePath) {
1727
1833
  const dir = basePath || process.cwd();
1728
1834
  const gitignorePath = path2.join(dir, ".gitignore");
@@ -2248,12 +2354,16 @@ async function setupMcp(secretKey, baseUrl = DEFAULT_BASE_URL) {
2248
2354
  } catch {
2249
2355
  const backupPath = `${mcpConfigPath}.backup`;
2250
2356
  fs2.copyFileSync(mcpConfigPath, backupPath);
2357
+ try {
2358
+ fs2.chmodSync(backupPath, 384);
2359
+ } catch {
2360
+ }
2251
2361
  warn(`.mcp.json \uD30C\uC2F1 \uC2E4\uD328, \uBC31\uC5C5 \uC0DD\uC131: ${backupPath}`);
2252
2362
  mcpConfig = { mcpServers: {} };
2253
2363
  }
2254
2364
  }
2255
2365
  mcpConfig.mcpServers["connect-base"] = mcpEntry;
2256
- fs2.writeFileSync(mcpConfigPath, `${JSON.stringify(mcpConfig, null, 2)}
2366
+ writeSecretFile(mcpConfigPath, `${JSON.stringify(mcpConfig, null, 2)}
2257
2367
  `);
2258
2368
  success(`${mcpConfigPath} \uC0DD\uC131 \uC644\uB8CC`);
2259
2369
  addToGitignore(".mcp.json", root);
@@ -2422,7 +2532,7 @@ ${colors.cyan}ConnectBase Update${colors.reset}
2422
2532
  headers: { Authorization: `Bearer ${secretKey}` }
2423
2533
  };
2424
2534
  mcpConfig.mcpServers = servers;
2425
- fs2.writeFileSync(
2535
+ writeSecretFile(
2426
2536
  mcpConfigPath,
2427
2537
  `${JSON.stringify(mcpConfig, null, 2)}
2428
2538
  `
@@ -2973,7 +3083,7 @@ async function startTunnel(port, config, tunnelOpts) {
2973
3083
  }
2974
3084
  }
2975
3085
  rcData.secretKey = sk;
2976
- fs2.writeFileSync(rcPath2, `${JSON.stringify(rcData, null, 2)}
3086
+ writeSecretFile(rcPath2, `${JSON.stringify(rcData, null, 2)}
2977
3087
  `);
2978
3088
  addToGitignore(".connectbaserc");
2979
3089
  success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
@@ -3007,7 +3117,7 @@ async function startTunnel(port, config, tunnelOpts) {
3007
3117
  }
3008
3118
  if (rcData.tunnelAppId !== appId) {
3009
3119
  rcData.tunnelAppId = appId;
3010
- fs2.writeFileSync(rcPath, `${JSON.stringify(rcData, null, 2)}
3120
+ writeSecretFile(rcPath, `${JSON.stringify(rcData, null, 2)}
3011
3121
  `);
3012
3122
  }
3013
3123
  } catch {
@@ -3688,6 +3798,7 @@ ${colors.yellow}\uC635\uC158:${colors.reset}
3688
3798
  -s, --storage <id> \uC2A4\uD1A0\uB9AC\uC9C0 ID
3689
3799
  -k, --public-key <key> API Key
3690
3800
  -u, --base-url <url> \uC11C\uBC84 URL (\uAE30\uBCF8: ${DEFAULT_BASE_URL})
3801
+ --allow-repo-base-url .connectbaserc \uC758 \uBE44\uACF5\uC2DD \uC11C\uBC84 URL \uC744 \uD655\uC778 \uC5C6\uC774 \uD5C8\uC6A9 (Secret Key \uAC00 \uADF8 \uC11C\uBC84\uB85C \uC804\uC1A1\uB428)
3691
3802
  -t, --timeout <sec> \uC694\uCCAD \uD0C0\uC784\uC544\uC6C3 (\uCD08, tunnel/deploy \uACF5\uD1B5. deploy \uBBF8\uC9C0\uC815 \uC2DC \uD30C\uC77C \uD06C\uAE30\uC5D0 \uB530\uB77C \uC790\uB3D9 \uC0B0\uC815)
3692
3803
  --max-body <MB> \uD130\uB110 \uCD5C\uB300 \uBC14\uB514 \uD06C\uAE30 (MB, tunnel \uC804\uC6A9)
3693
3804
  --force \uD130\uB110 lockfile \uBB34\uC2DC (\uC911\uBCF5 \uC2E4\uD589 \uAC15\uC81C, tunnel \uC804\uC6A9)
@@ -3748,7 +3859,7 @@ ${colors.yellow}\uD658\uACBD\uBCC0\uC218:${colors.reset}
3748
3859
  CONNECTBASE_SECRET_KEY Secret Key (cb_sk_*) \u2014 \uBC30\uD3EC/\uC2B9\uACA9/\uD130\uB110\uC5D0 \uD544\uC218
3749
3860
  CONNECTBASE_PUBLIC_KEY (deprecated) \uB808\uAC70\uC2DC \u2014 publicKey \uB610\uB294 secretKey \uB85C \uC0AC\uC6A9
3750
3861
  CONNECTBASE_STORAGE_ID \uC2A4\uD1A0\uB9AC\uC9C0 ID
3751
- CONNECTBASE_BASE_URL \uC11C\uBC84 URL
3862
+ CONNECTBASE_BASE_URL \uC11C\uBC84 URL (.connectbaserc \uBCF4\uB2E4 \uC6B0\uC120)
3752
3863
 
3753
3864
  ${colors.yellow}\uC124\uC815 \uD30C\uC77C (.connectbaserc):${colors.reset}
3754
3865
  {
@@ -3760,6 +3871,65 @@ ${colors.yellow}\uC124\uC815 \uD30C\uC77C (.connectbaserc):${colors.reset}
3760
3871
  }
3761
3872
  `);
3762
3873
  }
3874
+ var PLATFORM_API_HOST = new URL(DEFAULT_BASE_URL).host;
3875
+ var LOCAL_HOSTNAMES = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
3876
+ function isPlatformBaseUrl(baseUrl) {
3877
+ try {
3878
+ const url = new URL(baseUrl);
3879
+ if (url.protocol === "https:" && url.host === PLATFORM_API_HOST) {
3880
+ return true;
3881
+ }
3882
+ return (url.protocol === "http:" || url.protocol === "https:") && LOCAL_HOSTNAMES.has(url.hostname);
3883
+ } catch {
3884
+ return false;
3885
+ }
3886
+ }
3887
+ function describeBaseUrl(baseUrl) {
3888
+ try {
3889
+ return new URL(baseUrl).origin;
3890
+ } catch {
3891
+ return JSON.stringify(baseUrl);
3892
+ }
3893
+ }
3894
+ function decideBaseUrl(opts) {
3895
+ if (isPlatformBaseUrl(opts.baseUrl)) return "proceed";
3896
+ if (!opts.fromRepoConfig || opts.allowRepoBaseUrl) return "notice";
3897
+ return opts.interactive ? "confirm" : "refuse";
3898
+ }
3899
+ async function enforceBaseUrlPolicy(baseUrl, opts) {
3900
+ const decision = decideBaseUrl({
3901
+ baseUrl,
3902
+ ...opts,
3903
+ interactive: process.stdin.isTTY === true
3904
+ });
3905
+ if (decision === "proceed") return;
3906
+ const target = describeBaseUrl(baseUrl);
3907
+ if (decision === "notice") {
3908
+ warn(
3909
+ `\uC11C\uBC84 URL \uC774 ConnectBase \uACF5\uC2DD \uC8FC\uC18C\uAC00 \uC544\uB2D9\uB2C8\uB2E4: ${target}. Secret Key \uB97C \uD3EC\uD568\uD55C \uC790\uACA9\uC99D\uBA85\uC774 \uC774 \uC11C\uBC84\uB85C \uC804\uC1A1\uB429\uB2C8\uB2E4.`
3910
+ );
3911
+ return;
3912
+ }
3913
+ warn(`\uC774 \uC800\uC7A5\uC18C\uC758 .connectbaserc \uAC00 \uC11C\uBC84 URL \uC744 ${target} \uB85C \uC9C0\uC815\uD569\uB2C8\uB2E4.`);
3914
+ log(
3915
+ " \uACC4\uC18D\uD558\uBA74 Secret Key(CONNECTBASE_SECRET_KEY \uD3EC\uD568)\uAC00 \uC774 \uC11C\uBC84\uB85C \uC804\uC1A1\uB429\uB2C8\uB2E4. \uBC1B\uC740 \uC800\uC7A5\uC18C\uB77C\uBA74 \uC911\uB2E8\uD558\uC138\uC694."
3916
+ );
3917
+ if (decision === "confirm") {
3918
+ const answer = await prompt(` ${target} \uB85C \uACC4\uC18D\uD560\uAE4C\uC694? (y/N): `);
3919
+ if (answer.toLowerCase() === "y") return;
3920
+ error("\uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4.");
3921
+ process.exit(1);
3922
+ }
3923
+ error("\uBE44\uB300\uD654\uD615 \uD658\uACBD\uC774\uB77C \uD655\uC778\uD560 \uC218 \uC5C6\uC5B4 \uC911\uB2E8\uD569\uB2C8\uB2E4.");
3924
+ log(
3925
+ ` \uC774 \uC11C\uBC84\uAC00 \uB9DE\uB2E4\uBA74 ${colors.cyan}--allow-repo-base-url${colors.reset} \uB97C \uBD99\uC774\uAC70\uB098 ${colors.cyan}--base-url${colors.reset}, ${colors.cyan}CONNECTBASE_BASE_URL${colors.reset} \uB85C \uC9C1\uC811 \uC9C0\uC815\uD558\uC138\uC694.`
3926
+ );
3927
+ process.exit(1);
3928
+ }
3929
+ function isOutsideProject(dir, root) {
3930
+ const relative2 = path2.relative(path2.resolve(root), path2.resolve(root, dir));
3931
+ return relative2 === ".." || relative2.startsWith(`..${path2.sep}`) || path2.isAbsolute(relative2);
3932
+ }
3763
3933
  function parseArgs(args) {
3764
3934
  const result = {
3765
3935
  options: {},
@@ -3776,6 +3946,8 @@ function parseArgs(args) {
3776
3946
  result.options.secretKey = args[++i];
3777
3947
  } else if (arg === "-u" || arg === "--base-url") {
3778
3948
  result.options.baseUrl = args[++i];
3949
+ } else if (arg === "--allow-repo-base-url") {
3950
+ result.options.allowRepoBaseUrl = "true";
3779
3951
  } else if (arg === "-t" || arg === "--timeout") {
3780
3952
  result.options.timeout = args[++i];
3781
3953
  } else if (arg === "--max-body") {
@@ -3840,8 +4012,12 @@ async function main() {
3840
4012
  publicKey: parsed.options.publicKey || fileConfig.publicKey,
3841
4013
  secretKey: parsed.options.secretKey || fileConfig.secretKey,
3842
4014
  storageId: parsed.options.storageId || fileConfig.storageId,
3843
- baseUrl: parsed.options.baseUrl || fileConfig.baseUrl || DEFAULT_BASE_URL
4015
+ baseUrl: parsed.options.baseUrl || fileConfig.baseUrl
3844
4016
  };
4017
+ await enforceBaseUrlPolicy(config.baseUrl, {
4018
+ fromRepoConfig: !parsed.options.baseUrl && fileConfig.baseUrlFromRepo,
4019
+ allowRepoBaseUrl: parsed.options.allowRepoBaseUrl === "true"
4020
+ });
3845
4021
  if (parsed.command === "init") {
3846
4022
  await init(config, {
3847
4023
  yes: parsed.options.yes === "true",
@@ -3863,6 +4039,13 @@ async function main() {
3863
4039
  await setupMcp(config.secretKey, config.baseUrl);
3864
4040
  } else if (parsed.command === "deploy") {
3865
4041
  const directory = parsed.args[0] || fileConfig.deployDir || ".";
4042
+ if (!parsed.args[0] && fileConfig.deployDir && isOutsideProject(fileConfig.deployDir, getProjectRoot())) {
4043
+ error(
4044
+ `.connectbaserc \uC758 deployDir \uC774 \uD504\uB85C\uC81D\uD2B8 \uBC16\uC744 \uAC00\uB9AC\uCF1C \uBC30\uD3EC\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4: ${JSON.stringify(fileConfig.deployDir)}`
4045
+ );
4046
+ log(" \uC774 \uD3F4\uB354\uAC00 \uB9DE\uB2E4\uBA74 \uBA85\uB839\uC904\uC5D0\uC11C \uC9C1\uC811 \uC9C0\uC815\uD558\uC138\uC694: deploy <\uD3F4\uB354>");
4047
+ process.exit(1);
4048
+ }
3866
4049
  if (!config.publicKey) {
3867
4050
  error(
3868
4051
  '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'
@@ -3981,12 +4164,18 @@ main().catch((err) => {
3981
4164
  });
3982
4165
  // Annotate the CommonJS export names for ESM import in node:
3983
4166
  0 && (module.exports = {
4167
+ DEPLOY_REQUEST_BUDGET_BYTES,
3984
4168
  TunnelConnectSelector,
3985
4169
  ancestorCommandLines,
3986
4170
  apiErrorMessage,
4171
+ browserAuthStartBody,
4172
+ browserAuthUrl,
4173
+ browserOpenCommand,
3987
4174
  buildFirstHint,
3988
4175
  collectDeployFiles,
3989
4176
  computeDeployDiff,
4177
+ decideBaseUrl,
4178
+ describeBaseUrl,
3990
4179
  detectPackageManager,
3991
4180
  formatSkippedFilesReport,
3992
4181
  getTunnelConnectCandidates,
@@ -3994,13 +4183,19 @@ main().catch((err) => {
3994
4183
  isAllowedDeployExt,
3995
4184
  isBinaryDeployExt,
3996
4185
  isExtensionlessDataFile,
4186
+ isOutsideProject,
4187
+ isPlatformBaseUrl,
4188
+ loadConfig,
3997
4189
  makeRequest,
3998
4190
  normalizeRelativePath,
4191
+ openBrowser,
3999
4192
  packageManagerCommands,
4000
4193
  packageScriptInvocationPattern,
4001
4194
  parseArgs,
4002
4195
  registerEndpointBinding,
4003
4196
  runningInsidePackageScript,
4197
+ selectBinariesToOffload,
4004
4198
  sha256Hex,
4005
- storageAdminHeaders
4199
+ storageAdminHeaders,
4200
+ writeSecretFile
4006
4201
  });