cc-connect 1.0.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/README.md +26 -0
- package/install.js +172 -0
- package/package.json +36 -0
- package/run.js +27 -0
package/README.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# cc-connect
|
|
2
|
+
|
|
3
|
+
Bridge local AI coding agents (Claude Code, Cursor, Gemini CLI, Codex) to messaging platforms (Feishu/Lark, DingTalk, Slack, Telegram, Discord, LINE, WeChat Work).
|
|
4
|
+
|
|
5
|
+
Chat with your AI dev assistant from anywhere.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g cc-connect
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Usage
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Create config
|
|
17
|
+
cc-connect --version
|
|
18
|
+
|
|
19
|
+
# Edit config.toml, then run
|
|
20
|
+
cc-connect
|
|
21
|
+
cc-connect -config /path/to/config.toml
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Documentation
|
|
25
|
+
|
|
26
|
+
See full documentation at: https://github.com/chenhg5/cc-connect
|
package/install.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { execSync } = require("child_process");
|
|
6
|
+
const fs = require("fs");
|
|
7
|
+
const path = require("path");
|
|
8
|
+
const https = require("https");
|
|
9
|
+
const http = require("http");
|
|
10
|
+
const zlib = require("zlib");
|
|
11
|
+
|
|
12
|
+
const PACKAGE = require("./package.json");
|
|
13
|
+
const VERSION = `v${PACKAGE.version}`;
|
|
14
|
+
const NAME = "cc-connect";
|
|
15
|
+
|
|
16
|
+
const GITHUB_REPO = "chenhg5/cc-connect";
|
|
17
|
+
const GITEE_REPO = "cg33/cc-connect";
|
|
18
|
+
|
|
19
|
+
const PLATFORM_MAP = {
|
|
20
|
+
darwin: "darwin",
|
|
21
|
+
linux: "linux",
|
|
22
|
+
win32: "windows",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const ARCH_MAP = {
|
|
26
|
+
x64: "amd64",
|
|
27
|
+
arm64: "arm64",
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
function getPlatformInfo() {
|
|
31
|
+
const platform = PLATFORM_MAP[process.platform];
|
|
32
|
+
const arch = ARCH_MAP[process.arch];
|
|
33
|
+
if (!platform || !arch) {
|
|
34
|
+
throw new Error(
|
|
35
|
+
`Unsupported platform: ${process.platform}/${process.arch}. ` +
|
|
36
|
+
`Supported: linux/darwin/windows x64/arm64`
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
const ext = platform === "windows" ? ".zip" : ".tar.gz";
|
|
40
|
+
const filename = `${NAME}-${VERSION}-${platform}-${arch}${ext}`;
|
|
41
|
+
return { platform, arch, ext, filename };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function getDownloadURLs(filename) {
|
|
45
|
+
return [
|
|
46
|
+
`https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${filename}`,
|
|
47
|
+
`https://gitee.com/${GITEE_REPO}/releases/download/${VERSION}/${filename}`,
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function fetch(url, redirects = 5) {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
if (redirects <= 0) return reject(new Error("Too many redirects"));
|
|
54
|
+
const mod = url.startsWith("https") ? https : http;
|
|
55
|
+
mod
|
|
56
|
+
.get(url, { headers: { "User-Agent": "cc-connect-npm" } }, (res) => {
|
|
57
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
58
|
+
return resolve(fetch(res.headers.location, redirects - 1));
|
|
59
|
+
}
|
|
60
|
+
if (res.statusCode !== 200) {
|
|
61
|
+
res.resume();
|
|
62
|
+
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
|
|
63
|
+
}
|
|
64
|
+
const chunks = [];
|
|
65
|
+
res.on("data", (c) => chunks.push(c));
|
|
66
|
+
res.on("end", () => resolve(Buffer.concat(chunks)));
|
|
67
|
+
res.on("error", reject);
|
|
68
|
+
})
|
|
69
|
+
.on("error", reject);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function download(urls) {
|
|
74
|
+
for (const url of urls) {
|
|
75
|
+
try {
|
|
76
|
+
console.log(`[cc-connect] Downloading from ${url}`);
|
|
77
|
+
const data = await fetch(url);
|
|
78
|
+
console.log(`[cc-connect] Downloaded ${(data.length / 1024 / 1024).toFixed(1)} MB`);
|
|
79
|
+
return data;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.warn(`[cc-connect] Failed: ${err.message}, trying next source...`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
throw new Error(
|
|
85
|
+
`[cc-connect] Could not download binary from any source.\n` +
|
|
86
|
+
` Tried: ${urls.join(", ")}\n` +
|
|
87
|
+
` You can download manually from https://github.com/${GITHUB_REPO}/releases`
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function extractTarGz(buffer, destDir, binaryName) {
|
|
92
|
+
const tmpFile = path.join(destDir, "_tmp.tar.gz");
|
|
93
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
94
|
+
try {
|
|
95
|
+
execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, { stdio: "pipe" });
|
|
96
|
+
} finally {
|
|
97
|
+
fs.unlinkSync(tmpFile);
|
|
98
|
+
}
|
|
99
|
+
const extracted = fs.readdirSync(destDir).find((f) => f.startsWith(NAME) && !f.endsWith(".tar.gz"));
|
|
100
|
+
if (extracted && extracted !== binaryName) {
|
|
101
|
+
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function extractZip(buffer, destDir, binaryName) {
|
|
106
|
+
const tmpFile = path.join(destDir, "_tmp.zip");
|
|
107
|
+
fs.writeFileSync(tmpFile, buffer);
|
|
108
|
+
try {
|
|
109
|
+
try {
|
|
110
|
+
execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: "pipe" });
|
|
111
|
+
} catch {
|
|
112
|
+
execSync(`powershell -Command "Expand-Archive -Force '${tmpFile}' '${destDir}'"`, {
|
|
113
|
+
stdio: "pipe",
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
} finally {
|
|
117
|
+
try { fs.unlinkSync(tmpFile); } catch {}
|
|
118
|
+
}
|
|
119
|
+
const extracted = fs.readdirSync(destDir).find((f) => f.startsWith(NAME) && f.endsWith(".exe"));
|
|
120
|
+
if (extracted && extracted !== binaryName) {
|
|
121
|
+
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function main() {
|
|
126
|
+
const { platform, arch, ext, filename } = getPlatformInfo();
|
|
127
|
+
console.log(`[cc-connect] Platform: ${platform}/${arch}`);
|
|
128
|
+
|
|
129
|
+
const binDir = path.join(__dirname, "bin");
|
|
130
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
131
|
+
|
|
132
|
+
const binaryName = platform === "windows" ? `${NAME}.exe` : NAME;
|
|
133
|
+
const binaryPath = path.join(binDir, binaryName);
|
|
134
|
+
|
|
135
|
+
if (fs.existsSync(binaryPath)) {
|
|
136
|
+
console.log(`[cc-connect] Binary already exists at ${binaryPath}`);
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const urls = getDownloadURLs(filename);
|
|
141
|
+
const data = await download(urls);
|
|
142
|
+
|
|
143
|
+
if (ext === ".tar.gz") {
|
|
144
|
+
extractTarGz(data, binDir, binaryName);
|
|
145
|
+
} else {
|
|
146
|
+
extractZip(data, binDir, binaryName);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (platform !== "windows") {
|
|
150
|
+
fs.chmodSync(binaryPath, 0o755);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (platform === "darwin") {
|
|
154
|
+
try {
|
|
155
|
+
execSync(`xattr -d com.apple.quarantine "${binaryPath}"`, { stdio: "pipe" });
|
|
156
|
+
console.log(`[cc-connect] Removed macOS quarantine attribute`);
|
|
157
|
+
} catch {
|
|
158
|
+
// xattr fails if the attribute doesn't exist, which is fine
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
console.log(`[cc-connect] Installed to ${binaryPath}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
main().catch((err) => {
|
|
166
|
+
console.error(err.message);
|
|
167
|
+
console.error(
|
|
168
|
+
"[cc-connect] Installation failed. You can install manually:\n" +
|
|
169
|
+
` https://github.com/${GITHUB_REPO}/releases/tag/${VERSION}`
|
|
170
|
+
);
|
|
171
|
+
process.exit(1);
|
|
172
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cc-connect",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Bridge local AI coding agents (Claude Code, Cursor, Gemini CLI) to messaging platforms (Feishu, DingTalk, Slack, Telegram, Discord, LINE, WeChat Work)",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"claude-code",
|
|
7
|
+
"ai-coding",
|
|
8
|
+
"feishu",
|
|
9
|
+
"dingtalk",
|
|
10
|
+
"slack",
|
|
11
|
+
"telegram",
|
|
12
|
+
"discord",
|
|
13
|
+
"line",
|
|
14
|
+
"wechat-work",
|
|
15
|
+
"chatbot",
|
|
16
|
+
"bridge"
|
|
17
|
+
],
|
|
18
|
+
"homepage": "https://github.com/chenhg5/cc-connect",
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/chenhg5/cc-connect.git"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "chenhg5",
|
|
25
|
+
"bin": {
|
|
26
|
+
"cc-connect": "run.js"
|
|
27
|
+
},
|
|
28
|
+
"scripts": {
|
|
29
|
+
"postinstall": "node install.js"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"install.js",
|
|
33
|
+
"run.js",
|
|
34
|
+
"README.md"
|
|
35
|
+
]
|
|
36
|
+
}
|
package/run.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
"use strict";
|
|
4
|
+
|
|
5
|
+
const { execFileSync } = require("child_process");
|
|
6
|
+
const path = require("path");
|
|
7
|
+
const fs = require("fs");
|
|
8
|
+
|
|
9
|
+
const NAME = "cc-connect";
|
|
10
|
+
const binDir = path.join(__dirname, "bin");
|
|
11
|
+
const ext = process.platform === "win32" ? ".exe" : "";
|
|
12
|
+
const binaryPath = path.join(binDir, NAME + ext);
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(binaryPath)) {
|
|
15
|
+
console.error(
|
|
16
|
+
`[cc-connect] Binary not found at ${binaryPath}\n` +
|
|
17
|
+
` Run "node install.js" in ${__dirname} or reinstall:\n` +
|
|
18
|
+
` npm install -g cc-connect`
|
|
19
|
+
);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
try {
|
|
24
|
+
execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
|
|
25
|
+
} catch (err) {
|
|
26
|
+
process.exit(err.status || 1);
|
|
27
|
+
}
|