ccem 2.24.0 → 2.26.0

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/index.js +168 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1466,7 +1466,7 @@ function startCliClaudeProvenanceTracking(options) {
1466
1466
  if (!workingDir) {
1467
1467
  return null;
1468
1468
  }
1469
- const ccemSessionId = `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1469
+ const ccemSessionId = normalizeText(options.ccemSessionId) ?? `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1470
1470
  try {
1471
1471
  registerLaunch({
1472
1472
  ccemSessionId,
@@ -1826,6 +1826,7 @@ function ensureSessionsDir() {
1826
1826
  }
1827
1827
  async function launchClaude(options) {
1828
1828
  const { envName, envConfig, permMode, workingDir, sessionId, resumeSessionId, silent } = options;
1829
+ const ccemRuntimeId = sessionId ?? `cli-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
1829
1830
  const env = { ...process.env };
1830
1831
  for (const key of MANAGED_CLAUDE_ENV_KEYS) {
1831
1832
  delete env[key];
@@ -1834,6 +1835,8 @@ async function launchClaude(options) {
1834
1835
  Object.assign(env, buildEnvVars(envConfig));
1835
1836
  }
1836
1837
  delete env.CLAUDECODE;
1838
+ env.CCEM_RUNTIME_ID = ccemRuntimeId;
1839
+ env.CCEM_SESSION_ID = ccemRuntimeId;
1837
1840
  const args = [];
1838
1841
  if (permMode) {
1839
1842
  const preset = PERMISSION_PRESETS[permMode];
@@ -1870,6 +1873,7 @@ async function launchClaude(options) {
1870
1873
  return;
1871
1874
  }
1872
1875
  provenanceTracking = startCliClaudeProvenanceTracking({
1876
+ ccemSessionId: ccemRuntimeId,
1873
1877
  envName: envName ?? "unknown",
1874
1878
  workingDir: effectiveWorkingDir,
1875
1879
  permMode,
@@ -2769,6 +2773,66 @@ Loaded ${results.length} environment(s) from remote:`));
2769
2773
  return results;
2770
2774
  };
2771
2775
 
2776
+ // src/bot-bind-skill.ts
2777
+ var CCEM_BOT_BIND_SKILL_CONTENT = `# ccem-bot-bind
2778
+
2779
+ Bind the current CCEM-launched Claude Code session to a bot target such as WeCom or Weixin.
2780
+
2781
+ Use this skill when the user asks to bind, attach, connect, or push the current task/session to a bot, chat, WeCom, Weixin, Telegram, or a remote task card.
2782
+
2783
+ ## What this skill does
2784
+
2785
+ - Creates a bot-binding request for the current CCEM runtime.
2786
+ - Relies on \`CCEM_RUNTIME_ID\` or \`CCEM_SESSION_ID\`, which CCEM injects into sessions it launches.
2787
+ - Writes the request to \`~/.ccem/bot-bind-requests.jsonl\`.
2788
+ - CCEM Desktop consumes the request and binds the matching session to the bot outbox.
2789
+
2790
+ ## Required inputs
2791
+
2792
+ Ask for any missing target information:
2793
+
2794
+ - \`platform\`: one of \`wecom\`, \`weixin\`, or \`telegram\`.
2795
+ - \`peer_id\`: user id, chat id, group id, or equivalent target id.
2796
+ - For WeCom, include \`bot_id\` when known.
2797
+
2798
+ ## Command
2799
+
2800
+ Run:
2801
+
2802
+ \`\`\`bash
2803
+ ccem bot-bind --platform <platform> --peer-id <peer_id> --title "<short task title>" --summary "<one sentence summary>"
2804
+ \`\`\`
2805
+
2806
+ For WeCom:
2807
+
2808
+ \`\`\`bash
2809
+ ccem bot-bind --platform wecom --bot-id <bot_id> --peer-id <userid_or_chatid> --title "<short task title>" --summary "<one sentence summary>"
2810
+ \`\`\`
2811
+
2812
+ If the user only wants to create the binding without pushing a task card, add \`--no-send-card\`.
2813
+
2814
+ If CCEM did not inject a runtime id, ask the user for the runtime id or list sessions with:
2815
+
2816
+ \`\`\`bash
2817
+ ccem sessions
2818
+ \`\`\`
2819
+
2820
+ Then pass:
2821
+
2822
+ \`\`\`bash
2823
+ ccem bot-bind --runtime-id <runtime_id> --platform <platform> --peer-id <peer_id>
2824
+ \`\`\`
2825
+
2826
+ ## Behavior rules
2827
+
2828
+ - Do not invent peer ids or bot ids.
2829
+ - Keep the title short enough for a task card.
2830
+ - Include a summary that explains what this session is currently doing.
2831
+ - After the command succeeds, tell the user the request was queued and CCEM Desktop will consume it when running.
2832
+ - Do not ask the bot target to execute shell commands; user commands should flow back through the CCEM binding.
2833
+ - For WeCom, authorization and target membership are owned by the configured WeCom bot; do not create a separate allowlist.
2834
+ `;
2835
+
2772
2836
  // src/cron-skill.ts
2773
2837
  var CCEM_CRON_SKILL_CONTENT = `# ccem-cron
2774
2838
 
@@ -3308,6 +3372,7 @@ var switchEnvironment = async (name) => {
3308
3372
  };
3309
3373
  var getSessionsFilePath = () => path8.join(getCcemConfigDir(), "sessions.json");
3310
3374
  var getRuntimeStateFilePath = () => path8.join(getCcemConfigDir(), "runtime-state.json");
3375
+ var getBotBindRequestFilePath = () => path8.join(getCcemConfigDir(), "bot-bind-requests.jsonl");
3311
3376
  var parseJsonFile = (filePath) => {
3312
3377
  if (!fs10.existsSync(filePath)) {
3313
3378
  return null;
@@ -3387,6 +3452,29 @@ var attachTmuxTarget = (target) => {
3387
3452
  });
3388
3453
  });
3389
3454
  };
3455
+ var normalizeBotBindPlatform = (value) => {
3456
+ const normalized = value.trim().toLowerCase();
3457
+ return normalized === "telegram" || normalized === "weixin" || normalized === "wecom" ? normalized : null;
3458
+ };
3459
+ var resolveBotBindRuntimeId = (explicitRuntimeId) => {
3460
+ const explicit = explicitRuntimeId?.trim();
3461
+ if (explicit) {
3462
+ return explicit;
3463
+ }
3464
+ const fromEnv = process.env.CCEM_RUNTIME_ID?.trim() || process.env.CCEM_SESSION_ID?.trim();
3465
+ if (fromEnv) {
3466
+ return fromEnv;
3467
+ }
3468
+ const sessions = readInteractiveAttachSessions();
3469
+ return sessions.length === 1 ? sessions[0].id : null;
3470
+ };
3471
+ var appendBotBindRequest = (payload) => {
3472
+ const requestPath = getBotBindRequestFilePath();
3473
+ fs10.mkdirSync(path8.dirname(requestPath), { recursive: true });
3474
+ fs10.appendFileSync(requestPath, `${JSON.stringify(payload)}
3475
+ `, "utf-8");
3476
+ return requestPath;
3477
+ };
3390
3478
  program.command("ls").description("List all configured environments").action(() => {
3391
3479
  const registries = getRegistries();
3392
3480
  const current = config2.get("current");
@@ -3442,6 +3530,57 @@ program.command("attach [id]").description("Attach to a tmux-backed interactive
3442
3530
  process.exit(1);
3443
3531
  }
3444
3532
  });
3533
+ program.command("bot-bind").description("Request binding the current ccem session to a chat bot target").requiredOption("--platform <platform>", "target platform: telegram, weixin, or wecom").option("--peer-id <peerId>", "target user/chat id").option("--peer <peerId>", "alias for --peer-id").option("--runtime-id <runtimeId>", "ccem runtime id; defaults to CCEM_RUNTIME_ID").option("--bot-id <botId>", "WeCom bot id or platform bot id").option("--thread-id <threadId>", "thread/topic id for platforms that support it").option("--title <title>", "task card title").option("--summary <summary>", "task card summary").option("--no-send-card", "bind without sending the initial task card").option("--json", "print the queued request as JSON").action((options) => {
3534
+ const platform = normalizeBotBindPlatform(options.platform);
3535
+ if (!platform) {
3536
+ console.error(chalk7.red("\u2717 --platform must be telegram, weixin, or wecom"));
3537
+ process.exitCode = 1;
3538
+ return;
3539
+ }
3540
+ const peerId = (options.peerId ?? options.peer ?? "").trim();
3541
+ if (!peerId) {
3542
+ console.error(chalk7.red("\u2717 --peer-id is required"));
3543
+ process.exitCode = 1;
3544
+ return;
3545
+ }
3546
+ const runtimeId = resolveBotBindRuntimeId(options.runtimeId);
3547
+ if (!runtimeId) {
3548
+ console.error(chalk7.red("\u2717 Unable to resolve ccem runtime id"));
3549
+ console.error(chalk7.gray(" Run from a ccem-launched session or pass --runtime-id explicitly."));
3550
+ process.exitCode = 1;
3551
+ return;
3552
+ }
3553
+ const botId = options.botId?.trim() || void 0;
3554
+ if (platform === "wecom" && !botId) {
3555
+ console.error(chalk7.red("\u2717 --bot-id is required for WeCom bot bindings"));
3556
+ process.exitCode = 1;
3557
+ return;
3558
+ }
3559
+ const payload = {
3560
+ request_id: `botbind-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
3561
+ runtime_id: runtimeId,
3562
+ platform,
3563
+ peer_id: peerId,
3564
+ bot_id: botId,
3565
+ thread_id: options.threadId?.trim() || void 0,
3566
+ task_title: options.title?.trim() || void 0,
3567
+ task_summary: options.summary?.trim() || void 0,
3568
+ send_task_card: options.sendCard !== false,
3569
+ created_at: (/* @__PURE__ */ new Date()).toISOString()
3570
+ };
3571
+ const requestPath = appendBotBindRequest(payload);
3572
+ if (options.json) {
3573
+ console.log(JSON.stringify({ requestPath, payload }, null, 2));
3574
+ return;
3575
+ }
3576
+ console.log(chalk7.green("\u2713 \u5DF2\u63D0\u4EA4 bot \u7ED1\u5B9A\u8BF7\u6C42"));
3577
+ console.log(chalk7.gray(` runtime: ${runtimeId}`));
3578
+ console.log(chalk7.gray(` target: ${platform}/${peerId}`));
3579
+ console.log(chalk7.gray(` queue: ${requestPath}`));
3580
+ console.log(chalk7.cyan(
3581
+ options.sendCard === false ? " \u5982\u679C CCEM Desktop \u6B63\u5728\u8FD0\u884C\uFF0C\u4F1A\u81EA\u52A8\u6D88\u8D39\u8BE5\u8BF7\u6C42\u5E76\u5EFA\u7ACB\u7ED1\u5B9A\u3002" : " \u5982\u679C CCEM Desktop \u6B63\u5728\u8FD0\u884C\uFF0C\u4F1A\u81EA\u52A8\u6D88\u8D39\u8BE5\u8BF7\u6C42\u5E76\u53D1\u9001\u4EFB\u52A1\u5361\u7247\u5230\u7ED1\u5B9A\u76EE\u6807\u3002"
3582
+ ));
3583
+ });
3445
3584
  program.command("add <name>").description("Add a new environment configuration").action(async (name) => {
3446
3585
  const registries = getRegistries();
3447
3586
  if (registries[name]) {
@@ -3760,6 +3899,34 @@ setupCmd.command("cron").description("\u5B89\u88C5 ccem-cron skill \u5230 Claude
3760
3899
  console.log(chalk7.cyan(`
3761
3900
  \u5728 Claude Code \u4E2D\u4F7F\u7528 /ccem-cron \u5373\u53EF\u8C03\u7528\u6B64 skill`));
3762
3901
  });
3902
+ setupCmd.command("bot-bind").description("\u5B89\u88C5 ccem-bot-bind skill \u5230 Claude Code\uFF08~/.claude/skills/\uFF09").option("--force", "\u5F3A\u5236\u8986\u76D6\u5DF2\u6709\u6587\u4EF6").action(async function() {
3903
+ const options = this.opts();
3904
+ const skillDir = path8.join(getHomeDir(), ".claude", "skills");
3905
+ const targetPath = path8.join(skillDir, "ccem-bot-bind.md");
3906
+ if (!fs10.existsSync(skillDir)) {
3907
+ fs10.mkdirSync(skillDir, { recursive: true });
3908
+ console.log(chalk7.gray(`\u521B\u5EFA\u76EE\u5F55: ${skillDir}`));
3909
+ }
3910
+ if (fs10.existsSync(targetPath) && !options.force) {
3911
+ const { overwrite } = await inquirer.prompt([
3912
+ {
3913
+ type: "confirm",
3914
+ name: "overwrite",
3915
+ message: `${targetPath} \u5DF2\u5B58\u5728\uFF0C\u662F\u5426\u8986\u76D6\uFF1F`,
3916
+ default: false
3917
+ }
3918
+ ]);
3919
+ if (!overwrite) {
3920
+ console.log(chalk7.yellow("\u5DF2\u53D6\u6D88"));
3921
+ return;
3922
+ }
3923
+ }
3924
+ fs10.writeFileSync(targetPath, CCEM_BOT_BIND_SKILL_CONTENT, "utf-8");
3925
+ console.log(chalk7.green(`\u2713 \u5DF2\u5B89\u88C5 ccem-bot-bind skill`));
3926
+ console.log(chalk7.gray(` \u8DEF\u5F84: ${targetPath}`));
3927
+ console.log(chalk7.cyan(`
3928
+ \u5728 Claude Code \u4E2D\u8BA9\u5B83\u4F7F\u7528 ccem-bot-bind skill \u5373\u53EF\u7ED1\u5B9A\u5F53\u524D\u4F1A\u8BDD\u5230 bot`));
3929
+ });
3763
3930
  var skillCmd = program.command("skill").description("\u7BA1\u7406 Claude Code skills");
3764
3931
  skillCmd.command("add [url]").description("\u6DFB\u52A0 skill\uFF08\u4ECE\u5B98\u65B9\u9884\u8BBE\u6216 GitHub URL\uFF09").action(async (url) => {
3765
3932
  if (url) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ccem",
3
- "version": "2.24.0",
3
+ "version": "2.26.0",
4
4
  "type": "module",
5
5
  "description": "Claude Code Environment Manager",
6
6
  "repository": {