lark-coding-assistant 0.1.1 → 0.1.3

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.
@@ -143,6 +143,35 @@ function verifyBindCode(code, encoded) {
143
143
  return actual.length === expected.length && timingSafeEqual(actual, expected);
144
144
  }
145
145
 
146
+ // src/core/errors.ts
147
+ var AppError = class extends Error {
148
+ code;
149
+ context;
150
+ constructor(code, message, context = {}, options = {}) {
151
+ super(message, options);
152
+ this.name = "AppError";
153
+ this.code = code;
154
+ this.context = context;
155
+ }
156
+ };
157
+ function isAppError(error) {
158
+ return error instanceof AppError;
159
+ }
160
+ function errorMessage(error) {
161
+ return error instanceof Error ? error.message : String(error);
162
+ }
163
+ function serializeAppError(error) {
164
+ if (!isAppError(error)) return { error: errorMessage(error) };
165
+ const errorContext = Object.fromEntries(
166
+ Object.entries(error.context).filter(([, value]) => value !== void 0)
167
+ );
168
+ return {
169
+ error: error.message,
170
+ errorCode: error.code,
171
+ ...Object.keys(errorContext).length > 0 ? { errorContext } : {}
172
+ };
173
+ }
174
+
146
175
  // src/tmux/controller.ts
147
176
  import { randomBytes as randomBytes2 } from "crypto";
148
177
 
@@ -172,10 +201,18 @@ var TmuxController = class {
172
201
  }
173
202
  async create(options) {
174
203
  if (!/^[a-zA-Z0-9_-]{1,64}$/.test(options.sessionName)) {
175
- throw new Error("tmux session name must contain only letters, digits, underscore, or dash");
204
+ throw new AppError(
205
+ "INVALID_SESSION_NAME",
206
+ "tmux session name must contain only letters, digits, underscore, or dash",
207
+ { sessionId: options.sessionName }
208
+ );
176
209
  }
177
210
  if (await this.hasSession(options.sessionName)) {
178
- throw new Error(`tmux session already exists: ${options.sessionName}`);
211
+ throw new AppError(
212
+ "SESSION_EXISTS",
213
+ `tmux session already exists: ${options.sessionName}`,
214
+ { sessionId: displaySessionId(options.sessionName) }
215
+ );
179
216
  }
180
217
  const environment = Object.entries(options.env ?? {}).map(([key, value]) => {
181
218
  if (!/^[A-Z][A-Z0-9_]*$/.test(key)) throw new Error(`unsafe tmux environment key: ${key}`);
@@ -278,6 +315,9 @@ var TmuxController = class {
278
315
  await runFile(this.binary, ["kill-session", "-t", `=${sessionName}`]);
279
316
  }
280
317
  };
318
+ function displaySessionId(sessionName) {
319
+ return sessionName.replace(/^lark-coding-assistant-/, "");
320
+ }
281
321
  function parsePane(line) {
282
322
  const [sessionName, paneId, pidText, currentCommand, cwd, deadText, cursorXText, cursorYText] = line.trim().split(" ");
283
323
  const pid = Number(pidText);
@@ -1672,10 +1712,20 @@ var AssistantDaemon = class {
1672
1712
  }
1673
1713
  }
1674
1714
  async startSession(sessionId, cwd, agentId, resume) {
1675
- if (!validSessionId(sessionId)) return { ok: false, error: "session name must use letters, digits, underscore, or dash" };
1715
+ if (!validSessionId(sessionId)) {
1716
+ return fail(new AppError(
1717
+ "INVALID_SESSION_NAME",
1718
+ "session name must use letters, digits, underscore, or dash",
1719
+ { sessionId }
1720
+ ));
1721
+ }
1676
1722
  const existing = this.state.sessions?.[sessionId];
1677
1723
  if (existing && await this.tmux.inspect(existing.paneId)) {
1678
- return { ok: false, error: `managed coding-agent session is already running: ${sessionId}` };
1724
+ return fail(new AppError(
1725
+ "SESSION_EXISTS",
1726
+ `managed coding-agent session is already running: ${sessionId}`,
1727
+ { sessionId }
1728
+ ));
1679
1729
  }
1680
1730
  const becomesActive = !this.state.activeSessionId;
1681
1731
  if (becomesActive) {
@@ -1775,7 +1825,7 @@ var AssistantDaemon = class {
1775
1825
  await this.gateway?.sendText(message.chatId, "\u7528\u6CD5\uFF1A/tail [20-300]");
1776
1826
  return;
1777
1827
  }
1778
- const output = await this.tail(lines).catch((error) => `\u8BFB\u53D6\u5931\u8D25\uFF1A${errorMessage(error)}`);
1828
+ const output = await this.tail(lines).catch((error) => `\u8BFB\u53D6\u5931\u8D25\uFF1A${errorMessage2(error)}`);
1779
1829
  const session = this.activeSession();
1780
1830
  const metadata = session ? `**${session.id} \xB7 ${getAgentAdapter(session.agent).displayName}** \xB7 \u72B6\u6001 \`${this.screen?.state ?? "unknown"}\` \xB7 ${manualTimestamp()}` : "**\u5F53\u524D\u6CA1\u6709 active session**";
1781
1831
  await this.gateway?.sendMarkdown(message.chatId, `${metadata}
@@ -1797,7 +1847,7 @@ ${escapeFence2(output).slice(-6800)}
1797
1847
  try {
1798
1848
  await this.gateway?.sendManual(message.chatId, view);
1799
1849
  } catch (error) {
1800
- await this.log(`manual card failed: ${errorMessage(error)}`);
1850
+ await this.log(`manual card failed: ${errorMessage2(error)}`);
1801
1851
  await this.gateway?.sendText(
1802
1852
  message.chatId,
1803
1853
  "\u624B\u52A8\u9065\u63A7\u5361\u53D1\u9001\u5931\u8D25\u3002\u53EF\u4F7F\u7528 /tail 120 \u67E5\u770B\u7EC8\u7AEF\uFF0C\u6216\u4F7F\u7528 /key\u3001/type\u3001/submit \u64CD\u4F5C\u3002"
@@ -1843,7 +1893,7 @@ ${escapeFence2(output).slice(-6800)}
1843
1893
  this.state.activeSessionId
1844
1894
  );
1845
1895
  } catch (error) {
1846
- await this.log(`session picker notification failed: ${errorMessage(error)}`);
1896
+ await this.log(`session picker notification failed: ${errorMessage2(error)}`);
1847
1897
  await this.gateway?.sendText(
1848
1898
  message.chatId,
1849
1899
  "Session \u9009\u62E9\u5361\u7247\u53D1\u9001\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u4E5F\u53EF\u4EE5\u53D1\u9001 /use <session \u540D\u79F0> \u8FDB\u884C\u5207\u6362\u3002"
@@ -2062,7 +2112,7 @@ ${escapeFence2(output).slice(-6800)}
2062
2112
  return {
2063
2113
  type: "manual",
2064
2114
  content: "\u624B\u52A8\u64CD\u4F5C\u5931\u8D25\u3002",
2065
- view: this.manualView(session, current2, "error", errorMessage(error), operation, action.manualMode)
2115
+ view: this.manualView(session, current2, "error", errorMessage2(error), operation, action.manualMode)
2066
2116
  };
2067
2117
  }
2068
2118
  const current = this.screen ?? screen;
@@ -2126,7 +2176,7 @@ ${escapeFence2(output).slice(-6800)}
2126
2176
  ${escapeFence2(output).slice(-6500)}
2127
2177
  \`\`\``);
2128
2178
  } catch (error) {
2129
- await this.gateway?.sendText(chatId, `\u624B\u52A8\u64CD\u4F5C\u5931\u8D25\uFF1A${errorMessage(error)}`);
2179
+ await this.gateway?.sendText(chatId, `\u624B\u52A8\u64CD\u4F5C\u5931\u8D25\uFF1A${errorMessage2(error)}`);
2130
2180
  }
2131
2181
  }
2132
2182
  async handleChoiceAction(action, session, event) {
@@ -2395,9 +2445,13 @@ ${escapeFence2(output).slice(-6500)}
2395
2445
  return { ok: true, committed: false };
2396
2446
  }
2397
2447
  async stopSession(sessionId = this.state.activeSessionId) {
2398
- if (!sessionId) return { ok: false, error: "no active managed session" };
2448
+ if (!sessionId) {
2449
+ return fail(new AppError("SESSION_NOT_FOUND", "no active managed session", { sessionId: "default" }));
2450
+ }
2399
2451
  const session = this.state.sessions?.[sessionId];
2400
- if (!session) return { ok: false, error: `unknown session: ${sessionId}` };
2452
+ if (!session) {
2453
+ return fail(new AppError("SESSION_NOT_FOUND", `unknown session: ${sessionId}`, { sessionId }));
2454
+ }
2401
2455
  if (await this.tmux.hasSession(session.sessionName)) {
2402
2456
  await this.tmux.killSession(session.sessionName);
2403
2457
  }
@@ -2461,7 +2515,7 @@ ${escapeFence2(output).slice(-6500)}
2461
2515
  }
2462
2516
  async runScheduledPoll() {
2463
2517
  const operation = this.poll().catch(async (error) => {
2464
- if (!this.closing) await this.log(`poll failed: ${errorMessage(error)}`);
2518
+ if (!this.closing) await this.log(`poll failed: ${errorMessage2(error)}`);
2465
2519
  });
2466
2520
  this.pollInFlight = operation;
2467
2521
  try {
@@ -2515,7 +2569,7 @@ ${escapeFence2(output).slice(-6500)}
2515
2569
  await this.gateway.sendText(this.state.boundChatId, `${agentName}/tmux pane \u5DF2\u9000\u51FA\u3002`);
2516
2570
  }
2517
2571
  } catch (error) {
2518
- await this.log(`notification failed: ${errorMessage(error)}`);
2572
+ await this.log(`notification failed: ${errorMessage2(error)}`);
2519
2573
  }
2520
2574
  }
2521
2575
  async maybeNotifyUnresolved() {
@@ -2538,11 +2592,11 @@ ${escapeFence2(output).slice(-6500)}
2538
2592
  this.manualView(session, screen, "active", "\u5F53\u524D\u7EC8\u7AEF\u4EA4\u4E92\u65E0\u6CD5\u5B89\u5168\u8BC6\u522B\uFF0C\u5DF2\u81EA\u52A8\u8FDB\u5165\u624B\u52A8\u9065\u63A7\u515C\u5E95\u3002")
2539
2593
  );
2540
2594
  } catch (error) {
2541
- await this.log(`manual fallback card failed: ${errorMessage(error)}`);
2595
+ await this.log(`manual fallback card failed: ${errorMessage2(error)}`);
2542
2596
  await this.gateway.sendText(
2543
2597
  this.state.boundChatId,
2544
2598
  "\u5F53\u524D\u7EC8\u7AEF\u4EA4\u4E92\u65E0\u6CD5\u5B89\u5168\u8BC6\u522B\uFF0C\u4E14\u624B\u52A8\u9065\u63A7\u5361\u53D1\u9001\u5931\u8D25\u3002\u53EF\u4F7F\u7528 /tail 120\u3001/key\u3001/type \u6216 /submit \u5904\u7406\u3002"
2545
- ).catch((sendError) => this.log(`manual fallback text failed: ${errorMessage(sendError)}`));
2599
+ ).catch((sendError) => this.log(`manual fallback text failed: ${errorMessage2(sendError)}`));
2546
2600
  }
2547
2601
  }
2548
2602
  async handleTurnComplete(candidate) {
@@ -2624,7 +2678,7 @@ ${output}`
2624
2678
  await this.gateway?.sendText(
2625
2679
  this.state.boundChatId,
2626
2680
  `${getAgentAdapter(removedActive.agent).displayName}/tmux pane \u5DF2\u9000\u51FA\u3002`
2627
- ).catch((error) => this.log(`exit notification failed: ${errorMessage(error)}`));
2681
+ ).catch((error) => this.log(`exit notification failed: ${errorMessage2(error)}`));
2628
2682
  }
2629
2683
  this.state = {
2630
2684
  ...this.state,
@@ -2667,9 +2721,9 @@ ${output}`
2667
2721
  }
2668
2722
  };
2669
2723
  function fail(error) {
2670
- return { ok: false, error: error instanceof Error ? error.message : String(error) };
2724
+ return { ok: false, ...serializeAppError(error) };
2671
2725
  }
2672
- function errorMessage(error) {
2726
+ function errorMessage2(error) {
2673
2727
  return error instanceof Error ? error.message : String(error);
2674
2728
  }
2675
2729
  function isAlreadyExists(error) {