connectbase-client 0.14.0 → 0.15.1
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 +181 -156
- package/dist/connect-base.umd.js +3 -3
- package/dist/index.d.mts +163 -4
- package/dist/index.d.ts +163 -4
- package/dist/index.js +650 -2
- package/dist/index.mjs +649 -2
- package/package.json +4 -2
package/dist/cli.js
CHANGED
|
@@ -24,13 +24,90 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
-
var
|
|
28
|
-
var
|
|
29
|
-
var os = __toESM(require("os"));
|
|
27
|
+
var fs2 = __toESM(require("fs"));
|
|
28
|
+
var path2 = __toESM(require("path"));
|
|
30
29
|
var crypto = __toESM(require("crypto"));
|
|
31
30
|
var https = __toESM(require("https"));
|
|
32
31
|
var http = __toESM(require("http"));
|
|
33
32
|
var readline = __toESM(require("readline"));
|
|
33
|
+
|
|
34
|
+
// src/tunnel-utils.ts
|
|
35
|
+
var fs = __toESM(require("fs"));
|
|
36
|
+
var path = __toESM(require("path"));
|
|
37
|
+
var os = __toESM(require("os"));
|
|
38
|
+
function getTunnelLockDir() {
|
|
39
|
+
const platform2 = os.platform();
|
|
40
|
+
if (platform2 === "darwin") {
|
|
41
|
+
return path.join(os.homedir(), "Library", "Application Support", "connectbase", "tunnel-locks");
|
|
42
|
+
} else if (platform2 === "win32") {
|
|
43
|
+
return path.join(process.env.LOCALAPPDATA || path.join(os.homedir(), "AppData", "Local"), "connectbase", "tunnel-locks");
|
|
44
|
+
}
|
|
45
|
+
const stateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state");
|
|
46
|
+
return path.join(stateHome, "connectbase", "tunnel-locks");
|
|
47
|
+
}
|
|
48
|
+
function isProcessAlive(pid) {
|
|
49
|
+
try {
|
|
50
|
+
process.kill(pid, 0);
|
|
51
|
+
return true;
|
|
52
|
+
} catch (e) {
|
|
53
|
+
return e.code === "EPERM";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function acquireTunnelLock(appID, port, force, version) {
|
|
57
|
+
const lockDir = getTunnelLockDir();
|
|
58
|
+
fs.mkdirSync(lockDir, { recursive: true });
|
|
59
|
+
const lockPath = path.join(lockDir, `${appID}-${port}.lock`);
|
|
60
|
+
const lockData = {
|
|
61
|
+
pid: process.pid,
|
|
62
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
63
|
+
appID,
|
|
64
|
+
port,
|
|
65
|
+
host: os.hostname(),
|
|
66
|
+
version
|
|
67
|
+
};
|
|
68
|
+
if (!force) {
|
|
69
|
+
try {
|
|
70
|
+
const fd = fs.openSync(lockPath, "wx");
|
|
71
|
+
fs.writeSync(fd, JSON.stringify(lockData, null, 2));
|
|
72
|
+
fs.closeSync(fd);
|
|
73
|
+
return lockPath;
|
|
74
|
+
} catch (e) {
|
|
75
|
+
if (e.code !== "EEXIST") {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
const existing = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
|
|
81
|
+
if (isProcessAlive(existing.pid)) {
|
|
82
|
+
return `LOCKED:${existing.pid}:${existing.startedAt}:${existing.host}`;
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2));
|
|
88
|
+
return lockPath;
|
|
89
|
+
}
|
|
90
|
+
function releaseTunnelLock(lockPath) {
|
|
91
|
+
try {
|
|
92
|
+
fs.unlinkSync(lockPath);
|
|
93
|
+
} catch {
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function handleTunnelError(msg, appId, localPort) {
|
|
97
|
+
if (msg.code === "replaced") {
|
|
98
|
+
return {
|
|
99
|
+
action: "exit",
|
|
100
|
+
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.`,
|
|
101
|
+
exitCode: 1
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
action: "warn",
|
|
106
|
+
message: `\uD130\uB110 \uC5D0\uB7EC: ${msg.error || msg.message || "\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC"}`
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/cli.ts
|
|
34
111
|
var VERSION = "0.10.6";
|
|
35
112
|
var DEFAULT_BASE_URL = "https://api.connectbase.world";
|
|
36
113
|
var colors = {
|
|
@@ -119,10 +196,10 @@ function loadConfig() {
|
|
|
119
196
|
storageId: process.env.CONNECTBASE_STORAGE_ID,
|
|
120
197
|
baseUrl: process.env.CONNECTBASE_BASE_URL || DEFAULT_BASE_URL
|
|
121
198
|
};
|
|
122
|
-
const rcPath =
|
|
123
|
-
if (
|
|
199
|
+
const rcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
200
|
+
if (fs2.existsSync(rcPath)) {
|
|
124
201
|
try {
|
|
125
|
-
const rcContent = JSON.parse(
|
|
202
|
+
const rcContent = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
126
203
|
if (rcContent.publicKey) config.publicKey = rcContent.publicKey;
|
|
127
204
|
if (rcContent.secretKey) config.secretKey = rcContent.secretKey;
|
|
128
205
|
if (rcContent.storageId) config.storageId = rcContent.storageId;
|
|
@@ -136,16 +213,16 @@ function loadConfig() {
|
|
|
136
213
|
}
|
|
137
214
|
function collectFiles(dir, baseDir = dir) {
|
|
138
215
|
const files = [];
|
|
139
|
-
const entries =
|
|
216
|
+
const entries = fs2.readdirSync(dir, { withFileTypes: true });
|
|
140
217
|
for (const entry of entries) {
|
|
141
|
-
const fullPath =
|
|
142
|
-
const relativePath =
|
|
218
|
+
const fullPath = path2.join(dir, entry.name);
|
|
219
|
+
const relativePath = path2.relative(baseDir, fullPath);
|
|
143
220
|
if (entry.isDirectory()) {
|
|
144
221
|
if (!entry.name.startsWith(".")) {
|
|
145
222
|
files.push(...collectFiles(fullPath, baseDir));
|
|
146
223
|
}
|
|
147
224
|
} else if (entry.isFile()) {
|
|
148
|
-
const ext =
|
|
225
|
+
const ext = path2.extname(entry.name).toLowerCase();
|
|
149
226
|
if (!ALLOWED_EXTENSIONS.has(ext)) {
|
|
150
227
|
continue;
|
|
151
228
|
}
|
|
@@ -155,9 +232,9 @@ function collectFiles(dir, baseDir = dir) {
|
|
|
155
232
|
const isBinary = BINARY_EXTENSIONS.has(ext);
|
|
156
233
|
let content;
|
|
157
234
|
if (isBinary) {
|
|
158
|
-
content =
|
|
235
|
+
content = fs2.readFileSync(fullPath).toString("base64");
|
|
159
236
|
} else {
|
|
160
|
-
content =
|
|
237
|
+
content = fs2.readFileSync(fullPath, "utf-8");
|
|
161
238
|
}
|
|
162
239
|
files.push({
|
|
163
240
|
path: relativePath.replace(/\\/g, "/"),
|
|
@@ -213,15 +290,15 @@ async function makeRequest(url, method, headers, body) {
|
|
|
213
290
|
});
|
|
214
291
|
}
|
|
215
292
|
async function deploy(directory, config, isDev = false) {
|
|
216
|
-
const dir =
|
|
217
|
-
if (!
|
|
293
|
+
const dir = path2.resolve(directory);
|
|
294
|
+
if (!fs2.existsSync(dir)) {
|
|
218
295
|
error(`\uB514\uB809\uD1A0\uB9AC\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${dir}`);
|
|
219
296
|
process.exit(1);
|
|
220
297
|
}
|
|
221
|
-
const pkgPath =
|
|
222
|
-
if (
|
|
298
|
+
const pkgPath = path2.join(process.cwd(), "package.json");
|
|
299
|
+
if (fs2.existsSync(pkgPath)) {
|
|
223
300
|
try {
|
|
224
|
-
const pkg = JSON.parse(
|
|
301
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
225
302
|
if (pkg.scripts?.build) {
|
|
226
303
|
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}`);
|
|
227
304
|
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}`);
|
|
@@ -229,12 +306,12 @@ async function deploy(directory, config, isDev = false) {
|
|
|
229
306
|
} catch {
|
|
230
307
|
}
|
|
231
308
|
}
|
|
232
|
-
if (!
|
|
309
|
+
if (!fs2.statSync(dir).isDirectory()) {
|
|
233
310
|
error(`${dir}\uB294 \uB514\uB809\uD1A0\uB9AC\uAC00 \uC544\uB2D9\uB2C8\uB2E4`);
|
|
234
311
|
process.exit(1);
|
|
235
312
|
}
|
|
236
|
-
const indexPath =
|
|
237
|
-
if (!
|
|
313
|
+
const indexPath = path2.join(dir, "index.html");
|
|
314
|
+
if (!fs2.existsSync(indexPath)) {
|
|
238
315
|
error("index.html \uD30C\uC77C\uC774 \uD544\uC694\uD569\uB2C8\uB2E4");
|
|
239
316
|
process.exit(1);
|
|
240
317
|
}
|
|
@@ -450,8 +527,8 @@ ${colors.cyan}Connect Base \uD504\uB85C\uC81D\uD2B8 \uCD08\uAE30\uD654${colors.r
|
|
|
450
527
|
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
|
|
451
528
|
`);
|
|
452
529
|
}
|
|
453
|
-
const rcPath =
|
|
454
|
-
if (
|
|
530
|
+
const rcPath = path2.join(cwd, ".connectbaserc");
|
|
531
|
+
if (fs2.existsSync(rcPath)) {
|
|
455
532
|
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): `);
|
|
456
533
|
if (overwrite.toLowerCase() !== "y") {
|
|
457
534
|
info("\uCD08\uAE30\uD654\uAC00 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4");
|
|
@@ -540,7 +617,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
540
617
|
}
|
|
541
618
|
}
|
|
542
619
|
if (!appId) {
|
|
543
|
-
const projectName =
|
|
620
|
+
const projectName = path2.basename(cwd);
|
|
544
621
|
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
545
622
|
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
546
623
|
const createRes = await makeRequest(
|
|
@@ -608,7 +685,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
608
685
|
}
|
|
609
686
|
}
|
|
610
687
|
if (!storageId) {
|
|
611
|
-
const projectName =
|
|
688
|
+
const projectName = path2.basename(cwd);
|
|
612
689
|
const name = await prompt(`${colors.blue}?${colors.reset} \uC2A4\uD1A0\uB9AC\uC9C0 \uC774\uB984 (${projectName}): `) || projectName;
|
|
613
690
|
info("\uC6F9 \uC2A4\uD1A0\uB9AC\uC9C0 \uC0DD\uC131 \uC911...");
|
|
614
691
|
const createRes = await makeRequest(
|
|
@@ -648,7 +725,7 @@ ${colors.blue}?${colors.reset} \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
648
725
|
if (secretKey) {
|
|
649
726
|
config.secretKey = secretKey;
|
|
650
727
|
}
|
|
651
|
-
|
|
728
|
+
fs2.writeFileSync(rcPath, JSON.stringify(config, null, 2) + "\n");
|
|
652
729
|
success(".connectbaserc \uC0DD\uC131 \uC644\uB8CC");
|
|
653
730
|
addToGitignore(".connectbaserc");
|
|
654
731
|
addDeployScript(deployDir);
|
|
@@ -663,21 +740,21 @@ ${colors.dim}Claude Code\uC5D0\uC11C MCP\uAC00 \uC790\uB3D9 \uC5F0\uACB0\uB429\u
|
|
|
663
740
|
`);
|
|
664
741
|
}
|
|
665
742
|
function detectBuildDir() {
|
|
666
|
-
if (
|
|
667
|
-
if (
|
|
668
|
-
if (
|
|
669
|
-
if (
|
|
670
|
-
if (
|
|
671
|
-
if (
|
|
743
|
+
if (fs2.existsSync(path2.join(process.cwd(), "dist"))) return "./dist";
|
|
744
|
+
if (fs2.existsSync(path2.join(process.cwd(), "build"))) return "./build";
|
|
745
|
+
if (fs2.existsSync(path2.join(process.cwd(), "out"))) return "./out";
|
|
746
|
+
if (fs2.existsSync(path2.join(process.cwd(), ".next"))) return "./out";
|
|
747
|
+
if (fs2.existsSync(path2.join(process.cwd(), "vite.config.ts")) || fs2.existsSync(path2.join(process.cwd(), "vite.config.js"))) return "./dist";
|
|
748
|
+
if (fs2.existsSync(path2.join(process.cwd(), "next.config.js")) || fs2.existsSync(path2.join(process.cwd(), "next.config.mjs"))) return "./out";
|
|
672
749
|
return "./dist";
|
|
673
750
|
}
|
|
674
751
|
function addToGitignore(entry, basePath) {
|
|
675
752
|
const dir = basePath || process.cwd();
|
|
676
|
-
const gitignorePath =
|
|
677
|
-
if (
|
|
678
|
-
const content =
|
|
753
|
+
const gitignorePath = path2.join(dir, ".gitignore");
|
|
754
|
+
if (fs2.existsSync(gitignorePath)) {
|
|
755
|
+
const content = fs2.readFileSync(gitignorePath, "utf-8");
|
|
679
756
|
if (!content.includes(entry)) {
|
|
680
|
-
|
|
757
|
+
fs2.appendFileSync(gitignorePath, `
|
|
681
758
|
# Connect Base
|
|
682
759
|
${entry}
|
|
683
760
|
`);
|
|
@@ -686,20 +763,20 @@ ${entry}
|
|
|
686
763
|
info(`.gitignore\uC5D0 \uC774\uBBF8 ${entry}\uAC00 \uD3EC\uD568\uB418\uC5B4 \uC788\uC2B5\uB2C8\uB2E4`);
|
|
687
764
|
}
|
|
688
765
|
} else {
|
|
689
|
-
|
|
766
|
+
fs2.writeFileSync(gitignorePath, `# Connect Base
|
|
690
767
|
${entry}
|
|
691
768
|
`);
|
|
692
769
|
success(".gitignore \uC0DD\uC131 \uC644\uB8CC");
|
|
693
770
|
}
|
|
694
771
|
}
|
|
695
772
|
function addDeployScript(deployDir) {
|
|
696
|
-
const pkgPath =
|
|
697
|
-
if (!
|
|
773
|
+
const pkgPath = path2.join(process.cwd(), "package.json");
|
|
774
|
+
if (!fs2.existsSync(pkgPath)) {
|
|
698
775
|
warn("package.json\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC5B4 deploy \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uCD94\uAC00\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4");
|
|
699
776
|
return;
|
|
700
777
|
}
|
|
701
778
|
try {
|
|
702
|
-
const pkg = JSON.parse(
|
|
779
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf-8"));
|
|
703
780
|
if (!pkg.scripts) pkg.scripts = {};
|
|
704
781
|
if (pkg.scripts.deploy) {
|
|
705
782
|
info(`package.json\uC5D0 \uC774\uBBF8 deploy \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uC788\uC2B5\uB2C8\uB2E4: "${pkg.scripts.deploy}"`);
|
|
@@ -707,7 +784,7 @@ function addDeployScript(deployDir) {
|
|
|
707
784
|
}
|
|
708
785
|
const deployCmd = `connectbase-client deploy ${deployDir}`;
|
|
709
786
|
pkg.scripts.deploy = pkg.scripts.build ? `${pkg.scripts.build} && ${deployCmd}` : deployCmd;
|
|
710
|
-
|
|
787
|
+
fs2.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
|
|
711
788
|
success(`package.json\uC5D0 deploy \uC2A4\uD06C\uB9BD\uD2B8 \uCD94\uAC00 \uC644\uB8CC`);
|
|
712
789
|
} catch {
|
|
713
790
|
warn("package.json \uC218\uC815\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4");
|
|
@@ -724,32 +801,32 @@ function getGitRoot() {
|
|
|
724
801
|
function detectMonorepo(gitRoot) {
|
|
725
802
|
const cwd = process.cwd();
|
|
726
803
|
const result = { type: "none", root: gitRoot, isSubPackage: false };
|
|
727
|
-
if (
|
|
804
|
+
if (fs2.existsSync(path2.join(gitRoot, "pnpm-workspace.yaml"))) {
|
|
728
805
|
result.type = "pnpm";
|
|
729
|
-
} else if (
|
|
806
|
+
} else if (fs2.existsSync(path2.join(gitRoot, "turbo.json"))) {
|
|
730
807
|
result.type = "turborepo";
|
|
731
|
-
} else if (
|
|
808
|
+
} else if (fs2.existsSync(path2.join(gitRoot, "nx.json"))) {
|
|
732
809
|
result.type = "nx";
|
|
733
|
-
} else if (
|
|
810
|
+
} else if (fs2.existsSync(path2.join(gitRoot, "lerna.json"))) {
|
|
734
811
|
result.type = "lerna";
|
|
735
812
|
} else {
|
|
736
|
-
const rootPkgPath =
|
|
737
|
-
if (
|
|
813
|
+
const rootPkgPath = path2.join(gitRoot, "package.json");
|
|
814
|
+
if (fs2.existsSync(rootPkgPath)) {
|
|
738
815
|
try {
|
|
739
|
-
const pkg = JSON.parse(
|
|
816
|
+
const pkg = JSON.parse(fs2.readFileSync(rootPkgPath, "utf-8"));
|
|
740
817
|
if (pkg.workspaces) {
|
|
741
|
-
result.type =
|
|
818
|
+
result.type = fs2.existsSync(path2.join(gitRoot, "yarn.lock")) ? "yarn" : "npm";
|
|
742
819
|
}
|
|
743
820
|
} catch {
|
|
744
821
|
}
|
|
745
822
|
}
|
|
746
823
|
}
|
|
747
824
|
if (result.type !== "none" && cwd !== gitRoot) {
|
|
748
|
-
const cwdPkgPath =
|
|
749
|
-
if (
|
|
825
|
+
const cwdPkgPath = path2.join(cwd, "package.json");
|
|
826
|
+
if (fs2.existsSync(cwdPkgPath)) {
|
|
750
827
|
result.isSubPackage = true;
|
|
751
828
|
try {
|
|
752
|
-
const pkg = JSON.parse(
|
|
829
|
+
const pkg = JSON.parse(fs2.readFileSync(cwdPkgPath, "utf-8"));
|
|
753
830
|
result.subPackageName = pkg.name;
|
|
754
831
|
} catch {
|
|
755
832
|
}
|
|
@@ -766,13 +843,13 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
766
843
|
if (monorepo.type !== "none") {
|
|
767
844
|
info(`\uBAA8\uB178\uB808\uD3EC \uAC10\uC9C0: ${monorepo.type} (\uB8E8\uD2B8: ${gitRoot})`);
|
|
768
845
|
if (monorepo.isSubPackage) {
|
|
769
|
-
info(`\uD604\uC7AC \uC11C\uBE0C \uD328\uD0A4\uC9C0: ${monorepo.subPackageName ||
|
|
846
|
+
info(`\uD604\uC7AC \uC11C\uBE0C \uD328\uD0A4\uC9C0: ${monorepo.subPackageName || path2.basename(process.cwd())}`);
|
|
770
847
|
}
|
|
771
848
|
}
|
|
772
|
-
const docsDir =
|
|
773
|
-
const rootClaudeMd =
|
|
774
|
-
if (!
|
|
775
|
-
|
|
849
|
+
const docsDir = path2.join(gitRoot, ".claude", "docs");
|
|
850
|
+
const rootClaudeMd = path2.join(gitRoot, "CLAUDE.md");
|
|
851
|
+
if (!fs2.existsSync(docsDir)) {
|
|
852
|
+
fs2.mkdirSync(docsDir, { recursive: true });
|
|
776
853
|
}
|
|
777
854
|
if (!templates) {
|
|
778
855
|
templates = ["rules"];
|
|
@@ -791,13 +868,13 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
791
868
|
const data = typeof res.data === "string" ? JSON.parse(res.data) : res.data;
|
|
792
869
|
const sections = data.sections || [];
|
|
793
870
|
for (const section of sections) {
|
|
794
|
-
const filePath =
|
|
795
|
-
|
|
871
|
+
const filePath = path2.join(docsDir, section.filename);
|
|
872
|
+
fs2.writeFileSync(filePath, section.content);
|
|
796
873
|
}
|
|
797
874
|
success(`${gitRoot}/.claude/docs/ \uC5D0 ${sections.length}\uAC1C \uBB38\uC11C \uC800\uC7A5 \uC644\uB8CC`);
|
|
798
875
|
updateRootClaudeMd(rootClaudeMd);
|
|
799
876
|
if (monorepo.isSubPackage) {
|
|
800
|
-
const subClaudeMd =
|
|
877
|
+
const subClaudeMd = path2.join(process.cwd(), "CLAUDE.md");
|
|
801
878
|
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
802
879
|
}
|
|
803
880
|
log(`${colors.dim}\u203B SDK \uBB38\uC11C\uB294 MCP search_sdk_docs\uB85C \uAC80\uC0C9\uD558\uC138\uC694${colors.reset}`);
|
|
@@ -809,10 +886,10 @@ async function downloadDocs(publicKey, templates, baseDir) {
|
|
|
809
886
|
|
|
810
887
|
\uC0C1\uC138 \uAD6C\uD604\uBC95\uC740 MCP \`search_sdk_docs\` \uB3C4\uAD6C\uB85C \uAC80\uC0C9\uD558\uC138\uC694.
|
|
811
888
|
`;
|
|
812
|
-
|
|
889
|
+
fs2.writeFileSync(path2.join(docsDir, "project-rules.md"), fallbackContent);
|
|
813
890
|
updateRootClaudeMd(rootClaudeMd);
|
|
814
891
|
if (monorepo.isSubPackage) {
|
|
815
|
-
const subClaudeMd =
|
|
892
|
+
const subClaudeMd = path2.join(process.cwd(), "CLAUDE.md");
|
|
816
893
|
createSubPackageClaudeMd(subClaudeMd, gitRoot);
|
|
817
894
|
}
|
|
818
895
|
success(`.claude/docs/ \uC0DD\uC131 \uC644\uB8CC (\uAE30\uBCF8)`);
|
|
@@ -832,8 +909,8 @@ function updateRootClaudeMd(claudeMdPath) {
|
|
|
832
909
|
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
833
910
|
3. \uAC80\uC0C9 \uACB0\uACFC\uC758 \uCF54\uB4DC \uD328\uD134\uC744 \uB530\uB77C \uAD6C\uD604\uD558\uC138\uC694
|
|
834
911
|
${endMarker}`;
|
|
835
|
-
if (
|
|
836
|
-
let content =
|
|
912
|
+
if (fs2.existsSync(claudeMdPath)) {
|
|
913
|
+
let content = fs2.readFileSync(claudeMdPath, "utf-8");
|
|
837
914
|
const startIdx = content.indexOf(startMarker);
|
|
838
915
|
const endIdx = content.indexOf(endMarker);
|
|
839
916
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
@@ -841,10 +918,10 @@ ${endMarker}`;
|
|
|
841
918
|
} else {
|
|
842
919
|
content = sdkBlock + "\n\n" + content.trimStart();
|
|
843
920
|
}
|
|
844
|
-
|
|
921
|
+
fs2.writeFileSync(claudeMdPath, content);
|
|
845
922
|
info("CLAUDE.md\uC5D0 ConnectBase \uCC38\uC870 \uC5C5\uB370\uC774\uD2B8 \uC644\uB8CC");
|
|
846
923
|
} else {
|
|
847
|
-
|
|
924
|
+
fs2.writeFileSync(claudeMdPath, `# \uD504\uB85C\uC81D\uD2B8
|
|
848
925
|
|
|
849
926
|
${sdkBlock}
|
|
850
927
|
`);
|
|
@@ -854,7 +931,7 @@ ${sdkBlock}
|
|
|
854
931
|
function createSubPackageClaudeMd(subClaudeMdPath, gitRoot) {
|
|
855
932
|
const startMarker = "<!-- CONNECTBASE_SUB_START -->";
|
|
856
933
|
const endMarker = "<!-- CONNECTBASE_SUB_END -->";
|
|
857
|
-
const relPath =
|
|
934
|
+
const relPath = path2.relative(path2.dirname(subClaudeMdPath), gitRoot);
|
|
858
935
|
const subBlock = `${startMarker}
|
|
859
936
|
## ConnectBase SDK
|
|
860
937
|
|
|
@@ -866,8 +943,8 @@ function createSubPackageClaudeMd(subClaudeMdPath, gitRoot) {
|
|
|
866
943
|
2. \`search_sdk_docs("\uD0A4\uC6CC\uB4DC")\`\uB85C SDK \uAD6C\uD604 \uBC29\uBC95\uC744 \uAC80\uC0C9\uD558\uC138\uC694
|
|
867
944
|
3. \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8\uC758 \`${relPath}/CLAUDE.md\`\uB3C4 \uCC38\uACE0\uD558\uC138\uC694
|
|
868
945
|
${endMarker}`;
|
|
869
|
-
if (
|
|
870
|
-
let content =
|
|
946
|
+
if (fs2.existsSync(subClaudeMdPath)) {
|
|
947
|
+
let content = fs2.readFileSync(subClaudeMdPath, "utf-8");
|
|
871
948
|
const startIdx = content.indexOf(startMarker);
|
|
872
949
|
const endIdx = content.indexOf(endMarker);
|
|
873
950
|
if (startIdx !== -1 && endIdx !== -1) {
|
|
@@ -877,9 +954,9 @@ ${endMarker}`;
|
|
|
877
954
|
} else {
|
|
878
955
|
return;
|
|
879
956
|
}
|
|
880
|
-
|
|
957
|
+
fs2.writeFileSync(subClaudeMdPath, content);
|
|
881
958
|
} else {
|
|
882
|
-
|
|
959
|
+
fs2.writeFileSync(subClaudeMdPath, `# ${path2.basename(path2.dirname(subClaudeMdPath))}
|
|
883
960
|
|
|
884
961
|
${subBlock}
|
|
885
962
|
`);
|
|
@@ -896,7 +973,7 @@ async function setupMcp(secretKey) {
|
|
|
896
973
|
info(`MCP \uC124\uC815\uC740 \uD504\uB85C\uC81D\uD2B8 \uB8E8\uD2B8(${root})\uC5D0 \uC0DD\uC131\uB429\uB2C8\uB2E4`);
|
|
897
974
|
}
|
|
898
975
|
}
|
|
899
|
-
const mcpConfigPath =
|
|
976
|
+
const mcpConfigPath = path2.join(root, ".mcp.json");
|
|
900
977
|
if (!secretKey) {
|
|
901
978
|
log(`
|
|
902
979
|
${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${colors.reset}`);
|
|
@@ -907,16 +984,16 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
907
984
|
if (authChoice === "2") {
|
|
908
985
|
try {
|
|
909
986
|
secretKey = await browserAuthFlow();
|
|
910
|
-
const skRcPath =
|
|
987
|
+
const skRcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
911
988
|
let rcData = {};
|
|
912
|
-
if (
|
|
989
|
+
if (fs2.existsSync(skRcPath)) {
|
|
913
990
|
try {
|
|
914
|
-
rcData = JSON.parse(
|
|
991
|
+
rcData = JSON.parse(fs2.readFileSync(skRcPath, "utf-8"));
|
|
915
992
|
} catch {
|
|
916
993
|
}
|
|
917
994
|
}
|
|
918
995
|
rcData.secretKey = secretKey;
|
|
919
|
-
|
|
996
|
+
fs2.writeFileSync(skRcPath, JSON.stringify(rcData, null, 2) + "\n");
|
|
920
997
|
addToGitignore(".connectbaserc", root);
|
|
921
998
|
success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
|
|
922
999
|
} catch (err) {
|
|
@@ -935,21 +1012,21 @@ ${colors.dim}\uC778\uC99D \uBC29\uC2DD\uC744 \uC120\uD0DD\uD558\uC138\uC694:${co
|
|
|
935
1012
|
}
|
|
936
1013
|
};
|
|
937
1014
|
let mcpConfig = { mcpServers: {} };
|
|
938
|
-
if (
|
|
1015
|
+
if (fs2.existsSync(mcpConfigPath)) {
|
|
939
1016
|
try {
|
|
940
|
-
mcpConfig = JSON.parse(
|
|
1017
|
+
mcpConfig = JSON.parse(fs2.readFileSync(mcpConfigPath, "utf-8"));
|
|
941
1018
|
if (!mcpConfig.mcpServers || typeof mcpConfig.mcpServers !== "object") {
|
|
942
1019
|
mcpConfig.mcpServers = {};
|
|
943
1020
|
}
|
|
944
1021
|
} catch {
|
|
945
1022
|
const backupPath = mcpConfigPath + ".backup";
|
|
946
|
-
|
|
1023
|
+
fs2.copyFileSync(mcpConfigPath, backupPath);
|
|
947
1024
|
warn(`.mcp.json \uD30C\uC2F1 \uC2E4\uD328, \uBC31\uC5C5 \uC0DD\uC131: ${backupPath}`);
|
|
948
1025
|
mcpConfig = { mcpServers: {} };
|
|
949
1026
|
}
|
|
950
1027
|
}
|
|
951
1028
|
mcpConfig.mcpServers["connect-base"] = mcpEntry;
|
|
952
|
-
|
|
1029
|
+
fs2.writeFileSync(mcpConfigPath, JSON.stringify(mcpConfig, null, 2) + "\n");
|
|
953
1030
|
success(`${mcpConfigPath} \uC0DD\uC131 \uC644\uB8CC`);
|
|
954
1031
|
addToGitignore(".mcp.json", root);
|
|
955
1032
|
if (secretKey && secretKey !== "YOUR_SECRET_KEY_HERE") {
|
|
@@ -1125,7 +1202,7 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
1125
1202
|
success(`\uC120\uD0DD\uB428: ${apps[num - 1].name}`);
|
|
1126
1203
|
return apps[num - 1].id;
|
|
1127
1204
|
}
|
|
1128
|
-
const projectName =
|
|
1205
|
+
const projectName = path2.basename(process.cwd());
|
|
1129
1206
|
const appName = await prompt(`${colors.blue}?${colors.reset} \uC571 \uC774\uB984 (${projectName}): `) || projectName;
|
|
1130
1207
|
info("\uC571 \uC0DD\uC131 \uC911...");
|
|
1131
1208
|
const createRes = await makeRequest(
|
|
@@ -1147,67 +1224,15 @@ ${colors.blue}?${colors.reset} \uC571 \uC120\uD0DD (\uBC88\uD638): `);
|
|
|
1147
1224
|
success(`\uC571 \uC0DD\uC131 \uC644\uB8CC: ${createData.app_name}`);
|
|
1148
1225
|
return createData.app_id;
|
|
1149
1226
|
}
|
|
1150
|
-
function
|
|
1151
|
-
const
|
|
1152
|
-
if (
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
const stateHome = process.env.XDG_STATE_HOME || path.join(os.homedir(), ".local", "state");
|
|
1158
|
-
return path.join(stateHome, "connectbase", "tunnel-locks");
|
|
1159
|
-
}
|
|
1160
|
-
function isProcessAlive(pid) {
|
|
1161
|
-
try {
|
|
1162
|
-
process.kill(pid, 0);
|
|
1163
|
-
return true;
|
|
1164
|
-
} catch (e) {
|
|
1165
|
-
return e.code === "EPERM";
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
function acquireTunnelLock(appID, port, force) {
|
|
1169
|
-
const lockDir = getTunnelLockDir();
|
|
1170
|
-
fs.mkdirSync(lockDir, { recursive: true });
|
|
1171
|
-
const lockPath = path.join(lockDir, `${appID}-${port}.lock`);
|
|
1172
|
-
const lockData = {
|
|
1173
|
-
pid: process.pid,
|
|
1174
|
-
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1175
|
-
appID,
|
|
1176
|
-
port,
|
|
1177
|
-
host: os.hostname(),
|
|
1178
|
-
version: VERSION
|
|
1179
|
-
};
|
|
1180
|
-
if (!force) {
|
|
1181
|
-
try {
|
|
1182
|
-
const fd = fs.openSync(lockPath, "wx");
|
|
1183
|
-
fs.writeSync(fd, JSON.stringify(lockData, null, 2));
|
|
1184
|
-
fs.closeSync(fd);
|
|
1185
|
-
return lockPath;
|
|
1186
|
-
} catch (e) {
|
|
1187
|
-
if (e.code !== "EEXIST") {
|
|
1188
|
-
warn(`lockfile \uC0DD\uC131 \uC2E4\uD328: ${e.message}`);
|
|
1189
|
-
return null;
|
|
1190
|
-
}
|
|
1191
|
-
}
|
|
1192
|
-
try {
|
|
1193
|
-
const existing = JSON.parse(fs.readFileSync(lockPath, "utf-8"));
|
|
1194
|
-
if (isProcessAlive(existing.pid)) {
|
|
1195
|
-
error(`\uC774\uBBF8 \uC2E4\uD589 \uC911\uC778 \uD130\uB110\uC774 \uC788\uC2B5\uB2C8\uB2E4: PID ${existing.pid}, \uC2DC\uC791 ${existing.startedAt}, \uD638\uC2A4\uD2B8 ${existing.host}`);
|
|
1196
|
-
info("\uC911\uBCF5 \uC2E4\uD589\uC744 \uBB34\uC2DC\uD558\uB824\uBA74 --force \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694");
|
|
1197
|
-
process.exit(2);
|
|
1198
|
-
}
|
|
1199
|
-
warn(`stale lockfile \uBC1C\uACAC (PID ${existing.pid} \uC885\uB8CC\uB428), \uB36E\uC5B4\uC501\uB2C8\uB2E4`);
|
|
1200
|
-
} catch {
|
|
1201
|
-
}
|
|
1202
|
-
}
|
|
1203
|
-
fs.writeFileSync(lockPath, JSON.stringify(lockData, null, 2));
|
|
1204
|
-
return lockPath;
|
|
1205
|
-
}
|
|
1206
|
-
function releaseTunnelLock(lockPath) {
|
|
1207
|
-
try {
|
|
1208
|
-
fs.unlinkSync(lockPath);
|
|
1209
|
-
} catch {
|
|
1227
|
+
function acquireTunnelLock2(appID, port, force) {
|
|
1228
|
+
const result = acquireTunnelLock(appID, port, force, VERSION);
|
|
1229
|
+
if (result && result.startsWith("LOCKED:")) {
|
|
1230
|
+
const [, pid, startedAt, host] = result.split(":");
|
|
1231
|
+
error(`\uC774\uBBF8 \uC2E4\uD589 \uC911\uC778 \uD130\uB110\uC774 \uC788\uC2B5\uB2C8\uB2E4: PID ${pid}, \uC2DC\uC791 ${startedAt}, \uD638\uC2A4\uD2B8 ${host}`);
|
|
1232
|
+
info("\uC911\uBCF5 \uC2E4\uD589\uC744 \uBB34\uC2DC\uD558\uB824\uBA74 --force \uC635\uC158\uC744 \uC0AC\uC6A9\uD558\uC138\uC694");
|
|
1233
|
+
process.exit(2);
|
|
1210
1234
|
}
|
|
1235
|
+
return result;
|
|
1211
1236
|
}
|
|
1212
1237
|
async function startTunnel(port, config, tunnelOpts) {
|
|
1213
1238
|
let tunnelKey = config.secretKey || (config.publicKey?.startsWith("cb_sk_") ? config.publicKey : "");
|
|
@@ -1215,16 +1240,16 @@ async function startTunnel(port, config, tunnelOpts) {
|
|
|
1215
1240
|
info("Secret Key\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uBE0C\uB77C\uC6B0\uC800 \uB85C\uADF8\uC778\uC73C\uB85C \uBC1C\uAE09\uD569\uB2C8\uB2E4...");
|
|
1216
1241
|
try {
|
|
1217
1242
|
const sk = await browserAuthFlow();
|
|
1218
|
-
const rcPath2 =
|
|
1243
|
+
const rcPath2 = path2.join(process.cwd(), ".connectbaserc");
|
|
1219
1244
|
let rcData = {};
|
|
1220
|
-
if (
|
|
1245
|
+
if (fs2.existsSync(rcPath2)) {
|
|
1221
1246
|
try {
|
|
1222
|
-
rcData = JSON.parse(
|
|
1247
|
+
rcData = JSON.parse(fs2.readFileSync(rcPath2, "utf-8"));
|
|
1223
1248
|
} catch {
|
|
1224
1249
|
}
|
|
1225
1250
|
}
|
|
1226
1251
|
rcData.secretKey = sk;
|
|
1227
|
-
|
|
1252
|
+
fs2.writeFileSync(rcPath2, JSON.stringify(rcData, null, 2) + "\n");
|
|
1228
1253
|
addToGitignore(".connectbaserc");
|
|
1229
1254
|
success("Secret Key \uBC1C\uAE09 \uBC0F \uC800\uC7A5 \uC644\uB8CC");
|
|
1230
1255
|
tunnelKey = sk;
|
|
@@ -1237,11 +1262,11 @@ async function startTunnel(port, config, tunnelOpts) {
|
|
|
1237
1262
|
} else {
|
|
1238
1263
|
config.secretKey = tunnelKey;
|
|
1239
1264
|
}
|
|
1240
|
-
const rcPath =
|
|
1265
|
+
const rcPath = path2.join(process.cwd(), ".connectbaserc");
|
|
1241
1266
|
let savedAppId = tunnelOpts?.appId || "";
|
|
1242
1267
|
if (!savedAppId) {
|
|
1243
1268
|
try {
|
|
1244
|
-
const rc = JSON.parse(
|
|
1269
|
+
const rc = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1245
1270
|
if (rc.tunnelAppId) savedAppId = rc.tunnelAppId;
|
|
1246
1271
|
} catch {
|
|
1247
1272
|
}
|
|
@@ -1249,19 +1274,19 @@ async function startTunnel(port, config, tunnelOpts) {
|
|
|
1249
1274
|
const appId = await resolveAppForTunnel(tunnelKey, config.baseUrl, savedAppId);
|
|
1250
1275
|
try {
|
|
1251
1276
|
let rcData = {};
|
|
1252
|
-
if (
|
|
1277
|
+
if (fs2.existsSync(rcPath)) {
|
|
1253
1278
|
try {
|
|
1254
|
-
rcData = JSON.parse(
|
|
1279
|
+
rcData = JSON.parse(fs2.readFileSync(rcPath, "utf-8"));
|
|
1255
1280
|
} catch {
|
|
1256
1281
|
}
|
|
1257
1282
|
}
|
|
1258
1283
|
if (rcData.tunnelAppId !== appId) {
|
|
1259
1284
|
rcData.tunnelAppId = appId;
|
|
1260
|
-
|
|
1285
|
+
fs2.writeFileSync(rcPath, JSON.stringify(rcData, null, 2) + "\n");
|
|
1261
1286
|
}
|
|
1262
1287
|
} catch {
|
|
1263
1288
|
}
|
|
1264
|
-
const lockPath =
|
|
1289
|
+
const lockPath = acquireTunnelLock2(appId, port, tunnelOpts?.force ?? false);
|
|
1265
1290
|
const tunnelServerUrl = getTunnelServerUrl(config.baseUrl);
|
|
1266
1291
|
const parsedUrl = new URL(tunnelServerUrl);
|
|
1267
1292
|
const isHttps = parsedUrl.protocol === "https:";
|
|
@@ -1416,15 +1441,15 @@ ${colors.dim}Ctrl+C\uB85C \uC885\uB8CC${colors.reset}
|
|
|
1416
1441
|
case "http_request":
|
|
1417
1442
|
forwardRequest(msg, sock, localPort);
|
|
1418
1443
|
break;
|
|
1419
|
-
case "tunnel_error":
|
|
1420
|
-
|
|
1421
|
-
|
|
1444
|
+
case "tunnel_error": {
|
|
1445
|
+
const result = handleTunnelError(msg, appId, localPort);
|
|
1446
|
+
error(result.message);
|
|
1447
|
+
if (result.action === "exit") {
|
|
1422
1448
|
shouldReconnect = false;
|
|
1423
|
-
setTimeout(() => process.exit(1), 500);
|
|
1424
|
-
} else {
|
|
1425
|
-
error(`\uD130\uB110 \uC5D0\uB7EC: ${msg.error || msg.message || "\uC54C \uC218 \uC5C6\uB294 \uC5D0\uB7EC"}`);
|
|
1449
|
+
setTimeout(() => process.exit(result.exitCode ?? 1), 500);
|
|
1426
1450
|
}
|
|
1427
1451
|
break;
|
|
1452
|
+
}
|
|
1428
1453
|
case "ping":
|
|
1429
1454
|
sock.write(createWsTextFrame(JSON.stringify({ type: "pong" })));
|
|
1430
1455
|
break;
|