connectbase-client 0.10.3 → 0.10.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +158 -38
- package/dist/connect-base.umd.js +3 -3
- package/dist/index.d.mts +105 -66
- package/dist/index.d.ts +105 -66
- package/dist/index.js +31 -47
- package/dist/index.mjs +30 -47
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -369,7 +369,7 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
369
369
|
return;
|
|
370
370
|
}
|
|
371
371
|
}
|
|
372
|
-
log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 >
|
|
372
|
+
log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
373
373
|
`);
|
|
374
374
|
const secretKey = await promptSecret(`${colors.blue}?${colors.reset} Secret Key: `);
|
|
375
375
|
if (!secretKey) {
|
|
@@ -378,7 +378,7 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
378
378
|
}
|
|
379
379
|
if (secretKey.startsWith("cb_pk_")) {
|
|
380
380
|
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
381
|
-
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 >
|
|
381
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
382
382
|
process.exit(1);
|
|
383
383
|
}
|
|
384
384
|
if (!secretKey.startsWith("cb_sk_")) {
|
|
@@ -712,21 +712,12 @@ function updateRootClaudeMd(claudeMdPath) {
|
|
|
712
712
|
## ConnectBase SDK
|
|
713
713
|
|
|
714
714
|
\uC774 \uD504\uB85C\uC81D\uD2B8\uB294 **ConnectBase**\uB97C \uBC31\uC5D4\uB4DC\uB85C \uC0AC\uC6A9\uD569\uB2C8\uB2E4.
|
|
715
|
-
\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.
|
|
716
715
|
|
|
717
716
|
### AI \uD544\uB3C5 \u2014 \uCF54\uB529 \uC804 \uBC18\uB4DC\uC2DC \uC218\uD589
|
|
718
717
|
|
|
719
|
-
1. \`.claude/docs/project-rules.md
|
|
720
|
-
2. \
|
|
721
|
-
3.
|
|
722
|
-
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)
|
|
723
|
-
|
|
724
|
-
### \uBE44\uAC1C\uBC1C\uC790 \uC751\uB300 \uC6D0\uCE59
|
|
725
|
-
|
|
726
|
-
- \uC804\uBB38 \uC6A9\uC5B4 \uB300\uC2E0 \uC26C\uC6B4 \uB9D0 \uC0AC\uC6A9 ("API" \u2192 "\uAE30\uB2A5", "DB" \u2192 "\uC800\uC7A5\uC18C")
|
|
727
|
-
- \uC5D0\uB7EC \uBC1C\uC0DD \uC2DC \uC6D0\uC778\uACFC \uD574\uACB0\uCC45\uC744 \uD55C \uC904\uB85C \uC124\uBA85
|
|
728
|
-
- \uCF54\uB4DC \uC791\uC131 \uC804 "\uC774\uB807\uAC8C \uB9CC\uB4E4\uAC8C\uC694~" \uD55C \uC904 \uC694\uC57D \uBA3C\uC800
|
|
729
|
-
- \uBAA8\uB974\uB294 \uAC8C \uC788\uC73C\uBA74 \uB9CC\uB4E4\uAE30 \uC804\uC5D0 \uBA3C\uC800 \uC9C8\uBB38
|
|
718
|
+
1. \`.claude/docs/project-rules.md\`\uB97C **Read tool\uB85C \uC77D\uC73C\uC138\uC694** (\uAE30\uB2A5 \uB9E4\uD551\uD45C, \uBCF4\uC548 \uADDC\uCE59, \uC5D0\uB7EC \uAC00\uC774\uB4DC)
|
|
719
|
+
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
720
|
+
3. \uAC80\uC0C9 \uACB0\uACFC\uC758 \uCF54\uB4DC \uD328\uD134\uC744 \uB530\uB77C \uAD6C\uD604\uD558\uC138\uC694
|
|
730
721
|
${endMarker}`;
|
|
731
722
|
if (fs.existsSync(claudeMdPath)) {
|
|
732
723
|
let content = fs.readFileSync(claudeMdPath, "utf-8");
|
|
@@ -735,7 +726,7 @@ ${endMarker}`;
|
|
|
735
726
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
736
727
|
content = content.substring(0, startIdx) + sdkBlock + content.substring(endIdx + endMarker.length);
|
|
737
728
|
} else {
|
|
738
|
-
content =
|
|
729
|
+
content = sdkBlock + "\n\n" + content.trimStart();
|
|
739
730
|
}
|
|
740
731
|
fs.writeFileSync(claudeMdPath, content);
|
|
741
732
|
info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
@@ -949,15 +940,89 @@ function getTunnelServerUrl(baseUrl) {
|
|
|
949
940
|
}
|
|
950
941
|
return baseUrl.replace(/:\d+/, ":8090");
|
|
951
942
|
}
|
|
943
|
+
async function resolveAppForTunnel(apiKey, baseUrl, appIdOption) {
|
|
944
|
+
if (appIdOption) {
|
|
945
|
+
return appIdOption;
|
|
946
|
+
}
|
|
947
|
+
let apps = [];
|
|
948
|
+
try {
|
|
949
|
+
info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
950
|
+
const appsRes = await makeRequest(
|
|
951
|
+
`${baseUrl}/v1/public/cli/apps`,
|
|
952
|
+
"GET",
|
|
953
|
+
{ "X-API-Key": apiKey }
|
|
954
|
+
);
|
|
955
|
+
if (appsRes.status === 401) {
|
|
956
|
+
error("Secret Key\uAC00 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF58\uC194\uC5D0\uC11C \uD0A4\uB97C \uD655\uC778\uD558\uC138\uC694");
|
|
957
|
+
process.exit(1);
|
|
958
|
+
}
|
|
959
|
+
if (appsRes.status !== 200) {
|
|
960
|
+
throw new Error(`HTTP ${appsRes.status}`);
|
|
961
|
+
}
|
|
962
|
+
const appsData = appsRes.data;
|
|
963
|
+
apps = appsData.apps || [];
|
|
964
|
+
} catch (err) {
|
|
965
|
+
error(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
966
|
+
process.exit(1);
|
|
967
|
+
}
|
|
968
|
+
if (apps.length === 1) {
|
|
969
|
+
success(`\uC571 \uC790\uB3D9 \uC120\uD0DD: ${apps[0].name}`);
|
|
970
|
+
return apps[0].id;
|
|
971
|
+
}
|
|
972
|
+
if (apps.length > 0) {
|
|
973
|
+
log(`
|
|
974
|
+
${colors.dim}\uB0B4 \uC571 \uBAA9\uB85D:${colors.reset}`);
|
|
975
|
+
apps.forEach((a, i) => {
|
|
976
|
+
const date = a.created_at ? a.created_at.substring(0, 10) : "";
|
|
977
|
+
log(` ${colors.cyan}${i + 1}${colors.reset}) ${a.name} ${colors.dim}(${date})${colors.reset}`);
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
log(` ${colors.cyan}0${colors.reset}) \uC0C8 \uC571 \uB9CC\uB4E4\uAE30`);
|
|
981
|
+
const choice = await prompt(`
|
|
982
|
+
${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
|
|
983
|
+
const num = parseInt(choice, 10);
|
|
984
|
+
if (num > 0 && num <= apps.length) {
|
|
985
|
+
success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
|
|
986
|
+
return apps[num - 1].id;
|
|
987
|
+
}
|
|
988
|
+
const projectName = path.basename(process.cwd());
|
|
989
|
+
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
990
|
+
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
991
|
+
const createRes = await makeRequest(
|
|
992
|
+
`${baseUrl}/v1/public/cli/apps`,
|
|
993
|
+
"POST",
|
|
994
|
+
{ "X-API-Key": apiKey },
|
|
995
|
+
JSON.stringify({ name: appName })
|
|
996
|
+
);
|
|
997
|
+
if (createRes.status === 402) {
|
|
998
|
+
error("\uC571 \uC0DD\uC131 \uD55C\uB3C4 \uCD08\uACFC. \uD50C\uB79C \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
999
|
+
process.exit(1);
|
|
1000
|
+
}
|
|
1001
|
+
if (createRes.status !== 201) {
|
|
1002
|
+
const data = createRes.data;
|
|
1003
|
+
error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
|
|
1004
|
+
process.exit(1);
|
|
1005
|
+
}
|
|
1006
|
+
const createData = createRes.data;
|
|
1007
|
+
success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
|
|
1008
|
+
return createData.app_id;
|
|
1009
|
+
}
|
|
952
1010
|
async function startTunnel(port, config, tunnelOpts) {
|
|
953
1011
|
if (!config.apiKey) {
|
|
954
|
-
error("
|
|
1012
|
+
error("Secret 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");
|
|
1013
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
955
1014
|
process.exit(1);
|
|
956
1015
|
}
|
|
1016
|
+
if (!config.apiKey.startsWith("cb_sk_")) {
|
|
1017
|
+
error("\uD130\uB110\uC740 \uC720\uC800 Secret Key(cb_sk_)\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. Public Key(cb_pk_)\uB294 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4");
|
|
1018
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1019
|
+
process.exit(1);
|
|
1020
|
+
}
|
|
1021
|
+
const appId = await resolveAppForTunnel(config.apiKey, config.baseUrl, tunnelOpts?.appId);
|
|
957
1022
|
const tunnelServerUrl = getTunnelServerUrl(config.baseUrl);
|
|
958
1023
|
const parsedUrl = new URL(tunnelServerUrl);
|
|
959
1024
|
const isHttps = parsedUrl.protocol === "https:";
|
|
960
|
-
let wsPath = `/v1/tunnel/connect?
|
|
1025
|
+
let wsPath = `/v1/tunnel/connect?app_id=${encodeURIComponent(appId)}`;
|
|
961
1026
|
if (tunnelOpts?.timeout) {
|
|
962
1027
|
wsPath += `&timeout=${tunnelOpts.timeout}`;
|
|
963
1028
|
}
|
|
@@ -1003,7 +1068,8 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1003
1068
|
"Upgrade": "websocket",
|
|
1004
1069
|
"Connection": "Upgrade",
|
|
1005
1070
|
"Sec-WebSocket-Key": wsKey,
|
|
1006
|
-
"Sec-WebSocket-Version": "13"
|
|
1071
|
+
"Sec-WebSocket-Version": "13",
|
|
1072
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
1007
1073
|
}
|
|
1008
1074
|
};
|
|
1009
1075
|
const req = lib.request(reqOptions);
|
|
@@ -1129,29 +1195,78 @@ ${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
|
1129
1195
|
headers: localHeaders
|
|
1130
1196
|
};
|
|
1131
1197
|
const localReq = http.request(reqOptions, (res) => {
|
|
1132
|
-
const
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
const response = {
|
|
1141
|
-
type: "http_response",
|
|
1142
|
-
request_id: requestId,
|
|
1143
|
-
status: res.statusCode || 200,
|
|
1144
|
-
headers: responseHeaders,
|
|
1145
|
-
body: body.length > 0 ? body.toString("base64") : ""
|
|
1146
|
-
};
|
|
1198
|
+
const responseHeaders = {};
|
|
1199
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
1200
|
+
if (value) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
|
|
1201
|
+
}
|
|
1202
|
+
const contentType = (responseHeaders["content-type"] || "").toLowerCase();
|
|
1203
|
+
const transferEncoding = (responseHeaders["transfer-encoding"] || "").toLowerCase();
|
|
1204
|
+
const isStreaming = contentType.includes("text/event-stream") || transferEncoding.includes("chunked");
|
|
1205
|
+
if (isStreaming) {
|
|
1147
1206
|
try {
|
|
1148
|
-
sock.write(createWsTextFrame(JSON.stringify(
|
|
1149
|
-
|
|
1150
|
-
|
|
1207
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1208
|
+
type: "http_response_start",
|
|
1209
|
+
request_id: requestId,
|
|
1210
|
+
status: res.statusCode || 200,
|
|
1211
|
+
headers: responseHeaders
|
|
1212
|
+
})));
|
|
1151
1213
|
} catch {
|
|
1152
|
-
warn(`\
|
|
1214
|
+
warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uC2DC\uC791 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1215
|
+
return;
|
|
1153
1216
|
}
|
|
1154
|
-
|
|
1217
|
+
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
1218
|
+
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode} ${colors.cyan}[stream]${colors.reset}`);
|
|
1219
|
+
res.on("data", (chunk) => {
|
|
1220
|
+
try {
|
|
1221
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1222
|
+
type: "http_response_chunk",
|
|
1223
|
+
request_id: requestId,
|
|
1224
|
+
data: chunk.toString("base64")
|
|
1225
|
+
})));
|
|
1226
|
+
} catch {
|
|
1227
|
+
warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uCCAD\uD06C \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1228
|
+
}
|
|
1229
|
+
});
|
|
1230
|
+
res.on("end", () => {
|
|
1231
|
+
try {
|
|
1232
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1233
|
+
type: "http_response_end",
|
|
1234
|
+
request_id: requestId
|
|
1235
|
+
})));
|
|
1236
|
+
} catch {
|
|
1237
|
+
}
|
|
1238
|
+
});
|
|
1239
|
+
res.on("error", (err) => {
|
|
1240
|
+
try {
|
|
1241
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1242
|
+
type: "http_response_error",
|
|
1243
|
+
request_id: requestId,
|
|
1244
|
+
error: err.message
|
|
1245
|
+
})));
|
|
1246
|
+
} catch {
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
} else {
|
|
1250
|
+
const chunks = [];
|
|
1251
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
1252
|
+
res.on("end", () => {
|
|
1253
|
+
const body = Buffer.concat(chunks);
|
|
1254
|
+
const response = {
|
|
1255
|
+
type: "http_response",
|
|
1256
|
+
request_id: requestId,
|
|
1257
|
+
status: res.statusCode || 200,
|
|
1258
|
+
headers: responseHeaders,
|
|
1259
|
+
body: body.length > 0 ? body.toString("base64") : ""
|
|
1260
|
+
};
|
|
1261
|
+
try {
|
|
1262
|
+
sock.write(createWsTextFrame(JSON.stringify(response)));
|
|
1263
|
+
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
1264
|
+
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
|
|
1265
|
+
} catch {
|
|
1266
|
+
warn(`\uC751\uB2F5 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1267
|
+
}
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1155
1270
|
});
|
|
1156
1271
|
localReq.on("error", (err) => {
|
|
1157
1272
|
const response = {
|
|
@@ -1261,6 +1376,8 @@ function parseArgs(args) {
|
|
|
1261
1376
|
result.options.timeout = args[++i];
|
|
1262
1377
|
} else if (arg === "--max-body") {
|
|
1263
1378
|
result.options.maxBody = args[++i];
|
|
1379
|
+
} else if (arg === "-a" || arg === "--app") {
|
|
1380
|
+
result.options.appId = args[++i];
|
|
1264
1381
|
} else if (arg === "-d" || arg === "--dev") {
|
|
1265
1382
|
result.options.dev = "true";
|
|
1266
1383
|
} else if (arg === "-h" || arg === "--help") {
|
|
@@ -1340,6 +1457,9 @@ async function main() {
|
|
|
1340
1457
|
const m = parseInt(parsed.options.maxBody, 10);
|
|
1341
1458
|
if (!isNaN(m) && m > 0) tunnelOpts.maxBody = m;
|
|
1342
1459
|
}
|
|
1460
|
+
if (parsed.options.appId) {
|
|
1461
|
+
tunnelOpts.appId = parsed.options.appId;
|
|
1462
|
+
}
|
|
1343
1463
|
await startTunnel(port, config, tunnelOpts);
|
|
1344
1464
|
} else {
|
|
1345
1465
|
error(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${parsed.command}`);
|