klaus-ai 0.1.40 → 0.1.41
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.
|
@@ -76,7 +76,10 @@ function daemonize() {
|
|
|
76
76
|
const child = spawn(process.execPath, [...scriptArgs, "--foreground"], {
|
|
77
77
|
detached: true,
|
|
78
78
|
stdio: ["ignore", logFd, logFd],
|
|
79
|
-
env: {
|
|
79
|
+
env: {
|
|
80
|
+
...process.env,
|
|
81
|
+
PATH: [process.env.PATH, "/opt/homebrew/bin", "/usr/local/bin"].filter(Boolean).join(":")
|
|
82
|
+
}
|
|
80
83
|
});
|
|
81
84
|
child.unref();
|
|
82
85
|
const childPid = child.pid;
|
|
@@ -337,4 +340,4 @@ export {
|
|
|
337
340
|
tailLogs,
|
|
338
341
|
uninstallLaunchAgent
|
|
339
342
|
};
|
|
340
|
-
//# sourceMappingURL=daemon-
|
|
343
|
+
//# sourceMappingURL=daemon-DXK2GXWU.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon.ts"],"sourcesContent":["/**\n * Daemon management — PID file, log rotation, process lifecycle.\n *\n * `klaus start` → fork child in background, parent exits immediately\n * `klaus start -f` → run in foreground (current behavior)\n * `klaus stop` → send SIGTERM to daemon\n * `klaus status` → check if daemon is running\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n openSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { CONFIG_DIR } from \"./config.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst PID_FILE = join(CONFIG_DIR, \"klaus.pid\");\nconst LOG_DIR = join(CONFIG_DIR, \"logs\");\nconst LOG_FILE = join(LOG_DIR, \"klaus.log\");\n\n// ---------------------------------------------------------------------------\n// PID helpers\n// ---------------------------------------------------------------------------\n\nfunction readPid(): number | null {\n if (!existsSync(PID_FILE)) return null;\n const raw = readFileSync(PID_FILE, \"utf-8\").trim();\n const pid = parseInt(raw, 10);\n return Number.isFinite(pid) ? pid : null;\n}\n\n/** Atomic PID write using exclusive create ('wx') to prevent races. */\nfunction writePidExclusive(pid: number): boolean {\n mkdirSync(CONFIG_DIR, { recursive: true });\n try {\n writeFileSync(PID_FILE, String(pid), { mode: 0o644, flag: \"wx\" });\n return true;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") return false;\n throw err;\n }\n}\n\n/** Overwrite PID file (used after stale PID cleanup). */\nfunction writePid(pid: number): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(PID_FILE, String(pid), { mode: 0o644 });\n}\n\nfunction removePid(): void {\n try {\n unlinkSync(PID_FILE);\n } catch {\n // ignore if already gone\n }\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0); // signal 0 = existence check\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Fork `klaus start --foreground` as a detached background process.\n * Redirects stdout/stderr to the log file.\n * The parent process exits after the child is spawned.\n */\nexport function daemonize(): void {\n mkdirSync(LOG_DIR, { recursive: true });\n\n // Try atomic PID file creation first (prevents race between concurrent starts)\n const existingPid = readPid();\n if (existingPid !== null) {\n if (isProcessRunning(existingPid)) {\n console.log(`Klaus is already running (PID ${existingPid}).`);\n console.log(`Log: ${LOG_FILE}`);\n process.exit(0);\n }\n // Stale PID file — remove and retry\n removePid();\n }\n\n // Reserve PID file atomically BEFORE spawning to prevent race conditions.\n // Write a placeholder (parent PID) — will be overwritten with child PID.\n if (!writePidExclusive(process.pid)) {\n // Another daemonize() call won the race\n console.log(\"Klaus is already starting from another process.\");\n process.exit(0);\n }\n\n // Open log file for append\n const logFd = openSync(LOG_FILE, \"a\");\n\n // Re-spawn ourselves with --foreground\n const scriptArgs = getScriptArgs();\n const child = spawn(process.execPath, [...scriptArgs, \"--foreground\"], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n env: {\n ...process.env,\n PATH: [process.env.PATH, \"/opt/homebrew/bin\", \"/usr/local/bin\"]\n .filter(Boolean)\n .join(\":\"),\n },\n });\n\n child.unref();\n\n const childPid = child.pid;\n if (childPid == null) {\n removePid();\n console.error(\"Failed to start daemon.\");\n process.exit(1);\n }\n\n // Overwrite placeholder with actual child PID\n writePid(childPid);\n\n console.log(`Klaus started in background (PID ${childPid}).`);\n console.log(`Log: ${LOG_FILE}`);\n process.exit(0);\n}\n\n/**\n * Write PID file for foreground mode (so `klaus stop` still works).\n * Registers cleanup on exit.\n */\nexport function registerForegroundPid(): void {\n // Prevent overwriting an active daemon's PID\n const existing = readPid();\n if (\n existing !== null &&\n isProcessRunning(existing) &&\n existing !== process.pid\n ) {\n console.error(\n `Klaus is already running as daemon (PID ${existing}). Stop it first with: klaus stop`,\n );\n process.exit(1);\n }\n writePid(process.pid);\n let cleaned = false;\n const cleanup = () => {\n if (cleaned) return;\n cleaned = true;\n removePid();\n };\n process.on(\"exit\", cleanup);\n process.on(\"SIGINT\", () => {\n cleanup();\n process.exit(0);\n });\n process.on(\"SIGTERM\", () => {\n cleanup();\n process.exit(0);\n });\n}\n\n/**\n * Stop a running daemon by sending SIGTERM.\n * Waits up to 5 seconds for the process to exit before giving up.\n */\nexport async function stopDaemon(): Promise<void> {\n const pid = readPid();\n if (pid === null) {\n console.log(\"Klaus is not running (no PID file found).\");\n return;\n }\n\n if (!isProcessRunning(pid)) {\n console.log(`Klaus is not running (stale PID ${pid}). Cleaning up.`);\n removePid();\n return;\n }\n\n try {\n process.kill(pid, \"SIGTERM\");\n console.log(`Sent SIGTERM to Klaus (PID ${pid}). Waiting for exit...`);\n } catch (err) {\n console.error(`Failed to stop Klaus (PID ${pid}):`, err);\n process.exit(1);\n }\n\n // Poll until process exits (up to 5s)\n const deadline = Date.now() + 5_000;\n while (Date.now() < deadline) {\n if (!isProcessRunning(pid)) {\n removePid();\n console.log(\"Klaus stopped.\");\n return;\n }\n await sleep(200);\n }\n\n console.log(`Klaus (PID ${pid}) did not exit within 5s. PID file kept.`);\n}\n\n/**\n * Print daemon status.\n */\nexport function showStatus(): void {\n const pid = readPid();\n if (pid === null) {\n console.log(\"Klaus is not running.\");\n return;\n }\n\n if (isProcessRunning(pid)) {\n console.log(`Klaus is running (PID ${pid}).`);\n console.log(`Log: ${LOG_FILE}`);\n } else {\n console.log(`Klaus is not running (stale PID ${pid}). Cleaning up.`);\n removePid();\n }\n}\n\n/**\n * Tail the daemon log file (like `tail -f`).\n */\nexport function tailLogs(): void {\n if (!existsSync(LOG_FILE)) {\n console.log(\"No log file found. Is Klaus running?\");\n process.exit(1);\n }\n\n const tail = spawn(\"tail\", [\"-f\", LOG_FILE], {\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n });\n\n tail.on(\"error\", (err) => {\n console.error(\"Failed to tail logs:\", err.message);\n process.exit(1);\n });\n\n process.once(\"SIGINT\", () => {\n tail.kill(\"SIGINT\");\n process.exit(0);\n });\n process.once(\"SIGTERM\", () => {\n tail.kill(\"SIGTERM\");\n process.exit(0);\n });\n\n tail.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Reconstruct script args for re-spawn: [scriptPath, \"start\"] */\nfunction getScriptArgs(): string[] {\n const scriptPath = process.argv[1];\n if (scriptPath.endsWith(\".ts\")) {\n console.error(\n \"Daemon mode is not supported in dev (tsx). Use --foreground (-f) instead.\",\n );\n process.exit(1);\n }\n return [scriptPath, \"start\"];\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ---------------------------------------------------------------------------\n// launchd integration (macOS only)\n// ---------------------------------------------------------------------------\n\nconst LAUNCHD_LABEL = \"ai.klaus.daemon\";\nconst LAUNCHD_DIR = join(homedir(), \"Library\", \"LaunchAgents\");\nconst LAUNCHD_PLIST = join(LAUNCHD_DIR, `${LAUNCHD_LABEL}.plist`);\n\nfunction findKlausBinary(): string | null {\n // 1. If running from built dist, use the script path directly\n const scriptPath = process.argv[1];\n if (scriptPath && !scriptPath.endsWith(\".ts\") && existsSync(scriptPath)) {\n return scriptPath;\n }\n // 2. Try common global install locations\n const candidates = [\n join(homedir(), \".npm-global\", \"bin\", \"klaus\"),\n \"/usr/local/bin/klaus\",\n \"/opt/homebrew/bin/klaus\",\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n return null;\n}\n\nfunction buildPlist(binPath: string, port: number): string {\n const logPath = LOG_FILE;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${process.execPath}</string>\n <string>${binPath}</string>\n <string>start</string>\n <string>--foreground</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>StandardOutPath</key>\n <string>${logPath}</string>\n <key>StandardErrorPath</key>\n <string>${logPath}</string>\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>\n <key>HOME</key>\n <string>${homedir()}</string>\n <key>KLAUS_WEB_PORT</key>\n <string>${port}</string>\n </dict>\n</dict>\n</plist>`;\n}\n\n/**\n * Install a launchd plist so Klaus starts automatically on login.\n */\nexport function installLaunchAgent(port = 3000): void {\n if (process.platform !== \"darwin\") {\n console.error(\"launchd is only available on macOS.\");\n process.exit(1);\n }\n\n const binPath = findKlausBinary();\n if (!binPath) {\n console.error(\n \"Could not find the klaus binary. Install globally first: npm install -g klaus-ai\",\n );\n process.exit(1);\n }\n\n mkdirSync(LAUNCHD_DIR, { recursive: true });\n mkdirSync(LOG_DIR, { recursive: true });\n\n const plist = buildPlist(binPath, port);\n writeFileSync(LAUNCHD_PLIST, plist, \"utf-8\");\n\n // Load the agent\n try {\n // Unload first (ignore errors if not loaded)\n try {\n execFileSync(\"launchctl\", [\"unload\", LAUNCHD_PLIST], { stdio: \"ignore\" });\n } catch {\n /* ok */\n }\n execFileSync(\"launchctl\", [\"load\", LAUNCHD_PLIST], { stdio: \"inherit\" });\n } catch {\n console.error(\n \"Failed to load launchd agent. You may need to load it manually:\",\n );\n console.error(` launchctl load \"${LAUNCHD_PLIST}\"`);\n }\n\n console.log(`Installed launchd agent: ${LAUNCHD_LABEL}`);\n console.log(`Plist: ${LAUNCHD_PLIST}`);\n console.log(`Klaus will start automatically on login (port ${port}).`);\n}\n\n/**\n * Uninstall the launchd plist.\n */\nexport function uninstallLaunchAgent(): void {\n if (process.platform !== \"darwin\") {\n console.error(\"launchd is only available on macOS.\");\n process.exit(1);\n }\n\n if (!existsSync(LAUNCHD_PLIST)) {\n console.log(\"No launchd agent installed.\");\n return;\n }\n\n try {\n execFileSync(\"launchctl\", [\"unload\", LAUNCHD_PLIST], { stdio: \"ignore\" });\n } catch {\n /* ok if not loaded */\n }\n\n unlinkSync(LAUNCHD_PLIST);\n console.log(`Uninstalled launchd agent: ${LAUNCHD_LABEL}`);\n}\n\n/**\n * Machine-readable status output for the macOS app.\n */\nexport function showStatusJson(): void {\n const pid = readPid();\n const running = pid !== null && isProcessRunning(pid);\n\n // Clean up stale PID\n if (pid !== null && !running) {\n removePid();\n }\n\n const status = {\n running,\n pid: running ? pid : null,\n logFile: LOG_FILE,\n pidFile: PID_FILE,\n configDir: CONFIG_DIR,\n launchAgent: existsSync(LAUNCHD_PLIST) ? LAUNCHD_PLIST : null,\n version: getVersion(),\n };\n\n console.log(JSON.stringify(status));\n}\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version ?? \"unknown\";\n }\n } catch {\n /* ignore */\n }\n return \"unknown\";\n}\n"],"mappings":"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","names":[]}
|
package/dist/index.js
CHANGED
|
@@ -151,7 +151,8 @@ var ClaudeChat = class {
|
|
|
151
151
|
...this.options.oneProxyBaseUrl ? {
|
|
152
152
|
env: {
|
|
153
153
|
...process.env,
|
|
154
|
-
ANTHROPIC_BASE_URL: this.options.oneProxyBaseUrl
|
|
154
|
+
ANTHROPIC_BASE_URL: this.options.oneProxyBaseUrl,
|
|
155
|
+
ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY || "sk-oneproxy"
|
|
155
156
|
}
|
|
156
157
|
} : {}
|
|
157
158
|
}
|
|
@@ -1304,7 +1305,7 @@ async function main() {
|
|
|
1304
1305
|
case "start": {
|
|
1305
1306
|
const flags = process.argv.slice(3);
|
|
1306
1307
|
const foreground = flags.includes("--foreground") || flags.includes("-f");
|
|
1307
|
-
const daemon = await import("./daemon-
|
|
1308
|
+
const daemon = await import("./daemon-DXK2GXWU.js");
|
|
1308
1309
|
if (foreground) {
|
|
1309
1310
|
daemon.registerForegroundPid();
|
|
1310
1311
|
await start();
|
|
@@ -1314,12 +1315,12 @@ async function main() {
|
|
|
1314
1315
|
break;
|
|
1315
1316
|
}
|
|
1316
1317
|
case "stop": {
|
|
1317
|
-
const daemon = await import("./daemon-
|
|
1318
|
+
const daemon = await import("./daemon-DXK2GXWU.js");
|
|
1318
1319
|
await daemon.stopDaemon();
|
|
1319
1320
|
break;
|
|
1320
1321
|
}
|
|
1321
1322
|
case "status": {
|
|
1322
|
-
const daemon = await import("./daemon-
|
|
1323
|
+
const daemon = await import("./daemon-DXK2GXWU.js");
|
|
1323
1324
|
const flags = process.argv.slice(3);
|
|
1324
1325
|
if (flags.includes("--json")) {
|
|
1325
1326
|
daemon.showStatusJson();
|
|
@@ -1329,13 +1330,13 @@ async function main() {
|
|
|
1329
1330
|
break;
|
|
1330
1331
|
}
|
|
1331
1332
|
case "logs": {
|
|
1332
|
-
const daemon = await import("./daemon-
|
|
1333
|
+
const daemon = await import("./daemon-DXK2GXWU.js");
|
|
1333
1334
|
daemon.tailLogs();
|
|
1334
1335
|
break;
|
|
1335
1336
|
}
|
|
1336
1337
|
case "daemon": {
|
|
1337
1338
|
const sub = process.argv[3];
|
|
1338
|
-
const daemon = await import("./daemon-
|
|
1339
|
+
const daemon = await import("./daemon-DXK2GXWU.js");
|
|
1339
1340
|
if (sub === "install") {
|
|
1340
1341
|
const portFlag = process.argv.find((a) => a.startsWith("--port="));
|
|
1341
1342
|
const port = portFlag ? parseInt(portFlag.slice(7), 10) : 3e3;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/message.ts","../src/cron-marker.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { webPlugin } from \"./channels/web.js\";\nimport {\n registerChannel,\n getChannel,\n type ChannelPlugin,\n} from \"./channels/types.js\";\nimport {\n getChannelNames,\n CONFIG_FILE,\n loadSessionConfig,\n loadTranscriptsConfig,\n loadCronConfig,\n loadOneProxyConfig,\n} from \"./config.js\";\nimport { ensureConfigValid } from \"./config-validate.js\";\nimport { ChatSessionManager } from \"./core.js\";\nimport { t } from \"./i18n.js\";\nimport {\n type InboundMessage,\n formatPrompt,\n formatDisplayText,\n} from \"./message.js\";\nimport {\n loadEnabledSkills,\n listSkillNames,\n applySkillEnvOverrides,\n} from \"./skills/index.js\";\nimport type {\n ToolEventCallback,\n StreamChunkCallback,\n PermissionRequestCallback,\n} from \"./types.js\";\nimport { parseCronMarkers, type CronMarkerAction } from \"./cron-marker.js\";\nimport { generateLocalToken, generateExecToken } from \"./local-token.js\";\n\n// ---------------------------------------------------------------------------\n// Channel registration\n// ---------------------------------------------------------------------------\n\nregisterChannel(webPlugin);\n\n// ---------------------------------------------------------------------------\n// Model alias mapping\n// ---------------------------------------------------------------------------\n\nconst MODEL_ALIASES: Record<string, string> = {\n sonnet: \"claude-sonnet-4-6\",\n opus: \"claude-opus-4-6\",\n haiku: \"claude-haiku-4-5-20251001\",\n};\n\nasync function start(): Promise<void> {\n if (!existsSync(CONFIG_FILE)) {\n console.log(\"No config found. Starting setup wizard...\\n\");\n const { runSetup } = await import(\"./setup-wizard.js\");\n await runSetup();\n if (!existsSync(CONFIG_FILE)) return;\n }\n\n // Validate config before attempting to connect (fail-fast)\n ensureConfigValid();\n\n // Generate local token for macOS app authentication\n generateLocalToken();\n\n // Generate exec approval token for macOS app command approval\n generateExecToken();\n\n // Apply skill environment overrides (scoped to process lifetime)\n applySkillEnvOverrides();\n\n const channelNames = getChannelNames();\n const plugins: ChannelPlugin[] = [];\n for (const name of channelNames) {\n const plugin = getChannel(name);\n if (!plugin) {\n console.error(`Internal error: channel \"${name}\" not registered.`);\n process.exit(1);\n }\n plugins.push(plugin);\n }\n\n // Initialize session persistence\n const sessionCfg = loadSessionConfig();\n const { SessionStore } = await import(\"./session-store.js\");\n const store = new SessionStore();\n await store.load();\n store.pruneStale(sessionCfg.maxAgeMs);\n store.capEntries(sessionCfg.maxEntries);\n await store.save();\n\n // Initialize message persistence (JSONL transcripts)\n const { MessageStore } = await import(\"./message-store.js\");\n const messageStore = new MessageStore(loadTranscriptsConfig());\n messageStore.prune();\n\n // Initialize per-user memory store\n const { MemoryStore } = await import(\"./memory-store.js\");\n const memoryStore = new MemoryStore();\n\n const sessions = new ChatSessionManager(\n store,\n sessionCfg.idleMs,\n messageStore,\n memoryStore,\n );\n\n // Build delivery registry from active channel plugins (needed by cron)\n const deliverers = new Map<\n string,\n (to: string, text: string) => Promise<void>\n >();\n for (const p of plugins) {\n if (p.deliver) {\n deliverers.set(p.meta.id, p.deliver);\n }\n }\n\n // Initialize cron scheduler (eagerly if configured, lazily on first tool call)\n let cronScheduler: import(\"./cron.js\").CronScheduler | null = null;\n const cronCfg = loadCronConfig();\n\n /** Lazy-init with Promise cache to prevent concurrent double-init. */\n let schedulerPromise: Promise<import(\"./cron.js\").CronScheduler> | null =\n null;\n const ensureCronScheduler = (): Promise<\n import(\"./cron.js\").CronScheduler\n > => {\n schedulerPromise ??= (async () => {\n const { CronScheduler } = await import(\"./cron.js\");\n cronScheduler = new CronScheduler(\n { ...cronCfg, enabled: true },\n sessions,\n deliverers,\n store,\n );\n cronScheduler.start();\n console.log(\"[Cron] Scheduler started\");\n // Expose to web admin API if web channel is active\n if (channelNames.includes(\"web\")) {\n const { setCronScheduler } = await import(\"./channels/web.js\");\n setCronScheduler(cronScheduler);\n }\n return cronScheduler;\n })();\n return schedulerPromise;\n };\n\n // Eagerly start if cron is configured\n if (cronCfg.enabled) {\n await ensureCronScheduler();\n }\n\n // Always inject cron MCP tool so Claude can create tasks via tool_use.\n // Uses lazy scheduler init — first tool call starts the scheduler if needed.\n {\n const { createCronMcpServer } = await import(\"./cron-tool.js\");\n const cronMcp = createCronMcpServer({\n get scheduler() {\n return cronScheduler;\n },\n ensureScheduler: ensureCronScheduler,\n });\n const { createSendFileMcpServer } = await import(\"./send-file-tool.js\");\n const sendFileMcp = createSendFileMcpServer();\n\n // Skill registry tool (lazy init — first tool call creates RegistryManager)\n const { createSkillRegistryMcpServer } =\n await import(\"./skill-registry-tool.js\");\n let registryManager:\n | import(\"./skills/registry/registry-manager.js\").RegistryManager\n | null = null;\n const skillRegistryMcp = createSkillRegistryMcpServer({\n get manager() {\n return registryManager;\n },\n async ensureManager() {\n if (registryManager) return registryManager;\n const { RegistryManager } =\n await import(\"./skills/registry/registry-manager.js\");\n const { loadRegistryConfigs } = await import(\"./config.js\");\n registryManager = new RegistryManager(loadRegistryConfigs());\n return registryManager;\n },\n });\n\n sessions.setMcpServers({\n \"klaus-cron\": cronMcp,\n \"klaus-send-file\": sendFileMcp,\n \"klaus-skill-registry\": skillRegistryMcp,\n });\n }\n\n // Expose stores to web channel for API endpoints\n let inviteStoreInstance: { close(): void } | null = null;\n let userStoreInstance: { close(): void } | null = null;\n if (channelNames.includes(\"web\")) {\n const {\n setMessageStore,\n setInviteStore,\n setSessionStore,\n setUserStore,\n setChatManager,\n setCronScheduler,\n } = await import(\"./channels/web.js\");\n setMessageStore(messageStore);\n setSessionStore(store);\n setChatManager(sessions);\n // Expose cron scheduler to web admin API (may be null initially, set after init)\n if (cronScheduler) {\n setCronScheduler(cronScheduler);\n }\n\n const { InviteStore } = await import(\"./invite-store.js\");\n const inviteStore = new InviteStore();\n setInviteStore(inviteStore);\n inviteStoreInstance = inviteStore;\n\n const { UserStore } = await import(\"./user-store.js\");\n const { loadWebConfig } = await import(\"./config.js\");\n const webCfg = loadWebConfig();\n const sessionMaxAgeMs = webCfg.sessionMaxAgeDays * 24 * 60 * 60 * 1000;\n const userStore = new UserStore(undefined, sessionMaxAgeMs);\n setUserStore(userStore);\n userStoreInstance = userStore;\n\n // Prune expired auth sessions on startup\n const pruned = userStore.pruneExpiredSessions();\n if (pruned > 0) {\n console.log(`[UserStore] Pruned ${pruned} expired auth session(s)`);\n }\n }\n\n const handler = async (\n msg: InboundMessage,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string | null> => {\n const trimmed = msg.text.trim();\n\n // /new, /reset, /clear — reset conversation\n if ([\"/new\", \"/reset\", \"/clear\"].includes(trimmed)) {\n await sessions.reset(msg.sessionKey);\n return t(\"cmd_reset\");\n }\n\n // /help — list commands\n if (trimmed === \"/help\") {\n return t(\"cmd_help\");\n }\n\n // /session — show session info\n if (trimmed === \"/session\") {\n const info = sessions.getSessionInfo(msg.sessionKey);\n return t(\"cmd_session_info\", {\n key: msg.sessionKey,\n status: info.busy ? t(\"cmd_session_active\") : t(\"cmd_session_idle\"),\n model: info.model ?? t(\"cmd_default_model\"),\n });\n }\n\n // /skills — list enabled skills\n if (trimmed === \"/skills\") {\n const enabled = loadEnabledSkills();\n if (enabled.length === 0) {\n return t(\"cmd_skills_none\", {\n available: listSkillNames().join(\", \"),\n });\n }\n const list = enabled\n .map((s) => {\n const emoji = s.metadata?.emoji ? `${s.metadata.emoji} ` : \"\";\n const src = s.source === \"user\" ? \" (user)\" : \"\";\n return ` ${emoji}${s.name} — ${s.description}${src}`;\n })\n .join(\"\\n\");\n return t(\"cmd_skills_list\", { list, count: String(enabled.length) });\n }\n\n // /cron [subcommand] — cron task management\n if (trimmed === \"/cron\" || trimmed.startsWith(\"/cron \")) {\n const scheduler = await ensureCronScheduler();\n return handleCronCommand(trimmed, scheduler);\n }\n\n // /model [name] — show or switch model\n if (trimmed === \"/model\" || trimmed.startsWith(\"/model \")) {\n const arg = trimmed.slice(\"/model\".length).trim();\n if (!arg) {\n const current = sessions.getModel(msg.sessionKey);\n return t(\"cmd_model_current\", {\n model: current ?? t(\"cmd_default_model\"),\n });\n }\n const opCfg = loadOneProxyConfig();\n if (opCfg.enabled) {\n // OneProxy mode: use model name as-is (no alias resolution)\n sessions.setModel(msg.sessionKey, arg);\n return t(\"cmd_model_switched\", { model: arg });\n }\n const resolved = MODEL_ALIASES[arg.toLowerCase()] ?? MODEL_ALIASES[arg];\n if (!resolved) {\n return t(\"cmd_model_unknown\", { name: arg });\n }\n sessions.setModel(msg.sessionKey, resolved);\n return t(\"cmd_model_switched\", { model: resolved });\n }\n\n const prompt = formatPrompt(msg);\n if (!prompt) return null;\n const display = formatDisplayText(msg);\n const reply = await sessions.chat(\n msg.sessionKey,\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n display,\n );\n\n // Post-process: extract and execute [[cron:...]] markers\n if (reply) {\n const { text, actions } = parseCronMarkers(reply);\n if (actions.length > 0) {\n const scheduler = await ensureCronScheduler();\n executeCronActions(actions, scheduler);\n return text || null;\n }\n }\n\n return reply;\n };\n\n try {\n // Start all channels in parallel (each blocks forever).\n // If any rejects, Promise.all rejects → finally runs → process.exit(1) in main().\n await Promise.all(plugins.map((p) => p.start(handler)));\n } finally {\n (cronScheduler as import(\"./cron.js\").CronScheduler | null)?.stop();\n await sessions.close();\n inviteStoreInstance?.close();\n userStoreInstance?.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cron marker execution (AI-driven task management)\n// ---------------------------------------------------------------------------\n\nfunction executeCronActions(\n actions: readonly CronMarkerAction[],\n scheduler: import(\"./cron.js\").CronScheduler,\n): void {\n for (const action of actions) {\n try {\n switch (action.action) {\n case \"add\":\n scheduler.addTask(action.task);\n console.log(`[CronMarker] Added task \"${action.task.id}\"`);\n break;\n case \"edit\":\n if (scheduler.editTask(action.id, action.patch)) {\n console.log(`[CronMarker] Edited task \"${action.id}\"`);\n } else {\n console.warn(`[CronMarker] Task \"${action.id}\" not found for edit`);\n }\n break;\n case \"remove\":\n if (scheduler.removeTask(action.id)) {\n console.log(`[CronMarker] Removed task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for remove`,\n );\n }\n break;\n case \"enable\":\n if (scheduler.editTask(action.id, { enabled: true })) {\n console.log(`[CronMarker] Enabled task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for enable`,\n );\n }\n break;\n case \"disable\":\n if (scheduler.editTask(action.id, { enabled: false })) {\n console.log(`[CronMarker] Disabled task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for disable`,\n );\n }\n break;\n }\n } catch (err) {\n console.error(`[CronMarker] Failed to execute ${action.action}:`, err);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// /cron subcommand handler\n// ---------------------------------------------------------------------------\n\nconst SAFE_CRON_ID_RE = /^[a-zA-Z0-9._-]+$/;\n\nfunction validateCronId(id: string): string | null {\n if (!id || !SAFE_CRON_ID_RE.test(id)) {\n return `Invalid task ID \"${id}\". Use only letters, numbers, dash, underscore, dot.`;\n }\n return null;\n}\n\nasync function handleCronCommand(\n trimmed: string,\n cronScheduler: import(\"./cron.js\").CronScheduler,\n): Promise<string> {\n const args = trimmed.slice(\"/cron\".length).trim();\n\n // /cron or /cron list — list all tasks\n if (!args || args === \"list\") {\n const status = cronScheduler.getStatus();\n if (status.length === 0) return t(\"cmd_cron_empty\");\n const lines = status.map((s) => {\n const state = s.enabled ? \"✓\" : \"✗\";\n const errors = s.consecutiveErrors > 0 ? ` ⚠${s.consecutiveErrors}` : \"\";\n const last = s.lastRun\n ? `${s.lastRun.status === \"ok\" ? \"✓\" : \"✗\"} ${new Date(s.lastRun.finishedAt).toLocaleString()}`\n : \"-\";\n const next = s.nextRun ? new Date(s.nextRun).toLocaleString() : \"-\";\n return ` ${state} ${s.id}${s.name ? ` (${s.name})` : \"\"}${errors}\\n schedule: ${s.schedule}\\n last: ${last} | next: ${next}`;\n });\n return t(\"cmd_cron_list\", {\n count: String(status.length),\n list: lines.join(\"\\n\"),\n });\n }\n\n // /cron help\n if (args === \"help\") {\n return t(\"cmd_cron_help\");\n }\n\n // /cron status — scheduler status\n if (args === \"status\") {\n const s = cronScheduler.getSchedulerStatus();\n const concurrency = s.maxConcurrentRuns\n ? `${s.runningTasks}/${s.maxConcurrentRuns}`\n : String(s.runningTasks);\n return t(\"cmd_cron_status\", {\n state: s.running ? \"Running\" : \"Stopped\",\n total: String(s.taskCount),\n active: String(s.activeJobs),\n running: concurrency,\n next: s.nextWakeAt ? new Date(s.nextWakeAt).toLocaleString() : \"-\",\n });\n }\n\n // /cron run <id> [--due] — trigger task (optionally only if due)\n if (args.startsWith(\"run \")) {\n const runArgs = args.slice(4).trim().split(/\\s+/);\n const id = runArgs[0];\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const onlyIfDue = runArgs.includes(\"--due\");\n const result = await cronScheduler.runTask(id, { onlyIfDue });\n if (!result) {\n return onlyIfDue\n ? t(\"cmd_cron_not_due\", { id })\n : t(\"cmd_cron_not_found\", { id });\n }\n return t(\"cmd_cron_triggered\", {\n id,\n status:\n result.status === \"ok\"\n ? `✓ ${result.resultPreview?.slice(0, 100) ?? \"\"}`\n : `✗ ${result.error ?? \"unknown error\"}`,\n });\n }\n\n // /cron runs <id> [--status=ok|error|skipped] [--page=N] — view run history\n if (args.startsWith(\"runs \")) {\n const runsParts = args.slice(5).trim().split(/\\s+/);\n const id = runsParts[0];\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n\n // Parse optional flags\n let statusFilter: \"ok\" | \"error\" | \"skipped\" | undefined;\n let page = 0;\n const limit = 10;\n for (const flag of runsParts.slice(1)) {\n if (flag.startsWith(\"--status=\")) {\n const val = flag.slice(9);\n if (val === \"ok\" || val === \"error\" || val === \"skipped\") {\n statusFilter = val;\n }\n } else if (flag.startsWith(\"--page=\")) {\n page = Math.max(0, parseInt(flag.slice(7), 10) - 1);\n }\n }\n\n const result = cronScheduler.queryRunHistory(id, {\n limit,\n offset: page * limit,\n status: statusFilter,\n });\n\n if (result.entries.length === 0) return t(\"cmd_cron_runs_empty\", { id });\n const lines = result.entries.map((r) => {\n const time = new Date(r.ts).toLocaleString();\n const dur = `${(r.durationMs / 1000).toFixed(1)}s`;\n const status =\n r.status === \"ok\" ? \"✓\" : r.status === \"skipped\" ? \"⊘\" : \"✗\";\n const detail = r.error ?? r.summary?.slice(0, 80) ?? \"\";\n const delivery = r.deliveryStatus ? ` [${r.deliveryStatus}]` : \"\";\n return ` ${status} ${time} (${dur})${delivery}\\n ${detail}`;\n });\n const pageInfo = result.hasMore\n ? `\\n (page ${page + 1}, ${result.total} total, --page=${page + 2} for more)`\n : \"\";\n return t(\"cmd_cron_runs_header\", {\n id,\n count: String(result.total),\n list: lines.join(\"\\n\") + pageInfo,\n });\n }\n\n // /cron add <id> <schedule> <prompt> [--model=X] [--light] [--name=X] [--timeout=N]\n if (args.startsWith(\"add \")) {\n const parts = args.slice(4).trim().split(/\\s+/);\n if (parts.length < 3) return t(\"cmd_cron_help\");\n const id = parts[0];\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n\n // Separate flags from positional args\n const positional: string[] = [];\n let model: string | undefined;\n let light = false;\n let name: string | undefined;\n let timeoutSeconds: number | undefined;\n for (const p of parts.slice(1)) {\n if (p.startsWith(\"--model=\")) model = p.slice(8);\n else if (p === \"--light\") light = true;\n else if (p.startsWith(\"--name=\")) name = p.slice(7);\n else if (p.startsWith(\"--timeout=\")) {\n const n = parseInt(p.slice(10), 10);\n if (Number.isFinite(n) && n >= 0) timeoutSeconds = n;\n } else positional.push(p);\n }\n if (positional.length < 2) return t(\"cmd_cron_help\");\n\n const schedule = positional[0];\n const prompt = positional.slice(1).join(\" \");\n cronScheduler.addTask({\n id,\n schedule,\n prompt,\n enabled: true,\n ...(model ? { model } : {}),\n ...(light ? { lightContext: true } : {}),\n ...(name ? { name } : {}),\n ...(timeoutSeconds != null ? { timeoutSeconds } : {}),\n });\n return t(\"cmd_cron_added\", { id, schedule, prompt: prompt.slice(0, 60) });\n }\n\n // /cron edit <id> <field>=<value>\n if (args.startsWith(\"edit \")) {\n const parts = args.slice(5).trim().split(/\\s+/);\n if (parts.length < 2) return t(\"cmd_cron_help\");\n const id = parts[0];\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const patch: Record<string, unknown> = {};\n for (const kv of parts.slice(1)) {\n const eqIdx = kv.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = kv.slice(0, eqIdx);\n const value = kv.slice(eqIdx + 1);\n if (key === \"enabled\") patch[key] = value === \"true\";\n else if (key === \"light_context\" || key === \"lightContext\")\n patch[\"lightContext\"] = value === \"true\";\n else if (key === \"delete_after_run\" || key === \"deleteAfterRun\")\n patch[\"deleteAfterRun\"] = value === \"true\";\n else if (\n key === \"timeout\" ||\n key === \"timeout_seconds\" ||\n key === \"timeoutSeconds\"\n ) {\n const n = parseInt(value, 10);\n if (Number.isFinite(n) && n >= 0) patch[\"timeoutSeconds\"] = n;\n } else if (\n key === \"schedule\" ||\n key === \"prompt\" ||\n key === \"model\" ||\n key === \"name\" ||\n key === \"description\" ||\n key === \"webhook_url\" ||\n key === \"webhookUrl\"\n ) {\n const patchKey = key === \"webhook_url\" ? \"webhookUrl\" : key;\n patch[patchKey] = value;\n }\n }\n const ok = cronScheduler.editTask(\n id,\n patch as Partial<import(\"./types.js\").CronTask>,\n );\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_edited\", { id });\n }\n\n // /cron remove <id>\n if (args.startsWith(\"remove \")) {\n const id = args.slice(7).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.removeTask(id);\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_removed\", { id });\n }\n\n // /cron enable <id>\n if (args.startsWith(\"enable \")) {\n const id = args.slice(7).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.editTask(id, { enabled: true });\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_enabled\", { id });\n }\n\n // /cron disable <id>\n if (args.startsWith(\"disable \")) {\n const id = args.slice(8).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.editTask(id, { enabled: false });\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_disabled_task\", { id });\n }\n\n return t(\"cmd_cron_help\");\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2] ?? \"start\";\n\n const runWizard = async (\n fn: (m: typeof import(\"./setup-wizard.js\")) => Promise<void>,\n ) => {\n const m = await import(\"./setup-wizard.js\");\n await fn(m);\n process.exit(0);\n };\n\n switch (cmd) {\n case \"setup\": {\n const sub = process.argv[3];\n if (sub === \"--add-channel\") {\n await runWizard((m) => m.runAddChannel());\n } else if (sub === \"--remove-channel\") {\n await runWizard((m) => m.runRemoveChannel());\n } else {\n await runWizard((m) => m.runSetup());\n }\n break;\n }\n case \"add-channel\":\n await runWizard((m) => m.runAddChannel());\n break;\n case \"remove-channel\":\n await runWizard((m) => m.runRemoveChannel());\n break;\n case \"doctor\": {\n const doc = await import(\"./doctor.js\");\n await doc.runDoctor();\n process.exit(0);\n break;\n }\n case \"start\": {\n const flags = process.argv.slice(3);\n const foreground = flags.includes(\"--foreground\") || flags.includes(\"-f\");\n const daemon = await import(\"./daemon.js\");\n\n if (foreground) {\n daemon.registerForegroundPid();\n await start();\n } else {\n daemon.daemonize();\n }\n break;\n }\n case \"stop\": {\n const daemon = await import(\"./daemon.js\");\n await daemon.stopDaemon();\n break;\n }\n case \"status\": {\n const daemon = await import(\"./daemon.js\");\n const flags = process.argv.slice(3);\n if (flags.includes(\"--json\")) {\n daemon.showStatusJson();\n } else {\n daemon.showStatus();\n }\n break;\n }\n case \"logs\": {\n const daemon = await import(\"./daemon.js\");\n daemon.tailLogs();\n break;\n }\n case \"daemon\": {\n const sub = process.argv[3];\n const daemon = await import(\"./daemon.js\");\n if (sub === \"install\") {\n const portFlag = process.argv.find((a) => a.startsWith(\"--port=\"));\n const port = portFlag ? parseInt(portFlag.slice(7), 10) : 3000;\n daemon.installLaunchAgent(Number.isFinite(port) ? port : 3000);\n } else if (sub === \"uninstall\") {\n daemon.uninstallLaunchAgent();\n } else if (sub === \"status\") {\n const flags = process.argv.slice(4);\n if (flags.includes(\"--json\")) {\n daemon.showStatusJson();\n } else {\n daemon.showStatus();\n }\n } else {\n console.log(\n \"Usage: klaus daemon <command>\\n\\n\" +\n \"Commands:\\n\" +\n \" install [--port=N] Install launchd agent (macOS)\\n\" +\n \" uninstall Remove launchd agent\\n\" +\n \" status [--json] Show daemon status\\n\",\n );\n }\n process.exit(0);\n break;\n }\n default:\n console.log(\n \"Klaus — Use Claude Code from any messaging platform\\n\\n\" +\n \"Usage: klaus [command]\\n\\n\" +\n \"Commands:\\n\" +\n \" start Start the bot in background (default)\\n\" +\n \" start -f Start in foreground\\n\" +\n \" stop Stop the background daemon\\n\" +\n \" status Show daemon status\\n\" +\n \" status --json Machine-readable status\\n\" +\n \" logs Tail daemon logs\\n\" +\n \" daemon install Install launchd agent (macOS)\\n\" +\n \" daemon uninstall Remove launchd agent\\n\" +\n \" setup Interactive setup wizard\\n\" +\n \" add-channel Add a channel to existing config\\n\" +\n \" remove-channel Remove a channel from config\\n\" +\n \" doctor Diagnose environment issues\\n\",\n );\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","/**\n * Claude Code SDK wrapper for multi-turn conversations.\n *\n * ClaudeChat: single session with collect mode (message queuing when busy).\n * ChatSessionManager: per-session instances with LRU eviction.\n */\n\nimport { query, type McpServerConfig } from \"@anthropic-ai/claude-agent-sdk\";\nimport { loadConfig, loadOneProxyConfig } from \"./config.js\";\nimport { getToolConfig } from \"./tool-config.js\";\nimport { DEFAULT_PERSONA } from \"./persona.js\";\nimport { ensureWorkspace, extractUserId } from \"./workspace.js\";\nimport type { SessionStore, PersistedSession } from \"./session-store.js\";\nimport type { MessageStore } from \"./message-store.js\";\nimport { type MemoryStore, buildMemoryFlushPrompt } from \"./memory-store.js\";\nimport { buildSkillsPrompt } from \"./skills/index.js\";\nimport type {\n ToolEventCallback,\n StreamChunkCallback,\n PermissionRequestCallback,\n PermissionRequest,\n} from \"./types.js\";\n\n/**\n * Memory flush interval: trigger a silent memory-save turn every N chat rounds.\n * Aligned with OpenClaw's pre-compaction flush concept, but using message count\n * as proxy since we don't have access to SDK token counts.\n */\nconst MEMORY_FLUSH_INTERVAL = 20;\n\n// Read-only tools — auto-allow without permission prompt\nconst READ_ONLY_TOOLS = new Set([\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebSearch\",\n \"WebFetch\",\n \"TodoWrite\",\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isSessionExpiredError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"session\") &&\n (msg.includes(\"not found\") ||\n msg.includes(\"expired\") ||\n msg.includes(\"invalid\"))\n );\n}\n\n// ---------------------------------------------------------------------------\n// Deferred: equivalent of Python asyncio.Future\n// ---------------------------------------------------------------------------\n\ninterface Deferred<T> {\n readonly promise: Promise<T>;\n resolve(value: T): void;\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n const promise = new Promise<T>((r) => {\n resolve = r;\n });\n return { promise, resolve };\n}\n\n// ---------------------------------------------------------------------------\n// ClaudeChat: wraps Claude Agent SDK for multi-turn chat with collect mode\n// ---------------------------------------------------------------------------\n\ninterface ChatOptions {\n systemPrompt: string;\n model?: string;\n cwd?: string;\n mcpServers?: Record<string, McpServerConfig>;\n oneProxyBaseUrl?: string;\n}\n\ninterface PendingMessage {\n prompt: string;\n deferred: Deferred<string | null>;\n}\n\nexport class ClaudeChat {\n private sessionId: string | undefined;\n private busy = false;\n private pending: PendingMessage[] = [];\n private options: ChatOptions;\n private model: string | undefined;\n /** Number of completed chat rounds (for memory flush timing). */\n private chatRoundCount = 0;\n\n constructor(options: ChatOptions) {\n this.options = options;\n this.model = options.model;\n }\n\n /** Get the current Claude SDK session ID (for persistence). */\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n /** Restore a session ID from persistent storage. */\n restoreSessionId(id: string): void {\n this.sessionId = id;\n }\n\n /**\n * Send a message to Claude. If the error indicates a stale/expired session,\n * clears sessionId and retries once without resume.\n */\n private async doChat(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string> {\n try {\n return await this.doChatInner(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n } catch (err) {\n if (this.sessionId && isSessionExpiredError(err)) {\n console.log(\"[Chat] Session expired, starting fresh session\");\n this.sessionId = undefined;\n return await this.doChatInner(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n }\n throw err;\n }\n }\n\n private async doChatInner(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string> {\n let resultText: string | undefined;\n let lastSessionId: string | undefined;\n\n const conversation = query({\n prompt,\n options: {\n systemPrompt: this.options.systemPrompt || undefined,\n permissionMode: onPermissionRequest ? \"default\" : \"bypassPermissions\",\n ...(this.options.cwd ? { cwd: this.options.cwd } : {}),\n ...(onPermissionRequest\n ? {\n canUseTool: async (\n toolName: string,\n input: Record<string, unknown>,\n opts: { toolUseID: string; decisionReason?: string },\n ) => {\n if (READ_ONLY_TOOLS.has(toolName)) {\n return { behavior: \"allow\" as const };\n }\n const config = getToolConfig(toolName);\n const request: PermissionRequest = {\n requestId: opts.toolUseID,\n toolName,\n toolUseId: opts.toolUseID,\n input,\n description: opts.decisionReason,\n display: {\n icon: config.icon,\n label: config.label,\n style: config.style,\n value: config.getValue(input),\n ...(config.getSecondary\n ? { secondary: config.getSecondary(input) }\n : {}),\n },\n };\n const response = await onPermissionRequest(request);\n return response.allow\n ? { behavior: \"allow\" as const }\n : {\n behavior: \"deny\" as const,\n message: \"User denied the tool execution\",\n };\n },\n }\n : {}),\n ...(this.model ? { model: this.model } : {}),\n ...(this.sessionId ? { resume: this.sessionId } : {}),\n ...(onStreamChunk ? { includePartialMessages: true } : {}),\n ...(this.options.mcpServers\n ? { mcpServers: this.options.mcpServers }\n : {}),\n ...(this.options.oneProxyBaseUrl\n ? {\n env: {\n ...process.env,\n ANTHROPIC_BASE_URL: this.options.oneProxyBaseUrl,\n },\n }\n : {}),\n },\n });\n\n for await (const msg of conversation) {\n if (msg.type === \"result\" && msg.subtype === \"success\") {\n resultText = msg.result;\n }\n if (\"session_id\" in msg && typeof msg.session_id === \"string\") {\n lastSessionId = msg.session_id;\n }\n\n // Extract tool use events for Web channel visualization\n if (onToolEvent) {\n this.emitToolEvents(msg, onToolEvent);\n }\n\n // Extract streaming text deltas\n if (msg.type === \"stream_event\" && onStreamChunk) {\n this.emitStreamChunk(msg, onStreamChunk);\n }\n }\n\n if (lastSessionId) {\n this.sessionId = lastSessionId;\n }\n\n this.chatRoundCount++;\n return resultText || \"(no response)\";\n }\n\n private emitToolEvents(\n msg: { type: string; [key: string]: unknown },\n onToolEvent: ToolEventCallback,\n ): void {\n // Sub-agent context: non-null when inside a sub-agent execution\n const parentToolUseId =\n typeof msg.parent_tool_use_id === \"string\"\n ? msg.parent_tool_use_id\n : undefined;\n\n // SDKAssistantMessage: content[] may contain tool_use blocks\n if (msg.type === \"assistant\") {\n const message = msg.message as\n | {\n content?: readonly {\n type: string;\n id?: string;\n name?: string;\n input?: unknown;\n }[];\n }\n | undefined;\n if (message?.content) {\n for (const block of message.content) {\n if (block.type === \"tool_use\" && block.id && block.name) {\n onToolEvent({\n type: \"tool_start\",\n toolUseId: block.id,\n toolName: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n timestamp: Date.now(),\n ...(parentToolUseId ? { parentToolUseId } : {}),\n });\n }\n }\n }\n }\n\n // SDKUserMessage: content[] may contain tool_result blocks\n if (msg.type === \"user\") {\n const message = msg.message as\n | {\n content?:\n | readonly {\n type: string;\n tool_use_id?: string;\n is_error?: boolean;\n }[]\n | string;\n }\n | undefined;\n if (message?.content && Array.isArray(message.content)) {\n for (const block of message.content) {\n if (\n typeof block === \"object\" &&\n block !== null &&\n block.type === \"tool_result\" &&\n block.tool_use_id\n ) {\n onToolEvent({\n type: \"tool_result\",\n toolUseId: block.tool_use_id,\n toolName: \"\",\n isError: block.is_error ?? false,\n timestamp: Date.now(),\n ...(parentToolUseId ? { parentToolUseId } : {}),\n });\n }\n }\n }\n }\n }\n\n private emitStreamChunk(\n msg: { type: string; [key: string]: unknown },\n onStreamChunk: StreamChunkCallback,\n ): void {\n // SDKPartialAssistantMessage: stream_event with content_block_delta\n const event = msg.event as Record<string, unknown> | undefined;\n if (!event || event.type !== \"content_block_delta\") return;\n // Only emit top-level text (not sub-agent streams)\n if (typeof msg.parent_tool_use_id === \"string\") return;\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n onStreamChunk(delta.text);\n }\n }\n\n /**\n * Send a message, return the full text reply.\n *\n * If the agent is busy, the message is queued (collect mode).\n * Returns null for callers whose messages were merged into a batch.\n */\n async chat(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string | null> {\n if (this.busy) {\n const deferred = createDeferred<string | null>();\n this.pending.push({ prompt, deferred });\n console.log(\n `[Collect] Queued (pending: ${this.pending.length}): ${prompt.slice(0, 80)}`,\n );\n return deferred.promise;\n }\n\n this.busy = true;\n try {\n let reply = await this.doChat(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n\n // Drain queued messages (collect mode)\n while (this.pending.length > 0) {\n const batch = [...this.pending];\n this.pending = [];\n\n const prompts = batch.map((b) => b.prompt);\n const merged =\n \"[以下是你处理上一条消息期间用户追加发送的消息]\\n\" +\n prompts.join(\"\\n\");\n console.log(\n `[Collect] Merging ${batch.length} queued message(s): ${merged.slice(0, 120)}`,\n );\n\n // Earlier callers: their messages are merged, no separate reply\n for (const item of batch.slice(0, -1)) {\n item.deferred.resolve(null);\n }\n\n // Process the merged message; ensure last caller's deferred\n // is always resolved even if doChat throws.\n try {\n reply = await this.doChat(\n merged,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n batch[batch.length - 1].deferred.resolve(reply);\n } catch (e) {\n batch[batch.length - 1].deferred.resolve(null);\n throw e;\n }\n }\n\n return reply;\n } catch (err) {\n // Resolve all pending deferreds so callers don't hang forever\n for (const item of this.pending) {\n item.deferred.resolve(null);\n }\n this.pending = [];\n await this.reset();\n throw err;\n } finally {\n this.busy = false;\n }\n }\n\n get isBusy(): boolean {\n return this.busy;\n }\n\n getModel(): string | undefined {\n return this.model;\n }\n\n setModel(model: string): void {\n this.model = model;\n }\n\n /** Update the system prompt (e.g. to refresh memory context). */\n setSystemPrompt(prompt: string): void {\n this.options = { ...this.options, systemPrompt: prompt };\n }\n\n /** Get the number of completed chat rounds. */\n getRoundCount(): number {\n return this.chatRoundCount;\n }\n\n async reset(): Promise<void> {\n this.sessionId = undefined;\n this.chatRoundCount = 0;\n }\n\n async close(): Promise<void> {\n await this.reset();\n }\n}\n\n// ---------------------------------------------------------------------------\n// ChatSessionManager: per-session ClaudeChat instances with LRU eviction\n// ---------------------------------------------------------------------------\n\nexport class ChatSessionManager {\n static readonly MAX_SESSIONS = 20;\n private sessions = new Map<string, ClaudeChat>();\n private options: ChatOptions;\n private store: SessionStore | undefined;\n private messageStore: MessageStore | undefined;\n private memoryStore: MemoryStore | undefined;\n private idleMs: number;\n\n constructor(\n store?: SessionStore,\n idleMs?: number,\n messageStore?: MessageStore,\n memoryStore?: MemoryStore,\n ) {\n const cfg = loadConfig();\n const persona = (cfg.persona as string) || DEFAULT_PERSONA;\n const model = (cfg.model as string) || undefined;\n const opCfg = loadOneProxyConfig();\n this.options = {\n systemPrompt: persona,\n model,\n ...(opCfg.enabled ? { oneProxyBaseUrl: opCfg.baseUrl } : {}),\n };\n this.store = store;\n this.messageStore = messageStore;\n this.memoryStore = memoryStore;\n this.idleMs = idleMs ?? 4 * 60 * 60 * 1000; // 4 hours default\n }\n\n /** Update the default model for new and existing sessions. */\n setDefaultModel(model: string | undefined): void {\n this.options = { ...this.options, model };\n for (const session of this.sessions.values()) {\n if (model) {\n session.setModel(model);\n }\n }\n }\n\n /** Get the current default model. */\n getDefaultModel(): string | undefined {\n return this.options.model;\n }\n\n /** Update the system prompt for new sessions (existing sessions keep their prompt until reset). */\n setPersona(persona: string): void {\n this.options = { ...this.options, systemPrompt: persona };\n }\n\n /** Inject MCP servers into all future sessions (called after CronScheduler init). */\n setMcpServers(servers: Record<string, McpServerConfig>): void {\n this.options = { ...this.options, mcpServers: servers };\n }\n\n /** Switch OneProxy mode. Clears all sessions when mode changes. */\n async setOneProxyConfig(config: {\n enabled: boolean;\n baseUrl: string;\n }): Promise<void> {\n const newUrl = config.enabled ? config.baseUrl : undefined;\n const oldUrl = this.options.oneProxyBaseUrl;\n if (newUrl === oldUrl) return;\n\n // Mode changed — clear all sessions (resume tokens are bound to base URL)\n for (const [key] of this.sessions) {\n await this.reset(key);\n }\n this.options = { ...this.options, oneProxyBaseUrl: newUrl };\n }\n\n /** Whether OneProxy mode is currently active. */\n isOneProxyEnabled(): boolean {\n return !!this.options.oneProxyBaseUrl;\n }\n\n private persistSession(key: string, session: ClaudeChat): void {\n if (!this.store) return;\n const sessionId = session.getSessionId();\n if (!sessionId) return;\n const existing = this.store.get(key);\n this.store.set(key, {\n sessionId,\n sessionKey: key,\n createdAt: existing?.createdAt ?? Date.now(),\n updatedAt: Date.now(),\n model: session.getModel(),\n });\n }\n\n private async evictIfNeeded(): Promise<void> {\n if (this.sessions.size < ChatSessionManager.MAX_SESSIONS) return;\n for (const [key, session] of this.sessions) {\n if (!session.isBusy) {\n // Save sessionId before eviction so it can be restored later\n this.persistSession(key, session);\n await session.close();\n this.sessions.delete(key);\n console.log(`[Session] Evicted (LRU): ${key}`);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save after eviction failed:\", err);\n });\n return;\n }\n }\n }\n\n private buildSystemPrompt(sessionKey: string): string {\n let prompt = this.options.systemPrompt;\n\n // Append skills section\n const skillsSection = buildSkillsPrompt();\n if (skillsSection) {\n prompt = `${prompt}\\n\\n${skillsSection}`;\n }\n\n // Append memory section\n if (this.memoryStore) {\n const memorySection = this.memoryStore.buildMemoryPrompt(sessionKey);\n if (memorySection) {\n prompt = `${prompt}\\n\\n${memorySection}`;\n }\n }\n\n return prompt;\n }\n\n private getSession(sessionKey: string): ClaudeChat {\n const existing = this.sessions.get(sessionKey);\n if (existing) {\n // Move to end (most recently used)\n this.sessions.delete(sessionKey);\n this.sessions.set(sessionKey, existing);\n return existing;\n }\n\n // Resolve per-user workspace directory (isolates file access)\n const userId = extractUserId(sessionKey);\n const cwd = userId ? ensureWorkspace(userId) : undefined;\n\n const sessionOptions: ChatOptions = {\n ...this.options,\n systemPrompt: this.buildSystemPrompt(sessionKey),\n ...(cwd ? { cwd } : {}),\n };\n const chat = new ClaudeChat(sessionOptions);\n\n // Restore sessionId from persistent store if fresh\n if (this.store) {\n const persisted = this.store.get(sessionKey);\n if (persisted && this.store.isFresh(sessionKey, this.idleMs)) {\n chat.restoreSessionId(persisted.sessionId);\n if (persisted.model) {\n chat.setModel(persisted.model);\n }\n console.log(`[Session] Restored from store: ${sessionKey}`);\n }\n }\n\n this.sessions.set(sessionKey, chat);\n console.log(\n `[Session] New session: ${sessionKey} (total: ${this.sessions.size})`,\n );\n return chat;\n }\n\n async chat(\n sessionKey: string,\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n /** User-facing display text for history (defaults to prompt if omitted). */\n displayText?: string,\n ): Promise<string | null> {\n await this.evictIfNeeded();\n const session = this.getSession(sessionKey);\n\n // Refresh memory in system prompt before each chat\n if (this.memoryStore) {\n session.setSystemPrompt(this.buildSystemPrompt(sessionKey));\n }\n\n const result = await session.chat(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n\n // Persist after successful chat (fire-and-forget)\n if (result !== null) {\n this.persistSession(sessionKey, session);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n\n // Append messages to transcript (fire-and-forget async)\n if (this.messageStore) {\n this.messageStore\n .append(sessionKey, \"user\", displayText ?? prompt)\n .then(() =>\n this.messageStore!.append(sessionKey, \"assistant\", result),\n )\n .catch((err) => console.error(\"[MessageStore] Append failed:\", err));\n }\n\n // Memory flush: schedule a silent turn AFTER returning, so the user\n // gets their reply immediately. The flush runs when the session is idle.\n if (this.shouldFlushMemory(session)) {\n const sk = sessionKey;\n setTimeout(() => {\n this.runMemoryFlush(sk, session).catch((err) => {\n console.error(\"[Memory] Flush failed:\", err);\n });\n }, 500);\n }\n }\n\n return result;\n }\n\n /** Check if memory flush should trigger (without side effects). */\n private shouldFlushMemory(session: ClaudeChat): boolean {\n if (!this.memoryStore) return false;\n const rounds = session.getRoundCount();\n return rounds > 0 && rounds % MEMORY_FLUSH_INTERVAL === 0;\n }\n\n /**\n * Run a silent memory flush turn. Aligned with OpenClaw's pre-compaction\n * flush: a hidden agent turn that saves durable memories to disk.\n *\n * Skips if the session is busy (user sent a new message before flush ran).\n * The flush reply is discarded — the user never sees it.\n */\n private async runMemoryFlush(\n sessionKey: string,\n session: ClaudeChat,\n ): Promise<void> {\n // Skip if user already started a new conversation turn\n if (session.isBusy) {\n console.log(`[Memory] Flush skipped (session busy): ${sessionKey}`);\n return;\n }\n\n console.log(\n `[Memory] Triggering flush for ${sessionKey} (round ${session.getRoundCount()})`,\n );\n try {\n session.setSystemPrompt(this.buildSystemPrompt(sessionKey));\n const flushReply = await session.chat(buildMemoryFlushPrompt());\n if (flushReply) {\n console.log(\n `[Memory] Flush complete for ${sessionKey}: ${flushReply.slice(0, 80)}`,\n );\n }\n } catch (err) {\n console.error(`[Memory] Flush error for ${sessionKey}:`, err);\n }\n }\n\n /**\n * Lightweight chat: skips persona/skills/memory in system prompt.\n * Used by cron tasks with lightContext: true for faster execution.\n */\n async chatLight(sessionKey: string, prompt: string): Promise<string | null> {\n await this.evictIfNeeded();\n\n // Create a session with minimal system prompt (no persona, skills, memory)\n const existing = this.sessions.get(sessionKey);\n if (existing) {\n // LRU update: move to end\n this.sessions.delete(sessionKey);\n this.sessions.set(sessionKey, existing);\n } else {\n const userId = extractUserId(sessionKey);\n const cwd = userId ? ensureWorkspace(userId) : undefined;\n const chat = new ClaudeChat({\n systemPrompt: \"You are a helpful assistant. Be concise.\",\n model: this.options.model,\n ...(cwd ? { cwd } : {}),\n ...(this.options.mcpServers\n ? { mcpServers: this.options.mcpServers }\n : {}),\n });\n\n // Restore from store if available\n if (this.store) {\n const persisted = this.store.get(sessionKey);\n if (persisted && this.store.isFresh(sessionKey, this.idleMs)) {\n chat.restoreSessionId(persisted.sessionId);\n if (persisted.model) chat.setModel(persisted.model);\n }\n }\n\n this.sessions.set(sessionKey, chat);\n }\n\n const session = this.sessions.get(sessionKey)!;\n const result = await session.chat(prompt);\n\n if (result !== null) {\n this.persistSession(sessionKey, session);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n }\n\n return result;\n }\n\n setModel(sessionKey: string, model: string): void {\n const session = this.getSession(sessionKey);\n session.setModel(model);\n }\n\n getModel(sessionKey: string): string | undefined {\n return this.sessions.get(sessionKey)?.getModel();\n }\n\n getSessionInfo(sessionKey: string): {\n active: boolean;\n busy: boolean;\n model: string | undefined;\n } {\n const session = this.sessions.get(sessionKey);\n return {\n active: !!session,\n busy: session?.isBusy ?? false,\n model: session?.getModel(),\n };\n }\n\n async reset(sessionKey: string): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (session) {\n await session.reset();\n this.sessions.delete(sessionKey);\n\n // Remove from persistent store\n if (this.store) {\n this.store.delete(sessionKey);\n this.store.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n }\n\n console.log(`[Session] Reset: ${sessionKey}`);\n }\n }\n\n async close(): Promise<void> {\n // Persist all active sessions before closing\n if (this.store) {\n for (const [key, session] of this.sessions) {\n this.persistSession(key, session);\n }\n await this.store.close().catch((err) => {\n console.error(\"[SessionStore] Failed to save on close:\", err);\n });\n }\n\n for (const session of this.sessions.values()) {\n await session.close();\n }\n this.sessions.clear();\n }\n}\n","/**\n * Standardized inbound message types and formatting.\n *\n * Inspired by OpenClaw's MsgContext, simplified for Klaus.\n * Channels produce InboundMessage objects; formatPrompt() converts them\n * into text prompts for Claude.\n */\n\nimport { writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type MessageType =\n | \"text\"\n | \"image\"\n | \"voice\"\n | \"video\"\n | \"location\"\n | \"link\"\n | \"file\"\n | \"emoji\"\n | \"mixed\";\n\nexport interface MediaFile {\n readonly type: \"image\" | \"audio\" | \"video\" | \"file\";\n readonly path?: string;\n readonly url?: string;\n readonly fileName?: string;\n /** ASR transcription result (voice messages). */\n readonly transcription?: string;\n}\n\nexport interface ReplyContext {\n readonly messageId?: string;\n /** Preview of the replied-to message. */\n readonly text?: string;\n}\n\nexport interface LocationInfo {\n readonly label?: string;\n readonly latitude: number;\n readonly longitude: number;\n readonly scale?: number;\n}\n\nexport interface LinkInfo {\n readonly title?: string;\n readonly description?: string;\n readonly url: string;\n}\n\nexport interface InboundMessage {\n readonly sessionKey: string;\n /** Main text content (empty string when no text). */\n readonly text: string;\n readonly messageType: MessageType;\n readonly chatType: \"private\" | \"group\";\n readonly senderId: string;\n readonly senderName?: string;\n readonly media?: readonly MediaFile[];\n readonly replyTo?: ReplyContext;\n readonly mentions?: readonly string[];\n readonly location?: LocationInfo;\n readonly link?: LinkInfo;\n readonly emoji?: { readonly id?: number; readonly description?: string };\n readonly timestamp?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Display text: InboundMessage → user-facing text (no internal paths)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a structured InboundMessage into user-facing display text.\n * Unlike formatPrompt(), this hides internal file paths and only shows\n * file names — safe to persist in message history and show in the UI.\n */\nexport function formatDisplayText(msg: InboundMessage): string {\n const parts: string[] = [];\n\n if (msg.text) {\n parts.push(msg.text);\n }\n\n if (msg.media?.length) {\n for (const file of msg.media) {\n switch (file.type) {\n case \"image\":\n parts.push(file.fileName ? `[图片: ${file.fileName}]` : \"[图片]\");\n break;\n case \"audio\":\n parts.push(\n file.transcription\n ? `[语音: \"${file.transcription}\"]`\n : \"[语音消息]\",\n );\n break;\n case \"video\":\n parts.push(\"[视频]\");\n break;\n case \"file\":\n parts.push(`[文件: ${file.fileName || \"未知文件\"}]`);\n break;\n }\n }\n }\n\n return parts.join(\"\\n\").trim();\n}\n\n// ---------------------------------------------------------------------------\n// Format prompt: InboundMessage → text string for Claude\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a structured InboundMessage into a text prompt for Claude.\n * Centralizes the formatting logic previously duplicated in each channel.\n */\nexport function formatPrompt(msg: InboundMessage): string {\n const parts: string[] = [];\n\n // Reply context (prepend)\n if (msg.replyTo?.text) {\n const preview =\n msg.replyTo.text.length > 200\n ? msg.replyTo.text.slice(0, 200) + \"...\"\n : msg.replyTo.text;\n parts.push(`[回复消息: \"${preview}\"]`);\n } else if (msg.replyTo) {\n parts.push(\"[回复了一条消息]\");\n }\n\n // Mentions\n if (msg.mentions?.length) {\n for (const uid of msg.mentions) {\n parts.push(uid === \"all\" ? \"[@全体成员]\" : `[@用户:${uid}]`);\n }\n }\n\n // Text content\n if (msg.text) {\n parts.push(msg.text);\n }\n\n // Media files\n if (msg.media?.length) {\n for (const file of msg.media) {\n parts.push(formatMediaFile(file));\n }\n }\n\n // Emoji\n if (msg.emoji) {\n const desc = msg.emoji.description ?? msg.emoji.id;\n if (desc !== undefined && desc !== \"\") {\n parts.push(`[表情:${desc}]`);\n }\n }\n\n // Location\n if (msg.location) {\n const loc = msg.location;\n parts.push(\n \"[用户分享了一个位置]\\n\" +\n `地点: ${loc.label || \"未知\"}\\n` +\n `坐标: ${loc.latitude}, ${loc.longitude}` +\n (loc.scale != null ? `\\n缩放: ${loc.scale}` : \"\"),\n );\n }\n\n // Link\n if (msg.link) {\n const linkParts = [\"[用户分享了一个链接]\"];\n if (msg.link.title) linkParts.push(`标题: ${msg.link.title}`);\n if (msg.link.description) linkParts.push(`描述: ${msg.link.description}`);\n if (msg.link.url) linkParts.push(`链接: ${msg.link.url}`);\n parts.push(linkParts.join(\"\\n\"));\n }\n\n return parts.join(\"\\n\").trim();\n}\n\nfunction formatMediaFile(file: MediaFile): string {\n switch (file.type) {\n case \"image\": {\n if (file.path) {\n return `[图片: ${file.path},请用 Read 工具查看]`;\n }\n return \"[图片: 下载失败]\";\n }\n\n case \"audio\": {\n if (file.transcription) {\n return (\n `[用户发送了一段语音消息,语音识别结果: \"${file.transcription}\"]\\n` +\n \"请基于语音识别的内容回复用户。\"\n );\n }\n return (\n \"[用户发送了一段语音消息,但你目前无法听取语音。\" +\n \"请友好地告诉用户:语音消息暂不支持,请将想说的内容打字发送给你。]\"\n );\n }\n\n case \"video\":\n return (\n \"[用户发送了一段视频,但你目前无法观看视频。\" +\n \"请友好地告诉用户:视频消息暂不支持,请用文字描述视频内容或截图发送。]\"\n );\n\n case \"file\": {\n if (file.path) {\n const displayName = file.fileName || \"未知文件\";\n return `[文件: ${file.path},文件名: ${displayName},请用 Read 工具查看]`;\n }\n return `[文件 ${file.fileName || \"未知\"}: 下载失败]`;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared file download utility\n// ---------------------------------------------------------------------------\n\nconst TEMP_DIR = join(tmpdir(), \"klaus-files\");\nmkdirSync(TEMP_DIR, { recursive: true });\n\nconst MAX_DOWNLOAD_SIZE = 50 * 1024 * 1024; // 50 MB\n\n/**\n * Download a file from a URL to a temporary local path.\n * Returns the absolute path to the downloaded file.\n */\nexport async function downloadFile(\n rawUrl: string,\n name?: string,\n): Promise<string> {\n const url = rawUrl.startsWith(\"http\") ? rawUrl : `https://${rawUrl}`;\n const resp = await fetch(url);\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n\n const contentLength = Number(resp.headers.get(\"content-length\") ?? 0);\n if (contentLength > MAX_DOWNLOAD_SIZE) {\n throw new Error(`File too large: ${contentLength} bytes`);\n }\n\n const buffer = Buffer.from(await resp.arrayBuffer());\n if (buffer.byteLength > MAX_DOWNLOAD_SIZE) {\n throw new Error(`File too large: ${buffer.byteLength} bytes`);\n }\n\n const fallbackExt = url.match(/\\.([\\w]+)(?:\\?|$)/)?.[1] ?? \"bin\";\n const safeName = name ? basename(name).replace(/[^\\w.\\-]/g, \"_\") : undefined;\n const filename = safeName\n ? `${Date.now()}-${safeName}`\n : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${fallbackExt}`;\n const filepath = join(TEMP_DIR, filename);\n writeFileSync(filepath, buffer);\n return filepath;\n}\n\n/** The shared temp directory for downloaded files. */\nexport { TEMP_DIR, MAX_DOWNLOAD_SIZE };\n","/**\n * Cron marker parser — extracts [[cron:action {json}]] markers from Claude's reply.\n *\n * Enables AI-driven cron task management: Claude includes markers in replies,\n * the handler extracts them, executes cron operations, and strips markers\n * from the displayed text.\n */\n\nimport type { CronTask, CronDelivery } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CronMarkerAction =\n | { readonly action: \"add\"; readonly task: CronTask }\n | {\n readonly action: \"edit\";\n readonly id: string;\n readonly patch: Partial<CronTask>;\n }\n | { readonly action: \"remove\"; readonly id: string }\n | { readonly action: \"enable\"; readonly id: string }\n | { readonly action: \"disable\"; readonly id: string };\n\nexport interface CronMarkerResult {\n /** Reply text with all [[cron:...]] markers stripped. */\n readonly text: string;\n /** Parsed cron actions to execute. */\n readonly actions: readonly CronMarkerAction[];\n}\n\n// ---------------------------------------------------------------------------\n// Parser\n// ---------------------------------------------------------------------------\n\nconst CRON_MARKER_PATTERN =\n /\\[\\[cron:(add|edit|remove|enable|disable)\\s+(\\{[\\s\\S]*?\\})\\]\\]/g;\n\n// Safe ID: only alphanumeric, dash, underscore, dot\nconst SAFE_ID_RE = /^[a-zA-Z0-9._-]+$/;\n\n/**\n * Parse [[cron:action {json}]] markers from a reply string.\n * Returns the cleaned text and parsed actions.\n */\nexport function parseCronMarkers(reply: string): CronMarkerResult {\n const actions: CronMarkerAction[] = [];\n\n // Use fresh regex per call to avoid shared lastIndex state\n const re = new RegExp(CRON_MARKER_PATTERN.source, \"g\");\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(reply)) !== null) {\n const actionType = match[1] as\n | \"add\"\n | \"edit\"\n | \"remove\"\n | \"enable\"\n | \"disable\";\n const jsonStr = match[2];\n\n try {\n const data = JSON.parse(jsonStr) as Record<string, unknown>;\n const parsed = parseAction(actionType, data);\n if (parsed) {\n actions.push(parsed);\n }\n } catch (err) {\n console.warn(`[CronMarker] Failed to parse marker: ${err}`);\n }\n }\n\n // Strip all markers from displayed text\n const text = reply.replace(CRON_MARKER_PATTERN, \"\").trim();\n\n return { text, actions };\n}\n\nfunction parseAction(\n action: string,\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n switch (action) {\n case \"add\":\n return parseAddAction(data);\n case \"edit\":\n return parseEditAction(data);\n case \"remove\":\n case \"enable\":\n case \"disable\":\n return parseIdAction(action, data);\n default:\n return null;\n }\n}\n\nfunction parseAddAction(\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID: \"${id}\"`);\n return null;\n }\n\n const schedule = String(data.schedule ?? \"\");\n const prompt = String(data.prompt ?? \"\");\n if (!schedule || !prompt) {\n console.warn(`[CronMarker] Missing schedule or prompt for task \"${id}\"`);\n return null;\n }\n\n const task: CronTask = {\n id,\n schedule,\n prompt,\n enabled: true,\n ...(data.name != null ? { name: String(data.name) } : {}),\n ...(data.description != null\n ? { description: String(data.description) }\n : {}),\n ...(data.model != null ? { model: String(data.model) } : {}),\n ...(data.lightContext === true ? { lightContext: true } : {}),\n ...(data.timeoutSeconds != null\n ? { timeoutSeconds: Math.floor(Number(data.timeoutSeconds)) }\n : {}),\n ...(data.deliver ? { deliver: parseDeliver(data.deliver) } : {}),\n };\n\n return { action: \"add\", task };\n}\n\nfunction parseEditAction(\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID for edit: \"${id}\"`);\n return null;\n }\n\n // Build patch from all fields except 'id'\n const patch: Record<string, unknown> = {};\n if (data.schedule != null) patch.schedule = String(data.schedule);\n if (data.prompt != null) patch.prompt = String(data.prompt);\n if (data.name != null) patch.name = String(data.name);\n if (data.description != null) patch.description = String(data.description);\n if (data.model != null) patch.model = String(data.model);\n if (data.lightContext != null)\n patch.lightContext = data.lightContext === true;\n if (data.timeoutSeconds != null)\n patch.timeoutSeconds = Math.floor(Number(data.timeoutSeconds));\n if (data.enabled != null) patch.enabled = data.enabled === true;\n if (data.deliver != null) patch.deliver = parseDeliver(data.deliver);\n\n if (Object.keys(patch).length === 0) {\n console.warn(`[CronMarker] Empty patch for edit \"${id}\"`);\n return null;\n }\n\n return { action: \"edit\", id, patch: patch as Partial<CronTask> };\n}\n\nfunction parseIdAction(\n action: \"remove\" | \"enable\" | \"disable\",\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID for ${action}: \"${id}\"`);\n return null;\n }\n return { action, id };\n}\n\nfunction parseDeliver(raw: unknown): CronDelivery | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const d = raw as Record<string, unknown>;\n const channel = String(d.channel ?? \"web\");\n return {\n channel,\n ...(d.to ? { to: String(d.to) } : {}),\n ...(d.mode ? { mode: String(d.mode) as CronDelivery[\"mode\"] } : {}),\n ...(d.bestEffort === true ? { bestEffort: true } : {}),\n };\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/core.ts","../src/message.ts","../src/cron-marker.ts"],"sourcesContent":["import { existsSync } from \"node:fs\";\nimport { webPlugin } from \"./channels/web.js\";\nimport {\n registerChannel,\n getChannel,\n type ChannelPlugin,\n} from \"./channels/types.js\";\nimport {\n getChannelNames,\n CONFIG_FILE,\n loadSessionConfig,\n loadTranscriptsConfig,\n loadCronConfig,\n loadOneProxyConfig,\n} from \"./config.js\";\nimport { ensureConfigValid } from \"./config-validate.js\";\nimport { ChatSessionManager } from \"./core.js\";\nimport { t } from \"./i18n.js\";\nimport {\n type InboundMessage,\n formatPrompt,\n formatDisplayText,\n} from \"./message.js\";\nimport {\n loadEnabledSkills,\n listSkillNames,\n applySkillEnvOverrides,\n} from \"./skills/index.js\";\nimport type {\n ToolEventCallback,\n StreamChunkCallback,\n PermissionRequestCallback,\n} from \"./types.js\";\nimport { parseCronMarkers, type CronMarkerAction } from \"./cron-marker.js\";\nimport { generateLocalToken, generateExecToken } from \"./local-token.js\";\n\n// ---------------------------------------------------------------------------\n// Channel registration\n// ---------------------------------------------------------------------------\n\nregisterChannel(webPlugin);\n\n// ---------------------------------------------------------------------------\n// Model alias mapping\n// ---------------------------------------------------------------------------\n\nconst MODEL_ALIASES: Record<string, string> = {\n sonnet: \"claude-sonnet-4-6\",\n opus: \"claude-opus-4-6\",\n haiku: \"claude-haiku-4-5-20251001\",\n};\n\nasync function start(): Promise<void> {\n if (!existsSync(CONFIG_FILE)) {\n console.log(\"No config found. Starting setup wizard...\\n\");\n const { runSetup } = await import(\"./setup-wizard.js\");\n await runSetup();\n if (!existsSync(CONFIG_FILE)) return;\n }\n\n // Validate config before attempting to connect (fail-fast)\n ensureConfigValid();\n\n // Generate local token for macOS app authentication\n generateLocalToken();\n\n // Generate exec approval token for macOS app command approval\n generateExecToken();\n\n // Apply skill environment overrides (scoped to process lifetime)\n applySkillEnvOverrides();\n\n const channelNames = getChannelNames();\n const plugins: ChannelPlugin[] = [];\n for (const name of channelNames) {\n const plugin = getChannel(name);\n if (!plugin) {\n console.error(`Internal error: channel \"${name}\" not registered.`);\n process.exit(1);\n }\n plugins.push(plugin);\n }\n\n // Initialize session persistence\n const sessionCfg = loadSessionConfig();\n const { SessionStore } = await import(\"./session-store.js\");\n const store = new SessionStore();\n await store.load();\n store.pruneStale(sessionCfg.maxAgeMs);\n store.capEntries(sessionCfg.maxEntries);\n await store.save();\n\n // Initialize message persistence (JSONL transcripts)\n const { MessageStore } = await import(\"./message-store.js\");\n const messageStore = new MessageStore(loadTranscriptsConfig());\n messageStore.prune();\n\n // Initialize per-user memory store\n const { MemoryStore } = await import(\"./memory-store.js\");\n const memoryStore = new MemoryStore();\n\n const sessions = new ChatSessionManager(\n store,\n sessionCfg.idleMs,\n messageStore,\n memoryStore,\n );\n\n // Build delivery registry from active channel plugins (needed by cron)\n const deliverers = new Map<\n string,\n (to: string, text: string) => Promise<void>\n >();\n for (const p of plugins) {\n if (p.deliver) {\n deliverers.set(p.meta.id, p.deliver);\n }\n }\n\n // Initialize cron scheduler (eagerly if configured, lazily on first tool call)\n let cronScheduler: import(\"./cron.js\").CronScheduler | null = null;\n const cronCfg = loadCronConfig();\n\n /** Lazy-init with Promise cache to prevent concurrent double-init. */\n let schedulerPromise: Promise<import(\"./cron.js\").CronScheduler> | null =\n null;\n const ensureCronScheduler = (): Promise<\n import(\"./cron.js\").CronScheduler\n > => {\n schedulerPromise ??= (async () => {\n const { CronScheduler } = await import(\"./cron.js\");\n cronScheduler = new CronScheduler(\n { ...cronCfg, enabled: true },\n sessions,\n deliverers,\n store,\n );\n cronScheduler.start();\n console.log(\"[Cron] Scheduler started\");\n // Expose to web admin API if web channel is active\n if (channelNames.includes(\"web\")) {\n const { setCronScheduler } = await import(\"./channels/web.js\");\n setCronScheduler(cronScheduler);\n }\n return cronScheduler;\n })();\n return schedulerPromise;\n };\n\n // Eagerly start if cron is configured\n if (cronCfg.enabled) {\n await ensureCronScheduler();\n }\n\n // Always inject cron MCP tool so Claude can create tasks via tool_use.\n // Uses lazy scheduler init — first tool call starts the scheduler if needed.\n {\n const { createCronMcpServer } = await import(\"./cron-tool.js\");\n const cronMcp = createCronMcpServer({\n get scheduler() {\n return cronScheduler;\n },\n ensureScheduler: ensureCronScheduler,\n });\n const { createSendFileMcpServer } = await import(\"./send-file-tool.js\");\n const sendFileMcp = createSendFileMcpServer();\n\n // Skill registry tool (lazy init — first tool call creates RegistryManager)\n const { createSkillRegistryMcpServer } =\n await import(\"./skill-registry-tool.js\");\n let registryManager:\n | import(\"./skills/registry/registry-manager.js\").RegistryManager\n | null = null;\n const skillRegistryMcp = createSkillRegistryMcpServer({\n get manager() {\n return registryManager;\n },\n async ensureManager() {\n if (registryManager) return registryManager;\n const { RegistryManager } =\n await import(\"./skills/registry/registry-manager.js\");\n const { loadRegistryConfigs } = await import(\"./config.js\");\n registryManager = new RegistryManager(loadRegistryConfigs());\n return registryManager;\n },\n });\n\n sessions.setMcpServers({\n \"klaus-cron\": cronMcp,\n \"klaus-send-file\": sendFileMcp,\n \"klaus-skill-registry\": skillRegistryMcp,\n });\n }\n\n // Expose stores to web channel for API endpoints\n let inviteStoreInstance: { close(): void } | null = null;\n let userStoreInstance: { close(): void } | null = null;\n if (channelNames.includes(\"web\")) {\n const {\n setMessageStore,\n setInviteStore,\n setSessionStore,\n setUserStore,\n setChatManager,\n setCronScheduler,\n } = await import(\"./channels/web.js\");\n setMessageStore(messageStore);\n setSessionStore(store);\n setChatManager(sessions);\n // Expose cron scheduler to web admin API (may be null initially, set after init)\n if (cronScheduler) {\n setCronScheduler(cronScheduler);\n }\n\n const { InviteStore } = await import(\"./invite-store.js\");\n const inviteStore = new InviteStore();\n setInviteStore(inviteStore);\n inviteStoreInstance = inviteStore;\n\n const { UserStore } = await import(\"./user-store.js\");\n const { loadWebConfig } = await import(\"./config.js\");\n const webCfg = loadWebConfig();\n const sessionMaxAgeMs = webCfg.sessionMaxAgeDays * 24 * 60 * 60 * 1000;\n const userStore = new UserStore(undefined, sessionMaxAgeMs);\n setUserStore(userStore);\n userStoreInstance = userStore;\n\n // Prune expired auth sessions on startup\n const pruned = userStore.pruneExpiredSessions();\n if (pruned > 0) {\n console.log(`[UserStore] Pruned ${pruned} expired auth session(s)`);\n }\n }\n\n const handler = async (\n msg: InboundMessage,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string | null> => {\n const trimmed = msg.text.trim();\n\n // /new, /reset, /clear — reset conversation\n if ([\"/new\", \"/reset\", \"/clear\"].includes(trimmed)) {\n await sessions.reset(msg.sessionKey);\n return t(\"cmd_reset\");\n }\n\n // /help — list commands\n if (trimmed === \"/help\") {\n return t(\"cmd_help\");\n }\n\n // /session — show session info\n if (trimmed === \"/session\") {\n const info = sessions.getSessionInfo(msg.sessionKey);\n return t(\"cmd_session_info\", {\n key: msg.sessionKey,\n status: info.busy ? t(\"cmd_session_active\") : t(\"cmd_session_idle\"),\n model: info.model ?? t(\"cmd_default_model\"),\n });\n }\n\n // /skills — list enabled skills\n if (trimmed === \"/skills\") {\n const enabled = loadEnabledSkills();\n if (enabled.length === 0) {\n return t(\"cmd_skills_none\", {\n available: listSkillNames().join(\", \"),\n });\n }\n const list = enabled\n .map((s) => {\n const emoji = s.metadata?.emoji ? `${s.metadata.emoji} ` : \"\";\n const src = s.source === \"user\" ? \" (user)\" : \"\";\n return ` ${emoji}${s.name} — ${s.description}${src}`;\n })\n .join(\"\\n\");\n return t(\"cmd_skills_list\", { list, count: String(enabled.length) });\n }\n\n // /cron [subcommand] — cron task management\n if (trimmed === \"/cron\" || trimmed.startsWith(\"/cron \")) {\n const scheduler = await ensureCronScheduler();\n return handleCronCommand(trimmed, scheduler);\n }\n\n // /model [name] — show or switch model\n if (trimmed === \"/model\" || trimmed.startsWith(\"/model \")) {\n const arg = trimmed.slice(\"/model\".length).trim();\n if (!arg) {\n const current = sessions.getModel(msg.sessionKey);\n return t(\"cmd_model_current\", {\n model: current ?? t(\"cmd_default_model\"),\n });\n }\n const opCfg = loadOneProxyConfig();\n if (opCfg.enabled) {\n // OneProxy mode: use model name as-is (no alias resolution)\n sessions.setModel(msg.sessionKey, arg);\n return t(\"cmd_model_switched\", { model: arg });\n }\n const resolved = MODEL_ALIASES[arg.toLowerCase()] ?? MODEL_ALIASES[arg];\n if (!resolved) {\n return t(\"cmd_model_unknown\", { name: arg });\n }\n sessions.setModel(msg.sessionKey, resolved);\n return t(\"cmd_model_switched\", { model: resolved });\n }\n\n const prompt = formatPrompt(msg);\n if (!prompt) return null;\n const display = formatDisplayText(msg);\n const reply = await sessions.chat(\n msg.sessionKey,\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n display,\n );\n\n // Post-process: extract and execute [[cron:...]] markers\n if (reply) {\n const { text, actions } = parseCronMarkers(reply);\n if (actions.length > 0) {\n const scheduler = await ensureCronScheduler();\n executeCronActions(actions, scheduler);\n return text || null;\n }\n }\n\n return reply;\n };\n\n try {\n // Start all channels in parallel (each blocks forever).\n // If any rejects, Promise.all rejects → finally runs → process.exit(1) in main().\n await Promise.all(plugins.map((p) => p.start(handler)));\n } finally {\n (cronScheduler as import(\"./cron.js\").CronScheduler | null)?.stop();\n await sessions.close();\n inviteStoreInstance?.close();\n userStoreInstance?.close();\n }\n}\n\n// ---------------------------------------------------------------------------\n// Cron marker execution (AI-driven task management)\n// ---------------------------------------------------------------------------\n\nfunction executeCronActions(\n actions: readonly CronMarkerAction[],\n scheduler: import(\"./cron.js\").CronScheduler,\n): void {\n for (const action of actions) {\n try {\n switch (action.action) {\n case \"add\":\n scheduler.addTask(action.task);\n console.log(`[CronMarker] Added task \"${action.task.id}\"`);\n break;\n case \"edit\":\n if (scheduler.editTask(action.id, action.patch)) {\n console.log(`[CronMarker] Edited task \"${action.id}\"`);\n } else {\n console.warn(`[CronMarker] Task \"${action.id}\" not found for edit`);\n }\n break;\n case \"remove\":\n if (scheduler.removeTask(action.id)) {\n console.log(`[CronMarker] Removed task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for remove`,\n );\n }\n break;\n case \"enable\":\n if (scheduler.editTask(action.id, { enabled: true })) {\n console.log(`[CronMarker] Enabled task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for enable`,\n );\n }\n break;\n case \"disable\":\n if (scheduler.editTask(action.id, { enabled: false })) {\n console.log(`[CronMarker] Disabled task \"${action.id}\"`);\n } else {\n console.warn(\n `[CronMarker] Task \"${action.id}\" not found for disable`,\n );\n }\n break;\n }\n } catch (err) {\n console.error(`[CronMarker] Failed to execute ${action.action}:`, err);\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// /cron subcommand handler\n// ---------------------------------------------------------------------------\n\nconst SAFE_CRON_ID_RE = /^[a-zA-Z0-9._-]+$/;\n\nfunction validateCronId(id: string): string | null {\n if (!id || !SAFE_CRON_ID_RE.test(id)) {\n return `Invalid task ID \"${id}\". Use only letters, numbers, dash, underscore, dot.`;\n }\n return null;\n}\n\nasync function handleCronCommand(\n trimmed: string,\n cronScheduler: import(\"./cron.js\").CronScheduler,\n): Promise<string> {\n const args = trimmed.slice(\"/cron\".length).trim();\n\n // /cron or /cron list — list all tasks\n if (!args || args === \"list\") {\n const status = cronScheduler.getStatus();\n if (status.length === 0) return t(\"cmd_cron_empty\");\n const lines = status.map((s) => {\n const state = s.enabled ? \"✓\" : \"✗\";\n const errors = s.consecutiveErrors > 0 ? ` ⚠${s.consecutiveErrors}` : \"\";\n const last = s.lastRun\n ? `${s.lastRun.status === \"ok\" ? \"✓\" : \"✗\"} ${new Date(s.lastRun.finishedAt).toLocaleString()}`\n : \"-\";\n const next = s.nextRun ? new Date(s.nextRun).toLocaleString() : \"-\";\n return ` ${state} ${s.id}${s.name ? ` (${s.name})` : \"\"}${errors}\\n schedule: ${s.schedule}\\n last: ${last} | next: ${next}`;\n });\n return t(\"cmd_cron_list\", {\n count: String(status.length),\n list: lines.join(\"\\n\"),\n });\n }\n\n // /cron help\n if (args === \"help\") {\n return t(\"cmd_cron_help\");\n }\n\n // /cron status — scheduler status\n if (args === \"status\") {\n const s = cronScheduler.getSchedulerStatus();\n const concurrency = s.maxConcurrentRuns\n ? `${s.runningTasks}/${s.maxConcurrentRuns}`\n : String(s.runningTasks);\n return t(\"cmd_cron_status\", {\n state: s.running ? \"Running\" : \"Stopped\",\n total: String(s.taskCount),\n active: String(s.activeJobs),\n running: concurrency,\n next: s.nextWakeAt ? new Date(s.nextWakeAt).toLocaleString() : \"-\",\n });\n }\n\n // /cron run <id> [--due] — trigger task (optionally only if due)\n if (args.startsWith(\"run \")) {\n const runArgs = args.slice(4).trim().split(/\\s+/);\n const id = runArgs[0];\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const onlyIfDue = runArgs.includes(\"--due\");\n const result = await cronScheduler.runTask(id, { onlyIfDue });\n if (!result) {\n return onlyIfDue\n ? t(\"cmd_cron_not_due\", { id })\n : t(\"cmd_cron_not_found\", { id });\n }\n return t(\"cmd_cron_triggered\", {\n id,\n status:\n result.status === \"ok\"\n ? `✓ ${result.resultPreview?.slice(0, 100) ?? \"\"}`\n : `✗ ${result.error ?? \"unknown error\"}`,\n });\n }\n\n // /cron runs <id> [--status=ok|error|skipped] [--page=N] — view run history\n if (args.startsWith(\"runs \")) {\n const runsParts = args.slice(5).trim().split(/\\s+/);\n const id = runsParts[0];\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n\n // Parse optional flags\n let statusFilter: \"ok\" | \"error\" | \"skipped\" | undefined;\n let page = 0;\n const limit = 10;\n for (const flag of runsParts.slice(1)) {\n if (flag.startsWith(\"--status=\")) {\n const val = flag.slice(9);\n if (val === \"ok\" || val === \"error\" || val === \"skipped\") {\n statusFilter = val;\n }\n } else if (flag.startsWith(\"--page=\")) {\n page = Math.max(0, parseInt(flag.slice(7), 10) - 1);\n }\n }\n\n const result = cronScheduler.queryRunHistory(id, {\n limit,\n offset: page * limit,\n status: statusFilter,\n });\n\n if (result.entries.length === 0) return t(\"cmd_cron_runs_empty\", { id });\n const lines = result.entries.map((r) => {\n const time = new Date(r.ts).toLocaleString();\n const dur = `${(r.durationMs / 1000).toFixed(1)}s`;\n const status =\n r.status === \"ok\" ? \"✓\" : r.status === \"skipped\" ? \"⊘\" : \"✗\";\n const detail = r.error ?? r.summary?.slice(0, 80) ?? \"\";\n const delivery = r.deliveryStatus ? ` [${r.deliveryStatus}]` : \"\";\n return ` ${status} ${time} (${dur})${delivery}\\n ${detail}`;\n });\n const pageInfo = result.hasMore\n ? `\\n (page ${page + 1}, ${result.total} total, --page=${page + 2} for more)`\n : \"\";\n return t(\"cmd_cron_runs_header\", {\n id,\n count: String(result.total),\n list: lines.join(\"\\n\") + pageInfo,\n });\n }\n\n // /cron add <id> <schedule> <prompt> [--model=X] [--light] [--name=X] [--timeout=N]\n if (args.startsWith(\"add \")) {\n const parts = args.slice(4).trim().split(/\\s+/);\n if (parts.length < 3) return t(\"cmd_cron_help\");\n const id = parts[0];\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n\n // Separate flags from positional args\n const positional: string[] = [];\n let model: string | undefined;\n let light = false;\n let name: string | undefined;\n let timeoutSeconds: number | undefined;\n for (const p of parts.slice(1)) {\n if (p.startsWith(\"--model=\")) model = p.slice(8);\n else if (p === \"--light\") light = true;\n else if (p.startsWith(\"--name=\")) name = p.slice(7);\n else if (p.startsWith(\"--timeout=\")) {\n const n = parseInt(p.slice(10), 10);\n if (Number.isFinite(n) && n >= 0) timeoutSeconds = n;\n } else positional.push(p);\n }\n if (positional.length < 2) return t(\"cmd_cron_help\");\n\n const schedule = positional[0];\n const prompt = positional.slice(1).join(\" \");\n cronScheduler.addTask({\n id,\n schedule,\n prompt,\n enabled: true,\n ...(model ? { model } : {}),\n ...(light ? { lightContext: true } : {}),\n ...(name ? { name } : {}),\n ...(timeoutSeconds != null ? { timeoutSeconds } : {}),\n });\n return t(\"cmd_cron_added\", { id, schedule, prompt: prompt.slice(0, 60) });\n }\n\n // /cron edit <id> <field>=<value>\n if (args.startsWith(\"edit \")) {\n const parts = args.slice(5).trim().split(/\\s+/);\n if (parts.length < 2) return t(\"cmd_cron_help\");\n const id = parts[0];\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const patch: Record<string, unknown> = {};\n for (const kv of parts.slice(1)) {\n const eqIdx = kv.indexOf(\"=\");\n if (eqIdx === -1) continue;\n const key = kv.slice(0, eqIdx);\n const value = kv.slice(eqIdx + 1);\n if (key === \"enabled\") patch[key] = value === \"true\";\n else if (key === \"light_context\" || key === \"lightContext\")\n patch[\"lightContext\"] = value === \"true\";\n else if (key === \"delete_after_run\" || key === \"deleteAfterRun\")\n patch[\"deleteAfterRun\"] = value === \"true\";\n else if (\n key === \"timeout\" ||\n key === \"timeout_seconds\" ||\n key === \"timeoutSeconds\"\n ) {\n const n = parseInt(value, 10);\n if (Number.isFinite(n) && n >= 0) patch[\"timeoutSeconds\"] = n;\n } else if (\n key === \"schedule\" ||\n key === \"prompt\" ||\n key === \"model\" ||\n key === \"name\" ||\n key === \"description\" ||\n key === \"webhook_url\" ||\n key === \"webhookUrl\"\n ) {\n const patchKey = key === \"webhook_url\" ? \"webhookUrl\" : key;\n patch[patchKey] = value;\n }\n }\n const ok = cronScheduler.editTask(\n id,\n patch as Partial<import(\"./types.js\").CronTask>,\n );\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_edited\", { id });\n }\n\n // /cron remove <id>\n if (args.startsWith(\"remove \")) {\n const id = args.slice(7).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.removeTask(id);\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_removed\", { id });\n }\n\n // /cron enable <id>\n if (args.startsWith(\"enable \")) {\n const id = args.slice(7).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.editTask(id, { enabled: true });\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_enabled\", { id });\n }\n\n // /cron disable <id>\n if (args.startsWith(\"disable \")) {\n const id = args.slice(8).trim();\n if (!id) return t(\"cmd_cron_help\");\n const idErr = validateCronId(id);\n if (idErr) return idErr;\n const ok = cronScheduler.editTask(id, { enabled: false });\n if (!ok) return t(\"cmd_cron_not_found\", { id });\n return t(\"cmd_cron_disabled_task\", { id });\n }\n\n return t(\"cmd_cron_help\");\n}\n\nasync function main(): Promise<void> {\n const cmd = process.argv[2] ?? \"start\";\n\n const runWizard = async (\n fn: (m: typeof import(\"./setup-wizard.js\")) => Promise<void>,\n ) => {\n const m = await import(\"./setup-wizard.js\");\n await fn(m);\n process.exit(0);\n };\n\n switch (cmd) {\n case \"setup\": {\n const sub = process.argv[3];\n if (sub === \"--add-channel\") {\n await runWizard((m) => m.runAddChannel());\n } else if (sub === \"--remove-channel\") {\n await runWizard((m) => m.runRemoveChannel());\n } else {\n await runWizard((m) => m.runSetup());\n }\n break;\n }\n case \"add-channel\":\n await runWizard((m) => m.runAddChannel());\n break;\n case \"remove-channel\":\n await runWizard((m) => m.runRemoveChannel());\n break;\n case \"doctor\": {\n const doc = await import(\"./doctor.js\");\n await doc.runDoctor();\n process.exit(0);\n break;\n }\n case \"start\": {\n const flags = process.argv.slice(3);\n const foreground = flags.includes(\"--foreground\") || flags.includes(\"-f\");\n const daemon = await import(\"./daemon.js\");\n\n if (foreground) {\n daemon.registerForegroundPid();\n await start();\n } else {\n daemon.daemonize();\n }\n break;\n }\n case \"stop\": {\n const daemon = await import(\"./daemon.js\");\n await daemon.stopDaemon();\n break;\n }\n case \"status\": {\n const daemon = await import(\"./daemon.js\");\n const flags = process.argv.slice(3);\n if (flags.includes(\"--json\")) {\n daemon.showStatusJson();\n } else {\n daemon.showStatus();\n }\n break;\n }\n case \"logs\": {\n const daemon = await import(\"./daemon.js\");\n daemon.tailLogs();\n break;\n }\n case \"daemon\": {\n const sub = process.argv[3];\n const daemon = await import(\"./daemon.js\");\n if (sub === \"install\") {\n const portFlag = process.argv.find((a) => a.startsWith(\"--port=\"));\n const port = portFlag ? parseInt(portFlag.slice(7), 10) : 3000;\n daemon.installLaunchAgent(Number.isFinite(port) ? port : 3000);\n } else if (sub === \"uninstall\") {\n daemon.uninstallLaunchAgent();\n } else if (sub === \"status\") {\n const flags = process.argv.slice(4);\n if (flags.includes(\"--json\")) {\n daemon.showStatusJson();\n } else {\n daemon.showStatus();\n }\n } else {\n console.log(\n \"Usage: klaus daemon <command>\\n\\n\" +\n \"Commands:\\n\" +\n \" install [--port=N] Install launchd agent (macOS)\\n\" +\n \" uninstall Remove launchd agent\\n\" +\n \" status [--json] Show daemon status\\n\",\n );\n }\n process.exit(0);\n break;\n }\n default:\n console.log(\n \"Klaus — Use Claude Code from any messaging platform\\n\\n\" +\n \"Usage: klaus [command]\\n\\n\" +\n \"Commands:\\n\" +\n \" start Start the bot in background (default)\\n\" +\n \" start -f Start in foreground\\n\" +\n \" stop Stop the background daemon\\n\" +\n \" status Show daemon status\\n\" +\n \" status --json Machine-readable status\\n\" +\n \" logs Tail daemon logs\\n\" +\n \" daemon install Install launchd agent (macOS)\\n\" +\n \" daemon uninstall Remove launchd agent\\n\" +\n \" setup Interactive setup wizard\\n\" +\n \" add-channel Add a channel to existing config\\n\" +\n \" remove-channel Remove a channel from config\\n\" +\n \" doctor Diagnose environment issues\\n\",\n );\n process.exit(1);\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","/**\n * Claude Code SDK wrapper for multi-turn conversations.\n *\n * ClaudeChat: single session with collect mode (message queuing when busy).\n * ChatSessionManager: per-session instances with LRU eviction.\n */\n\nimport { query, type McpServerConfig } from \"@anthropic-ai/claude-agent-sdk\";\nimport { loadConfig, loadOneProxyConfig } from \"./config.js\";\nimport { getToolConfig } from \"./tool-config.js\";\nimport { DEFAULT_PERSONA } from \"./persona.js\";\nimport { ensureWorkspace, extractUserId } from \"./workspace.js\";\nimport type { SessionStore, PersistedSession } from \"./session-store.js\";\nimport type { MessageStore } from \"./message-store.js\";\nimport { type MemoryStore, buildMemoryFlushPrompt } from \"./memory-store.js\";\nimport { buildSkillsPrompt } from \"./skills/index.js\";\nimport type {\n ToolEventCallback,\n StreamChunkCallback,\n PermissionRequestCallback,\n PermissionRequest,\n} from \"./types.js\";\n\n/**\n * Memory flush interval: trigger a silent memory-save turn every N chat rounds.\n * Aligned with OpenClaw's pre-compaction flush concept, but using message count\n * as proxy since we don't have access to SDK token counts.\n */\nconst MEMORY_FLUSH_INTERVAL = 20;\n\n// Read-only tools — auto-allow without permission prompt\nconst READ_ONLY_TOOLS = new Set([\n \"Read\",\n \"Glob\",\n \"Grep\",\n \"WebSearch\",\n \"WebFetch\",\n \"TodoWrite\",\n]);\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction isSessionExpiredError(err: unknown): boolean {\n if (!(err instanceof Error)) return false;\n const msg = err.message.toLowerCase();\n return (\n msg.includes(\"session\") &&\n (msg.includes(\"not found\") ||\n msg.includes(\"expired\") ||\n msg.includes(\"invalid\"))\n );\n}\n\n// ---------------------------------------------------------------------------\n// Deferred: equivalent of Python asyncio.Future\n// ---------------------------------------------------------------------------\n\ninterface Deferred<T> {\n readonly promise: Promise<T>;\n resolve(value: T): void;\n}\n\nfunction createDeferred<T>(): Deferred<T> {\n let resolve!: (value: T) => void;\n const promise = new Promise<T>((r) => {\n resolve = r;\n });\n return { promise, resolve };\n}\n\n// ---------------------------------------------------------------------------\n// ClaudeChat: wraps Claude Agent SDK for multi-turn chat with collect mode\n// ---------------------------------------------------------------------------\n\ninterface ChatOptions {\n systemPrompt: string;\n model?: string;\n cwd?: string;\n mcpServers?: Record<string, McpServerConfig>;\n oneProxyBaseUrl?: string;\n}\n\ninterface PendingMessage {\n prompt: string;\n deferred: Deferred<string | null>;\n}\n\nexport class ClaudeChat {\n private sessionId: string | undefined;\n private busy = false;\n private pending: PendingMessage[] = [];\n private options: ChatOptions;\n private model: string | undefined;\n /** Number of completed chat rounds (for memory flush timing). */\n private chatRoundCount = 0;\n\n constructor(options: ChatOptions) {\n this.options = options;\n this.model = options.model;\n }\n\n /** Get the current Claude SDK session ID (for persistence). */\n getSessionId(): string | undefined {\n return this.sessionId;\n }\n\n /** Restore a session ID from persistent storage. */\n restoreSessionId(id: string): void {\n this.sessionId = id;\n }\n\n /**\n * Send a message to Claude. If the error indicates a stale/expired session,\n * clears sessionId and retries once without resume.\n */\n private async doChat(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string> {\n try {\n return await this.doChatInner(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n } catch (err) {\n if (this.sessionId && isSessionExpiredError(err)) {\n console.log(\"[Chat] Session expired, starting fresh session\");\n this.sessionId = undefined;\n return await this.doChatInner(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n }\n throw err;\n }\n }\n\n private async doChatInner(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string> {\n let resultText: string | undefined;\n let lastSessionId: string | undefined;\n\n const conversation = query({\n prompt,\n options: {\n systemPrompt: this.options.systemPrompt || undefined,\n permissionMode: onPermissionRequest ? \"default\" : \"bypassPermissions\",\n ...(this.options.cwd ? { cwd: this.options.cwd } : {}),\n ...(onPermissionRequest\n ? {\n canUseTool: async (\n toolName: string,\n input: Record<string, unknown>,\n opts: { toolUseID: string; decisionReason?: string },\n ) => {\n if (READ_ONLY_TOOLS.has(toolName)) {\n return { behavior: \"allow\" as const };\n }\n const config = getToolConfig(toolName);\n const request: PermissionRequest = {\n requestId: opts.toolUseID,\n toolName,\n toolUseId: opts.toolUseID,\n input,\n description: opts.decisionReason,\n display: {\n icon: config.icon,\n label: config.label,\n style: config.style,\n value: config.getValue(input),\n ...(config.getSecondary\n ? { secondary: config.getSecondary(input) }\n : {}),\n },\n };\n const response = await onPermissionRequest(request);\n return response.allow\n ? { behavior: \"allow\" as const }\n : {\n behavior: \"deny\" as const,\n message: \"User denied the tool execution\",\n };\n },\n }\n : {}),\n ...(this.model ? { model: this.model } : {}),\n ...(this.sessionId ? { resume: this.sessionId } : {}),\n ...(onStreamChunk ? { includePartialMessages: true } : {}),\n ...(this.options.mcpServers\n ? { mcpServers: this.options.mcpServers }\n : {}),\n ...(this.options.oneProxyBaseUrl\n ? {\n env: {\n ...process.env,\n ANTHROPIC_BASE_URL: this.options.oneProxyBaseUrl,\n ANTHROPIC_API_KEY:\n process.env.ANTHROPIC_API_KEY || \"sk-oneproxy\",\n },\n }\n : {}),\n },\n });\n\n for await (const msg of conversation) {\n if (msg.type === \"result\" && msg.subtype === \"success\") {\n resultText = msg.result;\n }\n if (\"session_id\" in msg && typeof msg.session_id === \"string\") {\n lastSessionId = msg.session_id;\n }\n\n // Extract tool use events for Web channel visualization\n if (onToolEvent) {\n this.emitToolEvents(msg, onToolEvent);\n }\n\n // Extract streaming text deltas\n if (msg.type === \"stream_event\" && onStreamChunk) {\n this.emitStreamChunk(msg, onStreamChunk);\n }\n }\n\n if (lastSessionId) {\n this.sessionId = lastSessionId;\n }\n\n this.chatRoundCount++;\n return resultText || \"(no response)\";\n }\n\n private emitToolEvents(\n msg: { type: string; [key: string]: unknown },\n onToolEvent: ToolEventCallback,\n ): void {\n // Sub-agent context: non-null when inside a sub-agent execution\n const parentToolUseId =\n typeof msg.parent_tool_use_id === \"string\"\n ? msg.parent_tool_use_id\n : undefined;\n\n // SDKAssistantMessage: content[] may contain tool_use blocks\n if (msg.type === \"assistant\") {\n const message = msg.message as\n | {\n content?: readonly {\n type: string;\n id?: string;\n name?: string;\n input?: unknown;\n }[];\n }\n | undefined;\n if (message?.content) {\n for (const block of message.content) {\n if (block.type === \"tool_use\" && block.id && block.name) {\n onToolEvent({\n type: \"tool_start\",\n toolUseId: block.id,\n toolName: block.name,\n input: (block.input ?? {}) as Record<string, unknown>,\n timestamp: Date.now(),\n ...(parentToolUseId ? { parentToolUseId } : {}),\n });\n }\n }\n }\n }\n\n // SDKUserMessage: content[] may contain tool_result blocks\n if (msg.type === \"user\") {\n const message = msg.message as\n | {\n content?:\n | readonly {\n type: string;\n tool_use_id?: string;\n is_error?: boolean;\n }[]\n | string;\n }\n | undefined;\n if (message?.content && Array.isArray(message.content)) {\n for (const block of message.content) {\n if (\n typeof block === \"object\" &&\n block !== null &&\n block.type === \"tool_result\" &&\n block.tool_use_id\n ) {\n onToolEvent({\n type: \"tool_result\",\n toolUseId: block.tool_use_id,\n toolName: \"\",\n isError: block.is_error ?? false,\n timestamp: Date.now(),\n ...(parentToolUseId ? { parentToolUseId } : {}),\n });\n }\n }\n }\n }\n }\n\n private emitStreamChunk(\n msg: { type: string; [key: string]: unknown },\n onStreamChunk: StreamChunkCallback,\n ): void {\n // SDKPartialAssistantMessage: stream_event with content_block_delta\n const event = msg.event as Record<string, unknown> | undefined;\n if (!event || event.type !== \"content_block_delta\") return;\n // Only emit top-level text (not sub-agent streams)\n if (typeof msg.parent_tool_use_id === \"string\") return;\n const delta = event.delta as Record<string, unknown> | undefined;\n if (delta?.type === \"text_delta\" && typeof delta.text === \"string\") {\n onStreamChunk(delta.text);\n }\n }\n\n /**\n * Send a message, return the full text reply.\n *\n * If the agent is busy, the message is queued (collect mode).\n * Returns null for callers whose messages were merged into a batch.\n */\n async chat(\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n ): Promise<string | null> {\n if (this.busy) {\n const deferred = createDeferred<string | null>();\n this.pending.push({ prompt, deferred });\n console.log(\n `[Collect] Queued (pending: ${this.pending.length}): ${prompt.slice(0, 80)}`,\n );\n return deferred.promise;\n }\n\n this.busy = true;\n try {\n let reply = await this.doChat(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n\n // Drain queued messages (collect mode)\n while (this.pending.length > 0) {\n const batch = [...this.pending];\n this.pending = [];\n\n const prompts = batch.map((b) => b.prompt);\n const merged =\n \"[以下是你处理上一条消息期间用户追加发送的消息]\\n\" +\n prompts.join(\"\\n\");\n console.log(\n `[Collect] Merging ${batch.length} queued message(s): ${merged.slice(0, 120)}`,\n );\n\n // Earlier callers: their messages are merged, no separate reply\n for (const item of batch.slice(0, -1)) {\n item.deferred.resolve(null);\n }\n\n // Process the merged message; ensure last caller's deferred\n // is always resolved even if doChat throws.\n try {\n reply = await this.doChat(\n merged,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n batch[batch.length - 1].deferred.resolve(reply);\n } catch (e) {\n batch[batch.length - 1].deferred.resolve(null);\n throw e;\n }\n }\n\n return reply;\n } catch (err) {\n // Resolve all pending deferreds so callers don't hang forever\n for (const item of this.pending) {\n item.deferred.resolve(null);\n }\n this.pending = [];\n await this.reset();\n throw err;\n } finally {\n this.busy = false;\n }\n }\n\n get isBusy(): boolean {\n return this.busy;\n }\n\n getModel(): string | undefined {\n return this.model;\n }\n\n setModel(model: string): void {\n this.model = model;\n }\n\n /** Update the system prompt (e.g. to refresh memory context). */\n setSystemPrompt(prompt: string): void {\n this.options = { ...this.options, systemPrompt: prompt };\n }\n\n /** Get the number of completed chat rounds. */\n getRoundCount(): number {\n return this.chatRoundCount;\n }\n\n async reset(): Promise<void> {\n this.sessionId = undefined;\n this.chatRoundCount = 0;\n }\n\n async close(): Promise<void> {\n await this.reset();\n }\n}\n\n// ---------------------------------------------------------------------------\n// ChatSessionManager: per-session ClaudeChat instances with LRU eviction\n// ---------------------------------------------------------------------------\n\nexport class ChatSessionManager {\n static readonly MAX_SESSIONS = 20;\n private sessions = new Map<string, ClaudeChat>();\n private options: ChatOptions;\n private store: SessionStore | undefined;\n private messageStore: MessageStore | undefined;\n private memoryStore: MemoryStore | undefined;\n private idleMs: number;\n\n constructor(\n store?: SessionStore,\n idleMs?: number,\n messageStore?: MessageStore,\n memoryStore?: MemoryStore,\n ) {\n const cfg = loadConfig();\n const persona = (cfg.persona as string) || DEFAULT_PERSONA;\n const model = (cfg.model as string) || undefined;\n const opCfg = loadOneProxyConfig();\n this.options = {\n systemPrompt: persona,\n model,\n ...(opCfg.enabled ? { oneProxyBaseUrl: opCfg.baseUrl } : {}),\n };\n this.store = store;\n this.messageStore = messageStore;\n this.memoryStore = memoryStore;\n this.idleMs = idleMs ?? 4 * 60 * 60 * 1000; // 4 hours default\n }\n\n /** Update the default model for new and existing sessions. */\n setDefaultModel(model: string | undefined): void {\n this.options = { ...this.options, model };\n for (const session of this.sessions.values()) {\n if (model) {\n session.setModel(model);\n }\n }\n }\n\n /** Get the current default model. */\n getDefaultModel(): string | undefined {\n return this.options.model;\n }\n\n /** Update the system prompt for new sessions (existing sessions keep their prompt until reset). */\n setPersona(persona: string): void {\n this.options = { ...this.options, systemPrompt: persona };\n }\n\n /** Inject MCP servers into all future sessions (called after CronScheduler init). */\n setMcpServers(servers: Record<string, McpServerConfig>): void {\n this.options = { ...this.options, mcpServers: servers };\n }\n\n /** Switch OneProxy mode. Clears all sessions when mode changes. */\n async setOneProxyConfig(config: {\n enabled: boolean;\n baseUrl: string;\n }): Promise<void> {\n const newUrl = config.enabled ? config.baseUrl : undefined;\n const oldUrl = this.options.oneProxyBaseUrl;\n if (newUrl === oldUrl) return;\n\n // Mode changed — clear all sessions (resume tokens are bound to base URL)\n for (const [key] of this.sessions) {\n await this.reset(key);\n }\n this.options = { ...this.options, oneProxyBaseUrl: newUrl };\n }\n\n /** Whether OneProxy mode is currently active. */\n isOneProxyEnabled(): boolean {\n return !!this.options.oneProxyBaseUrl;\n }\n\n private persistSession(key: string, session: ClaudeChat): void {\n if (!this.store) return;\n const sessionId = session.getSessionId();\n if (!sessionId) return;\n const existing = this.store.get(key);\n this.store.set(key, {\n sessionId,\n sessionKey: key,\n createdAt: existing?.createdAt ?? Date.now(),\n updatedAt: Date.now(),\n model: session.getModel(),\n });\n }\n\n private async evictIfNeeded(): Promise<void> {\n if (this.sessions.size < ChatSessionManager.MAX_SESSIONS) return;\n for (const [key, session] of this.sessions) {\n if (!session.isBusy) {\n // Save sessionId before eviction so it can be restored later\n this.persistSession(key, session);\n await session.close();\n this.sessions.delete(key);\n console.log(`[Session] Evicted (LRU): ${key}`);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save after eviction failed:\", err);\n });\n return;\n }\n }\n }\n\n private buildSystemPrompt(sessionKey: string): string {\n let prompt = this.options.systemPrompt;\n\n // Append skills section\n const skillsSection = buildSkillsPrompt();\n if (skillsSection) {\n prompt = `${prompt}\\n\\n${skillsSection}`;\n }\n\n // Append memory section\n if (this.memoryStore) {\n const memorySection = this.memoryStore.buildMemoryPrompt(sessionKey);\n if (memorySection) {\n prompt = `${prompt}\\n\\n${memorySection}`;\n }\n }\n\n return prompt;\n }\n\n private getSession(sessionKey: string): ClaudeChat {\n const existing = this.sessions.get(sessionKey);\n if (existing) {\n // Move to end (most recently used)\n this.sessions.delete(sessionKey);\n this.sessions.set(sessionKey, existing);\n return existing;\n }\n\n // Resolve per-user workspace directory (isolates file access)\n const userId = extractUserId(sessionKey);\n const cwd = userId ? ensureWorkspace(userId) : undefined;\n\n const sessionOptions: ChatOptions = {\n ...this.options,\n systemPrompt: this.buildSystemPrompt(sessionKey),\n ...(cwd ? { cwd } : {}),\n };\n const chat = new ClaudeChat(sessionOptions);\n\n // Restore sessionId from persistent store if fresh\n if (this.store) {\n const persisted = this.store.get(sessionKey);\n if (persisted && this.store.isFresh(sessionKey, this.idleMs)) {\n chat.restoreSessionId(persisted.sessionId);\n if (persisted.model) {\n chat.setModel(persisted.model);\n }\n console.log(`[Session] Restored from store: ${sessionKey}`);\n }\n }\n\n this.sessions.set(sessionKey, chat);\n console.log(\n `[Session] New session: ${sessionKey} (total: ${this.sessions.size})`,\n );\n return chat;\n }\n\n async chat(\n sessionKey: string,\n prompt: string,\n onToolEvent?: ToolEventCallback,\n onStreamChunk?: StreamChunkCallback,\n onPermissionRequest?: PermissionRequestCallback,\n /** User-facing display text for history (defaults to prompt if omitted). */\n displayText?: string,\n ): Promise<string | null> {\n await this.evictIfNeeded();\n const session = this.getSession(sessionKey);\n\n // Refresh memory in system prompt before each chat\n if (this.memoryStore) {\n session.setSystemPrompt(this.buildSystemPrompt(sessionKey));\n }\n\n const result = await session.chat(\n prompt,\n onToolEvent,\n onStreamChunk,\n onPermissionRequest,\n );\n\n // Persist after successful chat (fire-and-forget)\n if (result !== null) {\n this.persistSession(sessionKey, session);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n\n // Append messages to transcript (fire-and-forget async)\n if (this.messageStore) {\n this.messageStore\n .append(sessionKey, \"user\", displayText ?? prompt)\n .then(() =>\n this.messageStore!.append(sessionKey, \"assistant\", result),\n )\n .catch((err) => console.error(\"[MessageStore] Append failed:\", err));\n }\n\n // Memory flush: schedule a silent turn AFTER returning, so the user\n // gets their reply immediately. The flush runs when the session is idle.\n if (this.shouldFlushMemory(session)) {\n const sk = sessionKey;\n setTimeout(() => {\n this.runMemoryFlush(sk, session).catch((err) => {\n console.error(\"[Memory] Flush failed:\", err);\n });\n }, 500);\n }\n }\n\n return result;\n }\n\n /** Check if memory flush should trigger (without side effects). */\n private shouldFlushMemory(session: ClaudeChat): boolean {\n if (!this.memoryStore) return false;\n const rounds = session.getRoundCount();\n return rounds > 0 && rounds % MEMORY_FLUSH_INTERVAL === 0;\n }\n\n /**\n * Run a silent memory flush turn. Aligned with OpenClaw's pre-compaction\n * flush: a hidden agent turn that saves durable memories to disk.\n *\n * Skips if the session is busy (user sent a new message before flush ran).\n * The flush reply is discarded — the user never sees it.\n */\n private async runMemoryFlush(\n sessionKey: string,\n session: ClaudeChat,\n ): Promise<void> {\n // Skip if user already started a new conversation turn\n if (session.isBusy) {\n console.log(`[Memory] Flush skipped (session busy): ${sessionKey}`);\n return;\n }\n\n console.log(\n `[Memory] Triggering flush for ${sessionKey} (round ${session.getRoundCount()})`,\n );\n try {\n session.setSystemPrompt(this.buildSystemPrompt(sessionKey));\n const flushReply = await session.chat(buildMemoryFlushPrompt());\n if (flushReply) {\n console.log(\n `[Memory] Flush complete for ${sessionKey}: ${flushReply.slice(0, 80)}`,\n );\n }\n } catch (err) {\n console.error(`[Memory] Flush error for ${sessionKey}:`, err);\n }\n }\n\n /**\n * Lightweight chat: skips persona/skills/memory in system prompt.\n * Used by cron tasks with lightContext: true for faster execution.\n */\n async chatLight(sessionKey: string, prompt: string): Promise<string | null> {\n await this.evictIfNeeded();\n\n // Create a session with minimal system prompt (no persona, skills, memory)\n const existing = this.sessions.get(sessionKey);\n if (existing) {\n // LRU update: move to end\n this.sessions.delete(sessionKey);\n this.sessions.set(sessionKey, existing);\n } else {\n const userId = extractUserId(sessionKey);\n const cwd = userId ? ensureWorkspace(userId) : undefined;\n const chat = new ClaudeChat({\n systemPrompt: \"You are a helpful assistant. Be concise.\",\n model: this.options.model,\n ...(cwd ? { cwd } : {}),\n ...(this.options.mcpServers\n ? { mcpServers: this.options.mcpServers }\n : {}),\n });\n\n // Restore from store if available\n if (this.store) {\n const persisted = this.store.get(sessionKey);\n if (persisted && this.store.isFresh(sessionKey, this.idleMs)) {\n chat.restoreSessionId(persisted.sessionId);\n if (persisted.model) chat.setModel(persisted.model);\n }\n }\n\n this.sessions.set(sessionKey, chat);\n }\n\n const session = this.sessions.get(sessionKey)!;\n const result = await session.chat(prompt);\n\n if (result !== null) {\n this.persistSession(sessionKey, session);\n this.store?.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n }\n\n return result;\n }\n\n setModel(sessionKey: string, model: string): void {\n const session = this.getSession(sessionKey);\n session.setModel(model);\n }\n\n getModel(sessionKey: string): string | undefined {\n return this.sessions.get(sessionKey)?.getModel();\n }\n\n getSessionInfo(sessionKey: string): {\n active: boolean;\n busy: boolean;\n model: string | undefined;\n } {\n const session = this.sessions.get(sessionKey);\n return {\n active: !!session,\n busy: session?.isBusy ?? false,\n model: session?.getModel(),\n };\n }\n\n async reset(sessionKey: string): Promise<void> {\n const session = this.sessions.get(sessionKey);\n if (session) {\n await session.reset();\n this.sessions.delete(sessionKey);\n\n // Remove from persistent store\n if (this.store) {\n this.store.delete(sessionKey);\n this.store.save().catch((err) => {\n console.error(\"[SessionStore] Save failed:\", err);\n });\n }\n\n console.log(`[Session] Reset: ${sessionKey}`);\n }\n }\n\n async close(): Promise<void> {\n // Persist all active sessions before closing\n if (this.store) {\n for (const [key, session] of this.sessions) {\n this.persistSession(key, session);\n }\n await this.store.close().catch((err) => {\n console.error(\"[SessionStore] Failed to save on close:\", err);\n });\n }\n\n for (const session of this.sessions.values()) {\n await session.close();\n }\n this.sessions.clear();\n }\n}\n","/**\n * Standardized inbound message types and formatting.\n *\n * Inspired by OpenClaw's MsgContext, simplified for Klaus.\n * Channels produce InboundMessage objects; formatPrompt() converts them\n * into text prompts for Claude.\n */\n\nimport { writeFileSync, mkdirSync } from \"node:fs\";\nimport { join, basename } from \"node:path\";\nimport { tmpdir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type MessageType =\n | \"text\"\n | \"image\"\n | \"voice\"\n | \"video\"\n | \"location\"\n | \"link\"\n | \"file\"\n | \"emoji\"\n | \"mixed\";\n\nexport interface MediaFile {\n readonly type: \"image\" | \"audio\" | \"video\" | \"file\";\n readonly path?: string;\n readonly url?: string;\n readonly fileName?: string;\n /** ASR transcription result (voice messages). */\n readonly transcription?: string;\n}\n\nexport interface ReplyContext {\n readonly messageId?: string;\n /** Preview of the replied-to message. */\n readonly text?: string;\n}\n\nexport interface LocationInfo {\n readonly label?: string;\n readonly latitude: number;\n readonly longitude: number;\n readonly scale?: number;\n}\n\nexport interface LinkInfo {\n readonly title?: string;\n readonly description?: string;\n readonly url: string;\n}\n\nexport interface InboundMessage {\n readonly sessionKey: string;\n /** Main text content (empty string when no text). */\n readonly text: string;\n readonly messageType: MessageType;\n readonly chatType: \"private\" | \"group\";\n readonly senderId: string;\n readonly senderName?: string;\n readonly media?: readonly MediaFile[];\n readonly replyTo?: ReplyContext;\n readonly mentions?: readonly string[];\n readonly location?: LocationInfo;\n readonly link?: LinkInfo;\n readonly emoji?: { readonly id?: number; readonly description?: string };\n readonly timestamp?: number;\n}\n\n// ---------------------------------------------------------------------------\n// Display text: InboundMessage → user-facing text (no internal paths)\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a structured InboundMessage into user-facing display text.\n * Unlike formatPrompt(), this hides internal file paths and only shows\n * file names — safe to persist in message history and show in the UI.\n */\nexport function formatDisplayText(msg: InboundMessage): string {\n const parts: string[] = [];\n\n if (msg.text) {\n parts.push(msg.text);\n }\n\n if (msg.media?.length) {\n for (const file of msg.media) {\n switch (file.type) {\n case \"image\":\n parts.push(file.fileName ? `[图片: ${file.fileName}]` : \"[图片]\");\n break;\n case \"audio\":\n parts.push(\n file.transcription\n ? `[语音: \"${file.transcription}\"]`\n : \"[语音消息]\",\n );\n break;\n case \"video\":\n parts.push(\"[视频]\");\n break;\n case \"file\":\n parts.push(`[文件: ${file.fileName || \"未知文件\"}]`);\n break;\n }\n }\n }\n\n return parts.join(\"\\n\").trim();\n}\n\n// ---------------------------------------------------------------------------\n// Format prompt: InboundMessage → text string for Claude\n// ---------------------------------------------------------------------------\n\n/**\n * Convert a structured InboundMessage into a text prompt for Claude.\n * Centralizes the formatting logic previously duplicated in each channel.\n */\nexport function formatPrompt(msg: InboundMessage): string {\n const parts: string[] = [];\n\n // Reply context (prepend)\n if (msg.replyTo?.text) {\n const preview =\n msg.replyTo.text.length > 200\n ? msg.replyTo.text.slice(0, 200) + \"...\"\n : msg.replyTo.text;\n parts.push(`[回复消息: \"${preview}\"]`);\n } else if (msg.replyTo) {\n parts.push(\"[回复了一条消息]\");\n }\n\n // Mentions\n if (msg.mentions?.length) {\n for (const uid of msg.mentions) {\n parts.push(uid === \"all\" ? \"[@全体成员]\" : `[@用户:${uid}]`);\n }\n }\n\n // Text content\n if (msg.text) {\n parts.push(msg.text);\n }\n\n // Media files\n if (msg.media?.length) {\n for (const file of msg.media) {\n parts.push(formatMediaFile(file));\n }\n }\n\n // Emoji\n if (msg.emoji) {\n const desc = msg.emoji.description ?? msg.emoji.id;\n if (desc !== undefined && desc !== \"\") {\n parts.push(`[表情:${desc}]`);\n }\n }\n\n // Location\n if (msg.location) {\n const loc = msg.location;\n parts.push(\n \"[用户分享了一个位置]\\n\" +\n `地点: ${loc.label || \"未知\"}\\n` +\n `坐标: ${loc.latitude}, ${loc.longitude}` +\n (loc.scale != null ? `\\n缩放: ${loc.scale}` : \"\"),\n );\n }\n\n // Link\n if (msg.link) {\n const linkParts = [\"[用户分享了一个链接]\"];\n if (msg.link.title) linkParts.push(`标题: ${msg.link.title}`);\n if (msg.link.description) linkParts.push(`描述: ${msg.link.description}`);\n if (msg.link.url) linkParts.push(`链接: ${msg.link.url}`);\n parts.push(linkParts.join(\"\\n\"));\n }\n\n return parts.join(\"\\n\").trim();\n}\n\nfunction formatMediaFile(file: MediaFile): string {\n switch (file.type) {\n case \"image\": {\n if (file.path) {\n return `[图片: ${file.path},请用 Read 工具查看]`;\n }\n return \"[图片: 下载失败]\";\n }\n\n case \"audio\": {\n if (file.transcription) {\n return (\n `[用户发送了一段语音消息,语音识别结果: \"${file.transcription}\"]\\n` +\n \"请基于语音识别的内容回复用户。\"\n );\n }\n return (\n \"[用户发送了一段语音消息,但你目前无法听取语音。\" +\n \"请友好地告诉用户:语音消息暂不支持,请将想说的内容打字发送给你。]\"\n );\n }\n\n case \"video\":\n return (\n \"[用户发送了一段视频,但你目前无法观看视频。\" +\n \"请友好地告诉用户:视频消息暂不支持,请用文字描述视频内容或截图发送。]\"\n );\n\n case \"file\": {\n if (file.path) {\n const displayName = file.fileName || \"未知文件\";\n return `[文件: ${file.path},文件名: ${displayName},请用 Read 工具查看]`;\n }\n return `[文件 ${file.fileName || \"未知\"}: 下载失败]`;\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Shared file download utility\n// ---------------------------------------------------------------------------\n\nconst TEMP_DIR = join(tmpdir(), \"klaus-files\");\nmkdirSync(TEMP_DIR, { recursive: true });\n\nconst MAX_DOWNLOAD_SIZE = 50 * 1024 * 1024; // 50 MB\n\n/**\n * Download a file from a URL to a temporary local path.\n * Returns the absolute path to the downloaded file.\n */\nexport async function downloadFile(\n rawUrl: string,\n name?: string,\n): Promise<string> {\n const url = rawUrl.startsWith(\"http\") ? rawUrl : `https://${rawUrl}`;\n const resp = await fetch(url);\n if (!resp.ok) throw new Error(`HTTP ${resp.status}`);\n\n const contentLength = Number(resp.headers.get(\"content-length\") ?? 0);\n if (contentLength > MAX_DOWNLOAD_SIZE) {\n throw new Error(`File too large: ${contentLength} bytes`);\n }\n\n const buffer = Buffer.from(await resp.arrayBuffer());\n if (buffer.byteLength > MAX_DOWNLOAD_SIZE) {\n throw new Error(`File too large: ${buffer.byteLength} bytes`);\n }\n\n const fallbackExt = url.match(/\\.([\\w]+)(?:\\?|$)/)?.[1] ?? \"bin\";\n const safeName = name ? basename(name).replace(/[^\\w.\\-]/g, \"_\") : undefined;\n const filename = safeName\n ? `${Date.now()}-${safeName}`\n : `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.${fallbackExt}`;\n const filepath = join(TEMP_DIR, filename);\n writeFileSync(filepath, buffer);\n return filepath;\n}\n\n/** The shared temp directory for downloaded files. */\nexport { TEMP_DIR, MAX_DOWNLOAD_SIZE };\n","/**\n * Cron marker parser — extracts [[cron:action {json}]] markers from Claude's reply.\n *\n * Enables AI-driven cron task management: Claude includes markers in replies,\n * the handler extracts them, executes cron operations, and strips markers\n * from the displayed text.\n */\n\nimport type { CronTask, CronDelivery } from \"./types.js\";\n\n// ---------------------------------------------------------------------------\n// Types\n// ---------------------------------------------------------------------------\n\nexport type CronMarkerAction =\n | { readonly action: \"add\"; readonly task: CronTask }\n | {\n readonly action: \"edit\";\n readonly id: string;\n readonly patch: Partial<CronTask>;\n }\n | { readonly action: \"remove\"; readonly id: string }\n | { readonly action: \"enable\"; readonly id: string }\n | { readonly action: \"disable\"; readonly id: string };\n\nexport interface CronMarkerResult {\n /** Reply text with all [[cron:...]] markers stripped. */\n readonly text: string;\n /** Parsed cron actions to execute. */\n readonly actions: readonly CronMarkerAction[];\n}\n\n// ---------------------------------------------------------------------------\n// Parser\n// ---------------------------------------------------------------------------\n\nconst CRON_MARKER_PATTERN =\n /\\[\\[cron:(add|edit|remove|enable|disable)\\s+(\\{[\\s\\S]*?\\})\\]\\]/g;\n\n// Safe ID: only alphanumeric, dash, underscore, dot\nconst SAFE_ID_RE = /^[a-zA-Z0-9._-]+$/;\n\n/**\n * Parse [[cron:action {json}]] markers from a reply string.\n * Returns the cleaned text and parsed actions.\n */\nexport function parseCronMarkers(reply: string): CronMarkerResult {\n const actions: CronMarkerAction[] = [];\n\n // Use fresh regex per call to avoid shared lastIndex state\n const re = new RegExp(CRON_MARKER_PATTERN.source, \"g\");\n let match: RegExpExecArray | null;\n\n while ((match = re.exec(reply)) !== null) {\n const actionType = match[1] as\n | \"add\"\n | \"edit\"\n | \"remove\"\n | \"enable\"\n | \"disable\";\n const jsonStr = match[2];\n\n try {\n const data = JSON.parse(jsonStr) as Record<string, unknown>;\n const parsed = parseAction(actionType, data);\n if (parsed) {\n actions.push(parsed);\n }\n } catch (err) {\n console.warn(`[CronMarker] Failed to parse marker: ${err}`);\n }\n }\n\n // Strip all markers from displayed text\n const text = reply.replace(CRON_MARKER_PATTERN, \"\").trim();\n\n return { text, actions };\n}\n\nfunction parseAction(\n action: string,\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n switch (action) {\n case \"add\":\n return parseAddAction(data);\n case \"edit\":\n return parseEditAction(data);\n case \"remove\":\n case \"enable\":\n case \"disable\":\n return parseIdAction(action, data);\n default:\n return null;\n }\n}\n\nfunction parseAddAction(\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID: \"${id}\"`);\n return null;\n }\n\n const schedule = String(data.schedule ?? \"\");\n const prompt = String(data.prompt ?? \"\");\n if (!schedule || !prompt) {\n console.warn(`[CronMarker] Missing schedule or prompt for task \"${id}\"`);\n return null;\n }\n\n const task: CronTask = {\n id,\n schedule,\n prompt,\n enabled: true,\n ...(data.name != null ? { name: String(data.name) } : {}),\n ...(data.description != null\n ? { description: String(data.description) }\n : {}),\n ...(data.model != null ? { model: String(data.model) } : {}),\n ...(data.lightContext === true ? { lightContext: true } : {}),\n ...(data.timeoutSeconds != null\n ? { timeoutSeconds: Math.floor(Number(data.timeoutSeconds)) }\n : {}),\n ...(data.deliver ? { deliver: parseDeliver(data.deliver) } : {}),\n };\n\n return { action: \"add\", task };\n}\n\nfunction parseEditAction(\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID for edit: \"${id}\"`);\n return null;\n }\n\n // Build patch from all fields except 'id'\n const patch: Record<string, unknown> = {};\n if (data.schedule != null) patch.schedule = String(data.schedule);\n if (data.prompt != null) patch.prompt = String(data.prompt);\n if (data.name != null) patch.name = String(data.name);\n if (data.description != null) patch.description = String(data.description);\n if (data.model != null) patch.model = String(data.model);\n if (data.lightContext != null)\n patch.lightContext = data.lightContext === true;\n if (data.timeoutSeconds != null)\n patch.timeoutSeconds = Math.floor(Number(data.timeoutSeconds));\n if (data.enabled != null) patch.enabled = data.enabled === true;\n if (data.deliver != null) patch.deliver = parseDeliver(data.deliver);\n\n if (Object.keys(patch).length === 0) {\n console.warn(`[CronMarker] Empty patch for edit \"${id}\"`);\n return null;\n }\n\n return { action: \"edit\", id, patch: patch as Partial<CronTask> };\n}\n\nfunction parseIdAction(\n action: \"remove\" | \"enable\" | \"disable\",\n data: Record<string, unknown>,\n): CronMarkerAction | null {\n const id = String(data.id ?? \"\");\n if (!id || !SAFE_ID_RE.test(id)) {\n console.warn(`[CronMarker] Invalid task ID for ${action}: \"${id}\"`);\n return null;\n }\n return { action, id };\n}\n\nfunction parseDeliver(raw: unknown): CronDelivery | undefined {\n if (!raw || typeof raw !== \"object\") return undefined;\n const d = raw as Record<string, unknown>;\n const channel = String(d.channel ?? \"web\");\n return {\n channel,\n ...(d.to ? { to: String(d.to) } : {}),\n ...(d.mode ? { mode: String(d.mode) as CronDelivery[\"mode\"] } : {}),\n ...(d.bestEffort === true ? { bestEffort: true } : {}),\n };\n}\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/daemon.ts"],"sourcesContent":["/**\n * Daemon management — PID file, log rotation, process lifecycle.\n *\n * `klaus start` → fork child in background, parent exits immediately\n * `klaus start -f` → run in foreground (current behavior)\n * `klaus stop` → send SIGTERM to daemon\n * `klaus status` → check if daemon is running\n */\n\nimport { execFileSync, spawn } from \"node:child_process\";\nimport {\n existsSync,\n mkdirSync,\n readFileSync,\n unlinkSync,\n writeFileSync,\n openSync,\n} from \"node:fs\";\nimport { join, dirname } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { CONFIG_DIR } from \"./config.js\";\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\nconst PID_FILE = join(CONFIG_DIR, \"klaus.pid\");\nconst LOG_DIR = join(CONFIG_DIR, \"logs\");\nconst LOG_FILE = join(LOG_DIR, \"klaus.log\");\n\n// ---------------------------------------------------------------------------\n// PID helpers\n// ---------------------------------------------------------------------------\n\nfunction readPid(): number | null {\n if (!existsSync(PID_FILE)) return null;\n const raw = readFileSync(PID_FILE, \"utf-8\").trim();\n const pid = parseInt(raw, 10);\n return Number.isFinite(pid) ? pid : null;\n}\n\n/** Atomic PID write using exclusive create ('wx') to prevent races. */\nfunction writePidExclusive(pid: number): boolean {\n mkdirSync(CONFIG_DIR, { recursive: true });\n try {\n writeFileSync(PID_FILE, String(pid), { mode: 0o644, flag: \"wx\" });\n return true;\n } catch (err: unknown) {\n if ((err as NodeJS.ErrnoException).code === \"EEXIST\") return false;\n throw err;\n }\n}\n\n/** Overwrite PID file (used after stale PID cleanup). */\nfunction writePid(pid: number): void {\n mkdirSync(CONFIG_DIR, { recursive: true });\n writeFileSync(PID_FILE, String(pid), { mode: 0o644 });\n}\n\nfunction removePid(): void {\n try {\n unlinkSync(PID_FILE);\n } catch {\n // ignore if already gone\n }\n}\n\nfunction isProcessRunning(pid: number): boolean {\n try {\n process.kill(pid, 0); // signal 0 = existence check\n return true;\n } catch {\n return false;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Fork `klaus start --foreground` as a detached background process.\n * Redirects stdout/stderr to the log file.\n * The parent process exits after the child is spawned.\n */\nexport function daemonize(): void {\n mkdirSync(LOG_DIR, { recursive: true });\n\n // Try atomic PID file creation first (prevents race between concurrent starts)\n const existingPid = readPid();\n if (existingPid !== null) {\n if (isProcessRunning(existingPid)) {\n console.log(`Klaus is already running (PID ${existingPid}).`);\n console.log(`Log: ${LOG_FILE}`);\n process.exit(0);\n }\n // Stale PID file — remove and retry\n removePid();\n }\n\n // Reserve PID file atomically BEFORE spawning to prevent race conditions.\n // Write a placeholder (parent PID) — will be overwritten with child PID.\n if (!writePidExclusive(process.pid)) {\n // Another daemonize() call won the race\n console.log(\"Klaus is already starting from another process.\");\n process.exit(0);\n }\n\n // Open log file for append\n const logFd = openSync(LOG_FILE, \"a\");\n\n // Re-spawn ourselves with --foreground\n const scriptArgs = getScriptArgs();\n const child = spawn(process.execPath, [...scriptArgs, \"--foreground\"], {\n detached: true,\n stdio: [\"ignore\", logFd, logFd],\n env: { ...process.env },\n });\n\n child.unref();\n\n const childPid = child.pid;\n if (childPid == null) {\n removePid();\n console.error(\"Failed to start daemon.\");\n process.exit(1);\n }\n\n // Overwrite placeholder with actual child PID\n writePid(childPid);\n\n console.log(`Klaus started in background (PID ${childPid}).`);\n console.log(`Log: ${LOG_FILE}`);\n process.exit(0);\n}\n\n/**\n * Write PID file for foreground mode (so `klaus stop` still works).\n * Registers cleanup on exit.\n */\nexport function registerForegroundPid(): void {\n // Prevent overwriting an active daemon's PID\n const existing = readPid();\n if (\n existing !== null &&\n isProcessRunning(existing) &&\n existing !== process.pid\n ) {\n console.error(\n `Klaus is already running as daemon (PID ${existing}). Stop it first with: klaus stop`,\n );\n process.exit(1);\n }\n writePid(process.pid);\n let cleaned = false;\n const cleanup = () => {\n if (cleaned) return;\n cleaned = true;\n removePid();\n };\n process.on(\"exit\", cleanup);\n process.on(\"SIGINT\", () => {\n cleanup();\n process.exit(0);\n });\n process.on(\"SIGTERM\", () => {\n cleanup();\n process.exit(0);\n });\n}\n\n/**\n * Stop a running daemon by sending SIGTERM.\n * Waits up to 5 seconds for the process to exit before giving up.\n */\nexport async function stopDaemon(): Promise<void> {\n const pid = readPid();\n if (pid === null) {\n console.log(\"Klaus is not running (no PID file found).\");\n return;\n }\n\n if (!isProcessRunning(pid)) {\n console.log(`Klaus is not running (stale PID ${pid}). Cleaning up.`);\n removePid();\n return;\n }\n\n try {\n process.kill(pid, \"SIGTERM\");\n console.log(`Sent SIGTERM to Klaus (PID ${pid}). Waiting for exit...`);\n } catch (err) {\n console.error(`Failed to stop Klaus (PID ${pid}):`, err);\n process.exit(1);\n }\n\n // Poll until process exits (up to 5s)\n const deadline = Date.now() + 5_000;\n while (Date.now() < deadline) {\n if (!isProcessRunning(pid)) {\n removePid();\n console.log(\"Klaus stopped.\");\n return;\n }\n await sleep(200);\n }\n\n console.log(`Klaus (PID ${pid}) did not exit within 5s. PID file kept.`);\n}\n\n/**\n * Print daemon status.\n */\nexport function showStatus(): void {\n const pid = readPid();\n if (pid === null) {\n console.log(\"Klaus is not running.\");\n return;\n }\n\n if (isProcessRunning(pid)) {\n console.log(`Klaus is running (PID ${pid}).`);\n console.log(`Log: ${LOG_FILE}`);\n } else {\n console.log(`Klaus is not running (stale PID ${pid}). Cleaning up.`);\n removePid();\n }\n}\n\n/**\n * Tail the daemon log file (like `tail -f`).\n */\nexport function tailLogs(): void {\n if (!existsSync(LOG_FILE)) {\n console.log(\"No log file found. Is Klaus running?\");\n process.exit(1);\n }\n\n const tail = spawn(\"tail\", [\"-f\", LOG_FILE], {\n stdio: [\"ignore\", \"inherit\", \"inherit\"],\n });\n\n tail.on(\"error\", (err) => {\n console.error(\"Failed to tail logs:\", err.message);\n process.exit(1);\n });\n\n process.once(\"SIGINT\", () => {\n tail.kill(\"SIGINT\");\n process.exit(0);\n });\n process.once(\"SIGTERM\", () => {\n tail.kill(\"SIGTERM\");\n process.exit(0);\n });\n\n tail.on(\"exit\", (code) => {\n process.exit(code ?? 0);\n });\n}\n\n// ---------------------------------------------------------------------------\n// Internal helpers\n// ---------------------------------------------------------------------------\n\n/** Reconstruct script args for re-spawn: [scriptPath, \"start\"] */\nfunction getScriptArgs(): string[] {\n const scriptPath = process.argv[1];\n if (scriptPath.endsWith(\".ts\")) {\n console.error(\n \"Daemon mode is not supported in dev (tsx). Use --foreground (-f) instead.\",\n );\n process.exit(1);\n }\n return [scriptPath, \"start\"];\n}\n\nfunction sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n\n// ---------------------------------------------------------------------------\n// launchd integration (macOS only)\n// ---------------------------------------------------------------------------\n\nconst LAUNCHD_LABEL = \"ai.klaus.daemon\";\nconst LAUNCHD_DIR = join(homedir(), \"Library\", \"LaunchAgents\");\nconst LAUNCHD_PLIST = join(LAUNCHD_DIR, `${LAUNCHD_LABEL}.plist`);\n\nfunction findKlausBinary(): string | null {\n // 1. If running from built dist, use the script path directly\n const scriptPath = process.argv[1];\n if (scriptPath && !scriptPath.endsWith(\".ts\") && existsSync(scriptPath)) {\n return scriptPath;\n }\n // 2. Try common global install locations\n const candidates = [\n join(homedir(), \".npm-global\", \"bin\", \"klaus\"),\n \"/usr/local/bin/klaus\",\n \"/opt/homebrew/bin/klaus\",\n ];\n for (const c of candidates) {\n if (existsSync(c)) return c;\n }\n return null;\n}\n\nfunction buildPlist(binPath: string, port: number): string {\n const logPath = LOG_FILE;\n return `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n <key>Label</key>\n <string>${LAUNCHD_LABEL}</string>\n <key>ProgramArguments</key>\n <array>\n <string>${process.execPath}</string>\n <string>${binPath}</string>\n <string>start</string>\n <string>--foreground</string>\n </array>\n <key>RunAtLoad</key>\n <true/>\n <key>KeepAlive</key>\n <dict>\n <key>SuccessfulExit</key>\n <false/>\n </dict>\n <key>StandardOutPath</key>\n <string>${logPath}</string>\n <key>StandardErrorPath</key>\n <string>${logPath}</string>\n <key>EnvironmentVariables</key>\n <dict>\n <key>PATH</key>\n <string>/usr/local/bin:/opt/homebrew/bin:/usr/bin:/bin</string>\n <key>HOME</key>\n <string>${homedir()}</string>\n <key>KLAUS_WEB_PORT</key>\n <string>${port}</string>\n </dict>\n</dict>\n</plist>`;\n}\n\n/**\n * Install a launchd plist so Klaus starts automatically on login.\n */\nexport function installLaunchAgent(port = 3000): void {\n if (process.platform !== \"darwin\") {\n console.error(\"launchd is only available on macOS.\");\n process.exit(1);\n }\n\n const binPath = findKlausBinary();\n if (!binPath) {\n console.error(\n \"Could not find the klaus binary. Install globally first: npm install -g klaus-ai\",\n );\n process.exit(1);\n }\n\n mkdirSync(LAUNCHD_DIR, { recursive: true });\n mkdirSync(LOG_DIR, { recursive: true });\n\n const plist = buildPlist(binPath, port);\n writeFileSync(LAUNCHD_PLIST, plist, \"utf-8\");\n\n // Load the agent\n try {\n // Unload first (ignore errors if not loaded)\n try {\n execFileSync(\"launchctl\", [\"unload\", LAUNCHD_PLIST], { stdio: \"ignore\" });\n } catch {\n /* ok */\n }\n execFileSync(\"launchctl\", [\"load\", LAUNCHD_PLIST], { stdio: \"inherit\" });\n } catch {\n console.error(\n \"Failed to load launchd agent. You may need to load it manually:\",\n );\n console.error(` launchctl load \"${LAUNCHD_PLIST}\"`);\n }\n\n console.log(`Installed launchd agent: ${LAUNCHD_LABEL}`);\n console.log(`Plist: ${LAUNCHD_PLIST}`);\n console.log(`Klaus will start automatically on login (port ${port}).`);\n}\n\n/**\n * Uninstall the launchd plist.\n */\nexport function uninstallLaunchAgent(): void {\n if (process.platform !== \"darwin\") {\n console.error(\"launchd is only available on macOS.\");\n process.exit(1);\n }\n\n if (!existsSync(LAUNCHD_PLIST)) {\n console.log(\"No launchd agent installed.\");\n return;\n }\n\n try {\n execFileSync(\"launchctl\", [\"unload\", LAUNCHD_PLIST], { stdio: \"ignore\" });\n } catch {\n /* ok if not loaded */\n }\n\n unlinkSync(LAUNCHD_PLIST);\n console.log(`Uninstalled launchd agent: ${LAUNCHD_LABEL}`);\n}\n\n/**\n * Machine-readable status output for the macOS app.\n */\nexport function showStatusJson(): void {\n const pid = readPid();\n const running = pid !== null && isProcessRunning(pid);\n\n // Clean up stale PID\n if (pid !== null && !running) {\n removePid();\n }\n\n const status = {\n running,\n pid: running ? pid : null,\n logFile: LOG_FILE,\n pidFile: PID_FILE,\n configDir: CONFIG_DIR,\n launchAgent: existsSync(LAUNCHD_PLIST) ? LAUNCHD_PLIST : null,\n version: getVersion(),\n };\n\n console.log(JSON.stringify(status));\n}\n\nfunction getVersion(): string {\n try {\n const pkgPath = join(__dirname, \"..\", \"package.json\");\n if (existsSync(pkgPath)) {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n return pkg.version ?? \"unknown\";\n }\n } catch {\n /* ignore */\n }\n return \"unknown\";\n}\n"],"mappings":"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","names":[]}
|