sintbot 1.4.15 → 1.4.17
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/CHANGELOG.en.json +50 -0
- package/CHANGELOG.json +50 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/bin/sintbot-v2.js +104 -4
- package/docs/AI-ASSISTED-INSTALL.md +4 -4
- package/docs/install.md +46 -10
- package/docs/zh-CN/AI-ASSISTED-INSTALL.md +4 -4
- package/docs/zh-CN/install.md +46 -10
- package/install.sh +1 -1
- package/package.json +10 -3
- package/src/agent/claudecode.js +5 -2
- package/src/agent/claudemodels.js +79 -8
- package/src/agent/claudeusage.js +188 -3
- package/src/agent/codexapp.js +4 -3
- package/src/agent/codexcode.js +1 -1
- package/src/agent/envconflicts.js +74 -0
- package/src/agent/privatecore.js +65 -4
- package/src/agent/privatecore.wasm +0 -0
- package/src/c2configcontrol.js +29 -18
- package/src/c2console.js +22 -6
- package/src/c2install.js +1 -1
- package/src/c2multigroupservice.js +22 -2
- package/src/connect.js +488 -0
- package/src/core/brain.js +117 -0
- package/src/core/c2inputrejection.js +19 -6
- package/src/core/c2runnerinput.js +7 -2
- package/src/core/modelcmd.js +132 -0
- package/src/core/modelname.js +61 -0
- package/src/core/v2engine.js +304 -47
- package/src/groupbrain.js +135 -0
- package/src/modelcatalog.js +122 -0
- package/src/platform/feishu.js +39 -7
- package/src/platform/modelcard.js +103 -0
- package/src/ui/console.html +273 -147
- package/src/ui/serverbase.js +174 -17
- package/src/usage.js +131 -38
- package/src/modelcontext.js +0 -13
package/CHANGELOG.en.json
CHANGED
|
@@ -1,4 +1,54 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.4.17",
|
|
4
|
+
"date": "2026-11-12",
|
|
5
|
+
"name": "Feishu scan-to-connect and third-party quotas",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"t": "Connect Feishu with one scan",
|
|
9
|
+
"d": "New command sintbot connect: scan a QR code in the Feishu app to create the bot app; SintBot self-checks, opens the contact and availability scope and publishes, creates ~/Projects/<name>, creates a group with you in it, writes the configuration and initializes journals. On failure it stops at that step and tells you how to resume (--app-id, --chat); non-admins in a company organization can use --basic. The manual path remains."
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"t": "Simpler Feishu settings",
|
|
13
|
+
"d": "Connection status and project groups come first. New users get a three-step scan setup guide; manual setup and app details are folded away. Console administrator sign-in is separate."
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"t": "Third-party quota and balance",
|
|
17
|
+
"d": "GLM shows the 5-hour and weekly windows and falls back to the balance without a plan; MiniMax shows the 5-hour and weekly windows; DeepSeek shows the balance. Invalid credentials are shown as such; transient failures keep the last value for 10 minutes."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"t": "Claude subscription quota fallback",
|
|
21
|
+
"d": "When the Claude Code login token has expired, the CLI refreshes it itself and SintBot reads its cached quota instead of showing \"unverified\"; read-only, never refreshing on its own, never blocking group commands or the console."
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"t": "Environment conflict check",
|
|
25
|
+
"d": "Preflight and the console model cards list environment variables and ~/.claude/settings.json or ~/.codex/config.toml settings that would reroute Claude Code or Codex; key names only, warnings only."
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"t": "Built-in core ABI 36",
|
|
29
|
+
"d": "Connect decisions and wording, QR encoding, quota parsing and environment checks live in the built-in core; the core build pins its toolchain and no longer depends on the crate mirror used by the builder."
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"version": "1.4.16",
|
|
35
|
+
"date": "2026-11-11",
|
|
36
|
+
"name": "Per-group model and thinking depth",
|
|
37
|
+
"items": [
|
|
38
|
+
{
|
|
39
|
+
"t": "Choose model and depth per group",
|
|
40
|
+
"d": "Pick the backend, model and thinking depth on each group card in the console. Model lists come live from the local Codex and Claude Code. Subscription model cards no longer carry a model or depth; a third-party endpoint keeps its model on the model card."
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"t": "Group command /model",
|
|
44
|
+
"d": "Send /model for a tap-to-pick card and confirm to apply, or send /model high, /model <model>, or /model <backend> <model> <depth>. A model or depth change on the same backend continues the conversation; a backend change starts a new one. Changes wait until the group is idle; receipts and the audit log record who made them."
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"t": "Existing settings migrate automatically",
|
|
48
|
+
"d": "After the upgrade connects, each model card's model and depth move into its groups with the same effective values and no session reset, after a configuration backup. Custom Codex context windows are checked against each group's chosen model."
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
2
52
|
{
|
|
3
53
|
"version": "1.4.15",
|
|
4
54
|
"date": "2026-11-11",
|
package/CHANGELOG.json
CHANGED
|
@@ -1,4 +1,54 @@
|
|
|
1
1
|
[
|
|
2
|
+
{
|
|
3
|
+
"version": "1.4.17",
|
|
4
|
+
"date": "2026-11-12",
|
|
5
|
+
"name": "飞书扫码接入与第三方额度",
|
|
6
|
+
"items": [
|
|
7
|
+
{
|
|
8
|
+
"t": "扫码一键接入飞书",
|
|
9
|
+
"d": "新命令 sintbot connect:手机飞书扫码即创建机器人应用,自动自检、放开通讯录与可用范围并发版,新建项目目录 ~/Projects/<项目名>,建群并拉你进群,写入配置并初始化日志。失败时按步骤停下并给出续做方式(--app-id、--chat);公司组织里的非管理员可用 --basic。手动接入流程保留。"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"t": "飞书设置按使用流程整理",
|
|
13
|
+
"d": "优先查看连接状态和管理项目群;新用户按扫码、自动建群、群里发任务三步开始。手动接入与应用信息收进高级说明,管理员和控制台登录单独设置。"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
"t": "第三方额度与余额",
|
|
17
|
+
"d": "GLM 显示 5 小时 / 周配额窗口,没有套餐时退回查余额;MiniMax 显示 5 小时与周窗口;DeepSeek 余额。凭据失效会明确显示,瞬时故障保留上次结果 10 分钟。"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"t": "Claude 登录额度兜底",
|
|
21
|
+
"d": "Claude Code 登录令牌过期时,由 CLI 自行刷新并读取其缓存的额度,不再显示「未验证」;只读不代刷,不阻塞群命令和控制台。"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"t": "环境冲突检查",
|
|
25
|
+
"d": "预检与控制台模型卡列出会改道 Claude Code / Codex 的环境变量和 ~/.claude/settings.json、~/.codex/config.toml 设置,只列键名,只提示不修改。"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"t": "内置核心 ABI 36",
|
|
29
|
+
"d": "扫码接入的决策与文案、二维码编码、额度解析和环境冲突判定都在内置核心中实现;核心构建锁定工具链,产物不再依赖构建机使用的 crate 镜像。"
|
|
30
|
+
}
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"version": "1.4.16",
|
|
35
|
+
"date": "2026-11-11",
|
|
36
|
+
"name": "按群选型号与思考深度",
|
|
37
|
+
"items": [
|
|
38
|
+
{
|
|
39
|
+
"t": "型号与深度按群选",
|
|
40
|
+
"d": "控制台「群 · 项目」卡上选后端、具体型号和思考深度,型号清单取自本机 Codex / Claude Code 的实时数据;订阅登录的模型卡不再设型号和深度,第三方端点的型号仍在模型卡上。"
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"t": "群命令 /model",
|
|
44
|
+
"d": "发 /model 打开点选卡,选好点「确认更新」生效;也可直接发 /model high、/model 型号、/model 后端 型号 深度。同一后端换型号或调深度接着当前对话,换后端开新对话;都要等群空闲,回执和审计记下是谁改的。"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"t": "原有设置自动迁移",
|
|
48
|
+
"d": "升级连上后,模型卡上原有的型号和深度写进各群,生效值不变、会话不重置;改前自动备份配置。Codex 自定义上下文窗口按各群所选型号的上限校验。"
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
},
|
|
2
52
|
{
|
|
3
53
|
"version": "1.4.15",
|
|
4
54
|
"date": "2026-11-11",
|
package/README.md
CHANGED
|
@@ -31,7 +31,7 @@ SintBot is distributed under the [PolyForm Shield License 1.0.0](./LICENSE).
|
|
|
31
31
|
<p align="center"><img src="https://raw.githubusercontent.com/sintlab/sintbot/main/docs/assets/how-it-works.svg" alt="Group (Slack, Feishu or WeCom) → SintBot on your machine → Claude Code or Codex in the project directory → the model chosen for that group; progress and results flow back to the group" width="880"></p>
|
|
32
32
|
|
|
33
33
|
- **One group, one project.** Each group gets its own project directory, session, queue, permissions and scheduled tasks.
|
|
34
|
-
- **Same agent, any brain.**
|
|
34
|
+
- **Same agent, any brain.** Pick the backend, model and thinking depth with `/model` in the chat. A model or depth change continues the conversation; a backend change starts a new one, while project files and shared notes in `CLAUDE.md` and `AGENTS.md` carry over, so save important decisions in the project first.
|
|
35
35
|
- **Stays on your machine.** Projects, sessions, memory and logs live on your disk and never pass through a SintBot server. Prompts and the code the model reads still go to the provider you chose.
|
|
36
36
|
- **No public IP.** Every platform uses an outbound long connection (Socket Mode on Slack). One daemon can serve Slack, Feishu and WeCom at the same time.
|
|
37
37
|
|
|
@@ -45,7 +45,7 @@ curl -fsSL https://sintbot.com/install.sh | bash
|
|
|
45
45
|
|
|
46
46
|
The script checks the environment, installs SintBot and verifies the result, without `sudo`. You can read [install.sh](./install.sh) before running it. Installation does not sign in to a model account, connect a group or start a service.
|
|
47
47
|
|
|
48
|
-
**2. Connect your group and project.**
|
|
48
|
+
**2. Connect your group and project.** Run `sintbot connect` and scan the QR code with Feishu under an enterprise or team identity: it creates the app, a project folder and a group, then writes the configuration. The [installation guide](https://sintbot.com/guide/install/) covers the options, the manual route and the startup steps.
|
|
49
49
|
|
|
50
50
|
Installing with an AI assistant? Use the [AI-assisted install and first-use guide](./docs/AI-ASSISTED-INSTALL.md).
|
|
51
51
|
|
package/README.zh-CN.md
CHANGED
|
@@ -31,7 +31,7 @@ SintBot 按 [PolyForm Shield 1.0.0 许可](./LICENSE) 分发。
|
|
|
31
31
|
<p align="center"><img src="https://raw.githubusercontent.com/sintlab/sintbot/main/docs/assets/how-it-works-zh.svg" alt="群(飞书、Slack 或企业微信)→ 你机器上的 SintBot → 项目目录里的 Claude Code 或 Codex → 这个群选的模型;进度与结果回到群里" width="880"></p>
|
|
32
32
|
|
|
33
33
|
- **一群一项目。** 每个群各有自己的项目目录、会话、排队、权限和定时任务。
|
|
34
|
-
- **同一套 agent,任意大脑。** 群里发 `/model`
|
|
34
|
+
- **同一套 agent,任意大脑。** 群里发 `/model` 点选后端、型号和思考深度。换型号或调深度接着当前对话;换后端会开新对话,项目文件及 `CLAUDE.md`、`AGENTS.md` 中的共享记录可以延续,重要决定先写进项目。
|
|
35
35
|
- **数据留在本机。** 项目、会话、记忆、日志都在你自己的硬盘上,不经过任何 SintBot 服务器。提示词和模型读到的代码照常发给你选的模型服务商。
|
|
36
36
|
- **不要公网 IP。** 每个平台都是机器主动往外连的长连接(Slack 用 Socket Mode)。一个守护进程可以同时接飞书、Slack、企业微信。
|
|
37
37
|
|
|
@@ -45,7 +45,7 @@ curl -fsSL https://sintbot.com/install.sh | bash
|
|
|
45
45
|
|
|
46
46
|
脚本会检查环境、安装 SintBot 并验证结果,不用 `sudo`。也可以先读 [install.sh](./install.sh)。安装本身不登录模型账号、不连接群,也不启动服务。
|
|
47
47
|
|
|
48
|
-
**2. 接入群和项目。**
|
|
48
|
+
**2. 接入群和项目。** 运行 `sintbot connect`,用企业/团队身份的飞书扫码:它会创建应用、项目目录和群,并写好配置。参数、手动路径和启动步骤见[安装指南](https://sintbot.com/zh/guide/install/)。
|
|
49
49
|
|
|
50
50
|
想让本机 AI 助手帮忙安装并完成首次使用?先看 [AI 安装与首次使用说明](./docs/zh-CN/AI-ASSISTED-INSTALL.md)。
|
|
51
51
|
|
package/bin/sintbot-v2.js
CHANGED
|
@@ -43,11 +43,58 @@ async function startService(rest) {
|
|
|
43
43
|
throw new Error('Console options require --ui or ui.enabled:true')
|
|
44
44
|
}
|
|
45
45
|
assertInstallationJournals(installation)
|
|
46
|
+
// Settings outside this installation that can reroute a CLI (daemon env,
|
|
47
|
+
// ~/.claude/settings.json, ~/.codex/config.toml): warn-only, keys not values.
|
|
48
|
+
if (preflightOnly) require('../src/agent/envconflicts').reportEnvConflicts(installation)
|
|
46
49
|
// Every installation, including one group, runs the multi-group service:
|
|
47
50
|
// chat commands, the console writer and per-group fault closing live there.
|
|
48
51
|
return startMultiGroupService({ installation, preflightOnly, uiEnabled, uiHost, uiPort })
|
|
49
52
|
}
|
|
50
53
|
|
|
54
|
+
// Model and depth used to live on model cards; they now belong to each group.
|
|
55
|
+
// Once every group is connected (a console upgrade can no longer roll back
|
|
56
|
+
// onto this configuration), copy each card's value into the groups that have
|
|
57
|
+
// not chosen and remove it from the card, in one validated write. Effective
|
|
58
|
+
// values do not change, so no session resets. The file is backed up first;
|
|
59
|
+
// a group still running is retried later.
|
|
60
|
+
function scheduleCardDefaultsMigration({ service, configControl, updater, cfg, stateDir, log,
|
|
61
|
+
delayMs = 15_000, tries = 40 } = {}) {
|
|
62
|
+
const fs = require('node:fs')
|
|
63
|
+
const { fillGroupPicks, stripCardDefaults } = require('../src/groupbrain')
|
|
64
|
+
const pending = () => {
|
|
65
|
+
const probe = JSON.parse(fs.readFileSync(cfg._path, 'utf8'))
|
|
66
|
+
const filled = fillGroupPicks(probe).changed
|
|
67
|
+
return stripCardDefaults(probe).changed || filled
|
|
68
|
+
}
|
|
69
|
+
try { if (!pending()) return } catch (error) { log(`card defaults migration skipped: ${error?.message || String(error)}`); return }
|
|
70
|
+
let left = tries
|
|
71
|
+
const attempt = () => {
|
|
72
|
+
left -= 1
|
|
73
|
+
try {
|
|
74
|
+
if (!service.serving || service.held.size || service.inMaintenance ||
|
|
75
|
+
updater?.status?.()?.job?.state === 'switching') throw new Error('service not settled')
|
|
76
|
+
if (!pending()) return
|
|
77
|
+
const dir = path.join(stateDir, 'config-backups')
|
|
78
|
+
fs.mkdirSync(dir, { recursive: true, mode: 0o700 })
|
|
79
|
+
const backup = path.join(dir, `${new Date().toISOString().replace(/[:.]/g, '-')}-group-model.json`)
|
|
80
|
+
fs.copyFileSync(cfg._path, backup)
|
|
81
|
+
fs.chmodSync(backup, 0o600)
|
|
82
|
+
let summary = null
|
|
83
|
+
configControl.writeConfig(raw => {
|
|
84
|
+
const filled = fillGroupPicks(raw)
|
|
85
|
+
const stripped = stripCardDefaults(raw)
|
|
86
|
+
summary = { groups: filled.groups.length, providers: stripped.providers }
|
|
87
|
+
})
|
|
88
|
+
log(`model and depth moved from model cards into groups: ${summary.groups} group(s), cards ${summary.providers.join(', ') || '-'}; backup ${path.basename(backup)}`)
|
|
89
|
+
} catch (error) {
|
|
90
|
+
if (left > 0) { const t = setTimeout(attempt, delayMs * 4); t.unref?.() }
|
|
91
|
+
else log(`card defaults migration not applied: ${error?.message || String(error)}`)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
const timer = setTimeout(attempt, delayMs)
|
|
95
|
+
timer.unref?.()
|
|
96
|
+
}
|
|
97
|
+
|
|
51
98
|
async function startMultiGroupService({ installation, preflightOnly, uiEnabled = false, uiHost, uiPort }) {
|
|
52
99
|
const { stateDir, cfg, routes } = installation
|
|
53
100
|
const { assertPrivateDirectory, assertJournalScope } = require('../src/c2install')
|
|
@@ -67,11 +114,27 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
67
114
|
const { C2DurableRuntime } = require('../src/core/c2durableruntime')
|
|
68
115
|
// /hud reads the group's model quota; /issue appends to the console's log.
|
|
69
116
|
const usageProbe = require('../src/usage').createUsageProbe({
|
|
117
|
+
bin: cfg.agent?.bin || 'claude',
|
|
70
118
|
codexUsage: async () => { const status = require('../src/agent/codexstatus')
|
|
71
119
|
return status.formatUsage(await status.rateLimits('codex')) } })
|
|
72
120
|
const issues = require('../src/core/issues').createIssues(path.join(stateDir, 'issues.jsonl'),
|
|
73
121
|
{ withFileLock: require('../src/core/store').withFileLock,
|
|
74
122
|
log: message => process.stderr.write(`C2 multi-group · ${message}\n`) })
|
|
123
|
+
// Backend · model · depth: one live model list per process (also used by the
|
|
124
|
+
// console), and every effective change is audited.
|
|
125
|
+
const modelCatalog = require('../src/modelcatalog').createModelCatalog({
|
|
126
|
+
codexstatus: require('../src/agent/codexstatus'), claudemodels: require('../src/agent/claudemodels'),
|
|
127
|
+
claudeBin: cfg.agent?.bin || 'claude', cacheFile: path.join(stateDir, 'model-lists.json'),
|
|
128
|
+
log: message => process.stderr.write(`C2 multi-group · ${message}\n`) })
|
|
129
|
+
const groupBrainMod = require('../src/groupbrain')
|
|
130
|
+
const { buildModelCard } = require('../src/platform/modelcard')
|
|
131
|
+
const audit = require('../src/core/ledger').createLedger(path.join(stateDir, 'audit.jsonl'),
|
|
132
|
+
message => process.stderr.write(`C2 multi-group · ${message}\n`))
|
|
133
|
+
const setGroupBrain = (chatId, change, { by = '', source = 'console' } = {}) => {
|
|
134
|
+
const result = configControl.setGroupBrain(chatId, change)
|
|
135
|
+
if (result?.changed) audit.append(groupBrainMod.auditRow(chatId, result, { source, by }))
|
|
136
|
+
return result
|
|
137
|
+
}
|
|
75
138
|
const runner = require('../src/agent/runner')
|
|
76
139
|
const validators = { validateState: validatePersistentState,
|
|
77
140
|
validateReceipts: validatePersistentReceipts, validateRelation: validatePersistentRelation }
|
|
@@ -173,6 +236,18 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
173
236
|
// Faults in a shared resource stop every group; others hold one group.
|
|
174
237
|
sharedFault: () => capacity.snapshot().closed ? 'model concurrency closed' : null,
|
|
175
238
|
log: message => process.stderr.write(`C2 multi-group · ${message}\n`),
|
|
239
|
+
// /model card taps: the engine decides, the reply replaces the card.
|
|
240
|
+
onModelCard: async (event, binding) => {
|
|
241
|
+
let who = event.operator?.name || ''
|
|
242
|
+
const adapter = liveTransport?.platform.forChat(event.chatId)
|
|
243
|
+
if (!who && adapter?.nameOf && event.operator?.openId) { try { who = (await adapter.nameOf(event.operator.openId)) || '' } catch {} }
|
|
244
|
+
const out = await binding.engine.handleModelCard({ chatId: event.chatId, who,
|
|
245
|
+
value: event.action.value || {}, option: event.action.option })
|
|
246
|
+
const resp = {}
|
|
247
|
+
if (out?.toast) resp.toast = out.toast
|
|
248
|
+
if (out?.state) resp.card = { type: 'raw', data: buildModelCard(out.state) }
|
|
249
|
+
return Object.keys(resp).length ? resp : undefined
|
|
250
|
+
},
|
|
176
251
|
onFatal: (error, result) => {
|
|
177
252
|
process.stderr.write(`C2 multi-group failure: ${error?.message || String(error)}\n`)
|
|
178
253
|
finish(result)
|
|
@@ -250,8 +325,14 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
250
325
|
},
|
|
251
326
|
durableV2SwitchProvider: (chatId, name) => {
|
|
252
327
|
if (chatId !== route.chatId) throw new Error('Model switch targets another group')
|
|
253
|
-
|
|
328
|
+
setGroupBrain(chatId, { provider: name }, { source: 'command' })
|
|
329
|
+
},
|
|
330
|
+
durableV2SetBrain: (chatId, change, meta) => {
|
|
331
|
+
if (chatId !== route.chatId) throw new Error('Model change targets another group')
|
|
332
|
+
return setGroupBrain(chatId, change, meta)
|
|
254
333
|
},
|
|
334
|
+
durableV2ModelCatalog: (name, provider) => modelCatalog(name, provider),
|
|
335
|
+
durableV2ModelCard: state => buildModelCard(state),
|
|
255
336
|
durableV2UsageProbe: usageProbe, durableV2Issues: issues,
|
|
256
337
|
durableV2Version: require('../package.json').version,
|
|
257
338
|
durableV2DataDir: stateDir,
|
|
@@ -301,7 +382,7 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
301
382
|
getPlatform: chatId => liveTransport?.platform.forChat(chatId) || null,
|
|
302
383
|
routeMessage: (msg, adapter) => service.routeMessage(msg, adapter),
|
|
303
384
|
isServing: () => service.serving, isFrozen: () => service.inMaintenance,
|
|
304
|
-
assertWriterLock, ...configControl, updater,
|
|
385
|
+
assertWriterLock, ...configControl, setGroupBrain, modelCatalog, updater,
|
|
305
386
|
log: message => process.stderr.write(`Console · ${message}\n`) })
|
|
306
387
|
await new Promise((resolve, reject) => {
|
|
307
388
|
ui.server.once('error', reject)
|
|
@@ -337,6 +418,14 @@ async function startMultiGroupService({ installation, preflightOnly, uiEnabled =
|
|
|
337
418
|
// group is connected; then the daily version check starts.
|
|
338
419
|
if (!started.held?.length) updater?.confirmStarted()
|
|
339
420
|
void updater?.start()
|
|
421
|
+
// Model lists are probed once in the background shortly after startup, so
|
|
422
|
+
// a later /model or console group card does not wait for a CLI handshake.
|
|
423
|
+
const runners = new Set(Object.values(cfg.providers || {}).filter(p => p && !p.hidden)
|
|
424
|
+
.map(p => p.runner === 'codex' ? 'codex' : !p.baseUrl ? 'claude' : '').filter(Boolean))
|
|
425
|
+
const warm = setTimeout(() => { void modelCatalog.warm([...runners]).catch(() => {}) }, 30_000)
|
|
426
|
+
warm.unref?.()
|
|
427
|
+
scheduleCardDefaultsMigration({ service, configControl, updater, cfg, stateDir,
|
|
428
|
+
log: message => process.stderr.write(`SintBot · ${message}\n`) })
|
|
340
429
|
} catch (error) {
|
|
341
430
|
ui?.close()
|
|
342
431
|
const stopped = await service?.stop('startup-failed', 1)
|
|
@@ -381,12 +470,17 @@ const HELP = `SintBot macOS Feishu multi-group candidate
|
|
|
381
470
|
Usage:
|
|
382
471
|
sintbot init [--config <absolute path>] [--data-dir <absolute path>]
|
|
383
472
|
sintbot start [--config <absolute path>] [--data-dir <absolute path>] [--preflight] [--ui] [--ui-host <address>] [--ui-port <port>] [--no-open]
|
|
473
|
+
sintbot connect [--config <absolute path>] [--data-dir <absolute path>] [--workdir <absolute path>] [--name <project>] [--provider codex|claude] [--app-id <cli_…>] [--chat <oc_…>] [--basic] [--no-open]
|
|
384
474
|
sintbot incident seal --group <oc_…> [--config <absolute path>] [--data-dir <absolute path>] [--reason <text>]
|
|
385
475
|
sintbot feishu <action> [arguments…] [--chat <oc_…>] [--config <path>]
|
|
386
476
|
sintbot version
|
|
387
477
|
sintbot help
|
|
388
478
|
|
|
389
479
|
Supports explicit Feishu groups using Codex or Claude Code. Start runs in the foreground.
|
|
480
|
+
connect scans a Feishu QR code to create the app, then creates a group (or binds --chat), the project
|
|
481
|
+
folder under ~/Projects, the configuration and the journals, in that order; a failed check stops before
|
|
482
|
+
the folder and configuration exist. It refuses existing configuration, state or a running service;
|
|
483
|
+
--app-id resumes only when no configuration or an empty-group one for that app exists.
|
|
390
484
|
Initialization is for a new installation only; existing journals are never reset.
|
|
391
485
|
Console shows live groups, binds groups and switches models or projects of idle groups;
|
|
392
486
|
in a group, /model lists or switches models. Unsupported actions stay disabled.
|
|
@@ -407,7 +501,7 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
407
501
|
return
|
|
408
502
|
}
|
|
409
503
|
if (!sub || sub === 'help' || sub === '--help' || sub === '-h' ||
|
|
410
|
-
['init', 'start'].includes(sub) && rest.length === 1 && ['--help', '-h'].includes(rest[0])) {
|
|
504
|
+
['init', 'start', 'connect'].includes(sub) && rest.length === 1 && ['--help', '-h'].includes(rest[0])) {
|
|
411
505
|
process.stdout.write(HELP)
|
|
412
506
|
return
|
|
413
507
|
}
|
|
@@ -419,10 +513,16 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
419
513
|
process.exitCode = outcome === 'done' ? 0 : 1
|
|
420
514
|
return
|
|
421
515
|
}
|
|
422
|
-
if (!['init', 'start', 'incident', 'feishu'].includes(sub)) {
|
|
516
|
+
if (!['init', 'start', 'connect', 'incident', 'feishu'].includes(sub)) {
|
|
423
517
|
throw new Error(`${sub} is unavailable in this release; use sintbot help`)
|
|
424
518
|
}
|
|
425
519
|
if (process.platform !== 'darwin') throw new Error('this release supports macOS only')
|
|
520
|
+
if (sub === 'connect') {
|
|
521
|
+
// Operator-only onboarding; deliberately not an action of the feishu skill,
|
|
522
|
+
// which the agent may run from a group.
|
|
523
|
+
process.exitCode = await require('../src/connect').runConnect(rest)
|
|
524
|
+
return
|
|
525
|
+
}
|
|
426
526
|
if (sub === 'feishu') {
|
|
427
527
|
// The agent's group skills. Each call uses the app credentials directly;
|
|
428
528
|
// the service need not run. Text after the action may itself start with
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Install SintBot 1.4.
|
|
1
|
+
# Install SintBot 1.4.17 with an AI assistant
|
|
2
2
|
|
|
3
3
|
English · [简体中文](./zh-CN/AI-ASSISTED-INSTALL.md)
|
|
4
4
|
|
|
@@ -7,13 +7,13 @@ Use the terminal on the Mac that will run SintBot. A cloud shell cannot install
|
|
|
7
7
|
1. Check macOS, Node 22+, the actual Codex or Claude Code executable and login, and any existing SintBot process. Do not print credentials. Preserve existing installations, configuration and state.
|
|
8
8
|
2. Run the one-line installation command in the guide; `--dry-run` is optional. Continue within the user's authorization; ask only for missing decisions. The installer does not change Node, Codex, Claude Code, shell profiles or services.
|
|
9
9
|
3. The installer verifies the version and core at `~/.local/share/sintbot/bin/sintbot`. A successful package install is not a successful group test.
|
|
10
|
-
4.
|
|
11
|
-
5.
|
|
10
|
+
4. Prefer `sintbot connect`: it prints the scan link and opens a local QR page; only the user can scan and confirm on the phone, after which it creates the project folder and group, writes the configuration and initializes, so no secret passes through the assistant. It refuses when a Feishu configuration exists or the service is running; do not delete files to get past that. If the scan is not possible, prepare the configuration by the manual route in the install guide: let the user enter secrets locally or use already authorized credential references without revealing them. Use an existing authorized Feishu app only when another process is not connected to it.
|
|
11
|
+
5. On the manual route, run `init` for fresh state only (never again after `connect` has initialized), then `start --preflight`. Refusal due to an existing journal or unresolved receipts is a stop condition: never delete files, fabricate a new authority or alter receipts to bypass it.
|
|
12
12
|
6. Run the foreground service, send an authorized read-only group request and compare its response with the file. Stop cleanly and verify a restart. If no person is available, use an authorized test account to send the request; document the sender and do not call it independent human acceptance. A bot's own API message may be ignored and is not a substitute for an incoming user message.
|
|
13
13
|
7. Stop temporary processes before ending the task. Report what was installed, what actually ran and any remaining setup. Do not register an automatic background service for this release.
|
|
14
14
|
|
|
15
15
|
## Copyable request
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
Install SintBot 1.4.
|
|
18
|
+
Install SintBot 1.4.17 on this Mac using the one-line installation guide. Preserve existing services, configuration, state and Codex or Claude Code account data. Do not expose credentials. Verify the actual installed executable and binary core, configure the Feishu groups with their runners, and initialize only clean new state. Run the foreground preflight and an authorized read-only group test, check the result, then verify clean stop/restart. Stop temporary processes when finished, and distinguish package installation from successful group acceptance.
|
|
19
19
|
```
|
package/docs/install.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# Install SintBot 1.4.
|
|
1
|
+
# Install SintBot 1.4.17
|
|
2
2
|
|
|
3
3
|
English · [简体中文](./zh-CN/install.md)
|
|
4
4
|
|
|
@@ -10,13 +10,47 @@ You need a Mac, Node.js 22 or newer, and projects stored on that Mac. Install an
|
|
|
10
10
|
curl -fsSL https://sintbot.com/install.sh | bash
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The installer checks the environment, installs `sintbot@1.4.
|
|
13
|
+
The installer checks the environment, installs `sintbot@1.4.17` into `~/.local/share/sintbot`, and verifies the entry, built-in core and Feishu module. Continue when you see `Installed and verified SintBot 1.4.17`. No sudo is needed.
|
|
14
14
|
|
|
15
15
|
To preview, append `-s -- --dry-run`. To choose another location, set `SINTBOT_INSTALL_DIR` and adjust the executable paths below.
|
|
16
16
|
|
|
17
|
-
## 2. Connect
|
|
17
|
+
## 2. Connect by scanning (recommended)
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
Prerequisite: the Feishu app on your phone is signed in with an enterprise or team identity; a personal identity without an organization cannot scan, so create a team first (free). The app created by the scan belongs to the scanner's organization; approval depends on its policies.
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
"$HOME/.local/share/sintbot/bin/sintbot" connect
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
The terminal prints the scan link and opens a local page with the QR code; scan it with Feishu and confirm on the phone. It then, in order: obtains the app credentials (and prints the app ID) → runs the self-check → opens the contact range and visibility and publishes a version → creates a group and adds you → creates the project folder `~/Projects/sintbot` (with a `README.md`) → writes `~/.sbot/config.json` → initializes the message journals. Every step reports its result. Once the scan is confirmed, the app exists in your organization; any later failed check or group creation stops before the folder and the configuration are created and prints the rerun command with `--app-id`.
|
|
26
|
+
|
|
27
|
+
- The credentials are stored in clear text in `~/.sbot/config.json` (file 0600, directory 0700). To switch back to environment variables, change `appId` / `appSecret` to `"env:FEISHU_APP_ID"` / `"env:FEISHU_APP_SECRET"` and export both before every start.
|
|
28
|
+
- `--basic`: for a non-administrator in a company organization. It does not request `application:application:patch` and skips the range and publish steps; ask an administrator to finish those two in the Open Platform.
|
|
29
|
+
- `--app-id cli_…`: reuse an existing app (add permissions to it instead of creating one), to rerun after a stop or once a review is approved. Allowed only when there is no configuration file, or the file belongs to that app and binds no group yet; a configuration with groups is never replaced.
|
|
30
|
+
- `--chat oc_…`: skip group creation and bind a group you created by hand with the bot added (the notice after a failed group creation shows this command).
|
|
31
|
+
- `--workdir /absolute/path`: bind an existing directory instead of creating one; `--name` changes the default folder name; `--provider codex|claude` picks the runner (chosen automatically when only one is installed); `--no-open` prints the link without opening the page.
|
|
32
|
+
- `connect` refuses to run when a Feishu configuration already exists or the service is running. The scan requests the card callbacks used by `/model`. If the card does not respond, check the callback subscription as in section 4.
|
|
33
|
+
|
|
34
|
+
## 3. First start
|
|
35
|
+
|
|
36
|
+
`connect` has already initialized, so start directly:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
"$HOME/.local/share/sintbot/bin/sintbot" start --config "$HOME/.sbot/config.json" --ui
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
This checks the configuration and runners, connects Feishu and opens the local console. Keep the terminal open. In the group, send "Read the first line of the project README and reply with only that line", then compare it with `~/Projects/sintbot/README.md`. A passing preflight does not prove Feishu authentication or delivery.
|
|
43
|
+
|
|
44
|
+
## 4. Manual setup (alternative)
|
|
45
|
+
|
|
46
|
+
Use the manual route when the scan fails, an administrator does not approve, or you prefer to manage the app yourself; it is the same procedure as before.
|
|
47
|
+
|
|
48
|
+
<details>
|
|
49
|
+
<summary>Show the manual steps</summary>
|
|
50
|
+
|
|
51
|
+
#### 4.1 Create the app
|
|
52
|
+
|
|
53
|
+
In the [Feishu Open Platform](https://open.feishu.cn/), create a custom app, enable the bot and the permissions to receive and reply to messages, choose long connection for event subscription and subscribe to message receipt. For callbacks, also choose long connection and subscribe to card action callbacks (the tap-to-pick `/model` card needs them). Publish the app, add the bot to the groups you will use, and copy each group's `oc_…` chat ID.
|
|
20
54
|
|
|
21
55
|
Enter the app credentials in the default macOS terminal (the secret is not echoed):
|
|
22
56
|
|
|
@@ -29,7 +63,7 @@ export FEISHU_APP_ID FEISHU_APP_SECRET
|
|
|
29
63
|
|
|
30
64
|
Never post the secret in a group or commit it. Keep using this terminal; set both variables again in a new one.
|
|
31
65
|
|
|
32
|
-
|
|
66
|
+
#### 4.2 Write the configuration
|
|
33
67
|
|
|
34
68
|
Create a private directory with `mkdir -p ~/.sbot && chmod 700 ~/.sbot`, then create `~/.sbot/config.json`. Replace the chat IDs and absolute project paths; each project directory must already exist. The example binds two groups, one with Codex and one with Claude Code. With one group, delete the other entry.
|
|
35
69
|
|
|
@@ -69,9 +103,9 @@ Then run `chmod 600 ~/.sbot/config.json`.
|
|
|
69
103
|
- To pin a Claude model, add `"model"` to `providers.claude`; otherwise Claude Code's default model on this Mac is used.
|
|
70
104
|
- At most 2 tasks run at once by default; add `"agent": { "maxConcurrent": 4 }` at the top level to change it (1–32).
|
|
71
105
|
|
|
72
|
-
|
|
106
|
+
#### 4.3 Initialize and start
|
|
73
107
|
|
|
74
|
-
Copy and run in the same terminal:
|
|
108
|
+
Copy and run in the same terminal (the manual route runs `init` itself):
|
|
75
109
|
|
|
76
110
|
```bash
|
|
77
111
|
SINTBOT="$HOME/.local/share/sintbot/bin/sintbot"
|
|
@@ -84,6 +118,8 @@ This initializes the message journals, checks the configuration and runners, con
|
|
|
84
118
|
|
|
85
119
|
In a group, send "Read the first line of the project README and reply with only that line", then compare it with the file. A passing preflight does not prove Feishu authentication or delivery.
|
|
86
120
|
|
|
121
|
+
</details>
|
|
122
|
+
|
|
87
123
|
## 5. Use it in a group
|
|
88
124
|
|
|
89
125
|
Any text message is a task. Replies arrive in a card that appears once the model starts answering, or after 25 seconds if it is still working without output. While the task runs, the card shows how long it has run, what it is doing and the steps already delivered; at the end those steps fold into a Process section.
|
|
@@ -94,7 +130,7 @@ Any text message is a task. Replies arrive in a card that appears once the model
|
|
|
94
130
|
| `/skip` | Cancel only the current task; queued messages run next and the conversation is kept |
|
|
95
131
|
| `/stop` | Stop the current task and cancel queued ones; the conversation is kept |
|
|
96
132
|
| `/new` | Start a new conversation, forgetting the previous one (only while the group is idle) |
|
|
97
|
-
| `/model` |
|
|
133
|
+
| `/model` | Opens a tap-to-pick card: choose the backend, model and thinking depth, then confirm. You can also send `/model high`, `/model <model>` or `/model <backend>`. A model or depth change continues the conversation; a backend change starts a new one. Only while the group is idle |
|
|
98
134
|
| `/hud` | Quota of the current model and usage of this conversation |
|
|
99
135
|
| `/issue text` | Log feedback; review and close it in the console overview |
|
|
100
136
|
| `/help` | List commands, with this group's project and model |
|
|
@@ -107,7 +143,7 @@ When the reply card moves below a confirmed follow-up, its existing text is show
|
|
|
107
143
|
|
|
108
144
|
## 6. Console
|
|
109
145
|
|
|
110
|
-
Model cards distinguish requested settings from runtime reports; expand Capacity and runtime details for reference limits. Codex context and per-model compaction each offer Executor default or Custom. Numeric inputs appear only for Custom. Changes apply next turn without resetting the session; known catalog context limits are enforced. Compaction means a threshold for Codex and a window for Claude Code, and may start earlier. Explicit legacy global compaction values are converted into independent settings for existing models, preserving per-model overrides. New models use executor defaults. Reading does not rewrite the file; the converted settings are persisted on the next normal configuration save.
|
|
146
|
+
Model and thinking depth are chosen per group: on the group card, the Model row picks the backend and model and the next row picks the depth, from lists read live from the local Codex and Claude Code. Subscription model cards no longer carry a model or depth; a third-party endpoint keeps its model on the model card. After the upgrade, each model card's existing model and depth move into its groups with the same effective values and no session reset, after a configuration backup. Model cards distinguish requested settings from runtime reports; expand Capacity and runtime details for reference limits. Codex context and per-model compaction each offer Executor default or Custom. Numeric inputs appear only for Custom. Changes apply next turn without resetting the session; known catalog context limits are enforced. Compaction means a threshold for Codex and a window for Claude Code, and may start earlier. Explicit legacy global compaction values are converted into independent settings for existing models, preserving per-model overrides. New models use executor defaults. Reading does not rewrite the file; the converted settings are persisted on the next normal configuration save.
|
|
111
147
|
|
|
112
148
|
The overview combines group projects and concurrency. Each row shows the runner, state and elapsed time, project folder, model and configured reasoning effort, and the latest reported context usage. Select a group to view its tasks, then expand a task for tools, reply and usage. Context is a session sample, not cumulative token consumption; unknown capacity and older samples are labeled. Row icons provide console messages, stop and new-session controls according to the current group state.
|
|
113
149
|
|
|
@@ -123,7 +159,7 @@ You can also add a group without the console: add it to the configuration, stop
|
|
|
123
159
|
|
|
124
160
|
## Stop and restart
|
|
125
161
|
|
|
126
|
-
Press Ctrl-C and wait for exit.
|
|
162
|
+
Press Ctrl-C and wait for exit. If the configuration uses `env:` references, set those variables first, then run:
|
|
127
163
|
|
|
128
164
|
```bash
|
|
129
165
|
"$HOME/.local/share/sintbot/bin/sintbot" start --config "$HOME/.sbot/config.json" --ui
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# 用 AI 助手安装 SintBot 1.4.
|
|
1
|
+
# 用 AI 助手安装 SintBot 1.4.17
|
|
2
2
|
|
|
3
3
|
[English](../AI-ASSISTED-INSTALL.md) · 简体中文
|
|
4
4
|
|
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
1. 核对 macOS、Node 22 及以上、Codex 或 Claude Code 的实际程序与登录、是否已有 SintBot 进程。不输出凭据,保留已有安装、配置和状态。
|
|
8
8
|
2. 运行指南中的一行安装命令;`--dry-run` 为可选预览。用户已经授权的步骤继续执行,只问缺失的决定。安装器不修改 Node、Codex、Claude Code、终端配置或服务。
|
|
9
9
|
3. 安装器会验证 `~/.local/share/sintbot/bin/sintbot` 的版本和核心加载。安装程序包成功不等于群测试成功。
|
|
10
|
-
4.
|
|
11
|
-
5.
|
|
10
|
+
4. 优先用 `sintbot connect` 接入:它打印扫码链接并打开本机二维码页面,扫码和手机确认只能由用户本人完成;之后它自己建项目目录、建群、写配置并初始化,密钥不需要经过助手。已有飞书配置或服务在跑时它会拒绝,不要删文件绕过。扫码走不通再按安装指南的手动路径准备配置:让用户在本地输入密钥,或使用已经授权的凭据引用,不显示秘密。复用授权飞书应用时,确认没有另一个进程连接该应用。
|
|
11
|
+
5. 手动路径下,只有全新状态才能执行 `init`(`connect` 已经初始化过的不要再执行),然后执行 `start --preflight`。若已有日志或未结收据而拒绝启动,不得删文件、新造日志权威或篡改收据来绕过。
|
|
12
12
|
6. 前台运行,用已经授权的账号在群内发只读请求,把回复和文件对照,再正常停止并验证重启。没有真人协助时可用授权测试账号发送,并记录实际发送方式,不称为独立真人验收;机器人自己发的接口消息可能被忽略,不能当作用户入站消息。
|
|
13
13
|
7. 本轮临时进程用完停止。说明安装了什么、实际验证了什么、还缺什么。本版不注册自动后台服务。
|
|
14
14
|
|
|
15
15
|
## 可复制的请求
|
|
16
16
|
|
|
17
17
|
```text
|
|
18
|
-
按一行安装指南,在这台 Mac 安装 SintBot 1.4.
|
|
18
|
+
按一行安装指南,在这台 Mac 安装 SintBot 1.4.17。保留已有服务、配置、状态和 Codex 或 Claude Code 账号数据,不输出凭据。验证实际入口与二进制核心,配置飞书群及各群的执行器,只对全新状态初始化。运行前台预检和已授权的只读群测试,对照结果,再验证正常停止与重启。完成后停止临时进程,并区分程序包安装与群验收结果。
|
|
19
19
|
```
|