codex-weixin 0.2.1

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 (93) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/LICENSE +21 -0
  3. package/NOTICE +5 -0
  4. package/README.en.md +200 -0
  5. package/README.md +210 -0
  6. package/dist/bridge/access.d.ts +21 -0
  7. package/dist/bridge/access.js +31 -0
  8. package/dist/bridge/access.js.map +1 -0
  9. package/dist/bridge/actions.d.ts +21 -0
  10. package/dist/bridge/actions.js +120 -0
  11. package/dist/bridge/actions.js.map +1 -0
  12. package/dist/bridge/errors.d.ts +1 -0
  13. package/dist/bridge/errors.js +15 -0
  14. package/dist/bridge/errors.js.map +1 -0
  15. package/dist/bridge/format.d.ts +14 -0
  16. package/dist/bridge/format.js +62 -0
  17. package/dist/bridge/format.js.map +1 -0
  18. package/dist/bridge/prompt-buffer.d.ts +45 -0
  19. package/dist/bridge/prompt-buffer.js +56 -0
  20. package/dist/bridge/prompt-buffer.js.map +1 -0
  21. package/dist/bridge/service.d.ts +38 -0
  22. package/dist/bridge/service.js +292 -0
  23. package/dist/bridge/service.js.map +1 -0
  24. package/dist/codex/app-server-runner.d.ts +74 -0
  25. package/dist/codex/app-server-runner.js +514 -0
  26. package/dist/codex/app-server-runner.js.map +1 -0
  27. package/dist/codex/exec-runner.d.ts +36 -0
  28. package/dist/codex/exec-runner.js +153 -0
  29. package/dist/codex/exec-runner.js.map +1 -0
  30. package/dist/codex/runner.d.ts +22 -0
  31. package/dist/codex/runner.js +57 -0
  32. package/dist/codex/runner.js.map +1 -0
  33. package/dist/codex/sandbox.d.ts +3 -0
  34. package/dist/codex/sandbox.js +15 -0
  35. package/dist/codex/sandbox.js.map +1 -0
  36. package/dist/index.d.ts +15 -0
  37. package/dist/index.js +16 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/server/account-manager.d.ts +94 -0
  40. package/dist/server/account-manager.js +468 -0
  41. package/dist/server/account-manager.js.map +1 -0
  42. package/dist/server/arguments.d.ts +3 -0
  43. package/dist/server/arguments.js +25 -0
  44. package/dist/server/arguments.js.map +1 -0
  45. package/dist/server/http-server.d.ts +23 -0
  46. package/dist/server/http-server.js +481 -0
  47. package/dist/server/http-server.js.map +1 -0
  48. package/dist/server/index.d.ts +2 -0
  49. package/dist/server/index.js +69 -0
  50. package/dist/server/index.js.map +1 -0
  51. package/dist/server/login-manager.d.ts +27 -0
  52. package/dist/server/login-manager.js +49 -0
  53. package/dist/server/login-manager.js.map +1 -0
  54. package/dist/server/process-lock.d.ts +5 -0
  55. package/dist/server/process-lock.js +63 -0
  56. package/dist/server/process-lock.js.map +1 -0
  57. package/dist/state/config.d.ts +19 -0
  58. package/dist/state/config.js +41 -0
  59. package/dist/state/config.js.map +1 -0
  60. package/dist/state/json-store.d.ts +3 -0
  61. package/dist/state/json-store.js +23 -0
  62. package/dist/state/json-store.js.map +1 -0
  63. package/dist/state/paths.d.ts +12 -0
  64. package/dist/state/paths.js +28 -0
  65. package/dist/state/paths.js.map +1 -0
  66. package/dist/state/runtime-state.d.ts +57 -0
  67. package/dist/state/runtime-state.js +212 -0
  68. package/dist/state/runtime-state.js.map +1 -0
  69. package/dist/web/app.js +958 -0
  70. package/dist/web/index.html +273 -0
  71. package/dist/web/styles.css +324 -0
  72. package/dist/web/vendor/lucide.min.js +12 -0
  73. package/dist/web/vendor/marked.umd.js +79 -0
  74. package/dist/web/vendor/purify.min.js +3 -0
  75. package/dist/weixin/accounts.d.ts +22 -0
  76. package/dist/weixin/accounts.js +66 -0
  77. package/dist/weixin/accounts.js.map +1 -0
  78. package/dist/weixin/api.d.ts +75 -0
  79. package/dist/weixin/api.js +191 -0
  80. package/dist/weixin/api.js.map +1 -0
  81. package/dist/weixin/login.d.ts +29 -0
  82. package/dist/weixin/login.js +100 -0
  83. package/dist/weixin/login.js.map +1 -0
  84. package/dist/weixin/media.d.ts +32 -0
  85. package/dist/weixin/media.js +266 -0
  86. package/dist/weixin/media.js.map +1 -0
  87. package/dist/weixin/messages.d.ts +24 -0
  88. package/dist/weixin/messages.js +87 -0
  89. package/dist/weixin/messages.js.map +1 -0
  90. package/dist/weixin/monitor.d.ts +22 -0
  91. package/dist/weixin/monitor.js +110 -0
  92. package/dist/weixin/monitor.js.map +1 -0
  93. package/package.json +61 -0
@@ -0,0 +1,120 @@
1
+ import path from "node:path";
2
+ import { fileURLToPath } from "node:url";
3
+ import { inferMediaKind } from "../weixin/media.js";
4
+ const ACTION_BLOCK_RE = /```codex-weixin(?:-server)?-actions\s*([\s\S]*?)```/gi;
5
+ const MARKDOWN_LINK_RE = /(!)?\[[^\]]*]\(([^)]+)\)/g;
6
+ function isAbsoluteLocalPath(value) {
7
+ return path.isAbsolute(value) || /^[a-zA-Z]:[\\/]/.test(value) || /^\\\\/.test(value);
8
+ }
9
+ function assertAbsolutePath(value, label) {
10
+ if (!isAbsoluteLocalPath(value)) {
11
+ throw new Error(`${label} must be an absolute path: ${value}`);
12
+ }
13
+ }
14
+ function normalizeSendAction(raw) {
15
+ if (!raw || typeof raw !== "object") {
16
+ throw new Error("send action must be an object");
17
+ }
18
+ const candidate = raw;
19
+ if (candidate.type !== "image" && candidate.type !== "file" && candidate.type !== "video") {
20
+ throw new Error("send action type must be image, file, or video");
21
+ }
22
+ if (typeof candidate.path !== "string" || !candidate.path.trim()) {
23
+ throw new Error("send action path is required");
24
+ }
25
+ const resolved = candidate.path.trim();
26
+ assertAbsolutePath(resolved, "send action path");
27
+ return { type: candidate.type, path: resolved };
28
+ }
29
+ function normalizeLocalMarkdownTarget(raw) {
30
+ let target = raw.trim();
31
+ if (target.startsWith("<") && target.endsWith(">")) {
32
+ target = target.slice(1, -1).trim();
33
+ }
34
+ if (/^file:/i.test(target)) {
35
+ try {
36
+ target = fileURLToPath(target);
37
+ }
38
+ catch {
39
+ return undefined;
40
+ }
41
+ }
42
+ else {
43
+ try {
44
+ target = decodeURI(target);
45
+ }
46
+ catch {
47
+ // Keep the raw target if it contains characters decodeURI cannot parse.
48
+ }
49
+ }
50
+ return isAbsoluteLocalPath(target) ? target : undefined;
51
+ }
52
+ function appendSendAction(actions, seen, action) {
53
+ const key = action.path.toLowerCase();
54
+ if (seen.has(key)) {
55
+ return;
56
+ }
57
+ seen.add(key);
58
+ actions.send.push(action);
59
+ }
60
+ function extractLocalMarkdownLinks(text, actions) {
61
+ const seen = new Set(actions.send.map((action) => action.path.toLowerCase()));
62
+ const keptLines = [];
63
+ for (const line of text.split(/\r?\n/)) {
64
+ let containsLocalTarget = false;
65
+ for (const match of line.matchAll(MARKDOWN_LINK_RE)) {
66
+ const target = normalizeLocalMarkdownTarget(match[2]);
67
+ if (!target) {
68
+ continue;
69
+ }
70
+ containsLocalTarget = true;
71
+ appendSendAction(actions, seen, {
72
+ type: match[1] === "!" ? "image" : inferMediaKind(target),
73
+ path: target
74
+ });
75
+ }
76
+ if (!containsLocalTarget) {
77
+ keptLines.push(line);
78
+ }
79
+ }
80
+ return keptLines.join("\n").replace(/\n{3,}/g, "\n\n").trim();
81
+ }
82
+ function normalizeControlAction(raw) {
83
+ if (!raw || typeof raw !== "object") {
84
+ throw new Error("control action must be an object");
85
+ }
86
+ const candidate = raw;
87
+ if (candidate.type === "workspace.set") {
88
+ if (typeof candidate.path !== "string" || !candidate.path.trim()) {
89
+ throw new Error("workspace.set path is required");
90
+ }
91
+ const resolved = candidate.path.trim();
92
+ assertAbsolutePath(resolved, "workspace.set path");
93
+ return { type: "workspace.set", path: resolved };
94
+ }
95
+ if (candidate.type === "workspace.reset") {
96
+ return { type: "workspace.reset" };
97
+ }
98
+ if (candidate.type === "thread.reset") {
99
+ return { type: "thread.reset" };
100
+ }
101
+ throw new Error(`unsupported control action type: ${String(candidate.type)}`);
102
+ }
103
+ export function parseActionBlocks(text) {
104
+ const actions = { send: [], control: [] };
105
+ const visibleTextWithoutBlocks = text.replace(ACTION_BLOCK_RE, (_match, body) => {
106
+ const parsed = JSON.parse(String(body).trim());
107
+ if (Array.isArray(parsed.send)) {
108
+ const seen = new Set(actions.send.map((action) => action.path.toLowerCase()));
109
+ for (const action of parsed.send.map(normalizeSendAction)) {
110
+ appendSendAction(actions, seen, action);
111
+ }
112
+ }
113
+ if (Array.isArray(parsed.control)) {
114
+ actions.control.push(...parsed.control.map(normalizeControlAction));
115
+ }
116
+ return "";
117
+ }).trim();
118
+ return { visibleText: extractLocalMarkdownLinks(visibleTextWithoutBlocks, actions), actions };
119
+ }
120
+ //# sourceMappingURL=actions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actions.js","sourceRoot":"","sources":["../../src/bridge/actions.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export declare function userFacingMessageHandlingError(error: unknown): string;
@@ -0,0 +1,15 @@
1
+ export function userFacingMessageHandlingError(error) {
2
+ const message = error instanceof Error ? error.message : String(error);
3
+ if (/CreateProcessAsUserW failed:\s*1312|codexExecSandbox/i.test(message)) {
4
+ return [
5
+ "[codex-weixin] Windows Codex sandbox 启动失败。",
6
+ "可在 ~/.codex-weixin/config.json 中设置 \"codexExecSandbox\": \"danger-full-access\" 后重启。",
7
+ "该设置会让 Codex 获得本机完整访问权限,请仅在理解并接受安全风险时启用。"
8
+ ].join("\n");
9
+ }
10
+ if (/timed out|timeout/i.test(message)) {
11
+ return "[codex-weixin] 本轮任务执行时间过长,已停止处理。请拆成更小的步骤后重试。";
12
+ }
13
+ return "[codex-weixin] 本轮消息处理失败,详细错误已写入本机服务输出。";
14
+ }
15
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/bridge/errors.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,8BAA8B,CAAC,KAAc;IAC3D,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,IAAI,uDAAuD,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1E,OAAO;YACL,4CAA4C;YAC5C,sFAAsF;YACtF,yCAAyC;SAC1C,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACf,CAAC;IACD,IAAI,oBAAoB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACvC,OAAO,8CAA8C,CAAC;IACxD,CAAC;IACD,OAAO,wCAAwC,CAAC;AAClD,CAAC"}
@@ -0,0 +1,14 @@
1
+ import type { PromptBufferItem } from "./prompt-buffer.js";
2
+ export declare function buildPrompt(text: string, attachments?: PromptBufferItem[], attachmentSource?: "WeChat" | "Web"): string;
3
+ export type PromptAttachment = {
4
+ source: "WeChat" | "Web";
5
+ kind: "file" | "image" | "video" | "audio";
6
+ label: string;
7
+ path: string;
8
+ };
9
+ export declare function parsePrompt(text: string): {
10
+ text: string;
11
+ attachments: PromptAttachment[];
12
+ };
13
+ export declare function stripBridgeInstructions(text: string): string;
14
+ export declare function chunkText(text: string, limit?: number): string[];
@@ -0,0 +1,62 @@
1
+ const BRIDGE_ACTION_INSTRUCTIONS = [
2
+ "WeChat bridge rule: when you need to send a local image, video, or file to the user, do not use Markdown local file links.",
3
+ "When a WeChat attachment line includes a local path, inspect the saved local attachment with available tools before answering.",
4
+ "Use a fenced codex-weixin-actions JSON block instead, for example:",
5
+ "```codex-weixin-actions",
6
+ "{\"send\":[{\"type\":\"image\",\"path\":\"C:/absolute/path/image.png\"},{\"type\":\"video\",\"path\":\"C:/absolute/path/video.mp4\"}]}",
7
+ "```"
8
+ ].join("\n");
9
+ const LEGACY_BRIDGE_ACTION_INSTRUCTIONS = BRIDGE_ACTION_INSTRUCTIONS
10
+ .replaceAll("codex-weixin-actions", "codex-weixin-server-actions");
11
+ export function buildPrompt(text, attachments = [], attachmentSource = "WeChat") {
12
+ const lines = [BRIDGE_ACTION_INSTRUCTIONS];
13
+ if (text.trim()) {
14
+ lines.push(text.trim());
15
+ }
16
+ for (const attachment of attachments) {
17
+ if (attachment.kind === "text") {
18
+ lines.push(attachment.text);
19
+ }
20
+ else {
21
+ lines.push(`[${attachmentSource} ${attachment.kind}: ${attachment.label} saved to ${attachment.path}]\nInspect the saved local attachment before answering.`);
22
+ }
23
+ }
24
+ return lines.join("\n\n").trim();
25
+ }
26
+ export function parsePrompt(text) {
27
+ let normalized = text.trim();
28
+ for (const instructions of [BRIDGE_ACTION_INSTRUCTIONS, LEGACY_BRIDGE_ACTION_INSTRUCTIONS]) {
29
+ if (normalized.startsWith(instructions)) {
30
+ normalized = normalized.slice(instructions.length).trim();
31
+ break;
32
+ }
33
+ }
34
+ const attachments = [];
35
+ const visibleText = normalized.replace(/^\[(WeChat|Web) (file|image|video|audio): (.+) saved to (.+)]\nInspect the saved local attachment before answering\.$/gm, (_match, source, kind, label, filePath) => {
36
+ attachments.push({ source, kind, label, path: filePath });
37
+ return "";
38
+ }).replace(/\n{3,}/g, "\n\n").trim();
39
+ return { text: visibleText, attachments };
40
+ }
41
+ export function stripBridgeInstructions(text) {
42
+ return parsePrompt(text).text;
43
+ }
44
+ export function chunkText(text, limit = 1800) {
45
+ const normalized = text || "(empty reply)";
46
+ if (normalized.length <= limit) {
47
+ return [normalized];
48
+ }
49
+ const chunks = [];
50
+ let cursor = 0;
51
+ while (cursor < normalized.length) {
52
+ let end = Math.min(cursor + limit, normalized.length);
53
+ const newline = normalized.lastIndexOf("\n", end);
54
+ if (newline > cursor + Math.floor(limit * 0.5)) {
55
+ end = newline;
56
+ }
57
+ chunks.push(normalized.slice(cursor, end).trim());
58
+ cursor = end;
59
+ }
60
+ return chunks.filter(Boolean);
61
+ }
62
+ //# sourceMappingURL=format.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/bridge/format.ts"],"names":[],"mappings":"AAEA,MAAM,0BAA0B,GAAG;IACjC,4HAA4H;IAC5H,gIAAgI;IAChI,oEAAoE;IACpE,yBAAyB;IACzB,wIAAwI;IACxI,KAAK;CACN,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACb,MAAM,iCAAiC,GAAG,0BAA0B;KACjE,UAAU,CAAC,sBAAsB,EAAE,6BAA6B,CAAC,CAAC;AAErE,MAAM,UAAU,WAAW,CACzB,IAAY,EACZ,cAAkC,EAAE,EACpC,mBAAqC,QAAQ;IAE7C,MAAM,KAAK,GAAa,CAAC,0BAA0B,CAAC,CAAC;IACrD,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAC1B,CAAC;IACD,KAAK,MAAM,UAAU,IAAI,WAAW,EAAE,CAAC;QACrC,IAAI,UAAU,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC9B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,IAAI,UAAU,CAAC,IAAI,KAAK,UAAU,CAAC,KAAK,aAAa,UAAU,CAAC,IAAI,yDAAyD,CAAC,CAAC;QAChK,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AACnC,CAAC;AASD,MAAM,UAAU,WAAW,CAAC,IAAY;IACtC,IAAI,UAAU,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;IAC7B,KAAK,MAAM,YAAY,IAAI,CAAC,0BAA0B,EAAE,iCAAiC,CAAC,EAAE,CAAC;QAC3F,IAAI,UAAU,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YACxC,UAAU,GAAG,UAAU,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;YAC1D,MAAM;QACR,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,WAAW,GAAG,UAAU,CAAC,OAAO,CACpC,yHAAyH,EACzH,CAAC,MAAM,EAAE,MAAkC,EAAE,IAA8B,EAAE,KAAa,EAAE,QAAgB,EAAE,EAAE;QAC9G,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC1D,OAAO,EAAE,CAAC;IACZ,CAAC,CACF,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;AAC5C,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,OAAO,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,KAAK,GAAG,IAAI;IAClD,MAAM,UAAU,GAAG,IAAI,IAAI,eAAe,CAAC;IAC3C,IAAI,UAAU,CAAC,MAAM,IAAI,KAAK,EAAE,CAAC;QAC/B,OAAO,CAAC,UAAU,CAAC,CAAC;IACtB,CAAC;IACD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,MAAM,GAAG,CAAC,CAAC;IACf,OAAO,MAAM,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC;QAClC,IAAI,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,MAAM,GAAG,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAClD,IAAI,OAAO,GAAG,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,EAAE,CAAC;YAC/C,GAAG,GAAG,OAAO,CAAC;QAChB,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;AAChC,CAAC"}
@@ -0,0 +1,45 @@
1
+ export type PromptBufferItem = {
2
+ kind: "text";
3
+ text: string;
4
+ } | {
5
+ kind: "file";
6
+ path: string;
7
+ label: string;
8
+ } | {
9
+ kind: "image";
10
+ path: string;
11
+ label: string;
12
+ } | {
13
+ kind: "video";
14
+ path: string;
15
+ label: string;
16
+ } | {
17
+ kind: "audio";
18
+ path: string;
19
+ label: string;
20
+ };
21
+ export type PromptBufferOptions = {
22
+ maxItems: number;
23
+ ttlMs: number;
24
+ now?: () => number;
25
+ };
26
+ export declare class PromptBuffer {
27
+ private readonly options;
28
+ private readonly buffers;
29
+ private readonly now;
30
+ constructor(options: PromptBufferOptions);
31
+ start(key: string): {
32
+ status: "started" | "already-started";
33
+ };
34
+ append(key: string, item: PromptBufferItem): void;
35
+ done(key: string): {
36
+ status: "empty";
37
+ items: [];
38
+ } | {
39
+ status: "flushed";
40
+ items: PromptBufferItem[];
41
+ };
42
+ cancel(key: string): boolean;
43
+ isActive(key: string): boolean;
44
+ private expire;
45
+ }
@@ -0,0 +1,56 @@
1
+ export class PromptBuffer {
2
+ options;
3
+ buffers = new Map();
4
+ now;
5
+ constructor(options) {
6
+ this.options = options;
7
+ this.now = options.now ?? (() => Date.now());
8
+ }
9
+ start(key) {
10
+ this.expire(key);
11
+ if (this.buffers.has(key)) {
12
+ return { status: "already-started" };
13
+ }
14
+ const now = this.now();
15
+ this.buffers.set(key, { startedAt: now, updatedAt: now, items: [] });
16
+ return { status: "started" };
17
+ }
18
+ append(key, item) {
19
+ this.expire(key);
20
+ const state = this.buffers.get(key);
21
+ if (!state) {
22
+ throw new Error("prompt buffer is not active");
23
+ }
24
+ if (state.items.length >= this.options.maxItems) {
25
+ throw new Error(`too many buffered prompt items; limit is ${this.options.maxItems}`);
26
+ }
27
+ state.items.push(item);
28
+ state.updatedAt = this.now();
29
+ }
30
+ done(key) {
31
+ this.expire(key);
32
+ const state = this.buffers.get(key);
33
+ this.buffers.delete(key);
34
+ if (!state || state.items.length === 0) {
35
+ return { status: "empty", items: [] };
36
+ }
37
+ return { status: "flushed", items: state.items };
38
+ }
39
+ cancel(key) {
40
+ return this.buffers.delete(key);
41
+ }
42
+ isActive(key) {
43
+ this.expire(key);
44
+ return this.buffers.has(key);
45
+ }
46
+ expire(key) {
47
+ const state = this.buffers.get(key);
48
+ if (!state) {
49
+ return;
50
+ }
51
+ if (this.now() - state.updatedAt > this.options.ttlMs) {
52
+ this.buffers.delete(key);
53
+ }
54
+ }
55
+ }
56
+ //# sourceMappingURL=prompt-buffer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prompt-buffer.js","sourceRoot":"","sources":["../../src/bridge/prompt-buffer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import { HybridCodexRunner } from "../codex/runner.js";
2
+ import { type CodexWeixinConfig } from "../state/config.js";
3
+ import { RuntimeStateStore } from "../state/runtime-state.js";
4
+ import { WeixinApiClient, type FetchLike } from "../weixin/api.js";
5
+ import type { NormalizedWeixinMessage } from "../weixin/messages.js";
6
+ export type BridgeServiceOptions = {
7
+ config: CodexWeixinConfig;
8
+ stateStore: RuntimeStateStore;
9
+ weixin: WeixinApiClient;
10
+ runner?: HybridCodexRunner;
11
+ inboundDir?: string;
12
+ mediaFetch?: FetchLike;
13
+ onTurnStatus?: (status: {
14
+ senderId: string;
15
+ sessionId: string;
16
+ active: boolean;
17
+ }) => void;
18
+ };
19
+ export declare class BridgeService {
20
+ private readonly options;
21
+ private readonly access;
22
+ private readonly buffers;
23
+ private readonly runner;
24
+ constructor(options: BridgeServiceOptions);
25
+ handleMessage(message: NormalizedWeixinMessage): Promise<void>;
26
+ private handleCommand;
27
+ private bindWorkspace;
28
+ private handlePromptCommand;
29
+ private promptItemsFromMessage;
30
+ private runCodexTurn;
31
+ private sendLocalMedia;
32
+ private withTyping;
33
+ private statusText;
34
+ private reply;
35
+ allowSender(senderId: string): void;
36
+ removeSender(senderId: string): void;
37
+ listAllowedSenders(): string[];
38
+ }