cc-claw 0.5.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +240 -193
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -49,7 +49,7 @@ var VERSION;
49
49
  var init_version = __esm({
50
50
  "src/version.ts"() {
51
51
  "use strict";
52
- VERSION = true ? "0.5.0" : (() => {
52
+ VERSION = true ? "0.5.1" : (() => {
53
53
  try {
54
54
  return JSON.parse(readFileSync(join2(process.cwd(), "package.json"), "utf-8")).version ?? "unknown";
55
55
  } catch {
@@ -69,14 +69,14 @@ function ts() {
69
69
  const absM = Math.abs(offset) % 60;
70
70
  return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}${sign}${pad(absH)}${pad(absM)}`;
71
71
  }
72
- function log(...args2) {
73
- console.log(`[${ts()}]`, ...args2);
72
+ function log(...args) {
73
+ console.log(`[${ts()}]`, ...args);
74
74
  }
75
- function warn(...args2) {
76
- console.warn(`[${ts()}]`, ...args2);
75
+ function warn(...args) {
76
+ console.warn(`[${ts()}]`, ...args);
77
77
  }
78
- function error(...args2) {
79
- console.error(`[${ts()}]`, ...args2);
78
+ function error(...args) {
79
+ console.error(`[${ts()}]`, ...args);
80
80
  }
81
81
  function errorMessage(err) {
82
82
  return err instanceof Error ? err.message : String(err);
@@ -2639,7 +2639,7 @@ var init_claude = __esm({
2639
2639
  return { envOverrides: { MAX_THINKING_TOKENS: budgets[level] ?? "16384" } };
2640
2640
  }
2641
2641
  buildSpawnConfig(opts) {
2642
- const args2 = [
2642
+ const args = [
2643
2643
  "-p",
2644
2644
  opts.prompt,
2645
2645
  "--output-format",
@@ -2648,32 +2648,32 @@ var init_claude = __esm({
2648
2648
  "--max-turns",
2649
2649
  String(opts.maxTurns ?? 25)
2650
2650
  ];
2651
- if (opts.sessionId) args2.push("--resume", opts.sessionId);
2652
- if (opts.model) args2.push("--model", opts.model);
2651
+ if (opts.sessionId) args.push("--resume", opts.sessionId);
2652
+ if (opts.model) args.push("--model", opts.model);
2653
2653
  switch (opts.permMode) {
2654
2654
  case "plan":
2655
- args2.push("--permission-mode", "plan");
2656
- args2.push("--strict-mcp-config");
2655
+ args.push("--permission-mode", "plan");
2656
+ args.push("--strict-mcp-config");
2657
2657
  break;
2658
2658
  case "safe":
2659
- args2.push("--permission-mode", "bypassPermissions");
2659
+ args.push("--permission-mode", "bypassPermissions");
2660
2660
  if (opts.allowedTools.length > 0) {
2661
- args2.push("--tools", opts.allowedTools.join(","));
2661
+ args.push("--tools", opts.allowedTools.join(","));
2662
2662
  } else {
2663
- args2.push("--tools", "Read,Glob,Grep");
2663
+ args.push("--tools", "Read,Glob,Grep");
2664
2664
  }
2665
2665
  break;
2666
2666
  case "yolo":
2667
2667
  default:
2668
- args2.push("--permission-mode", "bypassPermissions");
2669
- args2.push("--allow-dangerously-skip-permissions");
2668
+ args.push("--permission-mode", "bypassPermissions");
2669
+ args.push("--allow-dangerously-skip-permissions");
2670
2670
  break;
2671
2671
  }
2672
2672
  const identityFile = join3(IDENTITY_PATH, "CC-CLAW.md");
2673
2673
  if (existsSync(identityFile)) {
2674
- args2.push("--append-system-prompt-file", identityFile);
2674
+ args.push("--append-system-prompt-file", identityFile);
2675
2675
  }
2676
- return { executable: this.getExecutablePath(), args: args2, cwd: opts.cwd };
2676
+ return { executable: this.getExecutablePath(), args, cwd: opts.cwd };
2677
2677
  }
2678
2678
  parseLine(raw) {
2679
2679
  if (typeof raw !== "object" || raw === null) return [];
@@ -2818,7 +2818,7 @@ var init_gemini = __esm({
2818
2818
  return {};
2819
2819
  }
2820
2820
  buildSpawnConfig(opts) {
2821
- const args2 = [
2821
+ const args = [
2822
2822
  "-p",
2823
2823
  opts.prompt,
2824
2824
  "-o",
@@ -2826,21 +2826,21 @@ var init_gemini = __esm({
2826
2826
  "--allowed-mcp-server-names=__none__"
2827
2827
  // nonexistent name = skip all user MCP servers (avoids crash/hang from broken servers)
2828
2828
  ];
2829
- if (opts.sessionId) args2.push("--resume", opts.sessionId);
2830
- if (opts.model) args2.push("-m", opts.model);
2829
+ if (opts.sessionId) args.push("--resume", opts.sessionId);
2830
+ if (opts.model) args.push("-m", opts.model);
2831
2831
  switch (opts.permMode) {
2832
2832
  case "plan":
2833
- args2.push("--approval-mode", "plan");
2833
+ args.push("--approval-mode", "plan");
2834
2834
  break;
2835
2835
  case "safe":
2836
- args2.push("--approval-mode", "plan");
2836
+ args.push("--approval-mode", "plan");
2837
2837
  break;
2838
2838
  case "yolo":
2839
2839
  default:
2840
- args2.push("--yolo");
2840
+ args.push("--yolo");
2841
2841
  break;
2842
2842
  }
2843
- return { executable: this.getExecutablePath(), args: args2, cwd: opts.cwd };
2843
+ return { executable: this.getExecutablePath(), args, cwd: opts.cwd };
2844
2844
  }
2845
2845
  parseLine(raw) {
2846
2846
  if (typeof raw !== "object" || raw === null) return [];
@@ -2975,31 +2975,31 @@ var init_codex = __esm({
2975
2975
  }
2976
2976
  buildSpawnConfig(opts) {
2977
2977
  const isResume = !!opts.sessionId;
2978
- const args2 = [];
2978
+ const args = [];
2979
2979
  if (isResume) {
2980
- args2.push("exec", "resume", opts.sessionId, opts.prompt, "--json");
2980
+ args.push("exec", "resume", opts.sessionId, opts.prompt, "--json");
2981
2981
  } else {
2982
- args2.push("exec", opts.prompt, "--json");
2982
+ args.push("exec", opts.prompt, "--json");
2983
2983
  }
2984
- if (opts.model) args2.push("-m", opts.model);
2984
+ if (opts.model) args.push("-m", opts.model);
2985
2985
  switch (opts.permMode) {
2986
2986
  case "plan":
2987
- args2.push("--sandbox", "read-only");
2987
+ args.push("--sandbox", "read-only");
2988
2988
  break;
2989
2989
  case "safe":
2990
- args2.push("--sandbox", "read-only");
2990
+ args.push("--sandbox", "read-only");
2991
2991
  break;
2992
2992
  case "yolo":
2993
2993
  default:
2994
- args2.push("--dangerously-bypass-approvals-and-sandbox");
2994
+ args.push("--dangerously-bypass-approvals-and-sandbox");
2995
2995
  break;
2996
2996
  }
2997
2997
  if (!isResume && opts.cwd) {
2998
- args2.push("-C", opts.cwd);
2998
+ args.push("-C", opts.cwd);
2999
2999
  }
3000
3000
  return {
3001
3001
  executable: this.getExecutablePath(),
3002
- args: args2,
3002
+ args,
3003
3003
  // For resume, pass cwd via spawn option since -C isn't supported
3004
3004
  cwd: isResume ? opts.cwd : void 0
3005
3005
  };
@@ -4130,8 +4130,8 @@ function spawnAgentProcess(runner, opts, callbacks) {
4130
4130
  permMode: opts.permMode,
4131
4131
  allowedTools: opts.allowedTools
4132
4132
  });
4133
- const args2 = opts.extraArgs?.length ? [...baseArgs, ...opts.extraArgs] : baseArgs;
4134
- const child = spawn2(runner.getExecutablePath(), args2, {
4133
+ const args = opts.extraArgs?.length ? [...baseArgs, ...opts.extraArgs] : baseArgs;
4134
+ const child = spawn2(runner.getExecutablePath(), args, {
4135
4135
  env: buildSpawnEnv(runner, opts.isSubAgent),
4136
4136
  cwd: opts.cwd,
4137
4137
  stdio: ["ignore", "pipe", "pipe"],
@@ -4252,8 +4252,8 @@ async function discoverExistingMcps(runner) {
4252
4252
  try {
4253
4253
  const listCmd = runner.getMcpListCommand();
4254
4254
  const exe = runner.getExecutablePath();
4255
- const args2 = listCmd.slice(1);
4256
- const result = await execFileAsync(exe, args2, {
4255
+ const args = listCmd.slice(1);
4256
+ const result = await execFileAsync(exe, args, {
4257
4257
  encoding: "utf-8",
4258
4258
  env: runner.getEnv(),
4259
4259
  cwd: homedir3(),
@@ -4301,8 +4301,8 @@ async function injectMcps(runner, mcpNames, db3, scope) {
4301
4301
  try {
4302
4302
  const config2 = defToConfig(def);
4303
4303
  const addCmd = runner.getMcpAddCommand(config2);
4304
- const args2 = addCmd.slice(1);
4305
- await execFileAsync(exe, args2, { encoding: "utf-8" });
4304
+ const args = addCmd.slice(1);
4305
+ await execFileAsync(exe, args, { encoding: "utf-8" });
4306
4306
  addPropagation(db3, name, runnerId, scope);
4307
4307
  added.push(name);
4308
4308
  } catch {
@@ -4316,8 +4316,8 @@ async function cleanupMcps(runner, mcps2, db3, scope) {
4316
4316
  for (const name of mcps2) {
4317
4317
  try {
4318
4318
  const removeCmd = runner.getMcpRemoveCommand(name);
4319
- const args2 = removeCmd.slice(1);
4320
- await execFileAsync(exe, args2, { encoding: "utf-8" });
4319
+ const args = removeCmd.slice(1);
4320
+ await execFileAsync(exe, args, { encoding: "utf-8" });
4321
4321
  } catch {
4322
4322
  }
4323
4323
  removePropagation(db3, name, runnerId, scope);
@@ -4673,8 +4673,8 @@ async function startAgent(agentId, chatId, opts) {
4673
4673
  const addCmd = runner.getMcpAddCommand(mcpConfig);
4674
4674
  const { execFileSync: execFileSync5 } = await import("child_process");
4675
4675
  const exe = addCmd[0];
4676
- const args2 = addCmd.slice(1);
4677
- execFileSync5(exe, args2, { encoding: "utf-8", timeout: 15e3, env: runner.getEnv() });
4676
+ const args = addCmd.slice(1);
4677
+ execFileSync5(exe, args, { encoding: "utf-8", timeout: 15e3, env: runner.getEnv() });
4678
4678
  });
4679
4679
  mcpsAdded = [...mcpsAdded, orchestratorMcpName];
4680
4680
  updateAgentMcpsAdded(db3, agentId, mcpsAdded);
@@ -5177,13 +5177,13 @@ __export(health_exports, {
5177
5177
  stopHealthMonitor: () => stopHealthMonitor
5178
5178
  });
5179
5179
  import { spawn as spawn3 } from "child_process";
5180
- async function checkStdioHealth(command, args2) {
5180
+ async function checkStdioHealth(command, args) {
5181
5181
  return new Promise((resolve) => {
5182
5182
  const timer = setTimeout(() => {
5183
5183
  child.kill("SIGKILL");
5184
5184
  resolve("timeout");
5185
5185
  }, CHECK_TIMEOUT_MS);
5186
- const child = spawn3(command, args2, {
5186
+ const child = spawn3(command, args, {
5187
5187
  stdio: ["pipe", "pipe", "pipe"]
5188
5188
  });
5189
5189
  let responded = false;
@@ -5238,8 +5238,8 @@ async function runHealthChecks(db3) {
5238
5238
  let status = "unhealthy";
5239
5239
  try {
5240
5240
  if (mcp.transport === "stdio" && mcp.command) {
5241
- const args2 = mcp.args ? JSON.parse(mcp.args) : [];
5242
- status = await checkStdioHealth(mcp.command, args2);
5241
+ const args = mcp.args ? JSON.parse(mcp.args) : [];
5242
+ status = await checkStdioHealth(mcp.command, args);
5243
5243
  } else if ((mcp.transport === "sse" || mcp.transport === "streamable-http") && mcp.url) {
5244
5244
  status = await checkHttpHealth(mcp.url);
5245
5245
  } else {
@@ -6480,10 +6480,10 @@ function getMcpConfigPath(chatId) {
6480
6480
  const config2 = generateOrchestratorMcpConfig({ chatId, agentId: "main", token });
6481
6481
  return writeMcpConfigFile(config2);
6482
6482
  }
6483
- function injectMcpConfig(adapterId, args2, mcpConfigPath) {
6483
+ function injectMcpConfig(adapterId, args, mcpConfigPath) {
6484
6484
  const flag = MCP_CONFIG_FLAG[adapterId];
6485
- if (!flag) return args2;
6486
- return [...args2, ...flag, mcpConfigPath];
6485
+ if (!flag) return args;
6486
+ return [...args, ...flag, mcpConfigPath];
6487
6487
  }
6488
6488
  var activeChats, chatLocks, SPAWN_TIMEOUT_MS, MCP_CONFIG_FLAG;
6489
6489
  var init_agent = __esm({
@@ -7187,17 +7187,17 @@ function wrapBackendAdapter(adapter) {
7187
7187
  if (method !== "config-file") return [];
7188
7188
  if (adapter.id === "codex") {
7189
7189
  const safeName = server.name.replace(/[^a-zA-Z0-9_-]/g, "_");
7190
- const args2 = [];
7191
- args2.push("-c", `mcp_servers.${safeName}.command="${server.command}"`);
7190
+ const args = [];
7191
+ args.push("-c", `mcp_servers.${safeName}.command="${server.command}"`);
7192
7192
  if (server.args?.length) {
7193
- args2.push("-c", `mcp_servers.${safeName}.args=${JSON.stringify(server.args)}`);
7193
+ args.push("-c", `mcp_servers.${safeName}.args=${JSON.stringify(server.args)}`);
7194
7194
  }
7195
7195
  if (server.env) {
7196
7196
  for (const [k, v] of Object.entries(server.env)) {
7197
- args2.push("-c", `mcp_servers.${safeName}.env.${k}="${v}"`);
7197
+ args.push("-c", `mcp_servers.${safeName}.env.${k}="${v}"`);
7198
7198
  }
7199
7199
  }
7200
- return args2;
7200
+ return args;
7201
7201
  }
7202
7202
  const configPath = writeMcpConfigFile(server);
7203
7203
  return ["--mcp-config", configPath];
@@ -7316,8 +7316,8 @@ function buildGenericParser(config2) {
7316
7316
  return events;
7317
7317
  };
7318
7318
  }
7319
- function templateReplace(args2, vars) {
7320
- return args2.map((a) => {
7319
+ function templateReplace(args, vars) {
7320
+ return args.map((a) => {
7321
7321
  let result = a;
7322
7322
  for (const [key, val] of Object.entries(vars)) {
7323
7323
  result = result.replace(`{${key}}`, val);
@@ -7343,23 +7343,23 @@ function configToRunner(config2) {
7343
7343
  getExecutablePath: () => exePath,
7344
7344
  getEnv: () => buildBaseEnv(config2.env),
7345
7345
  buildSpawnArgs(opts) {
7346
- const args2 = [];
7347
- if (config2.spawnPattern.extraArgs) args2.push(...config2.spawnPattern.extraArgs);
7348
- if (config2.spawnPattern.outputFormatFlag) args2.push(...config2.spawnPattern.outputFormatFlag);
7349
- args2.push(...config2.spawnPattern.promptFlag, opts.prompt);
7346
+ const args = [];
7347
+ if (config2.spawnPattern.extraArgs) args.push(...config2.spawnPattern.extraArgs);
7348
+ if (config2.spawnPattern.outputFormatFlag) args.push(...config2.spawnPattern.outputFormatFlag);
7349
+ args.push(...config2.spawnPattern.promptFlag, opts.prompt);
7350
7350
  if (opts.model && config2.spawnPattern.modelFlag) {
7351
- args2.push(...config2.spawnPattern.modelFlag, opts.model);
7351
+ args.push(...config2.spawnPattern.modelFlag, opts.model);
7352
7352
  }
7353
7353
  if (opts.cwd && config2.spawnPattern.cwdFlag) {
7354
- args2.push(...config2.spawnPattern.cwdFlag, opts.cwd);
7354
+ args.push(...config2.spawnPattern.cwdFlag, opts.cwd);
7355
7355
  }
7356
7356
  if (opts.sessionId && config2.spawnPattern.sessionResumeFlag) {
7357
- args2.push(...config2.spawnPattern.sessionResumeFlag, opts.sessionId);
7357
+ args.push(...config2.spawnPattern.sessionResumeFlag, opts.sessionId);
7358
7358
  }
7359
7359
  if (opts.permMode && config2.spawnPattern.permModes?.[opts.permMode]) {
7360
- args2.push(...config2.spawnPattern.permModes[opts.permMode]);
7360
+ args.push(...config2.spawnPattern.permModes[opts.permMode]);
7361
7361
  }
7362
- return args2;
7362
+ return args;
7363
7363
  },
7364
7364
  parseLine,
7365
7365
  shouldKillOnResult: () => config2.shouldKillOnResult,
@@ -8642,8 +8642,8 @@ function formatHeartbeatStatus(chatId) {
8642
8642
  }
8643
8643
  return lines.join("\n");
8644
8644
  }
8645
- function parseHeartbeatCommand(chatId, args2) {
8646
- const parts = args2.trim().toLowerCase().split(/\s+/);
8645
+ function parseHeartbeatCommand(chatId, args) {
8646
+ const parts = args.trim().toLowerCase().split(/\s+/);
8647
8647
  return { action: parts[0] ?? "status", value: parts.slice(1).join(" ") };
8648
8648
  }
8649
8649
  var HEARTBEAT_MD_PATH, HEARTBEAT_OK, registry2, activeTimers2;
@@ -10602,13 +10602,19 @@ ${lines.join("\n")}`, "plain");
10602
10602
  }
10603
10603
  case "model_signature": {
10604
10604
  const currentSig = getModelSignature(chatId);
10605
- const newSig = currentSig === "on" ? "off" : "on";
10606
- setModelSignature(chatId, newSig);
10607
- await channel.sendText(
10608
- chatId,
10609
- newSig === "on" ? "\u{1F9E0} Model signature enabled. Each response will show the active model and thinking level." : "Model signature disabled.",
10610
- "plain"
10611
- );
10605
+ if (typeof channel.sendKeyboard === "function") {
10606
+ await channel.sendKeyboard(chatId, `Model Signature: ${currentSig === "on" ? "ON" : "OFF"}
10607
+ Appends model + thinking level to each response.`, [
10608
+ [
10609
+ { label: currentSig === "on" ? "\u2713 On" : "On", data: "model_sig:on" },
10610
+ { label: currentSig !== "on" ? "\u2713 Off" : "Off", data: "model_sig:off" }
10611
+ ]
10612
+ ]);
10613
+ } else {
10614
+ const newSig = currentSig === "on" ? "off" : "on";
10615
+ setModelSignature(chatId, newSig);
10616
+ await channel.sendText(chatId, newSig === "on" ? "Model signature enabled." : "Model signature disabled.", "plain");
10617
+ }
10612
10618
  break;
10613
10619
  }
10614
10620
  case "imagine":
@@ -11251,7 +11257,7 @@ Use /skills to see it.`, "plain");
11251
11257
  break;
11252
11258
  }
11253
11259
  case "intent": {
11254
- const testMsg = args.join(" ") || "hey";
11260
+ const testMsg = commandArgs?.trim() || "hey";
11255
11261
  const result = classifyIntent(testMsg, chatId);
11256
11262
  await channel.sendText(chatId, `Intent: ${result}
11257
11263
  Message: "${testMsg}"`, "plain");
@@ -12086,6 +12092,14 @@ ${PERM_MODES[chosen]}`,
12086
12092
  }
12087
12093
  await channel.sendText(chatId, `Response style set to: ${selectedStyle}`, "plain");
12088
12094
  }
12095
+ } else if (data.startsWith("model_sig:")) {
12096
+ const value = data.slice(10);
12097
+ setModelSignature(chatId, value);
12098
+ await channel.sendText(
12099
+ chatId,
12100
+ value === "on" ? "\u{1F9E0} Model signature enabled. Each response will show the active model and thinking level." : "Model signature disabled.",
12101
+ "plain"
12102
+ );
12089
12103
  } else if (data.startsWith("vcfg:")) {
12090
12104
  const parts = data.slice(5).split(":");
12091
12105
  const action = parts[0];
@@ -13312,6 +13326,106 @@ var init_index = __esm({
13312
13326
  }
13313
13327
  });
13314
13328
 
13329
+ // src/cli/api-client.ts
13330
+ var api_client_exports = {};
13331
+ __export(api_client_exports, {
13332
+ apiGet: () => apiGet,
13333
+ apiPost: () => apiPost,
13334
+ isDaemonRunning: () => isDaemonRunning
13335
+ });
13336
+ import { readFileSync as readFileSync11, existsSync as existsSync17 } from "fs";
13337
+ import { request as httpRequest } from "http";
13338
+ function getToken() {
13339
+ if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
13340
+ try {
13341
+ if (existsSync17(TOKEN_PATH)) return readFileSync11(TOKEN_PATH, "utf-8").trim();
13342
+ } catch {
13343
+ }
13344
+ return null;
13345
+ }
13346
+ async function isDaemonRunning() {
13347
+ try {
13348
+ const res = await apiGet("/api/health");
13349
+ return res.ok;
13350
+ } catch {
13351
+ return false;
13352
+ }
13353
+ }
13354
+ async function apiGet(path) {
13355
+ const token = getToken();
13356
+ return new Promise((resolve, reject) => {
13357
+ const url = new URL(path, BASE_URL);
13358
+ const req = httpRequest(url, {
13359
+ method: "GET",
13360
+ headers: token ? { "Authorization": `Bearer ${token}` } : {},
13361
+ timeout: 3e3
13362
+ }, (res) => {
13363
+ const chunks = [];
13364
+ res.on("data", (c) => chunks.push(c));
13365
+ res.on("end", () => {
13366
+ const body = Buffer.concat(chunks).toString();
13367
+ try {
13368
+ const data = JSON.parse(body);
13369
+ resolve({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
13370
+ } catch {
13371
+ resolve({ ok: false, status: res.statusCode ?? 0, data: body });
13372
+ }
13373
+ });
13374
+ });
13375
+ req.on("error", reject);
13376
+ req.on("timeout", () => {
13377
+ req.destroy();
13378
+ reject(new Error("Request timed out"));
13379
+ });
13380
+ req.end();
13381
+ });
13382
+ }
13383
+ async function apiPost(path, body) {
13384
+ const token = getToken();
13385
+ const payload = JSON.stringify(body);
13386
+ return new Promise((resolve, reject) => {
13387
+ const url = new URL(path, BASE_URL);
13388
+ const req = httpRequest(url, {
13389
+ method: "POST",
13390
+ headers: {
13391
+ "Content-Type": "application/json",
13392
+ "Content-Length": Buffer.byteLength(payload).toString(),
13393
+ ...token ? { "Authorization": `Bearer ${token}` } : {}
13394
+ },
13395
+ timeout: 3e4
13396
+ }, (res) => {
13397
+ const chunks = [];
13398
+ res.on("data", (c) => chunks.push(c));
13399
+ res.on("end", () => {
13400
+ const responseBody = Buffer.concat(chunks).toString();
13401
+ try {
13402
+ const data = JSON.parse(responseBody);
13403
+ resolve({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
13404
+ } catch {
13405
+ resolve({ ok: false, status: res.statusCode ?? 0, data: responseBody });
13406
+ }
13407
+ });
13408
+ });
13409
+ req.on("error", reject);
13410
+ req.on("timeout", () => {
13411
+ req.destroy();
13412
+ reject(new Error("Request timed out"));
13413
+ });
13414
+ req.write(payload);
13415
+ req.end();
13416
+ });
13417
+ }
13418
+ var TOKEN_PATH, DEFAULT_PORT, BASE_URL;
13419
+ var init_api_client = __esm({
13420
+ "src/cli/api-client.ts"() {
13421
+ "use strict";
13422
+ init_paths();
13423
+ TOKEN_PATH = `${DATA_PATH}/api-token`;
13424
+ DEFAULT_PORT = parseInt(process.env.DASHBOARD_PORT ?? "3141", 10);
13425
+ BASE_URL = `http://127.0.0.1:${DEFAULT_PORT}`;
13426
+ }
13427
+ });
13428
+
13315
13429
  // src/service.ts
13316
13430
  var service_exports = {};
13317
13431
  __export(service_exports, {
@@ -13319,7 +13433,7 @@ __export(service_exports, {
13319
13433
  serviceStatus: () => serviceStatus,
13320
13434
  uninstallService: () => uninstallService
13321
13435
  });
13322
- import { existsSync as existsSync17, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
13436
+ import { existsSync as existsSync18, mkdirSync as mkdirSync8, writeFileSync as writeFileSync6, unlinkSync as unlinkSync3 } from "fs";
13323
13437
  import { execFileSync as execFileSync2, execSync as execSync5 } from "child_process";
13324
13438
  import { homedir as homedir6, platform } from "os";
13325
13439
  import { join as join19, dirname as dirname3 } from "path";
@@ -13397,9 +13511,9 @@ function generatePlist() {
13397
13511
  }
13398
13512
  function installMacOS() {
13399
13513
  const agentsDir = dirname3(PLIST_PATH);
13400
- if (!existsSync17(agentsDir)) mkdirSync8(agentsDir, { recursive: true });
13401
- if (!existsSync17(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
13402
- if (existsSync17(PLIST_PATH)) {
13514
+ if (!existsSync18(agentsDir)) mkdirSync8(agentsDir, { recursive: true });
13515
+ if (!existsSync18(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
13516
+ if (existsSync18(PLIST_PATH)) {
13403
13517
  try {
13404
13518
  execFileSync2("launchctl", ["unload", PLIST_PATH]);
13405
13519
  } catch {
@@ -13411,7 +13525,7 @@ function installMacOS() {
13411
13525
  console.log(" Service loaded and starting.");
13412
13526
  }
13413
13527
  function uninstallMacOS() {
13414
- if (!existsSync17(PLIST_PATH)) {
13528
+ if (!existsSync18(PLIST_PATH)) {
13415
13529
  console.log(" No service found to uninstall.");
13416
13530
  return;
13417
13531
  }
@@ -13422,6 +13536,24 @@ function uninstallMacOS() {
13422
13536
  unlinkSync3(PLIST_PATH);
13423
13537
  console.log(" Service uninstalled.");
13424
13538
  }
13539
+ function formatUptime(seconds) {
13540
+ seconds = Math.floor(seconds);
13541
+ if (seconds < 60) return `${seconds}s`;
13542
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
13543
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
13544
+ return `${Math.floor(seconds / 86400)}d ${Math.floor(seconds % 86400 / 3600)}h`;
13545
+ }
13546
+ async function getUptimeFromDaemon() {
13547
+ try {
13548
+ const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
13549
+ if (!await isDaemonRunning2()) return null;
13550
+ const res = await apiGet2("/api/health");
13551
+ const sec = res.data?.uptime;
13552
+ return sec ? formatUptime(sec) : null;
13553
+ } catch {
13554
+ return null;
13555
+ }
13556
+ }
13425
13557
  function statusMacOS() {
13426
13558
  try {
13427
13559
  const out = execSync5("launchctl list | grep cc-claw", { shell: "/bin/sh", encoding: "utf-8" }).trim();
@@ -13430,7 +13562,12 @@ function statusMacOS() {
13430
13562
  const pid = parts[0];
13431
13563
  const exitCode = parts[1];
13432
13564
  if (pid !== "-") {
13433
- console.log(` Running (PID ${pid})`);
13565
+ getUptimeFromDaemon().then((uptime) => {
13566
+ const uptimeStr = uptime ? `, uptime ${uptime}` : "";
13567
+ console.log(` Running (PID ${pid}${uptimeStr})`);
13568
+ }).catch(() => {
13569
+ console.log(` Running (PID ${pid})`);
13570
+ });
13434
13571
  } else {
13435
13572
  console.log(` Not running (last exit code: ${exitCode})`);
13436
13573
  }
@@ -13462,8 +13599,8 @@ WantedBy=default.target
13462
13599
  `;
13463
13600
  }
13464
13601
  function installLinux() {
13465
- if (!existsSync17(SYSTEMD_DIR)) mkdirSync8(SYSTEMD_DIR, { recursive: true });
13466
- if (!existsSync17(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
13602
+ if (!existsSync18(SYSTEMD_DIR)) mkdirSync8(SYSTEMD_DIR, { recursive: true });
13603
+ if (!existsSync18(LOGS_PATH)) mkdirSync8(LOGS_PATH, { recursive: true });
13467
13604
  writeFileSync6(UNIT_PATH, generateUnit());
13468
13605
  console.log(` Installed: ${UNIT_PATH}`);
13469
13606
  execFileSync2("systemctl", ["--user", "daemon-reload"]);
@@ -13472,7 +13609,7 @@ function installLinux() {
13472
13609
  console.log(" Service enabled and started.");
13473
13610
  }
13474
13611
  function uninstallLinux() {
13475
- if (!existsSync17(UNIT_PATH)) {
13612
+ if (!existsSync18(UNIT_PATH)) {
13476
13613
  console.log(" No service found to uninstall.");
13477
13614
  return;
13478
13615
  }
@@ -13497,7 +13634,7 @@ function statusLinux() {
13497
13634
  }
13498
13635
  }
13499
13636
  function installService() {
13500
- if (!existsSync17(join19(CC_CLAW_HOME, ".env"))) {
13637
+ if (!existsSync18(join19(CC_CLAW_HOME, ".env"))) {
13501
13638
  console.error(` Config not found at ${CC_CLAW_HOME}/.env`);
13502
13639
  console.error(" Run 'cc-claw setup' before installing the service.");
13503
13640
  process.exitCode = 1;
@@ -13695,13 +13832,13 @@ var init_daemon = __esm({
13695
13832
  });
13696
13833
 
13697
13834
  // src/cli/resolve-chat.ts
13698
- import { readFileSync as readFileSync12 } from "fs";
13835
+ import { readFileSync as readFileSync13 } from "fs";
13699
13836
  function resolveChatId(globalOpts) {
13700
13837
  const explicit = globalOpts.chat;
13701
13838
  if (explicit) return explicit;
13702
13839
  if (_cachedDefault) return _cachedDefault;
13703
13840
  try {
13704
- const content = readFileSync12(ENV_PATH, "utf-8");
13841
+ const content = readFileSync13(ENV_PATH, "utf-8");
13705
13842
  const match = content.match(/^ALLOWED_CHAT_ID=(.+)$/m);
13706
13843
  if (match) {
13707
13844
  _cachedDefault = match[1].split(",")[0].trim();
@@ -13720,106 +13857,6 @@ var init_resolve_chat = __esm({
13720
13857
  }
13721
13858
  });
13722
13859
 
13723
- // src/cli/api-client.ts
13724
- var api_client_exports = {};
13725
- __export(api_client_exports, {
13726
- apiGet: () => apiGet,
13727
- apiPost: () => apiPost,
13728
- isDaemonRunning: () => isDaemonRunning
13729
- });
13730
- import { readFileSync as readFileSync13, existsSync as existsSync18 } from "fs";
13731
- import { request as httpRequest } from "http";
13732
- function getToken() {
13733
- if (process.env.CC_CLAW_API_TOKEN) return process.env.CC_CLAW_API_TOKEN;
13734
- try {
13735
- if (existsSync18(TOKEN_PATH)) return readFileSync13(TOKEN_PATH, "utf-8").trim();
13736
- } catch {
13737
- }
13738
- return null;
13739
- }
13740
- async function isDaemonRunning() {
13741
- try {
13742
- const res = await apiGet("/api/health");
13743
- return res.ok;
13744
- } catch {
13745
- return false;
13746
- }
13747
- }
13748
- async function apiGet(path) {
13749
- const token = getToken();
13750
- return new Promise((resolve, reject) => {
13751
- const url = new URL(path, BASE_URL);
13752
- const req = httpRequest(url, {
13753
- method: "GET",
13754
- headers: token ? { "Authorization": `Bearer ${token}` } : {},
13755
- timeout: 3e3
13756
- }, (res) => {
13757
- const chunks = [];
13758
- res.on("data", (c) => chunks.push(c));
13759
- res.on("end", () => {
13760
- const body = Buffer.concat(chunks).toString();
13761
- try {
13762
- const data = JSON.parse(body);
13763
- resolve({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
13764
- } catch {
13765
- resolve({ ok: false, status: res.statusCode ?? 0, data: body });
13766
- }
13767
- });
13768
- });
13769
- req.on("error", reject);
13770
- req.on("timeout", () => {
13771
- req.destroy();
13772
- reject(new Error("Request timed out"));
13773
- });
13774
- req.end();
13775
- });
13776
- }
13777
- async function apiPost(path, body) {
13778
- const token = getToken();
13779
- const payload = JSON.stringify(body);
13780
- return new Promise((resolve, reject) => {
13781
- const url = new URL(path, BASE_URL);
13782
- const req = httpRequest(url, {
13783
- method: "POST",
13784
- headers: {
13785
- "Content-Type": "application/json",
13786
- "Content-Length": Buffer.byteLength(payload).toString(),
13787
- ...token ? { "Authorization": `Bearer ${token}` } : {}
13788
- },
13789
- timeout: 3e4
13790
- }, (res) => {
13791
- const chunks = [];
13792
- res.on("data", (c) => chunks.push(c));
13793
- res.on("end", () => {
13794
- const responseBody = Buffer.concat(chunks).toString();
13795
- try {
13796
- const data = JSON.parse(responseBody);
13797
- resolve({ ok: res.statusCode === 200, status: res.statusCode ?? 0, data });
13798
- } catch {
13799
- resolve({ ok: false, status: res.statusCode ?? 0, data: responseBody });
13800
- }
13801
- });
13802
- });
13803
- req.on("error", reject);
13804
- req.on("timeout", () => {
13805
- req.destroy();
13806
- reject(new Error("Request timed out"));
13807
- });
13808
- req.write(payload);
13809
- req.end();
13810
- });
13811
- }
13812
- var TOKEN_PATH, DEFAULT_PORT, BASE_URL;
13813
- var init_api_client = __esm({
13814
- "src/cli/api-client.ts"() {
13815
- "use strict";
13816
- init_paths();
13817
- TOKEN_PATH = `${DATA_PATH}/api-token`;
13818
- DEFAULT_PORT = parseInt(process.env.DASHBOARD_PORT ?? "3141", 10);
13819
- BASE_URL = `http://127.0.0.1:${DEFAULT_PORT}`;
13820
- }
13821
- });
13822
-
13823
13860
  // src/cli/commands/status.ts
13824
13861
  var status_exports = {};
13825
13862
  __export(status_exports, {
@@ -13917,7 +13954,7 @@ async function statusCommand(globalOpts, localOpts) {
13917
13954
  kvLine("Voice", s.voice ? success("on") : muted("off"))
13918
13955
  ];
13919
13956
  if (localOpts.deep) {
13920
- lines.push(kvLine("Daemon", s.daemon.running ? success(`running${s.daemon.uptime_seconds ? ` (uptime ${formatUptime(s.daemon.uptime_seconds)})` : ""}`) : error2("offline")));
13957
+ lines.push(kvLine("Daemon", s.daemon.running ? success(`running${s.daemon.uptime_seconds ? ` (uptime ${formatUptime2(s.daemon.uptime_seconds)})` : ""}`) : error2("offline")));
13921
13958
  }
13922
13959
  lines.push(
13923
13960
  "",
@@ -13935,7 +13972,7 @@ async function statusCommand(globalOpts, localOpts) {
13935
13972
  process.exit(1);
13936
13973
  }
13937
13974
  }
13938
- function formatUptime(seconds) {
13975
+ function formatUptime2(seconds) {
13939
13976
  if (seconds < 60) return `${seconds}s`;
13940
13977
  if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
13941
13978
  if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
@@ -14007,10 +14044,12 @@ async function doctorCommand(globalOpts, localOpts) {
14007
14044
  const { isDaemonRunning: isDaemonRunning2, apiGet: apiGet2 } = await Promise.resolve().then(() => (init_api_client(), api_client_exports));
14008
14045
  const running = await isDaemonRunning2();
14009
14046
  if (running) {
14010
- checks.push({ name: "Daemon", status: "ok", message: "running" });
14011
14047
  try {
14012
14048
  const health = await apiGet2("/api/health");
14013
14049
  const daemonVersion = health.data?.version;
14050
+ const uptimeSec = health.data?.uptime;
14051
+ const uptimeStr = uptimeSec ? ` (uptime ${formatUptime3(uptimeSec)})` : "";
14052
+ checks.push({ name: "Daemon", status: "ok", message: `running${uptimeStr}` });
14014
14053
  if (daemonVersion && daemonVersion !== VERSION) {
14015
14054
  checks.push({
14016
14055
  name: "Version",
@@ -14022,6 +14061,7 @@ async function doctorCommand(globalOpts, localOpts) {
14022
14061
  checks.push({ name: "Version", status: "ok", message: `v${VERSION}` });
14023
14062
  }
14024
14063
  } catch {
14064
+ checks.push({ name: "Daemon", status: "ok", message: "running" });
14025
14065
  }
14026
14066
  } else {
14027
14067
  checks.push({ name: "Daemon", status: "warning", message: "not running", fix: "cc-claw service start" });
@@ -14160,6 +14200,13 @@ async function doctorCommand(globalOpts, localOpts) {
14160
14200
  });
14161
14201
  process.exit(errors > 0 ? 1 : warnings > 0 ? 2 : 0);
14162
14202
  }
14203
+ function formatUptime3(seconds) {
14204
+ seconds = Math.floor(seconds);
14205
+ if (seconds < 60) return `${seconds}s`;
14206
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
14207
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m`;
14208
+ return `${Math.floor(seconds / 86400)}d ${Math.floor(seconds % 86400 / 3600)}h`;
14209
+ }
14163
14210
  var init_doctor = __esm({
14164
14211
  "src/cli/commands/doctor.ts"() {
14165
14212
  "use strict";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-claw",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "CC-Claw: Personal AI assistant on Telegram — multi-backend (Claude, Gemini, Codex), sub-agent orchestration, MCP management",
5
5
  "type": "module",
6
6
  "main": "dist/cli.js",