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.
- package/README.md +31 -0
- package/dist/cli.js +233 -21
- package/dist/cli.js.map +1 -1
- package/dist/daemon-entry.js +72 -18
- package/dist/daemon-entry.js.map +1 -1
- package/dist/hook-entry.js +23 -1
- package/dist/hook-entry.js.map +1 -1
- package/package.json +1 -1
package/dist/daemon-entry.js
CHANGED
|
@@ -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
|
|
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
|
|
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))
|
|
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
|
|
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${
|
|
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: ${
|
|
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: ${
|
|
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",
|
|
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${
|
|
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)
|
|
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)
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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: ${
|
|
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,
|
|
2724
|
+
return { ok: false, ...serializeAppError(error) };
|
|
2671
2725
|
}
|
|
2672
|
-
function
|
|
2726
|
+
function errorMessage2(error) {
|
|
2673
2727
|
return error instanceof Error ? error.message : String(error);
|
|
2674
2728
|
}
|
|
2675
2729
|
function isAlreadyExists(error) {
|