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,129 @@
|
|
|
1
|
+
# D9 团队协作
|
|
2
|
+
|
|
3
|
+
## 目标
|
|
4
|
+
|
|
5
|
+
让多个成员安全地共用一台本地 Codex 设备:默认不共享项目、thread、模型设置和队列;需要协作时显式转交控制权,并让团队看到足够的运行负载与治理信息。
|
|
6
|
+
|
|
7
|
+
## 已确认产品决策
|
|
8
|
+
|
|
9
|
+
1. **私聊负责私密任务,群聊负责显式协作。** 群聊中的消息和任务卡天然对群成员可见;包含未公开代码、事故信息或客户数据的任务应在私聊发起。
|
|
10
|
+
2. **群聊仍默认按成员隔离。** `FEISHU_GROUP_SESSION_SCOPE=member` 时,每个成员拥有自己的项目、thread、设置和串行队列。团队协作不等于共享 Codex 上下文。
|
|
11
|
+
3. **转交的是控制权,不是身份和权限。** 任务保留不可变的发起人;当前控制者可以停止任务、打开审阅和处理非敏感运行时交互。执行仍使用发起任务时冻结的项目和权限设置。
|
|
12
|
+
4. **发起人可以显式收回,管理员可以显式接管。** 接管不会静默发生,所有转交和接管写入本地审计。
|
|
13
|
+
5. **团队面板只展示元数据。** 成员名称、角色、任务数量、状态、项目负载和 token 用量可见;不展示提示词、结果正文、Diff、附件或原始 `open_id`。
|
|
14
|
+
6. **运行手册属于仓库。** `.feishu-codex-runbooks.json` 随项目代码评审和版本控制;第一版不提供飞书内编辑器。
|
|
15
|
+
7. **运行手册不能预授权危险动作。** 模板只能保持或降低当前权限,不能请求完全访问;提交、推送、部署和 PR 模板会校验失败。参数替换后再次检查。
|
|
16
|
+
8. **第一版一个实例代表一台设备。** 多设备项目路由需要中心控制面、实例身份和离线状态协议,延后到后续版本;当前团队通过不同飞书应用或独立系统账号部署多个实例。
|
|
17
|
+
|
|
18
|
+
## 角色与资源模型
|
|
19
|
+
|
|
20
|
+
| 能力 | 管理员 | 操作者 | 只读成员 |
|
|
21
|
+
|---|---:|---:|---:|
|
|
22
|
+
| 创建普通任务和运行手册 | 是 | 是 | 否 |
|
|
23
|
+
| 查看自己的团队概览 | 是 | 是 | 是 |
|
|
24
|
+
| 查看全团队聚合 | 是 | 否 | 否 |
|
|
25
|
+
| 控制自己当前持有的任务 | 是 | 是 | 否 |
|
|
26
|
+
| 接收任务转交 | 是 | 是,且需项目 ACL | 否 |
|
|
27
|
+
| 接管他人任务 | 是,显式操作 | 仅任务发起人收回 | 否 |
|
|
28
|
+
| 查看他人提示词和结果 | 仅群聊中原本可见的内容 | 同左 | 同左 |
|
|
29
|
+
|
|
30
|
+
任务包含两个身份:
|
|
31
|
+
|
|
32
|
+
- `ownerId`:不可变的发起人,也是执行策略、项目与历史 thread 的归属身份。
|
|
33
|
+
- `controllerId`:当前控制者,初始等于发起人,可在同一群聊中显式变化。
|
|
34
|
+
|
|
35
|
+
旧任务没有 `controllerId` 时,迁移会安全回填为 `ownerId`。服务恢复时如果控制者已被移除、降为只读或失去项目权限,控制权回到发起人;旧控制者不能继续批准或停止任务。
|
|
36
|
+
|
|
37
|
+
## 任务交接流程
|
|
38
|
+
|
|
39
|
+
```text
|
|
40
|
+
群聊成员发起任务
|
|
41
|
+
→ 任务卡显示发起人和当前控制者
|
|
42
|
+
→ 当前控制者从友好名称列表选择目标
|
|
43
|
+
→ 服务重新检查目标角色与项目 ACL
|
|
44
|
+
→ 更新 controllerId、任务卡与审计
|
|
45
|
+
→ 新控制者可停止、审阅或回答非敏感运行时问题
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
约束:
|
|
49
|
+
|
|
50
|
+
- 私聊不展示交接控件。
|
|
51
|
+
- 目标选项使用稳定匿名 selector,卡片不携带原始 `open_id`。
|
|
52
|
+
- 友好名称来自 `FEISHU_MEMBER_LABELS_JSON`;未配置时显示稳定匿名成员码。
|
|
53
|
+
- 转交不改变 sandbox、模型、推理强度、外部动作授权或项目。
|
|
54
|
+
- 密码、令牌和密钥问题即使完成转交也不能在飞书回答。
|
|
55
|
+
|
|
56
|
+
## 团队工作台
|
|
57
|
+
|
|
58
|
+
发送 `团队` 或 `/team` 打开。
|
|
59
|
+
|
|
60
|
+
管理员看到最近 7 天的授权团队汇总;其他成员只看到自己的发起/控制任务:
|
|
61
|
+
|
|
62
|
+
- 活跃与排队任务。
|
|
63
|
+
- 成员发起数、控制数、成功率与合计 token。
|
|
64
|
+
- 项目任务数、活跃数和参与成员数。
|
|
65
|
+
- 进入任务中心、运行手册和项目工作台的入口。
|
|
66
|
+
|
|
67
|
+
统计来自本地保留的任务记录,不上传遥测。记录清理后,面板只代表当前保留窗口,不是计费系统。
|
|
68
|
+
|
|
69
|
+
## 团队运行手册
|
|
70
|
+
|
|
71
|
+
复制 `.feishu-codex-runbooks.example.json` 为项目根目录的 `.feishu-codex-runbooks.json`:
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"version": 1,
|
|
76
|
+
"runbooks": [
|
|
77
|
+
{
|
|
78
|
+
"id": "inspect-module",
|
|
79
|
+
"name": "检查指定模块",
|
|
80
|
+
"description": "只读检查模块结构和风险",
|
|
81
|
+
"prompt": "只读检查 {{module}} 模块,说明职责、风险和测试缺口。",
|
|
82
|
+
"sandboxMode": "read-only",
|
|
83
|
+
"reasoningEffort": "high",
|
|
84
|
+
"parameters": [
|
|
85
|
+
{
|
|
86
|
+
"name": "module",
|
|
87
|
+
"label": "模块",
|
|
88
|
+
"required": true
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
}
|
|
92
|
+
]
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
使用方式:
|
|
97
|
+
|
|
98
|
+
- 发送 `运行手册` 或 `/runbooks` 查看当前项目模板。
|
|
99
|
+
- 所有必填参数都有默认值时,可以点击“一键运行”。
|
|
100
|
+
- 需要输入参数时,发送 `/run inspect-module module="auth"`。
|
|
101
|
+
|
|
102
|
+
校验规则:
|
|
103
|
+
|
|
104
|
+
- 每个项目最多 30 个模板、每个模板最多 12 个参数。
|
|
105
|
+
- ID 和参数名必须稳定、唯一且只使用安全字符。
|
|
106
|
+
- 每个参数必须在 prompt 中使用,未声明占位符会拒绝整份配置。
|
|
107
|
+
- `sandboxMode` 只允许 `read-only` 或 `workspace-write`,并与成员当前权限取更低值。
|
|
108
|
+
- 模型和推理设置仍经过当前 Codex 能力目录兼容检查。
|
|
109
|
+
- 参数替换后如果出现外部动作,任务不会启动,用户需作为普通任务发送并单独确认。
|
|
110
|
+
- 文件缺失、JSON 无效或任一模板不安全时失败关闭,不执行部分目录。
|
|
111
|
+
|
|
112
|
+
## 验收标准
|
|
113
|
+
|
|
114
|
+
- 两名成员在同一群聊发起任务,conversation key、项目、thread、设置和队列互不共享。
|
|
115
|
+
- 任务卡不出现原始 `open_id`,并明确显示发起人与当前控制者。
|
|
116
|
+
- 未被转交的普通操作者不能停止或回答他人任务;当前控制者和管理员可以。
|
|
117
|
+
- 只读成员、无项目 ACL 成员和已撤权成员不能成为控制者。
|
|
118
|
+
- 重启后旧任务自动回填控制者,失效控制权安全回收。
|
|
119
|
+
- 团队工作台不包含提示词、结果正文、文件路径或附件内容。
|
|
120
|
+
- 运行手册无法提升 sandbox,也无法静默执行外部动作。
|
|
121
|
+
- 所有交接、接管和运行手册执行均有审计记录。
|
|
122
|
+
- Card 2.0 payload 通过唯一 element ID、行列上限和敏感文本回归测试。
|
|
123
|
+
|
|
124
|
+
## 后续版本
|
|
125
|
+
|
|
126
|
+
- 中心控制面中的多设备心跳、实例证书、项目到设备路由和离线队列。
|
|
127
|
+
- 在安全的交互表单中填写运行手册参数,而不依赖命令文本。
|
|
128
|
+
- 团队级运行手册签名、版本固定和变更审批。
|
|
129
|
+
- 按项目设置可见聚合指标与更长统计窗口。
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "feishu-codex-console",
|
|
3
|
+
"version": "1.0.0-beta.6",
|
|
4
|
+
"description": "A reliable Feishu control plane for local Codex projects, sessions, approvals, and live Card 2.0 tasks.",
|
|
5
|
+
"private": false,
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/LeoChaseYoung/feishu-codex-console.git"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"codex",
|
|
14
|
+
"feishu",
|
|
15
|
+
"lark",
|
|
16
|
+
"bot",
|
|
17
|
+
"remote-development",
|
|
18
|
+
"team"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://github.com/LeoChaseYoung/feishu-codex-console#readme",
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/LeoChaseYoung/feishu-codex-console/issues"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=22"
|
|
26
|
+
},
|
|
27
|
+
"bin": {
|
|
28
|
+
"feishu-codex-console": "scripts/cli.mjs",
|
|
29
|
+
"feishu-codex-bridge": "scripts/cli.mjs"
|
|
30
|
+
},
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/",
|
|
33
|
+
"scripts/",
|
|
34
|
+
"docs/*.md",
|
|
35
|
+
"docs/requirements/*.md",
|
|
36
|
+
".env.example",
|
|
37
|
+
".feishu-codex-policy.example.json",
|
|
38
|
+
".feishu-codex-runbooks.example.json",
|
|
39
|
+
"README.md",
|
|
40
|
+
"README.en.md",
|
|
41
|
+
"ROADMAP.md",
|
|
42
|
+
"SECURITY.md",
|
|
43
|
+
"CONTRIBUTING.md",
|
|
44
|
+
"CODE_OF_CONDUCT.md",
|
|
45
|
+
"CHANGELOG.md"
|
|
46
|
+
],
|
|
47
|
+
"scripts": {
|
|
48
|
+
"build": "tsc -p tsconfig.json",
|
|
49
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
50
|
+
"dev": "tsx src/index.ts",
|
|
51
|
+
"doctor": "tsx src/doctor.ts",
|
|
52
|
+
"start": "node --disable-warning=ExperimentalWarning dist/index.js",
|
|
53
|
+
"service:install": "npm run build && node scripts/service.mjs install",
|
|
54
|
+
"service:status": "node scripts/service.mjs status",
|
|
55
|
+
"service:stop": "node scripts/service.mjs stop",
|
|
56
|
+
"service:restart": "node scripts/service.mjs restart",
|
|
57
|
+
"service:uninstall": "node scripts/service.mjs uninstall",
|
|
58
|
+
"smoke:codex": "tsx src/smoke.ts",
|
|
59
|
+
"test": "vitest run",
|
|
60
|
+
"test:watch": "vitest",
|
|
61
|
+
"test:package": "node scripts/package-smoke.mjs",
|
|
62
|
+
"release:check": "node scripts/verify-release.mjs",
|
|
63
|
+
"prepack": "npm run build"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@larksuite/cli": "1.0.70",
|
|
67
|
+
"@openai/codex": "0.144.4",
|
|
68
|
+
"dotenv": "17.2.3"
|
|
69
|
+
},
|
|
70
|
+
"devDependencies": {
|
|
71
|
+
"@types/node": "26.1.1",
|
|
72
|
+
"tsx": "4.23.1",
|
|
73
|
+
"typescript": "7.0.2",
|
|
74
|
+
"vitest": "4.1.10"
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
|
|
3
|
+
export function probeFeishuCapabilities(cliPath, options = {}) {
|
|
4
|
+
const run = options.run ?? runCommand;
|
|
5
|
+
const cwd = options.cwd ?? process.cwd();
|
|
6
|
+
const checks = [];
|
|
7
|
+
|
|
8
|
+
checks.push(
|
|
9
|
+
result(
|
|
10
|
+
"bot_identity",
|
|
11
|
+
"飞书 Bot 身份",
|
|
12
|
+
run(cliPath, ["whoami", "--as", "bot"], cwd),
|
|
13
|
+
"请重新绑定飞书应用:npx lark-cli config init",
|
|
14
|
+
),
|
|
15
|
+
);
|
|
16
|
+
checks.push(
|
|
17
|
+
eventResult(
|
|
18
|
+
"message_event",
|
|
19
|
+
"消息事件 im.message.receive_v1",
|
|
20
|
+
run(cliPath, eventProbeArgs("im.message.receive_v1"), cwd),
|
|
21
|
+
"请在飞书开发者后台启用 im.message.receive_v1,并确认消息读取权限已发布。",
|
|
22
|
+
),
|
|
23
|
+
);
|
|
24
|
+
checks.push(
|
|
25
|
+
eventResult(
|
|
26
|
+
"card_action_event",
|
|
27
|
+
"卡片事件 card.action.trigger",
|
|
28
|
+
run(cliPath, eventProbeArgs("card.action.trigger"), cwd),
|
|
29
|
+
"请在飞书开发者后台启用 card.action.trigger,并确认 im:message:readonly 已发布。",
|
|
30
|
+
),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
return { ok: checks.every((check) => check.ok), checks };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function eventProbeArgs(eventKey) {
|
|
37
|
+
return [
|
|
38
|
+
"event",
|
|
39
|
+
"consume",
|
|
40
|
+
eventKey,
|
|
41
|
+
"--as",
|
|
42
|
+
"bot",
|
|
43
|
+
"--max-events",
|
|
44
|
+
"0",
|
|
45
|
+
"--timeout",
|
|
46
|
+
"1s",
|
|
47
|
+
"--quiet",
|
|
48
|
+
];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function eventResult(id, label, outcome, remediation) {
|
|
52
|
+
if (outcome.status === 0) return { id, label, ok: true, detail: "事件订阅可用" };
|
|
53
|
+
const text = `${outcome.stdout ?? ""}\n${outcome.stderr ?? ""}`;
|
|
54
|
+
if (/another consumer .* already running|another consumer .* is already running/i.test(text)) {
|
|
55
|
+
return { id, label, ok: true, detail: "已有运行中的事件消费者" };
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
id,
|
|
59
|
+
label,
|
|
60
|
+
ok: false,
|
|
61
|
+
detail: extractError(text) || "事件订阅不可用",
|
|
62
|
+
remediation,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function result(id, label, outcome, remediation) {
|
|
67
|
+
if (outcome.status === 0) return { id, label, ok: true, detail: "可用" };
|
|
68
|
+
const text = `${outcome.stdout ?? ""}\n${outcome.stderr ?? ""}`;
|
|
69
|
+
return {
|
|
70
|
+
id,
|
|
71
|
+
label,
|
|
72
|
+
ok: false,
|
|
73
|
+
detail: extractError(text) || "不可用",
|
|
74
|
+
remediation,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runCommand(command, args, cwd) {
|
|
79
|
+
const outcome = spawnSync(command, args, {
|
|
80
|
+
cwd,
|
|
81
|
+
env: {
|
|
82
|
+
...process.env,
|
|
83
|
+
LARKSUITE_CLI_NO_UPDATE_NOTIFIER: "1",
|
|
84
|
+
LARKSUITE_CLI_NO_SKILLS_NOTIFIER: "1",
|
|
85
|
+
},
|
|
86
|
+
encoding: "utf8",
|
|
87
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
88
|
+
});
|
|
89
|
+
return { status: outcome.status, stdout: outcome.stdout, stderr: outcome.stderr };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function extractError(text) {
|
|
93
|
+
try {
|
|
94
|
+
const start = text.indexOf("{");
|
|
95
|
+
const value = JSON.parse(start >= 0 ? text.slice(start) : text);
|
|
96
|
+
if (typeof value?.error?.message === "string") return redact(value.error.message);
|
|
97
|
+
} catch {
|
|
98
|
+
// Fall back to the first useful line below.
|
|
99
|
+
}
|
|
100
|
+
return redact(
|
|
101
|
+
text
|
|
102
|
+
.split(/\r?\n/)
|
|
103
|
+
.map((line) => line.trim())
|
|
104
|
+
.find(Boolean) ?? "",
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function redact(value) {
|
|
109
|
+
return value
|
|
110
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, "Bearer [REDACTED]")
|
|
111
|
+
.replace(/\b(?:access|refresh)[_-]?token\s*[:=]\s*[^\s,;]+/gi, "token=[REDACTED]")
|
|
112
|
+
.slice(0, 500);
|
|
113
|
+
}
|