@keyou007/agent-bot 0.1.15-alpha.1 → 0.1.15-alpha.2

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.
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.15-alpha.1",
3
+ "version": "0.1.15-alpha.2",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@keyou007/agent-bot",
9
- "version": "0.1.15-alpha.1",
9
+ "version": "0.1.15-alpha.2",
10
10
  "license": "GPL-3.0-only",
11
11
  "dependencies": {
12
12
  "@agentclientprotocol/sdk": "1.2.1",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@keyou007/agent-bot",
3
- "version": "0.1.15-alpha.1",
3
+ "version": "0.1.15-alpha.2",
4
4
  "type": "module",
5
5
  "description": "Use Codex, TraeX, and compatible coding agents from Lark/Feishu.",
6
6
  "license": "GPL-3.0-only",
@@ -205,6 +205,13 @@ export interface DirectoryBrowserCardView {
205
205
  footerLines: string[];
206
206
  }
207
207
 
208
+ export interface DirectoryNewFolderCardView {
209
+ directory: string;
210
+ displayDirectory?: string;
211
+ contextKey: string;
212
+ page: number;
213
+ }
214
+
208
215
  const DIRECTORY_BROWSER_ROW_COUNT = 16;
209
216
 
210
217
  export type ThinkingCardLayout = "grouped" | "timeline";
@@ -1213,7 +1220,7 @@ export class CardRenderer {
1213
1220
  renderDirectoryBrowserCard(view: DirectoryBrowserCardView): Record<string, unknown> {
1214
1221
  const elements: Record<string, unknown>[] = [
1215
1222
  markdown(`**当前目录**:${inlineCode(view.directory)}`),
1216
- taskActionRow(view.currentActions),
1223
+ taskActionRow(view.currentActions, undefined, true),
1217
1224
  { tag: "hr" },
1218
1225
  ];
1219
1226
  const entryRows = view.entries.length === 0
@@ -1230,6 +1237,54 @@ export class CardRenderer {
1230
1237
  return sectionCard("文件浏览", elements, "blue", "2px");
1231
1238
  }
1232
1239
 
1240
+ renderDirectoryNewFolderCard(view: DirectoryNewFolderCardView): Record<string, unknown> {
1241
+ const actionValue = {
1242
+ action: "directory_new_folder_submit",
1243
+ directory: view.directory,
1244
+ contextKey: view.contextKey,
1245
+ page: String(view.page),
1246
+ };
1247
+ return compactCard("新建目录", "blue", [
1248
+ markdown(`将在当前目录下创建一个子目录:\n${inlineCode(view.displayDirectory ?? view.directory)}`),
1249
+ {
1250
+ tag: "form",
1251
+ name: "directory_new_folder_form",
1252
+ vertical_spacing: "8px",
1253
+ elements: [
1254
+ {
1255
+ tag: "input",
1256
+ element_id: "folder_name_input",
1257
+ name: "folderName",
1258
+ required: true,
1259
+ input_type: "text",
1260
+ width: "fill",
1261
+ max_length: 255,
1262
+ label: { tag: "plain_text", content: "目录名" },
1263
+ placeholder: { tag: "plain_text", content: "请输入目录名" },
1264
+ },
1265
+ {
1266
+ tag: "button",
1267
+ name: "directory_new_folder_create",
1268
+ text: { tag: "plain_text", content: "Create" },
1269
+ type: "primary",
1270
+ width: "fill",
1271
+ action_type: "form_submit",
1272
+ value: actionValue,
1273
+ },
1274
+ ],
1275
+ },
1276
+ taskActionRow([{
1277
+ text: "Back",
1278
+ value: {
1279
+ action: "directory_new_folder_cancel",
1280
+ directory: view.directory,
1281
+ contextKey: view.contextKey,
1282
+ page: String(view.page),
1283
+ },
1284
+ }]),
1285
+ ]);
1286
+ }
1287
+
1233
1288
  renderResetHistoryCard(view: ResetHistoryCardView): Record<string, unknown> {
1234
1289
  const elements: Record<string, unknown>[] = [
1235
1290
  {
@@ -2481,13 +2536,25 @@ function resetHistoryEntryRow(
2481
2536
  };
2482
2537
  }
2483
2538
 
2484
- function taskActionRow(actions: TaskListCardAction[], trailingText?: string): Record<string, unknown> {
2485
- const columns = actions.map((action) => ({
2486
- tag: "column",
2487
- width: "auto",
2488
- vertical_align: "center",
2489
- elements: [taskActionElement(action)],
2490
- }));
2539
+ function taskActionRow(
2540
+ actions: TaskListCardAction[],
2541
+ trailingText?: string,
2542
+ dotSeparated = false,
2543
+ ): Record<string, unknown> {
2544
+ const columns = actions.flatMap((action, index) => [
2545
+ ...(dotSeparated && index > 0 ? [{
2546
+ tag: "column",
2547
+ width: "auto",
2548
+ vertical_align: "center",
2549
+ elements: [markdown("<font color='grey'>·</font>")],
2550
+ }] : []),
2551
+ {
2552
+ tag: "column",
2553
+ width: "auto",
2554
+ vertical_align: "center",
2555
+ elements: [taskActionElement(action)],
2556
+ },
2557
+ ]);
2491
2558
  if (trailingText) {
2492
2559
  columns.push({
2493
2560
  tag: "column",
@@ -327,7 +327,10 @@ function getCardActionValue(action: Record<string, unknown>): Record<string, unk
327
327
  const option = parseJsonObject(action.option);
328
328
  if (Object.keys(option).length > 0) return option;
329
329
  }
330
- return isRecord(action.value) ? action.value : {};
330
+ const value = isRecord(action.value) ? action.value : {};
331
+ return isRecord(action.form_value)
332
+ ? { ...value, formValue: action.form_value }
333
+ : value;
331
334
  }
332
335
 
333
336
  function getEventId(data: unknown): string | undefined {
@@ -821,6 +821,30 @@ export class ProxySessionController {
821
821
  const filePath = directoryFileActionPath(scopedAction.value.filePath);
822
822
  await this.assertSendableFile(filePath);
823
823
  await this.outbound.sendFile(contextKey, filePath);
824
+ } else if (kind === "directory_new_folder_prompt") {
825
+ const directory = directoryActionPath(scopedAction.value.directory);
826
+ await this.assertBrowsableDirectory(directory);
827
+ await this.outbound.updateInteractiveCard(
828
+ contextKey,
829
+ requiredCardMessageId(scopedAction.messageId),
830
+ this.cardRenderer.renderDirectoryNewFolderCard({
831
+ directory,
832
+ displayDirectory: abbreviateHomeDirectory(directory),
833
+ contextKey,
834
+ page: directoryPageValue(scopedAction.value.page),
835
+ }),
836
+ );
837
+ } else if (kind === "directory_new_folder_submit") {
838
+ await this.createDirectoryFromCard(contextKey, scopedAction);
839
+ } else if (kind === "directory_new_folder_cancel") {
840
+ await this.openDirectoryBrowser(
841
+ contextKey,
842
+ directoryActionPath(scopedAction.value.directory),
843
+ {
844
+ updateMessageId: requiredCardMessageId(scopedAction.messageId),
845
+ page: directoryPageValue(scopedAction.value.page),
846
+ },
847
+ );
824
848
  } else if (kind === "directory_new" || kind === "directory_new_group") {
825
849
  const directory = directoryActionPath(scopedAction.value.directory);
826
850
  await this.assertBrowsableDirectory(directory);
@@ -4842,12 +4866,12 @@ export class ProxySessionController {
4842
4866
  const card = this.cardRenderer.renderDirectoryBrowserCard({
4843
4867
  directory: abbreviateHomeDirectory(directory),
4844
4868
  entries: visibleEntries,
4845
- currentActions: directoryCreationActions(directory, contextKey),
4869
+ currentActions: directoryCreationActions(directory, contextKey, page),
4846
4870
  navigationActions,
4847
4871
  footerLines: [
4848
4872
  `第 ${page + 1}/${totalPages} 页 · ${directoryCount} 个目录 · ${entries.length - directoryCount} 个文件`,
4849
4873
  "",
4850
- "> 点击目录名称进入,点击文件名称发送到当前会话;卡片顶部的 **New** 和 **NewGroup** 使用当前目录。",
4874
+ "> 点击目录名称进入,点击文件名称发送到当前会话;卡片顶部可新建目录、任务或群聊。",
4851
4875
  ],
4852
4876
  });
4853
4877
  if (options.updateMessageId) {
@@ -4913,6 +4937,27 @@ export class ProxySessionController {
4913
4937
  if (!stats.isDirectory()) throw new Error(`这不是目录:${directory}`);
4914
4938
  }
4915
4939
 
4940
+ private async createDirectoryFromCard(contextKey: string, action: CardAction): Promise<void> {
4941
+ const directory = directoryActionPath(action.value.directory);
4942
+ await this.assertBrowsableDirectory(directory);
4943
+ const folderName = directoryFolderName(cardFormValue(action.value, "folderName"));
4944
+ const newDirectory = path.join(directory, folderName);
4945
+ try {
4946
+ await fs.mkdir(newDirectory);
4947
+ } catch (error) {
4948
+ const code = typeof error === "object" && error !== null && "code" in error
4949
+ ? String(error.code)
4950
+ : undefined;
4951
+ if (code === "EEXIST") throw new Error(`目录已存在:${newDirectory}`);
4952
+ throw new Error(`无法创建目录 ${newDirectory}:${runtimeErrorMessage(error)}`);
4953
+ }
4954
+ await this.openDirectoryBrowser(contextKey, directory, {
4955
+ updateMessageId: requiredCardMessageId(action.messageId),
4956
+ page: directoryPageValue(action.value.page),
4957
+ });
4958
+ await this.outbound.sendText(contextKey, `已创建目录:${newDirectory}`);
4959
+ }
4960
+
4916
4961
  private async assertSendableFile(filePath: string): Promise<void> {
4917
4962
  let stats;
4918
4963
  try {
@@ -6424,10 +6469,45 @@ function directoryFileActionPath(value: unknown): string {
6424
6469
  return path.resolve(value);
6425
6470
  }
6426
6471
 
6427
- function directoryCreationActions(directory: string, contextKey: string): TaskListCardAction[] {
6472
+ function cardFormValue(value: Record<string, unknown>, name: string): unknown {
6473
+ const formValue = value.formValue;
6474
+ return typeof formValue === "object" && formValue !== null
6475
+ ? (formValue as Record<string, unknown>)[name]
6476
+ : undefined;
6477
+ }
6478
+
6479
+ function directoryFolderName(value: unknown): string {
6480
+ if (typeof value !== "string" || !value.trim()) throw new Error("请输入目录名。");
6481
+ const name = value.trim();
6482
+ if (name === "." || name === "..") throw new Error("目录名不能是 . 或 ..。");
6483
+ if (name.length > 255) throw new Error("目录名不能超过 255 个字符。");
6484
+ if (/[<>:"/\\|?*\u0000-\u001F]/u.test(name)) {
6485
+ throw new Error("目录名不能包含路径分隔符或以下字符:< > : \" / \\ | ? *");
6486
+ }
6487
+ if (/[. ]$/u.test(name)) throw new Error("目录名不能以空格或句点结尾。");
6488
+ if (/^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/iu.test(name)) {
6489
+ throw new Error("该名称是系统保留名称,请使用其他目录名。");
6490
+ }
6491
+ return name;
6492
+ }
6493
+
6494
+ function directoryCreationActions(
6495
+ directory: string,
6496
+ contextKey: string,
6497
+ page: number,
6498
+ ): TaskListCardAction[] {
6428
6499
  return [
6429
6500
  {
6430
- text: "New",
6501
+ text: "NewFolder",
6502
+ value: {
6503
+ action: "directory_new_folder_prompt",
6504
+ directory,
6505
+ contextKey,
6506
+ page: String(page),
6507
+ },
6508
+ },
6509
+ {
6510
+ text: "NewTask",
6431
6511
  value: {
6432
6512
  action: "directory_new",
6433
6513
  directory,
@@ -6435,7 +6515,7 @@ function directoryCreationActions(directory: string, contextKey: string): TaskLi
6435
6515
  },
6436
6516
  },
6437
6517
  {
6438
- text: "NewGroup",
6518
+ text: "NewGroupTask",
6439
6519
  value: {
6440
6520
  action: "directory_new_group",
6441
6521
  directory,