connectbase-client 0.10.3 → 0.10.5
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 +259 -50
- 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
|
@@ -350,6 +350,84 @@ function getProjectRoot() {
|
|
|
350
350
|
const gitRoot = getGitRoot();
|
|
351
351
|
return gitRoot || process.cwd();
|
|
352
352
|
}
|
|
353
|
+
var CONSOLE_URL = DEFAULT_BASE_URL.replace("api.", "");
|
|
354
|
+
function openBrowser(url) {
|
|
355
|
+
const { exec } = require("child_process");
|
|
356
|
+
const platform = process.platform;
|
|
357
|
+
let command;
|
|
358
|
+
if (platform === "darwin") {
|
|
359
|
+
command = `open "${url}"`;
|
|
360
|
+
} else if (platform === "win32") {
|
|
361
|
+
command = `start "" "${url}"`;
|
|
362
|
+
} else {
|
|
363
|
+
command = `xdg-open "${url}"`;
|
|
364
|
+
}
|
|
365
|
+
exec(command, () => {
|
|
366
|
+
});
|
|
367
|
+
}
|
|
368
|
+
async function browserAuthFlow() {
|
|
369
|
+
info("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D \uC138\uC158\uC744 \uC2DC\uC791\uD569\uB2C8\uB2E4...");
|
|
370
|
+
const startRes = await makeRequest(
|
|
371
|
+
`${DEFAULT_BASE_URL}/v1/public/cli-auth/start`,
|
|
372
|
+
"POST",
|
|
373
|
+
{}
|
|
374
|
+
);
|
|
375
|
+
if (startRes.status !== 200) {
|
|
376
|
+
error("\uC778\uC99D \uC138\uC158 \uC2DC\uC791\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4");
|
|
377
|
+
process.exit(1);
|
|
378
|
+
}
|
|
379
|
+
const { session_id } = startRes.data;
|
|
380
|
+
const authUrl = `${CONSOLE_URL}/auth/cli-auth?session=${session_id}`;
|
|
381
|
+
log(`
|
|
382
|
+
${colors.cyan}\uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uB85C\uADF8\uC778\uD558\uC138\uC694:${colors.reset}`);
|
|
383
|
+
log(`${colors.dim}${authUrl}${colors.reset}
|
|
384
|
+
`);
|
|
385
|
+
openBrowser(authUrl);
|
|
386
|
+
const pollInterval = 2e3;
|
|
387
|
+
const maxAttempts = 150;
|
|
388
|
+
let attempts = 0;
|
|
389
|
+
const spinnerFrames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
390
|
+
while (attempts < maxAttempts) {
|
|
391
|
+
const frame = spinnerFrames[attempts % spinnerFrames.length];
|
|
392
|
+
process.stdout.write(`\r${colors.blue}${frame}${colors.reset} \uBE0C\uB77C\uC6B0\uC800\uC5D0\uC11C \uC2B9\uC778 \uB300\uAE30 \uC911...`);
|
|
393
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
394
|
+
try {
|
|
395
|
+
const pollRes = await makeRequest(
|
|
396
|
+
`${DEFAULT_BASE_URL}/v1/public/cli-auth/poll/${session_id}`,
|
|
397
|
+
"GET",
|
|
398
|
+
{}
|
|
399
|
+
);
|
|
400
|
+
if (pollRes.status === 200) {
|
|
401
|
+
const data = pollRes.data;
|
|
402
|
+
if (data.status === "approved" && data.secret_key) {
|
|
403
|
+
process.stdout.write("\r \r");
|
|
404
|
+
success("\uBE0C\uB77C\uC6B0\uC800 \uC778\uC99D\uC774 \uC644\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4!");
|
|
405
|
+
return data.secret_key;
|
|
406
|
+
}
|
|
407
|
+
if (data.status === "expired") {
|
|
408
|
+
process.stdout.write("\r \r");
|
|
409
|
+
error("\uC778\uC99D \uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
410
|
+
process.exit(1);
|
|
411
|
+
}
|
|
412
|
+
if (data.status === "denied") {
|
|
413
|
+
process.stdout.write("\r \r");
|
|
414
|
+
error("\uC778\uC99D\uC774 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4.");
|
|
415
|
+
process.exit(1);
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
if (pollRes.status === 404) {
|
|
419
|
+
process.stdout.write("\r \r");
|
|
420
|
+
error("\uC778\uC99D \uC138\uC158\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
} catch {
|
|
424
|
+
}
|
|
425
|
+
attempts++;
|
|
426
|
+
}
|
|
427
|
+
process.stdout.write("\r \r");
|
|
428
|
+
error("\uC778\uC99D \uC2DC\uAC04\uC774 \uCD08\uACFC\uB418\uC5C8\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD574\uC8FC\uC138\uC694.");
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
353
431
|
async function init() {
|
|
354
432
|
log(`
|
|
355
433
|
${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.reset}
|
|
@@ -369,21 +447,32 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
369
447
|
return;
|
|
370
448
|
}
|
|
371
449
|
}
|
|
372
|
-
log(
|
|
450
|
+
log(`
|
|
451
|
+
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
452
|
+
log(` ${colors.cyan}1${colors.reset}) Secret Key \uC9C1\uC811 \uC785\uB825 (cb_sk_...)`);
|
|
453
|
+
log(` ${colors.cyan}2${colors.reset}) \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uC790\uB3D9 \uBC1C\uAE09
|
|
373
454
|
`);
|
|
374
|
-
const
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
455
|
+
const authChoice = await prompt(`${colors.blue}?${colors.reset} \uC120\uD0DD (1/2): `);
|
|
456
|
+
let secretKey = "";
|
|
457
|
+
if (authChoice === "2") {
|
|
458
|
+
secretKey = await browserAuthFlow();
|
|
459
|
+
} else {
|
|
460
|
+
log(`${colors.dim}Secret Key (cb_sk_): \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uBC1C\uAE09${colors.reset}
|
|
461
|
+
`);
|
|
462
|
+
secretKey = await promptSecret(`${colors.blue}?${colors.reset} Secret Key: `);
|
|
463
|
+
if (!secretKey) {
|
|
464
|
+
error("Secret Key\uB294 \uD544\uC218\uC785\uB2C8\uB2E4");
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
if (secretKey.startsWith("cb_pk_")) {
|
|
468
|
+
error("Public Key\uAC00 \uC544\uB2CC Secret Key(cb_sk_)\uB97C \uC785\uB825\uD558\uC138\uC694");
|
|
469
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
470
|
+
process.exit(1);
|
|
471
|
+
}
|
|
472
|
+
if (!secretKey.startsWith("cb_sk_")) {
|
|
473
|
+
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");
|
|
474
|
+
process.exit(1);
|
|
475
|
+
}
|
|
387
476
|
}
|
|
388
477
|
let appId = "";
|
|
389
478
|
let publicKey = "";
|
|
@@ -712,21 +801,12 @@ function updateRootClaudeMd(claudeMdPath) {
|
|
|
712
801
|
## ConnectBase SDK
|
|
713
802
|
|
|
714
803
|
\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
804
|
|
|
717
805
|
### AI \uD544\uB3C5 \u2014 \uCF54\uB529 \uC804 \uBC18\uB4DC\uC2DC \uC218\uD589
|
|
718
806
|
|
|
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
|
|
807
|
+
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)
|
|
808
|
+
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
809
|
+
3. \uAC80\uC0C9 \uACB0\uACFC\uC758 \uCF54\uB4DC \uD328\uD134\uC744 \uB530\uB77C \uAD6C\uD604\uD558\uC138\uC694
|
|
730
810
|
${endMarker}`;
|
|
731
811
|
if (fs.existsSync(claudeMdPath)) {
|
|
732
812
|
let content = fs.readFileSync(claudeMdPath, "utf-8");
|
|
@@ -735,7 +815,7 @@ ${endMarker}`;
|
|
|
735
815
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
736
816
|
content = content.substring(0, startIdx) + sdkBlock + content.substring(endIdx + endMarker.length);
|
|
737
817
|
} else {
|
|
738
|
-
content =
|
|
818
|
+
content = sdkBlock + "\n\n" + content.trimStart();
|
|
739
819
|
}
|
|
740
820
|
fs.writeFileSync(claudeMdPath, content);
|
|
741
821
|
info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
@@ -949,15 +1029,89 @@ function getTunnelServerUrl(baseUrl) {
|
|
|
949
1029
|
}
|
|
950
1030
|
return baseUrl.replace(/:\d+/, ":8090");
|
|
951
1031
|
}
|
|
1032
|
+
async function resolveAppForTunnel(apiKey, baseUrl, appIdOption) {
|
|
1033
|
+
if (appIdOption) {
|
|
1034
|
+
return appIdOption;
|
|
1035
|
+
}
|
|
1036
|
+
let apps = [];
|
|
1037
|
+
try {
|
|
1038
|
+
info("\uC571 \uBAA9\uB85D \uC870\uD68C \uC911...");
|
|
1039
|
+
const appsRes = await makeRequest(
|
|
1040
|
+
`${baseUrl}/v1/public/cli/apps`,
|
|
1041
|
+
"GET",
|
|
1042
|
+
{ "X-API-Key": apiKey }
|
|
1043
|
+
);
|
|
1044
|
+
if (appsRes.status === 401) {
|
|
1045
|
+
error("Secret Key\uAC00 \uC720\uD6A8\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4. \uCF58\uC194\uC5D0\uC11C \uD0A4\uB97C \uD655\uC778\uD558\uC138\uC694");
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
if (appsRes.status !== 200) {
|
|
1049
|
+
throw new Error(`HTTP ${appsRes.status}`);
|
|
1050
|
+
}
|
|
1051
|
+
const appsData = appsRes.data;
|
|
1052
|
+
apps = appsData.apps || [];
|
|
1053
|
+
} catch (err) {
|
|
1054
|
+
error(`\uC571 \uBAA9\uB85D \uC870\uD68C \uC2E4\uD328: ${err instanceof Error ? err.message : err}`);
|
|
1055
|
+
process.exit(1);
|
|
1056
|
+
}
|
|
1057
|
+
if (apps.length === 1) {
|
|
1058
|
+
success(`\uC571 \uC790\uB3D9 \uC120\uD0DD: ${apps[0].name}`);
|
|
1059
|
+
return apps[0].id;
|
|
1060
|
+
}
|
|
1061
|
+
if (apps.length > 0) {
|
|
1062
|
+
log(`
|
|
1063
|
+
${colors.dim}\uB0B4 \uC571 \uBAA9\uB85D:${colors.reset}`);
|
|
1064
|
+
apps.forEach((a, i) => {
|
|
1065
|
+
const date = a.created_at ? a.created_at.substring(0, 10) : "";
|
|
1066
|
+
log(` ${colors.cyan}${i + 1}${colors.reset}) ${a.name} ${colors.dim}(${date})${colors.reset}`);
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
log(` ${colors.cyan}0${colors.reset}) \uC0C8 \uC571 \uB9CC\uB4E4\uAE30`);
|
|
1070
|
+
const choice = await prompt(`
|
|
1071
|
+
${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
|
|
1072
|
+
const num = parseInt(choice, 10);
|
|
1073
|
+
if (num > 0 && num <= apps.length) {
|
|
1074
|
+
success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
|
|
1075
|
+
return apps[num - 1].id;
|
|
1076
|
+
}
|
|
1077
|
+
const projectName = path.basename(process.cwd());
|
|
1078
|
+
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1079
|
+
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
1080
|
+
const createRes = await makeRequest(
|
|
1081
|
+
`${baseUrl}/v1/public/cli/apps`,
|
|
1082
|
+
"POST",
|
|
1083
|
+
{ "X-API-Key": apiKey },
|
|
1084
|
+
JSON.stringify({ name: appName })
|
|
1085
|
+
);
|
|
1086
|
+
if (createRes.status === 402) {
|
|
1087
|
+
error("\uC571 \uC0DD\uC131 \uD55C\uB3C4 \uCD08\uACFC. \uD50C\uB79C \uC5C5\uADF8\uB808\uC774\uB4DC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
1088
|
+
process.exit(1);
|
|
1089
|
+
}
|
|
1090
|
+
if (createRes.status !== 201) {
|
|
1091
|
+
const data = createRes.data;
|
|
1092
|
+
error(`\uC571 \uC0DD\uC131 \uC2E4\uD328: ${data?.error || `HTTP ${createRes.status}`}`);
|
|
1093
|
+
process.exit(1);
|
|
1094
|
+
}
|
|
1095
|
+
const createData = createRes.data;
|
|
1096
|
+
success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
|
|
1097
|
+
return createData.app_id;
|
|
1098
|
+
}
|
|
952
1099
|
async function startTunnel(port, config, tunnelOpts) {
|
|
953
1100
|
if (!config.apiKey) {
|
|
954
|
-
error("
|
|
1101
|
+
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");
|
|
1102
|
+
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
1103
|
process.exit(1);
|
|
956
1104
|
}
|
|
1105
|
+
if (!config.apiKey.startsWith("cb_sk_")) {
|
|
1106
|
+
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");
|
|
1107
|
+
info("Secret Key\uB294 \uCF58\uC194 > \uD504\uB85C\uD544 > \uC2DC\uD06C\uB9BF \uD0A4 \uD0ED\uC5D0\uC11C \uC0DD\uC131\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4");
|
|
1108
|
+
process.exit(1);
|
|
1109
|
+
}
|
|
1110
|
+
const appId = await resolveAppForTunnel(config.apiKey, config.baseUrl, tunnelOpts?.appId);
|
|
957
1111
|
const tunnelServerUrl = getTunnelServerUrl(config.baseUrl);
|
|
958
1112
|
const parsedUrl = new URL(tunnelServerUrl);
|
|
959
1113
|
const isHttps = parsedUrl.protocol === "https:";
|
|
960
|
-
let wsPath = `/v1/tunnel/connect?
|
|
1114
|
+
let wsPath = `/v1/tunnel/connect?app_id=${encodeURIComponent(appId)}`;
|
|
961
1115
|
if (tunnelOpts?.timeout) {
|
|
962
1116
|
wsPath += `&timeout=${tunnelOpts.timeout}`;
|
|
963
1117
|
}
|
|
@@ -1003,7 +1157,8 @@ ${colors.cyan}ConnectBase Tunnel${colors.reset}`);
|
|
|
1003
1157
|
"Upgrade": "websocket",
|
|
1004
1158
|
"Connection": "Upgrade",
|
|
1005
1159
|
"Sec-WebSocket-Key": wsKey,
|
|
1006
|
-
"Sec-WebSocket-Version": "13"
|
|
1160
|
+
"Sec-WebSocket-Version": "13",
|
|
1161
|
+
"Authorization": `Bearer ${config.apiKey}`
|
|
1007
1162
|
}
|
|
1008
1163
|
};
|
|
1009
1164
|
const req = lib.request(reqOptions);
|
|
@@ -1129,29 +1284,78 @@ ${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
|
1129
1284
|
headers: localHeaders
|
|
1130
1285
|
};
|
|
1131
1286
|
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
|
-
};
|
|
1287
|
+
const responseHeaders = {};
|
|
1288
|
+
for (const [key, value] of Object.entries(res.headers)) {
|
|
1289
|
+
if (value) responseHeaders[key] = Array.isArray(value) ? value.join(", ") : value;
|
|
1290
|
+
}
|
|
1291
|
+
const contentType = (responseHeaders["content-type"] || "").toLowerCase();
|
|
1292
|
+
const transferEncoding = (responseHeaders["transfer-encoding"] || "").toLowerCase();
|
|
1293
|
+
const isStreaming = contentType.includes("text/event-stream") || transferEncoding.includes("chunked");
|
|
1294
|
+
if (isStreaming) {
|
|
1147
1295
|
try {
|
|
1148
|
-
sock.write(createWsTextFrame(JSON.stringify(
|
|
1149
|
-
|
|
1150
|
-
|
|
1296
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1297
|
+
type: "http_response_start",
|
|
1298
|
+
request_id: requestId,
|
|
1299
|
+
status: res.statusCode || 200,
|
|
1300
|
+
headers: responseHeaders
|
|
1301
|
+
})));
|
|
1151
1302
|
} catch {
|
|
1152
|
-
warn(`\
|
|
1303
|
+
warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uC2DC\uC791 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1304
|
+
return;
|
|
1153
1305
|
}
|
|
1154
|
-
|
|
1306
|
+
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
1307
|
+
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode} ${colors.cyan}[stream]${colors.reset}`);
|
|
1308
|
+
res.on("data", (chunk) => {
|
|
1309
|
+
try {
|
|
1310
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1311
|
+
type: "http_response_chunk",
|
|
1312
|
+
request_id: requestId,
|
|
1313
|
+
data: chunk.toString("base64")
|
|
1314
|
+
})));
|
|
1315
|
+
} catch {
|
|
1316
|
+
warn(`\uC2A4\uD2B8\uB9AC\uBC0D \uCCAD\uD06C \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1317
|
+
}
|
|
1318
|
+
});
|
|
1319
|
+
res.on("end", () => {
|
|
1320
|
+
try {
|
|
1321
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1322
|
+
type: "http_response_end",
|
|
1323
|
+
request_id: requestId
|
|
1324
|
+
})));
|
|
1325
|
+
} catch {
|
|
1326
|
+
}
|
|
1327
|
+
});
|
|
1328
|
+
res.on("error", (err) => {
|
|
1329
|
+
try {
|
|
1330
|
+
sock.write(createWsTextFrame(JSON.stringify({
|
|
1331
|
+
type: "http_response_error",
|
|
1332
|
+
request_id: requestId,
|
|
1333
|
+
error: err.message
|
|
1334
|
+
})));
|
|
1335
|
+
} catch {
|
|
1336
|
+
}
|
|
1337
|
+
});
|
|
1338
|
+
} else {
|
|
1339
|
+
const chunks = [];
|
|
1340
|
+
res.on("data", (chunk) => chunks.push(chunk));
|
|
1341
|
+
res.on("end", () => {
|
|
1342
|
+
const body = Buffer.concat(chunks);
|
|
1343
|
+
const response = {
|
|
1344
|
+
type: "http_response",
|
|
1345
|
+
request_id: requestId,
|
|
1346
|
+
status: res.statusCode || 200,
|
|
1347
|
+
headers: responseHeaders,
|
|
1348
|
+
body: body.length > 0 ? body.toString("base64") : ""
|
|
1349
|
+
};
|
|
1350
|
+
try {
|
|
1351
|
+
sock.write(createWsTextFrame(JSON.stringify(response)));
|
|
1352
|
+
const methodColor = method === "GET" ? colors.green : method === "POST" ? colors.blue : colors.yellow;
|
|
1353
|
+
log(`${colors.dim}${(/* @__PURE__ */ new Date()).toLocaleTimeString()}${colors.reset} ${methodColor}${method}${colors.reset} ${reqPath} \u2192 ${res.statusCode}`);
|
|
1354
|
+
} catch {
|
|
1355
|
+
warn(`\uC751\uB2F5 \uC804\uC1A1 \uC2E4\uD328: ${requestId}`);
|
|
1356
|
+
}
|
|
1357
|
+
});
|
|
1358
|
+
}
|
|
1155
1359
|
});
|
|
1156
1360
|
localReq.on("error", (err) => {
|
|
1157
1361
|
const response = {
|
|
@@ -1261,6 +1465,8 @@ function parseArgs(args) {
|
|
|
1261
1465
|
result.options.timeout = args[++i];
|
|
1262
1466
|
} else if (arg === "--max-body") {
|
|
1263
1467
|
result.options.maxBody = args[++i];
|
|
1468
|
+
} else if (arg === "-a" || arg === "--app") {
|
|
1469
|
+
result.options.appId = args[++i];
|
|
1264
1470
|
} else if (arg === "-d" || arg === "--dev") {
|
|
1265
1471
|
result.options.dev = "true";
|
|
1266
1472
|
} else if (arg === "-h" || arg === "--help") {
|
|
@@ -1340,6 +1546,9 @@ async function main() {
|
|
|
1340
1546
|
const m = parseInt(parsed.options.maxBody, 10);
|
|
1341
1547
|
if (!isNaN(m) && m > 0) tunnelOpts.maxBody = m;
|
|
1342
1548
|
}
|
|
1549
|
+
if (parsed.options.appId) {
|
|
1550
|
+
tunnelOpts.appId = parsed.options.appId;
|
|
1551
|
+
}
|
|
1343
1552
|
await startTunnel(port, config, tunnelOpts);
|
|
1344
1553
|
} else {
|
|
1345
1554
|
error(`\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC5B4: ${parsed.command}`);
|