cc-linker 0.0.1 → 0.0.2
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/LICENSE +21 -0
- package/README.md +157 -117
- package/dist/cli.js +565 -427
- package/dist/cli.js.map +34 -33
- package/package.json +10 -8
package/dist/cli.js
CHANGED
|
@@ -18312,16 +18312,16 @@ var require_proper_lockfile = __commonJS((exports, module) => {
|
|
|
18312
18312
|
});
|
|
18313
18313
|
|
|
18314
18314
|
// src/utils/errors.ts
|
|
18315
|
-
var
|
|
18315
|
+
var CCLinkerError;
|
|
18316
18316
|
var init_errors3 = __esm(() => {
|
|
18317
|
-
|
|
18317
|
+
CCLinkerError = class CCLinkerError extends Error {
|
|
18318
18318
|
code;
|
|
18319
18319
|
details;
|
|
18320
18320
|
constructor(code, message, details) {
|
|
18321
18321
|
super(message);
|
|
18322
18322
|
this.code = code;
|
|
18323
18323
|
this.details = details;
|
|
18324
|
-
this.name = "
|
|
18324
|
+
this.name = "CCLinkerError";
|
|
18325
18325
|
}
|
|
18326
18326
|
toString() {
|
|
18327
18327
|
return `[${this.code}] ${this.message}`;
|
|
@@ -18410,13 +18410,13 @@ async function withLock(filePath, fn, options = {}) {
|
|
|
18410
18410
|
elapsed += minTimeout * (attempt + 1);
|
|
18411
18411
|
if (elapsed >= MAX_WAIT) {
|
|
18412
18412
|
releaseInner();
|
|
18413
|
-
throw new
|
|
18413
|
+
throw new CCLinkerError("E007", "注册表被锁,等待超时");
|
|
18414
18414
|
}
|
|
18415
18415
|
await sleep(minTimeout * (attempt + 1));
|
|
18416
18416
|
}
|
|
18417
18417
|
}
|
|
18418
18418
|
releaseInner();
|
|
18419
|
-
throw new
|
|
18419
|
+
throw new CCLinkerError("E007", "注册表被锁,等待超时");
|
|
18420
18420
|
}
|
|
18421
18421
|
async function withReadLock(fn) {
|
|
18422
18422
|
const releaseRead = await rwLock.acquireRead();
|
|
@@ -18449,22 +18449,22 @@ function expandPath(p) {
|
|
|
18449
18449
|
return join(process.env.HOME ?? "", p.slice(2));
|
|
18450
18450
|
return p;
|
|
18451
18451
|
}
|
|
18452
|
-
var HOME,
|
|
18452
|
+
var HOME, CC_LINKER_DIR, REGISTRY_PATH, BACKUP_DIR, SCAN_CACHE_PATH, HOOK_LOG_PATH, CONFIG_PATH, USER_MAPPING_PATH, LIST_SNAPSHOT_PATH, RUNTIME_OWNER_LOCK_PATH, RUNTIME_SESSION_EVENTS_DIR, RUNTIME_PID_FILE, RUNTIME_LOG_FILE, SPOOL_DIR, SPOOL_PENDING_DIR, SPOOL_PROCESSING_DIR, SPOOL_REPLIED_DIR, SPOOL_DONE_DIR, SPOOL_FAILED_DIR, SPOOL_RECEIPTS_DIR, SPOOL_DELIVERIES_DIR, CLAUDE_PROJECTS_DIR, CLAUDE_SETTINGS_PATH;
|
|
18453
18453
|
var init_paths = __esm(() => {
|
|
18454
18454
|
HOME = getHome();
|
|
18455
|
-
|
|
18456
|
-
REGISTRY_PATH = process.env.
|
|
18457
|
-
BACKUP_DIR = join(
|
|
18458
|
-
SCAN_CACHE_PATH = join(
|
|
18459
|
-
HOOK_LOG_PATH = join(
|
|
18460
|
-
CONFIG_PATH = process.env.
|
|
18461
|
-
USER_MAPPING_PATH = join(
|
|
18462
|
-
LIST_SNAPSHOT_PATH = join(
|
|
18463
|
-
RUNTIME_OWNER_LOCK_PATH = join(
|
|
18464
|
-
RUNTIME_SESSION_EVENTS_DIR = join(
|
|
18465
|
-
RUNTIME_PID_FILE = join(
|
|
18466
|
-
RUNTIME_LOG_FILE = join(
|
|
18467
|
-
SPOOL_DIR = join(
|
|
18455
|
+
CC_LINKER_DIR = process.env.CC_LINKER_DIR ?? join(HOME, ".cc-linker");
|
|
18456
|
+
REGISTRY_PATH = process.env.CC_LINKER_REGISTRY_PATH ?? join(CC_LINKER_DIR, "registry.json");
|
|
18457
|
+
BACKUP_DIR = join(CC_LINKER_DIR, "backups");
|
|
18458
|
+
SCAN_CACHE_PATH = join(CC_LINKER_DIR, "scan_cache.json");
|
|
18459
|
+
HOOK_LOG_PATH = join(CC_LINKER_DIR, "hook.log");
|
|
18460
|
+
CONFIG_PATH = process.env.CC_LINKER_CONFIG_PATH ?? join(CC_LINKER_DIR, "config.toml");
|
|
18461
|
+
USER_MAPPING_PATH = join(CC_LINKER_DIR, "user-mapping.json");
|
|
18462
|
+
LIST_SNAPSHOT_PATH = join(CC_LINKER_DIR, "list-snapshot.json");
|
|
18463
|
+
RUNTIME_OWNER_LOCK_PATH = join(CC_LINKER_DIR, "owner.lock");
|
|
18464
|
+
RUNTIME_SESSION_EVENTS_DIR = join(CC_LINKER_DIR, "session-events");
|
|
18465
|
+
RUNTIME_PID_FILE = join(CC_LINKER_DIR, "cc-linker.pid");
|
|
18466
|
+
RUNTIME_LOG_FILE = join(CC_LINKER_DIR, "cc-linker.log");
|
|
18467
|
+
SPOOL_DIR = join(CC_LINKER_DIR, "spool");
|
|
18468
18468
|
SPOOL_PENDING_DIR = join(SPOOL_DIR, "pending");
|
|
18469
18469
|
SPOOL_PROCESSING_DIR = join(SPOOL_DIR, "processing");
|
|
18470
18470
|
SPOOL_REPLIED_DIR = join(SPOOL_DIR, "replied");
|
|
@@ -20600,22 +20600,22 @@ class ConfigManager {
|
|
|
20600
20600
|
}
|
|
20601
20601
|
loadEnv() {
|
|
20602
20602
|
const mappings = [
|
|
20603
|
-
["
|
|
20604
|
-
["
|
|
20605
|
-
["
|
|
20606
|
-
["
|
|
20607
|
-
["
|
|
20608
|
-
["
|
|
20609
|
-
["
|
|
20610
|
-
["
|
|
20611
|
-
["
|
|
20612
|
-
["
|
|
20613
|
-
["
|
|
20614
|
-
["
|
|
20615
|
-
["
|
|
20616
|
-
["
|
|
20617
|
-
["
|
|
20618
|
-
["
|
|
20603
|
+
["CC_LINKER_REGISTRY_PATH", "general", "registry_path"],
|
|
20604
|
+
["CC_LINKER_LOG_LEVEL", "general", "log_level"],
|
|
20605
|
+
["CC_LINKER_LOG_PATH", "general", "log_path"],
|
|
20606
|
+
["CC_LINKER_FEISHU_APP_ID", "feishu_bot", "app_id"],
|
|
20607
|
+
["CC_LINKER_FEISHU_APP_SECRET", "feishu_bot", "app_secret"],
|
|
20608
|
+
["CC_LINKER_FEISHU_OWNER_OPEN_ID", "feishu_bot", "owner_open_id"],
|
|
20609
|
+
["CC_LINKER_FEISHU_DEFAULT_CWD", "feishu_bot", "default_cwd"],
|
|
20610
|
+
["CC_LINKER_MAX_CONCURRENT_SESSIONS", "runtime", "max_concurrent_sessions"],
|
|
20611
|
+
["CC_LINKER_SESSION_LOCK_TIMEOUT_MS", "runtime", "session_lock_timeout_ms"],
|
|
20612
|
+
["CC_LINKER_MAX_QUEUE_SIZE", "queue", "max_pending"],
|
|
20613
|
+
["CC_LINKER_CONFIRM_RISKY_ACTIONS", "security", "confirm_risky_actions"],
|
|
20614
|
+
["CC_LINKER_STREAM_ENABLED", "stream", "enabled"],
|
|
20615
|
+
["CC_LINKER_STREAM_THROTTLE_MS", "stream", "throttle_ms"],
|
|
20616
|
+
["CC_LINKER_STREAM_SHOW_THINKING", "stream", "show_thinking"],
|
|
20617
|
+
["CC_LINKER_STREAM_MAX_CARD_BYTES", "stream", "max_card_bytes"],
|
|
20618
|
+
["CC_LINKER_STREAM_FALLBACK_TO_TEXT", "stream", "fallback_to_text"]
|
|
20619
20619
|
];
|
|
20620
20620
|
for (const [envKey, section, key] of mappings) {
|
|
20621
20621
|
const value = process.env[envKey];
|
|
@@ -20630,7 +20630,7 @@ class ConfigManager {
|
|
|
20630
20630
|
}
|
|
20631
20631
|
}
|
|
20632
20632
|
}
|
|
20633
|
-
const legacyOwner = process.env.
|
|
20633
|
+
const legacyOwner = process.env.CC_LINKER_FEISHU_OWNER_USER_ID;
|
|
20634
20634
|
if (legacyOwner && !this.data.feishu_bot.owner_open_id) {
|
|
20635
20635
|
this.data.feishu_bot.owner_open_id = legacyOwner;
|
|
20636
20636
|
}
|
|
@@ -20695,7 +20695,7 @@ var init_config = __esm(() => {
|
|
|
20695
20695
|
enabled: false
|
|
20696
20696
|
},
|
|
20697
20697
|
hook: {
|
|
20698
|
-
log_path: "~/.cc-
|
|
20698
|
+
log_path: "~/.cc-linker/hook.log",
|
|
20699
20699
|
timeout: 10
|
|
20700
20700
|
},
|
|
20701
20701
|
stream: {
|
|
@@ -20758,7 +20758,7 @@ class RegistryManager {
|
|
|
20758
20758
|
this.basePath = basePath;
|
|
20759
20759
|
this.registryPath = join2(this.basePath, "registry.json");
|
|
20760
20760
|
} else {
|
|
20761
|
-
const configuredPath = this.expandPath(config2.get("general.registry_path", join2(
|
|
20761
|
+
const configuredPath = this.expandPath(config2.get("general.registry_path", join2(CC_LINKER_DIR, "registry.json")));
|
|
20762
20762
|
this.registryPath = configuredPath;
|
|
20763
20763
|
this.basePath = dirname2(configuredPath);
|
|
20764
20764
|
}
|
|
@@ -21892,7 +21892,7 @@ class StateCoordinator2 {
|
|
|
21892
21892
|
}
|
|
21893
21893
|
static assertNotRunning(lockPath) {
|
|
21894
21894
|
if (StateCoordinator2.isLocked(lockPath)) {
|
|
21895
|
-
throw new
|
|
21895
|
+
throw new CCLinkerError("E013", "Bot 进程正在运行,请使用飞书命令操作会话,而非直接 CLI 操作");
|
|
21896
21896
|
}
|
|
21897
21897
|
}
|
|
21898
21898
|
isHeld() {
|
|
@@ -46541,7 +46541,7 @@ async function fileExists(path3) {
|
|
|
46541
46541
|
class ProviderManager {
|
|
46542
46542
|
providers = new Map;
|
|
46543
46543
|
source = "none";
|
|
46544
|
-
autoProviderDir = join11(
|
|
46544
|
+
autoProviderDir = join11(CC_LINKER_DIR, "auto-providers");
|
|
46545
46545
|
getSource() {
|
|
46546
46546
|
return this.source;
|
|
46547
46547
|
}
|
|
@@ -46591,7 +46591,7 @@ class ProviderManager {
|
|
|
46591
46591
|
const rawAlias = basename2(file2, ".json");
|
|
46592
46592
|
try {
|
|
46593
46593
|
const content = JSON.parse(readFileSync13(path3, "utf8"));
|
|
46594
|
-
const baseAlias = content.alias ? this.sanitizeName(content.alias) : this.generateShortAlias(rawAlias);
|
|
46594
|
+
const baseAlias = content.alias ? this.sanitizeName(content.alias) : isTemp ? rawAlias : this.generateShortAlias(rawAlias);
|
|
46595
46595
|
const alias = this.resolveAliasConflict(baseAlias);
|
|
46596
46596
|
const name = content.name ?? rawAlias;
|
|
46597
46597
|
if (this.providers.has(alias)) {
|
|
@@ -46622,17 +46622,18 @@ class ProviderManager {
|
|
|
46622
46622
|
try {
|
|
46623
46623
|
const cleanConfig = this.sanitizeSettingsConfig(row.settings_config);
|
|
46624
46624
|
const baseAlias = cleanConfig.alias ? this.sanitizeName(cleanConfig.alias) : this.generateShortAlias(row.name);
|
|
46625
|
-
|
|
46626
|
-
|
|
46625
|
+
let alias = baseAlias;
|
|
46626
|
+
let filePath = join11(this.autoProviderDir, `${alias}.json`);
|
|
46627
|
+
let counter = 2;
|
|
46628
|
+
while (existsSync19(filePath)) {
|
|
46629
|
+
alias = `${baseAlias}-${counter}`;
|
|
46630
|
+
filePath = join11(this.autoProviderDir, `${alias}.json`);
|
|
46631
|
+
counter++;
|
|
46632
|
+
}
|
|
46627
46633
|
const tmpPath = filePath + ".tmp";
|
|
46628
46634
|
const configWithName = { ...cleanConfig, name: row.name, alias: cleanConfig.alias };
|
|
46629
46635
|
writeFileSync12(tmpPath, JSON.stringify(configWithName, null, 2), { mode: 384 });
|
|
46630
46636
|
renameSync6(tmpPath, filePath);
|
|
46631
|
-
if (this.providers.has(alias)) {
|
|
46632
|
-
const existing = this.providers.get(alias);
|
|
46633
|
-
logger.warn(`Provider alias conflict in CC Switch: "${alias}" (from "${row.name}") overrides existing from ${existing.path}`);
|
|
46634
|
-
}
|
|
46635
|
-
this.providers.set(alias, { alias, name: row.name, path: filePath, isTemp: true });
|
|
46636
46637
|
} catch (err) {
|
|
46637
46638
|
logger.warn(`Skipping CC Switch provider "${row.name}": ${err}`);
|
|
46638
46639
|
}
|
|
@@ -46784,7 +46785,8 @@ class FeishuBot {
|
|
|
46784
46785
|
await this.replyFn(`消息过长(${text.length} 字符),请控制在 ${MAX_MESSAGE_LENGTH} 字符以内,或将内容分段发送。`, { messageId: event.message_id, openId: event.open_id, requestUuid: stableUuid(event.message_id) });
|
|
46785
46786
|
return;
|
|
46786
46787
|
}
|
|
46787
|
-
const
|
|
46788
|
+
const isCommand = text.startsWith("/") && text.length > 1 && text[1] !== " ";
|
|
46789
|
+
const target = isCommand ? { type: "no_target", openId: event.open_id, mappingVersion: this.userManager.getVersion() } : await this.resolveChatTarget(event.open_id, event.message_id);
|
|
46788
46790
|
const serialKey = target.type === "session" && target.sessionUuid ? target.sessionUuid : `new:${event.open_id}`;
|
|
46789
46791
|
const spoolMsg = {
|
|
46790
46792
|
messageId: event.message_id,
|
|
@@ -46892,6 +46894,16 @@ class FeishuBot {
|
|
|
46892
46894
|
await this.replyFn(reply, { messageId, openId, requestUuid: uniqueUuid() });
|
|
46893
46895
|
return reply;
|
|
46894
46896
|
}
|
|
46897
|
+
case "select_model": {
|
|
46898
|
+
const reply = await this.doSelectModel(openId, sessionId, messageId);
|
|
46899
|
+
await this.replyFn(reply, { messageId, openId, requestUuid: uniqueUuid() });
|
|
46900
|
+
return reply;
|
|
46901
|
+
}
|
|
46902
|
+
case "clear_model": {
|
|
46903
|
+
const reply = await this.doClearModel(openId, messageId);
|
|
46904
|
+
await this.replyFn(reply, { messageId, openId, requestUuid: uniqueUuid() });
|
|
46905
|
+
return reply;
|
|
46906
|
+
}
|
|
46895
46907
|
case "status": {
|
|
46896
46908
|
const reply = await this.doStatus(openId);
|
|
46897
46909
|
await this.replyFn(reply, { messageId, openId, requestUuid: uniqueUuid() });
|
|
@@ -46922,7 +46934,7 @@ class FeishuBot {
|
|
|
46922
46934
|
try {
|
|
46923
46935
|
if (msg.responseText) {
|
|
46924
46936
|
await this.replyAndFinalize(msg, msg.responseText);
|
|
46925
|
-
} else if (msg.text.startsWith("/
|
|
46937
|
+
} else if (msg.text.startsWith("/") && msg.text.length > 1 && msg.text[1] !== " ") {
|
|
46926
46938
|
await this.handleCommand(msg);
|
|
46927
46939
|
} else {
|
|
46928
46940
|
await this.handleChat(msg);
|
|
@@ -46946,7 +46958,7 @@ class FeishuBot {
|
|
|
46946
46958
|
}
|
|
46947
46959
|
async handleCommand(msg) {
|
|
46948
46960
|
const parts = msg.text.split(/\s+/);
|
|
46949
|
-
const cmd = parts[
|
|
46961
|
+
const cmd = parts[0]?.replace(/^\/+/, "")?.toLowerCase();
|
|
46950
46962
|
switch (cmd) {
|
|
46951
46963
|
case "help":
|
|
46952
46964
|
await this.replyAndFinalize(msg, this.helpText());
|
|
@@ -46955,16 +46967,16 @@ class FeishuBot {
|
|
|
46955
46967
|
await this.handleList(msg);
|
|
46956
46968
|
return;
|
|
46957
46969
|
case "new":
|
|
46958
|
-
await this.handleNew(msg, msg.text.replace(/^\/
|
|
46970
|
+
await this.handleNew(msg, msg.text.replace(/^\/new\b\s*/i, ""));
|
|
46959
46971
|
return;
|
|
46960
46972
|
case "switch":
|
|
46961
|
-
await this.handleSwitch(msg, parts.slice(
|
|
46973
|
+
await this.handleSwitch(msg, parts.slice(1).join(" "));
|
|
46962
46974
|
return;
|
|
46963
46975
|
case "model":
|
|
46964
|
-
await this.handleModel(msg, parts.slice(
|
|
46976
|
+
await this.handleModel(msg, parts.slice(1).join(" "));
|
|
46965
46977
|
return;
|
|
46966
46978
|
case "resume":
|
|
46967
|
-
await this.handleResume(msg, parts.slice(
|
|
46979
|
+
await this.handleResume(msg, parts.slice(1).join(" "));
|
|
46968
46980
|
return;
|
|
46969
46981
|
case "status":
|
|
46970
46982
|
await this.handleStatus(msg);
|
|
@@ -46975,7 +46987,7 @@ class FeishuBot {
|
|
|
46975
46987
|
将其填入 config.toml 的 feishu_bot.owner_open_id 可限制仅你本人使用。`);
|
|
46976
46988
|
return;
|
|
46977
46989
|
default:
|
|
46978
|
-
await this.replyAndFinalize(msg, `未知命令:
|
|
46990
|
+
await this.replyAndFinalize(msg, `未知命令: /${cmd}
|
|
46979
46991
|
|
|
46980
46992
|
${this.helpText()}`);
|
|
46981
46993
|
return;
|
|
@@ -46998,11 +47010,11 @@ ${this.helpText()}`);
|
|
|
46998
47010
|
const claimMessageId = msg.target.claimMessageId ?? msg.messageId;
|
|
46999
47011
|
const claimResult = await this.userManager.claimPendingNewSession(msg.openId, claimMessageId);
|
|
47000
47012
|
if (claimResult.status === "creating") {
|
|
47001
|
-
await this.replyAndFinalize(msg, "新会话正在创建,请稍后重试,或执行 /
|
|
47013
|
+
await this.replyAndFinalize(msg, "新会话正在创建,请稍后重试,或执行 /list 查看是否已生成。");
|
|
47002
47014
|
return;
|
|
47003
47015
|
}
|
|
47004
47016
|
if (claimResult.status !== "claimed") {
|
|
47005
|
-
await this.replyAndFinalize(msg, "新会话创建入口已失效,请重新执行 /
|
|
47017
|
+
await this.replyAndFinalize(msg, "新会话创建入口已失效,请重新执行 /new。");
|
|
47006
47018
|
return;
|
|
47007
47019
|
}
|
|
47008
47020
|
if (config2.get("stream.enabled", false)) {
|
|
@@ -47013,16 +47025,16 @@ ${this.helpText()}`);
|
|
|
47013
47025
|
return;
|
|
47014
47026
|
}
|
|
47015
47027
|
case "new_session_creating":
|
|
47016
|
-
await this.replyAndFinalize(msg, "新会话正在创建,请稍后重试,或执行 /
|
|
47028
|
+
await this.replyAndFinalize(msg, "新会话正在创建,请稍后重试,或执行 /list 查看是否已生成。");
|
|
47017
47029
|
return;
|
|
47018
47030
|
case "no_target":
|
|
47019
47031
|
default:
|
|
47020
47032
|
await this.replyAndFinalize(msg, [
|
|
47021
47033
|
"当前没有活跃会话。",
|
|
47022
47034
|
"请先执行以下任一命令:",
|
|
47023
|
-
"1. /
|
|
47024
|
-
"2. /
|
|
47025
|
-
"3. /
|
|
47035
|
+
"1. /list",
|
|
47036
|
+
"2. /switch <ID>",
|
|
47037
|
+
"3. /new [cwd] [-- prompt]"
|
|
47026
47038
|
].join(`
|
|
47027
47039
|
`));
|
|
47028
47040
|
return;
|
|
@@ -47271,7 +47283,7 @@ ${this.helpText()}`);
|
|
|
47271
47283
|
const provider = this.providerManager.resolve(providerAlias);
|
|
47272
47284
|
if (!provider) {
|
|
47273
47285
|
await this.replyAndFinalize(msg, `未知模型: "${providerAlias}"
|
|
47274
|
-
请使用 /
|
|
47286
|
+
请使用 /model 查看可用列表`);
|
|
47275
47287
|
return;
|
|
47276
47288
|
}
|
|
47277
47289
|
const entry = this.userManager.getEntry(msg.openId);
|
|
@@ -47284,7 +47296,7 @@ ${this.helpText()}`);
|
|
|
47284
47296
|
await this.userManager.compareAndSwap(msg.openId, entry ?? null, newEntry);
|
|
47285
47297
|
}
|
|
47286
47298
|
if (!cwd) {
|
|
47287
|
-
await this.replyAndFinalize(msg, "请使用 /
|
|
47299
|
+
await this.replyAndFinalize(msg, "请使用 /new <cwd>,或在配置里设置 feishu_bot.default_cwd。");
|
|
47288
47300
|
return;
|
|
47289
47301
|
}
|
|
47290
47302
|
const validationError = validateCwd(cwd);
|
|
@@ -47335,26 +47347,26 @@ ${this.helpText()}`);
|
|
|
47335
47347
|
}
|
|
47336
47348
|
async handleSwitch(msg, target) {
|
|
47337
47349
|
if (!target) {
|
|
47338
|
-
await this.replyAndFinalize(msg, "用法: /
|
|
47350
|
+
await this.replyAndFinalize(msg, "用法: /switch <序号或 UUID>");
|
|
47339
47351
|
return;
|
|
47340
47352
|
}
|
|
47341
47353
|
const index = parseInt(target, 10);
|
|
47342
47354
|
const uuid3 = Number.isNaN(index) ? this.registry.findByPrefix(target)?.[0] ?? null : this.listSnapshotManager.resolveIndex(index, msg.openId);
|
|
47343
47355
|
if (!uuid3) {
|
|
47344
|
-
await this.replyAndFinalize(msg, `未找到 "${target}" 对应的会话(或匹配到多个),请先执行 /
|
|
47356
|
+
await this.replyAndFinalize(msg, `未找到 "${target}" 对应的会话(或匹配到多个),请先执行 /list 查看完整列表。`);
|
|
47345
47357
|
return;
|
|
47346
47358
|
}
|
|
47347
47359
|
await this.doSwitch(msg.openId, uuid3, msg.messageId, msg);
|
|
47348
47360
|
}
|
|
47349
47361
|
async handleResume(msg, target) {
|
|
47350
47362
|
if (!target) {
|
|
47351
|
-
await this.replyAndFinalize(msg, "用法: /
|
|
47363
|
+
await this.replyAndFinalize(msg, "用法: /resume <序号或 UUID>");
|
|
47352
47364
|
return;
|
|
47353
47365
|
}
|
|
47354
47366
|
const index = parseInt(target, 10);
|
|
47355
47367
|
const uuid3 = Number.isNaN(index) ? this.registry.findByPrefix(target)?.[0] ?? null : this.listSnapshotManager.resolveIndex(index, msg.openId);
|
|
47356
47368
|
if (!uuid3) {
|
|
47357
|
-
await this.replyAndFinalize(msg, `未找到 "${target}" 对应的会话(或匹配到多个),请先执行 /
|
|
47369
|
+
await this.replyAndFinalize(msg, `未找到 "${target}" 对应的会话(或匹配到多个),请先执行 /list 查看完整列表。`);
|
|
47358
47370
|
return;
|
|
47359
47371
|
}
|
|
47360
47372
|
await this.doResumeReply(msg.openId, uuid3, msg);
|
|
@@ -47368,33 +47380,46 @@ ${this.helpText()}`);
|
|
|
47368
47380
|
if (!target) {
|
|
47369
47381
|
const entry2 = this.userManager.getEntry(msg.openId);
|
|
47370
47382
|
const currentAlias = entry2?.defaultProvider ?? null;
|
|
47371
|
-
const lines = [];
|
|
47372
|
-
if (currentAlias) {
|
|
47373
|
-
const provider2 = this.providerManager.resolve(currentAlias);
|
|
47374
|
-
lines.push(`当前默认模型: ${provider2?.name ?? currentAlias}`);
|
|
47375
|
-
} else {
|
|
47376
|
-
lines.push("当前默认模型: 未设置(跟随 Claude 全局配置)");
|
|
47377
|
-
}
|
|
47378
47383
|
const providers = this.providerManager.list();
|
|
47379
|
-
if (providers.length
|
|
47384
|
+
if (providers.length === 0) {
|
|
47385
|
+
const lines = [
|
|
47386
|
+
"当前默认模型: 未设置(跟随 Claude 全局配置)",
|
|
47387
|
+
"",
|
|
47388
|
+
"未检测到可切换模型。",
|
|
47389
|
+
"请安装 CC Switch 或手动创建 ~/.claude/providers/*.json"
|
|
47390
|
+
];
|
|
47391
|
+
await this.replyAndFinalize(msg, lines.join(`
|
|
47392
|
+
`));
|
|
47393
|
+
return;
|
|
47394
|
+
}
|
|
47395
|
+
const card = buildModelCard(providers, currentAlias);
|
|
47396
|
+
const replyId = await this.cardReplyFn(card, { messageId: msg.messageId, openId: msg.openId });
|
|
47397
|
+
if (replyId) {
|
|
47398
|
+
this.spoolQueue.recordDelivery(msg.messageId, "sent", stableUuid(msg.messageId, 0), 0, replyId, 1);
|
|
47399
|
+
this.spoolQueue.markReplied(msg.messageId, msg.serialKey, replyId);
|
|
47400
|
+
this.spoolQueue.markDone(msg.messageId, msg.serialKey, replyId);
|
|
47401
|
+
} else {
|
|
47402
|
+
const lines = [];
|
|
47403
|
+
if (currentAlias) {
|
|
47404
|
+
const provider2 = this.providerManager.resolve(currentAlias);
|
|
47405
|
+
lines.push(`当前默认模型: ${provider2?.name ?? currentAlias}`);
|
|
47406
|
+
} else {
|
|
47407
|
+
lines.push("当前默认模型: 未设置(跟随 Claude 全局配置)");
|
|
47408
|
+
}
|
|
47380
47409
|
lines.push("");
|
|
47381
47410
|
lines.push("可用模型:");
|
|
47382
47411
|
providers.forEach((p, i) => {
|
|
47383
47412
|
const marker = p.alias === currentAlias ? "●" : " ";
|
|
47384
47413
|
lines.push(` ${marker} ${i + 1}. ${p.name} (${p.alias})`);
|
|
47385
47414
|
});
|
|
47386
|
-
} else {
|
|
47387
47415
|
lines.push("");
|
|
47388
|
-
lines.push("
|
|
47389
|
-
lines.push("
|
|
47390
|
-
|
|
47391
|
-
|
|
47392
|
-
|
|
47393
|
-
lines.push(" /bridge model <序号|别名> 设置默认模型");
|
|
47394
|
-
lines.push(" /bridge model --clear 清除默认设置");
|
|
47395
|
-
lines.push(" /bridge new /path --model <别名> 创建会话时指定模型");
|
|
47396
|
-
await this.replyAndFinalize(msg, lines.join(`
|
|
47416
|
+
lines.push("用法:");
|
|
47417
|
+
lines.push(" /model <序号|别名> 设置默认模型");
|
|
47418
|
+
lines.push(" /model --clear 清除默认设置");
|
|
47419
|
+
lines.push(" /new /path --model <别名> 创建会话时指定模型");
|
|
47420
|
+
await this.replyAndFinalize(msg, lines.join(`
|
|
47397
47421
|
`));
|
|
47422
|
+
}
|
|
47398
47423
|
return;
|
|
47399
47424
|
}
|
|
47400
47425
|
if (target === "--clear") {
|
|
@@ -47410,7 +47435,7 @@ ${this.helpText()}`);
|
|
|
47410
47435
|
const provider = this.providerManager.resolve(target);
|
|
47411
47436
|
if (!provider) {
|
|
47412
47437
|
await this.replyAndFinalize(msg, `未知模型: "${target}"
|
|
47413
|
-
请使用 /
|
|
47438
|
+
请使用 /model 查看可用列表`);
|
|
47414
47439
|
return;
|
|
47415
47440
|
}
|
|
47416
47441
|
const entry = this.userManager.getEntry(msg.openId);
|
|
@@ -47426,17 +47451,17 @@ ${this.helpText()}`);
|
|
|
47426
47451
|
helpText() {
|
|
47427
47452
|
return [
|
|
47428
47453
|
"可用命令:",
|
|
47429
|
-
" /
|
|
47430
|
-
" /
|
|
47431
|
-
" /
|
|
47432
|
-
" /
|
|
47433
|
-
" /
|
|
47434
|
-
" /
|
|
47435
|
-
" /
|
|
47436
|
-
" /
|
|
47437
|
-
" /
|
|
47438
|
-
" /
|
|
47439
|
-
" /
|
|
47454
|
+
" /help - 显示此帮助",
|
|
47455
|
+
" /list - 列出会话",
|
|
47456
|
+
" /new [路径] [-- prompt] - 创建新会话",
|
|
47457
|
+
" /new [路径] --model <别名> [-- p] - 指定模型创建会话",
|
|
47458
|
+
" /switch <序号|UUID> - 切换会话",
|
|
47459
|
+
" /model - 查看可用模型和默认设置",
|
|
47460
|
+
" /model <序号|别名> - 设置默认模型",
|
|
47461
|
+
" /model --clear - 清除默认设置",
|
|
47462
|
+
" /resume <序号|UUID> - 获取安全恢复建议",
|
|
47463
|
+
" /status - 查看状态",
|
|
47464
|
+
" /whoami - 获取你的 open_id"
|
|
47440
47465
|
].join(`
|
|
47441
47466
|
`);
|
|
47442
47467
|
}
|
|
@@ -47626,7 +47651,7 @@ ${this.helpText()}`);
|
|
|
47626
47651
|
async doSwitch(openId, uuid3, messageId, msg) {
|
|
47627
47652
|
const session = this.registry.get(uuid3);
|
|
47628
47653
|
if (!session) {
|
|
47629
|
-
const reply2 = "未找到对应会话,请先执行 /
|
|
47654
|
+
const reply2 = "未找到对应会话,请先执行 /list。";
|
|
47630
47655
|
if (msg)
|
|
47631
47656
|
await this.replyAndFinalize(msg, reply2);
|
|
47632
47657
|
else
|
|
@@ -47649,16 +47674,45 @@ ${this.helpText()}`);
|
|
|
47649
47674
|
this.spoolQueue.recordReceipt(messageId ?? "");
|
|
47650
47675
|
return reply;
|
|
47651
47676
|
}
|
|
47677
|
+
async doSelectModel(openId, alias, messageId) {
|
|
47678
|
+
const provider = this.providerManager.resolve(alias);
|
|
47679
|
+
if (!provider) {
|
|
47680
|
+
return `未知模型: "${alias}"
|
|
47681
|
+
请使用 /model 查看可用列表`;
|
|
47682
|
+
}
|
|
47683
|
+
const entry = this.userManager.getEntry(openId);
|
|
47684
|
+
const newEntry = entry ? { ...entry, defaultProvider: provider.alias } : {
|
|
47685
|
+
type: "pending_new_session",
|
|
47686
|
+
sessionUuid: null,
|
|
47687
|
+
createdAt: new Date().toISOString(),
|
|
47688
|
+
defaultProvider: provider.alias
|
|
47689
|
+
};
|
|
47690
|
+
const swapped = await this.userManager.compareAndSwap(openId, entry ?? null, newEntry);
|
|
47691
|
+
if (!swapped)
|
|
47692
|
+
return "⚠️ 设置失败,请重试";
|
|
47693
|
+
this.spoolQueue.recordReceipt(messageId ?? "");
|
|
47694
|
+
return `✅ 默认模型已设置为 ${provider.name} (${provider.alias})`;
|
|
47695
|
+
}
|
|
47696
|
+
async doClearModel(openId, messageId) {
|
|
47697
|
+
const entry = this.userManager.getEntry(openId);
|
|
47698
|
+
if (!entry) {
|
|
47699
|
+
this.spoolQueue.recordReceipt(messageId ?? "");
|
|
47700
|
+
return "⚠️ 无当前会话状态,无需清除";
|
|
47701
|
+
}
|
|
47702
|
+
const swapped = await this.userManager.compareAndSwap(openId, entry, { ...entry, defaultProvider: undefined });
|
|
47703
|
+
this.spoolQueue.recordReceipt(messageId ?? "");
|
|
47704
|
+
return swapped ? "✅ 已清除默认模型设置" : "⚠️ 清除失败,请重试";
|
|
47705
|
+
}
|
|
47652
47706
|
async doResume(openId, uuid3, messageId) {
|
|
47653
47707
|
const entry = this.registry.get(uuid3);
|
|
47654
47708
|
if (!entry)
|
|
47655
|
-
return "未找到对应会话,请先执行 /
|
|
47709
|
+
return "未找到对应会话,请先执行 /list。";
|
|
47656
47710
|
if (entry.status === "corrupted")
|
|
47657
47711
|
return `会话 ${uuid3.slice(0, 8)} 已损坏,不能直接恢复。`;
|
|
47658
47712
|
if (entry.status === "provisioning" || entry.status === "degraded") {
|
|
47659
|
-
return `会话 ${uuid3.slice(0, 8)} 状态为 ${entry.status},建议先保持 cc-
|
|
47713
|
+
return `会话 ${uuid3.slice(0, 8)} 状态为 ${entry.status},建议先保持 cc-linker 运行让系统自动修复。`;
|
|
47660
47714
|
}
|
|
47661
|
-
return `在终端执行: cc-
|
|
47715
|
+
return `在终端执行: cc-linker resume ${uuid3.slice(0, 8)}`;
|
|
47662
47716
|
}
|
|
47663
47717
|
async doResumeReply(openId, uuid3, msg) {
|
|
47664
47718
|
const reply = await this.doResume(openId, uuid3);
|
|
@@ -47670,7 +47724,7 @@ ${this.helpText()}`);
|
|
|
47670
47724
|
const sessions = Object.values(this.registry.sessions);
|
|
47671
47725
|
const provider = entry?.defaultProvider ? this.providerManager.resolve(entry.defaultProvider) : null;
|
|
47672
47726
|
return [
|
|
47673
|
-
"cc-
|
|
47727
|
+
"cc-linker 状态",
|
|
47674
47728
|
"─".repeat(30),
|
|
47675
47729
|
`队列消息: ${queueSize}`,
|
|
47676
47730
|
`总会话数: ${sessions.length}`,
|
|
@@ -47718,6 +47772,58 @@ ID: \`${uuid3.slice(0, 8)}\` | ${entry.message_count}条 | ${formatTimeAgo(entry
|
|
|
47718
47772
|
header: { title: { tag: "plain_text", content: `\uD83D\uDCCB 我的会话(${sessions.length}/${total})` }, template: "blue" }
|
|
47719
47773
|
};
|
|
47720
47774
|
}
|
|
47775
|
+
function buildModelCard(providers, currentAlias) {
|
|
47776
|
+
const elements = [];
|
|
47777
|
+
if (currentAlias) {
|
|
47778
|
+
const current = providers.find((p) => p.alias === currentAlias);
|
|
47779
|
+
elements.push({
|
|
47780
|
+
tag: "markdown",
|
|
47781
|
+
content: `**当前默认:** ${current?.name ?? currentAlias} (\`${currentAlias}\`)`
|
|
47782
|
+
});
|
|
47783
|
+
elements.push({ tag: "hr" });
|
|
47784
|
+
}
|
|
47785
|
+
for (let i = 0;i < providers.length; i++) {
|
|
47786
|
+
const p = providers[i];
|
|
47787
|
+
const isCurrent = p.alias === currentAlias;
|
|
47788
|
+
elements.push({
|
|
47789
|
+
tag: "markdown",
|
|
47790
|
+
content: `${i + 1}. **${p.name}** \`${p.alias}\`${isCurrent ? " ✅" : ""}`
|
|
47791
|
+
});
|
|
47792
|
+
elements.push({
|
|
47793
|
+
tag: "action",
|
|
47794
|
+
actions: [
|
|
47795
|
+
{
|
|
47796
|
+
tag: "button",
|
|
47797
|
+
text: { tag: "plain_text", content: isCurrent ? "✅ 已选择" : "\uD83C\uDFAF 选择" },
|
|
47798
|
+
type: isCurrent ? "default" : "primary",
|
|
47799
|
+
value: { tag: "select_model", sessionId: p.alias }
|
|
47800
|
+
}
|
|
47801
|
+
]
|
|
47802
|
+
});
|
|
47803
|
+
if (i < providers.length - 1) {
|
|
47804
|
+
elements.push({ tag: "hr" });
|
|
47805
|
+
}
|
|
47806
|
+
}
|
|
47807
|
+
if (currentAlias) {
|
|
47808
|
+
elements.push({ tag: "hr" });
|
|
47809
|
+
elements.push({
|
|
47810
|
+
tag: "action",
|
|
47811
|
+
actions: [
|
|
47812
|
+
{
|
|
47813
|
+
tag: "button",
|
|
47814
|
+
text: { tag: "plain_text", content: "\uD83E\uDDF9 清除默认" },
|
|
47815
|
+
type: "danger",
|
|
47816
|
+
value: { tag: "clear_model", sessionId: "" }
|
|
47817
|
+
}
|
|
47818
|
+
]
|
|
47819
|
+
});
|
|
47820
|
+
}
|
|
47821
|
+
return {
|
|
47822
|
+
config: { wide_screen_mode: true },
|
|
47823
|
+
elements,
|
|
47824
|
+
header: { title: { tag: "plain_text", content: "\uD83E\uDD16 模型选择" }, template: "blue" }
|
|
47825
|
+
};
|
|
47826
|
+
}
|
|
47721
47827
|
function preview(text, maxLength = 80) {
|
|
47722
47828
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
47723
47829
|
return normalized.length > maxLength ? `${normalized.slice(0, maxLength - 3)}...` : normalized;
|
|
@@ -69724,7 +69830,7 @@ var require_index_minimal = __commonJS((exports) => {
|
|
|
69724
69830
|
|
|
69725
69831
|
// node_modules/@larksuiteoapi/node-sdk/lib/index.js
|
|
69726
69832
|
var require_lib5 = __commonJS((exports) => {
|
|
69727
|
-
var __dirname = "/Users/wuyujun/Git/cc-
|
|
69833
|
+
var __dirname = "/Users/wuyujun/Git/cc-linker/node_modules/@larksuiteoapi/node-sdk/lib";
|
|
69728
69834
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
69729
69835
|
var axios = require_axios();
|
|
69730
69836
|
var fs = __require("fs");
|
|
@@ -129031,7 +129137,7 @@ import { dirname as dirname6, join as join15 } from "path";
|
|
|
129031
129137
|
import { homedir as homedir5 } from "os";
|
|
129032
129138
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
129033
129139
|
async function start2(registry2, opts = {}) {
|
|
129034
|
-
if (process.env.
|
|
129140
|
+
if (process.env.CC_LINKER_DAEMON === "1") {
|
|
129035
129141
|
await startDaemonChild2(registry2, opts);
|
|
129036
129142
|
return;
|
|
129037
129143
|
}
|
|
@@ -129039,7 +129145,7 @@ async function start2(registry2, opts = {}) {
|
|
|
129039
129145
|
if (isRunning2()) {
|
|
129040
129146
|
const pid = readPid2();
|
|
129041
129147
|
console.log(source_default.yellow(`⚠️ Bot 已在后台运行 (PID: ${pid})`));
|
|
129042
|
-
console.log(source_default.cyan(` 停止: cc-
|
|
129148
|
+
console.log(source_default.cyan(` 停止: cc-linker stop`));
|
|
129043
129149
|
return;
|
|
129044
129150
|
}
|
|
129045
129151
|
await startDaemon2();
|
|
@@ -129047,7 +129153,7 @@ async function start2(registry2, opts = {}) {
|
|
|
129047
129153
|
}
|
|
129048
129154
|
const sc = new StateCoordinator2;
|
|
129049
129155
|
if (StateCoordinator2.isLocked()) {
|
|
129050
|
-
console.log(source_default.red("❌ Bot 进程正在运行,请先执行 cc-
|
|
129156
|
+
console.log(source_default.red("❌ Bot 进程正在运行,请先执行 cc-linker stop"));
|
|
129051
129157
|
process.exit(1);
|
|
129052
129158
|
}
|
|
129053
129159
|
await startForeground2(registry2, opts);
|
|
@@ -129068,7 +129174,7 @@ function readPid2() {
|
|
|
129068
129174
|
}
|
|
129069
129175
|
async function stop2() {
|
|
129070
129176
|
let stopped = false;
|
|
129071
|
-
const plistPath = join15(homedir5(), "Library", "LaunchAgents", "com.
|
|
129177
|
+
const plistPath = join15(homedir5(), "Library", "LaunchAgents", "com.cclinker.daemon.plist");
|
|
129072
129178
|
if (existsSync24(plistPath)) {
|
|
129073
129179
|
try {
|
|
129074
129180
|
spawnSync3("launchctl", ["unload", plistPath]);
|
|
@@ -129109,7 +129215,7 @@ async function stop2() {
|
|
|
129109
129215
|
}
|
|
129110
129216
|
try {
|
|
129111
129217
|
const { execSync } = await import("child_process");
|
|
129112
|
-
const pids = execSync("pgrep -f 'cc-
|
|
129218
|
+
const pids = execSync("pgrep -f 'cc-linker.*daemon' 2>/dev/null || true", { encoding: "utf8" }).trim().split(`
|
|
129113
129219
|
`).filter(Boolean);
|
|
129114
129220
|
for (const pidStr of pids) {
|
|
129115
129221
|
const p = parseInt(pidStr, 10);
|
|
@@ -129134,7 +129240,7 @@ async function daemonStatus() {
|
|
|
129134
129240
|
const pid = readPid2();
|
|
129135
129241
|
console.log(source_default.green(`✅ Bot 正在运行 (PID: ${pid})`));
|
|
129136
129242
|
console.log(source_default.gray(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
129137
|
-
console.log(source_default.gray(` 停止: cc-
|
|
129243
|
+
console.log(source_default.gray(` 停止: cc-linker stop`));
|
|
129138
129244
|
if (existsSync24(RUNTIME_LOG_FILE)) {
|
|
129139
129245
|
const log = readFileSync17(RUNTIME_LOG_FILE, "utf8");
|
|
129140
129246
|
const lines = log.trim().split(`
|
|
@@ -129344,7 +129450,7 @@ async function createBotRuntime2(registry2, log, wsLogLevel) {
|
|
|
129344
129450
|
return { bot, wsClient, stateCoordinator: stateCoordinator3, shutdown };
|
|
129345
129451
|
}
|
|
129346
129452
|
async function startForeground2(registry2, opts) {
|
|
129347
|
-
console.log(source_default.blue("\uD83D\uDE80 启动 cc-
|
|
129453
|
+
console.log(source_default.blue("\uD83D\uDE80 启动 cc-linker..."));
|
|
129348
129454
|
const { bot, stateCoordinator: stateCoordinator3, shutdown } = await createBotRuntime2(registry2, (level, msg) => {
|
|
129349
129455
|
if (level === "ERROR") {
|
|
129350
129456
|
console.error(source_default.red(msg));
|
|
@@ -129359,7 +129465,7 @@ async function startForeground2(registry2, opts) {
|
|
|
129359
129465
|
logger.info(msg);
|
|
129360
129466
|
}
|
|
129361
129467
|
});
|
|
129362
|
-
console.log(source_default.green("✅ cc-
|
|
129468
|
+
console.log(source_default.green("✅ cc-linker 已启动"));
|
|
129363
129469
|
let shuttingDown = false;
|
|
129364
129470
|
const gracefulShutdown = async (signal) => {
|
|
129365
129471
|
if (shuttingDown)
|
|
@@ -129368,7 +129474,7 @@ async function startForeground2(registry2, opts) {
|
|
|
129368
129474
|
console.log(source_default.yellow(`
|
|
129369
129475
|
收到 ${signal},优雅停机中...`));
|
|
129370
129476
|
await shutdown(signal);
|
|
129371
|
-
logger.info("cc-
|
|
129477
|
+
logger.info("cc-linker 已停止");
|
|
129372
129478
|
process.exit(0);
|
|
129373
129479
|
};
|
|
129374
129480
|
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
@@ -129409,13 +129515,13 @@ async function startDaemonChild2(registry2, opts) {
|
|
|
129409
129515
|
if (existsSync24(RUNTIME_PID_FILE))
|
|
129410
129516
|
unlinkSync9(RUNTIME_PID_FILE);
|
|
129411
129517
|
} catch {}
|
|
129412
|
-
log("INFO", "cc-
|
|
129518
|
+
log("INFO", "cc-linker 已停止");
|
|
129413
129519
|
process.exit(0);
|
|
129414
129520
|
};
|
|
129415
129521
|
process.on("SIGTERM", () => baseShutdown("SIGTERM"));
|
|
129416
129522
|
process.on("SIGINT", () => baseShutdown("SIGINT"));
|
|
129417
129523
|
const { bot, shutdown } = await createBotRuntime2(registry2, log);
|
|
129418
|
-
log("INFO", "cc-
|
|
129524
|
+
log("INFO", "cc-linker daemon started");
|
|
129419
129525
|
const daemonShutdown = async (signal) => {
|
|
129420
129526
|
if (shuttingDown)
|
|
129421
129527
|
return;
|
|
@@ -129426,7 +129532,7 @@ async function startDaemonChild2(registry2, opts) {
|
|
|
129426
129532
|
if (existsSync24(RUNTIME_PID_FILE))
|
|
129427
129533
|
unlinkSync9(RUNTIME_PID_FILE);
|
|
129428
129534
|
} catch {}
|
|
129429
|
-
log("INFO", "cc-
|
|
129535
|
+
log("INFO", "cc-linker 已停止");
|
|
129430
129536
|
process.exit(0);
|
|
129431
129537
|
};
|
|
129432
129538
|
process.removeAllListeners("SIGTERM");
|
|
@@ -129443,12 +129549,12 @@ async function startDaemonChild2(registry2, opts) {
|
|
|
129443
129549
|
}
|
|
129444
129550
|
function getExecutablePath2() {
|
|
129445
129551
|
const argv0 = process.argv[0];
|
|
129446
|
-
if (argv0.endsWith("cc-
|
|
129552
|
+
if (argv0.endsWith("cc-linker"))
|
|
129447
129553
|
return argv0;
|
|
129448
|
-
const distPath = join15(process.cwd(), "dist", "cc-
|
|
129554
|
+
const distPath = join15(process.cwd(), "dist", "cc-linker");
|
|
129449
129555
|
if (existsSync24(distPath))
|
|
129450
129556
|
return distPath;
|
|
129451
|
-
return "cc-
|
|
129557
|
+
return "cc-linker";
|
|
129452
129558
|
}
|
|
129453
129559
|
async function startDaemon2() {
|
|
129454
129560
|
const { spawn: spawn2 } = await import("child_process");
|
|
@@ -129457,7 +129563,7 @@ async function startDaemon2() {
|
|
|
129457
129563
|
const child = spawn2(exe, args, {
|
|
129458
129564
|
detached: true,
|
|
129459
129565
|
stdio: "ignore",
|
|
129460
|
-
env: { ...process.env,
|
|
129566
|
+
env: { ...process.env, CC_LINKER_DAEMON: "1" }
|
|
129461
129567
|
});
|
|
129462
129568
|
child.unref();
|
|
129463
129569
|
await new Promise((r) => setTimeout(r, 1500));
|
|
@@ -129466,10 +129572,10 @@ async function startDaemon2() {
|
|
|
129466
129572
|
process.exit(1);
|
|
129467
129573
|
}
|
|
129468
129574
|
const pid = readPid2();
|
|
129469
|
-
console.log(source_default.green(`✅ cc-
|
|
129575
|
+
console.log(source_default.green(`✅ cc-linker 已在后台启动 (PID: ${pid})`));
|
|
129470
129576
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
129471
|
-
console.log(source_default.cyan(` 停止: cc-
|
|
129472
|
-
console.log(source_default.cyan(` 状态: cc-
|
|
129577
|
+
console.log(source_default.cyan(` 停止: cc-linker stop`));
|
|
129578
|
+
console.log(source_default.cyan(` 状态: cc-linker daemon status`));
|
|
129473
129579
|
process.exit(0);
|
|
129474
129580
|
}
|
|
129475
129581
|
var init_start = __esm(() => {
|
|
@@ -129498,15 +129604,15 @@ import { join as join16, dirname as dirname7 } from "path";
|
|
|
129498
129604
|
import { spawnSync as spawnSync4 } from "child_process";
|
|
129499
129605
|
function getExecutablePath3() {
|
|
129500
129606
|
const exe = process.argv[0];
|
|
129501
|
-
if (exe.endsWith("cc-
|
|
129607
|
+
if (exe.endsWith("cc-linker"))
|
|
129502
129608
|
return exe;
|
|
129503
|
-
return "cc-
|
|
129609
|
+
return "cc-linker";
|
|
129504
129610
|
}
|
|
129505
129611
|
function getMacOSPlistPath() {
|
|
129506
|
-
return join16(HOME2, "Library", "LaunchAgents", "com.
|
|
129612
|
+
return join16(HOME2, "Library", "LaunchAgents", "com.cclinker.daemon.plist");
|
|
129507
129613
|
}
|
|
129508
129614
|
function getLinuxServicePath() {
|
|
129509
|
-
return join16(HOME2, ".config", "systemd", "user", "cc-
|
|
129615
|
+
return join16(HOME2, ".config", "systemd", "user", "cc-linker.service");
|
|
129510
129616
|
}
|
|
129511
129617
|
function generateMacOSPlist() {
|
|
129512
129618
|
const exe = getExecutablePath3();
|
|
@@ -129516,7 +129622,7 @@ function generateMacOSPlist() {
|
|
|
129516
129622
|
<plist version="1.0">
|
|
129517
129623
|
<dict>
|
|
129518
129624
|
<key>Label</key>
|
|
129519
|
-
<string>com.
|
|
129625
|
+
<string>com.cclinker.daemon</string>
|
|
129520
129626
|
<key>ProgramArguments</key>
|
|
129521
129627
|
<array>
|
|
129522
129628
|
<string>${exe}</string>
|
|
@@ -129545,7 +129651,7 @@ function generateLinuxService() {
|
|
|
129545
129651
|
const exe = getExecutablePath3();
|
|
129546
129652
|
const cwd = dirname7(exe) === "." ? process.cwd() : dirname7(exe);
|
|
129547
129653
|
return `[Unit]
|
|
129548
|
-
Description=cc-
|
|
129654
|
+
Description=cc-linker Feishu Bot Daemon
|
|
129549
129655
|
After=network.target
|
|
129550
129656
|
|
|
129551
129657
|
[Service]
|
|
@@ -129563,7 +129669,7 @@ Environment=PATH=/usr/local/bin:/usr/bin:${process.env.PATH || ""}
|
|
|
129563
129669
|
WantedBy=default.target`;
|
|
129564
129670
|
}
|
|
129565
129671
|
async function installDaemon() {
|
|
129566
|
-
console.log(source_default.blue(`=== cc-
|
|
129672
|
+
console.log(source_default.blue(`=== cc-linker 开机自启配置 ===
|
|
129567
129673
|
`));
|
|
129568
129674
|
if (IS_MACOS) {
|
|
129569
129675
|
await installMacOS();
|
|
@@ -129596,15 +129702,15 @@ async function installMacOS() {
|
|
|
129596
129702
|
if (loadResult.status !== 0 && !loadResult.stderr.toString().includes("already loaded")) {
|
|
129597
129703
|
console.log(source_default.yellow(`⚠️ launchctl load 警告: ${loadResult.stderr.toString().trim()}`));
|
|
129598
129704
|
}
|
|
129599
|
-
const startResult = spawnSync4("launchctl", ["start", "com.
|
|
129705
|
+
const startResult = spawnSync4("launchctl", ["start", "com.cclinker.daemon"]);
|
|
129600
129706
|
console.log(source_default.green("✅ 开机自启已配置"));
|
|
129601
129707
|
console.log(source_default.cyan(` 配置: ${plistPath}`));
|
|
129602
129708
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
129603
129709
|
console.log(source_default.gray(`
|
|
129604
129710
|
操作:`));
|
|
129605
|
-
console.log(source_default.gray(` 停止: launchctl stop com.
|
|
129606
|
-
console.log(source_default.gray(` 卸载: cc-
|
|
129607
|
-
console.log(source_default.gray(` 状态: cc-
|
|
129711
|
+
console.log(source_default.gray(` 停止: launchctl stop com.cclinker.daemon`));
|
|
129712
|
+
console.log(source_default.gray(` 卸载: cc-linker daemon uninstall`));
|
|
129713
|
+
console.log(source_default.gray(` 状态: cc-linker daemon status`));
|
|
129608
129714
|
}
|
|
129609
129715
|
async function installLinux() {
|
|
129610
129716
|
const servicePath = getLinuxServicePath();
|
|
@@ -129627,19 +129733,19 @@ async function installLinux() {
|
|
|
129627
129733
|
if (reloadResult.status !== 0) {
|
|
129628
129734
|
console.log(source_default.yellow(`⚠️ systemctl daemon-reload 警告: ${reloadResult.stderr.toString().trim()}`));
|
|
129629
129735
|
}
|
|
129630
|
-
const enableResult = spawnSync4("systemctl", ["--user", "enable", "cc-
|
|
129736
|
+
const enableResult = spawnSync4("systemctl", ["--user", "enable", "cc-linker.service"]);
|
|
129631
129737
|
if (enableResult.status !== 0) {
|
|
129632
129738
|
console.log(source_default.yellow(`⚠️ systemctl enable 警告: ${enableResult.stderr.toString().trim()}`));
|
|
129633
129739
|
}
|
|
129634
|
-
const startResult = spawnSync4("systemctl", ["--user", "start", "cc-
|
|
129740
|
+
const startResult = spawnSync4("systemctl", ["--user", "start", "cc-linker.service"]);
|
|
129635
129741
|
console.log(source_default.green("✅ 开机自启已配置"));
|
|
129636
129742
|
console.log(source_default.cyan(` 配置: ${servicePath}`));
|
|
129637
129743
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
129638
129744
|
console.log(source_default.gray(`
|
|
129639
129745
|
操作:`));
|
|
129640
|
-
console.log(source_default.gray(` 停止: systemctl --user stop cc-
|
|
129641
|
-
console.log(source_default.gray(` 卸载: cc-
|
|
129642
|
-
console.log(source_default.gray(` 状态: cc-
|
|
129746
|
+
console.log(source_default.gray(` 停止: systemctl --user stop cc-linker.service`));
|
|
129747
|
+
console.log(source_default.gray(` 卸载: cc-linker daemon uninstall`));
|
|
129748
|
+
console.log(source_default.gray(` 状态: cc-linker daemon status`));
|
|
129643
129749
|
}
|
|
129644
129750
|
async function uninstallDaemon() {
|
|
129645
129751
|
console.log(source_default.blue(`=== 卸载开机自启 ===
|
|
@@ -129669,25 +129775,25 @@ async function uninstallLinux() {
|
|
|
129669
129775
|
console.log(source_default.yellow("⚠️ systemd 配置不存在"));
|
|
129670
129776
|
return;
|
|
129671
129777
|
}
|
|
129672
|
-
spawnSync4("systemctl", ["--user", "disable", "cc-
|
|
129673
|
-
spawnSync4("systemctl", ["--user", "stop", "cc-
|
|
129778
|
+
spawnSync4("systemctl", ["--user", "disable", "cc-linker.service"]);
|
|
129779
|
+
spawnSync4("systemctl", ["--user", "stop", "cc-linker.service"]);
|
|
129674
129780
|
spawnSync4("systemctl", ["--user", "daemon-reload"]);
|
|
129675
129781
|
unlinkSync10(servicePath);
|
|
129676
129782
|
console.log(source_default.green("✅ 开机自启已卸载"));
|
|
129677
129783
|
}
|
|
129678
129784
|
async function daemonStatus2() {
|
|
129679
|
-
console.log(source_default.blue(`=== cc-
|
|
129785
|
+
console.log(source_default.blue(`=== cc-linker 服务状态 ===
|
|
129680
129786
|
`));
|
|
129681
129787
|
if (existsSync25(RUNTIME_PID_FILE)) {
|
|
129682
129788
|
try {
|
|
129683
129789
|
const pid = parseInt(readFileSync18(RUNTIME_PID_FILE, "utf8").trim(), 10);
|
|
129684
129790
|
process.kill(pid, 0);
|
|
129685
|
-
console.log(source_default.green(`✅ cc-
|
|
129791
|
+
console.log(source_default.green(`✅ cc-linker 正在运行 (PID: ${pid})`));
|
|
129686
129792
|
} catch {
|
|
129687
|
-
console.log(source_default.red("❌ cc-
|
|
129793
|
+
console.log(source_default.red("❌ cc-linker PID 存在但进程不存在"));
|
|
129688
129794
|
}
|
|
129689
129795
|
} else {
|
|
129690
|
-
console.log(source_default.yellow("⚠️ cc-
|
|
129796
|
+
console.log(source_default.yellow("⚠️ cc-linker 未在运行"));
|
|
129691
129797
|
}
|
|
129692
129798
|
console.log(source_default.cyan(`
|
|
129693
129799
|
开机自启配置:`));
|
|
@@ -129697,7 +129803,7 @@ async function daemonStatus2() {
|
|
|
129697
129803
|
console.log(source_default.green(` ✅ launchd 已配置 (${plistPath})`));
|
|
129698
129804
|
} else {
|
|
129699
129805
|
console.log(source_default.gray(" ️ 未配置 launchd"));
|
|
129700
|
-
console.log(source_default.gray(" 执行: cc-
|
|
129806
|
+
console.log(source_default.gray(" 执行: cc-linker daemon install"));
|
|
129701
129807
|
}
|
|
129702
129808
|
} else if (IS_LINUX) {
|
|
129703
129809
|
const servicePath = getLinuxServicePath();
|
|
@@ -129705,7 +129811,7 @@ async function daemonStatus2() {
|
|
|
129705
129811
|
console.log(source_default.green(` ✅ systemd 已配置 (${servicePath})`));
|
|
129706
129812
|
} else {
|
|
129707
129813
|
console.log(source_default.gray(" ⏸️ 未配置 systemd"));
|
|
129708
|
-
console.log(source_default.gray(" 执行: cc-
|
|
129814
|
+
console.log(source_default.gray(" 执行: cc-linker daemon install"));
|
|
129709
129815
|
}
|
|
129710
129816
|
}
|
|
129711
129817
|
if (existsSync25(RUNTIME_LOG_FILE)) {
|
|
@@ -129731,6 +129837,151 @@ var init_daemon = __esm(() => {
|
|
|
129731
129837
|
IS_LINUX = platform() === "linux";
|
|
129732
129838
|
});
|
|
129733
129839
|
|
|
129840
|
+
// src/cli/commands/init-feishu.ts
|
|
129841
|
+
import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
129842
|
+
import { dirname as dirname9 } from "path";
|
|
129843
|
+
import { homedir as homedir8, platform as platform3 } from "os";
|
|
129844
|
+
function isDaemonRunning2() {
|
|
129845
|
+
if (!existsSync27(RUNTIME_PID_FILE))
|
|
129846
|
+
return false;
|
|
129847
|
+
try {
|
|
129848
|
+
const pid = parseInt(readFileSync20(RUNTIME_PID_FILE, "utf8").trim(), 10);
|
|
129849
|
+
process.kill(pid, 0);
|
|
129850
|
+
return true;
|
|
129851
|
+
} catch {
|
|
129852
|
+
return false;
|
|
129853
|
+
}
|
|
129854
|
+
}
|
|
129855
|
+
async function getTenantToken2(appId, appSecret) {
|
|
129856
|
+
try {
|
|
129857
|
+
const resp = await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", {
|
|
129858
|
+
method: "POST",
|
|
129859
|
+
headers: { "Content-Type": "application/json" },
|
|
129860
|
+
body: JSON.stringify({ app_id: appId, app_secret: appSecret })
|
|
129861
|
+
});
|
|
129862
|
+
const data = await resp.json();
|
|
129863
|
+
return data.code === 0 ? data.tenant_access_token : null;
|
|
129864
|
+
} catch {
|
|
129865
|
+
return null;
|
|
129866
|
+
}
|
|
129867
|
+
}
|
|
129868
|
+
async function getBotName2(token) {
|
|
129869
|
+
try {
|
|
129870
|
+
const resp = await fetch("https://open.feishu.cn/open-apis/bot/v3/info", {
|
|
129871
|
+
headers: { Authorization: `Bearer ${token}` }
|
|
129872
|
+
});
|
|
129873
|
+
const data = await resp.json();
|
|
129874
|
+
return data.bot?.app_name ?? null;
|
|
129875
|
+
} catch {
|
|
129876
|
+
return null;
|
|
129877
|
+
}
|
|
129878
|
+
}
|
|
129879
|
+
async function captureOpenId2(appId, appSecret) {
|
|
129880
|
+
const Lark = await Promise.resolve().then(() => __toESM(require_lib5(), 1));
|
|
129881
|
+
const { WSClient, EventDispatcher, Domain, LoggerLevel } = Lark;
|
|
129882
|
+
return new Promise((resolve2) => {
|
|
129883
|
+
let settled = false;
|
|
129884
|
+
let wsClient = null;
|
|
129885
|
+
let timer = null;
|
|
129886
|
+
const settle = (id) => {
|
|
129887
|
+
if (settled)
|
|
129888
|
+
return;
|
|
129889
|
+
settled = true;
|
|
129890
|
+
if (timer)
|
|
129891
|
+
clearTimeout(timer);
|
|
129892
|
+
if (wsClient && typeof wsClient.close === "function") {
|
|
129893
|
+
try {
|
|
129894
|
+
wsClient.close();
|
|
129895
|
+
} catch {}
|
|
129896
|
+
}
|
|
129897
|
+
resolve2(id);
|
|
129898
|
+
};
|
|
129899
|
+
const eventDispatcher = new EventDispatcher({}).register({
|
|
129900
|
+
"im.message.receive_v1": async (data) => {
|
|
129901
|
+
const openId = data?.sender?.sender_id?.open_id;
|
|
129902
|
+
if (openId)
|
|
129903
|
+
settle(openId);
|
|
129904
|
+
}
|
|
129905
|
+
});
|
|
129906
|
+
wsClient = new WSClient({
|
|
129907
|
+
appId,
|
|
129908
|
+
appSecret,
|
|
129909
|
+
domain: Domain.Feishu,
|
|
129910
|
+
loggerLevel: LoggerLevel.warn,
|
|
129911
|
+
autoReconnect: false,
|
|
129912
|
+
onReady: () => {
|
|
129913
|
+
console.log(source_default.green(" ✅ WebSocket 已连接,等待消息..."));
|
|
129914
|
+
},
|
|
129915
|
+
onError: (err) => {
|
|
129916
|
+
console.log(source_default.red(` ❌ 连接失败: ${err.message}`));
|
|
129917
|
+
settle(null);
|
|
129918
|
+
}
|
|
129919
|
+
});
|
|
129920
|
+
wsClient.start({ eventDispatcher });
|
|
129921
|
+
timer = setTimeout(() => {
|
|
129922
|
+
console.log(source_default.yellow(" ⏰ 超时"));
|
|
129923
|
+
settle(null);
|
|
129924
|
+
}, 120000);
|
|
129925
|
+
});
|
|
129926
|
+
}
|
|
129927
|
+
function loadExistingConfig2() {
|
|
129928
|
+
if (!existsSync27(CONFIG_PATH))
|
|
129929
|
+
return {};
|
|
129930
|
+
try {
|
|
129931
|
+
return $parse(readFileSync20(CONFIG_PATH, "utf8"));
|
|
129932
|
+
} catch {
|
|
129933
|
+
return {};
|
|
129934
|
+
}
|
|
129935
|
+
}
|
|
129936
|
+
function formatTomlValue2(v) {
|
|
129937
|
+
if (Array.isArray(v)) {
|
|
129938
|
+
return `[${v.map((item) => JSON.stringify(item)).join(", ")}]`;
|
|
129939
|
+
}
|
|
129940
|
+
if (typeof v === "object" && v !== null) {
|
|
129941
|
+
return JSON.stringify(v);
|
|
129942
|
+
}
|
|
129943
|
+
return JSON.stringify(v);
|
|
129944
|
+
}
|
|
129945
|
+
function saveConfig2(config3) {
|
|
129946
|
+
const dir = dirname9(CONFIG_PATH);
|
|
129947
|
+
mkdirSync14(dir, { recursive: true });
|
|
129948
|
+
const lines = [];
|
|
129949
|
+
for (const section of ["general", "feishu_bot", "queue", "runtime", "security", "scanner", "cli_proxy", "hook"]) {
|
|
129950
|
+
const values = config3[section];
|
|
129951
|
+
if (!values || typeof values !== "object")
|
|
129952
|
+
continue;
|
|
129953
|
+
lines.push(`[${section}]`);
|
|
129954
|
+
for (const [k, v] of Object.entries(values)) {
|
|
129955
|
+
if (v === undefined || v === null)
|
|
129956
|
+
continue;
|
|
129957
|
+
lines.push(`${k} = ${formatTomlValue2(v)}`);
|
|
129958
|
+
}
|
|
129959
|
+
lines.push("");
|
|
129960
|
+
delete config3[section];
|
|
129961
|
+
}
|
|
129962
|
+
for (const [section, values] of Object.entries(config3)) {
|
|
129963
|
+
if (typeof values !== "object" || values === null)
|
|
129964
|
+
continue;
|
|
129965
|
+
lines.push(`[${section}]`);
|
|
129966
|
+
for (const [k, v] of Object.entries(values)) {
|
|
129967
|
+
if (v === undefined || v === null)
|
|
129968
|
+
continue;
|
|
129969
|
+
lines.push(`${k} = ${formatTomlValue2(v)}`);
|
|
129970
|
+
}
|
|
129971
|
+
lines.push("");
|
|
129972
|
+
}
|
|
129973
|
+
writeFileSync18(CONFIG_PATH, lines.join(`
|
|
129974
|
+
`), { mode: 384 });
|
|
129975
|
+
}
|
|
129976
|
+
var IS_MACOS3, IS_LINUX3;
|
|
129977
|
+
var init_init_feishu = __esm(() => {
|
|
129978
|
+
init_source();
|
|
129979
|
+
init_paths();
|
|
129980
|
+
init_toml();
|
|
129981
|
+
IS_MACOS3 = platform3() === "darwin";
|
|
129982
|
+
IS_LINUX3 = platform3() === "linux";
|
|
129983
|
+
});
|
|
129984
|
+
|
|
129734
129985
|
// src/cli/commands/hook.ts
|
|
129735
129986
|
var exports_hook = {};
|
|
129736
129987
|
__export(exports_hook, {
|
|
@@ -129746,7 +129997,7 @@ function isHookInstalled2(sessionStart) {
|
|
|
129746
129997
|
return sessionStart.some((matcher) => {
|
|
129747
129998
|
if (!matcher?.hooks)
|
|
129748
129999
|
return false;
|
|
129749
|
-
return matcher.hooks.some((h) => h?.command?.includes("cc-
|
|
130000
|
+
return matcher.hooks.some((h) => h?.command?.includes("cc-linker"));
|
|
129750
130001
|
});
|
|
129751
130002
|
}
|
|
129752
130003
|
function hookInstall2() {
|
|
@@ -129767,12 +130018,12 @@ function hookInstall2() {
|
|
|
129767
130018
|
return;
|
|
129768
130019
|
}
|
|
129769
130020
|
settings.hooks = settings.hooks ?? {};
|
|
129770
|
-
const
|
|
130021
|
+
const ccLinkerMatcher = {
|
|
129771
130022
|
matcher: "startup|resume|clear|compact",
|
|
129772
130023
|
hooks: [
|
|
129773
130024
|
{
|
|
129774
130025
|
type: "command",
|
|
129775
|
-
command: "cc-
|
|
130026
|
+
command: "cc-linker hook session-start",
|
|
129776
130027
|
timeout: 10
|
|
129777
130028
|
}
|
|
129778
130029
|
]
|
|
@@ -129780,14 +130031,14 @@ function hookInstall2() {
|
|
|
129780
130031
|
if (!Array.isArray(settings.hooks.SessionStart)) {
|
|
129781
130032
|
settings.hooks.SessionStart = [];
|
|
129782
130033
|
}
|
|
129783
|
-
const existingIndex = settings.hooks.SessionStart.findIndex((m) => m?.hooks?.some((h) => h?.command?.includes("cc-
|
|
130034
|
+
const existingIndex = settings.hooks.SessionStart.findIndex((m) => m?.hooks?.some((h) => h?.command?.includes("cc-linker") || h?.command?.includes("cc-linker")));
|
|
129784
130035
|
if (existingIndex === -1) {
|
|
129785
|
-
settings.hooks.SessionStart.push(
|
|
130036
|
+
settings.hooks.SessionStart.push(ccLinkerMatcher);
|
|
129786
130037
|
}
|
|
129787
130038
|
writeFileSync19(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), { mode: 384 });
|
|
129788
130039
|
console.log(source_default.green("Hook 安装成功"));
|
|
129789
130040
|
console.log(`已添加到 ${CLAUDE_SETTINGS_PATH}:`);
|
|
129790
|
-
console.log(JSON.stringify({ hooks: { SessionStart: [
|
|
130041
|
+
console.log(JSON.stringify({ hooks: { SessionStart: [ccLinkerMatcher] } }, null, 2));
|
|
129791
130042
|
}
|
|
129792
130043
|
function hookUninstall2() {
|
|
129793
130044
|
if (!existsSync28(CLAUDE_SETTINGS_PATH)) {
|
|
@@ -129807,7 +130058,7 @@ function hookUninstall2() {
|
|
|
129807
130058
|
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((m) => {
|
|
129808
130059
|
if (!m?.hooks)
|
|
129809
130060
|
return true;
|
|
129810
|
-
return !m.hooks.some((h) => h?.command?.includes("cc-
|
|
130061
|
+
return !m.hooks.some((h) => h?.command?.includes("cc-linker") || h?.command?.includes("cc-linker"));
|
|
129811
130062
|
});
|
|
129812
130063
|
if (settings.hooks.SessionStart.length === 0) {
|
|
129813
130064
|
delete settings.hooks.SessionStart;
|
|
@@ -129851,6 +130102,29 @@ var init_hook = __esm(() => {
|
|
|
129851
130102
|
init_session_start();
|
|
129852
130103
|
});
|
|
129853
130104
|
|
|
130105
|
+
// src/cli/commands/restart.ts
|
|
130106
|
+
var exports_restart = {};
|
|
130107
|
+
__export(exports_restart, {
|
|
130108
|
+
restart: () => restart
|
|
130109
|
+
});
|
|
130110
|
+
async function restart(registry2, deps = { isDaemonRunning: isDaemonRunning2, stop: stop2, start: start2 }) {
|
|
130111
|
+
const wasRunning = deps.isDaemonRunning();
|
|
130112
|
+
if (wasRunning) {
|
|
130113
|
+
console.log(source_default.cyan("\uD83D\uDD04 正在重启 cc-linker..."));
|
|
130114
|
+
await deps.stop();
|
|
130115
|
+
console.log(source_default.gray(" 等待进程完全停止..."));
|
|
130116
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
130117
|
+
} else {
|
|
130118
|
+
console.log(source_default.cyan("\uD83D\uDE80 Bot 未运行,直接启动..."));
|
|
130119
|
+
}
|
|
130120
|
+
await deps.start(registry2, { daemon: true });
|
|
130121
|
+
}
|
|
130122
|
+
var init_restart = __esm(() => {
|
|
130123
|
+
init_source();
|
|
130124
|
+
init_start();
|
|
130125
|
+
init_init_feishu();
|
|
130126
|
+
});
|
|
130127
|
+
|
|
129854
130128
|
// node_modules/commander/esm.mjs
|
|
129855
130129
|
var import__ = __toESM(require_commander(), 1);
|
|
129856
130130
|
var {
|
|
@@ -129892,34 +130166,34 @@ async function syncBeforeCommand(registry2, cachePath, claudeDir, skipFlush = fa
|
|
|
129892
130166
|
}
|
|
129893
130167
|
|
|
129894
130168
|
// src/utils/errors.ts
|
|
129895
|
-
class
|
|
130169
|
+
class CCLinkerError2 extends Error {
|
|
129896
130170
|
code;
|
|
129897
130171
|
details;
|
|
129898
130172
|
constructor(code, message, details) {
|
|
129899
130173
|
super(message);
|
|
129900
130174
|
this.code = code;
|
|
129901
130175
|
this.details = details;
|
|
129902
|
-
this.name = "
|
|
130176
|
+
this.name = "CCLinkerError";
|
|
129903
130177
|
}
|
|
129904
130178
|
toString() {
|
|
129905
130179
|
return `[${this.code}] ${this.message}`;
|
|
129906
130180
|
}
|
|
129907
130181
|
}
|
|
129908
130182
|
function handleError(err) {
|
|
129909
|
-
if (err instanceof
|
|
130183
|
+
if (err instanceof CCLinkerError2) {
|
|
129910
130184
|
console.error(`错误 [${err.code}]: ${err.message}`);
|
|
129911
130185
|
if (err.details) {
|
|
129912
130186
|
console.error(`详情: ${JSON.stringify(err.details)}`);
|
|
129913
130187
|
}
|
|
129914
130188
|
const suggestions = {
|
|
129915
|
-
E001: ["运行 cc-
|
|
129916
|
-
E002: ["会话已被清理,无法恢复", "运行 cc-
|
|
129917
|
-
E007: ["等待其他进程完成", "或删除 ~/.cc-
|
|
130189
|
+
E001: ["运行 cc-linker init 初始化 registry"],
|
|
130190
|
+
E002: ["会话已被清理,无法恢复", "运行 cc-linker sync 重新扫描"],
|
|
130191
|
+
E007: ["等待其他进程完成", "或删除 ~/.cc-linker/registry.json.lock"],
|
|
129918
130192
|
E008: ["会话创建目录已被删除,使用 --cwd 指定替代目录"],
|
|
129919
|
-
E010: ["会话处于降级状态,执行 cc-
|
|
130193
|
+
E010: ["会话处于降级状态,执行 cc-linker start 触发自动修复"],
|
|
129920
130194
|
E011: ["会话仍在创建中,请稍后重试"],
|
|
129921
|
-
E012: ["会话已损坏,请使用 /
|
|
129922
|
-
E013: ["服务正在运行,请先执行 cc-
|
|
130195
|
+
E012: ["会话已损坏,请使用 /switch 切换到其他会话"],
|
|
130196
|
+
E013: ["服务正在运行,请先执行 cc-linker stop 后再执行此命令"]
|
|
129923
130197
|
};
|
|
129924
130198
|
if (suggestions[err.code]) {
|
|
129925
130199
|
console.error("建议:");
|
|
@@ -130016,7 +130290,7 @@ class StateCoordinator {
|
|
|
130016
130290
|
}
|
|
130017
130291
|
static assertNotRunning(lockPath) {
|
|
130018
130292
|
if (StateCoordinator.isLocked(lockPath)) {
|
|
130019
|
-
throw new
|
|
130293
|
+
throw new CCLinkerError("E013", "Bot 进程正在运行,请使用飞书命令操作会话,而非直接 CLI 操作");
|
|
130020
130294
|
}
|
|
130021
130295
|
}
|
|
130022
130296
|
isHeld() {
|
|
@@ -130047,9 +130321,9 @@ async function init(registry2) {
|
|
|
130047
130321
|
console.log(source_default.green(`✅ Registered ${sessions.length} sessions total`));
|
|
130048
130322
|
console.log(`
|
|
130049
130323
|
Next steps:`);
|
|
130050
|
-
console.log(" 1. Run 'cc-
|
|
130051
|
-
console.log(" 2. Run 'cc-
|
|
130052
|
-
console.log(" 3. Run 'cc-
|
|
130324
|
+
console.log(" 1. Run 'cc-linker start' to launch the Feishu bot");
|
|
130325
|
+
console.log(" 2. Run 'cc-linker list' to view all sessions");
|
|
130326
|
+
console.log(" 3. Run 'cc-linker resume' to resume a session");
|
|
130053
130327
|
}
|
|
130054
130328
|
|
|
130055
130329
|
// src/cli/commands/list.ts
|
|
@@ -130094,7 +130368,7 @@ async function list(registry2, opts) {
|
|
|
130094
130368
|
}
|
|
130095
130369
|
console.log(formatTable(sessions));
|
|
130096
130370
|
console.log(`
|
|
130097
|
-
共 ${sessions.length} 个会话。使用 cc-
|
|
130371
|
+
共 ${sessions.length} 个会话。使用 cc-linker resume <Ref> 或完整 UUID 恢复会话。`);
|
|
130098
130372
|
}
|
|
130099
130373
|
}
|
|
130100
130374
|
|
|
@@ -130126,9 +130400,9 @@ async function resume(registry2, target, opts = {}) {
|
|
|
130126
130400
|
if (!match) {
|
|
130127
130401
|
const count = Object.keys(registry2.sessions).filter((u) => u.startsWith(target)).length;
|
|
130128
130402
|
if (count > 1) {
|
|
130129
|
-
throw new
|
|
130403
|
+
throw new CCLinkerError("E006", `前缀 "${target}" 匹配到 ${count} 个会话,请输入更长的前缀`);
|
|
130130
130404
|
}
|
|
130131
|
-
throw new
|
|
130405
|
+
throw new CCLinkerError("E002", `未找到匹配 "${target}" 的会话`);
|
|
130132
130406
|
}
|
|
130133
130407
|
uuid3 = match[0];
|
|
130134
130408
|
} else {
|
|
@@ -130136,7 +130410,7 @@ async function resume(registry2, target, opts = {}) {
|
|
|
130136
130410
|
}
|
|
130137
130411
|
let entry = registry2.get(uuid3);
|
|
130138
130412
|
if (!entry)
|
|
130139
|
-
throw new
|
|
130413
|
+
throw new CCLinkerError("E002", "会话不存在");
|
|
130140
130414
|
entry = await attemptRepairSession(registry2, uuid3, entry);
|
|
130141
130415
|
const status = entry.status ?? "active";
|
|
130142
130416
|
if (status === "archived") {} else if (status in STATUS_ERRORS) {
|
|
@@ -130145,7 +130419,7 @@ async function resume(registry2, target, opts = {}) {
|
|
|
130145
130419
|
if (status === "degraded" && entry.last_error) {
|
|
130146
130420
|
msg += `, 原因: ${entry.last_error}`;
|
|
130147
130421
|
}
|
|
130148
|
-
throw new
|
|
130422
|
+
throw new CCLinkerError(err.code, msg);
|
|
130149
130423
|
}
|
|
130150
130424
|
const busy = isSessionBusy(uuid3);
|
|
130151
130425
|
if (busy) {
|
|
@@ -130173,7 +130447,7 @@ async function resume(registry2, target, opts = {}) {
|
|
|
130173
130447
|
} else {
|
|
130174
130448
|
registry2.upsert(uuid3, { status: "corrupted" });
|
|
130175
130449
|
await registry2.flush();
|
|
130176
|
-
throw new
|
|
130450
|
+
throw new CCLinkerError("E002", "JSONL 文件不存在,会话可能已被清理(已标记 status=corrupted)");
|
|
130177
130451
|
}
|
|
130178
130452
|
} else if (!entry.jsonl_path) {
|
|
130179
130453
|
const found = findJsonlFile(uuid3);
|
|
@@ -130202,7 +130476,7 @@ async function resume(registry2, target, opts = {}) {
|
|
|
130202
130476
|
return;
|
|
130203
130477
|
}
|
|
130204
130478
|
if (!existsSync8(targetCwd)) {
|
|
130205
|
-
throw new
|
|
130479
|
+
throw new CCLinkerError("E008", `工作目录不存在: ${targetCwd},使用 --cwd 指定替代目录`);
|
|
130206
130480
|
}
|
|
130207
130481
|
console.log(source_default.green(`恢复会话: ${entry.title ?? uuid3}`));
|
|
130208
130482
|
if (entry.jsonl_path && existsSync8(entry.jsonl_path)) {
|
|
@@ -130225,7 +130499,7 @@ function findLatestSession(registry2, project) {
|
|
|
130225
130499
|
sessions = sessions.filter(([_2, s]) => s.project_name?.includes(project));
|
|
130226
130500
|
}
|
|
130227
130501
|
if (sessions.length === 0) {
|
|
130228
|
-
throw new
|
|
130502
|
+
throw new CCLinkerError("E002", "没有找到活跃会话");
|
|
130229
130503
|
}
|
|
130230
130504
|
sessions.sort((a, b) => b[1].last_active.localeCompare(a[1].last_active));
|
|
130231
130505
|
return sessions[0][0];
|
|
@@ -130233,7 +130507,7 @@ function findLatestSession(registry2, project) {
|
|
|
130233
130507
|
async function searchAndSelect(registry2, query) {
|
|
130234
130508
|
let matches = Object.entries(registry2.sessions).filter(([_2, s]) => s.title?.toLowerCase().includes(query.toLowerCase()));
|
|
130235
130509
|
if (matches.length === 0) {
|
|
130236
|
-
throw new
|
|
130510
|
+
throw new CCLinkerError("E002", `未找到包含 "${query}" 的会话`);
|
|
130237
130511
|
}
|
|
130238
130512
|
if (matches.length === 1)
|
|
130239
130513
|
return matches[0][0];
|
|
@@ -130252,7 +130526,7 @@ async function interactiveSelect(registry2) {
|
|
|
130252
130526
|
let sessions = Object.entries(registry2.sessions).filter(([_2, s]) => !s.status || s.status === "active");
|
|
130253
130527
|
sessions = sessions.sort((a, b) => b[1].last_active.localeCompare(a[1].last_active)).slice(0, 20);
|
|
130254
130528
|
if (sessions.length === 0) {
|
|
130255
|
-
throw new
|
|
130529
|
+
throw new CCLinkerError("E002", "没有找到会话");
|
|
130256
130530
|
}
|
|
130257
130531
|
const { selected } = await dist_default14.prompt([{
|
|
130258
130532
|
type: "list",
|
|
@@ -130319,9 +130593,9 @@ async function show(registry2, target) {
|
|
|
130319
130593
|
if (!match) {
|
|
130320
130594
|
const count = Object.keys(registry2.sessions).filter((u) => u.startsWith(target)).length;
|
|
130321
130595
|
if (count > 1) {
|
|
130322
|
-
throw new
|
|
130596
|
+
throw new CCLinkerError("E006", `前缀 "${target}" 匹配到 ${count} 个会话,请输入更长的前缀`);
|
|
130323
130597
|
}
|
|
130324
|
-
throw new
|
|
130598
|
+
throw new CCLinkerError("E002", `未找到匹配 "${target}" 的会话`);
|
|
130325
130599
|
}
|
|
130326
130600
|
const [uuid3, s] = match;
|
|
130327
130601
|
console.log(source_default.bold("会话详情"));
|
|
@@ -130339,7 +130613,7 @@ async function show(registry2, target) {
|
|
|
130339
130613
|
JSONL 文件: ${s.jsonl_path}`);
|
|
130340
130614
|
console.log(`
|
|
130341
130615
|
操作:`);
|
|
130342
|
-
console.log(` cc-
|
|
130616
|
+
console.log(` cc-linker resume ${uuid3.slice(0, 8)} 恢复此会话`);
|
|
130343
130617
|
}
|
|
130344
130618
|
|
|
130345
130619
|
// src/cli/commands/sync.ts
|
|
@@ -130390,7 +130664,7 @@ async function status(registry2) {
|
|
|
130390
130664
|
const fromCli = sessions.filter((s) => s.origin === "cli").length;
|
|
130391
130665
|
const fromFeishu = sessions.filter((s) => s.origin === "feishu").length;
|
|
130392
130666
|
const archivedOrCorrupted = sessions.filter((s) => s.status === "archived" || s.status === "corrupted" || s.status === "degraded" || s.status === "provisioning").length;
|
|
130393
|
-
console.log(source_default.bold("cc-
|
|
130667
|
+
console.log(source_default.bold("cc-linker Status"));
|
|
130394
130668
|
console.log("─".repeat(40));
|
|
130395
130669
|
console.log(`Registry: ${registry2.path}`);
|
|
130396
130670
|
if (existsSync10(registry2.path)) {
|
|
@@ -130412,17 +130686,17 @@ Runtime:`);
|
|
|
130412
130686
|
const settings = JSON.parse(readFileSync9(CLAUDE_SETTINGS_PATH, "utf8"));
|
|
130413
130687
|
const sessionStart = settings.hooks?.SessionStart;
|
|
130414
130688
|
if (Array.isArray(sessionStart)) {
|
|
130415
|
-
hookInstalled = sessionStart.some((matcher) => matcher?.hooks?.some((h) => h?.command?.includes("cc-
|
|
130689
|
+
hookInstalled = sessionStart.some((matcher) => matcher?.hooks?.some((h) => h?.command?.includes("cc-linker")));
|
|
130416
130690
|
}
|
|
130417
130691
|
} catch {}
|
|
130418
130692
|
}
|
|
130419
130693
|
console.log(` Claude Code hook: ${hookInstalled ? source_default.green("installed") : source_default.red("not installed")}`);
|
|
130420
130694
|
console.log(`
|
|
130421
130695
|
Commands:`);
|
|
130422
|
-
console.log(" cc-
|
|
130423
|
-
console.log(" cc-
|
|
130424
|
-
console.log(" cc-
|
|
130425
|
-
console.log(" cc-
|
|
130696
|
+
console.log(" cc-linker start Launch Feishu bot");
|
|
130697
|
+
console.log(" cc-linker list List all sessions");
|
|
130698
|
+
console.log(" cc-linker resume Resume a session");
|
|
130699
|
+
console.log(" cc-linker sync Sync sessions");
|
|
130426
130700
|
}
|
|
130427
130701
|
|
|
130428
130702
|
// src/cli/commands/hook.ts
|
|
@@ -130436,7 +130710,7 @@ function isHookInstalled(sessionStart) {
|
|
|
130436
130710
|
return sessionStart.some((matcher) => {
|
|
130437
130711
|
if (!matcher?.hooks)
|
|
130438
130712
|
return false;
|
|
130439
|
-
return matcher.hooks.some((h) => h?.command?.includes("cc-
|
|
130713
|
+
return matcher.hooks.some((h) => h?.command?.includes("cc-linker"));
|
|
130440
130714
|
});
|
|
130441
130715
|
}
|
|
130442
130716
|
function hookInstall() {
|
|
@@ -130457,12 +130731,12 @@ function hookInstall() {
|
|
|
130457
130731
|
return;
|
|
130458
130732
|
}
|
|
130459
130733
|
settings.hooks = settings.hooks ?? {};
|
|
130460
|
-
const
|
|
130734
|
+
const ccLinkerMatcher = {
|
|
130461
130735
|
matcher: "startup|resume|clear|compact",
|
|
130462
130736
|
hooks: [
|
|
130463
130737
|
{
|
|
130464
130738
|
type: "command",
|
|
130465
|
-
command: "cc-
|
|
130739
|
+
command: "cc-linker hook session-start",
|
|
130466
130740
|
timeout: 10
|
|
130467
130741
|
}
|
|
130468
130742
|
]
|
|
@@ -130470,14 +130744,14 @@ function hookInstall() {
|
|
|
130470
130744
|
if (!Array.isArray(settings.hooks.SessionStart)) {
|
|
130471
130745
|
settings.hooks.SessionStart = [];
|
|
130472
130746
|
}
|
|
130473
|
-
const existingIndex = settings.hooks.SessionStart.findIndex((m) => m?.hooks?.some((h) => h?.command?.includes("cc-
|
|
130747
|
+
const existingIndex = settings.hooks.SessionStart.findIndex((m) => m?.hooks?.some((h) => h?.command?.includes("cc-linker") || h?.command?.includes("cc-linker")));
|
|
130474
130748
|
if (existingIndex === -1) {
|
|
130475
|
-
settings.hooks.SessionStart.push(
|
|
130749
|
+
settings.hooks.SessionStart.push(ccLinkerMatcher);
|
|
130476
130750
|
}
|
|
130477
130751
|
writeFileSync9(CLAUDE_SETTINGS_PATH, JSON.stringify(settings, null, 2), { mode: 384 });
|
|
130478
130752
|
console.log(source_default.green("Hook 安装成功"));
|
|
130479
130753
|
console.log(`已添加到 ${CLAUDE_SETTINGS_PATH}:`);
|
|
130480
|
-
console.log(JSON.stringify({ hooks: { SessionStart: [
|
|
130754
|
+
console.log(JSON.stringify({ hooks: { SessionStart: [ccLinkerMatcher] } }, null, 2));
|
|
130481
130755
|
}
|
|
130482
130756
|
function hookUninstall() {
|
|
130483
130757
|
if (!existsSync12(CLAUDE_SETTINGS_PATH)) {
|
|
@@ -130497,7 +130771,7 @@ function hookUninstall() {
|
|
|
130497
130771
|
settings.hooks.SessionStart = settings.hooks.SessionStart.filter((m) => {
|
|
130498
130772
|
if (!m?.hooks)
|
|
130499
130773
|
return true;
|
|
130500
|
-
return !m.hooks.some((h) => h?.command?.includes("cc-
|
|
130774
|
+
return !m.hooks.some((h) => h?.command?.includes("cc-linker") || h?.command?.includes("cc-linker"));
|
|
130501
130775
|
});
|
|
130502
130776
|
if (settings.hooks.SessionStart.length === 0) {
|
|
130503
130777
|
delete settings.hooks.SessionStart;
|
|
@@ -130541,11 +130815,11 @@ init_types();
|
|
|
130541
130815
|
init_errors3();
|
|
130542
130816
|
async function registerSession(registry2, uuid3, opts = {}) {
|
|
130543
130817
|
if (!isValidUUID(uuid3)) {
|
|
130544
|
-
throw new
|
|
130818
|
+
throw new CCLinkerError("E005", `无效的 UUID 格式: ${uuid3}`);
|
|
130545
130819
|
}
|
|
130546
130820
|
const originResult = OriginSchema.safeParse(opts.origin ?? "cli");
|
|
130547
130821
|
if (!originResult.success) {
|
|
130548
|
-
throw new
|
|
130822
|
+
throw new CCLinkerError("E005", `无效的 origin 值: ${opts.origin}`);
|
|
130549
130823
|
}
|
|
130550
130824
|
const entry = {
|
|
130551
130825
|
origin: originResult.data,
|
|
@@ -130579,9 +130853,9 @@ async function exportSession(registry2, target, opts = {}) {
|
|
|
130579
130853
|
if (!match) {
|
|
130580
130854
|
const count2 = Object.keys(registry2.sessions).filter((u) => u.startsWith(target)).length;
|
|
130581
130855
|
if (count2 > 1) {
|
|
130582
|
-
throw new
|
|
130856
|
+
throw new CCLinkerError("E006", `前缀 "${target}" 匹配到 ${count2} 个会话,请输入更长的前缀`);
|
|
130583
130857
|
}
|
|
130584
|
-
throw new
|
|
130858
|
+
throw new CCLinkerError("E002", `未找到匹配 "${target}" 的会话`);
|
|
130585
130859
|
}
|
|
130586
130860
|
const [uuid3, entry] = match;
|
|
130587
130861
|
const format = opts.format ?? "markdown";
|
|
@@ -130589,11 +130863,11 @@ async function exportSession(registry2, target, opts = {}) {
|
|
|
130589
130863
|
const maxMessages = opts.maxMessages ? (() => {
|
|
130590
130864
|
const n = parseInt(opts.maxMessages, 10);
|
|
130591
130865
|
if (isNaN(n))
|
|
130592
|
-
throw new
|
|
130866
|
+
throw new CCLinkerError("E005", `无效的消息数: ${opts.maxMessages}`);
|
|
130593
130867
|
return n;
|
|
130594
130868
|
})() : undefined;
|
|
130595
130869
|
if (!entry.jsonl_path || !existsSync13(entry.jsonl_path)) {
|
|
130596
|
-
throw new
|
|
130870
|
+
throw new CCLinkerError("E002", `JSONL 文件不存在: ${entry.jsonl_path ?? uuid3}`);
|
|
130597
130871
|
}
|
|
130598
130872
|
const stat = statSync3(entry.jsonl_path);
|
|
130599
130873
|
if (stat.size > MAX_FILE_SIZE) {
|
|
@@ -130706,7 +130980,7 @@ async function exportSession(registry2, target, opts = {}) {
|
|
|
130706
130980
|
await new Promise((resolve, reject) => {
|
|
130707
130981
|
writeStream.on("close", () => {
|
|
130708
130982
|
if (writeError)
|
|
130709
|
-
reject(new
|
|
130983
|
+
reject(new CCLinkerError("E010", `写入文件失败: ${writeError}`));
|
|
130710
130984
|
else
|
|
130711
130985
|
resolve();
|
|
130712
130986
|
});
|
|
@@ -130800,7 +131074,7 @@ async function clean(registry2, opts = {}) {
|
|
|
130800
131074
|
const olderThanDays = opts.olderThan ? (() => {
|
|
130801
131075
|
const n = parseInt(opts.olderThan, 10);
|
|
130802
131076
|
if (isNaN(n))
|
|
130803
|
-
throw new
|
|
131077
|
+
throw new CCLinkerError("E005", `无效的天数: ${opts.olderThan}`);
|
|
130804
131078
|
return n;
|
|
130805
131079
|
})() : undefined;
|
|
130806
131080
|
const cutoff = olderThanDays ? new Date(Date.now() - olderThanDays * 24 * 60 * 60 * 1000).toISOString() : null;
|
|
@@ -130849,7 +131123,7 @@ import { dirname as dirname5, join as join14 } from "path";
|
|
|
130849
131123
|
import { homedir as homedir4 } from "os";
|
|
130850
131124
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
130851
131125
|
async function start(registry2, opts = {}) {
|
|
130852
|
-
if (process.env.
|
|
131126
|
+
if (process.env.CC_LINKER_DAEMON === "1") {
|
|
130853
131127
|
await startDaemonChild(registry2, opts);
|
|
130854
131128
|
return;
|
|
130855
131129
|
}
|
|
@@ -130857,7 +131131,7 @@ async function start(registry2, opts = {}) {
|
|
|
130857
131131
|
if (isRunning()) {
|
|
130858
131132
|
const pid = readPid();
|
|
130859
131133
|
console.log(source_default.yellow(`⚠️ Bot 已在后台运行 (PID: ${pid})`));
|
|
130860
|
-
console.log(source_default.cyan(` 停止: cc-
|
|
131134
|
+
console.log(source_default.cyan(` 停止: cc-linker stop`));
|
|
130861
131135
|
return;
|
|
130862
131136
|
}
|
|
130863
131137
|
await startDaemon();
|
|
@@ -130865,7 +131139,7 @@ async function start(registry2, opts = {}) {
|
|
|
130865
131139
|
}
|
|
130866
131140
|
const sc = new StateCoordinator2;
|
|
130867
131141
|
if (StateCoordinator2.isLocked()) {
|
|
130868
|
-
console.log(source_default.red("❌ Bot 进程正在运行,请先执行 cc-
|
|
131142
|
+
console.log(source_default.red("❌ Bot 进程正在运行,请先执行 cc-linker stop"));
|
|
130869
131143
|
process.exit(1);
|
|
130870
131144
|
}
|
|
130871
131145
|
await startForeground(registry2, opts);
|
|
@@ -130886,7 +131160,7 @@ function readPid() {
|
|
|
130886
131160
|
}
|
|
130887
131161
|
async function stop() {
|
|
130888
131162
|
let stopped = false;
|
|
130889
|
-
const plistPath = join14(homedir4(), "Library", "LaunchAgents", "com.
|
|
131163
|
+
const plistPath = join14(homedir4(), "Library", "LaunchAgents", "com.cclinker.daemon.plist");
|
|
130890
131164
|
if (existsSync23(plistPath)) {
|
|
130891
131165
|
try {
|
|
130892
131166
|
spawnSync2("launchctl", ["unload", plistPath]);
|
|
@@ -130927,7 +131201,7 @@ async function stop() {
|
|
|
130927
131201
|
}
|
|
130928
131202
|
try {
|
|
130929
131203
|
const { execSync } = await import("child_process");
|
|
130930
|
-
const pids = execSync("pgrep -f 'cc-
|
|
131204
|
+
const pids = execSync("pgrep -f 'cc-linker.*daemon' 2>/dev/null || true", { encoding: "utf8" }).trim().split(`
|
|
130931
131205
|
`).filter(Boolean);
|
|
130932
131206
|
for (const pidStr of pids) {
|
|
130933
131207
|
const p = parseInt(pidStr, 10);
|
|
@@ -131140,7 +131414,7 @@ async function createBotRuntime(registry2, log, wsLogLevel) {
|
|
|
131140
131414
|
return { bot, wsClient, stateCoordinator: stateCoordinator3, shutdown };
|
|
131141
131415
|
}
|
|
131142
131416
|
async function startForeground(registry2, opts) {
|
|
131143
|
-
console.log(source_default.blue("\uD83D\uDE80 启动 cc-
|
|
131417
|
+
console.log(source_default.blue("\uD83D\uDE80 启动 cc-linker..."));
|
|
131144
131418
|
const { bot, stateCoordinator: stateCoordinator3, shutdown } = await createBotRuntime(registry2, (level, msg) => {
|
|
131145
131419
|
if (level === "ERROR") {
|
|
131146
131420
|
console.error(source_default.red(msg));
|
|
@@ -131155,7 +131429,7 @@ async function startForeground(registry2, opts) {
|
|
|
131155
131429
|
logger.info(msg);
|
|
131156
131430
|
}
|
|
131157
131431
|
});
|
|
131158
|
-
console.log(source_default.green("✅ cc-
|
|
131432
|
+
console.log(source_default.green("✅ cc-linker 已启动"));
|
|
131159
131433
|
let shuttingDown = false;
|
|
131160
131434
|
const gracefulShutdown = async (signal) => {
|
|
131161
131435
|
if (shuttingDown)
|
|
@@ -131164,7 +131438,7 @@ async function startForeground(registry2, opts) {
|
|
|
131164
131438
|
console.log(source_default.yellow(`
|
|
131165
131439
|
收到 ${signal},优雅停机中...`));
|
|
131166
131440
|
await shutdown(signal);
|
|
131167
|
-
logger.info("cc-
|
|
131441
|
+
logger.info("cc-linker 已停止");
|
|
131168
131442
|
process.exit(0);
|
|
131169
131443
|
};
|
|
131170
131444
|
process.on("SIGINT", () => gracefulShutdown("SIGINT"));
|
|
@@ -131205,13 +131479,13 @@ async function startDaemonChild(registry2, opts) {
|
|
|
131205
131479
|
if (existsSync23(RUNTIME_PID_FILE))
|
|
131206
131480
|
unlinkSync8(RUNTIME_PID_FILE);
|
|
131207
131481
|
} catch {}
|
|
131208
|
-
log("INFO", "cc-
|
|
131482
|
+
log("INFO", "cc-linker 已停止");
|
|
131209
131483
|
process.exit(0);
|
|
131210
131484
|
};
|
|
131211
131485
|
process.on("SIGTERM", () => baseShutdown("SIGTERM"));
|
|
131212
131486
|
process.on("SIGINT", () => baseShutdown("SIGINT"));
|
|
131213
131487
|
const { bot, shutdown } = await createBotRuntime(registry2, log);
|
|
131214
|
-
log("INFO", "cc-
|
|
131488
|
+
log("INFO", "cc-linker daemon started");
|
|
131215
131489
|
const daemonShutdown = async (signal) => {
|
|
131216
131490
|
if (shuttingDown)
|
|
131217
131491
|
return;
|
|
@@ -131222,7 +131496,7 @@ async function startDaemonChild(registry2, opts) {
|
|
|
131222
131496
|
if (existsSync23(RUNTIME_PID_FILE))
|
|
131223
131497
|
unlinkSync8(RUNTIME_PID_FILE);
|
|
131224
131498
|
} catch {}
|
|
131225
|
-
log("INFO", "cc-
|
|
131499
|
+
log("INFO", "cc-linker 已停止");
|
|
131226
131500
|
process.exit(0);
|
|
131227
131501
|
};
|
|
131228
131502
|
process.removeAllListeners("SIGTERM");
|
|
@@ -131239,12 +131513,12 @@ async function startDaemonChild(registry2, opts) {
|
|
|
131239
131513
|
}
|
|
131240
131514
|
function getExecutablePath() {
|
|
131241
131515
|
const argv0 = process.argv[0];
|
|
131242
|
-
if (argv0.endsWith("cc-
|
|
131516
|
+
if (argv0.endsWith("cc-linker"))
|
|
131243
131517
|
return argv0;
|
|
131244
|
-
const distPath = join14(process.cwd(), "dist", "cc-
|
|
131518
|
+
const distPath = join14(process.cwd(), "dist", "cc-linker");
|
|
131245
131519
|
if (existsSync23(distPath))
|
|
131246
131520
|
return distPath;
|
|
131247
|
-
return "cc-
|
|
131521
|
+
return "cc-linker";
|
|
131248
131522
|
}
|
|
131249
131523
|
async function startDaemon() {
|
|
131250
131524
|
const { spawn: spawn2 } = await import("child_process");
|
|
@@ -131253,7 +131527,7 @@ async function startDaemon() {
|
|
|
131253
131527
|
const child = spawn2(exe, args, {
|
|
131254
131528
|
detached: true,
|
|
131255
131529
|
stdio: "ignore",
|
|
131256
|
-
env: { ...process.env,
|
|
131530
|
+
env: { ...process.env, CC_LINKER_DAEMON: "1" }
|
|
131257
131531
|
});
|
|
131258
131532
|
child.unref();
|
|
131259
131533
|
await new Promise((r) => setTimeout(r, 1500));
|
|
@@ -131262,10 +131536,10 @@ async function startDaemon() {
|
|
|
131262
131536
|
process.exit(1);
|
|
131263
131537
|
}
|
|
131264
131538
|
const pid = readPid();
|
|
131265
|
-
console.log(source_default.green(`✅ cc-
|
|
131539
|
+
console.log(source_default.green(`✅ cc-linker 已在后台启动 (PID: ${pid})`));
|
|
131266
131540
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
131267
|
-
console.log(source_default.cyan(` 停止: cc-
|
|
131268
|
-
console.log(source_default.cyan(` 状态: cc-
|
|
131541
|
+
console.log(source_default.cyan(` 停止: cc-linker stop`));
|
|
131542
|
+
console.log(source_default.cyan(` 状态: cc-linker daemon status`));
|
|
131269
131543
|
process.exit(0);
|
|
131270
131544
|
}
|
|
131271
131545
|
|
|
@@ -131282,10 +131556,10 @@ import { spawnSync as spawnSync5 } from "child_process";
|
|
|
131282
131556
|
var IS_MACOS2 = platform2() === "darwin";
|
|
131283
131557
|
var IS_LINUX2 = platform2() === "linux";
|
|
131284
131558
|
function getMacOSPlistPath2() {
|
|
131285
|
-
return join17(homedir7(), "Library", "LaunchAgents", "com.
|
|
131559
|
+
return join17(homedir7(), "Library", "LaunchAgents", "com.cclinker.daemon.plist");
|
|
131286
131560
|
}
|
|
131287
131561
|
function getLinuxServicePath2() {
|
|
131288
|
-
return join17(homedir7(), ".config", "systemd", "user", "cc-
|
|
131562
|
+
return join17(homedir7(), ".config", "systemd", "user", "cc-linker.service");
|
|
131289
131563
|
}
|
|
131290
131564
|
function isDaemonRunning() {
|
|
131291
131565
|
if (!existsSync26(RUNTIME_PID_FILE))
|
|
@@ -131312,7 +131586,7 @@ function disableAutoStartTemporarily() {
|
|
|
131312
131586
|
spawnSync5("launchctl", ["unload", plistPath]);
|
|
131313
131587
|
}
|
|
131314
131588
|
} else if (IS_LINUX2) {
|
|
131315
|
-
spawnSync5("systemctl", ["--user", "stop", "cc-
|
|
131589
|
+
spawnSync5("systemctl", ["--user", "stop", "cc-linker.service"]);
|
|
131316
131590
|
}
|
|
131317
131591
|
}
|
|
131318
131592
|
async function getTenantToken(appId, appSecret) {
|
|
@@ -131437,7 +131711,7 @@ function saveConfig(config3) {
|
|
|
131437
131711
|
`), { mode: 384 });
|
|
131438
131712
|
}
|
|
131439
131713
|
async function initFeishu() {
|
|
131440
|
-
console.log(source_default.blue(`=== cc-
|
|
131714
|
+
console.log(source_default.blue(`=== cc-linker 飞书配置向导 ===
|
|
131441
131715
|
`));
|
|
131442
131716
|
let skipCapture = false;
|
|
131443
131717
|
if (isDaemonRunning()) {
|
|
@@ -131507,7 +131781,7 @@ async function initFeishu() {
|
|
|
131507
131781
|
const { manualId } = await dist_default14.prompt([{
|
|
131508
131782
|
type: "input",
|
|
131509
131783
|
name: "manualId",
|
|
131510
|
-
message: "请输入 owner_open_id(在飞书发送 /
|
|
131784
|
+
message: "请输入 owner_open_id(在飞书发送 /whoami 可获取):",
|
|
131511
131785
|
default: feishu.owner_open_id || undefined,
|
|
131512
131786
|
validate: (v) => v.trim() ? true : "open_id 不能为空"
|
|
131513
131787
|
}]);
|
|
@@ -131539,8 +131813,8 @@ async function initFeishu() {
|
|
|
131539
131813
|
const { defaultCwd } = await dist_default14.prompt([{
|
|
131540
131814
|
type: "input",
|
|
131541
131815
|
name: "defaultCwd",
|
|
131542
|
-
message: "默认工作目录(/
|
|
131543
|
-
default: feishu.default_cwd || "~/Git/cc-
|
|
131816
|
+
message: "默认工作目录(/new 未指定路径时使用):",
|
|
131817
|
+
default: feishu.default_cwd || "~/Git/cc-linker"
|
|
131544
131818
|
}]);
|
|
131545
131819
|
existing.feishu_bot = {
|
|
131546
131820
|
app_id: appId.trim(),
|
|
@@ -131574,12 +131848,12 @@ async function initFeishu() {
|
|
|
131574
131848
|
if (startNow) {
|
|
131575
131849
|
console.log(source_default.cyan(`
|
|
131576
131850
|
启动 Bot 服务...`));
|
|
131577
|
-
let exePath = "cc-
|
|
131851
|
+
let exePath = "cc-linker";
|
|
131578
131852
|
const argv0 = process.argv[0];
|
|
131579
|
-
if (argv0.endsWith("cc-
|
|
131853
|
+
if (argv0.endsWith("cc-linker")) {
|
|
131580
131854
|
exePath = argv0;
|
|
131581
131855
|
} else {
|
|
131582
|
-
const distPath = join17(process.cwd(), "dist", "cc-
|
|
131856
|
+
const distPath = join17(process.cwd(), "dist", "cc-linker");
|
|
131583
131857
|
if (existsSync26(distPath))
|
|
131584
131858
|
exePath = distPath;
|
|
131585
131859
|
}
|
|
@@ -131588,9 +131862,9 @@ async function initFeishu() {
|
|
|
131588
131862
|
});
|
|
131589
131863
|
if (result.status === 0) {
|
|
131590
131864
|
botStarted = true;
|
|
131591
|
-
console.log(source_default.gray("运行 cc-
|
|
131865
|
+
console.log(source_default.gray("运行 cc-linker list 可查看会话"));
|
|
131592
131866
|
} else {
|
|
131593
|
-
console.log(source_default.yellow("⚠️ 自动启动失败,请手动执行: cc-
|
|
131867
|
+
console.log(source_default.yellow("⚠️ 自动启动失败,请手动执行: cc-linker start --daemon"));
|
|
131594
131868
|
}
|
|
131595
131869
|
}
|
|
131596
131870
|
const { autoStart } = await dist_default14.prompt([{
|
|
@@ -131612,10 +131886,10 @@ async function initFeishu() {
|
|
|
131612
131886
|
console.log(source_default.gray(` 开机自启: ${autoStart ? "已配置" : "未配置"}`));
|
|
131613
131887
|
console.log(source_default.cyan(`
|
|
131614
131888
|
常用命令:`));
|
|
131615
|
-
console.log(source_default.white(" cc-
|
|
131616
|
-
console.log(source_default.white(" cc-
|
|
131617
|
-
console.log(source_default.white(" cc-
|
|
131618
|
-
console.log(source_default.white(" cc-
|
|
131889
|
+
console.log(source_default.white(" cc-linker list — 查看会话"));
|
|
131890
|
+
console.log(source_default.white(" cc-linker daemon status — 查看服务状态"));
|
|
131891
|
+
console.log(source_default.white(" cc-linker daemon uninstall — 移除开机自启"));
|
|
131892
|
+
console.log(source_default.white(" cc-linker stop — 停止 Bot 服务"));
|
|
131619
131893
|
process.exit(0);
|
|
131620
131894
|
}
|
|
131621
131895
|
|
|
@@ -131624,151 +131898,8 @@ init_source();
|
|
|
131624
131898
|
init_dist18();
|
|
131625
131899
|
init_scanner();
|
|
131626
131900
|
init_paths();
|
|
131901
|
+
init_init_feishu();
|
|
131627
131902
|
import { existsSync as existsSync29, readFileSync as readFileSync22 } from "fs";
|
|
131628
|
-
|
|
131629
|
-
// src/cli/commands/init-feishu.ts
|
|
131630
|
-
init_source();
|
|
131631
|
-
init_paths();
|
|
131632
|
-
init_toml();
|
|
131633
|
-
import { existsSync as existsSync27, readFileSync as readFileSync20, writeFileSync as writeFileSync18, mkdirSync as mkdirSync14 } from "fs";
|
|
131634
|
-
import { dirname as dirname9 } from "path";
|
|
131635
|
-
import { homedir as homedir8, platform as platform3 } from "os";
|
|
131636
|
-
var IS_MACOS3 = platform3() === "darwin";
|
|
131637
|
-
var IS_LINUX3 = platform3() === "linux";
|
|
131638
|
-
function isDaemonRunning2() {
|
|
131639
|
-
if (!existsSync27(RUNTIME_PID_FILE))
|
|
131640
|
-
return false;
|
|
131641
|
-
try {
|
|
131642
|
-
const pid = parseInt(readFileSync20(RUNTIME_PID_FILE, "utf8").trim(), 10);
|
|
131643
|
-
process.kill(pid, 0);
|
|
131644
|
-
return true;
|
|
131645
|
-
} catch {
|
|
131646
|
-
return false;
|
|
131647
|
-
}
|
|
131648
|
-
}
|
|
131649
|
-
async function getTenantToken2(appId, appSecret) {
|
|
131650
|
-
try {
|
|
131651
|
-
const resp = await fetch("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", {
|
|
131652
|
-
method: "POST",
|
|
131653
|
-
headers: { "Content-Type": "application/json" },
|
|
131654
|
-
body: JSON.stringify({ app_id: appId, app_secret: appSecret })
|
|
131655
|
-
});
|
|
131656
|
-
const data = await resp.json();
|
|
131657
|
-
return data.code === 0 ? data.tenant_access_token : null;
|
|
131658
|
-
} catch {
|
|
131659
|
-
return null;
|
|
131660
|
-
}
|
|
131661
|
-
}
|
|
131662
|
-
async function getBotName2(token) {
|
|
131663
|
-
try {
|
|
131664
|
-
const resp = await fetch("https://open.feishu.cn/open-apis/bot/v3/info", {
|
|
131665
|
-
headers: { Authorization: `Bearer ${token}` }
|
|
131666
|
-
});
|
|
131667
|
-
const data = await resp.json();
|
|
131668
|
-
return data.bot?.app_name ?? null;
|
|
131669
|
-
} catch {
|
|
131670
|
-
return null;
|
|
131671
|
-
}
|
|
131672
|
-
}
|
|
131673
|
-
async function captureOpenId2(appId, appSecret) {
|
|
131674
|
-
const Lark = await Promise.resolve().then(() => __toESM(require_lib5(), 1));
|
|
131675
|
-
const { WSClient, EventDispatcher, Domain, LoggerLevel } = Lark;
|
|
131676
|
-
return new Promise((resolve2) => {
|
|
131677
|
-
let settled = false;
|
|
131678
|
-
let wsClient = null;
|
|
131679
|
-
let timer = null;
|
|
131680
|
-
const settle = (id) => {
|
|
131681
|
-
if (settled)
|
|
131682
|
-
return;
|
|
131683
|
-
settled = true;
|
|
131684
|
-
if (timer)
|
|
131685
|
-
clearTimeout(timer);
|
|
131686
|
-
if (wsClient && typeof wsClient.close === "function") {
|
|
131687
|
-
try {
|
|
131688
|
-
wsClient.close();
|
|
131689
|
-
} catch {}
|
|
131690
|
-
}
|
|
131691
|
-
resolve2(id);
|
|
131692
|
-
};
|
|
131693
|
-
const eventDispatcher = new EventDispatcher({}).register({
|
|
131694
|
-
"im.message.receive_v1": async (data) => {
|
|
131695
|
-
const openId = data?.sender?.sender_id?.open_id;
|
|
131696
|
-
if (openId)
|
|
131697
|
-
settle(openId);
|
|
131698
|
-
}
|
|
131699
|
-
});
|
|
131700
|
-
wsClient = new WSClient({
|
|
131701
|
-
appId,
|
|
131702
|
-
appSecret,
|
|
131703
|
-
domain: Domain.Feishu,
|
|
131704
|
-
loggerLevel: LoggerLevel.warn,
|
|
131705
|
-
autoReconnect: false,
|
|
131706
|
-
onReady: () => {
|
|
131707
|
-
console.log(source_default.green(" ✅ WebSocket 已连接,等待消息..."));
|
|
131708
|
-
},
|
|
131709
|
-
onError: (err) => {
|
|
131710
|
-
console.log(source_default.red(` ❌ 连接失败: ${err.message}`));
|
|
131711
|
-
settle(null);
|
|
131712
|
-
}
|
|
131713
|
-
});
|
|
131714
|
-
wsClient.start({ eventDispatcher });
|
|
131715
|
-
timer = setTimeout(() => {
|
|
131716
|
-
console.log(source_default.yellow(" ⏰ 超时"));
|
|
131717
|
-
settle(null);
|
|
131718
|
-
}, 120000);
|
|
131719
|
-
});
|
|
131720
|
-
}
|
|
131721
|
-
function loadExistingConfig2() {
|
|
131722
|
-
if (!existsSync27(CONFIG_PATH))
|
|
131723
|
-
return {};
|
|
131724
|
-
try {
|
|
131725
|
-
return $parse(readFileSync20(CONFIG_PATH, "utf8"));
|
|
131726
|
-
} catch {
|
|
131727
|
-
return {};
|
|
131728
|
-
}
|
|
131729
|
-
}
|
|
131730
|
-
function formatTomlValue2(v) {
|
|
131731
|
-
if (Array.isArray(v)) {
|
|
131732
|
-
return `[${v.map((item) => JSON.stringify(item)).join(", ")}]`;
|
|
131733
|
-
}
|
|
131734
|
-
if (typeof v === "object" && v !== null) {
|
|
131735
|
-
return JSON.stringify(v);
|
|
131736
|
-
}
|
|
131737
|
-
return JSON.stringify(v);
|
|
131738
|
-
}
|
|
131739
|
-
function saveConfig2(config3) {
|
|
131740
|
-
const dir = dirname9(CONFIG_PATH);
|
|
131741
|
-
mkdirSync14(dir, { recursive: true });
|
|
131742
|
-
const lines = [];
|
|
131743
|
-
for (const section of ["general", "feishu_bot", "queue", "runtime", "security", "scanner", "cli_proxy", "hook"]) {
|
|
131744
|
-
const values = config3[section];
|
|
131745
|
-
if (!values || typeof values !== "object")
|
|
131746
|
-
continue;
|
|
131747
|
-
lines.push(`[${section}]`);
|
|
131748
|
-
for (const [k, v] of Object.entries(values)) {
|
|
131749
|
-
if (v === undefined || v === null)
|
|
131750
|
-
continue;
|
|
131751
|
-
lines.push(`${k} = ${formatTomlValue2(v)}`);
|
|
131752
|
-
}
|
|
131753
|
-
lines.push("");
|
|
131754
|
-
delete config3[section];
|
|
131755
|
-
}
|
|
131756
|
-
for (const [section, values] of Object.entries(config3)) {
|
|
131757
|
-
if (typeof values !== "object" || values === null)
|
|
131758
|
-
continue;
|
|
131759
|
-
lines.push(`[${section}]`);
|
|
131760
|
-
for (const [k, v] of Object.entries(values)) {
|
|
131761
|
-
if (v === undefined || v === null)
|
|
131762
|
-
continue;
|
|
131763
|
-
lines.push(`${k} = ${formatTomlValue2(v)}`);
|
|
131764
|
-
}
|
|
131765
|
-
lines.push("");
|
|
131766
|
-
}
|
|
131767
|
-
writeFileSync18(CONFIG_PATH, lines.join(`
|
|
131768
|
-
`), { mode: 384 });
|
|
131769
|
-
}
|
|
131770
|
-
|
|
131771
|
-
// src/cli/commands/setup.ts
|
|
131772
131903
|
function isHookInstalled3() {
|
|
131773
131904
|
if (!existsSync29(CLAUDE_SETTINGS_PATH))
|
|
131774
131905
|
return false;
|
|
@@ -131776,7 +131907,7 @@ function isHookInstalled3() {
|
|
|
131776
131907
|
const settings = JSON.parse(readFileSync22(CLAUDE_SETTINGS_PATH, "utf8"));
|
|
131777
131908
|
if (!Array.isArray(settings.hooks?.SessionStart))
|
|
131778
131909
|
return false;
|
|
131779
|
-
return settings.hooks.SessionStart.some((matcher) => matcher?.hooks?.some((h) => h?.command?.includes("cc-
|
|
131910
|
+
return settings.hooks.SessionStart.some((matcher) => matcher?.hooks?.some((h) => h?.command?.includes("cc-linker")));
|
|
131780
131911
|
} catch {
|
|
131781
131912
|
return false;
|
|
131782
131913
|
}
|
|
@@ -131784,7 +131915,7 @@ function isHookInstalled3() {
|
|
|
131784
131915
|
async function setup(registry2, opts = {}) {
|
|
131785
131916
|
const totalSteps = opts.skipFeishu ? 2 : 3;
|
|
131786
131917
|
console.log(source_default.blue("═══════════════════════════════════════════"));
|
|
131787
|
-
console.log(source_default.blue(" cc-
|
|
131918
|
+
console.log(source_default.blue(" cc-linker 一键配置向导"));
|
|
131788
131919
|
console.log(source_default.blue(`═══════════════════════════════════════════
|
|
131789
131920
|
`));
|
|
131790
131921
|
console.log(source_default.gray("本向导将引导你完成以下配置:"));
|
|
@@ -131815,7 +131946,7 @@ async function setup(registry2, opts = {}) {
|
|
|
131815
131946
|
hookInstalled = true;
|
|
131816
131947
|
} catch (err) {
|
|
131817
131948
|
console.log(source_default.red(` ❌ Hook 安装失败: ${err}`));
|
|
131818
|
-
console.log(source_default.yellow(" 提示:你可以稍后手动执行 cc-
|
|
131949
|
+
console.log(source_default.yellow(" 提示:你可以稍后手动执行 cc-linker hook install"));
|
|
131819
131950
|
}
|
|
131820
131951
|
}
|
|
131821
131952
|
console.log("");
|
|
@@ -131840,7 +131971,7 @@ async function setup(registry2, opts = {}) {
|
|
|
131840
131971
|
feishuResult = { configured: true, appId: existingAppId, started: isDaemonRunning2(), autoStart: false };
|
|
131841
131972
|
console.log(source_default.green(" ✅ 使用现有飞书配置"));
|
|
131842
131973
|
} else {
|
|
131843
|
-
feishuResult = await runFeishuWizard();
|
|
131974
|
+
feishuResult = await runFeishuWizard(existingAppId, existingAppSecret);
|
|
131844
131975
|
}
|
|
131845
131976
|
} else {
|
|
131846
131977
|
const { setupFeishu } = await dist_default14.prompt([{
|
|
@@ -131858,8 +131989,9 @@ async function setup(registry2, opts = {}) {
|
|
|
131858
131989
|
console.log("");
|
|
131859
131990
|
}
|
|
131860
131991
|
printSummary(sessionCount, hookInstalled, feishuResult);
|
|
131992
|
+
process.exit(0);
|
|
131861
131993
|
}
|
|
131862
|
-
async function runFeishuWizard() {
|
|
131994
|
+
async function runFeishuWizard(existingAppId = "", existingAppSecret = "") {
|
|
131863
131995
|
const result = { configured: false, appId: "", started: false, autoStart: false };
|
|
131864
131996
|
let skipCapture = false;
|
|
131865
131997
|
if (isDaemonRunning2()) {
|
|
@@ -131894,12 +132026,14 @@ async function runFeishuWizard() {
|
|
|
131894
132026
|
type: "input",
|
|
131895
132027
|
name: "appId",
|
|
131896
132028
|
message: "飞书 App ID:",
|
|
132029
|
+
default: existingAppId || undefined,
|
|
131897
132030
|
validate: (v) => v.trim() ? true : "App ID 不能为空"
|
|
131898
132031
|
}]);
|
|
131899
132032
|
const { appSecret } = await dist_default14.prompt([{
|
|
131900
132033
|
type: "input",
|
|
131901
132034
|
name: "appSecret",
|
|
131902
132035
|
message: "飞书 App Secret:",
|
|
132036
|
+
default: existingAppSecret || undefined,
|
|
131903
132037
|
validate: (v) => v.trim() ? true : "App Secret 不能为空"
|
|
131904
132038
|
}]);
|
|
131905
132039
|
console.log(source_default.gray(" 验证凭据..."));
|
|
@@ -131919,7 +132053,7 @@ async function runFeishuWizard() {
|
|
|
131919
132053
|
const { manualId } = await dist_default14.prompt([{
|
|
131920
132054
|
type: "input",
|
|
131921
132055
|
name: "manualId",
|
|
131922
|
-
message: "请输入 owner_open_id(在飞书发送 /
|
|
132056
|
+
message: "请输入 owner_open_id(在飞书发送 /whoami 可获取):",
|
|
131923
132057
|
validate: (v) => v.trim() ? true : "open_id 不能为空"
|
|
131924
132058
|
}]);
|
|
131925
132059
|
openId = manualId.trim();
|
|
@@ -131949,7 +132083,7 @@ async function runFeishuWizard() {
|
|
|
131949
132083
|
const { defaultCwd } = await dist_default14.prompt([{
|
|
131950
132084
|
type: "input",
|
|
131951
132085
|
name: "defaultCwd",
|
|
131952
|
-
message: "默认工作目录(/
|
|
132086
|
+
message: "默认工作目录(/new 未指定路径时使用):",
|
|
131953
132087
|
default: process.env.HOME || "~/Git"
|
|
131954
132088
|
}]);
|
|
131955
132089
|
const existing = loadExistingConfig2();
|
|
@@ -131976,12 +132110,12 @@ async function runFeishuWizard() {
|
|
|
131976
132110
|
const { spawnSync: spawnSync6 } = await import("child_process");
|
|
131977
132111
|
const { join: join18 } = await import("path");
|
|
131978
132112
|
const { existsSync: existsSync30 } = await import("fs");
|
|
131979
|
-
let exePath = "cc-
|
|
132113
|
+
let exePath = "cc-linker";
|
|
131980
132114
|
const argv0 = process.argv[0];
|
|
131981
|
-
if (argv0.endsWith("cc-
|
|
132115
|
+
if (argv0.endsWith("cc-linker")) {
|
|
131982
132116
|
exePath = argv0;
|
|
131983
132117
|
} else {
|
|
131984
|
-
const distPath = join18(process.cwd(), "dist", "cc-
|
|
132118
|
+
const distPath = join18(process.cwd(), "dist", "cc-linker");
|
|
131985
132119
|
if (existsSync30(distPath))
|
|
131986
132120
|
exePath = distPath;
|
|
131987
132121
|
}
|
|
@@ -131990,7 +132124,7 @@ async function runFeishuWizard() {
|
|
|
131990
132124
|
if (result.started) {
|
|
131991
132125
|
console.log(source_default.green(" ✅ Bot 已启动"));
|
|
131992
132126
|
} else {
|
|
131993
|
-
console.log(source_default.yellow(" ⚠️ 自动启动失败,请手动执行: cc-
|
|
132127
|
+
console.log(source_default.yellow(" ⚠️ 自动启动失败,请手动执行: cc-linker start --daemon"));
|
|
131994
132128
|
}
|
|
131995
132129
|
}
|
|
131996
132130
|
const { autoStart } = await dist_default14.prompt([{
|
|
@@ -132044,25 +132178,25 @@ function printSummary(sessionCount, hookInstalled, feishu) {
|
|
|
132044
132178
|
console.log(source_default.gray(` Claude Code 钩子: ${hookInstalled ? "✅ 已安装" : "⏸️ 未安装"}`));
|
|
132045
132179
|
if (feishu.configured) {
|
|
132046
132180
|
console.log(source_default.gray(` 飞书 Bot: ✅ 已配置 (App ID: ${feishu.appId.slice(0, 6)}****)`));
|
|
132047
|
-
console.log(source_default.gray(` Bot 运行: ${feishu.started ? "✅ 运行中" : "⏸️ 未启动 (cc-
|
|
132181
|
+
console.log(source_default.gray(` Bot 运行: ${feishu.started ? "✅ 运行中" : "⏸️ 未启动 (cc-linker start --daemon)"}`));
|
|
132048
132182
|
} else {
|
|
132049
132183
|
console.log(source_default.gray(" 飞书 Bot: ⏸️ 未配置(终端侧功能已就绪)"));
|
|
132050
132184
|
}
|
|
132051
132185
|
console.log("");
|
|
132052
132186
|
console.log(source_default.cyan(" 常用命令:"));
|
|
132053
|
-
console.log(source_default.white(" cc-
|
|
132054
|
-
console.log(source_default.white(" cc-
|
|
132055
|
-
console.log(source_default.white(" cc-
|
|
132056
|
-
console.log(source_default.white(" cc-
|
|
132057
|
-
console.log(source_default.white(" cc-
|
|
132187
|
+
console.log(source_default.white(" cc-linker list — 查看会话"));
|
|
132188
|
+
console.log(source_default.white(" cc-linker resume <ID> — 恢复会话到终端"));
|
|
132189
|
+
console.log(source_default.white(" cc-linker daemon status — 查看 Bot 状态"));
|
|
132190
|
+
console.log(source_default.white(" cc-linker daemon uninstall — 移除开机自启"));
|
|
132191
|
+
console.log(source_default.white(" cc-linker stop — 停止 Bot 服务"));
|
|
132058
132192
|
console.log("");
|
|
132059
132193
|
if (feishu.configured) {
|
|
132060
132194
|
console.log(source_default.cyan(" 飞书端可用命令:"));
|
|
132061
|
-
console.log(source_default.white(" /
|
|
132062
|
-
console.log(source_default.white(" /
|
|
132063
|
-
console.log(source_default.white(" /
|
|
132064
|
-
console.log(source_default.white(" /
|
|
132065
|
-
console.log(source_default.white(" /
|
|
132195
|
+
console.log(source_default.white(" /list — 列出会话"));
|
|
132196
|
+
console.log(source_default.white(" /new [路径] -- 提示 — 创建新会话"));
|
|
132197
|
+
console.log(source_default.white(" /switch <序号> — 切换会话"));
|
|
132198
|
+
console.log(source_default.white(" /model — 管理模型"));
|
|
132199
|
+
console.log(source_default.white(" /status — 查看状态"));
|
|
132066
132200
|
console.log("");
|
|
132067
132201
|
}
|
|
132068
132202
|
}
|
|
@@ -132080,15 +132214,15 @@ var IS_MACOS4 = platform4() === "darwin";
|
|
|
132080
132214
|
var IS_LINUX4 = platform4() === "linux";
|
|
132081
132215
|
function getExecutablePath4() {
|
|
132082
132216
|
const exe = process.argv[0];
|
|
132083
|
-
if (exe.endsWith("cc-
|
|
132217
|
+
if (exe.endsWith("cc-linker"))
|
|
132084
132218
|
return exe;
|
|
132085
|
-
return "cc-
|
|
132219
|
+
return "cc-linker";
|
|
132086
132220
|
}
|
|
132087
132221
|
function getMacOSPlistPath3() {
|
|
132088
|
-
return join18(HOME3, "Library", "LaunchAgents", "com.
|
|
132222
|
+
return join18(HOME3, "Library", "LaunchAgents", "com.cclinker.daemon.plist");
|
|
132089
132223
|
}
|
|
132090
132224
|
function getLinuxServicePath3() {
|
|
132091
|
-
return join18(HOME3, ".config", "systemd", "user", "cc-
|
|
132225
|
+
return join18(HOME3, ".config", "systemd", "user", "cc-linker.service");
|
|
132092
132226
|
}
|
|
132093
132227
|
function generateMacOSPlist2() {
|
|
132094
132228
|
const exe = getExecutablePath4();
|
|
@@ -132098,7 +132232,7 @@ function generateMacOSPlist2() {
|
|
|
132098
132232
|
<plist version="1.0">
|
|
132099
132233
|
<dict>
|
|
132100
132234
|
<key>Label</key>
|
|
132101
|
-
<string>com.
|
|
132235
|
+
<string>com.cclinker.daemon</string>
|
|
132102
132236
|
<key>ProgramArguments</key>
|
|
132103
132237
|
<array>
|
|
132104
132238
|
<string>${exe}</string>
|
|
@@ -132127,7 +132261,7 @@ function generateLinuxService2() {
|
|
|
132127
132261
|
const exe = getExecutablePath4();
|
|
132128
132262
|
const cwd = dirname10(exe) === "." ? process.cwd() : dirname10(exe);
|
|
132129
132263
|
return `[Unit]
|
|
132130
|
-
Description=cc-
|
|
132264
|
+
Description=cc-linker Feishu Bot Daemon
|
|
132131
132265
|
After=network.target
|
|
132132
132266
|
|
|
132133
132267
|
[Service]
|
|
@@ -132145,7 +132279,7 @@ Environment=PATH=/usr/local/bin:/usr/bin:${process.env.PATH || ""}
|
|
|
132145
132279
|
WantedBy=default.target`;
|
|
132146
132280
|
}
|
|
132147
132281
|
async function installDaemon2() {
|
|
132148
|
-
console.log(source_default.blue(`=== cc-
|
|
132282
|
+
console.log(source_default.blue(`=== cc-linker 开机自启配置 ===
|
|
132149
132283
|
`));
|
|
132150
132284
|
if (IS_MACOS4) {
|
|
132151
132285
|
await installMacOS2();
|
|
@@ -132178,15 +132312,15 @@ async function installMacOS2() {
|
|
|
132178
132312
|
if (loadResult.status !== 0 && !loadResult.stderr.toString().includes("already loaded")) {
|
|
132179
132313
|
console.log(source_default.yellow(`⚠️ launchctl load 警告: ${loadResult.stderr.toString().trim()}`));
|
|
132180
132314
|
}
|
|
132181
|
-
const startResult = spawnSync6("launchctl", ["start", "com.
|
|
132315
|
+
const startResult = spawnSync6("launchctl", ["start", "com.cclinker.daemon"]);
|
|
132182
132316
|
console.log(source_default.green("✅ 开机自启已配置"));
|
|
132183
132317
|
console.log(source_default.cyan(` 配置: ${plistPath}`));
|
|
132184
132318
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
132185
132319
|
console.log(source_default.gray(`
|
|
132186
132320
|
操作:`));
|
|
132187
|
-
console.log(source_default.gray(` 停止: launchctl stop com.
|
|
132188
|
-
console.log(source_default.gray(` 卸载: cc-
|
|
132189
|
-
console.log(source_default.gray(` 状态: cc-
|
|
132321
|
+
console.log(source_default.gray(` 停止: launchctl stop com.cclinker.daemon`));
|
|
132322
|
+
console.log(source_default.gray(` 卸载: cc-linker daemon uninstall`));
|
|
132323
|
+
console.log(source_default.gray(` 状态: cc-linker daemon status`));
|
|
132190
132324
|
}
|
|
132191
132325
|
async function installLinux2() {
|
|
132192
132326
|
const servicePath = getLinuxServicePath3();
|
|
@@ -132209,19 +132343,19 @@ async function installLinux2() {
|
|
|
132209
132343
|
if (reloadResult.status !== 0) {
|
|
132210
132344
|
console.log(source_default.yellow(`⚠️ systemctl daemon-reload 警告: ${reloadResult.stderr.toString().trim()}`));
|
|
132211
132345
|
}
|
|
132212
|
-
const enableResult = spawnSync6("systemctl", ["--user", "enable", "cc-
|
|
132346
|
+
const enableResult = spawnSync6("systemctl", ["--user", "enable", "cc-linker.service"]);
|
|
132213
132347
|
if (enableResult.status !== 0) {
|
|
132214
132348
|
console.log(source_default.yellow(`⚠️ systemctl enable 警告: ${enableResult.stderr.toString().trim()}`));
|
|
132215
132349
|
}
|
|
132216
|
-
const startResult = spawnSync6("systemctl", ["--user", "start", "cc-
|
|
132350
|
+
const startResult = spawnSync6("systemctl", ["--user", "start", "cc-linker.service"]);
|
|
132217
132351
|
console.log(source_default.green("✅ 开机自启已配置"));
|
|
132218
132352
|
console.log(source_default.cyan(` 配置: ${servicePath}`));
|
|
132219
132353
|
console.log(source_default.cyan(` 日志: ${RUNTIME_LOG_FILE}`));
|
|
132220
132354
|
console.log(source_default.gray(`
|
|
132221
132355
|
操作:`));
|
|
132222
|
-
console.log(source_default.gray(` 停止: systemctl --user stop cc-
|
|
132223
|
-
console.log(source_default.gray(` 卸载: cc-
|
|
132224
|
-
console.log(source_default.gray(` 状态: cc-
|
|
132356
|
+
console.log(source_default.gray(` 停止: systemctl --user stop cc-linker.service`));
|
|
132357
|
+
console.log(source_default.gray(` 卸载: cc-linker daemon uninstall`));
|
|
132358
|
+
console.log(source_default.gray(` 状态: cc-linker daemon status`));
|
|
132225
132359
|
}
|
|
132226
132360
|
async function uninstallDaemon2() {
|
|
132227
132361
|
console.log(source_default.blue(`=== 卸载开机自启 ===
|
|
@@ -132251,25 +132385,25 @@ async function uninstallLinux2() {
|
|
|
132251
132385
|
console.log(source_default.yellow("⚠️ systemd 配置不存在"));
|
|
132252
132386
|
return;
|
|
132253
132387
|
}
|
|
132254
|
-
spawnSync6("systemctl", ["--user", "disable", "cc-
|
|
132255
|
-
spawnSync6("systemctl", ["--user", "stop", "cc-
|
|
132388
|
+
spawnSync6("systemctl", ["--user", "disable", "cc-linker.service"]);
|
|
132389
|
+
spawnSync6("systemctl", ["--user", "stop", "cc-linker.service"]);
|
|
132256
132390
|
spawnSync6("systemctl", ["--user", "daemon-reload"]);
|
|
132257
132391
|
unlinkSync11(servicePath);
|
|
132258
132392
|
console.log(source_default.green("✅ 开机自启已卸载"));
|
|
132259
132393
|
}
|
|
132260
132394
|
async function daemonStatus3() {
|
|
132261
|
-
console.log(source_default.blue(`=== cc-
|
|
132395
|
+
console.log(source_default.blue(`=== cc-linker 服务状态 ===
|
|
132262
132396
|
`));
|
|
132263
132397
|
if (existsSync30(RUNTIME_PID_FILE)) {
|
|
132264
132398
|
try {
|
|
132265
132399
|
const pid = parseInt(readFileSync23(RUNTIME_PID_FILE, "utf8").trim(), 10);
|
|
132266
132400
|
process.kill(pid, 0);
|
|
132267
|
-
console.log(source_default.green(`✅ cc-
|
|
132401
|
+
console.log(source_default.green(`✅ cc-linker 正在运行 (PID: ${pid})`));
|
|
132268
132402
|
} catch {
|
|
132269
|
-
console.log(source_default.red("❌ cc-
|
|
132403
|
+
console.log(source_default.red("❌ cc-linker PID 存在但进程不存在"));
|
|
132270
132404
|
}
|
|
132271
132405
|
} else {
|
|
132272
|
-
console.log(source_default.yellow("⚠️ cc-
|
|
132406
|
+
console.log(source_default.yellow("⚠️ cc-linker 未在运行"));
|
|
132273
132407
|
}
|
|
132274
132408
|
console.log(source_default.cyan(`
|
|
132275
132409
|
开机自启配置:`));
|
|
@@ -132279,7 +132413,7 @@ async function daemonStatus3() {
|
|
|
132279
132413
|
console.log(source_default.green(` ✅ launchd 已配置 (${plistPath})`));
|
|
132280
132414
|
} else {
|
|
132281
132415
|
console.log(source_default.gray(" ️ 未配置 launchd"));
|
|
132282
|
-
console.log(source_default.gray(" 执行: cc-
|
|
132416
|
+
console.log(source_default.gray(" 执行: cc-linker daemon install"));
|
|
132283
132417
|
}
|
|
132284
132418
|
} else if (IS_LINUX4) {
|
|
132285
132419
|
const servicePath = getLinuxServicePath3();
|
|
@@ -132287,7 +132421,7 @@ async function daemonStatus3() {
|
|
|
132287
132421
|
console.log(source_default.green(` ✅ systemd 已配置 (${servicePath})`));
|
|
132288
132422
|
} else {
|
|
132289
132423
|
console.log(source_default.gray(" ⏸️ 未配置 systemd"));
|
|
132290
|
-
console.log(source_default.gray(" 执行: cc-
|
|
132424
|
+
console.log(source_default.gray(" 执行: cc-linker daemon install"));
|
|
132291
132425
|
}
|
|
132292
132426
|
}
|
|
132293
132427
|
if (existsSync30(RUNTIME_LOG_FILE)) {
|
|
@@ -132306,7 +132440,7 @@ async function daemonStatus3() {
|
|
|
132306
132440
|
|
|
132307
132441
|
// src/index.ts
|
|
132308
132442
|
var program2 = new Command;
|
|
132309
|
-
program2.name("cc-
|
|
132443
|
+
program2.name("cc-linker").description("\u98DE\u4E66 \u2194 Claude Code CLI \u6865\u63A5\u5DE5\u5177").version("0.2.0");
|
|
132310
132444
|
async function withSync(fn, skipSync = false) {
|
|
132311
132445
|
const registry2 = new RegistryManager;
|
|
132312
132446
|
if (!skipSync) {
|
|
@@ -132356,6 +132490,10 @@ program2.command("start").description("\u542F\u52A8\u98DE\u4E66 Bot \u8FDB\u7A0B
|
|
|
132356
132490
|
await start(registry2, { daemon: opts.daemon });
|
|
132357
132491
|
}, true));
|
|
132358
132492
|
program2.command("stop").description("\u505C\u6B62\u540E\u53F0\u8FD0\u884C\u7684 Bot \u670D\u52A1").action(() => stop());
|
|
132493
|
+
program2.command("restart").description("\u91CD\u542F Bot \u670D\u52A1\uFF08\u5148 stop \u518D start --daemon\uFF09").action(() => withSync(async (registry2) => {
|
|
132494
|
+
const { restart: restart2 } = await Promise.resolve().then(() => (init_restart(), exports_restart));
|
|
132495
|
+
await restart2(registry2);
|
|
132496
|
+
}, true));
|
|
132359
132497
|
var daemonCmd = program2.command("daemon").description("\u7BA1\u7406\u540E\u53F0 Bot \u670D\u52A1\uFF08\u5F00\u673A\u81EA\u542F\uFF09");
|
|
132360
132498
|
daemonCmd.command("install").description("\u914D\u7F6E\u5F00\u673A\u81EA\u52A8\u542F\u52A8").action(() => installDaemon2());
|
|
132361
132499
|
daemonCmd.command("uninstall").description("\u79FB\u9664\u5F00\u673A\u81EA\u52A8\u542F\u52A8").action(() => uninstallDaemon2());
|
|
@@ -132366,5 +132504,5 @@ program2.command("setup").description("\u4E00\u952E\u914D\u7F6E\u5411\u5BFC\uFF0
|
|
|
132366
132504
|
program2.command("init-feishu").description("\u4EA4\u4E92\u5F0F\u914D\u7F6E\u98DE\u4E66\u96C6\u6210\uFF08App ID + App Secret + Owner\uFF09").action(() => initFeishu());
|
|
132367
132505
|
program2.parseAsync(process.argv).catch(handleError);
|
|
132368
132506
|
|
|
132369
|
-
//# debugId=
|
|
132507
|
+
//# debugId=B09E898E9174471664756E2164756E21
|
|
132370
132508
|
//# sourceMappingURL=index.js.map
|