feishu-codex-console 1.0.0-beta.6

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 (113) hide show
  1. package/.env.example +101 -0
  2. package/.feishu-codex-policy.example.json +11 -0
  3. package/.feishu-codex-runbooks.example.json +36 -0
  4. package/CHANGELOG.md +129 -0
  5. package/CODE_OF_CONDUCT.md +7 -0
  6. package/CONTRIBUTING.md +52 -0
  7. package/LICENSE +21 -0
  8. package/README.en.md +81 -0
  9. package/README.md +398 -0
  10. package/ROADMAP.md +40 -0
  11. package/SECURITY.md +53 -0
  12. package/dist/account-quota-card.js +233 -0
  13. package/dist/account-quota.js +125 -0
  14. package/dist/app-server-client.js +281 -0
  15. package/dist/card-session.js +166 -0
  16. package/dist/codex-events.js +1 -0
  17. package/dist/codex-runner.js +875 -0
  18. package/dist/config.js +198 -0
  19. package/dist/confirmation-card.js +135 -0
  20. package/dist/control-card.js +345 -0
  21. package/dist/conversation-turn-session.js +209 -0
  22. package/dist/data-maintenance.js +71 -0
  23. package/dist/device-card.js +460 -0
  24. package/dist/device-health.js +94 -0
  25. package/dist/diagnostics.js +253 -0
  26. package/dist/doctor.js +250 -0
  27. package/dist/fallback-card-session.js +37 -0
  28. package/dist/health-file.js +75 -0
  29. package/dist/index.js +4330 -0
  30. package/dist/lark-cli.js +558 -0
  31. package/dist/lark-retry.js +34 -0
  32. package/dist/maintenance.js +140 -0
  33. package/dist/model-capabilities.js +31 -0
  34. package/dist/onboarding-card.js +312 -0
  35. package/dist/permission-lease.js +22 -0
  36. package/dist/policy.js +506 -0
  37. package/dist/progress.js +267 -0
  38. package/dist/project-card.js +303 -0
  39. package/dist/project-overview-card.js +182 -0
  40. package/dist/project-overview.js +278 -0
  41. package/dist/project-policy.js +160 -0
  42. package/dist/project-registry.js +259 -0
  43. package/dist/project-status.js +45 -0
  44. package/dist/project-workspace.js +55 -0
  45. package/dist/quota-card.js +94 -0
  46. package/dist/recovery-policy.js +26 -0
  47. package/dist/redaction.js +67 -0
  48. package/dist/remote-ready.js +112 -0
  49. package/dist/response-card.js +139 -0
  50. package/dist/result-card.js +166 -0
  51. package/dist/review-card.js +452 -0
  52. package/dist/runbook-card.js +272 -0
  53. package/dist/runbooks.js +191 -0
  54. package/dist/runtime-card.js +337 -0
  55. package/dist/session-card.js +128 -0
  56. package/dist/session-naming.js +14 -0
  57. package/dist/smoke.js +28 -0
  58. package/dist/state-backup.js +302 -0
  59. package/dist/state-store.js +874 -0
  60. package/dist/task-card.js +640 -0
  61. package/dist/task-center-card.js +176 -0
  62. package/dist/task-failure.js +43 -0
  63. package/dist/task-intent.js +76 -0
  64. package/dist/task-queue.js +187 -0
  65. package/dist/task-reconciliation.js +80 -0
  66. package/dist/task-review.js +497 -0
  67. package/dist/team-card.js +275 -0
  68. package/dist/team-directory.js +54 -0
  69. package/dist/team-policy.js +93 -0
  70. package/dist/types.js +1 -0
  71. package/dist/version.js +9 -0
  72. package/dist/workspace-session.js +64 -0
  73. package/docs/ARCHITECTURE.md +54 -0
  74. package/docs/COMPATIBILITY.md +55 -0
  75. package/docs/CONFIGURATION.md +88 -0
  76. package/docs/DEMO.md +45 -0
  77. package/docs/GOOD_FIRST_ISSUES.md +23 -0
  78. package/docs/INSTALLATION.md +207 -0
  79. package/docs/OPEN_SOURCE_PRODUCT_PLAN.md +113 -0
  80. package/docs/PRODUCT_REQUIREMENTS_MAP.md +591 -0
  81. package/docs/RELEASE_CHECKLIST.md +65 -0
  82. package/docs/TEAM_DEPLOYMENT.md +35 -0
  83. package/docs/TROUBLESHOOTING.md +130 -0
  84. package/docs/V4_WORKSPACE_SESSION_FLOW.md +232 -0
  85. package/docs/requirements/D10_MAINTENANCE_AND_ECOSYSTEM.md +103 -0
  86. package/docs/requirements/D1_INSTALLATION_AND_FIRST_CONNECTION.md +479 -0
  87. package/docs/requirements/D2_DEVICE_AND_CONNECTIVITY.md +54 -0
  88. package/docs/requirements/D3_PROJECTS_AND_SESSIONS.md +107 -0
  89. package/docs/requirements/D4_REMOTE_TASK_EXECUTION.md +102 -0
  90. package/docs/requirements/D5_CODEX_NATIVE_INTERACTIONS.md +99 -0
  91. package/docs/requirements/D6_RESULTS_AND_CODE_REVIEW.md +100 -0
  92. package/docs/requirements/D7_SECURITY_GOVERNANCE.md +106 -0
  93. package/docs/requirements/D8_RELIABILITY_AND_RECOVERY.md +182 -0
  94. package/docs/requirements/D9_TEAM_COLLABORATION.md +129 -0
  95. package/package.json +76 -0
  96. package/scripts/capability-probe.mjs +113 -0
  97. package/scripts/cli.mjs +919 -0
  98. package/scripts/config-file.mjs +137 -0
  99. package/scripts/discovery-lib.mjs +78 -0
  100. package/scripts/install-card.mjs +37 -0
  101. package/scripts/install-detection.mjs +126 -0
  102. package/scripts/install-state.mjs +107 -0
  103. package/scripts/launchd.mjs +161 -0
  104. package/scripts/migrate-legacy.mjs +97 -0
  105. package/scripts/package-smoke.mjs +163 -0
  106. package/scripts/release-dist-tag.mjs +7 -0
  107. package/scripts/runbook-template.mjs +36 -0
  108. package/scripts/service-health.mjs +110 -0
  109. package/scripts/service.mjs +24 -0
  110. package/scripts/setup-lib.mjs +118 -0
  111. package/scripts/systemd.mjs +96 -0
  112. package/scripts/upgrade-lib.mjs +99 -0
  113. package/scripts/verify-release.mjs +37 -0
package/dist/policy.js ADDED
@@ -0,0 +1,506 @@
1
+ import { roleForSender } from "./team-policy.js";
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null;
4
+ }
5
+ export function parseFeishuEvent(value) {
6
+ if (!isRecord(value))
7
+ return null;
8
+ const requiredStrings = [
9
+ "event_id",
10
+ "message_id",
11
+ "chat_id",
12
+ "chat_type",
13
+ "sender_id",
14
+ "message_type",
15
+ "content",
16
+ ];
17
+ if (requiredStrings.some((key) => typeof value[key] !== "string"))
18
+ return null;
19
+ if (value.type !== "im.message.receive_v1")
20
+ return null;
21
+ if (value.chat_type !== "p2p" && value.chat_type !== "group")
22
+ return null;
23
+ const event = {
24
+ type: "im.message.receive_v1",
25
+ event_id: value.event_id,
26
+ message_id: value.message_id,
27
+ chat_id: value.chat_id,
28
+ chat_type: value.chat_type,
29
+ sender_id: value.sender_id,
30
+ message_type: value.message_type,
31
+ content: value.content,
32
+ };
33
+ if (typeof value.reply_to === "string")
34
+ event.reply_to = value.reply_to;
35
+ if (typeof value.root_id === "string")
36
+ event.root_id = value.root_id;
37
+ if (typeof value.thread_id === "string")
38
+ event.thread_id = value.thread_id;
39
+ if (typeof value.create_time === "string")
40
+ event.create_time = value.create_time;
41
+ if (typeof value.timestamp === "string")
42
+ event.timestamp = value.timestamp;
43
+ return event;
44
+ }
45
+ export function parseCardActionEvent(value) {
46
+ if (!isRecord(value) || value.type !== "card.action.trigger")
47
+ return null;
48
+ const requiredStrings = [
49
+ "event_id",
50
+ "operator_id",
51
+ "chat_id",
52
+ "message_id",
53
+ "token",
54
+ "action_value",
55
+ ];
56
+ if (requiredStrings.some((key) => typeof value[key] !== "string"))
57
+ return null;
58
+ const event = {
59
+ type: "card.action.trigger",
60
+ event_id: value.event_id,
61
+ operator_id: value.operator_id,
62
+ chat_id: value.chat_id,
63
+ message_id: value.message_id,
64
+ token: value.token,
65
+ action_value: value.action_value,
66
+ card_content: typeof value.card_content === "string" ? value.card_content : "",
67
+ };
68
+ if (typeof value.action_tag === "string")
69
+ event.action_tag = value.action_tag;
70
+ if (typeof value.action_name === "string")
71
+ event.action_name = value.action_name;
72
+ if (typeof value.option === "string")
73
+ event.option = value.option;
74
+ if (typeof value.timestamp === "string")
75
+ event.timestamp = value.timestamp;
76
+ return event;
77
+ }
78
+ export function isAuthorizedCardAction(event, config) {
79
+ if (!roleForSender(config, event.operator_id))
80
+ return false;
81
+ return config.allowedChatIds.size === 0 || config.allowedChatIds.has(event.chat_id);
82
+ }
83
+ export function isAuthorized(event, config) {
84
+ if (!roleForSender(config, event.sender_id))
85
+ return false;
86
+ if (config.sandboxMode === "danger-full-access" &&
87
+ event.chat_type === "group" &&
88
+ !config.allowedChatIds.has(event.chat_id)) {
89
+ return false;
90
+ }
91
+ return config.allowedChatIds.size === 0 || config.allowedChatIds.has(event.chat_id);
92
+ }
93
+ export function isTextualMessageType(messageType) {
94
+ return messageType === "text" || messageType === "post";
95
+ }
96
+ export function isAttachmentMessageType(messageType) {
97
+ return ["image", "file", "audio", "media", "video"].includes(messageType);
98
+ }
99
+ export function externalActionsForPrompt(prompt) {
100
+ const actions = [];
101
+ const commitPrompt = prompt.replace(/(?:不要|无需|不用|不需要|禁止|别)\s*(?:执行\s*)?(?:git\s+commit|commit|提交代码|代码提交|git\s*提交)/gi, "");
102
+ const pushPrompt = prompt.replace(/(?:不要|无需|不用|不需要|禁止|别)\s*(?:执行\s*)?(?:git\s+push|push|推送(?:代码|远端|分支)?)/gi, "");
103
+ const deployPrompt = prompt.replace(/(?:不要|无需|不用|不需要|禁止|别)\s*(?:执行\s*)?(?:deploy|release|publish|部署|上线|发布)/gi, "");
104
+ const pullRequestPrompt = prompt.replace(/(?:不要|无需|不用|不需要|禁止|别)\s*(?:执行\s*)?(?:创建|新建|发起|合并)?\s*(?:pull\s*request|merge\s*request|pr|mr|拉取请求|合并请求)/gi, "");
105
+ if (/\bgit\s+commit\b|\bcommit\b|提交代码|代码提交|git\s*提交/i.test(commitPrompt)) {
106
+ actions.push("commit");
107
+ }
108
+ if (/\bgit\s+push\b|\bpush\b|推送(?:代码|远端|分支|到|至)|推到(?:\s*github|\s*gitlab|\s*远端)/i.test(pushPrompt)) {
109
+ actions.push("push");
110
+ }
111
+ if (/\bdeploy\b|\brelease\b|\bpublish\b|部署|上线|发布(?:版本|到|至)/i.test(deployPrompt)) {
112
+ actions.push("deploy");
113
+ }
114
+ if (/\bgh\s+pr\s+(?:create|merge)\b|\bglab\s+mr\s+(?:create|merge)\b|\b(?:open|create|merge)\s+(?:a\s+)?(?:pull\s*request|merge\s*request|pr|mr)\b|\bpull\s*request\b|\bmerge\s*request\b|(?:创建|新建|发起|合并|提(?:交|个)?)\s*(?:pr|mr|拉取请求|合并请求)/i.test(pullRequestPrompt)) {
115
+ actions.push("pull_request");
116
+ }
117
+ return actions;
118
+ }
119
+ const EXTERNAL_ACTION_LABELS = {
120
+ commit: "提交代码",
121
+ push: "推送远端",
122
+ deploy: "发布或部署",
123
+ pull_request: "创建或合并 PR",
124
+ };
125
+ export function externalActionLabel(action) {
126
+ return EXTERNAL_ACTION_LABELS[action];
127
+ }
128
+ /**
129
+ * Defense in depth for full-access runs. Codex is also instructed not to perform
130
+ * these actions, but command events let the bridge stop an unexpected mutation
131
+ * even when the original prompt did not ask for it.
132
+ */
133
+ export function commandPolicyDecision(command) {
134
+ const requiredActions = [];
135
+ for (const invocation of shellInvocations(command)) {
136
+ const executable = invocation.executable;
137
+ const args = invocation.args.map((argument) => argument.replace(/^\(+|\)+$/g, "").toLocaleLowerCase());
138
+ if ((executable === "kubectl" && hasCommand(args, "delete")) ||
139
+ (executable === "terraform" && hasCommand(args, "destroy")) ||
140
+ (executable === "gh" &&
141
+ ["repo", "issue", "release"].some((name) => hasCommandPair(args, name, "delete"))) ||
142
+ (executable === "curl" && requestsDelete(args)) ||
143
+ (executable === "aws" &&
144
+ (args.some((argument) => argument.startsWith("delete")) ||
145
+ hasCommandPair(args, "s3", "rm"))) ||
146
+ (["gcloud", "az"].includes(executable) && args.some((argument) => argument === "delete"))) {
147
+ return { requiredActions: [], blockedReason: "禁止删除外部或云端数据" };
148
+ }
149
+ if ((executable === "gh" && hasCommand(args, "auth")) ||
150
+ (executable === "npm" && ["login", "logout", "token"].some((name) => hasCommand(args, name))) ||
151
+ (executable === "docker" && ["login", "logout"].some((name) => hasCommand(args, name))) ||
152
+ (executable === "aws" && hasCommand(args, "configure")) ||
153
+ (executable === "gcloud" && hasCommand(args, "auth")) ||
154
+ (executable === "az" && hasCommand(args, "login")) ||
155
+ (executable === "security" &&
156
+ ["add-generic-password", "delete-generic-password", "add-internet-password", "delete-internet-password"].includes(args[0] ?? ""))) {
157
+ return { requiredActions: [], blockedReason: "禁止修改凭据或登录状态" };
158
+ }
159
+ if (executable === "gh" &&
160
+ (["create", "comment", "edit"].some((name) => hasCommandPair(args, "issue", name)) ||
161
+ ["comment", "review"].some((name) => hasCommandPair(args, "pr", name)))) {
162
+ return { requiredActions: [], blockedReason: "禁止代表用户联系他人或发布评论" };
163
+ }
164
+ if (executable === "git") {
165
+ const subcommand = gitSubcommand(args);
166
+ if (subcommand === "commit")
167
+ requiredActions.push("commit");
168
+ if (subcommand === "push")
169
+ requiredActions.push("push");
170
+ }
171
+ if ((executable === "gh" &&
172
+ ["create", "merge"].some((name) => hasCommandPair(args, "pr", name))) ||
173
+ (executable === "glab" &&
174
+ ["create", "merge"].some((name) => hasCommandPair(args, "mr", name)))) {
175
+ requiredActions.push("pull_request");
176
+ }
177
+ if ((["npm", "pnpm", "yarn", "cargo"].includes(executable) && hasCommand(args, "publish")) ||
178
+ (executable === "twine" && hasCommand(args, "upload")) ||
179
+ (executable === "docker" && hasCommand(args, "push")) ||
180
+ (executable === "gh" && hasCommandPair(args, "release", "create")) ||
181
+ (executable === "kubectl" && ["apply", "rollout"].some((name) => hasCommand(args, name))) ||
182
+ (executable === "kubectl" && hasCommandPair(args, "set", "image")) ||
183
+ (executable === "helm" && ["install", "upgrade"].some((name) => hasCommand(args, name))) ||
184
+ (executable === "terraform" && hasCommand(args, "apply")) ||
185
+ (["fly", "firebase"].includes(executable) && hasCommand(args, "deploy")) ||
186
+ (executable === "vercel" && args.includes("--prod")) ||
187
+ (executable === "netlify" && hasCommand(args, "deploy") && args.includes("--prod"))) {
188
+ requiredActions.push("deploy");
189
+ }
190
+ }
191
+ return { requiredActions: [...new Set(requiredActions)] };
192
+ }
193
+ function shellInvocations(command, depth = 0) {
194
+ if (depth > 3)
195
+ return [];
196
+ return splitShellSegments(command.replace(/\\\n/g, " ")).flatMap((segment) => {
197
+ const tokens = shellTokens(segment);
198
+ if (tokens.length === 0)
199
+ return [];
200
+ return unwrapInvocation(tokens, depth);
201
+ });
202
+ }
203
+ function unwrapInvocation(tokens, depth) {
204
+ let cursor = 0;
205
+ while (cursor < tokens.length && isAssignment(tokens[cursor] ?? ""))
206
+ cursor += 1;
207
+ const executable = executableName(tokens[cursor] ?? "");
208
+ if (executable === "env") {
209
+ cursor += 1;
210
+ while (cursor < tokens.length) {
211
+ const token = tokens[cursor] ?? "";
212
+ if (token === "-u" || token === "--unset")
213
+ cursor += 2;
214
+ else if (token.startsWith("-") || isAssignment(token))
215
+ cursor += 1;
216
+ else
217
+ break;
218
+ }
219
+ return unwrapInvocation(tokens.slice(cursor), depth);
220
+ }
221
+ if (executable === "sudo") {
222
+ cursor += 1;
223
+ while (cursor < tokens.length) {
224
+ const token = tokens[cursor] ?? "";
225
+ if (["-u", "-g", "-h", "-p", "-C", "-T"].includes(token))
226
+ cursor += 2;
227
+ else if (token.startsWith("-"))
228
+ cursor += 1;
229
+ else
230
+ break;
231
+ }
232
+ return unwrapInvocation(tokens.slice(cursor), depth);
233
+ }
234
+ if (["command", "nohup", "time"].includes(executable)) {
235
+ cursor += 1;
236
+ while ((tokens[cursor] ?? "").startsWith("-"))
237
+ cursor += 1;
238
+ return unwrapInvocation(tokens.slice(cursor), depth);
239
+ }
240
+ if (["bash", "zsh", "sh", "dash"].includes(executable)) {
241
+ const commandIndex = tokens.findIndex((token, index) => index > cursor && (/^-[a-z]*c[a-z]*$/i.test(token) || token === "--command"));
242
+ const nested = commandIndex >= 0 ? tokens[commandIndex + 1] : undefined;
243
+ return nested ? shellInvocations(nested, depth + 1) : [];
244
+ }
245
+ if (!executable)
246
+ return [];
247
+ return [{ executable, args: tokens.slice(cursor + 1) }];
248
+ }
249
+ function splitShellSegments(command) {
250
+ const segments = [];
251
+ let current = "";
252
+ let quote = null;
253
+ let escaped = false;
254
+ for (let index = 0; index < command.length; index += 1) {
255
+ const character = command[index] ?? "";
256
+ if (escaped) {
257
+ current += character;
258
+ escaped = false;
259
+ continue;
260
+ }
261
+ if (character === "\\") {
262
+ current += character;
263
+ escaped = true;
264
+ continue;
265
+ }
266
+ if (quote) {
267
+ current += character;
268
+ if (character === quote)
269
+ quote = null;
270
+ continue;
271
+ }
272
+ if (character === "'" || character === '"') {
273
+ quote = character;
274
+ current += character;
275
+ continue;
276
+ }
277
+ if ([";", "|", "&", "\n"].includes(character)) {
278
+ if (current.trim())
279
+ segments.push(current.trim());
280
+ current = "";
281
+ continue;
282
+ }
283
+ current += character;
284
+ }
285
+ if (current.trim())
286
+ segments.push(current.trim());
287
+ return segments;
288
+ }
289
+ function shellTokens(segment) {
290
+ const tokens = [];
291
+ let current = "";
292
+ let quote = null;
293
+ let escaped = false;
294
+ const push = () => {
295
+ if (current)
296
+ tokens.push(current);
297
+ current = "";
298
+ };
299
+ for (const character of segment.trim()) {
300
+ if (escaped) {
301
+ current += character;
302
+ escaped = false;
303
+ continue;
304
+ }
305
+ if (character === "\\" && quote !== "'") {
306
+ escaped = true;
307
+ continue;
308
+ }
309
+ if (quote) {
310
+ if (character === quote)
311
+ quote = null;
312
+ else
313
+ current += character;
314
+ continue;
315
+ }
316
+ if (character === "'" || character === '"') {
317
+ quote = character;
318
+ continue;
319
+ }
320
+ if (/\s/.test(character))
321
+ push();
322
+ else
323
+ current += character;
324
+ }
325
+ push();
326
+ return tokens;
327
+ }
328
+ function executableName(token) {
329
+ return token.replace(/^\(+/, "").split("/").at(-1)?.toLocaleLowerCase() ?? "";
330
+ }
331
+ function isAssignment(token) {
332
+ return /^[A-Za-z_][A-Za-z0-9_]*=/.test(token);
333
+ }
334
+ function gitSubcommand(args) {
335
+ for (let index = 0; index < args.length; index += 1) {
336
+ const argument = args[index] ?? "";
337
+ if (["-c", "--git-dir", "--work-tree", "--namespace", "--exec-path"].includes(argument)) {
338
+ index += 1;
339
+ continue;
340
+ }
341
+ if (argument.startsWith("-"))
342
+ continue;
343
+ return argument;
344
+ }
345
+ return "";
346
+ }
347
+ function hasCommand(args, command) {
348
+ return args.some((argument) => argument === command);
349
+ }
350
+ function hasCommandPair(args, first, second) {
351
+ return args.some((argument, index) => argument === first && args[index + 1] === second);
352
+ }
353
+ function requestsDelete(args) {
354
+ return args.some((argument, index) => argument === "--request=delete" ||
355
+ ((argument === "-x" || argument === "--request") && args[index + 1] === "delete"));
356
+ }
357
+ export function normalizePrompt(content, chatType, botMentionNames) {
358
+ let prompt = content.trim();
359
+ if (chatType === "group") {
360
+ for (const name of botMentionNames) {
361
+ const prefix = `@${name}`;
362
+ if (prompt.toLocaleLowerCase().startsWith(prefix.toLocaleLowerCase())) {
363
+ prompt = prompt.slice(prefix.length).trimStart();
364
+ break;
365
+ }
366
+ }
367
+ }
368
+ return prompt.trim();
369
+ }
370
+ export function classifyCommand(prompt) {
371
+ const normalized = prompt.trim().toLocaleLowerCase();
372
+ if (["帮助", "help", "/help"].includes(normalized))
373
+ return "help";
374
+ if ([
375
+ "新手引导",
376
+ "使用指南",
377
+ "快速开始",
378
+ "onboarding",
379
+ "/onboarding",
380
+ "/start",
381
+ ].includes(normalized)) {
382
+ return "onboarding";
383
+ }
384
+ if ([
385
+ "状态",
386
+ "控制台",
387
+ "设备",
388
+ "status",
389
+ "/status",
390
+ "device",
391
+ "/device",
392
+ ].includes(normalized)) {
393
+ return "status";
394
+ }
395
+ if (["额度", "配额", "用量", "usage", "/usage", "quota", "/quota"].includes(normalized)) {
396
+ return "quota";
397
+ }
398
+ if (["新会话", "new", "/new", "reset", "/reset"].includes(normalized))
399
+ return "new";
400
+ if (["会话", "历史会话", "sessions", "/sessions"].includes(normalized))
401
+ return "sessions";
402
+ if (["任务", "任务中心", "tasks", "/tasks"].includes(normalized))
403
+ return "tasks";
404
+ if (["团队", "团队工作台", "team", "/team"].includes(normalized))
405
+ return "team";
406
+ if (["运行手册", "手册", "runbooks", "/runbooks"].includes(normalized))
407
+ return "runbooks";
408
+ if (/^(?:运行手册|运行|run|\/run)\s+[a-z0-9][a-z0-9_-]*/i.test(prompt.trim())) {
409
+ return "runbook_run";
410
+ }
411
+ if (["压缩", "压缩会话", "compact", "/compact"].includes(normalized))
412
+ return "compact";
413
+ if (["审计", "审计日志", "audit", "/audit"].includes(normalized))
414
+ return "audit";
415
+ if (["设置", "模型", "推理", "权限", "settings", "/settings", "model", "/model"].includes(normalized) ||
416
+ /^(?:模型|model|\/model|推理|effort|\/effort|权限|permission|\/permission)(?:\s|[::])/i.test(prompt.trim())) {
417
+ return "settings";
418
+ }
419
+ if (["取消", "停止", "cancel", "/cancel", "stop", "/stop", "interrupt", "/interrupt"].includes(normalized)) {
420
+ return "cancel";
421
+ }
422
+ if (["项目", "项目列表", "项目工作台", "projects", "/projects"].includes(normalized)) {
423
+ return "projects";
424
+ }
425
+ if ([
426
+ "读取项目",
427
+ "阅读项目",
428
+ "项目概览",
429
+ "项目摘要",
430
+ "overview",
431
+ "/overview",
432
+ ].includes(normalized)) {
433
+ return "project_overview";
434
+ }
435
+ if (/^(?:确认切换|切换项目|切换到项目|切换到|切换|use|\/use)(?:\s|[::]|$)/i.test(prompt.trim())) {
436
+ return "switch_project";
437
+ }
438
+ if (/^(?:追加|补充|steer|\/steer)(?:\s|[::]|$)/i.test(prompt.trim()))
439
+ return "steer";
440
+ if (/^(?:排队|稍后执行|queue|\/queue)(?:\s|[::]|$)/i.test(prompt.trim())) {
441
+ return "queue_prompt";
442
+ }
443
+ return "prompt";
444
+ }
445
+ export function projectSelector(prompt) {
446
+ const match = prompt
447
+ .trim()
448
+ .match(/^(?:确认切换|切换项目|切换到项目|切换到|切换|use|\/use)\s*[::]?\s*(.+)$/i);
449
+ return match?.[1]?.trim() || null;
450
+ }
451
+ export function isConfirmedProjectSwitch(prompt) {
452
+ return /^确认切换(?:\s|[::])/i.test(prompt.trim());
453
+ }
454
+ export function steerPrompt(prompt) {
455
+ const match = prompt
456
+ .trim()
457
+ .match(/^(?:追加|补充|steer|\/steer)(?:\s+|[::]\s*)(.+)$/i);
458
+ return match?.[1]?.trim() || null;
459
+ }
460
+ export function queuedPrompt(prompt) {
461
+ const match = prompt
462
+ .trim()
463
+ .match(/^(?:排队|稍后执行|queue|\/queue)(?:\s+|[::]\s*)(.+)$/i);
464
+ return match?.[1]?.trim() || null;
465
+ }
466
+ export function settingsChange(prompt) {
467
+ const match = prompt
468
+ .trim()
469
+ .match(/^(模型|model|\/model|推理|effort|\/effort|权限|permission|\/permission)(?:\s+|[::]\s*)(.+)$/i);
470
+ if (!match?.[1] || !match[2]?.trim())
471
+ return null;
472
+ const command = match[1].toLocaleLowerCase();
473
+ if (["模型", "model", "/model"].includes(command)) {
474
+ return { kind: "model", value: match[2].trim() };
475
+ }
476
+ if (["推理", "effort", "/effort"].includes(command)) {
477
+ return { kind: "effort", value: match[2].trim() };
478
+ }
479
+ return { kind: "sandbox", value: match[2].trim() };
480
+ }
481
+ export const HELP_TEXT = [
482
+ "飞书 Codex V5 已就绪。直接发送开发任务、截图或文本/代码文件即可。",
483
+ "",
484
+ "可用命令:",
485
+ "- 新手引导 / /start:打开一分钟交互式上手流程",
486
+ "- 状态 / 控制台:查看本地设备、当前项目与远程就绪开关",
487
+ "- 额度 / /usage:查看 Codex 各独立额度窗口与重置时间",
488
+ "- 项目 / /projects:打开项目工作台",
489
+ "- 读取项目 / /overview:生成零 AI token 的本地项目快照",
490
+ "- 设置 / 模型:切换模型、推理强度和后续任务权限",
491
+ "- 会话 / /sessions:恢复或压缩本人在当前项目的历史会话",
492
+ "- 任务 / /tasks:查看我的运行中、排队和最近任务",
493
+ "- 团队 / /team:查看成员、项目负载与资源使用概览",
494
+ "- 运行手册 / /runbooks:打开仓库内审核过的团队任务模板",
495
+ "- /run <手册> 参数=值:使用显式参数运行团队模板",
496
+ "- 审计 / /audit:查看最近的安全与操作记录",
497
+ "- 切换 <项目> / /use <编号>:预览切换影响;有任务或会话时需确认",
498
+ "- 追加 <要求> / /steer <要求>:把补充要求加入正在执行的任务",
499
+ "- 排队 <任务> / /queue <任务>:当前任务运行时仍创建一个独立任务",
500
+ "- 新会话 / new:清空当前聊天对应的 Codex 上下文",
501
+ "- 停止 / /stop:停止正在执行的任务,并移除本聊天的排队任务",
502
+ "- 帮助 / help:显示本说明",
503
+ "",
504
+ "团队模式:群聊默认按成员隔离会话;管理员可管理全局,操作者只能控制自己的任务,只读成员不能执行代码。项目 ACL 可进一步限制成员可见项目。",
505
+ "安全限制:只能操作允许列表中的项目;提交、推送、部署和创建/合并 PR 必须先在确认卡中同意;凭据、联系人和外部数据不允许修改。完全访问模式下,未加入聊天白名单的群聊会被拒绝。",
506
+ ].join("\n");