Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dist/
.env.local
*.local.json
*.bak.*
bun.lock

# Test output
/tmp/
Expand Down
52 changes: 52 additions & 0 deletions cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,54 @@ function installCmd(): void {
`);
}

// ── sync ────────────────────────────────────────────────────────────────────

function syncCmd(): void {
log("🔄 Forge Hub sync\n");

// 1. Re-stage package snapshot from current source
const packageRoot = stagePackageRuntime();
const serverSrc = path.join(packageRoot, "hub-server");

// 2. Copy hub-server .ts/.json/.lock files to runtime
cpDir(serverSrc, HUB_DIR, [".ts", ".json", ".lock"]);
cleanDirContents(CHANNELS_RUNTIME);
cpDir(path.join(serverSrc, "channels"), CHANNELS_RUNTIME, [".ts"]);

// Security: maintain 700 permissions
try {
fs.chmodSync(HUB_DIR, 0o700);
fs.chmodSync(CHANNELS_RUNTIME, 0o700);
} catch (err) {
console.warn(`⚠️ chmod 700 失败: ${String(err)}`);
}
log("✓ hub-server runtime synced(channels/ 已更新)");

// 3. Restart hub via launchctl
if (os.platform() === "darwin") {
const uid = os.userInfo().uid;
const label = `gui/${uid}/com.forge-hub`;
try {
// kickstart -k: forcibly restart by plist path
execFileSync("launchctl", ["kickstart", "-k", "-p", LAUNCHD_PLIST], { stdio: "ignore" });
log("✓ Hub 已重启");
} catch {
// Fallback: bootout + bootstrap if kickstart by path fails
try {
execFileSync("launchctl", ["bootout", label], { stdio: "ignore" });
} catch { /* might not be bootstrapped */ }
try {
execFileSync("launchctl", ["bootstrap", label, LAUNCHD_PLIST], { stdio: "ignore" });
log("✓ Hub 已重启");
} catch {
log(`⚠️ 无法重启 Hub。手动执行:launchctl bootout ${label} && launchctl bootstrap ${label} ${LAUNCHD_PLIST}`);
}
}
}

log("\n✅ Sync 完成。hub-server 运行时已对齐源码。");
}

// ── uninstall ───────────────────────────────────────────────────────────────

function uninstallCmd(): void {
Expand Down Expand Up @@ -660,6 +708,9 @@ switch (cmd) {
case "install":
installCmd();
break;
case "sync":
syncCmd();
break;
case "uninstall":
uninstallCmd();
break;
Expand All @@ -676,6 +727,7 @@ USAGE:

COMMANDS:
install 一键部署到 ~/.forge-hub/ + ~/.claude/channels/hub/ + launchd + MCP 注册
sync 仅同步 hub-server 运行时文件(git pull 后跑这个,不重装 deps/dashboard)
uninstall 反向操作(保留 state)
doctor 诊断 install 状态 + connectivity
--help 显示此帮助
Expand Down
2 changes: 1 addition & 1 deletion hub-server/channels/feishu.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ async function handleMessage(event: Record<string, unknown>): Promise<void> {
hub.log(`← ${displayName}: ${content.slice(0, 80)}${content.length > 80 ? "..." : ""}`);

// 群消息用 chat_id (oc_) 作为 fromId,这样 reply 会发到群里而不是私聊
const replyTo = chatId.startsWith("oc_") ? chatId : senderId;
const replyTo = isGroupMessage ? chatId : senderId;
hub.pushMessage({
channel: "feishu",
from: displayName,
Expand Down
1 change: 1 addition & 0 deletions 维护地图.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,4 @@ Claude Code permission_request
- 安全相关失败默认 fail closed;如果不能 fail closed,文档和日志必须讲清代价。
- 安装和运行时路径不要靠记忆,查 [部署.md](部署.md) 和 [运行时状态.md](运行时状态.md)。
- Preview / experimental 可以探索,但不要让默认安装路径变复杂。
- **源码更新后跑 `forge-hub sync` 对齐运行时**。git pull / 切换分支后 `~/.forge-hub/` 文件不会自动更新,源码与运行时版本不一致会导致通道插件报错。`forge-hub sync` 只同步 hub-server 源文件 + 重启 Hub,不重装 deps/dashboard/plist/MCP。
Loading