larkcc 0.12.7 → 0.13.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.
package/dist/index.js CHANGED
@@ -6753,9 +6753,10 @@ function saveProfile(profile2, feishu) {
6753
6753
  for (const [key, value] of Object.entries(SEED_DEFAULTS)) {
6754
6754
  if (!raw[key]) raw[key] = value;
6755
6755
  }
6756
- if (!raw.overflow.document?.cleanup) {
6757
- raw.overflow.document = raw.overflow.document || {};
6758
- raw.overflow.document.cleanup = DEFAULT_OVERFLOW.document.cleanup;
6756
+ const overflow = raw.overflow ??= {};
6757
+ if (!overflow.document?.cleanup) {
6758
+ overflow.document = overflow.document || {};
6759
+ overflow.document.cleanup = DEFAULT_OVERFLOW.document.cleanup;
6759
6760
  }
6760
6761
  fs.writeFileSync(GLOBAL_CONFIG_PATH, jsYaml.dump(raw), "utf8");
6761
6762
  }
@@ -135301,7 +135302,7 @@ function createWSClient(appId, appSecret) {
135301
135302
  return new WSClient(options);
135302
135303
  }
135303
135304
  async function getTenantAccessToken(appId, appSecret) {
135304
- if (cachedToken && cachedToken.expiresAt > Date.now()) {
135305
+ if (cachedToken && cachedToken.appId === appId && cachedToken.expiresAt > Date.now()) {
135305
135306
  return cachedToken.token;
135306
135307
  }
135307
135308
  const res = await fetchWithProxy("https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal", {
@@ -135319,12 +135320,13 @@ async function getTenantAccessToken(appId, appSecret) {
135319
135320
  }
135320
135321
  cachedToken = {
135321
135322
  token: data.tenant_access_token,
135322
- expiresAt: Date.now() + ((data.expire ?? 7200) - 300) * 1e3
135323
+ expiresAt: Date.now() + ((data.expire ?? 7200) - TOKEN_EXPIRY_BUFFER_S) * 1e3,
135324
+ appId
135323
135325
  };
135324
135326
  return cachedToken.token;
135325
135327
  }
135326
135328
  function checkTokenExpiry() {
135327
- if (cachedToken && cachedToken.expiresAt - Date.now() < 6e5) {
135329
+ if (cachedToken && cachedToken.expiresAt - Date.now() < TOKEN_MIN_REMAINING_MS) {
135328
135330
  cachedToken = null;
135329
135331
  }
135330
135332
  }
@@ -135345,13 +135347,15 @@ async function patchMessage(client, msgId, content) {
135345
135347
  data: { content }
135346
135348
  });
135347
135349
  }
135348
- var import_undici, _proxyDispatcher, cachedToken;
135350
+ var import_undici, _proxyDispatcher, TOKEN_EXPIRY_BUFFER_S, TOKEN_MIN_REMAINING_MS, cachedToken;
135349
135351
  var init_lark = __esm({
135350
135352
  "src/client/lark.ts"() {
135351
135353
  "use strict";
135352
135354
  init_es();
135353
135355
  import_undici = __toESM(require_undici(), 1);
135354
135356
  init_dist2();
135357
+ TOKEN_EXPIRY_BUFFER_S = 300;
135358
+ TOKEN_MIN_REMAINING_MS = 6e5;
135355
135359
  cachedToken = null;
135356
135360
  }
135357
135361
  });
@@ -136421,7 +136425,7 @@ function parseTable(lines, startIndex) {
136421
136425
  const separatorLine = lines[startIndex + 1].trim();
136422
136426
  const separatorCells = separatorLine.slice(1, -1).split("|").map((c2) => c2.trim());
136423
136427
  const alignments = separatorCells.map(parseAlignment);
136424
- const _alignments = alignments;
136428
+ void alignments;
136425
136429
  const rows = [headerRow];
136426
136430
  const columnCount = headerRow.length;
136427
136431
  let endIndex = startIndex + 2;
@@ -137081,6 +137085,43 @@ var init_compose = __esm({
137081
137085
  }
137082
137086
  });
137083
137087
 
137088
+ // src/shared/tool-labels.ts
137089
+ var TOOL_LABELS, TOOL_DISPLAY, TOOL_STATUS_WORDS;
137090
+ var init_tool_labels = __esm({
137091
+ "src/shared/tool-labels.ts"() {
137092
+ "use strict";
137093
+ TOOL_LABELS = {
137094
+ Read: "\u{1F4C2} \u8BFB\u53D6\u6587\u4EF6",
137095
+ Write: "\u270F\uFE0F \u5199\u5165\u6587\u4EF6",
137096
+ Edit: "\u270F\uFE0F \u7F16\u8F91\u6587\u4EF6",
137097
+ Bash: "\u26A1 \u6267\u884C\u547D\u4EE4",
137098
+ Glob: "\u{1F50D} \u67E5\u627E\u6587\u4EF6",
137099
+ Grep: "\u{1F50E} \u641C\u7D22\u5185\u5BB9",
137100
+ LS: "\u{1F4C1} \u5217\u51FA\u76EE\u5F55",
137101
+ ExitPlanMode: "\u{1F4CB} \u9000\u51FA\u8BA1\u5212\u6A21\u5F0F",
137102
+ AskUserQuestion: "\u{1F4AC} \u63D0\u95EE"
137103
+ };
137104
+ TOOL_DISPLAY = {
137105
+ Read: "\u{1F4C2} Read",
137106
+ Write: "\u{1F4DD} Write",
137107
+ Edit: "\u270F\uFE0F Edit",
137108
+ Bash: "\u26A1 Bash",
137109
+ Glob: "\u{1F4C2} Glob",
137110
+ Grep: "\u{1F50D} Grep",
137111
+ LS: "\u{1F4C1} LS"
137112
+ };
137113
+ TOOL_STATUS_WORDS = {
137114
+ Read: ["\u{1F4D6} Reading...", "\u{1F4D6} Scanning..."],
137115
+ Write: ["\u{1F4DD} Writing...", "\u{1F4DD} Creating..."],
137116
+ Edit: ["\u270F\uFE0F Editing...", "\u270F\uFE0F Modifying..."],
137117
+ Bash: ["\u26A1 Running...", "\u26A1 Executing..."],
137118
+ Glob: ["\u{1F4C2} Finding files...", "\u{1F4C2} Scanning..."],
137119
+ Grep: ["\u{1F50D} Searching...", "\u{1F50D} Analyzing..."],
137120
+ LS: ["\u{1F4C1} Listing...", "\u{1F4C1} Browsing..."]
137121
+ };
137122
+ }
137123
+ });
137124
+
137084
137125
  // src/card/task-panel.ts
137085
137126
  function fmtDur(sec) {
137086
137127
  return sec < 60 ? `${sec.toFixed(0)}s` : `${Math.floor(sec / 60)}m ${Math.round(sec % 60)}s`;
@@ -137138,7 +137179,7 @@ function buildTaskPanelCard(options) {
137138
137179
  })
137139
137180
  });
137140
137181
  }
137141
- var STATUS_TEMPLATE, TOOL_DISPLAY;
137182
+ var STATUS_TEMPLATE;
137142
137183
  var init_task_panel = __esm({
137143
137184
  "src/card/task-panel.ts"() {
137144
137185
  "use strict";
@@ -137146,21 +137187,13 @@ var init_task_panel = __esm({
137146
137187
  init_elements();
137147
137188
  init_header();
137148
137189
  init_containers();
137190
+ init_tool_labels();
137149
137191
  STATUS_TEMPLATE = {
137150
137192
  running: { color: "turquoise", icon: "\u{1F504}", label: "Running" },
137151
137193
  completed: { color: "green", icon: "\u2705", label: "Completed" },
137152
137194
  failed: { color: "red", icon: "\u274C", label: "Failed" },
137153
137195
  stopped: { color: "grey", icon: "\u23F9", label: "Stopped" }
137154
137196
  };
137155
- TOOL_DISPLAY = {
137156
- Read: "\u{1F4C2} Read",
137157
- Write: "\u{1F4DD} Write",
137158
- Edit: "\u270F\uFE0F Edit",
137159
- Bash: "\u26A1 Bash",
137160
- Glob: "\u{1F4C2} Glob",
137161
- Grep: "\u{1F50D} Grep",
137162
- LS: "\u{1F4C1} LS"
137163
- };
137164
137197
  }
137165
137198
  });
137166
137199
 
@@ -137764,7 +137797,7 @@ async function updateToolCard(client, msgId, label, detail, resultPreview) {
137764
137797
  data: { content: JSON.stringify(card) }
137765
137798
  });
137766
137799
  }
137767
- var CHUNK_SIZE, DEFAULT_MAX_TABLES_PER_CARD, TOOL_STATUS_WORDS;
137800
+ var CHUNK_SIZE, DEFAULT_MAX_TABLES_PER_CARD;
137768
137801
  var init_message = __esm({
137769
137802
  "src/client/message.ts"() {
137770
137803
  "use strict";
@@ -137772,17 +137805,32 @@ var init_message = __esm({
137772
137805
  init_card();
137773
137806
  init_lark();
137774
137807
  init_document();
137808
+ init_tool_labels();
137775
137809
  CHUNK_SIZE = 2800;
137776
137810
  DEFAULT_MAX_TABLES_PER_CARD = 5;
137777
- TOOL_STATUS_WORDS = {
137778
- Read: ["\u{1F4D6} Reading...", "\u{1F4D6} Scanning..."],
137779
- Write: ["\u{1F4DD} Writing...", "\u{1F4DD} Creating..."],
137780
- Edit: ["\u270F\uFE0F Editing...", "\u270F\uFE0F Modifying..."],
137781
- Bash: ["\u26A1 Running...", "\u26A1 Executing..."],
137782
- Glob: ["\u{1F4C2} Finding files...", "\u{1F4C2} Scanning..."],
137783
- Grep: ["\u{1F50D} Searching...", "\u{1F50D} Analyzing..."],
137784
- LS: ["\u{1F4C1} Listing...", "\u{1F4C1} Browsing..."]
137785
- };
137811
+ }
137812
+ });
137813
+
137814
+ // src/shared/image-type.ts
137815
+ function detectImageType(buf) {
137816
+ const header = buf.slice(0, 4).toString("hex");
137817
+ for (const sig of SIGNATURES) {
137818
+ if (header.startsWith(sig.hex)) {
137819
+ return { mediaType: sig.mediaType, ext: sig.ext };
137820
+ }
137821
+ }
137822
+ return { mediaType: "image/jpeg", ext: "jpg" };
137823
+ }
137824
+ var SIGNATURES;
137825
+ var init_image_type = __esm({
137826
+ "src/shared/image-type.ts"() {
137827
+ "use strict";
137828
+ SIGNATURES = [
137829
+ { hex: "89504e47", mediaType: "image/png", ext: "png" },
137830
+ { hex: "47494638", mediaType: "image/gif", ext: "gif" },
137831
+ { hex: "52494646", mediaType: "image/webp", ext: "webp" },
137832
+ { hex: "00000100", mediaType: "image/ico", ext: "ico" }
137833
+ ];
137786
137834
  }
137787
137835
  });
137788
137836
 
@@ -137796,7 +137844,8 @@ async function downloadImage(client, messageId, imageKey) {
137796
137844
  path: { message_id: messageId, file_key: imageKey },
137797
137845
  params: { type: "image" }
137798
137846
  });
137799
- const stream4 = res.getReadableStream();
137847
+ const stream4 = res.getReadableStream?.();
137848
+ if (!stream4) throw new Error("getReadableStream returned null");
137800
137849
  const chunks = [];
137801
137850
  await new Promise((resolve, reject) => {
137802
137851
  stream4.on("data", (chunk) => {
@@ -137807,11 +137856,7 @@ async function downloadImage(client, messageId, imageKey) {
137807
137856
  });
137808
137857
  const buf = Buffer.concat(chunks);
137809
137858
  const base64 = buf.toString("base64");
137810
- const header = buf.slice(0, 4).toString("hex");
137811
- let mediaType = "image/jpeg";
137812
- if (header.startsWith("89504e47")) mediaType = "image/png";
137813
- else if (header.startsWith("47494638")) mediaType = "image/gif";
137814
- else if (header.startsWith("52494646")) mediaType = "image/webp";
137859
+ const { mediaType } = detectImageType(buf);
137815
137860
  const sizeKB = Math.round(buf.length / 1024);
137816
137861
  console.error(`[IMAGE] Downloaded: ${mediaType}, ${sizeKB}KB, base64=${base64.length}chars`);
137817
137862
  return { base64, mediaType };
@@ -137830,7 +137875,8 @@ async function downloadFile(client, messageId, fileKey, tempDir, filename) {
137830
137875
  path: { message_id: messageId, file_key: fileKey },
137831
137876
  params: { type: "file" }
137832
137877
  });
137833
- const stream4 = res.getReadableStream();
137878
+ const stream4 = res.getReadableStream?.();
137879
+ if (!stream4) throw new Error("getReadableStream returned null");
137834
137880
  const chunks = [];
137835
137881
  await new Promise((resolve, reject) => {
137836
137882
  stream4.on("data", (chunk) => {
@@ -137893,6 +137939,7 @@ async function downloadFile(client, messageId, fileKey, tempDir, filename) {
137893
137939
  var init_download = __esm({
137894
137940
  "src/client/download.ts"() {
137895
137941
  "use strict";
137942
+ init_image_type();
137896
137943
  }
137897
137944
  });
137898
137945
 
@@ -138140,7 +138187,13 @@ var init_flush = __esm({
138140
138187
  this.flushing = true;
138141
138188
  try {
138142
138189
  await this.onFlush(this.buffer);
138143
- if (!force) this.sentLength = this.buffer.length;
138190
+ if (!force) {
138191
+ this.sentLength = this.buffer.length;
138192
+ if (this.sentLength > 1e4) {
138193
+ this.buffer = this.buffer.slice(this.sentLength);
138194
+ this.sentLength = 0;
138195
+ }
138196
+ }
138144
138197
  this.lastFlushTime = Date.now();
138145
138198
  } catch (error) {
138146
138199
  if (this.onError) {
@@ -138173,13 +138226,16 @@ var init_flush = __esm({
138173
138226
  });
138174
138227
 
138175
138228
  // src/client/cardkit.ts
138229
+ function randomSpinnerWord() {
138230
+ return SPINNER_WORDS[Math.floor(Math.random() * SPINNER_WORDS.length)];
138231
+ }
138176
138232
  function checkCardKitError(response, context) {
138177
138233
  if (response?.code !== void 0 && response.code !== 0) {
138178
138234
  return new CardKitApiError(response.code, response.msg ?? "unknown", context);
138179
138235
  }
138180
138236
  return null;
138181
138237
  }
138182
- var TRUNCATE_LIMIT, CardKitApiError, CardKitController;
138238
+ var SPINNER_WORDS, TRUNCATE_LIMIT, CardKitApiError, CardKitController;
138183
138239
  var init_cardkit = __esm({
138184
138240
  "src/client/cardkit.ts"() {
138185
138241
  "use strict";
@@ -138191,6 +138247,97 @@ var init_cardkit = __esm({
138191
138247
  init_message();
138192
138248
  init_document();
138193
138249
  init_format();
138250
+ SPINNER_WORDS = [
138251
+ "Accomplishing",
138252
+ "Actioning",
138253
+ "Actualizing",
138254
+ "Baking",
138255
+ "Booping",
138256
+ "Brewing",
138257
+ "Calculating",
138258
+ "Cerebrating",
138259
+ "Channelling",
138260
+ "Churning",
138261
+ "Clauding",
138262
+ "Coalescing",
138263
+ "Cogitating",
138264
+ "Combobulating",
138265
+ "Computing",
138266
+ "Concocting",
138267
+ "Conjuring",
138268
+ "Considering",
138269
+ "Contemplating",
138270
+ "Cooking",
138271
+ "Crafting",
138272
+ "Creating",
138273
+ "Crunching",
138274
+ "Deciphering",
138275
+ "Deliberating",
138276
+ "Determining",
138277
+ "Discombobulating",
138278
+ "Divining",
138279
+ "Doing",
138280
+ "Effecting",
138281
+ "Elucidating",
138282
+ "Enchanting",
138283
+ "Envisioning",
138284
+ "Finagling",
138285
+ "Flibbertigibbeting",
138286
+ "Forging",
138287
+ "Forming",
138288
+ "Frolicking",
138289
+ "Generating",
138290
+ "Germinating",
138291
+ "Hatching",
138292
+ "Herding",
138293
+ "Hustling",
138294
+ "Ideating",
138295
+ "Imagining",
138296
+ "Incubating",
138297
+ "Inferring",
138298
+ "Jiving",
138299
+ "Manifesting",
138300
+ "Marinating",
138301
+ "Meandering",
138302
+ "Moseying",
138303
+ "Mulling",
138304
+ "Mustering",
138305
+ "Musing",
138306
+ "Noodling",
138307
+ "Percolating",
138308
+ "Perusing",
138309
+ "Philosophising",
138310
+ "Pondering",
138311
+ "Pontificating",
138312
+ "Processing",
138313
+ "Puttering",
138314
+ "Puzzling",
138315
+ "Reticulating",
138316
+ "Ruminating",
138317
+ "Scheming",
138318
+ "Schlepping",
138319
+ "Shimmying",
138320
+ "Shucking",
138321
+ "Simmering",
138322
+ "Smooshing",
138323
+ "Spelunking",
138324
+ "Spinning",
138325
+ "Stewing",
138326
+ "Sussing",
138327
+ "Synthesizing",
138328
+ "Thinking",
138329
+ "Tinkering",
138330
+ "Transmuting",
138331
+ "Unfurling",
138332
+ "Unravelling",
138333
+ "Vibing",
138334
+ "Wandering",
138335
+ "Whirring",
138336
+ "Wibbling",
138337
+ "Wizarding",
138338
+ "Working",
138339
+ "Wrangling"
138340
+ ];
138194
138341
  TRUNCATE_LIMIT = STREAMING_TRUNCATE;
138195
138342
  CardKitApiError = class extends Error {
138196
138343
  code;
@@ -138208,6 +138355,7 @@ var init_cardkit = __esm({
138208
138355
  };
138209
138356
  CardKitController = class {
138210
138357
  phase = "idle";
138358
+ createPromise = null;
138211
138359
  client;
138212
138360
  rootMsgId;
138213
138361
  cardTitle;
@@ -138407,21 +138555,22 @@ ${hint}` : hint;
138407
138555
  async ensureCardCreated() {
138408
138556
  if (this.phase === "streaming") return;
138409
138557
  if (this.phase === "creating") {
138410
- while (this.phase === "creating") {
138411
- await new Promise((r2) => setTimeout(r2, 50));
138412
- }
138558
+ await this.createPromise;
138413
138559
  return;
138414
138560
  }
138415
138561
  this.phase = "creating";
138416
- try {
138417
- await this.createCardEntity();
138418
- await this.sendCardMessage();
138419
- this.flushCtrl.start();
138420
- this.phase = "streaming";
138421
- } catch (error) {
138422
- console.error("[CARDKIT] Card creation failed:", error);
138423
- this.phase = "aborted";
138424
- }
138562
+ this.createPromise = (async () => {
138563
+ try {
138564
+ await this.createCardEntity();
138565
+ await this.sendCardMessage();
138566
+ this.flushCtrl.start();
138567
+ this.phase = "streaming";
138568
+ } catch (error) {
138569
+ console.error("[CARDKIT] Card creation failed:", error);
138570
+ this.phase = "aborted";
138571
+ }
138572
+ })();
138573
+ await this.createPromise;
138425
138574
  }
138426
138575
  /**
138427
138576
  * 创建 CardKit 卡片实体(使用 SDK)
@@ -138441,17 +138590,18 @@ ${hint}` : hint;
138441
138590
  width_mode: "fill"
138442
138591
  }
138443
138592
  });
138593
+ const spinner = randomSpinnerWord();
138444
138594
  cardJson.summary = {
138445
- content: "\u{1F914} Claude \u6B63\u5728\u601D\u8003...",
138595
+ content: `\u{1F914} Claude ${spinner}...`,
138446
138596
  i18n_content: {
138447
- zh_cn: "\u{1F914} Claude \u6B63\u5728\u601D\u8003...",
138448
- en_us: "\u{1F914} Claude is thinking..."
138597
+ zh_cn: `\u{1F914} Claude ${spinner}...`,
138598
+ en_us: `\u{1F914} Claude ${spinner}...`
138449
138599
  }
138450
138600
  };
138451
138601
  if (this.cardTitle) {
138452
138602
  cardJson.header = buildHeader({
138453
138603
  title: this.cardTitle,
138454
- subtitle: "\u6B63\u5728\u601D\u8003...",
138604
+ subtitle: `\u26A1 ${spinner}...`,
138455
138605
  template: "indigo",
138456
138606
  iconImgKey: this.headerIconImgKey
138457
138607
  });
@@ -138835,6 +138985,23 @@ var init_client = __esm({
138835
138985
  }
138836
138986
  });
138837
138987
 
138988
+ // src/shared/claude-binary.ts
138989
+ import { execSync } from "child_process";
138990
+ function findClaudeBinary() {
138991
+ const cmd = process.platform === "win32" ? "where claude 2>nul" : "which claude 2>/dev/null || command -v claude 2>/dev/null";
138992
+ try {
138993
+ const result = execSync(cmd, { encoding: "utf8", timeout: 5e3 }).trim();
138994
+ if (result) return result.split(/[\r\n]/)[0];
138995
+ } catch {
138996
+ }
138997
+ return void 0;
138998
+ }
138999
+ var init_claude_binary = __esm({
139000
+ "src/shared/claude-binary.ts"() {
139001
+ "use strict";
139002
+ }
139003
+ });
139004
+
138838
139005
  // src/session.ts
138839
139006
  import fs5 from "fs";
138840
139007
  import path5 from "path";
@@ -138899,7 +139066,7 @@ var VERSION3;
138899
139066
  var init_version = __esm({
138900
139067
  "src/version.ts"() {
138901
139068
  "use strict";
138902
- VERSION3 = "0.12.7";
139069
+ VERSION3 = "0.13.0";
138903
139070
  }
138904
139071
  });
138905
139072
 
@@ -155728,22 +155895,7 @@ async function downloadExternalImage(url2) {
155728
155895
  }
155729
155896
  async function uploadImageToFeishu(buffer, token) {
155730
155897
  try {
155731
- const header = buffer.slice(0, 4).toString("hex");
155732
- let mediaType = "image/jpeg";
155733
- let ext = "jpg";
155734
- if (header.startsWith("89504e47")) {
155735
- mediaType = "image/png";
155736
- ext = "png";
155737
- } else if (header.startsWith("47494638")) {
155738
- mediaType = "image/gif";
155739
- ext = "gif";
155740
- } else if (header.startsWith("52494646")) {
155741
- mediaType = "image/webp";
155742
- ext = "webp";
155743
- } else if (header.startsWith("00000100")) {
155744
- mediaType = "image/ico";
155745
- ext = "ico";
155746
- }
155898
+ const { mediaType, ext } = detectImageType(buffer);
155747
155899
  const formData = new FormData();
155748
155900
  formData.append("image_type", "message");
155749
155901
  formData.append("image", new Blob([new Uint8Array(buffer)], { type: mediaType }), `image.${ext}`);
@@ -155783,6 +155935,7 @@ var init_image_resolver = __esm({
155783
155935
  "use strict";
155784
155936
  init_card_optimize();
155785
155937
  init_lark();
155938
+ init_image_type();
155786
155939
  IMAGE_UPLOAD_URL = "https://open.feishu.cn/open-apis/im/v1/images";
155787
155940
  MAX_IMAGE_SIZE = 10 * 1024 * 1024;
155788
155941
  DOWNLOAD_TIMEOUT_MS = 1e4;
@@ -155791,16 +155944,6 @@ var init_image_resolver = __esm({
155791
155944
  });
155792
155945
 
155793
155946
  // src/claude.ts
155794
- import { execSync } from "child_process";
155795
- function findClaudeBinary() {
155796
- const cmd = process.platform === "win32" ? "where claude 2>nul" : "which claude 2>/dev/null || command -v claude 2>/dev/null";
155797
- try {
155798
- const result = execSync(cmd, { encoding: "utf8", timeout: 5e3 }).trim();
155799
- if (result) return result.split(/[\r\n]/)[0];
155800
- } catch {
155801
- }
155802
- return void 0;
155803
- }
155804
155947
  function formatInput(name, input) {
155805
155948
  if (["Read", "Write", "Edit"].includes(name))
155806
155949
  return String(input.file_path ?? input.path ?? "");
@@ -155815,7 +155958,7 @@ function buildReplyContext(config2, profile2, cwd2, chatId, rootMsgId) {
155815
155958
  profile: profile2 ?? "default",
155816
155959
  cwd: cwd2,
155817
155960
  sessionId: getSession() ?? "",
155818
- overflow: config2.overflow,
155961
+ overflow: config2.overflow ?? DEFAULT_OVERFLOW,
155819
155962
  chatId,
155820
155963
  rootMsgId,
155821
155964
  appId: config2.feishu.app_id,
@@ -155834,15 +155977,15 @@ function processAssistantEvent(ctx, blocks) {
155834
155977
  const tasks = [];
155835
155978
  for (const block of blocks) {
155836
155979
  if (block.type === "thinking" && block.text) {
155837
- ctx.thinkingBuffer += block.text;
155838
- logger.info(`[thinking] received ${block.text.length} chars (total: ${ctx.thinkingBuffer.length})`);
155980
+ ctx.thinkingBuffer.push(block.text);
155981
+ logger.info(`[thinking] received ${block.text.length} chars (total: ${ctx.thinkingBuffer.reduce((s10, t) => s10 + t.length, 0)})`);
155839
155982
  if (!ctx.reasoningStartTime) {
155840
155983
  ctx.reasoningStartTime = Date.now();
155841
- if (ctx.isCardkitMode) tasks.push(ctx.cardkitCtrl.updateStatus("\u{1F4AD} \u601D\u8003\u4E2D..."));
155984
+ if (ctx.isCardkitMode && ctx.cardkitCtrl) tasks.push(ctx.cardkitCtrl.updateStatus("\u{1F4AD} \u601D\u8003\u4E2D..."));
155842
155985
  }
155843
155986
  }
155844
155987
  if (block.type === "text" && block.text) {
155845
- ctx.textBuffer += block.text;
155988
+ ctx.textBuffer.push(block.text);
155846
155989
  if (ctx.cardkitCtrl) tasks.push(ctx.cardkitCtrl.append(block.text));
155847
155990
  else if (ctx.streamingCard) tasks.push(ctx.streamingCard.append(block.text));
155848
155991
  }
@@ -155853,7 +155996,7 @@ function processAssistantEvent(ctx, blocks) {
155853
155996
  const detail = formatInput(block.name, block.input ?? {});
155854
155997
  if (ctx.isCardkitMode) {
155855
155998
  logger.tool(block.name, detail);
155856
- tasks.push(ctx.cardkitCtrl.updateStatus(`<text_tag color='orange'>${label}</text_tag> \`${detail}\``));
155999
+ tasks.push(ctx.cardkitCtrl?.updateStatus(`<text_tag color='orange'>${label}</text_tag> \`${detail}\``) ?? Promise.resolve());
155857
156000
  ctx.cardkitToolResults.push({ id: block.id, name: block.name, label, detail });
155858
156001
  continue;
155859
156002
  }
@@ -155877,7 +156020,7 @@ function processUserEvent(ctx, blocks) {
155877
156020
  if (block.type === "tool_result" && block.tool_use_id) {
155878
156021
  const raw = typeof block.content === "string" ? block.content : JSON.stringify(block.content ?? "");
155879
156022
  if (ctx.isCardkitMode) {
155880
- tasks.push(ctx.cardkitCtrl.clearStatus());
156023
+ if (ctx.cardkitCtrl) tasks.push(ctx.cardkitCtrl.clearStatus());
155881
156024
  const toolEntry = ctx.cardkitToolResults.find((t) => t.id === block.tool_use_id);
155882
156025
  if (toolEntry) toolEntry.resultPreview = raw;
155883
156026
  continue;
@@ -155898,20 +156041,22 @@ function processUserEvent(ctx, blocks) {
155898
156041
  }
155899
156042
  function processSystemEvent(ctx, event) {
155900
156043
  const tasks = [];
156044
+ const ctrl = ctx.taskPanelCtrl;
156045
+ if (!ctrl) return tasks;
155901
156046
  if (event.subtype === "task_started") {
155902
- tasks.push(ctx.taskPanelCtrl.onTaskStarted(event));
156047
+ tasks.push(ctrl.onTaskStarted(event));
155903
156048
  } else if (event.subtype === "task_progress") {
155904
- tasks.push(ctx.taskPanelCtrl.onTaskProgress(event));
156049
+ tasks.push(ctrl.onTaskProgress(event));
155905
156050
  } else if (event.subtype === "task_notification") {
155906
- tasks.push(ctx.taskPanelCtrl.onTaskNotification(event));
156051
+ tasks.push(ctrl.onTaskNotification(event));
155907
156052
  } else {
155908
156053
  return tasks;
155909
156054
  }
155910
156055
  const statusSummary = ctx.taskPanelCtrl?.getStatusSummary();
155911
156056
  if (statusSummary && ctx.isCardkitMode) {
155912
- tasks.push(ctx.cardkitCtrl.updateStatus(statusSummary));
156057
+ if (ctx.cardkitCtrl) tasks.push(ctx.cardkitCtrl.updateStatus(statusSummary));
155913
156058
  } else if (!statusSummary && ctx.isCardkitMode && event.subtype === "task_notification") {
155914
- tasks.push(ctx.cardkitCtrl.clearStatus());
156059
+ if (ctx.cardkitCtrl) tasks.push(ctx.cardkitCtrl.clearStatus());
155915
156060
  }
155916
156061
  return tasks;
155917
156062
  }
@@ -155921,7 +156066,9 @@ async function processResultEvent(ctx, event) {
155921
156066
  logger.dim(`session saved: ${event.session_id}`);
155922
156067
  }
155923
156068
  await ctx.taskPanelCtrl?.completeAll();
155924
- if (!ctx.textBuffer) {
156069
+ const fullText = ctx.textBuffer.join("");
156070
+ const fullThinking = ctx.thinkingBuffer.join("");
156071
+ if (!fullText) {
155925
156072
  return;
155926
156073
  }
155927
156074
  const elapsedSeconds = (Date.now() - ctx.startTime) / 1e3;
@@ -155947,15 +156094,15 @@ async function processResultEvent(ctx, event) {
155947
156094
  let finalContent;
155948
156095
  let thinking;
155949
156096
  if (thinkingEnabled) {
155950
- finalContent = ctx.textBuffer;
155951
- thinking = ctx.thinkingBuffer || void 0;
156097
+ finalContent = fullText;
156098
+ thinking = fullThinking || void 0;
155952
156099
  if (thinking) {
155953
156100
  logger.success(`[thinking] final: ${thinking.length} chars, ${reasoningElapsedMs ? (reasoningElapsedMs / 1e3).toFixed(1) + "s" : "no timing"}`);
155954
156101
  } else {
155955
156102
  logger.dim("[thinking] no thinking blocks received from SDK");
155956
156103
  }
155957
156104
  } else {
155958
- finalContent = stripThinking(ctx.textBuffer);
156105
+ finalContent = stripThinking(fullText);
155959
156106
  }
155960
156107
  let imageFailedCount = 0;
155961
156108
  if (finalContent && ctx.config.image_resolver?.enabled !== false) {
@@ -156039,8 +156186,8 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
156039
156186
  cardkitCtrl,
156040
156187
  taskPanelCtrl,
156041
156188
  startTime: Date.now(),
156042
- textBuffer: "",
156043
- thinkingBuffer: "",
156189
+ textBuffer: [],
156190
+ thinkingBuffer: [],
156044
156191
  reasoningStartTime: null,
156045
156192
  toolCallCount: 0,
156046
156193
  toolMsgMap: /* @__PURE__ */ new Map(),
@@ -156050,8 +156197,8 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
156050
156197
  logger.info(logMsg);
156051
156198
  await taskPanelCtrl?.abortAll();
156052
156199
  const abortOptions = {
156053
- content: ctx.textBuffer || void 0,
156054
- thinking: ctx.thinkingBuffer || void 0,
156200
+ content: ctx.textBuffer.join("") || void 0,
156201
+ thinking: ctx.thinkingBuffer.join("") || void 0,
156055
156202
  reasoningElapsedMs: ctx.reasoningStartTime ? Date.now() - ctx.reasoningStartTime : void 0
156056
156203
  };
156057
156204
  if (cardkitCtrl) {
@@ -156059,7 +156206,7 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
156059
156206
  } else if (streamingCard) {
156060
156207
  await streamingCard.abort(abortOptions);
156061
156208
  } else {
156062
- await replyFinalCard(client, chatId, rootMsgId, ctx.textBuffer || "\u23F9 \u4EFB\u52A1\u5DF2\u4E2D\u65AD", replyContext, { cardTitle: config2.card_title });
156209
+ await replyFinalCard(client, chatId, rootMsgId, ctx.textBuffer.join("") || "\u23F9 \u4EFB\u52A1\u5DF2\u4E2D\u65AD", replyContext, { cardTitle: config2.card_title });
156063
156210
  }
156064
156211
  };
156065
156212
  try {
@@ -156068,7 +156215,7 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
156068
156215
  options: {
156069
156216
  cwd: cwd2,
156070
156217
  resume: sessionId,
156071
- permissionMode: config2.claude.permission_mode,
156218
+ permissionMode: config2.claude.permission_mode ?? "acceptEdits",
156072
156219
  allowedTools: config2.claude.allowed_tools,
156073
156220
  thinking: config2.claude.thinking,
156074
156221
  abortController,
@@ -156107,7 +156254,7 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
156107
156254
  }
156108
156255
  return "completed";
156109
156256
  }
156110
- var TOOL_LABELS, SILENT_TOOLS;
156257
+ var SILENT_TOOLS;
156111
156258
  var init_claude = __esm({
156112
156259
  "src/claude.ts"() {
156113
156260
  "use strict";
@@ -156115,6 +156262,7 @@ var init_claude = __esm({
156115
156262
  init_client();
156116
156263
  init_session();
156117
156264
  init_logger();
156265
+ init_config();
156118
156266
  init_guide();
156119
156267
  init_thinking();
156120
156268
  init_time();
@@ -156124,17 +156272,8 @@ var init_claude = __esm({
156124
156272
  init_update();
156125
156273
  init_cardkit();
156126
156274
  init_task_panel_ctrl();
156127
- TOOL_LABELS = {
156128
- Read: "\u{1F4C2} \u8BFB\u53D6\u6587\u4EF6",
156129
- Write: "\u270F\uFE0F \u5199\u5165\u6587\u4EF6",
156130
- Edit: "\u270F\uFE0F \u7F16\u8F91\u6587\u4EF6",
156131
- Bash: "\u26A1 \u6267\u884C\u547D\u4EE4",
156132
- Glob: "\u{1F50D} \u67E5\u627E\u6587\u4EF6",
156133
- Grep: "\u{1F50E} \u641C\u7D22\u5185\u5BB9",
156134
- LS: "\u{1F4C1} \u5217\u51FA\u76EE\u5F55",
156135
- ExitPlanMode: "\u{1F4CB} \u9000\u51FA\u8BA1\u5212\u6A21\u5F0F",
156136
- AskUserQuestion: "\u{1F4AC} \u63D0\u95EE"
156137
- };
156275
+ init_tool_labels();
156276
+ init_claude_binary();
156138
156277
  SILENT_TOOLS = /* @__PURE__ */ new Set(["ExitPlanMode", "TodoWrite", "TodoRead"]);
156139
156278
  }
156140
156279
  });
@@ -156542,6 +156681,8 @@ function stripMentions(text) {
156542
156681
  }
156543
156682
  function createMessageHandler(ctx) {
156544
156683
  const { client, config: config2, profile: profile2, cwd: cwd2, botOpenId, startupTime, commandContext } = ctx;
156684
+ const DEDUPE_WINDOW_MS = 3e4;
156685
+ const EXEC_CONFIRM_TIMEOUT_MS = 5 * 60 * 1e3;
156545
156686
  let processing = false;
156546
156687
  let processingStartedAt = 0;
156547
156688
  let currentAbortController = null;
@@ -156553,11 +156694,12 @@ function createMessageHandler(ctx) {
156553
156694
  const getOwnerOpenId = () => config2.feishu.owner_open_id;
156554
156695
  const profileLabel = profile2 ? ` [${profile2}]` : "";
156555
156696
  return async (data) => {
156697
+ if (!data?.message || !data?.sender) return;
156556
156698
  const msg = data.message;
156557
156699
  const senderId = data.sender.sender_id?.open_id ?? "";
156558
156700
  const isGroup = msg.chat_type === "group";
156559
156701
  if (!["text", "post", "image", "file"].includes(msg.message_type)) return;
156560
- const msgTimestamp = Number(msg.create_time);
156702
+ const msgTimestamp = Number(msg.create_time) || 0;
156561
156703
  const now = Date.now();
156562
156704
  if (msgTimestamp < startupTime) {
156563
156705
  logger.dim(`skipped pre-startup message (${Math.round((now - msgTimestamp) / 1e3)}s ago)`);
@@ -156571,10 +156713,10 @@ function createMessageHandler(ctx) {
156571
156713
  }
156572
156714
  const dedupeKey = `${senderId}:${msg.message_id}`;
156573
156715
  const lastSeen = recentMessages.get(dedupeKey);
156574
- if (lastSeen && now - lastSeen < 3e4) return;
156716
+ if (lastSeen && now - lastSeen < DEDUPE_WINDOW_MS) return;
156575
156717
  recentMessages.set(dedupeKey, now);
156576
156718
  for (const [k2, t] of recentMessages) {
156577
- if (now - t > 3e4) recentMessages.delete(k2);
156719
+ if (now - t > DEDUPE_WINDOW_MS) recentMessages.delete(k2);
156578
156720
  }
156579
156721
  const owner_open_id = getOwnerOpenId();
156580
156722
  if (!owner_open_id) {
@@ -156598,10 +156740,21 @@ function createMessageHandler(ctx) {
156598
156740
  const fileConfig = config2.file;
156599
156741
  const profileKey = profile2 ?? "default";
156600
156742
  if (msg.message_type === "text") {
156601
- const raw = stripHtml(JSON.parse(msg.content).text ?? "");
156743
+ let parsed;
156744
+ try {
156745
+ parsed = JSON.parse(msg.content);
156746
+ } catch {
156747
+ return;
156748
+ }
156749
+ const raw = stripHtml(parsed.text ?? "");
156602
156750
  text = isGroup ? stripMentions(raw) : raw;
156603
156751
  } else if (msg.message_type === "post") {
156604
- const raw = JSON.parse(msg.content);
156752
+ let raw;
156753
+ try {
156754
+ raw = JSON.parse(msg.content);
156755
+ } catch {
156756
+ return;
156757
+ }
156605
156758
  const post = raw.zh_cn ?? raw;
156606
156759
  const title = stripHtml(post.title ?? "");
156607
156760
  const blocks = post.content ?? [];
@@ -156614,15 +156767,25 @@ function createMessageHandler(ctx) {
156614
156767
  for (const el2 of line) {
156615
156768
  if (el2.tag === "img" && el2.image_key) {
156616
156769
  logger.dim(`downloading image from post: ${el2.image_key}`);
156617
- const img = await downloadImage(client, msg.message_id, el2.image_key);
156618
- if (img) {
156619
- images.push(img);
156770
+ try {
156771
+ const img = await downloadImage(client, msg.message_id, el2.image_key);
156772
+ if (img) {
156773
+ images.push(img);
156774
+ }
156775
+ } catch (e2) {
156776
+ logger.warn(`failed to download image from post: ${el2.image_key}`);
156620
156777
  }
156621
156778
  }
156622
156779
  }
156623
156780
  }
156624
156781
  } else if (msg.message_type === "image") {
156625
- const imageKey = JSON.parse(msg.content).image_key;
156782
+ let parsed;
156783
+ try {
156784
+ parsed = JSON.parse(msg.content);
156785
+ } catch {
156786
+ return;
156787
+ }
156788
+ const imageKey = parsed.image_key;
156626
156789
  if (imageKey) {
156627
156790
  logger.dim(`downloading image: ${imageKey}`);
156628
156791
  const img = await downloadImage(client, msg.message_id, imageKey);
@@ -156639,7 +156802,12 @@ function createMessageHandler(ctx) {
156639
156802
  await sendText(client, chatId, "\u274C \u6587\u4EF6\u5904\u7406\u529F\u80FD\u672A\u542F\u7528");
156640
156803
  return;
156641
156804
  }
156642
- const fileContent = JSON.parse(msg.content);
156805
+ let fileContent;
156806
+ try {
156807
+ fileContent = JSON.parse(msg.content);
156808
+ } catch {
156809
+ return;
156810
+ }
156643
156811
  const fileKey = fileContent.file_key;
156644
156812
  const fileName = fileContent.file_name ?? "unknown";
156645
156813
  const fileSize = fileContent.file_size ?? 0;
@@ -156702,7 +156870,7 @@ function createMessageHandler(ctx) {
156702
156870
  return;
156703
156871
  }
156704
156872
  const pendingExec = pendingExecConfirm.get(chatId);
156705
- if (pendingExec && Date.now() - pendingExec.timestamp < 5 * 60 * 1e3) {
156873
+ if (pendingExec && Date.now() - pendingExec.timestamp < EXEC_CONFIRM_TIMEOUT_MS) {
156706
156874
  const reply = text.toLowerCase().trim();
156707
156875
  if (reply === "y" || reply === "yes" || reply === "\u786E\u8BA4") {
156708
156876
  pendingExecConfirm.delete(chatId);
@@ -156720,7 +156888,7 @@ ${output}
156720
156888
  }
156721
156889
  }
156722
156890
  for (const [key, value] of pendingExecConfirm.entries()) {
156723
- if (Date.now() - value.timestamp > 5 * 60 * 1e3) {
156891
+ if (Date.now() - value.timestamp > EXEC_CONFIRM_TIMEOUT_MS) {
156724
156892
  pendingExecConfirm.delete(key);
156725
156893
  }
156726
156894
  }
@@ -156828,7 +156996,8 @@ ${result.cmd}
156828
156996
  data: { reaction_type: { emoji_type: config2.reaction?.processing ?? "Typing" } }
156829
156997
  });
156830
156998
  reactionId = reactionRes.data?.reaction_id;
156831
- } catch {
156999
+ } catch (e2) {
157000
+ logger.dim(`[reaction] create failed: ${e2}`);
156832
157001
  }
156833
157002
  try {
156834
157003
  let finalPrompt = text;
@@ -157142,6 +157311,10 @@ function ensureClaudeInPath() {
157142
157311
  return;
157143
157312
  }
157144
157313
  }
157314
+ if (findClaudeBinary()) {
157315
+ logger.dim(`claude found: ${findClaudeBinary()}`);
157316
+ return;
157317
+ }
157145
157318
  logger.warn("claude CLI not found \u2014 make sure it's installed: npm install -g @anthropic-ai/claude-code");
157146
157319
  }
157147
157320
  async function startApp(cwd2, config2, profile2, continueSession = false, force = false) {
@@ -157164,7 +157337,8 @@ async function startApp(cwd2, config2, profile2, continueSession = false, force
157164
157337
  const botInfo = await client.bot.getBotInfo({});
157165
157338
  botOpenId = botInfo.data?.open_id ?? "";
157166
157339
  if (botOpenId) logger.dim(`bot open_id: ${botOpenId}`);
157167
- } catch {
157340
+ } catch (e2) {
157341
+ logger.dim(`[bot] getBotInfo failed: ${e2}`);
157168
157342
  }
157169
157343
  const startupTime = Date.now();
157170
157344
  if (continueSession) {
@@ -157190,15 +157364,20 @@ async function startApp(cwd2, config2, profile2, continueSession = false, force
157190
157364
  startupTime,
157191
157365
  commandContext
157192
157366
  });
157193
- wsClient.start({
157194
- eventDispatcher: new EventDispatcher({}).register({
157195
- "im.message.receive_v1": handler,
157196
- "im.message.reaction.created_v1": () => {
157197
- },
157198
- "im.message.reaction.deleted_v1": () => {
157199
- }
157200
- })
157201
- });
157367
+ try {
157368
+ wsClient.start({
157369
+ eventDispatcher: new EventDispatcher({}).register({
157370
+ "im.message.receive_v1": handler,
157371
+ "im.message.reaction.created_v1": () => {
157372
+ },
157373
+ "im.message.reaction.deleted_v1": () => {
157374
+ }
157375
+ })
157376
+ });
157377
+ } catch (e2) {
157378
+ logger.error(`WebSocket connection failed: ${e2}`);
157379
+ process.exit(1);
157380
+ }
157202
157381
  logger.success("Feishu connected! Waiting for messages...");
157203
157382
  logger.dim("Press Ctrl+C to stop\n");
157204
157383
  const knownChatId = getChatId();
@@ -157266,8 +157445,8 @@ async function startApp(cwd2, config2, profile2, continueSession = false, force
157266
157445
  }
157267
157446
  process.exit(0);
157268
157447
  };
157269
- process.on("SIGINT", shutdown);
157270
- process.on("SIGTERM", shutdown);
157448
+ process.on("SIGINT", () => void shutdown().catch(console.error));
157449
+ process.on("SIGTERM", () => void shutdown().catch(console.error));
157271
157450
  }
157272
157451
  var CLAUDE_SETTINGS_PATH, LOCK_DIR, IS_WIN, PATH_SEP;
157273
157452
  var init_app = __esm({
@@ -157277,6 +157456,7 @@ var init_app = __esm({
157277
157456
  init_source();
157278
157457
  init_client();
157279
157458
  init_card();
157459
+ init_claude_binary();
157280
157460
  init_session();
157281
157461
  init_logger();
157282
157462
  init_version();
@@ -157511,7 +157691,7 @@ if (opts.killAll) {
157511
157691
  }
157512
157692
  if (opts.cleanupTmpFiles) {
157513
157693
  const baseTempDir = path9.join(os9.homedir(), ".larkcc", "temp");
157514
- const olderThanHours = parseFloat(opts.olderThan) || 0;
157694
+ const olderThanHours = Math.max(0, parseFloat(opts.olderThan) || 0);
157515
157695
  const olderThanMs = olderThanHours * 60 * 60 * 1e3;
157516
157696
  const cutoffTime = Date.now() - olderThanMs;
157517
157697
  let deletedCount = 0;