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.
- package/.env.example +101 -0
- package/.feishu-codex-policy.example.json +11 -0
- package/.feishu-codex-runbooks.example.json +36 -0
- package/CHANGELOG.md +129 -0
- package/CODE_OF_CONDUCT.md +7 -0
- package/CONTRIBUTING.md +52 -0
- package/LICENSE +21 -0
- package/README.en.md +81 -0
- package/README.md +398 -0
- package/ROADMAP.md +40 -0
- package/SECURITY.md +53 -0
- package/dist/account-quota-card.js +233 -0
- package/dist/account-quota.js +125 -0
- package/dist/app-server-client.js +281 -0
- package/dist/card-session.js +166 -0
- package/dist/codex-events.js +1 -0
- package/dist/codex-runner.js +875 -0
- package/dist/config.js +198 -0
- package/dist/confirmation-card.js +135 -0
- package/dist/control-card.js +345 -0
- package/dist/conversation-turn-session.js +209 -0
- package/dist/data-maintenance.js +71 -0
- package/dist/device-card.js +460 -0
- package/dist/device-health.js +94 -0
- package/dist/diagnostics.js +253 -0
- package/dist/doctor.js +250 -0
- package/dist/fallback-card-session.js +37 -0
- package/dist/health-file.js +75 -0
- package/dist/index.js +4330 -0
- package/dist/lark-cli.js +558 -0
- package/dist/lark-retry.js +34 -0
- package/dist/maintenance.js +140 -0
- package/dist/model-capabilities.js +31 -0
- package/dist/onboarding-card.js +312 -0
- package/dist/permission-lease.js +22 -0
- package/dist/policy.js +506 -0
- package/dist/progress.js +267 -0
- package/dist/project-card.js +303 -0
- package/dist/project-overview-card.js +182 -0
- package/dist/project-overview.js +278 -0
- package/dist/project-policy.js +160 -0
- package/dist/project-registry.js +259 -0
- package/dist/project-status.js +45 -0
- package/dist/project-workspace.js +55 -0
- package/dist/quota-card.js +94 -0
- package/dist/recovery-policy.js +26 -0
- package/dist/redaction.js +67 -0
- package/dist/remote-ready.js +112 -0
- package/dist/response-card.js +139 -0
- package/dist/result-card.js +166 -0
- package/dist/review-card.js +452 -0
- package/dist/runbook-card.js +272 -0
- package/dist/runbooks.js +191 -0
- package/dist/runtime-card.js +337 -0
- package/dist/session-card.js +128 -0
- package/dist/session-naming.js +14 -0
- package/dist/smoke.js +28 -0
- package/dist/state-backup.js +302 -0
- package/dist/state-store.js +874 -0
- package/dist/task-card.js +640 -0
- package/dist/task-center-card.js +176 -0
- package/dist/task-failure.js +43 -0
- package/dist/task-intent.js +76 -0
- package/dist/task-queue.js +187 -0
- package/dist/task-reconciliation.js +80 -0
- package/dist/task-review.js +497 -0
- package/dist/team-card.js +275 -0
- package/dist/team-directory.js +54 -0
- package/dist/team-policy.js +93 -0
- package/dist/types.js +1 -0
- package/dist/version.js +9 -0
- package/dist/workspace-session.js +64 -0
- package/docs/ARCHITECTURE.md +54 -0
- package/docs/COMPATIBILITY.md +55 -0
- package/docs/CONFIGURATION.md +88 -0
- package/docs/DEMO.md +45 -0
- package/docs/GOOD_FIRST_ISSUES.md +23 -0
- package/docs/INSTALLATION.md +207 -0
- package/docs/OPEN_SOURCE_PRODUCT_PLAN.md +113 -0
- package/docs/PRODUCT_REQUIREMENTS_MAP.md +591 -0
- package/docs/RELEASE_CHECKLIST.md +65 -0
- package/docs/TEAM_DEPLOYMENT.md +35 -0
- package/docs/TROUBLESHOOTING.md +130 -0
- package/docs/V4_WORKSPACE_SESSION_FLOW.md +232 -0
- package/docs/requirements/D10_MAINTENANCE_AND_ECOSYSTEM.md +103 -0
- package/docs/requirements/D1_INSTALLATION_AND_FIRST_CONNECTION.md +479 -0
- package/docs/requirements/D2_DEVICE_AND_CONNECTIVITY.md +54 -0
- package/docs/requirements/D3_PROJECTS_AND_SESSIONS.md +107 -0
- package/docs/requirements/D4_REMOTE_TASK_EXECUTION.md +102 -0
- package/docs/requirements/D5_CODEX_NATIVE_INTERACTIONS.md +99 -0
- package/docs/requirements/D6_RESULTS_AND_CODE_REVIEW.md +100 -0
- package/docs/requirements/D7_SECURITY_GOVERNANCE.md +106 -0
- package/docs/requirements/D8_RELIABILITY_AND_RECOVERY.md +182 -0
- package/docs/requirements/D9_TEAM_COLLABORATION.md +129 -0
- package/package.json +76 -0
- package/scripts/capability-probe.mjs +113 -0
- package/scripts/cli.mjs +919 -0
- package/scripts/config-file.mjs +137 -0
- package/scripts/discovery-lib.mjs +78 -0
- package/scripts/install-card.mjs +37 -0
- package/scripts/install-detection.mjs +126 -0
- package/scripts/install-state.mjs +107 -0
- package/scripts/launchd.mjs +161 -0
- package/scripts/migrate-legacy.mjs +97 -0
- package/scripts/package-smoke.mjs +163 -0
- package/scripts/release-dist-tag.mjs +7 -0
- package/scripts/runbook-template.mjs +36 -0
- package/scripts/service-health.mjs +110 -0
- package/scripts/service.mjs +24 -0
- package/scripts/setup-lib.mjs +118 -0
- package/scripts/systemd.mjs +96 -0
- package/scripts/upgrade-lib.mjs +99 -0
- package/scripts/verify-release.mjs +37 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { prepareRemoteMarkdown } from "./redaction.js";
|
|
2
|
+
export function renderResponseCard(text, phase = "info") {
|
|
3
|
+
const presentation = responsePresentation(phase, text);
|
|
4
|
+
const content = safeMarkdown(normalizeResponseText(text));
|
|
5
|
+
return {
|
|
6
|
+
schema: "2.0",
|
|
7
|
+
config: {
|
|
8
|
+
update_multi: true,
|
|
9
|
+
width_mode: "default",
|
|
10
|
+
enable_forward: false,
|
|
11
|
+
summary: { content: `${presentation.title} · ${truncatePlain(content, 70)}` },
|
|
12
|
+
},
|
|
13
|
+
header: {
|
|
14
|
+
title: { tag: "plain_text", content: presentation.title },
|
|
15
|
+
subtitle: { tag: "plain_text", content: "本地 Codex 控制台" },
|
|
16
|
+
template: presentation.template,
|
|
17
|
+
icon: { tag: "standard_icon", token: "myai_colorful" },
|
|
18
|
+
text_tag_list: [
|
|
19
|
+
{
|
|
20
|
+
tag: "text_tag",
|
|
21
|
+
text: { tag: "plain_text", content: presentation.tag },
|
|
22
|
+
color: presentation.color,
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
body: {
|
|
27
|
+
direction: "vertical",
|
|
28
|
+
padding: "12px 12px 16px 12px",
|
|
29
|
+
vertical_spacing: "10px",
|
|
30
|
+
elements: [
|
|
31
|
+
{
|
|
32
|
+
tag: "column_set",
|
|
33
|
+
element_id: "resp_content_panel",
|
|
34
|
+
flex_mode: "none",
|
|
35
|
+
columns: [
|
|
36
|
+
{
|
|
37
|
+
tag: "column",
|
|
38
|
+
width: "weighted",
|
|
39
|
+
weight: 1,
|
|
40
|
+
background_style: presentation.background,
|
|
41
|
+
padding: "12px 12px 12px 12px",
|
|
42
|
+
elements: [
|
|
43
|
+
{
|
|
44
|
+
tag: "markdown",
|
|
45
|
+
element_id: "response_content",
|
|
46
|
+
content: truncate(content, 10_000),
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tag: "markdown",
|
|
54
|
+
element_id: "response_hint",
|
|
55
|
+
text_size: "notation",
|
|
56
|
+
content: `<font color='grey'>${safeMarkdown(presentation.hint)}</font>`,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
export function responsePresentation(phase, text) {
|
|
63
|
+
const key = `${phase} ${text}`.toLocaleLowerCase();
|
|
64
|
+
const area = responseArea(phase);
|
|
65
|
+
if (/denied|unauthorized|forbidden|权限|无权/.test(key)) {
|
|
66
|
+
return style("warning", "权限受限", "需要确认", "权限没有变更;如需继续,请由项目或团队管理员检查授权。");
|
|
67
|
+
}
|
|
68
|
+
if (/failed|failure|error|invalid|unavailable|not-found|unsupported|失败|异常|无效|没有找到|不支持/.test(key)) {
|
|
69
|
+
return style("error", `${area}未完成`, "未完成", "可以发送“状态”检查本机连接,或按消息中的建议重试。");
|
|
70
|
+
}
|
|
71
|
+
if (/expired|stale|queue-full|warning|confirm|过期|已满|确认|稍后|等待/.test(key)) {
|
|
72
|
+
return style("warning", `${area}提醒`, "请留意", "按上方说明处理后即可继续;不会静默执行高风险动作。");
|
|
73
|
+
}
|
|
74
|
+
if (/cancel|stopped|revoke|取消|停止|撤销/.test(key)) {
|
|
75
|
+
return style("neutral", `${area}已结束`, "已停止", "当前操作已经结束;需要继续时可重新发送任务或打开对应控制中心。");
|
|
76
|
+
}
|
|
77
|
+
if (/complete|success|accepted|approved|changed|switched|created|完成|成功|已接收|已切换|已创建|已更新|已开启/.test(key)) {
|
|
78
|
+
return style("success", `${area}已更新`, "已完成", "后续可继续发送任务;需要查看全局状态时发送“控制台”。");
|
|
79
|
+
}
|
|
80
|
+
return style("info", area, "信息", "发送“帮助”查看常用命令,或发送“控制台”查看本机状态。");
|
|
81
|
+
}
|
|
82
|
+
function responseArea(phase) {
|
|
83
|
+
const normalized = phase.toLocaleLowerCase();
|
|
84
|
+
if (/project|switch/.test(normalized))
|
|
85
|
+
return "项目";
|
|
86
|
+
if (/session|compact|new/.test(normalized))
|
|
87
|
+
return "会话";
|
|
88
|
+
if (/setting|model|effort|sandbox|permission|lease/.test(normalized))
|
|
89
|
+
return "设置";
|
|
90
|
+
if (/task|queue|steer|prompt|cancel|stop|timeout/.test(normalized))
|
|
91
|
+
return "任务";
|
|
92
|
+
if (/attachment|file|image/.test(normalized))
|
|
93
|
+
return "附件";
|
|
94
|
+
if (/device|status|recovery|service|codex/.test(normalized))
|
|
95
|
+
return "本机服务";
|
|
96
|
+
if (/audit|security|secret/.test(normalized))
|
|
97
|
+
return "安全提示";
|
|
98
|
+
if (/help|onboarding/.test(normalized))
|
|
99
|
+
return "使用指南";
|
|
100
|
+
return "操作结果";
|
|
101
|
+
}
|
|
102
|
+
function style(tone, title, tag, hint) {
|
|
103
|
+
if (tone === "success") {
|
|
104
|
+
return { tone, title, tag, hint, template: "green", background: "green-50", color: "green" };
|
|
105
|
+
}
|
|
106
|
+
if (tone === "warning") {
|
|
107
|
+
return { tone, title, tag, hint, template: "orange", background: "orange-50", color: "orange" };
|
|
108
|
+
}
|
|
109
|
+
if (tone === "error") {
|
|
110
|
+
return { tone, title, tag, hint, template: "red", background: "red-50", color: "red" };
|
|
111
|
+
}
|
|
112
|
+
if (tone === "neutral") {
|
|
113
|
+
return { tone, title, tag, hint, template: "grey", background: "grey-50", color: "neutral" };
|
|
114
|
+
}
|
|
115
|
+
return { tone, title, tag, hint, template: "blue", background: "blue-50", color: "blue" };
|
|
116
|
+
}
|
|
117
|
+
function normalizeResponseText(value) {
|
|
118
|
+
return prepareRemoteMarkdown(value)
|
|
119
|
+
.replace(/^[\s✅✔️☑️ℹ️⚠️❌]+/u, "")
|
|
120
|
+
.trim() || "操作已处理。";
|
|
121
|
+
}
|
|
122
|
+
function safeMarkdown(value) {
|
|
123
|
+
return value
|
|
124
|
+
.replaceAll("<at", "<at")
|
|
125
|
+
.replaceAll("</at>", "</at>")
|
|
126
|
+
.replaceAll("<person", "<person")
|
|
127
|
+
.replaceAll("</person>", "</person>");
|
|
128
|
+
}
|
|
129
|
+
function truncate(value, max) {
|
|
130
|
+
if (value.length <= max)
|
|
131
|
+
return value;
|
|
132
|
+
return `${value.slice(0, Math.max(0, max - 18))}\n\n[内容已截断]`;
|
|
133
|
+
}
|
|
134
|
+
function truncatePlain(value, max) {
|
|
135
|
+
const plain = value.replace(/[*_`>#\[\]()]/g, "").replace(/\s+/g, " ").trim();
|
|
136
|
+
if (plain.length <= max)
|
|
137
|
+
return plain;
|
|
138
|
+
return `${plain.slice(0, Math.max(0, max - 1))}…`;
|
|
139
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { effectiveTestRuns, testEvidenceState, } from "./progress.js";
|
|
2
|
+
import { prepareRemoteMarkdown, redactSensitiveText } from "./redaction.js";
|
|
3
|
+
import { taskModeLabel, taskModeOf } from "./task-intent.js";
|
|
4
|
+
const RESULT_DETAIL_LIMIT = 10_000;
|
|
5
|
+
export function renderTaskResultCard(progress) {
|
|
6
|
+
const mode = taskModeOf(progress);
|
|
7
|
+
const response = progress.finalResponse || progress.partialResponse || "任务已完成,但没有返回文字说明。";
|
|
8
|
+
const tests = effectiveTestRuns(progress.commandRuns);
|
|
9
|
+
const testState = testEvidenceState(tests);
|
|
10
|
+
const testLabel = testState === "passed"
|
|
11
|
+
? `${tests.length} 项通过`
|
|
12
|
+
: testState === "failed"
|
|
13
|
+
? "存在失败"
|
|
14
|
+
: testState === "running"
|
|
15
|
+
? "仍在运行"
|
|
16
|
+
: "未运行";
|
|
17
|
+
const changed = progress.review?.availability === "ready"
|
|
18
|
+
? progress.review.totalFiles
|
|
19
|
+
: progress.changedFiles.length;
|
|
20
|
+
const failed = testState === "failed";
|
|
21
|
+
return {
|
|
22
|
+
schema: "2.0",
|
|
23
|
+
config: {
|
|
24
|
+
update_multi: true,
|
|
25
|
+
width_mode: "default",
|
|
26
|
+
enable_forward: false,
|
|
27
|
+
summary: { content: `完整结果 · ${safePlain(progress.projectLabel)}` },
|
|
28
|
+
},
|
|
29
|
+
header: {
|
|
30
|
+
title: {
|
|
31
|
+
tag: "plain_text",
|
|
32
|
+
content: mode === "answer"
|
|
33
|
+
? "Codex 回答"
|
|
34
|
+
: mode === "analyze"
|
|
35
|
+
? "分析结果"
|
|
36
|
+
: mode === "write"
|
|
37
|
+
? "交付内容"
|
|
38
|
+
: "Codex 完整结果",
|
|
39
|
+
},
|
|
40
|
+
subtitle: {
|
|
41
|
+
tag: "plain_text",
|
|
42
|
+
content: truncatePlain(redactSensitiveText(progress.prompt).replace(/\s+/g, " "), 72),
|
|
43
|
+
},
|
|
44
|
+
template: failed ? "red" : "green",
|
|
45
|
+
icon: { tag: "standard_icon", token: "myai_colorful" },
|
|
46
|
+
text_tag_list: [
|
|
47
|
+
{
|
|
48
|
+
tag: "text_tag",
|
|
49
|
+
text: { tag: "plain_text", content: failed ? "测试未通过" : taskModeLabel(mode) },
|
|
50
|
+
color: failed ? "red" : "green",
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
tag: "text_tag",
|
|
54
|
+
text: { tag: "plain_text", content: `#${truncatePlain(progress.taskId, 16)}` },
|
|
55
|
+
color: "neutral",
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
|
+
body: {
|
|
60
|
+
direction: "vertical",
|
|
61
|
+
padding: "12px 12px 18px 12px",
|
|
62
|
+
vertical_spacing: "12px",
|
|
63
|
+
elements: [
|
|
64
|
+
{
|
|
65
|
+
tag: "column_set",
|
|
66
|
+
element_id: "result_panel",
|
|
67
|
+
flex_mode: "none",
|
|
68
|
+
columns: [
|
|
69
|
+
{
|
|
70
|
+
tag: "column",
|
|
71
|
+
width: "weighted",
|
|
72
|
+
weight: 1,
|
|
73
|
+
background_style: "grey-50",
|
|
74
|
+
padding: "12px 12px 12px 12px",
|
|
75
|
+
elements: [
|
|
76
|
+
{
|
|
77
|
+
tag: "markdown",
|
|
78
|
+
element_id: "result_content",
|
|
79
|
+
content: safe(truncate(response, RESULT_DETAIL_LIMIT)),
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
tag: "markdown",
|
|
87
|
+
element_id: "result_evidence",
|
|
88
|
+
text_size: "notation",
|
|
89
|
+
content: resultEvidence(progress, testLabel, changed),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
tag: "column_set",
|
|
93
|
+
element_id: "result_actions",
|
|
94
|
+
flex_mode: "none",
|
|
95
|
+
columns: [
|
|
96
|
+
{
|
|
97
|
+
tag: "column",
|
|
98
|
+
width: "weighted",
|
|
99
|
+
weight: 1,
|
|
100
|
+
elements: [
|
|
101
|
+
{
|
|
102
|
+
tag: "button",
|
|
103
|
+
element_id: "result_back_btn",
|
|
104
|
+
text: { tag: "plain_text", content: "返回结果摘要" },
|
|
105
|
+
type: "default",
|
|
106
|
+
width: "fill",
|
|
107
|
+
behaviors: [
|
|
108
|
+
{
|
|
109
|
+
type: "callback",
|
|
110
|
+
value: {
|
|
111
|
+
bridge: "feishu-codex-v3",
|
|
112
|
+
action: "result_back",
|
|
113
|
+
task_id: progress.taskId,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
tag: "markdown",
|
|
124
|
+
element_id: "result_footer",
|
|
125
|
+
text_size: "notation",
|
|
126
|
+
text_align: "center",
|
|
127
|
+
content: "<font color='grey'>直接回复可继续当前会话;本地文件路径不会生成远端无效链接。</font>",
|
|
128
|
+
},
|
|
129
|
+
],
|
|
130
|
+
},
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function resultEvidence(progress, testLabel, changed) {
|
|
134
|
+
const mode = taskModeOf(progress);
|
|
135
|
+
if (mode === "answer") {
|
|
136
|
+
return `<font color='grey'>上下文</font> ${safePlain(progress.projectLabel)} · 当前 Codex 会话`;
|
|
137
|
+
}
|
|
138
|
+
if (mode === "analyze") {
|
|
139
|
+
return `<font color='grey'>分析方式</font> 只读 · ${changed > 0 ? `发现 ${changed} 个文件变化` : "未修改文件"}`;
|
|
140
|
+
}
|
|
141
|
+
if (mode === "write") {
|
|
142
|
+
const test = testLabel === "未运行" ? "" : ` · 检查 ${testLabel}`;
|
|
143
|
+
return `<font color='grey'>交付</font> 文件 ${changed} 个${test}`;
|
|
144
|
+
}
|
|
145
|
+
return `<font color='grey'>验证</font> 测试 ${testLabel} · 文件 ${changed} 个`;
|
|
146
|
+
}
|
|
147
|
+
function safe(value) {
|
|
148
|
+
return prepareRemoteMarkdown(value)
|
|
149
|
+
.replaceAll("<at", "<at")
|
|
150
|
+
.replaceAll("</at>", "</at>")
|
|
151
|
+
.replaceAll("<person", "<person")
|
|
152
|
+
.replaceAll("</person>", "</person>");
|
|
153
|
+
}
|
|
154
|
+
function safePlain(value) {
|
|
155
|
+
return redactSensitiveText(value).replace(/\s+/g, " ").trim();
|
|
156
|
+
}
|
|
157
|
+
function truncate(value, max) {
|
|
158
|
+
if (value.length <= max)
|
|
159
|
+
return value;
|
|
160
|
+
return `${value.slice(0, Math.max(0, max - 32))}\n\n[内容已截断,请在本机查看完整输出]`;
|
|
161
|
+
}
|
|
162
|
+
function truncatePlain(value, max) {
|
|
163
|
+
if (value.length <= max)
|
|
164
|
+
return value;
|
|
165
|
+
return `${value.slice(0, Math.max(0, max - 1))}…`;
|
|
166
|
+
}
|