feat: add npm package for easy installation and improve release build

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
chenhg5andClaude committed 2026-02-28 16:22:50 +08:00
1 parent b3f9887454
commit c24db65243
6 files changed
+265 -1

No files matched your search

+11 -1
View File
@@ -48,7 +48,17 @@ release-all: clean
echo "Building $(OUT)" && \
GOOS=$(GOOS) GOARCH=$(GOARCH) CGO_ENABLED=0 \
go build -ldflags "$(LDFLAGS)" -o $(OUT) $(CMD) && \
) echo "Done. Binaries in $(DIST)/"
) true
@echo "Packaging archives..."
@cd $(DIST) && for f in $(APP)-*; do \
case "$$f" in \
*.tar.gz|*.zip) continue ;; \
*.exe) zip "$${f%.exe}.zip" "$$f" ;; \
*) tar czf "$$f.tar.gz" "$$f" ;; \
esac; \
done
@cd $(DIST) && sha256sum * > checksums.txt
@echo "Done. Binaries and archives in $(DIST)/"
release:
@if [ -z "$(TARGET)" ]; then \
+2
View File
@@ -0,0 +1,2 @@
bin/
node_modules/
+26
View File
@@ -0,0 +1,26 @@
# cc-connect
Bridge local AI coding agents (Claude Code, Cursor, Gemini CLI, Codex) to messaging platforms (Feishu/Lark, DingTalk, Slack, Telegram, Discord, LINE, WeChat Work).
Chat with your AI dev assistant from anywhere.
## Install
```bash
npm install -g cc-connect
```
## Usage
```bash
# Create config
cc-connect --version
# Edit config.toml, then run
cc-connect
cc-connect -config /path/to/config.toml
```
## Documentation
See full documentation at: https://github.com/chenhg5/cc-connect
+163
View File
@@ -0,0 +1,163 @@
#!/usr/bin/env node
"use strict";
const { execSync } = require("child_process");
const fs = require("fs");
const path = require("path");
const https = require("https");
const http = require("http");
const zlib = require("zlib");
const PACKAGE = require("./package.json");
const VERSION = `v${PACKAGE.version}`;
const NAME = "cc-connect";
const GITHUB_REPO = "chenhg5/cc-connect";
const GITEE_REPO = "chenhg5/cc-connect";
const PLATFORM_MAP = {
darwin: "darwin",
linux: "linux",
win32: "windows",
};
const ARCH_MAP = {
x64: "amd64",
arm64: "arm64",
};
function getPlatformInfo() {
const platform = PLATFORM_MAP[process.platform];
const arch = ARCH_MAP[process.arch];
if (!platform || !arch) {
throw new Error(
`Unsupported platform: ${process.platform}/${process.arch}. ` +
`Supported: linux/darwin/windows x64/arm64`
);
}
const ext = platform === "windows" ? ".zip" : ".tar.gz";
const filename = `${NAME}-${VERSION}-${platform}-${arch}${ext}`;
return { platform, arch, ext, filename };
}
function getDownloadURLs(filename) {
return [
`https://github.com/${GITHUB_REPO}/releases/download/${VERSION}/${filename}`,
`https://gitee.com/${GITEE_REPO}/releases/download/${VERSION}/${filename}`,
];
}
function fetch(url, redirects = 5) {
return new Promise((resolve, reject) => {
if (redirects <= 0) return reject(new Error("Too many redirects"));
const mod = url.startsWith("https") ? https : http;
mod
.get(url, { headers: { "User-Agent": "cc-connect-npm" } }, (res) => {
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
return resolve(fetch(res.headers.location, redirects - 1));
}
if (res.statusCode !== 200) {
res.resume();
return reject(new Error(`HTTP ${res.statusCode} for ${url}`));
}
const chunks = [];
res.on("data", (c) => chunks.push(c));
res.on("end", () => resolve(Buffer.concat(chunks)));
res.on("error", reject);
})
.on("error", reject);
});
}
async function download(urls) {
for (const url of urls) {
try {
console.log(`[cc-connect] Downloading from ${url}`);
const data = await fetch(url);
console.log(`[cc-connect] Downloaded ${(data.length / 1024 / 1024).toFixed(1)} MB`);
return data;
} catch (err) {
console.warn(`[cc-connect] Failed: ${err.message}, trying next source...`);
}
}
throw new Error(
`[cc-connect] Could not download binary from any source.\n` +
` Tried: ${urls.join(", ")}\n` +
` You can download manually from https://github.com/${GITHUB_REPO}/releases`
);
}
function extractTarGz(buffer, destDir, binaryName) {
const tmpFile = path.join(destDir, "_tmp.tar.gz");
fs.writeFileSync(tmpFile, buffer);
try {
execSync(`tar xzf "${tmpFile}" -C "${destDir}"`, { stdio: "pipe" });
} finally {
fs.unlinkSync(tmpFile);
}
const extracted = fs.readdirSync(destDir).find((f) => f.startsWith(NAME) && !f.endsWith(".tar.gz"));
if (extracted && extracted !== binaryName) {
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
}
}
function extractZip(buffer, destDir, binaryName) {
const tmpFile = path.join(destDir, "_tmp.zip");
fs.writeFileSync(tmpFile, buffer);
try {
try {
execSync(`unzip -o "${tmpFile}" -d "${destDir}"`, { stdio: "pipe" });
} catch {
execSync(`powershell -Command "Expand-Archive -Force '${tmpFile}' '${destDir}'"`, {
stdio: "pipe",
});
}
} finally {
try { fs.unlinkSync(tmpFile); } catch {}
}
const extracted = fs.readdirSync(destDir).find((f) => f.startsWith(NAME) && f.endsWith(".exe"));
if (extracted && extracted !== binaryName) {
fs.renameSync(path.join(destDir, extracted), path.join(destDir, binaryName));
}
}
async function main() {
const { platform, arch, ext, filename } = getPlatformInfo();
console.log(`[cc-connect] Platform: ${platform}/${arch}`);
const binDir = path.join(__dirname, "bin");
fs.mkdirSync(binDir, { recursive: true });
const binaryName = platform === "windows" ? `${NAME}.exe` : NAME;
const binaryPath = path.join(binDir, binaryName);
if (fs.existsSync(binaryPath)) {
console.log(`[cc-connect] Binary already exists at ${binaryPath}`);
return;
}
const urls = getDownloadURLs(filename);
const data = await download(urls);
if (ext === ".tar.gz") {
extractTarGz(data, binDir, binaryName);
} else {
extractZip(data, binDir, binaryName);
}
if (platform !== "windows") {
fs.chmodSync(binaryPath, 0o755);
}
console.log(`[cc-connect] Installed to ${binaryPath}`);
}
main().catch((err) => {
console.error(err.message);
console.error(
"[cc-connect] Installation failed. You can install manually:\n" +
` https://github.com/${GITHUB_REPO}/releases/tag/${VERSION}`
);
process.exit(1);
});
+36
View File
@@ -0,0 +1,36 @@
{
"name": "cc-connect",
"version": "1.0.0",
"description": "Bridge local AI coding agents (Claude Code, Cursor, Gemini CLI) to messaging platforms (Feishu, DingTalk, Slack, Telegram, Discord, LINE, WeChat Work)",
"keywords": [
"claude-code",
"ai-coding",
"feishu",
"dingtalk",
"slack",
"telegram",
"discord",
"line",
"wechat-work",
"chatbot",
"bridge"
],
"homepage": "https://github.com/chenhg5/cc-connect",
"repository": {
"type": "git",
"url": "https://github.com/chenhg5/cc-connect.git"
},
"license": "MIT",
"author": "chenhg5",
"bin": {
"cc-connect": "run.js"
},
"scripts": {
"postinstall": "node install.js"
},
"files": [
"install.js",
"run.js",
"README.md"
]
}
+27
View File
@@ -0,0 +1,27 @@
#!/usr/bin/env node
"use strict";
const { execFileSync } = require("child_process");
const path = require("path");
const fs = require("fs");
const NAME = "cc-connect";
const binDir = path.join(__dirname, "bin");
const ext = process.platform === "win32" ? ".exe" : "";
const binaryPath = path.join(binDir, NAME + ext);
if (!fs.existsSync(binaryPath)) {
console.error(
`[cc-connect] Binary not found at ${binaryPath}\n` +
` Run "node install.js" in ${__dirname} or reinstall:\n` +
` npm install -g cc-connect`
);
process.exit(1);
}
try {
execFileSync(binaryPath, process.argv.slice(2), { stdio: "inherit" });
} catch (err) {
process.exit(err.status || 1);
}