pi-remote-feishu 0.1.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 (102) hide show
  1. package/ARCHITECTURE.md +837 -0
  2. package/ARCHITECTURE.zh-CN.md +704 -0
  3. package/README.md +118 -0
  4. package/dist/attachments/mime.d.ts +11 -0
  5. package/dist/attachments/mime.js +27 -0
  6. package/dist/attachments/mime.js.map +1 -0
  7. package/dist/attachments/processor.d.ts +29 -0
  8. package/dist/attachments/processor.js +48 -0
  9. package/dist/attachments/processor.js.map +1 -0
  10. package/dist/attachments/temp-files.d.ts +16 -0
  11. package/dist/attachments/temp-files.js +32 -0
  12. package/dist/attachments/temp-files.js.map +1 -0
  13. package/dist/bin/pi-remote-feishu.d.ts +2 -0
  14. package/dist/bin/pi-remote-feishu.js +359 -0
  15. package/dist/bin/pi-remote-feishu.js.map +1 -0
  16. package/dist/bridge/card-actions.d.ts +13 -0
  17. package/dist/bridge/card-actions.js +156 -0
  18. package/dist/bridge/card-actions.js.map +1 -0
  19. package/dist/bridge/conversation-router.d.ts +21 -0
  20. package/dist/bridge/conversation-router.js +65 -0
  21. package/dist/bridge/conversation-router.js.map +1 -0
  22. package/dist/bridge/message-handler.d.ts +22 -0
  23. package/dist/bridge/message-handler.js +218 -0
  24. package/dist/bridge/message-handler.js.map +1 -0
  25. package/dist/bridge/message-normalizer.d.ts +17 -0
  26. package/dist/bridge/message-normalizer.js +81 -0
  27. package/dist/bridge/message-normalizer.js.map +1 -0
  28. package/dist/bridge/prompt-queue.d.ts +20 -0
  29. package/dist/bridge/prompt-queue.js +39 -0
  30. package/dist/bridge/prompt-queue.js.map +1 -0
  31. package/dist/bridge/runtime-host.d.ts +45 -0
  32. package/dist/bridge/runtime-host.js +133 -0
  33. package/dist/bridge/runtime-host.js.map +1 -0
  34. package/dist/bridge/session-host-manager.d.ts +59 -0
  35. package/dist/bridge/session-host-manager.js +143 -0
  36. package/dist/bridge/session-host-manager.js.map +1 -0
  37. package/dist/bridge/stream-renderer.d.ts +50 -0
  38. package/dist/bridge/stream-renderer.js +108 -0
  39. package/dist/bridge/stream-renderer.js.map +1 -0
  40. package/dist/bridge/ui-context.d.ts +19 -0
  41. package/dist/bridge/ui-context.js +150 -0
  42. package/dist/bridge/ui-context.js.map +1 -0
  43. package/dist/cards/common.d.ts +33 -0
  44. package/dist/cards/common.js +84 -0
  45. package/dist/cards/common.js.map +1 -0
  46. package/dist/cards/help.d.ts +11 -0
  47. package/dist/cards/help.js +31 -0
  48. package/dist/cards/help.js.map +1 -0
  49. package/dist/cards/models.d.ts +24 -0
  50. package/dist/cards/models.js +46 -0
  51. package/dist/cards/models.js.map +1 -0
  52. package/dist/cards/permission.d.ts +9 -0
  53. package/dist/cards/permission.js +17 -0
  54. package/dist/cards/permission.js.map +1 -0
  55. package/dist/cards/sessions.d.ts +11 -0
  56. package/dist/cards/sessions.js +54 -0
  57. package/dist/cards/sessions.js.map +1 -0
  58. package/dist/cards/status.d.ts +15 -0
  59. package/dist/cards/status.js +20 -0
  60. package/dist/cards/status.js.map +1 -0
  61. package/dist/cards/stop.d.ts +16 -0
  62. package/dist/cards/stop.js +24 -0
  63. package/dist/cards/stop.js.map +1 -0
  64. package/dist/config/load-config.d.ts +19 -0
  65. package/dist/config/load-config.js +51 -0
  66. package/dist/config/load-config.js.map +1 -0
  67. package/dist/config/schema.d.ts +52 -0
  68. package/dist/config/schema.js +260 -0
  69. package/dist/config/schema.js.map +1 -0
  70. package/dist/extensions/index.d.ts +7 -0
  71. package/dist/extensions/index.js +71 -0
  72. package/dist/extensions/index.js.map +1 -0
  73. package/dist/extensions/lark-cli-guard.d.ts +12 -0
  74. package/dist/extensions/lark-cli-guard.js +75 -0
  75. package/dist/extensions/lark-cli-guard.js.map +1 -0
  76. package/dist/feishu/channel.d.ts +28 -0
  77. package/dist/feishu/channel.js +191 -0
  78. package/dist/feishu/channel.js.map +1 -0
  79. package/dist/feishu/context.d.ts +5 -0
  80. package/dist/feishu/context.js +19 -0
  81. package/dist/feishu/context.js.map +1 -0
  82. package/dist/feishu/webhook.d.ts +5 -0
  83. package/dist/feishu/webhook.js +8 -0
  84. package/dist/feishu/webhook.js.map +1 -0
  85. package/dist/index.d.ts +30 -0
  86. package/dist/index.js +29 -0
  87. package/dist/index.js.map +1 -0
  88. package/dist/store/json-store.d.ts +25 -0
  89. package/dist/store/json-store.js +105 -0
  90. package/dist/store/json-store.js.map +1 -0
  91. package/dist/store/store.d.ts +7 -0
  92. package/dist/store/store.js +15 -0
  93. package/dist/store/store.js.map +1 -0
  94. package/dist/tools/send-file-to-chat.d.ts +26 -0
  95. package/dist/tools/send-file-to-chat.js +85 -0
  96. package/dist/tools/send-file-to-chat.js.map +1 -0
  97. package/dist/types.d.ts +300 -0
  98. package/dist/types.js +2 -0
  99. package/dist/types.js.map +1 -0
  100. package/package.json +67 -0
  101. package/skills/lark-doc-cli/SKILL.md +56 -0
  102. package/skills/lark-im-readonly/SKILL.md +49 -0
@@ -0,0 +1,108 @@
1
+ /**
2
+ * 订阅会话事件并把它们翻译成流式文本。
3
+ * 返回取消订阅函数。
4
+ *
5
+ * 细节:
6
+ * - 文本增量(text_delta)直接追加
7
+ * - 思考增量(thinking_delta)按 showThinking 配置处理:
8
+ * hide 丢弃 / plain 原文输出 / quote 逐字符加 "> " 前缀(形成引用块)
9
+ * - 工具调用、压缩、重试等事件在 showToolEvents 开启时输出提示文字
10
+ */
11
+ function createSessionEventForwarder(session, writer, config) {
12
+ /** 是否正处于思考块内(用于在切回正文时补换行) */
13
+ let inThinking = false;
14
+ /** 下一行是否需要在行首加 "> " 前缀 */
15
+ let needsQuotePrefix = true;
16
+ /** 结束思考块:返回补上的分隔空行 */
17
+ function closeThinking() {
18
+ if (!inThinking)
19
+ return "";
20
+ inThinking = false;
21
+ needsQuotePrefix = true;
22
+ return "\n\n";
23
+ }
24
+ return session.subscribe((event) => {
25
+ // ---- 助手消息相关事件 ----
26
+ if (event.type === "message_update") {
27
+ const messageEvent = event.assistantMessageEvent;
28
+ if (!messageEvent)
29
+ return;
30
+ if (messageEvent.type === "text_delta") {
31
+ // 正文增量:先关闭思考块再追加
32
+ void writer.append(closeThinking() + (messageEvent.delta ?? ""));
33
+ }
34
+ else if (messageEvent.type === "thinking_delta" && config.rendering.showThinking !== "hide") {
35
+ const delta = messageEvent.delta ?? "";
36
+ if (config.rendering.showThinking === "plain") {
37
+ // 原文模式:直接输出思考内容
38
+ void writer.append(delta);
39
+ return;
40
+ }
41
+ // quote 模式:逐字符处理换行,保证每行都有 "> " 前缀(Markdown 引用块)
42
+ let output = "";
43
+ for (const character of delta) {
44
+ if (needsQuotePrefix) {
45
+ output += "> ";
46
+ needsQuotePrefix = false;
47
+ }
48
+ output += character;
49
+ if (character === "\n") {
50
+ needsQuotePrefix = true;
51
+ }
52
+ }
53
+ inThinking = true;
54
+ void writer.append(output);
55
+ }
56
+ else if (messageEvent.type === "error") {
57
+ void writer.append(`${closeThinking()}Model returned an error.`);
58
+ }
59
+ return;
60
+ }
61
+ // ---- 工具/系统事件:仅在 showToolEvents 开启时输出 ----
62
+ if (!config.rendering.showToolEvents)
63
+ return;
64
+ if (event.type === "tool_execution_start") {
65
+ void writer.append(`${closeThinking()}\nRunning tool: ${event.toolName ?? "unknown"}\n`);
66
+ }
67
+ else if (event.type === "tool_execution_update") {
68
+ void writer.append(`${closeThinking()}${String(event.partialResult ?? "")}`);
69
+ }
70
+ else if (event.type === "tool_execution_end") {
71
+ void writer.append(`${closeThinking()}${event.isError ? "Tool failed" : "Tool completed"}\n`);
72
+ }
73
+ else if (event.type === "compaction_start") {
74
+ void writer.append(`${closeThinking()}Compacting context...\n`);
75
+ }
76
+ else if (event.type === "compaction_end") {
77
+ void writer.append(`${closeThinking()}Compaction complete.\n`);
78
+ }
79
+ else if (event.type === "auto_retry_start") {
80
+ void writer.append(`${closeThinking()}Retrying (${event.attempt ?? "?"}/${event.maxAttempts ?? "?"})...\n`);
81
+ }
82
+ else if (event.type === "auto_retry_end") {
83
+ void writer.append(`${closeThinking()}${event.success ? "Retry succeeded" : "Retry failed"}\n`);
84
+ }
85
+ });
86
+ }
87
+ /**
88
+ * 运行一次 prompt 并渲染输出:
89
+ * - mode 为 text 时静默执行,不渲染
90
+ * - 其他模式通过 channel.streamMarkdown 创建流式消息,期间把会话事件
91
+ * 转发给 writer,消息发送完成后 Pi 侧会自动结束流
92
+ */
93
+ export async function renderPromptStream(options) {
94
+ if (options.config.rendering.mode === "text") {
95
+ await options.runPrompt();
96
+ return;
97
+ }
98
+ await options.channel.streamMarkdown(options.chatId, async (writer) => {
99
+ const unsubscribe = createSessionEventForwarder(options.session, writer, options.config);
100
+ try {
101
+ await options.runPrompt();
102
+ }
103
+ finally {
104
+ unsubscribe();
105
+ }
106
+ }, options.replyTo ? { replyTo: options.replyTo } : undefined);
107
+ }
108
+ //# sourceMappingURL=stream-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stream-renderer.js","sourceRoot":"","sources":["../../src/bridge/stream-renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
2
+ /**
3
+ * 兑现一张待确认的权限卡片:
4
+ * 根据卡片按钮携带的 dialogId 找到等待中的 Promise,把用户选择传回去。
5
+ * 返回用户的选择(undefined 表示找不到对应弹窗)。
6
+ */
7
+ export declare function resolvePermissionCardAction(value: Record<string, unknown>): string | undefined;
8
+ /**
9
+ * 创建"飞书版"的 ExtensionUIContext:
10
+ * 把 Pi 的 ui.select / ui.confirm / notify 等 UI 能力桥接到飞书聊天。
11
+ *
12
+ * - select:发送一张"权限确认"卡片到当前聊天,等待用户在卡片上选择(可中止/超时)
13
+ * - confirm:复用 select,选项固定为 Yes/No
14
+ * - notify:往聊天发送一条普通文本消息
15
+ * - 其余编辑器/TUI 相关方法在飞书模式下为空实现或抛错
16
+ */
17
+ export declare function createFeishuUIContext(): ExtensionUIContext;
18
+ /** 根据卡片事件携带的 value,构建"权限已确认"的结果卡片 */
19
+ export declare function buildResolvedPermissionCard(value: Record<string, unknown>): import("../types.js").JsonObject;
@@ -0,0 +1,150 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { buildPermissionCard, buildPermissionResultCard } from "../cards/permission.js";
3
+ import { getFeishuContext } from "../feishu/context.js";
4
+ /**
5
+ * 全局待确认弹窗表:dialogId -> 等待中的回调。
6
+ * Pi 侧 ui.select 发起一张权限卡片后,这里登记回调;
7
+ * 用户在飞书卡片上点击按钮触发 cardAction 时,由 resolvePermissionCardAction 兑现。
8
+ */
9
+ const pendingDialogs = new Map();
10
+ /**
11
+ * 兑现一张待确认的权限卡片:
12
+ * 根据卡片按钮携带的 dialogId 找到等待中的 Promise,把用户选择传回去。
13
+ * 返回用户的选择(undefined 表示找不到对应弹窗)。
14
+ */
15
+ export function resolvePermissionCardAction(value) {
16
+ const dialogId = typeof value.dialogId === "string" ? value.dialogId : undefined;
17
+ if (!dialogId)
18
+ return undefined;
19
+ const pending = pendingDialogs.get(dialogId);
20
+ if (!pending)
21
+ return undefined;
22
+ pendingDialogs.delete(dialogId);
23
+ clearTimeout(pending.timer);
24
+ const choice = typeof value.choice === "string" ? value.choice : undefined;
25
+ pending.resolve(choice);
26
+ return choice;
27
+ }
28
+ /**
29
+ * 创建"飞书版"的 ExtensionUIContext:
30
+ * 把 Pi 的 ui.select / ui.confirm / notify 等 UI 能力桥接到飞书聊天。
31
+ *
32
+ * - select:发送一张"权限确认"卡片到当前聊天,等待用户在卡片上选择(可中止/超时)
33
+ * - confirm:复用 select,选项固定为 Yes/No
34
+ * - notify:往聊天发送一条普通文本消息
35
+ * - 其余编辑器/TUI 相关方法在飞书模式下为空实现或抛错
36
+ */
37
+ export function createFeishuUIContext() {
38
+ return {
39
+ /**
40
+ * 在飞书聊天里发起一个选择:
41
+ * 发送权限卡片,等待卡片事件 -> 用户点击 -> resolvePermissionCardAction 兑现。
42
+ * 支持 opts.signal 中止和 opts.timeout 超时。
43
+ */
44
+ async select(title, options, opts) {
45
+ const context = getFeishuContext();
46
+ // 不在飞书请求上下文中(比如 TUI 环境),直接选第一个选项兜底
47
+ if (!context)
48
+ return options[0];
49
+ const dialogId = randomUUID();
50
+ const timeout = opts?.timeout ?? 60_000;
51
+ return await new Promise((resolve) => {
52
+ // 超时兜底:到时间还没人点就放弃
53
+ const timer = setTimeout(() => {
54
+ pendingDialogs.delete(dialogId);
55
+ resolve(undefined);
56
+ }, timeout);
57
+ pendingDialogs.set(dialogId, { resolve, timer });
58
+ // 支持外部中止信号
59
+ if (opts?.signal) {
60
+ if (opts.signal.aborted) {
61
+ pendingDialogs.delete(dialogId);
62
+ clearTimeout(timer);
63
+ resolve(undefined);
64
+ return;
65
+ }
66
+ opts.signal.addEventListener("abort", () => {
67
+ pendingDialogs.delete(dialogId);
68
+ clearTimeout(timer);
69
+ resolve(undefined);
70
+ }, { once: true });
71
+ }
72
+ // 发送权限卡片到当前聊天;发送失败则直接放弃
73
+ context.channel.sendCard(context.chatId, buildPermissionCard(title, options, dialogId)).catch(() => {
74
+ pendingDialogs.delete(dialogId);
75
+ clearTimeout(timer);
76
+ resolve(undefined);
77
+ });
78
+ });
79
+ },
80
+ /** 确认弹窗:复用 select,选项固定为 Yes / No */
81
+ async confirm(title, message, opts) {
82
+ const text = message ? `${title}\n\n${message}` : title;
83
+ const choice = await this.select(text, ["Yes", "No"], opts);
84
+ return choice === "Yes";
85
+ },
86
+ /** 飞书模式无法做交互式输入,返回 undefined */
87
+ async input() {
88
+ return undefined;
89
+ },
90
+ /** 往当前聊天发送一条通知文本(error/warning 会带前缀) */
91
+ notify(message, type) {
92
+ const context = getFeishuContext();
93
+ if (!context)
94
+ return;
95
+ const prefix = type === "error" ? "Error: " : type === "warning" ? "Warning: " : "";
96
+ context.channel.sendText(context.chatId, `${prefix}${message}`).catch(() => { });
97
+ },
98
+ /* ---------- 以下为编辑器/TUI 相关方法,飞书模式下不可用 ---------- */
99
+ onTerminalInput() {
100
+ return () => { };
101
+ },
102
+ setStatus() { },
103
+ setWorkingMessage() { },
104
+ setWorkingVisible() { },
105
+ setWorkingIndicator() { },
106
+ setHiddenThinkingLabel() { },
107
+ setWidget() { },
108
+ setFooter() { },
109
+ setHeader() { },
110
+ setTitle() { },
111
+ async custom() {
112
+ throw new Error("Custom UI is not available in Feishu mode");
113
+ },
114
+ pasteToEditor() { },
115
+ setEditorText() { },
116
+ getEditorText() {
117
+ return "";
118
+ },
119
+ async editor() {
120
+ return undefined;
121
+ },
122
+ addAutocompleteProvider() { },
123
+ setEditorComponent() { },
124
+ getEditorComponent() {
125
+ return undefined;
126
+ },
127
+ get theme() {
128
+ return {};
129
+ },
130
+ getAllThemes() {
131
+ return [];
132
+ },
133
+ getTheme() {
134
+ return undefined;
135
+ },
136
+ setTheme() {
137
+ return { success: false, error: "Themes are not available in Feishu mode" };
138
+ },
139
+ getToolsExpanded() {
140
+ return false;
141
+ },
142
+ setToolsExpanded() { },
143
+ };
144
+ }
145
+ /** 根据卡片事件携带的 value,构建"权限已确认"的结果卡片 */
146
+ export function buildResolvedPermissionCard(value) {
147
+ const choice = resolvePermissionCardAction(value);
148
+ return buildPermissionResultCard(choice);
149
+ }
150
+ //# sourceMappingURL=ui-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-context.js","sourceRoot":"","sources":["../../src/bridge/ui-context.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,OAAO,EAAE,mBAAmB,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAQxD;;;;GAIG;AACH,MAAM,cAAc,GAAG,IAAI,GAAG,EAAyB,CAAC;AAExD;;;;GAIG;AACH,MAAM,UAAU,2BAA2B,CAAC,KAA8B;IACxE,MAAM,QAAQ,GAAG,OAAO,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;IACjF,IAAI,CAAC,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,CAAC,OAAO;QAAE,OAAO,SAAS,CAAC;IAC/B,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAChC,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC5B,MAAM,MAAM,GAAG,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;IAC3E,OAAO,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACxB,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB;IACnC,OAAO;QACL;;;;WAIG;QACH,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI;YAC/B,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;YACnC,mCAAmC;YACnC,IAAI,CAAC,OAAO;gBAAE,OAAO,OAAO,CAAC,CAAC,CAAC,CAAC;YAChC,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAC;YAC9B,MAAM,OAAO,GAAG,IAAI,EAAE,OAAO,IAAI,MAAM,CAAC;YAExC,OAAO,MAAM,IAAI,OAAO,CAAqB,CAAC,OAAO,EAAE,EAAE;gBACvD,kBAAkB;gBAClB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE;oBAC5B,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAChC,OAAO,CAAC,SAAS,CAAC,CAAC;gBACrB,CAAC,EAAE,OAAO,CAAC,CAAC;gBAEZ,cAAc,CAAC,GAAG,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;gBAEjD,WAAW;gBACX,IAAI,IAAI,EAAE,MAAM,EAAE,CAAC;oBACjB,IAAI,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;wBACxB,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;wBAChC,YAAY,CAAC,KAAK,CAAC,CAAC;wBACpB,OAAO,CAAC,SAAS,CAAC,CAAC;wBACnB,OAAO;oBACT,CAAC;oBACD,IAAI,CAAC,MAAM,CAAC,gBAAgB,CAC1B,OAAO,EACP,GAAG,EAAE;wBACH,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;wBAChC,YAAY,CAAC,KAAK,CAAC,CAAC;wBACpB,OAAO,CAAC,SAAS,CAAC,CAAC;oBACrB,CAAC,EACD,EAAE,IAAI,EAAE,IAAI,EAAE,CACf,CAAC;gBACJ,CAAC;gBAED,wBAAwB;gBACxB,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE;oBACjG,cAAc,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;oBAChC,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,SAAS,CAAC,CAAC;gBACrB,CAAC,CAAC,CAAC;YACL,CAAC,CAAC,CAAC;QACL,CAAC;QAED,oCAAoC;QACpC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI;YAChC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,KAAK,OAAO,OAAO,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC;YACxD,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,CAAC;YAC5D,OAAO,MAAM,KAAK,KAAK,CAAC;QAC1B,CAAC;QAED,gCAAgC;QAChC,KAAK,CAAC,KAAK;YACT,OAAO,SAAS,CAAC;QACnB,CAAC;QAED,wCAAwC;QACxC,MAAM,CAAC,OAAO,EAAE,IAAI;YAClB,MAAM,OAAO,GAAG,gBAAgB,EAAE,CAAC;YACnC,IAAI,CAAC,OAAO;gBAAE,OAAO;YACrB,MAAM,MAAM,GAAG,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;YACpF,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,MAAM,GAAG,OAAO,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAE,CAAC,CAAC,CAAC;QAClF,CAAC;QAED,oDAAoD;QACpD,eAAe;YACb,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;QAClB,CAAC;QACD,SAAS,KAAI,CAAC;QACd,iBAAiB,KAAI,CAAC;QACtB,iBAAiB,KAAI,CAAC;QACtB,mBAAmB,KAAI,CAAC;QACxB,sBAAsB,KAAI,CAAC;QAC3B,SAAS,KAAI,CAAC;QACd,SAAS,KAAI,CAAC;QACd,SAAS,KAAI,CAAC;QACd,QAAQ,KAAI,CAAC;QACb,KAAK,CAAC,MAAM;YACV,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,aAAa,KAAI,CAAC;QAClB,aAAa,KAAI,CAAC;QAClB,aAAa;YACX,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,KAAK,CAAC,MAAM;YACV,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,uBAAuB,KAAI,CAAC;QAC5B,kBAAkB,KAAI,CAAC;QACvB,kBAAkB;YAChB,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,IAAI,KAAK;YACP,OAAO,EAAiC,CAAC;QAC3C,CAAC;QACD,YAAY;YACV,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,QAAQ;YACN,OAAO,SAAS,CAAC;QACnB,CAAC;QACD,QAAQ;YACN,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,yCAAyC,EAAE,CAAC;QAC9E,CAAC;QACD,gBAAgB;YACd,OAAO,KAAK,CAAC;QACf,CAAC;QACD,gBAAgB,KAAI,CAAC;KACtB,CAAC;AACJ,CAAC;AAED,qCAAqC;AACrC,MAAM,UAAU,2BAA2B,CAAC,KAA8B;IACxE,MAAM,MAAM,GAAG,2BAA2B,CAAC,KAAK,CAAC,CAAC;IAClD,OAAO,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,33 @@
1
+ import type { JsonObject } from "../types.js";
2
+ /**
3
+ * ============================================================
4
+ * 飞书卡片构建公共组件
5
+ * ------------------------------------------------------------
6
+ * 提供一组小函数,用于按飞书卡片 JSON 结构组装:
7
+ * 文本块、注释块、按钮、按钮组、下拉菜单、整张卡片。
8
+ * 各 cards/*.ts 都基于这些函数构建具体卡片。
9
+ * ============================================================
10
+ */
11
+ /** 卡片模板(决定头部颜色):蓝 / 绿 / 红 / 黄 / 灰 */
12
+ export type CardTemplate = "blue" | "green" | "red" | "yellow" | "grey";
13
+ /** 普通文本块(支持 Markdown,tag: lark_md) */
14
+ export declare function textBlock(content: string): JsonObject;
15
+ /** 灰色注释文本块(通常用于辅助信息) */
16
+ export declare function noteBlock(content: string): JsonObject;
17
+ /**
18
+ * 按钮:点击后飞书会触发 cardAction 事件,value 字段原样带到事件里。
19
+ * value 里通常放 cmd(命令名)+ 相关参数(sessionKey、dialogId 等)。
20
+ */
21
+ export declare function button(text: string, value: JsonObject, type?: "primary" | "default" | "danger"): JsonObject;
22
+ /** 按钮组:把多个按钮排成一行 */
23
+ export declare function actions(elements: JsonObject[]): JsonObject;
24
+ /**
25
+ * 静态下拉菜单:value 同样会随 cardAction 事件带回。
26
+ * 第一个选项会设为 initial_option(默认选中)。
27
+ */
28
+ export declare function selectMenu(placeholder: string, value: JsonObject, options: Array<{
29
+ text: string;
30
+ value: string;
31
+ }>): JsonObject;
32
+ /** 组装一张完整的飞书卡片:头部(标题+颜色模板)+ 元素列表 */
33
+ export declare function card(title: string, template: CardTemplate, elements: JsonObject[]): JsonObject;
@@ -0,0 +1,84 @@
1
+ /** 普通文本块(支持 Markdown,tag: lark_md) */
2
+ export function textBlock(content) {
3
+ return {
4
+ tag: "div",
5
+ text: {
6
+ tag: "lark_md",
7
+ content,
8
+ },
9
+ };
10
+ }
11
+ /** 灰色注释文本块(通常用于辅助信息) */
12
+ export function noteBlock(content) {
13
+ return textBlock(`<font color="grey">${content}</font>`);
14
+ }
15
+ /**
16
+ * 按钮:点击后飞书会触发 cardAction 事件,value 字段原样带到事件里。
17
+ * value 里通常放 cmd(命令名)+ 相关参数(sessionKey、dialogId 等)。
18
+ */
19
+ export function button(text, value, type = "default") {
20
+ return {
21
+ tag: "button",
22
+ text: {
23
+ tag: "plain_text",
24
+ content: text,
25
+ },
26
+ type,
27
+ value,
28
+ };
29
+ }
30
+ /** 按钮组:把多个按钮排成一行 */
31
+ export function actions(elements) {
32
+ return {
33
+ tag: "action",
34
+ actions: elements,
35
+ };
36
+ }
37
+ /**
38
+ * 静态下拉菜单:value 同样会随 cardAction 事件带回。
39
+ * 第一个选项会设为 initial_option(默认选中)。
40
+ */
41
+ export function selectMenu(placeholder, value, options) {
42
+ const initialOption = options[0]
43
+ ? {
44
+ text: {
45
+ tag: "plain_text",
46
+ content: options[0].text,
47
+ },
48
+ value: options[0].value,
49
+ }
50
+ : undefined;
51
+ return {
52
+ tag: "select_static",
53
+ placeholder: {
54
+ tag: "plain_text",
55
+ content: placeholder,
56
+ },
57
+ ...(initialOption ? { initial_option: initialOption } : {}),
58
+ options: options.map((option) => ({
59
+ text: {
60
+ tag: "plain_text",
61
+ content: option.text,
62
+ },
63
+ value: option.value,
64
+ })),
65
+ value,
66
+ };
67
+ }
68
+ /** 组装一张完整的飞书卡片:头部(标题+颜色模板)+ 元素列表 */
69
+ export function card(title, template, elements) {
70
+ return {
71
+ config: {
72
+ wide_screen_mode: true,
73
+ },
74
+ header: {
75
+ title: {
76
+ tag: "plain_text",
77
+ content: title,
78
+ },
79
+ template,
80
+ },
81
+ elements: elements,
82
+ };
83
+ }
84
+ //# sourceMappingURL=common.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"common.js","sourceRoot":"","sources":["../../src/cards/common.ts"],"names":[],"mappings":"AAeA,sCAAsC;AACtC,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO;QACL,GAAG,EAAE,KAAK;QACV,IAAI,EAAE;YACJ,GAAG,EAAE,SAAS;YACd,OAAO;SACR;KACF,CAAC;AACJ,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,OAAO,SAAS,CAAC,sBAAsB,OAAO,SAAS,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,KAAiB,EAAE,OAAyC,SAAS;IACxG,OAAO;QACL,GAAG,EAAE,QAAQ;QACb,IAAI,EAAE;YACJ,GAAG,EAAE,YAAY;YACjB,OAAO,EAAE,IAAI;SACd;QACD,IAAI;QACJ,KAAK;KACN,CAAC;AACJ,CAAC;AAED,oBAAoB;AACpB,MAAM,UAAU,OAAO,CAAC,QAAsB;IAC5C,OAAO;QACL,GAAG,EAAE,QAAQ;QACb,OAAO,EAAE,QAAQ;KAClB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,WAAmB,EAAE,KAAiB,EAAE,OAA+C;IAChH,MAAM,aAAa,GAAG,OAAO,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC;YACE,IAAI,EAAE;gBACJ,GAAG,EAAE,YAAY;gBACjB,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI;aACzB;YACD,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK;SACxB;QACH,CAAC,CAAC,SAAS,CAAC;IAEd,OAAO;QACL,GAAG,EAAE,eAAe;QACpB,WAAW,EAAE;YACX,GAAG,EAAE,YAAY;YACjB,OAAO,EAAE,WAAW;SACrB;QACD,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC3D,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;YAChC,IAAI,EAAE;gBACJ,GAAG,EAAE,YAAY;gBACjB,OAAO,EAAE,MAAM,CAAC,IAAI;aACrB;YACD,KAAK,EAAE,MAAM,CAAC,KAAK;SACpB,CAAC,CAAC;QACH,KAAK;KACN,CAAC;AACJ,CAAC;AAED,oCAAoC;AACpC,MAAM,UAAU,IAAI,CAAC,KAAa,EAAE,QAAsB,EAAE,QAAsB;IAChF,OAAO;QACL,MAAM,EAAE;YACN,gBAAgB,EAAE,IAAI;SACvB;QACD,MAAM,EAAE;YACN,KAAK,EAAE;gBACL,GAAG,EAAE,YAAY;gBACjB,OAAO,EAAE,KAAK;aACf;YACD,QAAQ;SACT;QACD,QAAQ,EAAE,QAAuB;KAClC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { JsonObject } from "../types.js";
2
+ /**
3
+ * 帮助卡片(/help 命令触发):
4
+ * 列出常用命令、文件能力,以及当前聊天类型/会话模式的说明。
5
+ */
6
+ export declare function buildHelpCard(options: {
7
+ botName: string;
8
+ chatType: "private" | "group";
9
+ sessionMode: string;
10
+ requireMention: boolean;
11
+ }): JsonObject;
@@ -0,0 +1,31 @@
1
+ import { card, noteBlock, textBlock } from "./common.js";
2
+ /**
3
+ * 帮助卡片(/help 命令触发):
4
+ * 列出常用命令、文件能力,以及当前聊天类型/会话模式的说明。
5
+ */
6
+ export function buildHelpCard(options) {
7
+ return card(`${options.botName} 使用帮助`, "blue", [
8
+ textBlock([
9
+ "**常用命令**",
10
+ "`/help` 查看帮助",
11
+ "`/sessions` 管理当前会话",
12
+ "`/models` 选择模型和思考等级",
13
+ "`/new` 新建 Pi 会话",
14
+ "`/stop` 停止当前生成",
15
+ "`/reset` 重置当前映射",
16
+ "`/status` 查看运行状态",
17
+ ].join("\n")),
18
+ textBlock([
19
+ "**文件能力**",
20
+ "你让我生成文件时,Pi 可以通过 `send_file_to_chat` 把本地结果发回当前飞书聊天。",
21
+ ].join("\n")),
22
+ noteBlock([
23
+ `聊天类型:${options.chatType === "group" ? "群聊" : "私聊"}`,
24
+ `会话模式:${options.sessionMode}`,
25
+ options.chatType === "group" && options.requireMention ? "群聊默认需要 @机器人 才会响应。" : "",
26
+ ]
27
+ .filter((line) => line.length > 0)
28
+ .join("\n")),
29
+ ]);
30
+ }
31
+ //# sourceMappingURL=help.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/cards/help.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAEzD;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,OAK7B;IACC,OAAO,IAAI,CAAC,GAAG,OAAO,CAAC,OAAO,OAAO,EAAE,MAAM,EAAE;QAC7C,SAAS,CACP;YACE,UAAU;YACV,cAAc;YACd,oBAAoB;YACpB,qBAAqB;YACrB,iBAAiB;YACjB,gBAAgB;YAChB,iBAAiB;YACjB,kBAAkB;SACnB,CAAC,IAAI,CAAC,IAAI,CAAC,CACb;QACD,SAAS,CACP;YACE,UAAU;YACV,qDAAqD;SACtD,CAAC,IAAI,CAAC,IAAI,CAAC,CACb;QACD,SAAS,CACP;YACE,QAAQ,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE;YACpD,QAAQ,OAAO,CAAC,WAAW,EAAE;YAC7B,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,mBAAmB,CAAC,CAAC,CAAC,EAAE;SAClF;aACE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;aACjC,IAAI,CAAC,IAAI,CAAC,CACd;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { JsonObject } from "../types.js";
2
+ /** 模型卡片的单条模型信息(展示 + 切换用) */
3
+ export interface ModelCardEntry {
4
+ provider: string;
5
+ id: string;
6
+ name?: string;
7
+ /** 是否支持 thinking */
8
+ thinking?: boolean;
9
+ }
10
+ /**
11
+ * 模型选择卡片(/models 命令触发):
12
+ * 展示当前模型,并列出可用模型,每个模型配"使用"(普通)和"高思考"(high thinking)两个按钮。
13
+ * 按钮点击后由 card-actions 的 cmd: "model" 分支处理。
14
+ */
15
+ export declare function buildModelsCard(options: {
16
+ sessionKey: string;
17
+ current?: {
18
+ provider: string;
19
+ id: string;
20
+ thinkingLevel?: string;
21
+ };
22
+ models: ModelCardEntry[];
23
+ sessionMode: string;
24
+ }): JsonObject;
@@ -0,0 +1,46 @@
1
+ import { actions, button, card, noteBlock, textBlock } from "./common.js";
2
+ /**
3
+ * 模型选择卡片(/models 命令触发):
4
+ * 展示当前模型,并列出可用模型,每个模型配"使用"(普通)和"高思考"(high thinking)两个按钮。
5
+ * 按钮点击后由 card-actions 的 cmd: "model" 分支处理。
6
+ */
7
+ export function buildModelsCard(options) {
8
+ const current = options.current
9
+ ? `${options.current.provider}/${options.current.id} (${options.current.thinkingLevel ?? "default"})`
10
+ : "未选择";
11
+ const elements = [
12
+ textBlock([`**当前模型**`, `\`${current}\``, `会话:\`${options.sessionKey}\``, `模式:${options.sessionMode}`].join("\n")),
13
+ ];
14
+ // 没有可用模型时的提示
15
+ if (options.models.length === 0) {
16
+ elements.push(noteBlock("当前没有可用模型。请先检查 Pi 的模型配置。"));
17
+ }
18
+ // 最多展示 20 个模型
19
+ for (const model of options.models.slice(0, 20)) {
20
+ const label = model.name ?? model.id;
21
+ elements.push(textBlock([`**${label}**`, `\`${model.provider}/${model.id}\``, model.thinking ? "支持 thinking" : ""].filter(Boolean).join("\n")));
22
+ elements.push(actions([
23
+ button("使用", {
24
+ cmd: "model",
25
+ action: "select",
26
+ sessionKey: options.sessionKey,
27
+ provider: model.provider,
28
+ modelId: model.id,
29
+ thinkingLevel: "off",
30
+ }, "primary"),
31
+ button("高思考", {
32
+ cmd: "model",
33
+ action: "select",
34
+ sessionKey: options.sessionKey,
35
+ provider: model.provider,
36
+ modelId: model.id,
37
+ thinkingLevel: "high",
38
+ }),
39
+ ]));
40
+ }
41
+ if (options.models.length > 20) {
42
+ elements.push(noteBlock(`只展示前 20 个模型,共 ${options.models.length} 个。`));
43
+ }
44
+ return card("Pi 模型选择", "blue", elements);
45
+ }
46
+ //# sourceMappingURL=models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/cards/models.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAW1E;;;;GAIG;AACH,MAAM,UAAU,eAAe,CAAC,OAK/B;IACC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO;QAC7B,CAAC,CAAC,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,aAAa,IAAI,SAAS,GAAG;QACrG,CAAC,CAAC,KAAK,CAAC;IACV,MAAM,QAAQ,GAAiB;QAC7B,SAAS,CAAC,CAAC,UAAU,EAAE,KAAK,OAAO,IAAI,EAAE,QAAQ,OAAO,CAAC,UAAU,IAAI,EAAE,MAAM,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;KAClH,CAAC;IAEF,aAAa;IACb,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC,CAAC;IACtD,CAAC;IAED,cAAc;IACd,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;QAChD,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;QACrC,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,KAAK,IAAI,EAAE,KAAK,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAChJ,QAAQ,CAAC,IAAI,CACX,OAAO,CAAC;YACN,MAAM,CACJ,IAAI,EACJ;gBACE,GAAG,EAAE,OAAO;gBACZ,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,aAAa,EAAE,KAAK;aACrB,EACD,SAAS,CACV;YACD,MAAM,CACJ,KAAK,EACL;gBACE,GAAG,EAAE,OAAO;gBACZ,MAAM,EAAE,QAAQ;gBAChB,UAAU,EAAE,OAAO,CAAC,UAAU;gBAC9B,QAAQ,EAAE,KAAK,CAAC,QAAQ;gBACxB,OAAO,EAAE,KAAK,CAAC,EAAE;gBACjB,aAAa,EAAE,MAAM;aACtB,CACF;SACF,CAAC,CACH,CAAC;IACJ,CAAC;IAED,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,EAAE,EAAE,CAAC;QAC/B,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,OAAO,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC;IACxE,CAAC;IAED,OAAO,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { JsonObject } from "../types.js";
2
+ /**
3
+ * 权限确认卡片:Pi 的 ui.select 在飞书上的呈现。
4
+ * 每个选项生成一个按钮,按钮 value 携带 dialogId 和该选项,
5
+ * 用户点击后由 card-actions 的 cmd: "permission" 分支兑现等待中的 Promise。
6
+ */
7
+ export declare function buildPermissionCard(title: string, options: string[], dialogId: string): JsonObject;
8
+ /** 权限确认完成后的结果卡片(展示用户选择了什么) */
9
+ export declare function buildPermissionResultCard(choice: string | undefined): JsonObject;
@@ -0,0 +1,17 @@
1
+ import { actions, button, card, textBlock } from "./common.js";
2
+ /**
3
+ * 权限确认卡片:Pi 的 ui.select 在飞书上的呈现。
4
+ * 每个选项生成一个按钮,按钮 value 携带 dialogId 和该选项,
5
+ * 用户点击后由 card-actions 的 cmd: "permission" 分支兑现等待中的 Promise。
6
+ */
7
+ export function buildPermissionCard(title, options, dialogId) {
8
+ return card("Permission required", "red", [
9
+ textBlock(title),
10
+ actions(options.map((option, index) => button(option, { cmd: "permission", dialogId, choice: option }, index === 0 ? "primary" : "default"))),
11
+ ]);
12
+ }
13
+ /** 权限确认完成后的结果卡片(展示用户选择了什么) */
14
+ export function buildPermissionResultCard(choice) {
15
+ return card("Permission resolved", "grey", [textBlock(choice ? `Selected: **${choice}**` : "No selection.")]);
16
+ }
17
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/cards/permission.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE/D;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CAAC,KAAa,EAAE,OAAiB,EAAE,QAAgB;IACpF,OAAO,IAAI,CAAC,qBAAqB,EAAE,KAAK,EAAE;QACxC,SAAS,CAAC,KAAK,CAAC;QAChB,OAAO,CACL,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAC5B,MAAM,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CACrG,CACF;KACF,CAAC,CAAC;AACL,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,yBAAyB,CAAC,MAA0B;IAClE,OAAO,IAAI,CAAC,qBAAqB,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,eAAe,MAAM,IAAI,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC;AAChH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { JsonObject, SessionMapping } from "../types.js";
2
+ /**
3
+ * 会话管理卡片(/sessions 命令触发):
4
+ * 展示"新建会话"按钮,以及最多 10 条持久化的历史会话;
5
+ * 每个历史会话有"切换"和"删除"按钮,点击后由 card-actions 的 cmd: "session" 分支处理。
6
+ */
7
+ export declare function buildSessionsCard(options: {
8
+ currentSessionKey: string;
9
+ mappings: SessionMapping[];
10
+ sessionMode: string;
11
+ }): JsonObject;
@@ -0,0 +1,54 @@
1
+ import { actions, button, card, noteBlock, textBlock } from "./common.js";
2
+ /** 聊天类型中文标签 */
3
+ function chatTypeLabel(chatType) {
4
+ return chatType === "group" ? "群聊" : "私聊";
5
+ }
6
+ /** 截断过长的会话文件路径,避免卡片内容溢出 */
7
+ function compactPath(path) {
8
+ return path.length > 72 ? `...${path.slice(-69)}` : path;
9
+ }
10
+ /**
11
+ * 会话管理卡片(/sessions 命令触发):
12
+ * 展示"新建会话"按钮,以及最多 10 条持久化的历史会话;
13
+ * 每个历史会话有"切换"和"删除"按钮,点击后由 card-actions 的 cmd: "session" 分支处理。
14
+ */
15
+ export function buildSessionsCard(options) {
16
+ const elements = [
17
+ textBlock([`**当前会话**`, `\`${options.currentSessionKey}\``, `模式:${options.sessionMode}`].join("\n")),
18
+ actions([button("新建会话", { cmd: "session", action: "new", sessionKey: options.currentSessionKey }, "primary")]),
19
+ ];
20
+ const visibleMappings = options.mappings.slice(0, 10);
21
+ if (visibleMappings.length === 0) {
22
+ elements.push(noteBlock("当前还没有持久化的历史会话。"));
23
+ }
24
+ for (const mapping of visibleMappings) {
25
+ const currentMark = mapping.sessionKey === options.currentSessionKey ? "当前" : "可切换";
26
+ elements.push(textBlock([
27
+ `**${chatTypeLabel(mapping.chatType)} · ${currentMark}**`,
28
+ `\`${mapping.sessionKey}\``,
29
+ `更新时间:${mapping.updatedAt}`,
30
+ `文件:${compactPath(mapping.sessionFile)}`,
31
+ ].join("\n")));
32
+ elements.push(actions([
33
+ // 切换:把"当前会话 key"的运行时切换到目标会话文件
34
+ button("切换", {
35
+ cmd: "session",
36
+ action: "switch",
37
+ sessionKey: options.currentSessionKey,
38
+ targetSessionKey: mapping.sessionKey,
39
+ }),
40
+ // 删除:删除目标会话映射(并释放运行时)
41
+ button("删除", {
42
+ cmd: "session",
43
+ action: "delete",
44
+ sessionKey: options.currentSessionKey,
45
+ targetSessionKey: mapping.sessionKey,
46
+ }, "danger"),
47
+ ]));
48
+ }
49
+ if (options.mappings.length > visibleMappings.length) {
50
+ elements.push(noteBlock(`还有 ${options.mappings.length - visibleMappings.length} 个会话未展示。`));
51
+ }
52
+ return card("Pi 会话管理", "blue", elements);
53
+ }
54
+ //# sourceMappingURL=sessions.js.map