connectbase-client 3.24.0 → 3.25.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/README.md +52 -575
- package/dist/cli.js +353 -1748
- package/dist/connect-base.umd.js +2 -4
- package/dist/index.d.mts +361 -3254
- package/dist/index.d.ts +361 -3254
- package/dist/index.js +1081 -4163
- package/dist/index.mjs +1078 -4155
- package/package.json +7 -19
- package/CHANGELOG.md +0 -1884
- package/LICENSE +0 -21
- package/MIGRATION-v2.md +0 -149
package/dist/cli.js
CHANGED
|
@@ -6,10 +6,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
6
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
7
|
var __getProtoOf = Object.getPrototypeOf;
|
|
8
8
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __export = (target, all) => {
|
|
10
|
-
for (var name in all)
|
|
11
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
-
};
|
|
13
9
|
var __copyProps = (to, from, except, desc) => {
|
|
14
10
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
11
|
for (let key of __getOwnPropNames(from))
|
|
@@ -26,114 +22,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
22
|
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
23
|
mod
|
|
28
24
|
));
|
|
29
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
25
|
|
|
31
26
|
// src/cli.ts
|
|
32
|
-
var
|
|
33
|
-
|
|
34
|
-
computeDeployDiff: () => computeDeployDiff,
|
|
35
|
-
normalizeRelativePath: () => normalizeRelativePath,
|
|
36
|
-
parseArgs: () => parseArgs,
|
|
37
|
-
registerEndpointBinding: () => registerEndpointBinding,
|
|
38
|
-
sha256Hex: () => sha256Hex
|
|
39
|
-
});
|
|
40
|
-
module.exports = __toCommonJS(cli_exports);
|
|
41
|
-
var fs2 = __toESM(require("fs"));
|
|
42
|
-
var path2 = __toESM(require("path"));
|
|
27
|
+
var fs = __toESM(require("fs"));
|
|
28
|
+
var path = __toESM(require("path"));
|
|
43
29
|
var crypto = __toESM(require("crypto"));
|
|
44
30
|
var https = __toESM(require("https"));
|
|
45
31
|
var http = __toESM(require("http"));
|
|
46
32
|
var readline = __toESM(require("readline"));
|
|
47
|
-
|
|
48
|
-
// src/tunnel-utils.ts
|
|
49
|
-
var fs = __toESM(require("fs"));
|
|
50
|
-
var path = __toESM(require("path"));
|
|
51
|
-
var os = __toESM(require("os"));
|
|
52
|
-
function getTunnelLockDir() {
|
|
53
|
-
const platform2 = os.platform();
|
|
54
|
-
if (platform2 === "darwin") {
|
|
55
|
-
return path.join(os.homedir(), "Library", "Application Support", "connectbase", "tunnel-locks");
|
|
56
|
-
} else if (platform2 === "win32") {
|
|
57
|
-
return path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "connectbase", "tunnel-locks");
|
|
58
|
-
}
|
|
59
|
-
const stateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state");
|
|
60
|
-
return path.join(stateHome, "connectbase", "tunnel-locks");
|
|
61
|
-
}
|
|
62
|
-
function isProcessAlive(pid) {
|
|
63
|
-
try {
|
|
64
|
-
process.kill(pid, 0);
|
|
65
|
-
return true;
|
|
66
|
-
} catch (e) {
|
|
67
|
-
return e.code === "EPERM";
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
function acquireTunnelLock(appID, port, force, version) {
|
|
71
|
-
const lockDir = getTunnelLockDir();
|
|
72
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
73
|
-
const lockPath = path.join(lockDir, `${appID}-${port}.lock`);
|
|
74
|
-
const lockData = {
|
|
75
|
-
pid: process.pid,
|
|
76
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
77
|
-
appID,
|
|
78
|
-
port,
|
|
79
|
-
host: os.hostname(),
|
|
80
|
-
version
|
|
81
|
-
};
|
|
82
|
-
if (!force) {
|
|
83
|
-
try {
|
|
84
|
-
const fd = fs.openSync(lockPath, "wx");
|
|
85
|
-
fs.writeSync(fd, JSON.stringify(lockData, null, 2));
|
|
86
|
-
fs.closeSync(fd);
|
|
87
|
-
return lockPath;
|
|
88
|
-
} catch (e) {
|
|
89
|
-
if (e.code !== "EEXIST") {
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
try {
|
|
94
|
-
const existing = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
|
|
95
|
-
if (isProcessAlive(existing.pid)) {
|
|
96
|
-
return `LOCKED:${existing.pid}:${existing.startedAt}:${existing.host}`;
|
|
97
|
-
}
|
|
98
|
-
} catch {
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2));
|
|
102
|
-
return lockPath;
|
|
103
|
-
}
|
|
104
|
-
function releaseTunnelLock(lockPath) {
|
|
105
|
-
try {
|
|
106
|
-
fs.unlinkSync(lockPath);
|
|
107
|
-
} catch {
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
function handleTunnelError(msg, appId, localPort) {
|
|
111
|
-
if (msg.code === "replaced") {
|
|
112
|
-
return {
|
|
113
|
-
action: "exit",
|
|
114
|
-
message: `\u26A0 \uB2E4\uB978 \uC138\uC158\uC774 \uAC19\uC740 \uC571+\uD3EC\uD2B8(${appId}:${localPort})\uB85C \uC5F0\uACB0\uB418\uC5B4 \uC774 \uC138\uC158\uC774 \uC885\uB8CC\uB429\uB2C8\uB2E4. \uB3D9\uC77C \uBA38\uC2E0\uC5D0\uC11C \uB450 \uBC88 \uC2E4\uD589\uD558\uC9C0 \uB9C8\uC138\uC694.`,
|
|
115
|
-
exitCode: 1
|
|
116
|
-
};
|
|
117
|
-
}
|
|
118
|
-
return {
|
|
119
|
-
action: "warn",
|
|
120
|
-
message: `\uD130\uB110 \uC5D0\uB7EC: ${msg.error || msg.message || "\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC"}`
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// src/cli.ts
|
|
125
|
-
function getPackageVersion() {
|
|
126
|
-
try {
|
|
127
|
-
const pkgPath = path2.join(__dirname, "..", "package.json");
|
|
128
|
-
if (fs2.existsSync(pkgPath)) {
|
|
129
|
-
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
130
|
-
return pkg.version || "0.0.0";
|
|
131
|
-
}
|
|
132
|
-
} catch {
|
|
133
|
-
}
|
|
134
|
-
return "0.15.1";
|
|
135
|
-
}
|
|
136
|
-
var VERSION = getPackageVersion();
|
|
33
|
+
var VERSION = "3.25.0";
|
|
137
34
|
var DEFAULT_BASE_URL = "https://api.connectbase.world";
|
|
138
35
|
var colors = {
|
|
139
36
|
reset: "\x1B[0m",
|
|
@@ -214,27 +111,17 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
214
111
|
".zip",
|
|
215
112
|
".wasm"
|
|
216
113
|
]);
|
|
217
|
-
function sha256Hex(value) {
|
|
218
|
-
return crypto.createHash("sha256").update(value, "utf8").digest("hex");
|
|
219
|
-
}
|
|
220
|
-
function normalizeRelativePath(relativePath) {
|
|
221
|
-
let p = relativePath.replace(/\\/g, "/");
|
|
222
|
-
if (!p.startsWith("/")) p = `/${p}`;
|
|
223
|
-
return p;
|
|
224
|
-
}
|
|
225
114
|
function loadConfig() {
|
|
226
115
|
const config = {
|
|
227
|
-
|
|
228
|
-
secretKey: process.env.CONNECTBASE_SECRET_KEY,
|
|
116
|
+
apiKey: process.env.CONNECTBASE_API_KEY,
|
|
229
117
|
storageId: process.env.CONNECTBASE_STORAGE_ID,
|
|
230
118
|
baseUrl: process.env.CONNECTBASE_BASE_URL || DEFAULT_BASE_URL
|
|
231
119
|
};
|
|
232
|
-
const rcPath =
|
|
233
|
-
if (
|
|
120
|
+
const rcPath = path.join(process.cwd(), ".connectbaserc");
|
|
121
|
+
if (fs.existsSync(rcPath)) {
|
|
234
122
|
try {
|
|
235
|
-
const rcContent = JSON.parse(
|
|
236
|
-
if (rcContent.
|
|
237
|
-
if (rcContent.secretKey) config.secretKey = rcContent.secretKey;
|
|
123
|
+
const rcContent = JSON.parse(fs.readFileSync(rcPath, "utf-8"));
|
|
124
|
+
if (rcContent.apiKey) config.apiKey = rcContent.apiKey;
|
|
238
125
|
if (rcContent.storageId) config.storageId = rcContent.storageId;
|
|
239
126
|
if (rcContent.baseUrl) config.baseUrl = rcContent.baseUrl;
|
|
240
127
|
if (rcContent.deployDir) config.deployDir = rcContent.deployDir;
|
|
@@ -246,16 +133,16 @@ function loadConfig() {
|
|
|
246
133
|
}
|
|
247
134
|
function collectFiles(dir, baseDir = dir) {
|
|
248
135
|
const files = [];
|
|
249
|
-
const entries =
|
|
136
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
250
137
|
for (const entry of entries) {
|
|
251
|
-
const fullPath =
|
|
252
|
-
const relativePath =
|
|
138
|
+
const fullPath = path.join(dir, entry.name);
|
|
139
|
+
const relativePath = path.relative(baseDir, fullPath);
|
|
253
140
|
if (entry.isDirectory()) {
|
|
254
141
|
if (!entry.name.startsWith(".")) {
|
|
255
142
|
files.push(...collectFiles(fullPath, baseDir));
|
|
256
143
|
}
|
|
257
144
|
} else if (entry.isFile()) {
|
|
258
|
-
const ext =
|
|
145
|
+
const ext = path.extname(entry.name).toLowerCase();
|
|
259
146
|
if (!ALLOWED_EXTENSIONS.has(ext)) {
|
|
260
147
|
continue;
|
|
261
148
|
}
|
|
@@ -265,22 +152,22 @@ function collectFiles(dir, baseDir = dir) {
|
|
|
265
152
|
const isBinary = BINARY_EXTENSIONS.has(ext);
|
|
266
153
|
let content;
|
|
267
154
|
if (isBinary) {
|
|
268
|
-
content =
|
|
155
|
+
content = fs.readFileSync(fullPath).toString("base64");
|
|
269
156
|
} else {
|
|
270
|
-
content =
|
|
157
|
+
content = fs.readFileSync(fullPath, "utf-8");
|
|
271
158
|
}
|
|
272
|
-
const normalized = normalizeRelativePath(relativePath);
|
|
273
159
|
files.push({
|
|
274
|
-
path:
|
|
160
|
+
path: relativePath.replace(/\\/g, "/"),
|
|
161
|
+
// Windows 경로 변환
|
|
275
162
|
content,
|
|
276
|
-
isBinary
|
|
277
|
-
hash: sha256Hex(content)
|
|
163
|
+
isBinary
|
|
278
164
|
});
|
|
279
165
|
}
|
|
280
166
|
}
|
|
281
167
|
return files;
|
|
282
168
|
}
|
|
283
|
-
|
|
169
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 6e4;
|
|
170
|
+
async function makeRequest(url, method, headers, body, reqOpts = {}) {
|
|
284
171
|
return new Promise((resolve2, reject) => {
|
|
285
172
|
const parsedUrl = new URL(url);
|
|
286
173
|
const isHttps = parsedUrl.protocol === "https:";
|
|
@@ -313,8 +200,16 @@ async function makeRequest(url, method, headers, body) {
|
|
|
313
200
|
}
|
|
314
201
|
});
|
|
315
202
|
});
|
|
316
|
-
req.
|
|
317
|
-
|
|
203
|
+
req.on("socket", (socket) => {
|
|
204
|
+
socket.setKeepAlive(true, 3e4);
|
|
205
|
+
});
|
|
206
|
+
const timeoutMs = reqOpts.timeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
207
|
+
req.setTimeout(timeoutMs, () => {
|
|
208
|
+
req.destroy(
|
|
209
|
+
new Error(
|
|
210
|
+
`\uC694\uCCAD \uC2DC\uAC04 \uCD08\uACFC (${Math.round(timeoutMs / 1e3)}\uCD08). \uD070 \uC0AC\uC774\uD2B8\uB294 --timeout <\uCD08> \uB610\uB294 CONNECTBASE_DEPLOY_TIMEOUT \uD658\uACBD\uBCC0\uC218\uB85C \uB298\uB9AC\uC138\uC694.`
|
|
211
|
+
)
|
|
212
|
+
);
|
|
318
213
|
});
|
|
319
214
|
req.on("error", reject);
|
|
320
215
|
if (bodyBuffer) {
|
|
@@ -323,29 +218,39 @@ async function makeRequest(url, method, headers, body) {
|
|
|
323
218
|
req.end();
|
|
324
219
|
});
|
|
325
220
|
}
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
221
|
+
var DEPLOY_TIMEOUT_BASE_MS = 5 * 6e4;
|
|
222
|
+
var DEPLOY_TIMEOUT_PER_10MB_MS = 6e4;
|
|
223
|
+
var DEPLOY_TIMEOUT_MIN_MS = 10 * 6e4;
|
|
224
|
+
function computeDeployTimeout(totalBytes, override) {
|
|
225
|
+
if (override && override > 0) return override;
|
|
226
|
+
const envOverride = process.env.CONNECTBASE_DEPLOY_TIMEOUT ? parseInt(process.env.CONNECTBASE_DEPLOY_TIMEOUT, 10) * 1e3 : 0;
|
|
227
|
+
if (envOverride > 0) return envOverride;
|
|
228
|
+
const sizeBased = Math.ceil(totalBytes / (10 * 1024 * 1024)) * DEPLOY_TIMEOUT_PER_10MB_MS;
|
|
229
|
+
return Math.max(DEPLOY_TIMEOUT_MIN_MS, DEPLOY_TIMEOUT_BASE_MS + sizeBased);
|
|
230
|
+
}
|
|
231
|
+
async function deploy(directory, config, isDev = false, deployOpts = {}) {
|
|
232
|
+
const dir = path.resolve(directory);
|
|
233
|
+
if (!fs.existsSync(dir)) {
|
|
329
234
|
error(`\uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);
|
|
330
235
|
process.exit(1);
|
|
331
236
|
}
|
|
332
|
-
const pkgPath =
|
|
333
|
-
if (
|
|
237
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
238
|
+
if (fs.existsSync(pkgPath)) {
|
|
334
239
|
try {
|
|
335
|
-
const pkg = JSON.parse(
|
|
240
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
336
241
|
if (pkg.scripts?.build) {
|
|
337
|
-
warn(`\uBC30\uD3EC \uC804 \uBE4C\uB4DC\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: ${colors.cyan}npm run build && npx connectbase deploy${colors.reset}`);
|
|
338
|
-
warn(`\uB610\uB294 package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB4F1\uB85D\uD558\uC138\uC694: ${colors.cyan}npx connectbase init${colors.reset}`);
|
|
242
|
+
warn(`\uBC30\uD3EC \uC804 \uBE4C\uB4DC\uB97C \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: ${colors.cyan}npm run build && npx connectbase-client deploy${colors.reset}`);
|
|
243
|
+
warn(`\uB610\uB294 package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB4F1\uB85D\uD558\uC138\uC694: ${colors.cyan}npx connectbase-client init${colors.reset}`);
|
|
339
244
|
}
|
|
340
245
|
} catch {
|
|
341
246
|
}
|
|
342
247
|
}
|
|
343
|
-
if (!
|
|
248
|
+
if (!fs.statSync(dir).isDirectory()) {
|
|
344
249
|
error(`${dir}\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC544\uB2D9\uB2C8\uB2E4`);
|
|
345
250
|
process.exit(1);
|
|
346
251
|
}
|
|
347
|
-
const indexPath =
|
|
348
|
-
if (!
|
|
252
|
+
const indexPath = path.join(dir, "index.html");
|
|
253
|
+
if (!fs.existsSync(indexPath)) {
|
|
349
254
|
error("index.html \uD30C\uC77C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
350
255
|
process.exit(1);
|
|
351
256
|
}
|
|
@@ -360,133 +265,60 @@ async function deploy(directory, config, isDev = false) {
|
|
|
360
265
|
const totalSize = files.reduce((sum, f) => sum + f.content.length, 0);
|
|
361
266
|
const sizeKB = (totalSize / 1024).toFixed(1);
|
|
362
267
|
log(`${colors.green}\u2713${colors.reset} ${files.length}\uAC1C \uD30C\uC77C \uBC1C\uACAC (${sizeKB} KB)`);
|
|
268
|
+
const deployPath = isDev ? "deploy/dev" : "deploy";
|
|
269
|
+
const url = `${config.baseUrl}/v1/public/storages/webs/${config.storageId}/${deployPath}`;
|
|
363
270
|
const envLabel = isDev ? "Dev" : "Prod";
|
|
364
|
-
const
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
271
|
+
const timeoutMs = computeDeployTimeout(totalSize, deployOpts.timeoutMs);
|
|
272
|
+
log(`${colors.dim}\uD0C0\uC784\uC544\uC6C3: ${Math.round(timeoutMs / 1e3)}\uCD08${colors.reset}`);
|
|
273
|
+
process.stdout.write(`${colors.blue}\u27F3${colors.reset} ${envLabel} \uBC30\uD3EC \uC911...`);
|
|
274
|
+
let heartbeat = null;
|
|
275
|
+
if (process.stdout.isTTY) {
|
|
276
|
+
const startedAt = Date.now();
|
|
277
|
+
heartbeat = setInterval(() => {
|
|
278
|
+
const elapsed = Math.floor((Date.now() - startedAt) / 1e3);
|
|
279
|
+
process.stdout.write(`\r${colors.blue}\u27F3${colors.reset} ${envLabel} \uBC30\uD3EC \uC911... ${colors.dim}(${elapsed}s)${colors.reset}`);
|
|
280
|
+
}, 1e3);
|
|
369
281
|
}
|
|
370
282
|
try {
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
}
|
|
394
|
-
async function tryFetchManifest(baseStorageUrl, headers) {
|
|
395
|
-
const res = await makeRequest(`${baseStorageUrl}/deploy/manifest`, "GET", headers);
|
|
396
|
-
if (res.status === 404) return null;
|
|
397
|
-
if (res.status < 200 || res.status >= 300) {
|
|
398
|
-
const data = res.data;
|
|
399
|
-
const msg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : `HTTP ${res.status}`;
|
|
400
|
-
throw new Error(`manifest \uC870\uD68C \uC2E4\uD328: ${msg}`);
|
|
401
|
-
}
|
|
402
|
-
const parsed = res.data;
|
|
403
|
-
if (!parsed || !Array.isArray(parsed.files)) {
|
|
404
|
-
return null;
|
|
405
|
-
}
|
|
406
|
-
return parsed;
|
|
407
|
-
}
|
|
408
|
-
function computeDeployDiff(local, manifest) {
|
|
409
|
-
const localByPath = /* @__PURE__ */ new Map();
|
|
410
|
-
for (const f of local) localByPath.set(f.path, f);
|
|
411
|
-
const remoteByPath = /* @__PURE__ */ new Map();
|
|
412
|
-
for (const m of manifest.files) remoteByPath.set(m.path, m);
|
|
413
|
-
const upsert = [];
|
|
414
|
-
for (const f of local) {
|
|
415
|
-
const remote = remoteByPath.get(f.path);
|
|
416
|
-
if (!remote || remote.hash !== f.hash || !remote.hash) {
|
|
417
|
-
upsert.push({
|
|
418
|
-
path: f.path,
|
|
419
|
-
content: f.content,
|
|
420
|
-
is_binary: f.isBinary,
|
|
421
|
-
hash: f.hash
|
|
422
|
-
});
|
|
423
|
-
}
|
|
424
|
-
}
|
|
425
|
-
const toDelete = [];
|
|
426
|
-
for (const m of manifest.files) {
|
|
427
|
-
if (!localByPath.has(m.path)) toDelete.push(m.path);
|
|
428
|
-
}
|
|
429
|
-
return { upsert, delete: toDelete };
|
|
430
|
-
}
|
|
431
|
-
async function incrementalDeploy(baseStorageUrl, headers, diff, baseRevision, envLabel) {
|
|
432
|
-
process.stdout.write(`${colors.blue}\u27F3${colors.reset} ${envLabel} \uC99D\uBD84 \uBC30\uD3EC \uC911...`);
|
|
433
|
-
const body = JSON.stringify({
|
|
434
|
-
upsert: diff.upsert,
|
|
435
|
-
delete: diff.delete,
|
|
436
|
-
base_revision: baseRevision
|
|
437
|
-
});
|
|
438
|
-
const res = await makeRequest(`${baseStorageUrl}/deploy/incremental`, "POST", headers, body);
|
|
439
|
-
process.stdout.write("\r \r");
|
|
440
|
-
if (res.status === 409) {
|
|
441
|
-
warn("\uC11C\uBC84 revision \uC774 \uBCC0\uACBD\uB418\uC5C8\uC2B5\uB2C8\uB2E4. manifest \uC7AC\uC870\uD68C \uD6C4 \uC7AC\uC2DC\uB3C4\uD569\uB2C8\uB2E4.");
|
|
442
|
-
const manifest = await tryFetchManifest(baseStorageUrl, headers);
|
|
443
|
-
if (!manifest) {
|
|
444
|
-
error("\uC7AC\uC2DC\uB3C4 manifest \uC870\uD68C \uC2E4\uD328");
|
|
445
|
-
process.exit(1);
|
|
446
|
-
}
|
|
447
|
-
const body2 = JSON.stringify({
|
|
448
|
-
upsert: diff.upsert,
|
|
449
|
-
delete: diff.delete,
|
|
450
|
-
base_revision: manifest.revision
|
|
451
|
-
});
|
|
452
|
-
process.stdout.write(`${colors.blue}\u27F3${colors.reset} ${envLabel} \uC99D\uBD84 \uBC30\uD3EC \uC7AC\uC2DC\uB3C4...`);
|
|
453
|
-
const res2 = await makeRequest(`${baseStorageUrl}/deploy/incremental`, "POST", headers, body2);
|
|
454
|
-
process.stdout.write("\r \r");
|
|
455
|
-
handleDeployResponse(res2, envLabel);
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
handleDeployResponse(res, envLabel);
|
|
459
|
-
}
|
|
460
|
-
async function fullDeploy(baseStorageUrl, headers, files, envLabel, endpoint) {
|
|
461
|
-
process.stdout.write(`${colors.blue}\u27F3${colors.reset} ${envLabel} \uBC30\uD3EC \uC911...`);
|
|
462
|
-
const body = JSON.stringify({
|
|
463
|
-
files: files.map((f) => ({
|
|
464
|
-
path: f.path,
|
|
465
|
-
content: f.content,
|
|
466
|
-
is_binary: f.isBinary
|
|
467
|
-
}))
|
|
468
|
-
});
|
|
469
|
-
const res = await makeRequest(`${baseStorageUrl}/${endpoint}`, "POST", headers, body);
|
|
470
|
-
process.stdout.write("\r \r");
|
|
471
|
-
handleDeployResponse(res, envLabel);
|
|
472
|
-
}
|
|
473
|
-
function handleDeployResponse(response, envLabel) {
|
|
474
|
-
if (response.status >= 200 && response.status < 300) {
|
|
475
|
-
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
476
|
-
const data = response.data;
|
|
477
|
-
if (data?.dev_url) {
|
|
478
|
-
log(`
|
|
283
|
+
const response = await makeRequest(
|
|
284
|
+
url,
|
|
285
|
+
"POST",
|
|
286
|
+
{
|
|
287
|
+
"X-API-Key": config.apiKey
|
|
288
|
+
},
|
|
289
|
+
JSON.stringify({
|
|
290
|
+
files: files.map((f) => ({
|
|
291
|
+
path: f.path,
|
|
292
|
+
content: f.content,
|
|
293
|
+
is_binary: f.isBinary
|
|
294
|
+
}))
|
|
295
|
+
}),
|
|
296
|
+
{ timeoutMs }
|
|
297
|
+
);
|
|
298
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
299
|
+
process.stdout.write("\r \r");
|
|
300
|
+
if (response.status >= 200 && response.status < 300) {
|
|
301
|
+
success(`${envLabel} \uBC30\uD3EC \uC644\uB8CC!`);
|
|
302
|
+
const data = response.data;
|
|
303
|
+
if (data?.dev_url) {
|
|
304
|
+
log(`
|
|
479
305
|
${colors.yellow}Dev URL: ${data.dev_url}${colors.reset}
|
|
480
306
|
`);
|
|
481
|
-
|
|
482
|
-
|
|
307
|
+
} else if (data?.url) {
|
|
308
|
+
log(`
|
|
483
309
|
${colors.cyan}URL: ${data.url}${colors.reset}
|
|
484
310
|
`);
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
const data = response.data;
|
|
314
|
+
const errorMsg = typeof data === "object" && data !== null ? data.message || data.error || JSON.stringify(data) : typeof data === "string" ? data : `HTTP ${response.status}`;
|
|
315
|
+
error(`\uBC30\uD3EC \uC2E4\uD328: ${errorMsg}`);
|
|
316
|
+
process.exit(1);
|
|
485
317
|
}
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
error(`\
|
|
318
|
+
} catch (err) {
|
|
319
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
320
|
+
process.stdout.write("\r \r");
|
|
321
|
+
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
490
322
|
process.exit(1);
|
|
491
323
|
}
|
|
492
324
|
}
|
|
@@ -502,329 +334,77 @@ function prompt(question) {
|
|
|
502
334
|
});
|
|
503
335
|
});
|
|
504
336
|
}
|
|
505
|
-
function promptSecret(question) {
|
|
506
|
-
return new Promise((resolve2) => {
|
|
507
|
-
process.stdout.write(question);
|
|
508
|
-
const input = [];
|
|
509
|
-
if (!process.stdin.isTTY) {
|
|
510
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
511
|
-
rl.question("", (answer) => {
|
|
512
|
-
rl.close();
|
|
513
|
-
resolve2(answer.trim());
|
|
514
|
-
});
|
|
515
|
-
return;
|
|
516
|
-
}
|
|
517
|
-
process.stdin.setRawMode(true);
|
|
518
|
-
process.stdin.resume();
|
|
519
|
-
process.stdin.setEncoding("utf-8");
|
|
520
|
-
const onData = (data) => {
|
|
521
|
-
for (const char of data) {
|
|
522
|
-
const code = char.charCodeAt(0);
|
|
523
|
-
if (char === "\r" || char === "\n") {
|
|
524
|
-
process.stdin.setRawMode(false);
|
|
525
|
-
process.stdin.pause();
|
|
526
|
-
process.stdin.removeListener("data", onData);
|
|
527
|
-
process.stdout.write("\n");
|
|
528
|
-
resolve2(input.join("").trim());
|
|
529
|
-
return;
|
|
530
|
-
} else if (code === 3) {
|
|
531
|
-
process.stdin.setRawMode(false);
|
|
532
|
-
process.stdin.pause();
|
|
533
|
-
process.stdout.write("\n");
|
|
534
|
-
process.exit(0);
|
|
535
|
-
} else if (code === 127 || code === 8) {
|
|
536
|
-
if (input.length > 0) {
|
|
537
|
-
input.pop();
|
|
538
|
-
process.stdout.write("\b \b");
|
|
539
|
-
}
|
|
540
|
-
} else if (code >= 32) {
|
|
541
|
-
input.push(char);
|
|
542
|
-
process.stdout.write("*");
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
};
|
|
546
|
-
process.stdin.on("data", onData);
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
function getProjectRoot() {
|
|
550
|
-
const gitRoot = getGitRoot();
|
|
551
|
-
return gitRoot || process.cwd();
|
|
552
|
-
}
|
|
553
|
-
var CONSOLE_URL = DEFAULT_BASE_URL.replace("api.", "");
|
|
554
|
-
function openBrowser(url) {
|
|
555
|
-
const { exec } = require("child_process");
|
|
556
|
-
const platform2 = process.platform;
|
|
557
|
-
let command;
|
|
558
|
-
if (platform2 === "darwin") {
|
|
559
|
-
command = `open "${url}"`;
|
|
560
|
-
} else if (platform2 === "win32") {
|
|
561
|
-
command = `start "" "${url}"`;
|
|
562
|
-
} else {
|
|
563
|
-
command = `xdg-open "${url}"`;
|
|
564
|
-
}
|
|
565
|
-
exec(command, () => {
|
|
566
|
-
});
|
|
567
|
-
}
|
|
568
|
-
async function browserAuthFlow() {
|
|
569
|
-
info("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D \uC138\uC158\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
570
|
-
const startRes = await makeRequest(
|
|
571
|
-
`${DEFAULT_BASE_URL}/v1/public/cli-auth/start`,
|
|
572
|
-
"POST",
|
|
573
|
-
{}
|
|
574
|
-
);
|
|
575
|
-
if (startRes.status !== 200) {
|
|
576
|
-
const errData = startRes.data;
|
|
577
|
-
const detail = errData?.error || errData?.message || `HTTP ${startRes.status}`;
|
|
578
|
-
error(`\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4: ${detail}`);
|
|
579
|
-
process.exit(1);
|
|
580
|
-
}
|
|
581
|
-
const { session_id } = startRes.data;
|
|
582
|
-
const authUrl = `${CONSOLE_URL}/auth/cli-auth?session=${session_id}`;
|
|
583
|
-
log(`
|
|
584
|
-
${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC138\uC694:${colors.reset}`);
|
|
585
|
-
log(`${colors.dim}${authUrl}${colors.reset}
|
|
586
|
-
`);
|
|
587
|
-
openBrowser(authUrl);
|
|
588
|
-
const pollInterval = 3e3;
|
|
589
|
-
const maxAttempts = 100;
|
|
590
|
-
let attempts = 0;
|
|
591
|
-
let consecutive404 = 0;
|
|
592
|
-
const max404Retries = 5;
|
|
593
|
-
const spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
594
|
-
while (attempts < maxAttempts) {
|
|
595
|
-
const frame = spinnerFrames[attempts % spinnerFrames.length];
|
|
596
|
-
process.stdout.write(`\r${colors.blue}${frame}${colors.reset} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC2B9\uC778 \uB300\uAE30 \uC911...`);
|
|
597
|
-
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
598
|
-
try {
|
|
599
|
-
const pollRes = await makeRequest(
|
|
600
|
-
`${DEFAULT_BASE_URL}/v1/public/cli-auth/poll/${session_id}`,
|
|
601
|
-
"GET",
|
|
602
|
-
{}
|
|
603
|
-
);
|
|
604
|
-
if (pollRes.status === 200) {
|
|
605
|
-
consecutive404 = 0;
|
|
606
|
-
const data = pollRes.data;
|
|
607
|
-
if (data.status === "approved" && data.secret_key) {
|
|
608
|
-
process.stdout.write("\r \r");
|
|
609
|
-
success("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!");
|
|
610
|
-
return data.secret_key;
|
|
611
|
-
}
|
|
612
|
-
if (data.status === "expired") {
|
|
613
|
-
process.stdout.write("\r \r");
|
|
614
|
-
error("\uC778\uC99D \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
615
|
-
process.exit(1);
|
|
616
|
-
}
|
|
617
|
-
if (data.status === "denied") {
|
|
618
|
-
process.stdout.write("\r \r");
|
|
619
|
-
error("\uC778\uC99D\uC774 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
620
|
-
process.exit(1);
|
|
621
|
-
}
|
|
622
|
-
}
|
|
623
|
-
if (pollRes.status === 404) {
|
|
624
|
-
consecutive404++;
|
|
625
|
-
if (consecutive404 >= max404Retries) {
|
|
626
|
-
process.stdout.write("\r \r");
|
|
627
|
-
error("\uC778\uC99D \uC138\uC158\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
628
|
-
process.exit(1);
|
|
629
|
-
}
|
|
630
|
-
}
|
|
631
|
-
} catch {
|
|
632
|
-
}
|
|
633
|
-
attempts++;
|
|
634
|
-
}
|
|
635
|
-
process.stdout.write("\r \r");
|
|
636
|
-
error("\uC778\uC99D \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
637
|
-
process.exit(1);
|
|
638
|
-
}
|
|
639
337
|
async function init() {
|
|
640
338
|
log(`
|
|
641
339
|
${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.reset}
|
|
642
340
|
`);
|
|
643
|
-
const
|
|
644
|
-
|
|
645
|
-
if (cwd !== projectRoot) {
|
|
646
|
-
info(`\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8 \uAC10\uC9C0: ${projectRoot}`);
|
|
647
|
-
info(`MCP/\uBB38\uC11C\uB294 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0, \uBC30\uD3EC \uC124\uC815\uC740 \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4
|
|
648
|
-
`);
|
|
649
|
-
}
|
|
650
|
-
const rcPath = path2.join(cwd, ".connectbaserc");
|
|
651
|
-
if (fs2.existsSync(rcPath)) {
|
|
341
|
+
const rcPath = path.join(process.cwd(), ".connectbaserc");
|
|
342
|
+
if (fs.existsSync(rcPath)) {
|
|
652
343
|
const overwrite = await prompt(`${colors.yellow}\u26A0${colors.reset} .connectbaserc\uAC00 \uC774\uBBF8 \uC874\uC7AC\uD569\uB2C8\uB2E4. \uB36E\uC5B4\uC4F0\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? (y/N): `);
|
|
653
344
|
if (overwrite.toLowerCase() !== "y") {
|
|
654
345
|
info("\uCD08\uAE30\uD654\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4");
|
|
655
346
|
return;
|
|
656
347
|
}
|
|
657
348
|
}
|
|
658
|
-
log(`
|
|
659
|
-
|
|
660
|
-
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
661
|
-
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
662
|
-
`);
|
|
663
|
-
const authChoice = await prompt(`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `);
|
|
664
|
-
let secretKey = "";
|
|
665
|
-
if (authChoice === "2") {
|
|
666
|
-
try {
|
|
667
|
-
secretKey = await browserAuthFlow();
|
|
668
|
-
} catch (err) {
|
|
669
|
-
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
670
|
-
info("\uC778\uD130\uB137 \uC5F0\uACB0\uC744 \uD655\uC778\uD558\uAC70\uB098, Secret Key \uC9C1\uC811 \uC785\uB825(\uC635\uC158 1)\uC744 \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
671
|
-
process.exit(1);
|
|
672
|
-
}
|
|
673
|
-
} else {
|
|
674
|
-
log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
349
|
+
log(`${colors.dim}Public Key (cb_pk_): SDK\uC6A9 \u2014 \uC6F9/\uC571\uC5D0\uC11C \uC0AC\uC6A9${colors.reset}`);
|
|
350
|
+
log(`${colors.dim}Secret Key (cb_sk_): MCP/\uAD00\uB9AC\uC790\uC6A9 \u2014 \uC808\uB300 \uB178\uCD9C \uAE08\uC9C0${colors.reset}
|
|
675
351
|
`);
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
}
|
|
681
|
-
if (secretKey.startsWith("cb_pk_")) {
|
|
682
|
-
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
683
|
-
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
684
|
-
process.exit(1);
|
|
685
|
-
}
|
|
686
|
-
if (!secretKey.startsWith("cb_sk_")) {
|
|
687
|
-
error("\uC720\uD6A8\uD558\uC9C0 \uC54A\uC740 \uD0A4 \uD615\uC2DD\uC785\uB2C8\uB2E4. cb_sk_\uB85C \uC2DC\uC791\uD558\uB294 Secret Key\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
688
|
-
process.exit(1);
|
|
689
|
-
}
|
|
352
|
+
const apiKey = await prompt(`${colors.blue}?${colors.reset} API Key (Public Key \uAD8C\uC7A5): `);
|
|
353
|
+
if (!apiKey) {
|
|
354
|
+
error("API Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
355
|
+
process.exit(1);
|
|
690
356
|
}
|
|
691
|
-
|
|
692
|
-
|
|
357
|
+
if (apiKey.startsWith("cb_sk_")) {
|
|
358
|
+
warn("Secret Key\uAC00 \uC785\uB825\uB418\uC5C8\uC2B5\uB2C8\uB2E4. SDK\uC5D0\uB294 Public Key (cb_pk_) \uC0AC\uC6A9\uC744 \uAD8C\uC7A5\uD569\uB2C8\uB2E4.");
|
|
359
|
+
warn("Secret Key\uB294 \uC804\uCCB4 \uAD8C\uD55C\uC744 \uAC00\uC9C0\uBBC0\uB85C \uCF54\uB4DC\uC5D0 \uB178\uCD9C\uB418\uC9C0 \uC54A\uB3C4\uB85D \uC8FC\uC758\uD558\uC138\uC694.");
|
|
360
|
+
}
|
|
361
|
+
let storageId = "";
|
|
693
362
|
try {
|
|
694
|
-
info("\
|
|
695
|
-
const
|
|
696
|
-
`${DEFAULT_BASE_URL}/v1/public/
|
|
363
|
+
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC870\uD68C \uC911...");
|
|
364
|
+
const listRes = await makeRequest(
|
|
365
|
+
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
697
366
|
"GET",
|
|
698
|
-
{ "X-
|
|
367
|
+
{ "X-API-Key": apiKey }
|
|
699
368
|
);
|
|
700
|
-
if (
|
|
701
|
-
error(
|
|
369
|
+
if (listRes.status !== 200) {
|
|
370
|
+
error(`API Key \uC778\uC99D \uC2E4\uD328 (${listRes.status})`);
|
|
702
371
|
process.exit(1);
|
|
703
372
|
}
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
const appsData = appsRes.data;
|
|
708
|
-
const apps = appsData.apps || [];
|
|
709
|
-
if (apps.length > 0) {
|
|
373
|
+
const listData = listRes.data;
|
|
374
|
+
const storages = listData.storages || [];
|
|
375
|
+
if (storages.length > 0) {
|
|
710
376
|
log(`
|
|
711
|
-
${colors.dim}\
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
log(` ${colors.cyan}${i + 1}${colors.reset}) ${a.name} ${colors.dim}(${date})${colors.reset}`);
|
|
377
|
+
${colors.dim}\uAE30\uC874 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0:${colors.reset}`);
|
|
378
|
+
storages.forEach((s, i) => {
|
|
379
|
+
log(` ${colors.cyan}${i + 1}${colors.reset}) ${s.name} (${colors.dim}${s.id}${colors.reset})`);
|
|
715
380
|
});
|
|
716
|
-
log(` ${colors.cyan}0${colors.reset}) \uC0C8
|
|
381
|
+
log(` ${colors.cyan}0${colors.reset}) \uC0C8\uB85C \uC0DD\uC131`);
|
|
717
382
|
const choice = await prompt(`
|
|
718
383
|
${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
719
384
|
const num = parseInt(choice, 10);
|
|
720
|
-
if (num > 0 && num <=
|
|
721
|
-
|
|
722
|
-
success(`\uC120\uD0DD\uB428: ${
|
|
723
|
-
info("Public Key \uC0DD\uC131 \uC911...");
|
|
724
|
-
const newKeyRes = await makeRequest(
|
|
725
|
-
`${DEFAULT_BASE_URL}/v1/public/cli/apps/${appId}/public-keys`,
|
|
726
|
-
"POST",
|
|
727
|
-
{ "X-Public-Key": secretKey },
|
|
728
|
-
JSON.stringify({})
|
|
729
|
-
);
|
|
730
|
-
if (newKeyRes.status === 201) {
|
|
731
|
-
const keyData = newKeyRes.data;
|
|
732
|
-
publicKey = keyData.key;
|
|
733
|
-
success("Public Key \uBC1C\uAE09 \uC644\uB8CC");
|
|
734
|
-
} else {
|
|
735
|
-
warn("Public Key \uC790\uB3D9 \uC0DD\uC131 \uC2E4\uD328. \uC218\uB3D9\uC73C\uB85C \uC0DD\uC131\uD558\uC138\uC694");
|
|
736
|
-
}
|
|
385
|
+
if (num > 0 && num <= storages.length) {
|
|
386
|
+
storageId = storages[num - 1].id;
|
|
387
|
+
success(`\uC120\uD0DD\uB428: ${storages[num - 1].name}`);
|
|
737
388
|
}
|
|
738
389
|
}
|
|
739
|
-
if (!
|
|
740
|
-
const projectName =
|
|
741
|
-
const
|
|
742
|
-
info("\
|
|
390
|
+
if (!storageId) {
|
|
391
|
+
const projectName = path.basename(process.cwd());
|
|
392
|
+
const name = await prompt(`${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `) || projectName;
|
|
393
|
+
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
743
394
|
const createRes = await makeRequest(
|
|
744
|
-
`${DEFAULT_BASE_URL}/v1/public/
|
|
395
|
+
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
745
396
|
"POST",
|
|
746
|
-
{ "X-
|
|
747
|
-
JSON.stringify({ name
|
|
397
|
+
{ "X-API-Key": apiKey },
|
|
398
|
+
JSON.stringify({ name })
|
|
748
399
|
);
|
|
749
|
-
if (createRes.status
|
|
750
|
-
error("\uC571 \uC0DD\uC131 \uD55C\uB3C4 \uCD08\uACFC. \uD50C\uB79C \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
751
|
-
process.exit(1);
|
|
752
|
-
}
|
|
753
|
-
if (createRes.status !== 201) {
|
|
400
|
+
if (createRes.status !== 200) {
|
|
754
401
|
const data = createRes.data;
|
|
755
|
-
error(`\
|
|
402
|
+
error(`\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`);
|
|
756
403
|
process.exit(1);
|
|
757
404
|
}
|
|
758
405
|
const createData = createRes.data;
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
|
|
762
|
-
if (publicKey) {
|
|
763
|
-
success("Public Key \uC790\uB3D9 \uBC1C\uAE09 \uC644\uB8CC");
|
|
764
|
-
}
|
|
765
|
-
}
|
|
766
|
-
} catch (err) {
|
|
767
|
-
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
768
|
-
log(`${colors.dim}\uC218\uB3D9\uC73C\uB85C \uC785\uB825\uD558\uC138\uC694${colors.reset}`);
|
|
769
|
-
appId = await prompt(`${colors.blue}?${colors.reset} App ID: `);
|
|
770
|
-
publicKey = await prompt(`${colors.blue}?${colors.reset} Public Key (cb_pk_): `);
|
|
771
|
-
if (!appId || !publicKey) {
|
|
772
|
-
error("App ID\uC640 Public Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
773
|
-
process.exit(1);
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
if (!publicKey) {
|
|
777
|
-
error("Public Key\uAC00 \uC5C6\uC5B4 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uB97C \uC870\uD68C\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uCF58\uC194\uC5D0\uC11C Public Key\uB97C \uBC1C\uAE09\uBC1B\uC544 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694");
|
|
778
|
-
process.exit(1);
|
|
779
|
-
}
|
|
780
|
-
const publicKeyForSdk = publicKey;
|
|
781
|
-
let storageId = "";
|
|
782
|
-
try {
|
|
783
|
-
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC870\uD68C \uC911...");
|
|
784
|
-
const listRes = await makeRequest(
|
|
785
|
-
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
786
|
-
"GET",
|
|
787
|
-
{ "X-Public-Key": publicKeyForSdk }
|
|
788
|
-
);
|
|
789
|
-
if (listRes.status === 200) {
|
|
790
|
-
const listData = listRes.data;
|
|
791
|
-
const storages = listData.storages || [];
|
|
792
|
-
if (storages.length > 0) {
|
|
793
|
-
log(`
|
|
794
|
-
${colors.dim}\uAE30\uC874 \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0:${colors.reset}`);
|
|
795
|
-
storages.forEach((s, i) => {
|
|
796
|
-
log(` ${colors.cyan}${i + 1}${colors.reset}) ${s.name} (${colors.dim}${s.id}${colors.reset})`);
|
|
797
|
-
});
|
|
798
|
-
log(` ${colors.cyan}0${colors.reset}) \uC0C8\uB85C \uC0DD\uC131`);
|
|
799
|
-
const choice = await prompt(`
|
|
800
|
-
${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
801
|
-
const num = parseInt(choice, 10);
|
|
802
|
-
if (num > 0 && num <= storages.length) {
|
|
803
|
-
storageId = storages[num - 1].id;
|
|
804
|
-
success(`\uC120\uD0DD\uB428: ${storages[num - 1].name}`);
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
if (!storageId) {
|
|
808
|
-
const projectName = path2.basename(cwd);
|
|
809
|
-
const name = await prompt(`${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `) || projectName;
|
|
810
|
-
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
811
|
-
const createRes = await makeRequest(
|
|
812
|
-
`${DEFAULT_BASE_URL}/v1/public/storages/webs`,
|
|
813
|
-
"POST",
|
|
814
|
-
{ "X-Public-Key": publicKeyForSdk },
|
|
815
|
-
JSON.stringify({ name })
|
|
816
|
-
);
|
|
817
|
-
if (createRes.status !== 200) {
|
|
818
|
-
const data = createRes.data;
|
|
819
|
-
error(`\uC0DD\uC131 \uC2E4\uD328: ${data?.error || data?.message || `HTTP ${createRes.status}`}`);
|
|
820
|
-
process.exit(1);
|
|
821
|
-
}
|
|
822
|
-
const createData = createRes.data;
|
|
823
|
-
storageId = createData.storage_web_id;
|
|
824
|
-
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
825
|
-
}
|
|
826
|
-
} else {
|
|
827
|
-
throw new Error(`HTTP ${listRes.status}`);
|
|
406
|
+
storageId = createData.storage_web_id;
|
|
407
|
+
success(`\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC644\uB8CC: ${createData.name}`);
|
|
828
408
|
}
|
|
829
409
|
} catch (err) {
|
|
830
410
|
error(`\uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
@@ -838,43 +418,41 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
838
418
|
const defaultDir = detectBuildDir();
|
|
839
419
|
const deployDir = await prompt(`${colors.blue}?${colors.reset} \uBC30\uD3EC \uB514\uB809\uD1A0\uB9AC (${defaultDir}): `) || defaultDir;
|
|
840
420
|
const config = {
|
|
841
|
-
|
|
421
|
+
apiKey,
|
|
842
422
|
storageId,
|
|
843
423
|
deployDir
|
|
844
424
|
};
|
|
845
|
-
|
|
846
|
-
config.secretKey = secretKey;
|
|
847
|
-
}
|
|
848
|
-
fs2.writeFileSync(rcPath, JSON.stringify(config, null, 2) + "\n");
|
|
425
|
+
fs.writeFileSync(rcPath, JSON.stringify(config, null, 2) + "\n");
|
|
849
426
|
success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
|
|
850
427
|
addToGitignore(".connectbaserc");
|
|
851
428
|
addDeployScript(deployDir);
|
|
852
|
-
await
|
|
429
|
+
const setupClaude = await prompt(`
|
|
430
|
+
${colors.blue}?${colors.reset} Claude Code \uC124\uC815\uC744 \uCD94\uAC00\uD560\uAE4C\uC694? (CLAUDE.md, MCP \uC124\uC815) (Y/n): `);
|
|
431
|
+
if (setupClaude.toLowerCase() !== "n") {
|
|
432
|
+
await setupClaudeCode(apiKey);
|
|
433
|
+
}
|
|
853
434
|
log(`
|
|
854
435
|
${colors.green}\uCD08\uAE30\uD654 \uC644\uB8CC!${colors.reset}
|
|
855
436
|
`);
|
|
856
437
|
log(`${colors.dim}\uBC30\uD3EC\uD558\uB824\uBA74:${colors.reset}`);
|
|
857
|
-
log(` ${colors.cyan}npm run deploy${colors.reset}
|
|
858
|
-
log(`
|
|
859
|
-
${colors.dim}Claude Code\uC5D0\uC11C MCP\uAC00 \uC790\uB3D9 \uC5F0\uACB0\uB429\uB2C8\uB2E4${colors.reset}
|
|
438
|
+
log(` ${colors.cyan}npm run deploy${colors.reset}
|
|
860
439
|
`);
|
|
861
440
|
}
|
|
862
441
|
function detectBuildDir() {
|
|
863
|
-
if (
|
|
864
|
-
if (
|
|
865
|
-
if (
|
|
866
|
-
if (
|
|
867
|
-
if (
|
|
868
|
-
if (
|
|
442
|
+
if (fs.existsSync(path.join(process.cwd(), "dist"))) return "./dist";
|
|
443
|
+
if (fs.existsSync(path.join(process.cwd(), "build"))) return "./build";
|
|
444
|
+
if (fs.existsSync(path.join(process.cwd(), "out"))) return "./out";
|
|
445
|
+
if (fs.existsSync(path.join(process.cwd(), ".next"))) return "./out";
|
|
446
|
+
if (fs.existsSync(path.join(process.cwd(), "vite.config.ts")) || fs.existsSync(path.join(process.cwd(), "vite.config.js"))) return "./dist";
|
|
447
|
+
if (fs.existsSync(path.join(process.cwd(), "next.config.js")) || fs.existsSync(path.join(process.cwd(), "next.config.mjs"))) return "./out";
|
|
869
448
|
return "./dist";
|
|
870
449
|
}
|
|
871
|
-
function addToGitignore(entry
|
|
872
|
-
const
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
const content = fs2.readFileSync(gitignorePath, "utf-8");
|
|
450
|
+
function addToGitignore(entry) {
|
|
451
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
452
|
+
if (fs.existsSync(gitignorePath)) {
|
|
453
|
+
const content = fs.readFileSync(gitignorePath, "utf-8");
|
|
876
454
|
if (!content.includes(entry)) {
|
|
877
|
-
|
|
455
|
+
fs.appendFileSync(gitignorePath, `
|
|
878
456
|
# Connect Base
|
|
879
457
|
${entry}
|
|
880
458
|
`);
|
|
@@ -883,28 +461,28 @@ ${entry}
|
|
|
883
461
|
info(`.gitignore\uC5D0 \uC774\uBBF8 ${entry}\uAC00 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`);
|
|
884
462
|
}
|
|
885
463
|
} else {
|
|
886
|
-
|
|
464
|
+
fs.writeFileSync(gitignorePath, `# Connect Base
|
|
887
465
|
${entry}
|
|
888
466
|
`);
|
|
889
467
|
success(".gitignore \uC0DD\uC131 \uC644\uB8CC");
|
|
890
468
|
}
|
|
891
469
|
}
|
|
892
470
|
function addDeployScript(deployDir) {
|
|
893
|
-
const pkgPath =
|
|
894
|
-
if (!
|
|
471
|
+
const pkgPath = path.join(process.cwd(), "package.json");
|
|
472
|
+
if (!fs.existsSync(pkgPath)) {
|
|
895
473
|
warn("package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4");
|
|
896
474
|
return;
|
|
897
475
|
}
|
|
898
476
|
try {
|
|
899
|
-
const pkg = JSON.parse(
|
|
477
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
900
478
|
if (!pkg.scripts) pkg.scripts = {};
|
|
901
479
|
if (pkg.scripts.deploy) {
|
|
902
480
|
info(`package.json\uC5D0 \uC774\uBBF8 deploy \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: "${pkg.scripts.deploy}"`);
|
|
903
481
|
return;
|
|
904
482
|
}
|
|
905
|
-
const deployCmd = `connectbase deploy ${deployDir}`;
|
|
483
|
+
const deployCmd = `connectbase-client deploy ${deployDir}`;
|
|
906
484
|
pkg.scripts.deploy = pkg.scripts.build ? `${pkg.scripts.build} && ${deployCmd}` : deployCmd;
|
|
907
|
-
|
|
485
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
908
486
|
success(`package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC`);
|
|
909
487
|
} catch {
|
|
910
488
|
warn("package.json \uC218\uC815\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4");
|
|
@@ -918,58 +496,21 @@ function getGitRoot() {
|
|
|
918
496
|
return null;
|
|
919
497
|
}
|
|
920
498
|
}
|
|
921
|
-
function
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
} else if (fs2.existsSync(path2.join(gitRoot, "lerna.json"))) {
|
|
931
|
-
result.type = "lerna";
|
|
932
|
-
} else {
|
|
933
|
-
const rootPkgPath = path2.join(gitRoot, "package.json");
|
|
934
|
-
if (fs2.existsSync(rootPkgPath)) {
|
|
935
|
-
try {
|
|
936
|
-
const pkg = JSON.parse(fs2.readFileSync(rootPkgPath, "utf-8"));
|
|
937
|
-
if (pkg.workspaces) {
|
|
938
|
-
result.type = fs2.existsSync(path2.join(gitRoot, "yarn.lock")) ? "yarn" : "npm";
|
|
939
|
-
}
|
|
940
|
-
} catch {
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
if (result.type !== "none" && cwd !== gitRoot) {
|
|
945
|
-
const cwdPkgPath = path2.join(cwd, "package.json");
|
|
946
|
-
if (fs2.existsSync(cwdPkgPath)) {
|
|
947
|
-
result.isSubPackage = true;
|
|
948
|
-
try {
|
|
949
|
-
const pkg = JSON.parse(fs2.readFileSync(cwdPkgPath, "utf-8"));
|
|
950
|
-
result.subPackageName = pkg.name;
|
|
951
|
-
} catch {
|
|
952
|
-
}
|
|
953
|
-
}
|
|
954
|
-
}
|
|
955
|
-
return result;
|
|
956
|
-
}
|
|
957
|
-
async function downloadDocs(publicKey, templates, baseDir) {
|
|
958
|
-
if (!baseDir) {
|
|
959
|
-
baseDir = getProjectRoot();
|
|
960
|
-
}
|
|
961
|
-
const gitRoot = getGitRoot() || baseDir;
|
|
962
|
-
const monorepo = detectMonorepo(gitRoot);
|
|
963
|
-
if (monorepo.type !== "none") {
|
|
964
|
-
info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${gitRoot})`);
|
|
965
|
-
if (monorepo.isSubPackage) {
|
|
966
|
-
info(`\uD604\uC7AC \uC11C\uBE0C \uD328\uD0A4\uC9C0: ${monorepo.subPackageName || path2.basename(process.cwd())}`);
|
|
499
|
+
async function downloadDocs(apiKey, templates, monorepo) {
|
|
500
|
+
let baseDir = process.cwd();
|
|
501
|
+
if (monorepo) {
|
|
502
|
+
const gitRoot = getGitRoot();
|
|
503
|
+
if (gitRoot) {
|
|
504
|
+
baseDir = gitRoot;
|
|
505
|
+
info(`\uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8 \uAC10\uC9C0: ${gitRoot}`);
|
|
506
|
+
} else {
|
|
507
|
+
warn("git root\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uD604\uC7AC \uB514\uB809\uD1A0\uB9AC\uB97C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.");
|
|
967
508
|
}
|
|
968
509
|
}
|
|
969
|
-
const docsDir =
|
|
970
|
-
const rootClaudeMd =
|
|
971
|
-
if (!
|
|
972
|
-
|
|
510
|
+
const docsDir = path.join(baseDir, ".claude", "docs");
|
|
511
|
+
const rootClaudeMd = path.join(baseDir, "CLAUDE.md");
|
|
512
|
+
if (!fs.existsSync(docsDir)) {
|
|
513
|
+
fs.mkdirSync(docsDir, { recursive: true });
|
|
973
514
|
}
|
|
974
515
|
if (!templates) {
|
|
975
516
|
templates = ["rules"];
|
|
@@ -978,7 +519,7 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
978
519
|
try {
|
|
979
520
|
const templateParam = templates.join(",");
|
|
980
521
|
const res = await makeRequest(
|
|
981
|
-
`${DEFAULT_BASE_URL}/v1/storages/webs/claude-md?template=${encodeURIComponent(templateParam)}&format=sections&
|
|
522
|
+
`${DEFAULT_BASE_URL}/v1/storages/webs/claude-md?template=${encodeURIComponent(templateParam)}&format=sections&api_key=${encodeURIComponent(apiKey)}`,
|
|
982
523
|
"GET",
|
|
983
524
|
{}
|
|
984
525
|
);
|
|
@@ -988,15 +529,11 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
988
529
|
const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
989
530
|
const sections = data.sections || [];
|
|
990
531
|
for (const section of sections) {
|
|
991
|
-
const filePath =
|
|
992
|
-
|
|
532
|
+
const filePath = path.join(docsDir, section.filename);
|
|
533
|
+
fs.writeFileSync(filePath, section.content);
|
|
993
534
|
}
|
|
994
|
-
success(
|
|
535
|
+
success(`.claude/docs/ \uC5D0 ${sections.length}\uAC1C \uBB38\uC11C \uC800\uC7A5 \uC644\uB8CC`);
|
|
995
536
|
updateRootClaudeMd(rootClaudeMd);
|
|
996
|
-
if (monorepo.isSubPackage) {
|
|
997
|
-
const subClaudeMd = path2.join(process.cwd(), "CLAUDE.md");
|
|
998
|
-
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
999
|
-
}
|
|
1000
537
|
log(`${colors.dim}\u203B SDK \uBB38\uC11C\uB294 MCP search_sdk_docs\uB85C \uAC80\uC0C9\uD558\uC138\uC694${colors.reset}`);
|
|
1001
538
|
} catch {
|
|
1002
539
|
warn("SDK \uAC00\uC774\uB4DC \uB2E4\uC6B4\uB85C\uB4DC \uC2E4\uD328, \uAE30\uBCF8 \uBB38\uC11C\uB97C \uC0DD\uC131\uD569\uB2C8\uB2E4");
|
|
@@ -1006,12 +543,8 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
1006
543
|
|
|
1007
544
|
\uC0C1\uC138 \uAD6C\uD604\uBC95\uC740 MCP \`search_sdk_docs\` \uB3C4\uAD6C\uB85C \uAC80\uC0C9\uD558\uC138\uC694.
|
|
1008
545
|
`;
|
|
1009
|
-
|
|
546
|
+
fs.writeFileSync(path.join(docsDir, "project-rules.md"), fallbackContent);
|
|
1010
547
|
updateRootClaudeMd(rootClaudeMd);
|
|
1011
|
-
if (monorepo.isSubPackage) {
|
|
1012
|
-
const subClaudeMd = path2.join(process.cwd(), "CLAUDE.md");
|
|
1013
|
-
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
1014
|
-
}
|
|
1015
548
|
success(`.claude/docs/ \uC0DD\uC131 \uC644\uB8CC (\uAE30\uBCF8)`);
|
|
1016
549
|
}
|
|
1017
550
|
}
|
|
@@ -1022,395 +555,87 @@ function updateRootClaudeMd(claudeMdPath) {
|
|
|
1022
555
|
## ConnectBase SDK
|
|
1023
556
|
|
|
1024
557
|
\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 **ConnectBase**\uB97C \uBC31\uC5D4\uB4DC\uB85C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.
|
|
558
|
+
\uC0AC\uC6A9\uC790\uB294 **\uBE44\uAC1C\uBC1C\uC790**\uC785\uB2C8\uB2E4. \uC26C\uC6B4 \uB9D0\uB85C \uC124\uBA85\uD558\uACE0, \uBC14\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C \uC644\uC131\uB41C \uCF54\uB4DC\uB97C \uC81C\uACF5\uD558\uC138\uC694.
|
|
1025
559
|
|
|
1026
560
|
### AI \uD544\uB3C5 \u2014 \uCF54\uB529 \uC804 \uBC18\uB4DC\uC2DC \uC218\uD589
|
|
1027
561
|
|
|
1028
|
-
1. \`.claude/docs/project-rules.md
|
|
1029
|
-
2.
|
|
1030
|
-
3. \
|
|
562
|
+
1. \`.claude/docs/project-rules.md\` \uD30C\uC77C\uC744 **Read tool\uB85C \uC77D\uC73C\uC138\uC694** (\uAE30\uB2A5 \uB9E4\uD551\uD45C, \uBCF4\uC548 \uADDC\uCE59 \uD3EC\uD568)
|
|
563
|
+
2. \uC0AC\uC6A9\uC790 \uC694\uCCAD\uC5D0 \uB9DE\uB294 \uAE30\uB2A5\uC744 \uB9E4\uD551\uD45C\uC5D0\uC11C \uCC3E\uC73C\uC138\uC694
|
|
564
|
+
3. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
565
|
+
4. \uC644\uC131\uB41C \uCF54\uB4DC\uB97C \uC791\uC131\uD558\uC138\uC694 (\uC2A4\uB2C8\uD3AB X, \uBC14\uB85C \uC2E4\uD589 \uAC00\uB2A5\uD55C \uC804\uCCB4 \uCF54\uB4DC)
|
|
566
|
+
|
|
567
|
+
### \uBE44\uAC1C\uBC1C\uC790 \uC751\uB300 \uC6D0\uCE59
|
|
568
|
+
|
|
569
|
+
- \uC804\uBB38 \uC6A9\uC5B4 \uB300\uC2E0 \uC26C\uC6B4 \uB9D0 \uC0AC\uC6A9 ("API" \u2192 "\uAE30\uB2A5", "DB" \u2192 "\uC800\uC7A5\uC18C")
|
|
570
|
+
- \uC5D0\uB7EC \uBC1C\uC0DD \uC2DC \uC6D0\uC778\uACFC \uD574\uACB0\uCC45\uC744 \uD55C \uC904\uB85C \uC124\uBA85
|
|
571
|
+
- \uCF54\uB4DC \uC791\uC131 \uC804 "\uC774\uB807\uAC8C \uB9CC\uB4E4\uAC8C\uC694~" \uD55C \uC904 \uC694\uC57D \uBA3C\uC800
|
|
572
|
+
- \uBAA8\uB974\uB294 \uAC8C \uC788\uC73C\uBA74 \uB9CC\uB4E4\uAE30 \uC804\uC5D0 \uBA3C\uC800 \uC9C8\uBB38
|
|
1031
573
|
${endMarker}`;
|
|
1032
|
-
if (
|
|
1033
|
-
let content =
|
|
574
|
+
if (fs.existsSync(claudeMdPath)) {
|
|
575
|
+
let content = fs.readFileSync(claudeMdPath, "utf-8");
|
|
1034
576
|
const startIdx = content.indexOf(startMarker);
|
|
1035
577
|
const endIdx = content.indexOf(endMarker);
|
|
1036
578
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
1037
579
|
content = content.substring(0, startIdx) + sdkBlock + content.substring(endIdx + endMarker.length);
|
|
1038
580
|
} else {
|
|
1039
|
-
content =
|
|
581
|
+
content = content.trimEnd() + "\n\n" + sdkBlock + "\n";
|
|
1040
582
|
}
|
|
1041
|
-
|
|
583
|
+
fs.writeFileSync(claudeMdPath, content);
|
|
1042
584
|
info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
1043
585
|
} else {
|
|
1044
|
-
|
|
586
|
+
fs.writeFileSync(claudeMdPath, `# \uD504\uB85C\uC81D\uD2B8
|
|
1045
587
|
|
|
1046
588
|
${sdkBlock}
|
|
1047
589
|
`);
|
|
1048
590
|
success("CLAUDE.md \uC0DD\uC131 \uC644\uB8CC");
|
|
1049
591
|
}
|
|
1050
592
|
}
|
|
1051
|
-
function
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
const
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
1. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`${relPath}/.claude/docs/project-rules.md\`\uB97C **Read tool\uB85C \uC77D\uC73C\uC138\uC694**
|
|
1063
|
-
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C SDK \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
1064
|
-
3. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`${relPath}/CLAUDE.md\`\uB3C4 \uCC38\uACE0\uD558\uC138\uC694
|
|
1065
|
-
${endMarker}`;
|
|
1066
|
-
if (fs2.existsSync(subClaudeMdPath)) {
|
|
1067
|
-
let content = fs2.readFileSync(subClaudeMdPath, "utf-8");
|
|
1068
|
-
const startIdx = content.indexOf(startMarker);
|
|
1069
|
-
const endIdx = content.indexOf(endMarker);
|
|
1070
|
-
if (startIdx !== -1 && endIdx !== -1) {
|
|
1071
|
-
content = content.substring(0, startIdx) + subBlock + content.substring(endIdx + endMarker.length);
|
|
1072
|
-
} else if (content.indexOf("<!-- CONNECTBASE_START -->") === -1) {
|
|
1073
|
-
content = content.trimEnd() + "\n\n" + subBlock + "\n";
|
|
1074
|
-
} else {
|
|
1075
|
-
return;
|
|
1076
|
-
}
|
|
1077
|
-
fs2.writeFileSync(subClaudeMdPath, content);
|
|
1078
|
-
} else {
|
|
1079
|
-
fs2.writeFileSync(subClaudeMdPath, `# ${path2.basename(path2.dirname(subClaudeMdPath))}
|
|
1080
|
-
|
|
1081
|
-
${subBlock}
|
|
1082
|
-
`);
|
|
1083
|
-
}
|
|
1084
|
-
success(`\uC11C\uBE0C \uD328\uD0A4\uC9C0 CLAUDE.md \uC0DD\uC131 \uC644\uB8CC: ${subClaudeMdPath}`);
|
|
1085
|
-
}
|
|
1086
|
-
async function setupMcp(secretKey) {
|
|
1087
|
-
const gitRoot = getGitRoot();
|
|
1088
|
-
const root = gitRoot || process.cwd();
|
|
1089
|
-
const monorepo = detectMonorepo(root);
|
|
1090
|
-
if (monorepo.type !== "none") {
|
|
1091
|
-
info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${root})`);
|
|
1092
|
-
if (monorepo.isSubPackage) {
|
|
1093
|
-
info(`MCP \uC124\uC815\uC740 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8(${root})\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4`);
|
|
1094
|
-
}
|
|
1095
|
-
}
|
|
1096
|
-
const mcpConfigPath = path2.join(root, ".mcp.json");
|
|
1097
|
-
if (!secretKey) {
|
|
1098
|
-
log(`
|
|
1099
|
-
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
1100
|
-
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
1101
|
-
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
1102
|
-
`);
|
|
1103
|
-
const authChoice = await prompt(`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `);
|
|
1104
|
-
if (authChoice === "2") {
|
|
1105
|
-
try {
|
|
1106
|
-
secretKey = await browserAuthFlow();
|
|
1107
|
-
const skRcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
1108
|
-
let rcData = {};
|
|
1109
|
-
if (fs2.existsSync(skRcPath)) {
|
|
1110
|
-
try {
|
|
1111
|
-
rcData = JSON.parse(fs2.readFileSync(skRcPath, "utf-8"));
|
|
1112
|
-
} catch {
|
|
1113
|
-
}
|
|
593
|
+
async function setupClaudeCode(apiKey) {
|
|
594
|
+
const mcpConfigPath = path.join(process.cwd(), ".mcp.json");
|
|
595
|
+
await downloadDocs(apiKey);
|
|
596
|
+
const mcpConfig = {
|
|
597
|
+
mcpServers: {
|
|
598
|
+
"connect-base": {
|
|
599
|
+
type: "http",
|
|
600
|
+
url: "https://mcp.connectbase.world/mcp",
|
|
601
|
+
headers: {
|
|
602
|
+
Authorization: "Bearer YOUR_SECRET_KEY_HERE"
|
|
1114
603
|
}
|
|
1115
|
-
rcData.secretKey = secretKey;
|
|
1116
|
-
fs2.writeFileSync(skRcPath, JSON.stringify(rcData, null, 2) + "\n");
|
|
1117
|
-
addToGitignore(".connectbaserc", root);
|
|
1118
|
-
success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
|
|
1119
|
-
} catch (err) {
|
|
1120
|
-
error(`\uC778\uC99D \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
1121
|
-
process.exit(1);
|
|
1122
604
|
}
|
|
1123
|
-
} else {
|
|
1124
|
-
secretKey = await promptSecret(`${colors.blue}?${colors.reset} Secret Key: `);
|
|
1125
|
-
}
|
|
1126
|
-
}
|
|
1127
|
-
const mcpEntry = {
|
|
1128
|
-
type: "http",
|
|
1129
|
-
url: "https://mcp.connectbase.world/mcp",
|
|
1130
|
-
headers: {
|
|
1131
|
-
Authorization: `Bearer ${secretKey || "YOUR_SECRET_KEY_HERE"}`
|
|
1132
605
|
}
|
|
1133
606
|
};
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
607
|
+
fs.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
608
|
+
success(".mcp.json \uC0DD\uC131 \uC644\uB8CC");
|
|
609
|
+
addToGitignore(".mcp.json");
|
|
610
|
+
warn("MCP \uC11C\uBC84\uB294 Secret Key (cb_sk_)\uB9CC \uD5C8\uC6A9\uD569\uB2C8\uB2E4.");
|
|
611
|
+
info(".mcp.json \uD30C\uC77C\uC758 YOUR_SECRET_KEY_HERE\uB97C Secret Key\uB85C \uAD50\uCCB4\uD558\uC138\uC694.");
|
|
612
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uC124\uC815 > API Keys\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.");
|
|
613
|
+
}
|
|
614
|
+
function createWsTextFrame(payload) {
|
|
615
|
+
const data = Buffer.from(payload, "utf-8");
|
|
616
|
+
const len = data.length;
|
|
617
|
+
const maskKey = crypto.randomBytes(4);
|
|
618
|
+
let header;
|
|
619
|
+
if (len < 126) {
|
|
620
|
+
header = Buffer.alloc(2);
|
|
621
|
+
header[0] = 129;
|
|
622
|
+
header[1] = 128 | len;
|
|
623
|
+
} else if (len < 65536) {
|
|
624
|
+
header = Buffer.alloc(4);
|
|
625
|
+
header[0] = 129;
|
|
626
|
+
header[1] = 128 | 126;
|
|
627
|
+
header.writeUInt16BE(len, 2);
|
|
1154
628
|
} else {
|
|
1155
|
-
|
|
1156
|
-
|
|
629
|
+
header = Buffer.alloc(10);
|
|
630
|
+
header[0] = 129;
|
|
631
|
+
header[1] = 128 | 127;
|
|
632
|
+
header.writeBigUInt64BE(BigInt(len), 2);
|
|
1157
633
|
}
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
await downloadDocs(publicKey, void 0, root);
|
|
1164
|
-
await setupMcp(secretKey);
|
|
1165
|
-
}
|
|
1166
|
-
async function fetchLatestVersion(packageName) {
|
|
1167
|
-
try {
|
|
1168
|
-
const res = await makeRequest(
|
|
1169
|
-
`https://registry.npmjs.org/${packageName}/latest`,
|
|
1170
|
-
"GET",
|
|
1171
|
-
{}
|
|
1172
|
-
);
|
|
1173
|
-
if (res.status === 200) {
|
|
1174
|
-
const data = res.data;
|
|
1175
|
-
return data.version || null;
|
|
1176
|
-
}
|
|
1177
|
-
} catch {
|
|
1178
|
-
}
|
|
1179
|
-
return null;
|
|
1180
|
-
}
|
|
1181
|
-
function compareVersions(a, b) {
|
|
1182
|
-
const pa = a.split(".").map(Number);
|
|
1183
|
-
const pb = b.split(".").map(Number);
|
|
1184
|
-
for (let i = 0; i < 3; i++) {
|
|
1185
|
-
const diff = (pa[i] || 0) - (pb[i] || 0);
|
|
1186
|
-
if (diff !== 0) return diff > 0 ? 1 : -1;
|
|
1187
|
-
}
|
|
1188
|
-
return 0;
|
|
1189
|
-
}
|
|
1190
|
-
function findSubPackagesWithConfig(gitRoot) {
|
|
1191
|
-
const results = [];
|
|
1192
|
-
const candidates = ["apps", "packages", "projects", "services", "libs"];
|
|
1193
|
-
for (const candidate of candidates) {
|
|
1194
|
-
const dir = path2.join(gitRoot, candidate);
|
|
1195
|
-
if (!fs2.existsSync(dir)) continue;
|
|
1196
|
-
try {
|
|
1197
|
-
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
1198
|
-
for (const entry of entries) {
|
|
1199
|
-
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
|
|
1200
|
-
const subDir = path2.join(dir, entry.name);
|
|
1201
|
-
if (fs2.existsSync(path2.join(subDir, ".connectbaserc"))) {
|
|
1202
|
-
results.push(subDir);
|
|
1203
|
-
}
|
|
1204
|
-
}
|
|
1205
|
-
} catch {
|
|
1206
|
-
}
|
|
1207
|
-
}
|
|
1208
|
-
try {
|
|
1209
|
-
const entries = fs2.readdirSync(gitRoot, { withFileTypes: true });
|
|
1210
|
-
for (const entry of entries) {
|
|
1211
|
-
if (!entry.isDirectory() || entry.name.startsWith(".") || candidates.includes(entry.name)) continue;
|
|
1212
|
-
if (entry.name === "node_modules") continue;
|
|
1213
|
-
const subDir = path2.join(gitRoot, entry.name);
|
|
1214
|
-
if (fs2.existsSync(path2.join(subDir, ".connectbaserc")) && !results.includes(subDir)) {
|
|
1215
|
-
results.push(subDir);
|
|
1216
|
-
}
|
|
1217
|
-
}
|
|
1218
|
-
} catch {
|
|
1219
|
-
}
|
|
1220
|
-
return results;
|
|
1221
|
-
}
|
|
1222
|
-
async function update(config, opts) {
|
|
1223
|
-
log(`
|
|
1224
|
-
${colors.cyan}ConnectBase Update${colors.reset}
|
|
1225
|
-
`);
|
|
1226
|
-
const gitRoot = getGitRoot();
|
|
1227
|
-
const projectRoot = gitRoot || process.cwd();
|
|
1228
|
-
const monorepo = detectMonorepo(projectRoot);
|
|
1229
|
-
info("\uCD5C\uC2E0 \uBC84\uC804 \uD655\uC778 \uC911...");
|
|
1230
|
-
const latestVersion = await fetchLatestVersion("connectbase-client");
|
|
1231
|
-
if (latestVersion) {
|
|
1232
|
-
const cmp = compareVersions(VERSION, latestVersion);
|
|
1233
|
-
if (cmp >= 0) {
|
|
1234
|
-
success(`\uCD5C\uC2E0 \uBC84\uC804\uC785\uB2C8\uB2E4 (v${VERSION})`);
|
|
1235
|
-
} else {
|
|
1236
|
-
warn(`\uC0C8 \uBC84\uC804\uC774 \uC788\uC2B5\uB2C8\uB2E4: v${VERSION} \u2192 v${colors.green}${latestVersion}${colors.reset}`);
|
|
1237
|
-
log(` ${colors.cyan}npm install connectbase-client@latest${colors.reset}`);
|
|
1238
|
-
log(` ${colors.cyan}pnpm add connectbase-client@latest${colors.reset}`);
|
|
1239
|
-
log("");
|
|
1240
|
-
}
|
|
1241
|
-
} else {
|
|
1242
|
-
warn("npm \uB808\uC9C0\uC2A4\uD2B8\uB9AC\uC5D0\uC11C \uBC84\uC804 \uC815\uBCF4\uB97C \uAC00\uC838\uC62C \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
1243
|
-
}
|
|
1244
|
-
if (opts.checkOnly) {
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
if (monorepo.type !== "none") {
|
|
1248
|
-
info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${projectRoot})`);
|
|
1249
|
-
const subPackages = findSubPackagesWithConfig(projectRoot);
|
|
1250
|
-
if (subPackages.length > 0) {
|
|
1251
|
-
info(`ConnectBase\uB97C \uC0AC\uC6A9\uD558\uB294 \uC11C\uBE0C \uD328\uD0A4\uC9C0 ${subPackages.length}\uAC1C \uBC1C\uACAC:`);
|
|
1252
|
-
for (const sp of subPackages) {
|
|
1253
|
-
log(` ${colors.dim}\u2022 ${path2.relative(projectRoot, sp)}${colors.reset}`);
|
|
1254
|
-
}
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
if (!opts.skipDocs) {
|
|
1258
|
-
log("");
|
|
1259
|
-
info("SDK \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uC911...");
|
|
1260
|
-
let docsKey = config.publicKey || config.secretKey;
|
|
1261
|
-
if (!docsKey) {
|
|
1262
|
-
const rootRcPath = path2.join(projectRoot, ".connectbaserc");
|
|
1263
|
-
if (fs2.existsSync(rootRcPath)) {
|
|
1264
|
-
try {
|
|
1265
|
-
const rc = JSON.parse(fs2.readFileSync(rootRcPath, "utf-8"));
|
|
1266
|
-
docsKey = rc.publicKey || rc.secretKey;
|
|
1267
|
-
} catch {
|
|
1268
|
-
}
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
if (!docsKey && monorepo.type !== "none") {
|
|
1272
|
-
const subPackages = findSubPackagesWithConfig(projectRoot);
|
|
1273
|
-
for (const sp of subPackages) {
|
|
1274
|
-
try {
|
|
1275
|
-
const rc = JSON.parse(fs2.readFileSync(path2.join(sp, ".connectbaserc"), "utf-8"));
|
|
1276
|
-
if (rc.publicKey || rc.secretKey) {
|
|
1277
|
-
docsKey = rc.publicKey || rc.secretKey;
|
|
1278
|
-
break;
|
|
1279
|
-
}
|
|
1280
|
-
} catch {
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
if (docsKey) {
|
|
1285
|
-
await downloadDocs(docsKey, void 0, projectRoot);
|
|
1286
|
-
} else {
|
|
1287
|
-
warn("Public Key\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC5B4 \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4");
|
|
1288
|
-
info(".connectbaserc \uD30C\uC77C\uC774\uB098 --public-key \uC635\uC158\uC73C\uB85C \uD0A4\uB97C \uC9C0\uC815\uD558\uC138\uC694");
|
|
1289
|
-
}
|
|
1290
|
-
}
|
|
1291
|
-
if (!opts.skipMcp) {
|
|
1292
|
-
log("");
|
|
1293
|
-
const mcpConfigPath = path2.join(projectRoot, ".mcp.json");
|
|
1294
|
-
if (fs2.existsSync(mcpConfigPath)) {
|
|
1295
|
-
let secretKey = config.secretKey;
|
|
1296
|
-
if (!secretKey) {
|
|
1297
|
-
const rootRcPath = path2.join(projectRoot, ".connectbaserc");
|
|
1298
|
-
if (fs2.existsSync(rootRcPath)) {
|
|
1299
|
-
try {
|
|
1300
|
-
const rc = JSON.parse(fs2.readFileSync(rootRcPath, "utf-8"));
|
|
1301
|
-
secretKey = rc.secretKey;
|
|
1302
|
-
} catch {
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
if (!secretKey && monorepo.type !== "none") {
|
|
1307
|
-
const subPackages = findSubPackagesWithConfig(projectRoot);
|
|
1308
|
-
for (const sp of subPackages) {
|
|
1309
|
-
try {
|
|
1310
|
-
const rc = JSON.parse(fs2.readFileSync(path2.join(sp, ".connectbaserc"), "utf-8"));
|
|
1311
|
-
if (rc.secretKey) {
|
|
1312
|
-
secretKey = rc.secretKey;
|
|
1313
|
-
break;
|
|
1314
|
-
}
|
|
1315
|
-
} catch {
|
|
1316
|
-
}
|
|
1317
|
-
}
|
|
1318
|
-
}
|
|
1319
|
-
if (secretKey) {
|
|
1320
|
-
try {
|
|
1321
|
-
const mcpConfig = JSON.parse(fs2.readFileSync(mcpConfigPath, "utf-8"));
|
|
1322
|
-
const servers = mcpConfig.mcpServers || {};
|
|
1323
|
-
if (servers["connect-base"]) {
|
|
1324
|
-
servers["connect-base"] = {
|
|
1325
|
-
type: "http",
|
|
1326
|
-
url: "https://mcp.connectbase.world/mcp",
|
|
1327
|
-
headers: { Authorization: `Bearer ${secretKey}` }
|
|
1328
|
-
};
|
|
1329
|
-
mcpConfig.mcpServers = servers;
|
|
1330
|
-
fs2.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
1331
|
-
success("MCP \uC124\uC815 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
1332
|
-
} else {
|
|
1333
|
-
info('.mcp.json\uC5D0 connect-base \uD56D\uBAA9\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. "connectbase mcp"\uB85C \uC124\uC815\uD558\uC138\uC694');
|
|
1334
|
-
}
|
|
1335
|
-
} catch {
|
|
1336
|
-
warn(".mcp.json \uC5C5\uB370\uC774\uD2B8 \uC2E4\uD328");
|
|
1337
|
-
}
|
|
1338
|
-
} else {
|
|
1339
|
-
info("Secret Key\uAC00 \uC5C6\uC5B4 MCP \uC124\uC815 \uC5C5\uB370\uC774\uD2B8\uB97C \uAC74\uB108\uB701\uB2C8\uB2E4");
|
|
1340
|
-
}
|
|
1341
|
-
} else {
|
|
1342
|
-
info('.mcp.json\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. "connectbase mcp"\uB85C \uCD5C\uCD08 \uC124\uC815\uD558\uC138\uC694');
|
|
1343
|
-
}
|
|
1344
|
-
}
|
|
1345
|
-
if (opts.setupRoot || monorepo.type !== "none" && monorepo.isSubPackage) {
|
|
1346
|
-
log("");
|
|
1347
|
-
setupMonorepoRoot(projectRoot, monorepo);
|
|
1348
|
-
}
|
|
1349
|
-
log(`
|
|
1350
|
-
${colors.green}\uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC!${colors.reset}
|
|
1351
|
-
`);
|
|
1352
|
-
}
|
|
1353
|
-
function setupMonorepoRoot(projectRoot, monorepo) {
|
|
1354
|
-
const rootPkgPath = path2.join(projectRoot, "package.json");
|
|
1355
|
-
if (!fs2.existsSync(rootPkgPath)) {
|
|
1356
|
-
warn("\uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 package.json\uC774 \uC5C6\uC5B4 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
1357
|
-
return;
|
|
1358
|
-
}
|
|
1359
|
-
try {
|
|
1360
|
-
const pkg = JSON.parse(fs2.readFileSync(rootPkgPath, "utf-8"));
|
|
1361
|
-
if (!pkg.scripts) pkg.scripts = {};
|
|
1362
|
-
let changed = false;
|
|
1363
|
-
if (!pkg.scripts["cb:update"]) {
|
|
1364
|
-
pkg.scripts["cb:update"] = "npx connectbase update";
|
|
1365
|
-
changed = true;
|
|
1366
|
-
}
|
|
1367
|
-
if (!pkg.scripts["cb:docs"]) {
|
|
1368
|
-
pkg.scripts["cb:docs"] = "npx connectbase docs";
|
|
1369
|
-
changed = true;
|
|
1370
|
-
}
|
|
1371
|
-
if (!pkg.scripts["cb:mcp"]) {
|
|
1372
|
-
pkg.scripts["cb:mcp"] = "npx connectbase mcp";
|
|
1373
|
-
changed = true;
|
|
1374
|
-
}
|
|
1375
|
-
if (changed) {
|
|
1376
|
-
fs2.writeFileSync(rootPkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
1377
|
-
success("\uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC");
|
|
1378
|
-
log(` ${colors.dim}\uC0AC\uC6A9\uBC95:${colors.reset}`);
|
|
1379
|
-
log(` ${colors.cyan}${monorepo.type === "pnpm" ? "pnpm" : monorepo.type === "yarn" ? "yarn" : "npm run"} cb:update${colors.reset} \u2014 SDK \uC5C5\uB370\uC774\uD2B8 + \uBB38\uC11C \uAC31\uC2E0`);
|
|
1380
|
-
log(` ${colors.cyan}${monorepo.type === "pnpm" ? "pnpm" : monorepo.type === "yarn" ? "yarn" : "npm run"} cb:docs${colors.reset} \u2014 \uBB38\uC11C\uB9CC \uAC31\uC2E0`);
|
|
1381
|
-
log(` ${colors.cyan}${monorepo.type === "pnpm" ? "pnpm" : monorepo.type === "yarn" ? "yarn" : "npm run"} cb:mcp${colors.reset} \u2014 MCP \uC124\uC815`);
|
|
1382
|
-
} else {
|
|
1383
|
-
info("\uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uC774\uBBF8 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC124\uC815\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1384
|
-
}
|
|
1385
|
-
} catch {
|
|
1386
|
-
warn("\uB8E8\uD2B8 package.json \uC218\uC815\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4");
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
function createWsTextFrame(payload) {
|
|
1390
|
-
const data = Buffer.from(payload, "utf-8");
|
|
1391
|
-
const len = data.length;
|
|
1392
|
-
const maskKey = crypto.randomBytes(4);
|
|
1393
|
-
let header;
|
|
1394
|
-
if (len < 126) {
|
|
1395
|
-
header = Buffer.alloc(2);
|
|
1396
|
-
header[0] = 129;
|
|
1397
|
-
header[1] = 128 | len;
|
|
1398
|
-
} else if (len < 65536) {
|
|
1399
|
-
header = Buffer.alloc(4);
|
|
1400
|
-
header[0] = 129;
|
|
1401
|
-
header[1] = 128 | 126;
|
|
1402
|
-
header.writeUInt16BE(len, 2);
|
|
1403
|
-
} else {
|
|
1404
|
-
header = Buffer.alloc(10);
|
|
1405
|
-
header[0] = 129;
|
|
1406
|
-
header[1] = 128 | 127;
|
|
1407
|
-
header.writeBigUInt64BE(BigInt(len), 2);
|
|
1408
|
-
}
|
|
1409
|
-
const masked = Buffer.alloc(data.length);
|
|
1410
|
-
for (let i = 0; i < data.length; i++) {
|
|
1411
|
-
masked[i] = data[i] ^ maskKey[i % 4];
|
|
1412
|
-
}
|
|
1413
|
-
return Buffer.concat([header, maskKey, masked]);
|
|
634
|
+
const masked = Buffer.alloc(data.length);
|
|
635
|
+
for (let i = 0; i < data.length; i++) {
|
|
636
|
+
masked[i] = data[i] ^ maskKey[i % 4];
|
|
637
|
+
}
|
|
638
|
+
return Buffer.concat([header, maskKey, masked]);
|
|
1414
639
|
}
|
|
1415
640
|
function createWsCloseFrame(code) {
|
|
1416
641
|
const maskKey = crypto.randomBytes(4);
|
|
@@ -1431,159 +656,10 @@ function createWsPongFrame() {
|
|
|
1431
656
|
header[1] = 128 | 0;
|
|
1432
657
|
return Buffer.concat([header, maskKey]);
|
|
1433
658
|
}
|
|
1434
|
-
function createWsBinaryFrame(payload) {
|
|
1435
|
-
const len = payload.length;
|
|
1436
|
-
const maskKey = crypto.randomBytes(4);
|
|
1437
|
-
let header;
|
|
1438
|
-
if (len < 126) {
|
|
1439
|
-
header = Buffer.alloc(2);
|
|
1440
|
-
header[0] = 130;
|
|
1441
|
-
header[1] = 128 | len;
|
|
1442
|
-
} else if (len < 65536) {
|
|
1443
|
-
header = Buffer.alloc(4);
|
|
1444
|
-
header[0] = 130;
|
|
1445
|
-
header[1] = 128 | 126;
|
|
1446
|
-
header.writeUInt16BE(len, 2);
|
|
1447
|
-
} else {
|
|
1448
|
-
header = Buffer.alloc(10);
|
|
1449
|
-
header[0] = 130;
|
|
1450
|
-
header[1] = 128 | 127;
|
|
1451
|
-
header.writeBigUInt64BE(BigInt(len), 2);
|
|
1452
|
-
}
|
|
1453
|
-
const masked = Buffer.alloc(len);
|
|
1454
|
-
for (let i = 0; i < len; i++) {
|
|
1455
|
-
masked[i] = payload[i] ^ maskKey[i % 4];
|
|
1456
|
-
}
|
|
1457
|
-
return Buffer.concat([header, maskKey, masked]);
|
|
1458
|
-
}
|
|
1459
|
-
var UpstreamWsFrameParser = class {
|
|
1460
|
-
constructor(handlers) {
|
|
1461
|
-
this.buffer = Buffer.alloc(0);
|
|
1462
|
-
this.fragmentBuffer = null;
|
|
1463
|
-
this.h = handlers;
|
|
1464
|
-
}
|
|
1465
|
-
feed(chunk) {
|
|
1466
|
-
this.buffer = Buffer.concat([this.buffer, chunk]);
|
|
1467
|
-
try {
|
|
1468
|
-
this.parse();
|
|
1469
|
-
} catch (e) {
|
|
1470
|
-
this.h.onError(e instanceof Error ? e : new Error(String(e)));
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
parse() {
|
|
1474
|
-
while (this.buffer.length >= 2) {
|
|
1475
|
-
const firstByte = this.buffer[0];
|
|
1476
|
-
const secondByte = this.buffer[1];
|
|
1477
|
-
const fin = (firstByte & 128) !== 0;
|
|
1478
|
-
const rsv1 = (firstByte & 64) !== 0;
|
|
1479
|
-
const opcode = firstByte & 15;
|
|
1480
|
-
const isMasked = (secondByte & 128) !== 0;
|
|
1481
|
-
let payloadLen = secondByte & 127;
|
|
1482
|
-
let offset = 2;
|
|
1483
|
-
if (payloadLen === 126) {
|
|
1484
|
-
if (this.buffer.length < 4) return;
|
|
1485
|
-
payloadLen = this.buffer.readUInt16BE(2);
|
|
1486
|
-
offset = 4;
|
|
1487
|
-
} else if (payloadLen === 127) {
|
|
1488
|
-
if (this.buffer.length < 10) return;
|
|
1489
|
-
const big = this.buffer.readBigUInt64BE(2);
|
|
1490
|
-
if (big > BigInt(Number.MAX_SAFE_INTEGER)) {
|
|
1491
|
-
throw new Error(`payload too large: ${big}`);
|
|
1492
|
-
}
|
|
1493
|
-
payloadLen = Number(big);
|
|
1494
|
-
offset = 10;
|
|
1495
|
-
}
|
|
1496
|
-
let maskKey = null;
|
|
1497
|
-
if (isMasked) {
|
|
1498
|
-
if (this.buffer.length < offset + 4) return;
|
|
1499
|
-
maskKey = this.buffer.subarray(offset, offset + 4);
|
|
1500
|
-
offset += 4;
|
|
1501
|
-
}
|
|
1502
|
-
if (this.buffer.length < offset + payloadLen) return;
|
|
1503
|
-
if (rsv1 && (opcode === 1 || opcode === 2 || opcode === 0)) {
|
|
1504
|
-
throw new Error("upstream sent compressed (RSV1) frame without negotiation");
|
|
1505
|
-
}
|
|
1506
|
-
let payload = this.buffer.subarray(offset, offset + payloadLen);
|
|
1507
|
-
if (maskKey) {
|
|
1508
|
-
const unmasked = Buffer.alloc(payloadLen);
|
|
1509
|
-
for (let i = 0; i < payloadLen; i++) {
|
|
1510
|
-
unmasked[i] = payload[i] ^ maskKey[i % 4];
|
|
1511
|
-
}
|
|
1512
|
-
payload = unmasked;
|
|
1513
|
-
}
|
|
1514
|
-
this.buffer = this.buffer.subarray(offset + payloadLen);
|
|
1515
|
-
const payloadCopy = Buffer.from(payload);
|
|
1516
|
-
switch (opcode) {
|
|
1517
|
-
case 0:
|
|
1518
|
-
if (this.fragmentBuffer == null) {
|
|
1519
|
-
throw new Error("continuation frame without preceding non-final data frame");
|
|
1520
|
-
}
|
|
1521
|
-
this.fragmentBuffer = Buffer.concat([this.fragmentBuffer, payloadCopy]);
|
|
1522
|
-
if (fin) {
|
|
1523
|
-
const out = this.fragmentBuffer;
|
|
1524
|
-
this.fragmentBuffer = null;
|
|
1525
|
-
this.h.onPayload(out);
|
|
1526
|
-
}
|
|
1527
|
-
break;
|
|
1528
|
-
case 1:
|
|
1529
|
-
// TEXT
|
|
1530
|
-
case 2:
|
|
1531
|
-
if (fin) {
|
|
1532
|
-
this.h.onPayload(payloadCopy);
|
|
1533
|
-
} else {
|
|
1534
|
-
this.fragmentBuffer = payloadCopy;
|
|
1535
|
-
}
|
|
1536
|
-
break;
|
|
1537
|
-
case 8:
|
|
1538
|
-
this.h.onClose();
|
|
1539
|
-
break;
|
|
1540
|
-
case 9:
|
|
1541
|
-
this.h.onPing(payloadCopy);
|
|
1542
|
-
break;
|
|
1543
|
-
case 10:
|
|
1544
|
-
break;
|
|
1545
|
-
default:
|
|
1546
|
-
throw new Error(`unknown WS opcode 0x${opcode.toString(16)}`);
|
|
1547
|
-
}
|
|
1548
|
-
}
|
|
1549
|
-
}
|
|
1550
|
-
};
|
|
1551
|
-
function createUpstreamTextFrame(payload) {
|
|
1552
|
-
return buildClientFrame(129, payload);
|
|
1553
|
-
}
|
|
1554
|
-
function createUpstreamPongFrame(payload) {
|
|
1555
|
-
return buildClientFrame(138, payload);
|
|
1556
|
-
}
|
|
1557
|
-
function buildClientFrame(firstByte, payload) {
|
|
1558
|
-
const len = payload.length;
|
|
1559
|
-
const maskKey = crypto.randomBytes(4);
|
|
1560
|
-
let header;
|
|
1561
|
-
if (len < 126) {
|
|
1562
|
-
header = Buffer.alloc(2);
|
|
1563
|
-
header[0] = firstByte;
|
|
1564
|
-
header[1] = 128 | len;
|
|
1565
|
-
} else if (len < 65536) {
|
|
1566
|
-
header = Buffer.alloc(4);
|
|
1567
|
-
header[0] = firstByte;
|
|
1568
|
-
header[1] = 128 | 126;
|
|
1569
|
-
header.writeUInt16BE(len, 2);
|
|
1570
|
-
} else {
|
|
1571
|
-
header = Buffer.alloc(10);
|
|
1572
|
-
header[0] = firstByte;
|
|
1573
|
-
header[1] = 128 | 127;
|
|
1574
|
-
header.writeBigUInt64BE(BigInt(len), 2);
|
|
1575
|
-
}
|
|
1576
|
-
const masked = Buffer.alloc(len);
|
|
1577
|
-
for (let i = 0; i < len; i++) {
|
|
1578
|
-
masked[i] = payload[i] ^ maskKey[i % 4];
|
|
1579
|
-
}
|
|
1580
|
-
return Buffer.concat([header, maskKey, masked]);
|
|
1581
|
-
}
|
|
1582
659
|
var WsFrameParser = class {
|
|
1583
660
|
constructor(handlers) {
|
|
1584
661
|
this.buffer = Buffer.alloc(0);
|
|
1585
662
|
this.onMessage = handlers.onMessage;
|
|
1586
|
-
this.onBinary = handlers.onBinary;
|
|
1587
663
|
this.onClose = handlers.onClose;
|
|
1588
664
|
this.onPing = handlers.onPing;
|
|
1589
665
|
}
|
|
@@ -1628,13 +704,6 @@ var WsFrameParser = class {
|
|
|
1628
704
|
case 1:
|
|
1629
705
|
this.onMessage(payload.toString("utf-8"));
|
|
1630
706
|
break;
|
|
1631
|
-
case 2:
|
|
1632
|
-
if (this.onBinary) {
|
|
1633
|
-
const copy = Buffer.alloc(payload.length);
|
|
1634
|
-
payload.copy(copy);
|
|
1635
|
-
this.onBinary(copy);
|
|
1636
|
-
}
|
|
1637
|
-
break;
|
|
1638
707
|
case 8:
|
|
1639
708
|
this.onClose();
|
|
1640
709
|
break;
|
|
@@ -1656,195 +725,27 @@ function getTunnelServerUrl(baseUrl) {
|
|
|
1656
725
|
}
|
|
1657
726
|
return baseUrl.replace(/:\d+/, ":8090");
|
|
1658
727
|
}
|
|
1659
|
-
async function resolveApp(secretKey, baseUrl, appIdOption) {
|
|
1660
|
-
if (appIdOption) {
|
|
1661
|
-
return { appId: appIdOption };
|
|
1662
|
-
}
|
|
1663
|
-
let apps = [];
|
|
1664
|
-
try {
|
|
1665
|
-
info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
1666
|
-
const appsRes = await makeRequest(
|
|
1667
|
-
`${baseUrl}/v1/public/cli/apps`,
|
|
1668
|
-
"GET",
|
|
1669
|
-
{ "X-Public-Key": secretKey }
|
|
1670
|
-
);
|
|
1671
|
-
if (appsRes.status === 401) {
|
|
1672
|
-
error("Secret Key\uAC00 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF58\uC194\uC5D0\uC11C \uD0A4\uB97C \uD655\uC778\uD558\uC138\uC694");
|
|
1673
|
-
process.exit(1);
|
|
1674
|
-
}
|
|
1675
|
-
if (appsRes.status !== 200) {
|
|
1676
|
-
throw new Error(`HTTP ${appsRes.status}`);
|
|
1677
|
-
}
|
|
1678
|
-
const appsData = appsRes.data;
|
|
1679
|
-
apps = appsData.apps || [];
|
|
1680
|
-
} catch (err) {
|
|
1681
|
-
error(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
1682
|
-
process.exit(1);
|
|
1683
|
-
}
|
|
1684
|
-
if (apps.length === 1) {
|
|
1685
|
-
success(`\uC571 \uC790\uB3D9 \uC120\uD0DD: ${apps[0].name}`);
|
|
1686
|
-
return { appId: apps[0].id };
|
|
1687
|
-
}
|
|
1688
|
-
if (apps.length > 0) {
|
|
1689
|
-
log(`
|
|
1690
|
-
${colors.dim}\uB0B4 \uC571 \uBAA9\uB85D:${colors.reset}`);
|
|
1691
|
-
apps.forEach((a, i) => {
|
|
1692
|
-
const date = a.created_at ? a.created_at.substring(0, 10) : "";
|
|
1693
|
-
log(` ${colors.cyan}${i + 1}${colors.reset}) ${a.name} ${colors.dim}(${date})${colors.reset}`);
|
|
1694
|
-
});
|
|
1695
|
-
}
|
|
1696
|
-
log(` ${colors.cyan}0${colors.reset}) \uC0C8 \uC571 \uB9CC\uB4E4\uAE30`);
|
|
1697
|
-
const choice = await prompt(`
|
|
1698
|
-
${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
|
|
1699
|
-
const num = parseInt(choice, 10);
|
|
1700
|
-
if (num > 0 && num <= apps.length) {
|
|
1701
|
-
success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
|
|
1702
|
-
return { appId: apps[num - 1].id };
|
|
1703
|
-
}
|
|
1704
|
-
const projectName = path2.basename(process.cwd());
|
|
1705
|
-
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1706
|
-
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
1707
|
-
const createRes = await makeRequest(
|
|
1708
|
-
`${baseUrl}/v1/public/cli/apps`,
|
|
1709
|
-
"POST",
|
|
1710
|
-
{ "X-Public-Key": secretKey },
|
|
1711
|
-
JSON.stringify({ name: appName })
|
|
1712
|
-
);
|
|
1713
|
-
if (createRes.status === 402) {
|
|
1714
|
-
error("\uC571 \uC0DD\uC131 \uD55C\uB3C4 \uCD08\uACFC. \uD50C\uB79C \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
1715
|
-
process.exit(1);
|
|
1716
|
-
}
|
|
1717
|
-
if (createRes.status !== 201) {
|
|
1718
|
-
const data = createRes.data;
|
|
1719
|
-
error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
|
|
1720
|
-
process.exit(1);
|
|
1721
|
-
}
|
|
1722
|
-
const createData = createRes.data;
|
|
1723
|
-
success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
|
|
1724
|
-
return { appId: createData.app_id, publicKey: createData.public_key };
|
|
1725
|
-
}
|
|
1726
|
-
async function registerEndpointBinding(baseUrl, appId, secretKey, tunnelId, label, description) {
|
|
1727
|
-
try {
|
|
1728
|
-
const apiBase = baseUrl.replace(/\/+$/, "");
|
|
1729
|
-
const res = await fetch(`${apiBase}/v1/apps/${encodeURIComponent(appId)}/endpoints/cli`, {
|
|
1730
|
-
method: "POST",
|
|
1731
|
-
headers: {
|
|
1732
|
-
"Content-Type": "application/json",
|
|
1733
|
-
"X-Public-Key": secretKey
|
|
1734
|
-
},
|
|
1735
|
-
body: JSON.stringify({
|
|
1736
|
-
label,
|
|
1737
|
-
tunnel_id: tunnelId,
|
|
1738
|
-
description: description ?? `CLI tunnel start (${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)})`
|
|
1739
|
-
})
|
|
1740
|
-
});
|
|
1741
|
-
if (res.status === 201) {
|
|
1742
|
-
success(`Endpoint "${label}" \uC790\uB3D9 \uB4F1\uB85D \uC644\uB8CC`);
|
|
1743
|
-
log(`${colors.green}\u2192${colors.reset} SDK: ${colors.cyan}cb.endpoint.call("${label}", { path: "/...", method: "POST", body: ... })${colors.reset}`);
|
|
1744
|
-
} else if (res.status === 409) {
|
|
1745
|
-
warn(`"${label}" \uB77C\uBCA8\uC774 \uC774\uBBF8 \uB4F1\uB85D\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4. \uC0C8 tunnel_id \uB85C \uAC31\uC2E0\uD558\uB824\uBA74 \uCF58\uC194\uC5D0\uC11C \uC218\uB3D9 PATCH.`);
|
|
1746
|
-
} else {
|
|
1747
|
-
const text = await res.text().catch(() => "");
|
|
1748
|
-
error(`Endpoint "${label}" \uC790\uB3D9 \uB4F1\uB85D \uC2E4\uD328 (status ${res.status}): ${text || "(\uBE48 \uC751\uB2F5)"}`);
|
|
1749
|
-
if (res.status === 401) {
|
|
1750
|
-
info("cb_sk_ Secret Key \uC778\uC9C0, \uCF58\uC194\uC5D0\uC11C \uD68C\uC218\uB418\uC9C0 \uC54A\uC558\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694.");
|
|
1751
|
-
}
|
|
1752
|
-
}
|
|
1753
|
-
} catch (err) {
|
|
1754
|
-
error(`Endpoint "${label}" \uC790\uB3D9 \uB4F1\uB85D \uC911 \uB124\uD2B8\uC6CC\uD06C \uC624\uB958: ${err instanceof Error ? err.message : err}`);
|
|
1755
|
-
}
|
|
1756
|
-
}
|
|
1757
|
-
function acquireTunnelLock2(appID, port, force) {
|
|
1758
|
-
const result = acquireTunnelLock(appID, port, force, VERSION);
|
|
1759
|
-
if (result && result.startsWith("LOCKED:")) {
|
|
1760
|
-
const [, pid, startedAt, host] = result.split(":");
|
|
1761
|
-
error(`\uC774\uBBF8 \uC2E4\uD589 \uC911\uC778 \uD130\uB110\uC774 \uC788\uC2B5\uB2C8\uB2E4: PID ${pid}, \uC2DC\uC791 ${startedAt}, \uD638\uC2A4\uD2B8 ${host}`);
|
|
1762
|
-
info("\uC911\uBCF5 \uC2E4\uD589\uC744 \uBB34\uC2DC\uD558\uB824\uBA74 --force \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694");
|
|
1763
|
-
process.exit(2);
|
|
1764
|
-
}
|
|
1765
|
-
return result;
|
|
1766
|
-
}
|
|
1767
728
|
async function startTunnel(port, config, tunnelOpts) {
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
try {
|
|
1772
|
-
const sk = await browserAuthFlow();
|
|
1773
|
-
const rcPath2 = path2.join(process.cwd(), ".connectbaserc");
|
|
1774
|
-
let rcData = {};
|
|
1775
|
-
if (fs2.existsSync(rcPath2)) {
|
|
1776
|
-
try {
|
|
1777
|
-
rcData = JSON.parse(fs2.readFileSync(rcPath2, "utf-8"));
|
|
1778
|
-
} catch {
|
|
1779
|
-
}
|
|
1780
|
-
}
|
|
1781
|
-
rcData.secretKey = sk;
|
|
1782
|
-
fs2.writeFileSync(rcPath2, JSON.stringify(rcData, null, 2) + "\n");
|
|
1783
|
-
addToGitignore(".connectbaserc");
|
|
1784
|
-
success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
|
|
1785
|
-
tunnelKey = sk;
|
|
1786
|
-
config.secretKey = sk;
|
|
1787
|
-
} catch (err) {
|
|
1788
|
-
error(`\uC778\uC99D \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
1789
|
-
info("-k \uC635\uC158\uC73C\uB85C Secret Key\uB97C \uC9C1\uC811 \uC804\uB2EC\uD560 \uC218\uB3C4 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1790
|
-
process.exit(1);
|
|
1791
|
-
}
|
|
1792
|
-
} else {
|
|
1793
|
-
config.secretKey = tunnelKey;
|
|
1794
|
-
}
|
|
1795
|
-
const rcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
1796
|
-
let savedAppId = tunnelOpts?.appId || "";
|
|
1797
|
-
if (!savedAppId) {
|
|
1798
|
-
try {
|
|
1799
|
-
const rc = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1800
|
-
if (rc.tunnelAppId) savedAppId = rc.tunnelAppId;
|
|
1801
|
-
} catch {
|
|
1802
|
-
}
|
|
1803
|
-
}
|
|
1804
|
-
const { appId } = await resolveApp(tunnelKey, config.baseUrl, savedAppId);
|
|
1805
|
-
try {
|
|
1806
|
-
let rcData = {};
|
|
1807
|
-
if (fs2.existsSync(rcPath)) {
|
|
1808
|
-
try {
|
|
1809
|
-
rcData = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1810
|
-
} catch {
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
if (rcData.tunnelAppId !== appId) {
|
|
1814
|
-
rcData.tunnelAppId = appId;
|
|
1815
|
-
fs2.writeFileSync(rcPath, JSON.stringify(rcData, null, 2) + "\n");
|
|
1816
|
-
}
|
|
1817
|
-
} catch {
|
|
729
|
+
if (!config.apiKey) {
|
|
730
|
+
error("API Key\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. -k \uC635\uC158 \uB610\uB294 CONNECTBASE_API_KEY \uD658\uACBD\uBCC0\uC218\uB97C \uC124\uC815\uD558\uC138\uC694");
|
|
731
|
+
process.exit(1);
|
|
1818
732
|
}
|
|
1819
|
-
const lockPath = acquireTunnelLock2(appId, port, tunnelOpts?.force ?? false);
|
|
1820
733
|
const tunnelServerUrl = getTunnelServerUrl(config.baseUrl);
|
|
1821
734
|
const parsedUrl = new URL(tunnelServerUrl);
|
|
1822
735
|
const isHttps = parsedUrl.protocol === "https:";
|
|
1823
|
-
let wsPath = `/
|
|
736
|
+
let wsPath = `/v1/tunnel/connect?api_key=${encodeURIComponent(config.apiKey)}`;
|
|
1824
737
|
if (tunnelOpts?.timeout) {
|
|
1825
738
|
wsPath += `&timeout=${tunnelOpts.timeout}`;
|
|
1826
739
|
}
|
|
1827
740
|
if (tunnelOpts?.maxBody) {
|
|
1828
741
|
wsPath += `&max_body=${tunnelOpts.maxBody}`;
|
|
1829
742
|
}
|
|
1830
|
-
if (tunnelOpts?.public) {
|
|
1831
|
-
wsPath += `&public=1`;
|
|
1832
|
-
}
|
|
1833
743
|
let reconnectAttempts = 0;
|
|
1834
744
|
const maxReconnectAttempts = 10;
|
|
1835
745
|
let shouldReconnect = true;
|
|
1836
746
|
let socket = null;
|
|
1837
|
-
let lockReleased = false;
|
|
1838
|
-
const releaseLock = () => {
|
|
1839
|
-
if (lockPath && !lockReleased) {
|
|
1840
|
-
lockReleased = true;
|
|
1841
|
-
releaseTunnelLock(lockPath);
|
|
1842
|
-
}
|
|
1843
|
-
};
|
|
1844
|
-
process.on("exit", releaseLock);
|
|
1845
747
|
const cleanup = () => {
|
|
1846
748
|
shouldReconnect = false;
|
|
1847
|
-
releaseLock();
|
|
1848
749
|
if (socket) {
|
|
1849
750
|
try {
|
|
1850
751
|
socket.write(createWsCloseFrame(1e3));
|
|
@@ -1878,8 +779,7 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1878
779
|
"Upgrade": "websocket",
|
|
1879
780
|
"Connection": "Upgrade",
|
|
1880
781
|
"Sec-WebSocket-Key": wsKey,
|
|
1881
|
-
"Sec-WebSocket-Version": "13"
|
|
1882
|
-
"Authorization": `Bearer ${config.secretKey ?? config.publicKey}`
|
|
782
|
+
"Sec-WebSocket-Version": "13"
|
|
1883
783
|
}
|
|
1884
784
|
};
|
|
1885
785
|
const req = lib.request(reqOptions);
|
|
@@ -1890,22 +790,11 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1890
790
|
onMessage: (data) => {
|
|
1891
791
|
try {
|
|
1892
792
|
const msg = JSON.parse(data);
|
|
1893
|
-
handleMessage(msg, sock, port)
|
|
1894
|
-
error(`\uBA54\uC2DC\uC9C0 \uCC98\uB9AC \uC911 \uC608\uC678: ${e instanceof Error ? e.message : e}`);
|
|
1895
|
-
});
|
|
793
|
+
handleMessage(msg, sock, port);
|
|
1896
794
|
} catch (e) {
|
|
1897
795
|
warn(`\uBA54\uC2DC\uC9C0 \uD30C\uC2F1 \uC2E4\uD328: ${e}`);
|
|
1898
796
|
}
|
|
1899
797
|
},
|
|
1900
|
-
onBinary: (data) => {
|
|
1901
|
-
if (data.length < 8) {
|
|
1902
|
-
warn(`v2 binary frame too short (${data.length} bytes)`);
|
|
1903
|
-
return;
|
|
1904
|
-
}
|
|
1905
|
-
const streamId = data.readBigUInt64BE(0).toString();
|
|
1906
|
-
const payload = data.subarray(8);
|
|
1907
|
-
routeStreamData(streamId, payload);
|
|
1908
|
-
},
|
|
1909
798
|
onClose: () => {
|
|
1910
799
|
info("\uC11C\uBC84\uAC00 \uC5F0\uACB0\uC744 \uC885\uB8CC\uD588\uC2B5\uB2C8\uB2E4");
|
|
1911
800
|
sock.destroy();
|
|
@@ -1969,314 +858,95 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1969
858
|
info(`${(delay / 1e3).toFixed(0)}\uCD08 \uD6C4 \uC7AC\uC5F0\uACB0 \uC2DC\uB3C4... (${reconnectAttempts}/${maxReconnectAttempts})`);
|
|
1970
859
|
setTimeout(connect, delay);
|
|
1971
860
|
}
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
861
|
+
function handleMessage(msg, sock, localPort) {
|
|
862
|
+
switch (msg.type) {
|
|
863
|
+
case "tunnel_ready":
|
|
864
|
+
success(`\uD130\uB110 \uD65C\uC131\uD654!`);
|
|
865
|
+
log(`${colors.green}\u2192${colors.reset} URL: ${colors.cyan}${msg.url}${colors.reset}`);
|
|
866
|
+
log(`${colors.green}\u2192${colors.reset} \uB85C\uCEEC: ${colors.cyan}http://localhost:${localPort}${colors.reset}`);
|
|
867
|
+
if (msg.timeout || msg.max_body) {
|
|
868
|
+
log(`${colors.green}\u2192${colors.reset} \uC124\uC815: timeout=${colors.cyan}${msg.timeout}s${colors.reset}, max-body=${colors.cyan}${msg.max_body}MB${colors.reset}`);
|
|
869
|
+
}
|
|
870
|
+
log(`
|
|
871
|
+
${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
872
|
+
`);
|
|
873
|
+
break;
|
|
874
|
+
case "http_request":
|
|
875
|
+
forwardRequest(msg, sock, localPort);
|
|
876
|
+
break;
|
|
877
|
+
case "tunnel_error":
|
|
878
|
+
error(`\uD130\uB110 \uC5D0\uB7EC: ${msg.message}`);
|
|
879
|
+
break;
|
|
880
|
+
case "ping":
|
|
881
|
+
sock.write(createWsTextFrame(JSON.stringify({ type: "pong" })));
|
|
882
|
+
break;
|
|
1990
883
|
}
|
|
1991
884
|
}
|
|
1992
|
-
function
|
|
1993
|
-
const
|
|
1994
|
-
const
|
|
1995
|
-
const
|
|
1996
|
-
const
|
|
885
|
+
function forwardRequest(msg, sock, localPort) {
|
|
886
|
+
const requestId = msg.request_id;
|
|
887
|
+
const method = msg.method;
|
|
888
|
+
const reqPath = msg.path;
|
|
889
|
+
const query = msg.query || "";
|
|
890
|
+
const headers = msg.headers || {};
|
|
891
|
+
const bodyBase64 = msg.body;
|
|
1997
892
|
const fullPath = query ? `${reqPath}?${query}` : reqPath;
|
|
1998
893
|
const localHeaders = {};
|
|
1999
|
-
for (const [
|
|
2000
|
-
if (
|
|
2001
|
-
|
|
2002
|
-
localHeaders["host"] = `localhost:${localPort}`;
|
|
2003
|
-
let started = false;
|
|
2004
|
-
let cancelled = false;
|
|
2005
|
-
const localReq = http.request(
|
|
2006
|
-
{ hostname: "127.0.0.1", port: localPort, path: fullPath, method, headers: localHeaders },
|
|
2007
|
-
(res) => {
|
|
2008
|
-
const respHeaders = {};
|
|
2009
|
-
for (const [k, v] of Object.entries(res.headers)) {
|
|
2010
|
-
if (v == null) continue;
|
|
2011
|
-
respHeaders[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
2012
|
-
}
|
|
2013
|
-
started = true;
|
|
2014
|
-
sendControl(sock, {
|
|
2015
|
-
type: "stream_response",
|
|
2016
|
-
stream_id: streamId,
|
|
2017
|
-
status: res.statusCode || 502,
|
|
2018
|
-
headers: respHeaders
|
|
2019
|
-
});
|
|
2020
|
-
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
2021
|
-
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
|
|
2022
|
-
res.on("data", (chunk) => {
|
|
2023
|
-
if (cancelled) return;
|
|
2024
|
-
sendBinary(sock, streamId, chunk);
|
|
2025
|
-
});
|
|
2026
|
-
res.on("end", () => {
|
|
2027
|
-
if (cancelled) return;
|
|
2028
|
-
sendControl(sock, { type: "stream_eof", stream_id: streamId, side: "upstream" });
|
|
2029
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId });
|
|
2030
|
-
streams.delete(streamId);
|
|
2031
|
-
});
|
|
2032
|
-
res.on("error", (err) => {
|
|
2033
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_error: ${err.message}` });
|
|
2034
|
-
streams.delete(streamId);
|
|
2035
|
-
});
|
|
2036
|
-
}
|
|
2037
|
-
);
|
|
2038
|
-
localReq.on("error", (err) => {
|
|
2039
|
-
warn(`\uB85C\uCEEC \uC11C\uBC84 \uC5F0\uACB0 \uC2E4\uD328 (${method} ${reqPath}): ${err.message}`);
|
|
2040
|
-
if (!started) {
|
|
2041
|
-
sendControl(sock, {
|
|
2042
|
-
type: "stream_response",
|
|
2043
|
-
stream_id: streamId,
|
|
2044
|
-
status: 502,
|
|
2045
|
-
headers: { "content-type": "application/json" }
|
|
2046
|
-
});
|
|
2047
|
-
sendBinary(sock, streamId, Buffer.from(JSON.stringify({ error: `Local server error: ${err.message}` })));
|
|
2048
|
-
}
|
|
2049
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_error: ${err.message}` });
|
|
2050
|
-
streams.delete(streamId);
|
|
2051
|
-
});
|
|
2052
|
-
const forwarder = {
|
|
2053
|
-
kind: "http",
|
|
2054
|
-
feedBody: (chunk) => {
|
|
2055
|
-
if (!cancelled) localReq.write(chunk);
|
|
2056
|
-
},
|
|
2057
|
-
endBody: () => {
|
|
2058
|
-
if (!cancelled) localReq.end();
|
|
2059
|
-
},
|
|
2060
|
-
cancel: () => {
|
|
2061
|
-
cancelled = true;
|
|
2062
|
-
try {
|
|
2063
|
-
localReq.destroy();
|
|
2064
|
-
} catch {
|
|
2065
|
-
}
|
|
894
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
895
|
+
if (key.toLowerCase() !== "host") {
|
|
896
|
+
localHeaders[key] = value;
|
|
2066
897
|
}
|
|
2067
|
-
};
|
|
2068
|
-
streams.set(streamId, forwarder);
|
|
2069
|
-
}
|
|
2070
|
-
function startWSStream(sock, streamId, open, localPort) {
|
|
2071
|
-
const reqPath = open.path || "/";
|
|
2072
|
-
const query = open.query || "";
|
|
2073
|
-
const headers = open.headers || {};
|
|
2074
|
-
const fullPath = query ? `${reqPath}?${query}` : reqPath;
|
|
2075
|
-
const localHeaders = {};
|
|
2076
|
-
for (const [k, v] of Object.entries(headers)) {
|
|
2077
|
-
const lk = k.toLowerCase();
|
|
2078
|
-
if (lk === "host") continue;
|
|
2079
|
-
if (lk === "sec-websocket-extensions") continue;
|
|
2080
|
-
localHeaders[k] = v;
|
|
2081
898
|
}
|
|
2082
899
|
localHeaders["host"] = `localhost:${localPort}`;
|
|
2083
|
-
|
|
2084
|
-
localHeaders["upgrade"] = "websocket";
|
|
2085
|
-
let cancelled = false;
|
|
2086
|
-
let upstream = null;
|
|
2087
|
-
const req = http.request({
|
|
900
|
+
const reqOptions = {
|
|
2088
901
|
hostname: "127.0.0.1",
|
|
2089
902
|
port: localPort,
|
|
2090
903
|
path: fullPath,
|
|
2091
|
-
method
|
|
904
|
+
method,
|
|
2092
905
|
headers: localHeaders
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
type: "stream_response",
|
|
2103
|
-
stream_id: streamId,
|
|
2104
|
-
status: res.statusCode || 101,
|
|
2105
|
-
headers: respHeaders,
|
|
2106
|
-
websocket: true
|
|
2107
|
-
});
|
|
2108
|
-
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${colors.cyan}WS${colors.reset} ${reqPath} \u2192 101`);
|
|
2109
|
-
const upstreamParser = new UpstreamWsFrameParser({
|
|
2110
|
-
onPayload: (payload) => {
|
|
2111
|
-
if (cancelled) return;
|
|
2112
|
-
sendBinary(sock, streamId, payload);
|
|
2113
|
-
},
|
|
2114
|
-
onPing: (payload) => {
|
|
2115
|
-
if (cancelled || !upstream) return;
|
|
2116
|
-
upstream.write(createUpstreamPongFrame(payload));
|
|
2117
|
-
},
|
|
2118
|
-
onClose: () => {
|
|
2119
|
-
if (cancelled) return;
|
|
2120
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId });
|
|
2121
|
-
streams.delete(streamId);
|
|
2122
|
-
},
|
|
2123
|
-
onError: (err) => {
|
|
2124
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_frame_error: ${err.message}` });
|
|
2125
|
-
streams.delete(streamId);
|
|
906
|
+
};
|
|
907
|
+
const localReq = http.request(reqOptions, (res) => {
|
|
908
|
+
const chunks = [];
|
|
909
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
910
|
+
res.on("end", () => {
|
|
911
|
+
const body = Buffer.concat(chunks);
|
|
912
|
+
const responseHeaders = {};
|
|
913
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
914
|
+
if (value) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
|
|
2126
915
|
}
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId });
|
|
2135
|
-
streams.delete(streamId);
|
|
2136
|
-
});
|
|
2137
|
-
sk.on("error", (err) => {
|
|
2138
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_error: ${err.message}` });
|
|
2139
|
-
streams.delete(streamId);
|
|
2140
|
-
});
|
|
2141
|
-
});
|
|
2142
|
-
req.on("response", (res) => {
|
|
2143
|
-
const respHeaders = {};
|
|
2144
|
-
for (const [k, v] of Object.entries(res.headers)) {
|
|
2145
|
-
if (v == null) continue;
|
|
2146
|
-
respHeaders[k] = Array.isArray(v) ? v.join(", ") : v;
|
|
2147
|
-
}
|
|
2148
|
-
sendControl(sock, {
|
|
2149
|
-
type: "stream_response",
|
|
2150
|
-
stream_id: streamId,
|
|
2151
|
-
status: res.statusCode || 502,
|
|
2152
|
-
headers: respHeaders,
|
|
2153
|
-
websocket: false
|
|
2154
|
-
});
|
|
2155
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId });
|
|
2156
|
-
streams.delete(streamId);
|
|
2157
|
-
});
|
|
2158
|
-
req.on("error", (err) => {
|
|
2159
|
-
sendControl(sock, { type: "stream_close", stream_id: streamId, error: `upstream_error: ${err.message}` });
|
|
2160
|
-
streams.delete(streamId);
|
|
2161
|
-
});
|
|
2162
|
-
req.end();
|
|
2163
|
-
const forwarder = {
|
|
2164
|
-
kind: "ws",
|
|
2165
|
-
// Encode the v2 payload as an upstream WS frame BEFORE writing to
|
|
2166
|
-
// the socket. The prior implementation wrote raw payload to the
|
|
2167
|
-
// socket which is not a valid WS frame; upstream WS servers (e.g.
|
|
2168
|
-
// ComfyUI) would error out. Default to TEXT opcode — the v2
|
|
2169
|
-
// protocol does not propagate the original opcode from the client,
|
|
2170
|
-
// and most WS APIs (JSON-based) use text. Binary client→upstream
|
|
2171
|
-
// is a known limitation pending opcode propagation in v2.
|
|
2172
|
-
feedBody: (chunk) => {
|
|
2173
|
-
if (cancelled || !upstream) return;
|
|
2174
|
-
upstream.write(createUpstreamTextFrame(chunk));
|
|
2175
|
-
},
|
|
2176
|
-
endBody: () => {
|
|
2177
|
-
},
|
|
2178
|
-
cancel: () => {
|
|
2179
|
-
cancelled = true;
|
|
916
|
+
const response = {
|
|
917
|
+
type: "http_response",
|
|
918
|
+
request_id: requestId,
|
|
919
|
+
status: res.statusCode || 200,
|
|
920
|
+
headers: responseHeaders,
|
|
921
|
+
body: body.length > 0 ? body.toString("base64") : ""
|
|
922
|
+
};
|
|
2180
923
|
try {
|
|
2181
|
-
|
|
924
|
+
sock.write(createWsTextFrame(JSON.stringify(response)));
|
|
925
|
+
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
926
|
+
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
|
|
2182
927
|
} catch {
|
|
928
|
+
warn(`\uC751\uB2F5 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
2183
929
|
}
|
|
930
|
+
});
|
|
931
|
+
});
|
|
932
|
+
localReq.on("error", (err) => {
|
|
933
|
+
const response = {
|
|
934
|
+
type: "http_response",
|
|
935
|
+
request_id: requestId,
|
|
936
|
+
status: 502,
|
|
937
|
+
headers: { "content-type": "application/json" },
|
|
938
|
+
body: Buffer.from(JSON.stringify({ error: `Local server error: ${err.message}` })).toString("base64")
|
|
939
|
+
};
|
|
940
|
+
try {
|
|
941
|
+
sock.write(createWsTextFrame(JSON.stringify(response)));
|
|
942
|
+
} catch {
|
|
2184
943
|
}
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
|
|
2188
|
-
|
|
2189
|
-
switch (msg.type) {
|
|
2190
|
-
case "tunnel_ready": {
|
|
2191
|
-
const proxyToken = typeof msg.proxy_token === "string" ? msg.proxy_token : "";
|
|
2192
|
-
const tunnelUrl = typeof msg.url === "string" ? msg.url : "";
|
|
2193
|
-
const isPublic = msg.public === true;
|
|
2194
|
-
success(`\uD130\uB110 \uD65C\uC131\uD654!`);
|
|
2195
|
-
log(`${colors.green}\u2192${colors.reset} URL: ${colors.cyan}${tunnelUrl}${colors.reset}`);
|
|
2196
|
-
log(`${colors.green}\u2192${colors.reset} \uB85C\uCEEC: ${colors.cyan}http://localhost:${localPort}${colors.reset}`);
|
|
2197
|
-
if (msg.timeout || msg.max_body) {
|
|
2198
|
-
log(`${colors.green}\u2192${colors.reset} \uC124\uC815: timeout=${colors.cyan}${msg.timeout}s${colors.reset}, max-body=${colors.cyan}${msg.max_body}MB${colors.reset}`);
|
|
2199
|
-
}
|
|
2200
|
-
if (isPublic) {
|
|
2201
|
-
log("");
|
|
2202
|
-
log(`${colors.yellow}\u26A0 \uACF5\uAC1C \uBAA8\uB4DC (--public):${colors.reset} proxy_token \uAC80\uC99D\uC774 \uBE44\uD65C\uC131\uD654\uB410\uC2B5\uB2C8\uB2E4.`);
|
|
2203
|
-
log(` ${colors.dim}tunnelID \uB97C \uC544\uB294 \uB204\uAD6C\uB098 \uC774 URL \uB85C \uB85C\uCEEC \uC11C\uBE44\uC2A4\uC5D0 \uC811\uADFC\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.${colors.reset}`);
|
|
2204
|
-
log(` ${colors.dim}\uC6F9\uD6C5 \uC218\uC2E0 / \uC678\uBD80 \uC11C\uBE44\uC2A4 \uC5F0\uB3D9 \uBAA9\uC801\uC5D0 \uD55C\uD574 \uC0AC\uC6A9\uD558\uACE0, \uBBFC\uAC10 \uB370\uC774\uD130\uB294 \uCDE8\uAE09\uD558\uC9C0 \uB9C8\uC138\uC694.${colors.reset}`);
|
|
2205
|
-
log("");
|
|
2206
|
-
log(`${colors.dim} $ curl ${tunnelUrl}/${colors.reset}`);
|
|
2207
|
-
} else if (tunnelOpts?.showToken && proxyToken && tunnelUrl) {
|
|
2208
|
-
log("");
|
|
2209
|
-
log(`${colors.green}\u2192${colors.reset} \uD1A0\uD070: ${colors.yellow}${proxyToken}${colors.reset}`);
|
|
2210
|
-
log(`
|
|
2211
|
-
${colors.dim}\uC678\uBD80 \uC9C1\uC811 \uD638\uCD9C \uC2DC \uB2E4\uC74C \uD5E4\uB354 \uB610\uB294 \uCFFC\uB9AC\uB85C \uD1A0\uD070\uC744 \uC804\uB2EC\uD558\uC138\uC694:${colors.reset}`);
|
|
2212
|
-
log(`${colors.dim} $ curl -H "X-Proxy-Token: ${proxyToken}" ${tunnelUrl}/${colors.reset}`);
|
|
2213
|
-
log(`${colors.dim} $ curl "${tunnelUrl}/?proxy_token=${proxyToken}"${colors.reset}`);
|
|
2214
|
-
}
|
|
2215
|
-
if (tunnelOpts?.label) {
|
|
2216
|
-
const tunnelId = typeof msg.tunnel_id === "string" ? msg.tunnel_id : "";
|
|
2217
|
-
if (!tunnelId) {
|
|
2218
|
-
error(`tunnel_ready \uBA54\uC2DC\uC9C0\uC5D0 tunnel_id \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 endpoint "${tunnelOpts.label}" \uC790\uB3D9 \uB4F1\uB85D skip`);
|
|
2219
|
-
} else {
|
|
2220
|
-
await registerEndpointBinding(
|
|
2221
|
-
config.baseUrl,
|
|
2222
|
-
appId,
|
|
2223
|
-
tunnelKey,
|
|
2224
|
-
tunnelId,
|
|
2225
|
-
tunnelOpts.label,
|
|
2226
|
-
tunnelOpts.description
|
|
2227
|
-
);
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
log(`
|
|
2231
|
-
${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
2232
|
-
`);
|
|
2233
|
-
break;
|
|
2234
|
-
}
|
|
2235
|
-
case "stream_open": {
|
|
2236
|
-
const sid = String(msg.stream_id ?? "");
|
|
2237
|
-
if (!sid) {
|
|
2238
|
-
warn("stream_open with empty stream_id");
|
|
2239
|
-
break;
|
|
2240
|
-
}
|
|
2241
|
-
const kind = msg.kind || "http";
|
|
2242
|
-
if (kind === "ws") {
|
|
2243
|
-
startWSStream(sock, sid, msg, localPort);
|
|
2244
|
-
} else {
|
|
2245
|
-
startHTTPStream(sock, sid, msg, localPort);
|
|
2246
|
-
}
|
|
2247
|
-
break;
|
|
2248
|
-
}
|
|
2249
|
-
case "stream_eof": {
|
|
2250
|
-
const sid = String(msg.stream_id ?? "");
|
|
2251
|
-
const side = msg.side;
|
|
2252
|
-
if (side === "client") {
|
|
2253
|
-
const f = streams.get(sid);
|
|
2254
|
-
f?.endBody();
|
|
2255
|
-
}
|
|
2256
|
-
break;
|
|
2257
|
-
}
|
|
2258
|
-
case "stream_close": {
|
|
2259
|
-
const sid = String(msg.stream_id ?? "");
|
|
2260
|
-
const f = streams.get(sid);
|
|
2261
|
-
if (f) {
|
|
2262
|
-
f.cancel();
|
|
2263
|
-
streams.delete(sid);
|
|
2264
|
-
}
|
|
2265
|
-
break;
|
|
2266
|
-
}
|
|
2267
|
-
case "tunnel_error": {
|
|
2268
|
-
const result = handleTunnelError(msg, appId, localPort);
|
|
2269
|
-
error(result.message);
|
|
2270
|
-
if (result.action === "exit") {
|
|
2271
|
-
shouldReconnect = false;
|
|
2272
|
-
setTimeout(() => process.exit(result.exitCode ?? 1), 500);
|
|
2273
|
-
}
|
|
2274
|
-
break;
|
|
2275
|
-
}
|
|
2276
|
-
case "ping":
|
|
2277
|
-
sock.write(createWsTextFrame(JSON.stringify({ type: "pong" })));
|
|
2278
|
-
break;
|
|
944
|
+
warn(`\uB85C\uCEEC \uC11C\uBC84 \uC5F0\uACB0 \uC2E4\uD328 (${method} ${reqPath}): ${err.message}`);
|
|
945
|
+
});
|
|
946
|
+
if (bodyBase64) {
|
|
947
|
+
localReq.write(Buffer.from(bodyBase64, "base64"));
|
|
2279
948
|
}
|
|
949
|
+
localReq.end();
|
|
2280
950
|
}
|
|
2281
951
|
connect();
|
|
2282
952
|
await new Promise(() => {
|
|
@@ -2284,16 +954,14 @@ ${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
|
2284
954
|
}
|
|
2285
955
|
function showHelp() {
|
|
2286
956
|
log(`
|
|
2287
|
-
${colors.cyan}connectbase${colors.reset} - Connect Base SDK & CLI
|
|
957
|
+
${colors.cyan}connectbase-client${colors.reset} - Connect Base SDK & CLI
|
|
2288
958
|
|
|
2289
959
|
${colors.yellow}\uC0AC\uC6A9\uBC95:${colors.reset}
|
|
2290
|
-
npx connectbase <command> [options]
|
|
960
|
+
npx connectbase-client <command> [options]
|
|
2291
961
|
|
|
2292
962
|
${colors.yellow}\uBA85\uB839\uC5B4:${colors.reset}
|
|
2293
|
-
init \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 (\
|
|
2294
|
-
|
|
2295
|
-
docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (\uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
2296
|
-
mcp MCP \uC11C\uBC84 \uC124\uC815 (.mcp.json \uC0DD\uC131/\uC5C5\uB370\uC774\uD2B8, \uBAA8\uB178\uB808\uD3EC \uC790\uB3D9 \uAC10\uC9C0)
|
|
963
|
+
init \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654 (\uC124\uC815 \uD30C\uC77C \uC0DD\uC131)
|
|
964
|
+
docs SDK \uBB38\uC11C \uB2E4\uC6B4\uB85C\uB4DC/\uC5C5\uB370\uC774\uD2B8 (--monorepo: git root\uC5D0 \uC0DD\uC131)
|
|
2297
965
|
deploy <directory> \uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0\uC5D0 \uD30C\uC77C \uBC30\uD3EC (--dev: Dev \uD658\uACBD)
|
|
2298
966
|
tunnel <port> \uB85C\uCEEC \uC11C\uBE44\uC2A4\uB97C \uC778\uD130\uB137\uC5D0 \uB178\uCD9C
|
|
2299
967
|
|
|
@@ -2306,45 +974,27 @@ ${colors.yellow}\uCF54\uB4DC \uBD84\uC11D (MCP \uD1B5\uD574 \uC0AC\uC6A9):${colo
|
|
|
2306
974
|
|
|
2307
975
|
${colors.yellow}\uC635\uC158:${colors.reset}
|
|
2308
976
|
-s, --storage <id> \uC2A4\uD1A0\uB9AC\uC9C0 ID
|
|
2309
|
-
-k, --
|
|
977
|
+
-k, --api-key <key> API Key
|
|
2310
978
|
-u, --base-url <url> \uC11C\uBC84 URL (\uAE30\uBCF8: ${DEFAULT_BASE_URL})
|
|
2311
|
-
-t, --timeout <sec> \
|
|
979
|
+
-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)
|
|
2312
980
|
--max-body <MB> \uD130\uB110 \uCD5C\uB300 \uBC14\uB514 \uD06C\uAE30 (MB, tunnel \uC804\uC6A9)
|
|
2313
|
-
--force \uD130\uB110 lockfile \uBB34\uC2DC (\uC911\uBCF5 \uC2E4\uD589 \uAC15\uC81C, tunnel \uC804\uC6A9)
|
|
2314
|
-
--public proxy_token \uAC80\uC99D \uBE44\uD65C\uC131\uD654 \u2014 \uC6F9\uD6C5/\uC678\uBD80 \uC9C1\uC811 \uD638\uCD9C\uC6A9 (tunnel \uC804\uC6A9)
|
|
2315
|
-
--show-token proxy_token \uACFC curl \uC608\uC2DC\uB97C \uCD9C\uB825 (--public \uC544\uB2CC \uACBD\uC6B0)
|
|
2316
|
-
--label <name> Endpoint binding \uC790\uB3D9 \uB4F1\uB85D (tunnel \uC804\uC6A9) \u2014 SDK \uC758 cb.endpoint.call(label) \uD638\uCD9C \uAC00\uB2A5
|
|
2317
|
-
--description <text> Endpoint binding \uC124\uBA85 (--label \uB3D9\uBC18 \uC2DC\uB9CC)
|
|
2318
981
|
-d, --dev Dev \uD658\uACBD\uC5D0 \uBC30\uD3EC (deploy \uC804\uC6A9)
|
|
2319
|
-
--
|
|
2320
|
-
--skip-docs \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8 \uAC74\uB108\uB6F0\uAE30 (update \uC804\uC6A9)
|
|
2321
|
-
--skip-mcp MCP \uC5C5\uB370\uC774\uD2B8 \uAC74\uB108\uB6F0\uAE30 (update \uC804\uC6A9)
|
|
2322
|
-
--setup-root \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 (update \uC804\uC6A9)
|
|
2323
|
-
(docs, mcp, update\uB294 \uBAA8\uB178\uB808\uD3EC\uB97C \uC790\uB3D9 \uAC10\uC9C0\uD558\uC5EC \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC5D0 \uC0DD\uC131)
|
|
982
|
+
--monorepo \uBAA8\uB178\uB808\uD3EC git root\uC5D0 \uBB38\uC11C \uC0DD\uC131 (docs \uC804\uC6A9)
|
|
2324
983
|
-h, --help \uB3C4\uC6C0\uB9D0 \uD45C\uC2DC
|
|
2325
984
|
-v, --version \uBC84\uC804 \uD45C\uC2DC
|
|
2326
985
|
|
|
2327
986
|
${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
2328
987
|
${colors.dim}# 1. \uCD08\uAE30\uD654 (\uCD5C\uCD08 1\uD68C)${colors.reset}
|
|
2329
|
-
npx connectbase init
|
|
988
|
+
npx connectbase-client init
|
|
2330
989
|
|
|
2331
|
-
${colors.dim}# 2. SDK \
|
|
2332
|
-
npx connectbase update
|
|
2333
|
-
|
|
2334
|
-
${colors.dim}# 2-1. \uBAA8\uB178\uB808\uD3EC \uB8E8\uD2B8\uC5D0 \uD3B8\uC758 \uC2A4\uD06C\uB9BD\uD2B8 \uC124\uCE58${colors.reset}
|
|
2335
|
-
npx connectbase update --setup-root
|
|
2336
|
-
|
|
2337
|
-
${colors.dim}# 3. SDK \uBB38\uC11C\uB9CC \uC5C5\uB370\uC774\uD2B8${colors.reset}
|
|
990
|
+
${colors.dim}# 2. SDK \uBB38\uC11C \uC5C5\uB370\uC774\uD2B8${colors.reset}
|
|
2338
991
|
npx connectbase docs
|
|
2339
992
|
|
|
2340
|
-
${colors.dim}#
|
|
2341
|
-
npx connectbase mcp
|
|
2342
|
-
|
|
2343
|
-
${colors.dim}# 4. Prod \uBC30\uD3EC${colors.reset}
|
|
993
|
+
${colors.dim}# 3. Prod \uBC30\uD3EC${colors.reset}
|
|
2344
994
|
npm run deploy
|
|
2345
995
|
|
|
2346
996
|
${colors.dim}# 4. Dev \uD658\uACBD \uBC30\uD3EC (\uB0B4\uBD80 QA\uC6A9)${colors.reset}
|
|
2347
|
-
npx connectbase deploy ./dist --dev
|
|
997
|
+
npx connectbase-client deploy ./dist --dev
|
|
2348
998
|
|
|
2349
999
|
${colors.dim}# 5. \uD130\uB110 (\uAE30\uBCF8)${colors.reset}
|
|
2350
1000
|
npx connectbase tunnel 3000
|
|
@@ -2353,16 +1003,13 @@ ${colors.yellow}\uBE60\uB978 \uC2DC\uC791:${colors.reset}
|
|
|
2353
1003
|
npx connectbase tunnel 7860 --timeout 300 --max-body 50
|
|
2354
1004
|
|
|
2355
1005
|
${colors.yellow}\uD658\uACBD\uBCC0\uC218:${colors.reset}
|
|
2356
|
-
|
|
2357
|
-
CONNECTBASE_SECRET_KEY Secret Key (cb_sk_*, \uD130\uB110\uC6A9)
|
|
2358
|
-
CONNECTBASE_PUBLIC_KEY (deprecated) \uB808\uAC70\uC2DC \u2014 publicKey \uB610\uB294 secretKey \uB85C \uC0AC\uC6A9
|
|
1006
|
+
CONNECTBASE_API_KEY API Key
|
|
2359
1007
|
CONNECTBASE_STORAGE_ID \uC2A4\uD1A0\uB9AC\uC9C0 ID
|
|
2360
1008
|
CONNECTBASE_BASE_URL \uC11C\uBC84 URL
|
|
2361
1009
|
|
|
2362
1010
|
${colors.yellow}\uC124\uC815 \uD30C\uC77C (.connectbaserc):${colors.reset}
|
|
2363
1011
|
{
|
|
2364
|
-
"
|
|
2365
|
-
"secretKey": "cb_sk_...",
|
|
1012
|
+
"apiKey": "your-api-key",
|
|
2366
1013
|
"storageId": "your-storage-id",
|
|
2367
1014
|
"deployDir": "./dist"
|
|
2368
1015
|
}
|
|
@@ -2378,38 +1025,18 @@ function parseArgs(args) {
|
|
|
2378
1025
|
const arg = args[i];
|
|
2379
1026
|
if (arg === "-s" || arg === "--storage") {
|
|
2380
1027
|
result.options.storageId = args[++i];
|
|
2381
|
-
} else if (arg === "-k" || arg === "--
|
|
2382
|
-
result.options.
|
|
2383
|
-
} else if (arg === "--secret-key") {
|
|
2384
|
-
result.options.secretKey = args[++i];
|
|
1028
|
+
} else if (arg === "-k" || arg === "--api-key") {
|
|
1029
|
+
result.options.apiKey = args[++i];
|
|
2385
1030
|
} else if (arg === "-u" || arg === "--base-url") {
|
|
2386
1031
|
result.options.baseUrl = args[++i];
|
|
2387
1032
|
} else if (arg === "-t" || arg === "--timeout") {
|
|
2388
1033
|
result.options.timeout = args[++i];
|
|
2389
1034
|
} else if (arg === "--max-body") {
|
|
2390
1035
|
result.options.maxBody = args[++i];
|
|
2391
|
-
} else if (arg === "--label") {
|
|
2392
|
-
result.options.label = args[++i];
|
|
2393
|
-
} else if (arg === "--description") {
|
|
2394
|
-
result.options.description = args[++i];
|
|
2395
|
-
} else if (arg === "-a" || arg === "--app") {
|
|
2396
|
-
result.options.appId = args[++i];
|
|
2397
|
-
} else if (arg === "--force") {
|
|
2398
|
-
result.options.force = "true";
|
|
2399
|
-
} else if (arg === "--public") {
|
|
2400
|
-
result.options.public = "true";
|
|
2401
|
-
} else if (arg === "--show-token") {
|
|
2402
|
-
result.options.showToken = "true";
|
|
2403
1036
|
} else if (arg === "-d" || arg === "--dev") {
|
|
2404
1037
|
result.options.dev = "true";
|
|
2405
|
-
} else if (arg === "--
|
|
2406
|
-
result.options.
|
|
2407
|
-
} else if (arg === "--skip-docs") {
|
|
2408
|
-
result.options.skipDocs = "true";
|
|
2409
|
-
} else if (arg === "--skip-mcp") {
|
|
2410
|
-
result.options.skipMcp = "true";
|
|
2411
|
-
} else if (arg === "--setup-root") {
|
|
2412
|
-
result.options.setupRoot = "true";
|
|
1038
|
+
} else if (arg === "--monorepo") {
|
|
1039
|
+
result.options.monorepo = "true";
|
|
2413
1040
|
} else if (arg === "-h" || arg === "--help") {
|
|
2414
1041
|
result.options.help = "true";
|
|
2415
1042
|
} else if (arg === "-v" || arg === "--version") {
|
|
@@ -2428,7 +1055,7 @@ function parseArgs(args) {
|
|
|
2428
1055
|
async function main() {
|
|
2429
1056
|
const parsed = parseArgs(process.argv.slice(2));
|
|
2430
1057
|
if (parsed.options.version) {
|
|
2431
|
-
log(`connectbase v${VERSION}`);
|
|
1058
|
+
log(`connectbase-client v${VERSION}`);
|
|
2432
1059
|
return;
|
|
2433
1060
|
}
|
|
2434
1061
|
if (parsed.options.help || !parsed.command) {
|
|
@@ -2437,40 +1064,44 @@ async function main() {
|
|
|
2437
1064
|
}
|
|
2438
1065
|
const fileConfig = loadConfig();
|
|
2439
1066
|
const config = {
|
|
2440
|
-
|
|
2441
|
-
secretKey: parsed.options.secretKey || fileConfig.secretKey,
|
|
1067
|
+
apiKey: parsed.options.apiKey || fileConfig.apiKey,
|
|
2442
1068
|
storageId: parsed.options.storageId || fileConfig.storageId,
|
|
2443
1069
|
baseUrl: parsed.options.baseUrl || fileConfig.baseUrl || DEFAULT_BASE_URL
|
|
2444
1070
|
};
|
|
2445
1071
|
if (parsed.command === "init") {
|
|
2446
1072
|
await init();
|
|
2447
|
-
} else if (parsed.command === "update") {
|
|
2448
|
-
await update(config, {
|
|
2449
|
-
checkOnly: parsed.options.check === "true",
|
|
2450
|
-
skipDocs: parsed.options.skipDocs === "true",
|
|
2451
|
-
skipMcp: parsed.options.skipMcp === "true",
|
|
2452
|
-
setupRoot: parsed.options.setupRoot === "true"
|
|
2453
|
-
});
|
|
2454
1073
|
} else if (parsed.command === "docs") {
|
|
2455
|
-
|
|
2456
|
-
|
|
2457
|
-
|
|
1074
|
+
let docsApiKey = config.apiKey;
|
|
1075
|
+
if (!docsApiKey) {
|
|
1076
|
+
docsApiKey = await prompt(`${colors.blue}?${colors.reset} API Key: `);
|
|
1077
|
+
if (!docsApiKey) {
|
|
1078
|
+
error("API Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
1079
|
+
process.exit(1);
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
await downloadDocs(docsApiKey, void 0, parsed.options.monorepo === "true");
|
|
2458
1083
|
} else if (parsed.command === "deploy") {
|
|
2459
1084
|
const directory = parsed.args[0] || fileConfig.deployDir || ".";
|
|
2460
|
-
if (!config.
|
|
2461
|
-
error('
|
|
1085
|
+
if (!config.apiKey) {
|
|
1086
|
+
error('API 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');
|
|
2462
1087
|
process.exit(1);
|
|
2463
1088
|
}
|
|
2464
1089
|
if (!config.storageId) {
|
|
2465
|
-
error('\uC2A4\uD1A0\uB9AC\uC9C0 ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. "npx connectbase init"\uC73C\uB85C \uC124\uC815\uD558\uAC70\uB098 -s \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694');
|
|
1090
|
+
error('\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');
|
|
2466
1091
|
process.exit(1);
|
|
2467
1092
|
}
|
|
2468
1093
|
const isDev = parsed.options.dev === "true";
|
|
2469
|
-
|
|
1094
|
+
const deployOpts = {};
|
|
1095
|
+
if (parsed.options.timeout) {
|
|
1096
|
+
const t = parseInt(parsed.options.timeout, 10);
|
|
1097
|
+
if (!isNaN(t) && t > 0) deployOpts.timeoutMs = t * 1e3;
|
|
1098
|
+
else warn(`--timeout \uAC12\uC774 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC544 \uBB34\uC2DC\uB429\uB2C8\uB2E4: ${parsed.options.timeout}`);
|
|
1099
|
+
}
|
|
1100
|
+
await deploy(directory, config, isDev, deployOpts);
|
|
2470
1101
|
} else if (parsed.command === "tunnel") {
|
|
2471
1102
|
const portStr = parsed.args[0];
|
|
2472
1103
|
if (!portStr) {
|
|
2473
|
-
error("\uD3EC\uD2B8 \uBC88\uD638\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: npx connectbase tunnel 8084");
|
|
1104
|
+
error("\uD3EC\uD2B8 \uBC88\uD638\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. \uC608: npx connectbase-client tunnel 8084");
|
|
2474
1105
|
process.exit(1);
|
|
2475
1106
|
}
|
|
2476
1107
|
const port = parseInt(portStr, 10);
|
|
@@ -2487,24 +1118,6 @@ async function main() {
|
|
|
2487
1118
|
const m = parseInt(parsed.options.maxBody, 10);
|
|
2488
1119
|
if (!isNaN(m) && m > 0) tunnelOpts.maxBody = m;
|
|
2489
1120
|
}
|
|
2490
|
-
if (parsed.options.appId) {
|
|
2491
|
-
tunnelOpts.appId = parsed.options.appId;
|
|
2492
|
-
}
|
|
2493
|
-
if (parsed.options.force === "true") {
|
|
2494
|
-
tunnelOpts.force = true;
|
|
2495
|
-
}
|
|
2496
|
-
if (parsed.options.public === "true") {
|
|
2497
|
-
tunnelOpts.public = true;
|
|
2498
|
-
}
|
|
2499
|
-
if (parsed.options.showToken === "true") {
|
|
2500
|
-
tunnelOpts.showToken = true;
|
|
2501
|
-
}
|
|
2502
|
-
if (parsed.options.label) {
|
|
2503
|
-
tunnelOpts.label = parsed.options.label;
|
|
2504
|
-
}
|
|
2505
|
-
if (parsed.options.description) {
|
|
2506
|
-
tunnelOpts.description = parsed.options.description;
|
|
2507
|
-
}
|
|
2508
1121
|
await startTunnel(port, config, tunnelOpts);
|
|
2509
1122
|
} else {
|
|
2510
1123
|
error(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${parsed.command}`);
|
|
@@ -2516,11 +1129,3 @@ main().catch((err) => {
|
|
|
2516
1129
|
error(err.message);
|
|
2517
1130
|
process.exit(1);
|
|
2518
1131
|
});
|
|
2519
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
2520
|
-
0 && (module.exports = {
|
|
2521
|
-
computeDeployDiff,
|
|
2522
|
-
normalizeRelativePath,
|
|
2523
|
-
parseArgs,
|
|
2524
|
-
registerEndpointBinding,
|
|
2525
|
-
sha256Hex
|
|
2526
|
-
});
|